Purpose

There are multiple “disease labels” in the pbta-histologies.tsv file, including (from most broad to most narrow) broad_histology, cancer_group, and harmonized_diagnosis. For context, it is helpful to note that an individual cancer_group will be nested under a single broad_histology and that cancer_group is a shorter form of harmonized_diagnosis with the following edits:

  • Other, Benign tumor and Dysplasia/Gliosis, Dysplasia/Gliosis-Glial-neuronal tumor NOS removed from cancer_group
  • Neurofibroma/Plexiform;Other updated to Neurofibroma/Plexiform
  • Non-germinomatous germ cell tumor;Teratoma updated to Teratoma
  • Anaplastic (malignant) meningioma, Meningothelial meningioma and Clear cell meningioma updated to Meningioma
  • Embryonal Tumor with Multilayered Rosettes updated to Embryonal tumor with multilayer rosettes

It is often useful to use color to indicate disease label in a plot where multiple groups are visualized when we can not rely particularly heavily on labels (e.g., scatter plots). Unfortunately, there are too many potential labels for us to generate an effective color palette (e.g., of sufficiently distinct colors). In addition, some groupings will contain very few samples.

The purpose of this notebook is to create color palettes for the following:

  • broad_histology values, where a broad_histology contains at least one cancer_group with n >= 10
  • cancer_group values with n >= 10

Background

You may find #1174 to be helpful context.

Usage

This notebook can be run via the command line from the top directory of the repository as follows:

Rscript -e "rmarkdown::render('figures/mapping-histology-labels.Rmd', 
                              clean = TRUE)"

Set Up

library(tidyverse)
── Attaching packages ────────────────────────────────── tidyverse 1.2.1 ──
✔ ggplot2 3.2.0     ✔ purrr   0.3.2
✔ tibble  2.1.3     ✔ dplyr   0.8.3
✔ tidyr   0.8.3     ✔ stringr 1.4.0
✔ readr   1.3.1     ✔ forcats 0.4.0
── Conflicts ───────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
library(RColorBrewer)

We should perform some minimal checking to make sure the provided release is at least at version 23.

minimum_allowed_release <- 23
release_version <- as.numeric(stringr::str_match(params$release, "-v(\\d+)-")[[2]])
if (release_version < minimum_allowed_release) {
  stop("This notebook should only be used with data release version 23 or above.")
}

Directories and Files

# Path to input directory
input_dir <- file.path("..", "data", params$release)
output_dir <- "palettes"

Read in metadata

Let’s read in the pbta-histologies.tsv file.

histologies_df <-
  readr::read_tsv(file.path(input_dir, "pbta-histologies.tsv"), guess_max = 10000)
Parsed with column specification:
cols(
  .default = col_character(),
  OS_days = col_double(),
  age_last_update_days = col_double(),
  normal_fraction = col_double(),
  tumor_fraction = col_double(),
  tumor_ploidy = col_double()
)
See spec(...) for full column specifications.

Identify values to include in palettes

We will use cancer_group with n >= 10 to guide what values to include in both our cancer_group and broad_histology palettes.

included_labels_df <- histologies_df %>% 
  # Exclude normal samples
  filter(sample_type == "Tumor") %>%
  # Filter to unique sample--"disease label" pairs
  select(sample_id, 
         broad_histology, 
         cancer_group) %>% 
  distinct() %>%
  # Count samples (e.g., sample_id)
  group_by(broad_histology, cancer_group) %>% 
  tally() %>%
  # Add a column called included which is a logical that can be used as 
  # a sample size filter & also to drop the NA values
  filter(n >= 10, 
         !is.na(cancer_group))

included_labels_df

Collapsing “Other” categories

In a later step, we will recode certain cancer groups, e.g., “High-grade glioma astrocytoma” will become “Other high-grade gliomas.” There are other cancer groups that would fit in that category despite not meeting the sample size threshold above. For now, this applies to LGG, HGG, and embryonal tumors.

other_cg_df <- histologies_df %>%
  filter(sample_type == "Tumor",  # Exclude normal samples
         !is.na(cancer_group),
         broad_histology %in% c("Diffuse astrocytic and oligodendroglial tumor",
                                "Low-grade astrocytic tumor",
                                "Embryonal tumor")) %>%
  select(broad_histology, 
         cancer_group) %>% 
  distinct() %>%
  # Drop everything that meets the sample size requirement above
  anti_join(included_labels_df)  
Joining, by = c("broad_histology", "cancer_group")
other_cg_df

We’ll create some vectors we’ll use in later steps to group different cancer groups under the same “Other” cancer group display category.

# To become "Other low-grade gliomas"
other_lgg <- c(
  "Low-grade glioma astrocytoma",
  other_cg_df %>%
    filter(broad_histology == "Low-grade astrocytic tumor") %>% 
    pull(cancer_group)
)

other_lgg
[1] "Low-grade glioma astrocytoma"   "Gliomatosis cerebri"           
[3] "Diffuse fibrillary astrocytoma" "Oligodendroglioma"             
Low-grade glioma astrocytoma

Gliomatosis cerebri

Diffuse fibrillary astrocytoma

Oligodendroglioma
# To become "Other high-grade gliomas"
other_hgg <- c(
  "High-grade glioma astrocytoma",
    other_cg_df %>%
    filter(broad_histology == "Diffuse astrocytic and oligodendroglial tumor") %>% 
    pull(cancer_group)
)

other_hgg
[1] "High-grade glioma astrocytoma" "Oligodendroglioma"            
High-grade glioma astrocytoma

Oligodendroglioma
# To become "Other embryonal tumors"
other_embryonal <- c(
  "CNS Embryonal tumor",  # Has n >= 10, but we're trying to limit # of colors
  other_cg_df %>%
  filter(broad_histology == "Embryonal tumor") %>%
  pull(cancer_group)
)

other_embryonal
[1] "CNS Embryonal tumor"                     
[2] "Embryonal tumor with multilayer rosettes"
[3] "Neuroblastoma"                           
[4] "Ganglioneuroblastoma"                    
[5] "CNS neuroblastoma"                       
CNS Embryonal tumor

Embryonal tumor with multilayer rosettes

Neuroblastoma

Ganglioneuroblastoma

CNS neuroblastoma

Create palettes

Outside of this notebook, we’ve done quite a bit of work to identify suitable palettes using http://phrogz.net/css/distinct-colors.html as a reference/starting point. Check out the discussion on #1174.

broad_histology

We will start by creating a palette for broad histology display values. Broad histology values will be recoded specifically for display in figures in later step.

broad_histology_df <- data.frame(
  broad_histology_display = c("Benign tumor",
                              "High-grade glioma",
                              "Embryonal tumor",
                              # Replace instances of "Ependymal tumor" per reviewer comment
                              # See: https://github.com/AlexsLemonade/OpenPBTA-analysis/issues/1652
                              "Ependymoma",  
                              "Germ cell tumor",
                              "Low-grade glioma",
                              "Meningioma",
                              "Mesenchymal non-meningothelial tumor",
                              "Neuronal and mixed neuronal-glial tumor",
                              "Tumor of cranial and paraspinal nerves",
                              "Tumor of sellar region"), 
  broad_histology_hex = c("#590024",
                          "#ff80e5",
                          "#220040",
                          "#2200ff",
                          "#0074d9",
                          "#8f8fbf",
                          "#2db398",
                          "#7fbf00",
                          "#685815",
                          "#ffaa00",
                          "#b2502d"),
  stringsAsFactors = FALSE
)

# value for "other" histologies
broad_histology_other_hex <- "#808080"

Now to create a legend with legend() (h/t this StackOverflow answer)

plot(NULL, xaxt = "n", yaxt = "n", bty = "n", ylab = "", xlab = "", 
     xlim = 0:1, ylim = 0:1)
legend("topleft", 
       legend = c(broad_histology_df$broad_histology_display, "Other"),
       col = c(broad_histology_df$broad_histology_hex, 
               broad_histology_other_hex),
       pch = 15, pt.cex = 2, cex = 1, bty = "n")
mtext("Broad Histology", at = 0.135, cex = 1.5)

cancer_group

There are 20 cancer_group values that we need to account for. These are best used in conjunction with labels in figures, but are intended to allow readers to “track” labels across figures.

Where there’s a 1:1 mapping between broad_histology and cancer_group, the hex codes will be the same.

