{  ACM International Collegiate Programming Contest
   Central European Regional Contest 2000
   
   Problem B - The Bulk
   Martin Kacer
}

Program Volume;

Const MAX_POINTS = 512;
	MAX_POLYGONS = 512;

Type TPoint	  = Record X, Y, Z : Integer; End;
Type TPolygon = Record N : Integer; P: Array [1..MAX_POINTS] of TPoint; End;

Var TaskCnt, TaskNum  : Integer;
	Polys			  : Array [1..MAX_POLYGONS] of TPolygon;
	PolysCnt, PolysXY : Integer;
	I, J			  : Integer;
	SqInside		  : TPoint;
	TopSide			  : Boolean;
	SumVolume		  : Integer;

{ reads polygon into `Poly' and returns true if it is in XY direction }
Function ReadPolygon (Var Poly : TPolygon) : Boolean;
Var I : Integer;
Begin
	ReadPolygon:= true;
	Read (Poly.N);
	For I:= 1 to Poly.N do Begin
		Read (Poly.P[I].X, Poly.P[I].Y, Poly.P[I].Z);
		if (Poly.P[I].Z <> Poly.P[1].Z) then ReadPolygon:= false;
	End;
	ReadLn;
End; { ReadPolygon }


{ computes area of an XY polygon }
Function PolygonXYArea (Var Poly : TPolygon) : Integer;
Var I, I1 : Integer;
	Area  : Integer;
Begin
	Area:= 0;
	For I:= 1 to Poly.N do Begin
		I1:= I - 1;
		if (I1 = 0) then I1:= Poly.N;
		Area:= Area + Poly.P[I].Y * (Poly.P[I].X - Poly.P[I1].X);
	End;
	If (Area < 0) then
		PolygonXYArea:= 0 - Area
	else
		PolygonXYArea:= Area;
End; { PolygonXYArea }


{ returns one square inside a polygon; square is (X,Y,Z)-(X+1,Y+1,Z) }
Procedure SquareInsideXYPolygon (Var Poly : TPolygon; Var Sq : TPoint);
Var MinX, MinY : Integer;
	I, I1	   : Integer;
Begin
	MinX:= Poly.P[1].X + 1;
	For I:= 1 to Poly.N do Begin
		I1:= I - 1;
		if (I1 = 0) then I1:= Poly.N;
		If (Poly.P[I].X = Poly.P[I1].X) and (Poly.P[I].X < MinX) then Begin
			MinX:= Poly.P[I].X;
			MinY:= Poly.P[I].Y;
			if (MinY > Poly.P[I1].Y) then MinY:= Poly.P[I1].Y;
		End;
	End;
	Sq.X:= MinX;
	Sq.Y:= MinY;
	Sq.Z:= Poly.P[1].Z;
End; { SquareInXYPolygon }


{ finds out if the square is inside the polygon }
Function IsInsideXYPolygon (Var Poly : TPolygon; Sq : TPoint) : Boolean;
Var I, I1  : Integer;
	Y1, Y2 : Integer;
	Ret	   : Boolean;
Begin
	Ret:= false;
	For I:= 1 to Poly.N do Begin
		I1:= I - 1;
		if (I1 = 0) then I1:= Poly.N;		
		If (Poly.P[I].X = Poly.P[I1].X) and (Poly.P[I].X <= Sq.X) then
		Begin
			If (Poly.P[I].Y < Poly.P[I1].Y) then Begin
				Y1:= Poly.P[I].Y;
				Y2:= Poly.P[I1].Y;
			End else Begin
				Y1:= Poly.P[I1].Y;
				Y2:= Poly.P[I].Y;
			End;
			If (Y1 <= Sq.Y) and (Sq.Y < Y2) then Ret:= not Ret;
		End;
	End;
	IsInsideXYPolygon:= Ret;
End; { IsInsideXYPolygon }


Begin
	Readln (TaskCnt);
	For TaskNum:= 1 to TaskCnt do
	Begin
		ReadLn (PolysCnt);
		PolysXY:= 1;
		For I:= 1 to PolysCnt do
			If ( ReadPolygon (Polys[PolysXY]) ) then PolysXY:= PolysXY + 1;
		PolysXY:= PolysXY - 1;
		SumVolume:= 0;
		For I:= 1 to PolysXY do
		Begin
			TopSide:= false;
			SquareInsideXYPolygon (Polys[I], SqInside);
			For J:= 1 to PolysXY do
				If (Polys[J].P[1].Z < Polys[I].P[1].Z) then
					If (IsInsideXYPolygon (Polys[J], SqInside)) then
						TopSide:= not TopSide;
			{WriteLn ('POLY: area=', PolygonXYArea (Polys[I]),
					 ', z=', Polys[I].P[1].Z,
					 ', top=', TopSide,
					 ', square=(', SqInside.X,',',
					SqInside.Y,',',SqInside.Z,')');}
			If (TopSide) then
				SumVolume:= SumVolume
						+ Polys[I].P[1].Z * PolygonXYArea (Polys[I])
			else
				SumVolume:= SumVolume
						- Polys[I].P[1].Z * PolygonXYArea (Polys[I]);
		End;
		WriteLn ('The bulk is composed of ', SumVolume, ' units.');
	End;
End.
