#include "assert.h"
#include <algorithm>
#include <bitset>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <deque>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <time.h>
#include <vector>

using namespace std;

#if LOCAL
	#define DO_NOT_SEND
#endif

typedef long long LL;

int IntMaxVal = (int) 1e20;
int IntMinVal = (int) -1e20;
LL LongMaxVal = (LL) 1e20;
LL LongMinVal = (LL) -1e20;

#define FOR(i, a, b) for(int i = a; i < b ; ++i)
#define FORD(i, a, b) for(int i = a; i >= b; --i)

template<typename T> inline void minimize(T &a, T b) { a = std::min(a, b); }
template<typename T> inline void maximize(T &a, T b) { a = std::max(a, b); }

template<typename T> inline void swap(pair<T, T> &p) { swap(p.first , p.second ); }

#define all(v) v.begin(),v.end()

#define endl '\n'
template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define next(t, i) argument_type<void(t)>::type i; cin >> i;

template <typename T1, typename T2> istream& operator >>(istream& is, pair<T1, T2>& s) { is >> s.first >> s.second; return is; }
template <typename T> ostream& operator << (ostream& os, const vector<T> &v) { for (int i = 0 ; i < v.size() ; i++) { if (i) os << ' '; os << v[i]; } os << endl; return os; }
template <typename T1, typename T2> ostream& operator << (ostream& os, const vector<pair<T1, T2>> &v) { for (int i = 0 ; i < v.size() ; i++) { os << v[i] << endl; } return os; }
template <typename T1, typename T2> ostream& operator <<(ostream& s, const pair<T1, T2>& t) { s << t.first << ' ' << t.second; return s; }
template <typename T> vector<T> readVector(int n) { vector<T> res(n); for (int i = 0 ; i < n ; i++) cin >> res[i]; return res; }

struct matrix {
	vector<vector<double>> mx;

	matrix() {
		mx = vector<vector<double>>(3, vector<double>(3));
		mx[0][0] = mx[1][1] = mx[2][2] = 1;
	}

	matrix(int i) {
		mx = vector<vector<double>>(3, vector<double>(3));
		mx[0][0] = mx[1][1] = mx[2][2] = 1;
		mx[0][2] = 1;
	}

	void print(ostream& os) {
		FOR (i, 0, 3) {
			FOR (j, 0, 3) os << mx[i][j] << ' ';
			os << endl;
		}
	}

	matrix operator *(matrix &other) {
		matrix res = matrix();
		FOR (i, 0, 3) FOR (j, 0, 3) res.mx[i][j] = 0;
		FOR (i, 0, 3) FOR (j, 0, 3) FOR (k, 0, 3) res.mx[i][j] += mx[i][k] * other.mx[k][j];
		return res;
	}
};

ostream& operator << (ostream& os, const matrix &m) {
	FOR (i, 0, 3) {
		FOR (j, 0, 3) os << m.mx[i][j] << ' ';
		os << endl;
	}
	return os;
}

template <typename T, typename TCoord>
struct Tree {
	int gp2(int n) {
		int x = 1;
		while (x < n) x += x;
		return x;
	}

	int n;
	T zero_value;
	vector<T> data;
	vector<TCoord> leftmost;
	vector<TCoord> rightmost;
	function<T(T&, T&)> combine;

	void init(function<T(T&,T&)> combine, T zero_value) {
		this->combine = combine;
		this->zero_value = zero_value;
		for (int i = n - 1 ; i ; i--) data[i] = combine(data[i + i] , data[i + i + 1]), leftmost[i] = leftmost[i + i], rightmost[i] = rightmost[i + i + 1];
	}

	void init_plain_coord() {
		leftmost = rightmost = vector<TCoord>(n + n, (TCoord) 1e20);
		for (int i = 0 ; i < n ; i++) leftmost[n + i] = rightmost[n + i] = i;
	}

	Tree() { }

	Tree(int n, function<T(T&,T&)> combine, T zero_value, T init_value) {
		int n1 = n;
		this->n = n = gp2(n);
		data.assign(n + n, zero_value);
		FOR (i, 0, n1) data[n + i] = init_value;

		init_plain_coord();
		init(combine, zero_value);
	}

	T getValue(TCoord l, TCoord r, int i = 1) {
		if (leftmost[i] >= l && rightmost[i] <= r) return data[i];
		if (rightmost[i] < l || leftmost[i] > r) return zero_value;

		auto val1 = getValue(l, r, i + i);
		auto val2 = getValue(l, r, i + i + 1);

		return combine(val1, val2);
	}

	void set(TCoord x, T value) {
		int i = 1;
		while (i < n) {
			i += i;
			if (rightmost[i] < x) i++;
		}
		data[i] = value;
		i /= 2;

		while (i) {
			data[i] = combine(data[i + i], data[i + i + 1]);
			i /= 2;
		}
	}

	void change(TCoord x, function<T(T)> changer) {
		int i = 1;
		while (i < n) {
			i += i;
			if (rightmost[i] < x) i++;
		}
		data[i] = changer(data[i]);
		i /= 2;

		while (i) {
			data[i] = combine(data[i + i], data[i + i + 1]);
			i /= 2;
		}
	}
};

const double PI = atan2(0, -1);

int main() {
	srand (time(NULL));
	ios_base::sync_with_stdio(false); cin.tie(NULL);
	fixed(cout); cout << setprecision(10);
	fixed(cerr); cerr << setprecision(10);

	next(int, n);
	next(int, m);

	Tree<matrix, int> segment_tree(n, [] (matrix &a, matrix &b) { return a * b; }, matrix(), matrix(1));

	vector<matrix> rotations;

	while (m --> 0) {
		next(int, t);
		next(int, i); i--;
		next(int, x);

		matrix change;
		if (t == 1) {
			change.mx[0][2] = x;
				segment_tree.change(i, [&change] (matrix m) { return m * change; });
			} else {
			while (rotations.size() <= x) {
				double a = rotations.size() / 180.0 * PI;
				rotations.push_back(matrix());
				rotations.back().mx[0][0] = cos(a);
				rotations.back().mx[0][1] = sin(a);
				rotations.back().mx[1][0] = -sin(a);
				rotations.back().mx[1][1] = cos(a);
			}

			change = rotations[x];
			segment_tree.change(i, [&change] (matrix m) { return change * m ; });
		}

		auto res = segment_tree.data[1];

		printf("%.17lf %.17lf\n", res.mx[0][2], res.mx[1][2]);
	}
}


#include "assert.h"
#include <algorithm>
#include <bitset>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <deque>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <time.h>
#include <vector>

using namespace std;

#if LOCAL
	#define DO_NOT_SEND
#endif

typedef long long LL;

int IntMaxVal = (int) 1e20;
int IntMinVal = (int) -1e20;
LL LongMaxVal = (LL) 1e20;
LL LongMinVal = (LL) -1e20;

#define FOR(i, a, b) for(int i = a; i < b ; ++i)
#define FORD(i, a, b) for(int i = a; i >= b; --i)

template<typename T> inline void minimize(T &a, T b) { a = std::min(a, b); }
template<typename T> inline void maximize(T &a, T b) { a = std::max(a, b); }

template<typename T> inline void swap(pair<T, T> &p) { swap(p.first , p.second ); }

#define all(v) v.begin(),v.end()

#define endl '\n'
template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define next(t, i) argument_type<void(t)>::type i; cin >> i;

template <typename T1, typename T2> istream& operator >>(istream& is, pair<T1, T2>& s) { is >> s.first >> s.second; return is; }
template <typename T> ostream& operator << (ostream& os, const vector<T> &v) { for (int i = 0 ; i < v.size() ; i++) { if (i) os << ' '; os << v[i]; } os << endl; return os; }
template <typename T1, typename T2> ostream& operator << (ostream& os, const vector<pair<T1, T2>> &v) { for (int i = 0 ; i < v.size() ; i++) { os << v[i] << endl; } return os; }
template <typename T1, typename T2> ostream& operator <<(ostream& s, const pair<T1, T2>& t) { s << t.first << ' ' << t.second; return s; }
template <typename T> vector<T> readVector(int n) { vector<T> res(n); for (int i = 0 ; i < n ; i++) cin >> res[i]; return res; }

struct matrix {
	vector<vector<double>> mx;

	matrix() {
		mx = vector<vector<double>>(3, vector<double>(3));
		mx[0][0] = mx[1][1] = mx[2][2] = 1;
	}

	matrix(int i) {
		mx = vector<vector<double>>(3, vector<double>(3));
		mx[0][0] = mx[1][1] = mx[2][2] = 1;
		mx[0][2] = 1;
	}

	matrix operator *(matrix &other) {
		matrix res = matrix();
		FOR (i, 0, 2) FOR (j, 0, 3) res.mx[i][j] = 0;
		FOR (i, 0, 2) FOR (j, 0, 3) FOR (k, 0, 3) res.mx[i][j] += mx[i][k] * other.mx[k][j];
		return res;
	}
};

template <typename T, typename TCoord>
struct Tree {
	int gp2(int n) {
		int x = 1;
		while (x < n) x += x;
		return x;
	}

	int n;
	T zero_value;
	vector<T> data;
	vector<TCoord> leftmost;
	vector<TCoord> rightmost;
	function<T(T&, T&)> combine;

	void init(function<T(T&,T&)> combine, T zero_value) {
		this->combine = combine;
		this->zero_value = zero_value;
		for (int i = n - 1 ; i ; i--) data[i] = combine(data[i + i] , data[i + i + 1]), leftmost[i] = leftmost[i + i], rightmost[i] = rightmost[i + i + 1];
	}

	void init_plain_coord() {
		leftmost = rightmost = vector<TCoord>(n + n, (TCoord) 1e20);
		for (int i = 0 ; i < n ; i++) leftmost[n + i] = rightmost[n + i] = i;
	}

	Tree() { }

	Tree(int n, function<T(T&,T&)> combine, T zero_value, T init_value) {
		int n1 = n;
		this->n = n = gp2(n);
		data.assign(n + n, zero_value);
		FOR (i, 0, n1) data[n + i] = init_value;

		init_plain_coord();
		init(combine, zero_value);
	}

	T getValue(TCoord l, TCoord r, int i = 1) {
		if (leftmost[i] >= l && rightmost[i] <= r) return data[i];
		if (rightmost[i] < l || leftmost[i] > r) return zero_value;

		auto val1 = getValue(l, r, i + i);
		auto val2 = getValue(l, r, i + i + 1);

		return combine(val1, val2);
	}

	void set(TCoord x, T value) {
		int i = 1;
		while (i < n) {
			i += i;
			if (rightmost[i] < x) i++;
		}
		data[i] = value;
		i /= 2;

		while (i) {
			data[i] = combine(data[i + i], data[i + i + 1]);
			i /= 2;
		}
	}

	void change(TCoord x, function<T(T)> changer) {
		int i = 1;
		while (i < n) {
			i += i;
			if (rightmost[i] < x) i++;
		}
		data[i] = changer(data[i]);
		i /= 2;

		while (i) {
			data[i] = combine(data[i + i], data[i + i + 1]);
			i /= 2;
		}
	}
};

const double PI = atan2(0, -1);

int main() {
	srand (time(NULL));
	ios_base::sync_with_stdio(false); cin.tie(NULL);
	fixed(cout); cout << setprecision(10);
	fixed(cerr); cerr << setprecision(10);

	next(int, n);
	next(int, m);

	Tree<matrix, int> segment_tree(n, [] (matrix &a, matrix &b) { return a * b; }, matrix(), matrix(1));

	vector<matrix> rotations;

	while (m --> 0) {
		next(int, t);
		next(int, i); i--;
		next(int, x);

		matrix change;
		if (t == 1) {
			change.mx[0][2] = x;
				segment_tree.change(i, [&change] (matrix m) { return m * change; });
			} else {
			while (rotations.size() <= x) {
				double a = rotations.size() / 180.0 * PI;
				rotations.push_back(matrix());
				rotations.back().mx[0][0] = cos(a);
				rotations.back().mx[0][1] = sin(a);
				rotations.back().mx[1][0] = -sin(a);
				rotations.back().mx[1][1] = cos(a);
			}

			change = rotations[x];
			segment_tree.change(i, [&change] (matrix m) { return change * m ; });
		}

		auto res = segment_tree.data[1];

		printf("%.17lf %.17lf\n", res.mx[0][2], res.mx[1][2]);
	}
}


#include "assert.h"
#include <algorithm>
#include <bitset>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <deque>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <time.h>
#include <vector>

using namespace std;

#if LOCAL
	#define DO_NOT_SEND
#endif

typedef long long LL;

int IntMaxVal = (int) 1e20;
int IntMinVal = (int) -1e20;
LL LongMaxVal = (LL) 1e20;
LL LongMinVal = (LL) -1e20;

#define FOR(i, a, b) for(int i = a; i < b ; ++i)
#define FORD(i, a, b) for(int i = a; i >= b; --i)

template<typename T> inline void minimize(T &a, T b) { a = std::min(a, b); }
template<typename T> inline void maximize(T &a, T b) { a = std::max(a, b); }

template<typename T> inline void swap(pair<T, T> &p) { swap(p.first , p.second ); }

#define all(v) v.begin(),v.end()

#define endl '\n'
template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define next(t, i) argument_type<void(t)>::type i; cin >> i;

template <typename T1, typename T2> istream& operator >>(istream& is, pair<T1, T2>& s) { is >> s.first >> s.second; return is; }
template <typename T> ostream& operator << (ostream& os, const vector<T> &v) { for (int i = 0 ; i < v.size() ; i++) { if (i) os << ' '; os << v[i]; } os << endl; return os; }
template <typename T1, typename T2> ostream& operator << (ostream& os, const vector<pair<T1, T2>> &v) { for (int i = 0 ; i < v.size() ; i++) { os << v[i] << endl; } return os; }
template <typename T1, typename T2> ostream& operator <<(ostream& s, const pair<T1, T2>& t) { s << t.first << ' ' << t.second; return s; }
template <typename T> vector<T> readVector(int n) { vector<T> res(n); for (int i = 0 ; i < n ; i++) cin >> res[i]; return res; }

struct matrix {
	vector<vector<double>> mx;

	matrix() {
		mx = vector<vector<double>>(3, vector<double>(3));
		mx[0][0] = mx[1][1] = mx[2][2] = 1;
	}

	matrix(int i) {
		mx = vector<vector<double>>(3, vector<double>(3));
		mx[0][0] = mx[1][1] = mx[2][2] = 1;
		mx[0][2] = 1;
	}

	void print(ostream& os) {
		FOR (i, 0, 3) {
			FOR (j, 0, 3) os << mx[i][j] << ' ';
			os << endl;
		}
	}

	matrix operator *(matrix &other) {
		matrix res = matrix();
		FOR (i, 0, 3) FOR (j, 0, 3) res.mx[i][j] = 0;
		FOR (i, 0, 3) FOR (j, 0, 3) FOR (k, 0, 3) res.mx[i][j] += mx[i][k] * other.mx[k][j];
		return res;
	}
};

ostream& operator << (ostream& os, const matrix &m) {
	FOR (i, 0, 3) {
		FOR (j, 0, 3) os << m.mx[i][j] << ' ';
		os << endl;
	}
	return os;
}

template <typename T, typename TCoord>
struct Tree {
	int gp2(int n) {
		int x = 1;
		while (x < n) x += x;
		return x;
	}

	int n;
	T zero_value;
	vector<T> data;
	vector<TCoord> leftmost;
	vector<TCoord> rightmost;
	function<T(T&, T&)> combine;

	void init(function<T(T&,T&)> combine, T zero_value) {
		this->combine = combine;
		this->zero_value = zero_value;
		for (int i = n - 1 ; i ; i--) data[i] = combine(data[i + i] , data[i + i + 1]), leftmost[i] = leftmost[i + i], rightmost[i] = rightmost[i + i + 1];
	}

	void init_plain_coord() {
		leftmost = rightmost = vector<TCoord>(n + n, (TCoord) 1e20);
		for (int i = 0 ; i < n ; i++) leftmost[n + i] = rightmost[n + i] = i;
	}

	Tree() { }

	Tree(int n, function<T(T&,T&)> combine, T zero_value, T init_value) {
		int n1 = n;
		this->n = n = gp2(n);
		data.assign(n + n, zero_value);
		FOR (i, 0, n1) data[n + i] = init_value;

		init_plain_coord();
		init(combine, zero_value);
	}

	T getValue(TCoord l, TCoord r, int i = 1) {
		if (leftmost[i] >= l && rightmost[i] <= r) return data[i];
		if (rightmost[i] < l || leftmost[i] > r) return zero_value;

		auto val1 = getValue(l, r, i + i);
		auto val2 = getValue(l, r, i + i + 1);

		return combine(val1, val2);
	}

	void set(TCoord x, T value) {
		int i = 1;
		while (i < n) {
			i += i;
			if (rightmost[i] < x) i++;
		}
		data[i] = value;
		i /= 2;

		while (i) {
			data[i] = combine(data[i + i], data[i + i + 1]);
			i /= 2;
		}
	}

	void change(TCoord x, function<T(T)> changer) {
		int i = 1;
		while (i < n) {
			i += i;
			if (rightmost[i] < x) i++;
		}
		data[i] = changer(data[i]);
		i /= 2;

		while (i) {
			data[i] = combine(data[i + i], data[i + i + 1]);
			i /= 2;
		}
	}
};

const double PI = atan2(0, -1);

int main() {
	srand (time(NULL));
	ios_base::sync_with_stdio(false); cin.tie(NULL);
	fixed(cout); cout << setprecision(10);
	fixed(cerr); cerr << setprecision(10);

	next(int, n);
	next(int, m);

	Tree<matrix, int> segment_tree(n, [] (matrix &a, matrix &b) { return a * b; }, matrix(), matrix(1));

	while (m --> 0) {
		next(int, t);
		next(int, i); i--;
		next(int, x);

		matrix change;
		if (t == 1) {
			change.mx[0][2] = x;
			segment_tree.change(i, [&change] (matrix m) { return m * change ; });
		} else {
			double a = x / 180.0 * PI;
			change.mx[0][0] = cos(a);
			change.mx[0][1] = sin(a);
			change.mx[1][0] = -sin(a);
			change.mx[1][1] = cos(a);
			segment_tree.change(i, [&change] (matrix m) { return change * m ; });
		}

		auto res = segment_tree.data[1];

		printf("%.17lf %.17lf\n", res.mx[0][2], res.mx[1][2]);
	}
}


#include "assert.h"
#include <algorithm>
#include <bitset>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <deque>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <time.h>
#include <vector>

using namespace std;

#if LOCAL
	#define DO_NOT_SEND
#endif

typedef long long LL;

int IntMaxVal = (int) 1e20;
int IntMinVal = (int) -1e20;
LL LongMaxVal = (LL) 1e20;
LL LongMinVal = (LL) -1e20;

#define FOR(i, a, b) for(int i = a; i < b ; ++i)
#define FORD(i, a, b) for(int i = a; i >= b; --i)

template<typename T> inline void minimize(T &a, T b) { a = std::min(a, b); }
template<typename T> inline void maximize(T &a, T b) { a = std::max(a, b); }

template<typename T> inline void swap(pair<T, T> &p) { swap(p.first , p.second ); }

#define all(v) v.begin(),v.end()

#define endl '\n'
template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define next(t, i) argument_type<void(t)>::type i; cin >> i;

template <typename T1, typename T2> istream& operator >>(istream& is, pair<T1, T2>& s) { is >> s.first >> s.second; return is; }
template <typename T> ostream& operator << (ostream& os, const vector<T> &v) { for (int i = 0 ; i < v.size() ; i++) { if (i) os << ' '; os << v[i]; } os << endl; return os; }
template <typename T1, typename T2> ostream& operator << (ostream& os, const vector<pair<T1, T2>> &v) { for (int i = 0 ; i < v.size() ; i++) { os << v[i] << endl; } return os; }
template <typename T1, typename T2> ostream& operator <<(ostream& s, const pair<T1, T2>& t) { s << t.first << ' ' << t.second; return s; }
template <typename T> vector<T> readVector(int n) { vector<T> res(n); for (int i = 0 ; i < n ; i++) cin >> res[i]; return res; }

struct matrix {
	vector<vector<double>> mx;

	matrix() {
		mx = vector<vector<double>>(3, vector<double>(3));
		mx[0][0] = mx[1][1] = mx[2][2] = 1;
	}

	matrix(int i) {
		mx = vector<vector<double>>(3, vector<double>(3));
		mx[0][0] = mx[1][1] = mx[2][2] = 1;
		mx[0][2] = 1;
	}

	void print(ostream& os) {
		FOR (i, 0, 3) {
			FOR (j, 0, 3) os << mx[i][j] << ' ';
			os << endl;
		}
	}

	matrix operator *(matrix &other) {
		matrix res = matrix();
		FOR (i, 0, 3) FOR (j, 0, 3) res.mx[i][j] = 0;
		FOR (i, 0, 3) FOR (j, 0, 3) FOR (k, 0, 3) res.mx[i][j] += mx[i][k] * other.mx[k][j];
		return res;
	}
};

ostream& operator << (ostream& os, const matrix &m) {
	FOR (i, 0, 3) {
		FOR (j, 0, 3) os << m.mx[i][j] << ' ';
		os << endl;
	}
	return os;
}

template <typename T, typename TCoord>
struct Tree {
	int gp2(int n) {
		int x = 1;
		while (x < n) x += x;
		return x;
	}

	int n;
	T zero_value;
	vector<T> data;
	vector<TCoord> leftmost;
	vector<TCoord> rightmost;
	function<T(T&, T&)> combine;

	void init(function<T(T&,T&)> combine, T zero_value) {
		this->combine = combine;
		this->zero_value = zero_value;
		for (int i = n - 1 ; i ; i--) data[i] = combine(data[i + i] , data[i + i + 1]), leftmost[i] = leftmost[i + i], rightmost[i] = rightmost[i + i + 1];
	}

	void init_plain_coord() {
		leftmost = rightmost = vector<TCoord>(n + n, (TCoord) 1e20);
		for (int i = 0 ; i < n ; i++) leftmost[n + i] = rightmost[n + i] = i;
	}

	Tree() { }

	Tree(int n, function<T(T&,T&)> combine, T zero_value, T init_value) {
		int n1 = n;
		this->n = n = gp2(n);
		data.assign(n + n, zero_value);
		FOR (i, 0, n1) data[n + i] = init_value;

		init_plain_coord();
		init(combine, zero_value);
	}

	T getValue(TCoord l, TCoord r, int i = 1) {
		if (leftmost[i] >= l && rightmost[i] <= r) return data[i];
		if (rightmost[i] < l || leftmost[i] > r) return zero_value;

		auto val1 = getValue(l, r, i + i);
		auto val2 = getValue(l, r, i + i + 1);

		return combine(val1, val2);
	}

	void set(TCoord x, T value) {
		int i = 1;
		while (i < n) {
			i += i;
			if (rightmost[i] < x) i++;
		}
		data[i] = value;
		i /= 2;

		while (i) {
			data[i] = combine(data[i + i], data[i + i + 1]);
			i /= 2;
		}
	}

	void change(TCoord x, function<T(T)> changer) {
		int i = 1;
		while (i < n) {
			i += i;
			if (rightmost[i] < x) i++;
		}
		data[i] = changer(data[i]);
		i /= 2;

		while (i) {
			data[i] = combine(data[i + i], data[i + i + 1]);
			i /= 2;
		}
	}
};

const double PI = atan2(0, -1);

int main() {
	srand (time(NULL));
	ios_base::sync_with_stdio(false); cin.tie(NULL);
	fixed(cout); cout << setprecision(10);
	fixed(cerr); cerr << setprecision(10);

	next(int, n);
	next(int, m);

	Tree<matrix, int> segment_tree(n, [] (matrix &a, matrix &b) { return a * b; }, matrix(), matrix(1));

	vector<matrix> rotations;

	while (m --> 0) {
		next(int, t);
		next(int, i); i--;
		next(int, x);

		matrix change;
		if (t == 1) {
			change.mx[0][2] = x;
		} else {
			while (rotations.size() <= x) {
				double a = rotations.size() / 180.0 * PI;
				rotations.push_back(matrix());
				rotations.back().mx[0][0] = cos(a);
				rotations.back().mx[0][1] = sin(a);
				rotations.back().mx[1][0] = -sin(a);
				rotations.back().mx[1][1] = cos(a);
			}

			change = rotations[x];
		}
		segment_tree.change(i, [&change] (matrix m) { return change * m ; });

		auto res = segment_tree.data[1];

		printf("%.17lf %.17lf\n", res.mx[0][2], res.mx[1][2]);
	}
}


#include <bits/stdc++.h>
using namespace std;
#define fo(i,n) for(int i=0;i<(n);i++)

int N, M, t, n;
long double c, z[1200200];

struct node {
	long double sz, d;
} r[1200200];

node m (node a, node b) {
	complex<long double> p = polar(a.sz, a.d) + polar(b.sz, b.d);
	return {abs(p), arg(p)};
}
void take (int i) { r[i] = m(r[i*2+1], r[i*2+2]), r[i].d += z[i]; }

void R (int i, int rs, int re, int qs, int qe, long double d) {
	if (rs == qs && re == qe) r[i].d += d, z[i] += d;
	else {
		int m = (rs+re)/2;
		if (qs<m) R(i*2+1, rs, m, qs, min(m, qe), d);
		if (qe>m) R(i*2+2, m, re, max(qs, m), qe, d);
		take(i);
	}
}

void U (int i, int rs, int re, int p, long double v) {
	if (rs == re-1) r[i].sz += v;
	else {
		int m = (rs+re)/2;
		if (p<m) U(i*2+1, rs, m, p, v);
		else U(i*2+2, m, re, p, v);
		take(i);
	}
}

