#include <bits/stdc++.h>
using namespace std;
const int INF = 1000000000;
template <typename T>
using V = std::vector<T>;
struct debugger {
  template <typename T>
  debugger &operator,(const T &v) {
    cerr << v << " ";
    return *this;
  }
} dbg;
using namespace std;
const double pi = acos(-1);
struct SegmentTree {
  int n;
  int size;
  vector<pair<double, double> > dat;
  vector<double> lazy;
  int real_size;
  SegmentTree(int n_) {
    real_size = n_;
    n = 1;
    size = n_;
    while (n < n_) n *= 2;
    dat = vector<pair<double, double> >(2 * n - 1);
    lazy = vector<double>(2 * n - 1);
    init(0, 0, n);
  }
  void init(int k, int l, int r) {
    if (r - l == 1) {
      if (l < real_size) {
        dat[k] = make_pair(1.0, 0.0);
      }
    } else {
      int m = (l + r) / 2;
      init((k * 2 + 1), l, m);
      init((k * 2 + 2), m, r);
      dat[k] = combine(dat[(k * 2 + 1)], dat[(k * 2 + 2)]);
    }
  }
  void update(int a, int b, const double &d) { update(a, b + 1, 0, 0, n, d); }
  void propagate(int k, int l, int r) {
    double t = lazy[k];
    double x = dat[k].first * cos(t) + dat[k].second * sin(t);
    double y = -dat[k].first * sin(t) + dat[k].second * cos(t);
    dat[k] = make_pair(x, y);
    if ((k * 2 + 2) < lazy.size()) {
      lazy[(k * 2 + 1)] += lazy[k];
      if (lazy[(k * 2 + 1)] >= pi * 2) lazy[(k * 2 + 1)] -= pi * 2;
      lazy[(k * 2 + 2)] += lazy[k];
      if (lazy[(k * 2 + 2)] >= pi * 2) lazy[(k * 2 + 2)] -= pi * 2;
    }
    lazy[k] = 0.0;
  }
  void update(int a, int b, int k, int l, int r, const double &d) {
    propagate(k, l, r);
    if (r <= a || b <= l) return;
    if (a <= l && r <= b) {
      lazy[k] = d;
      propagate(k, l, r);
    } else {
      int m = (l + r) / 2;
      update(a, b, (k * 2 + 1), l, m, d);
      update(a, b, (k * 2 + 2), m, r, d);
      dat[k] = combine(dat[(k * 2 + 1)], dat[(k * 2 + 2)]);
    }
  }
  void update(int a, int b, int k, int l, int r, int z) {
    propagate(k, l, r);
    if (r <= a || b <= l) return;
    if (a <= l && r <= b) {
      double len =
          sqrt(dat[k].first * dat[k].first + dat[k].second * dat[k].second);
      dat[k].first *= (len + z) / len;
      dat[k].second *= (len + z) / len;
    } else {
      int m = (l + r) / 2;
      update(a, b, (k * 2 + 1), l, m, z);
      update(a, b, (k * 2 + 2), m, r, z);
      dat[k] = combine(dat[(k * 2 + 1)], dat[(k * 2 + 2)]);
    }
  }
  void update(int a, int z) { update(a, a + 1, 0, 0, n, z); }
  pair<double, double> query(int a, int b) { return query(a, b + 1, 0, 0, n); }
  pair<double, double> query(int a, int b, int k, int l, int r) {
    if (r <= a || b <= l) return make_pair(0.0, 0.0);
    propagate(k, l, r);
    if (a <= l && r <= b) {
      return dat[k];
    } else {
      pair<double, double> vl = query(a, b, (k * 2 + 1), l, (l + r) / 2);
      pair<double, double> vr = query(a, b, (k * 2 + 2), (l + r) / 2, r);
      return combine(vl, vr);
    }
  }
  pair<double, double> combine(pair<double, double> vl,
                               pair<double, double> vr) {
    return make_pair(vl.first + vr.first, vl.second + vr.second);
  }
};
int main() {
  int N, M;
  scanf("%d", &N), scanf("%d", &M);
  SegmentTree st(N + 1);
  for (int i = 0; i < (M); i++) {
    int x;
    scanf("%d", &x);
    if (x == 2) {
      int y, z;
      scanf("%d", &y), scanf("%d", &z);
      st.update(y, N, z * 1.0 / 360 * 2 * pi);
    } else {
      int y, z;
      scanf("%d", &y), scanf("%d", &z);
      st.update(y, z);
    }
    auto ans = st.query(1, N);
    printf("%.10f %.10f\n", ans.first, ans.second);
  }
}
