diff --git a/README.md b/README.md new file mode 100644 index 0000000..bef41bf --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# Copernicus Global Land Service Resampling Tool Using R + +This notebook shows how to resample Copernicus Global Land Service ([CGLS](https://land.copernicus.eu/global/)) vegetation-related products (i.e. NDVI, FAPAR...) from 333m resolution to 1km using R-based packages and functions. + +It is intended for users who would like to continue temporarily their 1km time series, in near real time, before switching to the new 333m baseline resolution. + +The user can apply the resample method shown here on older (non-near real time) 333m products and correlate the results with the 1km product of the same period. [This](https://github.com/xavi-rp/ResampleTool_notebook/blob/master/Resample_Report_v2.5.pdf) document provides the results of such comparisons between 1km-resampled and 1km-produced data layers. + + + diff --git a/ResampleTool_R_notebook.Rmd b/ResampleTool_R_notebook.Rmd index b5c621f..7e2ff00 100644 --- a/ResampleTool_R_notebook.Rmd +++ b/ResampleTool_R_notebook.Rmd @@ -22,44 +22,50 @@ For more details on the products, please see the description and Product User Ma -# Step 2: Reading in the data and dealing with invalid (no-data) pixels +# Step 2: Reading in the data -Once the data set is available, *raster* package functionality is used to prepare it for resampling. The use of the *knitr* package is optional, it only helps with pretty-printing tables. +Once the data set is available, *ncdf4* and *raster* packages functionalities are used to prepare it for resampling. Raw digital numbers (DN) stored in the netCFD files are used in order to avoid floating point imprecisions when scaling the values into real physical values (PV). R uses IEEE-754-Standard double-precision floating-point numbers, whereas the values in the netCDF are stored as float32. DN values can be scaled into PV aftewards using the formula PV = DN \* *scale* + *offset*. + +The use of the *knitr* package is optional, it only helps with pretty-printing tables. ```{r} -library(raster) +if(require(ncdf4) == FALSE){install.packages("ncdf4", repos = "https://cloud.r-project.org"); library(ncdf4)} else {library(ncdf4)} +if(require(raster) == FALSE){install.packages("raster", repos = "https://cloud.r-project.org"); library(raster)} else {library(raster)} if(require(knitr) == FALSE){install.packages("knitr", repos = "https://cloud.r-project.org"); library(knitr)} else {library(knitr)} -# ndvi_files is a list of the available files (netCFD or Raster* objects) -ndvi_files <- list(paste0(getwd(), "/ndvi300m_Cat_kk.tif")) -r <- raster(ndvi_files[[1]]) +# ndvi_files is a list of the available files (Raster* objects or netCDF) +ndvi_files <- list(paste0(getwd(), "/ndvi300m_Cat.tif")) ``` +  -The original Global Land product files typically come in two flavours: the global netCDF4 files (the native production format) or GeoTIFF subsets. Both can contain specific values (flags) for invalid pixels, which need to be dealt with. For this example, we’ll convert those into NoData (NA) values. - -The range of valid pixel values, the scale factor and offset and any flag values used are described in the product documentation and netCDF file metadata. - -For your convenience, a short table was prepared summarizing the range of valid values, both in raw digital number (DN) and physical value, for each product, version and data layer. Let’s read in this table from the csv file. - -The physical or real value is computed as digital number * scale + offset, but this applies only for valid pixels. - -```{r echo = FALSE} -cutoff_method_df <- read.csv(paste0(getwd(), "/Table_cutoff_and_resampleMethod.csv"), - stringsAsFactors = FALSE, - header = TRUE) -kable(cutoff_method_df[, 1:8], caption = "") +The original Global Land product files typically come in two flavours: the global netCDF4 files (the native production format) or GeoTIFF subsets. If the file is a GeoTIFF: +```{r} +if (grepl(".tif$", ndvi_files[[1]])){ + r <- raster(ndvi_files[[1]]) +} ``` -For example, in the 300m NDVI products, digital values > 250 are flagged and need to be converted into NA. When netCDF files are read in as a *Raster*\* object, the digital values are scaled into real NDVI values automatically. To make sure to not use any of the invalid (flagged) pixels, we’ll have to be set all pixels with > 0.92 (=DN 250 x scale + offset) to NA. - +  +If the file is a netCDF4: ```{r} -cutoff_flag <- 0.92 - -r[r > cutoff_flag] <- NA +if (grepl(".nc$", ndvi_files[[1]])){ + nc <- nc_open(ndvi_files[[1]]) + lon <- ncvar_get(nc, "lon") + lat <- ncvar_get(nc, "lat") + #Copernicus nc files have lat/long belonging to the centre of the pixel, and R uses upper/left corner; + #therefore, coordinates need to be adjusted + lon <- lon - (1/336)/2 # 1/336 is the pixel resolution of the 333m product + lat <- lat + (1/336)/2 + nc_data <- ncvar_get(nc, "NDVI", raw_datavals = TRUE) # raw_datavals = TRUE to avoid automatic scaling of DN + r <- raster(t(nc_data[, seq(dim(nc_data)[2], 1, -1)])) # creating a raster object + extent(r) <- c(range(lon)[1], (range(lon)[2] + (1/336)), + (range(lat)[1] - (1/336)), range(lat)[2]) # extent of the product + crs(r) <- CRS('+init=EPSG:4326') # coordinate reference system +} ``` - +**Note:** *raster::raster()* can also read *nc* files (using *ncdf4* functionalities), but Digital Numbers are automatically scaled to physical values. As said before, we want to avoid this because of floating point imprecisions. # Step 3: Checking the geographic extent and raster cropping @@ -77,7 +83,7 @@ This determines the next steps and raster cropping to be performed, so let’s t ## Option 1: resampling an entire global 300m raster -**Note:** File formats like netCDF4 and GeoTIFF can be optimized for reading or storing large rasters through chunking and compression. Take care when reading in such a full global raster in your computer’s working memory in one go. A global 300m raster has 120960 columns and 47040 rows, or close to 5.7bn cells. The size in bytes of each cell is provided in the CSV file (column Bytes per Cell; see table above). Consider working in subsets or using parallel computing techniques (rasterOptions, cluster processing, etc.), as described [here](https://strimas.com/post/processing-large-rasters-in-r/). +**Note:** File formats like netCDF4 and GeoTIFF can be optimized for reading or storing large rasters through chunking and compression. Take care when reading in such a full global raster in your computer’s working memory in one go. A global 300m raster has 120960 columns and 47040 rows, or close to 5.7bn cells. The size in bytes of each cell is provided in the CSV file (column Bytes per Cell; see table below). Consider working in subsets or using parallel computing techniques (rasterOptions, cluster processing, etc.), as described [here](https://strimas.com/post/processing-large-rasters-in-r/). Given the grid definition of the global 300m and 1km products (see image below), no proper aggregation of the finer resolution product can be performed at the minimum and maximum latitude and longitude. For this reason, the 300m *RasterLayer* object needs to be cropped accordingly. @@ -155,7 +161,57 @@ if(!all(round(my_extent[1], 7) %in% round(x_ext, 7) & ``` -# Step 4: Resampling using the aggregation approach + +# Step 4: Dealing with invalid (no-data) pixels + +The original Global Land product can contain specific values (flags) for invalid pixels, which need to be dealt with. For this example, we’ll convert those into NoData (NA) values. + +The range of valid pixel values, the scale factor and offset and any flag values used are described in the product documentation and netCDF file metadata. + +For your convenience, a short table was prepared summarizing the range of valid values, both in raw digital number (DN) and physical value, for each product, version and data layer. Let’s read in this table from the csv file. + + +```{r echo = FALSE} +cutoff_method_df <- read.csv(paste0(getwd(), "/Table_cutoff_and_resampleMethod.csv"), + stringsAsFactors = FALSE, + header = TRUE) +kable(cutoff_method_df[, 1:8], caption = "") +``` + +As an example, in the 300m NDVI products, digital values > 250 are flagged and need to be converted into NA. + + +```{r} +cutoff_flag <- 250 + +r[r > cutoff_flag] <- NA +``` + +  + +Now we can scale the digital numbers into physical values. As said before, the physical or real value is computed as digital number * scale + offset, but this applies only for valid pixels. + + +```{r} +if (exists("nc")){ + # Retrieving scale factor and offset from netCDF metadata + scale_fact <- nc$var$NDVI$scaleFact + offset_val <- nc$var$NDVI$addOffset +}else{ + # They can be set manually as well + scale_fact <- 0.00400000018998981 # double-precision floating-point + offset_val <- -0.07999999821186066 +} + +r <- r * scale_fact + offset_val +r # To check that the values are correctly scaled (e.g. for NDVI between -0.08 and 0.92) + +``` + +  + + +# Step 5: Resampling using the aggregation approach There are several approaches to resample data to a coarser resolution. The area-based aggregation methods group rectangular areas of cells of the finer resolution image to create a new map with larger cells. @@ -169,7 +225,7 @@ The following table recommends the best suited method for each product/layer. kable(cutoff_method_df[, c(1:3, 9)], caption = "") ``` -**Note:** *QFLAG cannot be resampled due to the different implementations for 1km-v2 and 300m-v1 products. +**Note:** Resampled QFLAG, LENGTH_BEFORE/AFTER and NOBS cannot be compared to the 1km products due to different implementations for 1km-v2 and 300m-v1 products. For example, LAI-NOBS ranges are 0-120 for 1km-v2 and 0-40 for 300m-v1, or LAI/FAPAR/FCOVER-LENGTH_BEFORE go up to 60 days and up to 210 days, respectively for both products. Now the process of resampling itself can go ahead using *aggregate()*. The resample method can be assigned at this point. @@ -225,11 +281,13 @@ r300m_resampled1km_Aggr <- aggregate(r, fun = aggr_method, na.rm = TRUE, filename = '') -``` +# Only when working with Digital Numbers (not in physical values): +# r300m_resampled1km_Aggr <- round(r300m_resampled1km_Aggr) +``` -# Step 5: Check the outcome and final remarks +# Step 6: Check the outcome and final remarks Here are a couple of plots in case the user wants to take a look at the resampled map. @@ -244,4 +302,4 @@ plot(r300m_resampled1km_Aggr, main = 'Resampled map to 1km') In addition, you could apply the chosen resample method on older (non-near real time) 300m products and correlate the results with the 1km product of the same period. -This document provides the results of such comparisons between 1km-resampled and 1km-produced data layers. +[This](https://github.com/xavi-rp/ResampleTool_notebook/blob/master/Resample_Report_v2.5.pdf) document provides the results of such comparisons between 1km-resampled and 1km-produced data layers. diff --git a/ResampleTool_R_notebook.ipynb b/ResampleTool_R_notebook.ipynb index c307169..b89176e 100644 --- a/ResampleTool_R_notebook.ipynb +++ b/ResampleTool_R_notebook.ipynb @@ -40,61 +40,58 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Step 2: Reading in the data and dealing with invalid (no-data) pixels\n", + "# Step 2: Reading in the data\n", "\n", - "Once the data set is available, *raster* package functionality is used to prepare it for resampling. The use of the *knitr* package is optional, it only helps with pretty-printing tables." + "Once the data set is available, *ncdf4* and *raster* packages functionalities are used to prepare it for resampling. Raw digital numbers (DN) stored in the netCFD files are used in order to avoid floating point imprecisions when scaling the values into real physical values (PV). R uses IEEE-754-Standard double-precision floating-point numbers, whereas the values in the netCDF are stored as float32. DN values can be scaled into PV aftewards using the formula PV = DN \\* *scale* + *offset*.\n", + "\n", + "The use of the *knitr* package is optional, it only helps with pretty-printing tables." ] }, { "cell_type": "code", "execution_count": null, - "metadata": { - "lines_to_next_cell": 2 - }, + "metadata": {}, "outputs": [], "source": [ - "library(raster)\n", + "if(require(ncdf4) == FALSE){install.packages(\"ncdf4\", repos = \"https://cloud.r-project.org\"); library(ncdf4)} else {library(ncdf4)}\n", + "if(require(raster) == FALSE){install.packages(\"raster\", repos = \"https://cloud.r-project.org\"); library(raster)} else {library(raster)}\n", "if(require(knitr) == FALSE){install.packages(\"knitr\", repos = \"https://cloud.r-project.org\"); library(knitr)} else {library(knitr)}\n", "\n", - "# ndvi_files is a list of the available files (netCFD or Raster* objects)\n", - "ndvi_files <- list(paste0(getwd(), \"/ndvi300m_Cat_kk.tif\"))\n", - "r <- raster(ndvi_files[[1]])" + "# ndvi_files is a list of the available files (Raster* objects or netCDF)\n", + "ndvi_files <- list(paste0(getwd(), \"/ndvi300m_Cat.tif\"))" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "lines_to_next_cell": 0 + }, "source": [ - "The original Global Land product files typically come in two flavours: the global netCDF4 files (the native production format) or GeoTIFF subsets. Both can contain specific values (flags) for invalid pixels, which need to be dealt with. For this example, we’ll convert those into NoData (NA) values.\n", - "\n", - "The range of valid pixel values, the scale factor and offset and any flag values used are described in the product documentation and netCDF file metadata.\n", - "\n", - "For your convenience, a short table was prepared summarizing the range of valid values, both in raw digital number (DN) and physical value, for each product, version and data layer. Let’s read in this table from the csv file. \n", + " \n", "\n", - "The physical or real value is computed as digital number * scale + offset, but this applies only for valid pixels." + "The original Global Land product files typically come in two flavours: the global netCDF4 files (the native production format) or GeoTIFF subsets. If the file is a GeoTIFF:" ] }, { "cell_type": "code", "execution_count": null, - "metadata": { - "tags": [ - "remove_input" - ] - }, + "metadata": {}, "outputs": [], "source": [ - "cutoff_method_df <- read.csv(paste0(getwd(), \"/Table_cutoff_and_resampleMethod.csv\"),\n", - " stringsAsFactors = FALSE,\n", - " header = TRUE)\n", - "kable(cutoff_method_df[, 1:8], caption = \"\")" + "if (grepl(\".tif$\", ndvi_files[[1]])){ \n", + " r <- raster(ndvi_files[[1]])\n", + "}" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "lines_to_next_cell": 0 + }, "source": [ - "For example, in the 300m NDVI products, digital values > 250 are flagged and need to be converted into NA. When netCDF files are read in as a *Raster*\\* object, the digital values are scaled into real NDVI values automatically. To make sure to not use any of the invalid (flagged) pixels, we’ll have to be set all pixels with > 0.92 (=DN 250 x scale + offset) to NA.\n" + " \n", + "\n", + "If the file is a netCDF4:" ] }, { @@ -105,15 +102,28 @@ }, "outputs": [], "source": [ - "cutoff_flag <- 0.92\n", - "\n", - "r[r > cutoff_flag] <- NA" + "if (grepl(\".nc$\", ndvi_files[[1]])){\n", + " nc <- nc_open(ndvi_files[[1]])\n", + " lon <- ncvar_get(nc, \"lon\")\n", + " lat <- ncvar_get(nc, \"lat\")\n", + " #Copernicus nc files have lat/long belonging to the centre of the pixel, and R uses upper/left corner;\n", + " #therefore, coordinates need to be adjusted\n", + " lon <- lon - (1/336)/2 # 1/336 is the pixel resolution of the 333m product\n", + " lat <- lat + (1/336)/2\n", + " nc_data <- ncvar_get(nc, \"NDVI\", raw_datavals = TRUE) # raw_datavals = TRUE to avoid automatic scaling of DN\n", + " r <- raster(t(nc_data[, seq(dim(nc_data)[2], 1, -1)])) # creating a raster object\n", + " extent(r) <- c(range(lon)[1], (range(lon)[2] + (1/336)),\n", + " (range(lat)[1] - (1/336)), range(lat)[2]) # extent of the product \n", + " crs(r) <- CRS('+init=EPSG:4326') # coordinate reference system\n", + "}" ] }, { "cell_type": "markdown", "metadata": {}, - "source": [] + "source": [ + "**Note:** *raster::raster()* can also read *nc* files (using *ncdf4* functionalities), but Digital Numbers are automatically scaled to physical values. As said before, we want to avoid this because of floating point imprecisions." + ] }, { "cell_type": "markdown", @@ -138,7 +148,7 @@ "source": [ "## Option 1: resampling an entire global 300m raster\n", "\n", - "**Note:** File formats like netCDF4 and GeoTIFF can be optimized for reading or storing large rasters through chunking and compression. Take care when reading in such a full global raster in your computer’s working memory in one go. A global 300m raster has 120960 columns and 47040 rows, or close to 5.7bn cells. The size in bytes of each cell is provided in the CSV file (column Bytes per Cell; see table above). Consider working in subsets or using parallel computing techniques (rasterOptions, cluster processing, etc.), as described [here](https://strimas.com/post/processing-large-rasters-in-r/).\n", + "**Note:** File formats like netCDF4 and GeoTIFF can be optimized for reading or storing large rasters through chunking and compression. Take care when reading in such a full global raster in your computer’s working memory in one go. A global 300m raster has 120960 columns and 47040 rows, or close to 5.7bn cells. The size in bytes of each cell is provided in the CSV file (column Bytes per Cell; see table below). Consider working in subsets or using parallel computing techniques (rasterOptions, cluster processing, etc.), as described [here](https://strimas.com/post/processing-large-rasters-in-r/).\n", "\n", "Given the grid definition of the global 300m and 1km products (see image below), no proper aggregation of the finer resolution product can be performed at the minimum and maximum latitude and longitude. For this reason, the 300m *RasterLayer* object needs to be cropped accordingly.\n", "\n", @@ -233,7 +243,7 @@ "execution_count": null, "metadata": { "echo": true, - "lines_to_next_cell": 2 + "lines_to_next_cell": 0 }, "outputs": [], "source": [ @@ -259,11 +269,99 @@ "}" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Step 4: Resampling using the aggregation approach\n", + "# Step 4: Dealing with invalid (no-data) pixels\n", + "\n", + "The original Global Land product can contain specific values (flags) for invalid pixels, which need to be dealt with. For this example, we’ll convert those into NoData (NA) values.\n", + "\n", + "The range of valid pixel values, the scale factor and offset and any flag values used are described in the product documentation and netCDF file metadata.\n", + "\n", + "For your convenience, a short table was prepared summarizing the range of valid values, both in raw digital number (DN) and physical value, for each product, version and data layer. Let’s read in this table from the csv file. \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "remove_input" + ] + }, + "outputs": [], + "source": [ + "cutoff_method_df <- read.csv(paste0(getwd(), \"/Table_cutoff_and_resampleMethod.csv\"),\n", + " stringsAsFactors = FALSE,\n", + " header = TRUE)\n", + "kable(cutoff_method_df[, 1:8], caption = \"\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As an example, in the 300m NDVI products, digital values > 250 are flagged and need to be converted into NA. \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cutoff_flag <- 250\n", + "\n", + "r[r > cutoff_flag] <- NA" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " \n", + "\n", + "Now we can scale the digital numbers into physical values. As said before, the physical or real value is computed as digital number * scale + offset, but this applies only for valid pixels.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "if (exists(\"nc\")){\n", + " # Retrieving scale factor and offset from netCDF metadata\n", + " scale_fact <- nc$var$NDVI$scaleFact \n", + " offset_val <- nc$var$NDVI$addOffset\n", + "}else{\n", + " # They can be set manually as well\n", + " scale_fact <- 0.00400000018998981 # double-precision floating-point\n", + " offset_val <- -0.07999999821186066\n", + "}\n", + "\n", + "r <- r * scale_fact + offset_val\n", + "r # To check that the values are correctly scaled (e.g. for NDVI between -0.08 and 0.92)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Step 5: Resampling using the aggregation approach\n", "\n", "There are several approaches to resample data to a coarser resolution. The area-based aggregation methods group rectangular areas of cells of the finer resolution image to create a new map with larger cells. \n", "\n", @@ -291,7 +389,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note:** *QFLAG cannot be resampled due to the different implementations for 1km-v2 and 300m-v1 products.\n", + "**Note:** Resampled QFLAG, LENGTH_BEFORE/AFTER and NOBS cannot be compared to the 1km products due to different implementations for 1km-v2 and 300m-v1 products. For example, LAI-NOBS ranges are 0-120 for 1km-v2 and 0-40 for 300m-v1, or LAI/FAPAR/FCOVER-LENGTH_BEFORE go up to 60 days and up to 210 days, respectively for both products.\n", "\n", "Now the process of resampling itself can go ahead using *aggregate()*. The resample method can be assigned at this point." ] @@ -300,7 +398,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "lines_to_next_cell": 0 + "lines_to_next_cell": 2 }, "outputs": [], "source": [ @@ -354,19 +452,17 @@ " fact = 3, # from 333m to 1km \n", " fun = aggr_method, \n", " na.rm = TRUE, \n", - " filename = '')" + " filename = '')\n", + "\n", + "# Only when working with Digital Numbers (not in physical values):\n", + "# r300m_resampled1km_Aggr <- round(r300m_resampled1km_Aggr)" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Step 5: Check the outcome and final remarks\n", + "# Step 6: Check the outcome and final remarks\n", "\n", "Here are a couple of plots in case the user wants to take a look at the resampled map." ] @@ -397,13 +493,13 @@ "source": [ "In addition, you could apply the chosen resample method on older (non-near real time) 300m products and correlate the results with the 1km product of the same period.\n", "\n", - "This document provides the results of such comparisons between 1km-resampled and 1km-produced data layers." + "[This](https://github.com/xavi-rp/ResampleTool_notebook/blob/master/Resample_Report_v2.5.pdf) document provides the results of such comparisons between 1km-resampled and 1km-produced data layers." ] } ], "metadata": { "jupytext": { - "cell_metadata_filter": "echo,tags,-all", + "cell_metadata_filter": "tags,echo,-all", "main_language": "R", "notebook_metadata_filter": "-all" } diff --git a/ResampleTool_R_notebook.nb.html b/ResampleTool_R_notebook.nb.html index 9ef6d08..f0ad4bf 100644 --- a/ResampleTool_R_notebook.nb.html +++ b/ResampleTool_R_notebook.nb.html @@ -1780,24 +1780,156 @@

Step 1: Download or locate the input 333m product files

Alternatively, if you are working locally on your PC or laptop, you can choose to automatically download Copernicus Global Land Service products using the functions found in https://github.com/cgls/Copernicus-Global-Land-Service-Data-Download-with-R.

For more details on the products, please see the description and Product User Manuals documentation at https://land.copernicus.eu/global/products/

-
-

Step 2: Reading in the data and dealing with invalid (no-data) pixels

-

Once the data set is available, raster package functionality is used to prepare it for resampling. The use of the knitr package is optional, it only helps with pretty-printing tables.

+
+

Step 2: Reading in the data

+

Once the data set is available, ncdf4 and raster packages functionalities are used to prepare it for resampling. Raw digital numbers (DN) stored in the netCFD files are used in order to avoid floating point imprecisions when scaling the values into real physical values (PV). R uses IEEE-754-Standard double-precision floating-point numbers, whereas the values in the netCDF are stored as float32. DN values can be scaled into PV aftewards using the formula PV = DN * scale + offset.

+

The use of the knitr package is optional, it only helps with pretty-printing tables.

- -
library(raster)
+
+
if(require(ncdf4) == FALSE){install.packages("ncdf4", repos = "https://cloud.r-project.org"); library(ncdf4)} else {library(ncdf4)}
+if(require(raster) == FALSE){install.packages("raster", repos = "https://cloud.r-project.org"); library(raster)} else {library(raster)}
 if(require(knitr) == FALSE){install.packages("knitr", repos = "https://cloud.r-project.org"); library(knitr)} else {library(knitr)}
-# ndvi_files is a list of the available files (netCFD or Raster* objects)
-ndvi_files <- list(paste0(getwd(), "/ndvi300m_Cat_kk.tif"))
-r <- raster(ndvi_files[[1]])
+# ndvi_files is a list of the available files (Raster* objects or netCDF) +ndvi_files <- list(paste0(getwd(), "/ndvi300m_Cat.tif"))
-

The original Global Land product files typically come in two flavours: the global netCDF4 files (the native production format) or GeoTIFF subsets. Both can contain specific values (flags) for invalid pixels, which need to be dealt with. For this example, we’ll convert those into NoData (NA) values.

+

 

+

The original Global Land product files typically come in two flavours: the global netCDF4 files (the native production format) or GeoTIFF subsets. If the file is a GeoTIFF:

+ + + +
if (grepl(".tif$", ndvi_files[[1]])){  
+  r <- raster(ndvi_files[[1]])
+}
+ + + +

 

+

If the file is a netCDF4:

+ + + +
if (grepl(".nc$", ndvi_files[[1]])){
+  nc  <- nc_open(ndvi_files[[1]])
+  lon <- ncvar_get(nc, "lon")
+  lat <- ncvar_get(nc, "lat")
+  #Copernicus nc files have lat/long belonging to the centre of the pixel, and R uses upper/left corner;
+  #therefore, coordinates need to be adjusted
+  lon <- lon - (1/336)/2                                   # 1/336 is the pixel resolution of the 333m product
+  lat <- lat + (1/336)/2
+  nc_data <- ncvar_get(nc, "NDVI", raw_datavals = TRUE)    # raw_datavals = TRUE to avoid automatic scaling of DN
+  r <- raster(t(nc_data[, seq(dim(nc_data)[2], 1, -1)]))   # creating a raster object
+  extent(r) <- c(range(lon)[1], (range(lon)[2] + (1/336)),
+                 (range(lat)[1] - (1/336)), range(lat)[2]) # extent of the product 
+  crs(r) <- CRS('+init=EPSG:4326')                         # coordinate reference system
+}
+ + + +

Note: raster::raster() can also read nc files (using ncdf4 functionalities), but Digital Numbers are automatically scaled to physical values. As said before, we want to avoid this because of floating point imprecisions.

+
+
+

Step 3: Checking the geographic extent and raster cropping

+

In the area-based resampling, we’ll aggregate a matrix of 3x3 pixels at the 333m resolution into a pixel at 1km resolution (aggregate() with factor 3). But before we do that, we need to make sure that the geographic extent matches nicely in both resolutions.

+

In your application, you may working with either

+ +

This determines the next steps and raster cropping to be performed, so let’s take a look at these two cases.

+
+

Option 1: resampling an entire global 300m raster

+

Note: File formats like netCDF4 and GeoTIFF can be optimized for reading or storing large rasters through chunking and compression. Take care when reading in such a full global raster in your computer’s working memory in one go. A global 300m raster has 120960 columns and 47040 rows, or close to 5.7bn cells. The size in bytes of each cell is provided in the CSV file (column Bytes per Cell; see table below). Consider working in subsets or using parallel computing techniques (rasterOptions, cluster processing, etc.), as described here.

+

Given the grid definition of the global 300m and 1km products (see image below), no proper aggregation of the finer resolution product can be performed at the minimum and maximum latitude and longitude. For this reason, the 300m RasterLayer object needs to be cropped accordingly.

+

+ + + +
if(extent(r)[1] < -180 & extent(r)[2] > 179.997 &
+   extent(r)[3] < -59.99554 & extent(r)[4] > 80){  # checking for full product (image)
+  extnt_r <- extent(r)
+  extnt_r[1] <- extent(r)[1] + (2 * (1 / 336)) # x-min
+  extnt_r[2] <- extent(r)[2] - (1 * (1 / 336)) # x-max
+  extnt_r[3] <- extent(r)[3] + (1 * (1 / 336))  # y-min
+  extnt_r[4] <- extent(r)[4] - (2 * (1 / 336))  # y-max
+  r <- crop(r, extnt_r)
+}else{
+  print("The image is not the full product; therefore, extent needs to be checked")
+}
+ + +
[1] "The image is not the full product; therefore, extent needs to be checked"
+ + + +
+
+

Option 2: resampling geographic subsets

+

If you want to resample a geographic subset of the original 300m product, its new extent should match with the 1km product grid.

+

This target 1km grid or extent can be either

+
    +
  • retrieved from a 1km Raster* object, i.e. one of the rasters in your existing 1km time series:
  • +
+ + + +
ndvi_files_1km <- list(paste0(getwd(), "/ndvi1km_Cat.tif"))
+r_1km <- raster(ndvi_files_1km[[1]])
+if(exists("r_1km") & all(round(res(r_1km), 10) == round(0.0089285714, 10))){
+   my_extent <- extent(r_1km)
+}else{
+  stop("The given raster file does not exist or does not have the 1km resolution.")
+}
+ + + +
    +
  • or provided as a vector with longitude/latitude coordinates, in decimal degrees, taking the form c(Xmin, Xmax, Ymin, Ymax). Notice that these coordinates might be slightly adjusted to the 1km PROBA-V products grid.
  • +
+ + + +
coords4subset <- c(0, 4, 40, 43)
+my_extent <- extent(coords4subset)
+ + + +

Next, we’ll check if the new extent is found in the PROBA-V 1km product grid (cell boundaries). If it’s not, it will be adjusted. Then, the product can be cropped to the new extent.

+ + + +
# The following vectors contain Long and Lat coordinates, respectively, of the 1km grid (cell boundaries):
+x_ext <- seq((-180 - ((1 / 112) / 2)), 180, (1/112))
+y_ext <- seq((80 + ((1 / 112) / 2)), - 60, - (1/112))
+ 
+if(!all(round(my_extent[1], 7) %in% round(x_ext, 7) &
+        round(my_extent[2], 7) %in% round(x_ext, 7) &
+        round(my_extent[3], 7) %in% round(y_ext, 7) &
+        round(my_extent[4], 7) %in% round(y_ext, 7))){
+  # The given extent from raster or coordinate vector does not fit into the 1km PROBA-V grid, so we are going to adjust it
+  for(crd in 1:length(as.vector(my_extent))){
+    if(crd <= 2){
+      my_extent[crd] <- x_ext[order(abs(x_ext - my_extent[crd]))][1]
+    }else{
+      my_extent[crd] <- y_ext[order(abs(y_ext - my_extent[crd]))][1]
+    }
+  }
+  
+  # Now we can crop the 300m raster
+  r <- crop(r, my_extent)
+}
+ + + +
+
+
+

Step 4: Dealing with invalid (no-data) pixels

+

The original Global Land product can contain specific values (flags) for invalid pixels, which need to be dealt with. For this example, we’ll convert those into NoData (NA) values.

The range of valid pixel values, the scale factor and offset and any flag values used are described in the product documentation and netCDF file metadata.

For your convenience, a short table was prepared summarizing the range of valid values, both in raw digital number (DN) and physical value, for each product, version and data layer. Let’s read in this table from the csv file.

-

The physical or real value is computed as digital number * scale + offset, but this applies only for valid pixels.

@@ -1838,16 +1970,6 @@

Step 2: Reading in the data and dealing with invalid (no-data) pixels

0.92 -NDVI 300m -v1 -NDVI_unc -2 -0 -1000 -0.00 -1.00 - - LAI 300m v1 LAI @@ -1857,7 +1979,7 @@

Step 2: Reading in the data and dealing with invalid (no-data) pixels

0.00 7.00 - + LAI 300m v1 RMSE @@ -1867,7 +1989,7 @@

Step 2: Reading in the data and dealing with invalid (no-data) pixels

0.00 7.00 - + LAI 300m v1 LENGTH_AFTER @@ -1877,7 +1999,7 @@

Step 2: Reading in the data and dealing with invalid (no-data) pixels

0.00 60.00 - + LAI 300m v1 LENGTH_BEFORE @@ -1887,7 +2009,7 @@

