Author

Abhinav Chaudhary

Preface

On 31 December 2019, WHO was alerted to several cases of pneumonia in Wuhan City, Hubei Province of China. The virus responsible was not a known one and this raised immediate concern regarding people’s safety, especially given the rate it was spreading throughout the global map. With it’s proper symptoms and treatments still unknown, daily level information on the affected people can give some interesting insights. We would like to thank Johns Hopkins University for making the data available for educational and academic research purposes.

Introduction

The 2019 Novel Coronavirus is a virus identified as the cause of an outbreak of respiratory illness first detected in Wuhan, China. At this time, it is unclear how easily or sustainable this virus is spreading between people. This dataset has daily level information on the number of affected cases, deaths and recovery from 2019 novel coronavirus, the name now shortened to COVID-19. For the purposes of this project, we will only be collecting data till 17th March 2020.

Our target for this project will be to study the effects of geography and population on the rate at which the virus is able to spread. Studying the geography of each case will help us analyzing if temperature plays a part in how well the virus is able to exist, and so how fast it is able to multiply. We will also look at the population density of the effected countries and see if the number of cases have a linear relationship with the population size itself.Most importantly, this project will help us better understand and prepare for the pandemic and possibly help react accordingly to every range of severity, ie. what the appropriate safety measures are.

Exploratory Data Analysis

Dependencies

To complete our Data Analysis we’ll be using the tidyverse library. To visualize our data we will be using ggplot2, dplyr, maps and viridis. To get the weather based on latitude and longitude, we will be making an api call using weatherr. To get country based data such as population density, country code etc… we will be using WDI which is the World Development Indicators DataBank and the countrycode Library.

# Tidyverse
library(tidyverse)
# Graphing Library
library(ggplot2)
library(dplyr)
library(maps)
library(viridis)
#------------------
library(dygraphs)
library(xts)
library(tidyverse)
library(lubridate)
# Weather and Country Data
library(weatherr)
library(WDI)
library(countrycode)

Reading The Data

Let’s first go ahead and see what our data looks like by printing out first few rows

# Reading
covid_19 <- read_csv("03-17-2020.csv")
Parsed with column specification:
cols(
  `Province/State` = col_character(),
  `Country/Region` = col_character(),
  `Last Update` = col_datetime(format = ""),
  Confirmed = col_double(),
  Deaths = col_double(),
  Recovered = col_double(),
  Latitude = col_double(),
  Longitude = col_double()
)
names(covid_19)[1] <- "Province"
names(covid_19)[2] <- "Country"
head(covid_19)

By the looks of the data we have the following fields:

1. Province : Region in the country where the virus was reported

2. Country : Country where it was reported

3. Last Update : The time when the total count was reviewed onto the datatset

4. Confirmed: Total number of people affected by the virus

5. Deaths : Total number of deaths from the virus

6. Recovered : Total number of people that recovered after they have been affected

7. Latitute : Geo-logical location of the reported incident

8. Longtitude : Geo-logical location of the reported incident

Graphing

Now let’s try to visualize our data to get a broader view of the problem at hand. Firstly we’ll take a look at the total number of confirmed cases.

world <- map_data("world")
ggplot() +
  geom_polygon(data = world, aes(x=long, y = lat, group = group), fill="grey", alpha=0.3) +
  geom_point( data=covid_19, aes(x=covid_19$Longitude, y=covid_19$Latitude, size=covid_19$Confirmed, color=covid_19$Confirmed)) +
    scale_color_viridis(trans="log") +

  theme_void()+ coord_map() 

By looking at the graph we can that the most number of cases reported to-date happen to be in China. We also see some cases being reported at sea, these are most probably ships which were traveling at the time.

world <- map_data("world")
ggplot() +
  geom_polygon(data = world, aes(x=long, y = lat, group = group), fill="grey", alpha=0.3) +
  geom_point( data=covid_19, aes(x=covid_19$Longitude, y=covid_19$Latitude, size=covid_19$Deaths, color=covid_19$Deaths)) +
  scale_color_viridis(trans="log") +
  theme_void()+ coord_map() 

From the graph we can see that the most number of deaths happened in China and Italy. As compared to the number of confirmed cases this graph seems to be less dense which is a good sign.

world <- map_data("world")
ggplot() +
  geom_polygon(data = world, aes(x=long, y = lat, group = group), fill="grey", alpha=0.3) +
  geom_point( data=covid_19, aes(x=covid_19$Longitude, y=covid_19$Latitude, size=covid_19$Recovered, color=covid_19$Recovered)) +
  scale_color_viridis(trans="log") +
  theme_void()+ coord_map() 

From this graph we see that most of the recovered cases happened again in China and Italy. With a number of recovered cases growing in North America.

Data Cleaning

Before we can begin to understand the data and effectively use it to do our analysis, we first need to do some data cleaning. We should group by country and we should get the weather for all of them. Population size will also be an important factor in our study, so well will also factor that in. We will not clean our data accordingly.


# Made a new data frame with a columns called temperature,then using the latitude and longitude got there respective weather by doing an api call to the weather station
data.frame(covid_19) -> country_data
country_data$Province = NULL
country_data$Temperature = c(0)

n = nrow(country_data)
for (i in 1:n){
  weather = suppressWarnings( locationforecast(lat=country_data$Latitude[i], lon=country_data$Longitude[i]) )
  temp = suppressWarnings(mean(weather$temperature))
  country_data$Temperature[i] = temp
}

# Get the weather data
Weather_data  = country_data
Weather_data$Latitude = NULL
Weather_data$Longitude = NULL
Weather_data %>%
  group_by(Country) %>%
  summarise(Confirmed_Cases = sum(Confirmed)) -> con
Weather_data %>%
  group_by(Country) %>%
  summarise(Death_Cases = sum(Deaths)) -> dea
Weather_data %>%
  group_by(Country) %>%
  summarise(Recovered_Cases = sum(Recovered)) -> rec
Weather_data %>%
  group_by(Country) %>%
  summarise(Mean_Temprature = mean(Temperature)) -> tem

Weather_data_country = data.frame(con$Country, con$Confirmed_Cases, dea$Death_Cases, rec$Recovered_Cases, tem$Mean_Temprature)
colnames(Weather_data_country) <- c("Country", "Confirmed Cases", "Death Cases", "Recovered Cases", "Average Temprature")
country = Weather_data_country$Country
countrycode(country, origin = 'country.name', destination = 'iso2c') -> Country_code
cbind(Country_code ,Weather_data_country) -> Weather_data_country
WDI(country = Weather_data_country$Country_code, indicator="SP.POP.TOTL",start=2018, end=2018) -> population
#
head(Weather_data_country)
merge(Weather_data_country, population, by.x = "Country_code", by.y = "iso2c") -> df
df$country = NULL
df$year = NULL
colnames(df)[7] = "Total Population"
head(df)
c_url = "https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_19-covid-Confirmed.csv"
d_url = "https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_19-covid-Deaths.csv"
r_url = "https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_19-covid-Recovered.csv"

# Reading in the Time Data-Set
confirmed_ts = read_delim(c_url, ",")
death_ts = read_delim(d_url, ",")
recover_ts = read_delim(r_url, ",")
# Confirmed Cases for dates
confirmed_ts %>%
  pivot_longer("1/22/20":"3/21/20", names_to = "Dates", values_to = "Confirmed_Cases") -> confirmed_ts

# Death Cases for dates
death_ts %>%
  pivot_longer("1/22/20":"3/21/20", names_to = "Dates", values_to = "Death_Cases") -> death_ts

# Recovered Cases for dates
recover_ts %>%
  pivot_longer("1/22/20":"3/21/20", names_to = "Dates", values_to = "Recover_Cases") -> recover_ts
confirmed_ts %>%
  group_by(Dates) %>%
  summarise(Confirmed_Cases = sum(`Confirmed_Cases`))->cs

recover_ts %>%
  group_by(Dates) %>%
  summarise(Recover_Cases = sum(`Recover_Cases`)) -> rs

death_ts %>%
  group_by(Dates) %>%
  summarise(Death_Cases = sum(`Death_Cases`)) -> ds
merge(cs, rs, by.x = "Dates", by.y = "Dates") -> dfr
merge(dfr, ds, by.x = "Dates", by.y = "Dates" ) -> TotalCount_ByDate

Now that we have cleaned our data lets answer some questions.

Analysis

a. Which country has the most number of casualties ?

df %>%
  group_by(Country) %>%
  arrange(desc(`Confirmed Cases`, `Death Cases`, `Recovered Cases`)) -> conf_df
head(conf_df,4) -> dfmax
dfmax

As we can see China ranks the most amongs all of them, followed by Italy, Iran and Spain

b. Which country has the least number of casualties ?

df %>%
  group_by(Country) %>%
  arrange(`Confirmed Cases`, `Death Cases`, `Recovered Cases`) -> conf_df
head(conf_df,4) -> conf_df
conf_df

As we can see Republic of the Congo ranks the most among all of them, followed by Puerto Rico, Palestinian territory and Antigua and Barbuda

c. What is the general trend of the COVID-19 virus as the day’s progress?

Let us call the data we cleaned up above and show some of the rows

head(TotalCount_ByDate)

We see one categorical data called Dates and three Quantitative Data called Confirmed Cases, Recover Cases and Death Cases . However since we have a time series data, we thought it would be interesting to try using the xts library.

as.Date(TotalCount_ByDate$Dates,format="%m/%d/%y") -> o

data.frame(o, TotalCount_ByDate$Confirmed_Cases, TotalCount_ByDate$Death_Cases, TotalCount_ByDate$Recover_Cases) -> data
don <- xts(x = data, order.by = data$o)
names(don)[2] = "Confirmed Cases"
names(don)[3] = "Death Cases"
names(don)[4] = "Recovered Cases"
# Finally the plot
p <- dygraph(don) %>%
  dyOptions(labelsUTC = TRUE, fillGraph=TRUE, fillAlpha=0.1, drawGrid = FALSE, colors = RColorBrewer::brewer.pal(3, "Set1")) %>%
  dyRangeSelector() %>%
  dyCrosshair(direction = "vertical") %>%
  dyHighlight(highlightCircleSize = 5, highlightSeriesBackgroundAlpha = 0.2, hideOnMouseOut = FALSE)  %>%
  dyRoller(rollPeriod = 1)
p

From the graph we can see that there’s been a global sharp increase in the number of cases after March 12. The graph might possibly be following an exponential curve, but have yet to hit the surge. We also see that the number of confirmed cases are growing at a faster rate than Recovered and Death Cases.

d. Does Climate have an effect on the COVID-19 virus?

