using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static string InputPattern = "InputX";
static List<string> GetInputList()
{
var WillReturn = new List<string>();
if (InputPattern == "Input1") {
WillReturn.Add("15 6");
WillReturn.Add("10");
WillReturn.Add("1 4 5 6");
WillReturn.Add("2 1 4 5");
WillReturn.Add("1 0 5 1");
WillReturn.Add("6 1 7 5");
WillReturn.Add("7 5 9 6");
WillReturn.Add("7 0 9 2");
WillReturn.Add("9 1 10 5");
WillReturn.Add("11 0 14 1");
WillReturn.Add("12 1 13 5");
WillReturn.Add("11 5 14 6");
WillReturn.Add("0 0");
//5
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
static long[] GetSplitArr(string pStr)
{
return (pStr == "" ? new string[0] : pStr.Split(' ')).Select(pX => long.Parse(pX)).ToArray();
}
static long mH;
static long mW;
struct RectInfoDef
{
internal long StaX;
internal long StaY;
internal long EndX;
internal long EndY;
}
static List<RectInfoDef> mRectInfoList = new List<RectInfoDef>();
static long UB_X;
static long UB_Y;
static long[,] mImosArr;
static void Main()
{
List<string> InputList = GetInputList();
long[] wkArr = { };
Action<string> SplitAct = (pStr) => wkArr = GetSplitArr(pStr);
int CurrInd = 0;
while (true) {
SplitAct(InputList[CurrInd]);
mW = wkArr[0];
mH = wkArr[1];
if (mW == 0 && mH == 0) {
break;
}
long N = long.Parse(InputList[CurrInd + 1]);
mRectInfoList.Clear();
for (int I = CurrInd + 2; I <= CurrInd + 2 + N - 1; I++) {
SplitAct(InputList[I]);
RectInfoDef WillAdd;
WillAdd.StaX = wkArr[0];
WillAdd.StaY = wkArr[1];
WillAdd.EndX = wkArr[2] - 1;
WillAdd.EndY = wkArr[3] - 1;
mRectInfoList.Add(WillAdd);
}
Solve();
CurrInd += 2 + (int)N;
}
}
static void Solve()
{
// X座標のset
var XSet = new HashSet<long>() { 0, mW - 1 };
// Y座標のset
var YSet = new HashSet<long>() { 0, mH - 1 };
foreach (RectInfoDef EachRectInfo in mRectInfoList) {
XSet.Add(EachRectInfo.StaX - 1);
XSet.Add(EachRectInfo.StaX);
XSet.Add(EachRectInfo.EndX);
XSet.Add(EachRectInfo.EndX + 1);
YSet.Add(EachRectInfo.StaY - 1);
YSet.Add(EachRectInfo.StaY);
YSet.Add(EachRectInfo.EndY);
YSet.Add(EachRectInfo.EndY + 1);
}
XSet.RemoveWhere(pX => pX < 0 || mW - 1 < pX);
YSet.RemoveWhere(pX => pX < 0 || mH - 1 < pX);
Dictionary<long, long> ZaatuDictX = DeriveZaatuDict(XSet);
Dictionary<long, long> ZaatuDictY = DeriveZaatuDict(YSet);
UB_X = ZaatuDictX.Values.Max();
UB_Y = ZaatuDictY.Values.Max();
mImosArr = new long[UB_X + 1, UB_Y + 1];
// 二次元いもす法
foreach (RectInfoDef EachRectInfo in mRectInfoList) {
long NewStaX = ZaatuDictX[EachRectInfo.StaX];
long NewStaY = ZaatuDictY[EachRectInfo.StaY];
long NewEndX = ZaatuDictX[EachRectInfo.EndX] + 1;
long NewEndY = ZaatuDictY[EachRectInfo.EndY] + 1;
mImosArr[NewStaX, NewStaY]++;
if (NewEndX <= UB_X) {
mImosArr[NewEndX, NewStaY]--;
}
if (NewEndY <= UB_Y) {
mImosArr[NewStaX, NewEndY]--;
}
if (NewEndX <= UB_X && NewEndY <= UB_Y) {
mImosArr[NewEndX, NewEndY]++;
}
}
// 横方向の累積和を求める
for (int X = 1; X <= UB_X; X++) {
for (int Y = 0; Y <= UB_Y; Y++) {
mImosArr[X, Y] += mImosArr[X - 1, Y];
}
}
// 縦方向の累積和を求める
for (int Y = 1; Y <= UB_Y; Y++) {
for (int X = 0; X <= UB_X; X++) {
mImosArr[X, Y] += mImosArr[X, Y - 1];
}
}
long Result = ExecBFS();
Console.WriteLine(Result);
}
// BFSを行い、何個の0の島があるかを判定
static long ExecBFS()
{
mVisitedSet.Clear();
long ShimaCnt = 0;
for (int X = 0; X <= UB_X; X++) {
for (int Y = 0; Y <= UB_Y; Y++) {
if (mImosArr[X, Y] != 0) continue;
long CurrHash = GetHash(X, Y);
if (mVisitedSet.Contains(CurrHash)) {
continue;
}
ExecBFSHelper(X, Y);
ShimaCnt++;
}
}
return ShimaCnt;
}
static void ExecBFSHelper(long pStaX, long pStaY)
{
var Que = new Queue<JyoutaiDef>();
JyoutaiDef WillEnqueue;
WillEnqueue.CurrX = pStaX;
WillEnqueue.CurrY = pStaY;
Que.Enqueue(WillEnqueue);
while (Que.Count > 0) {
JyoutaiDef Dequeued = Que.Dequeue();
Action<long, long> EnqueueAct = (pNewX, pNewY) =>
{
if (pNewX < 0 || UB_X < pNewX) return;
if (pNewY < 0 || UB_Y < pNewY) return;
if (mImosArr[pNewX, pNewY] != 0) return;
long CurrHash = GetHash(pNewX, pNewY);
if (mVisitedSet.Add(CurrHash)) {
WillEnqueue.CurrX = pNewX;
WillEnqueue.CurrY = pNewY;
Que.Enqueue(WillEnqueue);
}
};
EnqueueAct(Dequeued.CurrX, Dequeued.CurrY - 1);
EnqueueAct(Dequeued.CurrX, Dequeued.CurrY + 1);
EnqueueAct(Dequeued.CurrX - 1, Dequeued.CurrY);
EnqueueAct(Dequeued.CurrX + 1, Dequeued.CurrY);
}
}
static HashSet<long> mVisitedSet = new HashSet<long>();
struct JyoutaiDef
{
internal long CurrX;
internal long CurrY;
}
static long GetHash(long pX, long pY)
{
return pX * 10000 + pY;
}
//////////////////////////////////////////////////////////////////////////
// 列挙を引数として、座標圧縮し、座圧後の値[座圧前の値]なDictを返す
//////////////////////////////////////////////////////////////////////////
static Dictionary<long, long> DeriveZaatuDict(IEnumerable<long> pEnum)
{
var ZaatuDict = new Dictionary<long, long>();
var ValSet = new HashSet<long>(pEnum);
long No = 0;
foreach (long EachVal in ValSet.OrderBy(pX => pX)) {
ZaatuDict[EachVal] = No;
No++;
}
return ZaatuDict;
}
}