import java.util.*;
import java.util.stream.*;
import java.lang.*;
import java.io.*;
import static java.lang.Math.*;

class Util {
	public static IllegalArgumentException BadArgf(String fmt, Object... args) {
		return new IllegalArgumentException(String.format(fmt, args));
	};

	public static String trace(Exception e) {
		StringWriter sw = new StringWriter();
		e.printStackTrace(new PrintWriter(sw));
		return sw.toString();
	}

	public static int rangecheck(int x, int min, int max, String what) {
		if (x < min || x > max) throw BadArgf("Bad %s: %d, should be [%d; %d].", what, x, min, max);
		return x;
	}
}

class Bitfield2D {
	int[] data;
	int w, h;
	static final int BASE = Integer.SIZE;
	static final int SANE_BITS_LIMIT = 1_000_000_000;
	static final int SANE_DIMENSION_LIMIT = 100_000_000;

	public Bitfield2D(int w, int h) { init(null, w, h); }
	Bitfield2D(int[] data, int w, int h) { init(data, w, h); }

	private void init(int[] data, int w, int h) {
		validateSize(w, h);
		assert data == null || data.length >= (w*h + (BASE - 1)) / BASE;
		this.data = data != null ? data : new int[(w*h + (BASE - 1)) / BASE];
		this.w = w;
		this.h = h;
	}

	static void validateSize(int w, int h) {
		if (w < 0 || h < 0 || w > SANE_DIMENSION_LIMIT || h > SANE_DIMENSION_LIMIT
			|| w > 0 && h > SANE_BITS_LIMIT / w)
			throw Util.BadArgf("Bad bitfield size: %d×%d.", w, h);
	}

	public boolean validCoord(int x, int y) {
		return x >= 0 && y >= 0 && x < w && y < h;
	}

	public void validateCoord(int x, int y) {
		if (!validCoord(x, y))
			throw Util.BadArgf("Bad coord of bitfield %d×%d: (%d, %d).", this.w, this.h, x, y);
	}

	void validateRect(int x, int y, int w, int h) {
		if (x < 0 || y < 0 || w < 0 || h < 0 || x + w > this.w || y + h > this.h)
			throw Util.BadArgf("Bad rect of bitfield %d×%d: (%d, %d) ~ (%d, %d).",
				this.w, this.h, x, y, x+w, y+h);
	}

	boolean rawget(int index) { return (data[index / BASE] & (1 << (index % BASE))) != 0; }
	void rawset(int index) { data[index / BASE] |= 1 << (index % BASE); }
	void rawunset(int index) { data[index / BASE] &= ~(1 << (index % BASE)); }
	void rawset(int index, boolean value) { if (value) rawset(index); else rawunset(index); }

	public boolean get(int x, int y) { validateCoord(x, y); return rawget(y*w+x); }
	public void set(int x, int y) { validateCoord(x, y); rawset(y*w+x); }
	public void unset(int x, int y) { validateCoord(x, y); rawunset(y*w+x); }
	public void set(int x, int y, boolean value) { validateCoord(x, y); rawset(y*w+x, value); }
	public int width() { return w; }
	public int height() { return h; }

	// For functions like or() that apply one bitfield to another,
	// reduces potentially out-of-borders input to its valid part.
	static class CoordWarden {
		int srcX, srcY, w, h, destX, destY;
		CoordWarden(Bitfield2D dest, Bitfield2D src, int aSrcX, int aSrcY, int aW, int aH, int aDestX, int aDestY) {
			src.validateRect(aSrcX, aSrcY, aW, aH);
			srcX = aSrcX; srcY = aSrcY; w = aW; h = aH; destX = aDestX; destY = aDestY;
			if (destX < 0) { srcX -= destX; w += destX; destX = 0; }
			if (destX + w > dest.w) { w = dest.w - destX; }
			if (destY < 0) { srcY -= destY; h += destY; destY = 0; }
			if (destY + h > dest.h) { h = dest.h - destY; }
		}
	}

