-- Test1Impl.mesa
-- written by Bill Paxton, April 1981
-- last edit by Bill Paxton, 28-Jul-81 11:12:03

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

DIRECTORY
EditTest,
EditSpan,
TextEdit,
TextNode,
RunReader,
RopeReader,
RopeInline,
RandomLongInt;

Test1Impl: PROGRAM
	IMPORTS EditTest, EditSpan, TextEdit, RunReader, RopeReader, RandomLongInt
	EXPORTS EditTest =
BEGIN
OPEN
	EditTest,
	spanI:EditSpan,
	editI:TextEdit,
	nodeI:TextNode,
	runrdrI:RunReader,
	roperdrI:RopeReader,
	randLI:RandomLongInt;

Span: TYPE = nodeI.Span;

-- ***** 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, resultStart, resultLen: Offset;
	sourceRope, newRope, oldRope: Rope;
	sourceRuns, newRuns, oldRuns: Runs;
	result: Span;
	sourceRope ← editI.GetRope[source];
	sourceRuns ← editI.GetRuns[source];
	oldSize ← editI.Size[dest];
	oldRope ← editI.GetRope[dest];
	oldRuns ← editI.GetRuns[dest];
	BeforeUndo[dest,source];
	IF destStart=destEnd AND sourceStart=sourceEnd THEN RETURN;
	IF sourceStart=sourceEnd THEN { -- doing a delete
		sourceLen ← resultLen ← 0; resultStart ← destStart;
		spanI.Delete[TextSpan[dest,destStart,destLen←destEnd-destStart],event] }
	ELSE {
		IF destStart=destEnd THEN { -- doing a copy
			destLen ← 0;
			result ← spanI.Copy[dest: TextLoc[dest,destStart],
				source: TextSpan[source,sourceStart,sourceLen←sourceEnd-sourceStart],
				event: event] }
		ELSE result ← spanI.Replace[TextSpan[dest,destStart,destLen←destEnd-destStart],
			TextSpan[source,sourceStart,sourceLen←sourceEnd-sourceStart],
			FALSE, event];
		resultStart ← result.start.where;
		resultLen ← result.end.where - resultStart + 1 };
	newRope ← editI.GetRope[dest];
	newRuns ← editI.GetRuns[dest];
	IF resultStart # destStart OR resultLen # sourceLen THEN ERROR;
	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];
	TestUndo[] };

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;
	resultStart, resultLen, destStart: Offset;
	oldSourceRope, newSourceRope, oldDestRope, newDestRope: Rope;
	oldSourceRuns, newSourceRuns, oldDestRuns, newDestRuns: Runs;
	moveToRight: BOOLEAN;
	result: Span;
	[source,dest] ← PickNodes[];
	[sourceStart,sourceEnd] ← PickTwo[source];
	IF sourceStart=sourceEnd THEN RETURN;
	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];
		BeforeUndo[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];
		BeforeUndo[source,dest] };
	result ← spanI.Move[TextLoc[dest,destLoc],
		TextSpan[source,sourceStart,sourceLen←sourceEnd-sourceStart],
		FALSE,after,0,event];
	resultStart ← result.start.where;
	resultLen ← result.end.where - resultStart + 1;
	IF resultLen # sourceLen THEN ERROR;
	destStart ← IF dest=source AND destLoc > sourceStart THEN destLoc-sourceLen ELSE destLoc;
	IF resultStart # destStart THEN ERROR;
	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];
		TestUndo[] }
	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];
		TestUndo[];
		AdjustLengths[] }};

