home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d0xx / d034 / btree.lha / Btree / btree.c next >
Encoding:
C/C++ Source or Header  |  1986-09-03  |  22.7 KB  |  991 lines

  1. /********************************************************************
  2. *********************************************************************
  3.  
  4. Module Name:        btree.c
  5. ============
  6.  
  7. Function:        Provide a library of routines for creating 
  8. =========        and manipulating B-Trees.  
  9.             The "order" of the B-Tree is controlled by a 
  10.             manifest constant.
  11.  
  12. Description:
  13. ============
  14.     Main btree code Insert, Delete, Search etc.
  15.  
  16.     Interface Routines:
  17.     -------------------
  18.     int Insert(*tree, dtm)    
  19.         Insert DATUM dtm into B-tree "tree",
  20.         returns an error/success code.
  21.     int Delete(*tree, key)    
  22.         Remove the entry associated with "key" from the B-Tree
  23.         returns an error/success code.
  24.     int Search(tree, key, dtmptr)    
  25.         Look for key "key" in B-tree "tree".
  26.         Set "dtmptr" to point to matching DATUM or to NULL.
  27.         Return an error/success code.
  28.  
  29.     Libraries Used:
  30.     ---------------
  31.     stdio.h
  32.     btree.h 
  33.  
  34. ****************************************************************************
  35. ****************************************************************************/
  36.  
  37. static char btreeprog[] = "@(#)btree.c    1.7 7/17/86";
  38.  
  39. #include <stdio.h>
  40. #include "btree.h"
  41.  
  42. #define TRUE    1    /* A couple of useful constants */
  43. #define FALSE    0
  44.  
  45. #define    M    2    /* This constant defines the order of the B-tree */
  46.  
  47.  
  48.  
  49. /************** Global types and data structures **************/ 
  50.  
  51. typedef int    KEY;    /* Define the key field type */
  52. typedef int    INFO;    /* and define the type of the information field
  53.                associated with this key */
  54.  
  55. typedef struct         /* Define a structure (DATUM) consisting of a key */
  56. {            /* and an information field */
  57.     KEY    key;
  58.     INFO    inf;
  59. } DATUM;
  60.  
  61.  
  62.             /* The following is the structure used for defining 
  63.                B-trees and their nodes */
  64. typedef struct btnode 
  65. {
  66.     int    t_active;        /* # of active keys */
  67.     DATUM    t_dat[2*M];        /* Keys  + Data */
  68.     struct btnode *t_ptr[2*M+1];    /* Subtree ptrs */
  69. } NODE, *BTREE;
  70.  
  71.  
  72. /**********************************************************************
  73. * INSERTION ROUTINES
  74. **********************************************************************/
  75.  
  76. static BTREE splittree;    /* 
  77.             A global variable used by "InternalInsert"
  78.             to return the right hand portion of a tree
  79.             which has been bisected .
  80.             */
  81.  
  82. /*
  83. ** INSERT 
  84. ** ======
  85. **
  86. ** Purpose:    Enter the given key into the B-tree.
  87. **
  88. ** Parameters:    dtm    = DATUM to be inserted into tree
  89. **        tree    = B-tree to insert into
  90. **
  91. ** Returns:    An error/success code which will be one of:
  92. **            SUCCESS
  93. **            KEY_EXISTS_ERROR
  94. **        In addition the tree "tree" is updated.
  95. **
  96. ** Description: This is the "front end" of the insertion routine
  97. **        - most of the work is done by "InternalInsert".
  98. **
  99. */
  100.  
  101. int Insert(treeptr, dtm)
  102. BTREE    *treeptr;
  103. DATUM    dtm;
  104. {
  105.     int status;
  106.     int InternalInsert();
  107.     BTREE MkNode();
  108.  
  109.     /* 
  110.     Invoke the main insertion routine, let it do the guts of the
  111.     insertions, then let it return a final status code.
  112.     */
  113.     status = InternalInsert(*treeptr, &dtm);
  114.  
  115.     /* 
  116.     Now examine the returned status code - there are 3 cases:
  117.     (1) NODE_SPLIT
  118.     The root node was split - then stitch the tree together by
  119.     creating a new root node containing dtm only and link 'tree'
  120.     to the left-hand link and 'splittree' to the right-hand link
  121.     (2) SUCCESS
  122.     The tree has already been reassembled into a consistent state
  123.     so just return a success code
  124.     (3) KEY_EXISTS_ERROR
  125.     The given key already exists - the tree should already be unchanged
  126.     - just exit with this error code
  127.     */
  128.     if (status==NODE_SPLIT)
  129.     {
  130.         *treeptr = MkNode(dtm, *treeptr, splittree);
  131.         return SUCCESS;
  132.     }
  133.     else
  134.         return status;
  135. }
  136.  
  137.  
  138. /*
  139. ** INTERNALINSERT
  140. ** ==============
  141. **
  142. ** Purpose:    This routine performs the major part of key insertion
  143. **
  144. ** Parameters:     tree    = B-tree to use.
  145. **        dtmptr    = Pointer to the datum to be inserted into the tree
  146. **              on exit this should contain any datum to be passed
  147. **              back up the tree as a result of splitting.
  148. **
  149. ** Returns:     An error/success/information code which will be one of:
  150. **        SUCCESS: Tree is okay, no split occurred dtmptr will not
  151. **             point at anything meaningful and splittree will be
  152. **             NULL.
  153. **        NODE_SPLIT:
  154. **             Top level node has been split - dtmptr and splittree
  155. **             are now meaningful.
  156. **        KEY_EXISTS_ERROR:
  157. **             The key to be inserted already exists - the tree is
  158. **             unchanged.
  159. **
  160. ** Description: This routine works in a recursive manner under the following 
  161. **        algorithm:
  162. **        1) Scan down the tree for a place to insert the new datum
  163. **           - this should be a leaf unless the datum itself is found
  164. **             (in which case return an error code)
  165. **        2) Try to insert the new datum in the located node
  166. **           - if it will fit, then slot it into the node and exit with
  167. **             a success code.
  168. **           - if the node is already full, split it into 2 separate 
  169. **             nodes (one for the lowest M keys, one for the highest M
  170. **             keys) and return the NODE_SPLIT code and everything
  171. **             associated with it
  172. **
  173. */
  174.  
  175. int InternalInsert(tree, dtmptr)
  176. DATUM    *dtmptr;
  177. BTREE    tree;
  178. {
  179.     int    index,
  180.         j;
  181.     int    SearchNode();
  182.     int     status;
  183.     BTREE    tmp;
  184.     BTREE    MkNode();
  185.  
  186.     if (tree == (BTREE) NULL) 
  187.     {
  188.     /* 
  189.     If current tree is NULL then we're at a leaf, so we can't insert the
  190.     datum here - the procedure is to act as if we've just split a node
  191.     and are returning a datum to be slotted in further up the tree - the 
  192.     only difference is that splittree is set to NULL.
  193.     */
  194.         splittree = (BTREE) NULL;
  195.         return NODE_SPLIT;
  196.     } 
  197.     else 
  198.     {
  199.         /* 
  200.         Tree is not null - now search for occurence of datum's key 
  201.         in the current top-level node - return its position (or index 
  202.         of pointer to subtree) as a by-product.
  203.         */
  204.         if (SearchNode(tree, (*dtmptr).key, &index))
  205.             /*
  206.             If key has been found then return an error code 
  207.             */
  208.             return KEY_EXISTS_ERROR;
  209.         /*
  210.         If not found the try to insert datum in next level of tree -
  211.         this action will then return a status code and possibly a
  212.         splittree.
  213.         */
  214.         status = InternalInsert(tree->t_ptr[index],dtmptr);
  215.         
  216.         /*
  217.         If we have had a successful insertion (with tree in good order)
  218.         or we have had a key insertion error, then return with this 
  219.         status code without further ado.
  220.         */
  221.         if (status != NODE_SPLIT)
  222.             return status;
  223.         /*
  224.         We've now had a split node at the level below with dtmptr now
  225.         pointing to a datum throw back up from it and splittree 
  226.         pointing to the right-hand part of the split node
  227.         Next stage is to try to insert things here and tidy up the tree
  228.         once and for all - so check to see whether current top level
  229.         node is full or not (ie. can it support an insertion ?).
  230.         */
  231.         if (tree->t_active < 2*M)
  232.         {
  233.             /* 
  234.             If there is room then everything's hunky dory
  235.             */
  236.             InsInNode(tree, *dtmptr, splittree);
  237.             return SUCCESS;
  238.         }
  239.  
  240.         /*
  241.         Current top-level node is full - so split it into two 
  242.         - set left-hand half to contain lower M keys and leave it
  243.           attached to main tree.
  244.         - set splittree to point to right-hand half (which contains
  245.           the highest M keys).
  246.         - set dtmptr to point to central key value
  247.         */
  248.         if (index <= M) 
  249.         {
  250.             /* 
  251.             If datum should go in a left-hand or central position
  252.             of current top-level node then shift right-element out
  253.             Note that we need a temporary B-tree here just to do
  254.             the swap of datums
  255.             */
  256.             tmp = MkNode(tree->t_dat[2*M-1], (BTREE)NULL, tree->t_ptr[2*M]);
  257.             tree->t_active--;
  258.             InsInNode(tree, *dtmptr, splittree);
  259.         } 
  260.         else
  261.             /* 
  262.             If datum is definitely part of right-hand half of
  263.             the 2M+1 keys then transfer it to (what will be) 
  264.             splittree immediately.
  265.             */
  266.             tmp = MkNode(*dtmptr, (BTREE)NULL, splittree);
  267.  
  268.         /*
  269.         Final part is to move right hand half of current top level node
  270.         out into (whta is about to become) the new splittree
  271.         and then mode central (Mth) key of current top-level node into
  272.         *dtmptr
  273.         */
  274.         for (j = M+1; j < 2*M; j++)
  275.             InsInNode(tmp, tree->t_dat[j], tree->t_ptr[j+1]);
  276.  
  277.         *dtmptr        = tree->t_dat[M];
  278.         tree->t_active     = M;
  279.         tmp->t_ptr[0]     = tree->t_ptr[M+1];
  280.         splittree    = tmp;
  281.  
  282.         /* 
  283.         Finally return NODE_SPLIT code
  284.         */
  285.         return NODE_SPLIT;
  286.     }
  287. }
  288.  
  289.  
  290. /*
  291. ** INSINNODE
  292. ** =========
  293. ** 
  294. ** Purpose:    Add a datum into a B-tree node working on the assumption 
  295. **        that there is room for it.
  296. **
  297. ** Parameters:     nodeptr    = Ptr to the node,
  298. **        dtm    = Datum to enter,
  299. **        ptr    = "Greater than" link to subordinate node.
  300. **
  301. ** Returns:     None.
  302. **
  303. ** Description:    The workings of this routine are pretty straightforward - just
  304. **        sort out where the key should go, shift all greater keys
  305. **        right one position and then insert the key and pointer.
  306. */
  307.  
  308. InsInNode(nodeptr, dtm, ptr)
  309. BTREE    nodeptr,
  310.     ptr;
  311. DATUM    dtm;
  312. {
  313.     int    index;
  314.     int    KeyCmp();
  315.  
  316.     for (index = nodeptr->t_active; index>0 && KeyCmp(dtm.key, nodeptr->t_dat[index-1].key)<0; index--) 
  317.     {
  318.         nodeptr->t_dat[index]   = nodeptr->t_dat[index-1];
  319.         nodeptr->t_ptr[index+1] = nodeptr->t_ptr[index];
  320.     }
  321.  
  322.     nodeptr->t_active++;
  323.     nodeptr->t_dat[index]   = dtm;
  324.     nodeptr->t_ptr[index+1] = ptr;
  325. }
  326.  
  327.  
  328. /*************************************************************************
  329. * DELETION ROUTINES
  330. *************************************************************************/
  331.  
  332. /*
  333. ** DELETE
  334. ** ======
  335. **
  336. ** Purpose:    Remove the data associated with a given key from a B-tree.
  337. **
  338. ** Parameters:    tree    = B-tree to update
  339. **        key      = Key to use,
  340. **
  341. ** Returns:    An error/success code
  342. **        SUCCESS / KEY_NOT_FOUND_ERROR / TREE_CORRUPTED_ERROR
  343. **
  344. ** Description: The real work to do with deletion is performed by RealDelete()
  345. **        this routine only checks that RealDelete has actually done
  346. **        a deletion and tidies things up afterwards.
  347. **
  348. */
  349.  
  350. int Delete(treeptr, key)
  351. BTREE    *treeptr;
  352. KEY    key;
  353. {
  354.     int    status;
  355.     int    RealDelete();
  356.     BTREE    savetree;
  357.  
  358.     /*
  359.     Do the main deletion then check whether it managed to find anything
  360.     to delete. If it didn't - return an error code.
  361.     */
  362.     status = RealDelete(*treeptr, key);
  363.     if (status != SUCCESS)
  364.         return status;
  365.  
  366.     /* 
  367.     Now check to see whether the previous root node has now
  368.     been superceded.
  369.     */
  370.     else if ((*treeptr)->t_active == 0) 
  371.     {
  372.         /* 
  373.         If so then deallocate the space that was set aside for the
  374.         root node and make its leftmost child the new root node.
  375.         */
  376.         savetree = (*treeptr)->t_ptr[0];
  377.         Dispose(*treeptr);
  378.         *treeptr = savetree;
  379.     } 
  380.     return SUCCESS;
  381. }
  382.  
  383.  
  384. /*
  385. ** REALDELETE 
  386. ** ==========
  387. **
  388. ** Purpose:    Remove a key from a tree
  389. **
  390. ** Parameters:    tree    = B-tree to be updated
  391. **        key    = Key to use,
  392. **
  393. ** Returns:    Returns an error/success code (SUCCESS/KEY_NOT_FOUND_ERROR)
  394. **
  395. ** Description:    This routine operates recursively in the following manner:
  396. **        1) Locate the required key within the tree.
  397. **        2) If it's in a leaf then remove it and rebalance the tree
  398. **           otherwise replace it by the next key in the tree (in 
  399. **           ascending order), then delete that key from its node 
  400. **           (which must be a leaf).
  401. */
  402.  
  403. int RealDelete(tree, key)
  404. BTREE    tree;
  405. KEY    key;
  406. {
  407.     int index;
  408.     int status;
  409.     int SearchNode();
  410.     KEY nextkey;
  411.  
  412.     if (tree == (BTREE)NULL)
  413.         /* 
  414.         If now trying to scan down a non-existant link,
  415.         mark required key as not found and exit 
  416.         */
  417.         return KEY_NOT_FOUND_ERROR;
  418.  
  419.     /* 
  420.     First stage is to search for key within current top-level node
  421.     If it's not there then we must continue down the tree looking
  422.     for it.
  423.     */
  424.     if (! SearchNode(tree, key, &index))
  425.     {
  426.         /* 
  427.         If required key not found in current top-level node
  428.         then try deleting it from the next node down 
  429.         */
  430.         status = RealDelete(tree->t_ptr[index], key);
  431.         if (status==SUCCESS)
  432.             Balance(tree,index);
  433.         return status;
  434.     }
  435.     /* 
  436.     If the key was found, then check whether this is
  437.     a leaf 
  438.     */
  439.     if (tree->t_ptr[index] == (BTREE)NULL)
  440.     {
  441.         /* 
  442.         If this is a leaf, then just remove the required key for now 
  443.         - the tree will be rebalanced on the way back up 
  444.         */
  445.         Remove(tree,index);
  446.         return SUCCESS;
  447.     }
  448.     else 
  449.     {
  450.         /* 
  451.         If this isn't a leaf, the rule is to replace the key by 
  452.         the next highest in the tree, deleting that key from its 
  453.         node and rebalancing the tree 
  454.         */
  455.         nextkey = Succ(tree, index);
  456.         status  = RealDelete(tree->t_ptr[index+1],nextkey);
  457.         if (status==SUCCESS)
  458.         {
  459.             Balance(tree,index+1);
  460.             return status;
  461.         }
  462.         else
  463.             return TREE_CORRUPTED_ERROR;
  464.     }
  465. }
  466.  
  467.  
  468. /*
  469. ** REMOVE
  470. ** ======
  471. **
  472. ** Purpose:    Remove a single datum
  473. **
  474. ** Parameters:     tree    = Ptr to the B-tree node,
  475. **        pos    = Index of the key to be removed.
  476. **
  477. ** Returns:     None.
  478. **
  479. ** Description:    Remove a datum from a B-tree node and fill the gap left
  480. **        by the deletion by shuffling across the other DATUMs and 
  481. **        pointers.
  482. **
  483. */
  484.  
  485. Remove(tree, pos)
  486. BTREE    tree;
  487. int    pos;
  488. {
  489.     int    i;
  490.  
  491.     /* 
  492.     This is all pretty trivial - just shuffle everything to the
  493.     right of the deleted key, left by one.
  494.     */
  495.     for (i=pos; i<((tree->t_active)-1); ++i) 
  496.     {
  497.         tree->t_dat[i]   = tree->t_dat[i+1];
  498.         tree->t_ptr[i+1] = tree->t_ptr[i+2];
  499.     }
  500.  
  501.     /* 
  502.     Finally decrement the number of valid keys in the node 
  503.     */
  504.     tree->t_active--;
  505. }
  506.  
  507.  
  508. /*
  509. ** SUCC 
  510. ** ====
  511. **
  512. ** Purpose:    Replace a key by its successor
  513. **
  514. ** Parameters:     tree    = Ptr to a B-tree node,
  515. **        pos    = Index of the key to be succ'ed.
  516. **
  517. ** Returns:     Returns the successor key
  518. **
  519. ** Description: Using the natural ordering, replace a key with its successor 
  520. **        (ie the next key in the tree in ascending order).
  521. **        This routine relies on the fact    that the successor of a key 
  522. **        will be the leftmost key in the leftmost leaf of the 
  523. **        "greater than" subtree of the key to be deleted.
  524. **
  525. */
  526.  
  527. KEY Succ(tree, pos)
  528. BTREE    tree;
  529. int    pos;
  530. {
  531.     BTREE    ltree;
  532.     DATUM     successor;
  533.  
  534.     /*
  535.     Using the "greater than" branch the key chain down to leftmost node 
  536.     below it.
  537.     */
  538.     for (ltree = tree->t_ptr[pos+1]; ltree->t_ptr[0] != (BTREE)NULL; ltree = ltree->t_ptr[0])
  539.         ;        /* NULL BODY */
  540.  
  541.     successor = ltree->t_dat[0]; 
  542.     tree->t_dat[pos] = successor;
  543.  
  544.     return successor.key;
  545. }
  546.  
  547.  
  548. /*
  549. ** BALANCE 
  550. ** =======
  551. **
  552. ** Purpose:    Restore balance to a potentially unbalanced B-tree
  553. **
  554. ** Parameters:     parent    = Ptr to the parent node of a B-tree structure,
  555. **        pos    = Index of the out-of-balance point within the
  556. **              parent node.
  557. **
  558. ** Returns:     None.
  559. **
  560. ** Description: After removing an item from a B-tree we must restore 
  561. **        the balance to the tree.
  562. **         In this routine we consider the DATUM in t->t_dat[pos-1]
  563. **        and the DATUMs in its immediate children (and there should 
  564. **        be children for this routine to be called).
  565. **        The rules are:
  566. **        (1)     If total number of DATUMs <= 2M then we can combine 
  567. **            them into a single node 
  568. **        (2)    Otherwise if the difference in numbers between the 2
  569. **            children is greater than 1, then we must shuffle 
  570. **            DATUM(s) out of the fullest node, through the parent 
  571. **            and into the emptiest node 
  572. **
  573. */
  574.  
  575. Balance(parent, pos)
  576. BTREE    parent;
  577. int    pos;
  578. {
  579.     int     lchildindex, rchildindex;
  580.     int     lchildlen, rchildlen;
  581.  
  582.     /*  
  583.     Firstly decide where the children are - this pretty obvious
  584.     - t_ptr[pos] and t_ptr[pos+1] - unless pos=tree->t_active
  585.     when we have t_ptr[t_active-1] & t_ptr[t_active]
  586.     */
  587.     if (pos==parent->t_active)
  588.         rchildindex = parent->t_active;
  589.     else
  590.         rchildindex = pos+1;
  591.     lchildindex = rchildindex - 1;
  592.  
  593.     /* 
  594.     Now find out how many valid DATUMs reside in each of the children 
  595.     */
  596.     lchildlen = (parent->t_ptr[lchildindex])->t_active;
  597.     rchildlen = (parent->t_ptr[rchildindex])->t_active;
  598.  
  599.     /* 
  600.     Check total number of DATUMs involved 
  601.     */
  602.     if ((lchildlen + rchildlen + 1) <= 2*M)
  603.         /* 
  604.         If <=2M then combine into 1 node 
  605.         */
  606.         Combine(parent,rchildindex);
  607.  
  608.     /* 
  609.     Otherwise if difference in sizes of children is >1 then shift 
  610.     DATUMs one way or the other 
  611.     */
  612.     else if ((lchildlen-rchildlen)>1)
  613.         MoveRight(parent,lchildindex);
  614.     else if ((rchildlen-lchildlen)>1)
  615.         MoveLeft(parent,rchildindex);
  616. }
  617.  
  618.  
  619. /*
  620. ** COMBINE 
  621. ** =======
  622. **
  623. ** Purpose:     Coalesce two nodes of a B-tree when they have too many DATUMs.
  624. **
  625. ** Parameters:     parent    = Ptr to parent B-tree node,
  626. **        rindex    = Index of the right-hand of the 2 nodes to be combined.
  627. **
  628. ** Returns:     None.
  629. **
  630. ** Description: This all pretty simple - just move parent DATUM, followed by
  631. **        DATUMs from right-hand child into left-hand child. Then throw
  632. **        away right-hand child, delete parent DATUM and close the gap in
  633. **        the parent node.
  634. */
  635.  
  636. Combine(parent, rindex)
  637. BTREE    parent;
  638. int    rindex;
  639. {
  640.     int    i;
  641.     BTREE    ltree,
  642.         rtree;
  643.  
  644.     /* 
  645.     Set up pointers to the 2 child trees 
  646.     */
  647.     ltree = parent->t_ptr[rindex-1];
  648.     rtree = parent->t_ptr[rindex];
  649.  
  650.     /* 
  651.     We are going to combine the 2 trees into the left-hand tree
  652.     so first thing is to tag parent datum onto end of this left-hand
  653.     child node 
  654.     */
  655.     ltree->t_active++;
  656.     ltree->t_dat[(ltree->t_active)-1] = parent->t_dat[rindex-1];
  657.  
  658.     /* 
  659.     Now set right-hand link from this left-hand child to be the left-hand 
  660.     link right-hand child 
  661.     */
  662.     ltree->t_ptr[ltree->t_active] = rtree->t_ptr[0];
  663.  
  664.     /* 
  665.     Now tag all of right-hand child onto end of left-hand child 
  666.     */
  667.     for (i = 1; i <= rtree->t_active; ++i) 
  668.     {
  669.         ltree->t_active++;
  670.         ltree->t_dat[ltree->t_active-1] = rtree->t_dat[i-1];
  671.         ltree->t_ptr[ltree->t_active]   = rtree->t_ptr[i];
  672.     }
  673.  
  674.     /* 
  675.     Finally, remove parent DATUM from parent node by shifting
  676.     contents of parent node left as necessary 
  677.     */
  678.     for (i = rindex; i <= parent->t_active-1; ++i)
  679.     {
  680.         parent->t_dat[i-1] = parent->t_dat[i];
  681.         parent->t_ptr[i]   = parent->t_ptr[i+1];
  682.     }
  683.     parent->t_active--;
  684.  
  685.     Dispose(rtree);
  686. }
  687.  
  688.  
  689. /*
  690. ** MOVERIGHT
  691. ** =========
  692. **
  693. ** Purpose:     Move DATUMs right out of one child into its right-hand 
  694. **        brother.
  695. **
  696. ** Parameters:     parent    = Ptr to the parent B-tree node,
  697. **        lindex    = Index of the link to the left-hand child 
  698. **              from the parent node.
  699. **
  700. ** Returns:     None.
  701. **
  702. ** Description: Main thing to note is that we only need to shift 1 DATUM
  703. **        because the current imbalance was caused by a deletion from 
  704. **        the right-hand child and, prior to that, things were okay (the 
  705. **        tree was balanced last time around).
  706. */
  707.  
  708. MoveRight(parent, lindex)
  709. BTREE    parent;
  710. int    lindex;
  711. {
  712.     int    i;
  713.     BTREE    ltree,
  714.         rtree;
  715.  
  716.     /*
  717.     Set up pointers to 2 trees concerned
  718.     */
  719.     ltree = parent->t_ptr[lindex];
  720.     rtree = parent->t_ptr[lindex+1];
  721.  
  722.     /* 
  723.     First stage is to shift contents of right-hand child right by one
  724.     in order to accommodate the incoming DATUM from the parent node 
  725.     */
  726.     rtree->t_active++;
  727.     for (i = rtree->t_active; i >= 1; i--) 
  728.     {
  729.         rtree->t_dat[i]   = rtree->t_dat[i-1];
  730.         rtree->t_ptr[i+1] = rtree->t_ptr[i];
  731.     }
  732.     rtree->t_ptr[1] = rtree->t_ptr[0];
  733.  
  734.     /* 
  735.     Now copy parent DATUM into r-hand child 
  736.     */
  737.     rtree->t_dat[0] = parent->t_dat[lindex];
  738.  
  739.     /* 
  740.     Finally move rhand DATUM of lhand child into parent node 
  741.     */
  742.     rtree->t_ptr[0]      = ltree->t_ptr[ltree->t_active];
  743.     parent->t_dat[lindex]     = ltree->t_dat[(ltree->t_active)-1];
  744.     ltree->t_active--;
  745. }
  746.  
  747.  
  748. /*
  749. ** MOVELEFT
  750. ** ========
  751. **
  752. ** Purpose:     Move DATUM left from a right-hand child, through its parent
  753. **        DATUM and into a left-hand child.
  754. **
  755. ** Parameters:     parent    = Ptr to the parent B-tree node,
  756. **        rindex    = Index of the right-hand child within the
  757. **              parent node. 
  758. **
  759. ** Returns:     None.
  760. **
  761. ** Description: As with MoveRight (above) the main thing to note is that we
  762. **        only have to shift 1 DATUM.
  763. */
  764.  
  765. MoveLeft(parent, rindex)
  766. BTREE    parent;
  767. int    rindex;
  768. {
  769.     int    i;
  770.     BTREE    ltree,
  771.         rtree;
  772.  
  773.     /*
  774.     Set up pointers to 2 children
  775.     */
  776.     ltree = parent->t_ptr[rindex-1];
  777.     rtree = parent->t_ptr[rindex];
  778.  
  779.     /* 
  780.     First stage is to shift DATUM from parent node onto end of
  781.     lhand child 
  782.     */
  783.     ltree->t_active++;
  784.     ltree->t_dat[(ltree->t_active)-1] = parent->t_dat[rindex-1];
  785.  
  786.     /* 
  787.     Now move lhand link from rhand child to be rhand link from
  788.     lhand child 
  789.     */
  790.     ltree->t_ptr[ltree->t_active] = rtree->t_ptr[0];
  791.  
  792.     /* 
  793.     Next, shift lhand DATUM of rhand node into parent node 
  794.     */
  795.     parent->t_dat[rindex-1] = rtree->t_dat[0];
  796.  
  797.     /* 
  798.     Finally shift contents of rhand child left by one 
  799.     */
  800.  
  801.     rtree->t_ptr[0] = rtree->t_ptr[1];
  802.     for (i = 1; i <= rtree->t_active; ++i) 
  803.     {
  804.         rtree->t_dat[i-1] = rtree->t_dat[i];
  805.         rtree->t_ptr[i]   = rtree->t_ptr[i+1];
  806.     }
  807.     rtree->t_active--;
  808. }
  809.  
  810.  
  811. /*****************************************************************************
  812. * SEARCH ROUTINE
  813. *****************************************************************************/
  814.  
  815. /*
  816. ** SEARCH
  817. ** ======
  818. **
  819. ** Purpose:    Look for a key in a tree.
  820. **
  821. ** Parameters:  tree    = Tree to look in
  822. **        key    = key to look for
  823. **        dtmptr  = pointer to found datum
  824. **
  825. ** Returns:     A success/error code (SUCCESS/KEY_NOT_FOUND_ERROR)
  826. **
  827. ** Description:
  828. **
  829. */
  830.  
  831. int Search(tree, key, dtmptr)
  832. BTREE    tree;
  833. KEY    key;
  834. DATUM     *dtmptr;
  835. {
  836.     DATUM    dtm;
  837.     int    index;
  838.     int     SearchNode();
  839.  
  840.     while (tree != (BTREE)NULL) 
  841.     {
  842.         /* 
  843.         For each node just check to see whether the requd key value
  844.         is there - if not, go down 1 more level ...
  845.         */
  846.         if (SearchNode(tree, key, &index))
  847.         {
  848.             dtm = (tree->t_dat[index]);
  849.             *dtmptr = dtm;
  850.             return SUCCESS;
  851.         }
  852.         tree = tree->t_ptr[index];
  853.     }
  854.     dtmptr = (DATUM *)NULL;
  855.     return KEY_NOT_FOUND_ERROR;
  856. }
  857.  
  858.  
  859. /**************************************************************************
  860. * MISCELLANEOUS ROUTINES
  861. **************************************************************************/
  862.  
  863. /*
  864. ** SEARCHNODE 
  865. ** ==========
  866. **
  867. ** Purpose:     Look for a key in a single B-tree node.
  868. **
  869. ** Parameters:     nodeptr    = Ptr to B-tree node,
  870. **        key    = Key to look for,
  871. **        pindex  = Pointer to integer vbl in which to place
  872. **              index position of key within node
  873. **
  874. ** Returns:     TRUE/FALSE result indicating whether key was in the node
  875. **
  876. ** Description:    The workings of this routine are pretty trivial -see code.
  877. */
  878.  
  879. int SearchNode(nodeptr, key, pindex)
  880. BTREE    nodeptr;
  881. KEY    key;
  882. int    *pindex;
  883. {
  884.     int    i;
  885.     int    cmp;
  886.     int    KeyCmp();
  887.  
  888.     /* 
  889.     Scan down the node from highest to lowest key value - stop when 
  890.     a key <= 'key' is found
  891.     */
  892.     for (i=(nodeptr->t_active)-1; i>=0; i--)
  893.     {
  894.         cmp = KeyCmp(key,(nodeptr->t_dat[i]).key);
  895.         if (cmp>=0)
  896.         {
  897.             *pindex = (cmp==0)? i: i+1;
  898.             return (cmp==0);
  899.         }
  900.     }
  901.  
  902.     /* 
  903.     If passed through loop unscathed then 'key' must be less than 
  904.     everything in the node - so act accordingly
  905.     */
  906.     *pindex = 0;
  907.     return FALSE;
  908. }
  909.  
  910.  
  911. /*
  912. ** KEYCMP 
  913. ** ======
  914. **
  915. ** Purpose:    To compare two key values
  916. **
  917. ** Parameters:    key1    = First key,
  918. **        key2    = Second key.
  919. **
  920. ** Returns:     -1     if key1 < key2;
  921. **         0    if key1 = key2;
  922. **         1    if key1 > key2;
  923. **
  924. ** Description:    The contents of this routine are dependent upon the type of
  925. **        key being used - as long as it accepts the same parameters
  926. **        and returns the same results, any key type can be used.
  927. */
  928.  
  929. int KeyCmp(key1, key2)
  930. KEY    key1,
  931.     key2;
  932. {
  933.     return key1<key2 ? -1 : key1==key2 ? 0 : 1;
  934. }
  935.  
  936.  
  937. /*
  938. ** MKNODE
  939. ** ======
  940. **
  941. ** Purpose:    Allocate storage for a new B-tree node and copy in an
  942. **        initial datum and two pointers from it.
  943. **
  944. ** Parameters:    dtm    = Initial datum for new node
  945. **        ptr0    = "less than" pointer from dtm
  946. **        ptr1    = "greater than" pointer from dtm
  947. **
  948. ** Returns:     Reference to the new node.
  949. **
  950. ** Description:    All pretty obvious...
  951. */
  952.  
  953. BTREE MkNode(dtm, ptr0, ptr1)
  954. DATUM    dtm;
  955. BTREE    ptr0,
  956.     ptr1;
  957. {
  958.     char    *malloc();
  959.     BTREE    tree;
  960.  
  961.     tree = (BTREE) malloc(sizeof(NODE));
  962.  
  963.     tree->t_ptr[0] = ptr0;
  964.     tree->t_ptr[1] = ptr1;
  965.     tree->t_dat[0] = dtm;
  966.     tree->t_active = 1;
  967.  
  968.     return tree;
  969. }
  970.  
  971.  
  972. /*
  973. ** DISPOSE 
  974. ** =======
  975. **
  976. ** Purpose:    Return the storage occupied by a tree node to the pool.
  977. **
  978. ** Parameters:     nodeptr    = Ptr to the tree node.
  979. **
  980. ** Returns:     None.
  981. **
  982. ** Description: Another simple one ...
  983. */
  984.  
  985. Dispose(nodeptr)
  986. BTREE    nodeptr;
  987. {
  988.     free((char *) nodeptr);
  989. }
  990.  
  991.