//	B-Tree Maintenance Routines
// Copyright Xerox Corporation 1979, 1981, 1982


//	BTREEDEL.BCPL  -- Routines for deleting from B-Trees

//	last edited May 9, 1982  5:10 PM by Taft

get "btree.decl"

external
	[ Allocate	// Defined by OS
	Free
	DefaultArgs
	Dvec

	ReadBTreePage	// defined by OpenBTree
	WriteBTreePage
	LockBTreePtr
	UnlockBTreePtr
	FreeBTreePage
	BTreeBug

	PathRecLE	// Defined by BTreeRead.bcpl
	ReadPageAndLockBTreePtr
	PopStack
	RepairOffsets
	BackUpOneRecord

	RemoveEntry	// Defined by BTreeWrt.bcpl
	MakeEntSeqEnt
	AppendEntSeqEnt
	PushEntSeqEnt
	InsertRecords
	FabricateIS
	FreeIS
	]

external
	[ DeleteKey	// Defined in BTREEDEL.BCPL
	]


let DeleteKey(Tree, Key, CKRtn, PathStk, UseExistingPath; numargs na) = valof

	[ DefaultArgs(lv na, -2, Tree>>TREE.CompareKeyRtn, size PS/16, false)
	if PathStk eq size PS/16 then
		[ Dvec(DeleteKey, lv PathStk); UseExistingPath = false ]

	PathRecLE(Tree, PathStk, UseExistingPath, Key, CKRtn)

	let OrigPathStkTop = PathStk>>PS.PathStkTop
	if OrigPathStkTop eq 0 then resultis false

	let SP = lv (PathStk>>PS.PSE↑OrigPathStkTop)

	let PagePtr = nil
	ReadPageAndLockBTreePtr(Tree, SP>>PSE.PageNo, lv PagePtr)
	if CKRtn(Key, lv ((PagePtr+SP>>PSE.LastOffset)
			>>BTE.Record)) ne 0 then
		[ UnlockBTreePtr(Tree, lv PagePtr)
		resultis false
		]

	Tree>>TREE.RecordCount = Tree>>TREE.RecordCount-1
	Tree>>TREE.StateDirty = true
	Tree>>TREE.Version = Tree>>TREE.Version+1

	BackUpOneRecord(Tree, SP) // offset should index deletion victim
	let DescendantPg = (PagePtr+SP>>PSE.LastOffset)>>BTE.GrPtr
	let DSP = SP

	while DescendantPg ne Empty do
		[ // deletion surrogate is greatest key less than victim's
		PathStk>>PS.PathStkTop = PathStk>>PS.PathStkTop+1
		DSP = lv (PathStk>>PS.PSE↑(PathStk>>PS.PathStkTop))
		DSP>>PSE.PageNo = DescendantPg
		PagePtr = 0
		PagePtr = ReadBTreePage(Tree, DescendantPg)
		DSP>>PSE.Offset = (Tree>>TREE.PageLength)-
			(PagePtr>>BTP.FreeWords)
		DSP>>PSE.LastOffset = Rec1Offset
		DSP>>PSE.NextToLastOffset = Rec0Offset
		RepairOffsets(Tree, DSP)
		DSP>>PSE.ESLFront = Empty
		DSP>>PSE.ESLRear = Empty
		DSP>>PSE.LeastSon = Empty

		DescendantPg = (PagePtr+DSP>>PSE.LastOffset)>>BTE.GrPtr
		]

	UnlockBTreePtr(Tree, lv PagePtr)

	test PathStk>>PS.PathStkTop gr OrigPathStkTop
	ifso	[ BackUpOneRecord(Tree, DSP) // offset should index surrogate
		let LeafPageESE = RemoveEntry(Tree, DSP)
		let NonLeafPageESE = RemoveEntry(Tree, SP,
			lv (LeafPageESE>>ESLE.EntSeqP)>>BTE.GrPtr)
		AppendEntSeqEnt(SP, LeafPageESE)
		Free(Tree>>TREE.Zone, NonLeafPageESE)
		]
	ifnot	[ Free(Tree>>TREE.Zone, RemoveEntry(Tree, SP))
		]

	let IS = FabricateIS(Tree, PathStk)

	while PathStk>>PS.PathStkTop ge 0 &
		(FatherMayNeedWork(Tree, PathStk, IS)?
			true,
			PathStk>>PS.PathStkTop gr OrigPathStkTop)
		do
		[ PopStack(PathStk)
		]

	FreeIS(IS)
	PathStk>>PS.PathStkTop = 0
	resultis true
	]