cancer_group_df <- data.frame(
  cancer_group_display = c("Choroid plexus papilloma",
                           "Diffuse intrinsic pontine glioma",
                           "Diffuse midline glioma",
                           "Other high-grade glioma",
                           "Atypical Teratoid Rhabdoid Tumor",
                           "Medulloblastoma",
                           "Other embryonal tumor",
                           "Ependymoma",
                           "Teratoma",
                           "Ganglioglioma",
                           "Pilocytic astrocytoma",
                           "Pleomorphic xanthoastrocytoma",
                           "Subependymal Giant Cell Astrocytoma",
                           "Other low-grade glioma",
                           "Meningioma",
                           "Ewing sarcoma",
                           "Dysembryoplastic neuroepithelial tumor",
                           "Neurofibroma Plexiform",
                           "Schwannoma",
                           "Craniopharyngioma"),
  cancer_group_abbreviation = c("CPP",
                                "DIPG",
                                "DMG",
                                "Other HGG",
                                "ATRT",
                                "MB",
                                "Other ET",
                                "EPN",
                                "Teratoma",
                                "GNG",
                                "PA",
                                "PXA",
                                "SEGA",
                                "Other LGG",
                                "Meningioma",
                                "EWS",
                                "DNET",
                                "PNF",
                                "Schwannoma",
                                "CRANIO"),
  cancer_group_hex = c("#4d2635",
                       "#bf0099",
                       "#ff40d9",
                       "#ffccf5",
                       "#4d0d85",
                       "#a340ff",
                       "#b08ccf",
                       "#2200ff",
                       "#058aff",
                       "#c4c4ff",
                       "#8c8cff",
                       "#4e4ede",
                       "#2828a1",
                       "#000047",
                       "#2db398",
                       "#9fbf60",
                       "#614e01",
                       "#e6ac39",
                       "#ab7200",
                       "#b33000"),
  stringsAsFactors = FALSE
)
# Value for "other" groups
cancer_group_other_hex <- "#b5b5b5"

And again, we’ll create a legend with legend()

# Combine cancer groups with their abbreviations, WHEN an abbreviation exists for use in legend
legend_names <- cancer_group_df %>%
  mutate(cancer_group_legend = if_else(cancer_group_display == cancer_group_abbreviation,
                                       cancer_group_display, 
                                       as.character(glue::glue("{cancer_group_display} ({cancer_group_abbreviation})")))
  ) %>%
  pull(cancer_group_legend)

plot(NULL, xaxt = "n", yaxt = "n", bty = "n", ylab = "", xlab = "", 
     xlim = 0:1, ylim = 0:1)
legend("topleft", 
       legend = c(legend_names, "Other"),
       col = c(cancer_group_df$cancer_group_hex, cancer_group_other_hex),
       pch = 15, pt.cex = 1.5, cex = 0.75, bty = "n")
mtext("Cancer Group", at = 0.0625, cex = 1)

Output

We can create a data frame that contains both palettes with a series of left joins, where we will then fill the NA values with a single (gray) hex code per column (#808080 for broad_histology, #b5b5b5 for cancer_group.)

When multiple values are using the same color, it can be helpful to have a separate value for the legend, e.g., for all #808080 broad histologies, we may want to display Other. We’ll add a couple columns for legend-making convenience. We also are recoding some of the disease labels for display per #1403 and discussion on #1401.

palette_df <- histologies_df %>%
  # Exclude normal samples
  filter(sample_type == "Tumor") %>%
  # Filter to unique broad histology--cancer group pairs
  select(broad_histology, 
         cancer_group) %>% 
  distinct() %>%
  # For display specifically, recode broad histology label for LGG, HGG, and EPN
  mutate(broad_histology_display = case_when(
      broad_histology == "Low-grade astrocytic tumor" ~ "Low-grade glioma",
      broad_histology == "Diffuse astrocytic and oligodendroglial tumor" ~ "High-grade glioma", 
      broad_histology == "Tumors of sellar region" ~ "Tumor of sellar region",
      broad_histology == "Ependymal tumor" ~ "Ependymoma",  # https://github.com/AlexsLemonade/OpenPBTA-analysis/issues/1652
      TRUE ~ broad_histology
    ),
  # For display, group some cancer groups together into a specific "Other"
  # category
  cancer_group_display = case_when(
    # We must take into account broad histology for (at least) individual
    # Oligodendroglioma samples to get color coded correctly
      (broad_histology == "Diffuse astrocytic and oligodendroglial tumor") & (cancer_group %in% other_hgg) ~ "Other high-grade glioma",
      (broad_histology == "Low-grade astrocytic tumor") & (cancer_group %in% other_lgg) ~ "Other low-grade glioma",
      cancer_group %in% other_embryonal ~ "Other embryonal tumor",
      TRUE ~ cancer_group
    )
  ) %>%
  # Add broad histology palette
  left_join(broad_histology_df, by = "broad_histology_display") %>%
  # Add cancer group palette
  left_join(cancer_group_df, by = "cancer_group_display") %>%
  # Fill all other values with gray colors
  replace_na(list(broad_histology_hex = broad_histology_other_hex,
                  cancer_group_hex = cancer_group_other_hex)) %>%
  # The exception being - if cancer_group == NA, so should cancer_group_hex!
  mutate(cancer_group_hex = if_else(is.na(cancer_group), 
                                    NA_character_, 
                                    cancer_group_hex)) %>%
  # When a cancer group or broad histology display group does not get its
  # own hex code, we will group those samples into an "Other" category in 
  # figures
  mutate(cancer_group_display = if_else(cancer_group_hex == cancer_group_other_hex,
                                        "Other",
                                        cancer_group_display),
         broad_histology_display = if_else(broad_histology_hex == broad_histology_other_hex,
                                           "Other",
                                           broad_histology_display)) %>%
  # Sort by broad_histology for easy browsing
  arrange(broad_histology)

And now let’s take a look!

palette_df

Add broad_histology_order

Previously, we had a concept known as display_order where we ordered categories based on their number of samples (from large to small). Now that we’ve dropped display_group, let’s apply this same concept to broad_histology.

broad_histology_order_df <- histologies_df %>%  
  # Exclude normal samples
  filter(sample_type == "Tumor",
         # Only count histologies that we'll have a hex code for
         broad_histology %in% included_labels_df$broad_histology) %>%
  # Need to add broad_histology_display here because that's what we need to 
  # order/count
  inner_join(select(palette_df,
                    broad_histology,
                    broad_histology_display)) %>%
  # Filter to unique sample--broad_histology_display pairs
  select(sample_id, 
         broad_histology_display) %>%
  distinct() %>%
  # Count samples within a broad histology
  count(broad_histology_display) %>%
  # Add Other placeholder
  bind_rows(data.frame(broad_histology_display = "Other",
                       n = 0, 
                       stringsAsFactors = FALSE)) %>%
  # Reorder based on sample size except Benign tumor and Other should come last
  # And then add numeric column with the order
  mutate(broad_histology_display = forcats::fct_reorder(broad_histology_display,
                                                        n,
                                                        .desc = TRUE) %>%
           forcats::fct_relevel("Benign tumor",
                                "Other",
                                after = Inf),
         broad_histology_order = as.numeric(broad_histology_display)) %>%
  # No longer require the sample size
  select(-n) %>%
  # Sort for easy browsing
  arrange(broad_histology_display)
Joining, by = "broad_histology"
broad_histology_order_df

And now we’re ready to add this to the palette data frame.

palette_df <- palette_df %>%
  left_join(broad_histology_order_df,
            by = "broad_histology_display")
Warning: Column `broad_histology_display` joining character vector and
factor, coercing into character vector

Add oncoprint_group and oncoprint_main

For most plots that make use of the cancer_group palette, such as a box or violin plot, we will rely heavily on labels and therefore using the gray hex code for multiple groups will not be a problem.

We will have four panels of individual oncoprints, where many broad_histology values will get grouped together into the Other CNS panel which you can see here. We can move the information about which cancer groups will be included in each panel into our palette data frame.

# Taken from the current plot oncoprint script as of the writing of this
# See permalink above
other_cns_broad_histologies <- c(
  "Ependymal tumor",
  "Tumors of sellar region",
  "Neuronal and mixed neuronal-glial tumor",
  "Tumor of cranial and paraspinal nerves",
  "Meningioma",
  "Mesenchymal non-meningothelial tumor",
  "Germ cell tumor",
  "Choroid plexus tumor",
  "Histiocytic tumor",
  "Tumor of pineal region",
  "Metastatic tumors",
  "Other astrocytic tumor",
  "Lymphoma",
  "Melanocytic tumor",
  "Other tumor"
)

palette_df <- palette_df %>%
  mutate(oncoprint_group = case_when(
    broad_histology %in% other_cns_broad_histologies ~ "Other CNS",
    broad_histology %in% c(
      "Low-grade astrocytic tumor",
      "Embryonal tumor",
      "Diffuse astrocytic and oligodendroglial tumor"
    ) ~ broad_histology,
    TRUE ~ NA_character_
  ))

Now, we need to establish oncoprint_main, which is used to delineate whether “Other” cancer groups belong in the main text (TRUE value) or in the supplementary information (FALSE) oncoprint plots. Groups that belong in the supplement (oncoprint_main will be FALSE) should be “Other CNS” oncoprint groups that have an “Other” cancer_group_display value.

palette_df <- palette_df %>%
  mutate(
    oncoprint_main = case_when(
    oncoprint_group == "Other CNS" & cancer_group_display == "Other" ~ FALSE,
    oncoprint_group == "Other CNS" & cancer_group_display != "Other" ~ TRUE,
    TRUE ~ NA
    )
  )

Display final table and save to TSV

# For browsing
palette_df
readr::write_tsv(palette_df, 
                 file.path(output_dir, 
                           "broad_histology_cancer_group_palette.tsv"))

Session Info

sessionInfo()
R version 3.6.0 (2019-04-26)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Debian GNU/Linux 9 (stretch)

Matrix products: default
BLAS/LAPACK: /usr/lib/libopenblasp-r0.2.19.so

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=C             
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] RColorBrewer_1.1-2 forcats_0.4.0      stringr_1.4.0     
 [4] dplyr_0.8.3        purrr_0.3.2        readr_1.3.1       
 [7] tidyr_0.8.3        tibble_2.1.3       ggplot2_3.2.0     
