R Notebook: Provides reproducible analysis for Resistant Taxa in the following manuscript:

Citation: Romanowicz KJ, Resnick C, Hinton SR, Plesa C. Exploring antibiotic resistance in diverse homologs of the dihydrofolate reductase protein family through broad mutational scanning. bioRxiv, 2025.

GitHub Repository: https://github.com/PlesaLab/DHFR

NCBI BioProject: https://www.ncbi.nlm.nih.gov/bioproject/1189478

Experiment

This pipeline processes a library of 1,536 DHFR homologs and their associated mutants, with two-fold redundancy (two codon variants per sequence). Fitness scores are derived from a multiplexed in-vivo assay using a trimethoprim concentration gradient, assessing the ability of these homologs and their mutants to complement functionality in an E. coli knockout strain and their tolerance to trimethoprim treatment. This analysis provides insights into how antibiotic resistance evolves across a range of evolutionary starting points. Sequence data were generated using the Illumina NovaSeq platform with 100 bp paired-end sequencing of amplicons.

Methods overview to achieve a broad-mutational scan for DHFR homologs.
Methods overview to achieve a broad-mutational scan for DHFR homologs.

Packages

The following R packages must be installed prior to loading into the R session. See the Reproducibility tab for a complete list of packages and their versions used in this workflow.

# Load the latest version of python (3.10.14) for downstream use:
library(reticulate)
use_python("/Users/krom/miniforge3/bin/python3")

# Make a vector of required packages
required.packages <- c("ape", "bio3d", "Biostrings", "castor", "cowplot", "devtools", "dplyr", "ggExtra", "ggnewscale", "ggplot2", "ggridges", "ggtree", "ggtreeExtra", "glmnet", "gridExtra","igraph", "knitr", "matrixStats", "patchwork", "pheatmap", "purrr", "pscl", "RColorBrewer", "reshape","reshape2", "ROCR", "seqinr", "scales", "stringr", "stringi", "tidyr", "tidytree", "viridis", "zoo")

# Load required packages with error handling
loaded.packages <- lapply(required.packages, function(package) {
  if (!require(package, character.only = TRUE)) {
    install.packages(package, dependencies = TRUE)
    if (!require(package, character.only = TRUE)) {
      message("Package ", package, " could not be installed and loaded.")
      return(NULL)
    }
  }
  return(package)
})

# Remove NULL entries from loaded packages
loaded.packages <- loaded.packages[!sapply(loaded.packages, is.null)]
Loaded packages: ape, bio3d, Biostrings, castor, cowplot, devtools, dplyr, ggExtra, ggnewscale, ggplot2, ggridges, ggtree, ggtreeExtra, glmnet, gridExtra, igraph, knitr, matrixStats, patchwork, pheatmap, purrr, pscl, RColorBrewer, reshape, reshape2, ROCR, seqinr, scales, stringr, stringi, tidyr, tidytree, viridis, zoo 

Import Data Files

Import PERFECTS files generated from DHFR.3.Perfects.RMD

# Fittree15good_taxa_merged
Fittree15good_taxa_merged <- read.csv("Perfects/perfects_files_formatted/Fittree15good_taxa_merged.csv", 
                         header = TRUE, stringsAsFactors = FALSE)

# ncbi_taxa
ncbi_taxa <- read.csv("Perfects/perfects_files_formatted/ncbi_taxa.csv", 
                         header = TRUE, stringsAsFactors = FALSE)

Import MUTANTS files generated from DHFR.4.Mutants.RMD

# Alltree15_taxa_merged
Alltree15_taxa_merged <- read.csv("Mutants/mutants_files_formatted/Alltree15_taxa_merged.csv", 
                         header = TRUE, stringsAsFactors = FALSE)

# perfects_15_16_5BCs_tree
perfects_15_16_5BCs_tree <- read.csv("Mutants/mutants_files_formatted/perfects_15_16_5BCs_tree.csv", 
                         header = TRUE, stringsAsFactors = FALSE)

# mut_collapse_15_good_filtered
mut_collapse_15_good_filtered <- read.csv("Mutants/mutants_files_formatted/mut_collapse_15_good_filtered.csv", 
                         header = TRUE, stringsAsFactors = FALSE)

# orginfo
orginfo <- read.csv("Mutants/mutants_files_formatted/orginfo.csv", 
                         header = TRUE, stringsAsFactors = FALSE)

Resistance Analysis

Resistant Taxa b/w Codons

Start by plotting resistant taxa at 400x MIC (200 ug/mL TMP) shared between codon libraries

Add fitness scores for Lib15 to the Alltree15_taxa_merged object prior to plotting (may or may not use downstream)

Alltree15_taxa_merged <- Alltree15_taxa_merged %>%
  left_join(perfects_15_16_5BCs_tree %>% select(ID, fitD05D03, fitD06D03, fitD07D03, fitD08D03, fitD09D03, fitD10D03, fitD11D03), by = "ID")

Fitness Filtering

Filter the Alltree15_taxa_merged dataset for distinct mutIDs with fitness values less than -1 (bad), greater than -1 (good), or greater than 0 (zero) at each TMP:

# Complementation (0-TMP)
BadFit_0_tmp <- Alltree15_taxa_merged %>%
  filter(fitD05D03 < -1)
GoodFit_0_tmp <- Alltree15_taxa_merged %>%
  filter(fitD05D03 >= -1)
ZeroFit_0_tmp <- Alltree15_taxa_merged %>%
  filter(fitD05D03 >= 0)

# 0.058-TMP
BadFit_0.058_tmp <- Alltree15_taxa_merged %>%
  filter(fitD06D03 < -1)
GoodFit_0.058_tmp <- Alltree15_taxa_merged %>%
  filter(fitD06D03 >= -1)
ZeroFit_0.058_tmp <- Alltree15_taxa_merged %>%
  filter(fitD06D03 >= 0)

# 0.5-TMP
BadFit_0.5_tmp <- Alltree15_taxa_merged %>%
  filter(fitD07D03 < -1)
GoodFit_0.5_tmp <- Alltree15_taxa_merged %>%
  filter(fitD07D03 >= -1)
ZeroFit_0.5_tmp <- Alltree15_taxa_merged %>%
  filter(fitD07D03 >= 0)

# 1.0-TMP
BadFit_1.0_tmp <- Alltree15_taxa_merged %>%
  filter(fitD08D03 < -1)
GoodFit_1.0_tmp <- Alltree15_taxa_merged %>%
  filter(fitD08D03 >= -1)
ZeroFit_1.0_tmp <- Alltree15_taxa_merged %>%
  filter(fitD08D03 >= 0)

# 10-TMP
BadFit_10_tmp <- Alltree15_taxa_merged %>%
  filter(fitD09D03 < -1)
GoodFit_10_tmp <- Alltree15_taxa_merged %>%
  filter(fitD09D03 >= -1)
ZeroFit_10_tmp <- Alltree15_taxa_merged %>%
  filter(fitD09D03 >= 0)

# 50-TMP
BadFit_50_tmp <- Alltree15_taxa_merged %>%
  filter(fitD10D03 < -1)
GoodFit_50_tmp <- Alltree15_taxa_merged %>%
  filter(fitD10D03 >= -1)
ZeroFit_50_tmp <- Alltree15_taxa_merged %>%
  filter(fitD10D03 >= 0)

# 200-TMP
BadFit_200_tmp <- Alltree15_taxa_merged %>%
  filter(fitD11D03 < -1)
GoodFit_200_tmp <- Alltree15_taxa_merged %>%
  filter(fitD11D03 >= -1)
ZeroFit_200_tmp <- Alltree15_taxa_merged %>%
  filter(fitD11D03 >= 0)

Resistance Summary Table

Summarize the number of unique mutIDs with fitness values < -1 (bad), >= -1 (good), and >= 0 (zero):

fit_counts <- data.frame(
  "Condition" = c("Complementation (0-TMP)", "0.058-TMP", "0.5-TMP", "1.0-TMP", "10-TMP", "50-TMP", "200-TMP"),
  "mutIDs_bad" = c(nrow(BadFit_0_tmp), nrow(BadFit_0.058_tmp), nrow(BadFit_0.5_tmp),
                 nrow(BadFit_1.0_tmp),nrow(BadFit_10_tmp), nrow(BadFit_50_tmp), nrow(BadFit_200_tmp)),
  "mutIDs_good" = c(nrow(GoodFit_0_tmp), nrow(GoodFit_0.058_tmp), nrow(GoodFit_0.5_tmp),
                 nrow(GoodFit_1.0_tmp),nrow(GoodFit_10_tmp), nrow(GoodFit_50_tmp), nrow(GoodFit_200_tmp)),
  "mutIDs_zero" = c(nrow(ZeroFit_0_tmp), nrow(ZeroFit_0.058_tmp), nrow(ZeroFit_0.5_tmp), 
                 nrow(ZeroFit_1.0_tmp), nrow(ZeroFit_10_tmp), nrow(ZeroFit_50_tmp), nrow(ZeroFit_200_tmp)))

fit_counts

Resistance Plots

Plot at the PHYLUM level:

library(tidytext)

min(GoodFit_200_tmp$fitD11D03)
[1] -0.9570449
max(GoodFit_200_tmp$fitD11D03)
[1] 6.619919
# Create a long format data frame
GoodFit_200_tmp_phylum_data <- GoodFit_200_tmp %>%
  mutate(NCBI.genus = if_else(is.na(NCBI.genus), NCBI.family, NCBI.genus)) %>%
  mutate(NCBI.species = if_else(is.na(NCBI.species), NCBI.genus, NCBI.species)) %>%
  select(ID, NCBI.phylum, NCBI.genus, NCBI.species, fitD05D03, fitD06D03, fitD07D03, fitD08D03, fitD09D03, fitD10D03, fitD11D03) %>%
  pivot_longer(cols = starts_with("fitD"), names_to = "Condition", values_to = "Fitness")

# Plot the line graphs
GoodFit_200_tmp_phylum_plot <- ggplot(GoodFit_200_tmp_phylum_data, 
                                     aes(x = Condition, y = Fitness, 
                                         group = ID, color = reorder_within(NCBI.species, NCBI.phylum, NCBI.phylum))) +
  geom_line(linewidth = 0.75) +
  facet_wrap(~NCBI.phylum, ncol = 3, scales = "free_y") +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5),
        legend.position = "none",
        plot.title = element_text(hjust = 0.5)) +
  scale_x_discrete(labels = c("fitD05D03" = "0-TMP",
                              "fitD06D03" = "0.058-TMP",
                              "fitD07D03" = "0.5-TMP",
                              "fitD08D03" = "1.0-TMP",
                              "fitD09D03" = "10-TMP",
                              "fitD10D03" = "50-TMP",
                              "fitD11D03" = "200-TMP")) +
  #coord_cartesian(ylim = c(-6, 8)) +
  #geom_hline(yintercept = 0, linetype = "dashed") +
  geom_hline(yintercept = -1, linetype = "dashed", color = "red") +
  ggtitle("Taxonomic Resistance to Trimethoprim") +
  guides(color = guide_legend(ncol = 1)) +
  labs(color = "Species (mutID)")

GoodFit_200_tmp_phylum_plot

Plot at the CLASS level:

# Create a long format data frame
GoodFit_200_tmp_class_data <- GoodFit_200_tmp %>%
  mutate(NCBI.genus = if_else(is.na(NCBI.genus), NCBI.family, NCBI.genus)) %>%
  mutate(NCBI.species = if_else(is.na(NCBI.species), NCBI.genus, NCBI.species)) %>%
  select(ID, NCBI.class, NCBI.genus, NCBI.species, fitD05D03, fitD06D03, fitD07D03, fitD08D03, fitD09D03, fitD10D03, fitD11D03) %>%
  pivot_longer(cols = starts_with("fitD"), names_to = "Condition", values_to = "Fitness")

# Plot the line graphs
GoodFit_200_tmp_class_plot <- ggplot(GoodFit_200_tmp_class_data, 
                                    aes(x = Condition, y = Fitness, group = ID, color = NCBI.species)) +
  geom_line(linewidth = 0.75) +
  facet_wrap(~NCBI.class, ncol = 3, scales = "free_y") +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5),
        legend.position = "right",
        plot.title = element_text(hjust = 0.5)) +
  scale_x_discrete(labels = c("fitD05D03" = "0-TMP",
                              "fitD06D03" = "0.058-TMP",
                              "fitD07D03" = "0.5-TMP",
                              "fitD08D03" = "1.0-TMP",
                              "fitD09D03" = "10-TMP",
                              "fitD10D03" = "50-TMP",
                              "fitD11D03" = "200-TMP")) +
  #coord_cartesian(ylim = c(-10, 10)) +
  geom_hline(yintercept = -1, linetype = "dashed", color = "red") +
  ggtitle("Taxonomic Resistance at Class-level") +
  guides(color = guide_legend(ncol = 1)) +
  labs(color = "Species (mutID)")

GoodFit_200_tmp_class_plot

Plot at the GENUS level:

# Create a long format data frame
GoodFit_200_tmp_data <- GoodFit_200_tmp %>%
  mutate(NCBI.genus = if_else(is.na(NCBI.genus), NCBI.family, NCBI.genus)) %>%
  mutate(NCBI.species = if_else(is.na(NCBI.species), NCBI.genus, NCBI.species)) %>%
  select(ID, NCBI.class, NCBI.genus, NCBI.species, fitD05D03, fitD06D03, fitD07D03, fitD08D03, fitD09D03, fitD10D03, fitD11D03) %>%
  pivot_longer(cols = starts_with("fitD"), names_to = "Condition", values_to = "Fitness")

# Plot the line graphs
GoodFit_200_tmp_plot <- ggplot(GoodFit_200_tmp_data, aes(x = Condition, y = Fitness, group = ID, color = NCBI.genus)) +
  geom_line(linewidth = 0.75) +
  facet_wrap(~NCBI.genus, ncol = 4, scales = "free_y") +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5),
        legend.position = "none",
        plot.title = element_text(hjust = 0.5)) +
  scale_x_discrete(labels = c("fitD05D03" = "0-TMP",
                              "fitD06D03" = "0.058-TMP",
                              "fitD07D03" = "0.5-TMP",
                              "fitD08D03" = "1.0-TMP",
                              "fitD09D03" = "10-TMP",
                              "fitD10D03" = "50-TMP",
                              "fitD11D03" = "200-TMP")) +
  #coord_cartesian(ylim = c(-10, 10)) +
  geom_hline(yintercept = -1, linetype = "dashed", color = "red")+
  ggtitle("Unique mutID Positive Fitness at Genus-level")

GoodFit_200_tmp_plot

Resistant Taxa Codon 1

Retain the resistant taxa at 400x MIC for Codon 1 (Lib15):

# Retain all resistant taxa at 400x MIC (fitness > -1)
Fittree15good.200tmp.resistant <- Fittree15good_taxa_merged %>%
  filter(fitD11D03 > -1)

# Retain all susceptible taxa at 400x MIC (fitness < -1)
Fittree15good.200tmp.dropout <- Fittree15good_taxa_merged %>%
  filter(fitD11D03 < -1)

Resistant Taxa Plot (ID)

Plot in an organized manner for easier visual interpretation:

# Reshape the data from wide to long format
Fittree15good_long <- Fittree15good.200tmp.resistant %>%
  pivot_longer(
    cols = starts_with("fitD"),
    names_to = "fit_variable",
    values_to = "fit_value"
  ) %>%
  mutate(fit_variable = factor(fit_variable, 
                               levels = paste0("fitD", sprintf("%02d", 5:11), "D03"),
                               labels = c("0.0", "0.058", "0.5", "1.0", "10", "50", "200")))

# Create vectors of IDs that you want to highlight
highlight_blue_ids <- c("WP_007631135", "WP_007654866", "WP_008578924", "WP_008979999", "WP_008990832", "WP_010075211")
highlight_green_ids <- c("WP_002839715", "WP_003686654", "WP_004442738", "WP_007817825", "WP_008152545", "WP_008237079", "WP_008369618", "WP_008390643", "WP_009746425", "WP_009778768")
highlight_red_ids <- c("NP_229441", "NP_359022", "WP_001393556", "WP_003498667", "WP_003575033", "WP_004026355", "WP_004064107", "WP_004895238", "WP_006709593", "WP_008583481", "WP_010021426")

# Combine all highlighted IDs in the desired order
all_highlight_ids <- c(highlight_blue_ids, highlight_green_ids, highlight_red_ids)

# Add a new column to indicate whether and how the ID should be highlighted
Fittree15good_long <- Fittree15good_long %>%
  mutate(highlight = case_when(
    ID %in% highlight_blue_ids ~ "blue",
    ID %in% highlight_green_ids ~ "green",
    ID %in% highlight_red_ids ~ "red",
    TRUE ~ "normal"
  )) %>%
  # Convert ID to a factor with the specified order
  mutate(ID = factor(ID, levels = c(all_highlight_ids, setdiff(unique(ID), all_highlight_ids))))

# Create the facet wrap plot
Fittree15good.200tmp.resistant.plot.v2 <- ggplot(Fittree15good_long, aes(x = fit_variable, y = fit_value, group = ID)) +
  geom_line() +
  geom_point() +
  facet_wrap(~ ID, scales = "free_y", ncol = 3) +
  theme_bw() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    strip.background = element_rect(fill = "white"),  # Default background
    strip.text = element_text(face = "bold")
  ) +
  labs(x = "Trimethoprim Concentration (ug/mL)", y = "Fitness Value", title = "Fitness Values by ID")

# Add highlighted backgrounds for specific plots
Fittree15good.200tmp.resistant.plot.v2 <- Fittree15good.200tmp.resistant.plot.v2 +
  geom_rect(data = filter(Fittree15good_long, highlight == "blue"),
            aes(xmin = -Inf, xmax = Inf, ymin = -Inf, ymax = Inf),
            fill = "lightblue", alpha = 0.3, inherit.aes = FALSE) +
  geom_rect(data = filter(Fittree15good_long, highlight == "green"),
            aes(xmin = -Inf, xmax = Inf, ymin = -Inf, ymax = Inf),
            fill = "lightgreen", alpha = 0.3, inherit.aes = FALSE) +
  geom_rect(data = filter(Fittree15good_long, highlight == "red"),
            aes(xmin = -Inf, xmax = Inf, ymin = -Inf, ymax = Inf),
            fill = "pink", alpha = 0.3, inherit.aes = FALSE) +
  geom_line() +  # Redraw lines to ensure they appear on top of the colored background
  geom_point()   # Redraw points to ensure they appear on top of the colored background

# Add dashed line at y = -1 for facets where it's within the y-axis range
Fittree15good.200tmp.resistant.plot.v2 <- Fittree15good.200tmp.resistant.plot.v2 +
  geom_hline(data = Fittree15good_long %>% 
               group_by(ID) %>% 
               summarize(min_y = min(fit_value), max_y = max(fit_value)) %>%
               filter(min_y <= -1, max_y >= -1),
             aes(yintercept = -1), 
             linetype = "dashed", color = "red", alpha = 0.7)

# Display the plot
print(Fittree15good.200tmp.resistant.plot.v2)

Resistant Taxa Plot (Taxon)

# Define the desired ID order
desired_id_order <- c("WP_007631135", "WP_007654866", "WP_008578924", "WP_008979999", "WP_010075211", "WP_008990832",  
                      "WP_008369618", "WP_008390643", "WP_003686654", "WP_004442738", "WP_007817825", "WP_008152545", 
                      "WP_008237079", "WP_009746425", "WP_009778768", "WP_002839715", "NP_229441", "NP_359022", 
                      "WP_001393556", "WP_003498667", "WP_003575033", "WP_006709593", "WP_004064107", "WP_004895238",  
                      "WP_008583481", "WP_004026355", "WP_010021426")

Fittree15good_long <- Fittree15good_long %>%
  mutate(highlight = case_when(
    ID %in% highlight_blue_ids ~ "blue",
    ID %in% highlight_green_ids ~ "green",
    ID %in% highlight_red_ids ~ "red",
    TRUE ~ "normal"
  )) %>%
  # Convert ID to a factor with the specified order
  mutate(ID = factor(ID, levels = desired_id_order)) %>%
  # Create a new column for faceting, using NCBI.class if NCBI.genus is NA
  mutate(facet_var = ifelse(is.na(NCBI.class), NCBI.name, NCBI.class)) %>%
  # Create a unique facet variable that includes both facet_var and ID
  mutate(facet_var_unique = paste(facet_var, ID, sep = "_")) %>%
  # Order the facet_var_unique based on the ID order
  mutate(facet_var_unique = factor(facet_var_unique, 
                                   levels = unique(facet_var_unique[order(match(ID, desired_id_order))])))

# Now, when you create your plot, use facet_wrap with the ordered facet_var_unique
Fittree15good.200tmp.resistant.plot.v2 <- ggplot(Fittree15good_long, aes(x = fit_variable, y = fit_value, group = ID)) +
  geom_line() +
  geom_point() +
  facet_wrap(~ facet_var_unique, scales = "free_y", ncol = 3, 
             labeller = labeller(facet_var_unique = function(x) gsub("_.*", "", x))) +
  theme_bw() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    strip.background = element_rect(fill = "white"),
    strip.text = element_text(face = "bold")
  ) +
  labs(x = "Trimethoprim Concentration (ug/mL)", y = "Fitness Value", title = "Fitness Values by Genus/Class")

# Add highlighted backgrounds for specific plots
Fittree15good.200tmp.resistant.plot.v2 <- Fittree15good.200tmp.resistant.plot.v2 +
  geom_rect(data = filter(Fittree15good_long, highlight == "blue"),
            aes(xmin = -Inf, xmax = Inf, ymin = -Inf, ymax = Inf),
            fill = "lightblue", alpha = 0.3, inherit.aes = FALSE) +
  geom_rect(data = filter(Fittree15good_long, highlight == "green"),
            aes(xmin = -Inf, xmax = Inf, ymin = -Inf, ymax = Inf),
            fill = "lightgreen", alpha = 0.3, inherit.aes = FALSE) +
  geom_rect(data = filter(Fittree15good_long, highlight == "red"),
            aes(xmin = -Inf, xmax = Inf, ymin = -Inf, ymax = Inf),
            fill = "pink", alpha = 0.3, inherit.aes = FALSE) +
  geom_line() +
  geom_point()

# Add dashed line at y = -1 for facets where it's within the y-axis range
Fittree15good.200tmp.resistant.plot.v2 <- Fittree15good.200tmp.resistant.plot.v2 +
  geom_hline(data = Fittree15good_long %>% 
               group_by(facet_var_unique) %>%
               summarize(min_y = min(fit_value), max_y = max(fit_value)) %>%
               filter(min_y <= -1, max_y >= -1),
             aes(yintercept = -1), 
             linetype = "dashed", color = "red", alpha = 0.7)

# Display the plot
print(Fittree15good.200tmp.resistant.plot.v2)

Resistant Taxa Plot (Merged)

# Reshape the data from wide to long format
Fittree15good_long <- Fittree15good.200tmp.resistant %>%
  pivot_longer(
    cols = starts_with("fitD"),
    names_to = "fit_variable",
    values_to = "fit_value"
  ) %>%
  mutate(fit_variable = factor(fit_variable, 
                               levels = paste0("fitD", sprintf("%02d", 5:11), "D03"),
                               labels = c("0.0", "0.058", "0.5", "1.0", "10", "50", "200"))) %>%
  # Create a new column for faceting, using NCBI.phylum if NCBI.class is NA
  mutate(facet_var = ifelse(is.na(NCBI.class), NCBI.name, NCBI.class)) %>%
  # Arrange alphabetically by facet_var
  arrange(facet_var)

# Create the facet wrap plot
Fittree15good.200tmp.resistant.plot.v2 <- ggplot(Fittree15good_long, aes(x = fit_variable, y = fit_value, group = ID, color = ID)) +
  geom_line() +
  geom_point() +
  facet_wrap(~ facet_var, scales = "free_y", ncol = 3) +
  theme_bw() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    strip.background = element_rect(fill = "white"),
    strip.text = element_text(face = "bold"),
    legend.position = "none",
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank()
  ) +
  labs(x = "Trimethoprim Concentration (ug/mL)", y = "Fitness Value", 
       title = "Fitness Values by Class/Phylum")

# Add dashed line at y = -1 for facets where it's within the y-axis range
Fittree15good.200tmp.resistant.plot.v2 <- Fittree15good.200tmp.resistant.plot.v2 +
  geom_hline(data = Fittree15good_long %>% 
               group_by(facet_var) %>% 
               summarize(min_y = min(fit_value), max_y = max(fit_value)) %>%
               filter(min_y <= -1, max_y >= -1),
             aes(yintercept = -1), 
             linetype = "dashed", color = "red", alpha = 0.7)

# Display the plot
print(Fittree15good.200tmp.resistant.plot.v2)

Taxon Fitness by Phylum

There is clear evidence from our phylogenetic trees that resistance follows clade-specific patterns, with more clades (genus-level) dropping out with increasing trimethoprim concentration. The following genus especially:

  • Acinetobacter
  • Bacillus
  • Bacteroides
  • Clostridium
  • Neisseria
  • Pseudomonas
  • Streptococcus

Good Filtered Dataset

First step is to make a copy of the complementing homolog (and their mutants) dataset and filter to retain relevent columns for the first sampling timepoint. Add NCBI taxonomy based on shared TaxID.

# Make a copy of the filtered fitness data for homologs (and their mutants) capable of complementation (fit > -1 @ fitD05D03)
L15_homo_mut_good_filtered <- mut_collapse_15_good_filtered %>%
  select(ID, mutID, seq, 
         fitD05D03, fitD06D03, fitD07D03, fitD08D03, fitD09D03, fitD10D03, fitD11D03, 
         numprunedBCs, numBCs, mutations, pct_ident)

Add “taxID” and “PctIdentEcoli” to each “ID”

# Merge "TaxID" and "PctIdentEcoli" from `orginfo` to `L15_homo_mut_good_filtered`:
L15_homo_mut_good_filtered <- L15_homo_mut_good_filtered %>%
  left_join(orginfo %>% select(ID, TaxID, PctIdentEcoli), by = "ID")

Add the NCBI taxonomy based on shared TaxID:

# Merge the NCBI taxonomy columns to Fittree15good_taxa based on shared TaxID
L15_homo_mut_good_filtered$NCBI.name <- NA
L15_homo_mut_good_filtered$NCBI.superkingdom <- NA
L15_homo_mut_good_filtered$NCBI.phylum <- NA
L15_homo_mut_good_filtered$NCBI.class <- NA
L15_homo_mut_good_filtered$NCBI.order <- NA
L15_homo_mut_good_filtered$NCBI.family <- NA
L15_homo_mut_good_filtered$NCBI.genus <- NA
L15_homo_mut_good_filtered$NCBI.species <- NA

# NCBI.name
L15_homo_mut_good_filtered$NCBI.name[L15_homo_mut_good_filtered$TaxID %in% ncbi_taxa$TaxID] <- ncbi_taxa$NCBI.name[match(L15_homo_mut_good_filtered$TaxID[L15_homo_mut_good_filtered$TaxID %in% ncbi_taxa$TaxID], ncbi_taxa$TaxID)]

# NCBI.superkingdom
L15_homo_mut_good_filtered$NCBI.superkingdom[L15_homo_mut_good_filtered$TaxID %in% ncbi_taxa$TaxID] <- ncbi_taxa$NCBI.superkingdom[match(L15_homo_mut_good_filtered$TaxID[L15_homo_mut_good_filtered$TaxID %in% ncbi_taxa$TaxID], ncbi_taxa$TaxID)]

# NCBI.phylum
L15_homo_mut_good_filtered$NCBI.phylum[L15_homo_mut_good_filtered$TaxID %in% ncbi_taxa$TaxID] <- ncbi_taxa$NCBI.phylum[match(L15_homo_mut_good_filtered$TaxID[L15_homo_mut_good_filtered$TaxID %in% ncbi_taxa$TaxID], ncbi_taxa$TaxID)]

# NCBI.class
L15_homo_mut_good_filtered$NCBI.class[L15_homo_mut_good_filtered$TaxID %in% ncbi_taxa$TaxID] <- ncbi_taxa$NCBI.class[match(L15_homo_mut_good_filtered$TaxID[L15_homo_mut_good_filtered$TaxID %in% ncbi_taxa$TaxID], ncbi_taxa$TaxID)]

# NCBI.order
L15_homo_mut_good_filtered$NCBI.order[L15_homo_mut_good_filtered$TaxID %in% ncbi_taxa$TaxID] <- ncbi_taxa$NCBI.order[match(L15_homo_mut_good_filtered$TaxID[L15_homo_mut_good_filtered$TaxID %in% ncbi_taxa$TaxID], ncbi_taxa$TaxID)]

# NCBI.family
L15_homo_mut_good_filtered$NCBI.family[L15_homo_mut_good_filtered$TaxID %in% ncbi_taxa$TaxID] <- ncbi_taxa$NCBI.family[match(L15_homo_mut_good_filtered$TaxID[L15_homo_mut_good_filtered$TaxID %in% ncbi_taxa$TaxID], ncbi_taxa$TaxID)]

# NCBI.genus
L15_homo_mut_good_filtered$NCBI.genus[L15_homo_mut_good_filtered$TaxID %in% ncbi_taxa$TaxID] <- ncbi_taxa$NCBI.genus[match(L15_homo_mut_good_filtered$TaxID[L15_homo_mut_good_filtered$TaxID %in% ncbi_taxa$TaxID], ncbi_taxa$TaxID)]

# NCBI.species
L15_homo_mut_good_filtered$NCBI.species[L15_homo_mut_good_filtered$TaxID %in% ncbi_taxa$TaxID] <- ncbi_taxa$NCBI.species[match(L15_homo_mut_good_filtered$TaxID[L15_homo_mut_good_filtered$TaxID %in% ncbi_taxa$TaxID], ncbi_taxa$TaxID)]

# Replace the value in "NCBI.phylum" column with the value from "NCBI.class" if "NCBI.phylum" is "Pseudomonadota"
L15_homo_mut_good_filtered$NCBI.phylum <- ifelse(L15_homo_mut_good_filtered$NCBI.phylum == "Pseudomonadota", L15_homo_mut_good_filtered$NCBI.class, L15_homo_mut_good_filtered$NCBI.phylum)

# Remove rows with NA in NCBI.phylum column
L15_homo_mut_good_filtered <- L15_homo_mut_good_filtered[!is.na(L15_homo_mut_good_filtered$NCBI.phylum) & L15_homo_mut_good_filtered$NCBI.phylum != "NA", ]

Filter dataset to only retain perfects (mutations == 0)

L15_homo_mut_good_filtered_perfects <- L15_homo_mut_good_filtered %>%
  filter(mutations == 0)

length(unique(L15_homo_mut_good_filtered_perfects$ID))
[1] 412
# Sum the number of rows for each unique NCBI.phylum
L15_homo_mut_good_filtered_perfects_phylum_counts <- L15_homo_mut_good_filtered_perfects %>%
  group_by(NCBI.phylum) %>%
  summarise(count = n()) %>%
  arrange(desc(count))

# Print the table
print(L15_homo_mut_good_filtered_perfects_phylum_counts)

Phylum Datasets

Subset by phylum to include perfects (mutations == 0) and all mutants (up to 5 AA distance):

# Alphaproteobacteria (142 Variants)
Alphaproteobacteria <- L15_homo_mut_good_filtered[L15_homo_mut_good_filtered$NCBI.phylum == "Alphaproteobacteria" & !is.na(L15_homo_mut_good_filtered$NCBI.phylum), ]

# Betaproteobacteria (272 Variants)
Betaproteobacteria <- L15_homo_mut_good_filtered[L15_homo_mut_good_filtered$NCBI.phylum == "Betaproteobacteria" & !is.na(L15_homo_mut_good_filtered$NCBI.phylum), ]

# Gammaproteobacteria (1593 Variants)
Gammaproteobacteria <- L15_homo_mut_good_filtered[L15_homo_mut_good_filtered$NCBI.phylum == "Gammaproteobacteria" & !is.na(L15_homo_mut_good_filtered$NCBI.phylum), ]

# Bacillota (3623 Variants)
Bacillota <- L15_homo_mut_good_filtered[L15_homo_mut_good_filtered$NCBI.phylum == "Bacillota" & !is.na(L15_homo_mut_good_filtered$NCBI.phylum), ]

# Bacteroidota (1247 Variants)
Bacteroidota <- L15_homo_mut_good_filtered[L15_homo_mut_good_filtered$NCBI.phylum == "Bacteroidota" & !is.na(L15_homo_mut_good_filtered$NCBI.phylum), ]

Fitness Line Plots

Alphaproteobacteria

# Prepare the main data and remove rows with NA values in fitness conditions or NCBI.species
Alphaproteobacteria.data <- Alphaproteobacteria %>%
  select(mutID, mutations, NCBI.species, fitD05D03, fitD06D03, fitD07D03, fitD08D03, fitD09D03, fitD10D03, fitD11D03) %>%
  filter(!is.na(NCBI.species)) %>%  # Remove rows with NA in NCBI.species first
  filter(!is.na(fitD11D03)) %>%  # Remove rows with NA in fitD11D03
  pivot_longer(cols = starts_with("fitD"), names_to = "Condition", values_to = "Fitness") %>%
  filter(!is.na(Fitness))  # Remove rows with NA in Fitness

# Create a dataframe to determine the color for each mutID
color_assignment <- Alphaproteobacteria %>%
  select(mutID, mutations, NCBI.species, fitD11D03) %>%
  filter(!is.na(NCBI.species)) %>%  # Ensure no NA values for species
  filter(!is.na(fitD11D03)) %>%  # Ensure no NA values for fitD11D03
  distinct() %>%  # Ensure unique entries for color assignment
  mutate(color = case_when(
    mutations == 0 ~ "red",
    mutations != 0 & fitD11D03 < -1 ~ "darkblue",
    mutations != 0 & fitD11D03 > -1 ~ "gold",
    TRUE ~ "gray"  # Default color for other cases
  ))

# Join the color assignment to the main data
Alphaproteobacteria.data <- Alphaproteobacteria.data %>%
  left_join(color_assignment, by = c("mutID", "NCBI.species")) 

# Filter out any rows where color is NA after joining
Alphaproteobacteria.data <- Alphaproteobacteria.data %>%
  filter(!is.na(color), !is.na(NCBI.species))  # Ensure no NA values in color or species

# Create a subset for reference lines (mutations == 0)
reference_data <- Alphaproteobacteria %>%
  select(mutID, mutations, NCBI.species, fitD11D03) %>%
  filter(mutations == 0) %>%
  filter(!is.na(fitD11D03)) %>%  # Ensure no NA values for fitD11D03
  pivot_longer(cols = starts_with("fitD"), names_to = "Condition", values_to = "Fitness") %>%
  filter(!is.na(Fitness))  # Ensure Fitness is not NA

# Final check to remove any empty species from reference_data before plotting
reference_data <- reference_data %>%
  filter(NCBI.species != "") # Remove empty species if necessary

# Plot the line graphs
Alphaproteobacteria.plot <- ggplot(Alphaproteobacteria.data, 
                             aes(x = Condition, y = Fitness, 
                                 group = mutID, color = color)) +
  geom_line(linewidth = 0.75) +          # Plot mutant lines
  geom_point(size = 1) +                 # Plot points for visibility
  geom_line(data = reference_data, aes(x = Condition, y = Fitness), 
            color = "red", linewidth = 1.25) + # Plot reference lines on top
  facet_wrap(~NCBI.species, ncol = 2, scales = "free_y") + 
  theme_bw() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), # Slant x-axis labels
        legend.position = "none",
        plot.title = element_text(hjust = 0.5),
        strip.background = element_rect(fill = "gray90"),  
        strip.text = element_text(face = "plain"),
        panel.grid.major = element_blank(),     # Remove major grid lines
        panel.grid.minor = element_blank()) +   # Remove minor grid lines
  scale_x_discrete(labels = c("fitD05D03" = "0-TMP",
                              "fitD06D03" = "0.058-TMP",
                              "fitD07D03" = "0.5-TMP",
                              "fitD08D03" = "1.0-TMP",
                              "fitD09D03" = "10-TMP",
                              "fitD10D03" = "50-TMP",
                              "fitD11D03" = "200-TMP")) +
  scale_color_identity() +                # Use the color values directly
  geom_hline(yintercept = -1, linetype = "dashed", color = "red") +
  ggtitle("Alphaproteobacteria Resistance to Trimethoprim") +
  labs(x = "TMP Concentration", y = "Fitness")

# Display the plot
print(Alphaproteobacteria.plot)

Bacillota

# Prepare the main data and remove rows with NA values in fitness conditions or NCBI.species
Bacillota.data <- Bacillota %>%
  select(mutID, mutations, NCBI.species, fitD05D03, fitD06D03, fitD07D03, fitD08D03, fitD09D03, fitD10D03, fitD11D03) %>%
  filter(!is.na(NCBI.species)) %>%  # Remove rows with NA in NCBI.species first
  filter(!is.na(fitD11D03)) %>%  # Remove rows with NA in fitD11D03
  pivot_longer(cols = starts_with("fitD"), names_to = "Condition", values_to = "Fitness") %>%
  filter(!is.na(Fitness))  # Remove rows with NA in Fitness

# Create a dataframe to determine the color for each mutID
color_assignment <- Bacillota %>%
  select(mutID, mutations, NCBI.species, fitD11D03) %>%
  filter(!is.na(NCBI.species)) %>%  # Ensure no NA values for species
  filter(!is.na(fitD11D03)) %>%  # Ensure no NA values for fitD11D03
  distinct() %>%  # Ensure unique entries for color assignment
  mutate(color = case_when(
    mutations == 0 ~ "red",
    mutations != 0 & fitD11D03 < -1 ~ "darkblue",
    mutations != 0 & fitD11D03 > -1 ~ "gold",
    TRUE ~ "gray"  # Default color for other cases
  ))

# Join the color assignment to the main data
Bacillota.data <- Bacillota.data %>%
  left_join(color_assignment, by = c("mutID", "NCBI.species")) 

# Filter out any rows where color is NA after joining
Bacillota.data <- Bacillota.data %>%
  filter(!is.na(color), !is.na(NCBI.species))  # Ensure no NA values in color or species

# Create a subset for reference lines (mutations == 0)
reference_data <- Bacillota %>%
  select(mutID, mutations, NCBI.species, fitD11D03) %>%
  filter(mutations == 0) %>%
  filter(!is.na(fitD11D03)) %>%  # Ensure no NA values for fitD11D03
  pivot_longer(cols = starts_with("fitD"), names_to = "Condition", values_to = "Fitness") %>%
  filter(!is.na(Fitness))  # Ensure Fitness is not NA

# Final check to remove any empty species from reference_data before plotting
reference_data <- reference_data %>%
  filter(NCBI.species != "") # Remove empty species if necessary

# Plot the line graphs
Bacillota.plot <- ggplot(Bacillota.data, 
                             aes(x = Condition, y = Fitness, 
                                 group = mutID, color = color)) +
  geom_line(linewidth = 0.75) +          # Plot mutant lines
  geom_point(size = 1) +                 # Plot points for visibility
  geom_line(data = reference_data, aes(x = Condition, y = Fitness), 
            color = "red", linewidth = 1.25) + # Plot reference lines on top
  facet_wrap(~NCBI.species, ncol = 8, scales = "free_y") + 
  theme_bw() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), # Slant x-axis labels
        legend.position = "none",
        plot.title = element_text(hjust = 0.5),
        strip.background = element_rect(fill = "gray90"),  
        strip.text = element_text(face = "plain"),
        panel.grid.major = element_blank(),     # Remove major grid lines
        panel.grid.minor = element_blank()) +   # Remove minor grid lines
  scale_x_discrete(labels = c("fitD05D03" = "0-TMP",
                              "fitD06D03" = "0.058-TMP",
                              "fitD07D03" = "0.5-TMP",
                              "fitD08D03" = "1.0-TMP",
                              "fitD09D03" = "10-TMP",
                              "fitD10D03" = "50-TMP",
                              "fitD11D03" = "200-TMP")) +
  scale_color_identity() +                # Use the color values directly
  geom_hline(yintercept = -1, linetype = "dashed", color = "red") +
  ggtitle("Bacillota Resistance to Trimethoprim") +
  labs(x = "TMP Concentration", y = "Fitness")

# Display the plot
print(Bacillota.plot)

Bacteroidota

# Prepare the main data and remove rows with NA values in fitness conditions or NCBI.species
Bacteroidota.data <- Bacteroidota %>%
  select(mutID, mutations, NCBI.species, fitD05D03, fitD06D03, fitD07D03, fitD08D03, fitD09D03, fitD10D03, fitD11D03) %>%
  filter(!is.na(NCBI.species)) %>%  # Remove rows with NA in NCBI.species first
  filter(!is.na(fitD11D03)) %>%  # Remove rows with NA in fitD11D03
  pivot_longer(cols = starts_with("fitD"), names_to = "Condition", values_to = "Fitness") %>%
  filter(!is.na(Fitness))  # Remove rows with NA in Fitness

# Create a dataframe to determine the color for each mutID
color_assignment <- Bacteroidota %>%
  select(mutID, mutations, NCBI.species, fitD11D03) %>%
  filter(!is.na(NCBI.species)) %>%  # Ensure no NA values for species
  filter(!is.na(fitD11D03)) %>%  # Ensure no NA values for fitD11D03
  distinct() %>%  # Ensure unique entries for color assignment
  mutate(color = case_when(
    mutations == 0 ~ "red",
    mutations != 0 & fitD11D03 < -1 ~ "darkblue",
    mutations != 0 & fitD11D03 > -1 ~ "gold",
    TRUE ~ "gray"  # Default color for other cases
  ))

# Join the color assignment to the main data
Bacteroidota.data <- Bacteroidota.data %>%
  left_join(color_assignment, by = c("mutID", "NCBI.species")) 

# Filter out any rows where color is NA after joining
Bacteroidota.data <- Bacteroidota.data %>%
  filter(!is.na(color), !is.na(NCBI.species))  # Ensure no NA values in color or species

# Create a subset for reference lines (mutations == 0)
reference_data <- Bacteroidota %>%
  select(mutID, mutations, NCBI.species, fitD11D03) %>%
  filter(mutations == 0) %>%
  filter(!is.na(fitD11D03)) %>%  # Ensure no NA values for fitD11D03
  pivot_longer(cols = starts_with("fitD"), names_to = "Condition", values_to = "Fitness") %>%
  filter(!is.na(Fitness))  # Ensure Fitness is not NA

# Final check to remove any empty species from reference_data before plotting
reference_data <- reference_data %>%
  filter(NCBI.species != "") # Remove empty species if necessary

# Plot the line graphs
Bacteroidota.plot <- ggplot(Bacteroidota.data, 
                             aes(x = Condition, y = Fitness, 
                                 group = mutID, color = color)) +
  geom_line(linewidth = 0.75) +          # Plot mutant lines
  geom_point(size = 1) +                 # Plot points for visibility
  geom_line(data = reference_data, aes(x = Condition, y = Fitness), 
            color = "red", linewidth = 1.25) + # Plot reference lines on top
  facet_wrap(~NCBI.species, ncol = 7, scales = "free_y") + 
  theme_bw() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), # Slant x-axis labels
        legend.position = "none",
        plot.title = element_text(hjust = 0.5),
        strip.background = element_rect(fill = "gray90"),  
        strip.text = element_text(face = "plain"),
        panel.grid.major = element_blank(),     # Remove major grid lines
        panel.grid.minor = element_blank()) +   # Remove minor grid lines
  scale_x_discrete(labels = c("fitD05D03" = "0-TMP",
                              "fitD06D03" = "0.058-TMP",
                              "fitD07D03" = "0.5-TMP",
                              "fitD08D03" = "1.0-TMP",
                              "fitD09D03" = "10-TMP",
                              "fitD10D03" = "50-TMP",
                              "fitD11D03" = "200-TMP")) +
  scale_color_identity() +                # Use the color values directly
  geom_hline(yintercept = -1, linetype = "dashed", color = "red") +
  ggtitle("Bacteroidota Resistance to Trimethoprim") +
  labs(x = "TMP Concentration", y = "Fitness")

# Display the plot
print(Bacteroidota.plot)

Betaproteobacteria

# Prepare the main data and remove rows with NA values in fitness conditions or NCBI.species
Betaproteobacteria.data <- Betaproteobacteria %>%
  select(mutID, mutations, NCBI.species, fitD05D03, fitD06D03, fitD07D03, fitD08D03, fitD09D03, fitD10D03, fitD11D03) %>%
  filter(!is.na(NCBI.species)) %>%  # Remove rows with NA in NCBI.species first
  filter(!is.na(fitD11D03)) %>%  # Remove rows with NA in fitD11D03
  pivot_longer(cols = starts_with("fitD"), names_to = "Condition", values_to = "Fitness") %>%
  filter(!is.na(Fitness))  # Remove rows with NA in Fitness

# Create a dataframe to determine the color for each mutID
color_assignment <- Betaproteobacteria %>%
  select(mutID, mutations, NCBI.species, fitD11D03) %>%
  filter(!is.na(NCBI.species)) %>%  # Ensure no NA values for species
  filter(!is.na(fitD11D03)) %>%  # Ensure no NA values for fitD11D03
  distinct() %>%  # Ensure unique entries for color assignment
  mutate(color = case_when(
    mutations == 0 ~ "red",
    mutations != 0 & fitD11D03 < -1 ~ "darkblue",
    mutations != 0 & fitD11D03 > -1 ~ "gold",
    TRUE ~ "gray"  # Default color for other cases
  ))

# Join the color assignment to the main data
Betaproteobacteria.data <- Betaproteobacteria.data %>%
  left_join(color_assignment, by = c("mutID", "NCBI.species")) 

# Filter out any rows where color is NA after joining
Betaproteobacteria.data <- Betaproteobacteria.data %>%
  filter(!is.na(color), !is.na(NCBI.species))  # Ensure no NA values in color or species

# Create a subset for reference lines (mutations == 0)
reference_data <- Betaproteobacteria %>%
  select(mutID, mutations, NCBI.species, fitD11D03) %>%
  filter(mutations == 0) %>%
  filter(!is.na(fitD11D03)) %>%  # Ensure no NA values for fitD11D03
  pivot_longer(cols = starts_with("fitD"), names_to = "Condition", values_to = "Fitness") %>%
  filter(!is.na(Fitness))  # Ensure Fitness is not NA

# Final check to remove any empty species from reference_data before plotting
reference_data <- reference_data %>%
  filter(NCBI.species != "") # Remove empty species if necessary

# Plot the line graphs
Betaproteobacteria.plot <- ggplot(Betaproteobacteria.data, 
                             aes(x = Condition, y = Fitness, 
                                 group = mutID, color = color)) +
  geom_line(linewidth = 0.75) +          # Plot mutant lines
  geom_point(size = 1) +                 # Plot points for visibility
  geom_line(data = reference_data, aes(x = Condition, y = Fitness), 
            color = "red", linewidth = 1.25) + # Plot reference lines on top
  facet_wrap(~NCBI.species, ncol = 3, scales = "free_y") + 
  theme_bw() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), # Slant x-axis labels
        legend.position = "none",
        plot.title = element_text(hjust = 0.5),
        strip.background = element_rect(fill = "gray90"),  
        strip.text = element_text(face = "plain"),
        panel.grid.major = element_blank(),     # Remove major grid lines
        panel.grid.minor = element_blank()) +   # Remove minor grid lines
  scale_x_discrete(labels = c("fitD05D03" = "0-TMP",
                              "fitD06D03" = "0.058-TMP",
                              "fitD07D03" = "0.5-TMP",
                              "fitD08D03" = "1.0-TMP",
                              "fitD09D03" = "10-TMP",
                              "fitD10D03" = "50-TMP",
                              "fitD11D03" = "200-TMP")) +
  scale_color_identity() +                # Use the color values directly
  geom_hline(yintercept = -1, linetype = "dashed", color = "red") +
  ggtitle("Betaproteobacteria Resistance to Trimethoprim") +
  labs(x = "TMP Concentration", y = "Fitness")

# Display the plot
print(Betaproteobacteria.plot)

Gammaproteobacteria

# Prepare the main data and remove rows with NA values in fitness conditions or NCBI.species
Gammaproteobacteria.data <- Gammaproteobacteria %>%
  select(mutID, mutations, NCBI.species, fitD05D03, fitD06D03, fitD07D03, fitD08D03, fitD09D03, fitD10D03, fitD11D03) %>%
  filter(!is.na(NCBI.species)) %>%  # Remove rows with NA in NCBI.species first
  filter(!is.na(fitD11D03)) %>%  # Remove rows with NA in fitD11D03
  pivot_longer(cols = starts_with("fitD"), names_to = "Condition", values_to = "Fitness") %>%
  filter(!is.na(Fitness))  # Remove rows with NA in Fitness

# Create a dataframe to determine the color for each mutID
color_assignment <- Gammaproteobacteria %>%
  select(mutID, mutations, NCBI.species, fitD11D03) %>%
  filter(!is.na(NCBI.species)) %>%  # Ensure no NA values for species
  filter(!is.na(fitD11D03)) %>%  # Ensure no NA values for fitD11D03
  distinct() %>%  # Ensure unique entries for color assignment
  mutate(color = case_when(
    mutations == 0 ~ "red",
    mutations != 0 & fitD11D03 < -1 ~ "darkblue",
    mutations != 0 & fitD11D03 > -1 ~ "gold",
    TRUE ~ "gray"  # Default color for other cases
  ))

# Join the color assignment to the main data
Gammaproteobacteria.data <- Gammaproteobacteria.data %>%
  left_join(color_assignment, by = c("mutID", "NCBI.species")) 

# Filter out any rows where color is NA after joining
Gammaproteobacteria.data <- Gammaproteobacteria.data %>%
  filter(!is.na(color), !is.na(NCBI.species))  # Ensure no NA values in color or species

# Create a subset for reference lines (mutations == 0)
reference_data <- Gammaproteobacteria %>%
  select(mutID, mutations, NCBI.species, fitD11D03) %>%
  filter(mutations == 0) %>%
  filter(!is.na(fitD11D03)) %>%  # Ensure no NA values for fitD11D03
  pivot_longer(cols = starts_with("fitD"), names_to = "Condition", values_to = "Fitness") %>%
  filter(!is.na(Fitness))  # Ensure Fitness is not NA

# Final check to remove any empty species from reference_data before plotting
reference_data <- reference_data %>%
  filter(NCBI.species != "") # Remove empty species if necessary

# Plot the line graphs
Gammaproteobacteria.plot <- ggplot(Gammaproteobacteria.data, 
                             aes(x = Condition, y = Fitness, 
                                 group = mutID, color = color)) +
  geom_line(linewidth = 0.75) +          # Plot mutant lines
  geom_point(size = 1) +                 # Plot points for visibility
  geom_line(data = reference_data, aes(x = Condition, y = Fitness), 
            color = "red", linewidth = 1.25) + # Plot reference lines on top
  facet_wrap(~NCBI.species, ncol = 5, scales = "free_y") + 
  theme_bw() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), # Slant x-axis labels
        legend.position = "none",
        plot.title = element_text(hjust = 0.5),
        strip.background = element_rect(fill = "gray90"),  
        strip.text = element_text(face = "plain"),
        panel.grid.major = element_blank(),     # Remove major grid lines
        panel.grid.minor = element_blank()) +   # Remove minor grid lines
  scale_x_discrete(labels = c("fitD05D03" = "0-TMP",
                              "fitD06D03" = "0.058-TMP",
                              "fitD07D03" = "0.5-TMP",
                              "fitD08D03" = "1.0-TMP",
                              "fitD09D03" = "10-TMP",
                              "fitD10D03" = "50-TMP",
                              "fitD11D03" = "200-TMP")) +
  scale_color_identity() +                # Use the color values directly
  geom_hline(yintercept = -1, linetype = "dashed", color = "red") +
  ggtitle("Gammaproteobacteria Resistance to Trimethoprim") +
  labs(x = "TMP Concentration", y = "Fitness")

# Display the plot
print(Gammaproteobacteria.plot)

Genus Case-Studies

Now, create separate dataframes for each Genus of interest:

# Acinetobacter
Acinetobacter <- L15_homo_mut_good_filtered[L15_homo_mut_good_filtered$NCBI.genus == "Acinetobacter" & !is.na(L15_homo_mut_good_filtered$NCBI.genus), ]

# Bacillus
Bacillus <- L15_homo_mut_good_filtered[L15_homo_mut_good_filtered$NCBI.genus == "Bacillus" & !is.na(L15_homo_mut_good_filtered$NCBI.genus), ]

# Bacteroides
Bacteroides <- L15_homo_mut_good_filtered[L15_homo_mut_good_filtered$NCBI.genus == "Bacteroides" & !is.na(L15_homo_mut_good_filtered$NCBI.genus), ]

# Chlamydia
Chlamydia <- L15_homo_mut_good_filtered[L15_homo_mut_good_filtered$NCBI.genus == "Chlamydia" & !is.na(L15_homo_mut_good_filtered$NCBI.genus), ]

# Clostridium
Clostridium <- L15_homo_mut_good_filtered[L15_homo_mut_good_filtered$NCBI.genus == "Clostridium" & !is.na(L15_homo_mut_good_filtered$NCBI.genus), ]

# Escherichia
Escherichia <- L15_homo_mut_good_filtered[L15_homo_mut_good_filtered$NCBI.genus == "Escherichia" & !is.na(L15_homo_mut_good_filtered$NCBI.genus), ]

# Neisseria
Neisseria <- L15_homo_mut_good_filtered[L15_homo_mut_good_filtered$NCBI.genus == "Neisseria" & !is.na(L15_homo_mut_good_filtered$NCBI.genus), ]

# Pseudomonas
Pseudomonas <- L15_homo_mut_good_filtered[L15_homo_mut_good_filtered$NCBI.genus == "Pseudomonas" & !is.na(L15_homo_mut_good_filtered$NCBI.genus), ]

# Streptococcus
Streptococcus <- L15_homo_mut_good_filtered[L15_homo_mut_good_filtered$NCBI.genus == "Streptococcus" & !is.na(L15_homo_mut_good_filtered$NCBI.genus), ]

Streptococcus Fitness

Fitness line plot for each Streptococcus species (Bacillota)

Streptococcus.data <- Streptococcus %>%
  filter(mutations == 0) %>%
  select(ID, NCBI.species, fitD05D03, fitD06D03, fitD07D03, fitD08D03, fitD09D03, fitD10D03, fitD11D03) %>%
  pivot_longer(cols = starts_with("fitD"), names_to = "Condition", values_to = "Fitness")

# Plot the line graphs
Streptococcus.plot <- ggplot(Streptococcus.data, 
                             aes(x = Condition, y = Fitness, 
                                 group = ID, color = NCBI.species)) +
  geom_line(linewidth = 0.75) +
  facet_wrap(~NCBI.species, ncol = 4, scales = "free_y") + 
  theme_bw() +
  theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5),
        legend.position = "none",
        plot.title = element_text(hjust = 0.5)) +
  scale_x_discrete(labels = c("fitD05D03" = "0-TMP",
                              "fitD06D03" = "0.058-TMP",
                              "fitD07D03" = "0.5-TMP",
                              "fitD08D03" = "1.0-TMP",
                              "fitD09D03" = "10-TMP",
                              "fitD10D03" = "50-TMP",
                              "fitD11D03" = "200-TMP")) +
  geom_hline(yintercept = -1, linetype = "dashed", color = "red") +
  ggtitle("Streptococcus Resistance to Trimethoprim") +
  guides(color = guide_legend(ncol = 1)) +
  labs(color = "Species (ID)", x = "TMP Concentration", y = "Fitness")

# Display the plot
print(Streptococcus.plot)

Pathogenic Taxa

Subset the fitness scores across the TMP gradient for a select set of known pathogenic bacterial taxa.

# Subset the pathogenic variants of interest for plotting
pathogenic_mutID <- c("NP_414590", "WP_000175742", "WP_011272274", "WP_000973544", "WP_002205327", "WP_000624384", "NP_831957", "WP_004836669")

# Create the subset dataframe
pathogenic_data <- L15_homo_mut_good_filtered %>%
  filter(mutID %in% pathogenic_mutID)

Plot the change in fitness across the TMP gradient for a select set of known pathogenic bacterial taxa.

# Reshape the data and rename mutIDs to species names
pathogenic_data_long <- pathogenic_data %>%
  pivot_longer(cols = starts_with("fitD"), 
               names_to = "Day", 
               values_to = "Fitness",
               names_prefix = "fitD") %>%
  mutate(Day = factor(Day, levels = c("05D03", "06D03", "07D03", "08D03", "09D03", "10D03", "11D03"),
                      labels = c("0", "0.058", "0.5", "1.0", "10", "50", "200"))) %>%
  mutate(Species = NCBI.species)  # Create a new column for species names

# Generate a color palette with enough colors for all species
n_colors <- length(unique(pathogenic_data_long$Species))
color_palette <- colorRampPalette(brewer.pal(8, "Dark2"))(n_colors)
# Create the plot
pathogenic_plot <- ggplot(pathogenic_data_long, aes(x = Day, y = Fitness, group = Species, color = Species)) +
  geom_hline(yintercept = -1, linetype = "dashed", color = "gray50") +
  geom_line(size = 1.0) +
  geom_point(size = 3) +
  scale_color_manual(values = setNames(color_palette, unique(pathogenic_data_long$Species))) +
  labs(x = "Trimethoprim (ug/mL)",
       y = "Median Fitness (LogFC)") +
  theme_minimal() +
  theme(
    axis.line = element_line(colour = 'black', size = 1.0),
    axis.ticks = element_line(colour = "black", size = 1.0),
    axis.text.x = element_text(size = 12, angle = 45, hjust = 1),
    axis.text.y = element_text(size = 12),
    axis.title.x = element_text(size = 14),
    axis.title.y = element_text(size = 14),
    panel.background = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    legend.position = "right",
    legend.title = element_blank(),
    legend.text = element_text(size = 14)) +
  ggtitle("Pathogenic Homologs") + 
  theme(plot.title = element_text(hjust = 0.5, size = 16))

# Print the plot
print(pathogenic_plot)

# PNG
ggsave(file="Resistance/PLOTS/Final/Pathogenic.Fitness.png", 
       plot=pathogenic_plot,
       dpi=600, width = 8, height = 5, units = "in")

Taxa Alignments

Generate FASTA files for phylogenetic taxa of interest and perform a multiple sequence alignment to determine differences in AA positions influencing TMP resistance across the concentration gradient.

Escherichia coli

# Filter to retain Escherichia coli (NCBI.species) from L15_homo_mut_good_filtered
Escherichia <- L15_homo_mut_good_filtered[L15_homo_mut_good_filtered$NCBI.species == "Escherichia coli" & 
                                            !is.na(L15_homo_mut_good_filtered$NCBI.species), ]

# Filter the dataframe for rows where mutations == 0
Escherichia_filtered <- Escherichia[Escherichia$mutations == 0, ]

# Create a function to write FASTA entries
write_fasta <- function(id, seq, file) {
  cat(paste0(">", id, "\n"), file = file, append = TRUE)
  cat(paste0("M", seq, "\n"), file = file, append = TRUE)  # Add "M" at the beginning of the sequence
}

# Specify the output file name
Escherichia_output_file <- "Resistance/FASTA/Escherichia.fasta"

# Remove the file if it already exists
if (file.exists(Escherichia_output_file)) {
  file.remove(Escherichia_output_file)
}
[1] TRUE
# Write the FASTA entries to the file
for (i in 1:nrow(Escherichia_filtered)) {
  write_fasta(Escherichia_filtered$mutID[i], Escherichia_filtered$seq[i], Escherichia_output_file)
}

cat("FASTA file has been generated:", Escherichia_output_file, "\n")
FASTA file has been generated: Resistance/FASTA/Escherichia.fasta 

Perform a multiple sequence alignment on the FASTA file using CLUSTAL Omega:

# May need to enable permissions to run the executable:
#chmod +x clustalo
./Scripts/clustalo -i Resistance/FASTA/Escherichia.fasta -o Resistance/FASTA/Escherichia.aligned.fasta --outfmt=fa --force

Import the MSA file and determine which amino acid positions differ between the samples:

# Import the MSA file
msa <- readAAMultipleAlignment("Resistance/FASTA/Escherichia.aligned.fasta", format="fasta")

# Convert to a matrix for easier manipulation
msa_matrix <- as.matrix(msa)

# Set the reference sequence
ref_seq <- "NP_414590"

cat("Using reference sequence:", ref_seq, "\n")
Using reference sequence: NP_414590 
# Find positions where amino acids differ
diff_positions <- which(apply(msa_matrix, 2, function(col) length(unique(col)) > 1))

# Create a summary dataframe
summary_df <- data.frame(
  Position = diff_positions,
  RefAA = msa_matrix[ref_seq, diff_positions],
  stringsAsFactors = FALSE
)

# Add columns for each unique mutID
unique_mutIDs <- setdiff(rownames(msa_matrix), ref_seq)
for (mutID in unique_mutIDs) {
  summary_df[[mutID]] <- msa_matrix[mutID, diff_positions]
}

# Add a column for the amino acid changes
summary_df$Changes <- apply(summary_df, 1, function(row) {
  changes <- setdiff(as.character(row[3:ncol(summary_df)]), row["RefAA"])
  if (length(changes) > 0) {
    paste(row["RefAA"], paste(changes, collapse = "/"), sep = "->")
  } else {
    "No change"
  }
})

# Remove rows with "No change"
summary_df <- summary_df[summary_df$Changes != "No change", ]

# Reorder columns
summary_df <- summary_df %>% 
  select(Position, RefAA, Changes, everything())

# Print the summary table
print(summary_df, row.names = FALSE)

Heatmap for amino acid mutations by position based on reference sequence:

# Get all positions where there's a mutation
all_positions <- sort(unique(summary_df$Position))

# Reshape the data
heatmap_data <- summary_df %>%
  select(-Changes) %>%
  pivot_longer(cols = -c(Position, RefAA), 
               names_to = "mutID", 
               values_to = "AA") %>%
  mutate(Position = as.numeric(Position))

# Add the reference sequence
ref_data <- summary_df %>%
  select(Position, RefAA) %>%
  mutate(mutID = "NP_414590", AA = RefAA)

heatmap_data <- bind_rows(ref_data, heatmap_data)

# Create a function to compare amino acids
compare_aa <- function(aa, ref_aa) {
  ifelse(is.na(aa) | aa == "", "No data",
         ifelse(aa == ref_aa, "No change", "Mutation"))
}

# Apply the comparison function
heatmap_data <- heatmap_data %>%
  group_by(Position) %>%
  mutate(Comparison = compare_aa(AA, AA[mutID == "NP_220129"])) %>%
  ungroup()

# Create a continuous position variable
heatmap_data <- heatmap_data %>%
  arrange(Position) %>%
  mutate(PositionIndex = as.numeric(factor(Position, levels = unique(Position))))

# Create the heatmap
Escherichia.heatmap <- ggplot(heatmap_data, aes(x = PositionIndex, y = mutID, fill = Comparison)) +
  geom_tile(color = "white", width = 1, height = 0.9) +
  scale_fill_manual(values = c("No change" = "grey90", "Mutation" = "red", "No data" = "white")) +
  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5, size = 8),
    axis.text.y = element_text(size = 8),
    axis.title.y = element_blank(),
    panel.grid = element_blank(),
    panel.border = element_rect(fill = NA, color = "black", size = 0.5),
    legend.position = "bottom"
  ) +
  labs(x = "Position", fill = "Amino Acid Change") +
  scale_x_continuous(
    breaks = heatmap_data$PositionIndex,
    labels = heatmap_data$Position,
    expand = c(0, 0)
  ) +
  scale_y_discrete(expand = c(0, 0)) +
  geom_text(aes(label = AA), size = 3, na.rm = TRUE) +
  scale_y_discrete(limits = rev(unique(heatmap_data$mutID)))  # Reverse y-axis to put reference on top
Warning: The `size` argument of `element_rect()` is deprecated as of ggplot2 3.4.0.
Please use the `linewidth` argument instead.Scale for y is already present.
Adding another scale for y, which will replace the existing scale.
#print(Escherichia.heatmap)

Plot the change in fitness across the TMP gradient for both samples:

# Reshape the data from wide to long format
Escherichia_long <- Escherichia_filtered %>%
  pivot_longer(cols = starts_with("fitD"), 
               names_to = "Day", 
               values_to = "Fitness",
               names_prefix = "fitD") %>%
  mutate(Day = factor(Day, levels = c("05D03", "06D03", "07D03", "08D03", "09D03", "10D03", "11D03"),
                      labels = c("0", "0.058", "0.5", "1.0", "10", "50", "200")))

# Create a new column for coloring
Escherichia_long <- Escherichia_long %>%
  mutate(Color = ifelse(mutID == "NP_414590", "WT", "Mutant"))

# Create the plot
Escherichia_plot <- ggplot(Escherichia_long, aes(x = Day, y = Fitness, group = mutID)) +
  geom_hline(yintercept = -1, linetype = "dashed", color = "gray50") +
  geom_line(aes(color = Color), size = 1.0) +
  geom_point(aes(color = Color), size = 3) +
  scale_color_manual(values = c("WT" = "red", "Mutant" = "gold2")) +
  labs(x = "Trimethoprim (ug/mL)",
       y = "Median Fitness (LogFC)") +
  theme_minimal() +
  theme(
    axis.line = element_line(colour = 'black', size = 1.0),
    axis.ticks = element_line(colour = "black", size = 1.0),
    axis.text.x = element_text(size = 12, angle = 45, hjust = 1),
    axis.text.y = element_text(size = 12),
    axis.title.x = element_text(size = 14),
    axis.title.y = element_text(size = 14),
    panel.background = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    legend.position = "none",
    legend.title = element_blank(),
    legend.text = element_text(size = 8)) +
  ggtitle("Escherichia coli") + 
  theme(plot.title = element_text(hjust = 0.5, face = "italic", size = 16))

# Print the plot
print(Escherichia_plot)

Streptococcus pneumoniae

# Filter to retain Escherichia coli (NCBI.species) from L15_homo_mut_good_filtered
Streptococcus <- L15_homo_mut_good_filtered[L15_homo_mut_good_filtered$NCBI.species == "Streptococcus pneumoniae" & 
                                            !is.na(L15_homo_mut_good_filtered$NCBI.species), ]

# Filter to only retain specific variants for the MSA
Streptococcus_filtered <- Streptococcus %>%
  filter((mutID == "WP_002205327" | ID == "WP_002205327") & !is.na(fitD11D03))

# Create a csv copy of the filtered dataset
write.csv(Streptococcus_filtered, file = "Resistance/Streptococcus.fitness.csv", row.names = FALSE)

# Create a function to write FASTA entries
write_fasta <- function(id, seq, file) {
  cat(paste0(">", id, "\n"), file = file, append = TRUE)
  cat(paste0("M", seq, "\n"), file = file, append = TRUE)  # Add "M" at the beginning of the sequence
}

# Specify the output file name
Streptococcus_output_file <- "Resistance/FASTA/Streptococcus.fasta"

# Remove the file if it already exists
if (file.exists(Streptococcus_output_file)) {
  file.remove(Streptococcus_output_file)
}
[1] TRUE
# Write the FASTA entries to the file
for (i in 1:nrow(Streptococcus_filtered)) {
  write_fasta(Streptococcus_filtered$mutID[i], Streptococcus_filtered$seq[i], Streptococcus_output_file)
}

cat("FASTA file has been generated:", Streptococcus_output_file, "\n")
FASTA file has been generated: Resistance/FASTA/Streptococcus.fasta 

Perform a multiple sequence alignment on the FASTA file using CLUSTAL Omega:

# May need to enable permissions to run the executable:
#chmod +x clustalo
./Scripts/clustalo -i Resistance/FASTA/Streptococcus.fasta -o Resistance/FASTA/Streptococcus.aligned.fasta --outfmt=fa --force

Import the MSA file and determine which amino acid positions differ between the samples

# Import the MSA file
msa <- readAAMultipleAlignment("Resistance/FASTA/Streptococcus.aligned.fasta", format="fasta")

# Convert to a matrix for easier manipulation
msa_matrix <- as.matrix(msa)

# Set the reference sequence
ref_seq <- "WP_002205327"

cat("Using reference sequence:", ref_seq, "\n")
Using reference sequence: WP_002205327 
# Find positions where amino acids differ
diff_positions <- which(apply(msa_matrix, 2, function(col) length(unique(col)) > 1))

# Create a summary dataframe
summary_df <- data.frame(
  Position = diff_positions,
  RefAA = msa_matrix[ref_seq, diff_positions],
  stringsAsFactors = FALSE
)

# Add columns for each unique mutID
unique_mutIDs <- setdiff(rownames(msa_matrix), ref_seq)
for (mutID in unique_mutIDs) {
  summary_df[[mutID]] <- msa_matrix[mutID, diff_positions]
}

# Add a column for the amino acid changes
summary_df$Changes <- apply(summary_df, 1, function(row) {
  changes <- setdiff(as.character(row[3:ncol(summary_df)]), row["RefAA"])
  if (length(changes) > 0) {
    paste(row["RefAA"], paste(changes, collapse = "/"), sep = "->")
  } else {
    "No change"
  }
})

# Remove rows with "No change"
summary_df <- summary_df[summary_df$Changes != "No change", ]

# Reorder columns
summary_df <- summary_df %>% 
  select(Position, RefAA, Changes, everything())

# Print the summary table
print(summary_df, row.names = FALSE)

Arrange the data for the amino acid mutations heatmap:

# Get all positions where there's a mutation
all_positions <- sort(unique(summary_df$Position))

# Reshape the data
heatmap_data <- summary_df %>%
  select(-Changes) %>%
  pivot_longer(cols = -c(Position, RefAA), 
               names_to = "mutID", 
               values_to = "AA") %>%
  mutate(Position = as.numeric(Position))

# Add the reference sequence
ref_data <- summary_df %>%
  select(Position, RefAA) %>%
  mutate(mutID = "WP_002205327", AA = RefAA)

heatmap_data <- bind_rows(ref_data, heatmap_data)

# Create a function to compare amino acids
compare_aa <- function(aa, ref_aa) {
  ifelse(is.na(aa) | aa == "", "No data",
         ifelse(aa == ref_aa, "No change", "Mutation"))
}

# Apply the comparison function and modify AA display
heatmap_data <- heatmap_data %>%
  group_by(Position) %>%
  mutate(
    Comparison = compare_aa(AA, AA[mutID == "WP_002205327"]),
    DisplayAA = case_when(
      mutID == "WP_002205327" ~ AA,
      Comparison == "Mutation" ~ AA,
      TRUE ~ ""
    )
  ) %>%
  ungroup()

# Create a continuous position variable
heatmap_data <- heatmap_data %>%
  arrange(Position) %>%
  mutate(PositionIndex = as.numeric(factor(Position, levels = unique(Position))))

# Create Streptococcus_long
Streptococcus_long <- Streptococcus_filtered %>%
  pivot_longer(cols = starts_with("fitD"), 
               names_to = "TMP", 
               values_to = "Fitness",
               names_prefix = "fitD")

# Calculate the fitness at the highest TMP concentration for each mutID
max_fitness <- Streptococcus_long %>%
  filter(TMP == "11D03") %>%
  select(mutID, max_fitness = Fitness)

# Join max_fitness information and create MutantColor
heatmap_data <- heatmap_data %>%
  left_join(max_fitness, by = "mutID") %>%
  mutate(MutantColor = case_when(
    mutID == "WP_002205327" ~ "red",
    is.na(max_fitness) ~ "gray",
    max_fitness < -1 ~ "darkblue",
    max_fitness >= -1 ~ "gold2"
  ))

# Create a new column for ordering
heatmap_data <- heatmap_data %>%
  mutate(OrderGroup = case_when(
    mutID == "WP_002205327" ~ 1,
    max_fitness >= -1 ~ 2,
    max_fitness < -1 ~ 3,
    TRUE ~ 4  # for any NA or other cases
  ))

Heatmap for amino acid mutations by position based on reference sequence:

# Order the data with reference on top
heatmap_data <- heatmap_data %>%
  arrange(OrderGroup, desc(max_fitness)) %>%
  mutate(mutID = factor(mutID, levels = unique(mutID)))

# Ensure the colors are assigned correctly
y_colors <- heatmap_data %>%
  distinct(mutID, MutantColor) %>%
  arrange(match(mutID, levels(heatmap_data$mutID)))

# Modify the heatmap_data to include the color information for mutations and text
heatmap_data <- heatmap_data %>%
  mutate(
    MutationColor = case_when(
      Comparison == "Mutation" & max_fitness >= -1 ~ "gold2",
      Comparison == "Mutation" & max_fitness < -1 ~ "darkblue",
      TRUE ~ Comparison
    ),
    TextColor = case_when(
      MutationColor == "darkblue" ~ "white",
      TRUE ~ "black"
    )
  )

# Create the heatmap
Streptococcus.heatmap <- ggplot(heatmap_data, aes(x = PositionIndex, y = mutID, fill = MutationColor)) +
  geom_tile(color = "white", width = 1, height = 0.9) +
  scale_fill_manual(values = c("No change" = "gray90", "gold2" = "gold2", "darkblue" = "darkblue", "No data" = "white")) +
  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5, size = 12),
    axis.text.y = element_text(size = 12),
    axis.title.x = element_text(size = 14),
    axis.title.y = element_blank(),
    panel.grid.major.x = element_line(color = "white", size = 0.5),
    panel.grid.major.y = element_line(color = "white", size = 0.5),
    panel.grid.minor = element_blank(),
    legend.position = "none"
  ) +
  labs(x = "Position", fill = "Amino Acid Change") +
  scale_x_continuous(
    breaks = heatmap_data$PositionIndex,
    labels = heatmap_data$Position,
    expand = c(0, 0)
  ) +
  geom_text(aes(label = DisplayAA, color = TextColor), size = 4, na.rm = TRUE, fontface = "bold") +
  scale_color_identity() +
  scale_y_discrete(limits = rev(levels(heatmap_data$mutID)), expand = c(0, 0))

# Apply correct colors to y-axis labels
Streptococcus.heatmap <- Streptococcus.heatmap +
  theme(axis.text.y = element_text(color = rev(y_colors$MutantColor))) + 
  ggtitle("Streptococcus pneumoniae") + 
  theme(plot.title = element_text(hjust = 0.5, face = "italic", size = 16))
Warning: Vectorized input to `element_text()` is not officially supported.
ℹ Results may be unexpected or may change in future versions of ggplot2.
print(Streptococcus.heatmap)

Plot the change in fitness across the TMP gradient for both samples:

# Reshape the data from wide to long format
Streptococcus_long <- Streptococcus_filtered %>%
  pivot_longer(cols = starts_with("fitD"), 
               names_to = "TMP", 
               values_to = "Fitness",
               names_prefix = "fitD") %>%
  mutate(Day = factor(TMP, levels = c("05D03", "06D03", "07D03", "08D03", "09D03", "10D03", "11D03"),
                      labels = c("0", "0.058", "0.5", "1.0", "10", "50", "200")))

# Calculate the fitness at the highest TMP concentration for each mutID
Streptococcus_max_fitness <- Streptococcus_long %>%
  filter(TMP == "11D03") %>%
  select(mutID, max_fitness = Fitness)

# Join this information back to the original data and color accordingly
Streptococcus_long <- Streptococcus_long %>%
  left_join(Streptococcus_max_fitness, by = "mutID") %>%
  mutate(Color = case_when(
    mutID == "WP_002205327" ~ "WT",
    is.na(max_fitness) ~ "Mutant_Unknown",
    max_fitness < -1 ~ "Mutant_Low",
    max_fitness >= -1 ~ "Mutant_High"
  ))

# Reorder the levels of Color factor to plot WT last
Streptococcus_long$Color <- factor(Streptococcus_long$Color, 
                                   levels = c("Mutant_Unknown", "Mutant_Low", "Mutant_High", "WT"))

# Separate WT and mutant data
WT_data <- Streptococcus_long %>% filter(mutID == "WP_002205327")
mutant_data <- Streptococcus_long %>% filter(mutID != "WP_002205327")

Fitness plotting over the TMP gradient

# Create the plot
Streptococcus_plot <- ggplot() +
  geom_hline(yintercept = -1, linetype = "dashed", color = "gray50") +
  # Plot mutant lines first
  geom_line(data = mutant_data, aes(x = Day, y = Fitness, group = mutID, color = Color), size = 1.0) +
  geom_point(data = mutant_data, aes(x = Day, y = Fitness, color = Color), size = 3) +
  # Plot WT line on top
  geom_line(data = WT_data, aes(x = Day, y = Fitness, group = mutID), color = "red", size = 2.0) +
  geom_point(data = WT_data, aes(x = Day, y = Fitness), color = "red", size = 3) +
  scale_color_manual(values = c("Mutant_Low" = "darkblue", "Mutant_High" = "gold2", "Mutant_Unknown" = "gray")) +
  scale_x_discrete(limits = c("0", "0.058", "0.5", "1.0", "10", "50", "200")) +
  labs(x = "Trimethoprim (ug/mL)",
       y = "Median Fitness (LogFC)") +
  theme_minimal() +
  theme(
    axis.line = element_line(colour = 'black', size = 1.0),
    axis.ticks = element_line(colour = "black", size = 1.0),
    axis.text.x = element_text(size = 12, angle = 45, hjust = 1),
    axis.text.y = element_text(size = 12),
    axis.title.x = element_text(size = 14),
    axis.title.y = element_text(size = 14),
    panel.background = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    legend.position = "none") + 
  ggtitle("Streptococcus pneumoniae") + 
  theme(plot.title = element_text(hjust = 0.5, face = "italic", size = 16)) # Center and italicize the title

# Print the plot
print(Streptococcus_plot)

Plot together:

patch1 <- Streptococcus_plot | Streptococcus.heatmap
patch1

Bacillus cereus

# Filter to retain Escherichia coli (NCBI.species) from L15_homo_mut_good_filtered
Bacillus <- L15_homo_mut_good_filtered[L15_homo_mut_good_filtered$NCBI.species == "Bacillus cereus" & 
                                            !is.na(L15_homo_mut_good_filtered$NCBI.species), ]

# Filter to only retain specific variants for the MSA
Bacillus_filtered <- Bacillus %>%
  filter((mutID == "NP_831957" | ID == "NP_831957") & !is.na(fitD11D03))

# Write the data frame to a CSV file
write.csv(Bacillus_filtered, "Resistance/Bacillus.fitness.csv", row.names = FALSE)

# Create a function to write FASTA entries
write_fasta <- function(id, seq, file) {
  cat(paste0(">", id, "\n"), file = file, append = TRUE)
  cat(paste0("M", seq, "\n"), file = file, append = TRUE)  # Add "M" at the beginning of the sequence
}

# Specify the output file name
Bacillus_output_file <- "Resistance/FASTA/Bacillus.fasta"

# Remove the file if it already exists
if (file.exists(Bacillus_output_file)) {
  file.remove(Bacillus_output_file)
}
[1] TRUE
# Write the FASTA entries to the file
for (i in 1:nrow(Bacillus_filtered)) {
  write_fasta(Bacillus_filtered$mutID[i], Bacillus_filtered$seq[i], Bacillus_output_file)
}

cat("FASTA file has been generated:", Bacillus_output_file, "\n")
FASTA file has been generated: Resistance/FASTA/Bacillus.fasta 

Perform a multiple sequence alignment on the FASTA file using CLUSTAL Omega:

# May need to enable permissions to run the executable:
#chmod +x clustalo
./Scripts/clustalo -i Resistance/FASTA/Bacillus.fasta -o Resistance/FASTA/Bacillus.aligned.fasta --outfmt=fa --force

Import the MSA file and determine which amino acid positions differ between the samples

# Import the MSA file
msa <- readAAMultipleAlignment("Resistance/FASTA/Bacillus.aligned.fasta", format="fasta")

# Convert to a matrix for easier manipulation
msa_matrix <- as.matrix(msa)

# Set the reference sequence
ref_seq <- "NP_831957"

cat("Using reference sequence:", ref_seq, "\n")
Using reference sequence: NP_831957 
# Find positions where amino acids differ
diff_positions <- which(apply(msa_matrix, 2, function(col) length(unique(col)) > 1))

# Create a summary dataframe
summary_df <- data.frame(
  Position = diff_positions,
  RefAA = msa_matrix[ref_seq, diff_positions],
  stringsAsFactors = FALSE
)

# Add columns for each unique mutID
unique_mutIDs <- setdiff(rownames(msa_matrix), ref_seq)
for (mutID in unique_mutIDs) {
  summary_df[[mutID]] <- msa_matrix[mutID, diff_positions]
}

# Add a column for the amino acid changes
summary_df$Changes <- apply(summary_df, 1, function(row) {
  changes <- setdiff(as.character(row[3:ncol(summary_df)]), row["RefAA"])
  if (length(changes) > 0) {
    paste(row["RefAA"], paste(changes, collapse = "/"), sep = "->")
  } else {
    "No change"
  }
})

# Remove rows with "No change"
summary_df <- summary_df[summary_df$Changes != "No change", ]

# Reorder columns
summary_df <- summary_df %>% 
  select(Position, RefAA, Changes, everything())

# Print the summary table
print(summary_df, row.names = FALSE)

Arrange the data for the amino acid mutations heatmap:

# Get all positions where there's a mutation
all_positions <- sort(unique(summary_df$Position))

# Reshape the data
heatmap_data <- summary_df %>%
  select(-Changes) %>%
  pivot_longer(cols = -c(Position, RefAA), 
               names_to = "mutID", 
               values_to = "AA") %>%
  mutate(Position = as.numeric(Position))

# Add the reference sequence
ref_data <- summary_df %>%
  select(Position, RefAA) %>%
  mutate(mutID = "NP_831957", AA = RefAA)

heatmap_data <- bind_rows(ref_data, heatmap_data)

# Create a function to compare amino acids
compare_aa <- function(aa, ref_aa) {
  ifelse(is.na(aa) | aa == "", "No data",
         ifelse(aa == ref_aa, "No change", "Mutation"))
}

# Apply the comparison function and modify AA display
heatmap_data <- heatmap_data %>%
  group_by(Position) %>%
  mutate(
    Comparison = compare_aa(AA, AA[mutID == "NP_831957"]),
    DisplayAA = case_when(
      mutID == "NP_831957" ~ AA,
      Comparison == "Mutation" ~ AA,
      TRUE ~ ""
    )
  ) %>%
  ungroup()

# Create a continuous position variable
heatmap_data <- heatmap_data %>%
  arrange(Position) %>%
  mutate(PositionIndex = as.numeric(factor(Position, levels = unique(Position))))

# Create Bacillus_long
Bacillus_long <- Bacillus_filtered %>%
  pivot_longer(cols = starts_with("fitD"), 
               names_to = "TMP", 
               values_to = "Fitness",
               names_prefix = "fitD")

# Calculate the fitness at the highest TMP concentration for each mutID
max_fitness <- Bacillus_long %>%
  filter(TMP == "11D03") %>%
  select(mutID, max_fitness = Fitness)

# Join max_fitness information and create MutantColor
heatmap_data <- heatmap_data %>%
  left_join(max_fitness, by = "mutID") %>%
  mutate(MutantColor = case_when(
    mutID == "NP_831957" ~ "red",
    is.na(max_fitness) ~ "gray",
    max_fitness < -1 ~ "darkblue",
    max_fitness >= -1 ~ "gold2"
  ))

# Create a new column for ordering
heatmap_data <- heatmap_data %>%
  mutate(OrderGroup = case_when(
    mutID == "NP_831957" ~ 1,
    max_fitness >= -1 ~ 2,
    max_fitness < -1 ~ 3,
    TRUE ~ 4  # for any NA or other cases
  ))

Heatmap for amino acid mutations by position based on reference sequence:

# Order the data with reference on top
heatmap_data <- heatmap_data %>%
  arrange(OrderGroup, desc(max_fitness)) %>%
  mutate(mutID = factor(mutID, levels = unique(mutID)))

# Ensure the colors are assigned correctly
y_colors <- heatmap_data %>%
  distinct(mutID, MutantColor) %>%
  arrange(match(mutID, levels(heatmap_data$mutID)))

# Modify the heatmap_data to include the color information for mutations and text
heatmap_data <- heatmap_data %>%
  mutate(
    MutationColor = case_when(
      Comparison == "Mutation" & max_fitness >= -1 ~ "gold2",
      Comparison == "Mutation" & max_fitness < -1 ~ "darkblue",
      TRUE ~ Comparison
    ),
    TextColor = case_when(
      MutationColor == "darkblue" ~ "white",
      TRUE ~ "black"
    )
  )

# Create the heatmap
Bacillus.heatmap <- ggplot(heatmap_data, aes(x = PositionIndex, y = mutID, fill = MutationColor)) +
  geom_tile(color = "white", width = 1, height = 0.9) +
  scale_fill_manual(values = c("No change" = "gray90", "gold2" = "gold2", "darkblue" = "darkblue", "No data" = "white")) +
  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5, size = 12),
    axis.text.y = element_text(size = 12),
    axis.title.x = element_text(size = 14),
    axis.title.y = element_blank(),
    panel.grid.major.x = element_line(color = "white", size = 0.5),
    panel.grid.major.y = element_line(color = "white", size = 0.5),
    panel.grid.minor = element_blank(),
    legend.position = "none"
  ) +
  labs(x = "Position", fill = "Amino Acid Change") +
  scale_x_continuous(
    breaks = heatmap_data$PositionIndex,
    labels = heatmap_data$Position,
    expand = c(0, 0)
  ) +
  geom_text(aes(label = DisplayAA, color = TextColor), size = 6, na.rm = TRUE, fontface = "bold") +
  scale_color_identity() +
  scale_y_discrete(limits = rev(levels(heatmap_data$mutID)), expand = c(0, 0))

# Apply correct colors to y-axis labels
Bacillus.heatmap <- Bacillus.heatmap +
  theme(axis.text.y = element_text(color = rev(y_colors$MutantColor))) + 
  ggtitle("Bacillus cereus") + 
  theme(plot.title = element_text(hjust = 0.5, face = "italic", size = 16))
Warning: Vectorized input to `element_text()` is not officially supported.
ℹ Results may be unexpected or may change in future versions of ggplot2.
print(Bacillus.heatmap)

Plot the change in fitness across the TMP gradient for both samples:

# Reshape the data from wide to long format
Bacillus_long <- Bacillus_filtered %>%
  pivot_longer(cols = starts_with("fitD"), 
               names_to = "TMP", 
               values_to = "Fitness",
               names_prefix = "fitD") %>%
  mutate(Day = factor(TMP, levels = c("05D03", "06D03", "07D03", "08D03", "09D03", "10D03", "11D03"),
                      labels = c("0", "0.058", "0.5", "1.0", "10", "50", "200")))

# Calculate the fitness at the highest TMP concentration for each mutID
Bacillus_max_fitness <- Bacillus_long %>%
  filter(TMP == "11D03") %>%
  select(mutID, max_fitness = Fitness)

# Join this information back to the original data and color accordingly
Bacillus_long <- Bacillus_long %>%
  left_join(Bacillus_max_fitness, by = "mutID") %>%
  mutate(Color = case_when(
    mutID == "NP_831957" ~ "WT",
    is.na(max_fitness) ~ "Mutant_Unknown",
    max_fitness < -1 ~ "Mutant_Low",
    max_fitness >= -1 ~ "Mutant_High"
  ))

# Reorder the levels of Color factor to plot WT last
Bacillus_long$Color <- factor(Bacillus_long$Color, 
                                   levels = c("Mutant_Unknown", "Mutant_Low", "Mutant_High", "WT"))

# Separate WT and mutant data
WT_data <- Bacillus_long %>% filter(mutID == "NP_831957")
mutant_data <- Bacillus_long %>% filter(mutID != "NP_831957")

Fitness plotting over the TMP gradient

# First, interpolate NA values
mutant_data <- mutant_data %>%
  group_by(mutID) %>%
  arrange(Day) %>%
  mutate(Fitness = na.approx(Fitness, x = Day, na.rm = FALSE)) %>%
  ungroup()

WT_data <- WT_data %>%
  arrange(Day) %>%
  mutate(Fitness = na.approx(Fitness, x = Day, na.rm = FALSE))

# Then create the plot
Bacillus_plot <- ggplot() +
  geom_hline(yintercept = -1, linetype = "dashed", color = "gray50") +
  # Plot mutant lines first
  geom_line(data = mutant_data, aes(x = Day, y = Fitness, group = mutID, color = Color), size = 1.0) +
  geom_point(data = mutant_data, aes(x = Day, y = Fitness, color = Color), size = 3) +
  # Plot WT line on top
  geom_line(data = WT_data, aes(x = Day, y = Fitness, group = mutID), color = "red", size = 2.0) +
  geom_point(data = WT_data, aes(x = Day, y = Fitness), color = "red", size = 3) +
  scale_color_manual(values = c("Mutant_Low" = "darkblue", "Mutant_High" = "gold", "Mutant_Unknown" = "gray")) +
  scale_x_discrete(limits = c("0", "0.058", "0.5", "1.0", "10", "50", "200")) +
  labs(x = "Trimethoprim (ug/mL)",
       y = "Median Fitness (LogFC)") +
  theme_minimal() +
  theme(
    axis.line = element_line(colour = 'black', size = 1.0),
    axis.ticks = element_line(colour = "black", size = 1.0),
    axis.text.x = element_text(size = 12, angle = 45, hjust = 1),
    axis.text.y = element_text(size = 12),
    axis.title.x = element_text(size = 14),
    axis.title.y = element_text(size = 14),
    panel.background = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    legend.position = "none") + 
  ggtitle("Bacillus cereus") + 
  theme(plot.title = element_text(hjust = 0.5, face = "italic", size = 16))

# Print the plot
print(Bacillus_plot)

Plot together:

patch2 <- Bacillus_plot | Bacillus.heatmap
patch2

Anaerococcus tetradius

# Filter to retain Escherichia coli (NCBI.species) from L15_homo_mut_good_filtered
Anaerococcus <- L15_homo_mut_good_filtered[L15_homo_mut_good_filtered$NCBI.species == "Anaerococcus tetradius" & 
                                            !is.na(L15_homo_mut_good_filtered$NCBI.species), ]

# Filter to only retain specific variants for the MSA
Anaerococcus_filtered <- Anaerococcus %>%
  filter((mutID == "WP_004836669" | ID == "WP_004836669") & !is.na(fitD11D03))

# Create a function to write FASTA entries
write_fasta <- function(id, seq, file) {
  cat(paste0(">", id, "\n"), file = file, append = TRUE)
  cat(paste0("M", seq, "\n"), file = file, append = TRUE)  # Add "M" at the beginning of the sequence
}

# Specify the output file name
Anaerococcus_output_file <- "Resistance/FASTA/Anaerococcus.fasta"

# Remove the file if it already exists
if (file.exists(Anaerococcus_output_file)) {
  file.remove(Anaerococcus_output_file)
}
[1] TRUE
# Write the FASTA entries to the file
for (i in 1:nrow(Anaerococcus_filtered)) {
  write_fasta(Anaerococcus_filtered$mutID[i], Anaerococcus_filtered$seq[i], Anaerococcus_output_file)
}

cat("FASTA file has been generated:", Anaerococcus_output_file, "\n")
FASTA file has been generated: Resistance/FASTA/Anaerococcus.fasta 

Perform a multiple sequence alignment on the FASTA file using CLUSTAL Omega:

# May need to enable permissions to run the executable:
#chmod +x clustalo
./Scripts/clustalo -i Resistance/FASTA/Anaerococcus.fasta -o Resistance/FASTA/Anaerococcus.aligned.fasta --outfmt=fa --force

Import the MSA file and determine which amino acid positions differ between the samples

# Import the MSA file
msa <- readAAMultipleAlignment("Resistance/FASTA/Anaerococcus.aligned.fasta", format="fasta")

# Convert to a matrix for easier manipulation
msa_matrix <- as.matrix(msa)

# Set the reference sequence
ref_seq <- "WP_004836669"

cat("Using reference sequence:", ref_seq, "\n")
Using reference sequence: WP_004836669 
# Find positions where amino acids differ
diff_positions <- which(apply(msa_matrix, 2, function(col) length(unique(col)) > 1))

# Create a summary dataframe
summary_df <- data.frame(
  Position = diff_positions,
  RefAA = msa_matrix[ref_seq, diff_positions],
  stringsAsFactors = FALSE
)

# Add columns for each unique mutID
unique_mutIDs <- setdiff(rownames(msa_matrix), ref_seq)
for (mutID in unique_mutIDs) {
  summary_df[[mutID]] <- msa_matrix[mutID, diff_positions]
}

# Add a column for the amino acid changes
summary_df$Changes <- apply(summary_df, 1, function(row) {
  changes <- setdiff(as.character(row[3:ncol(summary_df)]), row["RefAA"])
  if (length(changes) > 0) {
    paste(row["RefAA"], paste(changes, collapse = "/"), sep = "->")
  } else {
    "No change"
  }
})

# Remove rows with "No change"
summary_df <- summary_df[summary_df$Changes != "No change", ]

# Reorder columns
summary_df <- summary_df %>% 
  select(Position, RefAA, Changes, everything())

# Print the summary table
print(summary_df, row.names = FALSE)

Arrange the data for the amino acid mutations heatmap:

# Get all positions where there's a mutation
all_positions <- sort(unique(summary_df$Position))

# Reshape the data
heatmap_data <- summary_df %>%
  select(-Changes) %>%
  pivot_longer(cols = -c(Position, RefAA), 
               names_to = "mutID", 
               values_to = "AA") %>%
  mutate(Position = as.numeric(Position))

# Add the reference sequence
ref_data <- summary_df %>%
  select(Position, RefAA) %>%
  mutate(mutID = "WP_004836669", AA = RefAA)

heatmap_data <- bind_rows(ref_data, heatmap_data)

# Create a function to compare amino acids
compare_aa <- function(aa, ref_aa) {
  ifelse(is.na(aa) | aa == "", "No data",
         ifelse(aa == ref_aa, "No change", "Mutation"))
}

# Apply the comparison function and modify AA display
heatmap_data <- heatmap_data %>%
  group_by(Position) %>%
  mutate(
    Comparison = compare_aa(AA, AA[mutID == "WP_004836669"]),
    DisplayAA = case_when(
      mutID == "WP_004836669" ~ AA,
      Comparison == "Mutation" ~ AA,
      TRUE ~ ""
    )
  ) %>%
  ungroup()

# Create a continuous position variable
heatmap_data <- heatmap_data %>%
  arrange(Position) %>%
  mutate(PositionIndex = as.numeric(factor(Position, levels = unique(Position))))

# Create Anaerococcus_long
Anaerococcus_long <- Anaerococcus_filtered %>%
  pivot_longer(cols = starts_with("fitD"), 
               names_to = "TMP", 
               values_to = "Fitness",
               names_prefix = "fitD")

# Calculate the fitness at the highest TMP concentration for each mutID
max_fitness <- Anaerococcus_long %>%
  filter(TMP == "11D03") %>%
  select(mutID, max_fitness = Fitness)

# Join max_fitness information and create MutantColor
heatmap_data <- heatmap_data %>%
  left_join(max_fitness, by = "mutID") %>%
  mutate(MutantColor = case_when(
    mutID == "WP_004836669" ~ "red",
    is.na(max_fitness) ~ "gray",
    max_fitness < -1 ~ "darkblue",
    max_fitness >= -1 ~ "gold2"
  ))

# Create a new column for ordering
heatmap_data <- heatmap_data %>%
  mutate(OrderGroup = case_when(
    mutID == "WP_004836669" ~ 1,
    max_fitness >= -1 ~ 2,
    max_fitness < -1 ~ 3,
    TRUE ~ 4  # for any NA or other cases
  ))

Heatmap for amino acid mutations by position based on reference sequence:

# Order the data with reference on top
heatmap_data <- heatmap_data %>%
  arrange(OrderGroup, desc(max_fitness)) %>%
  mutate(mutID = factor(mutID, levels = unique(mutID)))

# Ensure the colors are assigned correctly
y_colors <- heatmap_data %>%
  distinct(mutID, MutantColor) %>%
  arrange(match(mutID, levels(heatmap_data$mutID)))

# Modify the heatmap_data to include the color information for mutations and text
heatmap_data <- heatmap_data %>%
  mutate(
    MutationColor = case_when(
      Comparison == "Mutation" & max_fitness >= -1 ~ "gold2",
      Comparison == "Mutation" & max_fitness < -1 ~ "darkblue",
      TRUE ~ Comparison
    ),
    TextColor = case_when(
      MutationColor == "darkblue" ~ "white",
      TRUE ~ "black"
    )
  )

# Create the heatmap
Anaerococcus.heatmap <- ggplot(heatmap_data, aes(x = PositionIndex, y = mutID, fill = MutationColor)) +
  geom_tile(color = "white", width = 1, height = 0.9) +
  scale_fill_manual(values = c("No change" = "gray90", "gold2" = "gold2", "darkblue" = "darkblue", "No data" = "white")) +
  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5, size = 12),
    axis.text.y = element_text(size = 12),
    axis.title.x = element_text(size = 14),
    axis.title.y = element_blank(),
    panel.grid.major.x = element_line(color = "white", size = 0.5),
    panel.grid.major.y = element_line(color = "white", size = 0.5),
    panel.grid.minor = element_blank(),
    legend.position = "none"
  ) +
  labs(x = "Position", fill = "Amino Acid Change") +
  scale_x_continuous(
    breaks = heatmap_data$PositionIndex,
    labels = heatmap_data$Position,
    expand = c(0, 0)
  ) +
  geom_text(aes(label = DisplayAA, color = TextColor), size = 5, na.rm = TRUE, fontface = "bold") +
  scale_color_identity() +
  scale_y_discrete(limits = rev(levels(heatmap_data$mutID)), expand = c(0, 0))

# Apply correct colors to y-axis labels
Anaerococcus.heatmap <- Anaerococcus.heatmap +
  theme(axis.text.y = element_text(color = rev(y_colors$MutantColor))) + 
  ggtitle("Anaerococcus tetradius") + 
  theme(plot.title = element_text(hjust = 0.5, face = "italic", size = 16))
Warning: Vectorized input to `element_text()` is not officially supported.
ℹ Results may be unexpected or may change in future versions of ggplot2.
print(Anaerococcus.heatmap)

Plot the change in fitness across the TMP gradient for both samples:

# Reshape the data from wide to long format
Anaerococcus_long <- Anaerococcus_filtered %>%
  pivot_longer(cols = starts_with("fitD"), 
               names_to = "TMP", 
               values_to = "Fitness",
               names_prefix = "fitD") %>%
  mutate(Day = factor(TMP, levels = c("05D03", "06D03", "07D03", "08D03", "09D03", "10D03", "11D03"),
                      labels = c("0", "0.058", "0.5", "1.0", "10", "50", "200")))

# Calculate the fitness at the highest TMP concentration for each mutID
Anaerococcus_max_fitness <- Anaerococcus_long %>%
  filter(TMP == "11D03") %>%
  select(mutID, max_fitness = Fitness)

# Join this information back to the original data and color accordingly
Anaerococcus_long <- Anaerococcus_long %>%
  left_join(Anaerococcus_max_fitness, by = "mutID") %>%
  mutate(Color = case_when(
    mutID == "WP_004836669" ~ "WT",
    is.na(max_fitness) ~ "Mutant_Unknown",
    max_fitness < -1 ~ "Mutant_Low",
    max_fitness >= -1 ~ "Mutant_High"
  ))

# Reorder the levels of Color factor to plot WT last
Anaerococcus_long$Color <- factor(Anaerococcus_long$Color, 
                                   levels = c("Mutant_Unknown", "Mutant_Low", "Mutant_High", "WT"))

# Separate WT and mutant data
WT_data <- Anaerococcus_long %>% filter(mutID == "WP_004836669")
mutant_data <- Anaerococcus_long %>% filter(mutID != "WP_004836669")

Fitness plotting over the TMP gradient

# Create the plot
Anaerococcus_plot <- ggplot() +
  geom_hline(yintercept = -1, linetype = "dashed", color = "gray50") +
  # Plot mutant lines first
  geom_line(data = mutant_data, aes(x = Day, y = Fitness, group = mutID, color = Color), size = 1.0) +
  geom_point(data = mutant_data, aes(x = Day, y = Fitness, color = Color), size = 3) +
  # Plot WT line on top
  geom_line(data = WT_data, aes(x = Day, y = Fitness, group = mutID), color = "red", size = 2.0) +
  geom_point(data = WT_data, aes(x = Day, y = Fitness), color = "red", size = 3) +
  scale_color_manual(values = c("Mutant_Low" = "darkblue", "Mutant_High" = "gold", "Mutant_Unknown" = "gray")) +
  scale_x_discrete(limits = c("0", "0.058", "0.5", "1.0", "10", "50", "200")) +
  labs(x = "Trimethoprim (ug/mL)",
       y = "Median Fitness (LogFC)") +
  theme_minimal() +
  theme(
    axis.line = element_line(colour = 'black', size = 1.0),
    axis.ticks = element_line(colour = "black", size = 1.0),
    axis.text.x = element_text(size = 12, angle = 45, hjust = 1),
    axis.text.y = element_text(size = 12),
    axis.title.x = element_text(size = 14),
    axis.title.y = element_text(size = 14),
    panel.background = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    legend.position = "none") + 
  ggtitle("Anaerococcus tetradius") + 
  theme(plot.title = element_text(hjust = 0.5, face = "italic", size = 16)) # Center and italicize the title

# Print the plot
print(Anaerococcus_plot)

Plot together:

patch3 <- Anaerococcus_plot | Anaerococcus.heatmap
patch3

Full plot with Bacillus and Streptococcus taxa:

patch7 <- (Bacillus_plot | Bacillus.heatmap) / 
          (Streptococcus_plot | Streptococcus.heatmap) +
  plot_annotation(tag_levels = 'A') &
  theme(plot.tag = element_text(size = 18, face = "bold"))
patch7

Full plot with all three taxa:

patch4 <- (Streptococcus_plot | Streptococcus.heatmap) / 
          (Bacillus_plot | Bacillus.heatmap) / 
          (Anaerococcus_plot | Anaerococcus.heatmap) +
  plot_annotation(tag_levels = 'A') &
  theme(plot.tag = element_text(size = 18, face = "bold"))
patch4

Full plot with FOUR taxa:

patch5 <- (pathogenic_plot | Escherichia_plot) /
          (Bacillus_plot | Bacillus.heatmap) /
          (Streptococcus_plot | Streptococcus.heatmap) +
  plot_layout(heights = c(3, 3, 4)) +
  plot_annotation(tag_levels = 'A')

patch5 <- patch5 & 
  theme(plot.tag = element_text(size = 24, face = "bold"))

patch5

Full plot with all five taxa:

patch6 <- (pathogenic_plot | Escherichia_plot) /
          (Bacillus_plot | Bacillus.heatmap) / 
          (Anaerococcus_plot | Anaerococcus.heatmap) /
          (Streptococcus_plot | Streptococcus.heatmap) + 
  plot_layout(heights = c(3, 4, 4, 4)) +
  plot_annotation(tag_levels = 'A')

patch6 <- patch6 & 
  theme(plot.tag = element_text(size = 24, face = "bold"))

patch6

Dial-out Validation

Two of the pathogenic DHFR variants were also included in our Dial-out PCR verification analysis to measure growth rates independent of the multiplexed fitness assay. These include Bacillus cereus and Streptococcus pneumoniae.

Bacillus cereus

# Filter to only retain specific variants for the MSA
Bacillus_dialout <- Bacillus %>%
  filter(((mutID == "NP_831957") | 
          (mutID == "WP_000637209")) & 
         !is.na(fitD11D03))

Align the two variants and retain NP_831957 as the reference sequence

# Create a function to write FASTA entries
write_fasta <- function(id, seq, file) {
  cat(paste0(">", id, "\n"), file = file, append = TRUE)
  cat(paste0("M", seq, "\n"), file = file, append = TRUE)  # Add "M" at the beginning of the sequence
}

# Specify the output file name
Bacillus_dialout_output_file <- "Resistance/FASTA/Bacillus.dialout.fasta"

# Remove the file if it already exists
if (file.exists(Bacillus_dialout_output_file)) {
  file.remove(Bacillus_dialout_output_file)
}
[1] TRUE
# Write the FASTA entries to the file
for (i in 1:nrow(Bacillus_dialout)) {
  write_fasta(Bacillus_dialout$mutID[i], Bacillus_dialout$seq[i], Bacillus_dialout_output_file)
}

cat("FASTA file has been generated:", Bacillus_dialout_output_file, "\n")
FASTA file has been generated: Resistance/FASTA/Bacillus.dialout.fasta 

Perform a multiple sequence alignment on the FASTA file using CLUSTAL Omega:

# May need to enable permissions to run the executable:
#chmod +x clustalo
./Scripts/clustalo -i Resistance/FASTA/Bacillus.dialout.fasta -o Resistance/FASTA/Bacillus.dialout.aligned.fasta --outfmt=fa --force

Import the MSA file and determine which amino acid positions differ between the samples

# Import the MSA file
msa <- readAAMultipleAlignment("Resistance/FASTA/Bacillus.dialout.aligned.fasta", format="fasta")

# Convert to a matrix for easier manipulation
msa_matrix <- as.matrix(msa)

# Set the reference sequence
ref_seq <- "NP_831957"

cat("Using reference sequence:", ref_seq, "\n")
Using reference sequence: NP_831957 
# Find positions where amino acids differ
diff_positions <- which(apply(msa_matrix, 2, function(col) length(unique(col)) > 1))

# Create a summary dataframe
summary_df <- data.frame(
  Position = diff_positions,
  RefAA = msa_matrix[ref_seq, diff_positions],
  stringsAsFactors = FALSE
)

# Add columns for each unique mutID
unique_mutIDs <- setdiff(rownames(msa_matrix), ref_seq)
for (mutID in unique_mutIDs) {
  summary_df[[mutID]] <- msa_matrix[mutID, diff_positions]
}

# Add a column for the amino acid changes
summary_df$Changes <- apply(summary_df, 1, function(row) {
  changes <- setdiff(as.character(row[3:ncol(summary_df)]), row["RefAA"])
  if (length(changes) > 0) {
    paste(row["RefAA"], paste(changes, collapse = "/"), sep = "->")
  } else {
    "No change"
  }
})

# Remove rows with "No change"
summary_df <- summary_df[summary_df$Changes != "No change", ]

# Reorder columns
summary_df <- summary_df %>% 
  select(Position, RefAA, Changes, everything())

# Print the summary table
print(summary_df, row.names = FALSE)

Arrange the data for the amino acid mutations heatmap:

# Get all positions where there's a mutation
all_positions <- sort(unique(summary_df$Position))

# Reshape the data
heatmap_data <- summary_df %>%
  select(-Changes) %>%
  pivot_longer(cols = -c(Position, RefAA), 
               names_to = "mutID", 
               values_to = "AA") %>%
  mutate(Position = as.numeric(Position))

# Add the reference sequence
ref_data <- summary_df %>%
  select(Position, RefAA) %>%
  mutate(mutID = "NP_831957", AA = RefAA)

heatmap_data <- bind_rows(ref_data, heatmap_data)

# Create a function to compare amino acids
compare_aa <- function(aa, ref_aa) {
  ifelse(is.na(aa) | aa == "", "No data",
         ifelse(aa == ref_aa, "No change", "Mutation"))
}

# Apply the comparison function and modify AA display
heatmap_data <- heatmap_data %>%
  group_by(Position) %>%
  mutate(
    Comparison = compare_aa(AA, AA[mutID == "NP_831957"]),
    DisplayAA = case_when(
      mutID == "NP_831957" ~ AA,
      Comparison == "Mutation" ~ AA,
      TRUE ~ ""
    )
  ) %>%
  ungroup()

# Create a continuous position variable
heatmap_data <- heatmap_data %>%
  arrange(Position) %>%
  mutate(PositionIndex = as.numeric(factor(Position, levels = unique(Position))))

# Create Bacillus_long
Bacillus_long <- Bacillus_dialout %>%
  pivot_longer(cols = starts_with("fitD"), 
               names_to = "TMP", 
               values_to = "Fitness",
               names_prefix = "fitD")

# Calculate the fitness at the highest TMP concentration for each mutID
max_fitness <- Bacillus_long %>%
  filter(TMP == "11D03") %>%
  select(mutID, max_fitness = Fitness)

# Join max_fitness information and create MutantColor
heatmap_data <- heatmap_data %>%
  left_join(max_fitness, by = "mutID") %>%
  mutate(MutantColor = case_when(
    mutID == "NP_831957" ~ "red",
    is.na(max_fitness) ~ "gray",
    max_fitness < -1 ~ "darkblue",
    max_fitness >= -1 ~ "gold2"
  ))

# Create a new column for ordering
heatmap_data <- heatmap_data %>%
  mutate(OrderGroup = case_when(
    mutID == "NP_831957" ~ 1,
    max_fitness >= -1 ~ 2,
    max_fitness < -1 ~ 3,
    TRUE ~ 4  # for any NA or other cases
  ))

Heatmap for amino acid mutations by position based on reference sequence:

# Order the data with reference on top
heatmap_data <- heatmap_data %>%
  arrange(OrderGroup, desc(max_fitness)) %>%
  mutate(mutID = factor(mutID, levels = unique(mutID)))

# Ensure the colors are assigned correctly
y_colors <- heatmap_data %>%
  distinct(mutID, MutantColor) %>%
  arrange(match(mutID, levels(heatmap_data$mutID)))

# Modify the heatmap_data to include the color information for mutations and text
heatmap_data <- heatmap_data %>%
  mutate(
    MutationColor = case_when(
      Comparison == "Mutation" & max_fitness >= -1 ~ "gold2",
      Comparison == "Mutation" & max_fitness < -1 ~ "darkblue",
      TRUE ~ Comparison
    ),
    TextColor = case_when(
      MutationColor == "darkblue" ~ "white",
      TRUE ~ "black"
    )
  )

# Create the heatmap
Bacillus.dialout.heatmap <- ggplot(heatmap_data, aes(x = PositionIndex, y = mutID, fill = MutationColor)) +
  geom_tile(color = "white", width = 1, height = 0.9) +
  scale_fill_manual(values = c("No change" = "gray90", "gold2" = "gold2", "darkblue" = "darkblue", "No data" = "white")) +
  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5, size = 12),
    axis.text.y = element_text(size = 12),
    axis.title.x = element_text(size = 14),
    axis.title.y = element_blank(),
    panel.grid.major.x = element_line(color = "white", size = 0.5),
    panel.grid.major.y = element_line(color = "white", size = 0.5),
    panel.grid.minor = element_blank(),
    legend.position = "none"
  ) +
  labs(x = "Position", fill = "Amino Acid Change") +
  scale_x_continuous(
    breaks = heatmap_data$PositionIndex,
    labels = heatmap_data$Position,
    expand = c(0, 0)
  ) +
  geom_text(aes(label = DisplayAA, color = TextColor), size = 6, na.rm = TRUE, fontface = "bold") +
  scale_color_identity() +
  scale_y_discrete(limits = rev(levels(heatmap_data$mutID)), expand = c(0, 0))

# Apply correct colors to y-axis labels
Bacillus.dialout.heatmap <- Bacillus.dialout.heatmap +
  theme(axis.text.y = element_text(color = rev(y_colors$MutantColor))) + 
  ggtitle("Bacillus cereus") + 
  theme(plot.title = element_text(hjust = 0.5, face = "italic", size = 16))
Warning: Vectorized input to `element_text()` is not officially supported.
ℹ Results may be unexpected or may change in future versions of ggplot2.
print(Bacillus.dialout.heatmap)

Plot the change in fitness across the TMP gradient for both samples:

# Reshape the data from wide to long format
Bacillus_dialout_long <- Bacillus_dialout %>%
  pivot_longer(cols = starts_with("fitD"), 
               names_to = "TMP", 
               values_to = "Fitness",
               names_prefix = "fitD") %>%
  mutate(Day = factor(TMP, levels = c("05D03", "06D03", "07D03", "08D03", "09D03", "10D03", "11D03"),
                      labels = c("0", "0.058", "0.5", "1.0", "10", "50", "200")))

# Calculate the fitness at the highest TMP concentration for each mutID
Bacillus_dialout_max_fitness <- Bacillus_dialout_long %>%
  filter(TMP == "11D03") %>%
  select(mutID, max_fitness = Fitness)

# Join this information back to the original data and color accordingly
Bacillus_dialout_long <- Bacillus_dialout_long %>%
  left_join(Bacillus_dialout_max_fitness, by = "mutID") %>%
  mutate(Color = case_when(
    mutID == "NP_831957" ~ "WT",
    is.na(max_fitness) ~ "Mutant_Unknown",
    max_fitness < -1 ~ "Mutant_Low",
    max_fitness >= -1 ~ "Mutant_High"
  ))

# Reorder the levels of Color factor to plot WT last
Bacillus_dialout_long$Color <- factor(Bacillus_dialout_long$Color, 
                                   levels = c("Mutant_Unknown", "Mutant_Low", "Mutant_High", "WT"))

# Separate WT and mutant data
WT_data <- Bacillus_dialout_long %>% filter(mutID == "NP_831957")
mutant_data <- Bacillus_dialout_long %>% filter(mutID != "NP_831957")

Fitness plotting over the TMP gradient

# First, interpolate NA values
mutant_data <- mutant_data %>%
  group_by(mutID) %>%
  arrange(Day) %>%
  mutate(Fitness = na.approx(Fitness, x = Day, na.rm = FALSE)) %>%
  ungroup()

WT_data <- WT_data %>%
  arrange(Day) %>%
  mutate(Fitness = na.approx(Fitness, x = Day, na.rm = FALSE))

# Then create the plot
Bacillus_dialout_plot <- ggplot() +
  geom_hline(yintercept = -1, linetype = "dashed", color = "gray50") +
  # Plot mutant lines first
  geom_line(data = mutant_data, aes(x = Day, y = Fitness, group = mutID, color = Color), size = 1.0) +
  geom_point(data = mutant_data, aes(x = Day, y = Fitness, color = Color), size = 3) +
  # Plot WT line on top
  geom_line(data = WT_data, aes(x = Day, y = Fitness, group = mutID), color = "red", size = 2.0) +
  geom_point(data = WT_data, aes(x = Day, y = Fitness), color = "red", size = 3) +
  scale_color_manual(values = c("Mutant_Low" = "darkblue", "Mutant_High" = "gold", "Mutant_Unknown" = "gray")) +
  scale_x_discrete(limits = c("0", "0.058", "0.5", "1.0", "10", "50", "200")) +
  labs(x = "Trimethoprim (ug/mL)",
       y = "Median Fitness (LogFC)") +
  theme_minimal() +
  theme(
    axis.line = element_line(colour = 'black', size = 1.0),
    axis.ticks = element_line(colour = "black", size = 1.0),
    axis.text.x = element_text(size = 12, angle = 45, hjust = 1),
    axis.text.y = element_text(size = 12),
    axis.title.x = element_text(size = 14),
    axis.title.y = element_text(size = 14),
    panel.background = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    legend.position = "none") + 
  ggtitle("Bacillus cereus") + 
  theme(plot.title = element_text(hjust = 0.5, face = "italic", size = 16))

# Print the plot
print(Bacillus_dialout_plot)

Plot together:

patch22 <- Bacillus_dialout_plot | Bacillus.dialout.heatmap
patch22

Streptococcus pnemoniae

# Filter to only retain specific variants for the MSA
Strep_dialout <- Streptococcus %>%
  filter(((mutID == "WP_002205327") | 
          (mutID == "WP_000162453")) & 
         !is.na(fitD11D03))

Align the two variants and retain WP_002205327 as the reference sequence

# Create a function to write FASTA entries
write_fasta <- function(id, seq, file) {
  cat(paste0(">", id, "\n"), file = file, append = TRUE)
  cat(paste0("M", seq, "\n"), file = file, append = TRUE)  # Add "M" at the beginning of the sequence
}

# Specify the output file name
Strep_dialout_output_file <- "Resistance/FASTA/Strep.dialout.fasta"

# Remove the file if it already exists
if (file.exists(Strep_dialout_output_file)) {
  file.remove(Strep_dialout_output_file)
}
[1] TRUE
# Write the FASTA entries to the file
for (i in 1:nrow(Strep_dialout)) {
  write_fasta(Strep_dialout$mutID[i], Strep_dialout$seq[i], Strep_dialout_output_file)
}

cat("FASTA file has been generated:", Strep_dialout_output_file, "\n")
FASTA file has been generated: Resistance/FASTA/Strep.dialout.fasta 

Perform a multiple sequence alignment on the FASTA file using CLUSTAL Omega:

# May need to enable permissions to run the executable:
#chmod +x clustalo
./Scripts/clustalo -i Resistance/FASTA/Strep.dialout.fasta -o Resistance/FASTA/Strep.dialout.aligned.fasta --outfmt=fa --force

Import the MSA file and determine which amino acid positions differ between the samples

# Import the MSA file
msa <- readAAMultipleAlignment("Resistance/FASTA/Strep.dialout.aligned.fasta", format="fasta")

# Convert to a matrix for easier manipulation
msa_matrix <- as.matrix(msa)

# Set the reference sequence
ref_seq <- "WP_002205327"

cat("Using reference sequence:", ref_seq, "\n")
Using reference sequence: WP_002205327 
# Find positions where amino acids differ
diff_positions <- which(apply(msa_matrix, 2, function(col) length(unique(col)) > 1))

# Create a summary dataframe
summary_df <- data.frame(
  Position = diff_positions,
  RefAA = msa_matrix[ref_seq, diff_positions],
  stringsAsFactors = FALSE
)

# Add columns for each unique mutID
unique_mutIDs <- setdiff(rownames(msa_matrix), ref_seq)
for (mutID in unique_mutIDs) {
  summary_df[[mutID]] <- msa_matrix[mutID, diff_positions]
}

# Add a column for the amino acid changes
summary_df$Changes <- apply(summary_df, 1, function(row) {
  changes <- setdiff(as.character(row[3:ncol(summary_df)]), row["RefAA"])
  if (length(changes) > 0) {
    paste(row["RefAA"], paste(changes, collapse = "/"), sep = "->")
  } else {
    "No change"
  }
})

# Remove rows with "No change"
summary_df <- summary_df[summary_df$Changes != "No change", ]

# Reorder columns
summary_df <- summary_df %>% 
  select(Position, RefAA, Changes, everything())

# Print the summary table
print(summary_df, row.names = FALSE)

Arrange the data for the amino acid mutations heatmap:

# Get all positions where there's a mutation
all_positions <- sort(unique(summary_df$Position))

# Reshape the data
heatmap_data <- summary_df %>%
  select(-Changes) %>%
  pivot_longer(cols = -c(Position, RefAA), 
               names_to = "mutID", 
               values_to = "AA") %>%
  mutate(Position = as.numeric(Position))

# Add the reference sequence
ref_data <- summary_df %>%
  select(Position, RefAA) %>%
  mutate(mutID = "WP_002205327", AA = RefAA)

heatmap_data <- bind_rows(ref_data, heatmap_data)

# Create a function to compare amino acids
compare_aa <- function(aa, ref_aa) {
  ifelse(is.na(aa) | aa == "", "No data",
         ifelse(aa == ref_aa, "No change", "Mutation"))
}

# Apply the comparison function and modify AA display
heatmap_data <- heatmap_data %>%
  group_by(Position) %>%
  mutate(
    Comparison = compare_aa(AA, AA[mutID == "WP_002205327"]),
    DisplayAA = case_when(
      mutID == "WP_002205327" ~ AA,
      Comparison == "Mutation" ~ AA,
      TRUE ~ ""
    )
  ) %>%
  ungroup()

# Create a continuous position variable
heatmap_data <- heatmap_data %>%
  arrange(Position) %>%
  mutate(PositionIndex = as.numeric(factor(Position, levels = unique(Position))))

# Create Strep_long
Strep_long <- Strep_dialout %>%
  pivot_longer(cols = starts_with("fitD"), 
               names_to = "TMP", 
               values_to = "Fitness",
               names_prefix = "fitD")

# Calculate the fitness at the highest TMP concentration for each mutID
max_fitness <- Strep_long %>%
  filter(TMP == "11D03") %>%
  select(mutID, max_fitness = Fitness)

# Join max_fitness information and create MutantColor
heatmap_data <- heatmap_data %>%
  left_join(max_fitness, by = "mutID") %>%
  mutate(MutantColor = case_when(
    mutID == "WP_002205327" ~ "red",
    is.na(max_fitness) ~ "gray",
    max_fitness < -1 ~ "darkblue",
    max_fitness >= -1 ~ "gold2"
  ))

# Create a new column for ordering
heatmap_data <- heatmap_data %>%
  mutate(OrderGroup = case_when(
    mutID == "WP_002205327" ~ 1,
    max_fitness >= -1 ~ 2,
    max_fitness < -1 ~ 3,
    TRUE ~ 4  # for any NA or other cases
  ))

Heatmap for amino acid mutations by position based on reference sequence:

# Order the data with reference on top
heatmap_data <- heatmap_data %>%
  arrange(OrderGroup, desc(max_fitness)) %>%
  mutate(mutID = factor(mutID, levels = unique(mutID)))

# Ensure the colors are assigned correctly
y_colors <- heatmap_data %>%
  distinct(mutID, MutantColor) %>%
  arrange(match(mutID, levels(heatmap_data$mutID)))

# Modify the heatmap_data to include the color information for mutations and text
heatmap_data <- heatmap_data %>%
  mutate(
    MutationColor = case_when(
      Comparison == "Mutation" & max_fitness >= -1 ~ "gold2",
      Comparison == "Mutation" & max_fitness < -1 ~ "darkblue",
      TRUE ~ Comparison
    ),
    TextColor = case_when(
      MutationColor == "darkblue" ~ "white",
      TRUE ~ "black"
    )
  )

# Create the heatmap
Strep.dialout.heatmap <- ggplot(heatmap_data, aes(x = PositionIndex, y = mutID, fill = MutationColor)) +
  geom_tile(color = "white", width = 1, height = 0.9) +
  scale_fill_manual(values = c("No change" = "gray90", "gold2" = "gold2", "darkblue" = "darkblue", "No data" = "white")) +
  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5, size = 12),
    axis.text.y = element_text(size = 12),
    axis.title.x = element_text(size = 14),
    axis.title.y = element_blank(),
    panel.grid.major.x = element_line(color = "white", size = 0.5),
    panel.grid.major.y = element_line(color = "white", size = 0.5),
    panel.grid.minor = element_blank(),
    legend.position = "none"
  ) +
  labs(x = "Position", fill = "Amino Acid Change") +
  scale_x_continuous(
    breaks = heatmap_data$PositionIndex,
    labels = heatmap_data$Position,
    expand = c(0, 0)
  ) +
  geom_text(aes(label = DisplayAA, color = TextColor), size = 6, na.rm = TRUE, fontface = "bold") +
  scale_color_identity() +
  scale_y_discrete(limits = rev(levels(heatmap_data$mutID)), expand = c(0, 0))

# Apply correct colors to y-axis labels
Strep.dialout.heatmap <- Strep.dialout.heatmap +
  theme(axis.text.y = element_text(color = rev(y_colors$MutantColor))) + 
  ggtitle("Streptococcus pneumoniae") + 
  theme(plot.title = element_text(hjust = 0.5, face = "italic", size = 16))
Warning: Vectorized input to `element_text()` is not officially supported.
ℹ Results may be unexpected or may change in future versions of ggplot2.
print(Strep.dialout.heatmap)

Plot the change in fitness across the TMP gradient for both samples:

# Reshape the data from wide to long format
Strep_dialout_long <- Strep_dialout %>%
  pivot_longer(cols = starts_with("fitD"), 
               names_to = "TMP", 
               values_to = "Fitness",
               names_prefix = "fitD") %>%
  mutate(Day = factor(TMP, levels = c("05D03", "06D03", "07D03", "08D03", "09D03", "10D03", "11D03"),
                      labels = c("0", "0.058", "0.5", "1.0", "10", "50", "200")))

# Calculate the fitness at the highest TMP concentration for each mutID
Strep_dialout_max_fitness <- Strep_dialout_long %>%
  filter(TMP == "11D03") %>%
  select(mutID, max_fitness = Fitness)

# Join this information back to the original data and color accordingly
Strep_dialout_long <- Strep_dialout_long %>%
  left_join(Strep_dialout_max_fitness, by = "mutID") %>%
  mutate(Color = case_when(
    mutID == "WP_002205327" ~ "WT",
    is.na(max_fitness) ~ "Mutant_Unknown",
    max_fitness < -1 ~ "Mutant_Low",
    max_fitness >= -1 ~ "Mutant_High"
  ))

# Reorder the levels of Color factor to plot WT last
Strep_dialout_long$Color <- factor(Strep_dialout_long$Color, 
                                   levels = c("Mutant_Unknown", "Mutant_Low", "Mutant_High", "WT"))

# Separate WT and mutant data
WT_data <- Strep_dialout_long %>% filter(mutID == "WP_002205327")
mutant_data <- Strep_dialout_long %>% filter(mutID != "WP_002205327")

Fitness plotting over the TMP gradient

# First, interpolate NA values
mutant_data <- mutant_data %>%
  group_by(mutID) %>%
  arrange(Day) %>%
  mutate(Fitness = na.approx(Fitness, x = Day, na.rm = FALSE)) %>%
  ungroup()

WT_data <- WT_data %>%
  arrange(Day) %>%
  mutate(Fitness = na.approx(Fitness, x = Day, na.rm = FALSE))

# Then create the plot
Strep_dialout_plot <- ggplot() +
  geom_hline(yintercept = -1, linetype = "dashed", color = "gray50") +
  # Plot mutant lines first
  geom_line(data = mutant_data, aes(x = Day, y = Fitness, group = mutID, color = Color), size = 1.0) +
  geom_point(data = mutant_data, aes(x = Day, y = Fitness, color = Color), size = 3) +
  # Plot WT line on top
  geom_line(data = WT_data, aes(x = Day, y = Fitness, group = mutID), color = "red", size = 2.0) +
  geom_point(data = WT_data, aes(x = Day, y = Fitness), color = "red", size = 3) +
  scale_color_manual(values = c("Mutant_Low" = "darkblue", "Mutant_High" = "gold", "Mutant_Unknown" = "gray")) +
  scale_x_discrete(limits = c("0", "0.058", "0.5", "1.0", "10", "50", "200")) +
  labs(x = "Trimethoprim (ug/mL)",
       y = "Median Fitness (LogFC)") +
  theme_minimal() +
  theme(
    axis.line = element_line(colour = 'black', size = 1.0),
    axis.ticks = element_line(colour = "black", size = 1.0),
    axis.text.x = element_text(size = 12, angle = 45, hjust = 1),
    axis.text.y = element_text(size = 12),
    axis.title.x = element_text(size = 14),
    axis.title.y = element_text(size = 14),
    panel.background = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    legend.position = "none") + 
  ggtitle("Streptococcus pneumoniae") + 
  theme(plot.title = element_text(hjust = 0.5, face = "italic", size = 16))

# Print the plot
print(Strep_dialout_plot)

Plot together:

patch23 <- Strep_dialout_plot | Strep.dialout.heatmap
patch23

Plot both pathogenic variants together:

patch24 <- (Bacillus_dialout_plot | Bacillus.dialout.heatmap) / (Strep_dialout_plot | Strep.dialout.heatmap)
patch24

Reproducibility

The session information is provided for full reproducibility.

devtools::session_info()
─ Session info ─────────────────────────────────────────────────────────────────────────────────────────────────────────────
 setting  value
 version  R version 4.3.2 (2023-10-31)
 os       macOS 15.2
 system   aarch64, darwin20
 ui       RStudio
 language (EN)
 collate  en_US.UTF-8
 ctype    en_US.UTF-8
 tz       America/Los_Angeles
 date     2025-01-23
 rstudio  2024.09.0+375 Cranberry Hibiscus (desktop)
 pandoc   3.2 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/aarch64/ (via rmarkdown)

─ Packages ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 package          * version    date (UTC) lib source
 ade4               1.7-22     2023-02-06 [1] CRAN (R 4.3.0)
 ape              * 5.8        2024-04-11 [1] CRAN (R 4.3.1)
 aplot              0.2.2      2023-10-06 [1] CRAN (R 4.3.1)
 bio3d            * 2.4-5      2024-10-29 [1] CRAN (R 4.3.3)
 BiocGenerics     * 0.46.0     2023-06-04 [1] Bioconductor
 Biostrings       * 2.68.1     2023-05-21 [1] Bioconductor
 bitops             1.0-7      2021-04-24 [1] CRAN (R 4.3.0)
 cachem             1.0.8      2023-05-01 [1] CRAN (R 4.3.0)
 castor           * 1.8.0      2024-01-09 [1] CRAN (R 4.3.1)
 cli                3.6.2      2023-12-11 [1] CRAN (R 4.3.1)
 codetools          0.2-20     2024-03-31 [1] CRAN (R 4.3.1)
 colorspace         2.1-0      2023-01-23 [1] CRAN (R 4.3.0)
 cowplot          * 1.1.3      2024-01-22 [1] CRAN (R 4.3.1)
 crayon             1.5.2      2022-09-29 [1] CRAN (R 4.3.0)
 devtools         * 2.4.5      2022-10-11 [1] CRAN (R 4.3.0)
 digest             0.6.35     2024-03-11 [1] CRAN (R 4.3.1)
 dplyr            * 1.1.4      2023-11-17 [1] CRAN (R 4.3.1)
 ellipsis           0.3.2      2021-04-29 [1] CRAN (R 4.3.0)
 evaluate           0.23       2023-11-01 [1] CRAN (R 4.3.1)
 fansi              1.0.6      2023-12-08 [1] CRAN (R 4.3.1)
 farver             2.1.1      2022-07-06 [1] CRAN (R 4.3.0)
 fastmap            1.1.1      2023-02-24 [1] CRAN (R 4.3.0)
 foreach            1.5.2      2022-02-02 [1] CRAN (R 4.3.0)
 fs                 1.6.3      2023-07-20 [1] CRAN (R 4.3.0)
 generics           0.1.3      2022-07-05 [1] CRAN (R 4.3.0)
 GenomeInfoDb     * 1.36.4     2023-10-08 [1] Bioconductor
 GenomeInfoDbData   1.2.10     2023-09-13 [1] Bioconductor
 ggExtra          * 0.10.1     2023-08-21 [1] CRAN (R 4.3.0)
 ggfun              0.1.4      2024-01-19 [1] CRAN (R 4.3.1)
 ggnewscale       * 0.4.10     2024-02-08 [1] CRAN (R 4.3.1)
 ggplot2          * 3.5.1      2024-04-23 [1] CRAN (R 4.3.1)
 ggplotify          0.1.2      2023-08-09 [1] CRAN (R 4.3.0)
 ggridges         * 0.5.6      2024-01-23 [1] CRAN (R 4.3.1)
 ggtree           * 3.8.2      2023-07-30 [1] Bioconductor
 ggtreeExtra      * 1.10.0     2023-04-25 [1] Bioconductor
 glmnet           * 4.1-8      2023-08-22 [1] CRAN (R 4.3.0)
 glue               1.7.0      2024-01-09 [1] CRAN (R 4.3.1)
 gridExtra        * 2.3        2017-09-09 [1] CRAN (R 4.3.0)
 gridGraphics       0.5-1      2020-12-13 [1] CRAN (R 4.3.0)
 gtable             0.3.5      2024-04-22 [1] CRAN (R 4.3.1)
 htmltools          0.5.8.1    2024-04-04 [1] CRAN (R 4.3.1)
 htmlwidgets        1.6.4      2023-12-06 [1] CRAN (R 4.3.1)
 httpuv             1.6.15     2024-03-26 [1] CRAN (R 4.3.1)
 igraph           * 2.0.3      2024-03-13 [1] CRAN (R 4.3.1)
 IRanges          * 2.34.1     2023-07-02 [1] Bioconductor
 iterators          1.0.14     2022-02-05 [1] CRAN (R 4.3.0)
 janeaustenr        1.0.0      2022-08-26 [1] CRAN (R 4.3.0)
 jsonlite           1.8.8      2023-12-04 [1] CRAN (R 4.3.1)
 knitr            * 1.45       2023-10-30 [1] CRAN (R 4.3.1)
 labeling           0.4.3      2023-08-29 [1] CRAN (R 4.3.0)
 later              1.3.2      2023-12-06 [1] CRAN (R 4.3.1)
 lattice            0.22-6     2024-03-20 [1] CRAN (R 4.3.1)
 lazyeval           0.2.2      2019-03-15 [1] CRAN (R 4.3.0)
 lifecycle          1.0.4      2023-11-07 [1] CRAN (R 4.3.1)
 magrittr           2.0.3      2022-03-30 [1] CRAN (R 4.3.0)
 MASS               7.3-60.0.1 2024-01-13 [1] CRAN (R 4.3.1)
 Matrix           * 1.6-5      2024-01-11 [1] CRAN (R 4.3.1)
 matrixStats      * 1.3.0      2024-04-11 [1] CRAN (R 4.3.1)
 memoise            2.0.1      2021-11-26 [1] CRAN (R 4.3.0)
 mime               0.12       2021-09-28 [1] CRAN (R 4.3.0)
 miniUI             0.1.1.1    2018-05-18 [1] CRAN (R 4.3.0)
 munsell            0.5.1      2024-04-01 [1] CRAN (R 4.3.1)
 naturalsort        0.1.3      2016-08-30 [1] CRAN (R 4.3.0)
 nlme               3.1-164    2023-11-27 [1] CRAN (R 4.3.1)
 patchwork        * 1.2.0      2024-01-08 [1] CRAN (R 4.3.1)
 pheatmap         * 1.0.12     2019-01-04 [1] CRAN (R 4.3.0)
 pillar             1.9.0      2023-03-22 [1] CRAN (R 4.3.0)
 pkgbuild           1.4.4      2024-03-17 [1] CRAN (R 4.3.1)
 pkgconfig          2.0.3      2019-09-22 [1] CRAN (R 4.3.0)
 pkgload            1.3.4      2024-01-16 [1] CRAN (R 4.3.1)
 plyr               1.8.9      2023-10-02 [1] CRAN (R 4.3.1)
 png                0.1-8      2022-11-29 [1] CRAN (R 4.3.0)
 profvis            0.3.8      2023-05-02 [1] CRAN (R 4.3.0)
 promises           1.3.0      2024-04-05 [1] CRAN (R 4.3.1)
 pscl             * 1.5.9      2024-01-31 [1] CRAN (R 4.3.1)
 purrr            * 1.0.2      2023-08-10 [1] CRAN (R 4.3.0)
 R6                 2.5.1      2021-08-19 [1] CRAN (R 4.3.0)
 ragg               1.3.0      2024-03-13 [1] CRAN (R 4.3.1)
 RColorBrewer     * 1.1-3      2022-04-03 [1] CRAN (R 4.3.0)
 Rcpp             * 1.0.13     2024-07-17 [1] CRAN (R 4.3.3)
 RCurl              1.98-1.14  2024-01-09 [1] CRAN (R 4.3.1)
 remotes            2.5.0      2024-03-17 [1] CRAN (R 4.3.1)
 reshape          * 0.8.9      2022-04-12 [1] CRAN (R 4.3.0)
 reshape2         * 1.4.4      2020-04-09 [1] CRAN (R 4.3.0)
 reticulate       * 1.36.1     2024-04-22 [1] CRAN (R 4.3.1)
 rlang              1.1.3      2024-01-10 [1] CRAN (R 4.3.1)
 rmarkdown          2.26       2024-03-05 [1] CRAN (R 4.3.1)
 ROCR             * 1.0-11     2020-05-02 [1] CRAN (R 4.3.0)
 RSpectra           0.16-1     2022-04-24 [1] CRAN (R 4.3.0)
 rstudioapi         0.16.0     2024-03-24 [1] CRAN (R 4.3.1)
 S4Vectors        * 0.38.2     2023-09-24 [1] Bioconductor
 scales           * 1.3.0      2023-11-28 [1] CRAN (R 4.3.1)
 seqinr           * 4.2-36     2023-12-08 [1] CRAN (R 4.3.1)
 sessioninfo        1.2.2      2021-12-06 [1] CRAN (R 4.3.0)
 shape              1.4.6.1    2024-02-23 [1] CRAN (R 4.3.1)
 shiny              1.8.1.1    2024-04-02 [1] CRAN (R 4.3.1)
 SnowballC          0.7.1      2023-04-25 [1] CRAN (R 4.3.0)
 stringi          * 1.8.3      2023-12-11 [1] CRAN (R 4.3.1)
 stringr          * 1.5.1      2023-11-14 [1] CRAN (R 4.3.1)
 survival           3.5-8      2024-02-14 [1] CRAN (R 4.3.1)
 systemfonts        1.0.6      2024-03-07 [1] CRAN (R 4.3.1)
 textshaping        0.3.7      2023-10-09 [1] CRAN (R 4.3.1)
 tibble             3.2.1      2023-03-20 [1] CRAN (R 4.3.0)
 tidyr            * 1.3.1      2024-01-24 [1] CRAN (R 4.3.1)
 tidyselect         1.2.1      2024-03-11 [1] CRAN (R 4.3.1)
 tidytext         * 0.4.2      2024-04-10 [1] CRAN (R 4.3.1)
 tidytree         * 0.4.6      2023-12-12 [1] CRAN (R 4.3.1)
 tokenizers         0.3.0      2022-12-22 [1] CRAN (R 4.3.0)
 treeio             1.24.3     2023-07-30 [1] Bioconductor
 urlchecker         1.0.1      2021-11-30 [1] CRAN (R 4.3.0)
 usethis          * 2.2.3      2024-02-19 [1] CRAN (R 4.3.1)
 utf8               1.2.4      2023-10-22 [1] CRAN (R 4.3.1)
 vctrs              0.6.5      2023-12-01 [1] CRAN (R 4.3.1)
 viridis          * 0.6.5      2024-01-29 [1] CRAN (R 4.3.1)
 viridisLite      * 0.4.2      2023-05-02 [1] CRAN (R 4.3.0)
 withr              3.0.0      2024-01-16 [1] CRAN (R 4.3.1)
 xfun               0.43       2024-03-25 [1] CRAN (R 4.3.1)
 xtable             1.8-4      2019-04-21 [1] CRAN (R 4.3.0)
 XVector          * 0.40.0     2023-05-08 [1] Bioconductor
 yaml               2.3.8      2023-12-11 [1] CRAN (R 4.3.1)
 yulab.utils        0.1.4      2024-01-28 [1] CRAN (R 4.3.1)
 zlibbioc           1.46.0     2023-05-08 [1] Bioconductor
 zoo              * 1.8-12     2023-04-13 [1] CRAN (R 4.3.0)

 [1] /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/library

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
LS0tCnRpdGxlOiAiUmVzaXN0YW5jZSBBbmFseXNpcyIKYXV0aG9yOiAnQXV0aG9yczogW0thcmwgSi4gUm9tYW5vd2ljel0oaHR0cHM6Ly9rcm9tYW5vd2ljei5naXRodWIuaW8vKSwgQ2FybWVuIFJlc25pY2ssIFNhbXVlbCBSLiBIaW50b24sIENhbGluIFBsZXNhJwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRoZW1lOiBzcGFjZWxhYgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IHllcwogICAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogJzUnCiAgICBkZl9wcmludDogcGFnZWQKICBwZGZfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAnNScKLS0tCgoqKlIgTm90ZWJvb2s6KiogPGZvbnQgY29sb3I9ImdyZWVuIj5Qcm92aWRlcyByZXByb2R1Y2libGUgYW5hbHlzaXMgZm9yICoqUmVzaXN0YW50IFRheGEqKiBpbiB0aGUgZm9sbG93aW5nIG1hbnVzY3JpcHQ6PC9mb250PgoKKipDaXRhdGlvbjoqKiBSb21hbm93aWN6IEtKLCBSZXNuaWNrIEMsIEhpbnRvbiBTUiwgUGxlc2EgQy4gRXhwbG9yaW5nIGFudGliaW90aWMgcmVzaXN0YW5jZSBpbiBkaXZlcnNlIGhvbW9sb2dzIG9mIHRoZSBkaWh5ZHJvZm9sYXRlIHJlZHVjdGFzZSBwcm90ZWluIGZhbWlseSB0aHJvdWdoIGJyb2FkIG11dGF0aW9uYWwgc2Nhbm5pbmcuICoqKmJpb1J4aXYqKiosIDIwMjUuIFtdKCkKCioqR2l0SHViIFJlcG9zaXRvcnk6KiogW2h0dHBzOi8vZ2l0aHViLmNvbS9QbGVzYUxhYi9ESEZSXShodHRwczovL2dpdGh1Yi5jb20vUGxlc2FMYWIvREhGUikKCioqTkNCSSBCaW9Qcm9qZWN0OioqIFtodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L2Jpb3Byb2plY3QvMTE4OTQ3OF0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9iaW9wcm9qZWN0LzExODk0NzgpCgojIEV4cGVyaW1lbnQKClRoaXMgcGlwZWxpbmUgcHJvY2Vzc2VzIGEgbGlicmFyeSBvZiAxLDUzNiBESEZSIGhvbW9sb2dzIGFuZCB0aGVpciBhc3NvY2lhdGVkIG11dGFudHMsIHdpdGggdHdvLWZvbGQgcmVkdW5kYW5jeSAodHdvIGNvZG9uIHZhcmlhbnRzIHBlciBzZXF1ZW5jZSkuIEZpdG5lc3Mgc2NvcmVzIGFyZSBkZXJpdmVkIGZyb20gYSBtdWx0aXBsZXhlZCBpbi12aXZvIGFzc2F5IHVzaW5nIGEgdHJpbWV0aG9wcmltIGNvbmNlbnRyYXRpb24gZ3JhZGllbnQsIGFzc2Vzc2luZyB0aGUgYWJpbGl0eSBvZiB0aGVzZSBob21vbG9ncyBhbmQgdGhlaXIgbXV0YW50cyB0byBjb21wbGVtZW50IGZ1bmN0aW9uYWxpdHkgaW4gYW4gKkUuIGNvbGkqIGtub2Nrb3V0IHN0cmFpbiBhbmQgdGhlaXIgdG9sZXJhbmNlIHRvIHRyaW1ldGhvcHJpbSB0cmVhdG1lbnQuIFRoaXMgYW5hbHlzaXMgcHJvdmlkZXMgaW5zaWdodHMgaW50byBob3cgYW50aWJpb3RpYyByZXNpc3RhbmNlIGV2b2x2ZXMgYWNyb3NzIGEgcmFuZ2Ugb2YgZXZvbHV0aW9uYXJ5IHN0YXJ0aW5nIHBvaW50cy4gU2VxdWVuY2UgZGF0YSB3ZXJlIGdlbmVyYXRlZCB1c2luZyB0aGUgSWxsdW1pbmEgTm92YVNlcSBwbGF0Zm9ybSB3aXRoIDEwMCBicCBwYWlyZWQtZW5kIHNlcXVlbmNpbmcgb2YgYW1wbGljb25zLgoKIVtNZXRob2RzIG92ZXJ2aWV3IHRvIGFjaGlldmUgYSBicm9hZC1tdXRhdGlvbmFsIHNjYW4gZm9yIERIRlIgaG9tb2xvZ3MuXShJbWFnZXMvREhGUi5EaWFncmFtLnBuZykKCmBgYHtjc3N9Ci5iYWRDb2RlIHsKYmFja2dyb3VuZC1jb2xvcjogbGlnaHRwaW5rOwpmb250LXdlaWdodDogYm9sZDsKfQoKLmdvb2RDb2RlIHsKYmFja2dyb3VuZC1jb2xvcjogbGlnaHRncmVlbjsKZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KCi5zaGFyZWRDb2RlIHsKYmFja2dyb3VuZC1jb2xvcjogbGlnaHRibHVlOwpmb250LXdlaWdodDogYm9sZDsKfQoKdGFibGUgewogIG1hcmdpbjogYXV0bzsKICBib3JkZXItdG9wOiAxcHggc29saWQgIzY2NjsKICBib3JkZXItYm90dG9tOiAxcHggc29saWQgIzY2NjsKfQp0YWJsZSB0aGVhZCB0aCB7IGJvcmRlci1ib3R0b206IDFweCBzb2xpZCAjZGRkOyB9CnRoLCB0ZCB7IHBhZGRpbmc6IDVweDsgfQp0aGVhZCwgdGZvb3QsIHRyOm50aC1jaGlsZChldmVuKSB7IGJhY2tncm91bmQ6ICNlZWU7IH0KYGBgCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KIyBTZXQgZ2xvYmFsIG9wdGlvbnMgZm9yIG5vdGVib29rCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KHdhcm5pbmcgPSBUUlVFLCBtZXNzYWdlID0gVFJVRSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBjbGFzcy5zb3VyY2UgPSAiYmctc3VjY2VzcyIpCgojIEdldHRpbmcgdGhlIHBhdGggb2YgeW91ciBjdXJyZW50IG9wZW4gZmlsZSBhbmQgc2V0IGFzIHdkCmN1cnJlbnRfcGF0aCA9IHJzdHVkaW9hcGk6OmdldEFjdGl2ZURvY3VtZW50Q29udGV4dCgpJHBhdGggCnNldHdkKGRpcm5hbWUoY3VycmVudF9wYXRoKSkKcHJpbnQoZ2V0d2QoKSkKYGBgCgojIFBhY2thZ2VzClRoZSBmb2xsb3dpbmcgUiBwYWNrYWdlcyBtdXN0IGJlIGluc3RhbGxlZCBwcmlvciB0byBsb2FkaW5nIGludG8gdGhlIFIgc2Vzc2lvbi4gU2VlIHRoZSAqKlJlcHJvZHVjaWJpbGl0eSoqIHRhYiBmb3IgYSBjb21wbGV0ZSBsaXN0IG9mIHBhY2thZ2VzIGFuZCB0aGVpciB2ZXJzaW9ucyB1c2VkIGluIHRoaXMgd29ya2Zsb3cuCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQojIExvYWQgdGhlIGxhdGVzdCB2ZXJzaW9uIG9mIHB5dGhvbiAoMy4xMC4xNCkgZm9yIGRvd25zdHJlYW0gdXNlOgpsaWJyYXJ5KHJldGljdWxhdGUpCnVzZV9weXRob24oIi9Vc2Vycy9rcm9tL21pbmlmb3JnZTMvYmluL3B5dGhvbjMiKQoKIyBNYWtlIGEgdmVjdG9yIG9mIHJlcXVpcmVkIHBhY2thZ2VzCnJlcXVpcmVkLnBhY2thZ2VzIDwtIGMoImFwZSIsICJiaW8zZCIsICJCaW9zdHJpbmdzIiwgImNhc3RvciIsICJjb3dwbG90IiwgImRldnRvb2xzIiwgImRwbHlyIiwgImdnRXh0cmEiLCAiZ2duZXdzY2FsZSIsICJnZ3Bsb3QyIiwgImdncmlkZ2VzIiwgImdndHJlZSIsICJnZ3RyZWVFeHRyYSIsICJnbG1uZXQiLCAiZ3JpZEV4dHJhIiwiaWdyYXBoIiwgImtuaXRyIiwgIm1hdHJpeFN0YXRzIiwgInBhdGNod29yayIsICJwaGVhdG1hcCIsICJwdXJyciIsICJwc2NsIiwgIlJDb2xvckJyZXdlciIsICJyZXNoYXBlIiwicmVzaGFwZTIiLCAiUk9DUiIsICJzZXFpbnIiLCAic2NhbGVzIiwgInN0cmluZ3IiLCAic3RyaW5naSIsICJ0aWR5ciIsICJ0aWR5dHJlZSIsICJ2aXJpZGlzIiwgInpvbyIpCgojIExvYWQgcmVxdWlyZWQgcGFja2FnZXMgd2l0aCBlcnJvciBoYW5kbGluZwpsb2FkZWQucGFja2FnZXMgPC0gbGFwcGx5KHJlcXVpcmVkLnBhY2thZ2VzLCBmdW5jdGlvbihwYWNrYWdlKSB7CiAgaWYgKCFyZXF1aXJlKHBhY2thZ2UsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpIHsKICAgIGluc3RhbGwucGFja2FnZXMocGFja2FnZSwgZGVwZW5kZW5jaWVzID0gVFJVRSkKICAgIGlmICghcmVxdWlyZShwYWNrYWdlLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKSB7CiAgICAgIG1lc3NhZ2UoIlBhY2thZ2UgIiwgcGFja2FnZSwgIiBjb3VsZCBub3QgYmUgaW5zdGFsbGVkIGFuZCBsb2FkZWQuIikKICAgICAgcmV0dXJuKE5VTEwpCiAgICB9CiAgfQogIHJldHVybihwYWNrYWdlKQp9KQoKIyBSZW1vdmUgTlVMTCBlbnRyaWVzIGZyb20gbG9hZGVkIHBhY2thZ2VzCmxvYWRlZC5wYWNrYWdlcyA8LSBsb2FkZWQucGFja2FnZXNbIXNhcHBseShsb2FkZWQucGFja2FnZXMsIGlzLm51bGwpXQpgYGAKCmBgYHtyIGNsYXNzLm91dHB1dD0ic2hhcmVkQ29kZSIsIGVjaG89RkFMU0V9CiMgUHJpbnQgbG9hZGVkIHBhY2thZ2VzCmNhdCgiTG9hZGVkIHBhY2thZ2VzOiIsIHBhc3RlKGxvYWRlZC5wYWNrYWdlcywgY29sbGFwc2UgPSAiLCAiKSwgIlxuIikKYGBgCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQojIHNldC5zZWVkIGlzIHVzZWQgdG8gZml4IHRoZSByYW5kb20gbnVtYmVyIGdlbmVyYXRpb24gdG8gbWFrZSB0aGUgcmVzdWx0cyByZXBlYXRhYmxlCnNldC5zZWVkKDEyMykKYGBgCgojIEltcG9ydCBEYXRhIEZpbGVzCgpJbXBvcnQgKipQRVJGRUNUUyoqIGZpbGVzIGdlbmVyYXRlZCBmcm9tIFtESEZSLjMuUGVyZmVjdHMuUk1EXShodHRwczovL2dpdGh1Yi5jb20vUGxlc2FMYWIvREhGUikKYGBge3J9CiMgRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZApGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkIDwtIHJlYWQuY3N2KCJQZXJmZWN0cy9wZXJmZWN0c19maWxlc19mb3JtYXR0ZWQvRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZC5jc3YiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCiMgbmNiaV90YXhhCm5jYmlfdGF4YSA8LSByZWFkLmNzdigiUGVyZmVjdHMvcGVyZmVjdHNfZmlsZXNfZm9ybWF0dGVkL25jYmlfdGF4YS5jc3YiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKYGBgCgpJbXBvcnQgKipNVVRBTlRTKiogZmlsZXMgZ2VuZXJhdGVkIGZyb20gW0RIRlIuNC5NdXRhbnRzLlJNRF0oaHR0cHM6Ly9naXRodWIuY29tL1BsZXNhTGFiL0RIRlIpCmBgYHtyfQojIEFsbHRyZWUxNV90YXhhX21lcmdlZApBbGx0cmVlMTVfdGF4YV9tZXJnZWQgPC0gcmVhZC5jc3YoIk11dGFudHMvbXV0YW50c19maWxlc19mb3JtYXR0ZWQvQWxsdHJlZTE1X3RheGFfbWVyZ2VkLmNzdiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKIyBwZXJmZWN0c18xNV8xNl81QkNzX3RyZWUKcGVyZmVjdHNfMTVfMTZfNUJDc190cmVlIDwtIHJlYWQuY3N2KCJNdXRhbnRzL211dGFudHNfZmlsZXNfZm9ybWF0dGVkL3BlcmZlY3RzXzE1XzE2XzVCQ3NfdHJlZS5jc3YiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCiMgbXV0X2NvbGxhcHNlXzE1X2dvb2RfZmlsdGVyZWQKbXV0X2NvbGxhcHNlXzE1X2dvb2RfZmlsdGVyZWQgPC0gcmVhZC5jc3YoIk11dGFudHMvbXV0YW50c19maWxlc19mb3JtYXR0ZWQvbXV0X2NvbGxhcHNlXzE1X2dvb2RfZmlsdGVyZWQuY3N2IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgojIG9yZ2luZm8Kb3JnaW5mbyA8LSByZWFkLmNzdigiTXV0YW50cy9tdXRhbnRzX2ZpbGVzX2Zvcm1hdHRlZC9vcmdpbmZvLmNzdiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpgYGAKCiMgUmVzaXN0YW5jZSBBbmFseXNpcwoKIyMgUmVzaXN0YW50IFRheGEgYi93IENvZG9ucwoKU3RhcnQgYnkgcGxvdHRpbmcgcmVzaXN0YW50IHRheGEgYXQgNDAweCBNSUMgKDIwMCB1Zy9tTCBUTVApIHNoYXJlZCBiZXR3ZWVuIGNvZG9uIGxpYnJhcmllcwoKQWRkIGZpdG5lc3Mgc2NvcmVzIGZvciBMaWIxNSB0byB0aGUgYEFsbHRyZWUxNV90YXhhX21lcmdlZGAgb2JqZWN0IHByaW9yIHRvIHBsb3R0aW5nIChtYXkgb3IgbWF5IG5vdCB1c2UgZG93bnN0cmVhbSkKYGBge3J9CkFsbHRyZWUxNV90YXhhX21lcmdlZCA8LSBBbGx0cmVlMTVfdGF4YV9tZXJnZWQgJT4lCiAgbGVmdF9qb2luKHBlcmZlY3RzXzE1XzE2XzVCQ3NfdHJlZSAlPiUgc2VsZWN0KElELCBmaXREMDVEMDMsIGZpdEQwNkQwMywgZml0RDA3RDAzLCBmaXREMDhEMDMsIGZpdEQwOUQwMywgZml0RDEwRDAzLCBmaXREMTFEMDMpLCBieSA9ICJJRCIpCmBgYAoKIyMjIEZpdG5lc3MgRmlsdGVyaW5nCgpGaWx0ZXIgdGhlIGBBbGx0cmVlMTVfdGF4YV9tZXJnZWRgIGRhdGFzZXQgZm9yIGRpc3RpbmN0IG11dElEcyB3aXRoIGZpdG5lc3MgdmFsdWVzIGxlc3MgdGhhbiAtMSAoYmFkKSwgZ3JlYXRlciB0aGFuIC0xIChnb29kKSwgb3IgZ3JlYXRlciB0aGFuIDAgKHplcm8pIGF0IGVhY2ggVE1QOgpgYGB7cn0KIyBDb21wbGVtZW50YXRpb24gKDAtVE1QKQpCYWRGaXRfMF90bXAgPC0gQWxsdHJlZTE1X3RheGFfbWVyZ2VkICU+JQogIGZpbHRlcihmaXREMDVEMDMgPCAtMSkKR29vZEZpdF8wX3RtcCA8LSBBbGx0cmVlMTVfdGF4YV9tZXJnZWQgJT4lCiAgZmlsdGVyKGZpdEQwNUQwMyA+PSAtMSkKWmVyb0ZpdF8wX3RtcCA8LSBBbGx0cmVlMTVfdGF4YV9tZXJnZWQgJT4lCiAgZmlsdGVyKGZpdEQwNUQwMyA+PSAwKQoKIyAwLjA1OC1UTVAKQmFkRml0XzAuMDU4X3RtcCA8LSBBbGx0cmVlMTVfdGF4YV9tZXJnZWQgJT4lCiAgZmlsdGVyKGZpdEQwNkQwMyA8IC0xKQpHb29kRml0XzAuMDU4X3RtcCA8LSBBbGx0cmVlMTVfdGF4YV9tZXJnZWQgJT4lCiAgZmlsdGVyKGZpdEQwNkQwMyA+PSAtMSkKWmVyb0ZpdF8wLjA1OF90bXAgPC0gQWxsdHJlZTE1X3RheGFfbWVyZ2VkICU+JQogIGZpbHRlcihmaXREMDZEMDMgPj0gMCkKCiMgMC41LVRNUApCYWRGaXRfMC41X3RtcCA8LSBBbGx0cmVlMTVfdGF4YV9tZXJnZWQgJT4lCiAgZmlsdGVyKGZpdEQwN0QwMyA8IC0xKQpHb29kRml0XzAuNV90bXAgPC0gQWxsdHJlZTE1X3RheGFfbWVyZ2VkICU+JQogIGZpbHRlcihmaXREMDdEMDMgPj0gLTEpClplcm9GaXRfMC41X3RtcCA8LSBBbGx0cmVlMTVfdGF4YV9tZXJnZWQgJT4lCiAgZmlsdGVyKGZpdEQwN0QwMyA+PSAwKQoKIyAxLjAtVE1QCkJhZEZpdF8xLjBfdG1wIDwtIEFsbHRyZWUxNV90YXhhX21lcmdlZCAlPiUKICBmaWx0ZXIoZml0RDA4RDAzIDwgLTEpCkdvb2RGaXRfMS4wX3RtcCA8LSBBbGx0cmVlMTVfdGF4YV9tZXJnZWQgJT4lCiAgZmlsdGVyKGZpdEQwOEQwMyA+PSAtMSkKWmVyb0ZpdF8xLjBfdG1wIDwtIEFsbHRyZWUxNV90YXhhX21lcmdlZCAlPiUKICBmaWx0ZXIoZml0RDA4RDAzID49IDApCgojIDEwLVRNUApCYWRGaXRfMTBfdG1wIDwtIEFsbHRyZWUxNV90YXhhX21lcmdlZCAlPiUKICBmaWx0ZXIoZml0RDA5RDAzIDwgLTEpCkdvb2RGaXRfMTBfdG1wIDwtIEFsbHRyZWUxNV90YXhhX21lcmdlZCAlPiUKICBmaWx0ZXIoZml0RDA5RDAzID49IC0xKQpaZXJvRml0XzEwX3RtcCA8LSBBbGx0cmVlMTVfdGF4YV9tZXJnZWQgJT4lCiAgZmlsdGVyKGZpdEQwOUQwMyA+PSAwKQoKIyA1MC1UTVAKQmFkRml0XzUwX3RtcCA8LSBBbGx0cmVlMTVfdGF4YV9tZXJnZWQgJT4lCiAgZmlsdGVyKGZpdEQxMEQwMyA8IC0xKQpHb29kRml0XzUwX3RtcCA8LSBBbGx0cmVlMTVfdGF4YV9tZXJnZWQgJT4lCiAgZmlsdGVyKGZpdEQxMEQwMyA+PSAtMSkKWmVyb0ZpdF81MF90bXAgPC0gQWxsdHJlZTE1X3RheGFfbWVyZ2VkICU+JQogIGZpbHRlcihmaXREMTBEMDMgPj0gMCkKCiMgMjAwLVRNUApCYWRGaXRfMjAwX3RtcCA8LSBBbGx0cmVlMTVfdGF4YV9tZXJnZWQgJT4lCiAgZmlsdGVyKGZpdEQxMUQwMyA8IC0xKQpHb29kRml0XzIwMF90bXAgPC0gQWxsdHJlZTE1X3RheGFfbWVyZ2VkICU+JQogIGZpbHRlcihmaXREMTFEMDMgPj0gLTEpClplcm9GaXRfMjAwX3RtcCA8LSBBbGx0cmVlMTVfdGF4YV9tZXJnZWQgJT4lCiAgZmlsdGVyKGZpdEQxMUQwMyA+PSAwKQpgYGAKCiMjIyBSZXNpc3RhbmNlIFN1bW1hcnkgVGFibGUKClN1bW1hcml6ZSB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBtdXRJRHMgd2l0aCBmaXRuZXNzIHZhbHVlcyA8IC0xIChiYWQpLCA+PSAtMSAoZ29vZCksIGFuZCA+PSAwICh6ZXJvKToKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CmZpdF9jb3VudHMgPC0gZGF0YS5mcmFtZSgKICAiQ29uZGl0aW9uIiA9IGMoIkNvbXBsZW1lbnRhdGlvbiAoMC1UTVApIiwgIjAuMDU4LVRNUCIsICIwLjUtVE1QIiwgIjEuMC1UTVAiLCAiMTAtVE1QIiwgIjUwLVRNUCIsICIyMDAtVE1QIiksCiAgIm11dElEc19iYWQiID0gYyhucm93KEJhZEZpdF8wX3RtcCksIG5yb3coQmFkRml0XzAuMDU4X3RtcCksIG5yb3coQmFkRml0XzAuNV90bXApLAogICAgICAgICAgICAgICAgIG5yb3coQmFkRml0XzEuMF90bXApLG5yb3coQmFkRml0XzEwX3RtcCksIG5yb3coQmFkRml0XzUwX3RtcCksIG5yb3coQmFkRml0XzIwMF90bXApKSwKICAibXV0SURzX2dvb2QiID0gYyhucm93KEdvb2RGaXRfMF90bXApLCBucm93KEdvb2RGaXRfMC4wNThfdG1wKSwgbnJvdyhHb29kRml0XzAuNV90bXApLAogICAgICAgICAgICAgICAgIG5yb3coR29vZEZpdF8xLjBfdG1wKSxucm93KEdvb2RGaXRfMTBfdG1wKSwgbnJvdyhHb29kRml0XzUwX3RtcCksIG5yb3coR29vZEZpdF8yMDBfdG1wKSksCiAgIm11dElEc196ZXJvIiA9IGMobnJvdyhaZXJvRml0XzBfdG1wKSwgbnJvdyhaZXJvRml0XzAuMDU4X3RtcCksIG5yb3coWmVyb0ZpdF8wLjVfdG1wKSwgCiAgICAgICAgICAgICAgICAgbnJvdyhaZXJvRml0XzEuMF90bXApLCBucm93KFplcm9GaXRfMTBfdG1wKSwgbnJvdyhaZXJvRml0XzUwX3RtcCksIG5yb3coWmVyb0ZpdF8yMDBfdG1wKSkpCgpmaXRfY291bnRzCmBgYAoKIyMjIFJlc2lzdGFuY2UgUGxvdHMKClBsb3QgYXQgdGhlICoqUEhZTFVNKiogbGV2ZWw6CmBgYHtyIHdhcm5pbmc9RkFMU0UsIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQpsaWJyYXJ5KHRpZHl0ZXh0KQoKbWluKEdvb2RGaXRfMjAwX3RtcCRmaXREMTFEMDMpCm1heChHb29kRml0XzIwMF90bXAkZml0RDExRDAzKQoKIyBDcmVhdGUgYSBsb25nIGZvcm1hdCBkYXRhIGZyYW1lCkdvb2RGaXRfMjAwX3RtcF9waHlsdW1fZGF0YSA8LSBHb29kRml0XzIwMF90bXAgJT4lCiAgbXV0YXRlKE5DQkkuZ2VudXMgPSBpZl9lbHNlKGlzLm5hKE5DQkkuZ2VudXMpLCBOQ0JJLmZhbWlseSwgTkNCSS5nZW51cykpICU+JQogIG11dGF0ZShOQ0JJLnNwZWNpZXMgPSBpZl9lbHNlKGlzLm5hKE5DQkkuc3BlY2llcyksIE5DQkkuZ2VudXMsIE5DQkkuc3BlY2llcykpICU+JQogIHNlbGVjdChJRCwgTkNCSS5waHlsdW0sIE5DQkkuZ2VudXMsIE5DQkkuc3BlY2llcywgZml0RDA1RDAzLCBmaXREMDZEMDMsIGZpdEQwN0QwMywgZml0RDA4RDAzLCBmaXREMDlEMDMsIGZpdEQxMEQwMywgZml0RDExRDAzKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0c193aXRoKCJmaXREIiksIG5hbWVzX3RvID0gIkNvbmRpdGlvbiIsIHZhbHVlc190byA9ICJGaXRuZXNzIikKCiMgUGxvdCB0aGUgbGluZSBncmFwaHMKR29vZEZpdF8yMDBfdG1wX3BoeWx1bV9wbG90IDwtIGdncGxvdChHb29kRml0XzIwMF90bXBfcGh5bHVtX2RhdGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBDb25kaXRpb24sIHkgPSBGaXRuZXNzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cCA9IElELCBjb2xvciA9IHJlb3JkZXJfd2l0aGluKE5DQkkuc3BlY2llcywgTkNCSS5waHlsdW0sIE5DQkkucGh5bHVtKSkpICsKICBnZW9tX2xpbmUobGluZXdpZHRoID0gMC43NSkgKwogIGZhY2V0X3dyYXAofk5DQkkucGh5bHVtLCBuY29sID0gMywgc2NhbGVzID0gImZyZWVfeSIpICsKICB0aGVtZV9idygpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEsIHZqdXN0ID0gMC41KSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGMoImZpdEQwNUQwMyIgPSAiMC1UTVAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZml0RDA2RDAzIiA9ICIwLjA1OC1UTVAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZml0RDA3RDAzIiA9ICIwLjUtVE1QIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImZpdEQwOEQwMyIgPSAiMS4wLVRNUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmaXREMDlEMDMiID0gIjEwLVRNUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmaXREMTBEMDMiID0gIjUwLVRNUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmaXREMTFEMDMiID0gIjIwMC1UTVAiKSkgKwogICNjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoLTYsIDgpKSArCiAgI2dlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtMSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIikgKwogIGdndGl0bGUoIlRheG9ub21pYyBSZXNpc3RhbmNlIHRvIFRyaW1ldGhvcHJpbSIpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobmNvbCA9IDEpKSArCiAgbGFicyhjb2xvciA9ICJTcGVjaWVzIChtdXRJRCkiKQoKR29vZEZpdF8yMDBfdG1wX3BoeWx1bV9wbG90CmBgYAoKYGBge3IgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRX0KZ2dzYXZlKGZpbGU9IlJlc2lzdGFuY2UvUExPVFMvU2hhcmVkL0xpYjE1LjE2LnBlcmZlY3RzLk5DQkkucGh5bHVtLnBvc2l0aXZlLmZpdG5lc3MuMjAwLnRtcC5wbmciLAogICAgICAgcGxvdD1Hb29kRml0XzIwMF90bXBfcGh5bHVtX3Bsb3QsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDcuNSwgaGVpZ2h0ID0gNy41LCB1bml0cyA9ICJpbiIpCmBgYAoKUGxvdCBhdCB0aGUgKipDTEFTUyoqIGxldmVsOgpgYGB7ciB3YXJuaW5nPUZBTFNFfQojIENyZWF0ZSBhIGxvbmcgZm9ybWF0IGRhdGEgZnJhbWUKR29vZEZpdF8yMDBfdG1wX2NsYXNzX2RhdGEgPC0gR29vZEZpdF8yMDBfdG1wICU+JQogIG11dGF0ZShOQ0JJLmdlbnVzID0gaWZfZWxzZShpcy5uYShOQ0JJLmdlbnVzKSwgTkNCSS5mYW1pbHksIE5DQkkuZ2VudXMpKSAlPiUKICBtdXRhdGUoTkNCSS5zcGVjaWVzID0gaWZfZWxzZShpcy5uYShOQ0JJLnNwZWNpZXMpLCBOQ0JJLmdlbnVzLCBOQ0JJLnNwZWNpZXMpKSAlPiUKICBzZWxlY3QoSUQsIE5DQkkuY2xhc3MsIE5DQkkuZ2VudXMsIE5DQkkuc3BlY2llcywgZml0RDA1RDAzLCBmaXREMDZEMDMsIGZpdEQwN0QwMywgZml0RDA4RDAzLCBmaXREMDlEMDMsIGZpdEQxMEQwMywgZml0RDExRDAzKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0c193aXRoKCJmaXREIiksIG5hbWVzX3RvID0gIkNvbmRpdGlvbiIsIHZhbHVlc190byA9ICJGaXRuZXNzIikKCiMgUGxvdCB0aGUgbGluZSBncmFwaHMKR29vZEZpdF8yMDBfdG1wX2NsYXNzX3Bsb3QgPC0gZ2dwbG90KEdvb2RGaXRfMjAwX3RtcF9jbGFzc19kYXRhLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBDb25kaXRpb24sIHkgPSBGaXRuZXNzLCBncm91cCA9IElELCBjb2xvciA9IE5DQkkuc3BlY2llcykpICsKICBnZW9tX2xpbmUobGluZXdpZHRoID0gMC43NSkgKwogIGZhY2V0X3dyYXAofk5DQkkuY2xhc3MsIG5jb2wgPSAzLCBzY2FsZXMgPSAiZnJlZV95IikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSwgdmp1c3QgPSAwLjUpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGMoImZpdEQwNUQwMyIgPSAiMC1UTVAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZml0RDA2RDAzIiA9ICIwLjA1OC1UTVAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZml0RDA3RDAzIiA9ICIwLjUtVE1QIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImZpdEQwOEQwMyIgPSAiMS4wLVRNUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmaXREMDlEMDMiID0gIjEwLVRNUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmaXREMTBEMDMiID0gIjUwLVRNUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmaXREMTFEMDMiID0gIjIwMC1UTVAiKSkgKwogICNjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoLTEwLCAxMCkpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtMSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIikgKwogIGdndGl0bGUoIlRheG9ub21pYyBSZXNpc3RhbmNlIGF0IENsYXNzLWxldmVsIikgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChuY29sID0gMSkpICsKICBsYWJzKGNvbG9yID0gIlNwZWNpZXMgKG11dElEKSIpCgpHb29kRml0XzIwMF90bXBfY2xhc3NfcGxvdApgYGAKCmBgYHtyIGVjaG89RkFMU0V9Cmdnc2F2ZShmaWxlPSJSZXNpc3RhbmNlL1BMT1RTL1NoYXJlZC9MaWIxNS4xNi5wZXJmZWN0cy5OQ0JJLmNsYXNzLnBvc2l0aXZlLmZpdG5lc3MuMjAwLnRtcC5wbmciLAogICAgICAgcGxvdD1Hb29kRml0XzIwMF90bXBfY2xhc3NfcGxvdCwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gNiwgaGVpZ2h0ID0gOCwgdW5pdHMgPSAiaW4iKQpgYGAKClBsb3QgYXQgdGhlICoqR0VOVVMqKiBsZXZlbDoKYGBge3Igd2FybmluZz1GQUxTRX0KIyBDcmVhdGUgYSBsb25nIGZvcm1hdCBkYXRhIGZyYW1lCkdvb2RGaXRfMjAwX3RtcF9kYXRhIDwtIEdvb2RGaXRfMjAwX3RtcCAlPiUKICBtdXRhdGUoTkNCSS5nZW51cyA9IGlmX2Vsc2UoaXMubmEoTkNCSS5nZW51cyksIE5DQkkuZmFtaWx5LCBOQ0JJLmdlbnVzKSkgJT4lCiAgbXV0YXRlKE5DQkkuc3BlY2llcyA9IGlmX2Vsc2UoaXMubmEoTkNCSS5zcGVjaWVzKSwgTkNCSS5nZW51cywgTkNCSS5zcGVjaWVzKSkgJT4lCiAgc2VsZWN0KElELCBOQ0JJLmNsYXNzLCBOQ0JJLmdlbnVzLCBOQ0JJLnNwZWNpZXMsIGZpdEQwNUQwMywgZml0RDA2RDAzLCBmaXREMDdEMDMsIGZpdEQwOEQwMywgZml0RDA5RDAzLCBmaXREMTBEMDMsIGZpdEQxMUQwMykgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgiZml0RCIpLCBuYW1lc190byA9ICJDb25kaXRpb24iLCB2YWx1ZXNfdG8gPSAiRml0bmVzcyIpCgojIFBsb3QgdGhlIGxpbmUgZ3JhcGhzCkdvb2RGaXRfMjAwX3RtcF9wbG90IDwtIGdncGxvdChHb29kRml0XzIwMF90bXBfZGF0YSwgYWVzKHggPSBDb25kaXRpb24sIHkgPSBGaXRuZXNzLCBncm91cCA9IElELCBjb2xvciA9IE5DQkkuZ2VudXMpKSArCiAgZ2VvbV9saW5lKGxpbmV3aWR0aCA9IDAuNzUpICsKICBmYWNldF93cmFwKH5OQ0JJLmdlbnVzLCBuY29sID0gNCwgc2NhbGVzID0gImZyZWVfeSIpICsKICB0aGVtZV9idygpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEsIHZqdXN0ID0gMC41KSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGMoImZpdEQwNUQwMyIgPSAiMC1UTVAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZml0RDA2RDAzIiA9ICIwLjA1OC1UTVAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZml0RDA3RDAzIiA9ICIwLjUtVE1QIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImZpdEQwOEQwMyIgPSAiMS4wLVRNUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmaXREMDlEMDMiID0gIjEwLVRNUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmaXREMTBEMDMiID0gIjUwLVRNUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmaXREMTFEMDMiID0gIjIwMC1UTVAiKSkgKwogICNjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoLTEwLCAxMCkpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtMSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIikrCiAgZ2d0aXRsZSgiVW5pcXVlIG11dElEIFBvc2l0aXZlIEZpdG5lc3MgYXQgR2VudXMtbGV2ZWwiKQoKR29vZEZpdF8yMDBfdG1wX3Bsb3QKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iUmVzaXN0YW5jZS9QTE9UUy9TaGFyZWQvTGliMTUuMTYucGVyZmVjdHMuTkNCSS5nZW51cy5wb3NpdGl2ZS5maXRuZXNzLjIwMC50bXAucG5nIiwKICAgICAgIHBsb3Q9R29vZEZpdF8yMDBfdG1wX3Bsb3QsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDYsIGhlaWdodCA9IDgsIHVuaXRzID0gImluIikKYGBgCgojIyBSZXNpc3RhbnQgVGF4YSBDb2RvbiAxCgpSZXRhaW4gdGhlIHJlc2lzdGFudCB0YXhhIGF0IDQwMHggTUlDIGZvciBDb2RvbiAxIChMaWIxNSk6CmBgYHtyfQojIFJldGFpbiBhbGwgcmVzaXN0YW50IHRheGEgYXQgNDAweCBNSUMgKGZpdG5lc3MgPiAtMSkKRml0dHJlZTE1Z29vZC4yMDB0bXAucmVzaXN0YW50IDwtIEZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQgJT4lCiAgZmlsdGVyKGZpdEQxMUQwMyA+IC0xKQoKIyBSZXRhaW4gYWxsIHN1c2NlcHRpYmxlIHRheGEgYXQgNDAweCBNSUMgKGZpdG5lc3MgPCAtMSkKRml0dHJlZTE1Z29vZC4yMDB0bXAuZHJvcG91dCA8LSBGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkICU+JQogIGZpbHRlcihmaXREMTFEMDMgPCAtMSkKYGBgCgojIyMgUmVzaXN0YW50IFRheGEgUGxvdCAoSUQpCgpQbG90IGluIGFuIG9yZ2FuaXplZCBtYW5uZXIgZm9yIGVhc2llciB2aXN1YWwgaW50ZXJwcmV0YXRpb246CmBgYHtyIHdhcm5pbmc9RkFMU0V9CiMgUmVzaGFwZSB0aGUgZGF0YSBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQKRml0dHJlZTE1Z29vZF9sb25nIDwtIEZpdHRyZWUxNWdvb2QuMjAwdG1wLnJlc2lzdGFudCAlPiUKICBwaXZvdF9sb25nZXIoCiAgICBjb2xzID0gc3RhcnRzX3dpdGgoImZpdEQiKSwKICAgIG5hbWVzX3RvID0gImZpdF92YXJpYWJsZSIsCiAgICB2YWx1ZXNfdG8gPSAiZml0X3ZhbHVlIgogICkgJT4lCiAgbXV0YXRlKGZpdF92YXJpYWJsZSA9IGZhY3RvcihmaXRfdmFyaWFibGUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gcGFzdGUwKCJmaXREIiwgc3ByaW50ZigiJTAyZCIsIDU6MTEpLCAiRDAzIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCIwLjAiLCAiMC4wNTgiLCAiMC41IiwgIjEuMCIsICIxMCIsICI1MCIsICIyMDAiKSkpCgojIENyZWF0ZSB2ZWN0b3JzIG9mIElEcyB0aGF0IHlvdSB3YW50IHRvIGhpZ2hsaWdodApoaWdobGlnaHRfYmx1ZV9pZHMgPC0gYygiV1BfMDA3NjMxMTM1IiwgIldQXzAwNzY1NDg2NiIsICJXUF8wMDg1Nzg5MjQiLCAiV1BfMDA4OTc5OTk5IiwgIldQXzAwODk5MDgzMiIsICJXUF8wMTAwNzUyMTEiKQpoaWdobGlnaHRfZ3JlZW5faWRzIDwtIGMoIldQXzAwMjgzOTcxNSIsICJXUF8wMDM2ODY2NTQiLCAiV1BfMDA0NDQyNzM4IiwgIldQXzAwNzgxNzgyNSIsICJXUF8wMDgxNTI1NDUiLCAiV1BfMDA4MjM3MDc5IiwgIldQXzAwODM2OTYxOCIsICJXUF8wMDgzOTA2NDMiLCAiV1BfMDA5NzQ2NDI1IiwgIldQXzAwOTc3ODc2OCIpCmhpZ2hsaWdodF9yZWRfaWRzIDwtIGMoIk5QXzIyOTQ0MSIsICJOUF8zNTkwMjIiLCAiV1BfMDAxMzkzNTU2IiwgIldQXzAwMzQ5ODY2NyIsICJXUF8wMDM1NzUwMzMiLCAiV1BfMDA0MDI2MzU1IiwgIldQXzAwNDA2NDEwNyIsICJXUF8wMDQ4OTUyMzgiLCAiV1BfMDA2NzA5NTkzIiwgIldQXzAwODU4MzQ4MSIsICJXUF8wMTAwMjE0MjYiKQoKIyBDb21iaW5lIGFsbCBoaWdobGlnaHRlZCBJRHMgaW4gdGhlIGRlc2lyZWQgb3JkZXIKYWxsX2hpZ2hsaWdodF9pZHMgPC0gYyhoaWdobGlnaHRfYmx1ZV9pZHMsIGhpZ2hsaWdodF9ncmVlbl9pZHMsIGhpZ2hsaWdodF9yZWRfaWRzKQoKIyBBZGQgYSBuZXcgY29sdW1uIHRvIGluZGljYXRlIHdoZXRoZXIgYW5kIGhvdyB0aGUgSUQgc2hvdWxkIGJlIGhpZ2hsaWdodGVkCkZpdHRyZWUxNWdvb2RfbG9uZyA8LSBGaXR0cmVlMTVnb29kX2xvbmcgJT4lCiAgbXV0YXRlKGhpZ2hsaWdodCA9IGNhc2Vfd2hlbigKICAgIElEICVpbiUgaGlnaGxpZ2h0X2JsdWVfaWRzIH4gImJsdWUiLAogICAgSUQgJWluJSBoaWdobGlnaHRfZ3JlZW5faWRzIH4gImdyZWVuIiwKICAgIElEICVpbiUgaGlnaGxpZ2h0X3JlZF9pZHMgfiAicmVkIiwKICAgIFRSVUUgfiAibm9ybWFsIgogICkpICU+JQogICMgQ29udmVydCBJRCB0byBhIGZhY3RvciB3aXRoIHRoZSBzcGVjaWZpZWQgb3JkZXIKICBtdXRhdGUoSUQgPSBmYWN0b3IoSUQsIGxldmVscyA9IGMoYWxsX2hpZ2hsaWdodF9pZHMsIHNldGRpZmYodW5pcXVlKElEKSwgYWxsX2hpZ2hsaWdodF9pZHMpKSkpCgojIENyZWF0ZSB0aGUgZmFjZXQgd3JhcCBwbG90CkZpdHRyZWUxNWdvb2QuMjAwdG1wLnJlc2lzdGFudC5wbG90LnYyIDwtIGdncGxvdChGaXR0cmVlMTVnb29kX2xvbmcsIGFlcyh4ID0gZml0X3ZhcmlhYmxlLCB5ID0gZml0X3ZhbHVlLCBncm91cCA9IElEKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KCkgKwogIGZhY2V0X3dyYXAofiBJRCwgc2NhbGVzID0gImZyZWVfeSIsIG5jb2wgPSAzKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiksICAjIERlZmF1bHQgYmFja2dyb3VuZAogICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKQogICkgKwogIGxhYnMoeCA9ICJUcmltZXRob3ByaW0gQ29uY2VudHJhdGlvbiAodWcvbUwpIiwgeSA9ICJGaXRuZXNzIFZhbHVlIiwgdGl0bGUgPSAiRml0bmVzcyBWYWx1ZXMgYnkgSUQiKQoKIyBBZGQgaGlnaGxpZ2h0ZWQgYmFja2dyb3VuZHMgZm9yIHNwZWNpZmljIHBsb3RzCkZpdHRyZWUxNWdvb2QuMjAwdG1wLnJlc2lzdGFudC5wbG90LnYyIDwtIEZpdHRyZWUxNWdvb2QuMjAwdG1wLnJlc2lzdGFudC5wbG90LnYyICsKICBnZW9tX3JlY3QoZGF0YSA9IGZpbHRlcihGaXR0cmVlMTVnb29kX2xvbmcsIGhpZ2hsaWdodCA9PSAiYmx1ZSIpLAogICAgICAgICAgICBhZXMoeG1pbiA9IC1JbmYsIHhtYXggPSBJbmYsIHltaW4gPSAtSW5mLCB5bWF4ID0gSW5mKSwKICAgICAgICAgICAgZmlsbCA9ICJsaWdodGJsdWUiLCBhbHBoYSA9IDAuMywgaW5oZXJpdC5hZXMgPSBGQUxTRSkgKwogIGdlb21fcmVjdChkYXRhID0gZmlsdGVyKEZpdHRyZWUxNWdvb2RfbG9uZywgaGlnaGxpZ2h0ID09ICJncmVlbiIpLAogICAgICAgICAgICBhZXMoeG1pbiA9IC1JbmYsIHhtYXggPSBJbmYsIHltaW4gPSAtSW5mLCB5bWF4ID0gSW5mKSwKICAgICAgICAgICAgZmlsbCA9ICJsaWdodGdyZWVuIiwgYWxwaGEgPSAwLjMsIGluaGVyaXQuYWVzID0gRkFMU0UpICsKICBnZW9tX3JlY3QoZGF0YSA9IGZpbHRlcihGaXR0cmVlMTVnb29kX2xvbmcsIGhpZ2hsaWdodCA9PSAicmVkIiksCiAgICAgICAgICAgIGFlcyh4bWluID0gLUluZiwgeG1heCA9IEluZiwgeW1pbiA9IC1JbmYsIHltYXggPSBJbmYpLAogICAgICAgICAgICBmaWxsID0gInBpbmsiLCBhbHBoYSA9IDAuMywgaW5oZXJpdC5hZXMgPSBGQUxTRSkgKwogIGdlb21fbGluZSgpICsgICMgUmVkcmF3IGxpbmVzIHRvIGVuc3VyZSB0aGV5IGFwcGVhciBvbiB0b3Agb2YgdGhlIGNvbG9yZWQgYmFja2dyb3VuZAogIGdlb21fcG9pbnQoKSAgICMgUmVkcmF3IHBvaW50cyB0byBlbnN1cmUgdGhleSBhcHBlYXIgb24gdG9wIG9mIHRoZSBjb2xvcmVkIGJhY2tncm91bmQKCiMgQWRkIGRhc2hlZCBsaW5lIGF0IHkgPSAtMSBmb3IgZmFjZXRzIHdoZXJlIGl0J3Mgd2l0aGluIHRoZSB5LWF4aXMgcmFuZ2UKRml0dHJlZTE1Z29vZC4yMDB0bXAucmVzaXN0YW50LnBsb3QudjIgPC0gRml0dHJlZTE1Z29vZC4yMDB0bXAucmVzaXN0YW50LnBsb3QudjIgKwogIGdlb21faGxpbmUoZGF0YSA9IEZpdHRyZWUxNWdvb2RfbG9uZyAlPiUgCiAgICAgICAgICAgICAgIGdyb3VwX2J5KElEKSAlPiUgCiAgICAgICAgICAgICAgIHN1bW1hcml6ZShtaW5feSA9IG1pbihmaXRfdmFsdWUpLCBtYXhfeSA9IG1heChmaXRfdmFsdWUpKSAlPiUKICAgICAgICAgICAgICAgZmlsdGVyKG1pbl95IDw9IC0xLCBtYXhfeSA+PSAtMSksCiAgICAgICAgICAgICBhZXMoeWludGVyY2VwdCA9IC0xKSwgCiAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiLCBhbHBoYSA9IDAuNykKCiMgRGlzcGxheSB0aGUgcGxvdApwcmludChGaXR0cmVlMTVnb29kLjIwMHRtcC5yZXNpc3RhbnQucGxvdC52MikKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iUmVzaXN0YW5jZS9QTE9UUy9Db2RvbjEvTGliMTUuUGVyZmVjdHMuR29vZC41QkNzLjQwMHhNSUMuUmVzaXN0YW50LkxpbmVwbG90LnBkZiIsIHBsb3Q9Rml0dHJlZTE1Z29vZC4yMDB0bXAucmVzaXN0YW50LnBsb3QudjIsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDYsIGhlaWdodCA9IDEyLCB1bml0cyA9ICJpbiIpCmBgYAoKIyMjIFJlc2lzdGFudCBUYXhhIFBsb3QgKFRheG9uKQoKYGBge3Igd2FybmluZz1GQUxTRX0KIyBEZWZpbmUgdGhlIGRlc2lyZWQgSUQgb3JkZXIKZGVzaXJlZF9pZF9vcmRlciA8LSBjKCJXUF8wMDc2MzExMzUiLCAiV1BfMDA3NjU0ODY2IiwgIldQXzAwODU3ODkyNCIsICJXUF8wMDg5Nzk5OTkiLCAiV1BfMDEwMDc1MjExIiwgIldQXzAwODk5MDgzMiIsICAKICAgICAgICAgICAgICAgICAgICAgICJXUF8wMDgzNjk2MTgiLCAiV1BfMDA4MzkwNjQzIiwgIldQXzAwMzY4NjY1NCIsICJXUF8wMDQ0NDI3MzgiLCAiV1BfMDA3ODE3ODI1IiwgIldQXzAwODE1MjU0NSIsIAogICAgICAgICAgICAgICAgICAgICAgIldQXzAwODIzNzA3OSIsICJXUF8wMDk3NDY0MjUiLCAiV1BfMDA5Nzc4NzY4IiwgIldQXzAwMjgzOTcxNSIsICJOUF8yMjk0NDEiLCAiTlBfMzU5MDIyIiwgCiAgICAgICAgICAgICAgICAgICAgICAiV1BfMDAxMzkzNTU2IiwgIldQXzAwMzQ5ODY2NyIsICJXUF8wMDM1NzUwMzMiLCAiV1BfMDA2NzA5NTkzIiwgIldQXzAwNDA2NDEwNyIsICJXUF8wMDQ4OTUyMzgiLCAgCiAgICAgICAgICAgICAgICAgICAgICAiV1BfMDA4NTgzNDgxIiwgIldQXzAwNDAyNjM1NSIsICJXUF8wMTAwMjE0MjYiKQoKRml0dHJlZTE1Z29vZF9sb25nIDwtIEZpdHRyZWUxNWdvb2RfbG9uZyAlPiUKICBtdXRhdGUoaGlnaGxpZ2h0ID0gY2FzZV93aGVuKAogICAgSUQgJWluJSBoaWdobGlnaHRfYmx1ZV9pZHMgfiAiYmx1ZSIsCiAgICBJRCAlaW4lIGhpZ2hsaWdodF9ncmVlbl9pZHMgfiAiZ3JlZW4iLAogICAgSUQgJWluJSBoaWdobGlnaHRfcmVkX2lkcyB+ICJyZWQiLAogICAgVFJVRSB+ICJub3JtYWwiCiAgKSkgJT4lCiAgIyBDb252ZXJ0IElEIHRvIGEgZmFjdG9yIHdpdGggdGhlIHNwZWNpZmllZCBvcmRlcgogIG11dGF0ZShJRCA9IGZhY3RvcihJRCwgbGV2ZWxzID0gZGVzaXJlZF9pZF9vcmRlcikpICU+JQogICMgQ3JlYXRlIGEgbmV3IGNvbHVtbiBmb3IgZmFjZXRpbmcsIHVzaW5nIE5DQkkuY2xhc3MgaWYgTkNCSS5nZW51cyBpcyBOQQogIG11dGF0ZShmYWNldF92YXIgPSBpZmVsc2UoaXMubmEoTkNCSS5jbGFzcyksIE5DQkkubmFtZSwgTkNCSS5jbGFzcykpICU+JQogICMgQ3JlYXRlIGEgdW5pcXVlIGZhY2V0IHZhcmlhYmxlIHRoYXQgaW5jbHVkZXMgYm90aCBmYWNldF92YXIgYW5kIElECiAgbXV0YXRlKGZhY2V0X3Zhcl91bmlxdWUgPSBwYXN0ZShmYWNldF92YXIsIElELCBzZXAgPSAiXyIpKSAlPiUKICAjIE9yZGVyIHRoZSBmYWNldF92YXJfdW5pcXVlIGJhc2VkIG9uIHRoZSBJRCBvcmRlcgogIG11dGF0ZShmYWNldF92YXJfdW5pcXVlID0gZmFjdG9yKGZhY2V0X3Zhcl91bmlxdWUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IHVuaXF1ZShmYWNldF92YXJfdW5pcXVlW29yZGVyKG1hdGNoKElELCBkZXNpcmVkX2lkX29yZGVyKSldKSkpCgojIE5vdywgd2hlbiB5b3UgY3JlYXRlIHlvdXIgcGxvdCwgdXNlIGZhY2V0X3dyYXAgd2l0aCB0aGUgb3JkZXJlZCBmYWNldF92YXJfdW5pcXVlCkZpdHRyZWUxNWdvb2QuMjAwdG1wLnJlc2lzdGFudC5wbG90LnYyIDwtIGdncGxvdChGaXR0cmVlMTVnb29kX2xvbmcsIGFlcyh4ID0gZml0X3ZhcmlhYmxlLCB5ID0gZml0X3ZhbHVlLCBncm91cCA9IElEKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KCkgKwogIGZhY2V0X3dyYXAofiBmYWNldF92YXJfdW5pcXVlLCBzY2FsZXMgPSAiZnJlZV95IiwgbmNvbCA9IDMsIAogICAgICAgICAgICAgbGFiZWxsZXIgPSBsYWJlbGxlcihmYWNldF92YXJfdW5pcXVlID0gZnVuY3Rpb24oeCkgZ3N1YigiXy4qIiwgIiIsIHgpKSkgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpLAogICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKQogICkgKwogIGxhYnMoeCA9ICJUcmltZXRob3ByaW0gQ29uY2VudHJhdGlvbiAodWcvbUwpIiwgeSA9ICJGaXRuZXNzIFZhbHVlIiwgdGl0bGUgPSAiRml0bmVzcyBWYWx1ZXMgYnkgR2VudXMvQ2xhc3MiKQoKIyBBZGQgaGlnaGxpZ2h0ZWQgYmFja2dyb3VuZHMgZm9yIHNwZWNpZmljIHBsb3RzCkZpdHRyZWUxNWdvb2QuMjAwdG1wLnJlc2lzdGFudC5wbG90LnYyIDwtIEZpdHRyZWUxNWdvb2QuMjAwdG1wLnJlc2lzdGFudC5wbG90LnYyICsKICBnZW9tX3JlY3QoZGF0YSA9IGZpbHRlcihGaXR0cmVlMTVnb29kX2xvbmcsIGhpZ2hsaWdodCA9PSAiYmx1ZSIpLAogICAgICAgICAgICBhZXMoeG1pbiA9IC1JbmYsIHhtYXggPSBJbmYsIHltaW4gPSAtSW5mLCB5bWF4ID0gSW5mKSwKICAgICAgICAgICAgZmlsbCA9ICJsaWdodGJsdWUiLCBhbHBoYSA9IDAuMywgaW5oZXJpdC5hZXMgPSBGQUxTRSkgKwogIGdlb21fcmVjdChkYXRhID0gZmlsdGVyKEZpdHRyZWUxNWdvb2RfbG9uZywgaGlnaGxpZ2h0ID09ICJncmVlbiIpLAogICAgICAgICAgICBhZXMoeG1pbiA9IC1JbmYsIHhtYXggPSBJbmYsIHltaW4gPSAtSW5mLCB5bWF4ID0gSW5mKSwKICAgICAgICAgICAgZmlsbCA9ICJsaWdodGdyZWVuIiwgYWxwaGEgPSAwLjMsIGluaGVyaXQuYWVzID0gRkFMU0UpICsKICBnZW9tX3JlY3QoZGF0YSA9IGZpbHRlcihGaXR0cmVlMTVnb29kX2xvbmcsIGhpZ2hsaWdodCA9PSAicmVkIiksCiAgICAgICAgICAgIGFlcyh4bWluID0gLUluZiwgeG1heCA9IEluZiwgeW1pbiA9IC1JbmYsIHltYXggPSBJbmYpLAogICAgICAgICAgICBmaWxsID0gInBpbmsiLCBhbHBoYSA9IDAuMywgaW5oZXJpdC5hZXMgPSBGQUxTRSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KCkKCiMgQWRkIGRhc2hlZCBsaW5lIGF0IHkgPSAtMSBmb3IgZmFjZXRzIHdoZXJlIGl0J3Mgd2l0aGluIHRoZSB5LWF4aXMgcmFuZ2UKRml0dHJlZTE1Z29vZC4yMDB0bXAucmVzaXN0YW50LnBsb3QudjIgPC0gRml0dHJlZTE1Z29vZC4yMDB0bXAucmVzaXN0YW50LnBsb3QudjIgKwogIGdlb21faGxpbmUoZGF0YSA9IEZpdHRyZWUxNWdvb2RfbG9uZyAlPiUgCiAgICAgICAgICAgICAgIGdyb3VwX2J5KGZhY2V0X3Zhcl91bmlxdWUpICU+JQogICAgICAgICAgICAgICBzdW1tYXJpemUobWluX3kgPSBtaW4oZml0X3ZhbHVlKSwgbWF4X3kgPSBtYXgoZml0X3ZhbHVlKSkgJT4lCiAgICAgICAgICAgICAgIGZpbHRlcihtaW5feSA8PSAtMSwgbWF4X3kgPj0gLTEpLAogICAgICAgICAgICAgYWVzKHlpbnRlcmNlcHQgPSAtMSksIAogICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIiwgYWxwaGEgPSAwLjcpCgojIERpc3BsYXkgdGhlIHBsb3QKcHJpbnQoRml0dHJlZTE1Z29vZC4yMDB0bXAucmVzaXN0YW50LnBsb3QudjIpCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9IlJlc2lzdGFuY2UvUExPVFMvQ29kb24xL0xpYjE1LlBlcmZlY3RzLkdvb2QuNUJDcy40MDB4TUlDLlJlc2lzdGFudC5MaW5lcGxvdC5UYXhvbi5wZGYiLCBwbG90PUZpdHRyZWUxNWdvb2QuMjAwdG1wLnJlc2lzdGFudC5wbG90LnYyLAogICAgICAgZHBpPTYwMCwgd2lkdGggPSA2LCBoZWlnaHQgPSAxMiwgdW5pdHMgPSAiaW4iKQpgYGAKCiMjIyBSZXNpc3RhbnQgVGF4YSBQbG90IChNZXJnZWQpCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQojIFJlc2hhcGUgdGhlIGRhdGEgZnJvbSB3aWRlIHRvIGxvbmcgZm9ybWF0CkZpdHRyZWUxNWdvb2RfbG9uZyA8LSBGaXR0cmVlMTVnb29kLjIwMHRtcC5yZXNpc3RhbnQgJT4lCiAgcGl2b3RfbG9uZ2VyKAogICAgY29scyA9IHN0YXJ0c193aXRoKCJmaXREIiksCiAgICBuYW1lc190byA9ICJmaXRfdmFyaWFibGUiLAogICAgdmFsdWVzX3RvID0gImZpdF92YWx1ZSIKICApICU+JQogIG11dGF0ZShmaXRfdmFyaWFibGUgPSBmYWN0b3IoZml0X3ZhcmlhYmxlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IHBhc3RlMCgiZml0RCIsIHNwcmludGYoIiUwMmQiLCA1OjExKSwgIkQwMyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiMC4wIiwgIjAuMDU4IiwgIjAuNSIsICIxLjAiLCAiMTAiLCAiNTAiLCAiMjAwIikpKSAlPiUKICAjIENyZWF0ZSBhIG5ldyBjb2x1bW4gZm9yIGZhY2V0aW5nLCB1c2luZyBOQ0JJLnBoeWx1bSBpZiBOQ0JJLmNsYXNzIGlzIE5BCiAgbXV0YXRlKGZhY2V0X3ZhciA9IGlmZWxzZShpcy5uYShOQ0JJLmNsYXNzKSwgTkNCSS5uYW1lLCBOQ0JJLmNsYXNzKSkgJT4lCiAgIyBBcnJhbmdlIGFscGhhYmV0aWNhbGx5IGJ5IGZhY2V0X3ZhcgogIGFycmFuZ2UoZmFjZXRfdmFyKQoKIyBDcmVhdGUgdGhlIGZhY2V0IHdyYXAgcGxvdApGaXR0cmVlMTVnb29kLjIwMHRtcC5yZXNpc3RhbnQucGxvdC52MiA8LSBnZ3Bsb3QoRml0dHJlZTE1Z29vZF9sb25nLCBhZXMoeCA9IGZpdF92YXJpYWJsZSwgeSA9IGZpdF92YWx1ZSwgZ3JvdXAgPSBJRCwgY29sb3IgPSBJRCkpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludCgpICsKICBmYWNldF93cmFwKH4gZmFjZXRfdmFyLCBzY2FsZXMgPSAiZnJlZV95IiwgbmNvbCA9IDMpICsKICB0aGVtZV9idygpICsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiKSwKICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiksCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKQogICkgKwogIGxhYnMoeCA9ICJUcmltZXRob3ByaW0gQ29uY2VudHJhdGlvbiAodWcvbUwpIiwgeSA9ICJGaXRuZXNzIFZhbHVlIiwgCiAgICAgICB0aXRsZSA9ICJGaXRuZXNzIFZhbHVlcyBieSBDbGFzcy9QaHlsdW0iKQoKIyBBZGQgZGFzaGVkIGxpbmUgYXQgeSA9IC0xIGZvciBmYWNldHMgd2hlcmUgaXQncyB3aXRoaW4gdGhlIHktYXhpcyByYW5nZQpGaXR0cmVlMTVnb29kLjIwMHRtcC5yZXNpc3RhbnQucGxvdC52MiA8LSBGaXR0cmVlMTVnb29kLjIwMHRtcC5yZXNpc3RhbnQucGxvdC52MiArCiAgZ2VvbV9obGluZShkYXRhID0gRml0dHJlZTE1Z29vZF9sb25nICU+JSAKICAgICAgICAgICAgICAgZ3JvdXBfYnkoZmFjZXRfdmFyKSAlPiUgCiAgICAgICAgICAgICAgIHN1bW1hcml6ZShtaW5feSA9IG1pbihmaXRfdmFsdWUpLCBtYXhfeSA9IG1heChmaXRfdmFsdWUpKSAlPiUKICAgICAgICAgICAgICAgZmlsdGVyKG1pbl95IDw9IC0xLCBtYXhfeSA+PSAtMSksCiAgICAgICAgICAgICBhZXMoeWludGVyY2VwdCA9IC0xKSwgCiAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiLCBhbHBoYSA9IDAuNykKCiMgRGlzcGxheSB0aGUgcGxvdApwcmludChGaXR0cmVlMTVnb29kLjIwMHRtcC5yZXNpc3RhbnQucGxvdC52MikKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iUmVzaXN0YW5jZS9QTE9UUy9Db2RvbjEvTGliMTUuUGVyZmVjdHMuR29vZC41QkNzLjQwMHhNSUMuUmVzaXN0YW50LlRheG9uLk1lcmdlZC5wZGYiLCBwbG90PUZpdHRyZWUxNWdvb2QuMjAwdG1wLnJlc2lzdGFudC5wbG90LnYyLAogICAgICAgZHBpPTYwMCwgd2lkdGggPSA2LCBoZWlnaHQgPSAxMiwgdW5pdHMgPSAiaW4iKQpgYGAKCiMjIFRheG9uIEZpdG5lc3MgYnkgUGh5bHVtCgpUaGVyZSBpcyBjbGVhciBldmlkZW5jZSBmcm9tIG91ciBwaHlsb2dlbmV0aWMgdHJlZXMgdGhhdCByZXNpc3RhbmNlIGZvbGxvd3MgY2xhZGUtc3BlY2lmaWMgcGF0dGVybnMsIHdpdGggbW9yZSBjbGFkZXMgKGdlbnVzLWxldmVsKSBkcm9wcGluZyBvdXQgd2l0aCBpbmNyZWFzaW5nIHRyaW1ldGhvcHJpbSBjb25jZW50cmF0aW9uLiBUaGUgZm9sbG93aW5nIGdlbnVzIGVzcGVjaWFsbHk6CgotICAgQWNpbmV0b2JhY3RlcgotICAgQmFjaWxsdXMKLSAgIEJhY3Rlcm9pZGVzCi0gICBDbG9zdHJpZGl1bQotICAgTmVpc3NlcmlhCi0gICBQc2V1ZG9tb25hcwotICAgU3RyZXB0b2NvY2N1cwoKIyMjIEdvb2QgRmlsdGVyZWQgRGF0YXNldAoKRmlyc3Qgc3RlcCBpcyB0byBtYWtlIGEgY29weSBvZiB0aGUgY29tcGxlbWVudGluZyBob21vbG9nIChhbmQgdGhlaXIgbXV0YW50cykgZGF0YXNldCBhbmQgZmlsdGVyIHRvIHJldGFpbiByZWxldmVudCBjb2x1bW5zIGZvciB0aGUgZmlyc3Qgc2FtcGxpbmcgdGltZXBvaW50LiBBZGQgTkNCSSB0YXhvbm9teSBiYXNlZCBvbiBzaGFyZWQgVGF4SUQuCmBgYHtyfQojIE1ha2UgYSBjb3B5IG9mIHRoZSBmaWx0ZXJlZCBmaXRuZXNzIGRhdGEgZm9yIGhvbW9sb2dzIChhbmQgdGhlaXIgbXV0YW50cykgY2FwYWJsZSBvZiBjb21wbGVtZW50YXRpb24gKGZpdCA+IC0xIEAgZml0RDA1RDAzKQpMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCA8LSBtdXRfY29sbGFwc2VfMTVfZ29vZF9maWx0ZXJlZCAlPiUKICBzZWxlY3QoSUQsIG11dElELCBzZXEsIAogICAgICAgICBmaXREMDVEMDMsIGZpdEQwNkQwMywgZml0RDA3RDAzLCBmaXREMDhEMDMsIGZpdEQwOUQwMywgZml0RDEwRDAzLCBmaXREMTFEMDMsIAogICAgICAgICBudW1wcnVuZWRCQ3MsIG51bUJDcywgbXV0YXRpb25zLCBwY3RfaWRlbnQpCmBgYAoKQWRkICJ0YXhJRCIgYW5kICJQY3RJZGVudEVjb2xpIiB0byBlYWNoICJJRCIKYGBge3J9CiMgTWVyZ2UgIlRheElEIiBhbmQgIlBjdElkZW50RWNvbGkiIGZyb20gYG9yZ2luZm9gIHRvIGBMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZGA6CkwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkIDwtIEwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkICU+JQogIGxlZnRfam9pbihvcmdpbmZvICU+JSBzZWxlY3QoSUQsIFRheElELCBQY3RJZGVudEVjb2xpKSwgYnkgPSAiSUQiKQpgYGAKCkFkZCB0aGUgTkNCSSB0YXhvbm9teSBiYXNlZCBvbiBzaGFyZWQgVGF4SUQ6CmBgYHtyfQojIE1lcmdlIHRoZSBOQ0JJIHRheG9ub215IGNvbHVtbnMgdG8gRml0dHJlZTE1Z29vZF90YXhhIGJhc2VkIG9uIHNoYXJlZCBUYXhJRApMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCROQ0JJLm5hbWUgPC0gTkEKTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkTkNCSS5zdXBlcmtpbmdkb20gPC0gTkEKTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkTkNCSS5waHlsdW0gPC0gTkEKTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkTkNCSS5jbGFzcyA8LSBOQQpMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCROQ0JJLm9yZGVyIDwtIE5BCkwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJE5DQkkuZmFtaWx5IDwtIE5BCkwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJE5DQkkuZ2VudXMgPC0gTkEKTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkTkNCSS5zcGVjaWVzIDwtIE5BCgojIE5DQkkubmFtZQpMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCROQ0JJLm5hbWVbTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdIDwtIG5jYmlfdGF4YSROQ0JJLm5hbWVbbWF0Y2goTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkVGF4SURbTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdLCBuY2JpX3RheGEkVGF4SUQpXQoKIyBOQ0JJLnN1cGVya2luZ2RvbQpMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCROQ0JJLnN1cGVya2luZ2RvbVtMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCRUYXhJRCAlaW4lIG5jYmlfdGF4YSRUYXhJRF0gPC0gbmNiaV90YXhhJE5DQkkuc3VwZXJraW5nZG9tW21hdGNoKEwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJFRheElEW0wxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSwgbmNiaV90YXhhJFRheElEKV0KCiMgTkNCSS5waHlsdW0KTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkTkNCSS5waHlsdW1bTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdIDwtIG5jYmlfdGF4YSROQ0JJLnBoeWx1bVttYXRjaChMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCRUYXhJRFtMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCRUYXhJRCAlaW4lIG5jYmlfdGF4YSRUYXhJRF0sIG5jYmlfdGF4YSRUYXhJRCldCgojIE5DQkkuY2xhc3MKTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkTkNCSS5jbGFzc1tMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCRUYXhJRCAlaW4lIG5jYmlfdGF4YSRUYXhJRF0gPC0gbmNiaV90YXhhJE5DQkkuY2xhc3NbbWF0Y2goTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkVGF4SURbTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdLCBuY2JpX3RheGEkVGF4SUQpXQoKIyBOQ0JJLm9yZGVyCkwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJE5DQkkub3JkZXJbTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdIDwtIG5jYmlfdGF4YSROQ0JJLm9yZGVyW21hdGNoKEwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJFRheElEW0wxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSwgbmNiaV90YXhhJFRheElEKV0KCiMgTkNCSS5mYW1pbHkKTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkTkNCSS5mYW1pbHlbTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdIDwtIG5jYmlfdGF4YSROQ0JJLmZhbWlseVttYXRjaChMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCRUYXhJRFtMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCRUYXhJRCAlaW4lIG5jYmlfdGF4YSRUYXhJRF0sIG5jYmlfdGF4YSRUYXhJRCldCgojIE5DQkkuZ2VudXMKTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkTkNCSS5nZW51c1tMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCRUYXhJRCAlaW4lIG5jYmlfdGF4YSRUYXhJRF0gPC0gbmNiaV90YXhhJE5DQkkuZ2VudXNbbWF0Y2goTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkVGF4SURbTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdLCBuY2JpX3RheGEkVGF4SUQpXQoKIyBOQ0JJLnNwZWNpZXMKTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkTkNCSS5zcGVjaWVzW0wxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSA8LSBuY2JpX3RheGEkTkNCSS5zcGVjaWVzW21hdGNoKEwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJFRheElEW0wxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSwgbmNiaV90YXhhJFRheElEKV0KCiMgUmVwbGFjZSB0aGUgdmFsdWUgaW4gIk5DQkkucGh5bHVtIiBjb2x1bW4gd2l0aCB0aGUgdmFsdWUgZnJvbSAiTkNCSS5jbGFzcyIgaWYgIk5DQkkucGh5bHVtIiBpcyAiUHNldWRvbW9uYWRvdGEiCkwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJE5DQkkucGh5bHVtIDwtIGlmZWxzZShMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCROQ0JJLnBoeWx1bSA9PSAiUHNldWRvbW9uYWRvdGEiLCBMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCROQ0JJLmNsYXNzLCBMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCROQ0JJLnBoeWx1bSkKCiMgUmVtb3ZlIHJvd3Mgd2l0aCBOQSBpbiBOQ0JJLnBoeWx1bSBjb2x1bW4KTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQgPC0gTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWRbIWlzLm5hKEwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJE5DQkkucGh5bHVtKSAmIEwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJE5DQkkucGh5bHVtICE9ICJOQSIsIF0KYGBgCgpGaWx0ZXIgZGF0YXNldCB0byBvbmx5IHJldGFpbiBwZXJmZWN0cyAobXV0YXRpb25zID09IDApCmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQpMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZF9wZXJmZWN0cyA8LSBMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCAlPiUKICBmaWx0ZXIobXV0YXRpb25zID09IDApCgpsZW5ndGgodW5pcXVlKEwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkX3BlcmZlY3RzJElEKSkKCiMgU3VtIHRoZSBudW1iZXIgb2Ygcm93cyBmb3IgZWFjaCB1bmlxdWUgTkNCSS5waHlsdW0KTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWRfcGVyZmVjdHNfcGh5bHVtX2NvdW50cyA8LSBMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZF9wZXJmZWN0cyAlPiUKICBncm91cF9ieShOQ0JJLnBoeWx1bSkgJT4lCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUKICBhcnJhbmdlKGRlc2MoY291bnQpKQoKIyBQcmludCB0aGUgdGFibGUKcHJpbnQoTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWRfcGVyZmVjdHNfcGh5bHVtX2NvdW50cykKYGBgCgojIyMgUGh5bHVtIERhdGFzZXRzCgpTdWJzZXQgYnkgcGh5bHVtIHRvIGluY2x1ZGUgcGVyZmVjdHMgKG11dGF0aW9ucyA9PSAwKSBhbmQgYWxsIG11dGFudHMgKHVwIHRvIDUgQUEgZGlzdGFuY2UpOgpgYGB7cn0KIyBBbHBoYXByb3Rlb2JhY3RlcmlhICgxNDIgVmFyaWFudHMpCkFscGhhcHJvdGVvYmFjdGVyaWEgPC0gTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWRbTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkTkNCSS5waHlsdW0gPT0gIkFscGhhcHJvdGVvYmFjdGVyaWEiICYgIWlzLm5hKEwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJE5DQkkucGh5bHVtKSwgXQoKIyBCZXRhcHJvdGVvYmFjdGVyaWEgKDI3MiBWYXJpYW50cykKQmV0YXByb3Rlb2JhY3RlcmlhIDwtIEwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkW0wxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJE5DQkkucGh5bHVtID09ICJCZXRhcHJvdGVvYmFjdGVyaWEiICYgIWlzLm5hKEwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJE5DQkkucGh5bHVtKSwgXQoKIyBHYW1tYXByb3Rlb2JhY3RlcmlhICgxNTkzIFZhcmlhbnRzKQpHYW1tYXByb3Rlb2JhY3RlcmlhIDwtIEwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkW0wxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJE5DQkkucGh5bHVtID09ICJHYW1tYXByb3Rlb2JhY3RlcmlhIiAmICFpcy5uYShMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCROQ0JJLnBoeWx1bSksIF0KCiMgQmFjaWxsb3RhICgzNjIzIFZhcmlhbnRzKQpCYWNpbGxvdGEgPC0gTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWRbTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkTkNCSS5waHlsdW0gPT0gIkJhY2lsbG90YSIgJiAhaXMubmEoTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkTkNCSS5waHlsdW0pLCBdCgojIEJhY3Rlcm9pZG90YSAoMTI0NyBWYXJpYW50cykKQmFjdGVyb2lkb3RhIDwtIEwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkW0wxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJE5DQkkucGh5bHVtID09ICJCYWN0ZXJvaWRvdGEiICYgIWlzLm5hKEwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJE5DQkkucGh5bHVtKSwgXQpgYGAKCiMjIyBGaXRuZXNzIExpbmUgUGxvdHMKCiMjIyMgQWxwaGFwcm90ZW9iYWN0ZXJpYQpgYGB7ciB3YXJuaW5nPUZBTFNFfQojIFByZXBhcmUgdGhlIG1haW4gZGF0YSBhbmQgcmVtb3ZlIHJvd3Mgd2l0aCBOQSB2YWx1ZXMgaW4gZml0bmVzcyBjb25kaXRpb25zIG9yIE5DQkkuc3BlY2llcwpBbHBoYXByb3Rlb2JhY3RlcmlhLmRhdGEgPC0gQWxwaGFwcm90ZW9iYWN0ZXJpYSAlPiUKICBzZWxlY3QobXV0SUQsIG11dGF0aW9ucywgTkNCSS5zcGVjaWVzLCBmaXREMDVEMDMsIGZpdEQwNkQwMywgZml0RDA3RDAzLCBmaXREMDhEMDMsIGZpdEQwOUQwMywgZml0RDEwRDAzLCBmaXREMTFEMDMpICU+JQogIGZpbHRlcighaXMubmEoTkNCSS5zcGVjaWVzKSkgJT4lICAjIFJlbW92ZSByb3dzIHdpdGggTkEgaW4gTkNCSS5zcGVjaWVzIGZpcnN0CiAgZmlsdGVyKCFpcy5uYShmaXREMTFEMDMpKSAlPiUgICMgUmVtb3ZlIHJvd3Mgd2l0aCBOQSBpbiBmaXREMTFEMDMKICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0c193aXRoKCJmaXREIiksIG5hbWVzX3RvID0gIkNvbmRpdGlvbiIsIHZhbHVlc190byA9ICJGaXRuZXNzIikgJT4lCiAgZmlsdGVyKCFpcy5uYShGaXRuZXNzKSkgICMgUmVtb3ZlIHJvd3Mgd2l0aCBOQSBpbiBGaXRuZXNzCgojIENyZWF0ZSBhIGRhdGFmcmFtZSB0byBkZXRlcm1pbmUgdGhlIGNvbG9yIGZvciBlYWNoIG11dElECmNvbG9yX2Fzc2lnbm1lbnQgPC0gQWxwaGFwcm90ZW9iYWN0ZXJpYSAlPiUKICBzZWxlY3QobXV0SUQsIG11dGF0aW9ucywgTkNCSS5zcGVjaWVzLCBmaXREMTFEMDMpICU+JQogIGZpbHRlcighaXMubmEoTkNCSS5zcGVjaWVzKSkgJT4lICAjIEVuc3VyZSBubyBOQSB2YWx1ZXMgZm9yIHNwZWNpZXMKICBmaWx0ZXIoIWlzLm5hKGZpdEQxMUQwMykpICU+JSAgIyBFbnN1cmUgbm8gTkEgdmFsdWVzIGZvciBmaXREMTFEMDMKICBkaXN0aW5jdCgpICU+JSAgIyBFbnN1cmUgdW5pcXVlIGVudHJpZXMgZm9yIGNvbG9yIGFzc2lnbm1lbnQKICBtdXRhdGUoY29sb3IgPSBjYXNlX3doZW4oCiAgICBtdXRhdGlvbnMgPT0gMCB+ICJyZWQiLAogICAgbXV0YXRpb25zICE9IDAgJiBmaXREMTFEMDMgPCAtMSB+ICJkYXJrYmx1ZSIsCiAgICBtdXRhdGlvbnMgIT0gMCAmIGZpdEQxMUQwMyA+IC0xIH4gImdvbGQiLAogICAgVFJVRSB+ICJncmF5IiAgIyBEZWZhdWx0IGNvbG9yIGZvciBvdGhlciBjYXNlcwogICkpCgojIEpvaW4gdGhlIGNvbG9yIGFzc2lnbm1lbnQgdG8gdGhlIG1haW4gZGF0YQpBbHBoYXByb3Rlb2JhY3RlcmlhLmRhdGEgPC0gQWxwaGFwcm90ZW9iYWN0ZXJpYS5kYXRhICU+JQogIGxlZnRfam9pbihjb2xvcl9hc3NpZ25tZW50LCBieSA9IGMoIm11dElEIiwgIk5DQkkuc3BlY2llcyIpKSAKCiMgRmlsdGVyIG91dCBhbnkgcm93cyB3aGVyZSBjb2xvciBpcyBOQSBhZnRlciBqb2luaW5nCkFscGhhcHJvdGVvYmFjdGVyaWEuZGF0YSA8LSBBbHBoYXByb3Rlb2JhY3RlcmlhLmRhdGEgJT4lCiAgZmlsdGVyKCFpcy5uYShjb2xvciksICFpcy5uYShOQ0JJLnNwZWNpZXMpKSAgIyBFbnN1cmUgbm8gTkEgdmFsdWVzIGluIGNvbG9yIG9yIHNwZWNpZXMKCiMgQ3JlYXRlIGEgc3Vic2V0IGZvciByZWZlcmVuY2UgbGluZXMgKG11dGF0aW9ucyA9PSAwKQpyZWZlcmVuY2VfZGF0YSA8LSBBbHBoYXByb3Rlb2JhY3RlcmlhICU+JQogIHNlbGVjdChtdXRJRCwgbXV0YXRpb25zLCBOQ0JJLnNwZWNpZXMsIGZpdEQxMUQwMykgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyA9PSAwKSAlPiUKICBmaWx0ZXIoIWlzLm5hKGZpdEQxMUQwMykpICU+JSAgIyBFbnN1cmUgbm8gTkEgdmFsdWVzIGZvciBmaXREMTFEMDMKICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0c193aXRoKCJmaXREIiksIG5hbWVzX3RvID0gIkNvbmRpdGlvbiIsIHZhbHVlc190byA9ICJGaXRuZXNzIikgJT4lCiAgZmlsdGVyKCFpcy5uYShGaXRuZXNzKSkgICMgRW5zdXJlIEZpdG5lc3MgaXMgbm90IE5BCgojIEZpbmFsIGNoZWNrIHRvIHJlbW92ZSBhbnkgZW1wdHkgc3BlY2llcyBmcm9tIHJlZmVyZW5jZV9kYXRhIGJlZm9yZSBwbG90dGluZwpyZWZlcmVuY2VfZGF0YSA8LSByZWZlcmVuY2VfZGF0YSAlPiUKICBmaWx0ZXIoTkNCSS5zcGVjaWVzICE9ICIiKSAjIFJlbW92ZSBlbXB0eSBzcGVjaWVzIGlmIG5lY2Vzc2FyeQoKIyBQbG90IHRoZSBsaW5lIGdyYXBocwpBbHBoYXByb3Rlb2JhY3RlcmlhLnBsb3QgPC0gZ2dwbG90KEFscGhhcHJvdGVvYmFjdGVyaWEuZGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBDb25kaXRpb24sIHkgPSBGaXRuZXNzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBtdXRJRCwgY29sb3IgPSBjb2xvcikpICsKICBnZW9tX2xpbmUobGluZXdpZHRoID0gMC43NSkgKyAgICAgICAgICAjIFBsb3QgbXV0YW50IGxpbmVzCiAgZ2VvbV9wb2ludChzaXplID0gMSkgKyAgICAgICAgICAgICAgICAgIyBQbG90IHBvaW50cyBmb3IgdmlzaWJpbGl0eQogIGdlb21fbGluZShkYXRhID0gcmVmZXJlbmNlX2RhdGEsIGFlcyh4ID0gQ29uZGl0aW9uLCB5ID0gRml0bmVzcyksIAogICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ld2lkdGggPSAxLjI1KSArICMgUGxvdCByZWZlcmVuY2UgbGluZXMgb24gdG9wCiAgZmFjZXRfd3JhcCh+TkNCSS5zcGVjaWVzLCBuY29sID0gMiwgc2NhbGVzID0gImZyZWVfeSIpICsgCiAgdGhlbWVfYncoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwgIyBTbGFudCB4LWF4aXMgbGFiZWxzCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJncmF5OTAiKSwgIAogICAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJwbGFpbiIpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksICAgICAjIFJlbW92ZSBtYWpvciBncmlkIGxpbmVzCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKyAgICMgUmVtb3ZlIG1pbm9yIGdyaWQgbGluZXMKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGMoImZpdEQwNUQwMyIgPSAiMC1UTVAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZml0RDA2RDAzIiA9ICIwLjA1OC1UTVAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZml0RDA3RDAzIiA9ICIwLjUtVE1QIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImZpdEQwOEQwMyIgPSAiMS4wLVRNUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmaXREMDlEMDMiID0gIjEwLVRNUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmaXREMTBEMDMiID0gIjUwLVRNUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmaXREMTFEMDMiID0gIjIwMC1UTVAiKSkgKwogIHNjYWxlX2NvbG9yX2lkZW50aXR5KCkgKyAgICAgICAgICAgICAgICAjIFVzZSB0aGUgY29sb3IgdmFsdWVzIGRpcmVjdGx5CiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLTEsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsKICBnZ3RpdGxlKCJBbHBoYXByb3Rlb2JhY3RlcmlhIFJlc2lzdGFuY2UgdG8gVHJpbWV0aG9wcmltIikgKwogIGxhYnMoeCA9ICJUTVAgQ29uY2VudHJhdGlvbiIsIHkgPSAiRml0bmVzcyIpCgojIERpc3BsYXkgdGhlIHBsb3QKcHJpbnQoQWxwaGFwcm90ZW9iYWN0ZXJpYS5wbG90KQpgYGAKCgoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9IlJlc2lzdGFuY2UvUExPVFMvQ29kb24xL0NvZG9uMS5Hb29kLjVCQ3MuQWxwaGFwcm90ZW9iYWN0ZXJpYS5wbmciLCAKICAgICAgIHBsb3Q9QWxwaGFwcm90ZW9iYWN0ZXJpYS5wbG90LAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gMTAsIHVuaXRzID0gImluIikKYGBgCgojIyMjIEJhY2lsbG90YQpgYGB7ciB3YXJuaW5nPUZBTFNFfQojIFByZXBhcmUgdGhlIG1haW4gZGF0YSBhbmQgcmVtb3ZlIHJvd3Mgd2l0aCBOQSB2YWx1ZXMgaW4gZml0bmVzcyBjb25kaXRpb25zIG9yIE5DQkkuc3BlY2llcwpCYWNpbGxvdGEuZGF0YSA8LSBCYWNpbGxvdGEgJT4lCiAgc2VsZWN0KG11dElELCBtdXRhdGlvbnMsIE5DQkkuc3BlY2llcywgZml0RDA1RDAzLCBmaXREMDZEMDMsIGZpdEQwN0QwMywgZml0RDA4RDAzLCBmaXREMDlEMDMsIGZpdEQxMEQwMywgZml0RDExRDAzKSAlPiUKICBmaWx0ZXIoIWlzLm5hKE5DQkkuc3BlY2llcykpICU+JSAgIyBSZW1vdmUgcm93cyB3aXRoIE5BIGluIE5DQkkuc3BlY2llcyBmaXJzdAogIGZpbHRlcighaXMubmEoZml0RDExRDAzKSkgJT4lICAjIFJlbW92ZSByb3dzIHdpdGggTkEgaW4gZml0RDExRDAzCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgiZml0RCIpLCBuYW1lc190byA9ICJDb25kaXRpb24iLCB2YWx1ZXNfdG8gPSAiRml0bmVzcyIpICU+JQogIGZpbHRlcighaXMubmEoRml0bmVzcykpICAjIFJlbW92ZSByb3dzIHdpdGggTkEgaW4gRml0bmVzcwoKIyBDcmVhdGUgYSBkYXRhZnJhbWUgdG8gZGV0ZXJtaW5lIHRoZSBjb2xvciBmb3IgZWFjaCBtdXRJRApjb2xvcl9hc3NpZ25tZW50IDwtIEJhY2lsbG90YSAlPiUKICBzZWxlY3QobXV0SUQsIG11dGF0aW9ucywgTkNCSS5zcGVjaWVzLCBmaXREMTFEMDMpICU+JQogIGZpbHRlcighaXMubmEoTkNCSS5zcGVjaWVzKSkgJT4lICAjIEVuc3VyZSBubyBOQSB2YWx1ZXMgZm9yIHNwZWNpZXMKICBmaWx0ZXIoIWlzLm5hKGZpdEQxMUQwMykpICU+JSAgIyBFbnN1cmUgbm8gTkEgdmFsdWVzIGZvciBmaXREMTFEMDMKICBkaXN0aW5jdCgpICU+JSAgIyBFbnN1cmUgdW5pcXVlIGVudHJpZXMgZm9yIGNvbG9yIGFzc2lnbm1lbnQKICBtdXRhdGUoY29sb3IgPSBjYXNlX3doZW4oCiAgICBtdXRhdGlvbnMgPT0gMCB+ICJyZWQiLAogICAgbXV0YXRpb25zICE9IDAgJiBmaXREMTFEMDMgPCAtMSB+ICJkYXJrYmx1ZSIsCiAgICBtdXRhdGlvbnMgIT0gMCAmIGZpdEQxMUQwMyA+IC0xIH4gImdvbGQiLAogICAgVFJVRSB+ICJncmF5IiAgIyBEZWZhdWx0IGNvbG9yIGZvciBvdGhlciBjYXNlcwogICkpCgojIEpvaW4gdGhlIGNvbG9yIGFzc2lnbm1lbnQgdG8gdGhlIG1haW4gZGF0YQpCYWNpbGxvdGEuZGF0YSA8LSBCYWNpbGxvdGEuZGF0YSAlPiUKICBsZWZ0X2pvaW4oY29sb3JfYXNzaWdubWVudCwgYnkgPSBjKCJtdXRJRCIsICJOQ0JJLnNwZWNpZXMiKSkgCgojIEZpbHRlciBvdXQgYW55IHJvd3Mgd2hlcmUgY29sb3IgaXMgTkEgYWZ0ZXIgam9pbmluZwpCYWNpbGxvdGEuZGF0YSA8LSBCYWNpbGxvdGEuZGF0YSAlPiUKICBmaWx0ZXIoIWlzLm5hKGNvbG9yKSwgIWlzLm5hKE5DQkkuc3BlY2llcykpICAjIEVuc3VyZSBubyBOQSB2YWx1ZXMgaW4gY29sb3Igb3Igc3BlY2llcwoKIyBDcmVhdGUgYSBzdWJzZXQgZm9yIHJlZmVyZW5jZSBsaW5lcyAobXV0YXRpb25zID09IDApCnJlZmVyZW5jZV9kYXRhIDwtIEJhY2lsbG90YSAlPiUKICBzZWxlY3QobXV0SUQsIG11dGF0aW9ucywgTkNCSS5zcGVjaWVzLCBmaXREMTFEMDMpICU+JQogIGZpbHRlcihtdXRhdGlvbnMgPT0gMCkgJT4lCiAgZmlsdGVyKCFpcy5uYShmaXREMTFEMDMpKSAlPiUgICMgRW5zdXJlIG5vIE5BIHZhbHVlcyBmb3IgZml0RDExRDAzCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgiZml0RCIpLCBuYW1lc190byA9ICJDb25kaXRpb24iLCB2YWx1ZXNfdG8gPSAiRml0bmVzcyIpICU+JQogIGZpbHRlcighaXMubmEoRml0bmVzcykpICAjIEVuc3VyZSBGaXRuZXNzIGlzIG5vdCBOQQoKIyBGaW5hbCBjaGVjayB0byByZW1vdmUgYW55IGVtcHR5IHNwZWNpZXMgZnJvbSByZWZlcmVuY2VfZGF0YSBiZWZvcmUgcGxvdHRpbmcKcmVmZXJlbmNlX2RhdGEgPC0gcmVmZXJlbmNlX2RhdGEgJT4lCiAgZmlsdGVyKE5DQkkuc3BlY2llcyAhPSAiIikgIyBSZW1vdmUgZW1wdHkgc3BlY2llcyBpZiBuZWNlc3NhcnkKCiMgUGxvdCB0aGUgbGluZSBncmFwaHMKQmFjaWxsb3RhLnBsb3QgPC0gZ2dwbG90KEJhY2lsbG90YS5kYXRhLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IENvbmRpdGlvbiwgeSA9IEZpdG5lc3MsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cCA9IG11dElELCBjb2xvciA9IGNvbG9yKSkgKwogIGdlb21fbGluZShsaW5ld2lkdGggPSAwLjc1KSArICAgICAgICAgICMgUGxvdCBtdXRhbnQgbGluZXMKICBnZW9tX3BvaW50KHNpemUgPSAxKSArICAgICAgICAgICAgICAgICAjIFBsb3QgcG9pbnRzIGZvciB2aXNpYmlsaXR5CiAgZ2VvbV9saW5lKGRhdGEgPSByZWZlcmVuY2VfZGF0YSwgYWVzKHggPSBDb25kaXRpb24sIHkgPSBGaXRuZXNzKSwgCiAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIGxpbmV3aWR0aCA9IDEuMjUpICsgIyBQbG90IHJlZmVyZW5jZSBsaW5lcyBvbiB0b3AKICBmYWNldF93cmFwKH5OQ0JJLnNwZWNpZXMsIG5jb2wgPSA4LCBzY2FsZXMgPSAiZnJlZV95IikgKyAKICB0aGVtZV9idygpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLCAjIFNsYW50IHgtYXhpcyBsYWJlbHMKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImdyYXk5MCIpLCAgCiAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChmYWNlID0gInBsYWluIiksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgICAgICMgUmVtb3ZlIG1ham9yIGdyaWQgbGluZXMKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSArICAgIyBSZW1vdmUgbWlub3IgZ3JpZCBsaW5lcwogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gYygiZml0RDA1RDAzIiA9ICIwLVRNUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmaXREMDZEMDMiID0gIjAuMDU4LVRNUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmaXREMDdEMDMiID0gIjAuNS1UTVAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZml0RDA4RDAzIiA9ICIxLjAtVE1QIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImZpdEQwOUQwMyIgPSAiMTAtVE1QIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImZpdEQxMEQwMyIgPSAiNTAtVE1QIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImZpdEQxMUQwMyIgPSAiMjAwLVRNUCIpKSArCiAgc2NhbGVfY29sb3JfaWRlbnRpdHkoKSArICAgICAgICAgICAgICAgICMgVXNlIHRoZSBjb2xvciB2YWx1ZXMgZGlyZWN0bHkKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtMSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIikgKwogIGdndGl0bGUoIkJhY2lsbG90YSBSZXNpc3RhbmNlIHRvIFRyaW1ldGhvcHJpbSIpICsKICBsYWJzKHggPSAiVE1QIENvbmNlbnRyYXRpb24iLCB5ID0gIkZpdG5lc3MiKQoKIyBEaXNwbGF5IHRoZSBwbG90CnByaW50KEJhY2lsbG90YS5wbG90KQpgYGAKCmBgYHtyIGVjaG89RkFMU0V9Cmdnc2F2ZShmaWxlPSJSZXNpc3RhbmNlL1BMT1RTL0NvZG9uMS9Db2RvbjEuR29vZC41QkNzLkJhY2lsbG90YS5wbmciLCAKICAgICAgIHBsb3Q9QmFjaWxsb3RhLnBsb3QsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDIwLCBoZWlnaHQgPSAyMCwgdW5pdHMgPSAiaW4iKQpgYGAKCiMjIyMgQmFjdGVyb2lkb3RhCmBgYHtyIHdhcm5pbmc9RkFMU0V9CiMgUHJlcGFyZSB0aGUgbWFpbiBkYXRhIGFuZCByZW1vdmUgcm93cyB3aXRoIE5BIHZhbHVlcyBpbiBmaXRuZXNzIGNvbmRpdGlvbnMgb3IgTkNCSS5zcGVjaWVzCkJhY3Rlcm9pZG90YS5kYXRhIDwtIEJhY3Rlcm9pZG90YSAlPiUKICBzZWxlY3QobXV0SUQsIG11dGF0aW9ucywgTkNCSS5zcGVjaWVzLCBmaXREMDVEMDMsIGZpdEQwNkQwMywgZml0RDA3RDAzLCBmaXREMDhEMDMsIGZpdEQwOUQwMywgZml0RDEwRDAzLCBmaXREMTFEMDMpICU+JQogIGZpbHRlcighaXMubmEoTkNCSS5zcGVjaWVzKSkgJT4lICAjIFJlbW92ZSByb3dzIHdpdGggTkEgaW4gTkNCSS5zcGVjaWVzIGZpcnN0CiAgZmlsdGVyKCFpcy5uYShmaXREMTFEMDMpKSAlPiUgICMgUmVtb3ZlIHJvd3Mgd2l0aCBOQSBpbiBmaXREMTFEMDMKICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0c193aXRoKCJmaXREIiksIG5hbWVzX3RvID0gIkNvbmRpdGlvbiIsIHZhbHVlc190byA9ICJGaXRuZXNzIikgJT4lCiAgZmlsdGVyKCFpcy5uYShGaXRuZXNzKSkgICMgUmVtb3ZlIHJvd3Mgd2l0aCBOQSBpbiBGaXRuZXNzCgojIENyZWF0ZSBhIGRhdGFmcmFtZSB0byBkZXRlcm1pbmUgdGhlIGNvbG9yIGZvciBlYWNoIG11dElECmNvbG9yX2Fzc2lnbm1lbnQgPC0gQmFjdGVyb2lkb3RhICU+JQogIHNlbGVjdChtdXRJRCwgbXV0YXRpb25zLCBOQ0JJLnNwZWNpZXMsIGZpdEQxMUQwMykgJT4lCiAgZmlsdGVyKCFpcy5uYShOQ0JJLnNwZWNpZXMpKSAlPiUgICMgRW5zdXJlIG5vIE5BIHZhbHVlcyBmb3Igc3BlY2llcwogIGZpbHRlcighaXMubmEoZml0RDExRDAzKSkgJT4lICAjIEVuc3VyZSBubyBOQSB2YWx1ZXMgZm9yIGZpdEQxMUQwMwogIGRpc3RpbmN0KCkgJT4lICAjIEVuc3VyZSB1bmlxdWUgZW50cmllcyBmb3IgY29sb3IgYXNzaWdubWVudAogIG11dGF0ZShjb2xvciA9IGNhc2Vfd2hlbigKICAgIG11dGF0aW9ucyA9PSAwIH4gInJlZCIsCiAgICBtdXRhdGlvbnMgIT0gMCAmIGZpdEQxMUQwMyA8IC0xIH4gImRhcmtibHVlIiwKICAgIG11dGF0aW9ucyAhPSAwICYgZml0RDExRDAzID4gLTEgfiAiZ29sZCIsCiAgICBUUlVFIH4gImdyYXkiICAjIERlZmF1bHQgY29sb3IgZm9yIG90aGVyIGNhc2VzCiAgKSkKCiMgSm9pbiB0aGUgY29sb3IgYXNzaWdubWVudCB0byB0aGUgbWFpbiBkYXRhCkJhY3Rlcm9pZG90YS5kYXRhIDwtIEJhY3Rlcm9pZG90YS5kYXRhICU+JQogIGxlZnRfam9pbihjb2xvcl9hc3NpZ25tZW50LCBieSA9IGMoIm11dElEIiwgIk5DQkkuc3BlY2llcyIpKSAKCiMgRmlsdGVyIG91dCBhbnkgcm93cyB3aGVyZSBjb2xvciBpcyBOQSBhZnRlciBqb2luaW5nCkJhY3Rlcm9pZG90YS5kYXRhIDwtIEJhY3Rlcm9pZG90YS5kYXRhICU+JQogIGZpbHRlcighaXMubmEoY29sb3IpLCAhaXMubmEoTkNCSS5zcGVjaWVzKSkgICMgRW5zdXJlIG5vIE5BIHZhbHVlcyBpbiBjb2xvciBvciBzcGVjaWVzCgojIENyZWF0ZSBhIHN1YnNldCBmb3IgcmVmZXJlbmNlIGxpbmVzIChtdXRhdGlvbnMgPT0gMCkKcmVmZXJlbmNlX2RhdGEgPC0gQmFjdGVyb2lkb3RhICU+JQogIHNlbGVjdChtdXRJRCwgbXV0YXRpb25zLCBOQ0JJLnNwZWNpZXMsIGZpdEQxMUQwMykgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyA9PSAwKSAlPiUKICBmaWx0ZXIoIWlzLm5hKGZpdEQxMUQwMykpICU+JSAgIyBFbnN1cmUgbm8gTkEgdmFsdWVzIGZvciBmaXREMTFEMDMKICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0c193aXRoKCJmaXREIiksIG5hbWVzX3RvID0gIkNvbmRpdGlvbiIsIHZhbHVlc190byA9ICJGaXRuZXNzIikgJT4lCiAgZmlsdGVyKCFpcy5uYShGaXRuZXNzKSkgICMgRW5zdXJlIEZpdG5lc3MgaXMgbm90IE5BCgojIEZpbmFsIGNoZWNrIHRvIHJlbW92ZSBhbnkgZW1wdHkgc3BlY2llcyBmcm9tIHJlZmVyZW5jZV9kYXRhIGJlZm9yZSBwbG90dGluZwpyZWZlcmVuY2VfZGF0YSA8LSByZWZlcmVuY2VfZGF0YSAlPiUKICBmaWx0ZXIoTkNCSS5zcGVjaWVzICE9ICIiKSAjIFJlbW92ZSBlbXB0eSBzcGVjaWVzIGlmIG5lY2Vzc2FyeQoKIyBQbG90IHRoZSBsaW5lIGdyYXBocwpCYWN0ZXJvaWRvdGEucGxvdCA8LSBnZ3Bsb3QoQmFjdGVyb2lkb3RhLmRhdGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gQ29uZGl0aW9uLCB5ID0gRml0bmVzcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gbXV0SUQsIGNvbG9yID0gY29sb3IpKSArCiAgZ2VvbV9saW5lKGxpbmV3aWR0aCA9IDAuNzUpICsgICAgICAgICAgIyBQbG90IG11dGFudCBsaW5lcwogIGdlb21fcG9pbnQoc2l6ZSA9IDEpICsgICAgICAgICAgICAgICAgICMgUGxvdCBwb2ludHMgZm9yIHZpc2liaWxpdHkKICBnZW9tX2xpbmUoZGF0YSA9IHJlZmVyZW5jZV9kYXRhLCBhZXMoeCA9IENvbmRpdGlvbiwgeSA9IEZpdG5lc3MpLCAKICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXdpZHRoID0gMS4yNSkgKyAjIFBsb3QgcmVmZXJlbmNlIGxpbmVzIG9uIHRvcAogIGZhY2V0X3dyYXAofk5DQkkuc3BlY2llcywgbmNvbCA9IDcsIHNjYWxlcyA9ICJmcmVlX3kiKSArIAogIHRoZW1lX2J3KCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksICMgU2xhbnQgeC1heGlzIGxhYmVscwogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiZ3JheTkwIiksICAKICAgICAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAicGxhaW4iKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAgICAgIyBSZW1vdmUgbWFqb3IgZ3JpZCBsaW5lcwogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpICsgICAjIFJlbW92ZSBtaW5vciBncmlkIGxpbmVzCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBjKCJmaXREMDVEMDMiID0gIjAtVE1QIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImZpdEQwNkQwMyIgPSAiMC4wNTgtVE1QIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImZpdEQwN0QwMyIgPSAiMC41LVRNUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmaXREMDhEMDMiID0gIjEuMC1UTVAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZml0RDA5RDAzIiA9ICIxMC1UTVAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZml0RDEwRDAzIiA9ICI1MC1UTVAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZml0RDExRDAzIiA9ICIyMDAtVE1QIikpICsKICBzY2FsZV9jb2xvcl9pZGVudGl0eSgpICsgICAgICAgICAgICAgICAgIyBVc2UgdGhlIGNvbG9yIHZhbHVlcyBkaXJlY3RseQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC0xLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiKSArCiAgZ2d0aXRsZSgiQmFjdGVyb2lkb3RhIFJlc2lzdGFuY2UgdG8gVHJpbWV0aG9wcmltIikgKwogIGxhYnMoeCA9ICJUTVAgQ29uY2VudHJhdGlvbiIsIHkgPSAiRml0bmVzcyIpCgojIERpc3BsYXkgdGhlIHBsb3QKcHJpbnQoQmFjdGVyb2lkb3RhLnBsb3QpCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9IlJlc2lzdGFuY2UvUExPVFMvQ29kb24xL0NvZG9uMS5Hb29kLjVCQ3MuQmFjdGVyb2lkb3RhLnBuZyIsIAogICAgICAgcGxvdD1CYWN0ZXJvaWRvdGEucGxvdCwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gMjAsIGhlaWdodCA9IDIwLCB1bml0cyA9ICJpbiIpCmBgYAoKIyMjIyBCZXRhcHJvdGVvYmFjdGVyaWEKYGBge3Igd2FybmluZz1GQUxTRX0KIyBQcmVwYXJlIHRoZSBtYWluIGRhdGEgYW5kIHJlbW92ZSByb3dzIHdpdGggTkEgdmFsdWVzIGluIGZpdG5lc3MgY29uZGl0aW9ucyBvciBOQ0JJLnNwZWNpZXMKQmV0YXByb3Rlb2JhY3RlcmlhLmRhdGEgPC0gQmV0YXByb3Rlb2JhY3RlcmlhICU+JQogIHNlbGVjdChtdXRJRCwgbXV0YXRpb25zLCBOQ0JJLnNwZWNpZXMsIGZpdEQwNUQwMywgZml0RDA2RDAzLCBmaXREMDdEMDMsIGZpdEQwOEQwMywgZml0RDA5RDAzLCBmaXREMTBEMDMsIGZpdEQxMUQwMykgJT4lCiAgZmlsdGVyKCFpcy5uYShOQ0JJLnNwZWNpZXMpKSAlPiUgICMgUmVtb3ZlIHJvd3Mgd2l0aCBOQSBpbiBOQ0JJLnNwZWNpZXMgZmlyc3QKICBmaWx0ZXIoIWlzLm5hKGZpdEQxMUQwMykpICU+JSAgIyBSZW1vdmUgcm93cyB3aXRoIE5BIGluIGZpdEQxMUQwMwogIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoImZpdEQiKSwgbmFtZXNfdG8gPSAiQ29uZGl0aW9uIiwgdmFsdWVzX3RvID0gIkZpdG5lc3MiKSAlPiUKICBmaWx0ZXIoIWlzLm5hKEZpdG5lc3MpKSAgIyBSZW1vdmUgcm93cyB3aXRoIE5BIGluIEZpdG5lc3MKCiMgQ3JlYXRlIGEgZGF0YWZyYW1lIHRvIGRldGVybWluZSB0aGUgY29sb3IgZm9yIGVhY2ggbXV0SUQKY29sb3JfYXNzaWdubWVudCA8LSBCZXRhcHJvdGVvYmFjdGVyaWEgJT4lCiAgc2VsZWN0KG11dElELCBtdXRhdGlvbnMsIE5DQkkuc3BlY2llcywgZml0RDExRDAzKSAlPiUKICBmaWx0ZXIoIWlzLm5hKE5DQkkuc3BlY2llcykpICU+JSAgIyBFbnN1cmUgbm8gTkEgdmFsdWVzIGZvciBzcGVjaWVzCiAgZmlsdGVyKCFpcy5uYShmaXREMTFEMDMpKSAlPiUgICMgRW5zdXJlIG5vIE5BIHZhbHVlcyBmb3IgZml0RDExRDAzCiAgZGlzdGluY3QoKSAlPiUgICMgRW5zdXJlIHVuaXF1ZSBlbnRyaWVzIGZvciBjb2xvciBhc3NpZ25tZW50CiAgbXV0YXRlKGNvbG9yID0gY2FzZV93aGVuKAogICAgbXV0YXRpb25zID09IDAgfiAicmVkIiwKICAgIG11dGF0aW9ucyAhPSAwICYgZml0RDExRDAzIDwgLTEgfiAiZGFya2JsdWUiLAogICAgbXV0YXRpb25zICE9IDAgJiBmaXREMTFEMDMgPiAtMSB+ICJnb2xkIiwKICAgIFRSVUUgfiAiZ3JheSIgICMgRGVmYXVsdCBjb2xvciBmb3Igb3RoZXIgY2FzZXMKICApKQoKIyBKb2luIHRoZSBjb2xvciBhc3NpZ25tZW50IHRvIHRoZSBtYWluIGRhdGEKQmV0YXByb3Rlb2JhY3RlcmlhLmRhdGEgPC0gQmV0YXByb3Rlb2JhY3RlcmlhLmRhdGEgJT4lCiAgbGVmdF9qb2luKGNvbG9yX2Fzc2lnbm1lbnQsIGJ5ID0gYygibXV0SUQiLCAiTkNCSS5zcGVjaWVzIikpIAoKIyBGaWx0ZXIgb3V0IGFueSByb3dzIHdoZXJlIGNvbG9yIGlzIE5BIGFmdGVyIGpvaW5pbmcKQmV0YXByb3Rlb2JhY3RlcmlhLmRhdGEgPC0gQmV0YXByb3Rlb2JhY3RlcmlhLmRhdGEgJT4lCiAgZmlsdGVyKCFpcy5uYShjb2xvciksICFpcy5uYShOQ0JJLnNwZWNpZXMpKSAgIyBFbnN1cmUgbm8gTkEgdmFsdWVzIGluIGNvbG9yIG9yIHNwZWNpZXMKCiMgQ3JlYXRlIGEgc3Vic2V0IGZvciByZWZlcmVuY2UgbGluZXMgKG11dGF0aW9ucyA9PSAwKQpyZWZlcmVuY2VfZGF0YSA8LSBCZXRhcHJvdGVvYmFjdGVyaWEgJT4lCiAgc2VsZWN0KG11dElELCBtdXRhdGlvbnMsIE5DQkkuc3BlY2llcywgZml0RDExRDAzKSAlPiUKICBmaWx0ZXIobXV0YXRpb25zID09IDApICU+JQogIGZpbHRlcighaXMubmEoZml0RDExRDAzKSkgJT4lICAjIEVuc3VyZSBubyBOQSB2YWx1ZXMgZm9yIGZpdEQxMUQwMwogIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoImZpdEQiKSwgbmFtZXNfdG8gPSAiQ29uZGl0aW9uIiwgdmFsdWVzX3RvID0gIkZpdG5lc3MiKSAlPiUKICBmaWx0ZXIoIWlzLm5hKEZpdG5lc3MpKSAgIyBFbnN1cmUgRml0bmVzcyBpcyBub3QgTkEKCiMgRmluYWwgY2hlY2sgdG8gcmVtb3ZlIGFueSBlbXB0eSBzcGVjaWVzIGZyb20gcmVmZXJlbmNlX2RhdGEgYmVmb3JlIHBsb3R0aW5nCnJlZmVyZW5jZV9kYXRhIDwtIHJlZmVyZW5jZV9kYXRhICU+JQogIGZpbHRlcihOQ0JJLnNwZWNpZXMgIT0gIiIpICMgUmVtb3ZlIGVtcHR5IHNwZWNpZXMgaWYgbmVjZXNzYXJ5CgojIFBsb3QgdGhlIGxpbmUgZ3JhcGhzCkJldGFwcm90ZW9iYWN0ZXJpYS5wbG90IDwtIGdncGxvdChCZXRhcHJvdGVvYmFjdGVyaWEuZGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBDb25kaXRpb24sIHkgPSBGaXRuZXNzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBtdXRJRCwgY29sb3IgPSBjb2xvcikpICsKICBnZW9tX2xpbmUobGluZXdpZHRoID0gMC43NSkgKyAgICAgICAgICAjIFBsb3QgbXV0YW50IGxpbmVzCiAgZ2VvbV9wb2ludChzaXplID0gMSkgKyAgICAgICAgICAgICAgICAgIyBQbG90IHBvaW50cyBmb3IgdmlzaWJpbGl0eQogIGdlb21fbGluZShkYXRhID0gcmVmZXJlbmNlX2RhdGEsIGFlcyh4ID0gQ29uZGl0aW9uLCB5ID0gRml0bmVzcyksIAogICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ld2lkdGggPSAxLjI1KSArICMgUGxvdCByZWZlcmVuY2UgbGluZXMgb24gdG9wCiAgZmFjZXRfd3JhcCh+TkNCSS5zcGVjaWVzLCBuY29sID0gMywgc2NhbGVzID0gImZyZWVfeSIpICsgCiAgdGhlbWVfYncoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwgIyBTbGFudCB4LWF4aXMgbGFiZWxzCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJncmF5OTAiKSwgIAogICAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJwbGFpbiIpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksICAgICAjIFJlbW92ZSBtYWpvciBncmlkIGxpbmVzCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKyAgICMgUmVtb3ZlIG1pbm9yIGdyaWQgbGluZXMKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGMoImZpdEQwNUQwMyIgPSAiMC1UTVAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZml0RDA2RDAzIiA9ICIwLjA1OC1UTVAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZml0RDA3RDAzIiA9ICIwLjUtVE1QIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImZpdEQwOEQwMyIgPSAiMS4wLVRNUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmaXREMDlEMDMiID0gIjEwLVRNUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmaXREMTBEMDMiID0gIjUwLVRNUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmaXREMTFEMDMiID0gIjIwMC1UTVAiKSkgKwogIHNjYWxlX2NvbG9yX2lkZW50aXR5KCkgKyAgICAgICAgICAgICAgICAjIFVzZSB0aGUgY29sb3IgdmFsdWVzIGRpcmVjdGx5CiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLTEsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsKICBnZ3RpdGxlKCJCZXRhcHJvdGVvYmFjdGVyaWEgUmVzaXN0YW5jZSB0byBUcmltZXRob3ByaW0iKSArCiAgbGFicyh4ID0gIlRNUCBDb25jZW50cmF0aW9uIiwgeSA9ICJGaXRuZXNzIikKCiMgRGlzcGxheSB0aGUgcGxvdApwcmludChCZXRhcHJvdGVvYmFjdGVyaWEucGxvdCkKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iUmVzaXN0YW5jZS9QTE9UUy9Db2RvbjEvQ29kb24xLkdvb2QuNUJDcy5CZXRhcHJvdGVvYmFjdGVyaWEucG5nIiwgCiAgICAgICBwbG90PUJldGFwcm90ZW9iYWN0ZXJpYS5wbG90LAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTQsIHVuaXRzID0gImluIikKYGBgCgojIyMjIEdhbW1hcHJvdGVvYmFjdGVyaWEKYGBge3Igd2FybmluZz1GQUxTRX0KIyBQcmVwYXJlIHRoZSBtYWluIGRhdGEgYW5kIHJlbW92ZSByb3dzIHdpdGggTkEgdmFsdWVzIGluIGZpdG5lc3MgY29uZGl0aW9ucyBvciBOQ0JJLnNwZWNpZXMKR2FtbWFwcm90ZW9iYWN0ZXJpYS5kYXRhIDwtIEdhbW1hcHJvdGVvYmFjdGVyaWEgJT4lCiAgc2VsZWN0KG11dElELCBtdXRhdGlvbnMsIE5DQkkuc3BlY2llcywgZml0RDA1RDAzLCBmaXREMDZEMDMsIGZpdEQwN0QwMywgZml0RDA4RDAzLCBmaXREMDlEMDMsIGZpdEQxMEQwMywgZml0RDExRDAzKSAlPiUKICBmaWx0ZXIoIWlzLm5hKE5DQkkuc3BlY2llcykpICU+JSAgIyBSZW1vdmUgcm93cyB3aXRoIE5BIGluIE5DQkkuc3BlY2llcyBmaXJzdAogIGZpbHRlcighaXMubmEoZml0RDExRDAzKSkgJT4lICAjIFJlbW92ZSByb3dzIHdpdGggTkEgaW4gZml0RDExRDAzCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgiZml0RCIpLCBuYW1lc190byA9ICJDb25kaXRpb24iLCB2YWx1ZXNfdG8gPSAiRml0bmVzcyIpICU+JQogIGZpbHRlcighaXMubmEoRml0bmVzcykpICAjIFJlbW92ZSByb3dzIHdpdGggTkEgaW4gRml0bmVzcwoKIyBDcmVhdGUgYSBkYXRhZnJhbWUgdG8gZGV0ZXJtaW5lIHRoZSBjb2xvciBmb3IgZWFjaCBtdXRJRApjb2xvcl9hc3NpZ25tZW50IDwtIEdhbW1hcHJvdGVvYmFjdGVyaWEgJT4lCiAgc2VsZWN0KG11dElELCBtdXRhdGlvbnMsIE5DQkkuc3BlY2llcywgZml0RDExRDAzKSAlPiUKICBmaWx0ZXIoIWlzLm5hKE5DQkkuc3BlY2llcykpICU+JSAgIyBFbnN1cmUgbm8gTkEgdmFsdWVzIGZvciBzcGVjaWVzCiAgZmlsdGVyKCFpcy5uYShmaXREMTFEMDMpKSAlPiUgICMgRW5zdXJlIG5vIE5BIHZhbHVlcyBmb3IgZml0RDExRDAzCiAgZGlzdGluY3QoKSAlPiUgICMgRW5zdXJlIHVuaXF1ZSBlbnRyaWVzIGZvciBjb2xvciBhc3NpZ25tZW50CiAgbXV0YXRlKGNvbG9yID0gY2FzZV93aGVuKAogICAgbXV0YXRpb25zID09IDAgfiAicmVkIiwKICAgIG11dGF0aW9ucyAhPSAwICYgZml0RDExRDAzIDwgLTEgfiAiZGFya2JsdWUiLAogICAgbXV0YXRpb25zICE9IDAgJiBmaXREMTFEMDMgPiAtMSB+ICJnb2xkIiwKICAgIFRSVUUgfiAiZ3JheSIgICMgRGVmYXVsdCBjb2xvciBmb3Igb3RoZXIgY2FzZXMKICApKQoKIyBKb2luIHRoZSBjb2xvciBhc3NpZ25tZW50IHRvIHRoZSBtYWluIGRhdGEKR2FtbWFwcm90ZW9iYWN0ZXJpYS5kYXRhIDwtIEdhbW1hcHJvdGVvYmFjdGVyaWEuZGF0YSAlPiUKICBsZWZ0X2pvaW4oY29sb3JfYXNzaWdubWVudCwgYnkgPSBjKCJtdXRJRCIsICJOQ0JJLnNwZWNpZXMiKSkgCgojIEZpbHRlciBvdXQgYW55IHJvd3Mgd2hlcmUgY29sb3IgaXMgTkEgYWZ0ZXIgam9pbmluZwpHYW1tYXByb3Rlb2JhY3RlcmlhLmRhdGEgPC0gR2FtbWFwcm90ZW9iYWN0ZXJpYS5kYXRhICU+JQogIGZpbHRlcighaXMubmEoY29sb3IpLCAhaXMubmEoTkNCSS5zcGVjaWVzKSkgICMgRW5zdXJlIG5vIE5BIHZhbHVlcyBpbiBjb2xvciBvciBzcGVjaWVzCgojIENyZWF0ZSBhIHN1YnNldCBmb3IgcmVmZXJlbmNlIGxpbmVzIChtdXRhdGlvbnMgPT0gMCkKcmVmZXJlbmNlX2RhdGEgPC0gR2FtbWFwcm90ZW9iYWN0ZXJpYSAlPiUKICBzZWxlY3QobXV0SUQsIG11dGF0aW9ucywgTkNCSS5zcGVjaWVzLCBmaXREMTFEMDMpICU+JQogIGZpbHRlcihtdXRhdGlvbnMgPT0gMCkgJT4lCiAgZmlsdGVyKCFpcy5uYShmaXREMTFEMDMpKSAlPiUgICMgRW5zdXJlIG5vIE5BIHZhbHVlcyBmb3IgZml0RDExRDAzCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgiZml0RCIpLCBuYW1lc190byA9ICJDb25kaXRpb24iLCB2YWx1ZXNfdG8gPSAiRml0bmVzcyIpICU+JQogIGZpbHRlcighaXMubmEoRml0bmVzcykpICAjIEVuc3VyZSBGaXRuZXNzIGlzIG5vdCBOQQoKIyBGaW5hbCBjaGVjayB0byByZW1vdmUgYW55IGVtcHR5IHNwZWNpZXMgZnJvbSByZWZlcmVuY2VfZGF0YSBiZWZvcmUgcGxvdHRpbmcKcmVmZXJlbmNlX2RhdGEgPC0gcmVmZXJlbmNlX2RhdGEgJT4lCiAgZmlsdGVyKE5DQkkuc3BlY2llcyAhPSAiIikgIyBSZW1vdmUgZW1wdHkgc3BlY2llcyBpZiBuZWNlc3NhcnkKCiMgUGxvdCB0aGUgbGluZSBncmFwaHMKR2FtbWFwcm90ZW9iYWN0ZXJpYS5wbG90IDwtIGdncGxvdChHYW1tYXByb3Rlb2JhY3RlcmlhLmRhdGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gQ29uZGl0aW9uLCB5ID0gRml0bmVzcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gbXV0SUQsIGNvbG9yID0gY29sb3IpKSArCiAgZ2VvbV9saW5lKGxpbmV3aWR0aCA9IDAuNzUpICsgICAgICAgICAgIyBQbG90IG11dGFudCBsaW5lcwogIGdlb21fcG9pbnQoc2l6ZSA9IDEpICsgICAgICAgICAgICAgICAgICMgUGxvdCBwb2ludHMgZm9yIHZpc2liaWxpdHkKICBnZW9tX2xpbmUoZGF0YSA9IHJlZmVyZW5jZV9kYXRhLCBhZXMoeCA9IENvbmRpdGlvbiwgeSA9IEZpdG5lc3MpLCAKICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXdpZHRoID0gMS4yNSkgKyAjIFBsb3QgcmVmZXJlbmNlIGxpbmVzIG9uIHRvcAogIGZhY2V0X3dyYXAofk5DQkkuc3BlY2llcywgbmNvbCA9IDUsIHNjYWxlcyA9ICJmcmVlX3kiKSArIAogIHRoZW1lX2J3KCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksICMgU2xhbnQgeC1heGlzIGxhYmVscwogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiZ3JheTkwIiksICAKICAgICAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAicGxhaW4iKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAgICAgIyBSZW1vdmUgbWFqb3IgZ3JpZCBsaW5lcwogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpICsgICAjIFJlbW92ZSBtaW5vciBncmlkIGxpbmVzCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBjKCJmaXREMDVEMDMiID0gIjAtVE1QIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImZpdEQwNkQwMyIgPSAiMC4wNTgtVE1QIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImZpdEQwN0QwMyIgPSAiMC41LVRNUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmaXREMDhEMDMiID0gIjEuMC1UTVAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZml0RDA5RDAzIiA9ICIxMC1UTVAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZml0RDEwRDAzIiA9ICI1MC1UTVAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZml0RDExRDAzIiA9ICIyMDAtVE1QIikpICsKICBzY2FsZV9jb2xvcl9pZGVudGl0eSgpICsgICAgICAgICAgICAgICAgIyBVc2UgdGhlIGNvbG9yIHZhbHVlcyBkaXJlY3RseQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC0xLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiKSArCiAgZ2d0aXRsZSgiR2FtbWFwcm90ZW9iYWN0ZXJpYSBSZXNpc3RhbmNlIHRvIFRyaW1ldGhvcHJpbSIpICsKICBsYWJzKHggPSAiVE1QIENvbmNlbnRyYXRpb24iLCB5ID0gIkZpdG5lc3MiKQoKIyBEaXNwbGF5IHRoZSBwbG90CnByaW50KEdhbW1hcHJvdGVvYmFjdGVyaWEucGxvdCkKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iUmVzaXN0YW5jZS9QTE9UUy9Db2RvbjEvQ29kb24xLkdvb2QuNUJDcy5HYW1tYXByb3Rlb2JhY3RlcmlhLnBuZyIsIAogICAgICAgcGxvdD1HYW1tYXByb3Rlb2JhY3RlcmlhLnBsb3QsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDE2LCBoZWlnaHQgPSAyMCwgdW5pdHMgPSAiaW4iKQpgYGAKCiMjIyBHZW51cyBDYXNlLVN0dWRpZXMKCk5vdywgY3JlYXRlIHNlcGFyYXRlIGRhdGFmcmFtZXMgZm9yIGVhY2ggR2VudXMgb2YgaW50ZXJlc3Q6CmBgYHtyfQojIEFjaW5ldG9iYWN0ZXIKQWNpbmV0b2JhY3RlciA8LSBMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZFtMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCROQ0JJLmdlbnVzID09ICJBY2luZXRvYmFjdGVyIiAmICFpcy5uYShMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCROQ0JJLmdlbnVzKSwgXQoKIyBCYWNpbGx1cwpCYWNpbGx1cyA8LSBMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZFtMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCROQ0JJLmdlbnVzID09ICJCYWNpbGx1cyIgJiAhaXMubmEoTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkTkNCSS5nZW51cyksIF0KCiMgQmFjdGVyb2lkZXMKQmFjdGVyb2lkZXMgPC0gTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWRbTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkTkNCSS5nZW51cyA9PSAiQmFjdGVyb2lkZXMiICYgIWlzLm5hKEwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJE5DQkkuZ2VudXMpLCBdCgojIENobGFteWRpYQpDaGxhbXlkaWEgPC0gTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWRbTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkTkNCSS5nZW51cyA9PSAiQ2hsYW15ZGlhIiAmICFpcy5uYShMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCROQ0JJLmdlbnVzKSwgXQoKIyBDbG9zdHJpZGl1bQpDbG9zdHJpZGl1bSA8LSBMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZFtMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCROQ0JJLmdlbnVzID09ICJDbG9zdHJpZGl1bSIgJiAhaXMubmEoTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkTkNCSS5nZW51cyksIF0KCiMgRXNjaGVyaWNoaWEKRXNjaGVyaWNoaWEgPC0gTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWRbTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkTkNCSS5nZW51cyA9PSAiRXNjaGVyaWNoaWEiICYgIWlzLm5hKEwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJE5DQkkuZ2VudXMpLCBdCgojIE5laXNzZXJpYQpOZWlzc2VyaWEgPC0gTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWRbTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkTkNCSS5nZW51cyA9PSAiTmVpc3NlcmlhIiAmICFpcy5uYShMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCROQ0JJLmdlbnVzKSwgXQoKIyBQc2V1ZG9tb25hcwpQc2V1ZG9tb25hcyA8LSBMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZFtMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCROQ0JJLmdlbnVzID09ICJQc2V1ZG9tb25hcyIgJiAhaXMubmEoTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkTkNCSS5nZW51cyksIF0KCiMgU3RyZXB0b2NvY2N1cwpTdHJlcHRvY29jY3VzIDwtIEwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkW0wxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJE5DQkkuZ2VudXMgPT0gIlN0cmVwdG9jb2NjdXMiICYgIWlzLm5hKEwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJE5DQkkuZ2VudXMpLCBdCmBgYAoKIyMjIyBTdHJlcHRvY29jY3VzIEZpdG5lc3MKCkZpdG5lc3MgbGluZSBwbG90IGZvciBlYWNoIFN0cmVwdG9jb2NjdXMgc3BlY2llcyAoQmFjaWxsb3RhKQpgYGB7ciB3YXJuaW5nPUZBTFNFfQpTdHJlcHRvY29jY3VzLmRhdGEgPC0gU3RyZXB0b2NvY2N1cyAlPiUKICBmaWx0ZXIobXV0YXRpb25zID09IDApICU+JQogIHNlbGVjdChJRCwgTkNCSS5zcGVjaWVzLCBmaXREMDVEMDMsIGZpdEQwNkQwMywgZml0RDA3RDAzLCBmaXREMDhEMDMsIGZpdEQwOUQwMywgZml0RDEwRDAzLCBmaXREMTFEMDMpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoImZpdEQiKSwgbmFtZXNfdG8gPSAiQ29uZGl0aW9uIiwgdmFsdWVzX3RvID0gIkZpdG5lc3MiKQoKIyBQbG90IHRoZSBsaW5lIGdyYXBocwpTdHJlcHRvY29jY3VzLnBsb3QgPC0gZ2dwbG90KFN0cmVwdG9jb2NjdXMuZGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBDb25kaXRpb24sIHkgPSBGaXRuZXNzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBJRCwgY29sb3IgPSBOQ0JJLnNwZWNpZXMpKSArCiAgZ2VvbV9saW5lKGxpbmV3aWR0aCA9IDAuNzUpICsKICBmYWNldF93cmFwKH5OQ0JJLnNwZWNpZXMsIG5jb2wgPSA0LCBzY2FsZXMgPSAiZnJlZV95IikgKyAKICB0aGVtZV9idygpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEsIHZqdXN0ID0gMC41KSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGMoImZpdEQwNUQwMyIgPSAiMC1UTVAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZml0RDA2RDAzIiA9ICIwLjA1OC1UTVAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZml0RDA3RDAzIiA9ICIwLjUtVE1QIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImZpdEQwOEQwMyIgPSAiMS4wLVRNUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmaXREMDlEMDMiID0gIjEwLVRNUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmaXREMTBEMDMiID0gIjUwLVRNUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmaXREMTFEMDMiID0gIjIwMC1UTVAiKSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC0xLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiKSArCiAgZ2d0aXRsZSgiU3RyZXB0b2NvY2N1cyBSZXNpc3RhbmNlIHRvIFRyaW1ldGhvcHJpbSIpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobmNvbCA9IDEpKSArCiAgbGFicyhjb2xvciA9ICJTcGVjaWVzIChJRCkiLCB4ID0gIlRNUCBDb25jZW50cmF0aW9uIiwgeSA9ICJGaXRuZXNzIikKCiMgRGlzcGxheSB0aGUgcGxvdApwcmludChTdHJlcHRvY29jY3VzLnBsb3QpCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9IlJlc2lzdGFuY2UvUExPVFMvQ29kb24xL0xpYjE1Lkdvb2QuNUJDcy5TdHJlcHRvY29jY3VzLlBlcmZlY3RzLkxpbmVwbG90LnBkZiIsIAogICAgICAgcGxvdD1TdHJlcHRvY29jY3VzLnBsb3QsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDE0LCBoZWlnaHQgPSAxMiwgdW5pdHMgPSAiaW4iKQpgYGAKCiMjIFBhdGhvZ2VuaWMgVGF4YQoKU3Vic2V0IHRoZSBmaXRuZXNzIHNjb3JlcyBhY3Jvc3MgdGhlIFRNUCBncmFkaWVudCBmb3IgYSBzZWxlY3Qgc2V0IG9mIGtub3duIHBhdGhvZ2VuaWMgYmFjdGVyaWFsIHRheGEuCmBgYHtyfQojIFN1YnNldCB0aGUgcGF0aG9nZW5pYyB2YXJpYW50cyBvZiBpbnRlcmVzdCBmb3IgcGxvdHRpbmcKcGF0aG9nZW5pY19tdXRJRCA8LSBjKCJOUF80MTQ1OTAiLCAiV1BfMDAwMTc1NzQyIiwgIldQXzAxMTI3MjI3NCIsICJXUF8wMDA5NzM1NDQiLCAiV1BfMDAyMjA1MzI3IiwgIldQXzAwMDYyNDM4NCIsICJOUF84MzE5NTciLCAiV1BfMDA0ODM2NjY5IikKCiMgQ3JlYXRlIHRoZSBzdWJzZXQgZGF0YWZyYW1lCnBhdGhvZ2VuaWNfZGF0YSA8LSBMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCAlPiUKICBmaWx0ZXIobXV0SUQgJWluJSBwYXRob2dlbmljX211dElEKQpgYGAKClBsb3QgdGhlIGNoYW5nZSBpbiBmaXRuZXNzIGFjcm9zcyB0aGUgVE1QIGdyYWRpZW50IGZvciBhIHNlbGVjdCBzZXQgb2Yga25vd24gcGF0aG9nZW5pYyBiYWN0ZXJpYWwgdGF4YS4KYGBge3Igd2FybmluZz1GQUxTRX0KIyBSZXNoYXBlIHRoZSBkYXRhIGFuZCByZW5hbWUgbXV0SURzIHRvIHNwZWNpZXMgbmFtZXMKcGF0aG9nZW5pY19kYXRhX2xvbmcgPC0gcGF0aG9nZW5pY19kYXRhICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoImZpdEQiKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkRheSIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiRml0bmVzcyIsCiAgICAgICAgICAgICAgIG5hbWVzX3ByZWZpeCA9ICJmaXREIikgJT4lCiAgbXV0YXRlKERheSA9IGZhY3RvcihEYXksIGxldmVscyA9IGMoIjA1RDAzIiwgIjA2RDAzIiwgIjA3RDAzIiwgIjA4RDAzIiwgIjA5RDAzIiwgIjEwRDAzIiwgIjExRDAzIiksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCIwIiwgIjAuMDU4IiwgIjAuNSIsICIxLjAiLCAiMTAiLCAiNTAiLCAiMjAwIikpKSAlPiUKICBtdXRhdGUoU3BlY2llcyA9IE5DQkkuc3BlY2llcykgICMgQ3JlYXRlIGEgbmV3IGNvbHVtbiBmb3Igc3BlY2llcyBuYW1lcwoKIyBHZW5lcmF0ZSBhIGNvbG9yIHBhbGV0dGUgd2l0aCBlbm91Z2ggY29sb3JzIGZvciBhbGwgc3BlY2llcwpuX2NvbG9ycyA8LSBsZW5ndGgodW5pcXVlKHBhdGhvZ2VuaWNfZGF0YV9sb25nJFNwZWNpZXMpKQpjb2xvcl9wYWxldHRlIDwtIGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbCg4LCAiRGFyazIiKSkobl9jb2xvcnMpCmBgYAoKYGBge3Igd2FybmluZz1GQUxTRX0KIyBDcmVhdGUgdGhlIHBsb3QKcGF0aG9nZW5pY19wbG90IDwtIGdncGxvdChwYXRob2dlbmljX2RhdGFfbG9uZywgYWVzKHggPSBEYXksIHkgPSBGaXRuZXNzLCBncm91cCA9IFNwZWNpZXMsIGNvbG9yID0gU3BlY2llcykpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtMSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZ3JheTUwIikgKwogIGdlb21fbGluZShzaXplID0gMS4wKSArCiAgZ2VvbV9wb2ludChzaXplID0gMykgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBzZXROYW1lcyhjb2xvcl9wYWxldHRlLCB1bmlxdWUocGF0aG9nZW5pY19kYXRhX2xvbmckU3BlY2llcykpKSArCiAgbGFicyh4ID0gIlRyaW1ldGhvcHJpbSAodWcvbUwpIiwKICAgICAgIHkgPSAiTWVkaWFuIEZpdG5lc3MgKExvZ0ZDKSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKAogICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICdibGFjaycsIHNpemUgPSAxLjApLAogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMS4wKSwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCkpICsKICBnZ3RpdGxlKCJQYXRob2dlbmljIEhvbW9sb2dzIikgKyAKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTYpKQoKIyBQcmludCB0aGUgcGxvdApwcmludChwYXRob2dlbmljX3Bsb3QpCmBgYAoKYGBge3J9CiMgUE5HCmdnc2F2ZShmaWxlPSJSZXNpc3RhbmNlL1BMT1RTL0ZpbmFsL1BhdGhvZ2VuaWMuRml0bmVzcy5wbmciLCAKICAgICAgIHBsb3Q9cGF0aG9nZW5pY19wbG90LAogICAgICAgZHBpPTYwMCwgd2lkdGggPSA4LCBoZWlnaHQgPSA1LCB1bml0cyA9ICJpbiIpCmBgYAoKIyMgVGF4YSBBbGlnbm1lbnRzCgpHZW5lcmF0ZSBGQVNUQSBmaWxlcyBmb3IgcGh5bG9nZW5ldGljIHRheGEgb2YgaW50ZXJlc3QgYW5kIHBlcmZvcm0gYSBtdWx0aXBsZSBzZXF1ZW5jZSBhbGlnbm1lbnQgdG8gZGV0ZXJtaW5lIGRpZmZlcmVuY2VzIGluIEFBIHBvc2l0aW9ucyBpbmZsdWVuY2luZyBUTVAgcmVzaXN0YW5jZSBhY3Jvc3MgdGhlIGNvbmNlbnRyYXRpb24gZ3JhZGllbnQuCgojIyMgRXNjaGVyaWNoaWEgY29saQoKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgRmlsdGVyIHRvIHJldGFpbiBFc2NoZXJpY2hpYSBjb2xpIChOQ0JJLnNwZWNpZXMpIGZyb20gTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQKRXNjaGVyaWNoaWEgPC0gTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWRbTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkTkNCSS5zcGVjaWVzID09ICJFc2NoZXJpY2hpYSBjb2xpIiAmIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICFpcy5uYShMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCROQ0JJLnNwZWNpZXMpLCBdCgojIEZpbHRlciB0aGUgZGF0YWZyYW1lIGZvciByb3dzIHdoZXJlIG11dGF0aW9ucyA9PSAwCkVzY2hlcmljaGlhX2ZpbHRlcmVkIDwtIEVzY2hlcmljaGlhW0VzY2hlcmljaGlhJG11dGF0aW9ucyA9PSAwLCBdCgojIENyZWF0ZSBhIGZ1bmN0aW9uIHRvIHdyaXRlIEZBU1RBIGVudHJpZXMKd3JpdGVfZmFzdGEgPC0gZnVuY3Rpb24oaWQsIHNlcSwgZmlsZSkgewogIGNhdChwYXN0ZTAoIj4iLCBpZCwgIlxuIiksIGZpbGUgPSBmaWxlLCBhcHBlbmQgPSBUUlVFKQogIGNhdChwYXN0ZTAoIk0iLCBzZXEsICJcbiIpLCBmaWxlID0gZmlsZSwgYXBwZW5kID0gVFJVRSkgICMgQWRkICJNIiBhdCB0aGUgYmVnaW5uaW5nIG9mIHRoZSBzZXF1ZW5jZQp9CgojIFNwZWNpZnkgdGhlIG91dHB1dCBmaWxlIG5hbWUKRXNjaGVyaWNoaWFfb3V0cHV0X2ZpbGUgPC0gIlJlc2lzdGFuY2UvRkFTVEEvRXNjaGVyaWNoaWEuZmFzdGEiCgojIFJlbW92ZSB0aGUgZmlsZSBpZiBpdCBhbHJlYWR5IGV4aXN0cwppZiAoZmlsZS5leGlzdHMoRXNjaGVyaWNoaWFfb3V0cHV0X2ZpbGUpKSB7CiAgZmlsZS5yZW1vdmUoRXNjaGVyaWNoaWFfb3V0cHV0X2ZpbGUpCn0KCiMgV3JpdGUgdGhlIEZBU1RBIGVudHJpZXMgdG8gdGhlIGZpbGUKZm9yIChpIGluIDE6bnJvdyhFc2NoZXJpY2hpYV9maWx0ZXJlZCkpIHsKICB3cml0ZV9mYXN0YShFc2NoZXJpY2hpYV9maWx0ZXJlZCRtdXRJRFtpXSwgRXNjaGVyaWNoaWFfZmlsdGVyZWQkc2VxW2ldLCBFc2NoZXJpY2hpYV9vdXRwdXRfZmlsZSkKfQoKY2F0KCJGQVNUQSBmaWxlIGhhcyBiZWVuIGdlbmVyYXRlZDoiLCBFc2NoZXJpY2hpYV9vdXRwdXRfZmlsZSwgIlxuIikKYGBgCgpQZXJmb3JtIGEgbXVsdGlwbGUgc2VxdWVuY2UgYWxpZ25tZW50IG9uIHRoZSBGQVNUQSBmaWxlIHVzaW5nIENMVVNUQUwgT21lZ2E6CmBgYHtiYXNofQojIE1heSBuZWVkIHRvIGVuYWJsZSBwZXJtaXNzaW9ucyB0byBydW4gdGhlIGV4ZWN1dGFibGU6CiNjaG1vZCAreCBjbHVzdGFsbwouL1NjcmlwdHMvY2x1c3RhbG8gLWkgUmVzaXN0YW5jZS9GQVNUQS9Fc2NoZXJpY2hpYS5mYXN0YSAtbyBSZXNpc3RhbmNlL0ZBU1RBL0VzY2hlcmljaGlhLmFsaWduZWQuZmFzdGEgLS1vdXRmbXQ9ZmEgLS1mb3JjZQpgYGAKCkltcG9ydCB0aGUgTVNBIGZpbGUgYW5kIGRldGVybWluZSB3aGljaCBhbWlubyBhY2lkIHBvc2l0aW9ucyBkaWZmZXIgYmV0d2VlbiB0aGUgc2FtcGxlczoKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgSW1wb3J0IHRoZSBNU0EgZmlsZQptc2EgPC0gcmVhZEFBTXVsdGlwbGVBbGlnbm1lbnQoIlJlc2lzdGFuY2UvRkFTVEEvRXNjaGVyaWNoaWEuYWxpZ25lZC5mYXN0YSIsIGZvcm1hdD0iZmFzdGEiKQoKIyBDb252ZXJ0IHRvIGEgbWF0cml4IGZvciBlYXNpZXIgbWFuaXB1bGF0aW9uCm1zYV9tYXRyaXggPC0gYXMubWF0cml4KG1zYSkKCiMgU2V0IHRoZSByZWZlcmVuY2Ugc2VxdWVuY2UKcmVmX3NlcSA8LSAiTlBfNDE0NTkwIgoKY2F0KCJVc2luZyByZWZlcmVuY2Ugc2VxdWVuY2U6IiwgcmVmX3NlcSwgIlxuIikKCiMgRmluZCBwb3NpdGlvbnMgd2hlcmUgYW1pbm8gYWNpZHMgZGlmZmVyCmRpZmZfcG9zaXRpb25zIDwtIHdoaWNoKGFwcGx5KG1zYV9tYXRyaXgsIDIsIGZ1bmN0aW9uKGNvbCkgbGVuZ3RoKHVuaXF1ZShjb2wpKSA+IDEpKQoKIyBDcmVhdGUgYSBzdW1tYXJ5IGRhdGFmcmFtZQpzdW1tYXJ5X2RmIDwtIGRhdGEuZnJhbWUoCiAgUG9zaXRpb24gPSBkaWZmX3Bvc2l0aW9ucywKICBSZWZBQSA9IG1zYV9tYXRyaXhbcmVmX3NlcSwgZGlmZl9wb3NpdGlvbnNdLAogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQopCgojIEFkZCBjb2x1bW5zIGZvciBlYWNoIHVuaXF1ZSBtdXRJRAp1bmlxdWVfbXV0SURzIDwtIHNldGRpZmYocm93bmFtZXMobXNhX21hdHJpeCksIHJlZl9zZXEpCmZvciAobXV0SUQgaW4gdW5pcXVlX211dElEcykgewogIHN1bW1hcnlfZGZbW211dElEXV0gPC0gbXNhX21hdHJpeFttdXRJRCwgZGlmZl9wb3NpdGlvbnNdCn0KCiMgQWRkIGEgY29sdW1uIGZvciB0aGUgYW1pbm8gYWNpZCBjaGFuZ2VzCnN1bW1hcnlfZGYkQ2hhbmdlcyA8LSBhcHBseShzdW1tYXJ5X2RmLCAxLCBmdW5jdGlvbihyb3cpIHsKICBjaGFuZ2VzIDwtIHNldGRpZmYoYXMuY2hhcmFjdGVyKHJvd1szOm5jb2woc3VtbWFyeV9kZildKSwgcm93WyJSZWZBQSJdKQogIGlmIChsZW5ndGgoY2hhbmdlcykgPiAwKSB7CiAgICBwYXN0ZShyb3dbIlJlZkFBIl0sIHBhc3RlKGNoYW5nZXMsIGNvbGxhcHNlID0gIi8iKSwgc2VwID0gIi0+IikKICB9IGVsc2UgewogICAgIk5vIGNoYW5nZSIKICB9Cn0pCgojIFJlbW92ZSByb3dzIHdpdGggIk5vIGNoYW5nZSIKc3VtbWFyeV9kZiA8LSBzdW1tYXJ5X2RmW3N1bW1hcnlfZGYkQ2hhbmdlcyAhPSAiTm8gY2hhbmdlIiwgXQoKIyBSZW9yZGVyIGNvbHVtbnMKc3VtbWFyeV9kZiA8LSBzdW1tYXJ5X2RmICU+JSAKICBzZWxlY3QoUG9zaXRpb24sIFJlZkFBLCBDaGFuZ2VzLCBldmVyeXRoaW5nKCkpCgojIFByaW50IHRoZSBzdW1tYXJ5IHRhYmxlCnByaW50KHN1bW1hcnlfZGYsIHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKCkhlYXRtYXAgZm9yIGFtaW5vIGFjaWQgbXV0YXRpb25zIGJ5IHBvc2l0aW9uIGJhc2VkIG9uIHJlZmVyZW5jZSBzZXF1ZW5jZToKYGBge3J9CiMgR2V0IGFsbCBwb3NpdGlvbnMgd2hlcmUgdGhlcmUncyBhIG11dGF0aW9uCmFsbF9wb3NpdGlvbnMgPC0gc29ydCh1bmlxdWUoc3VtbWFyeV9kZiRQb3NpdGlvbikpCgojIFJlc2hhcGUgdGhlIGRhdGEKaGVhdG1hcF9kYXRhIDwtIHN1bW1hcnlfZGYgJT4lCiAgc2VsZWN0KC1DaGFuZ2VzKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IC1jKFBvc2l0aW9uLCBSZWZBQSksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJtdXRJRCIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiQUEiKSAlPiUKICBtdXRhdGUoUG9zaXRpb24gPSBhcy5udW1lcmljKFBvc2l0aW9uKSkKCiMgQWRkIHRoZSByZWZlcmVuY2Ugc2VxdWVuY2UKcmVmX2RhdGEgPC0gc3VtbWFyeV9kZiAlPiUKICBzZWxlY3QoUG9zaXRpb24sIFJlZkFBKSAlPiUKICBtdXRhdGUobXV0SUQgPSAiTlBfNDE0NTkwIiwgQUEgPSBSZWZBQSkKCmhlYXRtYXBfZGF0YSA8LSBiaW5kX3Jvd3MocmVmX2RhdGEsIGhlYXRtYXBfZGF0YSkKCiMgQ3JlYXRlIGEgZnVuY3Rpb24gdG8gY29tcGFyZSBhbWlubyBhY2lkcwpjb21wYXJlX2FhIDwtIGZ1bmN0aW9uKGFhLCByZWZfYWEpIHsKICBpZmVsc2UoaXMubmEoYWEpIHwgYWEgPT0gIiIsICJObyBkYXRhIiwKICAgICAgICAgaWZlbHNlKGFhID09IHJlZl9hYSwgIk5vIGNoYW5nZSIsICJNdXRhdGlvbiIpKQp9CgojIEFwcGx5IHRoZSBjb21wYXJpc29uIGZ1bmN0aW9uCmhlYXRtYXBfZGF0YSA8LSBoZWF0bWFwX2RhdGEgJT4lCiAgZ3JvdXBfYnkoUG9zaXRpb24pICU+JQogIG11dGF0ZShDb21wYXJpc29uID0gY29tcGFyZV9hYShBQSwgQUFbbXV0SUQgPT0gIk5QXzIyMDEyOSJdKSkgJT4lCiAgdW5ncm91cCgpCgojIENyZWF0ZSBhIGNvbnRpbnVvdXMgcG9zaXRpb24gdmFyaWFibGUKaGVhdG1hcF9kYXRhIDwtIGhlYXRtYXBfZGF0YSAlPiUKICBhcnJhbmdlKFBvc2l0aW9uKSAlPiUKICBtdXRhdGUoUG9zaXRpb25JbmRleCA9IGFzLm51bWVyaWMoZmFjdG9yKFBvc2l0aW9uLCBsZXZlbHMgPSB1bmlxdWUoUG9zaXRpb24pKSkpCgojIENyZWF0ZSB0aGUgaGVhdG1hcApFc2NoZXJpY2hpYS5oZWF0bWFwIDwtIGdncGxvdChoZWF0bWFwX2RhdGEsIGFlcyh4ID0gUG9zaXRpb25JbmRleCwgeSA9IG11dElELCBmaWxsID0gQ29tcGFyaXNvbikpICsKICBnZW9tX3RpbGUoY29sb3IgPSAid2hpdGUiLCB3aWR0aCA9IDEsIGhlaWdodCA9IDAuOSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIk5vIGNoYW5nZSIgPSAiZ3JleTkwIiwgIk11dGF0aW9uIiA9ICJyZWQiLCAiTm8gZGF0YSIgPSAid2hpdGUiKSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEsIHZqdXN0ID0gMC41LCBzaXplID0gOCksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gOCksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iCiAgKSArCiAgbGFicyh4ID0gIlBvc2l0aW9uIiwgZmlsbCA9ICJBbWlubyBBY2lkIENoYW5nZSIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoCiAgICBicmVha3MgPSBoZWF0bWFwX2RhdGEkUG9zaXRpb25JbmRleCwKICAgIGxhYmVscyA9IGhlYXRtYXBfZGF0YSRQb3NpdGlvbiwKICAgIGV4cGFuZCA9IGMoMCwgMCkKICApICsKICBzY2FsZV95X2Rpc2NyZXRlKGV4cGFuZCA9IGMoMCwgMCkpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gQUEpLCBzaXplID0gMywgbmEucm0gPSBUUlVFKSArCiAgc2NhbGVfeV9kaXNjcmV0ZShsaW1pdHMgPSByZXYodW5pcXVlKGhlYXRtYXBfZGF0YSRtdXRJRCkpKSAgIyBSZXZlcnNlIHktYXhpcyB0byBwdXQgcmVmZXJlbmNlIG9uIHRvcAoKI3ByaW50KEVzY2hlcmljaGlhLmhlYXRtYXApCmBgYAoKUGxvdCB0aGUgY2hhbmdlIGluIGZpdG5lc3MgYWNyb3NzIHRoZSBUTVAgZ3JhZGllbnQgZm9yIGJvdGggc2FtcGxlczoKYGBge3Igd2FybmluZz1GQUxTRX0KIyBSZXNoYXBlIHRoZSBkYXRhIGZyb20gd2lkZSB0byBsb25nIGZvcm1hdApFc2NoZXJpY2hpYV9sb25nIDwtIEVzY2hlcmljaGlhX2ZpbHRlcmVkICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoImZpdEQiKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkRheSIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiRml0bmVzcyIsCiAgICAgICAgICAgICAgIG5hbWVzX3ByZWZpeCA9ICJmaXREIikgJT4lCiAgbXV0YXRlKERheSA9IGZhY3RvcihEYXksIGxldmVscyA9IGMoIjA1RDAzIiwgIjA2RDAzIiwgIjA3RDAzIiwgIjA4RDAzIiwgIjA5RDAzIiwgIjEwRDAzIiwgIjExRDAzIiksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCIwIiwgIjAuMDU4IiwgIjAuNSIsICIxLjAiLCAiMTAiLCAiNTAiLCAiMjAwIikpKQoKIyBDcmVhdGUgYSBuZXcgY29sdW1uIGZvciBjb2xvcmluZwpFc2NoZXJpY2hpYV9sb25nIDwtIEVzY2hlcmljaGlhX2xvbmcgJT4lCiAgbXV0YXRlKENvbG9yID0gaWZlbHNlKG11dElEID09ICJOUF80MTQ1OTAiLCAiV1QiLCAiTXV0YW50IikpCgojIENyZWF0ZSB0aGUgcGxvdApFc2NoZXJpY2hpYV9wbG90IDwtIGdncGxvdChFc2NoZXJpY2hpYV9sb25nLCBhZXMoeCA9IERheSwgeSA9IEZpdG5lc3MsIGdyb3VwID0gbXV0SUQpKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLTEsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyYXk1MCIpICsKICBnZW9tX2xpbmUoYWVzKGNvbG9yID0gQ29sb3IpLCBzaXplID0gMS4wKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBDb2xvciksIHNpemUgPSAzKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIldUIiA9ICJyZWQiLCAiTXV0YW50IiA9ICJnb2xkMiIpKSArCiAgbGFicyh4ID0gIlRyaW1ldGhvcHJpbSAodWcvbUwpIiwKICAgICAgIHkgPSAiTWVkaWFuIEZpdG5lc3MgKExvZ0ZDKSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKAogICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICdibGFjaycsIHNpemUgPSAxLjApLAogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMS4wKSwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKSArCiAgZ2d0aXRsZSgiRXNjaGVyaWNoaWEgY29saSIpICsgCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJpdGFsaWMiLCBzaXplID0gMTYpKQoKIyBQcmludCB0aGUgcGxvdApwcmludChFc2NoZXJpY2hpYV9wbG90KQpgYGAKCmBgYHtyIGVjaG89RkFMU0V9Cmdnc2F2ZShmaWxlPSJSZXNpc3RhbmNlL1BMT1RTL0ZpbmFsL0VzY2hlcmljaGlhLmZpdG5lc3MucG5nIiwgCiAgICAgICBwbG90PUVzY2hlcmljaGlhX3Bsb3QsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDMsIGhlaWdodCA9IDMuNSwgdW5pdHMgPSAiaW4iKQpgYGAKCmBgYHtyIGVjaG89RkFMU0V9Cmdnc2F2ZShmaWxlPSJSZXNpc3RhbmNlL1BMT1RTL0ZpbmFsL0VzY2hlcmljaGlhLmZpdG5lc3MudjIucG5nIiwgCiAgICAgICBwbG90PUVzY2hlcmljaGlhX3Bsb3QsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDQsIHVuaXRzID0gImluIikKYGBgCgojIyMgU3RyZXB0b2NvY2N1cyBwbmV1bW9uaWFlCgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBGaWx0ZXIgdG8gcmV0YWluIEVzY2hlcmljaGlhIGNvbGkgKE5DQkkuc3BlY2llcykgZnJvbSBMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZApTdHJlcHRvY29jY3VzIDwtIEwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkW0wxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJE5DQkkuc3BlY2llcyA9PSAiU3RyZXB0b2NvY2N1cyBwbmV1bW9uaWFlIiAmIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICFpcy5uYShMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCROQ0JJLnNwZWNpZXMpLCBdCgojIEZpbHRlciB0byBvbmx5IHJldGFpbiBzcGVjaWZpYyB2YXJpYW50cyBmb3IgdGhlIE1TQQpTdHJlcHRvY29jY3VzX2ZpbHRlcmVkIDwtIFN0cmVwdG9jb2NjdXMgJT4lCiAgZmlsdGVyKChtdXRJRCA9PSAiV1BfMDAyMjA1MzI3IiB8IElEID09ICJXUF8wMDIyMDUzMjciKSAmICFpcy5uYShmaXREMTFEMDMpKQoKIyBDcmVhdGUgYSBjc3YgY29weSBvZiB0aGUgZmlsdGVyZWQgZGF0YXNldAp3cml0ZS5jc3YoU3RyZXB0b2NvY2N1c19maWx0ZXJlZCwgZmlsZSA9ICJSZXNpc3RhbmNlL1N0cmVwdG9jb2NjdXMuZml0bmVzcy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCiMgQ3JlYXRlIGEgZnVuY3Rpb24gdG8gd3JpdGUgRkFTVEEgZW50cmllcwp3cml0ZV9mYXN0YSA8LSBmdW5jdGlvbihpZCwgc2VxLCBmaWxlKSB7CiAgY2F0KHBhc3RlMCgiPiIsIGlkLCAiXG4iKSwgZmlsZSA9IGZpbGUsIGFwcGVuZCA9IFRSVUUpCiAgY2F0KHBhc3RlMCgiTSIsIHNlcSwgIlxuIiksIGZpbGUgPSBmaWxlLCBhcHBlbmQgPSBUUlVFKSAgIyBBZGQgIk0iIGF0IHRoZSBiZWdpbm5pbmcgb2YgdGhlIHNlcXVlbmNlCn0KCiMgU3BlY2lmeSB0aGUgb3V0cHV0IGZpbGUgbmFtZQpTdHJlcHRvY29jY3VzX291dHB1dF9maWxlIDwtICJSZXNpc3RhbmNlL0ZBU1RBL1N0cmVwdG9jb2NjdXMuZmFzdGEiCgojIFJlbW92ZSB0aGUgZmlsZSBpZiBpdCBhbHJlYWR5IGV4aXN0cwppZiAoZmlsZS5leGlzdHMoU3RyZXB0b2NvY2N1c19vdXRwdXRfZmlsZSkpIHsKICBmaWxlLnJlbW92ZShTdHJlcHRvY29jY3VzX291dHB1dF9maWxlKQp9CgojIFdyaXRlIHRoZSBGQVNUQSBlbnRyaWVzIHRvIHRoZSBmaWxlCmZvciAoaSBpbiAxOm5yb3coU3RyZXB0b2NvY2N1c19maWx0ZXJlZCkpIHsKICB3cml0ZV9mYXN0YShTdHJlcHRvY29jY3VzX2ZpbHRlcmVkJG11dElEW2ldLCBTdHJlcHRvY29jY3VzX2ZpbHRlcmVkJHNlcVtpXSwgU3RyZXB0b2NvY2N1c19vdXRwdXRfZmlsZSkKfQoKY2F0KCJGQVNUQSBmaWxlIGhhcyBiZWVuIGdlbmVyYXRlZDoiLCBTdHJlcHRvY29jY3VzX291dHB1dF9maWxlLCAiXG4iKQpgYGAKClBlcmZvcm0gYSBtdWx0aXBsZSBzZXF1ZW5jZSBhbGlnbm1lbnQgb24gdGhlIEZBU1RBIGZpbGUgdXNpbmcgQ0xVU1RBTCBPbWVnYToKYGBge2Jhc2h9CiMgTWF5IG5lZWQgdG8gZW5hYmxlIHBlcm1pc3Npb25zIHRvIHJ1biB0aGUgZXhlY3V0YWJsZToKI2NobW9kICt4IGNsdXN0YWxvCi4vU2NyaXB0cy9jbHVzdGFsbyAtaSBSZXNpc3RhbmNlL0ZBU1RBL1N0cmVwdG9jb2NjdXMuZmFzdGEgLW8gUmVzaXN0YW5jZS9GQVNUQS9TdHJlcHRvY29jY3VzLmFsaWduZWQuZmFzdGEgLS1vdXRmbXQ9ZmEgLS1mb3JjZQpgYGAKCkltcG9ydCB0aGUgTVNBIGZpbGUgYW5kIGRldGVybWluZSB3aGljaCBhbWlubyBhY2lkIHBvc2l0aW9ucyBkaWZmZXIgYmV0d2VlbiB0aGUgc2FtcGxlcwpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBJbXBvcnQgdGhlIE1TQSBmaWxlCm1zYSA8LSByZWFkQUFNdWx0aXBsZUFsaWdubWVudCgiUmVzaXN0YW5jZS9GQVNUQS9TdHJlcHRvY29jY3VzLmFsaWduZWQuZmFzdGEiLCBmb3JtYXQ9ImZhc3RhIikKCiMgQ29udmVydCB0byBhIG1hdHJpeCBmb3IgZWFzaWVyIG1hbmlwdWxhdGlvbgptc2FfbWF0cml4IDwtIGFzLm1hdHJpeChtc2EpCgojIFNldCB0aGUgcmVmZXJlbmNlIHNlcXVlbmNlCnJlZl9zZXEgPC0gIldQXzAwMjIwNTMyNyIKCmNhdCgiVXNpbmcgcmVmZXJlbmNlIHNlcXVlbmNlOiIsIHJlZl9zZXEsICJcbiIpCgojIEZpbmQgcG9zaXRpb25zIHdoZXJlIGFtaW5vIGFjaWRzIGRpZmZlcgpkaWZmX3Bvc2l0aW9ucyA8LSB3aGljaChhcHBseShtc2FfbWF0cml4LCAyLCBmdW5jdGlvbihjb2wpIGxlbmd0aCh1bmlxdWUoY29sKSkgPiAxKSkKCiMgQ3JlYXRlIGEgc3VtbWFyeSBkYXRhZnJhbWUKc3VtbWFyeV9kZiA8LSBkYXRhLmZyYW1lKAogIFBvc2l0aW9uID0gZGlmZl9wb3NpdGlvbnMsCiAgUmVmQUEgPSBtc2FfbWF0cml4W3JlZl9zZXEsIGRpZmZfcG9zaXRpb25zXSwKICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKKQoKIyBBZGQgY29sdW1ucyBmb3IgZWFjaCB1bmlxdWUgbXV0SUQKdW5pcXVlX211dElEcyA8LSBzZXRkaWZmKHJvd25hbWVzKG1zYV9tYXRyaXgpLCByZWZfc2VxKQpmb3IgKG11dElEIGluIHVuaXF1ZV9tdXRJRHMpIHsKICBzdW1tYXJ5X2RmW1ttdXRJRF1dIDwtIG1zYV9tYXRyaXhbbXV0SUQsIGRpZmZfcG9zaXRpb25zXQp9CgojIEFkZCBhIGNvbHVtbiBmb3IgdGhlIGFtaW5vIGFjaWQgY2hhbmdlcwpzdW1tYXJ5X2RmJENoYW5nZXMgPC0gYXBwbHkoc3VtbWFyeV9kZiwgMSwgZnVuY3Rpb24ocm93KSB7CiAgY2hhbmdlcyA8LSBzZXRkaWZmKGFzLmNoYXJhY3Rlcihyb3dbMzpuY29sKHN1bW1hcnlfZGYpXSksIHJvd1siUmVmQUEiXSkKICBpZiAobGVuZ3RoKGNoYW5nZXMpID4gMCkgewogICAgcGFzdGUocm93WyJSZWZBQSJdLCBwYXN0ZShjaGFuZ2VzLCBjb2xsYXBzZSA9ICIvIiksIHNlcCA9ICItPiIpCiAgfSBlbHNlIHsKICAgICJObyBjaGFuZ2UiCiAgfQp9KQoKIyBSZW1vdmUgcm93cyB3aXRoICJObyBjaGFuZ2UiCnN1bW1hcnlfZGYgPC0gc3VtbWFyeV9kZltzdW1tYXJ5X2RmJENoYW5nZXMgIT0gIk5vIGNoYW5nZSIsIF0KCiMgUmVvcmRlciBjb2x1bW5zCnN1bW1hcnlfZGYgPC0gc3VtbWFyeV9kZiAlPiUgCiAgc2VsZWN0KFBvc2l0aW9uLCBSZWZBQSwgQ2hhbmdlcywgZXZlcnl0aGluZygpKQoKIyBQcmludCB0aGUgc3VtbWFyeSB0YWJsZQpwcmludChzdW1tYXJ5X2RmLCByb3cubmFtZXMgPSBGQUxTRSkKYGBgCgpBcnJhbmdlIHRoZSBkYXRhIGZvciB0aGUgYW1pbm8gYWNpZCBtdXRhdGlvbnMgaGVhdG1hcDoKYGBge3J9CiMgR2V0IGFsbCBwb3NpdGlvbnMgd2hlcmUgdGhlcmUncyBhIG11dGF0aW9uCmFsbF9wb3NpdGlvbnMgPC0gc29ydCh1bmlxdWUoc3VtbWFyeV9kZiRQb3NpdGlvbikpCgojIFJlc2hhcGUgdGhlIGRhdGEKaGVhdG1hcF9kYXRhIDwtIHN1bW1hcnlfZGYgJT4lCiAgc2VsZWN0KC1DaGFuZ2VzKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IC1jKFBvc2l0aW9uLCBSZWZBQSksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJtdXRJRCIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiQUEiKSAlPiUKICBtdXRhdGUoUG9zaXRpb24gPSBhcy5udW1lcmljKFBvc2l0aW9uKSkKCiMgQWRkIHRoZSByZWZlcmVuY2Ugc2VxdWVuY2UKcmVmX2RhdGEgPC0gc3VtbWFyeV9kZiAlPiUKICBzZWxlY3QoUG9zaXRpb24sIFJlZkFBKSAlPiUKICBtdXRhdGUobXV0SUQgPSAiV1BfMDAyMjA1MzI3IiwgQUEgPSBSZWZBQSkKCmhlYXRtYXBfZGF0YSA8LSBiaW5kX3Jvd3MocmVmX2RhdGEsIGhlYXRtYXBfZGF0YSkKCiMgQ3JlYXRlIGEgZnVuY3Rpb24gdG8gY29tcGFyZSBhbWlubyBhY2lkcwpjb21wYXJlX2FhIDwtIGZ1bmN0aW9uKGFhLCByZWZfYWEpIHsKICBpZmVsc2UoaXMubmEoYWEpIHwgYWEgPT0gIiIsICJObyBkYXRhIiwKICAgICAgICAgaWZlbHNlKGFhID09IHJlZl9hYSwgIk5vIGNoYW5nZSIsICJNdXRhdGlvbiIpKQp9CgojIEFwcGx5IHRoZSBjb21wYXJpc29uIGZ1bmN0aW9uIGFuZCBtb2RpZnkgQUEgZGlzcGxheQpoZWF0bWFwX2RhdGEgPC0gaGVhdG1hcF9kYXRhICU+JQogIGdyb3VwX2J5KFBvc2l0aW9uKSAlPiUKICBtdXRhdGUoCiAgICBDb21wYXJpc29uID0gY29tcGFyZV9hYShBQSwgQUFbbXV0SUQgPT0gIldQXzAwMjIwNTMyNyJdKSwKICAgIERpc3BsYXlBQSA9IGNhc2Vfd2hlbigKICAgICAgbXV0SUQgPT0gIldQXzAwMjIwNTMyNyIgfiBBQSwKICAgICAgQ29tcGFyaXNvbiA9PSAiTXV0YXRpb24iIH4gQUEsCiAgICAgIFRSVUUgfiAiIgogICAgKQogICkgJT4lCiAgdW5ncm91cCgpCgojIENyZWF0ZSBhIGNvbnRpbnVvdXMgcG9zaXRpb24gdmFyaWFibGUKaGVhdG1hcF9kYXRhIDwtIGhlYXRtYXBfZGF0YSAlPiUKICBhcnJhbmdlKFBvc2l0aW9uKSAlPiUKICBtdXRhdGUoUG9zaXRpb25JbmRleCA9IGFzLm51bWVyaWMoZmFjdG9yKFBvc2l0aW9uLCBsZXZlbHMgPSB1bmlxdWUoUG9zaXRpb24pKSkpCgojIENyZWF0ZSBTdHJlcHRvY29jY3VzX2xvbmcKU3RyZXB0b2NvY2N1c19sb25nIDwtIFN0cmVwdG9jb2NjdXNfZmlsdGVyZWQgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgiZml0RCIpLCAKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiVE1QIiwgCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJGaXRuZXNzIiwKICAgICAgICAgICAgICAgbmFtZXNfcHJlZml4ID0gImZpdEQiKQoKIyBDYWxjdWxhdGUgdGhlIGZpdG5lc3MgYXQgdGhlIGhpZ2hlc3QgVE1QIGNvbmNlbnRyYXRpb24gZm9yIGVhY2ggbXV0SUQKbWF4X2ZpdG5lc3MgPC0gU3RyZXB0b2NvY2N1c19sb25nICU+JQogIGZpbHRlcihUTVAgPT0gIjExRDAzIikgJT4lCiAgc2VsZWN0KG11dElELCBtYXhfZml0bmVzcyA9IEZpdG5lc3MpCgojIEpvaW4gbWF4X2ZpdG5lc3MgaW5mb3JtYXRpb24gYW5kIGNyZWF0ZSBNdXRhbnRDb2xvcgpoZWF0bWFwX2RhdGEgPC0gaGVhdG1hcF9kYXRhICU+JQogIGxlZnRfam9pbihtYXhfZml0bmVzcywgYnkgPSAibXV0SUQiKSAlPiUKICBtdXRhdGUoTXV0YW50Q29sb3IgPSBjYXNlX3doZW4oCiAgICBtdXRJRCA9PSAiV1BfMDAyMjA1MzI3IiB+ICJyZWQiLAogICAgaXMubmEobWF4X2ZpdG5lc3MpIH4gImdyYXkiLAogICAgbWF4X2ZpdG5lc3MgPCAtMSB+ICJkYXJrYmx1ZSIsCiAgICBtYXhfZml0bmVzcyA+PSAtMSB+ICJnb2xkMiIKICApKQoKIyBDcmVhdGUgYSBuZXcgY29sdW1uIGZvciBvcmRlcmluZwpoZWF0bWFwX2RhdGEgPC0gaGVhdG1hcF9kYXRhICU+JQogIG11dGF0ZShPcmRlckdyb3VwID0gY2FzZV93aGVuKAogICAgbXV0SUQgPT0gIldQXzAwMjIwNTMyNyIgfiAxLAogICAgbWF4X2ZpdG5lc3MgPj0gLTEgfiAyLAogICAgbWF4X2ZpdG5lc3MgPCAtMSB+IDMsCiAgICBUUlVFIH4gNCAgIyBmb3IgYW55IE5BIG9yIG90aGVyIGNhc2VzCiAgKSkKYGBgCgpIZWF0bWFwIGZvciBhbWlubyBhY2lkIG11dGF0aW9ucyBieSBwb3NpdGlvbiBiYXNlZCBvbiByZWZlcmVuY2Ugc2VxdWVuY2U6CmBgYHtyfQojIE9yZGVyIHRoZSBkYXRhIHdpdGggcmVmZXJlbmNlIG9uIHRvcApoZWF0bWFwX2RhdGEgPC0gaGVhdG1hcF9kYXRhICU+JQogIGFycmFuZ2UoT3JkZXJHcm91cCwgZGVzYyhtYXhfZml0bmVzcykpICU+JQogIG11dGF0ZShtdXRJRCA9IGZhY3RvcihtdXRJRCwgbGV2ZWxzID0gdW5pcXVlKG11dElEKSkpCgojIEVuc3VyZSB0aGUgY29sb3JzIGFyZSBhc3NpZ25lZCBjb3JyZWN0bHkKeV9jb2xvcnMgPC0gaGVhdG1hcF9kYXRhICU+JQogIGRpc3RpbmN0KG11dElELCBNdXRhbnRDb2xvcikgJT4lCiAgYXJyYW5nZShtYXRjaChtdXRJRCwgbGV2ZWxzKGhlYXRtYXBfZGF0YSRtdXRJRCkpKQoKIyBNb2RpZnkgdGhlIGhlYXRtYXBfZGF0YSB0byBpbmNsdWRlIHRoZSBjb2xvciBpbmZvcm1hdGlvbiBmb3IgbXV0YXRpb25zIGFuZCB0ZXh0CmhlYXRtYXBfZGF0YSA8LSBoZWF0bWFwX2RhdGEgJT4lCiAgbXV0YXRlKAogICAgTXV0YXRpb25Db2xvciA9IGNhc2Vfd2hlbigKICAgICAgQ29tcGFyaXNvbiA9PSAiTXV0YXRpb24iICYgbWF4X2ZpdG5lc3MgPj0gLTEgfiAiZ29sZDIiLAogICAgICBDb21wYXJpc29uID09ICJNdXRhdGlvbiIgJiBtYXhfZml0bmVzcyA8IC0xIH4gImRhcmtibHVlIiwKICAgICAgVFJVRSB+IENvbXBhcmlzb24KICAgICksCiAgICBUZXh0Q29sb3IgPSBjYXNlX3doZW4oCiAgICAgIE11dGF0aW9uQ29sb3IgPT0gImRhcmtibHVlIiB+ICJ3aGl0ZSIsCiAgICAgIFRSVUUgfiAiYmxhY2siCiAgICApCiAgKQoKIyBDcmVhdGUgdGhlIGhlYXRtYXAKU3RyZXB0b2NvY2N1cy5oZWF0bWFwIDwtIGdncGxvdChoZWF0bWFwX2RhdGEsIGFlcyh4ID0gUG9zaXRpb25JbmRleCwgeSA9IG11dElELCBmaWxsID0gTXV0YXRpb25Db2xvcikpICsKICBnZW9tX3RpbGUoY29sb3IgPSAid2hpdGUiLCB3aWR0aCA9IDEsIGhlaWdodCA9IDAuOSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIk5vIGNoYW5nZSIgPSAiZ3JheTkwIiwgImdvbGQyIiA9ICJnb2xkMiIsICJkYXJrYmx1ZSIgPSAiZGFya2JsdWUiLCAiTm8gZGF0YSIgPSAid2hpdGUiKSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEsIHZqdXN0ID0gMC41LCBzaXplID0gMTIpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9saW5lKGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDAuNSksCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAid2hpdGUiLCBzaXplID0gMC41KSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIKICApICsKICBsYWJzKHggPSAiUG9zaXRpb24iLCBmaWxsID0gIkFtaW5vIEFjaWQgQ2hhbmdlIikgKwogIHNjYWxlX3hfY29udGludW91cygKICAgIGJyZWFrcyA9IGhlYXRtYXBfZGF0YSRQb3NpdGlvbkluZGV4LAogICAgbGFiZWxzID0gaGVhdG1hcF9kYXRhJFBvc2l0aW9uLAogICAgZXhwYW5kID0gYygwLCAwKQogICkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBEaXNwbGF5QUEsIGNvbG9yID0gVGV4dENvbG9yKSwgc2l6ZSA9IDQsIG5hLnJtID0gVFJVRSwgZm9udGZhY2UgPSAiYm9sZCIpICsKICBzY2FsZV9jb2xvcl9pZGVudGl0eSgpICsKICBzY2FsZV95X2Rpc2NyZXRlKGxpbWl0cyA9IHJldihsZXZlbHMoaGVhdG1hcF9kYXRhJG11dElEKSksIGV4cGFuZCA9IGMoMCwgMCkpCgojIEFwcGx5IGNvcnJlY3QgY29sb3JzIHRvIHktYXhpcyBsYWJlbHMKU3RyZXB0b2NvY2N1cy5oZWF0bWFwIDwtIFN0cmVwdG9jb2NjdXMuaGVhdG1hcCArCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoY29sb3IgPSByZXYoeV9jb2xvcnMkTXV0YW50Q29sb3IpKSkgKyAKICBnZ3RpdGxlKCJTdHJlcHRvY29jY3VzIHBuZXVtb25pYWUiKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiaXRhbGljIiwgc2l6ZSA9IDE2KSkKCnByaW50KFN0cmVwdG9jb2NjdXMuaGVhdG1hcCkKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIFBORwpnZ3NhdmUoZmlsZT0iUmVzaXN0YW5jZS9QTE9UUy9GaW5hbC9TdHJlcHRvY29jY3VzLmhlYXRtYXAucG5nIiwgCiAgICAgICBwbG90PVN0cmVwdG9jb2NjdXMuaGVhdG1hcCwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gNy41LCBoZWlnaHQgPSA0LjUsIHVuaXRzID0gImluIikKYGBgCgpQbG90IHRoZSBjaGFuZ2UgaW4gZml0bmVzcyBhY3Jvc3MgdGhlIFRNUCBncmFkaWVudCBmb3IgYm90aCBzYW1wbGVzOgpgYGB7ciB3YXJuaW5nPUZBTFNFfQojIFJlc2hhcGUgdGhlIGRhdGEgZnJvbSB3aWRlIHRvIGxvbmcgZm9ybWF0ClN0cmVwdG9jb2NjdXNfbG9uZyA8LSBTdHJlcHRvY29jY3VzX2ZpbHRlcmVkICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoImZpdEQiKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIlRNUCIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiRml0bmVzcyIsCiAgICAgICAgICAgICAgIG5hbWVzX3ByZWZpeCA9ICJmaXREIikgJT4lCiAgbXV0YXRlKERheSA9IGZhY3RvcihUTVAsIGxldmVscyA9IGMoIjA1RDAzIiwgIjA2RDAzIiwgIjA3RDAzIiwgIjA4RDAzIiwgIjA5RDAzIiwgIjEwRDAzIiwgIjExRDAzIiksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCIwIiwgIjAuMDU4IiwgIjAuNSIsICIxLjAiLCAiMTAiLCAiNTAiLCAiMjAwIikpKQoKIyBDYWxjdWxhdGUgdGhlIGZpdG5lc3MgYXQgdGhlIGhpZ2hlc3QgVE1QIGNvbmNlbnRyYXRpb24gZm9yIGVhY2ggbXV0SUQKU3RyZXB0b2NvY2N1c19tYXhfZml0bmVzcyA8LSBTdHJlcHRvY29jY3VzX2xvbmcgJT4lCiAgZmlsdGVyKFRNUCA9PSAiMTFEMDMiKSAlPiUKICBzZWxlY3QobXV0SUQsIG1heF9maXRuZXNzID0gRml0bmVzcykKCiMgSm9pbiB0aGlzIGluZm9ybWF0aW9uIGJhY2sgdG8gdGhlIG9yaWdpbmFsIGRhdGEgYW5kIGNvbG9yIGFjY29yZGluZ2x5ClN0cmVwdG9jb2NjdXNfbG9uZyA8LSBTdHJlcHRvY29jY3VzX2xvbmcgJT4lCiAgbGVmdF9qb2luKFN0cmVwdG9jb2NjdXNfbWF4X2ZpdG5lc3MsIGJ5ID0gIm11dElEIikgJT4lCiAgbXV0YXRlKENvbG9yID0gY2FzZV93aGVuKAogICAgbXV0SUQgPT0gIldQXzAwMjIwNTMyNyIgfiAiV1QiLAogICAgaXMubmEobWF4X2ZpdG5lc3MpIH4gIk11dGFudF9Vbmtub3duIiwKICAgIG1heF9maXRuZXNzIDwgLTEgfiAiTXV0YW50X0xvdyIsCiAgICBtYXhfZml0bmVzcyA+PSAtMSB+ICJNdXRhbnRfSGlnaCIKICApKQoKIyBSZW9yZGVyIHRoZSBsZXZlbHMgb2YgQ29sb3IgZmFjdG9yIHRvIHBsb3QgV1QgbGFzdApTdHJlcHRvY29jY3VzX2xvbmckQ29sb3IgPC0gZmFjdG9yKFN0cmVwdG9jb2NjdXNfbG9uZyRDb2xvciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiTXV0YW50X1Vua25vd24iLCAiTXV0YW50X0xvdyIsICJNdXRhbnRfSGlnaCIsICJXVCIpKQoKIyBTZXBhcmF0ZSBXVCBhbmQgbXV0YW50IGRhdGEKV1RfZGF0YSA8LSBTdHJlcHRvY29jY3VzX2xvbmcgJT4lIGZpbHRlcihtdXRJRCA9PSAiV1BfMDAyMjA1MzI3IikKbXV0YW50X2RhdGEgPC0gU3RyZXB0b2NvY2N1c19sb25nICU+JSBmaWx0ZXIobXV0SUQgIT0gIldQXzAwMjIwNTMyNyIpCmBgYAoKRml0bmVzcyBwbG90dGluZyBvdmVyIHRoZSBUTVAgZ3JhZGllbnQKYGBge3J9CiMgQ3JlYXRlIHRoZSBwbG90ClN0cmVwdG9jb2NjdXNfcGxvdCA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLTEsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyYXk1MCIpICsKICAjIFBsb3QgbXV0YW50IGxpbmVzIGZpcnN0CiAgZ2VvbV9saW5lKGRhdGEgPSBtdXRhbnRfZGF0YSwgYWVzKHggPSBEYXksIHkgPSBGaXRuZXNzLCBncm91cCA9IG11dElELCBjb2xvciA9IENvbG9yKSwgc2l6ZSA9IDEuMCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IG11dGFudF9kYXRhLCBhZXMoeCA9IERheSwgeSA9IEZpdG5lc3MsIGNvbG9yID0gQ29sb3IpLCBzaXplID0gMykgKwogICMgUGxvdCBXVCBsaW5lIG9uIHRvcAogIGdlb21fbGluZShkYXRhID0gV1RfZGF0YSwgYWVzKHggPSBEYXksIHkgPSBGaXRuZXNzLCBncm91cCA9IG11dElEKSwgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDIuMCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IFdUX2RhdGEsIGFlcyh4ID0gRGF5LCB5ID0gRml0bmVzcyksIGNvbG9yID0gInJlZCIsIHNpemUgPSAzKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIk11dGFudF9Mb3ciID0gImRhcmtibHVlIiwgIk11dGFudF9IaWdoIiA9ICJnb2xkMiIsICJNdXRhbnRfVW5rbm93biIgPSAiZ3JheSIpKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSBjKCIwIiwgIjAuMDU4IiwgIjAuNSIsICIxLjAiLCAiMTAiLCAiNTAiLCAiMjAwIikpICsKICBsYWJzKHggPSAiVHJpbWV0aG9wcmltICh1Zy9tTCkiLAogICAgICAgeSA9ICJNZWRpYW4gRml0bmVzcyAoTG9nRkMpIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2JsYWNrJywgc2l6ZSA9IDEuMCksCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAxLjApLAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsgCiAgZ2d0aXRsZSgiU3RyZXB0b2NvY2N1cyBwbmV1bW9uaWFlIikgKyAKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gIml0YWxpYyIsIHNpemUgPSAxNikpICMgQ2VudGVyIGFuZCBpdGFsaWNpemUgdGhlIHRpdGxlCgojIFByaW50IHRoZSBwbG90CnByaW50KFN0cmVwdG9jb2NjdXNfcGxvdCkKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIFBORwpnZ3NhdmUoZmlsZT0iUmVzaXN0YW5jZS9QTE9UUy9GaW5hbC9TdHJlcHRvY29jY3VzLmZpdG5lc3MucG5nIiwgCiAgICAgICBwbG90PVN0cmVwdG9jb2NjdXNfcGxvdCwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gNC41LCBoZWlnaHQgPSA0LjUsIHVuaXRzID0gImluIikKYGBgCgpQbG90IHRvZ2V0aGVyOgpgYGB7cn0KcGF0Y2gxIDwtIFN0cmVwdG9jb2NjdXNfcGxvdCB8IFN0cmVwdG9jb2NjdXMuaGVhdG1hcApwYXRjaDEKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIFBORwpnZ3NhdmUoZmlsZT0iUmVzaXN0YW5jZS9QTE9UUy9GaW5hbC9TdHJlcHRvY29jY3VzLkJvdGgucG5nIiwgCiAgICAgICBwbG90PXBhdGNoMSwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gMTEsIGhlaWdodCA9IDUsIHVuaXRzID0gImluIikKYGBgCgojIyMgQmFjaWxsdXMgY2VyZXVzCgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBGaWx0ZXIgdG8gcmV0YWluIEVzY2hlcmljaGlhIGNvbGkgKE5DQkkuc3BlY2llcykgZnJvbSBMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZApCYWNpbGx1cyA8LSBMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZFtMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCROQ0JJLnNwZWNpZXMgPT0gIkJhY2lsbHVzIGNlcmV1cyIgJiAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAhaXMubmEoTDE1X2hvbW9fbXV0X2dvb2RfZmlsdGVyZWQkTkNCSS5zcGVjaWVzKSwgXQoKIyBGaWx0ZXIgdG8gb25seSByZXRhaW4gc3BlY2lmaWMgdmFyaWFudHMgZm9yIHRoZSBNU0EKQmFjaWxsdXNfZmlsdGVyZWQgPC0gQmFjaWxsdXMgJT4lCiAgZmlsdGVyKChtdXRJRCA9PSAiTlBfODMxOTU3IiB8IElEID09ICJOUF84MzE5NTciKSAmICFpcy5uYShmaXREMTFEMDMpKQoKIyBXcml0ZSB0aGUgZGF0YSBmcmFtZSB0byBhIENTViBmaWxlCndyaXRlLmNzdihCYWNpbGx1c19maWx0ZXJlZCwgIlJlc2lzdGFuY2UvQmFjaWxsdXMuZml0bmVzcy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCiMgQ3JlYXRlIGEgZnVuY3Rpb24gdG8gd3JpdGUgRkFTVEEgZW50cmllcwp3cml0ZV9mYXN0YSA8LSBmdW5jdGlvbihpZCwgc2VxLCBmaWxlKSB7CiAgY2F0KHBhc3RlMCgiPiIsIGlkLCAiXG4iKSwgZmlsZSA9IGZpbGUsIGFwcGVuZCA9IFRSVUUpCiAgY2F0KHBhc3RlMCgiTSIsIHNlcSwgIlxuIiksIGZpbGUgPSBmaWxlLCBhcHBlbmQgPSBUUlVFKSAgIyBBZGQgIk0iIGF0IHRoZSBiZWdpbm5pbmcgb2YgdGhlIHNlcXVlbmNlCn0KCiMgU3BlY2lmeSB0aGUgb3V0cHV0IGZpbGUgbmFtZQpCYWNpbGx1c19vdXRwdXRfZmlsZSA8LSAiUmVzaXN0YW5jZS9GQVNUQS9CYWNpbGx1cy5mYXN0YSIKCiMgUmVtb3ZlIHRoZSBmaWxlIGlmIGl0IGFscmVhZHkgZXhpc3RzCmlmIChmaWxlLmV4aXN0cyhCYWNpbGx1c19vdXRwdXRfZmlsZSkpIHsKICBmaWxlLnJlbW92ZShCYWNpbGx1c19vdXRwdXRfZmlsZSkKfQoKIyBXcml0ZSB0aGUgRkFTVEEgZW50cmllcyB0byB0aGUgZmlsZQpmb3IgKGkgaW4gMTpucm93KEJhY2lsbHVzX2ZpbHRlcmVkKSkgewogIHdyaXRlX2Zhc3RhKEJhY2lsbHVzX2ZpbHRlcmVkJG11dElEW2ldLCBCYWNpbGx1c19maWx0ZXJlZCRzZXFbaV0sIEJhY2lsbHVzX291dHB1dF9maWxlKQp9CgpjYXQoIkZBU1RBIGZpbGUgaGFzIGJlZW4gZ2VuZXJhdGVkOiIsIEJhY2lsbHVzX291dHB1dF9maWxlLCAiXG4iKQpgYGAKClBlcmZvcm0gYSBtdWx0aXBsZSBzZXF1ZW5jZSBhbGlnbm1lbnQgb24gdGhlIEZBU1RBIGZpbGUgdXNpbmcgQ0xVU1RBTCBPbWVnYToKYGBge2Jhc2h9CiMgTWF5IG5lZWQgdG8gZW5hYmxlIHBlcm1pc3Npb25zIHRvIHJ1biB0aGUgZXhlY3V0YWJsZToKI2NobW9kICt4IGNsdXN0YWxvCi4vU2NyaXB0cy9jbHVzdGFsbyAtaSBSZXNpc3RhbmNlL0ZBU1RBL0JhY2lsbHVzLmZhc3RhIC1vIFJlc2lzdGFuY2UvRkFTVEEvQmFjaWxsdXMuYWxpZ25lZC5mYXN0YSAtLW91dGZtdD1mYSAtLWZvcmNlCmBgYAoKSW1wb3J0IHRoZSBNU0EgZmlsZSBhbmQgZGV0ZXJtaW5lIHdoaWNoIGFtaW5vIGFjaWQgcG9zaXRpb25zIGRpZmZlciBiZXR3ZWVuIHRoZSBzYW1wbGVzCmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIEltcG9ydCB0aGUgTVNBIGZpbGUKbXNhIDwtIHJlYWRBQU11bHRpcGxlQWxpZ25tZW50KCJSZXNpc3RhbmNlL0ZBU1RBL0JhY2lsbHVzLmFsaWduZWQuZmFzdGEiLCBmb3JtYXQ9ImZhc3RhIikKCiMgQ29udmVydCB0byBhIG1hdHJpeCBmb3IgZWFzaWVyIG1hbmlwdWxhdGlvbgptc2FfbWF0cml4IDwtIGFzLm1hdHJpeChtc2EpCgojIFNldCB0aGUgcmVmZXJlbmNlIHNlcXVlbmNlCnJlZl9zZXEgPC0gIk5QXzgzMTk1NyIKCmNhdCgiVXNpbmcgcmVmZXJlbmNlIHNlcXVlbmNlOiIsIHJlZl9zZXEsICJcbiIpCgojIEZpbmQgcG9zaXRpb25zIHdoZXJlIGFtaW5vIGFjaWRzIGRpZmZlcgpkaWZmX3Bvc2l0aW9ucyA8LSB3aGljaChhcHBseShtc2FfbWF0cml4LCAyLCBmdW5jdGlvbihjb2wpIGxlbmd0aCh1bmlxdWUoY29sKSkgPiAxKSkKCiMgQ3JlYXRlIGEgc3VtbWFyeSBkYXRhZnJhbWUKc3VtbWFyeV9kZiA8LSBkYXRhLmZyYW1lKAogIFBvc2l0aW9uID0gZGlmZl9wb3NpdGlvbnMsCiAgUmVmQUEgPSBtc2FfbWF0cml4W3JlZl9zZXEsIGRpZmZfcG9zaXRpb25zXSwKICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKKQoKIyBBZGQgY29sdW1ucyBmb3IgZWFjaCB1bmlxdWUgbXV0SUQKdW5pcXVlX211dElEcyA8LSBzZXRkaWZmKHJvd25hbWVzKG1zYV9tYXRyaXgpLCByZWZfc2VxKQpmb3IgKG11dElEIGluIHVuaXF1ZV9tdXRJRHMpIHsKICBzdW1tYXJ5X2RmW1ttdXRJRF1dIDwtIG1zYV9tYXRyaXhbbXV0SUQsIGRpZmZfcG9zaXRpb25zXQp9CgojIEFkZCBhIGNvbHVtbiBmb3IgdGhlIGFtaW5vIGFjaWQgY2hhbmdlcwpzdW1tYXJ5X2RmJENoYW5nZXMgPC0gYXBwbHkoc3VtbWFyeV9kZiwgMSwgZnVuY3Rpb24ocm93KSB7CiAgY2hhbmdlcyA8LSBzZXRkaWZmKGFzLmNoYXJhY3Rlcihyb3dbMzpuY29sKHN1bW1hcnlfZGYpXSksIHJvd1siUmVmQUEiXSkKICBpZiAobGVuZ3RoKGNoYW5nZXMpID4gMCkgewogICAgcGFzdGUocm93WyJSZWZBQSJdLCBwYXN0ZShjaGFuZ2VzLCBjb2xsYXBzZSA9ICIvIiksIHNlcCA9ICItPiIpCiAgfSBlbHNlIHsKICAgICJObyBjaGFuZ2UiCiAgfQp9KQoKIyBSZW1vdmUgcm93cyB3aXRoICJObyBjaGFuZ2UiCnN1bW1hcnlfZGYgPC0gc3VtbWFyeV9kZltzdW1tYXJ5X2RmJENoYW5nZXMgIT0gIk5vIGNoYW5nZSIsIF0KCiMgUmVvcmRlciBjb2x1bW5zCnN1bW1hcnlfZGYgPC0gc3VtbWFyeV9kZiAlPiUgCiAgc2VsZWN0KFBvc2l0aW9uLCBSZWZBQSwgQ2hhbmdlcywgZXZlcnl0aGluZygpKQoKIyBQcmludCB0aGUgc3VtbWFyeSB0YWJsZQpwcmludChzdW1tYXJ5X2RmLCByb3cubmFtZXMgPSBGQUxTRSkKYGBgCgpBcnJhbmdlIHRoZSBkYXRhIGZvciB0aGUgYW1pbm8gYWNpZCBtdXRhdGlvbnMgaGVhdG1hcDoKYGBge3J9CiMgR2V0IGFsbCBwb3NpdGlvbnMgd2hlcmUgdGhlcmUncyBhIG11dGF0aW9uCmFsbF9wb3NpdGlvbnMgPC0gc29ydCh1bmlxdWUoc3VtbWFyeV9kZiRQb3NpdGlvbikpCgojIFJlc2hhcGUgdGhlIGRhdGEKaGVhdG1hcF9kYXRhIDwtIHN1bW1hcnlfZGYgJT4lCiAgc2VsZWN0KC1DaGFuZ2VzKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IC1jKFBvc2l0aW9uLCBSZWZBQSksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJtdXRJRCIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiQUEiKSAlPiUKICBtdXRhdGUoUG9zaXRpb24gPSBhcy5udW1lcmljKFBvc2l0aW9uKSkKCiMgQWRkIHRoZSByZWZlcmVuY2Ugc2VxdWVuY2UKcmVmX2RhdGEgPC0gc3VtbWFyeV9kZiAlPiUKICBzZWxlY3QoUG9zaXRpb24sIFJlZkFBKSAlPiUKICBtdXRhdGUobXV0SUQgPSAiTlBfODMxOTU3IiwgQUEgPSBSZWZBQSkKCmhlYXRtYXBfZGF0YSA8LSBiaW5kX3Jvd3MocmVmX2RhdGEsIGhlYXRtYXBfZGF0YSkKCiMgQ3JlYXRlIGEgZnVuY3Rpb24gdG8gY29tcGFyZSBhbWlubyBhY2lkcwpjb21wYXJlX2FhIDwtIGZ1bmN0aW9uKGFhLCByZWZfYWEpIHsKICBpZmVsc2UoaXMubmEoYWEpIHwgYWEgPT0gIiIsICJObyBkYXRhIiwKICAgICAgICAgaWZlbHNlKGFhID09IHJlZl9hYSwgIk5vIGNoYW5nZSIsICJNdXRhdGlvbiIpKQp9CgojIEFwcGx5IHRoZSBjb21wYXJpc29uIGZ1bmN0aW9uIGFuZCBtb2RpZnkgQUEgZGlzcGxheQpoZWF0bWFwX2RhdGEgPC0gaGVhdG1hcF9kYXRhICU+JQogIGdyb3VwX2J5KFBvc2l0aW9uKSAlPiUKICBtdXRhdGUoCiAgICBDb21wYXJpc29uID0gY29tcGFyZV9hYShBQSwgQUFbbXV0SUQgPT0gIk5QXzgzMTk1NyJdKSwKICAgIERpc3BsYXlBQSA9IGNhc2Vfd2hlbigKICAgICAgbXV0SUQgPT0gIk5QXzgzMTk1NyIgfiBBQSwKICAgICAgQ29tcGFyaXNvbiA9PSAiTXV0YXRpb24iIH4gQUEsCiAgICAgIFRSVUUgfiAiIgogICAgKQogICkgJT4lCiAgdW5ncm91cCgpCgojIENyZWF0ZSBhIGNvbnRpbnVvdXMgcG9zaXRpb24gdmFyaWFibGUKaGVhdG1hcF9kYXRhIDwtIGhlYXRtYXBfZGF0YSAlPiUKICBhcnJhbmdlKFBvc2l0aW9uKSAlPiUKICBtdXRhdGUoUG9zaXRpb25JbmRleCA9IGFzLm51bWVyaWMoZmFjdG9yKFBvc2l0aW9uLCBsZXZlbHMgPSB1bmlxdWUoUG9zaXRpb24pKSkpCgojIENyZWF0ZSBCYWNpbGx1c19sb25nCkJhY2lsbHVzX2xvbmcgPC0gQmFjaWxsdXNfZmlsdGVyZWQgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgiZml0RCIpLCAKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiVE1QIiwgCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJGaXRuZXNzIiwKICAgICAgICAgICAgICAgbmFtZXNfcHJlZml4ID0gImZpdEQiKQoKIyBDYWxjdWxhdGUgdGhlIGZpdG5lc3MgYXQgdGhlIGhpZ2hlc3QgVE1QIGNvbmNlbnRyYXRpb24gZm9yIGVhY2ggbXV0SUQKbWF4X2ZpdG5lc3MgPC0gQmFjaWxsdXNfbG9uZyAlPiUKICBmaWx0ZXIoVE1QID09ICIxMUQwMyIpICU+JQogIHNlbGVjdChtdXRJRCwgbWF4X2ZpdG5lc3MgPSBGaXRuZXNzKQoKIyBKb2luIG1heF9maXRuZXNzIGluZm9ybWF0aW9uIGFuZCBjcmVhdGUgTXV0YW50Q29sb3IKaGVhdG1hcF9kYXRhIDwtIGhlYXRtYXBfZGF0YSAlPiUKICBsZWZ0X2pvaW4obWF4X2ZpdG5lc3MsIGJ5ID0gIm11dElEIikgJT4lCiAgbXV0YXRlKE11dGFudENvbG9yID0gY2FzZV93aGVuKAogICAgbXV0SUQgPT0gIk5QXzgzMTk1NyIgfiAicmVkIiwKICAgIGlzLm5hKG1heF9maXRuZXNzKSB+ICJncmF5IiwKICAgIG1heF9maXRuZXNzIDwgLTEgfiAiZGFya2JsdWUiLAogICAgbWF4X2ZpdG5lc3MgPj0gLTEgfiAiZ29sZDIiCiAgKSkKCiMgQ3JlYXRlIGEgbmV3IGNvbHVtbiBmb3Igb3JkZXJpbmcKaGVhdG1hcF9kYXRhIDwtIGhlYXRtYXBfZGF0YSAlPiUKICBtdXRhdGUoT3JkZXJHcm91cCA9IGNhc2Vfd2hlbigKICAgIG11dElEID09ICJOUF84MzE5NTciIH4gMSwKICAgIG1heF9maXRuZXNzID49IC0xIH4gMiwKICAgIG1heF9maXRuZXNzIDwgLTEgfiAzLAogICAgVFJVRSB+IDQgICMgZm9yIGFueSBOQSBvciBvdGhlciBjYXNlcwogICkpCmBgYAoKSGVhdG1hcCBmb3IgYW1pbm8gYWNpZCBtdXRhdGlvbnMgYnkgcG9zaXRpb24gYmFzZWQgb24gcmVmZXJlbmNlIHNlcXVlbmNlOgpgYGB7cn0KIyBPcmRlciB0aGUgZGF0YSB3aXRoIHJlZmVyZW5jZSBvbiB0b3AKaGVhdG1hcF9kYXRhIDwtIGhlYXRtYXBfZGF0YSAlPiUKICBhcnJhbmdlKE9yZGVyR3JvdXAsIGRlc2MobWF4X2ZpdG5lc3MpKSAlPiUKICBtdXRhdGUobXV0SUQgPSBmYWN0b3IobXV0SUQsIGxldmVscyA9IHVuaXF1ZShtdXRJRCkpKQoKIyBFbnN1cmUgdGhlIGNvbG9ycyBhcmUgYXNzaWduZWQgY29ycmVjdGx5CnlfY29sb3JzIDwtIGhlYXRtYXBfZGF0YSAlPiUKICBkaXN0aW5jdChtdXRJRCwgTXV0YW50Q29sb3IpICU+JQogIGFycmFuZ2UobWF0Y2gobXV0SUQsIGxldmVscyhoZWF0bWFwX2RhdGEkbXV0SUQpKSkKCiMgTW9kaWZ5IHRoZSBoZWF0bWFwX2RhdGEgdG8gaW5jbHVkZSB0aGUgY29sb3IgaW5mb3JtYXRpb24gZm9yIG11dGF0aW9ucyBhbmQgdGV4dApoZWF0bWFwX2RhdGEgPC0gaGVhdG1hcF9kYXRhICU+JQogIG11dGF0ZSgKICAgIE11dGF0aW9uQ29sb3IgPSBjYXNlX3doZW4oCiAgICAgIENvbXBhcmlzb24gPT0gIk11dGF0aW9uIiAmIG1heF9maXRuZXNzID49IC0xIH4gImdvbGQyIiwKICAgICAgQ29tcGFyaXNvbiA9PSAiTXV0YXRpb24iICYgbWF4X2ZpdG5lc3MgPCAtMSB+ICJkYXJrYmx1ZSIsCiAgICAgIFRSVUUgfiBDb21wYXJpc29uCiAgICApLAogICAgVGV4dENvbG9yID0gY2FzZV93aGVuKAogICAgICBNdXRhdGlvbkNvbG9yID09ICJkYXJrYmx1ZSIgfiAid2hpdGUiLAogICAgICBUUlVFIH4gImJsYWNrIgogICAgKQogICkKCiMgQ3JlYXRlIHRoZSBoZWF0bWFwCkJhY2lsbHVzLmhlYXRtYXAgPC0gZ2dwbG90KGhlYXRtYXBfZGF0YSwgYWVzKHggPSBQb3NpdGlvbkluZGV4LCB5ID0gbXV0SUQsIGZpbGwgPSBNdXRhdGlvbkNvbG9yKSkgKwogIGdlb21fdGlsZShjb2xvciA9ICJ3aGl0ZSIsIHdpZHRoID0gMSwgaGVpZ2h0ID0gMC45KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiTm8gY2hhbmdlIiA9ICJncmF5OTAiLCAiZ29sZDIiID0gImdvbGQyIiwgImRhcmtibHVlIiA9ICJkYXJrYmx1ZSIsICJObyBkYXRhIiA9ICJ3aGl0ZSIpKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSwgdmp1c3QgPSAwLjUsIHNpemUgPSAxMiksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2xpbmUoY29sb3IgPSAid2hpdGUiLCBzaXplID0gMC41KSwKICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSAwLjUpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIgogICkgKwogIGxhYnMoeCA9ICJQb3NpdGlvbiIsIGZpbGwgPSAiQW1pbm8gQWNpZCBDaGFuZ2UiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKAogICAgYnJlYWtzID0gaGVhdG1hcF9kYXRhJFBvc2l0aW9uSW5kZXgsCiAgICBsYWJlbHMgPSBoZWF0bWFwX2RhdGEkUG9zaXRpb24sCiAgICBleHBhbmQgPSBjKDAsIDApCiAgKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IERpc3BsYXlBQSwgY29sb3IgPSBUZXh0Q29sb3IpLCBzaXplID0gNiwgbmEucm0gPSBUUlVFLCBmb250ZmFjZSA9ICJib2xkIikgKwogIHNjYWxlX2NvbG9yX2lkZW50aXR5KCkgKwogIHNjYWxlX3lfZGlzY3JldGUobGltaXRzID0gcmV2KGxldmVscyhoZWF0bWFwX2RhdGEkbXV0SUQpKSwgZXhwYW5kID0gYygwLCAwKSkKCiMgQXBwbHkgY29ycmVjdCBjb2xvcnMgdG8geS1heGlzIGxhYmVscwpCYWNpbGx1cy5oZWF0bWFwIDwtIEJhY2lsbHVzLmhlYXRtYXAgKwogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gcmV2KHlfY29sb3JzJE11dGFudENvbG9yKSkpICsgCiAgZ2d0aXRsZSgiQmFjaWxsdXMgY2VyZXVzIikgKyAKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gIml0YWxpYyIsIHNpemUgPSAxNikpCgpwcmludChCYWNpbGx1cy5oZWF0bWFwKQpgYGAKCmBgYHtyIGVjaG89RkFMU0V9CiMgUE5HCmdnc2F2ZShmaWxlPSJSZXNpc3RhbmNlL1BMT1RTL0ZpbmFsL0JhY2lsbHVzLmhlYXRtYXAucG5nIiwgCiAgICAgICBwbG90PUJhY2lsbHVzLmhlYXRtYXAsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDcuNSwgaGVpZ2h0ID0gNC41LCB1bml0cyA9ICJpbiIpCmBgYAoKUGxvdCB0aGUgY2hhbmdlIGluIGZpdG5lc3MgYWNyb3NzIHRoZSBUTVAgZ3JhZGllbnQgZm9yIGJvdGggc2FtcGxlczoKYGBge3Igd2FybmluZz1GQUxTRX0KIyBSZXNoYXBlIHRoZSBkYXRhIGZyb20gd2lkZSB0byBsb25nIGZvcm1hdApCYWNpbGx1c19sb25nIDwtIEJhY2lsbHVzX2ZpbHRlcmVkICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoImZpdEQiKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIlRNUCIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiRml0bmVzcyIsCiAgICAgICAgICAgICAgIG5hbWVzX3ByZWZpeCA9ICJmaXREIikgJT4lCiAgbXV0YXRlKERheSA9IGZhY3RvcihUTVAsIGxldmVscyA9IGMoIjA1RDAzIiwgIjA2RDAzIiwgIjA3RDAzIiwgIjA4RDAzIiwgIjA5RDAzIiwgIjEwRDAzIiwgIjExRDAzIiksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCIwIiwgIjAuMDU4IiwgIjAuNSIsICIxLjAiLCAiMTAiLCAiNTAiLCAiMjAwIikpKQoKIyBDYWxjdWxhdGUgdGhlIGZpdG5lc3MgYXQgdGhlIGhpZ2hlc3QgVE1QIGNvbmNlbnRyYXRpb24gZm9yIGVhY2ggbXV0SUQKQmFjaWxsdXNfbWF4X2ZpdG5lc3MgPC0gQmFjaWxsdXNfbG9uZyAlPiUKICBmaWx0ZXIoVE1QID09ICIxMUQwMyIpICU+JQogIHNlbGVjdChtdXRJRCwgbWF4X2ZpdG5lc3MgPSBGaXRuZXNzKQoKIyBKb2luIHRoaXMgaW5mb3JtYXRpb24gYmFjayB0byB0aGUgb3JpZ2luYWwgZGF0YSBhbmQgY29sb3IgYWNjb3JkaW5nbHkKQmFjaWxsdXNfbG9uZyA8LSBCYWNpbGx1c19sb25nICU+JQogIGxlZnRfam9pbihCYWNpbGx1c19tYXhfZml0bmVzcywgYnkgPSAibXV0SUQiKSAlPiUKICBtdXRhdGUoQ29sb3IgPSBjYXNlX3doZW4oCiAgICBtdXRJRCA9PSAiTlBfODMxOTU3IiB+ICJXVCIsCiAgICBpcy5uYShtYXhfZml0bmVzcykgfiAiTXV0YW50X1Vua25vd24iLAogICAgbWF4X2ZpdG5lc3MgPCAtMSB+ICJNdXRhbnRfTG93IiwKICAgIG1heF9maXRuZXNzID49IC0xIH4gIk11dGFudF9IaWdoIgogICkpCgojIFJlb3JkZXIgdGhlIGxldmVscyBvZiBDb2xvciBmYWN0b3IgdG8gcGxvdCBXVCBsYXN0CkJhY2lsbHVzX2xvbmckQ29sb3IgPC0gZmFjdG9yKEJhY2lsbHVzX2xvbmckQ29sb3IsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIk11dGFudF9Vbmtub3duIiwgIk11dGFudF9Mb3ciLCAiTXV0YW50X0hpZ2giLCAiV1QiKSkKCiMgU2VwYXJhdGUgV1QgYW5kIG11dGFudCBkYXRhCldUX2RhdGEgPC0gQmFjaWxsdXNfbG9uZyAlPiUgZmlsdGVyKG11dElEID09ICJOUF84MzE5NTciKQptdXRhbnRfZGF0YSA8LSBCYWNpbGx1c19sb25nICU+JSBmaWx0ZXIobXV0SUQgIT0gIk5QXzgzMTk1NyIpCmBgYAoKRml0bmVzcyBwbG90dGluZyBvdmVyIHRoZSBUTVAgZ3JhZGllbnQKYGBge3J9CiMgRmlyc3QsIGludGVycG9sYXRlIE5BIHZhbHVlcwptdXRhbnRfZGF0YSA8LSBtdXRhbnRfZGF0YSAlPiUKICBncm91cF9ieShtdXRJRCkgJT4lCiAgYXJyYW5nZShEYXkpICU+JQogIG11dGF0ZShGaXRuZXNzID0gbmEuYXBwcm94KEZpdG5lc3MsIHggPSBEYXksIG5hLnJtID0gRkFMU0UpKSAlPiUKICB1bmdyb3VwKCkKCldUX2RhdGEgPC0gV1RfZGF0YSAlPiUKICBhcnJhbmdlKERheSkgJT4lCiAgbXV0YXRlKEZpdG5lc3MgPSBuYS5hcHByb3goRml0bmVzcywgeCA9IERheSwgbmEucm0gPSBGQUxTRSkpCgojIFRoZW4gY3JlYXRlIHRoZSBwbG90CkJhY2lsbHVzX3Bsb3QgPC0gZ2dwbG90KCkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC0xLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJncmF5NTAiKSArCiAgIyBQbG90IG11dGFudCBsaW5lcyBmaXJzdAogIGdlb21fbGluZShkYXRhID0gbXV0YW50X2RhdGEsIGFlcyh4ID0gRGF5LCB5ID0gRml0bmVzcywgZ3JvdXAgPSBtdXRJRCwgY29sb3IgPSBDb2xvciksIHNpemUgPSAxLjApICsKICBnZW9tX3BvaW50KGRhdGEgPSBtdXRhbnRfZGF0YSwgYWVzKHggPSBEYXksIHkgPSBGaXRuZXNzLCBjb2xvciA9IENvbG9yKSwgc2l6ZSA9IDMpICsKICAjIFBsb3QgV1QgbGluZSBvbiB0b3AKICBnZW9tX2xpbmUoZGF0YSA9IFdUX2RhdGEsIGFlcyh4ID0gRGF5LCB5ID0gRml0bmVzcywgZ3JvdXAgPSBtdXRJRCksIGNvbG9yID0gInJlZCIsIHNpemUgPSAyLjApICsKICBnZW9tX3BvaW50KGRhdGEgPSBXVF9kYXRhLCBhZXMoeCA9IERheSwgeSA9IEZpdG5lc3MpLCBjb2xvciA9ICJyZWQiLCBzaXplID0gMykgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJNdXRhbnRfTG93IiA9ICJkYXJrYmx1ZSIsICJNdXRhbnRfSGlnaCIgPSAiZ29sZCIsICJNdXRhbnRfVW5rbm93biIgPSAiZ3JheSIpKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSBjKCIwIiwgIjAuMDU4IiwgIjAuNSIsICIxLjAiLCAiMTAiLCAiNTAiLCAiMjAwIikpICsKICBsYWJzKHggPSAiVHJpbWV0aG9wcmltICh1Zy9tTCkiLAogICAgICAgeSA9ICJNZWRpYW4gRml0bmVzcyAoTG9nRkMpIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2JsYWNrJywgc2l6ZSA9IDEuMCksCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAxLjApLAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsgCiAgZ2d0aXRsZSgiQmFjaWxsdXMgY2VyZXVzIikgKyAKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gIml0YWxpYyIsIHNpemUgPSAxNikpCgojIFByaW50IHRoZSBwbG90CnByaW50KEJhY2lsbHVzX3Bsb3QpCmBgYAoKCmBgYHtyIGVjaG89RkFMU0V9CiMgUE5HCmdnc2F2ZShmaWxlPSJSZXNpc3RhbmNlL1BMT1RTL0ZpbmFsL0JhY2lsbHVzLmZpdG5lc3MucG5nIiwgCiAgICAgICBwbG90PUJhY2lsbHVzX3Bsb3QsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDQuNSwgaGVpZ2h0ID0gNC41LCB1bml0cyA9ICJpbiIpCmBgYAoKUGxvdCB0b2dldGhlcjoKYGBge3J9CnBhdGNoMiA8LSBCYWNpbGx1c19wbG90IHwgQmFjaWxsdXMuaGVhdG1hcApwYXRjaDIKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIFBORwpnZ3NhdmUoZmlsZT0iUmVzaXN0YW5jZS9QTE9UUy9GaW5hbC9CYWNpbGx1cy5Cb3RoLnBuZyIsIAogICAgICAgcGxvdD1wYXRjaDIsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDExLCBoZWlnaHQgPSA1LCB1bml0cyA9ICJpbiIpCmBgYAoKIyMjIEFuYWVyb2NvY2N1cyB0ZXRyYWRpdXMKCmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIEZpbHRlciB0byByZXRhaW4gRXNjaGVyaWNoaWEgY29saSAoTkNCSS5zcGVjaWVzKSBmcm9tIEwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkCkFuYWVyb2NvY2N1cyA8LSBMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZFtMMTVfaG9tb19tdXRfZ29vZF9maWx0ZXJlZCROQ0JJLnNwZWNpZXMgPT0gIkFuYWVyb2NvY2N1cyB0ZXRyYWRpdXMiICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIWlzLm5hKEwxNV9ob21vX211dF9nb29kX2ZpbHRlcmVkJE5DQkkuc3BlY2llcyksIF0KCiMgRmlsdGVyIHRvIG9ubHkgcmV0YWluIHNwZWNpZmljIHZhcmlhbnRzIGZvciB0aGUgTVNBCkFuYWVyb2NvY2N1c19maWx0ZXJlZCA8LSBBbmFlcm9jb2NjdXMgJT4lCiAgZmlsdGVyKChtdXRJRCA9PSAiV1BfMDA0ODM2NjY5IiB8IElEID09ICJXUF8wMDQ4MzY2NjkiKSAmICFpcy5uYShmaXREMTFEMDMpKQoKIyBDcmVhdGUgYSBmdW5jdGlvbiB0byB3cml0ZSBGQVNUQSBlbnRyaWVzCndyaXRlX2Zhc3RhIDwtIGZ1bmN0aW9uKGlkLCBzZXEsIGZpbGUpIHsKICBjYXQocGFzdGUwKCI+IiwgaWQsICJcbiIpLCBmaWxlID0gZmlsZSwgYXBwZW5kID0gVFJVRSkKICBjYXQocGFzdGUwKCJNIiwgc2VxLCAiXG4iKSwgZmlsZSA9IGZpbGUsIGFwcGVuZCA9IFRSVUUpICAjIEFkZCAiTSIgYXQgdGhlIGJlZ2lubmluZyBvZiB0aGUgc2VxdWVuY2UKfQoKIyBTcGVjaWZ5IHRoZSBvdXRwdXQgZmlsZSBuYW1lCkFuYWVyb2NvY2N1c19vdXRwdXRfZmlsZSA8LSAiUmVzaXN0YW5jZS9GQVNUQS9BbmFlcm9jb2NjdXMuZmFzdGEiCgojIFJlbW92ZSB0aGUgZmlsZSBpZiBpdCBhbHJlYWR5IGV4aXN0cwppZiAoZmlsZS5leGlzdHMoQW5hZXJvY29jY3VzX291dHB1dF9maWxlKSkgewogIGZpbGUucmVtb3ZlKEFuYWVyb2NvY2N1c19vdXRwdXRfZmlsZSkKfQoKIyBXcml0ZSB0aGUgRkFTVEEgZW50cmllcyB0byB0aGUgZmlsZQpmb3IgKGkgaW4gMTpucm93KEFuYWVyb2NvY2N1c19maWx0ZXJlZCkpIHsKICB3cml0ZV9mYXN0YShBbmFlcm9jb2NjdXNfZmlsdGVyZWQkbXV0SURbaV0sIEFuYWVyb2NvY2N1c19maWx0ZXJlZCRzZXFbaV0sIEFuYWVyb2NvY2N1c19vdXRwdXRfZmlsZSkKfQoKY2F0KCJGQVNUQSBmaWxlIGhhcyBiZWVuIGdlbmVyYXRlZDoiLCBBbmFlcm9jb2NjdXNfb3V0cHV0X2ZpbGUsICJcbiIpCmBgYAoKUGVyZm9ybSBhIG11bHRpcGxlIHNlcXVlbmNlIGFsaWdubWVudCBvbiB0aGUgRkFTVEEgZmlsZSB1c2luZyBDTFVTVEFMIE9tZWdhOgpgYGB7YmFzaH0KIyBNYXkgbmVlZCB0byBlbmFibGUgcGVybWlzc2lvbnMgdG8gcnVuIHRoZSBleGVjdXRhYmxlOgojY2htb2QgK3ggY2x1c3RhbG8KLi9TY3JpcHRzL2NsdXN0YWxvIC1pIFJlc2lzdGFuY2UvRkFTVEEvQW5hZXJvY29jY3VzLmZhc3RhIC1vIFJlc2lzdGFuY2UvRkFTVEEvQW5hZXJvY29jY3VzLmFsaWduZWQuZmFzdGEgLS1vdXRmbXQ9ZmEgLS1mb3JjZQpgYGAKCkltcG9ydCB0aGUgTVNBIGZpbGUgYW5kIGRldGVybWluZSB3aGljaCBhbWlubyBhY2lkIHBvc2l0aW9ucyBkaWZmZXIgYmV0d2VlbiB0aGUgc2FtcGxlcwpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBJbXBvcnQgdGhlIE1TQSBmaWxlCm1zYSA8LSByZWFkQUFNdWx0aXBsZUFsaWdubWVudCgiUmVzaXN0YW5jZS9GQVNUQS9BbmFlcm9jb2NjdXMuYWxpZ25lZC5mYXN0YSIsIGZvcm1hdD0iZmFzdGEiKQoKIyBDb252ZXJ0IHRvIGEgbWF0cml4IGZvciBlYXNpZXIgbWFuaXB1bGF0aW9uCm1zYV9tYXRyaXggPC0gYXMubWF0cml4KG1zYSkKCiMgU2V0IHRoZSByZWZlcmVuY2Ugc2VxdWVuY2UKcmVmX3NlcSA8LSAiV1BfMDA0ODM2NjY5IgoKY2F0KCJVc2luZyByZWZlcmVuY2Ugc2VxdWVuY2U6IiwgcmVmX3NlcSwgIlxuIikKCiMgRmluZCBwb3NpdGlvbnMgd2hlcmUgYW1pbm8gYWNpZHMgZGlmZmVyCmRpZmZfcG9zaXRpb25zIDwtIHdoaWNoKGFwcGx5KG1zYV9tYXRyaXgsIDIsIGZ1bmN0aW9uKGNvbCkgbGVuZ3RoKHVuaXF1ZShjb2wpKSA+IDEpKQoKIyBDcmVhdGUgYSBzdW1tYXJ5IGRhdGFmcmFtZQpzdW1tYXJ5X2RmIDwtIGRhdGEuZnJhbWUoCiAgUG9zaXRpb24gPSBkaWZmX3Bvc2l0aW9ucywKICBSZWZBQSA9IG1zYV9tYXRyaXhbcmVmX3NlcSwgZGlmZl9wb3NpdGlvbnNdLAogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQopCgojIEFkZCBjb2x1bW5zIGZvciBlYWNoIHVuaXF1ZSBtdXRJRAp1bmlxdWVfbXV0SURzIDwtIHNldGRpZmYocm93bmFtZXMobXNhX21hdHJpeCksIHJlZl9zZXEpCmZvciAobXV0SUQgaW4gdW5pcXVlX211dElEcykgewogIHN1bW1hcnlfZGZbW211dElEXV0gPC0gbXNhX21hdHJpeFttdXRJRCwgZGlmZl9wb3NpdGlvbnNdCn0KCiMgQWRkIGEgY29sdW1uIGZvciB0aGUgYW1pbm8gYWNpZCBjaGFuZ2VzCnN1bW1hcnlfZGYkQ2hhbmdlcyA8LSBhcHBseShzdW1tYXJ5X2RmLCAxLCBmdW5jdGlvbihyb3cpIHsKICBjaGFuZ2VzIDwtIHNldGRpZmYoYXMuY2hhcmFjdGVyKHJvd1szOm5jb2woc3VtbWFyeV9kZildKSwgcm93WyJSZWZBQSJdKQogIGlmIChsZW5ndGgoY2hhbmdlcykgPiAwKSB7CiAgICBwYXN0ZShyb3dbIlJlZkFBIl0sIHBhc3RlKGNoYW5nZXMsIGNvbGxhcHNlID0gIi8iKSwgc2VwID0gIi0+IikKICB9IGVsc2UgewogICAgIk5vIGNoYW5nZSIKICB9Cn0pCgojIFJlbW92ZSByb3dzIHdpdGggIk5vIGNoYW5nZSIKc3VtbWFyeV9kZiA8LSBzdW1tYXJ5X2RmW3N1bW1hcnlfZGYkQ2hhbmdlcyAhPSAiTm8gY2hhbmdlIiwgXQoKIyBSZW9yZGVyIGNvbHVtbnMKc3VtbWFyeV9kZiA8LSBzdW1tYXJ5X2RmICU+JSAKICBzZWxlY3QoUG9zaXRpb24sIFJlZkFBLCBDaGFuZ2VzLCBldmVyeXRoaW5nKCkpCgojIFByaW50IHRoZSBzdW1tYXJ5IHRhYmxlCnByaW50KHN1bW1hcnlfZGYsIHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKCkFycmFuZ2UgdGhlIGRhdGEgZm9yIHRoZSBhbWlubyBhY2lkIG11dGF0aW9ucyBoZWF0bWFwOgpgYGB7cn0KIyBHZXQgYWxsIHBvc2l0aW9ucyB3aGVyZSB0aGVyZSdzIGEgbXV0YXRpb24KYWxsX3Bvc2l0aW9ucyA8LSBzb3J0KHVuaXF1ZShzdW1tYXJ5X2RmJFBvc2l0aW9uKSkKCiMgUmVzaGFwZSB0aGUgZGF0YQpoZWF0bWFwX2RhdGEgPC0gc3VtbWFyeV9kZiAlPiUKICBzZWxlY3QoLUNoYW5nZXMpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gLWMoUG9zaXRpb24sIFJlZkFBKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIm11dElEIiwgCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJBQSIpICU+JQogIG11dGF0ZShQb3NpdGlvbiA9IGFzLm51bWVyaWMoUG9zaXRpb24pKQoKIyBBZGQgdGhlIHJlZmVyZW5jZSBzZXF1ZW5jZQpyZWZfZGF0YSA8LSBzdW1tYXJ5X2RmICU+JQogIHNlbGVjdChQb3NpdGlvbiwgUmVmQUEpICU+JQogIG11dGF0ZShtdXRJRCA9ICJXUF8wMDQ4MzY2NjkiLCBBQSA9IFJlZkFBKQoKaGVhdG1hcF9kYXRhIDwtIGJpbmRfcm93cyhyZWZfZGF0YSwgaGVhdG1hcF9kYXRhKQoKIyBDcmVhdGUgYSBmdW5jdGlvbiB0byBjb21wYXJlIGFtaW5vIGFjaWRzCmNvbXBhcmVfYWEgPC0gZnVuY3Rpb24oYWEsIHJlZl9hYSkgewogIGlmZWxzZShpcy5uYShhYSkgfCBhYSA9PSAiIiwgIk5vIGRhdGEiLAogICAgICAgICBpZmVsc2UoYWEgPT0gcmVmX2FhLCAiTm8gY2hhbmdlIiwgIk11dGF0aW9uIikpCn0KCiMgQXBwbHkgdGhlIGNvbXBhcmlzb24gZnVuY3Rpb24gYW5kIG1vZGlmeSBBQSBkaXNwbGF5CmhlYXRtYXBfZGF0YSA8LSBoZWF0bWFwX2RhdGEgJT4lCiAgZ3JvdXBfYnkoUG9zaXRpb24pICU+JQogIG11dGF0ZSgKICAgIENvbXBhcmlzb24gPSBjb21wYXJlX2FhKEFBLCBBQVttdXRJRCA9PSAiV1BfMDA0ODM2NjY5Il0pLAogICAgRGlzcGxheUFBID0gY2FzZV93aGVuKAogICAgICBtdXRJRCA9PSAiV1BfMDA0ODM2NjY5IiB+IEFBLAogICAgICBDb21wYXJpc29uID09ICJNdXRhdGlvbiIgfiBBQSwKICAgICAgVFJVRSB+ICIiCiAgICApCiAgKSAlPiUKICB1bmdyb3VwKCkKCiMgQ3JlYXRlIGEgY29udGludW91cyBwb3NpdGlvbiB2YXJpYWJsZQpoZWF0bWFwX2RhdGEgPC0gaGVhdG1hcF9kYXRhICU+JQogIGFycmFuZ2UoUG9zaXRpb24pICU+JQogIG11dGF0ZShQb3NpdGlvbkluZGV4ID0gYXMubnVtZXJpYyhmYWN0b3IoUG9zaXRpb24sIGxldmVscyA9IHVuaXF1ZShQb3NpdGlvbikpKSkKCiMgQ3JlYXRlIEFuYWVyb2NvY2N1c19sb25nCkFuYWVyb2NvY2N1c19sb25nIDwtIEFuYWVyb2NvY2N1c19maWx0ZXJlZCAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0c193aXRoKCJmaXREIiksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJUTVAiLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIkZpdG5lc3MiLAogICAgICAgICAgICAgICBuYW1lc19wcmVmaXggPSAiZml0RCIpCgojIENhbGN1bGF0ZSB0aGUgZml0bmVzcyBhdCB0aGUgaGlnaGVzdCBUTVAgY29uY2VudHJhdGlvbiBmb3IgZWFjaCBtdXRJRAptYXhfZml0bmVzcyA8LSBBbmFlcm9jb2NjdXNfbG9uZyAlPiUKICBmaWx0ZXIoVE1QID09ICIxMUQwMyIpICU+JQogIHNlbGVjdChtdXRJRCwgbWF4X2ZpdG5lc3MgPSBGaXRuZXNzKQoKIyBKb2luIG1heF9maXRuZXNzIGluZm9ybWF0aW9uIGFuZCBjcmVhdGUgTXV0YW50Q29sb3IKaGVhdG1hcF9kYXRhIDwtIGhlYXRtYXBfZGF0YSAlPiUKICBsZWZ0X2pvaW4obWF4X2ZpdG5lc3MsIGJ5ID0gIm11dElEIikgJT4lCiAgbXV0YXRlKE11dGFudENvbG9yID0gY2FzZV93aGVuKAogICAgbXV0SUQgPT0gIldQXzAwNDgzNjY2OSIgfiAicmVkIiwKICAgIGlzLm5hKG1heF9maXRuZXNzKSB+ICJncmF5IiwKICAgIG1heF9maXRuZXNzIDwgLTEgfiAiZGFya2JsdWUiLAogICAgbWF4X2ZpdG5lc3MgPj0gLTEgfiAiZ29sZDIiCiAgKSkKCiMgQ3JlYXRlIGEgbmV3IGNvbHVtbiBmb3Igb3JkZXJpbmcKaGVhdG1hcF9kYXRhIDwtIGhlYXRtYXBfZGF0YSAlPiUKICBtdXRhdGUoT3JkZXJHcm91cCA9IGNhc2Vfd2hlbigKICAgIG11dElEID09ICJXUF8wMDQ4MzY2NjkiIH4gMSwKICAgIG1heF9maXRuZXNzID49IC0xIH4gMiwKICAgIG1heF9maXRuZXNzIDwgLTEgfiAzLAogICAgVFJVRSB+IDQgICMgZm9yIGFueSBOQSBvciBvdGhlciBjYXNlcwogICkpCmBgYAoKSGVhdG1hcCBmb3IgYW1pbm8gYWNpZCBtdXRhdGlvbnMgYnkgcG9zaXRpb24gYmFzZWQgb24gcmVmZXJlbmNlIHNlcXVlbmNlOgpgYGB7cn0KIyBPcmRlciB0aGUgZGF0YSB3aXRoIHJlZmVyZW5jZSBvbiB0b3AKaGVhdG1hcF9kYXRhIDwtIGhlYXRtYXBfZGF0YSAlPiUKICBhcnJhbmdlKE9yZGVyR3JvdXAsIGRlc2MobWF4X2ZpdG5lc3MpKSAlPiUKICBtdXRhdGUobXV0SUQgPSBmYWN0b3IobXV0SUQsIGxldmVscyA9IHVuaXF1ZShtdXRJRCkpKQoKIyBFbnN1cmUgdGhlIGNvbG9ycyBhcmUgYXNzaWduZWQgY29ycmVjdGx5CnlfY29sb3JzIDwtIGhlYXRtYXBfZGF0YSAlPiUKICBkaXN0aW5jdChtdXRJRCwgTXV0YW50Q29sb3IpICU+JQogIGFycmFuZ2UobWF0Y2gobXV0SUQsIGxldmVscyhoZWF0bWFwX2RhdGEkbXV0SUQpKSkKCiMgTW9kaWZ5IHRoZSBoZWF0bWFwX2RhdGEgdG8gaW5jbHVkZSB0aGUgY29sb3IgaW5mb3JtYXRpb24gZm9yIG11dGF0aW9ucyBhbmQgdGV4dApoZWF0bWFwX2RhdGEgPC0gaGVhdG1hcF9kYXRhICU+JQogIG11dGF0ZSgKICAgIE11dGF0aW9uQ29sb3IgPSBjYXNlX3doZW4oCiAgICAgIENvbXBhcmlzb24gPT0gIk11dGF0aW9uIiAmIG1heF9maXRuZXNzID49IC0xIH4gImdvbGQyIiwKICAgICAgQ29tcGFyaXNvbiA9PSAiTXV0YXRpb24iICYgbWF4X2ZpdG5lc3MgPCAtMSB+ICJkYXJrYmx1ZSIsCiAgICAgIFRSVUUgfiBDb21wYXJpc29uCiAgICApLAogICAgVGV4dENvbG9yID0gY2FzZV93aGVuKAogICAgICBNdXRhdGlvbkNvbG9yID09ICJkYXJrYmx1ZSIgfiAid2hpdGUiLAogICAgICBUUlVFIH4gImJsYWNrIgogICAgKQogICkKCiMgQ3JlYXRlIHRoZSBoZWF0bWFwCkFuYWVyb2NvY2N1cy5oZWF0bWFwIDwtIGdncGxvdChoZWF0bWFwX2RhdGEsIGFlcyh4ID0gUG9zaXRpb25JbmRleCwgeSA9IG11dElELCBmaWxsID0gTXV0YXRpb25Db2xvcikpICsKICBnZW9tX3RpbGUoY29sb3IgPSAid2hpdGUiLCB3aWR0aCA9IDEsIGhlaWdodCA9IDAuOSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIk5vIGNoYW5nZSIgPSAiZ3JheTkwIiwgImdvbGQyIiA9ICJnb2xkMiIsICJkYXJrYmx1ZSIgPSAiZGFya2JsdWUiLCAiTm8gZGF0YSIgPSAid2hpdGUiKSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEsIHZqdXN0ID0gMC41LCBzaXplID0gMTIpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9saW5lKGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDAuNSksCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAid2hpdGUiLCBzaXplID0gMC41KSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIKICApICsKICBsYWJzKHggPSAiUG9zaXRpb24iLCBmaWxsID0gIkFtaW5vIEFjaWQgQ2hhbmdlIikgKwogIHNjYWxlX3hfY29udGludW91cygKICAgIGJyZWFrcyA9IGhlYXRtYXBfZGF0YSRQb3NpdGlvbkluZGV4LAogICAgbGFiZWxzID0gaGVhdG1hcF9kYXRhJFBvc2l0aW9uLAogICAgZXhwYW5kID0gYygwLCAwKQogICkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBEaXNwbGF5QUEsIGNvbG9yID0gVGV4dENvbG9yKSwgc2l6ZSA9IDUsIG5hLnJtID0gVFJVRSwgZm9udGZhY2UgPSAiYm9sZCIpICsKICBzY2FsZV9jb2xvcl9pZGVudGl0eSgpICsKICBzY2FsZV95X2Rpc2NyZXRlKGxpbWl0cyA9IHJldihsZXZlbHMoaGVhdG1hcF9kYXRhJG11dElEKSksIGV4cGFuZCA9IGMoMCwgMCkpCgojIEFwcGx5IGNvcnJlY3QgY29sb3JzIHRvIHktYXhpcyBsYWJlbHMKQW5hZXJvY29jY3VzLmhlYXRtYXAgPC0gQW5hZXJvY29jY3VzLmhlYXRtYXAgKwogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gcmV2KHlfY29sb3JzJE11dGFudENvbG9yKSkpICsgCiAgZ2d0aXRsZSgiQW5hZXJvY29jY3VzIHRldHJhZGl1cyIpICsgCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJpdGFsaWMiLCBzaXplID0gMTYpKQoKcHJpbnQoQW5hZXJvY29jY3VzLmhlYXRtYXApCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KIyBQTkcKZ2dzYXZlKGZpbGU9IlJlc2lzdGFuY2UvUExPVFMvRmluYWwvQW5hZXJvY29jY3VzLmhlYXRtYXAucG5nIiwgCiAgICAgICBwbG90PUFuYWVyb2NvY2N1cy5oZWF0bWFwLAogICAgICAgZHBpPTYwMCwgd2lkdGggPSA3LjUsIGhlaWdodCA9IDQuNSwgdW5pdHMgPSAiaW4iKQpgYGAKClBsb3QgdGhlIGNoYW5nZSBpbiBmaXRuZXNzIGFjcm9zcyB0aGUgVE1QIGdyYWRpZW50IGZvciBib3RoIHNhbXBsZXM6CmBgYHtyIHdhcm5pbmc9RkFMU0V9CiMgUmVzaGFwZSB0aGUgZGF0YSBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQKQW5hZXJvY29jY3VzX2xvbmcgPC0gQW5hZXJvY29jY3VzX2ZpbHRlcmVkICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoImZpdEQiKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIlRNUCIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiRml0bmVzcyIsCiAgICAgICAgICAgICAgIG5hbWVzX3ByZWZpeCA9ICJmaXREIikgJT4lCiAgbXV0YXRlKERheSA9IGZhY3RvcihUTVAsIGxldmVscyA9IGMoIjA1RDAzIiwgIjA2RDAzIiwgIjA3RDAzIiwgIjA4RDAzIiwgIjA5RDAzIiwgIjEwRDAzIiwgIjExRDAzIiksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCIwIiwgIjAuMDU4IiwgIjAuNSIsICIxLjAiLCAiMTAiLCAiNTAiLCAiMjAwIikpKQoKIyBDYWxjdWxhdGUgdGhlIGZpdG5lc3MgYXQgdGhlIGhpZ2hlc3QgVE1QIGNvbmNlbnRyYXRpb24gZm9yIGVhY2ggbXV0SUQKQW5hZXJvY29jY3VzX21heF9maXRuZXNzIDwtIEFuYWVyb2NvY2N1c19sb25nICU+JQogIGZpbHRlcihUTVAgPT0gIjExRDAzIikgJT4lCiAgc2VsZWN0KG11dElELCBtYXhfZml0bmVzcyA9IEZpdG5lc3MpCgojIEpvaW4gdGhpcyBpbmZvcm1hdGlvbiBiYWNrIHRvIHRoZSBvcmlnaW5hbCBkYXRhIGFuZCBjb2xvciBhY2NvcmRpbmdseQpBbmFlcm9jb2NjdXNfbG9uZyA8LSBBbmFlcm9jb2NjdXNfbG9uZyAlPiUKICBsZWZ0X2pvaW4oQW5hZXJvY29jY3VzX21heF9maXRuZXNzLCBieSA9ICJtdXRJRCIpICU+JQogIG11dGF0ZShDb2xvciA9IGNhc2Vfd2hlbigKICAgIG11dElEID09ICJXUF8wMDQ4MzY2NjkiIH4gIldUIiwKICAgIGlzLm5hKG1heF9maXRuZXNzKSB+ICJNdXRhbnRfVW5rbm93biIsCiAgICBtYXhfZml0bmVzcyA8IC0xIH4gIk11dGFudF9Mb3ciLAogICAgbWF4X2ZpdG5lc3MgPj0gLTEgfiAiTXV0YW50X0hpZ2giCiAgKSkKCiMgUmVvcmRlciB0aGUgbGV2ZWxzIG9mIENvbG9yIGZhY3RvciB0byBwbG90IFdUIGxhc3QKQW5hZXJvY29jY3VzX2xvbmckQ29sb3IgPC0gZmFjdG9yKEFuYWVyb2NvY2N1c19sb25nJENvbG9yLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJNdXRhbnRfVW5rbm93biIsICJNdXRhbnRfTG93IiwgIk11dGFudF9IaWdoIiwgIldUIikpCgojIFNlcGFyYXRlIFdUIGFuZCBtdXRhbnQgZGF0YQpXVF9kYXRhIDwtIEFuYWVyb2NvY2N1c19sb25nICU+JSBmaWx0ZXIobXV0SUQgPT0gIldQXzAwNDgzNjY2OSIpCm11dGFudF9kYXRhIDwtIEFuYWVyb2NvY2N1c19sb25nICU+JSBmaWx0ZXIobXV0SUQgIT0gIldQXzAwNDgzNjY2OSIpCmBgYAoKRml0bmVzcyBwbG90dGluZyBvdmVyIHRoZSBUTVAgZ3JhZGllbnQKYGBge3J9CiMgQ3JlYXRlIHRoZSBwbG90CkFuYWVyb2NvY2N1c19wbG90IDwtIGdncGxvdCgpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtMSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZ3JheTUwIikgKwogICMgUGxvdCBtdXRhbnQgbGluZXMgZmlyc3QKICBnZW9tX2xpbmUoZGF0YSA9IG11dGFudF9kYXRhLCBhZXMoeCA9IERheSwgeSA9IEZpdG5lc3MsIGdyb3VwID0gbXV0SUQsIGNvbG9yID0gQ29sb3IpLCBzaXplID0gMS4wKSArCiAgZ2VvbV9wb2ludChkYXRhID0gbXV0YW50X2RhdGEsIGFlcyh4ID0gRGF5LCB5ID0gRml0bmVzcywgY29sb3IgPSBDb2xvciksIHNpemUgPSAzKSArCiAgIyBQbG90IFdUIGxpbmUgb24gdG9wCiAgZ2VvbV9saW5lKGRhdGEgPSBXVF9kYXRhLCBhZXMoeCA9IERheSwgeSA9IEZpdG5lc3MsIGdyb3VwID0gbXV0SUQpLCBjb2xvciA9ICJyZWQiLCBzaXplID0gMi4wKSArCiAgZ2VvbV9wb2ludChkYXRhID0gV1RfZGF0YSwgYWVzKHggPSBEYXksIHkgPSBGaXRuZXNzKSwgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDMpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiTXV0YW50X0xvdyIgPSAiZGFya2JsdWUiLCAiTXV0YW50X0hpZ2giID0gImdvbGQiLCAiTXV0YW50X1Vua25vd24iID0gImdyYXkiKSkgKwogIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gYygiMCIsICIwLjA1OCIsICIwLjUiLCAiMS4wIiwgIjEwIiwgIjUwIiwgIjIwMCIpKSArCiAgbGFicyh4ID0gIlRyaW1ldGhvcHJpbSAodWcvbUwpIiwKICAgICAgIHkgPSAiTWVkaWFuIEZpdG5lc3MgKExvZ0ZDKSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKAogICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICdibGFjaycsIHNpemUgPSAxLjApLAogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMS4wKSwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArIAogIGdndGl0bGUoIkFuYWVyb2NvY2N1cyB0ZXRyYWRpdXMiKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiaXRhbGljIiwgc2l6ZSA9IDE2KSkgIyBDZW50ZXIgYW5kIGl0YWxpY2l6ZSB0aGUgdGl0bGUKCiMgUHJpbnQgdGhlIHBsb3QKcHJpbnQoQW5hZXJvY29jY3VzX3Bsb3QpCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KIyBQTkcKZ2dzYXZlKGZpbGU9IlJlc2lzdGFuY2UvUExPVFMvRmluYWwvQW5hZXJvY29jY3VzLmZpdG5lc3MucG5nIiwgCiAgICAgICBwbG90PUFuYWVyb2NvY2N1c19wbG90LAogICAgICAgZHBpPTYwMCwgd2lkdGggPSA0LjUsIGhlaWdodCA9IDQuNSwgdW5pdHMgPSAiaW4iKQpgYGAKClBsb3QgdG9nZXRoZXI6CmBgYHtyIHdhcm5pbmc9RkFMU0V9CnBhdGNoMyA8LSBBbmFlcm9jb2NjdXNfcGxvdCB8IEFuYWVyb2NvY2N1cy5oZWF0bWFwCnBhdGNoMwpgYGAKCmBgYHtyIGVjaG89RkFMU0V9CiMgUE5HCmdnc2F2ZShmaWxlPSJSZXNpc3RhbmNlL1BMT1RTL0ZpbmFsL0FuYWVyb2NvY2N1cy5Cb3RoLnBuZyIsIAogICAgICAgcGxvdD1wYXRjaDMsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDExLCBoZWlnaHQgPSA1LCB1bml0cyA9ICJpbiIpCmBgYAoKRnVsbCBwbG90IHdpdGggQmFjaWxsdXMgYW5kIFN0cmVwdG9jb2NjdXMgdGF4YToKYGBge3Igd2FybmluZz1GQUxTRX0KcGF0Y2g3IDwtIChCYWNpbGx1c19wbG90IHwgQmFjaWxsdXMuaGVhdG1hcCkgLyAKICAgICAgICAgIChTdHJlcHRvY29jY3VzX3Bsb3QgfCBTdHJlcHRvY29jY3VzLmhlYXRtYXApICsKICBwbG90X2Fubm90YXRpb24odGFnX2xldmVscyA9ICdBJykgJgogIHRoZW1lKHBsb3QudGFnID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCwgZmFjZSA9ICJib2xkIikpCnBhdGNoNwpgYGAKCmBgYHtyIGVjaG89RkFMU0V9CiMgUE5HCmdnc2F2ZShmaWxlPSJSZXNpc3RhbmNlL1BMT1RTL0ZpbmFsL1R3by5UYXhhLkFsbC5wbmciLCAKICAgICAgIHBsb3Q9cGF0Y2g3LAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTAsIHVuaXRzID0gImluIikKYGBgCgpGdWxsIHBsb3Qgd2l0aCBhbGwgdGhyZWUgdGF4YToKYGBge3Igd2FybmluZz1GQUxTRX0KcGF0Y2g0IDwtIChTdHJlcHRvY29jY3VzX3Bsb3QgfCBTdHJlcHRvY29jY3VzLmhlYXRtYXApIC8gCiAgICAgICAgICAoQmFjaWxsdXNfcGxvdCB8IEJhY2lsbHVzLmhlYXRtYXApIC8gCiAgICAgICAgICAoQW5hZXJvY29jY3VzX3Bsb3QgfCBBbmFlcm9jb2NjdXMuaGVhdG1hcCkgKwogIHBsb3RfYW5ub3RhdGlvbih0YWdfbGV2ZWxzID0gJ0EnKSAmCiAgdGhlbWUocGxvdC50YWcgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4LCBmYWNlID0gImJvbGQiKSkKcGF0Y2g0CmBgYAoKYGBge3IgZWNobz1GQUxTRX0KIyBQTkcKZ2dzYXZlKGZpbGU9IlJlc2lzdGFuY2UvUExPVFMvRmluYWwvVGhyZWUuVGF4YS5BbGwucG5nIiwgCiAgICAgICBwbG90PXBhdGNoNCwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gMTEsIGhlaWdodCA9IDEyLCB1bml0cyA9ICJpbiIpCmBgYAoKRnVsbCBwbG90IHdpdGggRk9VUiB0YXhhOgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpwYXRjaDUgPC0gKHBhdGhvZ2VuaWNfcGxvdCB8IEVzY2hlcmljaGlhX3Bsb3QpIC8KICAgICAgICAgIChCYWNpbGx1c19wbG90IHwgQmFjaWxsdXMuaGVhdG1hcCkgLwogICAgICAgICAgKFN0cmVwdG9jb2NjdXNfcGxvdCB8IFN0cmVwdG9jb2NjdXMuaGVhdG1hcCkgKwogIHBsb3RfbGF5b3V0KGhlaWdodHMgPSBjKDMsIDMsIDQpKSArCiAgcGxvdF9hbm5vdGF0aW9uKHRhZ19sZXZlbHMgPSAnQScpCgpwYXRjaDUgPC0gcGF0Y2g1ICYgCiAgdGhlbWUocGxvdC50YWcgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDI0LCBmYWNlID0gImJvbGQiKSkKCnBhdGNoNQpgYGAKCmBgYHtyIGVjaG89RkFMU0V9CiMgUE5HCmdnc2F2ZShmaWxlPSJSZXNpc3RhbmNlL1BMT1RTL0ZpbmFsL0ZvdXIuVGF4YS5BbGwucG5nIiwgCiAgICAgICBwbG90PXBhdGNoNSwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gMTAsIGhlaWdodCA9IDEyLCB1bml0cyA9ICJpbiIpCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KIyBQTkcKZ2dzYXZlKGZpbGU9IlJlc2lzdGFuY2UvUExPVFMvRmluYWwvRm91ci5UYXhhLkFsbC52Mi5wbmciLCAKICAgICAgIHBsb3Q9cGF0Y2g1LAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxNSwgaGVpZ2h0ID0gMTQsIHVuaXRzID0gImluIikKYGBgCgpGdWxsIHBsb3Qgd2l0aCBhbGwgZml2ZSB0YXhhOgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpwYXRjaDYgPC0gKHBhdGhvZ2VuaWNfcGxvdCB8IEVzY2hlcmljaGlhX3Bsb3QpIC8KICAgICAgICAgIChCYWNpbGx1c19wbG90IHwgQmFjaWxsdXMuaGVhdG1hcCkgLyAKICAgICAgICAgIChBbmFlcm9jb2NjdXNfcGxvdCB8IEFuYWVyb2NvY2N1cy5oZWF0bWFwKSAvCiAgICAgICAgICAoU3RyZXB0b2NvY2N1c19wbG90IHwgU3RyZXB0b2NvY2N1cy5oZWF0bWFwKSArIAogIHBsb3RfbGF5b3V0KGhlaWdodHMgPSBjKDMsIDQsIDQsIDQpKSArCiAgcGxvdF9hbm5vdGF0aW9uKHRhZ19sZXZlbHMgPSAnQScpCgpwYXRjaDYgPC0gcGF0Y2g2ICYgCiAgdGhlbWUocGxvdC50YWcgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDI0LCBmYWNlID0gImJvbGQiKSkKCnBhdGNoNgpgYGAKCmBgYHtyIGVjaG89RkFMU0V9CiMgUE5HCmdnc2F2ZShmaWxlPSJSZXNpc3RhbmNlL1BMT1RTL0ZpbmFsL0ZpdmUuVGF4YS5BbGwucG5nIiwgCiAgICAgICBwbG90PXBhdGNoNiwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gMTAsIGhlaWdodCA9IDE1LCB1bml0cyA9ICJpbiIpCmBgYAoKIyMgRGlhbC1vdXQgVmFsaWRhdGlvbgoKVHdvIG9mIHRoZSBwYXRob2dlbmljIERIRlIgdmFyaWFudHMgd2VyZSBhbHNvIGluY2x1ZGVkIGluIG91ciBEaWFsLW91dCBQQ1IgdmVyaWZpY2F0aW9uIGFuYWx5c2lzIHRvIG1lYXN1cmUgZ3Jvd3RoIHJhdGVzIGluZGVwZW5kZW50IG9mIHRoZSBtdWx0aXBsZXhlZCBmaXRuZXNzIGFzc2F5LiAgVGhlc2UgaW5jbHVkZSBCYWNpbGx1cyBjZXJldXMgYW5kIFN0cmVwdG9jb2NjdXMgcG5ldW1vbmlhZS4KCiMjIyBCYWNpbGx1cyBjZXJldXMKYGBge3J9CiMgRmlsdGVyIHRvIG9ubHkgcmV0YWluIHNwZWNpZmljIHZhcmlhbnRzIGZvciB0aGUgTVNBCkJhY2lsbHVzX2RpYWxvdXQgPC0gQmFjaWxsdXMgJT4lCiAgZmlsdGVyKCgobXV0SUQgPT0gIk5QXzgzMTk1NyIpIHwgCiAgICAgICAgICAobXV0SUQgPT0gIldQXzAwMDYzNzIwOSIpKSAmIAogICAgICAgICAhaXMubmEoZml0RDExRDAzKSkKYGBgCgpBbGlnbiB0aGUgdHdvIHZhcmlhbnRzIGFuZCByZXRhaW4gTlBfODMxOTU3IGFzIHRoZSByZWZlcmVuY2Ugc2VxdWVuY2UKYGBge3J9CiMgQ3JlYXRlIGEgZnVuY3Rpb24gdG8gd3JpdGUgRkFTVEEgZW50cmllcwp3cml0ZV9mYXN0YSA8LSBmdW5jdGlvbihpZCwgc2VxLCBmaWxlKSB7CiAgY2F0KHBhc3RlMCgiPiIsIGlkLCAiXG4iKSwgZmlsZSA9IGZpbGUsIGFwcGVuZCA9IFRSVUUpCiAgY2F0KHBhc3RlMCgiTSIsIHNlcSwgIlxuIiksIGZpbGUgPSBmaWxlLCBhcHBlbmQgPSBUUlVFKSAgIyBBZGQgIk0iIGF0IHRoZSBiZWdpbm5pbmcgb2YgdGhlIHNlcXVlbmNlCn0KCiMgU3BlY2lmeSB0aGUgb3V0cHV0IGZpbGUgbmFtZQpCYWNpbGx1c19kaWFsb3V0X291dHB1dF9maWxlIDwtICJSZXNpc3RhbmNlL0ZBU1RBL0JhY2lsbHVzLmRpYWxvdXQuZmFzdGEiCgojIFJlbW92ZSB0aGUgZmlsZSBpZiBpdCBhbHJlYWR5IGV4aXN0cwppZiAoZmlsZS5leGlzdHMoQmFjaWxsdXNfZGlhbG91dF9vdXRwdXRfZmlsZSkpIHsKICBmaWxlLnJlbW92ZShCYWNpbGx1c19kaWFsb3V0X291dHB1dF9maWxlKQp9CgojIFdyaXRlIHRoZSBGQVNUQSBlbnRyaWVzIHRvIHRoZSBmaWxlCmZvciAoaSBpbiAxOm5yb3coQmFjaWxsdXNfZGlhbG91dCkpIHsKICB3cml0ZV9mYXN0YShCYWNpbGx1c19kaWFsb3V0JG11dElEW2ldLCBCYWNpbGx1c19kaWFsb3V0JHNlcVtpXSwgQmFjaWxsdXNfZGlhbG91dF9vdXRwdXRfZmlsZSkKfQoKY2F0KCJGQVNUQSBmaWxlIGhhcyBiZWVuIGdlbmVyYXRlZDoiLCBCYWNpbGx1c19kaWFsb3V0X291dHB1dF9maWxlLCAiXG4iKQpgYGAKClBlcmZvcm0gYSBtdWx0aXBsZSBzZXF1ZW5jZSBhbGlnbm1lbnQgb24gdGhlIEZBU1RBIGZpbGUgdXNpbmcgQ0xVU1RBTCBPbWVnYToKYGBge2Jhc2h9CiMgTWF5IG5lZWQgdG8gZW5hYmxlIHBlcm1pc3Npb25zIHRvIHJ1biB0aGUgZXhlY3V0YWJsZToKI2NobW9kICt4IGNsdXN0YWxvCi4vU2NyaXB0cy9jbHVzdGFsbyAtaSBSZXNpc3RhbmNlL0ZBU1RBL0JhY2lsbHVzLmRpYWxvdXQuZmFzdGEgLW8gUmVzaXN0YW5jZS9GQVNUQS9CYWNpbGx1cy5kaWFsb3V0LmFsaWduZWQuZmFzdGEgLS1vdXRmbXQ9ZmEgLS1mb3JjZQpgYGAKCkltcG9ydCB0aGUgTVNBIGZpbGUgYW5kIGRldGVybWluZSB3aGljaCBhbWlubyBhY2lkIHBvc2l0aW9ucyBkaWZmZXIgYmV0d2VlbiB0aGUgc2FtcGxlcwpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBJbXBvcnQgdGhlIE1TQSBmaWxlCm1zYSA8LSByZWFkQUFNdWx0aXBsZUFsaWdubWVudCgiUmVzaXN0YW5jZS9GQVNUQS9CYWNpbGx1cy5kaWFsb3V0LmFsaWduZWQuZmFzdGEiLCBmb3JtYXQ9ImZhc3RhIikKCiMgQ29udmVydCB0byBhIG1hdHJpeCBmb3IgZWFzaWVyIG1hbmlwdWxhdGlvbgptc2FfbWF0cml4IDwtIGFzLm1hdHJpeChtc2EpCgojIFNldCB0aGUgcmVmZXJlbmNlIHNlcXVlbmNlCnJlZl9zZXEgPC0gIk5QXzgzMTk1NyIKCmNhdCgiVXNpbmcgcmVmZXJlbmNlIHNlcXVlbmNlOiIsIHJlZl9zZXEsICJcbiIpCgojIEZpbmQgcG9zaXRpb25zIHdoZXJlIGFtaW5vIGFjaWRzIGRpZmZlcgpkaWZmX3Bvc2l0aW9ucyA8LSB3aGljaChhcHBseShtc2FfbWF0cml4LCAyLCBmdW5jdGlvbihjb2wpIGxlbmd0aCh1bmlxdWUoY29sKSkgPiAxKSkKCiMgQ3JlYXRlIGEgc3VtbWFyeSBkYXRhZnJhbWUKc3VtbWFyeV9kZiA8LSBkYXRhLmZyYW1lKAogIFBvc2l0aW9uID0gZGlmZl9wb3NpdGlvbnMsCiAgUmVmQUEgPSBtc2FfbWF0cml4W3JlZl9zZXEsIGRpZmZfcG9zaXRpb25zXSwKICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKKQoKIyBBZGQgY29sdW1ucyBmb3IgZWFjaCB1bmlxdWUgbXV0SUQKdW5pcXVlX211dElEcyA8LSBzZXRkaWZmKHJvd25hbWVzKG1zYV9tYXRyaXgpLCByZWZfc2VxKQpmb3IgKG11dElEIGluIHVuaXF1ZV9tdXRJRHMpIHsKICBzdW1tYXJ5X2RmW1ttdXRJRF1dIDwtIG1zYV9tYXRyaXhbbXV0SUQsIGRpZmZfcG9zaXRpb25zXQp9CgojIEFkZCBhIGNvbHVtbiBmb3IgdGhlIGFtaW5vIGFjaWQgY2hhbmdlcwpzdW1tYXJ5X2RmJENoYW5nZXMgPC0gYXBwbHkoc3VtbWFyeV9kZiwgMSwgZnVuY3Rpb24ocm93KSB7CiAgY2hhbmdlcyA8LSBzZXRkaWZmKGFzLmNoYXJhY3Rlcihyb3dbMzpuY29sKHN1bW1hcnlfZGYpXSksIHJvd1siUmVmQUEiXSkKICBpZiAobGVuZ3RoKGNoYW5nZXMpID4gMCkgewogICAgcGFzdGUocm93WyJSZWZBQSJdLCBwYXN0ZShjaGFuZ2VzLCBjb2xsYXBzZSA9ICIvIiksIHNlcCA9ICItPiIpCiAgfSBlbHNlIHsKICAgICJObyBjaGFuZ2UiCiAgfQp9KQoKIyBSZW1vdmUgcm93cyB3aXRoICJObyBjaGFuZ2UiCnN1bW1hcnlfZGYgPC0gc3VtbWFyeV9kZltzdW1tYXJ5X2RmJENoYW5nZXMgIT0gIk5vIGNoYW5nZSIsIF0KCiMgUmVvcmRlciBjb2x1bW5zCnN1bW1hcnlfZGYgPC0gc3VtbWFyeV9kZiAlPiUgCiAgc2VsZWN0KFBvc2l0aW9uLCBSZWZBQSwgQ2hhbmdlcywgZXZlcnl0aGluZygpKQoKIyBQcmludCB0aGUgc3VtbWFyeSB0YWJsZQpwcmludChzdW1tYXJ5X2RmLCByb3cubmFtZXMgPSBGQUxTRSkKYGBgCgpBcnJhbmdlIHRoZSBkYXRhIGZvciB0aGUgYW1pbm8gYWNpZCBtdXRhdGlvbnMgaGVhdG1hcDoKYGBge3J9CiMgR2V0IGFsbCBwb3NpdGlvbnMgd2hlcmUgdGhlcmUncyBhIG11dGF0aW9uCmFsbF9wb3NpdGlvbnMgPC0gc29ydCh1bmlxdWUoc3VtbWFyeV9kZiRQb3NpdGlvbikpCgojIFJlc2hhcGUgdGhlIGRhdGEKaGVhdG1hcF9kYXRhIDwtIHN1bW1hcnlfZGYgJT4lCiAgc2VsZWN0KC1DaGFuZ2VzKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IC1jKFBvc2l0aW9uLCBSZWZBQSksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJtdXRJRCIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiQUEiKSAlPiUKICBtdXRhdGUoUG9zaXRpb24gPSBhcy5udW1lcmljKFBvc2l0aW9uKSkKCiMgQWRkIHRoZSByZWZlcmVuY2Ugc2VxdWVuY2UKcmVmX2RhdGEgPC0gc3VtbWFyeV9kZiAlPiUKICBzZWxlY3QoUG9zaXRpb24sIFJlZkFBKSAlPiUKICBtdXRhdGUobXV0SUQgPSAiTlBfODMxOTU3IiwgQUEgPSBSZWZBQSkKCmhlYXRtYXBfZGF0YSA8LSBiaW5kX3Jvd3MocmVmX2RhdGEsIGhlYXRtYXBfZGF0YSkKCiMgQ3JlYXRlIGEgZnVuY3Rpb24gdG8gY29tcGFyZSBhbWlubyBhY2lkcwpjb21wYXJlX2FhIDwtIGZ1bmN0aW9uKGFhLCByZWZfYWEpIHsKICBpZmVsc2UoaXMubmEoYWEpIHwgYWEgPT0gIiIsICJObyBkYXRhIiwKICAgICAgICAgaWZlbHNlKGFhID09IHJlZl9hYSwgIk5vIGNoYW5nZSIsICJNdXRhdGlvbiIpKQp9CgojIEFwcGx5IHRoZSBjb21wYXJpc29uIGZ1bmN0aW9uIGFuZCBtb2RpZnkgQUEgZGlzcGxheQpoZWF0bWFwX2RhdGEgPC0gaGVhdG1hcF9kYXRhICU+JQogIGdyb3VwX2J5KFBvc2l0aW9uKSAlPiUKICBtdXRhdGUoCiAgICBDb21wYXJpc29uID0gY29tcGFyZV9hYShBQSwgQUFbbXV0SUQgPT0gIk5QXzgzMTk1NyJdKSwKICAgIERpc3BsYXlBQSA9IGNhc2Vfd2hlbigKICAgICAgbXV0SUQgPT0gIk5QXzgzMTk1NyIgfiBBQSwKICAgICAgQ29tcGFyaXNvbiA9PSAiTXV0YXRpb24iIH4gQUEsCiAgICAgIFRSVUUgfiAiIgogICAgKQogICkgJT4lCiAgdW5ncm91cCgpCgojIENyZWF0ZSBhIGNvbnRpbnVvdXMgcG9zaXRpb24gdmFyaWFibGUKaGVhdG1hcF9kYXRhIDwtIGhlYXRtYXBfZGF0YSAlPiUKICBhcnJhbmdlKFBvc2l0aW9uKSAlPiUKICBtdXRhdGUoUG9zaXRpb25JbmRleCA9IGFzLm51bWVyaWMoZmFjdG9yKFBvc2l0aW9uLCBsZXZlbHMgPSB1bmlxdWUoUG9zaXRpb24pKSkpCgojIENyZWF0ZSBCYWNpbGx1c19sb25nCkJhY2lsbHVzX2xvbmcgPC0gQmFjaWxsdXNfZGlhbG91dCAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0c193aXRoKCJmaXREIiksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJUTVAiLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIkZpdG5lc3MiLAogICAgICAgICAgICAgICBuYW1lc19wcmVmaXggPSAiZml0RCIpCgojIENhbGN1bGF0ZSB0aGUgZml0bmVzcyBhdCB0aGUgaGlnaGVzdCBUTVAgY29uY2VudHJhdGlvbiBmb3IgZWFjaCBtdXRJRAptYXhfZml0bmVzcyA8LSBCYWNpbGx1c19sb25nICU+JQogIGZpbHRlcihUTVAgPT0gIjExRDAzIikgJT4lCiAgc2VsZWN0KG11dElELCBtYXhfZml0bmVzcyA9IEZpdG5lc3MpCgojIEpvaW4gbWF4X2ZpdG5lc3MgaW5mb3JtYXRpb24gYW5kIGNyZWF0ZSBNdXRhbnRDb2xvcgpoZWF0bWFwX2RhdGEgPC0gaGVhdG1hcF9kYXRhICU+JQogIGxlZnRfam9pbihtYXhfZml0bmVzcywgYnkgPSAibXV0SUQiKSAlPiUKICBtdXRhdGUoTXV0YW50Q29sb3IgPSBjYXNlX3doZW4oCiAgICBtdXRJRCA9PSAiTlBfODMxOTU3IiB+ICJyZWQiLAogICAgaXMubmEobWF4X2ZpdG5lc3MpIH4gImdyYXkiLAogICAgbWF4X2ZpdG5lc3MgPCAtMSB+ICJkYXJrYmx1ZSIsCiAgICBtYXhfZml0bmVzcyA+PSAtMSB+ICJnb2xkMiIKICApKQoKIyBDcmVhdGUgYSBuZXcgY29sdW1uIGZvciBvcmRlcmluZwpoZWF0bWFwX2RhdGEgPC0gaGVhdG1hcF9kYXRhICU+JQogIG11dGF0ZShPcmRlckdyb3VwID0gY2FzZV93aGVuKAogICAgbXV0SUQgPT0gIk5QXzgzMTk1NyIgfiAxLAogICAgbWF4X2ZpdG5lc3MgPj0gLTEgfiAyLAogICAgbWF4X2ZpdG5lc3MgPCAtMSB+IDMsCiAgICBUUlVFIH4gNCAgIyBmb3IgYW55IE5BIG9yIG90aGVyIGNhc2VzCiAgKSkKYGBgCgpIZWF0bWFwIGZvciBhbWlubyBhY2lkIG11dGF0aW9ucyBieSBwb3NpdGlvbiBiYXNlZCBvbiByZWZlcmVuY2Ugc2VxdWVuY2U6CmBgYHtyfQojIE9yZGVyIHRoZSBkYXRhIHdpdGggcmVmZXJlbmNlIG9uIHRvcApoZWF0bWFwX2RhdGEgPC0gaGVhdG1hcF9kYXRhICU+JQogIGFycmFuZ2UoT3JkZXJHcm91cCwgZGVzYyhtYXhfZml0bmVzcykpICU+JQogIG11dGF0ZShtdXRJRCA9IGZhY3RvcihtdXRJRCwgbGV2ZWxzID0gdW5pcXVlKG11dElEKSkpCgojIEVuc3VyZSB0aGUgY29sb3JzIGFyZSBhc3NpZ25lZCBjb3JyZWN0bHkKeV9jb2xvcnMgPC0gaGVhdG1hcF9kYXRhICU+JQogIGRpc3RpbmN0KG11dElELCBNdXRhbnRDb2xvcikgJT4lCiAgYXJyYW5nZShtYXRjaChtdXRJRCwgbGV2ZWxzKGhlYXRtYXBfZGF0YSRtdXRJRCkpKQoKIyBNb2RpZnkgdGhlIGhlYXRtYXBfZGF0YSB0byBpbmNsdWRlIHRoZSBjb2xvciBpbmZvcm1hdGlvbiBmb3IgbXV0YXRpb25zIGFuZCB0ZXh0CmhlYXRtYXBfZGF0YSA8LSBoZWF0bWFwX2RhdGEgJT4lCiAgbXV0YXRlKAogICAgTXV0YXRpb25Db2xvciA9IGNhc2Vfd2hlbigKICAgICAgQ29tcGFyaXNvbiA9PSAiTXV0YXRpb24iICYgbWF4X2ZpdG5lc3MgPj0gLTEgfiAiZ29sZDIiLAogICAgICBDb21wYXJpc29uID09ICJNdXRhdGlvbiIgJiBtYXhfZml0bmVzcyA8IC0xIH4gImRhcmtibHVlIiwKICAgICAgVFJVRSB+IENvbXBhcmlzb24KICAgICksCiAgICBUZXh0Q29sb3IgPSBjYXNlX3doZW4oCiAgICAgIE11dGF0aW9uQ29sb3IgPT0gImRhcmtibHVlIiB+ICJ3aGl0ZSIsCiAgICAgIFRSVUUgfiAiYmxhY2siCiAgICApCiAgKQoKIyBDcmVhdGUgdGhlIGhlYXRtYXAKQmFjaWxsdXMuZGlhbG91dC5oZWF0bWFwIDwtIGdncGxvdChoZWF0bWFwX2RhdGEsIGFlcyh4ID0gUG9zaXRpb25JbmRleCwgeSA9IG11dElELCBmaWxsID0gTXV0YXRpb25Db2xvcikpICsKICBnZW9tX3RpbGUoY29sb3IgPSAid2hpdGUiLCB3aWR0aCA9IDEsIGhlaWdodCA9IDAuOSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIk5vIGNoYW5nZSIgPSAiZ3JheTkwIiwgImdvbGQyIiA9ICJnb2xkMiIsICJkYXJrYmx1ZSIgPSAiZGFya2JsdWUiLCAiTm8gZGF0YSIgPSAid2hpdGUiKSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEsIHZqdXN0ID0gMC41LCBzaXplID0gMTIpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9saW5lKGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDAuNSksCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAid2hpdGUiLCBzaXplID0gMC41KSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIKICApICsKICBsYWJzKHggPSAiUG9zaXRpb24iLCBmaWxsID0gIkFtaW5vIEFjaWQgQ2hhbmdlIikgKwogIHNjYWxlX3hfY29udGludW91cygKICAgIGJyZWFrcyA9IGhlYXRtYXBfZGF0YSRQb3NpdGlvbkluZGV4LAogICAgbGFiZWxzID0gaGVhdG1hcF9kYXRhJFBvc2l0aW9uLAogICAgZXhwYW5kID0gYygwLCAwKQogICkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBEaXNwbGF5QUEsIGNvbG9yID0gVGV4dENvbG9yKSwgc2l6ZSA9IDYsIG5hLnJtID0gVFJVRSwgZm9udGZhY2UgPSAiYm9sZCIpICsKICBzY2FsZV9jb2xvcl9pZGVudGl0eSgpICsKICBzY2FsZV95X2Rpc2NyZXRlKGxpbWl0cyA9IHJldihsZXZlbHMoaGVhdG1hcF9kYXRhJG11dElEKSksIGV4cGFuZCA9IGMoMCwgMCkpCgojIEFwcGx5IGNvcnJlY3QgY29sb3JzIHRvIHktYXhpcyBsYWJlbHMKQmFjaWxsdXMuZGlhbG91dC5oZWF0bWFwIDwtIEJhY2lsbHVzLmRpYWxvdXQuaGVhdG1hcCArCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoY29sb3IgPSByZXYoeV9jb2xvcnMkTXV0YW50Q29sb3IpKSkgKyAKICBnZ3RpdGxlKCJCYWNpbGx1cyBjZXJldXMiKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiaXRhbGljIiwgc2l6ZSA9IDE2KSkKCnByaW50KEJhY2lsbHVzLmRpYWxvdXQuaGVhdG1hcCkKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIFBORwpnZ3NhdmUoZmlsZT0iUmVzaXN0YW5jZS9QTE9UUy9GaW5hbC9CYWNpbGx1cy5kaWFsb3V0LmhlYXRtYXAucG5nIiwgCiAgICAgICBwbG90PUJhY2lsbHVzLmRpYWxvdXQuaGVhdG1hcCwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gNy41LCBoZWlnaHQgPSA0LjUsIHVuaXRzID0gImluIikKYGBgCgpQbG90IHRoZSBjaGFuZ2UgaW4gZml0bmVzcyBhY3Jvc3MgdGhlIFRNUCBncmFkaWVudCBmb3IgYm90aCBzYW1wbGVzOgpgYGB7ciB3YXJuaW5nPUZBTFNFfQojIFJlc2hhcGUgdGhlIGRhdGEgZnJvbSB3aWRlIHRvIGxvbmcgZm9ybWF0CkJhY2lsbHVzX2RpYWxvdXRfbG9uZyA8LSBCYWNpbGx1c19kaWFsb3V0ICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoImZpdEQiKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIlRNUCIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiRml0bmVzcyIsCiAgICAgICAgICAgICAgIG5hbWVzX3ByZWZpeCA9ICJmaXREIikgJT4lCiAgbXV0YXRlKERheSA9IGZhY3RvcihUTVAsIGxldmVscyA9IGMoIjA1RDAzIiwgIjA2RDAzIiwgIjA3RDAzIiwgIjA4RDAzIiwgIjA5RDAzIiwgIjEwRDAzIiwgIjExRDAzIiksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCIwIiwgIjAuMDU4IiwgIjAuNSIsICIxLjAiLCAiMTAiLCAiNTAiLCAiMjAwIikpKQoKIyBDYWxjdWxhdGUgdGhlIGZpdG5lc3MgYXQgdGhlIGhpZ2hlc3QgVE1QIGNvbmNlbnRyYXRpb24gZm9yIGVhY2ggbXV0SUQKQmFjaWxsdXNfZGlhbG91dF9tYXhfZml0bmVzcyA8LSBCYWNpbGx1c19kaWFsb3V0X2xvbmcgJT4lCiAgZmlsdGVyKFRNUCA9PSAiMTFEMDMiKSAlPiUKICBzZWxlY3QobXV0SUQsIG1heF9maXRuZXNzID0gRml0bmVzcykKCiMgSm9pbiB0aGlzIGluZm9ybWF0aW9uIGJhY2sgdG8gdGhlIG9yaWdpbmFsIGRhdGEgYW5kIGNvbG9yIGFjY29yZGluZ2x5CkJhY2lsbHVzX2RpYWxvdXRfbG9uZyA8LSBCYWNpbGx1c19kaWFsb3V0X2xvbmcgJT4lCiAgbGVmdF9qb2luKEJhY2lsbHVzX2RpYWxvdXRfbWF4X2ZpdG5lc3MsIGJ5ID0gIm11dElEIikgJT4lCiAgbXV0YXRlKENvbG9yID0gY2FzZV93aGVuKAogICAgbXV0SUQgPT0gIk5QXzgzMTk1NyIgfiAiV1QiLAogICAgaXMubmEobWF4X2ZpdG5lc3MpIH4gIk11dGFudF9Vbmtub3duIiwKICAgIG1heF9maXRuZXNzIDwgLTEgfiAiTXV0YW50X0xvdyIsCiAgICBtYXhfZml0bmVzcyA+PSAtMSB+ICJNdXRhbnRfSGlnaCIKICApKQoKIyBSZW9yZGVyIHRoZSBsZXZlbHMgb2YgQ29sb3IgZmFjdG9yIHRvIHBsb3QgV1QgbGFzdApCYWNpbGx1c19kaWFsb3V0X2xvbmckQ29sb3IgPC0gZmFjdG9yKEJhY2lsbHVzX2RpYWxvdXRfbG9uZyRDb2xvciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiTXV0YW50X1Vua25vd24iLCAiTXV0YW50X0xvdyIsICJNdXRhbnRfSGlnaCIsICJXVCIpKQoKIyBTZXBhcmF0ZSBXVCBhbmQgbXV0YW50IGRhdGEKV1RfZGF0YSA8LSBCYWNpbGx1c19kaWFsb3V0X2xvbmcgJT4lIGZpbHRlcihtdXRJRCA9PSAiTlBfODMxOTU3IikKbXV0YW50X2RhdGEgPC0gQmFjaWxsdXNfZGlhbG91dF9sb25nICU+JSBmaWx0ZXIobXV0SUQgIT0gIk5QXzgzMTk1NyIpCmBgYAoKRml0bmVzcyBwbG90dGluZyBvdmVyIHRoZSBUTVAgZ3JhZGllbnQKYGBge3J9CiMgRmlyc3QsIGludGVycG9sYXRlIE5BIHZhbHVlcwptdXRhbnRfZGF0YSA8LSBtdXRhbnRfZGF0YSAlPiUKICBncm91cF9ieShtdXRJRCkgJT4lCiAgYXJyYW5nZShEYXkpICU+JQogIG11dGF0ZShGaXRuZXNzID0gbmEuYXBwcm94KEZpdG5lc3MsIHggPSBEYXksIG5hLnJtID0gRkFMU0UpKSAlPiUKICB1bmdyb3VwKCkKCldUX2RhdGEgPC0gV1RfZGF0YSAlPiUKICBhcnJhbmdlKERheSkgJT4lCiAgbXV0YXRlKEZpdG5lc3MgPSBuYS5hcHByb3goRml0bmVzcywgeCA9IERheSwgbmEucm0gPSBGQUxTRSkpCgojIFRoZW4gY3JlYXRlIHRoZSBwbG90CkJhY2lsbHVzX2RpYWxvdXRfcGxvdCA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLTEsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyYXk1MCIpICsKICAjIFBsb3QgbXV0YW50IGxpbmVzIGZpcnN0CiAgZ2VvbV9saW5lKGRhdGEgPSBtdXRhbnRfZGF0YSwgYWVzKHggPSBEYXksIHkgPSBGaXRuZXNzLCBncm91cCA9IG11dElELCBjb2xvciA9IENvbG9yKSwgc2l6ZSA9IDEuMCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IG11dGFudF9kYXRhLCBhZXMoeCA9IERheSwgeSA9IEZpdG5lc3MsIGNvbG9yID0gQ29sb3IpLCBzaXplID0gMykgKwogICMgUGxvdCBXVCBsaW5lIG9uIHRvcAogIGdlb21fbGluZShkYXRhID0gV1RfZGF0YSwgYWVzKHggPSBEYXksIHkgPSBGaXRuZXNzLCBncm91cCA9IG11dElEKSwgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDIuMCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IFdUX2RhdGEsIGFlcyh4ID0gRGF5LCB5ID0gRml0bmVzcyksIGNvbG9yID0gInJlZCIsIHNpemUgPSAzKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIk11dGFudF9Mb3ciID0gImRhcmtibHVlIiwgIk11dGFudF9IaWdoIiA9ICJnb2xkIiwgIk11dGFudF9Vbmtub3duIiA9ICJncmF5IikpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IGMoIjAiLCAiMC4wNTgiLCAiMC41IiwgIjEuMCIsICIxMCIsICI1MCIsICIyMDAiKSkgKwogIGxhYnMoeCA9ICJUcmltZXRob3ByaW0gKHVnL21MKSIsCiAgICAgICB5ID0gIk1lZGlhbiBGaXRuZXNzIChMb2dGQykiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZSgKICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMS4wKSwKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDEuMCksCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKyAKICBnZ3RpdGxlKCJCYWNpbGx1cyBjZXJldXMiKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiaXRhbGljIiwgc2l6ZSA9IDE2KSkKCiMgUHJpbnQgdGhlIHBsb3QKcHJpbnQoQmFjaWxsdXNfZGlhbG91dF9wbG90KQpgYGAKCgpgYGB7ciBlY2hvPUZBTFNFfQojIFBORwpnZ3NhdmUoZmlsZT0iUmVzaXN0YW5jZS9QTE9UUy9GaW5hbC9CYWNpbGx1cy5kaWFsb3V0LmZpdG5lc3MucG5nIiwgCiAgICAgICBwbG90PUJhY2lsbHVzX2RpYWxvdXRfcGxvdCwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gNC41LCBoZWlnaHQgPSA0LjUsIHVuaXRzID0gImluIikKYGBgCgpQbG90IHRvZ2V0aGVyOgpgYGB7cn0KcGF0Y2gyMiA8LSBCYWNpbGx1c19kaWFsb3V0X3Bsb3QgfCBCYWNpbGx1cy5kaWFsb3V0LmhlYXRtYXAKcGF0Y2gyMgpgYGAKCmBgYHtyIGVjaG89RkFMU0V9CiMgUE5HCmdnc2F2ZShmaWxlPSJSZXNpc3RhbmNlL1BMT1RTL0ZpbmFsL0JhY2lsbHVzLkRpYWxvdXQuQm90aC5wbmciLCAKICAgICAgIHBsb3Q9cGF0Y2gyMiwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gMTEsIGhlaWdodCA9IDUsIHVuaXRzID0gImluIikKYGBgCgojIyMgU3RyZXB0b2NvY2N1cyBwbmVtb25pYWUKYGBge3J9CiMgRmlsdGVyIHRvIG9ubHkgcmV0YWluIHNwZWNpZmljIHZhcmlhbnRzIGZvciB0aGUgTVNBClN0cmVwX2RpYWxvdXQgPC0gU3RyZXB0b2NvY2N1cyAlPiUKICBmaWx0ZXIoKChtdXRJRCA9PSAiV1BfMDAyMjA1MzI3IikgfCAKICAgICAgICAgIChtdXRJRCA9PSAiV1BfMDAwMTYyNDUzIikpICYgCiAgICAgICAgICFpcy5uYShmaXREMTFEMDMpKQpgYGAKCkFsaWduIHRoZSB0d28gdmFyaWFudHMgYW5kIHJldGFpbiBXUF8wMDIyMDUzMjcgYXMgdGhlIHJlZmVyZW5jZSBzZXF1ZW5jZQpgYGB7cn0KIyBDcmVhdGUgYSBmdW5jdGlvbiB0byB3cml0ZSBGQVNUQSBlbnRyaWVzCndyaXRlX2Zhc3RhIDwtIGZ1bmN0aW9uKGlkLCBzZXEsIGZpbGUpIHsKICBjYXQocGFzdGUwKCI+IiwgaWQsICJcbiIpLCBmaWxlID0gZmlsZSwgYXBwZW5kID0gVFJVRSkKICBjYXQocGFzdGUwKCJNIiwgc2VxLCAiXG4iKSwgZmlsZSA9IGZpbGUsIGFwcGVuZCA9IFRSVUUpICAjIEFkZCAiTSIgYXQgdGhlIGJlZ2lubmluZyBvZiB0aGUgc2VxdWVuY2UKfQoKIyBTcGVjaWZ5IHRoZSBvdXRwdXQgZmlsZSBuYW1lClN0cmVwX2RpYWxvdXRfb3V0cHV0X2ZpbGUgPC0gIlJlc2lzdGFuY2UvRkFTVEEvU3RyZXAuZGlhbG91dC5mYXN0YSIKCiMgUmVtb3ZlIHRoZSBmaWxlIGlmIGl0IGFscmVhZHkgZXhpc3RzCmlmIChmaWxlLmV4aXN0cyhTdHJlcF9kaWFsb3V0X291dHB1dF9maWxlKSkgewogIGZpbGUucmVtb3ZlKFN0cmVwX2RpYWxvdXRfb3V0cHV0X2ZpbGUpCn0KCiMgV3JpdGUgdGhlIEZBU1RBIGVudHJpZXMgdG8gdGhlIGZpbGUKZm9yIChpIGluIDE6bnJvdyhTdHJlcF9kaWFsb3V0KSkgewogIHdyaXRlX2Zhc3RhKFN0cmVwX2RpYWxvdXQkbXV0SURbaV0sIFN0cmVwX2RpYWxvdXQkc2VxW2ldLCBTdHJlcF9kaWFsb3V0X291dHB1dF9maWxlKQp9CgpjYXQoIkZBU1RBIGZpbGUgaGFzIGJlZW4gZ2VuZXJhdGVkOiIsIFN0cmVwX2RpYWxvdXRfb3V0cHV0X2ZpbGUsICJcbiIpCmBgYAoKUGVyZm9ybSBhIG11bHRpcGxlIHNlcXVlbmNlIGFsaWdubWVudCBvbiB0aGUgRkFTVEEgZmlsZSB1c2luZyBDTFVTVEFMIE9tZWdhOgpgYGB7YmFzaH0KIyBNYXkgbmVlZCB0byBlbmFibGUgcGVybWlzc2lvbnMgdG8gcnVuIHRoZSBleGVjdXRhYmxlOgojY2htb2QgK3ggY2x1c3RhbG8KLi9TY3JpcHRzL2NsdXN0YWxvIC1pIFJlc2lzdGFuY2UvRkFTVEEvU3RyZXAuZGlhbG91dC5mYXN0YSAtbyBSZXNpc3RhbmNlL0ZBU1RBL1N0cmVwLmRpYWxvdXQuYWxpZ25lZC5mYXN0YSAtLW91dGZtdD1mYSAtLWZvcmNlCmBgYAoKSW1wb3J0IHRoZSBNU0EgZmlsZSBhbmQgZGV0ZXJtaW5lIHdoaWNoIGFtaW5vIGFjaWQgcG9zaXRpb25zIGRpZmZlciBiZXR3ZWVuIHRoZSBzYW1wbGVzCmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIEltcG9ydCB0aGUgTVNBIGZpbGUKbXNhIDwtIHJlYWRBQU11bHRpcGxlQWxpZ25tZW50KCJSZXNpc3RhbmNlL0ZBU1RBL1N0cmVwLmRpYWxvdXQuYWxpZ25lZC5mYXN0YSIsIGZvcm1hdD0iZmFzdGEiKQoKIyBDb252ZXJ0IHRvIGEgbWF0cml4IGZvciBlYXNpZXIgbWFuaXB1bGF0aW9uCm1zYV9tYXRyaXggPC0gYXMubWF0cml4KG1zYSkKCiMgU2V0IHRoZSByZWZlcmVuY2Ugc2VxdWVuY2UKcmVmX3NlcSA8LSAiV1BfMDAyMjA1MzI3IgoKY2F0KCJVc2luZyByZWZlcmVuY2Ugc2VxdWVuY2U6IiwgcmVmX3NlcSwgIlxuIikKCiMgRmluZCBwb3NpdGlvbnMgd2hlcmUgYW1pbm8gYWNpZHMgZGlmZmVyCmRpZmZfcG9zaXRpb25zIDwtIHdoaWNoKGFwcGx5KG1zYV9tYXRyaXgsIDIsIGZ1bmN0aW9uKGNvbCkgbGVuZ3RoKHVuaXF1ZShjb2wpKSA+IDEpKQoKIyBDcmVhdGUgYSBzdW1tYXJ5IGRhdGFmcmFtZQpzdW1tYXJ5X2RmIDwtIGRhdGEuZnJhbWUoCiAgUG9zaXRpb24gPSBkaWZmX3Bvc2l0aW9ucywKICBSZWZBQSA9IG1zYV9tYXRyaXhbcmVmX3NlcSwgZGlmZl9wb3NpdGlvbnNdLAogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQopCgojIEFkZCBjb2x1bW5zIGZvciBlYWNoIHVuaXF1ZSBtdXRJRAp1bmlxdWVfbXV0SURzIDwtIHNldGRpZmYocm93bmFtZXMobXNhX21hdHJpeCksIHJlZl9zZXEpCmZvciAobXV0SUQgaW4gdW5pcXVlX211dElEcykgewogIHN1bW1hcnlfZGZbW211dElEXV0gPC0gbXNhX21hdHJpeFttdXRJRCwgZGlmZl9wb3NpdGlvbnNdCn0KCiMgQWRkIGEgY29sdW1uIGZvciB0aGUgYW1pbm8gYWNpZCBjaGFuZ2VzCnN1bW1hcnlfZGYkQ2hhbmdlcyA8LSBhcHBseShzdW1tYXJ5X2RmLCAxLCBmdW5jdGlvbihyb3cpIHsKICBjaGFuZ2VzIDwtIHNldGRpZmYoYXMuY2hhcmFjdGVyKHJvd1szOm5jb2woc3VtbWFyeV9kZildKSwgcm93WyJSZWZBQSJdKQogIGlmIChsZW5ndGgoY2hhbmdlcykgPiAwKSB7CiAgICBwYXN0ZShyb3dbIlJlZkFBIl0sIHBhc3RlKGNoYW5nZXMsIGNvbGxhcHNlID0gIi8iKSwgc2VwID0gIi0+IikKICB9IGVsc2UgewogICAgIk5vIGNoYW5nZSIKICB9Cn0pCgojIFJlbW92ZSByb3dzIHdpdGggIk5vIGNoYW5nZSIKc3VtbWFyeV9kZiA8LSBzdW1tYXJ5X2RmW3N1bW1hcnlfZGYkQ2hhbmdlcyAhPSAiTm8gY2hhbmdlIiwgXQoKIyBSZW9yZGVyIGNvbHVtbnMKc3VtbWFyeV9kZiA8LSBzdW1tYXJ5X2RmICU+JSAKICBzZWxlY3QoUG9zaXRpb24sIFJlZkFBLCBDaGFuZ2VzLCBldmVyeXRoaW5nKCkpCgojIFByaW50IHRoZSBzdW1tYXJ5IHRhYmxlCnByaW50KHN1bW1hcnlfZGYsIHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKCkFycmFuZ2UgdGhlIGRhdGEgZm9yIHRoZSBhbWlubyBhY2lkIG11dGF0aW9ucyBoZWF0bWFwOgpgYGB7cn0KIyBHZXQgYWxsIHBvc2l0aW9ucyB3aGVyZSB0aGVyZSdzIGEgbXV0YXRpb24KYWxsX3Bvc2l0aW9ucyA8LSBzb3J0KHVuaXF1ZShzdW1tYXJ5X2RmJFBvc2l0aW9uKSkKCiMgUmVzaGFwZSB0aGUgZGF0YQpoZWF0bWFwX2RhdGEgPC0gc3VtbWFyeV9kZiAlPiUKICBzZWxlY3QoLUNoYW5nZXMpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gLWMoUG9zaXRpb24sIFJlZkFBKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIm11dElEIiwgCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJBQSIpICU+JQogIG11dGF0ZShQb3NpdGlvbiA9IGFzLm51bWVyaWMoUG9zaXRpb24pKQoKIyBBZGQgdGhlIHJlZmVyZW5jZSBzZXF1ZW5jZQpyZWZfZGF0YSA8LSBzdW1tYXJ5X2RmICU+JQogIHNlbGVjdChQb3NpdGlvbiwgUmVmQUEpICU+JQogIG11dGF0ZShtdXRJRCA9ICJXUF8wMDIyMDUzMjciLCBBQSA9IFJlZkFBKQoKaGVhdG1hcF9kYXRhIDwtIGJpbmRfcm93cyhyZWZfZGF0YSwgaGVhdG1hcF9kYXRhKQoKIyBDcmVhdGUgYSBmdW5jdGlvbiB0byBjb21wYXJlIGFtaW5vIGFjaWRzCmNvbXBhcmVfYWEgPC0gZnVuY3Rpb24oYWEsIHJlZl9hYSkgewogIGlmZWxzZShpcy5uYShhYSkgfCBhYSA9PSAiIiwgIk5vIGRhdGEiLAogICAgICAgICBpZmVsc2UoYWEgPT0gcmVmX2FhLCAiTm8gY2hhbmdlIiwgIk11dGF0aW9uIikpCn0KCiMgQXBwbHkgdGhlIGNvbXBhcmlzb24gZnVuY3Rpb24gYW5kIG1vZGlmeSBBQSBkaXNwbGF5CmhlYXRtYXBfZGF0YSA8LSBoZWF0bWFwX2RhdGEgJT4lCiAgZ3JvdXBfYnkoUG9zaXRpb24pICU+JQogIG11dGF0ZSgKICAgIENvbXBhcmlzb24gPSBjb21wYXJlX2FhKEFBLCBBQVttdXRJRCA9PSAiV1BfMDAyMjA1MzI3Il0pLAogICAgRGlzcGxheUFBID0gY2FzZV93aGVuKAogICAgICBtdXRJRCA9PSAiV1BfMDAyMjA1MzI3IiB+IEFBLAogICAgICBDb21wYXJpc29uID09ICJNdXRhdGlvbiIgfiBBQSwKICAgICAgVFJVRSB+ICIiCiAgICApCiAgKSAlPiUKICB1bmdyb3VwKCkKCiMgQ3JlYXRlIGEgY29udGludW91cyBwb3NpdGlvbiB2YXJpYWJsZQpoZWF0bWFwX2RhdGEgPC0gaGVhdG1hcF9kYXRhICU+JQogIGFycmFuZ2UoUG9zaXRpb24pICU+JQogIG11dGF0ZShQb3NpdGlvbkluZGV4ID0gYXMubnVtZXJpYyhmYWN0b3IoUG9zaXRpb24sIGxldmVscyA9IHVuaXF1ZShQb3NpdGlvbikpKSkKCiMgQ3JlYXRlIFN0cmVwX2xvbmcKU3RyZXBfbG9uZyA8LSBTdHJlcF9kaWFsb3V0ICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoImZpdEQiKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIlRNUCIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiRml0bmVzcyIsCiAgICAgICAgICAgICAgIG5hbWVzX3ByZWZpeCA9ICJmaXREIikKCiMgQ2FsY3VsYXRlIHRoZSBmaXRuZXNzIGF0IHRoZSBoaWdoZXN0IFRNUCBjb25jZW50cmF0aW9uIGZvciBlYWNoIG11dElECm1heF9maXRuZXNzIDwtIFN0cmVwX2xvbmcgJT4lCiAgZmlsdGVyKFRNUCA9PSAiMTFEMDMiKSAlPiUKICBzZWxlY3QobXV0SUQsIG1heF9maXRuZXNzID0gRml0bmVzcykKCiMgSm9pbiBtYXhfZml0bmVzcyBpbmZvcm1hdGlvbiBhbmQgY3JlYXRlIE11dGFudENvbG9yCmhlYXRtYXBfZGF0YSA8LSBoZWF0bWFwX2RhdGEgJT4lCiAgbGVmdF9qb2luKG1heF9maXRuZXNzLCBieSA9ICJtdXRJRCIpICU+JQogIG11dGF0ZShNdXRhbnRDb2xvciA9IGNhc2Vfd2hlbigKICAgIG11dElEID09ICJXUF8wMDIyMDUzMjciIH4gInJlZCIsCiAgICBpcy5uYShtYXhfZml0bmVzcykgfiAiZ3JheSIsCiAgICBtYXhfZml0bmVzcyA8IC0xIH4gImRhcmtibHVlIiwKICAgIG1heF9maXRuZXNzID49IC0xIH4gImdvbGQyIgogICkpCgojIENyZWF0ZSBhIG5ldyBjb2x1bW4gZm9yIG9yZGVyaW5nCmhlYXRtYXBfZGF0YSA8LSBoZWF0bWFwX2RhdGEgJT4lCiAgbXV0YXRlKE9yZGVyR3JvdXAgPSBjYXNlX3doZW4oCiAgICBtdXRJRCA9PSAiV1BfMDAyMjA1MzI3IiB+IDEsCiAgICBtYXhfZml0bmVzcyA+PSAtMSB+IDIsCiAgICBtYXhfZml0bmVzcyA8IC0xIH4gMywKICAgIFRSVUUgfiA0ICAjIGZvciBhbnkgTkEgb3Igb3RoZXIgY2FzZXMKICApKQpgYGAKCkhlYXRtYXAgZm9yIGFtaW5vIGFjaWQgbXV0YXRpb25zIGJ5IHBvc2l0aW9uIGJhc2VkIG9uIHJlZmVyZW5jZSBzZXF1ZW5jZToKYGBge3J9CiMgT3JkZXIgdGhlIGRhdGEgd2l0aCByZWZlcmVuY2Ugb24gdG9wCmhlYXRtYXBfZGF0YSA8LSBoZWF0bWFwX2RhdGEgJT4lCiAgYXJyYW5nZShPcmRlckdyb3VwLCBkZXNjKG1heF9maXRuZXNzKSkgJT4lCiAgbXV0YXRlKG11dElEID0gZmFjdG9yKG11dElELCBsZXZlbHMgPSB1bmlxdWUobXV0SUQpKSkKCiMgRW5zdXJlIHRoZSBjb2xvcnMgYXJlIGFzc2lnbmVkIGNvcnJlY3RseQp5X2NvbG9ycyA8LSBoZWF0bWFwX2RhdGEgJT4lCiAgZGlzdGluY3QobXV0SUQsIE11dGFudENvbG9yKSAlPiUKICBhcnJhbmdlKG1hdGNoKG11dElELCBsZXZlbHMoaGVhdG1hcF9kYXRhJG11dElEKSkpCgojIE1vZGlmeSB0aGUgaGVhdG1hcF9kYXRhIHRvIGluY2x1ZGUgdGhlIGNvbG9yIGluZm9ybWF0aW9uIGZvciBtdXRhdGlvbnMgYW5kIHRleHQKaGVhdG1hcF9kYXRhIDwtIGhlYXRtYXBfZGF0YSAlPiUKICBtdXRhdGUoCiAgICBNdXRhdGlvbkNvbG9yID0gY2FzZV93aGVuKAogICAgICBDb21wYXJpc29uID09ICJNdXRhdGlvbiIgJiBtYXhfZml0bmVzcyA+PSAtMSB+ICJnb2xkMiIsCiAgICAgIENvbXBhcmlzb24gPT0gIk11dGF0aW9uIiAmIG1heF9maXRuZXNzIDwgLTEgfiAiZGFya2JsdWUiLAogICAgICBUUlVFIH4gQ29tcGFyaXNvbgogICAgKSwKICAgIFRleHRDb2xvciA9IGNhc2Vfd2hlbigKICAgICAgTXV0YXRpb25Db2xvciA9PSAiZGFya2JsdWUiIH4gIndoaXRlIiwKICAgICAgVFJVRSB+ICJibGFjayIKICAgICkKICApCgojIENyZWF0ZSB0aGUgaGVhdG1hcApTdHJlcC5kaWFsb3V0LmhlYXRtYXAgPC0gZ2dwbG90KGhlYXRtYXBfZGF0YSwgYWVzKHggPSBQb3NpdGlvbkluZGV4LCB5ID0gbXV0SUQsIGZpbGwgPSBNdXRhdGlvbkNvbG9yKSkgKwogIGdlb21fdGlsZShjb2xvciA9ICJ3aGl0ZSIsIHdpZHRoID0gMSwgaGVpZ2h0ID0gMC45KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiTm8gY2hhbmdlIiA9ICJncmF5OTAiLCAiZ29sZDIiID0gImdvbGQyIiwgImRhcmtibHVlIiA9ICJkYXJrYmx1ZSIsICJObyBkYXRhIiA9ICJ3aGl0ZSIpKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSwgdmp1c3QgPSAwLjUsIHNpemUgPSAxMiksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2xpbmUoY29sb3IgPSAid2hpdGUiLCBzaXplID0gMC41KSwKICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSAwLjUpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIgogICkgKwogIGxhYnMoeCA9ICJQb3NpdGlvbiIsIGZpbGwgPSAiQW1pbm8gQWNpZCBDaGFuZ2UiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKAogICAgYnJlYWtzID0gaGVhdG1hcF9kYXRhJFBvc2l0aW9uSW5kZXgsCiAgICBsYWJlbHMgPSBoZWF0bWFwX2RhdGEkUG9zaXRpb24sCiAgICBleHBhbmQgPSBjKDAsIDApCiAgKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IERpc3BsYXlBQSwgY29sb3IgPSBUZXh0Q29sb3IpLCBzaXplID0gNiwgbmEucm0gPSBUUlVFLCBmb250ZmFjZSA9ICJib2xkIikgKwogIHNjYWxlX2NvbG9yX2lkZW50aXR5KCkgKwogIHNjYWxlX3lfZGlzY3JldGUobGltaXRzID0gcmV2KGxldmVscyhoZWF0bWFwX2RhdGEkbXV0SUQpKSwgZXhwYW5kID0gYygwLCAwKSkKCiMgQXBwbHkgY29ycmVjdCBjb2xvcnMgdG8geS1heGlzIGxhYmVscwpTdHJlcC5kaWFsb3V0LmhlYXRtYXAgPC0gU3RyZXAuZGlhbG91dC5oZWF0bWFwICsKICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChjb2xvciA9IHJldih5X2NvbG9ycyRNdXRhbnRDb2xvcikpKSArIAogIGdndGl0bGUoIlN0cmVwdG9jb2NjdXMgcG5ldW1vbmlhZSIpICsgCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJpdGFsaWMiLCBzaXplID0gMTYpKQoKcHJpbnQoU3RyZXAuZGlhbG91dC5oZWF0bWFwKQpgYGAKCmBgYHtyIGVjaG89RkFMU0V9CiMgUE5HCmdnc2F2ZShmaWxlPSJSZXNpc3RhbmNlL1BMT1RTL0ZpbmFsL1N0cmVwLmRpYWxvdXQuaGVhdG1hcC5wbmciLCAKICAgICAgIHBsb3Q9U3RyZXAuZGlhbG91dC5oZWF0bWFwLAogICAgICAgZHBpPTYwMCwgd2lkdGggPSA3LjUsIGhlaWdodCA9IDQuNSwgdW5pdHMgPSAiaW4iKQpgYGAKClBsb3QgdGhlIGNoYW5nZSBpbiBmaXRuZXNzIGFjcm9zcyB0aGUgVE1QIGdyYWRpZW50IGZvciBib3RoIHNhbXBsZXM6CmBgYHtyIHdhcm5pbmc9RkFMU0V9CiMgUmVzaGFwZSB0aGUgZGF0YSBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQKU3RyZXBfZGlhbG91dF9sb25nIDwtIFN0cmVwX2RpYWxvdXQgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgiZml0RCIpLCAKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiVE1QIiwgCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJGaXRuZXNzIiwKICAgICAgICAgICAgICAgbmFtZXNfcHJlZml4ID0gImZpdEQiKSAlPiUKICBtdXRhdGUoRGF5ID0gZmFjdG9yKFRNUCwgbGV2ZWxzID0gYygiMDVEMDMiLCAiMDZEMDMiLCAiMDdEMDMiLCAiMDhEMDMiLCAiMDlEMDMiLCAiMTBEMDMiLCAiMTFEMDMiKSwKICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIjAiLCAiMC4wNTgiLCAiMC41IiwgIjEuMCIsICIxMCIsICI1MCIsICIyMDAiKSkpCgojIENhbGN1bGF0ZSB0aGUgZml0bmVzcyBhdCB0aGUgaGlnaGVzdCBUTVAgY29uY2VudHJhdGlvbiBmb3IgZWFjaCBtdXRJRApTdHJlcF9kaWFsb3V0X21heF9maXRuZXNzIDwtIFN0cmVwX2RpYWxvdXRfbG9uZyAlPiUKICBmaWx0ZXIoVE1QID09ICIxMUQwMyIpICU+JQogIHNlbGVjdChtdXRJRCwgbWF4X2ZpdG5lc3MgPSBGaXRuZXNzKQoKIyBKb2luIHRoaXMgaW5mb3JtYXRpb24gYmFjayB0byB0aGUgb3JpZ2luYWwgZGF0YSBhbmQgY29sb3IgYWNjb3JkaW5nbHkKU3RyZXBfZGlhbG91dF9sb25nIDwtIFN0cmVwX2RpYWxvdXRfbG9uZyAlPiUKICBsZWZ0X2pvaW4oU3RyZXBfZGlhbG91dF9tYXhfZml0bmVzcywgYnkgPSAibXV0SUQiKSAlPiUKICBtdXRhdGUoQ29sb3IgPSBjYXNlX3doZW4oCiAgICBtdXRJRCA9PSAiV1BfMDAyMjA1MzI3IiB+ICJXVCIsCiAgICBpcy5uYShtYXhfZml0bmVzcykgfiAiTXV0YW50X1Vua25vd24iLAogICAgbWF4X2ZpdG5lc3MgPCAtMSB+ICJNdXRhbnRfTG93IiwKICAgIG1heF9maXRuZXNzID49IC0xIH4gIk11dGFudF9IaWdoIgogICkpCgojIFJlb3JkZXIgdGhlIGxldmVscyBvZiBDb2xvciBmYWN0b3IgdG8gcGxvdCBXVCBsYXN0ClN0cmVwX2RpYWxvdXRfbG9uZyRDb2xvciA8LSBmYWN0b3IoU3RyZXBfZGlhbG91dF9sb25nJENvbG9yLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJNdXRhbnRfVW5rbm93biIsICJNdXRhbnRfTG93IiwgIk11dGFudF9IaWdoIiwgIldUIikpCgojIFNlcGFyYXRlIFdUIGFuZCBtdXRhbnQgZGF0YQpXVF9kYXRhIDwtIFN0cmVwX2RpYWxvdXRfbG9uZyAlPiUgZmlsdGVyKG11dElEID09ICJXUF8wMDIyMDUzMjciKQptdXRhbnRfZGF0YSA8LSBTdHJlcF9kaWFsb3V0X2xvbmcgJT4lIGZpbHRlcihtdXRJRCAhPSAiV1BfMDAyMjA1MzI3IikKYGBgCgpGaXRuZXNzIHBsb3R0aW5nIG92ZXIgdGhlIFRNUCBncmFkaWVudApgYGB7cn0KIyBGaXJzdCwgaW50ZXJwb2xhdGUgTkEgdmFsdWVzCm11dGFudF9kYXRhIDwtIG11dGFudF9kYXRhICU+JQogIGdyb3VwX2J5KG11dElEKSAlPiUKICBhcnJhbmdlKERheSkgJT4lCiAgbXV0YXRlKEZpdG5lc3MgPSBuYS5hcHByb3goRml0bmVzcywgeCA9IERheSwgbmEucm0gPSBGQUxTRSkpICU+JQogIHVuZ3JvdXAoKQoKV1RfZGF0YSA8LSBXVF9kYXRhICU+JQogIGFycmFuZ2UoRGF5KSAlPiUKICBtdXRhdGUoRml0bmVzcyA9IG5hLmFwcHJveChGaXRuZXNzLCB4ID0gRGF5LCBuYS5ybSA9IEZBTFNFKSkKCiMgVGhlbiBjcmVhdGUgdGhlIHBsb3QKU3RyZXBfZGlhbG91dF9wbG90IDwtIGdncGxvdCgpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtMSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZ3JheTUwIikgKwogICMgUGxvdCBtdXRhbnQgbGluZXMgZmlyc3QKICBnZW9tX2xpbmUoZGF0YSA9IG11dGFudF9kYXRhLCBhZXMoeCA9IERheSwgeSA9IEZpdG5lc3MsIGdyb3VwID0gbXV0SUQsIGNvbG9yID0gQ29sb3IpLCBzaXplID0gMS4wKSArCiAgZ2VvbV9wb2ludChkYXRhID0gbXV0YW50X2RhdGEsIGFlcyh4ID0gRGF5LCB5ID0gRml0bmVzcywgY29sb3IgPSBDb2xvciksIHNpemUgPSAzKSArCiAgIyBQbG90IFdUIGxpbmUgb24gdG9wCiAgZ2VvbV9saW5lKGRhdGEgPSBXVF9kYXRhLCBhZXMoeCA9IERheSwgeSA9IEZpdG5lc3MsIGdyb3VwID0gbXV0SUQpLCBjb2xvciA9ICJyZWQiLCBzaXplID0gMi4wKSArCiAgZ2VvbV9wb2ludChkYXRhID0gV1RfZGF0YSwgYWVzKHggPSBEYXksIHkgPSBGaXRuZXNzKSwgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDMpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiTXV0YW50X0xvdyIgPSAiZGFya2JsdWUiLCAiTXV0YW50X0hpZ2giID0gImdvbGQiLCAiTXV0YW50X1Vua25vd24iID0gImdyYXkiKSkgKwogIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gYygiMCIsICIwLjA1OCIsICIwLjUiLCAiMS4wIiwgIjEwIiwgIjUwIiwgIjIwMCIpKSArCiAgbGFicyh4ID0gIlRyaW1ldGhvcHJpbSAodWcvbUwpIiwKICAgICAgIHkgPSAiTWVkaWFuIEZpdG5lc3MgKExvZ0ZDKSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKAogICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICdibGFjaycsIHNpemUgPSAxLjApLAogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMS4wKSwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArIAogIGdndGl0bGUoIlN0cmVwdG9jb2NjdXMgcG5ldW1vbmlhZSIpICsgCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJpdGFsaWMiLCBzaXplID0gMTYpKQoKIyBQcmludCB0aGUgcGxvdApwcmludChTdHJlcF9kaWFsb3V0X3Bsb3QpCmBgYAoKCmBgYHtyIGVjaG89RkFMU0V9CiMgUE5HCmdnc2F2ZShmaWxlPSJSZXNpc3RhbmNlL1BMT1RTL0ZpbmFsL1N0cmVwLmRpYWxvdXQuZml0bmVzcy5wbmciLCAKICAgICAgIHBsb3Q9U3RyZXBfZGlhbG91dF9wbG90LAogICAgICAgZHBpPTYwMCwgd2lkdGggPSA0LjUsIGhlaWdodCA9IDQuNSwgdW5pdHMgPSAiaW4iKQpgYGAKClBsb3QgdG9nZXRoZXI6CmBgYHtyfQpwYXRjaDIzIDwtIFN0cmVwX2RpYWxvdXRfcGxvdCB8IFN0cmVwLmRpYWxvdXQuaGVhdG1hcApwYXRjaDIzCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KIyBQTkcKZ2dzYXZlKGZpbGU9IlJlc2lzdGFuY2UvUExPVFMvRmluYWwvU3RyZXAuRGlhbG91dC5Cb3RoLnBuZyIsIAogICAgICAgcGxvdD1wYXRjaDIzLAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMSwgaGVpZ2h0ID0gNSwgdW5pdHMgPSAiaW4iKQpgYGAKClBsb3QgYm90aCBwYXRob2dlbmljIHZhcmlhbnRzIHRvZ2V0aGVyOgpgYGB7cn0KcGF0Y2gyNCA8LSAoQmFjaWxsdXNfZGlhbG91dF9wbG90IHwgQmFjaWxsdXMuZGlhbG91dC5oZWF0bWFwKSAvIChTdHJlcF9kaWFsb3V0X3Bsb3QgfCBTdHJlcC5kaWFsb3V0LmhlYXRtYXApCnBhdGNoMjQKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIFBORwpnZ3NhdmUoZmlsZT0iUmVzaXN0YW5jZS9QTE9UUy9GaW5hbC9QYXRob2dlbmljLkJhY2lsbHVzLlN0cmVwdG9jb2NjdXMuRGlhbG91dC5Cb3RoLnBuZyIsIAogICAgICAgcGxvdD1wYXRjaDI0LAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMSwgaGVpZ2h0ID0gMTAsIHVuaXRzID0gImluIikKYGBgCgojIFJlcHJvZHVjaWJpbGl0eQoKVGhlIHNlc3Npb24gaW5mb3JtYXRpb24gaXMgcHJvdmlkZWQgZm9yIGZ1bGwgcmVwcm9kdWNpYmlsaXR5LgpgYGB7cn0KZGV2dG9vbHM6OnNlc3Npb25faW5mbygpCmBgYA==