For the purpose of the analysis we want to see if the temperature has any affect on the COVID-19 virus. From question (a) we found out that the countries with the most number of confirmed cases is China and Italy. We start by taking the mean of the two temperatures as a bench mark.

bench_temp = c(dfmax$`Average Temprature`[1],dfmax$`Average Temprature`[2])
mu = mean(bench_temp)
mu
[1] 11.62157

Our mu here is 11.6 which signifies that this temperature is ideal for the virus to sustain.

Now lets split our dataset into 3 categories by their number of confirmed cases. Which are as follows: 1. Small: Confirmed cases between 0 and 200 2. Medium: Confirmed cases between 200 and 20000 3. Large: Confirmed cases are larger than 20000

df %>%
  filter(df$`Confirmed Cases`  < 200) -> small

head(small)
df %>%
  filter(df$`Confirmed Cases`  <5000 ,df$`Confirmed Cases`  >200 ) -> medium

head(medium)
df %>%
  filter(df$`Confirmed Cases`  >= 5000) -> large

head(large)

This is a scatter plot of all of our separated data sets

ggplot(small, aes(y = `Confirmed Cases`, x = `Average Temprature`)) + geom_point()

Small Graph: We can see that the number of confirmed cases has a high density when the temperature is above 10 degrees celsius. So we can speculate that the temperature might play an important role when the number of confirmed cases is between 0 and 200.

ggplot(medium, aes(y = `Confirmed Cases`, x = `Average Temprature`)) + geom_point()

Medium Graph: We can see that the number of confirmed cases are mostly in between 500 and 750. We also see that for those cases, the temperature is evenly spread out meaning that once there is a certain threshold of people who have been affected, the temperature may no longer play an effective part in shortening the virus lifespan enough to make a difference. We can see that for the number of cases between 750 and 5000 have a lower average temperature, about 15 degrees celsius and below.

ggplot(large, aes(y = `Confirmed Cases`, x = `Average Temprature`)) +geom_point()

Large Graph: We can see that almost all of our data points lie below 15 degrees celsius. Also there is only one data point which is above 40000 confirmed cases. (This is China from our previous EDA).

Lets start by checking if there is any correlation between our variable

cor.test(small$`Average Temprature`, small$`Confirmed Cases`)

    Pearson's product-moment correlation

data:  small$`Average Temprature` and small$`Confirmed Cases`
t = -0.96404, df = 110, p-value = 0.3371
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 -0.27246040  0.09564811
sample estimates:
        cor
-0.09153218 
small.1 <- lm(small$`Average Temprature` ~ small$`Confirmed Cases`, data = small)
summary(small.1)

Call:
lm(formula = small$`Average Temprature` ~ small$`Confirmed Cases`,
    data = small)

Residuals:
    Min      1Q  Median      3Q     Max
-37.042  -7.943   3.804   7.070  14.597

Coefficients:
                        Estimate Std. Error t value Pr(>|t|)
(Intercept)             19.76523    1.17111  16.877   <2e-16 ***
small$`Confirmed Cases` -0.01802    0.01869  -0.964    0.337
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 9.776 on 110 degrees of freedom
Multiple R-squared:  0.008378,  Adjusted R-squared:  -0.0006366
F-statistic: 0.9294 on 1 and 110 DF,  p-value: 0.3371
medium.1 <- lm(medium$`Average Temprature` ~ medium$`Confirmed Cases`, data = medium)
summary(medium.1)

Call:
lm(formula = medium$`Average Temprature` ~ medium$`Confirmed Cases`,
    data = medium)

Residuals:
    Min      1Q  Median      3Q     Max
-21.451  -5.933  -2.701   6.218  16.041

Coefficients:
                          Estimate Std. Error t value Pr(>|t|)
(Intercept)              12.975510   2.853452   4.547 0.000111 ***
medium$`Confirmed Cases` -0.002692   0.003011  -0.894 0.379490
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 10.01 on 26 degrees of freedom
Multiple R-squared:  0.02983,   Adjusted R-squared:  -0.007487
F-statistic: 0.7994 on 1 and 26 DF,  p-value: 0.3795
large.1 <- lm(large$`Average Temprature` ~ large$`Confirmed Cases`, data = large)
summary(large.1)

Call:
lm(formula = large$`Average Temprature` ~ large$`Confirmed Cases`,
    data = large)

Residuals:
    Min      1Q  Median      3Q     Max
-8.5335 -3.2256 -0.7415  1.5434 11.0840

Coefficients:
                          Estimate Std. Error t value Pr(>|t|)
(Intercept)              1.250e+01  3.210e+00   3.896  0.00802 **
large$`Confirmed Cases` -9.623e-06  1.001e-04  -0.096  0.92658
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 6.728 on 6 degrees of freedom
Multiple R-squared:  0.001536,  Adjusted R-squared:  -0.1649
F-statistic: 0.009233 on 1 and 6 DF,  p-value: 0.9266

After running a linear regression on all 3 datasets, we can see that the p-value in all 3 sets are all above 0.05. This means that Confirmed Cases for all 3 sets is not significant. We also have an r-squared values which are very low, meaning that temperature does not tell us much about its correlation with confirmed cases across all 3 datasets.

After looking at our datasets, our graphs and running linear regressions on our sets, we cannot confidently say that there is a correlation between temperature and the amount of confirmed cases. From the small dataset, it initially may have seemed that there is a relation between higher temperatures and confirmed cases but as we went to the medium and large datasets, we were unable to continue on with the assumption. This makes sense, as our small data sets have a smaller numbers of confirmed cases so we may not have enough evidence in this set in order to make proper assumptions. Overall, based on our evidence and findings, we cannot say with confidence that there is a relationship between temperature and the spread of the virus. Of course something to note is that this virus began to spread during the cooler months of the most effected countries, so it hard to also tell if our conclusions could have been different if this were happening at a different time line.

e. Does Population have an effect on COVID-19 virus ?

Since our dataset has multiple fields and for the purpose of this questuon we will only be concerned with confirmed casea and total population and drop everything else.

df %>% select(Country,`Confirmed Cases`, `Total Population`) %>%
  arrange(desc(`Total Population`)) -> dfpop
head(dfpop)
ggplot(dfpop, aes(x = `Total Population`, y = `Confirmed Cases`)) +geom_point()

dfpop %>%
  filter(dfpop$`Total Population`  < 1000000) -> smallpop
head(smallpop)
dfpop %>%
  filter(dfpop$`Total Population`  > 1000000, dfpop$`Total Population` < 50000000 ) -> mediumpop

head(mediumpop)
dfpop %>%
  filter(dfpop$`Total Population`  > 50000000 ) -> largepop
head(largepop)
ggplot(smallpop, aes(x = `Total Population`, y = `Confirmed Cases`)) +geom_point()

ggplot(mediumpop, aes(x = `Total Population`, y = `Confirmed Cases`)) +geom_point()

ggplot(largepop, aes(x = `Total Population`, y = `Confirmed Cases`)) +geom_point()

From all three groups, we can see that the majority of confirmed cases are relatively low with respect to the countries total population. There are a couple outliers in each dataset but this can be expected. There is no trend to show that a higher population equates to more confirmed cases. We can run a linear regression once again to see if there is a trend that we cannot determine from our graphs.

smallpop.1 <- lm(smallpop$`Confirmed Cases` ~ smallpop$`Total Population`, data = smallpop)
summary(smallpop.1)

Call:
lm(formula = smallpop$`Confirmed Cases` ~ smallpop$`Total Population`,
    data = smallpop)

Residuals:
    Min      1Q  Median      3Q     Max
-37.948 -26.928 -23.287  -0.944 189.183

Coefficients:
                             Estimate Std. Error t value Pr(>|t|)
(Intercept)                 2.364e+01  1.972e+01   1.199    0.244
smallpop$`Total Population` 2.029e-05  5.001e-05   0.406    0.689

Residual standard error: 57.5 on 20 degrees of freedom
Multiple R-squared:  0.008158,  Adjusted R-squared:  -0.04143
F-statistic: 0.1645 on 1 and 20 DF,  p-value: 0.6893
mediumpop.1 <- lm(mediumpop$`Confirmed Cases` ~ mediumpop$`Total Population`, data = mediumpop)
summary(mediumpop.1)

Call:
lm(formula = mediumpop$`Confirmed Cases` ~ mediumpop$`Total Population`,
    data = mediumpop)

Residuals:
    Min      1Q  Median      3Q     Max
-1062.8  -332.8  -100.0   -24.1 10686.6

Coefficients:
                              Estimate Std. Error t value Pr(>|t|)
(Intercept)                  1.053e+00  1.765e+02   0.006   0.9953
mediumpop$`Total Population` 2.269e-05  9.458e-06   2.400   0.0183 *
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1205 on 97 degrees of freedom
Multiple R-squared:  0.05603,   Adjusted R-squared:  0.0463
F-statistic: 5.758 on 1 and 97 DF,  p-value: 0.01833
largepop.1 <- lm(largepop$`Confirmed Cases` ~ largepop$`Total Population`, data = largepop)
summary(largepop.1)

Call:
lm(formula = largepop$`Confirmed Cases` ~ largepop$`Total Population`,
    data = largepop)

Residuals:
   Min     1Q Median     3Q    Max
-38122  -3840  -2701   -802  41662

Coefficients:
                             Estimate Std. Error t value Pr(>|t|)
(Intercept)                 1.215e+02  3.112e+03   0.039  0.96918
largepop$`Total Population` 2.820e-05  7.841e-06   3.596  0.00139 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 13660 on 25 degrees of freedom
Multiple R-squared:  0.3409,    Adjusted R-squared:  0.3146
F-statistic: 12.93 on 1 and 25 DF,  p-value: 0.001386

Based on our data above, we can conclude that a higher population does not necessarily mean more confirmed cases. This can be clearly shown by looking at our dfpop dataframe. We can see that India has a total of 142 confirmed cases with a total population of 1352617328, where as Italy has a total of 31506 confirmed cases with a total population of 60431283.

Despite being half the population of India, there is tens of thousands of more confirmed cases. There are a number of factors which significantly contribute the amount of confirmed cases, however having more people in a country does not always mean more people have the virus. There is a an increased likelihood of the virus spreading faster, if precautions are not taken by the government and people, such is the case in Italy. However, there are many actions individuals can take in order to stop the spread of the virus so quickly, such as social distancing, self quarantining and practicing good hygiene and cleanliness. A high population doesn’t always mean a high number of confirmed cases, but more of a risk of more individuals to catch the virus.

Conclusion

