#include <bits/stdc++.h>
using namespace std;
template <typename T1, typename T2>
bool umax(T1 &a, const T2 &b) {
  return a < b ? a = b, 1 : 0;
}
template <typename T1, typename T2>
bool umin(T1 &a, const T2 &b) {
  return a > b ? a = b, 1 : 0;
}
template <typename T>
T sqr(T a) {
  return a * a;
}
mt19937 rng(20010709);
const int mod = 998244353;
const int INF = 1000000001;
const int N = 200005;
bool endNode[N * 8];
int n, q;
set<tuple<int, int, int>> used[N];
long long seg[8 * N], mult[8 * N], add[8 * N], len[8 * N];
void build(int v, int l, int r);
long long get(int v, int l, int r, int cl, int cr);
void modifyMult(int v, int l, int r, int cl, int cr);
void modifyAdd(int v, int l, int r, int cl, int cr);
void push(int v);
int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  cin >> n >> q;
  for (int i = 1; i < N; i++) used[i].insert(make_tuple(1, n, 0));
  build(1, 1, n);
  for (int i = 1; i <= q; i++) {
    int type, l, r, x;
    cin >> type >> l >> r;
    if (type == 1) {
      cin >> x;
      auto it = prev(used[x].upper_bound(make_tuple(l, INF, 0)));
      while ((it != used[x].end()) && (get<0>(*it) <= r)) {
        int L = get<0>(*it);
        int R = get<1>(*it);
        int type = get<2>(*it);
        if (l > L) {
          if (r < R) {
            used[x].insert(make_tuple(L, l - 1, type));
            used[x].insert(make_tuple(r + 1, R, type));
            if (type == 1)
              modifyMult(1, 1, n, l, r);
            else
              modifyAdd(1, 1, n, l, r);
            it = next(it);
            used[x].erase(prev(it));
          } else {
            used[x].insert(make_tuple(L, l - 1, type));
            if (type == 1)
              modifyMult(1, 1, n, l, R);
            else
              modifyAdd(1, 1, n, l, R);
            it = next(it);
            used[x].erase(prev(it));
          }
        } else if (r < R) {
          used[x].insert(make_tuple(r + 1, R, type));
          if (type == 1)
            modifyMult(1, 1, n, L, r);
          else
            modifyAdd(1, 1, n, L, r);
          it = next(it);
          used[x].erase(prev(it));
        } else {
          if (type == 1)
            modifyMult(1, 1, n, L, R);
          else
            modifyAdd(1, 1, n, L, R);
          it++;
          used[x].erase(prev(it));
        }
      }
      used[x].insert(make_tuple(l, r, 1));
      it = used[x].lower_bound(make_tuple(l, r, 1));
      if ((it != used[x].begin()) && (get<2>(*prev(it)) == 1)) {
        l = get<0>(*prev(it));
        used[x].erase(prev(it));
        used[x].erase(it);
        used[x].insert(make_tuple(l, r, 1));
      }
      it = used[x].lower_bound(make_tuple(l, r, 1));
      if ((next(it) != used[x].end()) && (get<2>(*next(it))) == 1) {
        r = get<1>(*next(it));
        used[x].erase(next(it));
        used[x].erase(it);
        used[x].insert(make_tuple(l, r, 1));
      }
    } else {
      cout << get(1, 1, n, l, r) << "\n";
    }
  }
  cout << "\n"
       << "\n";
  return 0;
}
void build(int v, int l, int r) {
  mult[v] = 1;
  len[v] = r - l + 1;
  if (l == r) {
    endNode[v] = 1;
    return;
  }
  int mid = (l + r) / 2;
  build(v * 2, l, mid);
  build(v * 2 + 1, mid + 1, r);
}
long long get(int v, int l, int r, int cl, int cr) {
  push(v);
  if ((l == cl) && (r == cr)) return seg[v];
  int mid = (l + r) / 2;
  long long result = 0;
  if (cl <= mid)
    result = get(v * 2, l, mid, cl, min(mid, cr));
  else
    push(v * 2);
  if (cr > mid)
    result = (result + get(v * 2 + 1, mid + 1, r, max(mid + 1, cl), cr)) % mod;
  else
    push(v * 2 + 1);
  seg[v] = (seg[v * 2] + seg[v * 2 + 1]) % mod;
  return result;
}
void modifyMult(int v, int l, int r, int cl, int cr) {
  push(v);
  if ((l == cl) && (r == cr)) {
    mult[v] = mult[v] * 2 % mod;
    push(v);
    return;
  }
  int mid = (l + r) / 2;
  if (cl <= mid)
    modifyMult(v * 2, l, mid, cl, min(mid, cr));
  else
    push(v * 2);
  if (cr > mid)
    modifyMult(v * 2 + 1, mid + 1, r, max(cl, mid + 1), cr);
  else
    push(v * 2 + 1);
  seg[v] = (seg[v * 2] + seg[v * 2 + 1]) % mod;
}
void modifyAdd(int v, int l, int r, int cl, int cr) {
  push(v);
  if ((l == cl) && (r == cr)) {
    add[v] = (add[v] + 1) % mod;
    push(v);
    return;
  }
  int mid = (l + r) / 2;
  if (cl <= mid)
    modifyAdd(v * 2, l, mid, cl, min(mid, cr));
  else
    push(v * 2);
  if (cr > mid)
    modifyAdd(v * 2 + 1, mid + 1, r, max(cl, mid + 1), cr);
  else
    push(v * 2 + 1);
  seg[v] = (seg[v * 2] + seg[v * 2 + 1]) % mod;
}
void sinkMult(int v) {
  mult[v] %= mod;
  add[v] %= mod;
  seg[v] = seg[v] * mult[v] % mod;
  add[v] = add[v] * mult[v] % mod;
  mult[v * 2] = mult[v * 2] * mult[v] % mod;
  mult[v * 2 + 1] = mult[v * 2 + 1] * mult[v] % mod;
  mult[v] = 1;
}
void push(int v) {
  mult[v] %= mod;
  add[v] %= mod;
  sinkMult(v);
  seg[v] = (seg[v] + add[v] * len[v] % mod * mult[v] % mod) % mod;
  if (!endNode[v]) {
    sinkMult(v * 2);
    sinkMult(v * 2 + 1);
  }
  add[v * 2] = (add[v * 2] + add[v]) % mod;
  add[v * 2 + 1] = (add[v * 2 + 1] + add[v]) % mod;
  add[v] = 0;
}
