#pragma GCC optimize(3)
#include<iostream>
#include<iomanip>
#include<stack>
#include<queue>
#include<list>
#include<vector>
#include<set>
#include<map>
#include<string>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<ctime>
#define ll long long
#define db double
#define inf 409
#define infm 100009
#define INF (ll)1e15
#define mod ((ll)1e9+7)
#define pi acos(-1)
#define rd(n) {n=0;char ch;int f=0;do{ch=getchar();if(ch=='-'){f=1;}}while(ch<'0'||ch>'9');while('0'<=ch&&ch<='9'){n=(n<<1)+(n<<3)+ch-48;ch=getchar();}if(f)n=-n;}
using namespace std;
int n;

struct brick{
	int a,b,c,d;
	brick(){}
	brick(int aa,int bb,int cc,int dd){
		a=aa,b=bb,c=cc,d=dd;
	}
}a[inf];

bool operator<(brick _1,brick _2){
	if (_1.a==_2.a){
		if (_1.b==_2.b){
			if (_1.c==_2.c){
				return _1.d<_2.d;
			}
			return _1.c<_2.c;
		}
		return _1.b<_2.b;
	}
	return _1.a<_2.a;
}

brick Nxt(brick x){
	return brick(x.b,x.c,x.d,x.a);
}

map <brick,ll> mp;

void insert(brick x,int val){
	for (int i=1;i<=4;i++){
		mp[x]+=val;
		x=Nxt(x);
	}
	return;
}

bool check(brick x){
	return mp.count(x);
}

int main(){
	rd(n)
	for (int i=1;i<=n;i++){
		rd(a[i].a) rd(a[i].b) rd(a[i].c) rd(a[i].d)
		insert(a[i],1);
	}
	ll ans=0LL;
	for (int i=1;i<=n;i++){
		insert(a[i],-1);
		for (int j=i+1;j<=n;j++){
			insert(a[j],-1);
			for (int k=0;k<4;k++){
				a[j]=Nxt(a[j]);
				brick t1,t2,t3,t4;
				t1=brick(a[i].b,a[i].a,a[j].a,a[j].d);
				t2=brick(a[i].c,a[i].b,a[j].d,a[j].c);
				t3=brick(a[i].d,a[i].c,a[j].c,a[j].b);
				t4=brick(a[i].a,a[i].d,a[j].b,a[j].a);
				if (check(t1) && check(t2) && check(t3) && check(t4)){
					ll now=1LL;
					now*=mp[t1];
					insert(t1,-1);
					now*=mp[t2];
					insert(t2,-1);
					now*=mp[t3];
					insert(t3,-1);
					now*=mp[t4];
					insert(t1,1);
					insert(t2,1);
					insert(t3,1);
					ans+=now;
				}
			}
			insert(a[j],1);
		}
	}
	printf("%lld\n",ans);
	return 0;
}