int main () {
	scanf("%d %d", &N, &M);
	fo(i, N) U(0, 0, N, i, 1);
	fo(i, M) {
		scanf("%d %d %lf", &t, &n, &c), n--;
		if (t == 1) U(0, 0, N, n, c);
		else R(0, 0, N, n, N, -c * acos(-1.) / 180.);
		printf("%.13lf %.13lf\n", cos(r[0].d) * r[0].sz, sin(r[0].d) * r[0].sz);
	}
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
#define fo(i,n) for(int i=0;i<(n);i++)

int N, M, t, n, e;
long double c, z[1200200];

struct node {
	long double sz, d;
} r[1200200];

node m (node a, node b) {
	complex<long double> p = polar(a.sz, a.d) + polar(b.sz, b.d);
	return {abs(p), arg(p)};
}
void take (int i) { r[i] = m(r[i*2+1], r[i*2+2]), r[i].d += z[i]; }

void R (int i, int rs, int re, int qs, int qe, long double d) {
	if (rs == qs && re == qe) r[i].d += d, z[i] += d;
	else {
		int m = (rs+re)/2;
		if (qs<m) R(i*2+1, rs, m, qs, min(m, qe), d);
		if (qe>m) R(i*2+2, m, re, max(qs, m), qe, d);
		take(i);
	}
}

void U (int i, int rs, int re, int p, long double v) {
	if (rs == re-1) r[i].sz += v;
	else {
		int m = (rs+re)/2;
		if (p<m) U(i*2+1, rs, m, p, v);
		else U(i*2+2, m, re, p, v);
		take(i);
	}
}

int main () {
	scanf("%d %d", &N, &M);
	fo(i, N) U(0, 0, N, i, 1);
	fo(i, M) {
		scanf("%d %d %d", &t, &n, &e), n--;
		c = e;
		if (t == 1) U(0, 0, N, n, c);
		else R(0, 0, N, n, N, -c * acos(-1.) / 180.);
		printf("%.13lf %.13lf\n", double(cos(r[0].d) * r[0].sz), double(sin(r[0].d) * r[0].sz));
	}
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
#define fo(i,n) for(int i=0;i<(n);i++)

int N, M, t, n;
double c, z[1200200];

struct node {
	double sz, d;
} r[1200200];

node m (node a, node b) {
	complex<double> p = polar(a.sz, a.d) + polar(b.sz, b.d);
	return {abs(p), arg(p)};
}
void take (int i) { r[i] = m(r[i*2+1], r[i*2+2]), r[i].d += z[i]; }

void R (int i, int rs, int re, int qs, int qe, double d) {
	if (rs == qs && re == qe) r[i].d += d, z[i] += d;
	else {
		int m = (rs+re)/2;
		if (qs<m) R(i*2+1, rs, m, qs, min(m, qe), d);
		if (qe>m) R(i*2+2, m, re, max(qs, m), qe, d);
		take(i);
	}
}

void U (int i, int rs, int re, int p, double v) {
	if (rs == re-1) r[i].sz += v;
	else {
		int m = (rs+re)/2;
		if (p<m) U(i*2+1, rs, m, p, v);
		else U(i*2+2, m, re, p, v);
		take(i);
	}
}

int main () {
	scanf("%d %d", &N, &M);
	fo(i, N) U(0, 0, N, i, 1);
	fo(i, M) {
		scanf("%d %d %lf", &t, &n, &c), n--;
		if (t == 1) U(0, 0, N, n, c);
		else R(0, 0, N, n, N, -c * acos(-1.) / 180.);
		printf("%.13lf %.13lf\n", cos(r[0].d) * r[0].sz, sin(r[0].d) * r[0].sz);
	}
	return 0;
}


#include <bits/stdc++.h>
#define F first
#define S second
#define X real()
#define Y imag()
using namespace std;
typedef long long ll;
typedef double ld;
typedef complex<ld> co;
const ld PI=atan2(0, -1);

const int N=1<<19;

co st[2*N];
int is[2*N][2];
co lz[2*N][2];

void add(int, int, int, int, int, co);
void mul(int, int, int, int, int, co);

void golazy(int i, int l, int r){
	int m=(l+r)/2;
	if (is[i][1]){
		mul(i*2, l, m, l, m, lz[i][1]);
		mul(i*2+1, m+1, r, m+1, r, lz[i][1]);
		is[i][1]=0;
		lz[i][1]={1, 0};
	}
	if (is[i][0]){
		add(i*2, l, m, l, m, lz[i][0]),
		add(i*2+1, m+1, r, m+1, r, lz[i][0]);
		is[i][0]=0;
		lz[i][0]={0, 0};
	}
}

void add(int i, int stl, int str, int l, int r, co v){
	if (stl>r||str<l) return;
	if (l<=stl&&str<=r){
		st[i]+=v;
		is[i][0]=1;
		lz[i][0]+=v;
	}
	else{
		golazy(i, stl, str);
		int m=(stl+str)/2;
		add(i*2, stl, m, l, r, v);
		add(i*2+1, m+1, str, l, r, v);
	}
}

void mul(int i, int stl, int str, int l, int r, co v){
	if (stl>r||str<l) return;
	if (l<=stl&&str<=r){
		st[i]*=v;
		lz[i][0]*=v;
		is[i][1]=1;
		lz[i][1]*=v;
	}
	else{
		golazy(i, stl, str);
		int m=(stl+str)/2;
		mul(i*2, stl, m, l, r, v);
		mul(i*2+1, m+1, str, l, r, v);
	}
}

co get(int i, int stl, int str, int x){
	if (stl==str){
		return st[i];
	}
	else{
		golazy(i, stl, str);
		int m=(stl+str)/2;
		if (x<=m){
			return get(i*2, stl, m, x);
		}
		else{
			return get(i*2+1, m+1, str, x);
		}
	}
}

int main(){
	ios_base::sync_with_stdio(0);
	cin.tie(0);
	int n,m;
	cin>>n>>m;
	for (int i=0;i<2*N;i++){
		lz[i][1]={1, 0};
	}
	for (int i=1;i<=n+1;i++){
		add(1, 0, N-1, i, i, {(ld)(i-1), 0});
	}
	for (int qq=0;qq<m;qq++){
		int x,y,z;
		cin>>x>>y>>z;
		if (x==1){
			co p1=get(1, 0, N-1, y);
			co p2=get(1, 0, N-1, y+1);
			co u=((p2-p1)/abs(p2-p1))*(ld)z;
			add(1, 0, N-1, y+1, n+1, u);
		}
		if (x==2){
			co A=get(1, 0, N-1, y);
			co a=polar((ld)1, -((ld)z/(ld)180*PI));
			mul(1, 0, N-1, y+1, n+1, a);
			add(1, 0, N-1, y+1, n+1, -(A*a)+A);
		}
		co v=get(1, 0, N-1, n+1);
		cout<<setprecision(15)<<v.X<<" "<<v.Y<<'\n';
	}
}


#include <bits/stdc++.h>
#define F first
#define S second
#define X real()
#define Y imag()
using namespace std;
typedef long long ll;
typedef long double ld;
typedef complex<ld> co;
const ld PI=atan2(0, -1);

const int N=1<<19;

co st[2*N];
int is[2*N][2];
co lz[2*N][2];

void add(int, int, int, int, int, co);
void mul(int, int, int, int, int, co);

void golazy(int i, int l, int r){
	int m=(l+r)/2;
	if (is[i][1]){
		mul(i*2, l, m, l, m, lz[i][1]);
		mul(i*2+1, m+1, r, m+1, r, lz[i][1]);
		is[i][1]=0;
		lz[i][1]={1, 0};
	}
	if (is[i][0]){
		add(i*2, l, m, l, m, lz[i][0]),
		add(i*2+1, m+1, r, m+1, r, lz[i][0]);
		is[i][0]=0;
		lz[i][0]={0, 0};
	}
}

void add(int i, int stl, int str, int l, int r, co v){
	if (stl>r||str<l) return;
	if (l<=stl&&str<=r){
		st[i]+=v;
		is[i][0]=1;
		lz[i][0]+=v;
	}
	else{
		golazy(i, stl, str);
		int m=(stl+str)/2;
		add(i*2, stl, m, l, r, v);
		add(i*2+1, m+1, str, l, r, v);
	}
}

void mul(int i, int stl, int str, int l, int r, co v){
	if (stl>r||str<l) return;
	if (l<=stl&&str<=r){
		st[i]*=v;
		lz[i][0]*=v;
		is[i][1]=1;
		lz[i][1]*=v;
	}
	else{
		golazy(i, stl, str);
		int m=(stl+str)/2;
		mul(i*2, stl, m, l, r, v);
		mul(i*2+1, m+1, str, l, r, v);
	}
}

co get(int i, int stl, int str, int x){
	if (stl==str){
		return st[i];
	}
	else{
		golazy(i, stl, str);
		int m=(stl+str)/2;
		if (x<=m){
			return get(i*2, stl, m, x);
		}
		else{
			return get(i*2+1, m+1, str, x);
		}
	}
}

int main(){
	ios_base::sync_with_stdio(0);
	cin.tie(0);
	int n,m;
	cin>>n>>m;
	for (int i=0;i<2*N;i++){
		lz[i][1]={1, 0};
	}
	for (int i=1;i<=n+1;i++){
		st[N+i]={(ld)(i-1), 0};
	}
	for (int qq=0;qq<m;qq++){
		int x,y,z;
		cin>>x>>y>>z;
		if (x==1){
			co p1=get(1, 0, N-1, y);
			co p2=get(1, 0, N-1, y+1);
			co u=((p2-p1)/abs(p2-p1))*(ld)z;
			add(1, 0, N-1, y+1, n+1, u);
		}
		if (x==2){
			co A=get(1, 0, N-1, y);
			co a=polar((ld)1, -((ld)z/(ld)180*PI));
			mul(1, 0, N-1, y+1, n+1, a);
			add(1, 0, N-1, y+1, n+1, -(A*a)+A);
		}
		co v=get(1, 0, N-1, n+1);
		cout<<setprecision(15)<<v.X<<" "<<v.Y<<'\n';
	}
}


#include <bits/stdc++.h>
#define F first
#define S second
#define X real()
#define Y imag()
using namespace std;
typedef long long ll;
typedef long double ld;
typedef complex<ld> co;
const ld PI=atan2(0, -1);

const int N=1<<19;

co st[2*N];
int is[2*N][2];
co lz[2*N][2];

void add(int, int, int, int, int, co);
void mul(int, int, int, int, int, co);

void golazy(int i, int l, int r){
	int m=(l+r)/2;
	if (is[i][1]){
		mul(i*2, l, m, l, m, lz[i][1]);
		mul(i*2+1, m+1, r, m+1, r, lz[i][1]);
		is[i][1]=0;
		lz[i][1]={1, 0};
	}
	if (is[i][0]){
		add(i*2, l, m, l, m, lz[i][0]),
		add(i*2+1, m+1, r, m+1, r, lz[i][0]);
		is[i][0]=0;
		lz[i][0]={0, 0};
	}
}

void add(int i, int stl, int str, int l, int r, co v){
	if (stl>r||str<l) return;
	if (l<=stl&&str<=r){
		st[i]+=v;
		is[i][0]=1;
		lz[i][0]+=v;
	}
	else{
		golazy(i, stl, str);
		int m=(stl+str)/2;
		add(i*2, stl, m, l, r, v);
		add(i*2+1, m+1, str, l, r, v);
	}
}

void mul(int i, int stl, int str, int l, int r, co v){
	if (stl>r||str<l) return;
	if (l<=stl&&str<=r){
		st[i]*=v;
		lz[i][0]*=v;
		is[i][1]=1;
		lz[i][1]*=v;
	}
	else{
		golazy(i, stl, str);
		int m=(stl+str)/2;
		mul(i*2, stl, m, l, r, v);
		mul(i*2+1, m+1, str, l, r, v);
	}
}

co get(int i, int stl, int str, int x){
	if (stl==str){
		return st[i];
	}
	else{
		golazy(i, stl, str);
		int m=(stl+str)/2;
		if (x<=m){
			return get(i*2, stl, m, x);
		}
		else{
			return get(i*2+1, m+1, str, x);
		}
	}
}

int main(){
	ios_base::sync_with_stdio(0);
	cin.tie(0);
	int n,m;
	cin>>n>>m;
	for (int i=0;i<2*N;i++){
		lz[i][1]={1, 0};
	}
	for (int i=1;i<=n+1;i++){
		add(1, 0, N-1, i, i, {(ld)(i-1), 0});
	}
	for (int qq=0;qq<m;qq++){
		int x,y,z;
		cin>>x>>y>>z;
		if (x==1){
			co p1=get(1, 0, N-1, y);
			co p2=get(1, 0, N-1, y+1);
			co u=((p2-p1)/abs(p2-p1))*(ld)z;
			add(1, 0, N-1, y+1, n+1, u);
		}
		if (x==2){
			co A=get(1, 0, N-1, y);
			co a=polar((ld)1, -((ld)z/(ld)180*PI));
			mul(1, 0, N-1, y+1, n+1, a);
			add(1, 0, N-1, y+1, n+1, -(A*a)+A);
		}
		co v=get(1, 0, N-1, n+1);
		cout<<setprecision(15)<<v.X<<" "<<v.Y<<'\n';
	}
}


#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>

using namespace std;

const double pi=acos(-1);

struct node
{
	double a[2][2];
	int r,c;
	node(int x=0,int y=0){a[0][0]=a[0][1]=a[1][0]=a[1][1]=0.0;r=x;c=y;}
};

node I(int n)
{
	node a;a.r=a.c=n;
	for(int i=0;i<n;i++)
		a.a[i][i]=1.0;
	return a;
}

node operator +(const node& a,const node& b)
{
	node ans;ans.r=a.r;ans.c=a.c;
	for(int i=0;i<a.r;i++)
		for(int j=0;j<a.c;j++)
			ans.a[i][j]=a.a[i][j]+b.a[i][j];
	return ans;
}

node operator -(const node& a,const node& b)
{
	node ans;ans.r=a.r;ans.c=a.c;
	for(int i=0;i<a.r;i++)
		for(int j=0;j<a.c;j++)
			ans.a[i][j]=a.a[i][j]-b.a[i][j];
	return ans;
}

node operator *(const node& a,const node& b)
{
	node ans;ans.r=a.r;ans.c=b.c;
	for(int i=0;i<a.r;i++)
		for(int j=0;j<b.c;j++)
			for(int k=0;k<a.c;k++)
				ans.a[i][j]+=a.a[i][k]*b.a[k][j];
	return ans;
}

node operator *(const node& a,const double& b)
{
	node ans;ans.r=a.r;ans.c=a.c;
	for(int i=0;i<a.r;i++)
		for(int j=0;j<a.c;j++)
			ans.a[i][j]=a.a[i][j]*b;
	return ans;
}

struct Tree
{
	node mul,add;
}T[1200010];

void pushdown(int root)
{
	T[root<<1].mul=T[root<<1].mul*T[root].mul;
	T[root<<1|1].mul=T[root<<1|1].mul*T[root].mul;
	T[root<<1].add=(T[root<<1].add*T[root].mul)+T[root].add;
	T[root<<1|1].add=(T[root<<1|1].add*T[root].mul)+T[root].add;
	T[root].mul=I(2);T[root].add=node(1,2);
	return ;
}

void build(int root,int l,int r)
{
	T[root].mul=I(2);T[root].add=node(1,2);
	if(l==r){
		node tmp(1,2);
		tmp.a[0][0]=l;tmp.a[0][1]=0;
		T[root].add=tmp;
		return ;
	}
	int mid=(l+r)>>1;
	build(root<<1,l,mid);
	build(root<<1|1,mid+1,r);
	return ;
}

void add(int root,int l,int r,int x,int y,node ad)
{
	if(x<=l&&r<=y){
		T[root].add=T[root].add+ad;
		return ;
	}
	pushdown(root);
	int mid=(l+r)>>1;
	if(x<=mid)add(root<<1,l,mid,x,y,ad);
	if(y>mid)add(root<<1|1,mid+1,r,x,y,ad);
	return ;
}

void nodeul(int root,int l,int r,int x,int y,node ad)
{
	if(x<=l&&r<=y){
		T[root].mul=T[root].mul*ad;
		T[root].add=T[root].add*ad;
		return ;
	}
	pushdown(root);
	int mid=(l+r)>>1;
	if(x<=mid)nodeul(root<<1,l,mid,x,y,ad);
	if(y>mid)nodeul(root<<1|1,mid+1,r,x,y,ad);
	return ;
}

node query(int root,int l,int r,int x)
{
	if(l==r)return T[root].add;
	pushdown(root);
	int mid=(l+r)>>1;
	if(x<=mid)return query(root<<1,l,mid,x);
	else return query(root<<1|1,mid+1,r,x);
}

double dist(node a,node b)
{
	a=a-b;
	return sqrt(a.a[0][0]*a.a[0][0]+a.a[0][1]*a.a[0][1]);
}

node rotate(double s)
{
	node ans(2,2);
	ans.a[0][0]=cos(s);
	ans.a[0][1]=sin(s);
	ans.a[1][0]=-sin(s);
	ans.a[1][1]=cos(s);
	return ans;
}
int main()
{
	int n,m;
	scanf("%d %d",&n,&m);
	build(1,1,n);
	for(int i=1;i<=m;i++)
	{
		int x,y,z;
		scanf("%d %d %d",&x,&y,&z);
		if(x==1){
			node a(2,2),B(2,2);
			if(y>1)a=query(1,1,n,y-1);
			B=query(1,1,n,y);
			add(1,1,n,y,n,(B-a)*(double)z*(1/dist(a,B)));
		}
		else{
			node a(2,2);
			if(y>1)a=query(1,1,n,y-1);
			add(1,1,n,y,n,a*(-1));
			nodeul(1,1,n,y,n,rotate(((double)360-z)/180*pi));
			add(1,1,n,y,n,a);
		}
		node ans=query(1,1,n,n);
		printf("%.10lf %.10lf\n",ans.a[0][0],ans.a[0][1]);
	}
	return 0;
}


#include <cmath>
#include <cstdio>
#define PI acos(-1)
int v, c;
template <class T> T read(T& x) {
	x = 0; v = 1; c = getchar();
	for (; c < '0' || c > '9'; c = getchar()) if (c == '-') v = -1;
	for (; c >= '0' && c <= '9'; c = getchar()) x = (x << 3) + (x << 1) + (c ^ 48);
	return x *= v;
}
const int N = 300010;
const double EPS = 1e-9;
int Q, n, opt, x, y;
struct Edge {
	double x1, y1, x2, y2;
} e[N];
void add(int x, double y) {
	double xx = e[x].x2 - e[x].x1;
	double yy = e[x].y2 - e[x].y1;
	double len = sqrt(xx * xx + yy * yy);
	double dx = y / len * xx;
	double dy = y / len * yy;
	e[x].x2 += dx;
	e[x].y2 += dy;
	for (register int i = x + 1; i <= n; ++i) {
		e[i].x1 += dx;
		e[i].x2 += dx;
		e[i].y1 += dy;
		e[i].y2 += dy;
	}
}
void rot(double x1, double y1, double& x2, double& y2, double ang) {
	double xx = x2 - x1;
	double yy = y2 - y1;
	double dx = xx * cos(ang) - yy * sin(ang);
	double dy = xx * sin(ang) + yy * cos(ang);
	x2 = x1 + dx;
	y2 = y1 + dy;
}
void rotate(int x, int y) {
	y = 360 - y % 360;
	if (y == 360) return;
	double ang = double(y) / 180.0 * PI;
	rot(e[x].x1, e[x].y1, e[x].x2, e[x].y2, ang);
	for (register int i = x + 1; i <= n; ++i) {
		rot(e[x].x1, e[x].y1, e[i].x1, e[i].y1, ang);
		rot(e[x].x1, e[x].y1, e[i].x2, e[i].y2, ang);
	}
}
int main() {
	read(n); read(Q);
	for (register int i = 1; i <= n; ++i) {
		e[i].x1 = double(i) - 1.0;
		e[i].x2 = double(i);
	}
	for (; Q; --Q) {
		read(opt);
		read(x); read(y);
		if (opt & 1) add(x, y);
		else rotate(x, y);
		printf("%.10lf %.10lf\n", e[n].x2, e[n].y2);
	}
	return 0;
}


#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<stack>
#include<queue>
#include<iomanip>
#include<map>
#include<ctime>
#include<cstdlib>
#define ls qk<<1
#define rs qk<<1|1
using namespace std;
const double pi=acos(-1.0);
inline double getsin(double du)
{
	return sin(du/180*pi);
}
inline double getcos(double du)
{
	return cos(du/180*pi);
}
inline double myacos(double zhi)
{
	return acos(zhi)*180/pi;
}
inline double myasin(double zhi)
{
	return asin(zhi)*180/pi;
}
struct lov
{
	double len,alf,tto;
}tree[1200010];
inline lov unio(lov a,lov b)
{
	lov ret;
	double totalf=a.tto+b.alf;
	if(totalf>=360)totalf=totalf-360;
//  cout<<"a:   "<<a.alf<<" "<<a.len<<'\n';
//  cout<<"b:   "<<b.alf<<" "<<b.len<<'\n';
//	double ax=;cout<<fixed<<setprecision(8)<<ax<<" ";
//	double ay=;cout<<fixed<<setprecision(8)<<ay<<" ";
//	double bx=;cout<<fixed<<setprecision(8)<<bx<<" ";
//	double by=;cout<<fixed<<setprecision(8)<<by<<"\n";
	double zzx=ax+bx;a.len*getcos(a.alf)+b.len*getcos(totalf);
	double zzy=ay+by;a.len*getsin(a.alf)+b.len*getsin(totalf);
	ret.len=sqrt(zzx*zzx+zzy*zzy);
	ret.alf=myacos(fabs(zzx)/ret.len);
	if(zzx>=0)
	{
		if(zzy>=0);
		else ret.alf=(double)360-ret.alf;
	}
	else
	{
		if(zzy>=0)ret.alf=(double)180-ret.alf;
		else ret.alf=ret.alf+180;
	}
//  cout<<"ret: "<<ret.alf<<" "<<ret.len<<'\n';
	ret.tto=a.tto+b.tto;
	return ret;
}
void build(int qk,int l,int r)
{
	if(l==r)
	{
		tree[qk].len=1;
		tree[qk].alf=0;
		tree[qk].tto=0;
		return;
	}
	int mid=l+r>>1;
	build(ls,l,mid);
	build(rs,mid+1,r);
	tree[qk].tto=tree[qk].alf=0;
	tree[qk].len=tree[ls].len+tree[rs].len;
}
void settagj(int qk,int l,int r,int x,double len)
{
	if(l==r)
	{
		tree[qk].len+=len;
		return;
	}
	int mid=l+r>>1;
	if(x<=mid)settagj(ls,l,mid,x,len);
	else settagj(rs,mid+1,r,x,len);
	tree[qk]=unio(tree[ls],tree[rs]);
}
void settagt(int qk,int l,int r,int x,double du)
{
	if(l==r)
	{
		tree[qk].alf+=du;tree[qk].tto+=du;
		if(tree[qk].alf>=360)tree[qk].alf=tree[qk].alf-360;
		if(tree[qk].tto>=360)tree[qk].tto=tree[qk].tto-360;
		return;
	}
	int mid=l+r>>1;
	if(x<=mid)settagt(ls,l,mid,x,du);
	else settagt(rs,mid+1,r,x,du);
	tree[qk]=unio(tree[ls],tree[rs]);
}
lov ask(int qk,int l,int r,int x,int y)
{
	if(x<=l&&r<=y)return tree[qk];
	int mid=l+r>>1;
	if(x<=mid)
	{
		if(y>mid)return unio(ask(ls,l,mid,x,y),ask(rs,mid+1,r,x,y));
		return ask(ls,l,mid,x,y);
	}
	return ask(rs,mid+1,r,x,y);
}
int main()
{
   // freopen("toy.in","r",stdin);
    //freopen("toy.out","w",stdout);
	ios::sync_with_stdio(0);
	int n,m,a,b,c;cin>>n>>m;
	double ace;
	build(1,1,n);
	for(int i=1;i<=m;i++)
	{
		cin>>a>>b;
		/*
		for(int j=1;j<=n;j++)
		{
			lov an=ask(1,1,n,1,j);
			cout<<j<<" "<<an.len*getcos(an.alf)<<" "<<an.len*getsin(an.alf)<<'\n';
		}
		cout<<"!!!\n";
		*/
		if(a==1)
		{
			cin>>c;
			settagj(1,1,n,b,c);
		}
		if(a==2)
		{
			cin>>ace;
			ace=(double)360-ace;
			settagt(1,1,n,b,ace);
		}
		lov an=ask(1,1,n,1,n);
		double ca=an.len*getcos(an.alf),cb=an.len*getsin(an.alf);
		cout<<fixed<<setprecision(10)<<ca<<" ";
		cout<<fixed<<setprecision(10)<<cb<<'\n';
	}
}


#include <bits/stdc++.h>

using namespace std;

#define pb push_back
#define mp make_pair
#define REP(i, n) for (int i = 0; i < (int)(n); ++i)
typedef long long LL;
typedef pair<int, int> PII;

int n, m;
int len[1 << 19] = {}, rot[1 << 19] = {};
double x[1 << 20], y[1 << 20];
int sta[1 << 20] = {};
const int off = 1 << 19;
double c[360], s[360];
const double PI = atan2(0, -1);

void stMerge(int v, int a, int b, int ind) {
    int ang = sta[a];
    double nx, ny;
    sta[v] = sta[a] + sta[b];
    if (sta[v] >= 360) sta[v] -= 360;
    if (ang == 0) {
        nx = x[b];
        ny = y[b];
    } else {
        nx = c[ang] * x[b] - s[ang] * y[b];
        ny = s[ang] * x[b] + c[ang] * y[b];
    }
    x[v] = x[a] + nx;
    y[v] = y[a] + ny;
}

void stBuild(int v, int L, int R) {
    if (L == R) {
        x[v] = len[L];
        y[v] = 0;
    } else {
        int mid = (L + R) >> 1;
        stBuild(2 * v, L, mid);
        stBuild(2 * v + 1, mid + 1, R);
        stMerge(v, 2 * v, 2 * v + 1, mid + 1);
    }
}

void stUpdate(int v, int L, int R, int pos) {
    if (L == R) {
        x[v] = len[L];
        y[v] = 0;
        sta[v] = rot[L];
        return;
    }
    int mid = (L + R) >> 1;
    if (pos <= mid) stUpdate(2 * v, L, mid, pos);
    else stUpdate(2 * v + 1, mid + 1, R, pos);
    stMerge(v, 2 * v, 2 * v + 1, mid + 1);
}

int main() {
    REP(i, 360) {
        c[i] = cos(-2 * i * PI / 360);
        s[i] = sin(-2 * i * PI / 360);
    }
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i) len[i] = 1;
    stBuild(1, 0, off - 1);
    REP(i, m) {
        int qx, qy, qz;
        scanf("%d%d%d", &qx, &qy, &qz);
        if (qx == 1) {
            len[qy] += qz;
        } else {
            --qy;
            rot[qy] += qz;
            if (rot[qy] >= 360) rot[qy] -= 360;
        }
        stUpdate(1, 0, off - 1, qy);
        printf("%.12f %.12f\n", x[1], y[1]);
    }
    return 0;
}


#include <bits/stdc++.h>

using namespace std;

#define pb push_back
#define mp make_pair
#define REP(i, n) for (int i = 0; i < (int)(n); ++i)
typedef long long LL;
typedef pair<int, int> PII;
typedef long double Double;

int n, m;
int len[1 << 19] = {}, rot[1 << 19] = {};
Double x[1 << 20], y[1 << 20];
int sta[1 << 20] = {};
const int off = 1 << 19;
Double c[360], s[360];
const Double PI = atan2((Double)0, (Double)-1);

void stMerge(int v, int a, int b, int ind) {
    int ang = sta[a];
    Double nx, ny;
    sta[v] = sta[a] + sta[b];
    if (sta[v] >= 360) sta[v] -= 360;
    if (ang == 0) {
        nx = x[b];
        ny = y[b];
    } else {
        nx = c[ang] * x[b] - s[ang] * y[b];
        ny = s[ang] * x[b] + c[ang] * y[b];
    }
    x[v] = x[a] + nx;
    y[v] = y[a] + ny;
}

void stBuild(int v, int L, int R) {
    if (L == R) {
        x[v] = len[L];
        y[v] = 0;
    } else {
        int mid = (L + R) >> 1;
        stBuild(2 * v, L, mid);
        stBuild(2 * v + 1, mid + 1, R);
        stMerge(v, 2 * v, 2 * v + 1, mid + 1);
    }
}

void stUpdate(int v, int L, int R, int pos) {
    if (L == R) {
        x[v] = len[L];
        y[v] = 0;
        sta[v] = rot[L];
        return;
    }
    int mid = (L + R) >> 1;
    if (pos <= mid) stUpdate(2 * v, L, mid, pos);
    else stUpdate(2 * v + 1, mid + 1, R, pos);
    stMerge(v, 2 * v, 2 * v + 1, mid + 1);
}

int main() {
    //freopen("input.txt", "r", stdin);
    REP(i, 360) {
        c[i] = cos(-2 * i * PI / 360);
        s[i] = sin(-2 * i * PI / 360);
    }
    scanf("%d%d", &n, &m);
    REP(i, n) len[i] = 1, rot[i] = 0;
    stBuild(1, 0, off - 1);
    int finAng = 0;
    REP(i, m) {
        int qx, qy, qz;
        scanf("%d%d%d", &qx, &qy, &qz), --qy;
        bool upd = true;
        if (qx == 1) {
            len[qy] += qz;
        } else if (qy) {
            --qy;
            rot[qy] += qz;
            if (rot[qy] >= 360) rot[qy] -= 360;
        } else {
            upd = false;
            finAng += qz;
            if (finAng >= 360) finAng -= 360;
        }
        if (upd) stUpdate(1, 0, off - 1, qy);
        Double nx, ny;
        if (finAng == 0) {
            nx = x[1];
            ny = y[1];
        } else {
            nx = c[finAng] * x[1] - s[finAng] * y[1];
            ny = s[finAng] * x[1] + c[finAng] * y[1];
        }
        printf("%.12f %.12f\n", (double)nx, (double)ny);
    }
    return 0;
}


#include <bits/stdc++.h>

using namespace std;

#define pb push_back
#define mp make_pair
#define REP(i, n) for (int i = 0; i < (int)(n); ++i)
typedef long long LL;
typedef pair<int, int> PII;

int n, m;
int len[1 << 19] = {}, rot[1 << 19] = {};
double x[1 << 20], y[1 << 20];
int sta[1 << 20] = {};
const int off = 1 << 19;
double c[360], s[360];
const double PI = atan2(0, -1);

void stMerge(int v, int a, int b, int ind) {
    int ang = sta[a];
    double nx, ny;
    sta[v] = sta[a] + sta[b];
    if (sta[v] >= 360) sta[v] -= 360;
    if (ang == 0) {
        nx = x[b];
        ny = y[b];
    } else {
        nx = c[ang] * x[b] - s[ang] * y[b];
        ny = s[ang] * x[b] + c[ang] * y[b];
    }
    x[v] = x[a] + nx;
    y[v] = y[a] + ny;
}

void stBuild(int v, int L, int R) {
    if (L == R) {
        x[v] = len[L];
        y[v] = 0;
    } else {
        int mid = (L + R) >> 1;
        stBuild(2 * v, L, mid);
        stBuild(2 * v + 1, mid + 1, R);
        stMerge(v, 2 * v, 2 * v + 1, mid + 1);
    }
}

void stUpdate(int v, int L, int R, int pos) {
    if (L == R) {
        x[v] = len[L];
        y[v] = 0;
        sta[v] = rot[L];
        return;
    }
    int mid = (L + R) >> 1;
    if (pos <= mid) stUpdate(2 * v, L, mid, pos);
    else stUpdate(2 * v + 1, mid + 1, R, pos);
    stMerge(v, 2 * v, 2 * v + 1, mid + 1);
}

int main() {
    //freopen("input.txt", "r", stdin);
    REP(i, 360) {
        c[i] = cos(-2 * i * PI / 360);
        s[i] = sin(-2 * i * PI / 360);
    }
    scanf("%d%d", &n, &m);
    REP(i, n) len[i] = 1, rot[i] = 0;
    stBuild(1, 0, off - 1);
    int finAng = 0;
    REP(i, m) {
        int qx, qy, qz;
        scanf("%d%d%d", &qx, &qy, &qz), --qy;
        bool upd = true;
        if (qx == 1) {
            len[qy] += qz;
        } else if (qy) {
            --qy;
            rot[qy] += qz;
            if (rot[qy] >= 360) rot[qy] -= 360;
        } else {
            upd = false;
            finAng += qz;
            if (finAng >= 360) finAng -= 360;
        }
        if (upd) stUpdate(1, 0, off - 1, qy);
        double nx, ny;
        if (finAng == 0) {
            nx = x[1];
            ny = y[1];
        } else {
            nx = c[finAng] * x[1] - s[finAng] * y[1];
            ny = s[finAng] * x[1] + c[finAng] * y[1];
        }
        printf("%.12f %.12f\n", nx, ny);
    }
    return 0;
}


#include <bits/stdc++.h>

using namespace std;

#define pb push_back
#define mp make_pair
#define REP(i, n) for (int i = 0; i < (int)(n); ++i)
typedef long long LL;
typedef pair<int, int> PII;

int n, m;
double x[1 << 20], y[1 << 20];
int ang[1 << 20] = {};
const int off = 1 << 19;
double c[360], s[360];
const double PI = atan2(0, -1);

void stMerge(int v, int a, int b) {
    int rot = ang[a];
    double nx, ny;
    ang[v] = ang[a] + ang[b];
    if (ang[v] >= 360) ang[v] -= 360;
    if (rot == 0) {
        nx = x[b];
        ny = y[b];
    } else {
        nx = c[rot] * x[b] - s[rot] * y[b];
        ny = s[rot] * x[b] + c[rot] * y[b];
    }
    x[v] = x[a] + nx;
    y[v] = y[a] + ny;
}

void stBuild(int v, int L, int R) {
    if (L != R) {
        int mid = (L + R) >> 1;
        stBuild(2 * v, L, mid);
        stBuild(2 * v + 1, mid + 1, R);
        stMerge(v, 2 * v, 2 * v + 1);
    }
}

void stUpdate(int v, int L, int R, int pos) {
    if (L == R) return;
    int mid = (L + R) >> 1;
    if (pos <= mid) stUpdate(2 * v, L, mid, pos);
    else stUpdate(2 * v + 1, mid + 1, R, pos);
    stMerge(v, 2 * v, 2 * v + 1);
}

int main() {
    REP(i, 360) {
        c[i] = cos(-2 * i * PI / 360);
        s[i] = sin(-2 * i * PI / 360);
    }
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i) x[off + i] = 1;
    stBuild(1, 0, off - 1);
    REP(i, m) {
        int qx, qy, qz;
        scanf("%d%d%d", &qx, &qy, &qz);
        if (qx == 1) {
            x[off + qy] += qz;
        } else {
            --qy;
            ang[off + qy] += qz;
            if (ang[off + qy] >= 360) ang[off + qy] -= 360;
        }
        stUpdate(1, 0, off - 1, qy);
        printf("%.12f %.12f\n", x[1], y[1]);
    }
    return 0;
}