Step 2: Reading in the data and dealing with invalid (no-data) pixels

15.00 210.00 - + LAI 300m v1 NOBS @@ -1897,7 +2019,7 @@

Step 2: Reading in the data and dealing with invalid (no-data) pixels

0.00 40.00 - + LAI 300m v1 QFLAG @@ -1907,7 +2029,7 @@

Step 2: Reading in the data and dealing with invalid (no-data) pixels

0.00 255.00 - + FAPAR 300m v1 FAPAR @@ -1917,7 +2039,7 @@

Step 2: Reading in the data and dealing with invalid (no-data) pixels

0.00 0.94 - + FAPAR 300m v1 RMSE @@ -1927,7 +2049,7 @@

Step 2: Reading in the data and dealing with invalid (no-data) pixels

0.00 0.94 - + FAPAR 300m v1 LENGTH_AFTER @@ -1937,7 +2059,7 @@

Step 2: Reading in the data and dealing with invalid (no-data) pixels

0.00 60.00 - + FAPAR 300m v1 LENGTH_BEFORE @@ -1947,7 +2069,7 @@

Step 2: Reading in the data and dealing with invalid (no-data) pixels

15.00 210.00 - + FAPAR 300m v1 NOBS @@ -1957,7 +2079,7 @@

Step 2: Reading in the data and dealing with invalid (no-data) pixels

0.00 40.00 - + FAPAR 300m v1 QFLAG @@ -1967,7 +2089,7 @@

Step 2: Reading in the data and dealing with invalid (no-data) pixels

0.00 255.00 - + FCOVER 300m v1 FCOVER @@ -1977,7 +2099,7 @@

Step 2: Reading in the data and dealing with invalid (no-data) pixels

0.00 1.00 - + FCOVER 300m v1 RMSE @@ -1987,7 +2109,7 @@

Step 2: Reading in the data and dealing with invalid (no-data) pixels

0.00 1.00 - + FCOVER 300m v1 LENGTH_AFTER @@ -1997,7 +2119,7 @@

Step 2: Reading in the data and dealing with invalid (no-data) pixels

0.00 60.00 - + FCOVER 300m v1 LENGTH_BEFORE @@ -2007,7 +2129,7 @@

Step 2: Reading in the data and dealing with invalid (no-data) pixels

15.00 210.00 - + FCOVER 300m v1 NOBS @@ -2017,7 +2139,7 @@

Step 2: Reading in the data and dealing with invalid (no-data) pixels

0.00 40.00 - + FCOVER 300m v1 QFLAG @@ -2027,7 +2149,7 @@

Step 2: Reading in the data and dealing with invalid (no-data) pixels

0.00 255.00 - + DMP 300m v1 DMP @@ -2037,7 +2159,7 @@

Step 2: Reading in the data and dealing with invalid (no-data) pixels

0.00 327.67 - + DMP 300m v1 QFLAG @@ -2047,7 +2169,7 @@

Step 2: Reading in the data and dealing with invalid (no-data) pixels

0.00 255.00 - + GDMP 300m v1 GDMP @@ -2057,7 +2179,7 @@

Step 2: Reading in the data and dealing with invalid (no-data) pixels

0.00 655.34 - + GDMP 300m v1 QFLAG @@ -2075,113 +2197,48 @@

Step 2: Reading in the data and dealing with invalid (no-data) pixels

-

For example, in the 300m NDVI products, digital values > 250 are flagged and need to be converted into NA. When netCDF files are read in as a Raster* object, the digital values are scaled into real NDVI values automatically. To make sure to not use any of the invalid (flagged) pixels, we’ll have to be set all pixels with > 0.92 (=DN 250 x scale + offset) to NA.

+

As an example, in the 300m NDVI products, digital values > 250 are flagged and need to be converted into NA.

- -
cutoff_flag <- 0.92
+
+
cutoff_flag <- 250
 r[r > cutoff_flag] <- NA
-
-
-

Step 3: Checking the geographic extent and raster cropping

-

In the area-based resampling, we’ll aggregate a matrix of 3x3 pixels at the 333m resolution into a pixel at 1km resolution (aggregate() with factor 3). But before we do that, we need to make sure that the geographic extent matches nicely in both resolutions.

-

In your application, you may working with either

-
    -
  • global 1km data

  • -
  • or a subset of one or more areas, whereby the desired extent is either defined as a coordinate vector or the extent of the series of 1km raster files that you are looking to continue.

  • -
-

This determines the next steps and raster cropping to be performed, so let’s take a look at these two cases.

-
-

Option 1: resampling an entire global 300m raster

-

Note: File formats like netCDF4 and GeoTIFF can be optimized for reading or storing large rasters through chunking and compression. Take care when reading in such a full global raster in your computer’s working memory in one go. A global 300m raster has 120960 columns and 47040 rows, or close to 5.7bn cells. The size in bytes of each cell is provided in the CSV file (column Bytes per Cell; see table above). Consider working in subsets or using parallel computing techniques (rasterOptions, cluster processing, etc.), as described here.

-

Given the grid definition of the global 300m and 1km products (see image below), no proper aggregation of the finer resolution product can be performed at the minimum and maximum latitude and longitude. For this reason, the 300m RasterLayer object needs to be cropped accordingly.

-

+

 

+

Now we can scale the digital numbers into physical values. As said before, the physical or real value is computed as digital number * scale + offset, but this applies only for valid pixels.

- -
if(extent(r)[1] < -180 & extent(r)[2] > 179.997 &
-   extent(r)[3] < -59.99554 & extent(r)[4] > 80){  # checking for full product (image)
-  extnt_r <- extent(r)
-  extnt_r[1] <- extent(r)[1] + (2 * (1 / 336)) # x-min
-  extnt_r[2] <- extent(r)[2] - (1 * (1 / 336)) # x-max
-  extnt_r[3] <- extent(r)[3] + (1 * (1 / 336))  # y-min
-  extnt_r[4] <- extent(r)[4] - (2 * (1 / 336))  # y-max
-  r <- crop(r, extnt_r)
+
+
if (exists("nc")){
+  # Retrieving scale factor and offset from netCDF metadata
+  scale_fact <- nc$var$NDVI$scaleFact  
+  offset_val <- nc$var$NDVI$addOffset
 }else{
-  print("The image is not the full product; therefore, extent needs to be checked")
-}
+ # They can be set manually as well + scale_fact <- 0.00400000018998981 # double-precision floating-point + offset_val <- -0.07999999821186066 +} +r <- r * scale_fact + offset_val +r # To check that the values are correctly scaled (e.g. for NDVI between -0.08 and 0.92)
- -
[1] "The image is not the full product; therefore, extent needs to be checked"
+ +
class      : RasterLayer 
+dimensions : 1008, 1344, 1354752  (nrow, ncol, ncell)
+resolution : 0.00297619, 0.00297619  (x, y)
+extent     : -0.004464285, 3.995536, 39.99554, 42.99554  (xmin, xmax, ymin, ymax)
+crs        : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0 
+source     : memory
+names      : ndvi300m_Cat 
+values     : -0.08, 0.92  (min, max)
+

 

-
-

Option 2: resampling geographic subsets

-

If you want to resample a geographic subset of the original 300m product, its new extent should match with the 1km product grid.

-

This target 1km grid or extent can be either

-
    -
  • retrieved from a 1km Raster* object, i.e. one of the rasters in your existing 1km time series:
  • -
- - - -
ndvi_files_1km <- list(paste0(getwd(), "/ndvi1km_Cat.tif"))
-r_1km <- raster(ndvi_files_1km[[1]])
-if(exists("r_1km") & all(round(res(r_1km), 10) == round(0.0089285714, 10))){
-   my_extent <- extent(r_1km)
-}else{
-  stop("The given raster file does not exist or does not have the 1km resolution.")
-}
- - - -
    -
  • or provided as a vector with longitude/latitude coordinates, in decimal degrees, taking the form c(Xmin, Xmax, Ymin, Ymax). Notice that these coordinates might be slightly adjusted to the 1km PROBA-V products grid.
  • -
- - - -
coords4subset <- c(0, 4, 40, 43)
-my_extent <- extent(coords4subset)
- - - -

Next, we’ll check if the new extent is found in the PROBA-V 1km product grid (cell boundaries). If it’s not, it will be adjusted. Then, the product can be cropped to the new extent.

- - - -
# The following vectors contain Long and Lat coordinates, respectively, of the 1km grid (cell boundaries):
-x_ext <- seq((-180 - ((1 / 112) / 2)), 180, (1/112))
-y_ext <- seq((80 + ((1 / 112) / 2)), - 60, - (1/112))
- 
-if(!all(round(my_extent[1], 7) %in% round(x_ext, 7) &
-        round(my_extent[2], 7) %in% round(x_ext, 7) &
-        round(my_extent[3], 7) %in% round(y_ext, 7) &
-        round(my_extent[4], 7) %in% round(y_ext, 7))){
-  # The given extent from raster or coordinate vector does not fit into the 1km PROBA-V grid, so we are going to adjust it
-  for(crd in 1:length(as.vector(my_extent))){
-    if(crd <= 2){
-      my_extent[crd] <- x_ext[order(abs(x_ext - my_extent[crd]))][1]
-    }else{
-      my_extent[crd] <- y_ext[order(abs(y_ext - my_extent[crd]))][1]
-    }
-  }
-  
-  # Now we can crop the 300m raster
-  r <- crop(r, my_extent)
-}
- - - -
-
-
-

Step 4: Resampling using the aggregation approach

+
+

Step 5: Resampling using the aggregation approach

There are several approaches to resample data to a coarser resolution. The area-based aggregation methods group rectangular areas of cells of the finer resolution image to create a new map with larger cells.

In this case, we’ll use the function aggregate() of the package raster, setting its aggregation factor to 3 (fact = 3) as each 1km cell will be built from a group of 3x3 333m cells. In addition, it’s advised to include a condition that at least 5 out of the 9 pixels had to have valid values (i.e. not NA).

aggregate() can perform the calculation using different functions. While the default is the average (mean()) it can work also with modal(), max(), min() or even your own ad hoc functions (see mean_w.cond(), closest_to_mean() and uncert_propag() examples below).

@@ -2208,142 +2265,136 @@

Step 4: Resampling using the aggregation approach

mean -NDVI 300m -v1 -NDVI_unc -uncertainty_propagation - - LAI 300m v1 LAI mean - + LAI 300m v1 RMSE mean - + LAI 300m v1 LENGTH_AFTER -closest_to_mean or round_mean_to_integer +modal - + LAI 300m v1 LENGTH_BEFORE -closest_to_mean or round_mean_to_integer +modal - + LAI 300m v1 NOBS -closest_to_mean or round_mean_to_integer +modal - + LAI 300m v1 QFLAG -* +modal - + FAPAR 300m v1 FAPAR mean - + FAPAR 300m v1 RMSE mean - + FAPAR 300m v1 LENGTH_AFTER -closest_to_mean or round_mean_to_integer +modal - + FAPAR 300m v1 LENGTH_BEFORE -closest_to_mean or round_mean_to_integer +modal - + FAPAR 300m v1 NOBS -closest_to_mean or round_mean_to_integer +modal - + FAPAR 300m v1 QFLAG -* +modal - + FCOVER 300m v1 FCOVER mean - + FCOVER 300m v1 RMSE mean - + FCOVER 300m v1 LENGTH_AFTER -closest_to_mean or round_mean_to_integer +modal - + FCOVER 300m v1 LENGTH_BEFORE -closest_to_mean or round_mean_to_integer +modal - + FCOVER 300m v1 NOBS -closest_to_mean or round_mean_to_integer +modal - + FCOVER 300m v1 QFLAG -* +modal - + DMP 300m v1 DMP mean - + DMP 300m v1 QFLAG -* +modal - + GDMP 300m v1 GDMP mean - + GDMP 300m v1 QFLAG -* +modal @@ -2353,11 +2404,11 @@

Step 4: Resampling using the aggregation approach

-

Note: *QFLAG cannot be resampled due to the different implementations for 1km-v2 and 300m-v1 products.

+

Note: Resampled QFLAG, LENGTH_BEFORE/AFTER and NOBS cannot be compared to the 1km products due to different implementations for 1km-v2 and 300m-v1 products. For example, LAI-NOBS ranges are 0-120 for 1km-v2 and 0-40 for 300m-v1, or LAI/FAPAR/FCOVER-LENGTH_BEFORE go up to 60 days and up to 210 days, respectively for both products.

Now the process of resampling itself can go ahead using aggregate(). The resample method can be assigned at this point.

- +
#aggr_method <- "mean_w.cond"
 #aggr_method <- "closest_to_mean"
 #aggr_method <- "uncert_propag"
@@ -2403,21 +2454,23 @@ 

Step 4: Resampling using the aggregation approach

fact = 3, # from 333m to 1km fun = aggr_method, na.rm = TRUE, - filename = '')
+ filename = '') +# Only when working with Digital Numbers (not in physical values): +# r300m_resampled1km_Aggr <- round(r300m_resampled1km_Aggr)
-
-

Step 5: Check the outcome and final remarks

+
+

Step 6: Check the outcome and final remarks

Here are a couple of plots in case the user wants to take a look at the resampled map.

plot(r, main = 'Original map at 300m')
- -

+ +

@@ -2426,17 +2479,17 @@

Step 5: Check the outcome and final remarks

plot(r300m_resampled1km_Aggr, main = 'Resampled map to 1km')
- -

+ +

In addition, you could apply the chosen resample method on older (non-near real time) 300m products and correlate the results with the 1km product of the same period.

-

This document provides the results of such comparisons between 1km-resampled and 1km-produced data layers.

+

This document provides the results of such comparisons between 1km-resampled and 1km-produced data layers.

-
LS0tCnRpdGxlOiAiUmVzYW1wbGUgVG9vbCBXaXRoIFIiCm91dHB1dDogaHRtbF9ub3RlYm9vawphdXRob3I6IFhhdmllciBSb3RsbGFuLVB1aWcgKHhhdmllci5yb3RsbGFuLnB1aWdAYXN0ZXItcHJvamVjdHMuY2F0KSBhbmQgVGltIEphY29icyAoVklUTywgQ29wZXJuaWN1cyBHbG9iYWwgSGVscCBEZXNrKQpub3RlOiAiVGhlIC5pcHluYiBoYXMgYmVlbiB0cmFuc2xhdGVkIGZyb20gLlJtZCB1c2luZyAnanVweXRleHQnIgotLS0KCiMgUHVycG9zZQoKVGhpcyBub3RlYm9vayBzaG93cyBob3cgdG8gcmVzYW1wbGUgQ29wZXJuaWN1cyBHbG9iYWwgTGFuZCBTZXJ2aWNlIHZlZ2V0YXRpb24tcmVsYXRlZCBwcm9kdWN0cyAoaS5lLiBORFZJLCBGQVBBUi4uLiksIGJhc2VkIG9uIFBST0JBLVYgb2JzZXJ2YXRpb25zLCBmcm9tIDMzM20gcmVzb2x1dGlvbiB0byAxa20gdXNpbmcgUi1iYXNlZCBwYWNrYWdlcyBhbmQgZnVuY3Rpb25zLgoKSXQgaXMgaW50ZW5kZWQgZm9yIHVzZXJzIHdobyB3b3VsZCBsaWtlIHRvIGNvbnRpbnVlIHRlbXBvcmFyaWx5IHRoZWlyIDFrbSB0aW1lIHNlcmllcywgaW4gbmVhciByZWFsIHRpbWUsIGJlZm9yZSBzd2l0Y2hpbmcgdG8gdGhlIG5ldyAzMzNtIGJhc2VsaW5lIHJlc29sdXRpb24uCgoKIyBTdGVwIDE6IERvd25sb2FkIG9yIGxvY2F0ZSB0aGUgaW5wdXQgMzMzbSBwcm9kdWN0IGZpbGVzCgpXaGVuIHJ1bm5pbmcgdGhpcyBub3RlYm9vayBvbiBWSVRP4oCZcyBzZXJ2ZXJzIChlLmcuIG5vdGVib29rcy50ZXJyYXNjb3BlLmJlLCBub3RlYm9va3Mudmd0LnZpdG8uYmUpLCB5b3UgY2FuIGZvbGxvdyB0aGUgZGlyZWN0aW9ucyBpbiBbdGhpcyBub3RlYm9va10oaHR0cHM6Ly9uYnZpZXdlci5qdXB5dGVyLm9yZy9naXRodWIvVklUT2JlbGdpdW0vbm90ZWJvb2stc2FtcGxlcy9yYXcvbWFzdGVyL2RhdGFzZXRzL3Byb2Jhdi9SZWFkaW5nJTIwUFJPQkEtViUyMHVzaW5nJTIwUi5pcHluYikgdG8gZmluZCB0aGUgbG9jYXRpb25zIG9mIGlucHV0IHByb2R1Y3RzLiBEb2luZyBzbywgdGhlIGRhdGEgc2V0IChuZXRDREYgZmlsZXMpIHdpbGwgYmUgbG9jYXRlZCBhbmQgYXZhaWxhYmxlIGluIHRoZSBWSVRPJ3Mgc2VydmVycy4gSW4gcGFydGljdWxhciBmb3IgdGhlIHByb2R1Y3QgdHlwZXMgQmlvUGFyX05EVkkzMDBfVjFfR2xvYmFsIGZvciAzMzNtIE5EVkkgdjEsIEJpb1Bhcl9MQUkzMDBfVjFfR2xvYmFsIGZvciBMQUkgMzMzbSB2MSBhbmQgc2ltaWxhciBmb3IgRkFQQVIsIEZDT1ZFUiwgRE1QIGFuZCBHRE1QLgoKQWx0ZXJuYXRpdmVseSwgaWYgeW91IGFyZSB3b3JraW5nIGxvY2FsbHkgb24geW91ciBQQyBvciBsYXB0b3AsIHlvdSBjYW4gY2hvb3NlIHRvIGF1dG9tYXRpY2FsbHkgZG93bmxvYWQgQ29wZXJuaWN1cyBHbG9iYWwgTGFuZCBTZXJ2aWNlIHByb2R1Y3RzIHVzaW5nIHRoZSBmdW5jdGlvbnMgZm91bmQgaW4gaHR0cHM6Ly9naXRodWIuY29tL2NnbHMvQ29wZXJuaWN1cy1HbG9iYWwtTGFuZC1TZXJ2aWNlLURhdGEtRG93bmxvYWQtd2l0aC1SLgoKRm9yIG1vcmUgZGV0YWlscyBvbiB0aGUgcHJvZHVjdHMsIHBsZWFzZSBzZWUgdGhlIGRlc2NyaXB0aW9uIGFuZCBQcm9kdWN0IFVzZXIgTWFudWFscyBkb2N1bWVudGF0aW9uIGF0IGh0dHBzOi8vbGFuZC5jb3Blcm5pY3VzLmV1L2dsb2JhbC9wcm9kdWN0cy8KCgoKIyBTdGVwIDI6IFJlYWRpbmcgaW4gdGhlIGRhdGEgYW5kIGRlYWxpbmcgd2l0aCBpbnZhbGlkIChuby1kYXRhKSBwaXhlbHMKCk9uY2UgdGhlIGRhdGEgc2V0IGlzIGF2YWlsYWJsZSwgKnJhc3RlciogcGFja2FnZSBmdW5jdGlvbmFsaXR5IGlzIHVzZWQgdG8gcHJlcGFyZSBpdCBmb3IgcmVzYW1wbGluZy4gVGhlIHVzZSBvZiB0aGUgKmtuaXRyKiBwYWNrYWdlIGlzIG9wdGlvbmFsLCBpdCBvbmx5IGhlbHBzIHdpdGggcHJldHR5LXByaW50aW5nIHRhYmxlcy4KCmBgYHtyfQpsaWJyYXJ5KHJhc3RlcikKaWYocmVxdWlyZShrbml0cikgPT0gRkFMU0Upe2luc3RhbGwucGFja2FnZXMoImtuaXRyIiwgcmVwb3MgPSAiaHR0cHM6Ly9jbG91ZC5yLXByb2plY3Qub3JnIik7IGxpYnJhcnkoa25pdHIpfSBlbHNlIHtsaWJyYXJ5KGtuaXRyKX0KCiMgbmR2aV9maWxlcyBpcyBhIGxpc3Qgb2YgdGhlIGF2YWlsYWJsZSBmaWxlcyAobmV0Q0ZEIG9yIFJhc3Rlciogb2JqZWN0cykKbmR2aV9maWxlcyA8LSBsaXN0KHBhc3RlMChnZXR3ZCgpLCAiL25kdmkzMDBtX0NhdF9ray50aWYiKSkKciA8LSByYXN0ZXIobmR2aV9maWxlc1tbMV1dKQpgYGAKCgpUaGUgb3JpZ2luYWwgR2xvYmFsIExhbmQgcHJvZHVjdCBmaWxlcyB0eXBpY2FsbHkgY29tZSBpbiB0d28gZmxhdm91cnM6IHRoZSBnbG9iYWwgbmV0Q0RGNCBmaWxlcyAodGhlIG5hdGl2ZSBwcm9kdWN0aW9uIGZvcm1hdCkgb3IgR2VvVElGRiBzdWJzZXRzLiBCb3RoIGNhbiBjb250YWluIHNwZWNpZmljIHZhbHVlcyAoZmxhZ3MpIGZvciBpbnZhbGlkIHBpeGVscywgd2hpY2ggbmVlZCB0byBiZSBkZWFsdCB3aXRoLiBGb3IgdGhpcyBleGFtcGxlLCB3ZeKAmWxsIGNvbnZlcnQgdGhvc2UgaW50byBOb0RhdGEgKE5BKSB2YWx1ZXMuCgpUaGUgcmFuZ2Ugb2YgdmFsaWQgcGl4ZWwgdmFsdWVzLCB0aGUgc2NhbGUgZmFjdG9yIGFuZCBvZmZzZXQgYW5kIGFueSBmbGFnIHZhbHVlcyB1c2VkIGFyZSBkZXNjcmliZWQgaW4gdGhlIHByb2R1Y3QgZG9jdW1lbnRhdGlvbiBhbmQgbmV0Q0RGIGZpbGUgbWV0YWRhdGEuCgpGb3IgeW91ciBjb252ZW5pZW5jZSwgYSBzaG9ydCB0YWJsZSB3YXMgcHJlcGFyZWQgc3VtbWFyaXppbmcgdGhlIHJhbmdlIG9mIHZhbGlkIHZhbHVlcywgYm90aCBpbiByYXcgZGlnaXRhbCBudW1iZXIgKEROKSBhbmQgcGh5c2ljYWwgdmFsdWUsIGZvciBlYWNoIHByb2R1Y3QsIHZlcnNpb24gYW5kIGRhdGEgbGF5ZXIuIExldOKAmXMgcmVhZCBpbiB0aGlzIHRhYmxlIGZyb20gdGhlIGNzdiBmaWxlLiAKClRoZSBwaHlzaWNhbCBvciByZWFsIHZhbHVlIGlzIGNvbXB1dGVkIGFzIGRpZ2l0YWwgbnVtYmVyICogc2NhbGUgKyBvZmZzZXQsIGJ1dCB0aGlzIGFwcGxpZXMgb25seSBmb3IgdmFsaWQgcGl4ZWxzLgoKYGBge3IgZWNobyA9IEZBTFNFfQpjdXRvZmZfbWV0aG9kX2RmIDwtIHJlYWQuY3N2KHBhc3RlMChnZXR3ZCgpLCAiL1RhYmxlX2N1dG9mZl9hbmRfcmVzYW1wbGVNZXRob2QuY3N2IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUpCmthYmxlKGN1dG9mZl9tZXRob2RfZGZbLCAxOjhdLCBjYXB0aW9uID0gIiIpCmBgYAoKRm9yIGV4YW1wbGUsIGluIHRoZSAzMDBtIE5EVkkgcHJvZHVjdHMsIGRpZ2l0YWwgdmFsdWVzID4gMjUwIGFyZSBmbGFnZ2VkIGFuZCBuZWVkIHRvIGJlIGNvbnZlcnRlZCBpbnRvIE5BLiBXaGVuIG5ldENERiBmaWxlcyBhcmUgcmVhZCBpbiBhcyBhICpSYXN0ZXIqXCogb2JqZWN0LCB0aGUgZGlnaXRhbCB2YWx1ZXMgYXJlIHNjYWxlZCBpbnRvIHJlYWwgTkRWSSB2YWx1ZXMgYXV0b21hdGljYWxseS4gVG8gbWFrZSBzdXJlIHRvIG5vdCB1c2UgYW55IG9mIHRoZSBpbnZhbGlkIChmbGFnZ2VkKSBwaXhlbHMsIHdl4oCZbGwgaGF2ZSB0byBiZSBzZXQgYWxsIHBpeGVscyB3aXRoID4gMC45MiAoPUROIDI1MCB4IHNjYWxlICsgb2Zmc2V0KSB0byBOQS4KCgpgYGB7cn0KY3V0b2ZmX2ZsYWcgPC0gMC45MgoKcltyID4gY3V0b2ZmX2ZsYWddIDwtIE5BCmBgYAoKCgojIFN0ZXAgMzogQ2hlY2tpbmcgdGhlIGdlb2dyYXBoaWMgZXh0ZW50IGFuZCByYXN0ZXIgY3JvcHBpbmcKCkluIHRoZSBhcmVhLWJhc2VkIHJlc2FtcGxpbmcsIHdl4oCZbGwgYWdncmVnYXRlIGEgbWF0cml4IG9mIDN4MyBwaXhlbHMgYXQgdGhlIDMzM20gcmVzb2x1dGlvbiBpbnRvIGEgcGl4ZWwgYXQgMWttIHJlc29sdXRpb24gKCphZ2dyZWdhdGUoKSogd2l0aCBmYWN0b3IgMykuIEJ1dCBiZWZvcmUgd2UgZG8gdGhhdCwgd2UgbmVlZCB0byBtYWtlIHN1cmUgdGhhdCB0aGUgZ2VvZ3JhcGhpYyBleHRlbnQgbWF0Y2hlcyBuaWNlbHkgaW4gYm90aCByZXNvbHV0aW9ucy4KCkluIHlvdXIgYXBwbGljYXRpb24sIHlvdSBtYXkgd29ya2luZyB3aXRoIGVpdGhlcgoKKiBnbG9iYWwgMWttIGRhdGEKCiogb3IgYSBzdWJzZXQgb2Ygb25lIG9yIG1vcmUgYXJlYXMsIHdoZXJlYnkgdGhlIGRlc2lyZWQgZXh0ZW50IGlzIGVpdGhlciBkZWZpbmVkIGFzIGEgY29vcmRpbmF0ZSB2ZWN0b3Igb3IgdGhlIGV4dGVudCBvZiB0aGUgc2VyaWVzIG9mIDFrbSByYXN0ZXIgZmlsZXMgdGhhdCB5b3UgYXJlIGxvb2tpbmcgdG8gY29udGludWUuCgpUaGlzIGRldGVybWluZXMgdGhlIG5leHQgc3RlcHMgYW5kIHJhc3RlciBjcm9wcGluZyB0byBiZSBwZXJmb3JtZWQsIHNvIGxldOKAmXMgdGFrZSBhIGxvb2sgYXQgdGhlc2UgdHdvIGNhc2VzLgoKCiMjIE9wdGlvbiAxOiByZXNhbXBsaW5nIGFuIGVudGlyZSBnbG9iYWwgMzAwbSByYXN0ZXIKCioqTm90ZToqKiAgRmlsZSBmb3JtYXRzIGxpa2UgbmV0Q0RGNCBhbmQgR2VvVElGRiBjYW4gYmUgb3B0aW1pemVkIGZvciByZWFkaW5nIG9yIHN0b3JpbmcgbGFyZ2UgcmFzdGVycyB0aHJvdWdoIGNodW5raW5nIGFuZCBjb21wcmVzc2lvbi4gVGFrZSBjYXJlIHdoZW4gcmVhZGluZyBpbiBzdWNoIGEgZnVsbCBnbG9iYWwgcmFzdGVyIGluIHlvdXIgY29tcHV0ZXLigJlzIHdvcmtpbmcgbWVtb3J5IGluIG9uZSBnby4gQSBnbG9iYWwgMzAwbSByYXN0ZXIgaGFzIDEyMDk2MCBjb2x1bW5zIGFuZCA0NzA0MCByb3dzLCBvciBjbG9zZSB0byA1LjdibiBjZWxscy4gVGhlIHNpemUgaW4gYnl0ZXMgb2YgZWFjaCBjZWxsIGlzIHByb3ZpZGVkIGluIHRoZSBDU1YgZmlsZSAoY29sdW1uIEJ5dGVzIHBlciBDZWxsOyBzZWUgdGFibGUgYWJvdmUpLiBDb25zaWRlciB3b3JraW5nIGluIHN1YnNldHMgb3IgdXNpbmcgcGFyYWxsZWwgY29tcHV0aW5nIHRlY2huaXF1ZXMgKHJhc3Rlck9wdGlvbnMsIGNsdXN0ZXIgcHJvY2Vzc2luZywgZXRjLiksIGFzIGRlc2NyaWJlZCBbaGVyZV0oaHR0cHM6Ly9zdHJpbWFzLmNvbS9wb3N0L3Byb2Nlc3NpbmctbGFyZ2UtcmFzdGVycy1pbi1yLykuCgpHaXZlbiB0aGUgZ3JpZCBkZWZpbml0aW9uIG9mIHRoZSBnbG9iYWwgMzAwbSBhbmQgMWttIHByb2R1Y3RzIChzZWUgaW1hZ2UgYmVsb3cpLCBubyBwcm9wZXIgYWdncmVnYXRpb24gb2YgdGhlIGZpbmVyIHJlc29sdXRpb24gcHJvZHVjdCBjYW4gYmUgcGVyZm9ybWVkIGF0IHRoZSBtaW5pbXVtIGFuZCBtYXhpbXVtIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUuIEZvciB0aGlzIHJlYXNvbiwgdGhlIDMwMG0gKlJhc3RlckxheWVyKiBvYmplY3QgbmVlZHMgdG8gYmUgY3JvcHBlZCBhY2NvcmRpbmdseS4KCiFbXShQaXhlbF9jZW50cmVfZXhhbXBsZV9DR0xTLnBuZykKCgpgYGB7cn0KaWYoZXh0ZW50KHIpWzFdIDwgLTE4MCAmIGV4dGVudChyKVsyXSA+IDE3OS45OTcgJgogICBleHRlbnQocilbM10gPCAtNTkuOTk1NTQgJiBleHRlbnQocilbNF0gPiA4MCl7ICAjIGNoZWNraW5nIGZvciBmdWxsIHByb2R1Y3QgKGltYWdlKQogIGV4dG50X3IgPC0gZXh0ZW50KHIpCiAgZXh0bnRfclsxXSA8LSBleHRlbnQocilbMV0gKyAoMiAqICgxIC8gMzM2KSkgIyB4LW1pbgogIGV4dG50X3JbMl0gPC0gZXh0ZW50KHIpWzJdIC0gKDEgKiAoMSAvIDMzNikpICMgeC1tYXgKICBleHRudF9yWzNdIDwtIGV4dGVudChyKVszXSArICgxICogKDEgLyAzMzYpKSAgIyB5LW1pbgogIGV4dG50X3JbNF0gPC0gZXh0ZW50KHIpWzRdIC0gKDIgKiAoMSAvIDMzNikpICAjIHktbWF4CiAgciA8LSBjcm9wKHIsIGV4dG50X3IpCn1lbHNlewogIHByaW50KCJUaGUgaW1hZ2UgaXMgbm90IHRoZSBmdWxsIHByb2R1Y3Q7IHRoZXJlZm9yZSwgZXh0ZW50IG5lZWRzIHRvIGJlIGNoZWNrZWQiKQp9CmBgYAoKCgojIyBPcHRpb24gMjogcmVzYW1wbGluZyBnZW9ncmFwaGljIHN1YnNldHMKCklmIHlvdSB3YW50IHRvIHJlc2FtcGxlIGEgZ2VvZ3JhcGhpYyBzdWJzZXQgb2YgdGhlIG9yaWdpbmFsIDMwMG0gcHJvZHVjdCwgaXRzIG5ldyBleHRlbnQgc2hvdWxkIG1hdGNoIHdpdGggdGhlIDFrbSBwcm9kdWN0IGdyaWQuCgpUaGlzIHRhcmdldCAxa20gZ3JpZCBvciBleHRlbnQgY2FuIGJlIGVpdGhlciAKCiogcmV0cmlldmVkIGZyb20gYSAxa20gKlJhc3RlcipcKiBvYmplY3QsIGkuZS4gb25lIG9mIHRoZSByYXN0ZXJzIGluIHlvdXIgZXhpc3RpbmcgMWttIHRpbWUgc2VyaWVzOgoKYGBge3J9Cm5kdmlfZmlsZXNfMWttIDwtIGxpc3QocGFzdGUwKGdldHdkKCksICIvbmR2aTFrbV9DYXQudGlmIikpCnJfMWttIDwtIHJhc3RlcihuZHZpX2ZpbGVzXzFrbVtbMV1dKQoKaWYoZXhpc3RzKCJyXzFrbSIpICYgYWxsKHJvdW5kKHJlcyhyXzFrbSksIDEwKSA9PSByb3VuZCgwLjAwODkyODU3MTQsIDEwKSkpewogICBteV9leHRlbnQgPC0gZXh0ZW50KHJfMWttKQp9ZWxzZXsKICBzdG9wKCJUaGUgZ2l2ZW4gcmFzdGVyIGZpbGUgZG9lcyBub3QgZXhpc3Qgb3IgZG9lcyBub3QgaGF2ZSB0aGUgMWttIHJlc29sdXRpb24uIikKfQpgYGAKCgoqIG9yIHByb3ZpZGVkIGFzIGEgdmVjdG9yIHdpdGggbG9uZ2l0dWRlL2xhdGl0dWRlIGNvb3JkaW5hdGVzLCBpbiBkZWNpbWFsIGRlZ3JlZXMsIHRha2luZyB0aGUgZm9ybSAqYyhYbWluLCBYbWF4LCBZbWluLCBZbWF4KSouIE5vdGljZSB0aGF0IHRoZXNlIGNvb3JkaW5hdGVzIG1pZ2h0IGJlIHNsaWdodGx5IGFkanVzdGVkIHRvIHRoZSAxa20gUFJPQkEtViBwcm9kdWN0cyBncmlkLgoKYGBge3J9CmNvb3JkczRzdWJzZXQgPC0gYygwLCA0LCA0MCwgNDMpCm15X2V4dGVudCA8LSBleHRlbnQoY29vcmRzNHN1YnNldCkKYGBgCgoKTmV4dCwgd2XigJlsbCBjaGVjayBpZiB0aGUgbmV3IGV4dGVudCBpcyBmb3VuZCBpbiB0aGUgUFJPQkEtViAxa20gcHJvZHVjdCBncmlkIChjZWxsIGJvdW5kYXJpZXMpLiBJZiBpdCdzIG5vdCwgaXQgd2lsbCBiZSBhZGp1c3RlZC4gVGhlbiwgdGhlIHByb2R1Y3QgY2FuIGJlIGNyb3BwZWQgdG8gdGhlIG5ldyBleHRlbnQuCgoKYGBge3IgZWNobyA9IFRSVUV9CiMgVGhlIGZvbGxvd2luZyB2ZWN0b3JzIGNvbnRhaW4gTG9uZyBhbmQgTGF0IGNvb3JkaW5hdGVzLCByZXNwZWN0aXZlbHksIG9mIHRoZSAxa20gZ3JpZCAoY2VsbCBib3VuZGFyaWVzKToKeF9leHQgPC0gc2VxKCgtMTgwIC0gKCgxIC8gMTEyKSAvIDIpKSwgMTgwLCAoMS8xMTIpKQp5X2V4dCA8LSBzZXEoKDgwICsgKCgxIC8gMTEyKSAvIDIpKSwgLSA2MCwgLSAoMS8xMTIpKQogCmlmKCFhbGwocm91bmQobXlfZXh0ZW50WzFdLCA3KSAlaW4lIHJvdW5kKHhfZXh0LCA3KSAmCiAgICAgICAgcm91bmQobXlfZXh0ZW50WzJdLCA3KSAlaW4lIHJvdW5kKHhfZXh0LCA3KSAmCiAgICAgICAgcm91bmQobXlfZXh0ZW50WzNdLCA3KSAlaW4lIHJvdW5kKHlfZXh0LCA3KSAmCiAgICAgICAgcm91bmQobXlfZXh0ZW50WzRdLCA3KSAlaW4lIHJvdW5kKHlfZXh0LCA3KSkpewogICMgVGhlIGdpdmVuIGV4dGVudCBmcm9tIHJhc3RlciBvciBjb29yZGluYXRlIHZlY3RvciBkb2VzIG5vdCBmaXQgaW50byB0aGUgMWttIFBST0JBLVYgZ3JpZCwgc28gd2UgYXJlIGdvaW5nIHRvIGFkanVzdCBpdAogIGZvcihjcmQgaW4gMTpsZW5ndGgoYXMudmVjdG9yKG15X2V4dGVudCkpKXsKICAgIGlmKGNyZCA8PSAyKXsKICAgICAgbXlfZXh0ZW50W2NyZF0gPC0geF9leHRbb3JkZXIoYWJzKHhfZXh0IC0gbXlfZXh0ZW50W2NyZF0pKV1bMV0KICAgIH1lbHNlewogICAgICBteV9leHRlbnRbY3JkXSA8LSB5X2V4dFtvcmRlcihhYnMoeV9leHQgLSBteV9leHRlbnRbY3JkXSkpXVsxXQogICAgfQogIH0KICAKICAjIE5vdyB3ZSBjYW4gY3JvcCB0aGUgMzAwbSByYXN0ZXIKICByIDwtIGNyb3AociwgbXlfZXh0ZW50KQp9CmBgYAoKCiMgU3RlcCA0OiBSZXNhbXBsaW5nIHVzaW5nIHRoZSBhZ2dyZWdhdGlvbiBhcHByb2FjaAoKVGhlcmUgYXJlIHNldmVyYWwgYXBwcm9hY2hlcyB0byByZXNhbXBsZSBkYXRhIHRvIGEgY29hcnNlciByZXNvbHV0aW9uLiBUaGUgYXJlYS1iYXNlZCBhZ2dyZWdhdGlvbiBtZXRob2RzIGdyb3VwIHJlY3Rhbmd1bGFyIGFyZWFzIG9mIGNlbGxzIG9mIHRoZSBmaW5lciByZXNvbHV0aW9uIGltYWdlIHRvIGNyZWF0ZSBhIG5ldyBtYXAgd2l0aCBsYXJnZXIgY2VsbHMuIAoKSW4gdGhpcyBjYXNlLCB3ZeKAmWxsIHVzZSB0aGUgZnVuY3Rpb24gKmFnZ3JlZ2F0ZSgpKiBvZiB0aGUgcGFja2FnZSAqcmFzdGVyKiwgc2V0dGluZyBpdHMgYWdncmVnYXRpb24gZmFjdG9yIHRvIDMgKGZhY3QgPSAzKSBhcyBlYWNoIDFrbSBjZWxsIHdpbGwgYmUgYnVpbHQgZnJvbSBhIGdyb3VwIG9mIDN4MyAzMzNtIGNlbGxzLiBJbiBhZGRpdGlvbiwgaXQncyBhZHZpc2VkIHRvIGluY2x1ZGUgYSBjb25kaXRpb24gdGhhdCBhdCBsZWFzdCA1IG91dCBvZiB0aGUgOSBwaXhlbHMgaGFkIHRvIGhhdmUgdmFsaWQgdmFsdWVzIChpLmUuIG5vdCBOQSkuCgoqYWdncmVnYXRlKCkqIGNhbiBwZXJmb3JtIHRoZSBjYWxjdWxhdGlvbiB1c2luZyBkaWZmZXJlbnQgZnVuY3Rpb25zLiBXaGlsZSB0aGUgZGVmYXVsdCBpcyB0aGUgYXZlcmFnZSAoKm1lYW4oKSopIGl0IGNhbiB3b3JrIGFsc28gd2l0aCAqbW9kYWwoKSosICptYXgoKSosICptaW4oKSogb3IgZXZlbiB5b3VyIG93biAqYWQgaG9jKiBmdW5jdGlvbnMgKHNlZSAqbWVhbl93LmNvbmQoKSosICpjbG9zZXN0X3RvX21lYW4oKSogYW5kICp1bmNlcnRfcHJvcGFnKCkqIGV4YW1wbGVzIGJlbG93KS4gCgpUaGUgZm9sbG93aW5nIHRhYmxlIHJlY29tbWVuZHMgdGhlIGJlc3Qgc3VpdGVkIG1ldGhvZCBmb3IgZWFjaCBwcm9kdWN0L2xheWVyLgoKYGBge3IgZWNobyA9IEZBTFNFfQprYWJsZShjdXRvZmZfbWV0aG9kX2RmWywgYygxOjMsIDkpXSwgY2FwdGlvbiA9ICIiKQpgYGAKCioqTm90ZToqKiAqUUZMQUcgY2Fubm90IGJlIHJlc2FtcGxlZCBkdWUgdG8gdGhlIGRpZmZlcmVudCBpbXBsZW1lbnRhdGlvbnMgZm9yIDFrbS12MiBhbmQgMzAwbS12MSBwcm9kdWN0cy4KCk5vdyB0aGUgcHJvY2VzcyBvZiByZXNhbXBsaW5nIGl0c2VsZiBjYW4gZ28gYWhlYWQgdXNpbmcgKmFnZ3JlZ2F0ZSgpKi4gVGhlIHJlc2FtcGxlIG1ldGhvZCBjYW4gYmUgYXNzaWduZWQgYXQgdGhpcyBwb2ludC4KCmBgYHtyfQojYWdncl9tZXRob2QgPC0gIm1lYW5fdy5jb25kIgojYWdncl9tZXRob2QgPC0gImNsb3Nlc3RfdG9fbWVhbiIKI2FnZ3JfbWV0aG9kIDwtICJ1bmNlcnRfcHJvcGFnIgoKbWVhbl93LmNvbmQgPC0gZnVuY3Rpb24oeCwgLi4uKXsgIyBtZWFuIGluY2x1ZGluZyBjb25kaXRpb24gJ21pbmltdW0gNSB2YWxpZCBwaXhlbHMnCiAgbl92YWxpZCA8LSBzdW0oIWlzLm5hKHgpKSAjIG51bWJlciBvZiBjZWxscyB3aXRoIHZhbGlkIHZhbHVlCiAgaWYobl92YWxpZCA+IDQpewogICAgZHRzIDwtIGxpc3QoLi4uKQogICAgaWYoaXMubnVsbChkdHMkbmFfcm0pKSBkdHMkbmFfcm0gPC0gVFJVRQogICAgeF9tZWFuIDwtIG1lYW4oeCwgbmEucm0gPSBkdHMkbmFfcm0pCiAgICByZXR1cm4oeF9tZWFuKQogIH1lbHNlewogICAgeF9tZWFuIDwtIE5BCiAgICByZXR1cm4oeF9tZWFuKQogIH0KfQoKY2xvc2VzdF90b19tZWFuIDwtIGZ1bmN0aW9uKHgsIC4uLil7CiAgbl92YWxpZCA8LSBzdW0oIWlzLm5hKHgpKSAjIG51bWJlciBvZiBjZWxscyB3aXRoIHZhbGlkIHZhbHVlCiAgaWYobl92YWxpZCA+IDQpeyAgIyBtaW5pbXVtIDUgdmFsaWQgcGl4ZWxzCiAgICBkdHMgPC0gbGlzdCguLi4pCiAgICBpZihpcy5udWxsKGR0cyRuYV9ybSkpIGR0cyRuYV9ybSA8LSBUUlVFCiAgICB4X21lYW4gPC0gbWVhbih4LCBuYS5ybSA9IGR0cyRuYV9ybSkKICAgIGNsb3Nlc3QyYXZyZ2UgPC0geFtvcmRlcihhYnMoeCAtIHhfbWVhbikpXVsxXQogICAgcmV0dXJuKGNsb3Nlc3QyYXZyZ2UpCiAgfWVsc2V7CiAgICBjbG9zZXN0MmF2cmdlIDwtIE5BCiAgICByZXR1cm4oY2xvc2VzdDJhdnJnZSkKICB9Cn0KCnVuY2VydF9wcm9wYWcgPC0gZnVuY3Rpb24oeCwgLi4uKXsgIyB1bmNlcnRhaW50eV9wcm9wYWdhdGlvbiAoaW5jbHVkaW5nIGNvbmRpdGlvbiAnbWluaW11bSA1IHZhbGlkIHBpeGVscycpCiAgbl92YWxpZCA8LSBzdW0oIWlzLm5hKHgpKSAjIG51bWJlciBvZiBjZWxscyB3aXRoIHZhbGlkIHZhbHVlCiAgaWYobl92YWxpZCA+IDQpewogICAgZHRzIDwtIGxpc3QoLi4uKQogICAgaWYoaXMubnVsbChkdHMkbmFfcm0pKSBkdHMkbmFfcm0gPC0gVFJVRQogICAgdW5jX3BycCA8LSBzcXJ0KHN1bSh4XjIsIG5hLnJtID0gZHRzJG5hX3JtKSkgLyBuX3ZhbGlkCiAgICByZXR1cm4odW5jX3BycCkKICB9ZWxzZXsKICAgIHVuY19wcnAgPC0gTkEKICAgIHJldHVybih1bmNfcHJwKQogIH0KfQoKCmFnZ3JfbWV0aG9kIDwtICJtZWFuX3cuY29uZCIKcjMwMG1fcmVzYW1wbGVkMWttX0FnZ3IgPC0gYWdncmVnYXRlKHIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWN0ID0gMywgIyBmcm9tIDMzM20gdG8gMWttICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1biA9IGFnZ3JfbWV0aG9kLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlbmFtZSA9ICcnKQpgYGAKCgoKIyBTdGVwIDU6IENoZWNrIHRoZSBvdXRjb21lIGFuZCBmaW5hbCByZW1hcmtzCgpIZXJlIGFyZSBhIGNvdXBsZSBvZiBwbG90cyBpbiBjYXNlIHRoZSB1c2VyIHdhbnRzIHRvIHRha2UgYSBsb29rIGF0IHRoZSByZXNhbXBsZWQgbWFwLgoKYGBge3J9CnBsb3QociwgbWFpbiA9ICdPcmlnaW5hbCBtYXAgYXQgMzAwbScpCmBgYAoKYGBge3J9CnBsb3QocjMwMG1fcmVzYW1wbGVkMWttX0FnZ3IsIG1haW4gPSAnUmVzYW1wbGVkIG1hcCB0byAxa20nKQpgYGAKCgpJbiBhZGRpdGlvbiwgeW91IGNvdWxkIGFwcGx5IHRoZSBjaG9zZW4gcmVzYW1wbGUgbWV0aG9kIG9uIG9sZGVyIChub24tbmVhciByZWFsIHRpbWUpIDMwMG0gcHJvZHVjdHMgYW5kIGNvcnJlbGF0ZSB0aGUgcmVzdWx0cyB3aXRoIHRoZSAxa20gcHJvZHVjdCBvZiB0aGUgc2FtZSBwZXJpb2QuCgpUaGlzIGRvY3VtZW50IHByb3ZpZGVzIHRoZSByZXN1bHRzIG9mIHN1Y2ggY29tcGFyaXNvbnMgYmV0d2VlbiAxa20tcmVzYW1wbGVkIGFuZCAxa20tcHJvZHVjZWQgZGF0YSBsYXllcnMuCg==
+
LS0tCnRpdGxlOiAiUmVzYW1wbGUgVG9vbCBXaXRoIFIiCm91dHB1dDogaHRtbF9ub3RlYm9vawphdXRob3I6IFhhdmllciBSb3RsbGFuLVB1aWcgKHhhdmllci5yb3RsbGFuLnB1aWdAYXN0ZXItcHJvamVjdHMuY2F0KSBhbmQgVGltIEphY29icyAoVklUTywgQ29wZXJuaWN1cyBHbG9iYWwgSGVscCBEZXNrKQpub3RlOiAiVGhlIC5pcHluYiBoYXMgYmVlbiB0cmFuc2xhdGVkIGZyb20gLlJtZCB1c2luZyAnanVweXRleHQnIgotLS0KCiMgUHVycG9zZQoKVGhpcyBub3RlYm9vayBzaG93cyBob3cgdG8gcmVzYW1wbGUgQ29wZXJuaWN1cyBHbG9iYWwgTGFuZCBTZXJ2aWNlIHZlZ2V0YXRpb24tcmVsYXRlZCBwcm9kdWN0cyAoaS5lLiBORFZJLCBGQVBBUi4uLiksIGJhc2VkIG9uIFBST0JBLVYgb2JzZXJ2YXRpb25zLCBmcm9tIDMzM20gcmVzb2x1dGlvbiB0byAxa20gdXNpbmcgUi1iYXNlZCBwYWNrYWdlcyBhbmQgZnVuY3Rpb25zLgoKSXQgaXMgaW50ZW5kZWQgZm9yIHVzZXJzIHdobyB3b3VsZCBsaWtlIHRvIGNvbnRpbnVlIHRlbXBvcmFyaWx5IHRoZWlyIDFrbSB0aW1lIHNlcmllcywgaW4gbmVhciByZWFsIHRpbWUsIGJlZm9yZSBzd2l0Y2hpbmcgdG8gdGhlIG5ldyAzMzNtIGJhc2VsaW5lIHJlc29sdXRpb24uCgoKIyBTdGVwIDE6IERvd25sb2FkIG9yIGxvY2F0ZSB0aGUgaW5wdXQgMzMzbSBwcm9kdWN0IGZpbGVzCgpXaGVuIHJ1bm5pbmcgdGhpcyBub3RlYm9vayBvbiBWSVRP4oCZcyBzZXJ2ZXJzIChlLmcuIG5vdGVib29rcy50ZXJyYXNjb3BlLmJlLCBub3RlYm9va3Mudmd0LnZpdG8uYmUpLCB5b3UgY2FuIGZvbGxvdyB0aGUgZGlyZWN0aW9ucyBpbiBbdGhpcyBub3RlYm9va10oaHR0cHM6Ly9uYnZpZXdlci5qdXB5dGVyLm9yZy9naXRodWIvVklUT2JlbGdpdW0vbm90ZWJvb2stc2FtcGxlcy9yYXcvbWFzdGVyL2RhdGFzZXRzL3Byb2Jhdi9SZWFkaW5nJTIwUFJPQkEtViUyMHVzaW5nJTIwUi5pcHluYikgdG8gZmluZCB0aGUgbG9jYXRpb25zIG9mIGlucHV0IHByb2R1Y3RzLiBEb2luZyBzbywgdGhlIGRhdGEgc2V0IChuZXRDREYgZmlsZXMpIHdpbGwgYmUgbG9jYXRlZCBhbmQgYXZhaWxhYmxlIGluIHRoZSBWSVRPJ3Mgc2VydmVycy4gSW4gcGFydGljdWxhciBmb3IgdGhlIHByb2R1Y3QgdHlwZXMgQmlvUGFyX05EVkkzMDBfVjFfR2xvYmFsIGZvciAzMzNtIE5EVkkgdjEsIEJpb1Bhcl9MQUkzMDBfVjFfR2xvYmFsIGZvciBMQUkgMzMzbSB2MSBhbmQgc2ltaWxhciBmb3IgRkFQQVIsIEZDT1ZFUiwgRE1QIGFuZCBHRE1QLgoKQWx0ZXJuYXRpdmVseSwgaWYgeW91IGFyZSB3b3JraW5nIGxvY2FsbHkgb24geW91ciBQQyBvciBsYXB0b3AsIHlvdSBjYW4gY2hvb3NlIHRvIGF1dG9tYXRpY2FsbHkgZG93bmxvYWQgQ29wZXJuaWN1cyBHbG9iYWwgTGFuZCBTZXJ2aWNlIHByb2R1Y3RzIHVzaW5nIHRoZSBmdW5jdGlvbnMgZm91bmQgaW4gaHR0cHM6Ly9naXRodWIuY29tL2NnbHMvQ29wZXJuaWN1cy1HbG9iYWwtTGFuZC1TZXJ2aWNlLURhdGEtRG93bmxvYWQtd2l0aC1SLgoKRm9yIG1vcmUgZGV0YWlscyBvbiB0aGUgcHJvZHVjdHMsIHBsZWFzZSBzZWUgdGhlIGRlc2NyaXB0aW9uIGFuZCBQcm9kdWN0IFVzZXIgTWFudWFscyBkb2N1bWVudGF0aW9uIGF0IGh0dHBzOi8vbGFuZC5jb3Blcm5pY3VzLmV1L2dsb2JhbC9wcm9kdWN0cy8KCgoKIyBTdGVwIDI6IFJlYWRpbmcgaW4gdGhlIGRhdGEKCk9uY2UgdGhlIGRhdGEgc2V0IGlzIGF2YWlsYWJsZSwgKm5jZGY0KiBhbmQgKnJhc3RlciogcGFja2FnZXMgZnVuY3Rpb25hbGl0aWVzIGFyZSB1c2VkIHRvIHByZXBhcmUgaXQgZm9yIHJlc2FtcGxpbmcuIFJhdyBkaWdpdGFsIG51bWJlcnMgKEROKSBzdG9yZWQgaW4gdGhlIG5ldENGRCBmaWxlcyBhcmUgdXNlZCBpbiBvcmRlciB0byBhdm9pZCBmbG9hdGluZyBwb2ludCBpbXByZWNpc2lvbnMgd2hlbiBzY2FsaW5nIHRoZSB2YWx1ZXMgaW50byByZWFsIHBoeXNpY2FsIHZhbHVlcyAoUFYpLiBSIHVzZXMgSUVFRS03NTQtU3RhbmRhcmQgZG91YmxlLXByZWNpc2lvbiBmbG9hdGluZy1wb2ludCBudW1iZXJzLCB3aGVyZWFzIHRoZSB2YWx1ZXMgaW4gdGhlIG5ldENERiBhcmUgc3RvcmVkIGFzIGZsb2F0MzIuIEROIHZhbHVlcyBjYW4gYmUgc2NhbGVkIGludG8gUFYgYWZ0ZXdhcmRzIHVzaW5nIHRoZSBmb3JtdWxhIFBWID0gRE4gXCogKnNjYWxlKiArICpvZmZzZXQqLgoKVGhlIHVzZSBvZiB0aGUgKmtuaXRyKiBwYWNrYWdlIGlzIG9wdGlvbmFsLCBpdCBvbmx5IGhlbHBzIHdpdGggcHJldHR5LXByaW50aW5nIHRhYmxlcy4KCmBgYHtyfQppZihyZXF1aXJlKG5jZGY0KSA9PSBGQUxTRSl7aW5zdGFsbC5wYWNrYWdlcygibmNkZjQiLCByZXBvcyA9ICJodHRwczovL2Nsb3VkLnItcHJvamVjdC5vcmciKTsgbGlicmFyeShuY2RmNCl9IGVsc2Uge2xpYnJhcnkobmNkZjQpfQppZihyZXF1aXJlKHJhc3RlcikgPT0gRkFMU0Upe2luc3RhbGwucGFja2FnZXMoInJhc3RlciIsIHJlcG9zID0gImh0dHBzOi8vY2xvdWQuci1wcm9qZWN0Lm9yZyIpOyBsaWJyYXJ5KHJhc3Rlcil9IGVsc2Uge2xpYnJhcnkocmFzdGVyKX0KaWYocmVxdWlyZShrbml0cikgPT0gRkFMU0Upe2luc3RhbGwucGFja2FnZXMoImtuaXRyIiwgcmVwb3MgPSAiaHR0cHM6Ly9jbG91ZC5yLXByb2plY3Qub3JnIik7IGxpYnJhcnkoa25pdHIpfSBlbHNlIHtsaWJyYXJ5KGtuaXRyKX0KCiMgbmR2aV9maWxlcyBpcyBhIGxpc3Qgb2YgdGhlIGF2YWlsYWJsZSBmaWxlcyAoUmFzdGVyKiBvYmplY3RzIG9yIG5ldENERikKbmR2aV9maWxlcyA8LSBsaXN0KHBhc3RlMChnZXR3ZCgpLCAiL25kdmkzMDBtX0NhdC50aWYiKSkKYGBgCgombmJzcDsKClRoZSBvcmlnaW5hbCBHbG9iYWwgTGFuZCBwcm9kdWN0IGZpbGVzIHR5cGljYWxseSBjb21lIGluIHR3byBmbGF2b3VyczogdGhlIGdsb2JhbCBuZXRDREY0IGZpbGVzICh0aGUgbmF0aXZlIHByb2R1Y3Rpb24gZm9ybWF0KSBvciBHZW9USUZGIHN1YnNldHMuIElmIHRoZSBmaWxlIGlzIGEgR2VvVElGRjoKYGBge3J9CmlmIChncmVwbCgiLnRpZiQiLCBuZHZpX2ZpbGVzW1sxXV0pKXsgIAogIHIgPC0gcmFzdGVyKG5kdmlfZmlsZXNbWzFdXSkKfQpgYGAKCiZuYnNwOwoKSWYgdGhlIGZpbGUgaXMgYSBuZXRDREY0OgpgYGB7cn0KaWYgKGdyZXBsKCIubmMkIiwgbmR2aV9maWxlc1tbMV1dKSl7CiAgbmMgIDwtIG5jX29wZW4obmR2aV9maWxlc1tbMV1dKQogIGxvbiA8LSBuY3Zhcl9nZXQobmMsICJsb24iKQogIGxhdCA8LSBuY3Zhcl9nZXQobmMsICJsYXQiKQogICNDb3Blcm5pY3VzIG5jIGZpbGVzIGhhdmUgbGF0L2xvbmcgYmVsb25naW5nIHRvIHRoZSBjZW50cmUgb2YgdGhlIHBpeGVsLCBhbmQgUiB1c2VzIHVwcGVyL2xlZnQgY29ybmVyOwogICN0aGVyZWZvcmUsIGNvb3JkaW5hdGVzIG5lZWQgdG8gYmUgYWRqdXN0ZWQKICBsb24gPC0gbG9uIC0gKDEvMzM2KS8yICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIDEvMzM2IGlzIHRoZSBwaXhlbCByZXNvbHV0aW9uIG9mIHRoZSAzMzNtIHByb2R1Y3QKICBsYXQgPC0gbGF0ICsgKDEvMzM2KS8yCiAgbmNfZGF0YSA8LSBuY3Zhcl9nZXQobmMsICJORFZJIiwgcmF3X2RhdGF2YWxzID0gVFJVRSkgICAgIyByYXdfZGF0YXZhbHMgPSBUUlVFIHRvIGF2b2lkIGF1dG9tYXRpYyBzY2FsaW5nIG9mIEROCiAgciA8LSByYXN0ZXIodChuY19kYXRhWywgc2VxKGRpbShuY19kYXRhKVsyXSwgMSwgLTEpXSkpICAgIyBjcmVhdGluZyBhIHJhc3RlciBvYmplY3QKICBleHRlbnQocikgPC0gYyhyYW5nZShsb24pWzFdLCAocmFuZ2UobG9uKVsyXSArICgxLzMzNikpLAogICAgICAgICAgICAgICAgIChyYW5nZShsYXQpWzFdIC0gKDEvMzM2KSksIHJhbmdlKGxhdClbMl0pICMgZXh0ZW50IG9mIHRoZSBwcm9kdWN0IAogIGNycyhyKSA8LSBDUlMoJytpbml0PUVQU0c6NDMyNicpICAgICAgICAgICAgICAgICAgICAgICAgICMgY29vcmRpbmF0ZSByZWZlcmVuY2Ugc3lzdGVtCn0KYGBgCioqTm90ZToqKiAgKnJhc3Rlcjo6cmFzdGVyKCkqIGNhbiBhbHNvIHJlYWQgKm5jKiBmaWxlcyAodXNpbmcgKm5jZGY0KiBmdW5jdGlvbmFsaXRpZXMpLCBidXQgRGlnaXRhbCBOdW1iZXJzIGFyZSBhdXRvbWF0aWNhbGx5IHNjYWxlZCB0byBwaHlzaWNhbCB2YWx1ZXMuIEFzIHNhaWQgYmVmb3JlLCB3ZSB3YW50IHRvIGF2b2lkIHRoaXMgYmVjYXVzZSBvZiBmbG9hdGluZyBwb2ludCBpbXByZWNpc2lvbnMuCgoKIyBTdGVwIDM6IENoZWNraW5nIHRoZSBnZW9ncmFwaGljIGV4dGVudCBhbmQgcmFzdGVyIGNyb3BwaW5nCgpJbiB0aGUgYXJlYS1iYXNlZCByZXNhbXBsaW5nLCB3ZeKAmWxsIGFnZ3JlZ2F0ZSBhIG1hdHJpeCBvZiAzeDMgcGl4ZWxzIGF0IHRoZSAzMzNtIHJlc29sdXRpb24gaW50byBhIHBpeGVsIGF0IDFrbSByZXNvbHV0aW9uICgqYWdncmVnYXRlKCkqIHdpdGggZmFjdG9yIDMpLiBCdXQgYmVmb3JlIHdlIGRvIHRoYXQsIHdlIG5lZWQgdG8gbWFrZSBzdXJlIHRoYXQgdGhlIGdlb2dyYXBoaWMgZXh0ZW50IG1hdGNoZXMgbmljZWx5IGluIGJvdGggcmVzb2x1dGlvbnMuCgpJbiB5b3VyIGFwcGxpY2F0aW9uLCB5b3UgbWF5IHdvcmtpbmcgd2l0aCBlaXRoZXIKCiogZ2xvYmFsIDFrbSBkYXRhCgoqIG9yIGEgc3Vic2V0IG9mIG9uZSBvciBtb3JlIGFyZWFzLCB3aGVyZWJ5IHRoZSBkZXNpcmVkIGV4dGVudCBpcyBlaXRoZXIgZGVmaW5lZCBhcyBhIGNvb3JkaW5hdGUgdmVjdG9yIG9yIHRoZSBleHRlbnQgb2YgdGhlIHNlcmllcyBvZiAxa20gcmFzdGVyIGZpbGVzIHRoYXQgeW91IGFyZSBsb29raW5nIHRvIGNvbnRpbnVlLgoKVGhpcyBkZXRlcm1pbmVzIHRoZSBuZXh0IHN0ZXBzIGFuZCByYXN0ZXIgY3JvcHBpbmcgdG8gYmUgcGVyZm9ybWVkLCBzbyBsZXTigJlzIHRha2UgYSBsb29rIGF0IHRoZXNlIHR3byBjYXNlcy4KCgojIyBPcHRpb24gMTogcmVzYW1wbGluZyBhbiBlbnRpcmUgZ2xvYmFsIDMwMG0gcmFzdGVyCgoqKk5vdGU6KiogIEZpbGUgZm9ybWF0cyBsaWtlIG5ldENERjQgYW5kIEdlb1RJRkYgY2FuIGJlIG9wdGltaXplZCBmb3IgcmVhZGluZyBvciBzdG9yaW5nIGxhcmdlIHJhc3RlcnMgdGhyb3VnaCBjaHVua2luZyBhbmQgY29tcHJlc3Npb24uIFRha2UgY2FyZSB3aGVuIHJlYWRpbmcgaW4gc3VjaCBhIGZ1bGwgZ2xvYmFsIHJhc3RlciBpbiB5b3VyIGNvbXB1dGVy4oCZcyB3b3JraW5nIG1lbW9yeSBpbiBvbmUgZ28uIEEgZ2xvYmFsIDMwMG0gcmFzdGVyIGhhcyAxMjA5NjAgY29sdW1ucyBhbmQgNDcwNDAgcm93cywgb3IgY2xvc2UgdG8gNS43Ym4gY2VsbHMuIFRoZSBzaXplIGluIGJ5dGVzIG9mIGVhY2ggY2VsbCBpcyBwcm92aWRlZCBpbiB0aGUgQ1NWIGZpbGUgKGNvbHVtbiBCeXRlcyBwZXIgQ2VsbDsgc2VlIHRhYmxlIGJlbG93KS4gQ29uc2lkZXIgd29ya2luZyBpbiBzdWJzZXRzIG9yIHVzaW5nIHBhcmFsbGVsIGNvbXB1dGluZyB0ZWNobmlxdWVzIChyYXN0ZXJPcHRpb25zLCBjbHVzdGVyIHByb2Nlc3NpbmcsIGV0Yy4pLCBhcyBkZXNjcmliZWQgW2hlcmVdKGh0dHBzOi8vc3RyaW1hcy5jb20vcG9zdC9wcm9jZXNzaW5nLWxhcmdlLXJhc3RlcnMtaW4tci8pLgoKR2l2ZW4gdGhlIGdyaWQgZGVmaW5pdGlvbiBvZiB0aGUgZ2xvYmFsIDMwMG0gYW5kIDFrbSBwcm9kdWN0cyAoc2VlIGltYWdlIGJlbG93KSwgbm8gcHJvcGVyIGFnZ3JlZ2F0aW9uIG9mIHRoZSBmaW5lciByZXNvbHV0aW9uIHByb2R1Y3QgY2FuIGJlIHBlcmZvcm1lZCBhdCB0aGUgbWluaW11bSBhbmQgbWF4aW11bSBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlLiBGb3IgdGhpcyByZWFzb24sIHRoZSAzMDBtICpSYXN0ZXJMYXllciogb2JqZWN0IG5lZWRzIHRvIGJlIGNyb3BwZWQgYWNjb3JkaW5nbHkuCgohW10oUGl4ZWxfY2VudHJlX2V4YW1wbGVfQ0dMUy5wbmcpCgoKYGBge3J9CmlmKGV4dGVudChyKVsxXSA8IC0xODAgJiBleHRlbnQocilbMl0gPiAxNzkuOTk3ICYKICAgZXh0ZW50KHIpWzNdIDwgLTU5Ljk5NTU0ICYgZXh0ZW50KHIpWzRdID4gODApeyAgIyBjaGVja2luZyBmb3IgZnVsbCBwcm9kdWN0IChpbWFnZSkKICBleHRudF9yIDwtIGV4dGVudChyKQogIGV4dG50X3JbMV0gPC0gZXh0ZW50KHIpWzFdICsgKDIgKiAoMSAvIDMzNikpICMgeC1taW4KICBleHRudF9yWzJdIDwtIGV4dGVudChyKVsyXSAtICgxICogKDEgLyAzMzYpKSAjIHgtbWF4CiAgZXh0bnRfclszXSA8LSBleHRlbnQocilbM10gKyAoMSAqICgxIC8gMzM2KSkgICMgeS1taW4KICBleHRudF9yWzRdIDwtIGV4dGVudChyKVs0XSAtICgyICogKDEgLyAzMzYpKSAgIyB5LW1heAogIHIgPC0gY3JvcChyLCBleHRudF9yKQp9ZWxzZXsKICBwcmludCgiVGhlIGltYWdlIGlzIG5vdCB0aGUgZnVsbCBwcm9kdWN0OyB0aGVyZWZvcmUsIGV4dGVudCBuZWVkcyB0byBiZSBjaGVja2VkIikKfQpgYGAKCgoKIyMgT3B0aW9uIDI6IHJlc2FtcGxpbmcgZ2VvZ3JhcGhpYyBzdWJzZXRzCgpJZiB5b3Ugd2FudCB0byByZXNhbXBsZSBhIGdlb2dyYXBoaWMgc3Vic2V0IG9mIHRoZSBvcmlnaW5hbCAzMDBtIHByb2R1Y3QsIGl0cyBuZXcgZXh0ZW50IHNob3VsZCBtYXRjaCB3aXRoIHRoZSAxa20gcHJvZHVjdCBncmlkLgoKVGhpcyB0YXJnZXQgMWttIGdyaWQgb3IgZXh0ZW50IGNhbiBiZSBlaXRoZXIgCgoqIHJldHJpZXZlZCBmcm9tIGEgMWttICpSYXN0ZXIqXCogb2JqZWN0LCBpLmUuIG9uZSBvZiB0aGUgcmFzdGVycyBpbiB5b3VyIGV4aXN0aW5nIDFrbSB0aW1lIHNlcmllczoKCmBgYHtyfQpuZHZpX2ZpbGVzXzFrbSA8LSBsaXN0KHBhc3RlMChnZXR3ZCgpLCAiL25kdmkxa21fQ2F0LnRpZiIpKQpyXzFrbSA8LSByYXN0ZXIobmR2aV9maWxlc18xa21bWzFdXSkKCmlmKGV4aXN0cygicl8xa20iKSAmIGFsbChyb3VuZChyZXMocl8xa20pLCAxMCkgPT0gcm91bmQoMC4wMDg5Mjg1NzE0LCAxMCkpKXsKICAgbXlfZXh0ZW50IDwtIGV4dGVudChyXzFrbSkKfWVsc2V7CiAgc3RvcCgiVGhlIGdpdmVuIHJhc3RlciBmaWxlIGRvZXMgbm90IGV4aXN0IG9yIGRvZXMgbm90IGhhdmUgdGhlIDFrbSByZXNvbHV0aW9uLiIpCn0KYGBgCgoKKiBvciBwcm92aWRlZCBhcyBhIHZlY3RvciB3aXRoIGxvbmdpdHVkZS9sYXRpdHVkZSBjb29yZGluYXRlcywgaW4gZGVjaW1hbCBkZWdyZWVzLCB0YWtpbmcgdGhlIGZvcm0gKmMoWG1pbiwgWG1heCwgWW1pbiwgWW1heCkqLiBOb3RpY2UgdGhhdCB0aGVzZSBjb29yZGluYXRlcyBtaWdodCBiZSBzbGlnaHRseSBhZGp1c3RlZCB0byB0aGUgMWttIFBST0JBLVYgcHJvZHVjdHMgZ3JpZC4KCmBgYHtyfQpjb29yZHM0c3Vic2V0IDwtIGMoMCwgNCwgNDAsIDQzKQpteV9leHRlbnQgPC0gZXh0ZW50KGNvb3JkczRzdWJzZXQpCmBgYAoKCk5leHQsIHdl4oCZbGwgY2hlY2sgaWYgdGhlIG5ldyBleHRlbnQgaXMgZm91bmQgaW4gdGhlIFBST0JBLVYgMWttIHByb2R1Y3QgZ3JpZCAoY2VsbCBib3VuZGFyaWVzKS4gSWYgaXQncyBub3QsIGl0IHdpbGwgYmUgYWRqdXN0ZWQuIFRoZW4sIHRoZSBwcm9kdWN0IGNhbiBiZSBjcm9wcGVkIHRvIHRoZSBuZXcgZXh0ZW50LgoKCmBgYHtyIGVjaG8gPSBUUlVFfQojIFRoZSBmb2xsb3dpbmcgdmVjdG9ycyBjb250YWluIExvbmcgYW5kIExhdCBjb29yZGluYXRlcywgcmVzcGVjdGl2ZWx5LCBvZiB0aGUgMWttIGdyaWQgKGNlbGwgYm91bmRhcmllcyk6CnhfZXh0IDwtIHNlcSgoLTE4MCAtICgoMSAvIDExMikgLyAyKSksIDE4MCwgKDEvMTEyKSkKeV9leHQgPC0gc2VxKCg4MCArICgoMSAvIDExMikgLyAyKSksIC0gNjAsIC0gKDEvMTEyKSkKIAppZighYWxsKHJvdW5kKG15X2V4dGVudFsxXSwgNykgJWluJSByb3VuZCh4X2V4dCwgNykgJgogICAgICAgIHJvdW5kKG15X2V4dGVudFsyXSwgNykgJWluJSByb3VuZCh4X2V4dCwgNykgJgogICAgICAgIHJvdW5kKG15X2V4dGVudFszXSwgNykgJWluJSByb3VuZCh5X2V4dCwgNykgJgogICAgICAgIHJvdW5kKG15X2V4dGVudFs0XSwgNykgJWluJSByb3VuZCh5X2V4dCwgNykpKXsKICAjIFRoZSBnaXZlbiBleHRlbnQgZnJvbSByYXN0ZXIgb3IgY29vcmRpbmF0ZSB2ZWN0b3IgZG9lcyBub3QgZml0IGludG8gdGhlIDFrbSBQUk9CQS1WIGdyaWQsIHNvIHdlIGFyZSBnb2luZyB0byBhZGp1c3QgaXQKICBmb3IoY3JkIGluIDE6bGVuZ3RoKGFzLnZlY3RvcihteV9leHRlbnQpKSl7CiAgICBpZihjcmQgPD0gMil7CiAgICAgIG15X2V4dGVudFtjcmRdIDwtIHhfZXh0W29yZGVyKGFicyh4X2V4dCAtIG15X2V4dGVudFtjcmRdKSldWzFdCiAgICB9ZWxzZXsKICAgICAgbXlfZXh0ZW50W2NyZF0gPC0geV9leHRbb3JkZXIoYWJzKHlfZXh0IC0gbXlfZXh0ZW50W2NyZF0pKV1bMV0KICAgIH0KICB9CiAgCiAgIyBOb3cgd2UgY2FuIGNyb3AgdGhlIDMwMG0gcmFzdGVyCiAgciA8LSBjcm9wKHIsIG15X2V4dGVudCkKfQpgYGAKCgoKIyBTdGVwIDQ6IERlYWxpbmcgd2l0aCBpbnZhbGlkIChuby1kYXRhKSBwaXhlbHMKClRoZSBvcmlnaW5hbCBHbG9iYWwgTGFuZCBwcm9kdWN0IGNhbiBjb250YWluIHNwZWNpZmljIHZhbHVlcyAoZmxhZ3MpIGZvciBpbnZhbGlkIHBpeGVscywgd2hpY2ggbmVlZCB0byBiZSBkZWFsdCB3aXRoLiBGb3IgdGhpcyBleGFtcGxlLCB3ZeKAmWxsIGNvbnZlcnQgdGhvc2UgaW50byBOb0RhdGEgKE5BKSB2YWx1ZXMuCgpUaGUgcmFuZ2Ugb2YgdmFsaWQgcGl4ZWwgdmFsdWVzLCB0aGUgc2NhbGUgZmFjdG9yIGFuZCBvZmZzZXQgYW5kIGFueSBmbGFnIHZhbHVlcyB1c2VkIGFyZSBkZXNjcmliZWQgaW4gdGhlIHByb2R1Y3QgZG9jdW1lbnRhdGlvbiBhbmQgbmV0Q0RGIGZpbGUgbWV0YWRhdGEuCgpGb3IgeW91ciBjb252ZW5pZW5jZSwgYSBzaG9ydCB0YWJsZSB3YXMgcHJlcGFyZWQgc3VtbWFyaXppbmcgdGhlIHJhbmdlIG9mIHZhbGlkIHZhbHVlcywgYm90aCBpbiByYXcgZGlnaXRhbCBudW1iZXIgKEROKSBhbmQgcGh5c2ljYWwgdmFsdWUsIGZvciBlYWNoIHByb2R1Y3QsIHZlcnNpb24gYW5kIGRhdGEgbGF5ZXIuIExldOKAmXMgcmVhZCBpbiB0aGlzIHRhYmxlIGZyb20gdGhlIGNzdiBmaWxlLiAKCgpgYGB7ciBlY2hvID0gRkFMU0V9CmN1dG9mZl9tZXRob2RfZGYgPC0gcmVhZC5jc3YocGFzdGUwKGdldHdkKCksICIvVGFibGVfY3V0b2ZmX2FuZF9yZXNhbXBsZU1ldGhvZC5jc3YiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSkKa2FibGUoY3V0b2ZmX21ldGhvZF9kZlssIDE6OF0sIGNhcHRpb24gPSAiIikKYGBgCgpBcyBhbiBleGFtcGxlLCBpbiB0aGUgMzAwbSBORFZJIHByb2R1Y3RzLCBkaWdpdGFsIHZhbHVlcyA+IDI1MCBhcmUgZmxhZ2dlZCBhbmQgbmVlZCB0byBiZSBjb252ZXJ0ZWQgaW50byBOQS4gCgoKYGBge3J9CmN1dG9mZl9mbGFnIDwtIDI1MAoKcltyID4gY3V0b2ZmX2ZsYWddIDwtIE5BCmBgYAoKJm5ic3A7CgpOb3cgd2UgY2FuIHNjYWxlIHRoZSBkaWdpdGFsIG51bWJlcnMgaW50byBwaHlzaWNhbCB2YWx1ZXMuIEFzIHNhaWQgYmVmb3JlLCB0aGUgcGh5c2ljYWwgb3IgcmVhbCB2YWx1ZSBpcyBjb21wdXRlZCBhcyBkaWdpdGFsIG51bWJlciAqIHNjYWxlICsgb2Zmc2V0LCBidXQgdGhpcyBhcHBsaWVzIG9ubHkgZm9yIHZhbGlkIHBpeGVscy4KCgpgYGB7cn0KaWYgKGV4aXN0cygibmMiKSl7CiAgIyBSZXRyaWV2aW5nIHNjYWxlIGZhY3RvciBhbmQgb2Zmc2V0IGZyb20gbmV0Q0RGIG1ldGFkYXRhCiAgc2NhbGVfZmFjdCA8LSBuYyR2YXIkTkRWSSRzY2FsZUZhY3QgIAogIG9mZnNldF92YWwgPC0gbmMkdmFyJE5EVkkkYWRkT2Zmc2V0Cn1lbHNlewogICMgVGhleSBjYW4gYmUgc2V0IG1hbnVhbGx5IGFzIHdlbGwKICBzY2FsZV9mYWN0IDwtIDAuMDA0MDAwMDAwMTg5OTg5ODEgICAjIGRvdWJsZS1wcmVjaXNpb24gZmxvYXRpbmctcG9pbnQKICBvZmZzZXRfdmFsIDwtIC0wLjA3OTk5OTk5ODIxMTg2MDY2Cn0KCnIgPC0gciAqIHNjYWxlX2ZhY3QgKyBvZmZzZXRfdmFsCnIgICMgVG8gY2hlY2sgdGhhdCB0aGUgdmFsdWVzIGFyZSBjb3JyZWN0bHkgc2NhbGVkIChlLmcuIGZvciBORFZJIGJldHdlZW4gLTAuMDggYW5kIDAuOTIpCgpgYGAKCiZuYnNwOwoKCiMgU3RlcCA1OiBSZXNhbXBsaW5nIHVzaW5nIHRoZSBhZ2dyZWdhdGlvbiBhcHByb2FjaAoKVGhlcmUgYXJlIHNldmVyYWwgYXBwcm9hY2hlcyB0byByZXNhbXBsZSBkYXRhIHRvIGEgY29hcnNlciByZXNvbHV0aW9uLiBUaGUgYXJlYS1iYXNlZCBhZ2dyZWdhdGlvbiBtZXRob2RzIGdyb3VwIHJlY3Rhbmd1bGFyIGFyZWFzIG9mIGNlbGxzIG9mIHRoZSBmaW5lciByZXNvbHV0aW9uIGltYWdlIHRvIGNyZWF0ZSBhIG5ldyBtYXAgd2l0aCBsYXJnZXIgY2VsbHMuIAoKSW4gdGhpcyBjYXNlLCB3ZeKAmWxsIHVzZSB0aGUgZnVuY3Rpb24gKmFnZ3JlZ2F0ZSgpKiBvZiB0aGUgcGFja2FnZSAqcmFzdGVyKiwgc2V0dGluZyBpdHMgYWdncmVnYXRpb24gZmFjdG9yIHRvIDMgKGZhY3QgPSAzKSBhcyBlYWNoIDFrbSBjZWxsIHdpbGwgYmUgYnVpbHQgZnJvbSBhIGdyb3VwIG9mIDN4MyAzMzNtIGNlbGxzLiBJbiBhZGRpdGlvbiwgaXQncyBhZHZpc2VkIHRvIGluY2x1ZGUgYSBjb25kaXRpb24gdGhhdCBhdCBsZWFzdCA1IG91dCBvZiB0aGUgOSBwaXhlbHMgaGFkIHRvIGhhdmUgdmFsaWQgdmFsdWVzIChpLmUuIG5vdCBOQSkuCgoqYWdncmVnYXRlKCkqIGNhbiBwZXJmb3JtIHRoZSBjYWxjdWxhdGlvbiB1c2luZyBkaWZmZXJlbnQgZnVuY3Rpb25zLiBXaGlsZSB0aGUgZGVmYXVsdCBpcyB0aGUgYXZlcmFnZSAoKm1lYW4oKSopIGl0IGNhbiB3b3JrIGFsc28gd2l0aCAqbW9kYWwoKSosICptYXgoKSosICptaW4oKSogb3IgZXZlbiB5b3VyIG93biAqYWQgaG9jKiBmdW5jdGlvbnMgKHNlZSAqbWVhbl93LmNvbmQoKSosICpjbG9zZXN0X3RvX21lYW4oKSogYW5kICp1bmNlcnRfcHJvcGFnKCkqIGV4YW1wbGVzIGJlbG93KS4gCgpUaGUgZm9sbG93aW5nIHRhYmxlIHJlY29tbWVuZHMgdGhlIGJlc3Qgc3VpdGVkIG1ldGhvZCBmb3IgZWFjaCBwcm9kdWN0L2xheWVyLgoKYGBge3IgZWNobyA9IEZBTFNFfQprYWJsZShjdXRvZmZfbWV0aG9kX2RmWywgYygxOjMsIDkpXSwgY2FwdGlvbiA9ICIiKQpgYGAKCioqTm90ZToqKiBSZXNhbXBsZWQgUUZMQUcsIExFTkdUSF9CRUZPUkUvQUZURVIgYW5kIE5PQlMgY2Fubm90IGJlIGNvbXBhcmVkIHRvIHRoZSAxa20gcHJvZHVjdHMgZHVlIHRvIGRpZmZlcmVudCBpbXBsZW1lbnRhdGlvbnMgZm9yIDFrbS12MiBhbmQgMzAwbS12MSBwcm9kdWN0cy4gRm9yIGV4YW1wbGUsIExBSS1OT0JTIHJhbmdlcyBhcmUgMC0xMjAgZm9yIDFrbS12MiBhbmQgMC00MCBmb3IgMzAwbS12MSwgb3IgTEFJL0ZBUEFSL0ZDT1ZFUi1MRU5HVEhfQkVGT1JFIGdvIHVwIHRvIDYwIGRheXMgYW5kIHVwIHRvIDIxMCBkYXlzLCByZXNwZWN0aXZlbHkgZm9yIGJvdGggcHJvZHVjdHMuCgpOb3cgdGhlIHByb2Nlc3Mgb2YgcmVzYW1wbGluZyBpdHNlbGYgY2FuIGdvIGFoZWFkIHVzaW5nICphZ2dyZWdhdGUoKSouIFRoZSByZXNhbXBsZSBtZXRob2QgY2FuIGJlIGFzc2lnbmVkIGF0IHRoaXMgcG9pbnQuCgpgYGB7cn0KI2FnZ3JfbWV0aG9kIDwtICJtZWFuX3cuY29uZCIKI2FnZ3JfbWV0aG9kIDwtICJjbG9zZXN0X3RvX21lYW4iCiNhZ2dyX21ldGhvZCA8LSAidW5jZXJ0X3Byb3BhZyIKCm1lYW5fdy5jb25kIDwtIGZ1bmN0aW9uKHgsIC4uLil7ICMgbWVhbiBpbmNsdWRpbmcgY29uZGl0aW9uICdtaW5pbXVtIDUgdmFsaWQgcGl4ZWxzJwogIG5fdmFsaWQgPC0gc3VtKCFpcy5uYSh4KSkgIyBudW1iZXIgb2YgY2VsbHMgd2l0aCB2YWxpZCB2YWx1ZQogIGlmKG5fdmFsaWQgPiA0KXsKICAgIGR0cyA8LSBsaXN0KC4uLikKICAgIGlmKGlzLm51bGwoZHRzJG5hX3JtKSkgZHRzJG5hX3JtIDwtIFRSVUUKICAgIHhfbWVhbiA8LSBtZWFuKHgsIG5hLnJtID0gZHRzJG5hX3JtKQogICAgcmV0dXJuKHhfbWVhbikKICB9ZWxzZXsKICAgIHhfbWVhbiA8LSBOQQogICAgcmV0dXJuKHhfbWVhbikKICB9Cn0KCmNsb3Nlc3RfdG9fbWVhbiA8LSBmdW5jdGlvbih4LCAuLi4pewogIG5fdmFsaWQgPC0gc3VtKCFpcy5uYSh4KSkgIyBudW1iZXIgb2YgY2VsbHMgd2l0aCB2YWxpZCB2YWx1ZQogIGlmKG5fdmFsaWQgPiA0KXsgICMgbWluaW11bSA1IHZhbGlkIHBpeGVscwogICAgZHRzIDwtIGxpc3QoLi4uKQogICAgaWYoaXMubnVsbChkdHMkbmFfcm0pKSBkdHMkbmFfcm0gPC0gVFJVRQogICAgeF9tZWFuIDwtIG1lYW4oeCwgbmEucm0gPSBkdHMkbmFfcm0pCiAgICBjbG9zZXN0MmF2cmdlIDwtIHhbb3JkZXIoYWJzKHggLSB4X21lYW4pKV1bMV0KICAgIHJldHVybihjbG9zZXN0MmF2cmdlKQogIH1lbHNlewogICAgY2xvc2VzdDJhdnJnZSA8LSBOQQogICAgcmV0dXJuKGNsb3Nlc3QyYXZyZ2UpCiAgfQp9Cgp1bmNlcnRfcHJvcGFnIDwtIGZ1bmN0aW9uKHgsIC4uLil7ICMgdW5jZXJ0YWludHlfcHJvcGFnYXRpb24gKGluY2x1ZGluZyBjb25kaXRpb24gJ21pbmltdW0gNSB2YWxpZCBwaXhlbHMnKQogIG5fdmFsaWQgPC0gc3VtKCFpcy5uYSh4KSkgIyBudW1iZXIgb2YgY2VsbHMgd2l0aCB2YWxpZCB2YWx1ZQogIGlmKG5fdmFsaWQgPiA0KXsKICAgIGR0cyA8LSBsaXN0KC4uLikKICAgIGlmKGlzLm51bGwoZHRzJG5hX3JtKSkgZHRzJG5hX3JtIDwtIFRSVUUKICAgIHVuY19wcnAgPC0gc3FydChzdW0oeF4yLCBuYS5ybSA9IGR0cyRuYV9ybSkpIC8gbl92YWxpZAogICAgcmV0dXJuKHVuY19wcnApCiAgfWVsc2V7CiAgICB1bmNfcHJwIDwtIE5BCiAgICByZXR1cm4odW5jX3BycCkKICB9Cn0KCgphZ2dyX21ldGhvZCA8LSAibWVhbl93LmNvbmQiCnIzMDBtX3Jlc2FtcGxlZDFrbV9BZ2dyIDwtIGFnZ3JlZ2F0ZShyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjdCA9IDMsICMgZnJvbSAzMzNtIHRvIDFrbSAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW4gPSBhZ2dyX21ldGhvZCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZW5hbWUgPSAnJykKCiMgT25seSB3aGVuIHdvcmtpbmcgd2l0aCBEaWdpdGFsIE51bWJlcnMgKG5vdCBpbiBwaHlzaWNhbCB2YWx1ZXMpOgojIHIzMDBtX3Jlc2FtcGxlZDFrbV9BZ2dyIDwtIHJvdW5kKHIzMDBtX3Jlc2FtcGxlZDFrbV9BZ2dyKQpgYGAKCgojIFN0ZXAgNjogQ2hlY2sgdGhlIG91dGNvbWUgYW5kIGZpbmFsIHJlbWFya3MKCkhlcmUgYXJlIGEgY291cGxlIG9mIHBsb3RzIGluIGNhc2UgdGhlIHVzZXIgd2FudHMgdG8gdGFrZSBhIGxvb2sgYXQgdGhlIHJlc2FtcGxlZCBtYXAuCgpgYGB7cn0KcGxvdChyLCBtYWluID0gJ09yaWdpbmFsIG1hcCBhdCAzMDBtJykKYGBgCgpgYGB7cn0KcGxvdChyMzAwbV9yZXNhbXBsZWQxa21fQWdnciwgbWFpbiA9ICdSZXNhbXBsZWQgbWFwIHRvIDFrbScpCmBgYAoKCkluIGFkZGl0aW9uLCB5b3UgY291bGQgYXBwbHkgdGhlIGNob3NlbiByZXNhbXBsZSBtZXRob2Qgb24gb2xkZXIgKG5vbi1uZWFyIHJlYWwgdGltZSkgMzAwbSBwcm9kdWN0cyBhbmQgY29ycmVsYXRlIHRoZSByZXN1bHRzIHdpdGggdGhlIDFrbSBwcm9kdWN0IG9mIHRoZSBzYW1lIHBlcmlvZC4KCltUaGlzXShodHRwczovL2dpdGh1Yi5jb20veGF2aS1ycC9SZXNhbXBsZVRvb2xfbm90ZWJvb2svYmxvYi9tYXN0ZXIvUmVzYW1wbGVfUmVwb3J0X3YyLjUucGRmKSBkb2N1bWVudCBwcm92aWRlcyB0aGUgcmVzdWx0cyBvZiBzdWNoIGNvbXBhcmlzb25zIGJldHdlZW4gMWttLXJlc2FtcGxlZCBhbmQgMWttLXByb2R1Y2VkIGRhdGEgbGF5ZXJzLgo=
diff --git a/Resample_Report_v2.5.pdf b/Resample_Report_v2.5.pdf new file mode 100644 index 0000000..8f93da4 Binary files /dev/null and b/Resample_Report_v2.5.pdf differ diff --git a/Table_cutoff_and_resampleMethod.csv b/Table_cutoff_and_resampleMethod.csv index 630cd70..941c3c0 100644 --- a/Table_cutoff_and_resampleMethod.csv +++ b/Table_cutoff_and_resampleMethod.csv @@ -1,25 +1,24 @@ Product,Version,Data layer in file,Bytes per Cell,Valid DN min, Valid DN max,Valid physical min, Valid physical max,Resample method,Additional cutoff NDVI 300m,v1,NDVI,1,0,250,-0.08,0.92,mean,remove DN>250; phys>0.92 -NDVI 300m,v1,NDVI_unc,2,0,1000,0,1,uncertainty_propagation,none LAI 300m,v1,LAI,1,0,210,0,7,mean,none LAI 300m,v1,RMSE,1,0,210,0,7,mean,none -LAI 300m,v1,LENGTH_AFTER,1,0,60,0,60,closest_to_mean or round_mean_to_integer,none -LAI 300m,v1,LENGTH_BEFORE,1,15,210,15,210,closest_to_mean or round_mean_to_integer,none -LAI 300m,v1,NOBS,1,0,40,0,40,closest_to_mean or round_mean_to_integer,none -LAI 300m,v1,QFLAG,1,0,255,0,255,*,none +LAI 300m,v1,LENGTH_AFTER,1,0,60,0,60,modal,none +LAI 300m,v1,LENGTH_BEFORE,1,15,210,15,210,modal,none +LAI 300m,v1,NOBS,1,0,40,0,40,modal,none +LAI 300m,v1,QFLAG,1,0,255,0,255,modal,none FAPAR 300m,v1,FAPAR,1,0,235,0,0.94,mean,none FAPAR 300m,v1,RMSE,1,0,235,0,0.94,mean,none -FAPAR 300m,v1,LENGTH_AFTER,1,0,60,0,60,closest_to_mean or round_mean_to_integer, -FAPAR 300m,v1,LENGTH_BEFORE,1,15,210,15,210,closest_to_mean or round_mean_to_integer, -FAPAR 300m,v1,NOBS,1,0,40,0,40,closest_to_mean or round_mean_to_integer,none -FAPAR 300m,v1,QFLAG,1,0,255,0,255,*,none +FAPAR 300m,v1,LENGTH_AFTER,1,0,60,0,60,modal, +FAPAR 300m,v1,LENGTH_BEFORE,1,15,210,15,210,modal, +FAPAR 300m,v1,NOBS,1,0,40,0,40,modal,none +FAPAR 300m,v1,QFLAG,1,0,255,0,255,modal,none FCOVER 300m,v1,FCOVER,1,0,250,0,1,mean,none FCOVER 300m,v1,RMSE,1,0,250,0,1,mean,none -FCOVER 300m,v1,LENGTH_AFTER,1,0,60,0,60,closest_to_mean or round_mean_to_integer,none -FCOVER 300m,v1,LENGTH_BEFORE,1,15,210,15,210,closest_to_mean or round_mean_to_integer,none -FCOVER 300m,v1,NOBS,1,0,40,0,40,closest_to_mean or round_mean_to_integer,none -FCOVER 300m,v1,QFLAG,1,0,255,0,255,*,none +FCOVER 300m,v1,LENGTH_AFTER,1,0,60,0,60,modal,none +FCOVER 300m,v1,LENGTH_BEFORE,1,15,210,15,210,modal,none +FCOVER 300m,v1,NOBS,1,0,40,0,40,modal,none +FCOVER 300m,v1,QFLAG,1,0,255,0,255,modal,none DMP 300m,v1,DMP,2,0,32767,0,327.67,mean,remove < 0 -DMP 300m,v1,QFLAG,1,0,255,0,255,*,none +DMP 300m,v1,QFLAG,1,0,255,0,255,modal,none GDMP 300m,v1,GDMP,2,0,32767,0,655.34,mean,remove < 0 -GDMP 300m,v1,QFLAG,1,0,255,0,255,*,none +GDMP 300m,v1,QFLAG,1,0,255,0,255,modal,none diff --git a/ndvi300m_Cat.tif b/ndvi300m_Cat.tif new file mode 100644 index 0000000..c55d36f Binary files /dev/null and b/ndvi300m_Cat.tif differ diff --git a/ndvi300m_Cat_kk.tif b/ndvi300m_Cat_kk.tif deleted file mode 100644 index 137ac11..0000000 Binary files a/ndvi300m_Cat_kk.tif and /dev/null differ