There are thousands of museums, aquariums, and zoos across the United States. In this project, we’ll take a look at the distribution of these institutions by geographic region, type, and revenue.
Our data is compiled from administrative records from the Institute of Museum and Library Services, IRS records, and private foundation grantmaking records. This data reflects the status of each institution as of 2013. For each institution, we have information on its name, type, and location. Each institution also has a parent organization – for example, if a museum housed at a university, its parent organization is the university where it resides. Financial data on annual revenue is available at the parent organization level.
library(dplyr)
library(ggplot2)
library(stringr)
library(tidyr)
library(plotrix)
Data Exploration
Let’s start by loading our dataset. We’ve provided a file named museums.csv. Load this file into a data frame named museums_df.
# Load file as data frame
museums_df <- read.csv("museums.csv")
Take a look at the head of this data frame. Make sure to click through using the arrows in the header to see all the available columns.
# Inspect data frame
head(museums_df)
The Museum.Name column represents the name of each individual institution, while the Legal.Name column represents the name of each institution’s parent entity. For example, if “Codecademy University” has two museums on campus, each of those museums would have their own names under Museum.Name and both would share the same Legal.Name i.e. “Codecademy University”.
Museums by Type
In this section, we’ll explore the distribution of institutions in our dataset by type. Our data frame contains a column called Museum.Type describing what kind of museum each location is – a history museum, a zoo, an aquarium, etc. Create and print a bar plot called museum_type that maps Museum.Type to the x axis and counts the frequency of each type on the y axis. Which category is most common?
museum_type <- ggplot(museums_df, aes(x = Museum.Type)) +
geom_bar()
museum_type

The plot we just created is hard to read because our categories are long. Add a scale_x_discrete() layer to customize our x axis, using the function scales::wrap_format(8) to reformat our labels.
wrap_format() is a function from the scales packages which comes included with ggplot2. By setting the value of wrap_format() to 8, we are telling it that the maximum width per line should be no more than 8 characters.
Now we should be able to see which category is most common.
# Create and print bar plot by type
museum_type <- ggplot(museums_df, aes(x = Museum.Type)) +
geom_bar() +
scale_x_discrete(labels = scales::wrap_format(8))
museum_type

We’ve included a boolean (TRUE or FALSE) column in our data frame called Is.Museum. The TRUE category includes typical museums like art, history, and science museums. The FALSE category includes zoos, aquariums, nature preserves, and historic sites, which are included in this data but aren’t what most people think of when they hear the word “museum.”
Create a new bar plot called museum_class, mapping Is.Museum to the x axis. Since “TRUE” and “FALSE” aren’t very descriptive, use scale_x_discrete() to rename the x axis labels to more easily understood terms – for example, “Museum” vs “Non-Museum”.
# Create and print bar plot by museum vs non-museum
museum_class <- ggplot(museums_df, aes(x = Is.Museum)) +
geom_bar() +
scale_x_discrete(labels = c( "TRUE" = "Museum", "FALSE" = "Non-Museum"))
museum_class

Instead of looking at the distribution across the entire United States, maybe we’re just interested in a few states. Filter museums_df to include a few states you might be interested in, using the State..Administrative.Location. column; for example, we can choose IL, CA, and NY. Call this filtered data frame museums_states.
After creating museums_states, recreate our bar plot showing the distribution of museums vs non-museums and use facet_grid() to display each state’s distribution in a separate panel. Call this plot museum_facet. How does the distribution of museum vs non-museum vary across the states you chose?
# Filter data frame to select states
museums_states <- museums_df %>%
filter(State..Administrative.Location. %in% c("IL", "CA", "NY"))
# Create and print bar plot with facets
museums_states <- ggplot(museums_states, aes(x = Is.Museum)) +
geom_bar() +
scale_x_discrete(labels = c( "TRUE" = "Museum", "FALSE" = "Non-Museum")) +
facet_grid(cols = vars(State..Administrative.Location.))
museums_states

Illinois has less number of Museums than any of the 3 states, and NY the most. California has more museums that “non-museums” as opposed to the other 2 states.
Our data also contains information on each museum’s region, representing groups of states. Create a stacked bar plot using museums_df showing the count of museums by region (Region.Code..AAM.), mapping Is.Museum to the fill aesthetic. Convert Region.Code..AAM. to a factor (e.g. factor(Region.Code..AAM.)) so ggplot2 plots its levels as discrete rather than continuous values. Call this plot museum_stacked.
museum_stacked <- ggplot(museums_df, aes(x = factor(Region.Code..AAM.), fill = Is.Museum)) +
geom_bar()
museum_stacked

Our plot is hard to read – right now, we don’t know what the region numbers correspond to. Use scale_x_discrete() to rename the numeric labels to text according to the following table.
1 |
New England |
2 |
Mid-Atlantic |
3 |
Southeastern |
4 |
Midwest |
5 |
Mountain Plains |
6 |
Western |
Similarly, add a scale_fill_discrete() layer to relabel the “TRUE” and “FALSE” labels in our legend to “Museum” and “Non-Museum”.
Based on the plot we created, which region has the most museums?
# Create and print stacked bar plot
museum_stacked <- ggplot(museums_df, aes(x = factor(Region.Code..AAM.), fill = Is.Museum)) +
geom_bar() +
scale_x_discrete(labels = c( "1" = "New England",
"2" = "Mid-Atlantic",
"3" = "Southeastern",
"4" = "Midwest",
"5" = "Montain Plains",
"6" = "Western")) +
scale_fill_discrete(labels = c( "TRUE" = "Museum", "FALSE" = "Non-Museum"))
museum_stacked

Midwest has the most museums.
Rather than seeing counts, perhaps we’re more interested in the percentage of museums vs non-museums by region. Transform the plot we just created to a stacked bar plot showing values out of 100% by passing position = “fill” to our geom_bar() layer. Apply the scales::percent_format() function to transform our y axis labels into percentage values.
How does the distribution of museum types vary by region?
# Create and print stacked bar plot
museum_stacked <- ggplot(museums_df, aes(x = factor(Region.Code..AAM.), fill = Is.Museum)) +
geom_bar(position = "fill") +
scale_x_discrete(labels = c( "1" = "New England",
"2" = "Mid-Atlantic",
"3" = "Southeastern",
"4" = "Midwest",
"5" = "Montain Plains",
"6" = "Western")) +
scale_fill_discrete(labels = c( "TRUE" = "Museum", "FALSE" = "Non-Museum"), ) +
scale_y_continuous(labels = scales::percent_format())
museum_stacked

Midwest has less percentage of museums. Southeastern, Montain Plains and Western has a similar distribution. New England and Mid-Atlantic are in between.
Our graph looks pretty good! However, our axes titles are a little non-descript. Using the labs() layer, let’s title this plot “Museum Types by Region”, relabel the x axis title as “Region”, relabel the y axis title as “Percentage of Total”, and relabel the fill legend title as “Type”.
museum_stacked <- ggplot(museums_df, aes(x = factor(Region.Code..AAM.), fill = Is.Museum)) +
geom_bar(position = "fill") +
scale_x_discrete(labels = c( "1" = "New England",
"2" = "Mid-Atlantic",
"3" = "Southeastern",
"4" = "Midwest",
"5" = "Montain Plains",
"6" = "Western")) +
scale_fill_discrete(labels = c( "TRUE" = "Museum", "FALSE" = "Non-Museum"), ) +
scale_y_continuous(labels = scales::percent_format()) +
labs(title = "Museum Types by Region", x = "Region", y = "Percentage of Total", fill = "Type")
museum_stacked

Museums by Revenue
For the next few tasks, we’ll switch to looking at how much money each institution brought in and how that varies across geographies. Because we only have revenue data at the parent organization level, we’ll want to first filter our dataset to omit any duplicates. Next, we’ll create a few data frames from our starting data to look at different groups of museums by how much money they bring in.
Create a new data frame called museums_revenue_df that retains only unique values of Legal.Name in museums_df. Additionally, filter this data frame to include only entities with Annual.Revenue greater than 0.
# Filter data frame
museums_revenue_df <- museums_df %>%
distinct(Legal.Name, .keep_all = TRUE) %>%
filter(Annual.Revenue > 0)
Create a second data frame from museums_revenue_df (the first data frame we created in this task) called museums_small_df that retains only museums with Annual.Revenue less than $1,000,000.
# Filter for only small museums
museums_small_df <- museums_revenue_df %>%
filter(Annual.Revenue < 1000000)
Create a third data frame from museums_revenue_df (the first data frame we created in this task) called museums_large_df that retains only museums with Annual.Revenue greater than $1,000,000,000.
# Filter for only large museums
museums_large_df <- museums_revenue_df %>%
filter(Annual.Revenue >= 1000000)
Let’s start by visualizing the distribution of annual revenue for our small museums dataset. Create a histogram called revenue_histogram using museums_small_df with Annual.Revenue mapped to the x axis. Experiment with different binwidth values to see what works best for our data, considering that our x axis variable ranges from 0 to $1,000,000.
# Create and print histogram
revenue_histogram <- ggplot(museums_small_df, aes(x = Annual.Revenue)) +
geom_histogram(binwidth = 25000)
revenue_histogram

Our x axis is a little hard to read. Let’s make it more clear! Add a scale_x_continuous() layer applying the function scales::dollar_format() to our x axis labels. dollar_format() is a function from the scales library included in ggplot2 that adds dollar signs and commas to monetary data.
revenue_histogram +
scale_x_continuous(labels = scales::dollar_format())

Now, let’s look at the variation in revenue for large museums by region. Create a boxplot called revenue_boxplot using museums_large_df, mapping Region.Code..AAM. to the x axis and Annual.Revenue to the y axis. Remember to convert Region.Code..AAM. to a factor (e.g. factor(Region.Code..AAM.)) so ggplot2 plots its levels as discrete rather than continuous values. Use scale_x_discrete() to rename the numeric region codes to their text equivalents.
revenue_boxplot <- ggplot(museums_large_df, aes(x = factor(Region.Code..AAM.), y = Annual.Revenue)) +
geom_boxplot() +
scale_x_discrete(labels = c( "1" = "New England",
"2" = "Mid-Atlantic",
"3" = "Southeastern",
"4" = "Midwest",
"5" = "Montain Plains",
"6" = "Western"))
revenue_boxplot

The plot we just created is a little hard to read, since there’s one outlier so far above all the other data points. This one museum made a lot of money in 2013! Let’s zoom in so we can see the rest of our boxes more clearly. Add a coord_cartesian() layer setting ylim to c(1e9, 3e10). This tells our plot to zoom in on the y axis range between $1,000,000,000 and $30,000,000,000. How do the median, 75th, and 25th quantiles vary by region?
revenue_boxplot <- revenue_boxplot +
coord_cartesian(ylim = c(1e9, 3e10))
revenue_boxplot

Though we can see the distribution by region more clearly now, our y axis label is hard to understand. Let’s reformat our y axis as billions of dollars. The function defined below will convert values like $1,000,000,000 to $1B, which is much easier to read. Add a scale_y_continuous() layer to map our y axis labels using this
function(x) paste0("$", x/1e9, "B")
We’ve made our box plot much clearer to read.
revenue_boxplot +
scale_y_continuous(labels = function(x) paste0("$", x/1e9, "B"))

Now, let’s take a look at revenue across all museums in our dataset. Using museums_revenue_df, create a bar plot called revenue_barplot mapping Region.Code..AAM. to the x axis and Annual.Revenue to the y axis. Remember to transform Region.Code..AAM. to a factor (factor(Region.Code..AAM.)) so it shows up as discrete values.
Use stat = “summary” and fun = “mean” to calculate and display the mean revenue by region. Apply the appropriate x and y axis label transformations to make our labels more clear. Which region has the highest and lowest mean revenues?
revenue_barplot <- ggplot(museums_revenue_df, aes(x = factor(Region.Code..AAM.), y = Annual.Revenue)) +
geom_bar(stat = "summary", fun = "mean") +
scale_x_discrete(labels = c( "1" = "New England",
"2" = "Mid-Atlantic",
"3" = "Southeastern",
"4" = "Midwest",
"5" = "Montain Plains",
"6" = "Western")) +
scale_y_continuous(labels = function(x) paste0("$", x/1e6, "M"))
revenue_barplot

Once again, use the labs layer to make our plot more clear. Title the plot “Mean Annual Revenue by Region”, relabel the y axis title to “Mean Annual Revenue”, and relabel the x axis title to “Region”.
revenue_barplot +
labs(title = "Mean Annual Revenue by Region", x = "Region", y = "Mean Annual Revenue")

Finally, let’s add some error bars to our means. We’ll need to calculate our standard errors before creating our plot. You can do so using the following code:
museums_error_df <- museums_revenue_df %>%
group_by(Region.Code..AAM.) %>%
summarize(
Mean.Revenue = mean(Annual.Revenue),
Mean.SE = std.error(Annual.Revenue)) %>%
mutate(
SE.Min = Mean.Revenue - Mean.SE,
SE.Max = Mean.Revenue + Mean.SE)
Add error bars to our mean revenue by geography bar plot using the geom_errorbar() layer. Call this new plot revenue_errorbar. Our new y variable is our calculated Mean.Revenue column. Make sure to change the stat being used, since we’re now displaying our calculated means as is rather than calculating them as we create the plot. Which regions have more or less variability around their mean revenues?
museums_error_df <- museums_revenue_df %>%
group_by(Region.Code..AAM.) %>%
summarize(
Mean.Revenue = mean(Annual.Revenue),
Mean.SE = std.error(Annual.Revenue)) %>%
mutate(
SE.Min = Mean.Revenue - Mean.SE,
SE.Max = Mean.Revenue + Mean.SE)
`summarise()` ungrouping output (override with `.groups` argument)
revenue_errorbar <- ggplot(museums_error_df, aes(x = factor(Region.Code..AAM.), y = Mean.Revenue)) +
geom_bar(stat = "identity") +
scale_x_discrete(labels = c( "1" = "New England",
"2" = "Mid-Atlantic",
"3" = "Southeastern",
"4" = "Midwest",
"5" = "Montain Plains",
"6" = "Western")) +
scale_y_continuous(labels = function(x) paste0("$", x/1e6, "M")) +
labs(title = "Mean Annual Revenue by Region", x = "Region", y = "Mean Annual Revenue") +
geom_errorbar(aes(ymin = SE.Min, ymax = SE.Max), width = 0.2)
revenue_errorbar

LS0tDQp0aXRsZTogIk11c2V1bXMgYW5kIE5hdHVyZSBDZW50ZXJzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NClRoZXJlIGFyZSB0aG91c2FuZHMgb2YgbXVzZXVtcywgYXF1YXJpdW1zLCBhbmQgem9vcyBhY3Jvc3MgdGhlIFVuaXRlZCBTdGF0ZXMuIEluIHRoaXMgcHJvamVjdCwgd2XigJlsbCB0YWtlIGEgbG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZXNlIGluc3RpdHV0aW9ucyBieSBnZW9ncmFwaGljIHJlZ2lvbiwgdHlwZSwgYW5kIHJldmVudWUuDQoNCk91ciBkYXRhIGlzIGNvbXBpbGVkIGZyb20gYWRtaW5pc3RyYXRpdmUgcmVjb3JkcyBmcm9tIHRoZSBJbnN0aXR1dGUgb2YgTXVzZXVtIGFuZCBMaWJyYXJ5IFNlcnZpY2VzLCBJUlMgcmVjb3JkcywgYW5kIHByaXZhdGUgZm91bmRhdGlvbiBncmFudG1ha2luZyByZWNvcmRzLiBUaGlzIGRhdGEgcmVmbGVjdHMgdGhlIHN0YXR1cyBvZiBlYWNoIGluc3RpdHV0aW9uIGFzIG9mIDIwMTMuIEZvciBlYWNoIGluc3RpdHV0aW9uLCB3ZSBoYXZlIGluZm9ybWF0aW9uIG9uIGl0cyBuYW1lLCB0eXBlLCBhbmQgbG9jYXRpb24uIEVhY2ggaW5zdGl0dXRpb24gYWxzbyBoYXMgYSBwYXJlbnQgb3JnYW5pemF0aW9uIOKAkyBmb3IgZXhhbXBsZSwgaWYgYSBtdXNldW0gaG91c2VkIGF0IGEgdW5pdmVyc2l0eSwgaXRzIHBhcmVudCBvcmdhbml6YXRpb24gaXMgdGhlIHVuaXZlcnNpdHkgd2hlcmUgaXQgcmVzaWRlcy4gRmluYW5jaWFsIGRhdGEgb24gYW5udWFsIHJldmVudWUgaXMgYXZhaWxhYmxlIGF0IHRoZSBwYXJlbnQgb3JnYW5pemF0aW9uIGxldmVsLg0KYGBge3IgZGF0YSwgbWVzc2FnZT1GQUxTRX0NCg0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoc3RyaW5ncikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KHBsb3RyaXgpDQoNCmBgYA0KIyMgRGF0YSBFeHBsb3JhdGlvbg0KDQpMZXTigJlzIHN0YXJ0IGJ5IGxvYWRpbmcgb3VyIGRhdGFzZXQuIFdl4oCZdmUgcHJvdmlkZWQgYSBmaWxlIG5hbWVkIG11c2V1bXMuY3N2LiBMb2FkIHRoaXMgZmlsZSBpbnRvIGEgZGF0YSBmcmFtZSBuYW1lZCBtdXNldW1zX2RmLg0KYGBge3IgbG9hZCwgbWVzc2FnZT1GQUxTRX0NCiMgTG9hZCBmaWxlIGFzIGRhdGEgZnJhbWUNCm11c2V1bXNfZGYgPC0gcmVhZC5jc3YoIm11c2V1bXMuY3N2IikNCmBgYA0KVGFrZSBhIGxvb2sgYXQgdGhlIGhlYWQgb2YgdGhpcyBkYXRhIGZyYW1lLiBNYWtlIHN1cmUgdG8gY2xpY2sgdGhyb3VnaCB1c2luZyB0aGUgYXJyb3dzIGluIHRoZSBoZWFkZXIgdG8gc2VlIGFsbCB0aGUgYXZhaWxhYmxlIGNvbHVtbnMuDQpgYGB7ciBpbnNwZWN0LCBtZXNzYWdlPUZBTFNFfQ0KIyBJbnNwZWN0IGRhdGEgZnJhbWUNCmhlYWQobXVzZXVtc19kZikNCmBgYA0KVGhlIE11c2V1bS5OYW1lIGNvbHVtbiByZXByZXNlbnRzIHRoZSBuYW1lIG9mIGVhY2ggaW5kaXZpZHVhbCBpbnN0aXR1dGlvbiwgd2hpbGUgdGhlIExlZ2FsLk5hbWUgY29sdW1uIHJlcHJlc2VudHMgdGhlIG5hbWUgb2YgZWFjaCBpbnN0aXR1dGlvbuKAmXMgcGFyZW50IGVudGl0eS4gRm9yIGV4YW1wbGUsIGlmICJDb2RlY2FkZW15IFVuaXZlcnNpdHkiIGhhcyB0d28gbXVzZXVtcyBvbiBjYW1wdXMsIGVhY2ggb2YgdGhvc2UgbXVzZXVtcyB3b3VsZCBoYXZlIHRoZWlyIG93biBuYW1lcyB1bmRlciBNdXNldW0uTmFtZSBhbmQgYm90aCB3b3VsZCBzaGFyZSB0aGUgc2FtZSBMZWdhbC5OYW1lIGkuZS4gIkNvZGVjYWRlbXkgVW5pdmVyc2l0eSIuDQoNCiMjIE11c2V1bXMgYnkgVHlwZQ0KDQoNCkluIHRoaXMgc2VjdGlvbiwgd2XigJlsbCBleHBsb3JlIHRoZSBkaXN0cmlidXRpb24gb2YgaW5zdGl0dXRpb25zIGluIG91ciBkYXRhc2V0IGJ5IHR5cGUuIE91ciBkYXRhIGZyYW1lIGNvbnRhaW5zIGEgY29sdW1uIGNhbGxlZCBNdXNldW0uVHlwZSBkZXNjcmliaW5nIHdoYXQga2luZCBvZiBtdXNldW0gZWFjaCBsb2NhdGlvbiBpcyDigJMgYSBoaXN0b3J5IG11c2V1bSwgYSB6b28sIGFuIGFxdWFyaXVtLCBldGMuIENyZWF0ZSBhbmQgcHJpbnQgYSBiYXIgcGxvdCBjYWxsZWQgbXVzZXVtX3R5cGUgdGhhdCBtYXBzIE11c2V1bS5UeXBlIHRvIHRoZSB4IGF4aXMgYW5kIGNvdW50cyB0aGUgZnJlcXVlbmN5IG9mIGVhY2ggdHlwZSBvbiB0aGUgeSBheGlzLiBXaGljaCBjYXRlZ29yeSBpcyBtb3N0IGNvbW1vbj8NCmBgYHtyfQ0KbXVzZXVtX3R5cGUgPC0gZ2dwbG90KG11c2V1bXNfZGYsIGFlcyh4ID0gTXVzZXVtLlR5cGUpKSArIA0KZ2VvbV9iYXIoKQ0KbXVzZXVtX3R5cGUNCmBgYA0KVGhlIHBsb3Qgd2UganVzdCBjcmVhdGVkIGlzIGhhcmQgdG8gcmVhZCBiZWNhdXNlIG91ciBjYXRlZ29yaWVzIGFyZSBsb25nLiBBZGQgYSBzY2FsZV94X2Rpc2NyZXRlKCkgbGF5ZXIgdG8gY3VzdG9taXplIG91ciB4IGF4aXMsIHVzaW5nIHRoZSBmdW5jdGlvbiBzY2FsZXM6OndyYXBfZm9ybWF0KDgpIHRvIHJlZm9ybWF0IG91ciBsYWJlbHMuDQoNCndyYXBfZm9ybWF0KCkgaXMgYSBmdW5jdGlvbiBmcm9tIHRoZSBzY2FsZXMgcGFja2FnZXMgd2hpY2ggY29tZXMgaW5jbHVkZWQgd2l0aCBnZ3Bsb3QyLiBCeSBzZXR0aW5nIHRoZSB2YWx1ZSBvZiB3cmFwX2Zvcm1hdCgpIHRvIDgsIHdlIGFyZSB0ZWxsaW5nIGl0IHRoYXQgdGhlIG1heGltdW0gd2lkdGggcGVyIGxpbmUgc2hvdWxkIGJlIG5vIG1vcmUgdGhhbiA4IGNoYXJhY3RlcnMuDQoNCk5vdyB3ZSBzaG91bGQgYmUgYWJsZSB0byBzZWUgd2hpY2ggY2F0ZWdvcnkgaXMgbW9zdCBjb21tb24uDQpgYGB7ciBiYXJwbG90LCBtZXNzYWdlPUZBTFNFfQ0KIyBDcmVhdGUgYW5kIHByaW50IGJhciBwbG90IGJ5IHR5cGUNCm11c2V1bV90eXBlIDwtIGdncGxvdChtdXNldW1zX2RmLCBhZXMoeCA9IE11c2V1bS5UeXBlKSkgKyANCmdlb21fYmFyKCkgKw0Kc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBzY2FsZXM6OndyYXBfZm9ybWF0KDgpKQ0KbXVzZXVtX3R5cGUNCmBgYA0KV2XigJl2ZSBpbmNsdWRlZCBhIGJvb2xlYW4gKFRSVUUgb3IgRkFMU0UpIGNvbHVtbiBpbiBvdXIgZGF0YSBmcmFtZSBjYWxsZWQgSXMuTXVzZXVtLiBUaGUgVFJVRSBjYXRlZ29yeSBpbmNsdWRlcyB0eXBpY2FsIG11c2V1bXMgbGlrZSBhcnQsIGhpc3RvcnksIGFuZCBzY2llbmNlIG11c2V1bXMuIFRoZSBGQUxTRSBjYXRlZ29yeSBpbmNsdWRlcyB6b29zLCBhcXVhcml1bXMsIG5hdHVyZSBwcmVzZXJ2ZXMsIGFuZCBoaXN0b3JpYyBzaXRlcywgd2hpY2ggYXJlIGluY2x1ZGVkIGluIHRoaXMgZGF0YSBidXQgYXJlbuKAmXQgd2hhdCBtb3N0IHBlb3BsZSB0aGluayBvZiB3aGVuIHRoZXkgaGVhciB0aGUgd29yZCDigJxtdXNldW0u4oCdDQoNCkNyZWF0ZSBhIG5ldyBiYXIgcGxvdCBjYWxsZWQgbXVzZXVtX2NsYXNzLCBtYXBwaW5nIElzLk11c2V1bSB0byB0aGUgeCBheGlzLiBTaW5jZSDigJxUUlVF4oCdIGFuZCDigJxGQUxTReKAnSBhcmVu4oCZdCB2ZXJ5IGRlc2NyaXB0aXZlLCB1c2Ugc2NhbGVfeF9kaXNjcmV0ZSgpIHRvIHJlbmFtZSB0aGUgeCBheGlzIGxhYmVscyB0byBtb3JlIGVhc2lseSB1bmRlcnN0b29kIHRlcm1zIOKAkyBmb3IgZXhhbXBsZSwg4oCcTXVzZXVt4oCdIHZzIOKAnE5vbi1NdXNldW3igJ0uDQpgYGB7ciBiYXJwbG90X211c2V1bSwgbWVzc2FnZT1GQUxTRX0NCiMgQ3JlYXRlIGFuZCBwcmludCBiYXIgcGxvdCBieSBtdXNldW0gdnMgbm9uLW11c2V1bQ0KbXVzZXVtX2NsYXNzIDwtIGdncGxvdChtdXNldW1zX2RmLCBhZXMoeCA9IElzLk11c2V1bSkpICsNCmdlb21fYmFyKCkgKw0Kc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBjKCAiVFJVRSIgPSAiTXVzZXVtIiwgIkZBTFNFIiA9ICJOb24tTXVzZXVtIikpDQptdXNldW1fY2xhc3MNCmBgYA0KSW5zdGVhZCBvZiBsb29raW5nIGF0IHRoZSBkaXN0cmlidXRpb24gYWNyb3NzIHRoZSBlbnRpcmUgVW5pdGVkIFN0YXRlcywgbWF5YmUgd2XigJlyZSBqdXN0IGludGVyZXN0ZWQgaW4gYSBmZXcgc3RhdGVzLiBGaWx0ZXIgbXVzZXVtc19kZiB0byBpbmNsdWRlIGEgZmV3IHN0YXRlcyB5b3UgbWlnaHQgYmUgaW50ZXJlc3RlZCBpbiwgdXNpbmcgdGhlIFN0YXRlLi5BZG1pbmlzdHJhdGl2ZS5Mb2NhdGlvbi4gY29sdW1uOyBmb3IgZXhhbXBsZSwgd2UgY2FuIGNob29zZSBJTCwgQ0EsIGFuZCBOWS4gQ2FsbCB0aGlzIGZpbHRlcmVkIGRhdGEgZnJhbWUgbXVzZXVtc19zdGF0ZXMuDQoNCkFmdGVyIGNyZWF0aW5nIG11c2V1bXNfc3RhdGVzLCByZWNyZWF0ZSBvdXIgYmFyIHBsb3Qgc2hvd2luZyB0aGUgZGlzdHJpYnV0aW9uIG9mIG11c2V1bXMgdnMgbm9uLW11c2V1bXMgYW5kIHVzZSBmYWNldF9ncmlkKCkgdG8gZGlzcGxheSBlYWNoIHN0YXRl4oCZcyBkaXN0cmlidXRpb24gaW4gYSBzZXBhcmF0ZSBwYW5lbC4gQ2FsbCB0aGlzIHBsb3QgbXVzZXVtX2ZhY2V0LiBIb3cgZG9lcyB0aGUgZGlzdHJpYnV0aW9uIG9mIG11c2V1bSB2cyBub24tbXVzZXVtIHZhcnkgYWNyb3NzIHRoZSBzdGF0ZXMgeW91IGNob3NlPw0KDQpgYGB7cn0NCiMgRmlsdGVyIGRhdGEgZnJhbWUgdG8gc2VsZWN0IHN0YXRlcw0KbXVzZXVtc19zdGF0ZXMgPC0gbXVzZXVtc19kZiAlPiUNCiAgZmlsdGVyKFN0YXRlLi5BZG1pbmlzdHJhdGl2ZS5Mb2NhdGlvbi4gJWluJSBjKCJJTCIsICJDQSIsICJOWSIpKQ0KDQoNCiMgQ3JlYXRlIGFuZCBwcmludCBiYXIgcGxvdCB3aXRoIGZhY2V0cw0KbXVzZXVtX2ZhY2V0IDwtIGdncGxvdChtdXNldW1zX3N0YXRlcywgYWVzKHggPSBJcy5NdXNldW0pKSArDQpnZW9tX2JhcigpICsNCnNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gYyggIlRSVUUiID0gIk11c2V1bSIsICJGQUxTRSIgPSAiTm9uLU11c2V1bSIpKSArDQogIGZhY2V0X2dyaWQoY29scyA9IHZhcnMoU3RhdGUuLkFkbWluaXN0cmF0aXZlLkxvY2F0aW9uLikpDQptdXNldW1zX3N0YXRlcw0KYGBgDQpJbGxpbm9pcyBoYXMgbGVzcyBudW1iZXIgb2YgTXVzZXVtcyB0aGFuIGFueSBvZiB0aGUgMyBzdGF0ZXMsIGFuZCBOWSB0aGUgbW9zdC4gQ2FsaWZvcm5pYSBoYXMgbW9yZSBtdXNldW1zIHRoYXQgIm5vbi1tdXNldW1zIiBhcyBvcHBvc2VkIHRvIHRoZSBvdGhlciAyIHN0YXRlcy4NCg0KT3VyIGRhdGEgYWxzbyBjb250YWlucyBpbmZvcm1hdGlvbiBvbiBlYWNoIG11c2V1beKAmXMgcmVnaW9uLCByZXByZXNlbnRpbmcgZ3JvdXBzIG9mIHN0YXRlcy4gQ3JlYXRlIGEgc3RhY2tlZCBiYXIgcGxvdCB1c2luZyBtdXNldW1zX2RmIHNob3dpbmcgdGhlIGNvdW50IG9mIG11c2V1bXMgYnkgcmVnaW9uIChSZWdpb24uQ29kZS4uQUFNLiksIG1hcHBpbmcgSXMuTXVzZXVtIHRvIHRoZSBmaWxsIGFlc3RoZXRpYy4gQ29udmVydCBSZWdpb24uQ29kZS4uQUFNLiB0byBhIGZhY3RvciAoZS5nLiBmYWN0b3IoUmVnaW9uLkNvZGUuLkFBTS4pKSBzbyBnZ3Bsb3QyIHBsb3RzIGl0cyBsZXZlbHMgYXMgZGlzY3JldGUgcmF0aGVyIHRoYW4gY29udGludW91cyB2YWx1ZXMuIENhbGwgdGhpcyBwbG90IG11c2V1bV9zdGFja2VkLg0KYGBge3J9DQptdXNldW1fc3RhY2tlZCA8LSBnZ3Bsb3QobXVzZXVtc19kZiwgYWVzKHggPSBmYWN0b3IoUmVnaW9uLkNvZGUuLkFBTS4pLCBmaWxsID0gSXMuTXVzZXVtKSkgKw0KICBnZW9tX2JhcigpIA0KbXVzZXVtX3N0YWNrZWQNCmBgYA0KT3VyIHBsb3QgaXMgaGFyZCB0byByZWFkIOKAkyByaWdodCBub3csIHdlIGRvbuKAmXQga25vdyB3aGF0IHRoZSByZWdpb24gbnVtYmVycyBjb3JyZXNwb25kIHRvLiBVc2Ugc2NhbGVfeF9kaXNjcmV0ZSgpIHRvIHJlbmFtZSB0aGUgbnVtZXJpYyBsYWJlbHMgdG8gdGV4dCBhY2NvcmRpbmcgdG8gdGhlIGZvbGxvd2luZyB0YWJsZS4NCg0KQ29kZSB8IFJlZ2lvbg0KLS0tLS18LS0tLS0tLQ0KMSB8IE5ldyBFbmdsYW5kDQoyCXwgTWlkLUF0bGFudGljDQozCXwgU291dGhlYXN0ZXJuDQo0CXwgTWlkd2VzdA0KNQl8IE1vdW50YWluIFBsYWlucw0KNgl8IFdlc3Rlcm4NCg0KDQpTaW1pbGFybHksIGFkZCBhIHNjYWxlX2ZpbGxfZGlzY3JldGUoKSBsYXllciB0byByZWxhYmVsIHRoZSDigJxUUlVF4oCdIGFuZCDigJxGQUxTReKAnSBsYWJlbHMgaW4gb3VyIGxlZ2VuZCB0byDigJxNdXNldW3igJ0gYW5kIOKAnE5vbi1NdXNldW3igJ0uDQoNCkJhc2VkIG9uIHRoZSBwbG90IHdlIGNyZWF0ZWQsIHdoaWNoIHJlZ2lvbiBoYXMgdGhlIG1vc3QgbXVzZXVtcz8NCg0KYGBge3J9DQojIENyZWF0ZSBhbmQgcHJpbnQgc3RhY2tlZCBiYXIgcGxvdA0KbXVzZXVtX3N0YWNrZWQgPC0gZ2dwbG90KG11c2V1bXNfZGYsIGFlcyh4ID0gZmFjdG9yKFJlZ2lvbi5Db2RlLi5BQU0uKSwgZmlsbCA9IElzLk11c2V1bSkpICsNCiAgZ2VvbV9iYXIoKSArDQogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gYyggIjEiID0gIk5ldyBFbmdsYW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMiIgPSAiTWlkLUF0bGFudGljIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMyIgPSAiU291dGhlYXN0ZXJuIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiNCIgPSAiTWlkd2VzdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjUiID0gIk1vbnRhaW4gUGxhaW5zIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiNiIgPSAiV2VzdGVybiIpKSArDQogIHNjYWxlX2ZpbGxfZGlzY3JldGUobGFiZWxzID0gYyggIlRSVUUiID0gIk11c2V1bSIsICJGQUxTRSIgPSAiTm9uLU11c2V1bSIpKQ0KbXVzZXVtX3N0YWNrZWQNCmBgYA0KTWlkd2VzdCBoYXMgdGhlIG1vc3QgbXVzZXVtcy4NCg0KUmF0aGVyIHRoYW4gc2VlaW5nIGNvdW50cywgcGVyaGFwcyB3ZeKAmXJlIG1vcmUgaW50ZXJlc3RlZCBpbiB0aGUgcGVyY2VudGFnZSBvZiBtdXNldW1zIHZzIG5vbi1tdXNldW1zIGJ5IHJlZ2lvbi4gVHJhbnNmb3JtIHRoZSBwbG90IHdlIGp1c3QgY3JlYXRlZCB0byBhIHN0YWNrZWQgYmFyIHBsb3Qgc2hvd2luZyB2YWx1ZXMgb3V0IG9mIDEwMCUgYnkgcGFzc2luZyBwb3NpdGlvbiA9ICJmaWxsIiB0byBvdXIgZ2VvbV9iYXIoKSBsYXllci4gQXBwbHkgdGhlIHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSBmdW5jdGlvbiB0byB0cmFuc2Zvcm0gb3VyIHkgYXhpcyBsYWJlbHMgaW50byBwZXJjZW50YWdlIHZhbHVlcy4NCg0KSG93IGRvZXMgdGhlIGRpc3RyaWJ1dGlvbiBvZiBtdXNldW0gdHlwZXMgdmFyeSBieSByZWdpb24/DQoNCmBgYHtyfQ0KIyBDcmVhdGUgYW5kIHByaW50IHN0YWNrZWQgYmFyIHBsb3QNCm11c2V1bV9zdGFja2VkIDwtIGdncGxvdChtdXNldW1zX2RmLCBhZXMoeCA9IGZhY3RvcihSZWdpb24uQ29kZS4uQUFNLiksIGZpbGwgPSBJcy5NdXNldW0pKSArDQogIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKSArDQogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gYyggIjEiID0gIk5ldyBFbmdsYW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMiIgPSAiTWlkLUF0bGFudGljIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMyIgPSAiU291dGhlYXN0ZXJuIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiNCIgPSAiTWlkd2VzdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjUiID0gIk1vbnRhaW4gUGxhaW5zIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiNiIgPSAiV2VzdGVybiIpKSArDQogIHNjYWxlX2ZpbGxfZGlzY3JldGUobGFiZWxzID0gYyggIlRSVUUiID0gIk11c2V1bSIsICJGQUxTRSIgPSAiTm9uLU11c2V1bSIpLCApICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSkNCiAgDQptdXNldW1fc3RhY2tlZA0KYGBgDQpNaWR3ZXN0IGhhcyBsZXNzIHBlcmNlbnRhZ2Ugb2YgbXVzZXVtcy4gU291dGhlYXN0ZXJuLCBNb250YWluIFBsYWlucyBhbmQgV2VzdGVybiBoYXMgYSBzaW1pbGFyIGRpc3RyaWJ1dGlvbi4gTmV3IEVuZ2xhbmQgYW5kIE1pZC1BdGxhbnRpYyBhcmUgaW4gYmV0d2Vlbi4NCg0KT3VyIGdyYXBoIGxvb2tzIHByZXR0eSBnb29kISBIb3dldmVyLCBvdXIgYXhlcyB0aXRsZXMgYXJlIGEgbGl0dGxlIG5vbi1kZXNjcmlwdC4gVXNpbmcgdGhlIGxhYnMoKSBsYXllciwgbGV04oCZcyB0aXRsZSB0aGlzIHBsb3Qg4oCcTXVzZXVtIFR5cGVzIGJ5IFJlZ2lvbuKAnSwgcmVsYWJlbCB0aGUgeCBheGlzIHRpdGxlIGFzIOKAnFJlZ2lvbuKAnSwgcmVsYWJlbCB0aGUgeSBheGlzIHRpdGxlIGFzIOKAnFBlcmNlbnRhZ2Ugb2YgVG90YWzigJ0sIGFuZCByZWxhYmVsIHRoZSBmaWxsIGxlZ2VuZCB0aXRsZSBhcyDigJxUeXBl4oCdLg0KDQpgYGB7cn0NCm11c2V1bV9zdGFja2VkIDwtIGdncGxvdChtdXNldW1zX2RmLCBhZXMoeCA9IGZhY3RvcihSZWdpb24uQ29kZS4uQUFNLiksIGZpbGwgPSBJcy5NdXNldW0pKSArDQogIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKSArDQogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gYyggIjEiID0gIk5ldyBFbmdsYW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMiIgPSAiTWlkLUF0bGFudGljIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMyIgPSAiU291dGhlYXN0ZXJuIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiNCIgPSAiTWlkd2VzdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjUiID0gIk1vbnRhaW4gUGxhaW5zIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiNiIgPSAiV2VzdGVybiIpKSArDQogIHNjYWxlX2ZpbGxfZGlzY3JldGUobGFiZWxzID0gYyggIlRSVUUiID0gIk11c2V1bSIsICJGQUxTRSIgPSAiTm9uLU11c2V1bSIpLCApICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSkgKw0KICBsYWJzKHRpdGxlID0gIk11c2V1bSBUeXBlcyBieSBSZWdpb24iLCB4ID0gIlJlZ2lvbiIsIHkgPSAiUGVyY2VudGFnZSBvZiBUb3RhbCIsIGZpbGwgPSAiVHlwZSIpDQogIA0KbXVzZXVtX3N0YWNrZWQNCmBgYA0KDQoNCiMjIE11c2V1bXMgYnkgUmV2ZW51ZQ0KDQpGb3IgdGhlIG5leHQgZmV3IHRhc2tzLCB3ZeKAmWxsIHN3aXRjaCB0byBsb29raW5nIGF0IGhvdyBtdWNoIG1vbmV5IGVhY2ggaW5zdGl0dXRpb24gYnJvdWdodCBpbiBhbmQgaG93IHRoYXQgdmFyaWVzIGFjcm9zcyBnZW9ncmFwaGllcy4gQmVjYXVzZSB3ZSBvbmx5IGhhdmUgcmV2ZW51ZSBkYXRhIGF0IHRoZSBwYXJlbnQgb3JnYW5pemF0aW9uIGxldmVsLCB3ZeKAmWxsIHdhbnQgdG8gZmlyc3QgZmlsdGVyIG91ciBkYXRhc2V0IHRvIG9taXQgYW55IGR1cGxpY2F0ZXMuIE5leHQsIHdl4oCZbGwgY3JlYXRlIGEgZmV3IGRhdGEgZnJhbWVzIGZyb20gb3VyIHN0YXJ0aW5nIGRhdGEgdG8gbG9vayBhdCBkaWZmZXJlbnQgZ3JvdXBzIG9mIG11c2V1bXMgYnkgaG93IG11Y2ggbW9uZXkgdGhleSBicmluZyBpbi4NCg0KDQpDcmVhdGUgYSBuZXcgZGF0YSBmcmFtZSBjYWxsZWQgbXVzZXVtc19yZXZlbnVlX2RmIHRoYXQgcmV0YWlucyBvbmx5IHVuaXF1ZSB2YWx1ZXMgb2YgTGVnYWwuTmFtZSBpbiBtdXNldW1zX2RmLiBBZGRpdGlvbmFsbHksIGZpbHRlciB0aGlzIGRhdGEgZnJhbWUgdG8gaW5jbHVkZSBvbmx5IGVudGl0aWVzIHdpdGggQW5udWFsLlJldmVudWUgZ3JlYXRlciB0aGFuIDAuDQpgYGB7ciBtZXNzYWdlPUZBTFNFfQ0KIyBGaWx0ZXIgZGF0YSBmcmFtZQ0KbXVzZXVtc19yZXZlbnVlX2RmIDwtIG11c2V1bXNfZGYgJT4lDQogIGRpc3RpbmN0KExlZ2FsLk5hbWUsIC5rZWVwX2FsbCA9IFRSVUUpICU+JSANCiAgZmlsdGVyKEFubnVhbC5SZXZlbnVlID4gMCkNCmBgYA0KDQpDcmVhdGUgYSBzZWNvbmQgZGF0YSBmcmFtZSBmcm9tIG11c2V1bXNfcmV2ZW51ZV9kZiAodGhlIGZpcnN0IGRhdGEgZnJhbWUgd2UgY3JlYXRlZCBpbiB0aGlzIHRhc2spIGNhbGxlZCBtdXNldW1zX3NtYWxsX2RmIHRoYXQgcmV0YWlucyBvbmx5IG11c2V1bXMgd2l0aCBBbm51YWwuUmV2ZW51ZSBsZXNzIHRoYW4gJDEsMDAwLDAwMC4NCmBgYHtyIG1lc3NhZ2U9RkFMU0V9DQojIEZpbHRlciBmb3Igb25seSBzbWFsbCBtdXNldW1zDQptdXNldW1zX3NtYWxsX2RmIDwtIG11c2V1bXNfcmV2ZW51ZV9kZiAlPiUgDQogIGZpbHRlcihBbm51YWwuUmV2ZW51ZSA8IDEwMDAwMDApDQpgYGANCg0KQ3JlYXRlIGEgdGhpcmQgZGF0YSBmcmFtZSBmcm9tIG11c2V1bXNfcmV2ZW51ZV9kZiAodGhlIGZpcnN0IGRhdGEgZnJhbWUgd2UgY3JlYXRlZCBpbiB0aGlzIHRhc2spIGNhbGxlZCBtdXNldW1zX2xhcmdlX2RmIHRoYXQgcmV0YWlucyBvbmx5IG11c2V1bXMgd2l0aCBBbm51YWwuUmV2ZW51ZSBncmVhdGVyIHRoYW4gJDEsMDAwLDAwMCwwMDAuDQpgYGB7ciBwcm9jZXNzLCBtZXNzYWdlPUZBTFNFfQ0KIyBGaWx0ZXIgZm9yIG9ubHkgbGFyZ2UgbXVzZXVtcw0KbXVzZXVtc19sYXJnZV9kZiA8LSBtdXNldW1zX3JldmVudWVfZGYgJT4lIA0KICBmaWx0ZXIoQW5udWFsLlJldmVudWUgPj0gMTAwMDAwMCkNCmBgYA0KTGV04oCZcyBzdGFydCBieSB2aXN1YWxpemluZyB0aGUgZGlzdHJpYnV0aW9uIG9mIGFubnVhbCByZXZlbnVlIGZvciBvdXIgc21hbGwgbXVzZXVtcyBkYXRhc2V0LiBDcmVhdGUgYSBoaXN0b2dyYW0gY2FsbGVkIHJldmVudWVfaGlzdG9ncmFtIHVzaW5nIG11c2V1bXNfc21hbGxfZGYgd2l0aCBBbm51YWwuUmV2ZW51ZSBtYXBwZWQgdG8gdGhlIHggYXhpcy4gRXhwZXJpbWVudCB3aXRoIGRpZmZlcmVudCBiaW53aWR0aCB2YWx1ZXMgdG8gc2VlIHdoYXQgd29ya3MgYmVzdCBmb3Igb3VyIGRhdGEsIGNvbnNpZGVyaW5nIHRoYXQgb3VyIHggYXhpcyB2YXJpYWJsZSByYW5nZXMgZnJvbSAwIHRvICQxLDAwMCwwMDAuDQpgYGB7ciBoaXN0b2dyYW1fc21hbGxfcmV2ZW51ZSwgbWVzc2FnZT1GQUxTRX0NCiMgQ3JlYXRlIGFuZCBwcmludCBoaXN0b2dyYW0NCnJldmVudWVfaGlzdG9ncmFtIDwtIGdncGxvdChtdXNldW1zX3NtYWxsX2RmLCBhZXMoeCA9IEFubnVhbC5SZXZlbnVlKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDI1MDAwKQ0KcmV2ZW51ZV9oaXN0b2dyYW0NCmBgYA0KT3VyIHggYXhpcyBpcyBhIGxpdHRsZSBoYXJkIHRvIHJlYWQuIExldOKAmXMgbWFrZSBpdCBtb3JlIGNsZWFyISBBZGQgYSBzY2FsZV94X2NvbnRpbnVvdXMoKSBsYXllciBhcHBseWluZyB0aGUgZnVuY3Rpb24gc2NhbGVzOjpkb2xsYXJfZm9ybWF0KCkgdG8gb3VyIHggYXhpcyBsYWJlbHMuIGRvbGxhcl9mb3JtYXQoKSBpcyBhIGZ1bmN0aW9uIGZyb20gdGhlIHNjYWxlcyBsaWJyYXJ5IGluY2x1ZGVkIGluIGdncGxvdDIgdGhhdCBhZGRzIGRvbGxhciBzaWducyBhbmQgY29tbWFzIHRvIG1vbmV0YXJ5IGRhdGEuDQpgYGB7cn0NCnJldmVudWVfaGlzdG9ncmFtIDwtIHJldmVudWVfaGlzdG9ncmFtICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6ZG9sbGFyX2Zvcm1hdCgpKQ0KcmV2ZW51ZV9oaXN0b2dyYW0NCmBgYA0KTm93LCBsZXTigJlzIGxvb2sgYXQgdGhlIHZhcmlhdGlvbiBpbiByZXZlbnVlIGZvciBsYXJnZSBtdXNldW1zIGJ5IHJlZ2lvbi4gQ3JlYXRlIGEgYm94cGxvdCBjYWxsZWQgcmV2ZW51ZV9ib3hwbG90IHVzaW5nIG11c2V1bXNfbGFyZ2VfZGYsIG1hcHBpbmcgUmVnaW9uLkNvZGUuLkFBTS4gdG8gdGhlIHggYXhpcyBhbmQgQW5udWFsLlJldmVudWUgdG8gdGhlIHkgYXhpcy4gUmVtZW1iZXIgdG8gY29udmVydCBSZWdpb24uQ29kZS4uQUFNLiB0byBhIGZhY3RvciAoZS5nLiBmYWN0b3IoUmVnaW9uLkNvZGUuLkFBTS4pKSBzbyBnZ3Bsb3QyIHBsb3RzIGl0cyBsZXZlbHMgYXMgZGlzY3JldGUgcmF0aGVyIHRoYW4gY29udGludW91cyB2YWx1ZXMuIFVzZSBzY2FsZV94X2Rpc2NyZXRlKCkgdG8gcmVuYW1lIHRoZSBudW1lcmljIHJlZ2lvbiBjb2RlcyB0byB0aGVpciB0ZXh0IGVxdWl2YWxlbnRzLg0KYGBge3J9DQpyZXZlbnVlX2JveHBsb3QgIDwtIGdncGxvdChtdXNldW1zX2xhcmdlX2RmLCBhZXMoeCA9IGZhY3RvcihSZWdpb24uQ29kZS4uQUFNLiksIHkgPSBBbm51YWwuUmV2ZW51ZSkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGMoICIxIiA9ICJOZXcgRW5nbGFuZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjIiID0gIk1pZC1BdGxhbnRpYyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjMiID0gIlNvdXRoZWFzdGVybiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjQiID0gIk1pZHdlc3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI1IiA9ICJNb250YWluIFBsYWlucyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjYiID0gIldlc3Rlcm4iKSkNCnJldmVudWVfYm94cGxvdA0KYGBgDQpUaGUgcGxvdCB3ZSBqdXN0IGNyZWF0ZWQgaXMgYSBsaXR0bGUgaGFyZCB0byByZWFkLCBzaW5jZSB0aGVyZeKAmXMgb25lIG91dGxpZXIgc28gZmFyIGFib3ZlIGFsbCB0aGUgb3RoZXIgZGF0YSBwb2ludHMuIFRoaXMgb25lIG11c2V1bSBtYWRlIGEgbG90IG9mIG1vbmV5IGluIDIwMTMhIExldOKAmXMgem9vbSBpbiBzbyB3ZSBjYW4gc2VlIHRoZSByZXN0IG9mIG91ciBib3hlcyBtb3JlIGNsZWFybHkuIEFkZCBhIGNvb3JkX2NhcnRlc2lhbigpIGxheWVyIHNldHRpbmcgeWxpbSB0byBjKDFlOSwgM2UxMCkuIFRoaXMgdGVsbHMgb3VyIHBsb3QgdG8gem9vbSBpbiBvbiB0aGUgeSBheGlzIHJhbmdlIGJldHdlZW4gXCQxLDAwMCwwMDAsMDAwIGFuZCBcJDMwLDAwMCwwMDAsMDAwLiBIb3cgZG8gdGhlIG1lZGlhbiwgNzV0aCwgYW5kIDI1dGggcXVhbnRpbGVzIHZhcnkgYnkgcmVnaW9uPw0KDQpgYGB7cn0NCnJldmVudWVfYm94cGxvdCAgPC0gcmV2ZW51ZV9ib3hwbG90ICsNCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDFlOSwgM2UxMCkpDQpyZXZlbnVlX2JveHBsb3QNCmBgYA0KVGhvdWdoIHdlIGNhbiBzZWUgdGhlIGRpc3RyaWJ1dGlvbiBieSByZWdpb24gbW9yZSBjbGVhcmx5IG5vdywgb3VyIHkgYXhpcyBsYWJlbCBpcyBoYXJkIHRvIHVuZGVyc3RhbmQuIExldOKAmXMgcmVmb3JtYXQgb3VyIHkgYXhpcyBhcyBiaWxsaW9ucyBvZiBkb2xsYXJzLiBUaGUgZnVuY3Rpb24gZGVmaW5lZCBiZWxvdyB3aWxsIGNvbnZlcnQgdmFsdWVzIGxpa2UgJDEsMDAwLDAwMCwwMDAgdG8gXCQxQiwgd2hpY2ggaXMgbXVjaCBlYXNpZXIgdG8gcmVhZC4gQWRkIGEgc2NhbGVfeV9jb250aW51b3VzKCkgbGF5ZXIgdG8gbWFwIG91ciB5IGF4aXMgbGFiZWxzIHVzaW5nIHRoaXMgDQoNCiAgICBmdW5jdGlvbih4KSBwYXN0ZTAoIiQiLCB4LzFlOSwgIkIiKQ0KDQpXZeKAmXZlIG1hZGUgb3VyIGJveCBwbG90IG11Y2ggY2xlYXJlciB0byByZWFkLiANCmBgYHtyfQ0KcmV2ZW51ZV9ib3hwbG90ICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGZ1bmN0aW9uKHgpIHBhc3RlMCgiJCIsIHgvMWU5LCAiQiIpKQ0KYGBgDQpOb3csIGxldOKAmXMgdGFrZSBhIGxvb2sgYXQgcmV2ZW51ZSBhY3Jvc3MgYWxsIG11c2V1bXMgaW4gb3VyIGRhdGFzZXQuIFVzaW5nIG11c2V1bXNfcmV2ZW51ZV9kZiwgY3JlYXRlIGEgYmFyIHBsb3QgY2FsbGVkIHJldmVudWVfYmFycGxvdCBtYXBwaW5nIFJlZ2lvbi5Db2RlLi5BQU0uIHRvIHRoZSB4IGF4aXMgYW5kIEFubnVhbC5SZXZlbnVlIHRvIHRoZSB5IGF4aXMuIFJlbWVtYmVyIHRvIHRyYW5zZm9ybSBSZWdpb24uQ29kZS4uQUFNLiB0byBhIGZhY3RvciAoZmFjdG9yKFJlZ2lvbi5Db2RlLi5BQU0uKSkgc28gaXQgc2hvd3MgdXAgYXMgZGlzY3JldGUgdmFsdWVzLg0KDQpVc2Ugc3RhdCA9ICJzdW1tYXJ5IiBhbmQgZnVuID0gIm1lYW4iIHRvIGNhbGN1bGF0ZSBhbmQgZGlzcGxheSB0aGUgbWVhbiByZXZlbnVlIGJ5IHJlZ2lvbi4gQXBwbHkgdGhlIGFwcHJvcHJpYXRlIHggYW5kIHkgYXhpcyBsYWJlbCB0cmFuc2Zvcm1hdGlvbnMgdG8gbWFrZSBvdXIgbGFiZWxzIG1vcmUgY2xlYXIuIFdoaWNoIHJlZ2lvbiBoYXMgdGhlIGhpZ2hlc3QgYW5kIGxvd2VzdCBtZWFuIHJldmVudWVzPw0KYGBge3J9DQpyZXZlbnVlX2JhcnBsb3QgPC0gZ2dwbG90KG11c2V1bXNfcmV2ZW51ZV9kZiwgYWVzKHggPSBmYWN0b3IoUmVnaW9uLkNvZGUuLkFBTS4pLCB5ID0gQW5udWFsLlJldmVudWUpKSArDQogIGdlb21fYmFyKHN0YXQgPSAic3VtbWFyeSIsIGZ1biA9ICJtZWFuIikgKw0KICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGMoICIxIiA9ICJOZXcgRW5nbGFuZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjIiID0gIk1pZC1BdGxhbnRpYyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjMiID0gIlNvdXRoZWFzdGVybiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjQiID0gIk1pZHdlc3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI1IiA9ICJNb250YWluIFBsYWlucyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjYiID0gIldlc3Rlcm4iKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gZnVuY3Rpb24oeCkgcGFzdGUwKCIkIiwgeC8xZTYsICJNIikpDQpyZXZlbnVlX2JhcnBsb3QNCmBgYA0KT25jZSBhZ2FpbiwgdXNlIHRoZSBsYWJzIGxheWVyIHRvIG1ha2Ugb3VyIHBsb3QgbW9yZSBjbGVhci4gVGl0bGUgdGhlIHBsb3Qg4oCcTWVhbiBBbm51YWwgUmV2ZW51ZSBieSBSZWdpb27igJ0sIHJlbGFiZWwgdGhlIHkgYXhpcyB0aXRsZSB0byDigJxNZWFuIEFubnVhbCBSZXZlbnVl4oCdLCBhbmQgcmVsYWJlbCB0aGUgeCBheGlzIHRpdGxlIHRvIOKAnFJlZ2lvbuKAnS4NCmBgYHtyfQ0KcmV2ZW51ZV9iYXJwbG90ICsNCiAgbGFicyh0aXRsZSA9ICJNZWFuIEFubnVhbCBSZXZlbnVlIGJ5IFJlZ2lvbiIsIHggPSAiUmVnaW9uIiwgeSA9ICJNZWFuIEFubnVhbCBSZXZlbnVlIikNCmBgYA0KRmluYWxseSwgbGV04oCZcyBhZGQgc29tZSBlcnJvciBiYXJzIHRvIG91ciBtZWFucy4gV2XigJlsbCBuZWVkIHRvIGNhbGN1bGF0ZSBvdXIgc3RhbmRhcmQgZXJyb3JzIGJlZm9yZSBjcmVhdGluZyBvdXIgcGxvdC4gWW91IGNhbiBkbyBzbyB1c2luZyB0aGUgZm9sbG93aW5nIGNvZGU6DQoNCiAgICBtdXNldW1zX2Vycm9yX2RmIDwtIG11c2V1bXNfcmV2ZW51ZV9kZiAlPiUNCiAgICAgIGdyb3VwX2J5KFJlZ2lvbi5Db2RlLi5BQU0uKSAlPiUNCiAgICAgIHN1bW1hcml6ZSgNCiAgICAgICAgTWVhbi5SZXZlbnVlID0gbWVhbihBbm51YWwuUmV2ZW51ZSksIA0KICAgICAgICBNZWFuLlNFID0gc3RkLmVycm9yKEFubnVhbC5SZXZlbnVlKSkgJT4lDQogICAgICBtdXRhdGUoDQogICAgICAgIFNFLk1pbiA9IE1lYW4uUmV2ZW51ZSAtIE1lYW4uU0UsIA0KICAgICAgICBTRS5NYXggPSBNZWFuLlJldmVudWUgKyBNZWFuLlNFKQ0KDQpBZGQgZXJyb3IgYmFycyB0byBvdXIgbWVhbiByZXZlbnVlIGJ5IGdlb2dyYXBoeSBiYXIgcGxvdCB1c2luZyB0aGUgZ2VvbV9lcnJvcmJhcigpIGxheWVyLiBDYWxsIHRoaXMgbmV3IHBsb3QgcmV2ZW51ZV9lcnJvcmJhci4gT3VyIG5ldyB5IHZhcmlhYmxlIGlzIG91ciBjYWxjdWxhdGVkIE1lYW4uUmV2ZW51ZSBjb2x1bW4uIE1ha2Ugc3VyZSB0byBjaGFuZ2UgdGhlIHN0YXQgYmVpbmcgdXNlZCwgc2luY2Ugd2XigJlyZSBub3cgZGlzcGxheWluZyBvdXIgY2FsY3VsYXRlZCBtZWFucyBhcyBpcyByYXRoZXIgdGhhbiBjYWxjdWxhdGluZyB0aGVtIGFzIHdlIGNyZWF0ZSB0aGUgcGxvdC4gV2hpY2ggcmVnaW9ucyBoYXZlIG1vcmUgb3IgbGVzcyB2YXJpYWJpbGl0eSBhcm91bmQgdGhlaXIgbWVhbiByZXZlbnVlcz8NCmBgYHtyfQ0KbXVzZXVtc19lcnJvcl9kZiA8LSBtdXNldW1zX3JldmVudWVfZGYgJT4lDQogIGdyb3VwX2J5KFJlZ2lvbi5Db2RlLi5BQU0uKSAlPiUNCiAgc3VtbWFyaXplKA0KICAgIE1lYW4uUmV2ZW51ZSA9IG1lYW4oQW5udWFsLlJldmVudWUpLCANCiAgICBNZWFuLlNFID0gc3RkLmVycm9yKEFubnVhbC5SZXZlbnVlKSkgJT4lDQogIG11dGF0ZSgNCiAgICBTRS5NaW4gPSBNZWFuLlJldmVudWUgLSBNZWFuLlNFLCANCiAgICBTRS5NYXggPSBNZWFuLlJldmVudWUgKyBNZWFuLlNFKQ0KYGBgDQpgYGB7cn0NCnJldmVudWVfZXJyb3JiYXIgPC0gZ2dwbG90KG11c2V1bXNfZXJyb3JfZGYsIGFlcyh4ID0gZmFjdG9yKFJlZ2lvbi5Db2RlLi5BQU0uKSwgeSA9IE1lYW4uUmV2ZW51ZSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBjKCAiMSIgPSAiTmV3IEVuZ2xhbmQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIyIiA9ICJNaWQtQXRsYW50aWMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIzIiA9ICJTb3V0aGVhc3Rlcm4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI0IiA9ICJNaWR3ZXN0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiNSIgPSAiTW9udGFpbiBQbGFpbnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI2IiA9ICJXZXN0ZXJuIikpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGZ1bmN0aW9uKHgpIHBhc3RlMCgiJCIsIHgvMWU2LCAiTSIpKSArDQogIGxhYnModGl0bGUgPSAiTWVhbiBBbm51YWwgUmV2ZW51ZSBieSBSZWdpb24iLCB4ID0gIlJlZ2lvbiIsIHkgPSAiTWVhbiBBbm51YWwgUmV2ZW51ZSIpICsNCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbiA9IFNFLk1pbiwgeW1heCA9IFNFLk1heCksIHdpZHRoID0gMC4yKQ0KcmV2ZW51ZV9lcnJvcmJhcg0KYGBgDQoNCg==