Problem: Yuta is addicted to the popular game "Beat Panel" at a nearby arcade. The game consists of a total of 16 panel-type buttons, 4x4, arranged in a grid as shown.

<image>


As shown in the figure, the buttons are arranged in the order of button 1, button 2,…, button 16 from the upper left to the lower right. In the game, you will hear a beat sound at regular intervals and a final sound at the end. Multiple buttons light up at the same time as the beat sounds. In some cases, even one does not shine. The player can press multiple buttons at the same time using the fingers of both hands from immediately after the beat sound to the next sound. It is also possible not to press anything. The game ends as soon as the end sound is heard.

Yuta has mastered c ways of pressing, and each time a beat sounds, he decides one of those pressing methods and presses the button. If the buttons you press are lit, those buttons will be lit and the number of buttons that have disappeared will be added to the player's score. Also, once a button is lit, the light will not go out until the button is pressed.

A program that outputs the maximum score that Yuta can obtain by inputting how to illuminate the button when the beat sound is played n times and how to press the button in c ways that Yuta has learned. Please create.



input

The input consists of multiple datasets. The end of the input is indicated by two lines of zeros. Each dataset is given in the following format.


n c
a1,1 a1,2 ... a1,16
a2,1 a2,2 ... a2,16
...
an, 1 an, 2 ... an, 16
b1,1 b1,2 ... b1,16
b2,1 b2,2 ... b2,16
...
bc,1 bc,2 ... bc,16


The first line consists of two integers separated by one space. n (1 ≤ n ≤ 30) indicates the number of beat sounds, and c (1 ≤ c ≤ 30) indicates the number of button presses that you have learned. The following n + c lines give how to illuminate the button and how to press the button. The input item in the line is separated by one blank. ak and i indicate how the button i shines when the kth beat sound is heard, and bj and i indicate whether the button i is pressed by pressing the jth button in the middle of c. As for the values ​​of ak and i, 0 means "does not shine" and 1 means "shines". For the values ​​of bj and i, 0 means "do not press" and 1 means "press".

The number of datasets does not exceed 20.

output

For each data set, the maximum score is output on one line.

Example

Input

2 2
0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1
1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0
0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1
1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0
2 2
0 0 1 0 1 1 1 1 0 0 1 0 0 0 1 0
0 1 0 1 0 1 1 1 0 1 0 1 0 0 0 0
0 0 1 0 1 0 0 0 0 0 1 0 0 0 1 0
0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0
5 3
0 0 0 0 0 1 1 0 0 1 1 0 0 0 0 0
1 0 0 1 0 1 1 0 0 1 1 0 1 0 0 1
0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0
0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0
0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 0
0 0 0 0 0 1 1 0 0 1 1 0 0 0 0 0
1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0
0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1
0 0


Output

8
7
16

Solution (in CPP):

Solution:
//全探索O(c^n)。以後の遷移が合同になるのは(今のボタンの組み合わせ,今までになったブザー音の回数)が一致するとき。そして、当然この時のスコアも最大のものになっている。
//よって、O(2^16nc)のDPなんだが、実装できるかなあ。
//スイッチのパターンを配列で記憶するとデータの保管が面倒くさい。そこで、Σ2^kの形で状態を保存する。すると、点灯はa | b,消灯は a & ~bでできそう。
//i回目のビート光る→i回目にcパターン押し方を試す→その後の結果をDP[i][65536]に保存(もちろん最大値を保存します)
//スコアの獲得 == (oned-pushedを2^iの形で表した時の項の数),前処理なしだとこの計算にO(16^2)かかってしまいTLEする。
//そこで、1、2^i(0 <= i <= 16)をメモ化する。2、消えたランプの組み合わせすべて(すなわち0 <= oned-pushed <= 65535)におけるスコアの獲得をあらかじめメモ化する。
//すると、O(16^2)のところをO(1)にできる。
#include<iostream>
using namespace std;

int ans[20],cor = 0;
int N,C,in[30],out[30];	//i番のボタンを押していたら2^(i-1)加える。(なお、底を状態数(2)より少なくすると、ボタンの点灯状態がただしく示せない)
int DP[31][65536];
int memo_pwd[16];		//memo_pwd[i] = 2^i;
int memo_GetScore[65536];	//GetScore[i] == oned-pushedがiのときのスコアの加点
int pwd(int tei,int sisuu){
	int ans = 1;
	for(int i = 0;i < sisuu;i++)
		ans *= tei;
	return ans;
}
int GetScore(int score){
	int ans = 0;
	for(int i = 0;i < 16;i++){
		if(score & memo_pwd[i])
			ans++;
	}
	return ans;
}
int main(){
	int i,j,k;
	int t;
	//メモを作る
	for(i = 0;i < 16;i++)
		memo_pwd[i] = pwd(2,i);
	for(i = 0;i < 65536;i++){
		memo_GetScore[i] = GetScore(i);
	}
	while(cin >> N >> C){
		if((N | C) == 0)
			break;
		for(i = 0;i < N;i++){
			in[i] = 0;
			for(j = 0;j < 16;j++){
				cin >> t;
				if(t)
					in[i] += memo_pwd[j];
			}
		}
		for(i = 0;i < C;i++){
			out[i] = 0;
			for(j = 0;j < 16;j++){
				cin >> t;
				if(t)
					out[i] += memo_pwd[j];
			}
		}
		//DP配列の値を初期値に戻す
		for(i = 0;i <= N;i++){
			for(j = 0;j < 65536;j++){
				DP[i][j] = -1;
			}
		}
		DP[0][0] = 0;
		//ここから探索
		int oned,pushed;
		for(i = 0;i < N;i++){
			for(j = 0;j < 65536;j++){
				if(DP[i][j] == -1)
					continue;
				//cout << "in = " << in[i] << endl;
				oned = (in[i] | j);
				//cout << "oned = " << oned << endl;
				for(k = 0;k < C;k++){
					int score;
					pushed = (oned & ~out[k]);
					//cout << "pushed = " << pushed << endl;
					score = memo_GetScore[oned-pushed];
					if(DP[i+1][pushed] < DP[i][j] + score)
						DP[i+1][pushed] = DP[i][j]+score;
				}
			}
		}
		for(i = 0;i < 65536;i++){
			if(ans[cor] < DP[N][i])
				ans[cor] = DP[N][i];
		}
		cor++;
	}
	for(i = 0;i < cor;i++){
		cout << ans[i] << endl;
	}
	return 0;
}