// Complexity (Estimated): n * m * log2(n)
#define PROBLEM "plant"
#ifndef EVIL_X8E0R2O9N8
#include <fstream>
std::ifstream Xin(PROBLEM ".in");
std::ofstream Xout(PROBLEM ".out");
#else
#define Xin cin
#define Xout cout
#endif
#include <iostream>
using namespace std;
#define downto -->
typedef signed long long s64; // Signed 64-bit integer.
constexpr s64 Modulo = 998244353;
bool IsHole[2000][2000];
namespace Lookup {
	int Lines, Cols;
	int LineSum[2000][2000], ColSum[2000][2000];

	// A segment tree that calculates the sum of the segment.
	class SegmentTree {
		typedef struct SEG {
			int Lpos;
			int Rpos;
			s64 Sum;
			SEG* Lseg, * Rseg, * Sseg; // Left, Right and Superior
			bool IsLeaf;

			// We assume that _Lseg and _Rseg is non-null here.
			SEG(int _Lpos, int _Rpos, SEG* _Lseg, SEG* _Rseg) {
				Lpos = _Lpos;
				Rpos = _Rpos;
				Lseg = _Lseg;
				Rseg = _Rseg;
				Sseg = nullptr;
				Lseg->Sseg = Rseg->Sseg = this;
				IsLeaf = false;
				Update();
			}

			// Initialize a leaf node.
			SEG(int pos) {
				Lpos = Rpos = pos;
				Lseg = Rseg = Sseg = nullptr;
				IsLeaf = true;
			}

			inline void Update() {
				if (!IsLeaf) { Sum = Lseg->Sum + Rseg->Sum; }
			}
		} * PSEG;
		PSEG root;
		PSEG* Leaves;
		PSEG _Build(int l, int r) {
			if (l == r) { return (Leaves[l] = new SEG(l)); }
			int mid = (l + r) / 2;
			return new SEG(l, r, 
				_Build(l, mid),
				_Build(mid + 1, r)
			);
		}
		int Ql, Qr;
		s64 _Sum(PSEG current) {
			if (current == nullptr || current->Lpos > Qr || current->Rpos < Ql) { return 0; }
			if (current->Lpos >= Ql && current->Rpos <= Qr) { return current->Sum; }
			return _Sum(current->Lseg) + _Sum(current->Rseg);
		}
		void _Update(PSEG current) {
			if (current != nullptr && !current->IsLeaf) {
				_Update(current->Lseg);
				_Update(current->Rseg);
				current->Update();
			}
		}
	public:
		SegmentTree() { }

		// Does allocation.
		void Initialize(int size) {
			Leaves = new PSEG[size];
			root = _Build(0, size - 1); 
		}

		// Modifies the value at the specified position. (No update)
		void Modify(int pos, s64 value) {
			PSEG current = Leaves[pos];
			current->Sum = value;
			// while ((current = current->Sseg) != nullptr) { current->Update(); }
		}

		// Updates all the segments.
		void Update() { _Update(root); }

		// Gets the sum of the segment.
		s64 Sum(int l, int r) {
			Ql = l;
			Qr = r;
			return _Sum(root);
		}
	};
	SegmentTree CoLookup[1000];

	void Initialize() {
		for (int i = 0; i < 1000; i++) { CoLookup[i].Initialize(1000); }
	}

	void Update(int x, int y) {
		Lines = x;
		Cols = y;
		for (int line = 0; line < Lines; line++) {
			for (int col = Cols; col >= 0; col--) {
				if (col == Cols || IsHole[line][col]) { LineSum[line][col] = -1; }
				else { LineSum[line][col] = LineSum[line][col + 1] + 1; }
			}
		}
		for (int line = Lines; line >= 0; line--) {
			for (int col = 0; col < Cols; col++) {
				if (line == Lines || IsHole[line][col]) { ColSum[line][col] = -1; }
				else { ColSum[line][col] = ColSum[line + 1][col] + 1; }
			}
		}
	}
}
namespace C {
	using namespace Lookup;
	s64 Calculate(int x, int y) {
		for (int line = 0; line < Lines; line++) {
			for (int col = 0; col < Cols; col++) { CoLookup[col].Modify(line, LineSum[line][col]); }
		}
		for (int col = 0; col < Cols; col++) { CoLookup[col].Update(); }
		s64 result = 0;
		for (int line = 0; line < Lines - 2; line++) {
			for (int col = 0; col < Cols; col++) {
				if (ColSum[line][col] < 2) { continue; }
				s64 x = (LineSum[line][col] * CoLookup[col].Sum(line + 2, line + ColSum[line][col])) % Modulo;
				result += x;
			}
		}
		return result % Modulo;
	}
};
namespace F {
	using namespace Lookup;
	s64 Calculate(int x, int y) {
		for (int line = 0; line < Lines; line++) {
			for (int col = 0; col < Cols; col++) { CoLookup[col].Modify(line, LineSum[line][col] * ColSum[line][col]); }
		}
		for (int col = 0; col < Cols; col++) { CoLookup[col].Update(); }
		s64 result = 0;
		for (int line = 0; line < Lines - 2; line++) {
			for (int col = 0; col < Cols; col++) {
				if (ColSum[line][col] < 2) { continue; }
				s64 x = (LineSum[line][col] * CoLookup[col].Sum(line + 2, line + ColSum[line][col])) % Modulo;
				result += x;
			}
		}
		return result % Modulo;
	}
}
int main() {
	int T, id;
	Xin >> T >> id;
	Lookup::Initialize();
	while (T downto 0) {
		int n, m, c, f;
		Xin >> n >> m >> c >> f;
		for (int line = 0; line < n; line++) {
			for (int col = 0; col < m; col++) {
				char stat;
				Xin >> stat;
				IsHole[line][col] = (stat == '1');
			}
		}
		if (c != 0 || f != 0) {
			Lookup::Update(n, m);
			if (c == 1) { Xout << C::Calculate(n, m); }
			else { Xout << '0'; }
			Xout << ' ';
			if (f == 1) { Xout << F::Calculate(n, m); }
			else { Xout << '0'; }
		}
		else {
			Xout << '0' << ' ' << '0';
		}
		Xout << '\n';
	}
}

/* Judge
#!/bin/sh
g++ $1.cpp -o $1.elf -O2 -static -std=c++14;
if [ $? != 0 ]; then exit; fi
i=1;
while [ -f $1$i.in ]; do
	echo Case $i;
	cp $1$i.in $1.in;
	time -f "TIME   %E (User %Us + System %Ss)\nMEMORY %M KiB" ./$1.elf;
	diff -sw $1.out $1$i.ans;
	#rm $1.in $1.out;
	i=$((i + 1));
done;
 */