containers/ntree.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
#ifndef STLPLUS_NTREE
#define STLPLUS_NTREE
////////////////////////////////////////////////////////////////////////////////
 
//   Author:    Andy Rushton
//   Copyright: (c) Southampton University 1999-2004
//              (c) Andy Rushton           2004 onwards
//   License:   BSD License, see ../docs/license.html
 
//   A templated n-ary tree data structure. STL-like but the definition of
//   iterators is really only applicable to one-dimensional structures. I use
//   iterators to access tree nodes, but there is no increment or decrement
//   operators for them. I also define prefix and postfix traversal iterators
//   which do have increment.
 
////////////////////////////////////////////////////////////////////////////////
#include "containers_fixes.hpp"
#include "exceptions.hpp"
#include "safe_iterator.hpp"
#include <vector>
#include <iterator>
 
namespace stlplus
{
 
  ////////////////////////////////////////////////////////////////////////////////
  // Internals
 
  template<typename T> class ntree_node;
  template<typename T> class ntree;
  template<typename T, typename TRef, typename TPtr> class ntree_iterator;
  template<typename T, typename TRef, typename TPtr> class ntree_prefix_iterator;
  template<typename T, typename TRef, typename TPtr> class ntree_postfix_iterator;
 
  ////////////////////////////////////////////////////////////////////////////////
  // Iterators
 
  // Simple iterators which are just used as pointers to tree nodes.
  // this is not really an iterator - it is a reference, i.e. an iterator with no increment
  // unfortunately the STL does not provide for this rather useful concept
  // I have called it an iterator for consistency, but it is really an element reference
  // An uninitialised iterator is null - similarly, if you ask for the root of an empty tree or the parent of
  // the root node then you get a null iterator.
 
  template<typename T, typename TRef, typename TPtr>
  class ntree_iterator : public safe_iterator<ntree<T>,ntree_node<T> >
  {
  public:
    // local type definitions
 
    // iterator traits, were inherited from std::iterator but inheriting from that was deprecated in C++17
    // there is no relevant iterator category since this is not incremenable
    typedef void iterator_category;
    typedef T value_type;
    typedef TPtr pointer;
    typedef TRef reference;
    typedef void difference_type;
 
    // an iterator points to an object whilst a const_iterator points to a const object
    typedef ntree_iterator<T,T&,T*> iterator;
    typedef ntree_iterator<T,const T&,const T*> const_iterator;
    typedef ntree_iterator<T,TRef,TPtr> this_iterator;
 
    // constructor to create a null iterator - you must assign a valid value to this iterator before using it
    ntree_iterator(void);
    ~ntree_iterator(void);
 
    // Type conversion methods allow const_iterator and iterator to be converted
    const_iterator constify(void) const;
    iterator deconstify(void) const;
 
    // tests useful for putting iterators into other STL structures and for testing whether iteration has completed
    bool operator == (const this_iterator& r) const;
    bool operator != (const this_iterator& r) const;
    bool operator < (const this_iterator& r) const;
 
    // access the node data - a const_iterator gives you a const element, an iterator a non-const element
    // it is illegal to dereference an invalid (i.e. null or end) iterator
    // exceptions: null_dereference,end_dereference
    reference operator*(void) const;
    // exceptions: null_dereference,end_dereference
    pointer operator->(void) const;
 
    friend class ntree<T>;
    friend class ntree_prefix_iterator<T,TRef,TPtr>;
    friend class ntree_postfix_iterator<T,TRef,TPtr>;
 
  public:
    // Note: I had to make this public to get round a problem implementing persistence - it should be private
    // you cannot create a valid iterator except by calling an ntree method that returns one
    // constructor used by ntree to create a non-null iterator
    explicit ntree_iterator(ntree_node<T>* node);
    // constructor used by ntree to create an end iterator
    explicit ntree_iterator(const ntree<T>* owner);
    // used to create an alias of an iterator
    explicit ntree_iterator(const safe_iterator<ntree<T>, ntree_node<T> >& iterator);
  };
 
