#define _CRT_SECURE_NO_WARNINGS
#pragma comment(linker, "/STACK:1000000000")
#include <algorithm>
#include <cstdio>
#include <vector>
#include <set>
 
using std :: pair;
using std :: vector;
using std :: set;
using std :: make_pair;
 
const int MaxN = 100010;
const int MaxQ = 100010;
const int MaxS = MaxN + MaxQ;
const int K = 100;
const int MaxB = MaxS / K + 10;
const int MOD = 1000000007;
 
pair < int, int > X[MaxS], Y[MaxS];
int n, q, total;
int a[MaxS], ind[MaxS], tmp[MaxS];
set < int > all[MaxS];
 
struct Node {
//Implicit cartesian tree
	int active, sum, size, index;
	int y;
	Node *l, *r;
	Node(int _index) : index(_index), active(1), sum(1), size(1), y(rand() * RAND_MAX + rand()), l(NULL), r(NULL) {
	}
	friend int GetSize(Node *t) {
		return t != NULL ? t->size : 0;
	}
	friend int GetSum(Node *t) {
		return t != NULL ? t->sum : 0;
	}
	friend void Update(Node *t) {
		if(t != NULL) {
			t->sum = t->active + GetSum(t->l) + GetSum(t->r);
			t->size = 1 + GetSize(t->l) + GetSize(t->r);
		}
	}
	friend void Split(Node *t, Node *&l, Node *&r, int key) {
		if(t == NULL) {
			l = r = NULL;
			return;
		}
		int my = GetSize(t->l) + 1;
		if(my <= key) {
			Split(t->r, t->r, r, key - my);
			l = t;
			Update(l);
			return;
		}
		Split(t->l, l, t->l, key);
		r = t;
		Update(r);
	}
	friend Node *Merge(Node *l, Node *r) {
		if(!l || !r) {
			return l ? l : r;
		}
		if(l->y < r->y) {
			l->r = Merge(l->r, r);
			Update(l);
			return l;
		}
		r->l = Merge(l, r->l);
		Update(r);
		return r;
	}
	void Debug() {
		struct Temp {
			void Print(Node *t) {
				if(t != NULL) {
					Print(t->l);
					printf("%d ", t->index);
					Print(t->r);
				}
			}
		} y;
		y.Print(this);
		printf("\n");
	}
	friend int FindIndex(Node *&root, int x) {
	//Find x-th 1 in tree
		Node *now = root;
		int pos = 0;
		while(now->l != NULL || now->r != NULL) {
			int lf = GetSum(now->l);
			if(x <= lf) {
				now = now->l;
				continue;
			}
			x -= lf;
			pos += GetSize(now->l);
			if(x == now->active) {
				break;
			}
			pos += 1;
			x -= now->active;
			now = now->r;
		}
		return pos + 1;
	}
	friend void SetValue(Node *&root, int Pos, int Val, int Index = -1) {
		int x = Index == -1 ? FindIndex(root, Pos) : Index;
		Node *a, *b;
		Split(root, a, b, x);
		Split(a, a, root, x - 1);
		root->active = Val;
		Update(root);
		root = Merge(a, Merge(root, b));
	}
 
	friend void Insert(Node *&root, int Pos, int Index) {
		if(Pos == 0) {
			root = Merge(new Node(Index), root);
			return;
		}
		int x = FindIndex(root, Pos);
		Node *a, *b;
		Split(root, a, b, x);
		root = Merge(a, Merge(new Node(Index), b));
	}
};
//If exists Palindromic Tree, Why not?:)
struct DistinctNumbersTree {
//But It's just 2D Fenwick Tree
	int t[MaxB][MaxB], n;
	vector < int > b;
	void add(int &a, int b) {
		if((a += b) >= MOD) {
			a -= MOD;
		}
	}
	void sub(int &a, int b) {
		if((a -= b) < 0) {
			a += MOD;
		}
	}
	void init(int sz, const vector < int > &tmp) {
		n = sz;
		b = tmp;
		for(int i = 0; i <= sz; ++i) {
			for(int j = 0; j <= sz; ++j) {
				t[i][j] = 0;
			}
		}
	}
	void upd2(int x, int y, int val) {
		for(int i = x; i <= n; i |= i + 1) {
			for(int j = y; j <= n; j |= j + 1) {
				add(t[i][j], val);
			}
		}
	}
	void upd(int x, int y, int val) {
		if(val < 0) {
			val = MOD - b[-val - 1];
		} else {
			val = b[val - 1];
		}
		upd2(x / K, y / K, val);
	}
	int sum(int x, int y) {
		int ret = 0;
		for(int i = x; i >= 0; i &= i + 1, --i) {
			for(int j = y; j >= 0; j &= j + 1, --j) {
				add(ret, t[i][j]);
			}
		}
		return ret;
	}
	int query(int p, int q) {
		int ret = 0;
		if(q - p <= 4 * K) {   // if length of [p..q] small, naive:
			for(int i = p; i <= q; ++i) {
				int bal = (X[i].first >= p && X[i].first <= q) - (X[i].second >= p && X[i].second <= q);
				if(bal != 0) add(ret, b[a[i] - 1]);
			}
			return ret;
		}
		//Split interval [p..q] into three intervals [p..(ceil(p/K)*K-1)], [ceil(L/K)*K..floor(R/K)*K-1], [floor(R/K)*K..q]
		//For first and third I used next fact - in each row and each column not more than two non-zero values(we can maintain these values)
		//For second interval I used two dimensional Fenwick tree
		int L = (p + K - 1) / K, R = q / K;
		add(ret, sum(R - 1, R - 1));
		sub(ret, sum(R - 1, L - 1));
		sub(ret, sum(L - 1, R - 1));
		add(ret, sum(L - 1, L - 1));
		L *= K; R *= K;
		for(int i = p; i < L; ++i) {
			int dx = (X[i].first >= p && X[i].first <= q) - (X[i].second >= p && X[i].second <= q);
			int dy = -(Y[i].first >= L && Y[i].first < R) + (Y[i].second >= L && Y[i].second < R);
			if(dx != 0) add(ret, b[a[i] - 1]);
			if(dy != 0) sub(ret, b[a[i] - 1]);
		}
		for(int i = R; i <= q; ++i) {
			int dx = (X[i].first >= p && X[i].first <= q) - (X[i].second >= p && X[i].second <= q);
			int dy = -(Y[i].first >= L && Y[i].first < R) + (Y[i].second >= L && Y[i].second < R);
			if(dx != 0) add(ret, b[a[i] - 1]);
			if(dy != 0) sub(ret, b[a[i] - 1]);
		}
		return ret;
	}
} pow0, pow1, pow2, pow3;
 
struct Query {
	int c, l, r;
} Q[MaxQ];
 
void Read() {
	scanf("%d%d", &n, &q);
	for(int i = 1; i <= n; ++i) {
		scanf("%d", &a[i]);
	}
	for(int i = 1; i <= q; ++i) {
		scanf("%d%d", &Q[i].c, &Q[i].l);
		if(Q[i].c != 3) {
			scanf("%d", &Q[i].r);
		}
	}
}
 
void Move(Node *root, int tmp) {
	if(root != NULL) {
		Move(root->l, tmp);
		Move(root->r, tmp);
		root->active = (root->index <= tmp);
		Update(root);
	}
}
 
void Order(Node *root, int &ptr) {
	if(root != NULL) {
		Order(root->l, ptr);
		ind[root->index] = ++ptr;
		Order(root->r, ptr);
	}
}
 
void GetIndexes() {
	Node *root = NULL;
	for(int i = 1; i <= n; ++i) {
		Insert(root, i - 1, ++total);
	}
	for(int i = 1; i <= q; ++i) {
		if(Q[i].c == 3) {
			SetValue(root, Q[i].l, 0);
		} else if(Q[i].c == 4) {
			Insert(root, Q[i].l, ++total);	//Add new item in Carteian tree
		}
	}
	Move(root, n);
	int now = 0;
	Order(root, now);
	for(int i = 1, temp = n; i <= q; ++i) {
		int fl = (Q[i].c != 4 ? FindIndex(root, Q[i].l) : ind[++temp]);
		int fr = (Q[i].c % 4 == 1 ? FindIndex(root, Q[i].r) : -1);
		if(Q[i].c == 3) {
			SetValue(root, fl, 0, fl);
		} else if(Q[i].c == 4) {
			SetValue(root, fl, 1, fl);
		}
		Q[i].l = fl;
		if(fr != -1) {
			Q[i].r = fr;
		}
	}
}
 
void upd(int x, int y, int val) {
	pow0.upd(x, y, val);
	pow1.upd(x, y, val);
	pow2.upd(x, y, val);
	pow3.upd(x, y, val);
}
 
void erase(int pos) {
	int old = a[pos];
	set < int > :: iterator it, pre, nxt;
	set < int > &s = all[old];
	nxt = it = s.lower_bound(pos); ++nxt;
	if(it != s.begin()) {
		pre = it; --pre;
	} else {
		pre = s.end();
	}
	X[*it] = Y[*it] = make_pair(-1, -1);
	upd(*it, *it, -old);
	if(pre != s.end()) {
		upd(*pre, *it, old);
		X[*pre].second = -1;
	}
	if(nxt != s.end()) {
		upd(*it, *nxt, old);
		Y[*nxt].first = -1;
	}
	if(pre != s.end() && nxt != s.end()) {
		upd(*pre, *nxt, -old);
		X[*pre].second = *nxt;
		Y[*nxt].first = *pre;
	}
	s.erase(pos);
	a[pos] = 0;
}
 
void insert(int pos, int val) {
	all[val].insert(pos);
	set < int > :: iterator it, pre, nxt;
	set < int > &t = all[val];
	nxt = it = t.lower_bound(pos); ++nxt;
	if(it != t.begin()) {
		pre = it; --pre;
	} else {
		pre = t.end();
	}
	upd(*it, *it, val);
	X[pos].first = Y[pos].second = pos;
	if(nxt != t.end()) {
		upd(*it, *nxt, -val);
		X[pos].second = *nxt;
		Y[*nxt].first = pos;
	}
	if(pre != t.end()) {
		upd(*pre, *it, -val);
		X[*pre].second = *it;
		Y[*it].first = *pre;
	}
	if(pre != t.end() && nxt != t.end()) {
		upd(*pre, *nxt, val);
	}
	a[pos] = val;
}
 
void BuildTree() {
	int size = total / K + 1;
	vector < int > p0, p1, p2, p3, b;
	for(int i = 1; i <= n; ++i) {
		b.push_back(a[i]);
	}
	for(int i = 1; i <= q; ++i) {
		if(Q[i].c % 2 == 0) {
			b.push_back(Q[i].r);
		}
	}
	sort(b.begin(), b.end());
	b.erase(unique(b.begin(), b.end()), b.end());	// Compress all values
	for(int i = 1; i <= n; ++i) {
		a[i] = lower_bound(b.begin(), b.end(), a[i]) - b.begin() + 1;
		tmp[ind[i]] = a[i];
	}
//	Build four DistinctNumbersTree(for sum of 3rd, 2nd, 1st and 0th powers of distinct numbers)
	p0.resize(b.size());
	p1.resize(b.size());
	p2.resize(b.size());
	p3.resize(b.size());
	for(int i = 0; i < b.size(); ++i) {
		p0[i] = 1;
		p1[i] = 1LL * p0[i] * b[i] % MOD;
		p2[i] = 1LL * p1[i] * b[i] % MOD;
		p3[i] = 1LL * p2[i] * b[i] % MOD;
	}
	pow0.init(size, p0);
	pow1.init(size, p1);
	pow2.init(size, p2);
	pow3.init(size, p3);
	for(int i = 1; i <= q; ++i) {
		if(Q[i].c % 2 == 0) {
			Q[i].r = lower_bound(b.begin(), b.end(), Q[i].r) - b.begin() + 1;
		}
	}
	for(int i = 1; i <= total; ++i) {
		a[i] = tmp[i];
		X[i] = Y[i] = make_pair(-1, -1);
	}
	for(int i = 1; i <= total; ++i) {	//Build 2D-table
		if(a[i] == 0) {
			continue;
		}
		upd(i, i, a[i]);
		X[i] = make_pair(i, -1);
		Y[i] = make_pair(i, -1);
		if(!all[a[i]].empty()) {
			int last = *--all[a[i]].end();
			upd(last, i, -a[i]);
			X[last].second = i;
			Y[i].first = last;
			Y[i].second = i;
		}
		all[a[i]].insert(all[a[i]].end(), i);
	}
}
 
