Example #1
0
/*
 * The GiST PickSplit method
 *
 * New linear algorithm, see 'New Linear Node Splitting Algorithm for R-tree',
 * C.H.Ang and T.C.Tan
 *
 * This is used for both boxes and points.
 */
Datum
gist_box_picksplit(PG_FUNCTION_ARGS)
{
	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
	OffsetNumber i;
	OffsetNumber *listL,
			   *listR,
			   *listB,
			   *listT;
	BOX		   *unionL,
			   *unionR,
			   *unionB,
			   *unionT;
	int			posL,
				posR,
				posB,
				posT;
	BOX			pageunion;
	BOX		   *cur;
	char		direction = ' ';
	bool		allisequal = true;
	OffsetNumber maxoff;
	int			nbytes;

	posL = posR = posB = posT = 0;
	maxoff = entryvec->n - 1;

	cur = DatumGetBoxP(entryvec->vector[FirstOffsetNumber].key);
	memcpy((void *) &pageunion, (void *) cur, sizeof(BOX));

	/* find MBR */
	for (i = OffsetNumberNext(FirstOffsetNumber); i <= maxoff; i = OffsetNumberNext(i))
	{
		cur = DatumGetBoxP(entryvec->vector[i].key);
		if (allisequal && (
						   pageunion.high.x != cur->high.x ||
						   pageunion.high.y != cur->high.y ||
						   pageunion.low.x != cur->low.x ||
						   pageunion.low.y != cur->low.y
						   ))
			allisequal = false;

		adjustBox(&pageunion, cur);
	}

	if (allisequal)
	{
		/*
		 * All entries are the same
		 */
		fallbackSplit(entryvec, v);
		PG_RETURN_POINTER(v);
	}

	nbytes = (maxoff + 2) * sizeof(OffsetNumber);
	listL = (OffsetNumber *) palloc(nbytes);
	listR = (OffsetNumber *) palloc(nbytes);
	listB = (OffsetNumber *) palloc(nbytes);
	listT = (OffsetNumber *) palloc(nbytes);
	unionL = (BOX *) palloc(sizeof(BOX));
	unionR = (BOX *) palloc(sizeof(BOX));
	unionB = (BOX *) palloc(sizeof(BOX));
	unionT = (BOX *) palloc(sizeof(BOX));

#define ADDLIST( list, unionD, pos, num ) do { \
	if ( pos ) { \
		if ( (unionD)->high.x < cur->high.x ) (unionD)->high.x	= cur->high.x; \
		if ( (unionD)->low.x  > cur->low.x	) (unionD)->low.x	= cur->low.x; \
		if ( (unionD)->high.y < cur->high.y ) (unionD)->high.y	= cur->high.y; \
		if ( (unionD)->low.y  > cur->low.y	) (unionD)->low.y	= cur->low.y; \
	} else { \
			memcpy( (void*)(unionD), (void*) cur, sizeof( BOX ) );	\
	} \
	(list)[pos] = num; \
	(pos)++; \
} while(0)

	for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
	{
		cur = DatumGetBoxP(entryvec->vector[i].key);
		if (cur->low.x - pageunion.low.x < pageunion.high.x - cur->high.x)
			ADDLIST(listL, unionL, posL, i);
		else
			ADDLIST(listR, unionR, posR, i);
		if (cur->low.y - pageunion.low.y < pageunion.high.y - cur->high.y)
			ADDLIST(listB, unionB, posB, i);
		else
			ADDLIST(listT, unionT, posT, i);
	}

