#define _CRT_SECURE_NO_WARNINGS

#include <cstdio>
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <vector>
#include <set>

typedef long long lLong;
typedef unsigned long uLong;
typedef unsigned long long ulLong;
typedef unsigned int uInt;
typedef unsigned char Byte;

using namespace std;

#define FOR(x, b, e) for(int x=b; x<=(e); ++x)
#define ALL(i) (i).begin(), (i).end()
#define CONTAINS(i,v) (find(ALL(i),v)!=(i).end())
typedef vector<bool> bit_vector;
typedef vector<ulLong> VI;
typedef vector<ulLong> VLL;


// struktura i funkcje porownujace wzorowana na literaturze - 
struct POINT 
{
    lLong x,y;
    POINT(lLong x = 0, lLong y = 0) : x(x), y(y) {}
    bool operator ==(POINT& a) {return a.x==x && a.y==y;}
};

inline bool OrdPointXY(POINT *a, POINT *b) 
{
	return a->x==b->x ? a->y<b->y : a->x<b->x;
}


struct  Rect
{
	POINT BottomLeft;
	POINT TopRight;

	Rect (){}
	Rect (POINT bl, POINT tr)
	{
		BottomLeft = bl;
		TopRight = tr;
	}
	bool IsCrossing(Rect *rect)
	{
		bool zwrot=false;
		Rect *near, *far;
		if(OrdPointXY(&BottomLeft,&rect->BottomLeft)==true)
		{
			near=this;far=rect;
		}
		else
		{
			near=rect;far=this;
		}
		zwrot=(		near->BottomLeft.y<=far->TopRight.y 
				&&	near->TopRight.y>=far->BottomLeft.y
				&&	near->TopRight.x>=far->BottomLeft.x
				&&	near->BottomLeft.x<=far->TopRight.x);
		/*
		bool zwrot
			BottomLeft.x < rect->TopRight.x
			&& TopRight.x > rect->BottomLeft.x
			&& BottomLeft.y < rect->TopRight.y
			&& TopRight.y > rect->BottomLeft.y;*/
		/*lLong x1,y1,x2,y2;
		x1=max(BottomLeft.x,rect->BottomLeft.x);
		y1=min(TopRight.y,rect->TopRight.y);
		x2=min(TopRight.x,rect->TopRight.x);
		y2=max(BottomLeft.y,rect->BottomLeft.y);
		bool zwrot = ((x1-x2) * (y1-y2)!=0);	*/	
		
		
		return zwrot;
	}
	void Scal(Rect *rect)
	{
		if(rect)
		{
			BottomLeft.x=min(BottomLeft.x,rect->BottomLeft.x);
			BottomLeft.y=min(BottomLeft.y,rect->BottomLeft.y);
			TopRight.x=max(TopRight.x,rect->TopRight.x);
			TopRight.y=max(TopRight.y,rect->TopRight.y);			
		}
	}
};

inline bool OrdRectXY(Rect *a, Rect *b) 
{
	return OrdPointXY(&a->BottomLeft,&b->BottomLeft);
}

struct Plemie
{
public:	
	int Id;
	Rect Obszar;
	bool Scalone;
	Plemie():Id(0),Scalone(false){}
	
	void WczytajObszar(FILE *input)
	{		
		fscanf(input,"%lld %lld %lld %lld",
			&Obszar.BottomLeft.x, &Obszar.TopRight.x,
			&Obszar.BottomLeft.y, &Obszar.TopRight.y);
	}
	void WypiszObszar()
	{
		printf("%lld %lld %lld %lld\n",
			Obszar.BottomLeft.x, Obszar.TopRight.x,
			Obszar.BottomLeft.y, Obszar.TopRight.y);
	}
	bool SprawdzCzyNachodzi(Plemie* plemie)
	{
		return Obszar.IsCrossing(&plemie->Obszar);	
	}
	bool operator ==(Plemie& a) {return a.Id==Id ;}
};

inline bool OrdPlemieXY(Plemie *a, Plemie *b) 
{
	return OrdRectXY(&a->Obszar,&b->Obszar);
}


class PrzypadekTestowy
{
private:
	int _iloscPlemion;
	inline void Reset()
	{
		Plemiona.clear();
	}
	inline void PosortujPlemiona()
	{
		PlemionaOrdXY.clear();
		for(vector<Plemie>::iterator it=Plemiona.begin();it!=Plemiona.end();++it)
		{
			PlemionaOrdXY.push_back(&(*it));
		}
		std::sort(ALL(PlemionaOrdXY),OrdPlemieXY);
	}
public:	
	vector<Plemie> Plemiona;
	vector<Plemie*> PlemionaOrdXY;

	inline void WczytajPrzypadekTestowy()
	{
		WczytajPrzypadekTestowy(stdin);
	}
	inline void WczytajPrzypadekTestowy(FILE *input)
	{
		Reset();
		if(NULL!=input)
		{
			fscanf(input,"%d",&_iloscPlemion);
			Plemiona.resize(_iloscPlemion);
			for(int i=0;i<_iloscPlemion;i++)
			{				
				Plemiona[i].Id=i;
				Plemiona[i].WczytajObszar(input);	
			}
		}
	}	

	inline void PobierzWynik(vector<Plemie*> &wyjscie)
	{
		vector<Plemie*>::iterator it;
		for(it = PlemionaOrdXY.begin(); it != PlemionaOrdXY.end();++it)
		{
			if((*it)->Scalone==false)
			{
				wyjscie.push_back((*it));
			}			
		}	
	}
	inline void GrupujPanstwa()
	{
		PosortujPlemiona();		
		Plemie* p1, *p2;
		int iloscScalen=-1;
		int gCount=-1;
		while(gCount!=0) //przetwarzaj do skutku
		{
			gCount=0;
			for(uInt i =0;i<PlemionaOrdXY.size();i++)
			{			
				if(iloscScalen>0 && i>0)
				{				
					i--; // krok w tył żeby ponownie zweryfyfikowac kwadraty pominiete
				}
				iloscScalen=0;			
				p1=PlemionaOrdXY[i];
				if(p1->Scalone==false)
				{
					for(uInt j=i+1;j<PlemionaOrdXY.size();j++)
					{
						p2=PlemionaOrdXY[j];
						if(p2->Scalone==false)
						{
							if(p1->Obszar.TopRight.x<p2->Obszar.BottomLeft.x)
							{
								break; // nie ma co sprawdzac kwadratow spoza obszaru
							}
							if(p1->SprawdzCzyNachodzi(p2))
							{
								p1->Obszar.Scal(&p2->Obszar);
								p2->Scalone=true;
								iloscScalen ++;
								gCount ++;
							}
						}
					}
				}
			}	
		}
	}

	void WypiszPlemiona(vector<Plemie*> &dane)
	{
		uInt ilosc=dane.size();
		printf("Ilosc plemion:%d\n",ilosc);
		for(uInt i=0;i<ilosc;i++)
		{
			dane[i]->WypiszObszar();
		}
	}
	void WypiszWynik()
	{	
		vector<Plemie*> wynik;
		PobierzWynik(wynik);
		uInt iloscPanstw=wynik.size();
		printf("%d\n",iloscPanstw);		
		for(uInt i=0;i<iloscPanstw;i++)
		{
			wynik[i]->WypiszObszar();			
		}		
	}
};



int main(int argc, char **argv)
{	
	/*Rect r1(POINT(11,31),POINT(995558,922989));
	Rect r2(POINT(209158,929120),POINT(757909,931570));
	bool t;
	t=r1.IsCrossing(&r2);
	t=r2.IsCrossing(&r1);
	
	r1.Scal(&r2);*/

	// Do celow testowych
	FILE *in=stdin;
	if(argc>1)
	{		
		in = fopen(argv[1],"rt");
		if(NULL==in)
		{
			in=stdin;
		}
	}
	///////////////////////////////////////////	
	PrzypadekTestowy przypadek;					
	przypadek.WczytajPrzypadekTestowy(in);	
	przypadek.GrupujPanstwa();
	przypadek.WypiszWynik();			
	return 0;
}
