#include <bits/stdc++.h>

typedef long long ll;
#define X first
#define Y second
using namespace std;

const int N=4e2+10;

struct info{
    int a[4];
    void roll(){
        int tmp=a[3];
        for(int i=3;i>=1;i--) a[i]=a[i-1];
        a[0]=tmp;
    }
    info (int _a0=0,int _a1=0,int _a2=0,int _a3=0){
        a[0]=_a0;
        a[1]=_a1;
        a[2]=_a2;
        a[3]=_a3;
    }
};
struct infocomp{
    bool operator ()(const info& a,const info& b){
        for(int i=0;i<4;i++) if (a.a[i]!=b.a[i])
            return a.a[i]<b.a[i];
        return 0;
    }
};
map <info,int,infocomp> need,store;
int n;
info a[N];

int tu1[5],tu2[5];
void prepare(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        for(int j=0;j<4;j++) scanf("%d",&a[i].a[j]);
        for(int j=0;j<4;j++){
//            cout<<"=>";
//                for(int k=0;k<4;k++) cout<<a[i].a[k]<<" \n"[k==3];
            store[a[i]]++;
            a[i].roll();
        }
    }
//    for(auto i:store) {
//        for(int j=0;j<4;j++) cout<<i.X.a[j]<<" ";
//        cout<<i.Y<<'\n';
//    }
}
int getstorage(info a){
    auto ite=store.find(a);
    if (ite==store.end()) return 0;
    return ite->Y;
}
void takestorage(info a,int val){
    auto ite=store.find(a);
    if (ite==store.end()) return;
    store[a]-=val;
}
ll A(int x,int y){
    if (y>x||y<0) return 0;
    ll ans=1;
    for(int i=0;i<y;i++) ans*=(x-i);
    return ans;
}
bool operator ==(info a,info b){
    for(int i=0;i<4;i++) if (a.a[i]!=b.a[i]) return 0;
    return 1;
}
int unidi(info a){
    int ans=0;
    info tmp=a;
    for(int i=0;i<4;i++) {
        if (a==tmp) ans++;
        tmp.roll();
    }
    return ans;
}
ll solve(){
    ll ans=0;
    for(int u1=1;u1<=n;u1++){
        for(int d1=0;d1<4;d1++){
            store[a[u1]]--;
            a[u1].roll();
        }
        for(int u2=u1+1;u2<=n;u2++){
            for(int d2=0;d2<4;d2++){
                store[a[u2]]--;
                a[u2].roll();
            }
            for(int d2=0;d2<4;d2++){
                need.clear();
                for(int i=0;i<5;i++) tu1[i]=a[u1].a[i%4];
                for(int i=0;i<5;i++) tu2[i]=a[u2].a[(1-i+4)%4];
                for(int i=0;i<4;i++) need[info(tu1[i+1],tu1[i],tu2[i],tu2[i+1])]++;
                ll cans=1;
                for(auto cur:need){
                    int n2=unidi(cur.X);
                    int n1=getstorage(cur.X)/n2;
                    cans*=A(n1,cur.Y);
                    for(int i=0;i<cur.Y;i++) cans*=n2;
                    info tmp=cur.X;
                    for(int dc=0;dc<4;dc++){
                        takestorage(tmp,cur.Y);
                        tmp.roll();
                    }
                }
                for(auto cur:need){
                    info tmp=cur.X;
                    for(int dc=0;dc<4;dc++){
                        takestorage(tmp,-cur.Y);
                        tmp.roll();
                    }
                }
                ans+=cans;
                a[u2].roll();
            }
            for(int d2=0;d2<4;d2++){
                store[a[u2]]++;
                a[u2].roll();
            }
        }
        for(int d1=0;d1<4;d1++){
            store[a[u1]]++;
            a[u1].roll();
        }
    }
    return ans/3;
}
int main(){
    prepare();
    cout<<solve();
}


Measured memory_bytes: 384