#include <bits/stdc++.h>

using namespace std;

#define pb push_back
#define mp make_pair
#define REP(i, n) for (int i = 0; i < (int)(n); ++i)
typedef long long LL;
typedef pair<int, int> PII;

int n, m;
int len[1 << 19] = {}, rot[1 << 19] = {};
double x[1 << 20], y[1 << 20];
int sta[1 << 20] = {};
const int off = 1 << 19;
double c[360], s[360];
const double PI = atan2(0, -1);

void stMerge(int v, int a, int b, int ind) {
    int ang = sta[a];
    double nx, ny;
    sta[v] = sta[a] + sta[b];
    if (sta[v] >= 360) sta[v] -= 360;
    if (ang == 0) {
        nx = x[b];
        ny = y[b];
    } else {
        nx = c[ang] * x[b] - s[ang] * y[b];
        ny = s[ang] * x[b] + c[ang] * y[b];
    }
    x[v] = x[a] + nx;
    y[v] = y[a] + ny;
}

void stBuild(int v, int L, int R) {
    if (L == R) {
        x[v] = len[L];
        y[v] = 0;
    } else {
        int mid = (L + R) >> 1;
        stBuild(2 * v, L, mid);
        stBuild(2 * v + 1, mid + 1, R);
        stMerge(v, 2 * v, 2 * v + 1, mid + 1);
    }
}

void stUpdate(int v, int L, int R, int pos) {
    if (L == R) {
        x[v] = len[L];
        y[v] = 0;
        sta[v] = rot[L];
        return;
    }
    int mid = (L + R) >> 1;
    if (pos <= mid) stUpdate(2 * v, L, mid, pos);
    else stUpdate(2 * v + 1, mid + 1, R, pos);
    stMerge(v, 2 * v, 2 * v + 1, mid + 1);
}

