#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <cmath>

using namespace std;


struct Node {
	Node *left;
	Node *right;
	int l;
	int r;
	int minimum;
	int assigment;

	Node (int l, int r, int minimum) {
		this->left = nullptr;
		this->right = nullptr;
		this->l = l;
		this->r = r;
		this->minimum = minimum;
		this->assigment = -1;
	}
};

int INF = exp2(31) - 1;

// Segment Tree - RMQ + RUQ
class SegmentTree {
	private:
		Node *root;

		int build (vector<int> &array, Node *node = nullptr) {
			if (node->l == node->r) {
				node->left = nullptr;
				node->right = nullptr;
				return node->minimum = array[node->l];
			}

			int size = node->r - node->l + 1;
			int middle = (size - 1) / 2;
			Node *left = new Node(node->l, node->l + middle, 0);
			Node *right = new Node(node->l + middle + 1, node->r, 0);
			node->left = left;
			node->right = right;

			return node->minimum = min(build(array, left), build(array, right));
		}

		void push (Node *node) {
			if (node->assigment == -1)
				return;

			if (node->left != nullptr)
				node->left->assigment = node->assigment;

			if (node->right != nullptr)
				node->right->assigment = node->assigment;

			node->minimum = node->assigment;
			node->assigment = -1;
		}

	public:
		SegmentTree (vector<int> &array) {
			root = new Node(0, array.size() - 1, 0);
			root->minimum = build(array, root);
		}

		int RMQ (int l, int r, Node *node = nullptr) {
			if (node == nullptr)
				node = root;

			push(node);

			// Текущий отрезок вложен в отрезок запроса.
			if (l <= node->l && node->r <= r)
				return node->minimum;

			// Текущий отрезок не пересекается с отрезком запроса.
			else if (node->r < l || r < node->l)
				return INF;
 
			// Иначе отрезки пересекаются, либо отрезок запроса вложен в текущий отрезок.
			return min(RMQ(l, r, node->left), RMQ(l, r, node->right));
		}

		void RUQ (int l , int r, int delta, Node *node = nullptr) {
			if (node == nullptr)
				node = root;

			push(node);

			// Текущий отрезок вложен в отрезок запроса.
			if (l <= node->l && node->r <= r) {
				node->assigment = delta;
				push(node);
				return;
			}

			// Текущий отрезок не пересекается с отрезком запроса.
			else if (node->r < l || r < node->l)
				return;

			// Иначе отрезки пересекаются, либо отрезок запроса вложен в текущий отрезок.
			RUQ(l, r, delta, node->left);
			RUQ(l, r, delta, node->right);
			node->minimum = min(node->left->minimum, node->right->minimum);
		}

		void print () const {
			queue<Node*> nodeQueue;
			nodeQueue.push(root);

			while (!nodeQueue.empty()) {
				Node *current = nodeQueue.front();
				nodeQueue.pop();

				cout << current->l << "-" << current->r << ", minimum = " << current->minimum << "(" << current->assigment << "): ";
				cout << "left: ";

				if (current->left != nullptr) {
					cout << current->left->l << "-" << current->left->r;
					nodeQueue.push(current->left);
				}

				else
					cout << "nullptr";

				cout << "; right: ";

				if (current->right != nullptr) {
					cout << current->right->l << "-" << current->right->r;
					nodeQueue.push(current->right);
				}

				else
					cout << "nullptr";

				cout << endl;
			}
		}
};


int main () {
	int n, q;
	cin >> n >> q;

	vector<int> array(n, INF);
	SegmentTree segmentTree(array);

	for (int i = 0; i < q; i ++) {
		int type;
		cin >> type;

		int l, r;
		cin >> l >> r;

		if (!type) {
			int assigment;
			cin >> assigment;
			segmentTree.RUQ(l, r, assigment);
		}

		else
			cout << segmentTree.RMQ(l, r) << endl;
	}
}



Measured memory_bytes: 12628