PostGIS  3.4.0dev-r@@SVN_REVISION@@

◆ compute_gserialized_stats_mode()

static void compute_gserialized_stats_mode ( VacAttrStats *  stats,
AnalyzeAttrFetchFunc  fetchfunc,
int  sample_rows,
double  total_rows,
int  mode 
)
static

The gserialized_analyze_nd sets this function as a callback on the stats object when called by the ANALYZE command.

ANALYZE then gathers the requisite number of sample rows and then calls this function.

We could also pass stats->extra_data in from gserialized_analyze_nd (things like the column type or other stuff from the system catalogs) but so far we don't use that capability.

Our job is to build some statistics on the sample data for use by operator estimators.

We will populate an n-d histogram using the provided sample rows. The selectivity estimators (sel and joinsel) can then use the histogram

Definition at line 1371 of file gserialized_estimate.c.

1373 {
1374  MemoryContext old_context;
1375  int d, i; /* Counters */
1376  int notnull_cnt = 0; /* # not null rows in the sample */
1377  int null_cnt = 0; /* # null rows in the sample */
1378  int histogram_features = 0; /* # rows that actually got counted in the histogram */
1379 
1380  ND_STATS *nd_stats; /* Our histogram */
1381  size_t nd_stats_size; /* Size to allocate */
1382 
1383  double total_width = 0; /* # of bytes used by sample */
1384  double total_cell_count = 0; /* # of cells in histogram affected by sample */
1385 
1386  ND_BOX sum; /* Sum of extents of sample boxes */
1387  ND_BOX avg; /* Avg of extents of sample boxes */
1388  ND_BOX stddev; /* StdDev of extents of sample boxes */
1389 
1390  const ND_BOX **sample_boxes; /* ND_BOXes for each of the sample features */
1391  ND_BOX sample_extent; /* Extent of the raw sample */
1392  int histo_size[ND_DIMS]; /* histogram nrows, ncols, etc */
1393  ND_BOX histo_extent; /* Spatial extent of the histogram */
1394  ND_BOX histo_extent_new; /* Temporary variable */
1395  int histo_cells_target; /* Number of cells we will shoot for, given the stats target */
1396  int histo_cells; /* Number of cells in the histogram */
1397  int histo_cells_new = 1; /* Temporary variable */
1398 
1399  int ndims = 2; /* Dimensionality of the sample */
1400  int histo_ndims = 0; /* Dimensionality of the histogram */
1401  double sample_distribution[ND_DIMS]; /* How homogeneous is distribution of sample in each axis? */
1402  double total_distribution; /* Total of sample_distribution */
1403 
1404  int stats_slot; /* What slot is this data going into? (2D vs ND) */
1405  int stats_kind; /* And this is what? (2D vs ND) */
1406 
1407  /* Initialize sum and stddev */
1408  nd_box_init(&sum);
1409  nd_box_init(&stddev);
1410  nd_box_init(&avg);
1411  nd_box_init(&histo_extent);
1412  nd_box_init(&histo_extent_new);
1413 
1414  /*
1415  * This is where gserialized_analyze_nd
1416  * should put its' custom parameters.
1417  */
1418  /* void *mystats = stats->extra_data; */
1419 
1420  POSTGIS_DEBUG(2, "compute_gserialized_stats called");
1421  POSTGIS_DEBUGF(3, " # sample_rows: %d", sample_rows);
1422  POSTGIS_DEBUGF(3, " estimate of total_rows: %.6g", total_rows);
1423 
1424  /*
1425  * We might need less space, but don't think
1426  * its worth saving...
1427  */
1428  sample_boxes = palloc(sizeof(ND_BOX*) * sample_rows);
1429 
1430  /*
1431  * First scan:
1432  * o read boxes
1433  * o find dimensionality of the sample
1434  * o find extent of the sample
1435  * o count null-infinite/not-null values
1436  * o compute total_width
1437  * o compute total features's box area (for avgFeatureArea)
1438  * o sum features box coordinates (for standard deviation)
1439  */
1440  for ( i = 0; i < sample_rows; i++ )
1441  {
1442  Datum datum;
1443  GBOX gbox = {0};
1444  ND_BOX *nd_box;
1445  bool is_null;
1446 
1447  datum = fetchfunc(stats, i, &is_null);
1448 
1449  /* Skip all NULLs. */
1450  if ( is_null )
1451  {
1452  POSTGIS_DEBUGF(4, " skipped null geometry %d", i);
1453  null_cnt++;
1454  continue;
1455  }
1456 
1457  /* Read the bounds from the gserialized. */
1458  if (LW_FAILURE == gserialized_datum_get_gbox_p(datum, &gbox))
1459  {
1460  /* Skip empties too. */
1461  POSTGIS_DEBUGF(3, " skipped empty geometry %d", i);
1462  continue;
1463  }
1464 
1465  /* If we're in 2D mode, zero out the higher dimensions for "safety" */
1466  if ( mode == 2 )
1467  gbox.zmin = gbox.zmax = gbox.mmin = gbox.mmax = 0.0;
1468 
1469  /* Check bounds for validity (finite and not NaN) */
1470  if ( ! gbox_is_valid(&gbox) )
1471  {
1472  POSTGIS_DEBUGF(3, " skipped infinite/nan geometry %d", i);
1473  continue;
1474  }
1475 
1476  /*
1477  * In N-D mode, set the ndims to the maximum dimensionality found
1478  * in the sample. Otherwise, leave at ndims == 2.
1479  */
1480  if ( mode != 2 )
1481  ndims = Max(gbox_ndims(&gbox), ndims);
1482 
1483  /* Convert gbox to n-d box */
1484  nd_box = palloc(sizeof(ND_BOX));
1485  nd_box_from_gbox(&gbox, nd_box);
1486 
1487  /* Cache n-d bounding box */
1488  sample_boxes[notnull_cnt] = nd_box;
1489 
1490  /* Initialize sample extent before merging first entry */
1491  if ( ! notnull_cnt )
1492  nd_box_init_bounds(&sample_extent);
1493 
1494  /* Add current sample to overall sample extent */
1495  nd_box_merge(nd_box, &sample_extent);
1496 
1497  /* How many bytes does this sample use? */
1498  total_width += toast_raw_datum_size(datum);
1499 
1500  /* Add bounds coordinates to sums for stddev calculation */
1501  for ( d = 0; d < ndims; d++ )
1502  {
1503  sum.min[d] += nd_box->min[d];
1504  sum.max[d] += nd_box->max[d];
1505  }
1506 
1507  /* Increment our "good feature" count */
1508  notnull_cnt++;
1509 
1510  /* Give backend a chance of interrupting us */
1511  vacuum_delay_point();
1512  }
1513 
1514  /*
1515  * We'll build a histogram having stats->attr->attstattarget
1516  * (default 100) cells on each side, within reason...
1517  * we'll use ndims*100000 as the maximum number of cells.
1518  * Also, if we're sampling a relatively small table, we'll try to ensure that
1519  * we have a smaller grid.
1520  */
1521 #if POSTGIS_PGSQL_VERSION >= 170
1522  histo_cells_target = (int)pow((double)(stats->attstattarget), (double)ndims);
1523  POSTGIS_DEBUGF(3, " stats->attstattarget: %d", stats->attstattarget);
1524 #else
1525  histo_cells_target = (int)pow((double)(stats->attr->attstattarget), (double)ndims);
1526  POSTGIS_DEBUGF(3, " stats->attr->attstattarget: %d", stats->attr->attstattarget);
1527 #endif
1528  histo_cells_target = Min(histo_cells_target, ndims * 100000);
1529  histo_cells_target = Min(histo_cells_target, (int)(10 * ndims * total_rows));
1530  POSTGIS_DEBUGF(3, " target # of histogram cells: %d", histo_cells_target);
1531 
1532  /* If there's no useful features, we can't work out stats */
1533  if ( ! notnull_cnt )
1534  {
1535  stats->stats_valid = false;
1536  return;
1537  }
1538 
1539  POSTGIS_DEBUGF(3, " sample_extent: %s", nd_box_to_json(&sample_extent, ndims));
1540 
1541  /*
1542  * Second scan:
1543  * o compute standard deviation
1544  */
1545  for ( d = 0; d < ndims; d++ )
1546  {
1547  /* Calculate average bounds values */
1548  avg.min[d] = sum.min[d] / notnull_cnt;
1549  avg.max[d] = sum.max[d] / notnull_cnt;
1550 
1551  /* Calculate standard deviation for this dimension bounds */
1552  for ( i = 0; i < notnull_cnt; i++ )
1553  {
1554  const ND_BOX *ndb = sample_boxes[i];
1555  stddev.min[d] += (ndb->min[d] - avg.min[d]) * (ndb->min[d] - avg.min[d]);
1556  stddev.max[d] += (ndb->max[d] - avg.max[d]) * (ndb->max[d] - avg.max[d]);
1557  }
1558  stddev.min[d] = sqrt(stddev.min[d] / notnull_cnt);
1559  stddev.max[d] = sqrt(stddev.max[d] / notnull_cnt);
1560 
1561  /* Histogram bounds for this dimension bounds is avg +/- SDFACTOR * stdev */
1562  histo_extent.min[d] = Max(avg.min[d] - SDFACTOR * stddev.min[d], sample_extent.min[d]);
1563  histo_extent.max[d] = Min(avg.max[d] + SDFACTOR * stddev.max[d], sample_extent.max[d]);
1564  }
1565 
1566  /*
1567  * Third scan:
1568  * o skip hard deviants
1569  * o compute new histogram box
1570  */
1571  nd_box_init_bounds(&histo_extent_new);
1572  for ( i = 0; i < notnull_cnt; i++ )
1573  {
1574  const ND_BOX *ndb = sample_boxes[i];
1575  /* Skip any hard deviants (boxes entirely outside our histo_extent */
1576  if ( ! nd_box_intersects(&histo_extent, ndb, ndims) )
1577  {
1578  POSTGIS_DEBUGF(4, " feature %d is a hard deviant, skipped", i);
1579  sample_boxes[i] = NULL;
1580  continue;
1581  }
1582  /* Expand our new box to fit all the other features. */
1583  nd_box_merge(ndb, &histo_extent_new);
1584  }
1585  /*
1586  * Expand the box slightly (1%) to avoid edge effects
1587  * with objects that are on the boundary
1588  */
1589  nd_box_expand(&histo_extent_new, 0.01);
1590  histo_extent = histo_extent_new;
1591 
1592  /*
1593  * How should we allocate our histogram cells to the
1594  * different dimensions? We can't do it by raw dimensional width,
1595  * because in x/y/z space, the z can have different units
1596  * from the x/y. Similarly for x/y/t space.
1597  * So, we instead calculate how much features overlap
1598  * each other in their dimension to figure out which
1599  * dimensions have useful selectivity characteristics (more
1600  * variability in density) and therefor would find
1601  * more cells useful (to distinguish between dense places and
1602  * homogeneous places).
1603  */
1604  nd_box_array_distribution(sample_boxes, notnull_cnt, &histo_extent, ndims,
1605  sample_distribution);
1606 
1607  /*
1608  * The sample_distribution array now tells us how spread out the
1609  * data is in each dimension, so we use that data to allocate
1610  * the histogram cells we have available.
1611  * At this point, histo_cells_target is the approximate target number
1612  * of cells.
1613  */
1614 
1615  /*
1616  * Some dimensions have basically a uniform distribution, we want
1617  * to allocate no cells to those dimensions, only to dimensions
1618  * that have some interesting differences in data distribution.
1619  * Here we count up the number of interesting dimensions
1620  */
1621  for ( d = 0; d < ndims; d++ )
1622  {
1623  if ( sample_distribution[d] > 0 )
1624  histo_ndims++;
1625  }
1626 
1627  if ( histo_ndims == 0 )
1628  {
1629  /* Special case: all our dimensions had low variability! */
1630  /* We just divide the cells up evenly */
1631  POSTGIS_DEBUG(3, " special case: no axes have variability");
1632  histo_cells_new = 1;
1633  for ( d = 0; d < ndims; d++ )
1634  {
1635  histo_size[d] = (int)pow((double)histo_cells_target, 1/(double)ndims);
1636  if ( ! histo_size[d] )
1637  histo_size[d] = 1;
1638  POSTGIS_DEBUGF(3, " histo_size[d]: %d", histo_size[d]);
1639  histo_cells_new *= histo_size[d];
1640  }
1641  POSTGIS_DEBUGF(3, " histo_cells_new: %d", histo_cells_new);
1642  }
1643  else
1644  {
1645  /*
1646  * We're going to express the amount of variability in each dimension
1647  * as a proportion of the total variability and allocate cells in that
1648  * dimension relative to that proportion.
1649  */
1650  POSTGIS_DEBUG(3, " allocating histogram axes based on axis variability");
1651  total_distribution = total_double(sample_distribution, ndims); /* First get the total */
1652  POSTGIS_DEBUGF(3, " total_distribution: %.8g", total_distribution);
1653  histo_cells_new = 1; /* For the number of cells in the final histogram */
1654  for ( d = 0; d < ndims; d++ )
1655  {
1656  if ( sample_distribution[d] == 0 ) /* Uninteresting dimensions don't get any room */
1657  {
1658  histo_size[d] = 1;
1659  }
1660  else /* Interesting dimension */
1661  {
1662  /* How does this dims variability compare to the total? */
1663  float edge_ratio = (float)sample_distribution[d] / (float)total_distribution;
1664  /*
1665  * Scale the target cells number by the # of dims and ratio,
1666  * then take the appropriate root to get the estimated number of cells
1667  * on this axis (eg, pow(0.5) for 2d, pow(0.333) for 3d, pow(0.25) for 4d)
1668  */
1669  histo_size[d] = (int)pow(histo_cells_target * histo_ndims * edge_ratio, 1/(double)histo_ndims);
1670  /* If something goes awry, just give this dim one slot */
1671  if ( ! histo_size[d] )
1672  histo_size[d] = 1;
1673  }
1674  histo_cells_new *= histo_size[d];
1675  }
1676  POSTGIS_DEBUGF(3, " histo_cells_new: %d", histo_cells_new);
1677  }
1678 
1679  /* Update histo_cells to the actual number of cells we need to allocate */
1680  histo_cells = histo_cells_new;
1681  POSTGIS_DEBUGF(3, " histo_cells: %d", histo_cells);
1682 
1683  /*
1684  * Create the histogram (ND_STATS) in the stats memory context
1685  */
1686  old_context = MemoryContextSwitchTo(stats->anl_context);
1687  nd_stats_size = sizeof(ND_STATS) + ((histo_cells - 1) * sizeof(float4));
1688  nd_stats = palloc(nd_stats_size);
1689  memset(nd_stats, 0, nd_stats_size); /* Initialize all values to 0 */
1690  MemoryContextSwitchTo(old_context);
1691 
1692  /* Initialize the #ND_STATS objects */
1693  nd_stats->ndims = ndims;
1694  nd_stats->extent = histo_extent;
1695  nd_stats->sample_features = sample_rows;
1696  nd_stats->table_features = total_rows;
1697  nd_stats->not_null_features = notnull_cnt;
1698  /* Copy in the histogram dimensions */
1699  for ( d = 0; d < ndims; d++ )
1700  nd_stats->size[d] = histo_size[d];
1701 
1702  /*
1703  * Fourth scan:
1704  * o fill histogram values with the proportion of
1705  * features' bbox overlaps: a feature's bvol
1706  * can fully overlap (1) or partially overlap
1707  * (fraction of 1) an histogram cell.
1708  *
1709  * Note that we are filling each cell with the "portion of
1710  * the feature's box that overlaps the cell". So, if we sum
1711  * up the values in the histogram, we could get the
1712  * histogram feature count.
1713  *
1714  */
1715  for ( i = 0; i < notnull_cnt; i++ )
1716  {
1717  const ND_BOX *nd_box;
1718  ND_IBOX nd_ibox;
1719  int at[ND_DIMS];
1720  double num_cells = 0;
1721  double min[ND_DIMS] = {0.0, 0.0, 0.0, 0.0};
1722  double max[ND_DIMS] = {0.0, 0.0, 0.0, 0.0};
1723  double cellsize[ND_DIMS] = {0.0, 0.0, 0.0, 0.0};
1724 
1725  nd_box = sample_boxes[i];
1726  if ( ! nd_box ) continue; /* Skip Null'ed out hard deviants */
1727 
1728  /* Give backend a chance of interrupting us */
1729  vacuum_delay_point();
1730 
1731  /* Find the cells that overlap with this box and put them into the ND_IBOX */
1732  nd_box_overlap(nd_stats, nd_box, &nd_ibox);
1733  memset(at, 0, sizeof(int)*ND_DIMS);
1734 
1735  POSTGIS_DEBUGF(3, " feature %d: ibox (%d, %d, %d, %d) (%d, %d, %d, %d)", i,
1736  nd_ibox.min[0], nd_ibox.min[1], nd_ibox.min[2], nd_ibox.min[3],
1737  nd_ibox.max[0], nd_ibox.max[1], nd_ibox.max[2], nd_ibox.max[3]);
1738 
1739  for ( d = 0; d < nd_stats->ndims; d++ )
1740  {
1741  /* Initialize the starting values */
1742  at[d] = nd_ibox.min[d];
1743  min[d] = nd_stats->extent.min[d];
1744  max[d] = nd_stats->extent.max[d];
1745  cellsize[d] = (max[d] - min[d])/(nd_stats->size[d]);
1746  }
1747 
1748  /*
1749  * Move through all the overlaped histogram cells values and
1750  * add the box overlap proportion to them.
1751  */
1752  do
1753  {
1754  ND_BOX nd_cell = { {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0} };
1755  double ratio;
1756  /* Create a box for this histogram cell */
1757  for ( d = 0; d < nd_stats->ndims; d++ )
1758  {
1759  nd_cell.min[d] = min[d] + (at[d]+0) * cellsize[d];
1760  nd_cell.max[d] = min[d] + (at[d]+1) * cellsize[d];
1761  }
1762 
1763  /*
1764  * If a feature box is completely inside one cell the ratio will be
1765  * 1.0. If a feature box is 50% in two cells, each cell will get
1766  * 0.5 added on.
1767  */
1768  ratio = nd_box_ratio(&nd_cell, nd_box, nd_stats->ndims);
1769  nd_stats->value[nd_stats_value_index(nd_stats, at)] += ratio;
1770  num_cells += ratio;
1771  POSTGIS_DEBUGF(3, " ratio (%.8g) num_cells (%.8g)", ratio, num_cells);
1772  POSTGIS_DEBUGF(3, " at (%d, %d, %d, %d)", at[0], at[1], at[2], at[3]);
1773  }
1774  while ( nd_increment(&nd_ibox, nd_stats->ndims, at) );
1775 
1776  /* Keep track of overall number of overlaps counted */
1777  total_cell_count += num_cells;
1778  /* How many features have we added to this histogram? */
1779  histogram_features++;
1780  }
1781 
1782  POSTGIS_DEBUGF(3, " histogram_features: %d", histogram_features);
1783  POSTGIS_DEBUGF(3, " sample_rows: %d", sample_rows);
1784  POSTGIS_DEBUGF(3, " table_rows: %.6g", total_rows);
1785 
1786  /* Error out if we got no sample information */
1787  if ( ! histogram_features )
1788  {
1789  POSTGIS_DEBUG(3, " no stats have been gathered");
1790  elog(NOTICE, " no features lie in the stats histogram, invalid stats");
1791  stats->stats_valid = false;
1792  return;
1793  }
1794 
1795  nd_stats->histogram_features = histogram_features;
1796  nd_stats->histogram_cells = histo_cells;
1797  nd_stats->cells_covered = total_cell_count;
1798 
1799  /* Put this histogram data into the right slot/kind */
1800  if ( mode == 2 )
1801  {
1802  stats_slot = STATISTIC_SLOT_2D;
1803  stats_kind = STATISTIC_KIND_2D;
1804  }
1805  else
1806  {
1807  stats_slot = STATISTIC_SLOT_ND;
1808  stats_kind = STATISTIC_KIND_ND;
1809  }
1810 
1811  /* Write the statistics data */
1812  stats->stakind[stats_slot] = stats_kind;
1813  stats->staop[stats_slot] = InvalidOid;
1814  stats->stanumbers[stats_slot] = (float4*)nd_stats;
1815  stats->numnumbers[stats_slot] = nd_stats_size/sizeof(float4);
1816  stats->stanullfrac = (float4)null_cnt/sample_rows;
1817  stats->stawidth = total_width/notnull_cnt;
1818  stats->stadistinct = -1.0;
1819  stats->stats_valid = true;
1820 
1821  POSTGIS_DEBUGF(3, " out: slot 0: kind %d (STATISTIC_KIND_ND)", stats->stakind[0]);
1822  POSTGIS_DEBUGF(3, " out: slot 0: op %d (InvalidOid)", stats->staop[0]);
1823  POSTGIS_DEBUGF(3, " out: slot 0: numnumbers %d", stats->numnumbers[0]);
1824  POSTGIS_DEBUGF(3, " out: null fraction: %f=%d/%d", stats->stanullfrac, null_cnt, sample_rows);
1825  POSTGIS_DEBUGF(3, " out: average width: %d bytes", stats->stawidth);
1826  POSTGIS_DEBUG (3, " out: distinct values: all (no check done)");
1827  POSTGIS_DEBUGF(3, " out: %s", nd_stats_to_json(nd_stats));
1828  /*
1829  POSTGIS_DEBUGF(3, " out histogram:\n%s", nd_stats_to_grid(nd_stats));
1830  */
1831 
1832  return;
1833 }
int gbox_is_valid(const GBOX *gbox)
Return false if any of the dimensions is NaN or infinite.
Definition: gbox.c:197
struct ND_STATS_T ND_STATS
N-dimensional statistics structure.
static char * nd_stats_to_json(const ND_STATS *nd_stats)
Convert an ND_STATS to a JSON representation for external use.
static int nd_box_intersects(const ND_BOX *a, const ND_BOX *b, int ndims)
Return true if ND_BOX a overlaps b, false otherwise.
static int nd_box_init_bounds(ND_BOX *a)
Prepare an ND_BOX for bounds calculation: set the maxes to the smallest thing possible and the mins t...
static int nd_increment(ND_IBOX *ibox, int ndims, int *counter)
Given an n-d index array (counter), and a domain to increment it in (ibox) increment it by one,...
#define STATISTIC_SLOT_ND
static int gbox_ndims(const GBOX *gbox)
Given that geodetic boxes are X/Y/Z regardless of the underlying geometry dimensionality and other bo...
#define ND_DIMS
The maximum number of dimensions our code can handle.
#define STATISTIC_KIND_2D
static int nd_box_merge(const ND_BOX *source, ND_BOX *target)
Expand the bounds of target to include source.
#define STATISTIC_KIND_ND
static double total_double(const double *vals, int nvals)
Given double array, return sum of values.
#define SDFACTOR
static void nd_box_from_gbox(const GBOX *gbox, ND_BOX *nd_box)
Set the values of an ND_BOX from a GBOX.
static int nd_box_init(ND_BOX *a)
Zero out an ND_BOX.
static int nd_box_expand(ND_BOX *nd_box, double expansion_factor)
Expand an ND_BOX ever so slightly.
static int nd_box_overlap(const ND_STATS *nd_stats, const ND_BOX *nd_box, ND_IBOX *nd_ibox)
What stats cells overlap with this ND_BOX? Put the lowest cell addresses in ND_IBOX->min and the high...
static double nd_box_ratio(const ND_BOX *b1, const ND_BOX *b2, int ndims)
Returns the proportion of b2 that is covered by b1.
static int nd_stats_value_index(const ND_STATS *stats, int *indexes)
Given a position in the n-d histogram (i,j,k) return the position in the 1-d values array.
static char * nd_box_to_json(const ND_BOX *nd_box, int ndims)
Convert an ND_BOX to a JSON string for printing.
#define STATISTIC_SLOT_2D
static int nd_box_array_distribution(const ND_BOX **nd_boxes, int num_boxes, const ND_BOX *extent, int ndims, double *distribution)
Calculate how much a set of boxes is homogenously distributed or contentrated within one dimension,...
int gserialized_datum_get_gbox_p(Datum gsdatum, GBOX *gbox)
Given a GSERIALIZED datum, as quickly as possible (peaking into the top of the memory) return the gbo...
#define LW_FAILURE
Definition: liblwgeom.h:96
double zmax
Definition: liblwgeom.h:359
double zmin
Definition: liblwgeom.h:358
double mmax
Definition: liblwgeom.h:361
double mmin
Definition: liblwgeom.h:360
float4 max[ND_DIMS]
float4 min[ND_DIMS]
N-dimensional box type for calculations, to avoid doing explicit axis conversions from GBOX in all ca...
int max[ND_DIMS]
int min[ND_DIMS]
N-dimensional box index type.
float4 size[ND_DIMS]
N-dimensional statistics structure.

References ND_STATS_T::cells_covered, ND_STATS_T::extent, gbox_is_valid(), gbox_ndims(), gserialized_datum_get_gbox_p(), ND_STATS_T::histogram_cells, ND_STATS_T::histogram_features, LW_FAILURE, ND_BOX_T::max, ND_IBOX_T::max, ND_BOX_T::min, ND_IBOX_T::min, GBOX::mmax, GBOX::mmin, nd_box_array_distribution(), nd_box_expand(), nd_box_from_gbox(), nd_box_init(), nd_box_init_bounds(), nd_box_intersects(), nd_box_merge(), nd_box_overlap(), nd_box_ratio(), nd_box_to_json(), ND_DIMS, nd_increment(), nd_stats_to_json(), nd_stats_value_index(), ND_STATS_T::ndims, ND_STATS_T::not_null_features, ND_STATS_T::sample_features, SDFACTOR, ND_STATS_T::size, STATISTIC_KIND_2D, STATISTIC_KIND_ND, STATISTIC_SLOT_2D, STATISTIC_SLOT_ND, ND_STATS_T::table_features, total_double(), ND_STATS_T::value, GBOX::zmax, and GBOX::zmin.

Referenced by compute_gserialized_stats().

Here is the call graph for this function:
Here is the caller graph for this function: