The Global Ecosystem Dynamics Investigation (GEDI) mission aims to characterize ecosystem structure and dynamics to enable radically improved quantification and understanding of the Earth’s carbon cycle and biodiversity. The Land Processes Distributed Active Archive Center (LP DAAC) distributes the GEDI Level 1 and Level 2 Version 1 and Version 2 products. The LP DAAC created the GEDI Finder Web Service to allow users to perform spatial queries of GEDI Version 1 L1-L2 full-orbit granules. One of the updates for GEDI Version 2 included additional spatial metadata that allows users to perform spatial queries via a graphical user interface (GUI) using NASA’s Earthdata Search or programmatically using NASA’s Common Metadata Repository (CMR). Another update is that each GEDI V1 full-orbit granule has been divided into 4 sub-orbit granules in V2.

Use Case Example:

This tutorial was developed using an example use case for a current GEDI Finder user who has been using the GEDI Finder web service in R to find intersecting GEDI L2A Version 1 full-orbit granules over the Amazon Rainforest. The user is now looking to use the same workflow to find intersecting GEDI L2A V2 sub-orbit granules.

This tutorial will show how to use R to perform a spatial query for GEDI V2 data using NASA’s CMR, how to reformat the CMR response into a list of links pointing to the intersecting sub-orbit granules on the LP DAAC Data Pool, and how to export the list of links as a text file.


Applicable Data Products:

This tutorial can be used to perform spatial queries on the following products:

  • GEDI L1B Geolocated Waveform Data Global Footprint Level - GEDI01_B.002
  • GEDI L2A Elevation and Height Metrics Data Global Footprint Level - GEDI02_A.002
  • GEDI L2B Canopy Cover and Vertical Profile Metrics Data Global Footprint Level - GEDI02_B.002

Topics Covered:

  1. Import Packages
  2. Define Function to Query CMR
  3. Execute GEDI_Finder Function
  4. Export Results

Before Starting this Tutorial:

Setup and Dependencies

This tutorial is written as an R Markdown Notebook. In order to execute the tutorial, users will need to have R/RStudio installed, including the required packages to execute an R Markdown notebook. httr is the only package used in this tutorial.

Having trouble getting set up? Contact LP DAAC User Services at: https://lpdaac.usgs.gov/lpdaac-contact-us/


Source Code used to Generate this Tutorial:

If you prefer to execute the code used in this tutorial outside of a Notebook, a simple R script version is available:


1. Import Packages

The only package used in this tutorial is httr.

# Check for required packages, install if not previously installed
if ("httr" %in% rownames(installed.packages()) == FALSE) { install.packages("httr")}
# Import Packages
library(httr)

2. Define Function to Query CMR

In the code cell below, define a function called gedi_finder that takes two user-submitted input values, a product and a bbox.

There are three available products for this function, including ‘GEDI01_B.002’, ‘GEDI02_A.002’ and ‘GEDI02_B.002’. A list is set up to relate each product shortname.version to its associated concept_id, which is a value used by NASA’s CMR to retrieve data for a specific product. Additional information on concept ID’s can be found in the CMR Search API Documentation.

The second user-submitted input value, bbox is a string of bounding box coordinate values (decimal degrees) in the following format: Lower Left Longitude, Lower Left Latitude, Upper Right Longitude, Upper Right Latitude (“LLLon,LLLat,URLon,URLat”)

Example: '-73.65,-12.64,-47.81,9.7'

# Define Function to Query CMR
gedi_finder <- function(product, bbox) {
  # Define the base CMR granule search url, including LPDAAC provider name and max page size (2000 is the max allowed)
  cmr <- "https://cmr.earthdata.nasa.gov/search/granules.json?pretty=true&provider=LPDAAC_ECS&page_size=2000&concept_id="
  
  # Set up list where key is GEDI shortname + version and value is CMR Concept ID
  concept_ids <- list('GEDI01_B.002'='C1908344278-LPDAAC_ECS', 
                      'GEDI02_A.002'='C1908348134-LPDAAC_ECS', 
                      'GEDI02_B.002'='C1908350066-LPDAAC_ECS')
  
  # CMR uses pagination for queries with more features returned than the page size
  page <- 1
  bbox <- sub(' ', '', bbox)  # Remove any white spaces
  granules <- list()          # Set up a list to store and append granule links to
  
  # Send GET request to CMR granule search endpoint w/ product concept ID, bbox & page number
  cmr_response <- GET(sprintf("%s%s&bounding_box=%s&pageNum=%s", cmr, concept_ids[[product]],bbox,page))
  
  # Verify the request submission was successful
  if (cmr_response$status_code==200){
    
    # Send GET request to CMR granule search endpoint w/ product concept ID, bbox & page number, format return as a list
    cmr_url <- sprintf("%s%s&bounding_box=%s&pageNum=%s", cmr, concept_ids[[product]],bbox,page)
    cmr_response <- content(GET(cmr_url))$feed$entry
    
    # If 2000 features are returned, move to the next page and submit another request, and append to the response
    while(length(cmr_response) %% 2000 == 0){
      page <- page + 1
      cmr_url <- sprintf("%s%s&bounding_box=%s&pageNum=%s", cmr, concept_ids[[product]],bbox,page)
      cmr_response <- c(cmr_response, content(GET(cmr_url))$feed$entry)
    }
    
    # CMR returns more info than just the Data Pool links, below use for loop to grab each DP link, and add to list
    for (i in 1:length(cmr_response)) {
      granules[[i]] <- cmr_response[[i]]$links[[1]]$href
    }
    
    # Print the number of granules found and return the list of links
    print(length(granules))
    return(granules)
  } else {
    
    # If the request did not complete successfully, print out the response from CMR
    print(content(GET(sprintf("%s%s&bounding_box=%s&pageNum=%s", cmr, concept_ids[[product]],bbox,page)))$errors)
  }
}

