-- TextTestImpl.mesa
-- written by Bill Paxton, April 1981
-- last edit by Bill Paxton, 11-Jun-81 15:23:01

-- This module provides random testing for editing Text nodes
-- Initially was part of EditTestImpl.mesa

DIRECTORY
EditTest,
TextEdit,
RopeEdit,
TextNode,
RunReader,
RopeReader,
Rope,
RopeInline,
RandomLongInt;

TextTestImpl: PROGRAM
	IMPORTS EditTest, TextEdit, RopeEdit,
		RunReader, RopeReader, Rope,
		RandomLongInt
	EXPORTS EditTest =
BEGIN
OPEN
	rI:Rope,
	EditTest,
	editI:TextEdit,
	ropeI:RopeEdit,
	nodeI:TextNode,
	runrdrI:RunReader,
	roperdrI:RopeReader,
	randLI:RandomLongInt;
	
-- ***** Text Edit operations

ReplaceText: PUBLIC PROC = {
	source, dest: Node;
	destStart, destEnd, sourceStart, sourceEnd: Offset;
	[source,dest] ← PickNodes[];
	[destStart,destEnd] ← PickTwo[dest];
	[sourceStart,sourceEnd] ← PickTwo[source];
	DoReplaceText[dest, source, destStart, destEnd, sourceStart, sourceEnd];
	AdjustLength[dest] };

DoReplaceText: PROC [
	dest, source: Node, destStart, destEnd, sourceStart, sourceEnd: Offset] = {
	destLen, sourceLen, oldPos, newPos, oldSize: Offset;
	sourceRope, newRope, oldRope: Rope;
	sourceRuns, newRuns, oldRuns: Runs;
	sourceRope ← editI.GetRope[source];
	sourceRuns ← editI.GetRuns[source];
	oldSize ← editI.Size[dest];
	oldRope ← editI.GetRope[dest];
	oldRuns ← editI.GetRuns[dest];
	editI.ReplaceText[
		dest,destStart,destLen←destEnd-destStart,
		source,sourceStart,sourceLen←sourceEnd-sourceStart];
	newRope ← editI.GetRope[dest];
	newRuns ← editI.GetRuns[dest];
	CheckSize[dest,oldSize+sourceLen-destLen];
	CheckRopes[newRope,0,oldRope,0,destStart];
	CheckRopes[newRope,destStart,sourceRope,sourceStart,sourceLen];
	oldPos ← destStart+destLen; newPos ← destStart+sourceLen;
	CheckRopes[newRope,newPos,oldRope,oldPos,oldSize-oldPos];
	CheckRuns[newRuns,0,oldRuns,0,destStart];
	CheckRuns[newRuns,destStart,sourceRuns,sourceStart,sourceLen];
	CheckRuns[newRuns,newPos,oldRuns,oldPos,oldSize-oldPos] };

DeleteText: PUBLIC PROC = {
	node: Node ← PickNode[];
	DeleteFromNode[node];
	AdjustLength[node] };

DeleteFromNode: PUBLIC PROC [node: Node] = {
	start, end: Offset;
	[start,end] ← PickTwo[node];
	DoReplaceText[node,NIL,start,end,0,0] };

CopyText: PUBLIC PROC = {
	source, dest: Node;
	destLoc, sourceStart, sourceEnd: Offset;
	[source,dest] ← PickNodes[];
	[sourceStart,sourceEnd] ← PickTwo[source];
	destLoc ← PickOne[dest];
	DoReplaceText[dest, source, destLoc, destLoc, sourceStart, sourceEnd];
	AdjustLength[dest] };

