feisty meow concerns codebase 2.140
tree.cpp
Go to the documentation of this file.
1/*****************************************************************************\
2* *
3* Name : tree *
4* Author : Chris Koeritz *
5* *
6*******************************************************************************
7* Copyright (c) 1992-$now By Author. This program is free software; you can *
8* redistribute it and/or modify it under the terms of the GNU General Public *
9* License as published by the Free Software Foundation; either version 2 of *
10* the License or (at your option) any later version. This is online at: *
11* http://www.fsf.org/copyleft/gpl.html *
12* Please send any updates to: fred@gruntose.com *
13\*****************************************************************************/
14
15#include "tree.h"
16
18#include <basis/functions.h>
19#include <basis/guards.h>
21#include <structures/stack.h>
22
23//#define DEBUG_TREE
24 // uncomment if you want lots of debugging info.
25
26#undef LOG
27#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), astring(s) + a_sprintf(" [obj=%p]", this))
28
29using namespace basis;
30using namespace loggers;
31using namespace structures;
32
33namespace nodes {
34
35const int BACKWARDS_BRANCH = 0;
36 // BACKWARDS_BRANCH is the branch from this tree to its parent. this is
37 // steeped in the perspective that the root is the backwards direction (where
38 // we came from, in a sense) and that the children of this node are the
39 // forwards direction.
40
42
43// iterator methods:
44
46: path(initial), _order(direction), _aim(AWAY_FROM_ROOT)
47{
48}
49
50tree::iterator::~iterator() { while (size()) pop(); }
51
52bool tree::iterator::next_node(tree *&to_return)
53{
54#ifdef DEBUG_TREE
55 FUNCDEF("next_node");
56#endif
57 to_return = NULL_POINTER;
58#ifdef DEBUG_TREE
59 if ( (_order != to_branches)
60 && (_order != reverse_branches) ) {
61 if (_aim == AWAY_FROM_ROOT) LOG("going down...")
62 else LOG("going up...");
63 }
64#endif
65 switch (_order) {
66 case prefix: {
67 if (_aim == AWAY_FROM_ROOT) {
68 // going down means this is the first time we have seen the current top
69 // node on the stack.
70 to_return = (tree *)(*this)[size() - 1];
71#ifdef DEBUG_TREE
72// LOG(a_sprintf("[%s] ", to_return->get_contents()->held().s());
73 if (to_return->branches()) LOG("pushing 0")
74 else LOG("switching direction");
75#endif
76 if (to_return->branches())
77 push(to_return->branch(0));
78 else
79 _aim = TOWARD_ROOT;
80 } else {
81 // going up means that we need to get rid of some things before we
82 // start seeing new nodes again.
83 if (size() == 1) return false;
84 // the last node has been seen....
85 tree *last = (tree *)pop();
86 tree *current_tree = (tree *)current();
87 int lastnum = current_tree->which(last);
88#ifdef DEBUG_TREE
89 if (lastnum < current_tree->branches() - 1)
90 LOG(a_sprintf("going down %d", lastnum+1))
91 else LOG("still going up");
92#endif
93 if (lastnum < current_tree->branches() - 1) {
94 _aim = AWAY_FROM_ROOT;
95 push(current_tree->branch(lastnum + 1));
96 } // else still going up.
97 }
98 break;
99 }
100 case infix: {
101 if (_aim == AWAY_FROM_ROOT) {
102 // going down means starting on the left branch.
103 tree *temp = (tree *)current();
104#ifdef DEBUG_TREE
105 if (temp->branches()) LOG("pushing 0")
106 else LOG("switching direction");
107#endif
108 if (temp->branches()) push(temp->branch(0));
109 else {
110 _aim = TOWARD_ROOT;
111 to_return = (tree *)current();
112#ifdef DEBUG_TREE
113// LOG(a_sprintf("[%s] ", to_return->get_contents()->held().s()));
114#endif
115 }
116 } else {
117 // going up means that the left branch is done and we need to either
118 // keep going up or go down the right branch.
119 if (size() == 1) return false;
120 // the last node has been seen....
121 tree *last = (tree *)pop();
122 tree *current_tree = (tree *)current();
123 int lastnum = current_tree->which(last);
124#ifdef DEBUG_TREE
125 if (lastnum < 1) LOG(a_sprintf("going down %d", lastnum+1))
126 else LOG("still going up");
127#endif
128 if (lastnum < 1) {
129 _aim = AWAY_FROM_ROOT;
130 to_return = (tree *)current();
131#ifdef DEBUG_TREE
133#endif
134 push(current_tree->branch(lastnum + 1));
135 } // else still going up.
136 }
137 break;
138 }
139 case to_branches: {
140 if (_aim == TOWARD_ROOT) return false;
141 else {
142 if (size() == 1) {
143 tree *temp = (tree *)current();
144 if (!temp->branches())
145 _aim = TOWARD_ROOT;
146 else
147 push(temp->branch(0));
148 } else {
149 tree *last = (tree *)pop();
150 tree *curr = (tree *)current();
151 int lastnum = curr->which(last);
152 if (lastnum < curr->branches() - 1)
153 push(curr->branch(lastnum + 1));
154 else _aim = TOWARD_ROOT;
155 to_return = last;
156 }
157 }
158 break;
159 }
160 case reverse_branches: {
161 if (_aim == TOWARD_ROOT) return false;
162 else {
163 if (size() == 1) {
164 tree *temp = (tree *)current();
165 if (!temp->branches()) _aim = TOWARD_ROOT;
166 else push(temp->branch(temp->branches() - 1));
167 } else {
168 tree *last = (tree *)pop();
169 tree *curr = (tree *)current();
170 int lastnum = curr->which(last);
171 if (lastnum > 0) push(curr->branch(lastnum - 1));
172 else _aim = TOWARD_ROOT;
173 to_return = last;
174 }
175 }
176 break;
177 }
178 default: // intentional fall-through to postfix.
179 case postfix: {
180 if (_aim == AWAY_FROM_ROOT) {
181 // going down means that the bottom is still being sought.
182 tree *temp = (tree *)current();
183#ifdef DEBUG_TREE
184 if (temp->branches()) LOG("pushing 0")
185 else LOG("switching direction");
186#endif
187 if (temp->branches()) push(temp->branch(0));
188 else _aim = TOWARD_ROOT;
189 } else {
190 // going up means that all nodes below current have been hit.
191 if (!size()) return false; // the last node has been seen...
192 else if (size() == 1) {
193 to_return = (tree *)pop();
194 // this is the last node.
195 return true;
196 }
197 tree *last = (tree *)pop();
198 to_return = last;
199#ifdef DEBUG_TREE
201#endif
202 tree *current_tree = (tree *)current();
203 int lastnum = current_tree->which(last);
204#ifdef DEBUG_TREE
205 if (lastnum < current_tree->branches() - 1)
206 LOG(a_sprintf("going down %d", lastnum+1))
207 else LOG("still going up");
208#endif
209 if (lastnum < current_tree->branches() - 1) {
210 _aim = AWAY_FROM_ROOT;
211 push(current_tree->branch(lastnum + 1));
212 } // else still going up.
213 }
214 break;
215 }
216 }
217 return true;
218 // it is assumed that termination conditions cause a return earlier on.
219}
220
222{
223#ifdef DEBUG_TREE
224 FUNCDEF("whack");
225#endif
226 if (!to_whack) return; // that's a bad goof.
227 if (size()) {
228 if (to_whack == current()) {
229 // we found that this is the one at the top right now.
230 pop();
231#ifdef DEBUG_TREE
232 LOG("found node in current top; removing it there.");
233#endif
234 } else if (to_whack->parent() == current()) {
235 // the parent is the current top. make sure we don't mess up traversal.
236#ifdef DEBUG_TREE
237 LOG("found node's parent as current top; don't know what to do.");
238#endif
239 } else {
240#ifdef DEBUG_TREE
241 LOG("found no match for either node to remove or parent in path.");
242#endif
243 }
244 }
245 tree *parent = to_whack->parent();
246 if (!parent) {
247#ifdef DEBUG_TREE
248 LOG("no parent node for the one to whack! would have whacked "
249 "root of tree!");
250#endif
251 } else {
252 parent->prune(to_whack);
253 WHACK(to_whack);
254 }
255}
256
258{
259#ifdef DEBUG_TREE
260 FUNCDEF("next");
261#endif
262 tree *to_return = NULL_POINTER;
263 bool found_tree = false;
264 while (!found_tree) {
265 bool still_running = next_node(to_return);
266 if (to_return || !still_running) found_tree = true;
267 }
268 return to_return;
269}
270
272
273// tree methods:
274
278
280{
281 FUNCDEF("destructor");
282#ifdef DEBUG_TREE
283 LOG("entry to tree destructor");
284#endif
285 {
286 // must at least unhook ourselves from the parent so we don't become a lost
287 // cousin.
288 tree *my_parent = parent();
289 if (my_parent) my_parent->prune(this);
290 }
291
292#if 0
293 //hmmm: clean this code when it's been examined long enough. maybe already.
294LOG("old code for tree destructor activating...");
295
296 //original version suffers from being too recursive on windoze,
297 //which blows out the stack. linux never showed this problem. so, i
298 //guess i'm glad i tested on windows.
299 //anyway, it's a general problem for a degenerate tree like the one
300 //i've constructed. current version has ~40000 direct descendants of
301 //the root in a single line, so the stack has to support 40000 frames
302 //for the delete implementation below to work.
303
304 // iterate over the child nodes and whack each individually.
305 while (branches()) delete branch(0);
306 // this relies on the child getting out of our branch list.
307#endif
308
309#if 0
310 LOG("new code for tree destructor activating...");
311
312//hmmm: this new code stinks on ice. keeps doing something awful, either double deleting or deleting wrong thing.
313// why didn't we just use our own iterator to traverse the tree depth first and clean that way??
314
315 // newer version of delete doesn't recurse; it just iterates instead,
316 // which avoids the massive recursive depth of the original approach.
317 while (branches()) {
318 // we know we have a branch to work on, due to conditional above.
319 tree *stop_node = branch(0); // don't whack the node we're at until we're done with its kids.
320 tree *curr_node = stop_node;
321 while (curr_node != NULL_POINTER) {
322 // make a breadcrumb for getting back to 'here' in the tree.
323 tree *way_back = curr_node;
324 // our main operation here is to go down a node without using any
325 // stack frames. so we just pick the first kid; it's either valid
326 // or there are no kids at all.
327 curr_node = curr_node->branch(0);
328
329 if (curr_node == NULL_POINTER) {
330 // wayback has no children, so we can take action.
331LOG("tree dtor: can take action on childless node...");
332
333 // if wayback is the same as stop_node, then we exit from iterations since
334 // we've cleaned all the kids out.
335 if (way_back == stop_node) {
336LOG("tree dtor: stopping since at wayback node...");
337 // we can actually delete wayback here, because we are at the stopping point.
338 // and since a tree node whack removes it from its parent, we are clean one branch now.
339 WHACK(way_back);
340LOG("tree dtor: whacked wayback before inner break.");
341 break;
342 }
343
344 // we want to whack the wayback node at this point, since it's a parent
345 // with no kids, i.e. a leaf. we've guaranteed we're not going beyond
346 // our remit, since wayback is not the same node as the top level one
347 // in the destructor (as long as there are no cycles in the tree...).
348 curr_node = way_back->parent(); // go up in tree on next iteration.
349
350LOG("tree dtor: reset currnode, about to whack wayback...");
351
352 if (curr_node == stop_node) {
353LOG("tree dtor: seeing curr_node hits our stop_node!");
355 } else if (curr_node == stop_node->parent()) {
356LOG("tree dtor: seeing curr_node ABOVE our stop_node!");
357 }
358
359 WHACK(way_back); // whack a node, finally.
360
361LOG("tree dtor: after whacking wayback...");
362 } else {
363 // okay, there's a node below here. we will spider down to it.
364LOG("tree dtor: spidering to node below...");
365 continue;
366 }
367 }
368 }
369#endif
370
371#if 1
372#ifdef DEBUG_TREE
373 LOG("newest code for tree destructor (stack based) activating...");
374#endif
375
376 // this version of delete doesn't recurse; it just iterates instead,
377 // which avoids the massive recursive depth of the original approach.
378 // it does use a stack object however, but that will use main memory,
379 // which is usually in greater supply than stack/heap space.
380
381 stack<tree *> sleestak; // accumulates nodes that we still need to clean up.
382
383 // iterate across this node first, to feed the stack.
384 // we feed it in reverse branch order, so that the first branch will be dealt with first (popped off stack first).
385 while (branches()) {
386 // get our current last branch...
387 tree *bran = branch(branches() - 1);
388 // pull that branch off the tree (non-destructively)...
389 prune(bran);
390 // and add that branch to the stack.
391#ifdef DEBUG_TREE
392 LOG(a_sprintf("tree dtor: adding child %p on stack.", bran));
393#endif
394 sleestak.push(bran);
395 }
396
397 // now iterate across our stack until we have dealt with every node in it.
398 while (sleestak.size()) {
399
400 tree *popper = NULL_POINTER;
401 sleestak.acquire_pop(popper);
402#ifdef DEBUG_TREE
403 LOG(a_sprintf("tree dtor: popped a tree %p off stack.", popper));
404#endif
405
406 // familiar scheme below; push last branch first, then we'll pop first branch first.
407 while (popper->branches()) {
408 tree *bran = popper->branch(popper->branches() - 1);
409 popper->prune(bran);
410#ifdef DEBUG_TREE
411 LOG(a_sprintf("tree dtor: inner, adding child %p on stack.", bran));
412#endif
413 sleestak.push(bran);
414 }
415
416 // now the popper gets cleaned up; this should be totally safe since all its kids
417 // have been pruned and put into the stack.
418#ifdef DEBUG_TREE
419 LOG(a_sprintf("tree dtor: about to zap a tree %p.", popper));
420#endif
421 WHACK(popper);
422#ifdef DEBUG_TREE
423 LOG("tree dtor: whacked that tree.");
424#endif
425 }
426#endif
427}
428
430
431int tree::branches() const { return links() - 1; }
432
433tree *tree::branch(int branch_number) const
434{
435 branch_number++;
436 bounds_return(branch_number, 1, branches(), NULL_POINTER);
437 return (tree *)get_link(branch_number);
438}
439
440int tree::which(tree *branch_to_find) const
441{ return node::which((node *)branch_to_find) - 1; }
442
444{
445 const tree *traveler = this;
446 // keep looking at the backwards branch until it is a NULL_POINTER. the tree with
447 // a NULL_POINTER BACKWARDS_BRANCH is the root. return that tree.
448 while (traveler->get_link(BACKWARDS_BRANCH))
449 traveler = (tree *)traveler->get_link(BACKWARDS_BRANCH);
450 return const_cast<tree *>(traveler);
451}
452
453void tree::attach(tree *new_branch)
454{
455 if (!new_branch) return;
456 insert_link(links(), new_branch);
457 new_branch->set_link(BACKWARDS_BRANCH, this);
458}
459
460void tree::insert(int branch_place, tree *new_branch)
461{
462 branch_place++;
464 if (branch_place >= links())
465 branch_place = links() - 1;
466 for (int i = links() - 1; i > branch_place; i--)
467 set_link(i, get_link(i-1));
468 set_link(branch_place, new_branch);
469 new_branch->set_link(BACKWARDS_BRANCH, this);
470}
471
472outcome tree::prune(tree *branch_to_cut)
473{
474 int branch_number = which(branch_to_cut);
475 if (branch_number == basis::common::NOT_FOUND) return basis::common::NOT_FOUND;
476 return prune_index(branch_number);
477}
478
479outcome tree::prune_index(int branch_to_cut)
480{
481 FUNCDEF("prune_index");
482 branch_to_cut++;
483 bounds_return(branch_to_cut, 1, branches(), basis::common::NOT_FOUND);
484#ifdef DEBUG_TREE
485 LOG(a_sprintf("got legit branch %d to cut", branch_to_cut));
486#endif
487 tree *that_branch = (tree *)get_link(branch_to_cut);
488#ifdef DEBUG_TREE
489 LOG(a_sprintf("whacking off branch %p", that_branch));
490#endif
492#ifdef DEBUG_TREE
493 LOG(a_sprintf("zapping link branch %d", branch_to_cut));
494#endif
495 zap_link(branch_to_cut);
496#ifdef DEBUG_TREE
497 LOG("returning, finished.");
498#endif
499 return basis::common::OKAY;
500}
501
502int tree::depth() const
503{
504 tree *my_root = root();
505 const tree *current_branch = this;
506 int deep = 0;
507 while (current_branch != my_root) {
508 current_branch = current_branch->parent();
509 deep++;
510 }
511 return deep;
512}
513
514//probably okay; we want to use this function rather than non-existent
515//node base function which isn't implemented yet.
516bool tree::generate_path(path &to_follow) const
517{
518if (to_follow.size()) {}
519/*
520 tree *traveller = this;
521 path to_accumulate(root());
522 while (traveller->parent() != NULL_POINTER) {
523// int branch_number = traveller->parent()->which(traveller);
524// if (branch_number == BRANCH_NOT_FOUND) non_continuable_error
525// (class_name(), "generate_path", "branch not found during path construction");
526// chunk *to_stuff = new chunk
527// (SELF_OWNED, (byte *)&branch_number, sizeof(int));
528 to_accumulate.push(traveller);
529 traveller = traveller->parent();
530 }
531 // the order of things on the stack needs to be reversed now.
532// path to_return = new stack(*to_accumulate.invert());
533// return to_return;
534 to_accumulate.invert();
535 return to_accumulate;
536*/
537return false;//temp.
538}
539
540//hmmm: need text form!
541
543{ return iterator(this, direction); }
544
545} // namespace.
546
#define LOG(s)
a_sprintf is a specialization of astring that provides printf style support.
Definition astring.h:440
Outcomes describe the state of completion for an operation.
Definition outcome.h:31
An object representing the interstitial cell in most linked data structures.
Definition node.h:41
void zap_link(int link_number)
the specified link is removed from the node.
Definition node.cpp:68
void set_link(int link_number, node *new_link)
Connects the node "new_link" to this node.
Definition node.cpp:62
int links() const
Returns the number of links the node currently holds.
Definition node.cpp:51
void insert_link(int where, node *to_add=NULL_POINTER)
adds a new link prior to the position specified in "where".
Definition node.cpp:74
int which(node *to_find) const
locates the index where "to_find" lives in our list of links.
Definition node.cpp:91
node * get_link(int link_number) const
Returns the node that is connected to the specified "link_number".
Definition node.cpp:85
A method for tracing a route from a tree's root to a particular node.
Definition path.h:36
int size() const
returns the number of items in the path.
Definition path.cpp:55
void whack(tree *to_whack)
destroys the tree "to_whack".
Definition tree.cpp:221
tree * next()
Returns a pointer to the next tree in the direction of traversal.
Definition tree.cpp:257
iterator(const tree *initial, traversal_directions direction)
Definition tree.cpp:45
A dynamically linked tree with an arbitrary number of branches.
Definition tree.h:40
virtual int depth() const
Returns the distance of "this" from the root. The root's depth is 0.
Definition tree.cpp:502
virtual void insert(int branch_place, tree *new_branch)
inserts "new_branch" before the branches starting at "branch_place".
Definition tree.cpp:460
virtual basis::outcome prune_index(int branch_to_cut)
Removes the branch at the specified index from this tree.
Definition tree.cpp:479
virtual int which(tree *branch_to_find) const
Returns the branch number for a particular branch in this tree.
Definition tree.cpp:440
virtual ~tree()
destroys the tree by recursively destroying all child tree nodes.
Definition tree.cpp:279
tree()
constructs a new tree with a root and zero branches.
Definition tree.cpp:275
virtual int branches() const
Returns the number of branches currently connected to this tree.
Definition tree.cpp:431
virtual void attach(tree *new_branch)
Attaches the specified branch to the current tree.
Definition tree.cpp:453
@ AWAY_FROM_ROOT
Definition tree.h:114
@ TOWARD_ROOT
Definition tree.h:114
virtual basis::outcome prune(tree *branch_to_cut)
Removes the specified branch from this tree.
Definition tree.cpp:472
virtual tree * parent() const
Returns the tree node that is the immediate ancestor of this one.
Definition tree.cpp:429
iterator start(traversal_directions direction) const
Returns a fresh iterator positioned at this tree node.
Definition tree.cpp:542
traversal_directions
Definition tree.h:94
@ postfix
Definition tree.h:94
@ prefix
Definition tree.h:94
@ to_branches
Definition tree.h:94
@ reverse_branches
Definition tree.h:95
@ infix
Definition tree.h:94
virtual bool generate_path(path &to_follow) const
Returns the path to "this" path_tree from its root.
Definition tree.cpp:516
virtual tree * root() const
Locates and returns the absolute root of the tree containing this tree.
Definition tree.cpp:443
virtual tree * branch(int branch_number) const
Returns the specified branch of this tree.
Definition tree.cpp:433
An abstraction that represents a stack data structure.
Definition stack.h:30
basis::outcome push(const contents &element)
Enters a new element onto the top of the stack.
Definition stack.h:139
basis::outcome acquire_pop(contents &to_stuff)
Used to grab the top off of the stack.
Definition stack.h:196
int size() const
returns the size of the stack.
Definition stack.h:127
#define NULL_POINTER
The value representing a pointer to nothing.
Definition definitions.h:32
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition enhance_cpp.h:54
#define bounds_return(value, low, high, to_return)
Verifies that "value" is between "low" and "high", inclusive.
Definition guards.h:48
The guards collection helps in testing preconditions and reporting errors.
Definition array.h:30
void WHACK(contents *&ptr)
deletion with clearing of the pointer.
Definition functions.h:121
A logger that sends to the console screen using the standard output device.
const int BACKWARDS_BRANCH
Definition tree.cpp:35
A dynamic container class that holds any kind of object via pointers.
Definition amorph.h:55