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 
17 #include <basis/common_outcomes.h>
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 
29 using namespace basis;
30 using namespace loggers;
31 using namespace structures;
32 
33 namespace nodes {
34 
35 const 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 
45 tree::iterator::iterator(const tree *initial, traversal_directions direction)
46 : path(initial), _order(direction), _aim(AWAY_FROM_ROOT)
47 {
48 }
49 
50 tree::iterator::~iterator() { while (size()) pop(); }
51 
52 bool 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 
276 : node(1)
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.
294 LOG("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.
331 LOG("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) {
336 LOG("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);
340 LOG("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 
350 LOG("tree dtor: reset currnode, about to whack wayback...");
351 
352  if (curr_node == stop_node) {
353 LOG("tree dtor: seeing curr_node hits our stop_node!");
355  } else if (curr_node == stop_node->parent()) {
356 LOG("tree dtor: seeing curr_node ABOVE our stop_node!");
357  }
358 
359  WHACK(way_back); // whack a node, finally.
360 
361 LOG("tree dtor: after whacking wayback...");
362  } else {
363  // okay, there's a node below here. we will spider down to it.
364 LOG("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 
431 int tree::branches() const { return links() - 1; }
432 
433 tree *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 
440 int tree::which(tree *branch_to_find) const
441 { return node::which((node *)branch_to_find) - 1; }
442 
443 tree *tree::root() const
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 
453 void 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 
460 void 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 
472 outcome 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 
479 outcome 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
491  that_branch->set_link(BACKWARDS_BRANCH, NULL_POINTER);
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 
502 int 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.
516 bool tree::generate_path(path &to_follow) const
517 {
518 if (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 */
537 return false;//temp.
538 }
539 
540 //hmmm: need text form!
541 
543 { return iterator(this, direction); }
544 
545 } // namespace.
546 
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
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:57
#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
#define LOG(s)
Definition: tree.cpp:27