}
}
+/*- rt_pixel ----------------------------------------------------------*/
+
+/*
+ * Convert an array of rt_pixel objects to two 2D arrays of value and NODATA
+ *
+ * @param npixel: array of rt_pixel objects
+ * @param count: number of elements in npixel
+ * @param x: the column of the center pixel (0-based)
+ * @param y: the line of the center pixel (0-based)
+ * @param distance: the number of pixels around the center pixel
+ * @param value: pointer to pointer for 2D value array
+ * @param nodata: pointer to pointer for 2D NODATA array
+ *
+ * @return 0 on error, otherwise the X/Y axis length of value and NODATA
+ */
+int rt_pixel_set_to_array(
+ rt_pixel npixel, int count,
+ int x, int y,
+ uint16_t distance,
+ double ***value,
+ int ***nodata
+) {
+ uint32_t i;
+ uint32_t j;
+ uint32_t length = 0;
+ double **values = NULL;
+ int **nodatas = NULL;
+ int zero[2] = {0};
+ int _x;
+ int _y;
+
+ assert(npixel != NULL);
+ assert(count > 0);
+
+ /* length */
+ length = distance * 2 + 1;
+ RASTER_DEBUGF(4, "length = %d", length);
+
+ /* establish 2D arrays */
+ values = rtalloc(sizeof(double *) * length);
+ nodatas = rtalloc(sizeof(int *) * length);
+
+ if (values == NULL || nodatas == NULL) {
+ rterror("rt_pixel_set_to_array: Unable to allocate memory for 2D array");
+ return 0;
+ }
+
+ /* initialize */
+ for (i = 0; i < length; i++) {
+ values[i] = rtalloc(sizeof(double) * length);
+ nodatas[i] = rtalloc(sizeof(int) * length);
+
+ if (values[i] == NULL || nodatas[i] == NULL) {
+ rterror("rt_pixel_set_to_array: Unable to allocate memory for dimension of 2D array");
+
+ if (values[i] == NULL) {
+ for (j = 0; j < i; j++) {
+ rtdealloc(values[j]);
+ rtdealloc(nodatas[j]);
+ }
+ }
+ else {
+ for (j = 0; j <= i; j++) {
+ rtdealloc(values[j]);
+ if (j < i)
+ rtdealloc(nodatas[j]);
+ }
+ }
+
+ rtdealloc(values);
+ rtdealloc(nodatas);
+
+ return 0;
+ }
+
+ /* set values to 0 */
+ memset(values[i], 0, sizeof(double) * length);
+
+ /* set nodatas to 1 */
+ for (j = 0; j < length; j++)
+ nodatas[i][j] = 1;
+ }
+
+ /* get zero, zero of grid */
+ zero[0] = x - distance;
+ zero[1] = y - distance;
+
+ /* populate 2D arrays */
+ for (i = 0; i < count; i++) {
+ if (npixel[i].nodata)
+ continue;
+
+ _x = npixel[i].x - zero[0];
+ _y = npixel[i].y - zero[1];
+
+ RASTER_DEBUGF(4, "absolute x,y: %d x %d", npixel[i].x, npixel[i].y);
+ RASTER_DEBUGF(4, "relative x,y: %d x %d", _x, _y);
+
+ values[_x][_y] = npixel[i].value;
+ nodatas[_x][_y] = 0;
+
+ RASTER_DEBUGF(4, "(x, y, nodata, value) = (%d, %d, %d, %f)", _x, _y, nodatas[_x][_y], values[_x][_y]);
+ }
+
+ *value = &(*values);
+ *nodata = &(*nodatas);
+
+ return length;
+}
+
/*- rt_band ----------------------------------------------------------*/
/**
/* no NODATA, set to minimum possible value */
if (!band->hasnodata)
pixval = minval;
+ /* has NODATA, use NODATA */
else
pixval = band->nodataval;
RASTER_DEBUGF(4, "NODATA pixel outside band extent: (x, y, val) = (%d, %d, %f)", _x, _y, pixval);
}
else {
- err = rt_band_get_pixel(
+ if (rt_band_get_pixel(
band,
_x, _y,
&pixval
- );
- if (err < 0) {
+ ) < 0) {
rterror("rt_band_get_nearest_pixel: Unable to get pixel value");
if (count) rtdealloc(*npixels);
return -1;
npixel = &((*npixels)[count - 1]);
npixel->x = _x;
npixel->y = _y;
+ npixel->nodata = 0;
npixel->value = pixval;
}
*/
double rt_pixtype_get_min_value(rt_pixtype pixtype);
+/*- rt_pixel ----------------------------------------------------------*/
+
+/*
+ * Convert an array of rt_pixel objects to two 2D arrays of value and NODATA
+ *
+ * @param npixel: array of rt_pixel objects
+ * @param count: number of elements in npixel
+ * @param x: the column of the center pixel (0-based)
+ * @param y: the line of the center pixel (0-based)
+ * @param distance: the number of pixels around the center pixel
+ * @param value: pointer to pointer for 2D value array
+ * @param nodata: pointer to pointer for 2D NODATA array
+ *
+ * @return 0 on error, otherwise the X/Y axis length of value and NODATA
+ */
+int rt_pixel_set_to_array(
+ rt_pixel npixel, int count,
+ int x, int y,
+ uint16_t distance,
+ double ***value,
+ int ***nodata
+);
+
/*- rt_band ----------------------------------------------------------*/
/**
struct rt_pixel_t {
int x; /* column */
int y; /* line */
+
+ uint8_t nodata;
double value;
};
PG_FUNCTION_INFO_V1(RASTER_neighborhood);
Datum RASTER_neighborhood(PG_FUNCTION_ARGS)
{
- FuncCallContext *funcctx;
- TupleDesc tupdesc;
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ rt_band band = NULL;
+ int bandindex = 1;
+ int num_bands = 0;
+ int x = 0;
+ int y = 0;
+ int _x = 0;
+ int _y = 0;
+ int distance = 0;
+ bool exclude_nodata_value = TRUE;
+ double pixval;
- int call_cntr;
- int max_calls;
+ rt_pixel npixels = NULL;
+ int count;
+ int length;
+ double **value2D = NULL;
+ int **nodata2D = NULL;
- rt_pixel npixel1 = NULL;
- rt_pixel npixel2 = NULL;
+ int i = 0;
+ int j = 0;
+ int k = 0;
+ Datum *value1D = NULL;
+ bool *nodata1D = NULL;
+ int dim[2] = {0};
+ int lbound[2] = {1, 1};
+ ArrayType *mdArray = NULL;
- if (SRF_IS_FIRSTCALL()) {
- MemoryContext oldcontext;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- rt_band band = NULL;
- int bandindex = 1;
- int num_bands = 0;
- int x = 0;
- int y = 0;
- int distance = 0;
- bool exclude_nodata_value = TRUE;
+ /* pgraster is null, return nothing */
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
- int count;
+ raster = rt_raster_deserialize(pgraster, FALSE);
+ if (!raster) {
+ elog(ERROR, "RASTER_neighborhood: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
- /* create a function context for cross-call persistence */
- funcctx = SRF_FIRSTCALL_INIT();
+ /* band index is 1-based */
+ if (!PG_ARGISNULL(1))
+ bandindex = PG_GETARG_INT32(1);
+ num_bands = rt_raster_get_num_bands(raster);
+ if (bandindex < 1 || bandindex > num_bands) {
+ elog(NOTICE, "Invalid band index (must use 1-based). Returning NULL");
+ rt_raster_destroy(raster);
+ PG_RETURN_NULL();
+ }
- /* switch to memory context appropriate for multiple function calls */
- oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+ /* pixel column, 1-based */
+ x = PG_GETARG_INT32(2);
+ _x = x - 1;
- /* pgraster is null, return nothing */
- if (PG_ARGISNULL(0)) {
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+ /* pixel row, 1-based */
+ y = PG_GETARG_INT32(3);
+ _y = y - 1;
- raster = rt_raster_deserialize(pgraster, FALSE);
- if (!raster) {
- elog(ERROR, "RASTER_neighborhood: Could not deserialize raster");
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
+ /* distance */
+ distance = PG_GETARG_INT32(4);
+ if (distance < 1) {
+ elog(NOTICE, "Invalid value for distance (must be greater than zero). Returning NULL");
+ rt_raster_destroy(raster);
+ PG_RETURN_NULL();
+ }
+ distance = (uint16_t) distance;
- /* band index is 1-based */
- if (!PG_ARGISNULL(1))
- bandindex = PG_GETARG_INT32(1);
- num_bands = rt_raster_get_num_bands(raster);
- if (bandindex < 1 || bandindex > num_bands) {
- elog(NOTICE, "Invalid band index (must use 1-based). Returning NULL");
- rt_raster_destroy(raster);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
+ /* exclude_nodata_value flag */
+ if (!PG_ARGISNULL(5))
+ exclude_nodata_value = PG_GETARG_BOOL(5);
+
+ /* get band */
+ band = rt_raster_get_band(raster, bandindex - 1);
+ if (!band) {
+ elog(NOTICE, "Could not find band at index %d. Returning NULL", bandindex);
+ rt_raster_destroy(raster);
+ PG_RETURN_NULL();
+ }
- /* pixel column, 1-based */
- x = PG_GETARG_INT32(2);
+ /* get neighborhood */
+ count = rt_band_get_nearest_pixel(
+ band,
+ _x, _y,
+ distance,
+ exclude_nodata_value,
+ &npixels
+ );
+ /* error or no neighbors */
+ if (count < 1) {
+ /* error */
+ if (count < 0)
+ elog(NOTICE, "Unable to get the pixel's neighborhood for band at index %d", bandindex);
+ /* no neighbors */
+ else
+ elog(NOTICE, "Pixel has no neighbors for band at distance %d", distance);
+
+ rt_band_destroy(band);
+ rt_raster_destroy(raster);
- /* pixel row, 1-based */
- y = PG_GETARG_INT32(3);
+ PG_RETURN_NULL();
+ }
- /* distance */
- distance = PG_GETARG_INT32(4);
- if (distance < 1) {
- elog(NOTICE, "Invalid value for distance (must be greater than zero). Returning NULL");
+ /* get pixel's value */
+ if (
+ (_x >= 0 && _x < rt_band_get_width(band)) &&
+ (_y >= 0 && _y < rt_band_get_height(band))
+ ) {
+ if (rt_band_get_pixel(
+ band,
+ _x, _y,
+ &pixval
+ ) < 0) {
+ elog(NOTICE, "Unable to get the pixel of band at index %d. Returning NULL", bandindex);
+ rt_band_destroy(band);
rt_raster_destroy(raster);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
+ PG_RETURN_NULL();
}
+ }
+ /* outside band extent, set to NODATA */
+ else {
+ /* has NODATA, use NODATA */
+ if (rt_band_get_hasnodata_flag(band))
+ pixval = rt_band_get_nodata(band);
+ /* no NODATA, use min possible value */
+ else
+ pixval = rt_band_get_min_value(band);
+ }
+ POSTGIS_RT_DEBUGF(4, "pixval: %f", pixval);
- /* exclude_nodata_value flag */
- if (!PG_ARGISNULL(5))
- exclude_nodata_value = PG_GETARG_BOOL(5);
+ /* add pixel to neighborhood */
+ if (
+ !exclude_nodata_value || (
+ exclude_nodata_value &&
+ (rt_band_get_hasnodata_flag(band) != FALSE) && (
+ FLT_NEQ(pixval, rt_band_get_nodata(band)) &&
+ (rt_band_clamped_value_is_nodata(band, pixval) != 1)
+ )
+ )
+ ) {
+ count++;
+ npixels = (rt_pixel) repalloc(npixels, sizeof(struct rt_pixel_t) * count);
+ if (npixels == NULL) {
+ elog(ERROR, "RASTER_neighborhood: Unable to reallocate memory for neighborhood");
- /* get band */
- band = rt_raster_get_band(raster, bandindex - 1);
- if (!band) {
- elog(NOTICE, "Could not find band at index %d. Returning NULL", bandindex);
+ rt_band_destroy(band);
rt_raster_destroy(raster);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- /* get neighborhood */
- count = rt_band_get_nearest_pixel(
- band,
- x - 1, y - 1,
- (uint16_t) distance,
- exclude_nodata_value,
- &npixel1
- );
- rt_band_destroy(band);
- rt_raster_destroy(raster);
- /* error or no neighbors */
- if (count < 1) {
- /* error */
- if (count < 0)
- elog(NOTICE, "Unable to get the pixel's neighborhood for band at index %d", bandindex);
- /* no neighbors */
- else
- elog(NOTICE, "Pixel has no neighbors for band at distance %d", distance);
-
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
+ PG_RETURN_NULL();
}
- /* Store needed information */
- funcctx->user_fctx = npixel1;
-
- /* total number of tuples to be returned */
- funcctx->max_calls = count;
-
- /* Build a tuple descriptor for our result type */
- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) {
- ereport(ERROR, (
- errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg(
- "function returning record called in context "
- "that cannot accept type record"
- )
- ));
- }
+ npixels[count - 1].x = _x;
+ npixels[count - 1].y = _y;
+ npixels[count - 1].nodata = 0;
+ npixels[count - 1].value = pixval;
+ }
- BlessTupleDesc(tupdesc);
- funcctx->tuple_desc = tupdesc;
+ /* free unnecessary stuff */
+ rt_band_destroy(band);
+ rt_raster_destroy(raster);
- MemoryContextSwitchTo(oldcontext);
+ /* convert set of rt_pixel to 2D array */
+ length = rt_pixel_set_to_array(
+ npixels, count,
+ _x, _y,
+ distance,
+ &value2D,
+ &nodata2D
+ );
+ pfree(npixels);
+ if (!length) {
+ elog(NOTICE, "Unable to create 2D array of neighborhood");
+ PG_RETURN_NULL();
}
- /* stuff done on every call of the function */
- funcctx = SRF_PERCALL_SETUP();
+ /* dimensions of the PG array */
+ dim[0] = length;
+ dim[1] = length;
- call_cntr = funcctx->call_cntr;
- max_calls = funcctx->max_calls;
- tupdesc = funcctx->tuple_desc;
- npixel2 = funcctx->user_fctx;
+ /* 1D arrays for values and nodata from 2D arrays */
+ value1D = palloc(sizeof(Datum) * length * length);
+ nodata1D = palloc(sizeof(bool) * length * length);
- /* do when there is more left to send */
- if (call_cntr < max_calls) {
- int values_length = 3;
- Datum values[values_length];
- bool *nulls = NULL;
- HeapTuple tuple;
- Datum result;
+ if (value1D == NULL || nodata1D == NULL) {
+ elog(ERROR, "RASTER_neighborhood: Unable to allocate memory for return 2D array");
- POSTGIS_RT_DEBUGF(3, "Result %d", call_cntr);
+ for (i = 0; i < length; i++) {
+ pfree(value2D[i]);
+ pfree(nodata2D[i]);
+ }
+ pfree(value2D);
+ pfree(nodata2D);
- nulls = palloc(sizeof(bool) * values_length);
- memset(nulls, FALSE, values_length);
+ PG_RETURN_NULL();
+ }
- /* x,y are 0-based, make 1-based for end users */
- values[0] = Int64GetDatum(npixel2[call_cntr].x + 1);
- values[1] = Int64GetDatum(npixel2[call_cntr].y + 1);
- values[2] = Float8GetDatum(npixel2[call_cntr].value);
+ /* copy values from 2D arrays to 1D arrays */
+ k = 0;
+ for (i = 0; i < length; i++) {
+ for (j = 0; j < length; j++) {
+ nodata1D[k] = (bool) nodata2D[i][j];
+ if (!nodata1D[k])
+ value1D[k] = Float8GetDatum(value2D[i][j]);
+ else
+ value1D[k] = PointerGetDatum(NULL);
- /* build a tuple */
- tuple = heap_form_tuple(tupdesc, values, nulls);
+ k++;
+ }
+ }
- /* make the tuple into a datum */
- result = HeapTupleGetDatum(tuple);
+ /* no more need for 2D arrays */
+ for (i = 0; i < length; i++) {
+ pfree(value2D[i]);
+ pfree(nodata2D[i]);
+ }
+ pfree(value2D);
+ pfree(nodata2D);
- /* clean up */
- pfree(nulls);
+ /* info about the type of item in the multi-dimensional array (float8). */
+ get_typlenbyvalalign(FLOAT8OID, &typlen, &typbyval, &typalign);
- SRF_RETURN_NEXT(funcctx, result);
- }
- /* do when there is no more left */
- else {
- pfree(npixel2);
- SRF_RETURN_DONE(funcctx);
- }
+ mdArray = construct_md_array(
+ value1D, nodata1D,
+ 2, dim, lbound,
+ FLOAT8OID,
+ typlen, typbyval, typalign
+ );
+
+ pfree(value1D);
+ pfree(nodata1D);
+ PG_RETURN_ARRAYTYPE_P(mdArray);
}
/**
rast raster, band integer,
ix integer, iy integer,
distance integer,
- exclude_nodata_value boolean DEFAULT TRUE,
- OUT x integer, OUT y integer,
- OUT val double precision
+ exclude_nodata_value boolean DEFAULT TRUE
)
- RETURNS SETOF record
+ RETURNS double precision[][]
AS 'MODULE_PATHNAME', 'RASTER_neighborhood'
LANGUAGE 'C' IMMUTABLE STRICT;
rast raster,
ix integer, iy integer,
distance integer,
- exclude_nodata_value boolean DEFAULT TRUE,
- OUT x integer, OUT y integer,
- OUT val double precision
+ exclude_nodata_value boolean DEFAULT TRUE
)
- RETURNS SETOF record
- AS $$ SELECT x, y, val FROM st_neighborhood($1, 1, $2, $3, $4, $5) $$
+ RETURNS double precision[][]
+ AS $$ SELECT st_neighborhood($1, 1, $2, $3, $4, $5) $$
LANGUAGE 'SQL' IMMUTABLE STRICT;
CREATE OR REPLACE FUNCTION st_neighborhood(
rast raster, band integer,
pt geometry,
distance integer,
- exclude_nodata_value boolean DEFAULT TRUE,
- OUT x integer, OUT y integer,
- OUT val double precision
+ exclude_nodata_value boolean DEFAULT TRUE
)
- RETURNS SETOF record
+ RETURNS double precision[][]
AS $$
DECLARE
wx int;
wy int;
+ rtn double precision[][];
BEGIN
IF (st_geometrytype($3) != 'ST_Point') THEN
RAISE EXCEPTION 'Attempting to get the neighbor of a pixel with a non-point geometry';
wx := st_x($3);
wy := st_y($3);
- RETURN QUERY
- SELECT x, y, val
- FROM st_neighborhood(
- $1, $2,
- st_world2rastercoordx(rast, wx, wy),
- st_world2rastercoordy(rast, wx, wy),
- $4,
- $5
- );
+ SELECT st_neighborhood(
+ $1, $2,
+ st_world2rastercoordx(rast, wx, wy),
+ st_world2rastercoordy(rast, wx, wy),
+ $4,
+ $5
+ ) INTO rtn;
+ RETURN rtn;
END;
$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
rast raster,
pt geometry,
distance integer,
- exclude_nodata_value boolean DEFAULT TRUE,
- OUT x integer, OUT y integer,
- OUT val double precision
+ exclude_nodata_value boolean DEFAULT TRUE
)
- RETURNS SETOF record
- AS $$ SELECT x, y, val FROM st_neighborhood($1, 1, $2, $3, $4) $$
+ RETURNS double precision[][]
+ AS $$ SELECT st_neighborhood($1, 1, $2, $3, $4) $$
LANGUAGE 'SQL' IMMUTABLE STRICT;
------------------------------------------------------------------------------
const int maxY = 10;
rt_pixel npixels = NULL;
+ int length;
+ double **value;
+ int **nodata;
+
rast = rt_raster_new(maxX, maxY);
assert(rast);
&npixels
);
CHECK((rtn == 2));
+
+ length = rt_pixel_set_to_array(
+ npixels, rtn,
+ -1, 1,
+ 1,
+ &value,
+ &nodata
+ );
+ CHECK((length == 3));
+
+ for (x = 0; x < length; x++) {
+ rtdealloc(nodata[x]);
+ rtdealloc(value[x]);
+ }
+
+ rtdealloc(nodata);
+ rtdealloc(value);
+
if (rtn)
rtdealloc(npixels);
NOTICE: table "raster_neighborhood" does not exist, skipping
-(1,2,1)
-(2,2,1)
-(2,1,1)
-(2,1,1)
-(3,1,1)
-(1,3,1)
-(3,3,1)
-(1,2,1)
-(3,2,1)
-(4,4,1)
-(5,4,1)
-(6,4,1)
-(4,6,1)
-(5,6,1)
-(6,6,1)
-(4,5,1)
-(4,4,1)
-(5,4,1)
-(6,4,1)
-(4,6,1)
-(5,6,1)
-(6,6,1)
-(4,5,1)
-(3,3,1)
-(4,3,1)
-(6,3,1)
-(7,3,1)
-(3,7,1)
-(5,7,1)
-(6,7,1)
-(3,4,1)
-(3,6,1)
-(7,4,1)
-(7,5,1)
-(7,6,1)
-(10,10,1)
+{{NULL,NULL,NULL},{NULL,NULL,1},{NULL,1,1}}
+{{NULL,1,1},{1,1,NULL},{1,1,1}}
+{{1,1,1},{1,1,1},{1,NULL,1}}
+{{1,1,NULL,1,1},{1,1,1,1,NULL},{NULL,1,1,1,1},{1,1,NULL,1,1},{1,1,1,1,NULL}}
+{{1,NULL,NULL},{NULL,NULL,NULL},{NULL,NULL,NULL}}
NOTICE: Pixel has no neighbors for band at distance 1
NOTICE: Pixel has no neighbors for band at distance 1
-(1,3,1)
-(1,2,1)
+{{NULL,NULL,NULL},{NULL,NULL,NULL},{NULL,1,1}}
NOTICE: Pixel has no neighbors for band at distance 1
NOTICE: Pixel has no neighbors for band at distance 3
-(-10,2,0)
-(-9,2,0)
-(-8,2,0)
-(-10,4,0)
-(-9,4,0)
-(-8,4,0)
-(-10,3,0)
-(-8,3,0)
-(-11,1,0)
-(-10,1,0)
-(-9,1,0)
-(-8,1,0)
-(-7,1,0)
-(-11,5,0)
-(-10,5,0)
-(-9,5,0)
-(-8,5,0)
-(-7,5,0)
-(-11,2,0)
-(-11,3,0)
-(-11,4,0)
-(-7,2,0)
-(-7,3,0)
-(-7,4,0)
-(-12,0,0)
-(-11,0,0)
-(-10,0,0)
-(-9,0,0)
-(-8,0,0)
-(-7,0,0)
-(-6,0,0)
-(-12,6,0)
-(-11,6,0)
-(-10,6,0)
-(-9,6,0)
-(-8,6,0)
-(-7,6,0)
-(-6,6,0)
-(-12,1,0)
-(-12,2,0)
-(-12,3,0)
-(-12,4,0)
-(-12,5,0)
-(-6,1,0)
-(-6,2,0)
-(-6,3,0)
-(-6,4,0)
-(-6,5,0)
+{{0,0,0,0,0,0,0},{0,0,0,0,0,0,0},{0,0,0,0,0,0,0},{0,0,0,0,0,0,0},{0,0,0,0,0,0,0},{0,0,0,0,0,0,0},{0,0,0,0,0,0,0}}