1 Introduction

The data properties are typically numerical or categorical values, while the visual properties include the x and y positions of points, colors of lines, heights of bars, and so on. The process of creating a data visualization is to map the data properties to visual properties.

In R’s base graphics functions, each mapping of data properties to visual properties is its special case. Changing the mappings in the base R graphics may require restructuring the data utilizing completely different plotting commands, or both.

On the other hand, ggplot2 is a system for declaratively creating graphics, based on The Grammar of Graphics. We provide the data, and tell ggplot2 how to map variables to aesthetics and what graphical primitives to use, ggplot() takes care of the details.

The graphic functions in base R are powerful, but in general, it is believed that ggplot() is better.

For those who program in Python, It is good to know that plotnine is an implementation of a grammar of graphics in Python, it is based on ggplot2().

For those who program in SAS, the SAS ODS graphics are roughly analogous to R’s ggplot() although it is not a direct implementation of The Grammar of Graphics.

2 Basics of ggplot()

Plotting with ggplot2 is based on “adding” plot layers and design elements on top of one another, with each command added to the previous ones with a plus symbol (+). The result is a multi-layer plot object that can be saved, modified, printed, exported, etc.

ggplot() objects can be highly complex, but the basic order of layers will usually look like this:

  1. Begin with the baseline ggplot() command - this “opens” the ggplot and allows subsequent functions to be added with +. Typically the data set is also specified in this command

  2. Add “geom” layers - these functions visualize the data as geometries (shapes), e.g. as a bar graph, line plot, scatter plot, histogram (or a combination!). These functions all start with geom_ as a prefix.

  3. Add design elements to the plot such as axis labels, titles, fonts, sizes, color schemes, legends, or axes rotation

We can check the tidyverse reference site for more details at https://ggplot2.tidyverse.org/reference/index.htm

A simple example of skeleton code is as follows. We will explain each component in the code below.

# Plot data from my data columns as red points
ggplot(data = my_data)  +               # use the dataset "my_data"
  geom_point(                           # add a layer of points (dots)
    mapping = aes(x = col1, y = col2),  # "map" data column to axes
    color = "red")  +                   # Other specifications for the geom
  labs()  +                             # here you add titles, axes labels, etc.
  theme()                               # here you adjust color, font, size etc                                         
                                        # of non-data plot elements (axes, 
                                        # title, etc.) 

In the following sections, we will detail each of the components in the above code.

3 Structure of ggplot()

The opening command of any ggplot2 plot is ggplot(). This command simply creates a blank canvas upon which to add layers. It “opens” the way for further layers to be added with a + symbol.

Typically, the command ggplot() includes the data = argument for the plot. This sets the default data set to be used for subsequent layers of the plot.

This command will end with a + after its closing parentheses. This leaves the command “open”. The ggplot will only execute/appear when the full command includes a final layer without a + at the end.

# This will create a plot that is a blank canvas
ggplot(data = linelist)

3.1 Geoms

The above code creates a blank canvas. We need to create geometries (shapes) from our data (e.g. bar plots, histograms, scatter plots, box plots).

This is done by adding layers of “geoms” to the initial ggplot() command. Many ggplot2 functions create “geoms”. Each of these functions begins with “geom_”, so we will refer to them generically as geom_XXXX().

There are many geoms in ggplot2 and many others created by fans. View them at the ggplot2 gallery. Some common geoms are listed below:

  • Histograms - geom_histogram()
  • Bar charts - geom_bar() or geom_col()
  • Box plots - geom_boxplot()
  • Points (e.g. scatter plots) - geom_point()
  • Line graphs - geom_line() or geom_path()
  • Trend lines - geom_smooth()

We can display one or multiple geoms in one plot. Each is added to previous ggplot2 commands with a +, and they are plotted sequentially such that later geoms are plotted on top of previous ones.

For the complete list of currently available geoms, using the following command in the R Console:

ls(pattern = '^geom_', env = as.environment('package:ggplot2'))

3.2 AI Generated Desciption of Commonly Used geoms

The generative AI (large language model) can help summarize most commonly use geoms in ggplot. The following is a list geoms with more details based on the phrase list of geoms in ggplot through the Microsoft Copilot via Mircosoft Edge:

Geoms are the geometric objects that define the type and appearance of the plots created by the ggplot2 package in R. Geoms can be combined and layered to create complex and customized visualizations of data. Geoms are specified by using geom_ functions, such as geom_point, geom_bar, geom_line, etc.

There are many geoms available in ggplot2, each with its own aesthetics and parameters. Some of the most common and useful geoms are:

  • geom_point: This geom draws points on a plot, and can be used to create scatterplots, dotplots, bubble charts, etc. It requires x and y aesthetics, and can also take size, shape, colour, fill and alpha aesthetics to control the appearance of the points.

  • geom_bar and geom_col: These geoms draw bars on a plot, and can be used to create bar charts, histograms, lollipop charts, etc. They require x and y aesthetics, where y represents the height of the bars. The difference between geom_bar and geom_col is that geom_bar uses stat_count by default, which counts the number of observations in each x group, while geom_col uses stat_identity by default, which takes the y values as given. These geoms can also take size, linetype, colour, fill and alpha aesthetics to control the appearance of the bars.

  • geom_line and geom_path: These geoms draw lines on a plot, and can be used to create line charts, time series plots, spaghetti plots, etc. They require x and y aesthetics, where x represents the order of the points along the line. The difference between geom_line and geom_path is that geom_line sorts the data by x before plotting, while geom_path plots the data in the order they appear in the data frame. These geoms can also take size, linetype, colour and alpha aesthetics to control the appearance of the lines.

  • geom_boxplot: This geom draws boxplots on a plot, and can be used to display the distribution of a numeric variable across different groups. It requires x and y aesthetics, where x represents the grouping variable and y represents the numeric variable. It also uses stat_boxplot by default, which computes the five-number summary (minimum, lower quartile, median, upper quartile and maximum) of each group. This geom can also take size, linetype, colour, fill and alpha aesthetics to control the appearance of the boxplots.

  • geom_histogram: This geom draws histograms on a plot, and can be used to display the distribution of a single numeric variable. It requires x aesthetic, which represents the numeric variable to be binned. It also uses stat_bin by default, which counts the number of observations in each bin. This geom can also take size, linetype, colour, fill and alpha aesthetics to control the appearance of the histogram.

  • geom_density and geom_density_2d: These geoms draw density plots on a plot, and can be used to display the distribution of one or two numeric variables using smooth curves. They require x aesthetic for one-dimensional density plots or x and y aesthetics for two-dimensional density plots. They also use stat_density or stat_density_2d by default, which estimate the probability density function of the variables using kernel smoothing. These geoms can also take size, linetype, colour, fill and alpha aesthetics to control the appearance of the density plots.

  • geom_tile and geom_bin2d: These geoms draw tiles on a plot, and can be used to create heatmaps or bivariate histograms of two numeric variables. They require x and y aesthetics, which represent the numeric variables to be binned. They also use stat_identity or stat_bin2d by default, which take or compute the values for each tile. These geoms can also take size, linetype, colour, fill and alpha aesthetics to control the appearance of the tiles.

  • geom_text and geom_label: These geoms draw text or labels on a plot, and can be used to add annotations or data labels to a plot. They require x and y aesthetics for positioning and label aesthetic for text content. They can also take size, colour, fill, alpha, hjust, vjust and parse aesthetics to control the appearance of the text or labels.

3.3 Mapping Data to Plot

geom functions require mapping (assigning) columns in the data to components of the plot like the axes, shape colors, shape sizes, etc. The mappings must be wrapped in the aes() function, so we would write something like mapping = aes(x = col1, y = col2).

For example, in the following example using iris data, Sepal Length is mapped to the x-axis, and Sepal Width is mapped to the y-axis. After a +, the plotting commands continue. A shape is created with the “geom” function geom_point().

ggplot(data = iris, mapping = aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point()

When creating a histogram, only one variable is used. See the following example.

ggplot(data = iris, mapping = aes(x = Petal.Width)) +
  geom_histogram(binwidth = 0.2)

3.4 Arranging Multiple Grobs on the Same Page

In the above subsection, we create two graphs on two different pages. Sometimes, we want to place two more graphs on the same page for comparison purposes. In base R, we have graphic functions such as par() and layout() to set up a layout for the graphic page.

In this note, we introduce the library cowplot to arrange multiple graphical objects (a.k.a grobs) on a page.

## Name the two plots first and then call the two grobs in the layout function
## scatter plot
scatter = ggplot(data = iris, mapping = aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point()
## histogram
hist = ggplot(data = iris, mapping = aes(x = Petal.Width)) +
  geom_histogram(binwidth = 0.2)
## use plot_grid() in {cowplot} to layout the two plots
plot_grid(scatter, hist, labels=c("A", "B"), ncol = 2, nrow = 1)

3.5 Plot Aesthetics

In ggplot terminology a plot “aesthetic” has a specific meaning. It refers to colors, sizes, transparencies, placement, etc. of the plotted data. Not all geoms will have the same aesthetic options, but many can be used by most geoms.

Here are some examples:

  • shape = Display a point with geom_point() as a dot, star, triangle, or square, etc.
  • fill = The interior color (e.g. of a bar or boxplot)
  • color = The exterior line of a bar, boxplot, etc., or the point color if using geom_point()
  • size = Size (e.g. line thickness, point size)
  • alpha = Transparency (1 = opaque, 0 = invisible)
  • binwidth = Width of histogram bins
  • width = Width of “bar plot” columns
  • linetype = Line type (e.g. solid, dashed, dotted)

The aesthetics of plot objects can be assigned values in two ways:

  1. Assigned a static value (e.g. color = “blue”) to apply across all plotted observations

  2. Assigned to a column of the data (e.g. color = hospital) such that the display of each observation depends on its value in that column

We have already added binwidth to the above histogram. Next, we add color to the histogram

# Change histogram plot line colors by groups
scatter01 <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, 
                              color = Species, 
                              size = Petal.Width)) +
  geom_point(alpha = 0.5)
# Overlaid histograms
hist01 <- ggplot(iris, aes(x = Petal.Width, color=Species)) +
          geom_histogram(fill="navy", 
                         alpha = 0.7, 
                         position = "identity", 
                         binwidth = 0.2)
## use plot_grid() in {cowplot} to lay out the two plots
plot_grid(scatter01, hist01, labels=c("A", "B"), ncol = 2, nrow = 1)

3.6 Labels in ggplot()

Surely you will want to add or adjust the plot’s labels. These are most easily done within the labs() function which is added to the plot with + just as the geoms were.

Within labs() you can provide character strings to these arguments:

  • x = and y =: The x-axis and y-axis title (labels)

  • title =: The main plot title

  • subtitle =: The subtitle of the plot, in smaller text below the title

  • caption =: The caption of the plot, in bottom-right by default

Here is a plot we made earlier, but with nicer labels:

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, 
                              color = Species, 
                              size = Petal.Width)) +
             geom_point(alpha = 0.5) +
             labs(
                 x = "Sepal Length",
                 y = "Sepal Width",
                 # label for legends
                 size = "Sepal Length:",
                 color = "Species:",
                 title = "Association between Sepal Length and Width",
                 subtitle = "This is a partial scatter plot",
                 caption = paste("Created on", Sys.Date())) +
             theme_minimal()   # minimal theme

4 Themes in ggplot()

The theme system in ggplot() does not affect how the data is rendered by geoms, or how it is transformed by scales. Themes don’t change the perceptual properties of the plot, but they do help you make the plot aesthetically pleasing or match an existing style guide. Themes give us control over things like fonts, ticks, panel stripes, and backgrounds.

In other words, when creating the plot we determine how the data is displayed, and then after it has been created we can edit every detail of the rendering, using the theming system.

4.1 Theming System Structure

The theming system is composed of four main components:

  • Theme elements specify the non-data elements that we can control. For example,

    • plot.title controls the appearance of the plot title;
    • axis.ticks.x controls the ticks on the x-axis;
    • legend.key.height controls the height of the keys in the legend.
  • Each element is associated with an element function, which describes the visual properties of the element. For example, element_text() sets the font size, color, and face of text elements like plot.title.

  • The theme() function which allows you to override the default theme elements by calling element functions, like theme(plot.title = element_text(colour = "red")).

  • Complete themes, like theme_grey() set all of the theme elements to values designed to work together harmoniously.

Here are some especially common theme() arguments. You will recognize some patterns, such as appending .x or .y to apply the change only to one axis.


sample ggplot theme


To get the complete list of themes, run the following code

#theme_get()

To make sure the plot can stand alone, we need to provide the plot with axes, legend labels, and title, and tweak the color scale for appropriate colors.

# adding themes
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, 
                              color = Species, 
                              size = Petal.Width)) +
             geom_point(alpha = 0.5) +
             labs(
                 x = "Sepal Length",
                 y = "Sepal Width",
                 # label for legends
                 size = "Sepal Length:",
                 color = "Species:",
                 title = "Association between Sepal Length and Width" ) +
             theme_minimal()  +   # minimal theme
             theme( # list of themes applied to the plot
                   # plot title features
                   # font family: c("sans", "serif", "mono")  
                   # font face: c("plain", "bold", "italic", "bold.italic")
                   plot.title = element_text(face = "bold", 
                                             size = 12,
                                             family = "sans", 
                                             color = "darkred",
                                             hjust = 0.5), # left(0),right(1)
                   # Labels of axes 
                   axis.title.x = element_text(color = "red",
                                               face = "italic",
                                               family = "serif",
                                               hjust = 0.5),
                   axis.title.y = element_text(color = "blue",
                                               face = "bold",
                                               vjust = 0.5),
                   axis.ticks = element_line(color = "red", 
                                             size = 0.5),
                   axis.line = element_line(color = "darkblue", 
                                            size = 1, 
                                            linetype = "solid"),
                   # Axis tick marks
                   axis.text.x = element_text(face="plain", 
                                              color="purple", 
                                              size=11, 
                                              angle=45),
                   axis.text.y = element_text(face="plain", 
                                              color="orange", 
                                              size=11, 
                                              angle=90),
                   # Features of legend
                   legend.background = element_rect(fill = "white", 
                                                    size = 0.1, 
                                                    color = "darkgreen"),
                   legend.justification = c(0.9, 0.8),
                   legend.position = "bottom",
                   ## Panel grid
                   panel.grid.major = element_line(color = "lightblue", 
                                                   size = 0.1),
                   panel.grid.minor = element_blank()
  )

4.2 Complete Components of Theme

Themes are a powerful way to customize the non-data components of the plots: i.e. titles, labels, fonts, background, gridlines, and legends. To give our plots a consistent customized look, we can define a theme function and call the theme function in any ggplots.

The tidyverse official website provides a comprehensive document on theme components in `ggplot``. https://ggplot2.tidyverse.org/reference/theme.html. Numerous examples have illustrated how to use various theme components.

We can define a theme function that can be reused to customize the plots. For example, we define the following theme and use it in different plots.

myplot.theme <- function() {
  theme(
    plot.title = element_text(face = "bold", 
                              size = 12,
                              family = "sans", 
                              color = "darkred",
                              hjust = 0.5), # left(0),right(1)
    # add border 1)
    panel.border = element_rect(colour = "blue", 
                                fill = NA, 
                                linetype = 2),
    # color background 2)
    panel.background = element_rect(fill = "aliceblue"),
    # modify grid 3)
    panel.grid.major.x = element_line(colour = "steelblue", 
                                      linetype = 3, 
                                      size = 0.5),
    panel.grid.minor.x = element_blank(),
    panel.grid.major.y =  element_line(colour = "steelblue", 
                                       linetype = 3, 
                                       size = 0.5),
    panel.grid.minor.y = element_blank(),
    # modify text, axis and colour 4) and 5)
    axis.text = element_text(colour = "steelblue", 
                             face = "italic", 
                             family = "Times New Roman"),
    axis.title = element_text(colour = "steelblue", 
                              family = "Times New Roman"),
    axis.ticks = element_line(colour = "steelblue"),
    # legend at the bottom 6)
    legend.position = "bottom",
    legend.key.size = unit(0.6, 'cm'), #change legend key size
    legend.key.height = unit(0.6, 'cm'), #change legend key height
    legend.key.width = unit(0.6, 'cm'), #change legend key width
    legend.title = element_text(size=8), #change legend title font size
    legend.text = element_text(size=8)) #change legend text font size
}

Now we use the above theme in the following scatter plots. Instead of using the colors based on the value of species, we manually select colors to encode the values of species. The following URL links to a PDF document with colors in R. http://www.stat.columbia.edu/~tzheng/files/Rcolor.pdf

# Change histogram plot line colors by groups
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, 
                              color = factor(Species), 
                              size = Petal.Width)) +
             geom_point(alpha = 0.5) +
             scale_color_manual(values=c("dodgerblue4", "darkolivegreen4","darkorchid3")) +
             labs(
                 x = "Sepal Length",
                 y = "Sepal Width",
                 ## Color and size of labels
                 size = "Sepal Length:",
                 color = "Species:",
                 title = "Association between Sepal Length and Width") +
             myplot.theme()

Next, we plot a histogram using the same theme.

ggplot(iris, aes(x = Petal.Width, color=Species)) +
          geom_histogram(fill="navy", 
                         alpha = 0.3, 
                         position = "identity", 
                         binwidth = 0.2) +
  scale_color_manual(values=c("dodgerblue4", "darkolivegreen4",
                              "darkorchid3")) +
               labs(
                 x = "Petal Width",
                 color = "Species:",
                 title = "Distribution of Petal Width") +
           myplot.theme()

5 Adding Annotations to Graphics

To make the graphic more informative, sometimes we may want to add annotations to the graphic. If we create a statistical and probabilistic graphic, occasionally we need to add mathematical equations with Greek letters to the graphics.

5.1 Adding Plain Text to Graphics

To add plain text to graphics in ggplot, we use the function annotate() with given coordinates. For example, the scatter plot shows two separate groups.

# Change histogram plot line colors by groups
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, 
                              color = factor(Species), 
                              size = Petal.Width)) +
             geom_point(alpha = 0.5) +
             scale_color_manual(values=c("dodgerblue4", "darkolivegreen4", "darkorchid3")) +
             labs(
                 x = "Sepal Length",
                 y = "Sepal Width",
                 ## color and size of labels
                 size = "Sepal Length:",
                 color = "Species:",
                 title = "Association between Sepal Length and Width") +
             myplot.theme() + 
             annotate(geom="text", 
                      x=7, 
                      y=4.1, 
                      label=paste("The distribution of Setosa is different", 
                      "from that of Versicolor and Viginica", sep = "\n"),
                      color="red",
                      hjust = 0.5)

Several other alternatives we can use to add text to graphics created using `ggplot``.

5.2 Passing Parameters in Annotation

# Change histogram plot line colors by groups
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, 
                              color = factor(Species), 
                              size = Petal.Width)) +
             geom_point(alpha = 0.5) +
             scale_color_manual(values=c("dodgerblue4", "darkolivegreen4", "darkorchid3")) +
             labs(
                 x = "Sepal Length",
                 y = "Sepal Width",
                 ## Color and size of labels
                 size = "Sepal Length:",
                 color = "Species:",
                 title = "Association between Sepal Length and Width") +
             myplot.theme() + 
              annotate(geom="text" , 
                       x=7, 
                       y=4.4,
                       label=paste("The Pearson correlation coefficient r = ",                          
                                   round(cor(iris$Sepal.Length, iris$Sepal.Width),3)), 
                          color = "blue")

The correlation coefficient between sepal width and sepal length is calculated directly from the data and passed to the annotation in the graphic. Note that we used a very handy and important graphic function paste() when adding the annotation.

5.3 Adding Mathematical Equations to Graphics

Mathematical expressions made with the text geoms using parse = TRUE in ggplot2 have a format similar to those made with plotmath() and expression() in base R, except that they are stored as strings, rather than as expression objects.

To mix regular text with expressions, use single quotes within double quotes (or vice versa) to mark the plain-text parts. Each block of text enclosed by the inner quotes is treated as a variable in a mathematical expression.

Bear in mind that, in R’s syntax for mathematical expressions, we can’t simply put a variable right next to another without something else in between. To display two variables next to each other, put a * operator between them. when * is displayed in a graphic, it is treated as an invisible multiplication sign (for a visible multiplication sign, use %*%):

x.axis <- seq(0, 20, length.out = 100)
y.axis <- (1/sqrt(2*pi)*3)*exp(-(x.axis-10)^2/(2*9))
normal.data = data.frame(x=x.axis , y=y.axis)
##
ggplot(normal.data, aes(x = x.axis, y = y.axis)) + 
     geom_line(color = "blue") +
     coord_cartesian(ylim = c(0, 1.25), xlim=c(0,20)) + 
     labs(
                 x = "Normal Score",
                 y = "Normal Density",
                 title = "Normal Density Curve") +
      annotate("text", x = 10, y = 0.2, 
               parse = TRUE, size = 4,
              label = "'Function:  ' * y==frac(1, sqrt(2*pi)* sigma) %*% e^{-(x- mu)^2/2}",
              color = "red")

5.4 Adding Images to Existing ggPlots

To embed a PNG image to an existing graph created by ggplot, we need to use readPNG() in library png to load the image to R and getURLcontent() in the RCurl to insert the image to the graph.

