program StarryNight;
{ by Artur Miguel Dias / 1998 }
{ maxSize=100*100, maxClusters=500, maxShapes=26, maxClusterSize=100 }

const
	maxMap = 100;
	maxMap1 = maxMap - 1;
	maxShapes = 26;
	maxShapes1 = maxShapes - 1;

type
	Pt = record
			x, y: integer
		end;
	StartPts = array[0..7] of Pt;
	Shape = record
			name: char;	{identification of the shape}
			start: StartPts; {start points for cluster comparison}
			nStars: integer; {number of stars in this shape}
			num: integer; {number of clusters with this shape (for debugging)}
		end;

var
	map: array[0..maxMap1, 0..maxMap1] of char; {the Map}
	size: Pt; {the Map's size}
	minP, maxP: Pt; {info on the current cluster}
	nStars: integer; {info on the current cluster}
	shapes: array[0..maxShapes1] of Shape;
{arrows: the neighbourhood of each point in relative coordinates arrows}
	arrows: array[0..7] of Pt;

procedure InitGlobals;
begin
	arrows[0].x := 1;
	arrows[0].y := 0;
	arrows[1].x := 1;
	arrows[1].y := 1;
	arrows[2].x := 0;
	arrows[2].y := 1;
	arrows[3].x := -1;
	arrows[3].y := 1;
	arrows[4].x := -1;
	arrows[4].y := 0;
	arrows[5].x := -1;
	arrows[5].y := -1;
	arrows[6].x := 0;
	arrows[6].y := -1;
	arrows[7].x := 1;
	arrows[7].y := -1
end;

procedure st (p: Pt; n: char);
begin
	map[p.y, p.x] := n
end;

function gt (p: Pt): char;
begin
	if p.x < 0 then
		gt := '1'
	else if p.x >= size.x then
		gt := '0'
	else if p.y < 0 then
		gt := '0'
	else if p.y >= size.y then
		gt := '0'
	else
		gt := map[p.y][p.x]
end;

{Walks one unit in the direction "arrow" using the co-ordinate}
{system represented by "rotSym". The values of "rotSym" range from 0 to 7}
{and have the following meanings: }
{       0=0 1=90 1=180 3=270 4=0s 5=90s 6=180s 7=270s}
{For example 7 represents a 270 rotation followed by a symetry}
procedure Walk (p: Pt; arrow: integer; rotSym: integer; var res: Pt);
begin
	if arrow = -1 then
		res := p
	else
		begin
			arrow := (arrow + 2 * (rotSym mod 4)) mod 8;
			if rotSym div 4 = 0 then
				begin
					res.x := p.x + arrows[arrow].x;
					res.y := p.y + arrows[arrow].y
				end
			else
				begin
					res.x := p.x + arrows[arrow].y;
					res.y := p.y + arrows[arrow].x
				end;
		end
end;

procedure LoadMap (fname: string);
	var
		f: text;
		w, h, i, j: integer;
		p: Pt;
		c: char;
begin
	assign(f, fname);
	reset(f);
	readln(f, size.x);
	readln(f, size.y);
	for i := 0 to size.y - 1 do
		begin
			for j := 0 to size.x - 1 do
				begin
					p.x := j;
					p.y := i;
					read(f, c);
					st(p, c);
				end;
			readln(f)
		end;
	close(f)
end;

procedure WriteMap (fname: string);
	var
		f: text;
		i, j: integer;
		p: Pt;
begin
	assign(f, fname);
	rewrite(f);
	for i := 0 to size.y - 1 do
		begin
			for j := 0 to size.x - 1 do
				begin
					p.x := j;
					p.y := i;
					write(f, gt(p))
				end;
			writeln(f)
		end;
	close(f)
end;

{Determinates the enclosing rectangle of the current cluster}
procedure UpdateMinMax (p: Pt);
begin
	nStars := succ(nStars);
	if p.x < minP.x then
		minP.x := p.x;
	if p.x > maxP.x then
		maxP.x := p.x;
	if p.y < minP.y then
		minP.y := p.y;
	if p.y > maxP.y then
		maxP.y := p.y
end;

{Fills a cluster with the char "name" (for the first time)}
procedure FirstFill (p: Pt; arrow: integer; name: char);
	var
		a: integer;
		np: Pt;
begin
	Walk(p, arrow, 0, np);
	if (gt(np) <> '0') and (gt(np) <> name) then
		begin
			st(np, name);
			UpdateMinMax(np);
			for a := 0 to 7 do
				FirstFill(np, a, name);
		end
end;

{Fills a cluster with the char "name"}
procedure Fill (p: Pt; arrow: integer; name: char);
	var
		a: integer;
		np: Pt;
begin
	Walk(p, arrow, 0, np);
	if (gt(np) <> '0') and (gt(np) <> name) then
		begin
			st(np, name);
			for a := 0 to 7 do
				Fill(np, a, name);
		end
end;

{Determines the start point where the comparison of the current cluster must begin for}
{ the orientation "rotSym" } 
procedure StartPt (rotSym: integer; name: char; var res: Pt);
	var
		p: Pt;
begin
	Walk(minP, 1, rotSym, p);
	res := minP;
	if p.x < minP.x then
		res.x := maxP.x;
	if p.y < minP.y then
		res.y := maxP.y;
	while gt(res) <> name do
		Walk(res, 0, rotSym, res);
end;

{Compares two clusters according to the orientation "rotSym"}
function CompareX (p, q: Pt; arrow: integer; rotSym: integer): boolean;
	label
		99;
	var
		a: integer;
		np, nq: Pt;
begin
	Walk(p, arrow, 0, np);
	Walk(q, arrow, rotSym, nq);
	if gt(np) = '0' then
		begin
			CompareX := gt(nq) = '0';
			goto 99
		end;
	if gt(np) = '1' then
		begin
			CompareX := true;
			goto 99
		end;
	st(np, '1');
	if gt(nq) = '0' then
		begin
			CompareX := false;
			goto 99
		end;
	for a := 0 to 7 do
		if not CompareX(np, nq, a, rotSym) then
			begin
				CompareX := false;
				goto 99
			end;
	CompareX := true;
99:
end;

{Compares the cluster starting at "p" with the cluster with starting points "start"}
function Compare (p: Pt; var start: StartPts; old: char): boolean;
	label
		99;
	var
		rotSym: integer;
begin
	for rotSym := 0 to 7 do
		begin
			if CompareX(p, start[rotSym], -1, rotSym) then
				begin
					Compare := true;
					goto 99
				end
			else
				Fill(p, -1, old)
		end;
	Compare := false;
99:
end;

{Scans the map, finds the clusters, paints them}
procedure ProcessMap;
	label
		77, 99;
	var
		p: Pt;
		k, rotSym, nShapes: integer;
		i, j: integer;
		name: char;
begin
	nShapes := 0;
	name := 'a';
	for j := 0 to size.y - 1 do
		for i := 0 to size.x - 1 do
			begin
				p.x := i;
				p.y := j;
				if gt(p) = '1' then
					begin
						minP := p;
						maxP := p;
						nStars := 0;
						FirstFill(p, -1, name);
						for k := 0 to nShapes - 1 do
							if shapes[k].nStars = nStars then
								if Compare(p, shapes[k].start, name) then
								   begin
								{Same shape}
									shapes[k].num := succ(shapes[k].num);
									Fill(p, -1, shapes[k].name);
									goto 77;
								   end;
					{New shape}
						if nShapes = maxShapes then
							begin
								write('Too many shapes');
								goto 99
							end;
						shapes[nShapes].name := name;
						shapes[nShapes].nStars := nStars;
						shapes[nShapes].num := 1;
						for rotSym := 0 to 7 do
						   StartPt(rotSym, name, shapes[nShapes].start[rotSym]);
						nShapes := succ(nShapes);
						name := succ(name);
					end;
77:
			end;
99:
{    for k := 0 to nShapes - 1 do}
{    writeln(shapes[k].name, ' = ', shapes[k].num)}
end;

begin
	InitGlobals;
	LoadMap('STARRY.IN');
	ProcessMap;
	WriteMap('STARRY.OUT');
end.
