'===========================================================================
' Subject: 32-BIT BTREE FOR PBDLL 5.0         Date: 01-10-98 (14:54)       
'  Author: Kerry S. Goodin                    Code: PBDLL                  
'  Origin: medvrsys@venus.net               Packet: PBDLL.ABC
'===========================================================================
'+-------------------------------------------------------------------------+
'|                                                                         |
'|                            BT-PBDLL-50.BAS                              |
'|            Modified By Gafny Jacob To Pbdll 5.0 Compiler                |
'|   Modified By Paul Propst, Additional Modification By Marty Francom     |
'|           (Adapted from Joe Vests B-Trees ,circa 1987,89)               |
'|                                                                         |
'|  Portions of this file adapted from "Joe Vest B-TREES for Turbo         |
'|  BASIC(c) and its various PowerBASIC adaptations.                       |
'|  The original source was modified, expanded and enhanced to             |
'|  support 4 byte pointers.                                               |
'|  This file provides "B-TREE" indexing for related datafiles.            |
'|                                                                         |
'|  The original versions were all limited to 32000 records because short  |
'|  integer record pointers were used.  This version uses long integer     |
'|  pointers so a billion or so keys could theoretically be "treeed"       |
'|                                                                         |
'|  If the key string input is not full length the key will be filled and  |
'|  left justified!  The DtaIn& should be the referenced record number     |
'|  as a long integer The RData& returned Will be a record pointer as a    |
'|  long integer.                                                          |
'|                                                                         |
'|  To use this version you must compile the code with the 32 bit pbdll    |
'|  compiler (ver 5.0). In Visual Basic You must declare the bt sub as     |
'|                                                                         |
'|  DECLARE SUB BT LIB "\MyDir\BT.DLL" (FileName$, Action$, KyIn$,_        |
'|                                      DtaIn&, RKey$, RData&, RCode%)     |
'|                                                                         |
'|  This version is released without copyright and is hereby placed        |
'|  directly into the public domain.  No warranty of any kind is provided. |
'|  Basically, you are on your own!                                        |
'+-------------------------------------------------------------------------+
$COMPILE DLL
$DIM ALL

'BT.Field.STATS:
'   FIELD BtFileN%, 2 AS HlfNode$, 2 AS KeyLen$, 2 AS DtaLen$, 2 AS ItmLen$,_
'   2 AS IDCode$, 4 AS RootNode$, 4 AS NxtNode$, 4 AS LstDel$, 2 AS NumAct$,_
'   4 AS NumKeys$

' The STATRECRD UDT Will replace the status field 

TYPE STATRECORD
   HlfNode    AS INTEGER
   KeyLen     AS INTEGER
   DtaLen     AS INTEGER
   ItmLen     AS INTEGER
   IDCode     AS STRING * 2
   RootNode   AS LONG
   NxtNode    AS LONG
   LstDel     AS LONG
   NumAct     AS INTEGER
   NumKeys    AS LONG
   Filler     AS STRING * 996
END TYPE

GLOBAL Stk&()
GLOBAL SttRec AS STATRECORD
GLOBAL RecBuf AS STRING * 1024
GLOBAL DPtr  AS STRING PTR
GLOBAL SPtr  As DWORD PTR
GLOBAL BtFileN%
GLOBAL StrLen%, Offset%
GLOBAL TmpBuf$

'BT.FIELD.NODE:
'  FIELD BtFileN%, 1 AS Act.Keys$, 4 AS Ptr$(0)
'  FOR Cnt% = 1 TO HlfNode% * 2
'    FIELD BtFileN%, 5 + ItmLen% * (Cnt% - 1) AS Tmp2$, (KeyLen%) AS Keys$(Cnt%), (DtaLen%) AS Dta$(Cnt%), 4 AS Ptr$(Cnt%)
'    FIELD BtFileN%, 5 + ItmLen% * (Cnt% - 1) AS Tmp2$, (ItmLen%) AS Itm$(Cnt%)
'  NEXT Cnt%

' The Following Functions Will Replace The field node

FUNCTION GetItmPl$ (Position%)
    FUNCTION = MID$(RecBuf, 6 + SttRec.ItmLen * (Position% - 1) ,SttRec.ItmLen)
END FUNCTION

SUB SetItmPl (Position%, Dt$)
  MID$(RecBuf, 6 + SttRec.ItmLen * (Position% - 1) ,SttRec.ItmLen) = Dt$
END SUB   
   
FUNCTION GetKeyPl$ (Position%)
    FUNCTION = MID$(RecBuf, 6 + SttRec.ItmLen * (Position% - 1) ,SttRec.KeyLen)
END FUNCTION

SUB SetKeyPl (Position%, Dt$)
    MID$(RecBuf, 6 + SttRec.ItmLen * (Position% - 1) ,SttRec.KeyLen) = Dt$
END SUB

FUNCTION GetDtaPl$ (Position%)
    FUNCTION = MID$(RecBuf, 6 + SttRec.ItmLen * (Position% - 1) + SttRec.KeyLen, 4)
END FUNCTION

SUB SetDtaPl (Position%, Dt$)
    MID$(RecBuf, 6 + SttRec.ItmLen * (Position% - 1) + SttRec.KeyLen, 4) = Dt$
END SUB

FUNCTION GetPtrPl$ (Position%)
  IF Position% = 0 THEN
     FUNCTION = MID$(RecBuf, 2, 4)  
  ELSE
     FUNCTION = MID$(RecBuf, 6 + SttRec.ItmLen * (Position% - 1) + SttRec.KeyLen + 4, 4)
  END IF
END FUNCTION

SUB SetPtrPl (Position%, Dt$)
    IF Position% = 0 THEN
       MID$(RecBuf, 2, 4) = Dt$
    ELSE
       MID$(RecBuf, 6 + SttRec.ItmLen * (Position% - 1) + SttRec.KeyLen + 4, 4) = Dt$
    END IF
END SUB

FUNCTION GetKeyAct%
  GetKeyAct% = ASC(RecBuf)
END FUNCTION

SUB SetKeyAct (Value%)
  MID$(RecBuf, 1, 1) = CHR$(Value%)
END SUB


SUB BT (FileName$, Action$, KyIn$, DtaIn&, RKey$, RData&, RCode%) EXPORT
  STATIC ItmPrt%, CurRec&, CurLvl%, LastFile$
  LOCAL LocFileName$, BtUpdtAlways%, UsrAct$, Status%
  LOCAL Ky$, Da$, RdTmpAdd$, CoreRecs&&, Temp&, TempStr$, Cnt%, WrkHlf%
  LOCAL TmpAdd$, Emerg$, FldStrt%, FldLen%, Tmp2$, Strt& 

  BtUpdtAlways% = (RCode% <> -99)
  ' if RCode% is not equal to -99 then index file will always be FLUSHed
  ' value of -1 means the file will be "FLUSH"ed after every write
  RKey$ = "": RData& = 0: RCode% = 0
  %BTMaxHalfNode = 25: BtFileN% = 200
  ' this is intended to access a single index file at a time
  FileName$ = UCASE$(FileName$): LocFileName$ = FileName$
  
  UsrAct$ = UCASE$(LEFT$(Action$ + " ", 1))
  IF UsrAct$ = "C" THEN
    GOSUB BT.Create
  ELSE
    ' if not creating then assume good status
    Status% = -1
    ' if not a close file (Q) status, check for open file
    ' open file if not already open
    IF UsrAct$ <> "Q" THEN
      IF FileName$ <> LastFile$ THEN GOSUB BT.OPEN.New
      IF LastFile$ = "" THEN Status% = 0
    END IF
    ' file open then select action
    