	public void or(Bitfield2D b, int destX, int destY) {
		or(b, 0, 0, b.w, b.h, destX, destY);
	}

	public void or(Bitfield2D b, int bx, int by, int w, int h, int destX, int destY) {
		var ward = new CoordWarden(this, b, bx, by, w, h, destX, destY);
		int thisOfs = ward.destY*this.w + ward.destX, bOfs = ward.srcY*b.w + ward.srcX;
		for (by = 0; by < ward.h; by++, thisOfs += this.w - ward.w, bOfs += b.w - ward.w) {
			for (bx = 0; bx < ward.w; bx++, thisOfs++, bOfs++)
				if (b.rawget(bOfs)) this.rawset(thisOfs);
		}
	}

	public void andNot(Bitfield2D b, int destX, int destY) {
		andNot(b, 0, 0, b.w, b.h, destX, destY);
	}

	public void andNot(Bitfield2D b, int bx, int by, int w, int h, int destX, int destY) {
		var ward = new CoordWarden(this, b, bx, by, w, h, destX, destY);
		int thisOfs = ward.destY*this.w + ward.destX, bOfs = ward.srcY*b.w + ward.srcX;
		for (by = 0; by < ward.h; by++, thisOfs += this.w - ward.w, bOfs += b.w - ward.w) {
			for (bx = 0; bx < ward.w; bx++, thisOfs++, bOfs++)
				if (b.rawget(bOfs)) this.rawunset(thisOfs);
		}
	}

	public Bitfield2D copyFrom(Bitfield2D b, int destX, int destY) {
		return copyFrom(b, 0, 0, b.w, b.h, destX, destY);
	}

	public Bitfield2D copyFrom(Bitfield2D b, int bx, int by, int w, int h, int destX, int destY) {
		var ward = new CoordWarden(this, b, bx, by, w, h, destX, destY);
		int thisOfs = ward.destY*this.w + ward.destX, bOfs = ward.srcY*b.w + ward.srcX;
		for (by = 0; by < ward.h; by++, thisOfs += this.w, bOfs += b.w) {
			copyBits(b.data, bOfs, this.data, thisOfs, ward.w);
		}
		return this;
	}

	static int readBitsSmall(int[] src, int srcPos, int count) {
		assert count > 0 && count < BASE;
		//       0       1       2       3       4       5       6
		// src = ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
		//            ^srcPos=5
		//                  ^srcPos+count=11
		int bits = src[srcPos / BASE] >>> (srcPos % BASE); // FGH
		if (count > BASE - srcPos % BASE)
			bits |= src[srcPos / BASE + 1] << (BASE - srcPos % BASE); // FGH | 000IJKLMNOP = FGHIJKLMNOP
		return bits & ((1 << count) - 1); // FGHIJK
	}

	static void writeBits1(int bits, int[] dst, int dstPos, int count) {
		assert count > 0 && count < BASE - dstPos % BASE && bits < 1 << count;
		// bits = qwer
		//       0       1       2       3       4       5       6
		// src = ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
		//          ^dstPos=3
		//              ^dstPos+count=7
		dst[dstPos / BASE] = dst[dstPos / BASE] // ABCDEFGH
			& ~(((1 << count) - 1) << (dstPos % BASE)) // ABC0000H
			| (bits << (dstPos % BASE)); // ABCqwerH
	}

	static void copyBits(int[] src, int srcPos, int[] dst, int dstPos, int count) {
		// Imagine BASE=8 (as if byte[] instead of int[]), srcPos = 3, dstPos = 2, count=25.
		//
		//       0       1       2       3       4       5       6
		// src = ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
		//          ^srcPos=3                ^srcPos+count=28
		// dst = ...............................................
		//       0 ^dstPos=2     2       3       4       5
		//
		// First we align dstPos to the multiple of BASE by reading
		// first BASE - dstPos%BASE bits with readBitsSmall() and writing them
		// into the first cell of dst[] with writeBits1.
		//
		// We'll get:
		//       0       1       2       3       4       5       6
		// src = ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
		//                ^srcPos=9          ^srcPos+count=28
		// dst = ..DEFGHI.......................................
		//       0       1       2       3       4       5
		//               ^dstPos=8
		if (dstPos % BASE != 0) {
			int n = min(count, BASE - dstPos % BASE);
			if (n > 0) writeBits1(readBitsSmall(src, srcPos, n), dst, dstPos, n);
			srcPos += n;
			dstPos += n;
			count -= n;
		}

		// Now we fill whole cells while possible.
		// We'll get:
		// src = ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
		//                                ^srcPos=25
		//                                   ^srcPos+count=28
		// dst = ..DEFGHIJKLMNOPQRSTUVWXY.......................
		//       0       1       2       3       4       5
		//                               ^dstPos=24
		if (srcPos % BASE == 0) {
			// Aligned case. This is not even an optimization, because 'int' shifts are restricted to 0..31.
			System.arraycopy(src, srcPos / BASE, dst, dstPos / BASE, count / BASE);
		} else {
			// Unaligned case.
			// src: ...ABCDE FGH.....
			//         ^srcPos
			// dst: ........
			for (int iSrc = srcPos / BASE, iDst = dstPos / BASE, iDstEd = iDst + count / BASE; iDst < iDstEd; iDst++, iSrc++)
				dst[iDst] =
					(src[iSrc] >>> (srcPos % BASE)) // ABCDE
					| (src[iSrc + 1] << (BASE - srcPos % BASE)); // ABCDE | 00000FGH = ABCDEFGH
		}
		srcPos += count - count % BASE;
		dstPos += count - count % BASE;
		count %= BASE;

		// Append the tail.
		if (count > 0)
			writeBits1(readBitsSmall(src, srcPos, count), dst, dstPos, count);
	}

	@Override
	public String toString() {
		return toString("##", "..");
	}

	public String toString(String one, String zero) {
		var sb = new StringBuilder();
		for (int ofs = 0, y = 0; y < h; y++) {
			if (y > 0) sb.append("\n");
			for (int x = 0; x < w; x++, ofs++)
				sb.append(rawget(ofs) ? one : zero);
		}
		return sb.toString();
	}

	static Bitfield2D parse(String src) { return parse(src, "##"); }

	static Bitfield2D parse(String src, String one) {
		if (one.isEmpty()) throw Util.BadArgf("'one' can't be empty.");
		if (one.indexOf('\n') >= 0) throw Util.BadArgf("Bad 'one': \"%s\". Shouldn't contain EOL.", one);
		// Prepass for size.
		int w = 0, h = 0;
		for (int pos = 0; pos < src.length(); ) {
			int eolp = src.indexOf("\n", pos);
			if (eolp < 0) eolp = src.length();
			if ((eolp - pos) % one.length() != 0)
				throw Util.BadArgf("Line %d:\n%s\ncontains %d character%s, which is not multiple of len(%s) = %d.",
					h, src.substring(pos, eolp), eolp - pos, eolp - pos != 1 ? "s" : "", one, one.length());
			w = max(w, (eolp - pos) / one.length());
			h++;
			pos = eolp + "\n".length();
		}
		var r = new Bitfield2D(w, h);

		for (int y = 0, x = 0, ofs = 0, pos = 0; pos < src.length(); )
			if (src.charAt(pos) == '\n') {
				ofs += r.w - x;
				x = 0; y++;
				pos += "\n".length();
			} else {
				if (src.startsWith(one, pos)) r.rawset(ofs);
				pos += one.length();
				x++; ofs++;
			}
		return r;
	}
}

class Ideone
{
	// Auto-growing bitfield for procedural generation when resulting size is impractical to predict.
	static class ProcBitfield2D {
		Bitfield2D b = new Bitfield2D(0, 0);
		int ax, ay, bx, by, boundAx, boundAy, boundBx, boundBy;

