source: src/Common/PersistentDisjointSet.h @ b21c77a

new-env
Last change on this file since b21c77a was 982f95d, checked in by Aaron Moss <a3moss@…>, 6 years ago

Start of new environment implementation; terribly broken

  • Property mode set to 100644
File size: 11.9 KB
Line 
1//
2// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
7// PersistentDisjointSet.h --
8//
9// Author           : Aaron B. Moss
10// Created On       : Wed Jun 13 16:31:00 2018
11// Last Modified By : Aaron B. Moss
12// Last Modified On : Wed Jun 13 16:31:00 2018
13// Update Count     : 1
14//
15
16#pragma once
17
18#include <cassert>
19#include <functional>
20#include <unordered_map>
21#include <utility>
22
23#include "GC.h"
24
25/// Persistent disjoint-set data structure based on the persistent array in
26/// Conchon & Filliatre "A Persistent Union-Find Data Structure". Path
27/// compression is not performed (to lower cost of persistent rollback).
28/// Auxilliary lists are kept for efficient retrieval of class members.
29/// Find root should still operate in O(log k), for k the size of an
30/// equivalence class.
31
32template<typename Elm, typename Hash = std::hash<Elm>, typename Eq = std::equal_to<Elm>>
33class PersistentDisjointSet : public GC_Object {
34public:
35        /// Type of this class
36        using Self = PersistentDisjointSet<Elm, Hash, Eq>;
37
38        /// Types of version nodes
39        enum Mode { 
40                BASE,    ///< Root node of version tree
41                ADD,     ///< Add key to set
42                REM,     ///< Reverse add operation
43                ADDTO,   ///< Merge one class root under another
44                REMFROM  ///< Reverse addTo operation
45        };
46
47private:
48        /// Type of node height
49        using Height = unsigned char;
50
51        /// Disjoint-set node
52        struct Node {
53                Elm parent;     ///< Parent node in equivalence class
54                Elm next;       ///< Next node in equivalence class
55                Height height;  ///< Tree height of the node
56
57                template<typename E>
58                Node(E&& e) : parent(e), next(std::forward<E>(e)), height(0) {}
59
60                template<typename E, typename F>
61                Node(E&& p, F&& n, Height h) 
62                        : parent(std::forward<E>(p)), next(std::forward<F>(n)), height(h) {}
63        };
64
65        /// Type of class map
66        using Base = std::unordered_map<Elm, Node, Hash, Eq>;
67
68        /// Node inserted into underlying map as new equivalence class
69        struct Add {
70                Self* base;  ///< Modified map
71                Elm root;    ///< Element added
72
73                template<typename E>
74                Add(Self* b, E&& r) : base(b), root(std::forward<E>(r)) {}
75        };
76
77        /// Two classes merged
78        struct AddTo {
79                Self* base;       ///< Modified map
80                Elm root;         ///< Root node
81                Elm child;        ///< Child node, formerly root of own class
82                bool new_height;  ///< Did the root node's height change?
83
84                template<typename R, typename C>
85                AddTo(Self* b, R&& r, C&& c, bool h) 
86                        : base(b), root(std::forward<R>(r)), child(std::forward<C>(c)), new_height(h) {}
87        };
88
89        /// Underlying storage
90        union Data {
91                char none;
92                Base base;
93                Add add;
94                AddTo add_to;
95
96                Data() : none('\0') {}
97                ~Data() {}
98        } data;
99
100        /// Type of version node
101        mutable Mode mode;
102
103        /// get mutable reference as T
104        template<typename T>
105        T& as() { return reinterpret_cast<T&>(data); }
106
107        /// get const reference as T
108        template<typename T>
109        const T& as() const { return reinterpret_cast<const T&>(data); }
110
111        /// get rvalue reference as T
112        template<typename T>
113        T&& take_as() { return std::move(as<T>()); }
114
115        /// initialize as T
116        template<typename T, typename... Args>
117        void init( Args&&... args ) {
118                new( &as<T>() ) T { std::forward<Args>(args)... };
119        }
120
121        /// reset according to current mode
122        void reset() {
123                switch( mode ) {
124                        case BASE:                as<Base>().~Base();   break;
125                        case ADD: case REM:       as<Add>().~Add();     break;
126                        case ADDTO: case REMFROM: as<AddTo>().~AddTo(); break;
127                        default: assertf(false, "invalid mode");
128                }
129        }
130
131        PersistentDisjointSet( Mode m, Base&& b ) : data(), mode(m) {
132                assertf(m == BASE, "invalid mode");
133                init<Base>(std::move(b));
134        }
135
136        template<typename R>
137        PersistentDisjointSet( Mode m, const Self* b, R&& r ) : data(), mode(m) {
138                assertf(m == ADD || m == REM, "invalid mode");
139                init<Add>(b, std::forward<R>(r));
140        }
141
142        template<typename R, typename C>
143        PersistentDisjointSet( Mode m, const Self* b, R&& r, C&& c, bool h ) : data(), mode(m) {
144                assertf(m == ADDTO || m == REMFROM, "invalid mode");
145                init<AddTo>(b, std::forward<R>(r), std::forward<C>(c), h);
146        }
147
148        /// Adds (also removes) graph edges.
149        /// * `from.parent` updated to `new_root`,
150        /// * `from.next` and `to.next` swapped (splices or un-splices class lists)
151        /// * `to.height` adjusted by change
152        template<typename R>
153        static void addEdge( Node& from, Node& to, R&& new_root, Height change ) {
154                from.parent = std::forward<R>(new_root);
155                std::swap(from.next, to.next);
156                to.height += change;
157        }
158
159protected:
160        void trace( const GC& gc ) const {
161                switch( mode ) {
162                        case BASE: {
163                                for (const auto& entry : as<Base>()) {
164                                        gc << entry.first;
165                                }
166                                return;
167                        }
168                        case ADD: case REM: {
169                                const Add& self = as<Add>();
170                                gc << self.base << self.root;
171                                return;
172                        }
173                        case ADDTO: case REMFROM: {
174                                const AddTo& self = as<AddTo>();
175                                gc << self.base << self.root << self.child;
176                                return;
177                        }
178                        default: assertf(false, "invalid mode");
179                }
180        }
181
182public:
183        using size_type = std::size_t;
184
185        using iterator = typename Base::const_iterator;
186
187        PersistentDisjointSet() : data(), mode(BASE) { init<Base>(); }
188
189        PersistentDisjointSet( const Self& o ) = delete;
190
191        Self& operator= ( const Self& o ) = delete;
192
193        ~PersistentDisjointSet() { reset(); }
194
195        /// reroot persistent data structure at current node
196        void reroot() const {
197                if ( mode == BASE ) return;
198
199                // reroot base
200                Self* mut_this = const_cast<Self*>(this);
201                Self* base = ( mode == ADD || mode == REM ) ? 
202                        mut_this->as<Add>().base : 
203                        mut_this->as<AddTo>().base;
204                base->reroot();
205                assertf(base->mode == BASE, "reroot results in base");
206
207                // take map out of base
208                Base base_map = base->take_as<Base>();
209                base->reset();
210
211                // switch base to inverse of self and mutate base map
212                switch ( mode ) {
213                        case ADD: {
214                                Add& self = mut_this->as<Add>();
215
216                                base->init<Add>( mut_this, self.root );
217                                base->mode = REM;
218
219                                base_map.emplace( self.root, Node{ std::move(self.root) } );
220                        } break;
221                        case REM: {
222                                Add& self = mut_this->as<Add>();
223
224                                base->init<Add>( mut_this, self.root );
225                                base->mode = ADD;
226
227                                base_map.erase( self.root );
228                        } break;
229                        case ADDTO: {
230                                AddTo& self = mut_this->as<AddTo>();
231
232                                base->init<AddTo>( mut_this, self.root, self.child, self.new_height );
233                                base->mode = REMFROM;
234
235                                auto child_it = base_map.find( self.child );
236                                auto root_it = base_map.find( self.root );
237                                assertf(child_it != base_map.end() && root_it != base_map.end(), 
238                                        "nodes must exist in base");
239                                Node& child = child_it->second;
240                                Node& root = root_it->second;
241
242                                addEdge( child, root, std::move(self.root), Height(self.new_height) );
243                        } break;
244                        case REMFROM: {
245                                AddTo& self = mut_this->as<AddTo>();
246
247                                base->init<AddTo>( mut_this, self.root, self.child, self.new_height );
248                                base->mode = ADDTO;
249
250                                auto child_it = base_map.find( self.child );
251                                auto root_it = base_map.find( self.root );
252                                assertf(child_it != base_map.end() && root_it != base_map.end(), 
253                                        "nodes must exist in base");
254                                Node& child = child_it->second;
255                                Node& root = root_it->second;
256
257                                addEdge( child, root, std::move(self.child), Height(-1 * self.new_height) );
258                        } break;
259                        default: assertf(false, "invalid mode");
260                }
261
262                // set base map into self
263                mut_this->reset();
264                mut_this->init<Base>( std::move(base_map) );
265                mode = BASE;
266        }
267
268private:
269        /// Gets the base after rerooting at the current node
270        const Base& rerooted() const {
271                reroot();
272                return as<Base>();
273        }
274
275public:
276        /// true if the set of sets is empty
277        bool empty() const { return rerooted().empty(); }
278
279        /// Get number of entries in the map
280        size_type size() const { return rerooted().size(); }
281
282        /// Get begin iterator for map; may be invalidated by calls to non-iteration functions
283        /// or functions on other maps in the same chain
284        iterator begin() const { return rerooted().begin(); }
285
286        /// Get end iterator for map; may be invalidated by calls to non-iteration functions
287        /// or functions on other maps in the same chain
288        iterator end() const { return rerooted().end(); }
289
290        /// Check if value is present
291        size_type count(Elm i) const { return rerooted().count( i ); }
292
293        /// Finds root for element i, assertfs i is present
294        Elm find(Elm i) const {
295                const Base& self = rerooted();
296               
297                auto it = self.find( i );
298                while (true) {
299                        assertf(it != self.end(), "find target not present");
300
301                        if ( it->first == it->second.parent ) return it->first;
302
303                        it = self.find( it->second.parent );
304                }
305        }
306
307        /// Finds root for element i, or default if i is not present
308        template<typename E>
309        Elm find_or_default(Elm i, E&& d) const {
310                const Base& self = rerooted();
311
312                auto it = self.find( i );
313                if ( it == self.end() ) return d;
314
315                while ( it->first != it->second.parent ) {
316                        it = self.find( it->second.parent );
317
318                        assertf(it != self.end(), "find target not present");
319                }
320                return it->first;
321        }
322
323        /// Adds fresh class including only one item; returns updated map
324        template<typename E>
325        Self* add(E&& i) {
326                reroot();
327               
328                // transfer map to new node
329                Self* ret = new Self{ BASE, take_as<Base>() };
330                reset();
331
332                // set self to REM node
333                init<Add>( ret, i );
334                mode = REM;
335               
336                // add element in returned map
337                Base& base_map = ret->as<Base>();
338                bool added = base_map.emplace( i, Node{ std::forward<E>(i) } ).second;
339                assertf(added, "added element already present in map");
340
341                return ret;
342        }
343
344        /// Merges two classes given by their roots; returns updated map.
345        /// If two classes have same height, `i` is new root.
346        Self* merge(Elm i, Elm j) {
347                reroot();
348
349                // transfer map to new node
350                Self* ret = new Self{ BASE, take_as<Base>() };
351                reset();
352
353                // find set nodes
354                Base& base_map = ret->as<Base>();
355                auto it = base_map.find( i );
356                auto jt = base_map.find( j );
357                assertf(it != base_map.end() && jt != base_map.end(), "nodes must exist in base");
358                Node& in = it->second;
359                Node& jn = jt->second;
360
361                // update returned map and set self to appropriate REMFROM node
362                if ( in.height < jn.height ) {
363                        addEdge( in, jn, j, 0 );
364                        init<AddTo>( ret, j, i, false );
365                } else if ( jn.height < in.height ) {
366                        addEdge( jn, in, i, 0 );
367                        init<AddTo>( ret, i, j, false );
368                } else /* if ( jn.height == in.height ) */ {
369                        addEdge( jn, in, i, 1 );
370                        init<AddTo>( ret, i, j, true );
371                }
372                mode = REMFROM;
373
374                return ret;
375        }
376
377        /// Reports all members of a class to `out`; none if i does not exist in map
378        template<typename OutputIterator>
379        void find_class(Elm i, OutputIterator&& out) const {
380                const Base& self = rerooted();
381
382                // skip empty classes
383                if ( ! self.count( i ) ) return;
384
385                Elm crnt = i;
386                do {
387                        *out++ = crnt;
388                        auto it = self.find( crnt );
389                        assertf( it != self.end(), "current node must exist in base" );
390                        crnt = it->second.next;
391                } while ( crnt != i );
392        }
393
394        /// Get version node type
395        Mode get_mode() const { return mode; }
396
397        /// Get next version up the revision tree (self if base node)
398        const Self* get_base() const {
399                switch ( mode ) {
400                        case BASE:                return this;
401                        case ADD: case REM:       return as<Add>().base;
402                        case ADDTO: case REMFROM: return as<AddTo>().base;
403                        default: assertf(false, "invalid mode");
404                }
405        }
406
407        /// Get root of new class formed/removed/split (undefined if called on base)
408        Elm get_root() const {
409                switch ( mode ) {
410                        case ADD: case REM:       return as<Add>().root;
411                        case ADDTO: case REMFROM: return as<AddTo>().root;
412                        default: assertf(false, "invalid mode for get_root()");
413                }
414        }
415
416        /// Get child root of new class formed/split (undefined if called on base or add/remove node)
417        Elm get_child() const {
418                switch ( mode ) {
419                        case ADDTO: case REMFROM: return as<AddTo>().child;
420                        default: assertf(false, "invalid mode for get_child()");
421                }
422        }
423
424        /// Gets acted-upon key for new class (root unless for add/remove node, child for add to/remove
425        /// from node, undefined otherwise)
426        Elm get_key() const {
427                switch ( mode ) {
428                        case ADD: case REM:       return as<Add>().root;
429                        case ADDTO: case REMFROM: return as<AddTo>().child;
430                        default: assertf(false, "invalid mode for get_key()");
431                }
432        }
433};
434
435// Local Variables: //
436// tab-width: 4 //
437// mode: c++ //
438// compile-command: "make install" //
439// End: //
Note: See TracBrowser for help on using the repository browser.