/*
//多回路问题,升级版为单回路(因为要考虑连通分量的合并),可以看做例题
#include<iostream>
#include<cstring>
using namespace std;
using LL=long long;
int n,m;
LL dp[2][1<<13];
int main(){
cin.tie(nullptr)->sync_with_stdio(false);
int T;
cin>>T;
while(T--){
memset(dp,0,sizeof dp);
cin>>n>>m;
int pre=0,now=1;//滚动数组优化
dp[pre][0]=1;//边界条件,显然这时肯定构成回路,并且显然不能有插头
for(int i=0;i<n;i++){//有的是正序,有的是倒序,有的两种皆可,不清楚
for(int j=0;j<m;j++){
for(int S=0;S<1<<(m+1);S++){
//<2^(m+1)? 因为有m个上(下)插头,1个左插头
int tmp;
cin>>tmp;
//注意!!!!!!!!!(本人感觉这是插头DP的重点)每次考虑当前方格下方和右方的插头,看从左方和上方的什么状态转移过来
int sl=S&(1<<(j+1)),su=S&(1<<j);//左边(列从右到左数)和上方的状态
if(!tmp){//存在障碍物,不能有任何插头
if(!sl && !su)
dp[now][S]+=dp[pre][S];
}
else{
if(sl^su){//左、上只有一个插头,那么右、下有一个即可
dp[now][S]+=dp[pre][S];//原状态不变(即不留插头)
dp[now][S^(1<<j)^(1<<(j+1))]+=dp[pre][S];//原状态改变,即留下插头
}
else{//即都有、都没有插头,那么只能改变原状态,否则不合法(可以画一下图)
dp[now][S^(1<<j)^(1<<(j+1))]+=dp[pre][S];
}
}
}
swap(pre,now);//处理完一个方格就交换
}
memset(dp[now],0,sizeof dp[now]);
for(int S=0;S<(1<<(m+1))>>1;S++) dp[now][S<<1]=dp[pre][S];
// 在两行交界处,左插头由最右边到最左边(最高位到最低位,除最后一位变为0其余状态不变),而最左边应为空,因此状态整体左移一位
swap(pre,now);
}
cout<<dp[now][0]<<'\n';
}
}
*/
#include<bits/stdc++.h>
using namespace std;
const int max_s = 1 << 12 | 5;
long long f[2][max_s],*f0,*f1; // 指针 f0,f1:表示当前从 f0 转移到 f1
int main() {
int T;
scanf("%d", &T);
while (T--) {
int n, m;
scanf("%d%d", &n, &m);
f0 = f[0], f1 = f[1];
int lim = 1 << m + 1; // m 个上插头,1 个左插头
fill(f1, f1 + lim, 0); // 初始时记得清空
f1[0] = 1; // 边界不能有插头
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) { // 为方便状压,下标从 0 开始
swap(f0, f1); // 上一次的计算结果存储于 f1 中,转移到 f0 中
fill(f1, f1 + lim, 0); // 并清空 f1
int mark;
scanf("%d", &mark);
// s 第 j 位由 f0 的 (i,j) 左边界到 f1 的 (i,j) 下边界
// s 第 j+1 位由 f0 的 (i,j) 上边界到 f1 的 (i,j) 右边界
for (int s = 0; s < lim; ++s) {
long long v = f0[s];
if (v) {
int l = s >> j & 1, u = s >> j + 1 & 1;
if (mark) {
// 要形成回路,每个格子应恰有 2 个插头
if (l == u) // 若左、上都有插头或都没有插头,则下、右都没有插头或都有插头
f1[s ^ 3 << j] += v; // s^3<<j 相当于 s^1<<j^1<<j+1
else // 若左、上恰有 1 个插头,则剩余插头可以向右,也可以向下
f1[s ^ 3 << j] += v, f1[s] += v; // 即可以都取反,也可以都不取反
} else {
if (!l && !u)
f1[s] += v; // 若有障碍物,则不能有任何一个插头
}
}
}
}
swap(f0, f1);
fill(f1, f1 + lim, 0);
for (int s = 0; s < (1 << m); ++s)
f1[s << 1] = f0[s];
// 在两行交界处,左插头由最右边到最左边(最高位到最低位),而最左边应为空,因此状态整体左移一位
}
printf("%lld\n", f1[0]); // 边界不能有插头
}
return 0;
}