//#pragma title( "TNode.cpp - List/Tree base classes" )
/*
Copyright (c) 1995-1998, Mission Critical Software, Inc. All rights reserved.
===============================================================================
Module      - TNode.cpp
System      - Common
Author      - Tom Bernhardt
Created     - 1989-11-19
Description - List/Tree base classes.
              TNode is a base class to define a collection element.  It
              contains a left and right pointer to another TNode item and
              these may be organized as a double-linked linear list or
              binary tree in the collection classes that use TNode items.

              Central to its utility are member functions to convert between
              binary tree, sorted 2-way linear linked lists, and unsorted 2-way
              linked linear lists.

 Collection and enum classes
   TNodeList         A simple collection of TNode elements.
   TNodeListSortable A TNodeList that is sortable by one or more compare functions.


 Conversion member functions for TNodeListSortable:
   The form of the list may be easily changed from binary tree to sorted list or
   vice versa.  The following member functions support these transformations:
      ToSorted       Converts the tree form into a sorted linear list form without
                     need for comparisons; the order is preserved.
      SortedToTree   Converts the sorted linear list form into a perfectly
                     balanced binary tree without comparisons; the order is preserved.
      UnsortedToTree Converts the sorted linear list form into a binary tree
                     that is not necesarily balanced.  It uses the PCompare function
                     to form the order of the tree.  Thus if the list order closely
                     matches the PCompare directed order, the resulting tree will be
                     grossly unbalanced.  This has a bearing on the performance and
                     memory requirements of the ToSorted function which is recursive.
                     So be careful, especially with large lists.
      Sort           This resorts either a tree or list form according to the argument
                     pCompare function pointer provided.  Note the above admonition.

   In either form, exposed are also Insert and Remove member functions.  The functions
   are wrappers for TreeInsert and SortedInsert function depending upon the current
   list type.
Updates     -
1995-05-01 TPB Converted to C++ classes.
===============================================================================
*/

#ifdef USE_STDAFX
#   include "stdafx.h"
#else
#   include <windows.h>
#endif

#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include "TNode.hpp"
#include "common.hpp"


#pragma page()
//------------------------------------------------------------------------------
// Warning: Must not pass top == NULL
//------------------------------------------------------------------------------
TNode *                                    // ret-head of sorted list
   TNodeListSortable::TreeToSortedList(
      TNode                * top          ,// i/o-top of [sub]tree to squash
      TNode               ** newhead      ,// out-leftmost branch from tree
      TNode               ** newtail       // out-rightmost branch from tree
   )
{
   TNode                   * temp;         // temporary pointer placeholder

   if ( top->left == NULL )
      *newhead = top;                      // this is leftmost of parent node
   else
   {
      TreeToSortedList(top->left, newhead, &temp);
      top->left = temp;                    // left = tail of sub-list
      top->left->right = top;
   }
   if ( top->right == NULL )
      *newtail = top;                      // tree is rightmost of parent node
   else
   {
      TreeToSortedList(top->right, &temp, newtail);
      top->right = temp;                   // right = head of sub-list
      top->right->left = top;
   }
   return *newhead;
}


//------------------------------------------------------------------------------
// converts sorted 2-linked list into balanced binary tree
//------------------------------------------------------------------------------
TNode *                                    // ret-middle of list (head of Btree)
   TNodeListSortable::ListSortedToTree(
      TNode                * top           // i/o-top of [sub]list to tree-ify
   )
{
   TNode                   * mid = top    ,// middle of list
                           * curr;
   int                       odd = 1;

   if ( top == NULL )
      return NULL;
   for ( curr = top;  curr;  curr = curr->right ) // find list middle
   {
      if ( odd ^= 1 )
         mid = mid->right;
   }
   if ( mid->left )                        // split list around mid point
   {
      mid->left->right = NULL;             // right terminate new sublist
      mid->left = ListSortedToTree(top);   // recursive call to set left side
   }
   if ( mid->right )
   {
      mid->right->left = NULL;             // left terminate new sublist
      mid->right = ListSortedToTree(mid->right);// recursive call to set right side
   }
   return mid;
}


