1 What is Google Earth Engine

Google Earth Engine (GEE) is a cloud-based tool for scientific analysis and visualization of geospatial datasets, for academic, non-profit, business and government users.

“Google Earth Engine combines a multi-petabyte catalog of satellite imagery and geospatial datasets with planetary-scale analysis capabilities and makes it available for scientists, researchers, and developers to detect changes, map trends, and quantify differences on the Earth’s surface.” [1]

While GEE is currently free, individuals must request access from Google at https://earthengine.google.com/signup/. With GEE, Google has created a public repository of geospatial data that harnesses the power of Google’s cloud-computing infrastructure to remove obstacles with regional to planetary scale analyses.

In a traditional sense, there was a specific set up steps required in order to begin any remote sensing analysis. In general those steps were:

  1. Locate imagery source
  2. Identify analysis location (path/row, longitude+latitude, etc.)
  3. Find scenes with appropriate attributes (date, cloud cover, etc.)
  4. Download or request scene(s)
  5. Compile bands into single image

This process of obtaining data could take anywhere from several minutes to several hours depending on the nature of the data and the users experience with the software. What makes GEE unique is the focus on analysis by removing the steps required to catalog, organize, and process remotely sensed imagery.

2 Data Catalog

GEE contains more than 29 petabytes (1 petabyte = 1 million gigabytes) of cataloged raster and vector datasets. This data are auto-updated daily by the Google Earth Engine team and new datasets are being added monthly. If you find that a dataset you use is not currently contained within the catalog you can suggest a new dataset be added by the GEE team.

With GEE, Google has provided the tools and application programming interface (API) necessary to quickly move from obtaining a dataset to exploratory analysis. All of these analyses are run on Google servers where the data is stored allowing for speed and processing power well beyond that of a single computer. Additionally, through the use of javascript or python, scientists can create repeatable analyses and share those with other users by simply sharing a single web address.

3 Working in the Earth Engine API

Working within the Google Chrome browser, GEE provides an API that allows you to search for datasets, visualize the data, develop code, examine the results of your analyses, and save your work within a repository.

  1. Editor Panel
    • Used for writing and editing your Javascript code
  2. Right Panel
    • Console tab for output information
    • Inspector tab for querying results
    • Tasks tab for managing executed tasks
  3. Left Panel
    • Scripts tab for managing your programming scripts
    • Docs tab for accessing documentation of Earth Engine objects and methods, as well as a few specific to the Code Editor application
    • Assets tab for managing assets that you upload
  4. Interactive Map
    • For visualizing map layer output
  5. Search Bar
    • For finding datasets and places of interest
  6. Help Menu
    • User guide
    • Help forum
    • Shortcuts
    • Feature Tour
    • Feedback
    • Suggest a dataset

4 Do some anlysis

In this exercise we will work though a simple example of how to use GEE to obtain satellite imagery of a location, examine the topography, and run a simple normalized difference vegetation index for a dataset.

To begin, navigate to Google Earth Engine in a Chrome Browser. If you do not have Chrome you can install it from here. Your screen should appear like the one below

Each of the sections of the screen were described above in “Working with the Earth Engine API”, however, your screen will now show your information in the script manager. To start, click on the New button and create a new file. If prompted to create a repository, provide the name of a new repository to store your exercise script(s). Now, using the “Search places and datasets…” bar along the top of the page, type in Clarksville, TN and click on the appropriate result.

This will allow use to zoom in to Clarksville. We can use the zoom tools on the left or a double-click to refine the level of zoom. Once you have centered Clarksville, click on the Add a Marker icon in the upper left of the image to place a point (by left-clicking) near downtown.

Notice that you can also add lines, shape, and square/rectangle. With this marker placed, you should now see geometry imports available as well as a new var in the script box. By moving your mouse over the geometry box you can use the gear icon you can change the name and color of the point. Rename the geometry to clarksville. Now that we have a location we can add a dataset. In the search box, type Sentinel 2 and select the import option next to the Sentinel-2 MSI: MultiSpectral Instrument, Level-1C from the rasters.

You will now see a new variable in your scripting window labeled as an ImageCollection. Essentially this means that the entire catalog of Sentinel 2 imagery is now available to this analysis. For more information on the sensor click the link for Sentinel 2. Here you will find information about he sensors availability range, provider, description, bands, spatial and temporal resolution, and image properties. This will be important later on when selecting the appropriate bands for analysis. Go ahead and rename this dataset sentinel2.

So now that we have a dataset to examine we can obtain information about our study site. If you were to run some datasets in the entirety you might receive an error statement in the console that says…

…this is due to an attempt to analyze an extremely large number of files in a dataset. If you need to examine global scale data you can contact Google to discuss a much larger quota. So in order to avoid such issues we are going to limit our search results.

To start, we will limit them based on geography. Because we only want images that intersect with our area of interest we can begin by setting the variable and providing a limit on its extent.

var image = ee.Image(sentinel2
  .filterBounds(clarksville) // only include scenes that intersect with our point geometry
  .filterDate("2019-01-01", "2019-10-30") // use to filter specific dates
  .sort("CLOUD_COVERAGE_ASSESSMENT") // filter by cloudy pixel percentage
  .first()); // select the least cloudy scene 

