#pragma GCC optimize("Ofast") #include struct segment_tree_base { virtual int size() const = 0; protected: template void forward(int l, int r, F f) const { int h = h1(l += size() - 1, r += size()); for (int s = 0; s < h; ++s) if (int i = l >> s; ~i & 1) f(i + 1); for (int s = h; s--;) if (int i = r >> s; i & 1) f(i - 1); } template void forward(int l, int r, F f) { const_cast(this)->forward(l, r, f); } template void backward(int l, int r, F f) const { int h = h1(l += size() - 1, r += size()); for (int s = 0; s < h; ++s) if (int i = r >> s; i & 1) f(i - 1); for (int s = h; s--;) if (int i = l >> s; ~i & 1) f(i + 1); } template void backward(int l, int r, F f) { const_cast(this)->backward(l, r, f); } template void downward(int l, int r, F f) const { if (l == r or (l == 0 and r == size())) return; int h = h2(l += size(), r += size()); for (int s = std::__lg(l); s > h; --s) f(l >> s); for (int s = h; s > __builtin_ctz(l); --s) f(l >> s); for (int s = h; s > __builtin_ctz(r); --s) f(r >> s); } template void downward(int l, int r, F f) { const_cast(this)->downward(l, r, f); } template void upward(int l, int r, F f) const { if (l == r or (l == 0 and r == size())) return; int h = h2(l += size(), r += size()); for (int s = __builtin_ctz(r); s++ < h;) f(r >> s); for (int s = __builtin_ctz(l); s++ < h;) f(l >> s); for (int s = h; s++ < std::__lg(l);) f(l >> s); } template void upward(int l, int r, F f) { const_cast(this)->upward(l, r, f); } private: static int h1(int l, int r) { for (int h = 0;; ++h) if ((r >> h) - (l >> h) == 1) return h; } static int h2(int l, int r) { l <<= std::__lg(l) < std::__lg(r); return std::__lg(l ^ r); } }; template struct lazy_segment_tree : segment_tree_base { lazy_segment_tree() {} template lazy_segment_tree(int n, Generator gen) : tree(2 * n), lazy(n) { for (int i = 0; i < n; ++i) tree[n + i] = gen(i); for (int i = n; i-- > 1;) pull(i); } int size() const override { return std::size(lazy); } T fold(int l, int r) { assert(0 <= l), assert(l <= r), assert(r <= size()); downward(l, r, [&](int i) { push(i); }); T res{}; forward(l, r, [&](int i) { res = res * tree[i]; }); return res; } T get(int i) { assert(0 <= i), assert(i < size()); return fold(i, i + 1); } T fold_all_rotated() const { return size() ? tree[1] : T{}; } template void update(int i, Function func) { assert(0 <= i), assert(i < size()); downward(i, i + 1, [&](int j) { push(j); }); tree[size() + i] = func(tree[size() + i]); upward(i, i + 1, [&](int j) { pull(j); }); } void act(int l, int r, const Action& f) { assert(0 <= l), assert(l <= r), assert(r <= size()); downward(l, r, [&](int i) { push(i); }); forward(l, r, [&](int i) { apply(i, f); }); upward(l, r, [&](int i) { pull(i); }); } template int forward_search(int l, int r, Predicate pred) { assert(0 <= l), assert(l <= r), assert(r <= size()); downward(l, r, [&](int i) { push(i); }); T a{}; assert(pred(a)); int res = r; forward(l, r, [&](int i) { if (res < r) return; if (T na = a * tree[i]; pred(na)) { a = na; return; } while (i < size()) { push(i); if (T na = a * tree[i *= 2]; pred(na)) a = na, ++i; } res = i - size(); }); return res; } template int backward_search(int l, int r, Predicate pred) { assert(0 <= l), assert(l <= r), assert(r <= size()); downward(l, r, [&](int i) { push(i); }); T a{}; assert(pred(a)); int res = l - 1; backward(l, r, [&](int i) { if (res >= l) return; if (T na = a * tree[i]; pred(na)) { a = na; return; } while (i < size()) { push(i); if (T na = a * tree[i = 2 * i + 1]; pred(na)) a = na, --i; } res = i - size(); }); return res; } private: std::vector tree; std::vector lazy; void apply(int i, const Action& f) { tree[i] = f(tree[i]); if (i < size()) lazy[i] = lazy[i] * f; } void push(int i) { apply(2 * i, lazy[i]), apply(2 * i + 1, lazy[i]); lazy[i] = Action{}; } void pull(int i) { tree[i] = tree[2 * i] * tree[2 * i + 1]; } }; using num = std::complex; struct node { num z; friend node operator*(const node& a, const node& b) { return {a.z + b.z}; } }; struct action { num a = 1, b; node operator()(const node& x) const { return {x.z * a + b}; } friend action operator*(const action& f, const action& g) { return {f.a * g.a, f.b * g.a + g.b}; } }; int main() { using namespace std; cin.tie(nullptr)->sync_with_stdio(false); cout << fixed << setprecision(10); int n, q; cin >> n >> q; lazy_segment_tree seg(n, [](int i) -> node { return {i + 1}; }); auto get_len = [&](int i) { num d = seg.get(i).z - (i ? seg.get(i - 1).z : 0); return abs(d); }; auto get_arg = [&](int i) -> num::value_type { if (i == -1) return 0; num d = seg.get(i).z - (i ? seg.get(i - 1).z : 0); return arg(d); }; while (q--) { int t, i; cin >> t >> i; --i; if (t == 0) { num::value_type x; cin >> x; x /= 180; x *= acos(num::value_type(-1)); num off = i ? seg.get(i - 1).z : 0; auto th = get_arg(i - 1) + x - get_arg(i); seg.act(i, n, {1, -off}); seg.act(i, n, {polar(num::value_type(1), th), 0}); seg.act(i, n, {1, +off}); } else if (t == 1) { int x; cin >> x; seg.act(i, n, {1, polar(x - get_len(i), get_arg(i))}); } else if (t == 2) { auto z = seg.get(i).z; cout << real(z) << ' ' << imag(z) << '\n'; } else assert(false); } }