Page last updated: 04 May, 2019

 

1 Intro

These pages go through the process of making different types of plots. We use simulated data that epidemiology researchers are likely to have access to, as well as larger data and genome-wide association data, and a correlation matrix researchers may come across during their work. You can download the .Rmd file from which this page is built by clicking the ‘Code’ tab at the top of the page.

 

First, we need some simulated data to use for the plots. Additionally we can spice up the plots by making our own colour choices and deciding what we want the plot style to be like.

 

2 Data

2.1 Basic data

Simulate some data that is likely to be available to an epidemiology researcher. This data has a simple structure of a top level (group) with subgroups (subgroup) and individual data (name).

# Data - create a dataframe of simulated data to use in our plots
name <- rep(LETTERS[1:10], length.out = 30)
group <- as.factor(rep(c("Red", "Green", "Black"), length.out = 30))
subgroup <- as.factor(rep(c(1:2), length.out = 30))
estimate = rnorm(n = 30, 0, 0.1)
se <- estimate/3
p = runif(n = 30, min = 0, max = 0.1)
data <- data.frame(name, group, subgroup, estimate, se, p)
data$group <- factor(data$group, levels = c("Black", "Red", "Green"))
dim(data)
[1] 30  6
head(data)

 

2.2 Larger data

For a larger and more comprehensive data set the ggplot2 package has an extended package (ggplot2movies) that contains a large data set of data from IMDB which we can use. The data is of thousands of films (title) and data on those films including a top level grouping (Genre) and year, length, budget, rating and number of votes.

##### Packages
library(ggplot2)
library(ggplot2movies)

##### Data
movies <- movies

##### Create a genre variable for the data set
genre <- rep(0,nrow(movies))
for(i in 18:24)
{
  genre[movies[,i]==1] <- names(movies)[i]
}; genre[genre==0] <- "Unknown"
movies$Genre <- as.factor(genre)

dim(movies)
[1] 58788    25
head(movies)

 

2.3 Genome wide data

For GWAS data the qqman package has a dummy GWAS data set we can use. The data is not as large as a full GWAS data set, but for testing plots it provides enough information

library(qqman)
gwas_data <- gwasResults
dim(gwas_data)
[1] 16470     4
head(gwas_data)

 

2.4 Correlation matrix

Simulate a matrix data set that is likely to be available to an epidemiology researcher.

matrix_data <- matrix(runif(100, min = 0, max = 1), ncol=10) 
matrix_data <- (matrix_data * lower.tri(matrix_data)) + t(matrix_data * lower.tri(matrix_data)) # If you want the upper and lower triangles of the matrix to be the same
diag(matrix_data) <- 1 

dim(matrix_data)
[1] 10 10
head(as.data.frame(matrix_data))

 

3 Theme

I use a custom ggplot theme for my plots because I don’t like the themes ggplot uses. The code for my theme is at the end of the document in the my_theme section. You can adapt this, start from scratch, edit specific aspects of the theme with the theme() functon, or use a preset theme from ggplot such as theme_bw().

 

In addition, I use a custom discrete and continuosu colour pallete using the wesanderson and yarrr packages.

##### Packages
library("wesanderson")
library("yarrr")
##### Palettes
d1 <- wes_palette("Royal1", type = "discrete")
d2 <- wes_palette("GrandBudapest2", type = "discrete")
d3 <- wes_palette("Cavalcanti1", type = "discrete")
discrete_wes_pal <- c(d1, d2, d3)
continuous_wes_pal <- wes_palette("Zissou1", 100, type = "continuous")

##### Palettes
d1

d2

d3

continuous_wes_pal

 

4 Hidden code

4.1 My theme

