This article is updated regularly.
All data presented are as of August 09, 2020, the last update was on August 10, 2020.

Introduction

The ongoing COVID-19 pandemic needs no explanation because the whole world is currently badly affected. The high infectivity of the SARS-CoV-2 pathogen and the potentially severe course of the disease are putting healthcare systems around the world to the test. However, to take the right steps to contain the pandemic, analyzing the number of cases is an essential step.

Many of us have now made it a habit to look at the current state of the case numbers at least once a day. The good availability of the data and new visualization technologies enable us to update graphs in the shortest possible time. Especially the data of the Johns Hopkins University and of the Robert Koch Institute are used intensively for reporting cases in Germany. On different platforms, for example Kaggle.com, a lot of data is available to everyone, in the hope that the large global community of data scientists will generate valuable information and predictions from the data.

We at StatSoft Europe GmbH offer a variety of R training courses and have also used the COVID-19 data to generate knowledge through visualization. We have used the Open-Source statistics software R and disclosed the code below to provide interested analysts with free help for visualizing data in R. We used the data from the Johns Hopkins University (downloaded from this Kaggle Link). The original data, as used in Kaggle, originated from the Git Repository of the Johns Hopkins University.

At this point we would like to take the opportunity to draw your attention to various support platforms for the Corona crisis. Perhaps you are affected yourself or know people who are particularly at risk and should stay at home for their own safety. In order to make life a little easier for those people, many support platforms have emerged, for example to provide free grocery shopping. This is a list of German aid platforms, where you can register as a person affected or as a potential helper. There you will also find links to Germany-wide help platforms. Please also remember that animal shelters in particular may have a large influx of pets during this time, whose owners are no longer able to take care of due to the illness. You can support the shelters by donating food or money.

Reommended literature

For data analysis and visualization we can warmly recommend the excellent books R for Data Science and Hands on Programming with R by Hadley Wickham and Garret Grolemund. Incidentally, both authors are also the authors of most packages that we need in this project.

The Current Situation

In this section we would like to use various graphics to show you how the COVID-19 pandemic is spreading throughout the world. The status of the data record is the August 09, 2020. In the next section below, we then show how we created these graphics using R.

Worldwide

The following figure shows the number of confirmed cases from all countries, divided into active cases, recoveries and deaths, which had registered at least 50000 confirmed cases on August 09, 2020.

The following figure shows the number of confirmed cases on a log scale by days after the 100th confirmed case was registered in each country. This way, it is possible to see how fast the outbreak developed in the respective countries. For this figure, only the countries which registered the most cases as of August 09, 2020 as well as South Korea were selected. South Korea is a special case, because their fast reaction and testing regime caused a strong decrease in further infections. By choosing the log scale, it is easier to compare the increase in cases irrespective of their order of magnitude.

China, Europe and USA

The following figure shows how the number of cases developed in China, Europe, the USA and all other countries (other). It becomes clear how quickly China managed to get the outbreak under control by taking very strict measures. About five weeks after the number of cases in China reached a plateau, the number of cases in Europe was already higher than in China. Just two more weeks later, there were five times as many cases in Europe as in China. The USA also showed a rapid increase, which follows Europe’s trend, delayed by about a week and a half. Europe and the USA are currently the new epicentres of the COVID-19 pandemic.

The following figure shows the daily increase in case numbers in China, Europe, the USA and in all other countries. This clearly shows how quickly the Chinese government reacted and took drastic measures, because the quarantine of Hubei already began on January 23, at a time when there were only 639 confirmed cases and 18 deaths in China. Nevertheless, despite the quarantine, the confirmed cases in China rose to 84668, including 4634 deaths (as of August 09, 2020). This makes it very clear that a strong restriction on travel and freedom of movement (however painful this may be) a) can effectively limit the spread, b) should be carried rather earlier than later, and c) may flatten the curve only after a certain delay.

In Europe on 04.04.2020 the highest daily increase in cases (54896) was recorded, in the USA the highest increase was 78310 cases on 16.07.2020. In China, on the other hand, the highest increase was only 15133 cases on 13.02.2020.

The following figure shows the total active cases of all defined regions for the respective period, the width of the colored band symbolizes the proportion of those diseases that occurred in the respective region. The spread began in China first, then continued in Europe and other countries and at the same time decreased again in China. Around two weeks after the outbreak in Europe, the disease spread to the United States. The United States is the country with the highest number of acute illnesses.

The following figure shows the confirmed cases, deaths and active cases for the different regions on August 09, 2020.

Europe

In the following figure we see the confirmed cases of all European countries in which at least 5000 confirmed cases were registered on 30.03.2020. The width of the colored band shows which proportion of confirmed cases states can be assigned to the respective state and how this proportion changes over time. This shows that Italy was the first country to register a high number of confirmed cases. A few weeks later, the number of infections also rose sharply in surrounding European countries. Most infections were recorded until August 09, 2020 in Italy, Spain and Germany and France.

In the following figure we see the deaths in all European countries where at least 5000 confirmed cases were registered on 30.03.2020. The number of deaths in Germany is still very small compared to the relatively high number of confirmed cases.

The following figure shows the number of recoveries within the different European countries. The number of recoveries is highest in Spain, Italy and Germany.

The following figure shows the course of active cases within the European countries.

Germany

The following figure shows the active cases, deaths and recoveries in Germany over time.

The following figure shows the daily percentage increase in confirmed cases in Germany. The highest daily increases (> 50%) were registered when there were fewer than 5000 cases in Germany. Subsequently, the daily increases were between 25 and 35% for 7 days and then decreased further to 10-17% for the following 8 days. Since March 29, 2020 daily increases have remained below the 10% limit.

This reduction in new infections in Germany since March 21, 2020 was very important, because otherwise the number of infections on March 30, 2020 would have been more than three times as the actual values. The following figure shows the actual development (light blue bars) of the case numbers in Germany. The red line simulates the number of cases that would have resulted if the daily increase of 25-35%, as it had taken place from 03/14/2020 - 03/20/2020, had continued. On March 30, 2020, 229108 infections would have occurred. In fact, only 66885 cases were registered due to this reduction.

The Code

In this section we will show you how we prepared the data to generate the figures shown above.

Loading Packages and Data

For this project we need the packages tidyverse andlubridate as well as scales, RColorBrewer and countrycode. These must have been installed beforehand.

library(tidyverse)
library(lubridate)
library(scales)
library(RColorBrewer)
library(countrycode)

Now we load the dataset (downloaded from this Kaggle Link) using the Function read_csv():

data <- read_csv("data/covid_19_data.csv")
Parsed with column specification:
cols(
  SNo = col_double(),
  ObservationDate = col_character(),
  `Province/State` = col_character(),
  `Country/Region` = col_character(),
  `Last Update` = col_character(),
  Confirmed = col_double(),
  Deaths = col_double(),
  Recovered = col_double()
)

Data Cleaning

Now we change the format of the variable ObservationDate into a valid form using lubridate::mdy() and save it as a new variable called Date using the function dplyr::mutate(). Then we group the data according to Date and Country/Region and sum up the confirmed cases (Confirmed) within each group using the summarize() function. This gives us the sums of all cases within a country and day. We do this mainly because, for example, all provinces of China or all states of the USA are available as individual entries, but we want to consider the case numbers for each whole country. Then we sort by the variable Date and Confirmed Cases to see which is the most current date of our data set and in which countries the highest number of cases was registered:

data <- data %>% 
  mutate(Date = mdy(ObservationDate)) %>% 
  group_by(Date, `Country/Region`) %>% 
  summarize(`Confirmed Cases` = sum(Confirmed),
            `Deaths` = sum(Deaths),
            `Recoveries` = sum(Recovered)) %>% 
  arrange(desc(Date), desc(`Confirmed Cases`))

head(data)

Later we would like to compare the progression in China with all of Europe. However, to do this, we need a list of European countries if we do not want to assign them manually because the variable continent does not exist in the data set. The package countrycode helps us here, which contains a list of all countries in the world and the associated country codes and continents. With the function countrycode() we create a new variable called continent. At the same time, we can take the opportunity to use the countrycode function to convert the English country names into German ones (only used in the German version of this article). We then call this variable Staat, which is the German word for country:

library(countrycode)
#data3
data <- data %>% 
  mutate(continent = countrycode(`Country/Region`,
                                 origin = "country.name",
                                 destination = "continent"),
         Staat = countrycode(`Country/Region`,
                             origin = "country.name",
                             destination = "country.name.de"))

Now we create another variable, which we call Region. In this variable we will define the regions Europe,China, United States and Others. We do this using the base::ifelse() function within the dplyr::mutate() function.

The following code results in full writing: If the variable continent equals Europe, then the variable Region should be set to Europe; otherwise it should be set to China if the variable Country/Region equals Mainland China; otherwise to USA if the variable Country/Region equals US; otherwise the variable Region should be set to Other.

We can now use the two variables (continent and Country/Region) to group the regions Europe, China, United States and Others:

#data4
data <- data %>% 
  mutate(Region = ifelse(continent == "Europe", "Europe",
                                ifelse(`Country/Region` == "Mainland China",
                                       "China",
                                       ifelse(`Country/Region` == "US",
                                              "USA",
                                              "Others"))))

Now we can take a look at our data set to check whether the new variables Region, continent and Staat were created correctly:

arrange(data, desc(Date), desc(`Confirmed Cases`))

Next we create the new variable Active Cases, which can be calculated from the other variables as follows:

data <- mutate(data, `Active Cases` = `Confirmed Cases` - `Deaths` - `Recoveries`)
data

The rough work is now complete and we can now use the data to generate the figures.

Cases Worldwide

For our first figure, we first have to filter the dataset according to the latest date and the number of confirmed cases of at least 50000. Then we use pivot_longer() to convert the table into the long form and create the variable Category and collect the number of cases in the variable Cases. Now the changed data record looks like this:

Countries_50000 <- data %>% 
  filter(Date == strftime(max(data$Date)),
         `Confirmed Cases` >= 50000) %>%  
  pivot_longer(cols = c(`Active Cases`, 
                        `Deaths`, 
                        `Recoveries`), 
               names_to = "Category", 
               values_to = "Cases")

head(Countries_50000)

Now we can create a bar graph with ggplot() and geom_col(). First we define the X-axis as state and the Y-axis as number of cases, then we flip the two axes with the command coord_flip(), so that the Y-axis becomes the X-axis and vice versa. Now the last step is to sort the bars according to the total number of confirmed cases. Since we have not converted the variable Confirmed Cases into the long form, this variable is still available to us and we can sort the bar chart with reorder() according to the variable Confirmed Cases.

Countries_50000$Category <- factor(Countries_50000$Category, levels = c('Active Cases', 'Recoveries', 'Deaths'))

g1 <- ggplot(data = Countries_50000) +
  geom_col(mapping = aes(x = reorder(`Country/Region`, 
                                     `Confirmed Cases`), 
                         y = `Cases`, 
                         fill = Category)) +
  coord_flip() +
  theme(legend.position = c(0.8, 0.3),
        legend.background = element_rect(fill=alpha(0.4)),
        plot.title = element_text(hjust = 0.5)) +
  scale_y_continuous(labels = number) +
  labs(x = "Country", 
       y = "Cases", 
       title = paste("Confirmed cases by country as of ", 
                     date_dataset,
                     sep=""))
g1

Creating the following figure is a bit tricky. First, we need to select all the cases from our original dataset and filter by those that have more than or equal to 100 cases. Then we group by Country/Region and use the summarize() function to calculate the minimum value of Confirmed Cases, which gives us only one observation per country (in most cases). These values are then set to 0 and stored in the new variable Days after 100th case using the mutate() function. We call this object worldwide_log_1.

worldwide_log_1 <- data %>%
  arrange(Date) %>% 
  filter(`Confirmed Cases` >=100) %>% 
  group_by(`Country/Region`) %>% 
  summarize(`Confirmed Cases` = min(`Confirmed Cases`)) %>% 
  mutate(`Days after 100th case` = 0)

The next step is to join this summarized dataset back to the original dataset, calling the new object worldwide_log_2. Then we filter again by those observations where Confirmed Cases are more than or equal to 100, ungroup (important for cumsum() later) the dataset and then arrange by Date. By joining the datasets together, only one row of each country has the value 0, the others have NA. These NAs are then replaced by 1s. Then, after grouping by Country/Region again, we can calculate the cumulative sum of the Days after 100th case and arrange the data by Country/Region and Date.

worldwide_log_2 <- full_join(data,worldwide_log_1) %>% 
  filter(`Confirmed Cases` >= 100) %>% 
  ungroup(worldwide_log_2) %>% 
  arrange(Date) %>%  
  mutate(`Days after 100th case` = ifelse(is.na(`Days after 100th case`) == T, 1, 0)) %>% 
  group_by(`Country/Region`) %>% 
  mutate(`Days after 100th case` = cumsum(`Days after 100th case`)) %>% 
  arrange(`Country/Region`, Date)

Now we calculate the number of cases which would have occurred at a Doubling Time of 2, 7, 14, and 60 days. In order to do this, we create a new dataset (DBLT) and define the variable Days after 100th case as ranging from 1 to 60. Now we create four new variables (2 Days, 7 Days etc.) and calculate the cases that would have occured with the respective doubling time. Now we transform this dataset using pivot_longer(). In the end, we filter this dataset just in order to not make the comparison lines too long.

DBLT <- tibble(`Days after 100th case` = seq(1:60)) %>% 
  mutate(`2 Days` = 100*(2^(1/2))^`Days after 100th case`,
         `7 Days` = 100*(2^(1/7))^`Days after 100th case`,
         `14 Days` = 100*(2^(1/14))^`Days after 100th case`,
         `60 Days` = 100*(2^(1/60))^`Days after 100th case`,) %>% 
  pivot_longer(cols = c("2 Days", "7 Days", "14 Days", "60 Days"), names_to = "Doubling Time", values_to = "Cases") %>% 
  mutate(`Doubling Time` = as_factor(`Doubling Time`)) %>% 
  filter(Cases <= 4000000 & `Days after 100th case` <= 50)

Now we can plot the Confirmed Cases against Days after 100th case using ggplot(). By choosing scale_y_continuous(trans="log10"), we can display the Y-axis on a log scale. This makes it easier to compare the growth of cases among different countries. Then we add another line plot which contains the calculated values from the step above and set the linetype to Doubling Time.

log_cases <- filter(worldwide_log_2, `Country/Region` %in% (c("Mainland China", "Germany", "Spain", "France", "Italy", "US", "Iran", "South Korea")))

g2 <- ggplot(data = log_cases) +
  geom_line(mapping = aes(x = `Days after 100th case`, y = `Confirmed Cases`, color = `Country/Region`), size = 1.5) +
  geom_line(data = DBLT, mapping = aes(x = `Days after 100th case`, y = Cases, linetype = `Doubling Time`), color = "royalblue4") +
  scale_y_continuous(trans='log10', labels = number) +
  labs(title = paste("Confirmed cases (log scale) by days after 100th case as of ", 
                     date_dataset,
                     sep="")) +
  theme(legend.position = c(0.85, 0.5),
        legend.background = element_rect(fill=alpha(0.4)),
        plot.title = element_text(hjust = 0.5)) +
    scale_color_brewer(palette="Set3") +
  guides(linetype = guide_legend(order = 1), color = guide_legend(order = 2))
g2

Cases by Region

Now we group our data set by Date and Region and add up the variables Confirmed Cases, Deaths and Recoveries with sum() to get the sum of all cases within each day and within each region. All missing values are then removed using filter() and !Is.na(). Now we group by Region and use mutate()to create the variable Increase in Confirmed Cases by Region with the lag() function and the variable Active Cases by Region:

Regions <- data %>% 
  group_by(Date, Region) %>% 
  summarize(`Confirmed Cases by Region` = sum(`Confirmed Cases`),
            `Deaths by Region` = sum(`Deaths`),
            `Recoveries by Region` = sum(`Recoveries`)) %>% 
  filter(!is.na(Region)) %>% 
  group_by(Region) %>% 
  mutate(`Increase in Confirmed Cases by Region` = `Confirmed Cases by Region` - lag(`Confirmed Cases by Region`),
         `Active Cases by Region` = `Confirmed Cases by Region` - `Recoveries by Region` - `Deaths by Region`)

head(arrange(Regions, desc(Date), desc(`Confirmed Cases by Region`)))

Now we can plot the confirmed cases for China, Europe, US and others with ggplot(). The X-axis is Date and the Y-axis is the sum of the confirmed cases per region (Confirmed Cases by Region). Note: The backticks are necessary if variable names have special characters (here spaces in the variable name Confirmed Cases by Region).

g3a <- ggplot(data = Regions) +
  geom_line(mapping = aes(x = Date, 
                          y = `Confirmed Cases by Region`, 
                          color = Region), 
            size = 1) +
  labs(x = "Date", 
       y = "Confirmed Cases by Region",
       title = paste("Confirmed Cases by Region as of ",
                     date_dataset,
                     sep="")) +
  scale_y_continuous(labels = number) +
  scale_x_date(date_breaks = "2 week", date_labels = "%d.%m") +
  theme(legend.position = c(0.13, 0.6),
        legend.background = element_rect(fill=alpha(0.4)),
        plot.title = element_text(hjust = 0.5))
g3a

If we also want to display the other two variables (Deaths by Region and Recoveries by Region) in the same plot, we first have to transfer the data record to the long form with tidyr::pivot_longer(). Here we create two new variables, which we call Category and Cases. The Category variable contains information as to whether it is a confirmed case, a death or a recovered case. The variable Cases contains the count values that were previously among the three combined variables.

Regions_long <- Regions %>% 
  pivot_longer(cols = c(`Confirmed Cases by Region`,
                        `Deaths by Region`,
                        `Recoveries by Region`,
                        `Increase in Confirmed Cases by Region`,
                        `Active Cases by Region`),
               names_to = "Category",
               values_to = "Cases")
head(Regions_long)

Then we add the new variable Category as aesthetics in ggplot() (here as linetype). Thus the line type changes depending on the variable Category. We select the newly created variable Cases as the Y axis, which contains the counted values. Since the function countrycode() used above could not assign names to all countries and thus generated some NAs, we remove them in the same step with drop_na(), otherwise they would also be shown in the figure (alternatively we could also assign the missing countries manually to the correct continent). With the argument scale_x_date we change the date format of the X-axis and set it to weekly steps, with the argument theme() we position the legend in the plot and set the background transparent.

g3 <- ggplot(data = filter(Regions_long, 
                     Category %in% c("Confirmed Cases by Region",
                                      "Deaths by Region",
                                      "Recoveries by Region"))) +
  geom_line(mapping = aes(x = Date, 
                          y = `Cases`, 
                          color = Region, 
                          linetype = Category), 
            size = 1)+
  labs(x = "Date", 
       y = "Cases", 
       title = paste("Cases by Category and Region as of ", 
                                               date_dataset,
                                               sep="")) +
   scale_y_continuous(labels = number) +
  scale_x_date(date_breaks = "2 week", 
               date_labels = "%d.%m") +
  theme(legend.position = c(0.2, 0.6),
        legend.background = element_rect(fill=alpha(0.4)),
        plot.title = element_text(hjust = 0.5))
g3

Now we can plot the daily increase in the number of cases. In addition, we use geom_vline() to add markers and geom_label() to place some labels where events have taken place that could affect the spread of the disease, namely the times at which different countries started the quarantine measures. China, for example, started the Hubei lockdown on January 23, at a time when there were only 639 confirmed cases and 18 deaths in China. Italy started the curfew on March 9th.

g4 <- ggplot(data = Regions) +
  geom_line(mapping = aes(x = Date, 
                          y = `Increase in Confirmed Cases by Region`, 
                          color = Region), size = 1) +
  geom_vline(xintercept = as.Date("2020-01-23")) +
  geom_label(label="Hubei \n quarantine", y=15000, x=as.Date("2020-01-24")+1)+
  geom_vline(xintercept = as.Date("2020-03-09"), color = "black") +
  geom_label(label="Italy \n lockdown", y=18000, x=as.Date("2020-03-09")) +
  scale_x_date(date_breaks = "2 week", date_labels = "%d.%m") +  
  theme(legend.position = c(0.2, 0.8),
        legend.background = element_rect(fill=alpha(0.4)),
        plot.title = element_text(hjust = 0.5)) +
  labs(title = paste("Daily Increase in Cases as of ", 
                                               date_dataset,
                                               sep=""))
g4

Now we want to plot the active cases as an area plot over time. An area plot is created with geom_area within ggplot().

g5 <- ggplot(data = Regions) +
  geom_area(mapping = aes(x = Date, 
                          y = `Active Cases by Region`, 
                          fill = Region), 
            size = 1) +
  labs(x = "Date", 
       y = "Active COVID-19 Cases", 
       title = paste("Active Cases by Region as of ",
                    date_dataset,
                    sep="")) +
  scale_y_continuous(labels = number) +
  scale_x_date(date_breaks = "2 week", 
               date_labels = "%d.%m") +
  theme(legend.position = c(0.1, 0.8),
        legend.background = element_rect(fill=alpha(0.4)),
        plot.title = element_text(hjust = 0.5)) +
  scale_fill_brewer(palette="Set3")
g5

We can also generate a bar chart in ggplot() with geom_col(), in which the X-axis represents the region, the Y-axis the number of cases and the color (fill) the region of the variables. The command facet_wrap(~Category) creates a separate plot for each category in the dataset as a further dimension. To do this, however, we first need to create a variable that should contain the levels of the Category variable that we want to use (we don’t want to map all levels fromCategory):

Selected_Category <- c("Active Cases by Region",
                            "Recoveries by Region",
                            "Deaths by Region")
g6 <- ggplot(data = filter(Regions_long, 
                     Date %in% max(Regions_long$Date),
                     Category %in% Selected_Category)
       ) +
  geom_col(mapping = aes(x = Region, 
                         y = `Cases`, 
                         fill = Region), 
           position = "dodge") +
  facet_wrap(~Category, ncol=6) +
  theme(axis.text.x = element_text(angle = 90)) +
  scale_y_continuous(labels = number) +
  labs(title = paste("Cases by Region and Category as of ",
                    date_dataset,
                    sep="")) +
  theme(legend.position = "top",
        legend.background = element_rect(fill=alpha(0.4)),
        plot.title = element_text(hjust = 0.5))
g6

Cases in Europe

This graphic shows the case numbers of all European countries in which at least 5000 confirmed cases were registered on March 30, 2020. To do this, we first have to filter the data set with filter() for the region Europe, the date 2020-03-30 and Confirmed Cases> = 5000 (don’t forget the back ticks). Then we have the variable Country/Region output from this object and save the list of countries that had more than 5000 confirmed cases on March 30th, 2020 in the object Europe_5000_list. Now we have a list of all of these countries and we filter our data set by countries that appear in this list. Then we create an area plot:

Europe_5000_list <- filter(data, Region == "Europe" &
           Date == as.Date("2020-03-30") &
           `Confirmed Cases` >= 5000)$`Country/Region`

Europe5000 <- filter(data, 
                  Region =="Europe" & 
                    `Country/Region` %in% Europe_5000_list & 
                    Date >= as.Date("2020-02-24"))
g7 <- ggplot(data = Europe5000) +
  geom_area(mapping = aes(x = Date, 
                          y = `Confirmed Cases`, 
                          fill = `Country/Region`)) +
  scale_x_date(date_breaks = "2 week", date_labels = "%d.%m") +
  scale_y_continuous(labels = number) +
  labs(x = "Date", 
       y = "Confirmed Cases", 
       title = paste("Confirmed Cases in Europe as of ",
                     date_dataset,
                     sep="")) +
  theme(legend.position = c(0.15, 0.6),
        legend.background = element_rect(fill=alpha(0.4)),
        plot.title = element_text(hjust = 0.5)) + 
  scale_fill_brewer(palette="Set3")
g7

We create the three other graphics in the same way, except that we do not have to do the previous filtering again:

g8 <- ggplot(data = Europe5000) +
  geom_area(mapping = aes(x = Date, 
                          y = `Deaths`, 
                          fill = `Country/Region`)) +
  scale_x_date(date_breaks = "2 week", date_labels = "%d.%m") +
  scale_y_continuous(labels = number) +
  labs(x = "Date", 
       y = "Deaths", 
       title = paste("Deaths in Europe as of ",
                    date_dataset,
                    sep="")) +
  theme(legend.position = c(0.15, 0.6),
        legend.background = element_rect(fill=alpha(0.4)),
        plot.title = element_text(hjust = 0.5)) + 
  scale_fill_brewer(palette="Set3")
g8

g9 <- ggplot(data = Europe5000) +
  geom_area(mapping = aes(x = Date, 
                          y = `Recoveries`, 
                          fill = `Country/Region`)) +
  scale_x_date(date_breaks = "2 week", date_labels = "%d.%m") +
  scale_y_continuous(labels = number) +
  labs(x = "Date", 
       y = "Recoveries", 
       title = paste("Recoveries in Europe as of ",
                    date_dataset,
                    sep="")) +
  theme(legend.position = c(0.15, 0.6),
        legend.background = element_rect(fill=alpha(0.4)),
        plot.title = element_text(hjust = 0.5)) + 
  scale_fill_brewer(palette="Set3")
g9

g10 <- ggplot(data = Europe5000) +
  geom_area(mapping = aes(x = Date, 
                          y = `Active Cases`, 
                          fill = `Country/Region`)) +
  scale_x_date(date_breaks = "2 week", date_labels = "%d.%m") +
  scale_y_continuous(labels = number) +
  labs(title = paste("Active Cases in Europe as of ",
                    date_dataset,
                    sep="")) +
  theme(legend.position = c(0.15, 0.6),
        legend.background = element_rect(fill=alpha(0.4)),
        plot.title = element_text(hjust = 0.5)) + 
  scale_fill_brewer(palette="Set3")
g10

Cases in Germany

In order to map all three categories of cases in Germany in a summarizing area plot, we first have to group the data set according to Country/Region and then filter it according to Germany. Then we sort the data set by Date in ascending order and create the variable Absolute Increase in Confirmed Cases using the lag() function and the variable Percentage Increase in Confirmed Cases.

Now we can convert this data record into the long form with pivot_longer().

Germany <- data %>% 
  group_by(`Country/Region`) %>% 
  filter(`Country/Region` == "Germany") %>% 
  arrange(Date) %>% 
  mutate(`Absolute Increase in Confirmed Cases` = lag(`Confirmed Cases`),
         `Percentage Increase in Confirmed Cases` = ((`Confirmed Cases` / `Absolute Increase in Confirmed Cases`)-1)*100)
  


Germany_long <- pivot_longer(data = Germany, 
                                 cols = c(`Confirmed Cases`,
                                          `Deaths`,
                                          `Recoveries`,
                                          `Active Cases`,
                                          `Absolute Increase in Confirmed Cases`,
                                          `Percentage Increase in Confirmed Cases`),
                                 names_to = "Category",
                                 values_to = "Cases")

Before creating an area plot for Germany, we have to wrangle a little more. Because the categories are usually colored in an alphabetical order, this would distort the order in which we would like to present the data. By using factor(), we reorder the factor levels of the Category variable in the order that we would like to present them. Since we have more levels within the Category variable in our long dataset than we want to map, we filter again in ggplot for the three variables Active Cases, Recoveries and Deaths.

Germany_long$Category <- factor(Germany_long$Category, levels = c("Active Cases", "Recoveries", "Deaths", "Absolute Increase in Confirmed Cases", "Percentage Increase in Confirmed Cases", "Confirmed Cases"))

g11 <- ggplot(data = filter(Germany_long, 
                     Category == "Active Cases" |
                       Category == "Recoveries" |
                       Category == "Deaths", Date >= as.Date("2020-03-01"))) +
  geom_area(mapping = aes(x = Date, 
                          y = `Cases`, 
                          fill = Category)) +
  scale_x_date(date_breaks = "2 week", date_labels = "%d.%m") +
  labs(x = "Date", 
       y = "Confirmed Cases in Germany",
       title = paste("Confirmed Cases in Germany as of ",
                    date_dataset,
                    sep="")) + 
  theme(legend.position = c(0.2, 0.8),
        legend.background = element_rect(fill=alpha(0.4)),
        plot.title = element_text(hjust = 0.5)) + 
  scale_fill_brewer(palette="Set3")
g11

Now we use ggplot() and geom_col() to create a bar chart and insert a few labels. We can read the case numbers of the labels from the filtered data set. Within ggplot() we filter for data that is newer than Feb 24, 2020.

g12 <- ggplot(data = filter(Germany, Date >= as.Date("2020-02-24"))) +
  geom_col(mapping = aes(x = Date, 
                         y = `Percentage Increase in Confirmed Cases`), 
           fill = "#6699ff") +
  scale_y_continuous(breaks=seq(0,max(Germany$`Percentage Increase in Confirmed Cases`, na.rm = T), 10)) +
  geom_vline(xintercept = as.Date("2020-03-06")) +
  geom_label(label="670 Cases", y=70, x=as.Date("2020-03-06")) +
  geom_vline(xintercept = as.Date("2020-03-14")) +
  geom_label(label="4585 Cases", y=60, x=as.Date("2020-03-14")) +
  geom_vline(xintercept = as.Date("2020-03-21")) +
  geom_vline(xintercept = as.Date("2020-03-23")) +
  geom_label(label="Bavaria \n lockdown, \n 22213 Cases", y=75, x=as.Date("2020-03-20")) +
  geom_label(label="Countrywide \n Restraining \n Order, \n 29056 Cases", y=40, x=as.Date("2020-03-25")) +
  scale_x_date(date_breaks = "2 week", date_labels = "%d.%m") + 
  labs(y = "Percent Increase", 
       title = paste("Percentage Increase in Confirmed Cases in Germany as of ",
                     date_dataset,
                     sep="")) +
  theme(legend.position = c(0.9, 0.8),
        legend.background = element_rect(fill=alpha(0.4)),
        plot.title = element_text(hjust = 0.5))
g12

In the following, we would like to create a bar chart with the case numbers in Germany and calculate an exponential function based on the case numbers from March 14th to 20th, 2020. We want to draw the values after March 20, 2020 in light blue, the values before March 20, 2020 should be darkblue.

For this we first filter the data set to the data from Feb 23, 2020 to Mar 30, 2020. For the dark blue bars, we filter again and only select the dates that lie between Mar 1, 2020 and Mar 20, 2020. On these days, the cases rose stronger than after March 20, 2020. We want to color this data dark blue.

Now we filter the dataset again on the data from March 14 to March 20, 2020, because we only want to use this data for the calculation of the model. We use this dataset to calculate the logarithm of the confirmed cases. Then we calculate a linear regression with lm() on the log-transformed data. We then use the seq() function to create a sequence of days for which we want to use our model to predict the number of cases. With the function predict.lm() we calculate the simulated values for this sequence and then expose them again with exp() and save them in the object prediction as a variable model. Now we join the newly created dataset to our previously filtered dataset prediction using the full_join() function.

all_data <- Germany %>% 
  filter(Date >= as.Date("2020-02-23") & Date <= max(data$Date))

darkblue <- Germany %>% 
  filter(Date >= as.Date("2020-03-01") & Date <= as.Date("2020-03-20"))

modeldata <- Germany %>% 
  filter(Date >= as.Date("2020-03-14") & Date <= as.Date("2020-03-20")) %>% 
  mutate(`Log Bestätigte Cases` = log(`Confirmed Cases`))

modell <- lm(`Log Bestätigte Cases` ~ Date, data = modeldata)
Date <- seq(as.Date("2020-03-14"), as.Date("2020-03-30"), by = 1)
prediction <- data.frame(Date)
prediction$modell <- exp(predict.lm(modell, newdata = prediction)) 

all_data_prediction <- full_join(all_data, prediction, by = "Date")

Now by using ggplot() we can first create a bar chart data set all_data_prediction and choose light blue (royalblue1) as the color. Then we create a second bar chart of the object darkblue, which only contains the values from Mar 1 to Mar 20, 2020 and color it darkblue (royalblue4). Then we add a line plot with geom_line(), which contains the predicted case numbers (model) based on the data from March 14th to March 20th, 2020.

Finally, we add some labels (geom_label()) and vertical lines (geom_vline()), set the date to an interval of 5 days and change the formatting of the date on the X axis.

g13 <- ggplot(data = all_data_prediction) +
  geom_col(mapping = aes(x = Date, 
                         y = `Confirmed Cases`), 
           fill ="royalblue1") +
  geom_col(data = darkblue, 
           mapping = aes(x = Date, 
                         y = `Confirmed Cases`), 
           fill ="royalblue4") +
  geom_line(mapping = aes(x = Date, 
                          y = modell), 
            color = "red4", size = 1) + 
  
  geom_vline(xintercept = as.Date("2020-03-06")) +
  geom_label(label="670 Cases", 
             y=20000, 
             x=as.Date("2020-03-06")) +
    
  geom_vline(xintercept = as.Date("2020-03-14")) +
  geom_label(label="4585 Cases", 
             y=50000, 
             x=as.Date("2020-03-14")) +
  
  geom_vline(xintercept = as.Date("2020-03-21")) +
  geom_vline(xintercept = as.Date("2020-03-23")) +
  
  geom_label(label="Bavaria \n lockdown, \n 22213 Cases", 
             y=80000, 
             x=as.Date("2020-03-20")) +
  geom_label(label="Countrywide \n Restraining \n Order, \n 29056 Cases", 
             y=150000, 
             x=as.Date("2020-03-23")) +
  
  geom_vline(xintercept = as.Date("2020-03-30")) +
  geom_label(label="229108 \n Cases", 
             y=225000, 
             x=as.Date("2020-03-30"), 
             color = "red") +
  geom_label(label="66885 \n Cases", 
             y=95000, 
             x=as.Date("2020-03-30")) +
  
  labs(x = "Date", 
       y = "Confirmed Cases",  
       title = paste("Confirmed Cases in Germany as of ",
                     date_dataset,
                     sep="")) +
  scale_x_date(date_breaks = "2 week", date_labels = "%d.%m") +
  theme(plot.title = element_text(hjust = 0.5))
g13

This page is constantly being updated. Last edited on August 10, 2020.

Date of the dataset: August 09, 2020














Contact us:

StatSoft (Europe) GmbH
Possmoorweg 1
22301 Hamburg
Germany

Fon +49 40 22 85 900-0
Fax +49 40 22 85 900-77
E-Mail info@statsoft.de
Internet: www.statsoft.de

Imprint
Data Protection

LS0tDQp0aXRsZTogIlZpc3VhbGl6YXRpb24gb2YgdGhlIENPVklELTE5IERhdGEgdXNpbmcgUiIgDQpzdWJ0aXRsZTogIiINCmF1dGhvcjogIkRyLiBOaWxzIFlhbm5pa29zLCBTdGF0U29mdCBFdXJvcGUgR21iSCINCiNkYXRlOiAiTcOkcnosIDIwMjAiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgY29kZV9mb2xkaW5nOiBzaG93DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICB0b2NfZGVwdGg6IDMNCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlDQogICAgaW5jbHVkZXM6DQogICAgICBpbl9oZWFkZXI6IGxvZ28uaHRtbA0KICB3b3JkX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogICAgdG9jOiB5ZXMNCiAgcGRmX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQotLS0NCg0KVGhpcyBhcnRpY2xlIGlzIHVwZGF0ZWQgcmVndWxhcmx5LiAgDQpBbGwgZGF0YSBwcmVzZW50ZWQgYXJlIGFzIG9mICoqYHIgZGF0ZV9kYXRhc2V0YCoqLCB0aGUgbGFzdCAqKnVwZGF0ZSoqIHdhcyBvbiAqKmByIGRhdGVfbW9kaWZpZWRgKiouDQoNCiMgSW50cm9kdWN0aW9uDQoNClRoZSBvbmdvaW5nIENPVklELTE5IHBhbmRlbWljIG5lZWRzIG5vIGV4cGxhbmF0aW9uIGJlY2F1c2UgdGhlIHdob2xlIHdvcmxkIGlzIGN1cnJlbnRseSBiYWRseSBhZmZlY3RlZC4gVGhlIGhpZ2ggaW5mZWN0aXZpdHkgb2YgdGhlIFNBUlMtQ29WLTIgcGF0aG9nZW4gYW5kIHRoZSBwb3RlbnRpYWxseSBzZXZlcmUgY291cnNlIG9mIHRoZSBkaXNlYXNlIGFyZSBwdXR0aW5nIGhlYWx0aGNhcmUgc3lzdGVtcyBhcm91bmQgdGhlIHdvcmxkIHRvIHRoZSB0ZXN0LiBIb3dldmVyLCB0byB0YWtlIHRoZSByaWdodCBzdGVwcyB0byBjb250YWluIHRoZSBwYW5kZW1pYywgYW5hbHl6aW5nIHRoZSBudW1iZXIgb2YgY2FzZXMgaXMgYW4gZXNzZW50aWFsIHN0ZXAuDQoNCk1hbnkgb2YgdXMgaGF2ZSBub3cgbWFkZSBpdCBhIGhhYml0IHRvIGxvb2sgYXQgdGhlIGN1cnJlbnQgc3RhdGUgb2YgdGhlIGNhc2UgbnVtYmVycyBhdCBsZWFzdCBvbmNlIGEgZGF5LiBUaGUgZ29vZCBhdmFpbGFiaWxpdHkgb2YgdGhlIGRhdGEgYW5kIG5ldyB2aXN1YWxpemF0aW9uIHRlY2hub2xvZ2llcyBlbmFibGUgdXMgdG8gdXBkYXRlIGdyYXBocyBpbiB0aGUgc2hvcnRlc3QgcG9zc2libGUgdGltZS4gRXNwZWNpYWxseSB0aGUgZGF0YSBvZiB0aGUgW0pvaG5zIEhvcGtpbnMgVW5pdmVyc2l0eV0oaHR0cHM6Ly9jb3JvbmF2aXJ1cy5qaHUuZWR1L21hcC5odG1sKSBhbmQgb2YgdGhlIFtSb2JlcnQgS29jaCBJbnN0aXR1dGVdKGh0dHBzOi8vd3d3LnJraS5kZS9ERS9Db250ZW50L0luZkFaL04vTmV1YXJ0aWdlc19Db3JvbmF2aXJ1cy9uQ29WLmh0bWwpIGFyZSB1c2VkIGludGVuc2l2ZWx5IGZvciByZXBvcnRpbmcgY2FzZXMgaW4gR2VybWFueS4gT24gZGlmZmVyZW50IHBsYXRmb3JtcywgZm9yIGV4YW1wbGUgW0thZ2dsZS5jb21dKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vKSwgYSBsb3Qgb2YgZGF0YSBpcyBhdmFpbGFibGUgdG8gZXZlcnlvbmUsIGluIHRoZSBob3BlIHRoYXQgdGhlIGxhcmdlIGdsb2JhbCBjb21tdW5pdHkgb2YgZGF0YSBzY2llbnRpc3RzIHdpbGwgZ2VuZXJhdGUgdmFsdWFibGUgaW5mb3JtYXRpb24gYW5kIHByZWRpY3Rpb25zIGZyb20gdGhlIGRhdGEuDQoNCldlIGF0IFtTdGF0U29mdCBFdXJvcGUgR21iSCBvZmZlciBhIHZhcmlldHkgb2YgUiB0cmFpbmluZyBjb3Vyc2VzXShodHRwczovL3d3dy5zdGF0c29mdC5kZS9lbi9kYXRlcy90cmFpbmluZykgYW5kIGhhdmUgYWxzbyB1c2VkIHRoZSBDT1ZJRC0xOSBkYXRhIHRvIGdlbmVyYXRlIGtub3dsZWRnZSB0aHJvdWdoIHZpc3VhbGl6YXRpb24uIFdlIGhhdmUgdXNlZCB0aGUgW09wZW4tU291cmNlIHN0YXRpc3RpY3Mgc29mdHdhcmUgUl0oaHR0cHM6Ly93d3cuci1wcm9qZWN0Lm9yZy8pIGFuZCBkaXNjbG9zZWQgdGhlIGNvZGUgYmVsb3cgdG8gcHJvdmlkZSBpbnRlcmVzdGVkIGFuYWx5c3RzIHdpdGggZnJlZSBoZWxwIGZvciB2aXN1YWxpemluZyBkYXRhIGluIFIuDQpXZSB1c2VkIHRoZSBkYXRhIGZyb20gdGhlIFtKb2hucyBIb3BraW5zIFVuaXZlcnNpdHldKGh0dHBzOi8vY29yb25hdmlydXMuamh1LmVkdS9tYXAuaHRtbCkgKGRvd25sb2FkZWQgZnJvbSBbdGhpcyBLYWdnbGUgTGlua10oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9zdWRhbGFpcmFqa3VtYXIvbm92ZWwtY29yb25hLXZpcnVzLTIwMTktZGF0YXNldCkpLiBUaGUgb3JpZ2luYWwgZGF0YSwgYXMgdXNlZCBpbiBLYWdnbGUsIG9yaWdpbmF0ZWQgZnJvbSB0aGUgW0dpdCBSZXBvc2l0b3J5IG9mIHRoZSBKb2hucyBIb3BraW5zIFVuaXZlcnNpdHldKGh0dHBzOi8vZ2l0aHViLmNvbS9DU1NFR0lTYW5kRGF0YS9DT1ZJRC0xOSkuDQoNCkF0IHRoaXMgcG9pbnQgd2Ugd291bGQgbGlrZSB0byB0YWtlIHRoZSBvcHBvcnR1bml0eSB0byBkcmF3IHlvdXIgYXR0ZW50aW9uIHRvIHZhcmlvdXMgc3VwcG9ydCBwbGF0Zm9ybXMgZm9yIHRoZSBDb3JvbmEgY3Jpc2lzLiBQZXJoYXBzIHlvdSBhcmUgYWZmZWN0ZWQgeW91cnNlbGYgb3Iga25vdyBwZW9wbGUgd2hvIGFyZSBwYXJ0aWN1bGFybHkgYXQgcmlzayBhbmQgc2hvdWxkIHN0YXkgYXQgaG9tZSBmb3IgdGhlaXIgb3duIHNhZmV0eS4gSW4gb3JkZXIgdG8gbWFrZSBsaWZlIGEgbGl0dGxlIGVhc2llciBmb3IgdGhvc2UgcGVvcGxlLCBtYW55IHN1cHBvcnQgcGxhdGZvcm1zIGhhdmUgZW1lcmdlZCwgZm9yIGV4YW1wbGUgdG8gcHJvdmlkZSBmcmVlIGdyb2Nlcnkgc2hvcHBpbmcuIFtUaGlzIGlzIGEgbGlzdCBvZiBHZXJtYW4gYWlkIHBsYXRmb3Jtc10oaHR0cHM6Ly93d3cuZnJlaXdpbGxpZy5oYW1idXJnL2Nvcm9uYS1oaWxmZS5odG1sKSwgd2hlcmUgeW91IGNhbiByZWdpc3RlciBhcyBhIHBlcnNvbiBhZmZlY3RlZCBvciBhcyBhIHBvdGVudGlhbCBoZWxwZXIuIFRoZXJlIHlvdSB3aWxsIGFsc28gZmluZCBsaW5rcyB0byBHZXJtYW55LXdpZGUgaGVscCBwbGF0Zm9ybXMuIFBsZWFzZSBhbHNvIHJlbWVtYmVyIHRoYXQgW2FuaW1hbCBzaGVsdGVycyBpbiBwYXJ0aWN1bGFyIG1heSBoYXZlIGEgbGFyZ2UgaW5mbHV4IG9mIHBldHMgZHVyaW5nIHRoaXMgdGltZV0oaHR0cHM6Ly93d3cuaGFtYnVyZ2VyLXRpZXJzY2h1dHp2ZXJlaW4uZGUvdGllcmhlaW0vYWt0dWVsbGVzLzEyNjg3LWh0di1yaWNodGV0LXNpY2gtYXVmLWRpZS1hdWZuYWhtZW4tZGVyLXRpZXJlLXZvbi1jb3JvbmEtcGF0aWVudC1pbm5lbi1laW4pLCB3aG9zZSBvd25lcnMgYXJlIG5vIGxvbmdlciBhYmxlIHRvIHRha2UgY2FyZSBvZiBkdWUgdG8gdGhlIGlsbG5lc3MuIFlvdSBjYW4gc3VwcG9ydCB0aGUgc2hlbHRlcnMgYnkgZG9uYXRpbmcgZm9vZCBvciBtb25leS4NCg0KIyMjIyBSZW9tbWVuZGVkIGxpdGVyYXR1cmUNCg0KRm9yIGRhdGEgYW5hbHlzaXMgYW5kIHZpc3VhbGl6YXRpb24gd2UgY2FuIHdhcm1seSByZWNvbW1lbmQgdGhlIGV4Y2VsbGVudCBib29rcyBbUiBmb3IgRGF0YSBTY2llbmNlXShodHRwczovL3I0ZHMuaGFkLmNvLm56LykgYW5kIFtIYW5kcyBvbiBQcm9ncmFtbWluZyB3aXRoIFJdKGh0dHBzOi8vcnN0dWRpby1lZHVjYXRpb24uZ2l0aHViLmlvL2hvcHIvKSBieSBIYWRsZXkgV2lja2hhbSBhbmQgR2FycmV0IEdyb2xlbXVuZC4gSW5jaWRlbnRhbGx5LCBib3RoIGF1dGhvcnMgYXJlIGFsc28gdGhlIGF1dGhvcnMgb2YgbW9zdCBwYWNrYWdlcyB0aGF0IHdlIG5lZWQgaW4gdGhpcyBwcm9qZWN0Lg0KDQoNCg0KDQoNCiMgVGhlIEN1cnJlbnQgU2l0dWF0aW9uDQoNCg0KSW4gdGhpcyBzZWN0aW9uIHdlIHdvdWxkIGxpa2UgdG8gdXNlIHZhcmlvdXMgZ3JhcGhpY3MgdG8gc2hvdyB5b3UgaG93IHRoZSBDT1ZJRC0xOSBwYW5kZW1pYyBpcyBzcHJlYWRpbmcgdGhyb3VnaG91dCB0aGUgd29ybGQuIFRoZSBzdGF0dXMgb2YgdGhlIGRhdGEgcmVjb3JkIGlzIHRoZSBgciBkYXRlX2RhdGFzZXRgLiBJbiB0aGUgbmV4dCBzZWN0aW9uIGJlbG93LCB3ZSB0aGVuIHNob3cgaG93IHdlIGNyZWF0ZWQgdGhlc2UgZ3JhcGhpY3MgdXNpbmcgUi4NCg0KIyMjIFdvcmxkd2lkZQ0KDQpUaGUgZm9sbG93aW5nIGZpZ3VyZSBzaG93cyB0aGUgbnVtYmVyIG9mIGNvbmZpcm1lZCBjYXNlcyBmcm9tIGFsbCBjb3VudHJpZXMsIGRpdmlkZWQgaW50byBhY3RpdmUgY2FzZXMsIHJlY292ZXJpZXMgYW5kIGRlYXRocywgd2hpY2ggaGFkIHJlZ2lzdGVyZWQgYXQgbGVhc3QgNTAwMDAgY29uZmlybWVkIGNhc2VzIG9uIGByIGRhdGVfZGF0YXNldGAuDQoNCmBgYHtyIGVjaG89RkFMU0V9DQpnMQ0KYGBgDQoNClRoZSBmb2xsb3dpbmcgZmlndXJlIHNob3dzIHRoZSBudW1iZXIgb2YgY29uZmlybWVkIGNhc2VzIG9uIGEgbG9nIHNjYWxlIGJ5IGRheXMgYWZ0ZXIgdGhlIDEwMHRoIGNvbmZpcm1lZCBjYXNlIHdhcyByZWdpc3RlcmVkIGluIGVhY2ggY291bnRyeS4gVGhpcyB3YXksIGl0IGlzIHBvc3NpYmxlIHRvIHNlZSBob3cgZmFzdCB0aGUgb3V0YnJlYWsgZGV2ZWxvcGVkIGluIHRoZSByZXNwZWN0aXZlIGNvdW50cmllcy4gRm9yIHRoaXMgZmlndXJlLCBvbmx5IHRoZSBjb3VudHJpZXMgd2hpY2ggcmVnaXN0ZXJlZCB0aGUgbW9zdCBjYXNlcyBhcyBvZiBgciBkYXRlX2RhdGFzZXRgIGFzIHdlbGwgYXMgU291dGggS29yZWEgd2VyZSBzZWxlY3RlZC4gU291dGggS29yZWEgaXMgYSBzcGVjaWFsIGNhc2UsIGJlY2F1c2UgdGhlaXIgZmFzdCByZWFjdGlvbiBhbmQgdGVzdGluZyByZWdpbWUgY2F1c2VkIGEgc3Ryb25nIGRlY3JlYXNlIGluIGZ1cnRoZXIgaW5mZWN0aW9ucy4gQnkgY2hvb3NpbmcgdGhlIGxvZyBzY2FsZSwgaXQgaXMgZWFzaWVyIHRvIGNvbXBhcmUgdGhlIGluY3JlYXNlIGluIGNhc2VzIGlycmVzcGVjdGl2ZSBvZiB0aGVpciBvcmRlciBvZiBtYWduaXR1ZGUuDQoNCmBgYHtyIGVjaG89RkFMU0V9DQpnMg0KYGBgDQoNCiMjIyBDaGluYSwgRXVyb3BlIGFuZCBVU0ENCg0KVGhlIGZvbGxvd2luZyBmaWd1cmUgc2hvd3MgaG93IHRoZSBudW1iZXIgb2YgY2FzZXMgZGV2ZWxvcGVkIGluIENoaW5hLCBFdXJvcGUsIHRoZSBVU0EgYW5kIGFsbCBvdGhlciBjb3VudHJpZXMgKG90aGVyKS4gSXQgYmVjb21lcyBjbGVhciBob3cgcXVpY2tseSBDaGluYSBtYW5hZ2VkIHRvIGdldCB0aGUgb3V0YnJlYWsgdW5kZXIgY29udHJvbCBieSB0YWtpbmcgdmVyeSBzdHJpY3QgbWVhc3VyZXMuIEFib3V0IGZpdmUgd2Vla3MgYWZ0ZXIgdGhlIG51bWJlciBvZiBjYXNlcyBpbiBDaGluYSByZWFjaGVkIGEgcGxhdGVhdSwgdGhlIG51bWJlciBvZiBjYXNlcyBpbiBFdXJvcGUgd2FzIGFscmVhZHkgaGlnaGVyIHRoYW4gaW4gQ2hpbmEuIEp1c3QgdHdvIG1vcmUgd2Vla3MgbGF0ZXIsIHRoZXJlIHdlcmUgZml2ZSB0aW1lcyBhcyBtYW55IGNhc2VzIGluIEV1cm9wZSBhcyBpbiBDaGluYS4gVGhlIFVTQSBhbHNvIHNob3dlZCBhIHJhcGlkIGluY3JlYXNlLCB3aGljaCBmb2xsb3dzIEV1cm9wZSdzIHRyZW5kLCBkZWxheWVkIGJ5IGFib3V0IGEgd2VlayBhbmQgYSBoYWxmLiBFdXJvcGUgYW5kIHRoZSBVU0EgYXJlIGN1cnJlbnRseSB0aGUgbmV3IGVwaWNlbnRyZXMgb2YgdGhlIENPVklELTE5IHBhbmRlbWljLg0KDQpgYGB7ciBlY2hvPUZBTFNFfQ0KZzMNCmBgYA0KDQoNCg0KVGhlIGZvbGxvd2luZyBmaWd1cmUgc2hvd3MgdGhlIGRhaWx5IGluY3JlYXNlIGluIGNhc2UgbnVtYmVycyBpbiBDaGluYSwgRXVyb3BlLCB0aGUgVVNBIGFuZCBpbiBhbGwgb3RoZXIgY291bnRyaWVzLiBUaGlzIGNsZWFybHkgc2hvd3MgaG93IHF1aWNrbHkgdGhlIENoaW5lc2UgZ292ZXJubWVudCByZWFjdGVkIGFuZCB0b29rIGRyYXN0aWMgbWVhc3VyZXMsIGJlY2F1c2UgdGhlIHF1YXJhbnRpbmUgb2YgSHViZWkgYWxyZWFkeSBiZWdhbiBvbiBKYW51YXJ5IDIzLCBhdCBhIHRpbWUgd2hlbiB0aGVyZSB3ZXJlIG9ubHkgNjM5IGNvbmZpcm1lZCBjYXNlcyBhbmQgMTggZGVhdGhzIGluIENoaW5hLiBOZXZlcnRoZWxlc3MsIGRlc3BpdGUgdGhlIHF1YXJhbnRpbmUsIHRoZSBjb25maXJtZWQgY2FzZXMgaW4gQ2hpbmEgcm9zZSB0byAqKmByIHJlZjAwMWAqKiwgaW5jbHVkaW5nICoqYHIgcmVmMDAyYCoqIGRlYXRocyAoYXMgb2YgYHIgZGF0ZV9kYXRhc2V0YCkuIFRoaXMgbWFrZXMgaXQgdmVyeSBjbGVhciB0aGF0IGEgc3Ryb25nIHJlc3RyaWN0aW9uIG9uIHRyYXZlbCBhbmQgZnJlZWRvbSBvZiBtb3ZlbWVudCAoaG93ZXZlciBwYWluZnVsIHRoaXMgbWF5IGJlKSBhKSBjYW4gZWZmZWN0aXZlbHkgbGltaXQgdGhlIHNwcmVhZCwgYikgc2hvdWxkIGJlIGNhcnJpZWQgcmF0aGVyIGVhcmxpZXIgdGhhbiBsYXRlciwgYW5kIGMpIG1heSBmbGF0dGVuIHRoZSBjdXJ2ZSBvbmx5IGFmdGVyIGEgY2VydGFpbiBkZWxheS4NCg0KDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQpyZWYwMDEgPC0gZm9ybWF0KHJvdW5kKG1heChmaWx0ZXIoUmVnaW9uc19sb25nLCBSZWdpb24gPT0gIkNoaW5hIiAmIENhdGVnb3J5ID09ICJDb25maXJtZWQgQ2FzZXMgYnkgUmVnaW9uIikkYENhc2VzYCksIGRpZ2l0cyA9IDApLCBzY2llbnRpZmljPTEpDQoNCnJlZjAwMiA8LSBmb3JtYXQocm91bmQobWF4KGZpbHRlcihSZWdpb25zX2xvbmcsIFJlZ2lvbiA9PSAiQ2hpbmEiICYgQ2F0ZWdvcnkgPT0gIkRlYXRocyBieSBSZWdpb24iKSRgQ2FzZXNgKSwgZGlnaXRzID0gMCksIHNjaWVudGlmaWM9MSkNCg0KDQpyZWYxIDwtIG1heChmaWx0ZXIoUmVnaW9ucywgUmVnaW9uID09ICJFdXJvcGUiKSRgSW5jcmVhc2UgaW4gQ29uZmlybWVkIENhc2VzIGJ5IFJlZ2lvbmAsIG5hLnJtID0gVCkNCg0KcmVmMSA8LSBmb3JtYXQocm91bmQocmVmMSwgZGlnaXRzID0gMCksIHNjaWVudGlmaWM9MSkNCnJlZjIgPC0gc3RyZnRpbWUoZmlsdGVyKFJlZ2lvbnMsIFJlZ2lvbiA9PSAiRXVyb3BlIiAmIGBJbmNyZWFzZSBpbiBDb25maXJtZWQgQ2FzZXMgYnkgUmVnaW9uYCA9PSBtYXgoZmlsdGVyKFJlZ2lvbnMsIFJlZ2lvbiA9PSAiRXVyb3BlIikkYEluY3JlYXNlIGluIENvbmZpcm1lZCBDYXNlcyBieSBSZWdpb25gLCBuYS5ybSA9IFQpKSREYXRlLCIlZC4lbS4lWSIpDQoNCnJlZjMgPC0gbWF4KGZpbHRlcihSZWdpb25zLCBSZWdpb24gPT0gIlVTQSIpJGBJbmNyZWFzZSBpbiBDb25maXJtZWQgQ2FzZXMgYnkgUmVnaW9uYCwgbmEucm0gPSBUKQ0KcmVmMyA8LSBmb3JtYXQocm91bmQocmVmMywgZGlnaXRzID0gMCksIHNjaWVudGlmaWM9MSkNCnJlZjQgPC0gc3RyZnRpbWUoZmlsdGVyKFJlZ2lvbnMsIFJlZ2lvbiA9PSAiVVNBIiAmIGBJbmNyZWFzZSBpbiBDb25maXJtZWQgQ2FzZXMgYnkgUmVnaW9uYCA9PSBtYXgoZmlsdGVyKFJlZ2lvbnMsIFJlZ2lvbiA9PSAiVVNBIikkYEluY3JlYXNlIGluIENvbmZpcm1lZCBDYXNlcyBieSBSZWdpb25gLCBuYS5ybSA9IFQpKSREYXRlLCIlZC4lbS4lWSIpDQoNCnJlZjUgPC0gbWF4KGZpbHRlcihSZWdpb25zLCBSZWdpb24gPT0gIkNoaW5hIikkYEluY3JlYXNlIGluIENvbmZpcm1lZCBDYXNlcyBieSBSZWdpb25gLCBuYS5ybSA9IFQpDQpyZWY1IDwtIGZvcm1hdChyb3VuZChyZWY1LCBkaWdpdHMgPSAwKSwgc2NpZW50aWZpYz0xKQ0KcmVmNiA8LSBzdHJmdGltZShmaWx0ZXIoUmVnaW9ucywgUmVnaW9uID09ICJDaGluYSIgJiBgSW5jcmVhc2UgaW4gQ29uZmlybWVkIENhc2VzIGJ5IFJlZ2lvbmAgPT0gbWF4KGZpbHRlcihSZWdpb25zLCBSZWdpb24gPT0gIkNoaW5hIikkYEluY3JlYXNlIGluIENvbmZpcm1lZCBDYXNlcyBieSBSZWdpb25gLCBuYS5ybSA9IFQpKSREYXRlLCIlZC4lbS4lWSIpDQoNCmBgYA0KDQoNCg0KSW4gRXVyb3BlIG9uIGByIHJlZjJgIHRoZSBoaWdoZXN0IGRhaWx5IGluY3JlYXNlIGluIGNhc2VzIChgciByZWYxYCkgd2FzIHJlY29yZGVkLCBpbiB0aGUgVVNBIHRoZSBoaWdoZXN0IGluY3JlYXNlIHdhcyBgciByZWYzYCBjYXNlcyBvbiBgciByZWY0YC4NCkluIENoaW5hLCBvbiB0aGUgb3RoZXIgaGFuZCwgdGhlIGhpZ2hlc3QgaW5jcmVhc2Ugd2FzIG9ubHkgYHIgcmVmNWAgY2FzZXMgb24gYHIgcmVmNmAuDQoNCmBgYHtyIGVjaG89RkFMU0V9DQpnNA0KYGBgDQoNCg0KDQoNCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCnJlZjcgPC0gKGZpbHRlcihSZWdpb25zLCBSZWdpb24gPT0gIkV1cm9wZSIsIERhdGUgPT0gYXMuRGF0ZSgiMjAyMC0wMy0yOCIpKSRgQWN0aXZlIENhc2VzIGJ5IFJlZ2lvbmAvIGZpbHRlcihSZWdpb25zLCBSZWdpb24gPT0gIkV1cm9wZSIsIERhdGUgPT0gYXMuRGF0ZSgiMjAyMC0wMy0yOCIpKSRgQ29uZmlybWVkIENhc2VzIGJ5IFJlZ2lvbmApKjEwMA0KcmVmNyA8LSByb3VuZChyZWY3LCBkaWdpdHM9MSkNCnJlZjggPC0gKGZpbHRlcihSZWdpb25zLCBSZWdpb24gPT0gIlVTQSIsIERhdGUgPT0gYXMuRGF0ZSgiMjAyMC0wMy0yOCIpKSRgQWN0aXZlIENhc2VzIGJ5IFJlZ2lvbmAvIGZpbHRlcihSZWdpb25zLCBSZWdpb24gPT0gIlVTQSIsIERhdGUgPT0gYXMuRGF0ZSgiMjAyMC0wMy0yOCIpKSRgQ29uZmlybWVkIENhc2VzIGJ5IFJlZ2lvbmApKjEwMA0KcmVmOCA8LSByb3VuZChyZWY4LCBkaWdpdHM9MSkNCnJlZjkgPC0gKGZpbHRlcihSZWdpb25zLCBSZWdpb24gPT0gIkNoaW5hIiwgRGF0ZSA9PSBhcy5EYXRlKCIyMDIwLTAzLTI4IikpJGBBY3RpdmUgQ2FzZXMgYnkgUmVnaW9uYC8gZmlsdGVyKFJlZ2lvbnMsIFJlZ2lvbiA9PSAiQ2hpbmEiLCBEYXRlID09IGFzLkRhdGUoIjIwMjAtMDMtMjgiKSkkYENvbmZpcm1lZCBDYXNlcyBieSBSZWdpb25gKSoxMDANCnJlZjkgPC0gcm91bmQocmVmOSwgZGlnaXRzPTEpDQpmaWx0ZXIoUmVnaW9ucywgUmVnaW9uID09IlVTQSIpDQpgYGANCg0KVGhlIGZvbGxvd2luZyBmaWd1cmUgc2hvd3MgdGhlIHRvdGFsIGFjdGl2ZSBjYXNlcyBvZiBhbGwgZGVmaW5lZCByZWdpb25zIGZvciB0aGUgcmVzcGVjdGl2ZSBwZXJpb2QsIHRoZSB3aWR0aCBvZiB0aGUgY29sb3JlZCBiYW5kIHN5bWJvbGl6ZXMgdGhlIHByb3BvcnRpb24gb2YgdGhvc2UgZGlzZWFzZXMgdGhhdCBvY2N1cnJlZCBpbiB0aGUgcmVzcGVjdGl2ZSByZWdpb24uIFRoZSBzcHJlYWQgYmVnYW4gaW4gQ2hpbmEgZmlyc3QsIHRoZW4gY29udGludWVkIGluIEV1cm9wZSBhbmQgb3RoZXIgY291bnRyaWVzIGFuZCBhdCB0aGUgc2FtZSB0aW1lIGRlY3JlYXNlZCBhZ2FpbiBpbiBDaGluYS4gQXJvdW5kIHR3byB3ZWVrcyBhZnRlciB0aGUgb3V0YnJlYWsgaW4gRXVyb3BlLCB0aGUgZGlzZWFzZSBzcHJlYWQgdG8gdGhlIFVuaXRlZCBTdGF0ZXMuIFRoZSBVbml0ZWQgU3RhdGVzIGlzIHRoZSBjb3VudHJ5IHdpdGggdGhlIGhpZ2hlc3QgbnVtYmVyIG9mIGFjdXRlIGlsbG5lc3Nlcy4NCiANCg0KYGBge3IgZWNobz1GQUxTRX0NCmc1DQpgYGANCg0KDQpUaGUgZm9sbG93aW5nIGZpZ3VyZSBzaG93cyB0aGUgY29uZmlybWVkIGNhc2VzLCBkZWF0aHMgYW5kIGFjdGl2ZSBjYXNlcyBmb3IgdGhlIGRpZmZlcmVudCByZWdpb25zIG9uIGByIGRhdGVfZGF0YXNldGAuDQoNCg0KYGBge3IgZWNobz1GQUxTRX0NCmc2DQpgYGANCg0KDQoNCg0KDQojIyMgRXVyb3BlDQoNCkluIHRoZSBmb2xsb3dpbmcgZmlndXJlIHdlIHNlZSB0aGUgY29uZmlybWVkIGNhc2VzIG9mIGFsbCBFdXJvcGVhbiBjb3VudHJpZXMgaW4gd2hpY2ggYXQgbGVhc3QgKio1MDAwIGNvbmZpcm1lZCBjYXNlcyoqIHdlcmUgcmVnaXN0ZXJlZCBvbiAqKjMwLjAzLjIwMjAqKi4gVGhlIHdpZHRoIG9mIHRoZSBjb2xvcmVkIGJhbmQgc2hvd3Mgd2hpY2ggcHJvcG9ydGlvbiBvZiBjb25maXJtZWQgY2FzZXMgc3RhdGVzIGNhbiBiZSBhc3NpZ25lZCB0byB0aGUgcmVzcGVjdGl2ZSBzdGF0ZSBhbmQgaG93IHRoaXMgcHJvcG9ydGlvbiBjaGFuZ2VzIG92ZXIgdGltZS4gVGhpcyBzaG93cyB0aGF0IEl0YWx5IHdhcyB0aGUgZmlyc3QgY291bnRyeSB0byByZWdpc3RlciBhIGhpZ2ggbnVtYmVyIG9mIGNvbmZpcm1lZCBjYXNlcy4gQSBmZXcgd2Vla3MgbGF0ZXIsIHRoZSBudW1iZXIgb2YgaW5mZWN0aW9ucyBhbHNvIHJvc2Ugc2hhcnBseSBpbiBzdXJyb3VuZGluZyBFdXJvcGVhbiBjb3VudHJpZXMuIE1vc3QgaW5mZWN0aW9ucyB3ZXJlIHJlY29yZGVkIHVudGlsIGByIGRhdGVfZGF0YXNldGAgaW4gSXRhbHksIFNwYWluIGFuZCBHZXJtYW55IGFuZCBGcmFuY2UuDQoNCmBgYHtyIGVjaG89RkFMU0V9DQpnNw0KYGBgDQoNCkluIHRoZSBmb2xsb3dpbmcgZmlndXJlIHdlIHNlZSB0aGUgZGVhdGhzIGluIGFsbCBFdXJvcGVhbiBjb3VudHJpZXMgd2hlcmUgYXQgbGVhc3QgKio1MDAwIGNvbmZpcm1lZCBjYXNlcyoqIHdlcmUgcmVnaXN0ZXJlZCBvbiAqKjMwLjAzLjIwMjAqKi4gVGhlIG51bWJlciBvZiBkZWF0aHMgaW4gR2VybWFueSBpcyBzdGlsbCB2ZXJ5IHNtYWxsIGNvbXBhcmVkIHRvIHRoZSByZWxhdGl2ZWx5IGhpZ2ggbnVtYmVyIG9mIGNvbmZpcm1lZCBjYXNlcy4NCg0KYGBge3IgZWNobz1GQUxTRX0NCmc4DQpgYGANCg0KDQpUaGUgZm9sbG93aW5nIGZpZ3VyZSBzaG93cyB0aGUgbnVtYmVyIG9mIHJlY292ZXJpZXMgd2l0aGluIHRoZSBkaWZmZXJlbnQgRXVyb3BlYW4gY291bnRyaWVzLiBUaGUgbnVtYmVyIG9mIHJlY292ZXJpZXMgaXMgaGlnaGVzdCBpbiBTcGFpbiwgSXRhbHkgYW5kIEdlcm1hbnkuDQoNCmBgYHtyIGVjaG89RkFMU0V9DQpnOQ0KYGBgDQoNCg0KVGhlIGZvbGxvd2luZyBmaWd1cmUgc2hvd3MgdGhlIGNvdXJzZSBvZiBhY3RpdmUgY2FzZXMgd2l0aGluIHRoZSBFdXJvcGVhbiBjb3VudHJpZXMuDQoNCg0KYGBge3IgZWNobz1GQUxTRX0NCmcxMA0KYGBgDQoNCg0KIyMjIEdlcm1hbnkNCg0KDQpUaGUgZm9sbG93aW5nIGZpZ3VyZSBzaG93cyB0aGUgYWN0aXZlIGNhc2VzLCBkZWF0aHMgYW5kIHJlY292ZXJpZXMgaW4gR2VybWFueSBvdmVyIHRpbWUuDQoNCmBgYHtyIGVjaG89RkFMU0V9DQpnMTENCmBgYA0KDQoNClRoZSBmb2xsb3dpbmcgZmlndXJlIHNob3dzIHRoZSBkYWlseSBwZXJjZW50YWdlIGluY3JlYXNlIGluIGNvbmZpcm1lZCBjYXNlcyBpbiBHZXJtYW55LiBUaGUgaGlnaGVzdCBkYWlseSBpbmNyZWFzZXMgKD4gNTAlKSB3ZXJlIHJlZ2lzdGVyZWQgd2hlbiB0aGVyZSB3ZXJlIGZld2VyIHRoYW4gNTAwMCBjYXNlcyBpbiBHZXJtYW55LiBTdWJzZXF1ZW50bHksIHRoZSBkYWlseSBpbmNyZWFzZXMgd2VyZSBiZXR3ZWVuIDI1IGFuZCAzNSUgZm9yIDcgZGF5cyBhbmQgdGhlbiBkZWNyZWFzZWQgZnVydGhlciB0byAxMC0xNyUgZm9yIHRoZSBmb2xsb3dpbmcgOCBkYXlzLiBTaW5jZSBNYXJjaCAyOSwgMjAyMCBkYWlseSBpbmNyZWFzZXMgaGF2ZSByZW1haW5lZCBiZWxvdyB0aGUgMTAlIGxpbWl0Lg0KDQoNCmBgYHtyIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpnMTINCmBgYCANCg0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0KcmVmMTAgPC0gcm91bmQoZmlsdGVyKHByZWRpY3Rpb24sIERhdGUgPT0gYXMuRGF0ZSgiMjAyMC0wMy0zMCIpKSRtb2RlbGwsIGRpZ2l0cyA9IDApDQpyZWYxMCA8LSBmb3JtYXQocmVmMTAsIHNjaWVudGlmaWM9MSkNCnJlZjExIDwtIHJvdW5kKGZpbHRlcihHZXJtYW55LCBEYXRlID09IGFzLkRhdGUoIjIwMjAtMDMtMzAiKSkkYENvbmZpcm1lZCBDYXNlc2AsIGRpZ2l0cyA9IDApDQpyZWYxMSA8LSBmb3JtYXQocmVmMTEsIHNjaWVudGlmaWM9MSkNCmBgYA0KDQpUaGlzIHJlZHVjdGlvbiBpbiBuZXcgaW5mZWN0aW9ucyBpbiBHZXJtYW55IHNpbmNlIE1hcmNoIDIxLCAyMDIwIHdhcyB2ZXJ5IGltcG9ydGFudCwgYmVjYXVzZSBvdGhlcndpc2UgdGhlIG51bWJlciBvZiBpbmZlY3Rpb25zIG9uIE1hcmNoIDMwLCAyMDIwIHdvdWxkIGhhdmUgYmVlbiBtb3JlIHRoYW4gdGhyZWUgdGltZXMgYXMgdGhlIGFjdHVhbCB2YWx1ZXMuIFRoZSBmb2xsb3dpbmcgZmlndXJlIHNob3dzIHRoZSBhY3R1YWwgZGV2ZWxvcG1lbnQgKGxpZ2h0IGJsdWUgYmFycykgb2YgdGhlIGNhc2UgbnVtYmVycyBpbiBHZXJtYW55LiBUaGUgcmVkIGxpbmUgc2ltdWxhdGVzIHRoZSBudW1iZXIgb2YgY2FzZXMgdGhhdCB3b3VsZCBoYXZlIHJlc3VsdGVkIGlmIHRoZSBkYWlseSBpbmNyZWFzZSBvZiAyNS0zNSUsIGFzIGl0IGhhZCB0YWtlbiBwbGFjZSBmcm9tIDAzLzE0LzIwMjAgLSAwMy8yMC8yMDIwLCBoYWQgY29udGludWVkLiBPbiBNYXJjaCAzMCwgMjAyMCwgYHIgcmVmMTBgIGluZmVjdGlvbnMgd291bGQgaGF2ZSBvY2N1cnJlZC4gSW4gZmFjdCwgb25seSBgciByZWYxMWAgY2FzZXMgd2VyZSByZWdpc3RlcmVkIGR1ZSB0byB0aGlzIHJlZHVjdGlvbi4NCg0KDQpgYGB7ciBlY2hvPUZBTFNFfQ0KZzEzDQpgYGANCg0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KIyBJZiB5b3Ugd2lzaCB0byBjb21waWxlIHRoaXMgbm90ZWJvb2sgYnkgeW91cnNlbGYsIHBsZWFzZSBmb2xsb3cgdGhlc2UgdHdvIHN0ZXBzOg0KIyBTdGVwIDE6IFNldCB5b3VyIGN1cnNvciB0byAiIyBUaGUgQ29kZSIgYW5kIHRoZW4gY2xpY2sgb24gIlJ1biBBbGwgQ29kZSBDaHVua3MgQmVsb3ciLg0KIyBBZnRlciB0aGlzIGlzIGRvbmUsIGZvbGxvdyB0aGUgbmV4dCBzdGVwLg0KIyBTdGVwIDI6IFNldCB5b3VyIGN1cnNvciB0byAiIyBUaGUgQ29kZSIgYW5kIHRoZW4gY2xpY2sgb24gIlJ1biBBbGwgQ29kZSBDaHVua3MgQWJvdmUiLg0KYGBgDQoNCg0KDQojIFRoZSBDb2RlDQoNCg0KSW4gdGhpcyBzZWN0aW9uIHdlIHdpbGwgc2hvdyB5b3UgaG93IHdlIHByZXBhcmVkIHRoZSBkYXRhIHRvIGdlbmVyYXRlIHRoZSBmaWd1cmVzIHNob3duIGFib3ZlLg0KDQojIyMgTG9hZGluZyBQYWNrYWdlcyBhbmQgRGF0YQ0KDQpGb3IgdGhpcyBwcm9qZWN0IHdlIG5lZWQgdGhlIHBhY2thZ2VzIGB0aWR5dmVyc2VgIGFuZGAgbHVicmlkYXRlYCBhcyB3ZWxsIGFzIGBzY2FsZXNgLCBgUkNvbG9yQnJld2VyYCBhbmQgYGNvdW50cnljb2RlYC4gVGhlc2UgbXVzdCBoYXZlIGJlZW4gaW5zdGFsbGVkIGJlZm9yZWhhbmQuDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9VFJVRX0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KHNjYWxlcykNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KbGlicmFyeShjb3VudHJ5Y29kZSkNCmBgYA0KDQoNCg0KDQoNCk5vdyB3ZSBsb2FkIHRoZSBkYXRhc2V0IChkb3dubG9hZGVkIGZyb20gW3RoaXMgS2FnZ2xlIExpbmtdKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vc3VkYWxhaXJhamt1bWFyL25vdmVsLWNvcm9uYS12aXJ1cy0yMDE5LWRhdGFzZXQpKSB1c2luZyB0aGUgRnVuY3Rpb24gYHJlYWRfY3N2KClgOg0KDQpgYGB7cn0NCmRhdGEgPC0gcmVhZF9jc3YoImRhdGEvY292aWRfMTlfZGF0YS5jc3YiKQ0KYGBgDQoNCiMjIyBEYXRhIENsZWFuaW5nDQoNCk5vdyB3ZSBjaGFuZ2UgdGhlIGZvcm1hdCBvZiB0aGUgdmFyaWFibGUgYE9ic2VydmF0aW9uRGF0ZWAgaW50byBhIHZhbGlkIGZvcm0gdXNpbmcgYGx1YnJpZGF0ZTo6bWR5KClgIGFuZCBzYXZlIGl0IGFzIGEgbmV3IHZhcmlhYmxlIGNhbGxlZCBgRGF0ZWAgdXNpbmcgdGhlIGZ1bmN0aW9uIGBkcGx5cjo6bXV0YXRlKClgLiBUaGVuIHdlIGdyb3VwIHRoZSBkYXRhIGFjY29yZGluZyB0byBgRGF0ZWAgYW5kIGBDb3VudHJ5L1JlZ2lvbmAgYW5kIHN1bSB1cCB0aGUgY29uZmlybWVkIGNhc2VzIChgQ29uZmlybWVkYCkgd2l0aGluIGVhY2ggZ3JvdXAgdXNpbmcgdGhlIGBzdW1tYXJpemUoKWAgZnVuY3Rpb24uIFRoaXMgZ2l2ZXMgdXMgdGhlIHN1bXMgb2YgYWxsIGNhc2VzIHdpdGhpbiBhIGNvdW50cnkgYW5kIGRheS4gV2UgZG8gdGhpcyBtYWlubHkgYmVjYXVzZSwgZm9yIGV4YW1wbGUsIGFsbCBwcm92aW5jZXMgb2YgQ2hpbmEgb3IgYWxsIHN0YXRlcyBvZiB0aGUgVVNBIGFyZSBhdmFpbGFibGUgYXMgaW5kaXZpZHVhbCBlbnRyaWVzLCBidXQgd2Ugd2FudCB0byBjb25zaWRlciB0aGUgY2FzZSBudW1iZXJzIGZvciBlYWNoIHdob2xlIGNvdW50cnkuIFRoZW4gd2Ugc29ydCBieSB0aGUgdmFyaWFibGUgYERhdGVgIGFuZCBgQ29uZmlybWVkIENhc2VzYCB0byBzZWUgd2hpY2ggaXMgdGhlIG1vc3QgY3VycmVudCBkYXRlIG9mIG91ciBkYXRhIHNldCBhbmQgaW4gd2hpY2ggY291bnRyaWVzIHRoZSBoaWdoZXN0IG51bWJlciBvZiBjYXNlcyB3YXMgcmVnaXN0ZXJlZDoNCg0KYGBge3J9DQpkYXRhIDwtIGRhdGEgJT4lIA0KICBtdXRhdGUoRGF0ZSA9IG1keShPYnNlcnZhdGlvbkRhdGUpKSAlPiUgDQogIGdyb3VwX2J5KERhdGUsIGBDb3VudHJ5L1JlZ2lvbmApICU+JSANCiAgc3VtbWFyaXplKGBDb25maXJtZWQgQ2FzZXNgID0gc3VtKENvbmZpcm1lZCksDQogICAgICAgICAgICBgRGVhdGhzYCA9IHN1bShEZWF0aHMpLA0KICAgICAgICAgICAgYFJlY292ZXJpZXNgID0gc3VtKFJlY292ZXJlZCkpICU+JSANCiAgYXJyYW5nZShkZXNjKERhdGUpLCBkZXNjKGBDb25maXJtZWQgQ2FzZXNgKSkNCg0KaGVhZChkYXRhKQ0KYGBgDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQpTeXMuc2V0bG9jYWxlKCJMQ19BTEwiLCJFbmdsaXNoIikNCmRhdGVfbW9kaWZpZWQgPC0gc3RyZnRpbWUodG9kYXkoKSwgIiVCICVkLCAlWSIpDQpkYXRlX2RhdGFzZXQgPC0gc3RyZnRpbWUobWF4KGRhdGEkRGF0ZSksICIlQiAlZCwgJVkiKQ0KYGBgDQoNCkxhdGVyIHdlIHdvdWxkIGxpa2UgdG8gY29tcGFyZSB0aGUgcHJvZ3Jlc3Npb24gaW4gQ2hpbmEgd2l0aCBhbGwgb2YgRXVyb3BlLiBIb3dldmVyLCB0byBkbyB0aGlzLCB3ZSBuZWVkIGEgbGlzdCBvZiBFdXJvcGVhbiBjb3VudHJpZXMgaWYgd2UgZG8gbm90IHdhbnQgdG8gYXNzaWduIHRoZW0gbWFudWFsbHkgYmVjYXVzZSB0aGUgdmFyaWFibGUgY29udGluZW50IGRvZXMgbm90IGV4aXN0IGluIHRoZSBkYXRhIHNldC4gVGhlIHBhY2thZ2UgYGNvdW50cnljb2RlYCBoZWxwcyB1cyBoZXJlLCB3aGljaCBjb250YWlucyBhIGxpc3Qgb2YgYWxsIGNvdW50cmllcyBpbiB0aGUgd29ybGQgYW5kIHRoZSBhc3NvY2lhdGVkIGNvdW50cnkgY29kZXMgYW5kIGNvbnRpbmVudHMuIFdpdGggdGhlIGZ1bmN0aW9uIGBjb3VudHJ5Y29kZSgpYCB3ZSBjcmVhdGUgYSBuZXcgdmFyaWFibGUgY2FsbGVkIGBjb250aW5lbnRgLiBBdCB0aGUgc2FtZSB0aW1lLCB3ZSBjYW4gdGFrZSB0aGUgb3Bwb3J0dW5pdHkgdG8gdXNlIHRoZSBgY291bnRyeWNvZGVgIGZ1bmN0aW9uIHRvIGNvbnZlcnQgdGhlIEVuZ2xpc2ggY291bnRyeSBuYW1lcyBpbnRvIEdlcm1hbiBvbmVzIChvbmx5IHVzZWQgaW4gdGhlIEdlcm1hbiB2ZXJzaW9uIG9mIHRoaXMgYXJ0aWNsZSkuIFdlIHRoZW4gY2FsbCB0aGlzIHZhcmlhYmxlIGBTdGFhdGAsIHdoaWNoIGlzIHRoZSBHZXJtYW4gd29yZCBmb3IgY291bnRyeToNCg0KYGBge3J9DQpsaWJyYXJ5KGNvdW50cnljb2RlKQ0KI2RhdGEzDQpkYXRhIDwtIGRhdGEgJT4lIA0KICBtdXRhdGUoY29udGluZW50ID0gY291bnRyeWNvZGUoYENvdW50cnkvUmVnaW9uYCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yaWdpbiA9ICJjb3VudHJ5Lm5hbWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzdGluYXRpb24gPSAiY29udGluZW50IiksDQogICAgICAgICBTdGFhdCA9IGNvdW50cnljb2RlKGBDb3VudHJ5L1JlZ2lvbmAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yaWdpbiA9ICJjb3VudHJ5Lm5hbWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXN0aW5hdGlvbiA9ICJjb3VudHJ5Lm5hbWUuZGUiKSkNCmBgYA0KDQpOb3cgd2UgY3JlYXRlIGFub3RoZXIgdmFyaWFibGUsIHdoaWNoIHdlIGNhbGwgYFJlZ2lvbmAuIEluIHRoaXMgdmFyaWFibGUgd2Ugd2lsbCBkZWZpbmUgdGhlIHJlZ2lvbnMgYEV1cm9wZWAsYCBDaGluYWAsIGBVbml0ZWQgU3RhdGVzYCBhbmQgYE90aGVyc2AuIFdlIGRvIHRoaXMgdXNpbmcgdGhlIGBiYXNlOjppZmVsc2UoKWAgZnVuY3Rpb24gd2l0aGluIHRoZSBgZHBseXI6Om11dGF0ZSgpYCBmdW5jdGlvbi4NCg0KVGhlIGZvbGxvd2luZyBjb2RlIHJlc3VsdHMgaW4gZnVsbCB3cml0aW5nOg0KSWYgdGhlIHZhcmlhYmxlIGBjb250aW5lbnRgIGVxdWFscyBgRXVyb3BlYCwgdGhlbiB0aGUgdmFyaWFibGUgYFJlZ2lvbmAgc2hvdWxkIGJlIHNldCB0byBgRXVyb3BlYDsgb3RoZXJ3aXNlIGl0IHNob3VsZCBiZSBzZXQgdG8gYENoaW5hYCBpZiB0aGUgdmFyaWFibGUgYENvdW50cnkvUmVnaW9uYCBlcXVhbHMgYE1haW5sYW5kIENoaW5hYDsgb3RoZXJ3aXNlIHRvIGBVU0FgIGlmIHRoZSB2YXJpYWJsZSBgQ291bnRyeS9SZWdpb25gIGVxdWFscyBgVVNgOyBvdGhlcndpc2UgdGhlIHZhcmlhYmxlIGBSZWdpb25gIHNob3VsZCBiZSBzZXQgdG8gYE90aGVyYC4NCg0KV2UgY2FuIG5vdyB1c2UgdGhlIHR3byB2YXJpYWJsZXMgKGBjb250aW5lbnRgIGFuZCBgQ291bnRyeS9SZWdpb25gKSB0byBncm91cCB0aGUgcmVnaW9ucyBgRXVyb3BlYCwgYENoaW5hYCwgYFVuaXRlZCBTdGF0ZXNgIGFuZCBgT3RoZXJzYDoNCg0KYGBge3J9DQojZGF0YTQNCmRhdGEgPC0gZGF0YSAlPiUgDQogIG11dGF0ZShSZWdpb24gPSBpZmVsc2UoY29udGluZW50ID09ICJFdXJvcGUiLCAiRXVyb3BlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGBDb3VudHJ5L1JlZ2lvbmAgPT0gIk1haW5sYW5kIENoaW5hIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDaGluYSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoYENvdW50cnkvUmVnaW9uYCA9PSAiVVMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJVU0EiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJPdGhlcnMiKSkpKQ0KYGBgDQoNCk5vdyB3ZSBjYW4gdGFrZSBhIGxvb2sgYXQgb3VyIGRhdGEgc2V0IHRvIGNoZWNrIHdoZXRoZXIgdGhlIG5ldyB2YXJpYWJsZXMgYFJlZ2lvbmAsIGBjb250aW5lbnRgIGFuZCBgU3RhYXRgIHdlcmUgY3JlYXRlZCBjb3JyZWN0bHk6DQoNCmBgYHtyfQ0KYXJyYW5nZShkYXRhLCBkZXNjKERhdGUpLCBkZXNjKGBDb25maXJtZWQgQ2FzZXNgKSkNCmBgYA0KDQpOZXh0IHdlIGNyZWF0ZSB0aGUgbmV3IHZhcmlhYmxlIGBBY3RpdmUgQ2FzZXNgLCB3aGljaCBjYW4gYmUgY2FsY3VsYXRlZCBmcm9tIHRoZSBvdGhlciB2YXJpYWJsZXMgYXMgZm9sbG93czoNCg0KYGBge3J9DQpkYXRhIDwtIG11dGF0ZShkYXRhLCBgQWN0aXZlIENhc2VzYCA9IGBDb25maXJtZWQgQ2FzZXNgIC0gYERlYXRoc2AgLSBgUmVjb3Zlcmllc2ApDQpkYXRhDQpgYGANCg0KICANClRoZSByb3VnaCB3b3JrIGlzIG5vdyBjb21wbGV0ZSBhbmQgd2UgY2FuIG5vdyB1c2UgdGhlIGRhdGEgdG8gZ2VuZXJhdGUgdGhlIGZpZ3VyZXMuDQoNCg0KDQoNCg0KDQoNCiMjIyBDYXNlcyBXb3JsZHdpZGUNCg0KRm9yIG91ciBmaXJzdCBmaWd1cmUsIHdlIGZpcnN0IGhhdmUgdG8gZmlsdGVyIHRoZSBkYXRhc2V0IGFjY29yZGluZyB0byB0aGUgbGF0ZXN0IGRhdGUgYW5kIHRoZSBudW1iZXIgb2YgY29uZmlybWVkIGNhc2VzIG9mIGF0IGxlYXN0IDUwMDAwLiBUaGVuIHdlIHVzZSBgcGl2b3RfbG9uZ2VyKClgIHRvIGNvbnZlcnQgdGhlIHRhYmxlIGludG8gdGhlIGxvbmcgZm9ybSBhbmQgY3JlYXRlIHRoZSB2YXJpYWJsZSBgQ2F0ZWdvcnlgIGFuZCBjb2xsZWN0IHRoZSBudW1iZXIgb2YgY2FzZXMgaW4gdGhlIHZhcmlhYmxlIGBDYXNlc2AuIE5vdyB0aGUgY2hhbmdlZCBkYXRhIHJlY29yZCBsb29rcyBsaWtlIHRoaXM6DQoNCg0KYGBge3J9DQpDb3VudHJpZXNfNTAwMDAgPC0gZGF0YSAlPiUgDQogIGZpbHRlcihEYXRlID09IHN0cmZ0aW1lKG1heChkYXRhJERhdGUpKSwNCiAgICAgICAgIGBDb25maXJtZWQgQ2FzZXNgID49IDUwMDAwKSAlPiUgIA0KICBwaXZvdF9sb25nZXIoY29scyA9IGMoYEFjdGl2ZSBDYXNlc2AsIA0KICAgICAgICAgICAgICAgICAgICAgICAgYERlYXRoc2AsIA0KICAgICAgICAgICAgICAgICAgICAgICAgYFJlY292ZXJpZXNgKSwgDQogICAgICAgICAgICAgICBuYW1lc190byA9ICJDYXRlZ29yeSIsIA0KICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIkNhc2VzIikNCg0KaGVhZChDb3VudHJpZXNfNTAwMDApDQpgYGANCg0KDQoNCg0KTm93IHdlIGNhbiBjcmVhdGUgYSBiYXIgZ3JhcGggd2l0aCBgZ2dwbG90KClgIGFuZCBgZ2VvbV9jb2woKWAuIEZpcnN0IHdlIGRlZmluZSB0aGUgWC1heGlzIGFzIHN0YXRlIGFuZCB0aGUgWS1heGlzIGFzIG51bWJlciBvZiBjYXNlcywgdGhlbiB3ZSBmbGlwIHRoZSB0d28gYXhlcyB3aXRoIHRoZSBjb21tYW5kIGBjb29yZF9mbGlwKClgLCBzbyB0aGF0IHRoZSBZLWF4aXMgYmVjb21lcyB0aGUgWC1heGlzIGFuZCB2aWNlIHZlcnNhLiBOb3cgdGhlIGxhc3Qgc3RlcCBpcyB0byBzb3J0IHRoZSBiYXJzIGFjY29yZGluZyB0byB0aGUgdG90YWwgbnVtYmVyIG9mIGNvbmZpcm1lZCBjYXNlcy4gU2luY2Ugd2UgaGF2ZSBub3QgY29udmVydGVkIHRoZSB2YXJpYWJsZSBgQ29uZmlybWVkIENhc2VzYCBpbnRvIHRoZSBsb25nIGZvcm0sIHRoaXMgdmFyaWFibGUgaXMgc3RpbGwgYXZhaWxhYmxlIHRvIHVzIGFuZCB3ZSBjYW4gc29ydCB0aGUgYmFyIGNoYXJ0IHdpdGggYHJlb3JkZXIoKWAgYWNjb3JkaW5nIHRvIHRoZSB2YXJpYWJsZSBgQ29uZmlybWVkIENhc2VzYC4NCg0KDQpgYGB7cn0NCkNvdW50cmllc181MDAwMCRDYXRlZ29yeSA8LSBmYWN0b3IoQ291bnRyaWVzXzUwMDAwJENhdGVnb3J5LCBsZXZlbHMgPSBjKCdBY3RpdmUgQ2FzZXMnLCAnUmVjb3ZlcmllcycsICdEZWF0aHMnKSkNCg0KZzEgPC0gZ2dwbG90KGRhdGEgPSBDb3VudHJpZXNfNTAwMDApICsNCiAgZ2VvbV9jb2wobWFwcGluZyA9IGFlcyh4ID0gcmVvcmRlcihgQ291bnRyeS9SZWdpb25gLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgQ29uZmlybWVkIENhc2VzYCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBgQ2FzZXNgLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gQ2F0ZWdvcnkpKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC44LCAwLjMpLA0KICAgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsPWFscGhhKDAuNCkpLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gbnVtYmVyKSArDQogIGxhYnMoeCA9ICJDb3VudHJ5IiwgDQogICAgICAgeSA9ICJDYXNlcyIsIA0KICAgICAgIHRpdGxlID0gcGFzdGUoIkNvbmZpcm1lZCBjYXNlcyBieSBjb3VudHJ5IGFzIG9mICIsIA0KICAgICAgICAgICAgICAgICAgICAgZGF0ZV9kYXRhc2V0LA0KICAgICAgICAgICAgICAgICAgICAgc2VwPSIiKSkNCmcxDQpgYGANCg0KDQpDcmVhdGluZyB0aGUgZm9sbG93aW5nIGZpZ3VyZSBpcyBhIGJpdCB0cmlja3kuIEZpcnN0LCB3ZSBuZWVkIHRvIHNlbGVjdCBhbGwgdGhlIGNhc2VzIGZyb20gb3VyIG9yaWdpbmFsIGRhdGFzZXQgYW5kIGZpbHRlciBieSB0aG9zZSB0aGF0IGhhdmUgbW9yZSB0aGFuIG9yIGVxdWFsIHRvIDEwMCBjYXNlcy4gVGhlbiB3ZSBncm91cCBieSBgQ291bnRyeS9SZWdpb25gIGFuZCAgdXNlIHRoZSBgc3VtbWFyaXplKClgIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgbWluaW11bSB2YWx1ZSBvZiBgQ29uZmlybWVkIENhc2VzYCwgd2hpY2ggZ2l2ZXMgIHVzIG9ubHkgb25lIG9ic2VydmF0aW9uIHBlciBjb3VudHJ5IChpbiBtb3N0IGNhc2VzKS4gVGhlc2UgdmFsdWVzIGFyZSB0aGVuIHNldCB0byBgMGAgYW5kIHN0b3JlZCBpbiB0aGUgbmV3IHZhcmlhYmxlIGBEYXlzIGFmdGVyIDEwMHRoIGNhc2VgIHVzaW5nIHRoZSBgbXV0YXRlKClgIGZ1bmN0aW9uLiBXZSBjYWxsIHRoaXMgb2JqZWN0IGB3b3JsZHdpZGVfbG9nXzFgLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kd29ybGR3aWRlX2xvZ18xIDwtIGRhdGEgJT4lDQogIGFycmFuZ2UoRGF0ZSkgJT4lIA0KICBmaWx0ZXIoYENvbmZpcm1lZCBDYXNlc2AgPj0xMDApICU+JSANCiAgZ3JvdXBfYnkoYENvdW50cnkvUmVnaW9uYCkgJT4lIA0KICBzdW1tYXJpemUoYENvbmZpcm1lZCBDYXNlc2AgPSBtaW4oYENvbmZpcm1lZCBDYXNlc2ApKSAlPiUgDQogIG11dGF0ZShgRGF5cyBhZnRlciAxMDB0aCBjYXNlYCA9IDApDQpgYGANCg0KVGhlIG5leHQgc3RlcCBpcyB0byBqb2luIHRoaXMgc3VtbWFyaXplZCBkYXRhc2V0IGJhY2sgdG8gdGhlIG9yaWdpbmFsIGRhdGFzZXQsIGNhbGxpbmcgdGhlIG5ldyBvYmplY3QgYHdvcmxkd2lkZV9sb2dfMmAuIFRoZW4gd2UgZmlsdGVyIGFnYWluIGJ5IHRob3NlIG9ic2VydmF0aW9ucyB3aGVyZSBgQ29uZmlybWVkIENhc2VzYCBhcmUgbW9yZSB0aGFuIG9yIGVxdWFsIHRvIDEwMCwgdW5ncm91cCAoaW1wb3J0YW50IGZvciBgY3Vtc3VtKClgIGxhdGVyKSB0aGUgZGF0YXNldCBhbmQgdGhlbiBhcnJhbmdlIGJ5IGBEYXRlYC4gQnkgam9pbmluZyB0aGUgZGF0YXNldHMgdG9nZXRoZXIsIG9ubHkgb25lIHJvdyBvZiBlYWNoIGNvdW50cnkgaGFzIHRoZSB2YWx1ZSBgMGAsIHRoZSBvdGhlcnMgaGF2ZSBgTkFgLiBUaGVzZSBgTkFgcyBhcmUgdGhlbiByZXBsYWNlZCBieSBgMWBzLiBUaGVuLCBhZnRlciBncm91cGluZyBieSBgQ291bnRyeS9SZWdpb25gIGFnYWluLCB3ZSBjYW4gY2FsY3VsYXRlIHRoZSBjdW11bGF0aXZlIHN1bSBvZiB0aGUgYERheXMgYWZ0ZXIgMTAwdGggY2FzZWAgYW5kIGFycmFuZ2UgdGhlIGRhdGEgYnkgYENvdW50cnkvUmVnaW9uYCBhbmQgYERhdGVgLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kd29ybGR3aWRlX2xvZ18yIDwtIGZ1bGxfam9pbihkYXRhLHdvcmxkd2lkZV9sb2dfMSkgJT4lIA0KICBmaWx0ZXIoYENvbmZpcm1lZCBDYXNlc2AgPj0gMTAwKSAlPiUgDQogIHVuZ3JvdXAod29ybGR3aWRlX2xvZ18yKSAlPiUgDQogIGFycmFuZ2UoRGF0ZSkgJT4lICANCiAgbXV0YXRlKGBEYXlzIGFmdGVyIDEwMHRoIGNhc2VgID0gaWZlbHNlKGlzLm5hKGBEYXlzIGFmdGVyIDEwMHRoIGNhc2VgKSA9PSBULCAxLCAwKSkgJT4lIA0KICBncm91cF9ieShgQ291bnRyeS9SZWdpb25gKSAlPiUgDQogIG11dGF0ZShgRGF5cyBhZnRlciAxMDB0aCBjYXNlYCA9IGN1bXN1bShgRGF5cyBhZnRlciAxMDB0aCBjYXNlYCkpICU+JSANCiAgYXJyYW5nZShgQ291bnRyeS9SZWdpb25gLCBEYXRlKQ0KYGBgDQoNCk5vdyB3ZSBjYWxjdWxhdGUgdGhlIG51bWJlciBvZiBjYXNlcyB3aGljaCB3b3VsZCBoYXZlIG9jY3VycmVkIGF0IGEgYERvdWJsaW5nIFRpbWVgIG9mIDIsIDcsIDE0LCBhbmQgNjAgZGF5cy4gSW4gb3JkZXIgdG8gZG8gdGhpcywgd2UgY3JlYXRlIGEgbmV3IGRhdGFzZXQgKGBEQkxUYCkgYW5kIGRlZmluZSB0aGUgdmFyaWFibGUgYERheXMgYWZ0ZXIgMTAwdGggY2FzZWAgYXMgcmFuZ2luZyBmcm9tIDEgdG8gNjAuIE5vdyB3ZSBjcmVhdGUgZm91ciBuZXcgdmFyaWFibGVzIChgMiBEYXlzYCwgYDcgRGF5c2AgZXRjLikgYW5kIGNhbGN1bGF0ZSB0aGUgY2FzZXMgdGhhdCB3b3VsZCBoYXZlIG9jY3VyZWQgd2l0aCB0aGUgcmVzcGVjdGl2ZSBkb3VibGluZyB0aW1lLiBOb3cgd2UgdHJhbnNmb3JtIHRoaXMgZGF0YXNldCB1c2luZyBgcGl2b3RfbG9uZ2VyKClgLiBJbiB0aGUgZW5kLCB3ZSBmaWx0ZXIgdGhpcyBkYXRhc2V0IGp1c3QgaW4gb3JkZXIgdG8gbm90IG1ha2UgdGhlIGNvbXBhcmlzb24gbGluZXMgdG9vIGxvbmcuDQoNCmBgYHtyfQ0KREJMVCA8LSB0aWJibGUoYERheXMgYWZ0ZXIgMTAwdGggY2FzZWAgPSBzZXEoMTo2MCkpICU+JSANCiAgbXV0YXRlKGAyIERheXNgID0gMTAwKigyXigxLzIpKV5gRGF5cyBhZnRlciAxMDB0aCBjYXNlYCwNCiAgICAgICAgIGA3IERheXNgID0gMTAwKigyXigxLzcpKV5gRGF5cyBhZnRlciAxMDB0aCBjYXNlYCwNCiAgICAgICAgIGAxNCBEYXlzYCA9IDEwMCooMl4oMS8xNCkpXmBEYXlzIGFmdGVyIDEwMHRoIGNhc2VgLA0KICAgICAgICAgYDYwIERheXNgID0gMTAwKigyXigxLzYwKSleYERheXMgYWZ0ZXIgMTAwdGggY2FzZWAsKSAlPiUgDQogIHBpdm90X2xvbmdlcihjb2xzID0gYygiMiBEYXlzIiwgIjcgRGF5cyIsICIxNCBEYXlzIiwgIjYwIERheXMiKSwgbmFtZXNfdG8gPSAiRG91YmxpbmcgVGltZSIsIHZhbHVlc190byA9ICJDYXNlcyIpICU+JSANCiAgbXV0YXRlKGBEb3VibGluZyBUaW1lYCA9IGFzX2ZhY3RvcihgRG91YmxpbmcgVGltZWApKSAlPiUgDQogIGZpbHRlcihDYXNlcyA8PSA0MDAwMDAwICYgYERheXMgYWZ0ZXIgMTAwdGggY2FzZWAgPD0gNTApDQpgYGANCk5vdyB3ZSBjYW4gcGxvdCB0aGUgYENvbmZpcm1lZCBDYXNlc2AgYWdhaW5zdCBgRGF5cyBhZnRlciAxMDB0aCBjYXNlYCB1c2luZyBgZ2dwbG90KClgLiBCeSBjaG9vc2luZyBgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPSJsb2cxMCIpYCwgd2UgY2FuIGRpc3BsYXkgdGhlIFktYXhpcyBvbiBhIGxvZyBzY2FsZS4gVGhpcyBtYWtlcyBpdCBlYXNpZXIgdG8gY29tcGFyZSB0aGUgZ3Jvd3RoIG9mIGNhc2VzIGFtb25nIGRpZmZlcmVudCBjb3VudHJpZXMuIFRoZW4gd2UgYWRkIGFub3RoZXIgbGluZSBwbG90IHdoaWNoIGNvbnRhaW5zIHRoZSBjYWxjdWxhdGVkIHZhbHVlcyBmcm9tIHRoZSBzdGVwIGFib3ZlIGFuZCBzZXQgdGhlIGBsaW5ldHlwZWAgdG8gYERvdWJsaW5nIFRpbWVgLg0KDQpgYGB7cn0NCmxvZ19jYXNlcyA8LSBmaWx0ZXIod29ybGR3aWRlX2xvZ18yLCBgQ291bnRyeS9SZWdpb25gICVpbiUgKGMoIk1haW5sYW5kIENoaW5hIiwgIkdlcm1hbnkiLCAiU3BhaW4iLCAiRnJhbmNlIiwgIkl0YWx5IiwgIlVTIiwgIklyYW4iLCAiU291dGggS29yZWEiKSkpDQoNCmcyIDwtIGdncGxvdChkYXRhID0gbG9nX2Nhc2VzKSArDQogIGdlb21fbGluZShtYXBwaW5nID0gYWVzKHggPSBgRGF5cyBhZnRlciAxMDB0aCBjYXNlYCwgeSA9IGBDb25maXJtZWQgQ2FzZXNgLCBjb2xvciA9IGBDb3VudHJ5L1JlZ2lvbmApLCBzaXplID0gMS41KSArDQogIGdlb21fbGluZShkYXRhID0gREJMVCwgbWFwcGluZyA9IGFlcyh4ID0gYERheXMgYWZ0ZXIgMTAwdGggY2FzZWAsIHkgPSBDYXNlcywgbGluZXR5cGUgPSBgRG91YmxpbmcgVGltZWApLCBjb2xvciA9ICJyb3lhbGJsdWU0IikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9J2xvZzEwJywgbGFiZWxzID0gbnVtYmVyKSArDQogIGxhYnModGl0bGUgPSBwYXN0ZSgiQ29uZmlybWVkIGNhc2VzIChsb2cgc2NhbGUpIGJ5IGRheXMgYWZ0ZXIgMTAwdGggY2FzZSBhcyBvZiAiLCANCiAgICAgICAgICAgICAgICAgICAgIGRhdGVfZGF0YXNldCwNCiAgICAgICAgICAgICAgICAgICAgIHNlcD0iIikpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjg1LCAwLjUpLA0KICAgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsPWFscGhhKDAuNCkpLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKw0KICAgIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJTZXQzIikgKw0KICBndWlkZXMobGluZXR5cGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxKSwgY29sb3IgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAyKSkNCmcyDQpgYGANCg0KDQoNCg0KIyMjIENhc2VzIGJ5IFJlZ2lvbg0KDQpOb3cgd2UgZ3JvdXAgb3VyIGRhdGEgc2V0IGJ5IGBEYXRlYCBhbmQgYFJlZ2lvbmAgYW5kIGFkZCB1cCB0aGUgdmFyaWFibGVzIGBDb25maXJtZWQgQ2FzZXNgLCBgIERlYXRoc2AgYW5kIGBSZWNvdmVyaWVzYCB3aXRoIGBzdW0oKWAgdG8gZ2V0IHRoZSBzdW0gb2YgYWxsIGNhc2VzIHdpdGhpbiBlYWNoIGRheSBhbmQgd2l0aGluIGVhY2ggcmVnaW9uLiBBbGwgbWlzc2luZyB2YWx1ZXMgYXJlIHRoZW4gcmVtb3ZlZCB1c2luZyBgZmlsdGVyKClgIGFuZCBgIUlzLm5hKClgLiBOb3cgd2UgZ3JvdXAgYnkgYFJlZ2lvbmAgYW5kIHVzZSBgbXV0YXRlKClgdG8gY3JlYXRlIHRoZSB2YXJpYWJsZSBgSW5jcmVhc2UgaW4gQ29uZmlybWVkIENhc2VzIGJ5IFJlZ2lvbmAgd2l0aCB0aGUgYGxhZygpYCBmdW5jdGlvbiBhbmQgdGhlIHZhcmlhYmxlIGBBY3RpdmUgQ2FzZXMgYnkgUmVnaW9uYDoNCg0KDQpgYGB7cn0NClJlZ2lvbnMgPC0gZGF0YSAlPiUgDQogIGdyb3VwX2J5KERhdGUsIFJlZ2lvbikgJT4lIA0KICBzdW1tYXJpemUoYENvbmZpcm1lZCBDYXNlcyBieSBSZWdpb25gID0gc3VtKGBDb25maXJtZWQgQ2FzZXNgKSwNCiAgICAgICAgICAgIGBEZWF0aHMgYnkgUmVnaW9uYCA9IHN1bShgRGVhdGhzYCksDQogICAgICAgICAgICBgUmVjb3ZlcmllcyBieSBSZWdpb25gID0gc3VtKGBSZWNvdmVyaWVzYCkpICU+JSANCiAgZmlsdGVyKCFpcy5uYShSZWdpb24pKSAlPiUgDQogIGdyb3VwX2J5KFJlZ2lvbikgJT4lIA0KICBtdXRhdGUoYEluY3JlYXNlIGluIENvbmZpcm1lZCBDYXNlcyBieSBSZWdpb25gID0gYENvbmZpcm1lZCBDYXNlcyBieSBSZWdpb25gIC0gbGFnKGBDb25maXJtZWQgQ2FzZXMgYnkgUmVnaW9uYCksDQogICAgICAgICBgQWN0aXZlIENhc2VzIGJ5IFJlZ2lvbmAgPSBgQ29uZmlybWVkIENhc2VzIGJ5IFJlZ2lvbmAgLSBgUmVjb3ZlcmllcyBieSBSZWdpb25gIC0gYERlYXRocyBieSBSZWdpb25gKQ0KDQpoZWFkKGFycmFuZ2UoUmVnaW9ucywgZGVzYyhEYXRlKSwgZGVzYyhgQ29uZmlybWVkIENhc2VzIGJ5IFJlZ2lvbmApKSkNCmBgYA0KDQpOb3cgd2UgY2FuIHBsb3QgdGhlIGNvbmZpcm1lZCBjYXNlcyBmb3IgQ2hpbmEsIEV1cm9wZSwgVVMgYW5kIG90aGVycyB3aXRoIGBnZ3Bsb3QoKWAuIFRoZSBYLWF4aXMgaXMgYERhdGVgIGFuZCB0aGUgWS1heGlzIGlzIHRoZSBzdW0gb2YgdGhlIGNvbmZpcm1lZCBjYXNlcyBwZXIgcmVnaW9uIChgQ29uZmlybWVkIENhc2VzIGJ5IFJlZ2lvbmApLiBOb3RlOiBUaGUgKipiYWNrdGlja3MqKiBhcmUgbmVjZXNzYXJ5IGlmIHZhcmlhYmxlIG5hbWVzIGhhdmUgc3BlY2lhbCBjaGFyYWN0ZXJzIChoZXJlIHNwYWNlcyBpbiB0aGUgdmFyaWFibGUgbmFtZSBgQ29uZmlybWVkIENhc2VzIGJ5IFJlZ2lvbmApLg0KDQoNCmBgYHtyfQ0KZzNhIDwtIGdncGxvdChkYXRhID0gUmVnaW9ucykgKw0KICBnZW9tX2xpbmUobWFwcGluZyA9IGFlcyh4ID0gRGF0ZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBgQ29uZmlybWVkIENhc2VzIGJ5IFJlZ2lvbmAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IFJlZ2lvbiksIA0KICAgICAgICAgICAgc2l6ZSA9IDEpICsNCiAgbGFicyh4ID0gIkRhdGUiLCANCiAgICAgICB5ID0gIkNvbmZpcm1lZCBDYXNlcyBieSBSZWdpb24iLA0KICAgICAgIHRpdGxlID0gcGFzdGUoIkNvbmZpcm1lZCBDYXNlcyBieSBSZWdpb24gYXMgb2YgIiwNCiAgICAgICAgICAgICAgICAgICAgIGRhdGVfZGF0YXNldCwNCiAgICAgICAgICAgICAgICAgICAgIHNlcD0iIikpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IG51bWJlcikgKw0KICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3MgPSAiMiB3ZWVrIiwgZGF0ZV9sYWJlbHMgPSAiJWQuJW0iKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4xMywgMC42KSwNCiAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD1hbHBoYSgwLjQpKSwNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQpnM2ENCmBgYA0KDQpJZiB3ZSBhbHNvIHdhbnQgdG8gZGlzcGxheSB0aGUgb3RoZXIgdHdvIHZhcmlhYmxlcyAoYERlYXRocyBieSBSZWdpb25gIGFuZCBgUmVjb3ZlcmllcyBieSBSZWdpb25gKSBpbiB0aGUgc2FtZSBwbG90LCB3ZSBmaXJzdCBoYXZlIHRvIHRyYW5zZmVyIHRoZSBkYXRhIHJlY29yZCB0byB0aGUgbG9uZyBmb3JtIHdpdGggYHRpZHlyOjpwaXZvdF9sb25nZXIoKWAuIEhlcmUgd2UgY3JlYXRlIHR3byBuZXcgdmFyaWFibGVzLCB3aGljaCB3ZSBjYWxsIGBDYXRlZ29yeWAgYW5kIGBDYXNlc2AuIFRoZSBgQ2F0ZWdvcnlgIHZhcmlhYmxlIGNvbnRhaW5zIGluZm9ybWF0aW9uIGFzIHRvIHdoZXRoZXIgaXQgaXMgYSBjb25maXJtZWQgY2FzZSwgYSBkZWF0aCBvciBhIHJlY292ZXJlZCBjYXNlLiBUaGUgdmFyaWFibGUgYENhc2VzYCBjb250YWlucyB0aGUgY291bnQgdmFsdWVzIHRoYXQgd2VyZSBwcmV2aW91c2x5IGFtb25nIHRoZSB0aHJlZSBjb21iaW5lZCB2YXJpYWJsZXMuDQoNCg0KDQpgYGB7cn0NClJlZ2lvbnNfbG9uZyA8LSBSZWdpb25zICU+JSANCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKGBDb25maXJtZWQgQ2FzZXMgYnkgUmVnaW9uYCwNCiAgICAgICAgICAgICAgICAgICAgICAgIGBEZWF0aHMgYnkgUmVnaW9uYCwNCiAgICAgICAgICAgICAgICAgICAgICAgIGBSZWNvdmVyaWVzIGJ5IFJlZ2lvbmAsDQogICAgICAgICAgICAgICAgICAgICAgICBgSW5jcmVhc2UgaW4gQ29uZmlybWVkIENhc2VzIGJ5IFJlZ2lvbmAsDQogICAgICAgICAgICAgICAgICAgICAgICBgQWN0aXZlIENhc2VzIGJ5IFJlZ2lvbmApLA0KICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiQ2F0ZWdvcnkiLA0KICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIkNhc2VzIikNCmhlYWQoUmVnaW9uc19sb25nKQ0KYGBgDQoNClRoZW4gd2UgYWRkIHRoZSBuZXcgdmFyaWFibGUgYENhdGVnb3J5YCBhcyAqKmFlc3RoZXRpY3MqKiBpbiBgZ2dwbG90KClgIChoZXJlIGFzIGBsaW5ldHlwZWApLiBUaHVzIHRoZSBsaW5lIHR5cGUgY2hhbmdlcyBkZXBlbmRpbmcgb24gdGhlIHZhcmlhYmxlIGBDYXRlZ29yeWAuIFdlIHNlbGVjdCB0aGUgbmV3bHkgY3JlYXRlZCB2YXJpYWJsZSBgQ2FzZXNgIGFzIHRoZSBZIGF4aXMsIHdoaWNoIGNvbnRhaW5zIHRoZSBjb3VudGVkIHZhbHVlcy4NClNpbmNlIHRoZSBmdW5jdGlvbiBgY291bnRyeWNvZGUoKWAgdXNlZCBhYm92ZSBjb3VsZCBub3QgYXNzaWduIG5hbWVzIHRvIGFsbCBjb3VudHJpZXMgYW5kIHRodXMgZ2VuZXJhdGVkIHNvbWUgYE5BYHMsIHdlIHJlbW92ZSB0aGVtIGluIHRoZSBzYW1lIHN0ZXAgd2l0aCBgZHJvcF9uYSgpYCwgb3RoZXJ3aXNlIHRoZXkgd291bGQgYWxzbyBiZSBzaG93biBpbiB0aGUgZmlndXJlIChhbHRlcm5hdGl2ZWx5IHdlIGNvdWxkIGFsc28gYXNzaWduIHRoZSBtaXNzaW5nIGNvdW50cmllcyBtYW51YWxseSB0byB0aGUgY29ycmVjdCBjb250aW5lbnQpLiBXaXRoIHRoZSBhcmd1bWVudCBgc2NhbGVfeF9kYXRlYCB3ZSBjaGFuZ2UgdGhlIGRhdGUgZm9ybWF0IG9mIHRoZSBYLWF4aXMgYW5kIHNldCBpdCB0byB3ZWVrbHkgc3RlcHMsIHdpdGggdGhlIGFyZ3VtZW50IGB0aGVtZSgpYCB3ZSBwb3NpdGlvbiB0aGUgbGVnZW5kIGluIHRoZSBwbG90IGFuZCBzZXQgdGhlIGJhY2tncm91bmQgdHJhbnNwYXJlbnQuDQoNCmBgYHtyfQ0KZzMgPC0gZ2dwbG90KGRhdGEgPSBmaWx0ZXIoUmVnaW9uc19sb25nLCANCiAgICAgICAgICAgICAgICAgICAgIENhdGVnb3J5ICVpbiUgYygiQ29uZmlybWVkIENhc2VzIGJ5IFJlZ2lvbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEZWF0aHMgYnkgUmVnaW9uIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJlY292ZXJpZXMgYnkgUmVnaW9uIikpKSArDQogIGdlb21fbGluZShtYXBwaW5nID0gYWVzKHggPSBEYXRlLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGBDYXNlc2AsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IFJlZ2lvbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gQ2F0ZWdvcnkpLCANCiAgICAgICAgICAgIHNpemUgPSAxKSsNCiAgbGFicyh4ID0gIkRhdGUiLCANCiAgICAgICB5ID0gIkNhc2VzIiwgDQogICAgICAgdGl0bGUgPSBwYXN0ZSgiQ2FzZXMgYnkgQ2F0ZWdvcnkgYW5kIFJlZ2lvbiBhcyBvZiAiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0ZV9kYXRhc2V0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXA9IiIpKSArDQogICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gbnVtYmVyKSArDQogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIyIHdlZWsiLCANCiAgICAgICAgICAgICAgIGRhdGVfbGFiZWxzID0gIiVkLiVtIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMiwgMC42KSwNCiAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD1hbHBoYSgwLjQpKSwNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQpnMw0KYGBgDQoNCk5vdyB3ZSBjYW4gcGxvdCB0aGUgZGFpbHkgaW5jcmVhc2UgaW4gdGhlIG51bWJlciBvZiBjYXNlcy4gSW4gYWRkaXRpb24sIHdlIHVzZSBgZ2VvbV92bGluZSgpYCB0byBhZGQgbWFya2VycyBhbmQgYGdlb21fbGFiZWwoKWAgdG8gcGxhY2Ugc29tZSBsYWJlbHMgd2hlcmUgZXZlbnRzIGhhdmUgdGFrZW4gcGxhY2UgdGhhdCBjb3VsZCBhZmZlY3QgdGhlIHNwcmVhZCBvZiB0aGUgZGlzZWFzZSwgbmFtZWx5IHRoZSB0aW1lcyBhdCB3aGljaCBkaWZmZXJlbnQgY291bnRyaWVzIHN0YXJ0ZWQgdGhlIHF1YXJhbnRpbmUgbWVhc3VyZXMuIENoaW5hLCBmb3IgZXhhbXBsZSwgc3RhcnRlZCB0aGUgSHViZWkgbG9ja2Rvd24gb24gSmFudWFyeSAyMywgYXQgYSB0aW1lIHdoZW4gdGhlcmUgd2VyZSBvbmx5IDYzOSBjb25maXJtZWQgY2FzZXMgYW5kIDE4IGRlYXRocyBpbiBDaGluYS4gSXRhbHkgc3RhcnRlZCB0aGUgY3VyZmV3IG9uIE1hcmNoIDl0aC4NCg0KDQpgYGB7cn0NCmc0IDwtIGdncGxvdChkYXRhID0gUmVnaW9ucykgKw0KICBnZW9tX2xpbmUobWFwcGluZyA9IGFlcyh4ID0gRGF0ZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBgSW5jcmVhc2UgaW4gQ29uZmlybWVkIENhc2VzIGJ5IFJlZ2lvbmAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IFJlZ2lvbiksIHNpemUgPSAxKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGFzLkRhdGUoIjIwMjAtMDEtMjMiKSkgKw0KICBnZW9tX2xhYmVsKGxhYmVsPSJIdWJlaSBcbiBxdWFyYW50aW5lIiwgeT0xNTAwMCwgeD1hcy5EYXRlKCIyMDIwLTAxLTI0IikrMSkrDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGFzLkRhdGUoIjIwMjAtMDMtMDkiKSwgY29sb3IgPSAiYmxhY2siKSArDQogIGdlb21fbGFiZWwobGFiZWw9Ikl0YWx5IFxuIGxvY2tkb3duIiwgeT0xODAwMCwgeD1hcy5EYXRlKCIyMDIwLTAzLTA5IikpICsNCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjIgd2VlayIsIGRhdGVfbGFiZWxzID0gIiVkLiVtIikgKyAgDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4yLCAwLjgpLA0KICAgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsPWFscGhhKDAuNCkpLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKw0KICBsYWJzKHRpdGxlID0gcGFzdGUoIkRhaWx5IEluY3JlYXNlIGluIENhc2VzIGFzIG9mICIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRlX2RhdGFzZXQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcD0iIikpDQpnNA0KYGBgDQoNCk5vdyB3ZSB3YW50IHRvIHBsb3QgdGhlIGFjdGl2ZSBjYXNlcyBhcyBhbiBhcmVhIHBsb3Qgb3ZlciB0aW1lLiBBbiBhcmVhIHBsb3QgaXMgY3JlYXRlZCB3aXRoIGBnZW9tX2FyZWFgIHdpdGhpbiBgZ2dwbG90KClgLg0KDQoNCmBgYHtyfQ0KZzUgPC0gZ2dwbG90KGRhdGEgPSBSZWdpb25zKSArDQogIGdlb21fYXJlYShtYXBwaW5nID0gYWVzKHggPSBEYXRlLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGBBY3RpdmUgQ2FzZXMgYnkgUmVnaW9uYCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBSZWdpb24pLCANCiAgICAgICAgICAgIHNpemUgPSAxKSArDQogIGxhYnMoeCA9ICJEYXRlIiwgDQogICAgICAgeSA9ICJBY3RpdmUgQ09WSUQtMTkgQ2FzZXMiLCANCiAgICAgICB0aXRsZSA9IHBhc3RlKCJBY3RpdmUgQ2FzZXMgYnkgUmVnaW9uIGFzIG9mICIsDQogICAgICAgICAgICAgICAgICAgIGRhdGVfZGF0YXNldCwNCiAgICAgICAgICAgICAgICAgICAgc2VwPSIiKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gbnVtYmVyKSArDQogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIyIHdlZWsiLCANCiAgICAgICAgICAgICAgIGRhdGVfbGFiZWxzID0gIiVkLiVtIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMSwgMC44KSwNCiAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD1hbHBoYSgwLjQpKSwNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iU2V0MyIpDQpnNQ0KYGBgDQoNCldlIGNhbiBhbHNvIGdlbmVyYXRlIGEgYmFyIGNoYXJ0IGluIGBnZ3Bsb3QoKWAgd2l0aCBgZ2VvbV9jb2woKWAsIGluIHdoaWNoIHRoZSBYLWF4aXMgcmVwcmVzZW50cyB0aGUgcmVnaW9uLCB0aGUgWS1heGlzIHRoZSBudW1iZXIgb2YgY2FzZXMgYW5kIHRoZSBjb2xvciAoYGZpbGxgKSB0aGUgcmVnaW9uIG9mIHRoZSB2YXJpYWJsZXMuIFRoZSBjb21tYW5kIGBmYWNldF93cmFwKH5DYXRlZ29yeSlgIGNyZWF0ZXMgYSBzZXBhcmF0ZSBwbG90IGZvciBlYWNoIGNhdGVnb3J5IGluIHRoZSBkYXRhc2V0IGFzIGEgZnVydGhlciBkaW1lbnNpb24uIFRvIGRvIHRoaXMsIGhvd2V2ZXIsIHdlIGZpcnN0IG5lZWQgdG8gY3JlYXRlIGEgdmFyaWFibGUgdGhhdCBzaG91bGQgY29udGFpbiB0aGUgbGV2ZWxzIG9mIHRoZSBgQ2F0ZWdvcnlgIHZhcmlhYmxlIHRoYXQgd2Ugd2FudCB0byB1c2UgKHdlIGRvbid0IHdhbnQgdG8gbWFwIGFsbCBsZXZlbHMgZnJvbWAgQ2F0ZWdvcnlgKToNCg0KDQpgYGB7cn0NClNlbGVjdGVkX0NhdGVnb3J5IDwtIGMoIkFjdGl2ZSBDYXNlcyBieSBSZWdpb24iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSZWNvdmVyaWVzIGJ5IFJlZ2lvbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRlYXRocyBieSBSZWdpb24iKQ0KDQpgYGANCg0KDQoNCmBgYHtyfQ0KZzYgPC0gZ2dwbG90KGRhdGEgPSBmaWx0ZXIoUmVnaW9uc19sb25nLCANCiAgICAgICAgICAgICAgICAgICAgIERhdGUgJWluJSBtYXgoUmVnaW9uc19sb25nJERhdGUpLA0KICAgICAgICAgICAgICAgICAgICAgQ2F0ZWdvcnkgJWluJSBTZWxlY3RlZF9DYXRlZ29yeSkNCiAgICAgICApICsNCiAgZ2VvbV9jb2wobWFwcGluZyA9IGFlcyh4ID0gUmVnaW9uLCANCiAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gYENhc2VzYCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IFJlZ2lvbiksIA0KICAgICAgICAgICBwb3NpdGlvbiA9ICJkb2RnZSIpICsNCiAgZmFjZXRfd3JhcCh+Q2F0ZWdvcnksIG5jb2w9NikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gbnVtYmVyKSArDQogIGxhYnModGl0bGUgPSBwYXN0ZSgiQ2FzZXMgYnkgUmVnaW9uIGFuZCBDYXRlZ29yeSBhcyBvZiAiLA0KICAgICAgICAgICAgICAgICAgICBkYXRlX2RhdGFzZXQsDQogICAgICAgICAgICAgICAgICAgIHNlcD0iIikpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIsDQogICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9YWxwaGEoMC40KSksDQogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KZzYNCmBgYA0KDQoNCg0KDQoNCiMjIyBDYXNlcyBpbiBFdXJvcGUNCg0KVGhpcyBncmFwaGljIHNob3dzIHRoZSBjYXNlIG51bWJlcnMgb2YgYWxsIEV1cm9wZWFuIGNvdW50cmllcyBpbiB3aGljaCBhdCBsZWFzdCAqKjUwMDAgY29uZmlybWVkIGNhc2VzKiogd2VyZSByZWdpc3RlcmVkIG9uICoqTWFyY2ggMzAsIDIwMjAqKi4gVG8gZG8gdGhpcywgd2UgZmlyc3QgaGF2ZSB0byBmaWx0ZXIgdGhlIGRhdGEgc2V0IHdpdGggYGZpbHRlcigpYCBmb3IgdGhlIHJlZ2lvbiBgRXVyb3BlYCwgdGhlIGRhdGUgYDIwMjAtMDMtMzBgIGFuZCBgQ29uZmlybWVkIENhc2VzPiA9IDUwMDBgIChkb24ndCBmb3JnZXQgdGhlIGJhY2sgdGlja3MpLiBUaGVuIHdlIGhhdmUgdGhlIHZhcmlhYmxlIGBDb3VudHJ5L1JlZ2lvbmAgb3V0cHV0IGZyb20gdGhpcyBvYmplY3QgYW5kIHNhdmUgdGhlIGxpc3Qgb2YgY291bnRyaWVzIHRoYXQgaGFkIG1vcmUgdGhhbiA1MDAwIGNvbmZpcm1lZCBjYXNlcyBvbiBNYXJjaCAzMHRoLCAyMDIwIGluIHRoZSBvYmplY3QgYEV1cm9wZV81MDAwX2xpc3RgLiBOb3cgd2UgaGF2ZSBhIGxpc3Qgb2YgYWxsIG9mIHRoZXNlIGNvdW50cmllcyBhbmQgd2UgZmlsdGVyIG91ciBkYXRhIHNldCBieSBjb3VudHJpZXMgdGhhdCBhcHBlYXIgaW4gdGhpcyBsaXN0LiBUaGVuIHdlIGNyZWF0ZSBhbiBhcmVhIHBsb3Q6DQoNCg0KDQpgYGB7cn0NCkV1cm9wZV81MDAwX2xpc3QgPC0gZmlsdGVyKGRhdGEsIFJlZ2lvbiA9PSAiRXVyb3BlIiAmDQogICAgICAgICAgIERhdGUgPT0gYXMuRGF0ZSgiMjAyMC0wMy0zMCIpICYNCiAgICAgICAgICAgYENvbmZpcm1lZCBDYXNlc2AgPj0gNTAwMCkkYENvdW50cnkvUmVnaW9uYA0KDQpFdXJvcGU1MDAwIDwtIGZpbHRlcihkYXRhLCANCiAgICAgICAgICAgICAgICAgIFJlZ2lvbiA9PSJFdXJvcGUiICYgDQogICAgICAgICAgICAgICAgICAgIGBDb3VudHJ5L1JlZ2lvbmAgJWluJSBFdXJvcGVfNTAwMF9saXN0ICYgDQogICAgICAgICAgICAgICAgICAgIERhdGUgPj0gYXMuRGF0ZSgiMjAyMC0wMi0yNCIpKQ0KYGBgDQoNCmBgYHtyfQ0KZzcgPC0gZ2dwbG90KGRhdGEgPSBFdXJvcGU1MDAwKSArDQogIGdlb21fYXJlYShtYXBwaW5nID0gYWVzKHggPSBEYXRlLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGBDb25maXJtZWQgQ2FzZXNgLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGBDb3VudHJ5L1JlZ2lvbmApKSArDQogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIyIHdlZWsiLCBkYXRlX2xhYmVscyA9ICIlZC4lbSIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IG51bWJlcikgKw0KICBsYWJzKHggPSAiRGF0ZSIsIA0KICAgICAgIHkgPSAiQ29uZmlybWVkIENhc2VzIiwgDQogICAgICAgdGl0bGUgPSBwYXN0ZSgiQ29uZmlybWVkIENhc2VzIGluIEV1cm9wZSBhcyBvZiAiLA0KICAgICAgICAgICAgICAgICAgICAgZGF0ZV9kYXRhc2V0LA0KICAgICAgICAgICAgICAgICAgICAgc2VwPSIiKSkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMTUsIDAuNiksDQogICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9YWxwaGEoMC40KSksDQogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArIA0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlPSJTZXQzIikNCmc3DQpgYGANCg0KDQpXZSBjcmVhdGUgdGhlIHRocmVlIG90aGVyIGdyYXBoaWNzIGluIHRoZSBzYW1lIHdheSwgZXhjZXB0IHRoYXQgd2UgZG8gbm90IGhhdmUgdG8gZG8gdGhlIHByZXZpb3VzIGZpbHRlcmluZyBhZ2FpbjoNCg0KYGBge3J9DQpnOCA8LSBnZ3Bsb3QoZGF0YSA9IEV1cm9wZTUwMDApICsNCiAgZ2VvbV9hcmVhKG1hcHBpbmcgPSBhZXMoeCA9IERhdGUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gYERlYXRoc2AsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gYENvdW50cnkvUmVnaW9uYCkpICsNCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjIgd2VlayIsIGRhdGVfbGFiZWxzID0gIiVkLiVtIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gbnVtYmVyKSArDQogIGxhYnMoeCA9ICJEYXRlIiwgDQogICAgICAgeSA9ICJEZWF0aHMiLCANCiAgICAgICB0aXRsZSA9IHBhc3RlKCJEZWF0aHMgaW4gRXVyb3BlIGFzIG9mICIsDQogICAgICAgICAgICAgICAgICAgIGRhdGVfZGF0YXNldCwNCiAgICAgICAgICAgICAgICAgICAgc2VwPSIiKSkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMTUsIDAuNiksDQogICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9YWxwaGEoMC40KSksDQogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArIA0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlPSJTZXQzIikNCmc4DQpgYGANCg0KDQpgYGB7cn0NCmc5IDwtIGdncGxvdChkYXRhID0gRXVyb3BlNTAwMCkgKw0KICBnZW9tX2FyZWEobWFwcGluZyA9IGFlcyh4ID0gRGF0ZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBgUmVjb3Zlcmllc2AsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gYENvdW50cnkvUmVnaW9uYCkpICsNCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjIgd2VlayIsIGRhdGVfbGFiZWxzID0gIiVkLiVtIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gbnVtYmVyKSArDQogIGxhYnMoeCA9ICJEYXRlIiwgDQogICAgICAgeSA9ICJSZWNvdmVyaWVzIiwgDQogICAgICAgdGl0bGUgPSBwYXN0ZSgiUmVjb3ZlcmllcyBpbiBFdXJvcGUgYXMgb2YgIiwNCiAgICAgICAgICAgICAgICAgICAgZGF0ZV9kYXRhc2V0LA0KICAgICAgICAgICAgICAgICAgICBzZXA9IiIpKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4xNSwgMC42KSwNCiAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD1hbHBoYSgwLjQpKSwNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsgDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGU9IlNldDMiKQ0KZzkNCmBgYA0KDQoNCg0KYGBge3J9DQpnMTAgPC0gZ2dwbG90KGRhdGEgPSBFdXJvcGU1MDAwKSArDQogIGdlb21fYXJlYShtYXBwaW5nID0gYWVzKHggPSBEYXRlLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGBBY3RpdmUgQ2FzZXNgLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGBDb3VudHJ5L1JlZ2lvbmApKSArDQogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIyIHdlZWsiLCBkYXRlX2xhYmVscyA9ICIlZC4lbSIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IG51bWJlcikgKw0KICBsYWJzKHRpdGxlID0gcGFzdGUoIkFjdGl2ZSBDYXNlcyBpbiBFdXJvcGUgYXMgb2YgIiwNCiAgICAgICAgICAgICAgICAgICAgZGF0ZV9kYXRhc2V0LA0KICAgICAgICAgICAgICAgICAgICBzZXA9IiIpKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4xNSwgMC42KSwNCiAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD1hbHBoYSgwLjQpKSwNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsgDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGU9IlNldDMiKQ0KZzEwDQpgYGANCg0KDQoNCg0KDQojIyMgQ2FzZXMgaW4gR2VybWFueQ0KDQpJbiBvcmRlciB0byBtYXAgYWxsIHRocmVlIGNhdGVnb3JpZXMgb2YgY2FzZXMgaW4gR2VybWFueSBpbiBhIHN1bW1hcml6aW5nIGFyZWEgcGxvdCwgd2UgZmlyc3QgaGF2ZSB0byBncm91cCB0aGUgZGF0YSBzZXQgYWNjb3JkaW5nIHRvIGBDb3VudHJ5L1JlZ2lvbmAgYW5kIHRoZW4gZmlsdGVyIGl0IGFjY29yZGluZyB0byBgR2VybWFueWAuIFRoZW4gd2Ugc29ydCB0aGUgZGF0YSBzZXQgYnkgYERhdGVgIGluIGFzY2VuZGluZyBvcmRlciBhbmQgY3JlYXRlIHRoZSB2YXJpYWJsZSBgQWJzb2x1dGUgSW5jcmVhc2UgaW4gQ29uZmlybWVkIENhc2VzYCB1c2luZyB0aGUgYGxhZygpYCBmdW5jdGlvbiBhbmQgdGhlIHZhcmlhYmxlIGBQZXJjZW50YWdlIEluY3JlYXNlIGluIENvbmZpcm1lZCBDYXNlc2AuDQoNCk5vdyB3ZSBjYW4gY29udmVydCB0aGlzIGRhdGEgcmVjb3JkIGludG8gdGhlIGxvbmcgZm9ybSB3aXRoIGBwaXZvdF9sb25nZXIoKWAuDQoNCmBgYHtyfQ0KR2VybWFueSA8LSBkYXRhICU+JSANCiAgZ3JvdXBfYnkoYENvdW50cnkvUmVnaW9uYCkgJT4lIA0KICBmaWx0ZXIoYENvdW50cnkvUmVnaW9uYCA9PSAiR2VybWFueSIpICU+JSANCiAgYXJyYW5nZShEYXRlKSAlPiUgDQogIG11dGF0ZShgQWJzb2x1dGUgSW5jcmVhc2UgaW4gQ29uZmlybWVkIENhc2VzYCA9IGxhZyhgQ29uZmlybWVkIENhc2VzYCksDQogICAgICAgICBgUGVyY2VudGFnZSBJbmNyZWFzZSBpbiBDb25maXJtZWQgQ2FzZXNgID0gKChgQ29uZmlybWVkIENhc2VzYCAvIGBBYnNvbHV0ZSBJbmNyZWFzZSBpbiBDb25maXJtZWQgQ2FzZXNgKS0xKSoxMDApDQogIA0KDQoNCkdlcm1hbnlfbG9uZyA8LSBwaXZvdF9sb25nZXIoZGF0YSA9IEdlcm1hbnksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29scyA9IGMoYENvbmZpcm1lZCBDYXNlc2AsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgRGVhdGhzYCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBSZWNvdmVyaWVzYCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBBY3RpdmUgQ2FzZXNgLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYEFic29sdXRlIEluY3JlYXNlIGluIENvbmZpcm1lZCBDYXNlc2AsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgUGVyY2VudGFnZSBJbmNyZWFzZSBpbiBDb25maXJtZWQgQ2FzZXNgKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkNhdGVnb3J5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJDYXNlcyIpDQpgYGANCg0KQmVmb3JlIGNyZWF0aW5nIGFuIGFyZWEgcGxvdCBmb3IgR2VybWFueSwgd2UgaGF2ZSB0byB3cmFuZ2xlIGEgbGl0dGxlIG1vcmUuIEJlY2F1c2UgdGhlIGNhdGVnb3JpZXMgYXJlIHVzdWFsbHkgY29sb3JlZCBpbiBhbiBhbHBoYWJldGljYWwgb3JkZXIsIHRoaXMgd291bGQgZGlzdG9ydCB0aGUgb3JkZXIgaW4gd2hpY2ggd2Ugd291bGQgbGlrZSB0byBwcmVzZW50IHRoZSBkYXRhLiBCeSB1c2luZyBgZmFjdG9yKClgLCB3ZSByZW9yZGVyIHRoZSBmYWN0b3IgbGV2ZWxzIG9mIHRoZSBgQ2F0ZWdvcnlgIHZhcmlhYmxlIGluIHRoZSBvcmRlciB0aGF0IHdlIHdvdWxkIGxpa2UgdG8gcHJlc2VudCB0aGVtLiBTaW5jZSB3ZSBoYXZlIG1vcmUgbGV2ZWxzIHdpdGhpbiB0aGUgYENhdGVnb3J5YCB2YXJpYWJsZSBpbiBvdXIgbG9uZyBkYXRhc2V0IHRoYW4gd2Ugd2FudCB0byBtYXAsIHdlIGZpbHRlciBhZ2FpbiBpbiBnZ3Bsb3QgZm9yIHRoZSB0aHJlZSB2YXJpYWJsZXMgYEFjdGl2ZSBDYXNlc2AsIGBSZWNvdmVyaWVzYCBhbmQgYERlYXRoc2AuIA0KDQpgYGB7cn0NCkdlcm1hbnlfbG9uZyRDYXRlZ29yeSA8LSBmYWN0b3IoR2VybWFueV9sb25nJENhdGVnb3J5LCBsZXZlbHMgPSBjKCJBY3RpdmUgQ2FzZXMiLCAiUmVjb3ZlcmllcyIsICJEZWF0aHMiLCAiQWJzb2x1dGUgSW5jcmVhc2UgaW4gQ29uZmlybWVkIENhc2VzIiwgIlBlcmNlbnRhZ2UgSW5jcmVhc2UgaW4gQ29uZmlybWVkIENhc2VzIiwgIkNvbmZpcm1lZCBDYXNlcyIpKQ0KDQpnMTEgPC0gZ2dwbG90KGRhdGEgPSBmaWx0ZXIoR2VybWFueV9sb25nLCANCiAgICAgICAgICAgICAgICAgICAgIENhdGVnb3J5ID09ICJBY3RpdmUgQ2FzZXMiIHwNCiAgICAgICAgICAgICAgICAgICAgICAgQ2F0ZWdvcnkgPT0gIlJlY292ZXJpZXMiIHwNCiAgICAgICAgICAgICAgICAgICAgICAgQ2F0ZWdvcnkgPT0gIkRlYXRocyIsIERhdGUgPj0gYXMuRGF0ZSgiMjAyMC0wMy0wMSIpKSkgKw0KICBnZW9tX2FyZWEobWFwcGluZyA9IGFlcyh4ID0gRGF0ZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBgQ2FzZXNgLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IENhdGVnb3J5KSkgKw0KICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3MgPSAiMiB3ZWVrIiwgZGF0ZV9sYWJlbHMgPSAiJWQuJW0iKSArDQogIGxhYnMoeCA9ICJEYXRlIiwgDQogICAgICAgeSA9ICJDb25maXJtZWQgQ2FzZXMgaW4gR2VybWFueSIsDQogICAgICAgdGl0bGUgPSBwYXN0ZSgiQ29uZmlybWVkIENhc2VzIGluIEdlcm1hbnkgYXMgb2YgIiwNCiAgICAgICAgICAgICAgICAgICAgZGF0ZV9kYXRhc2V0LA0KICAgICAgICAgICAgICAgICAgICBzZXA9IiIpKSArIA0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMiwgMC44KSwNCiAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD1hbHBoYSgwLjQpKSwNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsgDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGU9IlNldDMiKQ0KZzExDQpgYGANCg0KDQoNCk5vdyB3ZSB1c2UgYGdncGxvdCgpYCBhbmQgYGdlb21fY29sKClgIHRvIGNyZWF0ZSBhIGJhciBjaGFydCBhbmQgaW5zZXJ0IGEgZmV3IGxhYmVscy4gV2UgY2FuIHJlYWQgdGhlIGNhc2UgbnVtYmVycyBvZiB0aGUgbGFiZWxzIGZyb20gdGhlIGZpbHRlcmVkIGRhdGEgc2V0LiBXaXRoaW4gYGdncGxvdCgpYCB3ZSBmaWx0ZXIgZm9yIGRhdGEgdGhhdCBpcyBuZXdlciB0aGFuIEZlYiAyNCwgMjAyMC4NCg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0KZzEyIDwtIGdncGxvdChkYXRhID0gZmlsdGVyKEdlcm1hbnksIERhdGUgPj0gYXMuRGF0ZSgiMjAyMC0wMi0yNCIpKSkgKw0KICBnZW9tX2NvbChtYXBwaW5nID0gYWVzKHggPSBEYXRlLCANCiAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gYFBlcmNlbnRhZ2UgSW5jcmVhc2UgaW4gQ29uZmlybWVkIENhc2VzYCksIA0KICAgICAgICAgICBmaWxsID0gIiM2Njk5ZmYiKSArDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9c2VxKDAsbWF4KEdlcm1hbnkkYFBlcmNlbnRhZ2UgSW5jcmVhc2UgaW4gQ29uZmlybWVkIENhc2VzYCwgbmEucm0gPSBUKSwgMTApKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGFzLkRhdGUoIjIwMjAtMDMtMDYiKSkgKw0KICBnZW9tX2xhYmVsKGxhYmVsPSI2NzAgQ2FzZXMiLCB5PTcwLCB4PWFzLkRhdGUoIjIwMjAtMDMtMDYiKSkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBhcy5EYXRlKCIyMDIwLTAzLTE0IikpICsNCiAgZ2VvbV9sYWJlbChsYWJlbD0iNDU4NSBDYXNlcyIsIHk9NjAsIHg9YXMuRGF0ZSgiMjAyMC0wMy0xNCIpKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGFzLkRhdGUoIjIwMjAtMDMtMjEiKSkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBhcy5EYXRlKCIyMDIwLTAzLTIzIikpICsNCiAgZ2VvbV9sYWJlbChsYWJlbD0iQmF2YXJpYSBcbiBsb2NrZG93biwgXG4gMjIyMTMgQ2FzZXMiLCB5PTc1LCB4PWFzLkRhdGUoIjIwMjAtMDMtMjAiKSkgKw0KICBnZW9tX2xhYmVsKGxhYmVsPSJDb3VudHJ5d2lkZSBcbiBSZXN0cmFpbmluZyBcbiBPcmRlciwgXG4gMjkwNTYgQ2FzZXMiLCB5PTQwLCB4PWFzLkRhdGUoIjIwMjAtMDMtMjUiKSkgKw0KICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3MgPSAiMiB3ZWVrIiwgZGF0ZV9sYWJlbHMgPSAiJWQuJW0iKSArIA0KICBsYWJzKHkgPSAiUGVyY2VudCBJbmNyZWFzZSIsIA0KICAgICAgIHRpdGxlID0gcGFzdGUoIlBlcmNlbnRhZ2UgSW5jcmVhc2UgaW4gQ29uZmlybWVkIENhc2VzIGluIEdlcm1hbnkgYXMgb2YgIiwNCiAgICAgICAgICAgICAgICAgICAgIGRhdGVfZGF0YXNldCwNCiAgICAgICAgICAgICAgICAgICAgIHNlcD0iIikpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjksIDAuOCksDQogICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9YWxwaGEoMC40KSksDQogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KZzEyDQpgYGAgDQoNCg0KDQpJbiB0aGUgZm9sbG93aW5nLCB3ZSB3b3VsZCBsaWtlIHRvIGNyZWF0ZSBhIGJhciBjaGFydCB3aXRoIHRoZSBjYXNlIG51bWJlcnMgaW4gR2VybWFueSBhbmQgY2FsY3VsYXRlIGFuIGV4cG9uZW50aWFsIGZ1bmN0aW9uIGJhc2VkIG9uIHRoZSBjYXNlIG51bWJlcnMgZnJvbSBNYXJjaCAxNHRoIHRvIDIwdGgsIDIwMjAuIFdlIHdhbnQgdG8gZHJhdyB0aGUgdmFsdWVzIGFmdGVyIE1hcmNoIDIwLCAyMDIwIGluICoqbGlnaHQgYmx1ZSoqLCB0aGUgdmFsdWVzIGJlZm9yZSBNYXJjaCAyMCwgMjAyMCBzaG91bGQgYmUgKipkYXJrYmx1ZSoqLg0KDQpGb3IgdGhpcyB3ZSBmaXJzdCBmaWx0ZXIgdGhlIGRhdGEgc2V0IHRvIHRoZSBkYXRhIGZyb20gRmViIDIzLCAyMDIwIHRvIE1hciAzMCwgMjAyMC4gRm9yIHRoZSBkYXJrIGJsdWUgYmFycywgd2UgZmlsdGVyIGFnYWluIGFuZCBvbmx5IHNlbGVjdCB0aGUgZGF0ZXMgdGhhdCBsaWUgYmV0d2VlbiBNYXIgMSwgMjAyMCBhbmQgTWFyIDIwLCAyMDIwLiBPbiB0aGVzZSBkYXlzLCB0aGUgY2FzZXMgcm9zZSBzdHJvbmdlciB0aGFuIGFmdGVyIE1hcmNoIDIwLCAyMDIwLiBXZSB3YW50IHRvIGNvbG9yIHRoaXMgZGF0YSBkYXJrIGJsdWUuDQoNCk5vdyB3ZSBmaWx0ZXIgdGhlIGRhdGFzZXQgYWdhaW4gb24gdGhlIGRhdGEgZnJvbSBNYXJjaCAxNCB0byBNYXJjaCAyMCwgMjAyMCwgYmVjYXVzZSB3ZSBvbmx5IHdhbnQgdG8gdXNlIHRoaXMgZGF0YSBmb3IgdGhlIGNhbGN1bGF0aW9uIG9mIHRoZSBtb2RlbC4gV2UgdXNlIHRoaXMgZGF0YXNldCB0byBjYWxjdWxhdGUgdGhlIGxvZ2FyaXRobSBvZiB0aGUgY29uZmlybWVkIGNhc2VzLiBUaGVuIHdlIGNhbGN1bGF0ZSBhIGxpbmVhciByZWdyZXNzaW9uIHdpdGggYGxtKClgIG9uIHRoZSBsb2ctdHJhbnNmb3JtZWQgZGF0YS4gV2UgdGhlbiB1c2UgdGhlIGBzZXEoKWAgZnVuY3Rpb24gdG8gY3JlYXRlIGEgc2VxdWVuY2Ugb2YgZGF5cyBmb3Igd2hpY2ggd2Ugd2FudCB0byB1c2Ugb3VyIG1vZGVsIHRvIHByZWRpY3QgdGhlIG51bWJlciBvZiBjYXNlcy4gV2l0aCB0aGUgZnVuY3Rpb24gYHByZWRpY3QubG0oKWAgd2UgY2FsY3VsYXRlIHRoZSBzaW11bGF0ZWQgdmFsdWVzIGZvciB0aGlzIHNlcXVlbmNlIGFuZCB0aGVuIGV4cG9zZSB0aGVtIGFnYWluIHdpdGggYGV4cCgpYCBhbmQgc2F2ZSB0aGVtIGluIHRoZSBvYmplY3QgYHByZWRpY3Rpb25gIGFzIGEgdmFyaWFibGUgYG1vZGVsYC4gTm93IHdlIGpvaW4gdGhlIG5ld2x5IGNyZWF0ZWQgZGF0YXNldCB0byBvdXIgcHJldmlvdXNseSBmaWx0ZXJlZCBkYXRhc2V0IGBwcmVkaWN0aW9uYCB1c2luZyB0aGUgYGZ1bGxfam9pbigpYCBmdW5jdGlvbi4NCg0KDQpgYGB7cn0NCmFsbF9kYXRhIDwtIEdlcm1hbnkgJT4lIA0KICBmaWx0ZXIoRGF0ZSA+PSBhcy5EYXRlKCIyMDIwLTAyLTIzIikgJiBEYXRlIDw9IG1heChkYXRhJERhdGUpKQ0KDQpkYXJrYmx1ZSA8LSBHZXJtYW55ICU+JSANCiAgZmlsdGVyKERhdGUgPj0gYXMuRGF0ZSgiMjAyMC0wMy0wMSIpICYgRGF0ZSA8PSBhcy5EYXRlKCIyMDIwLTAzLTIwIikpDQoNCm1vZGVsZGF0YSA8LSBHZXJtYW55ICU+JSANCiAgZmlsdGVyKERhdGUgPj0gYXMuRGF0ZSgiMjAyMC0wMy0xNCIpICYgRGF0ZSA8PSBhcy5EYXRlKCIyMDIwLTAzLTIwIikpICU+JSANCiAgbXV0YXRlKGBMb2cgQmVzdMOkdGlndGUgQ2FzZXNgID0gbG9nKGBDb25maXJtZWQgQ2FzZXNgKSkNCg0KbW9kZWxsIDwtIGxtKGBMb2cgQmVzdMOkdGlndGUgQ2FzZXNgIH4gRGF0ZSwgZGF0YSA9IG1vZGVsZGF0YSkNCkRhdGUgPC0gc2VxKGFzLkRhdGUoIjIwMjAtMDMtMTQiKSwgYXMuRGF0ZSgiMjAyMC0wMy0zMCIpLCBieSA9IDEpDQpwcmVkaWN0aW9uIDwtIGRhdGEuZnJhbWUoRGF0ZSkNCnByZWRpY3Rpb24kbW9kZWxsIDwtIGV4cChwcmVkaWN0LmxtKG1vZGVsbCwgbmV3ZGF0YSA9IHByZWRpY3Rpb24pKSANCg0KYWxsX2RhdGFfcHJlZGljdGlvbiA8LSBmdWxsX2pvaW4oYWxsX2RhdGEsIHByZWRpY3Rpb24sIGJ5ID0gIkRhdGUiKQ0KYGBgDQoNCk5vdyBieSB1c2luZyBgZ2dwbG90KClgIHdlIGNhbiBmaXJzdCBjcmVhdGUgYSBiYXIgY2hhcnQgZGF0YSBzZXQgYGFsbF9kYXRhX3ByZWRpY3Rpb25gIGFuZCBjaG9vc2UgbGlnaHQgYmx1ZSAoYHJveWFsYmx1ZTFgKSBhcyB0aGUgY29sb3IuIFRoZW4gd2UgY3JlYXRlIGEgc2Vjb25kIGJhciBjaGFydCBvZiB0aGUgb2JqZWN0IGBkYXJrYmx1ZWAsIHdoaWNoIG9ubHkgY29udGFpbnMgdGhlIHZhbHVlcyALC2Zyb20gTWFyIDEgdG8gTWFyIDIwLCAyMDIwIGFuZCBjb2xvciBpdCBkYXJrYmx1ZSAoYHJveWFsYmx1ZTRgKS4gVGhlbiB3ZSBhZGQgYSBsaW5lIHBsb3Qgd2l0aCBgZ2VvbV9saW5lKClgLCB3aGljaCBjb250YWlucyB0aGUgcHJlZGljdGVkIGNhc2UgbnVtYmVycyAoYG1vZGVsYCkgYmFzZWQgb24gdGhlIGRhdGEgZnJvbSBNYXJjaCAxNHRoIHRvIE1hcmNoIDIwdGgsIDIwMjAuDQoNCkZpbmFsbHksIHdlIGFkZCBzb21lIGxhYmVscyAoYGdlb21fbGFiZWwoKWApIGFuZCB2ZXJ0aWNhbCBsaW5lcyAoYGdlb21fdmxpbmUoKWApLCBzZXQgdGhlIGRhdGUgdG8gYW4gaW50ZXJ2YWwgb2YgNSBkYXlzIGFuZCBjaGFuZ2UgdGhlIGZvcm1hdHRpbmcgb2YgdGhlIGRhdGUgb24gdGhlIFggYXhpcy4NCg0KYGBge3J9DQpnMTMgPC0gZ2dwbG90KGRhdGEgPSBhbGxfZGF0YV9wcmVkaWN0aW9uKSArDQogIGdlb21fY29sKG1hcHBpbmcgPSBhZXMoeCA9IERhdGUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBgQ29uZmlybWVkIENhc2VzYCksIA0KICAgICAgICAgICBmaWxsID0icm95YWxibHVlMSIpICsNCiAgZ2VvbV9jb2woZGF0YSA9IGRhcmtibHVlLCANCiAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gRGF0ZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGBDb25maXJtZWQgQ2FzZXNgKSwgDQogICAgICAgICAgIGZpbGwgPSJyb3lhbGJsdWU0IikgKw0KICBnZW9tX2xpbmUobWFwcGluZyA9IGFlcyh4ID0gRGF0ZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBtb2RlbGwpLCANCiAgICAgICAgICAgIGNvbG9yID0gInJlZDQiLCBzaXplID0gMSkgKyANCiAgDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGFzLkRhdGUoIjIwMjAtMDMtMDYiKSkgKw0KICBnZW9tX2xhYmVsKGxhYmVsPSI2NzAgQ2FzZXMiLCANCiAgICAgICAgICAgICB5PTIwMDAwLCANCiAgICAgICAgICAgICB4PWFzLkRhdGUoIjIwMjAtMDMtMDYiKSkgKw0KICAgIA0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBhcy5EYXRlKCIyMDIwLTAzLTE0IikpICsNCiAgZ2VvbV9sYWJlbChsYWJlbD0iNDU4NSBDYXNlcyIsIA0KICAgICAgICAgICAgIHk9NTAwMDAsIA0KICAgICAgICAgICAgIHg9YXMuRGF0ZSgiMjAyMC0wMy0xNCIpKSArDQogIA0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBhcy5EYXRlKCIyMDIwLTAzLTIxIikpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYXMuRGF0ZSgiMjAyMC0wMy0yMyIpKSArDQogIA0KICBnZW9tX2xhYmVsKGxhYmVsPSJCYXZhcmlhIFxuIGxvY2tkb3duLCBcbiAyMjIxMyBDYXNlcyIsIA0KICAgICAgICAgICAgIHk9ODAwMDAsIA0KICAgICAgICAgICAgIHg9YXMuRGF0ZSgiMjAyMC0wMy0yMCIpKSArDQogIGdlb21fbGFiZWwobGFiZWw9IkNvdW50cnl3aWRlIFxuIFJlc3RyYWluaW5nIFxuIE9yZGVyLCBcbiAyOTA1NiBDYXNlcyIsIA0KICAgICAgICAgICAgIHk9MTUwMDAwLCANCiAgICAgICAgICAgICB4PWFzLkRhdGUoIjIwMjAtMDMtMjMiKSkgKw0KICANCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYXMuRGF0ZSgiMjAyMC0wMy0zMCIpKSArDQogIGdlb21fbGFiZWwobGFiZWw9IjIyOTEwOCBcbiBDYXNlcyIsIA0KICAgICAgICAgICAgIHk9MjI1MDAwLCANCiAgICAgICAgICAgICB4PWFzLkRhdGUoIjIwMjAtMDMtMzAiKSwgDQogICAgICAgICAgICAgY29sb3IgPSAicmVkIikgKw0KICBnZW9tX2xhYmVsKGxhYmVsPSI2Njg4NSBcbiBDYXNlcyIsIA0KICAgICAgICAgICAgIHk9OTUwMDAsIA0KICAgICAgICAgICAgIHg9YXMuRGF0ZSgiMjAyMC0wMy0zMCIpKSArDQogIA0KICBsYWJzKHggPSAiRGF0ZSIsIA0KICAgICAgIHkgPSAiQ29uZmlybWVkIENhc2VzIiwgIA0KICAgICAgIHRpdGxlID0gcGFzdGUoIkNvbmZpcm1lZCBDYXNlcyBpbiBHZXJtYW55IGFzIG9mICIsDQogICAgICAgICAgICAgICAgICAgICBkYXRlX2RhdGFzZXQsDQogICAgICAgICAgICAgICAgICAgICBzZXA9IiIpKSArDQogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIyIHdlZWsiLCBkYXRlX2xhYmVscyA9ICIlZC4lbSIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQpnMTMNCmBgYA0KDQoNCg0KDQoNClRoaXMgcGFnZSBpcyBjb25zdGFudGx5IGJlaW5nIHVwZGF0ZWQuIExhc3QgZWRpdGVkIG9uIGByIGRhdGVfbW9kaWZpZWRgLg0KDQpEYXRlIG9mIHRoZSBkYXRhc2V0OiBgciBkYXRlX2RhdGFzZXRgDQogIA0KPGJyLz48YnIvPjxici8+PGJyLz48YnIvPjxici8+PGJyLz48YnIvPjxici8+PGJyLz48YnIvPjxici8+PGJyLz4NCiAgDQoNCg0KDQojIENvbnRhY3QgdXM6DQo8ZGl2Pg0KPHA+PHN0cm9uZz5TdGF0U29mdCAoRXVyb3BlKSBHbWJIPC9zdHJvbmc+PGJyPlBvc3Ntb29yd2VnIDE8YnI+IDIyMzAxIEhhbWJ1cmc8YnI+IEdlcm1hbnk8L3A+DQoNCjxwPkZvbiArNDkgNDAgMjIgODUgOTAwLTA8YnI+IEZheCArNDkgNDAgMjIgODUgOTAwLTc3PGJyPiANCkUtTWFpbCA8YSBocmVmPSJtYWlsdG86aW5mb0BzdGF0c29mdC5kZSIgdGl0bGU9IkVtYWlsIEFkZHJlc3MiIGNsYXNzPSJtYWlsIj5pbmZvQHN0YXRzb2Z0LmRlPC9hPg0KPGJyPg0KSW50ZXJuZXQ6IDxhIGhyZWY9Imh0dHBzOi8vd3d3LnN0YXRzb2Z0LmRlIj53d3cuc3RhdHNvZnQuZGU8L2E+PC9wPg0KPHA+PGEgaHJlZj0iaHR0cHM6Ly93d3cuc3RhdHNvZnQuZGUvZGUvaW1wcmVzc3VtIj5JbXByaW50PC9hPjxicj4NCjxhIGhyZWY9Imh0dHBzOi8vd3d3LnN0YXRzb2Z0LmRlL2RlL2RhdGVuc2NodXR6Ij5EYXRhIFByb3RlY3Rpb248L2E+PC9wPg0KPC9kaXY+