int main() {
    freopen("input.txt", "r", stdin);
    REP(i, 360) {
        c[i] = cos(-2 * i * PI / 360);
        s[i] = sin(-2 * i * PI / 360);
    }
    scanf("%d%d", &n, &m);
    REP(i, n) len[i] = 1, rot[i] = 0;
    stBuild(1, 0, off - 1);
    int finAng = 0;
    REP(i, m) {
        int qx, qy, qz;
        scanf("%d%d%d", &qx, &qy, &qz), --qy;
        bool upd = true;
        if (qx == 1) {
            len[qy] += qz;
        } else if (qy) {
            --qy;
            rot[qy] += qz;
            if (rot[qy] >= 360) rot[qy] -= 360;
        } else {
            upd = false;
            finAng += qz;
            if (finAng >= 360) finAng -= 360;
        }
        if (upd) stUpdate(1, 0, off - 1, qy);
        double nx, ny;
        if (finAng == 0) {
            nx = x[1];
            ny = y[1];
        } else {
            nx = c[finAng] * x[1] - s[finAng] * y[1];
            ny = s[finAng] * x[1] + c[finAng] * y[1];
        }
        printf("%.12f %.12f\n", nx, ny);
    }
    return 0;
}


#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <deque>
#include <iomanip>
#include <iostream>
#include <queue>
#include <map>
#include <numeric>
#include <set>
#include <sstream>
#include <stack>
#include <utility>
#include <vector>

#define INF 1000000000
#define FOR(i, a, b) for(int i=int(a); i<int(b); i++)
#define FORC(cont, it) for(typeof((cont).begin()) it = (cont).begin(); it != (cont).end(); it++)
#define pb push_back

using namespace std;

typedef long long ll;
typedef pair<int, int> ii;
typedef vector<int> vi;
typedef vector<ii> vii;
typedef vector<vi> vvi;

#define maxN 300000
#define PI acos(-1.)

struct Point;
Point rotate(Point p1, Point p2, long double ang);

struct Point {
	long double x, y, ang;
	void operator +=(const Point r) {
		Point t = rotate(*this, Point{ 0, 0, 0 }, r.ang);
		x = t.x + r.x;
		y = t.y + r.y;
		ang += r.ang;
	}
	bool empty() {
		return x == 0.0&&y == 0.0&&ang == 0.0;
	}
};

int start;
Point tree[maxN * 4], empt;

long double toRads(long double ang) {
	return PI*ang / 180.;
}

Point rotate(Point p1, Point p2, long double ang) {
	//MAGIC ROT
	long double tx = p1.x - p2.x;
	long double ty = p1.y - p2.y;
	p1.x = tx*cos(ang) - ty*sin(ang) + p2.x;
	p1.y = ty*cos(ang) + tx*sin(ang) + p2.y;
	p1.ang = 0;
 	return p1;
}

void query1(int n, int l, int r, int d, Point p) {
	if (n < start&&!tree[n].empty()) {
		tree[n << 1] += tree[n];
		tree[n << 1 | 1] += tree[n];
		tree[n] = empt;
	}
	if (l > r) return;
	if (!l&&r == d - 1) {
		tree[n] += p;
		return;
	}
	d /= 2;
	query1(n << 1, l, min(d - 1, r), d, p);
	query1(n << 1 | 1, max(l - d, 0), r - d, d, p);
}

Point query2(int id) {
	if(id>1) query2(id >> 1);
	if (id < start&&!tree[id].empty()) {
		tree[id<<1] += tree[id];
		tree[id << 1 | 1] += tree[id];
		tree[id] = empt;
	}
	return tree[id];
}

int main() {
	empt = Point{0,0,0};
	int n, m, x, y, z;
	cout << fixed << setprecision(12);
	while (scanf("%d %d", &n, &m)!=EOF) {
		n++;
		start = 1 << (int)ceil(log2(n));
		FOR(i, 0, 2 * start) tree[i].x = tree[i].y = tree[i].ang = 0.0;
		FOR(i, start+1, 2*start) {
			tree[i].x = start + n - i;
		}
		Point curAns = Point{n-1,0,0};
		FOR(i, 0, m) {
			scanf("%d %d %d", &x, &y, &z);
			if (x == 1) {
				Point p = rotate(Point{ z, 0, 0 }, Point{0,0,0}, query2(start+y).ang);
				query1(1,0,y,start, p);
				curAns = query2(start + 1);
				//MOVE
			}
			else {
				//ROTATE
				long double zz = -toRads(z);
				Point curD = query2(start + y), p = rotate(curD, empt, zz);
 				p.x -= curD.x;
				p.y -= curD.y;
				query1(1, 0, y-1, start, p);
				query1(1, y, start - 1, start, Point{ 0, 0, zz });
				//tree[start + y].ang += zz;
				curAns = query2(start + 1);
			}
			printf("%.9f %.9f\n", curAns.x, curAns.y);
		}
	}
	return 0;
}


#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <deque>
#include <iomanip>
#include <iostream>
#include <queue>
#include <map>
#include <numeric>
#include <set>
#include <sstream>
#include <stack>
#include <utility>
#include <vector>

#define INF 1000000000
#define FOR(i, a, b) for(int i=int(a); i<int(b); i++)
#define FORC(cont, it) for(typeof((cont).begin()) it = (cont).begin(); it != (cont).end(); it++)
#define pb push_back

using namespace std;

typedef long long ll;
typedef pair<int, int> ii;
typedef vector<int> vi;
typedef vector<ii> vii;
typedef vector<vi> vvi;

#define maxN 300000
#define PI acos(-1.)

struct Point;
Point rotate(Point p1, Point p2, long double ang);

struct Point {
	long double x, y, ang;
	void operator +=(const Point r) {
		Point t = rotate(*this, Point{ 0, 0, 0 }, r.ang);
		x = t.x + r.x;
		y = t.y + r.y;
		ang += r.ang;
	}
	bool empty() {
		return x == 0.0&&y == 0.0&&ang == 0.0;
	}
};

int start;
Point tree[maxN * 4], empt;

long double toRads(long double ang) {
	return PI*ang / 180.;
}

Point rotate(Point p1, Point p2, long double ang) {
	//MAGIC ROT
	long double tx = p1.x - p2.x;
	long double ty = p1.y - p2.y;
	p1.x = tx*cos(ang) - ty*sin(ang) + p2.x;
	p1.y = ty*cos(ang) + tx*sin(ang) + p2.y;
	p1.ang = 0;
 	return p1;
}

void query1(int n, int l, int r, int d, Point p) {
	if (n < start&&!tree[n].empty()) {
		tree[n << 1] += tree[n];
		tree[n << 1 | 1] += tree[n];
		tree[n] = empt;
	}
	if (l > r) return;
	if (!l&&r == d - 1) {
		tree[n] += p;
		return;
	}
	d /= 2;
	query1(n << 1, l, min(d - 1, r), d, p);
	query1(n << 1 | 1, max(l - d, 0), r - d, d, p);
}

Point query2(int id) {
	if(id>1) query2(id >> 1);
	if (id < start&&!tree[id].empty()) {
		tree[id<<1] += tree[id];
		tree[id << 1 | 1] += tree[id];
		tree[id] = empt;
	}
	return tree[id];
}

int main() {
	empt = Point{0,0,0};
	int n, m, x, y, z;
	cout << fixed << setprecision(12);
	while (scanf("%d %d", &n, &m)!=EOF) {
		n++;
		start = 1 << (int)ceil(log2(n));
		FOR(i, 0, 2 * start) tree[i].x = tree[i].y = tree[i].ang = 0.0;
		FOR(i, start+1, 2*start) {
			tree[i].x = start + n - i;
		}
		Point curAns = Point{n-1,0,0};
		FOR(i, 0, m) {
			scanf("%d %d %d", &x, &y, &z);
			if (x == 1) {
				Point p = rotate(Point{ z, 0, 0 }, Point{0,0,0}, query2(start+y).ang);
				query1(1,0,y,start, p);
				curAns = query2(start + 1);
				//MOVE
			}
			else {
				//ROTATE
				long double zz = -toRads(z);
				Point curD = query2(start + y), p = rotate(curD, empt, zz);
 				p.x -= curD.x;
				p.y -= curD.y;
				query1(1, 0, y-1, start, p);
				query1(1, y, start - 1, start, Point{ 0, 0, zz });
				//tree[start + y].ang += zz;
				curAns = query2(start + 1);
			}
			printf("%.9lf %.9lf\n", curAns.x, curAns.y);
		}
	}
	return 0;
}


#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <deque>
#include <iomanip>
#include <iostream>
#include <queue>
#include <map>
#include <numeric>
#include <set>
#include <sstream>
#include <stack>
#include <utility>
#include <vector>

#define INF 1000000000
#define FOR(i, a, b) for(int i=int(a); i<int(b); i++)
#define FORC(cont, it) for(typeof((cont).begin()) it = (cont).begin(); it != (cont).end(); it++)
#define pb push_back

using namespace std;

typedef long long ll;
typedef pair<int, int> ii;
typedef vector<int> vi;
typedef vector<ii> vii;
typedef vector<vi> vvi;

#define maxN 300000
#define PI acos(-1.)

struct Point;
Point rotate(Point p1, Point p2, double ang);

struct Point {
	double x, y, ang;
	void operator +=(const Point r) {
		Point t = rotate(*this, Point{ 0, 0, 0 }, r.ang);
		x = t.x + r.x;
		y = t.y + r.y;
		ang += r.ang;
	}
};

int start;
Point tree[maxN * 4], empt;

double toRads(double ang) {
	return PI*ang / 180.;
}

Point rotate(Point p1, Point p2, double ang) {
	//MAGIC ROT
	double tx = p1.x - p2.x;
	double ty = p1.y - p2.y;
	p1.x = tx*cos(ang) - ty*sin(ang) + p2.x;
	p1.y = ty*cos(ang) + tx*sin(ang) + p2.y;
	p1.ang = 0;
 	return p1;
}

void query1(int n, int l, int r, int d, Point p) {
	if (n < start) {
		tree[n << 1] += tree[n];
		tree[n << 1 | 1] += tree[n];
		tree[n] = empt;
	}
	if (l > r) return;
	if (!l&&r == d - 1) {
		tree[n] += p;
		return;
	}
	d /= 2;
	query1(n << 1, l, min(d - 1, r), d, p);
	query1(n << 1 | 1, max(l - d, 0), r - d, d, p);
}

Point query2(int id) {
	if(id>1) query2(id >> 1);
	if (id < start) {
		tree[id<<1] += tree[id];
		tree[id << 1 | 1] += tree[id];
		tree[id] = empt;
	}
	return tree[id];
}

int main() {
	cin.tie(0), cout.tie(0), ios::sync_with_stdio(false);
	empt = Point{0,0,0};
	int n, m, x, y, z;
	cout << fixed << setprecision(12);
	while (cin >> n >> m) {
		n++;
		start = 1 << (int)ceil(log2(n));
		FOR(i, 0, 2 * start) tree[i].x = tree[i].y = tree[i].ang = 0.0;
		FOR(i, start+1, 2*start) {
			tree[i].x = start + n - i;
		}
		Point curAns = Point{n-1,0,0};
		FOR(i, 0, m) {
			cin >> x >> y >> z;
			if (x == 1) {
				Point p = rotate(Point{ z, 0, 0 }, Point{0,0,0}, query2(start+y).ang);
				query1(1,0,y,start, p);
				curAns = query2(start + 1);
				//MOVE
			}
			else {
				//ROTATE
				double zz = -toRads(z);
				Point curD = query2(start + y), p = rotate(curD, empt, zz);
 				p.x -= curD.x;
				p.y -= curD.y;
				query1(1, 0, y-1, start, p);
				query1(1, y, start - 1, start, Point{ 0, 0, zz });
				//tree[start + y].ang += zz;
				curAns = query2(start + 1);
			}
			cout << curAns.x << " " << curAns.y << endl;
		}
	}
	return 0;
}


#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <deque>
#include <iomanip>
#include <iostream>
#include <queue>
#include <map>
#include <numeric>
#include <set>
#include <sstream>
#include <stack>
#include <utility>
#include <vector>

#define INF 1000000000
#define FOR(i, a, b) for(int i=int(a); i<int(b); i++)
#define FORC(cont, it) for(typeof((cont).begin()) it = (cont).begin(); it != (cont).end(); it++)
#define pb push_back

using namespace std;

typedef long long ll;
typedef pair<int, int> ii;
typedef vector<int> vi;
typedef vector<ii> vii;
typedef vector<vi> vvi;

#define maxN 300000
#define PI acos(-1.)

struct Point;
Point rotate(Point p1, Point p2, long double ang);

struct Point {
	long double x, y, ang;
	void operator +=(const Point r) {
		Point t = rotate(*this, Point{ 0, 0, 0 }, r.ang);
		x = t.x + r.x;
		y = t.y + r.y;
		ang += r.ang;
	}
};

int start;
Point tree[maxN * 4], empt;

long double toRads(long double ang) {
	return PI*ang / 180.;
}

Point rotate(Point p1, Point p2, long double ang) {
	//MAGIC ROT
	long double tx = p1.x - p2.x;
	long double ty = p1.y - p2.y;
	p1.x = tx*cos(ang) - ty*sin(ang) + p2.x;
	p1.y = ty*cos(ang) + tx*sin(ang) + p2.y;
	p1.ang = 0;
 	return p1;
}

void query1(int n, int l, int r, int d, Point p) {
	if (n < start) {
		tree[n << 1] += tree[n];
		tree[n << 1 | 1] += tree[n];
		tree[n] = empt;
	}
	if (l > r) return;
	if (!l&&r == d - 1) {
		tree[n] += p;
		return;
	}
	d /= 2;
	query1(n << 1, l, min(d - 1, r), d, p);
	query1(n << 1 | 1, max(l - d, 0), r - d, d, p);
}

Point query2(int id) {
	if(id>1) query2(id >> 1);
	if (id < start) {
		tree[id<<1] += tree[id];
		tree[id << 1 | 1] += tree[id];
		tree[id] = empt;
	}
	return tree[id];
}

int main() {
	cin.tie(0), cout.tie(0), ios::sync_with_stdio(false);
	empt = Point{0,0,0};
	int n, m, x, y, z;
	cout << fixed << setprecision(12);
	while (cin >> n >> m) {
		n++;
		start = 1 << (int)ceil(log2(n));
		FOR(i, 0, 2 * start) tree[i].x = tree[i].y = tree[i].ang = 0.0;
		FOR(i, start+1, 2*start) {
			tree[i].x = start + n - i;
		}
		Point curAns = Point{n-1,0,0};
		FOR(i, 0, m) {
			cin >> x >> y >> z;
			if (x == 1) {
				Point p = rotate(Point{ z, 0, 0 }, Point{0,0,0}, query2(start+y).ang);
				query1(1,0,y,start, p);
				curAns = query2(start + 1);
				//MOVE
			}
			else {
				//ROTATE
				long double zz = -toRads(z);
				Point curD = query2(start + y), p = rotate(curD, empt, zz);
 				p.x -= curD.x;
				p.y -= curD.y;
				query1(1, 0, y-1, start, p);
				query1(1, y, start - 1, start, Point{ 0, 0, zz });
				//tree[start + y].ang += zz;
				curAns = query2(start + 1);
			}
			cout << curAns.x << " " << curAns.y << endl;
		}
	}
	return 0;
}


#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <deque>
#include <iomanip>
#include <iostream>
#include <queue>
#include <map>
#include <numeric>
#include <set>
#include <sstream>
#include <stack>
#include <utility>
#include <vector>

#define INF 1000000000
#define FOR(i, a, b) for(int i=int(a); i<int(b); i++)
#define FORC(cont, it) for(typeof((cont).begin()) it = (cont).begin(); it != (cont).end(); it++)
#define pb push_back

using namespace std;

typedef long long ll;
typedef pair<int, int> ii;
typedef vector<int> vi;
typedef vector<ii> vii;
typedef vector<vi> vvi;

#define maxN 300000
#define PI acos(-1.)

struct Point;
Point rotate(Point p1, Point p2, long double ang);

struct Point {
	long double x, y, ang;
	void operator +=(const Point r) {
		Point t = rotate(*this, Point{ 0, 0, 0 }, r.ang);
		x = t.x + r.x;
		y = t.y + r.y;
		ang += r.ang;
	}
};

