The choropleths.Rmd notebook was getting long already, so I split this from it. It introduces the Montana 2018 election data which will be used in your published assignment.
There are two theories about Montana politics that I want us to test with these election results:
1. That more densely-populated areas and cities vote for Democrats, and more sparsely-populated and rural areas vote for Republicans, and
2. that the further West you go in the state, the more Democrats there are, and the further East, the more Republicans.
I will run through these hypotheses using he US House race with Gianforte (R) the winner and Williams (D). You will do your own notebook with Tester (D) the winner and Rosendale (R).
library(tidyverse)
library(leaflet)
library(sf)
library(readxl)
library(DT)
library(plotly)
library(broom)
library(tidycensus)
First you will need to upload the election data. Go to the MT secretary of state website here: http://mtelectionresults.gov
Then go to Election Resources at the bottom of the page, and then Statewide and County Exports, and then STATEWIDE EXPORT.
Then upload the spreadsheet to Files in RStudio. It should be called “Statewide Results.xlsx.”
In this spreadheet, sheet 2 is the 2018 US House race that we’ll use here. Sheet 1 is the Senate race that you’ll use for your analysis after this.
It’s an Excel file, so we use read_xlsx(). This won’t work quite right, but we’ll fix it next:
house_counties <- read_xlsx("Statewide Results.xlsx", sheet = 2)
New names:
* `` -> ...2
* `` -> ...3
* `` -> ...4
* `` -> ...5
Click on house_counties in the Global Environment, and see how the sheet isn’t all data. There’s some info in the first several rows, and the last row has a total. The range of the actual data is B7:E63.
house_counties <- read_xlsx("Statewide Results.xlsx", sheet = 2, range = "B7:E63")
Use glimpse() to view house_counties.
glimpse(house_counties)
Observations: 56
Variables: 4
$ County <chr> "Beaverhead", "Big Horn", "Blaine", "Broadwater", "Carbon", "Carter", "Casca…
$ `GREG GIANFORTE\r\nRepublican` <dbl> 3113, 1705, 1173, 2249, 3450, 636, 17219, 1515, 3085, 684, 2811, 1379, 1025,…
$ `KATHLEEN WILLIAMS\r\nDemocrat` <dbl> 1681, 2865, 1742, 897, 2442, 100, 15826, 1071, 1638, 240, 1128, 2693, 226, 1…
$ `ELINOR SWANSON\r\nLibertarian` <dbl> 122, 106, 87, 105, 184, 12, 925, 66, 168, 20, 125, 155, 24, 139, 1277, 1523,…
Notice that some of the column names are weird and long. Let’s change those with rename() so they’re easier to reference:
house_counties <- house_counties %>%
rename(Republican = "GREG GIANFORTE\r\nRepublican") %>%
rename(Democrat = "KATHLEEN WILLIAMS\r\nDemocrat") %>%
rename(Libertarian = "ELINOR SWANSON\r\nLibertarian")
Now glimpse() again to nake sure that worked.
glimpse(house_counties)
Observations: 56
Variables: 4
$ County <chr> "Beaverhead", "Big Horn", "Blaine", "Broadwater", "Carbon", "Carter", "Cascade", "Chouteau", "Cu…
$ Republican <dbl> 3113, 1705, 1173, 2249, 3450, 636, 17219, 1515, 3085, 684, 2811, 1379, 1025, 3915, 28450, 23157,…
$ Democrat <dbl> 1681, 2865, 1742, 897, 2442, 100, 15826, 1071, 1638, 240, 1128, 2693, 226, 1752, 18051, 31297, 5…
$ Libertarian <dbl> 122, 106, 87, 105, 184, 12, 925, 66, 168, 20, 125, 155, 24, 139, 1277, 1523, 20, 111, 22, 62, 23…
We want to know the Republican - Democrat difference in each county, as a percentage.
The following code has three mutate() commands 1. add up the total votes cast in the county
2. subtract the proportion of votes cast for the Dem from the proportion of votes cast for the Repub
3. multiply it by 100 to make it a percentage, and round it to 1 digit
house_counties <- house_counties %>%
mutate(total_votes = Republican + Democrat + Libertarian) %>%
mutate(Repub_advantage = Republican/total_votes - Democrat/total_votes) %>%
mutate(Repub_advantage = round(Repub_advantage*100, 1))
house_counties %>%
arrange(-Repub_advantage)
NA
Get census data for mapping
Now that we have the election data prepared, we need census data for the map of the state and the populations in each county.
- We get the map data by setting geometry = TRUE, and
- we get population data with variables = “B01003_001”
mt_counties <- get_acs(geography = "county",
variables = "B01003_001",
state = "MT",
geometry = TRUE)
Getting data from the 2014-2018 5-year ACS
Downloading feature geometry from the Census website. To cache shapefiles for use in future sessions, set `options(tigris_use_cache = TRUE)`.
Using FIPS code '30' for state 'MT'
Because we’re going to join the two datasets, we need to make sure they’re consistent with one another. One problem is that the names of the counties are a little different between the census data and the election data: ‘Lewis and Clark’ in the census data is ‘Lewis & Clark’ in the election data.
In addition, in the census data all the county names include the word County and Montana, like “Yellowstone County, Montana” whereas in the election data it’s called simply “Yellowstone.”
The following code changes both of those, and also changes the census column called estimate to Population, which we will use later:
house_counties[25, "County"] <- "Lewis and Clark" # Changes "&" "and"
mt_counties <- mt_counties %>%
mutate(County = gsub(" County, Montana", "", NAME)) %>% # Removes unnecessary words
rename(Population = estimate) # Renames the 'estimate' to 'Population'
This joins the election data with the map data into a new dataset called house_election
house_election <- mt_counties %>%
full_join(house_counties)
Joining, by = "County"
The following will make a nice table for displaying results. The as.tibble() command removes the geometry, which is not standard table format.
house_election %>%
as_tibble() %>%
select(County, Population, Democrat, Republican, Libertarian, total_votes, Repub_advantage) %>%
datatable()
NA
Montana counties choropleth
This is based on the code from the previous notebook.
vote_colors <- colorNumeric(palette = "viridis", domain = house_election$Repub_advantage)
house_election %>%
leaflet() %>%
addTiles() %>%
addPolygons(weight = 1,
fillColor = ~vote_colors(Repub_advantage),
label = ~paste0(County, ", Republican advantage = ", Repub_advantage),
highlight = highlightOptions(weight = 2)) %>%
setView(-110, 47, zoom = 6) %>%
addLegend(pal = vote_colors, values = ~Repub_advantage)
sf layer has inconsistent datum (+proj=longlat +datum=NAD83 +no_defs).
Need '+proj=longlat +datum=WGS84'
Predicting votes from population
One theory is that Democrats do better in more populated counties, and Republicans do better in more rural and less populated counties. That’s true of Democrats and Republicans at the national level - Democrats win in highly-populated cities and Republicans win in sparsely-populated rural areas.
Now let’s make a plotly graph.
house_election %>%
plot_ly(x = ~Population, y = ~Repub_advantage) %>%
add_markers()
Create better hover information and add title and better axis labels.
house_election %>%
plot_ly(x = ~Population,
y = ~Repub_advantage,
hoverinfo = "text",
text = ~paste("County:",
County, "<br>",
"Population: ", Population, "<br>",
"Republican advantage: ", Repub_advantage)) %>%
add_markers(marker = list(opacity = 0.7)) %>%
layout(title = "Predicting Republican Vote Advantage from Population, by County",
xaxis = list(title = "County population"),
yaxis = list(title = "Republican vote advantage"))
NA
Linear model or linear regression lm(y ~ x, data)
To create the regression model, use lm() for linear model. Call it pop_model for population model.
pop_model <- lm(Repub_advantage ~ Population, data = house_election)
The “old-fashioned” way to get the statistics out of this in R is to use summary. Put pop_model in summary() below:
summary(pop_model)
Call:
lm(formula = Repub_advantage ~ Population, data = house_election)
Residuals:
Min 1Q Median 3Q Max
-73.973 -10.320 3.959 15.305 47.232
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 34.1220288 4.0462304 8.433 1.99e-11 ***
Population -0.0003977 0.0001090 -3.648 0.000597 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 26.2 on 54 degrees of freedom
Multiple R-squared: 0.1977, Adjusted R-squared: 0.1828
F-statistic: 13.31 on 1 and 54 DF, p-value: 0.0005968
A more modern approach is to use some of the functions from the broom package, tidy() and glance(), because it sets up the output in a cleaner way. But most of the same information is there. Use both tidy() and glance() on pop_model below:
tidy(pop_model)
glance(pop_model)
NA
NA
R-squared is around .20, which means 20% of the variance in vote can be accounted for by population. To get the correlation, take the square root of .20, which is about .4. That’s a decent size, and it is statistically significant, so we this analysis shows support for our hypothesis that smaller the population, the greater the Republican vote advantage.
To plot the regression line onto the chart, copy-paste the code from the plotly chart above, and do the following. Don’t forget the pipe.
1. add_lines(y = ~fitted(pop_model)) That will plot the line.
2. add showlegend = F inside add_markers(). That will remove an unnecessary label given to the line.
house_election %>%
plot_ly(x = ~Population,
y = ~Repub_advantage,
hoverinfo = "text",
text = ~paste("County:",
County, "<br>",
"Population: ", Population, "<br>",
"Republican advantage: ", Repub_advantage)) %>%
add_markers(showlegend = F, marker = list(opacity = 0.7)) %>%
layout(title = "Predicting Republican Vote Advantage from Population, by County",
xaxis = list(title = "County population"),
yaxis = list(title = "Republican vote advantage")) %>%
add_lines(y = ~fitted(pop_model))
NA
NA
Predicting vote from longitude
A common belief about Montana politics is that the further West you go, the more Democrats there are, and the further East you go, the more Republicans. We can do a statistical test of this hypothesis by getting the longitude of each county and using that to predict Tester’s vote.
Figure the center of each county.
To be honest I don’t completely know what’s going on with the code below, but basically it’s getting the center of the area with st_centroid and then getting the coordinates with st_coordinates. The longitude (West-East) is the x-axis, so X, and the latitude (North-South) is the y-axis. I had to search the internet to figure out how to do it, and however it does it, it seems to work.
house_election <- house_election %>%
mutate(Longitude = as_tibble(st_coordinates(st_centroid(house_election$geometry)))$X) %>%
mutate(Latitude = as_tibble(st_coordinates(st_centroid(house_election$geometry)))$Y)
st_centroid does not give correct centroids for longitude/latitude datast_centroid does not give correct centroids for longitude/latitude data
When I run that code, I get an error saying that “st_centroid does not give correct centroids for longitude/latitude data,” so let’s check it with a quick map. This creates the map and then uses addCircleMarkers() to place dots in the centers of the counties that we found with st_centroid().
house_election %>%
leaflet() %>%
addTiles() %>%
addPolygons(weight = 1) %>%
setView(-110, 47, zoom = 6) %>%
addCircleMarkers(~Longitude, ~Latitude)
sf layer has inconsistent datum (+proj=longlat +datum=NAD83 +no_defs).
Need '+proj=longlat +datum=WGS84'
Despite the error, those dots look pretty close to the centers of each county to me.
Here’s a quick plotly graph of the Longitude and the election results. Here we’re looking for a positive correlation, because as the Longitude goes East (to the right on the graph), you should find more Republican votes.
house_election %>%
plot_ly(x = ~Longitude, y = ~Repub_advantage) %>%
add_markers()
Now let’s do the linear regression predicting Republican vote from Longitude.
longitude_lm <- lm(Repub_advantage ~ Longitude, data = house_election)
tidy(longitude_lm)
glance(longitude_lm)
The r-squared is similar to the previous: .18, giving a correlation of about .4 (actually -.4). This means that the more West you go, the more Democratic votes you get.
This next chunk puts the line on the graph, adds titles, etc.
house_election %>%
plot_ly(x = ~Longitude,
y = ~Repub_advantage,
hoverinfo = "text",
text = ~paste("County:", County, "<br>", "Longitude: ", Longitude, "<br>", "Republican advantage: ", Repub_advantage)) %>%
add_markers(marker = list(opacity = 0.7), showlegend = F) %>%
layout(title = "Predicting Republican Vote Advantage from Longitude, by County",
xaxis = list(title = "County longitude"),
yaxis = list(title = "Republican vote advantage")) %>%
add_lines(y = ~fitted(longitude_lm))
NA
NA
Multiple regression
Finally let’s do a multiple regression, predicting the Republican advantage from both Population and Longitude simultaneously.
multiple_lm <- lm(Repub_advantage ~ Population + Longitude, data = house_election)
tidy(multiple_lm)
glance(multiple_lm)
The tidy() command shows that both Population and Longitude remain statistically significant when examined simultaneously.
When we just have two variables, x & y, we can use a 2-dimensional scatterplot. So what do we do with three variables? A 3D scatterplot, of course! This is really cool, because you can spin it around to see it from different angles.
house_election %>%
plot_ly(x = ~Longitude, y = ~Population, z = ~Repub_advantage,
text = ~County, hoverinfo = "text") %>%
add_markers(opacity = .7, showlegend = F)
Assignment: 2016 US Senate Montana election: Tester/Rosendale
The Senate election may be an even better test of these two hypotheses, because the election was even closer to 50-50, but this time Tester the Democrat won.
Run through the senate election as we did above with the house election. You can get the data from the same spreadsheet, but it’s on sheet = 1.
LS0tCnRpdGxlOiAiZ3VpZGVkIE1UIGVsZWN0aW9ucyBjaGFkIHJhbmRhbGwiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KClRoZSBjaG9yb3BsZXRocy5SbWQgbm90ZWJvb2sgd2FzIGdldHRpbmcgbG9uZyBhbHJlYWR5LCBzbyBJIHNwbGl0IHRoaXMgZnJvbSBpdC4gSXQgaW50cm9kdWNlcyB0aGUgTW9udGFuYSAyMDE4IGVsZWN0aW9uIGRhdGEgd2hpY2ggd2lsbCBiZSB1c2VkIGluIHlvdXIgcHVibGlzaGVkIGFzc2lnbm1lbnQuCgpUaGVyZSBhcmUgdHdvIHRoZW9yaWVzIGFib3V0IE1vbnRhbmEgcG9saXRpY3MgdGhhdCBJIHdhbnQgdXMgdG8gdGVzdCB3aXRoIHRoZXNlIGVsZWN0aW9uIHJlc3VsdHM6ICAKMS4gVGhhdCBtb3JlIGRlbnNlbHktcG9wdWxhdGVkIGFyZWFzIGFuZCBjaXRpZXMgdm90ZSBmb3IgRGVtb2NyYXRzLCBhbmQgbW9yZSBzcGFyc2VseS1wb3B1bGF0ZWQgYW5kIHJ1cmFsIGFyZWFzIHZvdGUgZm9yIFJlcHVibGljYW5zLCBhbmQgICAKMi4gdGhhdCB0aGUgZnVydGhlciBXZXN0IHlvdSBnbyBpbiB0aGUgc3RhdGUsIHRoZSBtb3JlIERlbW9jcmF0cyB0aGVyZSBhcmUsIGFuZCB0aGUgZnVydGhlciBFYXN0LCB0aGUgbW9yZSBSZXB1YmxpY2Fucy4gIAoKSSB3aWxsIHJ1biB0aHJvdWdoIHRoZXNlIGh5cG90aGVzZXMgdXNpbmcgaGUgVVMgSG91c2UgcmFjZSB3aXRoIEdpYW5mb3J0ZSAoUikgdGhlIHdpbm5lciBhbmQgV2lsbGlhbXMgKEQpLiBZb3Ugd2lsbCBkbyB5b3VyIG93biBub3RlYm9vayB3aXRoIFRlc3RlciAoRCkgdGhlIHdpbm5lciBhbmQgUm9zZW5kYWxlIChSKS4KCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobGVhZmxldCkKbGlicmFyeShzZikKbGlicmFyeShyZWFkeGwpCmxpYnJhcnkoRFQpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KGJyb29tKQpsaWJyYXJ5KHRpZHljZW5zdXMpCmBgYAoKRmlyc3QgeW91IHdpbGwgbmVlZCB0byB1cGxvYWQgdGhlIGVsZWN0aW9uIGRhdGEuIEdvIHRvIHRoZSBNVCBzZWNyZXRhcnkgb2Ygc3RhdGUgd2Vic2l0ZSBoZXJlOiAgaHR0cDovL210ZWxlY3Rpb25yZXN1bHRzLmdvdgoKVGhlbiBnbyB0byBFbGVjdGlvbiBSZXNvdXJjZXMgYXQgdGhlIGJvdHRvbSBvZiB0aGUgcGFnZSwgYW5kIHRoZW4gU3RhdGV3aWRlIGFuZCBDb3VudHkgRXhwb3J0cywgYW5kIHRoZW4gU1RBVEVXSURFIEVYUE9SVC4KClRoZW4gdXBsb2FkIHRoZSBzcHJlYWRzaGVldCB0byBGaWxlcyBpbiBSU3R1ZGlvLiBJdCBzaG91bGQgYmUgY2FsbGVkICJTdGF0ZXdpZGUgUmVzdWx0cy54bHN4LiIKCkluIHRoaXMgc3ByZWFkaGVldCwgc2hlZXQgMiBpcyB0aGUgMjAxOCBVUyBIb3VzZSByYWNlIHRoYXQgd2UnbGwgdXNlIGhlcmUuIFNoZWV0IDEgaXMgdGhlIFNlbmF0ZSByYWNlIHRoYXQgeW91J2xsIHVzZSBmb3IgeW91ciBhbmFseXNpcyBhZnRlciB0aGlzLiAgCgpJdCdzIGFuIEV4Y2VsIGZpbGUsIHNvIHdlIHVzZSByZWFkX3hsc3goKS4gVGhpcyB3b24ndCB3b3JrIHF1aXRlIHJpZ2h0LCBidXQgd2UnbGwgZml4IGl0IG5leHQ6CmBgYHtyfQpob3VzZV9jb3VudGllcyA8LSByZWFkX3hsc3goIlN0YXRld2lkZSBSZXN1bHRzLnhsc3giLCBzaGVldCA9IDIpCmBgYAoKQ2xpY2sgb24gaG91c2VfY291bnRpZXMgaW4gdGhlIEdsb2JhbCBFbnZpcm9ubWVudCwgYW5kIHNlZSBob3cgdGhlIHNoZWV0IGlzbid0IGFsbCBkYXRhLiBUaGVyZSdzIHNvbWUgaW5mbyBpbiB0aGUgZmlyc3Qgc2V2ZXJhbCByb3dzLCBhbmQgdGhlIGxhc3Qgcm93IGhhcyBhIHRvdGFsLiBUaGUgcmFuZ2Ugb2YgdGhlIGFjdHVhbCBkYXRhIGlzIEI3OkU2My4KCmBgYHtyfQpob3VzZV9jb3VudGllcyA8LSByZWFkX3hsc3goIlN0YXRld2lkZSBSZXN1bHRzLnhsc3giLCBzaGVldCA9IDIsIHJhbmdlID0gIkI3OkU2MyIpCmBgYAoKVXNlIGdsaW1wc2UoKSB0byB2aWV3IGhvdXNlX2NvdW50aWVzLgoKYGBge3J9CmdsaW1wc2UoaG91c2VfY291bnRpZXMpCmBgYAoKCk5vdGljZSB0aGF0IHNvbWUgb2YgdGhlIGNvbHVtbiBuYW1lcyBhcmUgd2VpcmQgYW5kIGxvbmcuIExldCdzIGNoYW5nZSB0aG9zZSB3aXRoIHJlbmFtZSgpIHNvIHRoZXkncmUgZWFzaWVyIHRvIHJlZmVyZW5jZToKCmBgYHtyfQpob3VzZV9jb3VudGllcyA8LSBob3VzZV9jb3VudGllcyAlPiUgCiAgcmVuYW1lKFJlcHVibGljYW4gPSAiR1JFRyBHSUFORk9SVEVcclxuUmVwdWJsaWNhbiIpICU+JSAKICByZW5hbWUoRGVtb2NyYXQgPSAiS0FUSExFRU4gV0lMTElBTVNcclxuRGVtb2NyYXQiKSAlPiUgCiAgcmVuYW1lKExpYmVydGFyaWFuID0gIkVMSU5PUiBTV0FOU09OXHJcbkxpYmVydGFyaWFuIikKYGBgCgpOb3cgZ2xpbXBzZSgpIGFnYWluIHRvIG5ha2Ugc3VyZSB0aGF0IHdvcmtlZC4KCmBgYHtyfQpnbGltcHNlKGhvdXNlX2NvdW50aWVzKQpgYGAKV2Ugd2FudCB0byBrbm93IHRoZSBSZXB1YmxpY2FuIC0gRGVtb2NyYXQgZGlmZmVyZW5jZSBpbiBlYWNoIGNvdW50eSwgYXMgYSBwZXJjZW50YWdlLgoKVGhlIGZvbGxvd2luZyBjb2RlIGhhcyB0aHJlZSBtdXRhdGUoKSBjb21tYW5kcwoxLiBhZGQgdXAgdGhlIHRvdGFsIHZvdGVzIGNhc3QgaW4gdGhlIGNvdW50eSAgCjIuIHN1YnRyYWN0IHRoZSBwcm9wb3J0aW9uIG9mIHZvdGVzIGNhc3QgZm9yIHRoZSBEZW0gZnJvbSB0aGUgcHJvcG9ydGlvbiBvZiB2b3RlcyBjYXN0IGZvciB0aGUgUmVwdWIgIAozLiBtdWx0aXBseSBpdCBieSAxMDAgdG8gbWFrZSBpdCBhIHBlcmNlbnRhZ2UsIGFuZCByb3VuZCBpdCB0byAxIGRpZ2l0ICAKCgpgYGB7cn0KaG91c2VfY291bnRpZXMgPC0gaG91c2VfY291bnRpZXMgJT4lIAogIG11dGF0ZSh0b3RhbF92b3RlcyA9IFJlcHVibGljYW4gKyBEZW1vY3JhdCArIExpYmVydGFyaWFuKSAlPiUgCiAgbXV0YXRlKFJlcHViX2FkdmFudGFnZSA9IFJlcHVibGljYW4vdG90YWxfdm90ZXMgLSBEZW1vY3JhdC90b3RhbF92b3RlcykgJT4lIAogIG11dGF0ZShSZXB1Yl9hZHZhbnRhZ2UgPSByb3VuZChSZXB1Yl9hZHZhbnRhZ2UqMTAwLCAxKSkKCmhvdXNlX2NvdW50aWVzICU+JSAKICBhcnJhbmdlKC1SZXB1Yl9hZHZhbnRhZ2UpCgpgYGAKCgoKIyMjIEdldCBjZW5zdXMgZGF0YSBmb3IgbWFwcGluZwoKTm93IHRoYXQgd2UgaGF2ZSB0aGUgZWxlY3Rpb24gZGF0YSBwcmVwYXJlZCwgd2UgbmVlZCBjZW5zdXMgZGF0YSBmb3IgdGhlIG1hcCBvZiB0aGUgc3RhdGUgYW5kIHRoZSBwb3B1bGF0aW9ucyBpbiBlYWNoIGNvdW50eS4KCjEuIFdlIGdldCB0aGUgbWFwIGRhdGEgYnkgc2V0dGluZyBnZW9tZXRyeSA9IFRSVUUsIGFuZCAgCjIuIHdlIGdldCBwb3B1bGF0aW9uIGRhdGEgd2l0aCB2YXJpYWJsZXMgPSAiQjAxMDAzXzAwMSIKCgpgYGB7cn0KbXRfY291bnRpZXMgPC0gZ2V0X2FjcyhnZW9ncmFwaHkgPSAiY291bnR5IiwKICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZXMgPSAiQjAxMDAzXzAwMSIsCiAgICAgICAgICAgICAgICAgICAgICAgc3RhdGUgPSAiTVQiLAogICAgICAgICAgICAgICAgICAgICAgIGdlb21ldHJ5ID0gVFJVRSkgCmBgYAoKQmVjYXVzZSB3ZSdyZSBnb2luZyB0byBqb2luIHRoZSB0d28gZGF0YXNldHMsIHdlIG5lZWQgdG8gbWFrZSBzdXJlIHRoZXkncmUgY29uc2lzdGVudCB3aXRoIG9uZSBhbm90aGVyLiBPbmUgcHJvYmxlbSBpcyB0aGF0IHRoZSBuYW1lcyBvZiB0aGUgY291bnRpZXMgYXJlIGEgbGl0dGxlIGRpZmZlcmVudCBiZXR3ZWVuIHRoZSBjZW5zdXMgZGF0YSBhbmQgdGhlIGVsZWN0aW9uIGRhdGE6ICdMZXdpcyBhbmQgQ2xhcmsnIGluIHRoZSBjZW5zdXMgZGF0YSBpcyAnTGV3aXMgJiBDbGFyaycgaW4gdGhlIGVsZWN0aW9uIGRhdGEuCgpJbiBhZGRpdGlvbiwgaW4gdGhlIGNlbnN1cyBkYXRhIGFsbCB0aGUgY291bnR5IG5hbWVzIGluY2x1ZGUgdGhlIHdvcmQgQ291bnR5IGFuZCBNb250YW5hLCBsaWtlICJZZWxsb3dzdG9uZSBDb3VudHksIE1vbnRhbmEiIHdoZXJlYXMgaW4gdGhlIGVsZWN0aW9uIGRhdGEgaXQncyBjYWxsZWQgc2ltcGx5ICJZZWxsb3dzdG9uZS4iCgpUaGUgZm9sbG93aW5nIGNvZGUgY2hhbmdlcyBib3RoIG9mIHRob3NlLCBhbmQgYWxzbyBjaGFuZ2VzIHRoZSBjZW5zdXMgY29sdW1uIGNhbGxlZCBlc3RpbWF0ZSB0byBQb3B1bGF0aW9uLCB3aGljaCB3ZSB3aWxsIHVzZSBsYXRlcjoKCmBgYHtyfQoKaG91c2VfY291bnRpZXNbMjUsICJDb3VudHkiXSA8LSAiTGV3aXMgYW5kIENsYXJrIiAgICAgICAgICAgICAgIyBDaGFuZ2VzICAiJiIgImFuZCIKCm10X2NvdW50aWVzIDwtIG10X2NvdW50aWVzICU+JSAKICBtdXRhdGUoQ291bnR5ID0gZ3N1YigiIENvdW50eSwgTW9udGFuYSIsICIiLCBOQU1FKSkgJT4lICAgICAgIyBSZW1vdmVzIHVubmVjZXNzYXJ5IHdvcmRzCiAgcmVuYW1lKFBvcHVsYXRpb24gPSBlc3RpbWF0ZSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgUmVuYW1lcyB0aGUgJ2VzdGltYXRlJyB0byAnUG9wdWxhdGlvbicKYGBgCgoKVGhpcyBqb2lucyB0aGUgZWxlY3Rpb24gZGF0YSB3aXRoIHRoZSBtYXAgZGF0YSBpbnRvIGEgbmV3IGRhdGFzZXQgY2FsbGVkIGhvdXNlX2VsZWN0aW9uCgpgYGB7cn0KaG91c2VfZWxlY3Rpb24gPC0gbXRfY291bnRpZXMgJT4lIAogIGZ1bGxfam9pbihob3VzZV9jb3VudGllcykKYGBgCgoKClRoZSBmb2xsb3dpbmcgd2lsbCBtYWtlIGEgbmljZSB0YWJsZSBmb3IgZGlzcGxheWluZyByZXN1bHRzLiBUaGUgYXMudGliYmxlKCkgY29tbWFuZCByZW1vdmVzIHRoZSBnZW9tZXRyeSwgd2hpY2ggaXMgbm90IHN0YW5kYXJkIHRhYmxlIGZvcm1hdC4KCgpgYGB7cn0KaG91c2VfZWxlY3Rpb24gJT4lCiAgYXNfdGliYmxlKCkgJT4lIAogIHNlbGVjdChDb3VudHksIFBvcHVsYXRpb24sIERlbW9jcmF0LCBSZXB1YmxpY2FuLCBMaWJlcnRhcmlhbiwgdG90YWxfdm90ZXMsIFJlcHViX2FkdmFudGFnZSkgJT4lIAogIGRhdGF0YWJsZSgpCgpgYGAKCgoKCiMjIyBNb250YW5hIGNvdW50aWVzIGNob3JvcGxldGgKClRoaXMgaXMgYmFzZWQgb24gdGhlIGNvZGUgZnJvbSB0aGUgcHJldmlvdXMgbm90ZWJvb2suCgpgYGB7cn0KCnZvdGVfY29sb3JzIDwtIGNvbG9yTnVtZXJpYyhwYWxldHRlID0gInZpcmlkaXMiLCBkb21haW4gPSBob3VzZV9lbGVjdGlvbiRSZXB1Yl9hZHZhbnRhZ2UpCgpob3VzZV9lbGVjdGlvbiAlPiUKICBsZWFmbGV0KCkgJT4lIAogIGFkZFRpbGVzKCkgJT4lCiAgYWRkUG9seWdvbnMod2VpZ2h0ID0gMSwKICAgICAgICAgICAgICBmaWxsQ29sb3IgPSB+dm90ZV9jb2xvcnMoUmVwdWJfYWR2YW50YWdlKSwgCiAgICAgICAgICAgICAgbGFiZWwgPSB+cGFzdGUwKENvdW50eSwgIiwgUmVwdWJsaWNhbiBhZHZhbnRhZ2UgPSAiLCBSZXB1Yl9hZHZhbnRhZ2UpLAogICAgICAgICAgICAgIGhpZ2hsaWdodCA9IGhpZ2hsaWdodE9wdGlvbnMod2VpZ2h0ID0gMikpICU+JSAKICBzZXRWaWV3KC0xMTAsIDQ3LCB6b29tID0gNikgJT4lIAogIGFkZExlZ2VuZChwYWwgPSB2b3RlX2NvbG9ycywgdmFsdWVzID0gflJlcHViX2FkdmFudGFnZSkKCmBgYAoKCgoKCgojIyMgUHJlZGljdGluZyB2b3RlcyBmcm9tIHBvcHVsYXRpb24KCk9uZSB0aGVvcnkgaXMgdGhhdCBEZW1vY3JhdHMgZG8gYmV0dGVyIGluIG1vcmUgcG9wdWxhdGVkIGNvdW50aWVzLCBhbmQgUmVwdWJsaWNhbnMgZG8gYmV0dGVyIGluIG1vcmUgcnVyYWwgYW5kIGxlc3MgcG9wdWxhdGVkIGNvdW50aWVzLiBUaGF0J3MgdHJ1ZSBvZiBEZW1vY3JhdHMgYW5kIFJlcHVibGljYW5zIGF0IHRoZSBuYXRpb25hbCBsZXZlbCAtIERlbW9jcmF0cyB3aW4gaW4gaGlnaGx5LXBvcHVsYXRlZCBjaXRpZXMgYW5kIFJlcHVibGljYW5zIHdpbiBpbiBzcGFyc2VseS1wb3B1bGF0ZWQgcnVyYWwgYXJlYXMuIAoKTm93IGxldCdzIG1ha2UgYSBwbG90bHkgZ3JhcGguCgpgYGB7cn0KaG91c2VfZWxlY3Rpb24gJT4lCiAgcGxvdF9seSh4ID0gflBvcHVsYXRpb24sIHkgPSB+UmVwdWJfYWR2YW50YWdlKSAlPiUgCiAgYWRkX21hcmtlcnMoKQpgYGAKCgoKQ3JlYXRlIGJldHRlciBob3ZlciBpbmZvcm1hdGlvbiBhbmQgYWRkIHRpdGxlIGFuZCBiZXR0ZXIgYXhpcyBsYWJlbHMuCgoKYGBge3J9CmhvdXNlX2VsZWN0aW9uICU+JQogIHBsb3RfbHkoeCA9IH5Qb3B1bGF0aW9uLCAKICAgICAgICAgIHkgPSB+UmVwdWJfYWR2YW50YWdlLAogICAgICAgICAgaG92ZXJpbmZvID0gInRleHQiLCAKICAgICAgICAgIHRleHQgPSB+cGFzdGUoIkNvdW50eToiLCAKICAgICAgICAgICAgICAgICAgICAgICAgQ291bnR5LCAiPGJyPiIsIAogICAgICAgICAgICAgICAgICAgICAgICAiUG9wdWxhdGlvbjogIiwgUG9wdWxhdGlvbiwgIjxicj4iLCAKICAgICAgICAgICAgICAgICAgICAgICAgIlJlcHVibGljYW4gYWR2YW50YWdlOiAiLCBSZXB1Yl9hZHZhbnRhZ2UpKSAlPiUgCiAgYWRkX21hcmtlcnMobWFya2VyID0gbGlzdChvcGFjaXR5ID0gMC43KSkgJT4lCiAgbGF5b3V0KHRpdGxlID0gIlByZWRpY3RpbmcgUmVwdWJsaWNhbiBWb3RlIEFkdmFudGFnZSBmcm9tIFBvcHVsYXRpb24sIGJ5IENvdW50eSIsCiAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJDb3VudHkgcG9wdWxhdGlvbiIpLAogICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiUmVwdWJsaWNhbiB2b3RlIGFkdmFudGFnZSIpKSAgICAgCiAgICAgICAgICAgICAgICAKYGBgCgoKCgoKTGluZWFyIG1vZGVsIG9yIGxpbmVhciByZWdyZXNzaW9uICBsbSh5IH4geCwgZGF0YSkKClRvIGNyZWF0ZSB0aGUgcmVncmVzc2lvbiBtb2RlbCwgdXNlIGxtKCkgZm9yIGxpbmVhciBtb2RlbC4gQ2FsbCBpdCBwb3BfbW9kZWwgZm9yIHBvcHVsYXRpb24gbW9kZWwuICAKCgpgYGB7cn0KcG9wX21vZGVsIDwtIGxtKFJlcHViX2FkdmFudGFnZSB+IFBvcHVsYXRpb24sIGRhdGEgPSBob3VzZV9lbGVjdGlvbikKCmBgYAoKVGhlICJvbGQtZmFzaGlvbmVkIiB3YXkgdG8gZ2V0IHRoZSBzdGF0aXN0aWNzIG91dCBvZiB0aGlzIGluIFIgaXMgdG8gdXNlIHN1bW1hcnkuIFB1dCBwb3BfbW9kZWwgaW4gc3VtbWFyeSgpIGJlbG93OgoKCmBgYHtyfQpzdW1tYXJ5KHBvcF9tb2RlbCkKYGBgCgpBIG1vcmUgbW9kZXJuIGFwcHJvYWNoIGlzIHRvIHVzZSBzb21lIG9mIHRoZSBmdW5jdGlvbnMgZnJvbSB0aGUgYnJvb20gcGFja2FnZSwgdGlkeSgpIGFuZCBnbGFuY2UoKSwgYmVjYXVzZSBpdCBzZXRzIHVwIHRoZSBvdXRwdXQgaW4gYSBjbGVhbmVyIHdheS4gQnV0IG1vc3Qgb2YgdGhlIHNhbWUgaW5mb3JtYXRpb24gaXMgdGhlcmUuIFVzZSBib3RoIHRpZHkoKSBhbmQgZ2xhbmNlKCkgb24gcG9wX21vZGVsIGJlbG93OgoKYGBge3J9CnRpZHkocG9wX21vZGVsKQpnbGFuY2UocG9wX21vZGVsKQoKCmBgYAoKUi1zcXVhcmVkIGlzIGFyb3VuZCAuMjAsIHdoaWNoIG1lYW5zIDIwJSBvZiB0aGUgdmFyaWFuY2UgaW4gdm90ZSBjYW4gYmUgYWNjb3VudGVkIGZvciBieSBwb3B1bGF0aW9uLiBUbyBnZXQgdGhlIGNvcnJlbGF0aW9uLCB0YWtlIHRoZSBzcXVhcmUgcm9vdCBvZiAuMjAsIHdoaWNoIGlzIGFib3V0IC40LiBUaGF0J3MgYSBkZWNlbnQgc2l6ZSwgYW5kIGl0IGlzIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQsIHNvIHdlIHRoaXMgYW5hbHlzaXMgc2hvd3Mgc3VwcG9ydCBmb3Igb3VyIGh5cG90aGVzaXMgdGhhdCBzbWFsbGVyIHRoZSBwb3B1bGF0aW9uLCB0aGUgZ3JlYXRlciB0aGUgUmVwdWJsaWNhbiB2b3RlIGFkdmFudGFnZS4gIAoKClRvIHBsb3QgdGhlIHJlZ3Jlc3Npb24gbGluZSBvbnRvIHRoZSBjaGFydCwgY29weS1wYXN0ZSB0aGUgY29kZSBmcm9tIHRoZSBwbG90bHkgY2hhcnQgYWJvdmUsIGFuZCBkbyB0aGUgZm9sbG93aW5nLiBEb24ndCBmb3JnZXQgdGhlIHBpcGUuICAKMS4gYWRkX2xpbmVzKHkgPSB+Zml0dGVkKHBvcF9tb2RlbCkpICBUaGF0IHdpbGwgcGxvdCB0aGUgbGluZS4gIAoyLiBhZGQgc2hvd2xlZ2VuZCA9IEYgaW5zaWRlIGFkZF9tYXJrZXJzKCkuICBUaGF0IHdpbGwgcmVtb3ZlIGFuIHVubmVjZXNzYXJ5IGxhYmVsIGdpdmVuIHRvIHRoZSBsaW5lLiAgCgpgYGB7cn0KaG91c2VfZWxlY3Rpb24gJT4lCiAgcGxvdF9seSh4ID0gflBvcHVsYXRpb24sIAogICAgICAgICAgeSA9IH5SZXB1Yl9hZHZhbnRhZ2UsCiAgICAgICAgICBob3ZlcmluZm8gPSAidGV4dCIsIAogICAgICAgICAgdGV4dCA9IH5wYXN0ZSgiQ291bnR5OiIsIAogICAgICAgICAgICAgICAgICAgICAgICBDb3VudHksICI8YnI+IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICJQb3B1bGF0aW9uOiAiLCBQb3B1bGF0aW9uLCAiPGJyPiIsIAogICAgICAgICAgICAgICAgICAgICAgICAiUmVwdWJsaWNhbiBhZHZhbnRhZ2U6ICIsIFJlcHViX2FkdmFudGFnZSkpICU+JSAKICBhZGRfbWFya2VycyhzaG93bGVnZW5kID0gRiwgbWFya2VyID0gbGlzdChvcGFjaXR5ID0gMC43KSkgJT4lCiAgbGF5b3V0KHRpdGxlID0gIlByZWRpY3RpbmcgUmVwdWJsaWNhbiBWb3RlIEFkdmFudGFnZSBmcm9tIFBvcHVsYXRpb24sIGJ5IENvdW50eSIsCiAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJDb3VudHkgcG9wdWxhdGlvbiIpLAogICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiUmVwdWJsaWNhbiB2b3RlIGFkdmFudGFnZSIpKSAlPiUgCiAgICBhZGRfbGluZXMoeSA9IH5maXR0ZWQocG9wX21vZGVsKSkKICAgIAogICAgICAgICAgICAgICAgCmBgYAoKCgoKCgoKIyMjIFByZWRpY3Rpbmcgdm90ZSBmcm9tIGxvbmdpdHVkZQoKQSBjb21tb24gYmVsaWVmIGFib3V0IE1vbnRhbmEgcG9saXRpY3MgaXMgdGhhdCB0aGUgZnVydGhlciBXZXN0IHlvdSBnbywgdGhlIG1vcmUgRGVtb2NyYXRzIHRoZXJlIGFyZSwgYW5kIHRoZSBmdXJ0aGVyIEVhc3QgeW91IGdvLCB0aGUgbW9yZSBSZXB1YmxpY2Fucy4gV2UgY2FuIGRvIGEgc3RhdGlzdGljYWwgdGVzdCBvZiB0aGlzIGh5cG90aGVzaXMgYnkgZ2V0dGluZyB0aGUgbG9uZ2l0dWRlIG9mIGVhY2ggY291bnR5IGFuZCB1c2luZyB0aGF0IHRvIHByZWRpY3QgVGVzdGVyJ3Mgdm90ZS4gCgoKRmlndXJlIHRoZSBjZW50ZXIgb2YgZWFjaCBjb3VudHkuCgpUbyBiZSBob25lc3QgSSBkb24ndCBjb21wbGV0ZWx5IGtub3cgd2hhdCdzIGdvaW5nIG9uIHdpdGggdGhlIGNvZGUgYmVsb3csIGJ1dCBiYXNpY2FsbHkgaXQncyBnZXR0aW5nIHRoZSBjZW50ZXIgb2YgdGhlIGFyZWEgd2l0aCBzdF9jZW50cm9pZCBhbmQgdGhlbiBnZXR0aW5nIHRoZSBjb29yZGluYXRlcyB3aXRoIHN0X2Nvb3JkaW5hdGVzLiBUaGUgbG9uZ2l0dWRlIChXZXN0LUVhc3QpIGlzIHRoZSB4LWF4aXMsIHNvIFgsIGFuZCB0aGUgbGF0aXR1ZGUgKE5vcnRoLVNvdXRoKSBpcyB0aGUgeS1heGlzLiBJIGhhZCB0byBzZWFyY2ggdGhlIGludGVybmV0IHRvIGZpZ3VyZSBvdXQgaG93IHRvIGRvIGl0LCBhbmQgaG93ZXZlciBpdCBkb2VzIGl0LCBpdCBzZWVtcyB0byB3b3JrLgoKYGBge3J9CmhvdXNlX2VsZWN0aW9uIDwtIGhvdXNlX2VsZWN0aW9uICU+JSAKICBtdXRhdGUoTG9uZ2l0dWRlID0gYXNfdGliYmxlKHN0X2Nvb3JkaW5hdGVzKHN0X2NlbnRyb2lkKGhvdXNlX2VsZWN0aW9uJGdlb21ldHJ5KSkpJFgpICU+JSAKICBtdXRhdGUoTGF0aXR1ZGUgPSBhc190aWJibGUoc3RfY29vcmRpbmF0ZXMoc3RfY2VudHJvaWQoaG91c2VfZWxlY3Rpb24kZ2VvbWV0cnkpKSkkWSkKICAKYGBgCgpXaGVuIEkgcnVuIHRoYXQgY29kZSwgSSBnZXQgYW4gZXJyb3Igc2F5aW5nIHRoYXQgInN0X2NlbnRyb2lkIGRvZXMgbm90IGdpdmUgY29ycmVjdCBjZW50cm9pZHMgZm9yIGxvbmdpdHVkZS9sYXRpdHVkZSBkYXRhLCIgc28gbGV0J3MgY2hlY2sgaXQgd2l0aCBhIHF1aWNrIG1hcC4gVGhpcyBjcmVhdGVzIHRoZSBtYXAgYW5kIHRoZW4gdXNlcyBhZGRDaXJjbGVNYXJrZXJzKCkgdG8gcGxhY2UgZG90cyBpbiB0aGUgY2VudGVycyBvZiB0aGUgY291bnRpZXMgdGhhdCB3ZSBmb3VuZCB3aXRoIHN0X2NlbnRyb2lkKCkuCgpgYGB7cn0KCmhvdXNlX2VsZWN0aW9uICU+JQogIGxlYWZsZXQoKSAlPiUgCiAgYWRkVGlsZXMoKSAlPiUKICBhZGRQb2x5Z29ucyh3ZWlnaHQgPSAxKSAlPiUgCiAgc2V0VmlldygtMTEwLCA0Nywgem9vbSA9IDYpICU+JSAKYWRkQ2lyY2xlTWFya2Vycyh+TG9uZ2l0dWRlLCB+TGF0aXR1ZGUpCgpgYGAKCkRlc3BpdGUgdGhlIGVycm9yLCB0aG9zZSBkb3RzIGxvb2sgcHJldHR5IGNsb3NlIHRvIHRoZSBjZW50ZXJzIG9mIGVhY2ggY291bnR5IHRvIG1lLiAKCkhlcmUncyBhIHF1aWNrIHBsb3RseSBncmFwaCBvZiB0aGUgTG9uZ2l0dWRlIGFuZCB0aGUgZWxlY3Rpb24gcmVzdWx0cy4gSGVyZSB3ZSdyZSBsb29raW5nIGZvciBhIHBvc2l0aXZlIGNvcnJlbGF0aW9uLCBiZWNhdXNlIGFzIHRoZSBMb25naXR1ZGUgZ29lcyBFYXN0ICh0byB0aGUgcmlnaHQgb24gdGhlIGdyYXBoKSwgeW91IHNob3VsZCBmaW5kIG1vcmUgUmVwdWJsaWNhbiB2b3Rlcy4KCgpgYGB7cn0KaG91c2VfZWxlY3Rpb24gJT4lCiAgcGxvdF9seSh4ID0gfkxvbmdpdHVkZSwgeSA9IH5SZXB1Yl9hZHZhbnRhZ2UpICU+JSAKICBhZGRfbWFya2VycygpCmBgYAoKCk5vdyBsZXQncyBkbyB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gcHJlZGljdGluZyBSZXB1YmxpY2FuIHZvdGUgZnJvbSBMb25naXR1ZGUuICAKCmBgYHtyfQpsb25naXR1ZGVfbG0gPC0gbG0oUmVwdWJfYWR2YW50YWdlIH4gTG9uZ2l0dWRlLCBkYXRhID0gaG91c2VfZWxlY3Rpb24pCnRpZHkobG9uZ2l0dWRlX2xtKQpnbGFuY2UobG9uZ2l0dWRlX2xtKQpgYGAKCgpUaGUgci1zcXVhcmVkIGlzIHNpbWlsYXIgdG8gdGhlIHByZXZpb3VzOiAuMTgsIGdpdmluZyBhIGNvcnJlbGF0aW9uIG9mIGFib3V0IC40IChhY3R1YWxseSAtLjQpLiBUaGlzIG1lYW5zIHRoYXQgdGhlIG1vcmUgV2VzdCB5b3UgZ28sIHRoZSBtb3JlIERlbW9jcmF0aWMgdm90ZXMgeW91IGdldC4KClRoaXMgbmV4dCBjaHVuayBwdXRzIHRoZSBsaW5lIG9uIHRoZSBncmFwaCwgYWRkcyB0aXRsZXMsIGV0Yy4KCgpgYGB7cn0KaG91c2VfZWxlY3Rpb24gJT4lIAogIHBsb3RfbHkoeCA9IH5Mb25naXR1ZGUsIAogICAgICAgICAgeSA9IH5SZXB1Yl9hZHZhbnRhZ2UsCiAgICAgICAgICBob3ZlcmluZm8gPSAidGV4dCIsIAogICAgICAgICAgdGV4dCA9IH5wYXN0ZSgiQ291bnR5OiIsIENvdW50eSwgIjxicj4iLCAiTG9uZ2l0dWRlOiAiLCBMb25naXR1ZGUsICI8YnI+IiwgIlJlcHVibGljYW4gYWR2YW50YWdlOiAiLCBSZXB1Yl9hZHZhbnRhZ2UpKSAlPiUgCiAgYWRkX21hcmtlcnMobWFya2VyID0gbGlzdChvcGFjaXR5ID0gMC43KSwgc2hvd2xlZ2VuZCA9IEYpICU+JQogIGxheW91dCh0aXRsZSA9ICJQcmVkaWN0aW5nIFJlcHVibGljYW4gVm90ZSBBZHZhbnRhZ2UgZnJvbSBMb25naXR1ZGUsIGJ5IENvdW50eSIsCiAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJDb3VudHkgbG9uZ2l0dWRlIiksCiAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJSZXB1YmxpY2FuIHZvdGUgYWR2YW50YWdlIikpICU+JSAKICBhZGRfbGluZXMoeSA9IH5maXR0ZWQobG9uZ2l0dWRlX2xtKSkKICAgICAgICAgICAgICAgIAoKYGBgCgoKCiMjIyBNdWx0aXBsZSByZWdyZXNzaW9uCgpGaW5hbGx5IGxldCdzIGRvIGEgbXVsdGlwbGUgcmVncmVzc2lvbiwgcHJlZGljdGluZyB0aGUgUmVwdWJsaWNhbiBhZHZhbnRhZ2UgZnJvbSBib3RoIFBvcHVsYXRpb24gYW5kIExvbmdpdHVkZSBzaW11bHRhbmVvdXNseS4KCgpgYGB7cn0KbXVsdGlwbGVfbG0gPC0gbG0oUmVwdWJfYWR2YW50YWdlIH4gUG9wdWxhdGlvbiArIExvbmdpdHVkZSwgZGF0YSA9IGhvdXNlX2VsZWN0aW9uKQp0aWR5KG11bHRpcGxlX2xtKQpnbGFuY2UobXVsdGlwbGVfbG0pCmBgYAoKVGhlIHRpZHkoKSBjb21tYW5kIHNob3dzIHRoYXQgYm90aCBQb3B1bGF0aW9uIGFuZCBMb25naXR1ZGUgcmVtYWluIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgd2hlbiBleGFtaW5lZCBzaW11bHRhbmVvdXNseS4KCldoZW4gd2UganVzdCBoYXZlIHR3byB2YXJpYWJsZXMsIHggJiB5LCB3ZSBjYW4gdXNlIGEgMi1kaW1lbnNpb25hbCBzY2F0dGVycGxvdC4gU28gd2hhdCBkbyB3ZSBkbyB3aXRoIHRocmVlIHZhcmlhYmxlcz8gQSAzRCBzY2F0dGVycGxvdCwgb2YgY291cnNlISBUaGlzIGlzIHJlYWxseSBjb29sLCBiZWNhdXNlIHlvdSBjYW4gc3BpbiBpdCBhcm91bmQgdG8gc2VlIGl0IGZyb20gZGlmZmVyZW50IGFuZ2xlcy4KCmBgYHtyfQpob3VzZV9lbGVjdGlvbiAlPiUgCiAgcGxvdF9seSh4ID0gfkxvbmdpdHVkZSwgeSA9IH5Qb3B1bGF0aW9uLCB6ID0gflJlcHViX2FkdmFudGFnZSwgCiAgICAgICAgICB0ZXh0ID0gfkNvdW50eSwgaG92ZXJpbmZvID0gInRleHQiKSAlPiUgCiAgYWRkX21hcmtlcnMob3BhY2l0eSA9IC43LCBzaG93bGVnZW5kID0gRikKYGBgCgoKCgoKQXNzaWdubWVudDogMjAxNiBVUyBTZW5hdGUgTW9udGFuYSBlbGVjdGlvbjogVGVzdGVyL1Jvc2VuZGFsZQoKVGhlIFNlbmF0ZSBlbGVjdGlvbiBtYXkgYmUgYW4gZXZlbiBiZXR0ZXIgdGVzdCBvZiB0aGVzZSB0d28gaHlwb3RoZXNlcywgYmVjYXVzZSB0aGUgZWxlY3Rpb24gd2FzIGV2ZW4gY2xvc2VyIHRvIDUwLTUwLCBidXQgdGhpcyB0aW1lIFRlc3RlciB0aGUgRGVtb2NyYXQgd29uLgoKUnVuIHRocm91Z2ggdGhlIHNlbmF0ZSBlbGVjdGlvbiBhcyB3ZSBkaWQgYWJvdmUgd2l0aCB0aGUgaG91c2UgZWxlY3Rpb24uIFlvdSBjYW4gZ2V0IHRoZSBkYXRhIGZyb20gdGhlIHNhbWUgc3ByZWFkc2hlZXQsIGJ1dCBpdCdzIG9uIHNoZWV0ID0gMS4KCg==