#include <iostream>
#include <string>
#include <algorithm>
#include <deque>
#include <utility>
#include <unordered_set>
#include <functional>
#include <tuple>
#define	NDEBUG
//#undef	NDEBUG
#include <cassert>

#define		NOT_EXIST			(0xFFFF)

using namespace std;

class Dice
{
public:
	enum FACE
	{
		eTop = 0,
		eFront,
		eRight,
		eLeft,
		eBack,
		eBottom
	};

	Dice()
	{
	}

	Dice(bool isSetNumber)
	{
		if (isSetNumber)
		{
			SetDiceNumber();
		}
	}

	virtual ~Dice()
	{
	}

	void SetDiceNumber()
	{
		dqDiceNumber.resize(6);
		cin >> dqDiceNumber[eTop];
		cin >> dqDiceNumber[eFront];
		cin >> dqDiceNumber[eRight];
		cin >> dqDiceNumber[eLeft];
		cin >> dqDiceNumber[eBack];
		cin >> dqDiceNumber[eBottom];

		cin.clear();
		cin.ignore();

		size_t		sum = 0;

		for (auto it : dqDiceNumber)
		{
			sum += it;
		}
		SumDiceNumber = sum;
	}

	void OrderE()
	{
		deque<FACE>		Rotation{ eTop, eLeft, eBottom, eRight };

		OrderExecute(Rotation);
	}

	void OrderW()
	{
		deque<FACE>		Rotation{ eTop, eRight, eBottom, eLeft };

		OrderExecute(Rotation);
	}

	void OrderN()
	{
		deque<FACE>		Rotation{ eTop, eFront, eBottom, eBack };

		OrderExecute(Rotation);
	}

	void OrderS()
	{
		deque<FACE>		Rotation{ eTop, eBack, eBottom, eFront };

		OrderExecute(Rotation);
	}

	void OrderRotation()
	{
		deque<FACE>		Rotation{ eFront, eRight, eBack, eLeft };

		OrderExecute(Rotation);
	}

	int		GetDiceNumber(FACE face)
	{
		return (dqDiceNumber[face]);
	}

	void	SearchTop(size_t t)
	{
		for (int i = 0; i < 4; ++i)
		{
			if (t == dqDiceNumber[eTop])
			{
				return;
			}
			OrderN();
		}

		for (int i = 0; i < 4; ++i)
		{
			if (t == dqDiceNumber[eTop])
			{
				return;
			}
			OrderE();
		}
	}

	bool	SearchFront(size_t f)
	{
		for (int i = 0; i < 4; ++i)
		{
			if (f == dqDiceNumber[eFront])
			{
				return true;
			}
			OrderRotation();
		}

		return false;
	}

	size_t	GetSumDiceNumber()
	{
		return SumDiceNumber;
	}

	bool	IsSameDice(Dice &Right)
	{
		bool	Ret = true;

		if (SumDiceNumber != Right.GetSumDiceNumber())
		{
			return false;
		}

		if (IsSameNumber(Right) == false)
		{
			return false;
		}

		deque<SameAndDice>		DiceType(InvestigateDiceNumber());

		switch (DiceType.size())
		{
		case 6:
			//All numbers are different
			//???????????????????????????????????????
		case 5:
			//Same is 1
			//?????¨??????????????????????????????DiceType???????????????????????????????????????????????????2?????\?????????
			Ret = FixTopAndFront(Right, DiceType);
			break;
		case 4:
			//I need to solve why do I answer so?
			//case [1,1][1,2][2,3][2,4] or [1,1][1,2][1,3][3,4] and so on.
			Ret = SameCheckForSame4(Right, DiceType);
			break;
		case 3:
			//I need to solve why do I answer so?
			//case [2,1][2,2][2,3] or [1,1][2,2][3,3] or [1,1][1,2][4,3] and so on.
			Ret = SameCheckForSame3(Right, DiceType);
			break;
		case 2:
			//I need to solve why do I answer so?
			//case [1,1][5,2] or [2,1][4,2] or [3,1][3,2] and so on.
			Ret = SameCheckForSame2(Right, DiceType);
			break;
		case 1:
			//All numbers are same.
			//??¨???????????°???????????????
			Ret = true;
			break;
		default:
			assert(false);
			break;
		}

		return	Ret;
	}

private:
	typedef		pair<int, size_t>		SameAndDice;
	//Same:		The number to which the same number of dice has gone out
	//			???????????????????????????????????°
	//Dice:		Dice Number
	//			??????????????????

	deque<size_t>	dqDiceNumber;
	size_t			SumDiceNumber;

	void	OrderExecute(const deque<FACE> &Rotation)
	{
		auto	temp = dqDiceNumber[Rotation[0]];
		auto	LoopMax = Rotation.size() - 1;

		for (auto i = 0u; i < LoopMax; ++i)
		{
			dqDiceNumber[Rotation[i]] = dqDiceNumber[Rotation[i + 1]];
		}
		dqDiceNumber[Rotation[LoopMax]] = temp;
	}