#define LIMIT_RATIO 0.1
#define _IS_BADRATIO(x,y)	( (y) == 0 || (float)(x)/(float)(y) < LIMIT_RATIO )
#define IS_BADRATIO(x,y) ( _IS_BADRATIO((x),(y)) || _IS_BADRATIO((y),(x)) )
	/* bad disposition, try to split by centers of boxes  */
	if (IS_BADRATIO(posR, posL) && IS_BADRATIO(posT, posB))
	{
		double		avgCenterX = 0.0,
					avgCenterY = 0.0;
		double		CenterX,
					CenterY;

		for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
		{
			cur = DatumGetBoxP(entryvec->vector[i].key);
			avgCenterX += ((double) cur->high.x + (double) cur->low.x) / 2.0;
			avgCenterY += ((double) cur->high.y + (double) cur->low.y) / 2.0;
		}

		avgCenterX /= maxoff;
		avgCenterY /= maxoff;

		posL = posR = posB = posT = 0;
		for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
		{
			cur = DatumGetBoxP(entryvec->vector[i].key);

			CenterX = ((double) cur->high.x + (double) cur->low.x) / 2.0;
			CenterY = ((double) cur->high.y + (double) cur->low.y) / 2.0;

			if (CenterX < avgCenterX)
				ADDLIST(listL, unionL, posL, i);
			else if (CenterX == avgCenterX)
			{
				if (posL > posR)
					ADDLIST(listR, unionR, posR, i);
				else
					ADDLIST(listL, unionL, posL, i);
			}
			else
				ADDLIST(listR, unionR, posR, i);

			if (CenterY < avgCenterY)
				ADDLIST(listB, unionB, posB, i);
			else if (CenterY == avgCenterY)
			{
				if (posB > posT)
					ADDLIST(listT, unionT, posT, i);
				else
					ADDLIST(listB, unionB, posB, i);
			}
			else
				ADDLIST(listT, unionT, posT, i);
		}

		if (IS_BADRATIO(posR, posL) && IS_BADRATIO(posT, posB))
		{
			fallbackSplit(entryvec, v);
			PG_RETURN_POINTER(v);
		}
	}

	/* which split more optimal? */
	if (Max(posL, posR) < Max(posB, posT))
		direction = 'x';
	else if (Max(posL, posR) > Max(posB, posT))
		direction = 'y';
	else
	{
		Datum		interLR = DirectFunctionCall2(rt_box_inter,
												  BoxPGetDatum(unionL),
												  BoxPGetDatum(unionR));
		Datum		interBT = DirectFunctionCall2(rt_box_inter,
												  BoxPGetDatum(unionB),
												  BoxPGetDatum(unionT));
		double		sizeLR,
					sizeBT;

		sizeLR = size_box(interLR);
		sizeBT = size_box(interBT);

		if (sizeLR < sizeBT)
			direction = 'x';
		else
			direction = 'y';
	}

	if (direction == 'x')
		chooseLR(v,
				 listL, posL, unionL,
				 listR, posR, unionR);
	else
		chooseLR(v,
				 listB, posB, unionB,
				 listT, posT, unionT);

	PG_RETURN_POINTER(v);
}
Example #2
0
/*
** The GiST PickSplit method
** New linear algorithm, see 'New Linear Node Splitting Algorithm for R-tree',
** C.H.Ang and T.C.Tan
*/
Datum
gbox_picksplit(PG_FUNCTION_ARGS)
{
	bytea	   *entryvec = (bytea *) PG_GETARG_POINTER(0);
	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
	OffsetNumber i;
	OffsetNumber *listL,
			   *listR,
			   *listB,
			   *listT;
	BOX		   *unionL,
			   *unionR,
			   *unionB,
			   *unionT;
	int			posL,
				posR,
				posB,
				posT;
	BOX			pageunion;
	BOX		   *cur;
	char		direction = ' ';
	bool		allisequal = true;
	OffsetNumber maxoff;
	int			nbytes;

	posL = posR = posB = posT = 0;
	maxoff = ((VARSIZE(entryvec) - VARHDRSZ) / sizeof(GISTENTRY)) - 1;

	cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[FirstOffsetNumber].key);
	memcpy((void *) &pageunion, (void *) cur, sizeof(BOX));

	/* find MBR */
	for (i = OffsetNumberNext(FirstOffsetNumber); i <= maxoff; i = OffsetNumberNext(i))
	{
		cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[i].key);
		if (allisequal == true && (
								   pageunion.high.x != cur->high.x ||
								   pageunion.high.y != cur->high.y ||
								   pageunion.low.x != cur->low.x ||
								   pageunion.low.y != cur->low.y
								   ))
			allisequal = false;

		if (pageunion.high.x < cur->high.x)
			pageunion.high.x = cur->high.x;
		if (pageunion.low.x > cur->low.x)
			pageunion.low.x = cur->low.x;
		if (pageunion.high.y < cur->high.y)
			pageunion.high.y = cur->high.y;
		if (pageunion.low.y > cur->low.y)
			pageunion.low.y = cur->low.y;
	}

	nbytes = (maxoff + 2) * sizeof(OffsetNumber);
	listL = (OffsetNumber *) palloc(nbytes);
	listR = (OffsetNumber *) palloc(nbytes);
	unionL = (BOX *) palloc(sizeof(BOX));
	unionR = (BOX *) palloc(sizeof(BOX));
	if (allisequal)
	{
		cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[OffsetNumberNext(FirstOffsetNumber)].key);
		if (memcmp((void *) cur, (void *) &pageunion, sizeof(BOX)) == 0)
		{
			v->spl_left = listL;
			v->spl_right = listR;
			v->spl_nleft = v->spl_nright = 0;
			memcpy((void *) unionL, (void *) &pageunion, sizeof(BOX));
			memcpy((void *) unionR, (void *) &pageunion, sizeof(BOX));

			for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
			{
				if (i <= (maxoff - FirstOffsetNumber + 1) / 2)
				{
					v->spl_left[v->spl_nleft] = i;
					v->spl_nleft++;
				}
				else
				{
					v->spl_right[v->spl_nright] = i;
					v->spl_nright++;
				}
			}
			v->spl_ldatum = BoxPGetDatum(unionL);
			v->spl_rdatum = BoxPGetDatum(unionR);

			PG_RETURN_POINTER(v);
		}
	}

	listB = (OffsetNumber *) palloc(nbytes);
	listT = (OffsetNumber *) palloc(nbytes);
	unionB = (BOX *) palloc(sizeof(BOX));
	unionT = (BOX *) palloc(sizeof(BOX));