#pragma page()
TNode *                                    // ret-new head of tree
   TNodeListSortable::UnsortedToTree()
{
   TNode                   * treehead = NULL,
                           * tree,
                           * curr,
                           * next;

   MCSASSERTSZ( !IsTree(), "TNodeListSortable::UnsortedToTree - list is already a tree" );

   if ( !IsTree() )
   {
      for ( curr = head;  curr;  curr = next )// insert each node into BinTree
      {
         next = curr->right;                  // save right pointer
         curr->right = curr->left = NULL;     // break chains for insertion node
         if ( treehead == NULL )
            treehead = curr;                  // first node become BinTree head
         else
         {
            for ( tree = treehead;  ; )       // iterative BinTree insert algorithm
            {
               if ( PCompare(curr, tree) <=0 )// if belongs left of current node
                  if ( tree->left == NULL )   //    if left tree empty
                  {
                     tree->left = curr;       //       insert here
                     break;                   //       and process right node
                  }
                  else                        //    else
                     tree = tree->left;       //       go down left side 1 level
               else                           // must be right side
               {
                  if ( tree->right == NULL )
                  {
                     tree->right = curr;
                     break;
                  }
                  else
                     tree = tree->right;
               }
            }
         }
      }
      TypeSetTree();
   }
   return treehead;
}

#pragma page()

//------------------------------------------------------------------------------
// comparison function used for scrambling a sorted linked list
//------------------------------------------------------------------------------
TNodeCompare(ScrambledCompare)
{
   return (rand() - RAND_MAX/2);
}

//------------------------------------------------------------------------------
// converts sorted 2-linked list into a scrambled random binary tree
//------------------------------------------------------------------------------
void
   TNodeListSortable::SortedToScrambledTree()
{
   MCSASSERTSZ( !IsTree(), "TNodeListSortable::SortedToScrambledTree - list is already a tree" );

   if ( !IsTree() )
   {
      TNodeCompare((*pOldCompare));
      pOldCompare = PCompare;
      CompareSet(ScrambledCompare);
      UnsortedToTree();
      CompareSet(pOldCompare);
   }
}

#pragma page()
TNodeList::~TNodeList()
{

// _ASSERTE( (count == 0) && (head == NULL) );

   if ( (count == 0) && (head == NULL) )
      ;
   else
   {
      //printf( "\aTNodeList destructor failure - list is not empty!\a\n" );
   }
}

void
   TNodeList::InsertTop(
      TNode                * eIns          // i/o-element to be inserted
   )
{
   MCSVERIFY(this);
   MCSVERIFY(eIns);

   eIns->right = head;
   eIns->left  = NULL;
   if ( head )
      head->left = eIns;
   else
      tail = eIns;
   head = eIns;
   count++;
   return;
}

void
   TNodeList::InsertBottom(
      TNode                * eIns          // i/o-element to be inserted
   )
{
   MCSVERIFY(this);
   MCSVERIFY(eIns);

   eIns->right = NULL;
   eIns->left  = tail;
   if ( tail )
      tail->right = eIns;
   else
      head = eIns;
   tail = eIns;
   count++;
   return;
}

void
   TNodeList::InsertAfter(
      TNode                * eIns         ,// i/o-element to be inserted
      TNode                * eAft          // i/o-element insert point
   )
{
   TNode                   * eFwd;         // element after inserted element

   MCSVERIFY(this);
   MCSVERIFY(eIns);

   if ( !eAft )
      InsertTop( eIns );
   else
   {
      eFwd = eAft->right;
      eIns->right = eFwd;
      eIns->left  = eAft;
      if ( eFwd )
         eFwd->left  = eIns;
      else
         tail = eIns;
      eAft->right = eIns;
      count++;
   }
}

