EXERCISE 3.2.4

library(tidyverse)
Registered S3 method overwritten by 'dplyr':
  method           from
  print.rowwise_df     
-- Attaching packages --------------------------------------- tidyverse 1.2.1 --
v ggplot2 3.2.1     v purrr   0.3.3
v tibble  2.1.3     v dplyr   0.8.3
v tidyr   1.0.0     v stringr 1.4.0
v readr   1.3.1     v forcats 0.4.0
-- Conflicts ------------------------------------------ tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()

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=