#define ADDLIST( list, unionD, pos, num ) do { \
	if ( pos ) { \
		if ( unionD->high.x < cur->high.x ) unionD->high.x	= cur->high.x; \
		if ( unionD->low.x	> cur->low.x  ) unionD->low.x	= cur->low.x; \
		if ( unionD->high.y < cur->high.y ) unionD->high.y	= cur->high.y; \
		if ( unionD->low.y	> cur->low.y  ) unionD->low.y	= cur->low.y; \
	} else { \
			memcpy( (void*)unionD, (void*) cur, sizeof( BOX ) );  \
	} \
	list[pos] = num; \
	(pos)++; \
} while(0)

	for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
	{
		cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[i].key);
		if (cur->low.x - pageunion.low.x < pageunion.high.x - cur->high.x)
			ADDLIST(listL, unionL, posL, i);
		else
			ADDLIST(listR, unionR, posR, i);
		if (cur->low.y - pageunion.low.y < pageunion.high.y - cur->high.y)
			ADDLIST(listB, unionB, posB, i);
		else
			ADDLIST(listT, unionT, posT, i);
	}

	/* bad disposition, sort by ascending and resplit */
	if ((posR == 0 || posL == 0) && (posT == 0 || posB == 0))
	{
		KBsort	   *arr = (KBsort *) palloc(sizeof(KBsort) * maxoff);

		posL = posR = posB = posT = 0;
		for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
		{
			arr[i - 1].key = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[i].key);
			arr[i - 1].pos = i;
		}
		qsort(arr, maxoff, sizeof(KBsort), compare_KB);
		for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
		{
			cur = arr[i - 1].key;
			if (cur->low.x - pageunion.low.x < pageunion.high.x - cur->high.x)
				ADDLIST(listL, unionL, posL, arr[i - 1].pos);
			else if (cur->low.x - pageunion.low.x == pageunion.high.x - cur->high.x)
			{
				if (posL > posR)
					ADDLIST(listR, unionR, posR, arr[i - 1].pos);
				else
					ADDLIST(listL, unionL, posL, arr[i - 1].pos);
			}
			else
				ADDLIST(listR, unionR, posR, arr[i - 1].pos);

			if (cur->low.y - pageunion.low.y < pageunion.high.y - cur->high.y)
				ADDLIST(listB, unionB, posB, arr[i - 1].pos);
			else if (cur->low.y - pageunion.low.y == pageunion.high.y - cur->high.y)
			{
				if (posB > posT)
					ADDLIST(listT, unionT, posT, arr[i - 1].pos);
				else
					ADDLIST(listB, unionB, posB, arr[i - 1].pos);
			}
			else
				ADDLIST(listT, unionT, posT, arr[i - 1].pos);
		}
		pfree(arr);
	}

	/* which split more optimal? */
	if (Max(posL, posR) < Max(posB, posT))
		direction = 'x';
	else if (Max(posL, posR) > Max(posB, posT))
		direction = 'y';
	else
	{
		Datum		interLR = DirectFunctionCall2(rt_box_inter,
												  BoxPGetDatum(unionL),
												  BoxPGetDatum(unionR));
		Datum		interBT = DirectFunctionCall2(rt_box_inter,
												  BoxPGetDatum(unionB),
												  BoxPGetDatum(unionT));
		float		sizeLR,
					sizeBT;

		sizeLR = size_box(interLR);
		sizeBT = size_box(interBT);

		if (sizeLR < sizeBT)
			direction = 'x';
		else
			direction = 'y';
	}

	if (direction == 'x')
	{
		pfree(unionB);
		pfree(listB);
		pfree(unionT);
		pfree(listT);

		v->spl_left = listL;
		v->spl_right = listR;
		v->spl_nleft = posL;
		v->spl_nright = posR;
		v->spl_ldatum = BoxPGetDatum(unionL);
		v->spl_rdatum = BoxPGetDatum(unionR);
	}
	else
	{
		pfree(unionR);
		pfree(listR);
		pfree(unionL);
		pfree(listL);

		v->spl_left = listB;
		v->spl_right = listT;
		v->spl_nleft = posB;
		v->spl_nright = posT;
		v->spl_ldatum = BoxPGetDatum(unionB);
		v->spl_rdatum = BoxPGetDatum(unionT);
	}

	PG_RETURN_POINTER(v);
}
Example #3
0
/*
 * The GiST PickSplit method
 *
 * New linear algorithm, see 'New Linear node_n Splitting Algorithm for R-tree',
 * C.H.Ang and T.C.Tan
 *
 * This is used for both boxes and points.
 */
datum_t gist_box_picksplit(PG_FUNC_ARGS)
{
	struct gist_entry_vector *entryvec = (struct gist_entry_vector *)ARG_POINTER(0);
	struct gist_splitvec *v = (struct gist_splitvec *)ARG_POINTER(1);
	item_id_t i;
	item_id_t *listL;
	item_id_t *listR;
	item_id_t *listB;
	item_id_t *listT;
	BOX *unionL;
	BOX *unionR;
	BOX *unionB;
	BOX *unionT;
	int posL, posR, posB, posT;
	BOX pageunion;
	BOX *cur;
	char direction = ' ';
	bool allisequal = true;
	item_id_t maxoff;
	int nbytes;

	posL = posR = posB = posT = 0;
	maxoff = entryvec->n - 1;

	cur = D_TO_BOX_P(entryvec->vector[FIRST_ITEM_ID].key);
	memcpy((void*) &pageunion, (void*) cur, sizeof(BOX));

	/* find MBR */
	for (i = ITEM_ID_NEXT(FIRST_ITEM_ID); i <= maxoff; i = ITEM_ID_NEXT(i)) {
		cur = D_TO_BOX_P(entryvec->vector[i].key);
		if (allisequal
			&& (pageunion.high.x != cur->high.x
				|| pageunion.high.y != cur->high.y
				|| pageunion.low.x != cur->low.x
				|| pageunion.low.y != cur->low.y))
			allisequal = false;

		adjustBox(&pageunion, cur);
	}

	if (allisequal) {
		/*
		 * All entries are the same
		 */
		fallbackSplit(entryvec, v);
		RET_POINTER(v);
	}

	nbytes = (maxoff + 2) * sizeof(item_id_t);
	listL = (item_id_t *) palloc(nbytes);
	listR = (item_id_t *) palloc(nbytes);
	listB = (item_id_t *) palloc(nbytes);
	listT = (item_id_t *) palloc(nbytes);
	unionL = (BOX *) palloc(sizeof(BOX));
	unionR = (BOX *) palloc(sizeof(BOX));
	unionB = (BOX *) palloc(sizeof(BOX));
	unionT = (BOX *) palloc(sizeof(BOX));

#define ADDLIST( list, unionD, pos, num ) do { \
	if ( pos ) { \
		if ( (unionD)->high.x < cur->high.x ) (unionD)->high.x	= cur->high.x; \
		if ( (unionD)->low.x  > cur->low.x	) (unionD)->low.x	= cur->low.x; \
		if ( (unionD)->high.y < cur->high.y ) (unionD)->high.y	= cur->high.y; \
		if ( (unionD)->low.y  > cur->low.y	) (unionD)->low.y	= cur->low.y; \
	} else { \
			memcpy( (void*)(unionD), (void*) cur, sizeof( BOX ) );	\
	} \
		\
	(list)[pos] = num; \
	(pos)++; \
} while(0)

	for (i = FIRST_ITEM_ID; i <= maxoff; i = ITEM_ID_NEXT(i)) {
		cur = D_TO_BOX_P(entryvec->vector[i].key);
		if (cur->low.x - pageunion.low.x < pageunion.high.x - cur->high.x)
			ADDLIST(listL, unionL, posL, i);
		else
			ADDLIST(listR, unionR, posR, i);

		if (cur->low.y - pageunion.low.y < pageunion.high.y - cur->high.y)
			ADDLIST(listB, unionB, posB, i);
		else
			ADDLIST(listT, unionT, posT, i);
	}