void
   TNodeList::InsertBefore(
      TNode                * eIns         ,// i/o-element to be inserted
      TNode                * eBef          // i/o-element insert point
   )
{
   TNode                   * eBwd;         // element before inserted element

   MCSVERIFY(this);
   MCSVERIFY(eIns);

   if ( !eBef )
      InsertBottom( eIns );
   else
   {
      eBwd = eBef->left;
      eIns->right = eBef;
      eIns->left  = eBwd;
      if ( eBwd )
         eBwd->right = eIns;
      else
         head = eIns;
      eBef->left = eIns;
      count++;
   }
   return;
}

void
   TNodeList::Remove(
      TNode          const * t             // i/o-new node to remove from list but not delete
   )
{
   MCSVERIFY(this);
   MCSVERIFY(t);

   if ( t->left )
      t->left->right = t->right;
   else
      head = t->right;

   if ( t->right )
      t->right->left = t->left;
   else
      tail = t->left;
   count--;

   //Remove links to the list from t. We cant do this because
   // t is a const *
   //t->left = t->right = NULL;
}


void
   TNodeList::Reverse()
{
   TNode                   * node;
   TNode                   * swap;

   MCSVERIFY(this);

   for ( node = head;  node;  node = node->left )
   {
       swap        = node->left;
       node->left  = node->right;
       node->right = swap;
   }
   swap = head;
   head = tail;
   tail = swap;
}


TNode *
   TNodeList::Find(
      TNodeCompareValue(  (* Compare) )   ,// in -compares value in TNode to other value
      void const           * findval
   ) const
{
   TNode                   * curr;

   MCSASSERT(this);

   for ( curr = head;  curr;  curr = curr->right )
   {
      if ( !Compare( curr, findval ) )
         break;
   }
   return curr;
}

BOOL                                       // ret-TRUE if valid
   TNodeListSortable::CountTree(
      TNode                * pCurrentTop  ,// i/o-top of [sub]tree to count nodes
      DWORD                * pCount        // i/o-Number of nodes encountered in the tree
   )
{
   if ( !pCurrentTop )
      return TRUE;

   (*pCount)++;

   if( (*pCount) > count )
      return FALSE;

   if(!CountTree(pCurrentTop->left,pCount))
      return FALSE;

   if(!CountTree(pCurrentTop->right,pCount))
      return FALSE;

   return TRUE;
}


BOOL                                       // TRUE if Valid and FALSE if not
   TNodeListSortable::ValidateTree()
{
   DWORD                     dwTempCount=0;
   DWORD                     bValid;

   MCSVERIFY(listType == TNodeTypeTree);

   bValid = CountTree(head,&dwTempCount);

   return bValid;
}

// Routine to validate the state of the list
DWORD
   TNodeList::Validate(
      TNode               ** pErrorNode
   )
{
   DWORD                     dwError=0;
   DWORD                     nNodesVisited=0;
   TNode                   * pCurrentNode;
   DWORD                     dwNodeCount = Count();

   if(pErrorNode)
      *pErrorNode = NULL;

#ifndef WIN16_VERSION
   try
   {
#endif
      pCurrentNode = head;

      if ( pCurrentNode)  // If the list is not empty
      {
         if ( pCurrentNode->left)
         {
            dwError = MCS_ListError_InvalidHead;
         }
         else
         {
            while ( pCurrentNode->right )
            {
               if(pCurrentNode->right->left != pCurrentNode)
               {
                  dwError = MCS_ListError_InvalidPtr;
                  if(pErrorNode)
                     *pErrorNode = pCurrentNode->right;
                  break;
               }

               nNodesVisited++;

               if ( nNodesVisited > dwNodeCount )
               {
                  dwError = MCS_ListError_InvalidCount;
                  break;
               }
               pCurrentNode = pCurrentNode->right;
            }

            if ( (!dwError) && (!pCurrentNode->right) )
            {
               if ( pCurrentNode != tail)
               {
                  dwError = MCS_ListError_InvalidTail;
                  if(pErrorNode)
                     *pErrorNode = pCurrentNode->right;
               }
            }
         }
      }
      else  // if the list is empty
      {
         if(dwNodeCount)
         {
            dwError = MCS_ListError_InvalidCount;
         }
      }
#ifndef WIN16_VERSION
   }
   catch(...)
   {
      dwError = MCS_ListError_Exception;
   }
#endif

   return dwError;
}

void
   TNodeListSortable::TreeRemove(
      TNode                * item          // i/o-node to remove from binary tree
   )
{
   TNode                  ** prevNext = &head,
                           * rep,
                           * repLeft,
                           * temp;
   int                       cmp;

   MCSVERIFY(listType == TNodeTypeTree);

   while ( *prevNext )
   {
      cmp = PCompare( item, *prevNext );
      if ( cmp < 0 )
         prevNext = &(*prevNext)->left;
      else if ( cmp > 0 )
         prevNext = &(*prevNext)->right;
      else
      {
         // we've found a matching 'name' (they compare equal)
         if ( *prevNext == item )
         {
            // we've found the address we're looking for
            if ( (*prevNext)->right )
            {
               rep = repLeft = (*prevNext)->right;
               for ( temp = rep->left;  temp;  temp = temp->left )
                  repLeft = temp;
               repLeft->left = (*prevNext)->left;
               temp = *prevNext;
               *prevNext = rep;
            }
            else
            {
               temp = *prevNext;
               *prevNext = (*prevNext)->left; // simple case
            }

            // break removed nodes links to existing tree
            temp->left = temp->right = NULL;
            count--;
            break;
         }
      }
   }
   return;
}

// returns the insert point in a sorted list for a prospective node
TNode *                                    // ret-insert before point or NULL
   TNodeListSortable::SortedFindInsertBefore(
      TNode                * item         ,// i/o-node to insert into TNode
      BOOL                 * exists        // out-TRUE if already exists
   )
{
   int                       c;
   TNode                   * curr;

   *exists = FALSE;
   if ( !lastInsert )
   {
      if ( !head )           // if null head, empty list, return NULL
         return NULL;
      lastInsert = head;
   }

   c = PCompare(item, lastInsert);
   if ( c < 0 )
      lastInsert = head;

   for ( curr = lastInsert;  curr;  curr = curr->right )
   {
      c = PCompare(item, curr);
      if ( c <= 0 )
         if ( c == 0 )
            *exists = TRUE;
         else
            break;
   }

   return curr;
}

// inserts node into sorted linear list
void
   TNodeListSortable::SortedInsert(
      TNode                * item          // i/o-node to insert into TNode
   )
{
   BOOL                      exists;

   MCSVERIFY(listType != TNodeTypeTree);

   TNode                   * insertPoint = SortedFindInsertBefore(item, &exists);

   InsertBefore(item, insertPoint);
   lastInsert = item;
}


BOOL
   TNodeListSortable::SortedInsertIfNew(
      TNode                * item          // i/o-node to insert into TNode
   )
{
   BOOL                      exists;
   TNode                   * insertPoint = SortedFindInsertBefore(item, &exists);

   if ( !exists )
   {
      InsertBefore(item, insertPoint);
      lastInsert = item;
   }
   return !exists;
}


void
   TNodeListSortable::TreeInsert(
      TNode                * item         ,// i/o-node to insert into binary tree
      short                * depth         // out-tree/recursion depth of new item
   )
{
   TNode                  ** prevNext = &head;
   int                       cmp;

   MCSVERIFY(listType == TNodeTypeTree);

   for ( *depth = 0;  *prevNext;  (*depth)++ )
   {
      cmp = PCompare( item, *prevNext );
      if ( cmp <= 0 )
         prevNext = &(*prevNext)->left;
      else
         prevNext = &(*prevNext)->right;
   }
   *prevNext = item;
   item->left = item->right = NULL;
   count++;
   return;
}


TNode *
   TNodeListSortable::TreeFind(
      TNodeCompareValue(  (* Compare) )   ,// in -compares value in TNode to other value
      void const           * findval
   ) const
{
   TNode                   * curr = head;
   int                       cmp;

   while ( curr )
   {
      cmp = Compare( curr, findval );
      if ( cmp > 0 )
         curr = curr->left;
      else if ( cmp < 0 )
         curr = curr->right;
      else   // cmp == 0
         break;
   }
   return curr;
}


TNode *                                    // ret-TNode at pos n or NULL
   TNodeListOrdEnum::Get(
      long                   n             // in -new position
   )
{
   long                 disCurr = n - nCurr, // distance to curr
                        disTop  = n < (long)list->Count()/2 ? n : n - list->Count();

#ifdef WIN16_VERSION
   long absDisTop  = (disTop<0)  ? -disTop  : disTop;
   long absDisCurr = (disCurr<0) ? -disCurr : disCurr;
   if ( absDisTop < absDisCurr )
#else
   if ( abs(disTop) < abs(disCurr) )
#endif
   {
      Top();
      disCurr = disTop;
   }
   if ( disCurr < 0 )
      for ( Prev();  n < nCurr  &&  Prev(); );
   else
      for (       ;  n > nCurr  &&  Next(); );

   return curr;
}

// returns the first node of the tree
TNode *
   TNodeTreeEnum::First()
{
   stackPos = stackBase;
   if ( top )
      Push(top);
   return Next();
}

// Returns the tree node logically following the value per the sort organization
// specified by Compare, and sets up the enumeration to continue from that point.
TNode *
   TNodeTreeEnum::FirstAfter(
      TNodeCompareValue(  (* Compare) )   ,// in -compares value in TNode to other value
      void  const          * findVal       // in -findVal to position after
   )
{
   TNode                   * tn;
   int                       cmp;

   stackPos = stackBase;
   for ( tn = top;  tn;  )
   {
      Push(tn);
      cmp = Compare( tn, findVal );
      if ( cmp < 0 )
      {
         stackPos->state = Sright;
         if ( tn->right )
            tn = tn->right;
         else
            return Next();
      }
      else if ( cmp > 0 )
      {
         stackPos->state = Sleft;
         if ( tn->left )
            tn = tn->left;
         else
         {
            stackPos->state = Sused;
            return tn;
         }
      }
      else
      {
         stackPos->state = Sused;
         return Next();
      }
   }

   return NULL;
}


// returns the Next logical node of the tree ending with NULL when complete
TNode *
   TNodeTreeEnum::Next()
{
   for ( ;; )
   {
      switch ( stackPos->state )
      {
         case Snone:                       // we've done nothing here
            stackPos->state = Sleft;
            if ( stackPos->save->left )
               Push(stackPos->save->left);
            break;
         case Sleft:                       // we've gone left and are back
            stackPos->state = Sused;
            return stackPos->save;
         case Sused:                       // we've used the node
            stackPos->state = Sright;
            if ( stackPos->save->right )
               Push(stackPos->save->right);// process right side of branch
            break;
         case Sright:                      // we've gone right and are back
            if ( !Pop() )
               return NULL;
            break;
         case SComplete:
            return NULL;
            break;                         // Do we need this?
         default:                          // bad error
            MCSASSERT(FALSE);
            return NULL;
      }
   }

   return NULL;   // can't get here
}

// Returns the address of the forward (left/right) pointer where the find node
// already exists or would be inserted.  If the singly deferenced result is not
// null, the node's key value already exists in the tree.
// If, after obtaining the insertion point, you want to insert the node, just
// assign its address to the singly deferenced return value.  The following inserts
// the node "f" if it is not alread in the tree:
//    TNode **r = tree.TreeFindInsert(f);
//    if ( !*r )
//       *r = f;
TNode **                                   // ret-pointer forward pointer to find
   TNodeListSortable::TreeFindInsert(
      TNode const          * find         ,// in -node to find
      short                * depth         // out-tree depth of insertion point
   )
{
   TNode                  ** prevNext = &head;
   int                       cmp;

   for ( *depth = 0;  *prevNext;  (*depth)++ )
   {
      cmp = PCompare( find, *prevNext );
      if ( cmp < 0 )
         prevNext = &(*prevNext)->left;
      else if ( cmp > 0 )
         prevNext = &(*prevNext)->right;
      else
         break;
   }

   return prevNext;
}

// TNode.cpp - end of file