Bt.Start:
    IF Status% THEN
      IF UsrAct$ <> "Q" THEN
        Strt& = TIMER :Status% = 0
        DO 
          LOCK BtFileN%, 1 to 1
          IF ERR = 0 THEN Status% = - 1: EXIT DO
        LOOP UNTIL ABS(TIMER - Strt&) > 5
      END IF  
      SELECT CASE UsrAct$
        CASE "F"                               'Get First Key
          CurLvl% = 0: GOSUB BT.GET.NEXT
        CASE "L"                               'Get Last Key
          CurLvl% = 0: GOSUB BT.GET.PREV
        CASE "S"                                'Search for key in Ky$
          Ky$ = KyIn$: GOSUB BT.SEARCH
          IF Status% = 0 AND CVL(GetDtaPl$(ItmPrt%)) > 0 THEN Status% = -1
        CASE "G"                               'Get exact match
          Ky$ = KyIn$: GOSUB BT.SEARCH
        CASE "A"                               'Add a non-unique key
          Ky$ = KyIn$: DA$ = MKL$(DtaIn&): GOSUB BT.ADD.NON.UNIQUE
        CASE "U"                               'Add a unique key
          Ky$ = KyIn$: DA$ = MKL$(DtaIn&): GOSUB BT.ADD.UNIQUE
        CASE "D"                               'Delete the key/data given
          Ky$ = KyIn$: GOSUB BT.SEARCH
          DO UNTIL Status% = 0
            IF ItmPrt% = 0 THEN Status% = 0: EXIT DO
            IF Ky$ <> GetKeyPl$(ItmPrt%) THEN Status% = 0: EXIT DO
            IF DtaIn& = CVL(GetDtaPl$(ItmPrt%)) THEN
              GOSUB BT.Del.Cur: Status% = -1
              EXIT DO
            ELSE
              GOSUB BT.GET.NEXT
            END IF
          LOOP
        CASE "N"                               'Get Next Key
          Ky$ = KyIn$: GOSUB BT.SEARCH: GOSUB BT.GET.NEXT
        CASE "P"                               'Get Previous Key
          Ky$ = KyIn$: GOSUB BT.SEARCH: GOSUB BT.GET.PREV
        CASE "Q"
          Status% = -1: IF LastFile$ = "" THEN Status% = 0
        CASE ELSE                              'Error in Action code
          RKey$ = "": RdTmpAdd$ = "": Status% = 0
      END SELECT
      IF UsrAct$ <> "Q" THEN UNLOCK BtFileN%, 1 to 1
    END IF
    
    IF INSTR("AUDQ", UsrAct$) AND Status% AND (BtUpdtAlways% OR UsrAct$ = "Q") THEN
      PUT #BtFileN%, 1, SttRec: ' FLUSH (BtFileN%)
      IF UsrAct$ = "Q" THEN CLOSE BtFileN%: LastFile$ = ""
    END IF
  END IF
  IF ItmPrt% THEN
     RKey$ = GetKeyPl$(ItmPrt%): RData& = CVL(GetDtaPl$(ItmPrt%))
  ELSE
     RKey$ = "": RData& = 0
  END IF
  RCode% = Status%
  IF INSTR("PN", UsrAct$) THEN
    KyIn$ = RKey$: DtaIn& = RData&: UsrAct$ = "S": GOTO Bt.Start
  END IF
EXIT SUB

BT.OPEN.New:
  ' if file requested is not last file then up date
  ' current file and close, then open requested file
  IF LastFile$ <> "" THEN PUT #BtFileN%, 1, SttRec
  CLOSE BtFileN%
  OPEN LocFileName$ FOR RANDOM SHARED AS #BtFileN% LEN = 1024
  ' attempt to read file header
  GOSUB BT.GET.STATS
  ' if no header info in memory then close file and delete lastfile name
  IF Status% = 0 THEN
    LastFile$ = "": CLOSE BtFileN%
  ELSE
    LastFile$ = FileName$: GOSUB BT.GET.STATS
  END IF
  ' if all of this has been sucessful we are ready to "rock and roll"
 RETURN

BT.Create:
  CLOSE BtFileN%
  OPEN LocFileName$ FOR OUTPUT AS #BtFileN%
  CLOSE BtFileN%: BEEP:BEEP:BEEP
  OPEN LocFileName$ FOR RANDOM SHARED AS #BtFileN% LEN = 1024
  ' data passed in is ALWAYS a 4 byte record pointer
  ' key passed MUST be full length
  ' Data Length + Key Pointer Length = 8
  ' how many keys will fit inside a half page
  SttRec.KeyLen = LEN(KyIn$)
  SttRec.HlfNode = (1019 \ (SttRec.KeyLen + 8)) \ 2
  IF SttRec.HlfNode < 1 THEN Status% = 0: LastFile$ = "": RETURN
  IF SttRec.HlfNode > %BTMaxHalfNode THEN SttRec.HlfNode = %BTMaxHalfNode

  SttRec.DtaLen = 4:  SttRec.ItmLen = SttRec.KeyLen + 4 + 4
  SttRec.IDCode = "BL"
  SttRec.RootNode = 2:  SttRec.NxtNode = 3
  SttRec.LstDel = 0:  SttRec.NumAct = 1:  SttRec.NumKeys = 0
  PUT #BtFileN%, 1, SttRec
  Status% = -1:  CLOSE BtFileN%:  LastFile$ = ""
 RETURN

BT.GET.STATS:
  GET #BtFileN%, 1, SttRec
  IF SttRec.IDCode <> "BL" THEN
    Status% = 0:  LastFile$ = ""
  END IF
 RETURN

BT.GET.STACK.NODE:
  CurRec& = Stk&(CurLvl%, 0): ItmPrt% = Stk&(CurLvl%, 1): GOSUB BT.GET.CUR
 RETURN

