import std.stdio, std.string, std.algorithm, std.array, std.range, std.conv,
    std.typecons, std.math, std.container, std.format, std.numeric;

struct SplayNode
{
public:
    SplayNode* parent, left, right;
    long size;
    long value, minimum;

    void rotate()
    {
        SplayNode* p, pp, c;
        p = this.parent;
        pp = p.parent;
        if (p.left == &this)
        {
            c = this.right;
            this.right = p;
            p.left = c;
        }
        else
        {
            c = this.left;
            this.left = p;
            p.right = c;
        }
        p.parent = &this;
        this.parent = pp;
        if (pp && pp.left == p)
        {
            pp.left = &this;
        }
        if (pp && pp.right == p)
        {
            pp.right = &this;
        }
        if (c)
        {
            c.parent = p;
        }

        p.update();
        this.update();
    }

    auto relation()
    {
        if (this.parent is null)
        {
            return 0;
        }
        if (this.parent.left == &this)
        {
            return 1;
        }
        if (this.parent.right == &this)
        {
            return -1;
        }
        return 0;
    }

    void splay()
    {
        while (this.relation != 0)
        {
            if (this.parent.relation == 0)
            {
                this.rotate();
            }
            else if (this.relation == this.parent.relation)
            {
                this.parent.rotate();
                this.rotate();
            }
            else
            {
                this.rotate();
                this.rotate();
            }
        }
    }

    void update()
    {
        this.size = 1 + this.lsize + this.rsize;
        this.minimum = this.value;
        if (this.left)
        {
            this.minimum = min(this.minimum, this.left.minimum);
        }
        if (this.right)
        {
            this.minimum = min(this.minimum, this.right.minimum);
        }
    }

    long lsize()
    {
        return (this.left) ? this.left.size : 0;
    }

    long rsize()
    {
        return (this.right) ? this.right.size : 0;
    }
}

/// get i-th element (0 indexed)
auto get(SplayNode* root, long i)
{
    auto now = root;
    while (true)
    {
        if (i < now.lsize)
        {
            now = now.left;
        }
        else if (i == now.lsize)
        {
            now.splay();
            return now;
        }
        else if (i > now.lsize)
        {
            i = i - now.lsize - 1;
            now = now.right;
        }
    }
}

/// split tree into left and right
/// left.size == left_cnt
auto split(SplayNode* root, long left_cnt)
{
    if (left_cnt <= 0)
    {
        return tuple(cast(SplayNode*) null, root);
    }
    if (left_cnt >= root.size)
    {
        return tuple(root, cast(SplayNode*) null);
    }

    auto right = root.get(left_cnt);
    auto left = right.left;
    left.parent = null;
    right.left = null;
    right.update();
    return tuple(left, right);
}

/// merge 
auto merge(SplayNode* left, SplayNode* right)
{
    if (left is null)
    {
        return right;
    }
    if (right is null)
    {
        return left;
    }
    left = left.get(left.size - 1);
    left.right = right;
    right.parent = left;
    left.update();
    return left;
}

/// insert node as i-th element
auto insert(SplayNode* root, long i, SplayNode* node)
{
    auto tmp = root.split(i);
    return merge(merge(tmp[0], node), tmp[1]);
}

/// remove i-th element
auto remove(SplayNode* root, long i)
{
    root = root.get(i);
    auto left = root.left;
    auto right = root.right;
    if (left)
    {
        left.parent = null;
    }
    if (right)
    {
        right.parent = null;
    }
    root.left = null;
    root.right = null;
    root.update();
    return tuple(merge(left, right), root);
}

/// l and r are both inclusive
auto shift(SplayNode* root, long l, long r)
{
    auto tmp = root.remove(r);
    return tmp[0].insert(l, tmp[1]);
}

/// get minimum element in range [l, r]
auto query(SplayNode* root, long l, long r)
{
    auto tmp = root.split(r + 1);
    auto tmp2 = tmp[0].split(l);
    auto ans = tmp2[1].minimum;
    return tuple(merge(merge(tmp2[0], tmp2[1]), tmp[1]), ans);
}

void main(string[] args)
{
    long n, q;
    readf("%d %d\n", &n, &q);
    auto node = new SplayNode[](n);
    foreach (i; 0 .. n - 1)
    {
        node[i].parent = &node[i + 1];
        node[i + 1].left = &node[i];
    }

    foreach (i; 0 .. n)
    {
        readf("%d\n", &(node[i].value));
        node[i].update();
    }

    auto root = &node[$ - 1];
    long x, y, z;
    foreach (i; 0 .. q)
    {
        readf("%d %d %d\n", &x, &y, &z);
        if (x == 0)
        {
            root = root.shift(y, z);
        }
        else if (x == 1)
        {
            auto tmp = root.query(y, z);
            root = tmp[0];
            writeln(tmp[1]);
        }
        else
        {
            root = root.get(y);
            root.value = z;
            root.update();
        }
    }

}

