Problem:
写真に写っている景色は?

A 君は会津に観光にやってきました。宿泊したホテルの窓からは会津盆地が一望できます。景色を眺めていると、床に写真の切れ端が落ちているのに気がつきました。どうやら窓から外の景色を撮ったようです。「どの辺りを撮ったのかな」と、A 君は窓の外の景色と写真が同じ大きさになるように写真をかざして、写真と窓の外の景色が一致する場所を探し始めました。

それでは、A 君がやっていることをコンピュータでやってみましょう。窓の外の景色を n × n マスの正方形に分割します (n を景色の大きさと呼びます)。各マスには、そのマスの画像の情報を表す 0 以上の整数が書かれています。マスの位置は座標 (x, y) で表します。各マスの座標は以下の通りになります。

写真の切れ端は、m × m マスの正方形で表されます (m を写真の切れ端の大きさと呼びます) 。この正方形の中で、切れ端の部分のマスには画像の情報が、切れ端に含まれない部分のマスには -1 が書かれています。たとえば、下図では、色が塗られている部分が実際の写真の切れ端の形を表しています。切れ端に穴が開いていることはありますが、必ず 1 つにつながっています。 (0 以上の値をもつマスが頂点だけで接しているときは、つながっていないものとみなします。) また、値 -1 のマスが縦または横 1 列に端から端まで並ぶことはありません。

窓から見える景色の中で、写真の切れ端を 0、90、180、270 度回転させたいずれかと一致する領域を探します。たとえば、景色が下図のように表わせるとき、上図の切れ端を反時計回りに90 度回転させると、色が塗られた領域に一致します。

窓から見える景色と写真の切れ端の情報を入力とし、景色の中で、切れ端を 0、90、180、270 度回転させたいずれかと一致する領域の最も上端に近いマスのうち、最も左端に近いマスの座標を出力するプログラムを作成してください。

一致する領域が複数ある場合は、それらの領域内にあるマスの中で、最も上端に近いマスのうち、最も左端に近いマスの座標を出力してください。一致する領域がないときは、NA と出力します。

Input

複数のデータセットの並びが入力として与えられます。入力の終わりはゼロふたつの行で示されます。各データセットは以下の形式で与えられます。

n m
w11 w12 ... w1n
w21 w22 ... w2n
:
wn1 wn2 ... wnn
p11 p12 ... p1m
p21 p22 ... p2m
:
pm1 pm2 ... pmm

1 行目にn, m (1 ≤ n ≤ 100, 1 ≤ m ≤ 50, m ≤ n) が与えられます。

続く n 行に窓から見える景色の情報 wij (0 ≤ wij ≤ 15)、続く m 行に写真の切れ端のデータの情報 pij (-1 ≤ pij ≤ 15) が与えられます。

データセットの数は 100 を超えません。

Output

入力データセットごとに、マスの位置の座標または NA を１行に出力します。

Sample Input

8 4
2 1 3 1 1 5 1 3
2 3 2 4 1 0 2 1
0 3 1 2 1 1 4 2
1 2 3 2 1 1 5 4
0 2 0 1 1 3 2 1
1 3 1 2 2 4 3 2
5 1 2 1 4 1 1 5
4 1 1 0 1 2 2 1
2 -1 -1 -1
0 3 -1 -1
-1 2 2 4
-1 1 -1 1
5 3
1 0 2 3 5
2 3 7 2 1
2 5 4 2 2
8 9 0 3 3
3 6 0 4 7
-1 -1 2
-1 3 5
0 4 -1
0 0

Output for the Sample Input

4 2
NA

Solution in C# (judge verdict: Accepted)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace _0209
{
    public class Program

    {

        class Point
        {
            public int x, y;
            public Point(int x, int y) { this.x = x; this.y = y; }
        }


        public static int[,] map;
        public static int[][,] pieces;

        public static void Main(string[] args)
        {
            while (true)
            {
                int[] nm = RIntAr(); if (nm.Sum() == 0) break;
                Init(nm);
                Console.WriteLine(SearchMatchPoint());
            }
        }

        private static string SearchMatchPoint()
        {
            for (int i = 0 ; i < map.GetLength(0) ; i++)
            {
                for (int j = 0 ; j < map.GetLength(1) ; j++)
                {
                    Point p = IsMatch(i, j);
                    if (p != null) return (p.x + 1).ToString() + " " + (p.y + 1).ToString();
                }
            }
            return "NA";
        }

        private static Point IsMatch(int si, int sj)
        {
            for (int k = 0 ; k < pieces.Length ; k++)
            {
                Point p = IsMatchP(si, sj, pieces[k]);
                if (p != null) return p;
            }
            return null;
        }

        private static Point IsMatchP(int si, int sj, int[,] piece)
        {

            if (si > map.GetLength(0) - piece.GetLength(0) || sj > map.GetLength(1) - piece.GetLength(1)) return null;

            Point p = null;
            for (int i = 0 ; i < piece.GetLength(0) ; i++)
            {
                for (int j = 0 ; j < piece.GetLength(1) ; j++)
                {
                    if (piece[i, j] != -1)
                    {
                        if (p == null) p = new Point(j + sj, i + si);
                        if (piece[i, j] != map[i + si, j + sj]) return null;
                    }                    
                }
            }
            return p;
        }

        private static void Init(int[] nm)
        {
            map = new int[nm[0], nm[0]];
            for (int i = 0 ; i < map.GetLength(0) ; i++)
            {
                int[] items = RIntAr();
                for (int j = 0 ; j < map.GetLength(1) ; j++)
                {
                    map[i, j] = items[j];
                }
            }

            pieces = new int[4][,];

            pieces[0] = new int[nm[1], nm[1]];
            for (int i = 0 ; i < pieces[0].GetLength(0) ; i++)
            {
                int[] items = RIntAr();
                for (int j = 0 ; j < pieces[0].GetLength(1) ; j++)
                {
                    pieces[0][i, j] = items[j];
                }
            }

            for (int k = 1 ; k < pieces.Length ; k++)
            {

                int h = pieces[k - 1].GetLength(1);
                int w = pieces[k - 1].GetLength(0);

                pieces[k] = new int[h, w];
                for (int i = 0 ; i < h ; i++)
                {
                    for (int j = 0 ; j < w ; j++)
                    {
                        pieces[k][i, j] = pieces[k - 1][w - 1 - j, i];
                    }
                }
            }

        }

        static string RSt() { return Console.ReadLine(); }
        static int RInt() { return int.Parse(Console.ReadLine().Trim()); }
        static long RLong() { return long.Parse(Console.ReadLine().Trim()); }
        static double RDouble() { return double.Parse(Console.ReadLine()); }
        static string[] RStAr(char sep = ' ') { return Console.ReadLine().Trim().Split(sep); }
        static int[] RIntAr(char sep = ' ') { return Array.ConvertAll(Console.ReadLine().Trim().Split(sep), e => int.Parse(e)); }
        static long[] RLongAr(char sep = ' ') { return Array.ConvertAll(Console.ReadLine().Trim().Split(sep), e => long.Parse(e)); }
        static double[] RDoubleAr(char sep = ' ') { return Array.ConvertAll(Console.ReadLine().Trim().Split(sep), e => double.Parse(e)); }
        static string WAr<T>(IEnumerable<T> array, string sep = " ") { return string.Join(sep, array.Select(x => x.ToString()).ToArray()); }
    }

}