#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <math.h>
#include <assert.h>
#include <vector>
#include <queue>
#include <string>
#include <map>
#include <set>

using namespace std;
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
static const double EPS = 1e-9;
static const double PI = acos(-1.0);

#define REP(i, n) for (int i = 0; i < (int)(n); i++)
#define FOR(i, s, n) for (int i = (s); i < (int)(n); i++)
#define FOREQ(i, s, n) for (int i = (s); i <= (int)(n); i++)
#define FORIT(it, c) for (__typeof((c).begin())it = (c).begin(); it != (c).end(); it++)
#define MEMSET(v, h) memset((v), h, sizeof(v))

struct Node {
  int lower;
  int upper;
  Node() {;}
  Node(int lower, int upper)
    : lower(lower), upper(upper) {;}
};

inline Node Merge(Node left, Node right) {
  return Node(min(left.lower, right.lower), max(left.upper, right.upper));
}

struct SegmentTree {
  static const int MAX_DEPTH = 18;
  static const int SIZE = 1 << (MAX_DEPTH + 1);  // 2^18 = 262144

  bool updated[SIZE];
  Node data[SIZE];
  SegmentTree() { memset(updated, false, sizeof(updated)); }
  void change(int index, Node value) {
    int target = (1 << MAX_DEPTH) + index;
    data[target] = value;
    for (int i = 1; i <= MAX_DEPTH; i++) {
      target >>= 1;
      data[target] = Merge(data[target * 2], data[target * 2 + 1]);
    }
  }
  void set(Node v, int left, int right) {
    assert(left <= right);
    return in_set(v, 0, 1, left, right);
  }
  Node get(int left, int right) {
    assert(left <= right);
    return in_get(0, 1, left, right);
  }
private:
  void Divide(int node) {
    if (!updated[node] || node >= (1 << MAX_DEPTH)) { return; }
    updated[node] = false;
    updated[node * 2] = true;
    updated[node * 2 + 1] = true;
    data[node * 2] = data[node];
    data[node * 2 + 1] = data[node];
  }
  void in_set(Node v, int depth, int node, int left, int right) {
    int width = 1 << (MAX_DEPTH - depth);
    int index = node - (1 << depth);
    int node_left = index * width;
    int node_mid = node_left + (width >> 1);

    Divide(node);
    if (right - left + 1 == width && left == node_left) {
      updated[node] = true;
      data[node] = v;
    } else {
      if (right < node_mid) {
        in_set(v, depth + 1, node * 2, left, right);
      } else if (left >= node_mid) {
        in_set(v, depth + 1, node * 2 + 1, left, right);
      } else {
        in_set(v, depth + 1, node * 2, left, node_mid - 1);
        in_set(v, depth + 1, node * 2 + 1, node_mid, right);
      }
      data[node] = Merge(data[node * 2], data[node * 2 + 1]);
    }
  }
  Node in_get(int depth, int node, int left, int right) {
    int width = 1 << (MAX_DEPTH - depth);
    int index = node - (1 << depth);
    int node_left = index * width;
    int node_mid = node_left + (width >> 1);

    Divide(node);
    if (right - left + 1 == width && left == node_left) {
      return data[node];
    } else if (right < node_mid) {
      return in_get(depth + 1, node * 2, left, right);
    } else if (left >= node_mid) {
      return in_get(depth + 1, node * 2 + 1, left, right);
    }
    return Merge(in_get(depth + 1, node * 2, left, node_mid - 1), in_get(depth + 1, node * 2 + 1, node_mid, right));
  }
};

enum {
  QUERY_IN,
  QUERY_OUT,
  ASSIGN_IN,
  ASSIGN_OUT
};
struct Event {
  int index;
  int type;
  int x;
  int v;
  Event() {;}
  Event(int index, int type, int x, int v) :
    index(index), type(type), x(x), v(v) {;}
  bool operator<(const Event &rhs) const {
    return x < rhs.x;
  }
};

int t[50010];
int a[50010];
int b[50010];
int y[50010];
int q;
int n, m;
int ini[200010];
SegmentTree stree;
vector<Event> events;

void CalcIni() {
  MEMSET(stree.data, -1);
  int pos = 0;
  set<int> constraints;
  constraints.insert(q);
  set<int> rest;
  REP(i, n) {
    while (pos < (int)events.size() && events[pos].x == i) {
      Event e = events[pos++];
      if (e.type == QUERY_IN) {
        stree.change(e.index, Node(e.v, e.v));
        rest.insert(e.index);
      } else if (e.type == QUERY_OUT) {
        stree.change(e.index, Node(-1, -1));
        rest.erase(e.index);
      } else if (e.type == ASSIGN_IN) {
        constraints.insert(e.index);
      } else if (e.type == ASSIGN_OUT) {
        constraints.erase(e.index);
      }
    }
    ini[i] = stree.get(0, *constraints.begin()).upper;
  }
}

bool Check() {
  stree = SegmentTree();
  MEMSET(stree.data, 1 << 30);
  REP(i, n) { stree.change(i, Node(ini[i], ini[i])); }
  REP(i, q) {
    if (t[i] == 0) {
      if (stree.get(a[i], b[i]).lower != y[i]) { return false; }
    } else {
      stree.set(Node(y[i], y[i]), a[i], b[i]);
    }
  }
  return true;
}

int main() {
  while (scanf("%d", &q) > 0) {
    events.clear();
    n = m = 0;
    {
      set<int> open1;
      set<int> open2;
      REP(i, q) {
        scanf("%d %d %d %d", &t[i], &a[i], &b[i], &y[i]);
        open1.insert(a[i]);
        open1.insert(b[i]);
        open1.insert(b[i] + 1);
        open2.insert(y[i]);
      }
      map<int, int> mapto1;
      map<int, int> mapto2;
      FORIT(it, open1) {
        mapto1[*it] = n++;
      }
      FORIT(it, open2) {
        mapto2[*it] = m++;
      }
      REP(i, q) {
        a[i] = mapto1[a[i]];
        b[i] = mapto1[b[i]];
        y[i] = mapto2[y[i]];
      }
    }
    REP(i, q) {
      events.push_back(Event(i, 2 * t[i], a[i], y[i]));
      events.push_back(Event(i, 2 * t[i] + 1, b[i] + 1, y[i]));
    }
    sort(events.begin(), events.end());
    CalcIni();
    puts(Check() ? "YES" : "NO");
  }
}

Measured memory_bytes: 13000