my_theme <- function () 
  { 
  ggplot2::theme(
    
    # high level arguments
    
    ## line - all line elements (element_line())
    line = element_line(colour = "black",
                        size = 0.5,
                        linetype = 1,
                        lineend = "butt",
                        arrow = FALSE),

    ## rectangular - all rectangular elements (element_rect())
    rect = element_rect(fill = NULL,
                        colour = NULL,
                        size = 0.5,
                        linetype = 1),
    
    ## text - all text elements (element_text())
    text = element_text(family = "Helvetica",
                        size = 11,
                        hjust = 0.5,
                        vjust = 0,
                        face = "bold",
                        color = "#222222"),
    
    ## title - all title elements: plot, axes, legends (element_text(); inherits from text)
    title = element_text(family = "Helvetica",
                         size = 11,
                         hjust = 0.5,
                         vjust = 0,
                         face = "bold",
                         color = "#222222"),
    ## margin
    #margin = margin(t = 5.5, r = 5.5, b = 5.5, l = 5.5, unit = "pt"),    
    
    
    # axis
    
    ## title - labels of axes (element_text()). Specify all axes' labels (axis.title), labels by plane (using axis.title.x or axis.title.y), or individually for each axis (using axis.title.x.bottom, axis.title.x.top, axis.title.y.left, axis.title.y.right). axis.title.*.* inherits from axis.title.* which inherits from axis.title, which in turn inherits from text
    axis.title = element_text(family = "Helvetica", size = 11, hjust = 0.5, vjust = 0.5, face = "bold", color = "#222222"), 
    axis.title.x = element_text(family = "Helvetica", size = 11, hjust = 0.5, vjust = 0.5, face = "bold", color = "#222222"),
    axis.title.y = element_text(family = "Helvetica", size = 11, hjust = 0.5, vjust = 0.5, face = "bold", color = "#222222"),
    axis.title.x.top = element_text(family = "Helvetica", size = 11, hjust = 0.5, vjust = 0.5, face = "bold", color = "#222222"),
    axis.title.x.bottom = element_text(family = "Helvetica", size = 11, hjust = 0.5, vjust = 0.5, face = "bold", color = "#222222"),
    axis.title.y.left = element_text(family = "Helvetica", size = 11, hjust = 0.5, vjust = 0.5, face = "bold", color = "#222222"),
    axis.title.y.right = element_text(family = "Helvetica", size = 11, hjust = 0.5, vjust = 0.5, face = "bold", color = "#222222"),
    
    ## text of tick labels - tick labels along axes (element_text()). Specify all axis tick labels (axis.text), tick labels by plane (using axis.text.x or axis.text.y), or individually for each axis (using axis.text.x.bottom, axis.text.x.top, axis.text.y.left, axis.text.y.right). axis.text.*.* inherits from axis.text.* which inherits from axis.text, which in turn inherits from text
    axis.text = element_text(family = "Helvetica", size = 11, hjust = 0.5, vjust = 0.5, face = "bold", color = "#222222"), 
    
    ## ticks - tick marks along axes (element_line()). Specify all tick marks (axis.ticks), ticks by plane (using axis.ticks.x or axis.ticks.y), or individually for each axis (using axis.ticks.x.bottom, axis.ticks.x.top, axis.ticks.y.left, axis.ticks.y.right). axis.ticks.*.* inherits from axis.ticks.* which inherits from axis.ticks, which in turn inherits from line
    axis.ticks = element_line(colour = "black", size = 0.5, linetype = 1, lineend = "butt", arrow = FALSE),
    
    ## tick length - length of tick marks (unit)
    axis.ticks.length = unit(2.75, "pt"),
    
    ## axis lines - lines along axes (element_line()). Specify lines along all axes (axis.line), lines for each plane (using axis.line.x or axis.line.y), or individually for each axis (using axis.line.x.bottom, axis.line.x.top, axis.line.y.left, axis.line.y.right). axis.line.*.* inherits from axis.line.* which inherits from axis.line, which in turn inherits from line
    axis.line = element_blank(),
   
    
    # legend

    ## background - background of legend (element_rect(); inherits from rect)
    legend.background = element_blank(),

    ## margin - the margin around each legend (margin())
    legend.margin = margin(t = 5.5, r = 5.5, b = 5.5, l = 5.5, unit = "pt"),

    ## spacing - the spacing between legends (unit). legend.spacing.x & legend.spacing.y inherit from legend.spacing or can be specified separately
    legend.spacing = unit(11, "pt"),
    legend.spacing.x = unit(11, "pt"),
    legend.spacing.y = unit(11, "pt"),
    
    ## key - background underneath legend keys (element_rect(); inherits from rect)
    legend.key = element_rect(fill = NULL, colour = NULL, size = 0.5, linetype = 1),

    ## key size - size of legend keys (unit); key background height & width inherit from legend.key.size or can be specified separately
    legend.key.size = unit(1.2, "pt"),
    legend.key.height = unit(1.2, "pt"),
    legend.key.width = unit(1.2, "pt"),
    
    ## text - legend item labels (element_text(); inherits from text)
    legend.text = element_text(family = "Helvetica", size = 11, hjust = 0.5, vjust = 0.5, face = "bold", color = "#222222"), 

    ## text alignment - alignment of legend labels (number from 0 (left) to 1 (right))
    legend.text.align = 0, 
    
    ## title - title of legend (element_text(); inherits from title)
    legend.title = element_text(family = "Helvetica", size = 11, hjust = 0.5, vjust = 0.5, face = "bold", color = "#222222"), 

    ## title alignment - alignment of legend title (number from 0 (left) to 1 (right))
    legend.title.align = 0.5,
    
    ## position - the position of legends ("none", "left", "right", "bottom", "top", or two-element numeric vector)
    legend.position = "bottom", #c(0.5,0)

    ## direction - layout of items in legends ("horizontal" or "vertical")
    legend.direction = "horizontal",
    
    ## justification - anchor point for positioning legend inside plot ("center" or two-element numeric vector) or the justification according to the plot area when positioned outside the plot
    legend.justification = "center", # c(0.5,0.5)

    ## multiple legends 
    ### arrnagement - arrangement of multiple legends ("horizontal" or "vertical")
    legend.box = "horizontal",
    
    ### justification - justification of each legend within the overall bounding box, when there are multiple legends ("top", "bottom", "left", or "right")
    legend.box.just = "top",

    ### margin - margins around the full legend area, as specified using margin()
    legend.box.margin = margin(t = 5.5, r = 5.5, b = 5.5, l = 5.5, unit = "pt"),
    
    ### background - background of legend area (element_rect(); inherits from rect)
    legend.box.background = element_blank(),
    
    ### spacing - The spacing between the plotting area and the legend box (unit)
    legend.box.spacing = unit(11, "pt"),
    
    
    # panel
    
    ## background - # background of plotting area, drawn underneath plot (element_rect(); inherits from rect)
    panel.background = element_blank(), 
    
    ## border - border around plotting area, drawn on top of plot so that it covers tick marks and grid lines. This should be used with fill = NA (element_rect(); inherits from rect)
    panel.border = element_blank(), 
    
    ## spacing - spacing between facet panels (unit). panel.spacing.x & panel.spacing.y inherit from panel.spacing or can be specified separately.
    panel.spacing = unit(5.5, "pt"),
    panel.spacing.x = unit(5.5, "pt"),
    panel.spacing.y = unit(5.5, "pt"),
    
    ## grid - grid lines (element_line()). Specify major grid lines, or minor grid lines separately (using panel.grid.major or panel.grid.minor) or individually for each axis (using panel.grid.major.x, panel.grid.minor.x, panel.grid.major.y, panel.grid.minor.y). Y axis grid lines are horizontal and x axis grid lines are vertical. panel.grid.*.* inherits from panel.grid.* which inherits from panel.grid, which in turn inherits from line
    panel.grid  = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    panel.grid.major.x = element_blank(),
    panel.grid.minor.x = element_blank(),
    panel.grid.major.y = element_blank(),
    panel.grid.minor.y = element_blank(),
    
    # option to place the panel (background, gridlines) over the data layers (logical). Usually used with a transparent or blank panel.background.
    panel.ontop = FALSE,
    
    
    # plot
    
    ## background - background of the entire plot (element_rect(); inherits from rect)
    plot.background = element_blank(),

    ## title - plot title (text appearance) (element_text(); inherits from title) left-aligned by default
    plot.title = element_text(family = "Helvetica", size = 16, hjust = 0.5, vjust = 0, face = "bold", color = "#222222"), 
    
    ## subtitle - plot subtitle (text appearance) (element_text(); inherits from title) left-aligned by default
    plot.subtitle = element_text(family = "Helvetica", size = 14, hjust = 0.5, vjust = 0, face = "bold", color = "#222222"), 
    
    ## caption - caption below the plot (text appearance) (element_text(); inherits from title) right-aligned by default
    plot.caption = element_text(family = "Helvetica", size = 8, hjust = 0.5, vjust = 0, face = "bold", color = "#222222"),
    
    ## tag - upper-left label to identify a plot (text appearance) (element_text(); inherits from title) left-aligned by default
    plot.tag = element_text(family = "Helvetica", size = 8, hjust = 0.5, vjust = 0, face = "bold", color = "#222222"),

    ## tag position - The position of the tag as a string ("topleft", "top", "topright", "left", "right", "bottomleft", "bottom", "bottomright) or a coordinate. If a string, extra space will be added to accommodate the tag.
    plot.tag.position = "topleft",
    
    ## margin - margin around entire plot (unit with the sizes of the top, right, bottom, and left margins)
    plot.margin = margin(t = 5.5, r = 5.5, b = 5.5, l = 5.5, unit = "pt"),
   
    
    # strip (used when facetting a plot)

    ## background - background of facet labels (element_rect(); inherits from rect). Horizontal facet background (strip.background.x) & vertical facet background (strip.background.y) inherit from strip.background or can be specified separately
    strip.background = element_blank(),
    strip.background.x = element_blank(), 
    strip.background.y = element_blank(),

    # placement - placement of strip with respect to axes, either "inside" or "outside". Only important when axes and strips are on the same side of the plot.
    strip.placement = "inside",
    
    # text - facet labels (element_text(); inherits from text). Horizontal facet labels (strip.text.x) & vertical facet labels (strip.text.y) inherit from strip.text or can be specified separately
    strip.text = element_text(family = "Helvetica", size = 11, hjust = 0.5, vjust = 0.5, angle = 0, face = "bold", color = "#222222"),
    strip.text.x = element_text(family = "Helvetica", size = 11, hjust = 0.5, vjust = 0.5, angle = 0, face = "bold", color = "#222222"),
    strip.text.y = element_text(family = "Helvetica", size = 11, hjust = 0.5, vjust = 0.5, angle = 0, face = "bold", color = "#222222"),
    
    # padding - space between strips and axes when strips are switched (unit)
    strip.switch.pad.grid = unit(2.75, "pt"),
    
    # padding - space between strips and axes when strips are switched (unit)
    strip.switch.pad.wrap = unit(2.75, "pt")
  )
}

4.2 Session info

sessionInfo()
## R version 3.5.3 (2019-03-11)
## Platform: x86_64-apple-darwin15.6.0 (64-bit)
## Running under: macOS Mojave 10.14.4
## 
## Matrix products: default
## BLAS: /Library/Frameworks/R.framework/Versions/3.5/Resources/lib/libRblas.0.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/3.5/Resources/lib/libRlapack.dylib
## 
## locale:
## [1] en_GB.UTF-8/en_GB.UTF-8/en_GB.UTF-8/C/en_GB.UTF-8/en_GB.UTF-8
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## loaded via a namespace (and not attached):
##  [1] compiler_3.5.3  magrittr_1.5    tools_3.5.3     htmltools_0.3.6
##  [5] base64enc_0.1-3 yaml_2.2.0      Rcpp_1.0.1      stringi_1.4.3  
##  [9] rmarkdown_1.12  knitr_1.22      stringr_1.4.0   xfun_0.6       
## [13] digest_0.6.18   evaluate_0.13
LS0tCnRpdGxlOiAiUGxvdHMgb3ZlcnZpZXciCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvY19mbG9hdDogCiAgICAgICAgc21vb3RoX3Njcm9sbDogZmFsc2UKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgaGlnaGxpZ2h0ZXI6IG51bGwKLS0tCgpgYGB7ciBzZXR1cCwgZXZhbD1UUlVFLCBpbmNsdWRlPUZBTFNFLCBlY2hvPUZBTFNFLCBlcnJvcj1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY2FjaGU9VFJVRSwgZmlnLmFsaWduPSdjZW50ZXInLCBjb21tZW50PSIifQojIyMjIyBjaGVjayByZXF1aXJlZCBwYWNrYWdlcyBhcmUgaW5zdGFsbGVkLiBpZiBub3QgaW5zdGFsbGVkIGluc3RhbGwgdGhlbQpsaXN0X29mX3BhY2thZ2VzIDwtIGMoImJvb2tkb3duIiwgImdncGxvdDIiLCAiZ2dwbG90Mm1vdmllcyIsICJxcW1hbiIsICJnZ2ZvcmVzdHBsb3QiLCAiY293cGxvdCIsICJnZ3JlcGVsIiwgImRwbHlyIikKbmV3LnBhY2thZ2VzIDwtIGxpc3Rfb2ZfcGFja2FnZXNbIShsaXN0X29mX3BhY2thZ2VzICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKClbLCJQYWNrYWdlIl0pXQppZihsZW5ndGgobmV3LnBhY2thZ2VzKSkgaW5zdGFsbC5wYWNrYWdlcyhuZXcucGFja2FnZXMpCmxhcHBseShsaXN0X29mX3BhY2thZ2VzLCBsaWJyYXJ5LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpCmBgYAoKX19fClBhZ2UgbGFzdCB1cGRhdGVkOiBgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCLCAlWScpYAoKXCAgCgojIEludHJvIHsjaW50cm99CgpUaGVzZSBwYWdlcyBnbyB0aHJvdWdoIHRoZSBwcm9jZXNzIG9mIG1ha2luZyBkaWZmZXJlbnQgdHlwZXMgb2YgcGxvdHMuIFdlIHVzZSBbc2ltdWxhdGVkIGRhdGFdKCNzaW1wbGVfZGF0YSkgdGhhdCBlcGlkZW1pb2xvZ3kgcmVzZWFyY2hlcnMgYXJlIGxpa2VseSB0byBoYXZlIGFjY2VzcyB0bywgYXMgd2VsbCBhcyBbbGFyZ2VyIGRhdGFdKCNtb3ZpZXMpIGFuZCBbZ2Vub21lLXdpZGUgYXNzb2NpYXRpb24gZGF0YV0oI2d3YXNfZGF0YSksIGFuZCBhIFtjb3JyZWxhdGlvbiBtYXRyaXhdKCNtYXRyaXhfY29ycikgcmVzZWFyY2hlcnMgbWF5IGNvbWUgYWNyb3NzIGR1cmluZyB0aGVpciB3b3JrLiBZb3UgY2FuIGRvd25sb2FkIHRoZSBgLlJtZGAgZmlsZSBmcm9tIHdoaWNoIHRoaXMgcGFnZSBpcyBidWlsdCBieSBjbGlja2luZyB0aGUgJ0NvZGUnIHRhYiBhdCB0aGUgdG9wIG9mIHRoZSBwYWdlLgoKXCAgCgpGaXJzdCwgd2UgbmVlZCBzb21lIHNpbXVsYXRlZCBkYXRhIHRvIHVzZSBmb3IgdGhlIHBsb3RzLiBBZGRpdGlvbmFsbHkgd2UgY2FuIHNwaWNlIHVwIHRoZSBwbG90cyBieSBtYWtpbmcgb3VyIG93biBjb2xvdXIgY2hvaWNlcyBhbmQgZGVjaWRpbmcgd2hhdCB3ZSB3YW50IHRoZSBwbG90IHN0eWxlIHRvIGJlIGxpa2UuCgpcICAKCiMgRGF0YSB7I2RhdGEgLnRhYnNldH0KCiMjIEJhc2ljIGRhdGEgeyNzaW1wbGVfZGF0YX0KClNpbXVsYXRlIHNvbWUgZGF0YSB0aGF0IGlzIGxpa2VseSB0byBiZSBhdmFpbGFibGUgdG8gYW4gZXBpZGVtaW9sb2d5IHJlc2VhcmNoZXIuIFRoaXMgZGF0YSBoYXMgYSBzaW1wbGUgc3RydWN0dXJlIG9mIGEgdG9wIGxldmVsIChgZ3JvdXBgKSB3aXRoIHN1Ymdyb3VwcyAoYHN1Ymdyb3VwYCkgYW5kIGluZGl2aWR1YWwgZGF0YSAoYG5hbWVgKS4KYGBge3Igc2ltcGxlX2RhdGEsIGV2YWw9VFJVRSwgaW5jbHVkZT1UUlVFLCBlY2hvPVRSVUUsIGVycm9yPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjYWNoZT1UUlVFLCBmaWcuYWxpZ249J2NlbnRlcicsIGNvbW1lbnQ9IiJ9CiMgRGF0YSAtIGNyZWF0ZSBhIGRhdGFmcmFtZSBvZiBzaW11bGF0ZWQgZGF0YSB0byB1c2UgaW4gb3VyIHBsb3RzCm5hbWUgPC0gcmVwKExFVFRFUlNbMToxMF0sIGxlbmd0aC5vdXQgPSAzMCkKZ3JvdXAgPC0gYXMuZmFjdG9yKHJlcChjKCJSZWQiLCAiR3JlZW4iLCAiQmxhY2siKSwgbGVuZ3RoLm91dCA9IDMwKSkKc3ViZ3JvdXAgPC0gYXMuZmFjdG9yKHJlcChjKDE6MiksIGxlbmd0aC5vdXQgPSAzMCkpCmVzdGltYXRlID0gcm5vcm0obiA9IDMwLCAwLCAwLjEpCnNlIDwtIGVzdGltYXRlLzMKcCA9IHJ1bmlmKG4gPSAzMCwgbWluID0gMCwgbWF4ID0gMC4xKQpkYXRhIDwtIGRhdGEuZnJhbWUobmFtZSwgZ3JvdXAsIHN1Ymdyb3VwLCBlc3RpbWF0ZSwgc2UsIHApCmRhdGEkZ3JvdXAgPC0gZmFjdG9yKGRhdGEkZ3JvdXAsIGxldmVscyA9IGMoIkJsYWNrIiwgIlJlZCIsICJHcmVlbiIpKQpkaW0oZGF0YSkKaGVhZChkYXRhKQpgYGAKClwgIAoKIyMgTGFyZ2VyIGRhdGEgeyNtb3ZpZXN9CgpGb3IgYSBsYXJnZXIgYW5kIG1vcmUgY29tcHJlaGVuc2l2ZSBkYXRhIHNldCB0aGUgW2BnZ3Bsb3QyYF0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvKSBwYWNrYWdlIGhhcyBhbiBleHRlbmRlZCBwYWNrYWdlIChbYGdncGxvdDJtb3ZpZXNgXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZ2dwbG90Mm1vdmllcy8pKSB0aGF0IGNvbnRhaW5zIGEgbGFyZ2UgZGF0YSBzZXQgb2YgZGF0YSBmcm9tIElNREIgd2hpY2ggd2UgY2FuIHVzZS4gVGhlIGRhdGEgaXMgb2YgdGhvdXNhbmRzIG9mIGZpbG1zIChgdGl0bGVgKSBhbmQgZGF0YSBvbiB0aG9zZSBmaWxtcyBpbmNsdWRpbmcgYSB0b3AgbGV2ZWwgZ3JvdXBpbmcgKGBHZW5yZWApIGFuZCAgYHllYXJgLCBgbGVuZ3RoYCwgYGJ1ZGdldGAsIGByYXRpbmdgIGFuZCBudW1iZXIgb2YgYHZvdGVzYC4KYGBge3IgbW92aWVzLCBldmFsPVRSVUUsIGluY2x1ZGU9VFJVRSwgZWNobz1UUlVFLCBlcnJvcj1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY2FjaGU9VFJVRSwgZmlnLmFsaWduPSdjZW50ZXInLCBjb21tZW50PSIifQojIyMjIyBQYWNrYWdlcwpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dwbG90Mm1vdmllcykKCiMjIyMjIERhdGEKbW92aWVzIDwtIG1vdmllcwoKIyMjIyMgQ3JlYXRlIGEgZ2VucmUgdmFyaWFibGUgZm9yIHRoZSBkYXRhIHNldApnZW5yZSA8LSByZXAoMCxucm93KG1vdmllcykpCmZvcihpIGluIDE4OjI0KQp7CiAgZ2VucmVbbW92aWVzWyxpXT09MV0gPC0gbmFtZXMobW92aWVzKVtpXQp9OyBnZW5yZVtnZW5yZT09MF0gPC0gIlVua25vd24iCm1vdmllcyRHZW5yZSA8LSBhcy5mYWN0b3IoZ2VucmUpCgpkaW0obW92aWVzKQpoZWFkKG1vdmllcykKYGBgCgpcICAKCiMjIEdlbm9tZSB3aWRlIGRhdGEgeyNnd2FzX2RhdGF9CgpGb3IgR1dBUyBkYXRhIHRoZSBbYHFxbWFuYF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3FxbWFuL3ZpZ25ldHRlcy9xcW1hbi5odG1sKSBwYWNrYWdlIGhhcyBhIGR1bW15IEdXQVMgZGF0YSBzZXQgd2UgY2FuIHVzZS4gVGhlIGRhdGEgaXMgbm90IGFzIGxhcmdlIGFzIGEgZnVsbCBHV0FTIGRhdGEgc2V0LCBidXQgZm9yIHRlc3RpbmcgcGxvdHMgaXQgcHJvdmlkZXMgZW5vdWdoIGluZm9ybWF0aW9uIApgYGB7ciBnd2FzX2RhdGEsIGV2YWw9VFJVRSwgaW5jbHVkZT1UUlVFLCBlY2hvPVRSVUUsIGVycm9yPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjYWNoZT1UUlVFLCBmaWcuYWxpZ249J2NlbnRlcicsIGNvbW1lbnQ9IiJ9CmxpYnJhcnkocXFtYW4pCmd3YXNfZGF0YSA8LSBnd2FzUmVzdWx0cwpkaW0oZ3dhc19kYXRhKQpoZWFkKGd3YXNfZGF0YSkKYGBgCgpcICAKCiMjIENvcnJlbGF0aW9uIG1hdHJpeCB7I21hdHJpeF9jb3JyfQoKU2ltdWxhdGUgYSBtYXRyaXggZGF0YSBzZXQgdGhhdCBpcyBsaWtlbHkgdG8gYmUgYXZhaWxhYmxlIHRvIGFuIGVwaWRlbWlvbG9neSByZXNlYXJjaGVyLiAKYGBge3IgbWF0cml4X2NvcnIsIGV2YWw9VFJVRSwgaW5jbHVkZT1UUlVFLCBlY2hvPVRSVUUsIGVycm9yPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjYWNoZT1UUlVFLCBmaWcuYWxpZ249J2NlbnRlcicsIGNvbW1lbnQ9IiJ9Cm1hdHJpeF9kYXRhIDwtIG1hdHJpeChydW5pZigxMDAsIG1pbiA9IDAsIG1heCA9IDEpLCBuY29sPTEwKSAKbWF0cml4X2RhdGEgPC0gKG1hdHJpeF9kYXRhICogbG93ZXIudHJpKG1hdHJpeF9kYXRhKSkgKyB0KG1hdHJpeF9kYXRhICogbG93ZXIudHJpKG1hdHJpeF9kYXRhKSkgIyBJZiB5b3Ugd2FudCB0aGUgdXBwZXIgYW5kIGxvd2VyIHRyaWFuZ2xlcyBvZiB0aGUgbWF0cml4IHRvIGJlIHRoZSBzYW1lCmRpYWcobWF0cml4X2RhdGEpIDwtIDEgCgpkaW0obWF0cml4X2RhdGEpCmhlYWQoYXMuZGF0YS5mcmFtZShtYXRyaXhfZGF0YSkpCmBgYApcICAKCiMgVGhlbWUgeyN0aGVtZX0KCkkgdXNlIGEgY3VzdG9tIGBnZ3Bsb3RgIHRoZW1lIGZvciBteSBwbG90cyBiZWNhdXNlIEkgZG9uJ3QgbGlrZSB0aGUgdGhlbWVzIGBnZ3Bsb3RgIHVzZXMuIFRoZSBjb2RlIGZvciBteSB0aGVtZSBpcyBhdCB0aGUgZW5kIG9mIHRoZSBkb2N1bWVudCBpbiB0aGUgW215X3RoZW1lXSgjbXlfdGhlbWUpIHNlY3Rpb24uIFlvdSBjYW4gYWRhcHQgdGhpcywgW3N0YXJ0IGZyb20gc2NyYXRjaF0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3RoZW1lLmh0bWwpLCBlZGl0IHNwZWNpZmljIGFzcGVjdHMgb2YgdGhlIHRoZW1lIHdpdGggdGhlIGB0aGVtZSgpYCBmdW5jdG9uLCBvciB1c2UgYSBwcmVzZXQgdGhlbWUgZnJvbSBgZ2dwbG90YCBzdWNoIGFzIGB0aGVtZV9idygpYC4gIAoKYGBge3IgbXlfdGhlbWUsIGV2YWw9VFJVRSwgaW5jbHVkZT1GQUxTRSwgZWNobz1GQUxTRSwgZXJyb3I9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGNhY2hlPVRSVUUsIGZpZy5hbGlnbj0nY2VudGVyJywgY29tbWVudD0iIn0KbXlfdGhlbWUgPC0gZnVuY3Rpb24gKCkgCiAgeyAKICBnZ3Bsb3QyOjp0aGVtZSgKICAgIAogICAgIyBoaWdoIGxldmVsIGFyZ3VtZW50cwogICAgCiAgICAjIyBsaW5lIC0gYWxsIGxpbmUgZWxlbWVudHMgKGVsZW1lbnRfbGluZSgpKQogICAgbGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLAogICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMC41LAogICAgICAgICAgICAgICAgICAgICAgICBsaW5ldHlwZSA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgIGxpbmVlbmQgPSAiYnV0dCIsCiAgICAgICAgICAgICAgICAgICAgICAgIGFycm93ID0gRkFMU0UpLAoKICAgICMjIHJlY3Rhbmd1bGFyIC0gYWxsIHJlY3Rhbmd1bGFyIGVsZW1lbnRzIChlbGVtZW50X3JlY3QoKSkKICAgIHJlY3QgPSBlbGVtZW50X3JlY3QoZmlsbCA9IE5VTEwsCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IE5VTEwsCiAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gMSksCiAgICAKICAgICMjIHRleHQgLSBhbGwgdGV4dCBlbGVtZW50cyAoZWxlbWVudF90ZXh0KCkpCiAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJIZWx2ZXRpY2EiLAogICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTEsCiAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gMC41LAogICAgICAgICAgICAgICAgICAgICAgICB2anVzdCA9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgIGZhY2UgPSAiYm9sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIiMyMjIyMjIiKSwKICAgIAogICAgIyMgdGl0bGUgLSBhbGwgdGl0bGUgZWxlbWVudHM6IHBsb3QsIGF4ZXMsIGxlZ2VuZHMgKGVsZW1lbnRfdGV4dCgpOyBpbmhlcml0cyBmcm9tIHRleHQpCiAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiSGVsdmV0aWNhIiwKICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxMSwKICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gMC41LAogICAgICAgICAgICAgICAgICAgICAgICAgdmp1c3QgPSAwLAogICAgICAgICAgICAgICAgICAgICAgICAgZmFjZSA9ICJib2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIiMyMjIyMjIiKSwKICAgICMjIG1hcmdpbgogICAgI21hcmdpbiA9IG1hcmdpbih0ID0gNS41LCByID0gNS41LCBiID0gNS41LCBsID0gNS41LCB1bml0ID0gInB0IiksICAgIAogICAgCiAgICAKICAgICMgYXhpcwogICAgCiAgICAjIyB0aXRsZSAtIGxhYmVscyBvZiBheGVzIChlbGVtZW50X3RleHQoKSkuIFNwZWNpZnkgYWxsIGF4ZXMnIGxhYmVscyAoYXhpcy50aXRsZSksIGxhYmVscyBieSBwbGFuZSAodXNpbmcgYXhpcy50aXRsZS54IG9yIGF4aXMudGl0bGUueSksIG9yIGluZGl2aWR1YWxseSBmb3IgZWFjaCBheGlzICh1c2luZyBheGlzLnRpdGxlLnguYm90dG9tLCBheGlzLnRpdGxlLngudG9wLCBheGlzLnRpdGxlLnkubGVmdCwgYXhpcy50aXRsZS55LnJpZ2h0KS4gYXhpcy50aXRsZS4qLiogaW5oZXJpdHMgZnJvbSBheGlzLnRpdGxlLiogd2hpY2ggaW5oZXJpdHMgZnJvbSBheGlzLnRpdGxlLCB3aGljaCBpbiB0dXJuIGluaGVyaXRzIGZyb20gdGV4dAogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiSGVsdmV0aWNhIiwgc2l6ZSA9IDExLCBoanVzdCA9IDAuNSwgdmp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsIGNvbG9yID0gIiMyMjIyMjIiKSwgCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gIkhlbHZldGljYSIsIHNpemUgPSAxMSwgaGp1c3QgPSAwLjUsIHZqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiLCBjb2xvciA9ICIjMjIyMjIyIiksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gIkhlbHZldGljYSIsIHNpemUgPSAxMSwgaGp1c3QgPSAwLjUsIHZqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiLCBjb2xvciA9ICIjMjIyMjIyIiksCiAgICBheGlzLnRpdGxlLngudG9wID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJIZWx2ZXRpY2EiLCBzaXplID0gMTEsIGhqdXN0ID0gMC41LCB2anVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiwgY29sb3IgPSAiIzIyMjIyMiIpLAogICAgYXhpcy50aXRsZS54LmJvdHRvbSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiSGVsdmV0aWNhIiwgc2l6ZSA9IDExLCBoanVzdCA9IDAuNSwgdmp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsIGNvbG9yID0gIiMyMjIyMjIiKSwKICAgIGF4aXMudGl0bGUueS5sZWZ0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJIZWx2ZXRpY2EiLCBzaXplID0gMTEsIGhqdXN0ID0gMC41LCB2anVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiwgY29sb3IgPSAiIzIyMjIyMiIpLAogICAgYXhpcy50aXRsZS55LnJpZ2h0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJIZWx2ZXRpY2EiLCBzaXplID0gMTEsIGhqdXN0ID0gMC41LCB2anVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiwgY29sb3IgPSAiIzIyMjIyMiIpLAogICAgCiAgICAjIyB0ZXh0IG9mIHRpY2sgbGFiZWxzIC0gdGljayBsYWJlbHMgYWxvbmcgYXhlcyAoZWxlbWVudF90ZXh0KCkpLiBTcGVjaWZ5IGFsbCBheGlzIHRpY2sgbGFiZWxzIChheGlzLnRleHQpLCB0aWNrIGxhYmVscyBieSBwbGFuZSAodXNpbmcgYXhpcy50ZXh0Lnggb3IgYXhpcy50ZXh0LnkpLCBvciBpbmRpdmlkdWFsbHkgZm9yIGVhY2ggYXhpcyAodXNpbmcgYXhpcy50ZXh0LnguYm90dG9tLCBheGlzLnRleHQueC50b3AsIGF4aXMudGV4dC55LmxlZnQsIGF4aXMudGV4dC55LnJpZ2h0KS4gYXhpcy50ZXh0LiouKiBpbmhlcml0cyBmcm9tIGF4aXMudGV4dC4qIHdoaWNoIGluaGVyaXRzIGZyb20gYXhpcy50ZXh0LCB3aGljaCBpbiB0dXJuIGluaGVyaXRzIGZyb20gdGV4dAogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJIZWx2ZXRpY2EiLCBzaXplID0gMTEsIGhqdXN0ID0gMC41LCB2anVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiwgY29sb3IgPSAiIzIyMjIyMiIpLCAKICAgIAogICAgIyMgdGlja3MgLSB0aWNrIG1hcmtzIGFsb25nIGF4ZXMgKGVsZW1lbnRfbGluZSgpKS4gU3BlY2lmeSBhbGwgdGljayBtYXJrcyAoYXhpcy50aWNrcyksIHRpY2tzIGJ5IHBsYW5lICh1c2luZyBheGlzLnRpY2tzLnggb3IgYXhpcy50aWNrcy55KSwgb3IgaW5kaXZpZHVhbGx5IGZvciBlYWNoIGF4aXMgKHVzaW5nIGF4aXMudGlja3MueC5ib3R0b20sIGF4aXMudGlja3MueC50b3AsIGF4aXMudGlja3MueS5sZWZ0LCBheGlzLnRpY2tzLnkucmlnaHQpLiBheGlzLnRpY2tzLiouKiBpbmhlcml0cyBmcm9tIGF4aXMudGlja3MuKiB3aGljaCBpbmhlcml0cyBmcm9tIGF4aXMudGlja3MsIHdoaWNoIGluIHR1cm4gaW5oZXJpdHMgZnJvbSBsaW5lCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUsIGxpbmV0eXBlID0gMSwgbGluZWVuZCA9ICJidXR0IiwgYXJyb3cgPSBGQUxTRSksCiAgICAKICAgICMjIHRpY2sgbGVuZ3RoIC0gbGVuZ3RoIG9mIHRpY2sgbWFya3MgKHVuaXQpCiAgICBheGlzLnRpY2tzLmxlbmd0aCA9IHVuaXQoMi43NSwgInB0IiksCiAgICAKICAgICMjIGF4aXMgbGluZXMgLSBsaW5lcyBhbG9uZyBheGVzIChlbGVtZW50X2xpbmUoKSkuIFNwZWNpZnkgbGluZXMgYWxvbmcgYWxsIGF4ZXMgKGF4aXMubGluZSksIGxpbmVzIGZvciBlYWNoIHBsYW5lICh1c2luZyBheGlzLmxpbmUueCBvciBheGlzLmxpbmUueSksIG9yIGluZGl2aWR1YWxseSBmb3IgZWFjaCBheGlzICh1c2luZyBheGlzLmxpbmUueC5ib3R0b20sIGF4aXMubGluZS54LnRvcCwgYXhpcy5saW5lLnkubGVmdCwgYXhpcy5saW5lLnkucmlnaHQpLiBheGlzLmxpbmUuKi4qIGluaGVyaXRzIGZyb20gYXhpcy5saW5lLiogd2hpY2ggaW5oZXJpdHMgZnJvbSBheGlzLmxpbmUsIHdoaWNoIGluIHR1cm4gaW5oZXJpdHMgZnJvbSBsaW5lCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksCiAgIAogICAgCiAgICAjIGxlZ2VuZAoKICAgICMjIGJhY2tncm91bmQgLSBiYWNrZ3JvdW5kIG9mIGxlZ2VuZCAoZWxlbWVudF9yZWN0KCk7IGluaGVyaXRzIGZyb20gcmVjdCkKICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAoKICAgICMjIG1hcmdpbiAtIHRoZSBtYXJnaW4gYXJvdW5kIGVhY2ggbGVnZW5kIChtYXJnaW4oKSkKICAgIGxlZ2VuZC5tYXJnaW4gPSBtYXJnaW4odCA9IDUuNSwgciA9IDUuNSwgYiA9IDUuNSwgbCA9IDUuNSwgdW5pdCA9ICJwdCIpLAoKICAgICMjIHNwYWNpbmcgLSB0aGUgc3BhY2luZyBiZXR3ZWVuIGxlZ2VuZHMgKHVuaXQpLiBsZWdlbmQuc3BhY2luZy54ICYgbGVnZW5kLnNwYWNpbmcueSBpbmhlcml0IGZyb20gbGVnZW5kLnNwYWNpbmcgb3IgY2FuIGJlIHNwZWNpZmllZCBzZXBhcmF0ZWx5CiAgICBsZWdlbmQuc3BhY2luZyA9IHVuaXQoMTEsICJwdCIpLAogICAgbGVnZW5kLnNwYWNpbmcueCA9IHVuaXQoMTEsICJwdCIpLAogICAgbGVnZW5kLnNwYWNpbmcueSA9IHVuaXQoMTEsICJwdCIpLAogICAgCiAgICAjIyBrZXkgLSBiYWNrZ3JvdW5kIHVuZGVybmVhdGggbGVnZW5kIGtleXMgKGVsZW1lbnRfcmVjdCgpOyBpbmhlcml0cyBmcm9tIHJlY3QpCiAgICBsZWdlbmQua2V5ID0gZWxlbWVudF9yZWN0KGZpbGwgPSBOVUxMLCBjb2xvdXIgPSBOVUxMLCBzaXplID0gMC41LCBsaW5ldHlwZSA9IDEpLAoKICAgICMjIGtleSBzaXplIC0gc2l6ZSBvZiBsZWdlbmQga2V5cyAodW5pdCk7IGtleSBiYWNrZ3JvdW5kIGhlaWdodCAmIHdpZHRoIGluaGVyaXQgZnJvbSBsZWdlbmQua2V5LnNpemUgb3IgY2FuIGJlIHNwZWNpZmllZCBzZXBhcmF0ZWx5CiAgICBsZWdlbmQua2V5LnNpemUgPSB1bml0KDEuMiwgInB0IiksCiAgICBsZWdlbmQua2V5LmhlaWdodCA9IHVuaXQoMS4yLCAicHQiKSwKICAgIGxlZ2VuZC5rZXkud2lkdGggPSB1bml0KDEuMiwgInB0IiksCiAgICAKICAgICMjIHRleHQgLSBsZWdlbmQgaXRlbSBsYWJlbHMgKGVsZW1lbnRfdGV4dCgpOyBpbmhlcml0cyBmcm9tIHRleHQpCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiSGVsdmV0aWNhIiwgc2l6ZSA9IDExLCBoanVzdCA9IDAuNSwgdmp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsIGNvbG9yID0gIiMyMjIyMjIiKSwgCgogICAgIyMgdGV4dCBhbGlnbm1lbnQgLSBhbGlnbm1lbnQgb2YgbGVnZW5kIGxhYmVscyAobnVtYmVyIGZyb20gMCAobGVmdCkgdG8gMSAocmlnaHQpKQogICAgbGVnZW5kLnRleHQuYWxpZ24gPSAwLCAKICAgIAogICAgIyMgdGl0bGUgLSB0aXRsZSBvZiBsZWdlbmQgKGVsZW1lbnRfdGV4dCgpOyBpbmhlcml0cyBmcm9tIHRpdGxlKQogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJIZWx2ZXRpY2EiLCBzaXplID0gMTEsIGhqdXN0ID0gMC41LCB2anVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiwgY29sb3IgPSAiIzIyMjIyMiIpLCAKCiAgICAjIyB0aXRsZSBhbGlnbm1lbnQgLSBhbGlnbm1lbnQgb2YgbGVnZW5kIHRpdGxlIChudW1iZXIgZnJvbSAwIChsZWZ0KSB0byAxIChyaWdodCkpCiAgICBsZWdlbmQudGl0bGUuYWxpZ24gPSAwLjUsCiAgICAKICAgICMjIHBvc2l0aW9uIC0gdGhlIHBvc2l0aW9uIG9mIGxlZ2VuZHMgKCJub25lIiwgImxlZnQiLCAicmlnaHQiLCAiYm90dG9tIiwgInRvcCIsIG9yIHR3by1lbGVtZW50IG51bWVyaWMgdmVjdG9yKQogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsICNjKDAuNSwwKQoKICAgICMjIGRpcmVjdGlvbiAtIGxheW91dCBvZiBpdGVtcyBpbiBsZWdlbmRzICgiaG9yaXpvbnRhbCIgb3IgInZlcnRpY2FsIikKICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsCiAgICAKICAgICMjIGp1c3RpZmljYXRpb24gLSBhbmNob3IgcG9pbnQgZm9yIHBvc2l0aW9uaW5nIGxlZ2VuZCBpbnNpZGUgcGxvdCAoImNlbnRlciIgb3IgdHdvLWVsZW1lbnQgbnVtZXJpYyB2ZWN0b3IpIG9yIHRoZSBqdXN0aWZpY2F0aW9uIGFjY29yZGluZyB0byB0aGUgcGxvdCBhcmVhIHdoZW4gcG9zaXRpb25lZCBvdXRzaWRlIHRoZSBwbG90CiAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJjZW50ZXIiLCAjIGMoMC41LDAuNSkKCiAgICAjIyBtdWx0aXBsZSBsZWdlbmRzIAogICAgIyMjIGFycm5hZ2VtZW50IC0gYXJyYW5nZW1lbnQgb2YgbXVsdGlwbGUgbGVnZW5kcyAoImhvcml6b250YWwiIG9yICJ2ZXJ0aWNhbCIpCiAgICBsZWdlbmQuYm94ID0gImhvcml6b250YWwiLAogICAgCiAgICAjIyMganVzdGlmaWNhdGlvbiAtIGp1c3RpZmljYXRpb24gb2YgZWFjaCBsZWdlbmQgd2l0aGluIHRoZSBvdmVyYWxsIGJvdW5kaW5nIGJveCwgd2hlbiB0aGVyZSBhcmUgbXVsdGlwbGUgbGVnZW5kcyAoInRvcCIsICJib3R0b20iLCAibGVmdCIsIG9yICJyaWdodCIpCiAgICBsZWdlbmQuYm94Lmp1c3QgPSAidG9wIiwKCiAgICAjIyMgbWFyZ2luIC0gbWFyZ2lucyBhcm91bmQgdGhlIGZ1bGwgbGVnZW5kIGFyZWEsIGFzIHNwZWNpZmllZCB1c2luZyBtYXJnaW4oKQogICAgbGVnZW5kLmJveC5tYXJnaW4gPSBtYXJnaW4odCA9IDUuNSwgciA9IDUuNSwgYiA9IDUuNSwgbCA9IDUuNSwgdW5pdCA9ICJwdCIpLAogICAgCiAgICAjIyMgYmFja2dyb3VuZCAtIGJhY2tncm91bmQgb2YgbGVnZW5kIGFyZWEgKGVsZW1lbnRfcmVjdCgpOyBpbmhlcml0cyBmcm9tIHJlY3QpCiAgICBsZWdlbmQuYm94LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAKICAgICMjIyBzcGFjaW5nIC0gVGhlIHNwYWNpbmcgYmV0d2VlbiB0aGUgcGxvdHRpbmcgYXJlYSBhbmQgdGhlIGxlZ2VuZCBib3ggKHVuaXQpCiAgICBsZWdlbmQuYm94LnNwYWNpbmcgPSB1bml0KDExLCAicHQiKSwKICAgIAogICAgCiAgICAjIHBhbmVsCiAgICAKICAgICMjIGJhY2tncm91bmQgLSAjIGJhY2tncm91bmQgb2YgcGxvdHRpbmcgYXJlYSwgZHJhd24gdW5kZXJuZWF0aCBwbG90IChlbGVtZW50X3JlY3QoKTsgaW5oZXJpdHMgZnJvbSByZWN0KQogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAKICAgICMjIGJvcmRlciAtIGJvcmRlciBhcm91bmQgcGxvdHRpbmcgYXJlYSwgZHJhd24gb24gdG9wIG9mIHBsb3Qgc28gdGhhdCBpdCBjb3ZlcnMgdGljayBtYXJrcyBhbmQgZ3JpZCBsaW5lcy4gVGhpcyBzaG91bGQgYmUgdXNlZCB3aXRoIGZpbGwgPSBOQSAoZWxlbWVudF9yZWN0KCk7IGluaGVyaXRzIGZyb20gcmVjdCkKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAKICAgICMjIHNwYWNpbmcgLSBzcGFjaW5nIGJldHdlZW4gZmFjZXQgcGFuZWxzICh1bml0KS4gcGFuZWwuc3BhY2luZy54ICYgcGFuZWwuc3BhY2luZy55IGluaGVyaXQgZnJvbSBwYW5lbC5zcGFjaW5nIG9yIGNhbiBiZSBzcGVjaWZpZWQgc2VwYXJhdGVseS4KICAgIHBhbmVsLnNwYWNpbmcgPSB1bml0KDUuNSwgInB0IiksCiAgICBwYW5lbC5zcGFjaW5nLnggPSB1bml0KDUuNSwgInB0IiksCiAgICBwYW5lbC5zcGFjaW5nLnkgPSB1bml0KDUuNSwgInB0IiksCiAgICAKICAgICMjIGdyaWQgLSBncmlkIGxpbmVzIChlbGVtZW50X2xpbmUoKSkuIFNwZWNpZnkgbWFqb3IgZ3JpZCBsaW5lcywgb3IgbWlub3IgZ3JpZCBsaW5lcyBzZXBhcmF0ZWx5ICh1c2luZyBwYW5lbC5ncmlkLm1ham9yIG9yIHBhbmVsLmdyaWQubWlub3IpIG9yIGluZGl2aWR1YWxseSBmb3IgZWFjaCBheGlzICh1c2luZyBwYW5lbC5ncmlkLm1ham9yLngsIHBhbmVsLmdyaWQubWlub3IueCwgcGFuZWwuZ3JpZC5tYWpvci55LCBwYW5lbC5ncmlkLm1pbm9yLnkpLiBZIGF4aXMgZ3JpZCBsaW5lcyBhcmUgaG9yaXpvbnRhbCBhbmQgeCBheGlzIGdyaWQgbGluZXMgYXJlIHZlcnRpY2FsLiBwYW5lbC5ncmlkLiouKiBpbmhlcml0cyBmcm9tIHBhbmVsLmdyaWQuKiB3aGljaCBpbmhlcml0cyBmcm9tIHBhbmVsLmdyaWQsIHdoaWNoIGluIHR1cm4gaW5oZXJpdHMgZnJvbSBsaW5lCiAgICBwYW5lbC5ncmlkICA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5taW5vci54ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5taW5vci55ID0gZWxlbWVudF9ibGFuaygpLAogICAgCiAgICAjIG9wdGlvbiB0byBwbGFjZSB0aGUgcGFuZWwgKGJhY2tncm91bmQsIGdyaWRsaW5lcykgb3ZlciB0aGUgZGF0YSBsYXllcnMgKGxvZ2ljYWwpLiBVc3VhbGx5IHVzZWQgd2l0aCBhIHRyYW5zcGFyZW50IG9yIGJsYW5rIHBhbmVsLmJhY2tncm91bmQuCiAgICBwYW5lbC5vbnRvcCA9IEZBTFNFLAogICAgCiAgICAKICAgICMgcGxvdAogICAgCiAgICAjIyBiYWNrZ3JvdW5kIC0gYmFja2dyb3VuZCBvZiB0aGUgZW50aXJlIHBsb3QgKGVsZW1lbnRfcmVjdCgpOyBpbmhlcml0cyBmcm9tIHJlY3QpCiAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCgogICAgIyMgdGl0bGUgLSBwbG90IHRpdGxlICh0ZXh0IGFwcGVhcmFuY2UpIChlbGVtZW50X3RleHQoKTsgaW5oZXJpdHMgZnJvbSB0aXRsZSkgbGVmdC1hbGlnbmVkIGJ5IGRlZmF1bHQKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gIkhlbHZldGljYSIsIHNpemUgPSAxNiwgaGp1c3QgPSAwLjUsIHZqdXN0ID0gMCwgZmFjZSA9ICJib2xkIiwgY29sb3IgPSAiIzIyMjIyMiIpLCAKICAgIAogICAgIyMgc3VidGl0bGUgLSBwbG90IHN1YnRpdGxlICh0ZXh0IGFwcGVhcmFuY2UpIChlbGVtZW50X3RleHQoKTsgaW5oZXJpdHMgZnJvbSB0aXRsZSkgbGVmdC1hbGlnbmVkIGJ5IGRlZmF1bHQKICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gIkhlbHZldGljYSIsIHNpemUgPSAxNCwgaGp1c3QgPSAwLjUsIHZqdXN0ID0gMCwgZmFjZSA9ICJib2xkIiwgY29sb3IgPSAiIzIyMjIyMiIpLCAKICAgIAogICAgIyMgY2FwdGlvbiAtIGNhcHRpb24gYmVsb3cgdGhlIHBsb3QgKHRleHQgYXBwZWFyYW5jZSkgKGVsZW1lbnRfdGV4dCgpOyBpbmhlcml0cyBmcm9tIHRpdGxlKSByaWdodC1hbGlnbmVkIGJ5IGRlZmF1bHQKICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiSGVsdmV0aWNhIiwgc2l6ZSA9IDgsIGhqdXN0ID0gMC41LCB2anVzdCA9IDAsIGZhY2UgPSAiYm9sZCIsIGNvbG9yID0gIiMyMjIyMjIiKSwKICAgIAogICAgIyMgdGFnIC0gdXBwZXItbGVmdCBsYWJlbCB0byBpZGVudGlmeSBhIHBsb3QgKHRleHQgYXBwZWFyYW5jZSkgKGVsZW1lbnRfdGV4dCgpOyBpbmhlcml0cyBmcm9tIHRpdGxlKSBsZWZ0LWFsaWduZWQgYnkgZGVmYXVsdAogICAgcGxvdC50YWcgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gIkhlbHZldGljYSIsIHNpemUgPSA4LCBoanVzdCA9IDAuNSwgdmp1c3QgPSAwLCBmYWNlID0gImJvbGQiLCBjb2xvciA9ICIjMjIyMjIyIiksCgogICAgIyMgdGFnIHBvc2l0aW9uIC0gVGhlIHBvc2l0aW9uIG9mIHRoZSB0YWcgYXMgYSBzdHJpbmcgKCJ0b3BsZWZ0IiwgInRvcCIsICJ0b3ByaWdodCIsICJsZWZ0IiwgInJpZ2h0IiwgImJvdHRvbWxlZnQiLCAiYm90dG9tIiwgImJvdHRvbXJpZ2h0KSBvciBhIGNvb3JkaW5hdGUuIElmIGEgc3RyaW5nLCBleHRyYSBzcGFjZSB3aWxsIGJlIGFkZGVkIHRvIGFjY29tbW9kYXRlIHRoZSB0YWcuCiAgICBwbG90LnRhZy5wb3NpdGlvbiA9ICJ0b3BsZWZ0IiwKICAgIAogICAgIyMgbWFyZ2luIC0gbWFyZ2luIGFyb3VuZCBlbnRpcmUgcGxvdCAodW5pdCB3aXRoIHRoZSBzaXplcyBvZiB0aGUgdG9wLCByaWdodCwgYm90dG9tLCBhbmQgbGVmdCBtYXJnaW5zKQogICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4odCA9IDUuNSwgciA9IDUuNSwgYiA9IDUuNSwgbCA9IDUuNSwgdW5pdCA9ICJwdCIpLAogICAKICAgIAogICAgIyBzdHJpcCAodXNlZCB3aGVuIGZhY2V0dGluZyBhIHBsb3QpCgogICAgIyMgYmFja2dyb3VuZCAtIGJhY2tncm91bmQgb2YgZmFjZXQgbGFiZWxzIChlbGVtZW50X3JlY3QoKTsgaW5oZXJpdHMgZnJvbSByZWN0KS4gSG9yaXpvbnRhbCBmYWNldCBiYWNrZ3JvdW5kIChzdHJpcC5iYWNrZ3JvdW5kLngpICYgdmVydGljYWwgZmFjZXQgYmFja2dyb3VuZCAoc3RyaXAuYmFja2dyb3VuZC55KSBpbmhlcml0IGZyb20gc3RyaXAuYmFja2dyb3VuZCBvciBjYW4gYmUgc3BlY2lmaWVkIHNlcGFyYXRlbHkKICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBzdHJpcC5iYWNrZ3JvdW5kLnggPSBlbGVtZW50X2JsYW5rKCksIAogICAgc3RyaXAuYmFja2dyb3VuZC55ID0gZWxlbWVudF9ibGFuaygpLAoKICAgICMgcGxhY2VtZW50IC0gcGxhY2VtZW50IG9mIHN0cmlwIHdpdGggcmVzcGVjdCB0byBheGVzLCBlaXRoZXIgImluc2lkZSIgb3IgIm91dHNpZGUiLiBPbmx5IGltcG9ydGFudCB3aGVuIGF4ZXMgYW5kIHN0cmlwcyBhcmUgb24gdGhlIHNhbWUgc2lkZSBvZiB0aGUgcGxvdC4KICAgIHN0cmlwLnBsYWNlbWVudCA9ICJpbnNpZGUiLAogICAgCiAgICAjIHRleHQgLSBmYWNldCBsYWJlbHMgKGVsZW1lbnRfdGV4dCgpOyBpbmhlcml0cyBmcm9tIHRleHQpLiBIb3Jpem9udGFsIGZhY2V0IGxhYmVscyAoc3RyaXAudGV4dC54KSAmIHZlcnRpY2FsIGZhY2V0IGxhYmVscyAoc3RyaXAudGV4dC55KSBpbmhlcml0IGZyb20gc3RyaXAudGV4dCBvciBjYW4gYmUgc3BlY2lmaWVkIHNlcGFyYXRlbHkKICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gIkhlbHZldGljYSIsIHNpemUgPSAxMSwgaGp1c3QgPSAwLjUsIHZqdXN0ID0gMC41LCBhbmdsZSA9IDAsIGZhY2UgPSAiYm9sZCIsIGNvbG9yID0gIiMyMjIyMjIiKSwKICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiSGVsdmV0aWNhIiwgc2l6ZSA9IDExLCBoanVzdCA9IDAuNSwgdmp1c3QgPSAwLjUsIGFuZ2xlID0gMCwgZmFjZSA9ICJib2xkIiwgY29sb3IgPSAiIzIyMjIyMiIpLAogICAgc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJIZWx2ZXRpY2EiLCBzaXplID0gMTEsIGhqdXN0ID0gMC41LCB2anVzdCA9IDAuNSwgYW5nbGUgPSAwLCBmYWNlID0gImJvbGQiLCBjb2xvciA9ICIjMjIyMjIyIiksCiAgICAKICAgICMgcGFkZGluZyAtIHNwYWNlIGJldHdlZW4gc3RyaXBzIGFuZCBheGVzIHdoZW4gc3RyaXBzIGFyZSBzd2l0Y2hlZCAodW5pdCkKICAgIHN0cmlwLnN3aXRjaC5wYWQuZ3JpZCA9IHVuaXQoMi43NSwgInB0IiksCiAgICAKICAgICMgcGFkZGluZyAtIHNwYWNlIGJldHdlZW4gc3RyaXBzIGFuZCBheGVzIHdoZW4gc3RyaXBzIGFyZSBzd2l0Y2hlZCAodW5pdCkKICAgIHN0cmlwLnN3aXRjaC5wYWQud3JhcCA9IHVuaXQoMi43NSwgInB0IikKICApCn0KYGBgCgpcICAKCkluIGFkZGl0aW9uLCBJIHVzZSBhIGN1c3RvbSBkaXNjcmV0ZSBhbmQgY29udGludW9zdSBjb2xvdXIgcGFsbGV0ZSB1c2luZyB0aGUgW2B3ZXNhbmRlcnNvbmBdKGh0dHBzOi8vZ2l0aHViLmNvbS9rYXJ0aGlrL3dlc2FuZGVyc29uKSBhbmQgW2B5YXJycmBdKGh0dHBzOi8vYm9va2Rvd24ub3JnL25kcGhpbGxpcHMvWWFScnIvanVtcGluLmh0bWwpIHBhY2thZ2VzLgoKYGBge3IgcGFsZXR0ZSwgZXZhbD1UUlVFLCBpbmNsdWRlPVRSVUUsIGVjaG89VFJVRSwgZXJyb3I9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGNhY2hlPVRSVUUsIGZpZy5hbGlnbj0nY2VudGVyJywgY29tbWVudD0iIn0KIyMjIyMgUGFja2FnZXMKbGlicmFyeSgid2VzYW5kZXJzb24iKQpsaWJyYXJ5KCJ5YXJyciIpCiMjIyMjIFBhbGV0dGVzCmQxIDwtIHdlc19wYWxldHRlKCJSb3lhbDEiLCB0eXBlID0gImRpc2NyZXRlIikKZDIgPC0gd2VzX3BhbGV0dGUoIkdyYW5kQnVkYXBlc3QyIiwgdHlwZSA9ICJkaXNjcmV0ZSIpCmQzIDwtIHdlc19wYWxldHRlKCJDYXZhbGNhbnRpMSIsIHR5cGUgPSAiZGlzY3JldGUiKQpkaXNjcmV0ZV93ZXNfcGFsIDwtIGMoZDEsIGQyLCBkMykKY29udGludW91c193ZXNfcGFsIDwtIHdlc19wYWxldHRlKCJaaXNzb3UxIiwgMTAwLCB0eXBlID0gImNvbnRpbnVvdXMiKQoKIyMjIyMgUGFsZXR0ZXMKZDEKZDIKZDMKY29udGludW91c193ZXNfcGFsCmBgYAoKXCAgCgojIEhpZGRlbiBjb2RlIHsjaGlkZGVuX2NvZGUgLnRhYnNldH0KCiMjIE15IHRoZW1lIHsjbXlfdGhlbWV9ICAKYGBge3IgcmVmLmxhYmVsPSJteV90aGVtZSIsIHJlc3VsdHM9J2hpZGUnLCBlY2hvID0gVFJVRX0KYGBgCgojIyBTZXNzaW9uIGluZm8KYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCgo=