#include <cstdio>
#include <vector>
#include <climits>
using namespace std;
using ll = long long int;

template<typename Type>
struct SqrtDecomp {
    int SIZE;
    // node の前半には op2 を適用した値が入っているはずで、
    // 後半には op1 を適用した値が入っているはず

    // lazy は一様更新 → op1 を適用した値が入っているはず
    vector<Type> node, bucket, lazy, need_upd;
    Type (*op1) (Type x, Type y, int sz);
    Type (*op2) (Type x, Type y);
    Type I_op1, I_op2;

    SqrtDecomp( vector<Type> vec,
                Type (*g_op1) (Type x, Type y, int sz),
                Type (*g_op2) (Type x, Type y),
                Type X_op1, Type X_op2) {
        int N = vec.size();
        SIZE = 1; while(SIZE * SIZE < N) SIZE++;
        op1 = g_op1, op2 = g_op2;
        I_op1 = X_op1, I_op2 = X_op2;

        node.resize(SIZE*SIZE, I_op2);
        bucket.resize(SIZE, I_op2);
        lazy.resize(SIZE, I_op1);
        need_upd.resize(SIZE, 0);

        for(int i=0; i<SIZE; i++) {
            Type rep_val = I_op2;
            for(int j=0; j<SIZE && i*SIZE+j<N; j++) {
                int idx = (i*SIZE + j);
                node[idx] = vec[idx];
                rep_val = op2(rep_val, node[idx]);
            }
            bucket[i] = rep_val;
        }
    }

    void lazy_eval(int k) {
        int l = SIZE*k, r = SIZE*(k+1);
        // printf("lazy_eval: %d, lazy[k] = %d, l =  %d, r = %d\n", k, lazy[k], l, r);
        if(need_upd[k]) {
            Type seg_val = I_op2;

            // 該当する下のノードをまず更新
            for(int i=l; i<r; i++) {
                node[i] = op1(node[i], lazy[k], 1);
                seg_val = op2(seg_val, node[i]);
            }

            // 上のノードを更新
            bucket[k] = seg_val;
        }
        lazy[k] = I_op1;
        need_upd[k] = false;
    }

    void update(int l, int r, Type val) {
        int lidx = l / SIZE, ridx = r / SIZE;
        // 左端の更新
        lazy_eval(lidx);
        bucket[lidx] = I_op2;
        for(int k=SIZE*lidx; k<SIZE*(lidx+1); k++) {
            if(l <= k && k < r) node[k] = op1(node[k], val, 1);
            bucket[lidx] = op2(bucket[lidx], node[k]);
        }

        if(lidx != ridx) {
            // 真ん中 (lazy を更新)
            for(int k=lidx+1; k<ridx; k++) {
                lazy[k] = op1(lazy[k], val, 1);
                need_upd[k] = true;
            }

            if(ridx != SIZE) {
                lazy_eval(ridx);
                bucket[ridx] = I_op2;

                // 右端の更新
                for(int k=SIZE*ridx; k<SIZE*(ridx+1); k++) {
                    if(k < r) node[k] = op1(node[k], val, 1);
                    bucket[ridx] = op2(bucket[ridx], node[k]);
                }
            }
        }
    }

    Type query(int l, int r) {
        int lidx = l / SIZE, ridx = r / SIZE;
        // printf("lidx = %d, ridx = %d\n", lidx, ridx);
        Type ret = I_op2;
        
        // 左端の取得
        for(int k=l; k<min(r, SIZE*(lidx+1)); k++) {
            lazy_eval(lidx);
            ret = op2(ret, node[k]);
        }

        if(lidx != ridx) {
            for(int k=lidx+1; k<ridx; k++) {
                // 全体の値と lazy の値を考慮して値を計算
                Type val = (need_upd[k] ? op1(bucket[k], lazy[k], SIZE) : bucket[k]);
                ret = op2(ret, val);
            }

            for(int k=SIZE*ridx; k<r; k++) {
                lazy_eval(ridx);
                ret = op2(ret, node[k]);
            }
        }

        return ret;
    }
};

// RMQ and RAQ
const ll INF = 1LL << 60;
ll upd_f(ll a, ll b, int sz) {
    return a + b;
}
ll qry_f(ll a, ll b) {
    return min(a, b);
}

int main() {
    int N, Q; scanf("%d%d", &N, &Q);
    SqrtDecomp<ll> sqr(vector<ll>(N, 0), upd_f, qry_f, 0, INF);

    while(Q--) {
        int query; scanf("%d", &query);
        if(query == 0) {
            int l, r, x; scanf("%d%d%d", &l, &r, &x);
            sqr.update(l, r+1, x);
        }
        if(query == 1) {
            int l, r; scanf("%d%d", &l, &r);
            printf("%lld\n", sqr.query(l, r+1));
        }
    }
    return 0;
}


Measured memory_bytes: 3936