library(VariantAnnotation)
library(openxlsx)
library(tidyverse)
library(RColorBrewer)
library(ggrepel)
library(cowplot)
wd <- here::here()

extractFUNCOTATION <-
  function(vcf,
           fields = c(
             "Gencode_43_hugoSymbol",
             "Gencode_43_variantClassification",
             "Gencode_43_variantType"
           )) {
    annotation_colnames <-
      info(vcf@metadata$header)["FUNCOTATION", "Description"] %>%
      stringr::str_remove("Functional annotation from the Funcotator tool.  Funcotation fields are: ") %>%
      stringr::str_split(pattern = "\\|") %>%
      unlist()
    
    as.data.frame(info(vcf)[["FUNCOTATION"]]) %>%
      dplyr::select("FUNCOTATION" = value) %>%
      dplyr::mutate(
        FUNCOTATION = word(FUNCOTATION, 1, sep = "\\]"),
        FUNCOTATION = str_remove_all(FUNCOTATION, "[\\[\\]]")
      ) %>%
      tidyr::separate(FUNCOTATION, into = annotation_colnames,
                      sep = "\\|", fill = "right") %>%
      dplyr::select(all_of(fields))
  }

Load sample

vcf.list <- list()
vcf.filenames <- c("GATK" = "LL.annotated.vcf.gz", "PB" = "pb-LL.vcf")
for(i in seq_along(vcf.filenames)){
  vcf.dir <- file.path(wd, "VCF")
  vcf.filepath <- file.path(vcf.dir, vcf.filenames[[i]])
  
  vcf.list[[names(vcf.filenames)[[i]]]] <- readVcf(vcf.filepath, "hg38")
}
Warning: duplicate keys in header will be forced to unique rownames
  
vcf.list <- vcf.list[1]

Subsetting only mutations that passed the initial upstream quality filter and were not unmapped.

vcf.list.qc <-
  lapply(vcf.list, function(x)
    x[rowRanges(x)$FILTER == "PASS"])
vcf.list.qc <-
  lapply(vcf.list.qc, function(x)
    x[!(grepl(names(x), pattern = "chrUn_")),])

lapply(vcf.list.qc, dim)
$GATK
[1] 2605    2
vcf.list.anno <- lapply(vcf.list.qc, function(x)
  extractFUNCOTATION(
    x,
    fields = c(
      "Gencode_43_hugoSymbol",
      "Gencode_43_variantClassification",
      "Gencode_43_variantType"
    )
  ))

Construct a data frame with useful values for easier plotting:

snv.list <- list()
for(sample.name in names(vcf.list.qc)) {
  vcf <- vcf.list.qc[[sample.name]]
  df.anno <- vcf.list.anno[[sample.name]]
  snv.list[[sample.name]] <- as.data.frame(geno(vcf)[["AF"]]) %>%
    setNames(., c("AF_CTRL", "AF_PDLC")) %>%
    mutate_all( ~ unlist(.)) %>%
    cbind(., df.anno) %>%
    mutate(
      DP = info(vcf)[["DP"]],
      GERMQ = info(vcf)[["GERMQ"]],
      NALOD = info(vcf)[["NALOD"]] %>% unlist(),
      NLOD = info(vcf)[["NLOD"]] %>% unlist(),
      TLOD = info(vcf)[["TLOD"]] %>% unlist()
    ) %>%
    rownames_to_column("Variant") %>%
    mutate(Position_hg38 = word(Variant, 1, sep = "_"),
           .before = "Variant") %>%
    mutate(Variant = word(Variant, 2, sep = "_"))
}
snv.list
$GATK
NA

Visualizations

snv.df <- bind_rows(snv.list, .id = "sample")

ggplot(snv.df,
       aes(y = AF_PDLC,
           x = AF_CTRL)) +
  geom_jitter(aes(color = Gencode_43_variantClassification), size = 2) +
  ylim(0, 1) + xlim(0, 1) +
  theme_bw()

snv.df %>% 
  filter(Gencode_43_hugoSymbol == "TP53")

Variant origin filters

DP is an approximate read depth

GERMQ The phred-scaled posterior probability that the alternate allele(s) are NOT germline variants

NALOD (Normal Allele Log Odds) is negative log 10 odds of artifact in normal with same allele fraction as tumor. 5% chance is NALOD ~ 1.3

NLOD (Normal Log Odds) is log odds that the variant is not present in the normal sample

TLOD (Tumor Log Odds) is a log odds ratio that represents the likelihood that the observed tumor allele depth (coverage) is due to a real mutation.

filters = list(
  "DP" = 30,
  "GERMQ" = 90,
  "NALOD" = -log10(0.05),
  "NLOD" = 6,
  "TLOD" = 30,
  "AF_CTRL" = 0.025,
  "AF_TUMOR" = 0.1
)


snv.df %>% 
  ggplot(.,
       aes(y = AF_CTRL, x = DP)) +
  geom_point(aes(color = GERMQ),
             shape = 1, alpha = 0.75,
             size = 1.5, stroke = 1
             ) +
  scale_colour_steps2(
    high = "green",
    midpoint = 50,
    mid = "blue",
    low = "blue",
    breaks = filters[["GERMQ"]]
  ) +
  scale_x_continuous(trans = "log10") +
  scale_y_continuous(labels = scales::percent) +
  geom_hline(yintercept = filters[["AF_CTRL"]], linetype = "dashed") +
  geom_vline(xintercept = filters[["DP"]], linetype = "dashed") +
  labs(y = "CTRL AF, %")


snv.df %>% 
  dplyr::filter(
    GERMQ >= filters[["GERMQ"]],
    DP >= filters[["DP"]]
    ) %>% 
  ggplot(.,
       aes(x = TLOD,
           y = NLOD)) +
  geom_density_2d(color = "black") +
  geom_point(
    aes(color = NALOD,
        shape = GERMQ
        ), size = 2, stroke = 1) +
  scale_y_continuous(trans = "log10") +
  scale_x_continuous(trans = "log10") +
  scale_colour_steps2(
    high = "green",
    midpoint = filters[["NALOD"]] / 2,
    mid = "blue",
    low = "blue",
    breaks = filters[["NALOD"]]
  ) +
  scale_shape_binned(breaks = c(filters[["GERMQ"]]), solid = FALSE) +
  geom_hline(yintercept = filters[["NLOD"]], linetype = 2) +
  geom_vline(xintercept = filters[["TLOD"]], linetype = 2)

pull(snv.df, Gencode_43_variantClassification) %>% unique() %>%
  length() -> n.SNV.types.unfiltered

c(
  brewer.pal(n = 12, name = "Paired"),
  brewer.pal(n = n.SNV.types.unfiltered - 12, name = "Dark2")
) -> pal.colors

filter(snv.df, sample != "P069_PT_REL_BM") %>% 
  dplyr::filter(
    GERMQ >= filters[["GERMQ"]],
    DP >= filters[["DP"]]
    ) %>% 
  ggplot(.,
         aes(x = TLOD,
             y = NLOD)) +
  geom_hex(aes(fill = Gencode_43_variantClassification), alpha = 0.75) +
  scale_y_continuous(trans = "log10") +
  scale_x_continuous(trans = "log10") +
  geom_hline(yintercept = filters[["NLOD"]], linetype = 2) +
  geom_vline(xintercept = filters[["TLOD"]], linetype = 2) +
  scale_fill_manual(values = pal.colors)

Subsetting the VCF object

Constructing a filter vector for vcf_qc object:

colnames(snv.df)
 [1] "sample"                           "Position_hg38"                    "Variant"                          "AF_CTRL"                         
 [5] "AF_PDLC"                          "Gencode_43_hugoSymbol"            "Gencode_43_variantClassification" "Gencode_43_variantType"          
 [9] "DP"                               "GERMQ"                            "NALOD"                            "NLOD"                            
[13] "TLOD"                            
snv.filter.df <- snv.df %>%
  mutate(
    second_filter = case_when(
      DP <= filters[["DP"]] ~ "low_DP",
      GERMQ <= filters[["GERMQ"]] ~ "low_GERMQ",
      AF_CTRL > filters[["AF_CTRL"]] ~ "high_CTRL_AF",
      AF_PDLC < filters[["AF_TUMOR"]] ~ "low_Tumor_AF",
      NALOD < filters[["NALOD"]] ~   "low_NALOD",
      NLOD < filters[["NLOD"]] ~     "low_NLOD",
      TLOD < filters[["TLOD"]] ~     "low_TLOD",
      TRUE ~ "PASS"
    )
  )

allowed.positions <- snv.filter.df %>%
  filter(sample != "P069_PT_REL_BM" &
           second_filter == "PASS") %>% pull(Position_hg38)

snv.filter.df <- snv.filter.df %>%
  mutate(
    second_filter = case_when(
      sample == "P069_PT_REL_BM" &
        Position_hg38 %in% allowed.positions ~ "PASS",
      TRUE ~ second_filter
    )
  )

snv.filter.df %>% 
  filter(second_filter == "PASS")
snv.filter.df %>% 
  filter(Gencode_43_hugoSymbol == "TP53")
c(
  "low_DP" = "red",
  "low_GERMQ" = "orange",
  "low_NALOD" = "purple",
  "low_NLOD" = "violet",
  "low_TLOD" = "firebrick",
  "high_CTRL_AF" = "coral2",
  "low_Tumor_AF" = "blueviolet",
  "PASS" = "green"
) -> pal.colors

snv.filter.df %>%
  pivot_longer(cols = contains("AF"), values_to = "AF") %>%
  ggplot(aes(x = name, y = AF)) +
  ggbeeswarm::geom_quasirandom(aes(color = second_filter)) +
  geom_hline(yintercept = filters[["AF_CTRL"]], linetype = "dashed") +
  theme_bw() + labs(x = NULL) +
  scale_color_manual(values = pal.colors) +
  scale_y_continuous(labels = scales::percent)

snv.filter.df %>%
  filter(second_filter == "PASS") %>% 
  pivot_longer(cols = contains("AF"), values_to = "AF") %>%
  ggplot(aes(x = name, y = AF)) +
  ggbeeswarm::geom_quasirandom(aes(color = second_filter)) +
  geom_hline(yintercept = filters[["AF_CTRL"]], linetype = "dashed") +
  theme_bw() + labs(x = NULL) +
  scale_color_manual(values = pal.colors) +
  scale_y_continuous(labels = scales::percent)

Same plot but without the SNV which did not pass the filters.

Data frame of all filtered SNVs:

snv.filter.df.wide <- snv.filter.df %>%
  filter(second_filter == "PASS") %>% 
  dplyr::select(sample, Position_hg38, Variant, AF_PDLC,
         Gencode_43_hugoSymbol, Gencode_43_variantClassification) %>% 
  pivot_wider(names_from = "sample", values_from = "AF_PDLC") %>% 
  mutate(across(everything(), ~replace_na(., 0)))
snv.filter.df.wide

Data frame of missense filtered SNVs:

snv.filter.df %>%
  filter(second_filter == "PASS") %>% 
  filter(Gencode_43_variantClassification == "MISSENSE") %>% 
  dplyr::select(sample, Position_hg38, Variant, AF_PDLC,
         Gencode_43_hugoSymbol) %>% 
  pivot_wider(names_from = "sample", values_from = "AF_PDLC")
dot_position = position_jitter(height = 0,
                               width = 0.25,
                               seed = 413)