The function returns a list of links to download the intersecting GEDI sub-orbit V2 granules directly from the LP DAAC’s Data Pool.

3. Execute GEDI Finder Function

Below is a demonstration of how to set the two required inputs to the gedi_finder function to variables.

# User-provided inputs (update for your desired product and bounding box region of interest)
product <- 'GEDI02_B.002'           # Options include 'GEDI01_B.002', 'GEDI02_A.002', 'GEDI02_B.002'
bbox <- '-73.65,-12.64,-47.81,9.7'  # bounding box coords in LL Longitude, LL Latitude, UR Longitude, UR Latitude format

Above, the variables are defined to query the GEDI02_B.002 product for a bounding box covering the Amazon Rainforest.

Next, call the gedi_finder function for the desired product and bounding box region of interest defined above, and set the output to a variable.

# Call the gedi_finder function using the user-provided inputs
granules <- gedi_finder(product, bbox)
[1] 938
print(sprintf("%s %s Version 2 granules found.", length(granules), product))
[1] "938 GEDI02_B.002 Version 2 granules found."

Notice the print statement above will notify you how many granules intersected your bounding box for the product requested.

4. Export Results

Below is a demonstration of how to take the granules list of Data Pool links for intersecting GEDI V2 granules and export as a text file. The text file will be written to your current working directory, and will be named based on the date and time that the file was created.

# Export Results
# Set up output textfile name using the current datetime
outName <- sprintf("GEDI_FINDER_Results_%s.txt", format(Sys.time(), "%Y%m%d%H%M%S"))
# Save to text file in current working directory
write.table(granules, outName, row.names = FALSE, col.names = FALSE, quote = FALSE, sep='\n')
print(sprintf("File containing links to intersecting %s Version 2 data has been saved to: %s/%s", product, getwd(), outName))
[1] "File containing links to intersecting GEDI02_B.002 Version 2 data has been saved to: D:/GEDI_Finder/GEDI_FINDER_Results_20210507111303.txt"

Additional Resources

Looking to bulk download the intersecting GEDI V2 files from your request? Check out the following LP DAAC resources to get you started: 1. How to Access LP DAAC Data from the Command Line
2. How to Access the LP DAAC Data Pool with Python
3. How to Access the LP DAAC Data Pool with R

Also be sure to check out the following GEDI Resources:

  1. GEDI Spatial Querying and Subsetting Quick Guide V2
  • Explains how to perform spatial querying and subsetting of GEDI V2 data directly in NASA’s Earthdata Search Client
  1. GEDI Spatial and Band/layer Subsetting and Export to GeoJSON (GEDI Subsetter) Script
  • Allows you to subset GEDI V2 data by band/layer and region of interest
  1. Getting Started with GEDI L1B, L2A, and L2B V2 Data in Python Tutorial Series
  • Includes a series of tutorials that demonstrate how to start working with GEDI V2 data in Python.

Contact Information
Material written by Cole Krehbiel\(^{1}\)
Contact: LPDAAC@usgs.gov
Voice: +1-605-594-6116
Organization: Land Processes Distributed Active Archive Center (LP DAAC)
Website: https://lpdaac.usgs.gov/
Date last modified: 05-10-2021

\(^{1}\)KBR Inc., contractor to the U.S. Geological Survey, Earth Resources Observation and Science (EROS) Center, Sioux Falls, South Dakota, 57198-001, USA. Work performed under USGS contract G15PD00467 for LP DAAC\(^{2}\).

\(^{2}\)LP DAAC Work performed under NASA contract NNG14HH33I.

LS0tDQp0aXRsZTogIlNwYXRpYWwgUXVlcnlpbmcgb2YgR0VESSBWZXJzaW9uIDIgRGF0YSBpbiBSIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGRmX3ByaW50OiBwYWdlZA0KLS0tDQpUaGUgR2xvYmFsIEVjb3N5c3RlbSBEeW5hbWljcyBJbnZlc3RpZ2F0aW9uIChbR0VESV0oaHR0cHM6Ly9scGRhYWMudXNncy5nb3YvZGF0YS9nZXQtc3RhcnRlZC1kYXRhL2NvbGxlY3Rpb24tb3ZlcnZpZXcvbWlzc2lvbnMvZ2VkaS1vdmVydmlldy8pKSBtaXNzaW9uIGFpbXMgdG8gY2hhcmFjdGVyaXplIGVjb3N5c3RlbSBzdHJ1Y3R1cmUgYW5kIGR5bmFtaWNzIHRvIGVuYWJsZSByYWRpY2FsbHkgaW1wcm92ZWQgcXVhbnRpZmljYXRpb24gYW5kIHVuZGVyc3RhbmRpbmcgb2YgdGhlIEVhcnRoJ3MgY2FyYm9uIGN5Y2xlIGFuZCBiaW9kaXZlcnNpdHkuIFRoZSBMYW5kIFByb2Nlc3NlcyBEaXN0cmlidXRlZCBBY3RpdmUgQXJjaGl2ZSBDZW50ZXIgKExQIERBQUMpIGRpc3RyaWJ1dGVzIHRoZSBHRURJIExldmVsIDEgYW5kIExldmVsIDIgVmVyc2lvbiAxIGFuZCBWZXJzaW9uIDIgcHJvZHVjdHMuIFRoZSBMUCBEQUFDIGNyZWF0ZWQgdGhlIEdFREkgRmluZGVyIF9XZWIgU2VydmljZV8gdG8gYWxsb3cgdXNlcnMgdG8gcGVyZm9ybSBzcGF0aWFsIHF1ZXJpZXMgb2YgR0VESSBfVmVyc2lvbiAxXyBMMS1MMiBmdWxsLW9yYml0IGdyYW51bGVzLiBPbmUgb2YgdGhlIHVwZGF0ZXMgZm9yIEdFREkgX1ZlcnNpb24gMl8gaW5jbHVkZWQgYWRkaXRpb25hbCBzcGF0aWFsIG1ldGFkYXRhIHRoYXQgYWxsb3dzIHVzZXJzIHRvIHBlcmZvcm0gc3BhdGlhbCBxdWVyaWVzIHZpYSBhIGdyYXBoaWNhbCB1c2VyIGludGVyZmFjZSAoR1VJKSB1c2luZyBOQVNBJ3MgW0VhcnRoZGF0YSBTZWFyY2hdKGh0dHBzOi8vc2VhcmNoLmVhcnRoZGF0YS5uYXNhLmdvdi9zZWFyY2gpIG9yIHByb2dyYW1tYXRpY2FsbHkgdXNpbmcgTkFTQSdzIFtDb21tb24gTWV0YWRhdGEgUmVwb3NpdG9yeV0oaHR0cHM6Ly9jbXIuZWFydGhkYXRhLm5hc2EuZ292L3NlYXJjaCkgKENNUikuIEFub3RoZXIgdXBkYXRlIGlzIHRoYXQgZWFjaCBHRURJIFYxIGZ1bGwtb3JiaXQgZ3JhbnVsZSBoYXMgYmVlbiBkaXZpZGVkIGludG8gNCBzdWItb3JiaXQgZ3JhbnVsZXMgaW4gVjIuICAgIA0KDQojIyMgVGhlIG9iamVjdGl2ZSBvZiB0aGlzIHR1dG9yaWFsIGlzIHRvIGRlbW9uc3RyYXRlIGhvdyBjdXJyZW50IEdFREkgRmluZGVyIHVzZXJzIGNhbiB1cGRhdGUgdGhlaXIgd29ya2Zsb3cgZm9yIEdFREkgVmVyc2lvbiAyIChWMikgZGF0YSB1c2luZyBOQVNBJ3MgQ01SIHRvIHBlcmZvcm0gc3BhdGlhbCBbYm91bmRpbmcgYm94XSBxdWVyaWVzIGZvciBHRURJIFYyIEwxQiwgTDJBLCBhbmQgTDJCIGRhdGEsIGFuZCBob3cgdG8gcmVmb3JtYXQgdGhlIENNUiByZXNwb25zZSBpbnRvIGEgbGlzdCBvZiBsaW5rcyB0aGF0IHdpbGwgYWxsb3cgdXNlcnMgdG8gZG93bmxvYWQgdGhlIGludGVyc2VjdGluZyBHRURJIFYyIHN1Yi1vcmJpdCBncmFudWxlcyBkaXJlY3RseSBmcm9tIHRoZSBMUCBEQUFDIERhdGEgUG9vbC4gDQoNCiMjIFVzZSBDYXNlIEV4YW1wbGU6ICANClRoaXMgdHV0b3JpYWwgd2FzIGRldmVsb3BlZCB1c2luZyBhbiBleGFtcGxlIHVzZSBjYXNlIGZvciBhIGN1cnJlbnQgR0VESSBGaW5kZXIgdXNlciB3aG8gaGFzIGJlZW4gdXNpbmcgdGhlIEdFREkgRmluZGVyIHdlYiBzZXJ2aWNlIGluIFIgdG8gZmluZCBpbnRlcnNlY3RpbmcgR0VESSBMMkEgVmVyc2lvbiAxIGZ1bGwtb3JiaXQgZ3JhbnVsZXMgb3ZlciB0aGUgQW1hem9uIFJhaW5mb3Jlc3QuIFRoZSB1c2VyIGlzIG5vdyBsb29raW5nIHRvIHVzZSB0aGUgc2FtZSB3b3JrZmxvdyB0byBmaW5kIGludGVyc2VjdGluZyBHRURJIEwyQSBWMiBzdWItb3JiaXQgZ3JhbnVsZXMuICANCg0KVGhpcyB0dXRvcmlhbCB3aWxsIHNob3cgaG93IHRvIHVzZSBSIHRvIHBlcmZvcm0gYSBzcGF0aWFsIHF1ZXJ5IGZvciBHRURJIFYyIGRhdGEgdXNpbmcgTkFTQSdzIENNUiwgaG93IHRvIHJlZm9ybWF0IHRoZSBDTVIgcmVzcG9uc2UgaW50byBhIGxpc3Qgb2YgbGlua3MgcG9pbnRpbmcgdG8gdGhlIGludGVyc2VjdGluZyBzdWItb3JiaXQgZ3JhbnVsZXMgb24gdGhlIExQIERBQUMgRGF0YSBQb29sLCBhbmQgaG93IHRvIGV4cG9ydCB0aGUgbGlzdCBvZiBsaW5rcyBhcyBhIHRleHQgZmlsZS4gICANCg0KKioqICAgIA0KDQojIyBBcHBsaWNhYmxlIERhdGEgUHJvZHVjdHM6IA0KIyMjIFRoaXMgdHV0b3JpYWwgY2FuIGJlIHVzZWQgdG8gcGVyZm9ybSBzcGF0aWFsIHF1ZXJpZXMgb24gdGhlIGZvbGxvd2luZyBwcm9kdWN0czoNCi0gKipHRURJIEwxQiBHZW9sb2NhdGVkIFdhdmVmb3JtIERhdGEgR2xvYmFsIEZvb3RwcmludCBMZXZlbCAtIFtHRURJMDFfQi4wMDJdKGh0dHBzOi8vZG9pLm9yZy8xMC41MDY3L0dFREkvR0VESTAxX0IuMDAyKSoqDQotICoqR0VESSBMMkEgRWxldmF0aW9uIGFuZCBIZWlnaHQgTWV0cmljcyBEYXRhIEdsb2JhbCBGb290cHJpbnQgTGV2ZWwgLSBbR0VESTAyX0EuMDAyXShodHRwczovL2RvaS5vcmcvMTAuNTA2Ny9HRURJL0dFREkwMl9BLjAwMikqKg0KLSAqKkdFREkgTDJCIENhbm9weSBDb3ZlciBhbmQgVmVydGljYWwgUHJvZmlsZSBNZXRyaWNzIERhdGEgR2xvYmFsIEZvb3RwcmludCBMZXZlbCAtIFtHRURJMDJfQi4wMDJdKGh0dHBzOi8vZG9pLm9yZy8xMC41MDY3L0dFREkvR0VESTAyX0IuMDAyKSoqDQoNCioqKiAgDQoNCiMgVG9waWNzIENvdmVyZWQ6DQoxLiBbKipJbXBvcnQgUGFja2FnZXMqKl0oI2ltcG9ydHBhY2thZ2VzKSAgDQoyLiBbKipEZWZpbmUgRnVuY3Rpb24gdG8gUXVlcnkgQ01SKipdKCNkZWZpbmVmdW5jdGlvbikgICAgICANCjMuIFsqKkV4ZWN1dGUgR0VESV9GaW5kZXIgRnVuY3Rpb24qKl0oI2V4ZWN1dGVmdW5jdGlvbikgICAgICANCjQuIFsqKkV4cG9ydCBSZXN1bHRzKipdKCNleHBvcnRyZXN1bHRzKSAgICAgICAgDQoNCioqKg0KDQojIEJlZm9yZSBTdGFydGluZyB0aGlzIFR1dG9yaWFsOg0KIyMgU2V0dXAgYW5kIERlcGVuZGVuY2llcyANClRoaXMgdHV0b3JpYWwgaXMgd3JpdHRlbiBhcyBhbiBSIE1hcmtkb3duIE5vdGVib29rLiBJbiBvcmRlciB0byBleGVjdXRlIHRoZSB0dXRvcmlhbCwgdXNlcnMgd2lsbCBuZWVkIHRvIGhhdmUgUi9SU3R1ZGlvIGluc3RhbGxlZCwgaW5jbHVkaW5nIHRoZSByZXF1aXJlZCBwYWNrYWdlcyB0byBleGVjdXRlIGFuIFIgTWFya2Rvd24gbm90ZWJvb2suIGBodHRyYCBpcyB0aGUgb25seSBwYWNrYWdlIHVzZWQgaW4gdGhpcyB0dXRvcmlhbC4NCg0KIyMjIyBIYXZpbmcgdHJvdWJsZSBnZXR0aW5nIHNldCB1cD8gQ29udGFjdCBMUCBEQUFDIFVzZXIgU2VydmljZXMgYXQ6IGh0dHBzOi8vbHBkYWFjLnVzZ3MuZ292L2xwZGFhYy1jb250YWN0LXVzLw0KDQoqKioNCg0KIyMgU291cmNlIENvZGUgdXNlZCB0byBHZW5lcmF0ZSB0aGlzIFR1dG9yaWFsOg0KIyMjIyBUaGUgcmVwb3NpdG9yeSBjb250YWluaW5nIHRoZSBmaWxlcyBpcyBsb2NhdGVkIGF0OiBodHRwczovL2dpdC5lYXJ0aGRhdGEubmFzYS5nb3YvcHJvamVjdHMvTFBEVVIvcmVwb3MvZ2VkaS1maW5kZXItdHV0b3JpYWwtci9icm93c2UgIA0KLSBbUiBOb3RlYm9va10oaHR0cHM6Ly9naXQuZWFydGhkYXRhLm5hc2EuZ292L3Byb2plY3RzL0xQRFVSL3JlcG9zL2dlZGktZmluZGVyLXR1dG9yaWFsLXIvYnJvd3NlL0dFRElfRmluZGVyX1R1dG9yaWFsX1Iucm1kKSAgICANCg0KIyMjIyBJZiB5b3UgcHJlZmVyIHRvIGV4ZWN1dGUgdGhlIGNvZGUgdXNlZCBpbiB0aGlzIHR1dG9yaWFsIG91dHNpZGUgb2YgYSBOb3RlYm9vaywgYSBzaW1wbGUgUiBzY3JpcHQgdmVyc2lvbiBpcyBhdmFpbGFibGU6ICANCi0gW1IgU2NyaXB0XShodHRwczovL2dpdC5lYXJ0aGRhdGEubmFzYS5nb3YvcHJvamVjdHMvTFBEVVIvcmVwb3MvZ2VkaS1maW5kZXItdHV0b3JpYWwtci9icm93c2UvR0VESV9GaW5kZXIuUikgIA0KDQoqKioNCg0KIyAxLiBJbXBvcnQgUGFja2FnZXMgPGEgaWQ9ImltcG9ydHBhY2thZ2VzIj48L2E+DQpUaGUgb25seSBwYWNrYWdlIHVzZWQgaW4gdGhpcyB0dXRvcmlhbCBpcyBgaHR0cmAuDQoNCmBgYHtyfQ0KIyBDaGVjayBmb3IgcmVxdWlyZWQgcGFja2FnZXMsIGluc3RhbGwgaWYgbm90IHByZXZpb3VzbHkgaW5zdGFsbGVkDQppZiAoImh0dHIiICVpbiUgcm93bmFtZXMoaW5zdGFsbGVkLnBhY2thZ2VzKCkpID09IEZBTFNFKSB7IGluc3RhbGwucGFja2FnZXMoImh0dHIiKX0NCg0KIyBJbXBvcnQgUGFja2FnZXMNCmxpYnJhcnkoaHR0cikNCmBgYA0KDQojIDIuIERlZmluZSBGdW5jdGlvbiB0byBRdWVyeSBDTVIgPGEgaWQ9ImRlZmluZWZ1bmN0aW9uIj48L2E+DQpJbiB0aGUgY29kZSBjZWxsIGJlbG93LCBkZWZpbmUgYSBmdW5jdGlvbiBjYWxsZWQgYGdlZGlfZmluZGVyYCB0aGF0IHRha2VzIHR3byAqdXNlci1zdWJtaXR0ZWQqIGlucHV0IHZhbHVlcywgYSBgcHJvZHVjdGAgYW5kIGEgYGJib3hgLiAgDQoNClRoZXJlIGFyZSB0aHJlZSBhdmFpbGFibGUgcHJvZHVjdHMgZm9yIHRoaXMgZnVuY3Rpb24sIGluY2x1ZGluZyAnR0VESTAxX0IuMDAyJywgJ0dFREkwMl9BLjAwMicgYW5kICdHRURJMDJfQi4wMDInLiBBIGxpc3QgaXMgc2V0IHVwIHRvIHJlbGF0ZSBlYWNoIHByb2R1Y3QgYHNob3J0bmFtZS52ZXJzaW9uYCB0byBpdHMgYXNzb2NpYXRlZCBgY29uY2VwdF9pZGAsIHdoaWNoIGlzIGEgdmFsdWUgdXNlZCBieSBOQVNBJ3MgQ01SIHRvIHJldHJpZXZlIGRhdGEgZm9yIGEgc3BlY2lmaWMgcHJvZHVjdC4gQWRkaXRpb25hbCBpbmZvcm1hdGlvbiBvbiBjb25jZXB0IElEJ3MgY2FuIGJlIGZvdW5kIGluIHRoZSBbQ01SIFNlYXJjaCBBUEkgRG9jdW1lbnRhdGlvbl0oaHR0cHM6Ly9jbXIuZWFydGhkYXRhLm5hc2EuZ292L3NlYXJjaC9zaXRlL2RvY3Mvc2VhcmNoL2FwaS5odG1sI2MtY29uY2VwdC1pZCkuDQoNClRoZSBzZWNvbmQgdXNlci1zdWJtaXR0ZWQgaW5wdXQgdmFsdWUsIGBiYm94YCBpcyBhIHN0cmluZyBvZiBib3VuZGluZyBib3ggY29vcmRpbmF0ZSB2YWx1ZXMgKGRlY2ltYWwgZGVncmVlcykgaW4gdGhlIGZvbGxvd2luZyBmb3JtYXQ6DQpMb3dlciBMZWZ0IExvbmdpdHVkZSwgTG93ZXIgTGVmdCBMYXRpdHVkZSwgVXBwZXIgUmlnaHQgTG9uZ2l0dWRlLCBVcHBlciBSaWdodCBMYXRpdHVkZSAoIkxMTG9uLExMTGF0LFVSTG9uLFVSTGF0IikgIA0KDQo+ICMjIyMgRXhhbXBsZTogYCctNzMuNjUsLTEyLjY0LC00Ny44MSw5LjcnYA0KDQpgYGB7cn0NCiMgRGVmaW5lIEZ1bmN0aW9uIHRvIFF1ZXJ5IENNUg0KZ2VkaV9maW5kZXIgPC0gZnVuY3Rpb24ocHJvZHVjdCwgYmJveCkgew0KDQogICMgRGVmaW5lIHRoZSBiYXNlIENNUiBncmFudWxlIHNlYXJjaCB1cmwsIGluY2x1ZGluZyBMUERBQUMgcHJvdmlkZXIgbmFtZSBhbmQgbWF4IHBhZ2Ugc2l6ZSAoMjAwMCBpcyB0aGUgbWF4IGFsbG93ZWQpDQogIGNtciA8LSAiaHR0cHM6Ly9jbXIuZWFydGhkYXRhLm5hc2EuZ292L3NlYXJjaC9ncmFudWxlcy5qc29uP3ByZXR0eT10cnVlJnByb3ZpZGVyPUxQREFBQ19FQ1MmcGFnZV9zaXplPTIwMDAmY29uY2VwdF9pZD0iDQogIA0KICAjIFNldCB1cCBsaXN0IHdoZXJlIGtleSBpcyBHRURJIHNob3J0bmFtZSArIHZlcnNpb24gYW5kIHZhbHVlIGlzIENNUiBDb25jZXB0IElEDQogIGNvbmNlcHRfaWRzIDwtIGxpc3QoJ0dFREkwMV9CLjAwMic9J0MxOTA4MzQ0Mjc4LUxQREFBQ19FQ1MnLCANCiAgICAgICAgICAgICAgICAgICAgICAnR0VESTAyX0EuMDAyJz0nQzE5MDgzNDgxMzQtTFBEQUFDX0VDUycsIA0KICAgICAgICAgICAgICAgICAgICAgICdHRURJMDJfQi4wMDInPSdDMTkwODM1MDA2Ni1MUERBQUNfRUNTJykNCiAgDQogICMgQ01SIHVzZXMgcGFnaW5hdGlvbiBmb3IgcXVlcmllcyB3aXRoIG1vcmUgZmVhdHVyZXMgcmV0dXJuZWQgdGhhbiB0aGUgcGFnZSBzaXplDQogIHBhZ2UgPC0gMQ0KICBiYm94IDwtIHN1YignICcsICcnLCBiYm94KSAgIyBSZW1vdmUgYW55IHdoaXRlIHNwYWNlcw0KICBncmFudWxlcyA8LSBsaXN0KCkgICAgICAgICAgIyBTZXQgdXAgYSBsaXN0IHRvIHN0b3JlIGFuZCBhcHBlbmQgZ3JhbnVsZSBsaW5rcyB0bw0KICANCiAgIyBTZW5kIEdFVCByZXF1ZXN0IHRvIENNUiBncmFudWxlIHNlYXJjaCBlbmRwb2ludCB3LyBwcm9kdWN0IGNvbmNlcHQgSUQsIGJib3ggJiBwYWdlIG51bWJlcg0KICBjbXJfcmVzcG9uc2UgPC0gR0VUKHNwcmludGYoIiVzJXMmYm91bmRpbmdfYm94PSVzJnBhZ2VOdW09JXMiLCBjbXIsIGNvbmNlcHRfaWRzW1twcm9kdWN0XV0sYmJveCxwYWdlKSkNCiAgDQogICMgVmVyaWZ5IHRoZSByZXF1ZXN0IHN1Ym1pc3Npb24gd2FzIHN1Y2Nlc3NmdWwNCiAgaWYgKGNtcl9yZXNwb25zZSRzdGF0dXNfY29kZT09MjAwKXsNCiAgICANCiAgICAjIFNlbmQgR0VUIHJlcXVlc3QgdG8gQ01SIGdyYW51bGUgc2VhcmNoIGVuZHBvaW50IHcvIHByb2R1Y3QgY29uY2VwdCBJRCwgYmJveCAmIHBhZ2UgbnVtYmVyLCBmb3JtYXQgcmV0dXJuIGFzIGEgbGlzdA0KICAgIGNtcl91cmwgPC0gc3ByaW50ZigiJXMlcyZib3VuZGluZ19ib3g9JXMmcGFnZU51bT0lcyIsIGNtciwgY29uY2VwdF9pZHNbW3Byb2R1Y3RdXSxiYm94LHBhZ2UpDQogICAgY21yX3Jlc3BvbnNlIDwtIGNvbnRlbnQoR0VUKGNtcl91cmwpKSRmZWVkJGVudHJ5DQogICAgDQogICAgIyBJZiAyMDAwIGZlYXR1cmVzIGFyZSByZXR1cm5lZCwgbW92ZSB0byB0aGUgbmV4dCBwYWdlIGFuZCBzdWJtaXQgYW5vdGhlciByZXF1ZXN0LCBhbmQgYXBwZW5kIHRvIHRoZSByZXNwb25zZQ0KICAgIHdoaWxlKGxlbmd0aChjbXJfcmVzcG9uc2UpICUlIDIwMDAgPT0gMCl7DQogICAgICBwYWdlIDwtIHBhZ2UgKyAxDQogICAgICBjbXJfdXJsIDwtIHNwcmludGYoIiVzJXMmYm91bmRpbmdfYm94PSVzJnBhZ2VOdW09JXMiLCBjbXIsIGNvbmNlcHRfaWRzW1twcm9kdWN0XV0sYmJveCxwYWdlKQ0KICAgICAgY21yX3Jlc3BvbnNlIDwtIGMoY21yX3Jlc3BvbnNlLCBjb250ZW50KEdFVChjbXJfdXJsKSkkZmVlZCRlbnRyeSkNCiAgICB9DQogICAgDQogICAgIyBDTVIgcmV0dXJucyBtb3JlIGluZm8gdGhhbiBqdXN0IHRoZSBEYXRhIFBvb2wgbGlua3MsIGJlbG93IHVzZSBmb3IgbG9vcCB0byBncmFiIGVhY2ggRFAgbGluaywgYW5kIGFkZCB0byBsaXN0DQogICAgZm9yIChpIGluIDE6bGVuZ3RoKGNtcl9yZXNwb25zZSkpIHsNCiAgICAgIGdyYW51bGVzW1tpXV0gPC0gY21yX3Jlc3BvbnNlW1tpXV0kbGlua3NbWzFdXSRocmVmDQogICAgfQ0KICAgIA0KICAgICMgUmV0dXJuIHRoZSBsaXN0IG9mIGxpbmtzDQogICAgcmV0dXJuKGdyYW51bGVzKQ0KICB9IGVsc2Ugew0KICAgIA0KICAgICMgSWYgdGhlIHJlcXVlc3QgZGlkIG5vdCBjb21wbGV0ZSBzdWNjZXNzZnVsbHksIHByaW50IG91dCB0aGUgcmVzcG9uc2UgZnJvbSBDTVINCiAgICBwcmludChjb250ZW50KEdFVChzcHJpbnRmKCIlcyVzJmJvdW5kaW5nX2JveD0lcyZwYWdlTnVtPSVzIiwgY21yLCBjb25jZXB0X2lkc1tbcHJvZHVjdF1dLGJib3gscGFnZSkpKSRlcnJvcnMpDQogIH0NCn0NCg0KYGBgDQpUaGUgZnVuY3Rpb24gcmV0dXJucyBhIGxpc3Qgb2YgbGlua3MgdG8gZG93bmxvYWQgdGhlIGludGVyc2VjdGluZyBHRURJIHN1Yi1vcmJpdCBWMiBncmFudWxlcyBkaXJlY3RseSBmcm9tIHRoZSBMUCBEQUFDJ3MgRGF0YSBQb29sLiANCg0KIyAzLiBFeGVjdXRlIEdFREkgRmluZGVyIEZ1bmN0aW9uIDxhIGlkPSJleGVjdXRlZnVuY3Rpb24iPjwvYT4NCiMjIyBCZWxvdyBpcyBhIGRlbW9uc3RyYXRpb24gb2YgaG93IHRvIHNldCB0aGUgdHdvIHJlcXVpcmVkIGlucHV0cyB0byB0aGUgYGdlZGlfZmluZGVyYCBmdW5jdGlvbiB0byB2YXJpYWJsZXMuDQpgYGB7cn0NCiMgVXNlci1wcm92aWRlZCBpbnB1dHMgKFVQREFURSBGT1IgWU9VUiBERVNJUkVEIFBST0RVQ1QgQU5EIEJPVU5ESU5HIEJPWCBSRUdJT04gT0YgSU5URVJFU1QpDQpwcm9kdWN0IDwtICdHRURJMDJfQi4wMDInICAgICAgICAgICAjIE9wdGlvbnMgaW5jbHVkZSAnR0VESTAxX0IuMDAyJywgJ0dFREkwMl9BLjAwMicsICdHRURJMDJfQi4wMDInDQpiYm94IDwtICctNzMuNjUsLTEyLjY0LC00Ny44MSw5LjcnICAjIGJvdW5kaW5nIGJveCBjb29yZHMgaW4gTEwgTG9uZ2l0dWRlLCBMTCBMYXRpdHVkZSwgVVIgTG9uZ2l0dWRlLCBVUiBMYXRpdHVkZSBmb3JtYXQNCg0KYGBgDQpBYm92ZSwgdGhlIHZhcmlhYmxlcyBhcmUgZGVmaW5lZCB0byBxdWVyeSB0aGUgYEdFREkwMl9CLjAwMmAgcHJvZHVjdCBmb3IgYSBib3VuZGluZyBib3ggY292ZXJpbmcgdGhlIEFtYXpvbiBSYWluZm9yZXN0Lg0KDQpOZXh0LCBjYWxsIHRoZSBgZ2VkaV9maW5kZXJgIGZ1bmN0aW9uIGZvciB0aGUgZGVzaXJlZCBwcm9kdWN0IGFuZCBib3VuZGluZyBib3ggcmVnaW9uIG9mIGludGVyZXN0IGRlZmluZWQgYWJvdmUsIGFuZCBzZXQgdGhlIG91dHB1dCB0byBhIHZhcmlhYmxlLg0KDQpgYGB7cn0NCiMgQ2FsbCB0aGUgZ2VkaV9maW5kZXIgZnVuY3Rpb24gdXNpbmcgdGhlIHVzZXItcHJvdmlkZWQgaW5wdXRzDQpncmFudWxlcyA8LSBnZWRpX2ZpbmRlcihwcm9kdWN0LCBiYm94KQ0KcHJpbnQoc3ByaW50ZigiJXMgJXMgVmVyc2lvbiAyIGdyYW51bGVzIGZvdW5kLiIsIGxlbmd0aChncmFudWxlcyksIHByb2R1Y3QpKQ0KDQpgYGANCk5vdGljZSB0aGUgcHJpbnQgc3RhdGVtZW50IGFib3ZlIHdpbGwgbm90aWZ5IHlvdSBob3cgbWFueSBncmFudWxlcyBpbnRlcnNlY3RlZCB5b3VyIGJvdW5kaW5nIGJveCBmb3IgdGhlIHByb2R1Y3QgcmVxdWVzdGVkLg0KDQojIDQuIEV4cG9ydCBSZXN1bHRzIDxhIGlkPSJleHBvcnRyZXN1bHRzIj48L2E+DQoNCkJlbG93IGlzIGEgZGVtb25zdHJhdGlvbiBvZiBob3cgdG8gdGFrZSB0aGUgYGdyYW51bGVzYCBsaXN0IG9mIERhdGEgUG9vbCBsaW5rcyBmb3IgaW50ZXJzZWN0aW5nIEdFREkgVjIgZ3JhbnVsZXMgYW5kIGV4cG9ydCBhcyBhIHRleHQgZmlsZS4gVGhlIHRleHQgZmlsZSB3aWxsIGJlIHdyaXR0ZW4gdG8geW91ciBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5LCBhbmQgd2lsbCBiZSBuYW1lZCBiYXNlZCBvbiB0aGUgZGF0ZSBhbmQgdGltZSB0aGF0IHRoZSBmaWxlIHdhcyBjcmVhdGVkLiANCmBgYHtyfQ0KIyBFeHBvcnQgUmVzdWx0cw0KIyBTZXQgdXAgb3V0cHV0IHRleHRmaWxlIG5hbWUgdXNpbmcgdGhlIGN1cnJlbnQgZGF0ZXRpbWUNCm91dE5hbWUgPC0gc3ByaW50ZigiJXNfR3JhbnVsZUxpc3RfJXMudHh0Iiwgc3ViKCcuMDAyJywgJ18wMDInLCBwcm9kdWN0KSwgZm9ybWF0KFN5cy50aW1lKCksICIlWSVtJWQlSCVNJVMiKSkNCg0KIyBTYXZlIHRvIHRleHQgZmlsZSBpbiBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5DQp3cml0ZS50YWJsZShncmFudWxlcywgb3V0TmFtZSwgcm93Lm5hbWVzID0gRkFMU0UsIGNvbC5uYW1lcyA9IEZBTFNFLCBxdW90ZSA9IEZBTFNFLCBzZXA9J1xuJykNCnByaW50KHNwcmludGYoIkZpbGUgY29udGFpbmluZyBsaW5rcyB0byBpbnRlcnNlY3RpbmcgJXMgVmVyc2lvbiAyIGRhdGEgaGFzIGJlZW4gc2F2ZWQgdG86ICVzLyVzIiwgcHJvZHVjdCwgZ2V0d2QoKSwgb3V0TmFtZSkpDQoNCmBgYA0KDQojIyBBZGRpdGlvbmFsIFJlc291cmNlcw0KTG9va2luZyB0byBidWxrIGRvd25sb2FkIHRoZSBpbnRlcnNlY3RpbmcgR0VESSBWMiBmaWxlcyBmcm9tIHlvdXIgcmVxdWVzdD8gQ2hlY2sgb3V0IHRoZSBmb2xsb3dpbmcgTFAgREFBQyByZXNvdXJjZXMgdG8gZ2V0IHlvdSBzdGFydGVkOg0KMS4gW0hvdyB0byBBY2Nlc3MgTFAgREFBQyBEYXRhIGZyb20gdGhlIENvbW1hbmQgTGluZV0oaHR0cHM6Ly9scGRhYWMudXNncy5nb3YvcmVzb3VyY2VzL2UtbGVhcm5pbmcvaG93LWFjY2Vzcy1scC1kYWFjLWRhdGEtY29tbWFuZC1saW5lLykgIA0KMi4gW0hvdyB0byBBY2Nlc3MgdGhlIExQIERBQUMgRGF0YSBQb29sIHdpdGggUHl0aG9uXShodHRwczovL2dpdC5lYXJ0aGRhdGEubmFzYS5nb3YvcHJvamVjdHMvTFBEVVIvcmVwb3MvZGFhY19kYXRhX2Rvd25sb2FkX3B5dGhvbi9icm93c2UpICANCjMuIFtIb3cgdG8gQWNjZXNzIHRoZSBMUCBEQUFDIERhdGEgUG9vbCB3aXRoIFJdKGh0dHBzOi8vZ2l0LmVhcnRoZGF0YS5uYXNhLmdvdi9wcm9qZWN0cy9MUERVUi9yZXBvcy9kYWFjX2RhdGFfZG93bmxvYWRfci9icm93c2UpICANCg0KQWxzbyBiZSBzdXJlIHRvIGNoZWNrIG91dCB0aGUgZm9sbG93aW5nIEdFREkgUmVzb3VyY2VzOiAgDQoNCjEuIFtHRURJIFNwYXRpYWwgUXVlcnlpbmcgYW5kIFN1YnNldHRpbmcgUXVpY2sgR3VpZGUgVjJdKGh0dHBzOi8vbHBkYWFjLnVzZ3MuZ292L2RvY3VtZW50cy85ODkvR0VESV9RdWlja19HdWlkZV9WMi5wZGYpICAgIA0KICANCiAgLSBFeHBsYWlucyBob3cgdG8gcGVyZm9ybSBzcGF0aWFsIHF1ZXJ5aW5nIGFuZCBzdWJzZXR0aW5nIG9mIEdFREkgVjIgZGF0YSBkaXJlY3RseSBpbiBOQVNBJ3MgRWFydGhkYXRhIFNlYXJjaCBDbGllbnQgIA0KICANCjIuIFtHRURJIFNwYXRpYWwgYW5kIEJhbmQvbGF5ZXIgU3Vic2V0dGluZyBhbmQgRXhwb3J0IHRvIEdlb0pTT04gKEdFREkgU3Vic2V0dGVyKSAgIFNjcmlwdF0oaHR0cHM6Ly9naXQuZWFydGhkYXRhLm5hc2EuZ292L3Byb2plY3RzL0xQRFVSL3JlcG9zL2dlZGktc3Vic2V0dGVyL2Jyb3dzZT9fZ2E9Mi4xOTQ0MTU1MjguNDAyMDYyNjc3LjE2MjAwNTA4MzItMTE2MjYzMDQ4NS4xNjE1MjE3OTAwKSAgIA0KICANCiAgLSBBbGxvd3MgeW91IHRvIHN1YnNldCBHRURJIFYyIGRhdGEgYnkgYmFuZC9sYXllciBhbmQgcmVnaW9uIG9mIGludGVyZXN0ICANCiAgDQozLiBbR2V0dGluZyBTdGFydGVkIHdpdGggR0VESSBMMUIsIEwyQSwgYW5kIEwyQiBWMiBEYXRhIGluIFB5dGhvbiBUdXRvcmlhbCBTZXJpZXNdKGh0dHBzOi8vZ2l0LmVhcnRoZGF0YS5uYXNhLmdvdi9wcm9qZWN0cy9MUERVUi9yZXBvcy9nZWRpLXYyLXR1dG9yaWFscy9icm93c2UpICANCiAgDQogIC0gSW5jbHVkZXMgYSBzZXJpZXMgb2YgdHV0b3JpYWxzIHRoYXQgZGVtb25zdHJhdGUgaG93IHRvIHN0YXJ0IHdvcmtpbmcgd2l0aCBHRURJIFYyIGRhdGEgaW4gUHl0aG9uLiAgIA0KDQo8ZGl2IGNsYXNzPSJhbGVydCBhbGVydC1ibG9jayBhbGVydC1pbmZvIj4NCkNvbnRhY3QgSW5mb3JtYXRpb24gIA0KTWF0ZXJpYWwgd3JpdHRlbiBieSBDb2xlIEtyZWhiaWVsJF57MX0kICANCkNvbnRhY3Q6IExQREFBQ0B1c2dzLmdvdiAgIA0KVm9pY2U6ICsxLTYwNS01OTQtNjExNiAgIA0KT3JnYW5pemF0aW9uOiBMYW5kIFByb2Nlc3NlcyBEaXN0cmlidXRlZCBBY3RpdmUgQXJjaGl2ZSBDZW50ZXIgKExQIERBQUMpICAgDQpXZWJzaXRlOiBodHRwczovL2xwZGFhYy51c2dzLmdvdi8gICANCkRhdGUgbGFzdCBtb2RpZmllZDogMDUtMTAtMjAyMSAgDQoNCiReezF9JEtCUiBJbmMuLCBjb250cmFjdG9yIHRvIHRoZSBVLlMuIEdlb2xvZ2ljYWwgU3VydmV5LCBFYXJ0aCBSZXNvdXJjZXMgT2JzZXJ2YXRpb24gYW5kIFNjaWVuY2UgKEVST1MpIENlbnRlciwgU2lvdXggRmFsbHMsIFNvdXRoIERha290YSwgNTcxOTgtMDAxLCBVU0EuIFdvcmsgcGVyZm9ybWVkIHVuZGVyIFVTR1MgY29udHJhY3QgRzE1UEQwMDQ2NyBmb3IgTFAgREFBQyReezJ9JC4NCg0KJF57Mn0kTFAgREFBQyBXb3JrIHBlcmZvcm1lZCB1bmRlciBOQVNBIGNvbnRyYWN0IE5ORzE0SEgzM0kuDQo8L2Rpdj4NCg==