int Inv(int n) {
// Inverse element
	return n == 1 ? n : 1LL * (MOD - MOD / n) * Inv(MOD % n) % MOD;
}
 
int calc(int s1, int s2, int s3) {
	int a1 = s1;
	int a2 = (1LL * s1 * s1 - s2 + MOD) % MOD * Inv(2) % MOD;
	return (1LL * a2 * s1 - 1LL * a1 * s2 + s3 + 1LL * MOD * MOD) % MOD * Inv(3) % MOD;
}
 
void Solve() {
	for(int i = 1; i <= q; ++i) {
		if(Q[i].c == 1) {
			printf("%d\n", calc(pow1.query(Q[i].l, Q[i].r), 
								pow2.query(Q[i].l, Q[i].r),
								pow3.query(Q[i].l, Q[i].r)));
		} else if(Q[i].c == 2) {
			if(a[Q[i].l] != Q[i].r) {
				erase(Q[i].l);
				insert(Q[i].l, Q[i].r);
			}
		} else if(Q[i].c == 3) {
			erase(Q[i].l);
		} else if(Q[i].c == 4) {
			insert(Q[i].l, Q[i].r);
		} else {
			printf("%d\n", pow0.query(Q[i].l, Q[i].r));
		}
	}
}
 
void Stupid() {
	for(int i = 1; i <= q; ++i) {
		if(Q[i].c == 1) {
			vector < int > b;
			for(int j = Q[i].l; j <= Q[i].r; ++j) {
				b.push_back(a[j]);
			}
			sort(b.begin(), b.end());
			b.resize(unique(b.begin(), b.end()) - b.begin());
			int s1 = 0, s2 = 0, s3 = 0;
			for(int x = 0; x < b.size(); ++x) {
				s1 = (s1 + b[x]) % MOD;
				s2 = (s2 + 1LL * b[x] * b[x]) % MOD;
				s3 = (s3 + 1LL * b[x] * b[x] % MOD * b[x]) % MOD;
			}
			printf("%d\n", calc(s1, s2, s3));
		} else if(Q[i].c == 2) {
			a[Q[i].l] = Q[i].r;
		} else if(Q[i].c == 3) {
			for(int j = Q[i].l; j < n; ++j) {
				a[j] = a[j + 1];
			}
			n -= 1;
		} else if(Q[i].c == 4) {
			for(int j = n + 1; j - 1 > Q[i].l; --j) {
				a[j] = a[j - 1];
			}
			a[Q[i].l + 1] = Q[i].r;
			n += 1;
		} else {
			vector < int > b;
			for(int j = Q[i].l; j <= Q[i].r; ++j) {
				b.push_back(a[j]);
			}
			sort(b.begin(), b.end());
			b.resize(unique(b.begin(), b.end()) - b.begin());
			printf("%d\n", (int)b.size());
		}
	}
}
 
int main() {
	Read();
	GetIndexes();
	BuildTree();	
	Solve();
//	Stupid();
	return 0;
}