#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#define PREC 0.001
#define MAX_NODES 3004
#define MAX_TRIANGLES (2*MAX_NODES+1)
#define MAX_EDGES (2*MAX_TRIANGLES +1)
#define CACHE_INCREASE_COEF 12
#define MAX_CACHE_SIZE 64
typedef struct _COORD
{
float x;
float y;
} COORD, *PCOORD;
typedef struct _NODE
{
COORD crd;
int id;
} NODE, *PNODE;
typedef struct _TRIANGLE
{
NODE * n1, * n2, * n3;
struct _TRIANGLE * t1, *t2, *t3;
float d1, d2, d3;
char usedEdges;
} TRIANGLE, *PTRIANGLE;
typedef struct _REBUILD_ITEM
{
PTRIANGLE toProcess;
PTRIANGLE toExclude;
}REBUILD_ITEM, *PREBUILD_ITEM;
typedef struct _EDGE
{
float d;
int n1;
int n2;
PTRIANGLE trg1, trg2;
}EDGE, *PEDGE;
typedef struct _TRG_SQUARE
{
float sqr;
PTRIANGLE trg;
}TRG_SQUARE, *PTRG_SQUARE;
struct NORMALIZE
{
float minx, maxx, miny, maxy;
float divx;
float divy;
} norm;
PTRIANGLE cache[MAX_CACHE_SIZE][MAX_CACHE_SIZE];
int cache_size = 2;
REBUILD_ITEM rebuild_queue[MAX_TRIANGLES];
int start_queue = 0, end_queue = 0;
#define vect_abs_2(a) ((a).x*(a).x + (a).y*(a).y)
#define segm_abs_2(a,b) (((a).x-(b).x)*((a).x-(b).x) + ((a).y-(b).y)*((a).y-(b).y))
#define dots_equal(a, b) ((a).x==(b).x && (a).y==(b).y)
TRIANGLE triangles[MAX_TRIANGLES];
PTRIANGLE pnexttrg = triangles;
int nTriangles = 0;
NODE nodes[MAX_NODES+MAX_NODES-2];
int nNodes = 0;
int nSteinerNodes = 0;
EDGE edges[MAX_EDGES];
int nEdges = 0;
typedef void (*MSTCALLBACK)(PEDGE pArc);
__inline void addEdge(int node1, int node2, float d, PTRIANGLE trg1, PTRIANGLE trg2)
{
edges[nEdges].trg1 = trg1;
edges[nEdges].trg2 = trg2;
edges[nEdges].d = d;
edges[nEdges].n1 = node1;
edges[nEdges++].n2 = node2;
}
__inline PTRIANGLE addTriangle(PNODE a, PNODE b, PNODE c)
{
pnexttrg->n1 = a;
pnexttrg->n2 = b;
pnexttrg->n3 = c;
pnexttrg->d1 = pnexttrg->d2 = pnexttrg->d3 = 0;
pnexttrg->usedEdges = 0;
nTriangles++;
return pnexttrg++;
}
__inline void pushToRebuild(PTRIANGLE trg, PTRIANGLE excl)
{
int i;
for(i = start_queue; i < end_queue; i++)
if(trg == rebuild_queue[i].toProcess) return;
rebuild_queue[end_queue].toProcess = trg;
rebuild_queue[end_queue++].toExclude = excl;
}
void increaseCache()
{
int i, j;
for(i = cache_size-1; i >= 0; i--)
{
for(j = cache_size-1; j >= 0; j--)
{
cache[2*i][2*j] = cache[2*i][2*j+1] =
cache[2*i+1][2*j] = cache[2*i+1][2*j+1] = cache[i][j];
}
}
cache_size*=2;
}
__inline PTRIANGLE popToRebuild(PTRIANGLE * pexcl)
{
PTRIANGLE ret;
if(start_queue == end_queue) return 0;
ret = rebuild_queue[start_queue].toProcess;
*pexcl = rebuild_queue[start_queue++].toExclude;
if(start_queue == end_queue) start_queue = end_queue = 0;
return ret;
}
enum {LEFT, RIGHT, BEYOND, BEHIND, BETWEEN, START, END};
int dotRelation(PCOORD dot, PCOORD start, PCOORD end)
{
COORD a = {end->x - start->x, end->y - start->y};
COORD b = {dot->x - start->x, dot->y - start->y};
-
float sa = a.x * b.y - b.x * a.y;
-
if(sa > PREC)
return LEFT;
if(sa < -PREC)
return RIGHT;
if((a.x * b.x < 0.0) || (a.y * b.y < 0.0))
return BEHIND;
if(vect_abs_2(a) < vect_abs_2(b))
return BEYOND;
if(dots_equal(*start, *dot))
return START;
if(dots_equal(*end, *dot))
return END;
return BETWEEN;
}
__inline int whereIsDot(PCOORD dot, PTRIANGLE trg)
{
if(dotRelation (dot, &trg->n1->crd, &trg->n2->crd) == LEFT)
return 1;
if(dotRelation (dot, &trg->n2->crd, &trg->n3->crd) == LEFT)
return 2;
if(dotRelation (dot, &trg->n3->crd, &trg->n1->crd) == LEFT)
return 3;
return 0;
}
PTRIANGLE findTriangle(PCOORD dot)
{
int a, b;
PTRIANGLE tmp;
a = (int)floor(((dot->x - norm.minx)*cache_size)/norm.divx);
b = (int)floor(((dot->y - norm.miny)*cache_size)/norm.divy);
tmp = cache[a][b];
for(;;)
{
switch(whereIsDot(dot, tmp))
{
case 0:
break;
case 1:
tmp = tmp->t1;
continue;
case 2:
tmp = tmp->t2;
continue;
case 3:
tmp = tmp->t3;
continue;
}
break;
}
cache[a][b] = tmp;
return tmp;
}
__inline void rotateNodesFor(PTRIANGLE trg)
{
PNODE tmpn;
PTRIANGLE tmpt;
tmpn = trg->n3;
trg->n3 = trg->n2;
trg->n2 = trg->n1;
trg->n1 = tmpn;
tmpt = trg->t3;
trg->t3 = trg->t2;
trg->t2 = trg->t1;
trg->t1 = tmpt;
}
__inline void rotateNodesRev(PTRIANGLE trg)
{
PNODE tmpn;
PTRIANGLE tmpt;
tmpn = trg->n1;
trg->n1 = trg->n2;
trg->n2 = trg->n3;
trg->n3 = tmpn;
tmpt = trg->t1;
trg->t1 = trg->t2;
trg->t2 = trg->t3;
trg->t3 = tmpt;
}
__inline void updateTriangleRef(PTRIANGLE trg, PTRIANGLE ref, PTRIANGLE upd)
{
if( trg->t1 == ref )
trg->t1 = upd;
else if( trg->t2 == ref )
trg->t2 = upd;
else if( trg->t3 == ref )
trg->t3 = upd;
}
int checkDelaunay(PCOORD dot, PTRIANGLE trg)
{
// the 1-2 edge must be between the dot and node3
float sa, sb;
PCOORD c0 = dot, c1 = &trg->n2->crd, c2 = &trg->n3->crd, c3 = &trg->n1->crd;
sa = (c1->x - c0->x)*(c3->x - c0->x) + (c1->y - c0->y)*(c3->y - c0->y);
sb = (c1->x - c2->x)*(c3->x - c2->x) + (c1->y - c2->y)*(c3->y - c2->y);
-
if(sa < 0 && sb < 0) return 0;
if(sa >= 0 && sb >= 0) return 1;
return ((((c3->x - c0->x)*(c1->y - c0->y) - (c1->x - c0->x)*(c3->y - c0->y)) * sb +
((c1->x - c2->x)*(c3->y - c2->y) - (c3->x - c2->x)*(c1->y - c2->y)) * sa) >= 0);
}
void rebuildTriangles()
{
int nCheck;
PTRIANGLE trg1, trg2, excl;
while(trg1 = popToRebuild(&excl))
{
nCheck = 3;
do
{
trg2 = trg1->t1;
if(!trg2 || trg2 == excl) continue;
while(trg2->t1 != trg1) rotateNodesFor(trg2);
if(checkDelaunay(&trg2->n3->crd, trg1)) continue;
// rebuild triangles
trg1->n2 = trg2->n3;
trg2->n2 = trg1->n3;
if(trg1->t2) updateTriangleRef(trg1->t2, trg1, trg2);
if(trg2->t2) updateTriangleRef(trg2->t2, trg2, trg1);
trg1->t1 = trg2->t2;
trg2->t1 = trg1->t2;
trg1->t2 = trg2;
trg2->t2 = trg1;
pushToRebuild(trg2, trg1);
nCheck = 3;
rotateNodesRev(trg1);
} while(rotateNodesRev(trg1), --nCheck);
}
}
void splitTriangle(PNODE node, PTRIANGLE trg)
{
int r1, r2, r3;
PTRIANGLE trg1 = 0, trg2 = 0;
r1 = dotRelation (&node->crd, &trg->n1->crd, &trg->n2->crd);
r2 = dotRelation (&node->crd, &trg->n2->crd, &trg->n3->crd);
r3 = dotRelation (&node->crd, &trg->n3->crd, &trg->n1->crd);
if(r1 == RIGHT && r2 == RIGHT && r3 == RIGHT)
{
// the dot in the triangle
trg1 = addTriangle(node, trg->n1, trg->n2);
trg2 = addTriangle(node, trg->n3, trg->n1);
trg->n1 = node;
if(trg->t1) updateTriangleRef(trg->t1, trg, trg1);
if(trg->t3) updateTriangleRef(trg->t3, trg, trg2);
trg1->t1 = trg2;
trg1->t2 = trg->t1;
trg1->t3 = trg;
trg2->t1 = trg;
trg2->t2 = trg->t3;
trg2->t3 = trg1;
trg->t1 = trg1;
trg->t3 = trg2;
pushToRebuild(trg, trg2);
pushToRebuild(trg1, trg);
pushToRebuild(trg2, trg1);
}else
{
if(r1 == BETWEEN) {}
else if(r2 == BETWEEN)
rotateNodesRev(trg);
else if(r3 == BETWEEN)
rotateNodesFor(trg);
else
return;
if(trg->t1)
while(trg->t1->t1 != trg) rotateNodesFor(trg->t1);
// the dot on the edge 1-2 now
trg1 = addTriangle(node, trg->n3, trg->n1);
trg->n1 = node;
if(trg->t1)
{
trg2 = addTriangle(node, trg->t1->n3, trg->t1->n1);
trg->t1->n1 = node;
}
trg1->t1 = trg;
trg1->t2 = trg->t3;
trg1->t3 = trg->t1;
if(trg->t1)
{
trg2->t1 = trg->t1;
trg2->t2 = trg->t1->t3;
trg2->t3 = trg;
-
if(trg->t1->t3) updateTriangleRef(trg->t1->t3, trg->t1, trg2);
trg->t1->t1 = trg1;
trg->t1->t3 = trg2;
pushToRebuild(trg->t1, trg2);
pushToRebuild(trg2, trg);
}
pushToRebuild(trg, trg1);
pushToRebuild(trg1, trg->t1);
if(trg->t3) updateTriangleRef(trg->t3, trg, trg1);
trg->t1 = trg2;
trg->t3 = trg1;
}
rebuildTriangles();
}
__inline int checkEqualNodes(PNODE node, PNODE node1)
{
if(dots_equal(node->crd, node1->crd))
{
addEdge(node->id, node1->id, segm_abs_2(node->crd, node1->crd), 0, 0);
return 1;
}
return 0;
}
void delaunayTriangulation()
{
int i;
PTRIANGLE trg1, trg2;
cache_size = 2;
start_queue = 0;
end_queue = 0;
nEdges = 0;
pnexttrg = triangles;
nTriangles = 0;
-
nodes[nNodes].crd.x = norm.minx;
nodes[nNodes].crd.y = norm.miny;
nodes[nNodes].id = nNodes;
nodes[nNodes+1].crd.x = norm.maxx;
nodes[nNodes+1].crd.y = norm.miny;
nodes[nNodes+1].id = nNodes+1;
nodes[nNodes+2].crd.x = norm.maxx;
nodes[nNodes+2].crd.y = norm.maxy;
nodes[nNodes+2].id = nNodes+2;
nodes[nNodes+3].crd.x = norm.minx;
nodes[nNodes+3].crd.y = norm.maxy;
nodes[nNodes+3].id = nNodes+3;
trg1 = addTriangle(nodes+nNodes, nodes+nNodes+3, nodes+nNodes+1);
trg2 = addTriangle(nodes+nNodes+1, nodes+nNodes+3, nodes+nNodes+2);
cache[0][0] = cache[0][1] = trg1;
cache[1][0] = cache[1][1] = trg2;
trg1->t1 = trg1->t3 = 0;
trg1->t2 = trg2;
trg2->t2 = trg2->t3 = 0;
trg2->t1 = trg1;
-
for(i = 0; i< nNodes; i++)
{
trg1 = findTriangle(&nodes[i].crd);
if(checkEqualNodes(trg1->n1, nodes+i) ||
checkEqualNodes(trg1->n2, nodes+i) ||
checkEqualNodes(trg1->n3, nodes+i))
continue;
splitTriangle(nodes+i, trg1);
if(i >= CACHE_INCREASE_COEF*cache_size*cache_size)
increaseCache();
-
}
}
__inline int getEdgeMask(PTRIANGLE trg, int startNodeId)
{
if(trg->n1->id == startNodeId) return 1;
if(trg->n2->id == startNodeId) return 2;
if(trg->n3->id == startNodeId) return 4;
return 0;
}
void extractEdges()
{
char edgesmap[MAX_TRIANGLES] = {0};
int i = 0;
PTRIANGLE trg;
for(i = 0; i < nTriangles; i++)
{
trg = triangles + i;
-
trg->d1 = segm_abs_2(trg->n1->crd, trg->n2->crd);
trg->d2 = segm_abs_2(trg->n2->crd, trg->n3->crd);
trg->d3 = segm_abs_2(trg->n3->crd, trg->n1->crd);
if(trg->n1->id < nNodes && trg->n2->id < nNodes && trg->n3->id < nNodes)
{
if(trg->d1 > trg->d2)
{
if(trg->d1 > trg->d3)
{
if(trg->t1) edgesmap[trg->t1-triangles] |= getEdgeMask(trg->t1, trg->n2->id);
edgesmap[i] |= 1;
}
else
{
if(trg->t3) edgesmap[trg->t3-triangles] |= getEdgeMask(trg->t3, trg->n1->id);
edgesmap[i] |= 4;
}
}else
{
if(trg->d2 > trg->d3)
{
if(trg->t2) edgesmap[trg->t2-triangles] |= getEdgeMask(trg->t2, trg->n3->id);
edgesmap[i] |= 2;
}
else
{
if(trg->t3) edgesmap[trg->t3-triangles] |= getEdgeMask(trg->t3, trg->n1->id);
edgesmap[i] |= 4;
}
}
}else
{
if(trg->n1->id >= nNodes)
edgesmap[i] |= 5;
if(trg->n2->id >= nNodes)
edgesmap[i] |= 3;
if(trg->n3->id >= nNodes)
edgesmap[i] |= 6;
}
if((edgesmap[i] & 1) == 0)
{
addEdge(trg->n1->id, trg->n2->id, trg->d1, trg, trg->t1);
if(trg->t1) edgesmap[trg->t1-triangles] |= getEdgeMask(trg->t1, trg->n2->id);
}
-
if((edgesmap[i] & 2)==0)
{
addEdge(trg->n2->id, trg->n3->id, trg->d2, trg, trg->t2);
if(trg->t2) edgesmap[trg->t2-triangles] |= getEdgeMask(trg->t2, trg->n3->id);
}
-
if((edgesmap[i] & 4)==0)
{
addEdge(trg->n3->id, trg->n1->id, trg->d3, trg, trg->t3);
if(trg->t3) edgesmap[trg->t3-triangles] |= getEdgeMask(trg->t3, trg->n1->id);
}
-
}
}
__inline int floatcmp( const void *arg1, const void *arg2 )
{
return (*(float*)arg1)<(*(float*)arg2)?-1:(*(float*)arg1)==(*(float*)arg2)?0:1;
}
void findMST(MSTCALLBACK callback)
{
int i, j, q = 0;
short selected[MAX_NODES];
for(i = 0; i < nNodes; i++)
selected[i] = i;
qsort(edges, nEdges, sizeof(EDGE), floatcmp);
for(i = 0; i < nEdges; i++)
{
if(selected[edges[i].n1] != selected[edges[i].n2])
{
int s1 = selected[edges[i].n1],
s2 = selected[edges[i].n2];
for(j = 0; j < nNodes; j++)
if(selected[j] == s2) selected[j] = s1;
callback(edges + i);
q++;
if(q == nNodes - 1)
break;
}
}
}
#define two_edges(a) ((a)>4||(a)==3)
#define triangle_square(a,b,c) ((a).x*((b).y-(c).y)+(b).x*((c).y-(a).y)+(c).x*((a).y-(b).y))/2
#define sqrt32 (float)0.86602540378
__inline int floatrcmp( const void *arg1, const void *arg2 )
{
return (*(float*)arg1)>(*(float*)arg2)?-1:(*(float*)arg1)==(*(float*)arg2)?0:1;
}
int getSteinerPoint(PCOORD crd1, PCOORD crd2, PCOORD crd3, float d1, float d2, float d3, float sqr, PCOORD spoint)
{
COORD P, O, M;
double R2, a1, a2, b1, b2, a, b, c, t;
double e1, e2, e3;
if(d3-d1-d2 > 0.4*sqrt(d1*d2))
return 0;
P.y = crd1->x-crd3->x+crd3->y;
P.x = crd3->y-crd1->y+crd3->x;
if(dotRelation(&P, crd3, crd1) != LEFT)
{
P.y=-(P.y-crd3->y);
P.x=-(P.x-crd3->x);
-
}else
{
P.y-=crd3->y;
P.x-=crd3->x;
}
P.x*=sqrt32;
P.y*=sqrt32;
M.x = (crd1->x+crd3->x)/2;
M.y = (crd1->y+crd3->y)/2;
O.x = P.x/3+M.x;
O.y = P.y/3+M.y;
P.x += M.x;
P.y += M.y;
R2 = d3/3;
-
a1 = crd2->x - P.x;
a2 = crd2->y - P.y;
b1 = P.x - O.x;
b2 = P.y - O.y;
a = sqrt(a1*a1+a2*a2);
a1 /= a;
a2 /= a;
b = a1*b1+a2*b2;
c = b1*b1+b2*b2-R2;
-
if(b>0)
t = (-b-sqrt(b*b-c));
else
t = (-b+sqrt(b*b-c));
spoint->x = P.x + a1*t;
spoint->y = P.y + a2*t;
return 1;
}
void findSMT()
{
TRG_SQUARE squares[MAX_TRIANGLES];
PTRIANGLE trg;
int i, nSquares = 0, bExists;
for(i =0; i < nEdges; i++)
if(edges[i].d != 0) break;
nEdges = i;
nSteinerNodes = nNodes;
for(i = 0; i < nTriangles; i++ )
{
trg = triangles+i;
if( two_edges(trg->usedEdges) )
{
squares[nSquares].trg = trg;
squares[nSquares++].sqr = triangle_square(trg->n3->crd, trg->n2->crd, trg->n1->crd);
}
}
qsort(squares, nSquares, sizeof(TRG_SQUARE), floatrcmp);
for(i = 0; i < nSquares; i++)
{
trg = squares[i].trg;
if( !two_edges(trg->usedEdges) )
continue;
if(trg->usedEdges == 3)
bExists = getSteinerPoint(&trg->n1->crd, &trg->n2->crd, &trg->n3->crd, trg->d1, trg->d2, trg->d3, squares[i].sqr, &nodes[nSteinerNodes].crd);
else if(trg->usedEdges == 5)
bExists = getSteinerPoint(&trg->n3->crd, &trg->n1->crd, &trg->n2->crd, trg->d3, trg->d1, trg->d2, squares[i].sqr, &nodes[nSteinerNodes].crd);
else if(trg->usedEdges == 6)
bExists = getSteinerPoint(&trg->n2->crd, &trg->n3->crd, &trg->n1->crd, trg->d2, trg->d3, trg->d1, squares[i].sqr, &nodes[nSteinerNodes].crd);
if(bExists)
{
if(trg->t1) trg->t1->usedEdges &= ~getEdgeMask(trg->t1, trg->n2->id);
if(trg->t2) trg->t2->usedEdges &= ~getEdgeMask(trg->t2, trg->n3->id);
if(trg->t3) trg->t3->usedEdges &= ~getEdgeMask(trg->t3, trg->n1->id);
nodes[nSteinerNodes].id = nSteinerNodes;
addEdge(nSteinerNodes, trg->n1->id, 0, 0, 0);
addEdge(nSteinerNodes, trg->n2->id, 0, 0, 0);
addEdge(nSteinerNodes, trg->n3->id, 0, 0, 0);
trg->usedEdges = 0;
nSteinerNodes++;
}
}
for(i = 0; i < nTriangles; i++)
{
trg = triangles + i;
-
if(trg->usedEdges & 1)
{
addEdge(trg->n1->id, trg->n2->id, 0, 0, 0);
if(trg->t1) trg->t1->usedEdges &= ~getEdgeMask(trg->t1, trg->n2->id);
}
-
if(trg->usedEdges & 2)
{
addEdge(trg->n2->id, trg->n3->id, 0, 0, 0);
if(trg->t2) trg->t2->usedEdges &= ~getEdgeMask(trg->t2, trg->n3->id);
}
-
if(trg->usedEdges & 4)
{
addEdge(trg->n3->id, trg->n1->id, 0, 0, 0);
if(trg->t3) trg->t3->usedEdges &= ~getEdgeMask(trg->t3, trg->n1->id);
}
}
}
void readData()
{
int i;
norm.minx = norm.miny = 100000.0;
norm.maxx = norm.maxy = 0.0;
for(i = 0; i < nNodes; i++)
{
scanf("%f %f", &nodes
[i
].
crd.
x, &nodes
[i
].
crd.
y);
if(nodes[i].crd.x < norm.minx)
norm.minx = nodes[i].crd.x;
if(nodes[i].crd.x > norm.maxx)
norm.maxx = nodes[i].crd.x;
if(nodes[i].crd.y < norm.miny)
norm.miny = nodes[i].crd.y;
if(nodes[i].crd.y > norm.maxy)
norm.maxy = nodes[i].crd.y;
nodes[i].id = i;
}
norm.maxx += 1;
norm.maxy += 1;
norm.minx -= 1;
norm.miny -= 1;
-
norm.divx = (norm.maxx - norm.minx);
norm.divy = (norm.maxy - norm.miny);
}
void mstCallback(PEDGE pEdge)
{
if(pEdge->trg1) pEdge->trg1->usedEdges |= getEdgeMask(pEdge->trg1, pEdge->n1);
if(pEdge->trg2) pEdge->trg2->usedEdges |= getEdgeMask(pEdge->trg2, pEdge->n2);
}
void writeData()
{
int i;
printf("%d\n", nSteinerNodes - nNodes
);
for(i = nNodes; i < nSteinerNodes; i++)
{
printf("%f %f\n", nodes
[i
].
crd.
x, nodes
[i
].
crd.
y);
}
for(i = 0 ; i < nEdges; i++)
{
printf("%d %d\n", edges
[i
].
n1, edges
[i
].
n2);
}
}
int main()
{
int t;
while(t--)
{
readData();
delaunayTriangulation();
extractEdges();
findMST(mstCallback);
findSMT();
writeData();
}
return 0;
}