Problem: Ordinary Jigsaw puzzles are solved with visual hints; players solve a puzzle with the picture which the puzzle shows on finish, and the diverse patterns of pieces. Such Jigsaw puzzles may be suitable for human players, because they require abilities of pattern recognition and imagination.

On the other hand, "Jigsaw puzzles" described below may be just the things for simple-minded computers.

As shown in Figure 1, a puzzle is composed of nine square pieces, and each of the four edges of a piece is labeled with one of the following eight symbols:

"R", "G", "B", "W", "r", "g", "b", and "w".


<image>
---
Figure 1: The nine pieces of a puzzle

In a completed puzzle, the nine pieces are arranged in a 3 x 3 grid, and each of the 12 pairs of edges facing each other must be labeled with one of the following four combinations of symbols:

"R" with "r", "G" with "g", "B" with "b", and "W" with "w".

For example, an edge labeled "R" can only face an edge with "r". Figure 2 is an example of a completed state of a puzzle. In the figure, edges under this restriction are indicated by shadowing their labels. The player can freely move and rotate the pieces, but cannot turn them over. There are no symbols on the reverse side of a piece !

<image>
---
Figure 2: A completed puzzle example

Each piece is represented by a sequence of the four symbols on the piece, starting with the symbol of the top edge, followed by the symbols of the right edge, the bottom edge, and the left edge. For example, gwgW represents the leftmost piece in Figure 1. Note that the same piece can be represented as wgWg, gWgw or Wgwg since you can rotate it in 90, 180 or 270 degrees.

The mission for you is to create a program which counts the number of solutions. It is needless to say that these numbers must be multiples of four, because, as shown in Figure 3, a configuration created by rotating a solution in 90, 180 or 270 degrees is also a solution.

<image>
---
Figure 3: Four obvious variations for a completed puzzle

A term "rotationally equal" is defined; if two different pieces are identical when one piece is rotated (in 90, 180 or 270 degrees), they are rotationally equal. For example, WgWr and WrWg are rotationally equal.

Another term "rotationally symmetric" is defined; if a piece is rotationally equal to itself, it is rotationally symmetric. For example, a piece gWgW is rotationally symmetric.


Given puzzles satisfy the following three conditions:

1. There is no identical pair of pieces in a puzzle.
2. There is no rotationally equal pair of pieces in a puzzle.
3. There is no rotationally symmetric piece in a puzzle.



Input

The input consists of multiple puzzles.


	N

	Puzzle1

	Puzzle2

	. . .

	PuzzleN



N is the number of puzzles. Each Puzzlei gives a puzzle with a single line of 44 characters, consisting of four-character representations of the nine pieces of the puzzle, separated by a space character. For example, the following line represents the puzzle in Figure 1.



	gwgW RBbW GWrb GRRb BWGr Rbgw rGbR gBrg GRwb


Output

For each Puzzlei , the number of its solutions should be the output, each in a separate line.

Example

Input

6
WwRR wwrg RRGb rGBG RGrb RrRg RGrg rgBB Wrgr
RrGb WWGR rGgb Wbrg wgBb GgBg WbBG Wwwg WWGG
RBbr Wrbr wGGG wggR WgGR WBWb WRgB wBgG WBgG
wBrg rGgb WRrB WWbw wRRB RbbB WRrb wrbb WgrG
WrwB WWww wRRB WGGb Wbbg WBgG WrbG Wrww RBgg
WWgg RBrr Rggr RGBg Wbgr WGbg WBbr WGWB GGGg


Output

40
8
32
4
12
0

Solution (in CPP):

Solution:
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <cfloat>
#include <stack>
#include <queue>
#include <vector>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;

enum Loc{
	Top,
	Right,
	Under,
	Left,
};

enum Roll{
	r_0,
	r_90,
	r_180,
	r_270,
};

Roll roll_type[4] = {r_0,r_90,r_180,r_270};

struct Data{
	int data_id;
	Roll roll;
};

struct Info{
	int next_loc;
	bool check[9];
	Data table[3][3];
};

void copyInfo(Info from,Info& to){
	for(int i = 0; i < 9; i++)to.check[i] = from.check[i];
	for(int i = 0; i < 3; i++){
		for(int k = 0; k < 3; k++){
			to.table[i][k].data_id = from.table[i][k].data_id;
			to.table[i][k].roll = from.table[i][k].roll;
		}
	}
}

int N,ans;
char data[9][4];

bool isMatch(char left,char right){

	if(left > right){
		swap(left,right);
	}

	if(left == 'R' && right == 'r'){
		return true;
	}else if(left == 'G' && right == 'g'){
		return true;
	}else if(left == 'B' && right == 'b'){
		return true;
	}else if(left == 'W' && right == 'w'){
		return true;
	}

	return false;
}

char getWord(int data_id,Roll roll,Loc loc){

	switch(roll){
	case r_0:
		switch(loc){
		case Top:
			return data[data_id][Top];
		case Right:
			return data[data_id][Right];
		case Under:
			return data[data_id][Under];
		case Left:
			return data[data_id][Left];
		}
		break;
	case r_90:
		switch(loc){
		case Top:
			return data[data_id][Left];
		case Right:
			return data[data_id][Top];
		case Under:
			return data[data_id][Right];
		case Left:
			return data[data_id][Under];
		}
		break;
	case r_180:
		switch(loc){
		case Top:
			return data[data_id][Under];
		case Right:
			return data[data_id][Left];
		case Under:
			return data[data_id][Top];
		case Left:
			return data[data_id][Right];
		}
		break;
	case r_270:
		switch(loc){
		case Top:
			return data[data_id][Right];
		case Right:
			return data[data_id][Under];
		case Under:
			return data[data_id][Left];
		case Left:
			return data[data_id][Top];
		}
		break;
	}
	return 'X'; //must not reach here
}