MoveText: PUBLIC PROC = {
	source, dest: Node;
	destLoc, sourceStart, sourceEnd, sourceLen, oldSourceSize, oldDestSize: Offset;
	oldSourceRope, newSourceRope, oldDestRope, newDestRope: Rope;
	oldSourceRuns, newSourceRuns, oldDestRuns, newDestRuns: Runs;
	moveToRight: BOOLEAN;
	[source,dest] ← PickNodes[];
	[sourceStart,sourceEnd] ← PickTwo[source];
	IF source=dest THEN { 
		IF moveToRight←RandomBoolean[] THEN { -- move to right
			size: Offset ← editI.Size[source];
			IF size=sourceEnd THEN RETURN; -- cannot move to right
			destLoc ← randLI.Choose[sourceEnd,size] }
		ELSE { -- move to left
			IF sourceStart=0 THEN RETURN; -- cannot move to left
			destLoc ← randLI.Choose[0,sourceStart] };
		oldSourceSize ← oldDestSize ← editI.Size[dest];
		oldSourceRope ← oldDestRope ← editI.GetRope[dest];
		oldSourceRuns ← oldDestRuns ← editI.GetRuns[dest] }
	ELSE {
		destLoc ← PickOne[dest];
		oldSourceSize ← editI.Size[source];
		oldDestSize ← editI.Size[dest];
		oldDestRope ← editI.GetRope[dest];
		oldSourceRope ← editI.GetRope[source];
		oldDestRuns ← editI.GetRuns[dest];
		oldSourceRuns ← editI.GetRuns[source] };
	editI.MoveText[dest,destLoc,source,sourceStart,sourceLen←sourceEnd-sourceStart];
	IF source=dest THEN {
		newSourceRope ← editI.GetRope[source];
		newSourceRuns ← editI.GetRuns[source];
		CheckSize[source,oldSourceSize];
		IF moveToRight THEN { -- switch so like moving to left
			temp: Offset ← destLoc;
			destLoc ← sourceStart;
			sourceStart ← sourceEnd;
			sourceEnd ← temp;
			sourceLen ← sourceEnd-sourceStart };
		CheckRopes[newSourceRope,0,oldSourceRope,0,destLoc];
		CheckRopes[newSourceRope,destLoc,oldSourceRope,sourceStart,sourceLen];
		CheckRopes[newSourceRope,destLoc+sourceLen,oldSourceRope,destLoc,
						sourceStart-destLoc];
		CheckRopes[newSourceRope,sourceEnd,oldSourceRope,sourceEnd,
						oldSourceSize-sourceEnd];
		CheckRuns[newSourceRuns,0,oldSourceRuns,0,destLoc];
		CheckRuns[newSourceRuns,destLoc,oldSourceRuns,sourceStart,sourceLen];
		CheckRuns[newSourceRuns,destLoc+sourceLen,oldSourceRuns,destLoc,
						sourceStart-destLoc];
		CheckRuns[newSourceRuns,sourceEnd,oldSourceRuns,sourceEnd,
						oldSourceSize-sourceEnd] }
	ELSE {
		newDestRope ← editI.GetRope[dest];
		newSourceRope ← editI.GetRope[source];
		newDestRuns ← editI.GetRuns[dest];
		newSourceRuns ← editI.GetRuns[source];
		CheckSize[source,oldSourceSize-sourceLen];
		CheckSize[dest,oldDestSize+sourceLen];
		CheckRopes[newDestRope,0,oldDestRope,0,destLoc];
		CheckRopes[newDestRope,destLoc,oldSourceRope,sourceStart,sourceLen];
		CheckRopes[newDestRope,destLoc+sourceLen,oldDestRope,destLoc,
						oldDestSize-destLoc];
		CheckRopes[newSourceRope,0,oldSourceRope,0,sourceStart];
		CheckRopes[newSourceRope,sourceStart,oldSourceRope,sourceEnd,
						oldSourceSize-sourceEnd];
		CheckRuns[newDestRuns,0,oldDestRuns,0,destLoc];
		CheckRuns[newDestRuns,destLoc,oldSourceRuns,sourceStart,sourceLen];
		CheckRuns[newDestRuns,destLoc+sourceLen,oldDestRuns,destLoc,
						oldDestSize-destLoc];
		CheckRuns[newSourceRuns,0,oldSourceRuns,0,sourceStart];
		CheckRuns[newSourceRuns,sourceStart,oldSourceRuns,sourceEnd,
						oldSourceSize-sourceEnd];
		AdjustLengths[] }};

