#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using pii = pair<int, int>;
const i64 MOD = 998244353;
const int N = 2e5 + 5;
struct Segment {
  int l, r;
  bool on;
};
struct Nod {
  i64 value, lazy_mul, lazy_add;
};
multiset<Segment> ivset[N];
Nod pom[4 * N];
int n, q, ql, qr;
static bool operator<(const Segment &a, const Segment &b) {
  return pii(a.l, a.r) < pii(b.l, b.r);
}
static void spread(Nod &src, Nod &targ) {
  targ.lazy_mul = targ.lazy_mul * src.lazy_mul % MOD;
  targ.lazy_add = (targ.lazy_add * src.lazy_mul + src.lazy_add) % MOD;
}
static void mul_update(int nod, int st, int dr) {
  if (ql <= st && dr <= qr) {
    pom[nod].lazy_mul = pom[nod].lazy_mul * 2 % MOD;
    pom[nod].lazy_add = pom[nod].lazy_add * 2 % MOD;
    return;
  }
  spread(pom[nod], pom[2 * nod]);
  spread(pom[nod], pom[2 * nod + 1]);
  pom[nod].lazy_mul = 1;
  pom[nod].lazy_add = 0;
  int mid = (st + dr) / 2;
  if (ql <= mid) mul_update(2 * nod, st, mid);
  if (mid < qr) mul_update(2 * nod + 1, mid + 1, dr);
  pom[nod] = {0, 1, 0};
  pom[nod].value = (pom[2 * nod].value * pom[2 * nod].lazy_mul +
                    pom[2 * nod].lazy_add * (mid - st + 1)) %
                   MOD;
  pom[nod].value = (pom[2 * nod + 1].value * pom[2 * nod + 1].lazy_mul +
                    pom[2 * nod + 1].lazy_add * (dr - mid) + pom[nod].value) %
                   MOD;
}
static void add_update(int nod, int st, int dr) {
  if (ql <= st && dr <= qr) {
    pom[nod].lazy_add = (pom[nod].lazy_add + 1) % MOD;
    return;
  }
  spread(pom[nod], pom[2 * nod]);
  spread(pom[nod], pom[2 * nod + 1]);
  pom[nod].lazy_mul = 1;
  pom[nod].lazy_add = 0;
  int mid = (st + dr) / 2;
  if (ql <= mid) add_update(2 * nod, st, mid);
  if (mid < qr) add_update(2 * nod + 1, mid + 1, dr);
  pom[nod] = {0, 1, 0};
  pom[nod].value = (pom[2 * nod].value * pom[2 * nod].lazy_mul +
                    pom[2 * nod].lazy_add * (mid - st + 1)) %
                   MOD;
  pom[nod].value = (pom[2 * nod + 1].value * pom[2 * nod + 1].lazy_mul +
                    pom[2 * nod + 1].lazy_add * (dr - mid) + pom[nod].value) %
                   MOD;
}
static i64 tree_query(int nod, int st, int dr) {
  if (ql <= st && dr <= qr)
    return (pom[nod].value * pom[nod].lazy_mul +
            pom[nod].lazy_add * (dr - st + 1)) %
           MOD;
  spread(pom[nod], pom[2 * nod]);
  spread(pom[nod], pom[2 * nod + 1]);
  pom[nod].lazy_mul = 1;
  pom[nod].lazy_add = 0;
  i64 ant = 0;
  int mid = (st + dr) / 2;
  if (ql <= mid) ant += tree_query(2 * nod, st, mid);
  if (mid < qr) ant += tree_query(2 * nod + 1, mid + 1, dr);
  pom[nod] = {0, 1, 0};
  pom[nod].value = (pom[2 * nod].value * pom[2 * nod].lazy_mul +
                    pom[2 * nod].lazy_add * (mid - st + 1)) %
                   MOD;
  pom[nod].value = (pom[2 * nod + 1].value * pom[2 * nod + 1].lazy_mul +
                    pom[2 * nod + 1].lazy_add * (dr - mid) + pom[nod].value) %
                   MOD;
  return ant % MOD;
}
static void update(int l, int r, int val) {
  multiset<Segment> &ivs = ivset[val];
  multiset<Segment>::iterator it, itt;
  bool left_on, right_on;
  int left_bound, right_bound;
  it = ivs.upper_bound(Segment{l, N, 0});
  it = prev(it);
  while (it != end(ivs) && it->l <= r) {
    ql = max(it->l, l);
    qr = min(it->r, r);
    it->on ? mul_update(1, 1, n) : add_update(1, 1, n);
    it = next(it);
  }
  left_bound = l, left_on = false;
  right_bound = r, right_on = false;
  it = ivs.upper_bound(Segment{l - 1, N, 0});
  if (it != begin(ivs)) {
    it = prev(it);
    left_bound = it->l;
    right_bound = it->r;
    left_on = right_on = it->on;
    ivs.erase(it);
  }
  it = ivs.upper_bound(Segment{l, -1, 0});
  while (it != end(ivs) && it->l <= r) {
    itt = next(it);
    right_bound = it->r;
    right_on = it->on;
    ivs.erase(it);
    it = itt;
  }
  if (left_bound < l) ivs.insert(Segment{left_bound, l - 1, left_on});
  if (right_bound > r) ivs.insert(Segment{r + 1, right_bound, right_on});
  ivs.insert(Segment{l, r, 1});
}
int main() {
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
  int op, l, r, val;
  cin >> n >> q;
  for (int i = 0; i < N; ++i) ivset[i].insert({1, n, 0});
  for (int i = 0; i < 4 * N; ++i) pom[i] = {0, 1, 0};
  while (q--) {
    cin >> op;
    switch (op) {
      case 1: {
        cin >> l >> r >> val;
        ql = l, qr = r;
        update(l, r, val);
        break;
      }
      case 2: {
        cin >> l >> r;
        ql = l, qr = r;
        cout << tree_query(1, 1, n) << '\n';
        break;
      }
    }
  }
  return 0;
}
