#include <bits/stdc++.h>
#define up(i,a,b) for (int i = (int)a; i <= (int)b; i++)
using namespace std;

const int maxn = 1e5 + 10;
const int MOD = 1e9 + 7;
struct EVENT{
    int x;
    int y1;
    int y2;
    int type;
};
//events in sweep line
//there are 2 type of events: opening and closing, represented by 1 and -1

bool comp(EVENT& A, EVENT& B){
    if (A.x == B.x) return A.type > B.type;
    return A.x < B.x;
}
// Sort events by x; if equal, process "opening" events before "closing"

vector<EVENT> events;
vector<int> tempY;

int n, treesize;
long long T1[maxn << 3];
long long T2[maxn << 3];
long long T3[maxn << 3];
int delta[maxn << 3];
// maxn << 1 for the maximum number of possible events (each rectangle adds one open and one close event)
// maxn << 3 as an upper bound for the segment tree size over all events

void push_up(int nod, int l, int r){
    int len = tempY[r+1] - tempY[l];
    if (delta[nod] >= 3) {
    //  whole interval has +3 -> full length for all tree at this node
        T3[nod] = T2[nod] = T1[nod] = len;
        return;
    }

    if (l == r){
        // leaf
        if (delta[nod] == 2)      T1[nod] = T2[nod] = len, T3[nod] = 0;
        else if (delta[nod] == 1) T1[nod] = len, T2[nod] = T3[nod] = 0;
        else                      T1[nod] = T2[nod] = T3[nod] = 0;
        return;
    }

    if (delta[nod] == 2){
        // whole interval has +2 -> >=1 and >=2 are full length
        T1[nod] = T2[nod] = len;
        // parts that become >=3 are where children had >=1
        T3[nod] = T1[nod*2] + T1[nod*2+1];
        return;
    }
    if (delta[nod] == 1){
        // whole interval has +1 -> >=1 is full length
        T1[nod] = len;
        // >=2 are positions where child had >=1
        T2[nod] = T1[nod*2] + T1[nod*2+1];
        // >=3 are positions where child had >=2
        T3[nod] = T2[nod*2] + T2[nod*2+1];
        return;
    }
    // delta == 0
    T1[nod] = T1[nod*2] + T1[nod*2+1];
    T2[nod] = T2[nod*2] + T2[nod*2+1];
    T3[nod] = T3[nod*2] + T3[nod*2+1];
}
// If interval [l, r] is fully covered, take the full segment
// If [l, r] is partially covered (and not a leaf), update based on children
// If the leaf is not covered, its value must be 0
// Warning: Without (l != r) case, segment tree might access child indices outside the declared range

void update(int nod, int l, int r, int u, int v, int val){
    if (r < u || l > v) return;
    if (l >= u && r <= v){
        delta[nod] += val;
        push_up(nod, l, r);
        return;
    }
    int mid = (l+r) >> 1;
    update(nod*2, l, mid, u, v, val);
    update(nod*2+1, mid+1, r, u, v, val);
    push_up(nod, l, r);
}

signed main(){
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    #define Task "A"
    if (fopen(Task".inp", "r")){
        freopen(Task".inp", "r", stdin);
        freopen(Task".out", "w", stdout);
    }

    cin >> n;
    up(i,1,n){
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        tempY.push_back(y1);
        tempY.push_back(y2);
        events.push_back({x1, y1, y2, 1});
        events.push_back({x2, y1, y2, -1});
    }

    tempY.push_back(-MOD);
    sort(tempY.begin(), tempY.end());
    tempY.resize(unique(tempY.begin(), tempY.end()) - tempY.begin());
    treesize = tempY.size()-1;
	// tempY includes an extra element -MOD so lower_bound starts from index 1
	// treesize = tempY.size() - 1 because we ignore the -MOD element


    sort(events.begin(), events.end(), comp);

    long long res = 0;
    for (int i = 0; i < (int)(events.size()-1); i++){
        int u = lower_bound(tempY.begin(), tempY.end(), events[i].y1) - tempY.begin();
        int v = lower_bound(tempY.begin(), tempY.end(), events[i].y2) - tempY.begin();
		// u and v are 1-based indexing

        update(1, 1, treesize-1, u, v-1, events[i].type);
        res += 1ll * (T2[1] - T3[1]) * (events[i+1].x - events[i].x);
		// The segment tree manages "intervals" between consecutive coordinates
		// With treesize coordinate points, there are treesize-1 intervals
		// Each node with index v manages the interval [l, r]; when fully covered, its length is tempY[r+1] - tempY[l]
		// Thus, each query on coordinate range [u, v] corresponds to interval [u, v-1] in the segment tree
    }
    cout << res;
}











//#include <bits/stdc++.h>
//#define up(i,a,b) for (int i = (int)a; i <= (int)b; i++)
//#define down(i,a,b) for (int i = (int)a; i >= (int)b; i--)
//using namespace std;
//
//const int maxn = 5e2 + 10;
//int n;
//int a[maxn][maxn];
//int MAXX = 500;
//int MAXY = 500;
//
//signed main(){
//    ios_base::sync_with_stdio(false);
//    cin.tie(0);
//    #define Task "A"
//    if (fopen(Task".inp", "r")){
//        freopen(Task".inp", "r", stdin);
//        freopen(Task".out", "w", stdout);
//    }
//
//    int maxx, maxy;
//    maxx = -MAXX;
//    maxy = -MAXX;
//    cin >> n;
//    up(i,1,n){
//        int x1, y1, x2, y2;
//        cin >> x1 >> y1 >> x2 >> y2;
//        maxx = max(maxx, x2-1);
//        maxy = max(maxy, y2-1);
//        up(i, x1, x2-1){
//            up(j, y1, y2-1){
//                ++a[i][j];
//            }
//        }
//    }
//
//    int cnt = 0;
//    up(i,1,maxx){
//        up(j,1,maxy){
//            if (a[i][j] == 2) ++cnt;
//        }
//    }
//    cout << cnt << "\n";
//
//
//    down(j,maxy,1){
//        up(i,1,maxx){
//            cout << a[i][j] << " ";
//        }
//        cout << "\n";
//    }
//}