MoveTextOnto: PUBLIC PROC = {
	source, dest: Node;
	destStart, destEnd, destLen, sourceStart, sourceEnd, sourceLen,
		oldSourceSize, oldDestSize, resultStart, resultLen: Offset;
	oldSourceRope, newSourceRope, oldDestRope, newDestRope: Rope;
	oldSourceRuns, newSourceRuns, oldDestRuns, newDestRuns: Runs;
	result: Span;
	[source,dest] ← PickNodes[];
	[sourceStart,sourceEnd] ← PickTwo[source];
	[destStart,destEnd] ← PickTwo[dest];
	IF sourceStart=sourceEnd OR destStart=destEnd THEN RETURN;
	oldSourceSize ← editI.Size[source];
	oldDestSize ← editI.Size[dest];
	oldDestRope ← editI.GetRope[dest];
	oldSourceRope ← editI.GetRope[source];
	oldDestRuns ← editI.GetRuns[dest];
	oldSourceRuns ← editI.GetRuns[source];
	BeforeUndo[source,dest];
	result ← spanI.MoveOnto[TextSpan[dest,destStart,destLen←destEnd-destStart],
		TextSpan[source,sourceStart,sourceLen←sourceEnd-sourceStart],
		FALSE, event];
	resultStart ← result.start.where;
	resultLen ← result.end.where - resultStart + 1;
	IF source=dest THEN {
		newSourceRope ← editI.GetRope[source];
		newSourceRuns ← editI.GetRuns[source];
		IF sourceStart IN [destStart..destEnd) THEN {
			IF resultStart # destStart OR resultLen # sourceLen THEN ERROR;
			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 {
			IF resultStart # sourceStart OR resultLen # sourceLen THEN ERROR;
			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];
				IF resultStart # sourceStart OR resultLen # sourceLen THEN ERROR;
				CheckRopes[newSourceRope,0,oldSourceRope,0,oldSourceSize];
				CheckRuns[newSourceRuns,0,oldSourceRuns,0,oldSourceSize] }
			ELSE {	IF resultStart # destStart-sourceLen OR resultLen # sourceLen THEN ERROR;
				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 {	IF resultStart # destStart OR resultLen # sourceLen THEN ERROR;
			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] };
		TestUndo[] }
	ELSE {	newDestRope ← editI.GetRope[dest];
		newSourceRope ← editI.GetRope[source];
		newDestRuns ← editI.GetRuns[dest];
		newSourceRuns ← editI.GetRuns[source];
		IF resultStart # destStart OR resultLen # sourceLen THEN ERROR;
		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];
		TestUndo[];
		AdjustLengths[] }};

TransposeText: PUBLIC PROC = {
	alpha, beta: Node;
	alphaStart, alphaEnd, betaStart, betaEnd, oldAlphaSize, oldBetaSize: Offset;
	alphaLen, betaLen, alphaResultStart, alphaResultLen, betaResultStart, betaResultLen: Offset;
	oldAlphaRope, oldBetaRope, newAlphaRope, newBetaRope: Rope;
	oldAlphaRuns, oldBetaRuns, newAlphaRuns, newBetaRuns: Runs;
	betaOnRight: BOOLEAN;
	alphaResult, betaResult: Span;
	[alpha,beta] ← PickNodes[];
	[alphaStart,alphaEnd] ← PickTwo[alpha];
	IF alphaStart=alphaEnd THEN RETURN;
	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] };
	IF betaStart=betaEnd THEN RETURN;
	BeforeUndo[alpha,beta];
	[alphaResult,betaResult] ← spanI.Transpose[
		TextSpan[alpha,alphaStart,alphaLen←alphaEnd-alphaStart],
		TextSpan[beta,betaStart,betaLen←betaEnd-betaStart],
		FALSE, event];
	alphaResultStart ← alphaResult.start.where;
	alphaResultLen ← alphaResult.end.where - alphaResultStart + 1;
	betaResultStart ← betaResult.start.where;
	betaResultLen ← betaResult.end.where - betaResultStart + 1;
	IF alphaResultLen # alphaLen OR betaResultLen # betaLen THEN ERROR;
	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;
			IF alphaResultStart # betaStart THEN ERROR;
			IF betaResultStart # alphaStart+alphaLen-betaLen THEN ERROR;
			alphaStart ← betaStart;
			betaStart ← temp;
			temp ← alphaEnd;
			alphaEnd ← betaEnd;
			betaEnd ← temp }
		ELSE {	IF betaResultStart # alphaStart THEN ERROR;
			IF alphaResultStart # betaStart-alphaLen+betaLen THEN ERROR };
		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];
		TestUndo[] }
	ELSE {	IF betaResultStart # alphaStart OR alphaResultStart # betaStart THEN ERROR;
		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];
		TestUndo[];
		AdjustLengths[] }};

END.