Ported cons and nth methods, with all needed helpers. Added simple test.
authorJorge Gorbe <jgorbe@stcsl.es>
Tue, 4 Mar 2014 18:39:12 +0000 (19:39 +0100)
committerJorge Gorbe <jgorbe@stcsl.es>
Tue, 4 Mar 2014 18:39:12 +0000 (19:39 +0100)
persistent_vector.h
test_vector.cc [new file with mode: 0644]

index 0ac3f45af7f4e62eac52276bf05d952dba00ca74..d792b76d3aaec27802118df80f642716a7afe761 100644 (file)
@@ -2,12 +2,17 @@
 #define PERSISTENT_VECTOR_H
 
 #include <memory>
+#include <vector>
+#include <utility>
 #include <cstdint>
+#include <cassert>
 #include <stdexcept>
+#include <iostream>
 
 namespace persistent {
 
     class index_out_of_bounds_exception : public std::runtime_error {
+        public:
         index_out_of_bounds_exception(): std::runtime_error("Index out of bounds")
         {
         }
@@ -18,7 +23,7 @@ namespace persistent {
         private:
         typedef std::shared_ptr<void> NodePtr;
         typedef std::vector<T>        ElementVector;
-        typedef std::vector<NodePtr>  NodeVector;
+        typedef std::vector<NodePtr>  NodePtrVector;
 
         // U can be either T (for leaf nodes) or NodePtr (for inner nodes)
         template<typename U> struct node {
@@ -26,45 +31,69 @@ namespace persistent {
 
             node() : array() {
                 array.reserve(32);
+                std::cerr << "Empty node created" << std::endl;
+            }
+
+            node(const node& other): array(other.array) { std::cerr << "Node copied" << std::endl; }
+            node(const std::vector<U> &array): array(array) { std::cerr << "Node constructed from array" << std::endl; }
+            ~node() {
+                std::cerr << "Node destroyed" << std::endl;
             }
         };
 
+        typedef node<T> LeafNode;
+        typedef node<NodePtr> InnerNode;
 
-        uint64_t size;
+        uint64_t sz;
         uint32_t shift;
         NodePtr root;
         ElementVector tail;
 
-        static std::shared_ptr<void> EMPTY_NODE;
+        static NodePtr EMPTY_NODE;
 
         public:
-        persistent_vector(uint64_t size, uint32_t shift, std::shared_ptr<void> root, std::vector<std::shared_ptr<T>> &&tail) 
-            : size(size)
+        persistent_vector(uint64_t size, uint32_t shift, NodePtr root, const ElementVector &tail) 
+            : sz(size)
             , shift(shift)
             , root(root)
             , tail(tail)
         {
         }
 
-        persistent_vector() : persistent_vector(0, 5, EMPTY_NODE, std::vector<std::shared_ptr<T>>())
+        persistent_vector() : persistent_vector(0, 5, EMPTY_NODE, ElementVector())
+        {
+        }
+
+        persistent_vector(const persistent_vector& other)
+            : sz(other.sz)
+            , shift(other.shift)
+            , root(other.root)
+            , tail(other.tail)
         {
         }
 
+        persistent_vector &operator=(persistent_vector other)
+        {
+            swap(other);
+        }
+
         uint64_t tailoff() {
-            if (size < 32)
+            if (sz < 32)
                 return 0;
-            return ((cnt-1) >> 5) << 5;
+            return ((sz-1) >> 5) << 5;
         }
 
         ElementVector &array_for(uint64_t i) {
-            if (i < size) {
+            if (i < sz) {
                 if (i >= tailoff()) return tail;
 
                 NodePtr node = root;
                 for (uint32_t level = shift; level > 0; level -= 5) {
-                    node = (node) node->array[(i >> level) & 0x1f];
+                    InnerNode *p = (InnerNode *) node.get();
+                    node = p->array[(i >> level) & 0x1f];
                 }
-                return node->array;
+                LeafNode *p = (LeafNode *) node.get();
+                return p->array;
             }
             throw index_out_of_bounds_exception();
         }
@@ -75,15 +104,119 @@ namespace persistent {
         }
 
         const T& nth(uint64_t i, const T& notFound) {
-            if (i < size) {
+            if (i < sz) {
                 return nth(i);
             }
             return notFound;
         }
 
+
+        persistent_vector<T> assocN(uint64_t i, const T& val) {
+            if (i < sz) {
+                if (i >= tailoff()) {
+                    persistent_vector<T> result(sz, shift, root, tail);
+                    result.tail[i & 0x1f] = val;
+                    return result;
+                }
+                return persistent_vector<T>(sz, shift, do_assoc(shift, root, i, val), tail);
+            }
+            throw index_out_of_bounds_exception();
+        }
+
+        int size() {
+            return sz;
+        }
+
+        persistent_vector<T> cons(const T& val) {
+            uint64_t i = sz;
+            if (sz - tailoff() < 32) {
+                persistent_vector<T> result(sz+1, shift, root, tail);
+                result.tail.push_back(val);
+                return result;
+            }
+            // tail full, push into tree
+            NodePtr new_root;
+            NodePtr tail_node = std::make_shared<LeafNode>(tail);
+            uint32_t new_shift = shift;
+            // overflow root?
+            if ((sz >> 5) > (1 << shift)) {
+                new_root = std::make_shared<InnerNode>();
+                InnerNode *p = (InnerNode *) new_root.get();
+                p->array.push_back(root);
+                p->array.push_back(new_path(shift, tail_node));
+                new_shift += 5;
+            } else {
+                new_root = push_tail(shift, root, tail_node);
+            }
+
+            return persistent_vector<T>(sz + 1, new_shift, new_root, {val}); // old tail full, new tail contains only val
+        }
+
+
+        private:
+        static NodePtr do_assoc(uint32_t shift, NodePtr pNode, uint64_t i, const T& val) {
+            NodePtr ret;
+            if (shift == 0) { // leaf node
+                const LeafNode &node = *((LeafNode*) pNode.get());
+                ret = std::make_shared<LeafNode>(node);
+                ((LeafNode*)ret.get())->array[i & 0x1f] = val;
+            } else { // inner node
+                const InnerNode &node = *((InnerNode*) pNode.get());
+                ret = std::make_shared<InnerNode>(node);
+                uint64_t subidx = (i >> shift) & 0x1f;
+                ((InnerNode*)ret.get())->array[subidx] = do_assoc(shift-5, node.array[subidx], i, val); 
+            }
+            return ret;
+        }
+
+        NodePtr push_tail(uint32_t level, NodePtr parent, NodePtr tail_node) {
+            uint64_t subidx = ((sz - 1) >> level) & 0x1f;
+            NodePtr node_to_insert;
+
+            const InnerNode &parentRef = *((InnerNode*) parent.get());
+            NodePtr ret = std::make_shared<InnerNode>(parentRef);
+            NodePtrVector &array = ((InnerNode*) ret.get())->array;
+            if (level == 5) {
+                node_to_insert = tail_node;
+            } else {
+                if (subidx < parentRef.array.size()) {
+                    NodePtr child = parentRef.array[subidx];
+                    node_to_insert = push_tail(level-5, child, tail_node);
+                } else {
+                    node_to_insert = new_path(level-5, tail_node);
+                }
+            }
+
+            if (subidx < array.size()) {
+                array[subidx] = node_to_insert;
+            } else {
+                assert(subidx == array.size());
+                array.push_back(node_to_insert);
+            }
+
+            return ret;
+        }
+
+        static NodePtr new_path(uint32_t level, NodePtr node) {
+            if (level == 0)
+                return node;
+            NodePtr ret = std::make_shared<InnerNode>();
+            ((InnerNode*) ret.get())->array.push_back(new_path(level-5, node));
+            return ret;
+        }
+
+
+        void swap(persistent_vector &other) {
+            using std::swap;
+            swap(sz, other.sz);
+            swap(shift, other.shift);
+            swap(root, other.root);
+            swap(tail, other.tail);
+        }
+
     };
 
-    template<typename T> std::shared_ptr<void> persistent_vector<T>::EMPTY_NODE = std::make_shared<node<void>>();
+    template<typename T> std::shared_ptr<void> persistent_vector<T>::EMPTY_NODE = std::make_shared<InnerNode>();
 }
 
 
diff --git a/test_vector.cc b/test_vector.cc
new file mode 100644 (file)
index 0000000..f9a1f7b
--- /dev/null
@@ -0,0 +1,24 @@
+#include "persistent_vector.h"
+
+#include <iostream>
+
+using persistent::persistent_vector;
+
+int main() {
+    persistent_vector<int> v;
+
+    auto v1 = v.cons(1);
+    auto v12 = v1.cons(2);
+    auto v13 = v1.cons(3);
+    auto v123 = v12.cons(3);
+
+    std::cout << v123.nth(2) << std::endl;
+
+    for (int i=0; i < 2000; ++i) {
+        auto v2 = v.cons(i);
+        v = v2;
+    }
+
+    std::cout << v.nth(1500) << std::endl;
+
+}