This is an R Markdown Notebook for my second Course Project in the Coursera class Exploratory Data Analysis (from the Johns Hopkins University). Code for this project is kept in my GitHub repo here.
Course Project Prompt:
You must address the following questions and tasks in your exploratory analysis. For each question/task you will need to make a single plot. Unless specified, you can use any plotting system in R to make your plot.
Have total emissions from PM2.5 decreased in the United States from 1999 to 2008? Using the base plotting system, make a plot showing the total PM2.5 emission from all sources for each of the years 1999, 2002, 2005, and 2008.
Have total emissions from PM2.5 decreased in Baltimore City, Maryland (fips == “24510”) from 1999 to 2008? Use the base plotting system to make a plot answering this question.
Of the four types of sources indicated by the 𝚝𝚢𝚙𝚎 (point, nonpoint, onroad, nonroad) variable, which of these four sources have seen decreases in emissions from 1999–2008 for Baltimore City? Which have seen increases in emissions from 1999–2008? Use the ggplot2 plotting system to make a plot answer this question.
Across the United States, how have emissions from coal combustion-related sources changed from 1999–2008?
How have emissions from motor vehicle sources changed from 1999–2008 in Baltimore City?
Compare emissions from motor vehicle sources in Baltimore City with emissions from motor vehicle sources in Los Angeles County, California (𝚏𝚒𝚙𝚜 == “𝟶𝟼𝟶𝟹𝟽”). Which city has seen greater changes over time in motor vehicle emissions?
Load R Packages
We begin by loading R packages to make this easier.
library(dplyr)
library(ggplot2)
library(scales)
library(data.table)
Initial Data Exploration
Let’s start with the NEI data. This file contains a data frame with all of the PM2.5 emissions data for 1999, 2002, 2005, and 2008. For each year, the table contains number of tons of PM2.5 emitted from a specific type of source for the entire year. Here are the first few rows.
head(NEI)
Calling str on NEI reveals the types of data in each column.
str(NEI)
'data.frame': 6497651 obs. of 6 variables:
$ fips : chr "09001" "09001" "09001" "09001" ...
$ SCC : chr "10100401" "10100404" "10100501" "10200401" ...
$ Pollutant: chr "PM25-PRI" "PM25-PRI" "PM25-PRI" "PM25-PRI" ...
$ Emissions: num 15.714 234.178 0.128 2.036 0.388 ...
$ type : chr "POINT" "POINT" "POINT" "POINT" ...
$ year : int 1999 1999 1999 1999 1999 1999 1999 1999 1999 1999 ...
Nearly 6.5M observations! Here are some descriptions of each column:
- fips: A five-digit number (represented as a string) indicating the U.S. county
- SCC: The name of the source as indicated by a digit string (see source code classification table)
- Pollutant: A string indicating the pollutant
- Emission: Amount of PM2.5 emitted, in tons
- type: The type of source (point, non-point, on-road, or non-road)
- year: The year of emissions recorded
Let’s take a look at the other data set, Source Classification Code (SCC) table. This table provides a mapping from the SCC digit strings in the Emissions table to the actual name of the PM2.5 source. The sources are categorized in a few different ways from more general to more specific and you may choose to explore whatever categories you think are most useful. For example, source “10100101” is known as “Ext Comb /Electric Gen /Anthracite Coal /Pulverized Coal”.
head(SCC)
Calling str on SCC gives an overview of the data.frame structure.
str(SCC)
'data.frame': 11717 obs. of 15 variables:
$ SCC : Factor w/ 11717 levels "10100101","10100102",..: 1 2 3 4 5 6 7 8 9 10 ...
$ Data.Category : Factor w/ 6 levels "Biogenic","Event",..: 6 6 6 6 6 6 6 6 6 6 ...
$ Short.Name : Factor w/ 11238 levels "","2,4-D Salts and Esters Prod /Process Vents, 2,4-D Recovery: Filtration",..: 3283 3284 3293 3291 3290 3294 3295 3296 3292 3289 ...
$ EI.Sector : Factor w/ 59 levels "Agriculture - Crops & Livestock Dust",..: 18 18 18 18 18 18 18 18 18 18 ...
$ Option.Group : Factor w/ 25 levels "","C/I Kerosene",..: 1 1 1 1 1 1 1 1 1 1 ...
$ Option.Set : Factor w/ 18 levels "","A","B","B1A",..: 1 1 1 1 1 1 1 1 1 1 ...
$ SCC.Level.One : Factor w/ 17 levels "Brick Kilns",..: 3 3 3 3 3 3 3 3 3 3 ...
$ SCC.Level.Two : Factor w/ 146 levels "","Agricultural Chemicals Production",..: 32 32 32 32 32 32 32 32 32 32 ...
$ SCC.Level.Three : Factor w/ 1061 levels "","100% Biosolids (e.g., sewage sludge, manure, mixtures of these matls)",..: 88 88 156 156 156 156 156 156 156 156 ...
$ SCC.Level.Four : Factor w/ 6084 levels "","(NH4)2 SO4 Acid Bath System and Evaporator",..: 4455 5583 4466 4458 1341 5246 5584 5983 4461 776 ...
$ Map.To : num NA NA NA NA NA NA NA NA NA NA ...
$ Last.Inventory.Year: int NA NA NA NA NA NA NA NA NA NA ...
$ Created_Date : Factor w/ 57 levels "","1/27/2000 0:00:00",..: 1 1 1 1 1 1 1 1 1 1 ...
$ Revised_Date : Factor w/ 44 levels "","1/27/2000 0:00:00",..: 1 1 1 1 1 1 1 1 1 1 ...
$ Usage.Notes : Factor w/ 21 levels ""," ","includes bleaching towers, washer hoods, filtrate tanks, vacuum pump exhausts",..: 1 1 1 1 1 1 1 1 1 1 ...
Question #1
The first question is: Have total emissions from PM2.5 decreased in the United States from 1999 to 2008? Using the base plotting system, make a plot showing the total PM2.5 emission from all sources for each of the years 1999, 2002, 2005, and 2008.
My answer is: yes, total emissions from PM2.5 decreased in the United States from 1999 to 2008.
To answer this, we first make a data.frame called annual that uses dplyr group_by, filter, and summarize to add up the tons of emissions for each of the four years. Then we make a numeric vector called pts by dividing the annual emissions by 1,000,000 so that we can plot the annual totals in millions of tons. We then call plot with the arguments below. Of note, we divide the y-value by 1,000,000 to be consistent with the y-axis labels. Also, we set axes = FALSE the call to plot because we are going to manually plot them later. The calls to axis(1) and axis(2...) plots the x- and y-axes respectively and specifies the locations of the tick marks (equal to yrs and pts) and for the y-axis creates the labels by appending an M (for millions) using paste.
A note about labels in base plots: you can create subscripts or superscripts using expression in the label call (ie, main, ylab, etc) with square brackets or a caret, respectively. This is what the [2.5] is doing in the ylab argument in the R chunk below. Since this subscript occurs in the middle of the y-axis label, we use an asterisk to break up the label before the subscript and the label after the subscript.
annual <- NEI %>% group_by(year) %>%
filter(year == 1999|2002|2005|2008) %>%
summarize(Annual.Total = sum(Emissions));
pts <- pretty(annual$Annual.Total/1000000);
yrs <- c(1999,2002,2005,2008)
plot(annual$year, annual$Annual.Total/1000000, type = "l", lwd = 2, axes = FALSE,
xlab = "Year",
ylab = expression("Total Tons of PM"[2.5]*" Emissions"),
main = expression("Total Tons of PM"[2.5]*" Emissions in the United States"));
axis(1, at = yrs, labels = paste(yrs));
axis(2, at = pts, labels = paste(pts, "M", sep = ""));
box()

As we can see from this graph, there has been a decline in total tons of PM2.5 emissions in the United States over these 4 years.
Question #2
- Have total emissions from PM2.5 decreased in Baltimore City, Maryland (fips == “24510”) from 1999 to 2008? Use the base plotting system to make a plot answering this question.
My answer to this question is a qualified yes. As we can see from the graph below, there is an absolute decrease from the year 1999 to the year 2008 in total PM2.5 emissions in Baltimore City; however, it is not a consistently negative trend as indicated by the increase from 2002 to 2005.
baltimore <- NEI %>%
filter(fips == "24510") %>%
group_by(year) %>%
summarize(Annual.Total = sum(Emissions));
baltimore.pts <- pretty(baltimore$Annual.Total/1000);
plot(baltimore$year, baltimore$Annual.Total/1000, type = "l", lwd = 2, axes = FALSE,
xlab = "Year",
ylab = expression("Total Tons of PM"[2.5]*" Emissions"),
main = expression("Total Tons of PM"[2.5]*" Emissions in Baltimore"));
axis(1, at = c(1999,2002,2005,2008))
axis(2, at = baltimore.pts, labels = paste(baltimore.pts, "K", sep = ""));
box();

Question #3
- Of the four types of sources indicated by the 𝚝𝚢𝚙𝚎 (point, nonpoint, onroad, nonroad) variable, which of these four sources have seen decreases in emissions from 1999–2008 for Baltimore City? Which have seen increases in emissions from 1999–2008? Use the ggplot2 plotting system to make a plot answer this question.
My answer to this question is: on-road, non-road, and non-point sources have all seen decreases in PM2.5 emissions from 1999-2008 whereas point sources have seen an increase over the same time period.
nei.baltimore <- NEI %>% filter(fips == "24510") %>% group_by(type, year) %>% summarize(Annual.Total = sum(Emissions));
nei.baltimore$type <- factor(nei.baltimore$type, levels = c("ON-ROAD", "NON-ROAD", "POINT", "NONPOINT")) # Re-order factor levels so they plot in the order we wish
ggplot(nei.baltimore, aes(x = factor(year), y = Annual.Total, fill = type)) +
geom_bar(stat = "identity") +
facet_grid(. ~ type) +
xlab("Year") +
ylab(expression("Total Tons of PM"[2.5]*" Emissions")) +
ggtitle(expression("Total Tons of PM"[2.5]*" Emissions in Baltimore by Source Type")) +
theme(axis.text.x=element_text(angle = 90, vjust = 0.5, hjust = 1)) +
scale_y_continuous(labels = comma) +
guides(fill = FALSE)

Question #4
- Across the United States, how have emissions from coal combustion-related sources changed from 1999–2008?
My answer to this question is: emissions from coal combustion-relates sources have decreased from 1999-2008, especially from 2005 to 2008.
To show this graphically, we must first subset the SCC data.frame by using grep to pattern match on the string “Fuel Comb”, followed by any number of characters, followed by “Coal” in the EI.Sector column. To determine this was the right pattern, we use unique(SCC$EI.Sector) to give us all 59 levels of this factor (not shown in R chunk below). Subsetting SCC in this way will give us a smaller data.frame with only coal combustion-related source classification codes called scc.coal. From here, we make a list of unique coal-related source classification codes called scc.coal.list. This will be our lookup list to subset the NEI data.frame using the %in% operator, which we save in a data.frame called nei.coal.
From here, we use facets to show coal combustion-related emissions by type. We create a new type called “TOTAL” which is simply an annual total of all coal combustion-related emissions. This way we can show the annual total in the leftmost facet, then the breakout by type in the middle and right facets. Similar to Question #3, we had to re-order the factor levels so that the facets would plot in the order we wish.
scc.coal <- SCC[grep("Fuel Comb.*Coal", SCC$EI.Sector), ];
scc.coal.list <- unique(scc.coal$SCC);
nei.coal <- subset(NEI, SCC %in% scc.coal.list);
nei.coal <- nei.coal %>% group_by(type, year) %>% summarize(Annual.Total = sum(Emissions))
nei.coal.total <- nei.coal %>% group_by(year) %>% summarize(Annual.Total = sum(Annual.Total)) %>% mutate(type = "TOTAL");
nei.coal <- nei.coal %>% select(Annual.Total, type, year);
nei.coal <- bind_rows(nei.coal, nei.coal.total);
nei.coal$type <- factor(nei.coal$type, levels = c("TOTAL", "ON-ROAD", "NON-ROAD", "POINT", "NONPOINT")); # Re-order factor levels to they plot in the order we wish
ggplot(nei.coal, aes(x = factor(year), y = Annual.Total, fill = type)) +
geom_bar(stat = "identity") +
facet_grid(. ~ type) +
xlab("Year") +
ylab(expression("Total Tons of PM"[2.5]*" Emissions")) +
ggtitle(expression(atop("Total Tons of PM"[2.5]*" Emissions in the United States", paste("from Coal Combustion-Related Sources")))) +
theme(plot.margin = unit(c(1,1,1,1), "cm")) +
scale_y_continuous(labels = comma) +
scale_fill_brewer(palette = "Dark2") +
guides(fill = FALSE)

Question #5
- How have emissions from motor vehicle sources changed from 1999–2008 in Baltimore City?
My answer to this question is: emissions from motor vehicle sources have declined from 1999-2008 in Baltimore City.
To arrive at this conclusion, we must first subset the NEI data.frame by the source classification codes for motor vehicle sources. We do this by using grep to pattern match the string “Mobile” followed by any number of characters, followed by the string “Vehicles”.
scc.vehicles <- SCC[grep("Mobile.*Vehicles", SCC$EI.Sector), ]; # Pattern match mobile vehicles in SCC description
scc.vehicles.list <- unique(scc.vehicles$SCC); # Create motor vehicle lookup list by SCC
nei.vehicles <- subset(NEI, SCC %in% scc.vehicles.list); # Filter for motor vehicle sources
nei.vehicles <- nei.vehicles %>% filter(fips == "24510") # Filter for Baltimore
nei.vehicles <- merge(x = nei.vehicles, y = scc.vehicles[, c("SCC", "SCC.Level.Two", "SCC.Level.Three")], by = "SCC") # Join in descriptive data on SCC codes
nei.vehicles <- nei.vehicles %>% group_by(year, SCC.Level.Two, SCC.Level.Three) %>% summarize(Annual.Total = sum(Emissions))
nei.vehicles.total <- nei.vehicles %>% group_by(year) %>% summarize(Annual.Total = sum(Annual.Total)) %>% mutate(SCC.Level.Two = "Total")
nei.vehicles <- bind_rows(nei.vehicles, nei.vehicles.total);
nei.vehicles$SCC.Level.Two <- factor(nei.vehicles$SCC.Level.Two, levels = c("Total", "Highway Vehicles - Diesel", "Highway Vehicles - Gasoline"));
ggplot(nei.vehicles, aes(x = factor(year), y = Annual.Total, fill = SCC.Level.Two)) +
geom_bar(stat = "identity") +
facet_grid(. ~ SCC.Level.Two) +
xlab("Year") +
ylab(expression("Total Tons of PM"[2.5]*" Emissions")) +
ggtitle(expression(atop("Total Tons of PM"[2.5]*" Emissions in Baltimore City", paste("from Motor Vehicle Sources")))) +
theme(plot.title = element_text(hjust = 0.5)) + # Center the plot title
theme(plot.margin = unit(c(1,1,1,1), "cm")) + # Adjust plot margins
scale_fill_brewer(palette = "Set1") +
guides(fill = FALSE)

Question #6
- Compare emissions from motor vehicle sources in Baltimore City with emissions from motor vehicle sources in Los Angeles County, California (𝚏𝚒𝚙𝚜 == “𝟶𝟼𝟶𝟹𝟽”). Which city has seen greater changes over time in motor vehicle emissions?
My answer to this question is: Baltimore City has seen a negative 8% compound annual growth rate (CAGR) in emissions from motor vehicles over the 9 years of data whereas Los Angeles has experienced a positive 0.5% CAGR over the same period. The data indicate that motor vehicle emissions have declined in Baltimore City and increased in Los Angeles.
scc.vehicles <- SCC[grep("Mobile.*Vehicles", SCC$EI.Sector), ]; # Pattern match mobile vehicles in SCC description
scc.vehicles.list <- unique(scc.vehicles$SCC); # Create motor vehicle lookup list by SCC
nei.vehicles <- subset(NEI, SCC %in% scc.vehicles.list); # Filter for motor vehicle sources
nei.vehicles <- nei.vehicles %>% filter(fips == "24510"| fips == "06037"); # Filter for Baltimore City or Los Angeles County
nei.vehicles$fips[nei.vehicles$fips == "24510"] <- "Baltimore";
nei.vehicles$fips[nei.vehicles$fips == "06037"] <- "Los Angeles";
nei.vehicles <- merge(x = nei.vehicles, y = scc.vehicles[, c("SCC", "SCC.Level.Two")], by = "SCC"); # Join in descriptive data on SCC codes
nei.vehicles <- nei.vehicles %>% group_by(fips, year, SCC.Level.Two) %>% summarize(Annual.Total = sum(Emissions));
nei.vehicles.total <- nei.vehicles %>% group_by(fips, year) %>% summarize(Annual.Total = sum(Annual.Total)) %>% mutate(SCC.Level.Two = "Total");
nei.vehicles <- bind_rows(nei.vehicles, nei.vehicles.total);
nei.vehicles$SCC.Level.Two <- factor(nei.vehicles$SCC.Level.Two, levels = c("Total", "Highway Vehicles - Diesel", "Highway Vehicles - Gasoline"));
ggplot(nei.vehicles, aes(x = factor(year), y = Annual.Total, fill = SCC.Level.Two)) +
geom_bar(stat = "identity") +
facet_grid(fips ~ SCC.Level.Two) +
xlab("Year") +
ylab(expression("Total Tons of PM"[2.5]*" Emissions")) +
ggtitle(expression(atop("Total Tons of PM"[2.5]*" Emissions from Motor Vehicle Sources", paste("in Baltimore City, MD and Los Angeles County, CA")))) +
theme(plot.title = element_text(hjust = 0.5)) + # Center the plot title
theme(plot.margin = unit(c(1,1,1,1), "cm")) + # Adjust plot margins
scale_fill_brewer(palette = "Set1") +
guides(fill = FALSE)

scc.vehicles <- SCC[grep("Mobile.*Vehicles", SCC$EI.Sector), ]; # Pattern match mobile vehicles in SCC description
scc.vehicles.list <- unique(scc.vehicles$SCC); # Create motor vehicle lookup list by SCC
nei.vehicles <- subset(NEI, SCC %in% scc.vehicles.list); # Filter for motor vehicle sources
nei.vehicles <- nei.vehicles %>% filter(fips == "24510"| fips == "06037"); # Filter for Baltimore City or Los Angeles County
nei.vehicles$fips[nei.vehicles$fips == "24510"] <- "Baltimore";
nei.vehicles$fips[nei.vehicles$fips == "06037"] <- "Los Angeles";
nei.vehicles <- merge(x = nei.vehicles, y = scc.vehicles[, c("SCC", "SCC.Level.Two")], by = "SCC"); # Join in descriptive data on SCC codes
nei.vehicles <- nei.vehicles %>% group_by(fips, year, SCC.Level.Two) %>% summarize(Annual.Total = sum(Emissions));
nei.vehicles.total <- nei.vehicles %>% group_by(fips, year) %>% summarize(Annual.Total = sum(Annual.Total)) %>% mutate(SCC.Level.Two = "Total");
nei.vehicles <- bind_rows(nei.vehicles, nei.vehicles.total);
nei.vehicles$SCC.Level.Two <- factor(nei.vehicles$SCC.Level.Two, levels = c("Total", "Highway Vehicles - Diesel", "Highway Vehicles - Gasoline"));
ggplot(nei.vehicles, aes(x = factor(year), y = Annual.Total, fill = SCC.Level.Two)) +
geom_bar(stat = "identity") +
facet_grid(fips ~ SCC.Level.Two, scales = "free") + # Setup facets and allow scales to adjust to data in each location
xlab("Year") +
ylab(expression("Total Tons of PM"[2.5]*" Emissions")) +
ggtitle(expression(atop("Total Tons of PM"[2.5]*" Emissions from Motor Vehicle Sources", paste("in Baltimore City, MD and Los Angeles County, CA")))) +
theme(plot.title = element_text(hjust = 0.5)) + # Center the plot title
theme(plot.margin = unit(c(1,1,1,1), "cm")) + # Adjust plot margins
scale_fill_brewer(palette = "Set1") +
guides(fill = FALSE)

It’s still not very clear which has a greater change in percentage terms. We can use data.table function called shift to help us calculate percent change between measurement years.
nei.vehicles.DT <- data.table(nei.vehicles)
yoyFunc <- function(x) {x/shift(x)}
yoy.cols <- c("Annual.Total")
nei.vehicles.DT <- nei.vehicles.DT[, paste0("Percent.Change.", yoy.cols) := lapply(.SD, yoyFunc), by = "fips,SCC.Level.Two", .SDcols = yoy.cols]
nei.vehicles.DT <- mutate(nei.vehicles.DT, Percent.Change.Annual.Total = Percent.Change.Annual.Total - 1)
ggplot(nei.vehicles.DT, aes(x = factor(year), y = Percent.Change.Annual.Total, fill = SCC.Level.Two)) +
geom_bar(stat = "identity") +
facet_grid(fips ~ SCC.Level.Two) +
xlab("Year") +
ylab(expression("% Change From Prior Measurement")) +
ggtitle(expression(atop("Percentage Change in Total Tons of PM"[2.5]*" Emissions from Motor Vehicle", paste("Sources in Baltimore City, MD and Los Angeles County, CA")))) +
theme(plot.title = element_text(hjust = 0.5)) + # Center the plot title
theme(plot.margin = unit(c(1,1,1,1), "cm")) + # Adjust plot margins
scale_fill_brewer(palette = "Set1") +
guides(fill = FALSE)

As we can see from this bar chart, Baltimore City appears to have a more meaningful decline in PM2.5 emissions from motor vehicle sources than Los Angeles County. It is tempting to use use summary to look at the Percent.Change.Annual.Total in each location to find the average Percent.Change.Annual.Total; however, this is not correct. The correct way to do this is to calculate the growth rate, defined as:
\[\left[\left[\frac{\textrm{Ending Quantity - Beginning Quantity}}{\textrm{Number of Years}}\right]\div\textrm{Beginning Quantity}\right]\times\textrm{100}\]
We can do this easily using dplyr.
CAGR.df <- nei.vehicles.DT %>%
group_by(fips, SCC.Level.Two) %>%
summarize(N.Years = max(year) - min(year),
Beginning.Qty = Annual.Total[which(year==min(year))],
Ending.Qty = Annual.Total[which(year==max(year))],
CAGR = ((Ending.Qty-Beginning.Qty)/N.Years)/Beginning.Qty);
CAGR.df;
As we can see from using summary below, the annual growth rate is overstated by taking a simple mean of the Percent.Change.Annual.Total. First for Baltimore:
summary(nei.vehicles.DT$Percent.Change.Annual.Total[nei.vehicles.DT$fips=="Baltimore"]);
Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
-0.65100 -0.54980 -0.32320 -0.31270 -0.09681 0.03171 3
Now for Los Angeles:
summary(nei.vehicles.DT$Percent.Change.Annual.Total[nei.vehicles.DT$fips=="Los Angeles"]);
Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
-0.17950 -0.10870 0.04257 0.03217 0.08723 0.46030 3
This is why we must use CAGR when evaluating year-over-year growth (or decline).
LS0tCnRpdGxlOiAiRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcyAtIENvdXJzZSBQcm9qZWN0IDIiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRoZW1lOiB5ZXRpCi0tLQoKVGhpcyBpcyBhbiBSIE1hcmtkb3duIE5vdGVib29rIGZvciBteSBzZWNvbmQgQ291cnNlIFByb2plY3QgaW4gdGhlIENvdXJzZXJhIGNsYXNzIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMgKGZyb20gdGhlIEpvaG5zIEhvcGtpbnMgVW5pdmVyc2l0eSkuICBDb2RlIGZvciB0aGlzIHByb2plY3QgaXMga2VwdCBpbiBteSBHaXRIdWIgcmVwbyBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2NhbmRpZG9icmF1L2RhdGFzY2llbmNlY291cnNlcmEuZ2l0KS4KCioqKgojIyMgQ291cnNlIFByb2plY3QgUHJvbXB0OgoKWW91IG11c3QgYWRkcmVzcyB0aGUgZm9sbG93aW5nIHF1ZXN0aW9ucyBhbmQgdGFza3MgaW4geW91ciBleHBsb3JhdG9yeSBhbmFseXNpcy4gRm9yIGVhY2ggcXVlc3Rpb24vdGFzayB5b3Ugd2lsbCBuZWVkIHRvIG1ha2UgYSBzaW5nbGUgcGxvdC4gVW5sZXNzIHNwZWNpZmllZCwgeW91IGNhbiB1c2UgYW55IHBsb3R0aW5nIHN5c3RlbSBpbiBSIHRvIG1ha2UgeW91ciBwbG90LgoKMS4gSGF2ZSB0b3RhbCBlbWlzc2lvbnMgZnJvbSBQTTIuNSBkZWNyZWFzZWQgaW4gdGhlIFVuaXRlZCBTdGF0ZXMgZnJvbSAxOTk5IHRvIDIwMDg/IFVzaW5nIHRoZSBiYXNlIHBsb3R0aW5nIHN5c3RlbSwgbWFrZSBhIHBsb3Qgc2hvd2luZyB0aGUgdG90YWwgUE0yLjUgZW1pc3Npb24gZnJvbSBhbGwgc291cmNlcyBmb3IgZWFjaCBvZiB0aGUgeWVhcnMgMTk5OSwgMjAwMiwgMjAwNSwgYW5kIDIwMDguCgoyLiBIYXZlIHRvdGFsIGVtaXNzaW9ucyBmcm9tIFBNMi41IGRlY3JlYXNlZCBpbiBCYWx0aW1vcmUgQ2l0eSwgTWFyeWxhbmQgKGZpcHMgPT0gIjI0NTEwIikgZnJvbSAxOTk5IHRvIDIwMDg/ICBVc2UgdGhlIGJhc2UgcGxvdHRpbmcgc3lzdGVtIHRvIG1ha2UgYSBwbG90IGFuc3dlcmluZyB0aGlzIHF1ZXN0aW9uLgoKMy4gT2YgdGhlIGZvdXIgdHlwZXMgb2Ygc291cmNlcyBpbmRpY2F0ZWQgYnkgdGhlIPCdmp3wnZqi8J2amfCdmo4gKHBvaW50LCBub25wb2ludCwgb25yb2FkLCBub25yb2FkKSB2YXJpYWJsZSwgd2hpY2ggb2YgdGhlc2UgZm91ciBzb3VyY2VzIGhhdmUgc2VlbiBkZWNyZWFzZXMgaW4gZW1pc3Npb25zIGZyb20gMTk5OeKAkzIwMDggZm9yIEJhbHRpbW9yZSBDaXR5PyBXaGljaCBoYXZlIHNlZW4gaW5jcmVhc2VzIGluIGVtaXNzaW9ucyBmcm9tIDE5OTnigJMyMDA4PyBVc2UgdGhlIGdncGxvdDIgcGxvdHRpbmcgc3lzdGVtIHRvIG1ha2UgYSBwbG90IGFuc3dlciB0aGlzIHF1ZXN0aW9uLgoKNC4gQWNyb3NzIHRoZSBVbml0ZWQgU3RhdGVzLCBob3cgaGF2ZSBlbWlzc2lvbnMgZnJvbSBjb2FsIGNvbWJ1c3Rpb24tcmVsYXRlZCBzb3VyY2VzIGNoYW5nZWQgZnJvbSAxOTk54oCTMjAwOD8KCjUuIEhvdyBoYXZlIGVtaXNzaW9ucyBmcm9tIG1vdG9yIHZlaGljbGUgc291cmNlcyBjaGFuZ2VkIGZyb20gMTk5OeKAkzIwMDggaW4gQmFsdGltb3JlIENpdHk/Cgo2LiBDb21wYXJlIGVtaXNzaW9ucyBmcm9tIG1vdG9yIHZlaGljbGUgc291cmNlcyBpbiBCYWx0aW1vcmUgQ2l0eSB3aXRoIGVtaXNzaW9ucyBmcm9tIG1vdG9yIHZlaGljbGUgc291cmNlcyBpbiBMb3MgQW5nZWxlcyBDb3VudHksIENhbGlmb3JuaWEgKPCdmo/wnZqS8J2amfCdmpwgPT0gIvCdn7bwnZ+88J2ftvCdn7nwnZ+9IikuIFdoaWNoIGNpdHkgaGFzIHNlZW4gZ3JlYXRlciBjaGFuZ2VzIG92ZXIgdGltZSBpbiBtb3RvciB2ZWhpY2xlIGVtaXNzaW9ucz8KCioqKgojIyMgTG9hZCBSIFBhY2thZ2VzCgpXZSBiZWdpbiBieSBsb2FkaW5nIFIgcGFja2FnZXMgdG8gbWFrZSB0aGlzIGVhc2llci4KYGBge3IsIHJlc3VsdHMgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoc2NhbGVzKQpsaWJyYXJ5KGRhdGEudGFibGUpCmBgYAoKKioqCiMjIyBEYXRhIElucHV0CgpUaGUgLnJkcyBmaWxlcyBhcmUgbG9jYXRlZCBbaGVyZV0oaHR0cHM6Ly9kMzk2cXVzemE0MG9yYy5jbG91ZGZyb250Lm5ldC9leGRhdGElMkZkYXRhJTJGTkVJX2RhdGEuemlwKS4gIEZpbGVzIHdlcmUgdW56aXBwZWQgYW5kIHBsYWNlZCBpbiBteSB3b3JraW5nIGRpcmVjdG9yeS4gIEZpcnN0IHN0ZXAgaXMgdG8gcmVhZCAucmRzIGZpbGVzIGluIGFzIGRhdGEuZnJhbWVzIHVzaW5nIGJhc2UgZnVuY3Rpb24gYHJlYWRSRFNgLgpgYGB7cn0KTkVJIDwtIHJlYWRSRFMoInN1bW1hcnlTQ0NfUE0yNS5yZHMiKQpTQ0MgPC0gcmVhZFJEUygiU291cmNlX0NsYXNzaWZpY2F0aW9uX0NvZGUucmRzIikKYGBgCgoKKioqCiMjIyBJbml0aWFsIERhdGEgRXhwbG9yYXRpb24KCkxldCdzIHN0YXJ0IHdpdGggdGhlIE5FSSBkYXRhLiAgVGhpcyBmaWxlIGNvbnRhaW5zIGEgZGF0YSBmcmFtZSB3aXRoIGFsbCBvZiB0aGUgUE0yLjUgZW1pc3Npb25zIGRhdGEgZm9yIDE5OTksIDIwMDIsIDIwMDUsIGFuZCAyMDA4LiBGb3IgZWFjaCB5ZWFyLCB0aGUgdGFibGUgY29udGFpbnMgbnVtYmVyIG9mIHRvbnMgb2YgUE0yLjUgZW1pdHRlZCBmcm9tIGEgc3BlY2lmaWMgdHlwZSBvZiBzb3VyY2UgZm9yIHRoZSBlbnRpcmUgeWVhci4gSGVyZSBhcmUgdGhlIGZpcnN0IGZldyByb3dzLgoKYGBge3J9CmhlYWQoTkVJKQpgYGAKCkNhbGxpbmcgYHN0cmAgb24gTkVJIHJldmVhbHMgdGhlIHR5cGVzIG9mIGRhdGEgaW4gZWFjaCBjb2x1bW4uCmBgYHtyfQpzdHIoTkVJKQpgYGAKCk5lYXJseSA2LjVNIG9ic2VydmF0aW9ucyEgIEhlcmUgYXJlIHNvbWUgZGVzY3JpcHRpb25zIG9mIGVhY2ggY29sdW1uOgoKKiBfX2ZpcHM6X18gQSBmaXZlLWRpZ2l0IG51bWJlciAocmVwcmVzZW50ZWQgYXMgYSBzdHJpbmcpIGluZGljYXRpbmcgdGhlIFUuUy4gY291bnR5CiogX19TQ0M6X18gVGhlIG5hbWUgb2YgdGhlIHNvdXJjZSBhcyBpbmRpY2F0ZWQgYnkgYSBkaWdpdCBzdHJpbmcgKHNlZSBzb3VyY2UgY29kZSBjbGFzc2lmaWNhdGlvbiB0YWJsZSkKKiBfX1BvbGx1dGFudDpfXyBBIHN0cmluZyBpbmRpY2F0aW5nIHRoZSBwb2xsdXRhbnQKKiBfX0VtaXNzaW9uOl9fIEFtb3VudCBvZiBQTTIuNSBlbWl0dGVkLCBpbiB0b25zCiogX190eXBlOl9fIFRoZSB0eXBlIG9mIHNvdXJjZSAocG9pbnQsIG5vbi1wb2ludCwgb24tcm9hZCwgb3Igbm9uLXJvYWQpCiogX195ZWFyOl9fIFRoZSB5ZWFyIG9mIGVtaXNzaW9ucyByZWNvcmRlZAoKTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIG90aGVyIGRhdGEgc2V0LCBTb3VyY2UgQ2xhc3NpZmljYXRpb24gQ29kZSAoU0NDKSB0YWJsZS4gIFRoaXMgdGFibGUgcHJvdmlkZXMgYSBtYXBwaW5nIGZyb20gdGhlIFNDQyBkaWdpdCBzdHJpbmdzIGluIHRoZSBFbWlzc2lvbnMgdGFibGUgdG8gdGhlIGFjdHVhbCBuYW1lIG9mIHRoZSBQTTIuNSBzb3VyY2UuIFRoZSBzb3VyY2VzIGFyZSBjYXRlZ29yaXplZCBpbiBhIGZldyBkaWZmZXJlbnQgd2F5cyBmcm9tIG1vcmUgZ2VuZXJhbCB0byBtb3JlIHNwZWNpZmljIGFuZCB5b3UgbWF5IGNob29zZSB0byBleHBsb3JlIHdoYXRldmVyIGNhdGVnb3JpZXMgeW91IHRoaW5rIGFyZSBtb3N0IHVzZWZ1bC4gRm9yIGV4YW1wbGUsIHNvdXJjZSDigJwxMDEwMDEwMeKAnSBpcyBrbm93biBhcyDigJxFeHQgQ29tYiAvRWxlY3RyaWMgR2VuIC9BbnRocmFjaXRlIENvYWwgL1B1bHZlcml6ZWQgQ29hbOKAnS4KYGBge3J9CmhlYWQoU0NDKQpgYGAKCkNhbGxpbmcgYHN0cmAgb24gU0NDIGdpdmVzIGFuIG92ZXJ2aWV3IG9mIHRoZSBkYXRhLmZyYW1lIHN0cnVjdHVyZS4KYGBge3J9CnN0cihTQ0MpCmBgYAoKKioqCiMjIyBRdWVzdGlvbiAjMQoKVGhlIGZpcnN0IHF1ZXN0aW9uIGlzOiBIYXZlIHRvdGFsIGVtaXNzaW9ucyBmcm9tIFBNMi41IGRlY3JlYXNlZCBpbiB0aGUgVW5pdGVkIFN0YXRlcyBmcm9tIDE5OTkgdG8gMjAwOD8gVXNpbmcgdGhlIGJhc2UgcGxvdHRpbmcgc3lzdGVtLCBtYWtlIGEgcGxvdCBzaG93aW5nIHRoZSB0b3RhbCBQTTIuNSBlbWlzc2lvbiBmcm9tIGFsbCBzb3VyY2VzIGZvciBlYWNoIG9mIHRoZSB5ZWFycyAxOTk5LCAyMDAyLCAyMDA1LCBhbmQgMjAwOC4KCk15IGFuc3dlciBpczogeWVzLCB0b3RhbCBlbWlzc2lvbnMgZnJvbSBQTTIuNSBkZWNyZWFzZWQgaW4gdGhlIFVuaXRlZCBTdGF0ZXMgZnJvbSAxOTk5IHRvIDIwMDguCgpUbyBhbnN3ZXIgdGhpcywgd2UgZmlyc3QgbWFrZSBhIGRhdGEuZnJhbWUgY2FsbGVkIGBhbm51YWxgIHRoYXQgdXNlcyBgZHBseXJgIGBncm91cF9ieWAsIGBmaWx0ZXJgLCBhbmQgYHN1bW1hcml6ZWAgdG8gYWRkIHVwIHRoZSB0b25zIG9mIGVtaXNzaW9ucyBmb3IgZWFjaCBvZiB0aGUgZm91ciB5ZWFycy4gIFRoZW4gd2UgbWFrZSBhIG51bWVyaWMgdmVjdG9yIGNhbGxlZCBgcHRzYCBieSBkaXZpZGluZyB0aGUgYW5udWFsIGVtaXNzaW9ucyBieSAxLDAwMCwwMDAgc28gdGhhdCB3ZSBjYW4gcGxvdCB0aGUgYW5udWFsIHRvdGFscyBpbiBtaWxsaW9ucyBvZiB0b25zLiAgV2UgdGhlbiBjYWxsIHBsb3Qgd2l0aCB0aGUgYXJndW1lbnRzIGJlbG93LiAgT2Ygbm90ZSwgd2UgZGl2aWRlIHRoZSB5LXZhbHVlIGJ5IDEsMDAwLDAwMCB0byBiZSBjb25zaXN0ZW50IHdpdGggdGhlIHktYXhpcyBsYWJlbHMuICBBbHNvLCB3ZSBzZXQgYGF4ZXMgPSBGQUxTRWAgdGhlIGNhbGwgdG8gYHBsb3RgIGJlY2F1c2Ugd2UgYXJlIGdvaW5nIHRvIG1hbnVhbGx5IHBsb3QgdGhlbSBsYXRlci4gIFRoZSBjYWxscyB0byBgYXhpcygxKWAgYW5kIGBheGlzKDIuLi4pYCBwbG90cyB0aGUgeC0gYW5kIHktYXhlcyByZXNwZWN0aXZlbHkgYW5kIHNwZWNpZmllcyB0aGUgbG9jYXRpb25zIG9mIHRoZSB0aWNrIG1hcmtzIChlcXVhbCB0byBgeXJzYCBhbmQgYHB0c2ApIGFuZCBmb3IgdGhlIHktYXhpcyBjcmVhdGVzIHRoZSBsYWJlbHMgYnkgYXBwZW5kaW5nIGFuIE0gKGZvciBtaWxsaW9ucykgdXNpbmcgYHBhc3RlYC4KCkEgbm90ZSBhYm91dCBsYWJlbHMgaW4gYmFzZSBwbG90czogeW91IGNhbiBjcmVhdGUgc3Vic2NyaXB0cyBvciBzdXBlcnNjcmlwdHMgdXNpbmcgYGV4cHJlc3Npb25gIGluIHRoZSBsYWJlbCBjYWxsIChpZSwgYG1haW5gLCBgeWxhYmAsIGV0Yykgd2l0aCBzcXVhcmUgYnJhY2tldHMgb3IgYSBjYXJldCwgcmVzcGVjdGl2ZWx5LiAgVGhpcyBpcyB3aGF0IHRoZSBgWzIuNV1gIGlzIGRvaW5nIGluIHRoZSB5bGFiIGFyZ3VtZW50IGluIHRoZSBSIGNodW5rIGJlbG93LiAgU2luY2UgdGhpcyBzdWJzY3JpcHQgb2NjdXJzIGluIHRoZSBtaWRkbGUgb2YgdGhlIHktYXhpcyBsYWJlbCwgd2UgdXNlIGFuIGFzdGVyaXNrIHRvIGJyZWFrIHVwIHRoZSBsYWJlbCBiZWZvcmUgdGhlIHN1YnNjcmlwdCBhbmQgdGhlIGxhYmVsIGFmdGVyIHRoZSBzdWJzY3JpcHQuCgpgYGB7cn0KYW5udWFsIDwtIE5FSSAlPiUgZ3JvdXBfYnkoeWVhcikgJT4lIAogIGZpbHRlcih5ZWFyID09IDE5OTl8MjAwMnwyMDA1fDIwMDgpICU+JSAKICBzdW1tYXJpemUoQW5udWFsLlRvdGFsID0gc3VtKEVtaXNzaW9ucykpOwpwdHMgPC0gcHJldHR5KGFubnVhbCRBbm51YWwuVG90YWwvMTAwMDAwMCk7CnlycyA8LSBjKDE5OTksMjAwMiwyMDA1LDIwMDgpCnBsb3QoYW5udWFsJHllYXIsIGFubnVhbCRBbm51YWwuVG90YWwvMTAwMDAwMCwgdHlwZSA9ICJsIiwgbHdkID0gMiwgYXhlcyA9IEZBTFNFLAogICAgIHhsYWIgPSAiWWVhciIsIAogICAgIHlsYWIgPSBleHByZXNzaW9uKCJUb3RhbCBUb25zIG9mIFBNIlsyLjVdKiIgRW1pc3Npb25zIiksIAogICAgIG1haW4gPSBleHByZXNzaW9uKCJUb3RhbCBUb25zIG9mIFBNIlsyLjVdKiIgRW1pc3Npb25zIGluIHRoZSBVbml0ZWQgU3RhdGVzIikpOwpheGlzKDEsIGF0ID0geXJzLCBsYWJlbHMgPSBwYXN0ZSh5cnMpKTsKYXhpcygyLCBhdCA9IHB0cywgbGFiZWxzID0gcGFzdGUocHRzLCAiTSIsIHNlcCA9ICIiKSk7CmJveCgpCmBgYApBcyB3ZSBjYW4gc2VlIGZyb20gdGhpcyBncmFwaCwgdGhlcmUgaGFzIGJlZW4gYSBkZWNsaW5lIGluIHRvdGFsIHRvbnMgb2YgUE0yLjUgZW1pc3Npb25zIGluIHRoZSBVbml0ZWQgU3RhdGVzIG92ZXIgdGhlc2UgNCB5ZWFycy4KCioqKgojIyMgUXVlc3Rpb24gIzIKCjIuIEhhdmUgdG90YWwgZW1pc3Npb25zIGZyb20gUE0yLjUgZGVjcmVhc2VkIGluIEJhbHRpbW9yZSBDaXR5LCBNYXJ5bGFuZCAoZmlwcyA9PSAiMjQ1MTAiKSBmcm9tIDE5OTkgdG8gMjAwOD8gIFVzZSB0aGUgYmFzZSBwbG90dGluZyBzeXN0ZW0gdG8gbWFrZSBhIHBsb3QgYW5zd2VyaW5nIHRoaXMgcXVlc3Rpb24uCgpNeSBhbnN3ZXIgdG8gdGhpcyBxdWVzdGlvbiBpcyBhIHF1YWxpZmllZCB5ZXMuICBBcyB3ZSBjYW4gc2VlIGZyb20gdGhlIGdyYXBoIGJlbG93LCB0aGVyZSBpcyBhbiBhYnNvbHV0ZSBkZWNyZWFzZSBmcm9tIHRoZSB5ZWFyIDE5OTkgdG8gdGhlIHllYXIgMjAwOCBpbiB0b3RhbCBQTTIuNSBlbWlzc2lvbnMgaW4gQmFsdGltb3JlIENpdHk7IGhvd2V2ZXIsIGl0IGlzIG5vdCBhIGNvbnNpc3RlbnRseSBuZWdhdGl2ZSB0cmVuZCBhcyBpbmRpY2F0ZWQgYnkgdGhlIGluY3JlYXNlIGZyb20gMjAwMiB0byAyMDA1LgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQpiYWx0aW1vcmUgPC0gTkVJICU+JSAKICBmaWx0ZXIoZmlwcyA9PSAiMjQ1MTAiKSAlPiUgCiAgZ3JvdXBfYnkoeWVhcikgJT4lIAogIHN1bW1hcml6ZShBbm51YWwuVG90YWwgPSBzdW0oRW1pc3Npb25zKSk7CmJhbHRpbW9yZS5wdHMgPC0gcHJldHR5KGJhbHRpbW9yZSRBbm51YWwuVG90YWwvMTAwMCk7CnBsb3QoYmFsdGltb3JlJHllYXIsIGJhbHRpbW9yZSRBbm51YWwuVG90YWwvMTAwMCwgdHlwZSA9ICJsIiwgbHdkID0gMiwgYXhlcyA9IEZBTFNFLAogICAgIHhsYWIgPSAiWWVhciIsIAogICAgIHlsYWIgPSBleHByZXNzaW9uKCJUb3RhbCBUb25zIG9mIFBNIlsyLjVdKiIgRW1pc3Npb25zIiksIAogICAgIG1haW4gPSBleHByZXNzaW9uKCJUb3RhbCBUb25zIG9mIFBNIlsyLjVdKiIgRW1pc3Npb25zIGluIEJhbHRpbW9yZSIpKTsKYXhpcygxLCBhdCA9IGMoMTk5OSwyMDAyLDIwMDUsMjAwOCkpCmF4aXMoMiwgYXQgPSBiYWx0aW1vcmUucHRzLCBsYWJlbHMgPSBwYXN0ZShiYWx0aW1vcmUucHRzLCAiSyIsIHNlcCA9ICIiKSk7CmJveCgpOwpgYGAKCioqKiAKIyMjIFF1ZXN0aW9uICMzCgozLiBPZiB0aGUgZm91ciB0eXBlcyBvZiBzb3VyY2VzIGluZGljYXRlZCBieSB0aGUg8J2anfCdmqLwnZqZ8J2ajiAocG9pbnQsIG5vbnBvaW50LCBvbnJvYWQsIG5vbnJvYWQpIHZhcmlhYmxlLCB3aGljaCBvZiB0aGVzZSBmb3VyIHNvdXJjZXMgaGF2ZSBzZWVuIGRlY3JlYXNlcyBpbiBlbWlzc2lvbnMgZnJvbSAxOTk54oCTMjAwOCBmb3IgQmFsdGltb3JlIENpdHk/IFdoaWNoIGhhdmUgc2VlbiBpbmNyZWFzZXMgaW4gZW1pc3Npb25zIGZyb20gMTk5OeKAkzIwMDg/IFVzZSB0aGUgZ2dwbG90MiBwbG90dGluZyBzeXN0ZW0gdG8gbWFrZSBhIHBsb3QgYW5zd2VyIHRoaXMgcXVlc3Rpb24uCgpNeSBhbnN3ZXIgdG8gdGhpcyBxdWVzdGlvbiBpczogb24tcm9hZCwgbm9uLXJvYWQsIGFuZCBub24tcG9pbnQgc291cmNlcyBoYXZlIGFsbCBzZWVuIGRlY3JlYXNlcyBpbiBQTTIuNSBlbWlzc2lvbnMgZnJvbSAxOTk5LTIwMDggd2hlcmVhcyBwb2ludCBzb3VyY2VzIGhhdmUgc2VlbiBhbiBpbmNyZWFzZSBvdmVyIHRoZSBzYW1lIHRpbWUgcGVyaW9kLgoKCgpgYGB7cn0KbmVpLmJhbHRpbW9yZSA8LSBORUkgJT4lIGZpbHRlcihmaXBzID09ICIyNDUxMCIpICU+JSBncm91cF9ieSh0eXBlLCB5ZWFyKSAlPiUgc3VtbWFyaXplKEFubnVhbC5Ub3RhbCA9IHN1bShFbWlzc2lvbnMpKTsKbmVpLmJhbHRpbW9yZSR0eXBlIDwtIGZhY3RvcihuZWkuYmFsdGltb3JlJHR5cGUsIGxldmVscyA9IGMoIk9OLVJPQUQiLCAiTk9OLVJPQUQiLCAiUE9JTlQiLCAiTk9OUE9JTlQiKSkgIyBSZS1vcmRlciBmYWN0b3IgbGV2ZWxzIHNvIHRoZXkgcGxvdCBpbiB0aGUgb3JkZXIgd2Ugd2lzaApnZ3Bsb3QobmVpLmJhbHRpbW9yZSwgYWVzKHggPSBmYWN0b3IoeWVhciksIHkgPSBBbm51YWwuVG90YWwsIGZpbGwgPSB0eXBlKSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKyAKICBmYWNldF9ncmlkKC4gfiB0eXBlKSArIAogIHhsYWIoIlllYXIiKSArIAogIHlsYWIoZXhwcmVzc2lvbigiVG90YWwgVG9ucyBvZiBQTSJbMi41XSoiIEVtaXNzaW9ucyIpKSArIAogIGdndGl0bGUoZXhwcmVzc2lvbigiVG90YWwgVG9ucyBvZiBQTSJbMi41XSoiIEVtaXNzaW9ucyBpbiBCYWx0aW1vcmUgYnkgU291cmNlIFR5cGUiKSkgKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3QgPSAxKSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGd1aWRlcyhmaWxsID0gRkFMU0UpCmBgYAoKKioqCiMjIyBRdWVzdGlvbiAjNAoKNC4gQWNyb3NzIHRoZSBVbml0ZWQgU3RhdGVzLCBob3cgaGF2ZSBlbWlzc2lvbnMgZnJvbSBjb2FsIGNvbWJ1c3Rpb24tcmVsYXRlZCBzb3VyY2VzIGNoYW5nZWQgZnJvbSAxOTk54oCTMjAwOD8KCk15IGFuc3dlciB0byB0aGlzIHF1ZXN0aW9uIGlzOiBlbWlzc2lvbnMgZnJvbSBjb2FsIGNvbWJ1c3Rpb24tcmVsYXRlcyBzb3VyY2VzIGhhdmUgZGVjcmVhc2VkIGZyb20gMTk5OS0yMDA4LCBlc3BlY2lhbGx5IGZyb20gMjAwNSB0byAyMDA4LgoKVG8gc2hvdyB0aGlzIGdyYXBoaWNhbGx5LCB3ZSBtdXN0IGZpcnN0IHN1YnNldCB0aGUgYFNDQ2AgZGF0YS5mcmFtZSBieSB1c2luZyBgZ3JlcGAgdG8gcGF0dGVybiBtYXRjaCBvbiB0aGUgc3RyaW5nICJGdWVsIENvbWIiLCBmb2xsb3dlZCBieSBhbnkgbnVtYmVyIG9mIGNoYXJhY3RlcnMsIGZvbGxvd2VkIGJ5ICJDb2FsIiBpbiB0aGUgRUkuU2VjdG9yIGNvbHVtbi4gIFRvIGRldGVybWluZSB0aGlzIHdhcyB0aGUgcmlnaHQgcGF0dGVybiwgd2UgdXNlIGB1bmlxdWUoU0NDJEVJLlNlY3RvcilgIHRvIGdpdmUgdXMgYWxsIDU5IGxldmVscyBvZiB0aGlzIGZhY3RvciAobm90IHNob3duIGluIFIgY2h1bmsgYmVsb3cpLiAgU3Vic2V0dGluZyBgU0NDYCBpbiB0aGlzIHdheSB3aWxsIGdpdmUgdXMgYSBzbWFsbGVyIGRhdGEuZnJhbWUgd2l0aCBvbmx5IGNvYWwgY29tYnVzdGlvbi1yZWxhdGVkIHNvdXJjZSBjbGFzc2lmaWNhdGlvbiBjb2RlcyBjYWxsZWQgYHNjYy5jb2FsYC4gIEZyb20gaGVyZSwgd2UgbWFrZSBhIGxpc3Qgb2YgdW5pcXVlIGNvYWwtcmVsYXRlZCBzb3VyY2UgY2xhc3NpZmljYXRpb24gY29kZXMgY2FsbGVkIGBzY2MuY29hbC5saXN0YC4gIFRoaXMgd2lsbCBiZSBvdXIgbG9va3VwIGxpc3QgdG8gc3Vic2V0IHRoZSBgTkVJYCBkYXRhLmZyYW1lIHVzaW5nIHRoZSBgJWluJWAgb3BlcmF0b3IsIHdoaWNoIHdlIHNhdmUgaW4gYSBkYXRhLmZyYW1lIGNhbGxlZCBgbmVpLmNvYWxgLgoKRnJvbSBoZXJlLCB3ZSB1c2UgZmFjZXRzIHRvIHNob3cgY29hbCBjb21idXN0aW9uLXJlbGF0ZWQgZW1pc3Npb25zIGJ5IHR5cGUuICBXZSBjcmVhdGUgYSBuZXcgdHlwZSBjYWxsZWQgIlRPVEFMIiB3aGljaCBpcyBzaW1wbHkgYW4gYW5udWFsIHRvdGFsIG9mIGFsbCBjb2FsIGNvbWJ1c3Rpb24tcmVsYXRlZCBlbWlzc2lvbnMuICBUaGlzIHdheSB3ZSBjYW4gc2hvdyB0aGUgYW5udWFsIHRvdGFsIGluIHRoZSBsZWZ0bW9zdCBmYWNldCwgdGhlbiB0aGUgYnJlYWtvdXQgYnkgdHlwZSBpbiB0aGUgbWlkZGxlIGFuZCByaWdodCBmYWNldHMuICBTaW1pbGFyIHRvIFF1ZXN0aW9uICMzLCB3ZSBoYWQgdG8gcmUtb3JkZXIgdGhlIGZhY3RvciBsZXZlbHMgc28gdGhhdCB0aGUgZmFjZXRzIHdvdWxkIHBsb3QgaW4gdGhlIG9yZGVyIHdlIHdpc2guCgpgYGB7cn0Kc2NjLmNvYWwgPC0gU0NDW2dyZXAoIkZ1ZWwgQ29tYi4qQ29hbCIsIFNDQyRFSS5TZWN0b3IpLCAgXTsKc2NjLmNvYWwubGlzdCA8LSB1bmlxdWUoc2NjLmNvYWwkU0NDKTsKbmVpLmNvYWwgPC0gc3Vic2V0KE5FSSwgU0NDICVpbiUgc2NjLmNvYWwubGlzdCk7Cm5laS5jb2FsIDwtIG5laS5jb2FsICU+JSBncm91cF9ieSh0eXBlLCB5ZWFyKSAlPiUgc3VtbWFyaXplKEFubnVhbC5Ub3RhbCA9IHN1bShFbWlzc2lvbnMpKQpuZWkuY29hbC50b3RhbCA8LSBuZWkuY29hbCAlPiUgZ3JvdXBfYnkoeWVhcikgJT4lIHN1bW1hcml6ZShBbm51YWwuVG90YWwgPSBzdW0oQW5udWFsLlRvdGFsKSkgJT4lIG11dGF0ZSh0eXBlID0gIlRPVEFMIik7Cm5laS5jb2FsIDwtIG5laS5jb2FsICU+JSBzZWxlY3QoQW5udWFsLlRvdGFsLCB0eXBlLCB5ZWFyKTsKbmVpLmNvYWwgPC0gYmluZF9yb3dzKG5laS5jb2FsLCBuZWkuY29hbC50b3RhbCk7Cm5laS5jb2FsJHR5cGUgPC0gZmFjdG9yKG5laS5jb2FsJHR5cGUsIGxldmVscyA9IGMoIlRPVEFMIiwgIk9OLVJPQUQiLCAiTk9OLVJPQUQiLCAiUE9JTlQiLCAiTk9OUE9JTlQiKSk7ICMgUmUtb3JkZXIgZmFjdG9yIGxldmVscyB0byB0aGV5IHBsb3QgaW4gdGhlIG9yZGVyIHdlIHdpc2gKZ2dwbG90KG5laS5jb2FsLCBhZXMoeCA9IGZhY3Rvcih5ZWFyKSwgeSA9IEFubnVhbC5Ub3RhbCwgZmlsbCA9IHR5cGUpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBmYWNldF9ncmlkKC4gfiB0eXBlKSArCiAgeGxhYigiWWVhciIpICsKICB5bGFiKGV4cHJlc3Npb24oIlRvdGFsIFRvbnMgb2YgUE0iWzIuNV0qIiBFbWlzc2lvbnMiKSkgKyAKICBnZ3RpdGxlKGV4cHJlc3Npb24oYXRvcCgiVG90YWwgVG9ucyBvZiBQTSJbMi41XSoiIEVtaXNzaW9ucyBpbiB0aGUgVW5pdGVkIFN0YXRlcyIsIHBhc3RlKCJmcm9tIENvYWwgQ29tYnVzdGlvbi1SZWxhdGVkIFNvdXJjZXMiKSkpKSArCiAgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoMSwxLDEsMSksICJjbSIpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpICsKICBndWlkZXMoZmlsbCA9IEZBTFNFKQpgYGAKCioqKgojIyMgUXVlc3Rpb24gIzUKNS4gSG93IGhhdmUgZW1pc3Npb25zIGZyb20gbW90b3IgdmVoaWNsZSBzb3VyY2VzIGNoYW5nZWQgZnJvbSAxOTk54oCTMjAwOCBpbiBCYWx0aW1vcmUgQ2l0eT8KCk15IGFuc3dlciB0byB0aGlzIHF1ZXN0aW9uIGlzOiBlbWlzc2lvbnMgZnJvbSBtb3RvciB2ZWhpY2xlIHNvdXJjZXMgaGF2ZSBkZWNsaW5lZCBmcm9tIDE5OTktMjAwOCBpbiBCYWx0aW1vcmUgQ2l0eS4KClRvIGFycml2ZSBhdCB0aGlzIGNvbmNsdXNpb24sIHdlIG11c3QgZmlyc3Qgc3Vic2V0IHRoZSBgTkVJYCBkYXRhLmZyYW1lIGJ5IHRoZSBzb3VyY2UgY2xhc3NpZmljYXRpb24gY29kZXMgZm9yIG1vdG9yIHZlaGljbGUgc291cmNlcy4gIFdlIGRvIHRoaXMgYnkgdXNpbmcgYGdyZXBgIHRvIHBhdHRlcm4gbWF0Y2ggdGhlIHN0cmluZyAiTW9iaWxlIiBmb2xsb3dlZCBieSBhbnkgbnVtYmVyIG9mIGNoYXJhY3RlcnMsIGZvbGxvd2VkIGJ5IHRoZSBzdHJpbmcgIlZlaGljbGVzIi4gIAoKCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0Kc2NjLnZlaGljbGVzIDwtIFNDQ1tncmVwKCJNb2JpbGUuKlZlaGljbGVzIiwgU0NDJEVJLlNlY3RvciksICBdOyAjIFBhdHRlcm4gbWF0Y2ggbW9iaWxlIHZlaGljbGVzIGluIFNDQyBkZXNjcmlwdGlvbgpzY2MudmVoaWNsZXMubGlzdCA8LSB1bmlxdWUoc2NjLnZlaGljbGVzJFNDQyk7ICMgQ3JlYXRlIG1vdG9yIHZlaGljbGUgbG9va3VwIGxpc3QgYnkgU0NDCm5laS52ZWhpY2xlcyA8LSBzdWJzZXQoTkVJLCBTQ0MgJWluJSBzY2MudmVoaWNsZXMubGlzdCk7ICMgRmlsdGVyIGZvciBtb3RvciB2ZWhpY2xlIHNvdXJjZXMKbmVpLnZlaGljbGVzIDwtIG5laS52ZWhpY2xlcyAlPiUgZmlsdGVyKGZpcHMgPT0gIjI0NTEwIikgIyBGaWx0ZXIgZm9yIEJhbHRpbW9yZQpuZWkudmVoaWNsZXMgPC0gbWVyZ2UoeCA9IG5laS52ZWhpY2xlcywgeSA9IHNjYy52ZWhpY2xlc1ssIGMoIlNDQyIsICJTQ0MuTGV2ZWwuVHdvIiwgIlNDQy5MZXZlbC5UaHJlZSIpXSwgYnkgPSAiU0NDIikgIyBKb2luIGluIGRlc2NyaXB0aXZlIGRhdGEgb24gU0NDIGNvZGVzCm5laS52ZWhpY2xlcyA8LSBuZWkudmVoaWNsZXMgJT4lIGdyb3VwX2J5KHllYXIsIFNDQy5MZXZlbC5Ud28sIFNDQy5MZXZlbC5UaHJlZSkgJT4lIHN1bW1hcml6ZShBbm51YWwuVG90YWwgPSBzdW0oRW1pc3Npb25zKSkKbmVpLnZlaGljbGVzLnRvdGFsIDwtIG5laS52ZWhpY2xlcyAlPiUgZ3JvdXBfYnkoeWVhcikgJT4lIHN1bW1hcml6ZShBbm51YWwuVG90YWwgPSBzdW0oQW5udWFsLlRvdGFsKSkgJT4lIG11dGF0ZShTQ0MuTGV2ZWwuVHdvID0gIlRvdGFsIikKbmVpLnZlaGljbGVzIDwtIGJpbmRfcm93cyhuZWkudmVoaWNsZXMsIG5laS52ZWhpY2xlcy50b3RhbCk7Cm5laS52ZWhpY2xlcyRTQ0MuTGV2ZWwuVHdvIDwtIGZhY3RvcihuZWkudmVoaWNsZXMkU0NDLkxldmVsLlR3bywgbGV2ZWxzID0gYygiVG90YWwiLCAiSGlnaHdheSBWZWhpY2xlcyAtIERpZXNlbCIsICJIaWdod2F5IFZlaGljbGVzIC0gR2Fzb2xpbmUiKSk7CmdncGxvdChuZWkudmVoaWNsZXMsIGFlcyh4ID0gZmFjdG9yKHllYXIpLCB5ID0gQW5udWFsLlRvdGFsLCBmaWxsID0gU0NDLkxldmVsLlR3bykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIGZhY2V0X2dyaWQoLiB+IFNDQy5MZXZlbC5Ud28pICsKICB4bGFiKCJZZWFyIikgKwogIHlsYWIoZXhwcmVzc2lvbigiVG90YWwgVG9ucyBvZiBQTSJbMi41XSoiIEVtaXNzaW9ucyIpKSArIAogIGdndGl0bGUoZXhwcmVzc2lvbihhdG9wKCJUb3RhbCBUb25zIG9mIFBNIlsyLjVdKiIgRW1pc3Npb25zIGluIEJhbHRpbW9yZSBDaXR5IiwgcGFzdGUoImZyb20gTW90b3IgVmVoaWNsZSBTb3VyY2VzIikpKSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArICMgQ2VudGVyIHRoZSBwbG90IHRpdGxlCiAgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoMSwxLDEsMSksICJjbSIpKSArICMgQWRqdXN0IHBsb3QgbWFyZ2lucwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIpICsKICBndWlkZXMoZmlsbCA9IEZBTFNFKQpgYGAKCgoqKioKIyMjIFF1ZXN0aW9uICM2CjYuIENvbXBhcmUgZW1pc3Npb25zIGZyb20gbW90b3IgdmVoaWNsZSBzb3VyY2VzIGluIEJhbHRpbW9yZSBDaXR5IHdpdGggZW1pc3Npb25zIGZyb20gbW90b3IgdmVoaWNsZSBzb3VyY2VzIGluIExvcyBBbmdlbGVzIENvdW50eSwgQ2FsaWZvcm5pYSAo8J2aj/CdmpLwnZqZ8J2anCA9PSAi8J2ftvCdn7zwnZ+28J2fufCdn70iKS4gV2hpY2ggY2l0eSBoYXMgc2VlbiBncmVhdGVyIGNoYW5nZXMgb3ZlciB0aW1lIGluIG1vdG9yIHZlaGljbGUgZW1pc3Npb25zPwoKTXkgYW5zd2VyIHRvIHRoaXMgcXVlc3Rpb24gaXM6IEJhbHRpbW9yZSBDaXR5IGhhcyBzZWVuIGEgbmVnYXRpdmUgOCUgY29tcG91bmQgYW5udWFsIGdyb3d0aCByYXRlIChDQUdSKSBpbiBlbWlzc2lvbnMgZnJvbSBtb3RvciB2ZWhpY2xlcyBvdmVyIHRoZSA5IHllYXJzIG9mIGRhdGEgd2hlcmVhcyBMb3MgQW5nZWxlcyBoYXMgZXhwZXJpZW5jZWQgYSBwb3NpdGl2ZSAwLjUlIENBR1Igb3ZlciB0aGUgc2FtZSBwZXJpb2QuICBUaGUgZGF0YSBpbmRpY2F0ZSB0aGF0IG1vdG9yIHZlaGljbGUgZW1pc3Npb25zIGhhdmUgZGVjbGluZWQgaW4gQmFsdGltb3JlIENpdHkgYW5kIGluY3JlYXNlZCBpbiBMb3MgQW5nZWxlcy4KCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0Kc2NjLnZlaGljbGVzIDwtIFNDQ1tncmVwKCJNb2JpbGUuKlZlaGljbGVzIiwgU0NDJEVJLlNlY3RvciksICBdOyAjIFBhdHRlcm4gbWF0Y2ggbW9iaWxlIHZlaGljbGVzIGluIFNDQyBkZXNjcmlwdGlvbgpzY2MudmVoaWNsZXMubGlzdCA8LSB1bmlxdWUoc2NjLnZlaGljbGVzJFNDQyk7ICMgQ3JlYXRlIG1vdG9yIHZlaGljbGUgbG9va3VwIGxpc3QgYnkgU0NDCm5laS52ZWhpY2xlcyA8LSBzdWJzZXQoTkVJLCBTQ0MgJWluJSBzY2MudmVoaWNsZXMubGlzdCk7ICMgRmlsdGVyIGZvciBtb3RvciB2ZWhpY2xlIHNvdXJjZXMKbmVpLnZlaGljbGVzIDwtIG5laS52ZWhpY2xlcyAlPiUgZmlsdGVyKGZpcHMgPT0gIjI0NTEwInwgZmlwcyA9PSAiMDYwMzciKTsgIyBGaWx0ZXIgZm9yIEJhbHRpbW9yZSBDaXR5IG9yIExvcyBBbmdlbGVzIENvdW50eQpuZWkudmVoaWNsZXMkZmlwc1tuZWkudmVoaWNsZXMkZmlwcyA9PSAiMjQ1MTAiXSA8LSAiQmFsdGltb3JlIjsKbmVpLnZlaGljbGVzJGZpcHNbbmVpLnZlaGljbGVzJGZpcHMgPT0gIjA2MDM3Il0gPC0gIkxvcyBBbmdlbGVzIjsKbmVpLnZlaGljbGVzIDwtIG1lcmdlKHggPSBuZWkudmVoaWNsZXMsIHkgPSBzY2MudmVoaWNsZXNbLCBjKCJTQ0MiLCAiU0NDLkxldmVsLlR3byIpXSwgYnkgPSAiU0NDIik7ICMgSm9pbiBpbiBkZXNjcmlwdGl2ZSBkYXRhIG9uIFNDQyBjb2RlcwpuZWkudmVoaWNsZXMgPC0gbmVpLnZlaGljbGVzICU+JSBncm91cF9ieShmaXBzLCB5ZWFyLCBTQ0MuTGV2ZWwuVHdvKSAlPiUgc3VtbWFyaXplKEFubnVhbC5Ub3RhbCA9IHN1bShFbWlzc2lvbnMpKTsKbmVpLnZlaGljbGVzLnRvdGFsIDwtIG5laS52ZWhpY2xlcyAlPiUgZ3JvdXBfYnkoZmlwcywgeWVhcikgJT4lIHN1bW1hcml6ZShBbm51YWwuVG90YWwgPSBzdW0oQW5udWFsLlRvdGFsKSkgJT4lIG11dGF0ZShTQ0MuTGV2ZWwuVHdvID0gIlRvdGFsIik7Cm5laS52ZWhpY2xlcyA8LSBiaW5kX3Jvd3MobmVpLnZlaGljbGVzLCBuZWkudmVoaWNsZXMudG90YWwpOwpuZWkudmVoaWNsZXMkU0NDLkxldmVsLlR3byA8LSBmYWN0b3IobmVpLnZlaGljbGVzJFNDQy5MZXZlbC5Ud28sIGxldmVscyA9IGMoIlRvdGFsIiwgIkhpZ2h3YXkgVmVoaWNsZXMgLSBEaWVzZWwiLCAiSGlnaHdheSBWZWhpY2xlcyAtIEdhc29saW5lIikpOwpnZ3Bsb3QobmVpLnZlaGljbGVzLCBhZXMoeCA9IGZhY3Rvcih5ZWFyKSwgeSA9IEFubnVhbC5Ub3RhbCwgZmlsbCA9IFNDQy5MZXZlbC5Ud28pKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBmYWNldF9ncmlkKGZpcHMgfiBTQ0MuTGV2ZWwuVHdvKSArIAogIHhsYWIoIlllYXIiKSArCiAgeWxhYihleHByZXNzaW9uKCJUb3RhbCBUb25zIG9mIFBNIlsyLjVdKiIgRW1pc3Npb25zIikpICsgCiAgZ2d0aXRsZShleHByZXNzaW9uKGF0b3AoIlRvdGFsIFRvbnMgb2YgUE0iWzIuNV0qIiBFbWlzc2lvbnMgZnJvbSBNb3RvciBWZWhpY2xlIFNvdXJjZXMiLCBwYXN0ZSgiaW4gQmFsdGltb3JlIENpdHksIE1EIGFuZCBMb3MgQW5nZWxlcyBDb3VudHksIENBIikpKSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArICMgQ2VudGVyIHRoZSBwbG90IHRpdGxlCiAgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoMSwxLDEsMSksICJjbSIpKSArICMgQWRqdXN0IHBsb3QgbWFyZ2lucwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIpICsKICBndWlkZXMoZmlsbCA9IEZBTFNFKQpgYGAKCgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQpzY2MudmVoaWNsZXMgPC0gU0NDW2dyZXAoIk1vYmlsZS4qVmVoaWNsZXMiLCBTQ0MkRUkuU2VjdG9yKSwgIF07ICMgUGF0dGVybiBtYXRjaCBtb2JpbGUgdmVoaWNsZXMgaW4gU0NDIGRlc2NyaXB0aW9uCnNjYy52ZWhpY2xlcy5saXN0IDwtIHVuaXF1ZShzY2MudmVoaWNsZXMkU0NDKTsgIyBDcmVhdGUgbW90b3IgdmVoaWNsZSBsb29rdXAgbGlzdCBieSBTQ0MKbmVpLnZlaGljbGVzIDwtIHN1YnNldChORUksIFNDQyAlaW4lIHNjYy52ZWhpY2xlcy5saXN0KTsgIyBGaWx0ZXIgZm9yIG1vdG9yIHZlaGljbGUgc291cmNlcwpuZWkudmVoaWNsZXMgPC0gbmVpLnZlaGljbGVzICU+JSBmaWx0ZXIoZmlwcyA9PSAiMjQ1MTAifCBmaXBzID09ICIwNjAzNyIpOyAjIEZpbHRlciBmb3IgQmFsdGltb3JlIENpdHkgb3IgTG9zIEFuZ2VsZXMgQ291bnR5Cm5laS52ZWhpY2xlcyRmaXBzW25laS52ZWhpY2xlcyRmaXBzID09ICIyNDUxMCJdIDwtICJCYWx0aW1vcmUiOwpuZWkudmVoaWNsZXMkZmlwc1tuZWkudmVoaWNsZXMkZmlwcyA9PSAiMDYwMzciXSA8LSAiTG9zIEFuZ2VsZXMiOwpuZWkudmVoaWNsZXMgPC0gbWVyZ2UoeCA9IG5laS52ZWhpY2xlcywgeSA9IHNjYy52ZWhpY2xlc1ssIGMoIlNDQyIsICJTQ0MuTGV2ZWwuVHdvIildLCBieSA9ICJTQ0MiKTsgIyBKb2luIGluIGRlc2NyaXB0aXZlIGRhdGEgb24gU0NDIGNvZGVzCm5laS52ZWhpY2xlcyA8LSBuZWkudmVoaWNsZXMgJT4lIGdyb3VwX2J5KGZpcHMsIHllYXIsIFNDQy5MZXZlbC5Ud28pICU+JSBzdW1tYXJpemUoQW5udWFsLlRvdGFsID0gc3VtKEVtaXNzaW9ucykpOwpuZWkudmVoaWNsZXMudG90YWwgPC0gbmVpLnZlaGljbGVzICU+JSBncm91cF9ieShmaXBzLCB5ZWFyKSAlPiUgc3VtbWFyaXplKEFubnVhbC5Ub3RhbCA9IHN1bShBbm51YWwuVG90YWwpKSAlPiUgbXV0YXRlKFNDQy5MZXZlbC5Ud28gPSAiVG90YWwiKTsKbmVpLnZlaGljbGVzIDwtIGJpbmRfcm93cyhuZWkudmVoaWNsZXMsIG5laS52ZWhpY2xlcy50b3RhbCk7Cm5laS52ZWhpY2xlcyRTQ0MuTGV2ZWwuVHdvIDwtIGZhY3RvcihuZWkudmVoaWNsZXMkU0NDLkxldmVsLlR3bywgbGV2ZWxzID0gYygiVG90YWwiLCAiSGlnaHdheSBWZWhpY2xlcyAtIERpZXNlbCIsICJIaWdod2F5IFZlaGljbGVzIC0gR2Fzb2xpbmUiKSk7CmdncGxvdChuZWkudmVoaWNsZXMsIGFlcyh4ID0gZmFjdG9yKHllYXIpLCB5ID0gQW5udWFsLlRvdGFsLCBmaWxsID0gU0NDLkxldmVsLlR3bykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIGZhY2V0X2dyaWQoZmlwcyB+IFNDQy5MZXZlbC5Ud28sIHNjYWxlcyA9ICJmcmVlIikgKyAjIFNldHVwIGZhY2V0cyBhbmQgYWxsb3cgc2NhbGVzIHRvIGFkanVzdCB0byBkYXRhIGluIGVhY2ggbG9jYXRpb24KICB4bGFiKCJZZWFyIikgKwogIHlsYWIoZXhwcmVzc2lvbigiVG90YWwgVG9ucyBvZiBQTSJbMi41XSoiIEVtaXNzaW9ucyIpKSArIAogIGdndGl0bGUoZXhwcmVzc2lvbihhdG9wKCJUb3RhbCBUb25zIG9mIFBNIlsyLjVdKiIgRW1pc3Npb25zIGZyb20gTW90b3IgVmVoaWNsZSBTb3VyY2VzIiwgcGFzdGUoImluIEJhbHRpbW9yZSBDaXR5LCBNRCBhbmQgTG9zIEFuZ2VsZXMgQ291bnR5LCBDQSIpKSkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKyAjIENlbnRlciB0aGUgcGxvdCB0aXRsZQogIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDEsMSwxLDEpLCAiY20iKSkgKyAjIEFkanVzdCBwbG90IG1hcmdpbnMKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDEiKSArCiAgZ3VpZGVzKGZpbGwgPSBGQUxTRSkKYGBgCgpJdCdzIHN0aWxsIG5vdCB2ZXJ5IGNsZWFyIHdoaWNoIGhhcyBhIGdyZWF0ZXIgY2hhbmdlIGluIHBlcmNlbnRhZ2UgdGVybXMuICBXZSBjYW4gdXNlIGBkYXRhLnRhYmxlYCBmdW5jdGlvbiBjYWxsZWQgYHNoaWZ0YCB0byBoZWxwIHVzIGNhbGN1bGF0ZSBwZXJjZW50IGNoYW5nZSBiZXR3ZWVuIG1lYXN1cmVtZW50IHllYXJzLgo8YnI+CmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KbmVpLnZlaGljbGVzLkRUIDwtIGRhdGEudGFibGUobmVpLnZlaGljbGVzKQp5b3lGdW5jIDwtIGZ1bmN0aW9uKHgpIHt4L3NoaWZ0KHgpfQp5b3kuY29scyA8LSBjKCJBbm51YWwuVG90YWwiKQpuZWkudmVoaWNsZXMuRFQgPC0gbmVpLnZlaGljbGVzLkRUWywgcGFzdGUwKCJQZXJjZW50LkNoYW5nZS4iLCB5b3kuY29scykgOj0gbGFwcGx5KC5TRCwgeW95RnVuYyksIGJ5ID0gImZpcHMsU0NDLkxldmVsLlR3byIsIC5TRGNvbHMgPSB5b3kuY29sc10KbmVpLnZlaGljbGVzLkRUIDwtIG11dGF0ZShuZWkudmVoaWNsZXMuRFQsIFBlcmNlbnQuQ2hhbmdlLkFubnVhbC5Ub3RhbCA9IFBlcmNlbnQuQ2hhbmdlLkFubnVhbC5Ub3RhbCAtIDEpCmdncGxvdChuZWkudmVoaWNsZXMuRFQsIGFlcyh4ID0gZmFjdG9yKHllYXIpLCB5ID0gUGVyY2VudC5DaGFuZ2UuQW5udWFsLlRvdGFsLCBmaWxsID0gU0NDLkxldmVsLlR3bykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIGZhY2V0X2dyaWQoZmlwcyB+IFNDQy5MZXZlbC5Ud28pICsKICB4bGFiKCJZZWFyIikgKwogIHlsYWIoZXhwcmVzc2lvbigiJSBDaGFuZ2UgRnJvbSBQcmlvciBNZWFzdXJlbWVudCIpKSArIAogIGdndGl0bGUoZXhwcmVzc2lvbihhdG9wKCJQZXJjZW50YWdlIENoYW5nZSBpbiBUb3RhbCBUb25zIG9mIFBNIlsyLjVdKiIgRW1pc3Npb25zIGZyb20gTW90b3IgVmVoaWNsZSIsIHBhc3RlKCJTb3VyY2VzIGluIEJhbHRpbW9yZSBDaXR5LCBNRCBhbmQgTG9zIEFuZ2VsZXMgQ291bnR5LCBDQSIpKSkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKyAjIENlbnRlciB0aGUgcGxvdCB0aXRsZQogIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDEsMSwxLDEpLCAiY20iKSkgKyAjIEFkanVzdCBwbG90IG1hcmdpbnMKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDEiKSArCiAgZ3VpZGVzKGZpbGwgPSBGQUxTRSkKYGBgCgpBcyB3ZSBjYW4gc2VlIGZyb20gdGhpcyBiYXIgY2hhcnQsIEJhbHRpbW9yZSBDaXR5IGFwcGVhcnMgdG8gaGF2ZSBhIG1vcmUgbWVhbmluZ2Z1bCBkZWNsaW5lIGluIFBNMi41IGVtaXNzaW9ucyBmcm9tIG1vdG9yIHZlaGljbGUgc291cmNlcyB0aGFuIExvcyBBbmdlbGVzIENvdW50eS4gIEl0IGlzIHRlbXB0aW5nIHRvIHVzZSB1c2UgYHN1bW1hcnlgIHRvIGxvb2sgYXQgdGhlIFBlcmNlbnQuQ2hhbmdlLkFubnVhbC5Ub3RhbCBpbiBlYWNoIGxvY2F0aW9uIHRvIGZpbmQgdGhlIGF2ZXJhZ2UgUGVyY2VudC5DaGFuZ2UuQW5udWFsLlRvdGFsOyBob3dldmVyLCB0aGlzIGlzIG5vdCBjb3JyZWN0LiAgVGhlIGNvcnJlY3Qgd2F5IHRvIGRvIHRoaXMgaXMgdG8gY2FsY3VsYXRlIHRoZSBncm93dGggcmF0ZSwgZGVmaW5lZCBhczoKPGJyPgokJFxsZWZ0W1xsZWZ0W1xmcmFje1x0ZXh0cm17RW5kaW5nIFF1YW50aXR5IC0gQmVnaW5uaW5nIFF1YW50aXR5fX17XHRleHRybXtOdW1iZXIgb2YgWWVhcnN9fVxyaWdodF1cZGl2XHRleHRybXtCZWdpbm5pbmcgUXVhbnRpdHl9XHJpZ2h0XVx0aW1lc1x0ZXh0cm17MTAwfSQkCjxicj4KV2UgY2FuIGRvIHRoaXMgZWFzaWx5IHVzaW5nIGBkcGx5cmAuCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KQ0FHUi5kZiA8LSBuZWkudmVoaWNsZXMuRFQgJT4lIAogIGdyb3VwX2J5KGZpcHMsIFNDQy5MZXZlbC5Ud28pICU+JSAKICBzdW1tYXJpemUoTi5ZZWFycyA9IG1heCh5ZWFyKSAtIG1pbih5ZWFyKSwgCiAgICAgICAgICAgIEJlZ2lubmluZy5RdHkgPSBBbm51YWwuVG90YWxbd2hpY2goeWVhcj09bWluKHllYXIpKV0sCiAgICAgICAgICAgIEVuZGluZy5RdHkgPSBBbm51YWwuVG90YWxbd2hpY2goeWVhcj09bWF4KHllYXIpKV0sCiAgICAgICAgICAgIENBR1IgPSAoKEVuZGluZy5RdHktQmVnaW5uaW5nLlF0eSkvTi5ZZWFycykvQmVnaW5uaW5nLlF0eSk7CkNBR1IuZGY7CmBgYAo8YnI+CkFzIHdlIGNhbiBzZWUgZnJvbSB1c2luZyBgc3VtbWFyeWAgYmVsb3csIHRoZSBhbm51YWwgZ3Jvd3RoIHJhdGUgaXMgb3ZlcnN0YXRlZCBieSB0YWtpbmcgYSBzaW1wbGUgbWVhbiBvZiB0aGUgUGVyY2VudC5DaGFuZ2UuQW5udWFsLlRvdGFsLiAgRmlyc3QgZm9yIEJhbHRpbW9yZToKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQpzdW1tYXJ5KG5laS52ZWhpY2xlcy5EVCRQZXJjZW50LkNoYW5nZS5Bbm51YWwuVG90YWxbbmVpLnZlaGljbGVzLkRUJGZpcHM9PSJCYWx0aW1vcmUiXSk7CmBgYAo8YnI+Ck5vdyBmb3IgTG9zIEFuZ2VsZXM6CmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0Kc3VtbWFyeShuZWkudmVoaWNsZXMuRFQkUGVyY2VudC5DaGFuZ2UuQW5udWFsLlRvdGFsW25laS52ZWhpY2xlcy5EVCRmaXBzPT0iTG9zIEFuZ2VsZXMiXSk7CmBgYAo8YnI+ClRoaXMgaXMgd2h5IHdlIG11c3QgdXNlIENBR1Igd2hlbiBldmFsdWF0aW5nIHllYXItb3Zlci15ZWFyIGdyb3d0aCAob3IgZGVjbGluZSkuCg==