#include <bits/stdc++.h>
using namespace std;

using i64 = int64_t;

#define rep(i, x, y) for (i64 i = i64(x), i##_max_for_repmacro = i64(y); i < i##_max_for_repmacro; ++i)
#define debug(x) #x << "=" << (x)

#ifdef DEBUG
#define _GLIBCXX_DEBUG
#define print(x) std::cerr << debug(x) << " (L:" << __LINE__ << ")" << std::endl
#else
#define print(dat)
#endif

template <i64 p>
class fp {
    public:
    i64 x;
    fp() : x(0) {}
    fp(i64 x_) : x((x_ % p + p) % p) {}
    fp operator+() const { return fp(x); }
    fp operator-() const { return fp(-x); }
    fp& operator+=(const fp& y) {
        x += y.x;
        if (x >= p) x -= p;
        return *this;
    }
    fp& operator-=(const fp& y) { return *this += -y; }
    fp& operator*=(const fp& y) {
        x = x * y.x % p;
        return *this;
    }
    fp& operator/=(const fp& y) { return *this *= fp(inverse(y.x)); }
    fp operator+(const fp& y) const { return fp(x) += y; }
    fp operator-(const fp& y) const { return fp(x) -= y; }
    fp operator*(const fp& y) const { return fp(x) *= y; }
    fp operator/(const fp& y) const { return fp(x) /= y; }
    bool operator==(const fp& y) const { return x == y.x; }
    bool operator!=(const fp& y) const { return !(*this == y); }
    i64 extgcd(i64 a, i64 b, i64& x, i64& y) {
        i64 d = a;
        if (b != 0) {
            d = extgcd(b, a % b, y, x);
            y -= (a / b) * x;
        } else {
            x = 1;
            y = 0;
        }
        return d;
    }
    i64 inverse(i64 a) {
        i64 x, y;
        extgcd(a, p, x, y);
        return (x % p + p) % p;
    }
};

template <i64 p>
i64 abs(const fp<p>& x) { return x.x; }

template <i64 p>
istream& operator>>(istream& is, fp<p>& x) {
    is >> x.x;
    return is;
}

template <i64 p>
ostream& operator<<(ostream& os, const fp<p>& x) {
    os << x.x;
    return os;
}

template <typename T, typename U>
ostream& operator<<(ostream& os, const pair<T, U>& p) {
    os << "(" << p.first << ", " << p.second << ")";
    return os;
}

template <typename T>
ostream& operator<<(ostream& os, const vector<T>& vec) {
    os << "[";
    for (const auto& v : vec) {
        os << v << ",";
    }
    os << "]";
    return os;
}

template <typename T>
bool chmin(T& a, const T& b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}

template <typename T>
bool chmax(T& a, const T& b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}

template <typename A, typename T, size_t size>
void fill(A (&ary)[size], const T& val) {
    fill((T*)ary, (T*)(ary + size), val);
}

constexpr int inf = 1.01e9;
constexpr i64 inf64 = 4.01e18;
constexpr long double eps = 1e-9;

// double(64bit浮動小数)のn分探索のループ回数の上限(2分探索なら50でも十分かもしれない). long double(80ビットの x87 浮動小数点型?)だと, 2分探索であってもこれだと足りないケースがある気がするので, もうちょっと余裕を持たせた方が良さそう.
constexpr i64 max_loop = 100;

template <typename X, typename M, typename FX,typename FM,typename FA>
class lazy_propagation_segtree {
    FX fx;
    FM fm;
    FA fa;
    X id_x;
    M id_m;
    i64 n, size_;
    vector<X> dat;
    vector<M> lazy;

    void init(int size, X initial_value) {
        size_ = size;
        n = 1;
        while (n < size) n *= 2;

        dat.assign(2 * n - 1, id_x);
        lazy.assign(2 * n - 1, id_m);

        rep(i, n - 1, n - 1 + size_) dat[i] = initial_value;
        for (i64 i = n - 2; i >= 0; --i) dat[i] = fx(dat[i * 2 + 1], dat[i * 2 + 2]);
    }

    void propagate(i64 k){
        if(lazy[k] == id_m) return;
        if(k<n-1){
            lazy[k * 2 + 1]=fm(lazy[k * 2 + 1], lazy[k]);
            lazy[k * 2 + 2]=fm(lazy[k * 2 + 2], lazy[k]);
        }
        dat[k]=fa(dat[k], lazy[k]);
        lazy[k]=id_m;
    }

    void update(i64 a, i64 b, M val, i64 k, i64 l, i64 r){
        if(b<=l or r<=a){
            propagate(k);
            return;
        }
        if(a<=l and r<=b){
            lazy[k]=fm(lazy[k], val);
            propagate(k);
            return;
        }
        i64 m = (l + r) / 2;
        propagate(k);
        update(a, b, val, k * 2 + 1, l, m);
        update(a, b, val, k * 2 + 2, m, r);
        dat[k]=fx(dat[k * 2 + 1], dat[k * 2 + 2]);
    }

    X query(i64 a,i64 b,i64 k,i64 l,i64 r){
        if(b<=l or r<=a) return id_x;
        propagate(k);
        if(a<=l and r<=b) return dat[k];
        i64 m=(l+r)/2;
        return fx(query(a,b,k*2+1,l,m),query(a,b,k*2+2,m,r));
    }

public:
    lazy_propagation_segtree() = default;

    /*
     * fx: X × X → X
     * fm: M × M → M
     * fa: X × M → X
     */
    lazy_propagation_segtree(FX fx, X id_x, FM fm, M id_m, FA fa, i64 size, X initial_value) : fx(fx),fm(fm),fa(fa),id_x(id_x),id_m(id_m) { init(size, initial_value); }

    int size() const { return size_; }

    void update(i64 a, i64 b, M val){ update(a, b, val, 0, 0,n); }

    void update(i64 i, M val) { update(i, i + 1, val); }

    X query(i64 a, i64 b) { return query(a,b,0,0,n); }

    X at(int index) { return query(index,index+1); }
};

void solve() {
    //constexpr i64 mod = 1'000'000'007;
    i64 n,q;
    cin >> n >> q;

    auto fx=[](i64 x1,i64 x2){ return min(x1,x2); };
    auto fm=[](i64 m1,i64 m2){ return m2; };
    auto fa=[](i64 x,i64 m){ return m; };
    lazy_propagation_segtree<i64,i64,decltype(fx),decltype(fm),decltype(fa)> seg(fx,numeric_limits<int32_t>::max(),fm,numeric_limits<int32_t>::max(),fa,n,numeric_limits<int32_t>::max());
    rep(i,0,q){
        i64 ty;
        cin >> ty;
        if(ty==0){
            i64 s,t,x;
            cin >> s >> t >> x;
            seg.update(s,t+1,x);
        }else{
            i64 s,t;
            cin >> s >> t;
            cout << seg.query(s,t+1) << endl;
        }
    }

}

int main() {
    std::cin.tie(0);
    std::ios::sync_with_stdio(false);
    cout.setf(ios::fixed);
    cout.precision(16);
    solve();
    return 0;
}



Measured memory_bytes: 7056