	bool	IsSameNumber(Dice &Right)
	{
		deque<size_t>		SortLeft(this->dqDiceNumber);
		deque<size_t>		SortRight(Right.dqDiceNumber);

		auto	SortNumber = [](deque<size_t> &d)
		{
			sort(begin(d), end(d));
		};

		SortNumber(SortLeft);
		SortNumber(SortRight);
		
		return (SortLeft == SortRight);
	}

	deque<SameAndDice>	InvestigateDiceNumber()
	{
		deque<SameAndDice>			DiceType;
		unordered_set<size_t>		us;

		for (auto it : dqDiceNumber)
		{
			if (us.insert(it).second)
			{
				DiceType.push_back(make_pair(1, it));
			}
			else
			{
				for (auto &itdt : DiceType)
				{
					if (itdt.second == it)
					{
						itdt.first++;
					}
				}
			}
		}

		sort(begin(DiceType), end(DiceType));

		return DiceType;
	}

	bool	CheckSame(Dice &Right)
	{
		for (int i = eRight; i <= eBottom; i++)
		{
			if (this->dqDiceNumber[i] != Right.dqDiceNumber[i])
			{
				return false;
			}
		}

		return true;
	}

	bool	FixTopAndFront(Dice &Right, deque<SameAndDice> &DiceType)
	{
		size_t		top = DiceType[0].second;
		size_t		front = DiceType[1].second;

		this->SearchTop(top);
		if (this->SearchFront(front) == false)
		{
			front = DiceType[2].second;
			this->SearchFront(front);
		}

		Right.SearchTop(top);
		if (Right.SearchFront(front) == false)
		{
			return false;
		}

		return CheckSame(Right);
	}

	void	AdjustFace(Dice &Right, FACE face)
	{
		while (this->dqDiceNumber[face] != Right.GetDiceNumber(face))
		{
			Right.OrderRotation();
		}
	}

	//Can use know same number exists side
	void	AdjustFace(Dice &Right, size_t DiceNumber)
	{
		Right.OrderRotation();
		while (Right.GetDiceNumber(eFront) != DiceNumber)
		{
			Right.OrderRotation();
		}
	}

	//case [1,1][1,2][2,3][2,4] or [1,1][1,2][1,3][3,4] and so on.
	bool	SameCheckForSame4(Dice &Right, deque<SameAndDice> &DiceType)
	{
		bool	Ret = true;

		if (DiceType[2].first == 1)
		{
			//case [1,1][1,2][1,3][3,4]
			Ret = FixTopAndFront(Right, DiceType);
		}
		else
		{
			size_t		top = DiceType[0].second;

			this->SearchTop(top);
			if (this->dqDiceNumber[eBottom] == DiceType[1].second)
			{
				Right.SearchTop(top);

				//case [1,1][1,2] is Top and Bottom
				if (Right.GetDiceNumber(eBottom) == DiceType[1].second)
				{
					AdjustFace(Right, eFront);

					if (this->dqDiceNumber[eBack] == this->dqDiceNumber[eFront])
					{
						//case [2,3] is Front and Back
						if (Right.GetDiceNumber(eBack) != Right.GetDiceNumber(eFront))
						{
							Ret = false;
						}
					}
					else
					{
						//case [2,3] is Front and Side
						if (Right.GetDiceNumber(eBack) == Right.GetDiceNumber(eFront))
						{
							Ret = false;
						}
					}
				}
				else
				{
					Ret = false;
				}
			}
			else
			{
				//case [1,1][1,2] is Top and Not Bottom
				Ret = FixTopAndFront(Right, DiceType);
			}
		}

		return Ret;
	}


	//case [2,1][2,2][2,3] or [1,1][2,2][3,3] or [1,1][1,2][4,3] and so on.
	bool	SameCheckForSame3(Dice &Right, deque<SameAndDice> &DiceType)
	{
		bool	Ret = true;

		if (DiceType[0].first == 1)
		{
			if (DiceType[1].first == 1)
			{
				//case [1,1][1,2][4,3]
				Ret = FixTopAndFront(Right, DiceType);
			}
			else
			{
				//case [1,1][2,2][3,3]
				if (this->dqDiceNumber[eBottom] == DiceType[1].second)
				{
					//[2,2] is Bottom and Not Bottom
					Ret = FixTopAndFront(Right, DiceType);
				}
				else
				{
					//[2,2] is Both Not Bottom
					if (FixTopAndFront(Right, DiceType) == false)
					{
						AdjustFace(Right, DiceType[1].second);
						Ret = CheckSame(Right);
					}
				}
			}
		}
		else
		{
			//case [2,1][2,2][2,3]
			size_t	t, f;

			tie(t, f) = Reverse(this->dqDiceNumber, DiceType);

			if (t != NOT_EXIST)
			{
				//top and bottom is same
				this->SearchTop(t);
				Right.SearchTop(t);

				if (Right.GetDiceNumber(eBottom) != t)
				{
					Ret = false;
				}
				else
				{
					this->SearchFront(f);
					Right.SearchFront(f);
					if (CheckSame(Right) == false)
					{
						AdjustFace(Right, f);
						Ret = CheckSame(Right);
					}
				}
			}
			else
			{
				//case top and side is same -> top and front is same
				size_t	aDiceType = DiceType[0].second;

				this->SearchTop(aDiceType);
				Right.SearchTop(aDiceType);
				this->SearchFront(aDiceType);
				Right.SearchFront(aDiceType);

				if (CheckSame(Right) == false)
				{
					Right.OrderN();
					assert(Right.GetDiceNumber(eTop) == aDiceType);
					Right.SearchFront(aDiceType);
					Ret = CheckSame(Right);
				}
			}
		}

		return Ret;
	}

