Code
Getting started with GGPLOT
This time I’m using the robobook markdown theme, in case you want to copy it. (Download the code to get the stuff you put at the top.) The libraries used are:
knitr tidyverse janitor scales
rmdformats lubridate forcats
The data
This data was created from an early version of the Paycheck Protection Program given in Arizona during the pandemic. Here are the column definitions
zcta
char
Zip code used in the census
zipcode_city
char
Census name associated with that zip code
st_county_fip
char
FIP code for the county
ethnicity
factor
County demographic: “No people”, “White”, “No distinct group”, “Black”, “Hispanic”, “Native American” based on a formula used by the Urban institute to categorize counties.
lender_grp
factor
The top 100 lenders are named and all others lumped into “Other”; the biggest one will come first.
loans
num
# of loans by zip code and lender group
amount
num
$ amount of the loans, according to the initial amount reported, by zip code and lender group
usps_businesses
num
total number of business addresses in the zip code
A factor works like words, but is always shown in the order you specify, so you don’t have to worry about it alphabetizing everything by default. It’s particularly useful for charting.
A searchable, sortable table
Here’s what the first 20 rows look like:
Our first chart
One of the first things a lot of people do with a large dataset is to create a histogram of the data. One example is the number of loans by zip code.
Create a histogram
Alberto Cairo talked about the elements of a data visualization: The container, the encodings and the annotations. The first thing to do is to create the container. But first, we need to get the data that we want into the form needed for a visualization. We aggregate the lenders to zip code totals, and compute the percentage of businesses that got loans. I’ll cap it at 500 % because there were some places with more loans than there were businesses - a result of not having a good list of the eligible businesses for the program.
pppdata <-
ppp %>%
group_by ( zcta, ethnicity) %>%
summarise ( loans = sum (loans) , businesses = first (usps_businesses)) %>%
mutate ( pct_loans = pmin (loans / businesses, 5 ))
my_histogram <-
ggplot ( data= pppdata,
mapping = aes ( x = pct_loans ))
produces:
Nothing! That’s because we haven’t yet told R what geometry to use. The choice here is geom_histogram()
Add an encoding
my_histogram +
geom_histogram ( )
Fix bins and colors
This is hard to read, not just because I haven’t formatted the percentages (so 5 = 500% of all businesses got loans), but because of the default number of “bins” chosen. Here’s how to clean that up a little. At the same time, I’ll apply a theme that’s a little easier to read, and set the outline (color) and the fill so that the bars aren’t smushed together.
The “Colors of R ” document from Columbia University shows you the names of the colors you can use.
my_histogram +
geom_histogram ( binwidth= .1 , color= "white" , fill= "slategrey" ) +
theme_light ()
All I did was make the bins 10% of each zip code’s businesses (0.1)
That’s ok, but let’s make the labels more accurate:
my_histogram +
geom_histogram ( binwidth= .1 , color= "white" , fill= "slategrey" ) +
theme_light () +
scale_x_continuous ( labels= percent) +
labs (x= "% of busineses that got loans (maxed at 500%)" , y = "# of zip codes" )
Adding complexity
Say we want to see a histogram by the ethnicity of the zip code. We might choose to see a different plot for each one. When we walk through it, I’ll go through each of these items one by one. I’ve started with a blank plot with data for each zip code, showing the number of loans and the ethnicity of each zip code.
myplot <-
pppdata %>%
filter ( ethnicity %in% c ("White" , "Hispanic" , "Native American" )) %>%
ggplot ( )
Building this a little at a time, we can save the plot and keep working on it later. The only thing changed was the “fill” color – an encoding - to be set by ethnicity of the zip code.
myplot <-
myplot +
( aes ( x= pct_loans , fill= ethnicity)) +
geom_histogram ( binwidth= .1 ,
color= "gray" ) +
scale_x_continuous ( labels= percent)
myplot
That’s not very helpful. Instead, let’s try it by putting each ethnicity on its own scale and graphic, using facets. facet_grid creates a grid of graphis, with the variable on the left defining the rows and the variable on the right of the tilde as the variable for columns. Use a period to leave either one blank.
myplot +
facet_grid ( ethnicity ~ . )
This still isn’t that helpful - the scales are the same, making it hard to see the shape. This time, we’ll save it and then print it out, meaning we don’t have to type anything over again:
myplot <-
myplot +
facet_grid ( ethnicity ~ . , scales= "free_y" )
myplot
Finally, let’s get rid of the legend and use some other colors :
myplot +
theme_light () +
theme ( legend.position= "none" ) +
scale_fill_hue ()
A bar chart
Remember that people can see the difference between lenghts of lines better than angles or sizes of circles. That’s why you see so many bar charts. Let’s take the top 50 lenders and compare the number of loans each has made.
lender_totals <-
ppp %>%
group_by ( lender_grp, ethnicity ) %>%
summarise ( loans = sum (loans)) %>%
filter ( as.integer ( lender_grp) <= 50 )
This creates a sideways bar chart, which is easier to read with a lot of categories (lenders) and long names:
lender_totals %>%
group_by ( lender_grp) %>%
summarise (loans = sum (loans)) %>%
ggplot ( aes ( x= lender_grp, y= loans)) +
geom_bar ( position= position_dodge (), stat= "identity" ) +
guides (fill = FALSE ) +
labs (x = NULL ) +
coord_flip ()
One useful thing is to reverse the order of the graphic so the biggest are on top.
myplot <-
lender_totals %>%
group_by ( lender_grp) %>%
summarise (loans = sum (loans)) %>%
ggplot ( aes ( x= reorder ( lender_grp, loans) , y= loans)) +
geom_bar ( position= position_dodge (), stat= "identity" ) +
guides (fill = "none" ) +
labs (x = "Company" ) +
coord_flip () +
theme_bw ()
myplot
Dot plots
Percentages by ethnicity
We’d like to compare the percent of loans within each lender for Hispanic, White and Native American zip codes. This is when we really can use the power of the facets to create a grid tiny little graphics – one for each lender!
Set up the data
Our goal is to make a series of little dot plots, sometimes called “Cleveland” plots, after the statistical expert that invented the small multiple faceted approach.
To see a big version, let’s make a Cleveland dot plot of the above bar chart instead. This time, the name of the bank is the Y axis and the number of loans is the x axis.
myplot <-
lender_totals %>%
group_by ( lender_grp) %>%
summarise (loans = sum (loans)) %>%
ggplot ( aes ( y= reorder ( lender_grp, loans) , x= loans)) +
geom_point (size= 4 , color= "black" ) +
theme_bw () +
theme ( panel.grid.major.x = element_blank (),
panel.grid.minor.x = element_blank (),
panel.grid.major.y = element_line (color = "gray60" , linetype= "dotted" )) +
labs ( y= NULL ) +
scale_x_continuous ( label= scales:: comma)
myplot
Facets for ethnicity
That was easy enough, and is even a little easier to read than the bars. Now let’s make three of them – one for each ethnicity. This time, we’ll just take the same thing, but start with the data that includes ethnicity
ppp %>%
group_by ( lender_grp, ethnicity) %>%
filter ( as.integer (lender_grp) <= 50 , ethnicity %in% c ("White" , "Native American" , "Hispanic" )) %>%
summarise (loans = sum (loans), .groups= "drop" ) %>%
ggplot ( aes ( y= reorder ( lender_grp, desc (lender_grp) ) , x= loans, color= ethnicity)) +
geom_point (size= 2 , color= "black" ) +
facet_grid ( . ~ ethnicity , scales= "free_x" ) +
theme_bw () +
scale_color_brewer () +
theme ( panel.grid.major.x = element_blank (),
panel.grid.minor.x = element_blank (),
panel.grid.major.y = element_line (color = "gray60" , linetype= "dotted" )) +
labs ( y= NULL ) +
scale_x_continuous ( label= scales:: comma)
But this isn’t very useful, since it is the raw number of loans for each. Instead, we’ll convert the data so that, within each ethnicity, we show the market share (the percent of all loans) that went to each group.
totals <-
az_ppp_zip %>%
group_by ( census_zip) %>%
summarise ( loans = n (), lenders= n_distinct (lender), amount= sum (initial_amt)) %>%
right_join ( az_by_zipcode, by= c ("census_zip" = "zcta" ) ) %>%
select ( zcta= census_zip, zcta_ethnic, loans: amount, usps_businesses) %>%
mutate (ethnicity = ordered (zcta_ethnic),
loans = replace_na (loans, 0 )) %>%
group_by (ethnicity) %>%
summarise ( ethnic_loans = sum (loans) , ethnic_businesses= sum (usps_businesses, na.rm= T), .groups= "drop" ) %>%
mutate ( grand_total_loans = sum (ethnic_loans), grand_total_businesses = sum (ethnic_businesses),
ethnic_pct = ethnic_loans / grand_total_loans)
levels (totals$ ethnicity) <- c ("No people" , "White" , "No distinct group" , "Black" , "Hispanic" , "Native American" )
options (scipen= 999 )
loan_percentages <-
ppp %>%
group_by ( ethnicity, lender_grp ) %>%
summarise ( loans = sum (loans) , .groups= "drop" ) %>%
left_join ( totals, by= "ethnicity" ) %>%
group_by ( lender_grp ) %>%
mutate ( lender_pct = loans / sum (loans),
lender_ratio = lender_pct / ethnic_pct ) %>%
filter ( as.integer (lender_grp) <= 50 , ethnicity %in% c ("White" , "Native American" , "Hispanic" )) %>%
ungroup () %>%
select ( ethnicity: ethnic_loans, ethnic_pct, lender_pct, lender_ratio) %>%
mutate (capped_ratio = pmin (lender_ratio, 1.25 ),
capped_ratio = pmax (capped_ratio, .4 ))
Make facets by zip code ethnicity
loan_percentages %>%
ggplot ( aes ( y= reorder ( lender_grp, desc (lender_grp) ) , x= capped_ratio, color= ethnicity)) +
geom_point (size= 2 ) +
facet_grid ( . ~ ethnicity , scales= "free_x" ) +
theme_bw () +
theme ( panel.grid.major.x = element_blank (),
panel.grid.minor.x = element_blank (),
panel.grid.major.y = element_line (color = "gray60" , linetype= "dotted" ),
legend.position = "none" ) +
geom_vline (xintercept= 1 , color= "gray" , linetype= "solid" ) +
labs ( y= NULL , x= NULL ) +
scale_x_continuous ( label= scales:: comma) +
scale_color_discrete ()
Make a chart for each bank:
Here’s an example of making a little chart for each bank. The value is the relative percentage of each bank’s loans to zip codes by ethnicity, compared with the total for Arizona. Anything higher than 1 means that the bank is disprportionately lending to that group; anything lower than 1 means that they are not lending in those areas at the rate that others do. This is another easy way to pick out banks to focus on, such as Kabbage in Hispanic or Latino areas, and Wells Fargo in Native American areas.
myplot <-
loan_percentages %>%
ggplot ( aes ( y= reorder (ethnicity, desc (ethnicity)), x= capped_ratio, color= ethnicity)) +
geom_point (size= 2 ) +
facet_wrap ( ~ lender_grp, scales= "free_x" ) +
theme_light () +
theme ( panel.grid.major.x = element_blank (),
panel.grid.minor.x = element_blank (),
panel.grid.major.y = element_line (color = "gray60" , linetype= "dotted" ),
legend.position = "bottom" ,
axis.text.y = element_blank (),
axis.text.x = element_blank (),
strip.text.x = element_text (hjust= - .01 , size= 8 , color= "grey60" ),
strip.background = element_rect (fill= NA , color= NA , linetype = "dotted" )) +
labs ( y= NULL , x= NULL ) +
scale_x_continuous ( limits= c (0 , 1.25 ) , label= scales:: comma) +
scale_color_discrete () +
geom_vline (xintercept= 1 , color= "gray" , linetype= "solid" )
myplot
LS0tCnRpdGxlOiBHZXR0aW5nIHN0YXJ0ZWQgd2l0aCBHR1BMT1QKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgcm1kZm9ybWF0czo6cm9ib2Jvb2s6CiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBmaWdfd2lkdGg6IDUKICAgIGZpZ19oZWlnaHQ6IDMKICAgIHRvY19kZXB0aDogMwotLS0KCgoKVGhpcyB0aW1lIEknbSB1c2luZyB0aGUgYHJvYm9ib29rYCBtYXJrZG93biB0aGVtZSwgaW4gY2FzZSB5b3Ugd2FudCB0byBjb3B5IGl0LiAoRG93bmxvYWQgdGhlIGNvZGUgdG8gZ2V0IHRoZSBzdHVmZiB5b3UgcHV0IGF0IHRoZSB0b3AuKSBUaGUgbGlicmFyaWVzIHVzZWQgYXJlOiAKCiAgICAgICAga25pdHIgICAgICAgICAgICAgdGlkeXZlcnNlICAgICAgICAgIGphbml0b3IgICAgICAgICBzY2FsZXMKICAgICAgICBybWRmb3JtYXRzICAgICAgICBsdWJyaWRhdGUgICAgICAgICAgZm9yY2F0cwogICAgICAgIAoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfSAKCgpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHJtZGZvcm1hdHMpCgojIyBHbG9iYWwgb3B0aW9ucwpvcHRpb25zKG1heC5wcmludD0iNzUiKQpvcHRzX2NodW5rJHNldChlY2hvPVRSVUUsCiAgICAgICAgICAgICAgIHRpZHk9VFJVRSwKICAgICAgICAgICAgICAgbWVzc2FnZT1GQUxTRSwKICAgICAgICAgICAgICAgd2FybmluZz1GQUxTRSkKb3B0c19rbml0JHNldCh3aWR0aD03NSkKYGBgCgoKCgpgYGB7ciBnZXRfZGF0YSwgaW5jbHVkZT1GQUxTRX0KCiMgbGVhdmluZyB0aGlzIGluIGZvciBjcmVhdGluZyB0aGUgZGF0YSBidXQgSSdtIG5vdCB3b3JyeWluZyBhYm91dCB0aGF0IGZvciB0aGlzIHByZXNlbnRhdGlvbiAKCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShqYW5pdG9yKQpsaWJyYXJ5KGZvcmNhdHMpICMgd29ya2luZyB3aXRoIGZhY3RvcnMKbGlicmFyeShzY2FsZXMpICMgdHVybmluZyBudW1iZXJzIGludG8gc29tZXRoaW5nIHJlYWRhYmxlCgoKCmxvYWQgKCB1cmwgKCJodHRwczovL2dpdGh1Yi5jb20vY3JvbmtpdGVkYXRhL3JzdHVkeWd1aWRlL2Jsb2IvbWFzdGVyL2RhdGEvYXpfcHBwX3ppcGNvZGVzLlJkYT9yYXc9dHJ1ZSIpKQoKcHBwX3N1bXMgPC0gCiAgYXpfcHBwX3ppcCAlPiUKICBtdXRhdGUgKCBsZW5kZXJfZ3JwID0gZmN0X2x1bXAgKCBmY3RfaW5mcmVxICggbGVuZGVyKSwgMTAwICkpICU+JQogIGdyb3VwX2J5ICggY2Vuc3VzX3ppcCwgbGVuZGVyX2dycCApICU+JQogIHN1bW1hcmlzZSAoIGxvYW5zID0gbigpLCAgYW1vdW50ID0gc3VtKGluaXRpYWxfYW10KSwgLmdyb3Vwcz0iZHJvcCIpIAoKCnBwcCA8LSAKYXpfYnlfemlwY29kZSAlPiUKICBzZWxlY3QgKCB6Y3RhLCB6aXBjb2RlX2NpdHkgICwgc3RfY291bnR5X2ZpcCwgIHpjdGFfZXRobmljLCB1c3BzX2J1c2luZXNzZXMgKSAlPiUKICBsZWZ0X2pvaW4gKHBwcF9zdW1zLCBieT1jKCJ6Y3RhIiA9ICJjZW5zdXNfemlwIikpICU+JQogIG11dGF0ZSAoIGV0aG5pY2l0eSA9IG9yZGVyZWQgKCB6Y3RhX2V0aG5pYykgLCAKICAgICAgICAgICBsb2FucyA9IHJlcGxhY2VfbmEgKCBsb2FucywgMCkpICU+JQogIHNlbGVjdCAoIHpjdGE6c3RfY291bnR5X2ZpcCwgZXRobmljaXR5LCBsZW5kZXJfZ3JwLCBsb2FucywgYW1vdW50LCB1c3BzX2J1c2luZXNzZXMpCgpsZXZlbHMgKHBwcCRldGhuaWNpdHkpIDwtYyggIk5vIHBlb3BsZSIsICJXaGl0ZSIsICJObyBkaXN0aW5jdCBncm91cCIsICJCbGFjayIsICJIaXNwYW5pYyIsICJOYXRpdmUgQW1lcmljYW4iKQpsZXZlbHMgKHBwcCRldGhuaWNpdHkpCgoKc2tpbXI6OnNraW0oIHBwcCkKCgpgYGAKCgojIyBUaGUgZGF0YQoKVGhpcyBkYXRhIHdhcyBjcmVhdGVkIGZyb20gYW4gZWFybHkgdmVyc2lvbiBvZiB0aGUgUGF5Y2hlY2sgUHJvdGVjdGlvbiBQcm9ncmFtIGdpdmVuIGluIEFyaXpvbmEgZHVyaW5nIHRoZSBwYW5kZW1pYy4gIEhlcmUgYXJlIHRoZSBjb2x1bW4gZGVmaW5pdGlvbnMKCgp2YXJpYWJsZSBuYW1lICAgfCAgIHR5cGUgICAgIHwgICBkZXNjcmlwdGlvbgotLS0tLS0tLS0tLS0tLS0gfCAtLS0tLS0tLS0gIHwgLS0tLS0tLS0tLS0tLQp6Y3RhICAgICAgICAgICAgfCBjaGFyICAgICAgIHwgWmlwIGNvZGUgdXNlZCBpbiB0aGUgY2Vuc3VzCnppcGNvZGVfY2l0eSAgICB8IGNoYXIgICAgICAgfCBDZW5zdXMgbmFtZSBhc3NvY2lhdGVkIHdpdGggdGhhdCB6aXAgY29kZQpzdF9jb3VudHlfZmlwICAgfCBjaGFyICAgICAgIHwgRklQIGNvZGUgZm9yIHRoZSBjb3VudHkgCmV0aG5pY2l0eSAgICAgICB8IGZhY3RvciAgICAgfCBDb3VudHkgZGVtb2dyYXBoaWM6ICJObyBwZW9wbGUiLCAiV2hpdGUiLCAiTm8gZGlzdGluY3QgZ3JvdXAiLCAiQmxhY2siLCAiSGlzcGFuaWMiLCAiTmF0aXZlIEFtZXJpY2FuIiBiYXNlZCBvbiBhIGZvcm11bGEgdXNlZCBieSB0aGUgVXJiYW4gaW5zdGl0dXRlIHRvIGNhdGVnb3JpemUgY291bnRpZXMuIApsZW5kZXJfZ3JwICAgICAgfCBmYWN0b3IgICAgIHwgVGhlIHRvcCAxMDAgbGVuZGVycyBhcmUgbmFtZWQgYW5kIGFsbCBvdGhlcnMgbHVtcGVkIGludG8gIk90aGVyIjsgdGhlIGJpZ2dlc3Qgb25lIHdpbGwgY29tZSBmaXJzdC4gCmxvYW5zICAgICAgICAgICB8IG51bSAgICAgICAgfCAjIG9mIGxvYW5zIGJ5IHppcCBjb2RlIGFuZCBsZW5kZXIgZ3JvdXAgCmFtb3VudCAgICAgICAgICB8IG51bSAgICAgICAgfCAkIGFtb3VudCBvZiB0aGUgbG9hbnMsIGFjY29yZGluZyB0byB0aGUgaW5pdGlhbCBhbW91bnQgcmVwb3J0ZWQsIGJ5IHppcCBjb2RlIGFuZCBsZW5kZXIgZ3JvdXAKdXNwc19idXNpbmVzc2VzIHwgbnVtICAgICAgICB8IHRvdGFsIG51bWJlciBvZiBidXNpbmVzcyBhZGRyZXNzZXMgaW4gdGhlIHppcCBjb2RlCgoKQSBmYWN0b3Igd29ya3MgbGlrZSB3b3JkcywgYnV0IGlzIGFsd2F5cyBzaG93biBpbiB0aGUgb3JkZXIgeW91IHNwZWNpZnksIHNvIHlvdSBkb24ndCBoYXZlIHRvIHdvcnJ5IGFib3V0IGl0IGFscGhhYmV0aXppbmcgZXZlcnl0aGluZyBieSBkZWZhdWx0LiBJdCdzIHBhcnRpY3VsYXJseSB1c2VmdWwgZm9yIGNoYXJ0aW5nLiAKCgojIyBBIHNlYXJjaGFibGUsIHNvcnRhYmxlIHRhYmxlCgpIZXJlJ3Mgd2hhdCB0aGUgZmlyc3QgMjAgcm93cyBsb29rIGxpa2U6CgpgYGB7ciBvcmlnX2RhdGFfbGlzdCwgZWNobz1GQUxTRX0KCmxpYnJhcnkocmVhY3RhYmxlKQpwcHAgJT4lCmFycmFuZ2UgKGRlc2MgKCBsb2FucykpICU+JQpoZWFkKDIwKSAlPiUKcmVhY3RhYmxlICggCiAgIGRlZmF1bHRQYWdlU2l6ZSA9IDEwLAogICB3cmFwPUZBTFNFLAogICBzZWFyY2hhYmxlID0gVFJVRSwKICAgIGRlZmF1bHRDb2xEZWY9IAogICAgICAgIGNvbERlZiAobWF4V2lkdGg9MjAwLCBmb3JtYXQ9Y29sRm9ybWF0KHNlcGFyYXRvcnM9VFJVRSkpLAogICAgdGhlbWU9cmVhY3RhYmxlVGhlbWUoIGNvbG9yPSJncmF5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICBzdHlsZT1saXN0IChmb250RmFtaWx5ID0gIldvcmsgU2Fucywgc2Fucy1zZXJpZiIsIGZvbnRTaXplPSI3MCUiKSksCiAgICBjb2x1bW5zID0gbGlzdCAoCiAgICAgIHpjdGEgID0gY29sRGVmKG5hbWU9IlppcCBjb2RlIiksIAogICAgICB6aXBjb2RlX2NpdHkgPSBjb2xEZWYobmFtZT0iQ2l0eSIpLCAKICAgICAgc3RfY291bnR5X2ZpcCA9IGNvbERlZiAobmFtZT0iRklQIiksCiAgICAgIGV0aG5pY2l0eSAgPSBjb2xEZWYgKG5hbWU9IkV0aG5pY2l0eSIpLAogICAgICBsZW5kZXJfZ3JwID0gY29sRGVmIChuYW1lPSJMZW5kZXIiLCBtaW5XaWR0aD0yMDApLAogICAgICBsb2FucyA9IGNvbERlZiAobmFtZSA9ICIjIGxvYW5zIiksCiAgICAgIGFtb3VudCA9IGNvbERlZiAobmFtZT0iJCBhbW91bnQiLCAKICAgICAgICAgICAgICAgICAgICAgICBtaW5XaWR0aD0xNTAsIGZvcm1hdD1jb2xGb3JtYXQgKHNlcGFyYXRvcnM9VFJVRSwgY3VycmVuY3k9IlVTRCIsIGRpZ2l0cz0wKSksCiAgICAgIHVzcHNfYnVzaW5lc3NlcyA9IGNvbERlZihuYW1lPSAiIyBidXNpbmVzc2VzIikpCiAgKQoKCgpgYGAKCgojIyBPdXIgZmlyc3QgY2hhcnQKCgpPbmUgb2YgdGhlIGZpcnN0IHRoaW5ncyAgYSBsb3Qgb2YgcGVvcGxlIGRvIHdpdGggYSBsYXJnZSBkYXRhc2V0IGlzIHRvIGNyZWF0ZSBhIGBoaXN0b2dyYW1gIG9mIHRoZSBkYXRhLiBPbmUgZXhhbXBsZSBpcyB0aGUgbnVtYmVyIG9mIGxvYW5zIGJ5IHppcCBjb2RlLgoKCgojIyMgQ3JlYXRlIGEgaGlzdG9ncmFtCgpBbGJlcnRvIENhaXJvIHRhbGtlZCBhYm91dCB0aGUgZWxlbWVudHMgb2YgYSBkYXRhIHZpc3VhbGl6YXRpb246IFRoZSBjb250YWluZXIsIHRoZSBlbmNvZGluZ3MgYW5kIHRoZSBhbm5vdGF0aW9ucy4gVGhlIGZpcnN0IHRoaW5nIHRvIGRvIGlzIHRvIGNyZWF0ZSB0aGUgY29udGFpbmVyLiAgQnV0IGZpcnN0LCB3ZSBuZWVkIHRvIGdldCB0aGUgZGF0YSB0aGF0IHdlIHdhbnQgaW50byB0aGUgZm9ybSBuZWVkZWQgZm9yIGEgdmlzdWFsaXphdGlvbi4gV2UgYWdncmVnYXRlIHRoZSBsZW5kZXJzIHRvIHppcCBjb2RlIHRvdGFscywgYW5kIGNvbXB1dGUgdGhlIHBlcmNlbnRhZ2Ugb2YgYnVzaW5lc3NlcyB0aGF0IGdvdCBsb2Fucy4gSSdsbCBjYXAgaXQgYXQgNTAwICUgYmVjYXVzZSB0aGVyZSB3ZXJlIHNvbWUgcGxhY2VzIHdpdGggbW9yZSBsb2FucyB0aGFuIHRoZXJlIHdlcmUgYnVzaW5lc3NlcyAtIGEgcmVzdWx0IG9mIG5vdCBoYXZpbmcgYSBnb29kIGxpc3Qgb2YgdGhlIGVsaWdpYmxlIGJ1c2luZXNzZXMgZm9yIHRoZSBwcm9ncmFtLiAKCmBgYHtyIGJ1aWxkX2hpc3QxLCBjbGFzcy5zb3VyY2U9ICdmb2xkLXNob3cnfQoKcHBwZGF0YSA8LSAKICBwcHAgJT4lCiAgZ3JvdXBfYnkgKCB6Y3RhLCBldGhuaWNpdHkpICU+JQogIHN1bW1hcmlzZSAoIGxvYW5zID0gc3VtKGxvYW5zKSAsIGJ1c2luZXNzZXMgPSBmaXJzdCh1c3BzX2J1c2luZXNzZXMpKSAlPiUKICBtdXRhdGUgKCBwY3RfbG9hbnMgPSBwbWluIChsb2FucyAvIGJ1c2luZXNzZXMsIDUpKSAKCm15X2hpc3RvZ3JhbSA8LSAKICBnZ3Bsb3QgKCBkYXRhPSBwcHBkYXRhLCAKICAgICAgICAgICBtYXBwaW5nID0gYWVzICggeCA9IHBjdF9sb2FucyApKQogIAoKCmBgYAoKcHJvZHVjZXM6IAoKYGBge3IgcHJpbnRfc2hlbGx9Cm15X2hpc3RvZ3JhbQoKYGBgCgpOb3RoaW5nISBUaGF0J3MgYmVjYXVzZSB3ZSBoYXZlbid0IHlldCB0b2xkIFIgd2hhdCBgZ2VvbWV0cnlgIHRvIHVzZS4gVGhlIGNob2ljZSBoZXJlIGlzIGBnZW9tX2hpc3RvZ3JhbSgpYAoKIyMjIyBBZGQgYW4gZW5jb2RpbmcgCgoKYGBge3IgYWRkX2dlb20sIGNsYXNzLnNvdXJjZT0nZm9sZC1zaG93J30KCm15X2hpc3RvZ3JhbSArIAogIGdlb21faGlzdG9ncmFtKCApCgpgYGAKCgoKCiMjIyMgRml4IGJpbnMgYW5kIGNvbG9ycwoKVGhpcyBpcyBoYXJkIHRvIHJlYWQsIG5vdCBqdXN0IGJlY2F1c2UgSSBoYXZlbid0IGZvcm1hdHRlZCB0aGUgcGVyY2VudGFnZXMgKHNvIDUgPSA1MDAlIG9mIGFsbCBidXNpbmVzc2VzIGdvdCBsb2FucyksIGJ1dCBiZWNhdXNlIG9mIHRoZSBkZWZhdWx0IG51bWJlciBvZiAiYmlucyIgY2hvc2VuLiBIZXJlJ3MgaG93IHRvIGNsZWFuIHRoYXQgdXAgYSBsaXR0bGUuIEF0IHRoZSBzYW1lIHRpbWUsIEknbGwgYXBwbHkgYSB0aGVtZSB0aGF0J3MgYSBsaXR0bGUgZWFzaWVyIHRvIHJlYWQsIGFuZCBzZXQgdGhlIG91dGxpbmUgKGNvbG9yKSBhbmQgdGhlIGZpbGwgc28gdGhhdCB0aGUgYmFycyBhcmVuJ3Qgc211c2hlZCB0b2dldGhlci4gCgpUaGUgIltDb2xvcnMgb2YgUl0oaHR0cDovL3d3dy5zdGF0LmNvbHVtYmlhLmVkdS9+dHpoZW5nL2ZpbGVzL1Jjb2xvci5wZGYpIiBkb2N1bWVudCBmcm9tIENvbHVtYmlhIFVuaXZlcnNpdHkgc2hvd3MgeW91IHRoZSBuYW1lcyBvZiB0aGUgY29sb3JzIHlvdSBjYW4gdXNlLiAKCgpgYGB7ciBmaXhfaGlzdG9ncmFtLCBjbGFzcy5zb3VyY2U9J2ZvbGQtc2hvdyd9CgpteV9oaXN0b2dyYW0gKwogIGdlb21faGlzdG9ncmFtICggYmlud2lkdGg9LjEsIGNvbG9yPSJ3aGl0ZSIsIGZpbGw9InNsYXRlZ3JleSIpICsKICB0aGVtZV9saWdodCgpCgoKYGBgCkFsbCBJIGRpZCB3YXMgbWFrZSB0aGUgYmlucyAxMCUgb2YgZWFjaCB6aXAgY29kZSdzIGJ1c2luZXNzZXMgKDAuMSkKClRoYXQncyBvaywgYnV0IGxldCdzIG1ha2UgdGhlIGxhYmVscyBtb3JlIGFjY3VyYXRlOgoKYGBge3J9CgpteV9oaXN0b2dyYW0gKwogIGdlb21faGlzdG9ncmFtICggYmlud2lkdGg9LjEsIGNvbG9yPSJ3aGl0ZSIsIGZpbGw9InNsYXRlZ3JleSIpICsKICB0aGVtZV9saWdodCgpICsKICBzY2FsZV94X2NvbnRpbnVvdXMgKCBsYWJlbHM9cGVyY2VudCkgKwogIGxhYnMgICh4PSAiJSBvZiBidXNpbmVzZXMgdGhhdCBnb3QgbG9hbnMgKG1heGVkIGF0IDUwMCUpIiwgeSA9ICIjIG9mIHppcCBjb2RlcyIpCgoKYGBgCgoKCiMjIyMgQWRkaW5nIGNvbXBsZXhpdHkKClNheSB3ZSB3YW50IHRvIHNlZSBhIGhpc3RvZ3JhbSBieSB0aGUgZXRobmljaXR5IG9mIHRoZSB6aXAgY29kZS4gV2UgbWlnaHQgY2hvb3NlIHRvIHNlZSBhIGRpZmZlcmVudCBwbG90IGZvciBlYWNoIG9uZS4gV2hlbiB3ZSB3YWxrIHRocm91Z2ggaXQsIEknbGwgZ28gdGhyb3VnaCBlYWNoIG9mIHRoZXNlIGl0ZW1zIG9uZSBieSBvbmUuIEkndmUgc3RhcnRlZCB3aXRoIGEgYmxhbmsgcGxvdCB3aXRoIGRhdGEgZm9yIGVhY2ggemlwIGNvZGUsIHNob3dpbmcgdGhlIG51bWJlciBvZiBsb2FucyBhbmQgdGhlIGV0aG5pY2l0eSBvZiBlYWNoIHppcCBjb2RlLiAKCgpgYGB7ciBibGFua19wbG90X2V0aG5pY30KCm15cGxvdCA8LSAKICBwcHBkYXRhICU+JQogIGZpbHRlciAoIGV0aG5pY2l0eSAlaW4lIGMoIldoaXRlIiwgIkhpc3BhbmljIiwgIk5hdGl2ZSBBbWVyaWNhbiIpKSAlPiUKICBnZ3Bsb3QgKCApCgoKYGBgCgoKQnVpbGRpbmcgdGhpcyBhIGxpdHRsZSBhdCBhIHRpbWUsIHdlIGNhbiBzYXZlIHRoZSBwbG90IGFuZCBrZWVwIHdvcmtpbmcgb24gaXQgbGF0ZXIuIFRoZSBvbmx5IHRoaW5nIGNoYW5nZWQgd2FzIHRoZSAiZmlsbCIgY29sb3IgLS0gYW4gZW5jb2RpbmcgLSB0byBiZSBzZXQgYnkgZXRobmljaXR5IG9mIHRoZSB6aXAgY29kZS4gCgpgYGB7ciBldGhuaWNfaGlzdCAsIGNsYXNzLnNvdXJjZT0iZm9sZC1zaG93In0KCm15cGxvdCA8LSAKICBteXBsb3QgICsgCiAgKCBhZXMgKCB4PSBwY3RfbG9hbnMgLCBmaWxsPSBldGhuaWNpdHkpKSArIAogIGdlb21faGlzdG9ncmFtKCBiaW53aWR0aD0uMSwgCiAgICAgICAgICAgICAgICAgIGNvbG9yPSJncmF5IikgKyAKICBzY2FsZV94X2NvbnRpbnVvdXMoIGxhYmVscz1wZXJjZW50KQoKCm15cGxvdAoKYGBgCgoKVGhhdCdzIG5vdCB2ZXJ5IGhlbHBmdWwuIEluc3RlYWQsIGxldCdzIHRyeSBpdCBieSBwdXR0aW5nIGVhY2ggZXRobmljaXR5IG9uIGl0cyBvd24gc2NhbGUgYW5kIGdyYXBoaWMsIHVzaW5nIGZhY2V0cy4gYGZhY2V0X2dyaWRgIGNyZWF0ZXMgYSBncmlkIG9mIGdyYXBoaXMsIHdpdGggdGhlIHZhcmlhYmxlIG9uIHRoZSBsZWZ0IGRlZmluaW5nIHRoZSByb3dzIGFuZCB0aGUgdmFyaWFibGUgb24gdGhlIHJpZ2h0IG9mIHRoZSB0aWxkZSBhcyB0aGUgdmFyaWFibGUgZm9yIGNvbHVtbnMuIFVzZSBhIHBlcmlvZCB0byBsZWF2ZSBlaXRoZXIgb25lIGJsYW5rLiAKCmBgYHtyIGV0aG5pY19oaXN0MSAsIGNsYXNzLnNvdXJjZT0iZm9sZC1zaG93In0KCm15cGxvdCArIAogIGZhY2V0X2dyaWQgKCAgZXRobmljaXR5IH4gLiApCgpgYGAKCgpUaGlzIHN0aWxsIGlzbid0IHRoYXQgaGVscGZ1bCAtIHRoZSBzY2FsZXMgYXJlIHRoZSBzYW1lLCBtYWtpbmcgaXQgaGFyZCB0byBzZWUgdGhlIHNoYXBlLiBUaGlzIHRpbWUsIHdlJ2xsIHNhdmUgaXQgYW5kIHRoZW4gcHJpbnQgaXQgb3V0LCBtZWFuaW5nIHdlIGRvbid0IGhhdmUgdG8gdHlwZSBhbnl0aGluZyBvdmVyIGFnYWluOgoKYGBge3IgZXRobmljX2hpc3QyICwgY2xhc3Muc291cmNlPSJmb2xkLXNob3cifQoKbXlwbG90IDwtIAogIG15cGxvdCArIAogIGZhY2V0X2dyaWQgKCAgZXRobmljaXR5IH4gLiAsIHNjYWxlcz0iZnJlZV95IikKCgpteXBsb3QKCmBgYAoKCkZpbmFsbHksIGxldCdzIGdldCByaWQgb2YgdGhlIGxlZ2VuZCBhbmQgdXNlIHNvbWUgb3RoZXIgY29sb3JzIDoKCmBgYHtyIGV0aG5pY19oaXN0MyAsIGNsYXNzLnNvdXJjZT0iZm9sZC1zaG93In0KCm15cGxvdCArIAogIHRoZW1lX2xpZ2h0ICgpICsgCiAgdGhlbWUgKCBsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIAogIHNjYWxlX2ZpbGxfaHVlKCkKCmBgYAoKCgoKIyMgQSBiYXIgY2hhcnQKClJlbWVtYmVyIHRoYXQgcGVvcGxlIGNhbiBzZWUgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBsZW5naHRzIG9mIGxpbmVzIGJldHRlciB0aGFuIGFuZ2xlcyBvciBzaXplcyBvZiBjaXJjbGVzLiBUaGF0J3Mgd2h5IHlvdSBzZWUgc28gbWFueSBiYXIgY2hhcnRzLiBMZXQncyB0YWtlIHRoZSB0b3AgNTAgbGVuZGVycyBhbmQgY29tcGFyZSB0aGUgbnVtYmVyIG9mIGxvYW5zIGVhY2ggaGFzIG1hZGUuICAKCgpgYGB7ciBnZXRfbGVuZGVyX2RhdGF9CgpsZW5kZXJfdG90YWxzIDwtIAogIHBwcCAlPiUKICBncm91cF9ieSAoIGxlbmRlcl9ncnAsIGV0aG5pY2l0eSApICU+JQogIHN1bW1hcmlzZSAoIGxvYW5zID0gc3VtKGxvYW5zKSkgJT4lCiAgZmlsdGVyICggYXMuaW50ZWdlciggbGVuZGVyX2dycCkgPD01MCApCgoKYGBgCgoKVGhpcyBjcmVhdGVzIGEgc2lkZXdheXMgYmFyIGNoYXJ0LCB3aGljaCBpcyBlYXNpZXIgdG8gcmVhZCB3aXRoIGEgbG90IG9mIGNhdGVnb3JpZXMgKGxlbmRlcnMpIGFuZCBsb25nIG5hbWVzOgoKYGBge3Igcl9iYXIxICwgY2xhc3Muc291cmNlPSJmb2xkLXNob3ciLCBmaWcuaGVpZ2h0PTksIGZpZy53aWR0aD0xNX0KCmxlbmRlcl90b3RhbHMgJT4lCiAgZ3JvdXBfYnkgKCBsZW5kZXJfZ3JwKSAlPiUKICBzdW1tYXJpc2UgKGxvYW5zID0gc3VtKGxvYW5zKSkgJT4lCiAgZ2dwbG90KCBhZXMoIHg9IGxlbmRlcl9ncnAsIHk9IGxvYW5zKSkgKyAKICAgIGdlb21fYmFyICggcG9zaXRpb249cG9zaXRpb25fZG9kZ2UoKSwgc3RhdD0iaWRlbnRpdHkiKSAgKwogICAgZ3VpZGVzIChmaWxsID0gRkFMU0UpICsgCiAgICBsYWJzICh4ID0gTlVMTCkgKwogICAgY29vcmRfZmxpcCgpIAoKCgpgYGAKCgpPbmUgdXNlZnVsIHRoaW5nIGlzIHRvIHJldmVyc2UgdGhlIG9yZGVyIG9mIHRoZSBncmFwaGljIHNvIHRoZSBiaWdnZXN0IGFyZSBvbiB0b3AuIAoKCmBgYHtyIHJfYmFyMiAsIGNsYXNzLnNvdXJjZT0iZm9sZC1zaG93IiwgZmlnLmhlaWdodD05LCBmaWcud2lkdGg9OX0KCm15cGxvdCA8LSAKICBsZW5kZXJfdG90YWxzICU+JQogIGdyb3VwX2J5ICggbGVuZGVyX2dycCkgJT4lCiAgc3VtbWFyaXNlIChsb2FucyA9IHN1bShsb2FucykpICU+JQogIGdncGxvdCggYWVzKCB4PSByZW9yZGVyKCBsZW5kZXJfZ3JwLCBsb2FucykgLCB5PSBsb2FucykpICsgCiAgICBnZW9tX2JhciAoIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKCksIHN0YXQ9ImlkZW50aXR5IikgICsKICAgIGd1aWRlcyAoZmlsbCA9ICJub25lIikgKyAKICAgIGxhYnMgKHggPSAiQ29tcGFueSIpICsKICAgIGNvb3JkX2ZsaXAoKSArCiAgICB0aGVtZV9idygpIAoKbXlwbG90CiAgICAKYGBgCgoKIyMgRG90IHBsb3RzCgoKIyMjIFBlcmNlbnRhZ2VzIGJ5IGV0aG5pY2l0eSAKCldlJ2QgbGlrZSB0byBjb21wYXJlIHRoZSBwZXJjZW50IG9mIGxvYW5zIHdpdGhpbiBlYWNoIGxlbmRlciBmb3IgSGlzcGFuaWMsIFdoaXRlIGFuZCBOYXRpdmUgQW1lcmljYW4gemlwIGNvZGVzLiBUaGlzIGlzIHdoZW4gd2UgcmVhbGx5IGNhbiB1c2UgdGhlIHBvd2VyIG9mIHRoZSBmYWNldHMgdG8gY3JlYXRlIGEgZ3JpZCB0aW55IGxpdHRsZSBncmFwaGljcyAtLSBvbmUgZm9yIGVhY2ggbGVuZGVyISAKCiMjIyMgU2V0IHVwIHRoZSBkYXRhCgoKT3VyIGdvYWwgaXMgdG8gbWFrZSBhIHNlcmllcyBvZiBsaXR0bGUgZG90IHBsb3RzLCBzb21ldGltZXMgY2FsbGVkICJDbGV2ZWxhbmQiIHBsb3RzLCBhZnRlciB0aGUgc3RhdGlzdGljYWwgZXhwZXJ0IHRoYXQgaW52ZW50ZWQgdGhlIHNtYWxsIG11bHRpcGxlIGZhY2V0ZWQgYXBwcm9hY2guIAoKVG8gc2VlIGEgYmlnIHZlcnNpb24sIGxldCdzIG1ha2UgYSBDbGV2ZWxhbmQgZG90IHBsb3Qgb2YgdGhlIGFib3ZlIGJhciBjaGFydCBpbnN0ZWFkLiBUaGlzIHRpbWUsIHRoZSBuYW1lIG9mIHRoZSBiYW5rIGlzIHRoZSBZIGF4aXMgYW5kIHRoZSBudW1iZXIgb2YgbG9hbnMgaXMgdGhlIHggYXhpcy4KCmBgYHtyIGNsYXNzLnNvdXJjZT0iZm9sZC1zaG93IiwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTE1fQoKCm15cGxvdCA8LSAKbGVuZGVyX3RvdGFscyAlPiUKICBncm91cF9ieSAoIGxlbmRlcl9ncnApICU+JQogIHN1bW1hcmlzZSAobG9hbnMgPSBzdW0obG9hbnMpKSAlPiUKICBnZ3Bsb3QoIGFlcyggeT0gcmVvcmRlciggbGVuZGVyX2dycCwgbG9hbnMpICwgeD0gbG9hbnMpKSArCiAgZ2VvbV9wb2ludCAoc2l6ZT00LCBjb2xvcj0iYmxhY2siKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUgKCBwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9saW5lIChjb2xvciA9ICJncmF5NjAiLCBsaW5ldHlwZT0iZG90dGVkIikpICsKICBsYWJzICggeT1OVUxMKSArCiAgc2NhbGVfeF9jb250aW51b3VzICggbGFiZWw9c2NhbGVzOjpjb21tYSkKICAKCgpteXBsb3QKCgpgYGAKCgojIyMgRmFjZXRzIGZvciBldGhuaWNpdHkKClRoYXQgd2FzIGVhc3kgZW5vdWdoLCBhbmQgaXMgZXZlbiBhIGxpdHRsZSBlYXNpZXIgdG8gcmVhZCB0aGFuIHRoZSBiYXJzLiBOb3cgbGV0J3MgbWFrZSB0aHJlZSBvZiB0aGVtIC0tIG9uZSBmb3IgZWFjaCBldGhuaWNpdHkuIFRoaXMgdGltZSwgd2UnbGwganVzdCB0YWtlIHRoZSBzYW1lIHRoaW5nLCBidXQgc3RhcnQgd2l0aCB0aGUgZGF0YSB0aGF0IGluY2x1ZGVzIGV0aG5pY2l0eQoKCmBgYHtyICBtYWtlX2NsZXZlbGFuZDEgLCBjbGFzcy5zb3VyY2U9ImZvbGQtc2hvdyIsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xMn0KCgpwcHAgJT4lCiAgZ3JvdXBfYnkgKCBsZW5kZXJfZ3JwLCBldGhuaWNpdHkpICU+JQogIGZpbHRlciAoIGFzLmludGVnZXIobGVuZGVyX2dycCkgPD0gNTAsIGV0aG5pY2l0eSAlaW4lIGMoIldoaXRlIiwgIk5hdGl2ZSBBbWVyaWNhbiIsICJIaXNwYW5pYyIpKSAlPiUgCiAgc3VtbWFyaXNlIChsb2FucyA9IHN1bShsb2FucyksIC5ncm91cHM9ImRyb3AiKSAlPiUKICBnZ3Bsb3QoIGFlcyggeT0gcmVvcmRlciAoIGxlbmRlcl9ncnAsIGRlc2MobGVuZGVyX2dycCkgICkgLCB4PSBsb2FucywgY29sb3I9ZXRobmljaXR5KSkgKwogIGdlb21fcG9pbnQgKHNpemU9MiwgY29sb3I9ImJsYWNrIikgKwogIGZhY2V0X2dyaWQgKCAuIH4gZXRobmljaXR5ICwgc2NhbGVzPSJmcmVlX3giKSArCiAgdGhlbWVfYncoKSArCiAgc2NhbGVfY29sb3JfYnJld2VyICgpICsKICB0aGVtZSAoIHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2xpbmUgKGNvbG9yID0gImdyYXk2MCIsIGxpbmV0eXBlPSJkb3R0ZWQiKSkgKwogIGxhYnMgKCB5PU5VTEwpICsKICBzY2FsZV94X2NvbnRpbnVvdXMgKCBsYWJlbD1zY2FsZXM6OmNvbW1hKSAKICAKCmBgYAoKCkJ1dCB0aGlzIGlzbid0IHZlcnkgdXNlZnVsLCBzaW5jZSBpdCBpcyB0aGUgcmF3IG51bWJlciBvZiBsb2FucyBmb3IgZWFjaC4gSW5zdGVhZCwgd2UnbGwgY29udmVydCB0aGUgZGF0YSBzbyB0aGF0LCB3aXRoaW4gZWFjaCBldGhuaWNpdHksIHdlIHNob3cgdGhlIG1hcmtldCBzaGFyZSAodGhlIHBlcmNlbnQgb2YgYWxsIGxvYW5zKSB0aGF0IHdlbnQgdG8gZWFjaCBncm91cC4KCgpgYGB7cn0KCnRvdGFscyA8LSAKICBhel9wcHBfemlwICU+JQogIGdyb3VwX2J5ICggY2Vuc3VzX3ppcCkgJT4lCiAgc3VtbWFyaXNlICggbG9hbnMgPSBuKCksIGxlbmRlcnM9bl9kaXN0aW5jdCAobGVuZGVyKSwgYW1vdW50PXN1bShpbml0aWFsX2FtdCkpICU+JQogIHJpZ2h0X2pvaW4gKCBhel9ieV96aXBjb2RlLCBieT1jKCJjZW5zdXNfemlwIj0iemN0YSIpICkgJT4lCiAgc2VsZWN0ICggemN0YT1jZW5zdXNfemlwLCB6Y3RhX2V0aG5pYywgIGxvYW5zOmFtb3VudCwgdXNwc19idXNpbmVzc2VzKSAlPiUKICBtdXRhdGUgKGV0aG5pY2l0eSA9IG9yZGVyZWQgKHpjdGFfZXRobmljKSwgCiAgICAgICAgICBsb2FucyA9IHJlcGxhY2VfbmEgKGxvYW5zLCAwKSkgJT4lCiAgZ3JvdXBfYnkgKGV0aG5pY2l0eSkgJT4lCiAgc3VtbWFyaXNlICggZXRobmljX2xvYW5zID0gc3VtKGxvYW5zKSAsIGV0aG5pY19idXNpbmVzc2VzPXN1bSh1c3BzX2J1c2luZXNzZXMsIG5hLnJtPVQpLCAuZ3JvdXBzPSJkcm9wIikgICU+JQogIG11dGF0ZSAoIGdyYW5kX3RvdGFsX2xvYW5zID0gc3VtKGV0aG5pY19sb2FucyksIGdyYW5kX3RvdGFsX2J1c2luZXNzZXMgPSBzdW0oZXRobmljX2J1c2luZXNzZXMpLCAKICAgICAgICAgICBldGhuaWNfcGN0ID0gZXRobmljX2xvYW5zIC8gZ3JhbmRfdG90YWxfbG9hbnMpCgpsZXZlbHModG90YWxzJGV0aG5pY2l0eSkgPC0gYygiTm8gcGVvcGxlIiwgIldoaXRlIiwgIk5vIGRpc3RpbmN0IGdyb3VwIiwgIkJsYWNrIiwgIkhpc3BhbmljIiwgIk5hdGl2ZSBBbWVyaWNhbiIpCgpvcHRpb25zKHNjaXBlbj05OTkpCgpsb2FuX3BlcmNlbnRhZ2VzIDwtIAogIHBwcCAlPiUKICBncm91cF9ieSAoIGV0aG5pY2l0eSwgbGVuZGVyX2dycCApICU+JQogIHN1bW1hcmlzZSAoIGxvYW5zID0gc3VtKGxvYW5zKSAsIC5ncm91cHM9ImRyb3AiKSAlPiUKICBsZWZ0X2pvaW4gKCB0b3RhbHMsIGJ5PSJldGhuaWNpdHkiKSAgJT4lCiAgZ3JvdXBfYnkgKCBsZW5kZXJfZ3JwICkgJT4lCiAgbXV0YXRlICggbGVuZGVyX3BjdCA9IGxvYW5zIC8gc3VtKGxvYW5zKSwgCiAgICAgICAgICAgbGVuZGVyX3JhdGlvID0gbGVuZGVyX3BjdCAvIGV0aG5pY19wY3QgKSAlPiUKICBmaWx0ZXIgKCBhcy5pbnRlZ2VyKGxlbmRlcl9ncnApIDw9IDUwLCBldGhuaWNpdHkgJWluJSBjKCJXaGl0ZSIsICJOYXRpdmUgQW1lcmljYW4iLCAiSGlzcGFuaWMiKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIHNlbGVjdCAoIGV0aG5pY2l0eTpldGhuaWNfbG9hbnMsIGV0aG5pY19wY3QsIGxlbmRlcl9wY3QsIGxlbmRlcl9yYXRpbykgJT4lCiAgbXV0YXRlIChjYXBwZWRfcmF0aW8gPSBwbWluIChsZW5kZXJfcmF0aW8sIDEuMjUpLCAKICAgICAgICAgIGNhcHBlZF9yYXRpbyA9IHBtYXggKGNhcHBlZF9yYXRpbywgLjQpKQoKCgpgYGAKCgojIyMgTWFrZSBmYWNldHMgYnkgemlwIGNvZGUgZXRobmljaXR5CgoKYGBge3IgIG1ha2VfY2xldmVsYW5kX2RhdGEgICwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTEyfQoKCmxvYW5fcGVyY2VudGFnZXMgJT4lCiAgZ2dwbG90KCBhZXMoIHk9IHJlb3JkZXIgKCBsZW5kZXJfZ3JwLCBkZXNjKGxlbmRlcl9ncnApICApICwgeD0gY2FwcGVkX3JhdGlvLCBjb2xvcj1ldGhuaWNpdHkpKSArCiAgZ2VvbV9wb2ludCAoc2l6ZT0yKSArCiAgZmFjZXRfZ3JpZCAoIC4gfiBldGhuaWNpdHkgLCBzY2FsZXM9ImZyZWVfeCIpICsKICB0aGVtZV9idygpICsKICB0aGVtZSAoIHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2xpbmUgKGNvbG9yID0gImdyYXk2MCIsIGxpbmV0eXBlPSJkb3R0ZWQiKSwgCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBnZW9tX3ZsaW5lICh4aW50ZXJjZXB0PTEsIGNvbG9yPSJncmF5IiwgbGluZXR5cGU9InNvbGlkIikgKwogIGxhYnMgKCB5PU5VTEwsIHg9TlVMTCkgKwogIHNjYWxlX3hfY29udGludW91cyAoIGxhYmVsPXNjYWxlczo6Y29tbWEpICsgCiAgc2NhbGVfY29sb3JfZGlzY3JldGUoKQogIAoKCmBgYAoKCgojIyMgTWFrZSBhIGNoYXJ0IGZvciBlYWNoIGJhbms6IAoKSGVyZSdzIGFuIGV4YW1wbGUgb2YgbWFraW5nIGEgbGl0dGxlIGNoYXJ0IGZvciBlYWNoIGJhbmsuIFRoZSB2YWx1ZSBpcyB0aGUgcmVsYXRpdmUgcGVyY2VudGFnZSBvZiBlYWNoIGJhbmsncyBsb2FucyB0byB6aXAgY29kZXMgYnkgZXRobmljaXR5LCBjb21wYXJlZCB3aXRoIHRoZSB0b3RhbCBmb3IgQXJpem9uYS4gQW55dGhpbmcgaGlnaGVyIHRoYW4gMSBtZWFucyB0aGF0IHRoZSBiYW5rIGlzIGRpc3BycG9ydGlvbmF0ZWx5IGxlbmRpbmcgdG8gdGhhdCBncm91cDsgYW55dGhpbmcgbG93ZXIgdGhhbiAxIG1lYW5zIHRoYXQgdGhleSBhcmUgbm90IGxlbmRpbmcgaW4gdGhvc2UgYXJlYXMgYXQgdGhlIHJhdGUgdGhhdCBvdGhlcnMgZG8uIFRoaXMgaXMgYW5vdGhlciBlYXN5IHdheSB0byBwaWNrIG91dCBiYW5rcyB0byBmb2N1cyBvbiwgc3VjaCBhcyBLYWJiYWdlIGluIEhpc3BhbmljIG9yIExhdGlubyBhcmVhcywgYW5kIFdlbGxzIEZhcmdvIGluIE5hdGl2ZSBBbWVyaWNhbiBhcmVhcy4KCgoKYGBge3IgY2xhc3Muc291cmNlPSJmb2xkLXNob3ciLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD05fQoKCm15cGxvdCA8LSAKICBsb2FuX3BlcmNlbnRhZ2VzICU+JQogIGdncGxvdCggYWVzKCAgeT1yZW9yZGVyKGV0aG5pY2l0eSwgZGVzYyhldGhuaWNpdHkpKSwgeD0gY2FwcGVkX3JhdGlvLCBjb2xvcj1ldGhuaWNpdHkpKSArCiAgZ2VvbV9wb2ludCAoc2l6ZT0yKSArCiAgZmFjZXRfd3JhcCAoICB+IGxlbmRlcl9ncnAsIHNjYWxlcz0iZnJlZV94IiApICsKICB0aGVtZV9saWdodCAoKSArCiAgdGhlbWUgKCBwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9saW5lIChjb2xvciA9ICJncmF5NjAiLCBsaW5ldHlwZT0iZG90dGVkIiksIAogICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIAogICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0IChoanVzdD0tLjAxLCBzaXplPTgsIGNvbG9yPSJncmV5NjAiKSwKICAgICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QgKGZpbGw9TkEsIGNvbG9yPSBOQSwgbGluZXR5cGUgPSAiZG90dGVkIikpICsKICBsYWJzICggeT1OVUxMLCB4PU5VTEwpICsKICBzY2FsZV94X2NvbnRpbnVvdXMgKCAgbGltaXRzPWMoMCwgMS4yNSkgLCBsYWJlbD1zY2FsZXM6OmNvbW1hKSArIAogIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKCkgKwogIGdlb21fdmxpbmUgKHhpbnRlcmNlcHQ9MSwgY29sb3I9ImdyYXkiLCBsaW5ldHlwZT0ic29saWQiKQoKCgpteXBsb3QKCmBgYAoKCg==