#include <bits/stdc++.h>
using namespace std;
template <class A, class B>
inline bool mina(A &x, const B &y) {
  return (y < x) ? (x = y, 1) : 0;
}
template <class A, class B>
inline bool maxa(A &x, const B &y) {
  return (x < y) ? (x = y, 1) : 0;
}
const long double PI = acos(-1);
struct X {
  bool u;
  long double x, y, a;
  X(int u1 = 0) {
    u = u1;
    x = 1;
    y = 0;
    a = 0;
  }
  static X add(X s, X t) {
    if (!s.u) return t;
    if (!t.u) return s;
    X res(1);
    long double ac = cos(s.a);
    long double as = sin(s.a);
    res.x = s.x + ac * t.x - as * t.y;
    res.y = s.y + as * t.x + ac * t.y;
    res.a = s.a + t.a;
    return res;
  }
  inline void extend(long double l) {
    long double r = sqrt(x * x + y * y) + l;
    x = r * cos(a);
    y = r * sin(a);
  }
  inline void rotate(long double th) {
    long double r = sqrt(x * x + y * y);
    a -= th;
    x = r * cos(a);
    y = r * sin(a);
  }
};
class SegmentTree1 {
 private:
  int ready;
  vector<int> l_b, r_b;
  int q_l, q_r;
  int size, base;
  vector<X> seg_value;
  pair<int, long double> query_value;
  X query_result;
  inline X def_value() { return X(1); }
  inline X def_qresult() { return X(0); }
  inline void accum_result(int id) {
    assert(id == 1);
    query_result = seg_value[id];
  }
  inline void update_value(int id) {
    if (query_value.first == 1) {
      seg_value[id].extend(query_value.second);
    } else {
      seg_value[id].rotate(query_value.second * PI / 180.);
    }
  }
  inline void update_parent_value(int id, int lc, int rc) {
    seg_value[id] = X::add(seg_value[lc], seg_value[rc]);
  }
  inline void build_parent_value(int id, int lc, int rc) {
    update_parent_value(id, lc, rc);
  }
  inline int l_ch(int id) { return id + id; }
  inline int r_ch(int id) { return id + id + 1; }
  void seg_merge(int id) {
    int lc = l_ch(id), rc = r_ch(id);
    if (!ready)
      build_parent_value(id, lc, rc);
    else
      update_parent_value(id, lc, rc);
  }
  void internal_query(int id) {
    if (q_r < l_b[id] || r_b[id] < q_l) return;
    if (q_l <= l_b[id] && r_b[id] <= q_r) {
      accum_result(id);
      return;
    }
    internal_query(l_ch(id));
    internal_query(r_ch(id));
  }
  void update_bound(int id, int l, int r) {
    l_b[id] = l;
    r_b[id] = r;
    if (l >= r) return;
    int m = (l + r) / 2;
    update_bound(l_ch(id), l, m);
    update_bound(r_ch(id), m + 1, r);
  }

 public:
  SegmentTree1() { init(vector<X>()); }
  SegmentTree1(int n) { init(vector<X>(n, def_value())); }
  SegmentTree1(const vector<X> &A) { init(A); }
  void init(const vector<X> &A) {
    ready = 0;
    int n = A.size();
    if (n <= 0) {
      base = size = 0;
      return;
    }
    base = 2;
    while (base < n) base <<= 1;
    size = base * 2;
    l_b.resize(size);
    r_b.resize(size);
    seg_value.resize(size, def_value());
    update_bound(1, 0, base - 1);
    for (int _b = (n), i = (0); i < _b; ++i) seg_value[i + base] = A[i];
    for (int _a = (0), i = (base - 1); i > _a; --i) seg_merge(i);
    ready = 1;
  }
  void update(int p, const pair<int, long double> value) {
    int id = p + base;
    query_value = value;
    update_value(id);
    while ((id = id / 2) >= 1) seg_merge(id);
  }
  X query(int l, int r) {
    query_result = def_qresult();
    q_l = l;
    q_r = r;
    internal_query(1);
    return query_result;
  }
  int leaves_count() const { return size - base; }
};
int N, Q;
int main() {
  {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
  };
  cin >> N >> Q;
  vector<X> A((524288), X(0));
  for (int _b = (N), i = (0); i < _b; ++i) A[i].u = 1;
  SegmentTree1 S(A);
  for (int _n = (Q); _n--;) {
    int x, y, z;
    cin >> x >> y >> z;
    S.update(y - 1, pair<int, long double>(x, z));
    X res = S.query(0, (524288) - 1);
    printf("%.9f %.9f\n", double(res.x), double(res.y));
  }
  return 0;
}