void recursive(Info info){

	if(info.next_loc == 9){
		ans++;
		return;
	}

	for(int i = 0; i < 9; i++){
		if(info.check[i] == false){

			switch(info.next_loc){
			case 1:
				for(int k = 0; k < 4; k++){
					if(isMatch(data[info.table[1][1].data_id][Top],getWord(i,roll_type[k],Under))){
						Info new_info;
						copyInfo(info,new_info);
						new_info.check[i] = true;
						new_info.table[0][1].data_id = i;
						new_info.table[0][1].roll = roll_type[k];
						new_info.next_loc = info.next_loc+1;
						recursive(new_info);
					}
				}
				break;
			case 2:
				for(int k = 0; k < 4; k++){
					if(isMatch(data[info.table[1][1].data_id][Right],getWord(i,roll_type[k],Left))){
						Info new_info;
						copyInfo(info,new_info);
						new_info.check[i] = true;
						new_info.table[1][2].data_id = i;
						new_info.table[1][2].roll = roll_type[k];
						new_info.next_loc = info.next_loc+1;
						recursive(new_info);
					}
				}
				break;
			case 3:
				for(int k = 0; k < 4; k++){
					if(isMatch(data[info.table[1][1].data_id][Under],getWord(i,roll_type[k],Top))){
						Info new_info;
						copyInfo(info,new_info);
						new_info.check[i] = true;
						new_info.table[2][1].data_id = i;
						new_info.table[2][1].roll = roll_type[k];
						new_info.next_loc = info.next_loc+1;
						recursive(new_info);
					}
				}
				break;
			case 4:
				for(int k = 0; k < 4; k++){
					if(isMatch(data[info.table[1][1].data_id][Left],getWord(i,roll_type[k],Right))){
						Info new_info;
						copyInfo(info,new_info);
						new_info.check[i] = true;
						new_info.table[1][0].data_id = i;
						new_info.table[1][0].roll = roll_type[k];
						new_info.next_loc = info.next_loc+1;
						recursive(new_info);
					}
				}
				break;
			case 5:
				for(int k = 0; k < 4; k++){
					if(isMatch(getWord(info.table[0][1].data_id,info.table[0][1].roll,Right),getWord(i,roll_type[k],Left)) == true &&
							isMatch(getWord(info.table[1][2].data_id,info.table[1][2].roll,Top),getWord(i,roll_type[k],Under)) == true){
						Info new_info;
						copyInfo(info,new_info);
						new_info.check[i] = true;
						new_info.table[0][2].data_id = i;
						new_info.table[0][2].roll = roll_type[k];
						new_info.next_loc = info.next_loc+1;
						recursive(new_info);
					}
				}
				break;
			case 6:
				for(int k = 0; k < 4; k++){
					if(isMatch(getWord(info.table[1][2].data_id,info.table[1][2].roll,Under),getWord(i,roll_type[k],Top)) == true &&
							isMatch(getWord(info.table[2][1].data_id,info.table[2][1].roll,Right),getWord(i,roll_type[k],Left)) == true){
						Info new_info;
						copyInfo(info,new_info);
						new_info.check[i] = true;
						new_info.table[2][2].data_id = i;
						new_info.table[2][2].roll = roll_type[k];
						new_info.next_loc = info.next_loc+1;
						recursive(new_info);
					}
				}
				break;
			case 7:
				for(int k = 0; k < 4; k++){
					if(isMatch(getWord(info.table[2][1].data_id,info.table[2][1].roll,Left),getWord(i,roll_type[k],Right)) == true &&
							isMatch(getWord(info.table[1][0].data_id,info.table[1][0].roll,Under),getWord(i,roll_type[k],Top)) == true){
						Info new_info;
						copyInfo(info,new_info);
						new_info.check[i] = true;
						new_info.table[2][0].data_id = i;
						new_info.table[2][0].roll = roll_type[k];
						new_info.next_loc = info.next_loc+1;
						recursive(new_info);
					}
				}
				break;
			case 8:
				for(int k = 0; k < 4; k++){
					if(isMatch(getWord(info.table[1][0].data_id,info.table[1][0].roll,Top),getWord(i,roll_type[k],Under)) == true &&
							isMatch(getWord(info.table[0][1].data_id,info.table[0][1].roll,Left),getWord(i,roll_type[k],Right)) == true){
						Info new_info;
						copyInfo(info,new_info);
						new_info.check[i] = true;
						new_info.table[0][0].data_id = i;
						new_info.table[0][0].roll = roll_type[k];
						new_info.next_loc = info.next_loc+1;
						recursive(new_info);
					}
				}
				break;
			}
		}
	}

}

void func(){

	ans = 0;

	char buf[5];

	for(int i = 0; i < 9; i++){
		scanf("%s",buf);

		for(int k = 0; k < 4; k++){
			data[i][k] = buf[k];
		}
	}

	for(int i = 0; i < 9; i++){
		Info info;
		for(int k = 0; k < 9; k++)info.check[k] = false;
		info.table[1][1].data_id = i;
		info.table[1][1].roll = r_0;
		info.check[i] = true;
		info.next_loc = 1;
		recursive(info);
	}

	printf("%d\n",ans*4);
}

int main(){

	scanf("%d",&N);
	for(int i = 0; i < N; i++){
		func();
	}

	return 0;
}