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

// #undef DEBUG
// #define DEBUG
#ifdef DEBUG
#define dump(...) cerr<<"["<<__LINE__<< "] "<<#__VA_ARGS__<<" = "<<make_tuple(__VA_ARGS__)<<"\n";
template <typename T> ostream &operator<<(ostream &o, const vector<T> &v) { o << '{'; for(int i = 0; i < (int) v.size(); i++) o << v[i] << (i != (int)v.size()-1 ? ", " : ""); o << "}";  return o; }
template<int n, class...T> typename enable_if<(n>=sizeof...(T))>::type _ot(ostream &, tuple<T...> const &){}
template<int n, class...T> typename enable_if<(n< sizeof...(T))>::type _ot(ostream & os, tuple<T...> const & t){ os << (n==0?"":", ") << get<n>(t); _ot<n+1>(os, t); }
template<class...T> ostream & operator<<(ostream & os, tuple<T...> const & t){ _ot<0>(os, t); return os; }
#else
#define dump(...)
#endif

// use 2^i size by yourself
/// --- LazySegmentTree Library {{{ ///

/*
   struct Monoid {
   using T = _underlying_set_;
   static T op(const T& a, const T& b) { return _a_op_b_; }
   static constexpr T identity() { return _identity_element_; }
   };
   */

/*
   struct MM {
   using OperatorMonoid = _operator_monoid_;
   using DataMonoid = _data_monoid_;
   static T act(const OperatorMonoid::T& m, const T& a) { return _m_act_a; }
   };
   */

template<class MM>
struct LazySegTree {
  private:
    using DM = typename MM::DataMonoid;
    using OM = typename MM::OperatorMonoid;
    using DT = typename DM::T;
    using OT = typename OM::T;
    const int n, h;
    vector<DT> data;
    vector<OT> lazy; // act

    void propFrom(int i) {
      if(lazy[i] == OM::identity()) return;
      data[i] = MM::act(lazy[i], data[i]);
      if(i < n) {
        lazy[i * 2] = OM::op(lazy[i], lazy[i * 2]);
        lazy[i * 2 + 1] = OM::op(lazy[i], lazy[i * 2 + 1]);
      }
      lazy[i] = OM::identity();
    }

    void propTo(int i) {
      i += n;
      for(int j = h - 1; j >= 0; j--) propFrom(i >> j);
    }

    void propTo2(int i) {
      i += n;
      while(i >>= 1) propFrom(i * 2), propFrom(i * 2 + 1),
        data[i] = DM::op(data[i * 2], data[i * 2 + 1]);
    }

    void propToDataOne(int i) {
      data[i] = DM::op(data[i * 2], data[i * 2 + 1]);
    }

    int log(int x) {
      int h = 0;
      while(x >> h) h++;
      return h;
    }

  public:
    LazySegTree(int n, const DT& v = DM::identity(), const OT& w = OM::identity())
      : n(n), h(log(n)), data(2 * n, v), lazy(2 * n, w) {}

    template <class InputIt>
      LazySegTree(InputIt first, InputIt last)
      : n(distance(first, last)), h(log(n)), data(2 * n, DM::identity()) {
        copy(first, last, begin(data) + n);
        // たぶん深いところから更新するため
        for(int i = n; i > 0; i--) propToDataOne(i);
      }

    void act(int l, int r, const OT& m) {
      // なんか交換律を必要としないためにやっているのに,
      // 後半は完全に交換律を必用とする実装になっている感じがする
      l = max(0, l); r = min(r, n);
      propTo(l);
      propTo(r - 1);
      // propTo2(l);
      // propTo2(r - 1);
      int tl = l, tr = r;
      for(l += n, r += n; l < r; l >>= 1, r >>= 1) {
        if(l & 1) lazy[l] = OM::op(m, lazy[l]), propFrom(l), l++;
        if(r & 1) --r, lazy[r] = OM::op(m, lazy[r]), propFrom(r);
      }
      // propTo(tl);
      // propTo(tr - 1);
      propTo2(tl);
      propTo2(tr - 1);
    }

    void set(int i, const DT& v) {
      propTo(i);
      data[i] = v;
      propTo2(i);
    }

    DT get(int i) {
      propTo(i);
      return data[i + n];
    }

    DT query(int l, int r) {
      l = max(0, l); r = min(r, n);
      propTo(l);
      propTo(r - 1);
      // propTo2(l);
      // propTo2(r - 1);
      // propToData(l);
      // propToData(r - 1);
      DT tmpL = DM::identity(), tmpR = DM::identity();
      for(l += n, r += n; l < r; l >>= 1, r >>= 1) {
        if(l & 1) propFrom(l), tmpL = DM::op(tmpL, data[l++]);
        if(r & 1) propFrom(--r), tmpR = DM::op(data[r], tmpR);
      }
      return DM::op(tmpL, tmpR);
    }

    void dum() {
#ifdef dump
      dump("data");
      dump(data);
      dump("op");
      dump(lazy);
#endif
    }
};

struct RangeMin {
  using T = long long;
  static T op(const T& a, const T& b) { return min(a, b); }
  static constexpr T identity() { return numeric_limits<T>::max(); }
};

struct RangeMax {
  using T = long long;
  static T op(const T& a, const T& b) { return max(a, b); }
  static constexpr T identity() { return numeric_limits<T>::min(); }
};

struct RangeSum {
  using T = long long;
  static T op(const T& a, const T& b) { return a + b; }
  static constexpr T identity() { return 0; }
};

struct RangeSet {
  using T = long long;
  static T op(const T& a, const T&) { return a; }
  static constexpr T identity() { return -1; }
};

/// }}}--- ///

// ex)

struct RangeMinAdd {
  using OperatorMonoid = RangeSum;
  using DataMonoid = RangeMin;
  static DataMonoid::T act(const OperatorMonoid::T& m, const DataMonoid::T& a) { return m + a; }
};

struct RangeSumSet {
  using OperatorMonoid = RangeSet;
  using DataMonoid = RangeSum;
  static DataMonoid::T act(const OperatorMonoid::T& m, const DataMonoid::T&) { return m; }
};

struct RangeMinSet {
  using OperatorMonoid = RangeSet;
  using DataMonoid = RangeMin;
  static DataMonoid::T act(const OperatorMonoid::T& m, const DataMonoid::T&) { return m; }
};



int main() {
  ios::sync_with_stdio(false), cin.tie(0);
  int n, q;
  cin >> n >> q;
  int m = 1;
  while(m < n) m <<= 1;
  LazySegTree<RangeMinSet> seg(m, (1ll << 31) - 1, -1);
  while(q--) {
    int p, s, t;
    cin >> p >> s >> t;
    t++;
    if(!p) { // set
      ll x;
      cin >> x;
      seg.act(s, t, x);
      seg.dum();
    } else { // min
      cout << seg.query(s, t) << endl;
      seg.dum();
    }
  }
}



Measured memory_bytes: 6996