#include <bits/stdc++.h>
      
#define FOR(i,a,b) for( ll i = (a); i < (ll)(b); i++ )
#define REP(i,n) FOR(i,0,n)
#define YYS(x,arr) for(auto& x:arr)
#define ALL(x) (x).begin(),(x).end()
#define SORT(x) sort( (x).begin(),(x).end() )
#define REVERSE(x) reverse( (x).begin(),(x).end() )
#define UNIQUE(x) (x).erase( unique( ALL( (x) ) ) , (x).end() )
#define PW(x) (1LL<<(x))
#define SZ(x) ((ll)(x).size())
#define SHOW(x) cout << #x << " = " << x << endl
     
#define pb emplace_back
#define fi first
#define se second
     
using namespace std;
     
typedef long double ld;
typedef long long int ll;
typedef pair<int,int> pi;
typedef pair<ll,ll> pl;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef vector<bool> vb;
typedef vector<ld> vd;
typedef vector<pi> vpi;
typedef vector<pl> vpl;
typedef vector<vpl> gr;
typedef vector<vl> ml;
typedef vector<vd> md;
typedef vector<vi> mi;
     
const ll INF = (ll)1e9 + 10;
const ll INFLL = (ll)1e18 + 10;
const ld EPS = 1e-12;
const ll MOD = 1e9+7;
     
template<class T> T &chmin( T &a , const T &b ){ return a = min(a,b); }
template<class T> T &chmax( T &a , const T &b ){ return a = max(a,b); }
template<class T> inline T sq( T a ){ return a * a; }

ll in(){ ll x; scanf( "%lld" , &x ); return x; }
char yuyushiki[1000010]; string stin(){ scanf( "%s" , yuyushiki ); return yuyushiki; }

// head

struct Unionfind{
  vi size, par;
  Unionfind(){}
  Unionfind( int n ) :  size(n,1), par(n){
    REP( i , n ) par[i] = i;
  }
  void init( int n ){
    size = vi( n , 1 );
    par.resize( n );
    REP( i , n ) par[i] = i;
  }
  int find( int x ){
    if( par[x] == x ) return x;
    return par[x] = find( par[x] );
  }
  bool unite( int x , int y ){
    x = find(x);
    y = find(y);
    if( x == y ) return false;
    if( size[y] < size[x] ) swap( x , y );
    par[x] = y;
    size[y] += size[x];
    return true;
  }
  bool same( int x , int y ){
    return find(x) == find(y);
  }
};


int n;

vi c[410];
ll ha[410];

Unionfind uf;

vi rot( vi v ){
  return { v[1] , v[2] , v[3] , v[0] };
}

bool same( vi a , vi b ){
  REP( i , 4 ){
    if( a == b ){
      return true;
    }
    b = rot( b );
  }
  return false;
}

int weight( vi v ){
  vi cur = v;
  int cnt = 1;
  REP( i , 3 ){
    cur = rot( cur );
    if( cur == v ){
      cnt++;
    }
  }
  return cnt;
}

ll perm( ll a , ll b ){
  if( a < b ){
    return 0;
  }
  ll res = 1;
  REP( i , b ){
    res *= a - i;
  }
  return res;
}


vi norm( vi v ){
  vi res = v;
  REP( i , 3 ){
    v = rot( v );
    chmin( res , v );
  }
  return res;
}

ll calchash( vi v ){
  return (ll)v[0] * 1000000000 + v[1] * 1000000 + v[2] * 1000 + v[3];
}

int main(){

  n = in();
  REP( i , n ){
    REP( j , 4 ){
      c[i].pb( in() );
    }
    c[i] = norm( c[i] );
    ha[i] = calchash( c[i] );
  }


  /*
  n = 400;
  REP( i , n ){
    REP( j , 4 ){
      c[i].pb( 0 );
    }
    c[i] = norm( c[i] );
    ha[i] = calchash( c[i] );
  }
  */
  

  int cnn = 0;
  ll ans = 0;
  REP( up , n ){
    vi u = c[up];
    REP( down , up ){
      vi d = c[down];
      REP( rotation , 4 ){
	mi s(4,vi(4));
	vl sh(4);
	REP( i , 4 ){
	  s[i][0] = u[i];
	  s[i][1] = d[(5-i)%4];
	  s[i][2] = d[(4-i)%4];
	  s[i][3] = u[(i+1)%4];
	  s[i] = norm( s[i] );
	  sh[i] = calchash( s[i] );
	}

	uf.init( 4 );
	REP( i , 4 ){
	  REP( j , i ){
	    if( sh[i] == sh[j] ){
	      uf.unite( i , j );
	    }
	  }
	}

	vi we(4,1);
	REP( i , 4 ){
	  we[ uf.find( i ) ] *= weight( s[i] );
	}

	vi cnt(4,0);
	REP( i , up ){
	  if( i == down ){
	    continue;
	  }
	  REP( j , 4 ){
	    if( sh[j] == ha[i] ){
	      cnt[j]++;
	    }
	  }
	}

	ll cur = 1;
	REP( i , 4 ){
	  if( uf.find( i ) == i ){
	    cur *= we[i] * perm( cnt[i] , uf.size[i] );
	    /*
	    if( up == 5 and down == 2 and rotation == 0 ){
	      cout << i << " " << we[i] << " " << cnt[i] << " " << uf.size[i] << endl;
	      cout << we[i] * perm( cnt[i] , uf.size[i] ) << endl;
	    }
	    */
	  }
	}

	ans += cur;

	d = rot( d );
      }
    }
  }

  // cout << cnn << endl;
  cout << ans << endl;
  
  return 0;
}


Measured memory_bytes: 384