MoveTextOnto: PUBLIC PROC = {
	source, dest: Node;
	destStart, destEnd, destLen, sourceStart, sourceEnd, sourceLen,
		oldSourceSize, oldDestSize: Offset;
	oldSourceRope, newSourceRope, oldDestRope, newDestRope: Rope;
	oldSourceRuns, newSourceRuns, oldDestRuns, newDestRuns: Runs;
	[source,dest] ← PickNodes[];
	[sourceStart,sourceEnd] ← PickTwo[source];
	[destStart,destEnd] ← PickTwo[dest];
	oldSourceSize ← editI.Size[source];
	oldDestSize ← editI.Size[dest];
	oldDestRope ← editI.GetRope[dest];
	oldSourceRope ← editI.GetRope[source];
	oldDestRuns ← editI.GetRuns[dest];
	oldSourceRuns ← editI.GetRuns[source];
	editI.MoveTextOnto[dest,destStart,destLen←destEnd-destStart,
				source,sourceStart,sourceLen←sourceEnd-sourceStart];
	IF source=dest THEN {
		newSourceRope ← editI.GetRope[source];
		newSourceRuns ← editI.GetRuns[source];
		IF sourceStart IN [destStart..destEnd) THEN {
			CheckRopes[newSourceRope,0,oldSourceRope,0,destStart];
			CheckRopes[newSourceRope,destStart,oldSourceRope,sourceStart,sourceLen];
			CheckRuns[newSourceRuns,0,oldSourceRuns,0,destStart];
			CheckRuns[newSourceRuns,destStart,oldSourceRuns,sourceStart,sourceLen];
			IF sourceEnd IN [destStart..destEnd] THEN {
				CheckSize[source,oldSourceSize+sourceLen-destLen];
				CheckRopes[newSourceRope,destStart+sourceLen,
						oldSourceRope,destEnd,oldSourceSize-destEnd];
				CheckRuns[newSourceRuns,destStart+sourceLen,
						oldSourceRuns,destEnd,oldSourceSize-destEnd] }
			ELSE {	CheckSize[source,oldSourceSize+destStart-sourceStart];
				CheckRopes[newSourceRope,destStart+sourceLen,
						oldSourceRope,sourceEnd,oldSourceSize-sourceEnd];
				CheckRuns[newSourceRuns,destStart+sourceLen,
						oldSourceRuns,sourceEnd,oldSourceSize-sourceEnd] }}
		ELSE IF sourceEnd IN [destStart..destEnd) THEN {
			CheckSize[source,oldSourceSize+sourceEnd-destEnd];
			CheckRopes[newSourceRope,0,oldSourceRope,0,sourceEnd];
			CheckRopes[newSourceRope,sourceEnd,
					oldSourceRope,destEnd,oldSourceSize-destEnd];
			CheckRuns[newSourceRuns,0,oldSourceRuns,0,sourceEnd];
			CheckRuns[newSourceRuns,sourceEnd,
					oldSourceRuns,destEnd,oldSourceSize-destEnd] }
		ELSE IF sourceStart < destStart THEN {
			IF sourceEnd >= destEnd THEN {
				CheckSize[source,oldSourceSize];
				CheckRopes[newSourceRope,0,oldSourceRope,0,oldSourceSize];
				CheckRuns[newSourceRuns,0,oldSourceRuns,0,oldSourceSize] }
			ELSE {	CheckSize[source,oldSourceSize-destLen];
				CheckRopes[newSourceRope,0,oldSourceRope,0,sourceStart];
				CheckRopes[newSourceRope,sourceStart,
						oldSourceRope,sourceEnd,destStart-sourceEnd];
				CheckRopes[newSourceRope,sourceStart+destStart-sourceEnd,
						oldSourceRope,sourceStart,sourceLen];
				CheckRopes[newSourceRope,destStart,
						oldSourceRope,destEnd,oldSourceSize-destEnd];
				CheckRuns[newSourceRuns,0,oldSourceRuns,0,sourceStart];
				CheckRuns[newSourceRuns,sourceStart,
						oldSourceRuns,sourceEnd,destStart-sourceEnd];
				CheckRuns[newSourceRuns,sourceStart+destStart-sourceEnd,
						oldSourceRuns,sourceStart,sourceLen];
				CheckRuns[newSourceRuns,destStart,
						oldSourceRuns,destEnd,oldSourceSize-destEnd] }}
		ELSE {	CheckSize[source,oldSourceSize-destLen];
			CheckRopes[newSourceRope,0,oldSourceRope,0,destStart];
			CheckRopes[newSourceRope,destStart,
					oldSourceRope,sourceStart,sourceLen];
			CheckRopes[newSourceRope,destStart+sourceLen,
					oldSourceRope,destEnd,sourceStart-destEnd];
			CheckRopes[newSourceRope,sourceEnd-destLen,
					oldSourceRope,sourceEnd,oldSourceSize-sourceEnd];
			CheckRuns[newSourceRuns,0,oldSourceRuns,0,destStart];
			CheckRuns[newSourceRuns,destStart,
					oldSourceRuns,sourceStart,sourceLen];
			CheckRuns[newSourceRuns,destStart+sourceLen,
					oldSourceRuns,destEnd,sourceStart-destEnd];
			CheckRuns[newSourceRuns,sourceEnd-destLen,
					oldSourceRuns,sourceEnd,oldSourceSize-sourceEnd] }}
	ELSE {	newDestRope ← editI.GetRope[dest];
		newSourceRope ← editI.GetRope[source];
		newDestRuns ← editI.GetRuns[dest];
		newSourceRuns ← editI.GetRuns[source];
		CheckSize[source,oldSourceSize-sourceLen];
		CheckSize[dest,oldDestSize+sourceLen-destLen];
		CheckRopes[newDestRope,0,oldDestRope,0,destStart];
		CheckRopes[newDestRope,destStart,oldSourceRope,sourceStart,sourceLen];
		CheckRopes[newDestRope,destStart+sourceLen,oldDestRope,destEnd,
						oldDestSize-destEnd];
		CheckRopes[newSourceRope,0,oldSourceRope,0,sourceStart];
		CheckRopes[newSourceRope,sourceStart,oldSourceRope,sourceEnd,
						oldSourceSize-sourceEnd];
		CheckRuns[newDestRuns,0,oldDestRuns,0,destStart];
		CheckRuns[newDestRuns,destStart,oldSourceRuns,sourceStart,sourceLen];
		CheckRuns[newDestRuns,destStart+sourceLen,oldDestRuns,destEnd,
						oldDestSize-destEnd];
		CheckRuns[newSourceRuns,0,oldSourceRuns,0,sourceStart];
		CheckRuns[newSourceRuns,sourceStart,oldSourceRuns,sourceEnd,
						oldSourceSize-sourceEnd];
		AdjustLengths[] }};

