In the second part of this lab you will download hourly temperature measurements from weather stations within the boundary of the Landsat image to compare observed temperatures against the estimated temperatures from Landsat on July 30th, 2017. You will then use R to extract the temperatures from the land surface temperatures raster you created last week. These observed and estimated temperatures will be compared visually and statistically to hypothesize what time the Landsat image was captured. Finally, you will create a properly formatted R Notebook (modified from this version) or PDF lab report to present your findings.
Given the varying levels of familiarity with R, much of the coding for this lab will be done for you. However, make sure that you carefully walk through each step and understand what is being done because you will have less code written for you in future labs.
If you don’t already have R, you can download it at http://www.r-project.org.
We also recommend using R Studio as an integrated development environment (IDE) for R: https://www.rstudio.com/.
Open R (or R Studio) and install the necessary packages. R relies on externally written “packages” that carry many of the functions needed. You will be using several packages for this lab. For each package you will need to install them by typing the following code into the command console: install.packages("raster"), where raster is the name of the first package.
The cool thing about an R Notebook is that you can view it in any browser, but you can also open it in R. You can download this document as an Rmd file by clicking on the button that says “Code” in the top right-hand corner. Then select Download Rmd and open the file from R.
Here are a few helpful notes about R:
R allows you to run either the entire script, or just selected lines at a time. This notebook does a lot of the work for you. It has all the elements you need to modify the script,
Since this is an R Markdown Notebook, when you execute code within the notebook, the results appear beneath the code. Try executing the first code chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.
Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.
When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).
If you want to learn more about best practices for code writing, you can check out this style guide: http://adv-r.had.co.nz/Style.html.
Setup
This code chunk loads the packages used in this notebook.
library(raster)
library(rgdal)
library(data.table)
library(sp)
library(lubridate)
library(tidyr)
library(dplyr)
library(ggplot2)
library(plotly)
library(knitr)
knitr::opts_knit$set(echo=TRUE, warning=FALSE, message=FALSE)
This is also a good place to put helper functions. Below is a function that we will use to create a correlation matrix of the different temperatures against the estimated land surface temperatures from Landsat.
cor.matrix<-function(x,data=NA){
# panel.hist function adds the histogram
panel.hist <- function(x, ...)
{
usr <- par("usr"); on.exit(par(usr))
par(usr = c(usr[1:2], 0, 1.5) )
h <- hist(x, plot = FALSE)
breaks <- h$breaks; nB <- length(breaks)
y <- h$counts; y <- y/max(y)
rect(breaks[-nB], 0, breaks[-1], y, col="cyan", ...)
box()
}
panel.cor <- function(x, y, digits=2, prefix="", cex.cor)
{
usr <- par("usr"); on.exit(par(usr))
par(usr = c(0, 1, 0, 1))
r <- abs(cor(x, y,method="spearman"))
txt <- format(c(r, 0.123456789), digits=digits)[1]
txt <- paste(prefix, txt, sep="")
if(missing(cex.cor)) cex <- 0.8/strwidth(txt)
text(0.5, 0.5, txt, cex = cex)
}
if (class(x)=="formula"){
x<-model.frame(x,data=data)
}
pairs(x,
lower.panel=panel.smooth,
upper.panel=panel.cor,
diag.panel=panel.hist,
cex.labels = 1, font.labels=2)
}
Loading Files
Here the landsurfacetemp.tif file is loaded. If you had trouble converting your temperature values from Kelvin to Fahrenheit you can do that here. Then the weather and weather station data are loaded and merged.
To get the weather and weather station data, follow these steps:
Access the Daily Temperature and Precipitation Reports - Data Tables from NOAA at NOAA Temperature Tables. Click on “Data Access” and select GHCN Daily Observations. It may be better to use Chrome to view the NOAA maps.
Click on the Time-Related Maps tab. Select Hourly/Sub-Hourly.
Type in Portland, OR and zoom out so that you can select the same extent of the Landsat image. You may need to look at the latitude and longitude of the image.
Click the wrench icon next to Hourly Global. Then select “Rectangle” to draw a boundary around all the applicable weather stations.
Select all the weather stations and download the station list.
Then click the orange button, “Access Data”. Use the date range to select data from midnight to midnight on July 30th, 2017.
You will eventually be directed to a page with a text version of the weather data. Save this text file and convert it into a CSV file that you can then import into R. Here’s an example of how the files should be formatted before loading them into R:
You will need to change the directories in the code below so that they properly reference your files.
sat_temp = raster("D:/Google Drive/School/PDX/2018 Winter/ENGR510-DevelopmentEngineering/ENGR510-CourseMaterials/Labs/Remote Sensing/QGIS Files/surface_temperature3.tif")
# sat_temp = raster("C:/Users/bryan/Google Drive/School/PDX/2018 Winter/ENGR510-DevelopmentEngineering/ENGR510-CourseMaterials/Labs/Remote Sensing/QGIS Files/surface_temperature3.tif")
plot(sat_temp,col=rev(heat.colors(5)))

# converting kelvin to fahrenheit
sat_tempF = sat_temp * 9/5 - 459.67
# excluding edge values
sat_tempF = calc(sat_tempF, fun=function(x){ x[x < 0] <- NA; return(x)})
# changing projection
sat_tempF = projectRaster(sat_tempF,crs="+proj=longlat +datum=WGS84")
plot(sat_tempF,col=rev(heat.colors(5)))