		void set(int x, int y) {
			if (boundAx == boundBx) {
				ax = x; ay = y; bx = x; by = y;
				boundAx = x; boundAy = y; boundBx = x; boundBy = y;
			}
			if (x < ax || y < ay || x >= bx || y >= by)
				grow(
					x < ax ? growStgy(ax - x, bx - ax) : 0,
					y < ay ? growStgy(ay - y, by - ay) : 0,
					x >= bx ? growStgy(x - bx + 1, bx - ax) : 0,
					y >= by ? growStgy(y - by + 1, by - ay) : 0);
			if (x < boundAx) boundAx = x;
			if (x >= boundBx) boundBx = x + 1;
			if (y < boundAy) boundAy = y;
			if (y >= boundBy) boundBy = y + 1;
			b.set(x-ax, y-ay);
		}

		void unset(int x, int y) {
			if (x >= boundAx && x < boundBx && x >= boundAy && x < boundBy)
				b.unset(x-ax, y-ay);
		}

		void set(int x, int y, boolean value) { if (value) set(x, y); else unset(x, y); }

		static int growStgy(int atLeast, int present) {
			return max(max(8, present / 2), atLeast);
		}

		void grow(int Lx, int Ly, int Rx, int Ry) {
			this.b = new Bitfield2D((bx - ax) + Lx + Rx, (by - ay) + Ly + Ry).copyFrom(
				this.b, boundAx - ax, boundAy - ay, boundBx - boundAx, boundBy - boundAy, (boundAx - ax) + Lx, (boundAy - ay) + Ly);
			ax -= Lx; ay -= Ly; bx += Rx; by += Ry;
		}

		Bitfield2D bake() {
			return new Bitfield2D(boundBx - boundAx, boundBy - boundAy).copyFrom(
				this.b, boundAx - ax, boundAy - ay, boundBx - boundAx, boundBy - boundAy, 0, 0);
		}
	}

	static Bitfield2D makeSpruce(int size) {
		Util.rangecheck(size, 1, 20, "size");
		var r = new ProcBitfield2D();
		int nSegs = 3+size/4, trunkWidth = 1+size/3*2;
		int y = 0;
		for (int iSeg = 0; iSeg < nSegs; iSeg++) {
			int maxSegWidth = trunkWidth + 2 * (size + iSeg * size / 2);
			int segWidth = iSeg == 0 ? 1 : trunkWidth + (size + iSeg)/3*2;
			for (; segWidth < maxSegWidth; segWidth += 2) {
				for (int x = -segWidth/2; x < (segWidth+1)/2; x++)
					r.set(x, y);
				y += 1;
			}
		}
		for (int yTrunk = 0; yTrunk < size; yTrunk++) {
			for (int x = -trunkWidth/2; x < (trunkWidth + 1)/2; x++)
				r.set(x, y);
			y++;
		}
		return r.bake();
	}

	public static void main (String[] args) throws java.lang.Exception
	{
		try {
			var r = new Bitfield2D(50, 40);
			var moon = Bitfield2D.parse(
				"....######..........\n" +
				"..####..............\n" +
				"####................\n" +
				"####................\n" +
				"####................\n" +
				"####..............##\n" +
				"######..........####\n" +
				"..################..\n" +
				"....############", "##");
			r.or(moon, 11, 4);
			r.or(makeSpruce(4), -1, 15);
			r.or(makeSpruce(3), 29, 17);
			r.or(makeSpruce(2), 18, 18);
			r.or(makeSpruce(5), 32, 16);
			var rand = new Random(7);
			for (int iSnowflake = 0; iSnowflake < 25; iSnowflake++) {
				int x = rand.nextInt(r.width()),
					y = min(r.height() - 1, (int) (r.height() * pow(rand.nextFloat(), 2.0)));
				r.set(x, y, !r.get(x, y));
			}
			System.out.printf("%s\n\n", r);
		} catch (Exception e) {
			System.out.println(Util.trace(e));
		}
	}
}