and FatherMayNeedWork(Tree, PathStk, IS) = valof

	[ // This code assumes that
	// the son page is pointed to by the father page's
	// LastOffset index's GrPtr, and that this condition is
	// preserved by InsertRecords.
	

	let NeedsWork = ChangeInFather(Tree, PathStk, IS)

	let PathStkTop = PathStk>>PS.PathStkTop
	let SP = lv (PathStk>>PS.PSE↑PathStkTop)
	let CurPage = SP>>PSE.PageNo
	let CurPtr = nil
	ReadPageAndLockBTreePtr(Tree, SP>>PSE.PageNo, lv CurPtr)
	let FreeWords = CurPtr>>BTP.FreeWords

	if PathStkTop eq 1 & FreeWords eq IS>>IS.MaxFreeWords then
		// Bye-bye, old root page!
		[ Tree>>TREE.RootPage = CurPtr>>BTP.MinPtr
		Tree>>TREE.StateDirty = true
		UnlockBTreePtr(Tree, lv CurPtr)
		FreeBTreePage(Tree, SP>>PSE.PageNo)
		resultis false
		]

	if PathStkTop eq 1 %
		(IS>>IS.MaxFreeWords-FreeWords) ge IS>>IS.PrettyFull then
			[ UnlockBTreePtr(Tree, lv CurPtr)
			resultis NeedsWork
			]

	AppendEntSeqEnt(SP,
			MakeEntSeqEnt(Tree>>TREE.Zone, CurPtr+Rec1Offset,
				IS>>IS.MaxFreeWords-FreeWords))

	let SFP = lv (PathStk>>PS.PSE↑(PathStkTop-1))
	let OtherPtr = nil
	ReadPageAndLockBTreePtr(Tree, SFP>>PSE.PageNo, lv OtherPtr)
	let FatherOffset = SFP>>PSE.Offset
	let FatherFreeWords = OtherPtr>>BTP.FreeWords

	test ((FatherOffset-Rec1Offset) ls (IS>>IS.MaxFreeWords-
		FatherFreeWords)) % (SFP>>PSE.ESLFront ne Empty)

	ifso	// The current page has a right brother
		[ let RtBroPg = nil
		AppendEntSeqEnt(SP, RemoveEntry(Tree, SFP, lv RtBroPg))

		if (OtherPtr+SFP>>PSE.LastOffset)>>BTE.GrPtr ne CurPage then
			BTreeBug(Tree, ecMcCreightWasWrong)
		(OtherPtr+SFP>>PSE.LastOffset)>>BTE.GrPtr = RtBroPg

		OtherPtr = 0
		OtherPtr = WriteBTreePage(Tree, RtBroPg)
		OtherPtr>>BTP.MinPtr = CurPtr>>BTP.MinPtr

		SP>>PSE.PageNo = RtBroPg
		SP>>PSE.Offset = Rec1Offset
		SP>>PSE.LastOffset = Rec0Offset
		SP>>PSE.NextToLastOffset = Empty

		CurPtr = 0
		FreeBTreePage(Tree, CurPage)
		]

	ifnot	// The current page surely has a left brother.
		[ BackUpOneRecord(Tree, SFP) // offset should index last rec
		PushEntSeqEnt(SP, RemoveEntry(Tree, SFP))

		CurPtr = 0
		FreeBTreePage(Tree, CurPage)

		CurPage = (OtherPtr+SFP>>PSE.LastOffset)
					>>BTE.GrPtr
		SP>>PSE.PageNo = CurPage
		CurPtr = ReadBTreePage(Tree, CurPage)
		SP>>PSE.Offset = Rec1Offset+
			(IS>>IS.MaxFreeWords-CurPtr>>BTP.FreeWords)
		SP>>PSE.LastOffset = Rec1Offset
		SP>>PSE.NextToLastOffset = Rec0Offset
		RepairOffsets(Tree, SP)
		]

	UnlockBTreePtr(Tree, lv OtherPtr)
	UnlockBTreePtr(Tree, lv CurPtr)
	ChangeInFather(Tree, PathStk, IS)
	resultis true
	]


and ChangeInFather(Tree, PathStk, IS) = valof

	[ let SP = lv (PathStk>>PS.PSE↑(PathStk>>PS.PathStkTop))
	if SP>>PSE.ESLFront eq Empty then resultis false
	InsertRecords(Tree, PathStk, IS)
	if PathStk>>PS.PathStkTop eq 0 then resultis false
	resultis PathStk>>PS.PSE↑(PathStk>>PS.PathStkTop-1).ESLFront ne Empty
	]