BT.POP:
  CurLvl% = CurLvl% - 1: GOSUB BT.GET.STACK.NODE
 RETURN
 
BT.PUSH:
  Stk&(CurLvl%, 0) = CurRec&: Stk&(CurLvl%, 1) = ItmPrt%
 RETURN

BT.GET.CUR:
  CoreRecs&& = CurRec& * 1024
  IF (CoreRecs&& > LOF(BtFileN%)) THEN
    RecBuf = STRING$(1024, 0)
    PUT #BtFileN%, CurRec&, RecBuf
  END IF
  GET #BtFileN%, CurRec&, RecBuf
 RETURN

'*** SEARCH FOR FIRST OCCURANCE OF KEY ***

BT.SEARCH:
  Temp& = 0
BT.Non.Unq:
  Status% = 0:  CurLvl% = 1:  CurRec& = SttRec.RootNode
  IF LEN(Ky$) <> SttRec.KeyLen THEN Ky$ = LEFT$(Ky$ + STRING$(SttRec.KeyLen, " "), SttRec.KeyLen)
BT.SCAN.NODE:
  GOSUB BT.GET.CUR:  ItmPrt% = 1:  Cnt% = GetKeyAct
BT.S.N.LOOP:
  WrkHlf% = INT((ItmPrt% + Cnt%) / 2)
  IF WrkHlf% = 0 THEN
     ItmPrt% = WrkHlf% + 1
  ELSE
     IF (Ky$ > GetKeyPl$(WrkHlf%)) OR (Temp& < 0 AND Ky$ = GetKeyPl$(WrkHlf%)) THEN
       ItmPrt% = WrkHlf% + 1
     ELSE
      Cnt% = WrkHlf% - 1
     END IF
  END IF
  IF Cnt% >= ItmPrt% THEN
    GOTO BT.S.N.LOOP
  ELSE
    GOSUB BT.PUSH
    IF ItmPrt% <= GetKeyAct THEN
      IF Ky$ = GetKeyPl$(ItmPrt%) THEN
        Status% = -1: IF CVL(GetPtrPl$(ItmPrt% - 1)) = 0 THEN RETURN
      END IF
    END IF
  END IF
  IF CVL(GetPtrPl$(ItmPrt% - 1)) > 0 THEN
    CurRec& = CVL(GetPtrPl$(ItmPrt% - 1)): CurLvl% = CurLvl% + 1: GOTO BT.SCAN.NODE
  END IF
  IF Status% THEN GOTO BT.GN.L.SON
  IF Temp& = 0 THEN GOSUB BT.GN.OK:  Status% = 0
 RETURN

'*** ADD KEY AT CURRENT NODE LOCATION ***
BT.ADD.AT.CUR:
  TmpAdd$ = LEFT$(Ky$ + SPACE$(SttRec.KeyLen), SttRec.KeyLen) + LEFT$(DA$ + SPACE$(SttRec.DtaLen), SttRec.DtaLen) + MKL$(0)
  Temp& = 0
BT.CHK.FULL:
  IF GetKeyAct < SttRec.HlfNode * 2 THEN
    SetKeyAct GetKeyAct + 1: Cnt% = GetKeyAct
    GOSUB BT.INS.IN.NODE
    SetPtrPl ItmPrt% - 1, MKL$(Temp&)
    PUT #BtFileN%, CurRec&, RecBuf
    SetKeyAct GetKeyAct + 1: TmpAdd$ = "": TempStr$ = "": Emerg$ = "": Status% = -1
    RETURN
  END IF
  IF ItmPrt% > SttRec.HlfNode + 1 THEN
    GOTO BT.ADD.RIGHT
  ELSEIF ItmPrt% = SttRec.HlfNode + 1 THEN
    Emerg$ = TmpAdd$
  ELSE
    Emerg$ = GetItmPl$(SttRec.HlfNode): Cnt% = SttRec.HlfNode: GOSUB BT.INS.IN.NODE
  END IF
  SetPtrPl ItmPrt% - 1, MKL$(Temp&): SetKeyAct SttRec.HlfNode
'  FIELD BtFileN%, 5 + SttRec.HlfNode * (SttRec.ItmLen) AS Tmp2$, SttRec.HlfNode * (SttRec.ItmLen) AS Tmp2$
  FldStrt% = 6 + SttRec.HlfNode * (SttRec.ItmLen)
  FldLen% = SttRec.HlfNode * SttRec.ItmLen
  tmp2$ = MID$(RecBuf, FldStrt%, FldLen%)
  TempStr$ = tmp2$: PUT #BtFileN%, CurRec&, RecBuf: Temp& = CurRec&
  GOSUB BT.GET.AVAIL.NODE: GOSUB BT.SET.COPY: GOSUB BT.SET.RGHT.SON
 GOTO BT.WRT.NODE

BT.ADD.RIGHT:
'  FIELD BtFileN%, 1 AS Tmp2$, 4 + SttRec.HlfNode * (SttRec.ItmLen) AS Tmp2$
  FldStrt% = 2
  FldLen% = 4 + SttRec.HlfNode * (SttRec.ItmLen)
  tmp2$ = MID$(RecBuf, FldStrt%, FldLen%)
  TempStr$ = tmp2$: ItmPrt% = ItmPrt% - SttRec.HlfNode: Emerg$ = GetItmPl$(SttRec.HlfNode + 1)
  FOR Cnt% = 1 TO ItmPrt% - 2
    SetItmPl Cnt%, GetItmPl$(Cnt% + SttRec.HlfNode + 1)
  NEXT Cnt%
  SetItmPl ItmPrt% - 1, TmpAdd$
  IF ItmPrt% > SttRec.HlfNode THEN
    GOTO BT.SET.LFT.SON
  ELSE
    FOR Cnt% = ItmPrt% TO SttRec.HlfNode
    SetItmPl Cnt%, GetItmPl$(Cnt% + SttRec.HlfNode)
    NEXT Cnt%
  END IF
  
BT.SET.LFT.SON:
  GOSUB BT.SET.RGHT.SON: SetPtrPl ItmPrt% - 2, MKL$(Temp&)
  PUT #BtFileN%, CurRec&, RecBuf: GOSUB BT.GET.AVAIL.NODE
'  FIELD BtFileN%, 1 AS Tmp2$, LEN(TempStr$) AS Tmp2$
  FldStrt% = 2
  FldLen% = LEN(TempStr$)
  tmp2$ = MID$(RecBuf, FldStrt%, FldLen%)
  LSET tmp2$ = TempStr$
  MID$(RecBuf, FldStrt%, FldLen%) = tmp2$
  SetKeyAct SttRec.HlfNode: Temp& = CurRec&
BT.WRT.NODE:
  PUT #BtFileN%, CurRec&, RecBuf
  TmpAdd$ = Emerg$: CurLvl% = CurLvl% - 1
  IF CurLvl% = 0 THEN
    GOSUB BT.GET.AVAIL.NODE
    ItmPrt% = 1: SttRec.RootNode = CurRec&: SetPtrPl 0, MKL$(Temp&)
    GOTO BT.CHK.FULL
  ELSE
    GOSUB BT.GET.STACK.NODE: GOTO BT.CHK.FULL
  END IF
BT.INS.IN.NODE:
  FOR Cnt% = Cnt% TO ItmPrt% + 1 STEP -1
       SetItmPl Cnt%, GetItmPl$(Cnt% - 1)
  NEXT Cnt%
  SetItmPl ItmPrt%, TmpAdd$
 RETURN

BT.GET.AVAIL.NODE:
  IF SttRec.LstDel > 0 THEN
    CurRec& = SttRec.LstDel: GOSUB BT.GET.CUR: SttRec.LstDel = CVL(GetPtrPl$(0))
  ELSE
    CurRec& = SttRec.NxtNode: GOSUB BT.GET.CUR: SttRec.NxtNode = SttRec.NxtNode + 1
  END IF
  SttRec.NumAct = SttRec.NumAct + 1: SetKeyAct 0
 RETURN

BT.SET.RGHT.SON:
  SetKeyAct SttRec.HlfNode: SetPtrPl 0, RIGHT$(Emerg$, 4)
  MID$(Emerg$, LEN(Emerg$) - 3, 4) = MKL$(CurRec&)
 RETURN

BT.SET.COPY:
'  FIELD BtFileN%, 5 AS Tmp2$, LEN(TempStr$) AS Tmp2$: LSET Tmp2$ = TempStr$
  FldStrt% = 6
  FldLen% = LEN(TempStr$)
  tmp2$ = MID$(RecBuf, FldStrt%, FldLen%)
  LSET tmp2$ = TempStr$
  MID$(RecBuf, FldStrt%, FldLen%) = tmp2$
 RETURN
 
'*** Get Next Key in the Index ***
BT.GET.NEXT:
  IF CurLvl% = 0 THEN
    CurRec& = SttRec.RootNode: CurLvl% = 1: ItmPrt% = 1
  ELSE
    ItmPrt% = ItmPrt% + 1
  END IF
BT.GN.L.SON:
  GOSUB BT.GET.CUR
  IF CVL(GetPtrPl$(ItmPrt% - 1)) <> 0 THEN
    GOSUB BT.PUSH
    CurRec& = CVL(GetPtrPl$(ItmPrt% - 1)): CurLvl% = CurLvl% + 1: ItmPrt% = 1
    GOTO BT.GN.L.SON
  END IF
BT.GN.OK:
  IF ItmPrt% <= GetKeyAct THEN
    Status% = -1: RETURN
  ELSEIF CurLvl% = 1 THEN
    CurLvl% = 0: Status% = 0: RETURN
  ELSE
    GOSUB BT.POP:  GOTO BT.GN.OK
  END IF

'*** Get Previous Key in the Index ***
BT.GET.PREV:
  IF CurLvl% = 0 THEN CurRec& = SttRec.RootNode ELSE GOTO BT.GP.RHT
BT.DWN1:
  CurLvl% = CurLvl% + 1: GOSUB BT.GET.CUR: ItmPrt% = GetKeyAct + 1
BT.GP.RHT:
  GOSUB BT.PUSH
  IF CVL(GetPtrPl$(ItmPrt% - 1)) > 0 THEN
    CurRec& = CVL(GetPtrPl$(ItmPrt% - 1)): GOTO BT.DWN1
  END IF
BT.GP.OK:
  IF ItmPrt% > 1 THEN
    ItmPrt% = ItmPrt% - 1: Status% = -1: RETURN
  ELSEIF CurLvl% = 1 THEN
    Status% = 0: CurLvl% = 0: RETURN
  ELSE
    GOSUB BT.POP:  GOTO BT.GP.OK
  END IF

'*** Delete The Key at the Current Place in the Index ***
BT.Del.Cur:
  GOSUB BT.PUSH
  IF CVL(GetPtrPl$(ItmPrt%)) > 0 THEN
    GOTO BT.DC.REPLACE
  ELSE
    GOSUB BT.DECR.NODE
    IF ItmPrt% - 1 <> GetKeyAct THEN GOSUB BT.SHF.FM.RHT
  END IF
  PUT #BtFileN%, CurRec&, RecBuf
  IF (CurRec& = SttRec.RootNode) OR (GetKeyAct >= SttRec.HlfNode) THEN GOTO BT.DC.DONE
  DO
    GOSUB BT.UNDERFLOW
  LOOP UNTIL Status% = 0
BT.DC.DONE:
  SetKeyAct GetKeyAct - 1
 RETURN

BT.DC.REPLACE:
  GOSUB BT.GET.NEXT:  TmpAdd$ = GetItmPl$(ItmPrt%): GOSUB BT.GET.PREV
  GOSUB BT.REP.FTH.ITEM: PUT #BtFileN%, CurRec&, RecBuf: GOSUB BT.GET.NEXT
 GOTO BT.Del.Cur

BT.UNDERFLOW:
  Status% = -1: GOSUB BT.POP
  IF GetKeyAct = ItmPrt% - 1 THEN
    GOTO BT.UNF.2.LFT
  ELSE
    CurRec& = CVL(GetPtrPl$(ItmPrt%)): GOSUB BT.GET.MVBL: Emerg$ = GetPtrPl$(0)
  END IF
  IF WrkHlf% <= 0 THEN
    GOTO BT.MRG.RHT
  ELSE
'    FIELD BtFileN%, 5 AS Tmp2$, SttRec.ItmLen * (WrkHlf% - 1) AS Tmp2$
    FldStrt% = 6
    FldLen% = SttRec.ItmLen * (WrkHlf% - 1)
    tmp2$ = MID$(RecBuf, FldStrt%, FldLen%)
    TempStr$ = tmp2$: TmpAdd$ = GetItmPl$(WrkHlf%)
    SetPtrPl 0, GetPtrPl$(WrkHlf%): SetKeyAct GetKeyAct - WrkHlf%
    IF GetKeyAct > 0 THEN
      FOR Cnt% = 1 TO GetKeyAct
       SetItmPl Cnt%, GetItmPl$(Cnt% + WrkHlf%)
      NEXT Cnt%
    END IF
  END IF
  PUT #BtFileN%, CurRec&, RecBuf
  GOSUB BT.GET.STACK.NODE:  TempStr$ = GetItmPl$(ItmPrt%) + TempStr$
  GOSUB BT.REP.FTH.ITEM:  GOSUB BT.WRT.FTH
'  FIELD BtFileN%, 5 + SttRec.ItmLen * (SttRec.HlfNode - 1) AS Tmp2$, LEN(TempStr$) AS Tmp2$
  FldStrt% = 6 + SttRec.ItmLen * (SttRec.HlfNode - 1)
  FldLen% = LEN(TempStr$)
  tmp2$ = MID$(RecBuf, FldStrt%, FldLen%)
  LSET tmp2$ = TempStr$
  MID$(RecBuf, FldStrt%, FldLen%) = tmp2$
  SetPtrPl SttRec.HlfNode, Emerg$
 GOTO BT.ADJ.CNT

BT.MRG.RHT:
'  FIELD BtFileN%, 5 AS Tmp2$, SttRec.HlfNode * (SttRec.ItmLen) AS Tmp2$
  FldStrt% = 6
  FldLen% = SttRec.HlfNode * SttRec.ItmLen
  tmp2$ = MID$(RecBuf, FldStrt%, FldLen%)
  TempStr$ = tmp2$: tmp2$ = GetPtrPl$(0)
  SetKeyAct 0: SetPtrPl 0, MKL$(SttRec.LstDel)
  SttRec.LstDel = CurRec&: SttRec.NumAct = SttRec.NumAct - 1
  PUT #BtFileN%, CurRec&, RecBuf
  GOSUB BT.GET.STACK.NODE
  SetPtrPl ItmPrt%, tmp2$: TempStr$ = GetItmPl$(ItmPrt%) + TempStr$
  GOSUB BT.DECR.NODE
  IF CurRec& = SttRec.RootNode AND GetKeyAct = 0 THEN
    SttRec.RootNode = Stk&(CurLvl% + 1, 0)
    SetPtrPl 0, MKL$(SttRec.LstDel): SttRec.LstDel = CurRec&
    SttRec.NumAct = SttRec.NumAct - 1: Status% = 0
    GOTO BT.WRT.MOD.FTH
  END IF
  IF (GetKeyAct >= SttRec.HlfNode) OR (CurRec& = SttRec.RootNode) THEN Status% = 0
  IF GetKeyAct >= ItmPrt% THEN GOSUB BT.SHF.FM.RHT
BT.WRT.MOD.FTH:
  GOSUB BT.WRT.FTH
'  FIELD BtFileN%, 5 + SttRec.ItmLen * (SttRec.HlfNode - 1) AS Tmp2$, LEN(TempStr$) AS Tmp2$
  FldStrt% = 6 + SttRec.ItmLen * (SttRec.HlfNode - 1)
  FldLen% = LEN(TempStr$)
  tmp2$ = MID$(RecBuf, FldStrt%, FldLen%)
  GOTO BT.PUT.IN.BUF
BT.UNF.2.LFT:
  CurRec& = CVL(GetPtrPl$(ItmPrt% - 2)): GOSUB BT.GET.MVBL
  IF WrkHlf% <= 0 THEN GOTO BT.MRG.LFT
  SetKeyAct GetKeyAct - WrkHlf%
  TmpAdd$ = GetItmPl$(GetKeyAct + 1)
'  FIELD BtFileN%, 5 + SttRec.ItmLen * (ASC(Act.Keys$) + 1) AS Tmp2$, SttRec.ItmLen * (WrkHlf% - 1) AS Tmp2$
  FldStrt% = 6 + SttRec.ItmLen * (GetKeyAct + 1)
  FldLen% = SttRec.ItmLen * (WrkHlf% - 1)
  tmp2$ = MID$(RecBuf, FldStrt%, FldLen%)
  TempStr$ = tmp2$: Emerg$ = GetPtrPl$(GetKeyAct + 1)
  PUT #BtFileN%, CurRec&, RecBuf
  GOSUB BT.GET.STACK.NODE
  TempStr$ = TempStr$ + GetItmPl$(ItmPrt% - 1): SetItmPl ItmPrt% - 1, TmpAdd$
  SetPtrPl ItmPrt% - 1, MKL$(Stk&(CurLvl% + 1, 0))
  GOSUB BT.WRT.FTH
  IF SttRec.HlfNode > 1 THEN
    FOR Cnt% = SttRec.HlfNode - 1 TO 1 STEP -1
       SetItmPl Cnt% + WrkHlf%, GetItmPl$(Cnt%)
    NEXT Cnt%
  END IF
  GOSUB BT.SET.COPY
  SetPtrPl WrkHlf%, GetPtrPl$(0): SetPtrPl 0, Emerg$
