Kable package

In a previous weeks, we saw R Markdown in action, where multiple things can be created in one location: code, commentary, and output.

In this chapter we will explore package which will facilitate the creation of presentation-worthy tables: “kableExtra”.

Let’s work with the cross-sectional data on the credit history for a sample of applicants for a type of credit card.

data(CreditCard)
cardHead <- head(CreditCard)
cardHead
##   card reports      age income       share expenditure owner selfemp dependents
## 1  yes       0 37.66667 4.5200 0.033269910  124.983300   yes      no          3
## 2  yes       0 33.25000 2.4200 0.005216942    9.854167    no      no          3
## 3  yes       0 33.66667 4.5000 0.004155556   15.000000   yes      no          4
## 4  yes       0 30.50000 2.5400 0.065213780  137.869200    no      no          0
## 5  yes       0 32.16667 9.7867 0.067050590  546.503300   yes      no          2
## 6  yes       0 23.25000 2.5000 0.044438400   91.996670    no      no          0
##   months majorcards active
## 1     54          1     12
## 2     34          1     13
## 3     58          1      5
## 4     25          1      7
## 5     64          1      5
## 6     54          1      1


cardHead %>%
  kbl()
card reports age income share expenditure owner selfemp dependents months majorcards active
yes 0 37.66667 4.5200 0.0332699 124.983300 yes no 3 54 1 12
yes 0 33.25000 2.4200 0.0052169 9.854167 no no 3 34 1 13
yes 0 33.66667 4.5000 0.0041556 15.000000 yes no 4 58 1 5
yes 0 30.50000 2.5400 0.0652138 137.869200 no no 0 25 1 7
yes 0 32.16667 9.7867 0.0670506 546.503300 yes no 2 64 1 5
yes 0 23.25000 2.5000 0.0444384 91.996670 no no 0 54 1 1


Let’s tweak the appearance of this with the “align” and the “caption” arguments.

The align argument takes a character vector with letters “l”, “c”, or “r” - specifying where you want the columns to be aligned.

The caption argument gives a caption to the table.


base <- cardHead %>%
  kbl(align = c(rep("c", 7), rep("r", 5)), caption = "kable example with card data")
base
kable example with card data
card reports age income share expenditure owner selfemp dependents months majorcards active
yes 0 37.66667 4.5200 0.0332699 124.983300 yes no 3 54 1 12
yes 0 33.25000 2.4200 0.0052169 9.854167 no no 3 34 1 13
yes 0 33.66667 4.5000 0.0041556 15.000000 yes no 4 58 1 5
yes 0 30.50000 2.5400 0.0652138 137.869200 no no 0 25 1 7
yes 0 32.16667 9.7867 0.0670506 546.503300 yes no 2 64 1 5
yes 0 23.25000 2.5000 0.0444384 91.996670 no no 0 54 1 1


A key function, where we can enjoy much of the configuration for the table, is via kable_styling().

We have options “bootstrap_options” or “latex_options”, where the latter requires the use of the package “tinytex” and a local installation of LaTeX.

Possible options for “bootstrap_options” include ‘basic’, ‘striped’, ‘bordered’, ‘hover’, ‘condensed’, ‘responsive’, and none.

Possible for “latex_options” include ‘basic’, ‘striped’, ‘hold_position’, ‘HOLD_position’, ‘scale_down’, and ‘repeat_header’.


base %>%
  kable_styling(bootstrap_options = "striped")
kable example with card data
card reports age income share expenditure owner selfemp dependents months majorcards active
yes 0 37.66667 4.5200 0.0332699 124.983300 yes no 3 54 1 12
yes 0 33.25000 2.4200 0.0052169 9.854167 no no 3 34 1 13
yes 0 33.66667 4.5000 0.0041556 15.000000 yes no 4 58 1 5
yes 0 30.50000 2.5400 0.0652138 137.869200 no no 0 25 1 7
yes 0 32.16667 9.7867 0.0670506 546.503300 yes no 2 64 1 5
yes 0 23.25000 2.5000 0.0444384 91.996670 no no 0 54 1 1


Next, we can customize the look and feel of particular rows and columns.

Let’s see an example here, where we make the last three rows blue.

base %>%
  kable_styling(bootstrap_options = "bordered") %>%
  column_spec(8:12, bold = T) %>%
  row_spec(4:6, italic = T, color = "gold", background = "blue")
kable example with card data
card reports age income share expenditure owner selfemp dependents months majorcards active
yes 0 37.66667 4.5200 0.0332699 124.983300 yes no 3 54 1 12
yes 0 33.25000 2.4200 0.0052169 9.854167 no no 3 34 1 13
yes 0 33.66667 4.5000 0.0041556 15.000000 yes no 4 58 1 5
yes 0 30.50000 2.5400 0.0652138 137.869200 no no 0 25 1 7
yes 0 32.16667 9.7867 0.0670506 546.503300 yes no 2 64 1 5
yes 0 23.25000 2.5000 0.0444384 91.996670 no no 0 54 1 1


We can also create groups for our columns.


base %>%
  kable_styling(bootstrap_options = "bordered") %>%
  add_header_above(c("Group 1" = 4, "Group 2" = 2, "Group 3" = 6))
kable example with card data
Group 1
Group 2
Group 3
card reports age income share expenditure owner selfemp dependents months majorcards active
yes 0 37.66667 4.5200 0.0332699 124.983300 yes no 3 54 1 12
yes 0 33.25000 2.4200 0.0052169 9.854167 no no 3 34 1 13
yes 0 33.66667 4.5000 0.0041556 15.000000 yes no 4 58 1 5
yes 0 30.50000 2.5400 0.0652138 137.869200 no no 0 25 1 7
yes 0 32.16667 9.7867 0.0670506 546.503300 yes no 2 64 1 5
yes 0 23.25000 2.5000 0.0444384 91.996670 no no 0 54 1 1


Data Aggregation

In the first stage of our analysis we are going to group our data in the form of the simple frequency table.

First, let’s take a look at the distribution of income in our sample and verify the tabular accuracy using TAI measure:

options(scipen=999)

limits<- cut(CreditCard$income,seq(0,14,by=2))
tabelka <- freq(limits,type="html")
## 
  |                                                                            
  |                                                                      |   0%
  |                                                                            
  |======================================================================| 100%
tabelka
## $`x:`
##                x label Freq Percent Valid Percent Cumulative Percent
##    Valid   (0,2]        236    17.9          17.9               17.9
##            (2,4]        783    59.4          59.4               77.3
##            (4,6]        205    15.5          15.5               92.8
##            (6,8]         63     4.8           4.8               97.6
##           (8,10]         23     1.7           1.7               99.3
##          (10,12]          7     0.5           0.5               99.8
##          (12,14]          2     0.2           0.2              100.0
##            Total       1319   100.0         100.0                   
##  Missing <blank>          0     0.0                                 
##             <NA>          0     0.0                                 
##            Total       1319   100.0

Without ‘kable’ styling it’s quite ugly right? ;-)

Tabular accuracy

An index of tabular accuracy TAI, described by Jenks and Casspal in 1971 is to optimize the class distribution used in a cartograms/frequency tables etc.

The TAI indicator takes values in the range (0;1). The numerator of the expression is the sum of the absolute deviations of the values classified into classes, and the denominator is the sum of the absolute deviations of the entire classified set.

The better the class division reflects the nature of the data, the larger the indicator will be. As the number of classes increases, the indicator will take on larger values.

Let’s calculate TAI index to check the properties of the tabulated data:

tabelka2 <- classIntervals(CreditCard$income, n=7, style="fixed", fixedBreaks=seq(0,14,by=2))
jenks.tests(tabelka2)
##        # classes  Goodness of fit Tabular accuracy 
##        7.0000000        0.9085328        0.6568085

As we can see - TAI index…

We can use different recipes… (styles):

tabelka3<-classIntervals(CreditCard$income, n=10, style="sd")
plot(tabelka3,pal=c(1:10))

jenks.tests(tabelka3)
##        # classes  Goodness of fit Tabular accuracy 
##        8.0000000        0.9274792        0.6909392

Still, the TAI indicator is not satisfactory. What should we change in the final frequency table design?

hist(CreditCard$income)

Continuous variables

We can calculate the absolute and relative frequencies of a vector x with the function ‘Freq’ from the DescTools packages. Continuous (numeric) variables will be cut using the same logic as used by the function hist. Categorical variables will be aggregated by table. The result will contain single and cumulative frequencies for both, absolute values and percentages.

tabela4<-Freq(CreditCard$income,breaks=seq(0,14,by=2),useNA="ifany")

tabela4 %>%
  kable(col.names = c("Incomes in kUSD","Frequency","Percentage %","Cumulative frequency","Cumulative percentage %")) %>%
  kable_classic(full_width = F, html_font = "Cambria") 
Incomes in kUSD Frequency Percentage % Cumulative frequency Cumulative percentage %
[0,2] 236 0.1789234 236 0.1789234
(2,4] 783 0.5936315 1019 0.7725550
(4,6] 205 0.1554208 1224 0.9279757
(6,8] 63 0.0477635 1287 0.9757392
(8,10] 23 0.0174375 1310 0.9931766
(10,12] 7 0.0053071 1317 0.9984837
(12,14] 2 0.0015163 1319 1.0000000

BTW: what about TAI of that table?…

Categorical variables

Now, let’s take a look at the categorical data and make some tabulations. The xtabs function works like table except it can produce tables from frequencies using the formula interface.

Let’s say we want to see the table with data on how many card applications was accepted or not:

## card
##   no  yes 
##  296 1023

We may easily produce cross-tabs (status vs. Does the individual own their home?) as well:

crosstab<-xtabs(~ card + owner, data=CreditCard)
crosstab
##      owner
## card   no yes
##   no  206  90
##   yes 532 491

and transform it into pretty html table with the kable function:

crosstab %>% 
  kbl() %>%
  kable_styling(full_width = F) %>%
  column_spec(1, bold = T, border_right = T) %>%
  column_spec(2, background = "yellow")
no yes
no 206 90
yes 532 491

Data Visualization

We will explore the “ggplot2” package of the tidyverse for data visualization purposes. The “ggplot2” packages involve the the following three mandatory components:

  1. Data
  2. An aesthetic mapping
  3. Geoms (aka objects)

The following components can also optionally be added:

  1. Stats (aka transformations)
  2. Scales
  3. Facets
  4. Coordinate systems
  5. Position adjustments
  6. Themes

Please note that code in this tutorial was adapted from Chapters 3 of the book “R for Data Science” by Hadley Wickham and Garrett Grolemund.

The full book can be found at: https://r4ds.had.co.nz/

A good cheat sheet for ggplot2 functions can be found at: https://rstudio.com/wp-content/uploads/2015/03/ggplot2-cheatsheet.pdf

Scatterplots

Let’s create an extremely simple scatterplot.

We will use the function ggplot() to do this.

The format of any ggplot graph is this function, followed by another function to add objects.

The objects on a graph in the case of a scatterplot are points. The function we add to it is geom_point.

These functions rely on a function on the inside called aes().

The data and aesthetic mapping components can be added to either the ggplot() or geom functions.

ggplot(data = mpg) +
  geom_point(aes(x = displ, y = hwy))


This is one of the most basic graphs that one can make using the ggplot2 framework.

Next, let’s add color.

geom_point() understands the following aesthetics: x, y, alpha, color, fill, group, shape, size, and stroke (see help documentation).

Let’s map the color argument to the variable “class” from mpg.

ggplot(mpg) +
  geom_point(aes(x = displ, y = hwy, color = class))

This is not the only way to color objects.

Including the color argument inside of the aes() function can map colors to a choice of variable.

However, we can specify colors manually, by specifying color outside of the aes() function. We will also illustrate the “size” argument.

ggplot(mpg) +
  geom_point(aes(x = displ, y = hwy, size = class), color = "blue")

Barplots

Lastly, let’s examine other objects that we can plot using ggplot(). We will create a bar chart using the function geom_bar().

ggplot(mpg) +
  geom_bar(aes(x = class))


With the geom_bar() function, we have a great use-case for a stat transformation.

The following code can be used to convert these counts to proportions:

ggplot(mpg) +
  geom_bar(aes(x = class, y = stat(prop), group = 1))

Histograms

Next, let’s create a histogram with the geom_histogram() function.

ggplot(mpg) +
  geom_histogram(aes(x = hwy))

The geom_histogram() function accepts the argument “binwidth”, and has two key arguments for color: fill (this controls the overall color), and color (this controls the border).

Let’s fill all these in:

ggplot(mpg) +
  geom_histogram(aes(x = hwy), binwidth = 5, fill = "navy", color = "gold")


geom_histogram() provides a great example to modify the scale.

Notice in this example that the axis is automatically broken up by units of 10, and does not begin at 0.

We can modify this with the function scale_x_continuous(), as well as the y-axis with the function scale_y_continuous().

There are three key arguments we will feed this function: “breaks”, “limits”, and “expand”.

“breaks” will define the breaks on the axis.

“limits” will define the beginning and end of the axis, and the “expand” argument can be used to start the axes at 0 by using “expand = c(0,0)”.

ggplot(mpg) +
  geom_histogram(aes(x = hwy), binwidth = 5, fill = "navy", color = "gold") +
  scale_x_continuous(breaks = seq(0, 45, 5), limits = c(0, 50), expand = c(0,0)) +
  scale_y_continuous(breaks = seq(0, 90, 10), limits = c(0, 90), expand = c(0,0))

Boxplots

Next, we will create boxplots.

p <- ggplot(mpg) +
  geom_boxplot(aes(x = class, y = cty, fill = class))
p

Facets

Faceting generates small multiples each showing a different subset of the data. Small multiples are a powerful tool for exploratory data analysis: you can rapidly compare patterns in different parts of the data and see whether they are the same or different.

Read more about facets here.

Notice in this document the use of the fig.height and fig.width options.

Key arguments to facet_wrap() are “facets”, “nrow”, and “ncol”.

ggplot(mpg) +
  geom_boxplot(aes(x = class, y = cty, fill = class)) +
  facet_wrap(facets = ~cyl, nrow = 2, ncol = 2)

Coordinates

Other coordinate systems can be applied to graphs created from ggplot2.

One example is coord_polar(), which uses polar coordinates. Most of these are quite rare. Probably the most common one is coord_flip(), which will flip the X and Y axes. Let’s also illustrate the labs() function, which can be used to change labels.

ggplot(mpg) +
  geom_bar(aes(x = class, fill = factor(cyl))) +
  labs(title = "Cylinders by Class", fill = "cylinders") +
  coord_flip()


These bars are stacked on top of each of other, due to the “cyl” variable being mapped to the “fill” argument. There are various position adjustments that can be used. Again, most of these are not very common, but a common one is the argument “position = ‘dodge’”, which will put items side-by-side.

See this example:

ggplot(mpg) +
  geom_bar(aes(x = class, fill = factor(cyl)), position = "dodge") +
  labs(title = "Cylinders by Class", fill = "cylinders") + 
  coord_flip()

Themes

Lastly, we can alter the “theme”, or the overall appearance of our plot.

I recommend using the ggThemeAssist package, because this will make this incredibly easy, with an interface that will automatically generate reproducible code.

This can be used by highlighting a ggplot2 object, and navigating to Addins > ggplot Theme Assistant.

We’ll make the following changes: eliminating the panel grid lines, eliminating axis ticks, adding a title called “Boxplot Example”, making it bigger and putting it in bold, and adjusting it to the center.

# p
p + theme(axis.ticks = element_line(linetype = "blank"),
    panel.grid.major = element_line(linetype = "blank"),
    panel.grid.minor = element_line(linetype = "blank"),
    plot.title = element_text(size = 14,
        face = "bold", hjust = 0.5)) +labs(title = "Boxplot Example")


There are many more examples of things that can be done with ggplot2.

It is an amazingly powerful and flexible package, and it is worth getting acquainted with the cheat sheet.

Exercise 1.

“Using data on credit card applications’ status please present the frequency table with the nice, kable format for average monthly credit card expenditures of applicants.”

This piece of R code was written to analyze users’ average monthly spending by performing some operations on a credit card spending dataset.

Avg_Exp<- CreditCard %>%
  filter(months != 0) %>%
  mutate(monthly_avg_exp = expenditure / months) %>%
  arrange(desc(monthly_avg_exp))

Avg_Exp <- Avg_Exp %>%
  arrange(desc(monthly_avg_exp))

ggplot(Avg_Exp) +
  geom_histogram(aes(x = monthly_avg_exp))

frequency_table <- Freq(Avg_Exp$monthly_avg_exp, useNA = "ifany")

kable_table_2 <- frequency_table %>%
  kable(col.names = c("Avg exp in kUSD", "Frequency", "Percentage %", "Cumulative frequency", "Cumulative percentage %")) %>%
  kable_styling(bootstrap_options = "bordered") %>% 
  kable_classic(full_width = FALSE, html_font = "Arial")

frequency_table
##         level   freq   perc  cumfreq  cumperc
## 1      [0,50]  1'231  93.5%    1'231    93.5%
## 2    (50,100]     44   3.3%    1'275    96.9%
## 3   (100,150]     14   1.1%    1'289    97.9%
## 4   (150,200]     12   0.9%    1'301    98.9%
## 5   (200,250]      8   0.6%    1'309    99.5%
## 6   (250,300]      1   0.1%    1'310    99.5%
## 7   (300,350]      0   0.0%    1'310    99.5%
## 8   (350,400]      1   0.1%    1'311    99.6%
## 9   (400,450]      1   0.1%    1'312    99.7%
## 10  (450,500]      4   0.3%    1'316   100.0%
kable_table_2
Avg exp in kUSD Frequency Percentage % Cumulative frequency Cumulative percentage %
[0,50] 1231 0.9354103 1231 0.9354103
(50,100] 44 0.0334347 1275 0.9688450
(100,150] 14 0.0106383 1289 0.9794833
(150,200] 12 0.0091185 1301 0.9886018
(200,250] 8 0.0060790 1309 0.9946809
(250,300] 1 0.0007599 1310 0.9954407
(300,350] 0 0.0000000 1310 0.9954407
(350,400] 1 0.0007599 1311 0.9962006
(400,450] 1 0.0007599 1312 0.9969605
(450,500] 4 0.0030395 1316 1.0000000

Records whose months column was not 0 were filtered from the CreditCard data set. This is to prevent the divisor from being zero when calculating monthly expenses. A new column (monthly_avg_exp) is added with the mutate() function. This column shows the average monthly expenditure, calculated by dividing total expenditures by the number of months. With arrange(desc(monthly_avg_exp)) the data set is sorted in descending order by average monthly spending amount. Using the ggplot() function, a histogram is drawn for the monthly_avg_exp variable. This chart shows users’ average monthly spending distribution.

A frequency table for monthly_avg_exp is created with the Freq() function. This table includes the number of times each spend value occurs and the percentage of those values in the total. The frequency table is formatted aesthetically by using kable() and kable_styling() functions. Table headers are specified and a classic HTML table view is provided with kable_classic(). The table is presented in a user-friendly format with designated headings (“Avg exp in kUSD”, “Frequency” etc.).

This code is a typical example for financial data analysis. Calculating key metrics such as average monthly spend allows for a better understanding of customer spending behavior. Visualizations and presentations such as histograms and frequency tables, It provides in-depth insights in analyzing such data and can help make strategic decisions.

Exercise 2.

“The data comes from https://flixgem.com/ (dataset version as of March 12, 2021). The data contains information on 9425 movies and series available on Netlix.”

This piece of R code downloads and reads a CSV file from the internet and then loads the dataset into the R environment.

This code is a typical data loading process used as the initial stage in data analysis projects. Successful upload of the dataset is the first step in the analysis process

“Answer with the most appropriate data visualization for the following questions:”

“1. What is the distribution of Imdb scores for Polish movies and movie-series?”

The provided R code aims to visualize the distribution of IMDb scores for Polish movies and movie-series that are available in Poland, using two different types of plots: a bar chart and a histogram.

polish_movies_series <- mydata %>% 
  filter(Languages == "Polish", Country.Availability == "Poland")

ggplot(polish_movies_series) +
  geom_bar(aes(x = IMDb.Score, fill = factor(IMDb.Score))) +
  labs(title = "Polish movie ratings", fill = "ratings") +
  coord_flip()

ggplot(polish_movies_series) +
  
  geom_histogram(aes(x = IMDb.Score), binwidth = 0.1, fill = "black", color = "white")

In the first part of the code, we filtered the dataset to only include entries where the language is Polish and the movie or series is available in Poland.

filter(): This function is used to subset the data based on specified conditions. In this case, it ensures that only Polish movies and series available in Poland are included.

In the second part, we created a bar chart of IMDb scores to show the frequency of each score. ggplot(): The main function that starts a plot. geom_bar(): This geom creates a bar chart. The aes() function inside maps IMDb ratings to the x-axis and fills are colored based on unique IMDb ratings. Using factor(IMDb.Score),It causes each different score to have a different color. labs(): Used to add labels to the chart, including a title and description title. coord_flip(): Flips the x and y axes to make the graph horizontal; this can improve readability when x-axis labels are numbers or there are many categories.

In the third part of the code, we created a histogram to display the distribution of IMDb scores among Polish movies and TV series.

geom_histogram(): This geom creates a histogram with binwidth = 0.1 specifying the width of each bin. The bin width determines the level of detail in the histogram; a smaller chamber width, can provide a more detailed view of the distribution. aes(x = IMDb.Score): Specifies that IMDb scores should be plotted on the x-axis. fill="black", color="white": Increases visual contrast and clarity by setting the color of bars to black and their borders to white

Both visualizations serve different purposes:

Bar Chart: Useful for seeing the exact frequency of each IMDb score and comparing frequencies across different scores. Histogram: Provides a more general view of the distribution of scores, helping to understand the shape of the distribution (e.g., whether it is normal, skewed, has outliers, etc.). Together, these plots offer a comprehensive view of how IMDb scores are distributed among Polish movies and series available in Poland, which can be insightful for understanding audience reception and the quality distribution of the local film and series industry.

“2. What is the density function of Imdb scores for Polish movies and movie-series?”

The provided R code snippet is used to create a density plot for IMDb scores of Polish movies and series, using the ggplot2 package. This type of visualization is useful for examining the distribution’s shape, central tendency, and variability.

ggplot(polish_movies_series, aes(x = IMDb.Score)) +
  geom_density(fill = "skyblue", color = "black") +
  labs(title = "Densityof IMDb scores of polish movies and series",
       x = "IMDb Score", y = "Density")

aes(x = IMDb.Score): Defines the aesthetic mapping for the graph where IMDb scores are mapped to the x-axis, He states that the intensity will be calculated according to IMDb scores.

geom_density(): Adds a smoothed density estimate to the chart. Density plots are useful for visualizing the distribution of a variable and are particularly useful when the exact shape of the distribution is of interest.

fill = "skyblue": Specifies the color used to fill the area under the density curve, increases visual appeal and readability. color = "black": Defines the color of the density curve outline, providing contrast against the fill for better clarity.

labs(): Adds labels to the plot, including the main title and axis labels.

The density plot created by this code provides a visual interpretation of the distribution of IMDb scores within the filtered subset of Polish movies and series. Unlike histograms, density plots provide a continuous curve representing the distribution, which can be more informative for identifying modes, symmetry, and skewness in the data.

This visualization is particularly useful in identifying the concentration of data points and any potential outliers. By using a density plot, analysts and viewers can quickly grasp the range and commonality of IMDb scores, potentially guiding further statistical analysis or decision-making related to content quality and viewer preferences in the Polish media context.

  1. What are the most popular languages available on Netflix?

The provided R code snippet analyzes a dataset to find the frequency of different languages used in content available on Netflix, focusing particularly on the top five languages

library(dplyr)
library(tidyr)
library(ggplot2)

# Assuming mydata is already read and available
language_count <- mydata %>%
  tidyr::separate_rows(Languages, sep=", ") %>%
  dplyr::group_by(Languages) %>%
  dplyr::summarise(count = dplyr::n()) %>%
  dplyr::arrange(desc(count))

# Convert Languages to factor with desired order
language_count$Languages <- factor(language_count$Languages, levels = language_count$Languages[order(-language_count$count)])

# Plotting the top 5 languages
ggplot(language_count[1:5, ], aes(x = Languages, y = count)) +
  geom_bar(stat = "identity", fill = "black") +
  labs(title = "Top 5 Most Popular Languages on Netflix",
       x = "Languages")

In the first part of the code, function separates multiple languages listed in a single row into multiple rows to facilitate easier counting. For instance, if a movie is listed as “English, Spanish”, it will create two separate entries and groups the dataset by individual languages and calculates the count of each language’s occurrence across the dataset. Also, sorts the languages in descending order based on their frequency, helping identify the most popular languages.

In the second part, converts the Languages column into a factor with levels ordered by frequency. This ensures that the plot displays the languages in the order of their frequency.

In the last part, create a bar graph visualization that displays the top five most popular languages on Netflix based on their frequency of appearance in the dataset. It focus on the top five languages only, ensuring the plot is concise and focused, defines the aesthetic mappings for the plot and adds important labeling information to the graph.

The combined effect of these components is to produce a clear and informative visualization that highlights the languages most frequently used in Netflix content available in the dataset.

For extra credits:

Extra challenge 1.: Create a chart showing actors starring in the most popular productions.”

This R code snippet processes a dataset to identify the top ten movies with the highest box office earnings and then presents the results in both a detailed view and a stylized HTML table focusing on movie titles and actors.

#I thought the first step for getting the most popular movies would be the highest gain in the box office so i got highest boxoffice values

mydata$Boxoffice <- gsub(",", "", mydata$Boxoffice)
popular_movies <- mydata %>%
  arrange(desc(Boxoffice))
popular_movies <- head(popular_movies, 10)
#then put them in a kable cahrt where only title and actors are shown
popular_movie_actors<- popular_movies[, c("Title", "Actors")]

popular_movie_actors%>%
  kable("html") %>%
  kable_styling(bootstrap_options = c("striped", "hover"))
Title Actors
Little Italy Hayden Christensen, Danny Aiello, Andrea Martin, Emma Roberts
Olympus Has Fallen Dylan McDermott, Gerard Butler, Aaron Eckhart, Finley Jacobsen
In the Name of Mateusz Kosciukiewicz, Andrzej Chyra, Maja Ostaszewska, Lukasz Simlat
The Green Hornet Seth Rogen, Jay Chou, Tom Wilkinson, Cameron Diaz
Date Night Steve Carell, Taraji P. Henson, Tina Fey, Mark Wahlberg
The Color Purple Whoopi Goldberg, Danny Glover, Margaret Avery, Oprah Winfrey
Fida Fardeen Khan, Kareena Kapoor, Kim Sharma, Shahid Kapoor
The Swan Princess Liz Callaway, Michelle Nicastro, Jack Palance, Howard McGillin
Yes Man John Michael Higgins, Jim Carrey, Bradley Cooper, Zooey Deschanel
Sausage Party Michael Cera, Iris Apatow, Alistair Abell, Sugar Lyn Beard
print(popular_movies)
##                 Title                                                Genre
## 1        Little Italy                                      Comedy, Romance
## 2  Olympus Has Fallen                                     Action, Thriller
## 3      In the Name of                                      Drama, Thriller
## 4    The Green Hornet                                Action, Comedy, Crime
## 5          Date Night                     Comedy, Crime, Romance, Thriller
## 6    The Color Purple                                                Drama
## 7                Fida              Action, Crime, Drama, Romance, Thriller
## 8   The Swan Princess Animation, Comedy, Family, Fantasy, Musical, Romance
## 9             Yes Man                                      Comedy, Romance
## 10      Sausage Party                Animation, Adventure, Comedy, Fantasy
##                                                                                                                                                                                                                                                                       Tags
## 1                                                                                                                                                                                                            Romantic Movies,Romantic Comedies,Comedies,Romantic Favorites
## 2                                                                                                                                                                                                                                      Action Thrillers,Action & Adventure
## 3                                                                                                                                           Independent Dramas,Dramas,Independent Films,International Dramas,Gay & Lesbian Films,International Movies,Gay & Lesbian Dramas
## 4  Gangster Films,Comic Book and Superhero Films,Action Comedies,Comedies,Martial Arts Films,Action & Adventure,Crime Action & Adventure,Dark Comedies,Gangster Action & Adventure,US Movies,Crime Comedies,Crime Films,Blockbuster Action & Adventure,Action,Crime Action
## 5                                                           Police Action & Adventure,Crime Action & Adventure,Romantic Comedies,Police Movies,Action Comedies,Comedies,Romantic Movies,Action & Adventure,Quirky Romance,Romantic Favorites,Comedy Blockbusters,US Movies
## 6                      Dramas based on a book,Dramas,Social Issue Dramas,Dramas based on contemporary literature,Dramas,Social Issue Dramas,Dramas based on contemporary literature,Dramas based on a book,Tearjerkers,Movies Based on Books,Classic Dramas,Classic Movies
## 7                                                                                                                                                        Bollywood Movies,Crime Movies,Romantic Movies,Thriller Movies,Indian Movies,Crime Thrillers,Hindi-Language Movies
## 8                                                                                                                       Family Sci-Fi & Fantasy,Children & Family Films,Films for ages 8 to 10,Family Animation,Films for ages 5 to 7,Musicals,Kids Music,Music & Musicals
## 9                                                                                                                                                          Romantic Comedies,Comedies,Romantic Films,Films Based on Books,Slapstick Comedies,Romantic Favourites,US Movies
## 10                                                                                                                                                  Dark Comedies,Adult Animation,Comedies,Action & Adventure,Late Night Comedies,Adventures,Comedy Blockbusters,US Movies
##                    Languages Series.or.Movie Hidden.Gem.Score
## 1    English, Italian, Latin           Movie              1.7
## 2            English, Korean           Movie              2.6
## 3            Polish, English           Movie              6.8
## 4          English, Mandarin           Movie              2.3
## 5            English, Hebrew           Movie              3.1
## 6                    English           Movie              4.0
## 7                      Hindi           Movie              6.7
## 8                    English           Movie              2.9
## 9  English, Korean, Estonian           Movie              2.7
## 10                   English           Movie              3.5
##                                                                                                                                                                                                                                                                                                     Country.Availability
## 1                                                                                                                                                                                                                                                                 Czech Republic,Sweden,Argentina,Brazil,Mexico,Colombia
## 2                                                                                                                                                                                                       Japan,Romania,Sweden,United States,South Korea,Hungary,Czech Republic,Belgium,Canada,Greece,Slovakia,Netherlands
## 3                                                                                                                                                                                                                                                                                                                 Poland
## 4                                                                                                                                                                               Turkey,Hong Kong,Japan,Canada,Singapore,Switzerland,United States,Greece,Sweden,Thailand,Malaysia,Netherlands,Italy,Israel,India,Germany
## 5                                                                                                                                                                                                                                                                                                            South Korea
## 6                                                                                                                                                                                                                                                                                                              Australia
## 7  Lithuania,Mexico,India,Czech Republic,Russia,United Kingdom,Germany,United States,Australia,Poland,Hong Kong,Japan,France,Canada,Spain,Singapore,Argentina,Greece,Switzerland,Slovakia,Sweden,Thailand,Belgium,Turkey,Malaysia,Hungary,Brazil,Netherlands,Italy,South Africa,Iceland,Portugal,Israel,Colombia,Romania
## 8                                                                                                                                                                                                                                Iceland,Australia,Hong Kong,Thailand,Singapore,Malaysia,Sweden,France,Spain,Switzerland
## 9                                                                                                                                                                                                                     Switzerland,Canada,Sweden,France,Belgium,Turkey,United Kingdom,Portugal,Germany,Japan,Italy,Russia
## 10                                                                                                                                                                                            Canada,Thailand,Hong Kong,Switzerland,United Kingdom,France,South Korea,Spain,Singapore,Greece,Malaysia,Turkey,Netherlands
##     Runtime                    Director
## 1  1-2 hour               Donald Petrie
## 2  1-2 hour               Antoine Fuqua
## 3  1-2 hour        Malgorzata Szumowska
## 4  1-2 hour               Michel Gondry
## 5  1-2 hour                  Shawn Levy
## 6   > 2 hrs            Steven Spielberg
## 7  1-2 hour                   Ken Ghosh
## 8  1-2 hour                Richard Rich
## 9  1-2 hour                 Peyton Reed
## 10 1-2 hour Conrad Vernon, Greg Tiernan
##                                                               Writer
## 1                         Brent Cote, Steve Galluccio, Vinay Virmani
## 2                            Katrin Benedikt, Creighton Rothenberger
## 3            Szczepan Twardoch, Michal Englert, Malgorzata Szumowska
## 4                       Seth Rogen, George W. Trendle, Evan Goldberg
## 5                                                      Josh Klausner
## 6                                         Alice Walker, Menno Meyjes
## 7                        Kiran Kotrial, Lalit Mahajan, Sunny Mahajan
## 8                                         Richard Rich, Brian Nissen
## 9         Jarrad Paul, Nicholas Stoller, Andrew Mogel, Danny Wallace
## 10 Seth Rogen, Jonah Hill, Evan Goldberg, Ariel Shaffir, Kyle Hunter
##                                                                   Actors
## 1          Hayden Christensen, Danny Aiello, Andrea Martin, Emma Roberts
## 2         Dylan McDermott, Gerard Butler, Aaron Eckhart, Finley Jacobsen
## 3  Mateusz Kosciukiewicz, Andrzej Chyra, Maja Ostaszewska, Lukasz Simlat
## 4                      Seth Rogen, Jay Chou, Tom Wilkinson, Cameron Diaz
## 5                Steve Carell, Taraji P. Henson, Tina Fey, Mark Wahlberg
## 6           Whoopi Goldberg, Danny Glover, Margaret Avery, Oprah Winfrey
## 7                Fardeen Khan, Kareena Kapoor, Kim Sharma, Shahid Kapoor
## 8         Liz Callaway, Michelle Nicastro, Jack Palance, Howard McGillin
## 9      John Michael Higgins, Jim Carrey, Bradley Cooper, Zooey Deschanel
## 10            Michael Cera, Iris Apatow, Alistair Abell, Sugar Lyn Beard
##    View.Rating IMDb.Score Rotten.Tomatoes.Score Metacritic.Score
## 1            R        5.7                    14               28
## 2            R        6.5                    49               41
## 3    Not Rated        6.6                    77               52
## 4        PG-13        5.8                    44               39
## 5        PG-13        6.3                    66               56
## 6        PG-13        7.8                    81               78
## 7                     5.5                    60               NA
## 8            G        6.5                    50               NA
## 9        PG-13        6.8                    46               46
## 10           R        6.1                    82               66
##    Awards.Received Awards.Nominated.For  Boxoffice Release.Date
## 1               NA                   NA   $990230     9/21/2018
## 2                1                    5 $98925640     3/22/2013
## 3                7                    8     $9883     9/20/2013
## 4                4                    7 $98780042     1/14/2011
## 5                4                    8 $98711404      4/9/2010
## 6               14                   23 $98467863      2/7/1986
## 7               NA                   NA    $98297     8/20/2004
## 8                1                    8  $9771658    11/18/1994
## 9                3                    9 $97690976    12/19/2008
## 10               1                   24 $97685686     8/12/2016
##    Netflix.Release.Date
## 1             6/15/2019
## 2              8/5/2015
## 3             4/14/2015
## 4             4/14/2015
## 5              4/1/2016
## 6              6/1/2016
## 7            10/14/2020
## 8             4/14/2015
## 9             4/14/2015
## 10            2/23/2017
##                                                 Production.House
## 1                Lionsgate, Les Films Séville, Entertainment One
## 2                                               Millennium Films
## 3                                                               
## 4                                                  Original Film
## 5                                          21 Laps Entertainment
## 6    Warner Brothers, Guber-Peters Company, Amblin Entertainment
## 7                                                               
## 8                                         Rich Animation Studios
## 9                                   Heyday Films, Zanuck Company
## 10 Sony Music, Point Grey, Annapurna Pictures, Columbia Pictures
##                              Netflix.Link                            IMDb.Link
## 1  https://www.netflix.com/watch/81020104 https://www.imdb.com/title/tt6957966
## 2  https://www.netflix.com/watch/70259801 https://www.imdb.com/title/tt2302755
## 3  https://www.netflix.com/watch/70270778 https://www.imdb.com/title/tt2650642
## 4  https://www.netflix.com/watch/70117699 https://www.imdb.com/title/tt0990407
## 5  https://www.netflix.com/watch/70121501 https://www.imdb.com/title/tt1279935
## 6  https://www.netflix.com/watch/60026621 https://www.imdb.com/title/tt0088939
## 7  https://www.netflix.com/watch/70018449 https://www.imdb.com/title/tt0422236
## 8  https://www.netflix.com/watch/60034386 https://www.imdb.com/title/tt0111333
## 9  https://www.netflix.com/watch/70100379 https://www.imdb.com/title/tt1068680
## 10 https://www.netflix.com/watch/80098100 https://www.imdb.com/title/tt1700841
##                                                                                                                                                Summary
## 1                                          Two young lovers from warring pizza places try to hide their burgeoning affair from their feuding families.
## 2           A disgraced Secret Service agent must come to the rescue when Korean terrorists descend on the White House and take the president hostage.
## 3       Running toward God but away from his sexuality, Adam became a priest at age 21. Now the head of a rural parish, hes still tormented by desire.
## 4      A hard-partying heir dons a disguise to fight crime after hours. But with no talents or skills, he relies on his friend, a martial-arts genius.
## 5                     Who knew simple dinner reservations under a different name could turn one New Jersey couples date night so terribly upside-down?
## 6  A Southern womans correspondence with her sister in Africa and friendship with a singer help her escape an abusive husband and a hardscrabble life.
## 7            An all-around nice guy finds himself in a dangerous situation after he makes the ultimate sacrifice for the woman he loves in this drama.
## 8        Based on the tale of Swan Lake, this animated feature tells the story of Odette, a sweet girl whos turned into a graceful swan by a sorcerer.
## 9  After a bitter divorce, a bank drone falls under the sway of a self-help guru who urges him to say yes to everything that comes his way for a year.
## 10    After making a gruesome discovery about life beyond the supermarket, an affable sausage strives to save his fellow foods in this raunchy comedy.
##    IMDb.Votes
## 1       11636
## 2      257808
## 3        2819
## 4      155265
## 5      152742
## 6       78284
## 7        2423
## 8       22817
## 9      336720
## 10     176325
##                                                                                                                                                                                   Image
## 1   http://occ-0-1490-1489.1.nflxso.net/dnm/api/v6/evlCitJPPCVCry0BZlEFb5-QjKc/AAAABSB31_nbMkvKQkVYRpVzUhM4fSWTZuctZouH26CrnH6OKWiZzLjrYbAm1_VcmjfUZ44tQQ-fpzCdJqdYTf1wI2n97A.jpg?r=762
## 2  https://occ-0-2773-2774.1.nflxso.net/dnm/api/v6/evlCitJPPCVCry0BZlEFb5-QjKc/AAAABa8W2Oz8uTwZWGEGSEXhkc0v9JWRid9j3aqzjl7PZ_eO-CtyEqmxej5kax65kROSQCNaOjAoEEY_ktTKyBOh75a0kg.jpg?r=a18
## 3  https://occ-0-2506-1432.1.nflxso.net/dnm/api/v6/evlCitJPPCVCry0BZlEFb5-QjKc/AAAABdEzJ11sJJqr0JCfp8lCaPluekSNN2SerMn90_azUFuUlqvQ6IN9z5O9xKphqwlqVmTGRgolNb2AyAprGqG2pObIXw.jpg?r=7ad
## 4    https://occ-0-2851-38.1.nflxso.net/dnm/api/v6/evlCitJPPCVCry0BZlEFb5-QjKc/AAAABamCqGYXomwmrDUFlqkmo8uroCE03E9ssA25dFPkTmME4o6pepKD1H-Aj1GGuEu4i_4f3F0anQS5jUA1gOUbp3PhyQ.jpg?r=e4f
## 5     https://occ-0-64-325.1.nflxso.net/dnm/api/v6/evlCitJPPCVCry0BZlEFb5-QjKc/AAAABWJ3QnFafBG6vrfXpHQNxyqHhRWsDhaulMuoXYnaUZ6KfihhUDS2gqt7xiK1S9D5ZuaUlLdxaZ4mlb_Ddx-fAmCmTg.jpg?r=d46
## 6  https://occ-0-3466-2774.1.nflxso.net/dnm/api/v6/evlCitJPPCVCry0BZlEFb5-QjKc/AAAABQKgDRrfutZAuuotjed8qWZW8fViSskKx-fkNw08GcNXaz11xFxeSsv-yOcM7t3-JJKlDFLmWyyrWiB2384rs-lrvg.jpg?r=a6f
## 7  https://occ-0-2851-1432.1.nflxso.net/dnm/api/v6/evlCitJPPCVCry0BZlEFb5-QjKc/AAAABfsTONpq0wDxtNWaEQC_Z0rp_aYrvgpYBhxNsNknIwUm24syxdKj0X_F6treVzZLs1AvDm7cLyCg1WeJviTCaRxVmg.jpg?r=3ba
## 8    https://occ-0-2851-38.1.nflxso.net/dnm/api/v6/evlCitJPPCVCry0BZlEFb5-QjKc/AAAABSMBlN-XZaVXSEHRvT-Pra2tIiPr6tFUmL7tnCEzHivLXTYL4RQikENQaqEl37ynQDLjbEPjqoYK9FJRVfmQqfybCg.jpg?r=5a4
## 9    https://occ-0-2851-38.1.nflxso.net/dnm/api/v6/evlCitJPPCVCry0BZlEFb5-QjKc/AAAABRTzEOQA8YocVzGOzGquWjeYdTbXrXhQhcqHR0cmiiTGEDc0h7Ko3xi1ZRb1omJuHSgNQu2lIfE_rNy6DA3GAHDarQ.jpg?r=826
## 10    https://occ-0-138-38.1.nflxso.net/dnm/api/v6/evlCitJPPCVCry0BZlEFb5-QjKc/AAAABS_CbFdl2xQ5xUYUNCJiZrLY_kG9lc5ZVmlB31kJLI2nIbQ65w0gqINSPkQa_Od6JQWjv0GdSDaAgk_oJG2gjevcsA.jpg?r=ad1
##                                                                                                                                                             Poster
## 1                                                               https://m.media-amazon.com/images/M/MV5BMjM3MDc2NDc2N15BMl5BanBnXkFtZTgwNzg2NjExNjM@._V1_SX300.jpg
## 2                  https://images-na.ssl-images-amazon.com/images/M/MV5BNTU0NmY4MWYtNzRlMS00MDkxLWJkODYtOTM3NGI2ZDc1NTJhXkEyXkFqcGdeQXVyNjU0OTQ0OTY@._V1_SX300.jpg
## 3                                                                 http://ia.media-imdb.com/images/M/MV5BMjI5NDQxMjQ5Nl5BMl5BanBnXkFtZTgwNjU0MzAwMDE@._V1_SX300.jpg
## 4                                                  https://images-na.ssl-images-amazon.com/images/M/MV5BMTcwOTMwMDYyMl5BMl5BanBnXkFtZTcwMzAxMjMyNA@@._V1_SX300.jpg
## 5                                                  https://images-na.ssl-images-amazon.com/images/M/MV5BODgwMjM2ODE4M15BMl5BanBnXkFtZTcwMTU2MDcyMw@@._V1_SX300.jpg
## 6                                                                 http://ia.media-imdb.com/images/M/MV5BMTUzOTkxNjY4M15BMl5BanBnXkFtZTgwNjE5MDgxMTE@._V1_SX300.jpg
## 7                               https://m.media-amazon.com/images/M/MV5BMWIwOWZkZjYtMmZkMy00OTk1LTg4ZWMtNmE3YTJiYzA2NjU5XkEyXkFqcGdeQXVyODE5NzE3OTE@._V1_SX300.jpg
## 8  https://images-na.ssl-images-amazon.com/images/M/MV5BNDM2OGM1MjAtYjA3Zi00NzEzLWFiOWMtYjg4MDdiMzYzMWVkL2ltYWdlL2ltYWdlXkEyXkFqcGdeQXVyNDA5Mjg5MjA@._V1_SX300.jpg
## 9                  https://images-na.ssl-images-amazon.com/images/M/MV5BMzBmZTMzYmItNzhhMC00M2FkLWIxMGEtMjIxMjAwNmQ2ZmM4XkEyXkFqcGdeQXVyNTIzOTk5ODM@._V1_SX300.jpg
## 10                                                 https://images-na.ssl-images-amazon.com/images/M/MV5BMjkxOTk1MzY4MF5BMl5BanBnXkFtZTgwODQzOTU5ODE@._V1_SX300.jpg
##                                   TMDb.Trailer Trailer.Site
## 1  https://www.youtube.com/watch?v=ZH6kK9oiy4E      YouTube
## 2  https://www.youtube.com/watch?v=ar-IaAx7s8k      YouTube
## 3  https://www.youtube.com/watch?v=hmgD42ZHiP8      YouTube
## 4  https://www.youtube.com/watch?v=PMA-taGtfXs      YouTube
## 5  https://www.youtube.com/watch?v=aspBKFz2dBI      YouTube
## 6  https://www.youtube.com/watch?v=d83NnlL83mc      YouTube
## 7  https://www.youtube.com/watch?v=AVtvjfoXNXc      YouTube
## 8  https://www.youtube.com/watch?v=5wfMVdyDa_g      YouTube
## 9  https://www.youtube.com/watch?v=rvpsiIe2vBE      YouTube
## 10 https://www.youtube.com/watch?v=c7fP9q_LyDc      YouTube
print(popular_movie_actors)
##                 Title
## 1        Little Italy
## 2  Olympus Has Fallen
## 3      In the Name of
## 4    The Green Hornet
## 5          Date Night
## 6    The Color Purple
## 7                Fida
## 8   The Swan Princess
## 9             Yes Man
## 10      Sausage Party
##                                                                   Actors
## 1          Hayden Christensen, Danny Aiello, Andrea Martin, Emma Roberts
## 2         Dylan McDermott, Gerard Butler, Aaron Eckhart, Finley Jacobsen
## 3  Mateusz Kosciukiewicz, Andrzej Chyra, Maja Ostaszewska, Lukasz Simlat
## 4                      Seth Rogen, Jay Chou, Tom Wilkinson, Cameron Diaz
## 5                Steve Carell, Taraji P. Henson, Tina Fey, Mark Wahlberg
## 6           Whoopi Goldberg, Danny Glover, Margaret Avery, Oprah Winfrey
## 7                Fardeen Khan, Kareena Kapoor, Kim Sharma, Shahid Kapoor
## 8         Liz Callaway, Michelle Nicastro, Jack Palance, Howard McGillin
## 9      John Michael Higgins, Jim Carrey, Bradley Cooper, Zooey Deschanel
## 10            Michael Cera, Iris Apatow, Alistair Abell, Sugar Lyn Beard

In the first part of code, with the gsub() function, Boxoffice removes the commas in the column, making the data suitable for numerical operations. Then, these numerical data are arranged in descending order with the arrange() function of the dplyr package, and the top ten movies with the highest grossing are selected with the head() function. These transactions, It is done to rank the movies according to their revenues and to identify the most popular ones, so that the information obtained from the data set becomes more meaningful and useful.

In the second part, subsets the popular_movies dataframe to include only the columns Title and Actors, focusing on the most relevant information for a summary presentation.

and, the last part, make data more accessible and visually appealing. We implemented Bootstrap styles such as “striped” and “hover” to improve readability and interactivity.

The code is structured to achieve the task of identifying and presenting the top ten highest-grossing movies effectively. By focusing on the Boxoffice figures, the approach directly targets a measure of movie popularity. The final presentation, which showcases only the movie titles and actors in a styled HTML table, gives a summary suitable for our reports.

Extra challenge 2.: For movies and series, create rating charts from the various portals (Hidden Gem, IMDb, Rotten Tomatoes, Metacritic). Hint: it’s a good idea to reshape the data to long format.”

This R code downloads the Netflix dataset from an online source, reads it, and then uses it to visualize the rating distributions of movie and TV genres across different rating sources.

library(tidyr)
library(ggplot2)


download.file("https://raw.githubusercontent.com/kflisikowski/ds/master/netflix-dataset.csv?raw=true", destfile ="dane.csv",mode="wb")
mydata <- read.csv(file="dane.csv", encoding ="UTF-8", header=TRUE, sep = ",")


ggplot(mydata %>% pivot_longer(cols = c(`Hidden.Gem.Score`, `IMDb.Score`, `Rotten.Tomatoes.Score`, `Metacritic.Score`), names_to = "Rating_Source", values_to = "Rating") %>% drop_na(), aes(x = `Series.or.Movie`, y = Rating, fill = Rating_Source)) + geom_boxplot() + labs(title = "Rating Distribution on Different Platforms by Genre", x = "Genre (Movie or Series)",  y = "Rating") + theme_minimal() + theme(axis.text.x = element_text(angle = 45, hjust = 1)) + facet_wrap(~Rating_Source, scales = "free_y", ncol = 2)

Using the tidyr and ggplot2 libraries, the code collects the various rating scores (Hidden.Gem.Score, IMDb.Score, Rotten.Tomatoes.Score, Metacritic.Score) in the dataset into a single column, discards the missing data, and plots the data in a boxplot. presents with each rating source. Boxplots are visualized as having a different y-axis at the bottom, making it easier to compare score ranges from different sources. Boxplots are grouped by genre of movies and TV series, and a different chart is created for each rating source. This process allows to visually compare and analyze rating differences between genres on different platforms

Extra challenge 3.: Which film studios produce the most and how has this changed over the years?

LS0tDQp0aXRsZTogIkRhdGEgVmlzdWFsaXphdGlvbiINCmF1dGhvcjogIkVtcmUgQXlkaW4sIFNlbWloIEVsbWFzIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICB0aGVtZTogY2VydWxlYW4NCiAgICBoaWdobGlnaHQ6IHRleHRtYXRlDQogICAgZm9udHNpemU6IDhwdA0KICAgIHRvYzogeWVzDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBubw0KICAgIGRmX3ByaW50OiBkZWZhdWx0DQogICAgdG9jX2RlcHRoOiA1DQplZGl0b3Jfb3B0aW9uczogDQogIG1hcmtkb3duOiANCiAgICB3cmFwOiA3Mg0KLS0tDQoNCg0KDQpgYGB7ciwgaW5jbHVkZT1GQUxTRX0NCiMjIEdsb2JhbCBvcHRpb25zDQpvcHRpb25zKHF3cmFwczJfbWFya3VwID0gIm1hcmtkb3duIikNCmxpYnJhcnkocXdyYXBzMikNCmxpYnJhcnkoYXJzZW5hbCkNCmxpYnJhcnkoZTEwNzEpDQpsaWJyYXJ5KGhhdmVuKQ0KbGlicmFyeShwYXBlUikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoa2FibGVFeHRyYSkNCmxpYnJhcnkoc3VtbWFyeXRvb2xzKQ0KbGlicmFyeShjbGFzc0ludCkNCmxpYnJhcnkocGFzdGVjcykNCmxpYnJhcnkocmVwb3J0dG9vbHMpDQpsaWJyYXJ5KGRlc2N0YWJsZSkNCmxpYnJhcnkoRGVzY1Rvb2xzKQ0KbGlicmFyeShmcmVxdWVuY3kpDQpsaWJyYXJ5KGdncHVicikNCmxpYnJhcnkoZGxvb2tyKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeSh0aW55dGV4KQ0KbGlicmFyeShBRVIpDQpsaWJyYXJ5KGdnVGhlbWVBc3Npc3QpDQpsaWJyYXJ5KFNtYXJ0ZXJQb2xhbmQpDQprbml0cjo6b3B0c19jaHVuayRzZXQod2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UpDQpgYGANCg0KDQojIyBLYWJsZSBwYWNrYWdlDQoNCkluIGEgcHJldmlvdXMgd2Vla3MsIHdlIHNhdyBSIE1hcmtkb3duIGluIGFjdGlvbiwgd2hlcmUgbXVsdGlwbGUgdGhpbmdzIGNhbiBiZSBjcmVhdGVkIGluIG9uZSBsb2NhdGlvbjogY29kZSwgY29tbWVudGFyeSwgYW5kIG91dHB1dC4gDQoNCkluIHRoaXMgY2hhcHRlciB3ZSB3aWxsIGV4cGxvcmUgcGFja2FnZSB3aGljaCB3aWxsIGZhY2lsaXRhdGUgdGhlIGNyZWF0aW9uIG9mIHByZXNlbnRhdGlvbi13b3J0aHkgdGFibGVzOiAia2FibGVFeHRyYSIuDQoNCkxldCdzIHdvcmsgd2l0aCB0aGUgY3Jvc3Mtc2VjdGlvbmFsIGRhdGEgb24gdGhlIGNyZWRpdCBoaXN0b3J5IGZvciBhIHNhbXBsZSBvZiBhcHBsaWNhbnRzIGZvciBhIHR5cGUgb2YgY3JlZGl0IGNhcmQuDQoNCmBgYHtyfQ0KZGF0YShDcmVkaXRDYXJkKQ0KY2FyZEhlYWQgPC0gaGVhZChDcmVkaXRDYXJkKQ0KY2FyZEhlYWQNCmBgYA0KDQo8YnI+DQoNCmBgYHtyIFRpYmJsZSB3aXRoICJrYWJsZSJ9DQpjYXJkSGVhZCAlPiUNCiAga2JsKCkNCmBgYA0KDQo8YnI+DQoNCkxldCdzIHR3ZWFrIHRoZSBhcHBlYXJhbmNlIG9mIHRoaXMgd2l0aCB0aGUgImFsaWduIiBhbmQgdGhlICJjYXB0aW9uIiBhcmd1bWVudHMuICANCg0KVGhlIGFsaWduIGFyZ3VtZW50IHRha2VzIGEgY2hhcmFjdGVyIHZlY3RvciB3aXRoIGxldHRlcnMgImwiLCAiYyIsIG9yICJyIiAtIHNwZWNpZnlpbmcgd2hlcmUgeW91IHdhbnQgdGhlIGNvbHVtbnMgdG8gYmUgYWxpZ25lZC4NCg0KVGhlIGNhcHRpb24gYXJndW1lbnQgZ2l2ZXMgYSBjYXB0aW9uIHRvIHRoZSB0YWJsZS4NCg0KPGJyPg0KDQpgYGB7ciBBbGlnbmVkIGthYmxlfQ0KYmFzZSA8LSBjYXJkSGVhZCAlPiUNCiAga2JsKGFsaWduID0gYyhyZXAoImMiLCA3KSwgcmVwKCJyIiwgNSkpLCBjYXB0aW9uID0gImthYmxlIGV4YW1wbGUgd2l0aCBjYXJkIGRhdGEiKQ0KYmFzZQ0KYGBgDQoNCjxicj4NCg0KQSBrZXkgZnVuY3Rpb24sIHdoZXJlIHdlIGNhbiBlbmpveSBtdWNoIG9mIHRoZSBjb25maWd1cmF0aW9uIGZvciB0aGUgdGFibGUsIGlzIHZpYSBga2FibGVfc3R5bGluZygpYC4gICANCg0KV2UgaGF2ZSBvcHRpb25zICJib290c3RyYXBfb3B0aW9ucyIgb3IgImxhdGV4X29wdGlvbnMiLCB3aGVyZSB0aGUgbGF0dGVyIHJlcXVpcmVzIHRoZSB1c2Ugb2YgdGhlIHBhY2thZ2UgInRpbnl0ZXgiIGFuZCBhIGxvY2FsIGluc3RhbGxhdGlvbiBvZiBMYVRlWC4gIA0KDQpQb3NzaWJsZSBvcHRpb25zIGZvciAiYm9vdHN0cmFwX29wdGlvbnMiIGluY2x1ZGUgJ2Jhc2ljJywgJ3N0cmlwZWQnLCAnYm9yZGVyZWQnLCAnaG92ZXInLCAnY29uZGVuc2VkJywgJ3Jlc3BvbnNpdmUnLCBhbmQgbm9uZS4gIA0KDQpQb3NzaWJsZSBmb3IgImxhdGV4X29wdGlvbnMiIGluY2x1ZGUgJ2Jhc2ljJywgJ3N0cmlwZWQnLCAnaG9sZF9wb3NpdGlvbicsICdIT0xEX3Bvc2l0aW9uJywgJ3NjYWxlX2Rvd24nLCBhbmQgJ3JlcGVhdF9oZWFkZXInLg0KDQo8YnI+DQoNCg0KYGBge3IgQm9vdHN0cmFwIC0gc3RyaXBlZH0NCmJhc2UgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIpDQpgYGANCg0KPGJyPg0KDQpOZXh0LCB3ZSBjYW4gY3VzdG9taXplIHRoZSBsb29rIGFuZCBmZWVsIG9mIHBhcnRpY3VsYXIgcm93cyBhbmQgY29sdW1ucy4gICANCg0KTGV0J3Mgc2VlIGFuIGV4YW1wbGUgaGVyZSwgd2hlcmUgd2UgbWFrZSB0aGUgbGFzdCB0aHJlZSByb3dzIGJsdWUuDQoNCmBgYHtyIENvbHVtbiBhbmQgcm93IGN1c3RvbWl6aW5nfQ0KYmFzZSAlPiUNCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJib3JkZXJlZCIpICU+JQ0KICBjb2x1bW5fc3BlYyg4OjEyLCBib2xkID0gVCkgJT4lDQogIHJvd19zcGVjKDQ6NiwgaXRhbGljID0gVCwgY29sb3IgPSAiZ29sZCIsIGJhY2tncm91bmQgPSAiYmx1ZSIpDQpgYGANCg0KPGJyPg0KDQoNCldlIGNhbiBhbHNvIGNyZWF0ZSBncm91cHMgZm9yIG91ciBjb2x1bW5zLg0KDQo8YnI+DQoNCmBgYHtyfQ0KYmFzZSAlPiUNCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJib3JkZXJlZCIpICU+JQ0KICBhZGRfaGVhZGVyX2Fib3ZlKGMoIkdyb3VwIDEiID0gNCwgIkdyb3VwIDIiID0gMiwgIkdyb3VwIDMiID0gNikpDQpgYGANCg0KPGJyPg0KDQoNCiMjIERhdGEgQWdncmVnYXRpb24NCg0KSW4gdGhlIGZpcnN0IHN0YWdlIG9mIG91ciBhbmFseXNpcyB3ZSBhcmUgZ29pbmcgdG8gZ3JvdXAgb3VyIGRhdGEgaW4gdGhlIGZvcm0gb2YgdGhlIHNpbXBsZSBmcmVxdWVuY3kgdGFibGUuDQoNCkZpcnN0LCBsZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGluY29tZSBpbiBvdXIgc2FtcGxlIGFuZCB2ZXJpZnkgdGhlIHRhYnVsYXIgYWNjdXJhY3kgdXNpbmcgVEFJIG1lYXN1cmU6DQoNCmBgYHtyIHRhYmxlLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBwYWdlZC5wcmludD1GQUxTRX0NCm9wdGlvbnMoc2NpcGVuPTk5OSkNCg0KbGltaXRzPC0gY3V0KENyZWRpdENhcmQkaW5jb21lLHNlcSgwLDE0LGJ5PTIpKQ0KdGFiZWxrYSA8LSBmcmVxKGxpbWl0cyx0eXBlPSJodG1sIikNCnRhYmVsa2ENCmBgYA0KDQpXaXRob3V0ICdrYWJsZScgc3R5bGluZyBpdCdzIHF1aXRlIHVnbHkgcmlnaHQ/IDstKQ0KDQoNCiMjIyBUYWJ1bGFyIGFjY3VyYWN5DQoNCkFuIGluZGV4IG9mIHRhYnVsYXIgYWNjdXJhY3kgVEFJLCBkZXNjcmliZWQgYnkgSmVua3MgYW5kIENhc3NwYWwgaW4gMTk3MSBpcyB0byBvcHRpbWl6ZSB0aGUgY2xhc3MgZGlzdHJpYnV0aW9uIHVzZWQgaW4gYSBjYXJ0b2dyYW1zL2ZyZXF1ZW5jeSB0YWJsZXMgZXRjLiANCg0KVGhlIFRBSSBpbmRpY2F0b3IgdGFrZXMgdmFsdWVzIGluIHRoZSByYW5nZSAoMDsxKS4gVGhlIG51bWVyYXRvciBvZiB0aGUgZXhwcmVzc2lvbiBpcyB0aGUgc3VtIG9mIHRoZSBhYnNvbHV0ZSBkZXZpYXRpb25zIG9mIHRoZSB2YWx1ZXMgY2xhc3NpZmllZCBpbnRvIGNsYXNzZXMsIGFuZCB0aGUgZGVub21pbmF0b3IgaXMgdGhlIHN1bSBvZiB0aGUgYWJzb2x1dGUgZGV2aWF0aW9ucyBvZiB0aGUgZW50aXJlIGNsYXNzaWZpZWQgc2V0LiANCg0KVGhlIGJldHRlciB0aGUgY2xhc3MgZGl2aXNpb24gcmVmbGVjdHMgdGhlIG5hdHVyZSBvZiB0aGUgZGF0YSwgdGhlIGxhcmdlciB0aGUgaW5kaWNhdG9yIHdpbGwgYmUuIEFzIHRoZSBudW1iZXIgb2YgY2xhc3NlcyBpbmNyZWFzZXMsIHRoZSBpbmRpY2F0b3Igd2lsbCB0YWtlIG9uIGxhcmdlciB2YWx1ZXMuDQoNCkxldCdzIGNhbGN1bGF0ZSBUQUkgaW5kZXggdG8gY2hlY2sgdGhlIHByb3BlcnRpZXMgb2YgdGhlIHRhYnVsYXRlZCBkYXRhOg0KDQpgYGB7ciB0YWksIGVjaG89VFJVRX0NCnRhYmVsa2EyIDwtIGNsYXNzSW50ZXJ2YWxzKENyZWRpdENhcmQkaW5jb21lLCBuPTcsIHN0eWxlPSJmaXhlZCIsIGZpeGVkQnJlYWtzPXNlcSgwLDE0LGJ5PTIpKQ0KamVua3MudGVzdHModGFiZWxrYTIpDQpgYGANCg0KQXMgd2UgY2FuIHNlZSAtIFRBSSBpbmRleC4uLg0KDQoNCldlIGNhbiB1c2UgZGlmZmVyZW50IHJlY2lwZXMuLi4gKHN0eWxlcyk6DQoNCg0KYGBgIHtyfQ0KdGFiZWxrYTM8LWNsYXNzSW50ZXJ2YWxzKENyZWRpdENhcmQkaW5jb21lLCBuPTEwLCBzdHlsZT0ic2QiKQ0KcGxvdCh0YWJlbGthMyxwYWw9YygxOjEwKSkNCmplbmtzLnRlc3RzKHRhYmVsa2EzKQ0KYGBgDQoNClN0aWxsLCB0aGUgVEFJIGluZGljYXRvciBpcyBub3Qgc2F0aXNmYWN0b3J5LiBXaGF0IHNob3VsZCB3ZSBjaGFuZ2UgaW4gdGhlIGZpbmFsIGZyZXF1ZW5jeSB0YWJsZSBkZXNpZ24/DQoNCmBgYHtyfQ0KaGlzdChDcmVkaXRDYXJkJGluY29tZSkNCmBgYA0KDQoNCiMjIyBDb250aW51b3VzIHZhcmlhYmxlcw0KDQpXZSBjYW4gY2FsY3VsYXRlIHRoZSBhYnNvbHV0ZSBhbmQgcmVsYXRpdmUgZnJlcXVlbmNpZXMgb2YgYSB2ZWN0b3IgeCB3aXRoIHRoZSBmdW5jdGlvbiAnRnJlcScgZnJvbSB0aGUgKkRlc2NUb29scyogcGFja2FnZXMuIENvbnRpbnVvdXMgKG51bWVyaWMpIHZhcmlhYmxlcyB3aWxsIGJlIGN1dCB1c2luZyB0aGUgc2FtZSBsb2dpYyBhcyB1c2VkIGJ5IHRoZSBmdW5jdGlvbiBoaXN0LiBDYXRlZ29yaWNhbCB2YXJpYWJsZXMgd2lsbCBiZSBhZ2dyZWdhdGVkIGJ5IHRhYmxlLiBUaGUgcmVzdWx0IHdpbGwgY29udGFpbiBzaW5nbGUgYW5kIGN1bXVsYXRpdmUgZnJlcXVlbmNpZXMgZm9yIGJvdGgsIGFic29sdXRlIHZhbHVlcyBhbmQgcGVyY2VudGFnZXMuIA0KDQpgYGB7cn0NCnRhYmVsYTQ8LUZyZXEoQ3JlZGl0Q2FyZCRpbmNvbWUsYnJlYWtzPXNlcSgwLDE0LGJ5PTIpLHVzZU5BPSJpZmFueSIpDQoNCnRhYmVsYTQgJT4lDQogIGthYmxlKGNvbC5uYW1lcyA9IGMoIkluY29tZXMgaW4ga1VTRCIsIkZyZXF1ZW5jeSIsIlBlcmNlbnRhZ2UgJSIsIkN1bXVsYXRpdmUgZnJlcXVlbmN5IiwiQ3VtdWxhdGl2ZSBwZXJjZW50YWdlICUiKSkgJT4lDQogIGthYmxlX2NsYXNzaWMoZnVsbF93aWR0aCA9IEYsIGh0bWxfZm9udCA9ICJDYW1icmlhIikgDQpgYGANCg0KQlRXOiB3aGF0IGFib3V0IFRBSSBvZiB0aGF0IHRhYmxlPy4uLg0KDQojIyMgQ2F0ZWdvcmljYWwgdmFyaWFibGVzDQoNCk5vdywgbGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIGNhdGVnb3JpY2FsIGRhdGEgYW5kIG1ha2Ugc29tZSB0YWJ1bGF0aW9ucy4gVGhlICoqeHRhYnMqKiBmdW5jdGlvbiB3b3JrcyBsaWtlIHRhYmxlIGV4Y2VwdCBpdCBjYW4gcHJvZHVjZSB0YWJsZXMgZnJvbSBmcmVxdWVuY2llcyB1c2luZyB0aGUgZm9ybXVsYSBpbnRlcmZhY2UuIA0KDQpMZXQncyBzYXkgd2Ugd2FudCB0byBzZWUgdGhlIHRhYmxlIHdpdGggZGF0YSBvbiBob3cgbWFueSBjYXJkIGFwcGxpY2F0aW9ucyB3YXMgYWNjZXB0ZWQgb3Igbm90Og0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCnh0YWJzKH4gY2FyZCwgZGF0YT1DcmVkaXRDYXJkKQ0KYGBgDQoNCldlIG1heSBlYXNpbHkgcHJvZHVjZSAqY3Jvc3MtdGFicyogKHN0YXR1cyB2cy4gRG9lcyB0aGUgaW5kaXZpZHVhbCBvd24gdGhlaXIgaG9tZT8pIGFzIHdlbGw6DQoNCmBgYHtyfQ0KY3Jvc3N0YWI8LXh0YWJzKH4gY2FyZCArIG93bmVyLCBkYXRhPUNyZWRpdENhcmQpDQpjcm9zc3RhYg0KYGBgDQoNCmFuZCB0cmFuc2Zvcm0gaXQgaW50byBwcmV0dHkgaHRtbCB0YWJsZSB3aXRoIHRoZSBrYWJsZSBmdW5jdGlvbjoNCg0KYGBge3J9DQpjcm9zc3RhYiAlPiUgDQogIGtibCgpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGKSAlPiUNCiAgY29sdW1uX3NwZWMoMSwgYm9sZCA9IFQsIGJvcmRlcl9yaWdodCA9IFQpICU+JQ0KICBjb2x1bW5fc3BlYygyLCBiYWNrZ3JvdW5kID0gInllbGxvdyIpDQpgYGANCg0KDQojIyBEYXRhIFZpc3VhbGl6YXRpb24NCg0KV2Ugd2lsbCBleHBsb3JlIHRoZSAqKiJnZ3Bsb3QyIioqIHBhY2thZ2Ugb2YgdGhlIHRpZHl2ZXJzZSBmb3IgZGF0YSB2aXN1YWxpemF0aW9uIHB1cnBvc2VzLiBUaGUgImdncGxvdDIiIHBhY2thZ2VzIGludm9sdmUgdGhlIHRoZSBmb2xsb3dpbmcgdGhyZWUgbWFuZGF0b3J5IGNvbXBvbmVudHM6DQoNCjEpIERhdGENCjIpIEFuIGFlc3RoZXRpYyBtYXBwaW5nDQozKSBHZW9tcyAoYWthIG9iamVjdHMpDQoNClRoZSBmb2xsb3dpbmcgY29tcG9uZW50cyBjYW4gYWxzbyBvcHRpb25hbGx5IGJlIGFkZGVkOg0KDQo0KSBTdGF0cyAoYWthIHRyYW5zZm9ybWF0aW9ucykNCjUpIFNjYWxlcw0KNikgRmFjZXRzDQo3KSBDb29yZGluYXRlIHN5c3RlbXMNCjgpIFBvc2l0aW9uIGFkanVzdG1lbnRzDQo5KSBUaGVtZXMNCg0KUGxlYXNlIG5vdGUgdGhhdCBjb2RlIGluIHRoaXMgdHV0b3JpYWwgd2FzIGFkYXB0ZWQgZnJvbSBDaGFwdGVycyAzIG9mIHRoZSBib29rICJSIGZvciBEYXRhIFNjaWVuY2UiIGJ5IEhhZGxleSBXaWNraGFtIGFuZCBHYXJyZXR0IEdyb2xlbXVuZC4gIA0KDQpUaGUgZnVsbCBib29rIGNhbiBiZSBmb3VuZCBhdDogaHR0cHM6Ly9yNGRzLmhhZC5jby5uei8NCg0KQSBnb29kIGNoZWF0IHNoZWV0IGZvciBnZ3Bsb3QyIGZ1bmN0aW9ucyBjYW4gYmUgZm91bmQgYXQ6IGh0dHBzOi8vcnN0dWRpby5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTUvMDMvZ2dwbG90Mi1jaGVhdHNoZWV0LnBkZg0KDQoNCiMjIyBTY2F0dGVycGxvdHMNCg0KDQpMZXQncyBjcmVhdGUgYW4gZXh0cmVtZWx5IHNpbXBsZSBzY2F0dGVycGxvdC4gIA0KDQpXZSB3aWxsIHVzZSB0aGUgZnVuY3Rpb24gYGdncGxvdCgpYCB0byBkbyB0aGlzLiANCg0KVGhlIGZvcm1hdCBvZiBhbnkgZ2dwbG90IGdyYXBoIGlzIHRoaXMgZnVuY3Rpb24sIGZvbGxvd2VkIGJ5IGFub3RoZXIgZnVuY3Rpb24gdG8gYWRkIG9iamVjdHMuDQoNClRoZSBvYmplY3RzIG9uIGEgZ3JhcGggaW4gdGhlIGNhc2Ugb2YgYSBzY2F0dGVycGxvdCBhcmUgcG9pbnRzLiBUaGUgZnVuY3Rpb24gd2UgYWRkIHRvIGl0IGlzIGBnZW9tX3BvaW50YC4NCg0KVGhlc2UgZnVuY3Rpb25zIHJlbHkgb24gYSBmdW5jdGlvbiBvbiB0aGUgaW5zaWRlIGNhbGxlZCBgYWVzKClgLg0KDQpUaGUgZGF0YSBhbmQgYWVzdGhldGljIG1hcHBpbmcgY29tcG9uZW50cyBjYW4gYmUgYWRkZWQgdG8gZWl0aGVyIHRoZSBgZ2dwbG90KClgIG9yIGdlb20gZnVuY3Rpb25zLiAgDQoNCmBgYHtyIEdyYXBoIDF9DQpnZ3Bsb3QoZGF0YSA9IG1wZykgKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKQ0KYGBgIA0KDQo8YnI+DQoNClRoaXMgaXMgb25lIG9mIHRoZSBtb3N0IGJhc2ljIGdyYXBocyB0aGF0IG9uZSBjYW4gbWFrZSB1c2luZyB0aGUgZ2dwbG90MiBmcmFtZXdvcmsuIA0KDQpOZXh0LCBsZXQncyBhZGQgY29sb3IuDQoNCmBnZW9tX3BvaW50KClgIHVuZGVyc3RhbmRzIHRoZSBmb2xsb3dpbmcgYWVzdGhldGljczogeCwgeSwgYWxwaGEsIGNvbG9yLCBmaWxsLCBncm91cCwgc2hhcGUsIHNpemUsIGFuZCBzdHJva2UgKHNlZSBoZWxwIGRvY3VtZW50YXRpb24pLg0KDQpMZXQncyBtYXAgdGhlIGNvbG9yIGFyZ3VtZW50IHRvIHRoZSB2YXJpYWJsZSAiY2xhc3MiIGZyb20gbXBnLg0KDQpgYGB7ciBHcmFwaCAyfQ0KZ2dwbG90KG1wZykgKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gZGlzcGwsIHkgPSBod3ksIGNvbG9yID0gY2xhc3MpKQ0KYGBgDQoNCg0KVGhpcyBpcyBub3QgdGhlIG9ubHkgd2F5IHRvIGNvbG9yIG9iamVjdHMuICANCg0KSW5jbHVkaW5nIHRoZSBjb2xvciBhcmd1bWVudCBpbnNpZGUgb2YgdGhlIGBhZXMoKWAgZnVuY3Rpb24gY2FuIG1hcCBjb2xvcnMgdG8gYSBjaG9pY2Ugb2YgdmFyaWFibGUuIA0KDQpIb3dldmVyLCB3ZSBjYW4gc3BlY2lmeSBjb2xvcnMgbWFudWFsbHksIGJ5IHNwZWNpZnlpbmcgY29sb3Igb3V0c2lkZSBvZiB0aGUgYGFlcygpYCBmdW5jdGlvbi4gV2Ugd2lsbCBhbHNvIGlsbHVzdHJhdGUgdGhlICJzaXplIiBhcmd1bWVudC4NCg0KYGBge3IgR3JhcGggM30NCmdncGxvdChtcGcpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBzaXplID0gY2xhc3MpLCBjb2xvciA9ICJibHVlIikNCmBgYA0KDQojIyMgQmFycGxvdHMNCg0KTGFzdGx5LCBsZXQncyBleGFtaW5lIG90aGVyIG9iamVjdHMgdGhhdCB3ZSBjYW4gcGxvdCB1c2luZyBgZ2dwbG90KClgLiBXZSB3aWxsIGNyZWF0ZSBhIGJhciBjaGFydCB1c2luZyB0aGUgZnVuY3Rpb24gYGdlb21fYmFyKClgLiAgDQoNCmBgYHtyIEdyYXBoIDR9DQpnZ3Bsb3QobXBnKSArDQogIGdlb21fYmFyKGFlcyh4ID0gY2xhc3MpKQ0KYGBgDQoNCjxicj4NCg0KV2l0aCB0aGUgYGdlb21fYmFyKClgIGZ1bmN0aW9uLCB3ZSBoYXZlIGEgZ3JlYXQgdXNlLWNhc2UgZm9yIGEgc3RhdCB0cmFuc2Zvcm1hdGlvbi4NCg0KVGhlIGZvbGxvd2luZyBjb2RlIGNhbiBiZSB1c2VkIHRvIGNvbnZlcnQgdGhlc2UgY291bnRzIHRvIHByb3BvcnRpb25zOg0KDQpgYGB7ciBHcmFwaCA1fQ0KZ2dwbG90KG1wZykgKw0KICBnZW9tX2JhcihhZXMoeCA9IGNsYXNzLCB5ID0gc3RhdChwcm9wKSwgZ3JvdXAgPSAxKSkNCmBgYA0KDQojIyMgSGlzdG9ncmFtcw0KDQpOZXh0LCBsZXQncyBjcmVhdGUgYSBoaXN0b2dyYW0gd2l0aCB0aGUgYGdlb21faGlzdG9ncmFtKClgIGZ1bmN0aW9uLg0KDQpgYGB7ciBHcmFwaCA2fQ0KZ2dwbG90KG1wZykgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IGh3eSkpDQpgYGANCg0KVGhlIGBnZW9tX2hpc3RvZ3JhbSgpYCBmdW5jdGlvbiBhY2NlcHRzIHRoZSBhcmd1bWVudCAiYmlud2lkdGgiLCBhbmQgaGFzIHR3byBrZXkgYXJndW1lbnRzIGZvciBjb2xvcjogZmlsbCAodGhpcyBjb250cm9scyB0aGUgb3ZlcmFsbCBjb2xvciksIGFuZCBjb2xvciAodGhpcyBjb250cm9scyB0aGUgYm9yZGVyKS4gIA0KDQpMZXQncyBmaWxsIGFsbCB0aGVzZSBpbjoNCg0KYGBge3IgR3JhcGggN30NCmdncGxvdChtcGcpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBod3kpLCBiaW53aWR0aCA9IDUsIGZpbGwgPSAibmF2eSIsIGNvbG9yID0gImdvbGQiKQ0KYGBgDQoNCjxicj4NCg0KYGdlb21faGlzdG9ncmFtKClgIHByb3ZpZGVzIGEgZ3JlYXQgZXhhbXBsZSB0byBtb2RpZnkgdGhlIHNjYWxlLg0KDQpOb3RpY2UgaW4gdGhpcyBleGFtcGxlIHRoYXQgdGhlIGF4aXMgaXMgYXV0b21hdGljYWxseSBicm9rZW4gdXAgYnkgdW5pdHMgb2YgMTAsIGFuZCBkb2VzIG5vdCBiZWdpbiBhdCAwLg0KDQpXZSBjYW4gbW9kaWZ5IHRoaXMgd2l0aCB0aGUgZnVuY3Rpb24gYHNjYWxlX3hfY29udGludW91cygpYCwgYXMgd2VsbCBhcyB0aGUgeS1heGlzIHdpdGggdGhlIGZ1bmN0aW9uIGBzY2FsZV95X2NvbnRpbnVvdXMoKWAuICANCg0KVGhlcmUgYXJlIHRocmVlIGtleSBhcmd1bWVudHMgd2Ugd2lsbCBmZWVkIHRoaXMgZnVuY3Rpb246ICJicmVha3MiLCAibGltaXRzIiwgYW5kICJleHBhbmQiLg0KDQoiYnJlYWtzIiB3aWxsIGRlZmluZSB0aGUgYnJlYWtzIG9uIHRoZSBheGlzLiANCg0KImxpbWl0cyIgd2lsbCBkZWZpbmUgdGhlIGJlZ2lubmluZyBhbmQgZW5kIG9mIHRoZSBheGlzLCBhbmQgdGhlICJleHBhbmQiIGFyZ3VtZW50IGNhbiBiZSB1c2VkIHRvIHN0YXJ0IHRoZSBheGVzIGF0IDAgYnkgdXNpbmcgImV4cGFuZCA9IGMoMCwwKSIuIA0KDQpgYGB7ciBHcmFwaCA4fQ0KZ2dwbG90KG1wZykgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IGh3eSksIGJpbndpZHRoID0gNSwgZmlsbCA9ICJuYXZ5IiwgY29sb3IgPSAiZ29sZCIpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCA0NSwgNSksIGxpbWl0cyA9IGMoMCwgNTApLCBleHBhbmQgPSBjKDAsMCkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCA5MCwgMTApLCBsaW1pdHMgPSBjKDAsIDkwKSwgZXhwYW5kID0gYygwLDApKQ0KYGBgDQoNCiMjIyBCb3hwbG90cw0KDQpOZXh0LCB3ZSB3aWxsIGNyZWF0ZSBib3hwbG90cy4NCg0KYGBge3IgR3JhcGggOX0NCnAgPC0gZ2dwbG90KG1wZykgKw0KICBnZW9tX2JveHBsb3QoYWVzKHggPSBjbGFzcywgeSA9IGN0eSwgZmlsbCA9IGNsYXNzKSkNCnANCmBgYA0KDQojIyMgRmFjZXRzDQoNCkZhY2V0aW5nIGdlbmVyYXRlcyBzbWFsbCBtdWx0aXBsZXMgZWFjaCBzaG93aW5nIGEgZGlmZmVyZW50IHN1YnNldCBvZiB0aGUgZGF0YS4gU21hbGwgbXVsdGlwbGVzIGFyZSBhIHBvd2VyZnVsIHRvb2wgZm9yIGV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXM6IHlvdSBjYW4gcmFwaWRseSBjb21wYXJlIHBhdHRlcm5zIGluIGRpZmZlcmVudCBwYXJ0cyBvZiB0aGUgZGF0YSBhbmQgc2VlIHdoZXRoZXIgdGhleSBhcmUgdGhlIHNhbWUgb3IgZGlmZmVyZW50LiANCg0KUmVhZCBtb3JlIGFib3V0IGZhY2V0cyBbaGVyZV0oaHR0cHM6Ly9nZ3Bsb3QyLWJvb2sub3JnL2ZhY2V0Lmh0bWwpLg0KDQpOb3RpY2UgaW4gdGhpcyBkb2N1bWVudCB0aGUgdXNlIG9mIHRoZSBmaWcuaGVpZ2h0IGFuZCBmaWcud2lkdGggb3B0aW9ucy4NCg0KS2V5IGFyZ3VtZW50cyB0byBgZmFjZXRfd3JhcCgpYCBhcmUgImZhY2V0cyIsICJucm93IiwgYW5kICJuY29sIi4NCg0KYGBge3IgR3JhcGggMTAsIGZpZy5oZWlnaHQgPSA4LCBmaWcud2lkdGggPSAxMn0NCmdncGxvdChtcGcpICsNCiAgZ2VvbV9ib3hwbG90KGFlcyh4ID0gY2xhc3MsIHkgPSBjdHksIGZpbGwgPSBjbGFzcykpICsNCiAgZmFjZXRfd3JhcChmYWNldHMgPSB+Y3lsLCBucm93ID0gMiwgbmNvbCA9IDIpDQpgYGANCg0KIyMjIENvb3JkaW5hdGVzDQoNCk90aGVyIGNvb3JkaW5hdGUgc3lzdGVtcyBjYW4gYmUgYXBwbGllZCB0byBncmFwaHMgY3JlYXRlZCBmcm9tIGdncGxvdDIuICANCg0KT25lIGV4YW1wbGUgaXMgYGNvb3JkX3BvbGFyKClgLCB3aGljaCB1c2VzIHBvbGFyIGNvb3JkaW5hdGVzLiBNb3N0IG9mIHRoZXNlIGFyZSBxdWl0ZSByYXJlLiBQcm9iYWJseSB0aGUgbW9zdCBjb21tb24gb25lIGlzIGBjb29yZF9mbGlwKClgLCB3aGljaCB3aWxsIGZsaXAgdGhlIFggYW5kIFkgYXhlcy4gTGV0J3MgYWxzbyBpbGx1c3RyYXRlIHRoZSBgbGFicygpYCBmdW5jdGlvbiwgd2hpY2ggY2FuIGJlIHVzZWQgdG8gY2hhbmdlIGxhYmVscy4NCg0KYGBge3IgR3JhcGggMTF9DQpnZ3Bsb3QobXBnKSArDQogIGdlb21fYmFyKGFlcyh4ID0gY2xhc3MsIGZpbGwgPSBmYWN0b3IoY3lsKSkpICsNCiAgbGFicyh0aXRsZSA9ICJDeWxpbmRlcnMgYnkgQ2xhc3MiLCBmaWxsID0gImN5bGluZGVycyIpICsNCiAgY29vcmRfZmxpcCgpDQpgYGANCg0KPGJyPg0KDQpUaGVzZSBiYXJzIGFyZSBzdGFja2VkIG9uIHRvcCBvZiBlYWNoIG9mIG90aGVyLCBkdWUgdG8gdGhlICJjeWwiIHZhcmlhYmxlIGJlaW5nIG1hcHBlZCB0byB0aGUgImZpbGwiIGFyZ3VtZW50LiBUaGVyZSBhcmUgdmFyaW91cyBwb3NpdGlvbiBhZGp1c3RtZW50cyB0aGF0IGNhbiBiZSB1c2VkLiBBZ2FpbiwgbW9zdCBvZiB0aGVzZSBhcmUgbm90IHZlcnkgY29tbW9uLCBidXQgYSBjb21tb24gb25lIGlzIHRoZSBhcmd1bWVudCAicG9zaXRpb24gPSAnZG9kZ2UnIiwgd2hpY2ggd2lsbCBwdXQgaXRlbXMgc2lkZS1ieS1zaWRlLiAgIA0KDQpTZWUgdGhpcyBleGFtcGxlOg0KDQpgYGB7ciBHcmFwaCAxMn0NCmdncGxvdChtcGcpICsNCiAgZ2VvbV9iYXIoYWVzKHggPSBjbGFzcywgZmlsbCA9IGZhY3RvcihjeWwpKSwgcG9zaXRpb24gPSAiZG9kZ2UiKSArDQogIGxhYnModGl0bGUgPSAiQ3lsaW5kZXJzIGJ5IENsYXNzIiwgZmlsbCA9ICJjeWxpbmRlcnMiKSArIA0KICBjb29yZF9mbGlwKCkNCmBgYA0KDQojIyMgVGhlbWVzDQoNCkxhc3RseSwgd2UgY2FuIGFsdGVyIHRoZSAidGhlbWUiLCBvciB0aGUgb3ZlcmFsbCBhcHBlYXJhbmNlIG9mIG91ciBwbG90LiAgDQoNCkkgcmVjb21tZW5kIHVzaW5nIHRoZSBnZ1RoZW1lQXNzaXN0IHBhY2thZ2UsIGJlY2F1c2UgdGhpcyB3aWxsIG1ha2UgdGhpcyBpbmNyZWRpYmx5IGVhc3ksIHdpdGggYW4gaW50ZXJmYWNlIHRoYXQgd2lsbCBhdXRvbWF0aWNhbGx5IGdlbmVyYXRlIHJlcHJvZHVjaWJsZSBjb2RlLg0KDQpUaGlzIGNhbiBiZSB1c2VkIGJ5IGhpZ2hsaWdodGluZyBhIGdncGxvdDIgb2JqZWN0LCBhbmQgbmF2aWdhdGluZyB0byBBZGRpbnMgPiBnZ3Bsb3QgVGhlbWUgQXNzaXN0YW50Lg0KDQpXZSdsbCBtYWtlIHRoZSBmb2xsb3dpbmcgY2hhbmdlczogZWxpbWluYXRpbmcgdGhlIHBhbmVsIGdyaWQgbGluZXMsIGVsaW1pbmF0aW5nIGF4aXMgdGlja3MsIGFkZGluZyBhIHRpdGxlIGNhbGxlZCAiQm94cGxvdCBFeGFtcGxlIiwgbWFraW5nIGl0IGJpZ2dlciBhbmQgcHV0dGluZyBpdCBpbiBib2xkLCBhbmQgYWRqdXN0aW5nIGl0IHRvIHRoZSBjZW50ZXIuDQoNCmBgYHtyIEdyYXBoIDEzfQ0KIyBwDQpwICsgdGhlbWUoYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShsaW5ldHlwZSA9ICJibGFuayIpLA0KICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUobGluZXR5cGUgPSAiYmxhbmsiKSwNCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9saW5lKGxpbmV0eXBlID0gImJsYW5rIiksDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsDQogICAgICAgIGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41KSkgK2xhYnModGl0bGUgPSAiQm94cGxvdCBFeGFtcGxlIikNCmBgYA0KDQo8YnI+DQoNClRoZXJlIGFyZSBtYW55IG1vcmUgZXhhbXBsZXMgb2YgdGhpbmdzIHRoYXQgY2FuIGJlIGRvbmUgd2l0aCBnZ3Bsb3QyLiAgDQoNCkl0IGlzIGFuIGFtYXppbmdseSBwb3dlcmZ1bCBhbmQgZmxleGlibGUgcGFja2FnZSwgYW5kIGl0IGlzIHdvcnRoIGdldHRpbmcgYWNxdWFpbnRlZCB3aXRoIHRoZSBjaGVhdCBzaGVldC4NCg0KIyMgRXhlcmNpc2UgMS4gDQoNCiJVc2luZyBkYXRhIG9uIGNyZWRpdCBjYXJkIGFwcGxpY2F0aW9ucycgc3RhdHVzIHBsZWFzZSBwcmVzZW50IHRoZSBmcmVxdWVuY3kgdGFibGUgd2l0aCB0aGUgbmljZSwga2FibGUgZm9ybWF0IGZvciBhdmVyYWdlIG1vbnRobHkgY3JlZGl0IGNhcmQgZXhwZW5kaXR1cmVzIG9mIGFwcGxpY2FudHMuIg0KDQpUaGlzIHBpZWNlIG9mIFIgY29kZSB3YXMgd3JpdHRlbiB0byBhbmFseXplIHVzZXJzJyBhdmVyYWdlIG1vbnRobHkgc3BlbmRpbmcgYnkgcGVyZm9ybWluZyBzb21lIG9wZXJhdGlvbnMgb24gYSBjcmVkaXQgY2FyZCBzcGVuZGluZyBkYXRhc2V0Lg0KDQpgYGB7ciBleDEsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpBdmdfRXhwPC0gQ3JlZGl0Q2FyZCAlPiUNCiAgZmlsdGVyKG1vbnRocyAhPSAwKSAlPiUNCiAgbXV0YXRlKG1vbnRobHlfYXZnX2V4cCA9IGV4cGVuZGl0dXJlIC8gbW9udGhzKSAlPiUNCiAgYXJyYW5nZShkZXNjKG1vbnRobHlfYXZnX2V4cCkpDQoNCkF2Z19FeHAgPC0gQXZnX0V4cCAlPiUNCiAgYXJyYW5nZShkZXNjKG1vbnRobHlfYXZnX2V4cCkpDQoNCmdncGxvdChBdmdfRXhwKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gbW9udGhseV9hdmdfZXhwKSkNCg0KDQpmcmVxdWVuY3lfdGFibGUgPC0gRnJlcShBdmdfRXhwJG1vbnRobHlfYXZnX2V4cCwgdXNlTkEgPSAiaWZhbnkiKQ0KDQprYWJsZV90YWJsZV8yIDwtIGZyZXF1ZW5jeV90YWJsZSAlPiUNCiAga2FibGUoY29sLm5hbWVzID0gYygiQXZnIGV4cCBpbiBrVVNEIiwgIkZyZXF1ZW5jeSIsICJQZXJjZW50YWdlICUiLCAiQ3VtdWxhdGl2ZSBmcmVxdWVuY3kiLCAiQ3VtdWxhdGl2ZSBwZXJjZW50YWdlICUiKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAiYm9yZGVyZWQiKSAlPiUgDQogIGthYmxlX2NsYXNzaWMoZnVsbF93aWR0aCA9IEZBTFNFLCBodG1sX2ZvbnQgPSAiQXJpYWwiKQ0KDQpmcmVxdWVuY3lfdGFibGUNCmthYmxlX3RhYmxlXzINCmBgYA0KDQpSZWNvcmRzIHdob3NlIG1vbnRocyBjb2x1bW4gd2FzIG5vdCAwIHdlcmUgZmlsdGVyZWQgZnJvbSB0aGUgYENyZWRpdENhcmRgIGRhdGEgc2V0LiBUaGlzIGlzIHRvIHByZXZlbnQgdGhlIGRpdmlzb3IgZnJvbSBiZWluZyB6ZXJvIHdoZW4gY2FsY3VsYXRpbmcgbW9udGhseSBleHBlbnNlcy4NCkEgbmV3IGNvbHVtbiBgKG1vbnRobHlfYXZnX2V4cClgIGlzIGFkZGVkIHdpdGggdGhlIGBtdXRhdGUoKWAgZnVuY3Rpb24uIFRoaXMgY29sdW1uIHNob3dzIHRoZSBhdmVyYWdlIG1vbnRobHkgZXhwZW5kaXR1cmUsIGNhbGN1bGF0ZWQgYnkgZGl2aWRpbmcgdG90YWwgZXhwZW5kaXR1cmVzIGJ5IHRoZSBudW1iZXIgb2YgbW9udGhzLg0KV2l0aCBhcnJhbmdlYChkZXNjKG1vbnRobHlfYXZnX2V4cCkpYCB0aGUgZGF0YSBzZXQgaXMgc29ydGVkIGluIGRlc2NlbmRpbmcgb3JkZXIgYnkgYXZlcmFnZSBtb250aGx5IHNwZW5kaW5nIGFtb3VudC4NClVzaW5nIHRoZSBgZ2dwbG90KClgIGZ1bmN0aW9uLCBhIGhpc3RvZ3JhbSBpcyBkcmF3biBmb3IgdGhlIGBtb250aGx5X2F2Z19leHBgIHZhcmlhYmxlLiBUaGlzIGNoYXJ0IHNob3dzIHVzZXJzJyBhdmVyYWdlIG1vbnRobHkgc3BlbmRpbmcgZGlzdHJpYnV0aW9uLg0KDQpBIGZyZXF1ZW5jeSB0YWJsZSBmb3IgYG1vbnRobHlfYXZnX2V4cGAgaXMgY3JlYXRlZCB3aXRoIHRoZSBgRnJlcSgpYCBmdW5jdGlvbi4gVGhpcyB0YWJsZSBpbmNsdWRlcyB0aGUgbnVtYmVyIG9mIHRpbWVzIGVhY2ggc3BlbmQgdmFsdWUgb2NjdXJzIGFuZCB0aGUgcGVyY2VudGFnZSBvZiB0aG9zZSB2YWx1ZXMgaW4gdGhlIHRvdGFsLg0KVGhlIGZyZXF1ZW5jeSB0YWJsZSBpcyBmb3JtYXR0ZWQgYWVzdGhldGljYWxseSBieSB1c2luZyBga2FibGUoKWAgYW5kIGBrYWJsZV9zdHlsaW5nKClgIGZ1bmN0aW9ucy4gVGFibGUgaGVhZGVycyBhcmUgc3BlY2lmaWVkIGFuZCBhIGNsYXNzaWMgSFRNTCB0YWJsZSB2aWV3IGlzIHByb3ZpZGVkIHdpdGggYGthYmxlX2NsYXNzaWMoKWAuIFRoZSB0YWJsZSBpcyBwcmVzZW50ZWQgaW4gYSB1c2VyLWZyaWVuZGx5IGZvcm1hdCB3aXRoIGRlc2lnbmF0ZWQgaGVhZGluZ3MgKCJBdmcgZXhwIGluIGtVU0QiLCAiRnJlcXVlbmN5IiBldGMuKS4NCg0KVGhpcyBjb2RlIGlzIGEgdHlwaWNhbCBleGFtcGxlIGZvciBmaW5hbmNpYWwgZGF0YSBhbmFseXNpcy4gQ2FsY3VsYXRpbmcga2V5IG1ldHJpY3Mgc3VjaCBhcyBhdmVyYWdlIG1vbnRobHkgc3BlbmQgYWxsb3dzIGZvciBhIGJldHRlciB1bmRlcnN0YW5kaW5nIG9mIGN1c3RvbWVyIHNwZW5kaW5nIGJlaGF2aW9yLiBWaXN1YWxpemF0aW9ucyBhbmQgcHJlc2VudGF0aW9ucyBzdWNoIGFzIGhpc3RvZ3JhbXMgYW5kIGZyZXF1ZW5jeSB0YWJsZXMsIEl0IHByb3ZpZGVzIGluLWRlcHRoIGluc2lnaHRzIGluIGFuYWx5emluZyBzdWNoIGRhdGEgYW5kIGNhbiBoZWxwIG1ha2Ugc3RyYXRlZ2ljIGRlY2lzaW9ucy4NCg0KDQojIyBFeGVyY2lzZSAyLg0KDQoiVGhlIGRhdGEgY29tZXMgZnJvbSBbaHR0cHM6Ly9mbGl4Z2VtLmNvbS9dKGh0dHBzOi8vZmxpeGdlbS5jb20vKSAoZGF0YXNldCB2ZXJzaW9uIGFzIG9mIE1hcmNoIDEyLCAyMDIxKS4gVGhlIGRhdGEgY29udGFpbnMgaW5mb3JtYXRpb24gb24gOTQyNSBtb3ZpZXMgYW5kIHNlcmllcyBhdmFpbGFibGUgb24gTmV0bGl4LiINCg0KVGhpcyBwaWVjZSBvZiBSIGNvZGUgZG93bmxvYWRzIGFuZCByZWFkcyBhIENTViBmaWxlIGZyb20gdGhlIGludGVybmV0IGFuZCB0aGVuIGxvYWRzIHRoZSBkYXRhc2V0IGludG8gdGhlIFIgZW52aXJvbm1lbnQuDQoNCmBgYHtyIGV4MiwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCmRvd25sb2FkLmZpbGUoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9rZmxpc2lrb3dza2kvZHMvbWFzdGVyL25ldGZsaXgtZGF0YXNldC5jc3Y/cmF3PXRydWUiLCBkZXN0ZmlsZSA9ImRhbmUuY3N2Iixtb2RlPSJ3YiIpDQpteWRhdGE8LXJlYWQuY3N2KGZpbGU9ImRhbmUuY3N2IixlbmNvZGluZyA9IlVURi04IixoZWFkZXI9VFJVRSxzZXAgPSAiLCIpDQphdHRhY2gobXlkYXRhKQ0KYGBgDQoNClRoaXMgY29kZSBpcyBhIHR5cGljYWwgZGF0YSBsb2FkaW5nIHByb2Nlc3MgdXNlZCBhcyB0aGUgaW5pdGlhbCBzdGFnZSBpbiBkYXRhIGFuYWx5c2lzIHByb2plY3RzLiBTdWNjZXNzZnVsIHVwbG9hZCBvZiB0aGUgZGF0YXNldCBpcyB0aGUgZmlyc3Qgc3RlcCBpbiB0aGUgYW5hbHlzaXMgcHJvY2Vzcw0KDQoiQW5zd2VyIHdpdGggdGhlIG1vc3QgYXBwcm9wcmlhdGUgZGF0YSB2aXN1YWxpemF0aW9uIGZvciB0aGUgZm9sbG93aW5nIHF1ZXN0aW9uczoiDQoNCiIxLiBXaGF0IGlzIHRoZSBkaXN0cmlidXRpb24gb2YgSW1kYiBzY29yZXMgZm9yIFBvbGlzaCBtb3ZpZXMgYW5kIG1vdmllLXNlcmllcz8iDQoNClRoZSBwcm92aWRlZCBSIGNvZGUgYWltcyB0byB2aXN1YWxpemUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBJTURiIHNjb3JlcyBmb3IgUG9saXNoIG1vdmllcyBhbmQgbW92aWUtc2VyaWVzIHRoYXQgYXJlIGF2YWlsYWJsZSBpbiBQb2xhbmQsIHVzaW5nIHR3byBkaWZmZXJlbnQgdHlwZXMgb2YgcGxvdHM6IGEgYmFyIGNoYXJ0IGFuZCBhIGhpc3RvZ3JhbS4NCg0KYGBge3IgZXgyXzEsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpwb2xpc2hfbW92aWVzX3NlcmllcyA8LSBteWRhdGEgJT4lIA0KICBmaWx0ZXIoTGFuZ3VhZ2VzID09ICJQb2xpc2giLCBDb3VudHJ5LkF2YWlsYWJpbGl0eSA9PSAiUG9sYW5kIikNCg0KZ2dwbG90KHBvbGlzaF9tb3ZpZXNfc2VyaWVzKSArDQogIGdlb21fYmFyKGFlcyh4ID0gSU1EYi5TY29yZSwgZmlsbCA9IGZhY3RvcihJTURiLlNjb3JlKSkpICsNCiAgbGFicyh0aXRsZSA9ICJQb2xpc2ggbW92aWUgcmF0aW5ncyIsIGZpbGwgPSAicmF0aW5ncyIpICsNCiAgY29vcmRfZmxpcCgpDQoNCmdncGxvdChwb2xpc2hfbW92aWVzX3NlcmllcykgKw0KICANCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBJTURiLlNjb3JlKSwgYmlud2lkdGggPSAwLjEsIGZpbGwgPSAiYmxhY2siLCBjb2xvciA9ICJ3aGl0ZSIpDQpgYGANCg0KSW4gdGhlIGZpcnN0IHBhcnQgb2YgdGhlIGNvZGUsIHdlIGZpbHRlcmVkIHRoZSBkYXRhc2V0IHRvIG9ubHkgaW5jbHVkZSBlbnRyaWVzIHdoZXJlIHRoZSBsYW5ndWFnZSBpcyBQb2xpc2ggYW5kIHRoZSBtb3ZpZSBvciBzZXJpZXMgaXMgYXZhaWxhYmxlIGluIFBvbGFuZC4NCg0KYGZpbHRlcigpYDogVGhpcyBmdW5jdGlvbiBpcyB1c2VkIHRvIHN1YnNldCB0aGUgZGF0YSBiYXNlZCBvbiBzcGVjaWZpZWQgY29uZGl0aW9ucy4gSW4gdGhpcyBjYXNlLCBpdCBlbnN1cmVzIHRoYXQgb25seSBQb2xpc2ggbW92aWVzIGFuZCBzZXJpZXMgYXZhaWxhYmxlIGluIFBvbGFuZCBhcmUgaW5jbHVkZWQuDQoNCkluIHRoZSBzZWNvbmQgcGFydCwgd2UgY3JlYXRlZCBhIGJhciBjaGFydCBvZiBJTURiIHNjb3JlcyB0byBzaG93IHRoZSBmcmVxdWVuY3kgb2YgZWFjaCBzY29yZS4NCmBnZ3Bsb3QoKWA6IFRoZSBtYWluIGZ1bmN0aW9uIHRoYXQgc3RhcnRzIGEgcGxvdC4NCmBnZW9tX2JhcigpYDogVGhpcyBnZW9tIGNyZWF0ZXMgYSBiYXIgY2hhcnQuIFRoZSBgYWVzKClgIGZ1bmN0aW9uIGluc2lkZSBtYXBzIElNRGIgcmF0aW5ncyB0byB0aGUgeC1heGlzIGFuZCBmaWxscyBhcmUgY29sb3JlZCBiYXNlZCBvbiB1bmlxdWUgSU1EYiByYXRpbmdzLiANClVzaW5nIGBmYWN0b3IoSU1EYi5TY29yZSlgLEl0IGNhdXNlcyBlYWNoIGRpZmZlcmVudCBzY29yZSB0byBoYXZlIGEgZGlmZmVyZW50IGNvbG9yLg0KYGxhYnMoKWA6IFVzZWQgdG8gYWRkIGxhYmVscyB0byB0aGUgY2hhcnQsIGluY2x1ZGluZyBhIHRpdGxlIGFuZCBkZXNjcmlwdGlvbiB0aXRsZS4NCmBjb29yZF9mbGlwKClgOiBGbGlwcyB0aGUgeCBhbmQgeSBheGVzIHRvIG1ha2UgdGhlIGdyYXBoIGhvcml6b250YWw7IHRoaXMgY2FuIGltcHJvdmUgcmVhZGFiaWxpdHkgd2hlbiB4LWF4aXMgbGFiZWxzIGFyZSBudW1iZXJzIG9yIHRoZXJlIGFyZSBtYW55IGNhdGVnb3JpZXMuDQoNCkluIHRoZSB0aGlyZCBwYXJ0IG9mIHRoZSBjb2RlLCB3ZSBjcmVhdGVkIGEgaGlzdG9ncmFtIHRvIGRpc3BsYXkgdGhlIGRpc3RyaWJ1dGlvbiBvZiBJTURiIHNjb3JlcyBhbW9uZyBQb2xpc2ggbW92aWVzIGFuZCBUViBzZXJpZXMuDQoNCmBnZW9tX2hpc3RvZ3JhbSgpYDogVGhpcyBnZW9tIGNyZWF0ZXMgYSBoaXN0b2dyYW0gd2l0aCBgYmlud2lkdGggPSAwLjFgIHNwZWNpZnlpbmcgdGhlIHdpZHRoIG9mIGVhY2ggYmluLiBUaGUgYmluIHdpZHRoIGRldGVybWluZXMgdGhlIGxldmVsIG9mIGRldGFpbCBpbiB0aGUgaGlzdG9ncmFtOyBhIHNtYWxsZXIgY2hhbWJlciB3aWR0aCwNCmNhbiBwcm92aWRlIGEgbW9yZSBkZXRhaWxlZCB2aWV3IG9mIHRoZSBkaXN0cmlidXRpb24uDQpgYWVzKHggPSBJTURiLlNjb3JlKWA6IFNwZWNpZmllcyB0aGF0IElNRGIgc2NvcmVzIHNob3VsZCBiZSBwbG90dGVkIG9uIHRoZSB4LWF4aXMuDQpgZmlsbD0iYmxhY2siLCBjb2xvcj0id2hpdGUiYDogSW5jcmVhc2VzIHZpc3VhbCBjb250cmFzdCBhbmQgY2xhcml0eSBieSBzZXR0aW5nIHRoZSBjb2xvciBvZiBiYXJzIHRvIGJsYWNrIGFuZCB0aGVpciBib3JkZXJzIHRvIHdoaXRlDQoNCkJvdGggdmlzdWFsaXphdGlvbnMgc2VydmUgZGlmZmVyZW50IHB1cnBvc2VzOg0KDQpCYXIgQ2hhcnQ6IFVzZWZ1bCBmb3Igc2VlaW5nIHRoZSBleGFjdCBmcmVxdWVuY3kgb2YgZWFjaCBJTURiIHNjb3JlIGFuZCBjb21wYXJpbmcgZnJlcXVlbmNpZXMgYWNyb3NzIGRpZmZlcmVudCBzY29yZXMuDQpIaXN0b2dyYW06IFByb3ZpZGVzIGEgbW9yZSBnZW5lcmFsIHZpZXcgb2YgdGhlIGRpc3RyaWJ1dGlvbiBvZiBzY29yZXMsIGhlbHBpbmcgdG8gdW5kZXJzdGFuZCB0aGUgc2hhcGUgb2YgdGhlIGRpc3RyaWJ1dGlvbiAoZS5nLiwgd2hldGhlciBpdCBpcyBub3JtYWwsIHNrZXdlZCwgaGFzIG91dGxpZXJzLCBldGMuKS4NClRvZ2V0aGVyLCB0aGVzZSBwbG90cyBvZmZlciBhIGNvbXByZWhlbnNpdmUgdmlldyBvZiBob3cgSU1EYiBzY29yZXMgYXJlIGRpc3RyaWJ1dGVkIGFtb25nIFBvbGlzaCBtb3ZpZXMgYW5kIHNlcmllcyBhdmFpbGFibGUgaW4gUG9sYW5kLCB3aGljaCBjYW4gYmUgaW5zaWdodGZ1bCBmb3IgdW5kZXJzdGFuZGluZyBhdWRpZW5jZSByZWNlcHRpb24gYW5kIHRoZSBxdWFsaXR5IGRpc3RyaWJ1dGlvbiBvZiB0aGUgbG9jYWwgZmlsbSBhbmQgc2VyaWVzIGluZHVzdHJ5Lg0KDQoiMi4gV2hhdCBpcyB0aGUgZGVuc2l0eSBmdW5jdGlvbiBvZiBJbWRiIHNjb3JlcyBmb3IgUG9saXNoIG1vdmllcyBhbmQgbW92aWUtc2VyaWVzPyINCg0KVGhlIHByb3ZpZGVkIFIgY29kZSBzbmlwcGV0IGlzIHVzZWQgdG8gY3JlYXRlIGEgZGVuc2l0eSBwbG90IGZvciBJTURiIHNjb3JlcyBvZiBQb2xpc2ggbW92aWVzIGFuZCBzZXJpZXMsIHVzaW5nIHRoZSBgZ2dwbG90MmAgcGFja2FnZS4gVGhpcyB0eXBlIG9mIHZpc3VhbGl6YXRpb24gaXMgdXNlZnVsIGZvciBleGFtaW5pbmcgdGhlIGRpc3RyaWJ1dGlvbidzIHNoYXBlLCBjZW50cmFsIHRlbmRlbmN5LCBhbmQgdmFyaWFiaWxpdHkuDQoNCmBgYHtyIGV4Ml8yLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZ2dwbG90KHBvbGlzaF9tb3ZpZXNfc2VyaWVzLCBhZXMoeCA9IElNRGIuU2NvcmUpKSArDQogIGdlb21fZGVuc2l0eShmaWxsID0gInNreWJsdWUiLCBjb2xvciA9ICJibGFjayIpICsNCiAgbGFicyh0aXRsZSA9ICJEZW5zaXR5b2YgSU1EYiBzY29yZXMgb2YgcG9saXNoIG1vdmllcyBhbmQgc2VyaWVzIiwNCiAgICAgICB4ID0gIklNRGIgU2NvcmUiLCB5ID0gIkRlbnNpdHkiKQ0KYGBgDQoNCmBhZXMoeCA9IElNRGIuU2NvcmUpYDogRGVmaW5lcyB0aGUgYWVzdGhldGljIG1hcHBpbmcgZm9yIHRoZSBncmFwaCB3aGVyZSBJTURiIHNjb3JlcyBhcmUgbWFwcGVkIHRvIHRoZSB4LWF4aXMsDQpIZSBzdGF0ZXMgdGhhdCB0aGUgaW50ZW5zaXR5IHdpbGwgYmUgY2FsY3VsYXRlZCBhY2NvcmRpbmcgdG8gSU1EYiBzY29yZXMuDQoNCmBnZW9tX2RlbnNpdHkoKWA6IEFkZHMgYSBzbW9vdGhlZCBkZW5zaXR5IGVzdGltYXRlIHRvIHRoZSBjaGFydC4gRGVuc2l0eSBwbG90cyBhcmUgdXNlZnVsIGZvciB2aXN1YWxpemluZyB0aGUgZGlzdHJpYnV0aW9uIG9mIGEgdmFyaWFibGUgYW5kIGFyZSBwYXJ0aWN1bGFybHkgdXNlZnVsIHdoZW4gdGhlIGV4YWN0IHNoYXBlIG9mIHRoZSBkaXN0cmlidXRpb24gaXMgb2YgaW50ZXJlc3QuDQoNCmBmaWxsID0gInNreWJsdWUiYDogU3BlY2lmaWVzIHRoZSBjb2xvciB1c2VkIHRvIGZpbGwgdGhlIGFyZWEgdW5kZXIgdGhlIGRlbnNpdHkgY3VydmUsIGluY3JlYXNlcyB2aXN1YWwgYXBwZWFsIGFuZCByZWFkYWJpbGl0eS4NCmBjb2xvciA9ICJibGFjayJgOiBEZWZpbmVzIHRoZSBjb2xvciBvZiB0aGUgZGVuc2l0eSBjdXJ2ZSBvdXRsaW5lLCBwcm92aWRpbmcgY29udHJhc3QgYWdhaW5zdCB0aGUgZmlsbCBmb3IgYmV0dGVyIGNsYXJpdHkuDQoNCmBsYWJzKClgOiBBZGRzIGxhYmVscyB0byB0aGUgcGxvdCwgaW5jbHVkaW5nIHRoZSBtYWluIHRpdGxlIGFuZCBheGlzIGxhYmVscy4NCg0KVGhlIGRlbnNpdHkgcGxvdCBjcmVhdGVkIGJ5IHRoaXMgY29kZSBwcm92aWRlcyBhIHZpc3VhbCBpbnRlcnByZXRhdGlvbiBvZiB0aGUgZGlzdHJpYnV0aW9uIG9mIElNRGIgc2NvcmVzIHdpdGhpbiB0aGUgZmlsdGVyZWQgc3Vic2V0IG9mIFBvbGlzaCBtb3ZpZXMgYW5kIHNlcmllcy4gVW5saWtlIGhpc3RvZ3JhbXMsIGRlbnNpdHkgcGxvdHMgcHJvdmlkZSBhIGNvbnRpbnVvdXMgY3VydmUgcmVwcmVzZW50aW5nIHRoZSBkaXN0cmlidXRpb24sIHdoaWNoIGNhbiBiZSBtb3JlIGluZm9ybWF0aXZlIGZvciBpZGVudGlmeWluZyBtb2Rlcywgc3ltbWV0cnksIGFuZCBza2V3bmVzcyBpbiB0aGUgZGF0YS4NCg0KVGhpcyB2aXN1YWxpemF0aW9uIGlzIHBhcnRpY3VsYXJseSB1c2VmdWwgaW4gaWRlbnRpZnlpbmcgdGhlIGNvbmNlbnRyYXRpb24gb2YgZGF0YSBwb2ludHMgYW5kIGFueSBwb3RlbnRpYWwgb3V0bGllcnMuIEJ5IHVzaW5nIGEgZGVuc2l0eSBwbG90LCBhbmFseXN0cyBhbmQgdmlld2VycyBjYW4gcXVpY2tseSBncmFzcCB0aGUgcmFuZ2UgYW5kIGNvbW1vbmFsaXR5IG9mIElNRGIgc2NvcmVzLCBwb3RlbnRpYWxseSBndWlkaW5nIGZ1cnRoZXIgc3RhdGlzdGljYWwgYW5hbHlzaXMgb3IgZGVjaXNpb24tbWFraW5nIHJlbGF0ZWQgdG8gY29udGVudCBxdWFsaXR5IGFuZCB2aWV3ZXIgcHJlZmVyZW5jZXMgaW4gdGhlIFBvbGlzaCBtZWRpYSBjb250ZXh0Lg0KDQozLiBXaGF0IGFyZSB0aGUgbW9zdCBwb3B1bGFyIGxhbmd1YWdlcyBhdmFpbGFibGUgb24gTmV0ZmxpeD8NCg0KVGhlIHByb3ZpZGVkIFIgY29kZSBzbmlwcGV0IGFuYWx5emVzIGEgZGF0YXNldCB0byBmaW5kIHRoZSBmcmVxdWVuY3kgb2YgZGlmZmVyZW50IGxhbmd1YWdlcyB1c2VkIGluIGNvbnRlbnQgYXZhaWxhYmxlIG9uIE5ldGZsaXgsIGZvY3VzaW5nIHBhcnRpY3VsYXJseSBvbiB0aGUgdG9wIGZpdmUgbGFuZ3VhZ2VzDQoNCmBgYHtyIGV4Ml8zLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGdncGxvdDIpDQoNCiMgQXNzdW1pbmcgbXlkYXRhIGlzIGFscmVhZHkgcmVhZCBhbmQgYXZhaWxhYmxlDQpsYW5ndWFnZV9jb3VudCA8LSBteWRhdGEgJT4lDQogIHRpZHlyOjpzZXBhcmF0ZV9yb3dzKExhbmd1YWdlcywgc2VwPSIsICIpICU+JQ0KICBkcGx5cjo6Z3JvdXBfYnkoTGFuZ3VhZ2VzKSAlPiUNCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudCA9IGRwbHlyOjpuKCkpICU+JQ0KICBkcGx5cjo6YXJyYW5nZShkZXNjKGNvdW50KSkNCg0KIyBDb252ZXJ0IExhbmd1YWdlcyB0byBmYWN0b3Igd2l0aCBkZXNpcmVkIG9yZGVyDQpsYW5ndWFnZV9jb3VudCRMYW5ndWFnZXMgPC0gZmFjdG9yKGxhbmd1YWdlX2NvdW50JExhbmd1YWdlcywgbGV2ZWxzID0gbGFuZ3VhZ2VfY291bnQkTGFuZ3VhZ2VzW29yZGVyKC1sYW5ndWFnZV9jb3VudCRjb3VudCldKQ0KDQojIFBsb3R0aW5nIHRoZSB0b3AgNSBsYW5ndWFnZXMNCmdncGxvdChsYW5ndWFnZV9jb3VudFsxOjUsIF0sIGFlcyh4ID0gTGFuZ3VhZ2VzLCB5ID0gY291bnQpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImJsYWNrIikgKw0KICBsYWJzKHRpdGxlID0gIlRvcCA1IE1vc3QgUG9wdWxhciBMYW5ndWFnZXMgb24gTmV0ZmxpeCIsDQogICAgICAgeCA9ICJMYW5ndWFnZXMiKQ0KDQpgYGANCg0KSW4gdGhlIGZpcnN0IHBhcnQgb2YgdGhlIGNvZGUsIGZ1bmN0aW9uIHNlcGFyYXRlcyBtdWx0aXBsZSBsYW5ndWFnZXMgbGlzdGVkIGluIGEgc2luZ2xlIHJvdyBpbnRvIG11bHRpcGxlIHJvd3MgdG8gZmFjaWxpdGF0ZSBlYXNpZXIgY291bnRpbmcuIEZvciBpbnN0YW5jZSwgaWYgYSBtb3ZpZSBpcyBsaXN0ZWQgYXMgIkVuZ2xpc2gsIFNwYW5pc2giLCBpdCB3aWxsIGNyZWF0ZSB0d28gc2VwYXJhdGUgZW50cmllcyBhbmQgZ3JvdXBzIHRoZSBkYXRhc2V0IGJ5IGluZGl2aWR1YWwgbGFuZ3VhZ2VzIGFuZCBjYWxjdWxhdGVzIHRoZSBjb3VudCBvZiBlYWNoIGxhbmd1YWdlJ3Mgb2NjdXJyZW5jZSBhY3Jvc3MgdGhlIGRhdGFzZXQuIEFsc28sIHNvcnRzIHRoZSBsYW5ndWFnZXMgaW4gZGVzY2VuZGluZyBvcmRlciBiYXNlZCBvbiB0aGVpciBmcmVxdWVuY3ksIGhlbHBpbmcgaWRlbnRpZnkgdGhlIG1vc3QgcG9wdWxhciBsYW5ndWFnZXMuDQoNCkluIHRoZSBzZWNvbmQgcGFydCwgY29udmVydHMgdGhlIExhbmd1YWdlcyBjb2x1bW4gaW50byBhIGZhY3RvciB3aXRoIGxldmVscyBvcmRlcmVkIGJ5IGZyZXF1ZW5jeS4gVGhpcyBlbnN1cmVzIHRoYXQgdGhlIHBsb3QgZGlzcGxheXMgdGhlIGxhbmd1YWdlcyBpbiB0aGUgb3JkZXIgb2YgdGhlaXIgZnJlcXVlbmN5Lg0KDQpJbiB0aGUgbGFzdCBwYXJ0LCBjcmVhdGUgYSBiYXIgZ3JhcGggdmlzdWFsaXphdGlvbiB0aGF0IGRpc3BsYXlzIHRoZSB0b3AgZml2ZSBtb3N0IHBvcHVsYXIgbGFuZ3VhZ2VzIG9uIE5ldGZsaXggYmFzZWQgb24gdGhlaXIgZnJlcXVlbmN5IG9mIGFwcGVhcmFuY2UgaW4gdGhlIGRhdGFzZXQuIEl0IGZvY3VzIG9uIHRoZSB0b3AgZml2ZSBsYW5ndWFnZXMgb25seSwgZW5zdXJpbmcgdGhlIHBsb3QgaXMgY29uY2lzZSBhbmQgZm9jdXNlZCwgIGRlZmluZXMgdGhlIGFlc3RoZXRpYyBtYXBwaW5ncyBmb3IgdGhlIHBsb3QgYW5kIGFkZHMgaW1wb3J0YW50IGxhYmVsaW5nIGluZm9ybWF0aW9uIHRvIHRoZSBncmFwaC4NCg0KVGhlIGNvbWJpbmVkIGVmZmVjdCBvZiB0aGVzZSBjb21wb25lbnRzIGlzIHRvIHByb2R1Y2UgYSBjbGVhciBhbmQgaW5mb3JtYXRpdmUgdmlzdWFsaXphdGlvbiB0aGF0IGhpZ2hsaWdodHMgdGhlIGxhbmd1YWdlcyBtb3N0IGZyZXF1ZW50bHkgdXNlZCBpbiBOZXRmbGl4IGNvbnRlbnQgYXZhaWxhYmxlIGluIHRoZSBkYXRhc2V0LiANCg0KRm9yIGV4dHJhIGNyZWRpdHM6DQoNCiIqRXh0cmEgY2hhbGxlbmdlIDEuKjogQ3JlYXRlIGEgY2hhcnQgc2hvd2luZyBhY3RvcnMgc3RhcnJpbmcgaW4gdGhlIG1vc3QgcG9wdWxhciBwcm9kdWN0aW9ucy4iDQoNClRoaXMgUiBjb2RlIHNuaXBwZXQgcHJvY2Vzc2VzIGEgZGF0YXNldCB0byBpZGVudGlmeSB0aGUgdG9wIHRlbiBtb3ZpZXMgd2l0aCB0aGUgaGlnaGVzdCBib3ggb2ZmaWNlIGVhcm5pbmdzIGFuZCB0aGVuIHByZXNlbnRzIHRoZSByZXN1bHRzIGluIGJvdGggYSBkZXRhaWxlZCB2aWV3IGFuZCBhIHN0eWxpemVkIEhUTUwgdGFibGUgZm9jdXNpbmcgb24gbW92aWUgdGl0bGVzIGFuZCBhY3RvcnMuDQoNCmBgYHtyIGNoYWxsZW5nZTEsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojSSB0aG91Z2h0IHRoZSBmaXJzdCBzdGVwIGZvciBnZXR0aW5nIHRoZSBtb3N0IHBvcHVsYXIgbW92aWVzIHdvdWxkIGJlIHRoZSBoaWdoZXN0IGdhaW4gaW4gdGhlIGJveCBvZmZpY2Ugc28gaSBnb3QgaGlnaGVzdCBib3hvZmZpY2UgdmFsdWVzDQoNCm15ZGF0YSRCb3hvZmZpY2UgPC0gZ3N1YigiLCIsICIiLCBteWRhdGEkQm94b2ZmaWNlKQ0KcG9wdWxhcl9tb3ZpZXMgPC0gbXlkYXRhICU+JQ0KICBhcnJhbmdlKGRlc2MoQm94b2ZmaWNlKSkNCnBvcHVsYXJfbW92aWVzIDwtIGhlYWQocG9wdWxhcl9tb3ZpZXMsIDEwKQ0KI3RoZW4gcHV0IHRoZW0gaW4gYSBrYWJsZSBjYWhydCB3aGVyZSBvbmx5IHRpdGxlIGFuZCBhY3RvcnMgYXJlIHNob3duDQpwb3B1bGFyX21vdmllX2FjdG9yczwtIHBvcHVsYXJfbW92aWVzWywgYygiVGl0bGUiLCAiQWN0b3JzIildDQoNCnBvcHVsYXJfbW92aWVfYWN0b3JzJT4lDQogIGthYmxlKCJodG1sIikgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpDQoNCnByaW50KHBvcHVsYXJfbW92aWVzKQ0KcHJpbnQocG9wdWxhcl9tb3ZpZV9hY3RvcnMpDQpgYGANCg0KSW4gdGhlIGZpcnN0IHBhcnQgb2YgY29kZSwgd2l0aCB0aGUgYGdzdWIoKWAgZnVuY3Rpb24sIEJveG9mZmljZSByZW1vdmVzIHRoZSBjb21tYXMgaW4gdGhlIGNvbHVtbiwgbWFraW5nIHRoZSBkYXRhIHN1aXRhYmxlIGZvciBudW1lcmljYWwgb3BlcmF0aW9ucy4gVGhlbiwgdGhlc2UgbnVtZXJpY2FsIGRhdGEgYXJlIGFycmFuZ2VkIGluIGRlc2NlbmRpbmcgb3JkZXIgd2l0aCB0aGUgYGFycmFuZ2UoKWAgZnVuY3Rpb24gb2YgdGhlIGRwbHlyIHBhY2thZ2UsIGFuZCB0aGUgdG9wIHRlbiBtb3ZpZXMgd2l0aCB0aGUgaGlnaGVzdCBncm9zc2luZyBhcmUgc2VsZWN0ZWQgd2l0aCB0aGUgYGhlYWQoKWAgZnVuY3Rpb24uIFRoZXNlIHRyYW5zYWN0aW9ucywNCkl0IGlzIGRvbmUgdG8gcmFuayB0aGUgbW92aWVzIGFjY29yZGluZyB0byB0aGVpciByZXZlbnVlcyBhbmQgdG8gaWRlbnRpZnkgdGhlIG1vc3QgcG9wdWxhciBvbmVzLCBzbyB0aGF0IHRoZSBpbmZvcm1hdGlvbiBvYnRhaW5lZCBmcm9tIHRoZSBkYXRhIHNldCBiZWNvbWVzIG1vcmUgbWVhbmluZ2Z1bCBhbmQgdXNlZnVsLg0KDQpJbiB0aGUgc2Vjb25kIHBhcnQsIHN1YnNldHMgdGhlIHBvcHVsYXJfbW92aWVzIGRhdGFmcmFtZSB0byBpbmNsdWRlIG9ubHkgdGhlIGNvbHVtbnMgVGl0bGUgYW5kIEFjdG9ycywgZm9jdXNpbmcgb24gdGhlIG1vc3QgcmVsZXZhbnQgaW5mb3JtYXRpb24gZm9yIGEgc3VtbWFyeSBwcmVzZW50YXRpb24uDQoNCmFuZCwgdGhlIGxhc3QgcGFydCwgbWFrZSBkYXRhIG1vcmUgYWNjZXNzaWJsZSBhbmQgdmlzdWFsbHkgYXBwZWFsaW5nLiBXZSBpbXBsZW1lbnRlZCBCb290c3RyYXAgc3R5bGVzIHN1Y2ggYXMgInN0cmlwZWQiIGFuZCAiaG92ZXIiIHRvIGltcHJvdmUgcmVhZGFiaWxpdHkgYW5kIGludGVyYWN0aXZpdHkuDQoNClRoZSBjb2RlIGlzIHN0cnVjdHVyZWQgdG8gYWNoaWV2ZSB0aGUgdGFzayBvZiBpZGVudGlmeWluZyBhbmQgcHJlc2VudGluZyB0aGUgdG9wIHRlbiBoaWdoZXN0LWdyb3NzaW5nIG1vdmllcyBlZmZlY3RpdmVseS4gQnkgZm9jdXNpbmcgb24gdGhlIEJveG9mZmljZSBmaWd1cmVzLCB0aGUgYXBwcm9hY2ggZGlyZWN0bHkgdGFyZ2V0cyBhIG1lYXN1cmUgb2YgbW92aWUgcG9wdWxhcml0eS4gVGhlIGZpbmFsIHByZXNlbnRhdGlvbiwgd2hpY2ggc2hvd2Nhc2VzIG9ubHkgdGhlIG1vdmllIHRpdGxlcyBhbmQgYWN0b3JzIGluIGEgc3R5bGVkIEhUTUwgdGFibGUsIGdpdmVzIGEgc3VtbWFyeSBzdWl0YWJsZSBmb3Igb3VyIHJlcG9ydHMuDQoNCiIqRXh0cmEgY2hhbGxlbmdlIDIuKjogRm9yIG1vdmllcyBhbmQgc2VyaWVzLCBjcmVhdGUgcmF0aW5nIGNoYXJ0cyBmcm9tIHRoZSB2YXJpb3VzIHBvcnRhbHMgKEhpZGRlbiBHZW0sIElNRGIsIFJvdHRlbiBUb21hdG9lcywgTWV0YWNyaXRpYykuIEhpbnQ6IGl0J3MgYSBnb29kIGlkZWEgdG8gcmVzaGFwZSB0aGUgZGF0YSB0byAqbG9uZyogZm9ybWF0LiINCg0KVGhpcyBSIGNvZGUgZG93bmxvYWRzIHRoZSBOZXRmbGl4IGRhdGFzZXQgZnJvbSBhbiBvbmxpbmUgc291cmNlLCByZWFkcyBpdCwgYW5kIHRoZW4gdXNlcyBpdCB0byB2aXN1YWxpemUgdGhlIHJhdGluZyBkaXN0cmlidXRpb25zIG9mIG1vdmllIGFuZCBUViBnZW5yZXMgYWNyb3NzIGRpZmZlcmVudCByYXRpbmcgc291cmNlcy4NCg0KYGBge3IgY2hhbGxlbmdlMiwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGdncGxvdDIpDQoNCg0KZG93bmxvYWQuZmlsZSgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2tmbGlzaWtvd3NraS9kcy9tYXN0ZXIvbmV0ZmxpeC1kYXRhc2V0LmNzdj9yYXc9dHJ1ZSIsIGRlc3RmaWxlID0iZGFuZS5jc3YiLG1vZGU9IndiIikNCm15ZGF0YSA8LSByZWFkLmNzdihmaWxlPSJkYW5lLmNzdiIsIGVuY29kaW5nID0iVVRGLTgiLCBoZWFkZXI9VFJVRSwgc2VwID0gIiwiKQ0KDQoNCmdncGxvdChteWRhdGEgJT4lIHBpdm90X2xvbmdlcihjb2xzID0gYyhgSGlkZGVuLkdlbS5TY29yZWAsIGBJTURiLlNjb3JlYCwgYFJvdHRlbi5Ub21hdG9lcy5TY29yZWAsIGBNZXRhY3JpdGljLlNjb3JlYCksIG5hbWVzX3RvID0gIlJhdGluZ19Tb3VyY2UiLCB2YWx1ZXNfdG8gPSAiUmF0aW5nIikgJT4lIGRyb3BfbmEoKSwgYWVzKHggPSBgU2VyaWVzLm9yLk1vdmllYCwgeSA9IFJhdGluZywgZmlsbCA9IFJhdGluZ19Tb3VyY2UpKSArIGdlb21fYm94cGxvdCgpICsgbGFicyh0aXRsZSA9ICJSYXRpbmcgRGlzdHJpYnV0aW9uIG9uIERpZmZlcmVudCBQbGF0Zm9ybXMgYnkgR2VucmUiLCB4ID0gIkdlbnJlIChNb3ZpZSBvciBTZXJpZXMpIiwgIHkgPSAiUmF0aW5nIikgKyB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArIGZhY2V0X3dyYXAoflJhdGluZ19Tb3VyY2UsIHNjYWxlcyA9ICJmcmVlX3kiLCBuY29sID0gMikNCmBgYA0KDQpVc2luZyB0aGUgdGlkeXIgYW5kIGdncGxvdDIgbGlicmFyaWVzLCB0aGUgY29kZSBjb2xsZWN0cyB0aGUgdmFyaW91cyByYXRpbmcgc2NvcmVzIChIaWRkZW4uR2VtLlNjb3JlLCBJTURiLlNjb3JlLCBSb3R0ZW4uVG9tYXRvZXMuU2NvcmUsIE1ldGFjcml0aWMuU2NvcmUpIGluIHRoZSBkYXRhc2V0IGludG8gYSBzaW5nbGUgY29sdW1uLCBkaXNjYXJkcyB0aGUgbWlzc2luZyBkYXRhLCBhbmQgcGxvdHMgdGhlIGRhdGEgaW4gYSBib3hwbG90LiBwcmVzZW50cyB3aXRoIGVhY2ggcmF0aW5nIHNvdXJjZS4gQm94cGxvdHMgYXJlIHZpc3VhbGl6ZWQgYXMgaGF2aW5nIGEgZGlmZmVyZW50IHktYXhpcyBhdCB0aGUgYm90dG9tLCBtYWtpbmcgaXQgZWFzaWVyIHRvIGNvbXBhcmUgc2NvcmUgcmFuZ2VzIGZyb20gZGlmZmVyZW50IHNvdXJjZXMuIEJveHBsb3RzIGFyZSBncm91cGVkIGJ5IGdlbnJlIG9mIG1vdmllcyBhbmQgVFYgc2VyaWVzLCBhbmQgYSBkaWZmZXJlbnQgY2hhcnQgaXMgY3JlYXRlZCBmb3IgZWFjaCByYXRpbmcgc291cmNlLiBUaGlzIHByb2Nlc3MgYWxsb3dzIHRvIHZpc3VhbGx5IGNvbXBhcmUgYW5kIGFuYWx5emUgcmF0aW5nIGRpZmZlcmVuY2VzIGJldHdlZW4gZ2VucmVzIG9uIGRpZmZlcmVudCBwbGF0Zm9ybXMNCg0KKkV4dHJhIGNoYWxsZW5nZSAzLio6IFdoaWNoIGZpbG0gc3R1ZGlvcyBwcm9kdWNlIHRoZSBtb3N0IGFuZCBob3cgaGFzIHRoaXMgY2hhbmdlZCBvdmVyIHRoZSB5ZWFycz8NCg0KYGBge3IgY2hhbGxlbmdlM30NCg0KYGBgDQoNCg0K