It seems that the global fear of COVID-19 and it’s rapid increase is due to the same problem we drew from doing this project; it is difficult to contain an unknown disease from spreading when we cannot predict how well it thrives in its environment. Our analysis could not help us conclude if temperature played an effective role in controlling the growth of the virus, with the most number of cases being in China and the least number of cases being in Republic of Congo. Although there seemed to be signs of the virus being less effective in hotter climates when looking at the countries with the least number of casualties, as the number of victims grew exponentially, temperature’s effect on the lifespan of the virus became possibly insignificant. And since this growth happened during the colder months of the most effected countries, it is even more difficult to draw any strong conclusions. It is possible that on a different timeline, the scenario could have been completely different, and it may be still too early to draw an opinion. As we’ve seen from our first graph, we have yet to reach worldwide peak. Similarly, we could not conclude that the population size of a country had a play in its spread. There may be other factors that come into play, such as a country’s age distribution, travel restrictions etc., that could explain its current state. From recent events we do know that in general the more people that are traveling, the more the virus is able to spread by contact, with it being somewhat of a domino effect. So our solution has been to go into quarantine despite whatever level of cases in the area. Hopefully we can soon expect to gather enough data to successfully know how to reduce casualties arising from COVID-19.

LS0tCnRpdGxlOiAnRGF0YSBBbmFseXNpczogQ09WSUQtMTkgVmlydXMnCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAotLS0KIyBBdXRob3JzCkFiaGludiBDaGF1ZGhhcnkgOiAxMDAyNzA3NzMzCgpBZGhhbSBGYXJhZyA6IDEwMDM3ODgxOTAKCkV0aGFuIEFuYWRhIDogMTAwMzE3MTkwNwoKSGFtaXNoIFJhaml2IDoKCkx5bGEgVGFtaW0gOiAxMDAzNDU5NDY1CgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgojIFByZWZhY2UKCgoqKk9uIDMxIERlY2VtYmVyIDIwMTkqKiwgV0hPIHdhcyBhbGVydGVkIHRvIHNldmVyYWwgY2FzZXMgb2YgcG5ldW1vbmlhIGluIFd1aGFuIENpdHksIEh1YmVpIFByb3ZpbmNlIG9mIENoaW5hLiBUaGUgdmlydXMgcmVzcG9uc2libGUgd2FzIG5vdCBhIGtub3duIG9uZSBhbmQgdGhpcyByYWlzZWQgaW1tZWRpYXRlIGNvbmNlcm4gcmVnYXJkaW5nIHBlb3BsZSdzIHNhZmV0eSwgZXNwZWNpYWxseSBnaXZlbiB0aGUgcmF0ZSBpdCB3YXMgc3ByZWFkaW5nIHRocm91Z2hvdXQgdGhlIGdsb2JhbCBtYXAuIFdpdGggaXQncyBwcm9wZXIgc3ltcHRvbXMgYW5kIHRyZWF0bWVudHMgc3RpbGwgdW5rbm93biwgZGFpbHkgbGV2ZWwgaW5mb3JtYXRpb24gb24gdGhlIGFmZmVjdGVkIHBlb3BsZSBjYW4gZ2l2ZSBzb21lIGludGVyZXN0aW5nIGluc2lnaHRzLiBXZSB3b3VsZCBsaWtlIHRvIHRoYW5rIEpvaG5zIEhvcGtpbnMgVW5pdmVyc2l0eSBmb3IgbWFraW5nIHRoZSBkYXRhIGF2YWlsYWJsZSBmb3IgZWR1Y2F0aW9uYWwgYW5kIGFjYWRlbWljIHJlc2VhcmNoIHB1cnBvc2VzLgoKCiMgSW50cm9kdWN0aW9uCioqVGhlIDIwMTkgTm92ZWwgQ29yb25hdmlydXMqKiBpcyBhIHZpcnVzIGlkZW50aWZpZWQgYXMgdGhlIGNhdXNlIG9mIGFuIG91dGJyZWFrIG9mIHJlc3BpcmF0b3J5IGlsbG5lc3MgZmlyc3QgZGV0ZWN0ZWQgaW4gV3VoYW4sIENoaW5hLiBBdCB0aGlzIHRpbWUsIGl0IGlzIHVuY2xlYXIgaG93IGVhc2lseSBvciBzdXN0YWluYWJsZSB0aGlzIHZpcnVzIGlzIHNwcmVhZGluZyBiZXR3ZWVuIHBlb3BsZS4gVGhpcyBkYXRhc2V0IGhhcyBkYWlseSBsZXZlbCBpbmZvcm1hdGlvbiBvbiB0aGUgbnVtYmVyIG9mIGFmZmVjdGVkIGNhc2VzLCBkZWF0aHMgYW5kIHJlY292ZXJ5IGZyb20gKioyMDE5IG5vdmVsIGNvcm9uYXZpcnVzKiosIHRoZSBuYW1lIG5vdyBzaG9ydGVuZWQgdG8gQ09WSUQtMTkuIEZvciB0aGUgcHVycG9zZXMgb2YgdGhpcyBwcm9qZWN0LCB3ZSB3aWxsIG9ubHkgYmUgY29sbGVjdGluZyBkYXRhIHRpbGwgKioxN3RoIE1hcmNoIDIwMjAuKiogCgpPdXIgdGFyZ2V0IGZvciB0aGlzIHByb2plY3Qgd2lsbCBiZSB0byBzdHVkeSB0aGUgZWZmZWN0cyBvZiBnZW9ncmFwaHkgYW5kIHBvcHVsYXRpb24gb24gdGhlIHJhdGUgYXQgd2hpY2ggdGhlIHZpcnVzIGlzIGFibGUgdG8gc3ByZWFkLiBTdHVkeWluZyB0aGUgZ2VvZ3JhcGh5IG9mIGVhY2ggY2FzZSB3aWxsIGhlbHAgdXMgYW5hbHl6aW5nIGlmIHRlbXBlcmF0dXJlIHBsYXlzIGEgcGFydCBpbiBob3cgd2VsbCB0aGUgdmlydXMgaXMgYWJsZSB0byBleGlzdCwgYW5kIHNvIGhvdyBmYXN0IGl0IGlzIGFibGUgdG8gbXVsdGlwbHkuIFdlIHdpbGwgYWxzbyBsb29rIGF0IHRoZSBwb3B1bGF0aW9uIGRlbnNpdHkgb2YgdGhlIGVmZmVjdGVkIGNvdW50cmllcyBhbmQgc2VlIGlmIHRoZSBudW1iZXIgb2YgY2FzZXMgaGF2ZSBhIGxpbmVhciByZWxhdGlvbnNoaXAgd2l0aCB0aGUgcG9wdWxhdGlvbiBzaXplIGl0c2VsZi5Nb3N0IGltcG9ydGFudGx5LCB0aGlzIHByb2plY3Qgd2lsbCBoZWxwIHVzIGJldHRlciB1bmRlcnN0YW5kIGFuZCBwcmVwYXJlIGZvciB0aGUgcGFuZGVtaWMgYW5kIHBvc3NpYmx5IGhlbHAgcmVhY3QgYWNjb3JkaW5nbHkgdG8gZXZlcnkgcmFuZ2Ugb2Ygc2V2ZXJpdHksIGllLiB3aGF0IHRoZSBhcHByb3ByaWF0ZSBzYWZldHkgbWVhc3VyZXMgYXJlLiAKCgojIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMKCgojIyMgRGVwZW5kZW5jaWVzCgpUbyBjb21wbGV0ZSBvdXIgRGF0YSBBbmFseXNpcyB3ZSdsbCBiZSB1c2luZyB0aGUgKip0aWR5dmVyc2UqKiBsaWJyYXJ5LiBUbyB2aXN1YWxpemUgb3VyIGRhdGEgd2Ugd2lsbCBiZSB1c2luZyAqKmdncGxvdDIsIGRwbHlyLCBtYXBzIGFuZCB2aXJpZGlzKiouIFRvIGdldCB0aGUgd2VhdGhlciBiYXNlZCBvbiBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlLCB3ZSB3aWxsIGJlIG1ha2luZyBhbiBhcGkgY2FsbCB1c2luZyAqKndlYXRoZXJyKiouIFRvIGdldCBjb3VudHJ5IGJhc2VkIGRhdGEgc3VjaCBhcyBwb3B1bGF0aW9uIGRlbnNpdHksIGNvdW50cnkgY29kZSBldGMuLi4gd2Ugd2lsbCBiZSB1c2luZyAqKldESSoqIHdoaWNoIGlzIHRoZSAqV29ybGQgRGV2ZWxvcG1lbnQgSW5kaWNhdG9ycyogRGF0YUJhbmsgYW5kIHRoZSAqKmNvdW50cnljb2RlKiogIExpYnJhcnkuCmBgYHtyfQojIFRpZHl2ZXJzZQpsaWJyYXJ5KHRpZHl2ZXJzZSkKIyBHcmFwaGluZyBMaWJyYXJ5CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5cikKbGlicmFyeShtYXBzKQpsaWJyYXJ5KHZpcmlkaXMpCiMtLS0tLS0tLS0tLS0tLS0tLS0KbGlicmFyeShkeWdyYXBocykKbGlicmFyeSh4dHMpICAgICAgICAgCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKIyBXZWF0aGVyIGFuZCBDb3VudHJ5IERhdGEKbGlicmFyeSh3ZWF0aGVycikKbGlicmFyeShXREkpCmxpYnJhcnkoY291bnRyeWNvZGUpCgpgYGAKCiMjIyBSZWFkaW5nIFRoZSBEYXRhIApMZXQncyBmaXJzdCBnbyBhaGVhZCBhbmQgc2VlIHdoYXQgb3VyIGRhdGEgbG9va3MgbGlrZSBieSBwcmludGluZyBvdXQgZmlyc3QgZmV3IHJvd3MKCmBgYHtyfQojIFJlYWRpbmcKY292aWRfMTkgPC0gcmVhZF9jc3YoIjAzLTE3LTIwMjAuY3N2IikKCmBgYAoKYGBge3J9Cm5hbWVzKGNvdmlkXzE5KVsxXSA8LSAiUHJvdmluY2UiCm5hbWVzKGNvdmlkXzE5KVsyXSA8LSAiQ291bnRyeSIKaGVhZChjb3ZpZF8xOSkKYGBgCgpCeSB0aGUgbG9va3Mgb2YgdGhlIGRhdGEgd2UgaGF2ZSB0aGUgZm9sbG93aW5nIGZpZWxkczoKCgoqKjEuIFByb3ZpbmNlIDoqKiBSZWdpb24gaW4gdGhlIGNvdW50cnkgd2hlcmUgdGhlIHZpcnVzIHdhcyByZXBvcnRlZAoKCioqMi4gQ291bnRyeSA6KiogQ291bnRyeSB3aGVyZSBpdCB3YXMgcmVwb3J0ZWQKCgoqKjMuIExhc3QgVXBkYXRlIDoqKiBUaGUgdGltZSB3aGVuIHRoZSB0b3RhbCBjb3VudCB3YXMgcmV2aWV3ZWQgb250byB0aGUgZGF0YXRzZXQKCgoqKjQuIENvbmZpcm1lZDoqKiBUb3RhbCBudW1iZXIgb2YgcGVvcGxlIGFmZmVjdGVkIGJ5IHRoZSB2aXJ1cyAKCgoqKjUuIERlYXRocyA6KiogVG90YWwgbnVtYmVyIG9mIGRlYXRocyBmcm9tIHRoZSB2aXJ1cyAKCgoqKjYuIFJlY292ZXJlZCA6KiogVG90YWwgbnVtYmVyIG9mIHBlb3BsZSB0aGF0IHJlY292ZXJlZCBhZnRlciB0aGV5IGhhdmUgYmVlbiBhZmZlY3RlZCAKCgoqKjcuIExhdGl0dXRlIDoqKiBHZW8tbG9naWNhbCBsb2NhdGlvbiBvZiB0aGUgcmVwb3J0ZWQgaW5jaWRlbnQgCgoKKio4LiBMb25ndGl0dWRlIDoqKiBHZW8tbG9naWNhbCBsb2NhdGlvbiBvZiB0aGUgcmVwb3J0ZWQgaW5jaWRlbnQKCiMjIyBHcmFwaGluZyAKTm93IGxldCdzIHRyeSB0byB2aXN1YWxpemUgb3VyIGRhdGEgdG8gZ2V0IGEgYnJvYWRlciB2aWV3IG9mIHRoZSBwcm9ibGVtIGF0IGhhbmQuIEZpcnN0bHkgd2UnbGwgdGFrZSBhIGxvb2sgYXQgdGhlIHRvdGFsIG51bWJlciBvZiAqKmNvbmZpcm1lZCBjYXNlcyoqLgoKYGBge3J9CndvcmxkIDwtIG1hcF9kYXRhKCJ3b3JsZCIpCmdncGxvdCgpICsKICBnZW9tX3BvbHlnb24oZGF0YSA9IHdvcmxkLCBhZXMoeD1sb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSwgZmlsbD0iZ3JleSIsIGFscGhhPTAuMykgKwogIGdlb21fcG9pbnQoIGRhdGE9Y292aWRfMTksIGFlcyh4PWNvdmlkXzE5JExvbmdpdHVkZSwgeT1jb3ZpZF8xOSRMYXRpdHVkZSwgc2l6ZT1jb3ZpZF8xOSRDb25maXJtZWQsIGNvbG9yPWNvdmlkXzE5JENvbmZpcm1lZCkpICsKICAgIHNjYWxlX2NvbG9yX3ZpcmlkaXModHJhbnM9ImxvZyIpICsKCiAgdGhlbWVfdm9pZCgpKyBjb29yZF9tYXAoKSAKYGBgCkJ5IGxvb2tpbmcgYXQgdGhlIGdyYXBoIHdlIGNhbiB0aGF0IHRoZSBtb3N0IG51bWJlciBvZiBjYXNlcyByZXBvcnRlZCB0by1kYXRlIGhhcHBlbiB0byBiZSBpbiBDaGluYS4gV2UgYWxzbyBzZWUgc29tZSBjYXNlcyBiZWluZyByZXBvcnRlZCBhdCBzZWEsIHRoZXNlIGFyZSBtb3N0IHByb2JhYmx5IHNoaXBzIHdoaWNoIHdlcmUgdHJhdmVsaW5nIGF0IHRoZSB0aW1lLgoKCgoKYGBge3J9CndvcmxkIDwtIG1hcF9kYXRhKCJ3b3JsZCIpCmdncGxvdCgpICsKICBnZW9tX3BvbHlnb24oZGF0YSA9IHdvcmxkLCBhZXMoeD1sb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSwgZmlsbD0iZ3JleSIsIGFscGhhPTAuMykgKwogIGdlb21fcG9pbnQoIGRhdGE9Y292aWRfMTksIGFlcyh4PWNvdmlkXzE5JExvbmdpdHVkZSwgeT1jb3ZpZF8xOSRMYXRpdHVkZSwgc2l6ZT1jb3ZpZF8xOSREZWF0aHMsIGNvbG9yPWNvdmlkXzE5JERlYXRocykpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzKHRyYW5zPSJsb2ciKSArCiAgdGhlbWVfdm9pZCgpKyBjb29yZF9tYXAoKSAKYGBgCkZyb20gdGhlIGdyYXBoIHdlIGNhbiBzZWUgdGhhdCB0aGUgbW9zdCBudW1iZXIgb2YgZGVhdGhzIGhhcHBlbmVkIGluIENoaW5hIGFuZCBJdGFseS4gQXMgY29tcGFyZWQgdG8gdGhlIG51bWJlciBvZiBjb25maXJtZWQgY2FzZXMgdGhpcyBncmFwaCBzZWVtcyB0byBiZSBsZXNzIGRlbnNlIHdoaWNoIGlzIGEgZ29vZCBzaWduLiAKCgoKYGBge3J9CndvcmxkIDwtIG1hcF9kYXRhKCJ3b3JsZCIpCmdncGxvdCgpICsKICBnZW9tX3BvbHlnb24oZGF0YSA9IHdvcmxkLCBhZXMoeD1sb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSwgZmlsbD0iZ3JleSIsIGFscGhhPTAuMykgKwogIGdlb21fcG9pbnQoIGRhdGE9Y292aWRfMTksIGFlcyh4PWNvdmlkXzE5JExvbmdpdHVkZSwgeT1jb3ZpZF8xOSRMYXRpdHVkZSwgc2l6ZT1jb3ZpZF8xOSRSZWNvdmVyZWQsIGNvbG9yPWNvdmlkXzE5JFJlY292ZXJlZCkpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzKHRyYW5zPSJsb2ciKSArCiAgdGhlbWVfdm9pZCgpKyBjb29yZF9tYXAoKSAKYGBgCkZyb20gdGhpcyBncmFwaCB3ZSBzZWUgdGhhdCBtb3N0IG9mIHRoZSByZWNvdmVyZWQgY2FzZXMgaGFwcGVuZWQgYWdhaW4gaW4gQ2hpbmEgYW5kIEl0YWx5LiBXaXRoIGEgbnVtYmVyIG9mIHJlY292ZXJlZCBjYXNlcyBncm93aW5nIGluIE5vcnRoIEFtZXJpY2EuIAoKCiMjIERhdGEgQ2xlYW5pbmcgCkJlZm9yZSB3ZSBjYW4gYmVnaW4gdG8gdW5kZXJzdGFuZCB0aGUgZGF0YSBhbmQgZWZmZWN0aXZlbHkgdXNlIGl0IHRvIGRvIG91ciBhbmFseXNpcywgd2UgZmlyc3QgbmVlZCB0byBkbyBzb21lIGRhdGEgY2xlYW5pbmcuIFdlIHNob3VsZCBncm91cCBieSBjb3VudHJ5IGFuZCB3ZSBzaG91bGQgZ2V0IHRoZSB3ZWF0aGVyIGZvciBhbGwgb2YgdGhlbS4gUG9wdWxhdGlvbiBzaXplIHdpbGwgYWxzbyBiZSBhbiBpbXBvcnRhbnQgZmFjdG9yIGluIG91ciBzdHVkeSwgc28gd2VsbCB3aWxsIGFsc28gZmFjdG9yIHRoYXQgaW4uIFdlIHdpbGwgbm90IGNsZWFuIG91ciBkYXRhIGFjY29yZGluZ2x5LgoKYGBge3J9CgojIE1hZGUgYSBuZXcgZGF0YSBmcmFtZSB3aXRoIGEgY29sdW1ucyBjYWxsZWQgdGVtcGVyYXR1cmUsdGhlbiB1c2luZyB0aGUgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSBnb3QgdGhlcmUgcmVzcGVjdGl2ZSB3ZWF0aGVyIGJ5IGRvaW5nIGFuIGFwaSBjYWxsIHRvIHRoZSB3ZWF0aGVyIHN0YXRpb24gCmRhdGEuZnJhbWUoY292aWRfMTkpIC0+IGNvdW50cnlfZGF0YQpjb3VudHJ5X2RhdGEkUHJvdmluY2UgPSBOVUxMCmNvdW50cnlfZGF0YSRUZW1wZXJhdHVyZSA9IGMoMCkKCm4gPSBucm93KGNvdW50cnlfZGF0YSkKZm9yIChpIGluIDE6bil7CiAgd2VhdGhlciA9IHN1cHByZXNzV2FybmluZ3MoIGxvY2F0aW9uZm9yZWNhc3QobGF0PWNvdW50cnlfZGF0YSRMYXRpdHVkZVtpXSwgbG9uPWNvdW50cnlfZGF0YSRMb25naXR1ZGVbaV0pICkKICB0ZW1wID0gc3VwcHJlc3NXYXJuaW5ncyhtZWFuKHdlYXRoZXIkdGVtcGVyYXR1cmUpKQogIGNvdW50cnlfZGF0YSRUZW1wZXJhdHVyZVtpXSA9IHRlbXAKfQoKYGBgCmBgYHtyfQoKIyBHZXQgdGhlIHdlYXRoZXIgZGF0YSAKV2VhdGhlcl9kYXRhICA9IGNvdW50cnlfZGF0YQpXZWF0aGVyX2RhdGEkTGF0aXR1ZGUgPSBOVUxMCldlYXRoZXJfZGF0YSRMb25naXR1ZGUgPSBOVUxMCldlYXRoZXJfZGF0YSAlPiUgCiAgZ3JvdXBfYnkoQ291bnRyeSkgJT4lIAogIHN1bW1hcmlzZShDb25maXJtZWRfQ2FzZXMgPSBzdW0oQ29uZmlybWVkKSkgLT4gY29uCldlYXRoZXJfZGF0YSAlPiUgCiAgZ3JvdXBfYnkoQ291bnRyeSkgJT4lIAogIHN1bW1hcmlzZShEZWF0aF9DYXNlcyA9IHN1bShEZWF0aHMpKSAtPiBkZWEKV2VhdGhlcl9kYXRhICU+JSAKICBncm91cF9ieShDb3VudHJ5KSAlPiUgCiAgc3VtbWFyaXNlKFJlY292ZXJlZF9DYXNlcyA9IHN1bShSZWNvdmVyZWQpKSAtPiByZWMKV2VhdGhlcl9kYXRhICU+JSAKICBncm91cF9ieShDb3VudHJ5KSAlPiUgCiAgc3VtbWFyaXNlKE1lYW5fVGVtcHJhdHVyZSA9IG1lYW4oVGVtcGVyYXR1cmUpKSAtPiB0ZW0KCldlYXRoZXJfZGF0YV9jb3VudHJ5ID0gZGF0YS5mcmFtZShjb24kQ291bnRyeSwgY29uJENvbmZpcm1lZF9DYXNlcywgZGVhJERlYXRoX0Nhc2VzLCByZWMkUmVjb3ZlcmVkX0Nhc2VzLCB0ZW0kTWVhbl9UZW1wcmF0dXJlKQpjb2xuYW1lcyhXZWF0aGVyX2RhdGFfY291bnRyeSkgPC0gYygiQ291bnRyeSIsICJDb25maXJtZWQgQ2FzZXMiLCAiRGVhdGggQ2FzZXMiLCAiUmVjb3ZlcmVkIENhc2VzIiwgIkF2ZXJhZ2UgVGVtcHJhdHVyZSIpCgpgYGAKYGBge3J9CmNvdW50cnkgPSBXZWF0aGVyX2RhdGFfY291bnRyeSRDb3VudHJ5CmNvdW50cnljb2RlKGNvdW50cnksIG9yaWdpbiA9ICdjb3VudHJ5Lm5hbWUnLCBkZXN0aW5hdGlvbiA9ICdpc28yYycpIC0+IENvdW50cnlfY29kZQpjYmluZChDb3VudHJ5X2NvZGUgLFdlYXRoZXJfZGF0YV9jb3VudHJ5KSAtPiBXZWF0aGVyX2RhdGFfY291bnRyeQpXREkoY291bnRyeSA9IFdlYXRoZXJfZGF0YV9jb3VudHJ5JENvdW50cnlfY29kZSwgaW5kaWNhdG9yPSJTUC5QT1AuVE9UTCIsc3RhcnQ9MjAxOCwgZW5kPTIwMTgpIC0+IHBvcHVsYXRpb24KIyAKaGVhZChXZWF0aGVyX2RhdGFfY291bnRyeSkKYGBgCmBgYHtyfQptZXJnZShXZWF0aGVyX2RhdGFfY291bnRyeSwgcG9wdWxhdGlvbiwgYnkueCA9ICJDb3VudHJ5X2NvZGUiLCBieS55ID0gImlzbzJjIikgLT4gZGYKZGYkY291bnRyeSA9IE5VTEwKZGYkeWVhciA9IE5VTEwKY29sbmFtZXMoZGYpWzddID0gIlRvdGFsIFBvcHVsYXRpb24iCmhlYWQoZGYpCgpgYGAKCgoKYGBge3J9CmNfdXJsID0gImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9DU1NFR0lTYW5kRGF0YS9DT1ZJRC0xOS9tYXN0ZXIvY3NzZV9jb3ZpZF8xOV9kYXRhL2Nzc2VfY292aWRfMTlfdGltZV9zZXJpZXMvdGltZV9zZXJpZXNfMTktY292aWQtQ29uZmlybWVkLmNzdiIKZF91cmwgPSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0NTU0VHSVNhbmREYXRhL0NPVklELTE5L21hc3Rlci9jc3NlX2NvdmlkXzE5X2RhdGEvY3NzZV9jb3ZpZF8xOV90aW1lX3Nlcmllcy90aW1lX3Nlcmllc18xOS1jb3ZpZC1EZWF0aHMuY3N2IgpyX3VybCA9ICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vQ1NTRUdJU2FuZERhdGEvQ09WSUQtMTkvbWFzdGVyL2Nzc2VfY292aWRfMTlfZGF0YS9jc3NlX2NvdmlkXzE5X3RpbWVfc2VyaWVzL3RpbWVfc2VyaWVzXzE5LWNvdmlkLVJlY292ZXJlZC5jc3YiCgojIFJlYWRpbmcgaW4gdGhlIFRpbWUgRGF0YS1TZXQgIApjb25maXJtZWRfdHMgPSByZWFkX2RlbGltKGNfdXJsLCAiLCIpCmRlYXRoX3RzID0gcmVhZF9kZWxpbShkX3VybCwgIiwiKQpyZWNvdmVyX3RzID0gcmVhZF9kZWxpbShyX3VybCwgIiwiKQpgYGAKYGBge3J9CiMgQ29uZmlybWVkIENhc2VzIGZvciBkYXRlcwpjb25maXJtZWRfdHMgJT4lIAogIHBpdm90X2xvbmdlcigiMS8yMi8yMCI6IjMvMjEvMjAiLCBuYW1lc190byA9ICJEYXRlcyIsIHZhbHVlc190byA9ICJDb25maXJtZWRfQ2FzZXMiKSAtPiBjb25maXJtZWRfdHMKCiMgRGVhdGggQ2FzZXMgZm9yIGRhdGVzCmRlYXRoX3RzICU+JSAKICBwaXZvdF9sb25nZXIoIjEvMjIvMjAiOiIzLzIxLzIwIiwgbmFtZXNfdG8gPSAiRGF0ZXMiLCB2YWx1ZXNfdG8gPSAiRGVhdGhfQ2FzZXMiKSAtPiBkZWF0aF90cwoKIyBSZWNvdmVyZWQgQ2FzZXMgZm9yIGRhdGVzCnJlY292ZXJfdHMgJT4lIAogIHBpdm90X2xvbmdlcigiMS8yMi8yMCI6IjMvMjEvMjAiLCBuYW1lc190byA9ICJEYXRlcyIsIHZhbHVlc190byA9ICJSZWNvdmVyX0Nhc2VzIikgLT4gcmVjb3Zlcl90cwpgYGAKYGBge3J9CmNvbmZpcm1lZF90cyAlPiUgCiAgZ3JvdXBfYnkoRGF0ZXMpICU+JQogIHN1bW1hcmlzZShDb25maXJtZWRfQ2FzZXMgPSBzdW0oYENvbmZpcm1lZF9DYXNlc2ApKS0+Y3MKCnJlY292ZXJfdHMgJT4lCiAgZ3JvdXBfYnkoRGF0ZXMpICU+JSAKICBzdW1tYXJpc2UoUmVjb3Zlcl9DYXNlcyA9IHN1bShgUmVjb3Zlcl9DYXNlc2ApKSAtPiBycwoKZGVhdGhfdHMgJT4lCiAgZ3JvdXBfYnkoRGF0ZXMpICU+JSAKICBzdW1tYXJpc2UoRGVhdGhfQ2FzZXMgPSBzdW0oYERlYXRoX0Nhc2VzYCkpIC0+IGRzCmBgYApgYGB7cn0KbWVyZ2UoY3MsIHJzLCBieS54ID0gIkRhdGVzIiwgYnkueSA9ICJEYXRlcyIpIC0+IGRmciAgIAptZXJnZShkZnIsIGRzLCBieS54ID0gIkRhdGVzIiwgYnkueSA9ICJEYXRlcyIgKSAtPiBUb3RhbENvdW50X0J5RGF0ZQpgYGAKCgpOb3cgdGhhdCB3ZSBoYXZlIGNsZWFuZWQgb3VyIGRhdGEgbGV0cyBhbnN3ZXIgc29tZSBxdWVzdGlvbnMuCgojIEFuYWx5c2lzCgojIyMgYS4gV2hpY2ggY291bnRyeSBoYXMgdGhlIG1vc3QgbnVtYmVyIG9mIGNhc3VhbHRpZXMgPyAKYGBge3J9CmRmICU+JSAKICBncm91cF9ieShDb3VudHJ5KSAlPiUgCiAgYXJyYW5nZShkZXNjKGBDb25maXJtZWQgQ2FzZXNgLCBgRGVhdGggQ2FzZXNgLCBgUmVjb3ZlcmVkIENhc2VzYCkpIC0+IGNvbmZfZGYKaGVhZChjb25mX2RmLDQpIC0+IGRmbWF4CmRmbWF4CmBgYAogQXMgd2UgY2FuIHNlZSAqKkNoaW5hKiogcmFua3MgdGhlIG1vc3QgYW1vbmdzIGFsbCBvZiB0aGVtLCBmb2xsb3dlZCBieSAqKkl0YWx5KiosICoqSXJhbioqIGFuZCAqKlNwYWluKioKIAogCiMjIyBiLiBXaGljaCBjb3VudHJ5IGhhcyB0aGUgbGVhc3QgbnVtYmVyIG9mIGNhc3VhbHRpZXMgPwpgYGB7cn0KZGYgJT4lIAogIGdyb3VwX2J5KENvdW50cnkpICU+JSAKICBhcnJhbmdlKGBDb25maXJtZWQgQ2FzZXNgLCBgRGVhdGggQ2FzZXNgLCBgUmVjb3ZlcmVkIENhc2VzYCkgLT4gY29uZl9kZgpoZWFkKGNvbmZfZGYsNCkgLT4gY29uZl9kZgpjb25mX2RmCmBgYAogQXMgd2UgY2FuIHNlZSAqKlJlcHVibGljIG9mIHRoZSBDb25nbyoqIHJhbmtzIHRoZSBtb3N0IGFtb25nIGFsbCBvZiB0aGVtLCBmb2xsb3dlZCBieSAqKlB1ZXJ0byBSaWNvKiosICoqUGFsZXN0aW5pYW4gdGVycml0b3J5KiogYW5kICoqQW50aWd1YSBhbmQgQmFyYnVkYSoqCiAKIAojIyMgYy4gV2hhdCBpcyB0aGUgZ2VuZXJhbCB0cmVuZCBvZiB0aGUgQ09WSUQtMTkgdmlydXMgYXMgdGhlIGRheSdzIHByb2dyZXNzPwoKTGV0IHVzIGNhbGwgdGhlIGRhdGEgd2UgY2xlYW5lZCB1cCBhYm92ZSBhbmQgc2hvdyBzb21lIG9mIHRoZSByb3dzCmBgYHtyfQpoZWFkKFRvdGFsQ291bnRfQnlEYXRlKQpgYGAKV2Ugc2VlIG9uZSBjYXRlZ29yaWNhbCBkYXRhIGNhbGxlZCAqKkRhdGVzKiogYW5kIHRocmVlIFF1YW50aXRhdGl2ZSBEYXRhIGNhbGxlZCAqKkNvbmZpcm1lZCBDYXNlcywgUmVjb3ZlciBDYXNlcyBhbmQgRGVhdGggQ2FzZXMgKiouIEhvd2V2ZXIgc2luY2Ugd2UgaGF2ZSBhIHRpbWUgc2VyaWVzIGRhdGEsIHdlIHRob3VnaHQgaXQgd291bGQgYmUgaW50ZXJlc3RpbmcgdG8gdHJ5IHVzaW5nICB0aGUgKip4dHMqKiBsaWJyYXJ5LgoKYGBge3J9CmFzLkRhdGUoVG90YWxDb3VudF9CeURhdGUkRGF0ZXMsZm9ybWF0PSIlbS8lZC8leSIpIC0+IG8KCmRhdGEuZnJhbWUobywgVG90YWxDb3VudF9CeURhdGUkQ29uZmlybWVkX0Nhc2VzLCBUb3RhbENvdW50X0J5RGF0ZSREZWF0aF9DYXNlcywgVG90YWxDb3VudF9CeURhdGUkUmVjb3Zlcl9DYXNlcykgLT4gZGF0YQpkb24gPC0geHRzKHggPSBkYXRhLCBvcmRlci5ieSA9IGRhdGEkbykKbmFtZXMoZG9uKVsyXSA9ICJDb25maXJtZWQgQ2FzZXMiCm5hbWVzKGRvbilbM10gPSAiRGVhdGggQ2FzZXMiCm5hbWVzKGRvbilbNF0gPSAiUmVjb3ZlcmVkIENhc2VzIgpgYGAKCmBgYHtyfQojIEZpbmFsbHkgdGhlIHBsb3QKcCA8LSBkeWdyYXBoKGRvbikgJT4lCiAgZHlPcHRpb25zKGxhYmVsc1VUQyA9IFRSVUUsIGZpbGxHcmFwaD1UUlVFLCBmaWxsQWxwaGE9MC4xLCBkcmF3R3JpZCA9IEZBTFNFLCBjb2xvcnMgPSBSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoMywgIlNldDEiKSkgJT4lCiAgZHlSYW5nZVNlbGVjdG9yKCkgJT4lCiAgZHlDcm9zc2hhaXIoZGlyZWN0aW9uID0gInZlcnRpY2FsIikgJT4lCiAgZHlIaWdobGlnaHQoaGlnaGxpZ2h0Q2lyY2xlU2l6ZSA9IDUsIGhpZ2hsaWdodFNlcmllc0JhY2tncm91bmRBbHBoYSA9IDAuMiwgaGlkZU9uTW91c2VPdXQgPSBGQUxTRSkgICU+JQogIGR5Um9sbGVyKHJvbGxQZXJpb2QgPSAxKQpwCmBgYApGcm9tIHRoZSBncmFwaCB3ZSBjYW4gc2VlIHRoYXQgdGhlcmUncyBiZWVuIGEgZ2xvYmFsIHNoYXJwIGluY3JlYXNlIGluIHRoZSBudW1iZXIgb2YgY2FzZXMgYWZ0ZXIgTWFyY2ggMTIuIFRoZSBncmFwaCBtaWdodCBwb3NzaWJseSBiZSBmb2xsb3dpbmcgYW4gZXhwb25lbnRpYWwgY3VydmUsIGJ1dCBoYXZlIHlldCB0byBoaXQgdGhlIHN1cmdlLiBXZSBhbHNvIHNlZSB0aGF0IHRoZSBudW1iZXIgb2YgY29uZmlybWVkIGNhc2VzIGFyZSBncm93aW5nIGF0IGEgZmFzdGVyIHJhdGUgdGhhbiBSZWNvdmVyZWQgYW5kIERlYXRoIENhc2VzLgoKCiMjIyBkLiBEb2VzIENsaW1hdGUgaGF2ZSBhbiBlZmZlY3Qgb24gdGhlIENPVklELTE5IHZpcnVzPwogIEZvciB0aGUgcHVycG9zZSBvZiB0aGUgYW5hbHlzaXMgd2Ugd2FudCB0byBzZWUgaWYgdGhlIHRlbXBlcmF0dXJlIGhhcyBhbnkgYWZmZWN0IG9uIHRoZSBDT1ZJRC0xOSB2aXJ1cy4gRnJvbSBxdWVzdGlvbiAoYSkgd2UgZm91bmQgb3V0IHRoYXQgdGhlIGNvdW50cmllcyB3aXRoIHRoZSBtb3N0IG51bWJlciBvZiBjb25maXJtZWQgY2FzZXMgaXMgQ2hpbmEgYW5kIEl0YWx5LiBXZSBzdGFydCBieSB0YWtpbmcgdGhlIG1lYW4gb2YgdGhlIHR3byB0ZW1wZXJhdHVyZXMgYXMgYSBiZW5jaCBtYXJrLiAKYGBge3J9CmJlbmNoX3RlbXAgPSBjKGRmbWF4JGBBdmVyYWdlIFRlbXByYXR1cmVgWzFdLGRmbWF4JGBBdmVyYWdlIFRlbXByYXR1cmVgWzJdKQptdSA9IG1lYW4oYmVuY2hfdGVtcCkKbXUKYGBgCk91ciBtdSBoZXJlIGlzIDExLjYgd2hpY2ggc2lnbmlmaWVzIHRoYXQgdGhpcyB0ZW1wZXJhdHVyZSBpcyBpZGVhbCBmb3IgdGhlIHZpcnVzIHRvIHN1c3RhaW4uIAoKTm93IGxldHMgc3BsaXQgb3VyIGRhdGFzZXQgaW50byAzIGNhdGVnb3JpZXMgYnkgdGhlaXIgbnVtYmVyIG9mIGNvbmZpcm1lZCBjYXNlcy4gV2hpY2ggYXJlIGFzIGZvbGxvd3M6CjEuIFNtYWxsOiBDb25maXJtZWQgY2FzZXMgYmV0d2VlbiAwIGFuZCAyMDAKMi4gTWVkaXVtOiBDb25maXJtZWQgY2FzZXMgYmV0d2VlbiAyMDAgYW5kIDIwMDAwCjMuIExhcmdlOiBDb25maXJtZWQgY2FzZXMgYXJlIGxhcmdlciB0aGFuIDIwMDAwCgoKYGBge3J9CmRmICU+JSAKICBmaWx0ZXIoZGYkYENvbmZpcm1lZCBDYXNlc2AgIDwgMjAwKSAtPiBzbWFsbAoKaGVhZChzbWFsbCkKYGBgCgpgYGB7cn0KZGYgJT4lIAogIGZpbHRlcihkZiRgQ29uZmlybWVkIENhc2VzYCAgPDUwMDAgLGRmJGBDb25maXJtZWQgQ2FzZXNgICA+MjAwICkgLT4gbWVkaXVtCgpoZWFkKG1lZGl1bSkKYGBgCgoKYGBge3J9CmRmICU+JSAKICBmaWx0ZXIoZGYkYENvbmZpcm1lZCBDYXNlc2AgID49IDUwMDApIC0+IGxhcmdlCgpoZWFkKGxhcmdlKQpgYGAKCgoKClRoaXMgaXMgYSBzY2F0dGVyIHBsb3Qgb2YgYWxsIG9mIG91ciBzZXBhcmF0ZWQgZGF0YSBzZXRzIApgYGB7cn0KZ2dwbG90KHNtYWxsLCBhZXMoeSA9IGBDb25maXJtZWQgQ2FzZXNgLCB4ID0gYEF2ZXJhZ2UgVGVtcHJhdHVyZWApKSArIGdlb21fcG9pbnQoKQpgYGAKCioqU21hbGwgR3JhcGg6KiogV2UgY2FuIHNlZSB0aGF0IHRoZSBudW1iZXIgb2YgY29uZmlybWVkIGNhc2VzIGhhcyBhIGhpZ2ggZGVuc2l0eSB3aGVuIHRoZSB0ZW1wZXJhdHVyZSBpcyBhYm92ZSAxMCBkZWdyZWVzIGNlbHNpdXMuIFNvIHdlIGNhbiBzcGVjdWxhdGUgdGhhdCB0aGUgdGVtcGVyYXR1cmUgbWlnaHQgcGxheSBhbiBpbXBvcnRhbnQgcm9sZSB3aGVuIHRoZSBudW1iZXIgb2YgY29uZmlybWVkIGNhc2VzIGlzIGJldHdlZW4gMCBhbmQgMjAwLgoKCgpgYGB7cn0KZ2dwbG90KG1lZGl1bSwgYWVzKHkgPSBgQ29uZmlybWVkIENhc2VzYCwgeCA9IGBBdmVyYWdlIFRlbXByYXR1cmVgKSkgKyBnZW9tX3BvaW50KCkKYGBgCgoqKk1lZGl1bSBHcmFwaDoqKiBXZSBjYW4gc2VlIHRoYXQgdGhlIG51bWJlciBvZiBjb25maXJtZWQgY2FzZXMgYXJlIG1vc3RseSBpbiBiZXR3ZWVuIDUwMCBhbmQgNzUwLiBXZSBhbHNvIHNlZSB0aGF0IGZvciB0aG9zZSBjYXNlcywgdGhlIHRlbXBlcmF0dXJlIGlzIGV2ZW5seSBzcHJlYWQgb3V0IG1lYW5pbmcgdGhhdCBvbmNlIHRoZXJlIGlzIGEgY2VydGFpbiB0aHJlc2hvbGQgb2YgcGVvcGxlIHdobyBoYXZlIGJlZW4gYWZmZWN0ZWQsIHRoZSB0ZW1wZXJhdHVyZSBtYXkgbm8gbG9uZ2VyIHBsYXkgYW4gZWZmZWN0aXZlIHBhcnQgaW4gc2hvcnRlbmluZyB0aGUgdmlydXMgbGlmZXNwYW4gZW5vdWdoIHRvIG1ha2UgYSBkaWZmZXJlbmNlLiBXZSBjYW4gc2VlIHRoYXQgZm9yIHRoZSBudW1iZXIgb2YgY2FzZXMgYmV0d2VlbiA3NTAgYW5kIDUwMDAgaGF2ZSBhIGxvd2VyIGF2ZXJhZ2UgdGVtcGVyYXR1cmUsIGFib3V0IDE1IGRlZ3JlZXMgY2Vsc2l1cyBhbmQgYmVsb3cuCgoKCmBgYHtyfQpnZ3Bsb3QobGFyZ2UsIGFlcyh5ID0gYENvbmZpcm1lZCBDYXNlc2AsIHggPSBgQXZlcmFnZSBUZW1wcmF0dXJlYCkpICtnZW9tX3BvaW50KCkKYGBgCgoqKkxhcmdlIEdyYXBoOioqIFdlIGNhbiBzZWUgdGhhdCBhbG1vc3QgYWxsIG9mIG91ciBkYXRhIHBvaW50cyBsaWUgYmVsb3cgMTUgZGVncmVlcyBjZWxzaXVzLiBBbHNvIHRoZXJlIGlzIG9ubHkgb25lIGRhdGEgcG9pbnQgd2hpY2ggaXMgYWJvdmUgNDAwMDAgY29uZmlybWVkIGNhc2VzLiAoVGhpcyBpcyBDaGluYSBmcm9tIG91ciBwcmV2aW91cyBFREEpLiAKCkxldHMgc3RhcnQgYnkgY2hlY2tpbmcgaWYgdGhlcmUgaXMgYW55IGNvcnJlbGF0aW9uIGJldHdlZW4gb3VyIHZhcmlhYmxlIAoKYGBge3J9CmNvci50ZXN0KHNtYWxsJGBBdmVyYWdlIFRlbXByYXR1cmVgLCBzbWFsbCRgQ29uZmlybWVkIENhc2VzYCkKYGBgCgoKCgpgYGB7cn0Kc21hbGwuMSA8LSBsbShzbWFsbCRgQXZlcmFnZSBUZW1wcmF0dXJlYCB+IHNtYWxsJGBDb25maXJtZWQgQ2FzZXNgLCBkYXRhID0gc21hbGwpCnN1bW1hcnkoc21hbGwuMSkKYGBgCmBgYHtyfQptZWRpdW0uMSA8LSBsbShtZWRpdW0kYEF2ZXJhZ2UgVGVtcHJhdHVyZWAgfiBtZWRpdW0kYENvbmZpcm1lZCBDYXNlc2AsIGRhdGEgPSBtZWRpdW0pCnN1bW1hcnkobWVkaXVtLjEpCmBgYAoKYGBge3J9CmxhcmdlLjEgPC0gbG0obGFyZ2UkYEF2ZXJhZ2UgVGVtcHJhdHVyZWAgfiBsYXJnZSRgQ29uZmlybWVkIENhc2VzYCwgZGF0YSA9IGxhcmdlKQpzdW1tYXJ5KGxhcmdlLjEpCmBgYApBZnRlciBydW5uaW5nIGEgbGluZWFyIHJlZ3Jlc3Npb24gb24gYWxsIDMgZGF0YXNldHMsIHdlIGNhbiBzZWUgdGhhdCB0aGUgcC12YWx1ZSBpbiBhbGwgMyBzZXRzIGFyZSBhbGwgYWJvdmUgMC4wNS4gVGhpcyBtZWFucyB0aGF0IENvbmZpcm1lZCBDYXNlcyBmb3IgYWxsIDMgc2V0cyBpcyBub3Qgc2lnbmlmaWNhbnQuIFdlIGFsc28gaGF2ZSBhbiByLXNxdWFyZWQgdmFsdWVzIHdoaWNoIGFyZSB2ZXJ5IGxvdywgbWVhbmluZyB0aGF0IHRlbXBlcmF0dXJlIGRvZXMgbm90IHRlbGwgdXMgbXVjaCBhYm91dCBpdHMgY29ycmVsYXRpb24gd2l0aCBjb25maXJtZWQgY2FzZXMgYWNyb3NzIGFsbCAzIGRhdGFzZXRzLgoKQWZ0ZXIgbG9va2luZyBhdCBvdXIgZGF0YXNldHMsIG91ciBncmFwaHMgYW5kIHJ1bm5pbmcgbGluZWFyIHJlZ3Jlc3Npb25zIG9uIG91ciBzZXRzLCB3ZSBjYW5ub3QgY29uZmlkZW50bHkgc2F5IHRoYXQgdGhlcmUgaXMgYSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRlbXBlcmF0dXJlIGFuZCB0aGUgYW1vdW50IG9mIGNvbmZpcm1lZCBjYXNlcy4gRnJvbSB0aGUgc21hbGwgZGF0YXNldCwgaXQgaW5pdGlhbGx5IG1heSBoYXZlIHNlZW1lZCB0aGF0IHRoZXJlIGlzIGEgcmVsYXRpb24gYmV0d2VlbiBoaWdoZXIgdGVtcGVyYXR1cmVzIGFuZCBjb25maXJtZWQgY2FzZXMgYnV0IGFzIHdlIHdlbnQgdG8gdGhlIG1lZGl1bSBhbmQgbGFyZ2UgZGF0YXNldHMsIHdlIHdlcmUgdW5hYmxlIHRvIGNvbnRpbnVlIG9uIHdpdGggdGhlIGFzc3VtcHRpb24uIFRoaXMgbWFrZXMgc2Vuc2UsIGFzIG91ciBzbWFsbCBkYXRhIHNldHMgaGF2ZSBhIHNtYWxsZXIgbnVtYmVycyBvZiBjb25maXJtZWQgY2FzZXMgc28gd2UgbWF5IG5vdCBoYXZlIGVub3VnaCBldmlkZW5jZSBpbiB0aGlzIHNldCBpbiBvcmRlciB0byBtYWtlIHByb3BlciBhc3N1bXB0aW9ucy4gT3ZlcmFsbCwgYmFzZWQgb24gb3VyIGV2aWRlbmNlIGFuZCBmaW5kaW5ncywgd2UgY2Fubm90IHNheSB3aXRoIGNvbmZpZGVuY2UgdGhhdCB0aGVyZSBpcyBhIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRlbXBlcmF0dXJlIGFuZCB0aGUgc3ByZWFkIG9mIHRoZSB2aXJ1cy4gT2YgY291cnNlIHNvbWV0aGluZyB0byBub3RlIGlzIHRoYXQgdGhpcyB2aXJ1cyBiZWdhbiB0byBzcHJlYWQgZHVyaW5nIHRoZSBjb29sZXIgbW9udGhzIG9mIHRoZSBtb3N0IGVmZmVjdGVkIGNvdW50cmllcywgc28gaXQgaGFyZCB0byBhbHNvIHRlbGwgaWYgb3VyIGNvbmNsdXNpb25zIGNvdWxkIGhhdmUgYmVlbiBkaWZmZXJlbnQgaWYgdGhpcyB3ZXJlIGhhcHBlbmluZyBhdCBhIGRpZmZlcmVudCB0aW1lIGxpbmUuIAoKCgojIyMgZS4gRG9lcyBQb3B1bGF0aW9uIGhhdmUgYW4gZWZmZWN0IG9uIENPVklELTE5IHZpcnVzID8KClNpbmNlIG91ciBkYXRhc2V0IGhhcyBtdWx0aXBsZSBmaWVsZHMgYW5kIGZvciB0aGUgcHVycG9zZSBvZiB0aGlzIHF1ZXN0dW9uIHdlIHdpbGwgb25seSBiZSBjb25jZXJuZWQgd2l0aCBjb25maXJtZWQgY2FzZWEgYW5kIHRvdGFsIHBvcHVsYXRpb24gYW5kIGRyb3AgZXZlcnl0aGluZyBlbHNlLgoKCmBgYHtyfQpkZiAlPiUgc2VsZWN0KENvdW50cnksYENvbmZpcm1lZCBDYXNlc2AsIGBUb3RhbCBQb3B1bGF0aW9uYCkgJT4lIAogIGFycmFuZ2UoZGVzYyhgVG90YWwgUG9wdWxhdGlvbmApKSAtPiBkZnBvcApoZWFkKGRmcG9wKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZGZwb3AsIGFlcyh4ID0gYFRvdGFsIFBvcHVsYXRpb25gLCB5ID0gYENvbmZpcm1lZCBDYXNlc2ApKSArZ2VvbV9wb2ludCgpCmBgYAoKCmBgYHtyfQpkZnBvcCAlPiUgCiAgZmlsdGVyKGRmcG9wJGBUb3RhbCBQb3B1bGF0aW9uYCAgPCAxMDAwMDAwKSAtPiBzbWFsbHBvcApoZWFkKHNtYWxscG9wKQpgYGAKCmBgYHtyfQpkZnBvcCAlPiUgCiAgZmlsdGVyKGRmcG9wJGBUb3RhbCBQb3B1bGF0aW9uYCAgPiAxMDAwMDAwLCBkZnBvcCRgVG90YWwgUG9wdWxhdGlvbmAgPCA1MDAwMDAwMCApIC0+IG1lZGl1bXBvcAoKaGVhZChtZWRpdW1wb3ApCmBgYAoKCmBgYHtyfQpkZnBvcCAlPiUgCiAgZmlsdGVyKGRmcG9wJGBUb3RhbCBQb3B1bGF0aW9uYCAgPiA1MDAwMDAwMCApIC0+IGxhcmdlcG9wCmhlYWQobGFyZ2Vwb3ApCmBgYAoKCmBgYHtyfQpnZ3Bsb3Qoc21hbGxwb3AsIGFlcyh4ID0gYFRvdGFsIFBvcHVsYXRpb25gLCB5ID0gYENvbmZpcm1lZCBDYXNlc2ApKSArZ2VvbV9wb2ludCgpCmBgYAoKYGBge3J9CmdncGxvdChtZWRpdW1wb3AsIGFlcyh4ID0gYFRvdGFsIFBvcHVsYXRpb25gLCB5ID0gYENvbmZpcm1lZCBDYXNlc2ApKSArZ2VvbV9wb2ludCgpCmBgYAoKYGBge3J9CmdncGxvdChsYXJnZXBvcCwgYWVzKHggPSBgVG90YWwgUG9wdWxhdGlvbmAsIHkgPSBgQ29uZmlybWVkIENhc2VzYCkpICtnZW9tX3BvaW50KCkKYGBgCgoKRnJvbSBhbGwgdGhyZWUgZ3JvdXBzLCB3ZSBjYW4gc2VlIHRoYXQgdGhlIG1ham9yaXR5IG9mIGNvbmZpcm1lZCBjYXNlcyBhcmUgcmVsYXRpdmVseSBsb3cgd2l0aCByZXNwZWN0IHRvIHRoZSBjb3VudHJpZXMgdG90YWwgcG9wdWxhdGlvbi4gVGhlcmUgYXJlIGEgY291cGxlIG91dGxpZXJzIGluIGVhY2ggZGF0YXNldCBidXQgdGhpcyBjYW4gYmUgZXhwZWN0ZWQuIFRoZXJlIGlzIG5vIHRyZW5kIHRvIHNob3cgdGhhdCBhIGhpZ2hlciBwb3B1bGF0aW9uIGVxdWF0ZXMgdG8gbW9yZSBjb25maXJtZWQgY2FzZXMuCldlIGNhbiBydW4gYSBsaW5lYXIgcmVncmVzc2lvbiBvbmNlIGFnYWluIHRvIHNlZSBpZiB0aGVyZSBpcyBhIHRyZW5kIHRoYXQgd2UgY2Fubm90IGRldGVybWluZSBmcm9tIG91ciBncmFwaHMuCgpgYGB7cn0Kc21hbGxwb3AuMSA8LSBsbShzbWFsbHBvcCRgQ29uZmlybWVkIENhc2VzYCB+IHNtYWxscG9wJGBUb3RhbCBQb3B1bGF0aW9uYCwgZGF0YSA9IHNtYWxscG9wKQpzdW1tYXJ5KHNtYWxscG9wLjEpCmBgYApgYGB7cn0KbWVkaXVtcG9wLjEgPC0gbG0obWVkaXVtcG9wJGBDb25maXJtZWQgQ2FzZXNgIH4gbWVkaXVtcG9wJGBUb3RhbCBQb3B1bGF0aW9uYCwgZGF0YSA9IG1lZGl1bXBvcCkKc3VtbWFyeShtZWRpdW1wb3AuMSkKYGBgCmBgYHtyfQpsYXJnZXBvcC4xIDwtIGxtKGxhcmdlcG9wJGBDb25maXJtZWQgQ2FzZXNgIH4gbGFyZ2Vwb3AkYFRvdGFsIFBvcHVsYXRpb25gLCBkYXRhID0gbGFyZ2Vwb3ApCnN1bW1hcnkobGFyZ2Vwb3AuMSkKYGBgCkJhc2VkIG9uIG91ciBkYXRhIGFib3ZlLCB3ZSBjYW4gY29uY2x1ZGUgdGhhdCBhIGhpZ2hlciBwb3B1bGF0aW9uIGRvZXMgbm90IG5lY2Vzc2FyaWx5IG1lYW4gbW9yZSBjb25maXJtZWQgY2FzZXMuIFRoaXMgY2FuIGJlIGNsZWFybHkgc2hvd24gYnkgbG9va2luZyBhdCBvdXIgZGZwb3AgZGF0YWZyYW1lLiBXZSBjYW4gc2VlIHRoYXQgSW5kaWEJaGFzIGEgdG90YWwgb2YgMTQyIGNvbmZpcm1lZCBjYXNlcyB3aXRoIGEgdG90YWwgcG9wdWxhdGlvbiBvZiAxMzUyNjE3MzI4LCB3aGVyZSBhcyBJdGFseSBoYXMgYSB0b3RhbCBvZiAzMTUwNiBjb25maXJtZWQgY2FzZXMgd2l0aCBhIHRvdGFsIHBvcHVsYXRpb24gb2YgNjA0MzEyODMuIAoKRGVzcGl0ZSBiZWluZyBoYWxmIHRoZSBwb3B1bGF0aW9uIG9mIEluZGlhLCB0aGVyZSBpcyB0ZW5zIG9mIHRob3VzYW5kcyBvZiBtb3JlIGNvbmZpcm1lZCBjYXNlcy4gVGhlcmUgYXJlIGEgbnVtYmVyIG9mIGZhY3RvcnMgd2hpY2ggc2lnbmlmaWNhbnRseSBjb250cmlidXRlIHRoZSBhbW91bnQgb2YgY29uZmlybWVkIGNhc2VzLCBob3dldmVyIGhhdmluZyBtb3JlIHBlb3BsZSBpbiBhIGNvdW50cnkgZG9lcyBub3QgYWx3YXlzIG1lYW4gbW9yZSBwZW9wbGUgaGF2ZSB0aGUgdmlydXMuIFRoZXJlIGlzIGEgYW4gaW5jcmVhc2VkIGxpa2VsaWhvb2Qgb2YgdGhlIHZpcnVzIHNwcmVhZGluZyBmYXN0ZXIsIGlmIHByZWNhdXRpb25zIGFyZSBub3QgdGFrZW4gYnkgdGhlIGdvdmVybm1lbnQgYW5kIHBlb3BsZSwgc3VjaCBpcyB0aGUgY2FzZSBpbiBJdGFseS4gSG93ZXZlciwgdGhlcmUgYXJlIG1hbnkgYWN0aW9ucyBpbmRpdmlkdWFscyBjYW4gdGFrZSBpbiBvcmRlciB0byBzdG9wIHRoZSBzcHJlYWQgb2YgdGhlIHZpcnVzIHNvIHF1aWNrbHksIHN1Y2ggYXMgc29jaWFsIGRpc3RhbmNpbmcsIHNlbGYgcXVhcmFudGluaW5nIGFuZCBwcmFjdGljaW5nIGdvb2QgaHlnaWVuZSBhbmQgY2xlYW5saW5lc3MuIEEgaGlnaCBwb3B1bGF0aW9uIGRvZXNuJ3QgYWx3YXlzIG1lYW4gYSBoaWdoIG51bWJlciBvZiBjb25maXJtZWQgY2FzZXMsIGJ1dCBtb3JlIG9mIGEgcmlzayBvZiBtb3JlIGluZGl2aWR1YWxzIHRvIGNhdGNoIHRoZSB2aXJ1cy4gCgojIyMgQ29uY2x1c2lvbgoKSXQgc2VlbXMgdGhhdCB0aGUgZ2xvYmFsIGZlYXIgb2YgQ09WSUQtMTkgYW5kIGl0J3MgcmFwaWQgaW5jcmVhc2UgaXMgZHVlIHRvIHRoZSBzYW1lIHByb2JsZW0gd2UgZHJldyBmcm9tIGRvaW5nIHRoaXMgcHJvamVjdDsgaXQgaXMgZGlmZmljdWx0IHRvIGNvbnRhaW4gYW4gdW5rbm93biBkaXNlYXNlIGZyb20gc3ByZWFkaW5nIHdoZW4gd2UgY2Fubm90IHByZWRpY3QgaG93IHdlbGwgaXQgdGhyaXZlcyBpbiBpdHMgZW52aXJvbm1lbnQuIE91ciBhbmFseXNpcyBjb3VsZCBub3QgaGVscCB1cyBjb25jbHVkZSBpZiB0ZW1wZXJhdHVyZSBwbGF5ZWQgYW4gZWZmZWN0aXZlIHJvbGUgaW4gY29udHJvbGxpbmcgdGhlIGdyb3d0aCBvZiB0aGUgdmlydXMsIHdpdGggdGhlIG1vc3QgbnVtYmVyIG9mIGNhc2VzIGJlaW5nIGluIENoaW5hIGFuZCB0aGUgbGVhc3QgbnVtYmVyIG9mIGNhc2VzIGJlaW5nIGluIFJlcHVibGljIG9mIENvbmdvLiBBbHRob3VnaCB0aGVyZSBzZWVtZWQgdG8gYmUgc2lnbnMgb2YgdGhlIHZpcnVzIGJlaW5nIGxlc3MgZWZmZWN0aXZlIGluIGhvdHRlciBjbGltYXRlcyB3aGVuIGxvb2tpbmcgYXQgdGhlIGNvdW50cmllcyB3aXRoIHRoZSBsZWFzdCBudW1iZXIgb2YgY2FzdWFsdGllcywgYXMgdGhlIG51bWJlciBvZiB2aWN0aW1zIGdyZXcgZXhwb25lbnRpYWxseSwgdGVtcGVyYXR1cmUncyBlZmZlY3Qgb24gdGhlIGxpZmVzcGFuIG9mIHRoZSB2aXJ1cyBiZWNhbWUgcG9zc2libHkgaW5zaWduaWZpY2FudC4gQW5kIHNpbmNlIHRoaXMgZ3Jvd3RoIGhhcHBlbmVkIGR1cmluZyB0aGUgY29sZGVyIG1vbnRocyBvZiB0aGUgbW9zdCBlZmZlY3RlZCBjb3VudHJpZXMsIGl0IGlzIGV2ZW4gbW9yZSBkaWZmaWN1bHQgdG8gZHJhdyBhbnkgc3Ryb25nIGNvbmNsdXNpb25zLiBJdCBpcyBwb3NzaWJsZSB0aGF0IG9uIGEgZGlmZmVyZW50IHRpbWVsaW5lLCB0aGUgc2NlbmFyaW8gY291bGQgaGF2ZSBiZWVuIGNvbXBsZXRlbHkgZGlmZmVyZW50LCBhbmQgaXQgbWF5IGJlIHN0aWxsIHRvbyBlYXJseSB0byBkcmF3IGFuIG9waW5pb24uIEFzIHdlJ3ZlIHNlZW4gZnJvbSBvdXIgZmlyc3QgZ3JhcGgsIHdlIGhhdmUgeWV0IHRvIHJlYWNoIHdvcmxkd2lkZSBwZWFrLiBTaW1pbGFybHksIHdlIGNvdWxkIG5vdCBjb25jbHVkZSB0aGF0IHRoZSBwb3B1bGF0aW9uIHNpemUgb2YgYSBjb3VudHJ5IGhhZCBhIHBsYXkgaW4gaXRzIHNwcmVhZC4gVGhlcmUgbWF5IGJlIG90aGVyIGZhY3RvcnMgdGhhdCBjb21lIGludG8gcGxheSwgc3VjaCBhcyBhIGNvdW50cnkncyBhZ2UgZGlzdHJpYnV0aW9uLCB0cmF2ZWwgcmVzdHJpY3Rpb25zIGV0Yy4sIHRoYXQgY291bGQgZXhwbGFpbiBpdHMgY3VycmVudCBzdGF0ZS4gRnJvbSByZWNlbnQgZXZlbnRzIHdlIGRvIGtub3cgdGhhdCBpbiBnZW5lcmFsIHRoZSBtb3JlIHBlb3BsZSB0aGF0IGFyZSB0cmF2ZWxpbmcsIHRoZSBtb3JlIHRoZSB2aXJ1cyBpcyBhYmxlIHRvIHNwcmVhZCBieSBjb250YWN0LCB3aXRoIGl0IGJlaW5nIHNvbWV3aGF0IG9mIGEgZG9taW5vIGVmZmVjdC4gU28gb3VyIHNvbHV0aW9uIGhhcyBiZWVuIHRvIGdvIGludG8gcXVhcmFudGluZSBkZXNwaXRlIHdoYXRldmVyIGxldmVsIG9mIGNhc2VzIGluIHRoZSBhcmVhLiBIb3BlZnVsbHkgd2UgY2FuIHNvb24gZXhwZWN0IHRvIGdhdGhlciBlbm91Z2ggZGF0YSB0byBzdWNjZXNzZnVsbHkga25vdyBob3cgdG8gcmVkdWNlIGNhc3VhbHRpZXMgYXJpc2luZyBmcm9tIENPVklELTE5LiAKCgoKCgo=