Problem: Better things, cheaper. There is a fierce battle at the time sale held in some supermarkets today. "LL-do" here in Aizu is one such supermarket, and we are holding a slightly unusual time sale to compete with other chain stores. In a general time sale, multiple products are cheaper at the same time, but at LL-do, the time to start the time sale is set differently depending on the target product.

The Sakai family is one of the households that often use LL-do. Under the leadership of his wife, the Sakai family will hold a strategy meeting for the time sale to be held next Sunday, and analyze in what order the shopping will maximize the discount. .. The Sakai family, who are familiar with the inside of the store, drew a floor plan of the sales floor and succeeded in grasping where the desired items are, how much the discount is, and the time until they are sold out. The Sakai family was perfect so far, but no one could analyze it. So you got to know the Sakai family and decided to write a program. The simulation is performed assuming that one person moves.

There are 10 types of products, which are distinguished by the single-digit product number g. The time sale information includes the item number g, the discount amount d, the start time s of the time sale, and the sold-out time e.

The inside of the store is represented by a two-dimensional grid consisting of squares X in width and Y in height, and either a passage or a product shelf is assigned to each square. There is one type of product on a shelf, which is distinguished by item number g. You can buy any item, but you must not buy more than one item with the same item number. From the product shelves, you can pick up products in any direction as long as they are in the aisle squares that are adjacent to each other.

You can pick up items from the time when the time sale starts, but you cannot pick up items from the time when they are sold out. Also, the time starts from 0 when you enter the store.

You can move from the current square to the adjacent aisle squares on the top, bottom, left, and right, but you cannot move to the squares on the product shelves. You can't even go outside the store represented by the grid. One unit time elapses for each move. Also, do not consider the time to pick up the product.

Please create a program that outputs the maximum value of the total discount amount of the products that you could get by inputting the floor plan of the store, the initial position of the shopper and the time sale information of the product.



input

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


X Y
m1,1 m2,1 ... mX,1
m1,2 m2,2 ... mX,2
::
m1, Y m2, Y ... mX, Y
n
g1 d1 s1 e1
g2 d2 s2 e2
::
gn dn sn en


The first line gives the store sizes X, Y (3 ≤ X, Y ≤ 20). In the following Y row, the store information mi, j in the i-th column and j-th row is given with the following contents.
. (Period): Aisle square
Number: Product number
P: The initial position of the shopper


The following line is given the number of timesale information n (1 ≤ n ≤ 8). The next n lines are given the i-th time sale information gi di si ei (0 ≤ gi ≤ 9, 1 ≤ di ≤ 10000, 0 ≤ si, ei ≤ 100).

The number of datasets does not exceed 50.

output

For each data set, the maximum value of the total discount amount of the products that can be taken is output on one line.

Example

Input

6 5
1 1 . 0 0 4
1 . . . . .
. . 2 2 . .
. . 2 2 3 3
P . . . . .
5
0 50 5 10
1 20 0 10
2 10 5 15
3 150 3 5
4 100 8 9
0 0


Output

180

Solution (in JAVA):

Solution:
import static java.util.Arrays.*;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.StringTokenizer;

public class Main {

	static void tr(Object... os) {
		System.err.println(deepToString(os));
	}

	int R, C;
	int[][] table;

	int[] gain;
	int[] start;
	int[] end;

	int[] totalGain;

	int startR;
	int startC;

	int[][][] item;
	int[][][][] vis;

	int[] dr = {0, 1, 0, -1, 0};
	int[] dc = {1, 0, -1, 0, 0};

