#include <bits/stdc++.h>

//{ START
using namespace std;
#define int int64_t
#define rep(i, a, n) for (int i = (a); i < (n); ++i)
#define reps(i, a, n) for (int i = (a); i > (n); --i)
#define arep(i, x) for (auto &&i : (x))
#define irep(i, x) for (auto i = (x).begin(); i != (x).end(); ++i)
#define rirep(i, x) for (auto i = (x).rbegin(); i != (x).rend(); ++i)
//降順はgreater<T>()
#define all(x) (x).begin(), (x).end()
#define rv(s) reverse((s).begin(), (s).end())
// gcd lcmはそのままok
#define gcd(a, b) __gcd(a, b)
#define bits(n) (1LL << (n))
#define pcnt(x) __builtin_popcountll(x)
//配列内等要素削除
#define unique(x) (x).erase(unique((x).begin(), (x).end()), (x).end())
#define Fixed(n) fixed << setprecision(n)
//総和
#define sowa(n) ((n) * ((n) + 1)) / 2
#define pb(x, a) (x).push_back(a)
#define fb(x, a) (x).push_front(a)
#define eb(x, a) (x).emplace_back(a)
#define cauto const auto &
using P = pair<int, int>;
using Graph = vector<vector<P>>;
template <class T>  //昇順
using min_heap = priority_queue<T, vector<T>, greater<T>>;
template <class T>  //降順
using max_heap = priority_queue<T>;
template <class A, class B>
using umap = unordered_map<A, B>;
template <class A>
using uset = unordered_set<A>;
template <typename A, size_t N, typename T>
void Fill(A (&array)[N], const T &val) {  //多次元初期化
  std::fill((T *)array, (T *)(array + N), val);
}
template <class A, class B>
bool chmax(A &a, const B &b) {  //最大値更新 返り値はbool
  if (a < b) {
    a = b;
    return 1;
  }
  return 0;
}
template <class A, class B>
bool chmin(A &a, const B &b) {  //最小値更新 返り値はbool
  if (b < a) {
    a = b;
    return 1;
  }
  return 0;
}
int dx[4] = {1, 0, -1, 0};
int dy[4] = {0, 1, 0, -1};
constexpr int INF = 0x3f3f3f3f;
constexpr int LINF = 0x3f3f3f3f3f3f3f3fLL;
constexpr int mod1 = 1e9 + 7;
constexpr int mod2 = 998244353;
//} END

/*
RUQ：[0,n-1] について、区間ごとの最小値を管理する構造体
set(i,x),build():
i番目の要素をxにセット。まとめてセグ木を構築する。O(n)
update(a,b,x): 区間[a,b) の要素を x に更新。O(log(n))
query(a,b): [a,b) での最小の要素を取得。O(log(n))
find_rightest(a,b,x): [a,b) でx以下の要素を持つ最右位置を求める。O(log(n))
*/
template <typename T>
struct RUQ {
  const T INF = numeric_limits<T>::max();
  int n;
  vector<T> dat, lazy;
  RUQ(int n_) : n(), dat(n_ * 4, INF), lazy(n_ * 4, INF) {
    int x = 1;
    while (n_ > x) x *= 2;
    n = x;
  }

  void set(int i,T x){ dat[i+n-1] = x;}
  void build(){
    for(int k=n-2;k>=0;--k) dat[k] = min(dat[2*k+1],dat[2*k+2]);
  }

  /* lazy eval */
  void eval(int k) {
    if (lazy[k] == INF) return;  // 更新するものが無ければ終了
    if (k < n - 1) {             // 葉でなければ子に伝搬
      lazy[k * 2 + 1] = lazy[k];
      lazy[k * 2 + 2] = lazy[k];
    }
    // 自身を更新
    dat[k] = lazy[k];
    lazy[k] = INF;
  }

  void update(int a, int b, T x, int k, int l, int r) {
    eval(k);
    if (a <= l && r <= b) {  // 完全に内側の時
      lazy[k] = x;
      eval(k);
    } else if (a < r && l < b) {                   // 一部区間が被る時
      update(a, b, x, k * 2 + 1, l, (l + r) / 2);  // 左の子
      update(a, b, x, k * 2 + 2, (l + r) / 2, r);  // 右の子
      dat[k] = min(dat[k * 2 + 1], dat[k * 2 + 2]);
    }
  }
  void update(int a, int b, T x) { update(a, b, x, 0, 0, n); }

  T query_sub(int a, int b, int k, int l, int r) {
    eval(k);
    if (r <= a || b <= l) {  // 完全に外側の時
      return INF;
    } else if (a <= l && r <= b) {  // 完全に内側の時
      return dat[k];
    } else {  // 一部区間が被る時
      T vl = query_sub(a, b, k * 2 + 1, l, (l + r) / 2);
      T vr = query_sub(a, b, k * 2 + 2, (l + r) / 2, r);
      return min(vl, vr);
    }
  }
  T query(int a, int b) { return query_sub(a, b, 0, 0, n); }

  int find_rightest(int a, int b, T x) {
    return find_rightest_sub(a, b, x, 0, 0, n);
  }
  int find_rightest_sub(int a, int b, T x, int k, int l, int r) {
    eval(k);
    if (dat[k] > x || r <= a ||
        b <=
            l) {  // 自分の値がxより大きい or [a,b)が[l,r)の範囲外ならreturn a-1
      return a - 1;
    } else if (k >= n - 1) {  // 自分が葉ならその位置をreturn
      return (k - (n - 1));
    } else {
      int vr = find_rightest_sub(a, b, x, 2 * k + 2, (l + r) / 2, r);
      if (vr != a - 1) {  // 右の部分木を見て a-1 以外ならreturn
        return vr;
      } else {  // 左の部分木を見て値をreturn
        return find_rightest_sub(a, b, x, 2 * k + 1, l, (l + r) / 2);
      }
    }
  }

  /* debug */
  inline T operator[](int a) { return query(a, a + 1); }
  void print() {
    for (int i = 0; i < 2 * n - 1; ++i) {
      cout << (*this)[i];
      if (i != n) cout << ",";
    }
    cout << endl;
  }
};

signed main() {
  cin.tie(nullptr);
  ios_base::sync_with_stdio(false);

  int n,q;
  cin>>n>>q;

  RUQ<int> ruq(n);
  vector<int> ans;

  rep(i,0,q){
    int p,s,t,x;
    cin>>p;
    if(p == 0){
      cin>>s>>t>>x;
      ruq.update(s,t+1,x);
    }else{
      cin>>s>>t;
      ans.emplace_back(ruq.query(s,t+1));
    }
  }

  arep(i,ans){
    if(i == numeric_limits<int>::max()){
      cout<<2147483647<<'\n';
    }else{
      cout<<i<<'\n';
    }
  }

  return 0;
}


Measured memory_bytes: 10048