/* * SP-GiST choose function */ Datum spg_box_quad_choose(PG_FUNCTION_ARGS) { spgChooseIn *in = (spgChooseIn *) PG_GETARG_POINTER(0); spgChooseOut *out = (spgChooseOut *) PG_GETARG_POINTER(1); BOX *centroid = DatumGetBoxP(in->prefixDatum), *box = DatumGetBoxP(in->datum); out->resultType = spgMatchNode; out->result.matchNode.restDatum = BoxPGetDatum(box); /* nodeN will be set by core, when allTheSame. */ if (!in->allTheSame) out->result.matchNode.nodeN = getQuadrant(centroid, box); PG_RETURN_VOID(); }
SegmentPoint* GenericEllipse::makeSegmentPoint( const Point2D& p, const GenericShapeElement* parent2) const { float t2; if (parent2->containsPoint(p, t2)) { const GenericArc* parent = getQuadrant(p); return new SegmentPoint(p, parent->getT(p), parent, t2, parent2); } else { parent2->containsPoint(p, t2); throw error::ConsistencyError( "GenericEllipse::makeSegmentPoint: Point is not part of parent2"); } }
void mapQuadTreeAbstraction::addNodes(graph *g) { node_iterator ni = abstractions.back()->getNodeIter(); for (node *next = abstractions.back()->nodeIterNext(ni); next; next = abstractions.back()->nodeIterNext(ni)) { // if it isn't abstracted, do a bfs according to the quadrant and abstract these nodes together if (next->getLabelL(kParent) == -1) { node *parent; g->addNode(parent = new node("??")); parent->setLabelL(kAbstractionLevel, next->getLabelL(kAbstractionLevel)+1); // level in abstraction tree parent->setLabelL(kNumAbstractedNodes, 0); // number of abstracted nodes parent->setLabelL(kParent, -1); // parent of this node in abstraction hierarchy parent->setLabelF(kXCoordinate, kUnknownPosition); parent->setLabelL(kNodeBlocked, 0); abstractionBFS(next, parent, getQuadrant(next)); } } }
int main () { double x, y; int quadrant; char againResponse; int exitApp = 0; while (exitApp == 0) { printf("Enter X coordinate: "); scanf("%lf", &x); printf("Enter Y coordinate: "); scanf("%lf", &y); quadrant = getQuadrant(x, y); if (quadrant == -1) { printf("The point (%lf, %lf) lies on the x-axis\n", x, y); } else if (quadrant == -2) { printf("The point (%lf, %lf) lies on the y-axis\n", x, y); } else if (quadrant == -3) { printf("The point (%lf, %lf) is at the origin\n", x, y); } else { printf("Coordinate (%lf, %lf) is in quadrant %d\n", x, y, quadrant); } printf("Enter another coordinate? (y/n): "); scanf(" %c", &againResponse); if (againResponse == 'n') { printf("Goodbye\n"); exitApp = 1; } else if (againResponse != 'y') { printf("Response not reognised. Exiting application.\n"); exitApp = 1; } } return 0; }
Datum spg_quad_inner_consistent(PG_FUNCTION_ARGS) { spgInnerConsistentIn *in = (spgInnerConsistentIn*)PG_GETARG_POINTER(0); spgInnerConsistentOut *out = (spgInnerConsistentOut*)PG_GETARG_POINTER(1); Point *query, *centroid; query = DatumGetPointP(in->query); Assert(in->hasPrefix); centroid = DatumGetPointP(in->prefixDatum); out->levelAdd = 0; out->nodeNumbers = palloc(sizeof(int)); out->nNodes = 1; out->nodeNumbers[0] = getQuadrant(centroid, query) - 1; PG_RETURN_VOID(); }
NameTableTile Background::getTile(int x, int y, int nametable) { // Get true X,Y if (mirrorPositions[mirrorType][nametable][0]) { x += 32; } if (mirrorPositions[mirrorType][nametable][1]) { y += 30; } if (x > 64) int test = 0; // If this new X,Y is out of bounds, make it wrap around back from the top-left x %= mirrorSizes[mirrorType][0]; y %= mirrorSizes[mirrorType][1]; // Find which quadrant this ultimately falls into int quadrant = getQuadrant(x, y); // Reset X&Y to local quadrant coordinates x %= 32; y %= 30; return quadrants[mirrorLayouts[mirrorType][quadrant]].tiles[(y * 32) + x]; }
Datum spg_quad_inner_consistent(PG_FUNCTION_ARGS) { spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0); spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1); Point *query, *centroid; BOX *boxQuery; query = DatumGetPointP(in->query); Assert(in->hasPrefix); centroid = DatumGetPointP(in->prefixDatum); if (in->allTheSame) { /* Report that all nodes should be visited */ int i; out->nNodes = in->nNodes; out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes); for (i = 0; i < in->nNodes; i++) out->nodeNumbers[i] = i; PG_RETURN_VOID(); } Assert(in->nNodes == 4); out->nodeNumbers = (int *) palloc(sizeof(int) * 4); switch (in->strategy) { case RTLeftStrategyNumber: setNodes(out, SPTEST(point_left, centroid, query), 3, 4); break; case RTRightStrategyNumber: setNodes(out, SPTEST(point_right, centroid, query), 1, 2); break; case RTSameStrategyNumber: out->nNodes = 1; out->nodeNumbers[0] = getQuadrant(centroid, query) - 1; break; case RTBelowStrategyNumber: setNodes(out, SPTEST(point_below, centroid, query), 2, 3); break; case RTAboveStrategyNumber: setNodes(out, SPTEST(point_above, centroid, query), 1, 4); break; case RTContainedByStrategyNumber: /* * For this operator, the query is a box not a point. We cheat to * the extent of assuming that DatumGetPointP won't do anything * that would be bad for a pointer-to-box. */ boxQuery = DatumGetBoxP(in->query); if (DatumGetBool(DirectFunctionCall2(box_contain_pt, PointerGetDatum(boxQuery), PointerGetDatum(centroid)))) { /* centroid is in box, so descend to all quadrants */ setNodes(out, true, 0, 0); } else { /* identify quadrant(s) containing all corners of box */ Point p; int i, r = 0; p = boxQuery->low; r |= 1 << (getQuadrant(centroid, &p) - 1); p.y = boxQuery->high.y; r |= 1 << (getQuadrant(centroid, &p) - 1); p = boxQuery->high; r |= 1 << (getQuadrant(centroid, &p) - 1); p.x = boxQuery->low.x; r |= 1 << (getQuadrant(centroid, &p) - 1); /* we must descend into those quadrant(s) */ out->nNodes = 0; for (i = 0; i < 4; i++) { if (r & (1 << i)) { out->nodeNumbers[out->nNodes] = i; out->nNodes++; } } } break; default: elog(ERROR, "unrecognized strategy number: %d", in->strategy); break; } PG_RETURN_VOID(); }
Datum spgist_geom_inner_consistent(PG_FUNCTION_ARGS) { spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0); spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1); BOX2DF *centroidbox_p; int which; int i; if (in->allTheSame) { /* Report that all nodes should be visited */ out->nNodes = in->nNodes; out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes); for (i = 0; i < in->nNodes; i++) out->nodeNumbers[i] = i; PG_RETURN_VOID(); } Assert(in->hasPrefix); centroidbox_p = (BOX2DF *)in->prefixDatum; Assert(in->nNodes == 4); /* "which" is a bitmask of quadrants that satisfy all constraints */ which = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4); for (i = 0; i < in->nkeys; i++) { BOX2DF box; BOX2DF *box_p = &box; gserialized_datum_get_box2df_p(in->scankeys[i].sk_argument, box_p); switch (in->scankeys[i].sk_strategy) { case RTLeftStrategyNumber: if (box2df_left(box_p, centroidbox_p)) which &= (1 << 3) | (1 << 4); break; case RTRightStrategyNumber: if (box2df_right(box_p, centroidbox_p)) which &= (1 << 1) | (1 << 2); break; /* case RTSameStrategyNumber: */ /* which &= (1 << getQuadrant(centroidbox_p, box_p)); */ /* break; */ case RTBelowStrategyNumber: if (box2df_below(centroidbox_p, box_p)) which &= (1 << 2) | (1 << 3); break; case RTAboveStrategyNumber: if (box2df_above(centroidbox_p, box_p)) which &= (1 << 1) | (1 << 4); break; case RTContainedByStrategyNumber: if (box2df_contains(box_p, centroidbox_p)) { /* centroid is in box, so all quadrants are OK */ } else { /* identify quadrant(s) containing all corners of box */ POINT2D p; BOX2DF b; BOX2DF *b_p = &b; int r = 0; b_p->xmin = b_p->xmax = box_p->xmin; b_p->ymin = b_p->ymax = box_p->ymin; r |= 1 << getQuadrant(centroidbox_p, b_p); b_p->xmin = b_p->xmax = box_p->xmax; b_p->ymin = b_p->ymax = box_p->ymin; r |= 1 << getQuadrant(centroidbox_p, b_p); b_p->xmin = b_p->xmax = box_p->xmax; b_p->ymin = b_p->ymax = box_p->ymax; r |= 1 << getQuadrant(centroidbox_p, b_p); b_p->xmin = b_p->xmax = box_p->xmin; b_p->ymin = b_p->ymax = box_p->ymax; r |= 1 << getQuadrant(centroidbox_p, b_p); which &= r; } break; default: elog(ERROR, "unrecognized strategy number: %d", in->scankeys[i].sk_strategy); break; } if (which == 0) break; /* no need to consider remaining conditions */ } /* We must descend into the quadrant(s) identified by which */ out->nodeNumbers = (int *) palloc(sizeof(int) * 4); out->nNodes = 0; for (i = 1; i <= 4; i++) { if (which & (1 << i)) out->nodeNumbers[out->nNodes++] = i - 1; } PG_RETURN_VOID(); }
/* * SP-GiST pick-split function * * It splits a list of boxes into quadrants by choosing a central 4D * point as the median of the coordinates of the boxes. */ Datum spg_box_quad_picksplit(PG_FUNCTION_ARGS) { spgPickSplitIn *in = (spgPickSplitIn *) PG_GETARG_POINTER(0); spgPickSplitOut *out = (spgPickSplitOut *) PG_GETARG_POINTER(1); BOX *centroid; int median, i; double *lowXs = palloc(sizeof(double) * in->nTuples); double *highXs = palloc(sizeof(double) * in->nTuples); double *lowYs = palloc(sizeof(double) * in->nTuples); double *highYs = palloc(sizeof(double) * in->nTuples); /* Calculate median of all 4D coordinates */ for (i = 0; i < in->nTuples; i++) { BOX *box = DatumGetBoxP(in->datums[i]); lowXs[i] = box->low.x; highXs[i] = box->high.x; lowYs[i] = box->low.y; highYs[i] = box->high.y; } qsort(lowXs, in->nTuples, sizeof(double), compareDoubles); qsort(highXs, in->nTuples, sizeof(double), compareDoubles); qsort(lowYs, in->nTuples, sizeof(double), compareDoubles); qsort(highYs, in->nTuples, sizeof(double), compareDoubles); median = in->nTuples / 2; centroid = palloc(sizeof(BOX)); centroid->low.x = lowXs[median]; centroid->high.x = highXs[median]; centroid->low.y = lowYs[median]; centroid->high.y = highYs[median]; /* Fill the output */ out->hasPrefix = true; out->prefixDatum = BoxPGetDatum(centroid); out->nNodes = 16; out->nodeLabels = NULL; /* We don't need node labels. */ out->mapTuplesToNodes = palloc(sizeof(int) * in->nTuples); out->leafTupleDatums = palloc(sizeof(Datum) * in->nTuples); /* * Assign ranges to corresponding nodes according to quadrants * relative to the "centroid" range */ for (i = 0; i < in->nTuples; i++) { BOX *box = DatumGetBoxP(in->datums[i]); uint8 quadrant = getQuadrant(centroid, box); out->leafTupleDatums[i] = BoxPGetDatum(box); out->mapTuplesToNodes[i] = quadrant; } PG_RETURN_VOID(); }
void setStencil(Geometry *geometry, int mu, int k, enum direction wind) { register int l, m, n; int l0, m0, k0, countXZ, countYZ, count, Nvalid; bool_t *valid; double rx, ry, rz, frac1, frac2, muz, dz, longfracx, longfracy, svalid; Stencil *st; Longchar *lc; Intersect *vis; /* --- Fill interpolation stencil: Determine largest grid coordinate with x1_grid < x1_intersect and x2_grid < x2_intersect, then fill stencil with addresses of interpolation points surrounding the point of intersection. -- -------------- */ if (wind == UPWIND) { st = &geometry->stencil_uw[mu][k]; dz = geometry->z[k - 1] - geometry->z[k]; } else { st = &geometry->stencil_dw[mu][k]; dz = geometry->z[k] - geometry->z[k + 1]; } muz = sqrt(1.0 - (SQ(geometry->mux[mu]) + SQ(geometry->muy[mu]))); rx = geometry->mux[mu] / geometry->dx; ry = geometry->muy[mu] / geometry->dy; rz = muz / dz; /* --- Calculate quadrant ray point to -- -------------- */ st->quadrant = getQuadrant(geometry->mux[mu], geometry->muy[mu], wind); /* --- Determine which is the nearest plane in direction of ray - - */ st->plane = getIntersect(rx, ry, rz); /* --- Determine fractions and distance between gridpoint and point where ray intersects nearest plane -- -------------- */ calcFrac(&frac1, &frac2, rx, ry, rz, st->plane, wind); st->ds = calcDistance(frac1, frac2, st->plane, geometry->dx, geometry->dy, dz); switch (st->plane){ case XY: st->zbase[0] = (wind == UPWIND) ? k - 1 : k + 1; l0 = floor(frac1); frac1 -= l0; m0 = floor(frac2); frac2 -= m0; switch (input.interpolate_3D) { case LINEAR_3D: st->xbase[0] = l0; st->xbase[1] = l0 + 1; st->xkernel[0] = 1.0 - frac1; st->xkernel[1] = frac1; st->ybase[0] = m0; st->ybase[1] = m0 + 1; st->ykernel[0] = 1.0 - frac2; st->ykernel[1] = frac2; break; case BICUBIC_3D: for (l = 0; l < NCC; l++) st->xbase[l] = l0 - 1 + l; cc_kernel(frac1, st->xkernel); for (m = 0; m < NCC; m++) st->ybase[m] = m0 - 1 + m; cc_kernel(frac2, st->ykernel); } break; case XZ: st->ybase[0] = (st->quadrant == 1 || st->quadrant == 2) ? 1 : -1; l0 = floor(frac1); frac1 -= l0; if (frac2 < 0.0) { k0 = k + 1; frac2 += 1.0; } else k0 = k; st->xbase[0] = l0; st->xbase[1] = l0 + 1; st->xkernel[0] = 1.0 - frac1; st->xkernel[1] = frac1; st->zbase[0] = k0; st->zbase[1] = k0 - 1; st->zkernel[0] = 1.0 - frac2; st->zkernel[1] = frac2; break; case YZ: st->xbase[0] = (st->quadrant == 1 || st->quadrant == 4) ? 1 : -1; m0 = floor(frac1); frac1 -= m0; if (frac2 < 0.0) { k0 = k + 1; frac2 += 1.0; } else k0 = k; st->ybase[0] = m0; st->ybase[1] = m0 + 1; st->ykernel[0] = 1.0 - frac1; st->ykernel[1] = frac1; st->zbase[0] = k0; st->zbase[1] = k0 - 1; st->zkernel[0] = 1.0 - frac2; st->zkernel[1] = frac2; } st->longchar = NULL; /* --- If XZ or YZ plane is hit first, fill long characteristic structures -- -------------- */ if (st->plane == YZ || st->plane == XZ) { if (wind == UPWIND) { longfracx = rx / rz; longfracy = ry / rz; } else { longfracx = -rx / rz; longfracy = -ry / rz; } /* --- Determine number of crossings of YZ and XZ planes, respectively, before XY plane is crossed -- -------------- */ countYZ = (int) floor(fabs(longfracx)); countXZ = (int) floor(fabs(longfracy)); /* --- Tabulate the vertical intersects -- -------------- */ vis = (Intersect *) malloc((countYZ + countXZ) * sizeof(Intersect)); count = 0; for (l = 0; l < countYZ; l++) { vis[count].plane = YZ; vis[count].s = ((l+1) * geometry->dx) / fabs(geometry->mux[mu]); vis[count].x = geometry->mux[mu] * vis[count].s; vis[count].y = geometry->muy[mu] * vis[count].s; vis[count].z = muz * vis[count].s; count++; } for (m = 0; m < countXZ; m++) { vis[count].plane = XZ; vis[count].s = ((m+1) * geometry->dy) / fabs(geometry->muy[mu]); vis[count].x = geometry->mux[mu] * vis[count].s; vis[count].y = geometry->muy[mu] * vis[count].s; vis[count].z = muz * vis[count].s; count++; } /* --- Sort the intersections according to distance s from the point of origin -- -------------- */ qsort(vis, count, sizeof(Intersect), sascend); /* --- Determine if any of the intersections appear too close to one another, for instance when they occur on an intersection between YZ and XZ planes -- -------------- */ Nvalid = count; valid = (bool_t *) malloc(Nvalid * sizeof(bool_t)); valid[0] = TRUE; for (n = 1; n < count; n++) { if ((vis[n].s - vis[n-1].s) < MIN_FRAC * MIN(geometry->dx, geometry->dy)) { valid[n] = FALSE; Nvalid--; } else valid[n] = TRUE; } st->longchar = (Longchar *) malloc(sizeof(Longchar)); lc = st->longchar; lc->Nst = Nvalid + 1; lc->stencil = (Stencil *) malloc(lc->Nst * sizeof(Stencil)); /* --- The first stencil to be stored is the intersection with the horizontal XY plane, ds is the distance to the first valid vertical intersection -- -------------- */ lc->stencil[0].plane = XY; n = count; do { n--; lc->stencil[0].ds = dz / muz - vis[n].s; } while (!valid[n]); svalid = vis[n].s; l0 = floor(longfracx); frac1 = longfracx - l0; m0 = floor(longfracy); frac2 = longfracy - m0; switch (input.interpolate_3D) { case LINEAR_3D: lc->stencil[0].xbase[0] = l0; lc->stencil[0].xbase[1] = l0 + 1; lc->stencil[0].xkernel[0] = 1.0 - frac1; lc->stencil[0].xkernel[1] = frac1; lc->stencil[0].ybase[0] = m0; lc->stencil[0].ybase[1] = m0 + 1; lc->stencil[0].ykernel[0] = 1.0 - frac2; lc->stencil[0].ykernel[1] = frac2; break; case BICUBIC_3D: for (l = 0; l < NCC; l++) lc->stencil[0].xbase[l] = l0 - 1 + l; cc_kernel(frac1, lc->stencil[0].xkernel); for (m = 0; m < NCC; m++) lc->stencil[0].ybase[m] = m0 - 1 + m; cc_kernel(frac2, lc->stencil[0].ykernel); } lc->stencil[0].zbase[0] = (wind == UPWIND) ? k - 1 : k + 1; count = 1; for (n = (countXZ + countYZ)-1; n >= 0; n--) { if (valid[n]) { lc->stencil[count].plane = vis[n].plane; if (count == Nvalid) lc->stencil[count].ds = svalid; else { lc->stencil[count].ds = svalid - vis[n-1].s; svalid = vis[n-1].s; } switch (vis[n].plane) { case XZ: l0 = floor(vis[n].x / geometry->dx); lc->stencil[count].xbase[0] = l0; lc->stencil[count].xbase[1] = l0 + 1; frac1 = vis[n].x / geometry->dx - l0; lc->stencil[count].xkernel[0] = 1.0 - frac1; lc->stencil[count].xkernel[1] = frac1; lc->stencil[count].ybase[0] = (int) round(vis[n].y / geometry->dy); break; case YZ: lc->stencil[count].xbase[0] = (int) round(vis[n].x / geometry->dx); m0 = floor(vis[n].y / geometry->dy); lc->stencil[count].ybase[0] = m0; lc->stencil[count].ybase[1] = m0 + 1; frac1 = vis[n].y / geometry->dy - m0; lc->stencil[count].ykernel[0] = 1.0 - frac1; lc->stencil[count].ykernel[1] = frac1; } if (wind == UPWIND) { lc->stencil[count].zbase[0] = k; lc->stencil[count].zbase[1] = k - 1; frac2 = vis[n].z / dz; } else { lc->stencil[count].zbase[0] = k + 1; lc->stencil[count].zbase[1] = k; frac2 = 1.0 - vis[n].z / dz; } lc->stencil[count].zkernel[0] = 1.0 - frac2; lc->stencil[count].zkernel[1] = frac2; count++; } } /* --- Free temporary arrays -- -------------- */ free(vis); free(valid); /* --- Store total number of long characteristics to be used - -- */ if (st->plane == YZ) geometry->Nlongchar += geometry->Ny; else geometry->Nlongchar += geometry->Nx; } }
/* * SP-GiST consistent function for inner nodes: check which nodes are * consistent with given set of queries. */ Datum spg_range_quad_inner_consistent(PG_FUNCTION_ARGS) { spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0); spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1); int which; int i; if (in->allTheSame) { /* Report that all nodes should be visited */ out->nNodes = in->nNodes; out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes); for (i = 0; i < in->nNodes; i++) out->nodeNumbers[i] = i; PG_RETURN_VOID(); } if (!in->hasPrefix) { /* * No centroid on this inner node. Such a node has two child nodes, * the first for empty ranges, and the second for non-empty ones. */ Assert(in->nNodes == 2); /* * Nth bit of which variable means that (N - 1)th node should be * visited. Initially all bits are set. Bits of nodes which should be * skipped will be unset. */ which = (1 << 1) | (1 << 2); for (i = 0; i < in->nkeys; i++) { StrategyNumber strategy = in->scankeys[i].sk_strategy; bool empty; /* * The only strategy when second argument of operator is not range * is RANGESTRAT_CONTAINS_ELEM. */ if (strategy != RANGESTRAT_CONTAINS_ELEM) empty = RangeIsEmpty( DatumGetRangeType(in->scankeys[i].sk_argument)); else empty = false; switch (strategy) { case RANGESTRAT_BEFORE: case RANGESTRAT_OVERLEFT: case RANGESTRAT_OVERLAPS: case RANGESTRAT_OVERRIGHT: case RANGESTRAT_AFTER: /* These strategies return false if any argument is empty */ if (empty) which = 0; else which &= (1 << 2); break; case RANGESTRAT_CONTAINS: /* * All ranges contain an empty range. Only non-empty ranges * can contain a non-empty range. */ if (!empty) which &= (1 << 2); break; case RANGESTRAT_CONTAINED_BY: /* * Only an empty range is contained by an empty range. Both * empty and non-empty ranges can be contained by a * non-empty range. */ if (empty) which &= (1 << 1); break; case RANGESTRAT_CONTAINS_ELEM: which &= (1 << 2); break; case RANGESTRAT_EQ: if (empty) which &= (1 << 1); else which &= (1 << 2); break; default: elog(ERROR, "unrecognized range strategy: %d", strategy); break; } if (which == 0) break; /* no need to consider remaining conditions */ } } else { RangeBound centroidLower, centroidUpper; bool centroidEmpty; TypeCacheEntry *typcache; RangeType *centroid; /* This node has a centroid. Fetch it. */ centroid = DatumGetRangeType(in->prefixDatum); typcache = range_get_typcache(fcinfo, RangeTypeGetOid(DatumGetRangeType(centroid))); range_deserialize(typcache, centroid, ¢roidLower, ¢roidUpper, ¢roidEmpty); Assert(in->nNodes == 4 || in->nNodes == 5); /* * Nth bit of which variable means that (N - 1)th node (Nth quadrant) * should be visited. Initially all bits are set. Bits of nodes which * can be skipped will be unset. */ which = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5); for (i = 0; i < in->nkeys; i++) { StrategyNumber strategy; RangeBound lower, upper; bool empty; RangeType *range = NULL; /* Restrictions on range bounds according to scan strategy */ RangeBound *minLower = NULL, *maxLower = NULL, *minUpper = NULL, *maxUpper = NULL; /* Are the restrictions on range bounds inclusive? */ bool inclusive = true; bool strictEmpty = true; strategy = in->scankeys[i].sk_strategy; /* * RANGESTRAT_CONTAINS_ELEM is just like RANGESTRAT_CONTAINS, but * the argument is a single element. Expand the single element to * a range containing only the element, and treat it like * RANGESTRAT_CONTAINS. */ if (strategy == RANGESTRAT_CONTAINS_ELEM) { lower.inclusive = true; lower.infinite = false; lower.lower = true; lower.val = in->scankeys[i].sk_argument; upper.inclusive = true; upper.infinite = false; upper.lower = false; upper.val = in->scankeys[i].sk_argument; empty = false; strategy = RANGESTRAT_CONTAINS; } else { range = DatumGetRangeType(in->scankeys[i].sk_argument); range_deserialize(typcache, range, &lower, &upper, &empty); } /* * Most strategies are handled by forming a bounding box from the * search key, defined by a minLower, maxLower, minUpper, maxUpper. * Some modify 'which' directly, to specify exactly which quadrants * need to be visited. * * For most strategies, nothing matches an empty search key, and * an empty range never matches a non-empty key. If a strategy * does not behave like that wrt. empty ranges, set strictEmpty to * false. */ switch (strategy) { case RANGESTRAT_BEFORE: /* * Range A is before range B if upper bound of A is lower * than lower bound of B. */ maxUpper = &lower; inclusive = false; break; case RANGESTRAT_OVERLEFT: /* * Range A is overleft to range B if upper bound of A is * less or equal to upper bound of B. */ maxUpper = &upper; break; case RANGESTRAT_OVERLAPS: /* * Non-empty ranges overlap, if lower bound of each range * is lower or equal to upper bound of the other range. */ maxLower = &upper; minUpper = &lower; break; case RANGESTRAT_OVERRIGHT: /* * Range A is overright to range B if lower bound of A is * greater or equal to lower bound of B. */ minLower = &lower; break; case RANGESTRAT_AFTER: /* * Range A is after range B if lower bound of A is greater * than upper bound of B. */ minLower = &upper; inclusive = false; break; case RANGESTRAT_CONTAINS: /* * Non-empty range A contains non-empty range B if lower * bound of A is lower or equal to lower bound of range B * and upper bound of range A is greater or equal to upper * bound of range A. * * All non-empty ranges contain an empty range. */ strictEmpty = false; if (!empty) { which &= (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4); maxLower = &lower; minUpper = &upper; } break; case RANGESTRAT_CONTAINED_BY: /* The opposite of contains. */ strictEmpty = false; if (empty) { /* An empty range is only contained by an empty range */ which &= (1 << 5); } else { minLower = &lower; maxUpper = &upper; } break; case RANGESTRAT_EQ: /* * Equal range can be only in the same quadrant where * argument would be placed to. */ strictEmpty = false; which &= (1 << getQuadrant(typcache, centroid, range)); break; default: elog(ERROR, "unrecognized range strategy: %d", strategy); break; } if (strictEmpty) { if (empty) { /* Scan key is empty, no branches are satisfying */ which = 0; break; } else { /* Shouldn't visit tree branch with empty ranges */ which &= (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4); } } /* * Using the bounding box, see which quadrants we have to descend * into. */ if (minLower) { /* * If the centroid's lower bound is less than or equal to * the minimum lower bound, anything in the 3rd and 4th * quadrants will have an even smaller lower bound, and thus * can't match. */ if (range_cmp_bounds(typcache, ¢roidLower, minLower) <= 0) which &= (1 << 1) | (1 << 2) | (1 << 5); } if (maxLower) { /* * If the centroid's lower bound is greater than the maximum * lower bound, anything in the 1st and 2nd quadrants will * also have a greater than or equal lower bound, and thus * can't match. If the centroid's lower bound is equal to * the maximum lower bound, we can still exclude the 1st and * 2nd quadrants if we're looking for a value strictly greater * than the maximum. */ int cmp; cmp = range_cmp_bounds(typcache, ¢roidLower, maxLower); if (cmp > 0 || (!inclusive && cmp == 0)) which &= (1 << 3) | (1 << 4) | (1 << 5); } if (minUpper) { /* * If the centroid's upper bound is less than or equal to * the minimum upper bound, anything in the 2nd and 3rd * quadrants will have an even smaller upper bound, and thus * can't match. */ if (range_cmp_bounds(typcache, ¢roidUpper, minUpper) <= 0) which &= (1 << 1) | (1 << 4) | (1 << 5); } if (maxUpper) { /* * If the centroid's upper bound is greater than the maximum * upper bound, anything in the 1st and 4th quadrants will * also have a greater than or equal upper bound, and thus * can't match. If the centroid's upper bound is equal to * the maximum upper bound, we can still exclude the 1st and * 4th quadrants if we're looking for a value strictly greater * than the maximum. */ int cmp; cmp = range_cmp_bounds(typcache, ¢roidUpper, maxUpper); if (cmp > 0 || (!inclusive && cmp == 0)) which &= (1 << 2) | (1 << 3) | (1 << 5); } if (which == 0) break; /* no need to consider remaining conditions */ } } /* We must descend into the quadrant(s) identified by 'which' */ out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes); out->nNodes = 0; for (i = 1; i <= in->nNodes; i++) { if (which & (1 << i)) out->nodeNumbers[out->nNodes++] = i - 1; } PG_RETURN_VOID(); }
/* * Picksplit SP-GiST function: split ranges into nodes. Select "centroid" * range and distribute ranges according to quadrants. */ Datum spg_range_quad_picksplit(PG_FUNCTION_ARGS) { spgPickSplitIn *in = (spgPickSplitIn *) PG_GETARG_POINTER(0); spgPickSplitOut *out = (spgPickSplitOut *) PG_GETARG_POINTER(1); int i; int j; int nonEmptyCount; RangeType *centroid; bool empty; TypeCacheEntry *typcache; /* Use the median values of lower and upper bounds as the centroid range */ RangeBound *lowerBounds, *upperBounds; typcache = range_get_typcache(fcinfo, RangeTypeGetOid(DatumGetRangeType(in->datums[0]))); /* Allocate memory for bounds */ lowerBounds = palloc(sizeof(RangeBound) * in->nTuples); upperBounds = palloc(sizeof(RangeBound) * in->nTuples); j = 0; /* Deserialize bounds of ranges, count non-empty ranges */ for (i = 0; i < in->nTuples; i++) { range_deserialize(typcache, DatumGetRangeType(in->datums[i]), &lowerBounds[j], &upperBounds[j], &empty); if (!empty) j++; } nonEmptyCount = j; /* * All the ranges are empty. The best we can do is to construct an inner * node with no centroid, and put all ranges into node 0. If non-empty * ranges are added later, they will be routed to node 1. */ if (nonEmptyCount == 0) { out->nNodes = 2; out->hasPrefix = false; /* Prefix is empty */ out->prefixDatum = PointerGetDatum(NULL); out->nodeLabels = NULL; out->mapTuplesToNodes = palloc(sizeof(int) * in->nTuples); out->leafTupleDatums = palloc(sizeof(Datum) * in->nTuples); /* Place all ranges into node 0 */ for (i = 0; i < in->nTuples; i++) { RangeType *range = DatumGetRangeType(in->datums[i]); out->leafTupleDatums[i] = RangeTypeGetDatum(range); out->mapTuplesToNodes[i] = 0; } PG_RETURN_VOID(); } /* Sort range bounds in order to find medians */ qsort_arg(lowerBounds, nonEmptyCount, sizeof(RangeBound), bound_cmp, typcache); qsort_arg(upperBounds, nonEmptyCount, sizeof(RangeBound), bound_cmp, typcache); /* Construct "centroid" range from medians of lower and upper bounds */ centroid = range_serialize(typcache, &lowerBounds[nonEmptyCount / 2], &upperBounds[nonEmptyCount / 2], false); out->hasPrefix = true; out->prefixDatum = RangeTypeGetDatum(centroid); /* Create node for empty ranges only if it is a root node */ out->nNodes = (in->level == 0) ? 5 : 4; out->nodeLabels = NULL; /* we don't need node labels */ out->mapTuplesToNodes = palloc(sizeof(int) * in->nTuples); out->leafTupleDatums = palloc(sizeof(Datum) * in->nTuples); /* * Assign ranges to corresponding nodes according to quadrants relative to * "centroid" range. */ for (i = 0; i < in->nTuples; i++) { RangeType *range = DatumGetRangeType(in->datums[i]); int16 quadrant = getQuadrant(typcache, centroid, range); out->leafTupleDatums[i] = RangeTypeGetDatum(range); out->mapTuplesToNodes[i] = quadrant - 1; } PG_RETURN_VOID(); }