-- File DJExtMerge3.mesa
-- Written by Martin Newell/Dan Fitzpatrick, June 1981
-- Last updated (Pilot): 12-Aug-81 10:09:02

DIRECTORY

DJExtMergeDefs: FROM "DJExtMergeDefs",
DJExtTypes: FROM "DJExtTypes" USING [NodeNumber],
Runtime: FROM "Runtime" USING [CallDebugger],
Space USING[Handle, Create, CreateUniformSwapUnits, Map, LongPointer, virtualMemory];

DJExtMerge2: PROGRAM
IMPORTS Runtime, Space
EXPORTS DJExtMergeDefs =

BEGIN OPEN DJExtTypes, Runtime;


InitMerge: PUBLIC PROCEDURE [] =
-- Initialize merge to use file fileName
	BEGIN
	GenValue ← ZeroValue;
	SmallValue ← ZeroValue;
	OpenMergeFile[];
	END;

ConsolidateMerge: PUBLIC PROCEDURE =
	BEGIN
	END;

FinishMerge: PUBLIC PROCEDURE =
-- Close files and release structures
	BEGIN
	CloseMergeFile[];
	END;

GenNodeNumber: PUBLIC PROCEDURE RETURNS[n: NodeNumber] =
--Generate new Merge value
	BEGIN
	RETURN[GenValue ← GenValue + 1];
	END;

ReserveNodeNumbers: PUBLIC PROCEDURE [n: NodeNumber] =
	BEGIN
	GenValue ← GenValue + n;
	END;

Merge: PUBLIC PROCEDURE [n1,n2: NodeNumber] RETURNS[r: NodeNumber] =
--Merge numbers n1 and n2 towards smaller of the two
--Returns resulting value of Lookup[n1] (=Lookup[n2])
BEGIN
	r1,r2: NodeNumber;
	IF n1 = 0 OR n2 = 0 THEN CallDebugger["Merge called with 0"];
	IF n1 = n2 THEN RETURN[n1];
--find roots, r1,r2 of n1,n2
	[r1] ← IntLookup[n1];
	[r2] ← IntLookup[n2];
--merge roots
--new root is min of r1 and r2
	IF LessThan[r1,r2] THEN {
		r ← r1;
		[] ← PutNodeNumber[r2,r];
		}
	ELSE {
		r ← r2;
		[] ← PutNodeNumber[r1,r];
		};
--shorten paths from n1 and n2 to new root
	[] ← PutNodeNumber[n1,r];
	[] ← PutNodeNumber[n2,r];
	END;

Lookup: PUBLIC PROCEDURE[n: NodeNumber]
		RETURNS[r: NodeNumber] =
--Return smallest number to which n has been merged, transitively closed
	BEGIN
	IF n = 0 THEN CallDebugger["Lookup called with 0"];
	r ← IntLookup[n].r;
	END;

LookSmall: PUBLIC PROCEDURE[n: NodeNumber]
		RETURNS[r: NodeNumber] =
--Return smallest number it can assign to n
	BEGIN
	IF n = 0 THEN CallDebugger["LookSmall called with 0"];
	r ← IntLookup2[n].r;
	END;

GetSmall: PUBLIC PROCEDURE RETURNS[NodeNumber] =
--Return smallest number assigned
BEGIN
	RETURN[SmallValue];
	END;

IsSmall: PUBLIC PROCEDURE[n: NodeNumber] RETURNS[BOOLEAN] =
--Return true if it has been assigned a small number
BEGIN
	r,t: NodeNumber;
	flag: BOOLEAN ← FALSE;
	IF n = 0 THEN CallDebugger["IsSmall called with 0"];
	--find root, r, of n
	[t,flag] ← GetValue[n];
	r ← n;
	UNTIL flag OR t = r DO
		r ← t;
		[t,flag] ← GetValue[r];
		ENDLOOP;
	RETURN[flag];
	END;

PutProp: PUBLIC PROCEDURE[n: NodeNumber, prop:LONG UNSPECIFIED] =
--attach prop to node number n
BEGIN
	PutData[n,prop];
	END;

GetProp: PUBLIC PROCEDURE[n: NodeNumber] RETURNS[LONG UNSPECIFIED] =
--get the prop that has been attached to n.
BEGIN
	v: LONG  UNSPECIFIED;
	v ← Get[n].data;
	RETURN[v];
	END;


--Private procedures--

OpenMergeFile: PROCEDURE =
	BEGIN
	IF BaseAddress=NIL THEN
	{	BaseSpace ← Space.Create[MergeTableSizeP, Space.virtualMemory];
		BaseAddress ← Space.LongPointer[BaseSpace];
		NPagesMapped ← 0;
		NWordsMapped ← 0;
	};
	NPagesAllocated ← 0;
	NWordsAllocated ← 0;
	END;

CloseMergeFile: PROCEDURE =
	BEGIN
	END;

IntLookup: PROCEDURE[n: NodeNumber]
		RETURNS[r: NodeNumber] =
--Return smallest number to which n has been merged, transitively closed
	BEGIN
	t: NodeNumber ← 0;
	flag: BOOLEAN ← FALSE;
--find root, r, of n
	[t] ← GetValue[n];
	r ← n;
	UNTIL flag OR t = r DO
		r ← t;
		[t,flag] ← GetValue[r];
	ENDLOOP;
	IF flag THEN CallDebugger["Lookup called after LookSmall"];
	IF n # r THEN [] ← PutNodeNumber[n,r];
	END;

IntLookup2: PROCEDURE[n: NodeNumber]
		RETURNS[r: NodeNumber] =
--Return smallest number it can assign to n
	BEGIN
	t: NodeNumber;
	flag: BOOLEAN ← FALSE;
--find root, r, of n
	[t,flag] ← GetValue[n];
	r ← n;
	UNTIL flag OR t = r DO
		r ← t;
		[t,flag] ← GetValue[r];
	ENDLOOP;
	IF n # r THEN [] ← PutNodeNumber[n,r];		-- otherwise you'ld destroy small value
	IF flag THEN {
		r ← LOOPHOLE[LOOPHOLE[t,LONG CARDINAL] - 2B10]
		}
	 ELSE {
		SmallValue ← SmallValue + 1;
		[] ← PutNodeNumber[r,LOOPHOLE[SmallValue+2B10]];
		r ← SmallValue;
		};
	END;

GetValue: PROCEDURE[n: NodeNumber]
		RETURNS[v: NodeNumber, done:BOOLEAN] =
	BEGIN
	v ← Get[n].v;
	IF v = ZeroValue THEN v ← n;
	done ← (LOOPHOLE[v,LONG CARDINAL] >= 2B10);
	END;

LessThan: PROCEDURE[n1,n2: NodeNumber] RETURNS[BOOLEAN] = INLINE
	BEGIN
	c1: LONG CARDINAL ← LOOPHOLE[n1];
	c2: LONG CARDINAL ← LOOPHOLE[n2];
	RETURN[c1<c2];
	END;

GenValue: NodeNumber;
SmallValue: NodeNumber;
ZeroValue: NodeNumber = LOOPHOLE[LONG[0],NodeNumber];

MergeError: PUBLIC ERROR[reason: STRING] = CODE;

Get: PROCEDURE [n:NodeNumber] RETURNS[v:NodeNumber,data:LONG UNSPECIFIED] =
--return the record associated with node number n
BEGIN
	addr: Thing ← Validate[n];
	RETURN[addr.v,addr.data];
	END;

PutNodeNumber: PROCEDURE [n:NodeNumber, v:NodeNumber] =
-- put the value v at node location n
BEGIN
	Validate[n].v ← v;
	END;

PutData: PROCEDURE [n:NodeNumber, data:LONG UNSPECIFIED] =
-- put the data at node location n
BEGIN
	Validate[n].data ← data;
	END;

Validate: PROCEDURE [n:LONG CARDINAL] RETURNS[Thing] = INLINE
-- return a Thing, allocating it if necessary
BEGIN
	index: LONG CARDINAL ← 2*n;
	UNTIL index<NWordsAllocated DO ExtendAllocation[]; ENDLOOP;
	RETURN[BaseAddress+index];
	END;

ExtendAllocation: PROCEDURE =
-- Extend size of allocated space by one 'block'
BEGIN
	IF NWordsMapped=NWordsAllocated THEN
	{	h: Space.Handle ← Space.Create[BlockSizeP,BaseSpace,NPagesMapped];
		Space.CreateUniformSwapUnits[4,h];
		Space.Map[h];
		NPagesMapped ← NPagesMapped + BlockSizeP;
		NWordsMapped ← NWordsMapped + BlockSizeW;
	};
	Zero[BaseAddress+NWordsAllocated, BlockSizeW];
	NPagesAllocated ← NPagesAllocated + BlockSizeP;
	NWordsAllocated ← NWordsAllocated + BlockSizeW;
	END;

Zero: PROCEDURE [addr:LONG POINTER, nwords: LONG CARDINAL] = INLINE
-- There MUST be a fast system procedure for this!!!
	{ FOR i:LONG CARDINAL IN [0..nwords) DO (addr+i)↑ ← 0; ENDLOOP; };


Thing:TYPE = LONG POINTER TO ThingRecord;
ThingRecord: TYPE = RECORD [
		v: NodeNumber,
		data: LONG UNSPECIFIED
		];
		
MergeTableSizeP: CARDINAL = 10000;	-- Max size of Merge file in Pages
BlockSizeP: CARDINAL = 128;		-- Block size in Pages
BlockSizeW: LONG CARDINAL = BlockSizeP*256;	-- Block size in Words
BaseSpace: Space.Handle;		-- Address space of merge table
BaseAddress: LONG POINTER ← NIL;	-- Address of start of ParentSpace
NPagesMapped: CARDINAL;			-- Number of pages mapped to file
NWordsMapped: LONG CARDINAL;		-- NPagesMapped*256
NPagesAllocated: CARDINAL;		-- Number of pages allocated for use
NWordsAllocated: LONG CARDINAL;		-- NPagesAllocated*256


END.