TransposeText: PUBLIC PROC = {
	alpha, beta: Node;
	alphaStart, alphaEnd, betaStart, betaEnd, oldAlphaSize, oldBetaSize,
		alphaLen, betaLen: Offset;
	oldAlphaRope, oldBetaRope, newAlphaRope, newBetaRope: Rope;
	oldAlphaRuns, oldBetaRuns, newAlphaRuns, newBetaRuns: Runs;
	betaOnRight: BOOLEAN;
	[alpha,beta] ← PickNodes[];
	[alphaStart,alphaEnd] ← PickTwo[alpha];
	IF alpha=beta THEN { 
		oldAlphaSize ← oldBetaSize ← editI.Size[alpha];
		oldAlphaRope ← oldBetaRope ← editI.GetRope[alpha];
		oldAlphaRuns ← oldBetaRuns ← editI.GetRuns[alpha];
		IF betaOnRight←RandomBoolean[] THEN {
			-- pick beta section to right of alpha
			IF alphaEnd=oldAlphaSize THEN RETURN; -- cannot
			[betaStart,betaEnd] ← ChooseTwo[alphaEnd,oldAlphaSize] }
		ELSE { -- pick beta section to left of alpha
			IF alphaStart=0 THEN RETURN; -- cannot
			[betaStart,betaEnd] ← ChooseTwo[0,alphaStart] }}
	ELSE {
		oldAlphaSize ← editI.Size[alpha];
		oldBetaSize ← editI.Size[beta];
		oldAlphaRope ← editI.GetRope[alpha];
		oldBetaRope ← editI.GetRope[beta];
		oldAlphaRuns ← editI.GetRuns[alpha];
		oldBetaRuns ← editI.GetRuns[beta];
		[betaStart,betaEnd] ← PickTwo[beta] };
	editI.TransposeText[
		alpha,alphaStart,alphaEnd-alphaStart,
		beta,betaStart,betaEnd-betaStart];
	IF alpha = beta THEN {
		newAlphaRope ← editI.GetRope[alpha];
		newAlphaRuns ← editI.GetRuns[alpha];
		CheckSize[alpha,oldAlphaSize];
		IF ~betaOnRight THEN { -- switch so like beta section on right
			temp: Offset ← alphaStart;
			alphaStart ← betaStart;
			betaStart ← temp;
			temp ← alphaEnd;
			alphaEnd ← betaEnd;
			betaEnd ← temp };
		alphaLen ← alphaEnd-alphaStart;
		betaLen ← betaEnd-betaStart;
		CheckRopes[newAlphaRope,0,oldAlphaRope,0,alphaStart];
		CheckRopes[newAlphaRope,alphaStart,oldAlphaRope,betaStart,betaLen];
		CheckRopes[newAlphaRope,alphaStart+betaLen,oldAlphaRope,
						alphaEnd,betaStart-alphaEnd];
		CheckRopes[newAlphaRope,alphaStart+betaEnd-alphaEnd,
						oldAlphaRope,alphaStart,alphaLen];
		CheckRopes[newAlphaRope,betaEnd,oldAlphaRope,betaEnd,
						oldAlphaSize-betaEnd];
		CheckRuns[newAlphaRuns,0,oldAlphaRuns,0,alphaStart];
		CheckRuns[newAlphaRuns,alphaStart,oldAlphaRuns,betaStart,betaLen];
		CheckRuns[newAlphaRuns,alphaStart+betaLen,oldAlphaRuns,
						alphaEnd,betaStart-alphaEnd];
		CheckRuns[newAlphaRuns,alphaStart+betaEnd-alphaEnd,
						oldAlphaRuns,alphaStart,alphaLen];
		CheckRuns[newAlphaRuns,betaEnd,oldAlphaRuns,betaEnd,
						oldAlphaSize-betaEnd] }
	ELSE {
		alphaLen ← alphaEnd-alphaStart;
		betaLen ← betaEnd-betaStart;
		newBetaRope ← editI.GetRope[beta];
		newAlphaRope ← editI.GetRope[alpha];
		newBetaRuns ← editI.GetRuns[beta];
		newAlphaRuns ← editI.GetRuns[alpha];
		CheckSize[alpha,oldAlphaSize-alphaLen+betaLen];
		CheckSize[beta,oldBetaSize-betaLen+alphaLen];
		CheckRopes[newAlphaRope,0,oldAlphaRope,0,alphaStart];
		CheckRopes[newAlphaRope,alphaStart,oldBetaRope,betaStart,betaLen];
		CheckRopes[newAlphaRope,alphaStart+betaLen,oldAlphaRope,
						alphaEnd,oldAlphaSize-alphaEnd];
		CheckRopes[newBetaRope,0,oldBetaRope,0,betaStart];
		CheckRopes[newBetaRope,betaStart,oldAlphaRope,alphaStart,alphaLen];
		CheckRopes[newBetaRope,betaStart+alphaLen,oldBetaRope,
						betaEnd,oldBetaSize-betaEnd];
		CheckRuns[newAlphaRuns,0,oldAlphaRuns,0,alphaStart];
		CheckRuns[newAlphaRuns,alphaStart,oldBetaRuns,betaStart,betaLen];
		CheckRuns[newAlphaRuns,alphaStart+betaLen,oldAlphaRuns,
						alphaEnd,oldAlphaSize-alphaEnd];
		CheckRuns[newBetaRuns,0,oldBetaRuns,0,betaStart];
		CheckRuns[newBetaRuns,betaStart,oldAlphaRuns,alphaStart,alphaLen];
		CheckRuns[newBetaRuns,betaStart+alphaLen,oldBetaRuns,
						betaEnd,oldBetaSize-betaEnd];
		AdjustLengths[] }};