BT.ADJ.CNT:
  SetKeyAct SttRec.HlfNode - 1 + WrkHlf%
  PUT #BtFileN%, CurRec&, RecBuf: Status% = 0
 RETURN

BT.MRG.LFT:
'  FIELD BtFileN%, 1 AS Tmp2$, 4 + ASC(Act.Keys$) * (SttRec.ItmLen) AS Tmp2$
  FldStrt% = 2
  FldLen% = 4 + GetKeyAct * SttRec.ItmLen
  tmp2$ = MID$(RecBuf, FldStrt%, FldLen%)
  TempStr$ = tmp2$: SetKeyAct 0: SetPtrPl 0, MKL$(SttRec.LstDel)
  SttRec.LstDel = CurRec&: SttRec.NumAct = SttRec.NumAct - 1
  PUT #BtFileN%, CurRec&, RecBuf
  GOSUB BT.GET.STACK.NODE
  TempStr$ = TempStr$ + LEFT$(GetItmPl$(ItmPrt% - 1), SttRec.ItmLen - 2)
  SetPtrPl ItmPrt% - 2, MKL$(Stk&(CurLvl% + 1, 0))
  GOSUB BT.DECR.NODE:  Status% = 0
  IF (CurRec& = SttRec.RootNode) AND (GetKeyAct = 0) THEN
    SttRec.RootNode = Stk&(CurLvl% + 1, 0)
    SetPtrPl 0, MKL$(SttRec.LstDel): SttRec.LstDel = CurRec&: SttRec.NumAct = SttRec.NumAct - 1
  ELSEIF (CurRec& <> SttRec.RootNode) AND (GetKeyAct < SttRec.HlfNode) THEN
    Status% = -1
  END IF
  GOSUB BT.WRT.FTH
'  FIELD BtFileN%, 5 AS Tmp2$, SttRec.ItmLen * ASC(Act.Keys$) AS Tmp2$
  FldStrt% = 6
  FldLen% = SttRec.ItmLen * GetKeyAct
  tmp2$ = MID$(RecBuf, FldStrt%, FldLen%)
  TempStr$ = TempStr$ + GetPtrPl$(0) + tmp2$
'  FIELD BtFileN%, 1 AS Tmp2$, LEN(TempStr$) AS Tmp2$
  FldStrt% = 6
  FldLen% = SttRec.ItmLen * GetKeyAct
  tmp2$ = MID$(RecBuf, FldStrt%, FldLen%)
BT.PUT.IN.BUF:
  LSET tmp2$ = TempStr$
  MID$(RecBuf, FldStrt%, FldLen%) = tmp2$
  SetKeyAct SttRec.HlfNode * 2
  PUT #BtFileN%, CurRec&, RecBuf
  IF Status% THEN GOSUB BT.POP
 RETURN

BT.SHF.FM.RHT:
  FOR Cnt% = ItmPrt% TO GetKeyAct
    SetItmPl Cnt%, GetItmPl$(Cnt% + 1)
  NEXT Cnt%
 RETURN

BT.WRT.FTH:
  PUT #BtFileN%, CurRec&, RecBuf: CurLvl% = CurLvl% + 1: GOSUB BT.GET.STACK.NODE
 RETURN
  
BT.DECR.NODE:
  SetKeyAct GetKeyAct - 1
 RETURN

BT.GET.MVBL:
  GOSUB BT.GET.CUR:  WrkHlf% = INT((GetKeyAct - SttRec.HlfNode + 1) / 2)
 RETURN

BT.REP.FTH.ITEM:
  tmp2$ = GetPtrPl$(ItmPrt%): SetItmPl ItmPrt%, TmpAdd$: SetPtrPl ItmPrt%, tmp2$
 RETURN
 
BT.ADD.NON.UNIQUE:
  Temp& = -1: GOSUB BT.Non.Unq: GOSUB BT.ADD.AT.CUR
 RETURN

BT.ADD.UNIQUE:
  Temp& = 1:  GOSUB BT.Non.Unq
  IF Status% THEN Status% = 0 ELSE GOSUB BT.ADD.AT.CUR
 RETURN
END SUB 'BT

FUNCTION LibMain( BYVAL hInstDLL     AS LONG, _
                  BYVAL fdwReason    AS LONG, _
                  BYVAL lpvReserved  AS LONG) EXPORT AS LONG
   DIM Stk&(53,0:1)
   BtFileN% = FREEFILE
   FUNCTION = 1                
END FUNCTION      