int start;
Point tree[maxN * 4], empt;

long double toRads(long double ang) {
	return PI*ang / 180.;
}

Point rotate(Point p1, Point p2, long double ang) {
	//MAGIC ROT
	long double tx = p1.x - p2.x;
	long double ty = p1.y - p2.y;
	p1.x = tx*cos(ang) - ty*sin(ang) + p2.x;
	p1.y = ty*cos(ang) + tx*sin(ang) + p2.y;
	p1.ang = 0;
 	return p1;
}

void query1(int n, int l, int r, int d, Point p) {
	if (n < start) {
		tree[n << 1] += tree[n];
		tree[n << 1 | 1] += tree[n];
		tree[n] = empt;
	}
	if (l > r) return;
	if (!l&&r == d - 1) {
		tree[n] += p;
		return;
	}
	d /= 2;
	query1(n << 1, l, min(d - 1, r), d, p);
	query1(n << 1 | 1, max(l - d, 0), r - d, d, p);
}

Point query2(int id) {
	if(id>1) query2(id >> 1);
	if (id < start) {
		tree[id<<1] += tree[id];
		tree[id << 1 | 1] += tree[id];
		tree[id] = empt;
	}
	return tree[id];
}

int main() {
	cin.tie(0), cout.tie(0), ios::sync_with_stdio(false);
	empt = Point{0,0,0};
	int n, m, x, y, z;
	cout << fixed << setprecision(12);
	while (cin >> n >> m) {
		n++;
		start = 1 << (int)ceil(log2(n));
		FOR(i, 0, 2 * start) tree[i].x = tree[i].y = tree[i].ang = 0.0;
		FOR(i, start+1, 2*start) {
			tree[i].x = start + n - i;
		}
		Point curAns = Point{n-1,0,0};
		FOR(i, 0, m) {
			cin >> x >> y >> z;
			if (x == 1) {
				Point p = rotate(Point{ z, 0, 0 }, Point{0,0,0}, query2(start+y).ang);
				query1(1,0,y,start, p);
				curAns = query2(start + 1);
				//MOVE
			}
			else {
				//ROTATE
				long double zz = -toRads(z);
				Point curD = query2(start + y), p = rotate(curD, empt, zz);
				p.x -= curD.x;
				p.y -= curD.y;
				query1(1, 0, y, start, p);
				query1(1, y, start - 1, start, Point{ 0, 0, zz });
				//tree[start + y].ang += zz;
				curAns = query2(start + 1);
			}
			cout << curAns.x << " " << curAns.y << endl;
		}
	}
	return 0;
}


#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <deque>
#include <iomanip>
#include <iostream>
#include <queue>
#include <map>
#include <numeric>
#include <set>
#include <sstream>
#include <stack>
#include <utility>
#include <vector>

#define INF 1000000000
#define FOR(i, a, b) for(int i=int(a); i<int(b); i++)
#define FORC(cont, it) for(typeof((cont).begin()) it = (cont).begin(); it != (cont).end(); it++)
#define pb push_back

using namespace std;

typedef long long ll;
typedef pair<int, int> ii;
typedef vector<int> vi;
typedef vector<ii> vii;
typedef vector<vi> vvi;

#define maxN 300000
#define PI acos(-1.)

struct Point;
Point rotate(Point p1, Point p2, long double ang);

struct Point {
	long double x, y, ang;
	void operator +=(const Point r) {
		Point t = rotate(*this, Point{ 0, 0, 0 }, r.ang);
		x = t.x + r.x;
		y = t.y + r.y;
		ang += r.ang;
	}
	bool empty() {
		return x == 0.0&&y == 0.0&&ang == 0.0;
	}
};

int start;
Point tree[maxN * 4], empt;

long double toRads(long double ang) {
	return PI*ang / 180.;
}

Point rotate(Point p1, Point p2, long double ang) {
	//MAGIC ROT
	long double tx = p1.x - p2.x;
	long double ty = p1.y - p2.y;
	p1.x = tx*cos(ang) - ty*sin(ang) + p2.x;
	p1.y = ty*cos(ang) + tx*sin(ang) + p2.y;
	p1.ang = 0;
 	return p1;
}

void query1(int n, int l, int r, int d, Point p) {
	if (n < start&&!tree[n].empty()) {
		tree[n << 1] += tree[n];
		tree[n << 1 | 1] += tree[n];
		tree[n] = empt;
	}
	if (l > r) return;
	if (!l&&r == d - 1) {
		tree[n] += p;
		return;
	}
	d /= 2;
	query1(n << 1, l, min(d - 1, r), d, p);
	query1(n << 1 | 1, max(l - d, 0), r - d, d, p);
}

Point query2(int id) {
	if(id>1) query2(id >> 1);
	if (id < start&&!tree[id].empty()) {
		tree[id<<1] += tree[id];
		tree[id << 1 | 1] += tree[id];
		tree[id] = empt;
	}
	return tree[id];
}

int main() {
	empt = Point{0,0,0};
	int n, m, x, y, z;
	cout << fixed << setprecision(12);
	while (scanf("%d %d", &n, &m)!=EOF) {
		n++;
		start = 1 << (int)ceil(log2(n));
		FOR(i, 0, 2 * start) tree[i].x = tree[i].y = tree[i].ang = 0.0;
		FOR(i, start+1, 2*start) {
			tree[i].x = start + n - i;
		}
		Point curAns = Point{n-1,0,0};
		FOR(i, 0, m) {
			scanf("%d %d %d", &x, &y, &z);
			if (x == 1) {
				Point p = rotate(Point{ z, 0, 0 }, Point{0,0,0}, query2(start+y).ang);
				query1(1,0,y,start, p);
				curAns = query2(start + 1);
				//MOVE
			}
			else {
				//ROTATE
				long double zz = -toRads(z);
				Point curD = query2(start + y), p = rotate(curD, empt, zz);
 				p.x -= curD.x;
				p.y -= curD.y;
				query1(1, 0, y-1, start, p);
				query1(1, y, start - 1, start, Point{ 0, 0, zz });
				//tree[start + y].ang += zz;
				curAns = query2(start + 1);
			}
			printf("%.9f %.9f\n", (double)curAns.x, (double)curAns.y);
		}
	}
	return 0;
}


#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <deque>
#include <iomanip>
#include <iostream>
#include <queue>
#include <map>
#include <numeric>
#include <set>
#include <sstream>
#include <stack>
#include <utility>
#include <vector>

#define INF 1000000000
#define FOR(i, a, b) for(int i=int(a); i<int(b); i++)
#define FORC(cont, it) for(typeof((cont).begin()) it = (cont).begin(); it != (cont).end(); it++)
#define pb push_back

using namespace std;

typedef long long ll;
typedef pair<int, int> ii;
typedef vector<int> vi;
typedef vector<ii> vii;
typedef vector<vi> vvi;

#define maxN 300000
#define PI acos(-1.)

struct Point;
Point rotate(Point p1, Point p2, long double ang);

struct Point {
	long double x, y, ang;
	void operator +=(const Point r) {
		Point t = rotate(*this, Point{ 0, 0, 0 }, r.ang);
		x = t.x + r.x;
		y = t.y + r.y;
		ang += r.ang;
	}
	bool empty() {
		return x == 0.0&&y == 0.0&&ang == 0.0;
	}
};

int start;
Point tree[maxN * 4], empt;

long double toRads(long double ang) {
	return PI*ang / 180.;
}

Point rotate(Point p1, Point p2, long double ang) {
	//MAGIC ROT
	long double tx = p1.x - p2.x;
	long double ty = p1.y - p2.y;
	p1.x = tx*cos(ang) - ty*sin(ang) + p2.x;
	p1.y = ty*cos(ang) + tx*sin(ang) + p2.y;
	p1.ang = 0;
 	return p1;
}

void query1(int n, int l, int r, int d, Point p) {
	if (n < start&&!tree[n].empty()) {
		tree[n << 1] += tree[n];
		tree[n << 1 | 1] += tree[n];
		tree[n] = empt;
	}
	if (l > r) return;
	if (!l&&r == d - 1) {
		tree[n] += p;
		return;
	}
	d /= 2;
	query1(n << 1, l, min(d - 1, r), d, p);
	query1(n << 1 | 1, max(l - d, 0), r - d, d, p);
}

Point query2(int id) {
	if(id>1) query2(id >> 1);
	if (id < start&&!tree[id].empty()) {
		tree[id<<1] += tree[id];
		tree[id << 1 | 1] += tree[id];
		tree[id] = empt;
	}
	return tree[id];
}

int main() {
	empt = Point{0,0,0};
	int n, m, x, y, z;
	cout << fixed << setprecision(12);
	while (scanf("%d %d", &n, &m)!=EOF) {
		n++;
		start = 1 << (int)ceil(log2(n));
		FOR(i, 0, 2 * start) tree[i].x = tree[i].y = tree[i].ang = 0.0;
		FOR(i, start+1, 2*start) {
			tree[i].x = start + n - i;
		}
		Point curAns = Point{n-1,0,0};
		FOR(i, 0, m) {
			scanf("%d %d %d", &x, &y, &z);
			if (x == 1) {
				Point p = rotate(Point{ z, 0, 0 }, Point{0,0,0}, query2(start+y).ang);
				query1(1,0,y,start, p);
				curAns = query2(start + 1);
				//MOVE
			}
			else {
				//ROTATE
				long double zz = -toRads(z);
				Point curD = query2(start + y), p = rotate(curD, empt, zz);
 				p.x -= curD.x;
				p.y -= curD.y;
				query1(1, 0, y-1, start, p);
				query1(1, y, start - 1, start, Point{ 0, 0, zz });
				//tree[start + y].ang += zz;
				curAns = query2(start + 1);
			}
			printf("%.9Lf %.9Lf\n", curAns.x, curAns.y);
		}
	}
	return 0;
}


#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <deque>
#include <iomanip>
#include <iostream>
#include <queue>
#include <map>
#include <numeric>
#include <set>
#include <sstream>
#include <stack>
#include <utility>
#include <vector>

#define INF 1000000000
#define FOR(i, a, b) for(int i=int(a); i<int(b); i++)
#define FORC(cont, it) for(typeof((cont).begin()) it = (cont).begin(); it != (cont).end(); it++)
#define pb push_back

using namespace std;

typedef long long ll;
typedef pair<int, int> ii;
typedef vector<int> vi;
typedef vector<ii> vii;
typedef vector<vi> vvi;

#define maxN 300000
#define PI acos(-1.)

struct Point;
Point rotate(Point p1, Point p2, double ang);

struct Point {
	double x, y, ang;
	void operator +=(const Point r) {
		Point t = rotate(*this, Point{ 0, 0, 0 }, r.ang);
		x = t.x + r.x;
		y = t.y + r.y;
		ang += r.ang;
	}
	bool empty() {
		return x == 0.0&&y == 0.0&&ang == 0.0;
	}
};

int start;
Point tree[maxN * 4], empt;

double toRads(double ang) {
	return PI*ang / 180.;
}

Point rotate(Point p1, Point p2, double ang) {
	//MAGIC ROT
	double tx = p1.x - p2.x;
	double ty = p1.y - p2.y;
	p1.x = tx*cos(ang) - ty*sin(ang) + p2.x;
	p1.y = ty*cos(ang) + tx*sin(ang) + p2.y;
	p1.ang = 0;
 	return p1;
}

void query1(int n, int l, int r, int d, Point p) {
	if (n < start&&!tree[n].empty()) {
		tree[n << 1] += tree[n];
		tree[n << 1 | 1] += tree[n];
		tree[n] = empt;
	}
	if (l > r) return;
	if (!l&&r == d - 1) {
		tree[n] += p;
		return;
	}
	d /= 2;
	query1(n << 1, l, min(d - 1, r), d, p);
	query1(n << 1 | 1, max(l - d, 0), r - d, d, p);
}

Point query2(int id) {
	if(id>1) query2(id >> 1);
	if (id < start&&!tree[id].empty()) {
		tree[id<<1] += tree[id];
		tree[id << 1 | 1] += tree[id];
		tree[id] = empt;
	}
	return tree[id];
}

int main() {
	empt = Point{0,0,0};
	int n, m, x, y, z;
	cout << fixed << setprecision(12);
	while (scanf("%d %d", &n, &m)!=EOF) {
		n++;
		start = 1 << (int)ceil(log2(n));
		FOR(i, 0, 2 * start) tree[i].x = tree[i].y = tree[i].ang = 0.0;
		FOR(i, start+1, 2*start) {
			tree[i].x = start + n - i;
		}
		Point curAns = Point{n-1,0,0};
		FOR(i, 0, m) {
			scanf("%d %d %d", &x, &y, &z);
			if (x == 1) {
				Point p = rotate(Point{ z, 0, 0 }, Point{0,0,0}, query2(start+y).ang);
				query1(1,0,y,start, p);
				curAns = query2(start + 1);
				//MOVE
			}
			else {
				//ROTATE
				double zz = -toRads(z);
				Point curD = query2(start + y), p = rotate(curD, empt, zz);
 				p.x -= curD.x;
				p.y -= curD.y;
				query1(1, 0, y-1, start, p);
				query1(1, y, start - 1, start, Point{ 0, 0, zz });
				//tree[start + y].ang += zz;
				curAns = query2(start + 1);
			}
			printf("%.9f %.9f\n", curAns.x, curAns.y);
		}
	}
	return 0;
}


#include <iostream>
#include <vector>
#include <cstdio>
#include <sstream>
#include <map>
#include <string>
#include <algorithm>
#include <queue>
#include <cmath>
#include <functional>
#include <set>
#include <ctime>
#include <random>
using namespace std;
template<class T> istream& operator >> (istream& is, vector<T>& vec){for(T& val: vec) is >> val; return is;}
template<class T> istream& operator , (istream& is, T& val){ return is >> val;}
template<class T> ostream& operator << (ostream& os, vector<T>& vec){for(int i=0; i<vec.size(); i++) os << vec[i] << (i==vec.size()-1?"\n":" ");return os;}
template<class T> ostream& operator , (ostream& os, T& val){ return os << " " << val;}
template<class T> ostream& operator >> (ostream& os, T& val){ return os << " " << val;}

//segment tree
//using std::function as comparing function
template<class Value>
class SegmentTree{
	int n;
	vector<Value> V;
	Value DEFAULT_VALUE;

	//evaluation function
	static Value default_evaluate(Value a, Value b){
		return a*b;
	};

	function< Value(Value, Value) > evaluate;

	//return evaluated value in [a,b)
	//T[at] covers [l,r)
	Value RangeEvaluation(int a, int b, int at, int l, int r){
		//out of range
		if(r <= a || b <= l) return DEFAULT_VALUE;
		//covered
		if(a <= l && r <= b) return V[at];

		//partially covered
		else{
			Value val_left = RangeEvaluation(a,b, at*2+1, l, (l+r)/2);
			Value val_right = RangeEvaluation(a,b, at*2+2, (l+r)/2, r);
			return evaluate(val_left, val_right);
		}
	}

public:
	SegmentTree(int size, Value DEFAULT = Value(), function< Value(Value, Value) > eval = default_evaluate){
		DEFAULT_VALUE = DEFAULT;
		evaluate = eval;
		n=1;
		while(n<size) n <<= 1;
		V = vector<Value>(2*n - 1, DEFAULT_VALUE);
	}

	void update(int at, Value new_val){
		at += n-1;
		V[at] = new_val;
		while(at>0){
			at = (at-1)/2;
			V[at] = evaluate(V[at*2 + 1], V[at*2 + 2]);
		}
	}

	//return evaluated value in [l,r)
	Value RangeEvaluation(int l, int r){
		if(l>=r) return DEFAULT_VALUE;
		if(l>=n) return DEFAULT_VALUE;
		return RangeEvaluation(l,r, 0, 0, n);
	}
};

using Double = /*long*/ double;

constexpr Double PI = acos((Double)-1.0);

struct f{
	Double vx;
	Double vy;
	Double r;