	void solve() {
		C = sc.nextInt();
		R = sc.nextInt();
		if (R == 0) return;
		table = new int[R][C];
		for (int i = 0; i < R; i++) {
			for (int j = 0; j < C; j++) {
				char ch = sc.next().charAt(0);
				if (Character.isDigit(ch)) {
					table[i][j] = (ch - '0');
				} else {
					table[i][j] = -1;
				}
				if (ch == 'P') {
					startR = i;
					startC = j;
				}
			}
		}
		int M = 8;
		int m = sc.nextInt();
		gain = new int[M];
		start = new int[M];
		end = new int[M];
		for (int i = 0; i < m; i++) {
			int id = sc.nextInt();
			gain[id] = sc.nextInt();
			start[id] = sc.nextInt();
			end[id] = sc.nextInt();
		}
		item = new int[R][C][101];
		for (int i = 0; i < R; i++)
			for (int j = 0; j < C; j++)
				if (table[i][j] != -1)
					for (int k = 0; k < 4; k++)
					{
						int ni = i + dr[k];
						int nj = j + dc[k];
						if (isin(ni, nj)) {
							int id = table[i][j];
							for (int t = start[id]; t < end[id]; t++)
								item[ni][nj][t] |= 1 << table[i][j];
						}
					}
		totalGain = new int[1 << M];
		for (int i = 0; i < M; i++) {
			totalGain[1 << i] = gain[i];
		}
		for (int i = 0; i < M; i++) {
			for (int s = 0; s < 1 << M; s++) {
				if ((s >> i & 1) == 1)
					totalGain[s] += totalGain[s ^ (1<<i)];
			}
		}
		vis = new int[R][C][1 << M][4];
		Queue<State> q = new ArrayDeque<State>();
		State init = new State(startR, startC, item[startR][startC][0], 0);
		q.add(init);
		init.vis(true);
		int ans = 0;
		while (!q.isEmpty()) {
			State s = q.poll();
			ans = Math.max(ans, totalGain[s.set]);
			if (s.time >= 100) continue;
			for (int k = 0; k < 5; k++) {
				int ni = s.r + dr[k];
				int nj = s.c + dc[k];
				if (isin(ni, nj) && table[ni][nj] == -1) {
					State next = new State(ni, nj, s.set | item[ni][nj][s.time+1], s.time + 1);
					if (!next.vis()) {
						next.vis(true);
						q.add(next);
					}
				}
			}
		}
		out.println(ans);
	}

	class State {
		int r, c, set, time;
		public State(int r, int c, int set, int time) {
			this.r = r;
			this.c = c;
			this.set = set;
			this.time = time;
		}
		boolean vis() {
			return (vis[r][c][set][time>>5] >> (time & 31)) == 1;
		}
		void vis(boolean b) {
			if (b) {
				vis[r][c][set][time>>5] |= 1 << (time & 31);
			}
		}
	}

	boolean isin(int r, int c) {
		return 0 <= r && r < R && 0 <= c && c < C;
	}

	public static void main(String[] args) throws Exception {
		new Main().run();
	}

	MyScanner sc = null;
	PrintWriter out = null;
	public void run() throws Exception {
		sc = new MyScanner(System.in);
		out = new PrintWriter(System.out);
		for (;sc.hasNext();) {
			solve();
			out.flush();
		}
		out.close();
	}

	class MyScanner {
		String line;
		BufferedReader reader;
		StringTokenizer tokenizer;

		public MyScanner(InputStream stream) {
			reader = new BufferedReader(new InputStreamReader(stream));
			tokenizer = null;
		}
		public void eat() {
			while (tokenizer == null || !tokenizer.hasMoreTokens()) {
				try {
					line = reader.readLine();
					if (line == null) {
						tokenizer = null;
						return;
					}
					tokenizer = new StringTokenizer(line);
				} catch (IOException e) {
					throw new RuntimeException(e);
				}
			}
		}
		public String next() {
			eat();
			return tokenizer.nextToken();
		}
		public String nextLine() {
			try {
				return reader.readLine();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
		public boolean hasNext() {
			eat();
			return (tokenizer != null && tokenizer.hasMoreElements());
		}
		public int nextInt() {
			return Integer.parseInt(next());
		}
		public long nextLong() {
			return Long.parseLong(next());
		}
		public double nextDouble() {
			return Double.parseDouble(next());
		}
		public int[] nextIntArray(int n) {
			int[] a = new int[n];
			for (int i = 0; i < n; i++) a[i] = nextInt();
			return a;
		}
	}
}