In the script above:

  1. var image = ee.Image(sentinel2
    • Identification of the data set
  2. .filterBounds(clarksville)
    • Provides an intersection point for obtaining imagery
  3. .filterDate(“2019-01-01”, “2019-10-30”)
    • Temporal limitation of the dataset for the first 10 months of 2019
  4. .sort(“CLOUD_COVERAGE_ASSESSMENT”)
    • In the “Image Properties” of the Sentinel 2 link above, you will find that cloud coverage assessment refers to the percentage of could cover in the images. but using the sort() term we can organize them from smallest to largest.
  5. first()
    • Selects the first image from the sorted, filtered list. Notice that script ends with a );. Remember it is javascript syntax to use a ; for the completion of a command. Additional we needed to end the open parenthese that began with ee.Image(.

Now that we have our filtered data, we can view the information in the console with print(image);. This identifies the image variable and provides information in the console tab. Information can be found regarding the number of available bands, cloud cover, etc.

In order to display the image we can set visualization parameters based on the information we want to obtain and the band combinations available in the data. For instance, if you want a “natural color” image (meaning as close to what you would see with your eyes from a plane) you would select the Red, Green, and Blue bands to display. If you wanted to examine vegetation, you would likely be interested in a false color composite with the Infrared, Red, and Green bands. For this example we will go ahead and make both.

var naturalcolor = {
  bands: ["B4", "B3", "B2"],
  min: 0,
  max: 4000,
};

var falsecolor = {
  bands: ["B8", "B4", "B3"],
  min: 0,
  max: 4000,
};

Now we have two variables, naturalcolor and falsecolor, we can use when displaying the imagery. To do that we want to add the information to the map using Map.addLayer():

Map.addLayer(image, naturalcolor, "Natural Color Composite");
Map.addLayer(image, falsecolor, "False Color Composite");

This added both the natural color and false color composite images to the active display. Using the options available under the layers box along the top of the map we can turn layers off and on, change the band combinations with the gear icon, and set the layer transparency with the slider bar.

Now that we have imagery of our area we can export that to a TIF file that can be used within other programs. To do this we will need to create a variable to be exported, provide some parameters, and create an export function that will run separately in the Tasks tab.

var exportRGB = image.visualize({
  bands: ["B4", "B3", "B2"],
  min: 0,
  max: 4000,
});

Export.image.toDrive({
  image: exportRGB,
  scale: 10,
  description: "clarksvilleRGB",
  crs: "EPSG:4326",
});

Here we created a variable to export an RGB image. Next we used Export.image.t0Drive() to identify the variable (image), the scale of the pixels (10, found in the information on Sentinel), provide a description of the function, and finally the projection (EPSG:4326 = WGS84). When you run this script you can see there is a new task available.

Click run on the task and a new window will open prompting you to provide a location, file name, and additional resolution information.

These images will save in your Google Drive folder, so provide a file name and folder name. For resolution, because we learned that the spatial resolution of our bands was 10m we provided that information both in the scale and here in the resolution section. After a few minutes we will have a new raster file in our folder.

Try to repeat the process above using the USGS Landsat 8 Collection 1 Tier 1 TOA Reflectance. Remember to look at the description, bands, and image properties to include the appropriate information.

If we are interested in obtaining terrain information we can follow a similar process to the one above using a different sensor. The Shuttle Radar Topography Mission (SRTM Digital Elevation Data Version 4) from NASA-JPL provides near near-global scale, high-quality elevation data. Search for that dataset and import it as a variable named srtm. Now we can use Map.addLayer(srtm, {min:90, max:150}, 'DEM'); to add DEM information. The min and max values are the elevation range in meters.

5 Vegetation Analysis

One of the basic remote sensing analyses is a vegetation index called normalized differnce vegetation index (NDVI). It is calculated as a ration between red and near infrared bands with a range of -1 to 1. Where -1 is absence of vegetation and 1 is healthy green vegetation. This can quickly be calculated with the information we have already scripted.

To begin we need to create an equation and variables from the Sentinel data.

var NDVI = image.expression(
  "(NIR - RED) / (NIR + RED)",
  {
    RED: image.select("B4"),
    NIR: image.select("B8")
    });
  
Map.addLayer(NDVI, {min: 0, max: 1}, "NDVI");

Remember that in Sentinel, Band 4 is the Red band and Band 8 is Near Infrared. Now we have a black and white image scaled with NDVI values.

If we want to determine the value of an individual pixel, we can use the inspector tab and use the crosshairs to click on an area of interest. Once you select a location and left-click you will see a series of graphs appear in the tab.

These graphs depict the pixel values of each band in the two image variables and the NDVI value of 0.251897. Click around the image on dark and light pixels and areas where you think you know the land cover to see if the results match your idea of the location (forest = high values, little/no vegetation = low values).

The script above only details a tiny fraction of what is capacble with Google Earth Engine. Using the Google Earth Engine Developers Guide example, we can create custom time lapse videos to examine our changing landscapes.

Take a look at the numerous tutorials and videos Earth Engine users have made available to see what you can do with this platform.

6 YOUR TURN!

Now it’s your turn! A number of you had issues obtaining reliable imagery for your research area (or area of interest) due to complications with OpenStreetMap or not having an API. Using the script above, alter the information to focus on your thesis or research area and download an RGB image. If you have time, try to add that image to one of your previous exercises without aerial imagery as a basemap. Using the get link button at the top of the scripting window, copy the URL, and add it to the README document for that exercise to provide access to the script.

7 References and More

[1]: Gorelick, N., Hancher, M., Dixon, M., Ilyushchenko, S., Thau, D., and Moore, R. (2017) Google Earth Engine: Planetary-scale geospatial analysis for everyone. Remote Sensing of Environment 202(1). https://doi.org/10.1016/j.rse.2017.06.031

Exercise Repository: https://github.com/chrismgentry/Google-Earth-Engine

In addition to Earth Engine, Google has made a number of other services freely available (or by request) that can be beneficial to geospatial analysis and visualization. One of those services is an update to Google Earth that allows you to create points, lines, and polygons and save them into a project along with Google Earth imagery to create an interactive display of your data.

I put together a quick tutorial, unfortunately due to restrictions with the software the cursor isn’t shown in the video. It is relatively self-explanatory and the video might at least provide an idea of the capabilities. I will make an attempt to update the video and link in the upcoming months. Another serivce available in the Chrome browser is Google Earth Studio.

Google Earth Studio is a browser-based animation tool for Google Earth’s 3D and satellite imagery. The massive collection of 2D and 3D Earth data available in Google Earth, from large-scale geological features to individual city buildings, provides the easiest way to leverage geospatial data and imagery for still and animated content. I have also included a few presentations related to these services in the repository.

LS0tDQp0aXRsZTogQnJpZWYgSW50cm9kdWN0aW9uIHRvIEdvb2dsZSBFYXJ0aCBFbmdpbmUgPGJyPjxzbWFsbD5BZHZhbmNlZCBEYXRhIEFuYWx5dGljczwvc21hbGw+PC9icj4NCmF1dGhvcjogIkJJT0wgNTcwMCwgRmFsbCAyMDE5Ig0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIGhpZ2hsaWdodDogYnJlZXplZGFyaw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgcm93cy5wcmludDogMTANCiAgICB0aGVtZTogY29zbW8NCiAgICBiaWJsaW9ncmFwaHk6IGJpYmxpb2dyYXBoeS5iaWJ0ZXgNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogbm8NCiAgICAgIHNtb290aF9zY3JvbGw6IHllcw0KICBodG1sX2RvY3VtZW50Og0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIHRvYzogeWVzDQogIHBkZl9kb2N1bWVudDogZGVmYXVsdA0KZWRpdG9yX29wdGlvbnM6DQogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUNCi0tLQ0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4NCg0KaDEudGl0bGUgew0KICBmb250LXNpemU6IDQwcHg7DQogIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICBjb2xvcjogRGFya0JsdWU7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmg0LmF1dGhvciB7IC8qIEhlYWRlciA0IC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogIGZvbnQtc2l6ZTogMjBweDsNCiAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogIGNvbG9yOiBEYXJrQmx1ZTsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KPC9zdHlsZT4NCg0KIyBXaGF0IGlzIEdvb2dsZSBFYXJ0aCBFbmdpbmUNCg0KPHAgYWxpZ249ImNlbnRlciI+DQo8aWZyYW1lIHdpZHRoPSI3ODQiIGhlaWdodD0iNDQxIiBzcmM9Imh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL2VtYmVkL2dLR09lVEZIbktZIiBmcmFtZWJvcmRlcj0iMCIgYWxsb3c9ImFjY2VsZXJvbWV0ZXI7IGF1dG9wbGF5OyBlbmNyeXB0ZWQtbWVkaWE7IGd5cm9zY29wZTsgcGljdHVyZS1pbi1waWN0dXJlIiBhbGxvd2Z1bGxzY3JlZW4+PC9pZnJhbWU+DQo8L3A+DQoNCkdvb2dsZSBFYXJ0aCBFbmdpbmUgKEdFRSkgaXMgYSBjbG91ZC1iYXNlZCB0b29sIGZvciBzY2llbnRpZmljIGFuYWx5c2lzIGFuZCB2aXN1YWxpemF0aW9uIG9mIGdlb3NwYXRpYWwgZGF0YXNldHMsIGZvciBhY2FkZW1pYywgbm9uLXByb2ZpdCwgYnVzaW5lc3MgYW5kIGdvdmVybm1lbnQgdXNlcnMuDQoNCj4gIkdvb2dsZSBFYXJ0aCBFbmdpbmUgY29tYmluZXMgYSBtdWx0aS1wZXRhYnl0ZSBjYXRhbG9nIG9mIHNhdGVsbGl0ZSBpbWFnZXJ5IGFuZCBnZW9zcGF0aWFsIGRhdGFzZXRzIHdpdGggcGxhbmV0YXJ5LXNjYWxlIGFuYWx5c2lzIGNhcGFiaWxpdGllcyBhbmQgbWFrZXMgaXQgYXZhaWxhYmxlIGZvciBzY2llbnRpc3RzLCByZXNlYXJjaGVycywgYW5kIGRldmVsb3BlcnMgdG8gZGV0ZWN0IGNoYW5nZXMsIG1hcCB0cmVuZHMsIGFuZCBxdWFudGlmeSBkaWZmZXJlbmNlcyBvbiB0aGUgRWFydGgncyBzdXJmYWNlLiIgPHN1cD5bMV08L3N1cD4NCg0KV2hpbGUgR0VFIGlzIGN1cnJlbnRseSBmcmVlLCBpbmRpdmlkdWFscyBtdXN0IHJlcXVlc3QgYWNjZXNzIGZyb20gR29vZ2xlIGF0IFtodHRwczovL2VhcnRoZW5naW5lLmdvb2dsZS5jb20vc2lnbnVwL10oaHR0cHM6Ly9lYXJ0aGVuZ2luZS5nb29nbGUuY29tL3NpZ251cC8pLiBXaXRoIEdFRSwgR29vZ2xlIGhhcyBjcmVhdGVkIGEgcHVibGljIHJlcG9zaXRvcnkgb2YgZ2Vvc3BhdGlhbCBkYXRhIHRoYXQgaGFybmVzc2VzIHRoZSBwb3dlciBvZiBHb29nbGUncyBjbG91ZC1jb21wdXRpbmcgaW5mcmFzdHJ1Y3R1cmUgdG8gcmVtb3ZlIG9ic3RhY2xlcyB3aXRoIHJlZ2lvbmFsIHRvIHBsYW5ldGFyeSBzY2FsZSBhbmFseXNlcy4NCg0KSW4gYSB0cmFkaXRpb25hbCBzZW5zZSwgdGhlcmUgd2FzIGEgc3BlY2lmaWMgc2V0IHVwIHN0ZXBzIHJlcXVpcmVkIGluIG9yZGVyIHRvIGJlZ2luIGFueSByZW1vdGUgc2Vuc2luZyBhbmFseXNpcy4gSW4gZ2VuZXJhbCB0aG9zZSBzdGVwcyB3ZXJlOg0KDQoxLiBMb2NhdGUgaW1hZ2VyeSBzb3VyY2UNCjIuIElkZW50aWZ5IGFuYWx5c2lzIGxvY2F0aW9uIChwYXRoL3JvdywgbG9uZ2l0dWRlK2xhdGl0dWRlLCBldGMuKQ0KMy4gRmluZCBzY2VuZXMgd2l0aCBhcHByb3ByaWF0ZSBhdHRyaWJ1dGVzIChkYXRlLCBjbG91ZCBjb3ZlciwgZXRjLikNCjQuIERvd25sb2FkIG9yIHJlcXVlc3Qgc2NlbmUocykNCjUuIENvbXBpbGUgYmFuZHMgaW50byBzaW5nbGUgaW1hZ2UNCg0KVGhpcyBwcm9jZXNzIG9mIG9idGFpbmluZyBkYXRhIGNvdWxkIHRha2UgYW55d2hlcmUgZnJvbSBzZXZlcmFsIG1pbnV0ZXMgdG8gc2V2ZXJhbCBob3VycyBkZXBlbmRpbmcgb24gdGhlIG5hdHVyZSBvZiB0aGUgZGF0YSBhbmQgdGhlIHVzZXJzIGV4cGVyaWVuY2Ugd2l0aCB0aGUgc29mdHdhcmUuIFdoYXQgbWFrZXMgR0VFIHVuaXF1ZSBpcyB0aGUgZm9jdXMgb24gYW5hbHlzaXMgYnkgcmVtb3ZpbmcgdGhlIHN0ZXBzIHJlcXVpcmVkIHRvIGNhdGFsb2csIG9yZ2FuaXplLCBhbmQgcHJvY2VzcyByZW1vdGVseSBzZW5zZWQgaW1hZ2VyeS4gDQoNCiMgRGF0YSBDYXRhbG9nIA0KR0VFIGNvbnRhaW5zIG1vcmUgdGhhbiAyOSBwZXRhYnl0ZXMgKDEgcGV0YWJ5dGUgPSAgMSBtaWxsaW9uIGdpZ2FieXRlcykgb2YgY2F0YWxvZ2VkIHJhc3RlciBhbmQgdmVjdG9yIGRhdGFzZXRzLiBUaGlzIGRhdGEgYXJlIGF1dG8tdXBkYXRlZCBkYWlseSBieSB0aGUgR29vZ2xlIEVhcnRoIEVuZ2luZSB0ZWFtIGFuZCBuZXcgZGF0YXNldHMgYXJlIGJlaW5nIGFkZGVkIG1vbnRobHkuIElmIHlvdSBmaW5kIHRoYXQgYSBkYXRhc2V0IHlvdSB1c2UgaXMgbm90IGN1cnJlbnRseSBjb250YWluZWQgd2l0aGluIHRoZSBjYXRhbG9nIHlvdSBjYW4gW3N1Z2dlc3QgYSBuZXcgZGF0YXNldF0oaHR0cHM6Ly9pc3N1ZXRyYWNrZXIuZ29vZ2xlLmNvbS9pc3N1ZXMvbmV3P2NvbXBvbmVudD0xODQ0MjYmdGVtcGxhdGU9NzE5NTAzKSBiZSBhZGRlZCBieSB0aGUgR0VFIHRlYW0uDQoNCjxwIGFsaWduPSJjZW50ZXIiPg0KDQohW10oLi9JbWFnZXMvR0VfQ2F0YWxvZy5wbmcgIkVhcnRoIEVuZ2luZSBEYXRhIENhdGFsb2ciKQ0KDQo8L3A+DQoNCldpdGggR0VFLCBHb29nbGUgaGFzIHByb3ZpZGVkIHRoZSB0b29scyBhbmQgYXBwbGljYXRpb24gcHJvZ3JhbW1pbmcgaW50ZXJmYWNlIChBUEkpIG5lY2Vzc2FyeSB0byBxdWlja2x5IG1vdmUgZnJvbSBvYnRhaW5pbmcgYSBkYXRhc2V0IHRvIGV4cGxvcmF0b3J5IGFuYWx5c2lzLiBBbGwgb2YgdGhlc2UgYW5hbHlzZXMgYXJlIHJ1biBvbiBHb29nbGUgc2VydmVycyB3aGVyZSB0aGUgZGF0YSBpcyBzdG9yZWQgYWxsb3dpbmcgZm9yIHNwZWVkIGFuZCBwcm9jZXNzaW5nIHBvd2VyIHdlbGwgYmV5b25kIHRoYXQgb2YgYSBzaW5nbGUgY29tcHV0ZXIuIEFkZGl0aW9uYWxseSwgdGhyb3VnaCB0aGUgdXNlIG9mIGphdmFzY3JpcHQgb3IgcHl0aG9uLCBzY2llbnRpc3RzIGNhbiBjcmVhdGUgcmVwZWF0YWJsZSBhbmFseXNlcyBhbmQgc2hhcmUgdGhvc2Ugd2l0aCBvdGhlciB1c2VycyBieSBzaW1wbHkgc2hhcmluZyBhIHNpbmdsZSB3ZWIgYWRkcmVzcy4NCg0KIyBXb3JraW5nIGluIHRoZSBFYXJ0aCBFbmdpbmUgQVBJDQoNCldvcmtpbmcgd2l0aGluIHRoZSBHb29nbGUgQ2hyb21lIGJyb3dzZXIsIEdFRSBwcm92aWRlcyBhbiBBUEkgdGhhdCBhbGxvd3MgeW91IHRvIHNlYXJjaCBmb3IgZGF0YXNldHMsIHZpc3VhbGl6ZSB0aGUgZGF0YSwgZGV2ZWxvcCBjb2RlLCBleGFtaW5lIHRoZSByZXN1bHRzIG9mIHlvdXIgYW5hbHlzZXMsIGFuZCBzYXZlIHlvdXIgd29yayB3aXRoaW4gYSByZXBvc2l0b3J5Lg0KDQo8cCBhbGlnbj0iY2VudGVyIj4NCg0KIVtdKGh0dHBzOi8vZ2l0aHViLmNvbS9nZW9zcGF0aWFsZWNvL0dFQVJTL3Jhdy9tYXN0ZXIvZ2VlX2VkaXRvci5wbmcgIkdFRSBFZGl0b3IiKQ0KDQo8L3A+DQoNCjEuIEVkaXRvciBQYW5lbA0KICAgKiBVc2VkIGZvciB3cml0aW5nIGFuZCBlZGl0aW5nIHlvdXIgSmF2YXNjcmlwdCBjb2RlDQoyLiBSaWdodCBQYW5lbA0KICAgKiBDb25zb2xlIHRhYiBmb3Igb3V0cHV0IGluZm9ybWF0aW9uDQogICAqIEluc3BlY3RvciB0YWIgZm9yIHF1ZXJ5aW5nIHJlc3VsdHMNCiAgICogVGFza3MgdGFiIGZvciBtYW5hZ2luZyBleGVjdXRlZCB0YXNrcw0KMy4gTGVmdCBQYW5lbA0KICAgKiBTY3JpcHRzIHRhYiBmb3IgbWFuYWdpbmcgeW91ciBwcm9ncmFtbWluZyBzY3JpcHRzDQogICAqIERvY3MgdGFiIGZvciBhY2Nlc3NpbmcgZG9jdW1lbnRhdGlvbiBvZiBFYXJ0aCBFbmdpbmUgb2JqZWN0cyBhbmQgbWV0aG9kcywgYXMgd2VsbCBhcyBhIGZldyBzcGVjaWZpYyB0byB0aGUgQ29kZSBFZGl0b3IgYXBwbGljYXRpb24NCiAgICogQXNzZXRzIHRhYiBmb3IgbWFuYWdpbmcgYXNzZXRzIHRoYXQgeW91IHVwbG9hZA0KNC4gSW50ZXJhY3RpdmUgTWFwDQogICAqIEZvciB2aXN1YWxpemluZyBtYXAgbGF5ZXIgb3V0cHV0DQo1LiBTZWFyY2ggQmFyDQogICAqIEZvciBmaW5kaW5nIGRhdGFzZXRzIGFuZCBwbGFjZXMgb2YgaW50ZXJlc3QNCjYuIEhlbHAgTWVudQ0KICAgKiBVc2VyIGd1aWRlDQogICAqIEhlbHAgZm9ydW0NCiAgICogU2hvcnRjdXRzDQogICAqIEZlYXR1cmUgVG91cg0KICAgKiBGZWVkYmFjaw0KICAgKiBTdWdnZXN0IGEgZGF0YXNldA0KDQojIERvIHNvbWUgYW5seXNpcw0KDQpJbiB0aGlzIGV4ZXJjaXNlIHdlIHdpbGwgd29yayB0aG91Z2ggYSBzaW1wbGUgZXhhbXBsZSBvZiBob3cgdG8gdXNlIEdFRSB0byBvYnRhaW4gc2F0ZWxsaXRlIGltYWdlcnkgb2YgYSBsb2NhdGlvbiwgZXhhbWluZSB0aGUgdG9wb2dyYXBoeSwgYW5kIHJ1biBhIHNpbXBsZSBub3JtYWxpemVkIGRpZmZlcmVuY2UgdmVnZXRhdGlvbiBpbmRleCBmb3IgYSBkYXRhc2V0Lg0KDQpUbyBiZWdpbiwgbmF2aWdhdGUgdG8gW0dvb2dsZSBFYXJ0aCBFbmdpbmVdKGh0dHBzOi8vY29kZS5lYXJ0aGVuZ2luZS5nb29nbGUuY29tLykgaW4gYSBDaHJvbWUgQnJvd3Nlci4gSWYgeW91IGRvIG5vdCBoYXZlIENocm9tZSB5b3UgY2FuIGluc3RhbGwgaXQgZnJvbSBbaGVyZV0oaHR0cHM6Ly93d3cuZ29vZ2xlLmNvbS9jaHJvbWUvKS4gWW91ciBzY3JlZW4gc2hvdWxkIGFwcGVhciBsaWtlIHRoZSBvbmUgYmVsb3cNCg0KPHAgYWxpZ249ImNlbnRlciI+DQoNCiFbXSguL0ltYWdlcy9HRUUucG5nICJHb29nbGUgRWFydGggRW5naW5lIikNCg0KPC9wPg0KDQpFYWNoIG9mIHRoZSBzZWN0aW9ucyBvZiB0aGUgc2NyZWVuIHdlcmUgZGVzY3JpYmVkIGFib3ZlIGluICJXb3JraW5nIHdpdGggdGhlIEVhcnRoIEVuZ2luZSBBUEkiLCBob3dldmVyLCB5b3VyIHNjcmVlbiB3aWxsIG5vdyBzaG93IHlvdXIgaW5mb3JtYXRpb24gaW4gdGhlIHNjcmlwdCBtYW5hZ2VyLiBUbyBzdGFydCwgY2xpY2sgb24gdGhlICoqTmV3KiogYnV0dG9uIGFuZCBjcmVhdGUgYSBuZXcgZmlsZS4gSWYgcHJvbXB0ZWQgdG8gY3JlYXRlIGEgcmVwb3NpdG9yeSwgcHJvdmlkZSB0aGUgbmFtZSBvZiBhIG5ldyByZXBvc2l0b3J5IHRvIHN0b3JlIHlvdXIgZXhlcmNpc2Ugc2NyaXB0KHMpLiBOb3csIHVzaW5nIHRoZSAqIlNlYXJjaCBwbGFjZXMgYW5kIGRhdGFzZXRzLi4uIiogYmFyIGFsb25nIHRoZSB0b3Agb2YgdGhlIHBhZ2UsIHR5cGUgaW4gQ2xhcmtzdmlsbGUsIFROIGFuZCBjbGljayBvbiB0aGUgYXBwcm9wcmlhdGUgcmVzdWx0Lg0KDQo8cCBhbGlnbj0iY2VudGVyIj4NCg0KIVtdKC4vSW1hZ2VzL0NsYXJrc3ZpbGxlX1NlYXJjaC5wbmcgIlVzaW5nIHRoZSBzZWFyY2ggYmFyIikNCg0KPC9wPg0KDQpUaGlzIHdpbGwgYWxsb3cgdXNlIHRvIHpvb20gaW4gdG8gQ2xhcmtzdmlsbGUuIFdlIGNhbiB1c2UgdGhlIHpvb20gdG9vbHMgb24gdGhlIGxlZnQgb3IgYSBkb3VibGUtY2xpY2sgdG8gcmVmaW5lIHRoZSBsZXZlbCBvZiB6b29tLiBPbmNlIHlvdSBoYXZlIGNlbnRlcmVkIENsYXJrc3ZpbGxlLCBjbGljayBvbiB0aGUgKipBZGQgYSBNYXJrZXIqKiBpY29uIGluIHRoZSB1cHBlciBsZWZ0IG9mIHRoZSBpbWFnZSB0byBwbGFjZSBhIHBvaW50IChieSBsZWZ0LWNsaWNraW5nKSBuZWFyIGRvd250b3duLiANCg0KPHAgYWxpZ249ImNlbnRlciI+DQoNCiFbXSguL0ltYWdlcy9NYXJrZXJfSWNvbi5wbmcgIkFkZCBhIG1hcmtlciIpDQoNCjwvcD4NCk5vdGljZSB0aGF0IHlvdSBjYW4gYWxzbyBhZGQgbGluZXMsIHNoYXBlLCBhbmQgc3F1YXJlL3JlY3RhbmdsZS4gV2l0aCB0aGlzIG1hcmtlciBwbGFjZWQsIHlvdSBzaG91bGQgbm93IHNlZSAqKmdlb21ldHJ5IGltcG9ydHMqKiBhdmFpbGFibGUgYXMgd2VsbCBhcyBhIG5ldyAqdmFyKiBpbiB0aGUgc2NyaXB0IGJveC4gQnkgbW92aW5nIHlvdXIgbW91c2Ugb3ZlciB0aGUgKmdlb21ldHJ5KiBib3ggeW91IGNhbiB1c2UgdGhlIGdlYXIgaWNvbiAhW10oLi9JbWFnZXMvR2Vhcl9JY29uLnBuZyAiR2VhciBJY29uIikgeW91IGNhbiBjaGFuZ2UgdGhlIG5hbWUgYW5kIGNvbG9yIG9mIHRoZSBwb2ludC4gUmVuYW1lIHRoZSBnZW9tZXRyeSB0byBjbGFya3N2aWxsZS4gTm93IHRoYXQgd2UgaGF2ZSBhIGxvY2F0aW9uIHdlIGNhbiBhZGQgYSBkYXRhc2V0LiBJbiB0aGUgc2VhcmNoIGJveCwgdHlwZSAqKlNlbnRpbmVsIDIqKiBhbmQgc2VsZWN0IHRoZSBpbXBvcnQgb3B0aW9uIG5leHQgdG8gdGhlICpTZW50aW5lbC0yIE1TSTogTXVsdGlTcGVjdHJhbCBJbnN0cnVtZW50LCBMZXZlbC0xQyogZnJvbSB0aGUgcmFzdGVycy4gDQoNCjxwIGFsaWduPSJjZW50ZXIiPg0KDQohW10oLi9JbWFnZXMvSW1wb3J0X1NlbnRpbmVsLnBuZyAiSW1wb3J0aW5nIGEgbmV3IGRhdGFzZXQiKQ0KPC9wPg0KDQpZb3Ugd2lsbCBub3cgc2VlIGEgbmV3IHZhcmlhYmxlIGluIHlvdXIgc2NyaXB0aW5nIHdpbmRvdyBsYWJlbGVkIGFzIGFuICoqSW1hZ2VDb2xsZWN0aW9uKiouIEVzc2VudGlhbGx5IHRoaXMgbWVhbnMgdGhhdCB0aGUgZW50aXJlIGNhdGFsb2cgb2YgKlNlbnRpbmVsIDIqIGltYWdlcnkgaXMgbm93IGF2YWlsYWJsZSB0byB0aGlzIGFuYWx5c2lzLiBGb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiB0aGUgc2Vuc29yIGNsaWNrIHRoZSBsaW5rIGZvciBbU2VudGluZWwgMl0oaHR0cHM6Ly9kZXZlbG9wZXJzLmdvb2dsZS5jb20vZWFydGgtZW5naW5lL2RhdGFzZXRzL2NhdGFsb2cvQ09QRVJOSUNVU19TMikuIEhlcmUgeW91IHdpbGwgZmluZCBpbmZvcm1hdGlvbiBhYm91dCBoZSBzZW5zb3JzIGF2YWlsYWJpbGl0eSByYW5nZSwgcHJvdmlkZXIsIGRlc2NyaXB0aW9uLCBiYW5kcywgc3BhdGlhbCBhbmQgdGVtcG9yYWwgcmVzb2x1dGlvbiwgYW5kIGltYWdlIHByb3BlcnRpZXMuIFRoaXMgd2lsbCBiZSBpbXBvcnRhbnQgbGF0ZXIgb24gd2hlbiBzZWxlY3RpbmcgdGhlIGFwcHJvcHJpYXRlIGJhbmRzIGZvciBhbmFseXNpcy4gR28gYWhlYWQgYW5kIHJlbmFtZSB0aGlzIGRhdGFzZXQgKnNlbnRpbmVsMiouDQoNClNvIG5vdyB0aGF0IHdlIGhhdmUgYSBkYXRhc2V0IHRvIGV4YW1pbmUgd2UgY2FuIG9idGFpbiBpbmZvcm1hdGlvbiBhYm91dCBvdXIgc3R1ZHkgc2l0ZS4gSWYgeW91IHdlcmUgdG8gcnVuIHNvbWUgZGF0YXNldHMgaW4gdGhlIGVudGlyZXR5IHlvdSBtaWdodCByZWNlaXZlIGFuIGVycm9yIHN0YXRlbWVudCBpbiB0aGUgY29uc29sZSB0aGF0IHNheXMuLi4NCg0KPHAgYWxpZ249ImNlbnRlciI+DQoNCiFbXSguL0ltYWdlcy9wcmludF9lcnJvci5wbmcgIkVycm9yIG1lc3NhZ2UiKQ0KPC9wPg0KDQouLi50aGlzIGlzIGR1ZSB0byBhbiBhdHRlbXB0IHRvIGFuYWx5emUgYW4gZXh0cmVtZWx5IGxhcmdlIG51bWJlciBvZiBmaWxlcyBpbiBhIGRhdGFzZXQuIElmIHlvdSBuZWVkIHRvIGV4YW1pbmUgZ2xvYmFsIHNjYWxlIGRhdGEgeW91IGNhbiBjb250YWN0IEdvb2dsZSB0byBkaXNjdXNzIGEgbXVjaCBsYXJnZXIgcXVvdGEuIFNvIGluIG9yZGVyIHRvIGF2b2lkIHN1Y2ggaXNzdWVzIHdlIGFyZSBnb2luZyB0byBsaW1pdCBvdXIgc2VhcmNoIHJlc3VsdHMuDQoNClRvIHN0YXJ0LCB3ZSB3aWxsIGxpbWl0IHRoZW0gYmFzZWQgb24gZ2VvZ3JhcGh5LiBCZWNhdXNlIHdlIG9ubHkgd2FudCBpbWFnZXMgdGhhdCBpbnRlcnNlY3Qgd2l0aCBvdXIgYXJlYSBvZiBpbnRlcmVzdCB3ZSBjYW4gYmVnaW4gYnkgc2V0dGluZyB0aGUgdmFyaWFibGUgYW5kIHByb3ZpZGluZyBhIGxpbWl0IG9uIGl0cyBleHRlbnQuDQoNCmBgYA0KdmFyIGltYWdlID0gZWUuSW1hZ2Uoc2VudGluZWwyDQogIC5maWx0ZXJCb3VuZHMoY2xhcmtzdmlsbGUpIC8vIG9ubHkgaW5jbHVkZSBzY2VuZXMgdGhhdCBpbnRlcnNlY3Qgd2l0aCBvdXIgcG9pbnQgZ2VvbWV0cnkNCiAgLmZpbHRlckRhdGUoIjIwMTktMDEtMDEiLCAiMjAxOS0xMC0zMCIpIC8vIHVzZSB0byBmaWx0ZXIgc3BlY2lmaWMgZGF0ZXMNCiAgLnNvcnQoIkNMT1VEX0NPVkVSQUdFX0FTU0VTU01FTlQiKSAvLyBmaWx0ZXIgYnkgY2xvdWR5IHBpeGVsIHBlcmNlbnRhZ2UNCiAgLmZpcnN0KCkpOyAvLyBzZWxlY3QgdGhlIGxlYXN0IGNsb3VkeSBzY2VuZSANCmBgYA0KDQpJbiB0aGUgc2NyaXB0IGFib3ZlOg0KDQoxLiB2YXIgaW1hZ2UgPSBlZS5JbWFnZShzZW50aW5lbDINCiAgICAqIElkZW50aWZpY2F0aW9uIG9mIHRoZSBkYXRhIHNldA0KMi4gLmZpbHRlckJvdW5kcyhjbGFya3N2aWxsZSkNCiAgICAqIFByb3ZpZGVzIGFuIGludGVyc2VjdGlvbiBwb2ludCBmb3Igb2J0YWluaW5nIGltYWdlcnkNCjMuIC5maWx0ZXJEYXRlKCIyMDE5LTAxLTAxIiwgIjIwMTktMTAtMzAiKQ0KICAgICogVGVtcG9yYWwgbGltaXRhdGlvbiBvZiB0aGUgZGF0YXNldCBmb3IgdGhlIGZpcnN0IDEwIG1vbnRocyBvZiAyMDE5DQo0LiAuc29ydCgiQ0xPVURfQ09WRVJBR0VfQVNTRVNTTUVOVCIpDQogICAgKiBJbiB0aGUgIkltYWdlIFByb3BlcnRpZXMiIG9mIHRoZSAqU2VudGluZWwgMiogbGluayBhYm92ZSwgeW91IHdpbGwgZmluZCB0aGF0IGNsb3VkIGNvdmVyYWdlIGFzc2Vzc21lbnQgcmVmZXJzIHRvIHRoZSBwZXJjZW50YWdlIG9mIGNvdWxkIGNvdmVyIGluIHRoZSBpbWFnZXMuIGJ1dCB1c2luZyB0aGUgc29ydCgpIHRlcm0gd2UgY2FuIG9yZ2FuaXplIHRoZW0gZnJvbSBzbWFsbGVzdCB0byBsYXJnZXN0Lg0KNS4gZmlyc3QoKQ0KICAgICogU2VsZWN0cyB0aGUgZmlyc3QgaW1hZ2UgZnJvbSB0aGUgc29ydGVkLCBmaWx0ZXJlZCBsaXN0LiBOb3RpY2UgdGhhdCBzY3JpcHQgZW5kcyB3aXRoIGEgKTsuIFJlbWVtYmVyIGl0IGlzIGphdmFzY3JpcHQgc3ludGF4IHRvIHVzZSBhIDsgZm9yIHRoZSBjb21wbGV0aW9uIG9mIGEgY29tbWFuZC4gQWRkaXRpb25hbCB3ZSBuZWVkZWQgdG8gZW5kIHRoZSBvcGVuIHBhcmVudGhlc2UgdGhhdCBiZWdhbiB3aXRoIGVlLkltYWdlKC4NCg0KTm93IHRoYXQgd2UgaGF2ZSBvdXIgZmlsdGVyZWQgZGF0YSwgd2UgY2FuIHZpZXcgdGhlIGluZm9ybWF0aW9uIGluIHRoZSBjb25zb2xlIHdpdGggYGBgcHJpbnQoaW1hZ2UpO2BgYC4gVGhpcyBpZGVudGlmaWVzIHRoZSAqKmltYWdlKiogdmFyaWFibGUgYW5kIHByb3ZpZGVzIGluZm9ybWF0aW9uIGluIHRoZSBjb25zb2xlIHRhYi4gSW5mb3JtYXRpb24gY2FuIGJlIGZvdW5kIHJlZ2FyZGluZyB0aGUgbnVtYmVyIG9mIGF2YWlsYWJsZSBiYW5kcywgY2xvdWQgY292ZXIsIGV0Yy4NCg0KPHAgYWxpZ249ImNlbnRlciI+DQoNCiFbXSguL0ltYWdlcy9pbWFnZV9jb25zb2xlLnBuZyAiQ29uc29sZSIpDQo8L3A+DQoNCkluIG9yZGVyIHRvIGRpc3BsYXkgdGhlIGltYWdlIHdlIGNhbiBzZXQgdmlzdWFsaXphdGlvbiBwYXJhbWV0ZXJzIGJhc2VkIG9uIHRoZSBpbmZvcm1hdGlvbiB3ZSB3YW50IHRvIG9idGFpbiBhbmQgdGhlIGJhbmQgY29tYmluYXRpb25zIGF2YWlsYWJsZSBpbiB0aGUgZGF0YS4gRm9yIGluc3RhbmNlLCBpZiB5b3Ugd2FudCBhICJuYXR1cmFsIGNvbG9yIiBpbWFnZSAobWVhbmluZyBhcyBjbG9zZSB0byB3aGF0IHlvdSB3b3VsZCBzZWUgd2l0aCB5b3VyIGV5ZXMgZnJvbSBhIHBsYW5lKSB5b3Ugd291bGQgc2VsZWN0IHRoZSBSZWQsIEdyZWVuLCBhbmQgQmx1ZSBiYW5kcyB0byBkaXNwbGF5LiBJZiB5b3Ugd2FudGVkIHRvIGV4YW1pbmUgdmVnZXRhdGlvbiwgeW91IHdvdWxkIGxpa2VseSBiZSBpbnRlcmVzdGVkIGluIGEgZmFsc2UgY29sb3IgY29tcG9zaXRlIHdpdGggdGhlIEluZnJhcmVkLCBSZWQsIGFuZCBHcmVlbiBiYW5kcy4gRm9yIHRoaXMgZXhhbXBsZSB3ZSB3aWxsIGdvIGFoZWFkIGFuZCBtYWtlIGJvdGguDQoNCmBgYA0KdmFyIG5hdHVyYWxjb2xvciA9IHsNCiAgYmFuZHM6IFsiQjQiLCAiQjMiLCAiQjIiXSwNCiAgbWluOiAwLA0KICBtYXg6IDQwMDAsDQp9Ow0KDQp2YXIgZmFsc2Vjb2xvciA9IHsNCiAgYmFuZHM6IFsiQjgiLCAiQjQiLCAiQjMiXSwNCiAgbWluOiAwLA0KICBtYXg6IDQwMDAsDQp9Ow0KYGBgDQpOb3cgd2UgaGF2ZSB0d28gdmFyaWFibGVzLCAqbmF0dXJhbGNvbG9yKiBhbmQgKmZhbHNlY29sb3IqLCB3ZSBjYW4gdXNlIHdoZW4gZGlzcGxheWluZyB0aGUgaW1hZ2VyeS4gVG8gZG8gdGhhdCB3ZSB3YW50IHRvIGFkZCB0aGUgaW5mb3JtYXRpb24gdG8gdGhlIG1hcCB1c2luZyBNYXAuYWRkTGF5ZXIoKToNCg0KYGBgDQpNYXAuYWRkTGF5ZXIoaW1hZ2UsIG5hdHVyYWxjb2xvciwgIk5hdHVyYWwgQ29sb3IgQ29tcG9zaXRlIik7DQpNYXAuYWRkTGF5ZXIoaW1hZ2UsIGZhbHNlY29sb3IsICJGYWxzZSBDb2xvciBDb21wb3NpdGUiKTsNCmBgYA0KDQpUaGlzIGFkZGVkIGJvdGggdGhlIG5hdHVyYWwgY29sb3IgYW5kIGZhbHNlIGNvbG9yIGNvbXBvc2l0ZSBpbWFnZXMgdG8gdGhlIGFjdGl2ZSBkaXNwbGF5LiBVc2luZyB0aGUgb3B0aW9ucyBhdmFpbGFibGUgdW5kZXIgdGhlICoqbGF5ZXJzKiogYm94IGFsb25nIHRoZSB0b3Agb2YgdGhlIG1hcCB3ZSBjYW4gdHVybiBsYXllcnMgb2ZmIGFuZCBvbiwgY2hhbmdlIHRoZSBiYW5kIGNvbWJpbmF0aW9ucyB3aXRoIHRoZSBnZWFyIGljb24sIGFuZCBzZXQgdGhlIGxheWVyIHRyYW5zcGFyZW5jeSB3aXRoIHRoZSBzbGlkZXIgYmFyLg0KDQo8cCBhbGlnbj0iY2VudGVyIj4NCg0KIVtdKC4vSW1hZ2VzL21hcF92aWV3LnBuZyAiTWFwIFZpZXciKQ0KDQpOb3cgdGhhdCB3ZSBoYXZlIGltYWdlcnkgb2Ygb3VyIGFyZWEgd2UgY2FuIGV4cG9ydCB0aGF0IHRvIGEgVElGIGZpbGUgdGhhdCBjYW4gYmUgdXNlZCB3aXRoaW4gb3RoZXIgcHJvZ3JhbXMuIFRvIGRvIHRoaXMgd2Ugd2lsbCBuZWVkIHRvIGNyZWF0ZSBhIHZhcmlhYmxlIHRvIGJlIGV4cG9ydGVkLCBwcm92aWRlIHNvbWUgcGFyYW1ldGVycywgYW5kIGNyZWF0ZSBhbiBleHBvcnQgZnVuY3Rpb24gdGhhdCB3aWxsIHJ1biBzZXBhcmF0ZWx5IGluIHRoZSAqKlRhc2tzKiogdGFiLg0KDQpgYGANCnZhciBleHBvcnRSR0IgPSBpbWFnZS52aXN1YWxpemUoew0KICBiYW5kczogWyJCNCIsICJCMyIsICJCMiJdLA0KICBtaW46IDAsDQogIG1heDogNDAwMCwNCn0pOw0KDQpFeHBvcnQuaW1hZ2UudG9Ecml2ZSh7DQogIGltYWdlOiBleHBvcnRSR0IsDQogIHNjYWxlOiAxMCwNCiAgZGVzY3JpcHRpb246ICJjbGFya3N2aWxsZVJHQiIsDQogIGNyczogIkVQU0c6NDMyNiIsDQp9KTsNCmBgYA0KSGVyZSB3ZSBjcmVhdGVkIGEgdmFyaWFibGUgdG8gZXhwb3J0IGFuIFJHQiBpbWFnZS4gTmV4dCB3ZSB1c2VkIEV4cG9ydC5pbWFnZS50MERyaXZlKCkgdG8gaWRlbnRpZnkgdGhlIHZhcmlhYmxlIChpbWFnZSksIHRoZSBzY2FsZSBvZiB0aGUgcGl4ZWxzICgxMCwgZm91bmQgaW4gdGhlIGluZm9ybWF0aW9uIG9uIFNlbnRpbmVsKSwgcHJvdmlkZSBhIGRlc2NyaXB0aW9uIG9mIHRoZSBmdW5jdGlvbiwgYW5kIGZpbmFsbHkgdGhlIHByb2plY3Rpb24gKEVQU0c6NDMyNiA9IFdHUzg0KS4gV2hlbiB5b3UgcnVuIHRoaXMgc2NyaXB0IHlvdSBjYW4gc2VlIHRoZXJlIGlzIGEgbmV3IHRhc2sgYXZhaWxhYmxlLg0KDQo8cCBhbGlnbj0iY2VudGVyIj4NCg0KIVtdKC4vSW1hZ2VzL1Rhc2tfSW1hZ2VfRXhwb3J0LnBuZyAiVGFza3MgVGFiIikNCjwvcD4NCkNsaWNrIHJ1biBvbiB0aGUgdGFzayBhbmQgYSBuZXcgd2luZG93IHdpbGwgb3BlbiBwcm9tcHRpbmcgeW91IHRvIHByb3ZpZGUgYSBsb2NhdGlvbiwgZmlsZSBuYW1lLCBhbmQgYWRkaXRpb25hbCByZXNvbHV0aW9uIGluZm9ybWF0aW9uLg0KDQo8cCBhbGlnbj0iY2VudGVyIj4NCg0KIVtdKC4vSW1hZ2VzL0V4cG9ydF9PcHRpb25zLnBuZyAiRXhwb3J0IE9wdGlvbnMgTWVudSIpDQoNCjwvcD4NCg0KVGhlc2UgaW1hZ2VzIHdpbGwgc2F2ZSBpbiB5b3VyIEdvb2dsZSBEcml2ZSBmb2xkZXIsIHNvIHByb3ZpZGUgYSBmaWxlIG5hbWUgYW5kIGZvbGRlciBuYW1lLiBGb3IgcmVzb2x1dGlvbiwgYmVjYXVzZSB3ZSBsZWFybmVkIHRoYXQgdGhlIHNwYXRpYWwgcmVzb2x1dGlvbiBvZiBvdXIgYmFuZHMgd2FzIDEwbSB3ZSBwcm92aWRlZCB0aGF0IGluZm9ybWF0aW9uIGJvdGggaW4gdGhlIHNjYWxlIGFuZCBoZXJlIGluIHRoZSByZXNvbHV0aW9uIHNlY3Rpb24uIEFmdGVyIGEgZmV3IG1pbnV0ZXMgd2Ugd2lsbCBoYXZlIGEgbmV3IHJhc3RlciBmaWxlIGluIG91ciBmb2xkZXIuDQoNCj4gVHJ5IHRvIHJlcGVhdCB0aGUgcHJvY2VzcyBhYm92ZSB1c2luZyB0aGUgVVNHUyBMYW5kc2F0IDggQ29sbGVjdGlvbiAxIFRpZXIgMSBUT0EgUmVmbGVjdGFuY2UuIFJlbWVtYmVyIHRvIGxvb2sgYXQgdGhlIGRlc2NyaXB0aW9uLCBiYW5kcywgYW5kIGltYWdlIHByb3BlcnRpZXMgdG8gaW5jbHVkZSB0aGUgYXBwcm9wcmlhdGUgaW5mb3JtYXRpb24uDQoNCklmIHdlIGFyZSBpbnRlcmVzdGVkIGluIG9idGFpbmluZyB0ZXJyYWluIGluZm9ybWF0aW9uIHdlIGNhbiBmb2xsb3cgYSBzaW1pbGFyIHByb2Nlc3MgdG8gdGhlIG9uZSBhYm92ZSB1c2luZyBhIGRpZmZlcmVudCBzZW5zb3IuIFRoZSAqU2h1dHRsZSBSYWRhciBUb3BvZ3JhcGh5IE1pc3Npb24qIChTUlRNIERpZ2l0YWwgRWxldmF0aW9uIERhdGEgVmVyc2lvbiA0KSBmcm9tIE5BU0EtSlBMIHByb3ZpZGVzIG5lYXIgbmVhci1nbG9iYWwgc2NhbGUsIGhpZ2gtcXVhbGl0eSBlbGV2YXRpb24gZGF0YS4gU2VhcmNoIGZvciB0aGF0IGRhdGFzZXQgYW5kIGltcG9ydCBpdCBhcyBhIHZhcmlhYmxlIG5hbWVkICoqc3J0bSoqLiBOb3cgd2UgY2FuIHVzZSBgYGBNYXAuYWRkTGF5ZXIoc3J0bSwge21pbjo5MCwgbWF4OjE1MH0sICdERU0nKTtgYGAgdG8gYWRkIERFTSBpbmZvcm1hdGlvbi4gVGhlIG1pbiBhbmQgbWF4IHZhbHVlcyBhcmUgdGhlIGVsZXZhdGlvbiByYW5nZSBpbiBtZXRlcnMuDQoNCiMgVmVnZXRhdGlvbiBBbmFseXNpcw0KDQpPbmUgb2YgdGhlIGJhc2ljIHJlbW90ZSBzZW5zaW5nIGFuYWx5c2VzIGlzIGEgdmVnZXRhdGlvbiBpbmRleCBjYWxsZWQgbm9ybWFsaXplZCBkaWZmZXJuY2UgdmVnZXRhdGlvbiBpbmRleCAoTkRWSSkuIEl0IGlzIGNhbGN1bGF0ZWQgYXMgYSByYXRpb24gYmV0d2VlbiByZWQgYW5kIG5lYXIgaW5mcmFyZWQgYmFuZHMgd2l0aCBhIHJhbmdlIG9mIC0xIHRvIDEuIFdoZXJlIC0xIGlzIGFic2VuY2Ugb2YgdmVnZXRhdGlvbiBhbmQgMSBpcyBoZWFsdGh5IGdyZWVuIHZlZ2V0YXRpb24uIFRoaXMgY2FuIHF1aWNrbHkgYmUgY2FsY3VsYXRlZCB3aXRoIHRoZSBpbmZvcm1hdGlvbiB3ZSBoYXZlIGFscmVhZHkgc2NyaXB0ZWQuDQoNClRvIGJlZ2luIHdlIG5lZWQgdG8gY3JlYXRlIGFuIGVxdWF0aW9uIGFuZCB2YXJpYWJsZXMgZnJvbSB0aGUgU2VudGluZWwgZGF0YS4NCmBgYA0KdmFyIE5EVkkgPSBpbWFnZS5leHByZXNzaW9uKA0KICAiKE5JUiAtIFJFRCkgLyAoTklSICsgUkVEKSIsDQogIHsNCiAgICBSRUQ6IGltYWdlLnNlbGVjdCgiQjQiKSwNCiAgICBOSVI6IGltYWdlLnNlbGVjdCgiQjgiKQ0KICAgIH0pOw0KICANCk1hcC5hZGRMYXllcihORFZJLCB7bWluOiAwLCBtYXg6IDF9LCAiTkRWSSIpOw0KYGBgDQpSZW1lbWJlciB0aGF0IGluIFNlbnRpbmVsLCBCYW5kIDQgaXMgdGhlIFJlZCBiYW5kIGFuZCBCYW5kIDggaXMgTmVhciBJbmZyYXJlZC4gTm93IHdlIGhhdmUgYSBibGFjayBhbmQgd2hpdGUgaW1hZ2Ugc2NhbGVkIHdpdGggTkRWSSB2YWx1ZXMuDQoNCjxwIGFsaWduPSJjZW50ZXIiPg0KDQohW10oLi9JbWFnZXMvTkRWSS5wbmcgIk5EVkkgSW1hZ2UiKQ0KPC9wPg0KDQpJZiB3ZSB3YW50IHRvIGRldGVybWluZSB0aGUgdmFsdWUgb2YgYW4gaW5kaXZpZHVhbCBwaXhlbCwgd2UgY2FuIHVzZSB0aGUgaW5zcGVjdG9yIHRhYiBhbmQgdXNlIHRoZSBjcm9zc2hhaXJzIHRvIGNsaWNrIG9uIGFuIGFyZWEgb2YgaW50ZXJlc3QuIE9uY2UgeW91IHNlbGVjdCBhIGxvY2F0aW9uIGFuZCBsZWZ0LWNsaWNrIHlvdSB3aWxsIHNlZSBhIHNlcmllcyBvZiBncmFwaHMgYXBwZWFyIGluIHRoZSB0YWIuDQoNCjxwIGFsaWduPSJjZW50ZXIiPg0KDQohW10oLi9JbWFnZXMvSW5zcGVjdG9yX1RhYi5wbmcgIlJlc3VsdHMgaW4gdGhlIEluc3BlY3RvciBUYWIiKQ0KPC9wPg0KDQpUaGVzZSBncmFwaHMgZGVwaWN0IHRoZSBwaXhlbCB2YWx1ZXMgb2YgZWFjaCBiYW5kIGluIHRoZSB0d28gaW1hZ2UgdmFyaWFibGVzIGFuZCB0aGUgTkRWSSB2YWx1ZSBvZiAwLjI1MTg5Ny4gQ2xpY2sgYXJvdW5kIHRoZSBpbWFnZSBvbiBkYXJrIGFuZCBsaWdodCBwaXhlbHMgYW5kIGFyZWFzIHdoZXJlIHlvdSB0aGluayB5b3Uga25vdyB0aGUgbGFuZCBjb3ZlciB0byBzZWUgaWYgdGhlIHJlc3VsdHMgbWF0Y2ggeW91ciBpZGVhIG9mIHRoZSBsb2NhdGlvbiAoZm9yZXN0ID0gaGlnaCB2YWx1ZXMsIGxpdHRsZS9ubyB2ZWdldGF0aW9uID0gbG93IHZhbHVlcykuDQoNClRoZSBzY3JpcHQgYWJvdmUgb25seSBkZXRhaWxzIGEgdGlueSBmcmFjdGlvbiBvZiB3aGF0IGlzIGNhcGFjYmxlIHdpdGggR29vZ2xlIEVhcnRoIEVuZ2luZS4gVXNpbmcgdGhlIFtHb29nbGUgRWFydGggRW5naW5lIERldmVsb3BlcnMgR3VpZGVdKGh0dHBzOi8vZGV2ZWxvcGVycy5nb29nbGUuY29tL2VhcnRoLWVuZ2luZS9leHBvcnRpbmcpIGV4YW1wbGUsIHdlIGNhbiBjcmVhdGUgY3VzdG9tIHRpbWUgbGFwc2UgdmlkZW9zIHRvIGV4YW1pbmUgb3VyIGNoYW5naW5nIGxhbmRzY2FwZXMuDQoNCjxwIGFsaWduPSJjZW50ZXIiPg0KDQo8aWZyYW1lIHdpZHRoPSI1NjAiIGhlaWdodD0iMzE1IiBzcmM9Imh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL2VtYmVkL2JQT19QSmxvMk5jIiBmcmFtZWJvcmRlcj0iMCIgYWxsb3c9ImFjY2VsZXJvbWV0ZXI7IGF1dG9wbGF5OyBlbmNyeXB0ZWQtbWVkaWE7IGd5cm9zY29wZTsgcGljdHVyZS1pbi1waWN0dXJlIiBhbGxvd2Z1bGxzY3JlZW4+PC9pZnJhbWU+DQoNCjwvcD4NCg0KVGFrZSBhIGxvb2sgYXQgdGhlIG51bWVyb3VzIHR1dG9yaWFscyBhbmQgdmlkZW9zIEVhcnRoIEVuZ2luZSB1c2VycyBoYXZlIG1hZGUgYXZhaWxhYmxlIHRvIHNlZSB3aGF0IHlvdSBjYW4gZG8gd2l0aCB0aGlzIHBsYXRmb3JtLg0KDQojIFlPVVIgVFVSTiENCg0KTm93IGl0J3MgeW91ciB0dXJuISBBIG51bWJlciBvZiB5b3UgaGFkIGlzc3VlcyBvYnRhaW5pbmcgcmVsaWFibGUgaW1hZ2VyeSBmb3IgeW91ciByZXNlYXJjaCBhcmVhIChvciBhcmVhIG9mIGludGVyZXN0KSBkdWUgdG8gY29tcGxpY2F0aW9ucyB3aXRoIE9wZW5TdHJlZXRNYXAgb3Igbm90IGhhdmluZyBhbiBBUEkuIFVzaW5nIHRoZSBzY3JpcHQgYWJvdmUsIGFsdGVyIHRoZSBpbmZvcm1hdGlvbiB0byBmb2N1cyBvbiB5b3VyIHRoZXNpcyBvciByZXNlYXJjaCBhcmVhIGFuZCBkb3dubG9hZCBhbiBSR0IgaW1hZ2UuIElmIHlvdSBoYXZlIHRpbWUsIHRyeSB0byBhZGQgdGhhdCBpbWFnZSB0byBvbmUgb2YgeW91ciBwcmV2aW91cyBleGVyY2lzZXMgd2l0aG91dCBhZXJpYWwgaW1hZ2VyeSBhcyBhIGJhc2VtYXAuIFVzaW5nIHRoZSAqKmdldCBsaW5rKiogYnV0dG9uIGF0IHRoZSB0b3Agb2YgdGhlIHNjcmlwdGluZyB3aW5kb3csIGNvcHkgdGhlIFVSTCwgYW5kIGFkZCBpdCB0byB0aGUgUkVBRE1FIGRvY3VtZW50IGZvciB0aGF0IGV4ZXJjaXNlIHRvIHByb3ZpZGUgYWNjZXNzIHRvIHRoZSBzY3JpcHQuDQoNCiMgUmVmZXJlbmNlcyBhbmQgTW9yZQ0KWzFdOiBHb3JlbGljaywgTi4sIEhhbmNoZXIsIE0uLCBEaXhvbiwgTS4sIElseXVzaGNoZW5rbywgUy4sIFRoYXUsIEQuLCBhbmQgTW9vcmUsIFIuICgyMDE3KSBHb29nbGUgRWFydGggRW5naW5lOiBQbGFuZXRhcnktc2NhbGUgZ2Vvc3BhdGlhbCBhbmFseXNpcyBmb3IgZXZlcnlvbmUuIFJlbW90ZSBTZW5zaW5nIG9mIEVudmlyb25tZW50IDIwMigxKS4gaHR0cHM6Ly9kb2kub3JnLzEwLjEwMTYvai5yc2UuMjAxNy4wNi4wMzENCg0KRXhlcmNpc2UgUmVwb3NpdG9yeTogaHR0cHM6Ly9naXRodWIuY29tL2NocmlzbWdlbnRyeS9Hb29nbGUtRWFydGgtRW5naW5lDQoNCkluIGFkZGl0aW9uIHRvIEVhcnRoIEVuZ2luZSwgR29vZ2xlIGhhcyBtYWRlIGEgbnVtYmVyIG9mIG90aGVyIHNlcnZpY2VzIGZyZWVseSBhdmFpbGFibGUgKG9yIGJ5IHJlcXVlc3QpIHRoYXQgY2FuIGJlIGJlbmVmaWNpYWwgdG8gZ2Vvc3BhdGlhbCBhbmFseXNpcyBhbmQgdmlzdWFsaXphdGlvbi4gT25lIG9mIHRob3NlIHNlcnZpY2VzIGlzIGFuIHVwZGF0ZSB0byBbR29vZ2xlIEVhcnRoXShodHRwOi8vZWFydGguZ29vZ2xlLmNvbS93ZWIpIHRoYXQgYWxsb3dzIHlvdSB0byBjcmVhdGUgcG9pbnRzLCBsaW5lcywgYW5kIHBvbHlnb25zIGFuZCBzYXZlIHRoZW0gaW50byBhIHByb2plY3QgYWxvbmcgd2l0aCBHb29nbGUgRWFydGggaW1hZ2VyeSB0byBjcmVhdGUgYW4gaW50ZXJhY3RpdmUgZGlzcGxheSBvZiB5b3VyIGRhdGEuIA0KDQo8cCBhbGlnbj0iY2VudGVyIj4NCg0KIVtdKC4vSW1hZ2VzL05ld19FYXJ0aC5wbmcgIk5ldyBHb29nbGUgRWFydGggZm9yIHRoZSB3ZWIiKQ0KPC9wPg0KDQpJIHB1dCB0b2dldGhlciBhIHF1aWNrIHR1dG9yaWFsLCB1bmZvcnR1bmF0ZWx5IGR1ZSB0byByZXN0cmljdGlvbnMgd2l0aCB0aGUgc29mdHdhcmUgdGhlIGN1cnNvciBpc24ndCBzaG93biBpbiB0aGUgW3ZpZGVvXShodHRwczovL3lvdXR1LmJlL0ZsMFh4bUhiQm5FKS4gSXQgaXMgcmVsYXRpdmVseSBzZWxmLWV4cGxhbmF0b3J5IGFuZCB0aGUgdmlkZW8gbWlnaHQgYXQgbGVhc3QgcHJvdmlkZSBhbiBpZGVhIG9mIHRoZSBjYXBhYmlsaXRpZXMuIEkgd2lsbCBtYWtlIGFuIGF0dGVtcHQgdG8gdXBkYXRlIHRoZSB2aWRlbyBhbmQgbGluayBpbiB0aGUgdXBjb21pbmcgbW9udGhzLg0KQW5vdGhlciBzZXJpdmNlIGF2YWlsYWJsZSBpbiB0aGUgQ2hyb21lIGJyb3dzZXIgaXMgW0dvb2dsZSBFYXJ0aCBTdHVkaW9dKGh0dHBzOi8vZWFydGguZ29vZ2xlLmNvbS9zdHVkaW8vKS4NCg0KPHAgYWxpZ249ImNlbnRlciI+DQoNCiFbXSguL0ltYWdlcy9TdHVkaW8ucG5nICJHb29nbGUgRWFydGggU3R1ZGlvIikNCjwvcD4NCkdvb2dsZSBFYXJ0aCBTdHVkaW8gaXMgYSBicm93c2VyLWJhc2VkIGFuaW1hdGlvbiB0b29sIGZvciBHb29nbGUgRWFydGgncyAzRCBhbmQgc2F0ZWxsaXRlIGltYWdlcnkuIFRoZSBtYXNzaXZlIGNvbGxlY3Rpb24gb2YgMkQgYW5kIDNEIEVhcnRoIGRhdGEgYXZhaWxhYmxlIGluIEdvb2dsZSBFYXJ0aCwgZnJvbSBsYXJnZS1zY2FsZSBnZW9sb2dpY2FsIGZlYXR1cmVzIHRvIGluZGl2aWR1YWwgY2l0eSBidWlsZGluZ3MsIHByb3ZpZGVzIHRoZSBlYXNpZXN0IHdheSB0byBsZXZlcmFnZSBnZW9zcGF0aWFsIGRhdGEgYW5kIGltYWdlcnkgZm9yIHN0aWxsIGFuZCBhbmltYXRlZCBjb250ZW50LiBJIGhhdmUgYWxzbyBpbmNsdWRlZCBhIGZldyBwcmVzZW50YXRpb25zIHJlbGF0ZWQgdG8gdGhlc2Ugc2VydmljZXMgaW4gdGhlIHJlcG9zaXRvcnkuDQoNCg==