#define LIMIT_RATIO 0.1
#define _IS_BADRATIO(x,y)	( (y) == 0 || (float)(x)/(float)(y) < LIMIT_RATIO )
#define IS_BADRATIO(x,y) 	( _IS_BADRATIO((x),(y)) || _IS_BADRATIO((y),(x)) )

	/* bad disposition, try to split by centers of boxes  */
	if (IS_BADRATIO(posR, posL) && IS_BADRATIO(posT, posB)) {
		double avgCenterX = 0.0;
		double avgCenterY = 0.0;
		double CenterX;
		double CenterY;

		for (i = FIRST_ITEM_ID; i <= maxoff; i = ITEM_ID_NEXT(i)) {
			cur = D_TO_BOX_P(entryvec->vector[i].key);
			avgCenterX += ((double)cur->high.x + (double)cur->low.x) / 2.0;
			avgCenterY += ((double)cur->high.y + (double)cur->low.y) / 2.0;
		}

		avgCenterX /= maxoff;
		avgCenterY /= maxoff;

		posL = posR = posB = posT = 0;
		for (i = FIRST_ITEM_ID; i <= maxoff; i = ITEM_ID_NEXT(i)) {
			cur = D_TO_BOX_P(entryvec->vector[i].key);

			CenterX = ((double)cur->high.x + (double)cur->low.x) / 2.0;
			CenterY = ((double)cur->high.y + (double)cur->low.y) / 2.0;

			if (CenterX < avgCenterX)
				ADDLIST(listL, unionL, posL, i);
			else if (CenterX == avgCenterX) {
				if (posL > posR)
					ADDLIST(listR, unionR, posR, i);
				else
					ADDLIST(listL, unionL, posL, i);
			} else
				ADDLIST(listR, unionR, posR, i);

			if (CenterY < avgCenterY)
				ADDLIST(listB, unionB, posB, i);
			else if (CenterY == avgCenterY) {
				if (posB > posT)
					ADDLIST(listT, unionT, posT, i);
				else
					ADDLIST(listB, unionB, posB, i);
			} else
				ADDLIST(listT, unionT, posT, i);
		}

		if (IS_BADRATIO(posR, posL) && IS_BADRATIO(posT, posB)) {
			fallbackSplit(entryvec, v);
			RET_POINTER(v);
		}
	}

	/* which split more optimal? */
	if (Max(posL, posR) < Max(posB, posT))
		direction = 'x';
	else if (Max(posL, posR) > Max(posB, posT))
		direction = 'y';
	else {
		datum_t interLR = DIRECT_FC2(rt_box_inter, BOX_P_TO_D(unionL), BOX_P_TO_D(unionR));
		datum_t interBT = DIRECT_FC2(rt_box_inter, BOX_P_TO_D(unionB), BOX_P_TO_D(unionT));
		double sizeLR;
		double sizeBT;

		sizeLR = size_box(interLR);
		sizeBT = size_box(interBT);
		if (sizeLR < sizeBT)
			direction = 'x';
		else
			direction = 'y';
	}

	if (direction == 'x')
		chooseLR(v, listL, posL, unionL, listR, posR, unionR);
	else
		chooseLR(v, listB, posB, unionB, listT, posT, unionT);

	RET_POINTER(v);
}
Datum gserialized_gist_picksplit_2d(PG_FUNCTION_ARGS)
{
	GistEntryVector	*entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);

	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
	OffsetNumber i;
	OffsetNumber *listL, *listR, *listB, *listT;
	BOX2DF *unionL, *unionR, *unionB, *unionT;
	int posL, posR, posB, posT;
	BOX2DF pageunion;
	BOX2DF *cur;
	char direction = ' ';
	bool allisequal = true;
	OffsetNumber maxoff;
	int nbytes;

	POSTGIS_DEBUG(3, "[GIST] 'picksplit' entered");

	posL = posR = posB = posT = 0;

	maxoff = entryvec->n - 1;
	cur = (BOX2DF*) DatumGetPointer(entryvec->vector[FirstOffsetNumber].key);

	memcpy((void *) &pageunion, (void *) cur, sizeof(BOX2DF));

	/* find MBR */
	for (i = OffsetNumberNext(FirstOffsetNumber); i <= maxoff; i = OffsetNumberNext(i))
	{
		cur = (BOX2DF *) DatumGetPointer(entryvec->vector[i].key);

		if ( allisequal == true &&  (
		            pageunion.xmax != cur->xmax ||
		            pageunion.ymax != cur->ymax ||
		            pageunion.xmin != cur->xmin ||
		            pageunion.ymin != cur->ymin
		        ) )
			allisequal = false;

		if (pageunion.xmax < cur->xmax)
			pageunion.xmax = cur->xmax;
		if (pageunion.xmin > cur->xmin)
			pageunion.xmin = cur->xmin;
		if (pageunion.ymax < cur->ymax)
			pageunion.ymax = cur->ymax;
		if (pageunion.ymin > cur->ymin)
			pageunion.ymin = cur->ymin;
	}

	POSTGIS_DEBUGF(4, "pageunion is %s", box2df_to_string(&pageunion));

	nbytes = (maxoff + 2) * sizeof(OffsetNumber);
	listL = (OffsetNumber *) palloc(nbytes);
	listR = (OffsetNumber *) palloc(nbytes);
	unionL = (BOX2DF *) palloc(sizeof(BOX2DF));
	unionR = (BOX2DF *) palloc(sizeof(BOX2DF));

	if (allisequal)
	{
		POSTGIS_DEBUG(4, " AllIsEqual!");

		cur = (BOX2DF*) DatumGetPointer(entryvec->vector[OffsetNumberNext(FirstOffsetNumber)].key);


		if (memcmp((void *) cur, (void *) &pageunion, sizeof(BOX2DF)) == 0)
		{
			v->spl_left = listL;
			v->spl_right = listR;
			v->spl_nleft = v->spl_nright = 0;
			memcpy((void *) unionL, (void *) &pageunion, sizeof(BOX2DF));
			memcpy((void *) unionR, (void *) &pageunion, sizeof(BOX2DF));

			for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
			{
				if (i <= (maxoff - FirstOffsetNumber + 1) / 2)
				{
					v->spl_left[v->spl_nleft] = i;
					v->spl_nleft++;
				}
				else
				{
					v->spl_right[v->spl_nright] = i;
					v->spl_nright++;
				}
			}
			v->spl_ldatum = PointerGetDatum(unionL);
			v->spl_rdatum = PointerGetDatum(unionR);

			PG_RETURN_POINTER(v);
		}
	}

	listB = (OffsetNumber *) palloc(nbytes);
	listT = (OffsetNumber *) palloc(nbytes);
	unionB = (BOX2DF *) palloc(sizeof(BOX2DF));
	unionT = (BOX2DF *) palloc(sizeof(BOX2DF));

