#include <bits/stdc++.h>
#define REP(i,s,e) for(int i=(s); i<(e);i++)
#define rep(i,n) REP(i,0,n)
#define rep1(i,n) REP(i,1,n)
#define repe(i,n) for(auto &&i:n)
#define all(v) (v).begin(),(v).end()
#define decimal fixed<<setprecision(20)
#define fastcin() cin.tie(0);ios::sync_with_stdio(false)
using namespace std;
using LL = long long;
template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return 1; } return 0; }
template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return 1; } return 0; }
const int INF = 1e9;
const LL LLINF = 1e16;

/*
!) 0-indexed 遅延伝搬セグメント木
!) [a, b)に対する演算
!) M0, L0, merge, updateNode, propagateを書く
*/
template <typename Monoid>
struct LazySegtree {
    int n;
    vector<Monoid> dat, lazy;
    // データと遅延配列の初期化値 queryに合わせて選択する
    Monoid M0 = LLINF, L0 = LLINF;

    LazySegtree(int sz, Monoid dat_init){
        n = 1;
        while (n < sz) n *= 2;
        dat.assign(2*n-1, dat_init);
        lazy.assign(2*n-1, L0);
    }

    Monoid merge(Monoid a, Monoid b){ return min(a, b); }
    void updateNode(int k, Monoid x){ lazy[k] = x; }
    void propagate(int k, int l, int r){ dat[k] = lazy[k]; }

    void eval(int k, int l, int r) {
        if(lazy[k] == L0) return;
        propagate(k, l, r);
        if(r-l > 1) {
            updateNode(2*k+1, lazy[k]);
            updateNode(2*k+2, lazy[k]);
        }
        lazy[k] = L0;
    }

    void update(int a, int b, Monoid x, int k, int l, int r) {
        eval(k, l, r);
        if (r <= a || b <= l) return;
        if (a <= l && r <= b) {
            updateNode(k, x);
            eval(k, l, r);
        }else{
            update(a, b, x, k*2+1, l, (l+r)/2);
            update(a, b, x, k*2+2, (l+r)/2, r);
            dat[k] = merge(dat[2*k+1], dat[2*k+2]);
        }
    }

    void update(int a, int b, Monoid x) {
        update(a, b, x, 0, 0, n);
    }

    Monoid query(int a, int b, int k, int l, int r) {
        eval(k, l, r);
        if (r <= a || b <= l) return M0;
        if (a <= l && r <= b) return dat[k];
        Monoid L = query(a, b, k*2+1, l, (l+r)/2);
        Monoid R = query(a, b, k*2+2, (l+r)/2, r);
        return merge(L, R);
    }

    Monoid query(int a, int b){
        return query(a, b, 0, 0, n);
    }
};

//########## 例 ##########
// Range update - min (AOJ DSL_2_F)
// Monoid M0 = LLINF, L0 = LLINF;
// Monoid merge(Monoid a, Monoid b){ return min(a, b); }
// void updateNode(int k, Monoid x){ lazy[k] = x; }
// void propagate(int k, int l, int r){ dat[k] = lazy[k]; }
// LazySegtree<LL> seg(n+1, (1LL<<31)-1);

// // Range update - sum (AOJ DSL_2_I)
// Monoid M0 = 0, L0 = LLINF;
// Monoid merge(Monoid a, Monoid b){ return a + b; }
// void updateNode(int k, Monoid x){ lazy[k] = x; }
// void propagate(int k, int l, int r){ dat[k] = lazy[k]*(r-l); }
// LazySegtree<LL> seg(n+1, 0);

// // Range add - min (AOJ DSL_2_H)
// Monoid M0 = LLINF, L0 = 0;
// Monoid merge(Monoid a, Monoid b){ return min(a, b); }
// void updateNode(int k, Monoid x){ lazy[k] += x; }
// void propagate(int k, int l, int r){ dat[k] += lazy[k]; }
// LazySegtree<LL> seg(n+1, 0);

// // Range add - sum (AOJ DSL_2_G)
// Monoid M0 = 0, L0 = 0;
// Monoid merge(Monoid a, Monoid b){ return a + b; }
// void updateNode(int k, Monoid x){ lazy[k] += x; }
// void propagate(int k, int l, int r){ dat[k] += lazy[k]*(r-l); }
// LazySegtree<LL> seg(n+1, 0);



int main(){
    int n, q;
    cin >> n >> q;
    LazySegtree<LL> seg(n+1, (1LL<<31)-1);

    rep(i, q){
        int p;
        cin >> p;
        if(p == 0){
            LL s, t, x;
            cin >> s >> t >> x;
            seg.update(s, t+1, x);
        }else{
            LL s, t;
            cin >> s >> t;
            cout << seg.query(s, t+1) << endl;
        }
    }
}


Measured memory_bytes: 7072