-- DynamicBitsXXXImpl.meta
-- Copyright (C) 1984, 1985, Xerox Corporation.  All rights reserved.
-- Michael Plass, November 6, 1985 9:52:38 am PST
-- PARAMETERS sKernelRadius, fKernelRadius, sModelRadius, fModelRadius, swathSize

DIRECTORY DynamicBits, Basics, ImagerPixelMap, ImagerSample, PixelMapOps;

DynamicBits011Impl: CEDAR PROGRAM
IMPORTS Basics, ImagerPixelMap, ImagerSample, PixelMapOps, DynamicBits
~ BEGIN OPEN DynamicBits;
	
	
	KernelArray: TYPE ~ ARRAY [0..1) OF ARRAY [0..1) OF INTEGER;
	
	SampleBuffer: TYPE ~ ImagerSample.SampleBuffer;
	
	PixelRow: TYPE ~ RECORD[sOrigin, fOrigin: INTEGER, sample: SampleBuffer];
	
	CreatePixelRow: PROC [s, f: INTEGER, size: NAT] RETURNS [PixelRow] ~ {
		buffer: SampleBuffer ~ ImagerSample.NewBuffer[iSize: 1, jSize: size];
		RETURN [[sOrigin: s, fOrigin: f, sample: buffer]]
		};
	
	CopyPixelRow: PROC [old: PixelRow] RETURNS [PixelRow] ~ {
		new: PixelRow ~ CreatePixelRow[old.sOrigin, old.fOrigin, old.sample.jSize];
		PixelMapOps.CopySamples[buffer: new.sample, bi: 0, bj: 0, count: old.sample.jSize, source: old.sample, si: 0, sj: 0];
		RETURN [new]
		};
	
	ModelData: TYPE ~ REF ModelDataRep;
	ModelDataRep: TYPE ~ ARRAY [0..512) OF REF ModelDataItem;
	ModelDataItem: TYPE ~ RECORD [
		intensity: Intensity,
		noisePenalty: NoisePenalty,
		a: KernelArray
		];
	
	ModelParameterFault: PUBLIC ERROR [data: REF] ~ CODE;
	
	neighborhoodBounds: DeviceRectangle ~ [(-1), (-1), 3, 3];
	CheckNeighborhood: PROC [neighborhood: DeviceRectangle] ~ {
		IF neighborhood # neighborhoodBounds THEN ModelParameterFault[$neighborhood];
		};
	
	kernelBounds: DeviceRectangle ~ [0, 0, 1, 1];
	CheckKernel: PROC [bounds: DeviceRectangle] ~ {
		IF bounds # kernelBounds THEN ModelParameterFault[$kernel];
		};
	
	ArrayFromKernel: PROC [kernel: Kernel] RETURNS [kernelArray: KernelArray]  ~ {
		CheckKernel[kernel.Window];
		FOR s: INTEGER IN [0..1) DO
			FOR f: INTEGER IN [0..1) DO
				kernelArray[s][f] ← LOOPHOLE[kernel.GetPixel[s, f], INTEGER];
				ENDLOOP;
			ENDLOOP;
		};
	
	CreatePrinterModel: PROC [neighborhood: DeviceRectangle, printerModel: PrinterModel, kernel: PixelMap] RETURNS [model: Model] ~ {
		bitmap: PixelMap ← ImagerPixelMap.Create[0, neighborhood];
		data: ModelData ← NEW[ModelDataRep];
		kernelArray: KernelArray ← ArrayFromKernel[kernel];
		CheckNeighborhood[neighborhood];
		model ← NEW[ModelRep];
		FOR encoding: CARDINAL IN [0..512) DO
			intensity: Intensity;
			noisePenalty: NoisePenalty;
			scaledArray: KernelArray;
			TRUSTED {
				t: CARDINAL ← Basics.BITSHIFT[encoding, Basics.bitsPerWord-9];
				bitmap ← MakePixelMapFromBits[@t, 3, neighborhoodBounds, bitmap.refRep];
				};
			[intensity, noisePenalty] ← printerModel[bitmap, encoding];
			FOR s: INTEGER IN [0..1) DO
				FOR f: INTEGER IN [0..1) DO
					k: INTEGER ← kernelArray[s][f];
					scaledArray[s][f] ←
					IF k>=0 THEN (Basics.LongMult[k, intensity]+127)/255
					ELSE -((Basics.LongMult[-k, intensity]+127)/255);
					ENDLOOP;
				ENDLOOP;
			data[encoding] ← NEW[ModelDataItem ← [intensity, noisePenalty, scaledArray]];
			ENDLOOP;
		model.neighborhood ← neighborhood;
		model.kernel ← kernel.Copy;
		model.data ← data;
		};
	
	DoWithPrinterModel: PROC [model: Model, action: PROC[PrinterModel]] ~ {
		data: ModelData ~ NARROW[model.data];
		printerModel: PROC [bitmap: PixelMap, encoding: CARDINAL] RETURNS [Intensity, NoisePenalty] ~ {
			item: REF ModelDataItem ~ data↑[encoding];
			RETURN [item.intensity, item.noisePenalty];
			};
		action[printerModel];
		};
	
	StorePixelRow: PROC [p: PixelRow, pixelMap: PixelMap] ~ {
		fMin: INTEGER ← MAX[p.fOrigin, pixelMap.fOrigin+pixelMap.fMin];
		fMax: INTEGER ← MIN[p.fOrigin+p.sample.jSize, pixelMap.fOrigin+pixelMap.fMin+pixelMap.fSize];
		IF fMax<=fMin THEN RETURN;
		PixelMapOps.PutF[pixelMap: pixelMap, s: p.sOrigin, f: fMin, buffer: p.sample, bi: 0, bj: fMin-p.fOrigin, count: fMax-fMin];
		};
	
	Load: PROC [p: PixelRow, pixelMap: PixelMap] ~ {
		-- Takes care of boundary conditions
		fMin: INTEGER ← MAX[p.fOrigin, pixelMap.fOrigin+pixelMap.fMin];
		fMax: INTEGER ← MIN[p.fOrigin+p.sample.jSize, pixelMap.fOrigin+pixelMap.fMin+pixelMap.fSize];
		sMin: INTEGER ← MIN[MAX[p.sOrigin, pixelMap.sOrigin+pixelMap.sMin], pixelMap.sOrigin+pixelMap.sMin+pixelMap.sSize-1];
		IF fMax<=fMin THEN {PixelMapOps.ClearSamples[buffer: p.sample, count: p.sample.jSize]; RETURN};
		PixelMapOps.GetF[pixelMap: pixelMap, s: sMin, f: fMin, buffer: p.sample, bi: 0, bj: fMin-p.fOrigin, count: fMax-fMin];
		FOR i: INTEGER IN [0..fMin-p.fOrigin) DO
			p.sample[i] ← p.sample[fMin-p.fOrigin];
			ENDLOOP;
		FOR i: INTEGER IN [fMax-p.fOrigin..p.sample.jSize) DO
			p.sample[i] ← p.sample[fMax-1-p.fOrigin];
			ENDLOOP;
		};
	
	Convolve: PROC [pixelMap: PixelMap, kernel: Kernel, unitIntensity: CARDINAL] ~ {
		bounds: DeviceRectangle ← pixelMap.BoundedWindow;
		kernelArray: KernelArray ← ArrayFromKernel[kernel];
		p: ARRAY [0..1) OF PixelRow ← CreatePixelRows[];
		CreatePixelRows: PROC RETURNS[p: ARRAY[0..1) OF PixelRow]~{
			FOR i: INTEGER IN [0..1) DO
				p[i] ← CreatePixelRow[bounds.sMin+i, bounds.fMin+0, bounds.fSize+1-1];
				ENDLOOP;
			};
		q: PixelRow ← CopyPixelRow[p[0]];
		kernelMag: INTEGER ← KernelWeight[kernel];
		halfUnit: CARDINAL ← unitIntensity/2;
		maxMag: INT ← Basics.LongMult[ABS[kernelMag], INT[unitIntensity]]+halfUnit;
		Cd: PROC [i: INTEGER] RETURNS [CARDINAL] ~ INLINE {RETURN [LOOPHOLE[i]]};
		short: BOOLEAN ← maxMag < CARDINAL.LAST;
		FOR ss: INT IN [0..1) DO
			FOR ff: INT IN [0..1) DO
				short ← short AND kernelArray[ss][ff] >= 0;
				ENDLOOP;
			ENDLOOP;
		FOR i: INTEGER IN [0..1) DO Load[p[i], pixelMap] ENDLOOP;
		THROUGH [0..bounds.sSize) DO
			IF short THEN {
				-- Use 16-bit arithemetic for greater speed.
				FOR f: NAT IN [-0..bounds.fSize-0) DO
					pix: CARDINAL ← p[-0].sample[f-0]*Cd[kernelArray[0][0]] +  0;
					q.sample[f] ← (pix+halfUnit)/unitIntensity;
					ENDLOOP;
				}
			ELSE {
				FOR f: NAT IN [-0..bounds.fSize-0) DO
					pix: INT ← 0;
					FOR ss: INT IN [0..1) DO
						FOR ff: INT IN [0..1) DO
							pix ← pix + p[-ss].sample[f-ff]*INT[kernelArray[ss][ff]];
							ENDLOOP;
						ENDLOOP;
					q.sample[f] ← (pix+halfUnit)/unitIntensity;
					ENDLOOP;
				};
			StorePixelRow[q, pixelMap];
			{t: PixelRow ← p[0]; p[1-1] ← t};
			p[1-1].sOrigin ← p[1-1].sOrigin + (1-0);
			Load[p[1-1], pixelMap];
			q.sOrigin ← p[0].sOrigin;
			ENDLOOP;
		};
	
	ApplyModel: PROC [bitmap: PixelMap, model: Model] RETURNS [PixelMap] ~ {
		w: DeviceRectangle ← bitmap.Window;
		gray: PixelMap ← ImagerPixelMap.Create[3, w];
		bitRow: PixelRow ← CreatePixelRow[w.sMin+(-1), w.fMin+(-1), w.fSize+3];
		indexRow: PixelRow ← CreatePixelRow[w.sMin, w.fMin, w.fSize];
		destRow: PixelRow ← CopyPixelRow[indexRow];
		data: ModelData ← NARROW[model.data];
		MergeBits: PROC ~ {
			FOR f: NAT IN [0..indexRow.sample.jSize) DO
				k: CARDINAL ← 0;
				FOR ff: NAT IN [f..f+3) DO
					k ← k*2+bitRow.sample[ff];
					ENDLOOP;
				indexRow.sample[f] ← (indexRow.sample[f] * 8 + k) MOD 512;
				ENDLOOP;
			};
		PixelMapOps.ClearSamples[buffer: bitRow.sample, count: bitRow.sample.jSize];
		PixelMapOps.ClearSamples[buffer: indexRow.sample, count: indexRow.sample.jSize];
		PixelMapOps.ClearSamples[buffer: destRow.sample, count: destRow.sample.jSize];
		FOR i: NAT IN [0..3) DO
			Load[bitRow, bitmap];
			MergeBits[];
			bitRow.sOrigin ← bitRow.sOrigin + 1;
			ENDLOOP;
		FOR s: NAT IN [0..w.sSize) DO
			FOR f: NAT IN [0..destRow.sample.jSize) DO
				destRow.sample[f] ← data[indexRow.sample[f]].intensity;
				ENDLOOP;
			StorePixelRow[destRow, gray];
			destRow.sOrigin ← destRow.sOrigin + 1;
			Load[bitRow, bitmap];
			MergeBits[];
			bitRow.sOrigin ← bitRow.sOrigin + 1;
			ENDLOOP;
		RETURN [gray]
		};
	
	TuneSwath: PROC [gray: PixelMap, bitmap: PixelMap, fixedBits: PixelMap, swath: DeviceRectangle, model: Model, scratch: REF] RETURNS [newScratch: REF] ~ {
		
		
		-- Make sure the swath has the right fSize
		xxx: [0..0] ← swath.fSize-1;
		-- sOrigin and fOrigin give the position of the origin of the local swatch in the bitmap coordinate system.
		sOrigin: INTEGER ← swath.sMin-1-1;
		fOrigin: INTEGER ← swath.fMin;
		pixelRow: PixelRow ← CreatePixelRow[sOrigin, fOrigin+(-2), 3-(-2)];
		swathRow: PixelRow ← CreatePixelRow[sOrigin, fOrigin, 1];
		bit: ARRAY [(-1)..2) OF ARRAY [(-2)..3) OF [0..1];
		-- The current bits from the bitmap within the swatch
		index: ARRAY [0..1) OF ARRAY [(-1)..2) OF [0..512);
		-- The current bits, packed up into indices into the model array
		fixed: ARRAY [(-1)..2) OF ARRAY [0..1) OF [0..1];
		-- A local copy of a piece of fixedBits
		target: ARRAY [(-1)..2) OF INTEGER;
		-- A local copy of a piece of gray
		trial: ARRAY [(-1)..2) OF INTEGER;
		-- The partially computed approximation to the gray
		modelData: ModelData ← NARROW[model.data];
		
		nonFixedBitCount: NAT ← 0;
		SF: TYPE ~ RECORD [s: [(-1)..2), f: [0..1)];
		nonFixedBitIndex: ARRAY [0..(2-(-1))*1) OF SF;
		
		questionBits: [0..2);
		-- The bits that need to be determined for a given context.
		contextBits: [0..4);
		-- The context bits in the swath.
		prevContextBits: [0..4);
		-- The context bits for the previous subproblem.
		
		SubproblemSolution: TYPE ~ RECORD [
			setting: [0..2),
			-- The setting of questionBits that achieves the lowest total penalty for the swath so far.
			minErrHighHalf: [0..CARDINAL.LAST/2],
			minErrLowHalf: CARDINAL
			-- The lowest total penalty for the swath so far.
			-- Packed so the whole thing fits in two words, since there are a lot of them.
			];
		
		MakeSubproblemSolution: PROC [setting: [0..2), minErr: INT] RETURNS [SubproblemSolution] ~ INLINE BEGIN
			long: Basics.LongNumber ← [li[minErr]];
			RETURN [[setting: setting, minErrHighHalf: long.highbits, minErrLowHalf: long.lowbits]]
			END;
		
		MinErr: PROC [s: SubproblemSolution] RETURNS [INT] ~ INLINE BEGIN
			long: Basics.LongNumber ← [num[lowbits: s.minErrLowHalf, highbits: s.minErrHighHalf]];
			RETURN [long.li]
			END;
		
		PerSwatchTable: TYPE ~ ARRAY [0..4) OF SubproblemSolution;
		
		avail: LIST OF REF PerSwatchTable ← NARROW[scratch];
		
		subproblemTable: LIST OF REF PerSwatchTable ← NIL;
		
		Alloc: PROC RETURNS [new: LIST OF REF PerSwatchTable] ~ BEGIN
			IF avail = NIL THEN new ← LIST[NEW[PerSwatchTable]]
			ELSE {
				new ← avail;
				avail ← avail.rest;
				new.rest ← NIL;
				};
			new.first↑ ← ALL[[0, CARDINAL.LAST/2, CARDINAL.LAST]];
			END;
		
		ComputeFixedIndex: PROC ~ BEGIN
			nonFixedBitCount ← 0;
			FOR s: [(-1)..2) IN [(-1)..2) DO
				FOR f: [0..1) IN [0..1) DO
					IF fixed[s][f] = 0 THEN {
						nonFixedBitIndex[nonFixedBitCount] ← [s,f];
						nonFixedBitCount ← nonFixedBitCount + 1;
						};
					ENDLOOP;
				ENDLOOP;
			END;
		
		Initialize: PROC ~ BEGIN
			FOR s: [(-1)..2) IN [(-1)..2) DO
				pixelRow.sOrigin ← sOrigin+s;
				Load[pixelRow, bitmap];
				FOR f: [(-2)..3) IN [(-2)..3) DO
					bit[s][f] ← pixelRow.sample[f+fOrigin-pixelRow.fOrigin];
					ENDLOOP;
				IF s+sOrigin NOT IN [swath.sMin..swath.sMin+swath.sSize)
				THEN fixed[s] ← ALL[1]
				ELSE {
					swathRow.sOrigin ← sOrigin+s;
					Load[swathRow, fixedBits];
					FOR f: [0..1) IN [0..1) DO
						fixed[s][f] ← swathRow.sample[f+fOrigin-swathRow.fOrigin];
						ENDLOOP;
					};
				ENDLOOP;
			pixelRow.sOrigin ← sOrigin;
			Load[pixelRow, gray];
			FOR f: INTEGER IN [(-1)..2) DO
				target[f] ← pixelRow.sample[f+fOrigin-pixelRow.fOrigin];
				ENDLOOP;
			ComputeOuterModelIndices[];
			ComputePartialTrial[];
			ComputeFixedIndex[];
			END;
		
		Advance: PROC ~ BEGIN
			sOrigin ← sOrigin + 1;
			FOR s: [(-1)..2) IN [(-1)..2-1) DO
				bit[s] ← bit[s+1];
				fixed[s] ← fixed[s+1];
				ENDLOOP;
			pixelRow.sOrigin ← sOrigin+2-1;
			Load[pixelRow, bitmap];
			FOR f: [(-2)..3) IN [(-2)..3) DO
				bit[2-1][f] ← pixelRow.sample[f+fOrigin-pixelRow.fOrigin];
				ENDLOOP;
			IF 2-1+sOrigin NOT IN [swath.sMin..swath.sMin+swath.sSize)
			THEN fixed[2-1] ← ALL[1]
			ELSE {
				swathRow.sOrigin ← sOrigin+2-1;
				Load[swathRow, fixedBits];
				FOR f: [0..1) IN [0..1) DO
					fixed[2-1][f] ← swathRow.sample[f+fOrigin-swathRow.fOrigin];
					ENDLOOP;
				};
			pixelRow.sOrigin ← sOrigin;
			Load[pixelRow, gray];
			FOR f: INTEGER IN [(-1)..2) DO
				target[f] ← pixelRow.sample[f+fOrigin-pixelRow.fOrigin];
				ENDLOOP;
			ComputeOuterModelIndices[];
			ComputePartialTrial[];
			ComputeFixedIndex[];
			END;
		
		ComputeOuterModelIndices: PROC ~ BEGIN
			
			END;
		
		ComputeInnerModelIndices: PROC ~ BEGIN
			index[0][(-1)] ← ((((((((bit[(-1)][(-2)]*2 + bit[(-1)][(-1)])*2 + bit[(-1)][0])*2 + bit[0][(-2)])*2 + bit[0][(-1)])*2 + bit[0][0])*2 + bit[1][(-2)])*2 + bit[1][(-1)])*2 + bit[1][0]);
			index[0][0] ← ((((((((bit[(-1)][(-1)]*2 + bit[(-1)][0])*2 + bit[(-1)][1])*2 + bit[0][(-1)])*2 + bit[0][0])*2 + bit[0][1])*2 + bit[1][(-1)])*2 + bit[1][0])*2 + bit[1][1]);
			index[0][1] ← ((((((((bit[(-1)][0]*2 + bit[(-1)][1])*2 + bit[(-1)][2])*2 + bit[0][0])*2 + bit[0][1])*2 + bit[0][2])*2 + bit[1][0])*2 + bit[1][1])*2 + bit[1][2]);
			
			END;
		
		ComputePartialTrial: PROC ~ BEGIN
			-- From the index values
			t: REF ModelDataItem ← NIL;
			trial ← ALL[0];
			 
			END;
		
		ComputeRowPenalty: PROC RETURNS [penalty: CARDINAL] ~ BEGIN
			-- From the index values
			t: REF ModelDataItem ← NIL;
			try: ARRAY [(-1)..2) OF INTEGER ← trial;
			t ← modelData[index[0][(-1)]];
			try[(-1)] ← try[(-1)] + t.a[0][0];
			t ← modelData[index[0][0]];
			try[0] ← try[0] + t.a[0][0];
			t ← modelData[index[0][1]];
			try[1] ← try[1] + t.a[0][0];
			penalty ← 0 + ABS[try[(-1)]-target[(-1)]] + modelData[index[0][(-1)]].noisePenalty + ABS[try[0]-target[0]] + modelData[index[0][0]].noisePenalty + ABS[try[1]-target[1]] + modelData[index[0][1]].noisePenalty;
			END;
		
		SetCombination: PROC [k: CARDINAL] ~ BEGIN
			-- k is a packed version of the non-fixed bits
			-- Sets questionBits and contextBits
			FOR i: NAT IN [0..nonFixedBitCount) DO
				sf: SF ← nonFixedBitIndex[i];
				bit[sf.s][sf.f] ← k MOD 2;
				k ← k/2;
				ENDLOOP;
			questionBits ← bit[(-1)][0];
			contextBits ← (bit[0][0]*2 + bit[1][0]);
			
			IF k # 0 THEN ERROR;
			prevContextBits ← contextBits/2 + 
			questionBits*(4/2);
			END;
		
		UnpackBits: PROC [questionBits, contextBits: CARDINAL] ~ BEGIN
			-- Unpacks the args into the swath portion of the bit array
			k: CARDINAL ← contextBits+questionBits*4;
			bit[1][0] ← k MOD 2;
			k ← k / 2;
			bit[0][0] ← k MOD 2;
			k ← k / 2;
			bit[(-1)][0] ← k MOD 2;
			k ← k / 2;
			
			END;
		
		DoDynamicProgramming: PROC ~ {
			-- If subproblemTable#NIL, subproblemTable.first gives the best ways to color the pixels up to sOrigin
			UNTIL sOrigin = swath.sMin+swath.sSize DO
				Initialize[];
				BEGIN
					nComb: NAT ← Basics.BITSHIFT[1, nonFixedBitCount];
					new: LIST OF REF PerSwatchTable ← Alloc[];
					pst: REF PerSwatchTable ← new.first;
					prevPST: REF PerSwatchTable ← NIL;
					IF subproblemTable # NIL THEN prevPST ← subproblemTable.first;
					FOR k: NAT IN [0..nComb) DO
						totBad: INT ← 0;
						SetCombination[k];
						ComputeInnerModelIndices[];
						IF prevPST # NIL THEN totBad ← MinErr[prevPST[prevContextBits]];
						totBad ← totBad+ComputeRowPenalty[];
						IF totBad < MinErr[pst[contextBits]] THEN {
							pst[contextBits] ← MakeSubproblemSolution[questionBits, totBad];
							};
						ENDLOOP;
					new.rest ← subproblemTable;
					subproblemTable ← new;
					END;
				sOrigin ← sOrigin + 1;
				ENDLOOP;
			};
		
		Traceback: PROC ~ {
			lowestPenalty: INT ← MinErr[subproblemTable.first[0]];
			contextBits ← 0;
			-- First look through the most recent subproblem to find the best entry.
			FOR i: NAT IN (0..4) DO
				err: INT ← MinErr[subproblemTable.first[i]];
				IF err < lowestPenalty THEN {
					lowestPenalty ← err;
					contextBits ← i;
					};
				ENDLOOP;
			sOrigin ← swath.sMin+swath.sSize;
			FOR t: LIST OF REF PerSwatchTable ← subproblemTable, t.rest WHILE sOrigin-1 > swath.sMin DO
				questionBits ← t.first[contextBits].setting;
				sOrigin ← sOrigin - 1;
				UnpackBits[questionBits, contextBits];
				swathRow.sOrigin ← sOrigin;
				FOR j: NAT IN [0..swathRow.sample.jSize) DO
					swathRow.sample[j] ← bit[0][j];
					ENDLOOP;
				StorePixelRow[swathRow, bitmap];
				contextBits ← contextBits/2 + 
				questionBits*(4/2);
				ENDLOOP;
			};
		
		DoDynamicProgramming[];
		Traceback[];
		newScratch ← subproblemTable;
		 
		};
	
	
	
	DynamicBits.RegisterImpl[[
			sKernelRadius: 0,
			fKernelRadius: 0,
			sModelRadius: 1,
			fModelRadius: 1,
			swathSize: 1,
			CreatePrinterModel: CreatePrinterModel,
			DoWithPrinterModel: DoWithPrinterModel,
			Convolve: Convolve,
			ApplyModel: ApplyModel,
			TuneSwath: TuneSwath
			]];
	
	END.