#include <bits/stdc++.h>
using namespace std;
const int N = 300010;
const int DIM = 3;
struct matrix {
  double mat[DIM][DIM];
  matrix() { memset(mat, 0, sizeof(mat)); }
};
matrix mul(matrix a, matrix b) {
  matrix c;
  for (int i = 0; i < (DIM); ++i) {
    for (int j = 0; j < (2); ++j) {
      for (int k = 0; k < (DIM); ++k) {
        c.mat[i][j] += a.mat[i][k] * b.mat[k][j];
      }
    }
  }
  c.mat[0][2] = c.mat[1][2] = 0;
  c.mat[2][2] = 1;
  return c;
}
matrix identity;
struct node {
  matrix mat;
  bool push;
  matrix toPush;
  inline void combine(const node& left, const node& right) {}
} T[N << 2];
struct segmentTree {
  int n;
  segmentTree(int m) : n(m) { build(0, 0, n - 1); }
  void build(int x, int l, int r) {
    T[x].toPush = identity;
    T[x].push = false;
    if (l == r) {
      T[x].mat = identity;
      return;
    }
    int m = (l + r) >> 1;
    build((x << 1) + 1, l, m);
    build((x << 1) + 2, m + 1, r);
    T[x].combine(T[(x << 1) + 1], T[(x << 1) + 2]);
  }
  inline void resolve(int x, int l, int r) {
    if (T[x].push) {
      T[x].mat = mul(T[x].mat, T[x].toPush);
      if (l != r) {
        T[(x << 1) + 1].toPush = mul(T[(x << 1) + 1].toPush, T[x].toPush);
        T[(x << 1) + 2].toPush = mul(T[(x << 1) + 2].toPush, T[x].toPush);
        T[(x << 1) + 1].push = T[(x << 1) + 2].push = true;
      }
      T[x].push = false;
      T[x].toPush = identity;
    }
  }
  void updateRange(int x, int l, int r, int ql, int qr, matrix v) {
    resolve(x, l, r);
    if (l > qr || r < ql) return;
    if (l >= ql && r <= qr) {
      T[x].toPush = v;
      T[x].push = true;
      resolve(x, l, r);
      return;
    }
    int m = (l + r) >> 1;
    updateRange((x << 1) + 1, l, m, ql, qr, v);
    updateRange((x << 1) + 2, m + 1, r, ql, qr, v);
    T[x].combine(T[(x << 1) + 1], T[(x << 1) + 2]);
  }
  matrix queryPoint(int x, int l, int r, int pos) {
    resolve(x, l, r);
    if (l == pos and r == pos) return T[x].mat;
    int m = (l + r) >> 1;
    if (pos <= m)
      return queryPoint((x << 1) + 1, l, m, pos);
    else
      return queryPoint((x << 1) + 2, m + 1, r, pos);
  }
  void update(int ql, int qr, matrix v) { updateRange(0, 0, n - 1, ql, qr, v); }
  pair<double, double> query(int pos) {
    matrix mat = queryPoint(0, 0, n - 1, pos);
    return make_pair(mat.mat[0][0] * pos + mat.mat[1][0] * 0 + mat.mat[2][0],
                     mat.mat[0][1] * pos + mat.mat[1][1] * 0 + mat.mat[2][1]);
  }
};
double s[400], c[400];
const double PI = acos(-1.0);
int main() {
  for (int i = 0; i < (DIM); ++i) {
    identity.mat[i][i] = 1;
  }
  for (int i = 0; i < (360); ++i) {
    s[i] = sin(PI * i / 180.0);
    c[i] = cos(PI * i / 180.0);
  }
  int n, m;
  scanf("%d%d", &n, &m);
  segmentTree seg(n + 1);
  while (m--) {
    int cmd;
    scanf("%d", &cmd);
    if (cmd == 1) {
      int x, l;
      scanf("%d%d", &x, &l);
      pair<double, double> p1 = seg.query(x - 1);
      pair<double, double> p2 = seg.query(x);
      double angle = atan2(p2.second - p1.second, p2.first - p1.first);
      matrix mat = identity;
      mat.mat[2][0] = (double)l * cos(angle);
      mat.mat[2][1] = (double)l * sin(angle);
      seg.update(x, n, mat);
    } else {
      int x, ang;
      scanf("%d%d", &x, &ang);
      ang = 360 - ang;
      pair<double, double> p = seg.query(x - 1);
      matrix mat;
      mat.mat[0][0] = c[ang], mat.mat[0][1] = s[ang], mat.mat[0][2] = 0;
      mat.mat[1][0] = -s[ang], mat.mat[1][1] = c[ang], mat.mat[1][2] = 0;
      mat.mat[2][0] = -p.first * c[ang] + p.second * s[ang] + p.first;
      mat.mat[2][1] = -p.first * s[ang] - p.second * c[ang] + p.second;
      mat.mat[2][2] = 1;
      seg.update(x, n, mat);
    }
    pair<double, double> p = seg.query(n);
    printf("%.10lf %.10lf\n", p.first, p.second);
  }
  return 0;
}
