Statistics

Smoothing

The mtcars dataset contains information for 32 cars from Motor Trends magazine from 1974. This dataset is small, intuitive, and contains a variety of continuous and categorical (both nominal and ordinal) variables.

library(ggplot2)
package 㤼㸱ggplot2㤼㸲 was built under R version 3.6.3
library(dplyr )

Attaching package: 㤼㸱dplyr㤼㸲

The following objects are masked from 㤼㸱package:stats㤼㸲:

    filter, lag

The following objects are masked from 㤼㸱package:base㤼㸲:

    intersect, setdiff, setequal, union
# View the structure of mtcars
str(cars)
'data.frame':   50 obs. of  2 variables:
 $ speed: num  4 4 7 7 8 9 10 10 10 11 ...
 $ dist : num  2 10 4 22 16 10 18 26 34 17 ...
# Using mtcars, draw a scatter plot of mpg vs. wt
ggplot(mtcars, aes(wt, mpg)) +
  geom_point() +
  geom_smooth()

# Amend the plot. Use lin. reg. smoothing; turn off std err ribbon
ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  geom_smooth(method = "lm", se = FALSE)

# Amend the plot. Swap geom_smooth() for stat_smooth().
ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  stat_smooth(method = "lm", se = FALSE)

You can use either stat_smooth() or geom_smooth() to apply a linear model.

Grouping variables

We’ll continue by considering the situation of looking at sub-groups in our dataset. For this we’ll encounter the invisible group aesthetic.

#create factor cyl and am
mtcars <- mtcars %>% 
  mutate(fcyl = as.factor(cyl),
         fam = as.factor(am))
# Using mtcars, plot mpg vs. wt, colored by fcyl
ggplot(mtcars, aes(wt, mpg, color = fcyl)) +
  # Add a point layer
  geom_point() +
  # Add a smooth lin reg stat, no ribbon
  stat_smooth(method = "lm", se = FALSE)

# Amend the plot to add another smooth layer with dummy grouping
ggplot(mtcars, aes(x = wt, y = mpg, color = fcyl)) +
  geom_point() +
  stat_smooth(method = "lm", se = FALSE) +
  stat_smooth(mapping = aes(group = 1), method = "lm", se = FALSE)

Notice that the color aesthetic defined an invisible group aesthetic. Defining the group aesthetic for a specific geom means we can overwrite that. Here, we use a dummy variable to calculate the smoothing model for all values.

Modifying stat_smooth

We’ll consider another argument, span, used in LOESS smoothing, and we’ll take a look at a nice scenario of properly mapping different models.

ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  # Add 3 smooth LOESS stats, varying span & color
  stat_smooth(color = "red", se = FALSE, span = 0.9) +
  stat_smooth(color = "green", se = FALSE, span = 0.6) +
  stat_smooth(color = "blue", se = FALSE, span = 0.3)

# Amend the plot to color by fcyl
ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  # Add a smooth LOESS stat, no ribbon
  stat_smooth(se = FALSE) +
  # Add a smooth lin. reg. stat, no ribbon
  stat_smooth(method = "lm", se = FALSE)

# Amend the plot
ggplot(mtcars, aes(x = wt, y = mpg, color = fcyl)) +
  geom_point() +
  # Map color to dummy variable "All"
  stat_smooth(mapping = aes(color = "All"), se = FALSE) +
  stat_smooth(method = "lm", se = FALSE)

The default span for LOESS is 0.9. A lower span will result in a better fit with more detail; but don’t overdo it or you’ll end up over-fitting!

We’ll take a look at the standard error ribbons, which show the 95% confidence interval of smoothing models.

library(carData)

Attaching package: 㤼㸱carData㤼㸲

The following object is masked _by_ 㤼㸱.GlobalEnv㤼㸲:

    Vocab

Vocab has been given an extra column, year_group, splitting the dates into before and after 1995.

Vocab <- Vocab %>% 
  mutate(year_group = as.factor(ifelse(year<1995, "[1974,1995]", "[1995,2016]") ))
# Using Vocab, plot vocabulary vs. education, colored by year group
ggplot(Vocab, aes(education, vocabulary, color = year_group)) +
  # Add jittered points with transparency 0.25
  geom_jitter(alpha = 0.25) +
  # Add a smooth lin. reg. line (with ribbon)
  stat_smooth(method = "lm", se = FALSE)

It’s easier to read the plot if the standard error ribbons match the lines, and the lines have more emphasis.

# Amend the plot
ggplot(Vocab, aes(x = education, y = vocabulary, color = year_group)) +
  geom_jitter(alpha = 0.25) +
  # Map the fill color to year_group, set the line size to 2
  stat_smooth(method = "lm",size = 2, mapping= aes(fill = year_group))

Notice that since 1995, education has relatively smaller effect on increasing vocabulary.

Quantiles

we’ll use stat_quantile() to apply a quantile regression.

Linear regression predicts the mean response from the explanatory variables, quantile regression predicts a quantile response (e.g. the median) from the explanatory variables. Specific quantiles can be specified with the quantiles argument.

Specifying many quantiles and color your models according to year can make plots too busy.

ggplot(Vocab, aes(x = education, y = vocabulary)) +
  geom_jitter(alpha = 0.25) +
  # Add a quantile stat, at 0.05, 0.5, and 0.95
  stat_quantile(quantiles = c(0.05, 0.5, 0.95))

# Amend the plot to color by year_group
ggplot(Vocab, aes(x = education, y = vocabulary, color = year_group)) +
  geom_jitter(alpha = 0.25) +
  stat_quantile(quantiles = c(0.05, 0.5, 0.95))

Quantile regression is a great tool for getting a more detailed overview of a large dataset.

Using stat_sum

1 solution for overplotting is jittering with transparency. Another solution is to use stat_sum(), which calculates the total number of overlapping observations and maps that onto the size aesthetic.

stat_sum() allows a special variable, ..prop.., to show the proportion of values within the dataset.

# Run this, look at the plot, then update it
ggplot(Vocab, aes(x = education, y = vocabulary)) +
  # Replace this with a sum stat
  geom_jitter(alpha = 0.25)

# Run this, look at the plot, then update it
ggplot(Vocab, aes(x = education, y = vocabulary)) +
  # Replace this with a sum stat
  stat_sum()

ggplot(Vocab, aes(x = education, y = vocabulary)) +
  stat_sum() +
  # Add a size scale, from 1 to 10
  scale_size(range = c(1,10))

# Amend the stat to use proportion sizes
ggplot(Vocab, aes(x = education, y = vocabulary)) +
  stat_sum(aes(size =..prop..))

# Amend the plot to group by education
ggplot(Vocab, aes(x = education, y = vocabulary, group = education)) +
  stat_sum(aes(size = ..prop..))

If a few data points overlap, jittering is great. When you have lots of overlaps (particularly where continuous data has been rounded), using stat_sum() to count the overlaps is more useful.

Stats outside geom

Preparations

We’ll aim to make the below plot:

stats

stats

We’ll establish our positions and base layer of the plot.

Establishing these items as independent objects will allow us to recycle them easily in many layers, or plots.

  • position_jitter() adds jittering (e.g. for points).
  • position_dodge() dodges geoms, (e.g. bar, col, boxplot, violin, errorbar, pointrange).
  • position_jitterdodge() jitters and dodges geoms, (e.g. points).
# Define position objects
# 1. Jitter with width 0.2
posn_j <- position_jitter(width = 0.2)
# 2. Dodge with width 0.1
posn_d <- position_dodge(width = 0.1)
# 3. Jitter-dodge with jitter.width 0.2 and dodge.width 0.1
posn_jd <- position_jitterdodge(jitter.width = 0.2, dodge.width = 0.1)
# Create the plot base: wt vs. fcyl, colored by fam
p_wt_vs_fcyl_by_fam <- ggplot(mtcars, aes(fcyl, wt, color = fam))
# Add a point layer
p_wt_vs_fcyl_by_fam +
  geom_point()

The default positioning of the points is highly susceptible to overplotting.

Using position objects

Now that the position objects have been created, you can apply them to the base plot to see their effects. You do this by adding a point geom and setting the position argument to the position object.

# Add jittering only
p_wt_vs_fcyl_by_fam +
  geom_point(position = posn_j)

# Add dodging only
p_wt_vs_fcyl_by_fam_jit <- p_wt_vs_fcyl_by_fam +
  geom_point(position = posn_d)
p_wt_vs_fcyl_by_fam_jit

# Add jittering and dodging
p_wt_vs_fcyl_by_fam +
  geom_point(position = posn_jd)

Although you can set position by setting the position argument to a string (for example position = “dodge”), defining objects promotes consistency between layers.

Plotting variations

The preparation is done; now let’s explore stat_summary().

Summary statistics refers to a combination of location (mean or median) and spread (standard deviation or confidence interval).

These metrics are calculated in stat_summary() by passing a function to the fun.data argument. mean_sdl(), calculates multiples of the standard deviation and mean_cl_normal() calculates the t-corrected 95% CI.

Arguments to the data function are passed to stat_summary()’s fun.args argument as a list.

p_wt_vs_fcyl_by_fam_jit +
  # Change the geom to be an errorbar
  stat_summary(fun.data = mean_sdl, fun.args = list(mult = 1), position = posn_d)

p_wt_vs_fcyl_by_fam_jit +
  # Change the geom to be an errorbar
  stat_summary(fun.data = mean_sdl, fun.args = list(mult = 1), position = posn_d,
  geom  ="errorbar")

p_wt_vs_fcyl_by_fam_jit +
  # Add a summary stat of normal confidence limits
  stat_summary(fun.data = mean_cl_normal, position = posn_d)

You can always assign your own function to the fun.data argument as long as the result is a data frame and the variable names match the aesthetics that you will need for the geom layer.

Coordinates

Zooming In

We’ll compare zooming by changing scales and by changing coordinates.

The big difference is that the scale functions change the underlying dataset, which affects calculations made by computed geoms (like histograms or smooth trend lines), whereas coordinate functions make no changes to the dataset.

# Run the code, view the plot, then update it
ggplot(mtcars, aes(x = wt, y = hp, color = fam)) +
  geom_point() +
  geom_smooth() +
  # Add a continuous x scale from 3 to 6
  scale_x_continuous(limits = c(3, 6))

ggplot(mtcars, aes(x = wt, y = hp, color = fam)) +
  geom_point() +
  geom_smooth() +
  # Add Cartesian coordinates with x limits from 3 to 6
  coord_cartesian(xlim = c(3, 6))

Using the scale function to zoom in meant that there wasn’t enough data to calculate the trend line, and geom_smooth() failed. When coord_cartesian() was applied, the full dataset was used for the trend calculation.

Aspect ratio: 1:1 ratios

We can set the aspect ratio of a plot with coord_fixed(), which uses ratio = 1 as a default. A 1:1 aspect ratio is most appropriate when two continuous variables are on the same scale, as with the iris dataset.

All variables are measured in centimeters, so it only makes sense that one unit on the plot should be the same physical distance on each axis. This gives a more truthful depiction of the relationship between the two variables since the aspect ratio can change the angle of our smoothing line. This would give an erroneous impression of the data. Of course the underlying linear models don’t change, but our perception can be influenced by the angle drawn.

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
  geom_jitter() +
  geom_smooth(method = "lm", se = FALSE) +
  # Fix the coordinate ratio
  coord_fixed(ratio = 1)

A 1:1 aspect ratio is helpful when your axes show the same scales.

Setting rations

When values are not on the same scale it can be a bit tricky to set an appropriate aspect ratio. A classic William Cleveland (inventor of dot plots) example is the sunspots data set. We have 3200 observations from 1750 to 2016.

sun_plot is a plot without any set aspect ratio. It fills up the graphics device.

library(zoo)
package 㤼㸱zoo㤼㸲 was built under R version 3.6.3
Attaching package: 㤼㸱zoo㤼㸲

The following objects are masked from 㤼㸱package:base㤼㸲:

    as.Date, as.Date.numeric
sunspots.m <- data.frame(
year = index(sunspot.month),
value = reshape2::melt(sunspot.month)$value)
sun_plot <- ggplot(sunspots.m, aes(x = year, y = value)) +
  geom_line() +
  coord_fixed() # default to 1:1 aspect ratio

sun_plot

# Fix the aspect ratio to 1:1
sun_plot +
  coord_fixed(ratio = 1)
Coordinate system already present. Adding new coordinate system, which will replace the existing one.

# Change the aspect ratio to 20:1
sun_plot +
  coord_fixed(ratio = 0.0555)
Coordinate system already present. Adding new coordinate system, which will replace the existing one.

Making a wide plot by calling coord_fixed() with a high ratio is often useful for long time series.

Expand and clip

The coord_*() layer functions offer two useful arguments that work well together: expand and clip.

  • expand sets a buffer margin around the plot, so data and axes don’t overlap. Setting expand to 0 draws the axes to the limits of the data.
  • clip decides whether plot elements that would lie outside the plot panel are displayed or ignored (“clipped”).

When done properly this can make a great visual effect! We’ll use theme_classic() and modify the axis lines in this example.

ggplot(mtcars, aes(wt, mpg)) +
  geom_point(size = 2) +
  # Add Cartesian coordinates with zero expansion
  coord_cartesian(expand = 0) +
  theme_classic()

ggplot(mtcars, aes(wt, mpg)) +
  geom_point(size = 2) +
  # Turn clipping off
  coord_cartesian(expand = 0, clip = "off") +
  theme_classic() +
  # Remove axis lines
  theme(axis.line = element_blank())

These arguments make clean and accurate plots by not cutting off data.

Log-transforming scales

Using scale_y_log10() and scale_x_log10() is equivalent to transforming our actual dataset before getting to ggplot2.

Using coord_trans(), setting x = “log10” and/or y = “log10” arguments, transforms the data after statistics have been calculated. The plot will look the same as with using scale_*_log10(), but the scales will be different, meaning that we’ll see the original values on our log10 transformed axes. This can be useful since log scales can be somewhat

# Produce a scatter plot of brainwt vs. bodywt
ggplot(msleep, aes(bodywt, brainwt)) +
  geom_point() +
  ggtitle("Raw Values")

# Add scale_*_*() functions
ggplot(msleep, aes(bodywt, brainwt)) +
  geom_point() +
  scale_x_log10() +
  scale_y_log10() +
  ggtitle("Scale_ functions")

# Perform a log10 coordinate system transformation
ggplot(msleep, aes(bodywt, brainwt)) +
  geom_point() +
  coord_trans(x = "log10", y = "log10")

Each transformation method has implications for the plot’s interpretability. Think about your audience when choosing a method for applying transformations.

Adding stats to transformed scales

Remember that statistics are calculated on the untransformed data. A linear model may end up looking not-so-linear after an axis transformation. Let’s revisit the two plots from the previous exercise and compare their linear models.

# Plot with a scale_*_*() function:
ggplot(msleep, aes(bodywt, brainwt)) +
  geom_point() +
  geom_smooth(method = "lm", se = FALSE) +
  # Add a log10 x scale
  scale_x_log10() +
  # Add a log10 y scale
  scale_y_log10() +
  ggtitle("Scale functions")

# Plot with transformed coordinates
ggplot(msleep, aes(bodywt, brainwt)) +
  geom_point() +
  geom_smooth(method = "lm", se = FALSE) +
  # Add a log10 coordinate transformation for x and y axes
  coord_trans(x = "log10", y = "log10")

The smooth trend line is calculated after scale transformations but not coordinate transformations, so the second plot doesn’t make sense.

Useful double axes

library(lubridate)

Attaching package: 㤼㸱lubridate㤼㸲

The following object is masked from 㤼㸱package:base㤼㸲:

    date
airquality <- airquality %>% 
  mutate(Date = make_date(1974, Month, Day))

Double x and y-axes are a contentious topic in data visualization. Here, we will review a great use case where double axes actually do add value to a plot.

# Using airquality, plot Temp vs. Date
ggplot(airquality, aes(Date, Temp)) +
  # Add a line layer
  geom_line() +
  labs(x = "Date (1973)", y = "Fahrenheit")

# Define breaks (Fahrenheit)
y_breaks <- c(59, 68, 77, 86, 95, 104)

# Convert y_breaks from Fahrenheit to Celsius
y_labels <- (y_breaks - 32)*5/9

# Create a secondary x-axis
secondary_y_axis <- sec_axis(
  # Use identity transformation
  trans = identity,
  name = "Celsius",
  # Define breaks and labels as above
  breaks = y_breaks,
  labels = y_labels
)

# Examine the object
secondary_y_axis
<ggproto object: Class AxisSecondary, gg>
    axis: NULL
    break_info: function
    breaks: 59 68 77 86 95 104
    create_scale: function
    detail: 1000
    empty: function
    guide: waiver
    init: function
    labels: 15 20 25 30 35 40
    make_title: function
    mono_test: function
    name: Celsius
    trans: function
    transform_range: function
    super:  <ggproto object: Class AxisSecondary, gg>
# Update the plot
ggplot(airquality, aes(Date, Temp)) +
  geom_line() +
  # Add the secondary y-axis 
  scale_y_continuous(sec.axis = secondary_y_axis) +
  labs(x = "Date (1973)", y = "Fahrenheit")

Double axes are most useful when you want to display the same value in two differnt units.

### Flipping axes

Flipping axes means to reverse the variables mapped onto the x and y aesthetics. We can just change the mappings in aes(), but we can also use the coord_flip() layer function.

There are two reasons to use this function:

  • We want a vertical geom to be horizontal, or
  • We’ve completed a long series of plotting functions and want to flip it without having to rewrite all our commands.
# Plot fcyl bars, filled by fam
ggplot(mtcars, aes(fcyl, fill = fam)) +
  # Place bars side by side
  geom_bar(position = "dodge")

ggplot(mtcars, aes(fcyl, fill = fam)) +
  geom_bar(position = "dodge") +
  # Flip the x and y coordinates
  coord_flip()

ggplot(mtcars, aes(fcyl, fill = fam)) +
  # Set a dodge width of 0.5 for partially overlapping bars
  geom_bar(position = position_dodge(width = 0.5)) +
  coord_flip()

Horizontal bars are especially useful when the axis labels are long.

Another example:

mtcars <- read.csv("mtcars.csv", stringsAsFactors=FALSE)
mtcars <- mtcars %>% 
  mutate(fam = as.factor(am), fcyl = as.factor(cyl), car = model, fvs = as.factor(vs))
# Plot of wt vs. car
ggplot(mtcars, aes(car, wt)) +
  # Add a point layer
  geom_point() +
  labs(x = "car", y = "weight")

# Flip the axes to set car to the y axis
ggplot(mtcars, aes(car, wt)) +
  geom_point() +
  labs(x = "car", y = "weight") +
  coord_flip()

Pie charts

The coord_polar() function converts a planar x-y Cartesian plot to polar coordinates. This can be useful if you are producing pie charts.

We can imagine two forms for pie charts - the typical filled circle, or a colored ring.

Typical pie charts omit all of the non-data ink. Pie charts are not really better than stacked bar charts.

# Run the code, view the plot, then update it
ggplot(mtcars, aes(x = 1, fill = fcyl)) +
  geom_bar() +
  # Add a polar coordinate system
  coord_polar(theta = "y")

ggplot(mtcars, aes(x = 1, fill = fcyl)) +
  # Reduce the bar width to 0.1
  geom_bar(width = 0.1) +
  coord_polar(theta = "y") +
  # Add a continuous x scale from 0.5 to 1.5
  scale_x_continuous(limits = c(0.5, 1.5))

Polar coordinates are particularly useful if you are dealing with a cycle, like yearly data, that you would like to see represented as such.

Wind rose plots

Polar coordinate plots are well-suited to scales like compass direction or time of day. A popular example is the “wind rose”.

library(openair)
package 㤼㸱openair㤼㸲 was built under R version 3.6.3

Converting degrees into factor:

library(rvest)
Loading required package: xml2
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
-- Attaching packages --------------------------------------- tidyverse 1.3.0 --
v tibble  2.1.3     v purrr   0.3.3
v tidyr   1.0.2     v stringr 1.4.0
v readr   1.3.1     v forcats 0.4.0
-- Conflicts ------------------------------------------ tidyverse_conflicts() --
x lubridate::as.difftime() masks base::as.difftime()
x lubridate::date()        masks base::date()
x dplyr::filter()          masks stats::filter()
x readr::guess_encoding()  masks rvest::guess_encoding()
x lubridate::intersect()   masks base::intersect()
x dplyr::lag()             masks stats::lag()
x purrr::pluck()           masks rvest::pluck()
x lubridate::setdiff()     masks base::setdiff()
x lubridate::union()       masks base::union()
# grab a table of values from the web with a little scraping
url <- 'http://snowfence.umn.edu/Components/winddirectionanddegreeswithouttable3.htm'
page <- read_html(url)
directions_raw <- page %>% html_node('td table') %>% html_table(header = TRUE)
directions <- directions_raw %>% 
    set_names(~tolower(sub(' Direction', '', .x))) %>% 
    slice(-1) %>% 
    separate(degree, c('degree_min', 'degree_max'), sep = '\\s+-\\s+', convert = TRUE)
directions
wind <- mydata %>% 
    mutate(wd_cardinal = cut(
        wd, 
        breaks = c(0, directions$degree_max, 360), 
        labels = c(directions$cardinal, 'N')
    ), ws_interval = cut(ws,
                         breaks = c(0,2,4,6,8,10,12,14),
                         include.lowest=T)
    )
# Using wind, plot wd filled by ws
ggplot(wind, aes(wd_cardinal, fill = ws_interval)) +
  # Add a bar layer with width 1
  geom_bar(width = 1)

# Convert to polar coordinates:
ggplot(wind, aes(wd_cardinal, fill = ws_interval)) +
  geom_bar(width = 1) +
  coord_polar()

# Convert to polar coordinates:
ggplot(wind, aes(wd_cardinal, fill = ws_interval)) +
  geom_bar(width = 1) +
  coord_polar(start = -pi/16)

They are not common, but polar coordinate plots are really useful.

Facet

Facets layer

Faceting splits the data up into groups, according to a categorical variable, then plots each group in its own panel. For splitting the data by one or two categorical variables, facet_grid() is best.

Given categorical variables A and B, the code pattern is

plot +
  facet_grid(rows = vars(A), cols = vars(B))
  

This draws a panel for each pairwise combination of the values of A and B.

ggplot(mtcars, aes(wt, mpg)) + 
  geom_point() +
  # Facet rows by am
  facet_grid(rows = vars(am))

ggplot(mtcars, aes(wt, mpg)) + 
  geom_point() +
  # Facet columns by cyl
  facet_grid(cols=vars(cyl))

ggplot(mtcars, aes(wt, mpg)) + 
  geom_point() +
  # Facet rows by am and columns by cyl
  facet_grid(rows = vars(am), cols = vars(cyl))

Many variables

In addition to aesthetics, facets are another way of encoding factor (i.e. categorical) variables. They can be used to reduce the complexity of plots with many variables.

Two variables are mapped onto the color aesthetic, using hue and lightness. To achieve this we combined fcyl and fam into a single interaction variable, fcyl_fam. This will allow us to take advantage of Color Brewer’s Paired color palette.

mtcars <- mtcars %>% 
  mutate(fcyl_fam = interaction(fcyl, fam, sep=":"))
# See the interaction column
mtcars$fcyl_fam
 [1] 6:1 6:1 4:1 6:0 8:0 6:0 8:0 4:0 4:0 6:0 6:0 8:0 8:0 8:0 8:0 8:0 8:0 4:1 4:1 4:1 4:0 8:0 8:0 8:0 8:0 4:1
[27] 4:1 4:1 8:1 6:1 8:1 4:1
Levels: 4:0 6:0 8:0 4:1 6:1 8:1
# Color the points by fcyl_fam
ggplot(mtcars, aes(x = wt, y = mpg, color = fcyl_fam)) +
  geom_point() +
  # Use a paired color palette
  scale_color_brewer(palette = "Paired")

# Update the plot to map disp to size
ggplot(mtcars, aes(x = wt, y = mpg, color = fcyl_fam, size = disp)) +
  geom_point() +
  scale_color_brewer(palette = "Paired")

# Update the plot
ggplot(mtcars, aes(x = wt, y = mpg, color = fcyl_fam, size = disp)) +
  geom_point() +
  scale_color_brewer(palette = "Paired") +
  # Grid facet on gear and vs
  facet_grid(rows = vars(gear), cols = vars(vs)) 

The last plot you’ve created contains 7 variables (4 categorical, 3 continuous). Useful combinations of aesthetics and facets help to achieve this.

Formula notation

As well as the vars() notation for specifying which variables should be used to split the dataset into facets, there is also a traditional formula notation. The three cases are shown in the table.

Modern notation Formula notation
facet_grid(rows = vars(A)) facet_grid(A ~ .)
facet_grid(cols = vars(B)) facet_grid(. ~ B)
facet_grid(rows = vars(A), cols = vars(B)) facet_grid(A ~ B)
ggplot(mtcars, aes(wt, mpg)) + 
  geom_point() +
  # Facet rows by am using formula notation
  facet_grid(am ~ .)

ggplot(mtcars, aes(wt, mpg)) + 
  geom_point() +
  # Facet columns by cyl using formula notation
  facet_grid(. ~ cyl)

ggplot(mtcars, aes(wt, mpg)) + 
  geom_point() +
  # Facet rows by am and columns by cyl using formula notation
  facet_grid(am ~ cyl)

While many ggplots still use the traditional formula notation, using vars() is now preferred.

Facet labels and order

Labelling facets

If our factor levels are not clear, your facet labels may be confusing. We can assign proper labels in your original data before plotting or we can use the labeller argument in the facet layer.

The default value is

  • label_value: Default, displays only the value

Common alternatives are:

  • label_both: Displays both the value and the variable name
  • label_context: Displays only the values or both the values and variables depending on whether multiple factors are faceted
# Plot wt by mpg
ggplot(mtcars, aes(wt, mpg)) +
  geom_point() +
  # The default is label_value
  facet_grid(cols = vars(cyl))

O

# Plot wt by mpg
ggplot(mtcars, aes(wt, mpg)) +
  geom_point() +
  # Displaying both the values and the variables
  facet_grid(cols = vars(cyl), labeller = label_both)

# Plot wt by mpg
ggplot(mtcars, aes(wt, mpg)) +
  geom_point() +
  # Label context
  facet_grid(cols = vars(cyl), labeller = label_context)

# Plot wt by mpg
ggplot(mtcars, aes(wt, mpg)) +
  geom_point() +
  # Two variables
  facet_grid(cols = vars(vs, cyl), labeller = label_context)

Make sure there is no ambiguity in interpreting plots by using proper labels.

Setting order

If you want to change the order of your facets, it’s best to properly define your factor variables before plotting.

Let’s see this in action with the mtcars transmission variable am. In this case, 0 = “automatic” and 1 = “manual”.

Here, we’ll make am a factor variable and relabel the numbers to proper names. The default order is alphabetical. To rearrange them we’ll call fct_rev() from the forcats package to reverse the order.

library(forcats)

# Make factor, set proper labels explictly
mtcars$fam <- factor(mtcars$am, labels = c(`0` = "automatic",
                                           `1` = "manual"))

# Default order is alphabetical
ggplot(mtcars, aes(wt, mpg)) +
  geom_point() +
  facet_grid(cols = vars(fam))

# Make factor, set proper labels explictly, and
# manually set the label order
mtcars$fam <- factor(mtcars$am,
                     levels = c(1, 0),
                     labels = c("manual", "automatic"))
mtcars$fvs <- factor(mtcars$vs,
                     levels = c(1, 0),
                     labels = c("straight", "V-shaped"))

# View again
ggplot(mtcars, aes(wt, mpg)) +
  geom_point() +
  facet_grid(cols = vars(fam))

Arrange your facets in an intuitive order for your data.

Facet plotting spaces

Continuous variables

By default every facet of a plot has the same axes. If the data ranges vary wildly between facets, it can be clearer if each facet has its own scale. This is achieved with the scales argument to facet_grid().

  • “fixed” (default): axes are shared between facets.
  • free: each facet has its own axes.
  • free_x: each facet has its own x-axis, but the y-axis is shared.
  • free_y: each facet has its own y-axis, but the x-axis is shared.

When faceting by columns, “free_y” has no effect, but we can adjust the x-axis. In contrast, when faceting by rows, “free_x” has no effect, but we can adjust the y-axis.

ggplot(mtcars, aes(wt, mpg)) +
  geom_point() + 
  # Facet columns by cyl 
  facet_grid(cols = vars(cyl))

ggplot(mtcars, aes(wt, mpg)) +
  geom_point() + 
  # Facet columns by cyl 
  facet_grid(cols = vars(cyl),
             scales = "free_x")

ggplot(mtcars, aes(wt, mpg)) +
  geom_point() + 
  # Swap cols for rows; free the y-axis scales
  facet_grid(rows = vars(cyl), scales = "free_y")

Shared scales make it easy to compare between facets, but can be confusing if the data ranges are very different. In that case, used free scales.

Categorical variables

When you have a categorical variable with many levels which are not all present in each sub-group of another variable, it’s usually desirable to drop the unused levels.

By default, each facet of a plot is the same size. This behavior can be changed with the spaces argument, which works in the same way as scales: “free_x” allows different sized facets on the x-axis, “free_y”, allows different sized facets on the y-axis, “free” allows different sizes in both directions.

ggplot(mtcars, aes(x = mpg, y = car, color = fam)) +
  geom_point() +
  # Facet rows by gear
  facet_grid(rows = vars(gear))

ggplot(mtcars, aes(x = mpg, y = car, color = fam)) +
  geom_point() +
  # Free the y scales and space
  facet_grid(rows = vars(gear),
  scales = "free_y",
  space = "free_y")

mtcars2 <- mtcars %>% 
  # arrange from lo to hi
  arrange(-mpg) %>% 
  # redefine in factor leveles
  mutate(car = as_factor(car))
ggplot(mtcars2, aes(x = mpg, y = car, color = fam)) +
  geom_point() +
  # Free the y scales and space
  facet_grid(rows = vars(gear),
  scales = "free_y",
  space = "free_y")

Freeing the y-scale to remove blank lines helps focus attention on the actual data present.

Facet wrap and margins

Wrapping for many levels

facet_grid() is fantastic for categorical variables with a small number of levels. Although it is possible to facet variables with many levels, the resulting plot will be very wide or very tall, which can make it difficult to view.

The solution is to use facet_wrap() which separates levels along one axis but wraps all the subsets across a given number of rows or columns.

library(carData)
ggplot(Vocab, aes(x = education, y = vocabulary)) +
  stat_smooth(method = "lm", se = FALSE) +
  # Create facets, wrapping by year, using vars()
  facet_wrap(vars(year))

ggplot(Vocab, aes(x = education, y = vocabulary)) +
  stat_smooth(method = "lm", se = FALSE) +
  # Create facets, wrapping by year, using a formula
  facet_wrap(~ year)

ggplot(Vocab, aes(x = education, y = vocabulary)) +
  stat_smooth(method = "lm", se = FALSE) +
  # Update the facet layout, using 11 columns
  facet_wrap(~ year,
  ncol = 11)

Start experimenting with facets in your own plots.

Margin plots

Facets are great for seeing subsets in a variable, but sometimes you want to see both those subsets and all values in a variable.

Here, the margins argument to facet_grid() is your friend.

  • FALSE (default): no margins.
  • TRUE: add margins to every variable being faceted by.
  • c(“variable1”, “variable2”): only add margins to the variables listed.
ggplot(mtcars, aes(x = wt, y = mpg)) + 
  geom_point() +
  # Facet rows by fvs and cols by fam
  facet_grid(cols = vars(gear), rows = vars(fvs, fam))

ggplot(mtcars, aes(x = wt, y = mpg)) + 
  geom_point() +
  # Update the facets to add margins
  facet_grid(rows = vars(fvs, fam), cols = vars(gear),
  margins = TRUE)

ggplot(mtcars, aes(x = wt, y = mpg)) + 
  geom_point() +
  # Update the facets to only show margins on fam
  facet_grid(rows = vars(fvs, fam), cols = vars(gear), margins = "fam")

ggplot(mtcars, aes(x = wt, y = mpg)) + 
  geom_point() +
  # Update the facets to only show margins on gear and fvs
  facet_grid(rows = vars(fvs, fam), cols = vars(gear), margins = c("gear", "fvs"))

It can be really helpful to show the full margin plots!

Best Practices

Bar plots

Dynamite plots

“Dynamite plots” (bar plots with error bars) are not well suited for their intended purpose of depicting distributions. If we really want error bars on bar plots, we can of course get them, but we’ll need to set the positions manually. A point geom will typically serve you much better.

# Plot wt vs. fcyl
ggplot(mtcars, aes(x = fcyl, y = wt)) +
  # Add a bar summary stat of means, colored skyblue
  stat_summary(fun.y = mean, geom = "bar", fill = "skyblue") +
  # Add an errorbar summary stat std deviation limits
  stat_summary(fun.data = mean_sdl, fun.args = list(mult = 1), geom = "errorbar", width = 0.1)
`fun.y` is deprecated. Use `fun` instead.

Remember, we can specify any function in fun.data or fun.y and we can also specify any geom, as long as it’s appropriate to the data type.

Position dodging

we will add a distinction between transmission type, fam, for the dynamite plots and explore position dodging (where bars are side-by-side).

# Update the aesthetics to color and fill by fam
ggplot(mtcars, aes(x = fcyl, y = wt, color = fam, fill = fam)) +
  stat_summary(fun.y = mean, geom = "bar") +
  stat_summary(fun.data = mean_sdl, fun.args = list(mult = 1), geom = "errorbar", width = 0.1)
`fun.y` is deprecated. Use `fun` instead.

# Set alpha for the first and set position for each stat summary function
ggplot(mtcars, aes(x = fcyl, y = wt, color = fam, fill = fam)) +
  stat_summary(fun.y = mean, geom = "bar", alpha = 0.5, position = "dodge") +
  stat_summary(fun.data = mean_sdl, fun.args = list(mult = 1), geom = "errorbar", position = "dodge", width = 0.1)
`fun.y` is deprecated. Use `fun` instead.

# Define a dodge position object with width 0.9
posn_d <- position_dodge(width = 0.9)

# For each summary stat, update the position to posn_d
ggplot(mtcars, aes(x = fcyl, y = wt, color = fam, fill = fam)) +
  stat_summary(fun.y = mean, geom = "bar", position = posn_d, alpha = 0.5) +
  stat_summary(fun.data = mean_sdl, fun.args = list(mult = 1), width = 0.1, position = posn_d, geom = "errorbar")
`fun.y` is deprecated. Use `fun` instead.

Slightly overlapping bar plots are common in the popular press and add a bit of style to your data viz.

Using aggregated data

If it is appropriate to use bar plots, then it nice to give an impression of the number of values in each group.

stat_summary() doesn’t keep track of the count. stat_sum() does (that’s the whole point), but it’s difficult to access. It’s more straightforward to calculate exactly what we want to plot ourselves.

mtcars_by_cyl <- mtcars %>%
  group_by(cyl) %>% 
  summarize(mean_wt = mean(wt),
          sd_wt = sd(wt),
          n_wt = n()) %>% 
          mutate( prop = n_wt/sum(n_wt))
mtcars_by_cyl
# Using mtcars_cyl, plot mean_wt vs. cyl
ggplot(mtcars_by_cyl, aes(cyl, mean_wt)) +
  # Add a bar layer with identity stat, filled skyblue
  geom_bar(stat = "identity", fill = "skyblue")

ggplot(mtcars_by_cyl, aes(x = cyl, y = mean_wt)) +
  # Swap geom_bar() for geom_col()
  geom_col(fill = "skyblue")

ggplot(mtcars_by_cyl, aes(x = cyl, y = mean_wt)) +
  # Set the width aesthetic to prop
  geom_col(fill = "skyblue", aes(width = prop))
Ignoring unknown aesthetics: width

ggplot(mtcars_by_cyl, aes(x = cyl, y = mean_wt)) +
  geom_col(aes(width = prop), fill = "skyblue") +
  # Add an errorbar layer
  geom_errorbar(
    # ... at mean weight plus or minus 1 std dev
    aes(ymin = mean_wt - sd_wt,
    ymax = mean_wt + sd_wt),
    # with width 0.1
    width = 0.1
  )
Ignoring unknown aesthetics: width

This is a good start, but it’s difficult to adjust the spacing between the bars.

Heatmaps

Since heat maps encode color on a continuous scale, they are difficult to accurately decode. Hence, heat maps are most useful if you have a small number of boxes and/or a clear pattern that allows you to overcome decoding difficulties.

To produce them, map two categorical variables onto the x and y aesthetics, along with a continuous variable onto fill. The geom_tile() layer adds the boxes.

library(lattice)
str(barley)
'data.frame':   120 obs. of  4 variables:
 $ yield  : num  27 48.9 27.4 39.9 33 ...
 $ variety: Factor w/ 10 levels "Svansota","No. 462",..: 3 3 3 3 3 3 7 7 7 7 ...
 $ year   : Factor w/ 2 levels "1932","1931": 2 2 2 2 2 2 2 2 2 2 ...
 $ site   : Factor w/ 6 levels "Grand Rapids",..: 3 6 4 5 1 2 3 6 4 5 ...
# Using barley, plot variety vs. year, filled by yield
ggplot(barley, aes(x = year, y = variety, fill = yield)) +
  # Add a tile geom
  geom_tile()

# Previously defined
ggplot(barley, aes(x = year, y = variety, fill = yield)) +
  geom_tile() + 
  # Facet, wrapping by site, with 1 column
  facet_wrap(facets = vars(site), ncol = 1) +
  # Add a fill scale using an 2-color gradient
  scale_fill_gradient(low = "white", high = "red")

library(RColorBrewer)
# A palette of 9 reds
red_brewer_palette <- brewer.pal(9, "Reds")

# Update the plot
ggplot(barley, aes(x = year, y = variety, fill = yield)) +
  geom_tile() + 
  facet_wrap(facets = vars(site), ncol = 1) +
  # Update scale to use n-colors from red_brewer_palette
  scale_fill_gradientn(colors = red_brewer_palette)

We can continue by using breaks, limits and labels to modify the fill scale and update the theme, but this is a pretty good start.

Heat map alternatives

There are several alternatives to heat maps. The best choice really depends on the data and the story you want to tell with this data. If there is a time component, the most obvious choice is a line plot.

# Using barley, plot yield vs. year, colored and grouped by variety
ggplot(barley, aes(x = year, y = yield, color = variety, group = variety)) +
  # Add a line layer
  geom_line() +
  # Facet, wrapping by site, with 1 row
  facet_wrap( ~ site, nrow = 1)

# Using barely, plot yield vs. year, colored, grouped, and filled by site
ggplot(barley, aes(x = year, y = yield, color = site, group = site, fill = site)) +
  # Add a line summary stat aggregated by mean
  stat_summary(fun.y = mean, geom = "line") +
  # Add a ribbon summary stat with 10% opacity, no color
  stat_summary(fun.data = mean_sdl, fun.args = list(mult = 1), geom = "ribbon", alpha = 0.1, color = NA)
`fun.y` is deprecated. Use `fun` instead.

Whenever you see a heat map, ask yourself it it’s really necessary. Many people use them because they look fancy and complicated - signs of poor communication skills.

When good data makes bad plots

When you first encounter a data visualization, either from yourself or a colleague, you always want to critically ask if it’s obscuring the data in any way.

Let’s take a look at the steps we could take to produce and improve the plot in the view.

The data comes from an experiment where the effect of two different types of vitamin C sources, orange juice or ascorbic acid, were tested on the growth of the odontoblasts (cells responsible for tooth growth) in 60 guinea pigs.

The data is stored in the TG data frame, which contains three variables: dose, len, and supp.

library(datasets)
TG = (ToothGrowth)
str(TG)
'data.frame':   60 obs. of  3 variables:
 $ len : num  4.2 11.5 7.3 5.8 6.4 10 11.2 11.2 5.2 7 ...
 $ supp: Factor w/ 2 levels "OJ","VC": 2 2 2 2 2 2 2 2 2 2 ...
 $ dose: num  0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 ...
# Initial plot
growth_by_dose <- ggplot(TG, aes(dose, len, color = supp)) +
  stat_summary(fun.data = mean_sdl,
               fun.args = list(mult = 1),
               position = position_dodge(0.1)) +
  theme_gray(3)

# View plot
growth_by_dose

# Initial plot
growth_by_dose <- ggplot(TG, aes(dose, len, color = supp)) +
  stat_summary(fun.data = mean_sdl,
               fun.args = list(mult = 1),
               position = position_dodge(0.1)) +
  theme_classic()

# View plot
growth_by_dose

# Change type
TG$dose <- as.numeric(as.character(TG$dose))

# Plot
growth_by_dose <- ggplot(TG, aes(dose, len, color = supp)) +
  stat_summary(fun.data = mean_sdl,
               fun.args = list(mult = 1),
               position = position_dodge(0.2)) +
  theme_classic()

# View plot
growth_by_dose

# Change type
TG$dose <- as.numeric(as.character(TG$dose))

# Plot
growth_by_dose <- ggplot(TG, aes(dose, len, color = supp)) +
  stat_summary(fun.data = mean_sdl,
               fun.args = list(mult = 1),
               position = position_dodge(0.2)) +
  # Use the right geometry
  stat_summary(fun.y = mean,
               geom = "line",
               position = position_dodge(0.1)) +
  theme_classic()
`fun.y` is deprecated. Use `fun` instead.
# View plot
growth_by_dose

# Change type
TG$dose <- as.numeric(as.character(TG$dose))

# Plot
growth_by_dose <- ggplot(TG, aes(dose, len, color = supp)) +
  stat_summary(fun.data = mean_sdl,
               fun.args = list(mult = 1),
               position = position_dodge(0.2)) +
  stat_summary(fun.y = mean,
               geom = "line",
               position = position_dodge(0.1)) +
  theme_classic() +
  # Adjust labels and colors:
  labs(x = "Dose (mg/day)", y = "Odontoblasts length (mean, standard deviation)", color = "Supplement") +
  scale_color_brewer(palette = "Set1", labels = c("Orange juice", "Ascorbic acid")) +
  scale_y_continuous(limits = c(0,35), breaks = seq(0, 35, 5), expand = c(0,0))
`fun.y` is deprecated. Use `fun` instead.
# View plot
growth_by_dose

LS0tDQp0aXRsZTogIkludGVybWVkaWF0ZSBEYXRhIFZpc3VhbGl6YXRpb24gd2l0aCBnZ3Bsb3QyIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIHRvY19jb2xsYXBzZWQ6IGZhbHNlDQogICAgDQp0b2NfZGVwdGg6IDMNCi0tLQ0KIyBTdGF0aXN0aWNzDQoNCiMjIFNtb290aGluZw0KDQpUaGUgbXRjYXJzIGRhdGFzZXQgY29udGFpbnMgaW5mb3JtYXRpb24gZm9yIDMyIGNhcnMgZnJvbSBNb3RvciBUcmVuZHMgbWFnYXppbmUgZnJvbSAxOTc0LiBUaGlzIGRhdGFzZXQgaXMgc21hbGwsIGludHVpdGl2ZSwgYW5kIGNvbnRhaW5zIGEgdmFyaWV0eSBvZiBjb250aW51b3VzIGFuZCBjYXRlZ29yaWNhbCAoYm90aCBub21pbmFsIGFuZCBvcmRpbmFsKSB2YXJpYWJsZXMuDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIgKQ0KYGBgDQoNCmBgYHtyfQ0KIyBWaWV3IHRoZSBzdHJ1Y3R1cmUgb2YgbXRjYXJzDQpzdHIoY2FycykNCmBgYA0KYGBge3J9DQojIFVzaW5nIG10Y2FycywgZHJhdyBhIHNjYXR0ZXIgcGxvdCBvZiBtcGcgdnMuIHd0DQpnZ3Bsb3QobXRjYXJzLCBhZXMod3QsIG1wZykpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zbW9vdGgoKQ0KYGBgDQpgYGB7cn0NCiMgQW1lbmQgdGhlIHBsb3QuIFVzZSBsaW4uIHJlZy4gc21vb3RoaW5nOyB0dXJuIG9mZiBzdGQgZXJyIHJpYmJvbg0KZ2dwbG90KG10Y2FycywgYWVzKHggPSB3dCwgeSA9IG1wZykpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkNCmBgYA0KYGBge3J9DQojIEFtZW5kIHRoZSBwbG90LiBTd2FwIGdlb21fc21vb3RoKCkgZm9yIHN0YXRfc21vb3RoKCkuDQpnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IHd0LCB5ID0gbXBnKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFKQ0KYGBgDQpZb3UgY2FuIHVzZSBlaXRoZXIgc3RhdF9zbW9vdGgoKSBvciBnZW9tX3Ntb290aCgpIHRvIGFwcGx5IGEgbGluZWFyIG1vZGVsLg0KDQojIyBHcm91cGluZyB2YXJpYWJsZXMNCg0KV2UnbGwgY29udGludWUgYnkgY29uc2lkZXJpbmcgdGhlIHNpdHVhdGlvbiBvZiBsb29raW5nIGF0IHN1Yi1ncm91cHMgaW4gb3VyIGRhdGFzZXQuIEZvciB0aGlzIHdlJ2xsIGVuY291bnRlciB0aGUgaW52aXNpYmxlIGdyb3VwIGFlc3RoZXRpYy4NCmBgYHtyfQ0KI2NyZWF0ZSBmYWN0b3IgY3lsIGFuZCBhbQ0KbXRjYXJzIDwtIG10Y2FycyAlPiUgDQogIG11dGF0ZShmY3lsID0gYXMuZmFjdG9yKGN5bCksDQogICAgICAgICBmYW0gPSBhcy5mYWN0b3IoYW0pKQ0KYGBgDQpgYGB7cn0NCiMgVXNpbmcgbXRjYXJzLCBwbG90IG1wZyB2cy4gd3QsIGNvbG9yZWQgYnkgZmN5bA0KZ2dwbG90KG10Y2FycywgYWVzKHd0LCBtcGcsIGNvbG9yID0gZmN5bCkpICsNCiAgIyBBZGQgYSBwb2ludCBsYXllcg0KICBnZW9tX3BvaW50KCkgKw0KICAjIEFkZCBhIHNtb290aCBsaW4gcmVnIHN0YXQsIG5vIHJpYmJvbg0KICBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFKQ0KYGBgDQpgYGB7cn0NCiMgQW1lbmQgdGhlIHBsb3QgdG8gYWRkIGFub3RoZXIgc21vb3RoIGxheWVyIHdpdGggZHVtbXkgZ3JvdXBpbmcNCmdncGxvdChtdGNhcnMsIGFlcyh4ID0gd3QsIHkgPSBtcGcsIGNvbG9yID0gZmN5bCkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgc3RhdF9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkgKw0KICBzdGF0X3Ntb290aChtYXBwaW5nID0gYWVzKGdyb3VwID0gMSksIG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UpDQpgYGANCk5vdGljZSB0aGF0IHRoZSBjb2xvciBhZXN0aGV0aWMgZGVmaW5lZCBhbiBpbnZpc2libGUgZ3JvdXAgYWVzdGhldGljLiBEZWZpbmluZyB0aGUgZ3JvdXAgYWVzdGhldGljIGZvciBhIHNwZWNpZmljIGdlb20gbWVhbnMgd2UgY2FuIG92ZXJ3cml0ZSB0aGF0LiBIZXJlLCB3ZSB1c2UgYSBkdW1teSB2YXJpYWJsZSB0byBjYWxjdWxhdGUgdGhlIHNtb290aGluZyBtb2RlbCBmb3IgYWxsIHZhbHVlcy4NCg0KIyMgTW9kaWZ5aW5nIHN0YXRfc21vb3RoDQoNCldlJ2xsIGNvbnNpZGVyIGFub3RoZXIgYXJndW1lbnQsIHNwYW4sIHVzZWQgaW4gTE9FU1Mgc21vb3RoaW5nLCBhbmQgd2UnbGwgdGFrZSBhIGxvb2sgYXQgYSBuaWNlIHNjZW5hcmlvIG9mIHByb3Blcmx5IG1hcHBpbmcgZGlmZmVyZW50IG1vZGVscy4NCmBgYHtyfQ0KZ2dwbG90KG10Y2FycywgYWVzKHggPSB3dCwgeSA9IG1wZykpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgIyBBZGQgMyBzbW9vdGggTE9FU1Mgc3RhdHMsIHZhcnlpbmcgc3BhbiAmIGNvbG9yDQogIHN0YXRfc21vb3RoKGNvbG9yID0gInJlZCIsIHNlID0gRkFMU0UsIHNwYW4gPSAwLjkpICsNCiAgc3RhdF9zbW9vdGgoY29sb3IgPSAiZ3JlZW4iLCBzZSA9IEZBTFNFLCBzcGFuID0gMC42KSArDQogIHN0YXRfc21vb3RoKGNvbG9yID0gImJsdWUiLCBzZSA9IEZBTFNFLCBzcGFuID0gMC4zKQ0KYGBgDQpgYGB7cn0NCiMgQW1lbmQgdGhlIHBsb3QgdG8gY29sb3IgYnkgZmN5bA0KZ2dwbG90KG10Y2FycywgYWVzKHggPSB3dCwgeSA9IG1wZykpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgIyBBZGQgYSBzbW9vdGggTE9FU1Mgc3RhdCwgbm8gcmliYm9uDQogIHN0YXRfc21vb3RoKHNlID0gRkFMU0UpICsNCiAgIyBBZGQgYSBzbW9vdGggbGluLiByZWcuIHN0YXQsIG5vIHJpYmJvbg0KICBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFKQ0KYGBgDQpgYGB7cn0NCiMgQW1lbmQgdGhlIHBsb3QNCmdncGxvdChtdGNhcnMsIGFlcyh4ID0gd3QsIHkgPSBtcGcsIGNvbG9yID0gZmN5bCkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgIyBNYXAgY29sb3IgdG8gZHVtbXkgdmFyaWFibGUgIkFsbCINCiAgc3RhdF9zbW9vdGgobWFwcGluZyA9IGFlcyhjb2xvciA9ICJBbGwiKSwgc2UgPSBGQUxTRSkgKw0KICBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFKQ0KYGBgDQpUaGUgZGVmYXVsdCBzcGFuIGZvciBMT0VTUyBpcyAwLjkuIEEgbG93ZXIgc3BhbiB3aWxsIHJlc3VsdCBpbiBhIGJldHRlciBmaXQgd2l0aCBtb3JlIGRldGFpbDsgYnV0IGRvbid0IG92ZXJkbyBpdCBvciB5b3UnbGwgZW5kIHVwIG92ZXItZml0dGluZyENCg0KV2UnbGwgdGFrZSBhIGxvb2sgYXQgdGhlIHN0YW5kYXJkIGVycm9yIHJpYmJvbnMsIHdoaWNoIHNob3cgdGhlIDk1JSBjb25maWRlbmNlIGludGVydmFsIG9mIHNtb290aGluZyBtb2RlbHMuIA0KYGBge3J9DQpsaWJyYXJ5KGNhckRhdGEpDQpgYGANClZvY2FiIGhhcyBiZWVuIGdpdmVuIGFuIGV4dHJhIGNvbHVtbiwgeWVhcl9ncm91cCwgc3BsaXR0aW5nIHRoZSBkYXRlcyBpbnRvIGJlZm9yZSBhbmQgYWZ0ZXIgMTk5NS4NCmBgYHtyfQ0KVm9jYWIgPC0gVm9jYWIgJT4lIA0KICBtdXRhdGUoeWVhcl9ncm91cCA9IGFzLmZhY3RvcihpZmVsc2UoeWVhcjwxOTk1LCAiWzE5NzQsMTk5NV0iLCAiWzE5OTUsMjAxNl0iKSApKQ0KYGBgDQoNCg0KYGBge3J9DQojIFVzaW5nIFZvY2FiLCBwbG90IHZvY2FidWxhcnkgdnMuIGVkdWNhdGlvbiwgY29sb3JlZCBieSB5ZWFyIGdyb3VwDQpnZ3Bsb3QoVm9jYWIsIGFlcyhlZHVjYXRpb24sIHZvY2FidWxhcnksIGNvbG9yID0geWVhcl9ncm91cCkpICsNCiAgIyBBZGQgaml0dGVyZWQgcG9pbnRzIHdpdGggdHJhbnNwYXJlbmN5IDAuMjUNCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjI1KSArDQogICMgQWRkIGEgc21vb3RoIGxpbi4gcmVnLiBsaW5lICh3aXRoIHJpYmJvbikNCiAgc3RhdF9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkNCmBgYA0KSXQncyBlYXNpZXIgdG8gcmVhZCB0aGUgcGxvdCBpZiB0aGUgc3RhbmRhcmQgZXJyb3IgcmliYm9ucyBtYXRjaCB0aGUgbGluZXMsIGFuZCB0aGUgbGluZXMgaGF2ZSBtb3JlIGVtcGhhc2lzLg0KDQpgYGB7cn0NCiMgQW1lbmQgdGhlIHBsb3QNCmdncGxvdChWb2NhYiwgYWVzKHggPSBlZHVjYXRpb24sIHkgPSB2b2NhYnVsYXJ5LCBjb2xvciA9IHllYXJfZ3JvdXApKSArDQogIGdlb21faml0dGVyKGFscGhhID0gMC4yNSkgKw0KICAjIE1hcCB0aGUgZmlsbCBjb2xvciB0byB5ZWFyX2dyb3VwLCBzZXQgdGhlIGxpbmUgc2l6ZSB0byAyDQogIHN0YXRfc21vb3RoKG1ldGhvZCA9ICJsbSIsc2l6ZSA9IDIsIG1hcHBpbmc9IGFlcyhmaWxsID0geWVhcl9ncm91cCkpDQpgYGANCk5vdGljZSB0aGF0IHNpbmNlIDE5OTUsIGVkdWNhdGlvbiBoYXMgcmVsYXRpdmVseSBzbWFsbGVyIGVmZmVjdCBvbiBpbmNyZWFzaW5nIHZvY2FidWxhcnkuDQoNCiMjIFF1YW50aWxlcw0KDQp3ZSdsbCB1c2Ugc3RhdF9xdWFudGlsZSgpIHRvIGFwcGx5IGEgcXVhbnRpbGUgcmVncmVzc2lvbi4NCg0KTGluZWFyIHJlZ3Jlc3Npb24gcHJlZGljdHMgdGhlIG1lYW4gcmVzcG9uc2UgZnJvbSB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGVzLCBxdWFudGlsZSByZWdyZXNzaW9uIHByZWRpY3RzIGEgcXVhbnRpbGUgcmVzcG9uc2UgKGUuZy4gdGhlIG1lZGlhbikgZnJvbSB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGVzLiBTcGVjaWZpYyBxdWFudGlsZXMgY2FuIGJlIHNwZWNpZmllZCB3aXRoIHRoZSBxdWFudGlsZXMgYXJndW1lbnQuDQoNClNwZWNpZnlpbmcgbWFueSBxdWFudGlsZXMgYW5kIGNvbG9yIHlvdXIgbW9kZWxzIGFjY29yZGluZyB0byB5ZWFyIGNhbiBtYWtlIHBsb3RzIHRvbyBidXN5Lg0KYGBge3J9DQpnZ3Bsb3QoVm9jYWIsIGFlcyh4ID0gZWR1Y2F0aW9uLCB5ID0gdm9jYWJ1bGFyeSkpICsNCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjI1KSArDQogICMgQWRkIGEgcXVhbnRpbGUgc3RhdCwgYXQgMC4wNSwgMC41LCBhbmQgMC45NQ0KICBzdGF0X3F1YW50aWxlKHF1YW50aWxlcyA9IGMoMC4wNSwgMC41LCAwLjk1KSkNCmBgYA0KYGBge3J9DQojIEFtZW5kIHRoZSBwbG90IHRvIGNvbG9yIGJ5IHllYXJfZ3JvdXANCmdncGxvdChWb2NhYiwgYWVzKHggPSBlZHVjYXRpb24sIHkgPSB2b2NhYnVsYXJ5LCBjb2xvciA9IHllYXJfZ3JvdXApKSArDQogIGdlb21faml0dGVyKGFscGhhID0gMC4yNSkgKw0KICBzdGF0X3F1YW50aWxlKHF1YW50aWxlcyA9IGMoMC4wNSwgMC41LCAwLjk1KSkNCmBgYA0KUXVhbnRpbGUgcmVncmVzc2lvbiBpcyBhIGdyZWF0IHRvb2wgZm9yIGdldHRpbmcgYSBtb3JlIGRldGFpbGVkIG92ZXJ2aWV3IG9mIGEgbGFyZ2UgZGF0YXNldC4NCg0KIyMgVXNpbmcgc3RhdF9zdW0NCg0KMSBzb2x1dGlvbiBmb3Igb3ZlcnBsb3R0aW5nIGlzIGppdHRlcmluZyB3aXRoIHRyYW5zcGFyZW5jeS4gQW5vdGhlciBzb2x1dGlvbiBpcyB0byB1c2Ugc3RhdF9zdW0oKSwgd2hpY2ggY2FsY3VsYXRlcyB0aGUgdG90YWwgbnVtYmVyIG9mIG92ZXJsYXBwaW5nIG9ic2VydmF0aW9ucyBhbmQgbWFwcyB0aGF0IG9udG8gdGhlIHNpemUgYWVzdGhldGljLg0KDQpzdGF0X3N1bSgpIGFsbG93cyBhIHNwZWNpYWwgdmFyaWFibGUsIC4ucHJvcC4uLCB0byBzaG93IHRoZSBwcm9wb3J0aW9uIG9mIHZhbHVlcyB3aXRoaW4gdGhlIGRhdGFzZXQuDQpgYGB7cn0NCiMgUnVuIHRoaXMsIGxvb2sgYXQgdGhlIHBsb3QsIHRoZW4gdXBkYXRlIGl0DQpnZ3Bsb3QoVm9jYWIsIGFlcyh4ID0gZWR1Y2F0aW9uLCB5ID0gdm9jYWJ1bGFyeSkpICsNCiAgIyBSZXBsYWNlIHRoaXMgd2l0aCBhIHN1bSBzdGF0DQogIGdlb21faml0dGVyKGFscGhhID0gMC4yNSkNCmBgYA0KYGBge3J9DQojIFJ1biB0aGlzLCBsb29rIGF0IHRoZSBwbG90LCB0aGVuIHVwZGF0ZSBpdA0KZ2dwbG90KFZvY2FiLCBhZXMoeCA9IGVkdWNhdGlvbiwgeSA9IHZvY2FidWxhcnkpKSArDQogICMgUmVwbGFjZSB0aGlzIHdpdGggYSBzdW0gc3RhdA0KICBzdGF0X3N1bSgpDQpgYGANCmBgYHtyfQ0KZ2dwbG90KFZvY2FiLCBhZXMoeCA9IGVkdWNhdGlvbiwgeSA9IHZvY2FidWxhcnkpKSArDQogIHN0YXRfc3VtKCkgKw0KICAjIEFkZCBhIHNpemUgc2NhbGUsIGZyb20gMSB0byAxMA0KICBzY2FsZV9zaXplKHJhbmdlID0gYygxLDEwKSkNCmBgYA0KYGBge3J9DQojIEFtZW5kIHRoZSBzdGF0IHRvIHVzZSBwcm9wb3J0aW9uIHNpemVzDQpnZ3Bsb3QoVm9jYWIsIGFlcyh4ID0gZWR1Y2F0aW9uLCB5ID0gdm9jYWJ1bGFyeSkpICsNCiAgc3RhdF9zdW0oYWVzKHNpemUgPS4ucHJvcC4uKSkNCmBgYA0KYGBge3J9DQojIEFtZW5kIHRoZSBwbG90IHRvIGdyb3VwIGJ5IGVkdWNhdGlvbg0KZ2dwbG90KFZvY2FiLCBhZXMoeCA9IGVkdWNhdGlvbiwgeSA9IHZvY2FidWxhcnksIGdyb3VwID0gZWR1Y2F0aW9uKSkgKw0KICBzdGF0X3N1bShhZXMoc2l6ZSA9IC4ucHJvcC4uKSkNCmBgYA0KIElmIGEgZmV3IGRhdGEgcG9pbnRzIG92ZXJsYXAsIGppdHRlcmluZyBpcyBncmVhdC4gV2hlbiB5b3UgaGF2ZSBsb3RzIG9mIG92ZXJsYXBzIChwYXJ0aWN1bGFybHkgd2hlcmUgY29udGludW91cyBkYXRhIGhhcyBiZWVuIHJvdW5kZWQpLCB1c2luZyBzdGF0X3N1bSgpIHRvIGNvdW50IHRoZSBvdmVybGFwcyBpcyBtb3JlIHVzZWZ1bC4NCg0KIyMgU3RhdHMgb3V0c2lkZSBnZW9tDQoNCiMjIyBQcmVwYXJhdGlvbnMNCg0KV2UnbGwgYWltIHRvIG1ha2UgdGhlIGJlbG93IHBsb3Q6DQoNCiFbc3RhdHNdKGltYWdlcy9zdGF0cy5wbmcpDQoNCldlJ2xsIGVzdGFibGlzaCBvdXIgcG9zaXRpb25zIGFuZCBiYXNlIGxheWVyIG9mIHRoZSBwbG90Lg0KDQpFc3RhYmxpc2hpbmcgdGhlc2UgaXRlbXMgYXMgaW5kZXBlbmRlbnQgb2JqZWN0cyB3aWxsIGFsbG93IHVzIHRvIHJlY3ljbGUgdGhlbSBlYXNpbHkgaW4gbWFueSBsYXllcnMsIG9yIHBsb3RzLg0KDQotIHBvc2l0aW9uX2ppdHRlcigpIGFkZHMgaml0dGVyaW5nIChlLmcuIGZvciBwb2ludHMpLg0KLSBwb3NpdGlvbl9kb2RnZSgpIGRvZGdlcyBnZW9tcywgKGUuZy4gYmFyLCBjb2wsIGJveHBsb3QsIHZpb2xpbiwgZXJyb3JiYXIsIHBvaW50cmFuZ2UpLg0KLSBwb3NpdGlvbl9qaXR0ZXJkb2RnZSgpIGppdHRlcnMgYW5kIGRvZGdlcyBnZW9tcywgKGUuZy4gcG9pbnRzKS4NCg0KYGBge3J9DQojIERlZmluZSBwb3NpdGlvbiBvYmplY3RzDQojIDEuIEppdHRlciB3aXRoIHdpZHRoIDAuMg0KcG9zbl9qIDwtIHBvc2l0aW9uX2ppdHRlcih3aWR0aCA9IDAuMikNCiMgMi4gRG9kZ2Ugd2l0aCB3aWR0aCAwLjENCnBvc25fZCA8LSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuMSkNCiMgMy4gSml0dGVyLWRvZGdlIHdpdGggaml0dGVyLndpZHRoIDAuMiBhbmQgZG9kZ2Uud2lkdGggMC4xDQpwb3NuX2pkIDwtIHBvc2l0aW9uX2ppdHRlcmRvZGdlKGppdHRlci53aWR0aCA9IDAuMiwgZG9kZ2Uud2lkdGggPSAwLjEpDQpgYGANCmBgYHtyfQ0KIyBDcmVhdGUgdGhlIHBsb3QgYmFzZTogd3QgdnMuIGZjeWwsIGNvbG9yZWQgYnkgZmFtDQpwX3d0X3ZzX2ZjeWxfYnlfZmFtIDwtIGdncGxvdChtdGNhcnMsIGFlcyhmY3lsLCB3dCwgY29sb3IgPSBmYW0pKQ0KYGBgDQpgYGB7cn0NCiMgQWRkIGEgcG9pbnQgbGF5ZXINCnBfd3RfdnNfZmN5bF9ieV9mYW0gKw0KICBnZW9tX3BvaW50KCkNCmBgYA0KVGhlIGRlZmF1bHQgcG9zaXRpb25pbmcgb2YgdGhlIHBvaW50cyBpcyBoaWdobHkgc3VzY2VwdGlibGUgdG8gb3ZlcnBsb3R0aW5nLg0KDQojIyMgVXNpbmcgcG9zaXRpb24gb2JqZWN0cw0KDQpOb3cgdGhhdCB0aGUgcG9zaXRpb24gb2JqZWN0cyBoYXZlIGJlZW4gY3JlYXRlZCwgeW91IGNhbiBhcHBseSB0aGVtIHRvIHRoZSBiYXNlIHBsb3QgdG8gc2VlIHRoZWlyIGVmZmVjdHMuIFlvdSBkbyB0aGlzIGJ5IGFkZGluZyBhIHBvaW50IGdlb20gYW5kIHNldHRpbmcgdGhlIHBvc2l0aW9uIGFyZ3VtZW50IHRvIHRoZSBwb3NpdGlvbiBvYmplY3QuDQoNCmBgYHtyfQ0KIyBBZGQgaml0dGVyaW5nIG9ubHkNCnBfd3RfdnNfZmN5bF9ieV9mYW0gKw0KICBnZW9tX3BvaW50KHBvc2l0aW9uID0gcG9zbl9qKQ0KYGBgDQpgYGB7cn0NCiMgQWRkIGRvZGdpbmcgb25seQ0KcF93dF92c19mY3lsX2J5X2ZhbV9qaXQgPC0gcF93dF92c19mY3lsX2J5X2ZhbSArDQogIGdlb21fcG9pbnQocG9zaXRpb24gPSBwb3NuX2QpDQpwX3d0X3ZzX2ZjeWxfYnlfZmFtX2ppdA0KYGBgDQpgYGB7cn0NCiMgQWRkIGppdHRlcmluZyBhbmQgZG9kZ2luZw0KcF93dF92c19mY3lsX2J5X2ZhbSArDQogIGdlb21fcG9pbnQocG9zaXRpb24gPSBwb3NuX2pkKQ0KYGBgDQpBbHRob3VnaCB5b3UgY2FuIHNldCBwb3NpdGlvbiBieSBzZXR0aW5nIHRoZSBwb3NpdGlvbiBhcmd1bWVudCB0byBhIHN0cmluZyAoZm9yIGV4YW1wbGUgcG9zaXRpb24gPSAiZG9kZ2UiKSwgZGVmaW5pbmcgb2JqZWN0cyBwcm9tb3RlcyBjb25zaXN0ZW5jeSBiZXR3ZWVuIGxheWVycy4NCg0KIyMjIFBsb3R0aW5nIHZhcmlhdGlvbnMNCg0KVGhlIHByZXBhcmF0aW9uIGlzIGRvbmU7IG5vdyBsZXQncyBleHBsb3JlIHN0YXRfc3VtbWFyeSgpLg0KDQpTdW1tYXJ5IHN0YXRpc3RpY3MgcmVmZXJzIHRvIGEgY29tYmluYXRpb24gb2YgbG9jYXRpb24gKG1lYW4gb3IgbWVkaWFuKSBhbmQgc3ByZWFkIChzdGFuZGFyZCBkZXZpYXRpb24gb3IgY29uZmlkZW5jZSBpbnRlcnZhbCkuDQoNClRoZXNlIG1ldHJpY3MgYXJlIGNhbGN1bGF0ZWQgaW4gc3RhdF9zdW1tYXJ5KCkgYnkgcGFzc2luZyBhIGZ1bmN0aW9uIHRvIHRoZSBmdW4uZGF0YSBhcmd1bWVudC4gbWVhbl9zZGwoKSwgY2FsY3VsYXRlcyBtdWx0aXBsZXMgb2YgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBhbmQgbWVhbl9jbF9ub3JtYWwoKSBjYWxjdWxhdGVzIHRoZSB0LWNvcnJlY3RlZCA5NSUgQ0kuDQoNCkFyZ3VtZW50cyB0byB0aGUgZGF0YSBmdW5jdGlvbiBhcmUgcGFzc2VkIHRvIHN0YXRfc3VtbWFyeSgpJ3MgZnVuLmFyZ3MgYXJndW1lbnQgYXMgYSBsaXN0Lg0KYGBge3J9DQpwX3d0X3ZzX2ZjeWxfYnlfZmFtX2ppdCArDQogICMgQ2hhbmdlIHRoZSBnZW9tIHRvIGJlIGFuIGVycm9yYmFyDQogIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IG1lYW5fc2RsLCBmdW4uYXJncyA9IGxpc3QobXVsdCA9IDEpLCBwb3NpdGlvbiA9IHBvc25fZCkNCmBgYA0KYGBge3J9DQpwX3d0X3ZzX2ZjeWxfYnlfZmFtX2ppdCArDQogICMgQ2hhbmdlIHRoZSBnZW9tIHRvIGJlIGFuIGVycm9yYmFyDQogIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IG1lYW5fc2RsLCBmdW4uYXJncyA9IGxpc3QobXVsdCA9IDEpLCBwb3NpdGlvbiA9IHBvc25fZCwNCiAgZ2VvbSAgPSJlcnJvcmJhciIpDQpgYGANCmBgYHtyfQ0KcF93dF92c19mY3lsX2J5X2ZhbV9qaXQgKw0KICAjIEFkZCBhIHN1bW1hcnkgc3RhdCBvZiBub3JtYWwgY29uZmlkZW5jZSBsaW1pdHMNCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gbWVhbl9jbF9ub3JtYWwsIHBvc2l0aW9uID0gcG9zbl9kKQ0KYGBgDQpZb3UgY2FuIGFsd2F5cyBhc3NpZ24geW91ciBvd24gZnVuY3Rpb24gdG8gdGhlIGZ1bi5kYXRhIGFyZ3VtZW50IGFzIGxvbmcgYXMgdGhlIHJlc3VsdCBpcyBhIGRhdGEgZnJhbWUgYW5kIHRoZSB2YXJpYWJsZSBuYW1lcyBtYXRjaCB0aGUgYWVzdGhldGljcyB0aGF0IHlvdSB3aWxsIG5lZWQgZm9yIHRoZSBnZW9tIGxheWVyLg0KDQojIENvb3JkaW5hdGVzDQoNCiMjIFpvb21pbmcgSW4NCg0KV2UnbGwgY29tcGFyZSB6b29taW5nIGJ5IGNoYW5naW5nIHNjYWxlcyBhbmQgYnkgY2hhbmdpbmcgY29vcmRpbmF0ZXMuDQoNClRoZSBiaWcgZGlmZmVyZW5jZSBpcyB0aGF0IHRoZSBzY2FsZSBmdW5jdGlvbnMgY2hhbmdlIHRoZSB1bmRlcmx5aW5nIGRhdGFzZXQsIHdoaWNoIGFmZmVjdHMgY2FsY3VsYXRpb25zIG1hZGUgYnkgY29tcHV0ZWQgZ2VvbXMgKGxpa2UgaGlzdG9ncmFtcyBvciBzbW9vdGggdHJlbmQgbGluZXMpLCB3aGVyZWFzIGNvb3JkaW5hdGUgZnVuY3Rpb25zIG1ha2Ugbm8gY2hhbmdlcyB0byB0aGUgZGF0YXNldC4NCmBgYHtyfQ0KIyBSdW4gdGhlIGNvZGUsIHZpZXcgdGhlIHBsb3QsIHRoZW4gdXBkYXRlIGl0DQpnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IHd0LCB5ID0gaHAsIGNvbG9yID0gZmFtKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aCgpICsNCiAgIyBBZGQgYSBjb250aW51b3VzIHggc2NhbGUgZnJvbSAzIHRvIDYNCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMywgNikpDQpgYGANCmBgYHtyfQ0KZ2dwbG90KG10Y2FycywgYWVzKHggPSB3dCwgeSA9IGhwLCBjb2xvciA9IGZhbSkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zbW9vdGgoKSArDQogICMgQWRkIENhcnRlc2lhbiBjb29yZGluYXRlcyB3aXRoIHggbGltaXRzIGZyb20gMyB0byA2DQogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygzLCA2KSkNCmBgYA0KVXNpbmcgdGhlIHNjYWxlIGZ1bmN0aW9uIHRvIHpvb20gaW4gbWVhbnQgdGhhdCB0aGVyZSB3YXNuJ3QgZW5vdWdoIGRhdGEgdG8gY2FsY3VsYXRlIHRoZSB0cmVuZCBsaW5lLCBhbmQgZ2VvbV9zbW9vdGgoKSBmYWlsZWQuIFdoZW4gY29vcmRfY2FydGVzaWFuKCkgd2FzIGFwcGxpZWQsIHRoZSBmdWxsIGRhdGFzZXQgd2FzIHVzZWQgZm9yIHRoZSB0cmVuZCBjYWxjdWxhdGlvbi4NCg0KIyMgQXNwZWN0IHJhdGlvOiAxOjEgcmF0aW9zDQoNCldlIGNhbiBzZXQgdGhlIGFzcGVjdCByYXRpbyBvZiBhIHBsb3Qgd2l0aCBjb29yZF9maXhlZCgpLCB3aGljaCB1c2VzIHJhdGlvID0gMSBhcyBhIGRlZmF1bHQuIEEgMToxIGFzcGVjdCByYXRpbyBpcyBtb3N0IGFwcHJvcHJpYXRlIHdoZW4gdHdvIGNvbnRpbnVvdXMgdmFyaWFibGVzIGFyZSBvbiB0aGUgc2FtZSBzY2FsZSwgYXMgd2l0aCB0aGUgaXJpcyBkYXRhc2V0Lg0KDQpBbGwgdmFyaWFibGVzIGFyZSBtZWFzdXJlZCBpbiBjZW50aW1ldGVycywgc28gaXQgb25seSBtYWtlcyBzZW5zZSB0aGF0IG9uZSB1bml0IG9uIHRoZSBwbG90IHNob3VsZCBiZSB0aGUgc2FtZSBwaHlzaWNhbCBkaXN0YW5jZSBvbiBlYWNoIGF4aXMuIFRoaXMgZ2l2ZXMgYSBtb3JlIHRydXRoZnVsIGRlcGljdGlvbiBvZiB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHR3byB2YXJpYWJsZXMgc2luY2UgdGhlIGFzcGVjdCByYXRpbyBjYW4gY2hhbmdlIHRoZSBhbmdsZSBvZiBvdXIgc21vb3RoaW5nIGxpbmUuIFRoaXMgd291bGQgZ2l2ZSBhbiBlcnJvbmVvdXMgaW1wcmVzc2lvbiBvZiB0aGUgZGF0YS4gT2YgY291cnNlIHRoZSB1bmRlcmx5aW5nIGxpbmVhciBtb2RlbHMgZG9uJ3QgY2hhbmdlLCBidXQgb3VyIHBlcmNlcHRpb24gY2FuIGJlIGluZmx1ZW5jZWQgYnkgdGhlIGFuZ2xlIGRyYXduLg0KYGBge3J9DQpnZ3Bsb3QoaXJpcywgYWVzKHggPSBTZXBhbC5MZW5ndGgsIHkgPSBTZXBhbC5XaWR0aCwgY29sb3IgPSBTcGVjaWVzKSkgKw0KICBnZW9tX2ppdHRlcigpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkgKw0KICAjIEZpeCB0aGUgY29vcmRpbmF0ZSByYXRpbw0KICBjb29yZF9maXhlZChyYXRpbyA9IDEpDQpgYGANCkEgMToxIGFzcGVjdCByYXRpbyBpcyBoZWxwZnVsIHdoZW4geW91ciBheGVzIHNob3cgdGhlIHNhbWUgc2NhbGVzLg0KDQojIyMgU2V0dGluZyByYXRpb25zDQoNCldoZW4gdmFsdWVzIGFyZSBub3Qgb24gdGhlIHNhbWUgc2NhbGUgaXQgY2FuIGJlIGEgYml0IHRyaWNreSB0byBzZXQgYW4gYXBwcm9wcmlhdGUgYXNwZWN0IHJhdGlvLiBBIGNsYXNzaWMgV2lsbGlhbSBDbGV2ZWxhbmQgKGludmVudG9yIG9mIGRvdCBwbG90cykgZXhhbXBsZSBpcyB0aGUgc3Vuc3BvdHMgZGF0YSBzZXQuIFdlIGhhdmUgMzIwMCBvYnNlcnZhdGlvbnMgZnJvbSAxNzUwIHRvIDIwMTYuDQoNCnN1bl9wbG90IGlzIGEgcGxvdCB3aXRob3V0IGFueSBzZXQgYXNwZWN0IHJhdGlvLiBJdCBmaWxscyB1cCB0aGUgZ3JhcGhpY3MgZGV2aWNlLg0KYGBge3J9DQpsaWJyYXJ5KHpvbykNCnN1bnNwb3RzLm0gPC0gZGF0YS5mcmFtZSgNCnllYXIgPSBpbmRleChzdW5zcG90Lm1vbnRoKSwNCnZhbHVlID0gcmVzaGFwZTI6Om1lbHQoc3Vuc3BvdC5tb250aCkkdmFsdWUpDQpgYGANCmBgYHtyfQ0Kc3VuX3Bsb3QgPC0gZ2dwbG90KHN1bnNwb3RzLm0sIGFlcyh4ID0geWVhciwgeSA9IHZhbHVlKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGNvb3JkX2ZpeGVkKCkgIyBkZWZhdWx0IHRvIDE6MSBhc3BlY3QgcmF0aW8NCg0Kc3VuX3Bsb3QNCmBgYA0KYGBge3J9DQojIEZpeCB0aGUgYXNwZWN0IHJhdGlvIHRvIDE6MQ0Kc3VuX3Bsb3QgKw0KICBjb29yZF9maXhlZChyYXRpbyA9IDEpDQpgYGANCmBgYHtyfQ0KIyBDaGFuZ2UgdGhlIGFzcGVjdCByYXRpbyB0byAyMDoxDQpzdW5fcGxvdCArDQogIGNvb3JkX2ZpeGVkKHJhdGlvID0gMC4wNTU1KQ0KYGBgDQogTWFraW5nIGEgd2lkZSBwbG90IGJ5IGNhbGxpbmcgY29vcmRfZml4ZWQoKSB3aXRoIGEgaGlnaCByYXRpbyBpcyBvZnRlbiB1c2VmdWwgZm9yIGxvbmcgdGltZSBzZXJpZXMuDQoNCiMjIyBFeHBhbmQgYW5kIGNsaXANCg0KVGhlIGNvb3JkXyooKSBsYXllciBmdW5jdGlvbnMgb2ZmZXIgdHdvIHVzZWZ1bCBhcmd1bWVudHMgdGhhdCB3b3JrIHdlbGwgdG9nZXRoZXI6IGV4cGFuZCBhbmQgY2xpcC4NCg0KLSBleHBhbmQgc2V0cyBhIGJ1ZmZlciBtYXJnaW4gYXJvdW5kIHRoZSBwbG90LCBzbyBkYXRhIGFuZCBheGVzIGRvbid0IG92ZXJsYXAuIFNldHRpbmcgZXhwYW5kIHRvIDAgZHJhd3MgdGhlIGF4ZXMgdG8gdGhlIGxpbWl0cyBvZiB0aGUgZGF0YS4NCi0gY2xpcCBkZWNpZGVzIHdoZXRoZXIgcGxvdCBlbGVtZW50cyB0aGF0IHdvdWxkIGxpZSBvdXRzaWRlIHRoZSBwbG90IHBhbmVsIGFyZSBkaXNwbGF5ZWQgb3IgaWdub3JlZCAoImNsaXBwZWQiKS4NCg0KV2hlbiBkb25lIHByb3Blcmx5IHRoaXMgY2FuIG1ha2UgYSBncmVhdCB2aXN1YWwgZWZmZWN0ISBXZSdsbCB1c2UgdGhlbWVfY2xhc3NpYygpIGFuZCBtb2RpZnkgdGhlIGF4aXMgbGluZXMgaW4gdGhpcyBleGFtcGxlLg0KYGBge3J9DQpnZ3Bsb3QobXRjYXJzLCBhZXMod3QsIG1wZykpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMikgKw0KICAjIEFkZCBDYXJ0ZXNpYW4gY29vcmRpbmF0ZXMgd2l0aCB6ZXJvIGV4cGFuc2lvbg0KICBjb29yZF9jYXJ0ZXNpYW4oZXhwYW5kID0gMCkgKw0KICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KYGBge3J9DQpnZ3Bsb3QobXRjYXJzLCBhZXMod3QsIG1wZykpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMikgKw0KICAjIFR1cm4gY2xpcHBpbmcgb2ZmDQogIGNvb3JkX2NhcnRlc2lhbihleHBhbmQgPSAwLCBjbGlwID0gIm9mZiIpICsNCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgIyBSZW1vdmUgYXhpcyBsaW5lcw0KICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCkpDQpgYGANClRoZXNlIGFyZ3VtZW50cyBtYWtlIGNsZWFuIGFuZCBhY2N1cmF0ZSBwbG90cyBieSBub3QgY3V0dGluZyBvZmYgZGF0YS4NCg0KIyMjIExvZy10cmFuc2Zvcm1pbmcgc2NhbGVzDQpVc2luZyBzY2FsZV95X2xvZzEwKCkgYW5kIHNjYWxlX3hfbG9nMTAoKSBpcyBlcXVpdmFsZW50IHRvIHRyYW5zZm9ybWluZyBvdXIgYWN0dWFsIGRhdGFzZXQgYmVmb3JlIGdldHRpbmcgdG8gZ2dwbG90Mi4NCg0KVXNpbmcgY29vcmRfdHJhbnMoKSwgc2V0dGluZyB4ID0gImxvZzEwIiBhbmQvb3IgeSA9ICJsb2cxMCIgYXJndW1lbnRzLCB0cmFuc2Zvcm1zIHRoZSBkYXRhIGFmdGVyIHN0YXRpc3RpY3MgaGF2ZSBiZWVuIGNhbGN1bGF0ZWQuIFRoZSBwbG90IHdpbGwgbG9vayB0aGUgc2FtZSBhcyB3aXRoIHVzaW5nIHNjYWxlXypfbG9nMTAoKSwgYnV0IHRoZSBzY2FsZXMgd2lsbCBiZSBkaWZmZXJlbnQsIG1lYW5pbmcgdGhhdCB3ZSdsbCBzZWUgdGhlIG9yaWdpbmFsIHZhbHVlcyBvbiBvdXIgbG9nMTAgdHJhbnNmb3JtZWQgYXhlcy4gVGhpcyBjYW4gYmUgdXNlZnVsIHNpbmNlIGxvZyBzY2FsZXMgY2FuIGJlIHNvbWV3aGF0DQpgYGB7cn0NCiMgUHJvZHVjZSBhIHNjYXR0ZXIgcGxvdCBvZiBicmFpbnd0IHZzLiBib2R5d3QNCmdncGxvdChtc2xlZXAsIGFlcyhib2R5d3QsIGJyYWlud3QpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdndGl0bGUoIlJhdyBWYWx1ZXMiKQ0KYGBgDQpgYGB7cn0NCiMgQWRkIHNjYWxlXypfKigpIGZ1bmN0aW9ucw0KZ2dwbG90KG1zbGVlcCwgYWVzKGJvZHl3dCwgYnJhaW53dCkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgc2NhbGVfeF9sb2cxMCgpICsNCiAgc2NhbGVfeV9sb2cxMCgpICsNCiAgZ2d0aXRsZSgiU2NhbGVfIGZ1bmN0aW9ucyIpDQpgYGANCmBgYHtyfQ0KIyBQZXJmb3JtIGEgbG9nMTAgY29vcmRpbmF0ZSBzeXN0ZW0gdHJhbnNmb3JtYXRpb24NCmdncGxvdChtc2xlZXAsIGFlcyhib2R5d3QsIGJyYWlud3QpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGNvb3JkX3RyYW5zKHggPSAibG9nMTAiLCB5ID0gImxvZzEwIikNCmBgYA0KRWFjaCB0cmFuc2Zvcm1hdGlvbiBtZXRob2QgaGFzIGltcGxpY2F0aW9ucyBmb3IgdGhlIHBsb3QncyBpbnRlcnByZXRhYmlsaXR5LiBUaGluayBhYm91dCB5b3VyIGF1ZGllbmNlIHdoZW4gY2hvb3NpbmcgYSBtZXRob2QgZm9yIGFwcGx5aW5nIHRyYW5zZm9ybWF0aW9ucy4NCg0KIyMjIEFkZGluZyBzdGF0cyB0byB0cmFuc2Zvcm1lZCBzY2FsZXMNCg0KUmVtZW1iZXIgdGhhdCBzdGF0aXN0aWNzIGFyZSBjYWxjdWxhdGVkIG9uIHRoZSB1bnRyYW5zZm9ybWVkIGRhdGEuIEEgbGluZWFyIG1vZGVsIG1heSBlbmQgdXAgbG9va2luZyBub3Qtc28tbGluZWFyIGFmdGVyIGFuIGF4aXMgdHJhbnNmb3JtYXRpb24uIExldCdzIHJldmlzaXQgdGhlIHR3byBwbG90cyBmcm9tIHRoZSBwcmV2aW91cyBleGVyY2lzZSBhbmQgY29tcGFyZSB0aGVpciBsaW5lYXIgbW9kZWxzLg0KYGBge3J9DQojIFBsb3Qgd2l0aCBhIHNjYWxlXypfKigpIGZ1bmN0aW9uOg0KZ2dwbG90KG1zbGVlcCwgYWVzKGJvZHl3dCwgYnJhaW53dCkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkgKw0KICAjIEFkZCBhIGxvZzEwIHggc2NhbGUNCiAgc2NhbGVfeF9sb2cxMCgpICsNCiAgIyBBZGQgYSBsb2cxMCB5IHNjYWxlDQogIHNjYWxlX3lfbG9nMTAoKSArDQogIGdndGl0bGUoIlNjYWxlIGZ1bmN0aW9ucyIpDQpgYGANCmBgYHtyfQ0KIyBQbG90IHdpdGggdHJhbnNmb3JtZWQgY29vcmRpbmF0ZXMNCmdncGxvdChtc2xlZXAsIGFlcyhib2R5d3QsIGJyYWlud3QpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UpICsNCiAgIyBBZGQgYSBsb2cxMCBjb29yZGluYXRlIHRyYW5zZm9ybWF0aW9uIGZvciB4IGFuZCB5IGF4ZXMNCiAgY29vcmRfdHJhbnMoeCA9ICJsb2cxMCIsIHkgPSAibG9nMTAiKQ0KYGBgDQpUaGUgc21vb3RoIHRyZW5kIGxpbmUgaXMgY2FsY3VsYXRlZCBhZnRlciBzY2FsZSB0cmFuc2Zvcm1hdGlvbnMgYnV0IG5vdCBjb29yZGluYXRlIHRyYW5zZm9ybWF0aW9ucywgc28gdGhlIHNlY29uZCBwbG90IGRvZXNuJ3QgbWFrZSBzZW5zZS4NCg0KIyMjIFVzZWZ1bCBkb3VibGUgYXhlcw0KYGBge3J9DQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmFpcnF1YWxpdHkgPC0gYWlycXVhbGl0eSAlPiUgDQogIG11dGF0ZShEYXRlID0gbWFrZV9kYXRlKDE5NzQsIE1vbnRoLCBEYXkpKQ0KYGBgDQoNCg0KRG91YmxlIHggYW5kIHktYXhlcyBhcmUgYSBjb250ZW50aW91cyB0b3BpYyBpbiBkYXRhIHZpc3VhbGl6YXRpb24uIEhlcmUsIHdlIHdpbGwgcmV2aWV3IGEgZ3JlYXQgdXNlIGNhc2Ugd2hlcmUgZG91YmxlIGF4ZXMgYWN0dWFsbHkgZG8gYWRkIHZhbHVlIHRvIGEgcGxvdC4NCmBgYHtyfQ0KIyBVc2luZyBhaXJxdWFsaXR5LCBwbG90IFRlbXAgdnMuIERhdGUNCmdncGxvdChhaXJxdWFsaXR5LCBhZXMoRGF0ZSwgVGVtcCkpICsNCiAgIyBBZGQgYSBsaW5lIGxheWVyDQogIGdlb21fbGluZSgpICsNCiAgbGFicyh4ID0gIkRhdGUgKDE5NzMpIiwgeSA9ICJGYWhyZW5oZWl0IikNCmBgYA0KYGBge3J9DQojIERlZmluZSBicmVha3MgKEZhaHJlbmhlaXQpDQp5X2JyZWFrcyA8LSBjKDU5LCA2OCwgNzcsIDg2LCA5NSwgMTA0KQ0KDQojIENvbnZlcnQgeV9icmVha3MgZnJvbSBGYWhyZW5oZWl0IHRvIENlbHNpdXMNCnlfbGFiZWxzIDwtICh5X2JyZWFrcyAtIDMyKSo1LzkNCg0KIyBDcmVhdGUgYSBzZWNvbmRhcnkgeC1heGlzDQpzZWNvbmRhcnlfeV9heGlzIDwtIHNlY19heGlzKA0KICAjIFVzZSBpZGVudGl0eSB0cmFuc2Zvcm1hdGlvbg0KICB0cmFucyA9IGlkZW50aXR5LA0KICBuYW1lID0gIkNlbHNpdXMiLA0KICAjIERlZmluZSBicmVha3MgYW5kIGxhYmVscyBhcyBhYm92ZQ0KICBicmVha3MgPSB5X2JyZWFrcywNCiAgbGFiZWxzID0geV9sYWJlbHMNCikNCg0KIyBFeGFtaW5lIHRoZSBvYmplY3QNCnNlY29uZGFyeV95X2F4aXMNCmBgYA0KYGBge3J9DQojIFVwZGF0ZSB0aGUgcGxvdA0KZ2dwbG90KGFpcnF1YWxpdHksIGFlcyhEYXRlLCBUZW1wKSkgKw0KICBnZW9tX2xpbmUoKSArDQogICMgQWRkIHRoZSBzZWNvbmRhcnkgeS1heGlzIA0KICBzY2FsZV95X2NvbnRpbnVvdXMoc2VjLmF4aXMgPSBzZWNvbmRhcnlfeV9heGlzKSArDQogIGxhYnMoeCA9ICJEYXRlICgxOTczKSIsIHkgPSAiRmFocmVuaGVpdCIpDQpgYGANCiBEb3VibGUgYXhlcyBhcmUgbW9zdCB1c2VmdWwgd2hlbiB5b3Ugd2FudCB0byBkaXNwbGF5IHRoZSBzYW1lIHZhbHVlIGluIHR3byBkaWZmZXJudCB1bml0cy4NCiANCiAjIyMgRmxpcHBpbmcgYXhlcw0KIA0KIEZsaXBwaW5nIGF4ZXMgbWVhbnMgdG8gcmV2ZXJzZSB0aGUgdmFyaWFibGVzIG1hcHBlZCBvbnRvIHRoZSB4IGFuZCB5IGFlc3RoZXRpY3MuIFdlIGNhbiBqdXN0IGNoYW5nZSB0aGUgbWFwcGluZ3MgaW4gYWVzKCksIGJ1dCB3ZSBjYW4gYWxzbyB1c2UgdGhlIGNvb3JkX2ZsaXAoKSBsYXllciBmdW5jdGlvbi4NCg0KVGhlcmUgYXJlIHR3byByZWFzb25zIHRvIHVzZSB0aGlzIGZ1bmN0aW9uOg0KDQotIFdlIHdhbnQgYSB2ZXJ0aWNhbCBnZW9tIHRvIGJlIGhvcml6b250YWwsIG9yDQotIFdlJ3ZlIGNvbXBsZXRlZCBhIGxvbmcgc2VyaWVzIG9mIHBsb3R0aW5nIGZ1bmN0aW9ucyBhbmQgd2FudCB0byBmbGlwIGl0IHdpdGhvdXQgaGF2aW5nIHRvIHJld3JpdGUgYWxsIG91ciBjb21tYW5kcy4NCmBgYHtyfQ0KIyBQbG90IGZjeWwgYmFycywgZmlsbGVkIGJ5IGZhbQ0KZ2dwbG90KG10Y2FycywgYWVzKGZjeWwsIGZpbGwgPSBmYW0pKSArDQogICMgUGxhY2UgYmFycyBzaWRlIGJ5IHNpZGUNCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiKQ0KYGBgDQpgYGB7cn0NCmdncGxvdChtdGNhcnMsIGFlcyhmY3lsLCBmaWxsID0gZmFtKSkgKw0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIpICsNCiAgIyBGbGlwIHRoZSB4IGFuZCB5IGNvb3JkaW5hdGVzDQogIGNvb3JkX2ZsaXAoKQ0KYGBgDQpgYGB7cn0NCmdncGxvdChtdGNhcnMsIGFlcyhmY3lsLCBmaWxsID0gZmFtKSkgKw0KICAjIFNldCBhIGRvZGdlIHdpZHRoIG9mIDAuNSBmb3IgcGFydGlhbGx5IG92ZXJsYXBwaW5nIGJhcnMNCiAgZ2VvbV9iYXIocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNSkpICsNCiAgY29vcmRfZmxpcCgpDQpgYGANCkhvcml6b250YWwgYmFycyBhcmUgZXNwZWNpYWxseSB1c2VmdWwgd2hlbiB0aGUgYXhpcyBsYWJlbHMgYXJlIGxvbmcuDQoNCkFub3RoZXIgZXhhbXBsZToNCmBgYHtyfQ0KbXRjYXJzIDwtIHJlYWQuY3N2KCJtdGNhcnMuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSkNCm10Y2FycyA8LSBtdGNhcnMgJT4lIA0KICBtdXRhdGUoZmFtID0gYXMuZmFjdG9yKGFtKSwgZmN5bCA9IGFzLmZhY3RvcihjeWwpLCBjYXIgPSBtb2RlbCwgZnZzID0gYXMuZmFjdG9yKHZzKSkNCmBgYA0KDQpgYGB7cn0NCiMgUGxvdCBvZiB3dCB2cy4gY2FyDQpnZ3Bsb3QobXRjYXJzLCBhZXMoY2FyLCB3dCkpICsNCiAgIyBBZGQgYSBwb2ludCBsYXllcg0KICBnZW9tX3BvaW50KCkgKw0KICBsYWJzKHggPSAiY2FyIiwgeSA9ICJ3ZWlnaHQiKQ0KYGBgDQpgYGB7cn0NCiMgRmxpcCB0aGUgYXhlcyB0byBzZXQgY2FyIHRvIHRoZSB5IGF4aXMNCmdncGxvdChtdGNhcnMsIGFlcyhjYXIsIHd0KSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBsYWJzKHggPSAiY2FyIiwgeSA9ICJ3ZWlnaHQiKSArDQogIGNvb3JkX2ZsaXAoKQ0KYGBgDQojIyMgUGllIGNoYXJ0cw0KDQpUaGUgY29vcmRfcG9sYXIoKSBmdW5jdGlvbiBjb252ZXJ0cyBhIHBsYW5hciB4LXkgQ2FydGVzaWFuIHBsb3QgdG8gcG9sYXIgY29vcmRpbmF0ZXMuIFRoaXMgY2FuIGJlIHVzZWZ1bCBpZiB5b3UgYXJlIHByb2R1Y2luZyBwaWUgY2hhcnRzLg0KDQpXZSBjYW4gaW1hZ2luZSB0d28gZm9ybXMgZm9yIHBpZSBjaGFydHMgLSB0aGUgdHlwaWNhbCBmaWxsZWQgY2lyY2xlLCBvciBhIGNvbG9yZWQgcmluZy4NCg0KVHlwaWNhbCBwaWUgY2hhcnRzIG9taXQgYWxsIG9mIHRoZSBub24tZGF0YSBpbmsuIFBpZSBjaGFydHMgYXJlIG5vdCByZWFsbHkgYmV0dGVyIHRoYW4gc3RhY2tlZCBiYXIgY2hhcnRzLg0KYGBge3J9DQojIFJ1biB0aGUgY29kZSwgdmlldyB0aGUgcGxvdCwgdGhlbiB1cGRhdGUgaXQNCmdncGxvdChtdGNhcnMsIGFlcyh4ID0gMSwgZmlsbCA9IGZjeWwpKSArDQogIGdlb21fYmFyKCkgKw0KICAjIEFkZCBhIHBvbGFyIGNvb3JkaW5hdGUgc3lzdGVtDQogIGNvb3JkX3BvbGFyKHRoZXRhID0gInkiKQ0KYGBgDQpgYGB7cn0NCmdncGxvdChtdGNhcnMsIGFlcyh4ID0gMSwgZmlsbCA9IGZjeWwpKSArDQogICMgUmVkdWNlIHRoZSBiYXIgd2lkdGggdG8gMC4xDQogIGdlb21fYmFyKHdpZHRoID0gMC4xKSArDQogIGNvb3JkX3BvbGFyKHRoZXRhID0gInkiKSArDQogICMgQWRkIGEgY29udGludW91cyB4IHNjYWxlIGZyb20gMC41IHRvIDEuNQ0KICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLjUsIDEuNSkpDQpgYGANCg0KUG9sYXIgY29vcmRpbmF0ZXMgYXJlIHBhcnRpY3VsYXJseSB1c2VmdWwgaWYgeW91IGFyZSBkZWFsaW5nIHdpdGggYSBjeWNsZSwgbGlrZSB5ZWFybHkgZGF0YSwgdGhhdCB5b3Ugd291bGQgbGlrZSB0byBzZWUgcmVwcmVzZW50ZWQgYXMgc3VjaC4NCg0KIyMjIFdpbmQgcm9zZSBwbG90cw0KDQpQb2xhciBjb29yZGluYXRlIHBsb3RzIGFyZSB3ZWxsLXN1aXRlZCB0byBzY2FsZXMgbGlrZSBjb21wYXNzIGRpcmVjdGlvbiBvciB0aW1lIG9mIGRheS4gQSBwb3B1bGFyIGV4YW1wbGUgaXMgdGhlICJ3aW5kIHJvc2UiLg0KYGBge3J9DQpsaWJyYXJ5KG9wZW5haXIpDQpgYGANCkNvbnZlcnRpbmcgW2RlZ3JlZXMgaW50byBmYWN0b3JdKGh0dHBzOi8vY29tbXVuaXR5LnJzdHVkaW8uY29tL3QvY29udmVydC13aW5kLWRpcmVjdGlvbi1kZWdyZWVzLWludG8tZmFjdG9ycy1pbi1hLWRhdGEtZnJhbWUvMTQ2MzZWKToNCmBgYHtyfQ0KbGlicmFyeShydmVzdCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KDQojIGdyYWIgYSB0YWJsZSBvZiB2YWx1ZXMgZnJvbSB0aGUgd2ViIHdpdGggYSBsaXR0bGUgc2NyYXBpbmcNCnVybCA8LSAnaHR0cDovL3Nub3dmZW5jZS51bW4uZWR1L0NvbXBvbmVudHMvd2luZGRpcmVjdGlvbmFuZGRlZ3JlZXN3aXRob3V0dGFibGUzLmh0bScNCnBhZ2UgPC0gcmVhZF9odG1sKHVybCkNCmRpcmVjdGlvbnNfcmF3IDwtIHBhZ2UgJT4lIGh0bWxfbm9kZSgndGQgdGFibGUnKSAlPiUgaHRtbF90YWJsZShoZWFkZXIgPSBUUlVFKQ0KZGlyZWN0aW9ucyA8LSBkaXJlY3Rpb25zX3JhdyAlPiUgDQogICAgc2V0X25hbWVzKH50b2xvd2VyKHN1YignIERpcmVjdGlvbicsICcnLCAueCkpKSAlPiUgDQogICAgc2xpY2UoLTEpICU+JSANCiAgICBzZXBhcmF0ZShkZWdyZWUsIGMoJ2RlZ3JlZV9taW4nLCAnZGVncmVlX21heCcpLCBzZXAgPSAnXFxzKy1cXHMrJywgY29udmVydCA9IFRSVUUpDQpkaXJlY3Rpb25zDQpgYGANCmBgYHtyfQ0Kd2luZCA8LSBteWRhdGEgJT4lIA0KICAgIG11dGF0ZSh3ZF9jYXJkaW5hbCA9IGN1dCgNCiAgICAgICAgd2QsIA0KICAgICAgICBicmVha3MgPSBjKDAsIGRpcmVjdGlvbnMkZGVncmVlX21heCwgMzYwKSwgDQogICAgICAgIGxhYmVscyA9IGMoZGlyZWN0aW9ucyRjYXJkaW5hbCwgJ04nKQ0KICAgICksIHdzX2ludGVydmFsID0gY3V0KHdzLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoMCwyLDQsNiw4LDEwLDEyLDE0KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmxvd2VzdD1UKQ0KICAgICkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBVc2luZyB3aW5kLCBwbG90IHdkIGZpbGxlZCBieSB3cw0KZ2dwbG90KHdpbmQsIGFlcyh3ZF9jYXJkaW5hbCwgZmlsbCA9IHdzX2ludGVydmFsKSkgKw0KICAjIEFkZCBhIGJhciBsYXllciB3aXRoIHdpZHRoIDENCiAgZ2VvbV9iYXIod2lkdGggPSAxKQ0KYGBgDQpgYGB7cn0NCiMgQ29udmVydCB0byBwb2xhciBjb29yZGluYXRlczoNCmdncGxvdCh3aW5kLCBhZXMod2RfY2FyZGluYWwsIGZpbGwgPSB3c19pbnRlcnZhbCkpICsNCiAgZ2VvbV9iYXIod2lkdGggPSAxKSArDQogIGNvb3JkX3BvbGFyKCkNCmBgYA0KYGBge3J9DQojIENvbnZlcnQgdG8gcG9sYXIgY29vcmRpbmF0ZXM6DQpnZ3Bsb3Qod2luZCwgYWVzKHdkX2NhcmRpbmFsLCBmaWxsID0gd3NfaW50ZXJ2YWwpKSArDQogIGdlb21fYmFyKHdpZHRoID0gMSkgKw0KICBjb29yZF9wb2xhcihzdGFydCA9IC1waS8xNikNCmBgYA0KVGhleSBhcmUgbm90IGNvbW1vbiwgYnV0IHBvbGFyIGNvb3JkaW5hdGUgcGxvdHMgYXJlIHJlYWxseSB1c2VmdWwuDQoNCiMgRmFjZXQNCg0KIyMgRmFjZXRzIGxheWVyDQoNCkZhY2V0aW5nIHNwbGl0cyB0aGUgZGF0YSB1cCBpbnRvIGdyb3VwcywgYWNjb3JkaW5nIHRvIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUsIHRoZW4gcGxvdHMgZWFjaCBncm91cCBpbiBpdHMgb3duIHBhbmVsLiBGb3Igc3BsaXR0aW5nIHRoZSBkYXRhIGJ5IG9uZSBvciB0d28gY2F0ZWdvcmljYWwgdmFyaWFibGVzLCBmYWNldF9ncmlkKCkgaXMgYmVzdC4NCg0KR2l2ZW4gY2F0ZWdvcmljYWwgdmFyaWFibGVzIEEgYW5kIEIsIHRoZSBjb2RlIHBhdHRlcm4gaXMNCg0KICAgIHBsb3QgKw0KICAgICAgZmFjZXRfZ3JpZChyb3dzID0gdmFycyhBKSwgY29scyA9IHZhcnMoQikpDQogICAgICANClRoaXMgZHJhd3MgYSBwYW5lbCBmb3IgZWFjaCBwYWlyd2lzZSBjb21iaW5hdGlvbiBvZiB0aGUgdmFsdWVzIG9mIEEgYW5kIEIuDQpgYGB7cn0NCmdncGxvdChtdGNhcnMsIGFlcyh3dCwgbXBnKSkgKyANCiAgZ2VvbV9wb2ludCgpICsNCiAgIyBGYWNldCByb3dzIGJ5IGFtDQogIGZhY2V0X2dyaWQocm93cyA9IHZhcnMoYW0pKQ0KYGBgDQpgYGB7cn0NCmdncGxvdChtdGNhcnMsIGFlcyh3dCwgbXBnKSkgKyANCiAgZ2VvbV9wb2ludCgpICsNCiAgIyBGYWNldCBjb2x1bW5zIGJ5IGN5bA0KICBmYWNldF9ncmlkKGNvbHM9dmFycyhjeWwpKQ0KYGBgDQpgYGB7cn0NCmdncGxvdChtdGNhcnMsIGFlcyh3dCwgbXBnKSkgKyANCiAgZ2VvbV9wb2ludCgpICsNCiAgIyBGYWNldCByb3dzIGJ5IGFtIGFuZCBjb2x1bW5zIGJ5IGN5bA0KICBmYWNldF9ncmlkKHJvd3MgPSB2YXJzKGFtKSwgY29scyA9IHZhcnMoY3lsKSkNCmBgYA0KIyMjIE1hbnkgdmFyaWFibGVzDQoNCkluIGFkZGl0aW9uIHRvIGFlc3RoZXRpY3MsIGZhY2V0cyBhcmUgYW5vdGhlciB3YXkgb2YgZW5jb2RpbmcgZmFjdG9yIChpLmUuIGNhdGVnb3JpY2FsKSB2YXJpYWJsZXMuIFRoZXkgY2FuIGJlIHVzZWQgdG8gcmVkdWNlIHRoZSBjb21wbGV4aXR5IG9mIHBsb3RzIHdpdGggbWFueSB2YXJpYWJsZXMuDQoNClR3byB2YXJpYWJsZXMgYXJlIG1hcHBlZCBvbnRvIHRoZSBjb2xvciBhZXN0aGV0aWMsIHVzaW5nIGh1ZSBhbmQgbGlnaHRuZXNzLiBUbyBhY2hpZXZlIHRoaXMgd2UgY29tYmluZWQgZmN5bCBhbmQgZmFtIGludG8gYSBzaW5nbGUgaW50ZXJhY3Rpb24gdmFyaWFibGUsIGZjeWxfZmFtLiBUaGlzIHdpbGwgYWxsb3cgdXMgdG8gdGFrZSBhZHZhbnRhZ2Ugb2YgQ29sb3IgQnJld2VyJ3MgUGFpcmVkIGNvbG9yIHBhbGV0dGUuDQpgYGB7cn0NCm10Y2FycyA8LSBtdGNhcnMgJT4lIA0KICBtdXRhdGUoZmN5bF9mYW0gPSBpbnRlcmFjdGlvbihmY3lsLCBmYW0sIHNlcD0iOiIpKQ0KIyBTZWUgdGhlIGludGVyYWN0aW9uIGNvbHVtbg0KbXRjYXJzJGZjeWxfZmFtDQpgYGANCmBgYHtyfQ0KIyBDb2xvciB0aGUgcG9pbnRzIGJ5IGZjeWxfZmFtDQpnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IHd0LCB5ID0gbXBnLCBjb2xvciA9IGZjeWxfZmFtKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICAjIFVzZSBhIHBhaXJlZCBjb2xvciBwYWxldHRlDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlBhaXJlZCIpDQpgYGANCmBgYHtyfQ0KIyBVcGRhdGUgdGhlIHBsb3QgdG8gbWFwIGRpc3AgdG8gc2l6ZQ0KZ2dwbG90KG10Y2FycywgYWVzKHggPSB3dCwgeSA9IG1wZywgY29sb3IgPSBmY3lsX2ZhbSwgc2l6ZSA9IGRpc3ApKSArDQogIGdlb21fcG9pbnQoKSArDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlBhaXJlZCIpDQpgYGANCmBgYHtyfQ0KIyBVcGRhdGUgdGhlIHBsb3QNCmdncGxvdChtdGNhcnMsIGFlcyh4ID0gd3QsIHkgPSBtcGcsIGNvbG9yID0gZmN5bF9mYW0sIHNpemUgPSBkaXNwKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJQYWlyZWQiKSArDQogICMgR3JpZCBmYWNldCBvbiBnZWFyIGFuZCB2cw0KICBmYWNldF9ncmlkKHJvd3MgPSB2YXJzKGdlYXIpLCBjb2xzID0gdmFycyh2cykpIA0KYGBgDQpUaGUgbGFzdCBwbG90IHlvdSd2ZSBjcmVhdGVkIGNvbnRhaW5zIDcgdmFyaWFibGVzICg0IGNhdGVnb3JpY2FsLCAzIGNvbnRpbnVvdXMpLiBVc2VmdWwgY29tYmluYXRpb25zIG9mIGFlc3RoZXRpY3MgYW5kIGZhY2V0cyBoZWxwIHRvIGFjaGlldmUgdGhpcy4NCg0KIyMgRm9ybXVsYSBub3RhdGlvbg0KDQpBcyB3ZWxsIGFzIHRoZSB2YXJzKCkgbm90YXRpb24gZm9yIHNwZWNpZnlpbmcgd2hpY2ggdmFyaWFibGVzIHNob3VsZCBiZSB1c2VkIHRvIHNwbGl0IHRoZSBkYXRhc2V0IGludG8gZmFjZXRzLCB0aGVyZSBpcyBhbHNvIGEgdHJhZGl0aW9uYWwgZm9ybXVsYSBub3RhdGlvbi4gVGhlIHRocmVlIGNhc2VzIGFyZSBzaG93biBpbiB0aGUgdGFibGUuDQoNCk1vZGVybiBub3RhdGlvbiB8CUZvcm11bGEgbm90YXRpb24NCi0tLS0tLS0tLS0tLS0tLSB8IC0tLS0tLS0tLS0tLS0tLS0NCmZhY2V0X2dyaWQocm93cyA9IHZhcnMoQSkpIHwJZmFjZXRfZ3JpZChBIH4gLikNCmZhY2V0X2dyaWQoY29scyA9IHZhcnMoQikpIHwJZmFjZXRfZ3JpZCguIH4gQikNCmZhY2V0X2dyaWQocm93cyA9IHZhcnMoQSksIGNvbHMgPSB2YXJzKEIpKSB8CWZhY2V0X2dyaWQoQSB+IEIpDQoNCmBgYHtyfQ0KZ2dwbG90KG10Y2FycywgYWVzKHd0LCBtcGcpKSArIA0KICBnZW9tX3BvaW50KCkgKw0KICAjIEZhY2V0IHJvd3MgYnkgYW0gdXNpbmcgZm9ybXVsYSBub3RhdGlvbg0KICBmYWNldF9ncmlkKGFtIH4gLikNCmBgYA0KYGBge3J9DQpnZ3Bsb3QobXRjYXJzLCBhZXMod3QsIG1wZykpICsgDQogIGdlb21fcG9pbnQoKSArDQogICMgRmFjZXQgY29sdW1ucyBieSBjeWwgdXNpbmcgZm9ybXVsYSBub3RhdGlvbg0KICBmYWNldF9ncmlkKC4gfiBjeWwpDQpgYGANCmBgYHtyfQ0KZ2dwbG90KG10Y2FycywgYWVzKHd0LCBtcGcpKSArIA0KICBnZW9tX3BvaW50KCkgKw0KICAjIEZhY2V0IHJvd3MgYnkgYW0gYW5kIGNvbHVtbnMgYnkgY3lsIHVzaW5nIGZvcm11bGEgbm90YXRpb24NCiAgZmFjZXRfZ3JpZChhbSB+IGN5bCkNCmBgYA0KV2hpbGUgbWFueSBnZ3Bsb3RzIHN0aWxsIHVzZSB0aGUgdHJhZGl0aW9uYWwgZm9ybXVsYSBub3RhdGlvbiwgdXNpbmcgdmFycygpIGlzIG5vdyBwcmVmZXJyZWQuDQoNCiMjIEZhY2V0IGxhYmVscyBhbmQgb3JkZXINCg0KIyMjIExhYmVsbGluZyBmYWNldHMNCg0KSWYgb3VyIGZhY3RvciBsZXZlbHMgYXJlIG5vdCBjbGVhciwgeW91ciBmYWNldCBsYWJlbHMgbWF5IGJlIGNvbmZ1c2luZy4gV2UgY2FuIGFzc2lnbiBwcm9wZXIgbGFiZWxzIGluIHlvdXIgb3JpZ2luYWwgZGF0YSBiZWZvcmUgcGxvdHRpbmcgb3Igd2UgY2FuIHVzZSB0aGUgbGFiZWxsZXIgYXJndW1lbnQgaW4gdGhlIGZhY2V0IGxheWVyLg0KDQpUaGUgZGVmYXVsdCB2YWx1ZSBpcw0KDQotIGxhYmVsX3ZhbHVlOiBEZWZhdWx0LCBkaXNwbGF5cyBvbmx5IHRoZSB2YWx1ZQ0KDQpDb21tb24gYWx0ZXJuYXRpdmVzIGFyZToNCg0KLSBsYWJlbF9ib3RoOiBEaXNwbGF5cyBib3RoIHRoZSB2YWx1ZSBhbmQgdGhlIHZhcmlhYmxlIG5hbWUNCi0gbGFiZWxfY29udGV4dDogRGlzcGxheXMgb25seSB0aGUgdmFsdWVzIG9yIGJvdGggdGhlIHZhbHVlcyBhbmQgdmFyaWFibGVzIGRlcGVuZGluZyBvbiB3aGV0aGVyIG11bHRpcGxlIGZhY3RvcnMgYXJlIGZhY2V0ZWQNCg0KYGBge3J9DQojIFBsb3Qgd3QgYnkgbXBnDQpnZ3Bsb3QobXRjYXJzLCBhZXMod3QsIG1wZykpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgIyBUaGUgZGVmYXVsdCBpcyBsYWJlbF92YWx1ZQ0KICBmYWNldF9ncmlkKGNvbHMgPSB2YXJzKGN5bCkpDQpgYGANCk8NCmBgYHtyfQ0KIyBQbG90IHd0IGJ5IG1wZw0KZ2dwbG90KG10Y2FycywgYWVzKHd0LCBtcGcpKSArDQogIGdlb21fcG9pbnQoKSArDQogICMgRGlzcGxheWluZyBib3RoIHRoZSB2YWx1ZXMgYW5kIHRoZSB2YXJpYWJsZXMNCiAgZmFjZXRfZ3JpZChjb2xzID0gdmFycyhjeWwpLCBsYWJlbGxlciA9IGxhYmVsX2JvdGgpDQpgYGANCmBgYHtyfQ0KIyBQbG90IHd0IGJ5IG1wZw0KZ2dwbG90KG10Y2FycywgYWVzKHd0LCBtcGcpKSArDQogIGdlb21fcG9pbnQoKSArDQogICMgTGFiZWwgY29udGV4dA0KICBmYWNldF9ncmlkKGNvbHMgPSB2YXJzKGN5bCksIGxhYmVsbGVyID0gbGFiZWxfY29udGV4dCkNCmBgYA0KYGBge3J9DQojIFBsb3Qgd3QgYnkgbXBnDQpnZ3Bsb3QobXRjYXJzLCBhZXMod3QsIG1wZykpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgIyBUd28gdmFyaWFibGVzDQogIGZhY2V0X2dyaWQoY29scyA9IHZhcnModnMsIGN5bCksIGxhYmVsbGVyID0gbGFiZWxfY29udGV4dCkNCmBgYA0KTWFrZSBzdXJlIHRoZXJlIGlzIG5vIGFtYmlndWl0eSBpbiBpbnRlcnByZXRpbmcgcGxvdHMgYnkgdXNpbmcgcHJvcGVyIGxhYmVscy4NCg0KIyMjIFNldHRpbmcgb3JkZXINCg0KSWYgeW91IHdhbnQgdG8gY2hhbmdlIHRoZSBvcmRlciBvZiB5b3VyIGZhY2V0cywgaXQncyBiZXN0IHRvIHByb3Blcmx5IGRlZmluZSB5b3VyIGZhY3RvciB2YXJpYWJsZXMgYmVmb3JlIHBsb3R0aW5nLg0KDQpMZXQncyBzZWUgdGhpcyBpbiBhY3Rpb24gd2l0aCB0aGUgbXRjYXJzIHRyYW5zbWlzc2lvbiB2YXJpYWJsZSBhbS4gSW4gdGhpcyBjYXNlLCAwID0gImF1dG9tYXRpYyIgYW5kIDEgPSAibWFudWFsIi4NCg0KSGVyZSwgd2UnbGwgbWFrZSBhbSBhIGZhY3RvciB2YXJpYWJsZSBhbmQgcmVsYWJlbCB0aGUgbnVtYmVycyB0byBwcm9wZXIgbmFtZXMuIFRoZSBkZWZhdWx0IG9yZGVyIGlzIGFscGhhYmV0aWNhbC4gVG8gcmVhcnJhbmdlIHRoZW0gd2UnbGwgY2FsbCBmY3RfcmV2KCkgZnJvbSB0aGUgZm9yY2F0cyBwYWNrYWdlIHRvIHJldmVyc2UgdGhlIG9yZGVyLg0KYGBge3J9DQpsaWJyYXJ5KGZvcmNhdHMpDQoNCiMgTWFrZSBmYWN0b3IsIHNldCBwcm9wZXIgbGFiZWxzIGV4cGxpY3RseQ0KbXRjYXJzJGZhbSA8LSBmYWN0b3IobXRjYXJzJGFtLCBsYWJlbHMgPSBjKGAwYCA9ICJhdXRvbWF0aWMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGAxYCA9ICJtYW51YWwiKSkNCg0KIyBEZWZhdWx0IG9yZGVyIGlzIGFscGhhYmV0aWNhbA0KZ2dwbG90KG10Y2FycywgYWVzKHd0LCBtcGcpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGZhY2V0X2dyaWQoY29scyA9IHZhcnMoZmFtKSkNCmBgYA0KYGBge3J9DQojIE1ha2UgZmFjdG9yLCBzZXQgcHJvcGVyIGxhYmVscyBleHBsaWN0bHksIGFuZA0KIyBtYW51YWxseSBzZXQgdGhlIGxhYmVsIG9yZGVyDQptdGNhcnMkZmFtIDwtIGZhY3RvcihtdGNhcnMkYW0sDQogICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKDEsIDApLA0KICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygibWFudWFsIiwgImF1dG9tYXRpYyIpKQ0KbXRjYXJzJGZ2cyA8LSBmYWN0b3IobXRjYXJzJHZzLA0KICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygxLCAwKSwNCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoInN0cmFpZ2h0IiwgIlYtc2hhcGVkIikpDQoNCiMgVmlldyBhZ2Fpbg0KZ2dwbG90KG10Y2FycywgYWVzKHd0LCBtcGcpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGZhY2V0X2dyaWQoY29scyA9IHZhcnMoZmFtKSkNCmBgYA0KQXJyYW5nZSB5b3VyIGZhY2V0cyBpbiBhbiBpbnR1aXRpdmUgb3JkZXIgZm9yIHlvdXIgZGF0YS4NCg0KIyMgRmFjZXQgcGxvdHRpbmcgc3BhY2VzDQoNCiMjIyBDb250aW51b3VzIHZhcmlhYmxlcw0KDQpCeSBkZWZhdWx0IGV2ZXJ5IGZhY2V0IG9mIGEgcGxvdCBoYXMgdGhlIHNhbWUgYXhlcy4gSWYgdGhlIGRhdGEgcmFuZ2VzIHZhcnkgd2lsZGx5IGJldHdlZW4gZmFjZXRzLCBpdCBjYW4gYmUgY2xlYXJlciBpZiBlYWNoIGZhY2V0IGhhcyBpdHMgb3duIHNjYWxlLiBUaGlzIGlzIGFjaGlldmVkIHdpdGggdGhlIHNjYWxlcyBhcmd1bWVudCB0byBmYWNldF9ncmlkKCkuDQoNCi0gImZpeGVkIiAoZGVmYXVsdCk6IGF4ZXMgYXJlIHNoYXJlZCBiZXR3ZWVuIGZhY2V0cy4NCi0gZnJlZTogZWFjaCBmYWNldCBoYXMgaXRzIG93biBheGVzLg0KLSBmcmVlX3g6IGVhY2ggZmFjZXQgaGFzIGl0cyBvd24geC1heGlzLCBidXQgdGhlIHktYXhpcyBpcyBzaGFyZWQuDQotIGZyZWVfeTogZWFjaCBmYWNldCBoYXMgaXRzIG93biB5LWF4aXMsIGJ1dCB0aGUgeC1heGlzIGlzIHNoYXJlZC4NCg0KV2hlbiBmYWNldGluZyBieSBjb2x1bW5zLCAiZnJlZV95IiBoYXMgbm8gZWZmZWN0LCBidXQgd2UgY2FuIGFkanVzdCB0aGUgeC1heGlzLiBJbiBjb250cmFzdCwgd2hlbiBmYWNldGluZyBieSByb3dzLCAiZnJlZV94IiBoYXMgbm8gZWZmZWN0LCBidXQgd2UgY2FuIGFkanVzdCB0aGUgeS1heGlzLg0KYGBge3J9DQpnZ3Bsb3QobXRjYXJzLCBhZXMod3QsIG1wZykpICsNCiAgZ2VvbV9wb2ludCgpICsgDQogICMgRmFjZXQgY29sdW1ucyBieSBjeWwgDQogIGZhY2V0X2dyaWQoY29scyA9IHZhcnMoY3lsKSkNCmBgYA0KYGBge3J9DQpnZ3Bsb3QobXRjYXJzLCBhZXMod3QsIG1wZykpICsNCiAgZ2VvbV9wb2ludCgpICsgDQogICMgRmFjZXQgY29sdW1ucyBieSBjeWwgDQogIGZhY2V0X2dyaWQoY29scyA9IHZhcnMoY3lsKSwNCiAgICAgICAgICAgICBzY2FsZXMgPSAiZnJlZV94IikNCmBgYA0KYGBge3J9DQpnZ3Bsb3QobXRjYXJzLCBhZXMod3QsIG1wZykpICsNCiAgZ2VvbV9wb2ludCgpICsgDQogICMgU3dhcCBjb2xzIGZvciByb3dzOyBmcmVlIHRoZSB5LWF4aXMgc2NhbGVzDQogIGZhY2V0X2dyaWQocm93cyA9IHZhcnMoY3lsKSwgc2NhbGVzID0gImZyZWVfeSIpDQpgYGANClNoYXJlZCBzY2FsZXMgbWFrZSBpdCBlYXN5IHRvIGNvbXBhcmUgYmV0d2VlbiBmYWNldHMsIGJ1dCBjYW4gYmUgY29uZnVzaW5nIGlmIHRoZSBkYXRhIHJhbmdlcyBhcmUgdmVyeSBkaWZmZXJlbnQuIEluIHRoYXQgY2FzZSwgdXNlZCBmcmVlIHNjYWxlcy4NCg0KIyMjIENhdGVnb3JpY2FsIHZhcmlhYmxlcw0KDQpXaGVuIHlvdSBoYXZlIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgd2l0aCBtYW55IGxldmVscyB3aGljaCBhcmUgbm90IGFsbCBwcmVzZW50IGluIGVhY2ggc3ViLWdyb3VwIG9mIGFub3RoZXIgdmFyaWFibGUsIGl0J3MgdXN1YWxseSBkZXNpcmFibGUgdG8gZHJvcCB0aGUgdW51c2VkIGxldmVscy4NCg0KQnkgZGVmYXVsdCwgZWFjaCBmYWNldCBvZiBhIHBsb3QgaXMgdGhlIHNhbWUgc2l6ZS4gVGhpcyBiZWhhdmlvciBjYW4gYmUgY2hhbmdlZCB3aXRoIHRoZSBzcGFjZXMgYXJndW1lbnQsIHdoaWNoIHdvcmtzIGluIHRoZSBzYW1lIHdheSBhcyBzY2FsZXM6ICJmcmVlX3giIGFsbG93cyBkaWZmZXJlbnQgc2l6ZWQgZmFjZXRzIG9uIHRoZSB4LWF4aXMsICJmcmVlX3kiLCBhbGxvd3MgZGlmZmVyZW50IHNpemVkIGZhY2V0cyBvbiB0aGUgeS1heGlzLCAiZnJlZSIgYWxsb3dzIGRpZmZlcmVudCBzaXplcyBpbiBib3RoIGRpcmVjdGlvbnMuDQpgYGB7cn0NCmdncGxvdChtdGNhcnMsIGFlcyh4ID0gbXBnLCB5ID0gY2FyLCBjb2xvciA9IGZhbSkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgIyBGYWNldCByb3dzIGJ5IGdlYXINCiAgZmFjZXRfZ3JpZChyb3dzID0gdmFycyhnZWFyKSkNCmBgYA0KYGBge3J9DQpnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IG1wZywgeSA9IGNhciwgY29sb3IgPSBmYW0pKSArDQogIGdlb21fcG9pbnQoKSArDQogICMgRnJlZSB0aGUgeSBzY2FsZXMgYW5kIHNwYWNlDQogIGZhY2V0X2dyaWQocm93cyA9IHZhcnMoZ2VhciksDQogIHNjYWxlcyA9ICJmcmVlX3kiLA0KICBzcGFjZSA9ICJmcmVlX3kiKQ0KYGBgDQpgYGB7cn0NCm10Y2FyczIgPC0gbXRjYXJzICU+JSANCiAgIyBhcnJhbmdlIGZyb20gbG8gdG8gaGkNCiAgYXJyYW5nZSgtbXBnKSAlPiUgDQogICMgcmVkZWZpbmUgaW4gZmFjdG9yIGxldmVsZXMNCiAgbXV0YXRlKGNhciA9IGFzX2ZhY3RvcihjYXIpKQ0KYGBgDQpgYGB7cn0NCmdncGxvdChtdGNhcnMyLCBhZXMoeCA9IG1wZywgeSA9IGNhciwgY29sb3IgPSBmYW0pKSArDQogIGdlb21fcG9pbnQoKSArDQogICMgRnJlZSB0aGUgeSBzY2FsZXMgYW5kIHNwYWNlDQogIGZhY2V0X2dyaWQocm93cyA9IHZhcnMoZ2VhciksDQogIHNjYWxlcyA9ICJmcmVlX3kiLA0KICBzcGFjZSA9ICJmcmVlX3kiKQ0KYGBgDQpGcmVlaW5nIHRoZSB5LXNjYWxlIHRvIHJlbW92ZSBibGFuayBsaW5lcyBoZWxwcyBmb2N1cyBhdHRlbnRpb24gb24gdGhlIGFjdHVhbCBkYXRhIHByZXNlbnQuDQoNCiMjIEZhY2V0IHdyYXAgYW5kIG1hcmdpbnMNCg0KIyMjIFdyYXBwaW5nIGZvciBtYW55IGxldmVscw0KDQpmYWNldF9ncmlkKCkgaXMgZmFudGFzdGljIGZvciBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgd2l0aCBhIHNtYWxsIG51bWJlciBvZiBsZXZlbHMuIEFsdGhvdWdoIGl0IGlzIHBvc3NpYmxlIHRvIGZhY2V0IHZhcmlhYmxlcyB3aXRoIG1hbnkgbGV2ZWxzLCB0aGUgcmVzdWx0aW5nIHBsb3Qgd2lsbCBiZSB2ZXJ5IHdpZGUgb3IgdmVyeSB0YWxsLCB3aGljaCBjYW4gbWFrZSBpdCBkaWZmaWN1bHQgdG8gdmlldy4NCg0KVGhlIHNvbHV0aW9uIGlzIHRvIHVzZSBmYWNldF93cmFwKCkgd2hpY2ggc2VwYXJhdGVzIGxldmVscyBhbG9uZyBvbmUgYXhpcyBidXQgd3JhcHMgYWxsIHRoZSBzdWJzZXRzIGFjcm9zcyBhIGdpdmVuIG51bWJlciBvZiByb3dzIG9yIGNvbHVtbnMuDQpgYGB7cn0NCmxpYnJhcnkoY2FyRGF0YSkNCmBgYA0KYGBge3J9DQpnZ3Bsb3QoVm9jYWIsIGFlcyh4ID0gZWR1Y2F0aW9uLCB5ID0gdm9jYWJ1bGFyeSkpICsNCiAgc3RhdF9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkgKw0KICAjIENyZWF0ZSBmYWNldHMsIHdyYXBwaW5nIGJ5IHllYXIsIHVzaW5nIHZhcnMoKQ0KICBmYWNldF93cmFwKHZhcnMoeWVhcikpDQpgYGANCmBgYHtyfQ0KZ2dwbG90KFZvY2FiLCBhZXMoeCA9IGVkdWNhdGlvbiwgeSA9IHZvY2FidWxhcnkpKSArDQogIHN0YXRfc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UpICsNCiAgIyBDcmVhdGUgZmFjZXRzLCB3cmFwcGluZyBieSB5ZWFyLCB1c2luZyBhIGZvcm11bGENCiAgZmFjZXRfd3JhcCh+IHllYXIpDQpgYGANCmBgYHtyfQ0KZ2dwbG90KFZvY2FiLCBhZXMoeCA9IGVkdWNhdGlvbiwgeSA9IHZvY2FidWxhcnkpKSArDQogIHN0YXRfc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UpICsNCiAgIyBVcGRhdGUgdGhlIGZhY2V0IGxheW91dCwgdXNpbmcgMTEgY29sdW1ucw0KICBmYWNldF93cmFwKH4geWVhciwNCiAgbmNvbCA9IDExKQ0KYGBgDQpTdGFydCBleHBlcmltZW50aW5nIHdpdGggZmFjZXRzIGluIHlvdXIgb3duIHBsb3RzLg0KDQojIyMgTWFyZ2luIHBsb3RzDQoNCkZhY2V0cyBhcmUgZ3JlYXQgZm9yIHNlZWluZyBzdWJzZXRzIGluIGEgdmFyaWFibGUsIGJ1dCBzb21ldGltZXMgeW91IHdhbnQgdG8gc2VlIGJvdGggdGhvc2Ugc3Vic2V0cyBhbmQgYWxsIHZhbHVlcyBpbiBhIHZhcmlhYmxlLg0KDQpIZXJlLCB0aGUgbWFyZ2lucyBhcmd1bWVudCB0byBmYWNldF9ncmlkKCkgaXMgeW91ciBmcmllbmQuDQoNCi0gRkFMU0UgKGRlZmF1bHQpOiBubyBtYXJnaW5zLg0KLSBUUlVFOiBhZGQgbWFyZ2lucyB0byBldmVyeSB2YXJpYWJsZSBiZWluZyBmYWNldGVkIGJ5Lg0KLSBjKCJ2YXJpYWJsZTEiLCAidmFyaWFibGUyIik6IG9ubHkgYWRkIG1hcmdpbnMgdG8gdGhlIHZhcmlhYmxlcyBsaXN0ZWQuDQoNCg0KYGBge3J9DQpnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IHd0LCB5ID0gbXBnKSkgKyANCiAgZ2VvbV9wb2ludCgpICsNCiAgIyBGYWNldCByb3dzIGJ5IGZ2cyBhbmQgY29scyBieSBmYW0NCiAgZmFjZXRfZ3JpZChjb2xzID0gdmFycyhnZWFyKSwgcm93cyA9IHZhcnMoZnZzLCBmYW0pKQ0KYGBgDQpgYGB7cn0NCmdncGxvdChtdGNhcnMsIGFlcyh4ID0gd3QsIHkgPSBtcGcpKSArIA0KICBnZW9tX3BvaW50KCkgKw0KICAjIFVwZGF0ZSB0aGUgZmFjZXRzIHRvIGFkZCBtYXJnaW5zDQogIGZhY2V0X2dyaWQocm93cyA9IHZhcnMoZnZzLCBmYW0pLCBjb2xzID0gdmFycyhnZWFyKSwNCiAgbWFyZ2lucyA9IFRSVUUpDQpgYGANCmBgYHtyfQ0KZ2dwbG90KG10Y2FycywgYWVzKHggPSB3dCwgeSA9IG1wZykpICsgDQogIGdlb21fcG9pbnQoKSArDQogICMgVXBkYXRlIHRoZSBmYWNldHMgdG8gb25seSBzaG93IG1hcmdpbnMgb24gZmFtDQogIGZhY2V0X2dyaWQocm93cyA9IHZhcnMoZnZzLCBmYW0pLCBjb2xzID0gdmFycyhnZWFyKSwgbWFyZ2lucyA9ICJmYW0iKQ0KYGBgDQpgYGB7cn0NCmdncGxvdChtdGNhcnMsIGFlcyh4ID0gd3QsIHkgPSBtcGcpKSArIA0KICBnZW9tX3BvaW50KCkgKw0KICAjIFVwZGF0ZSB0aGUgZmFjZXRzIHRvIG9ubHkgc2hvdyBtYXJnaW5zIG9uIGdlYXIgYW5kIGZ2cw0KICBmYWNldF9ncmlkKHJvd3MgPSB2YXJzKGZ2cywgZmFtKSwgY29scyA9IHZhcnMoZ2VhciksIG1hcmdpbnMgPSBjKCJnZWFyIiwgImZ2cyIpKQ0KYGBgDQpJdCBjYW4gYmUgcmVhbGx5IGhlbHBmdWwgdG8gc2hvdyB0aGUgZnVsbCBtYXJnaW4gcGxvdHMhDQoNCiMgQmVzdCBQcmFjdGljZXMNCg0KIyMgQmFyIHBsb3RzDQoNCiMjIyBEeW5hbWl0ZSBwbG90cw0KDQoiRHluYW1pdGUgcGxvdHMiIChiYXIgcGxvdHMgd2l0aCBlcnJvciBiYXJzKSBhcmUgbm90IHdlbGwgc3VpdGVkIGZvciB0aGVpciBpbnRlbmRlZCBwdXJwb3NlIG9mIGRlcGljdGluZyBkaXN0cmlidXRpb25zLiBJZiB3ZSByZWFsbHkgd2FudCBlcnJvciBiYXJzIG9uIGJhciBwbG90cywgd2UgY2FuIG9mIGNvdXJzZSBnZXQgdGhlbSwgYnV0IHdlJ2xsIG5lZWQgdG8gc2V0IHRoZSBwb3NpdGlvbnMgbWFudWFsbHkuIEEgcG9pbnQgZ2VvbSB3aWxsIHR5cGljYWxseSBzZXJ2ZSB5b3UgbXVjaCBiZXR0ZXIuDQoNCmBgYHtyfQ0KIyBQbG90IHd0IHZzLiBmY3lsDQpnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IGZjeWwsIHkgPSB3dCkpICsNCiAgIyBBZGQgYSBiYXIgc3VtbWFyeSBzdGF0IG9mIG1lYW5zLCBjb2xvcmVkIHNreWJsdWUNCiAgc3RhdF9zdW1tYXJ5KGZ1bi55ID0gbWVhbiwgZ2VvbSA9ICJiYXIiLCBmaWxsID0gInNreWJsdWUiKSArDQogICMgQWRkIGFuIGVycm9yYmFyIHN1bW1hcnkgc3RhdCBzdGQgZGV2aWF0aW9uIGxpbWl0cw0KICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBtZWFuX3NkbCwgZnVuLmFyZ3MgPSBsaXN0KG11bHQgPSAxKSwgZ2VvbSA9ICJlcnJvcmJhciIsIHdpZHRoID0gMC4xKQ0KYGBgDQpSZW1lbWJlciwgd2UgY2FuIHNwZWNpZnkgYW55IGZ1bmN0aW9uIGluIGZ1bi5kYXRhIG9yIGZ1bi55IGFuZCB3ZSBjYW4gYWxzbyBzcGVjaWZ5IGFueSBnZW9tLCBhcyBsb25nIGFzIGl0J3MgYXBwcm9wcmlhdGUgdG8gdGhlIGRhdGEgdHlwZS4NCg0KIyMjIFBvc2l0aW9uIGRvZGdpbmcNCg0Kd2Ugd2lsbCBhZGQgYSBkaXN0aW5jdGlvbiBiZXR3ZWVuIHRyYW5zbWlzc2lvbiB0eXBlLCBmYW0sIGZvciB0aGUgZHluYW1pdGUgcGxvdHMgYW5kIGV4cGxvcmUgcG9zaXRpb24gZG9kZ2luZyAod2hlcmUgYmFycyBhcmUgc2lkZS1ieS1zaWRlKS4NCmBgYHtyfQ0KIyBVcGRhdGUgdGhlIGFlc3RoZXRpY3MgdG8gY29sb3IgYW5kIGZpbGwgYnkgZmFtDQpnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IGZjeWwsIHkgPSB3dCwgY29sb3IgPSBmYW0sIGZpbGwgPSBmYW0pKSArDQogIHN0YXRfc3VtbWFyeShmdW4ueSA9IG1lYW4sIGdlb20gPSAiYmFyIikgKw0KICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBtZWFuX3NkbCwgZnVuLmFyZ3MgPSBsaXN0KG11bHQgPSAxKSwgZ2VvbSA9ICJlcnJvcmJhciIsIHdpZHRoID0gMC4xKQ0KYGBgDQpgYGB7cn0NCiMgU2V0IGFscGhhIGZvciB0aGUgZmlyc3QgYW5kIHNldCBwb3NpdGlvbiBmb3IgZWFjaCBzdGF0IHN1bW1hcnkgZnVuY3Rpb24NCmdncGxvdChtdGNhcnMsIGFlcyh4ID0gZmN5bCwgeSA9IHd0LCBjb2xvciA9IGZhbSwgZmlsbCA9IGZhbSkpICsNCiAgc3RhdF9zdW1tYXJ5KGZ1bi55ID0gbWVhbiwgZ2VvbSA9ICJiYXIiLCBhbHBoYSA9IDAuNSwgcG9zaXRpb24gPSAiZG9kZ2UiKSArDQogIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IG1lYW5fc2RsLCBmdW4uYXJncyA9IGxpc3QobXVsdCA9IDEpLCBnZW9tID0gImVycm9yYmFyIiwgcG9zaXRpb24gPSAiZG9kZ2UiLCB3aWR0aCA9IDAuMSkNCmBgYA0KYGBge3J9DQojIERlZmluZSBhIGRvZGdlIHBvc2l0aW9uIG9iamVjdCB3aXRoIHdpZHRoIDAuOQ0KcG9zbl9kIDwtIHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC45KQ0KDQojIEZvciBlYWNoIHN1bW1hcnkgc3RhdCwgdXBkYXRlIHRoZSBwb3NpdGlvbiB0byBwb3NuX2QNCmdncGxvdChtdGNhcnMsIGFlcyh4ID0gZmN5bCwgeSA9IHd0LCBjb2xvciA9IGZhbSwgZmlsbCA9IGZhbSkpICsNCiAgc3RhdF9zdW1tYXJ5KGZ1bi55ID0gbWVhbiwgZ2VvbSA9ICJiYXIiLCBwb3NpdGlvbiA9IHBvc25fZCwgYWxwaGEgPSAwLjUpICsNCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gbWVhbl9zZGwsIGZ1bi5hcmdzID0gbGlzdChtdWx0ID0gMSksIHdpZHRoID0gMC4xLCBwb3NpdGlvbiA9IHBvc25fZCwgZ2VvbSA9ICJlcnJvcmJhciIpDQpgYGANClNsaWdodGx5IG92ZXJsYXBwaW5nIGJhciBwbG90cyBhcmUgY29tbW9uIGluIHRoZSBwb3B1bGFyIHByZXNzIGFuZCBhZGQgYSBiaXQgb2Ygc3R5bGUgdG8geW91ciBkYXRhIHZpei4NCg0KIyMjIFVzaW5nIGFnZ3JlZ2F0ZWQgZGF0YQ0KDQpJZiBpdCBpcyBhcHByb3ByaWF0ZSB0byB1c2UgYmFyIHBsb3RzLCB0aGVuIGl0IG5pY2UgdG8gZ2l2ZSBhbiBpbXByZXNzaW9uIG9mIHRoZSBudW1iZXIgb2YgdmFsdWVzIGluIGVhY2ggZ3JvdXAuDQoNCnN0YXRfc3VtbWFyeSgpIGRvZXNuJ3Qga2VlcCB0cmFjayBvZiB0aGUgY291bnQuIHN0YXRfc3VtKCkgZG9lcyAodGhhdCdzIHRoZSB3aG9sZSBwb2ludCksIGJ1dCBpdCdzIGRpZmZpY3VsdCB0byBhY2Nlc3MuIEl0J3MgbW9yZSBzdHJhaWdodGZvcndhcmQgdG8gY2FsY3VsYXRlIGV4YWN0bHkgd2hhdCB3ZSB3YW50IHRvIHBsb3Qgb3Vyc2VsdmVzLg0KDQpgYGB7cn0NCm10Y2Fyc19ieV9jeWwgPC0gbXRjYXJzICU+JQ0KICBncm91cF9ieShjeWwpICU+JSANCiAgc3VtbWFyaXplKG1lYW5fd3QgPSBtZWFuKHd0KSwNCiAgICAgICAgICBzZF93dCA9IHNkKHd0KSwNCiAgICAgICAgICBuX3d0ID0gbigpKSAlPiUgDQogICAgICAgICAgbXV0YXRlKCBwcm9wID0gbl93dC9zdW0obl93dCkpDQptdGNhcnNfYnlfY3lsDQpgYGANCmBgYHtyfQ0KIyBVc2luZyBtdGNhcnNfY3lsLCBwbG90IG1lYW5fd3QgdnMuIGN5bA0KZ2dwbG90KG10Y2Fyc19ieV9jeWwsIGFlcyhjeWwsIG1lYW5fd3QpKSArDQogICMgQWRkIGEgYmFyIGxheWVyIHdpdGggaWRlbnRpdHkgc3RhdCwgZmlsbGVkIHNreWJsdWUNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAic2t5Ymx1ZSIpDQpgYGANCmBgYHtyfQ0KZ2dwbG90KG10Y2Fyc19ieV9jeWwsIGFlcyh4ID0gY3lsLCB5ID0gbWVhbl93dCkpICsNCiAgIyBTd2FwIGdlb21fYmFyKCkgZm9yIGdlb21fY29sKCkNCiAgZ2VvbV9jb2woZmlsbCA9ICJza3libHVlIikNCmBgYA0KYGBge3J9DQpnZ3Bsb3QobXRjYXJzX2J5X2N5bCwgYWVzKHggPSBjeWwsIHkgPSBtZWFuX3d0KSkgKw0KICAjIFNldCB0aGUgd2lkdGggYWVzdGhldGljIHRvIHByb3ANCiAgZ2VvbV9jb2woZmlsbCA9ICJza3libHVlIiwgYWVzKHdpZHRoID0gcHJvcCkpDQpgYGANCmBgYHtyfQ0KZ2dwbG90KG10Y2Fyc19ieV9jeWwsIGFlcyh4ID0gY3lsLCB5ID0gbWVhbl93dCkpICsNCiAgZ2VvbV9jb2woYWVzKHdpZHRoID0gcHJvcCksIGZpbGwgPSAic2t5Ymx1ZSIpICsNCiAgIyBBZGQgYW4gZXJyb3JiYXIgbGF5ZXINCiAgZ2VvbV9lcnJvcmJhcigNCiAgICAjIC4uLiBhdCBtZWFuIHdlaWdodCBwbHVzIG9yIG1pbnVzIDEgc3RkIGRldg0KICAgIGFlcyh5bWluID0gbWVhbl93dCAtIHNkX3d0LA0KICAgIHltYXggPSBtZWFuX3d0ICsgc2Rfd3QpLA0KICAgICMgd2l0aCB3aWR0aCAwLjENCiAgICB3aWR0aCA9IDAuMQ0KICApDQpgYGANClRoaXMgaXMgYSBnb29kIHN0YXJ0LCBidXQgaXQncyBkaWZmaWN1bHQgdG8gYWRqdXN0IHRoZSBzcGFjaW5nIGJldHdlZW4gdGhlIGJhcnMuDQoNCiMjIEhlYXRtYXBzDQoNClNpbmNlIGhlYXQgbWFwcyBlbmNvZGUgY29sb3Igb24gYSBjb250aW51b3VzIHNjYWxlLCB0aGV5IGFyZSBkaWZmaWN1bHQgdG8gYWNjdXJhdGVseSBkZWNvZGUuIEhlbmNlLCBoZWF0IG1hcHMgYXJlIG1vc3QgdXNlZnVsIGlmIHlvdSBoYXZlIGEgc21hbGwgbnVtYmVyIG9mIGJveGVzIGFuZC9vciBhIGNsZWFyIHBhdHRlcm4gdGhhdCBhbGxvd3MgeW91IHRvIG92ZXJjb21lIGRlY29kaW5nIGRpZmZpY3VsdGllcy4NCg0KVG8gcHJvZHVjZSB0aGVtLCBtYXAgdHdvIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBvbnRvIHRoZSB4IGFuZCB5IGFlc3RoZXRpY3MsIGFsb25nIHdpdGggYSBjb250aW51b3VzIHZhcmlhYmxlIG9udG8gZmlsbC4gVGhlIGdlb21fdGlsZSgpIGxheWVyIGFkZHMgdGhlIGJveGVzLg0KYGBge3J9DQpsaWJyYXJ5KGxhdHRpY2UpDQpgYGANCmBgYHtyfQ0Kc3RyKGJhcmxleSkNCmBgYA0KYGBge3J9DQojIFVzaW5nIGJhcmxleSwgcGxvdCB2YXJpZXR5IHZzLiB5ZWFyLCBmaWxsZWQgYnkgeWllbGQNCmdncGxvdChiYXJsZXksIGFlcyh4ID0geWVhciwgeSA9IHZhcmlldHksIGZpbGwgPSB5aWVsZCkpICsNCiAgIyBBZGQgYSB0aWxlIGdlb20NCiAgZ2VvbV90aWxlKCkNCmBgYA0KYGBge3J9DQojIFByZXZpb3VzbHkgZGVmaW5lZA0KZ2dwbG90KGJhcmxleSwgYWVzKHggPSB5ZWFyLCB5ID0gdmFyaWV0eSwgZmlsbCA9IHlpZWxkKSkgKw0KICBnZW9tX3RpbGUoKSArIA0KICAjIEZhY2V0LCB3cmFwcGluZyBieSBzaXRlLCB3aXRoIDEgY29sdW1uDQogIGZhY2V0X3dyYXAoZmFjZXRzID0gdmFycyhzaXRlKSwgbmNvbCA9IDEpICsNCiAgIyBBZGQgYSBmaWxsIHNjYWxlIHVzaW5nIGFuIDItY29sb3IgZ3JhZGllbnQNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAid2hpdGUiLCBoaWdoID0gInJlZCIpDQpgYGANCmBgYHtyfQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQojIEEgcGFsZXR0ZSBvZiA5IHJlZHMNCnJlZF9icmV3ZXJfcGFsZXR0ZSA8LSBicmV3ZXIucGFsKDksICJSZWRzIikNCg0KIyBVcGRhdGUgdGhlIHBsb3QNCmdncGxvdChiYXJsZXksIGFlcyh4ID0geWVhciwgeSA9IHZhcmlldHksIGZpbGwgPSB5aWVsZCkpICsNCiAgZ2VvbV90aWxlKCkgKyANCiAgZmFjZXRfd3JhcChmYWNldHMgPSB2YXJzKHNpdGUpLCBuY29sID0gMSkgKw0KICAjIFVwZGF0ZSBzY2FsZSB0byB1c2Ugbi1jb2xvcnMgZnJvbSByZWRfYnJld2VyX3BhbGV0dGUNCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3JzID0gcmVkX2JyZXdlcl9wYWxldHRlKQ0KYGBgDQpXZSBjYW4gY29udGludWUgYnkgdXNpbmcgYnJlYWtzLCBsaW1pdHMgYW5kIGxhYmVscyB0byBtb2RpZnkgdGhlIGZpbGwgc2NhbGUgYW5kIHVwZGF0ZSB0aGUgdGhlbWUsIGJ1dCB0aGlzIGlzIGEgcHJldHR5IGdvb2Qgc3RhcnQuDQoNCiMjIyBIZWF0IG1hcCBhbHRlcm5hdGl2ZXMNCg0KVGhlcmUgYXJlIHNldmVyYWwgYWx0ZXJuYXRpdmVzIHRvIGhlYXQgbWFwcy4gVGhlIGJlc3QgY2hvaWNlIHJlYWxseSBkZXBlbmRzIG9uIHRoZSBkYXRhIGFuZCB0aGUgc3RvcnkgeW91IHdhbnQgdG8gdGVsbCB3aXRoIHRoaXMgZGF0YS4gSWYgdGhlcmUgaXMgYSB0aW1lIGNvbXBvbmVudCwgdGhlIG1vc3Qgb2J2aW91cyBjaG9pY2UgaXMgYSBsaW5lIHBsb3QuDQoNCmBgYHtyfQ0KIyBVc2luZyBiYXJsZXksIHBsb3QgeWllbGQgdnMuIHllYXIsIGNvbG9yZWQgYW5kIGdyb3VwZWQgYnkgdmFyaWV0eQ0KZ2dwbG90KGJhcmxleSwgYWVzKHggPSB5ZWFyLCB5ID0geWllbGQsIGNvbG9yID0gdmFyaWV0eSwgZ3JvdXAgPSB2YXJpZXR5KSkgKw0KICAjIEFkZCBhIGxpbmUgbGF5ZXINCiAgZ2VvbV9saW5lKCkgKw0KICAjIEZhY2V0LCB3cmFwcGluZyBieSBzaXRlLCB3aXRoIDEgcm93DQogIGZhY2V0X3dyYXAoIH4gc2l0ZSwgbnJvdyA9IDEpDQpgYGANCmBgYHtyfQ0KIyBVc2luZyBiYXJlbHksIHBsb3QgeWllbGQgdnMuIHllYXIsIGNvbG9yZWQsIGdyb3VwZWQsIGFuZCBmaWxsZWQgYnkgc2l0ZQ0KZ2dwbG90KGJhcmxleSwgYWVzKHggPSB5ZWFyLCB5ID0geWllbGQsIGNvbG9yID0gc2l0ZSwgZ3JvdXAgPSBzaXRlLCBmaWxsID0gc2l0ZSkpICsNCiAgIyBBZGQgYSBsaW5lIHN1bW1hcnkgc3RhdCBhZ2dyZWdhdGVkIGJ5IG1lYW4NCiAgc3RhdF9zdW1tYXJ5KGZ1bi55ID0gbWVhbiwgZ2VvbSA9ICJsaW5lIikgKw0KICAjIEFkZCBhIHJpYmJvbiBzdW1tYXJ5IHN0YXQgd2l0aCAxMCUgb3BhY2l0eSwgbm8gY29sb3INCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gbWVhbl9zZGwsIGZ1bi5hcmdzID0gbGlzdChtdWx0ID0gMSksIGdlb20gPSAicmliYm9uIiwgYWxwaGEgPSAwLjEsIGNvbG9yID0gTkEpDQpgYGANCiBXaGVuZXZlciB5b3Ugc2VlIGEgaGVhdCBtYXAsIGFzayB5b3Vyc2VsZiBpdCBpdCdzIHJlYWxseSBuZWNlc3NhcnkuIE1hbnkgcGVvcGxlIHVzZSB0aGVtIGJlY2F1c2UgdGhleSBsb29rIGZhbmN5IGFuZCBjb21wbGljYXRlZCAtIHNpZ25zIG9mIHBvb3IgY29tbXVuaWNhdGlvbiBza2lsbHMuDQoNCiMjIFdoZW4gZ29vZCBkYXRhIG1ha2VzIGJhZCBwbG90cw0KV2hlbiB5b3UgZmlyc3QgZW5jb3VudGVyIGEgZGF0YSB2aXN1YWxpemF0aW9uLCBlaXRoZXIgZnJvbSB5b3Vyc2VsZiBvciBhIGNvbGxlYWd1ZSwgeW91IGFsd2F5cyB3YW50IHRvIGNyaXRpY2FsbHkgYXNrIGlmIGl0J3Mgb2JzY3VyaW5nIHRoZSBkYXRhIGluIGFueSB3YXkuDQoNCkxldCdzIHRha2UgYSBsb29rIGF0IHRoZSBzdGVwcyB3ZSBjb3VsZCB0YWtlIHRvIHByb2R1Y2UgYW5kIGltcHJvdmUgdGhlIHBsb3QgaW4gdGhlIHZpZXcuDQoNClRoZSBkYXRhIGNvbWVzIGZyb20gYW4gZXhwZXJpbWVudCB3aGVyZSB0aGUgZWZmZWN0IG9mIHR3byBkaWZmZXJlbnQgdHlwZXMgb2Ygdml0YW1pbiBDIHNvdXJjZXMsIG9yYW5nZSBqdWljZSBvciBhc2NvcmJpYyBhY2lkLCB3ZXJlIHRlc3RlZCBvbiB0aGUgZ3Jvd3RoIG9mIHRoZSBvZG9udG9ibGFzdHMgKGNlbGxzIHJlc3BvbnNpYmxlIGZvciB0b290aCBncm93dGgpIGluIDYwIGd1aW5lYSBwaWdzLg0KDQpUaGUgZGF0YSBpcyBzdG9yZWQgaW4gdGhlIFRHIGRhdGEgZnJhbWUsIHdoaWNoIGNvbnRhaW5zIHRocmVlIHZhcmlhYmxlczogZG9zZSwgbGVuLCBhbmQgc3VwcC4NCmBgYHtyfQ0KbGlicmFyeShkYXRhc2V0cykNClRHID0gKFRvb3RoR3Jvd3RoKQ0Kc3RyKFRHKQ0KYGBgDQpgYGB7cn0NCiMgSW5pdGlhbCBwbG90DQpncm93dGhfYnlfZG9zZSA8LSBnZ3Bsb3QoVEcsIGFlcyhkb3NlLCBsZW4sIGNvbG9yID0gc3VwcCkpICsNCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gbWVhbl9zZGwsDQogICAgICAgICAgICAgICBmdW4uYXJncyA9IGxpc3QobXVsdCA9IDEpLA0KICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjEpKSArDQogIHRoZW1lX2dyYXkoMykNCg0KIyBWaWV3IHBsb3QNCmdyb3d0aF9ieV9kb3NlDQpgYGANCmBgYHtyfQ0KIyBjamhhbmdlIHRoZW1lX2dyYXkoMykgdG8gdGhlbWVfY2xhc3NpYygpDQpncm93dGhfYnlfZG9zZSA8LSBnZ3Bsb3QoVEcsIGFlcyhkb3NlLCBsZW4sIGNvbG9yID0gc3VwcCkpICsNCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gbWVhbl9zZGwsDQogICAgICAgICAgICAgICBmdW4uYXJncyA9IGxpc3QobXVsdCA9IDEpLA0KICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjEpKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KDQojIFZpZXcgcGxvdA0KZ3Jvd3RoX2J5X2Rvc2UNCmBgYA0KYGBge3J9DQojIENoYW5nZSB0eXBlDQpURyRkb3NlIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKFRHJGRvc2UpKQ0KDQojIFBsb3QNCmdyb3d0aF9ieV9kb3NlIDwtIGdncGxvdChURywgYWVzKGRvc2UsIGxlbiwgY29sb3IgPSBzdXBwKSkgKw0KICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBtZWFuX3NkbCwNCiAgICAgICAgICAgICAgIGZ1bi5hcmdzID0gbGlzdChtdWx0ID0gMSksDQogICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuMikpICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCiMgVmlldyBwbG90DQpncm93dGhfYnlfZG9zZQ0KYGBgDQpgYGB7cn0NCiMgQ2hhbmdlIHR5cGUNClRHJGRvc2UgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoVEckZG9zZSkpDQoNCiMgUGxvdA0KZ3Jvd3RoX2J5X2Rvc2UgPC0gZ2dwbG90KFRHLCBhZXMoZG9zZSwgbGVuLCBjb2xvciA9IHN1cHApKSArDQogIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IG1lYW5fc2RsLA0KICAgICAgICAgICAgICAgZnVuLmFyZ3MgPSBsaXN0KG11bHQgPSAxKSwNCiAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMC4yKSkgKw0KICAjIFVzZSB0aGUgcmlnaHQgZ2VvbWV0cnkNCiAgc3RhdF9zdW1tYXJ5KGZ1bi55ID0gbWVhbiwNCiAgICAgICAgICAgICAgIGdlb20gPSAibGluZSIsDQogICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuMSkpICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCiMgVmlldyBwbG90DQpncm93dGhfYnlfZG9zZQ0KYGBgDQpgYGB7cn0NCiMgQ2hhbmdlIHR5cGUNClRHJGRvc2UgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoVEckZG9zZSkpDQoNCiMgUGxvdA0KZ3Jvd3RoX2J5X2Rvc2UgPC0gZ2dwbG90KFRHLCBhZXMoZG9zZSwgbGVuLCBjb2xvciA9IHN1cHApKSArDQogIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IG1lYW5fc2RsLA0KICAgICAgICAgICAgICAgZnVuLmFyZ3MgPSBsaXN0KG11bHQgPSAxKSwNCiAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMC4yKSkgKw0KICBzdGF0X3N1bW1hcnkoZnVuLnkgPSBtZWFuLA0KICAgICAgICAgICAgICAgZ2VvbSA9ICJsaW5lIiwNCiAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMC4xKSkgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICAjIEFkanVzdCBsYWJlbHMgYW5kIGNvbG9yczoNCiAgbGFicyh4ID0gIkRvc2UgKG1nL2RheSkiLCB5ID0gIk9kb250b2JsYXN0cyBsZW5ndGggKG1lYW4sIHN0YW5kYXJkIGRldmlhdGlvbikiLCBjb2xvciA9ICJTdXBwbGVtZW50IikgKw0KICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIiwgbGFiZWxzID0gYygiT3JhbmdlIGp1aWNlIiwgIkFzY29yYmljIGFjaWQiKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLDM1KSwgYnJlYWtzID0gc2VxKDAsIDM1LCA1KSwgZXhwYW5kID0gYygwLDApKQ0KDQojIFZpZXcgcGxvdA0KZ3Jvd3RoX2J5X2Rvc2UNCmBgYA0KDQo=