# loading the weather station data
stations = setDT(read.csv("D:/Google Drive/School/PDX/2018 Winter/ENGR510-DevelopmentEngineering/ENGR510-CourseMaterials/Labs/Remote Sensing/R Files/weather_data/stations.csv"))
# eliminating repeat stations
stations = unique(stations,by="USAF")
stations = unique(stations,by="BAN_ID")
stations = stations[USAF!=726881]
stations = stations[USAF!=726988]
# loading the weather data (from NOAA)
weather = setDT(read.csv("D:/Google Drive/School/PDX/2018 Winter/ENGR510-DevelopmentEngineering/ENGR510-CourseMaterials/Labs/Remote Sensing/R Files/weather_data/weather.csv"))
weather[,dt:=ymd_hms(paste(YEAR,MONTH,DAY,HOUR,MINUTE,SECOND,sep="-"))]
weather_data = merge(stations, weather, by="USAF")
Correlating Temperatures
In this section we extract the estimated land surface temperatures for each weather station. Then we compare the hourly weather station data against the estimated land surface temperatures to hypothesize what time the landsat image was captured.
# creating a spatial points data frame for each weather station
coordinates = SpatialPoints(stations[,.(LONGITUDE,LATITUDE)],
proj4string = CRS("+proj=longlat +datum=WGS84
+ellps=WGS84 +towgs84=0,0,0"))
weather_map = SpatialPointsDataFrame(coordinates,stations)
# pulling temperatures from sat_tempF at each spatial point
sat_temps = raster::extract(sat_tempF,weather_map)
# merging observed temperatures with station location
stations[,sat_temp:=sat_temps]
# plotting sat_temps with weather stations highlighted
plot(sat_tempF,col=rev(heat.colors(5)))
points(stations[,.(LONGITUDE,LATITUDE,sat_temps)],pch="+",cex=2,col=rev(heat.colors(5)))

# display data table
weather_data
# combining station and satellite temperature data
stations[,HOUR:="sat"]
setnames(stations,"sat_temp","TEMP")
sat_station_temps = rbind(weather_data[,.(USAF,NAME,LATITUDE,LONGITUDE,TEMP,HOUR)],
stations[,.(USAF,NAME,LATITUDE,LONGITUDE,TEMP,HOUR)])
sat_station_temps$TEMP = as.numeric(as.character(sat_station_temps$TEMP))
NAs introduced by coercion
# converting groups to factor
sat_station_temps$HOUR = as.factor(sat_station_temps$HOUR)
sat_station_temps$USAF = as.factor(sat_station_temps$USAF)
# graphing the temperatures
ggplot(sat_station_temps, aes(x=USAF,y=TEMP)) +
geom_point(aes(color=HOUR))

Sometimes ggplot or static graphics are not helpful for distinguishing patterns in the data. Here’s another plot using an interactive HTML widget. You can use your mouse to highlight particular points, toggle values in the legend, or zoom in and out for a better view.
# now in plotly
plot_ly(data=sat_station_temps, x=~USAF, y=~TEMP, color=~HOUR, type = 'scatter')
No scatter mode specifed:
Setting the mode to markers
Read more about this attribute -> https://plot.ly/r/reference/#scatter-mode
Ignoring 6 observationsn too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
No scatter mode specifed:
Setting the mode to markers
Read more about this attribute -> https://plot.ly/r/reference/#scatter-mode
Ignoring 6 observationsn too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
However, if we want to get a better idea what the actual correlations are, we can calculate them for each hour. You can change the columns displayed in the cor.matrix argument (i.e., change weather_combined[,c(2:10,26),with=FALSE] to a different subset of columns like 2:10, 10-20, etc.).
# reshaping the data so that the hours are arranged as columns
weather_data$TEMP = as.numeric(as.character(weather_data$TEMP))
NAs introduced by coercion
weather_data[,HOUR_column:=paste0("H",HOUR)]
weather_simple = weather_data[,.(TEMP),by=.(USAF,HOUR_column)]
weather_simple = unique(weather_simple,by=c("USAF","HOUR_column"))
weather_wide = weather_simple %>% spread(HOUR_column,TEMP)
# merge hour temperatures with sat temperatures
setnames(stations,"TEMP","sat_temp")
weather_combined = merge(weather_wide,stations[,.(USAF,sat_temp)],by="USAF")
cor.matrix(weather_combined[,c(15:20,26),with=FALSE])
the condition has length > 1 and only the first element will be used

# may have to omit NA values to perform cor function
# weather_combined = na.omit(weather_combined)
# correlations = cor(weather_combined[,2:25,with=FALSE],weather_combined[,26,with=FALSE],method="spearman")
# correlations
# rownames(correlations)[which.max(correlations)]
# correlations[which.max(correlations)]
Lab Assignment
You will need to compose a formal lab report not exceeding five pages (excluding appendices). This can be submitted as an R Notebook (PDF version of the main report less than 5 pages) or as a PDF. You will need to include a brief introduction, a methods section, results, discussion, and a brief conclusion. See the syllabus and grading rubric for more a more detailed description. Feel free to include information or images from the first assignment that you think are relevant. Also, if you feel pressed for space you can include some of the images or tables in the appendices.
In particular, you should address the following questions:
Based on your analysis, what time do you hypothesize that the Landsat image was captured? Provide a justification for your hypothesis.
See if you can determine the actual time the image was captured. Does it match up with your analysis? If they are different, what could contribute to the difference? *Hint: check the file name when downloading through the SCP plug-in or search for the MTL file.
How would you characterize the different forms of error that may have influenced your analysis? Provide a qualitative analysis of precision (random errors), accuracy (systematic errors), and operator error (proper execution of methodology and/or use of technology).
Provide a brief description of how this ground-truth data could be used to calibrate measurements from remote sensors.
How would you characterize the potential for remote sensors to be used in development engineering?
Submit your final PDF or Rmd file on D2L through Dropbox by 3pm on Tuesday, January 30th.
LS0tDQp0aXRsZTogIlJlbW90ZSBUZW1wZXJhdHVyZSBTZW5zaW5nIExhYiBQYXJ0IElJIg0Kb3V0cHV0OiANCiAgIGh0bWxfbm90ZWJvb2s6DQogICAgIHRvYzogeWVzDQogICAgIHRvY19mbG9hdDogeWVzDQogICAgIHRoZW1lOiBzaW1wbGV4DQotLS0NCg0KSW4gdGhlIHNlY29uZCBwYXJ0IG9mIHRoaXMgbGFiIHlvdSB3aWxsIGRvd25sb2FkIGhvdXJseSB0ZW1wZXJhdHVyZSBtZWFzdXJlbWVudHMgZnJvbSB3ZWF0aGVyIHN0YXRpb25zIHdpdGhpbiB0aGUgYm91bmRhcnkgb2YgdGhlIExhbmRzYXQgaW1hZ2UgdG8gY29tcGFyZSBvYnNlcnZlZCB0ZW1wZXJhdHVyZXMgYWdhaW5zdCB0aGUgZXN0aW1hdGVkIHRlbXBlcmF0dXJlcyBmcm9tIExhbmRzYXQgb24gSnVseSAzMHRoLCAyMDE3LiBZb3Ugd2lsbCB0aGVuIHVzZSBSIHRvIGV4dHJhY3QgdGhlIHRlbXBlcmF0dXJlcyBmcm9tIHRoZSBsYW5kIHN1cmZhY2UgdGVtcGVyYXR1cmVzIHJhc3RlciB5b3UgY3JlYXRlZCBsYXN0IHdlZWsuIFRoZXNlIG9ic2VydmVkIGFuZCBlc3RpbWF0ZWQgdGVtcGVyYXR1cmVzIHdpbGwgYmUgY29tcGFyZWQgdmlzdWFsbHkgYW5kIHN0YXRpc3RpY2FsbHkgdG8gaHlwb3RoZXNpemUgd2hhdCB0aW1lIHRoZSBMYW5kc2F0IGltYWdlIHdhcyBjYXB0dXJlZC4gRmluYWxseSwgeW91IHdpbGwgY3JlYXRlIGEgcHJvcGVybHkgZm9ybWF0dGVkIFIgTm90ZWJvb2sgKG1vZGlmaWVkIGZyb20gdGhpcyB2ZXJzaW9uKSBvciBQREYgbGFiIHJlcG9ydCB0byBwcmVzZW50IHlvdXIgZmluZGluZ3MuDQoNCkdpdmVuIHRoZSB2YXJ5aW5nIGxldmVscyBvZiBmYW1pbGlhcml0eSB3aXRoIFIsIG11Y2ggb2YgdGhlIGNvZGluZyBmb3IgdGhpcyBsYWIgd2lsbCBiZSBkb25lIGZvciB5b3UuIEhvd2V2ZXIsIG1ha2Ugc3VyZSB0aGF0IHlvdSBjYXJlZnVsbHkgd2FsayB0aHJvdWdoIGVhY2ggc3RlcCBhbmQgdW5kZXJzdGFuZCB3aGF0IGlzIGJlaW5nIGRvbmUgYmVjYXVzZSB5b3Ugd2lsbCBoYXZlIGxlc3MgY29kZSB3cml0dGVuIGZvciB5b3UgaW4gZnV0dXJlIGxhYnMuDQoNCjEuIElmIHlvdSBkb24ndCBhbHJlYWR5IGhhdmUgUiwgeW91IGNhbiBkb3dubG9hZCBpdCBhdCA8YSBocmVmPSJodHRwOi8vd3d3LnItcHJvamVjdC5vcmciIHRhcmdldD0iX2JsYW5rIj5odHRwOi8vd3d3LnItcHJvamVjdC5vcmc8L2E+Lg0KDQoyLiBXZSBhbHNvIHJlY29tbWVuZCB1c2luZyBSIFN0dWRpbyBhcyBhbiBpbnRlZ3JhdGVkIGRldmVsb3BtZW50IGVudmlyb25tZW50IChJREUpIGZvciBSOiA8YSBocmVmPSJodHRwczovL3d3dy5yc3R1ZGlvLmNvbS8iIHRhcmdldD0iX2JsYW5rIj5odHRwczovL3d3dy5yc3R1ZGlvLmNvbS88L2E+Lg0KDQozLiBPcGVuIFIgKG9yIFIgU3R1ZGlvKSBhbmQgaW5zdGFsbCB0aGUgbmVjZXNzYXJ5IHBhY2thZ2VzLiBSIHJlbGllcyBvbiBleHRlcm5hbGx5IHdyaXR0ZW4gInBhY2thZ2VzIiB0aGF0IGNhcnJ5IG1hbnkgb2YgdGhlIGZ1bmN0aW9ucyBuZWVkZWQuIFlvdSB3aWxsIGJlIHVzaW5nIHNldmVyYWwgcGFja2FnZXMgZm9yIHRoaXMgbGFiLiBGb3IgZWFjaCBwYWNrYWdlIHlvdSB3aWxsIG5lZWQgdG8gaW5zdGFsbCB0aGVtIGJ5IHR5cGluZyB0aGUgZm9sbG93aW5nIGNvZGUgaW50byB0aGUgY29tbWFuZCBjb25zb2xlOiBgaW5zdGFsbC5wYWNrYWdlcygicmFzdGVyIilgLCB3aGVyZSBgcmFzdGVyYCBpcyB0aGUgbmFtZSBvZiB0aGUgZmlyc3QgcGFja2FnZS4NCg0KNC4gVGhlIGNvb2wgdGhpbmcgYWJvdXQgYW4gUiBOb3RlYm9vayBpcyB0aGF0IHlvdSBjYW4gdmlldyBpdCBpbiBhbnkgYnJvd3NlciwgYnV0IHlvdSBjYW4gYWxzbyBvcGVuIGl0IGluIFIuIFlvdSBjYW4gZG93bmxvYWQgdGhpcyBkb2N1bWVudCBhcyBhbiBSbWQgZmlsZSBieSBjbGlja2luZyBvbiB0aGUgYnV0dG9uIHRoYXQgc2F5cyAiQ29kZSIgaW4gdGhlIHRvcCByaWdodC1oYW5kIGNvcm5lci4gVGhlbiBzZWxlY3QgRG93bmxvYWQgUm1kIGFuZCBvcGVuIHRoZSBmaWxlIGZyb20gUi4NCg0KSGVyZSBhcmUgYSBmZXcgaGVscGZ1bCBub3RlcyBhYm91dCBSOg0KDQoqIFIgYWxsb3dzIHlvdSB0byBydW4gZWl0aGVyIHRoZSBlbnRpcmUgc2NyaXB0LCBvciBqdXN0IHNlbGVjdGVkIGxpbmVzIGF0IGEgdGltZS4gVGhpcyBub3RlYm9vayBkb2VzIGEgbG90IG9mIHRoZSB3b3JrIGZvciB5b3UuIEl0IGhhcyBhbGwgdGhlIGVsZW1lbnRzIHlvdSBuZWVkIHRvIG1vZGlmeSB0aGUgc2NyaXB0LA0KDQoqIFNpbmNlIHRoaXMgaXMgYW4gW1IgTWFya2Rvd25dKGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20pIE5vdGVib29rLCB3aGVuIHlvdSBleGVjdXRlIGNvZGUgd2l0aGluIHRoZSBub3RlYm9vaywgdGhlIHJlc3VsdHMgYXBwZWFyIGJlbmVhdGggdGhlIGNvZGUuIFRyeSBleGVjdXRpbmcgdGhlIGZpcnN0IGNvZGUgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ3RybCtTaGlmdCtFbnRlciouDQoNCiogQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkN0cmwrQWx0K0kqLg0KDQoqIFdoZW4geW91IHNhdmUgdGhlIG5vdGVib29rLCBhbiBIVE1MIGZpbGUgY29udGFpbmluZyB0aGUgY29kZSBhbmQgb3V0cHV0IHdpbGwgYmUgc2F2ZWQgYWxvbmdzaWRlIGl0IChjbGljayB0aGUgKlByZXZpZXcqIGJ1dHRvbiBvciBwcmVzcyAqQ3RybCtTaGlmdCtLKiB0byBwcmV2aWV3IHRoZSBIVE1MIGZpbGUpLg0KDQoqIElmIHlvdSB3YW50IHRvIGxlYXJuIG1vcmUgYWJvdXQgYmVzdCBwcmFjdGljZXMgZm9yIGNvZGUgd3JpdGluZywgeW91IGNhbiBjaGVjayBvdXQgdGhpcyBzdHlsZSBndWlkZTogPGEgaHJlZj0iaHR0cDovL2Fkdi1yLmhhZC5jby5uei9TdHlsZS5odG1sIiB0YXJnZXQ9Il9ibGFuayI+aHR0cDovL2Fkdi1yLmhhZC5jby5uei9TdHlsZS5odG1sPC9hPi4NCg0KIyBTZXR1cA0KDQpUaGlzIGNvZGUgY2h1bmsgbG9hZHMgdGhlIHBhY2thZ2VzIHVzZWQgaW4gdGhpcyBub3RlYm9vay4NCg0KYGBge3IgZ2xvYmFsX29wdGlvbnMsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkocmFzdGVyKQ0KbGlicmFyeShyZ2RhbCkNCmxpYnJhcnkoZGF0YS50YWJsZSkNCmxpYnJhcnkoc3ApDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShwbG90bHkpDQpsaWJyYXJ5KGtuaXRyKQ0Ka25pdHI6Om9wdHNfa25pdCRzZXQoZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFKQ0KDQpgYGANCg0KVGhpcyBpcyBhbHNvIGEgZ29vZCBwbGFjZSB0byBwdXQgaGVscGVyIGZ1bmN0aW9ucy4gQmVsb3cgaXMgYSBmdW5jdGlvbiB0aGF0IHdlIHdpbGwgdXNlIHRvIGNyZWF0ZSBhIGNvcnJlbGF0aW9uIG1hdHJpeCBvZiB0aGUgZGlmZmVyZW50IHRlbXBlcmF0dXJlcyBhZ2FpbnN0IHRoZSBlc3RpbWF0ZWQgbGFuZCBzdXJmYWNlIHRlbXBlcmF0dXJlcyBmcm9tIExhbmRzYXQuDQoNCmBgYHtyfQ0KY29yLm1hdHJpeDwtZnVuY3Rpb24oeCxkYXRhPU5BKXsNCiAgIyBwYW5lbC5oaXN0IGZ1bmN0aW9uIGFkZHMgdGhlIGhpc3RvZ3JhbSANCiAgcGFuZWwuaGlzdCA8LSBmdW5jdGlvbih4LCAuLi4pDQogIHsNCiAgICAgIHVzciA8LSBwYXIoInVzciIpOyBvbi5leGl0KHBhcih1c3IpKQ0KICAgICAgcGFyKHVzciA9IGModXNyWzE6Ml0sIDAsIDEuNSkgKQ0KICAgICAgaCA8LSBoaXN0KHgsIHBsb3QgPSBGQUxTRSkNCiAgICAgIGJyZWFrcyA8LSBoJGJyZWFrczsgbkIgPC0gbGVuZ3RoKGJyZWFrcykNCiAgICAgIHkgPC0gaCRjb3VudHM7IHkgPC0geS9tYXgoeSkNCiAgICAgIHJlY3QoYnJlYWtzWy1uQl0sIDAsIGJyZWFrc1stMV0sIHksIGNvbD0iY3lhbiIsIC4uLikNCiAgICAgIGJveCgpDQogIH0NCiAgcGFuZWwuY29yIDwtIGZ1bmN0aW9uKHgsIHksIGRpZ2l0cz0yLCBwcmVmaXg9IiIsIGNleC5jb3IpDQogIHsNCiAgICAgIHVzciA8LSBwYXIoInVzciIpOyBvbi5leGl0KHBhcih1c3IpKQ0KICAgICAgcGFyKHVzciA9IGMoMCwgMSwgMCwgMSkpDQogICAgICByIDwtIGFicyhjb3IoeCwgeSxtZXRob2Q9InNwZWFybWFuIikpDQogICAgICB0eHQgPC0gZm9ybWF0KGMociwgMC4xMjM0NTY3ODkpLCBkaWdpdHM9ZGlnaXRzKVsxXQ0KICAgICAgdHh0IDwtIHBhc3RlKHByZWZpeCwgdHh0LCBzZXA9IiIpDQogICAgICBpZihtaXNzaW5nKGNleC5jb3IpKSBjZXggPC0gMC44L3N0cndpZHRoKHR4dCkNCiAgICAgIHRleHQoMC41LCAwLjUsIHR4dCwgY2V4ID0gY2V4KQ0KICB9DQoNCiAgaWYgKGNsYXNzKHgpPT0iZm9ybXVsYSIpew0KICAgIHg8LW1vZGVsLmZyYW1lKHgsZGF0YT1kYXRhKQ0KICB9DQoNCiAgcGFpcnMoeCwNCiAgICAgICAgbG93ZXIucGFuZWw9cGFuZWwuc21vb3RoLA0KICAgICAgICB1cHBlci5wYW5lbD1wYW5lbC5jb3IsDQogICAgICAgIGRpYWcucGFuZWw9cGFuZWwuaGlzdCwNCiAgICAgICAgY2V4LmxhYmVscyA9IDEsIGZvbnQubGFiZWxzPTIpDQoNCn0NCg0KYGBgDQoNCiMgTG9hZGluZyBGaWxlcw0KDQpIZXJlIHRoZSBsYW5kc3VyZmFjZXRlbXAudGlmIGZpbGUgaXMgbG9hZGVkLiBJZiB5b3UgaGFkIHRyb3VibGUgY29udmVydGluZyB5b3VyIHRlbXBlcmF0dXJlIHZhbHVlcyBmcm9tIEtlbHZpbiB0byBGYWhyZW5oZWl0IHlvdSBjYW4gZG8gdGhhdCBoZXJlLiBUaGVuIHRoZSB3ZWF0aGVyIGFuZCB3ZWF0aGVyIHN0YXRpb24gZGF0YSBhcmUgbG9hZGVkIGFuZCBtZXJnZWQuDQoNClRvIGdldCB0aGUgd2VhdGhlciBhbmQgd2VhdGhlciBzdGF0aW9uIGRhdGEsIGZvbGxvdyB0aGVzZSBzdGVwczoNCg0KMS4gQWNjZXNzIHRoZSBEYWlseSBUZW1wZXJhdHVyZSBhbmQgUHJlY2lwaXRhdGlvbiBSZXBvcnRzIC0gRGF0YSBUYWJsZXMgZnJvbSBOT0FBIGF0IDxhIGhyZWY9Imh0dHBzOi8vd3d3LmNsaW1hdGUuZ292L21hcHMtZGF0YS9kYXRhc2V0L2RhaWx5LXRlbXBlcmF0dXJlLWFuZC1wcmVjaXBpdGF0aW9uLXJlcG9ydHMtZGF0YS10YWJsZXMiIHRhcmdldD0iX2JsYW5rIj5OT0FBIFRlbXBlcmF0dXJlIFRhYmxlczwvYT4uIENsaWNrIG9uICJEYXRhIEFjY2VzcyIgYW5kIHNlbGVjdCBHSENOIERhaWx5IE9ic2VydmF0aW9ucy4gSXQgbWF5IGJlIGJldHRlciB0byB1c2UgQ2hyb21lIHRvIHZpZXcgdGhlIE5PQUEgbWFwcy4NCg0KMi4gQ2xpY2sgb24gdGhlIFRpbWUtUmVsYXRlZCBNYXBzIHRhYi4gU2VsZWN0IEhvdXJseS9TdWItSG91cmx5Lg0KDQozLiBUeXBlIGluIFBvcnRsYW5kLCBPUiBhbmQgem9vbSBvdXQgc28gdGhhdCB5b3UgY2FuIHNlbGVjdCB0aGUgc2FtZSBleHRlbnQgb2YgdGhlIExhbmRzYXQgaW1hZ2UuIFlvdSBtYXkgbmVlZCB0byBsb29rIGF0IHRoZSBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlIG9mIHRoZSBpbWFnZS4NCg0KNC4gQ2xpY2sgdGhlIHdyZW5jaCBpY29uIG5leHQgdG8gSG91cmx5IEdsb2JhbC4gVGhlbiBzZWxlY3QgIlJlY3RhbmdsZSIgdG8gZHJhdyBhIGJvdW5kYXJ5IGFyb3VuZCBhbGwgdGhlIGFwcGxpY2FibGUgd2VhdGhlciBzdGF0aW9ucy4NCg0KNS4gU2VsZWN0IGFsbCB0aGUgd2VhdGhlciBzdGF0aW9ucyBhbmQgZG93bmxvYWQgdGhlIHN0YXRpb24gbGlzdC4NCg0KNi4gVGhlbiBjbGljayB0aGUgb3JhbmdlIGJ1dHRvbiwgIkFjY2VzcyBEYXRhIi4gVXNlIHRoZSBkYXRlIHJhbmdlIHRvIHNlbGVjdCBkYXRhIGZyb20gbWlkbmlnaHQgdG8gbWlkbmlnaHQgb24gSnVseSAzMHRoLCAyMDE3Lg0KDQo3LiBZb3Ugd2lsbCBldmVudHVhbGx5IGJlIGRpcmVjdGVkIHRvIGEgcGFnZSB3aXRoIGEgdGV4dCB2ZXJzaW9uIG9mIHRoZSB3ZWF0aGVyIGRhdGEuIFNhdmUgdGhpcyB0ZXh0IGZpbGUgYW5kIGNvbnZlcnQgaXQgaW50byBhIENTViBmaWxlIHRoYXQgeW91IGNhbiB0aGVuIGltcG9ydCBpbnRvIFIuIEhlcmUncyBhbiBleGFtcGxlIG9mIGhvdyB0aGUgZmlsZXMgc2hvdWxkIGJlIGZvcm1hdHRlZCBiZWZvcmUgbG9hZGluZyB0aGVtIGludG8gUjoNCg0KICAgICsgPGEgaHJlZj0iaHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL29wZW4/aWQ9MU1FOHJKNzZoRFRZcFdxUXc3WFpiaDE3V2VjSjRZSkhFIiB0YXJnZXQ9Il9ibGFuayI+V2VhdGhlciBkYXRhIGV4YW1wbGU8L2E+DQogICAgDQogICAgKyA8YSBocmVmPSJodHRwczovL2RyaXZlLmdvb2dsZS5jb20vYS9wZHguZWR1L2ZpbGUvZC8xQjk5Q3NIQTItRmRFQmNOMy0zWXg0QzczVlJMSXAwVFAvdmlldz91c3A9c2hhcmluZyIgdGFyZ2V0PSJfYmxhbmsiPldlYXRoZXIgc3RhdGlvbiBkYXRhIGV4YW1wbGU8L2E+DQoNCllvdSB3aWxsIG5lZWQgdG8gY2hhbmdlIHRoZSBkaXJlY3RvcmllcyBpbiB0aGUgY29kZSBiZWxvdyBzbyB0aGF0IHRoZXkgcHJvcGVybHkgcmVmZXJlbmNlIHlvdXIgZmlsZXMuDQoNCmBgYHtyfQ0Kc2F0X3RlbXAgPSByYXN0ZXIoIkQ6L0dvb2dsZSBEcml2ZS9TY2hvb2wvUERYLzIwMTggV2ludGVyL0VOR1I1MTAtRGV2ZWxvcG1lbnRFbmdpbmVlcmluZy9FTkdSNTEwLUNvdXJzZU1hdGVyaWFscy9MYWJzL1JlbW90ZSBTZW5zaW5nL1FHSVMgRmlsZXMvc3VyZmFjZV90ZW1wZXJhdHVyZTMudGlmIikNCiMgc2F0X3RlbXAgPSByYXN0ZXIoIkM6L1VzZXJzL2JyeWFuL0dvb2dsZSBEcml2ZS9TY2hvb2wvUERYLzIwMTggV2ludGVyL0VOR1I1MTAtRGV2ZWxvcG1lbnRFbmdpbmVlcmluZy9FTkdSNTEwLUNvdXJzZU1hdGVyaWFscy9MYWJzL1JlbW90ZSBTZW5zaW5nL1FHSVMgRmlsZXMvc3VyZmFjZV90ZW1wZXJhdHVyZTMudGlmIikNCnBsb3Qoc2F0X3RlbXAsY29sPXJldihoZWF0LmNvbG9ycyg1KSkpDQoNCiMgY29udmVydGluZyBrZWx2aW4gdG8gZmFocmVuaGVpdA0Kc2F0X3RlbXBGID0gc2F0X3RlbXAgKiA5LzUgLSA0NTkuNjcNCg0KIyBleGNsdWRpbmcgZWRnZSB2YWx1ZXMNCnNhdF90ZW1wRiA9IGNhbGMoc2F0X3RlbXBGLCBmdW49ZnVuY3Rpb24oeCl7IHhbeCA8IDBdIDwtIE5BOyByZXR1cm4oeCl9KQ0KDQojIGNoYW5naW5nIHByb2plY3Rpb24NCnNhdF90ZW1wRiA9IHByb2plY3RSYXN0ZXIoc2F0X3RlbXBGLGNycz0iK3Byb2o9bG9uZ2xhdCArZGF0dW09V0dTODQiKQ0KcGxvdChzYXRfdGVtcEYsY29sPXJldihoZWF0LmNvbG9ycyg1KSkpDQoNCiMgbG9hZGluZyB0aGUgd2VhdGhlciBzdGF0aW9uIGRhdGENCnN0YXRpb25zID0gc2V0RFQocmVhZC5jc3YoIkQ6L0dvb2dsZSBEcml2ZS9TY2hvb2wvUERYLzIwMTggV2ludGVyL0VOR1I1MTAtRGV2ZWxvcG1lbnRFbmdpbmVlcmluZy9FTkdSNTEwLUNvdXJzZU1hdGVyaWFscy9MYWJzL1JlbW90ZSBTZW5zaW5nL1IgRmlsZXMvd2VhdGhlcl9kYXRhL3N0YXRpb25zLmNzdiIpKQ0KDQojIGVsaW1pbmF0aW5nIHJlcGVhdCBzdGF0aW9ucw0Kc3RhdGlvbnMgPSB1bmlxdWUoc3RhdGlvbnMsYnk9IlVTQUYiKQ0Kc3RhdGlvbnMgPSB1bmlxdWUoc3RhdGlvbnMsYnk9IkJBTl9JRCIpDQpzdGF0aW9ucyA9IHN0YXRpb25zW1VTQUYhPTcyNjg4MV0NCnN0YXRpb25zID0gc3RhdGlvbnNbVVNBRiE9NzI2OTg4XQ0KDQojIGxvYWRpbmcgdGhlIHdlYXRoZXIgZGF0YSAoZnJvbSBOT0FBKQ0Kd2VhdGhlciA9IHNldERUKHJlYWQuY3N2KCJEOi9Hb29nbGUgRHJpdmUvU2Nob29sL1BEWC8yMDE4IFdpbnRlci9FTkdSNTEwLURldmVsb3BtZW50RW5naW5lZXJpbmcvRU5HUjUxMC1Db3Vyc2VNYXRlcmlhbHMvTGFicy9SZW1vdGUgU2Vuc2luZy9SIEZpbGVzL3dlYXRoZXJfZGF0YS93ZWF0aGVyLmNzdiIpKQ0Kd2VhdGhlclssZHQ6PXltZF9obXMocGFzdGUoWUVBUixNT05USCxEQVksSE9VUixNSU5VVEUsU0VDT05ELHNlcD0iLSIpKV0NCg0Kd2VhdGhlcl9kYXRhID0gbWVyZ2Uoc3RhdGlvbnMsIHdlYXRoZXIsIGJ5PSJVU0FGIikNCg0KYGBgDQoNCiMgQ29ycmVsYXRpbmcgVGVtcGVyYXR1cmVzDQoNCkluIHRoaXMgc2VjdGlvbiB3ZSBleHRyYWN0IHRoZSBlc3RpbWF0ZWQgbGFuZCBzdXJmYWNlIHRlbXBlcmF0dXJlcyBmb3IgZWFjaCB3ZWF0aGVyIHN0YXRpb24uIFRoZW4gd2UgY29tcGFyZSB0aGUgaG91cmx5IHdlYXRoZXIgc3RhdGlvbiBkYXRhIGFnYWluc3QgdGhlIGVzdGltYXRlZCBsYW5kIHN1cmZhY2UgdGVtcGVyYXR1cmVzIHRvIGh5cG90aGVzaXplIHdoYXQgdGltZSB0aGUgbGFuZHNhdCBpbWFnZSB3YXMgY2FwdHVyZWQuDQoNCmBgYHtyfQ0KDQojIGNyZWF0aW5nIGEgc3BhdGlhbCBwb2ludHMgZGF0YSBmcmFtZSBmb3IgZWFjaCB3ZWF0aGVyIHN0YXRpb24NCmNvb3JkaW5hdGVzID0gU3BhdGlhbFBvaW50cyhzdGF0aW9uc1ssLihMT05HSVRVREUsTEFUSVRVREUpXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9qNHN0cmluZyA9IENSUygiK3Byb2o9bG9uZ2xhdCArZGF0dW09V0dTODQgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgK2VsbHBzPVdHUzg0ICt0b3dnczg0PTAsMCwwIikpDQp3ZWF0aGVyX21hcCA9IFNwYXRpYWxQb2ludHNEYXRhRnJhbWUoY29vcmRpbmF0ZXMsc3RhdGlvbnMpDQoNCiMgcHVsbGluZyB0ZW1wZXJhdHVyZXMgZnJvbSBzYXRfdGVtcEYgYXQgZWFjaCBzcGF0aWFsIHBvaW50DQpzYXRfdGVtcHMgPSByYXN0ZXI6OmV4dHJhY3Qoc2F0X3RlbXBGLHdlYXRoZXJfbWFwKQ0KDQojIG1lcmdpbmcgb2JzZXJ2ZWQgdGVtcGVyYXR1cmVzIHdpdGggc3RhdGlvbiBsb2NhdGlvbg0Kc3RhdGlvbnNbLHNhdF90ZW1wOj1zYXRfdGVtcHNdDQoNCiMgcGxvdHRpbmcgc2F0X3RlbXBzIHdpdGggd2VhdGhlciBzdGF0aW9ucyBoaWdobGlnaHRlZA0KcGxvdChzYXRfdGVtcEYsY29sPXJldihoZWF0LmNvbG9ycyg1KSkpDQpwb2ludHMoc3RhdGlvbnNbLC4oTE9OR0lUVURFLExBVElUVURFLHNhdF90ZW1wcyldLHBjaD0iKyIsY2V4PTIsY29sPXJldihoZWF0LmNvbG9ycyg1KSkpDQoNCiMgZGlzcGxheSBkYXRhIHRhYmxlDQp3ZWF0aGVyX2RhdGENCg0KIyBjb21iaW5pbmcgc3RhdGlvbiBhbmQgc2F0ZWxsaXRlIHRlbXBlcmF0dXJlIGRhdGENCnN0YXRpb25zWyxIT1VSOj0ic2F0Il0NCnNldG5hbWVzKHN0YXRpb25zLCJzYXRfdGVtcCIsIlRFTVAiKQ0Kc2F0X3N0YXRpb25fdGVtcHMgPSByYmluZCh3ZWF0aGVyX2RhdGFbLC4oVVNBRixOQU1FLExBVElUVURFLExPTkdJVFVERSxURU1QLEhPVVIpXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhdGlvbnNbLC4oVVNBRixOQU1FLExBVElUVURFLExPTkdJVFVERSxURU1QLEhPVVIpXSkNCnNhdF9zdGF0aW9uX3RlbXBzJFRFTVAgPSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihzYXRfc3RhdGlvbl90ZW1wcyRURU1QKSkNCg0KIyBjb252ZXJ0aW5nIGdyb3VwcyB0byBmYWN0b3INCnNhdF9zdGF0aW9uX3RlbXBzJEhPVVIgPSBhcy5mYWN0b3Ioc2F0X3N0YXRpb25fdGVtcHMkSE9VUikNCnNhdF9zdGF0aW9uX3RlbXBzJFVTQUYgPSBhcy5mYWN0b3Ioc2F0X3N0YXRpb25fdGVtcHMkVVNBRikNCg0KIyBncmFwaGluZyB0aGUgdGVtcGVyYXR1cmVzDQpnZ3Bsb3Qoc2F0X3N0YXRpb25fdGVtcHMsIGFlcyh4PVVTQUYseT1URU1QKSkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvcj1IT1VSKSkNCg0KYGBgDQoNClNvbWV0aW1lcyBnZ3Bsb3Qgb3Igc3RhdGljIGdyYXBoaWNzIGFyZSBub3QgaGVscGZ1bCBmb3IgZGlzdGluZ3Vpc2hpbmcgcGF0dGVybnMgaW4gdGhlIGRhdGEuIEhlcmUncyBhbm90aGVyIHBsb3QgdXNpbmcgYW4gaW50ZXJhY3RpdmUgSFRNTCB3aWRnZXQuIFlvdSBjYW4gdXNlIHlvdXIgbW91c2UgdG8gaGlnaGxpZ2h0IHBhcnRpY3VsYXIgcG9pbnRzLCB0b2dnbGUgdmFsdWVzIGluIHRoZSBsZWdlbmQsIG9yIHpvb20gaW4gYW5kIG91dCBmb3IgYSBiZXR0ZXIgdmlldy4NCg0KYGBge3J9DQoNCiMgbm93IGluIHBsb3RseQ0KcGxvdF9seShkYXRhPXNhdF9zdGF0aW9uX3RlbXBzLCB4PX5VU0FGLCB5PX5URU1QLCBjb2xvcj1+SE9VUiwgdHlwZSA9ICdzY2F0dGVyJykNCg0KYGBgDQoNCkhvd2V2ZXIsIGlmIHdlIHdhbnQgdG8gZ2V0IGEgYmV0dGVyIGlkZWEgd2hhdCB0aGUgYWN0dWFsIGNvcnJlbGF0aW9ucyBhcmUsIHdlIGNhbiBjYWxjdWxhdGUgdGhlbSBmb3IgZWFjaCBob3VyLiBZb3UgY2FuIGNoYW5nZSB0aGUgY29sdW1ucyBkaXNwbGF5ZWQgaW4gdGhlIGNvci5tYXRyaXggYXJndW1lbnQgKGkuZS4sIGNoYW5nZSBgd2VhdGhlcl9jb21iaW5lZFssYygyOjEwLDI2KSx3aXRoPUZBTFNFXWAgdG8gYSBkaWZmZXJlbnQgc3Vic2V0IG9mIGNvbHVtbnMgbGlrZSAyOjEwLCAxMC0yMCwgZXRjLikuDQoNCmBgYHtyfQ0KIyByZXNoYXBpbmcgdGhlIGRhdGEgc28gdGhhdCB0aGUgaG91cnMgYXJlIGFycmFuZ2VkIGFzIGNvbHVtbnMNCndlYXRoZXJfZGF0YSRURU1QID0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIod2VhdGhlcl9kYXRhJFRFTVApKQ0Kd2VhdGhlcl9kYXRhWyxIT1VSX2NvbHVtbjo9cGFzdGUwKCJIIixIT1VSKV0NCndlYXRoZXJfc2ltcGxlID0gd2VhdGhlcl9kYXRhWywuKFRFTVApLGJ5PS4oVVNBRixIT1VSX2NvbHVtbildDQp3ZWF0aGVyX3NpbXBsZSA9IHVuaXF1ZSh3ZWF0aGVyX3NpbXBsZSxieT1jKCJVU0FGIiwiSE9VUl9jb2x1bW4iKSkNCndlYXRoZXJfd2lkZSA9IHdlYXRoZXJfc2ltcGxlICU+JSBzcHJlYWQoSE9VUl9jb2x1bW4sVEVNUCkNCg0KIyBtZXJnZSBob3VyIHRlbXBlcmF0dXJlcyB3aXRoIHNhdCB0ZW1wZXJhdHVyZXMNCnNldG5hbWVzKHN0YXRpb25zLCJURU1QIiwic2F0X3RlbXAiKQ0Kd2VhdGhlcl9jb21iaW5lZCA9IG1lcmdlKHdlYXRoZXJfd2lkZSxzdGF0aW9uc1ssLihVU0FGLHNhdF90ZW1wKV0sYnk9IlVTQUYiKQ0KY29yLm1hdHJpeCh3ZWF0aGVyX2NvbWJpbmVkWyxjKDE1OjIwLDI2KSx3aXRoPUZBTFNFXSkNCg0KIyBtYXkgaGF2ZSB0byBvbWl0IE5BIHZhbHVlcyB0byBwZXJmb3JtIGNvciBmdW5jdGlvbg0KIyB3ZWF0aGVyX2NvbWJpbmVkID0gbmEub21pdCh3ZWF0aGVyX2NvbWJpbmVkKQ0KIyBjb3JyZWxhdGlvbnMgPSBjb3Iod2VhdGhlcl9jb21iaW5lZFssMjoyNSx3aXRoPUZBTFNFXSx3ZWF0aGVyX2NvbWJpbmVkWywyNix3aXRoPUZBTFNFXSxtZXRob2Q9InNwZWFybWFuIikNCiMgY29ycmVsYXRpb25zDQojIHJvd25hbWVzKGNvcnJlbGF0aW9ucylbd2hpY2gubWF4KGNvcnJlbGF0aW9ucyldDQojIGNvcnJlbGF0aW9uc1t3aGljaC5tYXgoY29ycmVsYXRpb25zKV0NCg0KYGBgDQoNCiMgTGFiIEFzc2lnbm1lbnQNCg0KWW91IHdpbGwgbmVlZCB0byBjb21wb3NlIGEgZm9ybWFsIGxhYiByZXBvcnQgbm90IGV4Y2VlZGluZyBmaXZlIHBhZ2VzIChleGNsdWRpbmcgYXBwZW5kaWNlcykuIFRoaXMgY2FuIGJlIHN1Ym1pdHRlZCBhcyBhbiBSIE5vdGVib29rIChQREYgdmVyc2lvbiBvZiB0aGUgbWFpbiByZXBvcnQgbGVzcyB0aGFuIDUgcGFnZXMpIG9yIGFzIGEgUERGLiBZb3Ugd2lsbCBuZWVkIHRvIGluY2x1ZGUgYSBicmllZiBpbnRyb2R1Y3Rpb24sIGEgbWV0aG9kcyBzZWN0aW9uLCByZXN1bHRzLCBkaXNjdXNzaW9uLCBhbmQgYSBicmllZiBjb25jbHVzaW9uLiBTZWUgdGhlIHN5bGxhYnVzIGFuZCBncmFkaW5nIHJ1YnJpYyBmb3IgbW9yZSBhIG1vcmUgZGV0YWlsZWQgZGVzY3JpcHRpb24uIEZlZWwgZnJlZSB0byBpbmNsdWRlIGluZm9ybWF0aW9uIG9yIGltYWdlcyBmcm9tIHRoZSBmaXJzdCBhc3NpZ25tZW50IHRoYXQgeW91IHRoaW5rIGFyZSByZWxldmFudC4gQWxzbywgaWYgeW91IGZlZWwgcHJlc3NlZCBmb3Igc3BhY2UgeW91IGNhbiBpbmNsdWRlIHNvbWUgb2YgdGhlIGltYWdlcyBvciB0YWJsZXMgaW4gdGhlIGFwcGVuZGljZXMuDQoNCkluIHBhcnRpY3VsYXIsIHlvdSBzaG91bGQgYWRkcmVzcyB0aGUgZm9sbG93aW5nIHF1ZXN0aW9uczoNCg0KMS4gQmFzZWQgb24geW91ciBhbmFseXNpcywgd2hhdCB0aW1lIGRvIHlvdSBoeXBvdGhlc2l6ZSB0aGF0IHRoZSBMYW5kc2F0IGltYWdlIHdhcyBjYXB0dXJlZD8gUHJvdmlkZSBhIGp1c3RpZmljYXRpb24gZm9yIHlvdXIgaHlwb3RoZXNpcy4NCg0KMi4gU2VlIGlmIHlvdSBjYW4gZGV0ZXJtaW5lIHRoZSBhY3R1YWwgdGltZSB0aGUgaW1hZ2Ugd2FzIGNhcHR1cmVkLiBEb2VzIGl0IG1hdGNoIHVwIHdpdGggeW91ciBhbmFseXNpcz8gSWYgdGhleSBhcmUgZGlmZmVyZW50LCB3aGF0IGNvdWxkIGNvbnRyaWJ1dGUgdG8gdGhlIGRpZmZlcmVuY2U/ICpIaW50OiBjaGVjayB0aGUgZmlsZSBuYW1lIHdoZW4gZG93bmxvYWRpbmcgdGhyb3VnaCB0aGUgU0NQIHBsdWctaW4gb3Igc2VhcmNoIGZvciB0aGUgTVRMIGZpbGUuDQoNCjMuIEhvdyB3b3VsZCB5b3UgY2hhcmFjdGVyaXplIHRoZSBkaWZmZXJlbnQgZm9ybXMgb2YgZXJyb3IgdGhhdCBtYXkgaGF2ZSBpbmZsdWVuY2VkIHlvdXIgYW5hbHlzaXM/IFByb3ZpZGUgYSBxdWFsaXRhdGl2ZSBhbmFseXNpcyBvZiBwcmVjaXNpb24gKHJhbmRvbSBlcnJvcnMpLCBhY2N1cmFjeSAoc3lzdGVtYXRpYyBlcnJvcnMpLCBhbmQgb3BlcmF0b3IgZXJyb3IgKHByb3BlciBleGVjdXRpb24gb2YgbWV0aG9kb2xvZ3kgYW5kL29yIHVzZSBvZiB0ZWNobm9sb2d5KS4NCg0KNC4gUHJvdmlkZSBhIGJyaWVmIGRlc2NyaXB0aW9uIG9mIGhvdyB0aGlzIGdyb3VuZC10cnV0aCBkYXRhIGNvdWxkIGJlIHVzZWQgdG8gY2FsaWJyYXRlIG1lYXN1cmVtZW50cyBmcm9tIHJlbW90ZSBzZW5zb3JzLg0KDQo1LiBIb3cgd291bGQgeW91IGNoYXJhY3Rlcml6ZSB0aGUgcG90ZW50aWFsIGZvciByZW1vdGUgc2Vuc29ycyB0byBiZSB1c2VkIGluIGRldmVsb3BtZW50IGVuZ2luZWVyaW5nPw0KDQpTdWJtaXQgeW91ciBmaW5hbCBQREYgb3IgUm1kIGZpbGUgb24gRDJMIHRocm91Z2ggRHJvcGJveCBieSAzcG0gb24gVHVlc2RheSwgSmFudWFyeSAzMHRoLg0K