# caturl <- "https://stat553.s3.amazonaws.com/ggplot/cat.png"
caturl <- "https://raw.githubusercontent.com/pengdsci/sta553/main/ggplot/cat.png"
my_cat <-  readPNG(getURLContent(caturl))
raster.cat <- as.raster(my_cat) 
# Change histogram plot line colors by groups
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, 
                              color = factor(Species), 
                              size = Petal.Width)) +
             geom_point(alpha = 0.5) +
             labs(
                 x = "Sepal Length",
                 y = "Sepal Width",
                 ## Color and size of labels
                 size = "Sepal Length:",
                 color = "Species:",
                 title = "Association between Sepal Length and Width") +
             myplot.theme() + 
              annotation_raster(raster.cat, 4, 4.85, 3.65, 4.5)

6 Removing Chart Junks Via Themes

We remove some unnecessary marks and channels from the chart via theme: change the background color, grid, and plot title. Essentially, we use a them to lay out a ggplot style without any chart junk.

myplot.theme_new <- function() {
  theme(
    #ggplot margins
     plot.margin = margin(t = 50,  # Top margin
                          r = 30,  # Right margin
                          b = 30,  # Bottom margin
                          l = 30), # Left margin
    ## ggplot titles
    plot.title = element_text(face = "bold", 
                              size = 12,
                              family = "sans", 
                              color = "navy",
                              hjust = 0.5,
                              margin=margin(0,0,30,0)), # left(0),right(1)
    # add border 1)
    panel.border = element_rect(colour = NA, 
                                fill = NA, 
                                linetype = 2),
    # color background 2)
    panel.background = element_rect(fill = "#f6f6f6"),
    # modify grid 3)
    panel.grid.major.x = element_line(colour = 'white', 
                                      linetype = 3, 
                                      size = 0.5),
    panel.grid.minor.x = element_blank(),
    panel.grid.major.y =  element_line(colour = 'white', 
                                       linetype = 3, 
                                       size = 0.5),
    panel.grid.minor.y = element_blank(),
    # modify text, axis and colour 4) and 5)
    axis.text = element_text(colour = "navy", 
                             #face = "italic", 
                             size = 7,
                             #family = "Times New Roman"
                             ),
    axis.title = element_text(colour = "navy", 
                              size = 7,
                              #family = "Times New Roman"
                              ),
    axis.ticks = element_line(colour = "navy"),
    # legend at the bottom 6)
    legend.position = "bottom",
    legend.key.size = unit(0.6, 'cm'), #change legend key size
    legend.key.height = unit(0.6, 'cm'), #change legend key height
    legend.key.width = unit(0.6, 'cm'), #change legend key width
    #legend.title = element_text(size=8), #change legend title font size
    legend.title=element_blank(),  # remove all legend titles
    legend.key = element_rect(fill = "white"),
    #####
    legend.text = element_text(size=8)) #change legend text font size
}

Using the above theme, we re-plot the iris data with less irrelevant graphical elements.

# Change histogram plot line colors by groups
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width,
                              color = factor(Species)), linetype = Species) +
             geom_point(size = 2, alpha = 0.7) +
             stat_smooth(method = lm, se=FALSE, size = 0.3) +
             scale_color_manual(values=c("dodgerblue4", "darkolivegreen4", "darkorchid3")) +
             labs(
                 x = "Sepal Length",
                 y = "Sepal Width",
                 ## labels of color and size
                 #size = "Sepal Length",
                 #color = NA,
                 title = "Association between Sepal Length and Width") +
             myplot.theme_new() + 
              annotate(geom="text" , 
                       x=6.8, 
                       y=2,
                       label=paste("The Pearson correlation coefficient r = ",                          
                                   round(cor(iris$Sepal.Length, iris$Sepal.Width),3)), 
                          size = 2,
                          color = "navy") + 
               coord_fixed(1)    ## This changes the aspect ratio of the graph

7 Common Extensions to ggplot

There are different extensions of ggplot. This section introduces two commonly used extensions. We will also briefly outline a few other extensions of ggplot.

7.1 Aminated Graph with gganimate()

gganimate() extends the grammar of graphics as implemented by ggplot2 to include the description of animation. It does this by providing a range of new grammar classes that can be added to the plot object in order to customize how it should change with time.

  • transition_*() defines how the data should be spread out and how it relates to itself across time.

  • view_*() defines how the positional scales should change along with the animation.

  • shadow_*() defines how data from other points in time should be presented in the given point in time.

  • enter_*()/exit_*() defines how new data should appear and how old data should disappear during the course of the animation.

  • ease_aes() defines how different aesthetics should be eased during transitions.

The logic behind the gganimate is to create a sequence of images and then make a gif image. We need to write HTML to include this gif in the RMarkdown document.

library(gapminder)

p <- ggplot(gapminder, aes(x = gdpPercap, 
                           y=lifeExp, 
                           size = pop, 
                           colour = country)) +
        geom_point(aes(size = pop, ids = country ),
                   show.legend = FALSE, 
                   alpha = 0.7) +
        scale_color_viridis_d() +      # color pallets 
        scale_size(range = c(2, 12)) +
        scale_x_log10() +
        labs(x = "GDP per capita", 
             y = "Life expectancy") +
        ## gganimate command
       transition_time(year)
## 
anim_save("LifeExp.gif", p)
#  animate(p, renderer = gifski_renderer())  # This command will pop up a new graphic window showing the animation.


LifeExpectancy Animation


Since the gif image is made of individual static images, it is different from the interactive plot presented in the previous sections that have the capability of showing mode information of the data via hover message.

The next gif graph consists of 5 panels, each representing a continent. They are also fig images. Therefore, no hover message is available for these gif figures.

We use the {gifki} package to render the images in the form of gif and then include the gif image into the RMarkdown document directly.

w <- ggplot(gapminder, aes(gdpPercap, lifeExp, 
                  size = pop, colour = country)) +
           geom_point(alpha = 0.7, show.legend = FALSE) +
           scale_colour_manual(values = country_colors) +
           #scale_color_manual(values=c("dodgerblue4", "darkolivegreen4","darkorchid3")) +
           #scale_color_brewer(palette="Set1") +
           scale_size(range = c(2, 12)) +
           scale_x_log10() +
           # break down the previous single plot by continent 
           # facet_wrap(~continent) +     # create multiple panels according to the continents
           # Here comes the gganimate specific bits
           labs(title = 'Year: {frame_time}', 
                    x = 'GDP per capita',
                    y = 'life expectancy') +
           transition_time(year) +
           ease_aes('linear')
###
animate(w, renderer = gifski_renderer(),
          rewind = TRUE)

The above code does not save the generated gif image to the document folder (directory). If need to save it from the viewer window to the designated folder and then embed it to a web page created by tools other than the RMarkdown.

<br>
<center><img src="https://raw.githubusercontent.com/pengdsci/sta553/main/ggplot/LifeExpRewind.gif" alt="Life Expectancy Animation Rewind" height="500" width="400"></center>
<br>

Next, we create a group gif using facet_wrap() function. The code is the same as the above example except for one additional function call.

w <- ggplot(gapminder, aes(gdpPercap, lifeExp, 
                  size = pop, colour = country)) +
           geom_point(alpha = 0.7, show.legend = FALSE) +
           scale_colour_manual(values = country_colors) +
           #scale_color_manual(values=c("dodgerblue4", "darkolivegreen4","darkorchid3")) +
           #scale_color_brewer(palette="Set1") +
           scale_size(range = c(2, 12)) +
           scale_x_log10() +
           # break down the previous single plot by continent 
           facet_wrap(~continent) +     # create multiple panels according to the continents
           # Here comes the gganimate-specific bits
           labs(title = 'Year: {frame_time}', 
                    x = 'GDP per capita',
                    y = 'life expectancy') +
           transition_time(year) +
           ease_aes('linear')
###
animate(w, renderer = gifski_renderer(),
          rewind = TRUE)

7.2 Ridgetline Plot with ggridges Library

The ridgeline plot is a useful 3D to compare multiple densities. It creates a 3D impression and has gained increasing popularity. Here we use the California Housing Data that is available on the Project Data Set https://pengdsci.github.io/datasets/#cal-housing.

CalHousing = read.csv("https://raw.githubusercontent.com/pengdsci/sta553.html/main/data/ca-housing-price.csv")
ggplot(CalHousing, aes(x = median_house_value, y = ocean_proximity, fill = ocean_proximity)) +
  geom_density_ridges()

You can pass stat(x) or factor(stat(x)) to the fill argument of aes and use geom_density_ridges_gradient and a continuous fill color scale to fill each ridgeline with a gradient.

ggplot(CalHousing, aes(x = median_house_value, y = ocean_proximity, fill = stat(x))) +
      geom_density_ridges_gradient(jittered_points = TRUE,
                                          position = position_points_jitter(width = 0.05, height = 0),
    point_shape = '|', point_size = 1, point_alpha = 1, alpha = 0.3,) + 
  scale_fill_viridis_c(name = "median_house_value", option = "C") 

Next, we explore the distribution of continuous variables in the iris data set. As an example, we make the following ridgeline plot to see the distribution of sepal widths across the species.

ggplot(iris, aes(x = Sepal.Width, y = Species, fill = stat(x))) +
      geom_density_ridges_gradient(jittered_points = TRUE,
                                          position = position_points_jitter(width = 0.05, height = 0),
    point_shape = '|', point_size = 1, point_alpha = 1, alpha = 0.3,) + 
  scale_fill_viridis_c(name = "Sepal Width", option = "C") 

The above distributions have similar shapes (variations) but with different means. This also indicates the ANOVA model between sepal width and species is appropriate.

7.3 Other Extensions to ggplot

We have used ggplot extensions {gganimate} to create animated graphs and {ggridges} to create ridgeline graphs to compare multiple densities. There are several other important ggplot extensions that enhance the basic ggplots.

  • ggdendro - controls the appearance and display of your cluster analyses

  • ggthemes - contains themes and scales that enhance the standard ggplots.

  • ggpubr - makes it easy to produce publication-ready plots using ggplot.

  • Plotly - brings interactivity to ggplots. We will spend a week on plotly().

  • patchwork - arranges multiple R plots on the same graphics page

  • ggmap - is a powerful package for visualizing spatial data and models. It layers data on top of static maps from popular online sources. We will use these packages to make maps later.

  • ggrepel - gives ggplot2 users greater control over how text labels appear in their charts.

  • ggcorrplot - controls the appearance of the matrix, from altering the color, shape, or size of the boxes (as in the circle-matrix above), to adding coefficient labels, reordering the matrix according to hierarchical clustering, and so on.

  • GGally - brings together many useful additional visualization functionality, all in one package.

  • ggiraph -is an htmlwidget that can be extended to an existing ggplot2 such as bar chart, scatterplot, boxplot, map, etc., and does things like displaying a tooltip of your choice.

8 Save ggplot Images

A ggplot can be saved to different file formats, including PDF, SVG vector files, PNG, TIFF, JPEG, etc.

We can either print directly a ggplot into PNG/PDF files or use the convenient function ggsave() for saving a ggplot.

The default of ggsave() is to export the last plot that you displayed, using the size of the current graphics device. It also guesses the type of graphics device from the extension.

8.1 General Steps

The standard procedure to save any graphics from R is as follows:

  • Open a graphic device using one of the following functions:

    • pdf(“r-graphics.pdf”),
    • svg(“r-graphics.svg”),
    • png(“r-graphics.png”),
    • tiff(“r-graphics.tiff”),
    • jpeg(“r-graphics.jpg”), etc.
  • Additional arguments indicating the width and the height (in inches) of the graphics region can be also specified in the mentioned function.

  • Create and print a plot. Close the graphic device using the function dev.off().

8.2 Save ggplot into a PDF File

The following code illustrates how to save a ggplot in a folder in PDF format.

# scatter plots
iris.scatter <- ggplot(iris, aes(Sepal.Length, Sepal.Width)) + 
           geom_point()
## box-plot
iris.boxplot <- ggplot(iris, aes(Species, Sepal.Length)) + 
  geom_boxplot()
# Print plots to a PDF file: one page per PDF file
pdf("savePDFggplot.pdf")   # Save the PDF file in ggplot folder.
print(iris.scatter)     # Plot 1 --> in the first page of PDF
print(iris.boxplot)     # Plot 2 ---> in the second page of the PDF
dev.off() 
png 
  2 

8.3 Save ggplot with ggsave()

It’s also possible to make a ggplot and save it from the screen using the function ggsave().

# 1. Create a plot: displayed on the screen (by default)
ggplot(mtcars, aes(wt, mpg)) + geom_point()

# 2.1. Save the plot to a pdf
ggsave("mtcarmyplot.pdf")
# 2.2 OR save it to png file
ggsave("mtcarmyplot.png")

We can also save multiple plots in the sample format to a single file. We can use plot_grid() in {cowplot} to make two figures on the same graphic page and then use ggsave() to save it to a single file.

# 
p1 <- ggplot(mtcars, aes(wt, mpg)) + geom_point()
p2 <- ggplot(mtcars, aes(wt)) + geom_histogram()
combinedPlot <- plot_grid(p1, p2, labels=c("A", "B"), ncol = 2, nrow = 1)
##
ggsave("CombinedPlot.png", plot = combinedPlot)

9 The Role of Color in Effective Visualization

Substantial research shows that color plays a pivotal role in our visual experiences. In this class, we use colors for two main purposes: encoding and highlighting information.

We use different colors to denote different values of a variable. There are many continuous and discrete color palettes available in different R libraries that can be used on different occasions. However, we need to pay very special attention to the cases when colors are used for encoding because we see colors differently - people with vision deficiency are less sensitive to some of the colors. The next figure illustrates the major types of color blindness.

include_graphics("ColorBlindnessTypes.png")

What colors should you use? There are different color-picker tools available for us to choose colors while making sure that the color palette is accessible. Here is a set of 8 pairs of contrasting colors that maintain their contrast for people who are colorblind.

include_graphics("r-color-palettes.png")

For example, the following three sample palettes are colorblind-friendly.

  • IBM Design Library
par(mfrow=c(1,5), oma=c(0,0,0,0), mar = c(1,0.5,1,0.5))
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#648FFF")
text(0,0, "#648FFF")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#785EF0")
text(0,0, "#785EF0")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#DC267F")
text(0,0, "#DC267F")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#FE6100")
text(0,0, "#FE6100")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#FFB000")
text(0,0, "#FFB000")

  • Wong’s Palette
par(mfrow=c(1,8), oma=c(0,0,0,0), mar = c(1,0.5,1,0.5))
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#000000")
text(0,0, "#000000", col = "white")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#E69F00")
text(0,0, "#E69F00")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#56B4E9")
text(0,0, "#56B4E9")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#009E73")
text(0,0, "#009E73")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#F0E442")
text(0,0, "#F0E442")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#0072B2")
text(0,0, "#0072B2")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#D55E00")
text(0,0, "#D55E00")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#CC79A7")
text(0,0, "#CC79A7")

  • Pal’s Pallete
par(mfrow=c(1,8), oma=c(0,0,0,0), mar = c(1,0.5,1,0.5))

plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#332288")
text(0,0, "#332288", col = "white")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#117733")
text(0,0, "#117733")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#44AA99")
text(0,0, "#44AA99")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#88CCEE")
text(0,0, "#88CCEE")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#DDCC77")
text(0,0, "#DDCC77")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#CC6677")
text(0,0, "#CC6677")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#AA4499")
text(0,0, "#AA4499")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#882255")
text(0,0, "#882255", col = "white")

We can find more R color palettes from https://r-charts.com/color-palettes/.

As an example, we use the above color blind friendly color scheme and draw various density curves.

iris0 = iris
Type = c(paste(iris$Species,".Sepal.Length", sep = ""),paste(iris$Species,".Sepal.Width", sep = ""))
Measure = c(iris$Sepal.Length ,iris$Sepal.Width)
irisNew = data.frame(Type = Type, Measure = Measure)
cols1 = c("#332288","#117733","#44AA99","#88CCEE","#DDCC77","#CC6677")
cols3 = c("#AA4499","#882255")
p = ggplot() + 
      geom_density(data = irisNew, aes(x = Measure, color = Type), lwd = 1) + 
      scale_color_manual(values = cols1) +
      ggtitle("Multiple Density Curves") +
      theme(plot.title = element_text(hjust = 1,  
                                      face = "bold", 
                                      color = "navy"))
p

Some people like filled density curves. alpha is a function in the library of ggplot2.

p1 = ggplot(data = irisNew, aes(x = Measure, color = Type, fill = Type)) + 
      geom_density(alpha = 0.25, lwd = 1.5) +
      scale_fill_manual(values = cols1) + 
      ggtitle("Multiple Filled Density Curves") +
      theme(plot.title = element_text(hjust = 1,  
                                      face = "bold", 
                                      color = "purple"))

p1

LS0tDQp0aXRsZTogIkludHJvZHVjdGlvbiB0byBnZ3Bsb3QoKSINCmF1dGhvcjogIkNoZW5nIFBlbmciDQpkYXRlOiAiV2VzdCBDaGVzdGVyIFVuaXZlcnNpdHkgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIHRvY19mbG9hdDogeWVzDQogICAgZmlnX3dpZHRoOiA2DQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlDQogICAgdGhlbWU6IHJlYWRhYmxlDQogICAgZmlnX2hlaWdodDogNA0KLS0tDQoNCmBgYHs9aHRtbH0NCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoNCmRpdiNUT0MgbGkgew0KICAgIGxpc3Qtc3R5bGU6bm9uZTsNCiAgICBiYWNrZ3JvdW5kLWNvbG9yOmxpZ2h0Z3JheTsNCiAgICBiYWNrZ3JvdW5kLWltYWdlOm5vbmU7DQogICAgYmFja2dyb3VuZC1yZXBlYXQ6bm9uZTsNCiAgICBiYWNrZ3JvdW5kLXBvc2l0aW9uOjA7DQogICAgZm9udC1mYW1pbHk6IEFyaWFsLCBIZWx2ZXRpY2EsIHNhbnMtc2VyaWY7DQogICAgY29sb3I6ICM3ODBjMGM7DQp9DQoNCi8qIG1vdXNlIG92ZXIgbGluayAqLw0KZGl2I1RPQyBhOmhvdmVyIHsNCiAgY29sb3I6IHJlZDsNCn0NCg0KLyogdW52aXNpdGVkIGxpbmsgKi8NCmRpdiNUT0MgYTpsaW5rIHsNCiAgY29sb3I6IGJsdWU7DQp9DQoNCg0KDQpoMS50aXRsZSB7DQogIGZvbnQtc2l6ZTogMjRweDsNCiAgY29sb3I6IERhcmtibHVlOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtZmFtaWx5OiBBcmlhbCwgSGVsdmV0aWNhLCBzYW5zLXNlcmlmOw0KICBmb250LXZhcmlhbnQtY2Fwczogbm9ybWFsOw0KfQ0KaDQuYXV0aG9yIHsgDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgY29sb3I6IERhcmtSZWQ7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmg0LmRhdGUgeyANCiAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgY29sb3I6IERhcmtCbHVlOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoMSB7DQogICAgZm9udC1zaXplOiAyNHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBkYXJrcmVkOw0KICAgIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmgyIHsNCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDMgeyANCiAgICBmb250LXNpemU6IDE1cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDQgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQovKiB1bnZpc2l0ZWQgbGluayAqLw0KYTpsaW5rIHsNCiAgY29sb3I6IGdyZWVuOw0KfQ0KDQovKiB2aXNpdGVkIGxpbmsgKi8NCmE6dmlzaXRlZCB7DQogIGNvbG9yOiBncmVlbjsNCn0NCg0KLyogbW91c2Ugb3ZlciBsaW5rICovDQphOmhvdmVyIHsNCiAgY29sb3I6IHJlZDsNCn0NCg0KLyogc2VsZWN0ZWQgbGluayAqLw0KYTphY3RpdmUgew0KICBjb2xvcjogeWVsbG93Ow0KfQ0KDQo8L3N0eWxlPg0KYGBgDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCiMgY29kZSBjaHVuayBzcGVjaWZpZXMgd2hldGhlciB0aGUgUiBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCANCiMgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUgb3V0cHV0IGZpbGVzLg0Kb3B0aW9ucyhyZXBvcyA9IGxpc3QoQ1JBTj0iaHR0cDovL2NyYW4ucnN0dWRpby5jb20vIikpDQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KICAgbGlicmFyeSh0aWR5dmVyc2UpDQp9DQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikNCiAgIGxpYnJhcnkoa25pdHIpDQp9DQppZiAoIXJlcXVpcmUoImNvd3Bsb3QiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiY293cGxvdCIpDQogICBsaWJyYXJ5KGNvd3Bsb3QpDQp9DQppZiAoIXJlcXVpcmUoImxhdGV4MmV4cCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJsYXRleDJleHAiKQ0KICAgbGlicmFyeShsYXRleDJleHApDQp9DQppZiAoIXJlcXVpcmUoInBsb3RseSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQ0KICAgbGlicmFyeShwbG90bHkpDQp9DQppZiAoIXJlcXVpcmUoImdhcG1pbmRlciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJnYXBtaW5kZXIiKQ0KICAgbGlicmFyeShnYXBtaW5kZXIpDQp9DQppZiAoIXJlcXVpcmUoInBuZyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygicG5nIikgICAgICAgICAgICAgIyBJbnN0YWxsIHBuZyBwYWNrYWdlDQogICAgbGlicmFyeSgicG5nIikNCn0NCmlmICghcmVxdWlyZSgiUkN1cmwiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoIlJDdXJsIikgICAgICAgICAgICMgSW5zdGFsbCBSQ3VybCBwYWNrYWdlDQogICAgbGlicmFyeSgiUkN1cmwiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJjb2xvdXJwaWNrZXIiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImNvbG91cnBpY2tlciIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJjb2xvdXJwaWNrZXIiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJnaWZza2kiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImdpZnNraSIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJnaWZza2kiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJtYWdpY2siKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoIm1hZ2ljayIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJtYWdpY2siKQ0KfQ0KaWYgKCFyZXF1aXJlKCJnckRldmljZXMiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImdyRGV2aWNlcyIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJnckRldmljZXMiKQ0KfQ0KIyMjIGdncGxvdCBhbmQgZXh0ZW5zaW9ucw0KaWYgKCFyZXF1aXJlKCJnZ3Bsb3QyIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImdncGxvdDIiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJnZ2FuaW1hdGUiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImdnYW5pbWF0ZSIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJnZ2FuaW1hdGUiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJnZ3JpZGdlcyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dyaWRnZXMiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZ2dyaWRnZXMiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJncmFwaGljcyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ3JhcGhpY3MiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZ3JhcGhpY3MiKQ0KfQ0KDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwgICANCiAgICAgICAgICAgICAgICAgICAgICByZXN1bHQgPSBUUlVFLCAgIA0KICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBjb21tZW50ID0gTkEpDQpgYGANCg0KXA0KDQojIEludHJvZHVjdGlvbg0KDQpUaGUgZGF0YSBwcm9wZXJ0aWVzIGFyZSB0eXBpY2FsbHkgbnVtZXJpY2FsIG9yIGNhdGVnb3JpY2FsIHZhbHVlcywgd2hpbGUgdGhlIHZpc3VhbCBwcm9wZXJ0aWVzIGluY2x1ZGUgdGhlIHggYW5kIHkgcG9zaXRpb25zIG9mIHBvaW50cywgY29sb3JzIG9mIGxpbmVzLCBoZWlnaHRzIG9mIGJhcnMsIGFuZCBzbyBvbi4gVGhlIHByb2Nlc3Mgb2YgY3JlYXRpbmcgYSBkYXRhIHZpc3VhbGl6YXRpb24gaXMgdG8gbWFwIHRoZSBkYXRhIHByb3BlcnRpZXMgdG8gdmlzdWFsIHByb3BlcnRpZXMuDQoNCkluIFIncyBiYXNlIGdyYXBoaWNzIGZ1bmN0aW9ucywgZWFjaCBtYXBwaW5nIG9mIGRhdGEgcHJvcGVydGllcyB0byB2aXN1YWwgcHJvcGVydGllcyBpcyBpdHMgc3BlY2lhbCBjYXNlLiBDaGFuZ2luZyB0aGUgbWFwcGluZ3MgaW4gdGhlIGJhc2UgUiBncmFwaGljcyBtYXkgcmVxdWlyZSByZXN0cnVjdHVyaW5nIHRoZSBkYXRhIHV0aWxpemluZyBjb21wbGV0ZWx5IGRpZmZlcmVudCBwbG90dGluZyBjb21tYW5kcywgb3IgYm90aC4NCg0KT24gdGhlIG90aGVyIGhhbmQsIGBnZ3Bsb3QyYCBpcyBhIHN5c3RlbSBmb3IgZGVjbGFyYXRpdmVseSBjcmVhdGluZyBncmFwaGljcywgYmFzZWQgb24gVGhlIEdyYW1tYXIgb2YgR3JhcGhpY3MuIFdlIHByb3ZpZGUgdGhlIGRhdGEsIGFuZCB0ZWxsIGdncGxvdDIgaG93IHRvIG1hcCB2YXJpYWJsZXMgdG8gYWVzdGhldGljcyBhbmQgd2hhdCBncmFwaGljYWwgcHJpbWl0aXZlcyB0byB1c2UsIGBnZ3Bsb3QoKWAgdGFrZXMgY2FyZSBvZiB0aGUgZGV0YWlscy4NCg0KVGhlIGdyYXBoaWMgZnVuY3Rpb25zIGluIGJhc2UgUiBhcmUgcG93ZXJmdWwsIGJ1dCBpbiBnZW5lcmFsLCBpdCBpcyBiZWxpZXZlZCB0aGF0IGBnZ3Bsb3QoKWAgaXMgYmV0dGVyLg0KDQpGb3IgdGhvc2Ugd2hvIHByb2dyYW0gaW4gUHl0aG9uLCBJdCBpcyBnb29kIHRvIGtub3cgdGhhdCBgcGxvdG5pbmVgIGlzIGFuIGltcGxlbWVudGF0aW9uIG9mIGEgZ3JhbW1hciBvZiBncmFwaGljcyBpbiAqKlB5dGhvbioqLCBpdCBpcyBiYXNlZCBvbiBgZ2dwbG90MigpYC4NCg0KRm9yIHRob3NlIHdobyBwcm9ncmFtIGluIFNBUywgdGhlIFNBUyBPRFMgZ3JhcGhpY3MgYXJlIHJvdWdobHkgYW5hbG9nb3VzIHRvIFIncyBgZ2dwbG90KClgIGFsdGhvdWdoIGl0IGlzIG5vdCBhIGRpcmVjdCBpbXBsZW1lbnRhdGlvbiBvZiBUaGUgR3JhbW1hciBvZiBHcmFwaGljcy4NCg0KIyBCYXNpY3Mgb2YgYGdncGxvdCgpYA0KDQpQbG90dGluZyB3aXRoIGBnZ3Bsb3QyYCBpcyBiYXNlZCBvbiAiYWRkaW5nIiBwbG90IGxheWVycyBhbmQgZGVzaWduIGVsZW1lbnRzIG9uIHRvcCBvZiBvbmUgYW5vdGhlciwgd2l0aCBlYWNoIGNvbW1hbmQgYWRkZWQgdG8gdGhlIHByZXZpb3VzIG9uZXMgd2l0aCBhIHBsdXMgc3ltYm9sIChgK2ApLiBUaGUgcmVzdWx0IGlzIGEgbXVsdGktbGF5ZXIgcGxvdCBvYmplY3QgdGhhdCBjYW4gYmUgc2F2ZWQsIG1vZGlmaWVkLCBwcmludGVkLCBleHBvcnRlZCwgZXRjLg0KDQpgZ2dwbG90KClgIG9iamVjdHMgY2FuIGJlIGhpZ2hseSBjb21wbGV4LCBidXQgdGhlIGJhc2ljIG9yZGVyIG9mIGxheWVycyB3aWxsIHVzdWFsbHkgbG9vayBsaWtlIHRoaXM6DQoNCjEuICBCZWdpbiB3aXRoIHRoZSBiYXNlbGluZSBgZ2dwbG90KClgIGNvbW1hbmQgLSB0aGlzICJvcGVucyIgdGhlIGdncGxvdCBhbmQgYWxsb3dzIHN1YnNlcXVlbnQgZnVuY3Rpb25zIHRvIGJlIGFkZGVkIHdpdGggYCtgLiBUeXBpY2FsbHkgdGhlIGRhdGEgc2V0IGlzIGFsc28gc3BlY2lmaWVkIGluIHRoaXMgY29tbWFuZA0KDQoyLiAgQWRkIGDigJxnZW9t4oCdYCBsYXllcnMgLSB0aGVzZSBmdW5jdGlvbnMgdmlzdWFsaXplIHRoZSBkYXRhIGFzIGdlb21ldHJpZXMgKHNoYXBlcyksIGUuZy4gYXMgYSBiYXIgZ3JhcGgsIGxpbmUgcGxvdCwgc2NhdHRlciBwbG90LCBoaXN0b2dyYW0gKG9yIGEgY29tYmluYXRpb24hKS4gVGhlc2UgZnVuY3Rpb25zIGFsbCBzdGFydCB3aXRoIGBnZW9tX2AgYXMgYSBwcmVmaXguDQoNCjMuICBBZGQgZGVzaWduIGVsZW1lbnRzIHRvIHRoZSBwbG90IHN1Y2ggYXMgYXhpcyBsYWJlbHMsIHRpdGxlcywgZm9udHMsIHNpemVzLCBjb2xvciBzY2hlbWVzLCBsZWdlbmRzLCBvciBheGVzIHJvdGF0aW9uDQoNCldlIGNhbiBjaGVjayB0aGUgdGlkeXZlcnNlIHJlZmVyZW5jZSBzaXRlIGZvciBtb3JlIGRldGFpbHMgYXQgPGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9pbmRleC5odG0+DQoNCkEgc2ltcGxlIGV4YW1wbGUgb2Ygc2tlbGV0b24gY29kZSBpcyBhcyBmb2xsb3dzLiBXZSB3aWxsIGV4cGxhaW4gZWFjaCBjb21wb25lbnQgaW4gdGhlIGNvZGUgYmVsb3cuDQoNCmBgYCAgICAgICAgIA0KIyBQbG90IGRhdGEgZnJvbSBteSBkYXRhIGNvbHVtbnMgYXMgcmVkIHBvaW50cw0KZ2dwbG90KGRhdGEgPSBteV9kYXRhKSAgKyAgICAgICAgICAgICAgICMgdXNlIHRoZSBkYXRhc2V0ICJteV9kYXRhIg0KICBnZW9tX3BvaW50KCAgICAgICAgICAgICAgICAgICAgICAgICAgICMgYWRkIGEgbGF5ZXIgb2YgcG9pbnRzIChkb3RzKQ0KICAgIG1hcHBpbmcgPSBhZXMoeCA9IGNvbDEsIHkgPSBjb2wyKSwgICMgIm1hcCIgZGF0YSBjb2x1bW4gdG8gYXhlcw0KICAgIGNvbG9yID0gInJlZCIpICArICAgICAgICAgICAgICAgICAgICMgT3RoZXIgc3BlY2lmaWNhdGlvbnMgZm9yIHRoZSBnZW9tDQogIGxhYnMoKSAgKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBoZXJlIHlvdSBhZGQgdGl0bGVzLCBheGVzIGxhYmVscywgZXRjLg0KICB0aGVtZSgpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaGVyZSB5b3UgYWRqdXN0IGNvbG9yLCBmb250LCBzaXplIGV0YyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBvZiBub24tZGF0YSBwbG90IGVsZW1lbnRzIChheGVzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRpdGxlLCBldGMuKSANCmBgYA0KDQpJbiB0aGUgZm9sbG93aW5nIHNlY3Rpb25zLCB3ZSB3aWxsIGRldGFpbCBlYWNoIG9mIHRoZSBjb21wb25lbnRzIGluIHRoZSBhYm92ZSBjb2RlLg0KDQojIFN0cnVjdHVyZSBvZiBgZ2dwbG90KClgDQoNClRoZSBvcGVuaW5nIGNvbW1hbmQgb2YgYW55IGBnZ3Bsb3QyYCBwbG90IGlzIGBnZ3Bsb3QoKWAuIFRoaXMgY29tbWFuZCBzaW1wbHkgY3JlYXRlcyBhIGJsYW5rIGNhbnZhcyB1cG9uIHdoaWNoIHRvIGFkZCBsYXllcnMuIEl0ICJvcGVucyIgdGhlIHdheSBmb3IgZnVydGhlciBsYXllcnMgdG8gYmUgYWRkZWQgd2l0aCBhIGArYCBzeW1ib2wuDQoNClR5cGljYWxseSwgdGhlIGNvbW1hbmQgZ2dwbG90KCkgaW5jbHVkZXMgdGhlIGBkYXRhID0gYXJndW1lbnRgIGZvciB0aGUgcGxvdC4gVGhpcyBzZXRzIHRoZSBkZWZhdWx0IGRhdGEgc2V0IHRvIGJlIHVzZWQgZm9yIHN1YnNlcXVlbnQgbGF5ZXJzIG9mIHRoZSBwbG90Lg0KDQpUaGlzIGNvbW1hbmQgd2lsbCBlbmQgd2l0aCBhIGArYCBhZnRlciBpdHMgY2xvc2luZyBwYXJlbnRoZXNlcy4gVGhpcyBsZWF2ZXMgdGhlIGNvbW1hbmQgIm9wZW4iLiBUaGUgYGdncGxvdGAgd2lsbCBvbmx5IGV4ZWN1dGUvYXBwZWFyIHdoZW4gdGhlIGZ1bGwgY29tbWFuZCBpbmNsdWRlcyBhIGZpbmFsIGxheWVyICoqd2l0aG91dCoqIGEgYCtgIGF0IHRoZSBlbmQuDQoNCmBgYCAgICAgICAgIA0KIyBUaGlzIHdpbGwgY3JlYXRlIGEgcGxvdCB0aGF0IGlzIGEgYmxhbmsgY2FudmFzDQpnZ3Bsb3QoZGF0YSA9IGxpbmVsaXN0KQ0KYGBgDQoNCiMjIEdlb21zDQoNClRoZSBhYm92ZSBjb2RlIGNyZWF0ZXMgYSBibGFuayBjYW52YXMuIFdlIG5lZWQgdG8gY3JlYXRlIGdlb21ldHJpZXMgKHNoYXBlcykgZnJvbSBvdXIgZGF0YSAoZS5nLiBiYXIgcGxvdHMsIGhpc3RvZ3JhbXMsIHNjYXR0ZXIgcGxvdHMsIGJveCBwbG90cykuDQoNClRoaXMgaXMgZG9uZSBieSBhZGRpbmcgbGF5ZXJzIG9mICJnZW9tcyIgdG8gdGhlIGluaXRpYWwgYGdncGxvdCgpYCBjb21tYW5kLiBNYW55IGBnZ3Bsb3QyYCBmdW5jdGlvbnMgY3JlYXRlICJnZW9tcyIuIEVhY2ggb2YgdGhlc2UgZnVuY3Rpb25zIGJlZ2lucyB3aXRoICJnZW9tXF8iLCBzbyB3ZSB3aWxsIHJlZmVyIHRvIHRoZW0gZ2VuZXJpY2FsbHkgYXMgYGdlb21fWFhYWCgpYC4NCg0KVGhlcmUgYXJlIG1hbnkgZ2VvbXMgaW4gZ2dwbG90MiBhbmQgbWFueSBvdGhlcnMgY3JlYXRlZCBieSBmYW5zLiBWaWV3IHRoZW0gYXQgdGhlIGBnZ3Bsb3QyYCBnYWxsZXJ5LiBTb21lIGNvbW1vbiBgZ2VvbXNgIGFyZSBsaXN0ZWQgYmVsb3c6DQoNCi0gICBIaXN0b2dyYW1zIC0gYGdlb21faGlzdG9ncmFtKClgDQotICAgQmFyIGNoYXJ0cyAtIGBnZW9tX2JhcigpYCBvciBgZ2VvbV9jb2woKWANCi0gICBCb3ggcGxvdHMgLSBgZ2VvbV9ib3hwbG90KClgDQotICAgUG9pbnRzIChlLmcuIHNjYXR0ZXIgcGxvdHMpIC0gYGdlb21fcG9pbnQoKWANCi0gICBMaW5lIGdyYXBocyAtIGBnZW9tX2xpbmUoKWAgb3IgYGdlb21fcGF0aCgpYA0KLSAgIFRyZW5kIGxpbmVzIC0gYGdlb21fc21vb3RoKClgDQoNCldlIGNhbiBkaXNwbGF5IG9uZSBvciBtdWx0aXBsZSBgZ2VvbXNgIGluIG9uZSBwbG90LiBFYWNoIGlzIGFkZGVkIHRvIHByZXZpb3VzIGBnZ3Bsb3QyYCBjb21tYW5kcyB3aXRoIGEgYCtgLCBhbmQgdGhleSBhcmUgcGxvdHRlZCBzZXF1ZW50aWFsbHkgc3VjaCB0aGF0IGxhdGVyIGBnZW9tc2AgYXJlIHBsb3R0ZWQgb24gdG9wIG9mIHByZXZpb3VzIG9uZXMuDQoNCkZvciB0aGUgY29tcGxldGUgbGlzdCBvZiBjdXJyZW50bHkgYXZhaWxhYmxlIGdlb21zLCB1c2luZyB0aGUgZm9sbG93aW5nIGNvbW1hbmQgaW4gdGhlIFIgQ29uc29sZToNCg0KYGBgICAgICAgICAgDQpscyhwYXR0ZXJuID0gJ15nZW9tXycsIGVudiA9IGFzLmVudmlyb25tZW50KCdwYWNrYWdlOmdncGxvdDInKSkNCmBgYA0KDQojIyBBSSBHZW5lcmF0ZWQgRGVzY2lwdGlvbiBvZiBDb21tb25seSBVc2VkIGBnZW9tc2ANCg0KVGhlIGdlbmVyYXRpdmUgQUkgKGxhcmdlIGxhbmd1YWdlIG1vZGVsKSBjYW4gaGVscCBzdW1tYXJpemUgbW9zdCBjb21tb25seSB1c2UgYGdlb21zYCBpbiBnZ3Bsb3QuIFRoZSBmb2xsb3dpbmcgaXMgYSBsaXN0IGBnZW9tc2Agd2l0aCBtb3JlIGRldGFpbHMgYmFzZWQgb24gdGhlIHBocmFzZSAqKmxpc3Qgb2YgZ2VvbXMgaW4gZ2dwbG90KiogdGhyb3VnaCB0aGUgTWljcm9zb2Z0IENvcGlsb3QgdmlhIE1pcmNvc29mdCBFZGdlOg0KDQpHZW9tcyBhcmUgdGhlIGdlb21ldHJpYyBvYmplY3RzIHRoYXQgZGVmaW5lIHRoZSB0eXBlIGFuZCBhcHBlYXJhbmNlIG9mIHRoZSBwbG90cyBjcmVhdGVkIGJ5IHRoZSBnZ3Bsb3QyIHBhY2thZ2UgaW4gUi4gR2VvbXMgY2FuIGJlIGNvbWJpbmVkIGFuZCBsYXllcmVkIHRvIGNyZWF0ZSBjb21wbGV4IGFuZCBjdXN0b21pemVkIHZpc3VhbGl6YXRpb25zIG9mIGRhdGEuIEdlb21zIGFyZSBzcGVjaWZpZWQgYnkgdXNpbmcgZ2VvbVxfIGZ1bmN0aW9ucywgc3VjaCBhcyBnZW9tX3BvaW50LCBnZW9tX2JhciwgZ2VvbV9saW5lLCBldGMuDQoNClRoZXJlIGFyZSBtYW55IGdlb21zIGF2YWlsYWJsZSBpbiBnZ3Bsb3QyLCBlYWNoIHdpdGggaXRzIG93biBhZXN0aGV0aWNzIGFuZCBwYXJhbWV0ZXJzLiBTb21lIG9mIHRoZSBtb3N0IGNvbW1vbiBhbmQgdXNlZnVsIGdlb21zIGFyZToNCg0KLSAgICoqZ2VvbV9wb2ludCoqOiBUaGlzIGdlb20gZHJhd3MgcG9pbnRzIG9uIGEgcGxvdCwgYW5kIGNhbiBiZSB1c2VkIHRvIGNyZWF0ZSBgc2NhdHRlcnBsb3RzLCBkb3RwbG90cywgYnViYmxlIGNoYXJ0c2AsIGV0Yy4gSXQgcmVxdWlyZXMgeCBhbmQgeSBhZXN0aGV0aWNzLCBhbmQgY2FuIGFsc28gdGFrZSBgc2l6ZSwgc2hhcGUsIGNvbG91ciwgZmlsbGAgYW5kIGBhbHBoYWAgYWVzdGhldGljcyB0byBjb250cm9sIHRoZSBhcHBlYXJhbmNlIG9mIHRoZSBwb2ludHMuDQoNCi0gICAqKmdlb21fYmFyKiogYW5kICoqZ2VvbV9jb2wqKjogVGhlc2UgZ2VvbXMgZHJhdyBiYXJzIG9uIGEgcGxvdCwgYW5kIGNhbiBiZSB1c2VkIHRvIGNyZWF0ZSBgYmFyIGNoYXJ0cywgaGlzdG9ncmFtcywgbG9sbGlwb3AgY2hhcnRzYCwgZXRjLiBUaGV5IHJlcXVpcmUgeCBhbmQgeSBhZXN0aGV0aWNzLCB3aGVyZSB5IHJlcHJlc2VudHMgdGhlIGhlaWdodCBvZiB0aGUgYmFycy4gVGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBgZ2VvbV9iYXJgIGFuZCBgZ2VvbV9jb2xgIGlzIHRoYXQgYGdlb21fYmFyYCB1c2VzIGBzdGF0X2NvdW50YCBieSBkZWZhdWx0LCB3aGljaCBjb3VudHMgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgaW4gZWFjaCB4IGdyb3VwLCB3aGlsZSBgZ2VvbV9jb2xgIHVzZXMgYHN0YXRfaWRlbnRpdHlgIGJ5IGRlZmF1bHQsIHdoaWNoIHRha2VzIHRoZSB5IHZhbHVlcyBhcyBnaXZlbi4gVGhlc2UgZ2VvbXMgY2FuIGFsc28gdGFrZSBgc2l6ZSwgbGluZXR5cGUsIGNvbG91ciwgZmlsbGAgYW5kIGFscGhhIGFlc3RoZXRpY3MgdG8gY29udHJvbCB0aGUgYXBwZWFyYW5jZSBvZiB0aGUgYmFycy4NCg0KLSAgICoqZ2VvbV9saW5lKiogYW5kICoqZ2VvbV9wYXRoKio6IFRoZXNlIGdlb21zIGRyYXcgbGluZXMgb24gYSBwbG90LCBhbmQgY2FuIGJlIHVzZWQgdG8gY3JlYXRlIGBsaW5lIGNoYXJ0cywgdGltZSBzZXJpZXMgcGxvdHMsIHNwYWdoZXR0aSBwbG90c2AsIGV0Yy4gVGhleSByZXF1aXJlIHggYW5kIHkgYWVzdGhldGljcywgd2hlcmUgeCByZXByZXNlbnRzIHRoZSBvcmRlciBvZiB0aGUgcG9pbnRzIGFsb25nIHRoZSBsaW5lLiBUaGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGBnZW9tX2xpbmVgIGFuZCBgZ2VvbV9wYXRoYCBpcyB0aGF0IGBnZW9tX2xpbmVgIHNvcnRzIHRoZSBkYXRhIGJ5IHggYmVmb3JlIHBsb3R0aW5nLCB3aGlsZSBgZ2VvbV9wYXRoYCBwbG90cyB0aGUgZGF0YSBpbiB0aGUgb3JkZXIgdGhleSBhcHBlYXIgaW4gdGhlIGRhdGEgZnJhbWUuIFRoZXNlIGdlb21zIGNhbiBhbHNvIHRha2UgYHNpemUsIGxpbmV0eXBlLCBjb2xvdXJgIGFuZCBgYWxwaGFgIGFlc3RoZXRpY3MgdG8gY29udHJvbCB0aGUgYXBwZWFyYW5jZSBvZiB0aGUgbGluZXMuDQoNCi0gICAqKmdlb21fYm94cGxvdCoqOiBUaGlzIGdlb20gZHJhd3MgYm94cGxvdHMgb24gYSBwbG90LCBhbmQgY2FuIGJlIHVzZWQgdG8gZGlzcGxheSB0aGUgZGlzdHJpYnV0aW9uIG9mIGEgbnVtZXJpYyB2YXJpYWJsZSBhY3Jvc3MgZGlmZmVyZW50IGdyb3Vwcy4gSXQgcmVxdWlyZXMgeCBhbmQgeSBhZXN0aGV0aWNzLCB3aGVyZSB4IHJlcHJlc2VudHMgdGhlIGdyb3VwaW5nIHZhcmlhYmxlIGFuZCB5IHJlcHJlc2VudHMgdGhlIG51bWVyaWMgdmFyaWFibGUuIEl0IGFsc28gdXNlcyBzdGF0X2JveHBsb3QgYnkgZGVmYXVsdCwgd2hpY2ggY29tcHV0ZXMgdGhlIGBmaXZlLW51bWJlciBzdW1tYXJ5YCAobWluaW11bSwgbG93ZXIgcXVhcnRpbGUsIG1lZGlhbiwgdXBwZXIgcXVhcnRpbGUgYW5kIG1heGltdW0pIG9mIGVhY2ggZ3JvdXAuIFRoaXMgZ2VvbSBjYW4gYWxzbyB0YWtlIGBzaXplLCBsaW5ldHlwZSwgY29sb3VyLCBmaWxsYCBhbmQgYGFscGhhYCBhZXN0aGV0aWNzIHRvIGNvbnRyb2wgdGhlIGFwcGVhcmFuY2Ugb2YgdGhlIGJveHBsb3RzLg0KDQotICAgKipnZW9tX2hpc3RvZ3JhbSoqOiBUaGlzIGdlb20gZHJhd3MgaGlzdG9ncmFtcyBvbiBhIHBsb3QsIGFuZCBjYW4gYmUgdXNlZCB0byBkaXNwbGF5IHRoZSBkaXN0cmlidXRpb24gb2YgYSBzaW5nbGUgbnVtZXJpYyB2YXJpYWJsZS4gSXQgcmVxdWlyZXMgeCBhZXN0aGV0aWMsIHdoaWNoIHJlcHJlc2VudHMgdGhlIG51bWVyaWMgdmFyaWFibGUgdG8gYmUgYmlubmVkLiBJdCBhbHNvIHVzZXMgc3RhdF9iaW4gYnkgZGVmYXVsdCwgd2hpY2ggY291bnRzIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIGVhY2ggYmluLiBUaGlzIGdlb20gY2FuIGFsc28gdGFrZSBgc2l6ZSwgbGluZXR5cGUsIGNvbG91ciwgZmlsbGAgYW5kIGBhbHBoYWAgYWVzdGhldGljcyB0byBjb250cm9sIHRoZSBhcHBlYXJhbmNlIG9mIHRoZSBoaXN0b2dyYW0uDQoNCi0gICAqKmdlb21fZGVuc2l0eSoqIGFuZCAqKmdlb21fZGVuc2l0eV8yZCoqOiBUaGVzZSBnZW9tcyBkcmF3IGRlbnNpdHkgcGxvdHMgb24gYSBwbG90LCBhbmQgY2FuIGJlIHVzZWQgdG8gZGlzcGxheSB0aGUgZGlzdHJpYnV0aW9uIG9mIG9uZSBvciB0d28gbnVtZXJpYyB2YXJpYWJsZXMgdXNpbmcgc21vb3RoIGN1cnZlcy4gVGhleSByZXF1aXJlIHggYWVzdGhldGljIGZvciBvbmUtZGltZW5zaW9uYWwgZGVuc2l0eSBwbG90cyBvciB4IGFuZCB5IGFlc3RoZXRpY3MgZm9yIHR3by1kaW1lbnNpb25hbCBkZW5zaXR5IHBsb3RzLiBUaGV5IGFsc28gdXNlIGBzdGF0X2RlbnNpdHlgIG9yIGBzdGF0X2RlbnNpdHlfMmRgIGJ5IGRlZmF1bHQsIHdoaWNoIGVzdGltYXRlIHRoZSBwcm9iYWJpbGl0eSBkZW5zaXR5IGZ1bmN0aW9uIG9mIHRoZSB2YXJpYWJsZXMgdXNpbmcga2VybmVsIHNtb290aGluZy4gVGhlc2UgZ2VvbXMgY2FuIGFsc28gdGFrZSBgc2l6ZSwgbGluZXR5cGUsIGNvbG91ciwgZmlsbGAgYW5kIGBhbHBoYWAgYWVzdGhldGljcyB0byBjb250cm9sIHRoZSBhcHBlYXJhbmNlIG9mIHRoZSBkZW5zaXR5IHBsb3RzLg0KDQotICAgKipnZW9tX3RpbGUqKiBhbmQgKipnZW9tX2JpbjJkKio6IFRoZXNlIGdlb21zIGRyYXcgdGlsZXMgb24gYSBwbG90LCBhbmQgY2FuIGJlIHVzZWQgdG8gY3JlYXRlIGhlYXRtYXBzIG9yIGJpdmFyaWF0ZSBoaXN0b2dyYW1zIG9mIHR3byBudW1lcmljIHZhcmlhYmxlcy4gVGhleSByZXF1aXJlIHggYW5kIHkgYWVzdGhldGljcywgd2hpY2ggcmVwcmVzZW50IHRoZSBudW1lcmljIHZhcmlhYmxlcyB0byBiZSBiaW5uZWQuIFRoZXkgYWxzbyB1c2Ugc3RhdF9pZGVudGl0eSBvciBzdGF0X2JpbjJkIGJ5IGRlZmF1bHQsIHdoaWNoIHRha2Ugb3IgY29tcHV0ZSB0aGUgdmFsdWVzIGZvciBlYWNoIHRpbGUuIFRoZXNlIGdlb21zIGNhbiBhbHNvIHRha2UgYHNpemUsIGxpbmV0eXBlLCBjb2xvdXIsIGZpbGxgIGFuZCBgYWxwaGFgIGFlc3RoZXRpY3MgdG8gY29udHJvbCB0aGUgYXBwZWFyYW5jZSBvZiB0aGUgdGlsZXMuDQoNCi0gICAqKmdlb21fdGV4dCoqIGFuZCAqKmdlb21fbGFiZWwqKjogVGhlc2UgZ2VvbXMgZHJhdyB0ZXh0IG9yIGxhYmVscyBvbiBhIHBsb3QsIGFuZCBjYW4gYmUgdXNlZCB0byBhZGQgYW5ub3RhdGlvbnMgb3IgZGF0YSBsYWJlbHMgdG8gYSBwbG90LiBUaGV5IHJlcXVpcmUgeCBhbmQgeSBhZXN0aGV0aWNzIGZvciBwb3NpdGlvbmluZyBhbmQgbGFiZWwgYWVzdGhldGljIGZvciB0ZXh0IGNvbnRlbnQuIFRoZXkgY2FuIGFsc28gdGFrZSBgc2l6ZSwgY29sb3VyLCBmaWxsLCBhbHBoYSwgaGp1c3QsIHZqdXN0YCBhbmQgYHBhcnNlYCBhZXN0aGV0aWNzIHRvIGNvbnRyb2wgdGhlIGFwcGVhcmFuY2Ugb2YgdGhlIHRleHQgb3IgbGFiZWxzLg0KDQojIyBNYXBwaW5nIERhdGEgdG8gUGxvdA0KDQpgZ2VvbWAgZnVuY3Rpb25zIHJlcXVpcmUgbWFwcGluZyAoYXNzaWduaW5nKSBjb2x1bW5zIGluIHRoZSBkYXRhIHRvIGNvbXBvbmVudHMgb2YgdGhlIHBsb3QgbGlrZSB0aGUgYXhlcywgc2hhcGUgY29sb3JzLCBzaGFwZSBzaXplcywgZXRjLiBUaGUgbWFwcGluZ3MgbXVzdCBiZSB3cmFwcGVkIGluIHRoZSBgYWVzKClgIGZ1bmN0aW9uLCBzbyB3ZSB3b3VsZCB3cml0ZSBzb21ldGhpbmcgbGlrZSBgbWFwcGluZyA9IGFlcyh4ID0gY29sMSwgeSA9IGNvbDIpYC4NCg0KRm9yIGV4YW1wbGUsIGluIHRoZSBmb2xsb3dpbmcgZXhhbXBsZSB1c2luZyBgaXJpcyBkYXRhYCwgU2VwYWwgTGVuZ3RoIGlzIG1hcHBlZCB0byB0aGUgeC1heGlzLCBhbmQgU2VwYWwgV2lkdGggaXMgbWFwcGVkIHRvIHRoZSB5LWF4aXMuIEFmdGVyIGEgKywgdGhlIHBsb3R0aW5nIGNvbW1hbmRzIGNvbnRpbnVlLiBBIHNoYXBlIGlzIGNyZWF0ZWQgd2l0aCB0aGUgImdlb20iIGZ1bmN0aW9uIGdlb21fcG9pbnQoKS4NCg0KYGBge3IsIGZpZy5hbGlnbj0nY2VudGVyJ30NCmdncGxvdChkYXRhID0gaXJpcywgbWFwcGluZyA9IGFlcyh4ID0gU2VwYWwuTGVuZ3RoLCB5ID0gU2VwYWwuV2lkdGgpKSArDQogIGdlb21fcG9pbnQoKQ0KYGBgDQoNCldoZW4gY3JlYXRpbmcgYSBoaXN0b2dyYW0sIG9ubHkgb25lIHZhcmlhYmxlIGlzIHVzZWQuIFNlZSB0aGUgZm9sbG93aW5nIGV4YW1wbGUuDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQpnZ3Bsb3QoZGF0YSA9IGlyaXMsIG1hcHBpbmcgPSBhZXMoeCA9IFBldGFsLldpZHRoKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMikNCmBgYA0KDQojIyBBcnJhbmdpbmcgTXVsdGlwbGUgR3JvYnMgb24gdGhlIFNhbWUgUGFnZQ0KDQpJbiB0aGUgYWJvdmUgc3Vic2VjdGlvbiwgd2UgY3JlYXRlIHR3byBncmFwaHMgb24gdHdvIGRpZmZlcmVudCBwYWdlcy4gU29tZXRpbWVzLCB3ZSB3YW50IHRvIHBsYWNlIHR3byBtb3JlIGdyYXBocyBvbiB0aGUgc2FtZSBwYWdlIGZvciBjb21wYXJpc29uIHB1cnBvc2VzLiBJbiBiYXNlIFIsIHdlIGhhdmUgZ3JhcGhpYyBmdW5jdGlvbnMgc3VjaCBhcyBgcGFyKClgIGFuZCBgbGF5b3V0KClgIHRvIHNldCB1cCBhIGxheW91dCBmb3IgdGhlIGdyYXBoaWMgcGFnZS4NCg0KSW4gdGhpcyBub3RlLCB3ZSBpbnRyb2R1Y2UgdGhlIGxpYnJhcnkgYGNvd3Bsb3RgIHRvIGFycmFuZ2UgbXVsdGlwbGUgZ3JhcGhpY2FsIG9iamVjdHMgKGEuay5hIGBncm9ic2ApIG9uIGEgcGFnZS4NCg0KYGBge3IsIGZpZy5hbGlnbj0nY2VudGVyJ30NCiMjIE5hbWUgdGhlIHR3byBwbG90cyBmaXJzdCBhbmQgdGhlbiBjYWxsIHRoZSB0d28gZ3JvYnMgaW4gdGhlIGxheW91dCBmdW5jdGlvbg0KIyMgc2NhdHRlciBwbG90DQpzY2F0dGVyID0gZ2dwbG90KGRhdGEgPSBpcmlzLCBtYXBwaW5nID0gYWVzKHggPSBTZXBhbC5MZW5ndGgsIHkgPSBTZXBhbC5XaWR0aCkpICsNCiAgZ2VvbV9wb2ludCgpDQojIyBoaXN0b2dyYW0NCmhpc3QgPSBnZ3Bsb3QoZGF0YSA9IGlyaXMsIG1hcHBpbmcgPSBhZXMoeCA9IFBldGFsLldpZHRoKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMikNCiMjIHVzZSBwbG90X2dyaWQoKSBpbiB7Y293cGxvdH0gdG8gbGF5b3V0IHRoZSB0d28gcGxvdHMNCnBsb3RfZ3JpZChzY2F0dGVyLCBoaXN0LCBsYWJlbHM9YygiQSIsICJCIiksIG5jb2wgPSAyLCBucm93ID0gMSkNCmBgYA0KDQojIyBQbG90IEFlc3RoZXRpY3MNCg0KSW4gYGdncGxvdGAgdGVybWlub2xvZ3kgYSBwbG90ICJhZXN0aGV0aWMiIGhhcyBhIHNwZWNpZmljIG1lYW5pbmcuIEl0IHJlZmVycyB0byBjb2xvcnMsIHNpemVzLCB0cmFuc3BhcmVuY2llcywgcGxhY2VtZW50LCBldGMuIG9mIHRoZSBwbG90dGVkIGRhdGEuIGBOb3QgYWxsIGdlb21zIHdpbGwgaGF2ZSB0aGUgc2FtZSBhZXN0aGV0aWMgb3B0aW9uc2AsIGJ1dCBtYW55IGNhbiBiZSB1c2VkIGJ5IG1vc3QgYGdlb21zYC4NCg0KSGVyZSBhcmUgc29tZSBleGFtcGxlczoNCg0KLSAgIGBzaGFwZWAgPSBEaXNwbGF5IGEgcG9pbnQgd2l0aCBgZ2VvbV9wb2ludCgpYCBhcyBhIGBkb3RgLCBgc3RhcmAsIGB0cmlhbmdsZWAsIG9yIGBzcXVhcmVgLCBldGMuDQotICAgYGZpbGxgID0gVGhlIGludGVyaW9yIGNvbG9yIChlLmcuIG9mIGEgYmFyIG9yIGJveHBsb3QpDQotICAgYGNvbG9yYCA9IFRoZSBleHRlcmlvciBsaW5lIG9mIGEgYmFyLCBib3hwbG90LCBldGMuLCBvciB0aGUgcG9pbnQgY29sb3IgaWYgdXNpbmcgYGdlb21fcG9pbnQoKWANCi0gICBgc2l6ZWAgPSBTaXplIChlLmcuIGxpbmUgdGhpY2tuZXNzLCBwb2ludCBzaXplKQ0KLSAgIGBhbHBoYWAgPSBUcmFuc3BhcmVuY3kgKDEgPSBvcGFxdWUsIDAgPSBpbnZpc2libGUpDQotICAgYGJpbndpZHRoYCA9IFdpZHRoIG9mIGhpc3RvZ3JhbSBiaW5zDQotICAgYHdpZHRoYCA9IFdpZHRoIG9mICJiYXIgcGxvdCIgY29sdW1ucw0KLSAgIGBsaW5ldHlwZWAgPSBMaW5lIHR5cGUgKGUuZy4gYHNvbGlkYCwgYGRhc2hlZGAsIGBkb3R0ZWRgKQ0KDQpUaGUgYWVzdGhldGljcyBvZiBwbG90IG9iamVjdHMgY2FuIGJlIGFzc2lnbmVkIHZhbHVlcyBpbiB0d28gd2F5czoNCg0KMS4gIEFzc2lnbmVkIGEgc3RhdGljIHZhbHVlIChlLmcuIGNvbG9yID0gImJsdWUiKSB0byBhcHBseSBhY3Jvc3MgYWxsIHBsb3R0ZWQgb2JzZXJ2YXRpb25zDQoNCjIuICBBc3NpZ25lZCB0byBhIGNvbHVtbiBvZiB0aGUgZGF0YSAoZS5nLiBjb2xvciA9IGhvc3BpdGFsKSBzdWNoIHRoYXQgdGhlIGRpc3BsYXkgb2YgZWFjaCBvYnNlcnZhdGlvbiBkZXBlbmRzIG9uIGl0cyB2YWx1ZSBpbiB0aGF0IGNvbHVtbg0KDQpXZSBoYXZlIGFscmVhZHkgYWRkZWQgYmlud2lkdGggdG8gdGhlIGFib3ZlIGhpc3RvZ3JhbS4gTmV4dCwgd2UgYWRkIGNvbG9yIHRvIHRoZSBoaXN0b2dyYW0NCg0KYGBge3IsIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9NH0NCiMgQ2hhbmdlIGhpc3RvZ3JhbSBwbG90IGxpbmUgY29sb3JzIGJ5IGdyb3Vwcw0Kc2NhdHRlcjAxIDwtIGdncGxvdChpcmlzLCBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gU3BlY2llcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gUGV0YWwuV2lkdGgpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpDQojIE92ZXJsYWlkIGhpc3RvZ3JhbXMNCmhpc3QwMSA8LSBnZ3Bsb3QoaXJpcywgYWVzKHggPSBQZXRhbC5XaWR0aCwgY29sb3I9U3BlY2llcykpICsNCiAgICAgICAgICBnZW9tX2hpc3RvZ3JhbShmaWxsPSJuYXZ5IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjcsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gImlkZW50aXR5IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgYmlud2lkdGggPSAwLjIpDQojIyB1c2UgcGxvdF9ncmlkKCkgaW4ge2Nvd3Bsb3R9IHRvIGxheSBvdXQgdGhlIHR3byBwbG90cw0KcGxvdF9ncmlkKHNjYXR0ZXIwMSwgaGlzdDAxLCBsYWJlbHM9YygiQSIsICJCIiksIG5jb2wgPSAyLCBucm93ID0gMSkNCmBgYA0KDQojIyBMYWJlbHMgaW4gYGdncGxvdCgpYA0KDQpTdXJlbHkgeW91IHdpbGwgd2FudCB0byBhZGQgb3IgYWRqdXN0IHRoZSBwbG90J3MgbGFiZWxzLiBUaGVzZSBhcmUgbW9zdCBlYXNpbHkgZG9uZSB3aXRoaW4gdGhlIGBsYWJzKClgIGZ1bmN0aW9uIHdoaWNoIGlzIGFkZGVkIHRvIHRoZSBwbG90IHdpdGggYCtgIGp1c3QgYXMgdGhlIGBnZW9tc2Agd2VyZS4NCg0KV2l0aGluIGBsYWJzKClgIHlvdSBjYW4gcHJvdmlkZSBjaGFyYWN0ZXIgc3RyaW5ncyB0byB0aGVzZSBhcmd1bWVudHM6DQoNCi0gICBgeCA9YCBhbmQgYHkgPWA6IFRoZSB4LWF4aXMgYW5kIHktYXhpcyB0aXRsZSAobGFiZWxzKQ0KDQotICAgYHRpdGxlID1gOiBUaGUgbWFpbiBwbG90IHRpdGxlDQoNCi0gICBgc3VidGl0bGUgPWA6IFRoZSBzdWJ0aXRsZSBvZiB0aGUgcGxvdCwgaW4gc21hbGxlciB0ZXh0IGJlbG93IHRoZSB0aXRsZQ0KDQotICAgYGNhcHRpb24gPWA6IFRoZSBjYXB0aW9uIG9mIHRoZSBwbG90LCBpbiBib3R0b20tcmlnaHQgYnkgZGVmYXVsdA0KDQpIZXJlIGlzIGEgcGxvdCB3ZSBtYWRlIGVhcmxpZXIsIGJ1dCB3aXRoIG5pY2VyIGxhYmVsczoNCg0KYGBge3IsIGZpZy5hbGlnbj0nY2VudGVyJ30NCmdncGxvdChpcmlzLCBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gU3BlY2llcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gUGV0YWwuV2lkdGgpKSArDQogICAgICAgICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKw0KICAgICAgICAgICAgIGxhYnMoDQogICAgICAgICAgICAgICAgIHggPSAiU2VwYWwgTGVuZ3RoIiwNCiAgICAgICAgICAgICAgICAgeSA9ICJTZXBhbCBXaWR0aCIsDQogICAgICAgICAgICAgICAgICMgbGFiZWwgZm9yIGxlZ2VuZHMNCiAgICAgICAgICAgICAgICAgc2l6ZSA9ICJTZXBhbCBMZW5ndGg6IiwNCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiU3BlY2llczoiLA0KICAgICAgICAgICAgICAgICB0aXRsZSA9ICJBc3NvY2lhdGlvbiBiZXR3ZWVuIFNlcGFsIExlbmd0aCBhbmQgV2lkdGgiLA0KICAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9ICJUaGlzIGlzIGEgcGFydGlhbCBzY2F0dGVyIHBsb3QiLA0KICAgICAgICAgICAgICAgICBjYXB0aW9uID0gcGFzdGUoIkNyZWF0ZWQgb24iLCBTeXMuRGF0ZSgpKSkgKw0KICAgICAgICAgICAgIHRoZW1lX21pbmltYWwoKSAgICMgbWluaW1hbCB0aGVtZQ0KYGBgDQoNCiMgVGhlbWVzIGluIGBnZ3Bsb3QoKWANCg0KVGhlIHRoZW1lIHN5c3RlbSBpbiBgZ2dwbG90KClgIGRvZXMgbm90IGFmZmVjdCBob3cgdGhlIGRhdGEgaXMgcmVuZGVyZWQgYnkgYGdlb21zYCwgb3IgaG93IGl0IGlzIHRyYW5zZm9ybWVkIGJ5IHNjYWxlcy4gYFRoZW1lc2AgZG9uJ3QgY2hhbmdlIHRoZSBwZXJjZXB0dWFsIHByb3BlcnRpZXMgb2YgdGhlIHBsb3QsIGJ1dCB0aGV5IGRvIGhlbHAgeW91IG1ha2UgdGhlIHBsb3QgYWVzdGhldGljYWxseSBwbGVhc2luZyBvciBtYXRjaCBhbiBleGlzdGluZyBzdHlsZSBndWlkZS4gYFRoZW1lc2AgZ2l2ZSB1cyBjb250cm9sIG92ZXIgdGhpbmdzIGxpa2UgZm9udHMsIHRpY2tzLCBwYW5lbCBzdHJpcGVzLCBhbmQgYmFja2dyb3VuZHMuDQoNCkluIG90aGVyIHdvcmRzLCB3aGVuIGNyZWF0aW5nIHRoZSBwbG90IHdlIGRldGVybWluZSBob3cgdGhlIGRhdGEgaXMgZGlzcGxheWVkLCBhbmQgdGhlbiBhZnRlciBpdCBoYXMgYmVlbiBjcmVhdGVkIHdlIGNhbiBlZGl0IGV2ZXJ5IGRldGFpbCBvZiB0aGUgcmVuZGVyaW5nLCB1c2luZyB0aGUgdGhlbWluZyBzeXN0ZW0uDQoNCiMjIFRoZW1pbmcgU3lzdGVtIFN0cnVjdHVyZQ0KDQpUaGUgdGhlbWluZyBzeXN0ZW0gaXMgY29tcG9zZWQgb2YgZm91ciBtYWluIGNvbXBvbmVudHM6DQoNCi0gICBUaGVtZSBlbGVtZW50cyBzcGVjaWZ5IHRoZSBub24tZGF0YSBlbGVtZW50cyB0aGF0IHdlIGNhbiBjb250cm9sLiBGb3IgZXhhbXBsZSwNCg0KICAgIC0gICBgcGxvdC50aXRsZWAgY29udHJvbHMgdGhlIGFwcGVhcmFuY2Ugb2YgdGhlIHBsb3QgdGl0bGU7DQogICAgLSAgIGBheGlzLnRpY2tzLnhgIGNvbnRyb2xzIHRoZSB0aWNrcyBvbiB0aGUgeC1heGlzOw0KICAgIC0gICBgbGVnZW5kLmtleS5oZWlnaHRgIGNvbnRyb2xzIHRoZSBoZWlnaHQgb2YgdGhlIGtleXMgaW4gdGhlIGxlZ2VuZC4NCg0KLSAgIEVhY2ggZWxlbWVudCBpcyBhc3NvY2lhdGVkIHdpdGggYW4gZWxlbWVudCBmdW5jdGlvbiwgd2hpY2ggZGVzY3JpYmVzIHRoZSB2aXN1YWwgcHJvcGVydGllcyBvZiB0aGUgZWxlbWVudC4gRm9yIGV4YW1wbGUsIGBlbGVtZW50X3RleHQoKWAgc2V0cyB0aGUgZm9udCBzaXplLCBjb2xvciwgYW5kIGZhY2Ugb2YgdGV4dCBlbGVtZW50cyBsaWtlIGBwbG90LnRpdGxlYC4NCg0KLSAgIFRoZSBgdGhlbWUoKWAgZnVuY3Rpb24gd2hpY2ggYWxsb3dzIHlvdSB0byBvdmVycmlkZSB0aGUgZGVmYXVsdCB0aGVtZSBlbGVtZW50cyBieSBjYWxsaW5nIGVsZW1lbnQgZnVuY3Rpb25zLCBsaWtlIGB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJyZWQiKSlgLg0KDQotICAgQ29tcGxldGUgdGhlbWVzLCBsaWtlIGB0aGVtZV9ncmV5KClgIHNldCBhbGwgb2YgdGhlIHRoZW1lIGVsZW1lbnRzIHRvIHZhbHVlcyBkZXNpZ25lZCB0byB3b3JrIHRvZ2V0aGVyIGhhcm1vbmlvdXNseS4NCg0KSGVyZSBhcmUgc29tZSBlc3BlY2lhbGx5IGNvbW1vbiB0aGVtZSgpIGFyZ3VtZW50cy4gWW91IHdpbGwgcmVjb2duaXplIHNvbWUgcGF0dGVybnMsIHN1Y2ggYXMgYXBwZW5kaW5nIC54IG9yIC55IHRvIGFwcGx5IHRoZSBjaGFuZ2Ugb25seSB0byBvbmUgYXhpcy4NCg0KPGJyPg0KDQo8Y2VudGVyPjxpbWcgc3JjPSJodHRwczovL2dpdGh1Yi5jb20vcGVuZ2RzY2kvc3RhNTUzL3Jhdy9tYWluL2dncGxvdC90aGVtZS5wbmciIGFsdD0ic2FtcGxlIGdncGxvdCB0aGVtZSIgaGVpZ2h0PSI0MDAiIHdpZHRoPSI2NTAiLz48L2NlbnRlcj4NCg0KPGJyPg0KDQpUbyBnZXQgdGhlIGNvbXBsZXRlIGxpc3Qgb2YgdGhlbWVzLCBydW4gdGhlIGZvbGxvd2luZyBjb2RlDQoNCmBgYHtyfQ0KI3RoZW1lX2dldCgpDQpgYGANCg0KVG8gbWFrZSBzdXJlIHRoZSBwbG90IGNhbiBzdGFuZCBhbG9uZSwgd2UgbmVlZCB0byBwcm92aWRlIHRoZSBwbG90IHdpdGggYXhlcywgbGVnZW5kIGxhYmVscywgYW5kIHRpdGxlLCBhbmQgdHdlYWsgdGhlIGNvbG9yIHNjYWxlIGZvciBhcHByb3ByaWF0ZSBjb2xvcnMuDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQojIGFkZGluZyB0aGVtZXMNCmdncGxvdChpcmlzLCBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gU3BlY2llcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gUGV0YWwuV2lkdGgpKSArDQogICAgICAgICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKw0KICAgICAgICAgICAgIGxhYnMoDQogICAgICAgICAgICAgICAgIHggPSAiU2VwYWwgTGVuZ3RoIiwNCiAgICAgICAgICAgICAgICAgeSA9ICJTZXBhbCBXaWR0aCIsDQogICAgICAgICAgICAgICAgICMgbGFiZWwgZm9yIGxlZ2VuZHMNCiAgICAgICAgICAgICAgICAgc2l6ZSA9ICJTZXBhbCBMZW5ndGg6IiwNCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiU3BlY2llczoiLA0KICAgICAgICAgICAgICAgICB0aXRsZSA9ICJBc3NvY2lhdGlvbiBiZXR3ZWVuIFNlcGFsIExlbmd0aCBhbmQgV2lkdGgiICkgKw0KICAgICAgICAgICAgIHRoZW1lX21pbmltYWwoKSAgKyAgICMgbWluaW1hbCB0aGVtZQ0KICAgICAgICAgICAgIHRoZW1lKCAjIGxpc3Qgb2YgdGhlbWVzIGFwcGxpZWQgdG8gdGhlIHBsb3QNCiAgICAgICAgICAgICAgICAgICAjIHBsb3QgdGl0bGUgZmVhdHVyZXMNCiAgICAgICAgICAgICAgICAgICAjIGZvbnQgZmFtaWx5OiBjKCJzYW5zIiwgInNlcmlmIiwgIm1vbm8iKSAgDQogICAgICAgICAgICAgICAgICAgIyBmb250IGZhY2U6IGMoInBsYWluIiwgImJvbGQiLCAiaXRhbGljIiwgImJvbGQuaXRhbGljIikNCiAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDEyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gInNhbnMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtyZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3QgPSAwLjUpLCAjIGxlZnQoMCkscmlnaHQoMSkNCiAgICAgICAgICAgICAgICAgICAjIExhYmVscyBvZiBheGVzIA0KICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJyZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gIml0YWxpYyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9ICJzZXJpZiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gMC41KSwNCiAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmx1ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2UgPSAiYm9sZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZqdXN0ID0gMC41KSwNCiAgICAgICAgICAgICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG9yID0gInJlZCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDAuNSksDQogICAgICAgICAgICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yID0gImRhcmtibHVlIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZXR5cGUgPSAic29saWQiKSwNCiAgICAgICAgICAgICAgICAgICAjIEF4aXMgdGljayBtYXJrcw0KICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhY2U9InBsYWluIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9InB1cnBsZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemU9MTEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFuZ2xlPTQ1KSwNCiAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChmYWNlPSJwbGFpbiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yPSJvcmFuZ2UiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplPTExLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmdsZT05MCksDQogICAgICAgICAgICAgICAgICAgIyBGZWF0dXJlcyBvZiBsZWdlbmQNCiAgICAgICAgICAgICAgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDAuMSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2dyZWVuIiksDQogICAgICAgICAgICAgICAgICAgbGVnZW5kLmp1c3RpZmljYXRpb24gPSBjKDAuOSwgMC44KSwNCiAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICAgICAgICAgICAgICAgICAjIyBQYW5lbCBncmlkDQogICAgICAgICAgICAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJsaWdodGJsdWUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAwLjEpLA0KICAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkNCiAgKQ0KYGBgDQoNCiMjIENvbXBsZXRlIENvbXBvbmVudHMgb2YgVGhlbWUNCg0KVGhlbWVzIGFyZSBhIHBvd2VyZnVsIHdheSB0byBjdXN0b21pemUgdGhlIGBub24tZGF0YSBjb21wb25lbnRzYCBvZiB0aGUgcGxvdHM6IGkuZS4gdGl0bGVzLCBsYWJlbHMsIGZvbnRzLCBiYWNrZ3JvdW5kLCBncmlkbGluZXMsIGFuZCBsZWdlbmRzLiBUbyBnaXZlIG91ciBwbG90cyBhIGNvbnNpc3RlbnQgY3VzdG9taXplZCBsb29rLCB3ZSBjYW4gZGVmaW5lIGEgdGhlbWUgZnVuY3Rpb24gYW5kIGNhbGwgdGhlIHRoZW1lIGZ1bmN0aW9uIGluIGFueSBgZ2dwbG90c2AuDQoNClRoZSBgdGlkeXZlcnNlYCBvZmZpY2lhbCB3ZWJzaXRlIHByb3ZpZGVzIGEgY29tcHJlaGVuc2l2ZSBkb2N1bWVudCBvbiB0aGVtZSBjb21wb25lbnRzIGluIFxgZ2dwbG90XGBcYC4gPGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS90aGVtZS5odG1sPi4gTnVtZXJvdXMgZXhhbXBsZXMgaGF2ZSBpbGx1c3RyYXRlZCBob3cgdG8gdXNlIHZhcmlvdXMgdGhlbWUgY29tcG9uZW50cy4NCg0KV2UgY2FuIGRlZmluZSBhIHRoZW1lIGZ1bmN0aW9uIHRoYXQgY2FuIGJlIHJldXNlZCB0byBjdXN0b21pemUgdGhlIHBsb3RzLiBGb3IgZXhhbXBsZSwgd2UgZGVmaW5lIHRoZSBmb2xsb3dpbmcgdGhlbWUgYW5kIHVzZSBpdCBpbiBkaWZmZXJlbnQgcGxvdHMuDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQpteXBsb3QudGhlbWUgPC0gZnVuY3Rpb24oKSB7DQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSAic2FucyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya3JlZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDAuNSksICMgbGVmdCgwKSxyaWdodCgxKQ0KICAgICMgYWRkIGJvcmRlciAxKQ0KICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiYmx1ZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gTkEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5ldHlwZSA9IDIpLA0KICAgICMgY29sb3IgYmFja2dyb3VuZCAyKQ0KICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJhbGljZWJsdWUiKSwNCiAgICAjIG1vZGlmeSBncmlkIDMpDQogICAgcGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJzdGVlbGJsdWUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZXR5cGUgPSAzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDAuNSksDQogICAgcGFuZWwuZ3JpZC5taW5vci54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQubWFqb3IueSA9ICBlbGVtZW50X2xpbmUoY29sb3VyID0gInN0ZWVsYmx1ZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZXR5cGUgPSAzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAwLjUpLA0KICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAjIG1vZGlmeSB0ZXh0LCBheGlzIGFuZCBjb2xvdXIgNCkgYW5kIDUpDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJzdGVlbGJsdWUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZSA9ICJpdGFsaWMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gIlRpbWVzIE5ldyBSb21hbiIpLA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3VyID0gInN0ZWVsYmx1ZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gIlRpbWVzIE5ldyBSb21hbiIpLA0KICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gInN0ZWVsYmx1ZSIpLA0KICAgICMgbGVnZW5kIGF0IHRoZSBib3R0b20gNikNCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNiwgJ2NtJyksICNjaGFuZ2UgbGVnZW5kIGtleSBzaXplDQogICAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDAuNiwgJ2NtJyksICNjaGFuZ2UgbGVnZW5kIGtleSBoZWlnaHQNCiAgICBsZWdlbmQua2V5LndpZHRoID0gdW5pdCgwLjYsICdjbScpLCAjY2hhbmdlIGxlZ2VuZCBrZXkgd2lkdGgNCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwgI2NoYW5nZSBsZWdlbmQgdGl0bGUgZm9udCBzaXplDQogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT04KSkgI2NoYW5nZSBsZWdlbmQgdGV4dCBmb250IHNpemUNCn0NCmBgYA0KDQpOb3cgd2UgdXNlIHRoZSBhYm92ZSB0aGVtZSBpbiB0aGUgZm9sbG93aW5nIHNjYXR0ZXIgcGxvdHMuIEluc3RlYWQgb2YgdXNpbmcgdGhlIGNvbG9ycyBiYXNlZCBvbiB0aGUgdmFsdWUgb2Ygc3BlY2llcywgd2UgbWFudWFsbHkgc2VsZWN0IGNvbG9ycyB0byBlbmNvZGUgdGhlIHZhbHVlcyBvZiBzcGVjaWVzLiBUaGUgZm9sbG93aW5nIFVSTCBsaW5rcyB0byBhIFBERiBkb2N1bWVudCB3aXRoIGNvbG9ycyBpbiBSLiA8aHR0cDovL3d3dy5zdGF0LmNvbHVtYmlhLmVkdS9+dHpoZW5nL2ZpbGVzL1Jjb2xvci5wZGY+DQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQojIENoYW5nZSBoaXN0b2dyYW0gcGxvdCBsaW5lIGNvbG9ycyBieSBncm91cHMNCmdncGxvdChpcmlzLCBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gZmFjdG9yKFNwZWNpZXMpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSBQZXRhbC5XaWR0aCkpICsNCiAgICAgICAgICAgICBnZW9tX3BvaW50KGFscGhhID0gMC41KSArDQogICAgICAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJkb2RnZXJibHVlNCIsICJkYXJrb2xpdmVncmVlbjQiLCJkYXJrb3JjaGlkMyIpKSArDQogICAgICAgICAgICAgbGFicygNCiAgICAgICAgICAgICAgICAgeCA9ICJTZXBhbCBMZW5ndGgiLA0KICAgICAgICAgICAgICAgICB5ID0gIlNlcGFsIFdpZHRoIiwNCiAgICAgICAgICAgICAgICAgIyMgQ29sb3IgYW5kIHNpemUgb2YgbGFiZWxzDQogICAgICAgICAgICAgICAgIHNpemUgPSAiU2VwYWwgTGVuZ3RoOiIsDQogICAgICAgICAgICAgICAgIGNvbG9yID0gIlNwZWNpZXM6IiwNCiAgICAgICAgICAgICAgICAgdGl0bGUgPSAiQXNzb2NpYXRpb24gYmV0d2VlbiBTZXBhbCBMZW5ndGggYW5kIFdpZHRoIikgKw0KICAgICAgICAgICAgIG15cGxvdC50aGVtZSgpDQpgYGANCg0KTmV4dCwgd2UgcGxvdCBhIGhpc3RvZ3JhbSB1c2luZyB0aGUgc2FtZSB0aGVtZS4NCg0KYGBge3IsIGZpZy5hbGlnbj0nY2VudGVyJ30NCmdncGxvdChpcmlzLCBhZXMoeCA9IFBldGFsLldpZHRoLCBjb2xvcj1TcGVjaWVzKSkgKw0KICAgICAgICAgIGdlb21faGlzdG9ncmFtKGZpbGw9Im5hdnkiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuMywgDQogICAgICAgICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSAiaWRlbnRpdHkiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBiaW53aWR0aCA9IDAuMikgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImRvZGdlcmJsdWU0IiwgImRhcmtvbGl2ZWdyZWVuNCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZGFya29yY2hpZDMiKSkgKw0KICAgICAgICAgICAgICAgbGFicygNCiAgICAgICAgICAgICAgICAgeCA9ICJQZXRhbCBXaWR0aCIsDQogICAgICAgICAgICAgICAgIGNvbG9yID0gIlNwZWNpZXM6IiwNCiAgICAgICAgICAgICAgICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFBldGFsIFdpZHRoIikgKw0KICAgICAgICAgICBteXBsb3QudGhlbWUoKQ0KYGBgDQoNCiMgQWRkaW5nIEFubm90YXRpb25zIHRvIEdyYXBoaWNzDQoNClRvIG1ha2UgdGhlIGdyYXBoaWMgbW9yZSBpbmZvcm1hdGl2ZSwgc29tZXRpbWVzIHdlIG1heSB3YW50IHRvIGFkZCBhbm5vdGF0aW9ucyB0byB0aGUgZ3JhcGhpYy4gSWYgd2UgY3JlYXRlIGEgc3RhdGlzdGljYWwgYW5kIHByb2JhYmlsaXN0aWMgZ3JhcGhpYywgb2NjYXNpb25hbGx5IHdlIG5lZWQgdG8gYWRkIG1hdGhlbWF0aWNhbCBlcXVhdGlvbnMgd2l0aCBHcmVlayBsZXR0ZXJzIHRvIHRoZSBncmFwaGljcy4NCg0KIyMgQWRkaW5nIFBsYWluIFRleHQgdG8gR3JhcGhpY3MNCg0KVG8gYWRkIHBsYWluIHRleHQgdG8gZ3JhcGhpY3MgaW4gZ2dwbG90LCB3ZSB1c2UgdGhlIGZ1bmN0aW9uIGBhbm5vdGF0ZSgpYCB3aXRoIGdpdmVuIGNvb3JkaW5hdGVzLiBGb3IgZXhhbXBsZSwgdGhlIHNjYXR0ZXIgcGxvdCBzaG93cyB0d28gc2VwYXJhdGUgZ3JvdXBzLg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KIyBDaGFuZ2UgaGlzdG9ncmFtIHBsb3QgbGluZSBjb2xvcnMgYnkgZ3JvdXBzDQpnZ3Bsb3QoaXJpcywgYWVzKHggPSBTZXBhbC5MZW5ndGgsIHkgPSBTZXBhbC5XaWR0aCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGZhY3RvcihTcGVjaWVzKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gUGV0YWwuV2lkdGgpKSArDQogICAgICAgICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKw0KICAgICAgICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiZG9kZ2VyYmx1ZTQiLCAiZGFya29saXZlZ3JlZW40IiwgImRhcmtvcmNoaWQzIikpICsNCiAgICAgICAgICAgICBsYWJzKA0KICAgICAgICAgICAgICAgICB4ID0gIlNlcGFsIExlbmd0aCIsDQogICAgICAgICAgICAgICAgIHkgPSAiU2VwYWwgV2lkdGgiLA0KICAgICAgICAgICAgICAgICAjIyBjb2xvciBhbmQgc2l6ZSBvZiBsYWJlbHMNCiAgICAgICAgICAgICAgICAgc2l6ZSA9ICJTZXBhbCBMZW5ndGg6IiwNCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiU3BlY2llczoiLA0KICAgICAgICAgICAgICAgICB0aXRsZSA9ICJBc3NvY2lhdGlvbiBiZXR3ZWVuIFNlcGFsIExlbmd0aCBhbmQgV2lkdGgiKSArDQogICAgICAgICAgICAgbXlwbG90LnRoZW1lKCkgKyANCiAgICAgICAgICAgICBhbm5vdGF0ZShnZW9tPSJ0ZXh0IiwgDQogICAgICAgICAgICAgICAgICAgICAgeD03LCANCiAgICAgICAgICAgICAgICAgICAgICB5PTQuMSwgDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWw9cGFzdGUoIlRoZSBkaXN0cmlidXRpb24gb2YgU2V0b3NhIGlzIGRpZmZlcmVudCIsIA0KICAgICAgICAgICAgICAgICAgICAgICJmcm9tIHRoYXQgb2YgVmVyc2ljb2xvciBhbmQgVmlnaW5pY2EiLCBzZXAgPSAiXG4iKSwNCiAgICAgICAgICAgICAgICAgICAgICBjb2xvcj0icmVkIiwNCiAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDAuNSkNCmBgYA0KDQpTZXZlcmFsIG90aGVyIGFsdGVybmF0aXZlcyB3ZSBjYW4gdXNlIHRvIGFkZCB0ZXh0IHRvIGdyYXBoaWNzIGNyZWF0ZWQgdXNpbmcgXGBnZ3Bsb3RcYFxgLg0KDQojIyBQYXNzaW5nIFBhcmFtZXRlcnMgaW4gQW5ub3RhdGlvbg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KIyBDaGFuZ2UgaGlzdG9ncmFtIHBsb3QgbGluZSBjb2xvcnMgYnkgZ3JvdXBzDQpnZ3Bsb3QoaXJpcywgYWVzKHggPSBTZXBhbC5MZW5ndGgsIHkgPSBTZXBhbC5XaWR0aCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGZhY3RvcihTcGVjaWVzKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gUGV0YWwuV2lkdGgpKSArDQogICAgICAgICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKw0KICAgICAgICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiZG9kZ2VyYmx1ZTQiLCAiZGFya29saXZlZ3JlZW40IiwgImRhcmtvcmNoaWQzIikpICsNCiAgICAgICAgICAgICBsYWJzKA0KICAgICAgICAgICAgICAgICB4ID0gIlNlcGFsIExlbmd0aCIsDQogICAgICAgICAgICAgICAgIHkgPSAiU2VwYWwgV2lkdGgiLA0KICAgICAgICAgICAgICAgICAjIyBDb2xvciBhbmQgc2l6ZSBvZiBsYWJlbHMNCiAgICAgICAgICAgICAgICAgc2l6ZSA9ICJTZXBhbCBMZW5ndGg6IiwNCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiU3BlY2llczoiLA0KICAgICAgICAgICAgICAgICB0aXRsZSA9ICJBc3NvY2lhdGlvbiBiZXR3ZWVuIFNlcGFsIExlbmd0aCBhbmQgV2lkdGgiKSArDQogICAgICAgICAgICAgbXlwbG90LnRoZW1lKCkgKyANCiAgICAgICAgICAgICAgYW5ub3RhdGUoZ2VvbT0idGV4dCIgLCANCiAgICAgICAgICAgICAgICAgICAgICAgeD03LCANCiAgICAgICAgICAgICAgICAgICAgICAgeT00LjQsDQogICAgICAgICAgICAgICAgICAgICAgIGxhYmVsPXBhc3RlKCJUaGUgUGVhcnNvbiBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCByID0gIiwgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChjb3IoaXJpcyRTZXBhbC5MZW5ndGgsIGlyaXMkU2VwYWwuV2lkdGgpLDMpKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsdWUiKQ0KYGBgDQoNClRoZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBiZXR3ZWVuIHNlcGFsIHdpZHRoIGFuZCBzZXBhbCBsZW5ndGggaXMgY2FsY3VsYXRlZCBkaXJlY3RseSBmcm9tIHRoZSBkYXRhIGFuZCBwYXNzZWQgdG8gdGhlIGFubm90YXRpb24gaW4gdGhlIGdyYXBoaWMuIE5vdGUgdGhhdCB3ZSB1c2VkIGEgdmVyeSBoYW5keSBhbmQgaW1wb3J0YW50IGdyYXBoaWMgZnVuY3Rpb24gYHBhc3RlKClgIHdoZW4gYWRkaW5nIHRoZSBhbm5vdGF0aW9uLg0KDQojIyBBZGRpbmcgTWF0aGVtYXRpY2FsIEVxdWF0aW9ucyB0byBHcmFwaGljcw0KDQpNYXRoZW1hdGljYWwgZXhwcmVzc2lvbnMgbWFkZSB3aXRoIHRoZSB0ZXh0IGBnZW9tc2AgdXNpbmcgYHBhcnNlID0gVFJVRWAgaW4gYGdncGxvdDJgIGhhdmUgYSBmb3JtYXQgc2ltaWxhciB0byB0aG9zZSBtYWRlIHdpdGggYHBsb3RtYXRoKClgIGFuZCBgZXhwcmVzc2lvbigpYCBpbiBiYXNlIFIsIGV4Y2VwdCB0aGF0IHRoZXkgYXJlIHN0b3JlZCBhcyBzdHJpbmdzLCByYXRoZXIgdGhhbiBhcyBleHByZXNzaW9uIG9iamVjdHMuDQoNClRvIG1peCByZWd1bGFyIHRleHQgd2l0aCBleHByZXNzaW9ucywgdXNlIHNpbmdsZSBxdW90ZXMgd2l0aGluIGRvdWJsZSBxdW90ZXMgKG9yIHZpY2UgdmVyc2EpIHRvIG1hcmsgdGhlIHBsYWluLXRleHQgcGFydHMuIEVhY2ggYmxvY2sgb2YgdGV4dCBlbmNsb3NlZCBieSB0aGUgaW5uZXIgcXVvdGVzIGlzIHRyZWF0ZWQgYXMgYSB2YXJpYWJsZSBpbiBhIG1hdGhlbWF0aWNhbCBleHByZXNzaW9uLg0KDQpCZWFyIGluIG1pbmQgdGhhdCwgaW4gUidzIHN5bnRheCBmb3IgbWF0aGVtYXRpY2FsIGV4cHJlc3Npb25zLCB3ZSAqKmNhbid0Kiogc2ltcGx5IHB1dCBhIHZhcmlhYmxlIHJpZ2h0IG5leHQgdG8gYW5vdGhlciB3aXRob3V0IHNvbWV0aGluZyBlbHNlIGluIGJldHdlZW4uIFRvIGRpc3BsYXkgdHdvIHZhcmlhYmxlcyBuZXh0IHRvIGVhY2ggb3RoZXIsIHB1dCBhIGAqYCBvcGVyYXRvciBiZXR3ZWVuIHRoZW0uIHdoZW4gYCpgIGlzIGRpc3BsYXllZCBpbiBhIGdyYXBoaWMsIGl0IGlzIHRyZWF0ZWQgYXMgYW4gaW52aXNpYmxlIG11bHRpcGxpY2F0aW9uIHNpZ24gKGZvciBhIHZpc2libGUgbXVsdGlwbGljYXRpb24gc2lnbiwgdXNlIGAlKiVgKToNCg0KYGBge3IsIGZpZy5hbGlnbj0nY2VudGVyJ30NCnguYXhpcyA8LSBzZXEoMCwgMjAsIGxlbmd0aC5vdXQgPSAxMDApDQp5LmF4aXMgPC0gKDEvc3FydCgyKnBpKSozKSpleHAoLSh4LmF4aXMtMTApXjIvKDIqOSkpDQpub3JtYWwuZGF0YSA9IGRhdGEuZnJhbWUoeD14LmF4aXMgLCB5PXkuYXhpcykNCiMjDQpnZ3Bsb3Qobm9ybWFsLmRhdGEsIGFlcyh4ID0geC5heGlzLCB5ID0geS5heGlzKSkgKyANCiAgICAgZ2VvbV9saW5lKGNvbG9yID0gImJsdWUiKSArDQogICAgIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLCAxLjI1KSwgeGxpbT1jKDAsMjApKSArIA0KICAgICBsYWJzKA0KICAgICAgICAgICAgICAgICB4ID0gIk5vcm1hbCBTY29yZSIsDQogICAgICAgICAgICAgICAgIHkgPSAiTm9ybWFsIERlbnNpdHkiLA0KICAgICAgICAgICAgICAgICB0aXRsZSA9ICJOb3JtYWwgRGVuc2l0eSBDdXJ2ZSIpICsNCiAgICAgIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDEwLCB5ID0gMC4yLCANCiAgICAgICAgICAgICAgIHBhcnNlID0gVFJVRSwgc2l6ZSA9IDQsDQogICAgICAgICAgICAgIGxhYmVsID0gIidGdW5jdGlvbjogICcgKiB5PT1mcmFjKDEsIHNxcnQoMipwaSkqIHNpZ21hKSAlKiUgZV57LSh4LSBtdSleMi8yfSIsDQogICAgICAgICAgICAgIGNvbG9yID0gInJlZCIpDQpgYGANCg0KIyMgQWRkaW5nIEltYWdlcyB0byBFeGlzdGluZyBnZ1Bsb3RzDQoNClRvIGVtYmVkIGEgUE5HIGltYWdlIHRvIGFuIGV4aXN0aW5nIGdyYXBoIGNyZWF0ZWQgYnkgYGdncGxvdGAsIHdlIG5lZWQgdG8gdXNlIGByZWFkUE5HKClgIGluIGxpYnJhcnkgKipwbmcqKiB0byBsb2FkIHRoZSBpbWFnZSB0byBSIGFuZCBgZ2V0VVJMY29udGVudCgpYCBpbiB0aGUgKipSQ3VybCoqIHRvIGluc2VydCB0aGUgaW1hZ2UgdG8gdGhlIGdyYXBoLg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KIyBjYXR1cmwgPC0gImh0dHBzOi8vc3RhdDU1My5zMy5hbWF6b25hd3MuY29tL2dncGxvdC9jYXQucG5nIg0KY2F0dXJsIDwtICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcGVuZ2RzY2kvc3RhNTUzL21haW4vZ2dwbG90L2NhdC5wbmciDQpteV9jYXQgPC0gIHJlYWRQTkcoZ2V0VVJMQ29udGVudChjYXR1cmwpKQ0KcmFzdGVyLmNhdCA8LSBhcy5yYXN0ZXIobXlfY2F0KSANCiMgQ2hhbmdlIGhpc3RvZ3JhbSBwbG90IGxpbmUgY29sb3JzIGJ5IGdyb3Vwcw0KZ2dwbG90KGlyaXMsIGFlcyh4ID0gU2VwYWwuTGVuZ3RoLCB5ID0gU2VwYWwuV2lkdGgsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBmYWN0b3IoU3BlY2llcyksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IFBldGFsLldpZHRoKSkgKw0KICAgICAgICAgICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpICsNCiAgICAgICAgICAgICBsYWJzKA0KICAgICAgICAgICAgICAgICB4ID0gIlNlcGFsIExlbmd0aCIsDQogICAgICAgICAgICAgICAgIHkgPSAiU2VwYWwgV2lkdGgiLA0KICAgICAgICAgICAgICAgICAjIyBDb2xvciBhbmQgc2l6ZSBvZiBsYWJlbHMNCiAgICAgICAgICAgICAgICAgc2l6ZSA9ICJTZXBhbCBMZW5ndGg6IiwNCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiU3BlY2llczoiLA0KICAgICAgICAgICAgICAgICB0aXRsZSA9ICJBc3NvY2lhdGlvbiBiZXR3ZWVuIFNlcGFsIExlbmd0aCBhbmQgV2lkdGgiKSArDQogICAgICAgICAgICAgbXlwbG90LnRoZW1lKCkgKyANCiAgICAgICAgICAgICAgYW5ub3RhdGlvbl9yYXN0ZXIocmFzdGVyLmNhdCwgNCwgNC44NSwgMy42NSwgNC41KQ0KYGBgDQoNCiMgUmVtb3ZpbmcgQ2hhcnQgSnVua3MgVmlhIFRoZW1lcw0KDQpXZSByZW1vdmUgc29tZSB1bm5lY2Vzc2FyeSBtYXJrcyBhbmQgY2hhbm5lbHMgZnJvbSB0aGUgY2hhcnQgdmlhIHRoZW1lOiBjaGFuZ2UgdGhlIGJhY2tncm91bmQgY29sb3IsIGdyaWQsIGFuZCBwbG90IHRpdGxlLiBFc3NlbnRpYWxseSwgd2UgdXNlIGEgdGhlbSB0byBsYXkgb3V0IGEgZ2dwbG90IHN0eWxlIHdpdGhvdXQgYW55IGNoYXJ0IGp1bmsuDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQpteXBsb3QudGhlbWVfbmV3IDwtIGZ1bmN0aW9uKCkgew0KICB0aGVtZSgNCiAgICAjZ2dwbG90IG1hcmdpbnMNCiAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4odCA9IDUwLCAgIyBUb3AgbWFyZ2luDQogICAgICAgICAgICAgICAgICAgICAgICAgIHIgPSAzMCwgICMgUmlnaHQgbWFyZ2luDQogICAgICAgICAgICAgICAgICAgICAgICAgIGIgPSAzMCwgICMgQm90dG9tIG1hcmdpbg0KICAgICAgICAgICAgICAgICAgICAgICAgICBsID0gMzApLCAjIExlZnQgbWFyZ2luDQogICAgIyMgZ2dwbG90IHRpdGxlcw0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSAic2FucyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAibmF2eSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDAuNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcmdpbj1tYXJnaW4oMCwwLDMwLDApKSwgIyBsZWZ0KDApLHJpZ2h0KDEpDQogICAgIyBhZGQgYm9yZGVyIDEpDQogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9IE5BLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IE5BLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZXR5cGUgPSAyKSwNCiAgICAjIGNvbG9yIGJhY2tncm91bmQgMikNCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiI2Y2ZjZmNiIpLA0KICAgICMgbW9kaWZ5IGdyaWQgMykNCiAgICBwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ3doaXRlJywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gMywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAwLjUpLA0KICAgIHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSAgZWxlbWVudF9saW5lKGNvbG91ciA9ICd3aGl0ZScsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZXR5cGUgPSAzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAwLjUpLA0KICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAjIG1vZGlmeSB0ZXh0LCBheGlzIGFuZCBjb2xvdXIgNCkgYW5kIDUpDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJuYXZ5IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICNmYWNlID0gIml0YWxpYyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gNywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI2ZhbWlseSA9ICJUaW1lcyBOZXcgUm9tYW4iDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICksDQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAibmF2eSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjZmFtaWx5ID0gIlRpbWVzIE5ldyBSb21hbiINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksDQogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAibmF2eSIpLA0KICAgICMgbGVnZW5kIGF0IHRoZSBib3R0b20gNikNCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNiwgJ2NtJyksICNjaGFuZ2UgbGVnZW5kIGtleSBzaXplDQogICAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDAuNiwgJ2NtJyksICNjaGFuZ2UgbGVnZW5kIGtleSBoZWlnaHQNCiAgICBsZWdlbmQua2V5LndpZHRoID0gdW5pdCgwLjYsICdjbScpLCAjY2hhbmdlIGxlZ2VuZCBrZXkgd2lkdGgNCiAgICAjbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksICNjaGFuZ2UgbGVnZW5kIHRpdGxlIGZvbnQgc2l6ZQ0KICAgIGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCksICAjIHJlbW92ZSBhbGwgbGVnZW5kIHRpdGxlcw0KICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpLA0KICAgICMjIyMjDQogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT04KSkgI2NoYW5nZSBsZWdlbmQgdGV4dCBmb250IHNpemUNCn0NCmBgYA0KDQpVc2luZyB0aGUgYWJvdmUgdGhlbWUsIHdlIHJlLXBsb3QgdGhlIGlyaXMgZGF0YSB3aXRoIGxlc3MgaXJyZWxldmFudCBncmFwaGljYWwgZWxlbWVudHMuDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTV9DQojIENoYW5nZSBoaXN0b2dyYW0gcGxvdCBsaW5lIGNvbG9ycyBieSBncm91cHMNCmdncGxvdChpcmlzLCBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBmYWN0b3IoU3BlY2llcykpLCBsaW5ldHlwZSA9IFNwZWNpZXMpICsNCiAgICAgICAgICAgICBnZW9tX3BvaW50KHNpemUgPSAyLCBhbHBoYSA9IDAuNykgKw0KICAgICAgICAgICAgIHN0YXRfc21vb3RoKG1ldGhvZCA9IGxtLCBzZT1GQUxTRSwgc2l6ZSA9IDAuMykgKw0KICAgICAgICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiZG9kZ2VyYmx1ZTQiLCAiZGFya29saXZlZ3JlZW40IiwgImRhcmtvcmNoaWQzIikpICsNCiAgICAgICAgICAgICBsYWJzKA0KICAgICAgICAgICAgICAgICB4ID0gIlNlcGFsIExlbmd0aCIsDQogICAgICAgICAgICAgICAgIHkgPSAiU2VwYWwgV2lkdGgiLA0KICAgICAgICAgICAgICAgICAjIyBsYWJlbHMgb2YgY29sb3IgYW5kIHNpemUNCiAgICAgICAgICAgICAgICAgI3NpemUgPSAiU2VwYWwgTGVuZ3RoIiwNCiAgICAgICAgICAgICAgICAgI2NvbG9yID0gTkEsDQogICAgICAgICAgICAgICAgIHRpdGxlID0gIkFzc29jaWF0aW9uIGJldHdlZW4gU2VwYWwgTGVuZ3RoIGFuZCBXaWR0aCIpICsNCiAgICAgICAgICAgICBteXBsb3QudGhlbWVfbmV3KCkgKyANCiAgICAgICAgICAgICAgYW5ub3RhdGUoZ2VvbT0idGV4dCIgLCANCiAgICAgICAgICAgICAgICAgICAgICAgeD02LjgsIA0KICAgICAgICAgICAgICAgICAgICAgICB5PTIsDQogICAgICAgICAgICAgICAgICAgICAgIGxhYmVsPXBhc3RlKCJUaGUgUGVhcnNvbiBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCByID0gIiwgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChjb3IoaXJpcyRTZXBhbC5MZW5ndGgsIGlyaXMkU2VwYWwuV2lkdGgpLDMpKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJuYXZ5IikgKyANCiAgICAgICAgICAgICAgIGNvb3JkX2ZpeGVkKDEpICAgICMjIFRoaXMgY2hhbmdlcyB0aGUgYXNwZWN0IHJhdGlvIG9mIHRoZSBncmFwaA0KYGBgDQoNCiMgQ29tbW9uIEV4dGVuc2lvbnMgdG8gYGdncGxvdGANCg0KVGhlcmUgYXJlIGRpZmZlcmVudCBleHRlbnNpb25zIG9mIGBnZ3Bsb3RgLiBUaGlzIHNlY3Rpb24gaW50cm9kdWNlcyB0d28gY29tbW9ubHkgdXNlZCBleHRlbnNpb25zLiBXZSB3aWxsIGFsc28gYnJpZWZseSBvdXRsaW5lIGEgZmV3IG90aGVyIGV4dGVuc2lvbnMgb2YgYGdncGxvdGAuDQoNCiMjIEFtaW5hdGVkIEdyYXBoIHdpdGggYGdnYW5pbWF0ZSgpYA0KDQpgZ2dhbmltYXRlKClgIGV4dGVuZHMgdGhlIGdyYW1tYXIgb2YgZ3JhcGhpY3MgYXMgaW1wbGVtZW50ZWQgYnkgYGdncGxvdDJgIHRvIGluY2x1ZGUgdGhlIGRlc2NyaXB0aW9uIG9mIGFuaW1hdGlvbi4gSXQgZG9lcyB0aGlzIGJ5IHByb3ZpZGluZyBhIHJhbmdlIG9mIG5ldyBncmFtbWFyIGNsYXNzZXMgdGhhdCBjYW4gYmUgYWRkZWQgdG8gdGhlIHBsb3Qgb2JqZWN0IGluIG9yZGVyIHRvIGN1c3RvbWl6ZSBob3cgaXQgc2hvdWxkIGNoYW5nZSB3aXRoIHRpbWUuDQoNCi0gICBgdHJhbnNpdGlvbl8qKClgIGRlZmluZXMgaG93IHRoZSBkYXRhIHNob3VsZCBiZSBzcHJlYWQgb3V0IGFuZCBob3cgaXQgcmVsYXRlcyB0byBpdHNlbGYgYWNyb3NzIHRpbWUuDQoNCi0gICBgdmlld18qKClgIGRlZmluZXMgaG93IHRoZSBwb3NpdGlvbmFsIHNjYWxlcyBzaG91bGQgY2hhbmdlIGFsb25nIHdpdGggdGhlIGFuaW1hdGlvbi4NCg0KLSAgIGBzaGFkb3dfKigpYCBkZWZpbmVzIGhvdyBkYXRhIGZyb20gb3RoZXIgcG9pbnRzIGluIHRpbWUgc2hvdWxkIGJlIHByZXNlbnRlZCBpbiB0aGUgZ2l2ZW4gcG9pbnQgaW4gdGltZS4NCg0KLSAgIGBlbnRlcl8qKCkvZXhpdF8qKClgIGRlZmluZXMgaG93IG5ldyBkYXRhIHNob3VsZCBhcHBlYXIgYW5kIGhvdyBvbGQgZGF0YSBzaG91bGQgZGlzYXBwZWFyIGR1cmluZyB0aGUgY291cnNlIG9mIHRoZSBhbmltYXRpb24uDQoNCi0gICBgZWFzZV9hZXMoKWAgZGVmaW5lcyBob3cgZGlmZmVyZW50IGFlc3RoZXRpY3Mgc2hvdWxkIGJlIGVhc2VkIGR1cmluZyB0cmFuc2l0aW9ucy4NCg0KVGhlIGxvZ2ljIGJlaGluZCB0aGUgYGdnYW5pbWF0ZWAgaXMgdG8gY3JlYXRlIGEgc2VxdWVuY2Ugb2YgaW1hZ2VzIGFuZCB0aGVuIG1ha2UgYSBnaWYgaW1hZ2UuIFdlIG5lZWQgdG8gd3JpdGUgSFRNTCB0byBpbmNsdWRlIHRoaXMgZ2lmIGluIHRoZSBSTWFya2Rvd24gZG9jdW1lbnQuDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQpsaWJyYXJ5KGdhcG1pbmRlcikNCg0KcCA8LSBnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeCA9IGdkcFBlcmNhcCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICB5PWxpZmVFeHAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IHBvcCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvdXIgPSBjb3VudHJ5KSkgKw0KICAgICAgICBnZW9tX3BvaW50KGFlcyhzaXplID0gcG9wLCBpZHMgPSBjb3VudHJ5ICksDQogICAgICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGQUxTRSwgDQogICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjcpICsNCiAgICAgICAgc2NhbGVfY29sb3JfdmlyaWRpc19kKCkgKyAgICAgICMgY29sb3IgcGFsbGV0cyANCiAgICAgICAgc2NhbGVfc2l6ZShyYW5nZSA9IGMoMiwgMTIpKSArDQogICAgICAgIHNjYWxlX3hfbG9nMTAoKSArDQogICAgICAgIGxhYnMoeCA9ICJHRFAgcGVyIGNhcGl0YSIsIA0KICAgICAgICAgICAgIHkgPSAiTGlmZSBleHBlY3RhbmN5IikgKw0KICAgICAgICAjIyBnZ2FuaW1hdGUgY29tbWFuZA0KICAgICAgIHRyYW5zaXRpb25fdGltZSh5ZWFyKQ0KIyMgDQphbmltX3NhdmUoIkxpZmVFeHAuZ2lmIiwgcCkNCiMgIGFuaW1hdGUocCwgcmVuZGVyZXIgPSBnaWZza2lfcmVuZGVyZXIoKSkgICMgVGhpcyBjb21tYW5kIHdpbGwgcG9wIHVwIGEgbmV3IGdyYXBoaWMgd2luZG93IHNob3dpbmcgdGhlIGFuaW1hdGlvbi4NCmBgYA0KDQo8YnI+DQoNCjxjZW50ZXI+PGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9wZW5nZHNjaS9zdGE1NTMvcmF3L21haW4vZ2dwbG90L0xpZmVFeHAuZ2lmIiBhbHQ9IkxpZmVFeHBlY3RhbmN5IEFuaW1hdGlvbiIgaGVpZ2h0PSI0NTAiIHdpZHRoPSI0MDAiLz48L2NlbnRlcj4NCg0KPGJyPg0KDQpTaW5jZSB0aGUgZ2lmIGltYWdlIGlzIG1hZGUgb2YgaW5kaXZpZHVhbCBzdGF0aWMgaW1hZ2VzLCBpdCBpcyBkaWZmZXJlbnQgZnJvbSB0aGUgaW50ZXJhY3RpdmUgcGxvdCBwcmVzZW50ZWQgaW4gdGhlIHByZXZpb3VzIHNlY3Rpb25zIHRoYXQgaGF2ZSB0aGUgY2FwYWJpbGl0eSBvZiBzaG93aW5nIG1vZGUgaW5mb3JtYXRpb24gb2YgdGhlIGRhdGEgdmlhIGhvdmVyIG1lc3NhZ2UuDQoNClRoZSBuZXh0IGdpZiBncmFwaCBjb25zaXN0cyBvZiA1IHBhbmVscywgZWFjaCByZXByZXNlbnRpbmcgYSBjb250aW5lbnQuIFRoZXkgYXJlIGFsc28gZmlnIGltYWdlcy4gVGhlcmVmb3JlLCBubyBob3ZlciBtZXNzYWdlIGlzIGF2YWlsYWJsZSBmb3IgdGhlc2UgZ2lmIGZpZ3VyZXMuDQoNCldlIHVzZSB0aGUge2dpZmtpfSBwYWNrYWdlIHRvIHJlbmRlciB0aGUgaW1hZ2VzIGluIHRoZSBmb3JtIG9mIGdpZiBhbmQgdGhlbiBpbmNsdWRlIHRoZSBnaWYgaW1hZ2UgaW50byB0aGUgUk1hcmtkb3duIGRvY3VtZW50IGRpcmVjdGx5Lg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KdyA8LSBnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoZ2RwUGVyY2FwLCBsaWZlRXhwLCANCiAgICAgICAgICAgICAgICAgIHNpemUgPSBwb3AsIGNvbG91ciA9IGNvdW50cnkpKSArDQogICAgICAgICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjcsIHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgICAgICAgICAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjb3VudHJ5X2NvbG9ycykgKw0KICAgICAgICAgICAjc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJkb2RnZXJibHVlNCIsICJkYXJrb2xpdmVncmVlbjQiLCJkYXJrb3JjaGlkMyIpKSArDQogICAgICAgICAgICNzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iU2V0MSIpICsNCiAgICAgICAgICAgc2NhbGVfc2l6ZShyYW5nZSA9IGMoMiwgMTIpKSArDQogICAgICAgICAgIHNjYWxlX3hfbG9nMTAoKSArDQogICAgICAgICAgICMgYnJlYWsgZG93biB0aGUgcHJldmlvdXMgc2luZ2xlIHBsb3QgYnkgY29udGluZW50IA0KICAgICAgICAgICAjIGZhY2V0X3dyYXAofmNvbnRpbmVudCkgKyAgICAgIyBjcmVhdGUgbXVsdGlwbGUgcGFuZWxzIGFjY29yZGluZyB0byB0aGUgY29udGluZW50cw0KICAgICAgICAgICAjIEhlcmUgY29tZXMgdGhlIGdnYW5pbWF0ZSBzcGVjaWZpYyBiaXRzDQogICAgICAgICAgIGxhYnModGl0bGUgPSAnWWVhcjoge2ZyYW1lX3RpbWV9JywgDQogICAgICAgICAgICAgICAgICAgIHggPSAnR0RQIHBlciBjYXBpdGEnLA0KICAgICAgICAgICAgICAgICAgICB5ID0gJ2xpZmUgZXhwZWN0YW5jeScpICsNCiAgICAgICAgICAgdHJhbnNpdGlvbl90aW1lKHllYXIpICsNCiAgICAgICAgICAgZWFzZV9hZXMoJ2xpbmVhcicpDQojIyMNCmFuaW1hdGUodywgcmVuZGVyZXIgPSBnaWZza2lfcmVuZGVyZXIoKSwNCiAgICAgICAgICByZXdpbmQgPSBUUlVFKQ0KYGBgDQoNClRoZSBhYm92ZSBjb2RlIGRvZXMgbm90IHNhdmUgdGhlIGdlbmVyYXRlZCBnaWYgaW1hZ2UgdG8gdGhlIGRvY3VtZW50IGZvbGRlciAoZGlyZWN0b3J5KS4gSWYgbmVlZCB0byBzYXZlIGl0IGZyb20gdGhlIHZpZXdlciB3aW5kb3cgdG8gdGhlIGRlc2lnbmF0ZWQgZm9sZGVyIGFuZCB0aGVuIGVtYmVkIGl0IHRvIGEgd2ViIHBhZ2UgY3JlYXRlZCBieSB0b29scyBvdGhlciB0aGFuIHRoZSBSTWFya2Rvd24uDQoNCmBgYCAgICAgICAgIA0KPGJyPg0KPGNlbnRlcj48aW1nIHNyYz0iaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Blbmdkc2NpL3N0YTU1My9tYWluL2dncGxvdC9MaWZlRXhwUmV3aW5kLmdpZiIgYWx0PSJMaWZlIEV4cGVjdGFuY3kgQW5pbWF0aW9uIFJld2luZCIgaGVpZ2h0PSI1MDAiIHdpZHRoPSI0MDAiPjwvY2VudGVyPg0KPGJyPg0KYGBgDQoNCk5leHQsIHdlIGNyZWF0ZSBhIGdyb3VwIGdpZiB1c2luZyBmYWNldF93cmFwKCkgZnVuY3Rpb24uIFRoZSBjb2RlIGlzIHRoZSBzYW1lIGFzIHRoZSBhYm92ZSBleGFtcGxlIGV4Y2VwdCBmb3Igb25lIGFkZGl0aW9uYWwgZnVuY3Rpb24gY2FsbC4NCg0KYGBge3IsIGZpZy5hbGlnbj0nY2VudGVyJ30NCncgPC0gZ2dwbG90KGdhcG1pbmRlciwgYWVzKGdkcFBlcmNhcCwgbGlmZUV4cCwgDQogICAgICAgICAgICAgICAgICBzaXplID0gcG9wLCBjb2xvdXIgPSBjb3VudHJ5KSkgKw0KICAgICAgICAgICBnZW9tX3BvaW50KGFscGhhID0gMC43LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogICAgICAgICAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gY291bnRyeV9jb2xvcnMpICsNCiAgICAgICAgICAgI3NjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiZG9kZ2VyYmx1ZTQiLCAiZGFya29saXZlZ3JlZW40IiwiZGFya29yY2hpZDMiKSkgKw0KICAgICAgICAgICAjc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGU9IlNldDEiKSArDQogICAgICAgICAgIHNjYWxlX3NpemUocmFuZ2UgPSBjKDIsIDEyKSkgKw0KICAgICAgICAgICBzY2FsZV94X2xvZzEwKCkgKw0KICAgICAgICAgICAjIGJyZWFrIGRvd24gdGhlIHByZXZpb3VzIHNpbmdsZSBwbG90IGJ5IGNvbnRpbmVudCANCiAgICAgICAgICAgZmFjZXRfd3JhcCh+Y29udGluZW50KSArICAgICAjIGNyZWF0ZSBtdWx0aXBsZSBwYW5lbHMgYWNjb3JkaW5nIHRvIHRoZSBjb250aW5lbnRzDQogICAgICAgICAgICMgSGVyZSBjb21lcyB0aGUgZ2dhbmltYXRlLXNwZWNpZmljIGJpdHMNCiAgICAgICAgICAgbGFicyh0aXRsZSA9ICdZZWFyOiB7ZnJhbWVfdGltZX0nLCANCiAgICAgICAgICAgICAgICAgICAgeCA9ICdHRFAgcGVyIGNhcGl0YScsDQogICAgICAgICAgICAgICAgICAgIHkgPSAnbGlmZSBleHBlY3RhbmN5JykgKw0KICAgICAgICAgICB0cmFuc2l0aW9uX3RpbWUoeWVhcikgKw0KICAgICAgICAgICBlYXNlX2FlcygnbGluZWFyJykNCiMjIw0KYW5pbWF0ZSh3LCByZW5kZXJlciA9IGdpZnNraV9yZW5kZXJlcigpLA0KICAgICAgICAgIHJld2luZCA9IFRSVUUpDQpgYGANCg0KIyMgUmlkZ2V0bGluZSBQbG90IHdpdGggYGdncmlkZ2VzYCBMaWJyYXJ5DQoNClRoZSByaWRnZWxpbmUgcGxvdCBpcyBhIHVzZWZ1bCAzRCB0byBjb21wYXJlIG11bHRpcGxlIGRlbnNpdGllcy4gSXQgY3JlYXRlcyBhIDNEIGltcHJlc3Npb24gYW5kIGhhcyBnYWluZWQgaW5jcmVhc2luZyBwb3B1bGFyaXR5LiBIZXJlIHdlIHVzZSB0aGUgQ2FsaWZvcm5pYSBIb3VzaW5nIERhdGEgdGhhdCBpcyBhdmFpbGFibGUgb24gdGhlIFByb2plY3QgRGF0YSBTZXQgPGh0dHBzOi8vcGVuZ2RzY2kuZ2l0aHViLmlvL2RhdGFzZXRzLyNjYWwtaG91c2luZz4uDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJ30NCkNhbEhvdXNpbmcgPSByZWFkLmNzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Blbmdkc2NpL3N0YTU1My5odG1sL21haW4vZGF0YS9jYS1ob3VzaW5nLXByaWNlLmNzdiIpDQpnZ3Bsb3QoQ2FsSG91c2luZywgYWVzKHggPSBtZWRpYW5faG91c2VfdmFsdWUsIHkgPSBvY2Vhbl9wcm94aW1pdHksIGZpbGwgPSBvY2Vhbl9wcm94aW1pdHkpKSArDQogIGdlb21fZGVuc2l0eV9yaWRnZXMoKQ0KYGBgDQoNCllvdSBjYW4gcGFzcyBgc3RhdCh4KWAgb3IgYGZhY3RvcihzdGF0KHgpKWAgdG8gdGhlIGZpbGwgYXJndW1lbnQgb2YgYGFlc2AgYW5kIHVzZSBgZ2VvbV9kZW5zaXR5X3JpZGdlc19ncmFkaWVudGAgYW5kIGEgY29udGludW91cyBmaWxsIGNvbG9yIHNjYWxlIHRvIGZpbGwgZWFjaCByaWRnZWxpbmUgd2l0aCBhIGdyYWRpZW50Lg0KDQpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcid9DQpnZ3Bsb3QoQ2FsSG91c2luZywgYWVzKHggPSBtZWRpYW5faG91c2VfdmFsdWUsIHkgPSBvY2Vhbl9wcm94aW1pdHksIGZpbGwgPSBzdGF0KHgpKSkgKw0KICAgICAgZ2VvbV9kZW5zaXR5X3JpZGdlc19ncmFkaWVudChqaXR0ZXJlZF9wb2ludHMgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9wb2ludHNfaml0dGVyKHdpZHRoID0gMC4wNSwgaGVpZ2h0ID0gMCksDQogICAgcG9pbnRfc2hhcGUgPSAnfCcsIHBvaW50X3NpemUgPSAxLCBwb2ludF9hbHBoYSA9IDEsIGFscGhhID0gMC4zLCkgKyANCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MobmFtZSA9ICJtZWRpYW5faG91c2VfdmFsdWUiLCBvcHRpb24gPSAiQyIpIA0KYGBgDQoNCk5leHQsIHdlIGV4cGxvcmUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjb250aW51b3VzIHZhcmlhYmxlcyBpbiB0aGUgaXJpcyBkYXRhIHNldC4gQXMgYW4gZXhhbXBsZSwgd2UgbWFrZSB0aGUgZm9sbG93aW5nIHJpZGdlbGluZSBwbG90IHRvIHNlZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHNlcGFsIHdpZHRocyBhY3Jvc3MgdGhlIHNwZWNpZXMuDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJ30NCmdncGxvdChpcmlzLCBhZXMoeCA9IFNlcGFsLldpZHRoLCB5ID0gU3BlY2llcywgZmlsbCA9IHN0YXQoeCkpKSArDQogICAgICBnZW9tX2RlbnNpdHlfcmlkZ2VzX2dyYWRpZW50KGppdHRlcmVkX3BvaW50cyA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX3BvaW50c19qaXR0ZXIod2lkdGggPSAwLjA1LCBoZWlnaHQgPSAwKSwNCiAgICBwb2ludF9zaGFwZSA9ICd8JywgcG9pbnRfc2l6ZSA9IDEsIHBvaW50X2FscGhhID0gMSwgYWxwaGEgPSAwLjMsKSArIA0KICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhuYW1lID0gIlNlcGFsIFdpZHRoIiwgb3B0aW9uID0gIkMiKSANCmBgYA0KDQpUaGUgYWJvdmUgZGlzdHJpYnV0aW9ucyBoYXZlIHNpbWlsYXIgc2hhcGVzICh2YXJpYXRpb25zKSBidXQgd2l0aCBkaWZmZXJlbnQgbWVhbnMuIFRoaXMgYWxzbyBpbmRpY2F0ZXMgdGhlIEFOT1ZBIG1vZGVsIGJldHdlZW4gc2VwYWwgd2lkdGggYW5kIHNwZWNpZXMgaXMgYXBwcm9wcmlhdGUuDQoNCiMjIE90aGVyIEV4dGVuc2lvbnMgdG8gZ2dwbG90DQoNCldlIGhhdmUgdXNlZCBnZ3Bsb3QgZXh0ZW5zaW9ucyAqKntnZ2FuaW1hdGV9KiogdG8gY3JlYXRlIGFuaW1hdGVkIGdyYXBocyBhbmQgKip7Z2dyaWRnZXN9KiogdG8gY3JlYXRlIHJpZGdlbGluZSBncmFwaHMgdG8gY29tcGFyZSBtdWx0aXBsZSBkZW5zaXRpZXMuIFRoZXJlIGFyZSBzZXZlcmFsIG90aGVyIGltcG9ydGFudCBnZ3Bsb3QgZXh0ZW5zaW9ucyB0aGF0IGVuaGFuY2UgdGhlIGJhc2ljIGdncGxvdHMuDQoNCi0gICBnZ2RlbmRybyAtIGNvbnRyb2xzIHRoZSBhcHBlYXJhbmNlIGFuZCBkaXNwbGF5IG9mIHlvdXIgY2x1c3RlciBhbmFseXNlcw0KDQotICAgZ2d0aGVtZXMgLSBjb250YWlucyB0aGVtZXMgYW5kIHNjYWxlcyB0aGF0IGVuaGFuY2UgdGhlIHN0YW5kYXJkIGdncGxvdHMuDQoNCi0gICBnZ3B1YnIgLSBtYWtlcyBpdCBlYXN5IHRvIHByb2R1Y2UgcHVibGljYXRpb24tcmVhZHkgcGxvdHMgdXNpbmcgZ2dwbG90Lg0KDQotICAgUGxvdGx5IC0gYnJpbmdzIGludGVyYWN0aXZpdHkgdG8gZ2dwbG90cy4gV2Ugd2lsbCBzcGVuZCBhIHdlZWsgb24gcGxvdGx5KCkuDQoNCi0gICBwYXRjaHdvcmsgLSBhcnJhbmdlcyBtdWx0aXBsZSBSIHBsb3RzIG9uIHRoZSBzYW1lIGdyYXBoaWNzIHBhZ2UNCg0KLSAgIGdnbWFwIC0gaXMgYSBwb3dlcmZ1bCBwYWNrYWdlIGZvciB2aXN1YWxpemluZyBzcGF0aWFsIGRhdGEgYW5kIG1vZGVscy4gSXQgbGF5ZXJzIGRhdGEgb24gdG9wIG9mIHN0YXRpYyBtYXBzIGZyb20gcG9wdWxhciBvbmxpbmUgc291cmNlcy4gV2Ugd2lsbCB1c2UgdGhlc2UgcGFja2FnZXMgdG8gbWFrZSBtYXBzIGxhdGVyLg0KDQotICAgZ2dyZXBlbCAtIGdpdmVzIGdncGxvdDIgdXNlcnMgZ3JlYXRlciBjb250cm9sIG92ZXIgaG93IHRleHQgbGFiZWxzIGFwcGVhciBpbiB0aGVpciBjaGFydHMuDQoNCi0gICBnZ2NvcnJwbG90IC0gY29udHJvbHMgdGhlIGFwcGVhcmFuY2Ugb2YgdGhlIG1hdHJpeCwgZnJvbSBhbHRlcmluZyB0aGUgY29sb3IsIHNoYXBlLCBvciBzaXplIG9mIHRoZSBib3hlcyAoYXMgaW4gdGhlIGNpcmNsZS1tYXRyaXggYWJvdmUpLCB0byBhZGRpbmcgY29lZmZpY2llbnQgbGFiZWxzLCByZW9yZGVyaW5nIHRoZSBtYXRyaXggYWNjb3JkaW5nIHRvIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nLCBhbmQgc28gb24uDQoNCi0gICBHR2FsbHkgLSBicmluZ3MgdG9nZXRoZXIgbWFueSB1c2VmdWwgYWRkaXRpb25hbCB2aXN1YWxpemF0aW9uIGZ1bmN0aW9uYWxpdHksIGFsbCBpbiBvbmUgcGFja2FnZS4NCg0KLSAgIGdnaXJhcGggLWlzIGFuIGh0bWx3aWRnZXQgdGhhdCBjYW4gYmUgZXh0ZW5kZWQgdG8gYW4gZXhpc3RpbmcgZ2dwbG90MiBzdWNoIGFzIGJhciBjaGFydCwgc2NhdHRlcnBsb3QsIGJveHBsb3QsIG1hcCwgZXRjLiwgYW5kIGRvZXMgdGhpbmdzIGxpa2UgZGlzcGxheWluZyBhIHRvb2x0aXAgb2YgeW91ciBjaG9pY2UuDQoNCiMgU2F2ZSBgZ2dwbG90YCBJbWFnZXMNCg0KQSBgZ2dwbG90YCBjYW4gYmUgc2F2ZWQgdG8gZGlmZmVyZW50IGZpbGUgZm9ybWF0cywgaW5jbHVkaW5nIFBERiwgU1ZHIHZlY3RvciBmaWxlcywgUE5HLCBUSUZGLCBKUEVHLCBldGMuDQoNCldlIGNhbiBlaXRoZXIgcHJpbnQgZGlyZWN0bHkgYSBgZ2dwbG90YCBpbnRvIGBQTkcvUERGYCBmaWxlcyBvciB1c2UgdGhlIGNvbnZlbmllbnQgZnVuY3Rpb24gYGdnc2F2ZSgpYCBmb3Igc2F2aW5nIGEgYGdncGxvdGAuDQoNClRoZSBkZWZhdWx0IG9mIGBnZ3NhdmUoKWAgaXMgdG8gZXhwb3J0IHRoZSBsYXN0IHBsb3QgdGhhdCB5b3UgZGlzcGxheWVkLCB1c2luZyB0aGUgc2l6ZSBvZiB0aGUgY3VycmVudCBncmFwaGljcyBkZXZpY2UuIEl0IGFsc28gZ3Vlc3NlcyB0aGUgdHlwZSBvZiBncmFwaGljcyBkZXZpY2UgZnJvbSB0aGUgZXh0ZW5zaW9uLg0KDQojIyBHZW5lcmFsIFN0ZXBzDQoNClRoZSBzdGFuZGFyZCBwcm9jZWR1cmUgdG8gc2F2ZSBhbnkgZ3JhcGhpY3MgZnJvbSBSIGlzIGFzIGZvbGxvd3M6DQoNCi0gICBPcGVuIGEgZ3JhcGhpYyBkZXZpY2UgdXNpbmcgb25lIG9mIHRoZSBmb2xsb3dpbmcgZnVuY3Rpb25zOg0KDQogICAgLSAgIGBwZGYo4oCcci1ncmFwaGljcy5wZGbigJ0pYCwNCiAgICAtICAgYHN2ZyjigJxyLWdyYXBoaWNzLnN2Z+KAnSlgLA0KICAgIC0gICBgcG5nKOKAnHItZ3JhcGhpY3MucG5n4oCdKWAsDQogICAgLSAgIGB0aWZmKOKAnHItZ3JhcGhpY3MudGlmZuKAnSlgLA0KICAgIC0gICBganBlZyjigJxyLWdyYXBoaWNzLmpwZ+KAnSlgLCBldGMuDQoNCi0gICBBZGRpdGlvbmFsIGFyZ3VtZW50cyBpbmRpY2F0aW5nIHRoZSB3aWR0aCBhbmQgdGhlIGhlaWdodCAoaW4gaW5jaGVzKSBvZiB0aGUgZ3JhcGhpY3MgcmVnaW9uIGNhbiBiZSBhbHNvIHNwZWNpZmllZCBpbiB0aGUgbWVudGlvbmVkIGZ1bmN0aW9uLg0KDQotICAgQ3JlYXRlIGFuZCBwcmludCBhIHBsb3QuIENsb3NlIHRoZSBncmFwaGljIGRldmljZSB1c2luZyB0aGUgZnVuY3Rpb24gYGRldi5vZmYoKWAuDQoNCiMjIFNhdmUgYGdncGxvdGAgaW50byBhIFBERiBGaWxlDQoNClRoZSBmb2xsb3dpbmcgY29kZSBpbGx1c3RyYXRlcyBob3cgdG8gc2F2ZSBhIGdncGxvdCBpbiBhIGZvbGRlciBpbiBQREYgZm9ybWF0Lg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KIyBzY2F0dGVyIHBsb3RzDQppcmlzLnNjYXR0ZXIgPC0gZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFNlcGFsLldpZHRoKSkgKyANCiAgICAgICAgICAgZ2VvbV9wb2ludCgpDQojIyBib3gtcGxvdA0KaXJpcy5ib3hwbG90IDwtIGdncGxvdChpcmlzLCBhZXMoU3BlY2llcywgU2VwYWwuTGVuZ3RoKSkgKyANCiAgZ2VvbV9ib3hwbG90KCkNCiMgUHJpbnQgcGxvdHMgdG8gYSBQREYgZmlsZTogb25lIHBhZ2UgcGVyIFBERiBmaWxlDQpwZGYoInNhdmVQREZnZ3Bsb3QucGRmIikgICAjIFNhdmUgdGhlIFBERiBmaWxlIGluIGdncGxvdCBmb2xkZXIuDQpwcmludChpcmlzLnNjYXR0ZXIpICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGDQpwcmludChpcmlzLmJveHBsb3QpICAgICAjIFBsb3QgMiAtLS0+IGluIHRoZSBzZWNvbmQgcGFnZSBvZiB0aGUgUERGDQpkZXYub2ZmKCkgDQpgYGANCg0KIyMgU2F2ZSBnZ3Bsb3Qgd2l0aCBgZ2dzYXZlKClgDQoNCkl0J3MgYWxzbyBwb3NzaWJsZSB0byBtYWtlIGEgZ2dwbG90IGFuZCBzYXZlIGl0IGZyb20gdGhlIHNjcmVlbiB1c2luZyB0aGUgZnVuY3Rpb24gYGdnc2F2ZSgpYC4NCg0KYGBge3IsIGZpZy5hbGlnbj0nY2VudGVyJ30NCiMgMS4gQ3JlYXRlIGEgcGxvdDogZGlzcGxheWVkIG9uIHRoZSBzY3JlZW4gKGJ5IGRlZmF1bHQpDQpnZ3Bsb3QobXRjYXJzLCBhZXMod3QsIG1wZykpICsgZ2VvbV9wb2ludCgpDQojIDIuMS4gU2F2ZSB0aGUgcGxvdCB0byBhIHBkZg0KZ2dzYXZlKCJtdGNhcm15cGxvdC5wZGYiKQ0KIyAyLjIgT1Igc2F2ZSBpdCB0byBwbmcgZmlsZQ0KZ2dzYXZlKCJtdGNhcm15cGxvdC5wbmciKQ0KYGBgDQoNCldlIGNhbiBhbHNvIHNhdmUgbXVsdGlwbGUgcGxvdHMgaW4gdGhlIHNhbXBsZSBmb3JtYXQgdG8gYSBzaW5nbGUgZmlsZS4gV2UgY2FuIHVzZSBgcGxvdF9ncmlkKClgIGluICoqe2Nvd3Bsb3R9KiogdG8gbWFrZSB0d28gZmlndXJlcyBvbiB0aGUgc2FtZSBncmFwaGljIHBhZ2UgYW5kIHRoZW4gdXNlIGBnZ3NhdmUoKWAgdG8gc2F2ZSBpdCB0byBhIHNpbmdsZSBmaWxlLg0KDQpgYGB7cn0NCiMgDQpwMSA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMod3QsIG1wZykpICsgZ2VvbV9wb2ludCgpDQpwMiA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMod3QpKSArIGdlb21faGlzdG9ncmFtKCkNCmNvbWJpbmVkUGxvdCA8LSBwbG90X2dyaWQocDEsIHAyLCBsYWJlbHM9YygiQSIsICJCIiksIG5jb2wgPSAyLCBucm93ID0gMSkNCiMjDQpnZ3NhdmUoIkNvbWJpbmVkUGxvdC5wbmciLCBwbG90ID0gY29tYmluZWRQbG90KQ0KYGBgDQoNCiMgVGhlIFJvbGUgb2YgQ29sb3IgaW4gRWZmZWN0aXZlIFZpc3VhbGl6YXRpb24NCg0KU3Vic3RhbnRpYWwgcmVzZWFyY2ggc2hvd3MgdGhhdCBjb2xvciBwbGF5cyBhIHBpdm90YWwgcm9sZSBpbiBvdXIgdmlzdWFsIGV4cGVyaWVuY2VzLiBJbiB0aGlzIGNsYXNzLCB3ZSB1c2UgY29sb3JzIGZvciB0d28gbWFpbiBwdXJwb3NlczogZW5jb2RpbmcgYW5kIGhpZ2hsaWdodGluZyBpbmZvcm1hdGlvbi4NCg0KV2UgdXNlIGRpZmZlcmVudCBjb2xvcnMgdG8gZGVub3RlIGRpZmZlcmVudCB2YWx1ZXMgb2YgYSB2YXJpYWJsZS4gVGhlcmUgYXJlIG1hbnkgY29udGludW91cyBhbmQgZGlzY3JldGUgY29sb3IgcGFsZXR0ZXMgYXZhaWxhYmxlIGluIGRpZmZlcmVudCBSIGxpYnJhcmllcyB0aGF0IGNhbiBiZSB1c2VkIG9uIGRpZmZlcmVudCBvY2Nhc2lvbnMuIEhvd2V2ZXIsIHdlIG5lZWQgdG8gcGF5IHZlcnkgc3BlY2lhbCBhdHRlbnRpb24gdG8gdGhlIGNhc2VzIHdoZW4gY29sb3JzIGFyZSB1c2VkIGZvciBlbmNvZGluZyBiZWNhdXNlIHdlIHNlZSBjb2xvcnMgZGlmZmVyZW50bHkgLSBwZW9wbGUgd2l0aCB2aXNpb24gZGVmaWNpZW5jeSBhcmUgbGVzcyBzZW5zaXRpdmUgdG8gc29tZSBvZiB0aGUgY29sb3JzLiBUaGUgbmV4dCBmaWd1cmUgaWxsdXN0cmF0ZXMgdGhlIG1ham9yIHR5cGVzIG9mIGNvbG9yIGJsaW5kbmVzcy4NCg0KYGBge3IgZmlnLmFsaWduID0gJ2NlbnRlcicsIG91dC53aWR0aCA9ICc4MCUnfQ0KaW5jbHVkZV9ncmFwaGljcygiQ29sb3JCbGluZG5lc3NUeXBlcy5wbmciKQ0KYGBgDQoNCldoYXQgY29sb3JzIHNob3VsZCB5b3UgdXNlPyBUaGVyZSBhcmUgZGlmZmVyZW50IGNvbG9yLXBpY2tlciB0b29scyBhdmFpbGFibGUgZm9yIHVzIHRvIGNob29zZSBjb2xvcnMgd2hpbGUgbWFraW5nIHN1cmUgdGhhdCB0aGUgY29sb3IgcGFsZXR0ZSBpcyBhY2Nlc3NpYmxlLiBIZXJlIGlzIGEgc2V0IG9mIDggcGFpcnMgb2YgY29udHJhc3RpbmcgY29sb3JzIHRoYXQgbWFpbnRhaW4gdGhlaXIgY29udHJhc3QgZm9yIHBlb3BsZSB3aG8gYXJlIGNvbG9yYmxpbmQuDQoNCmBgYHtyIGZpZy5hbGlnbiA9ICdjZW50ZXInLCBvdXQud2lkdGggPSAnODAlJ30NCmluY2x1ZGVfZ3JhcGhpY3MoInItY29sb3ItcGFsZXR0ZXMucG5nIikNCmBgYA0KDQpGb3IgZXhhbXBsZSwgdGhlIGZvbGxvd2luZyB0aHJlZSBzYW1wbGUgcGFsZXR0ZXMgYXJlIGNvbG9yYmxpbmQtZnJpZW5kbHkuDQoNCi0gICAqKklCTSBEZXNpZ24gTGlicmFyeSoqDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9MX0NCnBhcihtZnJvdz1jKDEsNSksIG9tYT1jKDAsMCwwLDApLCBtYXIgPSBjKDEsMC41LDEsMC41KSkNCnBsb3QoTlVMTCwgdHlwZT0ibiIsIHhsaW09YygtMSwxKSwgeWxpbT1jKC0xLDEpLCBheGVzID0gRkFMU0UsIHhsYWIgPSAiIiwgeWxhYiA9ICIiKQ0KcmVjdCh4bGVmdCA9IC0xLCB5Ym90dG9tID0gLTAuNSwgeHJpZ2h0ID0xLCB5dG9wID0gMC41LCBsdHkgPSAxLCBjb2wgPSAiIzY0OEZGRiIpDQp0ZXh0KDAsMCwgIiM2NDhGRkYiKQ0KIyMNCnBsb3QoTlVMTCwgdHlwZT0ibiIsIHhsaW09YygtMSwxKSwgeWxpbT1jKC0xLDEpLCBheGVzID0gRkFMU0UsIHhsYWIgPSAiIiwgeWxhYiA9ICIiKQ0KcmVjdCh4bGVmdCA9IC0xLCB5Ym90dG9tID0gLTAuNSwgeHJpZ2h0ID0xLCB5dG9wID0gMC41LCBsdHkgPSAxLCBjb2wgPSAiIzc4NUVGMCIpDQp0ZXh0KDAsMCwgIiM3ODVFRjAiKQ0KIyMNCnBsb3QoTlVMTCwgdHlwZT0ibiIsIHhsaW09YygtMSwxKSwgeWxpbT1jKC0xLDEpLCBheGVzID0gRkFMU0UsIHhsYWIgPSAiIiwgeWxhYiA9ICIiKQ0KcmVjdCh4bGVmdCA9IC0xLCB5Ym90dG9tID0gLTAuNSwgeHJpZ2h0ID0xLCB5dG9wID0gMC41LCBsdHkgPSAxLCBjb2wgPSAiI0RDMjY3RiIpDQp0ZXh0KDAsMCwgIiNEQzI2N0YiKQ0KIyMNCnBsb3QoTlVMTCwgdHlwZT0ibiIsIHhsaW09YygtMSwxKSwgeWxpbT1jKC0xLDEpLCBheGVzID0gRkFMU0UsIHhsYWIgPSAiIiwgeWxhYiA9ICIiKQ0KcmVjdCh4bGVmdCA9IC0xLCB5Ym90dG9tID0gLTAuNSwgeHJpZ2h0ID0xLCB5dG9wID0gMC41LCBsdHkgPSAxLCBjb2wgPSAiI0ZFNjEwMCIpDQp0ZXh0KDAsMCwgIiNGRTYxMDAiKQ0KIyMNCnBsb3QoTlVMTCwgdHlwZT0ibiIsIHhsaW09YygtMSwxKSwgeWxpbT1jKC0xLDEpLCBheGVzID0gRkFMU0UsIHhsYWIgPSAiIiwgeWxhYiA9ICIiKQ0KcmVjdCh4bGVmdCA9IC0xLCB5Ym90dG9tID0gLTAuNSwgeHJpZ2h0ID0xLCB5dG9wID0gMC41LCBsdHkgPSAxLCBjb2wgPSAiI0ZGQjAwMCIpDQp0ZXh0KDAsMCwgIiNGRkIwMDAiKQ0KYGBgDQoNCi0gICAqKldvbmcncyBQYWxldHRlKioNCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD0xfQ0KcGFyKG1mcm93PWMoMSw4KSwgb21hPWMoMCwwLDAsMCksIG1hciA9IGMoMSwwLjUsMSwwLjUpKQ0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjMDAwMDAwIikNCnRleHQoMCwwLCAiIzAwMDAwMCIsIGNvbCA9ICJ3aGl0ZSIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjRTY5RjAwIikNCnRleHQoMCwwLCAiI0U2OUYwMCIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjNTZCNEU5IikNCnRleHQoMCwwLCAiIzU2QjRFOSIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjMDA5RTczIikNCnRleHQoMCwwLCAiIzAwOUU3MyIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjRjBFNDQyIikNCnRleHQoMCwwLCAiI0YwRTQ0MiIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjMDA3MkIyIikNCnRleHQoMCwwLCAiIzAwNzJCMiIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjRDU1RTAwIikNCnRleHQoMCwwLCAiI0Q1NUUwMCIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjQ0M3OUE3IikNCnRleHQoMCwwLCAiI0NDNzlBNyIpDQpgYGANCg0KLSAgICoqUGFsJ3MgUGFsbGV0ZSoqDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9MX0NCnBhcihtZnJvdz1jKDEsOCksIG9tYT1jKDAsMCwwLDApLCBtYXIgPSBjKDEsMC41LDEsMC41KSkNCg0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjMzMyMjg4IikNCnRleHQoMCwwLCAiIzMzMjI4OCIsIGNvbCA9ICJ3aGl0ZSIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjMTE3NzMzIikNCnRleHQoMCwwLCAiIzExNzczMyIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjNDRBQTk5IikNCnRleHQoMCwwLCAiIzQ0QUE5OSIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjODhDQ0VFIikNCnRleHQoMCwwLCAiIzg4Q0NFRSIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjRERDQzc3IikNCnRleHQoMCwwLCAiI0REQ0M3NyIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjQ0M2Njc3IikNCnRleHQoMCwwLCAiI0NDNjY3NyIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjQUE0NDk5IikNCnRleHQoMCwwLCAiI0FBNDQ5OSIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjODgyMjU1IikNCnRleHQoMCwwLCAiIzg4MjI1NSIsIGNvbCA9ICJ3aGl0ZSIpDQpgYGANCg0KV2UgY2FuIGZpbmQgbW9yZSBSIGNvbG9yIHBhbGV0dGVzIGZyb20gPGh0dHBzOi8vci1jaGFydHMuY29tL2NvbG9yLXBhbGV0dGVzLz4uDQoNCg0KQXMgYW4gIGV4YW1wbGUsIHdlIHVzZSB0aGUgYWJvdmUgY29sb3IgYmxpbmQgZnJpZW5kbHkgY29sb3Igc2NoZW1lIGFuZCBkcmF3IHZhcmlvdXMgZGVuc2l0eSBjdXJ2ZXMuDQoNCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD00fQ0KaXJpczAgPSBpcmlzDQpUeXBlID0gYyhwYXN0ZShpcmlzJFNwZWNpZXMsIi5TZXBhbC5MZW5ndGgiLCBzZXAgPSAiIikscGFzdGUoaXJpcyRTcGVjaWVzLCIuU2VwYWwuV2lkdGgiLCBzZXAgPSAiIikpDQpNZWFzdXJlID0gYyhpcmlzJFNlcGFsLkxlbmd0aCAsaXJpcyRTZXBhbC5XaWR0aCkNCmlyaXNOZXcgPSBkYXRhLmZyYW1lKFR5cGUgPSBUeXBlLCBNZWFzdXJlID0gTWVhc3VyZSkNCmNvbHMxID0gYygiIzMzMjI4OCIsIiMxMTc3MzMiLCIjNDRBQTk5IiwiIzg4Q0NFRSIsIiNERENDNzciLCIjQ0M2Njc3IikNCmNvbHMzID0gYygiI0FBNDQ5OSIsIiM4ODIyNTUiKQ0KcCA9IGdncGxvdCgpICsgDQogICAgICBnZW9tX2RlbnNpdHkoZGF0YSA9IGlyaXNOZXcsIGFlcyh4ID0gTWVhc3VyZSwgY29sb3IgPSBUeXBlKSwgbHdkID0gMSkgKyANCiAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjb2xzMSkgKw0KICAgICAgZ2d0aXRsZSgiTXVsdGlwbGUgRGVuc2l0eSBDdXJ2ZXMiKSArDQogICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMSwgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAibmF2eSIpKQ0KcA0KYGBgDQoNCg0KU29tZSBwZW9wbGUgbGlrZSBmaWxsZWQgZGVuc2l0eSBjdXJ2ZXMuIGBhbHBoYWAgaXMgYSBmdW5jdGlvbiBpbiB0aGUgbGlicmFyeSBvZiBgZ2dwbG90MmAuDQoNCg0KYGBge3J9DQpwMSA9IGdncGxvdChkYXRhID0gaXJpc05ldywgYWVzKHggPSBNZWFzdXJlLCBjb2xvciA9IFR5cGUsIGZpbGwgPSBUeXBlKSkgKyANCiAgICAgIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuMjUsIGx3ZCA9IDEuNSkgKw0KICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29sczEpICsgDQogICAgICBnZ3RpdGxlKCJNdWx0aXBsZSBGaWxsZWQgRGVuc2l0eSBDdXJ2ZXMiKSArDQogICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMSwgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAicHVycGxlIikpDQoNCnAxDQpgYGANCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg==