// Original C++ solution (slower):
#include <iostream>

#include <algorithm>

#include <array>

#include <cstdint>

#include <climits>

#include <functional>

#include <map>

#include <math.h>

#include <queue>

#include <set>

#include <stack>

#include <stdlib.h>

#include <string>

#include <time.h>

#include <type_traits>

#include <utility>

#include <vector>



using int32 = std::int_fast32_t;

using int64 = std::int_fast64_t;

using uint32 = std::uint_fast32_t;

using uint64 = std::uint_fast64_t;

using intl32 = std::int_least32_t;

using intl64 = std::int_least64_t;

using uintl32 = std::uint_least32_t;

using uintl64 = std::uint_least64_t;





struct MIN {

	int32 e;

	MIN(int32 x = 2147483647) { e = x; }

	void reverse(void) {}

	MIN operator+(const MIN &other) {

		if (e < other.e) return *this;

		return other;

	};

	MIN operator*(const MIN &other) {

		return other;

	}

};



template<typename Monoid, typename Operand>

class lazy_segment_tree {

	struct node {

		Monoid element;

		Operand lazy;

		bool h;

	};

	std::vector<node> tree;

	std::uint_fast32_t size, height;

	void assign(std::uint_fast32_t index,const Operand &data) {

		if (tree[index].h) tree[index].lazy = tree[index].lazy*data;

		else { tree[index].lazy = data;tree[index].h = 1; }

	}

	Monoid reflect(std::uint_fast32_t index) {

		if (tree[index].h) return tree[index].element*tree[index].lazy;

		return tree[index].element;

	}

	void recalc(std::uint_fast32_t index) {

		tree[index].element = reflect(index << 1) + reflect(index << 1 | 1);

	}

	void push(std::uint_fast32_t index) {

		if (!tree[index].h) return;

		assign(index << 1, tree[index].lazy);

		assign(index << 1 | 1, tree[index].lazy);

		tree[index].element = tree[index].element*tree[index].lazy;

		tree[index].h = 0;

	}

	void propagate(std::uint_fast32_t index) {

		std::uint_fast32_t i = height;

		do { push(index >> i); } while (--i);

	}

	void build(std::uint_fast32_t index) {

		while (index >>= 1)recalc(index);

	}

public:

	void update(std::uint_fast32_t begin, std::uint_fast32_t end, const Operand &data) {

		begin += size;

		end += size - 1;

		propagate(begin);

		propagate(end);

		for (std::uint_fast32_t left = begin, right = end + 1;left < right;left >>= 1, right >>= 1) {

			if (left & 1) assign(left++, data);

			if (right & 1) assign(--right, data);

		}

		build(begin);

		build(end);

	}

	Monoid range(std::uint_fast32_t begin, std::uint_fast32_t end) {

		begin += size;end += size;

		propagate(begin);

		propagate(end - 1);

		Monoid retL, retR;

		for (;begin < end;begin >>= 1, end >>= 1) {

			if (begin & 1) retL = retL + reflect(begin++);

			if (end & 1) retR = reflect(--end) + retR;

		}

		return retL + retR;

	}

	//trueを返す最小のインデックス

	std::uint_fast32_t binary_search(std::function<bool(Monoid)> bound) {

		if (!bound(reflect(1))) return size;

		Monoid ret;

		std::uint_fast32_t i = 1;

		while (i < size) {

			push(i);

			if (!bound(ret + reflect(i <<= 1))) ret = ret + reflect(i++);

		}

		return i - size;

	}

	lazy_segment_tree(std::uint_fast32_t length) :height(static_cast<std::uint_fast32_t>(0)) {

		--length;

		length |= length >> 1;

		length |= length >> 2;

		length |= length >> 4;

		length |= length >> 8;

		length |= length >> 16;

		if (!length) length = 1;

		height = __builtin_popcount(length);

		size = length + 1;

		tree = std::vector<node>(size << 1);

		initialize();

	}

	void update(std::uint_fast32_t index, std::function<Monoid(Monoid)> f) {

		index += size;

		propagate(index);

		if (tree[index].h) {

			tree[index].element = tree[index].element*tree[index].lazy;

			tree[index].h = 0;

		}

		tree[index].element = f(tree[index].element);

		build(index);

	}

	void initialize(void) {

		std::uint_fast32_t const s = size;

		std::uint_fast32_t i = s << 1;

		while (--i >= s) { tree[i].element = Monoid();tree[i].h = 0; }

		do { recalc(i);tree[i].h = 0; } while (--i);

	}

};



int main(void) {

	std::ios::sync_with_stdio(false);

	std::cin.tie(0);

	uint32 n, q;

	std::cin >> n >> q;

	lazy_segment_tree<MIN, MIN> T(n);

	uint32 c, s, t;

	MIN x;

	do {

		std::cin >> c >> s >> t;

		if (c) {

			std::cout << T.range(s, t + 1).e << "\n";

		}

		else {

			std::cin >> x.e;

			T.update(s, t + 1, x);

		}

	} while (--q);

	return 0;

}


// Optimized solution to the same problem (faster):
#define NDEBUG

#include <cassert>

#include <stdexcept>

#include <utility>



template <class ValueMonoid, class OperatorMonoid, class Modifier,

	template <class> class Container>

class lazy_segment_tree {

public:

	using value_structure = ValueMonoid;

	using value_type = typename value_structure::value_type;

	using operator_structure = OperatorMonoid;

	using operator_type = typename operator_structure::value_type;

	using modifier_type = Modifier;

	using container_type = Container<::std::pair<value_type, operator_type>>;

	using size_type = typename container_type::size_type;



private:

	using e_cref = typename container_type::const_reference;

	using e_ref = typename container_type::reference;

	size_type size_, height;

	container_type c;

	static size_type getheight(const size_type size) noexcept {

		size_type ret = 0;

		while (static_cast<size_type>(1) << ret < size)

			++ret;

		return ret;

	}

	static value_type reflect(e_cref element) {

		return modifier_type::operation(element.first, element.second);

	}

	void recalc(const size_type index) {

		c[index].first = value_structure::operation(reflect(c[index << 1]) ,

			reflect(c[index << 1 | 1]));

	}

	static void assign(operator_type &element, const operator_type &data) {

		element = operator_structure::operation(element, data);

	}

	void push(const size_type index) {

		assign(c[index << 1].second, c[index].second);

		assign(c[index << 1 | 1].second, c[index].second);

		c[index].second = operator_structure::identity();

	}

	void propagate(const size_type index) {

		for (size_type i = height; i; --i)

			push(index >> i);

	}

	void thrust(const size_type index) {

		c[index].first = reflect(c[index]);

		push(index);

	}

	void evaluate(const size_type index) {

		for (size_type i = height; i; --i)

			thrust(index >> i);

	}

	void build(size_type index) {

		while (index >>= 1)

			recalc(index);

	}

	size_type base_size() const { return static_cast<size_type>(1) << height; }



public:

	lazy_segment_tree() : size_(0), height(0), c() {}

	explicit lazy_segment_tree(const size_type size)

		: size_(size), height(getheight(size_)),

		c(static_cast<size_type>(1) << (height + 1),

		{ value_structure::identity(), operator_structure::identity() }) {}



	bool empty() const { return !size_; }

	size_type size() const { return size_; }



	const value_type &operator[](size_type index) {

		assert(index < size());

		index += base_size();

		evaluate(index);

		c[index].first = reflect(c[index]);

		c[index].second = operator_structure::identity();

		return c[index].first;

	}

	const value_type &at(size_type index) {

		if (index < size()) {

			throw ::std::out_of_range("index out of range");

		}

		else {

			index += base_size();

			evaluate(index);

			c[index].first = reflect(c[index]);

			c[index].second = operator_structure::identity();

			return c[index].first;

		}

	}

	value_type fold(size_type first, size_type last) {

		assert(first <= last);

		assert(first <= size());

		assert(last <= size());

		first += base_size();

		last += base_size();

		evaluate(first);

		evaluate(last - 1);

		value_type retL = value_structure::identity(),

			retR = value_structure::identity();

		for (; first < last; first >>= 1, last >>= 1) {

			if (first & 1)

				retL = value_structure::operation(retL, reflect(c[first++]));

			if (last & 1)

				retR = value_structure::operation(reflect(c[last - 1]), retR);

		}

		return value_structure::operation(retL, retR);

	}

	template <class F> size_type search(const F &f) {

		if (f(value_structure::identity()))

			return static_cast<size_type>(0);

		if (!f(reflect(c[1])))

			return size() + 1;

		value_type acc = value_structure::identity();

		size_type i = 1;

		while (i < base_size()) {

			thrust(i);

			if (!f(value_structure::operation(acc, reflect(c[i <<= 1]))))

				acc = value_structure::operation(acc, reflect(c[i++]));

		}

		return i - base_size() + 1;

	}



	void update(const size_type index, const value_type &data) {

		assert(index < size());

		index += base_size();

		propagate(index);

		c[index].first = data;

		c[index].second = operator_structure::identity();

		build(index);

	}

	template <class F> void update(size_type index, const F &f) {

		assert(index < size());

		index += base_size();

		propagate(index);

		c[index].first = f(reflect(index));

		c[index].second = operator_structure::identity();

		build(index);

	}

	void update(size_type first, size_type last, const operator_type data) {

		assert(first <= last);

		assert(first <= size());

		assert(last <= size());

		first += base_size();

		last += base_size();

		propagate(first);

		propagate(last - 1);

		for (size_type left = first, right = last; left < right;

			left >>= 1, right >>= 1) {

			if (left & 1)

				assign(c[left++].second, data);

			if (right & 1)

				assign(c[right - 1].second, data);

		}

		build(first);

		build(last - 1);

	}

};



#include <algorithm>

#include <limits>

template <class T> class min_monoid {

public:

	using value_type = T;

	static value_type operation(const value_type &x, const value_type &y) {

		return ::std::min(x, y);

	}

	static value_type identity() {

		return ::std::numeric_limits<value_type>::max();

	}

	static value_type reverse(const value_type &x) { return x; }

};



#include <utility>

template <class T> class assign_monoid {

public:

	using value_type = ::std::pair<T, bool>;

	static value_type operation(const value_type &x, const value_type &y) {

		return y.second ? y : x;

	}

	static value_type identity() { return value_type(T(), 0); }

	static value_type reverse(const value_type &x) { return x; }

};



template <class T> class minmax_assign {

public:

	static T operation(const T &x, const ::std::pair<T, bool> &y) {

		return y.second ? y.first : x;

	}

};



#include <cstdio>

#include <vector>



template<class T>

using vec = ::std::vector<T>;



int main() {

	using uint = unsigned int;

	uint n, q;

	scanf("%u%u", &n, &q);



	lazy_segment_tree<min_monoid<uint>,

					  assign_monoid<uint>,

					  minmax_assign<uint>,

					  vec> T(n);



	T.update(0, T.size(), { 2147483647 ,1 });



	while (q--) {

		uint c, s, t, x;

		scanf("%u%u%u", &c, &s, &t);



		if (c) {

			printf("%u\n", T.fold(s, t + 1));

		}

		else {

			scanf("%u", &x);

			T.update(s, t + 1, { x,1 });

		}

	}



	return 0;

}


// Fastest known solution for this problem: