1. Drawing a Map of the US
1.1
If you look at the structure of the statesMap data frame using the str function, you should see that there are 6 variables. One of the variables, group, defines the different shapes or polygons on the map. Sometimes a state may have multiple groups, for example, if it includes islands. How many different groups are there?
statesMap = map_data('state')
table(statesMap$group) %>% length
[1] 63
### 方法二
statesMap = map_data("state")
str(statesMap)
'data.frame': 15537 obs. of 6 variables:
$ long : num -87.5 -87.5 -87.5 -87.5 -87.6 ...
$ lat : num 30.4 30.4 30.4 30.3 30.3 ...
$ group : num 1 1 1 1 1 1 1 1 1 1 ...
$ order : int 1 2 3 4 5 6 7 8 9 10 ...
$ region : chr "alabama" "alabama" "alabama" "alabama" ...
$ subregion: chr NA NA NA NA ...
head(statesMap)
table(statesMap$group) # 63
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
202 149 312 516 79 91 94 10 872 381 233 329 257 256 113 397 650
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
399 566 36 220 30 460 370 373 382 315 238 208 70 125 205 78 16
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
290 21 168 37 733 12 105 238 284 236 172 66 304 166 289 1088 59
52 53 54 55 56 57 58 59 60 61 62 63
129 96 15 623 17 17 19 44 448 373 388 68
length(table(statesMap$group))
[1] 63
1.2
You can draw a map of the United States by typing the following in your R console:
ggplot(statesMap, aes(x=long, y=lat, group=group)) +
geom_polygon(fill="white", color="black")

We specified two colors in geom_polygon – fill and color. Which one defined the color of the outline of the states?
- fill: 行政區內所填充之顏色
- color: 行政區界線之顏色(O)
2 Coloring the States by Predictions
2.1 Predictive Model
Now, let’s color the map of the US according to our 2012 US presidential election predictions from the Unit 3 Recitation. We’ll rebuild the model here, using the dataset PollingImputed.csv. Be sure to use this file so that you don’t have to redo the imputation to fill in the missing values, like we did in the Unit 3 Recitation.
Load the data using the read.csv function, and call it “polling”. Then split the data using the subset function into a training set called “Train” that has observations from 2004 and 2008, and a testing set called “Test” that has observations from 2012.
Note that we only have 45 states in our testing set, since we are missing observations for Alaska, Delaware, Alabama, Wyoming, and Vermont, so these states will not appear colored in our map.
Then, create a logistic regression model and make predictions on the test set using the following commands:
polling = read.csv('data/PollingImputed.csv')
trn = subset(polling, Year != 2012)
tst = subset(polling, Year == 2012)
mod2 = glm(Republican~SurveyUSA+DiffCount, trn, family=binomial)
pred = predict(mod2,tst,type='response')
repub = as.numeric(pred > 0.5)
df = data.frame(pred, repub, state=tst$State)
head(df)
For how many states is our binary prediction 1 (for 2012), corresponding to Republican?
sum(repub)
[1] 22
What is the average predicted probability of our model (on the Test set, for 2012)?
mean(pred)
[1] 0.4853
### 方法二
PollingImputed <- read.csv("Data/PollingImputed.csv")
TR <- subset(PollingImputed, Year == 2004 | Year == 2008)
TS <- subset(PollingImputed, Year == 2012)
str(TS)
'data.frame': 45 obs. of 7 variables:
$ State : Factor w/ 50 levels "Alabama","Alaska",..: 3 4 5 6 7 9 10 11 12 13 ...
$ Year : int 2012 2012 2012 2012 2012 2012 2012 2012 2012 2012 ...
$ Rasmussen : int 8 13 -12 3 -7 2 5 -22 31 -22 ...
$ SurveyUSA : int 5 21 -14 -2 -13 0 8 -24 24 -16 ...
$ DiffCount : int 4 2 -6 -5 -8 6 4 -2 1 -5 ...
$ PropR : num 0.833 1 0 0.308 0 ...
$ Republican: int 1 1 0 0 0 0 1 0 1 0 ...
mod2 = glm(Republican~SurveyUSA+DiffCount, data=TR, family="binomial")
TestPrediction = predict(mod2, newdata=TS, type="response")
TestPredictionBinary = as.numeric(TestPrediction > 0.5)
predictionDataFrame = data.frame(TestPrediction, TestPredictionBinary, TS$State)
str(predictionDataFrame)
'data.frame': 45 obs. of 3 variables:
$ TestPrediction : num 0.9739028 0.9994949 0.0000926 0.009433 0.0000343 ...
$ TestPredictionBinary: num 1 1 0 0 0 1 1 0 1 0 ...
$ TS.State : Factor w/ 50 levels "Alabama","Alaska",..: 3 4 5 6 7 9 10 11 12 13 ...
summary(predictionDataFrame) # Mean = 0.4852626
TestPrediction TestPredictionBinary TS.State
Min. :0.0000 Min. :0.000 Arizona : 1
1st Qu.:0.0001 1st Qu.:0.000 Arkansas : 1
Median :0.0649 Median :0.000 California : 1
Mean :0.4853 Mean :0.489 Colorado : 1
3rd Qu.:0.9986 3rd Qu.:1.000 Connecticut: 1
Max. :0.9999 Max. :1.000 Florida : 1
(Other) :39
table(predictionDataFrame$TestPredictionBinary) # 22
0 1
23 22
2.2 Merge Data into Map
Now, we need to merge “predictionDataFrame” with the map data “statesMap”, like we did in lecture. Before doing so, we need to convert the Test.State variable to lowercase, so that it matches the region variable in statesMap. Do this by typing the following in your R console:
df$region = tolower(df$state)
pmap = merge(statesMap, df, by='region')
How many observations are there in predictionMap?
nrow(pmap) # 15034
[1] 15034
How many observations are there in stateMap?
nrow(statesMap) # 15537
[1] 15537
### 方法二
predictionDataFrame$region = tolower(predictionDataFrame$TS.State)
predictionMap = merge(statesMap, predictionDataFrame, by = "region")
predictionMap = predictionMap[order(predictionMap$order),]
str(predictionMap) # 15034
'data.frame': 15034 obs. of 9 variables:
$ region : chr "arizona" "arizona" "arizona" "arizona" ...
$ long : num -115 -115 -115 -115 -115 ...
$ lat : num 35 35.1 35.1 35.2 35.2 ...
$ group : num 2 2 2 2 2 2 2 2 2 2 ...
$ order : int 204 205 206 207 208 209 210 211 212 213 ...
$ subregion : chr NA NA NA NA ...
$ TestPrediction : num 0.974 0.974 0.974 0.974 0.974 ...
$ TestPredictionBinary: num 1 1 1 1 1 1 1 1 1 1 ...
$ TS.State : Factor w/ 50 levels "Alabama","Alaska",..: 3 3 3 3 3 3 3 3 3 3 ...
str(statesMap) # 15537
'data.frame': 15537 obs. of 6 variables:
$ long : num -87.5 -87.5 -87.5 -87.5 -87.6 ...
$ lat : num 30.4 30.4 30.4 30.3 30.3 ...
$ group : num 1 1 1 1 1 1 1 1 1 1 ...
$ order : int 1 2 3 4 5 6 7 8 9 10 ...
$ region : chr "alabama" "alabama" "alabama" "alabama" ...
$ subregion: chr NA NA NA NA ...
2.3 The Rule of merge()
When we merged the data in the previous problem, it caused the number of observations to change. Why? Check out the help page for merge by typing ?merge to help you answer this question.
- Because we only make predictions for 45 states, we no longer have observations for some of the states. These observations were removed in the merging process.
2.4 Plot the color map
Now we are ready to color the US map with our predictions! You can color the states according to our binary predictions by typing the following in your R console:
pmap = pmap[order(pmap$group, pmap$order) , ]
ggplot(pmap, aes(x=long, y=lat, group=group, fill=repub)) +
geom_polygon(color='black')

