#include <bits/stdc++.h>
#pragma comment(linker, "/stack:200000000")
#pragma GCC optimize("Ofast")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
using namespace std;
const int maxn = 300000;
const int maxx = 359;
const double pi = 4.0 * atan(1.0);
double SIN[maxx + 1];
double COS[maxx + 1];
class Matrix {
  const static int maxn = 3;
  int n, m;
  double a[maxn][maxn];

 public:
  Matrix() {
    n = 0;
    m = 0;
  }
  Matrix(int n, int m) {
    this->n = n;
    this->m = m;
    for (int i = 0; i < n; i++)
      for (int j = 0; j < m; j++) this->a[i][j] = 0;
  }
  void setA(int i, int j, double v) { this->a[i][j] = v; }
  double getA(int i, int j) { return this->a[i][j]; }
  string toString() {
    string res = "";
    res += to_string(this->n) + " " + to_string(this->m) + "\n";
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < m; j++) res += to_string(this->getA(i, j)) + " ";
      res += "\n";
    }
    return res;
  }
  Matrix multiply(Matrix o) {
    Matrix res = Matrix(this->n, o.m);
    for (int i = 0; i < this->n; i++) {
      for (int j = 0; j < o.m; j++) {
        double sum = 0;
        for (int k = 0; k < this->m; k++)
          sum += this->getA(i, k) * o.getA(k, j);
        res.setA(i, j, sum);
      }
    }
    return res;
  }
};
int n, m;
Matrix identity;
void init() {
  identity = Matrix(3, 3);
  for (int i = 0; i < 3; i++) identity.setA(i, i, 1);
  for (int i = 1; i <= 359; i++) {
    SIN[i] = sin(i / 180.0 * pi);
    COS[i] = cos(i / 180.0 * pi);
  }
}
class SegmentTree {
  const static int maxn = 300000;

 public:
  int n;
  int *l, *r;
  Matrix *it;
  bool *lazy;
  Matrix *lazyMatrix;
  SegmentTree() {}
  void initHelper(int i, int x, int y) {
    l[i] = x;
    r[i] = y;
    it[i] = identity;
    lazy[i] = false;
    lazyMatrix[i] = identity;
    if (x != y) {
      int m = (x + y) / 2;
      initHelper(i * 2, x, m);
      initHelper(i * 2 + 1, m + 1, y);
    }
  }
  SegmentTree(int n) {
    this->n = n;
    this->l = (int *)malloc(n * 4 * sizeof(int));
    this->r = (int *)malloc(n * 4 * sizeof(int));
    this->it = (Matrix *)malloc(n * 4 * sizeof(Matrix));
    this->lazy = (bool *)malloc(n * 4 * sizeof(bool));
    this->lazyMatrix = (Matrix *)malloc(n * 4 * sizeof(Matrix));
    this->initHelper(1, 1, n);
  }
  void updateLazy(int i) {
    if (lazy[i]) {
      if (l[i] != r[i]) {
        lazy[i * 2] = true;
        lazyMatrix[i * 2] = lazyMatrix[i].multiply(lazyMatrix[i * 2]);
        lazy[i * 2 + 1] = true;
        lazyMatrix[i * 2 + 1] = lazyMatrix[i].multiply(lazyMatrix[i * 2 + 1]);
      }
      it[i] = lazyMatrix[i].multiply(it[i]);
      lazyMatrix[i] = identity;
      lazy[i] = false;
    }
  }
  void updateHelper(int i, int x, int y, Matrix m) {
    updateLazy(i);
    if (y < l[i] or r[i] < x) return;
    if (x <= l[i] and r[i] <= y) {
      lazy[i] = true;
      lazyMatrix[i] = m.multiply(lazyMatrix[i]);
      return;
    }
    updateHelper(i * 2, x, y, m);
    updateHelper(i * 2 + 1, x, y, m);
  }
  void update(int i, Matrix m) { updateHelper(1, i, n, m); }
  Matrix getHelper(int i, int x) {
    updateLazy(i);
    if (x < l[i] or r[i] < x) return identity;
    if (l[i] == r[i] and x == l[i]) return it[i];
    Matrix res = identity;
    res = getHelper(i * 2, x).multiply(res);
    res = getHelper(i * 2 + 1, x).multiply(res);
    return res;
  }
  Matrix get(int i) { return getHelper(1, i); }
} ST;
Matrix findPoint(int i) {
  Matrix m = ST.get(i);
  Matrix p = Matrix(3, 1);
  p.setA(0, 0, i - 1);
  p.setA(1, 0, 0);
  p.setA(2, 0, 1);
  p = m.multiply(p);
  return p;
}
Matrix getVector(int i) {
  Matrix p1 = findPoint(i);
  Matrix p2 = findPoint(i + 1);
  Matrix r = Matrix(3, 1);
  r.setA(0, 0, p2.getA(0, 0) - p1.getA(0, 0));
  r.setA(1, 0, p2.getA(1, 0) - p1.getA(1, 0));
  r.setA(2, 0, 1);
  return r;
}
void updateTranslate(int i, double u, double v) {
  Matrix uv = Matrix(3, 3);
  uv.setA(0, 0, 1);
  uv.setA(1, 1, 1);
  uv.setA(2, 2, 1);
  uv.setA(0, 2, u);
  uv.setA(1, 2, v);
  ST.update(i + 1, uv);
}
void updateTranslate(int i, double x) {
  Matrix p1 = findPoint(i);
  Matrix p2 = findPoint(i + 1);
  Matrix uv = Matrix(3, 3);
  uv.setA(0, 0, 1);
  uv.setA(1, 1, 1);
  uv.setA(2, 2, 1);
  uv.setA(0, 2, p2.getA(0, 0) - p1.getA(0, 0));
  uv.setA(1, 2, p2.getA(1, 0) - p1.getA(1, 0));
  double norm = hypot(uv.getA(0, 2), uv.getA(1, 2));
  uv.setA(0, 2, uv.getA(0, 2) / norm * x);
  uv.setA(1, 2, uv.getA(1, 2) / norm * x);
  ST.update(i + 1, uv);
}
void updateRotation(int i, int x) {
  Matrix uv = Matrix(3, 3);
  uv.setA(0, 0, COS[x]);
  uv.setA(0, 1, SIN[x]);
  uv.setA(1, 0, -SIN[x]);
  uv.setA(1, 1, COS[x]);
  uv.setA(2, 2, 1);
  ST.update(i + 1, uv);
}
int main() {
  ios_base::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  cin >> n >> m;
  init();
  ST = SegmentTree(n + 1);
  for (int j = 1; j <= m; j++) {
    int o, i;
    double x;
    cin >> o >> i >> x;
    if (o == 1) {
      updateTranslate(i, x);
    } else {
      Matrix uv = findPoint(i);
      updateTranslate(i, -uv.getA(0, 0), -uv.getA(1, 0));
      updateRotation(i, x);
      updateTranslate(i, uv.getA(0, 0), uv.getA(1, 0));
    }
    Matrix r = findPoint(n + 1);
    cout << setprecision(4) << fixed << r.getA(0, 0) << ' ' << r.getA(1, 0)
         << '\n';
  }
  return 0;
}
