#include <bits/stdc++.h>
using namespace std;

using ll = long long;

template<class T> struct Point3D {
	typedef Point3D P;
	typedef const P& R;
	T x, y, z;
	explicit Point3D(T x=0, T y=0, T z=0) : x(x), y(y), z(z) {}
	bool operator<(R p) const {
		return tie(x, y, z) < tie(p.x, p.y, p.z); }
	bool operator==(R p) const {
		return tie(x, y, z) == tie(p.x, p.y, p.z); }
	P operator+(R p) const { return P(x+p.x, y+p.y, z+p.z); }
	P operator-(R p) const { return P(x-p.x, y-p.y, z-p.z); }
	P operator*(T d) const { return P(x*d, y*d, z*d); }
	P operator/(T d) const { return P(x/d, y/d, z/d); }
	T dot(R p) const { return x*p.x + y*p.y + z*p.z; }
	P cross(R p) const {
		return P(y*p.z - z*p.y, z*p.x - x*p.z, x*p.y - y*p.x);
	}
	T dist2() const { return x*x + y*y + z*z; }
	double dist() const { return sqrt((double)dist2()); }
	//Azimuthal angle (longitude) to x-axis in interval [-pi, pi]
	double phi() const { return atan2(y, x); } 
	//Zenith angle (latitude) to the z-axis in interval [0, pi]
	double theta() const { return atan2(sqrt(x*x+y*y),z); }
	P unit() const { return *this/(T)dist(); } //makes dist()=1
	//returns unit vector normal to *this and p
	P normal(P p) const { return cross(p).unit(); }
	//returns point rotated 'angle' radians ccw around axis
	P rotate(double angle, P axis) const {
		double s = sin(angle), c = cos(angle); P u = axis.unit();
		return u*dot(u)*(1-c) + (*this)*c - cross(u)*s;
	}
};

using P = Point3D<ll>;

int main(){
	ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int n;
	cin >> n;
	vector<vector<P> > p(2, vector<P>(n));
	vector<vector<vector<int> > > edges(2, vector<vector<int> > (n));
	for(int z = 0; z < 2; z++){
		for(int j = 0; j < n; j++){
			cin >> p[z][j].x >> p[z][j].y >> p[z][j].z;
		}
		for(int j = 0; j < n-1; j++){
			int a, b;
			cin >> a >> b;
			a--; b--;
			a %= n; b %= n;
			edges[z][a].push_back(b);
			edges[z][b].push_back(a);
		}
	}
	int a = -1, b = -1, c = -1;
	for(int i = 0; i < n; i++){
		for(int j : edges[0][i]){
			for(int k : edges[0][i]){
				// n^2 here
				if(j == k) continue;
				if(a == -1 || !((p[0][j] - p[0][i]).cross(p[0][k] - p[0][i]) == P(0,0,0)) ){
					a = i;
					b = j;
					c = k;
				}
			}
		}
	}
	assert(a >= 0);
	int ans = 0;
	for(int i = 0; i < n; i++){
		for(int j : edges[1][i]){
			for(int k : edges[1][i]){
				// n^2 here
				if(j == k) continue;
				vector<ll> gs(2);
				for(int z = 0; z < 2; z++){
					vector<int> ref;
					if(z == 0) ref = {a, b, c};
					if(z == 1) ref = {i, j, k};
					ll g = __gcd((p[z][ref[0]]-p[z][ref[1]]).dist2(), 
						   __gcd((p[z][ref[0]]-p[z][ref[2]]).dist2(), 
							     (p[z][ref[1]]-p[z][ref[2]]).dist2()));
					gs[z] = abs(g);
				}
				vector<pair<vector<ll>, ll> > where[2];
				vector<int> inv[2];
				for(int z = 0; z < 2; z++){
					inv[z].resize(n);
					vector<int> ref;
					if(z == 0) ref = {a, b, c};
					if(z == 1) ref = {i, j, k};
					ll multg = gs[1 ^ z];
					P cr = (p[z][ref[2]] - p[z][ref[0]]).cross(p[z][ref[1]] - p[z][ref[0]]);
					for(int q = 0; q < n; q++){
						ll r = (p[z][q] - p[z][ref[0]]).dist2() * multg;
						ll s = (p[z][q] - p[z][ref[1]]).dist2() * multg;
						ll t = (p[z][q] - p[z][ref[2]]).dist2() * multg;
						ll u = (p[z][q] - p[z][ref[0]]).dot(cr);
						if(u > 0) u = 1;
						if(u < 0) u = -1;
						where[z].push_back({{r, s, t, u}, q});
					}
					sort(where[z].begin(), where[z].end());
					for(int q = 0; q < n; q++){
						inv[z][where[z][q].second] = q;
					}
				}
				bool bad = false;
				for(int q = 0; q < n; q++){
					if(where[0][q].first != where[1][q].first){
						bad = true;
						break;
					}
				}
				if(bad) continue;
				vector<pair<int,int> > new_edges[2];
				for(int z = 0; z < 2; z++){
					for(int q = 0; q < n; q++){
						for(int r : edges[z][q]){
							new_edges[z].push_back({inv[z][q], inv[z][r]});
						}
					}
					sort(new_edges[z].begin(), new_edges[z].end());
				}
				if(new_edges[0] == new_edges[1]){
					ans++;
				}
			}
		}
	}
	cout << ans << '\n';
}


Measured memory_bytes: 3304