### 方法二
ggplot(predictionMap, aes(x = long, y = lat, group = group, fill = TestPredictionBinary)) + geom_polygon(color = "black")

str(PollingImputed) # (1) Light blue
'data.frame': 145 obs. of 7 variables:
$ State : Factor w/ 50 levels "Alabama","Alaska",..: 1 1 2 2 3 3 3 4 4 4 ...
$ Year : int 2004 2008 2004 2008 2004 2008 2012 2004 2008 2012 ...
$ Rasmussen : int 11 21 19 16 5 5 8 7 10 13 ...
$ SurveyUSA : int 18 25 21 18 15 3 5 5 7 21 ...
$ DiffCount : int 5 5 1 6 8 9 4 8 5 2 ...
$ PropR : num 1 1 1 1 1 ...
$ Republican: int 1 1 1 1 1 1 1 1 1 1 ...
# Republican欄位的1,即表示為Republican;若為0,則為Democrat
The states appear light blue and dark blue in this map. Which color represents a Republican prediction?
2.5
We see that the legend displays a blue gradient for outcomes between 0 and 1. However, when plotting the binary predictions there are only two possible outcomes: 0 or 1. Let’s replot the map with discrete outcomes. We can also change the color scheme to blue and red, to match the blue color associated with the Democratic Party in the US and the red color associated with the Republican Party in the US. This can be done with the following command:
Alternatively, we could plot the probabilities instead of the binary predictions. Change the plot command above to instead color the states by the variable TestPrediction.
ggplot(pmap, aes(x=long, y=lat, group=group, fill=pred)) +
geom_polygon(color='black') +
scale_fill_gradient(
low="blue", high="red",
guide="legend", breaks= c(0,1),
labels=c("Democrat", "Republican"), name="Prediction 2012")

# scale: 顏色
### 方法二
ggplot(predictionMap, aes(x = long, y = lat, group = group, fill = TestPredictionBinary)) + geom_polygon(color = "black") + scale_fill_gradient(low = "blue", high = "red", guide = "legend", breaks= c(0,1), labels = c("Democrat", "Republican"), name = "Prediction 2012")

ggplot(predictionMap, aes(x = long, y = lat, group = group, fill = TestPrediction)) + geom_polygon(color = "black") + scale_fill_gradient(low = "blue", high = "red", guide = "legend", breaks= c(0,1), labels = c("Democrat", "Republican"), name = "Prediction 2012")

# (1) The two maps look very similar. This is because most of our predicted probabilities are close to 0 or close to 1.
# 輸入後兩張圖長得差不多
# TestPredictionBinary是Discrete,僅有「是 (1, Republican)」、「否 (0, Democrat)」的2種結果
# TestPrediction則是Continous,機率0-1,故結果從0-1之間的任何機率都有可能
# 二者差異不大的原因是:由於數值的範圍都是(0, 1),所以當如果該地區的政治立場極為分明時,結果便會偏向0或是1,這樣得出的結果便會與原先Binary的結果差異不大
# 唯一可能會出現顏色差異大的情況是:當整個美國所有地區的政治立場皆不鮮明時,會導致機率接近0.5,這時候整張圖會偏向機率=0.5時所出現所fill的紫色
You should see a gradient of colors ranging from red to blue. Do the colors of the states in the map for TestPrediction look different from the colors of the states in the map with TestPredictionBinary? Why or why not?
- 顏色在TR、TS之間有一點點不一樣 +輸入後兩張圖長得差不多
- TestPredictionBinary是Discrete,僅有「是 (1, Republican)」、「否 (0, Democrat)」的2種結果
- TestPrediction則是Continous,機率0-1,故結果從0-1之間的任何機率都有可能
- 二者差異不大的原因是:由於數值的範圍都是(0, 1),所以當如果該地區的政治立場極為分明時,結果便會偏向0或是1,這樣得出的結果便會與原先Binary的結果差異不大
- 唯一可能會出現顏色差異大的情況是:當整個美國所有地區的政治立場皆不鮮明時,整張圖會偏向機率=0.5時所出現所fill的紫色
3. Understanding the Predictions
3.1
In the 2012 election, the state of Florida ended up being a very close race. It was ultimately won by the Democratic party.
df$pred[ df$state == 'Florida'] # 0.96404
[1] 0.964
Did we predict this state correctly or incorrectly?
- We incorrectly predicted this state by predicting that it would be won by the Republican party.
3.2
What was our predicted probability for the state of Florida?
df$pred[ df$state == 'Florida'] # 0.96404
[1] 0.964
### 方法二
ggplot(predictionMap, aes(x = long, y = lat, group = group, fill = TestPrediction)) + geom_polygon(color = "black") + scale_fill_gradient(low = "blue", high = "red", guide = "legend", breaks= c(0,1), labels = c("Democrat", "Republican"), name = "Prediction 2012")

table(PollingImputed$State, PollingImputed$Republican)
0 1
Alabama 0 2
Alaska 0 2
Arizona 0 3
Arkansas 0 3
California 3 0
Colorado 2 1
Connecticut 3 0
Delaware 2 0
Florida 2 1
Georgia 0 3
Hawaii 3 0
Idaho 0 3
Illinois 3 0
Indiana 1 2
Iowa 2 1
Kansas 0 3
Kentucky 0 3
Louisiana 0 3
Maine 3 0
Maryland 3 0
Massachusetts 3 0
Michigan 3 0
Minnesota 3 0
Mississippi 0 3
Missouri 0 3
Montana 0 3
Nebraska 0 3
Nevada 2 1
New Hampshire 3 0
New Jersey 3 0
New Mexico 2 1
New York 3 0
North Carolina 1 2
North Dakota 0 3
Ohio 2 1
Oklahoma 0 3
Oregon 3 0
Pennsylvania 3 0
Rhode Island 3 0
South Carolina 0 3
South Dakota 0 3
Tennessee 0 3
Texas 0 3
Utah 0 3
Vermont 2 0
Virginia 2 1
Washington 3 0
West Virginia 0 3
Wisconsin 3 0
Wyoming 0 2
predictionDataFrame # 9.640395e-01 = 0.9640395
### (4) Our prediction model did not do a very good job of correctly predicting the state of Florida, and we were very confident in our incorrect prediction.
What does this imply?
- Our prediction model did not do a very good job of correctly predicting the state of Florida, and we were very confident in our incorrect prediction.
4. Parameter Settings
In this part, we’ll explore what the different parameter settings of geom_polygon do. Throughout the problem, use the help page for geom_polygon, which can be accessed by ?geom_polygon. To see more information about a certain parameter, just type a question mark and then the parameter name to get the help page for that parameter. Experiment with different parameter settings to try and replicate the plots!
We’ll be asking questions about the following three plots:
grad = scale_fill_gradient(
low="blue", high="red",
guide="legend", breaks= c(0,1),
labels=c("Democrat", "Republican"), name="Prediction 2012")
ggplot(pmap, aes(x=long, y=lat, group=group, fill=repub)) + grad +
geom_polygon(color='black',linetype=3,size=1) + ggtitle("Plot(1)")

ggplot(pmap, aes(x=long, y=lat, group=group, fill=repub)) + grad +
geom_polygon(color='black',linetype=1,size=3) + ggtitle("Plot(2)")

ggplot(pmap, aes(x=long, y=lat, group=group, fill=repub)) + grad +
geom_polygon(color='black',linetype=1,size=1,alpha=0.3) + ggtitle("Plot(3)")

4.1
Plots (1) and (2) were created by changing different parameters of geom_polygon from their default values.
### linetype
ggplot(predictionMap, aes(x = long, y = lat, group = group, fill = TestPrediction))+ geom_polygon(color = "black", linetype=3) + scale_fill_gradient(low = "blue", high = "red", guide = "legend", breaks= c(0,1), labels = c("Democrat", "Republican"), name = "Prediction 2012")

### Plot(1): 虛線(linetype = 2)
What is the name of the parameter we changed to create plot (1)?
What is the name of the parameter we changed to create plot (2)?
4.2
Plot (3) was created by changing the value of a different geom_polygon parameter to have value 0.3. Which parameter did we use?
### alpha
ggplot(predictionMap, aes(x = long, y = lat, group = group, fill = TestPrediction))+ geom_polygon(color = "black", alpha=0.3) + scale_fill_gradient(low = "blue", high = "red", guide = "legend", breaks= c(0,1), labels = c("Democrat", "Republican"), name = "Prediction 2012")

?alpha
### alpha: Modify colour transparency. Vectorised in both colour and alpha.
LS0tCnRpdGxlOiAiQVM3LTEg576O5ZyL57i957Wx5aSn6YG45Zyw5ZyWIgphdXRob3I6ICJNMDY0NjEwMDIxIOaliuWHseWAqywgMjAxOC8wOC8wMyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKPGJyPgoKKirkuLvopoHorbDpoYzvvJrooYzmlL/ljYDnlYzlpZflnJYqKgoKCmBgYHtyIGVjaG89VCwgbWVzc2FnZT1GLCBjYWNoZT1GLCB3YXJuaW5nPUZ9ClN5cy5zZXRsb2NhbGUoJ0xDX0FMTCcsJ0MnKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkobWFwcykKbGlicmFyeShnZ21hcCkKbGlicmFyeShjYVRvb2xzKQpgYGAKCjxicj48aHI+CgojIyMgMS4gRHJhd2luZyBhIE1hcCBvZiB0aGUgVVMKCiMjIyMjIDEuMSAKSWYgeW91IGxvb2sgYXQgdGhlIHN0cnVjdHVyZSBvZiB0aGUgc3RhdGVzTWFwIGRhdGEgZnJhbWUgdXNpbmcgdGhlIHN0ciBmdW5jdGlvbiwgeW91IHNob3VsZCBzZWUgdGhhdCB0aGVyZSBhcmUgNiB2YXJpYWJsZXMuIE9uZSBvZiB0aGUgdmFyaWFibGVzLCBncm91cCwgZGVmaW5lcyB0aGUgZGlmZmVyZW50IHNoYXBlcyBvciBwb2x5Z29ucyBvbiB0aGUgbWFwLiBTb21ldGltZXMgYSBzdGF0ZSBtYXkgaGF2ZSBtdWx0aXBsZSBncm91cHMsIGZvciBleGFtcGxlLCBpZiBpdCBpbmNsdWRlcyBpc2xhbmRzLiBfSG93IG1hbnkgZGlmZmVyZW50IGdyb3VwcyBhcmUgdGhlcmU/XwoKYGBge3J9CnN0YXRlc01hcCA9IG1hcF9kYXRhKCdzdGF0ZScpCnRhYmxlKHN0YXRlc01hcCRncm91cCkgJT4lIGxlbmd0aApgYGAKCmBgYHtyfQojIyMg5pa55rOV5LqMCnN0YXRlc01hcCA9IG1hcF9kYXRhKCJzdGF0ZSIpCnN0cihzdGF0ZXNNYXApCmhlYWQoc3RhdGVzTWFwKQp0YWJsZShzdGF0ZXNNYXAkZ3JvdXApICAgIyA2MwpsZW5ndGgodGFibGUoc3RhdGVzTWFwJGdyb3VwKSkKYGBgCgojIyMjIyAxLjIKWW91IGNhbiBkcmF3IGEgbWFwIG9mIHRoZSBVbml0ZWQgU3RhdGVzIGJ5IHR5cGluZyB0aGUgZm9sbG93aW5nIGluIHlvdXIgUiBjb25zb2xlOgpgYGB7cn0KZ2dwbG90KHN0YXRlc01hcCwgYWVzKHg9bG9uZywgeT1sYXQsIGdyb3VwPWdyb3VwKSkgKyAKICBnZW9tX3BvbHlnb24oZmlsbD0id2hpdGUiLCBjb2xvcj0iYmxhY2siKQpgYGAKV2Ugc3BlY2lmaWVkIHR3byBjb2xvcnMgaW4gZ2VvbV9wb2x5Z29uIC0tIGBmaWxsYCBhbmQgYGNvbG9yYC4gX1doaWNoIG9uZSBkZWZpbmVkIHRoZSBjb2xvciBvZiB0aGUgb3V0bGluZSBvZiB0aGUgc3RhdGVzP18KCisgZmlsbDog6KGM5pS/5Y2A5YWn5omA5aGr5YWF5LmL6aGP6ImyCisgY29sb3I6IOihjOaUv+WNgOeVjOe3muS5i+mhj+iJsijvvK8pCgo8YnI+PGhyPgoKIyMjIDIgQ29sb3JpbmcgdGhlIFN0YXRlcyBieSBQcmVkaWN0aW9ucwoKIyMjIyMgMi4xIFByZWRpY3RpdmUgTW9kZWwKCk5vdywgbGV0J3MgY29sb3IgdGhlIG1hcCBvZiB0aGUgVVMgYWNjb3JkaW5nIHRvIG91ciAyMDEyIFVTIHByZXNpZGVudGlhbCBlbGVjdGlvbiBwcmVkaWN0aW9ucyBmcm9tIHRoZSBVbml0IDMgUmVjaXRhdGlvbi4gV2UnbGwgcmVidWlsZCB0aGUgbW9kZWwgaGVyZSwgdXNpbmcgdGhlIGRhdGFzZXQgUG9sbGluZ0ltcHV0ZWQuY3N2LiBCZSBzdXJlIHRvIHVzZSB0aGlzIGZpbGUgc28gdGhhdCB5b3UgZG9uJ3QgaGF2ZSB0byByZWRvIHRoZSBpbXB1dGF0aW9uIHRvIGZpbGwgaW4gdGhlIG1pc3NpbmcgdmFsdWVzLCBsaWtlIHdlIGRpZCBpbiB0aGUgVW5pdCAzIFJlY2l0YXRpb24uCgpMb2FkIHRoZSBkYXRhIHVzaW5nIHRoZSByZWFkLmNzdiBmdW5jdGlvbiwgYW5kIGNhbGwgaXQgInBvbGxpbmciLiBUaGVuIHNwbGl0IHRoZSBkYXRhIHVzaW5nIHRoZSBzdWJzZXQgZnVuY3Rpb24gaW50byBhIHRyYWluaW5nIHNldCBjYWxsZWQgIlRyYWluIiB0aGF0IGhhcyBvYnNlcnZhdGlvbnMgZnJvbSAyMDA0IGFuZCAyMDA4LCBhbmQgYSB0ZXN0aW5nIHNldCBjYWxsZWQgIlRlc3QiIHRoYXQgaGFzIG9ic2VydmF0aW9ucyBmcm9tIDIwMTIuCgpOb3RlIHRoYXQgd2Ugb25seSBoYXZlIDQ1IHN0YXRlcyBpbiBvdXIgdGVzdGluZyBzZXQsIHNpbmNlIHdlIGFyZSBtaXNzaW5nIG9ic2VydmF0aW9ucyBmb3IgQWxhc2thLCBEZWxhd2FyZSwgQWxhYmFtYSwgV3lvbWluZywgYW5kIFZlcm1vbnQsIHNvIHRoZXNlIHN0YXRlcyB3aWxsIG5vdCBhcHBlYXIgY29sb3JlZCBpbiBvdXIgbWFwLgoKVGhlbiwgY3JlYXRlIGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCBhbmQgbWFrZSBwcmVkaWN0aW9ucyBvbiB0aGUgdGVzdCBzZXQgdXNpbmcgdGhlIGZvbGxvd2luZyBjb21tYW5kczoKCgpgYGB7cn0KcG9sbGluZyA9IHJlYWQuY3N2KCdkYXRhL1BvbGxpbmdJbXB1dGVkLmNzdicpCnRybiA9IHN1YnNldChwb2xsaW5nLCBZZWFyICE9IDIwMTIpCnRzdCA9IHN1YnNldChwb2xsaW5nLCBZZWFyID09IDIwMTIpCm1vZDIgPSBnbG0oUmVwdWJsaWNhbn5TdXJ2ZXlVU0ErRGlmZkNvdW50LCB0cm4sIGZhbWlseT1iaW5vbWlhbCkKcHJlZCA9IHByZWRpY3QobW9kMix0c3QsdHlwZT0ncmVzcG9uc2UnKQpyZXB1YiA9IGFzLm51bWVyaWMocHJlZCA+IDAuNSkKZGYgPSBkYXRhLmZyYW1lKHByZWQsIHJlcHViLCBzdGF0ZT10c3QkU3RhdGUpCmhlYWQoZGYpCmBgYAoKX0ZvciBob3cgbWFueSBzdGF0ZXMgaXMgb3VyIGJpbmFyeSBwcmVkaWN0aW9uIDEgKGZvciAyMDEyKSwgY29ycmVzcG9uZGluZyB0byBSZXB1YmxpY2FuP18KYGBge3J9CnN1bShyZXB1YikKYGBgCgpfV2hhdCBpcyB0aGUgYXZlcmFnZSBwcmVkaWN0ZWQgcHJvYmFiaWxpdHkgb2Ygb3VyIG1vZGVsIChvbiB0aGUgVGVzdCBzZXQsIGZvciAyMDEyKT9fCmBgYHtyfQptZWFuKHByZWQpIApgYGAKCmBgYHtyfQojIyMg5pa55rOV5LqMClBvbGxpbmdJbXB1dGVkIDwtIHJlYWQuY3N2KCJEYXRhL1BvbGxpbmdJbXB1dGVkLmNzdiIpClRSIDwtIHN1YnNldChQb2xsaW5nSW1wdXRlZCwgWWVhciA9PSAyMDA0IHwgWWVhciA9PSAyMDA4KQpUUyA8LSBzdWJzZXQoUG9sbGluZ0ltcHV0ZWQsIFllYXIgPT0gMjAxMikKc3RyKFRTKQoKbW9kMiA9IGdsbShSZXB1YmxpY2FuflN1cnZleVVTQStEaWZmQ291bnQsIGRhdGE9VFIsIGZhbWlseT0iYmlub21pYWwiKQpUZXN0UHJlZGljdGlvbiA9IHByZWRpY3QobW9kMiwgbmV3ZGF0YT1UUywgdHlwZT0icmVzcG9uc2UiKQpUZXN0UHJlZGljdGlvbkJpbmFyeSA9IGFzLm51bWVyaWMoVGVzdFByZWRpY3Rpb24gPiAwLjUpCnByZWRpY3Rpb25EYXRhRnJhbWUgPSBkYXRhLmZyYW1lKFRlc3RQcmVkaWN0aW9uLCBUZXN0UHJlZGljdGlvbkJpbmFyeSwgVFMkU3RhdGUpCnN0cihwcmVkaWN0aW9uRGF0YUZyYW1lKQpzdW1tYXJ5KHByZWRpY3Rpb25EYXRhRnJhbWUpICAgIyBNZWFuID0gMC40ODUyNjI2CnRhYmxlKHByZWRpY3Rpb25EYXRhRnJhbWUkVGVzdFByZWRpY3Rpb25CaW5hcnkpICAgIyAyMgpgYGAKCiMjIyMjIDIuMiBNZXJnZSBEYXRhIGludG8gTWFwCk5vdywgd2UgbmVlZCB0byBtZXJnZSAicHJlZGljdGlvbkRhdGFGcmFtZSIgd2l0aCB0aGUgbWFwIGRhdGEgInN0YXRlc01hcCIsIGxpa2Ugd2UgZGlkIGluIGxlY3R1cmUuIEJlZm9yZSBkb2luZyBzbywgd2UgbmVlZCB0byBjb252ZXJ0IHRoZSBUZXN0LlN0YXRlIHZhcmlhYmxlIHRvIGxvd2VyY2FzZSwgc28gdGhhdCBpdCBtYXRjaGVzIHRoZSByZWdpb24gdmFyaWFibGUgaW4gc3RhdGVzTWFwLiBEbyB0aGlzIGJ5IHR5cGluZyB0aGUgZm9sbG93aW5nIGluIHlvdXIgUiBjb25zb2xlOgoKYGBge3J9CmRmJHJlZ2lvbiA9IHRvbG93ZXIoZGYkc3RhdGUpCnBtYXAgPSBtZXJnZShzdGF0ZXNNYXAsIGRmLCBieT0ncmVnaW9uJykgCmBgYAoKX0hvdyBtYW55IG9ic2VydmF0aW9ucyBhcmUgdGhlcmUgaW4gcHJlZGljdGlvbk1hcD9fCmBgYHtyfQpucm93KHBtYXApICAgICAgICMgMTUwMzQKYGBgCgpfSG93IG1hbnkgb2JzZXJ2YXRpb25zIGFyZSB0aGVyZSBpbiBzdGF0ZU1hcD9fCmBgYHtyfQpucm93KHN0YXRlc01hcCkgICMgMTU1MzcKYGBgCgpgYGB7cn0KIyMjIOaWueazleS6jApwcmVkaWN0aW9uRGF0YUZyYW1lJHJlZ2lvbiA9IHRvbG93ZXIocHJlZGljdGlvbkRhdGFGcmFtZSRUUy5TdGF0ZSkKcHJlZGljdGlvbk1hcCA9IG1lcmdlKHN0YXRlc01hcCwgcHJlZGljdGlvbkRhdGFGcmFtZSwgYnkgPSAicmVnaW9uIikKcHJlZGljdGlvbk1hcCA9IHByZWRpY3Rpb25NYXBbb3JkZXIocHJlZGljdGlvbk1hcCRvcmRlciksXQpzdHIocHJlZGljdGlvbk1hcCkgICAjIDE1MDM0CnN0cihzdGF0ZXNNYXApICAgIyAxNTUzNwpgYGAKCgojIyMjIyAyLjMgVGhlIFJ1bGUgb2YgYG1lcmdlKClgCl9XaGVuIHdlIG1lcmdlZCB0aGUgZGF0YSBpbiB0aGUgcHJldmlvdXMgcHJvYmxlbSwgaXQgY2F1c2VkIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIHRvIGNoYW5nZS4gV2h5P18gQ2hlY2sgb3V0IHRoZSBoZWxwIHBhZ2UgZm9yIG1lcmdlIGJ5IHR5cGluZyA/bWVyZ2UgdG8gaGVscCB5b3UgYW5zd2VyIHRoaXMgcXVlc3Rpb24uCgorIEJlY2F1c2Ugd2Ugb25seSBtYWtlIHByZWRpY3Rpb25zIGZvciA0NSBzdGF0ZXMsIHdlIG5vIGxvbmdlciBoYXZlIG9ic2VydmF0aW9ucyBmb3Igc29tZSBvZiB0aGUgc3RhdGVzLiBUaGVzZSBvYnNlcnZhdGlvbnMgd2VyZSByZW1vdmVkIGluIHRoZSBtZXJnaW5nIHByb2Nlc3MuCgojIyMjIyAyLjQgUGxvdCB0aGUgY29sb3IgbWFwCk5vdyB3ZSBhcmUgcmVhZHkgdG8gY29sb3IgdGhlIFVTIG1hcCB3aXRoIG91ciBwcmVkaWN0aW9ucyEgWW91IGNhbiBjb2xvciB0aGUgc3RhdGVzIGFjY29yZGluZyB0byBvdXIgYmluYXJ5IHByZWRpY3Rpb25zIGJ5IHR5cGluZyB0aGUgZm9sbG93aW5nIGluIHlvdXIgUiBjb25zb2xlOgpgYGB7cn0KcG1hcCA9IHBtYXBbb3JkZXIocG1hcCRncm91cCwgcG1hcCRvcmRlcikgLCBdCmdncGxvdChwbWFwLCBhZXMoeD1sb25nLCB5PWxhdCwgZ3JvdXA9Z3JvdXAsIGZpbGw9cmVwdWIpKSArCiAgZ2VvbV9wb2x5Z29uKGNvbG9yPSdibGFjaycpCmBgYAoKYGBge3J9CiMjIyDmlrnms5XkuowKZ2dwbG90KHByZWRpY3Rpb25NYXAsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCwgZmlsbCA9IFRlc3RQcmVkaWN0aW9uQmluYXJ5KSkgKyBnZW9tX3BvbHlnb24oY29sb3IgPSAiYmxhY2siKQpzdHIoUG9sbGluZ0ltcHV0ZWQpICAgIyAoMSkgTGlnaHQgYmx1ZQojIFJlcHVibGljYW7mrITkvY3nmoQx77yM5Y2z6KGo56S654K6UmVwdWJsaWNhbu+8m+iLpeeCujDvvIzliYfngrpEZW1vY3JhdApgYGAKCgpUaGUgc3RhdGVzIGFwcGVhciBsaWdodCBibHVlIGFuZCBkYXJrIGJsdWUgaW4gdGhpcyBtYXAuIF9XaGljaCBjb2xvciByZXByZXNlbnRzIGEgUmVwdWJsaWNhbiBwcmVkaWN0aW9uP18KCisgTGlnaHQgYmx1ZQoKIyMjIyMgMi41CldlIHNlZSB0aGF0IHRoZSBsZWdlbmQgZGlzcGxheXMgYSBibHVlIGdyYWRpZW50IGZvciBvdXRjb21lcyBiZXR3ZWVuIDAgYW5kIDEuIEhvd2V2ZXIsIHdoZW4gcGxvdHRpbmcgdGhlIGJpbmFyeSBwcmVkaWN0aW9ucyB0aGVyZSBhcmUgb25seSB0d28gcG9zc2libGUgb3V0Y29tZXM6IDAgb3IgMS4gTGV0J3MgcmVwbG90IHRoZSBtYXAgd2l0aCBkaXNjcmV0ZSBvdXRjb21lcy4gV2UgY2FuIGFsc28gY2hhbmdlIHRoZSBjb2xvciBzY2hlbWUgdG8gYmx1ZSBhbmQgcmVkLCB0byBtYXRjaCB0aGUgYmx1ZSBjb2xvciBhc3NvY2lhdGVkIHdpdGggdGhlIERlbW9jcmF0aWMgUGFydHkgaW4gdGhlIFVTIGFuZCB0aGUgcmVkIGNvbG9yIGFzc29jaWF0ZWQgd2l0aCB0aGUgUmVwdWJsaWNhbiBQYXJ0eSBpbiB0aGUgVVMuIFRoaXMgY2FuIGJlIGRvbmUgd2l0aCB0aGUgZm9sbG93aW5nIGNvbW1hbmQ6CgpBbHRlcm5hdGl2ZWx5LCB3ZSBjb3VsZCBwbG90IHRoZSBwcm9iYWJpbGl0aWVzIGluc3RlYWQgb2YgdGhlIGJpbmFyeSBwcmVkaWN0aW9ucy4gQ2hhbmdlIHRoZSBwbG90IGNvbW1hbmQgYWJvdmUgdG8gaW5zdGVhZCBjb2xvciB0aGUgc3RhdGVzIGJ5IHRoZSB2YXJpYWJsZSBUZXN0UHJlZGljdGlvbi4gCgpgYGB7cn0KZ2dwbG90KHBtYXAsIGFlcyh4PWxvbmcsIHk9bGF0LCBncm91cD1ncm91cCwgZmlsbD1wcmVkKSkgKwogIGdlb21fcG9seWdvbihjb2xvcj0nYmxhY2snKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudCgKICAgIGxvdz0iYmx1ZSIsIGhpZ2g9InJlZCIsIAogICAgZ3VpZGU9ImxlZ2VuZCIsIGJyZWFrcz0gYygwLDEpLCAKICAgIGxhYmVscz1jKCJEZW1vY3JhdCIsICJSZXB1YmxpY2FuIiksIG5hbWU9IlByZWRpY3Rpb24gMjAxMiIpCiMgc2NhbGU6IOmhj+iJsgpgYGAKCmBgYHtyfQojIyMg5pa55rOV5LqMCmdncGxvdChwcmVkaWN0aW9uTWFwLCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXAsIGZpbGwgPSBUZXN0UHJlZGljdGlvbkJpbmFyeSkpICsgZ2VvbV9wb2x5Z29uKGNvbG9yID0gImJsYWNrIikgKyBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJibHVlIiwgaGlnaCA9ICJyZWQiLCBndWlkZSA9ICJsZWdlbmQiLCBicmVha3M9IGMoMCwxKSwgbGFiZWxzID0gYygiRGVtb2NyYXQiLCAiUmVwdWJsaWNhbiIpLCBuYW1lID0gIlByZWRpY3Rpb24gMjAxMiIpCmdncGxvdChwcmVkaWN0aW9uTWFwLCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXAsIGZpbGwgPSBUZXN0UHJlZGljdGlvbikpICsgZ2VvbV9wb2x5Z29uKGNvbG9yID0gImJsYWNrIikgKyBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJibHVlIiwgaGlnaCA9ICJyZWQiLCBndWlkZSA9ICJsZWdlbmQiLCBicmVha3M9IGMoMCwxKSwgbGFiZWxzID0gYygiRGVtb2NyYXQiLCAiUmVwdWJsaWNhbiIpLCBuYW1lID0gIlByZWRpY3Rpb24gMjAxMiIpCiMgKDEpIFRoZSB0d28gbWFwcyBsb29rIHZlcnkgc2ltaWxhci4gVGhpcyBpcyBiZWNhdXNlIG1vc3Qgb2Ygb3VyIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIGFyZSBjbG9zZSB0byAwIG9yIGNsb3NlIHRvIDEuCmBgYAoKCllvdSBzaG91bGQgc2VlIGEgZ3JhZGllbnQgb2YgY29sb3JzIHJhbmdpbmcgZnJvbSByZWQgdG8gYmx1ZS4gX0RvIHRoZSBjb2xvcnMgb2YgdGhlIHN0YXRlcyBpbiB0aGUgbWFwIGZvciBUZXN0UHJlZGljdGlvbiBsb29rIGRpZmZlcmVudCBmcm9tIHRoZSBjb2xvcnMgb2YgdGhlIHN0YXRlcyBpbiB0aGUgbWFwIHdpdGggVGVzdFByZWRpY3Rpb25CaW5hcnk/IFdoeSBvciB3aHkgbm90P18KCisg6aGP6Imy5ZyoVFLjgIFUU+S5i+mWk+acieS4gOm7num7nuS4jeS4gOaoowor6Ly45YWl5b6M5YWp5by15ZyW6ZW35b6X5beu5LiN5aSaCisgVGVzdFByZWRpY3Rpb25CaW5hcnnmmK9EaXNjcmV0Ze+8jOWDheacieOAjOaYryAoMSwgUmVwdWJsaWNhbinjgI3jgIHjgIzlkKYgKDAsIERlbW9jcmF0KeOAjeeahDLnqK7ntZDmnpwKKyBUZXN0UHJlZGljdGlvbuWJh+aYr0NvbnRpbm91c++8jOapn+eOhzAtMe+8jOaVhee1kOaenOW+njAtMeS5i+mWk+eahOS7u+S9leapn+eOh+mDveacieWPr+iDvQorIOS6jOiAheW3rueVsOS4jeWkp+eahOWOn+WboOaYr++8mueUseaWvOaVuOWAvOeahOevhOWcjemDveaYrygwLCAxKe+8jOaJgOS7peeVtuWmguaenOipsuWcsOWNgOeahOaUv+ayu+eri+WgtOalteeCuuWIhuaYjuaZgu+8jOe1kOaenOS+v+acg+WBj+WQkTDmiJbmmK8x77yM6YCZ5qij5b6X5Ye655qE57WQ5p6c5L6/5pyD6IiH5Y6f5YWIQmluYXJ555qE57WQ5p6c5beu55Ww5LiN5aSnCisg5ZSv5LiA5Y+v6IO95pyD5Ye654++6aGP6Imy5beu55Ww5aSn55qE5oOF5rOB5piv77ya55W25pW05YCL576O5ZyL5omA5pyJ5Zyw5Y2A55qE5pS/5rK756uL5aC055qG5LiN6a6u5piO5pmC77yM5pW05by15ZyW5pyD5YGP5ZCR5qmf546HPTAuNeaZguaJgOWHuuePvuaJgGZpbGznmoTntKvoibIKCjxicj48aHI+CgojIyMgMy4gVW5kZXJzdGFuZGluZyB0aGUgUHJlZGljdGlvbnMKCiMjIyMjIDMuMSAKSW4gdGhlIDIwMTIgZWxlY3Rpb24sIHRoZSBzdGF0ZSBvZiBGbG9yaWRhIGVuZGVkIHVwIGJlaW5nIGEgdmVyeSBjbG9zZSByYWNlLiBJdCB3YXMgdWx0aW1hdGVseSB3b24gYnkgdGhlIERlbW9jcmF0aWMgcGFydHkuIApgYGB7cn0KZGYkcHJlZFsgZGYkc3RhdGUgPT0gJ0Zsb3JpZGEnXSAjIDAuOTY0MDQKYGBgCl9EaWQgd2UgcHJlZGljdCB0aGlzIHN0YXRlIGNvcnJlY3RseSBvciBpbmNvcnJlY3RseT8gXwoKKyBXZSBpbmNvcnJlY3RseSBwcmVkaWN0ZWQgdGhpcyBzdGF0ZSBieSBwcmVkaWN0aW5nIHRoYXQgaXQgd291bGQgYmUgd29uIGJ5IHRoZSBSZXB1YmxpY2FuIHBhcnR5LiAKKwoKIyMjIyMgMy4yCl9XaGF0IHdhcyBvdXIgcHJlZGljdGVkIHByb2JhYmlsaXR5IGZvciB0aGUgc3RhdGUgb2YgRmxvcmlkYT9fCmBgYHtyfQpkZiRwcmVkWyBkZiRzdGF0ZSA9PSAnRmxvcmlkYSddICMgMC45NjQwNApgYGAKCmBgYHtyfQojIyMg5pa55rOV5LqMCmdncGxvdChwcmVkaWN0aW9uTWFwLCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXAsIGZpbGwgPSBUZXN0UHJlZGljdGlvbikpICsgZ2VvbV9wb2x5Z29uKGNvbG9yID0gImJsYWNrIikgKyBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJibHVlIiwgaGlnaCA9ICJyZWQiLCBndWlkZSA9ICJsZWdlbmQiLCBicmVha3M9IGMoMCwxKSwgbGFiZWxzID0gYygiRGVtb2NyYXQiLCAiUmVwdWJsaWNhbiIpLCBuYW1lID0gIlByZWRpY3Rpb24gMjAxMiIpCnRhYmxlKFBvbGxpbmdJbXB1dGVkJFN0YXRlLCBQb2xsaW5nSW1wdXRlZCRSZXB1YmxpY2FuKQpwcmVkaWN0aW9uRGF0YUZyYW1lICAgIyAgIDkuNjQwMzk1ZS0wMSA9IDAuOTY0MDM5NQojIyMgKDQpIE91ciBwcmVkaWN0aW9uIG1vZGVsIGRpZCBub3QgZG8gYSB2ZXJ5IGdvb2Qgam9iIG9mIGNvcnJlY3RseSBwcmVkaWN0aW5nIHRoZSBzdGF0ZSBvZiBGbG9yaWRhLCBhbmQgd2Ugd2VyZSB2ZXJ5IGNvbmZpZGVudCBpbiBvdXIgaW5jb3JyZWN0IHByZWRpY3Rpb24uCmBgYAoKCl9XaGF0IGRvZXMgdGhpcyBpbXBseT9fCgorIE91ciBwcmVkaWN0aW9uIG1vZGVsIGRpZCBub3QgZG8gYSB2ZXJ5IGdvb2Qgam9iIG9mIGNvcnJlY3RseSBwcmVkaWN0aW5nIHRoZSBzdGF0ZSBvZiBGbG9yaWRhLCBhbmQgd2Ugd2VyZSB2ZXJ5IGNvbmZpZGVudCBpbiBvdXIgaW5jb3JyZWN0IHByZWRpY3Rpb24uCisKCjxicj48aHI+CgojIyMjIyA0LiBQYXJhbWV0ZXIgU2V0dGluZ3MKSW4gdGhpcyBwYXJ0LCB3ZSdsbCBleHBsb3JlIHdoYXQgdGhlIGRpZmZlcmVudCBwYXJhbWV0ZXIgc2V0dGluZ3Mgb2YgZ2VvbV9wb2x5Z29uIGRvLiBUaHJvdWdob3V0IHRoZSBwcm9ibGVtLCB1c2UgdGhlIGhlbHAgcGFnZSBmb3IgZ2VvbV9wb2x5Z29uLCB3aGljaCBjYW4gYmUgYWNjZXNzZWQgYnkgP2dlb21fcG9seWdvbi4gVG8gc2VlIG1vcmUgaW5mb3JtYXRpb24gYWJvdXQgYSBjZXJ0YWluIHBhcmFtZXRlciwganVzdCB0eXBlIGEgcXVlc3Rpb24gbWFyayBhbmQgdGhlbiB0aGUgcGFyYW1ldGVyIG5hbWUgdG8gZ2V0IHRoZSBoZWxwIHBhZ2UgZm9yIHRoYXQgcGFyYW1ldGVyLiBFeHBlcmltZW50IHdpdGggZGlmZmVyZW50IHBhcmFtZXRlciBzZXR0aW5ncyB0byB0cnkgYW5kIHJlcGxpY2F0ZSB0aGUgcGxvdHMhCgpXZSdsbCBiZSBhc2tpbmcgcXVlc3Rpb25zIGFib3V0IHRoZSBmb2xsb3dpbmcgdGhyZWUgcGxvdHM6CmBgYHtyfQpncmFkID0gc2NhbGVfZmlsbF9ncmFkaWVudCgKICBsb3c9ImJsdWUiLCBoaWdoPSJyZWQiLCAKICBndWlkZT0ibGVnZW5kIiwgYnJlYWtzPSBjKDAsMSksIAogIGxhYmVscz1jKCJEZW1vY3JhdCIsICJSZXB1YmxpY2FuIiksIG5hbWU9IlByZWRpY3Rpb24gMjAxMiIpCmBgYAoKYGBge3J9CmdncGxvdChwbWFwLCBhZXMoeD1sb25nLCB5PWxhdCwgZ3JvdXA9Z3JvdXAsIGZpbGw9cmVwdWIpKSArIGdyYWQgKwogIGdlb21fcG9seWdvbihjb2xvcj0nYmxhY2snLGxpbmV0eXBlPTMsc2l6ZT0xKSArIGdndGl0bGUoIlBsb3QoMSkiKQpgYGAKCmBgYHtyfQpnZ3Bsb3QocG1hcCwgYWVzKHg9bG9uZywgeT1sYXQsIGdyb3VwPWdyb3VwLCBmaWxsPXJlcHViKSkgKyBncmFkICsKICBnZW9tX3BvbHlnb24oY29sb3I9J2JsYWNrJyxsaW5ldHlwZT0xLHNpemU9MykgKyBnZ3RpdGxlKCJQbG90KDIpIikKYGBgCgoKYGBge3J9CmdncGxvdChwbWFwLCBhZXMoeD1sb25nLCB5PWxhdCwgZ3JvdXA9Z3JvdXAsIGZpbGw9cmVwdWIpKSArIGdyYWQgKwogIGdlb21fcG9seWdvbihjb2xvcj0nYmxhY2snLGxpbmV0eXBlPTEsc2l6ZT0xLGFscGhhPTAuMykgKyBnZ3RpdGxlKCJQbG90KDMpIikKYGBgCgojIyMjIyA0LjEgClBsb3RzICgxKSBhbmQgKDIpIHdlcmUgY3JlYXRlZCBieSBjaGFuZ2luZyBkaWZmZXJlbnQgcGFyYW1ldGVycyBvZiBnZW9tX3BvbHlnb24gZnJvbSB0aGVpciBkZWZhdWx0IHZhbHVlcy4KCmBgYHtyfQojIyMgbGluZXR5cGUKZ2dwbG90KHByZWRpY3Rpb25NYXAsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCwgZmlsbCA9IFRlc3RQcmVkaWN0aW9uKSkrIGdlb21fcG9seWdvbihjb2xvciA9ICJibGFjayIsIGxpbmV0eXBlPTMpICsgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAiYmx1ZSIsIGhpZ2ggPSAicmVkIiwgZ3VpZGUgPSAibGVnZW5kIiwgYnJlYWtzPSBjKDAsMSksIGxhYmVscyA9IGMoIkRlbW9jcmF0IiwgIlJlcHVibGljYW4iKSwgbmFtZSA9ICJQcmVkaWN0aW9uIDIwMTIiKQojIyMgUGxvdCgxKTog6Jmb57ea77yIbGluZXR5cGUgPSAy77yJCmBgYAoKX1doYXQgaXMgdGhlIG5hbWUgb2YgdGhlIHBhcmFtZXRlciB3ZSBjaGFuZ2VkIHRvIGNyZWF0ZSBwbG90ICgxKT9fCgorIGxpbmV0eXBlICjomZvnt5rmlLnmiJDlr6bnt5opCgpfV2hhdCBpcyB0aGUgbmFtZSBvZiB0aGUgcGFyYW1ldGVyIHdlIGNoYW5nZWQgdG8gY3JlYXRlIHBsb3QgKDIpP18KCisgc2l6Ze+8iOWKoOWvrOWKoOeyl++8iQoKCiMjIyMjIDQuMiAKUGxvdCAoMykgd2FzIGNyZWF0ZWQgYnkgY2hhbmdpbmcgdGhlIHZhbHVlIG9mIGEgZGlmZmVyZW50IGdlb21fcG9seWdvbiBwYXJhbWV0ZXIgdG8gaGF2ZSB2YWx1ZSAwLjMuIF9XaGljaCBwYXJhbWV0ZXIgZGlkIHdlIHVzZT9fCgpgYGB7cn0KIyMjIGFscGhhCmdncGxvdChwcmVkaWN0aW9uTWFwLCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXAsIGZpbGwgPSBUZXN0UHJlZGljdGlvbikpKyBnZW9tX3BvbHlnb24oY29sb3IgPSAiYmxhY2siLCBhbHBoYT0wLjMpICsgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAiYmx1ZSIsIGhpZ2ggPSAicmVkIiwgZ3VpZGUgPSAibGVnZW5kIiwgYnJlYWtzPSBjKDAsMSksIGxhYmVscyA9IGMoIkRlbW9jcmF0IiwgIlJlcHVibGljYW4iKSwgbmFtZSA9ICJQcmVkaWN0aW9uIDIwMTIiKQo/YWxwaGEKIyMjIGFscGhhOiBNb2RpZnkgY29sb3VyIHRyYW5zcGFyZW5jeS4gVmVjdG9yaXNlZCBpbiBib3RoIGNvbG91ciBhbmQgYWxwaGEuCmBgYAoKKyBhbHBoYe+8iOiqv+aVtOmhj+iJsueahOmAj+aYjuW6pu+8iQoKPGJyPjxocj4KCjxicj48YnI+PGJyPjxicj48YnI+Cgo8c3R5bGU+Ci5jYXB0aW9uIHsKICBjb2xvcjogIzc3NzsKICBtYXJnaW4tdG9wOiAxMHB4Owp9CnAgY29kZSB7CiAgd2hpdGUtc3BhY2U6IGluaGVyaXQ7Cn0KcHJlIHsKICB3b3JkLWJyZWFrOiBub3JtYWw7CiAgd29yZC13cmFwOiBub3JtYWw7CiAgbGluZS1oZWlnaHQ6IDE7Cn0KcHJlIGNvZGUgewogIHdoaXRlLXNwYWNlOiBpbmhlcml0Owp9CnAsbGkgewogIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOwp9CgoucnsKICBsaW5lLWhlaWdodDogMS4yOwp9Cgp0aXRsZXsKICBjb2xvcjogI2NjMDAwMDsKICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsKfQoKYm9keXsKICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsKfQoKaDEsaDIsaDMsaDQsaDV7CiAgY29sb3I6ICMwMDg4MDA7CiAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7Cn0KCmgzewogIGNvbG9yOiAjYjM2YjAwOwogIGJhY2tncm91bmQ6ICNmZmUwYjM7CiAgbGluZS1oZWlnaHQ6IDI7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KCmg1ewogIGNvbG9yOiAjMDA2MDAwOwogIGJhY2tncm91bmQ6ICNmZmZmZTA7CiAgbGluZS1oZWlnaHQ6IDI7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KCmVtewogIGNvbG9yOiAjMDAwMGMwOwogIGJhY2tncm91bmQ6ICNmMGYwZjA7CiAgfQoKPC9zdHlsZT4KCg==