snv.filter.df %>%
  filter(second_filter == "PASS") %>% 
  
  ggplot() +
  lemon::geom_pointline(
    aes(
      x = sample,
      y = AF_PDLC,
      group = Position_hg38,
      fill = Gencode_43_variantClassification
    ),
    linecolor = "gray30",
    linetype = 3,
    position = dot_position,
    shape = 21,
    size = 2
  ) +
  geom_point(
    aes(
      x = sample,
      y = AF_PDLC,
      group = Position_hg38,
      fill = Gencode_43_variantClassification
    ),
    position = dot_position,
    shape = 21,
    size = 2
  ) +
  geom_hline(yintercept = 0.05, linetype = "dashed") +
  scale_y_continuous(labels = scales::percent, limits = c(0, 1)) +
  theme_bw() +
  labs(x = NULL, y = "Allelic frequency",
       fill = "SNV type")

dot_position = position_jitter(height = 0,
                               width = 0.25,
                               seed = 413)

snv.filter.df %>%
  filter(second_filter == "PASS") %>%
  filter(Gencode_43_variantClassification == "MISSENSE") %>%
  ggplot() +
  lemon::geom_pointline(
    aes(x = sample,
        y = AF_PDLC,
        group = Position_hg38),
    linecolor = "gray30",
    linetype = 3,
    position = dot_position,
    shape = 21,
    size = 2,
    fill = "gray90"
  ) +
  
  geom_hline(yintercept = 0.05, linetype = "dashed") +
  scale_y_continuous(labels = scales::percent, limits = c(0, 1)) +
  theme_bw() +
  labs(x = NULL, y = "Allelic frequency",
       fill = "SNV type")

TODO: add the second sample after fixing the filters.

Save the variant table

output_file_xlsx <- paste0("FilteredSNV.xlsx")
output_path_xlsx <- file.path(output_dir, output_file_xlsx)

list(
  "Total SNV" = snv_filter.df,
  "Filtered SNV" = snv_filter.df %>% 
    filter(second_filter == "PASS") %>% 
    dplyr::select(-second_filter)
) %>% 
  write.xlsx(., output_path_xlsx, overwrite = T)
sessionInfo()
R version 4.3.1 (2023-06-16)
Platform: x86_64-conda-linux-gnu (64-bit)
Running under: Ubuntu 22.04.3 LTS

Matrix products: default
BLAS/LAPACK: /home/bench-user/.apps/conda/lib/libopenblasp-r0.3.25.so;  LAPACK version 3.11.0

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

time zone: Etc/UTC
tzcode source: system (glibc)

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

other attached packages:
 [1] ggrepel_0.9.4               VariantAnnotation_1.48.1    Rsamtools_2.18.0            Biostrings_2.70.1           XVector_0.42.0             
 [6] SummarizedExperiment_1.32.0 Biobase_2.62.0              GenomicRanges_1.54.1        GenomeInfoDb_1.38.1         IRanges_2.36.0             
[11] S4Vectors_0.40.2            MatrixGenerics_1.14.0       matrixStats_1.2.0           BiocGenerics_0.48.1         cowplot_1.1.2              
[16] RColorBrewer_1.1-3          openxlsx_4.2.5.2            lubridate_1.9.3             forcats_1.0.0               stringr_1.5.1              
[21] dplyr_1.1.4                 purrr_1.0.2                 readr_2.1.4                 tidyr_1.3.0                 tibble_3.2.1               
[26] ggplot2_3.4.4               tidyverse_2.0.0            

loaded via a namespace (and not attached):
 [1] DBI_1.2.0                bitops_1.0-7             gridExtra_2.3            biomaRt_2.58.0           rlang_1.1.2              magrittr_2.0.3          
 [7] compiler_4.3.1           RSQLite_2.3.4            GenomicFeatures_1.54.1   png_0.1-8                vctrs_0.6.5              pkgconfig_2.0.3         
[13] crayon_1.5.2             fastmap_1.1.1            dbplyr_2.4.0             lemon_0.4.7              labeling_0.4.3           utf8_1.2.4              
[19] rmarkdown_2.25           tzdb_0.4.0               ggbeeswarm_0.7.2         bit_4.0.5                xfun_0.41                zlibbioc_1.48.0         
[25] cachem_1.0.8             progress_1.2.3           blob_1.2.4               DelayedArray_0.28.0      BiocParallel_1.36.0      parallel_4.3.1          
[31] prettyunits_1.2.0        R6_2.5.1                 stringi_1.8.3            rtracklayer_1.62.0       Rcpp_1.0.11              knitr_1.45              
[37] Matrix_1.6-4             timechange_0.2.0         tidyselect_1.2.0         rstudioapi_0.15.0        abind_1.4-5              yaml_2.3.8              
[43] codetools_0.2-19         curl_5.1.0               lattice_0.22-5           plyr_1.8.9               withr_2.5.2              KEGGREST_1.42.0         
[49] evaluate_0.23            isoband_0.2.7            BiocFileCache_2.10.1     xml2_1.3.6               zip_2.3.0                filelock_1.0.3          
[55] pillar_1.9.0             BiocManager_1.30.22      generics_0.1.3           rprojroot_2.0.4          RCurl_1.98-1.13          hms_1.1.3               
[61] munsell_0.5.0            scales_1.3.0             glue_1.6.2               tools_4.3.1              hexbin_1.28.3            BiocIO_1.12.0           
[67] BSgenome_1.70.1          GenomicAlignments_1.38.0 XML_3.99-0.16            grid_4.3.1               AnnotationDbi_1.64.1     colorspace_2.1-0        
[73] GenomeInfoDbData_1.2.11  beeswarm_0.4.0           restfulr_0.0.15          vipor_0.4.7              cli_3.6.2                rappdirs_0.3.3          
[79] fansi_1.0.6              S4Arrays_1.2.0           gtable_0.3.4             digest_0.6.33            SparseArray_1.2.2        farver_2.1.1            
[85] rjson_0.2.21             memoise_2.0.1            htmltools_0.5.7          lifecycle_1.0.4          httr_1.4.7               here_1.0.1              
[91] MASS_7.3-60              bit64_4.0.5             
LS0tCnRpdGxlOiAiUUMgYW5kIGZpbHRlcmluZyBvZiBhbm5vdGF0ZWQgVkNGIGZpbGVzIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0b2NfZGVwdGg6IDMKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgdGhlbWU6IHVuaXRlZAotLS0KCmBgYHtyIHNldHVwfQpsaWJyYXJ5KFZhcmlhbnRBbm5vdGF0aW9uKQpsaWJyYXJ5KG9wZW54bHN4KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShjb3dwbG90KQp3ZCA8LSBoZXJlOjpoZXJlKCkKCmV4dHJhY3RGVU5DT1RBVElPTiA8LQogIGZ1bmN0aW9uKHZjZiwKICAgICAgICAgICBmaWVsZHMgPSBjKAogICAgICAgICAgICAgIkdlbmNvZGVfNDNfaHVnb1N5bWJvbCIsCiAgICAgICAgICAgICAiR2VuY29kZV80M192YXJpYW50Q2xhc3NpZmljYXRpb24iLAogICAgICAgICAgICAgIkdlbmNvZGVfNDNfdmFyaWFudFR5cGUiCiAgICAgICAgICAgKSkgewogICAgYW5ub3RhdGlvbl9jb2xuYW1lcyA8LQogICAgICBpbmZvKHZjZkBtZXRhZGF0YSRoZWFkZXIpWyJGVU5DT1RBVElPTiIsICJEZXNjcmlwdGlvbiJdICU+JQogICAgICBzdHJpbmdyOjpzdHJfcmVtb3ZlKCJGdW5jdGlvbmFsIGFubm90YXRpb24gZnJvbSB0aGUgRnVuY290YXRvciB0b29sLiAgRnVuY290YXRpb24gZmllbGRzIGFyZTogIikgJT4lCiAgICAgIHN0cmluZ3I6OnN0cl9zcGxpdChwYXR0ZXJuID0gIlxcfCIpICU+JQogICAgICB1bmxpc3QoKQogICAgCiAgICBhcy5kYXRhLmZyYW1lKGluZm8odmNmKVtbIkZVTkNPVEFUSU9OIl1dKSAlPiUKICAgICAgZHBseXI6OnNlbGVjdCgiRlVOQ09UQVRJT04iID0gdmFsdWUpICU+JQogICAgICBkcGx5cjo6bXV0YXRlKAogICAgICAgIEZVTkNPVEFUSU9OID0gd29yZChGVU5DT1RBVElPTiwgMSwgc2VwID0gIlxcXSIpLAogICAgICAgIEZVTkNPVEFUSU9OID0gc3RyX3JlbW92ZV9hbGwoRlVOQ09UQVRJT04sICJbXFxbXFxdXSIpCiAgICAgICkgJT4lCiAgICAgIHRpZHlyOjpzZXBhcmF0ZShGVU5DT1RBVElPTiwgaW50byA9IGFubm90YXRpb25fY29sbmFtZXMsCiAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiXFx8IiwgZmlsbCA9ICJyaWdodCIpICU+JQogICAgICBkcGx5cjo6c2VsZWN0KGFsbF9vZihmaWVsZHMpKQogIH0KYGBgCgojIExvYWQgc2FtcGxlCgpgYGB7cn0KdmNmLmxpc3QgPC0gbGlzdCgpCnZjZi5maWxlbmFtZXMgPC0gYygiR0FUSyIgPSAiTEwuYW5ub3RhdGVkLnZjZi5neiIsICJQQiIgPSAicGItTEwudmNmIikKZm9yKGkgaW4gc2VxX2Fsb25nKHZjZi5maWxlbmFtZXMpKXsKICB2Y2YuZGlyIDwtIGZpbGUucGF0aCh3ZCwgIlZDRiIpCiAgdmNmLmZpbGVwYXRoIDwtIGZpbGUucGF0aCh2Y2YuZGlyLCB2Y2YuZmlsZW5hbWVzW1tpXV0pCiAgCiAgdmNmLmxpc3RbW25hbWVzKHZjZi5maWxlbmFtZXMpW1tpXV1dXSA8LSByZWFkVmNmKHZjZi5maWxlcGF0aCwgImhnMzgiKQp9CiAgCnZjZi5saXN0IDwtIHZjZi5saXN0WzFdCmBgYAoKU3Vic2V0dGluZyBvbmx5IG11dGF0aW9ucyB0aGF0IHBhc3NlZCB0aGUgaW5pdGlhbCB1cHN0cmVhbSBxdWFsaXR5IGZpbHRlciBhbmQgd2VyZSBub3QgdW5tYXBwZWQuCgpgYGB7cn0KdmNmLmxpc3QucWMgPC0KICBsYXBwbHkodmNmLmxpc3QsIGZ1bmN0aW9uKHgpCiAgICB4W3Jvd1Jhbmdlcyh4KSRGSUxURVIgPT0gIlBBU1MiXSkKdmNmLmxpc3QucWMgPC0KICBsYXBwbHkodmNmLmxpc3QucWMsIGZ1bmN0aW9uKHgpCiAgICB4WyEoZ3JlcGwobmFtZXMoeCksIHBhdHRlcm4gPSAiY2hyVW5fIikpLF0pCgpsYXBwbHkodmNmLmxpc3QucWMsIGRpbSkKYGBgCgpgYGB7cn0KdmNmLmxpc3QuYW5ubyA8LSBsYXBwbHkodmNmLmxpc3QucWMsIGZ1bmN0aW9uKHgpCiAgZXh0cmFjdEZVTkNPVEFUSU9OKAogICAgeCwKICAgIGZpZWxkcyA9IGMoCiAgICAgICJHZW5jb2RlXzQzX2h1Z29TeW1ib2wiLAogICAgICAiR2VuY29kZV80M192YXJpYW50Q2xhc3NpZmljYXRpb24iLAogICAgICAiR2VuY29kZV80M192YXJpYW50VHlwZSIKICAgICkKICApKQpgYGAKCkNvbnN0cnVjdCBhIGRhdGEgZnJhbWUgd2l0aCB1c2VmdWwgdmFsdWVzIGZvciBlYXNpZXIgcGxvdHRpbmc6CgpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9NX0Kc252Lmxpc3QgPC0gbGlzdCgpCmZvcihzYW1wbGUubmFtZSBpbiBuYW1lcyh2Y2YubGlzdC5xYykpIHsKICB2Y2YgPC0gdmNmLmxpc3QucWNbW3NhbXBsZS5uYW1lXV0KICBkZi5hbm5vIDwtIHZjZi5saXN0LmFubm9bW3NhbXBsZS5uYW1lXV0KICBzbnYubGlzdFtbc2FtcGxlLm5hbWVdXSA8LSBhcy5kYXRhLmZyYW1lKGdlbm8odmNmKVtbIkFGIl1dKSAlPiUKICAgIHNldE5hbWVzKC4sIGMoIkFGX0NUUkwiLCAiQUZfUERMQyIpKSAlPiUKICAgIG11dGF0ZV9hbGwoIH4gdW5saXN0KC4pKSAlPiUKICAgIGNiaW5kKC4sIGRmLmFubm8pICU+JQogICAgbXV0YXRlKAogICAgICBEUCA9IGluZm8odmNmKVtbIkRQIl1dLAogICAgICBHRVJNUSA9IGluZm8odmNmKVtbIkdFUk1RIl1dLAogICAgICBOQUxPRCA9IGluZm8odmNmKVtbIk5BTE9EIl1dICU+JSB1bmxpc3QoKSwKICAgICAgTkxPRCA9IGluZm8odmNmKVtbIk5MT0QiXV0gJT4lIHVubGlzdCgpLAogICAgICBUTE9EID0gaW5mbyh2Y2YpW1siVExPRCJdXSAlPiUgdW5saXN0KCkKICAgICkgJT4lCiAgICByb3duYW1lc190b19jb2x1bW4oIlZhcmlhbnQiKSAlPiUKICAgIG11dGF0ZShQb3NpdGlvbl9oZzM4ID0gd29yZChWYXJpYW50LCAxLCBzZXAgPSAiXyIpLAogICAgICAgICAgIC5iZWZvcmUgPSAiVmFyaWFudCIpICU+JQogICAgbXV0YXRlKFZhcmlhbnQgPSB3b3JkKFZhcmlhbnQsIDIsIHNlcCA9ICJfIikpCn0Kc252Lmxpc3QKYGBgCgojIFZpc3VhbGl6YXRpb25zCgpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9Nn0Kc252LmRmIDwtIGJpbmRfcm93cyhzbnYubGlzdCwgLmlkID0gInNhbXBsZSIpCgpnZ3Bsb3Qoc252LmRmLAogICAgICAgYWVzKHkgPSBBRl9QRExDLAogICAgICAgICAgIHggPSBBRl9DVFJMKSkgKwogIGdlb21faml0dGVyKGFlcyhjb2xvciA9IEdlbmNvZGVfNDNfdmFyaWFudENsYXNzaWZpY2F0aW9uKSwgc2l6ZSA9IDIpICsKICB5bGltKDAsIDEpICsgeGxpbSgwLCAxKSArCiAgdGhlbWVfYncoKQpgYGAKCmBgYHtyfQpzbnYuZGYgJT4lIAogIGZpbHRlcihHZW5jb2RlXzQzX2h1Z29TeW1ib2wgPT0gIlRQNTMiKQpgYGAKCgojIyBWYXJpYW50IG9yaWdpbiBmaWx0ZXJzCgpEUCBpcyBhbiBhcHByb3hpbWF0ZSByZWFkIGRlcHRoCgpHRVJNUSBUaGUgcGhyZWQtc2NhbGVkIHBvc3RlcmlvciBwcm9iYWJpbGl0eSB0aGF0IHRoZSBhbHRlcm5hdGUgYWxsZWxlKHMpIGFyZSBOT1QgZ2VybWxpbmUgdmFyaWFudHMKCk5BTE9EIChOb3JtYWwgQWxsZWxlIExvZyBPZGRzKSBpcyBuZWdhdGl2ZSBsb2cgMTAgb2RkcyBvZiBhcnRpZmFjdCBpbiBub3JtYWwgd2l0aCBzYW1lIGFsbGVsZSBmcmFjdGlvbiBhcyB0dW1vci4KNSUgY2hhbmNlIGlzIE5BTE9EIH4gMS4zCgpOTE9EIChOb3JtYWwgTG9nIE9kZHMpIGlzIGxvZyBvZGRzIHRoYXQgdGhlIHZhcmlhbnQgaXMgbm90IHByZXNlbnQgaW4gdGhlIG5vcm1hbCBzYW1wbGUKClRMT0QgKFR1bW9yIExvZyBPZGRzKSBpcyBhIGxvZyBvZGRzIHJhdGlvIHRoYXQgcmVwcmVzZW50cyB0aGUgbGlrZWxpaG9vZCB0aGF0IHRoZSBvYnNlcnZlZCB0dW1vciBhbGxlbGUgZGVwdGggKGNvdmVyYWdlKSBpcyBkdWUgdG8gYSByZWFsIG11dGF0aW9uLgoKYGBge3IsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTV9CmZpbHRlcnMgPSBsaXN0KAogICJEUCIgPSAzMCwKICAiR0VSTVEiID0gOTAsCiAgIk5BTE9EIiA9IC1sb2cxMCgwLjA1KSwKICAiTkxPRCIgPSA2LAogICJUTE9EIiA9IDMwLAogICJBRl9DVFJMIiA9IDAuMDI1LAogICJBRl9UVU1PUiIgPSAwLjEKKQoKCnNudi5kZiAlPiUgCiAgZ2dwbG90KC4sCiAgICAgICBhZXMoeSA9IEFGX0NUUkwsIHggPSBEUCkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IEdFUk1RKSwKICAgICAgICAgICAgIHNoYXBlID0gMSwgYWxwaGEgPSAwLjc1LAogICAgICAgICAgICAgc2l6ZSA9IDEuNSwgc3Ryb2tlID0gMQogICAgICAgICAgICAgKSArCiAgc2NhbGVfY29sb3VyX3N0ZXBzMigKICAgIGhpZ2ggPSAiZ3JlZW4iLAogICAgbWlkcG9pbnQgPSA1MCwKICAgIG1pZCA9ICJibHVlIiwKICAgIGxvdyA9ICJibHVlIiwKICAgIGJyZWFrcyA9IGZpbHRlcnNbWyJHRVJNUSJdXQogICkgKwogIHNjYWxlX3hfY29udGludW91cyh0cmFucyA9ICJsb2cxMCIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gZmlsdGVyc1tbIkFGX0NUUkwiXV0sIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBmaWx0ZXJzW1siRFAiXV0sIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBsYWJzKHkgPSAiQ1RSTCBBRiwgJSIpCgpzbnYuZGYgJT4lIAogIGRwbHlyOjpmaWx0ZXIoCiAgICBHRVJNUSA+PSBmaWx0ZXJzW1siR0VSTVEiXV0sCiAgICBEUCA+PSBmaWx0ZXJzW1siRFAiXV0KICAgICkgJT4lIAogIGdncGxvdCguLAogICAgICAgYWVzKHggPSBUTE9ELAogICAgICAgICAgIHkgPSBOTE9EKSkgKwogIGdlb21fZGVuc2l0eV8yZChjb2xvciA9ICJibGFjayIpICsKICBnZW9tX3BvaW50KAogICAgYWVzKGNvbG9yID0gTkFMT0QsCiAgICAgICAgc2hhcGUgPSBHRVJNUQogICAgICAgICksIHNpemUgPSAyLCBzdHJva2UgPSAxKSArCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zID0gImxvZzEwIikgKwogIHNjYWxlX3hfY29udGludW91cyh0cmFucyA9ICJsb2cxMCIpICsKICBzY2FsZV9jb2xvdXJfc3RlcHMyKAogICAgaGlnaCA9ICJncmVlbiIsCiAgICBtaWRwb2ludCA9IGZpbHRlcnNbWyJOQUxPRCJdXSAvIDIsCiAgICBtaWQgPSAiYmx1ZSIsCiAgICBsb3cgPSAiYmx1ZSIsCiAgICBicmVha3MgPSBmaWx0ZXJzW1siTkFMT0QiXV0KICApICsKICBzY2FsZV9zaGFwZV9iaW5uZWQoYnJlYWtzID0gYyhmaWx0ZXJzW1siR0VSTVEiXV0pLCBzb2xpZCA9IEZBTFNFKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gZmlsdGVyc1tbIk5MT0QiXV0sIGxpbmV0eXBlID0gMikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGZpbHRlcnNbWyJUTE9EIl1dLCBsaW5ldHlwZSA9IDIpCmBgYAoKYGBge3Igd2FybmluZz1GQUxTRX0KcHVsbChzbnYuZGYsIEdlbmNvZGVfNDNfdmFyaWFudENsYXNzaWZpY2F0aW9uKSAlPiUgdW5pcXVlKCkgJT4lCiAgbGVuZ3RoKCkgLT4gbi5TTlYudHlwZXMudW5maWx0ZXJlZAoKYygKICBicmV3ZXIucGFsKG4gPSAxMiwgbmFtZSA9ICJQYWlyZWQiKSwKICBicmV3ZXIucGFsKG4gPSBuLlNOVi50eXBlcy51bmZpbHRlcmVkIC0gMTIsIG5hbWUgPSAiRGFyazIiKQopIC0+IHBhbC5jb2xvcnMKCmZpbHRlcihzbnYuZGYsIHNhbXBsZSAhPSAiUDA2OV9QVF9SRUxfQk0iKSAlPiUgCiAgZHBseXI6OmZpbHRlcigKICAgIEdFUk1RID49IGZpbHRlcnNbWyJHRVJNUSJdXSwKICAgIERQID49IGZpbHRlcnNbWyJEUCJdXQogICAgKSAlPiUgCiAgZ2dwbG90KC4sCiAgICAgICAgIGFlcyh4ID0gVExPRCwKICAgICAgICAgICAgIHkgPSBOTE9EKSkgKwogIGdlb21faGV4KGFlcyhmaWxsID0gR2VuY29kZV80M192YXJpYW50Q2xhc3NpZmljYXRpb24pLCBhbHBoYSA9IDAuNzUpICsKICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnMgPSAibG9nMTAiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gImxvZzEwIikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGZpbHRlcnNbWyJOTE9EIl1dLCBsaW5ldHlwZSA9IDIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBmaWx0ZXJzW1siVExPRCJdXSwgbGluZXR5cGUgPSAyKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsLmNvbG9ycykKYGBgCgojIFN1YnNldHRpbmcgdGhlIFZDRiBvYmplY3QKCkNvbnN0cnVjdGluZyBhIGZpbHRlciB2ZWN0b3IgZm9yIGB2Y2ZfcWNgIG9iamVjdDoKCmBgYHtyfQpjb2xuYW1lcyhzbnYuZGYpCmBgYAoKYGBge3J9CnNudi5maWx0ZXIuZGYgPC0gc252LmRmICU+JQogIG11dGF0ZSgKICAgIHNlY29uZF9maWx0ZXIgPSBjYXNlX3doZW4oCiAgICAgIERQIDw9IGZpbHRlcnNbWyJEUCJdXSB+ICJsb3dfRFAiLAogICAgICBHRVJNUSA8PSBmaWx0ZXJzW1siR0VSTVEiXV0gfiAibG93X0dFUk1RIiwKICAgICAgQUZfQ1RSTCA+IGZpbHRlcnNbWyJBRl9DVFJMIl1dIH4gImhpZ2hfQ1RSTF9BRiIsCiAgICAgIEFGX1BETEMgPCBmaWx0ZXJzW1siQUZfVFVNT1IiXV0gfiAibG93X1R1bW9yX0FGIiwKICAgICAgTkFMT0QgPCBmaWx0ZXJzW1siTkFMT0QiXV0gfiAgICJsb3dfTkFMT0QiLAogICAgICBOTE9EIDwgZmlsdGVyc1tbIk5MT0QiXV0gfiAgICAgImxvd19OTE9EIiwKICAgICAgVExPRCA8IGZpbHRlcnNbWyJUTE9EIl1dIH4gICAgICJsb3dfVExPRCIsCiAgICAgIFRSVUUgfiAiUEFTUyIKICAgICkKICApCgphbGxvd2VkLnBvc2l0aW9ucyA8LSBzbnYuZmlsdGVyLmRmICU+JQogIGZpbHRlcihzYW1wbGUgIT0gIlAwNjlfUFRfUkVMX0JNIiAmCiAgICAgICAgICAgc2Vjb25kX2ZpbHRlciA9PSAiUEFTUyIpICU+JSBwdWxsKFBvc2l0aW9uX2hnMzgpCgpzbnYuZmlsdGVyLmRmIDwtIHNudi5maWx0ZXIuZGYgJT4lCiAgbXV0YXRlKAogICAgc2Vjb25kX2ZpbHRlciA9IGNhc2Vfd2hlbigKICAgICAgc2FtcGxlID09ICJQMDY5X1BUX1JFTF9CTSIgJgogICAgICAgIFBvc2l0aW9uX2hnMzggJWluJSBhbGxvd2VkLnBvc2l0aW9ucyB+ICJQQVNTIiwKICAgICAgVFJVRSB+IHNlY29uZF9maWx0ZXIKICAgICkKICApCgpzbnYuZmlsdGVyLmRmICU+JSAKICBmaWx0ZXIoc2Vjb25kX2ZpbHRlciA9PSAiUEFTUyIpCmBgYAoKYGBge3J9CnNudi5maWx0ZXIuZGYgJT4lIAogIGZpbHRlcihHZW5jb2RlXzQzX2h1Z29TeW1ib2wgPT0gIlRQNTMiKQpgYGAKCgpgYGB7ciwgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9NX0KYygKICAibG93X0RQIiA9ICJyZWQiLAogICJsb3dfR0VSTVEiID0gIm9yYW5nZSIsCiAgImxvd19OQUxPRCIgPSAicHVycGxlIiwKICAibG93X05MT0QiID0gInZpb2xldCIsCiAgImxvd19UTE9EIiA9ICJmaXJlYnJpY2siLAogICJoaWdoX0NUUkxfQUYiID0gImNvcmFsMiIsCiAgImxvd19UdW1vcl9BRiIgPSAiYmx1ZXZpb2xldCIsCiAgIlBBU1MiID0gImdyZWVuIgopIC0+IHBhbC5jb2xvcnMKCnNudi5maWx0ZXIuZGYgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjb250YWlucygiQUYiKSwgdmFsdWVzX3RvID0gIkFGIikgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbmFtZSwgeSA9IEFGKSkgKwogIGdnYmVlc3dhcm06Omdlb21fcXVhc2lyYW5kb20oYWVzKGNvbG9yID0gc2Vjb25kX2ZpbHRlcikpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBmaWx0ZXJzW1siQUZfQ1RSTCJdXSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIHRoZW1lX2J3KCkgKyBsYWJzKHggPSBOVUxMKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBhbC5jb2xvcnMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD01fQpzbnYuZmlsdGVyLmRmICU+JQogIGZpbHRlcihzZWNvbmRfZmlsdGVyID09ICJQQVNTIikgJT4lIAogIHBpdm90X2xvbmdlcihjb2xzID0gY29udGFpbnMoIkFGIiksIHZhbHVlc190byA9ICJBRiIpICU+JQogIGdncGxvdChhZXMoeCA9IG5hbWUsIHkgPSBBRikpICsKICBnZ2JlZXN3YXJtOjpnZW9tX3F1YXNpcmFuZG9tKGFlcyhjb2xvciA9IHNlY29uZF9maWx0ZXIpKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gZmlsdGVyc1tbIkFGX0NUUkwiXV0sIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICB0aGVtZV9idygpICsgbGFicyh4ID0gTlVMTCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWwuY29sb3JzKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkKYGBgCgpTYW1lIHBsb3QgYnV0IHdpdGhvdXQgdGhlIFNOViB3aGljaCBkaWQgbm90IHBhc3MgdGhlIGZpbHRlcnMuCgpEYXRhIGZyYW1lIG9mIGFsbCBmaWx0ZXJlZCBTTlZzOgoKYGBge3J9CnNudi5maWx0ZXIuZGYud2lkZSA8LSBzbnYuZmlsdGVyLmRmICU+JQogIGZpbHRlcihzZWNvbmRfZmlsdGVyID09ICJQQVNTIikgJT4lIAogIGRwbHlyOjpzZWxlY3Qoc2FtcGxlLCBQb3NpdGlvbl9oZzM4LCBWYXJpYW50LCBBRl9QRExDLAogICAgICAgICBHZW5jb2RlXzQzX2h1Z29TeW1ib2wsIEdlbmNvZGVfNDNfdmFyaWFudENsYXNzaWZpY2F0aW9uKSAlPiUgCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9ICJzYW1wbGUiLCB2YWx1ZXNfZnJvbSA9ICJBRl9QRExDIikgJT4lIAogIG11dGF0ZShhY3Jvc3MoZXZlcnl0aGluZygpLCB+cmVwbGFjZV9uYSguLCAwKSkpCnNudi5maWx0ZXIuZGYud2lkZQpgYGAKCkRhdGEgZnJhbWUgb2YgbWlzc2Vuc2UgZmlsdGVyZWQgU05WczoKCmBgYHtyfQpzbnYuZmlsdGVyLmRmICU+JQogIGZpbHRlcihzZWNvbmRfZmlsdGVyID09ICJQQVNTIikgJT4lIAogIGZpbHRlcihHZW5jb2RlXzQzX3ZhcmlhbnRDbGFzc2lmaWNhdGlvbiA9PSAiTUlTU0VOU0UiKSAlPiUgCiAgZHBseXI6OnNlbGVjdChzYW1wbGUsIFBvc2l0aW9uX2hnMzgsIFZhcmlhbnQsIEFGX1BETEMsCiAgICAgICAgIEdlbmNvZGVfNDNfaHVnb1N5bWJvbCkgJT4lIAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSAic2FtcGxlIiwgdmFsdWVzX2Zyb20gPSAiQUZfUERMQyIpCmBgYAoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD01fQpkb3RfcG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXIoaGVpZ2h0ID0gMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdpZHRoID0gMC4yNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZWQgPSA0MTMpCgpzbnYuZmlsdGVyLmRmICU+JQogIGZpbHRlcihzZWNvbmRfZmlsdGVyID09ICJQQVNTIikgJT4lIAogIAogIGdncGxvdCgpICsKICBsZW1vbjo6Z2VvbV9wb2ludGxpbmUoCiAgICBhZXMoCiAgICAgIHggPSBzYW1wbGUsCiAgICAgIHkgPSBBRl9QRExDLAogICAgICBncm91cCA9IFBvc2l0aW9uX2hnMzgsCiAgICAgIGZpbGwgPSBHZW5jb2RlXzQzX3ZhcmlhbnRDbGFzc2lmaWNhdGlvbgogICAgKSwKICAgIGxpbmVjb2xvciA9ICJncmF5MzAiLAogICAgbGluZXR5cGUgPSAzLAogICAgcG9zaXRpb24gPSBkb3RfcG9zaXRpb24sCiAgICBzaGFwZSA9IDIxLAogICAgc2l6ZSA9IDIKICApICsKICBnZW9tX3BvaW50KAogICAgYWVzKAogICAgICB4ID0gc2FtcGxlLAogICAgICB5ID0gQUZfUERMQywKICAgICAgZ3JvdXAgPSBQb3NpdGlvbl9oZzM4LAogICAgICBmaWxsID0gR2VuY29kZV80M192YXJpYW50Q2xhc3NpZmljYXRpb24KICAgICksCiAgICBwb3NpdGlvbiA9IGRvdF9wb3NpdGlvbiwKICAgIHNoYXBlID0gMjEsCiAgICBzaXplID0gMgogICkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAuMDUsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50LCBsaW1pdHMgPSBjKDAsIDEpKSArCiAgdGhlbWVfYncoKSArCiAgbGFicyh4ID0gTlVMTCwgeSA9ICJBbGxlbGljIGZyZXF1ZW5jeSIsCiAgICAgICBmaWxsID0gIlNOViB0eXBlIikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NX0KZG90X3Bvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKGhlaWdodCA9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aWR0aCA9IDAuMjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWVkID0gNDEzKQoKc252LmZpbHRlci5kZiAlPiUKICBmaWx0ZXIoc2Vjb25kX2ZpbHRlciA9PSAiUEFTUyIpICU+JQogIGZpbHRlcihHZW5jb2RlXzQzX3ZhcmlhbnRDbGFzc2lmaWNhdGlvbiA9PSAiTUlTU0VOU0UiKSAlPiUKICBnZ3Bsb3QoKSArCiAgbGVtb246Omdlb21fcG9pbnRsaW5lKAogICAgYWVzKHggPSBzYW1wbGUsCiAgICAgICAgeSA9IEFGX1BETEMsCiAgICAgICAgZ3JvdXAgPSBQb3NpdGlvbl9oZzM4KSwKICAgIGxpbmVjb2xvciA9ICJncmF5MzAiLAogICAgbGluZXR5cGUgPSAzLAogICAgcG9zaXRpb24gPSBkb3RfcG9zaXRpb24sCiAgICBzaGFwZSA9IDIxLAogICAgc2l6ZSA9IDIsCiAgICBmaWxsID0gImdyYXk5MCIKICApICsKICAKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLjA1LCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCwgbGltaXRzID0gYygwLCAxKSkgKwogIHRoZW1lX2J3KCkgKwogIGxhYnMoeCA9IE5VTEwsIHkgPSAiQWxsZWxpYyBmcmVxdWVuY3kiLAogICAgICAgZmlsbCA9ICJTTlYgdHlwZSIpCmBgYAoKVE9ETzogYWRkIHRoZSBzZWNvbmQgc2FtcGxlIGFmdGVyIGZpeGluZyB0aGUgZmlsdGVycy4KCiMgU2F2ZSB0aGUgdmFyaWFudCB0YWJsZQoKYGBge3J9Cm91dHB1dF9maWxlX3hsc3ggPC0gcGFzdGUwKCJGaWx0ZXJlZFNOVi54bHN4IikKb3V0cHV0X3BhdGhfeGxzeCA8LSBmaWxlLnBhdGgob3V0cHV0X2Rpciwgb3V0cHV0X2ZpbGVfeGxzeCkKCmxpc3QoCiAgIlRvdGFsIFNOViIgPSBzbnZfZmlsdGVyLmRmLAogICJGaWx0ZXJlZCBTTlYiID0gc252X2ZpbHRlci5kZiAlPiUgCiAgICBmaWx0ZXIoc2Vjb25kX2ZpbHRlciA9PSAiUEFTUyIpICU+JSAKICAgIGRwbHlyOjpzZWxlY3QoLXNlY29uZF9maWx0ZXIpCikgJT4lIAogIHdyaXRlLnhsc3goLiwgb3V0cHV0X3BhdGhfeGxzeCwgb3ZlcndyaXRlID0gVCkKYGBgCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAKCg==