#include <stdio.h>
#include <stdlib.h>

	int number,queue;

int int_sort( const void * a , const void * b ) {
  if( *( int * )a < *( int * )b ) {
    return -1;
  }
  else
  if( *( int * )a == *( int * )b ) {
    return 0;
  }
  return 1;
}

int query(int numberset[],int queueset[]);

int main(){
	int i=0,j=0;
	int *numberset,*queueset;
	scanf("%d %d",&number,&queue);
	numberset=(int*)malloc(sizeof(int)*number);
	queueset=(int*)malloc(sizeof(int)*queue*3);
	for(i=0;i<number;i++){
	scanf("%d",&numberset[i]);
	}
	for(j=0;j<queue*3;j+=3){
	scanf("%d %d %d",&queueset[j],&queueset[j+1],&queueset[j+2]);
	}
	query(numberset,queueset);
	free(numberset);
	free(queueset);
	/*int numberl[5]={50,40,30,20,60};
	int queuel[3]={1,0,4};
	number=5; queue=1;
	query(numberl,queuel);
	*/
	return 0;
}

//int query(int numberset[],int queueset[]){
//	int i,j;
//	int temp;
//	int min_number;
//	int queueparam,num1,num2;
//	//printf("----------\n");
//	for(i=0;i<queue;i++){
//		temp=0; min_number=0;
//		queueparam=queueset[i*3];
//		num1=queueset[1+i*3]; num2=queueset[2+i*3];
//		switch(queueparam){
//			
//		case 0: //shift
//			temp=numberset[num2];
//		for(j=num2;j>num1;j--){
//			numberset[j]=numberset[j-1];
//		}
//			numberset[num1]=temp;
//		/*for(j=0;j<number;j++){
//			printf("%d\n",numberset[j]);
//		}*/
//		//printf("----------\n");
//		break;
//		
//		case 1: //min
//		min_number=numberset[num1];
//		for(j=num1;j<num2+1;j++){
//			if(min_number>numberset[j]){min_number=numberset[j];}
//		}
//		printf("%d\n",min_number);
//		//printf("----------\n");
//		break;
//		
//		case 2: //change
//		numberset[num1]=num2;
//		/*for(j=0;j<number;j++){
//			printf("%d\n",numberset[j]);
//		}*/
//		//printf("----------\n");
//		break;
//		}
//	}
//	return 0;
//}

/*
とりあえず書くのにかかったのは1.5hくらい。scanfの受け取り方で詰まってるとかふざけるな！->自分
あとcase2の処理を思いっきり勘違いしてたのもアホっぽい。
case0の処理も若干間違ってたので、考えながら修正。
case1は昨日勉強したとこなので流石にね…、と言いたいけどその理屈ならscanf間違えちゃだめでしょ…
講座を参考にしながら、関数へのポインタ渡しと、グローバル変数宣言を利用して効率化
queuesetの方はおそらくポインタ構造体配列だとシンプルなんだろうな、と今思った
今書いてるので通すとCase#36(36/63)でTLEを受ける(Memory Overにはなっていない)
制限時間が5sで、今掛かってるのは18.1s
話になってないのは分かるんだけど、どうやって効率化すべきなのか…
今の方法だと、最悪のパターンで、200000^200000=(2*10^5)^2=4*10^10になる
(分かりやすくO記法で示すと、O(number*queue)となる)
おそらくCase#36は最悪パターンに近いパターンなのだと思われる
最悪パターンは400億回の計算をこなすことになるが、それは流石に不可能だろう
だとすると、どこかを減らさないといけないが、
queueの読み込みを変えるのは難しそうなので、
ソート方法を考えてみる
*/

/*#include <stdio.h>
#include <stdlib.h>

	int number,queue;

int query(int numberset[],int queueset[]);

int main(){
	int i=0,j=0;
	int *numberset,*queueset;
	scanf("%d %d",&number,&queue);
	numberset=(int*)malloc(sizeof(int)*number);
	queueset=(int*)malloc(sizeof(int)*queue*3);
	for(i=0;i<number;i++){
	scanf("%d",&numberset[i]);
	}
	for(j=0;j<queue*3;j+=3){
	scanf("%d %d %d",&queueset[j],&queueset[j+1],&queueset[j+2]);
	}
	query(numberset,queueset);
	free(numberset);
	free(queueset);
	/*int numberl[5]={50,40,30,20,60};
	int queuel[3]={1,0,4};
	number=5; queue=1;
	query(numberl,queuel);
	*/
//	return 0;
//}*/

int query(int numberset[],int queueset[]){
	int i,j;
	int temp;
	int min_number;
	int queueparam,num1,num2;
	int *data;
	//printf("----------\n");
	for(i=0;i<queue;i++){
		temp=0; min_number=0;
		queueparam=queueset[i*3];
		num1=queueset[1+i*3]; num2=queueset[2+i*3];
		switch(queueparam){
			
		case 0: //shift
			temp=numberset[num2];
		for(j=num2;j>num1;j--){
			numberset[j]=numberset[j-1];
		}
			numberset[num1]=temp;
		/*for(j=0;j<number;j++){
			printf("%d\n",numberset[j]);
		}*/
		//printf("----------\n");
		break;
		case 1: //min
		data=(int*)malloc(sizeof(int)*(num2-num1+1));
		for(j=num1;j<num2;j++){
		data[j-num1]=numberset[j];
		}
		qsort((void *)data,(num2-num1+1),sizeof(data[0]),int_sort);
		printf("%d\n",data[0]);
		//printf("----------\n");
		free (data);
		break;
		
		case 2: //change
		numberset[num1]=num2;
		/*for(j=0;j<number;j++){
			printf("%d\n",numberset[j]);
		}*/
		//printf("----------\n");
		break;
		}
	}
	return 0;
}