  // Traversal iterators are like iterators but they have increment operators (++)
  // - prefix_iterator visits the nodes of the tree in prefix order
  // - postfix_iterator visits the nodes of the tree in postfix order.
  // There is no such thing as infix order for an n-ary tree and you cannot
  // traverse backwards with these iterators. These follow the STL convention in
  // that you iterate from a begin to an end - in this case ntree exports
  // prefix_begin()/prefix_end() and postfix_begin()/postfix_end(). You can
  // simplify these iterators to the basic iterator above for functions that
  // require a simple iterator.
 
  template<typename T, typename TRef, typename TPtr>
  class ntree_prefix_iterator
  {
  public:
 
    // iterator traits, were inherited from std::iterator but inheriting from that was deprecated in C++17
    // the ntree iterators are unidirectional
    typedef std::forward_iterator_tag iterator_category;
    typedef T value_type;
    typedef TPtr pointer;
    typedef TRef reference;
    // this is not random access, so declare a void difference type, not sure this is supported by everything
    // typedef std::ptrdiff_t  difference_type;
    typedef void difference_type;
 
    // an iterator points to an object whilst a const_iterator points to a const object
    typedef ntree_prefix_iterator<T,T&,T*>             iterator;
    typedef ntree_prefix_iterator<T,const T&,const T*> const_iterator;
    typedef ntree_prefix_iterator<T,TRef,TPtr>         this_iterator;
    typedef ntree_iterator<T,TRef,TPtr>                simple_iterator;
 
    // constructor to create a null iterator - you must assign a valid value to this iterator before using it
    ntree_prefix_iterator(void);
    ~ntree_prefix_iterator(void);
 
    // tests
    // a null iterator is one that has not been initialised with a value yet
    // i.e. you just declared it but didn't assign to it
    bool null(void) const;
    // an end iterator is one that points to the end element of the list of nodes
    // in STL conventions this is one past the last valid element and must not be dereferenced
    bool end(void) const;
    // a valid iterator is one that can be dereferenced
    // i.e. non-null and non-end
    bool valid(void) const;
 
    // Type conversion methods allow const_iterator and iterator to be converted
    // convert an iterator/const_iterator to a const_iterator
    const_iterator constify(void) const;
    iterator deconstify(void) const;
 
    // generate a simple iterator from a traversal iterator
    simple_iterator simplify(void) const;
 
    // tests useful for putting iterators into other STL structures and for testing whether iteration has completed
    bool operator == (const this_iterator& r) const;
    bool operator != (const this_iterator& r) const;
    bool operator < (const this_iterator& r) const;
 
    // increment/decrement operators used to step through the set of all nodes in a graph
    // it is only legal to increment a valid iterator
    // pre-increment
    // exceptions: null_dereference,end_dereference
    this_iterator& operator ++ (void);
    // post-increment
    // exceptions: null_dereference,end_dereference
    this_iterator operator ++ (int);
 
    // access the node data - a const_iterator gives you a const element, an iterator a non-const element
    // it is illegal to dereference an invalid (i.e. null or end) iterator
    // exceptions: null_dereference,end_dereference
    reference operator*(void) const;
    // exceptions: null_dereference,end_dereference
    pointer operator->(void) const;
 
    friend class ntree<T>;
    friend class ntree_iterator<T,TRef,TPtr>;
 
  private:
    simple_iterator m_iterator;
 
    explicit ntree_prefix_iterator(const simple_iterator& i);
    const simple_iterator& get_iterator(void) const;
    simple_iterator& get_iterator(void);
  };
 
  ////////////////////////////////////////////////////////////////////////////////
 
  template<typename T, typename TRef, typename TPtr>
  class ntree_postfix_iterator
  {
  public:
 
    // iterator traits, were inherited from std::iterator but inheriting from that was deprecated in C++17
    // the ntree iterators are unidirectional
    typedef std::forward_iterator_tag iterator_category;
    typedef T value_type;
    typedef TPtr pointer;
    typedef TRef reference;
    // this is not random access, so declare a void difference type, not sure this is supported by everything
    // typedef std::ptrdiff_t  difference_type;
    typedef void difference_type;
 
    typedef ntree_postfix_iterator<T,T&,T*>             iterator;
    typedef ntree_postfix_iterator<T,const T&,const T*> const_iterator;
    typedef ntree_postfix_iterator<T,TRef,TPtr>         this_iterator;
    typedef ntree_iterator<T,TRef,TPtr>                 simple_iterator;
 
    // constructor to create a null iterator - you must assign a valid value to this iterator before using it
    ntree_postfix_iterator(void);
    ~ntree_postfix_iterator(void);
 
    // tests
    // a null iterator is one that has not been initialised with a value yet
    // i.e. you just declared it but didn't assign to it
    bool null(void) const;
    // an end iterator is one that points to the end element of the list of nodes
    // in STL conventions this is one past the last valid element and must not be dereferenced
    bool end(void) const;
    // a valid iterator is one that can be dereferenced
    // i.e. non-null and non-end
    bool valid(void) const;
 
    // Type conversion methods allow const_iterator and iterator to be converted
    // convert an iterator/const_iterator to a const_iterator
    const_iterator constify(void) const;
    iterator deconstify(void) const;
 
    // generate a simple iterator from a traversal iterator
    simple_iterator simplify(void) const;
 
    // tests useful for putting iterators into other STL structures and for testing whether iteration has completed
    bool operator == (const this_iterator& r) const;
    bool operator != (const this_iterator& r) const;
    bool operator < (const this_iterator& r) const;
 
    // increment/decrement operators used to step through the set of all nodes in a graph
    // it is only legal to increment a valid iterator
    // pre-increment
    // exceptions: null_dereference,end_dereference
    this_iterator& operator ++ (void);
    // post-increment
    // exceptions: null_dereference,end_dereference
    this_iterator operator ++ (int);
 
    // access the node data - a const_iterator gives you a const element, an iterator a non-const element
    // it is illegal to dereference an invalid (i.e. null or end) iterator
    // exceptions: null_dereference,end_dereference
    reference operator*(void) const;
    // exceptions: null_dereference,end_dereference
    pointer operator->(void) const;
 
    friend class ntree<T>;
    friend class ntree_iterator<T,TRef,TPtr>;
 
  private:
    simple_iterator m_iterator;
 
    explicit ntree_postfix_iterator(const simple_iterator& i);
    const simple_iterator& get_iterator(void) const;
    simple_iterator& get_iterator(void);
  };
 
  ////////////////////////////////////////////////////////////////////////////////
  // The Ntree class
  ////////////////////////////////////////////////////////////////////////////////
 
  template<typename T>
  class ntree
  {
  public:
    // STL-like typedefs for the types and iterators
    typedef T value_type;
 
    typedef ntree_iterator<T,T&,T*> iterator;
    typedef ntree_iterator<T,const T&,const T*> const_iterator;
 
    typedef ntree_prefix_iterator<T,T&,T*> prefix_iterator;
    typedef ntree_prefix_iterator<T,const T&,const T*> const_prefix_iterator;
 
    typedef ntree_postfix_iterator<T,T&,T*> postfix_iterator;
    typedef ntree_postfix_iterator<T,const T&,const T*> const_postfix_iterator;
 
    typedef std::vector<iterator> iterator_vector;
    typedef std::vector<const_iterator> const_iterator_vector;
 
    //////////////////////////////////////////////////////////////////////////////
    // Constructors, destructors and copies
 
    ntree(void);
    ~ntree(void);
 
    // copy constructor and assignment both copy the tree
    ntree(const ntree<T>&);
    ntree<T>& operator=(const ntree<T>&);
 
    //////////////////////////////////////////////////////////////////////////////
    // size tests
 
    // tests on whole tree
    bool empty(void) const;
    unsigned size(void) const;
 
    // tests for number of nodes in subtree starting at node
    // exceptions: wrong_object,null_dereference,end_dereference
    unsigned size(const const_iterator& node) const;
    // exceptions: wrong_object,null_dereference,end_dereference
    unsigned size(const iterator& node);
 
    // test for depth of tree from root to node
    // exceptions: wrong_object,null_dereference,end_dereference
    unsigned depth(const const_iterator& node) const;
    // exceptions: wrong_object,null_dereference,end_dereference
    unsigned depth(const iterator& node);
 
    //////////////////////////////////////////////////////////////////////////////
    // direct traversal
 
    // get the root node's iterator to start the traversal
    const_iterator root(void) const;
    iterator root(void);
 
    // get the number of children of this node, so they can be accessed 0..n-1
    // exceptions: wrong_object,null_dereference,end_dereference
    unsigned children(const const_iterator& node) const;
    // exceptions: wrong_object,null_dereference,end_dereference
    unsigned children(const iterator& node);
 
    // get the iterator for a child given it's offset into the children array
    // exceptions: wrong_object,null_dereference,end_dereference,std::out_of_range
    const_iterator child(const const_iterator& node, unsigned child) const;
    // exceptions: wrong_object,null_dereference,end_dereference,std::out_of_range
    iterator child(const iterator& node, unsigned child);
 
    // search a node's children array to find a child given its iterator
    // exceptions: wrong_object,null_dereference,end_dereference
    unsigned child_offset(const const_iterator& node, const const_iterator& child) const;
    // exceptions: wrong_object,null_dereference,end_dereference
    unsigned child_offset(const iterator& node, const iterator& child);
 
    // go back up the tree by getting the iterator to a node's parent - the parent of root is null
    // exceptions: wrong_object,null_dereference,end_dereference
    const_iterator parent(const const_iterator& node) const;
    // exceptions: wrong_object,null_dereference,end_dereference
    iterator parent(const iterator& node);
 
    //////////////////////////////////////////////////////////////////////////////
    // iterator traversal
 
    const_prefix_iterator prefix_begin(void) const;
    prefix_iterator prefix_begin(void);
    const_prefix_iterator prefix_end(void) const;
    prefix_iterator prefix_end(void);
 
    const_postfix_iterator postfix_begin(void) const;
    postfix_iterator postfix_begin(void);
    const_postfix_iterator postfix_end(void) const;
    postfix_iterator postfix_end(void);
 
    //////////////////////////////////////////////////////////////////////////////
    // breadth-first traversal
 
    const_iterator_vector breadth_first_traversal(void) const;
    iterator_vector breadth_first_traversal(void);
 
    //////////////////////////////////////////////////////////////////////////////
    // modification
 
    // insert a node
 
    // discard previous contents and create a new root node
    iterator insert(const T&);
    // add a new child inserted into the node's children at the specified place
    // exceptions: wrong_object,null_dereference,end_dereference,std::out_of_range
    iterator insert(const iterator& node, unsigned child, const T&);
    // shortcut for insert at the end i.e. tree.insert(node, node.children(), value)
    // exceptions: wrong_object,null_dereference,end_dereference
    iterator insert(const iterator& node, const T&);
    // old name for the above
    // exceptions: wrong_object,null_dereference,end_dereference
    iterator append(const iterator& node, const T&);
 
    // insert a copy of a subtree
 
    // discard previous contents and copy the tree
    iterator insert(const ntree<T>&);
    // add a copy of the tree as a new child inserted into the node's children at the specified place
    // exceptions: wrong_object,null_dereference,end_dereference,std::out_of_range
    iterator insert(const iterator& node, unsigned child, const ntree<T>&);
    // shortcut for insert at the end i.e. tree.insert(node, node.children(), value)
    // exceptions: wrong_object,null_dereference,end_dereference
    iterator insert(const iterator& node, const ntree<T>&);
    // old name for the above
    // exceptions: wrong_object,null_dereference,end_dereference
    iterator append(const iterator& node, const ntree<T>&);
 
    // insert the subtree without copying
 
    // discard previous contents and move the tree without copying
    // invalidates all iterators to the old tree
    iterator move(ntree<T>&);
    // move the tree to become the designated child
    // invalidates all iterators to the old tree
    // exceptions: wrong_object,null_dereference,end_dereference,std::out_of_range
    iterator move(const iterator& node, unsigned child, ntree<T>&);
    // shortcut for move to the last child i.e. node.move(node, node.children(), value)
    // exceptions: wrong_object,null_dereference,end_dereference
    iterator move(const iterator& node, ntree<T>&);
 
    // insert/erase in the middle of a tree
 
    // replace the node with the new value, pushing the old node down to make it the child
    // returns the iterator to the new, pushed node
    // exceptions: wrong_object,null_dereference,end_dereference
    iterator push(const iterator& node, const T&);
    // erases the specified child, moving its children up to become the node's children
    // exceptions: wrong_object,null_dereference,end_dereference
    void pop(const iterator& node, unsigned child);
 
    // erase nodes and subtrees
 
    // erase the whole tree
    void erase(void);
    // erase the node and all its children
    // exceptions: wrong_object,null_dereference,end_dereference
    void erase(const iterator& node);
    // erase the specified child
    // exceptions: wrong_object,null_dereference,end_dereference,std::out_of_range
    void erase_child(const iterator& node, unsigned child);
    // synonym for above
    // exceptions: wrong_object,null_dereference,end_dereference,std::out_of_range
    void erase(const iterator& node, unsigned child);
    // erase all children of the node, but leave the node
    // exceptions: wrong_object,null_dereference,end_dereference,std::out_of_range
    void erase_children(const iterator& node);
 
    // extract a subtree as a copy leaving the original tree unchanged
 
    // get a copy of the tree as a tree
    ntree<T> subtree(void);
    // get a copy of the subtree as a tree with the specified node as root
    // exceptions: wrong_object,null_dereference,end_dereference
    ntree<T> subtree(const iterator& node);
    // get a copy of the subtree as a tree with the specified child as root
    // exceptions: wrong_object,null_dereference,end_dereference,std::out_of_range
    ntree<T> subtree(const iterator& node, unsigned child);
 
    // extract a subtree by moving the contents
 
    // move the whole tree to make a new tree
    ntree<T> cut(void);
    // move the subtree to make a new tree with the specified node as root
    // exceptions: wrong_object,null_dereference,end_dereference
    ntree<T> cut(const iterator& node);
    // move the subtree to make a new tree with the specified child as root
    // exceptions: wrong_object,null_dereference,end_dereference,std::out_of_range
    ntree<T> cut(const iterator& node, unsigned child);
 
    // re-ordering of child nodes
 
    // reorder the children of a node
    // moves the child at the given offset to the new offset, reordering its siblings to make room
    // this preserves the order of the remaining siblings but not their positions e.g. reorder([a,b,c,d],0,3) = [b,c,d,a]
    // exceptions: wrong_object,null_dereference,end_dereference,std::out_of_range
    void reorder(const iterator& node, unsigned child_offset, unsigned new_offset);
 
    // swap two children of a node
    // swaps the child at the given offset with the new offset
    // this preserves the position of the remaining siblings e.g. swap([a,b,c,d],0,3) = [d,b,c,a]
    // exceptions: wrong_object,null_dereference,end_dereference,std::out_of_range
    void swap(const iterator& node, unsigned child1, unsigned child2);
 
    //////////////////////////////////////////////////////////////////////////////
 
  private:
    ntree_node<T>* m_root;
  };
 
  ////////////////////////////////////////////////////////////////////////////////
 
} // end namespace stlplus
 
#include "ntree.tpp"
#endif