#include <bits/stdc++.h>   using namespace std;   const long long MOD = 1e9 + 7;   template <typename T>   void chmin(T &a, T b) {     if (a > b) a = b;   }   template <typename T>   void chmax(T &a, T b) {     if (a < b) a = b;   }   template <typename T>   void add(T &a, T b) {     a += b;     if (a < 0) a += MOD;     if (a >= MOD) a -= MOD;   }   void FastIn(int &T) {     char C;     bool F = 0;     for (; C = getchar(), C <  0  || C >  9 ;)       if (C ==  - ) F = 1;     for (T = C -  0 ; C = getchar(), C >=  0  && C <=  9 ; T = T * 10 + C -  0 )       ;     F && (T = -T);   }   int in() {     int x;     scanf( %d , &x);     return x;   }   long long lin() {     long long x;     scanf( %lld , &x);     return x;   }   double din() {     double x;     scanf( %lf , &x);     return x;   }   long long p2[63];   void initP2() {     p2[0] = 1;     for (int i = (1); i <= (62); ++i) {       p2[i] = p2[i - 1] * 2;     }   }   int countBitsBaseB(long long n, long long b) {     if (n == 0) return 1;     int ret = 0;     while (n) {       ret++;       n /= b;     }     return ret;   }   int count1(long long n) {     int ret = 0;     while (n) {       ret += n & 1;       n >>= 1;     }     return ret;   }   long long c[1024][1024] = {0};   void initC() {     for (int i = 0; i < (1024); ++i) {       for (int j = 0; j < (i + 1); ++j) {         if (j == 0 || j == i) {           c[i][j] = 1;         } else {           c[i][j] = c[i - 1][j - 1];           add(c[i][j], c[i - 1][j]);         }       }     }   }   bool IsPrime(int n) {     if (n < 2) return false;     for (int i = 2; i * i <= n; i++) {       if (n % i == 0) return false;     }     return true;   }   void init() {     initP2();     initC();   }   const unsigned INVALID = 2000000001;   struct CMD {     int cmd, x, y;   } q[(int)(2e5 + 10)];   struct DSU {     int f[(int)(2e5 + 10)];     int h[(int)(2e5 + 10)];     int *addr[(int)(2e5 + 10) << 2];     int val[(int)(2e5 + 10) << 2];     int stkSize = 0;     void init(int n) {       for (int i = (1); i <= (n); ++i) {         f[i] = i;         h[i] = 1;       }       stkSize = 0;     }     int find(int x) { return x == f[x] ? x : find(f[x]); }     void merge(int x, int y) {       x = find(x);       y = find(y);       if (x == y) {         return;       }       if (h[x] > h[y]) swap(x, y);       addr[stkSize] = &f[x];       val[stkSize++] = f[x];       f[x] = y;       if (h[x] == h[y]) {         addr[stkSize] = &h[y];         val[stkSize++] = h[y];         h[y]++;       }     }     void mergeFast(int x, int y) {       x = find(x);       y = find(y);       if (x == y) {         return;       }       if (h[x] > h[y]) swap(x, y);       f[x] = y;       if (h[x] == h[y]) {         h[y]++;       }     }     void undo(int size) {       while (stkSize > size) {         stkSize--;         *addr[stkSize] = val[stkSize];       }     }       private:   } dsu;   const int BLOCK = 5000;   int last[(int)(2e5 + 10)];   pair<int, int> GetEdge(int x, int y, int der, int n) {     x += der - 1;     if (x >= n) x -= n;     y += der - 1;     if (y >= n) y -= n;     if (x > y) swap(x, y);     return make_pair(x + 1, y + 1);   }   void Switch(const pair<int, int> &edge, set<pair<int, int>> &edges) {     if (edges.find(edge) != edges.end()) {       edges.erase(edge);     } else {       edges.insert(edge);     }   }   int main() {     init();     int n = in(), m = in();     for (int i = (1); i <= (m); ++i) {       q[i].cmd = in();       q[i].x = in();       q[i].y = in();     }     last[0] = 0;     for (int start = 1; start <= m; start += BLOCK) {       int end = start + BLOCK - 1;       if (end > m) {         end = m;       }       set<pair<int, int>> nowEdges;       nowEdges.clear();       for (int i0 = (start); i0 <= (end); ++i0) {         nowEdges.insert(GetEdge(q[i0].x, q[i0].y, 0, n));         nowEdges.insert(GetEdge(q[i0].x, q[i0].y, 1, n));       }       set<pair<int, int>> possible;       possible.clear();       set<pair<int, int>> impossible;       impossible.clear();       for (int i1 = (1); i1 <= (start - 1); ++i1) {         if (q[i1].cmd == 2) {           continue;         }         auto edge0 = GetEdge(q[i1].x, q[i1].y, last[i1 - 1], n);         if (nowEdges.find(edge0) == nowEdges.end()) {           Switch(edge0, impossible);         } else {           Switch(edge0, possible);         }       }       dsu.init(n);       for (auto edge1 : impossible) {         dsu.mergeFast(edge1.first, edge1.second);       }       for (int i2 = (start); i2 <= (end); ++i2) {         if (q[i2].cmd == 1) {           last[i2] = last[i2 - 1];           Switch(GetEdge(q[i2].x, q[i2].y, last[i2 - 1], n), possible);           continue;         } else {           const int top = dsu.stkSize;           for (auto edge1 : possible) {             dsu.merge(edge1.first, edge1.second);           }           auto queryEdge = GetEdge(q[i2].x, q[i2].y, last[i2 - 1], n);           last[i2] = dsu.find(queryEdge.first) == dsu.find(queryEdge.second);           putchar( 0  + last[i2]);           dsu.undo(top);         }       }     }     cout << endl;     return 0;   }   