EXERCISE 3.2.4
library(tidyverse)
Registered S3 method overwritten by 'dplyr':
method from
print.rowwise_df
[30m-- [1mAttaching packages[22m --------------------------------------- tidyverse 1.2.1 --[39m
[30m[32mv[30m [34mggplot2[30m 3.2.1 [32mv[30m [34mpurrr [30m 0.3.3
[32mv[30m [34mtibble [30m 2.1.3 [32mv[30m [34mdplyr [30m 0.8.3
[32mv[30m [34mtidyr [30m 1.0.0 [32mv[30m [34mstringr[30m 1.4.0
[32mv[30m [34mreadr [30m 1.3.1 [32mv[30m [34mforcats[30m 0.4.0[39m
[30m-- [1mConflicts[22m ------------------------------------------ tidyverse_conflicts() --
[31mx[30m [34mdplyr[30m::[32mfilter()[30m masks [34mstats[30m::filter()
[31mx[30m [34mdplyr[30m::[32mlag()[30m masks [34mstats[30m::lag()[39m
Run ggplot(data = mpg). What do you see?
ggplot(data = mpg)

ANSWER
empty graph
How many rows are in mpg? How many columns?
?mpg
ANSWER
A data frame with 234 rows and 11 variables
What does the drv variable describe? Read the help for ?mpg to find out.
ANSWER
f = front-wheel drive, r = rear wheel drive, 4 = 4wd
Make a scatterplot of hwy vs cyl
ggplot(data=mpg)+
geom_point(mapping = aes(x=hwy, y=cyl))

What happens if you make a scatterplot of class vs drv? Why is the plot not useful?
ggplot(data=mpg)+
geom_point(mapping = aes(x=class, y=drv))

ANSWER
The plot is not useful because varible class is categorical
3.3.1 EXERCISES
What’s gone wrong with this code? Why are the points not blue?
SOLUTION
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy), color = "blue")

Which variables in mpg are categorical? Which variables are continuous? (Hint: type ?mpg to read the documentation for the dataset). How can you see this information when you run mpg?
?mpg
str(mpg)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 234 obs. of 11 variables:
$ manufacturer: chr "audi" "audi" "audi" "audi" ...
$ model : chr "a4" "a4" "a4" "a4" ...
$ displ : num 1.8 1.8 2 2 2.8 2.8 3.1 1.8 1.8 2 ...
$ year : int 1999 1999 2008 2008 1999 1999 2008 1999 1999 2008 ...
$ cyl : int 4 4 4 4 6 6 6 4 4 4 ...
$ trans : chr "auto(l5)" "manual(m5)" "manual(m6)" "auto(av)" ...
$ drv : chr "f" "f" "f" "f" ...
$ cty : int 18 21 20 21 16 18 18 18 16 20 ...
$ hwy : int 29 29 31 30 26 26 27 26 25 28 ...
$ fl : chr "p" "p" "p" "p" ...
$ class : chr "compact" "compact" "compact" "compact" ...
ANSWER
categorical - drv,class,fl,trans,manufacturer,model,trans
Continous - model,displ,cyl,hwy,year,cty
Map a continuous variable to color, size, and shape. How do these aesthetics behave differently for categorical vs. continuous variables?
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy, color = cty))

NA
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy, size = cty))

NA
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy, shape = cty))
Error: A continuous variable can not be mapped to shape

What happens if you map the same variable to multiple aesthetics?
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy, size=cty, color=cty))

NA
What does the stroke aesthetic do? What shapes does it work with? (Hint: use ?geom_point)
Use the stroke aesthetic to modify the width of the border
ggplot(data=mpg)+
geom_point(mapping=aes(x = displ, y = hwy, size=cty, color=cty,stroke = 5))

What happens if you map an aesthetic to something other than a variable name, like aes(colour = displ < 5)? Note, you’ll also need to specify x and y.
ggplot(data=mpg)+
geom_point(mapping=aes(x = displ, y = hwy, color=displ<5))

3.5.1 EXERCISE
What happens if you facet on a continuous variable?
facet_wrap() will work with continous variable but not as useful as working with categorical variable.
ggplot(data=mpg)+
geom_point(mapping=aes(x = displ, y = hwy))+
facet_wrap(~cty)

What do the empty cells in plot with facet_grid(drv ~ cyl) mean? How do they relate to this plot?
No combination of data points
ggplot(data = mpg) +
geom_point(mapping = aes(x = drv, y = cyl))+
facet_grid(drv ~ cyl)

What plots does the following code make? What does . do?
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy)) +
facet_grid(drv ~ .)

ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy)) +
facet_grid(. ~ cyl)

conclusion
rows are facetted by the variable on the left hand side of ~. and columns are facetted by the variable on the right hand side of .~
Take the first faceted plot in this section:
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy)) +
facet_wrap(~ class, nrow = 2)

What are the advantages to using faceting instead of the colour aesthetic? What are the disadvantages? How might the balance change if you had a larger dataset?
Advantages of using faceting is that it is useful for categorical variables,to split your plot into facets, subplots that each display one subset of the data making focus on particular facets alone. in contrast colour aesthetic having display of multiple colors with increase in categorical features can cause confusion.
Disadvantage of using faceting is that since the points are on separate plots direct comparison may not be direct
Read ?facet_wrap. What does nrow do? What does ncol do? What other options control the layout of the individual panels? Why doesn’t facet_grid() have nrow and ncol arguments?
?facet_wrap()
nrow, ncol
Number of rows and columns.
When using facet_grid() you should usually put the variable with more unique levels in the columns. Why?
One logical reason is that since the dependent variables are usually plotted on the y-axis, it is much easier to compare the highs and lows and the trends of the variables if the plots are placed side by side.
EXERCISE 3.6.1
What geom would you use to draw a line chart? A boxplot? A histogram? An area chart?
ggplot(data = mpg)+
geom_line(mapping=aes(x=displ,y=hwy))

NA
ggplot(data = mpg)+
geom_boxplot(mapping=aes(x=displ,y=hwy,color=drv))

NA
ggplot(data = mpg)+
geom_histogram()
ggplot(data = mpg)+
geom_area(mapping=aes(x=displ,y=hwy))

NA
Run this code in your head and predict what the output will look like. Then, run the code in R and check your predictions.
ggplot(data = mpg, mapping = aes(x = displ, y = hwy, color = drv)) +
geom_point() +
geom_smooth(se = FALSE)

What does show.legend = FALSE do?
Removes a legend that is used to explain which levels correspond to which values.
What happens if you remove it?
You see the legend explaining what levels correspond to which values.
Why do you think I used it earlier in the chapter?
To show how to remove a legend
ggplot(data = mpg) +
geom_smooth(
mapping = aes(x = displ, y = hwy, color = drv),
show.legend = FALSE
)

What does the se argument to geom_smooth() do
ggplot(data = mpg, mapping = aes(x = displ, y = hwy, color = drv)) +
geom_point() +
geom_smooth(se = FALSE)

ggplot(data = mpg, mapping = aes(x = displ, y = hwy, color = drv)) +
geom_point() +
geom_smooth(se = TRUE)

ANSWER
se argument to geom_smooth() does add a loess smooth
Will these two graphs look different? Why/why not?
ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_point() +
geom_smooth()

ggplot() +
geom_point(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_smooth(data = mpg, mapping = aes(x = displ, y = hwy))

ANSWER
No the two graphs will not look different, because both functions produce the same output. The difference is that second function only does duplication in your code, which is not good practice for obtaining clean code.
Recreate the R code necessary to generate the following graphs.
ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_point(mapping = aes(size=class),show.legend = FALSE) +
geom_smooth(se=FALSE)

ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_point(mapping = aes(size=class),show.legend = FALSE) +
geom_smooth(mapping = aes(group=drv),se=FALSE)

ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_point(mapping = aes(color=drv,size=class),show.legend = TRUE) +
geom_smooth(mapping = aes(color=drv),show.legend = TRUE,se=FALSE)

ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_point(mapping = aes(color=drv,size=class),show.legend = TRUE) +
geom_smooth(show.legend = FALSE,se=FALSE)

ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_point(mapping = aes(color=drv,size=class),show.legend = TRUE) +
geom_smooth(mapping = aes(linetype=drv),show.legend = TRUE,se=FALSE)

ggplot(data = mpg, mapping = aes(x = displ, y = hwy,size=class)) +
geom_point(mapping = aes(color=drv),show.legend = TRUE)

NA
EXERCISE 3.7.1
What is the default geom associated with stat_summary()? How could you rewrite the previous plot to use that geom function instead of the stat function?
?stat_summary()
PREVIOUS PLOT
ggplot(data = diamonds) +
stat_summary(
mapping = aes(x = cut, y = depth),
fun.ymin = min,
fun.ymax = max,
fun.y = median
)

SOLUTION
ggplot(data = diamonds) +
geom_pointrange(mapping = aes(x = cut, y = depth),
stat = "summary",
fun.ymin = min,
fun.ymax = max,
fun.y = median)

?stat_summary()
What does geom_col() do? How is it different to geom_bar()?
?geom_bar
ANSWER
geom_bar() makes the height of the bar proportional to the number of cases in each group (or if the weight aesthetic is supplied, the sum of the weights). If you want the heights of the bars to represent values in the data, use geom_col() instead. geom_bar() uses stat_count() by default: it counts the number of cases at each x position. geom_col() uses stat_identity(): it leaves the data as is.
Most geoms and stats come in pairs that are almost always used in concert. Read through the documentation and make a list of all the pairs. What do they have in common?
?geom_bar
geom_bar(mapping = NULL, data = NULL, stat = “count”, position = “stack”, …, width = NULL, binwidth = NULL, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE)
stat_count(mapping = NULL, data = NULL, geom = “bar”, position = “stack”, …, width = NULL, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) Both stat_count & geom_bar() understands the following aesthetics: x, y, alpha, colour, fill, group, linetype & size.
What variables does stat_smooth() compute? What parameters control its behaviour?
?stat_smooth
y - predicted value
ymin - lower pointwise confidence interval around the mean
ymax - upper pointwise confidence interval around the mean
se - standard error
In our proportion bar chart, we need to set group = 1. Why? In other words what is the problem with these two graphs?
THE PLOT IS WRONG: when you exclude the “group = 1”
ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut, y = ..prop..))

THE PLOT IS WRONG when: you exclude the “group = 1”
ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut, fill = color, y = ..prop..))

ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut, y = ..prop..,group=1))

ANSWER
we need to set group=“1” to override the default behavior, which here is to group by cut and in general is to group by the x variable. For example, here, the default would be for geom_bar to return the number of rows with cut equal to “Fair”, “Good”, etc.
3.8.1 Exercises
What is the problem with this plot? How could you improve it?
ggplot(data = mpg, mapping = aes(x = cty, y = hwy)) +
geom_point()

Answer
cty & hwy are rounded so the points appear on a grid and many points overlap each other.
What parameters to geom_jitter() control the amount of jittering?
?geom_jitter
ANSWER
Height & Weight
ggplot(data = mpg, mapping = aes(x = cty, y = hwy)) +
geom_jitter(width = 0.5, height = 0.5)

Compare and contrast geom_jitter() with geom_count().
?geom_count
This is a variant geom_point() that counts the number of observations at each location, then maps the count to point area. It useful when you have discrete data and overplotting.
?geom_jitter
The jitter geom is a convenient shortcut for geom_point(position = “jitter”). It adds a small amount of random variation to the location of each point, and is a useful way of handling overplotting caused by discreteness in smaller datasets.
What’s the default position adjustment for geom_boxplot()? Create a visualisation of the mpg dataset that demonstrates it.
ggplot(data = mpg) +
geom_boxplot(mapping = aes(y = displ, x = drv, color = factor(year)))

3.9.1 Exercises
Turn a stacked bar chart into a pie chart using coord_polar().
ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut, fill = clarity)) +
coord_polar()

What does labs() do? Read the documentation.
?labs
Modify axis, legend, and plot labels
What’s the difference between coord_quickmap() and coord_map()?
?coord_quickmap
?coord_map
coord_map projects a portion of the earth, which is approximately spherical, onto a flat 2D plane using any projection defined by the mapproj package. Map projections do not, in general, preserve straight lines, so this requires considerable computation. coord_quickmap is a quick approximation that does preserve straight lines. It works best for smaller areas closer to the equator.
What does the plot below tell you about the relationship between city and highway mpg? Why is coord_fixed() important? What does geom_abline() do?
ggplot(data = mpg, mapping = aes(x = cty, y = hwy)) +
geom_point() +
geom_abline() +
coord_fixed()

The plot shows us that there is a positive linear trend between hwy and cty, and the slope is approximately close 1, meaning that a unit increase in cty is associated with a unit increase in hwy.
?coord_fixed()
coord_fixed forces a specified aspect ratio between the physical representation of the units on the axes. The ratio is 1 by default. It is important to fix the aspect ratio in this case because hwy and cty are measured in the same unit (miles per gallon). Any other aspect ratios will give a visually incorrect representation and might lead us to believe that one increasese at a faster rate than the other.
?geom_abline()
geom_abline() adds a diagonal reference line to the plot, thus allows us
4.4 Practice
Why does this code not work?
my_variable <- 10
my_varlable
Error: object 'my_varlable' not found
Look carefully! (This may seem like an exercise in pointlessness, but training your brain to notice even the tiniest difference will pay off when programming.)
object called in the second line does not match the object name
Tweak each of the following R commands so that they run correctly:
library(tidyverse)
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy))

fliter(mpg, cyl = 8)
Error in fliter(mpg, cyl = 8) : could not find function "fliter"
Press Alt + Shift + K. What happens? How can you get to the same place using the menus?
keyboard shortcut reference menu apppears
you can get the same place using the Tools menu then choosing the keyboard shortcut help.
LS0tDQp0aXRsZTogIkRhdGEgVmlzdWFsaXphdGlvbnMiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQoqKkVYRVJDSVNFIDMuMi40KioNCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQoqKlJ1biBnZ3Bsb3QoZGF0YSA9IG1wZykuIFdoYXQgZG8geW91IHNlZT8qKg0KDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBtcGcpDQpgYGANCioqQU5TV0VSKioNCg0KZW1wdHkgZ3JhcGgNCg0KKipIb3cgbWFueSByb3dzIGFyZSBpbiBtcGc/IEhvdyBtYW55IGNvbHVtbnM/KioNCg0KYGBge3J9DQo/bXBnDQpgYGANCg0KKipBTlNXRVIqKg0KDQpBIGRhdGEgZnJhbWUgd2l0aCAyMzQgcm93cyBhbmQgMTEgdmFyaWFibGVzDQoNCioqV2hhdCBkb2VzIHRoZSBkcnYgdmFyaWFibGUgZGVzY3JpYmU/IFJlYWQgdGhlIGhlbHAgZm9yID9tcGcgdG8gZmluZCBvdXQuKioNCg0KKipBTlNXRVIqKg0KDQpmID0gZnJvbnQtd2hlZWwgZHJpdmUsIHIgPSByZWFyIHdoZWVsIGRyaXZlLCA0ID0gNHdkDQoNCg0KKipNYWtlIGEgc2NhdHRlcnBsb3Qgb2YgaHd5IHZzIGN5bCoqDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGE9bXBnKSsNCmdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4PWh3eSwgeT1jeWwpKQ0KYGBgDQoqKldoYXQgaGFwcGVucyBpZiB5b3UgbWFrZSBhIHNjYXR0ZXJwbG90IG9mIGNsYXNzIHZzIGRydj8gV2h5IGlzIHRoZSBwbG90IG5vdCB1c2VmdWw/KioNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YT1tcGcpKw0KZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHg9Y2xhc3MsIHk9ZHJ2KSkNCmBgYA0KKipBTlNXRVIqKg0KDQpUaGUgcGxvdCBpcyBub3QgdXNlZnVsIGJlY2F1c2UgdmFyaWJsZSBjbGFzcyBpcyBjYXRlZ29yaWNhbA0KDQoqKjMuMy4xIEVYRVJDSVNFUyoqDQoNCioqV2hhdOKAmXMgZ29uZSB3cm9uZyB3aXRoIHRoaXMgY29kZT8gV2h5IGFyZSB0aGUgcG9pbnRzIG5vdCBibHVlPyoqDQoNCioqU09MVVRJT04qKg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IG1wZykgKyANCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSksIGNvbG9yID0gImJsdWUiKQ0KYGBgDQoqKldoaWNoIHZhcmlhYmxlcyBpbiBtcGcgYXJlIGNhdGVnb3JpY2FsPyBXaGljaCB2YXJpYWJsZXMgYXJlIGNvbnRpbnVvdXM/IChIaW50OiB0eXBlID9tcGcgdG8gcmVhZCB0aGUgZG9jdW1lbnRhdGlvbiBmb3IgdGhlIGRhdGFzZXQpLiBIb3cgY2FuIHlvdSBzZWUgdGhpcyBpbmZvcm1hdGlvbiB3aGVuIHlvdSBydW4gbXBnPyoqDQoNCmBgYHtyfQ0KP21wZw0KYGBgDQoNCmBgYHtyfQ0Kc3RyKG1wZykNCmBgYA0KDQoqKkFOU1dFUioqDQoNCmNhdGVnb3JpY2FsIC0gZHJ2LGNsYXNzLGZsLHRyYW5zLG1hbnVmYWN0dXJlcixtb2RlbCx0cmFucw0KDQpDb250aW5vdXMgLSBtb2RlbCxkaXNwbCxjeWwsaHd5LHllYXIsY3R5DQoNCioqTWFwIGEgY29udGludW91cyB2YXJpYWJsZSB0byBjb2xvciwgc2l6ZSwgYW5kIHNoYXBlLiBIb3cgZG8gdGhlc2UgYWVzdGhldGljcyBiZWhhdmUgZGlmZmVyZW50bHkgZm9yIGNhdGVnb3JpY2FsIHZzLiBjb250aW51b3VzIHZhcmlhYmxlcz8qKg0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXBnKSArIA0KICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBjb2xvciA9IGN0eSkpDQogIA0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBtcGcpICsgDQogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3ksIHNpemUgPSBjdHkpKQ0KICANCmBgYA0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXBnKSArIA0KICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBzaGFwZSA9IGN0eSkpDQogIA0KYGBgDQoNCioqV2hhdCBoYXBwZW5zIGlmIHlvdSBtYXAgdGhlIHNhbWUgdmFyaWFibGUgdG8gbXVsdGlwbGUgYWVzdGhldGljcz8qKg0KDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBtcGcpICsgDQogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3ksIHNpemU9Y3R5LCBjb2xvcj1jdHkpKQ0KICANCmBgYA0KDQoqKldoYXQgZG9lcyB0aGUgc3Ryb2tlIGFlc3RoZXRpYyBkbz8gV2hhdCBzaGFwZXMgZG9lcyBpdCB3b3JrIHdpdGg/IChIaW50OiB1c2UgP2dlb21fcG9pbnQpKioNCg0KIFVzZSB0aGUgc3Ryb2tlIGFlc3RoZXRpYyB0byBtb2RpZnkgdGhlIHdpZHRoIG9mIHRoZQ0KYm9yZGVyDQoNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YT1tcGcpKw0KICBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHggPSBkaXNwbCwgeSA9IGh3eSwgc2l6ZT1jdHksIGNvbG9yPWN0eSxzdHJva2UgPSA1KSkNCmBgYA0KKipXaGF0IGhhcHBlbnMgaWYgeW91IG1hcCBhbiBhZXN0aGV0aWMgdG8gc29tZXRoaW5nIG90aGVyIHRoYW4gYSB2YXJpYWJsZSBuYW1lLCBsaWtlIGFlcyhjb2xvdXIgPSBkaXNwbCA8IDUpPyBOb3RlLCB5b3XigJlsbCBhbHNvIG5lZWQgdG8gc3BlY2lmeSB4IGFuZCB5LioqDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGE9bXBnKSsNCiAgZ2VvbV9wb2ludChtYXBwaW5nPWFlcyh4ID0gZGlzcGwsIHkgPSBod3ksIGNvbG9yPWRpc3BsPDUpKQ0KYGBgDQoNCioqMy41LjEgRVhFUkNJU0UqKg0KDQoqKldoYXQgaGFwcGVucyBpZiB5b3UgZmFjZXQgb24gYSBjb250aW51b3VzIHZhcmlhYmxlPyoqDQoNCmZhY2V0X3dyYXAoKSB3aWxsIHdvcmsgd2l0aCBjb250aW5vdXMgdmFyaWFibGUgYnV0IG5vdCBhcyB1c2VmdWwgYXMgd29ya2luZyB3aXRoIGNhdGVnb3JpY2FsIHZhcmlhYmxlLg0KDQpgYGB7cn0NCmdncGxvdChkYXRhPW1wZykrDQogIGdlb21fcG9pbnQobWFwcGluZz1hZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkrDQogIGZhY2V0X3dyYXAofmN0eSkNCmBgYA0KDQoqKldoYXQgZG8gdGhlIGVtcHR5IGNlbGxzIGluIHBsb3Qgd2l0aCBmYWNldF9ncmlkKGRydiB+IGN5bCkgbWVhbj8gSG93IGRvIHRoZXkgcmVsYXRlIHRvIHRoaXMgcGxvdD8qKg0KDQpObyBjb21iaW5hdGlvbiBvZiBkYXRhIHBvaW50cw0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXBnKSArIA0KICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRydiwgeSA9IGN5bCkpKw0KZmFjZXRfZ3JpZChkcnYgfiBjeWwpDQpgYGANCg0KDQoqKldoYXQgcGxvdHMgZG9lcyB0aGUgZm9sbG93aW5nIGNvZGUgbWFrZT8gV2hhdCBkb2VzIC4gZG8/KioNCg0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXBnKSArIA0KICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKw0KICBmYWNldF9ncmlkKGRydiB+IC4pDQpgYGANCg0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXBnKSArIA0KICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKw0KICBmYWNldF9ncmlkKC4gfiBjeWwpDQpgYGANCiBjb25jbHVzaW9uIA0KIA0Kcm93cyBhcmUgZmFjZXR0ZWQgYnkgdGhlIHZhcmlhYmxlIG9uIHRoZSBsZWZ0IGhhbmQgc2lkZSBvZiB+LiBhbmQgY29sdW1ucyBhcmUgZmFjZXR0ZWQgYnkgdGhlIHZhcmlhYmxlIG9uIHRoZSByaWdodCBoYW5kIHNpZGUgb2YgLn4gDQoNCg0KICAqKlRha2UgdGhlIGZpcnN0IGZhY2V0ZWQgcGxvdCBpbiB0aGlzIHNlY3Rpb246KioNCiAgDQpgYGB7cn0gIA0KZ2dwbG90KGRhdGEgPSBtcGcpICsgDQogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKSArIA0KICBmYWNldF93cmFwKH4gY2xhc3MsIG5yb3cgPSAyKQ0KYGBgICANCiAgDQoqKldoYXQgYXJlIHRoZSBhZHZhbnRhZ2VzIHRvIHVzaW5nIGZhY2V0aW5nIGluc3RlYWQgb2YgdGhlIGNvbG91ciBhZXN0aGV0aWM/IFdoYXQgYXJlIHRoZSBkaXNhZHZhbnRhZ2VzPyBIb3cgbWlnaHQgdGhlIGJhbGFuY2UgY2hhbmdlIGlmIHlvdSBoYWQgYSBsYXJnZXIgZGF0YXNldD8qKg0KDQoNCkFkdmFudGFnZXMgb2YgdXNpbmcgZmFjZXRpbmcgaXMgdGhhdCBpdCBpcyB1c2VmdWwgZm9yIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyx0byBzcGxpdCB5b3VyIHBsb3QgaW50byBmYWNldHMsIHN1YnBsb3RzIHRoYXQgZWFjaCBkaXNwbGF5IG9uZSBzdWJzZXQgb2YgdGhlIGRhdGEgbWFraW5nIGZvY3VzIG9uIHBhcnRpY3VsYXIgZmFjZXRzIGFsb25lLg0KaW4gY29udHJhc3QgY29sb3VyIGFlc3RoZXRpYyBoYXZpbmcgZGlzcGxheSBvZiBtdWx0aXBsZSBjb2xvcnMgd2l0aCBpbmNyZWFzZSBpbiBjYXRlZ29yaWNhbCBmZWF0dXJlcyBjYW4gY2F1c2UgY29uZnVzaW9uLg0KDQpEaXNhZHZhbnRhZ2Ugb2YgdXNpbmcgZmFjZXRpbmcgaXMgdGhhdCBzaW5jZSB0aGUgcG9pbnRzIGFyZSBvbiBzZXBhcmF0ZSBwbG90cyBkaXJlY3QgY29tcGFyaXNvbiBtYXkgbm90IGJlIGRpcmVjdA0KDQoNCioqUmVhZCA/ZmFjZXRfd3JhcC4gV2hhdCBkb2VzIG5yb3cgZG8/IFdoYXQgZG9lcyBuY29sIGRvPyBXaGF0IG90aGVyIG9wdGlvbnMgY29udHJvbCB0aGUgbGF5b3V0IG9mIHRoZSBpbmRpdmlkdWFsIHBhbmVscz8gV2h5IGRvZXNu4oCZdCBmYWNldF9ncmlkKCkgaGF2ZSBucm93IGFuZCBuY29sIGFyZ3VtZW50cz8qKg0KYGBge3J9DQo/ZmFjZXRfd3JhcCgpDQpgYGANCm5yb3csIG5jb2wJDQpOdW1iZXIgb2Ygcm93cyBhbmQgY29sdW1ucy4NCg0KKipXaGVuIHVzaW5nIGZhY2V0X2dyaWQoKSB5b3Ugc2hvdWxkIHVzdWFsbHkgcHV0IHRoZSB2YXJpYWJsZSB3aXRoIG1vcmUgdW5pcXVlIGxldmVscyBpbiB0aGUgY29sdW1ucy4gV2h5PyoqDQoNCg0KT25lIGxvZ2ljYWwgcmVhc29uIGlzIHRoYXQgc2luY2UgdGhlIGRlcGVuZGVudCB2YXJpYWJsZXMgYXJlIHVzdWFsbHkgcGxvdHRlZCBvbiB0aGUgeS1heGlzLCBpdCBpcyBtdWNoIGVhc2llciB0byBjb21wYXJlIHRoZSBoaWdocyBhbmQgbG93cyBhbmQgdGhlIHRyZW5kcyBvZiB0aGUgdmFyaWFibGVzIGlmIHRoZSBwbG90cyBhcmUgcGxhY2VkIHNpZGUgYnkgc2lkZS4NCg0KDQoNCg0KDQoqKkVYRVJDSVNFIDMuNi4xKioNCg0KKipXaGF0IGdlb20gd291bGQgeW91IHVzZSB0byBkcmF3IGEgbGluZSBjaGFydD8gQSBib3hwbG90PyBBIGhpc3RvZ3JhbT8gQW4gYXJlYSBjaGFydD8qKg0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXBnKSsNCiAgZ2VvbV9saW5lKG1hcHBpbmc9YWVzKHg9ZGlzcGwseT1od3kpKQ0KICANCmBgYA0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IG1wZykrDQogIGdlb21fYm94cGxvdChtYXBwaW5nPWFlcyh4PWRpc3BsLHk9aHd5LGNvbG9yPWRydikpDQogIA0KYGBgDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXBnKSsNCiAgZ2VvbV9oaXN0b2dyYW0oKQ0KICANCmBgYA0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IG1wZykrDQogIGdlb21fYXJlYShtYXBwaW5nPWFlcyh4PWRpc3BsLHk9aHd5KSkNCiAgDQpgYGANCg0KKipSdW4gdGhpcyBjb2RlIGluIHlvdXIgaGVhZCBhbmQgcHJlZGljdCB3aGF0IHRoZSBvdXRwdXQgd2lsbCBsb29rIGxpa2UuIFRoZW4sIHJ1biB0aGUgY29kZSBpbiBSIGFuZCBjaGVjayB5b3VyIHByZWRpY3Rpb25zLioqDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBtcGcsIG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBjb2xvciA9IGRydikpICsgDQogIGdlb21fcG9pbnQoKSArIA0KICBnZW9tX3Ntb290aChzZSA9IEZBTFNFKQ0KYGBgDQoNCg0KKipXaGF0IGRvZXMgc2hvdy5sZWdlbmQgPSBGQUxTRSBkbz8qKg0KDQpSZW1vdmVzIGEgbGVnZW5kIHRoYXQgaXMgdXNlZCB0byBleHBsYWluIHdoaWNoIGxldmVscyBjb3JyZXNwb25kIHRvIHdoaWNoIHZhbHVlcy4NCg0KKipXaGF0IGhhcHBlbnMgaWYgeW91IHJlbW92ZSBpdD8qKiANCg0KWW91IHNlZSB0aGUgbGVnZW5kIGV4cGxhaW5pbmcgd2hhdCBsZXZlbHMgY29ycmVzcG9uZCB0byB3aGljaCB2YWx1ZXMuDQoNCioqV2h5IGRvIHlvdSB0aGluayBJIHVzZWQgaXQgZWFybGllciBpbiB0aGUgY2hhcHRlcj8qKg0KDQpUbyBzaG93IGhvdyB0byByZW1vdmUgYSBsZWdlbmQNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IG1wZykgKw0KICBnZW9tX3Ntb290aCgNCiAgICBtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSwgY29sb3IgPSBkcnYpLA0KICAgIHNob3cubGVnZW5kID0gRkFMU0UNCiAgKQ0KYGBgDQoqKldoYXQgZG9lcyB0aGUgc2UgYXJndW1lbnQgdG8gZ2VvbV9zbW9vdGgoKSBkbyoqDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBtcGcsIG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBjb2xvciA9IGRydikpICsgDQogIGdlb21fcG9pbnQoKSArIA0KICBnZW9tX3Ntb290aChzZSA9IEZBTFNFKQ0KYGBgDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSwgY29sb3IgPSBkcnYpKSArIA0KICBnZW9tX3BvaW50KCkgKyANCiAgZ2VvbV9zbW9vdGgoc2UgPSBUUlVFKQ0KYGBgDQoqKkFOU1dFUioqDQoNCnNlIGFyZ3VtZW50IHRvIGdlb21fc21vb3RoKCkgIGRvZXMgYWRkIGEgbG9lc3Mgc21vb3RoDQoNCioqV2lsbCB0aGVzZSB0d28gZ3JhcGhzIGxvb2sgZGlmZmVyZW50PyBXaHkvd2h5IG5vdD8qKg0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpICsgDQogIGdlb21fcG9pbnQoKSArIA0KICBnZW9tX3Ntb290aCgpDQpgYGANCmBgYHtyfQ0KZ2dwbG90KCkgKyANCiAgZ2VvbV9wb2ludChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpICsgDQogIGdlb21fc21vb3RoKGRhdGEgPSBtcGcsIG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkNCmBgYA0KICANCiAgDQoqKkFOU1dFUioqDQoNCk5vIHRoZSB0d28gZ3JhcGhzIHdpbGwgbm90IGxvb2sgZGlmZmVyZW50LCBiZWNhdXNlIGJvdGggZnVuY3Rpb25zIHByb2R1Y2UgdGhlIHNhbWUgb3V0cHV0LiBUaGUgZGlmZmVyZW5jZSBpcyB0aGF0IHNlY29uZCBmdW5jdGlvbiBvbmx5IGRvZXMgZHVwbGljYXRpb24gaW4geW91ciBjb2RlLCB3aGljaCBpcyBub3QgZ29vZCBwcmFjdGljZSBmb3Igb2J0YWluaW5nIGNsZWFuIGNvZGUuDQoNCioqUmVjcmVhdGUgdGhlIFIgY29kZSBuZWNlc3NhcnkgdG8gZ2VuZXJhdGUgdGhlIGZvbGxvd2luZyBncmFwaHMuKioNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IG1wZywgbWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKSArIA0KICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoc2l6ZT1jbGFzcyksc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyANCiAgZ2VvbV9zbW9vdGgoc2U9RkFMU0UpDQpgYGANCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBtcGcsIG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKyANCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHNpemU9Y2xhc3MpLHNob3cubGVnZW5kID0gRkFMU0UpICsgDQogIGdlb21fc21vb3RoKG1hcHBpbmcgPSBhZXMoZ3JvdXA9ZHJ2KSxzZT1GQUxTRSkNCmBgYA0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IG1wZywgbWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3ksY29sb3I9ZHJ2KSkgKyANCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHNpemU9Y2xhc3MpLHNob3cubGVnZW5kID0gVFJVRSkgKyANCiAgZ2VvbV9zbW9vdGgoc2hvdy5sZWdlbmQgPSBUUlVFLHNlPUZBTFNFKQ0KYGBgDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpICsgDQogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyhjb2xvcj1kcnYsc2l6ZT1jbGFzcyksc2hvdy5sZWdlbmQgPSBUUlVFKSArIA0KICBnZW9tX3Ntb290aChzaG93LmxlZ2VuZCA9IEZBTFNFLHNlPUZBTFNFKQ0KYGBgDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpICsgDQogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyhjb2xvcj1kcnYsc2l6ZT1jbGFzcyksc2hvdy5sZWdlbmQgPSBUUlVFKSArIA0KICBnZW9tX3Ntb290aChtYXBwaW5nID0gYWVzKGxpbmV0eXBlPWRydiksc2hvdy5sZWdlbmQgPSBUUlVFLHNlPUZBTFNFKQ0KYGBgDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSxzaXplPWNsYXNzKSkgKyANCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKGNvbG9yPWRydiksc2hvdy5sZWdlbmQgPSBUUlVFKSANCiANCmBgYA0KDQoqKkVYRVJDSVNFIDMuNy4xKioNCg0KKipXaGF0IGlzIHRoZSBkZWZhdWx0IGdlb20gYXNzb2NpYXRlZCB3aXRoIHN0YXRfc3VtbWFyeSgpPyBIb3cgY291bGQgeW91IHJld3JpdGUgdGhlIHByZXZpb3VzIHBsb3QgdG8gdXNlIHRoYXQgZ2VvbSBmdW5jdGlvbiBpbnN0ZWFkIG9mIHRoZSBzdGF0IGZ1bmN0aW9uPyoqDQoNCmBgYHtyfQ0KP3N0YXRfc3VtbWFyeShnZW9tID0gInBvaW50cmFuZ2UiKQ0KYGBgDQoqKlBSRVZJT1VTIFBMT1QqKg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzKSArIA0KICBzdGF0X3N1bW1hcnkoDQogICAgbWFwcGluZyA9IGFlcyh4ID0gY3V0LCB5ID0gZGVwdGgpLA0KICAgIGZ1bi55bWluID0gbWluLA0KICAgIGZ1bi55bWF4ID0gbWF4LA0KICAgIGZ1bi55ID0gbWVkaWFuDQogICkNCmBgYA0KDQoqKlNPTFVUSU9OKioNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzKSArDQogIGdlb21fcG9pbnRyYW5nZShtYXBwaW5nID0gYWVzKHggPSBjdXQsIHkgPSBkZXB0aCksDQogICAgICAgICAgICAgICAgICBzdGF0ID0gInN1bW1hcnkiLA0KICAgICAgICAgICAgICAgICAgZnVuLnltaW4gPSBtaW4sDQogICAgICAgICAgICAgICAgICBmdW4ueW1heCA9IG1heCwNCiAgICAgICAgICAgICAgICAgIGZ1bi55ID0gbWVkaWFuKQ0KYGBgDQpgYGB7cn0NCj9zdGF0X3N1bW1hcnkoKQ0KYGBgDQoNCioqV2hhdCBkb2VzIGdlb21fY29sKCkgZG8/IEhvdyBpcyBpdCBkaWZmZXJlbnQgdG8gZ2VvbV9iYXIoKT8qKg0KYGBge3J9DQo/Z2VvbV9iYXINCmBgYA0KKipBTlNXRVIqKg0KDQpnZW9tX2JhcigpIG1ha2VzIHRoZSBoZWlnaHQgb2YgdGhlIGJhciBwcm9wb3J0aW9uYWwgdG8gdGhlIG51bWJlciBvZiBjYXNlcyBpbiBlYWNoIGdyb3VwIChvciBpZiB0aGUgd2VpZ2h0IGFlc3RoZXRpYyBpcyBzdXBwbGllZCwgdGhlIHN1bSBvZiB0aGUgd2VpZ2h0cykuIElmIHlvdSB3YW50IHRoZSBoZWlnaHRzIG9mIHRoZSBiYXJzIHRvIHJlcHJlc2VudCB2YWx1ZXMgaW4gdGhlIGRhdGEsIHVzZSBnZW9tX2NvbCgpIGluc3RlYWQuIGdlb21fYmFyKCkgdXNlcyBzdGF0X2NvdW50KCkgYnkgZGVmYXVsdDogaXQgY291bnRzIHRoZSBudW1iZXIgb2YgY2FzZXMgYXQgZWFjaCB4IHBvc2l0aW9uLiBnZW9tX2NvbCgpIHVzZXMgc3RhdF9pZGVudGl0eSgpOiBpdCBsZWF2ZXMgdGhlIGRhdGEgYXMgaXMuDQoNCioqTW9zdCBnZW9tcyBhbmQgc3RhdHMgY29tZSBpbiBwYWlycyB0aGF0IGFyZSBhbG1vc3QgYWx3YXlzIHVzZWQgaW4gY29uY2VydC4gUmVhZCB0aHJvdWdoIHRoZSBkb2N1bWVudGF0aW9uIGFuZCBtYWtlIGEgbGlzdCBvZiBhbGwgdGhlIHBhaXJzLiBXaGF0IGRvIHRoZXkgaGF2ZSBpbiBjb21tb24/KioNCmBgYHtyfQ0KP2dlb21fYmFyDQpgYGANCmdlb21fYmFyKG1hcHBpbmcgPSBOVUxMLCBkYXRhID0gTlVMTCwgc3RhdCA9ICJjb3VudCIsDQogIHBvc2l0aW9uID0gInN0YWNrIiwgLi4uLCB3aWR0aCA9IE5VTEwsIGJpbndpZHRoID0gTlVMTCwNCiAgbmEucm0gPSBGQUxTRSwgc2hvdy5sZWdlbmQgPSBOQSwgaW5oZXJpdC5hZXMgPSBUUlVFKQ0KICANCnN0YXRfY291bnQobWFwcGluZyA9IE5VTEwsIGRhdGEgPSBOVUxMLCBnZW9tID0gImJhciIsDQogIHBvc2l0aW9uID0gInN0YWNrIiwgLi4uLCB3aWR0aCA9IE5VTEwsIG5hLnJtID0gRkFMU0UsDQogIHNob3cubGVnZW5kID0gTkEsIGluaGVyaXQuYWVzID0gVFJVRSkNCkJvdGggc3RhdF9jb3VudCAmIGdlb21fYmFyKCkgdW5kZXJzdGFuZHMgdGhlIGZvbGxvd2luZyBhZXN0aGV0aWNzOiAgeCwgeSwgYWxwaGEsIGNvbG91ciwgZmlsbCwgZ3JvdXAsIGxpbmV0eXBlICYgc2l6ZS4NCg0KKipXaGF0IHZhcmlhYmxlcyBkb2VzIHN0YXRfc21vb3RoKCkgY29tcHV0ZT8gV2hhdCBwYXJhbWV0ZXJzIGNvbnRyb2wgaXRzIGJlaGF2aW91cj8qKg0KYGBge3J9DQo/c3RhdF9zbW9vdGgNCmBgYA0KDQp5IC0gcHJlZGljdGVkIHZhbHVlDQoNCnltaW4gLSBsb3dlciBwb2ludHdpc2UgY29uZmlkZW5jZSBpbnRlcnZhbCBhcm91bmQgdGhlIG1lYW4NCg0KeW1heCAtIHVwcGVyIHBvaW50d2lzZSBjb25maWRlbmNlIGludGVydmFsIGFyb3VuZCB0aGUgbWVhbg0KDQpzZSAtIHN0YW5kYXJkIGVycm9yDQoNCg0KDQoqKkluIG91ciBwcm9wb3J0aW9uIGJhciBjaGFydCwgd2UgbmVlZCB0byBzZXQgZ3JvdXAgPSAxLiBXaHk/IEluIG90aGVyIHdvcmRzIHdoYXQgaXMgdGhlIHByb2JsZW0gd2l0aCB0aGVzZSB0d28gZ3JhcGhzPyoqDQoNClRIRSBQTE9UIElTIFdST05HOiB3aGVuIHlvdSBleGNsdWRlIHRoZSAiZ3JvdXAgPSAxIiANCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzKSArIA0KICBnZW9tX2JhcihtYXBwaW5nID0gYWVzKHggPSBjdXQsIHkgPSAuLnByb3AuLikpDQpgYGANCg0KVEhFIFBMT1QgSVMgV1JPTkcgd2hlbjogeW91IGV4Y2x1ZGUgdGhlICJncm91cCA9IDEiIA0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gZGlhbW9uZHMpICsgDQogIGdlb21fYmFyKG1hcHBpbmcgPSBhZXMoeCA9IGN1dCwgZmlsbCA9IGNvbG9yLCB5ID0gLi5wcm9wLi4pKQ0KYGBgDQoNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzKSArIA0KICBnZW9tX2JhcihtYXBwaW5nID0gYWVzKHggPSBjdXQsIHkgPSAuLnByb3AuLixncm91cD0xKSkNCmBgYA0KKipBTlNXRVIqKg0KDQp3ZSBuZWVkIHRvIHNldCBncm91cD0iMSIgdG8gb3ZlcnJpZGUgdGhlIGRlZmF1bHQgYmVoYXZpb3IsIHdoaWNoIGhlcmUgaXMgdG8gZ3JvdXAgYnkgY3V0IGFuZCBpbiBnZW5lcmFsIGlzIHRvIGdyb3VwIGJ5IHRoZSB4IHZhcmlhYmxlLiAgRm9yIGV4YW1wbGUsIGhlcmUsIHRoZSBkZWZhdWx0IHdvdWxkIGJlIGZvciBnZW9tX2JhciB0byByZXR1cm4gdGhlIG51bWJlciBvZiByb3dzIHdpdGggY3V0IGVxdWFsIHRvICJGYWlyIiwgIkdvb2QiLCBldGMuDQoNCioqMy44LjEgRXhlcmNpc2VzKioNCg0KDQoqKldoYXQgaXMgdGhlIHByb2JsZW0gd2l0aCB0aGlzIHBsb3Q/IEhvdyBjb3VsZCB5b3UgaW1wcm92ZSBpdD8qKg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IG1wZywgbWFwcGluZyA9IGFlcyh4ID0gY3R5LCB5ID0gaHd5KSkgKyANCiAgZ2VvbV9wb2ludCgpDQpgYGANCkFuc3dlcg0KDQpjdHkgJiBod3kgIGFyZSByb3VuZGVkIHNvIHRoZSBwb2ludHMgYXBwZWFyIG9uIGEgZ3JpZCBhbmQgbWFueSBwb2ludHMgb3ZlcmxhcCBlYWNoIG90aGVyLg0KDQoqKldoYXQgcGFyYW1ldGVycyB0byBnZW9tX2ppdHRlcigpIGNvbnRyb2wgdGhlIGFtb3VudCBvZiBqaXR0ZXJpbmc/KioNCg0KYGBge3J9DQo/Z2VvbV9qaXR0ZXINCmBgYA0KQU5TV0VSDQoNCkhlaWdodCAmIFdlaWdodA0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBjdHksIHkgPSBod3kpKSArIA0KICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuNSwgaGVpZ2h0ID0gMC41KQ0KYGBgDQoNCioqQ29tcGFyZSBhbmQgY29udHJhc3QgZ2VvbV9qaXR0ZXIoKSB3aXRoIGdlb21fY291bnQoKS4qKg0KDQpgYGB7cn0NCj9nZW9tX2NvdW50DQpgYGANCg0KVGhpcyBpcyBhIHZhcmlhbnQgZ2VvbV9wb2ludCgpIHRoYXQgY291bnRzIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGF0IGVhY2ggbG9jYXRpb24sIHRoZW4gbWFwcyB0aGUgY291bnQgdG8gcG9pbnQgYXJlYS4gSXQgdXNlZnVsIHdoZW4geW91IGhhdmUgZGlzY3JldGUgZGF0YSBhbmQgb3ZlcnBsb3R0aW5nLg0KDQpgYGB7cn0NCj9nZW9tX2ppdHRlcg0KYGBgDQpUaGUgaml0dGVyIGdlb20gaXMgYSBjb252ZW5pZW50IHNob3J0Y3V0IGZvciBnZW9tX3BvaW50KHBvc2l0aW9uID0gImppdHRlciIpLiBJdCBhZGRzIGEgc21hbGwgYW1vdW50IG9mIHJhbmRvbSB2YXJpYXRpb24gdG8gdGhlIGxvY2F0aW9uIG9mIGVhY2ggcG9pbnQsIGFuZCBpcyBhIHVzZWZ1bCB3YXkgb2YgaGFuZGxpbmcgb3ZlcnBsb3R0aW5nIGNhdXNlZCBieSBkaXNjcmV0ZW5lc3MgaW4gc21hbGxlciBkYXRhc2V0cy4NCg0KKipXaGF04oCZcyB0aGUgZGVmYXVsdCBwb3NpdGlvbiBhZGp1c3RtZW50IGZvciBnZW9tX2JveHBsb3QoKT8gQ3JlYXRlIGEgdmlzdWFsaXNhdGlvbiBvZiB0aGUgbXBnIGRhdGFzZXQgdGhhdCBkZW1vbnN0cmF0ZXMgaXQuKioNCg0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXBnKSArDQogIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKHkgPSBkaXNwbCwgeCA9IGRydiwgY29sb3IgPSBmYWN0b3IoeWVhcikpKQ0KDQpgYGANCg0KDQoNCioqMy45LjEgRXhlcmNpc2VzKioNCg0KDQoqKlR1cm4gYSBzdGFja2VkIGJhciBjaGFydCBpbnRvIGEgcGllIGNoYXJ0IHVzaW5nIGNvb3JkX3BvbGFyKCkuKioNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcykgKyANCiAgZ2VvbV9iYXIobWFwcGluZyA9IGFlcyh4ID0gY3V0LCBmaWxsID0gY2xhcml0eSkpICsNCiAgY29vcmRfcG9sYXIoKQ0KYGBgDQoNCioqV2hhdCBkb2VzIGxhYnMoKSBkbz8gUmVhZCB0aGUgZG9jdW1lbnRhdGlvbi4qKg0KYGBge3J9DQo/bGFicw0KYGBgDQpNb2RpZnkgYXhpcywgbGVnZW5kLCBhbmQgcGxvdCBsYWJlbHMNCg0KKipXaGF04oCZcyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGNvb3JkX3F1aWNrbWFwKCkgYW5kIGNvb3JkX21hcCgpPyoqDQoNCmBgYHtyfQ0KP2Nvb3JkX3F1aWNrbWFwDQpgYGANCg0KYGBge3J9DQo/Y29vcmRfbWFwDQpgYGANCg0KY29vcmRfbWFwIHByb2plY3RzIGEgcG9ydGlvbiBvZiB0aGUgZWFydGgsIHdoaWNoIGlzIGFwcHJveGltYXRlbHkgc3BoZXJpY2FsLCBvbnRvIGEgZmxhdCAyRCBwbGFuZSB1c2luZyBhbnkgcHJvamVjdGlvbiBkZWZpbmVkIGJ5IHRoZSBtYXBwcm9qIHBhY2thZ2UuIE1hcCBwcm9qZWN0aW9ucyBkbyBub3QsIGluIGdlbmVyYWwsIHByZXNlcnZlIHN0cmFpZ2h0IGxpbmVzLCBzbyB0aGlzIHJlcXVpcmVzIGNvbnNpZGVyYWJsZSBjb21wdXRhdGlvbi4gY29vcmRfcXVpY2ttYXAgaXMgYSBxdWljayBhcHByb3hpbWF0aW9uIHRoYXQgZG9lcyBwcmVzZXJ2ZSBzdHJhaWdodCBsaW5lcy4gSXQgd29ya3MgYmVzdCBmb3Igc21hbGxlciBhcmVhcyBjbG9zZXIgdG8gdGhlIGVxdWF0b3IuDQoNCg0KDQoqKldoYXQgZG9lcyB0aGUgcGxvdCBiZWxvdyB0ZWxsIHlvdSBhYm91dCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gY2l0eSBhbmQgaGlnaHdheSBtcGc/IFdoeSBpcyBjb29yZF9maXhlZCgpIGltcG9ydGFudD8gV2hhdCBkb2VzIGdlb21fYWJsaW5lKCkgZG8/KioNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IG1wZywgbWFwcGluZyA9IGFlcyh4ID0gY3R5LCB5ID0gaHd5KSkgKw0KICBnZW9tX3BvaW50KCkgKyANCiAgZ2VvbV9hYmxpbmUoKSArDQogIGNvb3JkX2ZpeGVkKCkNCmBgYA0KDQpUaGUgcGxvdCBzaG93cyB1cyB0aGF0IHRoZXJlIGlzIGEgcG9zaXRpdmUgbGluZWFyIHRyZW5kIGJldHdlZW4gaHd5IGFuZCBjdHksIGFuZCB0aGUgc2xvcGUgaXMgYXBwcm94aW1hdGVseSBjbG9zZSAxLCBtZWFuaW5nIHRoYXQgYSB1bml0IGluY3JlYXNlIGluIGN0eSBpcyBhc3NvY2lhdGVkIHdpdGggYSB1bml0IGluY3JlYXNlIGluIGh3eS4NCg0KYGBge3J9DQo/Y29vcmRfZml4ZWQoKQ0KYGBgDQpjb29yZF9maXhlZCBmb3JjZXMgYSBzcGVjaWZpZWQgYXNwZWN0IHJhdGlvIGJldHdlZW4gdGhlIHBoeXNpY2FsIHJlcHJlc2VudGF0aW9uIG9mIHRoZSB1bml0cyBvbiB0aGUgYXhlcy4gVGhlIHJhdGlvIGlzIDEgYnkgZGVmYXVsdC4gSXQgaXMgaW1wb3J0YW50IHRvIGZpeCB0aGUgYXNwZWN0IHJhdGlvIGluIHRoaXMgY2FzZSBiZWNhdXNlIGh3eSBhbmQgY3R5IGFyZSBtZWFzdXJlZCBpbiB0aGUgc2FtZSB1bml0IChtaWxlcyBwZXIgZ2FsbG9uKS4gQW55IG90aGVyIGFzcGVjdCByYXRpb3Mgd2lsbCBnaXZlIGEgdmlzdWFsbHkgaW5jb3JyZWN0IHJlcHJlc2VudGF0aW9uIGFuZCBtaWdodCBsZWFkIHVzIHRvIGJlbGlldmUgdGhhdCBvbmUgaW5jcmVhc2VzZSBhdCBhIGZhc3RlciByYXRlIHRoYW4gdGhlIG90aGVyLg0KYGBge3J9DQo/Z2VvbV9hYmxpbmUoKQ0KYGBgDQpnZW9tX2FibGluZSgpIGFkZHMgYSBkaWFnb25hbCByZWZlcmVuY2UgbGluZSB0byB0aGUgcGxvdCwgdGh1cyBhbGxvd3MgdXMNCg0KDQoNCg0KDQoqKjQuNCBQcmFjdGljZSoqDQoNCioqV2h5IGRvZXMgdGhpcyBjb2RlIG5vdCB3b3JrPyoqDQoNCmBgYHtyfQ0KbXlfdmFyaWFibGUgPC0gMTANCm15X3ZhcmxhYmxlDQpgYGANCg0KKipMb29rIGNhcmVmdWxseSEgKFRoaXMgbWF5IHNlZW0gbGlrZSBhbiBleGVyY2lzZSBpbiBwb2ludGxlc3NuZXNzLCBidXQgdHJhaW5pbmcgeW91ciBicmFpbiB0byBub3RpY2UgZXZlbiB0aGUgdGluaWVzdCBkaWZmZXJlbmNlIHdpbGwgcGF5IG9mZiB3aGVuIHByb2dyYW1taW5nLikqKg0KDQpvYmplY3QgY2FsbGVkIGluIHRoZSBzZWNvbmQgbGluZSBkb2VzIG5vdCBtYXRjaCB0aGUgb2JqZWN0IG5hbWUNCg0KDQoqKlR3ZWFrIGVhY2ggb2YgdGhlIGZvbGxvd2luZyBSIGNvbW1hbmRzIHNvIHRoYXQgdGhleSBydW4gY29ycmVjdGx5OioqDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQoNCmdncGxvdChkYXRhID0gbXBnKSArIA0KICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkNCg0KZmxpdGVyKG1wZywgY3lsID0gOCkNCmZpbHRlcihkaWFtb25kLCBjYXJhdCA+IDMpDQpgYGANCg0KDQoNCioqUHJlc3MgQWx0ICsgU2hpZnQgKyBLLiBXaGF0IGhhcHBlbnM/IEhvdyBjYW4geW91IGdldCB0byB0aGUgc2FtZSBwbGFjZSB1c2luZyB0aGUgbWVudXM/KioNCg0KDQpgYGB7cn0NCg0KYGBgDQoNCmtleWJvYXJkIHNob3J0Y3V0IHJlZmVyZW5jZSBtZW51IGFwcHBlYXJzDQoNCnlvdSBjYW4gZ2V0IHRoZSBzYW1lIHBsYWNlIHVzaW5nIHRoZSBUb29scyBtZW51IHRoZW4gY2hvb3NpbmcgdGhlIGtleWJvYXJkIHNob3J0Y3V0IGhlbHAuDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=