// Slower C++ solution:
#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;

}


// Faster C++ solution for the same problem (measured speedup: 1.1018753614960828x):
#define NDEBUG

#define _CRT_SECURE_NO_WARNINGS

#include <cassert>

#include <functional>

#include <utility>

#include <vector>



template <typename ValueMonoid, typename OperatorMonoid> class LazySegmentTree {

public:

	using value_type = ValueMonoid;

	using reference = value_type &;

	using const_reference = const value_type &;

	using operator_type = OperatorMonoid;



private:

	using operator_constref = const operator_type &;

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



public:

	using size_type = typename container_type::size_type;



private:

	using F = std::function<value_type(const_reference, const_reference)>;

	using G = std::function<operator_type(operator_constref, operator_constref)>;

	using H = std::function<value_type(const_reference, operator_constref)>;

	const F f;

	const G g;

	const H h;

	const value_type v_neutral;

	const operator_type o_neutral;

	size_type height, size_;

	container_type tree;

	static size_type getheight(const size_type &size) {

		size_type ret = 0;

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

			++ret;

		return ret;

	}

	inline value_type reflect(const size_type &index) {

		return h(tree[index].first, tree[index].second);

	}

	inline void recalc(const size_type &index) {

		tree[index].first = f(reflect(index << 1), reflect(index << 1 | 1));

	}

	inline void assign(const size_type &index, operator_constref data) {

		tree[index].second = g(tree[index].second, data);

	}

	inline void push(const size_type &index) {

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

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

		tree[index].second = o_neutral;

	}

	inline void propagate(const size_type &index) {

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

			push(index >> i);

	}

	inline void thrust(const size_type &index) {

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

		push(index);

	}

	inline void evaluate(const size_type &index) {

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

			thrust(index >> i);

	}

	inline void build(size_type index) {

		while (index >>= 1)

			recalc(index);

	}



public:

	LazySegmentTree(

		const size_type &size, const F &value_addition = std::plus<value_type>(),

		const_reference value_neutral = value_type(),

		const G &operator_multiplication = std::multiplies<operator_type>(),

		operator_constref operator_neutral = operator_type(),

		const H &mutual_multiplication =

		[](const_reference l, operator_constref r) { return l * r; })

		: f(value_addition), v_neutral(value_neutral), g(operator_multiplication),

		o_neutral(operator_neutral), h(mutual_multiplication),

		height(getheight(size)), size_((size_type)1 << height),

		tree(size_ << 1, std::make_pair(v_neutral, o_neutral)) {}

	void update(size_type begin, size_type end, operator_constref data) {

		assert(begin <= end);

		assert(begin <= size_);

		assert(end <= size_);

		begin += size_;

		end += size_;

		propagate(begin);

		propagate(end - 1);

		for (size_type left = begin, right = end; left < right;

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

			if (left & 1)

				assign(left++, data);

			if (right & 1)

				assign(right - 1, data);

		}

		build(begin);

		build(end - 1);

	}

	void update(size_type index,

		const std::function<value_type(const_reference)> &e) {

		assert(index < size_);

		index += size_;

		propagate(index);

		tree[index].first = e(reflect(index));

		tree[index].second = o_neutral;

		build(index);

	}

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

		assert(index < size_);

		update(index, [&data](const_reference d) { return data; });

	}

	value_type range(size_type begin, size_type end) {

		assert(begin <= end);

		assert(begin <= size_);

		assert(end <= size_);

		begin += size_;

		end += size_;

		evaluate(begin);

		evaluate(end - 1);

		value_type retL = v_neutral, retR = v_neutral;

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

			if (begin & 1)

				retL = f(retL, reflect(begin++));

			if (end & 1)

				retR = f(reflect(end - 1), retR);

		}

		return f(retL, retR);

	}

	size_type search(const std::function<bool(const_reference)> &b) {

		if (!b(reflect(1)))

			return size_;

		value_type acc = v_neutral;

		size_type i = 1;

		while (i < size) {

			thrust(i);

			if (!b(f(acc, reflect(i <<= 1))))

				acc = f(acc, reflect(i++));

		}

		return i - size_;

	}

	size_type size() const { return size_; }

};



#include <algorithm>

#include <limits>

template<typename T>

struct Assign {

	T a;

	bool f;

	Assign():f(1){}

	Assign(const T &x):a(x),f(0){}

	Assign operator*(const Assign &o)const {

		return o.f ? *this : o;

	}

};

template<typename T>

struct Mini {

	T a;

	Mini(const T &x=std::numeric_limits<T>::max()):a(x){}

	Mini operator+(const Mini &o)const {

		return Mini(std::min(a, o.a));

	}

	Mini operator*(const Assign<T> &o)const {

		return o.f ? *this : Mini(o.a);

	}

};





#include <cstdio>

#include <algorithm>



int main(void) {

	using M = Mini<int>;

	using A = Assign<int>;

	int n, q;

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

	LazySegmentTree<M, A> T(n, std::plus<M>(), M(2147483647));

	while (q--) {

		int c, s, t, x;

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

		if (c) {

			printf("%d\n", T.range(s, t + 1).a);

		}

		else {

			scanf("%d", &x);

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

		}

	}

	return 0;

}