	tuple<size_t, size_t>	Reverse(deque<size_t> &dq, deque<SameAndDice> &DiceType)
	{
		size_t	t = NOT_EXIST;
		size_t	f = NOT_EXIST;

		if (dq[eTop] == dq[eBottom])
		{
			t = dq[eTop];
		}
		else if (dq[eFront] == dq[eBack])
		{
			t = dq[eFront];
		}
		else if (dq[eRight] == dq[eLeft])
		{
			t = dq[eRight];
		}

		if (t != NOT_EXIST)
		{
			this->SearchTop(t);
			f = DiceType[0].second;

			if (f == t)
			{
				f = DiceType[1].second;
			}
		}

		return (forward_as_tuple(t, f));
	}

	//case [1,1][5,2] or [2,1][4,2] or [3,1][3,2] and so on.
	bool	SameCheckForSame2(Dice &Right, deque<SameAndDice> &DiceType)
	{
		bool	Ret = true;
		auto	TopBottomSame1 = [=](deque<size_t> dx, FACE f1, FACE f2, size_t) -> bool
		{
			return (dx[f1] == dx[f2]);
		};
		auto	TopBottomSame2 = [=](deque<size_t> dx, FACE f1, FACE f2, size_t DN) -> bool
		{
			return (dx[f1] == DN) && (dx[f2] == DN);
		};

		switch (DiceType[0].first)
		{
		case 3:
			//case [1,3][2,3] and so on.
			Ret = CheckSameCheckForSame2(Right, DiceType[0].second, TopBottomSame1);
			break;
		case 2:
			Ret = CheckSameCheckForSame2(Right, DiceType[0].second, TopBottomSame2);
			break;
		case 1:
			Ret = true;
			break;
		default:
			assert(false);
			break;
		}

		return Ret;
	}

	bool	CheckSameCheckForSame2(Dice &Right, size_t DN, function<bool(deque<size_t>, FACE, FACE, size_t)> func)
	{
		bool	LeftTopBottomSame = false;
		bool	RightTopBottomSame = false;

		if ((func(this->dqDiceNumber, eTop, eBottom, DN)) ||
			(func(this->dqDiceNumber, eFront, eBack, DN)) ||
			(func(this->dqDiceNumber, eRight, eLeft, DN)))
		{
			LeftTopBottomSame = true;
		}

		if ((func(Right.dqDiceNumber, eTop, eBottom, DN)) ||
			(func(Right.dqDiceNumber, eFront, eBack, DN)) ||
			(func(Right.dqDiceNumber, eRight, eLeft, DN)))
		{
			RightTopBottomSame = true;
		}

		return (LeftTopBottomSame == RightTopBottomSame);
	}


};

void DiceI();
void DiceII();
void DiceIII();
void DiceIV();

int main()
{
	DiceIV();

	return 0;
}

void DiceI()
{
	Dice	d(true);

	string	str;

	getline(cin, str);
	for (auto it : str)
	{
		switch (toupper(it))
		{
		case 'S':
			d.OrderS();
			break;
		case 'N':
			d.OrderN();
			break;
		case 'E':
			d.OrderE();
			break;
		case 'W':
			d.OrderW();
			break;
		default:
			break;
		}
	}

	cout << d.GetDiceNumber(Dice::eTop) << "\n";
}

void DiceII()
{
	Dice	d(true);

	int		q;
	cin >> q;
	cin.clear();
	cin.ignore();

	for (int i = 0; i < q; ++i)
	{
		int		t, f;

		cin >> t >> f;
		cin.clear();
		cin.ignore();

		d.SearchTop(t);
		d.SearchFront(f);

		cout << d.GetDiceNumber(Dice::eRight) << "\n";
	}
}

void DiceIII()
{
	Dice		dl(true);
	Dice		dr(true);

	if (dl.IsSameDice(dr))
	{
		cout << "Yes\n";
	}
	else
	{
		cout << "No\n";
	}
}

void DiceIV()
{
	deque<Dice>		dq;
	size_t			n;

	cin >> n;
	cin.clear();
	cin.ignore();

	for (size_t i = 0; i < n; i++)
	{
		Dice		d(true);
		dq.push_back(d);
	}

	for (auto i = 0u; i < (dq.size() - 1); i++)
	{
		for (auto j = i + 1; j < dq.size(); j++)
		{
			if (dq[i].IsSameDice(dq[j]))
			{
				cout << "No\n";
				return;
			}
		}
	}

	cout << "Yes\n";
	return;
}

Measured memory_bytes: 3260