Unit bTree; { Zak's Binary Tree Object / routines.. } {$O+,F+} { allow overlays } Interface Type KeyType = String[35]; {This can be changed if needed .., int, word, etc} Type StatusType = (Used,Free); Type ShowAllFuncType = Function (k:keytype;var Data):boolean; LeafType = record { A "living" leaf } Status: StatusType; { Status of node .. unused but useful } Mother,Left,Right:longint; { pointers to Parent, Left, and Right nodes } Key: KeyType; { the keyed data } end; GenericProcedure = procedure; { used to dispay balancing status } FileHeaderType = record DataRecSize, { size of data records } Root, { pointer to root node } NextFree: longint; { next free, unused node } end; DirectionType = (Right,Left); { the directions, duh } DeletedLeaf = record { a "dead" leaf -- overlaps old LeafType } Status : StatusType; { node status, hopefully Free} NextFree: longint; { pointer to next unused, free node } Filler : array[1..2] of longint; { pad LeafType.Left and Right } Filler2 : KeyType; { pad LeafType.Key } end; pbTreeObj = ^bTreeObj; bTreeObj = Object Constructor Init ( filename:string ; DataRecSize_:longint ); { Initialize the object.. DataRecSize_ is ignored if the file is not new (has been Init'd before)} Destructor Done; { unused the memory and close the file } Function Add (Key: keytype; Var Data):boolean; { Add Data by Key -- returns FALSE if key exists, otherwise TRUE } Function Find (key: keytype):boolean; { returns TRUE if key could be found, FALSE otherwise } Function FindData (key: keytype; var data):boolean; { if key is found, then returns TRUE and correct data, FALSE otherwise } Function Delete (key: keytype):boolean; { returns TRUE if successful, FALSE if key not found } Function BalanceHeapReq:longint; { returns bytes of heap required for a Balance } Procedure Balance (Reading,Sorting,Updating:GenericProcedure); { Makes the AVERAGE number of links needed to find a key the least possible } Procedure ShowAll (func:ShowAllFuncType); { cycles through all the nodes, calling func until it returns FALSE or no more nodes.. } function Update(key:keytype; Var Data):boolean; { if key found, writes new Data to it, otherwise returns FALSE } private { INTERNAL to the object } f:file; { the file we're playing with } dataRecSize:longint; { current data record size } Function RecOfs (n:longint):longint; { returns offset of given record } Procedure ReadRecLeaf (n:longint;var RecHdr:LeafType); { reads only the LeafType of record n } Procedure ReadRecBoth (n:longint;var RecHdr:LeafType;var data); { reads both the LeafType and the data } Procedure WriteRecLeaf (n:longint;RecHdr:LeafType); { writes only the LeafType} Procedure WriteRecBoth (n:longint;RecHdr:LeafType;var data); { write both the LeafType and Data } Procedure WriteRecData (n:longint;var data); { just write the data for record n } Function NumRecords (filehdr:fileheadertype):longint; { returns number of total records in file } Function GetNewRecNum (filehdr:fileheadertype):longint; { returns next free record number } Procedure ReadFileHdr (var filehdr:fileheadertype); { reads the file header .. cryptic, eh? } Procedure WriteFileHdr (filehdr:fileheadertype); { writes the file's header } Procedure FindNewMother(r:longint;filehdr:fileheadertype); { reassign this node a new, more suitable, parent when orphaned :-) } Function FindKeyRec (key: keytype):longint; { returns record number with this key, 0 otherwise } end; Implementation uses Dos; Constructor bTreeObj.Init( filename:string; datarecsize_:longint ); var fileheader:fileheadertype; t:word; begin {$I-} assign(f,filename); reset(f,1); {$I+} t:=ioresult; Case t of 0: begin { file exists.. ok so far } ReadFileHdr(fileheader); datarecsize:=fileheader.datarecsize; { init. prv. datarecsize } end; 2: begin { new file, let's initialize it, ok? } ReWrite(f,1); FileHeader.DataRecSize:=DataRecSize_; { setup header data } datarecsize:=datarecsize_; FileHeader.Root:=0; FileHeader.NextFree:=0; BlockWrite(f,FileHeader,Sizeof(FileHeader)) { write header data } end else RunError(t); { some other error .. } end end; Procedure bTreeObj.ShowAll (func:ShowAllFuncType); var fileheader:fileheadertype; rh :leaftype; data :pointer; cont :boolean; procedure climb(r:longint); var right:longint; begin ReadRecboth(r,rh,Data^); right:=rh.right; if not(rh.left=0) then begin Climb(rh.left); ReadRecBoth(r,rh,data^) { read back current data if needed } end; if not cont then exit; { "just checking" } cont := func(rh.key,data^); if not cont then exit; if not(right=0) then Climb(right); end; begin cont := true; ReadFileHdr(fileheader); GetMem(data,fileheader.datarecsize); if fileheader.root<>0 then Climb(fileheader.root); FreeMem(data,fileheader.datarecsize); end; Destructor bTreeObj.Done; begin close(f) { just close the file.. no big deal } end; Function bTreeObj.Add(Key: keytype; var data):boolean; var FileHdr: FileHeaderType; RecHdr : LeafType; Procedure AddNewRec; Function FindMother(var direction:directiontype):longint; var RecHdr :leaftype; LastNode:longint; procedure Search_Tree(n:longint); begin ReadRecLeaf(n,RecHdr); if Key>RecHdr.Key then if not(RecHdr.Right=0) then Search_Tree(RecHdr.Right) else begin LastNode:=n; Direction:=Right; end else if KeyNumRecords(filehdr)) then begin ReadRecLeaf(NewRecNum,NewRecHdr); FileHdr.NextFree:=DeletedLeaf(NewRecHdr).NextFree; end; Case MotherDirection of Right: MotherRecHdr.Right:=NewRecNum; Left : MotherRecHdr.Left :=NewRecNum; end; With NewRecHdr do { initialize record.. } begin Status := used; Right := 0; Left := 0; Mother := MotherRec; end; NewRecHdr.Key:=Key; WriteFileHdr(FileHdr); { update file header } WriteRecLeaf(MotherRec,MotherRecHdr); { write mother } WriteRecBoth(newrecnum,NewRecHdr,Data); { write daughter } end; procedure AddFirstRec; begin { we're adding the first record in the file.. scary eh? } With RecHdr do { init. it } begin Status := Used; Right := 0; Left := 0; Mother := 0; end; RecHdr.key:=key; FileHdr.Root := 1; FileHdr.NextFree := 0; Seek(f,0); BlockWrite(f,Filehdr,sizeof(filehdr)); BlockWrite(f,RecHdr,Sizeof(RecHdr)); BlockWrite(f,data,filehdr.datarecsize); end; begin if not Find(key) then { if not found, then .. } begin ReadFileHdr(filehdr); if FileHdr.Root=0 then AddFirstRec else AddNewRec; add := true; end else Add := false; end; Function bTreeObj.Find (key: keytype):boolean; begin Find:=FindKeyRec(key)>0; { or BOOLEAN(FindKey(key)) would work too } end; Function bTreeObj.Update(key:keytype; Var Data):boolean; var i:longint; begin i:=FindKeyRec(key); if i=0 then begin Update:=False; end else begin WriteRecData(i,data); update:=true; end end; Function bTreeObj.FindData (key: keytype; var data):boolean; var filehdr:fileheadertype; rechdr :leaftype; r :longint; begin r:=FindKeyRec(key); if r>0 then begin ReadRecBoth(r,rechdr,data); FindData:=true; end else finddata:=false end; Function bTreeObj.Delete(key: keytype):boolean; var filehdr:fileheadertype; procedure Unlink(r:longint;var delhdr:leaftype); Function GetDirection(sonhdr:leaftype):directiontype; var sonrighthdr,sonlefthdr,motherhdr:leaftype; sre,sle:boolean; begin ReadRecLeaf(sonhdr.mother,motherhdr); if not(motherhdr.left=0) then begin ReadRecLeaf(motherhdr.left,sonlefthdr); sle:=true end else sle:=false; if not(motherhdr.right=0) then begin ReadRecLeaf(motherhdr.right,sonrighthdr); sre:=true; end else sre:=false; {$B-} if sle and not sre then GetDirection:=Left else if sre and not sle then GetDirection:=Right else if (sle and sre) and (sonrighthdr.key=sonhdr.key) then GetDirection:=Right else if (sle and sre) and (sonlefthdr.key=sonhdr.key) then GetDirection:=left; {$B+} end; var MotherHdr:leaftype; direction:directiontype; begin if not(DelHdr.Mother=0) then begin ReadRecLeaf(DelHdr.Mother,MotherHdr); Direction:=GetDirection(DelHdr); case Direction Of Left : MotherHdr.Left:=0; Right: MotherHdr.Right:=0; end; WriteRecLeaf(delhdr.mother,motherhdr); end end; Procedure UpdateFreeList(r:longint); function LastFree:longint; var rechdr:leaftype;n,ths:longint; begin n:=filehdr.nextfree; ths:=n; repeat begin ReadRecLeaf(n,rechdr); ths:=n; n:=deletedleaf(rechdr).nextfree; end until DeletedLeaf(RecHdr).nextfree=0; LastFree:=ths; end; Var updatedptrhdr:leaftype;lf:longint; begin if filehdr.nextfree=0 then begin filehdr.nextfree:=r; writefilehdr(filehdr); end else begin lf:=lastfree; ReadRecLeaf(Lf,updatedptrhdr); DeletedLeaf(updatedptrhdr).nextfree:=r; WriteRecLeaf(lf,updatedptrhdr); end; end; Procedure AddChildren(var dhdr:leaftype); begin if not(dhdr.left=0) then FindNewMother(dhdr.left,filehdr); if not(dhdr.right=0) then FindNewMother(dhdr.right,filehdr); end; Procedure ChangeMother(r,tor:longint); var rechdr:leaftype; begin ReadRecLeaf(r,rechdr); rechdr.mother:=tor; WriteRecLeaf(r,rechdr); end; { this is huge } var DelRecNum:longint; delhdr :leaftype; begin ReadFileHdr(filehdr); DelRecNum:=FindKeyRec(key); { find the record we're refering to } DelHdr.Status:=Free; { change its status } if not(DelRecNum>0) then Delete:=False else begin ReadRecLeaf(delrecnum,delhdr); { read the dead-to-be's header } if delhdr.Mother=0 then { we're dealing with the ROOT node ! } begin Delete:=true; UpdateFreeList(delrecnum); { add to free list } if not(delhdr.Right=0) then begin FileHdr.Root := delhdr.Right; ChangeMother(delhdr.Right,0); if not(delhdr.left=0) then FindNewMother(delhdr.left,filehdr); end; if not(delhdr.left=0) and (delhdr.right=0) then begin FileHdr.Root := delhdr.Left; ChangeMother(delhdr.Left,0); end; if (delhdr.right=0) and (delhdr.left=0) then begin FileHdr.Root:=0; end; DelHdr.Status:=Free; WriteFileHdr(filehdr); DeletedLeaf(DelHdr).NextFree:=0; WriteRecLeaf(delrecnum,delhdr); end else { the easy part } begin Delete:=true; Unlink(DelRecNum,delhdr); { unlink it from its parent } UpdateFreeList(delrecnum); { add to free list } DeletedLeaf(DelHdr).NextFree:=0; { this is the last in the chain .. } WriteRecLeaf(delrecnum,delhdr); AddChildren(delhdr); { re-classify its offspring } end; end; end; Function bTreeObj.BalanceHeapReq:longint; var rechdr :leaftype; filehdr :fileheadertype; numnodes :longint; procedure Climb(r:longint); begin ReadRecLeaf(r,rechdr); if not(rechdr.left=0) then Climb(rechdr.left); ReadRecLeaf(r,rechdr); inc(numnodes); if not(rechdr.right=0) then Climb(rechdr.right); end; begin numnodes:=0; readfilehdr(filehdr); if not(FileHdr.Root=0) then Climb(FileHdr.Root); balanceheapreq:=numnodes*20; { sizeof(ListRecType) } end; Procedure bTreeObj.Balance(Reading,Sorting,Updating:GenericProcedure ); type ToListRecType = ^ListRecType; ListRecType = Record node,mother,left,right:longint; Next:ToListRecType; end; var filehdr : fileheadertype; ListRecRoot : ToListRecType; NumNodes : longint; MarkMem : pointer; Procedure ReadFileToLL; var rechdr :leaftype; curlistrec:tolistrectype; Procedure Add(r:longint); begin inc(NumNodes); if CurListRec=Nil then begin new(CurListRec); CurListRec^.Next := Nil; ListRecRoot := CurListRec; end else begin New(CurListRec^.next); CurListRec:=CurListRec^.Next; CurListRec^.Next := Nil; end; CurListRec^.Node:=r; CurListRec^.Mother:=0; CurListRec^.Left:=0; CurListRec^.Right:=0; end; procedure Climb(r:longint); begin ReadRecLeaf(r,rechdr); if not(rechdr.left=0) then Climb(rechdr.left); ReadRecLeaf(r,rechdr); Add(r); if not(rechdr.right=0) then Climb(rechdr.right); end; begin CurListRec:=ListRecRoot; if not(FileHdr.Root=0) then Climb(FileHdr.Root); end; Procedure GetRecNumInfo(n:longint; var mother,left,right:longint); var c:tolistrectype; begin c:=listrecroot; while c^.node<>n do c:=c^.next; mother:=c^.mother; left:=c^.left; right:=c^.right; end; Procedure PutRecNumInfo(n,mother,left,right:longint); var c:tolistrectype; begin c:=listrecroot; while c^.node<>n do c:=c^.next; c^.mother:=mother; c^.left:=left; c^.right:=right; end; Function Power(b,e:longint):longint; var t,c:longint; begin t:=b; if e=0 then begin power:=1 ; exit end; for c:=1 to e-1 do t:=t*b; power:=t; end; Procedure ProcessLL; var MaxNumNodes: longint; NumSubLevels : longint; TempMother,TempRight,TempLeft:longint; Modifier : longint; Function FindNumSubLevels(n:longint):longint; var i:longint; begin i:=1; repeat inc(i,1) until (power(2,i)>=n+1); FindNumSubLevels:=i-1; end; Function RightMod(root,modi:longint):longint; begin repeat begin modi := modi div 2; end until root+modi<=numnodes; RightMod := modi; end; Procedure FixSubTree(root:longint;mthr:longint); var sr:longint; begin if not(abs(mthr-root)=1) then begin modifier:=abs(mthr-root) div 2; templeft:=root-modifier; if (root+modifier<=NumNodes) then tempright:=root+modifier else begin modifier:=Rightmod(root,modifier); if not(modifier=0) then TempRight:=root+modifier else tempright:=0; end; tempmother:=mthr; PutRecNumInfo(root,tempmother,templeft,tempright); sr:=tempright; if not(templeft=0) then FixSubTree(templeft,root); if not(sr=0) then FixSubTree(sr,root); end else { lowest leaves } begin PutRecNumInfo(root,mthr,0,0); end; end; Function MaxNodes:longint; var i:longint; begin i:=0; repeat inc(i,1) until (power(2,i+1)-1)>=NumNodes; MaxNodes:= Power(2,i+1)-1; end; Var NewRoot:longint; begin MaxNumNodes := MaxNodes; NumSubLevels := FindNumSubLevels(MaxNumNodes); { number of "shelves" } if NumNodes<2 then NewRoot:=FileHdr.Root else NewRoot:=Power(2,NumSubLevels); FileHdr.Root := NewRoot; FixSubTree(NewRoot,0); end; Procedure WriteLLtoFile; var CurListRec: tolistrectype; l:leaftype; begin curlistrec:=listrecroot; while curlistrec<>nil do begin ReadRecLeaf(curlistrec^.node,l); l.left:=curlistrec^.left; l.right:=curlistrec^.right; l.mother:=curlistrec^.mother; WriteRecLeaf(curlistrec^.node,l); curlistrec:=curlistrec^.next; end; end; begin NumNodes := 0; ListRecRoot:=nil; Mark(MarkMem); ReadFileHdr(filehdr); reading; { status } if not(filehdr.root=0) then ReadFileToLL; { if there are >0 records then } sorting; { status } { read data into the linked list} if not(filehdr.root=0) then ProcessLL; { change data in LL } updating; { status } if not(filehdr.root=0) then WriteLLtoFile; { updated disk with LL data } WriteFileHdr(filehdr); Release(MarkMem); end; {privates} Function bTreeObj.RecOfs(n:longint):longint; begin RecOfs:=Sizeof(FileHeaderType)+((n-1)*(DataRecSize+Sizeof(LeafType))); end; Procedure bTreeObj.ReadRecLeaf(n:longint;var RecHdr:LeafType); begin seek(f,recofs(n)); blockread(f,rechdr,sizeof(leaftype)); end; Procedure bTreeObj.ReadRecBoth(n:longint;var RecHdr:LeafType;var data); begin seek(f,recofs(n)); blockread(f,rechdr,sizeof(rechdr)); blockread(f,data,datarecsize); end; Procedure bTreeObj.WriteRecLeaf(n:longint;RecHdr:LeafType); begin seek(f,recofs(n)); blockwrite(f,rechdr,sizeof(rechdr)); end; Procedure bTreeObj.WriteRecBoth(n:longint;RecHdr:LeafType;var data); begin seek(f,recofs(n)); blockwrite(f,rechdr,sizeof(rechdr)); blockwrite(f,data,datarecsize); end; Procedure bTreeObj.WriteRecData (n:longint;var data); begin Seek(f,recofs(n)+Sizeof(LeafType)); blockwrite(f,data,datarecsize); end; Function bTreeObj.NumRecords(filehdr:fileheadertype):longint; var tv:longint; begin NumRecords := (FileSize(f)-Sizeof(FileHdr)) div (Sizeof(LeafType)+FileHdr.DataRecSize); end; Function bTreeObj.GetNewRecNum(filehdr:fileheadertype):longint; begin if filehdr.nextfree=0 then begin GetNewRecNum := NumRecords(filehdr)+1; exit end else GetNewRecNum := FileHdr.NextFree; end; Procedure bTreeObj.ReadFileHdr(var filehdr:fileheadertype); begin seek(f,0); blockread(f,FileHdr, sizeof(filehdr)); end; Procedure bTreeObj.WriteFileHdr( filehdr:fileheadertype); begin seek(f,0); blockwrite(f,FileHdr, sizeof(filehdr)); end; Procedure bTreeObj.FindNewMother ( r:longint;filehdr:fileheadertype); var rechdr:leaftype; Function FindMother(var direction:directiontype):longint; var Hdr :leaftype; LastNode:longint; procedure Search_Tree(n:longint); begin ReadRecLeaf(n,Hdr); if RecHdr.Key>Hdr.Key then if not(Hdr.Right=0) then Search_Tree(Hdr.Right) else begin LastNode:=n; Direction:=Right; end else if RecHdr.KeyRecHdr.Key then if not(RecHdr.Right=0) then FindKey(RecHdr.Right) else begin FindKeyRec:=0; end else if Key