using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static string InputPattern = "InputX";
static List<string> GetInputList()
{
var WillReturn = new List<string>();
if (InputPattern == "Input1") {
WillReturn.Add("1");
WillReturn.Add("1 2");
WillReturn.Add("4");
WillReturn.Add("1");
WillReturn.Add("3 3");
WillReturn.Add("2");
WillReturn.Add("4 2");
WillReturn.Add("5");
WillReturn.Add("0 1");
WillReturn.Add("1 1");
WillReturn.Add("2 1");
WillReturn.Add("3 1");
WillReturn.Add("4 1");
//1 2
//2 -1
//4 -1
//1 4
//1 0
}
else if (InputPattern == "Input2") {
WillReturn.Add("2");
WillReturn.Add("1000000000 0");
WillReturn.Add("0 1000000000");
WillReturn.Add("4");
WillReturn.Add("3 -1000000000");
WillReturn.Add("4 -1000000000");
WillReturn.Add("3 1000000000");
WillReturn.Add("4 1000000000");
WillReturn.Add("2");
WillReturn.Add("4 1");
WillReturn.Add("4 2");
//5000000000 4000000000
//4000000000 5000000000
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
struct XYInfoDef
{
internal long X;
internal long Y;
}
static List<XYInfoDef> mXYInfoList = new List<XYInfoDef>();
struct OPInfoDef
{
internal long Type;
internal long P;
}
static List<OPInfoDef> mOPInfoList = new List<OPInfoDef>();
struct QInfoDef
{
internal long A;
internal long B;
}
static List<QInfoDef> mQInfoList = new List<QInfoDef>();
static Queue<QInfoDef> mQInfoQue;
// クエリの回答[クエリのハッシュ値]
static Dictionary<string, string> mAnswerDict = new Dictionary<string, string>();
static void Main()
{
List<string> InputList = GetInputList();
long[] wkArr = { };
Action<string> SplitAct = pStr =>
wkArr = pStr.Split(' ').Select(pX => long.Parse(pX)).ToArray();
long N = long.Parse(InputList[0]);
foreach (string EachStr in InputList.Skip(1).Take((int)N)) {
SplitAct(EachStr);
XYInfoDef WillAdd;
WillAdd.X = wkArr[0];
WillAdd.Y = wkArr[1];
mXYInfoList.Add(WillAdd);
}
long M = long.Parse(InputList[(int)N + 1]);
foreach (string EachStr in InputList.Skip((int)N + 2).Take((int)M)) {
SplitAct(EachStr);
OPInfoDef WillAdd;
WillAdd.Type = wkArr[0];
WillAdd.P = int.MinValue;
if (wkArr[0] == 3 || wkArr[0] == 4) {
WillAdd.P = wkArr[1];
}
mOPInfoList.Add(WillAdd);
}
foreach (string EachStr in InputList.Skip(1 + (int)N + 1 + (int)M + 1)) {
SplitAct(EachStr);
QInfoDef WillAdd;
WillAdd.A = wkArr[0];
WillAdd.B = wkArr[1];
mQInfoList.Add(WillAdd);
}
// クエリのキューも用意しておく
mQInfoQue = new Queue<QInfoDef>(mQInfoList.OrderBy(pX => pX.A));
Solve();
}
// 初期座標を(a,b)として、座標変換の累積情報
struct HenkanInfoDef
{
internal long X_Keisuu_a;
internal long X_Keisuu_b;
internal long X_Tesuu;
internal long Y_Keisuu_a;
internal long Y_Keisuu_b;
internal long Y_Tesuu;
}
static void Solve()
{
HenkanInfoDef CurrHenkanInfo;
CurrHenkanInfo.X_Keisuu_a = 1;
CurrHenkanInfo.X_Keisuu_b = 0;
CurrHenkanInfo.X_Tesuu = 0;
CurrHenkanInfo.Y_Keisuu_a = 0;
CurrHenkanInfo.Y_Keisuu_b = 1;
CurrHenkanInfo.Y_Tesuu = 0;
for (long I = 0; I <= mOPInfoList.Count; I++) {
while (mQInfoQue.Count > 0) {
long PeekA = mQInfoQue.Peek().A;
long PeekB = mQInfoQue.Peek().B;
if (PeekA == I) {
long BFirstX = mXYInfoList[(int)PeekB - 1].X;
long BFirstY = mXYInfoList[(int)PeekB - 1].Y;
string Pos = GetPos(CurrHenkanInfo, BFirstX, BFirstY);
string Hash = string.Format("{0},{1}", PeekA, PeekB);
mAnswerDict[Hash] = Pos;
mQInfoQue.Dequeue();
}
else {
break;
}
}
if (I == mOPInfoList.Count) break;
long CurrType = mOPInfoList[(int)I].Type;
long CurrP = mOPInfoList[(int)I].P;
if (CurrType == 1) AddHenkan1(ref CurrHenkanInfo);
if (CurrType == 2) AddHenkan2(ref CurrHenkanInfo);
if (CurrType == 3) AddHenkan3(ref CurrHenkanInfo, CurrP);
if (CurrType == 4) AddHenkan4(ref CurrHenkanInfo, CurrP);
}
foreach (QInfoDef EachQInfo in mQInfoList) {
string Hash = string.Format("{0},{1}", EachQInfo.A, EachQInfo.B);
Console.WriteLine(mAnswerDict[Hash]);
}
}
// 初期座標と累積変換を引数として、現在の座標を返す
static string GetPos(HenkanInfoDef pHenkanInfo, long a, long b)
{
long X = 0;
X += pHenkanInfo.X_Keisuu_a * a;
X += pHenkanInfo.X_Keisuu_b * b;
X += pHenkanInfo.X_Tesuu;
long Y = 0;
Y += pHenkanInfo.Y_Keisuu_a * a;
Y += pHenkanInfo.Y_Keisuu_b * b;
Y += pHenkanInfo.Y_Tesuu;
return string.Format("{0} {1}", X, Y);
}
// 累積変換に変換1(時計周りに90度)を行う
static void AddHenkan1(ref HenkanInfoDef pHenkanInfo)
{
HenkanInfoDef Before = pHenkanInfo;
pHenkanInfo.X_Keisuu_a = Before.Y_Keisuu_a;
pHenkanInfo.X_Keisuu_b = Before.Y_Keisuu_b;
pHenkanInfo.X_Tesuu = Before.Y_Tesuu;
pHenkanInfo.Y_Keisuu_a = -(Before.X_Keisuu_a);
pHenkanInfo.Y_Keisuu_b = -(Before.X_Keisuu_b);
pHenkanInfo.Y_Tesuu = -(Before.X_Tesuu);
}
// 累積変換に変換2(反時計周りに90度)を行う
static void AddHenkan2(ref HenkanInfoDef pHenkanInfo)
{
HenkanInfoDef Before = pHenkanInfo;
pHenkanInfo.X_Keisuu_a = -(Before.Y_Keisuu_a);
pHenkanInfo.X_Keisuu_b = -(Before.Y_Keisuu_b);
pHenkanInfo.X_Tesuu = -(Before.Y_Tesuu);
pHenkanInfo.Y_Keisuu_a = Before.X_Keisuu_a;
pHenkanInfo.Y_Keisuu_b = Before.X_Keisuu_b;
pHenkanInfo.Y_Tesuu = Before.X_Tesuu;
}
// 累積変換に変換3(直線X=Pで対称移動)を行う
static void AddHenkan3(ref HenkanInfoDef pHenkanInfo, long pP)
{
// Y座標は変化しない
// X座標は、直線X=Pを中点とした、対称移動
pHenkanInfo.X_Keisuu_a *= -1;
pHenkanInfo.X_Keisuu_b *= -1;
pHenkanInfo.X_Tesuu *= -1;
pHenkanInfo.X_Tesuu += 2 * pP;
}
// 累積変換に変換4(直線Y=Pで対称移動)を行う
static void AddHenkan4(ref HenkanInfoDef pHenkanInfo, long pP)
{
// X座標は変化しない
// Y座標は、直線Y=Pを中点とした、対称移動
pHenkanInfo.Y_Keisuu_a *= -1;
pHenkanInfo.Y_Keisuu_b *= -1;
pHenkanInfo.Y_Tesuu *= -1;
pHenkanInfo.Y_Tesuu += 2 * pP;
}
}