	f(){
		vx = 1;
		vy = 0;
		r = 0;
	}

	f(int l_, int r_){
		r = r_*PI/180.0;

		vx = l_*cos(r);
		vy = l_*sin(r);
	}

	f operator * (const f& w){
		f ret;
		ret.r = this->r + w.r;
		//if(ret.r > 360.0) ret.r -= 360.0;

		ret.vx = cos(this->r) * w.vx  - sin(this->r) * w.vy + this->vx;
		ret.vy = sin(this->r) * w.vx  + cos(this->r) * w.vy + this->vy;
		return ret;
	}

	pair<Double,Double> operator () (int xx, int yy){
		pair<Double,Double> ret = {0.0, 0.0};
		ret.first  = this->vx + cos(this->r) * xx - sin(this->r) * yy;
		ret.second = this->vy + cos(this->r) * xx - cos(this->r) * yy;
		return ret;
	}
};

/*
fi(x,y) -> (xx, yy)
fi{
	l
	theta
}

fi(x,y) = (x + l, y) * rot(t)
        = x*cos(t) - y*sin(t) + l*cos(t)
          x*sin(t) + y*cos(t) + l*sin(t)
        = rt [(x,y) + (l,0)]
        = rt(x,y) + rt(l, 0)

f2(f1(x,y)) = r2 * [ (r1(x,y) + r1(l1,0)) + (l2,0) ]
            = r2 * r1 * (x,y) + r2 * r1 * (l1, 0) + r2 * (l2,0)
            = r1r2 * [(x,y) + (l1,0)] + r2 * [(l2, 0)]
            = r2*r1 * [(x,y)] +
              r2    * [r1(l1,0) + (l2,0)]
            = r2*r1 [(x,y)] +
              r2*   [(vx1,vy1)] +
              (vx2 , vy2)

f3(f2(f1(x,y))) = r3 * [ f2(x,y) + (l3,0) ]
                = r3 * [ r2* [ f1(x,y) + (l2,0) ] + (l3,0) ]
                = r3 * [ r2* [ r1 * [ (x,y) + (l1, 0) ] + (l2,0) ] + (l3,0) ]
                = r3*r2*r1 * [(x,y) ] +
                  r3*r2*r1 * [(l1,0)] +
                  r3*r2    * [(l2,0)] +
                  r3       * [(l3,0)]
                = r3*r2*r1 * (x,y) +
                  r3       * (L' + (l3,0))

                = r3* [R(x,y)] +
                  r3* L        +
                  (vx3,vy3)

*/

int main(){
	int n,m;
	cin >> n >> m;

	vector<int> l(n, 1);
	vector<int> r(n, 0);

	SegmentTree<f> seg(n, f(0,0));
	for(int i=0; i<n; i++){
		seg.update(i, f(1,0));
	}
	while(m--){
		int x,y,z;
		scanf("%d%d%d", &x,&y,&z);
		y--;
		if(x==1){
			l[y] += z;
		}else if(x==2){
			r[y] = (r[y]+360-z)%360;
		}
		seg.update(y, f(l[y], r[y]));

		f func = seg.RangeEvaluation(0,n);
		auto ans = func(0,0);

		printf("%.16f %.16f\n", (double)ans.first,  (double)ans.second);
	}

	return 0;
}


#include <iostream>
#include <vector>
#include <cstdio>
#include <sstream>
#include <map>
#include <string>
#include <algorithm>
#include <queue>
#include <cmath>
#include <functional>
#include <set>
#include <ctime>
#include <random>
using namespace std;
template<class T> istream& operator >> (istream& is, vector<T>& vec){for(T& val: vec) is >> val; return is;}
template<class T> istream& operator , (istream& is, T& val){ return is >> val;}
template<class T> ostream& operator << (ostream& os, vector<T>& vec){for(int i=0; i<vec.size(); i++) os << vec[i] << (i==vec.size()-1?"\n":" ");return os;}
template<class T> ostream& operator , (ostream& os, T& val){ return os << " " << val;}
template<class T> ostream& operator >> (ostream& os, T& val){ return os << " " << val;}

//segment tree
//using std::function as comparing function
template<class Value>
class SegmentTree{
	int n;
	vector<Value> V;
	Value DEFAULT_VALUE;

	//evaluation function
	static Value default_evaluate(Value a, Value b){
		return a*b;
	};

	function< Value(Value, Value) > evaluate;

	//return evaluated value in [a,b)
	//T[at] covers [l,r)
	Value RangeEvaluation(int a, int b, int at, int l, int r){
		//out of range
		if(r <= a || b <= l) return DEFAULT_VALUE;
		//covered
		if(a <= l && r <= b) return V[at];

		//partially covered
		else{
			Value val_left = RangeEvaluation(a,b, at*2+1, l, (l+r)/2);
			Value val_right = RangeEvaluation(a,b, at*2+2, (l+r)/2, r);
			return evaluate(val_left, val_right);
		}
	}

public:
	SegmentTree(int size, Value DEFAULT = Value(), function< Value(Value, Value) > eval = default_evaluate){
		DEFAULT_VALUE = DEFAULT;
		evaluate = eval;
		n=1;
		while(n<size) n <<= 1;
		V = vector<Value>(2*n - 1, DEFAULT_VALUE);
	}

	void update(int at, Value new_val){
		at += n-1;
		V[at] = new_val;
		while(at>0){
			at = (at-1)/2;
			V[at] = evaluate(V[at*2 + 1], V[at*2 + 2]);
		}
	}

	//return evaluated value in [l,r)
	Value RangeEvaluation(int l, int r){
		if(l>=r) return DEFAULT_VALUE;
		if(l>=n) return DEFAULT_VALUE;
		return RangeEvaluation(l,r, 0, 0, n);
	}
};

using Double = long double;

constexpr Double PI = acos((Double)-1.0);

struct f{
	Double vx;
	Double vy;
	Double r;

	f(){
		vx = 1;
		vy = 0;
		r = 0;
	}

	f(int l_, int r_){
		r = r_*PI/180.0;

		vx = l_*cos(r);
		vy = l_*sin(r);
	}

	f operator * (const f& w){
		f ret;
		ret.r = this->r + w.r;
		//if(ret.r > 360.0) ret.r -= 360.0;

		ret.vx = cos(this->r) * w.vx  - sin(this->r) * w.vy + this->vx;
		ret.vy = sin(this->r) * w.vx  + cos(this->r) * w.vy + this->vy;
		return ret;
	}

	pair<Double,Double> operator () (int xx, int yy){
		pair<Double,Double> ret = {0.0, 0.0};
		ret.first  = this->vx + cos(this->r) * xx - sin(this->r) * yy;
		ret.second = this->vy + cos(this->r) * xx - cos(this->r) * yy;
		return ret;
	}
};

/*
fi(x,y) -> (xx, yy)
fi{
	l
	theta
}

fi(x,y) = (x + l, y) * rot(t)
        = x*cos(t) - y*sin(t) + l*cos(t)
          x*sin(t) + y*cos(t) + l*sin(t)
        = rt [(x,y) + (l,0)]
        = rt(x,y) + rt(l, 0)

f2(f1(x,y)) = r2 * [ (r1(x,y) + r1(l1,0)) + (l2,0) ]
            = r2 * r1 * (x,y) + r2 * r1 * (l1, 0) + r2 * (l2,0)
            = r1r2 * [(x,y) + (l1,0)] + r2 * [(l2, 0)]
            = r2*r1 * [(x,y)] +
              r2    * [r1(l1,0) + (l2,0)]
            = r2*r1 [(x,y)] +
              r2*   [(vx1,vy1)] +
              (vx2 , vy2)

f3(f2(f1(x,y))) = r3 * [ f2(x,y) + (l3,0) ]
                = r3 * [ r2* [ f1(x,y) + (l2,0) ] + (l3,0) ]
                = r3 * [ r2* [ r1 * [ (x,y) + (l1, 0) ] + (l2,0) ] + (l3,0) ]
                = r3*r2*r1 * [(x,y) ] +
                  r3*r2*r1 * [(l1,0)] +
                  r3*r2    * [(l2,0)] +
                  r3       * [(l3,0)]
                = r3*r2*r1 * (x,y) +
                  r3       * (L' + (l3,0))

                = r3* [R(x,y)] +
                  r3* L        +
                  (vx3,vy3)

*/

int main(){
	int n,m;
	cin >> n >> m;

	vector<int> l(n, 1);
	vector<int> r(n, 0);

	SegmentTree<f> seg(n, f(0,0));
	for(int i=0; i<n; i++){
		seg.update(i, f(1,0));
	}
	while(m--){
		int x,y,z;
		scanf("%d%d%d", &x,&y,&z);
		y--;
		if(x==1){
			l[y] += z;
		}else if(x==2){
			r[y] = (r[y]+360-z)%360;
		}
		seg.update(y, f(l[y], r[y]));

		f func = seg.RangeEvaluation(0,n);
		auto ans = func(0,0);

		printf("%.16f %.16f\n", (double)ans.first,  (double)ans.second);
	}

	return 0;
}


#include <iostream>
#include <vector>
#include <cstdio>
#include <sstream>
#include <map>
#include <string>
#include <algorithm>
#include <queue>
#include <cmath>
#include <functional>
#include <set>
#include <ctime>
#include <random>
using namespace std;
template<class T> istream& operator >> (istream& is, vector<T>& vec){for(T& val: vec) is >> val; return is;}
template<class T> istream& operator , (istream& is, T& val){ return is >> val;}
template<class T> ostream& operator << (ostream& os, vector<T>& vec){for(int i=0; i<vec.size(); i++) os << vec[i] << (i==vec.size()-1?"\n":" ");return os;}
template<class T> ostream& operator , (ostream& os, T& val){ return os << " " << val;}
template<class T> ostream& operator >> (ostream& os, T& val){ return os << " " << val;}

//segment tree
//using std::function as comparing function
template<class Value>
class SegmentTree{
	int n;
	vector<Value> V;
	Value DEFAULT_VALUE;

	//evaluation function
	static Value default_evaluate(Value a, Value b){
		return a*b;
	};

	function< Value(Value, Value) > evaluate;

	//return evaluated value in [a,b)
	//T[at] covers [l,r)
	Value RangeEvaluation(int a, int b, int at, int l, int r){
		//out of range
		if(r <= a || b <= l) return DEFAULT_VALUE;
		//covered
		if(a <= l && r <= b) return V[at];

		//partially covered
		else{
			Value val_left = RangeEvaluation(a,b, at*2+1, l, (l+r)/2);
			Value val_right = RangeEvaluation(a,b, at*2+2, (l+r)/2, r);
			return evaluate(val_left, val_right);
		}
	}

public:
	SegmentTree(int size, Value DEFAULT = Value(), function< Value(Value, Value) > eval = default_evaluate){
		DEFAULT_VALUE = DEFAULT;
		evaluate = eval;
		n=1;
		while(n<size) n <<= 1;
		V = vector<Value>(2*n - 1, DEFAULT_VALUE);
	}

	void update(int at, Value new_val){
		at += n-1;
		V[at] = new_val;
		while(at>0){
			at = (at-1)/2;
			V[at] = evaluate(V[at*2 + 1], V[at*2 + 2]);
		}
	}

	void set(int at, Value new_val){	//for initialization
		at += n-1;
		V[at] = new_val;
	}

	//T[at] covers [l,r)
	Value all_init(int at, int l, int r){
		if(r-l == 1) return V[at];
		Value val_left = all_init(at*2+1, l, (l+r)/2);
		Value val_right = all_init(at*2+2, (l+r)/2, r);
		V[at] = evaluate(val_left, val_right);
		return V[at];
	}
	void all_init(){
		all_init(0, 0, n);
	}

	//return evaluated value in [l,r)
	Value RangeEvaluation(int l, int r){
		if(l>=r) return DEFAULT_VALUE;
		if(l>=n) return DEFAULT_VALUE;
		return RangeEvaluation(l,r, 0, 0, n);
	}
};

using Double = long double;

constexpr Double PI = acos((Double)-1.0);

struct f{
	Double vx;
	Double vy;
	Double r;

	f(){
		vx = 1;
		vy = 0;
		r = 0;
	}

	f(int l_, int r_){
		r = r_*PI/180.0;

		vx = l_*cos(r);
		vy = l_*sin(r);
	}

	f operator * (const f& w){
		f ret;
		ret.r = this->r + w.r;
		//if(ret.r > 360.0) ret.r -= 360.0;

		ret.vx = cos(this->r) * w.vx  - sin(this->r) * w.vy + this->vx;
		ret.vy = sin(this->r) * w.vx  + cos(this->r) * w.vy + this->vy;
		return ret;
	}

	pair<Double,Double> operator () (int xx, int yy){
		pair<Double,Double> ret = {0.0, 0.0};
		ret.first  = this->vx + cos(this->r) * xx - sin(this->r) * yy;
		ret.second = this->vy + cos(this->r) * xx - cos(this->r) * yy;
		return ret;
	}
};

/*
fi(x,y) -> (xx, yy)
fi{
	l
	theta
}

fi(x,y) = (x + l, y) * rot(t)
        = x*cos(t) - y*sin(t) + l*cos(t)
          x*sin(t) + y*cos(t) + l*sin(t)
        = rt [(x,y) + (l,0)]
        = rt(x,y) + rt(l, 0)

f2(f1(x,y)) = r2 * [ (r1(x,y) + r1(l1,0)) + (l2,0) ]
            = r2 * r1 * (x,y) + r2 * r1 * (l1, 0) + r2 * (l2,0)
            = r1r2 * [(x,y) + (l1,0)] + r2 * [(l2, 0)]
            = r2*r1 * [(x,y)] +
              r2    * [r1(l1,0) + (l2,0)]
            = r2*r1 [(x,y)] +
              r2*   [(vx1,vy1)] +
              (vx2 , vy2)

f3(f2(f1(x,y))) = r3 * [ f2(x,y) + (l3,0) ]
                = r3 * [ r2* [ f1(x,y) + (l2,0) ] + (l3,0) ]
                = r3 * [ r2* [ r1 * [ (x,y) + (l1, 0) ] + (l2,0) ] + (l3,0) ]
                = r3*r2*r1 * [(x,y) ] +
                  r3*r2*r1 * [(l1,0)] +
                  r3*r2    * [(l2,0)] +
                  r3       * [(l3,0)]
                = r3*r2*r1 * (x,y) +
                  r3       * (L' + (l3,0))

                = r3* [R(x,y)] +
                  r3* L        +
                  (vx3,vy3)

*/

int main(){
	int n,m;
	//cin >> n >> m;
	scanf("%d%d", &n,&m);

	vector<int> l(n, 1);
	vector<int> r(n, 0);

	SegmentTree<f> seg(n, f(0,0));
	for(int i=0; i<n; i++){
		seg.set(i, f(1,0));
	}
	seg.all_init();

	while(m--){
		int x,y,z;
		scanf("%d%d%d", &x,&y,&z);
		y--;
		if(x==1){
			l[y] += z;
		}else if(x==2){
			r[y] = (r[y]+360-z)%360;
		}
		seg.update(y, f(l[y], r[y]));

		f func = seg.RangeEvaluation(0,n);
		auto ans = func(0,0);

		printf("%.16f %.16f\n", (double)ans.first,  (double)ans.second);
	}

	return 0;
}


#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <string.h>
#include <fstream>

using namespace std;

#define LL long long
#define N 1500100
#define M 1500100
#define MP make_pair
#define Pi acos(-1.0)
#pragma comment(linker,"/STACK:1024000000,1024000000")
#define ls (rt << 1)
#define rs (ls | 1)
#define md ((ll+rr)/2)
#define lson ll, md, ls
#define rson md+1, rr, rs
#define mod 1000000007
#define inf 0x3f3f3f3f
#define sqr(x) ((x)*(x))
#define eps 1e-6
#define MP make_pair
#define uLL unsigned long long
LL powmod(LL a,LL b) {LL res=1;a%=mod;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
#define F(x) ((x)/3+((x)%3 == 1 ? 0 : tb))
#define G(x) ((x)<tb ? (x)*3+1 : ((x) - tb)*3+2)
#define lowbit(x) ((x)&(-x))
#define fi first
#define se second
#define pii pair<int,int>

int gcd(int x,int y){
    while(y){
        int t = x % y;
        x = y;
        y = t;
    }
    return x;
}

double tx[N<<2],ty[N<<2],ta[N<<2];

void update(double r,double A,int x,int ll,int rr,int rt){
	if(ll == rr){
		double l = sqrt(tx[rt] * tx[rt] + ty[rt] * ty[rt]);
		l += r;
		ta[rt] += A;
		tx[rt] = l * cos(ta[rt]);
		ty[rt] = l * sin(ta[rt]);
	   	return;
	}
	if(x <= md) update(r,A,x,lson);
	else update(r,A,x,rson);

	double l = sqrt(tx[rs] * tx[rs] + ty[rs] * ty[rs]);
	double a = atan2(ty[rs],tx[rs]);

	tx[rt] = tx[ls] + l * cos(a + ta[ls]);
	ty[rt] = ty[ls] + l * sin(a + ta[ls]);
	ta[rt] = ta[ls] + ta[rs];
}

int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= n; i++)
		update(1,0,i,1,n,1);
	while(m--){
		int tp,x,y;
		scanf("%d%d%d",&tp,&x,&y);
		if(tp == 1){
			update(y,0,x,1,n,1);
		}else{
			update(0,2 * Pi - y / 360.0 * 2.0 * Pi,x,1,n,1);
		}
		printf("%.10lf %.10lf\n",tx[1],ty[1]);
	}
}


#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <string.h>
#include <fstream>

using namespace std;

#define LL long long
#define N 300100
#define M 300100
#define MP make_pair
#define Pi acos(-1.0)
#pragma comment(linker,"/STACK:1024000000,1024000000")
#define ls (rt << 1)
#define rs (ls | 1)
#define md ((ll+rr)/2)
#define lson ll, md, ls
#define rson md+1, rr, rs
#define mod 1000000007
#define inf 0x3f3f3f3f
#define sqr(x) ((x)*(x))
#define eps 1e-6
#define MP make_pair
#define uLL unsigned long long
LL powmod(LL a,LL b) {LL res=1;a%=mod;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
#define F(x) ((x)/3+((x)%3 == 1 ? 0 : tb))
#define G(x) ((x)<tb ? (x)*3+1 : ((x) - tb)*3+2)
#define lowbit(x) ((x)&(-x))
#define fi first
#define se second
#define pii pair<int,int>

int gcd(int x,int y){
    while(y){
        int t = x % y;
        x = y;
        y = t;
    }
    return x;
}

double tx[N<<2],ty[N<<2],ta[N<<2];

void update(int r,double A,int x,int ll,int rr,int rt){
	if(ll == rr){
		double l = sqrt(tx[rt] * tx[rt] + ty[rt] * ty[rt]);
		double a = atan2(ty[rt],tx[tr]);

		l += r;
		a += A;
		tx[rt] = l * cos(a);
		ty[rt] = l * sin(a);
		ta[rt] = a;
	   	return;
	}
	if(x <= md) update(r,A,x,lson);
	else update(r,A,x,rson);

	double l = sqrt(tx[rs] * tx[rs] + ty[rs] * ty[rs]);
	double a = atan2(ty[rs],tx[rs]);

	tx[rt] = tx[ls] + l * cos(a + ta[ls]);
	ty[rt] = ty[ls] + l * sin(a + ta[ls]);
	ta[rt] = ta[ls] + ta[rs];
}

int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= n; i++)
		update(1,0,i,1,n,1);
	while(m--){
		int tp,x,y;
		scanf("%d%d%d",&tp,&x,&y);
		if(tp == 1){
			update(y,0,x,1,n,1);
		}else{
			update(0,2.0 * Pi - y / 360.0 * 2.0 * Pi,x,1,n,1);
		}
		printf("%.10lf %.10lf\n",tx[1],ty[1]);
	}
}


#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <string.h>
#include <fstream>

using namespace std;

#define LL long long
#define N 300100
#define M 300100
#define MP make_pair
#define Pi acos(-1.0)
#pragma comment(linker,"/STACK:1024000000,1024000000")
#define ls (rt << 1)
#define rs (ls | 1)
#define md ((ll+rr)/2)
#define lson ll, md, ls
#define rson md+1, rr, rs
#define mod 1000000007
#define inf 0x3f3f3f3f
#define sqr(x) ((x)*(x))
#define eps 1e-6
#define MP make_pair
#define uLL unsigned long long
LL powmod(LL a,LL b) {LL res=1;a%=mod;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
#define F(x) ((x)/3+((x)%3 == 1 ? 0 : tb))
#define G(x) ((x)<tb ? (x)*3+1 : ((x) - tb)*3+2)
#define lowbit(x) ((x)&(-x))
#define fi first
#define se second
#define pii pair<int,int>

int gcd(int x,int y){
    while(y){
        int t = x % y;
        x = y;
        y = t;
    }
    return x;
}

double tx[N<<2],ty[N<<2],ta[N<<2];

void update(int r,double A,int x,int ll,int rr,int rt){
	if(ll == rr){
		double l = sqrt(tx[rt] * tx[rt] + ty[rt] * ty[rt]);
		l += r;
		ta[rt] += A;
		tx[rt] = l * cos(ta[rt]);
		ty[rt] = l * sin(ta[rt]);
	   	return;
	}
	if(x <= md) update(r,A,x,lson);
	else update(r,A,x,rson);

	double l = sqrt(tx[rs] * tx[rs] + ty[rs] * ty[rs]);
	double a = atan2(ty[rs],tx[rs]);

	tx[rt] = tx[ls] + l * cos(a + ta[ls]);
	ty[rt] = ty[ls] + l * sin(a + ta[ls]);
	ta[rt] = ta[ls] + ta[rs];
}

int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= n; i++)
		update(1,0,i,1,n,1);
	while(m--){
		int tp,x,y;
		scanf("%d%d%d",&tp,&x,&y);
		if(tp == 1){
			update(y,0,x,1,n,1);
		}else{
			update(0,2.0 * Pi - y / 360.0 * 2.0 * Pi,x,1,n,1);
		}
		printf("%.10lf %.10lf\n",tx[1],ty[1]);
	}
}


#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <string.h>
#include <fstream>

using namespace std;

#define LL long long
#define N 300100
#define M 300100
#define MP make_pair
#define Pi acos(-1.0)
#pragma comment(linker,"/STACK:1024000000,1024000000")
#define ls (rt << 1)
#define rs (ls | 1)
#define md ((ll+rr)/2)
#define lson ll, md, ls
#define rson md+1, rr, rs
#define mod 1000000007
#define inf 0x3f3f3f3f
#define sqr(x) ((x)*(x))
#define eps 1e-6
#define MP make_pair
#define uLL unsigned long long
LL powmod(LL a,LL b) {LL res=1;a%=mod;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
#define F(x) ((x)/3+((x)%3 == 1 ? 0 : tb))
#define G(x) ((x)<tb ? (x)*3+1 : ((x) - tb)*3+2)
#define lowbit(x) ((x)&(-x))
#define fi first
#define se second
#define pii pair<int,int>

int gcd(int x,int y){
    while(y){
        int t = x % y;
        x = y;
        y = t;
    }
    return x;
}

double tx[N<<2],ty[N<<2],ta[N<<2];

void update(int r,double A,int x,int ll,int rr,int rt){
	if(ll == rr){
		double l = sqrt(tx[rt] * tx[rt] + ty[rt] * ty[rt]);
		double a = atan2(ty[rt],tx[rt]);

		l += r;
		a += A;
		tx[rt] = l * cos(a);
		ty[rt] = l * sin(a);
		ta[rt] = a;
	   	return;
	}
	if(x <= md) update(r,A,x,lson);
	else update(r,A,x,rson);

	double l = sqrt(tx[rs] * tx[rs] + ty[rs] * ty[rs]);
	double a = atan2(ty[rs],tx[rs]);

	tx[rt] = tx[ls] + l * cos(a + ta[ls]);
	ty[rt] = ty[ls] + l * sin(a + ta[ls]);
	ta[rt] = ta[ls] + ta[rs];
}

int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= n; i++)
		update(1,0,i,1,n,1);
	while(m--){
		int tp,x,y;
		scanf("%d%d%d",&tp,&x,&y);
		if(tp == 1){
			update(y,0,x,1,n,1);
		}else{
			update(0,2.0 * Pi - y / 360.0 * 2.0 * Pi,x,1,n,1);
		}
		printf("%.10lf %.10lf\n",tx[1],ty[1]);
	}
}


#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <string.h>
#include <fstream>

using namespace std;

#define LL long long
#define N 1000100
#define M 1000100
#define MP make_pair
#define Pi acos(-1.0)
#pragma comment(linker,"/STACK:1024000000,1024000000")
#define ls (rt << 1)
#define rs (ls | 1)
#define md ((ll+rr)/2)
#define lson ll, md, ls
#define rson md+1, rr, rs
#define mod 1000000007
#define inf 0x3f3f3f3f
#define sqr(x) ((x)*(x))
#define eps 1e-6
#define MP make_pair
#define uLL unsigned long long
LL powmod(LL a,LL b) {LL res=1;a%=mod;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
#define F(x) ((x)/3+((x)%3 == 1 ? 0 : tb))
#define G(x) ((x)<tb ? (x)*3+1 : ((x) - tb)*3+2)
#define lowbit(x) ((x)&(-x))
#define fi first
#define se second
#define pii pair<int,int>

int gcd(int x,int y){
    while(y){
        int t = x % y;
        x = y;
        y = t;
    }
    return x;
}

double tx[N<<2],ty[N<<2],ta[N<<2];

void update(double r,double A,int x,int ll,int rr,int rt){
	if(ll == rr){
		double l = sqrt(tx[rt] * tx[rt] + ty[rt] * ty[rt]);
		l += r;
		ta[rt] += A;
		tx[rt] = l * cos(ta[rt]);
		ty[rt] = l * sin(ta[rt]);
	   	return;
	}
	if(x <= md) update(r,A,x,lson);
	else update(r,A,x,rson);

	double l = sqrt(tx[rs] * tx[rs] + ty[rs] * ty[rs]);
	double a = atan2(ty[rs],tx[rs]);

	tx[rt] = tx[ls] + l * cos(a + ta[ls]);
	ty[rt] = ty[ls] + l * sin(a + ta[ls]);
	ta[rt] = ta[ls] + ta[rs];
}

int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= n; i++)
		update(1,0,i,1,n,1);
	while(m--){
		int tp,x,y;
		scanf("%d%d%d",&tp,&x,&y);
		if(tp == 1){
			update(y,0,x,1,n,1);
		}else{
			update(0,2 * Pi - y / 360.0 * 2.0 * Pi,x,1,n,1);
		}
		printf("%.10lf %.10lf\n",tx[1],ty[1]);
	}
}


#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <string.h>
#include <fstream>

using namespace std;

#define LL long long
#define N 300100
#define M 300100
#define MP make_pair
#define Pi 2 * acos(0.0)
#pragma comment(linker,"/STACK:1024000000,1024000000")
#define ls (rt << 1)
#define rs (ls | 1)
#define md ((ll+rr)/2)
#define lson ll, md, ls
#define rson md+1, rr, rs
#define mod 1000000007
#define inf 0x3f3f3f3f
#define sqr(x) ((x)*(x))
#define eps 1e-6
#define MP make_pair
#define uLL unsigned long long
LL powmod(LL a,LL b) {LL res=1;a%=mod;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
#define F(x) ((x)/3+((x)%3 == 1 ? 0 : tb))
#define G(x) ((x)<tb ? (x)*3+1 : ((x) - tb)*3+2)
#define lowbit(x) ((x)&(-x))
#define fi first
#define se second
#define pii pair<int,int>

int gcd(int x,int y){
    while(y){
        int t = x % y;
        x = y;
        y = t;
    }
    return x;
}

double tx[N<<2],ty[N<<2],ta[N<<2];

void update(int r,double A,int x,int ll,int rr,int rt){
	if(ll == rr){
		double l = sqrt(tx[rt] * tx[rt] + ty[rt] * ty[rt]);
		l += r;
		ta[rt] += A;
		tx[rt] = l * cos(ta[rt]);
		ty[rt] = l * sin(ta[rt]);
	   	return;
	}
	if(x <= md) update(r,A,x,lson);
	else update(r,A,x,rson);

	double l = sqrt(tx[rs] * tx[rs] + ty[rs] * ty[rs]);
	double a = atan2(ty[rs],tx[rs]);

	tx[rt] = tx[ls] + l * cos(a + ta[ls]);
	ty[rt] = ty[ls] + l * sin(a + ta[ls]);
	ta[rt] = ta[ls] + ta[rs];
}

int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= n; i++)
		update(1,0,i,1,n,1);
	while(m--){
		int tp,x,y;
		scanf("%d%d%d",&tp,&x,&y);
		if(tp == 1){
			update(y,0,x,1,n,1);
		}else{
			update(0,2.0 * Pi - y / 360.0 * 2.0 * Pi,x,1,n,1);
		}
		printf("%.10lf %.10lf\n",tx[1],ty[1]);
	}
}


#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
#include <cmath>
using namespace std;
#define maxn 300005
const double pi=acos(-1);
int n,m;
struct Segtree
{
	double x,y;//这一条线段相对于上一条线段，所在直线为x轴，以该线段起点为坐标原点的平面直角坐标系中的相对位置；
	double arc;//这条线段相当于上一条线段的角度
}s[maxn<<2];

inline void update(int rt)
{
	s[rt].x=s[rt<<1].x+s[rt<<1|1].x*cos(s[rt<<1].arc)-s[rt<<1|1].y*sin(s[rt<<1].arc);
	s[rt].y=s[rt<<1].y+s[rt<<1|1].x*sin(s[rt<<1].arc)+s[rt<<1|1].y*cos(s[rt<<1].arc);
	s[rt].arc=s[rt<<1].arc+s[rt<<1|1].arc;
}

inline void build(int rt,int l,int r)
{
	if(l==r)
	{
		s[rt].x=1.0;
		s[rt].y=0.0;
		s[rt].arc=0.0;
		return;
	}
	int mid=l+r>>1;
	build(rt<<1,l,mid);
	build(rt<<1|1,mid+1,r);
	update(rt);
}

inline void modify1(int rt,int l,int r,int L,int R,double len)
{
	if(L<=l&&r<=R)
	{
		s[rt].x+=len*cos(s[rt].arc);
		s[rt].y+=len*sin(s[rt].arc);
		return;
	}
	int mid=l+r>>1;
	if(L<=mid) modify1(rt<<1,l,mid,L,R,len);
	if(R>mid) modify1(rt<<1|1,mid+1,r,L,R,len);
	update(rt);
}

inline void modify2(int rt,int l,int r,int L,int R,double arc)
{
	if(L<=l&&r<=R)
	{
		double len=sqrt(s[rt].x*s[rt].x+s[rt].y*s[rt].y);
		s[rt].arc-=arc*pi/(double)180;
		s[rt].x=len*cos(s[rt].arc);
		s[rt].y=len*sin(s[rt].arc);
		return;
	}
	int mid=l+r>>1;
	if(L<=mid) modify2(rt<<1,l,mid,L,R,arc);
	if(R>mid) modify2(rt<<1|1,mid+1,r,L,R,arc);
	update(rt);
}

int main()
{
	scanf("%d%d",&n,&m);
	build(1,1,n);
	for(register int i=1;i<=m;i++)
	{
		int op,x;
		double y;
		scanf("%d%d%lf",&op,&x,&y);
		if(op==1) modify1(1,1,n,x,x,y);
		if(op==2) modify2(1,1,n,x,x,y);
		printf("%.12lf %.12lf\n",s[1].x,s[1].y);
	}
	return 0;
}