#define ADDLIST( list, unionD, pos, num ) do { \
	if ( pos ) { \
		if ( unionD->xmax < cur->xmax )    unionD->xmax	= cur->xmax; \
		if ( unionD->xmin	> cur->xmin  ) unionD->xmin	= cur->xmin; \
		if ( unionD->ymax < cur->ymax )    unionD->ymax	= cur->ymax; \
		if ( unionD->ymin	> cur->ymin  ) unionD->ymin	= cur->ymin; \
	} else { \
			memcpy( (void*)unionD, (void*) cur, sizeof( BOX2DF ) );  \
	} \
	list[pos] = num; \
	(pos)++; \
} while(0)

	for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
	{
		cur = (BOX2DF*) DatumGetPointer(entryvec->vector[i].key);

		if (cur->xmin - pageunion.xmin < pageunion.xmax - cur->xmax)
			ADDLIST(listL, unionL, posL,i);
		else
			ADDLIST(listR, unionR, posR,i);
		if (cur->ymin - pageunion.ymin < pageunion.ymax - cur->ymax)
			ADDLIST(listB, unionB, posB,i);
		else
			ADDLIST(listT, unionT, posT,i);
	}

	POSTGIS_DEBUGF(4, "unionL is %s", box2df_to_string(unionL));
	POSTGIS_DEBUGF(4, "unionR is %s", box2df_to_string(unionR));
	POSTGIS_DEBUGF(4, "unionT is %s", box2df_to_string(unionT));
	POSTGIS_DEBUGF(4, "unionB is %s", box2df_to_string(unionB));

	/* bad disposition, sort by ascending and resplit */
	if ( (posR==0 || posL==0) && (posT==0 || posB==0) )
	{
		KBsort *arr = (KBsort*)palloc( sizeof(KBsort) * maxoff );
		posL = posR = posB = posT = 0;
		for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
		{
			arr[i-1].key = (BOX2DF*) DatumGetPointer(entryvec->vector[i].key);
			arr[i-1].pos = i;
		}
		qsort( arr, maxoff, sizeof(KBsort), compare_KB );
		for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
		{
			cur = arr[i-1].key;
			if (cur->xmin - pageunion.xmin < pageunion.xmax - cur->xmax)
				ADDLIST(listL, unionL, posL,arr[i-1].pos);
			else if ( cur->xmin - pageunion.xmin == pageunion.xmax - cur->xmax )
			{
				if ( posL>posR )
					ADDLIST(listR, unionR, posR,arr[i-1].pos);
				else
					ADDLIST(listL, unionL, posL,arr[i-1].pos);
			}
			else
				ADDLIST(listR, unionR, posR,arr[i-1].pos);

			if (cur->ymin - pageunion.ymin < pageunion.ymax - cur->ymax)
				ADDLIST(listB, unionB, posB,arr[i-1].pos);
			else if ( cur->ymin - pageunion.ymin == pageunion.ymax - cur->ymax )
			{
				if ( posB>posT )
					ADDLIST(listT, unionT, posT,arr[i-1].pos);
				else
					ADDLIST(listB, unionB, posB,arr[i-1].pos);
			}
			else
				ADDLIST(listT, unionT, posT,arr[i-1].pos);
		}
		pfree(arr);
	}

	/* which split more optimal? */
	if (Max(posL, posR) < Max(posB, posT))
		direction = 'x';
	else if (Max(posL, posR) > Max(posB, posT))
		direction = 'y';
	else
	{
		float sizeLR, sizeBT;
		BOX2DF interLR, interBT;
		
		if ( box2df_intersection(unionL, unionR, &interLR) == FALSE )
			sizeLR = 0.0;
		else
			sizeLR = box2df_size(&interLR);

		if ( box2df_intersection(unionB, unionT, &interBT) == FALSE )
			sizeBT = 0.0;
		else
			sizeBT = box2df_size(&interBT);
		
		if (sizeLR < sizeBT)
			direction = 'x';
		else
			direction = 'y';
	}

	POSTGIS_DEBUGF(4, "split direction '%c'", direction);

	if (direction == 'x')
	{
		pfree(unionB);
		pfree(listB);
		pfree(unionT);
		pfree(listT);

		v->spl_left = listL;
		v->spl_right = listR;
		v->spl_nleft = posL;
		v->spl_nright = posR;
		v->spl_ldatum = PointerGetDatum(unionL);
		v->spl_rdatum = PointerGetDatum(unionR);
	}
	else
	{
		pfree(unionR);
		pfree(listR);
		pfree(unionL);
		pfree(listL);

		v->spl_left = listB;
		v->spl_right = listT;
		v->spl_nleft = posB;
		v->spl_nright = posT;
		v->spl_ldatum = PointerGetDatum(unionB);
		v->spl_rdatum = PointerGetDatum(unionT);
	}
	
	POSTGIS_DEBUG(4, "[GIST] 'picksplit' completed");
	
	PG_RETURN_POINTER(v);
}