#include <bits/stdc++.h>
#define int long long

using namespace std;

struct face{
	int a,b,c,d,orien;
	face(){}
	face(int aa, int bb, int cc, int dd){
		a=aa, b=bb, c=cc, d=dd;
		orien = 1;
		if(a==c and b==d) orien = 2; 
		if(a==b and b==c and c==d and d==a) orien = 4; // step 4e
	}
	void rotate(){
		int t=a;
		a=b,b=c,c=d,d=t;
	}
	bool operator<(face f) const{
		return make_tuple(a,b,c,d) < make_tuple(f.a,f.b,f.c,f.d);
	}
	bool operator==(face f) const{
		return make_tuple(a,b,c,d) == make_tuple(f.a,f.b,f.c,f.d);
	}
	face arrange(){
		face best = face(a,b,c,d);
		face cur = face(a,b,c,d);
		for(int i=0; i<4; ++i){
			cur.rotate();
			if(cur < best) best = cur;
		}
		return best;
	}
	void print(){
//		printf("(%d %d %d %d)\n",a,b,c,d);
	}
};
int n;
face A[500];
map<face, int> cnt;
int32_t main(){
	cin >> n;
	/*
idea:
	1. we pick two faces to be the opposite sides of the cube, because these 2 face choices (top and bottom of cube)will determine the entire cube.
	2. fix one face's rotation, rotate the other such to cover all 4 directions
	3. now, we just need to search among the list of faces for the other 4 cube faces. (we will call the 4 cube faces "sides" from now on)
	4. this may appear to be a simple task, but there are a few things we need to look out for:
		4a. firstly, for each side face, just count among the list of faces how many times it appears, then multiply
			e.g. side A appears 3, side B appears 1, C appears 3, D appears 2. then the total number of cubes of 
			that "colour (of the 8 corners)" would be 3x1x3x2
		4b. we need an simple way to "count among the list"
			e.g. if we just store the faces in an array, (1 0 0 0) would not be counted if we were looking for (0 0 0 1) even though
			they are actually the same face
			hence, for each face, we find the rotation of that face that is lexicographically the smallest (for (1 4 0 2) it would be (0 2 1 4))
			we then store a map<face, int> to count the number of times a face is in the list of faces
		4c. then another complication is, what if two side faces are identical. then if we simply multiply, it would be double using a single face
			e.g. the 4 side faces are (0 0 0 1) (0 0 0 1) (0 0 1 1) (0 1 1 1) [might not make sense, for illustrative purpose]
			then lets say (0 0 0 1) appears 3 times in the list of faces (with the faces being A,B,C)
			it would be double using the same face if we do 3x3 (AA,AB,AC,BA,BB,BC,CA,CB,CC)
			what we actually want is 3x2 (AB,AC,BA,BC,CA,CB) (note AB and BA are different)
			So, for the 4 side faces, we 'accumulate' them and count how many of each face we need
			in the example above, this would become (0 0 0 1)x2 (0 0 1 1)x1 (0 1 1 1)x1
			then we can calculate more easily
		4d. another complication is, what if one or more of the side faces are equal to the 2 face choices we made?
			hence, when counting the number of times a side face appears, we must subtract one or two if the top/bottom/both face matches the side face
		4e. lastly, each face may be invariant under 
			i. a single rotation (hence all 4 rotations yield the same face)
			ii. two rotations (hence 2 rotations yield the same face)
			so remember to multiply that when calculating the number of faces eligible to be the needed side face
	*/
	for(int i=0; i<n; ++i){
		int a,b,c,d;
		cin >> a >> b >> c >> d;
		A[i] = face(a,b,c,d).arrange(); // step 4b
		if(cnt.find(A[i]) == cnt.end()) cnt[A[i]]=1; // step 4b
		else cnt[A[i]]++;
	}
	for(int i=0; i<n; ++i){
		A[i].print();
	}
	int ans = 0;
	for(int i=0; i<n; ++i){ 		// top face
		for(int j=i+1; j<n; ++j){ 	// bottom face
			face t=A[i], b=A[j];
			for(int k=0; k<4; ++k){ // 4 rotations of the bottom face
				face q = face(t.b,b.a,b.d,t.c).arrange(); 	// 4 side faces
				face w = face(t.a,b.b,b.a,t.b).arrange();
				face e = face(b.b,t.a,t.d,b.c).arrange();
				face r = face(t.c,b.d,b.c,t.d).arrange();
				vector<face> four;  				// placed into a vector for easy manipulation
				four.push_back(q);
				four.push_back(w);
				four.push_back(e);
				four.push_back(r);

				typedef pair<face,int> pf;
				vector<pf> v;
				for(auto l : four){ 				// collecting it into pair<face, int> 
										// step 4c
					bool newone = true;
					for(auto it = v.begin(); it!=v.end(); ++it){
						if(it->first == l){ it->second++; newone = false; break; }
					}
					if(newone) v.push_back(pf(l,1));
				}
				int add = 1;
				for(auto m : v){				// for each side face [m.first] needed (and the quantity [m.second])
					int c = cnt[m.first];			
					if(m.first == t) c--;			// if matches top, subtract one
					if(m.first == b.arrange()) c--; 	// if matches bottom, subtract one
										// step 4d
//					m.first.print(); printf("x%d, %d\n", c, m.second);
					while(m.second--){ 			// c * (c-1) * (c-2) ...  [effectively c P m.second, where P is permute]
						add *= c--; 
						add *= m.first.orien;
					}
				}
				ans += add;
//				printf(" === %d %d: +%d\n", i, j, add);
				b.rotate();
			}
		}
	}
	cout << ans/3; // finally, each cube formed will be counted 3 times (by the 3 pairs of opposite faces), so ans will be 3 times larger than the correct value
}


Measured memory_bytes: 124928