{Copyright © Chris Crawford 1990}
UNIT Globals;
INTERFACE
CONST
	headersize = 512; 				{header for MacPaint documents}
	MaxScreenSize = 21888;		{maximum RAM consumption of MacPaint document}
	MaxNoCards = 160;				{maximum number of cards}
	BaseX = 410;						{origin for graph display}
	BaseY = 310;						{origin for graph display}
	MaxTriangles = 32;				{maximum number of color triangles permitted}
	HandleSize = 8;					{size of tiny handle for grabbing a triangle -- editor only}
	HandleRad = HandleSize DIV 2;  {a variation on HandleSize -- editor only}
	BitMapWidth = 400;				{size of standard screen bitmap in Macintosh version}
	BitMapHeight = 322;				{size of standard screen bitmap in Macintosh version}
	MaxCauses = 8;					{maximum number of causes (and effects) permitted}
	MaxTextBoxes = 4;				{maximum number of textboxes permitted}
	MaxConstants = 4;				{maximum number of equation constants permitted}
	EarthProc = 1;						{code for "earth display" procedure}
	PointProc = 2;						{code for "points display" procedure}
	MoneyProc = 3;					{code for "money display" procedure}
	CardProc = 4;						{code for "card display" procedure}
	FormulaProc = 5;					{code for the formula display procedure}
{ECO 6: a new addition here}
	TreeProc = 6;						{code for the tree display procedure}
	DeathPtsCard = 1;					{code for a standard death points card}
	LifePtsCard = 2;					{code for a standard life points card}
	TaxCard = 3;						{code for a standard tax card}
	SubsidyCard = 4;					{code for a standard subsidy card}
	PriceCard = 5;						{code for a standard price card}
	TotalCard = 6;						{code for a standard total card}
{ECO 6: converted this constant into an arguement of NextTurn}
{YearsPerTurn = 5;}
				{number of years in one turn}
	NoTaxCards = 10;					{number of tax cards}
	NoSubsidyCards = 13;			{number of subsidy cards}

{these constants define all the cards used in the simulation.  Although they are a long drag, they are}
{necessary to prevent confusion in the RunSimulation module, which will, when done, boast several}
{hundred equations.  We don't want to try to do source code QA by constantly checking ID numbers.}
{There is a shorthand here: the characters '%S' indicate a dollar sign ($) because my compiler won't}
{accept the dollar sign as a valid character in this context.}

{ECO 4: I made a change in the sequence number of Dam%S}
	AcidRain = 1;
	AveEnergyPrice = AcidRain + 1;
	BasicResearch%S = AveEnergyPrice + 1;
	BeefProduction = BasicResearch%S + 1;
	BeefTax = BeefProduction + 1;
	BioResearch%S = BeefTax + 1;
	BiodiversityPoints = BioResearch%S + 1;
	Biotechnology = BiodiversityPoints + 1;
	BirthRate = Biotechnology + 1;
	CFCProduction = BirthRate + 1;
	CFCTax = CFCProduction + 1;
	CarbonDioxide = CFCTax + 1;
	CoalPrice = CarbonDioxide + 1;
	CoalResearch%S = CoalPrice + 1;
	CoalSupply = CoalResearch%S + 1;
	CoalTax = CoalSupply + 1;
	CoalTechnology = CoalTax + 1;
	CoalUse = CoalTechnology + 1;
	ComputerGamesPts = CoalUse + 1;
	ConsumerGoods = ComputerGamesPts + 1;
	CropStrains = ConsumerGoods + 1;
	CropTechnology = CropStrains + 1;
	CropYields = CropTechnology + 1;
	Crops = CropYields + 1;
	DamPrice = Crops + 1;
	DamUse = DamPrice + 1;
	Dam%S = DamUse + 1;
	DeathPoints = Dam%S + 1;
	DebtForNature%S = DeathPoints + 1;
	Desertification = DebtForNature%S + 1;
	DrinkingWater = Desertification + 1;
	EnergyConservation = DrinkingWater + 1;
	EnergyDemand = EnergyConservation + 1;
	FallPoints = EnergyDemand + 1;
	FallsFromRoofs = FallPoints + 1;
	FamilyPlanning%S = FallsFromRoofs + 1;
	FarmLand = FamilyPlanning%S + 1;
	FertilizerTax = FarmLand + 1;
	FertilizerUse = FertilizerTax + 1;
	FloodDeathPoints = FertilizerUse + 1;
	FloodDeaths = FloodDeathPoints + 1;
	FoodSupply = FloodDeaths + 1;
	ForestClearing = FoodSupply + 1;
	ForestHabitats = ForestClearing + 1;
	ForestLand = ForestHabitats + 1;
	ForestLifePoints = ForestLand + 1;
	FuelwoodUse = ForestLifePoints + 1;
	Garbage = FuelwoodUse + 1;
	GlobalGenePool = Garbage + 1;
	GlobalTemperature = GlobalGenePool + 1;
	Grasslands = GlobalTemperature + 1;
	GrossGlobalProduct = Grasslands + 1;
	GroundwaterSupply = GrossGlobalProduct + 1;
	GroundwaterUse = GroundwaterSupply + 1;
	HeavyMetalDeaths = GroundwaterUse + 1;
	HeavyMetalPoints = HeavyMetalDeaths + 1;
	HeavyMetalPrice = HeavyMetalPoints + 1;
	HeavyMetalSupply = HeavyMetalPrice + 1;
	HeavyMetalTax = HeavyMetalSupply + 1;
	HeavyMetalUse = HeavyMetalTax + 1;
	Housing = HeavyMetalUse + 1;
	IndustrialInput = Housing + 1;
	IndustrialOutput = IndustrialInput + 1;
	InundationPoints = IndustrialOutput + 1;
	LakeAcidity = InundationPoints + 1;
	LakeHabitats = LakeAcidity + 1;
	LakeLifePoints = LakeHabitats + 1;
	LandAbuse = LakeLifePoints + 1;
	LandAbusePoints = LandAbuse + 1;
	LifePoints = LandAbusePoints + 1;
	Logging = LifePoints + 1;
	LoggingTax = Logging + 1;
	LungDiseaseDeaths = LoggingTax + 1;
	LungDiseasePts = LungDiseaseDeaths + 1;
	MarineLife = LungDiseasePts + 1;
	MarineLifePoints = MarineLife + 1;
	MaterialsDemand = MarineLifePoints + 1;
	Medicines = MaterialsDemand + 1;
	Methane = Medicines + 1;
	NaturalGasPrice = Methane + 1;
	NaturalGasSupply = NaturalGasPrice + 1;
	NaturalGasTax = NaturalGasSupply + 1;
	NaturalGasUse = NaturalGasTax + 1;
	NetEnergy = NaturalGasUse + 1;
	NitrousDioxide = NetEnergy + 1;
	NonrenewEnergy = NitrousDioxide + 1;
	NorthernLifestyle = NonrenewEnergy + 1;
	NuclearAccidents = NorthernLifestyle + 1;
	NuclearPrice = NuclearAccidents + 1;
	NuclearResearch%S = NuclearPrice + 1;
	NuclearSupply = NuclearResearch%S + 1;
	NuclearTax = NuclearSupply + 1;
	NuclearTechnology = NuclearTax + 1;
	NuclearUse = NuclearTechnology + 1;
	OilPrice = NuclearUse + 1;
	OilResearch%S = OilPrice + 1;
	OilSpills = OilResearch%S + 1;
	OilSupply = OilSpills + 1;
	OilTax = OilSupply + 1;
	OilTechnology = OilTax + 1;
	OilUse = OilTechnology + 1;
	Overgrazing = OilUse + 1;
	Ozone = Overgrazing + 1;
	PesticideDeathPts = Ozone + 1;
	PesticideDeaths = PesticideDeathPts + 1;
	PesticideTax = PesticideDeaths + 1;
	PesticideUse = PesticideTax + 1;
	Phytoplankton = PesticideUse + 1;
	Population = Phytoplankton + 1;
	Price = Population + 1;
	PropertyDamage%S = Price + 1;
	QualityOfLife = PropertyDamage%S + 1;
	QualityPoints = QualityOfLife + 1;
	RadWastePoints = QualityPoints + 1;
	Radiation = RadWastePoints + 1;
	RadiationCancer = Radiation + 1;
	RadiationPoints = RadiationCancer + 1;
	RadWaste = RadiationPoints + 1;
	RecycledAluminum = RadWaste + 1;
	RecycledPaper = RecycledAluminum + 1;
	RecyclingCenter%S = RecycledPaper + 1;
	RenewableEnergy = RecyclingCenter%S + 1;
	ReservoirCapacity = RenewableEnergy + 1;
	RiparianHabitats = ReservoirCapacity + 1;
	SeaLevel = RiparianHabitats + 1;
	SeaFood = SeaLevel + 1;
	SkinCancerDeaths = SeaFood + 1;
	SkinCancerPoints = SkinCancerDeaths + 1;
	SoilErosion = SkinCancerPoints + 1;
	SolarEnergy%S = SoilErosion + 1;
	SolarEnergyPrice = SolarEnergy%S + 1;
	SolarEnergyUse = SolarEnergyPrice + 1;
	SolarResearch%S = SolarEnergyUse + 1;
	SolarTechnology = SolarResearch%S + 1;
	SouthernLifestyle = SolarTechnology + 1;
	StarvationPoints = SouthernLifestyle + 1;
	Starvation = StarvationPoints + 1;
	StratosphericCFC = Starvation + 1;
	StripMining = StratosphericCFC + 1;
	Subsidy = StripMining + 1;
	SulfurDioxide = Subsidy + 1;
	Sustainability = SulfurDioxide + 1;
	SustainabilityPts = Sustainability + 1;
	Tax = SustainabilityPts + 1;
	TotHeavyMetUse = Tax + 1;
	Total = TotHeavyMetUse + 1;
	TotalCoalUse = Total + 1;
	TotalNatGasUse = TotalCoalUse + 1;
	TotalNuclearUse = TotalNatGasUse + 1;
	TotalOilUse = TotalNuclearUse + 1;
	TroposphericCFCs = TotalOilUse + 1;
	UV = TroposphericCFCs + 1;
	WaterPollution = UV + 1;
	WaterSupply = WaterPollution + 1;
	WoodStove%S = WaterSupply + 1;

	BeefTaxIndex = 1;
	CFCTaxIndex = 2;
	CoalTaxIndex = 3;
	FertilizerTaxIndex = 4;
	HeavyMetalTaxIndex = 5;
	LoggingTaxIndex = 6;
	NaturalGasTaxIndex = 7;
	NuclearTaxIndex = 8;
	OilTaxIndex = 9;
	PesticideTaxIndex = 10;

	PropertyDamage%SIndex = 1;
	BasicResearch%SIndex = 2;
	BioResearch%SIndex = 3;
	CoalResearch%SIndex = 4;
	Dam%SIndex = 5;
	DebtForNature%SIndex = 6;
	FamilyPlanning%SIndex = 7;
	NuclearResearch%SIndex = 8;
	OilResearch%SIndex = 9;
	RecyclingCenter%SIndex = 10;
	SolarEnergy%SIndex = 11;
	SolarResearch%SIndex = 12;
	WoodStove%SIndex = 13;

TYPE
	Str16 = STRING[16];
	Str20 = STRING[20];
	Str32 = STRING[32];

{This is the record for a text box}
	TBox = RECORD
			TRect: Rect;
			TFont: Integer;
			TSize: Integer;
			TFace: Integer;
		END;

{This is the record for a color triangle.}
	CTriRec = RECORD
			Vertices: ARRAY[1..3] OF Point;
			color: RGBColor;
		END;

VAR
{first come some standard Macintosh housekeeping items}
	MenuArr: ARRAY[1..4] OF MenuHandle;
	SwapPort: GrafPort;
	whichWindow: WindowPtr;
	MainWind: WindowPtr;
	myEvent: EventRecord;
	WatchCursr: CursHandle;
	MaltaKreuz: CursHandle;
	ScrollConstant: ARRAY[1..MaxConstants] OF ControlHandle;
	PolicyScroll: ControlHandle;

	ExitFlag: Boolean;							{exit this display or mode}
	QuitGame: Boolean;						{quit the game}
	PScrollFlag: Boolean;						{remembers whether the Policy scroll bar is alive}
{ECO 6: These next two flags are new}
	WarningFlag: Boolean;
	NoWarningYet: Boolean;
	LoadGameFlag: Boolean;
	JumpLocation: Integer;					{code for procedure to which to jump}
	TextBox: ARRAY[1..MaxTextBoxes] OF TBox;				{the set of textboxes for ThisCard}
	TextBoxText: ARRAY[1..MaxTextBoxes] OF Str255;		{content of said textboxes}

{warning: my file reading code assumes that these next four variables -- ten bytes -- }
{are preserved in EXACTLY this order!}
	PackedSize: LongInt;						{the number of valid bytes in block pointed to by PackedMapPtr}
	NumTriangles: Integer;					{the number of color triangles in ThisCard}
	NumTextBoxes: Integer;					{the number of textboxes in ThisCard}
	EquationLength: Integer;					{the number of valid characters in EquationText}

	PackedMapPtr: Ptr;						{pointer to the packed bitmap for ThisCard}
	StdRect: Rect;								{a standardized rectangle equal to the small Mac screen minus menu}
	BitMapRect: Rect;							{another standardized rectangle equal to the card bitmap: 400h by 320v}
	CauseCount: ARRAY[1..MaxNoCards] OF Integer;		{the number of cause-links for each card}
	EffectCount: ARRAY[1..MaxNoCards] OF Integer;		{the number of effect-links for each card}

{these next two arrays store the cause and effect links for all the cards}
	Cause: ARRAY[1..MaxNoCards] OF ARRAY[1..MaxCauses] OF Integer;
	Effect: ARRAY[1..MaxNoCards] OF ARRAY[1..MaxCauses] OF Integer;

	CardName: ARRAY[1..MaxNoCards] OF Str20;		{the name of each card -- used for a variety of things}
	CardUnit: ARRAY[1..MaxNoCards] OF Str16;		{the units for each card's associated variable}
	SavePort: GrafPtr;
	Error: OSErr;								{a global OS function result variable}
	Count: LongInt;								{used in file reads and writes}
	RefNum: Integer;							{used in file IO calls}
	thePart: Integer;							{the part in the control that was clicked on}
	ThisCard: Integer;							{VERY IMPORTANT variable -- the card currently displayed}
	PEquationStr: Str255;					{the equation that is displayed}
	ExplanationMark: Integer;				{marks the place in EquationText where the explanation begins}

{This next array is messy. It is the contents of the equation file, but it must be parsed by}
{ProcessEqnText into the component pieces. Unfortunately, a bad EquationText file will cause}
{ProcessEqnText to crash; not very good design, is it?}
	EquationText: PACKED ARRAY[1..2048] OF Char;

{Along with PEquationStr, these six variables are the component pieces that are parsed}
{out of EquationText by ProcessEqnText.}
	CConstantText: ARRAY[1..MaxConstants] OF Str32;
	CConstantUnits: ARRAY[1..MaxConstants] OF Str32;
	CardCULimit: ARRAY[1..MaxConstants] OF Real;		{upper limits on values of constants}
	CardCLLimit: ARRAY[1..MaxConstants] OF Real;		{lower limits on values of constants}
	CVariable: ARRAY[1..5] OF Integer;
	WhichConstant: Integer;

	CTriangle: ARRAY[1..MaxTriangles] OF CTriRec;		{the color triangles; file data loaded into this array}
	YesColor: Boolean;											{Is color available?}

{these are used to save the bitmaps and triangles for the standard cards}
	SavedMapPtr: ARRAY[1..6] OF Ptr;
	SavedNumTriangles: ARRAY[1..6] OF Integer;
	SavedCTriangle: ARRAY[1..6] OF ARRAY[1..MaxTriangles] OF CTriRec;

{ECO 4: This bundle of variables [BarRect...Turn] is now part of a saved game; preserve this order!}
	BarRect: ARRAY[1..10] OF Rect;		{ECO 2: changed range to 10}
	ValueRect: Rect;
	BiggestValue: Real;						{ECO 0: globalized from DrawCard!}
	TotalSubsidy: Real;
	Treasury: Real;
	Points: Real;								{ECO 2: Globalized from RunPoints}
{ECO 6: Big change here: new variable added. This affects SaveGame and LoadGame.}
	LastPoints: Real;
{ECO 4: note that I moved Year to this position from another place. This is important!}
	Year: Integer;								{the current year}
	Turn: Integer;								{ECO 2: this is a new variable}

{ECO 0: I changed the order in which these arrays are saved to facilitate LoadGame}
	CardValue: ARRAY[1..MaxNoCards] OF Real;
	CardConstant: ARRAY[1..MaxNoCards] OF ARRAY[1..MaxConstants] OF Real;
	CardHistory: ARRAY[1..MaxNoCards] OF ARRAY[1..10] OF Real;  {ECO 2: second range now 10}
	SubsidyValue: ARRAY[1..NoSubsidyCards] OF Real;
	TaxValue: ARRAY[1..NoTaxCards] OF Real;
	InitialSubsidyValue: ARRAY[1..NoSubsidyCards] OF Real;
	InitialTaxValue: ARRAY[1..NoTaxCards] OF Real;

	CardType: ARRAY[1..MaxNoCards] OF Integer;
	SubsidyIndex: ARRAY[1..NoSubsidyCards] OF Integer;
	TaxIndex: ARRAY[1..NoTaxCards] OF Integer;
	InversionIndex: ARRAY[1..MaxNoCards] OF Integer;

{These variables are used by NeanderCard only.}
	QuitFlag: Boolean;							{quit NeanderCard}
	whichControl: ControlHandle;
	ThisTextBox: Integer;
	ThisColor: RGBColor;
	MyBlack: RGBColor;
	ThisTriangle: Integer;
	CardScroll: ControlHandle;
	ListedCard: Integer;

PROCEDURE ClearRect (Right, Top, Left, Bottom: Integer);
FUNCTION HoldThatClick (ClickRect: Rect): Boolean;
FUNCTION MyReadString: Str255;
PROCEDURE ProcessEquationText;
PROCEDURE MyIntegerWrite (Value: Integer);
PROCEDURE SubWrite (Value: Real);
PROCEDURE MyFloatWrite (Value: Real);
PROCEDURE DrawNumericValue (Index: Integer);
PROCEDURE DrawBarChart;
PROCEDURE DrawCard;
PROCEDURE LoadCard;
PROCEDURE LoadScreen (ScreenName: Str255);
PROCEDURE CalcTreasury;
PROCEDURE DoDrag;

IMPLEMENTATION
{*****************************************************************}
PROCEDURE ClearRect (Right, Top, Left, Bottom: Integer);
{Frozen}
	VAR
		TempRect: Rect;
	BEGIN
		SetRect(TempRect, Right, Top, Left, Bottom);
		FillRect(TempRect, white);
	END;
{*****************************************************************}
FUNCTION HoldThatClick (ClickRect: Rect): Boolean;
{Frozen}
	VAR
		MousePoint: Point;
	BEGIN
		InvertRect(ClickRect);
		REPEAT
		UNTIL NOT StillDown;
		InvertRect(ClickRect);
		GetMouse(MousePoint);
		HoldThatClick := PtInRect(MousePoint, ClickRect);
	END;
{*****************************************************************}
FUNCTION MyReadString: Str255;
{Frozen}
	VAR
		BufferStr: Str255;
	BEGIN
		Count := 1;
		Error := FSRead(RefNum, Count, @BufferStr);
		error := SetFPos(RefNum, FSFromMark, -1);
		Count := Length(BufferStr) + 1;
		Error := FSRead(RefNum, Count, @BufferStr);
		MyReadString := BufferStr;
	END;
{*****************************************************************}
PROCEDURE ProcessEquationText;
{frozen}
	VAR
		i, j, m, n: Integer;
		ColonFlag: Boolean;
		ConstStr: Str255;
		TopLim: Integer;
		BotLim: Integer;
		LoopCounter: Integer;
		VariableText: Str255;
		VariableFlag: Boolean;
	BEGIN
		FOR i := 1 TO MaxConstants DO
			BEGIN
				CConstantText[i] := '';
				CConstantUnits[i] := '';
				CVariable[i] := 0;
			END;
		PEquationStr := '';
		IF EquationLength <= 1 THEN
			BEGIN
				ExplanationMark := 0;
			END
		ELSE
			BEGIN
				i := 1;
				m := 1;
				n := 1;
				VariableFlag := TRUE;
{Warning: this has got to be one of the most convoluted bits of code I have written in a long time; sorry}
{I am trying to parse the raw text file.  The curly brackets delimit a string to be used later, and the}
{colon within the curly brackets marks the string for the units to be used.  Not only must this code}
{isolate these substrings for later use in CConstantText and CConstantUnits, but it must also delete}
{them from the PEquationStr that will be printed.  To make matters worse, I have to mix an array}
{of characters with a Pascal string, and they must be handled differently.  What a mess!}
				REPEAT
					IF EquationText[i] = '{' THEN
						BEGIN
							i := i + 1;
							ColonFlag := FALSE;
							WHILE EquationText[i] <> '}' DO
								BEGIN
									IF EquationText[i] = ':' THEN
										BEGIN
											ColonFlag := TRUE;
										END
									ELSE
										BEGIN
											IF ColonFlag THEN
												BEGIN
													CConstantUnits[m] := Concat(CConstantUnits[m], EquationText[i]);
												END
											ELSE
												BEGIN
													PEquationStr := Concat(PEquationStr, EquationText[i]);
													CConstantText[m] := Concat(CConstantText[m], EquationText[i]);
												END;
										END;
									i := i + 1;
								END; {WHILE-statement}
							i := i + 1;
							IF m < MaxConstants THEN
								m := m + 1;
						END; {EquationText IF}

					IF EquationText[i] = '"' THEN
						BEGIN
							i := i + 1;
							VariableText := '';
							REPEAT
								PEquationStr := Concat(PEquationStr, EquationText[i]);
								VariableText := Concat(VariableText, EquationText[i]);
								i := i + 1;
							UNTIL EquationText[i] = '"';
							i := i + 1;
{This is just a binary search routine for the ID number of the CardName that matches VariableText}
							LoopCounter := 0;
							TopLim := MaxNoCards;
							BotLim := 0;
							j := MaxNoCards DIV 2;
							REPEAT
								IF CardName[j] > VariableText THEN
									BEGIN
										TopLim := j;
										j := (TopLim + BotLim) DIV 2;
									END;
								IF CardName[j] < VariableText THEN
									BEGIN
										BotLim := j;
										j := (TopLim + BotLim) DIV 2;
									END;
								LoopCounter := LoopCounter + 1;
							UNTIL (CardName[j] = VariableText) | (LoopCounter = 8);
							IF LoopCounter < 8 THEN
								BEGIN
									CVariable[n] := j;
									n := n + 1;
								END
							ELSE
								BEGIN
									SysBeep(10); {signal an error to Chris; this can be removed from final version.}
									LoopCounter := 8;
								END;
{End of binary search routine}
						END; {EquationText[i]='"' IF}

					IF ord(EquationText[i]) <> 13 THEN {This test is a kluge to fix a parsing bug}
						BEGIN
							PEquationStr := Concat(PEquationStr, EquationText[i]);
							i := i + 1;
						END;
				UNTIL ord(EquationText[i]) = 13;

				i := i + 1;
				ConstStr := '';
				REPEAT
					ConstStr := Concat(ConstStr, EquationText[i]);
					i := i + 1;
				UNTIL ord(EquationText[i]) = 13;
				ReadString(ConstStr, CardCULimit[1], CardCLLimit[1], CardCULimit[2], CardCLLimit[2], CardCULimit[3], CardCLLimit[3], CardCULimit[4], CardCLLimit[4]);
				ExplanationMark := i + 1;
			END;
	END;
{*****************************************************************}
PROCEDURE MyIntegerWrite (Value: Integer);
{Frozen}
	VAR
		NumStr: Str255;
	BEGIN
		NumToString(LongInt(Value), NumStr);
		DrawString(NumStr);
	END;
{*****************************************************************}
PROCEDURE SubWrite (Value: Real);
{Frozen}
	VAR
		LongX: LongInt;
		NumStr: Str255;
	BEGIN
		LongX := Trunc(Value);
		NumToString(LongX, NumStr);
		DrawString(NumStr);
	END;
{*****************************************************************}
PROCEDURE PostWrite (Value: Real; SuffixText: Str255);
{frozen}
	BEGIN
		IF Value < 10 THEN
			WriteDraw(Value : 3 : 2)
		ELSE IF Value < 100 THEN
			WriteDraw(Value : 3 : 1)
		ELSE
			SubWrite(Value);
		DrawString(' ');
		DrawString(SuffixText);
	END;
{*****************************************************************}
PROCEDURE MyFloatWrite (Value: Real);
{Frozen}
{This code is truly dumb. It's just a way of insuring that Real variables are printed in the format that}
{I want for them. The WriteDraw procedure in the library loves to default to scientific notation at}
{every opportunity, so I have to handle almost every case. Perhaps your library routine is a little}
{smarter, in which case this routine may be unnecessary or may need redesign.  The only real}
{constraint is that the number fit on the screen properly.  That achieved, it would be nice if it}
{were presented in a friendly format.}
{ECO0: I made major changes in this routine, adding a call to the new procedure PostWrite}
	BEGIN
		IF Value = 0 THEN
			DrawString('0')
		ELSE IF Value < 0.001 THEN
			WriteDraw(Value)
		ELSE IF Value < 0.01 THEN
			WriteDraw(Value : 5 : 5)
		ELSE IF Value < 0.1 THEN
			WriteDraw(Value : 4 : 4)
		ELSE IF Value < 1 THEN
			WriteDraw(Value : 3 : 3)
		ELSE IF Value < 1000 THEN
			PostWrite(Value, '')
		ELSE IF Value < 1e6 THEN
			PostWrite(Value / 1e3, 'thousand')
		ELSE IF Value < 1e9 THEN
			PostWrite(Value / 1e6, 'million')
		ELSE IF Value < 1e12 THEN
			PostWrite(Value / 1e9, 'billion')
		ELSE IF Value < 1e15 THEN
			PostWrite(Value / 1e12, 'trillion')
		ELSE
			WriteDraw(Value);
	END;
{*****************************************************************}
PROCEDURE DrawBitMap;
{Frozen}
{This procedure takes a packed-format bitmap and upacks it and copies it out to the screen in}
{fourteen chunks of 23 scan lines each. That particular combination seems to be the fastest for}
{the Macintosh. You will probably want to do something simpler or faster. Note that there is}
{enough RAM (I hope) left over that you can probably just store entire unpacked bitmaps and}
{blit them directly to the screen. My experience with the Mac is that it's just as fast to leave}
{them packed.}
	VAR
		dstBits: PACKED ARRAY[1..1656] OF byte;
		srcPtr: Ptr;
		dstPtr: Ptr;
		dstBMap: BitMap;
		showRect: Rect;
		lineRect: Rect;
		Scanline: Integer;
		i: Integer;
	BEGIN
		IF CardType[ThisCard] > 0 THEN
			srcPtr := SavedMapPtr[CardType[ThisCard]]
		ELSE
			srcPtr := PackedMapPtr;
		SetRect(lineRect, 0, 0, BitMapWidth, 23);
		dstBMap.rowBytes := 72;
		dstBMap.baseAddr := @dstBits;
		SetRect(dstBMap.bounds, 0, 0, 576, 23);
		dstPtr := pointer(dstBMap.baseAddr);
		SetRect(showRect, 0, 0, BitMapWidth, 23);
		FOR Scanline := 1 TO 14 DO
			BEGIN
				FOR i := 1 TO 23 DO
					UnpackBits(srcPtr, dstPtr, 72);
				CopyBits(dstBMap, MainWind^.portbits, lineRect, showRect, srcCopy, NIL);
				dstPtr := pointer(dstBMap.baseAddr);
				OffsetRect(showRect, 0, 23);
			END;
	END;
{*****************************************************************}
PROCEDURE DrawNumericValue (Index: Integer);
{frozen}
{ECO 2: This is a new routine that simplifies some code in various places. It also introduces some}
{           changes that are otherwise undocumented. I figured that, since your are redoing the code,}
{           I might as well get away with some undocumented changes. Cute, huh?}
	BEGIN
		FillRect(ValueRect, white);
		TextSize(9);
		TextFont(1);
		MoveTo(404, BaseY - 114);
		MyIntegerWrite(1985 + 5 * Index);
		DrawString(' value:');
		MoveTo(404, BaseY - 102);
		MyFloatWrite(CardHistory[ThisCard, Index]);
{ECO 4: added an IF-statement here}
		IF (CardType[ThisCard] = TaxCard) | (CardType[ThisCard] = SubsidyCard) THEN
			DrawString(' $')
		ELSE
			DrawString(Concat(' ', CardUnit[ThisCard]));
	END;
{*****************************************************************}
PROCEDURE DrawBarChart;
{frozen}
{This is pulled out of DrawCard to allow me to refresh the values in the bar chart when the}
{user modifies the scroll bar in subsidies and taxes.}
{ECO 2: Major changes here; I am being loosey-goosey in precisely documenting because they}
{           are more structural. The basic idea is to replace the previous four-bar bar chart with}
{           a new ten-bar bar chart. Along the way, I loopified a lot of code that had previously}
{           been explicit for each of the four cases. Note that ValueA, ValueB, etc, are gone.}
{           Note also the minor change adding the label above the numeric presentation of the value.}
{           There are also ripple-effect changes in the global declaration of CardHistory and BarRect,}
{            in the initialization code for CardHistory, and in the event-handler for RunCard.}
	VAR
		i, y: Integer;
		BiggestValue: Real;
		TempRect: Rect;
	BEGIN
{ECO 1: added these two lines to clear the area}
		SetRect(TempRect, BaseX, BaseY - 100, BaseX + 100, BaseY);
		FillRect(TempRect, white);

		BiggestValue := 0;
		FOR i := 1 TO Turn DO
			BEGIN
				IF CardHistory[ThisCard, i] > BiggestValue THEN
					BiggestValue := CardHistory[ThisCard, i];
			END;
		FOR i := 1 TO 10 DO    {ECO 2: Changed upper limit of the loop to 10}
			SetRect(BarRect[i], 0, 0, 0, 0);
		IF BiggestValue > 0 THEN
			BEGIN
				FOR i := 1 TO Turn DO
					BEGIN
						y := BaseY - trunc((CardHistory[ThisCard, i] * 100) / BiggestValue);
						SetRect(BarRect[i], BaseX + (i - 1) * 10, y, BaseX + i * 10, BaseY + 1);
						FillRect(BarRect[i], gray);
						FrameRect(BarRect[i]);
					END;
			END;

		MoveTo(BaseX - 2, BaseY);
		LineTo(BaseX + 98, BaseY);

{ECO 1: I commented out these four lines}
{PenPat(ltGray);}
{MoveTo(BaseX, BaseY - 100);}
{LineTo(BaseX + 96, BaseY - 100);}
{PenNormal;}
		DrawNumericValue(Turn);
	END;
{*****************************************************************}
PROCEDURE DrawCard;
{frozen}
{This is probably the most important procedure in the program. I will not freeze it just yet, but it}
{is fairly stable already. The basic task is to blit the bitmap onto the screen, then draw the}
{textboxes and color triangles.  Note that the order in which the tasks are executed can be}
{significant. I do not require you to use different fonts; if you have a library routine that will}
{draw multiple fonts, good, but we may want to save some cycles here. The color triangles,}
{however, must be retained.    There are a lot of judgements implicit in this routine, and I }
{will be expecting you to consult with me on corners you can cut to make this procedure run}
{with acceptable speed. I do not expect you to be able to give me everything in here at the}
{same speed. So what do we cut?}
	VAR
		i, j, k, n, x, y, z, r, b: Integer;
		TempRect: Rect;
		TempStr: Str255;
		LineStr: Str255;
		WordStr: Str255;
		EndOfLine: Boolean;
		EndOfText: Boolean;
		MyPoly: PolyHandle;
		LineFits: Boolean;
		SavedBitMap: BitMap;
		LongX: LongInt;
		NumStr: Str255;
	BEGIN
		IF ThisCard = 0 THEN
			Exit(DrawCard);
		DrawBitMap;

{Note that the XOr mode permits me to put white characters on a black background, or}
{black characters on a white background. Can you do that?  Do we have a problem?}
		TextMode(srcXOr);
		PenMode(PatXOr);
		i := 1;
		WHILE i <= NumTextBoxes DO
			BEGIN
				z := TextBox[i].TSize;
				TextFont(TextBox[i].TFont);
				TextSize(z);
				CASE TextBox[i].TFace OF
					0: 
						TextFace([]);
					1: 
						TextFace([bold]);
					2: 
						TextFace([outline]);
					OTHERWISE
						TextFace([]);
				END;
				x := TextBox[i].TRect.left;
				y := TextBox[i].TRect.top;
				r := TextBox[i].TRect.right;
				b := TextBox[i].TRect.bottom;
				TempStr := TextBoxText[i];

{this code draws the text in the box with wordwrap.}
				j := 1;
				k := 1;
				EndOfText := FALSE;
				REPEAT {add lines to box}
					LineStr := '';
					REPEAT {add words to line}
						EndOfLine := FALSE;
						REPEAT {add characters to word}
							j := j + 1;
						UNTIL (j > Length(TempStr)) | (TempStr[j] = ' ');
						WordStr := Copy(TempStr, k, j - k);
						LineFits := (StringWidth(LineStr) + StringWidth(WordStr) <= (r - x - 4));
						IF j > Length(TempStr) THEN
							BEGIN
								EndOfText := TRUE;
								IF LineFits THEN
									LineStr := Concat(LineStr, Copy(TempStr, k, j - k));
								MoveTo(x + 2, y + z + 2);
								DrawString(LineStr);
								y := y + z + 2;
								k := k + 1;
								EndOfLine := TRUE;
								IF NOT LineFits THEN
									BEGIN
										MoveTo(x + 2, y + z + 2);
										DrawString(Copy(TempStr, k, j - k));
										y := y + z + 2;
										k := k + 1;
									END;
							END
						ELSE {have not yet reached end of TempString}
							BEGIN
								IF LineFits THEN
									BEGIN
										LineStr := Concat(LineStr, WordStr);
										k := j;
									END
								ELSE
									BEGIN
										MoveTo(x + 2, y + z + 2);
										DrawString(LineStr);
										y := y + z + 2;
										k := k + 1;
										EndOfLine := TRUE;
									END;
							END;
					UNTIL EndOfLine;
				UNTIL (j = Length(TempStr)) | (y > b) | EndOfText;

{$IFC NeanderLives}
				IF i = ThisTextBox THEN
					FrameRect(TextBox[i].TRect);
{$ENDC}

				TextFace([]);
				i := i + 1;
			END; {TextBoxes WHILE}
		TextMode(SrcCopy);
		PenNormal;

{ECO 2: insert this one line here}
		PScrollFlag := FALSE;

{here follow modifications specific to certain types of cards}
		IF CardType[ThisCard] = TaxCard THEN
			BEGIN
				k := InversionIndex[ThisCard];
				TextFont(1);
				TextSize(12);
				MoveTo(164, 116);
				MyFloatWrite(TaxValue[k]);
				DrawString(CardUnit[ThisCard]);
				MoveTo(54, 155);
				DrawString('This is the tax that you levy on ');
				n := Effect[ThisCard, 1];
				DrawString(CardName[n]);
				DrawString('.');
{ECO 4: I made changes in the wording here.}
				MoveTo(54, 170);
				DrawString('Increasing it will discourage production. This');
				MoveTo(54, 185);
				DrawString('will have both environmental and economic');
				MoveTo(54, 200);
				DrawString('consequences. Although you can change');
				MoveTo(54, 215);
				DrawString('the numbers now and see immediate effects');
				MoveTo(54, 230);
				DrawString('in the bar chart, the effects on the world');
				MoveTo(54, 245);
				DrawString('will not take place until you Execute Policies.');
{ECO 2: moved these next four lines in from RunCard}
				SetRect(TempRect, 150, 120, 250, 136);
				x := trunc(100 * (TaxValue[k] - InitialTaxValue[k] / 4) / (3.75 * InitialTaxValue[k]));
				PolicyScroll := NewControl(MainWind, TempRect, '', TRUE, x, 0, 100, ScrollBarProc, 0);
				PScrollFlag := TRUE;
			END;

		IF (CardType[ThisCard] = SubsidyCard) & (ThisCard <> PropertyDamage%S) THEN
			BEGIN
				k := InversionIndex[ThisCard];
				TextFont(1);
				TextSize(12);
				MoveTo(164, 116);
				MyFloatWrite(SubsidyValue[k]);
				DrawString('%');
				MoveTo(54, 155);
				DrawString('This is your subsidy for ');
				DrawString(CardName[ThisCard]);
{This is a real kluge! I'm just backing up and wiping out the $ left over in CardName.}
				Move(-10, 0);
				DrawString('.   ');
{ECO 4: I made changes in the wording here.}
				MoveTo(54, 170);
				DrawString('You can increase its percentage of your budget,');
				MoveTo(54, 185);
				DrawString('but only if you have budget percentage');
				MoveTo(54, 200);
				DrawString('points to spare. Although you can change the');
				MoveTo(54, 215);
				DrawString('numbers now and see immediate effects in');
				MoveTo(54, 230);
				DrawString('the bar chart, the effects on the world');
				MoveTo(54, 245);
				DrawString('will not take place until you Execute Policies.');
{ECO 2: Moved these next eight lines in from RunCard}
				TotalSubsidy := 0;
				FOR i := 1 TO NoSubsidyCards DO
					TotalSubsidy := TotalSubsidy + SubsidyValue[i];
				y := trunc(100 - TotalSubsidy + SubsidyValue[k]);
				SetRect(TempRect, 150, 120, 250, 136);
				x := trunc(SubsidyValue[k]);
				PolicyScroll := NewControl(MainWind, TempRect, '', TRUE, x, 0, y, ScrollBarProc, 0);
				PScrollFlag := TRUE;
			END;

		ClearRect(BitMapWidth + 1, 0, 512, BitMapHeight);
		MoveTo(BitMapWidth, 0);
		LineTo(BitMapWidth, BitMapHeight);

{here we draw the causes and the effects}
		TextFont(0);
		TextSize(12);
		x := StringWidth('CAUSES') DIV 2;
		MoveTo(456 - x, 14);
		DrawString('CAUSES');
		TextFont(1);
		TextSize(10);
		y := 26;
		FOR i := 1 TO CauseCount[ThisCard] DO
			BEGIN
				x := Cause[ThisCard, i];
				MoveTo(404, y);
				y := y + 12;
				DrawString(CardName[x]);
			END;
		TextFont(0);
		TextSize(12);
		y := y + 18;
		x := StringWidth('EFFECTS') DIV 2;
		MoveTo(456 - x, y);
		DrawString('EFFECTS');
		y := y + 10;
		TextFont(1);
		TextSize(10);
		FOR i := 1 TO EffectCount[ThisCard] DO
			BEGIN
				x := Effect[ThisCard, i];
				MoveTo(404, y);
				y := y + 12;
				DrawString(CardName[x]);
			END;

{Now I draw the bar chart, self-scaling}
		DrawBarChart; {ECO 1: This code has been separated out for screen refreshes}
		TextFont(20);
		TextSize(10);
{ECO 2: documenting this got to be such a mess I just cleared it out and started over.}
{           Please recode these next six lines.}
		MoveTo(BaseX - 4, BaseY + 10);
		DrawString('1990');
		MoveTo(BaseX + 37, BaseY + 10);
		DrawString('DATE');
		MoveTo(BaseX + 80, BaseY + 10);
		DrawString('2040');

{ECO 1: I simply eliminated the equation and earth clickrects. Voila!}
{TextFont(1);}
{TextSize(10);}
{ECO 0: I moved the word up and eliminated the SetRect and FrameRect calls.}
{MoveTo(404, BaseY - 139);}
{DrawString('Equation');}

{ECO 0: I moved the word left and eliminated the SetRect and FrameRect calls.}
{MoveTo(404, BaseY - 124);}
{DrawString('Earth');}

{The basic strategy of this code relies heavily on some special characteristics of the Toolbox.}
{In particular, I am using Copybits in a srcOr mode with a foreground color -- that's odd.}
{I am also using FillPoly, a routine that you may have difficulty replicating. All in all, }
{you are probably best advised to take a completely different tack here.  Just read in the}
{triangle coordinates and use whatever means you can to directly draw the color onto the screen.}
		IF YesColor THEN
			BEGIN
				SavedBitMap.bounds := BitMapRect;
				SavedBitMap.rowBytes := 50;
				SavedBitMap.baseAddr := NewPtr(16100);
				CopyBits(MainWind^.portbits, SavedBitMap, SavedBitMap.bounds, SavedBitMap.bounds, srcCopy, NIL);
				i := 1;
				WHILE i <= NumTriangles DO
					BEGIN
						MyPoly := OpenPoly;
						MoveTo(CTriangle[i].Vertices[3].h, CTriangle[i].Vertices[3].v);
						FOR j := 1 TO 3 DO
							LineTo(CTriangle[i].Vertices[j].h, CTriangle[i].Vertices[j].v);
						ClosePoly;
						RGBForeColor(CTriangle[i].color);
						FillPoly(MyPoly, black);
						RGBForeColor(MyBlack);
						CopyBits(SavedBitMap, MainWind^.portbits, MyPoly^^.PolyBBox, MyPoly^^.PolyBBox, srcOr, NIL);
						KillPoly(MyPoly);
{$IFC NeanderLives}
						IF i = ThisTriangle THEN
							BEGIN
								x := 0;
								y := 0;
								FOR j := 1 TO 3 DO
									BEGIN
										x := x + CTriangle[i].Vertices[j].h;
										y := y + CTriangle[i].Vertices[j].v;
									END;
								x := x DIV 3;
								y := y DIV 3;
								MyPoly := OpenPoly;
								MoveTo((CTriangle[i].Vertices[3].h + x) DIV 2, (CTriangle[i].Vertices[3].v + y) DIV 2);
								FOR j := 1 TO 3 DO
									LineTo((CTriangle[i].Vertices[j].h + x) DIV 2, (CTriangle[i].Vertices[j].v + y) DIV 2);
								ClosePoly;
								InvertPoly(MyPoly);
								RGBForeColor(MyBlack);
								CopyBits(SavedBitMap, MainWind^.portbits, MyPoly^^.PolyBBox, MyPoly^^.PolyBBox, srcOr, NIL);
								KillPoly(MyPoly);
							END; {i=ThisTriangle IF}
{$ENDC}
						i := i + 1;
					END; {NumTriangles WHILE}
				DisposPtr(SavedBitMap.baseaddr);
			END; {YesColor IF}

		SetRect(TempRect, 0, 0, 512, 342);
		ValidRect(TempRect); {turn off the damn update mechanism, thank you!}

	END;
{*****************************************************************}
PROCEDURE LoadCard;
{frozen}
{This procedure loads in the card record. The record structure is rather messy, and I think}
{that this code is the best documentation for it. However, if you have difficulty figuring it}
{out, I'll be happy to come up with something more formal.}
	VAR
		i: Integer;
		CType: Integer;
	BEGIN
{ECO 2: added this IF statement}
{ECO 4: moved this IF-statement to RunCard in Game.p}
{IF PScrollFlag THEN}
{DisposeControl(PolicyScroll);}
		CType := CardType[ThisCard];
{ECO 0: I changed the pathname to break up the folders into manageable sizes.}
		IF CardName[ThisCard] <= 'J' THEN
			Error := FSOpen(Concat(':Cards A-J:', CardName[ThisCard]), 0, RefNum)
		ELSE
			Error := FSOpen(Concat(':Cards K-W:', CardName[ThisCard]), 0, RefNum);
		IF Error = 0 THEN
			BEGIN
{First come 10 bytes specifying PackedSize, NumTriangles, NumTextBoxes, and EquationLength}
				Count := 10;
				Error := FSRead(RefNum, Count, @PackedSize);

				IF CType = 0 THEN
					BEGIN
{next come PackedSize bytes -- note variable length -- of bitmap; but only if it's CardType=0.}
						Error := FSRead(RefNum, PackedSize, PackedMapPtr);
{Again, if CardType=0, then we read in some number of color triangles.}
						i := 1;
						WHILE i <= NumTriangles DO
							BEGIN
								Count := SizeOf(CTriRec);
								Error := FSRead(RefNum, Count, @CTriangle[i]);
								i := i + 1;
							END;
					END;

{Now we read in some number of textbox records.}
				i := 1;
				WHILE i <= NumTextBoxes DO
					BEGIN
						Count := 14;
						Error := FSRead(RefNum, Count, @TextBox[i]);
						TextBoxText[i] := MyReadString;
						i := i + 1;
					END;

{now we read in EquationLength bytes into EquationText.}
				Count := LongInt(EquationLength);
				Error := FSRead(RefNum, Count, @EquationText);

{This call will crash if the equation text won't parse. What a pain! What bad programming!}
				ProcessEquationText;

{If this is a standard card, then we need to fake out DrawCard.}
				IF CType > 0 THEN
					BEGIN
						NumTriangles := SavedNumTriangles[CType];
						i := 1;
						WHILE i <= NumTriangles DO
							BEGIN
								CTriangle[i] := SavedCTriangle[CType, i];
								i := i + 1;
							END;
					END;
			END {of Error=0 IF}
{ECO 0: I decided it would be nice to put in some error checking here.}
		ELSE
			i := StopAlert(344, NIL);

		Error := FSClose(RefNum);
	END;
{*****************************************************************}
PROCEDURE LoadScreen (ScreenName: Str255);
{frozen}
	BEGIN
		Error := FSOpen(ScreenName, 0, RefNum);
		IF Error = 0 THEN
			BEGIN
				Count := headersize;
{skip over the silly 512-byte MacPaint header.}
				Error := FSRead(RefNum, Count, PackedMapPtr);
				PackedSize := 21888;
{Now read the real bitmap.}
				Error := FSRead(RefNum, PackedSize, PackedMapPtr);
				Error := FSClose(RefNum);
			END
		ELSE
			Error := StopAlert(344, NIL);
	END;
{*****************************************************************}
PROCEDURE CalcTreasury;
{frozen}
	BEGIN
		Treasury := CardValue[CFCTax] + CardValue[CoalTax] + CardValue[OilTax] + CardValue[NaturalGasTax];
		Treasury := Treasury + CardValue[NuclearTax] + CardValue[BeefTax] + CardValue[LoggingTax];
		Treasury := Treasury + CardValue[HeavyMetalTax] + CardValue[FertilizerTax] + CardValue[PesticideTax];
		Treasury := Treasury - CardValue[PropertyDamage%S];
		IF Treasury < 0 THEN
			Treasury := 0;
		CardValue[BasicResearch%S] := Treasury * SubsidyValue[BasicResearch%SIndex] / 100;
		CardValue[BioResearch%S] := Treasury * SubsidyValue[BioResearch%SIndex] / 100;
		CardValue[CoalResearch%S] := Treasury * SubsidyValue[CoalResearch%SIndex] / 100;
		CardValue[NuclearResearch%S] := Treasury * SubsidyValue[NuclearResearch%SIndex] / 100;
		CardValue[OilResearch%S] := Treasury * SubsidyValue[OilResearch%SIndex] / 100;
		CardValue[SolarResearch%S] := Treasury * SubsidyValue[SolarResearch%SIndex] / 100;
		CardValue[Dam%S] := Treasury * SubsidyValue[Dam%SIndex] / 100;
		CardValue[DebtForNature%S] := Treasury * SubsidyValue[DebtForNature%SIndex] / 100;
		CardValue[FamilyPlanning%S] := Treasury * SubsidyValue[FamilyPlanning%SIndex] / 100;
		CardValue[RecyclingCenter%S] := Treasury * SubsidyValue[RecyclingCenter%SIndex] / 100;
		CardValue[SolarEnergy%S] := Treasury * SubsidyValue[SolarEnergy%SIndex] / 100;
		CardValue[WoodStove%S] := Treasury * SubsidyValue[WoodStove%SIndex] / 100;

{ECO 6: These lines are necessary to correct a minor bug in the card display}
		IF Turn > 0 THEN
			BEGIN
				CardHistory[BasicResearch%S, Turn] := CardValue[BasicResearch%S];
				CardHistory[BioResearch%S, Turn] := CardValue[BioResearch%S];
				CardHistory[CoalResearch%S, Turn] := CardValue[CoalResearch%S];
				CardHistory[NuclearResearch%S, Turn] := CardValue[NuclearResearch%S];
				CardHistory[OilResearch%S, Turn] := CardValue[OilResearch%S];
				CardHistory[SolarResearch%S, Turn] := CardValue[SolarResearch%S];
				CardHistory[Dam%S, Turn] := CardValue[Dam%S];
				CardHistory[DebtForNature%S, Turn] := CardValue[DebtForNature%S];
				CardHistory[FamilyPlanning%S, Turn] := CardValue[FamilyPlanning%S];
				CardHistory[RecyclingCenter%S, Turn] := CardValue[RecyclingCenter%S];
				CardHistory[SolarEnergy%S, Turn] := CardValue[SolarEnergy%S];
				CardHistory[WoodStove%S, Turn] := CardValue[WoodStove%S];
			END;
	END;
{*****************************************************************}
PROCEDURE DoDrag;
{frozen}
	VAR
		x, y: Integer;
		LimitRect: Rect;
	BEGIN
		x := ScreenBits.bounds.right;
		y := ScreenBits.bounds.bottom;
		SetRect(LimitRect, 0, 20, x, y);
		InsetRect(LimitRect, 4, 4);
		DragWindow(MainWind, MyEvent.where, LimitRect);
	END;
{************************************************************************}
END.