ReplaceByChar: PUBLIC PROC = {
	dest: Node ← PickNode[];
	destStart, destEnd, destLen, oldSize, oldPos, newPos: Offset;
	newRope, oldRope: Rope;
	newRuns, oldRuns: Runs;
	inherit: BOOLEAN ← RandomBoolean[];
	looks: Looks;
	IF ~inherit THEN looks ← PickLooks[];
	[destStart,destEnd] ← PickTwo[dest];
	oldSize ← editI.Size[dest];
	oldRope ← editI.GetRope[dest];
	oldRuns ← editI.GetRuns[dest];
	editI.ReplaceByChar[dest,'#,destStart,destLen←destEnd-destStart,inherit,looks];
	newRope ← editI.GetRope[dest];
	newRuns ← editI.GetRuns[dest];
	CheckSize[dest,oldSize+1-destLen];
	CheckRopes[newRope,0,oldRope,0,destStart];
	IF editI.FetchChar[dest,destStart] # '# THEN ERROR;
	oldPos ← destStart+destLen; newPos ← destStart+1;
	CheckRopes[newRope,newPos,oldRope,oldPos,oldSize-oldPos];
	CheckRuns[newRuns,0,oldRuns,0,destStart];
	IF ~inherit AND editI.FetchLooks[dest,destStart] # looks THEN ERROR;
	CheckRuns[newRuns,newPos,oldRuns,oldPos,oldSize-oldPos];
	AdjustLength[dest] };

InsertChar: PUBLIC PROC = {
	dest: Node ← PickNode[];
	destLoc: Offset ← PickOne[dest];
	oldSize, loc, oldPos, newPos: Offset;
	newRope, oldRope: Rope;
	newRuns, oldRuns: Runs;
	inherit: BOOLEAN ← RandomBoolean[];
	looks: Looks;
	num: NAT ← ChooseNAT[1,20]; -- number of chars to insert
	IF ~inherit THEN looks ← PickLooks[];
	oldSize ← editI.Size[dest];
	oldRope ← editI.GetRope[dest];
	oldRuns ← editI.GetRuns[dest];
	loc ← destLoc;
	FOR i:NAT IN [0..num) DO
		editI.InsertChar[dest,'z-i,loc,inherit,looks];
		loc ← loc+1;
		ENDLOOP;
	newRope ← editI.GetRope[dest];
	newRuns ← editI.GetRuns[dest];
	CheckSize[dest,oldSize+num];
	CheckRopes[newRope,0,oldRope,0,destLoc];
	loc ← destLoc;
	FOR i:NAT IN [0..num) DO
		IF editI.FetchChar[dest,loc] # 'z-i THEN ERROR;
		IF ~inherit AND editI.FetchLooks[dest,loc] # looks THEN ERROR;
		loc ← loc+1;
		ENDLOOP;
	oldPos ← destLoc; newPos ← destLoc+num;
	CheckRopes[newRope,newPos,oldRope,oldPos,oldSize-oldPos];
	CheckRuns[newRuns,0,oldRuns,0,destLoc];
	CheckRuns[newRuns,newPos,oldRuns,oldPos,oldSize-oldPos];
	AdjustLength[dest] };

AppendChar: PUBLIC PROC = {
	dest: Node ← PickNode[];
	inherit: BOOLEAN ← RandomBoolean[];
	looks: Looks;
	loc, oldSize: Offset;
	newRope, oldRope: Rope;
	newRuns, oldRuns: Runs;
	num: NAT ← ChooseNAT[1,20]; -- number of chars to append
	IF ~inherit THEN looks ← PickLooks[];
	oldSize ← editI.Size[dest];
	oldRope ← editI.GetRope[dest];
	oldRuns ← editI.GetRuns[dest];
	FOR i:NAT IN [0..num) DO
		editI.AppendChar[dest,'Z-i,inherit,looks];
		ENDLOOP;
	newRope ← editI.GetRope[dest];
	newRuns ← editI.GetRuns[dest];
	CheckSize[dest,oldSize+num];
	CheckRopes[newRope,0,oldRope,0,oldSize];
	loc ← oldSize;
	FOR i:NAT IN [0..num) DO
		IF editI.FetchChar[dest,loc] # 'Z-i THEN ERROR;
		IF ~inherit AND editI.FetchLooks[dest,loc] # looks THEN ERROR;
		loc ← loc+1;
		ENDLOOP;
	CheckRuns[newRuns,0,oldRuns,0,oldSize];
	AdjustLength[dest] };

string: REF READONLY TEXT ← "1234567890123456789012345678901234567890";
stringRope: Rope ← rI.FromString[string];

ReplaceByString: PUBLIC PROC = {
	dest: Node ← PickNode[];
	destStart, destEnd, destLen, oldSize, oldPos, newPos: Offset;
	newRope, oldRope: Rope;
	newRuns, oldRuns: Runs;
	inherit: BOOLEAN ← RandomBoolean[];
	looks: Looks;
	strStart, strEnd, strLen: NAT;
	oldSize ← editI.Size[dest];
	oldRope ← editI.GetRope[dest];
	oldRuns ← editI.GetRuns[dest];
	IF ~inherit THEN looks ← PickLooks[];
	[destStart,destEnd] ← PickTwo[dest];
	[strStart,strEnd] ← ChooseTwoNATs[0,string.length];
	editI.ReplaceByString[dest,string,strStart,strLen←strEnd-strStart,
		destStart,destLen←destEnd-destStart,inherit,looks];
	newRope ← editI.GetRope[dest];
	newRuns ← editI.GetRuns[dest];
	CheckSize[dest,oldSize+strLen-destLen];
	CheckRopes[newRope,0,oldRope,0,destStart];
	CheckRopes[newRope,destStart,stringRope,strStart,strLen];
	oldPos ← destStart+destLen; newPos ← destStart+strLen;
	CheckRopes[newRope,newPos,oldRope,oldPos,oldSize-oldPos];
	CheckRuns[newRuns,0,oldRuns,0,destStart];
	IF ~inherit AND strLen>0 AND
		editI.FetchLooks[dest,destStart] # looks THEN ERROR;
	CheckRuns[newRuns,newPos,oldRuns,oldPos,oldSize-oldPos];
	AdjustLength[dest] };

InsertString: PUBLIC PROC = {
	dest: Node ← PickNode[];
	InsertStringInNode[dest];
	AdjustLength[dest] };

InsertStringInNode: PUBLIC PROC [dest: Node] = {
	oldSize: Offset;
	destLoc: Offset ← PickOne[dest];
	inherit: BOOLEAN ← RandomBoolean[];
	newRope, oldRope: Rope;
	newRuns, oldRuns: Runs;
	looks: Looks;
	strStart, strEnd, strLen: NAT;
	IF ~inherit THEN looks ← PickLooks[];
	[strStart,strEnd] ← ChooseTwoNATs[0,string.length];
	oldSize ← editI.Size[dest];
	oldRope ← editI.GetRope[dest];
	oldRuns ← editI.GetRuns[dest];
	editI.InsertString[dest,string,strStart,strLen←strEnd-strStart,
				destLoc,inherit,looks];
	newRope ← editI.GetRope[dest];
	newRuns ← editI.GetRuns[dest];
	CheckSize[dest,oldSize+strLen];
	CheckRopes[newRope,0,oldRope,0,destLoc];
	CheckRopes[newRope,destLoc,stringRope,strStart,strLen];
	CheckRopes[newRope,destLoc+strLen,oldRope,destLoc,oldSize-destLoc];
	CheckRuns[newRuns,0,oldRuns,0,destLoc];
	IF ~inherit AND strLen>0 AND
		editI.FetchLooks[dest,destLoc] # looks THEN ERROR;
	CheckRuns[newRuns,destLoc+strLen,oldRuns,destLoc,oldSize-destLoc] };

AppendString: PUBLIC PROC = {
	dest: Node ← PickNode[];
	inherit: BOOLEAN ← RandomBoolean[];
	looks: Looks;
	oldSize: Offset;
	newRope, oldRope: Rope;
	newRuns, oldRuns: Runs;
	strStart, strEnd, strLen: NAT;
	IF ~inherit THEN looks ← PickLooks[];
	[strStart,strEnd] ← ChooseTwoNATs[0,string.length];
	oldSize ← editI.Size[dest];
	oldRope ← editI.GetRope[dest];
	oldRuns ← editI.GetRuns[dest];
	editI.AppendString[dest,string,strStart,strLen←strEnd-strStart,
								inherit,looks];
	newRope ← editI.GetRope[dest];
	newRuns ← editI.GetRuns[dest];
	CheckSize[dest,oldSize+strLen];
	CheckRopes[newRope,0,oldRope,0,oldSize];
	CheckRopes[newRope,oldSize,stringRope,strStart,strLen];
	CheckRuns[newRuns,0,oldRuns,0,oldSize];
	IF ~inherit AND strLen>0 AND
		editI.FetchLooks[dest,oldSize] # looks THEN ERROR;
	AdjustLength[dest] };

ReplaceByRope: PUBLIC PROC = {
	dest: Node ← PickNode[];
	rope: Rope ← PickRope[];
	newRope, oldRope: Rope;
	newRuns, oldRuns: Runs;
	destStart, destEnd, destLen, oldSize, oldPos, newPos, ropeLen: Offset;
	inherit: BOOLEAN ← RandomBoolean[];
	looks: Looks;
	[destStart,destEnd] ← PickTwo[dest];
	IF ~inherit THEN looks ← PickLooks[];
	oldSize ← editI.Size[dest];
	oldRope ← editI.GetRope[dest];
	oldRuns ← editI.GetRuns[dest];
	ropeLen ← ropeI.Size[rope];
	editI.ReplaceByRope[dest,rope,destStart,destLen←destEnd-destStart,inherit,looks];
	newRope ← editI.GetRope[dest];
	newRuns ← editI.GetRuns[dest];
	CheckSize[dest,oldSize+ropeLen-destLen];
	CheckRopes[newRope,0,oldRope,0,destStart];
	CheckRopes[newRope,destStart,rope,0,ropeLen];
	oldPos ← destStart+destLen; newPos ← destStart+ropeLen;
	CheckRopes[newRope,newPos,oldRope,oldPos,oldSize-oldPos];
	CheckRuns[newRuns,0,oldRuns,0,destStart];
	IF ~inherit AND ropeLen>0 AND
		editI.FetchLooks[dest,destStart] # looks THEN ERROR;
	CheckRuns[newRuns,newPos,oldRuns,oldPos,oldSize-oldPos];
	AdjustLength[dest] };

InsertRope: PUBLIC PROC = {
	dest: Node ← PickNode[];
	destLoc: Offset ← PickOne[dest];
	oldSize, ropeLen: Offset;
	rope: Rope ← PickRope[];
	newRope, oldRope: Rope;
	newRuns, oldRuns: Runs;
	inherit: BOOLEAN ← RandomBoolean[];
	looks: Looks;
	IF ~inherit THEN looks ← PickLooks[];
	oldSize ← editI.Size[dest];
	oldRope ← editI.GetRope[dest];
	oldRuns ← editI.GetRuns[dest];
	ropeLen ← ropeI.Size[rope];
	editI.InsertRope[dest,rope,destLoc,inherit,looks];
	newRope ← editI.GetRope[dest];
	newRuns ← editI.GetRuns[dest];
	CheckSize[dest,oldSize+ropeLen];
	CheckRopes[newRope,0,oldRope,0,destLoc];
	CheckRopes[newRope,destLoc,rope,0,ropeLen];
	CheckRopes[newRope,destLoc+ropeLen,oldRope,destLoc,oldSize-destLoc];
	CheckRuns[newRuns,0,oldRuns,0,destLoc];
	IF ~inherit AND ropeLen>0 AND
		editI.FetchLooks[dest,destLoc] # looks THEN ERROR;
	CheckRuns[newRuns,destLoc+ropeLen,oldRuns,destLoc,oldSize-destLoc];
	AdjustLength[dest] };

AppendRope: PUBLIC PROC = {
	dest: Node ← PickNode[];
	rope: Rope ← PickRope[];
	oldSize, ropeLen: Offset;
	newRope, oldRope: Rope;
	newRuns, oldRuns: Runs;
	inherit: BOOLEAN ← RandomBoolean[];
	looks: Looks;
	IF ~inherit THEN looks ← PickLooks[];
	oldSize ← editI.Size[dest];
	oldRope ← editI.GetRope[dest];
	oldRuns ← editI.GetRuns[dest];
	ropeLen ← ropeI.Size[rope];
	editI.AppendRope[dest,rope,inherit,looks];
	newRope ← editI.GetRope[dest];
	newRuns ← editI.GetRuns[dest];
	CheckSize[dest,oldSize+ropeLen];
	CheckRopes[newRope,0,oldRope,0,oldSize];
	CheckRopes[newRope,oldSize,rope,0,ropeLen];
	CheckRuns[newRuns,0,oldRuns,0,oldSize];
	IF ~inherit AND ropeLen>0 AND
		editI.FetchLooks[dest,oldSize] # looks THEN ERROR;
	AdjustLength[dest] };

END.