[10] tidyverse_1.2.1   

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.1       cellranger_1.1.0 pillar_1.4.2     compiler_3.6.0  
 [5] base64enc_0.1-3  tools_3.6.0      digest_0.6.20    lubridate_1.7.4 
 [9] jsonlite_1.6     evaluate_0.14    nlme_3.1-140     gtable_0.3.0    
[13] lattice_0.20-38  pkgconfig_2.0.2  rlang_0.4.0      cli_1.1.0       
[17] rstudioapi_0.10  yaml_2.2.0       haven_2.1.1      xfun_0.8        
[21] withr_2.1.2      xml2_1.2.0       httr_1.4.0       knitr_1.23      
[25] generics_0.0.2   hms_0.4.2        grid_3.6.0       tidyselect_0.2.5
[29] glue_1.3.1       R6_2.4.0         readxl_1.3.1     rmarkdown_1.13  
[33] modelr_0.1.4     magrittr_1.5     ellipsis_0.2.0.1 backports_1.1.4 
[37] scales_1.0.0     htmltools_0.3.6  rvest_0.3.4      assertthat_0.2.1
[41] colorspace_1.4-1 stringi_1.4.3    lazyeval_0.2.2   munsell_0.5.0   
[45] broom_0.5.2      crayon_1.3.4    
LS0tCnRpdGxlOiAiQ3JlYXRlIGEgbWluaW1hbCBwYWxldHRlIGZvciBkaXNwbGF5aW5nIG11bHRpcGxlIGRpc2Vhc2UgbGFiZWxzIgpvdXRwdXQ6ICAgCiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQphdXRob3I6IENhbmRhY2UgU2F2b25lbiwgS3J1dGlrYSBHYW9ua2FyLCBKYWNseW4gVGFyb25pLCBhbmQgU3RlcGhhbmllIFNwaWVsbWFuCnBhcmFtczoKICByZWxlYXNlOiAicmVsZWFzZS12MjMtMjAyMzAxMTUiCmRhdGU6IDIwMjIKLS0tCgojIyBQdXJwb3NlCgpUaGVyZSBhcmUgbXVsdGlwbGUgImRpc2Vhc2UgbGFiZWxzIiBpbiB0aGUgYHBidGEtaGlzdG9sb2dpZXMudHN2YCBmaWxlLCBpbmNsdWRpbmcgKGZyb20gbW9zdCBicm9hZCB0byBtb3N0IG5hcnJvdykgYGJyb2FkX2hpc3RvbG9neWAsIGBjYW5jZXJfZ3JvdXBgLCBhbmQgYGhhcm1vbml6ZWRfZGlhZ25vc2lzYC4KRm9yIGNvbnRleHQsIGl0IGlzIGhlbHBmdWwgdG8gbm90ZSB0aGF0IGFuIGluZGl2aWR1YWwgYGNhbmNlcl9ncm91cGAgd2lsbCBiZSBuZXN0ZWQgdW5kZXIgYSBzaW5nbGUgYGJyb2FkX2hpc3RvbG9neWAgYW5kIHRoYXQgYGNhbmNlcl9ncm91cGAgaXMgYSBzaG9ydGVyIGZvcm0gb2YgYGhhcm1vbml6ZWRfZGlhZ25vc2lzYCB3aXRoIHRoZSBmb2xsb3dpbmcgZWRpdHM6CgotIE90aGVyLCBCZW5pZ24gdHVtb3IgYW5kIER5c3BsYXNpYS9HbGlvc2lzLCBEeXNwbGFzaWEvR2xpb3Npcy1HbGlhbC1uZXVyb25hbCB0dW1vciBOT1MgcmVtb3ZlZCBmcm9tIGBjYW5jZXJfZ3JvdXBgCi0gTmV1cm9maWJyb21hL1BsZXhpZm9ybTtPdGhlciB1cGRhdGVkIHRvIE5ldXJvZmlicm9tYS9QbGV4aWZvcm0KLSBOb24tZ2VybWlub21hdG91cyBnZXJtIGNlbGwgdHVtb3I7VGVyYXRvbWEgdXBkYXRlZCB0byBUZXJhdG9tYQotIEFuYXBsYXN0aWMgKG1hbGlnbmFudCkgbWVuaW5naW9tYSwgTWVuaW5nb3RoZWxpYWwgbWVuaW5naW9tYSBhbmQgQ2xlYXIgY2VsbCBtZW5pbmdpb21hIHVwZGF0ZWQgdG8gTWVuaW5naW9tYQotIEVtYnJ5b25hbCBUdW1vciB3aXRoIE11bHRpbGF5ZXJlZCBSb3NldHRlcyB1cGRhdGVkIHRvIEVtYnJ5b25hbCB0dW1vciB3aXRoIG11bHRpbGF5ZXIgcm9zZXR0ZXMKCkl0IGlzIG9mdGVuIHVzZWZ1bCB0byB1c2UgY29sb3IgdG8gaW5kaWNhdGUgZGlzZWFzZSBsYWJlbCBpbiBhIHBsb3Qgd2hlcmUgbXVsdGlwbGUgZ3JvdXBzIGFyZSB2aXN1YWxpemVkIHdoZW4gd2UgY2FuIG5vdCByZWx5IHBhcnRpY3VsYXJseSBoZWF2aWx5IG9uIGxhYmVscyAoZS5nLiwgc2NhdHRlciBwbG90cykuClVuZm9ydHVuYXRlbHksIHRoZXJlIGFyZSB0b28gbWFueSBwb3RlbnRpYWwgbGFiZWxzIGZvciB1cyB0byBnZW5lcmF0ZSBhbiBlZmZlY3RpdmUgY29sb3IgcGFsZXR0ZSAoZS5nLiwgb2Ygc3VmZmljaWVudGx5IGRpc3RpbmN0IGNvbG9ycykuCkluIGFkZGl0aW9uLCBzb21lIGdyb3VwaW5ncyB3aWxsIGNvbnRhaW4gdmVyeSBmZXcgc2FtcGxlcy4KClRoZSBwdXJwb3NlIG9mIHRoaXMgbm90ZWJvb2sgaXMgdG8gY3JlYXRlIGNvbG9yIHBhbGV0dGVzIGZvciB0aGUgZm9sbG93aW5nOgoKKiBgYnJvYWRfaGlzdG9sb2d5YCB2YWx1ZXMsIHdoZXJlIGEgYGJyb2FkX2hpc3RvbG9neWAgY29udGFpbnMgYXQgbGVhc3Qgb25lIGBjYW5jZXJfZ3JvdXBgIHdpdGggbiA+PSAxMAoqIGBjYW5jZXJfZ3JvdXBgIHZhbHVlcyB3aXRoIG4gPj0gMTAKCiMjIyBCYWNrZ3JvdW5kCgpZb3UgbWF5IGZpbmQgWyMxMTc0XShodHRwczovL2dpdGh1Yi5jb20vQWxleHNMZW1vbmFkZS9PcGVuUEJUQS1hbmFseXNpcy9pc3N1ZXMvMTE3NCkgdG8gYmUgaGVscGZ1bCBjb250ZXh0LgoKIyMgVXNhZ2UKClRoaXMgbm90ZWJvb2sgY2FuIGJlIHJ1biB2aWEgdGhlIGNvbW1hbmQgbGluZSBmcm9tIHRoZSB0b3AgZGlyZWN0b3J5IG9mIHRoZSByZXBvc2l0b3J5IGFzIGZvbGxvd3M6CgpgYGAKUnNjcmlwdCAtZSAicm1hcmtkb3duOjpyZW5kZXIoJ2ZpZ3VyZXMvbWFwcGluZy1oaXN0b2xvZ3ktbGFiZWxzLlJtZCcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGVhbiA9IFRSVUUpIgpgYGAKCiMjIFNldCBVcAoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKYGBgCgoKV2Ugc2hvdWxkIHBlcmZvcm0gc29tZSBtaW5pbWFsIGNoZWNraW5nIHRvIG1ha2Ugc3VyZSB0aGUgcHJvdmlkZWQgcmVsZWFzZSBpcyBfYXQgbGVhc3RfIGF0IHZlcnNpb24gMjMuCmBgYHtyfQptaW5pbXVtX2FsbG93ZWRfcmVsZWFzZSA8LSAyMwpyZWxlYXNlX3ZlcnNpb24gPC0gYXMubnVtZXJpYyhzdHJpbmdyOjpzdHJfbWF0Y2gocGFyYW1zJHJlbGVhc2UsICItdihcXGQrKS0iKVtbMl1dKQppZiAocmVsZWFzZV92ZXJzaW9uIDwgbWluaW11bV9hbGxvd2VkX3JlbGVhc2UpIHsKICBzdG9wKCJUaGlzIG5vdGVib29rIHNob3VsZCBvbmx5IGJlIHVzZWQgd2l0aCBkYXRhIHJlbGVhc2UgdmVyc2lvbiAyMyBvciBhYm92ZS4iKQp9CmBgYAoKIyMjIERpcmVjdG9yaWVzIGFuZCBGaWxlcwoKYGBge3J9CiMgUGF0aCB0byBpbnB1dCBkaXJlY3RvcnkKaW5wdXRfZGlyIDwtIGZpbGUucGF0aCgiLi4iLCAiZGF0YSIsIHBhcmFtcyRyZWxlYXNlKQpvdXRwdXRfZGlyIDwtICJwYWxldHRlcyIKYGBgCgojIyBSZWFkIGluIG1ldGFkYXRhIAoKTGV0J3MgcmVhZCBpbiB0aGUgYHBidGEtaGlzdG9sb2dpZXMudHN2YCBmaWxlLgoKYGBge3J9Cmhpc3RvbG9naWVzX2RmIDwtCiAgcmVhZHI6OnJlYWRfdHN2KGZpbGUucGF0aChpbnB1dF9kaXIsICJwYnRhLWhpc3RvbG9naWVzLnRzdiIpLCBndWVzc19tYXggPSAxMDAwMCkKYGBgCgojIyBJZGVudGlmeSB2YWx1ZXMgdG8gaW5jbHVkZSBpbiBwYWxldHRlcwoKV2Ugd2lsbCB1c2UgYGNhbmNlcl9ncm91cGAgd2l0aCBuID49IDEwIHRvIGd1aWRlIHdoYXQgdmFsdWVzIHRvIGluY2x1ZGUgaW4gYm90aCBvdXIgYGNhbmNlcl9ncm91cGAgYW5kIGBicm9hZF9oaXN0b2xvZ3lgIHBhbGV0dGVzLgoKYGBge3J9CmluY2x1ZGVkX2xhYmVsc19kZiA8LSBoaXN0b2xvZ2llc19kZiAlPiUgCiAgIyBFeGNsdWRlIG5vcm1hbCBzYW1wbGVzCiAgZmlsdGVyKHNhbXBsZV90eXBlID09ICJUdW1vciIpICU+JQogICMgRmlsdGVyIHRvIHVuaXF1ZSBzYW1wbGUtLSJkaXNlYXNlIGxhYmVsIiBwYWlycwogIHNlbGVjdChzYW1wbGVfaWQsIAogICAgICAgICBicm9hZF9oaXN0b2xvZ3ksIAogICAgICAgICBjYW5jZXJfZ3JvdXApICU+JSAKICBkaXN0aW5jdCgpICU+JQogICMgQ291bnQgc2FtcGxlcyAoZS5nLiwgc2FtcGxlX2lkKQogIGdyb3VwX2J5KGJyb2FkX2hpc3RvbG9neSwgY2FuY2VyX2dyb3VwKSAlPiUgCiAgdGFsbHkoKSAlPiUKICAjIEFkZCBhIGNvbHVtbiBjYWxsZWQgaW5jbHVkZWQgd2hpY2ggaXMgYSBsb2dpY2FsIHRoYXQgY2FuIGJlIHVzZWQgYXMgCiAgIyBhIHNhbXBsZSBzaXplIGZpbHRlciAmIGFsc28gdG8gZHJvcCB0aGUgTkEgdmFsdWVzCiAgZmlsdGVyKG4gPj0gMTAsIAogICAgICAgICAhaXMubmEoY2FuY2VyX2dyb3VwKSkKCmluY2x1ZGVkX2xhYmVsc19kZgpgYGAKCiMjIyBDb2xsYXBzaW5nICJPdGhlciIgY2F0ZWdvcmllcwoKSW4gYSBsYXRlciBzdGVwLCB3ZSB3aWxsIHJlY29kZSBjZXJ0YWluIGNhbmNlciBncm91cHMsIGUuZy4sICJIaWdoLWdyYWRlIGdsaW9tYSBhc3Ryb2N5dG9tYSIgd2lsbCBiZWNvbWUgIk90aGVyIGhpZ2gtZ3JhZGUgZ2xpb21hcy4iClRoZXJlIGFyZSBvdGhlciBjYW5jZXIgZ3JvdXBzIHRoYXQgd291bGQgZml0IGluIHRoYXQgY2F0ZWdvcnkgZGVzcGl0ZSBub3QgbWVldGluZyB0aGUgc2FtcGxlIHNpemUgdGhyZXNob2xkIGFib3ZlLgpGb3Igbm93LCB0aGlzIGFwcGxpZXMgdG8gTEdHLCBIR0csIGFuZCBlbWJyeW9uYWwgdHVtb3JzLgoKYGBge3J9Cm90aGVyX2NnX2RmIDwtIGhpc3RvbG9naWVzX2RmICU+JQogIGZpbHRlcihzYW1wbGVfdHlwZSA9PSAiVHVtb3IiLCAgIyBFeGNsdWRlIG5vcm1hbCBzYW1wbGVzCiAgICAgICAgICFpcy5uYShjYW5jZXJfZ3JvdXApLAogICAgICAgICBicm9hZF9oaXN0b2xvZ3kgJWluJSBjKCJEaWZmdXNlIGFzdHJvY3l0aWMgYW5kIG9saWdvZGVuZHJvZ2xpYWwgdHVtb3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMb3ctZ3JhZGUgYXN0cm9jeXRpYyB0dW1vciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkVtYnJ5b25hbCB0dW1vciIpKSAlPiUKICBzZWxlY3QoYnJvYWRfaGlzdG9sb2d5LCAKICAgICAgICAgY2FuY2VyX2dyb3VwKSAlPiUgCiAgZGlzdGluY3QoKSAlPiUKICAjIERyb3AgZXZlcnl0aGluZyB0aGF0IG1lZXRzIHRoZSBzYW1wbGUgc2l6ZSByZXF1aXJlbWVudCBhYm92ZQogIGFudGlfam9pbihpbmNsdWRlZF9sYWJlbHNfZGYpICAKCm90aGVyX2NnX2RmCmBgYAoKV2UnbGwgY3JlYXRlIHNvbWUgdmVjdG9ycyB3ZSdsbCB1c2UgaW4gbGF0ZXIgc3RlcHMgdG8gZ3JvdXAgZGlmZmVyZW50IGNhbmNlciBncm91cHMgdW5kZXIgdGhlIHNhbWUgIk90aGVyIiBjYW5jZXIgZ3JvdXAgZGlzcGxheSBjYXRlZ29yeS4KCmBgYHtyfQojIFRvIGJlY29tZSAiT3RoZXIgbG93LWdyYWRlIGdsaW9tYXMiCm90aGVyX2xnZyA8LSBjKAogICJMb3ctZ3JhZGUgZ2xpb21hIGFzdHJvY3l0b21hIiwKICBvdGhlcl9jZ19kZiAlPiUKICAgIGZpbHRlcihicm9hZF9oaXN0b2xvZ3kgPT0gIkxvdy1ncmFkZSBhc3Ryb2N5dGljIHR1bW9yIikgJT4lIAogICAgcHVsbChjYW5jZXJfZ3JvdXApCikKCm90aGVyX2xnZwpgYGAKCmBgYHtyfQojIFRvIGJlY29tZSAiT3RoZXIgaGlnaC1ncmFkZSBnbGlvbWFzIgpvdGhlcl9oZ2cgPC0gYygKICAiSGlnaC1ncmFkZSBnbGlvbWEgYXN0cm9jeXRvbWEiLAogICAgb3RoZXJfY2dfZGYgJT4lCiAgICBmaWx0ZXIoYnJvYWRfaGlzdG9sb2d5ID09ICJEaWZmdXNlIGFzdHJvY3l0aWMgYW5kIG9saWdvZGVuZHJvZ2xpYWwgdHVtb3IiKSAlPiUgCiAgICBwdWxsKGNhbmNlcl9ncm91cCkKKQoKb3RoZXJfaGdnCmBgYAoKYGBge3J9CiMgVG8gYmVjb21lICJPdGhlciBlbWJyeW9uYWwgdHVtb3JzIgpvdGhlcl9lbWJyeW9uYWwgPC0gYygKICAiQ05TIEVtYnJ5b25hbCB0dW1vciIsICAjIEhhcyBuID49IDEwLCBidXQgd2UncmUgdHJ5aW5nIHRvIGxpbWl0ICMgb2YgY29sb3JzCiAgb3RoZXJfY2dfZGYgJT4lCiAgZmlsdGVyKGJyb2FkX2hpc3RvbG9neSA9PSAiRW1icnlvbmFsIHR1bW9yIikgJT4lCiAgcHVsbChjYW5jZXJfZ3JvdXApCikKCm90aGVyX2VtYnJ5b25hbApgYGAKCiMjIENyZWF0ZSBwYWxldHRlcwoKT3V0c2lkZSBvZiB0aGlzIG5vdGVib29rLCB3ZSd2ZSBkb25lIHF1aXRlIGEgYml0IG9mIHdvcmsgdG8gaWRlbnRpZnkgc3VpdGFibGUgcGFsZXR0ZXMgdXNpbmcgaHR0cDovL3Bocm9nei5uZXQvY3NzL2Rpc3RpbmN0LWNvbG9ycy5odG1sIGFzIGEgcmVmZXJlbmNlL3N0YXJ0aW5nIHBvaW50LgpDaGVjayBvdXQgdGhlIGRpc2N1c3Npb24gb24gWyMxMTc0XShodHRwczovL2dpdGh1Yi5jb20vQWxleHNMZW1vbmFkZS9PcGVuUEJUQS1hbmFseXNpcy9pc3N1ZXMvMTE3NCkuCgojIyMgYGJyb2FkX2hpc3RvbG9neWAKCldlIHdpbGwgc3RhcnQgYnkgY3JlYXRpbmcgYSBwYWxldHRlIGZvciBicm9hZCBoaXN0b2xvZ3kgZGlzcGxheSB2YWx1ZXMuCkJyb2FkIGhpc3RvbG9neSB2YWx1ZXMgd2lsbCBiZSByZWNvZGVkIHNwZWNpZmljYWxseSBmb3IgZGlzcGxheSBpbiBmaWd1cmVzIGluIGxhdGVyIHN0ZXAuCgpgYGB7cn0KYnJvYWRfaGlzdG9sb2d5X2RmIDwtIGRhdGEuZnJhbWUoCiAgYnJvYWRfaGlzdG9sb2d5X2Rpc3BsYXkgPSBjKCJCZW5pZ24gdHVtb3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSGlnaC1ncmFkZSBnbGlvbWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRW1icnlvbmFsIHR1bW9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBSZXBsYWNlIGluc3RhbmNlcyBvZiAiRXBlbmR5bWFsIHR1bW9yIiBwZXIgcmV2aWV3ZXIgY29tbWVudAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFNlZTogaHR0cHM6Ly9naXRodWIuY29tL0FsZXhzTGVtb25hZGUvT3BlblBCVEEtYW5hbHlzaXMvaXNzdWVzLzE2NTIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkVwZW5keW1vbWEiLCAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHZXJtIGNlbGwgdHVtb3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTG93LWdyYWRlIGdsaW9tYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNZW5pbmdpb21hIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1lc2VuY2h5bWFsIG5vbi1tZW5pbmdvdGhlbGlhbCB0dW1vciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOZXVyb25hbCBhbmQgbWl4ZWQgbmV1cm9uYWwtZ2xpYWwgdHVtb3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVHVtb3Igb2YgY3JhbmlhbCBhbmQgcGFyYXNwaW5hbCBuZXJ2ZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVHVtb3Igb2Ygc2VsbGFyIHJlZ2lvbiIpLCAKICBicm9hZF9oaXN0b2xvZ3lfaGV4ID0gYygiIzU5MDAyNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIiNmZjgwZTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICIjMjIwMDQwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiIzIyMDBmZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIiMwMDc0ZDkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICIjOGY4ZmJmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiIzJkYjM5OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIiM3ZmJmMDAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICIjNjg1ODE1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiI2ZmYWEwMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIiNiMjUwMmQiKSwKICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKKQoKIyB2YWx1ZSBmb3IgIm90aGVyIiBoaXN0b2xvZ2llcwpicm9hZF9oaXN0b2xvZ3lfb3RoZXJfaGV4IDwtICIjODA4MDgwIgpgYGAKCk5vdyB0byBjcmVhdGUgYSBsZWdlbmQgd2l0aCBgbGVnZW5kKClgIChoL3QgW3RoaXMgU3RhY2tPdmVyZmxvdyBhbnN3ZXJdKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzQ4OTY2NjQ1L2hvdy1jYW4taS1jcmVhdGUtYS1sZWdlbmQtd2l0aG91dC1hLXBsb3QtaW4tci80ODk2NjkyNCkpCgpgYGB7cn0KcGxvdChOVUxMLCB4YXh0ID0gIm4iLCB5YXh0ID0gIm4iLCBidHkgPSAibiIsIHlsYWIgPSAiIiwgeGxhYiA9ICIiLCAKICAgICB4bGltID0gMDoxLCB5bGltID0gMDoxKQpsZWdlbmQoInRvcGxlZnQiLCAKICAgICAgIGxlZ2VuZCA9IGMoYnJvYWRfaGlzdG9sb2d5X2RmJGJyb2FkX2hpc3RvbG9neV9kaXNwbGF5LCAiT3RoZXIiKSwKICAgICAgIGNvbCA9IGMoYnJvYWRfaGlzdG9sb2d5X2RmJGJyb2FkX2hpc3RvbG9neV9oZXgsIAogICAgICAgICAgICAgICBicm9hZF9oaXN0b2xvZ3lfb3RoZXJfaGV4KSwKICAgICAgIHBjaCA9IDE1LCBwdC5jZXggPSAyLCBjZXggPSAxLCBidHkgPSAibiIpCm10ZXh0KCJCcm9hZCBIaXN0b2xvZ3kiLCBhdCA9IDAuMTM1LCBjZXggPSAxLjUpCmBgYAoKIyMjIGBjYW5jZXJfZ3JvdXBgCgpUaGVyZSBhcmUgMjAgYGNhbmNlcl9ncm91cGAgdmFsdWVzIHRoYXQgd2UgbmVlZCB0byBhY2NvdW50IGZvci4KVGhlc2UgYXJlIGJlc3QgdXNlZCBpbiBjb25qdW5jdGlvbiB3aXRoIF9sYWJlbHNfIGluIGZpZ3VyZXMsIGJ1dCBhcmUgaW50ZW5kZWQgdG8gYWxsb3cgcmVhZGVycyB0byAidHJhY2siIGxhYmVscyBfYWNyb3NzIGZpZ3VyZXNfLgoKV2hlcmUgdGhlcmUncyBhIDE6MSBtYXBwaW5nIGJldHdlZW4gYGJyb2FkX2hpc3RvbG9neWAgYW5kIGBjYW5jZXJfZ3JvdXBgLCB0aGUgaGV4IGNvZGVzIHdpbGwgYmUgdGhlIHNhbWUuCgpgYGB7cn0KY2FuY2VyX2dyb3VwX2RmIDwtIGRhdGEuZnJhbWUoCiAgY2FuY2VyX2dyb3VwX2Rpc3BsYXkgPSBjKCJDaG9yb2lkIHBsZXh1cyBwYXBpbGxvbWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiRGlmZnVzZSBpbnRyaW5zaWMgcG9udGluZSBnbGlvbWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiRGlmZnVzZSBtaWRsaW5lIGdsaW9tYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJPdGhlciBoaWdoLWdyYWRlIGdsaW9tYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJBdHlwaWNhbCBUZXJhdG9pZCBSaGFiZG9pZCBUdW1vciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJNZWR1bGxvYmxhc3RvbWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiT3RoZXIgZW1icnlvbmFsIHR1bW9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIkVwZW5keW1vbWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiVGVyYXRvbWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiR2FuZ2xpb2dsaW9tYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJQaWxvY3l0aWMgYXN0cm9jeXRvbWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiUGxlb21vcnBoaWMgeGFudGhvYXN0cm9jeXRvbWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiU3ViZXBlbmR5bWFsIEdpYW50IENlbGwgQXN0cm9jeXRvbWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiT3RoZXIgbG93LWdyYWRlIGdsaW9tYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJNZW5pbmdpb21hIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIkV3aW5nIHNhcmNvbWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiRHlzZW1icnlvcGxhc3RpYyBuZXVyb2VwaXRoZWxpYWwgdHVtb3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiTmV1cm9maWJyb21hIFBsZXhpZm9ybSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJTY2h3YW5ub21hIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNyYW5pb3BoYXJ5bmdpb21hIiksCiAgY2FuY2VyX2dyb3VwX2FiYnJldmlhdGlvbiA9IGMoIkNQUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRJUEciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJETUciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJPdGhlciBIR0ciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBVFJUIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTUIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJPdGhlciBFVCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkVQTiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRlcmF0b21hIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR05HIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUEEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQWEEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTRUdBIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiT3RoZXIgTEdHIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTWVuaW5naW9tYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkVXUyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRORVQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQTkYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTY2h3YW5ub21hIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ1JBTklPIiksCiAgY2FuY2VyX2dyb3VwX2hleCA9IGMoIiM0ZDI2MzUiLAogICAgICAgICAgICAgICAgICAgICAgICIjYmYwMDk5IiwKICAgICAgICAgICAgICAgICAgICAgICAiI2ZmNDBkOSIsCiAgICAgICAgICAgICAgICAgICAgICAgIiNmZmNjZjUiLAogICAgICAgICAgICAgICAgICAgICAgICIjNGQwZDg1IiwKICAgICAgICAgICAgICAgICAgICAgICAiI2EzNDBmZiIsCiAgICAgICAgICAgICAgICAgICAgICAgIiNiMDhjY2YiLAogICAgICAgICAgICAgICAgICAgICAgICIjMjIwMGZmIiwKICAgICAgICAgICAgICAgICAgICAgICAiIzA1OGFmZiIsCiAgICAgICAgICAgICAgICAgICAgICAgIiNjNGM0ZmYiLAogICAgICAgICAgICAgICAgICAgICAgICIjOGM4Y2ZmIiwKICAgICAgICAgICAgICAgICAgICAgICAiIzRlNGVkZSIsCiAgICAgICAgICAgICAgICAgICAgICAgIiMyODI4YTEiLAogICAgICAgICAgICAgICAgICAgICAgICIjMDAwMDQ3IiwKICAgICAgICAgICAgICAgICAgICAgICAiIzJkYjM5OCIsCiAgICAgICAgICAgICAgICAgICAgICAgIiM5ZmJmNjAiLAogICAgICAgICAgICAgICAgICAgICAgICIjNjE0ZTAxIiwKICAgICAgICAgICAgICAgICAgICAgICAiI2U2YWMzOSIsCiAgICAgICAgICAgICAgICAgICAgICAgIiNhYjcyMDAiLAogICAgICAgICAgICAgICAgICAgICAgICIjYjMzMDAwIiksCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCikKIyBWYWx1ZSBmb3IgIm90aGVyIiBncm91cHMKY2FuY2VyX2dyb3VwX290aGVyX2hleCA8LSAiI2I1YjViNSIKYGBgCgpBbmQgYWdhaW4sIHdlJ2xsIGNyZWF0ZSBhIGxlZ2VuZCB3aXRoIGBsZWdlbmQoKWAKCmBgYHtyfQojIENvbWJpbmUgY2FuY2VyIGdyb3VwcyB3aXRoIHRoZWlyIGFiYnJldmlhdGlvbnMsIFdIRU4gYW4gYWJicmV2aWF0aW9uIGV4aXN0cyBmb3IgdXNlIGluIGxlZ2VuZApsZWdlbmRfbmFtZXMgPC0gY2FuY2VyX2dyb3VwX2RmICU+JQogIG11dGF0ZShjYW5jZXJfZ3JvdXBfbGVnZW5kID0gaWZfZWxzZShjYW5jZXJfZ3JvdXBfZGlzcGxheSA9PSBjYW5jZXJfZ3JvdXBfYWJicmV2aWF0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYW5jZXJfZ3JvdXBfZGlzcGxheSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihnbHVlOjpnbHVlKCJ7Y2FuY2VyX2dyb3VwX2Rpc3BsYXl9ICh7Y2FuY2VyX2dyb3VwX2FiYnJldmlhdGlvbn0pIikpKQogICkgJT4lCiAgcHVsbChjYW5jZXJfZ3JvdXBfbGVnZW5kKQoKcGxvdChOVUxMLCB4YXh0ID0gIm4iLCB5YXh0ID0gIm4iLCBidHkgPSAibiIsIHlsYWIgPSAiIiwgeGxhYiA9ICIiLCAKICAgICB4bGltID0gMDoxLCB5bGltID0gMDoxKQpsZWdlbmQoInRvcGxlZnQiLCAKICAgICAgIGxlZ2VuZCA9IGMobGVnZW5kX25hbWVzLCAiT3RoZXIiKSwKICAgICAgIGNvbCA9IGMoY2FuY2VyX2dyb3VwX2RmJGNhbmNlcl9ncm91cF9oZXgsIGNhbmNlcl9ncm91cF9vdGhlcl9oZXgpLAogICAgICAgcGNoID0gMTUsIHB0LmNleCA9IDEuNSwgY2V4ID0gMC43NSwgYnR5ID0gIm4iKQptdGV4dCgiQ2FuY2VyIEdyb3VwIiwgYXQgPSAwLjA2MjUsIGNleCA9IDEpCmBgYAoKIyMjIE91dHB1dAoKV2UgY2FuIGNyZWF0ZSBhIGRhdGEgZnJhbWUgdGhhdCBjb250YWlucyBib3RoIHBhbGV0dGVzIHdpdGggYSBzZXJpZXMgb2YgbGVmdCBqb2lucywgd2hlcmUgd2Ugd2lsbCB0aGVuIGZpbGwgdGhlIE5BIHZhbHVlcyB3aXRoIGEgc2luZ2xlIChncmF5KSBoZXggY29kZSBwZXIgY29sdW1uIChgciBicm9hZF9oaXN0b2xvZ3lfb3RoZXJfaGV4YCBmb3IgYGJyb2FkX2hpc3RvbG9neWAsIGByIGNhbmNlcl9ncm91cF9vdGhlcl9oZXhgIGZvciBgY2FuY2VyX2dyb3VwYC4pCgpXaGVuIG11bHRpcGxlIHZhbHVlcyBhcmUgdXNpbmcgdGhlIHNhbWUgY29sb3IsIGl0IGNhbiBiZSBoZWxwZnVsIHRvIGhhdmUgYSBzZXBhcmF0ZSB2YWx1ZSBmb3IgdGhlIGxlZ2VuZCwgZS5nLiwgZm9yIGFsbCBgIzgwODA4MGAgYnJvYWQgaGlzdG9sb2dpZXMsIHdlIG1heSB3YW50IHRvIGRpc3BsYXkgYE90aGVyYC4gCldlJ2xsIGFkZCBhIGNvdXBsZSBjb2x1bW5zIGZvciBsZWdlbmQtbWFraW5nIGNvbnZlbmllbmNlLgpXZSBhbHNvIGFyZSByZWNvZGluZyBzb21lIG9mIHRoZSBkaXNlYXNlIGxhYmVscyBmb3IgZGlzcGxheSBwZXIgWyMxNDAzXShodHRwczovL2dpdGh1Yi5jb20vQWxleHNMZW1vbmFkZS9PcGVuUEJUQS1hbmFseXNpcy9pc3N1ZXMvMTQwMykgYW5kIGRpc2N1c3Npb24gb24gWyMxNDAxXShodHRwczovL2dpdGh1Yi5jb20vQWxleHNMZW1vbmFkZS9PcGVuUEJUQS1hbmFseXNpcy9wdWxsLzE0MDEpLgoKYGBge3J9CnBhbGV0dGVfZGYgPC0gaGlzdG9sb2dpZXNfZGYgJT4lCiAgIyBFeGNsdWRlIG5vcm1hbCBzYW1wbGVzCiAgZmlsdGVyKHNhbXBsZV90eXBlID09ICJUdW1vciIpICU+JQogICMgRmlsdGVyIHRvIHVuaXF1ZSBicm9hZCBoaXN0b2xvZ3ktLWNhbmNlciBncm91cCBwYWlycwogIHNlbGVjdChicm9hZF9oaXN0b2xvZ3ksIAogICAgICAgICBjYW5jZXJfZ3JvdXApICU+JSAKICBkaXN0aW5jdCgpICU+JQogICMgRm9yIGRpc3BsYXkgc3BlY2lmaWNhbGx5LCByZWNvZGUgYnJvYWQgaGlzdG9sb2d5IGxhYmVsIGZvciBMR0csIEhHRywgYW5kIEVQTgogIG11dGF0ZShicm9hZF9oaXN0b2xvZ3lfZGlzcGxheSA9IGNhc2Vfd2hlbigKICAgICAgYnJvYWRfaGlzdG9sb2d5ID09ICJMb3ctZ3JhZGUgYXN0cm9jeXRpYyB0dW1vciIgfiAiTG93LWdyYWRlIGdsaW9tYSIsCiAgICAgIGJyb2FkX2hpc3RvbG9neSA9PSAiRGlmZnVzZSBhc3Ryb2N5dGljIGFuZCBvbGlnb2RlbmRyb2dsaWFsIHR1bW9yIiB+ICJIaWdoLWdyYWRlIGdsaW9tYSIsIAogICAgICBicm9hZF9oaXN0b2xvZ3kgPT0gIlR1bW9ycyBvZiBzZWxsYXIgcmVnaW9uIiB+ICJUdW1vciBvZiBzZWxsYXIgcmVnaW9uIiwKICAgICAgYnJvYWRfaGlzdG9sb2d5ID09ICJFcGVuZHltYWwgdHVtb3IiIH4gIkVwZW5keW1vbWEiLCAgIyBodHRwczovL2dpdGh1Yi5jb20vQWxleHNMZW1vbmFkZS9PcGVuUEJUQS1hbmFseXNpcy9pc3N1ZXMvMTY1MgogICAgICBUUlVFIH4gYnJvYWRfaGlzdG9sb2d5CiAgICApLAogICMgRm9yIGRpc3BsYXksIGdyb3VwIHNvbWUgY2FuY2VyIGdyb3VwcyB0b2dldGhlciBpbnRvIGEgc3BlY2lmaWMgIk90aGVyIgogICMgY2F0ZWdvcnkKICBjYW5jZXJfZ3JvdXBfZGlzcGxheSA9IGNhc2Vfd2hlbigKICAgICMgV2UgbXVzdCB0YWtlIGludG8gYWNjb3VudCBicm9hZCBoaXN0b2xvZ3kgZm9yIChhdCBsZWFzdCkgaW5kaXZpZHVhbAogICAgIyBPbGlnb2RlbmRyb2dsaW9tYSBzYW1wbGVzIHRvIGdldCBjb2xvciBjb2RlZCBjb3JyZWN0bHkKICAgICAgKGJyb2FkX2hpc3RvbG9neSA9PSAiRGlmZnVzZSBhc3Ryb2N5dGljIGFuZCBvbGlnb2RlbmRyb2dsaWFsIHR1bW9yIikgJiAoY2FuY2VyX2dyb3VwICVpbiUgb3RoZXJfaGdnKSB+ICJPdGhlciBoaWdoLWdyYWRlIGdsaW9tYSIsCiAgICAgIChicm9hZF9oaXN0b2xvZ3kgPT0gIkxvdy1ncmFkZSBhc3Ryb2N5dGljIHR1bW9yIikgJiAoY2FuY2VyX2dyb3VwICVpbiUgb3RoZXJfbGdnKSB+ICJPdGhlciBsb3ctZ3JhZGUgZ2xpb21hIiwKICAgICAgY2FuY2VyX2dyb3VwICVpbiUgb3RoZXJfZW1icnlvbmFsIH4gIk90aGVyIGVtYnJ5b25hbCB0dW1vciIsCiAgICAgIFRSVUUgfiBjYW5jZXJfZ3JvdXAKICAgICkKICApICU+JQogICMgQWRkIGJyb2FkIGhpc3RvbG9neSBwYWxldHRlCiAgbGVmdF9qb2luKGJyb2FkX2hpc3RvbG9neV9kZiwgYnkgPSAiYnJvYWRfaGlzdG9sb2d5X2Rpc3BsYXkiKSAlPiUKICAjIEFkZCBjYW5jZXIgZ3JvdXAgcGFsZXR0ZQogIGxlZnRfam9pbihjYW5jZXJfZ3JvdXBfZGYsIGJ5ID0gImNhbmNlcl9ncm91cF9kaXNwbGF5IikgJT4lCiAgIyBGaWxsIGFsbCBvdGhlciB2YWx1ZXMgd2l0aCBncmF5IGNvbG9ycwogIHJlcGxhY2VfbmEobGlzdChicm9hZF9oaXN0b2xvZ3lfaGV4ID0gYnJvYWRfaGlzdG9sb2d5X290aGVyX2hleCwKICAgICAgICAgICAgICAgICAgY2FuY2VyX2dyb3VwX2hleCA9IGNhbmNlcl9ncm91cF9vdGhlcl9oZXgpKSAlPiUKICAjIFRoZSBleGNlcHRpb24gYmVpbmcgLSBpZiBjYW5jZXJfZ3JvdXAgPT0gTkEsIHNvIHNob3VsZCBjYW5jZXJfZ3JvdXBfaGV4IQogIG11dGF0ZShjYW5jZXJfZ3JvdXBfaGV4ID0gaWZfZWxzZShpcy5uYShjYW5jZXJfZ3JvdXApLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTkFfY2hhcmFjdGVyXywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbmNlcl9ncm91cF9oZXgpKSAlPiUKICAjIFdoZW4gYSBjYW5jZXIgZ3JvdXAgb3IgYnJvYWQgaGlzdG9sb2d5IGRpc3BsYXkgZ3JvdXAgZG9lcyBub3QgZ2V0IGl0cwogICMgb3duIGhleCBjb2RlLCB3ZSB3aWxsIGdyb3VwIHRob3NlIHNhbXBsZXMgaW50byBhbiAiT3RoZXIiIGNhdGVnb3J5IGluIAogICMgZmlndXJlcwogIG11dGF0ZShjYW5jZXJfZ3JvdXBfZGlzcGxheSA9IGlmX2Vsc2UoY2FuY2VyX2dyb3VwX2hleCA9PSBjYW5jZXJfZ3JvdXBfb3RoZXJfaGV4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk90aGVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbmNlcl9ncm91cF9kaXNwbGF5KSwKICAgICAgICAgYnJvYWRfaGlzdG9sb2d5X2Rpc3BsYXkgPSBpZl9lbHNlKGJyb2FkX2hpc3RvbG9neV9oZXggPT0gYnJvYWRfaGlzdG9sb2d5X290aGVyX2hleCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJPdGhlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicm9hZF9oaXN0b2xvZ3lfZGlzcGxheSkpICU+JQogICMgU29ydCBieSBicm9hZF9oaXN0b2xvZ3kgZm9yIGVhc3kgYnJvd3NpbmcKICBhcnJhbmdlKGJyb2FkX2hpc3RvbG9neSkKYGBgCgpBbmQgbm93IGxldCdzIHRha2UgYSBsb29rIQoKYGBge3J9CnBhbGV0dGVfZGYKYGBgCgojIyMgQWRkIGBicm9hZF9oaXN0b2xvZ3lfb3JkZXJgIAoKUHJldmlvdXNseSwgd2UgaGFkIGEgY29uY2VwdCBrbm93biBhcyBgZGlzcGxheV9vcmRlcmAgd2hlcmUgd2Ugb3JkZXJlZCBjYXRlZ29yaWVzIGJhc2VkIG9uIHRoZWlyIG51bWJlciBvZiBzYW1wbGVzIChmcm9tIGxhcmdlIHRvIHNtYWxsKS4KTm93IHRoYXQgd2UndmUgZHJvcHBlZCBgZGlzcGxheV9ncm91cGAsIGxldCdzIGFwcGx5IHRoaXMgc2FtZSBjb25jZXB0IHRvIGBicm9hZF9oaXN0b2xvZ3lgLgoKYGBge3J9CmJyb2FkX2hpc3RvbG9neV9vcmRlcl9kZiA8LSBoaXN0b2xvZ2llc19kZiAlPiUgIAogICMgRXhjbHVkZSBub3JtYWwgc2FtcGxlcwogIGZpbHRlcihzYW1wbGVfdHlwZSA9PSAiVHVtb3IiLAogICAgICAgICAjIE9ubHkgY291bnQgaGlzdG9sb2dpZXMgdGhhdCB3ZSdsbCBoYXZlIGEgaGV4IGNvZGUgZm9yCiAgICAgICAgIGJyb2FkX2hpc3RvbG9neSAlaW4lIGluY2x1ZGVkX2xhYmVsc19kZiRicm9hZF9oaXN0b2xvZ3kpICU+JQogICMgTmVlZCB0byBhZGQgYnJvYWRfaGlzdG9sb2d5X2Rpc3BsYXkgaGVyZSBiZWNhdXNlIHRoYXQncyB3aGF0IHdlIG5lZWQgdG8gCiAgIyBvcmRlci9jb3VudAogIGlubmVyX2pvaW4oc2VsZWN0KHBhbGV0dGVfZGYsCiAgICAgICAgICAgICAgICAgICAgYnJvYWRfaGlzdG9sb2d5LAogICAgICAgICAgICAgICAgICAgIGJyb2FkX2hpc3RvbG9neV9kaXNwbGF5KSkgJT4lCiAgIyBGaWx0ZXIgdG8gdW5pcXVlIHNhbXBsZS0tYnJvYWRfaGlzdG9sb2d5X2Rpc3BsYXkgcGFpcnMKICBzZWxlY3Qoc2FtcGxlX2lkLCAKICAgICAgICAgYnJvYWRfaGlzdG9sb2d5X2Rpc3BsYXkpICU+JQogIGRpc3RpbmN0KCkgJT4lCiAgIyBDb3VudCBzYW1wbGVzIHdpdGhpbiBhIGJyb2FkIGhpc3RvbG9neQogIGNvdW50KGJyb2FkX2hpc3RvbG9neV9kaXNwbGF5KSAlPiUKICAjIEFkZCBPdGhlciBwbGFjZWhvbGRlcgogIGJpbmRfcm93cyhkYXRhLmZyYW1lKGJyb2FkX2hpc3RvbG9neV9kaXNwbGF5ID0gIk90aGVyIiwKICAgICAgICAgICAgICAgICAgICAgICBuID0gMCwgCiAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSkgJT4lCiAgIyBSZW9yZGVyIGJhc2VkIG9uIHNhbXBsZSBzaXplIGV4Y2VwdCBCZW5pZ24gdHVtb3IgYW5kIE90aGVyIHNob3VsZCBjb21lIGxhc3QKICAjIEFuZCB0aGVuIGFkZCBudW1lcmljIGNvbHVtbiB3aXRoIHRoZSBvcmRlcgogIG11dGF0ZShicm9hZF9oaXN0b2xvZ3lfZGlzcGxheSA9IGZvcmNhdHM6OmZjdF9yZW9yZGVyKGJyb2FkX2hpc3RvbG9neV9kaXNwbGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLmRlc2MgPSBUUlVFKSAlPiUKICAgICAgICAgICBmb3JjYXRzOjpmY3RfcmVsZXZlbCgiQmVuaWduIHR1bW9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiT3RoZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFmdGVyID0gSW5mKSwKICAgICAgICAgYnJvYWRfaGlzdG9sb2d5X29yZGVyID0gYXMubnVtZXJpYyhicm9hZF9oaXN0b2xvZ3lfZGlzcGxheSkpICU+JQogICMgTm8gbG9uZ2VyIHJlcXVpcmUgdGhlIHNhbXBsZSBzaXplCiAgc2VsZWN0KC1uKSAlPiUKICAjIFNvcnQgZm9yIGVhc3kgYnJvd3NpbmcKICBhcnJhbmdlKGJyb2FkX2hpc3RvbG9neV9kaXNwbGF5KQoKYnJvYWRfaGlzdG9sb2d5X29yZGVyX2RmCmBgYAoKQW5kIG5vdyB3ZSdyZSByZWFkeSB0byBhZGQgdGhpcyB0byB0aGUgcGFsZXR0ZSBkYXRhIGZyYW1lLgoKYGBge3J9CnBhbGV0dGVfZGYgPC0gcGFsZXR0ZV9kZiAlPiUKICBsZWZ0X2pvaW4oYnJvYWRfaGlzdG9sb2d5X29yZGVyX2RmLAogICAgICAgICAgICBieSA9ICJicm9hZF9oaXN0b2xvZ3lfZGlzcGxheSIpCmBgYAoKIyMjIEFkZCBgb25jb3ByaW50X2dyb3VwYCBhbmQgYG9uY29wcmludF9tYWluYAoKRm9yIG1vc3QgcGxvdHMgdGhhdCBtYWtlIHVzZSBvZiB0aGUgYGNhbmNlcl9ncm91cGAgcGFsZXR0ZSwgc3VjaCBhcyBhIGJveCBvciB2aW9saW4gcGxvdCwgd2Ugd2lsbCByZWx5IGhlYXZpbHkgb24gbGFiZWxzIGFuZCB0aGVyZWZvcmUgdXNpbmcgdGhlIGdyYXkgaGV4IGNvZGUgZm9yIG11bHRpcGxlIGdyb3VwcyB3aWxsIG5vdCBiZSBhIHByb2JsZW0uCgpXZSB3aWxsIGhhdmUgZm91ciBwYW5lbHMgb2YgaW5kaXZpZHVhbCBvbmNvcHJpbnRzLCB3aGVyZSBtYW55IGBicm9hZF9oaXN0b2xvZ3lgIHZhbHVlcyB3aWxsIGdldCBncm91cGVkIHRvZ2V0aGVyIGludG8gdGhlIGBPdGhlciBDTlNgIHBhbmVsIHdoaWNoIHlvdSBjYW4gc2VlIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vQWxleHNMZW1vbmFkZS9PcGVuUEJUQS1hbmFseXNpcy9ibG9iL2QzMWM5MjdhMjc4MTNlYzBiODAzMmZiZTc2ODAwMmYzMTcyMzYzNmYvYW5hbHlzZXMvb25jb3ByaW50LWxhbmRzY2FwZS8wMi1wbG90LW9uY29wcmludC5SI0wxODEpLgpXZSBjYW4gbW92ZSB0aGUgaW5mb3JtYXRpb24gYWJvdXQgd2hpY2ggY2FuY2VyIGdyb3VwcyB3aWxsIGJlIGluY2x1ZGVkIGluIGVhY2ggcGFuZWwgaW50byBvdXIgcGFsZXR0ZSBkYXRhIGZyYW1lLgoKYGBge3J9CiMgVGFrZW4gZnJvbSB0aGUgY3VycmVudCBwbG90IG9uY29wcmludCBzY3JpcHQgYXMgb2YgdGhlIHdyaXRpbmcgb2YgdGhpcwojIFNlZSBwZXJtYWxpbmsgYWJvdmUKb3RoZXJfY25zX2Jyb2FkX2hpc3RvbG9naWVzIDwtIGMoCiAgIkVwZW5keW1hbCB0dW1vciIsCiAgIlR1bW9ycyBvZiBzZWxsYXIgcmVnaW9uIiwKICAiTmV1cm9uYWwgYW5kIG1peGVkIG5ldXJvbmFsLWdsaWFsIHR1bW9yIiwKICAiVHVtb3Igb2YgY3JhbmlhbCBhbmQgcGFyYXNwaW5hbCBuZXJ2ZXMiLAogICJNZW5pbmdpb21hIiwKICAiTWVzZW5jaHltYWwgbm9uLW1lbmluZ290aGVsaWFsIHR1bW9yIiwKICAiR2VybSBjZWxsIHR1bW9yIiwKICAiQ2hvcm9pZCBwbGV4dXMgdHVtb3IiLAogICJIaXN0aW9jeXRpYyB0dW1vciIsCiAgIlR1bW9yIG9mIHBpbmVhbCByZWdpb24iLAogICJNZXRhc3RhdGljIHR1bW9ycyIsCiAgIk90aGVyIGFzdHJvY3l0aWMgdHVtb3IiLAogICJMeW1waG9tYSIsCiAgIk1lbGFub2N5dGljIHR1bW9yIiwKICAiT3RoZXIgdHVtb3IiCikKCnBhbGV0dGVfZGYgPC0gcGFsZXR0ZV9kZiAlPiUKICBtdXRhdGUob25jb3ByaW50X2dyb3VwID0gY2FzZV93aGVuKAogICAgYnJvYWRfaGlzdG9sb2d5ICVpbiUgb3RoZXJfY25zX2Jyb2FkX2hpc3RvbG9naWVzIH4gIk90aGVyIENOUyIsCiAgICBicm9hZF9oaXN0b2xvZ3kgJWluJSBjKAogICAgICAiTG93LWdyYWRlIGFzdHJvY3l0aWMgdHVtb3IiLAogICAgICAiRW1icnlvbmFsIHR1bW9yIiwKICAgICAgIkRpZmZ1c2UgYXN0cm9jeXRpYyBhbmQgb2xpZ29kZW5kcm9nbGlhbCB0dW1vciIKICAgICkgfiBicm9hZF9oaXN0b2xvZ3ksCiAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXwogICkpCmBgYAoKCk5vdywgd2UgbmVlZCB0byBlc3RhYmxpc2ggYG9uY29wcmludF9tYWluYCwgd2hpY2ggaXMgdXNlZCB0byBkZWxpbmVhdGUgd2hldGhlciAiT3RoZXIiIGNhbmNlciBncm91cHMgYmVsb25nIGluIHRoZSBtYWluIHRleHQgKGBUUlVFYCB2YWx1ZSkgb3IgaW4gdGhlIHN1cHBsZW1lbnRhcnkgaW5mb3JtYXRpb24gKGBGQUxTRWApIG9uY29wcmludCBwbG90cy4KR3JvdXBzIHRoYXQgYmVsb25nIGluIHRoZSBzdXBwbGVtZW50IChgb25jb3ByaW50X21haW5gIHdpbGwgYmUgYEZBTFNFYCkgc2hvdWxkIGJlICJPdGhlciBDTlMiIG9uY29wcmludCBncm91cHMgdGhhdCBoYXZlIGFuICJPdGhlciIgYGNhbmNlcl9ncm91cF9kaXNwbGF5YCB2YWx1ZS4KCmBgYHtyfQpwYWxldHRlX2RmIDwtIHBhbGV0dGVfZGYgJT4lCiAgbXV0YXRlKAogICAgb25jb3ByaW50X21haW4gPSBjYXNlX3doZW4oCiAgICBvbmNvcHJpbnRfZ3JvdXAgPT0gIk90aGVyIENOUyIgJiBjYW5jZXJfZ3JvdXBfZGlzcGxheSA9PSAiT3RoZXIiIH4gRkFMU0UsCiAgICBvbmNvcHJpbnRfZ3JvdXAgPT0gIk90aGVyIENOUyIgJiBjYW5jZXJfZ3JvdXBfZGlzcGxheSAhPSAiT3RoZXIiIH4gVFJVRSwKICAgIFRSVUUgfiBOQQogICAgKQogICkKYGBgCgoKIyMjIERpc3BsYXkgZmluYWwgdGFibGUgYW5kIHNhdmUgdG8gVFNWIAoKYGBge3J9CiMgRm9yIGJyb3dzaW5nCnBhbGV0dGVfZGYKYGBgCgoKYGBge3J9CnJlYWRyOjp3cml0ZV90c3YocGFsZXR0ZV9kZiwgCiAgICAgICAgICAgICAgICAgZmlsZS5wYXRoKG91dHB1dF9kaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAiYnJvYWRfaGlzdG9sb2d5X2NhbmNlcl9ncm91cF9wYWxldHRlLnRzdiIpKQpgYGAKCiMjIFNlc3Npb24gSW5mbwoKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCg==