R Notebook: Provides reproducible analysis for Perfect Homologs 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", "ggpubr", "ggridges", "ggtree", "ggtreeExtra", "glmnet", "gridExtra","igraph", "knitr", "matrixStats", "patchwork", "pheatmap", "purrr", "pscl", "RColorBrewer", "reshape","reshape2", "ROCR", "rstatix", "seqinr", "scales", "stringr", "stringi", "tidyr", "tidytree", "viridis")

# 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, ggpubr, ggridges, ggtree, ggtreeExtra, glmnet, gridExtra, igraph, knitr, matrixStats, patchwork, pheatmap, purrr, pscl, RColorBrewer, reshape, reshape2, ROCR, rstatix, seqinr, scales, stringr, stringi, tidyr, tidytree, viridis 

Import Data Files

Import MAPPING files generated from DHFR.1.Mapping.RMD relevant for downstream analysis.

### BCcontrols

# Lib15 (Codon 1)
BCcontrols_15_16_shared_median_WT <- read.csv("Count/count_files_formatted/BCcontrols_15_16_shared_median_WT.csv", 
                         header = TRUE, stringsAsFactors = FALSE)

# Lib16 (Codon 2)
BCcontrols_15_16_shared_median_Neg <- read.csv("Count/count_files_formatted/BCcontrols_15_16_shared_median_Neg.csv", 
                         header = TRUE, stringsAsFactors = FALSE)

### mutIDinfo

# mutIDinfo.15.16.zeros.shared
mutIDinfo.15.16.zeros.shared <- read.csv("Mapping/map_files_formatted/mutIDinfo.15.16.zeros.shared.csv", 
                         header = TRUE, stringsAsFactors = FALSE)

# mutIDinfo.15.16.zeros.unique
mutIDinfo.15.16.zeros.unique <- read.csv("Mapping/map_files_formatted/mutIDinfo.15.16.zeros.unique.csv", 
                         header = TRUE, stringsAsFactors = FALSE)

Import COUNTS files generated from DHFR.2.Counts.RMD relevant for downstream analysis.

### BCs_map

# Lib15 (Codon 1)
BCs15_map <- read.csv("Count/count_files_formatted/BCs15_map.csv", 
                         header = TRUE, stringsAsFactors = FALSE)

# Lib16 (Codon 2)
BCs16_map <- read.csv("Count/count_files_formatted/BCs16_map.csv", 
                         header = TRUE, stringsAsFactors = FALSE)

### mutIDinfo (mapping files that have been updated with count data)

# Lib15 (Codon 1)
mutIDinfo15 <- read.csv("Count/count_files_formatted/mutIDinfo15.csv", 
                         header = TRUE, stringsAsFactors = FALSE)

# Lib16 (Codon 2)
mutIDinfo16 <- read.csv("Count/count_files_formatted/mutIDinfo16.csv", 
                         header = TRUE, stringsAsFactors = FALSE)

Perfects Data Analysis

This section is based on the R file: “R_perfects_only.R”. It describes how to select only for perfects (perfect match to designed homolog sequence where mutations = 0).

Select Perfects

Next, begin by selecting perfects BCs from “BCs_map” object where mutations = 0:

# Lib15
BCperfect15 <- BCs15_map %>%
  filter(mutations==0) %>%
  dplyr::rename(ID=IDalign)

# Lib16
BCperfect16 <- BCs16_map %>%
  filter(mutations==0) %>%
  dplyr::rename(ID=IDalign)

Perform a quick check to verify no IDs are missing:

# Lib15
#check how many are missing ID
nrow(BCperfect15 %>% filter(ID == ""))
[1] 0
# Lib16
#check how many are missing ID
nrow(BCperfect16 %>% filter(ID == ""))
[1] 0

Perfects Stats

Synonymous Counts

Create a function to check if each unique perfect sequence represents a “true perfect” based on the designed homolog sequence or a “synonymous mutation” that is functionally redundant.

#check if this is a synonymous mutant
cigar_syn_mutant_check <- function(cigarin){
  flag_out <- "Perfect"
  if (length(grep("X",cigarin)) != 0) {
    flag_out <- "SynMutant"
  }
  return(flag_out)
}

Apply the “perfect check function” to the “BCperfect” dataset and add a column to indicate each BCs status.

# Lib15
#check if this BC is a synonymous mutant
BCperfect15 <- BCperfect15 %>%
  rowwise() %>%
  mutate(synflag=cigar_syn_mutant_check(cigar))

# Lib16
#check if this BC is a synonymous mutant
BCperfect16 <- BCperfect16 %>%
  rowwise() %>%
  mutate(synflag=cigar_syn_mutant_check(cigar))

Count the number of “Perfects” from the dataset:

# Lib15
# Number of perfects
perfects.count15 <- length(which(BCperfect15$synflag=="Perfect"))

format(perfects.count15, big.mark = ",")
[1] "69,114"
# Lib16
# Number of perfects
perfects.count16 <- length(which(BCperfect16$synflag=="Perfect"))

format(perfects.count16, big.mark = ",")
[1] "98,968"

Count the number of “SynMutants” from the dataset:

# Lib15
# Number of synonymous mutants
synmut.count15 <- length(which(BCperfect15$synflag=="SynMutant"))

format(synmut.count15, big.mark = ",")
[1] "3,838"
# Lib16
# Number of synonymous mutants
synmut.count16 <- length(which(BCperfect16$synflag=="SynMutant"))

format(synmut.count16, big.mark = ",")
[1] "8,335"

Perfects mutID

Create a perfects object that further filters the mutIDinfo object to retain mutIDs only if they have 0 mutations:

# Lib15
perfects15 <- mutIDinfo15 %>%
  filter(mutations==0) %>%
  dplyr::rename(ID = IDalign)

# Lib16
perfects16 <- mutIDinfo16 %>%
  filter(mutations==0) %>%
  dplyr::rename(ID = IDalign)

Perfects Filtered by BCs

Select only those perfects with a minimum number of barcode:

# Lib15

# Select only those with a minimum of 2 BCs
perfects15_2BCs <- perfects15 %>%
  filter(numprunedBCs > 1)

# Select only those with a minimum of 5 BCs
perfects15_5BCs <- perfects15 %>%
  filter(numprunedBCs > 4)

# Select only those with a minimum of 10 BCs
perfects15_10BCs <- perfects15 %>%
  filter(numprunedBCs > 9)

# Select only those with a minimum of 100 BCs
perfects15_100BCs <- perfects15 %>%
  filter(numprunedBCs > 99)

# Select only those with a minimum of 200 BCs
perfects15_200BCs <- perfects15 %>%
  filter(numprunedBCs > 199)

# Lib16

# Select only those with a minimum of 2 BCs
perfects16_2BCs <- perfects16 %>%
  filter(numprunedBCs > 1)

### Select only those with a minimum of 5 BCs
perfects16_5BCs <- perfects16 %>%
  filter(numprunedBCs > 4)

### Select only those with a minimum of 10 BCs
perfects16_10BCs <- perfects16 %>%
  filter(numprunedBCs > 9)

# Select only those with a minimum of 100 BCs
perfects16_100BCs <- perfects16 %>%
  filter(numprunedBCs > 99)

# Select only those with a minimum of 200 BCs
perfects16_200BCs <- perfects16 %>%
  filter(numprunedBCs > 199)

Perfects Counts by BCs

Count the number of perfects before and after filtering by minimum BC counts (2BCs and 5BCs):

# Lib15

# Count the number of unique perfects prior to filtering by BCs in Lib15
perfects15.count <- length(unique(perfects15$ID))
format(perfects15.count, big.mark = ",")
[1] "961"
# Count the number of unique perfects at 2BCs minimum retained in Lib15
perfects15_2BCs.count <- length(unique(perfects15_2BCs$ID))
format(perfects15_2BCs.count, big.mark = ",")
[1] "896"
# Count the number of unique perfects at 5BCs minimum retained in Lib15
perfects15_5BCs.count <- length(unique(perfects15_5BCs$ID))
format(perfects15_5BCs.count, big.mark = ",")
[1] "797"
# Count the number of unique perfects at 10BCs minimum retained in Lib15
perfects15_10BCs.count <- length(unique(perfects15_10BCs$ID))
format(perfects15_10BCs.count, big.mark = ",")
[1] "704"
# Count the number of unique perfects at 100BCs minimum retained in Lib15
perfects15_100BCs.count <- length(unique(perfects15_100BCs$ID))
format(perfects15_100BCs.count, big.mark = ",")
[1] "257"
# Count the number of unique perfects at 200BCs minimum retained in Lib15
perfects15_200BCs.count <- length(unique(perfects15_200BCs$ID))
format(perfects15_200BCs.count, big.mark = ",")
[1] "105"
# Lib16

# Count the number of unique perfects prior to filtering by BCs in Lib16
perfects16.count <- length(unique(perfects16$ID))
format(perfects16.count, big.mark = ",")
[1] "818"
# Count the number of unique perfects at 2BCs minimum retained in Lib16
perfects16_2BCs.count <- length(unique(perfects16_2BCs$ID))
format(perfects16_2BCs.count, big.mark = ",")
[1] "748"
# Count the number of unique perfects at 5BCs minimum retained in Lib16
perfects16_5BCs.count <- length(unique(perfects16_5BCs$ID))
format(perfects16_5BCs.count, big.mark = ",")
[1] "666"
# Count the number of unique perfects at 10BCs minimum retained in Lib16
perfects16_10BCs.count <- length(unique(perfects16_10BCs$ID))
format(perfects16_10BCs.count, big.mark = ",")
[1] "600"
# Count the number of unique perfects at 100BCs minimum retained in Lib15
perfects16_100BCs.count <- length(unique(perfects16_100BCs$ID))
format(perfects16_100BCs.count, big.mark = ",")
[1] "293"
# Count the number of unique perfects at 200BCs minimum retained in Lib15
perfects16_200BCs.count <- length(unique(perfects16_200BCs$ID))
format(perfects16_200BCs.count, big.mark = ",")
[1] "180"

Perfects Filtered by Fitness

Filter the Perfects datasets to retain only those with good fitness scores for complementation (>=-1) and those with poor fitness in complementation (<-1). The good fitness Perfects will be used for downstream complementation and TMP resistance analyses, while the poor fitness Perfects will be used for gain-of-function (GOF) analysis.

Good Perfects: Select only those perfects with a minimum fitness value >=-1 for each BC level:

# Lib15

# Select only those with a minimum of 1 BC
perfects15_good <- perfects15 %>%
  filter(fitD05D03 >= -1)

# Select only those with a minimum of 2 BCs
perfects15_2BCs_good <- perfects15_2BCs %>%
  filter(fitD05D03 >= -1)

# Select only those with a minimum of 5 BCs
perfects15_5BCs_good <- perfects15_5BCs %>%
  filter(fitD05D03 >= -1)

# Select only those with a minimum of 10 BCs
perfects15_10BCs_good <- perfects15_10BCs %>%
  filter(fitD05D03 >= -1)

# Select only those with a minimum of 100 BCs
perfects15_100BCs_good <- perfects15_100BCs %>%
  filter(fitD05D03 >= -1)

# Select only those with a minimum of 200 BCs
perfects15_200BCs_good <- perfects15_200BCs %>%
  filter(fitD05D03 >= -1)

# Lib16

# Select only those with a minimum of 1 BCs
perfects16_good <- perfects16 %>%
  filter(fitD12D04 >= -1)

# Select only those with a minimum of 2 BCs
perfects16_2BCs_good <- perfects16_2BCs %>%
  filter(fitD12D04 >= -1)

### Select only those with a minimum of 5 BCs
perfects16_5BCs_good <- perfects16_5BCs %>%
  filter(fitD12D04 >= -1)

### Select only those with a minimum of 10 BCs
perfects16_10BCs_good <- perfects16_10BCs %>%
  filter(fitD12D04 >= -1)

# Select only those with a minimum of 100 BCs
perfects16_100BCs_good <- perfects16_100BCs %>%
  filter(fitD12D04 >= -1)

# Select only those with a minimum of 200 BCs
perfects16_200BCs_good <- perfects16_200BCs %>%
  filter(fitD12D04 >= -1)

Perfects Count by Fitness

Count the number of perfects before and after filtering by minimum fitness (fitness >= -1):

# Lib15

# Count the number of unique perfects prior to filtering by BCs in Lib15
perfects15.good.count <- length(unique(perfects15_good$ID))
format(perfects15.good.count, big.mark = ",")
[1] "489"
# Determine Minimum fitness perfect prior to filtering by BCs in Lib15
perfects15.good.min <- min(perfects15_good$fitD05D03)
format(perfects15.good.min, big.mark = ",")
[1] "-0.9993416"
# Determine Maximum fitness perfect prior to filtering by BCs in Lib15
perfects15.good.max <- max(perfects15_good$fitD05D03)
format(perfects15.good.max, big.mark = ",")
[1] "1.856875"
# Filtered by BCs

# Count the number of unique perfects at 2BCs minimum retained in Lib15
perfects15_2BCs.good.count <- length(unique(perfects15_2BCs_good$ID))
format(perfects15_2BCs.good.count, big.mark = ",")
[1] "459"
# Count the number of unique perfects at 5BCs minimum retained in Lib15
perfects15_5BCs.good.count <- length(unique(perfects15_5BCs_good$ID))
format(perfects15_5BCs.good.count, big.mark = ",")
[1] "416"
# Count the number of unique perfects at 10BCs minimum retained in Lib15
perfects15_10BCs.good.count <- length(unique(perfects15_10BCs_good$ID))
format(perfects15_10BCs.good.count, big.mark = ",")
[1] "371"
# Count the number of unique perfects at 100BCs minimum retained in Lib15
perfects15_100BCs.good.count <- length(unique(perfects15_100BCs_good$ID))
format(perfects15_100BCs.good.count, big.mark = ",")
[1] "144"
# Count the number of unique perfects at 200BCs minimum retained in Lib15
perfects15_200BCs.good.count <- length(unique(perfects15_200BCs_good$ID))
format(perfects15_200BCs.good.count, big.mark = ",")
[1] "54"
# Lib16

# Count the number of unique perfects prior to filtering by BCs in Lib16
perfects16.good.count <- length(unique(perfects16_good$ID))
format(perfects16.good.count, big.mark = ",")
[1] "447"
# Determine Minimum fitness perfect prior to filtering by BCs in Lib16
perfects16.good.min <- min(perfects16_good$fitD12D04)
format(perfects16.good.min, big.mark = ",")
[1] "-0.9948904"
# Determine Maximum fitness perfect prior to filtering by BCs in Lib16
perfects16.good.max <- max(perfects16_good$fitD12D04)
format(perfects16.good.max, big.mark = ",")
[1] "1.298934"
# Filtered by BCs

# Count the number of unique perfects at 2BCs minimum retained in Lib16
perfects16_2BCs.good.count <- length(unique(perfects16_2BCs_good$ID))
format(perfects16_2BCs.good.count, big.mark = ",")
[1] "416"
# Count the number of unique perfects at 5BCs minimum retained in Lib16
perfects16_5BCs.good.count <- length(unique(perfects16_5BCs_good$ID))
format(perfects16_5BCs.good.count, big.mark = ",")
[1] "377"
# Count the number of unique perfects at 10BCs minimum retained in Lib16
perfects16_10BCs.good.count <- length(unique(perfects16_10BCs_good$ID))
format(perfects16_10BCs.good.count, big.mark = ",")
[1] "343"
# Count the number of unique perfects at 100BCs minimum retained in Lib16
perfects16_100BCs.good.count <- length(unique(perfects16_100BCs_good$ID))
format(perfects16_100BCs.good.count, big.mark = ",")
[1] "176"
# Count the number of unique perfects at 200BCs minimum retained in Lib16
perfects16_200BCs.good.count <- length(unique(perfects16_200BCs_good$ID))
format(perfects16_200BCs.good.count, big.mark = ",")
[1] "108"

Merged Perfects

Merged by BCs

Merge perfects filtered at 1BC derived from each library into a single dataframe based on shared ID between the two dataset

# Merge by shared "mutID" (1BC)
perfects_15_16_shared <- merge(perfects15, perfects16, by = "mutID", all = FALSE)

# Merge by shared "mutID" (2BC)
perfects_15_16_shared_2BCs <- merge(perfects15_2BCs, perfects16_2BCs, by = "mutID", all = FALSE)

# Merge by shared "mutID" (5BC)
perfects_15_16_shared_5BCs <- merge(perfects15_5BCs, perfects16_5BCs, by = "mutID", all = FALSE)

# Merge by shared "mutID" (10BC)
perfects_15_16_shared_10BCs <- merge(perfects15_10BCs, perfects16_10BCs, by = "mutID", all = FALSE)

# Merge by shared "mutID" (100BC)
perfects_15_16_shared_100BCs <- merge(perfects15_100BCs, perfects16_100BCs, by = "mutID", all = FALSE)

# Merge by shared "mutID" (200BC)
perfects_15_16_shared_200BCs <- merge(perfects15_200BCs, perfects16_200BCs, by = "mutID", all = FALSE)

Count the number of perfects shared between both libraries.

# Count the number of unique perfects shared between libraries (1BC)
perfects_15_16_shared.count <- length(unique(perfects_15_16_shared$mutID))
format(perfects_15_16_shared.count, big.mark = ",")
[1] "643"
# Count the number of unique perfects shared between libraries (2BC)
perfects_15_16_shared_2BCs.count <- length(unique(perfects_15_16_shared_2BCs$mutID))
format(perfects_15_16_shared_2BCs.count, big.mark = ",")
[1] "579"
# Count the number of unique perfects shared between libraries (5BC)
perfects_15_16_shared_5BCs.count <- length(unique(perfects_15_16_shared_5BCs$mutID))
format(perfects_15_16_shared_5BCs.count, big.mark = ",")
[1] "493"
# Count the number of unique perfects shared between libraries (10BC)
perfects_15_16_shared_10BCs.count <- length(unique(perfects_15_16_shared_10BCs$mutID))
format(perfects_15_16_shared_10BCs.count, big.mark = ",")
[1] "419"
# Count the number of unique perfects shared between libraries (100BC)
perfects_15_16_shared_100BCs.count <- length(unique(perfects_15_16_shared_100BCs$mutID))
format(perfects_15_16_shared_100BCs.count, big.mark = ",")
[1] "115"
# Count the number of unique perfects shared between libraries (200BC)
perfects_15_16_shared_200BCs.count <- length(unique(perfects_15_16_shared_200BCs$mutID))
format(perfects_15_16_shared_200BCs.count, big.mark = ",")
[1] "38"

Count the number of perfects unique to one library or the other.

# Create a new dataset retaining unique values from both datasets (1BC)
perfects_15_16_unique <- bind_rows(
  anti_join(perfects15, perfects16, by = "mutID"),
  anti_join(perfects16, perfects15, by = "mutID"))

# Count the number of perfects unique to one library or the other
perfects_15_16_unique.count <- length(unique(perfects_15_16_unique$mutID))
format(perfects_15_16_unique.count, big.mark = ",")
[1] "493"
# Create a new dataset retaining unique values from both datasets (2BC)
perfects_15_16_unique_2BCs <- bind_rows(
  anti_join(perfects15_2BCs, perfects16_2BCs, by = "mutID"),
  anti_join(perfects16_2BCs, perfects15_2BCs, by = "mutID"))

# Count the number of perfects unique to one library or the other
perfects_15_16_unique_2BCs.count <- length(unique(perfects_15_16_unique_2BCs$mutID))
format(perfects_15_16_unique_2BCs.count, big.mark = ",")
[1] "486"
# Create a new dataset retaining unique values from both datasets (5BC)
perfects_15_16_unique_5BCs <- bind_rows(
  anti_join(perfects15_5BCs, perfects16_5BCs, by = "mutID"),
  anti_join(perfects16_5BCs, perfects15_5BCs, by = "mutID"))

# Count the number of perfects unique to one library or the other
perfects_15_16_unique_5BCs.count <- length(unique(perfects_15_16_unique_5BCs$mutID))
format(perfects_15_16_unique_5BCs.count, big.mark = ",")
[1] "477"
# Create a new dataset retaining unique values from both datasets (10BC)
perfects_15_16_unique_10BCs <- bind_rows(
  anti_join(perfects15_10BCs, perfects16_10BCs, by = "mutID"),
  anti_join(perfects16_10BCs, perfects15_10BCs, by = "mutID"))

# Count the number of perfects unique to one library or the other
perfects_15_16_unique_10BCs.count <- length(unique(perfects_15_16_unique_10BCs$mutID))
format(perfects_15_16_unique_10BCs.count, big.mark = ",")
[1] "466"
# Create a new dataset retaining unique values from both datasets (100BC)
perfects_15_16_unique_100BCs <- bind_rows(
  anti_join(perfects15_100BCs, perfects16_100BCs, by = "mutID"),
  anti_join(perfects16_100BCs, perfects15_100BCs, by = "mutID"))

# Count the number of perfects unique to one library or the other
perfects_15_16_unique_100BCs.count <- length(unique(perfects_15_16_unique_100BCs$mutID))
format(perfects_15_16_unique_100BCs.count, big.mark = ",")
[1] "320"
# Create a new dataset retaining unique values from both datasets (200BC)
perfects_15_16_unique_200BCs <- bind_rows(
  anti_join(perfects15_200BCs, perfects16_200BCs, by = "mutID"),
  anti_join(perfects16_200BCs, perfects15_200BCs, by = "mutID"))

# Count the number of perfects unique to one library or the other
perfects_15_16_unique_200BCs.count <- length(unique(perfects_15_16_unique_200BCs$mutID))
format(perfects_15_16_unique_200BCs.count, big.mark = ",")
[1] "209"

Summarize the number of perfects shared or unique between both libraries based on minimum BC count.

# Count number of shared and unique perfects (1BC)
perfects_15_16_all.count <- sum(perfects_15_16_shared.count + perfects_15_16_unique.count)
format(perfects_15_16_all.count, big.mark = ",")
[1] "1,136"
# Count number of shared and unique perfects (2BC)
perfects_15_16_all_2BCs.count <- sum(perfects_15_16_shared_2BCs.count + perfects_15_16_unique_2BCs.count)
format(perfects_15_16_all_2BCs.count, big.mark = ",")
[1] "1,065"
# Count number of shared and unique perfects (5BC)
perfects_15_16_all_5BCs.count <- sum(perfects_15_16_shared_5BCs.count + perfects_15_16_unique_5BCs.count)
format(perfects_15_16_all_5BCs.count, big.mark = ",")
[1] "970"
# Count number of shared and unique perfects (10BC)
perfects_15_16_all_10BCs.count <- sum(perfects_15_16_shared_10BCs.count + perfects_15_16_unique_10BCs.count)
format(perfects_15_16_all_10BCs.count, big.mark = ",")
[1] "885"
# Count number of shared and unique perfects (100BC)
perfects_15_16_all_100BCs.count <- sum(perfects_15_16_shared_100BCs.count + perfects_15_16_unique_100BCs.count)
format(perfects_15_16_all_100BCs.count, big.mark = ",")
[1] "435"
# Count number of shared and unique perfects (200BC)
perfects_15_16_all_200BCs.count <- sum(perfects_15_16_shared_200BCs.count + perfects_15_16_unique_200BCs.count)
format(perfects_15_16_all_200BCs.count, big.mark = ",")
[1] "247"

Merged by Fitness

Merge perfects filtered by minimum fitness at complementation (>-1) based on shared ID between both libraries:

# Merge by shared "mutID" (1BC)
perfects_15_16_shared_good <- merge(perfects15_good, perfects16_good, by = "mutID", all = FALSE)

# Merge by shared "mutID" (2BC)
perfects_15_16_shared_2BCs_good <- merge(perfects15_2BCs_good, perfects16_2BCs_good, by = "mutID", all = FALSE)

# Merge by shared "mutID" (5BC)
perfects_15_16_shared_5BCs_good <- merge(perfects15_5BCs_good, perfects16_5BCs_good, by = "mutID", all = FALSE)

Count the number of perfects shared between both libraries.

# Count the number of unique perfects shared between libraries (1BC)
perfects_15_16_shared.good.count <- length(unique(perfects_15_16_shared_good$mutID))
format(perfects_15_16_shared.good.count, big.mark = ",")
[1] "236"
# Count the number of unique perfects shared between libraries (2BC)
perfects_15_16_shared_2BCs.good.count <- length(unique(perfects_15_16_shared_2BCs_good$mutID))
format(perfects_15_16_shared_2BCs.good.count, big.mark = ",")
[1] "217"
# Count the number of unique perfects shared between libraries (5BC)
perfects_15_16_shared_5BCs.good.count <- length(unique(perfects_15_16_shared_5BCs_good$mutID))
format(perfects_15_16_shared_5BCs.good.count, big.mark = ",")
[1] "194"

Count the number of perfects unique to one library or the other.

# Create a new dataset retaining unique values from both datasets (1BC)
perfects_15_16_unique_good <- bind_rows(
  anti_join(perfects15_good, perfects16_good, by = "mutID"),
  anti_join(perfects16_good, perfects15_good, by = "mutID"))

# Count the number of perfects unique to one library or the other
perfects_15_16_unique.good.count <- length(unique(perfects_15_16_unique_good$mutID))
format(perfects_15_16_unique.good.count, big.mark = ",")
[1] "464"
# Create a new dataset retaining unique values from both datasets (2BC)
perfects_15_16_unique_2BCs_good <- bind_rows(
  anti_join(perfects15_2BCs_good, perfects16_2BCs_good, by = "mutID"),
  anti_join(perfects16_2BCs_good, perfects15_2BCs_good, by = "mutID"))

# Count the number of perfects unique to one library or the other
perfects_15_16_unique_2BCs.good.count <- length(unique(perfects_15_16_unique_2BCs_good$mutID))
format(perfects_15_16_unique_2BCs.good.count, big.mark = ",")
[1] "441"
# Create a new dataset retaining unique values from both datasets (5BC)
perfects_15_16_unique_5BCs_good <- bind_rows(
  anti_join(perfects15_5BCs_good, perfects16_5BCs_good, by = "mutID"),
  anti_join(perfects16_5BCs_good, perfects15_5BCs_good, by = "mutID"))

# Count the number of perfects unique to one library or the other
perfects_15_16_unique_5BCs.good.count <- length(unique(perfects_15_16_unique_5BCs_good$mutID))
format(perfects_15_16_unique_5BCs.good.count, big.mark = ",")
[1] "405"

Summarize the number of perfects shared or unique between both libraries based on minimum BC count.

# Count number of shared and unique perfects (1BC)
perfects_15_16_all.good.count <- sum(perfects_15_16_shared.good.count + perfects_15_16_unique.good.count)
format(perfects_15_16_all.good.count, big.mark = ",")
[1] "700"
# Count number of shared and unique perfects (2BC)
perfects_15_16_all_2BCs.good.count <- sum(perfects_15_16_shared_2BCs.good.count + perfects_15_16_unique_2BCs.good.count)
format(perfects_15_16_all_2BCs.good.count, big.mark = ",")
[1] "658"
# Count number of shared and unique perfects (5BC)
perfects_15_16_all_5BCs.good.count <- sum(perfects_15_16_shared_5BCs.good.count + perfects_15_16_unique_5BCs.good.count)
format(perfects_15_16_all_5BCs.good.count, big.mark = ",")
[1] "599"

Correlations

Codon 1 vs. Codon 2

Subset for Plotting

Because many of the shared mutIDs have “NA” values for one library or the other at certain treatment conditions, we need to subset the dataframe into smaller datasets relevant for each condition we want to plot and remove rows containing “NA” values. All correlations are based on the “BCs.15.16.mutID.fitness.perfects.shared” dataset.

Subset relevant data columns and remove rows containing “NA” values:

# Complementation - 0-TMP
L15.L16.Counts.0.TMP <- perfects_15_16_shared_5BCs[, c("mutID", "numprunedBCs.x", "numprunedBCs.y", "fitD05D03", "fitD12D04")] %>%
  na.omit(L15.L16.Counts.0.TMP)

# Trimethoprim - 0.058-TMP
L15.L16.Counts.0.058.TMP <- perfects_15_16_shared_5BCs_good[, c("mutID", "numprunedBCs.x", "numprunedBCs.y", "fitD06D03", "fitE01D04")] %>%
  na.omit(L15.L16.Counts.0.058.TMP)

# Trimethoprim - 0.5-TMP
L15.L16.Counts.0.5.TMP <- perfects_15_16_shared_5BCs_good[, c("mutID", "numprunedBCs.x", "numprunedBCs.y", "fitD07D03", "fitE02D04")] %>%
  na.omit(L15.L16.Counts.0.5.TMP)

# Trimethoprim - 1.0-TMP
L15.L16.Counts.1.0.TMP <- perfects_15_16_shared_5BCs_good[, c("mutID", "numprunedBCs.x", "numprunedBCs.y", "fitD08D03", "fitE03D04")] %>%
  na.omit(L15.L16.Counts.1.0.TMP)

# Trimethoprim - 10-TMP
L15.L16.Counts.10.TMP <- perfects_15_16_shared_5BCs_good[, c("mutID", "numprunedBCs.x", "numprunedBCs.y", "fitD09D03", "fitE04D04")] %>%
  na.omit(L15.L16.Counts.10.TMP)

# Trimethoprim - 50-TMP
L15.L16.Counts.50.TMP <- perfects_15_16_shared_5BCs_good[, c("mutID", "numprunedBCs.x", "numprunedBCs.y", "fitD10D03", "fitE05D04")] %>%
  na.omit(L15.L16.Counts.50.TMP)

# Trimethoprim - 200-TMP
L15.L16.Counts.200.TMP <- perfects_15_16_shared_5BCs_good[, c("mutID", "numprunedBCs.x", "numprunedBCs.y", "fitD11D03", "fitE06D04")]%>%
  na.omit(L15.L16.Counts.200.TMP)

Pearson Correlations

The following section calculates Pearson’s correlations based on the fitness scores of unique mutIDs shared between Lib15 and Lib16 for Time Point 1.

Pearson’s Correlation: Determine correlations using fitness values from Libraries 15 and 16 at Time Point 1:

# Pearson's Correlation: 0-TMP (Complementation)
cor_test_Counts_shared_0_tmp <- cor.test(L15.L16.Counts.0.TMP$fitD05D03,
                                         L15.L16.Counts.0.TMP$fitD12D04)

# Pearson's Correlation: 0.058-TMP
cor_test_Counts_shared_0.058_tmp <- cor.test(L15.L16.Counts.0.058.TMP$fitD06D03,
                                             L15.L16.Counts.0.058.TMP$fitE01D04)

# Pearson's Correlation: 0.5-TMP
cor_test_Counts_shared_0.5_tmp <- cor.test(L15.L16.Counts.0.5.TMP$fitD07D03,
                                           L15.L16.Counts.0.5.TMP$fitE02D04)

# Pearson's Correlation: 1.0-TMP
cor_test_Counts_shared_1.0_tmp <- cor.test(L15.L16.Counts.1.0.TMP$fitD08D03,
                                           L15.L16.Counts.1.0.TMP$fitE03D04)

# Pearson's Correlation: 10-TMP
cor_test_Counts_shared_10_tmp <- cor.test(L15.L16.Counts.10.TMP$fitD09D03,
                                          L15.L16.Counts.10.TMP$fitE04D04)

# Pearson's Correlation: 50-TMP
cor_test_Counts_shared_50_tmp <- cor.test(L15.L16.Counts.50.TMP$fitD10D03,
                                          L15.L16.Counts.50.TMP$fitE05D04)

# Pearson's Correlation: 200-TMP
cor_test_Counts_shared_200_tmp <- cor.test(L15.L16.Counts.200.TMP$fitD11D03,
                                           L15.L16.Counts.200.TMP$fitE06D04)
# Print the full statistical output:
print(cor_test_Counts_shared_0_tmp)

    Pearson's product-moment correlation

data:  L15.L16.Counts.0.TMP$fitD05D03 and L15.L16.Counts.0.TMP$fitD12D04
t = 11.66, df = 483, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.3961813 0.5353816
sample estimates:
      cor 
0.4686859 
print(cor_test_Counts_shared_0.058_tmp)

    Pearson's product-moment correlation

data:  L15.L16.Counts.0.058.TMP$fitD06D03 and L15.L16.Counts.0.058.TMP$fitE01D04
t = 20.713, df = 189, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.7839299 0.8720023
sample estimates:
      cor 
0.8331753 
print(cor_test_Counts_shared_0.5_tmp)

    Pearson's product-moment correlation

data:  L15.L16.Counts.0.5.TMP$fitD07D03 and L15.L16.Counts.0.5.TMP$fitE02D04
t = 23.064, df = 188, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.8172151 0.8926925
sample estimates:
      cor 
0.8595716 
print(cor_test_Counts_shared_1.0_tmp)

    Pearson's product-moment correlation

data:  L15.L16.Counts.1.0.TMP$fitD08D03 and L15.L16.Counts.1.0.TMP$fitE03D04
t = 21.535, df = 188, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.7968731 0.8801966
sample estimates:
      cor 
0.8435376 
print(cor_test_Counts_shared_10_tmp)

    Pearson's product-moment correlation

data:  L15.L16.Counts.10.TMP$fitD09D03 and L15.L16.Counts.10.TMP$fitE04D04
t = 19.153, df = 187, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.7593228 0.8570267
sample estimates:
      cor 
0.8138494 
print(cor_test_Counts_shared_50_tmp)

    Pearson's product-moment correlation

data:  L15.L16.Counts.50.TMP$fitD10D03 and L15.L16.Counts.50.TMP$fitE05D04
t = 13.155, df = 187, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.6109957 0.7607242
sample estimates:
      cor 
0.6932678 
print(cor_test_Counts_shared_200_tmp)

    Pearson's product-moment correlation

data:  L15.L16.Counts.200.TMP$fitD11D03 and L15.L16.Counts.200.TMP$fitE06D04
t = 10.781, df = 172, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.5370002 0.7161493
sample estimates:
      cor 
0.6350373 

Correlation Plots

Plot median fitness correlations between Lib15 & Lib16 (perfects) for Complementation:

# Extract correlation value from cor_value_shared object
cor_value_shared_0_tmp <- cor_test_Counts_shared_0_tmp$estimate

# Format p-value in scientific notation
p_value_scientific_shared_0_tmp <- format(cor_test_Counts_shared_0_tmp$p.value, scientific = TRUE, digits = 4)

# Extract number of rows
num_rows.counts.0.tmp <- nrow(L15.L16.Counts.0.TMP)

# Plot based on shared mutID
Lib15_16_0_TMP <- ggplot(L15.L16.Counts.0.TMP, 
                   aes(x=fitD05D03, y=fitD12D04)) +
  labs(x = "Codon 1 Median Fitness (LogFC) \n(0 μg/mL tmp)",
       y ="Codon 2 Median Fitness (LogFC) \n(0 μg/mL tmp)") +
  geom_smooth(method=lm,colour="black") +
  geom_density2d(colour="black",alpha=0.2) +
  geom_point(aes(color = case_when(
    fitD05D03 >= -1 & fitD12D04 >= -1 ~ "lightblue4",
    fitD05D03 >= -1 & fitD12D04 < -1 ~ "#0072B2",
    fitD12D04 >= -1 & fitD05D03 < -1 ~ "#E69F00",
    TRUE ~ "black"
  ),
  fill = case_when(
    fitD05D03 >= -1 & fitD12D04 >= -1 ~ "lightblue4",
    fitD05D03 >= -1 & fitD12D04 < -1 ~ "#0072B2",
    fitD12D04 >= -1 & fitD05D03 < -1 ~ "#E69F00",
    TRUE ~ "white"
  ),
  shape = case_when(
    fitD05D03 >= -1 & fitD12D04 >= -1 ~ 16,
    fitD05D03 >= -1 & fitD12D04 < -1 ~ 16,
    fitD12D04 >= -1 & fitD05D03 < -1 ~ 16,
    TRUE ~ 21
  )), 
  alpha = 0.75, size = 2.5) +
scale_shape_identity() +
scale_color_identity() +
scale_fill_identity() +
  # Add a new point for WT E. coli median fitness
  geom_point(data = BCcontrols_15_16_shared_median_WT, 
             aes(x = fitD05D03, y = fitD12D04), 
             fill = "red", color = "black", size = 4, shape = 24) +
  # Add a new point for Neg Ctrl (D27N, mCherry) median fitness
  geom_point(data = BCcontrols_15_16_shared_median_Neg, 
             aes(x = fitD05D03, y = fitD12D04), 
             color = "black", size = 5, shape = 18) +
  theme(legend.position="none") +
  theme(axis.text.x = element_text(size = 12),
        axis.text.y = element_text(size = 12),
        axis.title.x = element_text(size = 14),
        axis.title.y = element_text(size = 14),
        axis.line = element_line(colour = 'black', size = 1.0),
        axis.ticks = element_line(colour = "black", size = 1.0),
        panel.background = element_blank()) + #,
        #panel.border = element_rect(colour = "black", fill=NA, size=1.0)) +
  annotate("text",
           x = max(L15.L16.Counts.0.TMP$fitD05D03),
           y = min(L15.L16.Counts.0.TMP$fitD12D04), 
           label = paste("p-value =", p_value_scientific_shared_0_tmp), hjust = 1, vjust = 0) +
  annotate("text",
           x = max(L15.L16.Counts.0.TMP$fitD05D03),
           y = min(L15.L16.Counts.0.TMP$fitD12D04),
            label = paste("Correlation =", round(cor_value_shared_0_tmp, 2)), hjust = 1, vjust = -1.5) +
  annotate("text",
           x = min(L15.L16.Counts.0.TMP$fitD05D03),
           y = max(L15.L16.Counts.0.TMP$fitD12D04),
           label = paste("Shared Assemblies =", num_rows.counts.0.tmp), hjust = 0, vjust = 1.5) +
  scale_x_continuous(breaks = seq(floor(min(L15.L16.Counts.0.TMP$fitD05D03)), 
                                  ceiling(max(L15.L16.Counts.0.TMP$fitD05D03)), by = 1)) +
  scale_y_continuous(breaks = seq(floor(min(L15.L16.Counts.0.TMP$fitD12D04)), 
                                  ceiling(max(L15.L16.Counts.0.TMP$fitD12D04)), by = 1))
Warning: The `size` argument of `element_line()` is deprecated as of ggplot2 3.4.0.
Please use the `linewidth` argument instead.
Lib15_16_0_TMP_p01 <- ggMarginal(Lib15_16_0_TMP, type = "histogram", fill = "lightblue4", alpha = 0.5)
`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'
Lib15_16_0_TMP_p01

Count Complementing Homologs Unique to Codon Version: Count the number of unique homologs capable of complementation in both codon versions and in one codon version or the other.

library(dplyr)

# Count unique mutIDs for each condition
counts <- L15.L16.Counts.0.TMP %>%
  summarise(
    both_ge_neg1 = n_distinct(mutID[fitD05D03 >= -1 & fitD12D04 >= -1]),
    D05D03_ge_neg1_D12D04_lt_neg1 = n_distinct(mutID[fitD05D03 >= -1 & fitD12D04 < -1]),
    D12D04_ge_neg1_D05D03_lt_neg1 = n_distinct(mutID[fitD12D04 >= -1 & fitD05D03 < -1]),
    both_lt_neg1 = n_distinct(mutID[fitD05D03 < -1 & fitD12D04 < -1])
  )

# Print the results
print("Number of unique mutIDs where:")
[1] "Number of unique mutIDs where:"
print(paste("1. Both fitD05D03 and fitD12D04 are >= -1:", counts$both_ge_neg1))
[1] "1. Both fitD05D03 and fitD12D04 are >= -1: 194"
print(paste("2. fitD05D03 is >= -1 but fitD12D04 is < -1:", counts$D05D03_ge_neg1_D12D04_lt_neg1))
[1] "2. fitD05D03 is >= -1 but fitD12D04 is < -1: 77"
print(paste("3. fitD12D04 is >= -1 but fitD05D03 is < -1:", counts$D12D04_ge_neg1_D05D03_lt_neg1))
[1] "3. fitD12D04 is >= -1 but fitD05D03 is < -1: 86"
print(paste("4. Both fitD05D03 and fitD12D04 are < -1:", counts$both_lt_neg1))
[1] "4. Both fitD05D03 and fitD12D04 are < -1: 128"
# Optional: Calculate total number of unique mutIDs
total_unique_mutIDs <- n_distinct(L15.L16.Counts.0.TMP$mutID)
print(paste("Total number of unique mutIDs:", total_unique_mutIDs))
[1] "Total number of unique mutIDs: 485"

Plot shared fitness correlations between Lib15 & Lib16 (perfects) for 0.058 TMP:

# Extract correlation value from cor_value_shared object
cor_value_shared_0.058_tmp <- cor_test_Counts_shared_0.058_tmp$estimate

# Format p-value in scientific notation
p_value_scientific_shared_0.058_tmp <- format(cor_test_Counts_shared_0.058_tmp$p.value, scientific = TRUE, digits = 4)

# Extract number of rows
num_rows.counts.0.058.tmp <- nrow(L15.L16.Counts.0.058.TMP)

# Plot based on shared mutID
Lib15_16_0.058_TMP <- ggplot(L15.L16.Counts.0.058.TMP, 
                   aes(x=fitD06D03, y=fitE01D04)) +
  labs(x = "Codon 1 Median Fitness (LogFC) \n(0.058 μg/mL tmp)", y ="Codon 2 Median Fitness (LogFC) \n(0.058 μg/mL tmp)") +
  geom_smooth(method=lm,colour="black") +
  geom_density2d(colour="black",alpha=0.2) +
  geom_point(aes(color = case_when(
    fitD06D03 >= -1 & fitE01D04 >= -1 ~ "lightblue4",
    fitD06D03 >= -1 & fitE01D04 < -1 ~ "#0072B2",
    fitE01D04 >= -1 & fitD06D03 < -1 ~ "#E69F00",
    TRUE ~ "black"
  ),
  fill = case_when(
    fitD06D03 >= -1 & fitE01D04 >= -1 ~ "lightblue4",
    fitD06D03 >= -1 & fitE01D04 < -1 ~ "#0072B2",
    fitE01D04 >= -1 & fitD06D03 < -1 ~ "#E69F00",
    TRUE ~ "white"
  ),
  shape = case_when(
    fitD06D03 >= -1 & fitE01D04 >= -1 ~ 16,
    fitD06D03 >= -1 & fitE01D04 < -1 ~ 16,
    fitE01D04 >= -1 & fitD06D03 < -1 ~ 16,
    TRUE ~ 21
  )), 
  alpha = 0.75, size = 4) +
scale_shape_identity() +
scale_color_identity() +
scale_fill_identity() +
  # Add a new point for WT E. coli median fitness
  geom_point(data = BCcontrols_15_16_shared_median_WT, 
             aes(x = fitD06D03, y = fitE01D04), 
             fill = "red", color = "black", size = 3, shape = 24) +
  # Add a new point for Neg Ctrl (D27N, mCherry) median fitness
  geom_point(data = BCcontrols_15_16_shared_median_Neg, 
             aes(x = fitD06D03, y = fitE01D04), 
             color = "black", size = 5, shape = 18) +
  theme(legend.position="none") +
  theme(axis.text.x = element_text(size = 14),
        axis.text.y = element_text(size = 14),
        axis.title.x = element_text(size = 16),
        axis.title.y = element_text(size = 16),
        axis.line = element_line(colour = 'black', size = 1.0),
        axis.ticks = element_line(colour = "black", size = 1.0),
        panel.background = element_blank()) + #,
        #panel.border = element_rect(colour = "black", fill=NA, size=1.0)) +
  annotate("text",
           x = max(L15.L16.Counts.0.058.TMP$fitD06D03),
           y = min(L15.L16.Counts.0.058.TMP$fitE01D04), 
           label = paste("p-value =", p_value_scientific_shared_0.058_tmp), hjust = 1, vjust = 0) +
  annotate("text",
           x = max(L15.L16.Counts.0.058.TMP$fitD06D03),
           y = min(L15.L16.Counts.0.058.TMP$fitE01D04),
            label = paste("Correlation =", round(cor_value_shared_0.058_tmp, 2)), hjust = 1, vjust = -1.5) +
  annotate("text",
           x = min(L15.L16.Counts.0.058.TMP$fitD06D03),
           y = max(L15.L16.Counts.0.058.TMP$fitE01D04),
           label = paste("Shared Assemblies =", num_rows.counts.0.058.tmp), hjust = 0, vjust = 1.5) +
  scale_x_continuous(breaks = seq(floor(min(L15.L16.Counts.0.058.TMP$fitD06D03)), 
                                  ceiling(max(L15.L16.Counts.0.058.TMP$fitD06D03)), by = 1)) +
  scale_y_continuous(breaks = seq(floor(min(L15.L16.Counts.0.058.TMP$fitE01D04)), 
                                  ceiling(max(L15.L16.Counts.0.058.TMP$fitE01D04)), by = 1))

Lib15_16_0.058_TMP_p01 <- ggMarginal(Lib15_16_0.058_TMP, type = "histogram", fill = "lightblue4", alpha = 0.5)
`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'
Lib15_16_0.058_TMP_p01

Plot shared fitness correlations between Lib15 & Lib16 (perfects) for 0.5 TMP:

# Extract correlation value from cor_value_shared object
cor_value_shared_0.5_tmp <- cor_test_Counts_shared_0.5_tmp$estimate

# Format p-value in scientific notation
p_value_scientific_shared_0.5_tmp <- format(cor_test_Counts_shared_0.5_tmp$p.value, scientific = TRUE, digits = 4)

# Extract number of rows
num_rows.counts.0.5.tmp <- nrow(L15.L16.Counts.0.5.TMP)

# Plot based on shared mutID
Lib15_16_0.5_TMP <- ggplot(L15.L16.Counts.0.5.TMP, 
                   aes(x=fitD07D03, y=fitE02D04)) +
  labs(x = "Codon 1 Median Fitness (LogFC) \nMIC (0.5 μg/mL tmp)", y ="Codon 2 Median Fitness (LogFC) \nMIC (0.5 μg/mL tmp)") +
  geom_smooth(method=lm,colour="black") +
  geom_density2d(colour="black",alpha=0.2) +
  geom_point(aes(color = case_when(
    fitD07D03 >= -1 & fitE02D04 >= -1 ~ "lightblue4",
    fitD07D03 >= -1 & fitE02D04 < -1 ~ "#0072B2",
    fitE02D04 >= -1 & fitD07D03 < -1 ~ "#E69F00",
    TRUE ~ "black"
  ),
  fill = case_when(
    fitD07D03 >= -1 & fitE02D04 >= -1 ~ "lightblue4",
    fitD07D03 >= -1 & fitE02D04 < -1 ~ "#0072B2",
    fitE02D04 >= -1 & fitD07D03 < -1 ~ "#E69F00",
    TRUE ~ "white"
  ),
  shape = case_when(
    fitD07D03 >= -1 & fitE02D04 >= -1 ~ 16,
    fitD07D03 >= -1 & fitE02D04 < -1 ~ 16,
    fitE02D04 >= -1 & fitD07D03 < -1 ~ 16,
    TRUE ~ 21
  )), 
  alpha = 0.75, size = 4) +
scale_shape_identity() +
scale_color_identity() +
scale_fill_identity() +
  # Add a new point for WT E. coli median fitness
  geom_point(data = BCcontrols_15_16_shared_median_WT, 
             aes(x = fitD07D03, y = fitE02D04), 
             fill = "red", color = "black", size = 4, shape = 24) +
  # Add a new point for Neg Ctrl (D27N, mCherry) median fitness
  geom_point(data = BCcontrols_15_16_shared_median_Neg, 
             aes(x = fitD07D03, y = fitE02D04), 
             color = "black", size = 5, shape = 18) +
  theme(legend.position="none") +
  theme(axis.text.x = element_text(size = 14),
        axis.text.y = element_text(size = 14),
        axis.title.x = element_text(size = 16),
        axis.title.y = element_text(size = 16),
        axis.line = element_line(colour = 'black', size = 1.0),
        axis.ticks = element_line(colour = "black", size = 1.0),
        panel.background = element_blank()) + #,
        #panel.border = element_rect(colour = "black", fill=NA, size=1.0)) +
  annotate("text",
           x = max(L15.L16.Counts.0.5.TMP$fitD07D03),
           y = min(L15.L16.Counts.0.5.TMP$fitE02D04), 
           label = paste("p-value =", p_value_scientific_shared_0.5_tmp), hjust = 1, vjust = 0, size = 5) +
  annotate("text",
           x = max(L15.L16.Counts.0.5.TMP$fitD07D03),
           y = min(L15.L16.Counts.0.5.TMP$fitE02D04),
            label = paste("Correlation =", round(cor_value_shared_0.5_tmp, 2)), hjust = 1, vjust = -1.5, size = 5) +
  annotate("text",
           x = min(L15.L16.Counts.0.5.TMP$fitD07D03),
           y = max(L15.L16.Counts.0.5.TMP$fitE02D04),
           label = paste("Shared Assemblies =", num_rows.counts.0.5.tmp), hjust = 0, vjust = 1.5, size = 5) +
  scale_x_continuous(breaks = seq(floor(min(L15.L16.Counts.0.5.TMP$fitD07D03)), 
                                  ceiling(max(L15.L16.Counts.0.5.TMP$fitD07D03)), by = 1)) +
  scale_y_continuous(breaks = seq(floor(min(L15.L16.Counts.0.5.TMP$fitE02D04)), 
                                  ceiling(max(L15.L16.Counts.0.5.TMP$fitE02D04)), by = 1))

Lib15_16_0.5_TMP_p01 <- ggMarginal(Lib15_16_0.5_TMP, type = "histogram", fill = "lightblue4", alpha = 0.5)
`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'
Lib15_16_0.5_TMP_p01

Plot shared fitness correlations between Lib15 & Lib16 (perfects) for 1.0 TMP:

# Extract correlation value from cor_value_shared object
cor_value_shared_1.0_tmp <- cor_test_Counts_shared_1.0_tmp$estimate

# Format p-value in scientific notation
p_value_scientific_shared_1.0_tmp <- format(cor_test_Counts_shared_1.0_tmp$p.value, scientific = TRUE, digits = 4)

# Extract number of rows
num_rows.counts.1.0.tmp <- nrow(L15.L16.Counts.1.0.TMP)

# Plot based on shared mutID
Lib15_16_1.0_TMP <- ggplot(L15.L16.Counts.1.0.TMP, 
                   aes(x=fitD08D03, y=fitE03D04)) +
  labs(x = "Codon 1 Median Fitness (LogFC) \n(1.0 μg/mL tmp)", y ="Codon 2 Median Fitness (LogFC) \n(1.0 μg/mL tmp)") +
  geom_smooth(method=lm,colour="black") +
  geom_density2d(colour="black",alpha=0.2) +
  geom_point(aes(color = case_when(
    fitD08D03 >= -1 & fitE03D04 >= -1 ~ "lightblue4",
    fitD08D03 >= -1 & fitE03D04 < -1 ~ "#0072B2",
    fitE03D04 >= -1 & fitD08D03 < -1 ~ "#E69F00",
    TRUE ~ "black"
  ),
  fill = case_when(
    fitD08D03 >= -1 & fitE03D04 >= -1 ~ "lightblue4",
    fitD08D03 >= -1 & fitE03D04 < -1 ~ "#0072B2",
    fitE03D04 >= -1 & fitD08D03 < -1 ~ "#E69F00",
    TRUE ~ "white"
  ),
  shape = case_when(
    fitD08D03 >= -1 & fitE03D04 >= -1 ~ 16,
    fitD08D03 >= -1 & fitE03D04 < -1 ~ 16,
    fitE03D04 >= -1 & fitD08D03 < -1 ~ 16,
    TRUE ~ 21
  )), 
  alpha = 0.75, size = 4) +
scale_shape_identity() +
scale_color_identity() +
scale_fill_identity() +
  # Add a new point for WT E. coli median fitness
  geom_point(data = BCcontrols_15_16_shared_median_WT, 
             aes(x = fitD08D03, y = fitE03D04), 
             fill = "red", color = "black", size = 3, shape = 24) +
  # Add a new point for Neg Ctrl (D27N, mCherry) median fitness
  geom_point(data = BCcontrols_15_16_shared_median_Neg, 
             aes(x = fitD08D03, y = fitE03D04), 
             color = "black", size = 5, shape = 18) +
  theme(legend.position="none") +
  theme(axis.text.x = element_text(size = 14),
        axis.text.y = element_text(size = 14),
        axis.title.x = element_text(size = 16),
        axis.title.y = element_text(size = 16),
        axis.line = element_line(colour = 'black', size = 1.0),
        axis.ticks = element_line(colour = "black", size = 1.0),
        panel.background = element_blank()) + #,
        #panel.border = element_rect(colour = "black", fill=NA, size=1.0)) +
  annotate("text",
           x = max(L15.L16.Counts.1.0.TMP$fitD08D03),
           y = min(L15.L16.Counts.1.0.TMP$fitE03D04), 
           label = paste("p-value =", p_value_scientific_shared_1.0_tmp), hjust = 1, vjust = 0) +
  annotate("text",
           x = max(L15.L16.Counts.1.0.TMP$fitD08D03),
           y = min(L15.L16.Counts.1.0.TMP$fitE03D04),
            label = paste("Correlation =", round(cor_value_shared_1.0_tmp, 2)), hjust = 1, vjust = -1.5) +
  annotate("text",
           x = min(L15.L16.Counts.1.0.TMP$fitD08D03),
           y = max(L15.L16.Counts.1.0.TMP$fitE03D04),
           label = paste("Shared Assemblies =", num_rows.counts.1.0.tmp), hjust = 0, vjust = 1.5) +
  scale_x_continuous(breaks = seq(floor(min(L15.L16.Counts.1.0.TMP$fitD08D03)), 
                                  ceiling(max(L15.L16.Counts.1.0.TMP$fitD08D03)), by = 1)) +
  scale_y_continuous(breaks = seq(floor(min(L15.L16.Counts.1.0.TMP$fitE03D04)), 
                                  ceiling(max(L15.L16.Counts.1.0.TMP$fitE03D04)), by = 1))

Lib15_16_1.0_TMP_p01 <- ggMarginal(Lib15_16_1.0_TMP, type = "histogram", fill = "lightblue4", alpha = 0.5)
`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'
Lib15_16_1.0_TMP_p01

Plot shared fitness correlations between Lib15 & Lib16 (perfects) for 10 TMP:

# Extract correlation value from cor_value_shared object
cor_value_shared_10_tmp <- cor_test_Counts_shared_10_tmp$estimate

# Format p-value in scientific notation
p_value_scientific_shared_10_tmp <- format(cor_test_Counts_shared_10_tmp$p.value, scientific = TRUE, digits = 4)

# Extract number of rows
num_rows.counts.10.tmp <- nrow(L15.L16.Counts.10.TMP)

# Plot based on shared mutID
Lib15_16_10_TMP <- ggplot(L15.L16.Counts.10.TMP, 
                   aes(x=fitD09D03, y=fitE04D04)) +
  labs(x = "Codon 1 Median Fitness (LogFC) \n(10 μg/mL tmp)", y ="Codon 2 Median Fitness (LogFC) \n(10 μg/mL tmp)") +
  geom_smooth(method=lm,colour="black") +
  geom_density2d(colour="black",alpha=0.2) +
  geom_point(aes(color = case_when(
    fitD09D03 >= -1 & fitE04D04 >= -1 ~ "lightblue4",
    fitD09D03 >= -1 & fitE04D04 < -1 ~ "#0072B2",
    fitE04D04 >= -1 & fitD09D03 < -1 ~ "#E69F00",
    TRUE ~ "black"
  ),
  fill = case_when(
    fitD09D03 >= -1 & fitE04D04 >= -1 ~ "lightblue4",
    fitD09D03 >= -1 & fitE04D04 < -1 ~ "#0072B2",
    fitE04D04 >= -1 & fitD09D03 < -1 ~ "#E69F00",
    TRUE ~ "white"
  ),
  shape = case_when(
    fitD09D03 >= -1 & fitE04D04 >= -1 ~ 16,
    fitD09D03 >= -1 & fitE04D04 < -1 ~ 16,
    fitE04D04 >= -1 & fitD09D03 < -1 ~ 16,
    TRUE ~ 21
  )), 
  alpha = 0.75, size = 4) +
scale_shape_identity() +
scale_color_identity() +
scale_fill_identity() +
  # Add a new point for WT E. coli median fitness
  geom_point(data = BCcontrols_15_16_shared_median_WT, 
             aes(x = fitD09D03, y = fitE04D04), 
             fill = "red", color = "black", size = 3, shape = 24) +
  # Add a new point for Neg Ctrl (D27N, mCherry) median fitness
  geom_point(data = BCcontrols_15_16_shared_median_Neg, 
             aes(x = fitD09D03, y = fitE04D04), 
             color = "black", size = 5, shape = 18) +
  theme(legend.position="none") +
  theme(axis.text.x = element_text(size = 14),
        axis.text.y = element_text(size = 14),
        axis.title.x = element_text(size = 16),
        axis.title.y = element_text(size = 16),
        axis.line = element_line(colour = 'black', size = 1.0),
        axis.ticks = element_line(colour = "black", size = 1.0),
        panel.background = element_blank()) + #,
        #panel.border = element_rect(colour = "black", fill=NA, size=1.0)) +
  annotate("text",
           x = max(L15.L16.Counts.10.TMP$fitD09D03),
           y = min(L15.L16.Counts.10.TMP$fitE04D04), 
           label = paste("p-value =", p_value_scientific_shared_10_tmp), hjust = 1, vjust = 0) +
  annotate("text",
           x = max(L15.L16.Counts.10.TMP$fitD09D03),
           y = min(L15.L16.Counts.10.TMP$fitE04D04),
            label = paste("Correlation =", round(cor_value_shared_10_tmp, 2)), hjust = 1, vjust = -1.5) +
  annotate("text",
           x = min(L15.L16.Counts.10.TMP$fitD09D03),
           y = max(L15.L16.Counts.10.TMP$fitE04D04),
           label = paste("Shared Assemblies =", num_rows.counts.10.tmp), hjust = 0, vjust = 1.5) +
  scale_x_continuous(breaks = seq(floor(min(L15.L16.Counts.10.TMP$fitD09D03)), 
                                  ceiling(max(L15.L16.Counts.10.TMP$fitD09D03)), by = 1)) +
  scale_y_continuous(breaks = seq(floor(min(L15.L16.Counts.10.TMP$fitE04D04)), 
                                  ceiling(max(L15.L16.Counts.10.TMP$fitE04D04)), by = 1))

Lib15_16_10_TMP_p01 <- ggMarginal(Lib15_16_10_TMP, type = "histogram", fill = "lightblue4", alpha = 0.5)
`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'
Lib15_16_10_TMP_p01

Plot shared fitness correlations between Lib15 & Lib16 (perfects) for 50 TMP:

# Extract correlation value from cor_value_shared object
cor_value_shared_50_tmp <- cor_test_Counts_shared_50_tmp$estimate

# Format p-value in scientific notation
p_value_scientific_shared_50_tmp <- format(cor_test_Counts_shared_50_tmp$p.value, scientific = TRUE, digits = 4)

# Extract number of rows
num_rows.counts.50.tmp <- nrow(L15.L16.Counts.50.TMP)

# Plot based on shared mutID
Lib15_16_50_TMP <- ggplot(L15.L16.Counts.50.TMP, 
                   aes(x=fitD10D03, y=fitE05D04)) +
  labs(x = "Codon 1 Median Fitness (LogFC) \n(50 μg/mL tmp)", y ="Codon 2 Median Fitness (LogFC) \n(50 μg/mL tmp)") +
  geom_smooth(method=lm,colour="black") +
  geom_density2d(colour="black",alpha=0.2) +
  geom_point(aes(color = case_when(
    fitD10D03 >= -1 & fitE05D04 >= -1 ~ "lightblue4",
    fitD10D03 >= -1 & fitE05D04 < -1 ~ "#0072B2",
    fitE05D04 >= -1 & fitD10D03 < -1 ~ "#E69F00",
    TRUE ~ "black"
  ),
  fill = case_when(
    fitD10D03 >= -1 & fitE05D04 >= -1 ~ "lightblue4",
    fitD10D03 >= -1 & fitE05D04 < -1 ~ "#0072B2",
    fitE05D04 >= -1 & fitD10D03 < -1 ~ "#E69F00",
    TRUE ~ "white"
  ),
  shape = case_when(
    fitD10D03 >= -1 & fitE05D04 >= -1 ~ 16,
    fitD10D03 >= -1 & fitE05D04 < -1 ~ 16,
    fitE05D04 >= -1 & fitD10D03 < -1 ~ 16,
    TRUE ~ 21
  )), 
  alpha = 0.75, size = 4) +
scale_shape_identity() +
scale_color_identity() +
scale_fill_identity() +
  # Add a new point for WT E. coli median fitness
  geom_point(data = BCcontrols_15_16_shared_median_WT, 
             aes(x = fitD10D03, y = fitE05D04), 
             fill = "red", color = "black", size = 3, shape = 24) +
  # Add a new point for Neg Ctrl (D27N, mCherry) median fitness
  geom_point(data = BCcontrols_15_16_shared_median_Neg, 
             aes(x = fitD10D03, y = fitE05D04), 
             color = "black", size = 5, shape = 18) +
  theme(legend.position="none") +
  theme(axis.text.x = element_text(size = 14),
        axis.text.y = element_text(size = 14),
        axis.title.x = element_text(size = 16),
        axis.title.y = element_text(size = 16),
        axis.line = element_line(colour = 'black', size = 1.0),
        axis.ticks = element_line(colour = "black", size = 1.0),
        panel.background = element_blank()) + #,
        #panel.border = element_rect(colour = "black", fill=NA, size=1.0)) +
  annotate("text",
           x = max(L15.L16.Counts.50.TMP$fitD10D03),
           y = min(L15.L16.Counts.50.TMP$fitE05D04), 
           label = paste("p-value =", p_value_scientific_shared_50_tmp), hjust = 1, vjust = 0) +
  annotate("text",
           x = max(L15.L16.Counts.50.TMP$fitD10D03),
           y = min(L15.L16.Counts.50.TMP$fitE05D04),
            label = paste("Correlation =", round(cor_value_shared_50_tmp, 2)), hjust = 1, vjust = -1.5) +
  annotate("text",
           x = min(L15.L16.Counts.50.TMP$fitD10D03),
           y = max(L15.L16.Counts.50.TMP$fitE05D04),
           label = paste("Shared Assemblies =", num_rows.counts.50.tmp), hjust = 0, vjust = 1.5) +
  scale_x_continuous(breaks = seq(floor(min(L15.L16.Counts.50.TMP$fitD10D03)), 
                                  ceiling(max(L15.L16.Counts.50.TMP$fitD10D03)), by = 1)) +
  scale_y_continuous(breaks = seq(floor(min(L15.L16.Counts.50.TMP$fitE05D04)), 
                                  ceiling(max(L15.L16.Counts.50.TMP$fitE05D04)), by = 1))

Lib15_16_50_TMP_p01 <- ggMarginal(Lib15_16_50_TMP, type = "histogram", fill = "lightblue4", alpha = 0.5)
`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'
Lib15_16_50_TMP_p01

Plot shared fitness correlations between Lib15 & Lib16 (perfects) for 200 TMP:

# Extract correlation value from cor_value_shared object
cor_value_shared_200_tmp <- cor_test_Counts_shared_200_tmp$estimate

# Format p-value in scientific notation
p_value_scientific_shared_200_tmp <- format(cor_test_Counts_shared_200_tmp$p.value, scientific = TRUE, digits = 4)

# Extract number of rows
num_rows.counts.200.tmp <- nrow(L15.L16.Counts.200.TMP)

# Plot based on shared mutID
Lib15_16_200_TMP <- ggplot(L15.L16.Counts.200.TMP, 
                   aes(x=fitD11D03, y=fitE06D04)) +
  labs(x = "Codon 1 Median Fitness (LogFC) \n400x MIC (200 μg/mL tmp)",
       y ="Codon 2 Median Fitness (LogFC) \n400x MIC (200 μg/mL tmp)") +
  geom_smooth(method=lm,colour="black") +
  geom_density2d(colour="black",alpha=0.2) +
  geom_point(aes(color = case_when(
    fitD11D03 >= -1 & fitE06D04 >= -1 ~ "lightblue4",
    fitD11D03 >= -1 & fitE06D04 < -1 ~ "#0072B2",
    fitE06D04 >= -1 & fitD11D03 < -1 ~ "#E69F00",
    TRUE ~ "black"
  ),
  fill = case_when(
    fitD11D03 >= -1 & fitE06D04 >= -1 ~ "lightblue4",
    fitD11D03 >= -1 & fitE06D04 < -1 ~ "#0072B2",
    fitE06D04 >= -1 & fitD11D03 < -1 ~ "#E69F00",
    TRUE ~ "white"
  ),
  shape = case_when(
    fitD11D03 >= -1 & fitE06D04 >= -1 ~ 16,
    fitD11D03 >= -1 & fitE06D04 < -1 ~ 16,
    fitE06D04 >= -1 & fitD11D03 < -1 ~ 16,
    TRUE ~ 21
  )), 
  alpha = 0.75, size = 4) +
scale_shape_identity() +
scale_color_identity() +
scale_fill_identity() +
  # Add a new point for WT E. coli median fitness
  geom_point(data = BCcontrols_15_16_shared_median_WT, 
             aes(x = fitD11D03, y = fitE06D04), 
             fill = "red", color = "black", size = 4, shape = 24) +
  # Add a new point for Neg Ctrl (D27N, mCherry) median fitness
  geom_point(data = BCcontrols_15_16_shared_median_Neg, 
             aes(x = fitD11D03, y = fitE06D04), 
             color = "black", size = 5, shape = 18) +
  theme(legend.position="none") +
  theme(axis.text.x = element_text(size = 14),
        axis.text.y = element_text(size = 14),
        axis.title.x = element_text(size = 16),
        axis.title.y = element_text(size = 16),
        axis.line = element_line(colour = 'black', size = 1.0),
        axis.ticks = element_line(colour = "black", size = 1.0),
        panel.background = element_blank()) + #,
        #panel.border = element_rect(colour = "black", fill=NA, size=1.0)) +
  annotate("text",
           x = max(L15.L16.Counts.200.TMP$fitD11D03),
           y = min(L15.L16.Counts.200.TMP$fitE06D04), 
           label = paste("p-value =", p_value_scientific_shared_200_tmp), hjust = 1, vjust = 0, size = 5) +
  annotate("text",
           x = max(L15.L16.Counts.200.TMP$fitD11D03),
           y = min(L15.L16.Counts.200.TMP$fitE06D04),
            label = paste("Correlation =", round(cor_value_shared_200_tmp, 2)), hjust = 1, vjust = -1.5, size = 5) +
  annotate("text",
           x = min(L15.L16.Counts.200.TMP$fitD11D03),
           y = max(L15.L16.Counts.200.TMP$fitE06D04),
           label = paste("Shared Assemblies =", num_rows.counts.200.tmp), hjust = 0, vjust = 1.5, size = 5) +
  scale_x_continuous(breaks = seq(floor(min(L15.L16.Counts.200.TMP$fitD11D03)), 
                                  ceiling(max(L15.L16.Counts.200.TMP$fitD11D03)), by = 1)) +
  scale_y_continuous(breaks = seq(floor(min(L15.L16.Counts.200.TMP$fitE06D04)), 
                                  ceiling(max(L15.L16.Counts.200.TMP$fitE06D04)), by = 1))

Lib15_16_200_TMP_p01 <- ggMarginal(Lib15_16_200_TMP, type = "histogram", fill = "lightblue4", alpha = 0.5)
`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'
Lib15_16_200_TMP_p01

Perfects Plots

The following section plots various summarizes of the Perfects BCs (minimum 5BC count/mutID):

Re-order the “perfects” dataset by “numprunedBCs” in ascending order:

# Rank order based on numprunedBCs in Lib15
perfects_shared.rank.15 <- perfects_15_16_shared %>%
  arrange(numprunedBCs.x)

# Rank order based on numprunedBCs in Lib16
perfects_shared.rank.16 <- perfects_15_16_shared %>%
  arrange(numprunedBCs.y)

Make plotting key based on “numprunedBCs”

# Plotting key based on numprunedBCs in Lib15
perfects_shared.rank.15$numprunedBCs.x.key <- 1:length(perfects_shared.rank.15$mutID)

# Plotting key based on numprunedBCs in Lib16
perfects_shared.rank.16$numprunedBCs.y.key <- 1:length(perfects_shared.rank.16$mutID)

Homolog Fitness Plots

Rank Order Fitness Plots

Plot the number of perfects barcodes recovered based on the rank order of homologs:

Shared mutIDs

# Rename columns in one of the data frames
colnames(perfects_shared.rank.16) <- paste0(colnames(perfects_shared.rank.16), "_16")

# Combine the data frames
perfects_shared.rank.15.16 <- cbind(perfects_shared.rank.15, perfects_shared.rank.16)
# Create the plot
perfects_shared.rank.15.16_plot <- ggplot(perfects_shared.rank.15.16, aes(x = numprunedBCs.x.key)) +
  geom_point(aes(y = numprunedBCs.x, color = "Lib15"), size = 2) +
  geom_point(aes(y = numprunedBCs.y_16, color = "Lib16"), size = 2) +
  scale_color_manual(name = "Library", values = c("Lib15" = "#0072B2", "Lib16" = "#E69F00")) +
  ylab("Number of Barcodes") +
  xlab("Rank Ordered Homolog") +
  scale_y_log10(limits = c(1, 2000)) +
  theme_minimal() +
  theme(axis.line = element_line(colour = 'black', size = 0.5), 
        axis.ticks = element_line(colour = "black", size = 0.5),
        axis.text.x = element_text(size = 12),
        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.title = element_blank(),
        legend.text = element_text(size = 12),
        legend.position = "none")

perfects_shared.rank.15.16_plot

Perfects (>1 BC) Histogram

Plot the Perfects counts based on fitness values between D05 vs. D03 (Lib15) or D12 vs. D04 (Lib16):

rank_histogram_1BC_15_16 <- ggplot(perfects_shared.rank.15.16, aes(x = fitD05D03, fill = "Lib15")) +
  geom_histogram(binwidth = 0.1, alpha = 0.5, color = "black") +
  geom_histogram(aes(x = fitD12D04, fill = "Lib16"), binwidth = 0.1, alpha = 0.5, color = "black") +
  xlab("Median Fitness \n(Log2 Fold Change)") +
  ylab("Counts") +
  ggtitle("Complementation Rank Count \n(Perfects >1 BC)") +
  theme_minimal() +
  theme(
    axis.line = element_line(colour = 'black', size = 0.5),
    axis.ticks = element_line(colour = "black", size = 0.5),
    plot.title = element_text(size = 16, hjust = 0.5, face = "bold"),
    axis.text.x = element_text(size = 12),
    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.title = element_blank(),
    legend.text = element_text(size = 12),
    legend.position = "none") +
  scale_x_continuous(limits = c(-7, 3)) +
  scale_y_continuous(expand = c(0, 0), limits = c(0, 50)) +
  scale_fill_manual(values = c("Lib15" = "#0072B2", "Lib16" = "#E69F00"), name = "Library")

rank_histogram_1BC_15_16

Summarize the total number of unique Perfects regardless of fitness value:

# Lib15
perfects15 %>%
  nrow(.)
[1] 961
# Lib16
perfects16 %>%
  nrow(.)
[1] 818
# Shared Perfects
perfects_15_16_shared %>%
  nrow(.)
[1] 643

Summarize the number of Perfects with fitness greater than -1.0 in D05 vs. D03 (Lib15) or D12 vs. D04 (Lib16) conditions:

# Shared Perfects - Lib15
perfects_15_16_shared %>% filter(fitD05D03>-1.0) %>%
  nrow(.)
[1] 335
# Shared Perfects - Lib16
perfects_15_16_shared %>% filter(fitD12D04>-1.0) %>%
  nrow(.)
[1] 350
Perfects (>2 BCs) Histogram

Plot the Perfects counts based on fitness values between D05 vs. D03 (Lib15) or D12 vs. D04 (Lib16) at > 2 BCs:

rank_histogram_2BC_15_16 <- ggplot(perfects_15_16_shared_2BCs, aes(x = fitD05D03, fill = "Lib15")) +
  geom_histogram(binwidth = 0.1, alpha = 0.5, color = "black") +
  geom_histogram(aes(x = fitD12D04, fill = "Lib16"), binwidth = 0.1, alpha = 0.5, color = "black") +
  xlab("Fitness (Log2 Fold Change)") +
  ylab("Counts") +
  ggtitle("Complementation Rank Count \n(Perfects >2 BC)") +
  theme_minimal() +
  theme(
    axis.line = element_line(colour = 'black', size = 0.5),
    axis.ticks = element_line(colour = "black", size = 0.5),
    plot.title = element_text(size = 16, hjust = 0.5, face = "bold"),
    axis.text.x = element_text(size = 12),
    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.title = element_blank(),
    legend.text = element_text(size = 12),
    legend.position = "none") +
  scale_x_continuous(limits = c(-7, 3)) +
  scale_y_continuous(expand = c(0, 0), limits = c(0, 50)) +
  scale_fill_manual(values = c("Lib15" = "#0072B2", "Lib16" = "#E69F00"), name = "Library")

rank_histogram_2BC_15_16

Summarize the total number of unique Perfects (>2 BCs) regardless of fitness value:

# Shared Perfects
perfects_15_16_shared_2BCs %>%
  nrow(.)
[1] 579

Summarize the number of Perfects (>2 BCs) with fitness greater than -1.0 in D05 vs. D03 (Lib15) or D12 vs. D04 (Lib16) conditions:

# Lib15
perfects_15_16_shared_2BCs %>% filter(fitD05D03>-1.0) %>%
  nrow(.)
[1] 306
# Lib16
perfects_15_16_shared_2BCs %>% filter(fitD12D04>-1.0) %>%
  nrow(.)
[1] 322
Perfects (>5 BCs) Histogram

Plot the Perfects counts based on fitness values between D05 vs. D03 (Lib15) or D12 vs. D04 (Lib16) at > 5 BCs:

rank_histogram_5BC_15_16 <- ggplot(perfects_15_16_shared_5BCs, aes(x = fitD05D03, fill = "Lib15")) +
  geom_histogram(binwidth = 0.1, alpha = 0.5, color = "black") +
  geom_histogram(aes(x = fitD12D04, fill = "Lib16"), binwidth = 0.1, alpha = 0.5, color = "black") +
  xlab("Fitness (Log2 Fold Change)") +
  ylab("Counts") +
  ggtitle("Complementation Rank Count \n(Perfects >5 BC)") +
  theme_minimal() +
  theme(
    axis.line = element_line(colour = 'black', size = 0.5),
    axis.ticks = element_line(colour = "black", size = 0.5),
    plot.title = element_text(size = 16, hjust = 0.5, face = "bold"),
    axis.text.x = element_text(size = 12),
    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.title = element_blank(),
    legend.text = element_text(size = 12),
    legend.position = "bottom") +
  scale_x_continuous(limits = c(-7, 3)) +
  scale_y_continuous(expand = c(0, 0), limits = c(0, 50)) +
  scale_fill_manual(values = c("Lib15" = "#0072B2", "Lib16" = "#E69F00"), name = "Library")

rank_histogram_5BC_15_16

Summarize the total number of unique Perfects (>2 BCs) regardless of fitness value:

# Shared Perfects
perfects_15_16_shared_5BCs %>%
  nrow(.)
[1] 493

Summarize the number of Perfects (>2 BCs) with fitness greater than -1.0 in D05 vs. D03 (Lib15) or D12 vs. D04 (Lib16) conditions:

# Lib15
perfects_15_16_shared_5BCs %>% filter(fitD05D03>-1.0) %>%
  nrow(.)
[1] 273
# Lib16
perfects_15_16_shared_5BCs %>% filter(fitD12D04>-1.0) %>%
  nrow(.)
[1] 280

Plot all minimum BC levels together

Perfects Scatter Plot (>5 BCs)

Plot the Perfects fitness scores for D05 vs. D03 (Lib15) or D12 vs. D04 (Lib16) based on the number of recovered barcodes:

BC_scatter_5BC_15_16 <- ggplot() +
  geom_point(data = perfects_15_16_shared_5BCs_good, aes(x = numprunedBCs.x, y = fitD05D03), color = "#0072B2", alpha = 0.8) +
  geom_point(data = perfects_15_16_shared_5BCs_good, aes(x = numprunedBCs.y, y = fitD12D04), color = "#E69F00", alpha = 0.8) +
  xlab("Number of Barcodes") +
  ylab("Fitness (Log2 Fold Change)") +
  ggtitle("Barcode Counts \n(Perfects >5 BC)") +
  scale_x_log10() +
  theme_minimal() +
  theme(
    axis.line = element_line(colour = 'black', size = 0.5),
    axis.ticks = element_line(colour = "black", size = 0.5),
    plot.title = element_text(size = 16, hjust = 0.5, face = "bold"),
    axis.text.x = element_text(size = 12),
    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())

BC_scatter_5BC_15_16

Perfects Dose Response (>5 BCs)

Gather the median fitness of Perfects at each Trimethoprim concentration based on filtered 1BC dataset.

Library 15

# Filter perfects_15_16 dataset by minimum of numprunedBCs
perfects15_5BCsdr <- perfects_15_16_shared_5BCs_good %>%
  select(mutID, fitD05D03, fitD06D03, fitD07D03, fitD08D03, fitD09D03, fitD10D03, fitD11D03, numprunedBCs.x) %>%#
  gather("selection","fitness",-mutID,-numprunedBCs.x)

# Create naming factors to convert column names into trimethoprim concentrations for plotting
perfects15_5BCsdr$tmpfactor <- "0"
perfects15_5BCsdr$tmpfactor[perfects15_5BCsdr$selection== "fitD05D03"] <- "0"
perfects15_5BCsdr$tmpfactor[perfects15_5BCsdr$selection== "fitD06D03"] <- "0.0058"
perfects15_5BCsdr$tmpfactor[perfects15_5BCsdr$selection== "fitD07D03"] <- "0.5"
perfects15_5BCsdr$tmpfactor[perfects15_5BCsdr$selection== "fitD08D03"] <- "1"
perfects15_5BCsdr$tmpfactor[perfects15_5BCsdr$selection== "fitD09D03"] <- "10"
perfects15_5BCsdr$tmpfactor[perfects15_5BCsdr$selection== "fitD10D03"] <- "50"
perfects15_5BCsdr$tmpfactor[perfects15_5BCsdr$selection== "fitD11D03"] <- "200"

perfects15_5BCsdr$tmpname[perfects15_5BCsdr$selection== "fitD05D03"] <- "A"
perfects15_5BCsdr$tmpname[perfects15_5BCsdr$selection== "fitD06D03"] <- "B"
perfects15_5BCsdr$tmpname[perfects15_5BCsdr$selection== "fitD07D03"] <- "C"
perfects15_5BCsdr$tmpname[perfects15_5BCsdr$selection== "fitD08D03"] <- "D"
perfects15_5BCsdr$tmpname[perfects15_5BCsdr$selection== "fitD09D03"] <- "E"
perfects15_5BCsdr$tmpname[perfects15_5BCsdr$selection== "fitD10D03"] <- "F"
perfects15_5BCsdr$tmpname[perfects15_5BCsdr$selection== "fitD11D03"] <- "G"

perfects15_5BCsdrlabs <- c("0","0.058","0.5","1","10","50","200")
Lib15 Violin Plot
TMP_plot_5BC_15 <- ggplot(perfects15_5BCsdr,aes(x=tmpname,y=fitness)) + 
  geom_violin(fill = "#0072B2", alpha=0.75) +
  geom_boxplot(width=0.1) +
  xlab("Trimethoprim (ug/mL)") +
  ylab("Median Fitness") +
  scale_x_discrete(labels= perfects15_5BCsdrlabs) +
  ggtitle("Library 15 Dose Response \n(Perfects >5 BC)") +
  theme_minimal() +
  theme(axis.line = element_line(colour = 'black', size = 0.5), 
        axis.ticks = element_line(colour = "black", size = 0.5),
        plot.title = element_text(size = 16, hjust = 0.5, face = "bold"),
        axis.text.x = element_text(size = 12),
        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.title = element_blank(),
        legend.text = element_text(size = 12),
        legend.position = c(0.16, 1),
        legend.justification = c(1, 1),
        legend.box.background = element_rect(colour = "black")) +
  scale_y_continuous(expand = c(0, 0), limits = c(-12,8))
Warning: A numeric `legend.position` argument in `theme()` was deprecated in ggplot2 3.5.0.
Please use the `legend.position.inside` argument of `theme()` instead.
TMP_plot_5BC_15

Lib16 Violin Plot
# Filter perfects16_5BCs dataset by minimum of numprunedBCs
perfects16_5BCsdr <- perfects_15_16_shared_5BCs_good %>%
  #filter(numprunedBCs>10) %>%
  select(mutID, fitD12D04, fitE01D04, fitE02D04, fitE03D04, fitE04D04, fitE05D04, fitE06D04, numprunedBCs.y) %>%#
  gather("selection","fitness",-mutID,-numprunedBCs.y)

# Create naming factors to convert column names into trimethoprim concentrations for plotting
perfects16_5BCsdr$tmpfactor <- "0"
perfects16_5BCsdr$tmpfactor[perfects16_5BCsdr$selection== "fitD12D04"] <- "0"
perfects16_5BCsdr$tmpfactor[perfects16_5BCsdr$selection== "fitE01D04"] <- "0.0058"
perfects16_5BCsdr$tmpfactor[perfects16_5BCsdr$selection== "fitE02D04"] <- "0.5"
perfects16_5BCsdr$tmpfactor[perfects16_5BCsdr$selection== "fitE03D04"] <- "1"
perfects16_5BCsdr$tmpfactor[perfects16_5BCsdr$selection== "fitE04D04"] <- "10"
perfects16_5BCsdr$tmpfactor[perfects16_5BCsdr$selection== "fitE05D04"] <- "50"
perfects16_5BCsdr$tmpfactor[perfects16_5BCsdr$selection== "fitE06D04"] <- "200"

perfects16_5BCsdr$tmpname[perfects16_5BCsdr$selection== "fitD12D04"] <- "A"
perfects16_5BCsdr$tmpname[perfects16_5BCsdr$selection== "fitE01D04"] <- "B"
perfects16_5BCsdr$tmpname[perfects16_5BCsdr$selection== "fitE02D04"] <- "C"
perfects16_5BCsdr$tmpname[perfects16_5BCsdr$selection== "fitE03D04"] <- "D"
perfects16_5BCsdr$tmpname[perfects16_5BCsdr$selection== "fitE04D04"] <- "E"
perfects16_5BCsdr$tmpname[perfects16_5BCsdr$selection== "fitE05D04"] <- "F"
perfects16_5BCsdr$tmpname[perfects16_5BCsdr$selection== "fitE06D04"] <- "G"

perfects16_5BCsdrlabs <- c("0","0.058","0.5","1","10","50","200")

Lib16 Violin Plot: based on filtered 5BC dataset:

TMP_plot_5BC_16 <- ggplot(perfects16_5BCsdr,aes(x=tmpname,y=fitness)) + 
  geom_violin(fill = "#E69F00") +
  geom_boxplot(width=0.1) +
  xlab("Trimethoprim (ug/mL)") +
  ylab("Median Fitness") +
  scale_x_discrete(labels= perfects16_5BCsdrlabs) +
  ggtitle("Library 16 Dose Response \n(Perfects >5 BC)") +
  theme_minimal() +
  theme(axis.line = element_line(colour = 'black', size = 0.5), 
        axis.ticks = element_line(colour = "black", size = 0.5),
        plot.title = element_text(size = 16, hjust = 0.5, face = "bold"),
        axis.text.x = element_text(size = 12),
        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.title = element_blank(),
        legend.text = element_text(size = 12),
        legend.position = c(0.16, 1),
        legend.justification = c(1, 1),
        legend.box.background = element_rect(colour = "black")) +
  scale_y_continuous(expand = c(0, 0), limits = c(-12,8))

TMP_plot_5BC_16

Both Lib Boxplot

Combine the two data frames:

perfects_15_16_5BCsdr <- bind_rows(
  perfects15_5BCsdr %>% mutate(Lib = "Lib15"), # Values derived from Lib15 perfects_15_16_shared_5BCs
  perfects16_5BCsdr %>% mutate(Lib = "Lib16"), # Values derived from Lib16 perfects_15_16_shared_5BCs
  .id = "id")

perfects_15_16_5BCsdrlabs <- c("0","0.058","0.5","1","10","50","200")

Plot the median fitness of Perfects at each Trimethoprim concentration for Day 1 based on filtered 5BC dataset:

perfects_15_16_5BCsdr_plot <- ggplot(perfects_15_16_5BCsdr, aes(x = tmpname, y = fitness, fill = Lib)) +
  #geom_hline(yintercept = 0, linetype = "dashed", color = "black", size = 0.5) +
  geom_boxplot(position = "dodge", alpha=0.8) +
  xlab("Trimethoprim (ug/mL)") +
  ylab(expression(paste("Median Fitness (LogFC)"))) +
  scale_x_discrete(labels = perfects_15_16_5BCsdrlabs) +
  #ggtitle("Median Fitness \nPerfects (>5BCs)") +
  theme_minimal() +
  theme(
    axis.line = element_line(colour = 'black', size = 1.0),
    axis.ticks = element_line(colour = "black", size = 1.0),
    plot.title = element_text(size = 16, hjust = 0.5, face = "bold"),
    axis.text.x = element_text(size = 14),
    axis.text.y = element_text(size = 14),
    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.title = element_blank(),
    legend.text = element_text(size = 12),
    legend.position = "bottom") +
  scale_y_continuous(expand = c(0, 0), limits = c(-12, 8)) +
  scale_fill_manual(values = c("Lib15" = "#0072B2", "Lib16" = "#E69F00"),
                    labels = c("Codon1", "Codon2"))

# Display the plot
perfects_15_16_5BCsdr_plot

DHFR Genes

Load in the organism dataset associated with DHFR genes for mapping taxonomy to the phylogenetic tree.

#load in all the organisms info for unique BCs associated with DHFR genes: 
orginfo = read.csv("Perfects/INPUT/DHFR_chip_3_proteins_addedseveral.csv", head=TRUE)  # read csv file

Subset the dataset:

orginfo <- orginfo %>%
  select(-Lib.codon1, -Lib.codon2, -Construct., -Barcode) %>%
  dplyr::rename(IDfull=Accession) %>%
  #mutate(ID=strsplit(IDfull, split = ".")[1])
  tidyr::separate(IDfull, c("ID", "ver"), "\\.",remove = FALSE) %>%
  select(-ver)
Warning: Expected 2 pieces. Missing pieces filled with `NA` in 15 rows [4834, 4835, 5690, 5691, 5692, 5693, 5694, 5695, 5696, 5697, 5698, 5699, 5700, 5701, 5712].
names(orginfo)
 [1] "IDfull"        "ID"            "PctIdentEcoli" "Source"        "Definition"    "TaxID"         "Taxa1"        
 [8] "Taxa2"         "Taxa3"         "Taxa4"         "Sequence"     

Add the organism info to the perfects info as a new object called “perfects_tree”:

# Make a copy of "mutID" column called "ID" for matching with orginfo object:
perfects_15_16_shared_5BCs <- perfects_15_16_shared_5BCs %>%
  mutate(ID = mutID)

# Add orginfo to perfects_5BCs_tree
perfects_15_16_5BCs_tree <- right_join(orginfo,perfects_15_16_shared_5BCs,by="ID") %>%
  select(-IDfull,-ID.x,-seq.x,-pct_ident.x,-ID.y,-seq.y,-pct_ident.y)

Perfects % Similar E. coli Plot

Scatter Plot (>5 BCs)

Plot the Perfects fitness scores for D05 vs. D03 (Lib15) or D12 vs. D04 (Lib16) based on percent similarity of the each variant’s gene sequences to the E. coli DHFR gene sequence:

perfects_15_16_5BCs_scatter_plot <- ggplot() +
  geom_point(data = perfects_15_16_5BCs_tree, aes(x = PctIdentEcoli, y = fitD05D03), color = "#0072B2", alpha = 0.8) +
  geom_point(data = perfects_15_16_5BCs_tree, aes(x = PctIdentEcoli, y = fitD12D04), color = "#E69F00", alpha = 0.8) +
  xlab("Percent Similarity to E. coli DHFR") +
  ylab("Fitness (Log2 Fold Change)") +
  ggtitle("Median Fitness based on \n% Similarity to E. coli") +
  scale_x_log10() +
  theme_minimal() +
  theme(
    axis.line = element_line(colour = 'black', size = 0.5),
    axis.ticks = element_line(colour = "black", size = 0.5),
    plot.title = element_text(size = 16, hjust = 0.5, face = "bold"),
    axis.text.x = element_text(size = 12),
    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()) #+
  #scale_y_continuous(expand = c(0, 0), limits = c(-7, 1))

perfects_15_16_5BCs_scatter_plot

Binned Plot (>5 BCs)

Bin the % Similarity values in 10% increments and replot as boxplots:

# Convert data to long format
data_long <- perfects_15_16_5BCs_tree %>%
  pivot_longer(cols = c(fitD05D03, fitD12D04), names_to = "variable", values_to = "value")

# Define new x-axis labels
new_x_labels <- c("30-40%", "40-50%", "50-60%", "60-70%", "70-80%", "80-90%", "90-100%")

perfects_15_16_5BCs_binned_plot <- ggplot(data_long, aes(x = cut(PctIdentEcoli, breaks = seq(0, 1.0, by = 0.1)), y = value, fill = variable)) +
  geom_boxplot(position = "dodge", alpha = 0.8) +
  geom_hline(yintercept = -1, linetype = "dashed", color = "red") +
  xlab("Percent Similarity to E. coli DHFR (Binned)") +
  ylab("Median Fitness \n(Log2 Fold Change)") +
  ggtitle("Percent Similarity to E. coli \n(Perfects >5 BC)") +
  scale_x_discrete(labels = new_x_labels) +
  theme_minimal() +
  theme(
    axis.line = element_line(colour = 'black', size = 0.5),
    axis.ticks = element_line(colour = "black", size = 0.5),
    plot.title = element_text(size = 16, hjust = 0.5, face = "bold"),
    axis.text.x = element_text(size = 12),
    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.text = element_text(size = 12),
    legend.title = element_blank(),
    legend.position = "bottom") +
  scale_fill_manual(values = c("#0072B2", "#E69F00"), labels = c("Codon 1", "Codon 2"))

perfects_15_16_5BCs_binned_plot

Calculate the median percent similarity to E. coli across all homologs (5 BCs):

# Make a copy of "mutID" column called "ID" for matching with orginfo object:
perfects15_5BCs_PctIdentEcoli <- perfects15_5BCs

# Add orginfo to perfects_5BCs_tree
perfects15_5BCs_tree <- right_join(orginfo,perfects15_5BCs_PctIdentEcoli,by="ID") #%>%
  #select(-IDfull,-ID.x,-seq.x,-pct_ident.x,-ID.y,-seq.y,-pct_ident.y)
# Calculate mean and median PctIdentEcoli value across all homologs for Lib15
mean(perfects15_5BCs_tree$PctIdentEcoli)
[1] 0.4831003
median(perfects15_5BCs_tree$PctIdentEcoli)
[1] 0.4567901

Perfects Fitness Ridge Plots

This section uses the library(ggridges) package.

Subset the perfects_tree_5BCs object to retain only “ID” and fitness scores for select conditions. This code will transpose the individual fitness score columns into two columns with the fitness label and values, respectively, for each unique ID.

Day 1 (>5 BCs)

# Lib15
perfects15_tree_5BCs_d1 <- perfects_15_16_5BCs_tree %>%
  select(ID, fitD05D03, fitD06D03, fitD07D03, fitD08D03, fitD09D03, fitD10D03, fitD11D03) %>%
  pivot_longer(!ID, names_to = "fc", values_to = "val") %>%
  mutate(TMP = case_when(
    fc == "fitD05D03" ~ "0-TMP",
    fc == "fitD06D03" ~ "0.058-TMP",
    fc == "fitD07D03" ~ "0.5-TMP",
    fc == "fitD08D03" ~ "1.0-TMP",
    fc == "fitD09D03" ~ "10-TMP",
    fc == "fitD10D03" ~ "50-TMP",
    fc == "fitD11D03" ~ "200-TMP",
    TRUE ~ NA_character_))

# Lib16
perfects16_tree_5BCs_d1 <- perfects_15_16_5BCs_tree %>%
  select(ID, fitD12D04, fitE01D04, fitE02D04, fitE03D04, fitE04D04, fitE05D04, fitE06D04) %>%
  pivot_longer(!ID, names_to = "fc", values_to = "val") %>%
  mutate(TMP = case_when(
    fc == "fitD12D04" ~ "0-TMP",
    fc == "fitE01D04" ~ "0.058-TMP",
    fc == "fitE02D04" ~ "0.5-TMP",
    fc == "fitE03D04" ~ "1.0-TMP",
    fc == "fitE04D04" ~ "10-TMP",
    fc == "fitE05D04" ~ "50-TMP",
    fc == "fitE06D04" ~ "200-TMP",
    TRUE ~ NA_character_))
# Combine the two data frames
perfects_15_16_tree_5BCs_d1 <- bind_rows(
  perfects15_tree_5BCs_d1 %>% mutate(Lib = "Lib15"),
  perfects16_tree_5BCs_d1 %>% mutate(Lib = "Lib16"),
  .id = "id")

Plot Perfects fitness scores based on Supplementation treatment for first sampling time point

perfects_15_16_tree_5BCs_d1_order <- c("200-TMP", "50-TMP", "10-TMP", "1.0-TMP", "0.5-TMP", "0.058-TMP", "0-TMP")

# Rename the levels in the Lib column
perfects_15_16_tree_5BCs_d1$Lib <- factor(perfects_15_16_tree_5BCs_d1$Lib,
                                          levels = c("Lib15", "Lib16"),
                                          labels = c("Codon1", "Codon2"))

tmp_ridges_15_16_d1 <- ggplot(perfects_15_16_tree_5BCs_d1, aes(x = val, y = factor(TMP, level = perfects_15_16_tree_5BCs_d1_order), fill = Lib)) +
  geom_density_ridges(alpha = 0.7) +
  scale_y_discrete(labels = c('200 μg/mL TMP', '50 μg/mL TMP', '10 μg/mL TMP', '1 μg/mL TMP', '0.5 μg/mL TMP', '0.058 μg/mL TMP', 'Complementation')) +
  xlab("Median Fitness (LogFC)") +
  ylab("Selection Condition (ug/mL TMP)") +
  #ggtitle("Dose Response \nDay 1") +
  theme_minimal() +
  theme(
    axis.line = element_line(colour = 'black', size = 0.5),
    axis.ticks = element_line(colour = "black", size = 0.5),
    plot.title = element_text(size = 14, hjust = 0.5, face = "bold"),
    axis.text.x = element_text(size = 12),
    axis.text.y = element_text(size = 12),
    axis.title.x = element_text(size = 14),
    axis.title.y = element_blank(),
    panel.background = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    legend.position = "bottom",
    legend.text = element_text(size = 12),
    legend.title = element_blank()) +
  scale_x_continuous(limits = c(-15, 10)) +
  scale_fill_manual(values = c("Codon1" = "#0072B2", "Codon2" = "#E69F00"), name = "Library")

# Display the plot
tmp_ridges_15_16_d1

patch6 <- (perfects_15_16_5BCs_binned_plot / perfects_15_16_5BCsdr_plot) | (tmp_ridges_15_16_d1)
patch6

Perfects Summary

Plot the mapped perfects for each library and when the shared perfects are combined with the unique perfects:

# Create the data frame
mapped.data <- data.frame(
  Library = c("Codon1", "Codon2", "Both"),
  Perfects = c(1048, 904, 1208),
  Percent = c(68.2, 58.9, 78.7)
)

# Create a ggplot object
L15.L16.1208.Mapped.Barchart <- ggplot(mapped.data) +
  # Add a horizontal line at y=0
  geom_hline(yintercept = 0, color = "black") +
  # Wider bar for "Both" plotted behind
  geom_bar(data = subset(mapped.data, Library == "Both"),
           aes(x = 1.5, y = Perfects, fill = Library),
           stat = "identity", 
           width = 0.75, 
           alpha = 0.5) +
  # Bar plot for Codon1 and Codon2
  geom_bar(data = subset(mapped.data, Library != "Both"),
           aes(x = as.numeric(factor(Library, levels = c("Codon1", "Codon2"))), y = Perfects, fill = Library), 
           stat = "identity", 
           width = 0.6) +
  scale_x_continuous(breaks = c(1, 2), labels = c("Codon1", "Codon2"), 
                     limits = c(0.5, 2.5)) +
  scale_y_continuous(
    name = "Assemblies Represented \n(1536 Designs)", 
    limits = c(0, 1536),
    expand = c(0, 0),  # Remove padding
    sec.axis = sec_axis(~ . * 100 / 1536, name = "Library Representation (%)", breaks = seq(0, 100, by = 20))
  ) +
  # Add an invisible layer to ensure the secondary y-axis is plotted
  geom_point(data = subset(mapped.data, Library != "Both"),
             aes(x = as.numeric(factor(Library, levels = c("Codon1", "Codon2"))), y = Percent * 1536 / 100), 
             color = NA) +
  theme_minimal() +
  theme(
    axis.title.y.right = element_text(color = "red", size = 14),
    axis.text.y.right = element_text(color = "red", size = 12),
    axis.ticks.y.right = element_line(color = "red"),
    axis.title.x = element_text(size = 14),
    axis.text.x = element_blank(),
    axis.title.y = element_text(size = 14),
    axis.text.y = element_text(size = 12),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    panel.border = element_blank(),
    legend.title = element_blank(),
    legend.text = element_text(size = 12),
    legend.position = "none",
    axis.line = element_line(color = "black", size=1.0),
    axis.ticks = element_line(color = "black", size=1.0),
    axis.line.x.top = element_blank(),
    axis.ticks.y.left = element_line(color = "black"),
    axis.ticks.length = unit(0.2, "cm")
  ) +
  scale_fill_manual(values = c("Codon1" = "#0072B2", "Codon2" = "#E69F00", "Both" = "lightblue4"),
                    breaks = c("Codon1", "Codon2", "Both")) +
  labs(x = "Mapped \nAssemblies") +
  coord_cartesian(xlim = c(0.5, 2.5), ylim = c(0, 1536)) +
  guides(fill = guide_legend(override.aes = list(alpha = 1)))

# Display the plot
print(L15.L16.1208.Mapped.Barchart)

Plot the perfects recovered in the KO model for each library and the combined perfects (shared perfects + unique perfects):

# Create the data frame
recovered.data <- data.frame(
  Library = c("Codon1", "Codon2", "Both"),
  Perfects = c(961, 818, 1136),
  Percent = c(80, 68, 94)
)

# Create a ggplot object
L15.L16.1150.Recovered.Barchart <- ggplot(recovered.data) +
  # Add a horizontal line at y=0
  geom_hline(yintercept = 0, color = "black") +
  # Wider bar for "Both" plotted behind
  geom_bar(data = subset(recovered.data, Library == "Both"),
           aes(x = 1.5, y = Perfects, fill = Library),
           stat = "identity", 
           width = 0.75, 
           alpha = 0.5) +
  # Bar plot for Codon1 and Codon2
  geom_bar(data = subset(recovered.data, Library != "Both"),
           aes(x = as.numeric(factor(Library, levels = c("Codon1", "Codon2"))), y = Perfects, fill = Library), 
           stat = "identity", 
           width = 0.6) +
  scale_x_continuous(breaks = c(1, 2), labels = c("Codon1", "Codon2"), 
                     limits = c(0.5, 2.5)) +
  scale_y_continuous(
    name = "Homologs Represented \n(1208 Assembled)", 
    limits = c(0, 1208),
    expand = c(0, 0),  # Remove padding
    sec.axis = sec_axis(~ . * 100 / 1208, name = "Library Representation (%)", breaks = seq(0, 100, by = 20))
  ) +
  # Add an invisible layer to ensure the secondary y-axis is plotted
  geom_point(data = subset(recovered.data, Library != "Both"),
             aes(x = as.numeric(factor(Library, levels = c("Codon1", "Codon2"))), y = Percent * 1208 / 100), 
             color = NA) +
  theme_minimal() +
  theme(
    axis.title.y.right = element_blank(),
    axis.text.y.right = element_blank(),
    axis.ticks.y.right = element_blank(),
    axis.line.y.right = element_blank(),
    axis.title.x = element_text(size = 15),
    axis.text.x = element_blank(),
    axis.title.y = element_text(size = 18),
    axis.text.y = element_text(size = 16),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    panel.border = element_blank(),
    legend.title = element_blank(),
    legend.text = element_text(size = 14),
    legend.position = "none",
    axis.line = element_line(color = "black", size=1.0),
    axis.ticks = element_line(color = "black", size=1.0),
    axis.line.x.top = element_blank(),
    axis.ticks.y.left = element_line(color = "black"),
    axis.ticks.length = unit(0.2, "cm")
  ) +
  scale_fill_manual(values = c("Codon1" = "#0072B2", "Codon2" = "#E69F00", "Both" = "lightblue4"),
                    breaks = c("Codon1", "Codon2", "Both")) +
  labs(x = "Recovered \nHomologs") +
  coord_cartesian(xlim = c(0.5, 2.5), ylim = c(0, 1250)) +
  guides(fill = guide_legend(override.aes = list(alpha = 1)))

# Display the plot
print(L15.L16.1150.Recovered.Barchart)

Plot the perfects that complement in the KO model for each library and the combined perfects (shared perfects + unique perfects):

# Create the data frame
complement.data <- data.frame(
  Library = c("Codon1", "Codon2", "Both"),
  Perfects = c(417, 377, 600),
  Percent = c(35, 31, 50)
)

# Create a ggplot object
L15.L16.1150.Complement.Barchart <- ggplot(complement.data) +
  # Add a horizontal line at y=0
  geom_hline(yintercept = 0, color = "black") +
  # Wider bar for "Both" plotted behind
  geom_bar(data = subset(complement.data, Library == "Both"),
           aes(x = 1.5, y = Perfects, fill = Library),
           stat = "identity", 
           width = 0.75, 
           alpha = 0.5) +
  # Bar plot for Codon1 and Codon2
  geom_bar(data = subset(complement.data, Library != "Both"),
           aes(x = as.numeric(factor(Library, levels = c("Codon1", "Codon2"))), y = Perfects, fill = Library), 
           stat = "identity", 
           width = 0.6) +
  scale_x_continuous(breaks = c(1, 2), labels = c("Codon1", "Codon2"), 
                     limits = c(0.5, 2.5)) +
  scale_y_continuous(
    name = " ", 
    limits = c(0, 1208),
    expand = c(0, 0),  # Remove padding
    sec.axis = sec_axis(~ . * 100 / 1208, name = "Library Representation (%)", breaks = seq(0, 100, by = 20))
  ) +
  # Add an invisible layer to ensure the secondary y-axis is plotted
  geom_point(data = subset(complement.data, Library != "Both"),
             aes(x = as.numeric(factor(Library, levels = c("Codon1", "Codon2"))), y = Percent * 1208 / 80), 
             color = NA) +
  theme_minimal() +
  theme(
    axis.title.y.right = element_text(color = "red", size = 18),
    axis.text.y.right = element_text(color = "red", size = 16),
    axis.ticks.y.right = element_line(color = "black"),
    axis.ticks.y = element_blank(),
    axis.title.x = element_text(size = 15),
    axis.text.x = element_blank(),
    axis.title.y = element_blank(),
    axis.text.y = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    panel.border = element_blank(),
    legend.title = element_blank(),
    legend.text = element_blank(),
    legend.position = "none",
    axis.line = element_line(color = "black", size=1.0),
    axis.ticks = element_line(color = "black", size=1.0),
    axis.line.y.right = element_line(color = "black"),
    axis.line.y = element_blank(),
    axis.line.x.top = element_blank(),
    axis.ticks.length = unit(0.2, "cm")
  ) +
  scale_fill_manual(values = c("Codon1" = "#0072B2", "Codon2" = "#E69F00", "Both" = "lightblue4"),
                    breaks = c("Codon1", "Codon2", "Both")) +
  labs(x = "Complementing \nHomologs") +  # Changed x-axis label
  coord_cartesian(xlim = c(0.5, 2.5), ylim = c(0, 1250)) +
  guides(fill = guide_legend(override.aes = list(alpha = 1)))

# Display the plot
print(L15.L16.1150.Complement.Barchart)

patch7 <- L15.L16.1150.Recovered.Barchart | L15.L16.1150.Complement.Barchart
patch7

Phylogenetic Trees

Full Tree Alignment (1,208)

This section uses the library(ggtree) and library(castor) packages.

Create a full unique perfects mutID dataframe based on mutIDinfo.15.16.zeros.shared and mutIDinfo.15.16.zeros.unique to generate a phylogenetic tree composed on 1,208 mutID sequences.

# Subset relevant columns before combining datasets:

# Shared mutIDs
mutIDinfo.15.16.zeros.shared.perfects <- mutIDinfo.15.16.zeros.shared %>%
  filter(mutations.x == 0) %>%
  select(mutID, seq = seq.x)

# Unique mutIDs
mutIDinfo.15.16.zeros.unique.perfects <- mutIDinfo.15.16.zeros.unique %>%
  filter(mutations == 0) %>%
  select(mutID, seq)

# Combine both datasets
mutIDinfo.15.16.zeros.all.perfects <- rbind(mutIDinfo.15.16.zeros.shared.perfects,
                                        mutIDinfo.15.16.zeros.unique.perfects)

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

# Rename "mutID" to "ID" to merge with `orginfo` df:
mutIDinfo.15.16.zeros.all.perfects <- mutIDinfo.15.16.zeros.all.perfects %>% rename(ID = mutID)

# Merge "TaxID" and "PctIdentEcoli" from `orginfo` to `mutIDinfo.15.16.zeros.all.perfects`:
mutIDinfo.15.16.zeros.all.perfects <- mutIDinfo.15.16.zeros.all.perfects %>%
  left_join(orginfo %>% select(ID, TaxID, PctIdentEcoli), by = "ID")

Create a FASTA file containing the ID and its associated protein sequence for alignment

# Collect the sequences in FASTA format
mutIDinfo.15.16.zeros.all.perfects_fasta_content <- paste(">",mutIDinfo.15.16.zeros.all.perfects$ID, "\n", mutIDinfo.15.16.zeros.all.perfects$seq, "\n", sep = "", collapse = "")

# Define the file path in the working directory
mutIDinfo.15.16.zeros.all.perfects_fasta_path <- file.path(getwd(), "Perfects/TREES/DHFR.Lib.15.16.ID.all.perfects.mapped.fasta")

# Write the FASTA content to the file
writeLines(mutIDinfo.15.16.zeros.all.perfects_fasta_content, con = mutIDinfo.15.16.zeros.all.perfects_fasta_path)

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 Perfects/TREES/DHFR.Lib.15.16.ID.all.perfects.mapped.fasta -o Perfects/TREES/DHFR.Lib.15.16.ID.all.perfects.mapped.aligned.fasta --outfmt=fa --force

Use FastTree (phylogenetic tree building program) to infer the tree from the aligned amino acid sequences:

# ML Model: Jones-Taylor-Thorton
# chmod +x FastTree
./Scripts/FastTree Perfects/TREES/DHFR.Lib.15.16.ID.all.perfects.mapped.aligned.fasta > Perfects/TREES/DHFR.Lib.15.16.ID.all.perfects.mapped.tree.newick

Newick Tree File

Import the newick tree file based on the sequence alignment of shared perfects derived from Lib15 & Lib16 mapping file:

# Full tree alignment (1,208 mutID)
Alltree15 <- read.tree("Perfects/TREES/DHFR.Lib.15.16.ID.all.perfects.mapped.tree.newick")   # newick format
Alltree15

Phylogenetic tree with 1208 tips and 1206 internal nodes.

Tip labels:
  WP_011201021, WP_005655824, WP_011272274, WP_006995561, WP_005632134, WP_009500922, ...
Node labels:
  , 0.917, 0.957, 0.898, 0.923, 0.819, ...

Unrooted; includes branch lengths.

Extract the tip labels from the newick tree file to match with NCBI taxonomy for downstream plotting:

# Extract tip labels from Alltree15
Alltree15_tip_labels <- Alltree15$tip.label

# Create a new data frame with unique tip labels
Alltree15_tip_labels_df <- data.frame(tip.label = unique(Alltree15_tip_labels))

# Print the first few rows of the new data frame
head(Alltree15_tip_labels_df)

Match each tip.label ID in Alltree15 with it’s associated TaxID from orginfo dataframe:

# Rename column "tip.label" to "ID"
colnames(Alltree15_tip_labels_df) <- c("ID")

# Merge orginfo with Alltree15_tip_labels_df based on the shared ID
Alltree15_taxa <- merge(Alltree15_tip_labels_df, orginfo, by = "ID", all.x = TRUE)

# Print the first few rows of the merged data frame
head(Alltree15_taxa)

NCBI Taxonomy File

Import the full up-to-date NCBI taxonomy dataset containing 2,580,388 unique TaxID.

# Import updated NCBI taxonomy mapping file
ncbi_taxa = read.csv("Perfects/INPUT/all.ncbi.taxa.lineage.csv", head=TRUE)  # read csv file
Warning: EOF within quoted string
# Convert TaxID column from chr to int
ncbi_taxa$TaxID <- as.integer(ncbi_taxa$TaxID)
Warning: NAs introduced by coercion
# Print the first few rows of the NCBI taxonomy data frame
head(ncbi_taxa)
# Merge the NCBI taxonomy columns to Alltree15_taxa based on shared TaxID
Alltree15_taxa_merged <- Alltree15_taxa
Alltree15_taxa_merged$NCBI.name <- NA
Alltree15_taxa_merged$NCBI.superkingdom <- NA
Alltree15_taxa_merged$NCBI.phylum <- NA
Alltree15_taxa_merged$NCBI.class <- NA
Alltree15_taxa_merged$NCBI.order <- NA
Alltree15_taxa_merged$NCBI.family <- NA
Alltree15_taxa_merged$NCBI.genus <- NA
Alltree15_taxa_merged$NCBI.species <- NA

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

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

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

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

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

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

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

# NCBI.family
Alltree15_taxa_merged$NCBI.species[Alltree15_taxa_merged$TaxID %in% ncbi_taxa$TaxID] <- ncbi_taxa$NCBI.species[match(Alltree15_taxa_merged$TaxID[Alltree15_taxa_merged$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"
Alltree15_taxa_merged$NCBI.phylum <- ifelse(Alltree15_taxa_merged$NCBI.phylum == "Pseudomonadota", Alltree15_taxa_merged$NCBI.class, Alltree15_taxa_merged$NCBI.phylum)

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

# Print the first few rows of the Alltree15_taxa_merged data frame
head(Alltree15_taxa_merged)

DHFR Diversity Trees

Pct Similarity E coli

Plot a circular phylogenetic tree with color-scale based on phylogenetic distance between each DHFR homolog relative to the E. coli DHFR gene.

phylotree_NCBI_ecoli_ident <- ggtree(Alltree15, layout="circular", branch.length="none") %<+% 
  Alltree15_taxa_merged + 
  aes(color=PctIdentEcoli*100) +
  geom_tippoint(aes(color=PctIdentEcoli*100), size=1, alpha=0.8) +
  geom_tiplab2(aes(color=PctIdentEcoli*100, label=NCBI.name, angle=angle), size=1) +
  theme(legend.position="left") + 
  scale_colour_gradient2("Sequence\nIdentity (%)", low="blue", mid="green", high="red",
                         midpoint=(100+min(Alltree15_taxa_merged$PctIdentEcoli)*100)/2)

phylotree_NCBI_ecoli_ident

Plot with only the pathogenic homologs labelled:

# Define the specific NCBI.name values you want to label
pathogenic_labels <- c("Anaerococcus tetradius", "Bacillus cereus", "Escherichia coli", "Haemophilus influenzae", 
                       "Salmonella enterica", "Staphylococcus aureus", "Streptococcus pneumoniae", "Vibrio cholerae", 
                       "Enterococcus faecium", "Klebsiella pneumoniae", "Acinetobacter baumannii", "Pseudomonas aeruginosa", 
                       "Proteus mirabilis", "Bordetella holmesii", "Mycoplasmoides pneumoniae", "Chlamydia pneumoniae")

phylotree_NCBI_ecoli_ident_pathogenic <- ggtree(Alltree15, layout="circular", branch.length="none") %<+% 
  Alltree15_taxa_merged + 
  aes(color=PctIdentEcoli*100) +
  geom_tippoint(aes(color=PctIdentEcoli*100), size=1, alpha=0.8) +
  geom_tiplab2(aes(color=PctIdentEcoli*100, 
                   label=ifelse(NCBI.name %in% pathogenic_labels, NCBI.name, ""), 
                   angle=angle), 
               size=1) +
  theme(legend.position="left") + 
  scale_colour_gradient2("Sequence\nIdentity (%)", low="blue", mid="green", high="red",
                         midpoint=(100+min(Alltree15_taxa_merged$PctIdentEcoli)*100)/2)

phylotree_NCBI_ecoli_ident_pathogenic

Taxonomic Rings

The following section builds on the initial DHFR phylogenetic tree and adds the NCBI taxonomic lineages as an outer ring to display the breadth of sequence diversity in the 1,536-DHFR designed library. However, this tree only contains 643 unique branch tips (41% of full library diversity) from the recovered shared perfects between libraries.

Distinct Phylum Colors for Plotting

# Generate distinct colors for each unique value in the "NCBI.phylum" column
distinct_colors_ncbi <- rainbow(length(unique(Alltree15_taxa_merged$NCBI.phylum)))

# Reverse the order of colors
#distinct_colors_ncbi <- rev(distinct_colors_ncbi)

# Create a new column "NCBI.phylum_colors" and assign colors based on the unique values in "NCBI.phylum"
Alltree15_taxa_merged <- Alltree15_taxa_merged %>%
  mutate(NCBI.phylum_colors = distinct_colors_ncbi[as.integer(factor(NCBI.phylum))])

#Establish color scheme for plotting
NCBIphyloColor <- Alltree15_taxa_merged %>%
  select(c("NCBI.phylum", "NCBI.phylum_colors")) %>%
  distinct()
NCBIphyloColor <- NCBIphyloColor[order(NCBIphyloColor$NCBI.phylum, decreasing=FALSE),]

# List phylum in alphabetical order for legend and plotting
Alltree15_taxa_merged$NCBI.phylum <- factor(Alltree15_taxa_merged$NCBI.phylum, levels=NCBIphyloColor$NCBI.phylum)

Distinct Class Colors for Plotting

# Generate distinct colors for each unique value in the "NCBI.class" column
distinct_colors_ncbi_class <- rainbow(length(unique(Alltree15_taxa_merged$NCBI.class)))

# Create a new column "NCBI.class_colors" and assign colors based on the unique values in "NCBI.class"
Alltree15_taxa_merged <- Alltree15_taxa_merged %>%
  mutate(NCBI.class_colors = distinct_colors_ncbi_class[as.integer(factor(NCBI.class))])

#Establish color scheme for plotting
NCBIclassColor <- Alltree15_taxa_merged %>%
  select(c("NCBI.class", "NCBI.class_colors")) %>%
  distinct()
NCBIclassColor <- NCBIclassColor[order(NCBIclassColor$NCBI.class, decreasing=FALSE),]

# List phylum in alphabetical order for legend and plotting
Alltree15_taxa_merged$NCBI.class <- factor(Alltree15_taxa_merged$NCBI.class, levels=NCBIclassColor$NCBI.class)

Build the initial DHFR tree with color-scale based on phylogenetic distance between each DHFR homolog relative to the E. coli DHFR gene and add an additional ring representing taxonomic lineages at the Phylum-level.

Note that the phylum “Pseudomonadota” has been replaced with it’s associated classes (“Alphaproteobacteria”, “Betaproteobacteria”, etc.). This tree includes Archaea (Euryarchaeota) and dsDNA Viruses (Uroviricota), but no Eukaryota from the original 1,536-DHFR library.

phylotree_NCBI_ecoli_phylum_p1 <- ggtree(Alltree15, layout="circular", branch.length="none") %<+% 
  Alltree15_taxa_merged + 
  aes(color=PctIdentEcoli*100) +
  geom_tippoint(aes(color=PctIdentEcoli*100), size=1, alpha=0.8) +
  theme(legend.position="left") + 
  scale_colour_gradient2("Sequence\nIdentity (%)", low="blue", mid="green", high="red",
                         midpoint=(100+min(Alltree15_taxa_merged$PctIdentEcoli)*100)/2)

phylotree_NCBI_ecoli_phylum_p2 <- phylotree_NCBI_ecoli_phylum_p1 +
  new_scale_fill() +
  geom_fruit(
    geom=geom_tile,
    mapping=aes(fill=NCBI.phylum),
    width=4,
    offset=0.1
  ) +
  scale_fill_manual(
    name="Phylum",
    values=NCBIphyloColor$NCBI.phylum_colors,
    guide=guide_legend(keywidth=0.3, keyheight=0.3, ncol=1)
  ) +
  theme(
    legend.title=element_text(size=10), 
           legend.text=element_text(size=8),
           legend.spacing.y = unit(0.2, "cm")
  )

phylotree_NCBI_ecoli_phylum_p2

Build the initial DHFR tree with color-scale based on phylogenetic distance between each DHFR homolog relative to the E. coli DHFR gene and add an additional ring representing taxonomic lineages at the Class-level.

phylotree_NCBI_ecoli_class_p1 <- ggtree(Alltree15, layout="circular", branch.length="none") %<+% 
  Alltree15_taxa_merged + 
  aes(color=PctIdentEcoli*100) +
  geom_tippoint(aes(color=PctIdentEcoli*100), size=1, alpha=0.8) +
  theme(legend.position="left") + 
  scale_colour_gradient2("Sequence\nIdentity (%)", low="blue", mid="green", high="red",
                         midpoint=(100+min(Alltree15_taxa_merged$PctIdentEcoli)*100)/2)

phylotree_NCBI_ecoli_class_p2 <- phylotree_NCBI_ecoli_class_p1 +
  new_scale_fill() +
  geom_fruit(
    geom=geom_tile,
    mapping=aes(fill=NCBI.class),
    width=4,
    offset=0.1
  ) +
  scale_fill_manual(
    name="Class",
    values=NCBIclassColor$NCBI.class_colors,
    guide=guide_legend(keywidth=0.3, keyheight=0.3, ncol=1)
  ) +
  theme(
    legend.title=element_text(size=10), 
           legend.text=element_text(size=8),
           legend.spacing.y = unit(0.2, "cm")
  )

phylotree_NCBI_ecoli_class_p2

Show both version of the phylogeny tree (Phylum and Class level annotations):

patch8 <- (phylotree_NCBI_ecoli_phylum_p2 | phylotree_NCBI_ecoli_class_p2)
patch8

Taxonomic Tip Labels

Re-build the phylogenetic tree but color the branches by the NCBI phylum-level taxonomy (instead of by pctIdent E. coli). Leave the phylum name labels on the tree as reference.

phylotree_NCBI_ecoli_phylum_names_p1 <- ggtree(Alltree15, layout="circular", branch.length="none") %<+%
  Alltree15_taxa_merged + 
  aes(color=NCBI.phylum) +
  geom_tippoint(aes(color=NCBI.phylum), size=1, alpha=0.8) +
  geom_tiplab2(aes(color=NCBI.phylum, label=NCBI.phylum, angle=angle), size=1)

#p13NCBI_15taxa2

phylotree_NCBI_ecoli_phylum_names_p2 <- phylotree_NCBI_ecoli_phylum_names_p1 +
  geom_tippoint(
    mapping=aes(colour=NCBI.phylum),
    size=1.5,
    stroke=0,
    alpha=0.4
  ) +
  scale_colour_manual(
    name="Phylum",
    values=NCBIphyloColor$NCBI.phylum_colors,
    guide=guide_legend(keywidth=0.3,
                            keyheight=0.3,
                            ncol=1,
                            override.aes=list(size=2,alpha=1),
                            order=1)
  ) +
  theme(
    legend.title=element_text(size=12),
         legend.text=element_text(size=10),
         legend.spacing.y = unit(0.1, "cm")
  )

phylotree_NCBI_ecoli_phylum_names_p2

Re-build the phylogenetic tree but color the branches by the NCBI class-level taxonomy (instead of by pctIdent E. coli). Leave the class name labels on the tree as reference.

phylotree_NCBI_ecoli_class_names_p1 <- ggtree(Alltree15, layout="circular", branch.length="none") %<+%
  Alltree15_taxa_merged + 
  aes(color=NCBI.class) +
  geom_tippoint(aes(color=NCBI.class), size=1, alpha=0.8) +
  geom_tiplab2(aes(color=NCBI.class, label=NCBI.class, angle=angle), size=1)

#p13NCBI_15taxa2

phylotree_NCBI_ecoli_class_names_p2 <- phylotree_NCBI_ecoli_class_names_p1 +
  geom_tippoint(
    mapping=aes(colour=NCBI.class),
    size=1.5,
    stroke=0,
    alpha=0.4) +
  scale_colour_manual(
    name="Class",
    values=NCBIclassColor$NCBI.class_colors,
    guide=guide_legend(keywidth=0.3,
                            keyheight=0.3,
                            ncol=1,
                            override.aes=list(size=2,alpha=1),
                            order=1)) +
  theme(
    legend.title=element_text(size=12),
         legend.text=element_text(size=10),
         legend.spacing.y = unit(0.1, "cm"))

phylotree_NCBI_ecoli_class_names_p2

Taxa Summary Tables

Taxonomy: Count the number of unique IDs associated with each taxonomic level:

# Count unique values in each column
unique_taxa_counts <- Alltree15_taxa_merged %>%
  summarise(across(c(NCBI.superkingdom, NCBI.phylum, NCBI.class, NCBI.order, NCBI.family, NCBI.genus, NCBI.species),
      ~ n_distinct(.)))

# Print the result
print(unique_taxa_counts)

Domain: Count the number of unique IDs associated with each superkingdom (domain):

# Sum the number of rows for each unique NCBI.phylum
domain_counts <- Alltree15_taxa_merged %>%
  group_by(NCBI.superkingdom) %>%
  summarise(count = n()) %>%
  arrange(desc(count))

# Print the table
print(domain_counts)

Phylum: Count the number of unique IDs associated with each Phylum:

# Sum the number of rows for each unique NCBI.phylum
phylum_counts <- Alltree15_taxa_merged %>%
  group_by(NCBI.phylum) %>%
  summarise(count = n()) %>%
  arrange(desc(count))

# Print the table
print(phylum_counts)

Class: Count the number of unique IDs associated with each Class:

# Sum the number of rows for each unique NCBI.class
class_counts <- Alltree15_taxa_merged %>%
  group_by(NCBI.class) %>%
  summarise(count = n()) %>%
  arrange(desc(count))

# Print the table
print(class_counts)

Order: Count the number of unique IDs associated with each Order:

# Sum the number of rows for each unique NCBI.class
order_counts <- Alltree15_taxa_merged %>%
  group_by(NCBI.order) %>%
  summarise(count = n()) %>%
  arrange(desc(count))

# Print the table
print(order_counts)

Family: Count the number of unique IDs associated with each Family:

# Sum the number of rows for each unique NCBI.class
family_counts <- Alltree15_taxa_merged %>%
  group_by(NCBI.family) %>%
  summarise(count = n()) %>%
  arrange(desc(count))

# Print the table
print(family_counts)

Genus: Count the number of unique IDs associated with each Genus:

# Sum the number of rows for each unique NCBI.class
genus_counts <- Alltree15_taxa_merged %>%
  group_by(NCBI.genus) %>%
  summarise(count = n()) %>%
  arrange(desc(count))

# Print the table
print(genus_counts)

Species: Count the number of unique IDs associated with each Species:

# Sum the number of rows for each unique NCBI.class
species_counts <- Alltree15_taxa_merged %>%
  group_by(NCBI.species) %>%
  summarise(count = n()) %>%
  arrange(desc(count))

# Print the table
print(species_counts)

Lib15 Fitness Tree (797)

Subsetting Data

Retain unique DHFR perfects with a fitness score >= -1 and a minimum of 5 BCs (numprunedBCs > 4) in Lib15 (fitD05D03):

# Subset dataset for tree building
L15_perfects_complementation_tree <- perfects15_5BCs %>%
  select(mutID, seq,
         fitD05D03, fitD06D03, fitD07D03, fitD08D03, fitD09D03, fitD10D03, fitD11D03)

Count perfects associated with fitD05D03 (Complementation):

# Count unique perfects in dataset
count_unique_comp_perfects <- L15_perfects_complementation_tree %>%
  filter(!is.na(fitD05D03)) %>%
  distinct(fitD05D03) %>%
  nrow()
print(paste("Number of unique perfects:", count_unique_comp_perfects))
[1] "Number of unique perfects: 780"
# Count unique perfects with fitness greater than -1 in fitD05D03 (Complementation):
count_unique_fit_comp_high <- L15_perfects_complementation_tree %>%
  filter(!is.na(fitD05D03) & fitD05D03 >= -1) %>%
  distinct(fitD05D03) %>%
  nrow()
print(paste("Number of unique perfects with fitD05D03 values greater than -1:", count_unique_fit_comp_high))
[1] "Number of unique perfects with fitD05D03 values greater than -1: 412"
# Count unique perfects with fitness less than -1 in fitD05D03 (Complementation):
count_unique_fit_comp_low <- L15_perfects_complementation_tree %>%
  filter(!is.na(fitD05D03) & fitD05D03 <= -1) %>%
  distinct(fitD05D03) %>%
  nrow()
print(paste("Number of unique perfects with fitD05D03 values less than -1:", count_unique_fit_comp_low))
[1] "Number of unique perfects with fitD05D03 values less than -1: 368"

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

# Rename "mutID" to "ID" to merge with `orginfo` df:
L15_perfects_complementation_tree <- L15_perfects_complementation_tree %>% rename(ID = mutID)

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

Create a FASTA file containing the ID and its associated protein sequence for alignment

# Collect the sequences in FASTA format
L15_perfects_complementation_tree_fasta_content <- paste(">",L15_perfects_complementation_tree$ID, "\n", L15_perfects_complementation_tree$seq, "\n", sep = "", collapse = "")

# Define the file path in the working directory
L15_perfects_complementation_tree_fasta_path <- file.path(getwd(), 
                                                          "Perfects/TREES/Lib.15.5BCs.perfects.complement.fasta")

# Write the FASTA content to the file
writeLines(L15_perfects_complementation_tree_fasta_content, con = L15_perfects_complementation_tree_fasta_path)

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 Perfects/TREES/Lib.15.5BCs.perfects.complement.fasta -o Perfects/TREES/Lib.15.5BCs.perfects.complement.aligned.fasta --outfmt=fa --force

Use FastTree (phylogenetic tree building program) to infer the tree from the aligned amino acid sequences:

# ML Model: Jones-Taylor-Thorton
# chmod +x FastTree
./Scripts/FastTree Perfects/TREES/Lib.15.5BCs.perfects.complement.aligned.fasta > Perfects/TREES/Lib.15.5BCs.perfects.complement.tree.newick
FastTree Version 2.1.11 No SSE3
Alignment: Perfects/TREES/Lib.15.5BCs.perfects.complement.aligned.fasta
Amino acid distances: BLOSUM45 Joins: balanced Support: SH-like 1000
Search: Normal +NNI +SPR (2 rounds range 10) +ML-NNI opt-each=1
TopHits: 1.00*sqrtN close=default refresh=0.80
ML Model: Jones-Taylor-Thorton, CAT approximation with 20 rate categories
      0.12 seconds: Joined    200 of    794
      0.24 seconds: Joined    500 of    794
      0.35 seconds: Joined    700 of    794
Initial topology in 0.40 seconds
Refining topology: 39 rounds ME-NNIs, 2 rounds ME-SPRs, 19 rounds ML-NNIs
      0.45 seconds: ME NNI round 3 of 39, 201 of 795 splits, 4 changes (max delta 0.015)
      0.57 seconds: SPR round   1 of   2, 201 of 1592 nodes
      0.71 seconds: SPR round   1 of   2, 501 of 1592 nodes
      0.85 seconds: SPR round   1 of   2, 801 of 1592 nodes
      0.96 seconds: SPR round   1 of   2, 1001 of 1592 nodes
      1.10 seconds: SPR round   1 of   2, 1301 of 1592 nodes
      1.23 seconds: ME NNI round 14 of 39, 1 of 795 splits
      1.33 seconds: SPR round   2 of   2, 101 of 1592 nodes
      1.44 seconds: SPR round   2 of   2, 301 of 1592 nodes
      1.56 seconds: SPR round   2 of   2, 501 of 1592 nodes
      1.66 seconds: SPR round   2 of   2, 701 of 1592 nodes
      1.79 seconds: SPR round   2 of   2, 1001 of 1592 nodes
      1.90 seconds: SPR round   2 of   2, 1301 of 1592 nodes
      2.01 seconds: SPR round   2 of   2, 1501 of 1592 nodes
Total branch-length 148.069 after 2.13 sec
      2.13 seconds: ML Lengths 1 of 795 splits
      2.24 seconds: ML Lengths 101 of 795 splits
      2.34 seconds: ML Lengths 201 of 795 splits
      2.45 seconds: ML Lengths 301 of 795 splits
      2.55 seconds: ML Lengths 401 of 795 splits
      2.66 seconds: ML Lengths 501 of 795 splits
      2.76 seconds: ML Lengths 601 of 795 splits
      2.86 seconds: ML Lengths 701 of 795 splits
      3.36 seconds: ML NNI round 1 of 19, 101 of 795 splits, 16 changes (max delta 3.489)
      3.72 seconds: ML NNI round 1 of 19, 201 of 795 splits, 42 changes (max delta 11.221)
      4.08 seconds: ML NNI round 1 of 19, 301 of 795 splits, 54 changes (max delta 11.221)
      4.49 seconds: ML NNI round 1 of 19, 401 of 795 splits, 67 changes (max delta 11.221)
      4.87 seconds: ML NNI round 1 of 19, 501 of 795 splits, 82 changes (max delta 11.221)
      5.23 seconds: ML NNI round 1 of 19, 601 of 795 splits, 101 changes (max delta 11.221)
      5.62 seconds: ML NNI round 1 of 19, 701 of 795 splits, 118 changes (max delta 11.221)
ML-NNI round 1: LogLk = -129180.341 NNIs 140 max delta 11.22 Time 6.01
      6.06 seconds: Site likelihoods with rate category 1 of 20
      6.16 seconds: Site likelihoods with rate category 3 of 20
      6.27 seconds: Site likelihoods with rate category 5 of 20
      6.38 seconds: Site likelihoods with rate category 7 of 20
      6.48 seconds: Site likelihoods with rate category 9 of 20
      6.59 seconds: Site likelihoods with rate category 11 of 20
      6.69 seconds: Site likelihoods with rate category 13 of 20
      6.80 seconds: Site likelihoods with rate category 15 of 20
      6.90 seconds: Site likelihoods with rate category 17 of 20
      7.01 seconds: Site likelihoods with rate category 19 of 20
Switched to using 20 rate categories (CAT approximation)
Rate categories were divided by 1.106 so that average rate = 1.0
CAT-based log-likelihoods may not be comparable across runs
Use -gamma for approximate but comparable Gamma(20) log-likelihoods
      7.17 seconds: ML NNI round 2 of 19, 1 of 795 splits
      7.45 seconds: ML NNI round 2 of 19, 101 of 795 splits, 11 changes (max delta 3.696)
      7.72 seconds: ML NNI round 2 of 19, 201 of 795 splits, 22 changes (max delta 4.148)
      7.97 seconds: ML NNI round 2 of 19, 301 of 795 splits, 34 changes (max delta 5.429)
      8.26 seconds: ML NNI round 2 of 19, 401 of 795 splits, 46 changes (max delta 5.429)
      8.54 seconds: ML NNI round 2 of 19, 501 of 795 splits, 53 changes (max delta 5.429)
      8.83 seconds: ML NNI round 2 of 19, 601 of 795 splits, 68 changes (max delta 5.448)
      9.14 seconds: ML NNI round 2 of 19, 701 of 795 splits, 84 changes (max delta 5.448)
ML-NNI round 2: LogLk = -121930.723 NNIs 93 max delta 5.45 Time 9.46
      9.45 seconds: ML NNI round 3 of 19, 1 of 795 splits
      9.77 seconds: ML NNI round 3 of 19, 101 of 795 splits, 8 changes (max delta 1.199)
     10.03 seconds: ML NNI round 3 of 19, 201 of 795 splits, 16 changes (max delta 3.181)
     10.34 seconds: ML NNI round 3 of 19, 301 of 795 splits, 31 changes (max delta 3.924)
     10.60 seconds: ML NNI round 3 of 19, 401 of 795 splits, 37 changes (max delta 3.924)
     10.89 seconds: ML NNI round 3 of 19, 501 of 795 splits, 47 changes (max delta 5.502)
ML-NNI round 3: LogLk = -121875.324 NNIs 50 max delta 5.50 Time 11.04
     11.04 seconds: ML NNI round 4 of 19, 1 of 795 splits
     11.33 seconds: ML NNI round 4 of 19, 101 of 795 splits, 4 changes (max delta 3.149)
     11.65 seconds: ML NNI round 4 of 19, 201 of 795 splits, 16 changes (max delta 3.149)
     11.96 seconds: ML NNI round 4 of 19, 301 of 795 splits, 25 changes (max delta 4.485)
ML-NNI round 4: LogLk = -121838.596 NNIs 27 max delta 4.48 Time 12.08
     12.07 seconds: ML NNI round 5 of 19, 1 of 795 splits
     12.41 seconds: ML NNI round 5 of 19, 101 of 795 splits, 9 changes (max delta 5.984)
ML-NNI round 5: LogLk = -121822.923 NNIs 10 max delta 5.98 Time 12.68
     12.67 seconds: ML NNI round 6 of 19, 1 of 795 splits
ML-NNI round 6: LogLk = -121810.022 NNIs 5 max delta 9.49 Time 12.93
     12.92 seconds: ML NNI round 7 of 19, 1 of 795 splits
ML-NNI round 7: LogLk = -121809.234 NNIs 3 max delta 0.42 Time 13.12
     13.12 seconds: ML NNI round 8 of 19, 1 of 795 splits
ML-NNI round 8: LogLk = -121806.065 NNIs 1 max delta 0.69 Time 13.27
     13.26 seconds: ML NNI round 9 of 19, 1 of 795 splits
ML-NNI round 9: LogLk = -121806.529 NNIs 1 max delta 0.23 Time 13.35
Turning off heuristics for final round of ML NNIs (converged)
     13.77 seconds: ML NNI round 10 of 19, 101 of 795 splits, 4 changes (max delta 0.945)
     14.22 seconds: ML NNI round 10 of 19, 201 of 795 splits, 7 changes (max delta 0.945)
     14.63 seconds: ML NNI round 10 of 19, 301 of 795 splits, 11 changes (max delta 0.945)
     15.03 seconds: ML NNI round 10 of 19, 401 of 795 splits, 20 changes (max delta 3.025)
     15.46 seconds: ML NNI round 10 of 19, 501 of 795 splits, 21 changes (max delta 3.025)
     15.84 seconds: ML NNI round 10 of 19, 601 of 795 splits, 23 changes (max delta 3.025)
     16.27 seconds: ML NNI round 10 of 19, 701 of 795 splits, 27 changes (max delta 3.025)
ML-NNI round 10: LogLk = -121743.666 NNIs 32 max delta 10.46 Time 16.69 (final)
     16.69 seconds: ML Lengths 1 of 795 splits
     16.80 seconds: ML Lengths 101 of 795 splits
     16.91 seconds: ML Lengths 201 of 795 splits
     17.02 seconds: ML Lengths 301 of 795 splits
     17.13 seconds: ML Lengths 401 of 795 splits
     17.23 seconds: ML Lengths 501 of 795 splits
     17.34 seconds: ML Lengths 601 of 795 splits
     17.45 seconds: ML Lengths 701 of 795 splits
Optimize all lengths: LogLk = -121741.655 Time 17.56
     17.89 seconds: ML split tests for    100 of    794 internal splits
     18.22 seconds: ML split tests for    200 of    794 internal splits
     18.55 seconds: ML split tests for    300 of    794 internal splits
     18.87 seconds: ML split tests for    400 of    794 internal splits
     19.20 seconds: ML split tests for    500 of    794 internal splits
     19.51 seconds: ML split tests for    600 of    794 internal splits
     19.84 seconds: ML split tests for    700 of    794 internal splits
Total time: 20.15 seconds Unique: 797/797 Bad splits: 4/794 Worst delta-LogLk 0.706
Newick Tree File

Import the newick tree file based on the sequence alignment of shared perfects derived from Lib15 & Lib16 mapping file:

# Full tree alignment (417 mutID)
Fittree15 <- read.tree("Perfects/TREES/Lib.15.5BCs.perfects.complement.tree.newick")   # newick format
Fittree15

Phylogenetic tree with 797 tips and 795 internal nodes.

Tip labels:
  WP_011238316, WP_002931755, WP_004375525, WP_006460949, WP_007277186, WP_004138800, ...
Node labels:
  , 0.871, 0.397, 0.997, 0.859, 0.202, ...

Unrooted; includes branch lengths.

Extract the tip labels from the newick tree file to match with NCBI taxonomy for downstream plotting:

# Extract tip labels from Fittree15
Fittree15_tip_labels <- Fittree15$tip.label

# Create a new data frame with unique tip labels
Fittree15_tip_labels_df <- data.frame(tip.label = unique(Fittree15_tip_labels))

# Print the first few rows of the new data frame
head(Fittree15_tip_labels_df)

Match each tip.label ID in Alltree15 with it’s associated TaxID from orginfo dataframe:

# Rename column "tip.label" to "ID"
colnames(Fittree15_tip_labels_df) <- c("ID")

# Merge orginfo with Alltree15_tip_labels_df based on the shared ID
Fittree15_taxa <- merge(Fittree15_tip_labels_df, orginfo, by = "ID", all.x = TRUE)

# Print the first few rows of the merged data frame
head(Fittree15_taxa)

Add fitness scores to the Fittree15_taxa_merged object prior to plotting

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

Merge the NCBI taxonomy columns to Fittree15_taxa based on shared TaxID

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

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

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

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

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

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

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

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

# NCBI.family
Fittree15_taxa_merged$NCBI.species[Fittree15_taxa_merged$TaxID %in% ncbi_taxa$TaxID] <- ncbi_taxa$NCBI.species[match(Fittree15_taxa_merged$TaxID[Fittree15_taxa_merged$TaxID %in% ncbi_taxa$TaxID], ncbi_taxa$TaxID)]
Pct Similarity E coli

Plot a circular phylogenetic tree with color-scale based on phylogenetic distance between each DHFR homolog relative to the E. coli DHFR gene.

fittree15_NCBI_ecoli_ident <- ggtree(Fittree15, layout="circular", branch.length="none") %<+% 
  Fittree15_taxa_merged + 
  aes(color=PctIdentEcoli*100) +
  geom_tippoint(aes(color=PctIdentEcoli*100), size=1, alpha=0.8) +
  geom_tiplab2(aes(color=PctIdentEcoli*100, label=NCBI.name, angle=angle), size=1) +
  theme(legend.position="left") + 
  scale_colour_gradient2("Sequence\nIdentity (%)", low="blue", mid="green", high="red",
                         midpoint=(100+min(Fittree15_taxa_merged$PctIdentEcoli)*100)/2)

fittree15_NCBI_ecoli_ident

Taxonomic Rings

The following section builds on the initial DHFR phylogenetic tree and adds the NCBI taxonomic lineages as an outer ring to display the breadth of sequence diversity in the 417 unique branch tips (41% of full library diversity) from the recovered perfects with fitness > -1 in Library 15.

Replace the value in “NCBI.phylum” column with the value from “NCBI.class” if “NCBI.phylum” is “Pseudomonadota”

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

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

Distinct Phylum Colors for Plotting

# Merge distinct phylum colors from Alltree15_taxa_merged dataframe:
Fittree15_taxa_merged$NCBI.phylum_colors <- NA
Fittree15_taxa_merged$NCBI.class_colors <- NA

# NCBI.phylum_colors
Fittree15_taxa_merged$NCBI.phylum_colors[Fittree15_taxa_merged$TaxID %in% Alltree15_taxa_merged$TaxID] <- Alltree15_taxa_merged$NCBI.phylum_colors[match(Fittree15_taxa_merged$TaxID[Fittree15_taxa_merged$TaxID %in% Alltree15_taxa_merged$TaxID], Alltree15_taxa_merged$TaxID)]

# NCBI.class_colors
Fittree15_taxa_merged$NCBI.class_colors[Fittree15_taxa_merged$TaxID %in% Alltree15_taxa_merged$TaxID] <- Alltree15_taxa_merged$NCBI.class_colors[match(Fittree15_taxa_merged$TaxID[Fittree15_taxa_merged$TaxID %in% Alltree15_taxa_merged$TaxID], Alltree15_taxa_merged$TaxID)]

# List phylum in alphabetical order for legend and plotting
Fittree15_taxa_merged$NCBI.phylum <- factor(Fittree15_taxa_merged$NCBI.phylum, levels=NCBIphyloColor$NCBI.phylum)

# List phylum in alphabetical order for legend and plotting
Fittree15_taxa_merged$NCBI.class <- factor(Fittree15_taxa_merged$NCBI.class, levels=NCBIclassColor$NCBI.class)

Complement Tree (797)

Show the minimum and maximum fitness values between D05 (M9 no supp) vs. D03 (M9 full supp) dataset:

min(Fittree15_taxa_merged$fitD05D03,na.rm=T)
[1] -6.29133
max(Fittree15_taxa_merged$fitD05D03,na.rm=T)
[1] 0.7437646

Lib15: Complementation Plot

tree_perfects15_5BCs_good_0tmp_fitness_labelled <- ggtree(Fittree15, layout="circular", branch.length="none") %<+%
  Fittree15_taxa_merged +
  aes(color = ifelse(fitD05D03 < -1, "Low fitness", "High fitness")) +
  geom_tippoint(aes(color = ifelse(fitD05D03 < -1, "Low fitness", "High fitness")), size=1, alpha=0.8) +
  geom_tiplab2(aes(label=NCBI.species, angle=angle), size=1) +
  theme(legend.position="none") + 
  scale_color_manual(
    "Fitness",
    values = c("Low fitness" = "darkblue", "High fitness" = "gold"),
    na.value = "gray"
  ) +
  guides(color = guide_legend(title = "Fitness"))

tree_perfects15_5BCs_good_0tmp_fitness_labelled

Build the initial DHFR tree with color-scale based on fitness at COMPLEMENTATION and add an additional ring representing taxonomic lineages at the Phylum-level.

tree_perfects15_5BCs_good_0tmp_fitness_phylum_p1 <- ggtree(Fittree15, layout="circular", branch.length="none") %<+%
  Fittree15_taxa_merged +
  aes(color = ifelse(fitD05D03 < -1, "Low fitness", "High fitness")) +
  geom_tippoint(aes(color = ifelse(fitD05D03 < -1, "Low fitness", "High fitness")), size=1, alpha=0.8) +
  theme(legend.position="right") + 
  scale_color_manual(
    "Fitness",
    values = c("Low fitness" = "darkblue", "High fitness" = "gold"),
    na.value = "gray")

tree_perfects15_5BCs_good_0tmp_fitness_phylum_p2 <- tree_perfects15_5BCs_good_0tmp_fitness_phylum_p1 +
  new_scale_fill() +
  geom_fruit(
    geom=geom_tile,
    mapping=aes(fill=NCBI.phylum),
    width=4,
    offset=0.1
  ) +
  scale_fill_manual(
    name="Phylum",
    values=NCBIphyloColor$NCBI.phylum_colors,
    guide=guide_legend(keywidth=0.3, keyheight=0.3, ncol=1)
  ) +
  theme(
    legend.title=element_text(size=10), 
           legend.text=element_text(size=8),
           legend.spacing.y = unit(0.2, "cm")
  )

tree_perfects15_5BCs_good_0tmp_fitness_phylum_p2

Trimethoprim Trees (416)

Data Subsetting

Retain unique DHFR perfects with a fitness score >= -1 and a minimum of 5 BCs (numprunedBCs > 4) in Lib15 (fitD05D03):

# Subset dataset for tree building
L15_perfects_complement_tree <- perfects15_5BCs_good %>%
  select(mutID, seq,
         fitD05D03, fitD06D03, fitD07D03, fitD08D03, fitD09D03, fitD10D03, fitD11D03)

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

# Rename "mutID" to "ID" to merge with `orginfo` df:
L15_perfects_complement_tree <- L15_perfects_complement_tree %>% rename(ID = mutID)

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

Create a FASTA file containing the ID and its associated protein sequence for alignment

# Collect the sequences in FASTA format
L15_perfects_complement_tree_fasta_content <- paste(">",L15_perfects_complement_tree$ID, "\n", L15_perfects_complement_tree$seq, "\n", sep = "", collapse = "")

# Define the file path in the working directory
L15_perfects_complement_tree_fasta_path <- file.path(getwd(), "Perfects/TREES/Lib.15.5BCs.good.perfects.complement.fasta")

# Write the FASTA content to the file
writeLines(L15_perfects_complement_tree_fasta_content, con = L15_perfects_complement_tree_fasta_path)

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 Perfects/TREES/Lib.15.5BCs.good.perfects.complement.fasta -o Perfects/TREES/Lib.15.5BCs.good.perfects.complement.aligned.fasta --outfmt=fa --force

Use FastTree (phylogenetic tree building program) to infer the tree from the aligned amino acid sequences:

# ML Model: Jones-Taylor-Thorton
# chmod +x FastTree
./Scripts/FastTree Perfects/TREES/Lib.15.5BCs.good.perfects.complement.aligned.fasta > Perfects/TREES/Lib.15.5BCs.good.perfects.complement.tree.newick
FastTree Version 2.1.11 No SSE3
Alignment: Perfects/TREES/Lib.15.5BCs.good.perfects.complement.aligned.fasta
Amino acid distances: BLOSUM45 Joins: balanced Support: SH-like 1000
Search: Normal +NNI +SPR (2 rounds range 10) +ML-NNI opt-each=1
TopHits: 1.00*sqrtN close=default refresh=0.80
ML Model: Jones-Taylor-Thorton, CAT approximation with 20 rate categories
      0.10 seconds: Joined    300 of    413
Initial topology in 0.14 seconds
Refining topology: 35 rounds ME-NNIs, 2 rounds ME-SPRs, 17 rounds ML-NNIs
      0.22 seconds: SPR round   1 of   2, 101 of 830 nodes
      0.33 seconds: SPR round   1 of   2, 401 of 830 nodes
      0.46 seconds: SPR round   1 of   2, 701 of 830 nodes
      0.57 seconds: SPR round   2 of   2, 101 of 830 nodes
      0.68 seconds: SPR round   2 of   2, 401 of 830 nodes
      0.81 seconds: SPR round   2 of   2, 701 of 830 nodes
Total branch-length 83.288 after 0.90 sec
      0.98 seconds: ML Lengths 101 of 414 splits
      1.15 seconds: ML Lengths 301 of 414 splits
      1.54 seconds: ML NNI round 1 of 17, 101 of 414 splits, 16 changes (max delta 5.024)
      1.86 seconds: ML NNI round 1 of 17, 201 of 414 splits, 36 changes (max delta 7.726)
      2.18 seconds: ML NNI round 1 of 17, 301 of 414 splits, 51 changes (max delta 9.061)
      2.50 seconds: ML NNI round 1 of 17, 401 of 414 splits, 67 changes (max delta 9.568)
ML-NNI round 1: LogLk = -70987.860 NNIs 71 max delta 9.57 Time 2.55
      2.60 seconds: Site likelihoods with rate category 2 of 20
      2.71 seconds: Site likelihoods with rate category 6 of 20
      2.82 seconds: Site likelihoods with rate category 9 of 20
      2.93 seconds: Site likelihoods with rate category 13 of 20
      3.04 seconds: Site likelihoods with rate category 17 of 20
Switched to using 20 rate categories (CAT approximation)
Rate categories were divided by 1.118 so that average rate = 1.0
CAT-based log-likelihoods may not be comparable across runs
Use -gamma for approximate but comparable Gamma(20) log-likelihoods
      3.17 seconds: ML NNI round 2 of 17, 1 of 414 splits
      3.45 seconds: ML NNI round 2 of 17, 101 of 414 splits, 12 changes (max delta 7.045)
      3.68 seconds: ML NNI round 2 of 17, 201 of 414 splits, 24 changes (max delta 7.045)
      3.90 seconds: ML NNI round 2 of 17, 301 of 414 splits, 33 changes (max delta 7.045)
      4.22 seconds: ML NNI round 2 of 17, 401 of 414 splits, 48 changes (max delta 7.045)
ML-NNI round 2: LogLk = -66952.695 NNIs 50 max delta 7.05 Time 4.27
      4.50 seconds: ML NNI round 3 of 17, 101 of 414 splits, 5 changes (max delta 2.261)
      4.77 seconds: ML NNI round 3 of 17, 201 of 414 splits, 11 changes (max delta 2.261)
ML-NNI round 3: LogLk = -66932.666 NNIs 20 max delta 2.67 Time 4.95
      4.94 seconds: ML NNI round 4 of 17, 1 of 414 splits
      5.20 seconds: ML NNI round 4 of 17, 101 of 414 splits, 0 changes
ML-NNI round 4: LogLk = -66920.309 NNIs 9 max delta 4.28 Time 5.42
      5.41 seconds: ML NNI round 5 of 17, 1 of 414 splits
ML-NNI round 5: LogLk = -66918.357 NNIs 0 max delta 0.00 Time 5.62
Turning off heuristics for final round of ML NNIs (converged)
      5.62 seconds: ML NNI round 6 of 17, 1 of 414 splits
      5.97 seconds: ML NNI round 6 of 17, 101 of 414 splits, 2 changes (max delta 0.744)
      6.32 seconds: ML NNI round 6 of 17, 201 of 414 splits, 6 changes (max delta 1.888)
      6.69 seconds: ML NNI round 6 of 17, 301 of 414 splits, 11 changes (max delta 2.170)
      7.02 seconds: ML NNI round 6 of 17, 401 of 414 splits, 19 changes (max delta 2.170)
ML-NNI round 6: LogLk = -66881.675 NNIs 19 max delta 2.17 Time 7.08 (final)
      7.16 seconds: ML Lengths 101 of 414 splits
      7.35 seconds: ML Lengths 301 of 414 splits
Optimize all lengths: LogLk = -66879.917 Time 7.46
      7.72 seconds: ML split tests for    100 of    413 internal splits
      7.99 seconds: ML split tests for    200 of    413 internal splits
      8.26 seconds: ML split tests for    300 of    413 internal splits
      8.53 seconds: ML split tests for    400 of    413 internal splits
Total time: 8.57 seconds Unique: 416/416 Bad splits: 3/413 Worst delta-LogLk 5.276
Newick Tree File

Import the newick tree file based on the sequence alignment of complementing perfects derived from Lib15 mapping file:

# Full tree alignment (417 mutID)
Fittree15good <- read.tree("Perfects/TREES/Lib.15.5BCs.good.perfects.complement.tree.newick")   # newick format
Fittree15good

Phylogenetic tree with 416 tips and 414 internal nodes.

Tip labels:
  WP_007642986, WP_008302089, WP_010560929, WP_006794536, WP_008136828, WP_007420749, ...
Node labels:
  , 0.910, 0.959, 0.651, 0.692, 0.995, ...

Unrooted; includes branch lengths.

Extract the tip labels from the newick tree file to match with NCBI taxonomy for downstream plotting:

# Extract tip labels from Fittree15good
Fittree15good_tip_labels <- Fittree15good$tip.label

# Create a new data frame with unique tip labels
Fittree15good_tip_labels_df <- data.frame(tip.label = unique(Fittree15good_tip_labels))

# Print the first few rows of the new data frame
head(Fittree15good_tip_labels_df)

Match each tip.label ID in Fittree15good with it’s associated TaxID from orginfo dataframe:

# Rename column "tip.label" to "ID"
colnames(Fittree15good_tip_labels_df) <- c("ID")

# Merge orginfo with Alltree15_tip_labels_df based on the shared ID
Fittree15good_taxa <- merge(Fittree15good_tip_labels_df, orginfo, by = "ID", all.x = TRUE)

# Print the first few rows of the merged data frame
head(Fittree15good_taxa)

Add fitness scores to the Fittree15_taxa_merged object prior to plotting

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

Merge the NCBI taxonomy columns to Fittree15_taxa based on shared TaxID

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

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

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

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

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

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

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

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

# NCBI.family
Fittree15good_taxa_merged$NCBI.species[Fittree15good_taxa_merged$TaxID %in% ncbi_taxa$TaxID] <- ncbi_taxa$NCBI.species[match(Fittree15good_taxa_merged$TaxID[Fittree15good_taxa_merged$TaxID %in% ncbi_taxa$TaxID], ncbi_taxa$TaxID)]
Taxonomic Rings

The following section builds on the initial DHFR phylogenetic tree and adds the NCBI taxonomic lineages as an outer ring to display the breadth of sequence diversity in the 417 unique branch tips (41% of full library diversity) from the recovered perfects with fitness > -1 in Library 15.

Replace the value in “NCBI.phylum” column with the value from “NCBI.class” if “NCBI.phylum” is “Pseudomonadota”

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

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

Distinct Phylum Colors for Plotting

# Merge distinct phylum colors from Fittree15_taxa_merged dataframe:
Fittree15good_taxa_merged$NCBI.phylum_colors <- NA

# Add NCBI.phylum_colors from Fittree15_taxa_merged based on matching NCBI.phylum
Fittree15good_taxa_merged$NCBI.phylum_colors <- Fittree15_taxa_merged$NCBI.phylum_colors[match(Fittree15good_taxa_merged$NCBI.phylum, Fittree15_taxa_merged$NCBI.phylum)]

# List phylum in alphabetical order for legend and plotting
Fittree15good_taxa_merged$NCBI.phylum <- factor(Fittree15good_taxa_merged$NCBI.phylum, levels=NCBIphyloColor$NCBI.phylum)

0.0 ug/mL Tree

Lib15: (0.0 ug/mL TMP)

tree_perfects15_5BCs_good_0.0tmp_fitness_labelled <- ggtree(Fittree15good, layout="circular", branch.length="none") %<+%
  Fittree15good_taxa_merged +
  aes(color = ifelse(fitD05D03 < -1, "Low fitness", "High fitness")) +
  geom_tippoint(aes(color = ifelse(fitD05D03 < -1, "Low fitness", "High fitness")), size=1, alpha=0.8) +
  #geom_tiplab2(aes(label=NCBI.phylum, angle=angle), size=1) +
  theme(legend.position="none") + 
  scale_color_manual(
    "Fitness",
    values = c("Low fitness" = "darkblue", "High fitness" = "gold"),
    na.value = "gray"
  ) +
  guides(color = guide_legend(title = "Fitness"))

tree_perfects15_5BCs_good_0.0tmp_fitness_labelled

Count the number of complementing homologs (fit > -1) and dropout homologs (fit < -1):

# Count unique IDs with fitD05D03 < -1
low_fitness_count <- Fittree15good_taxa_merged %>%
  filter(fitD05D03 < -1) %>%
  distinct(ID) %>%
  nrow()

# Count unique IDs with fitD06D03 > -1
high_fitness_count <- Fittree15good_taxa_merged %>%
  filter(fitD05D03 > -1) %>%
  distinct(ID) %>%
  nrow()

# Print the results
cat("Number of unique IDs with fitD05D03 > -1:", high_fitness_count, "\n")
Number of unique IDs with fitD05D03 > -1: 411 
cat("Number of unique IDs with fitD05D03 < -1:", low_fitness_count, "\n")
Number of unique IDs with fitD05D03 < -1: 0 

Build the initial DHFR tree with color-scale based on fitness at COMPLEMENTATION and add an additional ring representing taxonomic lineages at the Phylum-level.

tree_perfects15_5BCs_good_0.0tmp_fitness_phylum_p1 <- ggtree(Fittree15good, layout="circular", branch.length="none") %<+%
  Fittree15good_taxa_merged +
  aes(color = ifelse(fitD05D03 < -1, "Low fitness", "High fitness")) +
  geom_tippoint(aes(color = ifelse(fitD05D03 < -1, "Low fitness", "High fitness")), size=1, alpha=0.8) +
  theme(legend.position="right") + 
  scale_color_manual(
    "Fitness",
    values = c("Low fitness" = "darkblue", "High fitness" = "gold"),
    na.value = "gray")

tree_perfects15_5BCs_good_0.0tmp_fitness_phylum_p2 <- tree_perfects15_5BCs_good_0.0tmp_fitness_phylum_p1 +
  new_scale_fill() +
  geom_fruit(
    geom=geom_tile,
    mapping=aes(fill=NCBI.phylum),  # Changed this line
    width=3,
    offset=0.1
  ) +
  scale_fill_manual(
    name="Phylum",
    values=setNames(Fittree15good_taxa_merged$NCBI.phylum_colors, Fittree15good_taxa_merged$NCBI.phylum),
    guide=guide_legend(keywidth=0.3, keyheight=0.3, ncol=1)
  ) +
  theme(
    legend.title=element_text(size=10), 
    legend.text=element_text(size=8),
    legend.spacing.y = unit(0.2, "cm")
  )

tree_perfects15_5BCs_good_0.0tmp_fitness_phylum_p2

0.058 ug/mL Tree

Lib15: MIC (0.058 ug/mL TMP)

tree_perfects15_5BCs_good_0.058tmp_fitness_labelled <- ggtree(Fittree15good, layout="circular", branch.length="none") %<+%
  Fittree15good_taxa_merged +
  aes(color = ifelse(fitD06D03 < -1, "Low fitness", "High fitness")) +
  geom_tippoint(aes(color = ifelse(fitD06D03 < -1, "Low fitness", "High fitness")), size=1, alpha=0.8) +
  #geom_tiplab2(aes(label=NCBI.phylum, angle=angle), size=1) +
  theme(legend.position="none") + 
  scale_color_manual(
    "Fitness",
    values = c("Low fitness" = "darkblue", "High fitness" = "gold"),
    na.value = "gray"
  ) +
  guides(color = guide_legend(title = "Fitness"))

tree_perfects15_5BCs_good_0.058tmp_fitness_labelled

Count the number of complementing homologs (fit > -1) and dropout homologs (fit < -1):

# Count unique IDs with fitD06D03 < -1
low_fitness_count <- Fittree15good_taxa_merged %>%
  filter(fitD06D03 < -1) %>%
  distinct(ID) %>%
  nrow()

# Count unique IDs with fitD06D03 > -1
high_fitness_count <- Fittree15good_taxa_merged %>%
  filter(fitD06D03 > -1) %>%
  distinct(ID) %>%
  nrow()

# Print the results
cat("Number of unique IDs with fitD06D03 > -1:", high_fitness_count, "\n")
Number of unique IDs with fitD06D03 > -1: 318 
cat("Number of unique IDs with fitD06D03 < -1:", low_fitness_count, "\n")
Number of unique IDs with fitD06D03 < -1: 92 

Build the initial DHFR tree with color-scale based on fitness at COMPLEMENTATION and add an additional ring representing taxonomic lineages at the Phylum-level.

tree_perfects15_5BCs_good_0.058tmp_fitness_phylum_p1 <- ggtree(Fittree15good, layout="circular", branch.length="none") %<+%
  Fittree15good_taxa_merged +
  aes(color = ifelse(fitD06D03 < -1, "Low fitness", "High fitness")) +
  geom_tippoint(aes(color = ifelse(fitD06D03 < -1, "Low fitness", "High fitness")), size=1, alpha=0.8) +
  theme(legend.position="right") + 
  scale_color_manual(
    "Fitness",
    values = c("Low fitness" = "darkblue", "High fitness" = "gold"),
    na.value = "gray")

tree_perfects15_5BCs_good_0.058tmp_fitness_phylum_p2 <- tree_perfects15_5BCs_good_0.058tmp_fitness_phylum_p1 +
  new_scale_fill() +
  geom_fruit(
    geom=geom_tile,
    mapping=aes(fill=NCBI.phylum),  # Changed this line
    width=3,
    offset=0.1
  ) +
  scale_fill_manual(
    name="Phylum",
    values=setNames(Fittree15good_taxa_merged$NCBI.phylum_colors, Fittree15good_taxa_merged$NCBI.phylum),
    guide=guide_legend(keywidth=0.3, keyheight=0.3, ncol=1)
  ) +
  theme(
    legend.title=element_text(size=10), 
    legend.text=element_text(size=8),
    legend.spacing.y = unit(0.2, "cm")
  )

tree_perfects15_5BCs_good_0.058tmp_fitness_phylum_p2

Summary Stats

The following statistical tests will determine if fitness clusters by evolutionary distance within the phylogenetic tree

# Create a function to calculate distances and differences between 
fitD06D03_dist_diff <- function(pairwise_distances, taxa_merged) {
  distance_df <- as.data.frame(as.table(pairwise_distances)) %>%
    rename(Tip1 = Var1, Tip2 = Var2, Distance = Freq) %>%
    mutate(Tip1 = as.character(Tip1),
           Tip2 = as.character(Tip2)) %>%
    filter(Tip1 != Tip2)  # Remove self-comparisons
  
  results <- distance_df %>%
    left_join(taxa_merged, by = c("Tip1" = "ID")) %>%
    rename(fitD06D03v1 = fitD06D03) %>%
    left_join(taxa_merged, by = c("Tip2" = "ID")) %>%
    rename(fitD06D03v2 = fitD06D03) %>%
    mutate(
      AbsDistance = abs(Distance),
      fitD06D03AbsDiff = abs(fitD06D03v1 - fitD06D03v2)
    ) %>%
    select(Tip1, Tip2, AbsDistance, fitD06D03AbsDiff)
  
  return(results)}

Pearson Correlation: Calculate pairwise distances between tips and differences in fitness values. Plot distances and differences as scatter plot and test for significant correlation using Pearson.

# Calculate pairwise distances
fitD06D03_pairwise_distances <- cophenetic.phylo(Fittree15good)

# Calculate distances and differences
fitD06D03_results <- fitD06D03_dist_diff(fitD06D03_pairwise_distances,Fittree15good_taxa_merged)

# Create the scatter plot
fitD06D03_scatter_plot <- ggplot(fitD06D03_results, aes(x = AbsDistance, y = fitD06D03AbsDiff)) +
  geom_point(alpha = 0.5) +  # Add transparency to points
  geom_smooth(method = "lm", color = "red", se = FALSE) +  # Add a linear trend line
  stat_cor(method = "pearson", p.accuracy = 0.000001, r.accuracy = 0.01) +
  theme_minimal() +
  labs(title = "Absolute Distance vs Absolute Difference in fitD06D03",
       x = "Absolute Value of Distance",
       y = "Absolute Value of Difference in fitD06D03") +
  theme(plot.title = element_text(hjust = 0.5),
        legend.position = "none")

# Display the plot
print(fitD06D03_scatter_plot)


# Optional: Save the plot
ggsave("Perfects/PLOTS/Wilcox/fitD06D03_pearson_scatter_plot.png", 
       fitD06D03_scatter_plot, width = 10, height = 8, dpi = 300)

Close-Related Pairs: Subset to include only pairs of taxa with an absolute phylogenetic distance less than 1. Graphical summaries include a Q-Q (Quantile-Quantile) plot and histrogram of the absolute difference values. The Q-Q plot is graphical method to assess if a dataset follows a normal distribution. The red line added by qqline() represents the expected line if the data were perfectly normally distributed. The histogram provides a visual representation of the distribution of the data.

# This focuses the analysis on closely related taxa
fitD06D03_close <- fitD06D03_results %>% 
  filter(AbsDistance < 1)

# Graphical Summaries

# Create a Q-Q (Quantile-Quantile) plot
qqnorm(fitD06D03_close$fitD06D03AbsDiff)
qqline(fitD06D03_close$fitD06D03AbsDiff, col = "red")


# Create a histogram plot
hist(fitD06D03_close$fitD06D03AbsDiff, main = "Histogram of Close Data")

Far-Related Pairs: Subset to include only pairs of taxa with an absolute phylogenetic distance greater than 1. Graphical summaries include a Q-Q (Quantile-Quantile) plot and histrogram of the absolute difference values. The Q-Q plot is graphical method to assess if a dataset follows a normal distribution. The red line added by qqline() represents the expected line if the data were perfectly normally distributed. The histogram provides a visual representation of the distribution of the data.

# This focuses the analysis on distantly related taxa
fitD06D03_far <- fitD06D03_results %>% 
  filter(AbsDistance > 1)

# Graphical Summaries

# Create a Q-Q (Quantile-Quantile) plot
qqnorm(fitD06D03_far$fitD06D03AbsDiff)
qqline(fitD06D03_far$fitD06D03AbsDiff, col = "red")


# Create a histogram plot
hist(fitD06D03_far$fitD06D03AbsDiff, main = "Histogram of Far Data")

Wilcox Test: Perform Wilcoxon Rank Sum Test (also known as Mann-Whitney U test) comparing the absolute distance values between the close-related and far-related groups.

# Wilcox test between close and far related distances
wilcox_result_fitD06D03 <- wilcox.test(fitD06D03_close$fitD06D03AbsDiff,
                                       fitD06D03_far$fitD06D03AbsDiff)

print(wilcox_result_fitD06D03)

    Wilcoxon rank sum test with continuity correction

data:  fitD06D03_close$fitD06D03AbsDiff and fitD06D03_far$fitD06D03AbsDiff
W = 294122436, p-value < 2.2e-16
alternative hypothesis: true location shift is not equal to 0

Combine Data: Examine the distributions of these differences to understand how they differ and whether these differences are generally larger or smaller for closely related taxa.

# Combine the data and add a group label
fitD06D03_combined_data <- bind_rows(
  mutate(fitD06D03_close, group = "Close"),
  mutate(fitD06D03_far, group = "Far"))

# Remove NA values
fitD06D03_combined_data <- fitD06D03_combined_data %>% 
  filter(!is.na(fitD06D03AbsDiff))

# Create a box plot
fitD06D03_boxplot <- ggplot(fitD06D03_combined_data, aes(x = group, y = fitD06D03AbsDiff)) +
  geom_boxplot() +
  geom_jitter(alpha = 0.1, width = 0.2) +  # Add individual points with some transparency
  labs(title = "Distribution of fitD06D03 Differences",
       x = "Phylogenetic Relationship",
       y = "Absolute Difference in fitD06D03") +
  theme_minimal()

# Display the plot
print(fitD06D03_boxplot)


# Calculate summary statistics
fitD06D03_summary_stats <- fitD06D03_combined_data %>%
  group_by(group) %>%
  summarise(
    n = n(),
    mean = mean(fitD06D03AbsDiff),
    median = median(fitD06D03AbsDiff),
    sd = sd(fitD06D03AbsDiff),
    min = min(fitD06D03AbsDiff),
    max = max(fitD06D03AbsDiff)
  )

# Print summary statistics
print(fitD06D03_summary_stats)

# Perform a formal test for difference in means
fitD06D03_t_test_result <- t.test(fitD06D03AbsDiff ~ group, data = fitD06D03_combined_data)
print(fitD06D03_t_test_result)

    Welch Two Sample t-test

data:  fitD06D03AbsDiff by group
t = -72.579, df = 7431.6, p-value < 2.2e-16
alternative hypothesis: true difference in means between group Close and group Far is not equal to 0
95 percent confidence interval:
 -0.8238030 -0.7804733
sample estimates:
mean in group Close   mean in group Far 
          0.6984165           1.5005546 

MIC Tree (416)

Lib15: MIC (0.5 ug/mL TMP)

tree_perfects15_5BCs_good_0.5tmp_fitness_labelled <- ggtree(Fittree15good, layout="circular", branch.length="none") %<+%
  Fittree15good_taxa_merged +
  aes(color = ifelse(fitD07D03 < -1, "Low fitness", "High fitness")) +
  geom_tippoint(aes(color = ifelse(fitD07D03 < -1, "Low fitness", "High fitness")), size=1, alpha=0.8) +
  #geom_tiplab2(aes(label=NCBI.phylum, angle=angle), size=1) +
  theme(legend.position="none") + 
  scale_color_manual(
    "Fitness",
    values = c("Low fitness" = "darkblue", "High fitness" = "gold"),
    na.value = "gray"
  ) +
  guides(color = guide_legend(title = "Fitness"))

tree_perfects15_5BCs_good_0.5tmp_fitness_labelled

Count the number of complementing homologs (fit > -1) and dropout homologs (fit < -1):

# Count unique IDs with fitD07D03 > -1
high_fitness_count <- Fittree15good_taxa_merged %>%
  filter(fitD07D03 > -1) %>%
  distinct(ID) %>%
  nrow()

# Count unique IDs with fitD07D03 < -1
low_fitness_count <- Fittree15good_taxa_merged %>%
  filter(fitD07D03 < -1) %>%
  distinct(ID) %>%
  nrow()

# Print the results
cat("Number of unique IDs with fitD07D03 > -1:", high_fitness_count, "\n")
Number of unique IDs with fitD07D03 > -1: 246 
cat("Number of unique IDs with fitD07D03 < -1:", low_fitness_count, "\n")
Number of unique IDs with fitD07D03 < -1: 162 

Build the initial DHFR tree with color-scale based on fitness at COMPLEMENTATION and add an additional ring representing taxonomic lineages at the Phylum-level.

tree_perfects15_5BCs_good_0.5tmp_fitness_phylum_p1 <- ggtree(Fittree15good, layout="circular", branch.length="none") %<+%
  Fittree15good_taxa_merged +
  aes(color = ifelse(fitD07D03 < -1, "Low fitness", "High fitness")) +
  geom_tippoint(aes(color = ifelse(fitD07D03 < -1, "Low fitness", "High fitness")), size=1, alpha=0.8) +
  theme(legend.position="right") + 
  scale_color_manual(
    "Fitness",
    values = c("Low fitness" = "darkblue", "High fitness" = "gold"),
    na.value = "gray")

tree_perfects15_5BCs_good_0.5tmp_fitness_phylum_p2 <- tree_perfects15_5BCs_good_0.5tmp_fitness_phylum_p1 +
  new_scale_fill() +
  geom_fruit(
    geom=geom_tile,
    mapping=aes(fill=NCBI.phylum),  # Changed this line
    width=3,
    offset=0.1
  ) +
  scale_fill_manual(
    name="Phylum",
    values=setNames(Fittree15good_taxa_merged$NCBI.phylum_colors, Fittree15good_taxa_merged$NCBI.phylum),
    guide=guide_legend(keywidth=0.3, keyheight=0.3, ncol=1)
  ) +
  theme(
    legend.title=element_text(size=10), 
    legend.text=element_text(size=8),
    legend.spacing.y = unit(0.2, "cm")
  )

tree_perfects15_5BCs_good_0.5tmp_fitness_phylum_p2

Summary Stats

The following statistical tests will determine if fitness clusters by evolutionary distance within the phylogenetic tree

# Create a function to calculate distances and differences between 
fitD07D03_dist_diff <- function(pairwise_distances, taxa_merged) {
  distance_df <- as.data.frame(as.table(pairwise_distances)) %>%
    rename(Tip1 = Var1, Tip2 = Var2, Distance = Freq) %>%
    mutate(Tip1 = as.character(Tip1),
           Tip2 = as.character(Tip2)) %>%
    filter(Tip1 != Tip2)  # Remove self-comparisons
  
  results <- distance_df %>%
    left_join(taxa_merged, by = c("Tip1" = "ID")) %>%
    rename(fitD07D03v1 = fitD07D03) %>%
    left_join(taxa_merged, by = c("Tip2" = "ID")) %>%
    rename(fitD07D03v2 = fitD07D03) %>%
    mutate(
      AbsDistance = abs(Distance),
      fitD07D03AbsDiff = abs(fitD07D03v1 - fitD07D03v2)
    ) %>%
    select(Tip1, Tip2, AbsDistance, fitD07D03AbsDiff)
  
  return(results)}

Pearson Correlation: Calculate pairwise distances between tips and differences in fitness values. Plot distances and differences as scatter plot and test for significant correlation using Pearson.

# Calculate pairwise distances
fitD07D03_pairwise_distances <- cophenetic.phylo(Fittree15good)

# Calculate distances and differences
fitD07D03_results <- fitD07D03_dist_diff(fitD07D03_pairwise_distances,Fittree15good_taxa_merged)

# Create the scatter plot
fitD07D03_scatter_plot <- ggplot(fitD07D03_results, aes(x = AbsDistance, y = fitD07D03AbsDiff)) +
  geom_point(alpha = 0.5) +  # Add transparency to points
  geom_smooth(method = "lm", color = "red", se = FALSE) +  # Add a linear trend line
  stat_cor(method = "pearson", p.accuracy = 0.000001, r.accuracy = 0.01) +
  theme_minimal() +
  labs(title = "Absolute Distance vs Absolute Difference in fitD07D03",
       x = "Absolute Value of Distance",
       y = "Absolute Value of Difference in fitD07D03") +
  theme(plot.title = element_text(hjust = 0.5),
        legend.position = "none")

# Display the plot
print(fitD07D03_scatter_plot)


# Optional: Save the plot
ggsave("Perfects/PLOTS/Wilcox/fitD07D03_pearson_scatter_plot.png", 
       fitD07D03_scatter_plot, width = 10, height = 8, dpi = 300)

Close-Related Pairs: Subset to include only pairs of taxa with an absolute phylogenetic distance less than 1. Graphical summaries include a Q-Q (Quantile-Quantile) plot and histrogram of the absolute difference values. The Q-Q plot is graphical method to assess if a dataset follows a normal distribution. The red line added by qqline() represents the expected line if the data were perfectly normally distributed. The histogram provides a visual representation of the distribution of the data.

# This focuses the analysis on closely related taxa
fitD07D03_close <- fitD07D03_results %>% 
  filter(AbsDistance < 1)

# Graphical Summaries

# Create a Q-Q (Quantile-Quantile) plot
qqnorm(fitD07D03_close$fitD07D03AbsDiff)
qqline(fitD07D03_close$fitD07D03AbsDiff, col = "red")


# Create a histogram plot
hist(fitD07D03_close$fitD07D03AbsDiff, main = "Histogram of Close Data")

Far-Related Pairs: Subset to include only pairs of taxa with an absolute phylogenetic distance greater than 1. Graphical summaries include a Q-Q (Quantile-Quantile) plot and histrogram of the absolute difference values. The Q-Q plot is graphical method to assess if a dataset follows a normal distribution. The red line added by qqline() represents the expected line if the data were perfectly normally distributed. The histogram provides a visual representation of the distribution of the data.

# This focuses the analysis on distantly related taxa
fitD07D03_far <- fitD07D03_results %>% 
  filter(AbsDistance > 1)

# Graphical Summaries

# Create a Q-Q (Quantile-Quantile) plot
qqnorm(fitD07D03_far$fitD07D03AbsDiff)
qqline(fitD07D03_far$fitD07D03AbsDiff, col = "red")


# Create a histogram plot
hist(fitD07D03_far$fitD07D03AbsDiff, main = "Histogram of Far Data")

Wilcox Test: Perform Wilcoxon Rank Sum Test (also known as Mann-Whitney U test) comparing the absolute distance values between the close-related and far-related groups.

# Wilcox test between close and far related distances
wilcox_result_fitD07D03 <- wilcox.test(fitD07D03_close$fitD07D03AbsDiff,
                                       fitD07D03_far$fitD07D03AbsDiff)

print(wilcox_result_fitD07D03)

    Wilcoxon rank sum test with continuity correction

data:  fitD07D03_close$fitD07D03AbsDiff and fitD07D03_far$fitD07D03AbsDiff
W = 317479680, p-value < 2.2e-16
alternative hypothesis: true location shift is not equal to 0

Interpretation:

  • Significant Difference: The extremely low p-value (< 2.2e-16) indicates a statistically significant difference between the fitD07D03AbsDiff values of closely related taxa (fitnessD07D03_close) and more distantly related taxa (fitD07D03_far).

  • Distribution Comparison: The test suggests that the distribution of fitD07D03AbsDiff values is significantly different between close and far phylogenetic relationships.

  • Magnitude and Direction: While the test tells us there’s a significant difference, it doesn’t tell us about the magnitude or direction of this difference. You’d need to look at descriptive statistics (like medians) of both groups to understand this.

  • Biological Significance: This result suggests that the absolute differences in fitD07D03 values are not the same for closely related taxa compared to more distantly related taxa. This could imply that evolutionary distance does play a role in the similarity (or difference) of fitD07D03 values, despite the weak correlation you found earlier.

  • Further Investigation: It would be worth examining the actual distributions of these differences (e.g., with box plots) to understand how they differ. Are the differences generally larger or smaller for closely related taxa?

  • Caution: Remember that with very large sample sizes, even small differences can become statistically significant. It’s important to consider the practical or biological significance of these differences, not just the statistical significance.

This result adds nuance to your earlier findings, suggesting that while there might not be a strong linear correlation between phylogenetic distance and fitD07D03 differences, there is a significant difference in how these values vary among close relatives versus more distant relatives.

Combine Data: Examine the distributions of these differences to understand how they differ and whether these differences are generally larger or smaller for closely related taxa.

# Combine the data and add a group label
fitD07D03_combined_data <- bind_rows(
  mutate(fitD07D03_close, group = "Close"),
  mutate(fitD07D03_far, group = "Far"))

# Remove NA values
fitD07D03_combined_data <- fitD07D03_combined_data %>% 
  filter(!is.na(fitD07D03AbsDiff))

# Create a box plot
fitD07D03_boxplot <- ggplot(fitD07D03_combined_data, aes(x = group, y = fitD07D03AbsDiff)) +
  geom_boxplot() +
  geom_jitter(alpha = 0.1, width = 0.2) +  # Add individual points with some transparency
  labs(title = "Distribution of fitD07D03 Differences",
       x = "Phylogenetic Relationship",
       y = "Absolute Difference in fitD07D03") +
  theme_minimal()

# Display the plot
print(fitD07D03_boxplot)


# Calculate summary statistics
fitD07D03_summary_stats <- fitD07D03_combined_data %>%
  group_by(group) %>%
  summarise(
    n = n(),
    mean = mean(fitD07D03AbsDiff),
    median = median(fitD07D03AbsDiff),
    sd = sd(fitD07D03AbsDiff),
    min = min(fitD07D03AbsDiff),
    max = max(fitD07D03AbsDiff)
  )

# Print summary statistics
print(fitD07D03_summary_stats)

# Perform a formal test for difference in means
fitD07D03_t_test_result <- t.test(fitD07D03AbsDiff ~ group, data = fitD07D03_combined_data)
print(fitD07D03_t_test_result)

    Welch Two Sample t-test

data:  fitD07D03AbsDiff by group
t = -63.505, df = 7020.5, p-value < 2.2e-16
alternative hypothesis: true difference in means between group Close and group Far is not equal to 0
95 percent confidence interval:
 -1.263865 -1.188174
sample estimates:
mean in group Close   mean in group Far 
           1.482027            2.708046 

Interpretation:

  • Significant Difference: There is a statistically significant difference in the fitD07D03AbsDiff values between closely related taxa and more distantly related taxa.

  • Magnitude of Difference: On average, the absolute difference in fitD07D03 values is about 1.226 units smaller for closely related taxa compared to distantly related taxa.

  • Consistency of Difference: The narrow confidence interval (-1.263865 to -1.188174) suggests that this difference is quite consistent across the dataset.

  • Biological Significance: Closely related taxa tend to have more similar fitD07D03 values (smaller differences) compared to distantly related taxa. This suggests that the fitD07D03 trait shows some degree of phylogenetic signal or conservation.

  • Evolutionary Implications: This result indicates that evolutionary relatedness does play a role in the similarity of fitD07D03 values. Closely related species tend to have more similar values, which could be due to shared evolutionary history or similar environmental adaptations.

  • Contrast with Earlier Correlation: While earlier you found a weak correlation between phylogenetic distance and fitD07D03 differences, this t-test reveals a clear distinction between close and distant relationships. This highlights the importance of considering different analytical approaches, as they can reveal different aspects of the data.

  • Practical Significance: The difference in means (about 1.226) should be considered in the context of the overall range and biological meaning of fitD07D03 values to determine its practical significance.

In summary, this analysis provides strong evidence that closely related taxa have more similar fitD07D03 values compared to distantly related taxa. This supports the idea that there is a phylogenetic component to the fitD07D03 trait, even if it’s not captured well by a simple linear correlation with phylogenetic distance. The large sample size and highly significant result suggest that this is a robust finding, though you should always consider the biological context and potential confounding factors in your interpretation.

1.0 ug/mL TMP Tree

Lib15: MIC (1.0 ug/mL TMP)

tree_perfects15_5BCs_good_1.0tmp_fitness_labelled <- ggtree(Fittree15good, layout="circular", branch.length="none") %<+%
  Fittree15good_taxa_merged +
  aes(color = ifelse(fitD08D03 < -1, "Low fitness", "High fitness")) +
  geom_tippoint(aes(color = ifelse(fitD08D03 < -1, "Low fitness", "High fitness")), size=1, alpha=0.8) +
  #geom_tiplab2(aes(label=NCBI.phylum, angle=angle), size=1) +
  theme(legend.position="none") + 
  scale_color_manual(
    "Fitness",
    values = c("Low fitness" = "darkblue", "High fitness" = "gold"),
    na.value = "gray"
  ) +
  guides(color = guide_legend(title = "Fitness"))

tree_perfects15_5BCs_good_1.0tmp_fitness_labelled

Count the number of complementing homologs (fit > -1) and dropout homologs (fit < -1):

# Count unique IDs with fitD08D03 > -1
high_fitness_count <- Fittree15good_taxa_merged %>%
  filter(fitD08D03 > -1) %>%
  distinct(ID) %>%
  nrow()

# Count unique IDs with fitD08D03 < -1
low_fitness_count <- Fittree15good_taxa_merged %>%
  filter(fitD08D03 < -1) %>%
  distinct(ID) %>%
  nrow()

# Print the results
cat("Number of unique IDs with fitD08D03 > -1:", high_fitness_count, "\n")
Number of unique IDs with fitD08D03 > -1: 226 
cat("Number of unique IDs with fitD08D03 < -1:", low_fitness_count, "\n")
Number of unique IDs with fitD08D03 < -1: 183 

Build the initial DHFR tree with color-scale based on fitness at COMPLEMENTATION and add an additional ring representing taxonomic lineages at the Phylum-level.

tree_perfects15_5BCs_good_1.0tmp_fitness_phylum_p1 <- ggtree(Fittree15good, layout="circular", branch.length="none") %<+%
  Fittree15good_taxa_merged +
  aes(color = ifelse(fitD08D03 < -1, "Low fitness", "High fitness")) +
  geom_tippoint(aes(color = ifelse(fitD08D03 < -1, "Low fitness", "High fitness")), size=1, alpha=0.8) +
  theme(legend.position="right") + 
  scale_color_manual(
    "Fitness",
    values = c("Low fitness" = "darkblue", "High fitness" = "gold"),
    na.value = "gray")

tree_perfects15_5BCs_good_1.0tmp_fitness_phylum_p2 <- tree_perfects15_5BCs_good_1.0tmp_fitness_phylum_p1 +
  new_scale_fill() +
  geom_fruit(
    geom=geom_tile,
    mapping=aes(fill=NCBI.phylum),  # Changed this line
    width=3,
    offset=0.1
  ) +
  scale_fill_manual(
    name="Phylum",
    values=setNames(Fittree15good_taxa_merged$NCBI.phylum_colors, Fittree15good_taxa_merged$NCBI.phylum),
    guide=guide_legend(keywidth=0.3, keyheight=0.3, ncol=1)
  ) +
  theme(
    legend.title=element_text(size=10), 
    legend.text=element_text(size=8),
    legend.spacing.y = unit(0.2, "cm")
  )

tree_perfects15_5BCs_good_1.0tmp_fitness_phylum_p2

Summary Stats

The following statistical tests will determine if fitness clusters by evolutionary distance within the phylogenetic tree

# Create a function to calculate distances and differences between 
fitD08D03_dist_diff <- function(pairwise_distances, taxa_merged) {
  distance_df <- as.data.frame(as.table(pairwise_distances)) %>%
    rename(Tip1 = Var1, Tip2 = Var2, Distance = Freq) %>%
    mutate(Tip1 = as.character(Tip1),
           Tip2 = as.character(Tip2)) %>%
    filter(Tip1 != Tip2)  # Remove self-comparisons
  
  results <- distance_df %>%
    left_join(taxa_merged, by = c("Tip1" = "ID")) %>%
    rename(fitD08D03v1 = fitD08D03) %>%
    left_join(taxa_merged, by = c("Tip2" = "ID")) %>%
    rename(fitD08D03v2 = fitD08D03) %>%
    mutate(
      AbsDistance = abs(Distance),
      fitD08D03AbsDiff = abs(fitD08D03v1 - fitD08D03v2)
    ) %>%
    select(Tip1, Tip2, AbsDistance, fitD08D03AbsDiff)
  
  return(results)}

Pearson Correlation: Calculate pairwise distances between tips and differences in fitness values. Plot distances and differences as scatter plot and test for significant correlation using Pearson.

# Calculate pairwise distances
fitD08D03_pairwise_distances <- cophenetic.phylo(Fittree15good)

# Calculate distances and differences
fitD08D03_results <- fitD08D03_dist_diff(fitD08D03_pairwise_distances,Fittree15good_taxa_merged)

# Create the scatter plot
fitD08D03_scatter_plot <- ggplot(fitD08D03_results, aes(x = AbsDistance, y = fitD08D03AbsDiff)) +
  geom_point(alpha = 0.5) +  # Add transparency to points
  geom_smooth(method = "lm", color = "red", se = FALSE) +  # Add a linear trend line
  stat_cor(method = "pearson", p.accuracy = 0.000001, r.accuracy = 0.01) +
  theme_minimal() +
  labs(title = "Absolute Distance vs Absolute Difference in fitD08D03",
       x = "Absolute Value of Distance",
       y = "Absolute Value of Difference in fitD08D03") +
  theme(plot.title = element_text(hjust = 0.5),
        legend.position = "none")

# Display the plot
print(fitD08D03_scatter_plot)


# Optional: Save the plot
ggsave("Perfects/PLOTS/Wilcox/fitD08D03_pearson_scatter_plot.png", 
       fitD08D03_scatter_plot, width = 10, height = 8, dpi = 300)

Close-Related Pairs: Subset to include only pairs of taxa with an absolute phylogenetic distance less than 1. Graphical summaries include a Q-Q (Quantile-Quantile) plot and histrogram of the absolute difference values. The Q-Q plot is graphical method to assess if a dataset follows a normal distribution. The red line added by qqline() represents the expected line if the data were perfectly normally distributed. The histogram provides a visual representation of the distribution of the data.

# This focuses the analysis on closely related taxa
fitD08D03_close <- fitD08D03_results %>% 
  filter(AbsDistance < 1)

# Graphical Summaries

# Create a Q-Q (Quantile-Quantile) plot
qqnorm(fitD08D03_close$fitD08D03AbsDiff)
qqline(fitD08D03_close$fitD08D03AbsDiff, col = "red")


# Create a histogram plot
hist(fitD08D03_close$fitD08D03AbsDiff, main = "Histogram of Close Data")

Far-Related Pairs: Subset to include only pairs of taxa with an absolute phylogenetic distance greater than 1. Graphical summaries include a Q-Q (Quantile-Quantile) plot and histrogram of the absolute difference values. The Q-Q plot is graphical method to assess if a dataset follows a normal distribution. The red line added by qqline() represents the expected line if the data were perfectly normally distributed. The histogram provides a visual representation of the distribution of the data.

# This focuses the analysis on distantly related taxa
fitD08D03_far <- fitD08D03_results %>% 
  filter(AbsDistance > 1)

# Graphical Summaries

# Create a Q-Q (Quantile-Quantile) plot
qqnorm(fitD08D03_far$fitD08D03AbsDiff)
qqline(fitD08D03_far$fitD08D03AbsDiff, col = "red")


# Create a histogram plot
hist(fitD08D03_far$fitD08D03AbsDiff, main = "Histogram of Far Data")

Wilcox Test: Perform Wilcoxon Rank Sum Test (also known as Mann-Whitney U test) comparing the absolute distance values between the close-related and far-related groups.

# Wilcox test between close and far related distances
wilcox_result_fitD08D03 <- wilcox.test(fitD08D03_close$fitD08D03AbsDiff,
                                       fitD08D03_far$fitD08D03AbsDiff)

print(wilcox_result_fitD08D03)

    Wilcoxon rank sum test with continuity correction

data:  fitD08D03_close$fitD08D03AbsDiff and fitD08D03_far$fitD08D03AbsDiff
W = 334730856, p-value < 2.2e-16
alternative hypothesis: true location shift is not equal to 0

Combine Data: Examine the distributions of these differences to understand how they differ and whether these differences are generally larger or smaller for closely related taxa.

# Combine the data and add a group label
fitD08D03_combined_data <- bind_rows(
  mutate(fitD08D03_close, group = "Close"),
  mutate(fitD08D03_far, group = "Far"))

# Remove NA values
fitD08D03_combined_data <- fitD08D03_combined_data %>% 
  filter(!is.na(fitD08D03AbsDiff))

# Create a box plot
fitD08D03_boxplot <- ggplot(fitD08D03_combined_data, aes(x = group, y = fitD08D03AbsDiff)) +
  geom_boxplot() +
  geom_jitter(alpha = 0.1, width = 0.2) +  # Add individual points with some transparency
  labs(title = "Distribution of fitD08D03 Differences",
       x = "Phylogenetic Relationship",
       y = "Absolute Difference in fitD08D03") +
  theme_minimal()

# Display the plot
print(fitD08D03_boxplot)


# Calculate summary statistics
fitD08D03_summary_stats <- fitD08D03_combined_data %>%
  group_by(group) %>%
  summarise(
    n = n(),
    mean = mean(fitD08D03AbsDiff),
    median = median(fitD08D03AbsDiff),
    sd = sd(fitD08D03AbsDiff),
    min = min(fitD08D03AbsDiff),
    max = max(fitD08D03AbsDiff)
  )

# Print summary statistics
print(fitD08D03_summary_stats)

# Perform a formal test for difference in means
fitD08D03_t_test_result <- t.test(fitD08D03AbsDiff ~ group, data = fitD08D03_combined_data)
print(fitD08D03_t_test_result)

    Welch Two Sample t-test

data:  fitD08D03AbsDiff by group
t = -51.84, df = 6728.6, p-value < 2.2e-16
alternative hypothesis: true difference in means between group Close and group Far is not equal to 0
95 percent confidence interval:
 -1.246943 -1.156073
sample estimates:
mean in group Close   mean in group Far 
           1.883948            3.085455 

10 ug/mL TMP Tree

Lib15: MIC (10 ug/mL TMP)

tree_perfects15_5BCs_good_10tmp_fitness_labelled <- ggtree(Fittree15good, layout="circular", branch.length="none") %<+%
  Fittree15good_taxa_merged +
  aes(color = ifelse(fitD09D03 < -1, "Low fitness", "High fitness")) +
  geom_tippoint(aes(color = ifelse(fitD09D03 < -1, "Low fitness", "High fitness")), size=1, alpha=0.8) +
  #geom_tiplab2(aes(label=NCBI.phylum, angle=angle), size=1) +
  theme(legend.position="none") + 
  scale_color_manual(
    "Fitness",
    values = c("Low fitness" = "darkblue", "High fitness" = "gold"),
    na.value = "gray"
  ) +
  guides(color = guide_legend(title = "Fitness"))

tree_perfects15_5BCs_good_10tmp_fitness_labelled

Count the number of complementing homologs (fit > -1) and dropout homologs (fit < -1):

# Count unique IDs with fitD09D03 > -1
high_fitness_count <- Fittree15good_taxa_merged %>%
  filter(fitD09D03 > -1) %>%
  distinct(ID) %>%
  nrow()

# Count unique IDs with fitD09D03 < -1
low_fitness_count <- Fittree15good_taxa_merged %>%
  filter(fitD09D03 < -1) %>%
  distinct(ID) %>%
  nrow()

# Print the results
cat("Number of unique IDs with fitD09D03 > -1:", high_fitness_count, "\n")
Number of unique IDs with fitD09D03 > -1: 128 
cat("Number of unique IDs with fitD09D03 < -1:", low_fitness_count, "\n")
Number of unique IDs with fitD09D03 < -1: 276 

Build the initial DHFR tree with color-scale based on fitness at COMPLEMENTATION and add an additional ring representing taxonomic lineages at the Phylum-level.

tree_perfects15_5BCs_good_10tmp_fitness_phylum_p1 <- ggtree(Fittree15good, layout="circular", branch.length="none") %<+%
  Fittree15good_taxa_merged +
  aes(color = ifelse(fitD09D03 < -1, "Low fitness", "High fitness")) +
  geom_tippoint(aes(color = ifelse(fitD09D03 < -1, "Low fitness", "High fitness")), size=1, alpha=0.8) +
  theme(legend.position="right") + 
  scale_color_manual(
    "Fitness",
    values = c("Low fitness" = "darkblue", "High fitness" = "gold"),
    na.value = "gray")

tree_perfects15_5BCs_good_10tmp_fitness_phylum_p2 <- tree_perfects15_5BCs_good_10tmp_fitness_phylum_p1 +
  new_scale_fill() +
  geom_fruit(
    geom=geom_tile,
    mapping=aes(fill=NCBI.phylum),  # Changed this line
    width=3,
    offset=0.1
  ) +
  scale_fill_manual(
    name="Phylum",
    values=setNames(Fittree15good_taxa_merged$NCBI.phylum_colors, Fittree15good_taxa_merged$NCBI.phylum),
    guide=guide_legend(keywidth=0.3, keyheight=0.3, ncol=1)
  ) +
  theme(
    legend.title=element_text(size=10), 
    legend.text=element_text(size=8),
    legend.spacing.y = unit(0.2, "cm")
  )

tree_perfects15_5BCs_good_10tmp_fitness_phylum_p2

Summary Stats

The following statistical tests will determine if fitness clusters by evolutionary distance within the phylogenetic tree

# Create a function to calculate distances and differences between 
fitD09D03_dist_diff <- function(pairwise_distances, taxa_merged) {
  distance_df <- as.data.frame(as.table(pairwise_distances)) %>%
    rename(Tip1 = Var1, Tip2 = Var2, Distance = Freq) %>%
    mutate(Tip1 = as.character(Tip1),
           Tip2 = as.character(Tip2)) %>%
    filter(Tip1 != Tip2)  # Remove self-comparisons
  
  results <- distance_df %>%
    left_join(taxa_merged, by = c("Tip1" = "ID")) %>%
    rename(fitD09D03v1 = fitD09D03) %>%
    left_join(taxa_merged, by = c("Tip2" = "ID")) %>%
    rename(fitD09D03v2 = fitD09D03) %>%
    mutate(
      AbsDistance = abs(Distance),
      fitD09D03AbsDiff = abs(fitD09D03v1 - fitD09D03v2)
    ) %>%
    select(Tip1, Tip2, AbsDistance, fitD09D03AbsDiff)
  
  return(results)}

Pearson Correlation: Calculate pairwise distances between tips and differences in fitness values. Plot distances and differences as scatter plot and test for significant correlation using Pearson.

# Calculate pairwise distances
fitD09D03_pairwise_distances <- cophenetic.phylo(Fittree15good)

# Calculate distances and differences
fitD09D03_results <- fitD09D03_dist_diff(fitD09D03_pairwise_distances,Fittree15good_taxa_merged)

# Create the scatter plot
fitD09D03_scatter_plot <- ggplot(fitD09D03_results, aes(x = AbsDistance, y = fitD09D03AbsDiff)) +
  geom_point(alpha = 0.5) +  # Add transparency to points
  geom_smooth(method = "lm", color = "red", se = FALSE) +  # Add a linear trend line
  stat_cor(method = "pearson", p.accuracy = 0.000001, r.accuracy = 0.01) +
  theme_minimal() +
  labs(title = "Absolute Distance vs Absolute Difference in fitD09D03",
       x = "Absolute Value of Distance",
       y = "Absolute Value of Difference in fitD09D03") +
  theme(plot.title = element_text(hjust = 0.5),
        legend.position = "none")

# Display the plot
print(fitD09D03_scatter_plot)


# Optional: Save the plot
ggsave("Perfects/PLOTS/Wilcox/fitD09D03_pearson_scatter_plot.png", 
       fitD09D03_scatter_plot, width = 10, height = 8, dpi = 300)

Close-Related Pairs: Subset to include only pairs of taxa with an absolute phylogenetic distance less than 1. Graphical summaries include a Q-Q (Quantile-Quantile) plot and histrogram of the absolute difference values. The Q-Q plot is graphical method to assess if a dataset follows a normal distribution. The red line added by qqline() represents the expected line if the data were perfectly normally distributed. The histogram provides a visual representation of the distribution of the data.

# This focuses the analysis on closely related taxa
fitD09D03_close <- fitD09D03_results %>% 
  filter(AbsDistance < 1)

# Graphical Summaries

# Create a Q-Q (Quantile-Quantile) plot
qqnorm(fitD09D03_close$fitD09D03AbsDiff)
qqline(fitD09D03_close$fitD09D03AbsDiff, col = "red")


# Create a histogram plot
hist(fitD09D03_close$fitD09D03AbsDiff, main = "Histogram of Close Data")

Far-Related Pairs: Subset to include only pairs of taxa with an absolute phylogenetic distance greater than 1. Graphical summaries include a Q-Q (Quantile-Quantile) plot and histrogram of the absolute difference values. The Q-Q plot is graphical method to assess if a dataset follows a normal distribution. The red line added by qqline() represents the expected line if the data were perfectly normally distributed. The histogram provides a visual representation of the distribution of the data.

# This focuses the analysis on distantly related taxa
fitD09D03_far <- fitD09D03_results %>% 
  filter(AbsDistance > 1)

# Graphical Summaries

# Create a Q-Q (Quantile-Quantile) plot
qqnorm(fitD09D03_far$fitD09D03AbsDiff)
qqline(fitD09D03_far$fitD09D03AbsDiff, col = "red")


# Create a histogram plot
hist(fitD09D03_far$fitD09D03AbsDiff, main = "Histogram of Far Data")

Wilcox Test: Perform Wilcoxon Rank Sum Test (also known as Mann-Whitney U test) comparing the absolute distance values between the close-related and far-related groups.

# Wilcox test between close and far related distances
wilcox_result_fitD09D03 <- wilcox.test(fitD09D03_close$fitD09D03AbsDiff,
                                       fitD09D03_far$fitD09D03AbsDiff)

print(wilcox_result_fitD09D03)

    Wilcoxon rank sum test with continuity correction

data:  fitD09D03_close$fitD09D03AbsDiff and fitD09D03_far$fitD09D03AbsDiff
W = 387510616, p-value < 2.2e-16
alternative hypothesis: true location shift is not equal to 0

Combine Data: Examine the distributions of these differences to understand how they differ and whether these differences are generally larger or smaller for closely related taxa.

# Combine the data and add a group label
fitD09D03_combined_data <- bind_rows(
  mutate(fitD09D03_close, group = "Close"),
  mutate(fitD09D03_far, group = "Far"))

# Remove NA values
fitD09D03_combined_data <- fitD09D03_combined_data %>% 
  filter(!is.na(fitD09D03AbsDiff))

# Create a box plot
fitD09D03_boxplot <- ggplot(fitD09D03_combined_data, aes(x = group, y = fitD09D03AbsDiff)) +
  geom_boxplot() +
  geom_jitter(alpha = 0.1, width = 0.2) +  # Add individual points with some transparency
  labs(title = "Distribution of fitD09D03 Differences",
       x = "Phylogenetic Relationship",
       y = "Absolute Difference in fitD09D03") +
  theme_minimal()

# Display the plot
print(fitD09D03_boxplot)


# Calculate summary statistics
fitD09D03_summary_stats <- fitD09D03_combined_data %>%
  group_by(group) %>%
  summarise(
    n = n(),
    mean = mean(fitD09D03AbsDiff),
    median = median(fitD09D03AbsDiff),
    sd = sd(fitD09D03AbsDiff),
    min = min(fitD09D03AbsDiff),
    max = max(fitD09D03AbsDiff)
  )

# Print summary statistics
print(fitD09D03_summary_stats)

# Perform a formal test for difference in means
fitD09D03_t_test_result <- t.test(fitD09D03AbsDiff ~ group, data = fitD09D03_combined_data)
print(fitD09D03_t_test_result)

    Welch Two Sample t-test

data:  fitD09D03AbsDiff by group
t = -14.539, df = 6131.3, p-value < 2.2e-16
alternative hypothesis: true difference in means between group Close and group Far is not equal to 0
95 percent confidence interval:
 -0.7057103 -0.5380190
sample estimates:
mean in group Close   mean in group Far 
           3.684020            4.305885 

50 ug/mL TMP Tree

Lib15: MIC (50 ug/mL TMP)

tree_perfects15_5BCs_good_50tmp_fitness_labelled <- ggtree(Fittree15good, layout="circular", branch.length="none") %<+%
  Fittree15good_taxa_merged +
  aes(color = ifelse(fitD10D03 < -1, "Low fitness", "High fitness")) +
  geom_tippoint(aes(color = ifelse(fitD10D03 < -1, "Low fitness", "High fitness")), size=1, alpha=0.8) +
  #geom_tiplab2(aes(label=NCBI.phylum, angle=angle), size=1) +
  theme(legend.position="none") + 
  scale_color_manual(
    "Fitness",
    values = c("Low fitness" = "darkblue", "High fitness" = "gold"),
    na.value = "gray"
  ) +
  guides(color = guide_legend(title = "Fitness"))

tree_perfects15_5BCs_good_50tmp_fitness_labelled

Count the number of complementing homologs (fit > -1) and dropout homologs (fit < -1):

# Count unique IDs with fitD10D03 > -1
high_fitness_count <- Fittree15good_taxa_merged %>%
  filter(fitD10D03 > -1) %>%
  distinct(ID) %>%
  nrow()

# Count unique IDs with fitD10D03 < -1
low_fitness_count <- Fittree15good_taxa_merged %>%
  filter(fitD10D03 < -1) %>%
  distinct(ID) %>%
  nrow()

# Print the results
cat("Number of unique IDs with fitD10D03 > -1:", high_fitness_count, "\n")
Number of unique IDs with fitD10D03 > -1: 80 
cat("Number of unique IDs with fitD10D03 < -1:", low_fitness_count, "\n")
Number of unique IDs with fitD10D03 < -1: 321 

Build the initial DHFR tree with color-scale based on fitness at COMPLEMENTATION and add an additional ring representing taxonomic lineages at the Phylum-level.

tree_perfects15_5BCs_good_50tmp_fitness_phylum_p1 <- ggtree(Fittree15good, layout="circular", branch.length="none") %<+%
  Fittree15good_taxa_merged +
  aes(color = ifelse(fitD10D03 < -1, "Low fitness", "High fitness")) +
  geom_tippoint(aes(color = ifelse(fitD10D03 < -1, "Low fitness", "High fitness")), size=1, alpha=0.8) +
  theme(legend.position="right") + 
  scale_color_manual(
    "Fitness",
    values = c("Low fitness" = "darkblue", "High fitness" = "gold"),
    na.value = "gray")

tree_perfects15_5BCs_good_50tmp_fitness_phylum_p2 <- tree_perfects15_5BCs_good_50tmp_fitness_phylum_p1 +
  new_scale_fill() +
  geom_fruit(
    geom=geom_tile,
    mapping=aes(fill=NCBI.phylum),  # Changed this line
    width=3,
    offset=0.1
  ) +
  scale_fill_manual(
    name="Phylum",
    values=setNames(Fittree15good_taxa_merged$NCBI.phylum_colors, Fittree15good_taxa_merged$NCBI.phylum),
    guide=guide_legend(keywidth=0.3, keyheight=0.3, ncol=1)
  ) +
  theme(
    legend.title=element_text(size=10), 
    legend.text=element_text(size=8),
    legend.spacing.y = unit(0.2, "cm")
  )

tree_perfects15_5BCs_good_50tmp_fitness_phylum_p2

Summary Stats

The following statistical tests will determine if fitness clusters by evolutionary distance within the phylogenetic tree

# Create a function to calculate distances and differences between 
fitD10D03_dist_diff <- function(pairwise_distances, taxa_merged) {
  distance_df <- as.data.frame(as.table(pairwise_distances)) %>%
    rename(Tip1 = Var1, Tip2 = Var2, Distance = Freq) %>%
    mutate(Tip1 = as.character(Tip1),
           Tip2 = as.character(Tip2)) %>%
    filter(Tip1 != Tip2)  # Remove self-comparisons
  
  results <- distance_df %>%
    left_join(taxa_merged, by = c("Tip1" = "ID")) %>%
    rename(fitD10D03v1 = fitD10D03) %>%
    left_join(taxa_merged, by = c("Tip2" = "ID")) %>%
    rename(fitD10D03v2 = fitD10D03) %>%
    mutate(
      AbsDistance = abs(Distance),
      fitD10D03AbsDiff = abs(fitD10D03v1 - fitD10D03v2)
    ) %>%
    select(Tip1, Tip2, AbsDistance, fitD10D03AbsDiff)
  
  return(results)}

Pearson Correlation: Calculate pairwise distances between tips and differences in fitness values. Plot distances and differences as scatter plot and test for significant correlation using Pearson.

# Calculate pairwise distances
fitD10D03_pairwise_distances <- cophenetic.phylo(Fittree15good)

# Calculate distances and differences
fitD10D03_results <- fitD10D03_dist_diff(fitD10D03_pairwise_distances,Fittree15good_taxa_merged)

# Create the scatter plot
fitD10D03_scatter_plot <- ggplot(fitD10D03_results, aes(x = AbsDistance, y = fitD10D03AbsDiff)) +
  geom_point(alpha = 0.5) +  # Add transparency to points
  geom_smooth(method = "lm", color = "red", se = FALSE) +  # Add a linear trend line
  stat_cor(method = "pearson", p.accuracy = 0.000001, r.accuracy = 0.01) +
  theme_minimal() +
  labs(title = "Absolute Distance vs Absolute Difference in fitD10D03",
       x = "Absolute Value of Distance",
       y = "Absolute Value of Difference in fitD10D03") +
  theme(plot.title = element_text(hjust = 0.5),
        legend.position = "none")

# Display the plot
print(fitD10D03_scatter_plot)


# Optional: Save the plot
ggsave("Perfects/PLOTS/Wilcox/fitD10D03_pearson_scatter_plot.png", 
       fitD10D03_scatter_plot, width = 10, height = 8, dpi = 300)

Close-Related Pairs: Subset to include only pairs of taxa with an absolute phylogenetic distance less than 1. Graphical summaries include a Q-Q (Quantile-Quantile) plot and histrogram of the absolute difference values. The Q-Q plot is graphical method to assess if a dataset follows a normal distribution. The red line added by qqline() represents the expected line if the data were perfectly normally distributed. The histogram provides a visual representation of the distribution of the data.

# This focuses the analysis on closely related taxa
fitD10D03_close <- fitD10D03_results %>% 
  filter(AbsDistance < 1)

# Graphical Summaries

# Create a Q-Q (Quantile-Quantile) plot
qqnorm(fitD10D03_close$fitD10D03AbsDiff)
qqline(fitD10D03_close$fitD10D03AbsDiff, col = "red")


# Create a histogram plot
hist(fitD10D03_close$fitD10D03AbsDiff, main = "Histogram of Close Data")

Far-Related Pairs: Subset to include only pairs of taxa with an absolute phylogenetic distance greater than 1. Graphical summaries include a Q-Q (Quantile-Quantile) plot and histrogram of the absolute difference values. The Q-Q plot is graphical method to assess if a dataset follows a normal distribution. The red line added by qqline() represents the expected line if the data were perfectly normally distributed. The histogram provides a visual representation of the distribution of the data.

# This focuses the analysis on distantly related taxa
fitD10D03_far <- fitD10D03_results %>% 
  filter(AbsDistance > 1)

# Graphical Summaries

# Create a Q-Q (Quantile-Quantile) plot
qqnorm(fitD10D03_far$fitD10D03AbsDiff)
qqline(fitD10D03_far$fitD10D03AbsDiff, col = "red")


# Create a histogram plot
hist(fitD10D03_far$fitD10D03AbsDiff, main = "Histogram of Far Data")

Wilcox Test: Perform Wilcoxon Rank Sum Test (also known as Mann-Whitney U test) comparing the absolute distance values between the close-related and far-related groups.

# Wilcox test between close and far related distances
wilcox_result_fitD10D03 <- wilcox.test(fitD10D03_close$fitD10D03AbsDiff,
                                       fitD10D03_far$fitD10D03AbsDiff)

print(wilcox_result_fitD10D03)

    Wilcoxon rank sum test with continuity correction

data:  fitD10D03_close$fitD10D03AbsDiff and fitD10D03_far$fitD10D03AbsDiff
W = 389640364, p-value < 2.2e-16
alternative hypothesis: true location shift is not equal to 0

Combine Data: Examine the distributions of these differences to understand how they differ and whether these differences are generally larger or smaller for closely related taxa.

# Combine the data and add a group label
fitD10D03_combined_data <- bind_rows(
  mutate(fitD10D03_close, group = "Close"),
  mutate(fitD10D03_far, group = "Far"))

# Remove NA values
fitD10D03_combined_data <- fitD10D03_combined_data %>% 
  filter(!is.na(fitD10D03AbsDiff))

# Create a box plot
fitD10D03_boxplot <- ggplot(fitD10D03_combined_data, aes(x = group, y = fitD10D03AbsDiff)) +
  geom_boxplot() +
  geom_jitter(alpha = 0.1, width = 0.2) +  # Add individual points with some transparency
  labs(title = "Distribution of fitD10D03 Differences",
       x = "Phylogenetic Relationship",
       y = "Absolute Difference in fitD10D03") +
  theme_minimal()

# Display the plot
print(fitD10D03_boxplot)


# Calculate summary statistics
fitD10D03_summary_stats <- fitD10D03_combined_data %>%
  group_by(group) %>%
  summarise(
    n = n(),
    mean = mean(fitD10D03AbsDiff),
    median = median(fitD10D03AbsDiff),
    sd = sd(fitD10D03AbsDiff),
    min = min(fitD10D03AbsDiff),
    max = max(fitD10D03AbsDiff)
  )

# Print summary statistics
print(fitD10D03_summary_stats)

# Perform a formal test for difference in means
fitD10D03_t_test_result <- t.test(fitD10D03AbsDiff ~ group, data = fitD10D03_combined_data)
print(fitD10D03_t_test_result)

    Welch Two Sample t-test

data:  fitD10D03AbsDiff by group
t = -13.392, df = 6216.1, p-value < 2.2e-16
alternative hypothesis: true difference in means between group Close and group Far is not equal to 0
95 percent confidence interval:
 -0.5300265 -0.3946694
sample estimates:
mean in group Close   mean in group Far 
           2.880586            3.342934 

400x MIC Tree (417)

Lib15: 400x MIC (200 ug/mL TMP)

tree_perfects15_5BCs_good_200tmp_fitness_labelled <- ggtree(Fittree15good, layout="circular", branch.length="none") %<+%
  Fittree15good_taxa_merged +
  aes(color = ifelse(fitD11D03 < -1, "Low fitness", "High fitness")) +
  geom_tippoint(aes(color = ifelse(fitD11D03 < -1, "Low fitness", "High fitness")), size=1, alpha=0.8) +
  #geom_tiplab2(aes(label=NCBI.phylum, angle=angle), size=1) +
  theme(legend.position="none") + 
  scale_color_manual(
    "Fitness",
    values = c("Low fitness" = "darkblue", "High fitness" = "gold"),
    na.value = "gray"
  ) +
  guides(color = guide_legend(title = "Fitness"))

tree_perfects15_5BCs_good_200tmp_fitness_labelled

Count the number of complementing homologs (fit > -1) and dropout homologs (fit < -1):

# Count unique IDs with fitD11D03 > -1
high_fitness_count <- Fittree15good_taxa_merged %>%
  filter(fitD11D03 > -1) %>%
  distinct(ID) %>%
  nrow()

# Count unique IDs with fitD11D03 < -1
low_fitness_count <- Fittree15good_taxa_merged %>%
  filter(fitD11D03 < -1) %>%
  distinct(ID) %>%
  nrow()

# Print the results
cat("Number of unique IDs with fitD11D03 > -1:", high_fitness_count, "\n")
Number of unique IDs with fitD11D03 > -1: 27 
cat("Number of unique IDs with fitD11D03 < -1:", low_fitness_count, "\n")
Number of unique IDs with fitD11D03 < -1: 332 

Build the Complementation-capable DHFR tree with color-scale based on fitness at 400x MIC and add an additional ring representing taxonomic lineages at the Phylum-level.

tree_perfects15_5BCs_good_200tmp_fitness_phylum_p1 <- ggtree(Fittree15good, layout="circular", branch.length="none") %<+%
  Fittree15good_taxa_merged +
  aes(color = ifelse(fitD11D03 < -1, "Low fitness", "High fitness")) +
  geom_tippoint(aes(color = ifelse(fitD11D03 < -1, "Low fitness", "High fitness")), size=1, alpha=0.8) +
  theme(legend.position="right") + 
  scale_color_manual(
    "Fitness",
    values = c("Low fitness" = "darkblue", "High fitness" = "gold"),
    na.value = "gray")

tree_perfects15_5BCs_good_200tmp_fitness_phylum_p2 <- tree_perfects15_5BCs_good_200tmp_fitness_phylum_p1 +
  new_scale_fill() +
  geom_fruit(
    geom=geom_tile,
    mapping=aes(fill=NCBI.phylum),  # Changed this line
    width=3,
    offset=0.1
  ) +
  scale_fill_manual(
    name="Phylum",
    values=setNames(Fittree15good_taxa_merged$NCBI.phylum_colors, Fittree15good_taxa_merged$NCBI.phylum),
    guide=guide_legend(keywidth=0.3, keyheight=0.3, ncol=1)
  ) +
  theme(
    legend.title=element_text(size=10), 
    legend.text=element_text(size=8),
    legend.spacing.y = unit(0.2, "cm")
  )

tree_perfects15_5BCs_good_200tmp_fitness_phylum_p2

Summary Stats

The following statistical tests will determine if fitness clusters by evolutionary distance within the phylogenetic tree

# Create a function to calculate distances and differences between 
fitD11D03_dist_diff <- function(pairwise_distances, taxa_merged) {
  distance_df <- as.data.frame(as.table(pairwise_distances)) %>%
    rename(Tip1 = Var1, Tip2 = Var2, Distance = Freq) %>%
    mutate(Tip1 = as.character(Tip1),
           Tip2 = as.character(Tip2)) %>%
    filter(Tip1 != Tip2)  # Remove self-comparisons
  
  results <- distance_df %>%
    left_join(taxa_merged, by = c("Tip1" = "ID")) %>%
    rename(fitD11D03v1 = fitD11D03) %>%
    left_join(taxa_merged, by = c("Tip2" = "ID")) %>%
    rename(fitD11D03v2 = fitD11D03) %>%
    mutate(
      AbsDistance = abs(Distance),
      fitD11D03AbsDiff = abs(fitD11D03v1 - fitD11D03v2)
    ) %>%
    select(Tip1, Tip2, AbsDistance, fitD11D03AbsDiff)
  
  return(results)}

Pearson Correlation: Calculate pairwise distances between tips and differences in fitness values. Plot distances and differences as scatter plot and test for significant correlation using Pearson.

# Calculate pairwise distances
fitD11D03_pairwise_distances <- cophenetic.phylo(Fittree15good)

# Calculate distances and differences
fitD11D03_results <- fitD11D03_dist_diff(fitD11D03_pairwise_distances,Fittree15good_taxa_merged)

# Create the scatter plot
fitD11D03_scatter_plot <- ggplot(fitD11D03_results, aes(x = AbsDistance, y = fitD11D03AbsDiff)) +
  geom_point(alpha = 0.5) +  # Add transparency to points
  geom_smooth(method = "lm", color = "red", se = FALSE) +  # Add a linear trend line
  stat_cor(method = "pearson", p.accuracy = 0.000001, r.accuracy = 0.01) +
  theme_minimal() +
  labs(title = "Absolute Distance vs Absolute Difference in fitD11D03",
       x = "Absolute Value of Distance",
       y = "Absolute Value of Difference in fitD11D03") +
  theme(plot.title = element_text(hjust = 0.5),
        legend.position = "none")

# Display the plot
print(fitD11D03_scatter_plot)


# Optional: Save the plot
ggsave("Perfects/PLOTS/Wilcox/fitD11D03_pearson_scatter_plot.png", 
       fitD11D03_scatter_plot, width = 10, height = 8, dpi = 300)

Close-Related Pairs: Subset to include only pairs of taxa with an absolute phylogenetic distance less than 1. Graphical summaries include a Q-Q (Quantile-Quantile) plot and histrogram of the absolute difference values. The Q-Q plot is graphical method to assess if a dataset follows a normal distribution. The red line added by qqline() represents the expected line if the data were perfectly normally distributed. The histogram provides a visual representation of the distribution of the data.

# This focuses the analysis on closely related taxa
fitD11D03_close <- fitD11D03_results %>% 
  filter(AbsDistance < 1)

# Graphical Summaries

# Create a Q-Q (Quantile-Quantile) plot
qqnorm(fitD11D03_close$fitD11D03AbsDiff)
qqline(fitD11D03_close$fitD11D03AbsDiff, col = "red")


# Create a histogram plot
hist(fitD11D03_close$fitD11D03AbsDiff, main = "Histogram of Close Data")

Far-Related Pairs: Subset to include only pairs of taxa with an absolute phylogenetic distance greater than 1. Graphical summaries include a Q-Q (Quantile-Quantile) plot and histrogram of the absolute difference values. The Q-Q plot is graphical method to assess if a dataset follows a normal distribution. The red line added by qqline() represents the expected line if the data were perfectly normally distributed. The histogram provides a visual representation of the distribution of the data.

# This focuses the analysis on distantly related taxa
fitD11D03_far <- fitD11D03_results %>% 
  filter(AbsDistance > 1)

# Graphical Summaries

# Create a Q-Q (Quantile-Quantile) plot
qqnorm(fitD11D03_far$fitD11D03AbsDiff)
qqline(fitD11D03_far$fitD11D03AbsDiff, col = "red")


# Create a histogram plot
hist(fitD11D03_far$fitD11D03AbsDiff, main = "Histogram of Far Data")

Wilcox Test: Perform Wilcoxon Rank Sum Test (also known as Mann-Whitney U test) comparing the absolute distance values between the close-related and far-related groups.

# Wilcox test between close and far related distances
wilcox_result_fitD11D03 <- wilcox.test(fitD11D03_close$fitD11D03AbsDiff,
                                       fitD11D03_far$fitD11D03AbsDiff)

print(wilcox_result_fitD11D03)

    Wilcoxon rank sum test with continuity correction

data:  fitD11D03_close$fitD11D03AbsDiff and fitD11D03_far$fitD11D03AbsDiff
W = 283156404, p-value < 2.2e-16
alternative hypothesis: true location shift is not equal to 0

Interpretation:

  • Significant Difference: The extremely low p-value (< 2.2e-16) indicates a statistically significant difference between the fitD11D03AbsDiff values of closely related taxa (fitnessD07D03_close) and more distantly related taxa (fitD11D03_far).

  • Distribution Comparison: The test suggests that the distribution of fitD11D03AbsDiff values is significantly different between close and far phylogenetic relationships.

  • Magnitude and Direction: While the test tells us there’s a significant difference, it doesn’t tell us about the magnitude or direction of this difference. You’d need to look at descriptive statistics (like medians) of both groups to understand this.

  • Biological Significance: This result suggests that the absolute differences in fitD11D03 values are not the same for closely related taxa compared to more distantly related taxa. This could imply that evolutionary distance does play a role in the similarity (or difference) of fitD11D03 values, despite the weak correlation you found earlier.

  • Further Investigation: It would be worth examining the actual distributions of these differences (e.g., with box plots) to understand how they differ. Are the differences generally larger or smaller for closely related taxa?

  • Caution: Remember that with very large sample sizes, even small differences can become statistically significant. It’s important to consider the practical or biological significance of these differences, not just the statistical significance.

This result adds nuance to your earlier findings, suggesting that while there might not be a strong linear correlation between phylogenetic distance and fitD11D03 differences, there is a significant difference in how these values vary among close relatives versus more distant relatives.

Combine Data: Examine the distributions of these differences to understand how they differ and whether these differences are generally larger or smaller for closely related taxa.

# Combine the data and add a group label
fitD11D03_combined_data <- bind_rows(
  mutate(fitD11D03_close, group = "Close"),
  mutate(fitD11D03_far, group = "Far"))

# Remove NA values
fitD11D03_combined_data <- fitD11D03_combined_data %>% 
  filter(!is.na(fitD11D03AbsDiff))

# Create a box plot
fitD11D03_boxplot <- ggplot(fitD11D03_combined_data, aes(x = group, y = fitD11D03AbsDiff)) +
  geom_boxplot() +
  geom_jitter(alpha = 0.1, width = 0.2) +  # Add individual points with some transparency
  labs(title = "Distribution of fitD11D03 Differences",
       x = "Phylogenetic Relationship",
       y = "Absolute Difference in fitD11D03") +
  theme_minimal()

# Display the plot
print(fitD11D03_boxplot)


# Calculate summary statistics
fitD11D03_summary_stats <- fitD11D03_combined_data %>%
  group_by(group) %>%
  summarise(
    n = n(),
    mean = mean(fitD11D03AbsDiff),
    median = median(fitD11D03AbsDiff),
    sd = sd(fitD11D03AbsDiff),
    min = min(fitD11D03AbsDiff),
    max = max(fitD11D03AbsDiff)
  )

# Print summary statistics
print(fitD11D03_summary_stats)

# Perform a formal test for difference in means
fitD11D03_t_test_result <- t.test(fitD11D03AbsDiff ~ group, data = fitD11D03_combined_data)
print(fitD11D03_t_test_result)

    Welch Two Sample t-test

data:  fitD11D03AbsDiff by group
t = -26.178, df = 6206.9, p-value < 2.2e-16
alternative hypothesis: true difference in means between group Close and group Far is not equal to 0
95 percent confidence interval:
 -0.9692797 -0.8342237
sample estimates:
mean in group Close   mean in group Far 
           2.238397            3.140148 

Interpretation:

  • Significant Difference: There is a statistically significant difference in the fitD11D03AbsDiff values between closely related taxa and more distantly related taxa.

  • Magnitude of Difference: On average, the absolute difference in fitD11D03 values is about 1.226 units smaller for closely related taxa compared to distantly related taxa.

  • Consistency of Difference: The narrow confidence interval (-1.263865 to -1.188174) suggests that this difference is quite consistent across the dataset.

  • Biological Significance: Closely related taxa tend to have more similar fitD11D03 values (smaller differences) compared to distantly related taxa. This suggests that the fitD11D03 trait shows some degree of phylogenetic signal or conservation.

  • Evolutionary Implications: This result indicates that evolutionary relatedness does play a role in the similarity of fitD11D03 values. Closely related species tend to have more similar values, which could be due to shared evolutionary history or similar environmental adaptations.

  • Contrast with Earlier Correlation: While earlier you found a weak correlation between phylogenetic distance and fitD11D03 differences, this t-test reveals a clear distinction between close and distant relationships. This highlights the importance of considering different analytical approaches, as they can reveal different aspects of the data.

  • Practical Significance: The difference in means (about 1.226) should be considered in the context of the overall range and biological meaning of fitD11D03 values to determine its practical significance.

In summary, this analysis provides strong evidence that closely related taxa have more similar fitD11D03 values compared to distantly related taxa. This supports the idea that there is a phylogenetic component to the fitD11D03 trait, even if it’s not captured well by a simple linear correlation with phylogenetic distance. The large sample size and highly significant result suggest that this is a robust finding, though you should always consider the biological context and potential confounding factors in your interpretation.

Plot Together

# Remove legends
tmp0.0.tree <- tree_perfects15_5BCs_good_0.0tmp_fitness_phylum_p2 +
  theme(legend.position = 'none') +
  ggtitle("0.0 TMP") +
  theme(plot.title = element_text(hjust = 0.5))

tmp0.058.tree <- tree_perfects15_5BCs_good_0.058tmp_fitness_phylum_p2 +
  theme(legend.position = 'none') +
  ggtitle("0.058 TMP") +
  theme(plot.title = element_text(hjust = 0.5))

tmp0.5.tree <- tree_perfects15_5BCs_good_0.5tmp_fitness_phylum_p2 +
  theme(legend.position = 'none') +
  ggtitle("0.5 TMP \n(MIC)") +
  theme(plot.title = element_text(hjust = 0.5))

tmp1.0.tree <- tree_perfects15_5BCs_good_1.0tmp_fitness_phylum_p2 +
  theme(legend.position = 'none') +
  ggtitle("1.0 TMP") +
  theme(plot.title = element_text(hjust = 0.5))

tmp10.tree <- tree_perfects15_5BCs_good_10tmp_fitness_phylum_p2 +
  theme(legend.position = 'none') +
  ggtitle("10 TMP") +
  theme(plot.title = element_text(hjust = 0.5))

tmp50.tree <- tree_perfects15_5BCs_good_50tmp_fitness_phylum_p2 +
  theme(legend.position = 'none') +
  ggtitle("50 TMP") +
  theme(plot.title = element_text(hjust = 0.5))

tmp200.tree <- tree_perfects15_5BCs_good_200tmp_fitness_phylum_p2 +
  theme(legend.position = 'none') +
  ggtitle("200 TMP \n(400x MIC)") +
  theme(plot.title = element_text(hjust = 0.5))
# Create the patchwork with the legend on the right
patch9 <- (tmp0.0.tree) /
           (tmp0.058.tree | tmp0.5.tree | tmp1.0.tree) /
           (tmp10.tree | tmp50.tree | tmp200.tree) +
           plot_layout(heights = c(1, 1, 1))

patch9

Plot just the colored tree tips (no phylum ring)

# Remove legends
tmp0.0.tree.tips <- tree_perfects15_5BCs_good_0.0tmp_fitness_labelled +
  ggtitle("0.0 TMP \n(Complementation)") +
  theme(plot.title = element_text(hjust = 0.5))

tmp0.058.tree.tips <- tree_perfects15_5BCs_good_0.058tmp_fitness_labelled +
  ggtitle("0.058 TMP") +
  theme(plot.title = element_text(hjust = 0.5))

tmp0.5.tree.tips <- tree_perfects15_5BCs_good_0.5tmp_fitness_labelled +
  ggtitle("0.05 TMP \n(MIC)") +
  theme(plot.title = element_text(hjust = 0.5))

tmp1.0.tree.tips <- tree_perfects15_5BCs_good_1.0tmp_fitness_labelled +
  ggtitle("1.0 TMP") +
  theme(plot.title = element_text(hjust = 0.5))

tmp10.tree.tips <- tree_perfects15_5BCs_good_10tmp_fitness_labelled +
  ggtitle("10 TMP") +
  theme(plot.title = element_text(hjust = 0.5))

tmp50.tree.tips <- tree_perfects15_5BCs_good_50tmp_fitness_labelled +
  ggtitle("50 TMP") +
  theme(plot.title = element_text(hjust = 0.5))

tmp200.tree.tips <- tree_perfects15_5BCs_good_200tmp_fitness_labelled +
  ggtitle("200 TMP \n(400x MIC)") +
  theme(plot.title = element_text(hjust = 0.5))
# Plot together
patch10 <- (tmp0.0.tree.tips) /
           (tmp0.058.tree.tips | tmp0.5.tree.tips | tmp1.0.tree.tips) /
           (tmp10.tree.tips | tmp50.tree.tips | tmp200.tree.tips) +
           plot_layout(heights = c(1, 1, 1))

patch10

Plot the MIC and 400x MIC trees side-by-side:

# For the first tree
tree_perfects15_5BCs_good_0.5tmp_fitness_phylum_p2_no_legend <- tree_perfects15_5BCs_good_0.5tmp_fitness_phylum_p2 +
  theme(legend.position="none") +
  ggtitle("Codon 1 Library \nMIC (0.5 ug/mL TMP)") +
  theme(plot.title = element_text(hjust = 0.5))

# For the second tree (this seems correct, but I'll include it for completeness)
tree_perfects15_5BCs_good_200tmp_fitness_phylum_p2_no_legend <- tree_perfects15_5BCs_good_200tmp_fitness_phylum_p2 +
  theme(legend.position="right") +
  ggtitle("Codon 1 Library \n400x MIC (200 ug/mL TMP)") +
  theme(plot.title = element_text(hjust = 0.5))

# Combine the plots
patch11 <- tree_perfects15_5BCs_good_0.5tmp_fitness_phylum_p2_no_legend | tree_perfects15_5BCs_good_200tmp_fitness_phylum_p2_no_legend
patch11

Shared Perfects Trees

Retain only perfects with a fitness score >= -1 and a minimum of 5 BCs (numprunedBCs > 4) shared between Lib15 (fitD05D03) and Lib16 (fitD12D04). This list includes 194 unique DHFR perfects. Use the perfects_15_16_shared_5BCs_good dataset to construct a phylogenetic tree for displaying complementation fitness and resistance to trimethoprim fitness.

First, calculate min and max fitness by library:

# Minimum fitness of Shared Perfects in Lib15 at Complementation (5BCs)
min(perfects_15_16_shared_5BCs$fitD05D03, na.rm = TRUE)
[1] -6.29133
# Maximum fitness of Shared Perfects in Lib15 at Complementation (5BCs)
max(perfects_15_16_shared_5BCs$fitD05D03, na.rm = TRUE)
[1] 0.7437646
# Minimum fitness of Shared Perfects in Lib16 at Complementation (5BCs)
min(perfects_15_16_shared_5BCs$fitD12D04, na.rm = TRUE)
[1] -5.204886
# Maximum fitness of Shared Perfects in Lib16 at Complementation (5BCs)
max(perfects_15_16_shared_5BCs$fitD12D04, na.rm = TRUE)
[1] 0.7499431
# Average fitness of Shared Perfects in Lib15 at Complementation (5BCs)
mean(perfects_15_16_shared_5BCs$fitD05D03, na.rm = TRUE)
[1] -1.041182
# Average fitness of Shared Perfects in Lib16 at Complementation (5BCs)
mean(perfects_15_16_shared_5BCs$fitD12D04, na.rm = TRUE)
[1] -0.9544985

Retain 194 unique DHFR perfects with a fitness score >= -1 and a minimum of 5 BCs (numprunedBCs > 4) shared between Lib15 (fitD05D03) and Lib16 (fitD12D04).

# Subset dataset for tree building
perfects_15_16_shared_5BCs_good_tree <- perfects_15_16_shared_5BCs_good %>%
  select(mutID, seq.x,
         fitD05D03, fitD06D03, fitD07D03, fitD08D03, fitD09D03, fitD10D03, fitD11D03,
         fitD12D04, fitE01D04, fitE02D04, fitE03D04, fitE04D04, fitE05D04, fitE06D04)

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

# Rename "mutID" to "ID" to merge with `orginfo` df:
perfects_15_16_shared_5BCs_good_tree <- perfects_15_16_shared_5BCs_good_tree %>% rename(ID = mutID)

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

Create a FASTA file containing the ID and its associated protein sequence for alignment

# Collect the sequences in FASTA format
perfects_15_16_shared_5BCs_good_tree_fasta_content <- paste(">",perfects_15_16_shared_5BCs_good_tree$ID, "\n", perfects_15_16_shared_5BCs_good_tree$seq, "\n", sep = "", collapse = "")

# Define the file path in the working directory
perfects_15_16_shared_5BCs_good_tree_fasta_path <- file.path(getwd(), "Perfects/TREES/Lib.15.16.shared.5BCs.good.perfects.complement.fasta")

# Write the FASTA content to the file
writeLines(perfects_15_16_shared_5BCs_good_tree_fasta_content, con = perfects_15_16_shared_5BCs_good_tree_fasta_path)

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 Perfects/TREES/Lib.15.16.shared.5BCs.good.perfects.complement.fasta -o Perfects/TREES/Lib.15.16.shared.5BCs.good.perfects.complement.aligned.fasta --outfmt=fa --force

Use FastTree (phylogenetic tree building program) to infer the tree from the aligned amino acid sequences:

# ML Model: Jones-Taylor-Thorton
# chmod +x FastTree
./Scripts/FastTree Perfects/TREES/Lib.15.16.shared.5BCs.good.perfects.complement.aligned.fasta > Perfects/TREES/Lib.15.16.shared.5BCs.good.perfects.complement.tree.newick
FastTree Version 2.1.11 No SSE3
Alignment: Perfects/TREES/Lib.15.16.shared.5BCs.good.perfects.complement.aligned.fasta
Amino acid distances: BLOSUM45 Joins: balanced Support: SH-like 1000
Search: Normal +NNI +SPR (2 rounds range 10) +ML-NNI opt-each=1
TopHits: 1.00*sqrtN close=default refresh=0.80
ML Model: Jones-Taylor-Thorton, CAT approximation with 20 rate categories
Initial topology in 0.05 seconds
Refining topology: 30 rounds ME-NNIs, 2 rounds ME-SPRs, 15 rounds ML-NNIs
      0.10 seconds: SPR round   1 of   2, 101 of 386 nodes
      0.21 seconds: ME NNI round 11 of 30, 1 of 192 splits
      0.33 seconds: SPR round   2 of   2, 301 of 386 nodes
Total branch-length 42.049 after 0.38 sec
      0.46 seconds: ML Lengths 101 of 192 splits
      0.82 seconds: ML NNI round 1 of 15, 101 of 192 splits, 13 changes (max delta 4.395)
ML-NNI round 1: LogLk = -35653.947 NNIs 29 max delta 7.54 Time 1.10
      1.11 seconds: Site likelihoods with rate category 1 of 20
      1.21 seconds: Site likelihoods with rate category 9 of 20
      1.32 seconds: Site likelihoods with rate category 17 of 20
Switched to using 20 rate categories (CAT approximation)
Rate categories were divided by 1.100 so that average rate = 1.0
CAT-based log-likelihoods may not be comparable across runs
Use -gamma for approximate but comparable Gamma(20) log-likelihoods
      1.58 seconds: ML NNI round 2 of 15, 101 of 192 splits, 7 changes (max delta 1.826)
ML-NNI round 2: LogLk = -33572.122 NNIs 21 max delta 5.82 Time 1.83
      1.82 seconds: ML NNI round 3 of 15, 1 of 192 splits
      2.04 seconds: ML NNI round 3 of 15, 101 of 192 splits, 6 changes (max delta 2.045)
ML-NNI round 3: LogLk = -33558.731 NNIs 7 max delta 2.05 Time 2.10
ML-NNI round 4: LogLk = -33557.361 NNIs 1 max delta 0.01 Time 2.24
Turning off heuristics for final round of ML NNIs (converged)
      2.23 seconds: ML NNI round 5 of 15, 1 of 192 splits
      2.57 seconds: ML NNI round 5 of 15, 101 of 192 splits, 1 changes (max delta 0.005)
ML-NNI round 5: LogLk = -33541.170 NNIs 7 max delta 2.92 Time 2.88 (final)
      2.88 seconds: ML Lengths 1 of 192 splits
Optimize all lengths: LogLk = -33540.447 Time 3.05
      3.30 seconds: ML split tests for    100 of    191 internal splits
Total time: 3.54 seconds Unique: 194/194 Bad splits: 0/191

Newick Tree File

Import the newick tree file based on the sequence alignment of shared perfects derived from Lib15 & Lib16 mapping file:

# Full tree alignment (194 mutID)
Fittree15.16 <- read.tree("Perfects/TREES/Lib.15.16.shared.5BCs.good.perfects.complement.tree.newick")   # newick format
Fittree15.16

Phylogenetic tree with 194 tips and 192 internal nodes.

Tip labels:
  WP_006716908, WP_005647059, WP_009500922, WP_005765106, WP_011201021, WP_011608342, ...
Node labels:
  , 0.890, 0.935, 0.983, 0.994, 0.904, ...

Unrooted; includes branch lengths.

Extract the tip labels from the newick tree file to match with NCBI taxonomy for downstream plotting:

# Extract tip labels from Alltree15
Fittree15.16_tip_labels <- Fittree15.16$tip.label

# Create a new data frame with unique tip labels
Fittree15.16_tip_labels_df <- data.frame(tip.label = unique(Fittree15.16_tip_labels))

# Print the first few rows of the new data frame
head(Fittree15.16_tip_labels_df)

Match each tip.label ID in Alltree15 with it’s associated TaxID from orginfo dataframe:

# Rename column "tip.label" to "ID"
colnames(Fittree15.16_tip_labels_df) <- c("ID")

# Merge orginfo with Alltree15_tip_labels_df based on the shared ID
Fittree15.16_taxa <- merge(Fittree15.16_tip_labels_df, orginfo, by = "ID", all.x = TRUE)

# Print the first few rows of the merged data frame
head(Fittree15.16_taxa)

Add fitness scores to the Fittree15_taxa_merged object prior to plotting

Fittree15.16_taxa_merged <- Fittree15.16_taxa %>%
  left_join(perfects_15_16_shared_5BCs_good_tree %>% select(ID, fitD05D03, fitD06D03, fitD07D03, fitD08D03, fitD09D03, fitD10D03, fitD11D03, fitD12D04, fitE01D04, fitE02D04, fitE03D04, fitE04D04, fitE05D04, fitE06D04), by = "ID")

Merge the NCBI taxonomy columns to Fittree15.16_taxa based on shared TaxID

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

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

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

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

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

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

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

# NCBI.family
Fittree15.16_taxa_merged$NCBI.genus[Fittree15.16_taxa_merged$TaxID %in% ncbi_taxa$TaxID] <- ncbi_taxa$NCBI.genus[match(Fittree15.16_taxa_merged$TaxID[Fittree15.16_taxa_merged$TaxID %in% ncbi_taxa$TaxID], ncbi_taxa$TaxID)]

# NCBI.family
Fittree15.16_taxa_merged$NCBI.species[Fittree15.16_taxa_merged$TaxID %in% ncbi_taxa$TaxID] <- ncbi_taxa$NCBI.species[match(Fittree15.16_taxa_merged$TaxID[Fittree15.16_taxa_merged$TaxID %in% ncbi_taxa$TaxID], ncbi_taxa$TaxID)]

Pct Similarity E coli

Plot a circular phylogenetic tree with color-scale based on phylogenetic distance between each DHFR homolog relative to the E. coli DHFR gene.

fittree15.16_NCBI_ecoli_ident <- ggtree(Fittree15.16, layout="circular", branch.length="none") %<+% 
  Fittree15.16_taxa_merged + 
  aes(color=PctIdentEcoli*100) +
  geom_tippoint(aes(color=PctIdentEcoli*100), size=1, alpha=0.8) +
  geom_tiplab2(aes(color=PctIdentEcoli*100, label=NCBI.name, angle=angle), size=1) +
  theme(legend.position="left") + 
  scale_colour_gradient2("Sequence\nIdentity (%)", low="blue", mid="green", high="red",
                         midpoint=(100+min(Fittree15.16_taxa_merged$PctIdentEcoli)*100)/2)

fittree15.16_NCBI_ecoli_ident

Taxonomic Rings

The following section builds on the initial DHFR phylogenetic tree and adds the NCBI taxonomic lineages as an outer ring to display the breadth of sequence diversity in the 1,536-DHFR designed library. However, this tree only contains 643 unique branch tips (41% of full library diversity) from the recovered shared perfects between libraries.

Replace the value in “NCBI.phylum” column with the value from “NCBI.class” if “NCBI.phylum” is “Pseudomonadota”

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

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

Distinct Phylum Colors for Plotting

# Merge distinct phylum colors from Alltree15_taxa_merged dataframe:
Fittree15.16_taxa_merged$NCBI.phylum_colors <- NA
Fittree15.16_taxa_merged$NCBI.class_colors <- NA

# NCBI.phylum_colors
Fittree15.16_taxa_merged$NCBI.phylum_colors[Fittree15.16_taxa_merged$TaxID %in% Alltree15_taxa_merged$TaxID] <- Alltree15_taxa_merged$NCBI.phylum_colors[match(Fittree15.16_taxa_merged$TaxID[Fittree15.16_taxa_merged$TaxID %in% Alltree15_taxa_merged$TaxID], Alltree15_taxa_merged$TaxID)]

# NCBI.class_colors
Fittree15.16_taxa_merged$NCBI.class_colors[Fittree15.16_taxa_merged$TaxID %in% Alltree15_taxa_merged$TaxID] <- Alltree15_taxa_merged$NCBI.class_colors[match(Fittree15.16_taxa_merged$TaxID[Fittree15.16_taxa_merged$TaxID %in% Alltree15_taxa_merged$TaxID], Alltree15_taxa_merged$TaxID)]

# List phylum in alphabetical order for legend and plotting
Fittree15.16_taxa_merged$NCBI.phylum <- factor(Fittree15.16_taxa_merged$NCBI.phylum, levels=NCBIphyloColor$NCBI.phylum)

# List phylum in alphabetical order for legend and plotting
Fittree15.16_taxa_merged$NCBI.class <- factor(Fittree15.16_taxa_merged$NCBI.class, levels=NCBIclassColor$NCBI.class)

Complementation Tree

Lib15: Show the minimum and maximum fitness values between D05 (M9 no supp) vs. D03 (M9 full supp) dataset:

min(Fittree15.16_taxa_merged$fitD05D03,na.rm=T)
[1] -0.9956887
max(Fittree15.16_taxa_merged$fitD05D03,na.rm=T)
[1] 0.7437646

Lib15: Plot a circular phylogenetic tree for complementation with color-scale based on fitness values between D05 vs. D03 for each DHFR homolog

tree_perfects15.16_5BCs_good_0tmp_fitness_labelled <- ggtree(Fittree15.16, layout="circular", branch.length="none") %<+%
  Fittree15.16_taxa_merged +
  aes(color=fitD05D03) +
  geom_tippoint(aes(color=fitD05D03), size=1, alpha=0.8) +
  theme(legend.position="left") + 
  scale_colour_gradientn(
    "fitness",
    colors = c("darkblue", "white", "gold"),
    values = scales::rescale(c(-1, 0, 1)),
    na.value = "gray",
    limits = c(-1, 1)
  )

tree_perfects15.16_5BCs_good_0tmp_fitness_labelled

Lib16: Min and Max fitness for complementation

min(Fittree15.16_taxa_merged$fitD12D04,na.rm=T)
[1] -0.9948904
max(Fittree15.16_taxa_merged$fitD12D04,na.rm=T)
[1] 0.7499431

Lib16: Plot a circular phylogenetic tree for complementation with color-scale based on fitness values between D05 vs. D03 for each DHFR homolog

tree_perfects16.15_5BCs_good_0tmp_fitness_labelled <- ggtree(Fittree15.16, layout="circular", branch.length="none") %<+%
  Fittree15.16_taxa_merged +
  aes(color=fitD12D04) +
  geom_tippoint(aes(color=fitD12D04), size=1, alpha=0.8) +
  #geom_tiplab2(aes(color=fitD12D04, label=NCBI.species, angle=angle), size=1) +
  theme(legend.position="left") + 
  #ggtitle("Library 15 Median Fitness at Complementation") +
  scale_colour_gradient2("fitness",
                         low="darkblue", 
                         mid="white",
                         high="gold",
                         #mid="gold",
                         #high="red",
                         na.value="gray",
                         limit = c(-1,1))

tree_perfects16.15_5BCs_good_0tmp_fitness_labelled

patch12 <- tree_perfects15.16_5BCs_good_0tmp_fitness_labelled | tree_perfects16.15_5BCs_good_0tmp_fitness_labelled
patch12

0.5-TMP (MIC) Tree Plot

Lib15: Show the minimum and maximum fitness values between D07 (0.5 mg/uL Day 1) vs. D03 (M9 no supp.) in the dataset:

min(Fittree15.16_taxa_merged$fitD07D03,na.rm=T)
[1] -7.942436
max(Fittree15.16_taxa_merged$fitD07D03,na.rm=T)
[1] 1.701834

Lib15: Plot a circular phylogenetic tree for resistance with color-scale based on fitness values between D07 vs. D03 for each DHFR homolog

tree_perfects15.16_5BCs_good_0.5tmp_fitness_labelled <- ggtree(Fittree15.16, layout="circular", branch.length="none") %<+%
  Fittree15.16_taxa_merged +
  aes(color=fitD07D03) +
  geom_tippoint(aes(color=fitD07D03), size=1, alpha=0.8) +
  geom_tiplab2(aes(color=fitD07D03, label=NCBI.species, angle=angle), size=1) +
  theme(legend.position="left") + 
  scale_colour_gradient2("fitness",
                         low="black", 
                         mid="gold",
                         high="red",
                         na.value="gray",
                         limit = c(-8.5,2.5)) +
  ggtitle("Codon 1 Library MIC \n(0.5 ug/mL TMP)") +
  theme(plot.title = element_text(hjust = 0.5))

tree_perfects15.16_5BCs_good_0.5tmp_fitness_labelled

Lib16: Show the minimum and maximum fitness values between E02 (0.5 mg/uL Day 1) vs. D04 (M9 no supp.) in the dataset:

min(Fittree15.16_taxa_merged$fitE02D04,na.rm=T)
[1] -10.39234
max(Fittree15.16_taxa_merged$fitE02D04,na.rm=T)
[1] 2.217343

Lib16: Plot a circular phylogenetic tree for resistance with color-scale based on fitness values between D07 vs. D03 for each DHFR homolog

tree_perfects16.15_5BCs_good_0.5tmp_fitness_labelled <- ggtree(Fittree15.16, layout="circular", branch.length="none") %<+%
  Fittree15.16_taxa_merged +
  aes(color=fitE02D04) +
  geom_tippoint(aes(color=fitE02D04), size=1, alpha=0.8) +
  geom_tiplab2(aes(color=fitE02D04, label=NCBI.species, angle=angle), size=1) +
  theme(legend.position="right") + 
  scale_colour_gradient2("fitness",
                         low="black", 
                         mid="gold",
                         high="red",
                         na.value="gray",
                         limit = c(-10.5,2.5)) +
  ggtitle("Codon 2 Library MIC \n(0.5 ug/mL TMP)") +
  theme(plot.title = element_text(hjust = 0.5))

tree_perfects16.15_5BCs_good_0.5tmp_fitness_labelled

patch13 <- tree_perfects15.16_5BCs_good_0.5tmp_fitness_labelled | tree_perfects16.15_5BCs_good_0.5tmp_fitness_labelled
patch13

200-TMP Tree Plot

Lib15: Show the minimum and maximum fitness values between D11 (200 mg/uL Day 1) vs. D03 (M9 no supp.) in the dataset:

min(Fittree15.16_taxa_merged$fitD11D03,na.rm=T)
[1] -10.96071
max(Fittree15.16_taxa_merged$fitD11D03,na.rm=T)
[1] 6.619919

Lib15: Plot a circular phylogenetic tree for resistance with color-scale based on fitness values between D07 vs. D03 for each DHFR homolog

tree_perfects15.16_5BCs_good_200tmp_fitness_labelled <- ggtree(Fittree15.16, layout="circular", branch.length="none") %<+%
  Fittree15.16_taxa_merged +
  aes(color=fitD11D03) +
  geom_tippoint(aes(color=fitD11D03), size=1, alpha=0.8) +
  geom_tiplab2(aes(color=fitD11D03, label=NCBI.species, angle=angle), size=1) +
  theme(legend.position="left") + 
  scale_colour_gradient2("fitness",
                         low="black", 
                         mid="gold",
                         high="red",
                         na.value="gray",
                         limit = c(-11,7))

tree_perfects15.16_5BCs_good_200tmp_fitness_labelled

Lib16: Show the minimum and maximum fitness values between E06 (200 mg/uL Day 1) vs. D04 (M9 no supp.) in the dataset:

min(Fittree15.16_taxa_merged$fitE06D04,na.rm=T)
[1] -7.526125
max(Fittree15.16_taxa_merged$fitE06D04,na.rm=T)
[1] 5.477812

Lib16: Plot a circular phylogenetic tree for resistance with color-scale based on fitness values between E06 vs. D04 for each DHFR homolog

tree_perfects16.15_5BCs_good_200tmp_fitness_labelled <- ggtree(Fittree15.16, layout="circular", branch.length="none") %<+%
  Fittree15.16_taxa_merged +
  aes(color=fitE06D04) +
  geom_tippoint(aes(color=fitE06D04), size=1, alpha=0.8) +
  geom_tiplab2(aes(color=fitE06D04, label=NCBI.species, angle=angle), size=1) +
  theme(legend.position="right") + 
  scale_colour_gradient2("fitness",
                         low="black", 
                         mid="gold",
                         high="red",
                         na.value="gray",
                         limit = c(-8,6))

tree_perfects16.15_5BCs_good_200tmp_fitness_labelled

patch14 <- tree_perfects15.16_5BCs_good_200tmp_fitness_labelled | tree_perfects16.15_5BCs_good_200tmp_fitness_labelled
patch14

Plot MIC and 400x MIC for Lib15 side-by-side:

patch15 <- tree_perfects15.16_5BCs_good_0.5tmp_fitness_labelled | tree_perfects16.15_5BCs_good_200tmp_fitness_labelled
patch15

Save Perfects Files

Save the formatted perfects files to import for downstream analyses

# BCs15_map (62.6 MB)
write.csv(BCs15_map, "Perfects/perfects_files_formatted/BCs15_map.csv", row.names = FALSE)

# BCs16_map (80.5 MB)
write.csv(BCs16_map, "Perfects/perfects_files_formatted/BCs16_map.csv", row.names = FALSE)

###------------------------------------

# mutIDinfo15 (17 MB)
write.csv(mutIDinfo15, "Perfects/perfects_files_formatted/mutIDinfo15.csv", row.names = FALSE)

# mutIDinfo16 (14.7 MB)
write.csv(mutIDinfo16, "Perfects/perfects_files_formatted/mutIDinfo16.csv", row.names = FALSE)

###------------------------------------

# perfects15_5BCs (275 KB)
write.csv(perfects15_5BCs, "Perfects/perfects_files_formatted/perfects15_5BCs.csv", 
          row.names = FALSE)

# perfects16_5BCs (231 KB)
write.csv(perfects16_5BCs, "Perfects/perfects_files_formatted/perfects16_5BCs.csv", 
          row.names = FALSE)

# perfects_15_16_5BCs_tree (319 KB)
write.csv(perfects_15_16_5BCs_tree, "Perfects/perfects_files_formatted/perfects_15_16_5BCs_tree.csv", 
          row.names = FALSE)

###------------------------------------

# orginfo (2.1 MB)
write.csv(orginfo, "Perfects/perfects_files_formatted/orginfo.csv", 
          row.names = FALSE)

###------------------------------------

# Alltree15_taxa_merged (611 KB)
write.csv(Alltree15_taxa_merged, "Perfects/perfects_files_formatted/Alltree15_taxa_merged.csv", 
          row.names = FALSE)

###------------------------------------

# BCcontrols_15_16_shared_median_WT (435 Bytes)
write.csv(BCcontrols_15_16_shared_median_WT, "Perfects/perfects_files_formatted/BCcontrols_15_16_shared_median_WT.csv", 
          row.names = FALSE)

# BCcontrols_15_16_shared_median_Neg (697 Bytes)
write.csv(BCcontrols_15_16_shared_median_Neg, "Perfects/perfects_files_formatted/BCcontrols_15_16_shared_median_Neg.csv", 
          row.names = FALSE)

###------------------------------------

# Fittree15good_taxa_merged (256 KB)
write.csv(Fittree15good_taxa_merged, 
          "Perfects/perfects_files_formatted/Fittree15good_taxa_merged.csv", 
          row.names = FALSE)

###------------------------------------

# ncbi_taxa (353.1 MB)
write.csv(ncbi_taxa, 
          "Perfects/perfects_files_formatted/ncbi_taxa.csv", 
          row.names = FALSE)

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
 abind              1.4-5      2016-07-21 [1] CRAN (R 4.3.0)
 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)
 backports          1.4.1      2021-12-13 [1] CRAN (R 4.3.0)
 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)
 broom              1.0.7      2024-09-26 [1] CRAN (R 4.3.3)
 cachem             1.0.8      2023-05-01 [1] CRAN (R 4.3.0)
 car                3.1-3      2024-09-27 [1] CRAN (R 4.3.3)
 carData            3.0-5      2022-01-06 [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)
 Formula            1.2-5      2023-02-24 [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)
 ggpubr           * 0.6.0      2023-02-10 [1] CRAN (R 4.3.0)
 ggridges         * 0.5.6      2024-01-23 [1] CRAN (R 4.3.1)
 ggsignif           0.6.4      2022-10-13 [1] CRAN (R 4.3.0)
 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
 isoband            0.2.7      2022-12-20 [1] CRAN (R 4.3.0)
 iterators          1.0.14     2022-02-05 [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)
 mgcv               1.9-1      2023-12-21 [1] CRAN (R 4.3.1)
 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)
 rstatix          * 0.7.2      2023-02-01 [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)
 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)
 tidytree         * 0.4.6      2023-12-12 [1] CRAN (R 4.3.1)
 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

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

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
LS0tCnRpdGxlOiAiUGVyZmVjdCBIb21vbG9ncyBBbmFseXNpcyIKYXV0aG9yOiAnQXV0aG9yczogW0thcmwgSi4gUm9tYW5vd2ljel0oaHR0cHM6Ly9rcm9tYW5vd2ljei5naXRodWIuaW8vKSwgQ2FybWVuIFJlc25pY2ssIFNhbXVlbCBSLiBIaW50b24sIENhbGluIFBsZXNhJwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRoZW1lOiBzcGFjZWxhYgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IHllcwogICAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogJzUnCiAgICBkZl9wcmludDogcGFnZWQKICBwZGZfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAnNScKLS0tCgoqKlIgTm90ZWJvb2s6KiogPGZvbnQgY29sb3I9ImdyZWVuIj5Qcm92aWRlcyByZXByb2R1Y2libGUgYW5hbHlzaXMgZm9yICoqUGVyZmVjdCBIb21vbG9ncyoqIGluIHRoZSBmb2xsb3dpbmcgbWFudXNjcmlwdDo8L2ZvbnQ+CgoqKkNpdGF0aW9uOioqIFJvbWFub3dpY3ogS0osIFJlc25pY2sgQywgSGludG9uIFNSLCBQbGVzYSBDLiBFeHBsb3JpbmcgYW50aWJpb3RpYyByZXNpc3RhbmNlIGluIGRpdmVyc2UgaG9tb2xvZ3Mgb2YgdGhlIGRpaHlkcm9mb2xhdGUgcmVkdWN0YXNlIHByb3RlaW4gZmFtaWx5IHRocm91Z2ggYnJvYWQgbXV0YXRpb25hbCBzY2FubmluZy4gKioqYmlvUnhpdioqKiwgMjAyNS4gW10oKQoKKipHaXRIdWIgUmVwb3NpdG9yeToqKiBbaHR0cHM6Ly9naXRodWIuY29tL1BsZXNhTGFiL0RIRlJdKGh0dHBzOi8vZ2l0aHViLmNvbS9QbGVzYUxhYi9ESEZSKQoKKipOQ0JJIEJpb1Byb2plY3Q6KiogW2h0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvYmlvcHJvamVjdC8xMTg5NDc4XShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L2Jpb3Byb2plY3QvMTE4OTQ3OCkKCiMgRXhwZXJpbWVudAoKVGhpcyBwaXBlbGluZSBwcm9jZXNzZXMgYSBsaWJyYXJ5IG9mIDEsNTM2IERIRlIgaG9tb2xvZ3MgYW5kIHRoZWlyIGFzc29jaWF0ZWQgbXV0YW50cywgd2l0aCB0d28tZm9sZCByZWR1bmRhbmN5ICh0d28gY29kb24gdmFyaWFudHMgcGVyIHNlcXVlbmNlKS4gRml0bmVzcyBzY29yZXMgYXJlIGRlcml2ZWQgZnJvbSBhIG11bHRpcGxleGVkIGluLXZpdm8gYXNzYXkgdXNpbmcgYSB0cmltZXRob3ByaW0gY29uY2VudHJhdGlvbiBncmFkaWVudCwgYXNzZXNzaW5nIHRoZSBhYmlsaXR5IG9mIHRoZXNlIGhvbW9sb2dzIGFuZCB0aGVpciBtdXRhbnRzIHRvIGNvbXBsZW1lbnQgZnVuY3Rpb25hbGl0eSBpbiBhbiBFLiBjb2xpIGtub2Nrb3V0IHN0cmFpbiBhbmQgdGhlaXIgdG9sZXJhbmNlIHRvIHRyaW1ldGhvcHJpbSB0cmVhdG1lbnQuIFRoaXMgYW5hbHlzaXMgcHJvdmlkZXMgaW5zaWdodHMgaW50byBob3cgYW50aWJpb3RpYyByZXNpc3RhbmNlIGV2b2x2ZXMgYWNyb3NzIGEgcmFuZ2Ugb2YgZXZvbHV0aW9uYXJ5IHN0YXJ0aW5nIHBvaW50cy4gU2VxdWVuY2UgZGF0YSB3ZXJlIGdlbmVyYXRlZCB1c2luZyB0aGUgSWxsdW1pbmEgTm92YVNlcSBwbGF0Zm9ybSB3aXRoIDEwMCBicCBwYWlyZWQtZW5kIHNlcXVlbmNpbmcgb2YgYW1wbGljb25zLgoKIVtNZXRob2RzIG92ZXJ2aWV3IHRvIGFjaGlldmUgYSBicm9hZC1tdXRhdGlvbmFsIHNjYW4gZm9yIERIRlIgaG9tb2xvZ3MuXShJbWFnZXMvREhGUi5EaWFncmFtLnBuZykKCmBgYHtjc3N9Ci5iYWRDb2RlIHsKYmFja2dyb3VuZC1jb2xvcjogbGlnaHRwaW5rOwpmb250LXdlaWdodDogYm9sZDsKfQoKLmdvb2RDb2RlIHsKYmFja2dyb3VuZC1jb2xvcjogbGlnaHRncmVlbjsKZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KCi5zaGFyZWRDb2RlIHsKYmFja2dyb3VuZC1jb2xvcjogbGlnaHRibHVlOwpmb250LXdlaWdodDogYm9sZDsKfQoKdGFibGUgewogIG1hcmdpbjogYXV0bzsKICBib3JkZXItdG9wOiAxcHggc29saWQgIzY2NjsKICBib3JkZXItYm90dG9tOiAxcHggc29saWQgIzY2NjsKfQp0YWJsZSB0aGVhZCB0aCB7IGJvcmRlci1ib3R0b206IDFweCBzb2xpZCAjZGRkOyB9CnRoLCB0ZCB7IHBhZGRpbmc6IDVweDsgfQp0aGVhZCwgdGZvb3QsIHRyOm50aC1jaGlsZChldmVuKSB7IGJhY2tncm91bmQ6ICNlZWU7IH0KYGBgCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KIyBTZXQgZ2xvYmFsIG9wdGlvbnMgZm9yIG5vdGVib29rCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KHdhcm5pbmcgPSBUUlVFLCBtZXNzYWdlID0gVFJVRSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBjbGFzcy5zb3VyY2UgPSAiYmctc3VjY2VzcyIpCgojIEdldHRpbmcgdGhlIHBhdGggb2YgeW91ciBjdXJyZW50IG9wZW4gZmlsZSBhbmQgc2V0IGFzIHdkCmN1cnJlbnRfcGF0aCA9IHJzdHVkaW9hcGk6OmdldEFjdGl2ZURvY3VtZW50Q29udGV4dCgpJHBhdGggCnNldHdkKGRpcm5hbWUoY3VycmVudF9wYXRoKSkKcHJpbnQoZ2V0d2QoKSkKYGBgCgojIFBhY2thZ2VzClRoZSBmb2xsb3dpbmcgUiBwYWNrYWdlcyBtdXN0IGJlIGluc3RhbGxlZCBwcmlvciB0byBsb2FkaW5nIGludG8gdGhlIFIgc2Vzc2lvbi4gU2VlIHRoZSAqKlJlcHJvZHVjaWJpbGl0eSoqIHRhYiBmb3IgYSBjb21wbGV0ZSBsaXN0IG9mIHBhY2thZ2VzIGFuZCB0aGVpciB2ZXJzaW9ucyB1c2VkIGluIHRoaXMgd29ya2Zsb3cuCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQojIExvYWQgdGhlIGxhdGVzdCB2ZXJzaW9uIG9mIHB5dGhvbiAoMy4xMC4xNCkgZm9yIGRvd25zdHJlYW0gdXNlOgpsaWJyYXJ5KHJldGljdWxhdGUpCnVzZV9weXRob24oIi9Vc2Vycy9rcm9tL21pbmlmb3JnZTMvYmluL3B5dGhvbjMiKQoKIyBNYWtlIGEgdmVjdG9yIG9mIHJlcXVpcmVkIHBhY2thZ2VzCnJlcXVpcmVkLnBhY2thZ2VzIDwtIGMoImFwZSIsICJiaW8zZCIsICJCaW9zdHJpbmdzIiwgImNhc3RvciIsICJjb3dwbG90IiwgImRldnRvb2xzIiwgImRwbHlyIiwgImdnRXh0cmEiLCAiZ2duZXdzY2FsZSIsICJnZ3Bsb3QyIiwgImdncHViciIsICJnZ3JpZGdlcyIsICJnZ3RyZWUiLCAiZ2d0cmVlRXh0cmEiLCAiZ2xtbmV0IiwgImdyaWRFeHRyYSIsImlncmFwaCIsICJrbml0ciIsICJtYXRyaXhTdGF0cyIsICJwYXRjaHdvcmsiLCAicGhlYXRtYXAiLCAicHVycnIiLCAicHNjbCIsICJSQ29sb3JCcmV3ZXIiLCAicmVzaGFwZSIsInJlc2hhcGUyIiwgIlJPQ1IiLCAicnN0YXRpeCIsICJzZXFpbnIiLCAic2NhbGVzIiwgInN0cmluZ3IiLCAic3RyaW5naSIsICJ0aWR5ciIsICJ0aWR5dHJlZSIsICJ2aXJpZGlzIikKCiMgTG9hZCByZXF1aXJlZCBwYWNrYWdlcyB3aXRoIGVycm9yIGhhbmRsaW5nCmxvYWRlZC5wYWNrYWdlcyA8LSBsYXBwbHkocmVxdWlyZWQucGFja2FnZXMsIGZ1bmN0aW9uKHBhY2thZ2UpIHsKICBpZiAoIXJlcXVpcmUocGFja2FnZSwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKSkgewogICAgaW5zdGFsbC5wYWNrYWdlcyhwYWNrYWdlLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQogICAgaWYgKCFyZXF1aXJlKHBhY2thZ2UsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpIHsKICAgICAgbWVzc2FnZSgiUGFja2FnZSAiLCBwYWNrYWdlLCAiIGNvdWxkIG5vdCBiZSBpbnN0YWxsZWQgYW5kIGxvYWRlZC4iKQogICAgICByZXR1cm4oTlVMTCkKICAgIH0KICB9CiAgcmV0dXJuKHBhY2thZ2UpCn0pCgojIFJlbW92ZSBOVUxMIGVudHJpZXMgZnJvbSBsb2FkZWQgcGFja2FnZXMKbG9hZGVkLnBhY2thZ2VzIDwtIGxvYWRlZC5wYWNrYWdlc1shc2FwcGx5KGxvYWRlZC5wYWNrYWdlcywgaXMubnVsbCldCmBgYAoKYGBge3IgY2xhc3Mub3V0cHV0PSJzaGFyZWRDb2RlIiwgZWNobz1GQUxTRX0KIyBQcmludCBsb2FkZWQgcGFja2FnZXMKY2F0KCJMb2FkZWQgcGFja2FnZXM6IiwgcGFzdGUobG9hZGVkLnBhY2thZ2VzLCBjb2xsYXBzZSA9ICIsICIpLCAiXG4iKQpgYGAKCmBgYHtyIGluY2x1ZGU9RkFMU0V9CiMgc2V0LnNlZWQgaXMgdXNlZCB0byBmaXggdGhlIHJhbmRvbSBudW1iZXIgZ2VuZXJhdGlvbiB0byBtYWtlIHRoZSByZXN1bHRzIHJlcGVhdGFibGUKc2V0LnNlZWQoMTIzKQpgYGAKCiMgSW1wb3J0IERhdGEgRmlsZXMKCkltcG9ydCAqKk1BUFBJTkcqKiBmaWxlcyBnZW5lcmF0ZWQgZnJvbSBbREhGUi4xLk1hcHBpbmcuUk1EXShodHRwczovL2dpdGh1Yi5jb20vUGxlc2FMYWIvREhGUikgcmVsZXZhbnQgZm9yIGRvd25zdHJlYW0gYW5hbHlzaXMuCmBgYHtyfQojIyMgQkNjb250cm9scwoKIyBMaWIxNSAoQ29kb24gMSkKQkNjb250cm9sc18xNV8xNl9zaGFyZWRfbWVkaWFuX1dUIDwtIHJlYWQuY3N2KCJDb3VudC9jb3VudF9maWxlc19mb3JtYXR0ZWQvQkNjb250cm9sc18xNV8xNl9zaGFyZWRfbWVkaWFuX1dULmNzdiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKIyBMaWIxNiAoQ29kb24gMikKQkNjb250cm9sc18xNV8xNl9zaGFyZWRfbWVkaWFuX05lZyA8LSByZWFkLmNzdigiQ291bnQvY291bnRfZmlsZXNfZm9ybWF0dGVkL0JDY29udHJvbHNfMTVfMTZfc2hhcmVkX21lZGlhbl9OZWcuY3N2IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgojIyMgbXV0SURpbmZvCgojIG11dElEaW5mby4xNS4xNi56ZXJvcy5zaGFyZWQKbXV0SURpbmZvLjE1LjE2Lnplcm9zLnNoYXJlZCA8LSByZWFkLmNzdigiTWFwcGluZy9tYXBfZmlsZXNfZm9ybWF0dGVkL211dElEaW5mby4xNS4xNi56ZXJvcy5zaGFyZWQuY3N2IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgojIG11dElEaW5mby4xNS4xNi56ZXJvcy51bmlxdWUKbXV0SURpbmZvLjE1LjE2Lnplcm9zLnVuaXF1ZSA8LSByZWFkLmNzdigiTWFwcGluZy9tYXBfZmlsZXNfZm9ybWF0dGVkL211dElEaW5mby4xNS4xNi56ZXJvcy51bmlxdWUuY3N2IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmBgYAoKSW1wb3J0ICoqQ09VTlRTKiogZmlsZXMgZ2VuZXJhdGVkIGZyb20gW0RIRlIuMi5Db3VudHMuUk1EXShodHRwczovL2dpdGh1Yi5jb20vUGxlc2FMYWIvREhGUikgcmVsZXZhbnQgZm9yIGRvd25zdHJlYW0gYW5hbHlzaXMuCmBgYHtyfQojIyMgQkNzX21hcAoKIyBMaWIxNSAoQ29kb24gMSkKQkNzMTVfbWFwIDwtIHJlYWQuY3N2KCJDb3VudC9jb3VudF9maWxlc19mb3JtYXR0ZWQvQkNzMTVfbWFwLmNzdiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKIyBMaWIxNiAoQ29kb24gMikKQkNzMTZfbWFwIDwtIHJlYWQuY3N2KCJDb3VudC9jb3VudF9maWxlc19mb3JtYXR0ZWQvQkNzMTZfbWFwLmNzdiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKIyMjIG11dElEaW5mbyAobWFwcGluZyBmaWxlcyB0aGF0IGhhdmUgYmVlbiB1cGRhdGVkIHdpdGggY291bnQgZGF0YSkKCiMgTGliMTUgKENvZG9uIDEpCm11dElEaW5mbzE1IDwtIHJlYWQuY3N2KCJDb3VudC9jb3VudF9maWxlc19mb3JtYXR0ZWQvbXV0SURpbmZvMTUuY3N2IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgojIExpYjE2IChDb2RvbiAyKQptdXRJRGluZm8xNiA8LSByZWFkLmNzdigiQ291bnQvY291bnRfZmlsZXNfZm9ybWF0dGVkL211dElEaW5mbzE2LmNzdiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpgYGAKCiMgUGVyZmVjdHMgRGF0YSBBbmFseXNpcwoKPGZvbnQgY29sb3I9ImJsdWUiPioqVGhpcyBzZWN0aW9uIGlzIGJhc2VkIG9uIHRoZSBSIGZpbGU6ICJSX3BlcmZlY3RzX29ubHkuUiIuKio8L2ZvbnQ+IEl0IGRlc2NyaWJlcyBob3cgdG8gc2VsZWN0IG9ubHkgZm9yIHBlcmZlY3RzIChwZXJmZWN0IG1hdGNoIHRvIGRlc2lnbmVkIGhvbW9sb2cgc2VxdWVuY2Ugd2hlcmUgbXV0YXRpb25zID0gMCkuCgojIyBTZWxlY3QgUGVyZmVjdHMKCk5leHQsIGJlZ2luIGJ5IHNlbGVjdGluZyBwZXJmZWN0cyBCQ3MgZnJvbSAiQkNzX21hcCIgb2JqZWN0IHdoZXJlIG11dGF0aW9ucyA9IDA6CmBgYHtyfQojIExpYjE1CkJDcGVyZmVjdDE1IDwtIEJDczE1X21hcCAlPiUKICBmaWx0ZXIobXV0YXRpb25zPT0wKSAlPiUKICBkcGx5cjo6cmVuYW1lKElEPUlEYWxpZ24pCgojIExpYjE2CkJDcGVyZmVjdDE2IDwtIEJDczE2X21hcCAlPiUKICBmaWx0ZXIobXV0YXRpb25zPT0wKSAlPiUKICBkcGx5cjo6cmVuYW1lKElEPUlEYWxpZ24pCmBgYAoKUGVyZm9ybSBhIHF1aWNrIGNoZWNrIHRvIHZlcmlmeSBubyBJRHMgYXJlIG1pc3Npbmc6CmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIExpYjE1CiNjaGVjayBob3cgbWFueSBhcmUgbWlzc2luZyBJRApucm93KEJDcGVyZmVjdDE1ICU+JSBmaWx0ZXIoSUQgPT0gIiIpKQoKIyBMaWIxNgojY2hlY2sgaG93IG1hbnkgYXJlIG1pc3NpbmcgSUQKbnJvdyhCQ3BlcmZlY3QxNiAlPiUgZmlsdGVyKElEID09ICIiKSkKYGBgCgojIyBQZXJmZWN0cyBTdGF0cwoKIyMjIFN5bm9ueW1vdXMgQ291bnRzCgpDcmVhdGUgYSBmdW5jdGlvbiB0byBjaGVjayBpZiBlYWNoIHVuaXF1ZSBwZXJmZWN0IHNlcXVlbmNlIHJlcHJlc2VudHMgYSAqKiJ0cnVlIHBlcmZlY3QiKiogYmFzZWQgb24gdGhlIGRlc2lnbmVkIGhvbW9sb2cgc2VxdWVuY2Ugb3IgYSAqKiJzeW5vbnltb3VzIG11dGF0aW9uIioqIHRoYXQgaXMgZnVuY3Rpb25hbGx5IHJlZHVuZGFudC4KYGBge3J9CiNjaGVjayBpZiB0aGlzIGlzIGEgc3lub255bW91cyBtdXRhbnQKY2lnYXJfc3luX211dGFudF9jaGVjayA8LSBmdW5jdGlvbihjaWdhcmluKXsKICBmbGFnX291dCA8LSAiUGVyZmVjdCIKICBpZiAobGVuZ3RoKGdyZXAoIlgiLGNpZ2FyaW4pKSAhPSAwKSB7CiAgICBmbGFnX291dCA8LSAiU3luTXV0YW50IgogIH0KICByZXR1cm4oZmxhZ19vdXQpCn0KYGBgCgpBcHBseSB0aGUgInBlcmZlY3QgY2hlY2sgZnVuY3Rpb24iIHRvIHRoZSAiQkNwZXJmZWN0IiBkYXRhc2V0IGFuZCBhZGQgYSBjb2x1bW4gdG8gaW5kaWNhdGUgZWFjaCBCQ3Mgc3RhdHVzLgpgYGB7cn0KIyBMaWIxNQojY2hlY2sgaWYgdGhpcyBCQyBpcyBhIHN5bm9ueW1vdXMgbXV0YW50CkJDcGVyZmVjdDE1IDwtIEJDcGVyZmVjdDE1ICU+JQogIHJvd3dpc2UoKSAlPiUKICBtdXRhdGUoc3luZmxhZz1jaWdhcl9zeW5fbXV0YW50X2NoZWNrKGNpZ2FyKSkKCiMgTGliMTYKI2NoZWNrIGlmIHRoaXMgQkMgaXMgYSBzeW5vbnltb3VzIG11dGFudApCQ3BlcmZlY3QxNiA8LSBCQ3BlcmZlY3QxNiAlPiUKICByb3d3aXNlKCkgJT4lCiAgbXV0YXRlKHN5bmZsYWc9Y2lnYXJfc3luX211dGFudF9jaGVjayhjaWdhcikpCmBgYAoKQ291bnQgdGhlIG51bWJlciBvZiAiUGVyZmVjdHMiIGZyb20gdGhlIGRhdGFzZXQ6CmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIExpYjE1CiMgTnVtYmVyIG9mIHBlcmZlY3RzCnBlcmZlY3RzLmNvdW50MTUgPC0gbGVuZ3RoKHdoaWNoKEJDcGVyZmVjdDE1JHN5bmZsYWc9PSJQZXJmZWN0IikpCgpmb3JtYXQocGVyZmVjdHMuY291bnQxNSwgYmlnLm1hcmsgPSAiLCIpCgojIExpYjE2CiMgTnVtYmVyIG9mIHBlcmZlY3RzCnBlcmZlY3RzLmNvdW50MTYgPC0gbGVuZ3RoKHdoaWNoKEJDcGVyZmVjdDE2JHN5bmZsYWc9PSJQZXJmZWN0IikpCgpmb3JtYXQocGVyZmVjdHMuY291bnQxNiwgYmlnLm1hcmsgPSAiLCIpCmBgYAoKQ291bnQgdGhlIG51bWJlciBvZiAiU3luTXV0YW50cyIgZnJvbSB0aGUgZGF0YXNldDoKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgTGliMTUKIyBOdW1iZXIgb2Ygc3lub255bW91cyBtdXRhbnRzCnN5bm11dC5jb3VudDE1IDwtIGxlbmd0aCh3aGljaChCQ3BlcmZlY3QxNSRzeW5mbGFnPT0iU3luTXV0YW50IikpCgpmb3JtYXQoc3lubXV0LmNvdW50MTUsIGJpZy5tYXJrID0gIiwiKQoKIyBMaWIxNgojIE51bWJlciBvZiBzeW5vbnltb3VzIG11dGFudHMKc3lubXV0LmNvdW50MTYgPC0gbGVuZ3RoKHdoaWNoKEJDcGVyZmVjdDE2JHN5bmZsYWc9PSJTeW5NdXRhbnQiKSkKCmZvcm1hdChzeW5tdXQuY291bnQxNiwgYmlnLm1hcmsgPSAiLCIpCmBgYAoKIyMjIFBlcmZlY3RzIG11dElECgpDcmVhdGUgYSBgcGVyZmVjdHNgIG9iamVjdCB0aGF0IGZ1cnRoZXIgZmlsdGVycyB0aGUgYG11dElEaW5mb2Agb2JqZWN0IHRvIHJldGFpbiBtdXRJRHMgb25seSBpZiB0aGV5IGhhdmUgMCBtdXRhdGlvbnM6CmBgYHtyfQojIExpYjE1CnBlcmZlY3RzMTUgPC0gbXV0SURpbmZvMTUgJT4lCiAgZmlsdGVyKG11dGF0aW9ucz09MCkgJT4lCiAgZHBseXI6OnJlbmFtZShJRCA9IElEYWxpZ24pCgojIExpYjE2CnBlcmZlY3RzMTYgPC0gbXV0SURpbmZvMTYgJT4lCiAgZmlsdGVyKG11dGF0aW9ucz09MCkgJT4lCiAgZHBseXI6OnJlbmFtZShJRCA9IElEYWxpZ24pCmBgYAoKIyMjIFBlcmZlY3RzIEZpbHRlcmVkIGJ5IEJDcwoKU2VsZWN0IG9ubHkgdGhvc2UgcGVyZmVjdHMgd2l0aCBhIG1pbmltdW0gbnVtYmVyIG9mIGJhcmNvZGU6CmBgYHtyfQojIExpYjE1CgojIFNlbGVjdCBvbmx5IHRob3NlIHdpdGggYSBtaW5pbXVtIG9mIDIgQkNzCnBlcmZlY3RzMTVfMkJDcyA8LSBwZXJmZWN0czE1ICU+JQogIGZpbHRlcihudW1wcnVuZWRCQ3MgPiAxKQoKIyBTZWxlY3Qgb25seSB0aG9zZSB3aXRoIGEgbWluaW11bSBvZiA1IEJDcwpwZXJmZWN0czE1XzVCQ3MgPC0gcGVyZmVjdHMxNSAlPiUKICBmaWx0ZXIobnVtcHJ1bmVkQkNzID4gNCkKCiMgU2VsZWN0IG9ubHkgdGhvc2Ugd2l0aCBhIG1pbmltdW0gb2YgMTAgQkNzCnBlcmZlY3RzMTVfMTBCQ3MgPC0gcGVyZmVjdHMxNSAlPiUKICBmaWx0ZXIobnVtcHJ1bmVkQkNzID4gOSkKCiMgU2VsZWN0IG9ubHkgdGhvc2Ugd2l0aCBhIG1pbmltdW0gb2YgMTAwIEJDcwpwZXJmZWN0czE1XzEwMEJDcyA8LSBwZXJmZWN0czE1ICU+JQogIGZpbHRlcihudW1wcnVuZWRCQ3MgPiA5OSkKCiMgU2VsZWN0IG9ubHkgdGhvc2Ugd2l0aCBhIG1pbmltdW0gb2YgMjAwIEJDcwpwZXJmZWN0czE1XzIwMEJDcyA8LSBwZXJmZWN0czE1ICU+JQogIGZpbHRlcihudW1wcnVuZWRCQ3MgPiAxOTkpCgojIExpYjE2CgojIFNlbGVjdCBvbmx5IHRob3NlIHdpdGggYSBtaW5pbXVtIG9mIDIgQkNzCnBlcmZlY3RzMTZfMkJDcyA8LSBwZXJmZWN0czE2ICU+JQogIGZpbHRlcihudW1wcnVuZWRCQ3MgPiAxKQoKIyMjIFNlbGVjdCBvbmx5IHRob3NlIHdpdGggYSBtaW5pbXVtIG9mIDUgQkNzCnBlcmZlY3RzMTZfNUJDcyA8LSBwZXJmZWN0czE2ICU+JQogIGZpbHRlcihudW1wcnVuZWRCQ3MgPiA0KQoKIyMjIFNlbGVjdCBvbmx5IHRob3NlIHdpdGggYSBtaW5pbXVtIG9mIDEwIEJDcwpwZXJmZWN0czE2XzEwQkNzIDwtIHBlcmZlY3RzMTYgJT4lCiAgZmlsdGVyKG51bXBydW5lZEJDcyA+IDkpCgojIFNlbGVjdCBvbmx5IHRob3NlIHdpdGggYSBtaW5pbXVtIG9mIDEwMCBCQ3MKcGVyZmVjdHMxNl8xMDBCQ3MgPC0gcGVyZmVjdHMxNiAlPiUKICBmaWx0ZXIobnVtcHJ1bmVkQkNzID4gOTkpCgojIFNlbGVjdCBvbmx5IHRob3NlIHdpdGggYSBtaW5pbXVtIG9mIDIwMCBCQ3MKcGVyZmVjdHMxNl8yMDBCQ3MgPC0gcGVyZmVjdHMxNiAlPiUKICBmaWx0ZXIobnVtcHJ1bmVkQkNzID4gMTk5KQpgYGAKCiMjIyMgUGVyZmVjdHMgQ291bnRzIGJ5IEJDcwoKKipDb3VudCB0aGUgbnVtYmVyIG9mIHBlcmZlY3RzIGJlZm9yZSBhbmQgYWZ0ZXIgZmlsdGVyaW5nIGJ5IG1pbmltdW0gQkMgY291bnRzICgyQkNzIGFuZCA1QkNzKToqKgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBMaWIxNQoKIyBDb3VudCB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBwZXJmZWN0cyBwcmlvciB0byBmaWx0ZXJpbmcgYnkgQkNzIGluIExpYjE1CnBlcmZlY3RzMTUuY291bnQgPC0gbGVuZ3RoKHVuaXF1ZShwZXJmZWN0czE1JElEKSkKZm9ybWF0KHBlcmZlY3RzMTUuY291bnQsIGJpZy5tYXJrID0gIiwiKQoKIyBDb3VudCB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBwZXJmZWN0cyBhdCAyQkNzIG1pbmltdW0gcmV0YWluZWQgaW4gTGliMTUKcGVyZmVjdHMxNV8yQkNzLmNvdW50IDwtIGxlbmd0aCh1bmlxdWUocGVyZmVjdHMxNV8yQkNzJElEKSkKZm9ybWF0KHBlcmZlY3RzMTVfMkJDcy5jb3VudCwgYmlnLm1hcmsgPSAiLCIpCgojIENvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIHBlcmZlY3RzIGF0IDVCQ3MgbWluaW11bSByZXRhaW5lZCBpbiBMaWIxNQpwZXJmZWN0czE1XzVCQ3MuY291bnQgPC0gbGVuZ3RoKHVuaXF1ZShwZXJmZWN0czE1XzVCQ3MkSUQpKQpmb3JtYXQocGVyZmVjdHMxNV81QkNzLmNvdW50LCBiaWcubWFyayA9ICIsIikKCiMgQ291bnQgdGhlIG51bWJlciBvZiB1bmlxdWUgcGVyZmVjdHMgYXQgMTBCQ3MgbWluaW11bSByZXRhaW5lZCBpbiBMaWIxNQpwZXJmZWN0czE1XzEwQkNzLmNvdW50IDwtIGxlbmd0aCh1bmlxdWUocGVyZmVjdHMxNV8xMEJDcyRJRCkpCmZvcm1hdChwZXJmZWN0czE1XzEwQkNzLmNvdW50LCBiaWcubWFyayA9ICIsIikKCiMgQ291bnQgdGhlIG51bWJlciBvZiB1bmlxdWUgcGVyZmVjdHMgYXQgMTAwQkNzIG1pbmltdW0gcmV0YWluZWQgaW4gTGliMTUKcGVyZmVjdHMxNV8xMDBCQ3MuY291bnQgPC0gbGVuZ3RoKHVuaXF1ZShwZXJmZWN0czE1XzEwMEJDcyRJRCkpCmZvcm1hdChwZXJmZWN0czE1XzEwMEJDcy5jb3VudCwgYmlnLm1hcmsgPSAiLCIpCgojIENvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIHBlcmZlY3RzIGF0IDIwMEJDcyBtaW5pbXVtIHJldGFpbmVkIGluIExpYjE1CnBlcmZlY3RzMTVfMjAwQkNzLmNvdW50IDwtIGxlbmd0aCh1bmlxdWUocGVyZmVjdHMxNV8yMDBCQ3MkSUQpKQpmb3JtYXQocGVyZmVjdHMxNV8yMDBCQ3MuY291bnQsIGJpZy5tYXJrID0gIiwiKQpgYGAKCmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIExpYjE2CgojIENvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIHBlcmZlY3RzIHByaW9yIHRvIGZpbHRlcmluZyBieSBCQ3MgaW4gTGliMTYKcGVyZmVjdHMxNi5jb3VudCA8LSBsZW5ndGgodW5pcXVlKHBlcmZlY3RzMTYkSUQpKQpmb3JtYXQocGVyZmVjdHMxNi5jb3VudCwgYmlnLm1hcmsgPSAiLCIpCgojIENvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIHBlcmZlY3RzIGF0IDJCQ3MgbWluaW11bSByZXRhaW5lZCBpbiBMaWIxNgpwZXJmZWN0czE2XzJCQ3MuY291bnQgPC0gbGVuZ3RoKHVuaXF1ZShwZXJmZWN0czE2XzJCQ3MkSUQpKQpmb3JtYXQocGVyZmVjdHMxNl8yQkNzLmNvdW50LCBiaWcubWFyayA9ICIsIikKCiMgQ291bnQgdGhlIG51bWJlciBvZiB1bmlxdWUgcGVyZmVjdHMgYXQgNUJDcyBtaW5pbXVtIHJldGFpbmVkIGluIExpYjE2CnBlcmZlY3RzMTZfNUJDcy5jb3VudCA8LSBsZW5ndGgodW5pcXVlKHBlcmZlY3RzMTZfNUJDcyRJRCkpCmZvcm1hdChwZXJmZWN0czE2XzVCQ3MuY291bnQsIGJpZy5tYXJrID0gIiwiKQoKIyBDb3VudCB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBwZXJmZWN0cyBhdCAxMEJDcyBtaW5pbXVtIHJldGFpbmVkIGluIExpYjE2CnBlcmZlY3RzMTZfMTBCQ3MuY291bnQgPC0gbGVuZ3RoKHVuaXF1ZShwZXJmZWN0czE2XzEwQkNzJElEKSkKZm9ybWF0KHBlcmZlY3RzMTZfMTBCQ3MuY291bnQsIGJpZy5tYXJrID0gIiwiKQoKIyBDb3VudCB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBwZXJmZWN0cyBhdCAxMDBCQ3MgbWluaW11bSByZXRhaW5lZCBpbiBMaWIxNQpwZXJmZWN0czE2XzEwMEJDcy5jb3VudCA8LSBsZW5ndGgodW5pcXVlKHBlcmZlY3RzMTZfMTAwQkNzJElEKSkKZm9ybWF0KHBlcmZlY3RzMTZfMTAwQkNzLmNvdW50LCBiaWcubWFyayA9ICIsIikKCiMgQ291bnQgdGhlIG51bWJlciBvZiB1bmlxdWUgcGVyZmVjdHMgYXQgMjAwQkNzIG1pbmltdW0gcmV0YWluZWQgaW4gTGliMTUKcGVyZmVjdHMxNl8yMDBCQ3MuY291bnQgPC0gbGVuZ3RoKHVuaXF1ZShwZXJmZWN0czE2XzIwMEJDcyRJRCkpCmZvcm1hdChwZXJmZWN0czE2XzIwMEJDcy5jb3VudCwgYmlnLm1hcmsgPSAiLCIpCmBgYAoKIyMjIFBlcmZlY3RzIEZpbHRlcmVkIGJ5IEZpdG5lc3MKCkZpbHRlciB0aGUgUGVyZmVjdHMgZGF0YXNldHMgdG8gcmV0YWluIG9ubHkgdGhvc2Ugd2l0aCBnb29kIGZpdG5lc3Mgc2NvcmVzIGZvciBjb21wbGVtZW50YXRpb24gKD49LTEpIGFuZCB0aG9zZSB3aXRoIHBvb3IgZml0bmVzcyBpbiBjb21wbGVtZW50YXRpb24gKDwtMSkuIFRoZSBnb29kIGZpdG5lc3MgUGVyZmVjdHMgd2lsbCBiZSB1c2VkIGZvciBkb3duc3RyZWFtIGNvbXBsZW1lbnRhdGlvbiBhbmQgVE1QIHJlc2lzdGFuY2UgYW5hbHlzZXMsIHdoaWxlIHRoZSBwb29yIGZpdG5lc3MgUGVyZmVjdHMgd2lsbCBiZSB1c2VkIGZvciBnYWluLW9mLWZ1bmN0aW9uIChHT0YpIGFuYWx5c2lzLgoKKipHb29kIFBlcmZlY3RzOioqIFNlbGVjdCBvbmx5IHRob3NlIHBlcmZlY3RzIHdpdGggYSBtaW5pbXVtIGZpdG5lc3MgdmFsdWUgPj0tMSBmb3IgZWFjaCBCQyBsZXZlbDoKYGBge3J9CiMgTGliMTUKCiMgU2VsZWN0IG9ubHkgdGhvc2Ugd2l0aCBhIG1pbmltdW0gb2YgMSBCQwpwZXJmZWN0czE1X2dvb2QgPC0gcGVyZmVjdHMxNSAlPiUKICBmaWx0ZXIoZml0RDA1RDAzID49IC0xKQoKIyBTZWxlY3Qgb25seSB0aG9zZSB3aXRoIGEgbWluaW11bSBvZiAyIEJDcwpwZXJmZWN0czE1XzJCQ3NfZ29vZCA8LSBwZXJmZWN0czE1XzJCQ3MgJT4lCiAgZmlsdGVyKGZpdEQwNUQwMyA+PSAtMSkKCiMgU2VsZWN0IG9ubHkgdGhvc2Ugd2l0aCBhIG1pbmltdW0gb2YgNSBCQ3MKcGVyZmVjdHMxNV81QkNzX2dvb2QgPC0gcGVyZmVjdHMxNV81QkNzICU+JQogIGZpbHRlcihmaXREMDVEMDMgPj0gLTEpCgojIFNlbGVjdCBvbmx5IHRob3NlIHdpdGggYSBtaW5pbXVtIG9mIDEwIEJDcwpwZXJmZWN0czE1XzEwQkNzX2dvb2QgPC0gcGVyZmVjdHMxNV8xMEJDcyAlPiUKICBmaWx0ZXIoZml0RDA1RDAzID49IC0xKQoKIyBTZWxlY3Qgb25seSB0aG9zZSB3aXRoIGEgbWluaW11bSBvZiAxMDAgQkNzCnBlcmZlY3RzMTVfMTAwQkNzX2dvb2QgPC0gcGVyZmVjdHMxNV8xMDBCQ3MgJT4lCiAgZmlsdGVyKGZpdEQwNUQwMyA+PSAtMSkKCiMgU2VsZWN0IG9ubHkgdGhvc2Ugd2l0aCBhIG1pbmltdW0gb2YgMjAwIEJDcwpwZXJmZWN0czE1XzIwMEJDc19nb29kIDwtIHBlcmZlY3RzMTVfMjAwQkNzICU+JQogIGZpbHRlcihmaXREMDVEMDMgPj0gLTEpCgojIExpYjE2CgojIFNlbGVjdCBvbmx5IHRob3NlIHdpdGggYSBtaW5pbXVtIG9mIDEgQkNzCnBlcmZlY3RzMTZfZ29vZCA8LSBwZXJmZWN0czE2ICU+JQogIGZpbHRlcihmaXREMTJEMDQgPj0gLTEpCgojIFNlbGVjdCBvbmx5IHRob3NlIHdpdGggYSBtaW5pbXVtIG9mIDIgQkNzCnBlcmZlY3RzMTZfMkJDc19nb29kIDwtIHBlcmZlY3RzMTZfMkJDcyAlPiUKICBmaWx0ZXIoZml0RDEyRDA0ID49IC0xKQoKIyMjIFNlbGVjdCBvbmx5IHRob3NlIHdpdGggYSBtaW5pbXVtIG9mIDUgQkNzCnBlcmZlY3RzMTZfNUJDc19nb29kIDwtIHBlcmZlY3RzMTZfNUJDcyAlPiUKICBmaWx0ZXIoZml0RDEyRDA0ID49IC0xKQoKIyMjIFNlbGVjdCBvbmx5IHRob3NlIHdpdGggYSBtaW5pbXVtIG9mIDEwIEJDcwpwZXJmZWN0czE2XzEwQkNzX2dvb2QgPC0gcGVyZmVjdHMxNl8xMEJDcyAlPiUKICBmaWx0ZXIoZml0RDEyRDA0ID49IC0xKQoKIyBTZWxlY3Qgb25seSB0aG9zZSB3aXRoIGEgbWluaW11bSBvZiAxMDAgQkNzCnBlcmZlY3RzMTZfMTAwQkNzX2dvb2QgPC0gcGVyZmVjdHMxNl8xMDBCQ3MgJT4lCiAgZmlsdGVyKGZpdEQxMkQwNCA+PSAtMSkKCiMgU2VsZWN0IG9ubHkgdGhvc2Ugd2l0aCBhIG1pbmltdW0gb2YgMjAwIEJDcwpwZXJmZWN0czE2XzIwMEJDc19nb29kIDwtIHBlcmZlY3RzMTZfMjAwQkNzICU+JQogIGZpbHRlcihmaXREMTJEMDQgPj0gLTEpCmBgYAoKIyMjIyBQZXJmZWN0cyBDb3VudCBieSBGaXRuZXNzCgoqKkNvdW50IHRoZSBudW1iZXIgb2YgcGVyZmVjdHMgYmVmb3JlIGFuZCBhZnRlciBmaWx0ZXJpbmcgYnkgbWluaW11bSBmaXRuZXNzIChmaXRuZXNzID49IC0xKToqKgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBMaWIxNQoKIyBDb3VudCB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBwZXJmZWN0cyBwcmlvciB0byBmaWx0ZXJpbmcgYnkgQkNzIGluIExpYjE1CnBlcmZlY3RzMTUuZ29vZC5jb3VudCA8LSBsZW5ndGgodW5pcXVlKHBlcmZlY3RzMTVfZ29vZCRJRCkpCmZvcm1hdChwZXJmZWN0czE1Lmdvb2QuY291bnQsIGJpZy5tYXJrID0gIiwiKQoKIyBEZXRlcm1pbmUgTWluaW11bSBmaXRuZXNzIHBlcmZlY3QgcHJpb3IgdG8gZmlsdGVyaW5nIGJ5IEJDcyBpbiBMaWIxNQpwZXJmZWN0czE1Lmdvb2QubWluIDwtIG1pbihwZXJmZWN0czE1X2dvb2QkZml0RDA1RDAzKQpmb3JtYXQocGVyZmVjdHMxNS5nb29kLm1pbiwgYmlnLm1hcmsgPSAiLCIpCgojIERldGVybWluZSBNYXhpbXVtIGZpdG5lc3MgcGVyZmVjdCBwcmlvciB0byBmaWx0ZXJpbmcgYnkgQkNzIGluIExpYjE1CnBlcmZlY3RzMTUuZ29vZC5tYXggPC0gbWF4KHBlcmZlY3RzMTVfZ29vZCRmaXREMDVEMDMpCmZvcm1hdChwZXJmZWN0czE1Lmdvb2QubWF4LCBiaWcubWFyayA9ICIsIikKCiMgRmlsdGVyZWQgYnkgQkNzCgojIENvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIHBlcmZlY3RzIGF0IDJCQ3MgbWluaW11bSByZXRhaW5lZCBpbiBMaWIxNQpwZXJmZWN0czE1XzJCQ3MuZ29vZC5jb3VudCA8LSBsZW5ndGgodW5pcXVlKHBlcmZlY3RzMTVfMkJDc19nb29kJElEKSkKZm9ybWF0KHBlcmZlY3RzMTVfMkJDcy5nb29kLmNvdW50LCBiaWcubWFyayA9ICIsIikKCiMgQ291bnQgdGhlIG51bWJlciBvZiB1bmlxdWUgcGVyZmVjdHMgYXQgNUJDcyBtaW5pbXVtIHJldGFpbmVkIGluIExpYjE1CnBlcmZlY3RzMTVfNUJDcy5nb29kLmNvdW50IDwtIGxlbmd0aCh1bmlxdWUocGVyZmVjdHMxNV81QkNzX2dvb2QkSUQpKQpmb3JtYXQocGVyZmVjdHMxNV81QkNzLmdvb2QuY291bnQsIGJpZy5tYXJrID0gIiwiKQoKIyBDb3VudCB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBwZXJmZWN0cyBhdCAxMEJDcyBtaW5pbXVtIHJldGFpbmVkIGluIExpYjE1CnBlcmZlY3RzMTVfMTBCQ3MuZ29vZC5jb3VudCA8LSBsZW5ndGgodW5pcXVlKHBlcmZlY3RzMTVfMTBCQ3NfZ29vZCRJRCkpCmZvcm1hdChwZXJmZWN0czE1XzEwQkNzLmdvb2QuY291bnQsIGJpZy5tYXJrID0gIiwiKQoKIyBDb3VudCB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBwZXJmZWN0cyBhdCAxMDBCQ3MgbWluaW11bSByZXRhaW5lZCBpbiBMaWIxNQpwZXJmZWN0czE1XzEwMEJDcy5nb29kLmNvdW50IDwtIGxlbmd0aCh1bmlxdWUocGVyZmVjdHMxNV8xMDBCQ3NfZ29vZCRJRCkpCmZvcm1hdChwZXJmZWN0czE1XzEwMEJDcy5nb29kLmNvdW50LCBiaWcubWFyayA9ICIsIikKCiMgQ291bnQgdGhlIG51bWJlciBvZiB1bmlxdWUgcGVyZmVjdHMgYXQgMjAwQkNzIG1pbmltdW0gcmV0YWluZWQgaW4gTGliMTUKcGVyZmVjdHMxNV8yMDBCQ3MuZ29vZC5jb3VudCA8LSBsZW5ndGgodW5pcXVlKHBlcmZlY3RzMTVfMjAwQkNzX2dvb2QkSUQpKQpmb3JtYXQocGVyZmVjdHMxNV8yMDBCQ3MuZ29vZC5jb3VudCwgYmlnLm1hcmsgPSAiLCIpCmBgYAoKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgTGliMTYKCiMgQ291bnQgdGhlIG51bWJlciBvZiB1bmlxdWUgcGVyZmVjdHMgcHJpb3IgdG8gZmlsdGVyaW5nIGJ5IEJDcyBpbiBMaWIxNgpwZXJmZWN0czE2Lmdvb2QuY291bnQgPC0gbGVuZ3RoKHVuaXF1ZShwZXJmZWN0czE2X2dvb2QkSUQpKQpmb3JtYXQocGVyZmVjdHMxNi5nb29kLmNvdW50LCBiaWcubWFyayA9ICIsIikKCiMgRGV0ZXJtaW5lIE1pbmltdW0gZml0bmVzcyBwZXJmZWN0IHByaW9yIHRvIGZpbHRlcmluZyBieSBCQ3MgaW4gTGliMTYKcGVyZmVjdHMxNi5nb29kLm1pbiA8LSBtaW4ocGVyZmVjdHMxNl9nb29kJGZpdEQxMkQwNCkKZm9ybWF0KHBlcmZlY3RzMTYuZ29vZC5taW4sIGJpZy5tYXJrID0gIiwiKQoKIyBEZXRlcm1pbmUgTWF4aW11bSBmaXRuZXNzIHBlcmZlY3QgcHJpb3IgdG8gZmlsdGVyaW5nIGJ5IEJDcyBpbiBMaWIxNgpwZXJmZWN0czE2Lmdvb2QubWF4IDwtIG1heChwZXJmZWN0czE2X2dvb2QkZml0RDEyRDA0KQpmb3JtYXQocGVyZmVjdHMxNi5nb29kLm1heCwgYmlnLm1hcmsgPSAiLCIpCgojIEZpbHRlcmVkIGJ5IEJDcwoKIyBDb3VudCB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBwZXJmZWN0cyBhdCAyQkNzIG1pbmltdW0gcmV0YWluZWQgaW4gTGliMTYKcGVyZmVjdHMxNl8yQkNzLmdvb2QuY291bnQgPC0gbGVuZ3RoKHVuaXF1ZShwZXJmZWN0czE2XzJCQ3NfZ29vZCRJRCkpCmZvcm1hdChwZXJmZWN0czE2XzJCQ3MuZ29vZC5jb3VudCwgYmlnLm1hcmsgPSAiLCIpCgojIENvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIHBlcmZlY3RzIGF0IDVCQ3MgbWluaW11bSByZXRhaW5lZCBpbiBMaWIxNgpwZXJmZWN0czE2XzVCQ3MuZ29vZC5jb3VudCA8LSBsZW5ndGgodW5pcXVlKHBlcmZlY3RzMTZfNUJDc19nb29kJElEKSkKZm9ybWF0KHBlcmZlY3RzMTZfNUJDcy5nb29kLmNvdW50LCBiaWcubWFyayA9ICIsIikKCiMgQ291bnQgdGhlIG51bWJlciBvZiB1bmlxdWUgcGVyZmVjdHMgYXQgMTBCQ3MgbWluaW11bSByZXRhaW5lZCBpbiBMaWIxNgpwZXJmZWN0czE2XzEwQkNzLmdvb2QuY291bnQgPC0gbGVuZ3RoKHVuaXF1ZShwZXJmZWN0czE2XzEwQkNzX2dvb2QkSUQpKQpmb3JtYXQocGVyZmVjdHMxNl8xMEJDcy5nb29kLmNvdW50LCBiaWcubWFyayA9ICIsIikKCiMgQ291bnQgdGhlIG51bWJlciBvZiB1bmlxdWUgcGVyZmVjdHMgYXQgMTAwQkNzIG1pbmltdW0gcmV0YWluZWQgaW4gTGliMTYKcGVyZmVjdHMxNl8xMDBCQ3MuZ29vZC5jb3VudCA8LSBsZW5ndGgodW5pcXVlKHBlcmZlY3RzMTZfMTAwQkNzX2dvb2QkSUQpKQpmb3JtYXQocGVyZmVjdHMxNl8xMDBCQ3MuZ29vZC5jb3VudCwgYmlnLm1hcmsgPSAiLCIpCgojIENvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIHBlcmZlY3RzIGF0IDIwMEJDcyBtaW5pbXVtIHJldGFpbmVkIGluIExpYjE2CnBlcmZlY3RzMTZfMjAwQkNzLmdvb2QuY291bnQgPC0gbGVuZ3RoKHVuaXF1ZShwZXJmZWN0czE2XzIwMEJDc19nb29kJElEKSkKZm9ybWF0KHBlcmZlY3RzMTZfMjAwQkNzLmdvb2QuY291bnQsIGJpZy5tYXJrID0gIiwiKQpgYGAKCiMjIyBNZXJnZWQgUGVyZmVjdHMKCiMjIyMgTWVyZ2VkIGJ5IEJDcwoKTWVyZ2UgcGVyZmVjdHMgZmlsdGVyZWQgYXQgMUJDIGRlcml2ZWQgZnJvbSBlYWNoIGxpYnJhcnkgaW50byBhIHNpbmdsZSBkYXRhZnJhbWUgYmFzZWQgb24gc2hhcmVkIElEIGJldHdlZW4gdGhlIHR3byBkYXRhc2V0CmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIE1lcmdlIGJ5IHNoYXJlZCAibXV0SUQiICgxQkMpCnBlcmZlY3RzXzE1XzE2X3NoYXJlZCA8LSBtZXJnZShwZXJmZWN0czE1LCBwZXJmZWN0czE2LCBieSA9ICJtdXRJRCIsIGFsbCA9IEZBTFNFKQoKIyBNZXJnZSBieSBzaGFyZWQgIm11dElEIiAoMkJDKQpwZXJmZWN0c18xNV8xNl9zaGFyZWRfMkJDcyA8LSBtZXJnZShwZXJmZWN0czE1XzJCQ3MsIHBlcmZlY3RzMTZfMkJDcywgYnkgPSAibXV0SUQiLCBhbGwgPSBGQUxTRSkKCiMgTWVyZ2UgYnkgc2hhcmVkICJtdXRJRCIgKDVCQykKcGVyZmVjdHNfMTVfMTZfc2hhcmVkXzVCQ3MgPC0gbWVyZ2UocGVyZmVjdHMxNV81QkNzLCBwZXJmZWN0czE2XzVCQ3MsIGJ5ID0gIm11dElEIiwgYWxsID0gRkFMU0UpCgojIE1lcmdlIGJ5IHNoYXJlZCAibXV0SUQiICgxMEJDKQpwZXJmZWN0c18xNV8xNl9zaGFyZWRfMTBCQ3MgPC0gbWVyZ2UocGVyZmVjdHMxNV8xMEJDcywgcGVyZmVjdHMxNl8xMEJDcywgYnkgPSAibXV0SUQiLCBhbGwgPSBGQUxTRSkKCiMgTWVyZ2UgYnkgc2hhcmVkICJtdXRJRCIgKDEwMEJDKQpwZXJmZWN0c18xNV8xNl9zaGFyZWRfMTAwQkNzIDwtIG1lcmdlKHBlcmZlY3RzMTVfMTAwQkNzLCBwZXJmZWN0czE2XzEwMEJDcywgYnkgPSAibXV0SUQiLCBhbGwgPSBGQUxTRSkKCiMgTWVyZ2UgYnkgc2hhcmVkICJtdXRJRCIgKDIwMEJDKQpwZXJmZWN0c18xNV8xNl9zaGFyZWRfMjAwQkNzIDwtIG1lcmdlKHBlcmZlY3RzMTVfMjAwQkNzLCBwZXJmZWN0czE2XzIwMEJDcywgYnkgPSAibXV0SUQiLCBhbGwgPSBGQUxTRSkKYGBgCgoqKkNvdW50IHRoZSBudW1iZXIgb2YgcGVyZmVjdHMgc2hhcmVkIGJldHdlZW4gYm90aCBsaWJyYXJpZXMuKioKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgQ291bnQgdGhlIG51bWJlciBvZiB1bmlxdWUgcGVyZmVjdHMgc2hhcmVkIGJldHdlZW4gbGlicmFyaWVzICgxQkMpCnBlcmZlY3RzXzE1XzE2X3NoYXJlZC5jb3VudCA8LSBsZW5ndGgodW5pcXVlKHBlcmZlY3RzXzE1XzE2X3NoYXJlZCRtdXRJRCkpCmZvcm1hdChwZXJmZWN0c18xNV8xNl9zaGFyZWQuY291bnQsIGJpZy5tYXJrID0gIiwiKQoKIyBDb3VudCB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBwZXJmZWN0cyBzaGFyZWQgYmV0d2VlbiBsaWJyYXJpZXMgKDJCQykKcGVyZmVjdHNfMTVfMTZfc2hhcmVkXzJCQ3MuY291bnQgPC0gbGVuZ3RoKHVuaXF1ZShwZXJmZWN0c18xNV8xNl9zaGFyZWRfMkJDcyRtdXRJRCkpCmZvcm1hdChwZXJmZWN0c18xNV8xNl9zaGFyZWRfMkJDcy5jb3VudCwgYmlnLm1hcmsgPSAiLCIpCgojIENvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIHBlcmZlY3RzIHNoYXJlZCBiZXR3ZWVuIGxpYnJhcmllcyAoNUJDKQpwZXJmZWN0c18xNV8xNl9zaGFyZWRfNUJDcy5jb3VudCA8LSBsZW5ndGgodW5pcXVlKHBlcmZlY3RzXzE1XzE2X3NoYXJlZF81QkNzJG11dElEKSkKZm9ybWF0KHBlcmZlY3RzXzE1XzE2X3NoYXJlZF81QkNzLmNvdW50LCBiaWcubWFyayA9ICIsIikKCiMgQ291bnQgdGhlIG51bWJlciBvZiB1bmlxdWUgcGVyZmVjdHMgc2hhcmVkIGJldHdlZW4gbGlicmFyaWVzICgxMEJDKQpwZXJmZWN0c18xNV8xNl9zaGFyZWRfMTBCQ3MuY291bnQgPC0gbGVuZ3RoKHVuaXF1ZShwZXJmZWN0c18xNV8xNl9zaGFyZWRfMTBCQ3MkbXV0SUQpKQpmb3JtYXQocGVyZmVjdHNfMTVfMTZfc2hhcmVkXzEwQkNzLmNvdW50LCBiaWcubWFyayA9ICIsIikKCiMgQ291bnQgdGhlIG51bWJlciBvZiB1bmlxdWUgcGVyZmVjdHMgc2hhcmVkIGJldHdlZW4gbGlicmFyaWVzICgxMDBCQykKcGVyZmVjdHNfMTVfMTZfc2hhcmVkXzEwMEJDcy5jb3VudCA8LSBsZW5ndGgodW5pcXVlKHBlcmZlY3RzXzE1XzE2X3NoYXJlZF8xMDBCQ3MkbXV0SUQpKQpmb3JtYXQocGVyZmVjdHNfMTVfMTZfc2hhcmVkXzEwMEJDcy5jb3VudCwgYmlnLm1hcmsgPSAiLCIpCgojIENvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIHBlcmZlY3RzIHNoYXJlZCBiZXR3ZWVuIGxpYnJhcmllcyAoMjAwQkMpCnBlcmZlY3RzXzE1XzE2X3NoYXJlZF8yMDBCQ3MuY291bnQgPC0gbGVuZ3RoKHVuaXF1ZShwZXJmZWN0c18xNV8xNl9zaGFyZWRfMjAwQkNzJG11dElEKSkKZm9ybWF0KHBlcmZlY3RzXzE1XzE2X3NoYXJlZF8yMDBCQ3MuY291bnQsIGJpZy5tYXJrID0gIiwiKQpgYGAKCioqQ291bnQgdGhlIG51bWJlciBvZiBwZXJmZWN0cyB1bmlxdWUgdG8gb25lIGxpYnJhcnkgb3IgdGhlIG90aGVyLioqCmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIENyZWF0ZSBhIG5ldyBkYXRhc2V0IHJldGFpbmluZyB1bmlxdWUgdmFsdWVzIGZyb20gYm90aCBkYXRhc2V0cyAoMUJDKQpwZXJmZWN0c18xNV8xNl91bmlxdWUgPC0gYmluZF9yb3dzKAogIGFudGlfam9pbihwZXJmZWN0czE1LCBwZXJmZWN0czE2LCBieSA9ICJtdXRJRCIpLAogIGFudGlfam9pbihwZXJmZWN0czE2LCBwZXJmZWN0czE1LCBieSA9ICJtdXRJRCIpKQoKIyBDb3VudCB0aGUgbnVtYmVyIG9mIHBlcmZlY3RzIHVuaXF1ZSB0byBvbmUgbGlicmFyeSBvciB0aGUgb3RoZXIKcGVyZmVjdHNfMTVfMTZfdW5pcXVlLmNvdW50IDwtIGxlbmd0aCh1bmlxdWUocGVyZmVjdHNfMTVfMTZfdW5pcXVlJG11dElEKSkKZm9ybWF0KHBlcmZlY3RzXzE1XzE2X3VuaXF1ZS5jb3VudCwgYmlnLm1hcmsgPSAiLCIpCgojIENyZWF0ZSBhIG5ldyBkYXRhc2V0IHJldGFpbmluZyB1bmlxdWUgdmFsdWVzIGZyb20gYm90aCBkYXRhc2V0cyAoMkJDKQpwZXJmZWN0c18xNV8xNl91bmlxdWVfMkJDcyA8LSBiaW5kX3Jvd3MoCiAgYW50aV9qb2luKHBlcmZlY3RzMTVfMkJDcywgcGVyZmVjdHMxNl8yQkNzLCBieSA9ICJtdXRJRCIpLAogIGFudGlfam9pbihwZXJmZWN0czE2XzJCQ3MsIHBlcmZlY3RzMTVfMkJDcywgYnkgPSAibXV0SUQiKSkKCiMgQ291bnQgdGhlIG51bWJlciBvZiBwZXJmZWN0cyB1bmlxdWUgdG8gb25lIGxpYnJhcnkgb3IgdGhlIG90aGVyCnBlcmZlY3RzXzE1XzE2X3VuaXF1ZV8yQkNzLmNvdW50IDwtIGxlbmd0aCh1bmlxdWUocGVyZmVjdHNfMTVfMTZfdW5pcXVlXzJCQ3MkbXV0SUQpKQpmb3JtYXQocGVyZmVjdHNfMTVfMTZfdW5pcXVlXzJCQ3MuY291bnQsIGJpZy5tYXJrID0gIiwiKQoKIyBDcmVhdGUgYSBuZXcgZGF0YXNldCByZXRhaW5pbmcgdW5pcXVlIHZhbHVlcyBmcm9tIGJvdGggZGF0YXNldHMgKDVCQykKcGVyZmVjdHNfMTVfMTZfdW5pcXVlXzVCQ3MgPC0gYmluZF9yb3dzKAogIGFudGlfam9pbihwZXJmZWN0czE1XzVCQ3MsIHBlcmZlY3RzMTZfNUJDcywgYnkgPSAibXV0SUQiKSwKICBhbnRpX2pvaW4ocGVyZmVjdHMxNl81QkNzLCBwZXJmZWN0czE1XzVCQ3MsIGJ5ID0gIm11dElEIikpCgojIENvdW50IHRoZSBudW1iZXIgb2YgcGVyZmVjdHMgdW5pcXVlIHRvIG9uZSBsaWJyYXJ5IG9yIHRoZSBvdGhlcgpwZXJmZWN0c18xNV8xNl91bmlxdWVfNUJDcy5jb3VudCA8LSBsZW5ndGgodW5pcXVlKHBlcmZlY3RzXzE1XzE2X3VuaXF1ZV81QkNzJG11dElEKSkKZm9ybWF0KHBlcmZlY3RzXzE1XzE2X3VuaXF1ZV81QkNzLmNvdW50LCBiaWcubWFyayA9ICIsIikKCiMgQ3JlYXRlIGEgbmV3IGRhdGFzZXQgcmV0YWluaW5nIHVuaXF1ZSB2YWx1ZXMgZnJvbSBib3RoIGRhdGFzZXRzICgxMEJDKQpwZXJmZWN0c18xNV8xNl91bmlxdWVfMTBCQ3MgPC0gYmluZF9yb3dzKAogIGFudGlfam9pbihwZXJmZWN0czE1XzEwQkNzLCBwZXJmZWN0czE2XzEwQkNzLCBieSA9ICJtdXRJRCIpLAogIGFudGlfam9pbihwZXJmZWN0czE2XzEwQkNzLCBwZXJmZWN0czE1XzEwQkNzLCBieSA9ICJtdXRJRCIpKQoKIyBDb3VudCB0aGUgbnVtYmVyIG9mIHBlcmZlY3RzIHVuaXF1ZSB0byBvbmUgbGlicmFyeSBvciB0aGUgb3RoZXIKcGVyZmVjdHNfMTVfMTZfdW5pcXVlXzEwQkNzLmNvdW50IDwtIGxlbmd0aCh1bmlxdWUocGVyZmVjdHNfMTVfMTZfdW5pcXVlXzEwQkNzJG11dElEKSkKZm9ybWF0KHBlcmZlY3RzXzE1XzE2X3VuaXF1ZV8xMEJDcy5jb3VudCwgYmlnLm1hcmsgPSAiLCIpCgojIENyZWF0ZSBhIG5ldyBkYXRhc2V0IHJldGFpbmluZyB1bmlxdWUgdmFsdWVzIGZyb20gYm90aCBkYXRhc2V0cyAoMTAwQkMpCnBlcmZlY3RzXzE1XzE2X3VuaXF1ZV8xMDBCQ3MgPC0gYmluZF9yb3dzKAogIGFudGlfam9pbihwZXJmZWN0czE1XzEwMEJDcywgcGVyZmVjdHMxNl8xMDBCQ3MsIGJ5ID0gIm11dElEIiksCiAgYW50aV9qb2luKHBlcmZlY3RzMTZfMTAwQkNzLCBwZXJmZWN0czE1XzEwMEJDcywgYnkgPSAibXV0SUQiKSkKCiMgQ291bnQgdGhlIG51bWJlciBvZiBwZXJmZWN0cyB1bmlxdWUgdG8gb25lIGxpYnJhcnkgb3IgdGhlIG90aGVyCnBlcmZlY3RzXzE1XzE2X3VuaXF1ZV8xMDBCQ3MuY291bnQgPC0gbGVuZ3RoKHVuaXF1ZShwZXJmZWN0c18xNV8xNl91bmlxdWVfMTAwQkNzJG11dElEKSkKZm9ybWF0KHBlcmZlY3RzXzE1XzE2X3VuaXF1ZV8xMDBCQ3MuY291bnQsIGJpZy5tYXJrID0gIiwiKQoKIyBDcmVhdGUgYSBuZXcgZGF0YXNldCByZXRhaW5pbmcgdW5pcXVlIHZhbHVlcyBmcm9tIGJvdGggZGF0YXNldHMgKDIwMEJDKQpwZXJmZWN0c18xNV8xNl91bmlxdWVfMjAwQkNzIDwtIGJpbmRfcm93cygKICBhbnRpX2pvaW4ocGVyZmVjdHMxNV8yMDBCQ3MsIHBlcmZlY3RzMTZfMjAwQkNzLCBieSA9ICJtdXRJRCIpLAogIGFudGlfam9pbihwZXJmZWN0czE2XzIwMEJDcywgcGVyZmVjdHMxNV8yMDBCQ3MsIGJ5ID0gIm11dElEIikpCgojIENvdW50IHRoZSBudW1iZXIgb2YgcGVyZmVjdHMgdW5pcXVlIHRvIG9uZSBsaWJyYXJ5IG9yIHRoZSBvdGhlcgpwZXJmZWN0c18xNV8xNl91bmlxdWVfMjAwQkNzLmNvdW50IDwtIGxlbmd0aCh1bmlxdWUocGVyZmVjdHNfMTVfMTZfdW5pcXVlXzIwMEJDcyRtdXRJRCkpCmZvcm1hdChwZXJmZWN0c18xNV8xNl91bmlxdWVfMjAwQkNzLmNvdW50LCBiaWcubWFyayA9ICIsIikKYGBgCgoqKlN1bW1hcml6ZSB0aGUgbnVtYmVyIG9mIHBlcmZlY3RzIHNoYXJlZCBvciB1bmlxdWUgYmV0d2VlbiBib3RoIGxpYnJhcmllcyBiYXNlZCBvbiBtaW5pbXVtIEJDIGNvdW50LioqCmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIENvdW50IG51bWJlciBvZiBzaGFyZWQgYW5kIHVuaXF1ZSBwZXJmZWN0cyAoMUJDKQpwZXJmZWN0c18xNV8xNl9hbGwuY291bnQgPC0gc3VtKHBlcmZlY3RzXzE1XzE2X3NoYXJlZC5jb3VudCArIHBlcmZlY3RzXzE1XzE2X3VuaXF1ZS5jb3VudCkKZm9ybWF0KHBlcmZlY3RzXzE1XzE2X2FsbC5jb3VudCwgYmlnLm1hcmsgPSAiLCIpCgojIENvdW50IG51bWJlciBvZiBzaGFyZWQgYW5kIHVuaXF1ZSBwZXJmZWN0cyAoMkJDKQpwZXJmZWN0c18xNV8xNl9hbGxfMkJDcy5jb3VudCA8LSBzdW0ocGVyZmVjdHNfMTVfMTZfc2hhcmVkXzJCQ3MuY291bnQgKyBwZXJmZWN0c18xNV8xNl91bmlxdWVfMkJDcy5jb3VudCkKZm9ybWF0KHBlcmZlY3RzXzE1XzE2X2FsbF8yQkNzLmNvdW50LCBiaWcubWFyayA9ICIsIikKCiMgQ291bnQgbnVtYmVyIG9mIHNoYXJlZCBhbmQgdW5pcXVlIHBlcmZlY3RzICg1QkMpCnBlcmZlY3RzXzE1XzE2X2FsbF81QkNzLmNvdW50IDwtIHN1bShwZXJmZWN0c18xNV8xNl9zaGFyZWRfNUJDcy5jb3VudCArIHBlcmZlY3RzXzE1XzE2X3VuaXF1ZV81QkNzLmNvdW50KQpmb3JtYXQocGVyZmVjdHNfMTVfMTZfYWxsXzVCQ3MuY291bnQsIGJpZy5tYXJrID0gIiwiKQoKIyBDb3VudCBudW1iZXIgb2Ygc2hhcmVkIGFuZCB1bmlxdWUgcGVyZmVjdHMgKDEwQkMpCnBlcmZlY3RzXzE1XzE2X2FsbF8xMEJDcy5jb3VudCA8LSBzdW0ocGVyZmVjdHNfMTVfMTZfc2hhcmVkXzEwQkNzLmNvdW50ICsgcGVyZmVjdHNfMTVfMTZfdW5pcXVlXzEwQkNzLmNvdW50KQpmb3JtYXQocGVyZmVjdHNfMTVfMTZfYWxsXzEwQkNzLmNvdW50LCBiaWcubWFyayA9ICIsIikKCiMgQ291bnQgbnVtYmVyIG9mIHNoYXJlZCBhbmQgdW5pcXVlIHBlcmZlY3RzICgxMDBCQykKcGVyZmVjdHNfMTVfMTZfYWxsXzEwMEJDcy5jb3VudCA8LSBzdW0ocGVyZmVjdHNfMTVfMTZfc2hhcmVkXzEwMEJDcy5jb3VudCArIHBlcmZlY3RzXzE1XzE2X3VuaXF1ZV8xMDBCQ3MuY291bnQpCmZvcm1hdChwZXJmZWN0c18xNV8xNl9hbGxfMTAwQkNzLmNvdW50LCBiaWcubWFyayA9ICIsIikKCiMgQ291bnQgbnVtYmVyIG9mIHNoYXJlZCBhbmQgdW5pcXVlIHBlcmZlY3RzICgyMDBCQykKcGVyZmVjdHNfMTVfMTZfYWxsXzIwMEJDcy5jb3VudCA8LSBzdW0ocGVyZmVjdHNfMTVfMTZfc2hhcmVkXzIwMEJDcy5jb3VudCArIHBlcmZlY3RzXzE1XzE2X3VuaXF1ZV8yMDBCQ3MuY291bnQpCmZvcm1hdChwZXJmZWN0c18xNV8xNl9hbGxfMjAwQkNzLmNvdW50LCBiaWcubWFyayA9ICIsIikKYGBgCgojIyMjIE1lcmdlZCBieSBGaXRuZXNzCgpNZXJnZSBwZXJmZWN0cyBmaWx0ZXJlZCBieSBtaW5pbXVtIGZpdG5lc3MgYXQgY29tcGxlbWVudGF0aW9uICg+LTEpIGJhc2VkIG9uIHNoYXJlZCBJRCBiZXR3ZWVuIGJvdGggbGlicmFyaWVzOgpgYGB7cn0KIyBNZXJnZSBieSBzaGFyZWQgIm11dElEIiAoMUJDKQpwZXJmZWN0c18xNV8xNl9zaGFyZWRfZ29vZCA8LSBtZXJnZShwZXJmZWN0czE1X2dvb2QsIHBlcmZlY3RzMTZfZ29vZCwgYnkgPSAibXV0SUQiLCBhbGwgPSBGQUxTRSkKCiMgTWVyZ2UgYnkgc2hhcmVkICJtdXRJRCIgKDJCQykKcGVyZmVjdHNfMTVfMTZfc2hhcmVkXzJCQ3NfZ29vZCA8LSBtZXJnZShwZXJmZWN0czE1XzJCQ3NfZ29vZCwgcGVyZmVjdHMxNl8yQkNzX2dvb2QsIGJ5ID0gIm11dElEIiwgYWxsID0gRkFMU0UpCgojIE1lcmdlIGJ5IHNoYXJlZCAibXV0SUQiICg1QkMpCnBlcmZlY3RzXzE1XzE2X3NoYXJlZF81QkNzX2dvb2QgPC0gbWVyZ2UocGVyZmVjdHMxNV81QkNzX2dvb2QsIHBlcmZlY3RzMTZfNUJDc19nb29kLCBieSA9ICJtdXRJRCIsIGFsbCA9IEZBTFNFKQpgYGAKCioqQ291bnQgdGhlIG51bWJlciBvZiBwZXJmZWN0cyBzaGFyZWQgYmV0d2VlbiBib3RoIGxpYnJhcmllcy4qKgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBDb3VudCB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBwZXJmZWN0cyBzaGFyZWQgYmV0d2VlbiBsaWJyYXJpZXMgKDFCQykKcGVyZmVjdHNfMTVfMTZfc2hhcmVkLmdvb2QuY291bnQgPC0gbGVuZ3RoKHVuaXF1ZShwZXJmZWN0c18xNV8xNl9zaGFyZWRfZ29vZCRtdXRJRCkpCmZvcm1hdChwZXJmZWN0c18xNV8xNl9zaGFyZWQuZ29vZC5jb3VudCwgYmlnLm1hcmsgPSAiLCIpCgojIENvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIHBlcmZlY3RzIHNoYXJlZCBiZXR3ZWVuIGxpYnJhcmllcyAoMkJDKQpwZXJmZWN0c18xNV8xNl9zaGFyZWRfMkJDcy5nb29kLmNvdW50IDwtIGxlbmd0aCh1bmlxdWUocGVyZmVjdHNfMTVfMTZfc2hhcmVkXzJCQ3NfZ29vZCRtdXRJRCkpCmZvcm1hdChwZXJmZWN0c18xNV8xNl9zaGFyZWRfMkJDcy5nb29kLmNvdW50LCBiaWcubWFyayA9ICIsIikKCiMgQ291bnQgdGhlIG51bWJlciBvZiB1bmlxdWUgcGVyZmVjdHMgc2hhcmVkIGJldHdlZW4gbGlicmFyaWVzICg1QkMpCnBlcmZlY3RzXzE1XzE2X3NoYXJlZF81QkNzLmdvb2QuY291bnQgPC0gbGVuZ3RoKHVuaXF1ZShwZXJmZWN0c18xNV8xNl9zaGFyZWRfNUJDc19nb29kJG11dElEKSkKZm9ybWF0KHBlcmZlY3RzXzE1XzE2X3NoYXJlZF81QkNzLmdvb2QuY291bnQsIGJpZy5tYXJrID0gIiwiKQpgYGAKCioqQ291bnQgdGhlIG51bWJlciBvZiBwZXJmZWN0cyB1bmlxdWUgdG8gb25lIGxpYnJhcnkgb3IgdGhlIG90aGVyLioqCmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIENyZWF0ZSBhIG5ldyBkYXRhc2V0IHJldGFpbmluZyB1bmlxdWUgdmFsdWVzIGZyb20gYm90aCBkYXRhc2V0cyAoMUJDKQpwZXJmZWN0c18xNV8xNl91bmlxdWVfZ29vZCA8LSBiaW5kX3Jvd3MoCiAgYW50aV9qb2luKHBlcmZlY3RzMTVfZ29vZCwgcGVyZmVjdHMxNl9nb29kLCBieSA9ICJtdXRJRCIpLAogIGFudGlfam9pbihwZXJmZWN0czE2X2dvb2QsIHBlcmZlY3RzMTVfZ29vZCwgYnkgPSAibXV0SUQiKSkKCiMgQ291bnQgdGhlIG51bWJlciBvZiBwZXJmZWN0cyB1bmlxdWUgdG8gb25lIGxpYnJhcnkgb3IgdGhlIG90aGVyCnBlcmZlY3RzXzE1XzE2X3VuaXF1ZS5nb29kLmNvdW50IDwtIGxlbmd0aCh1bmlxdWUocGVyZmVjdHNfMTVfMTZfdW5pcXVlX2dvb2QkbXV0SUQpKQpmb3JtYXQocGVyZmVjdHNfMTVfMTZfdW5pcXVlLmdvb2QuY291bnQsIGJpZy5tYXJrID0gIiwiKQoKIyBDcmVhdGUgYSBuZXcgZGF0YXNldCByZXRhaW5pbmcgdW5pcXVlIHZhbHVlcyBmcm9tIGJvdGggZGF0YXNldHMgKDJCQykKcGVyZmVjdHNfMTVfMTZfdW5pcXVlXzJCQ3NfZ29vZCA8LSBiaW5kX3Jvd3MoCiAgYW50aV9qb2luKHBlcmZlY3RzMTVfMkJDc19nb29kLCBwZXJmZWN0czE2XzJCQ3NfZ29vZCwgYnkgPSAibXV0SUQiKSwKICBhbnRpX2pvaW4ocGVyZmVjdHMxNl8yQkNzX2dvb2QsIHBlcmZlY3RzMTVfMkJDc19nb29kLCBieSA9ICJtdXRJRCIpKQoKIyBDb3VudCB0aGUgbnVtYmVyIG9mIHBlcmZlY3RzIHVuaXF1ZSB0byBvbmUgbGlicmFyeSBvciB0aGUgb3RoZXIKcGVyZmVjdHNfMTVfMTZfdW5pcXVlXzJCQ3MuZ29vZC5jb3VudCA8LSBsZW5ndGgodW5pcXVlKHBlcmZlY3RzXzE1XzE2X3VuaXF1ZV8yQkNzX2dvb2QkbXV0SUQpKQpmb3JtYXQocGVyZmVjdHNfMTVfMTZfdW5pcXVlXzJCQ3MuZ29vZC5jb3VudCwgYmlnLm1hcmsgPSAiLCIpCgojIENyZWF0ZSBhIG5ldyBkYXRhc2V0IHJldGFpbmluZyB1bmlxdWUgdmFsdWVzIGZyb20gYm90aCBkYXRhc2V0cyAoNUJDKQpwZXJmZWN0c18xNV8xNl91bmlxdWVfNUJDc19nb29kIDwtIGJpbmRfcm93cygKICBhbnRpX2pvaW4ocGVyZmVjdHMxNV81QkNzX2dvb2QsIHBlcmZlY3RzMTZfNUJDc19nb29kLCBieSA9ICJtdXRJRCIpLAogIGFudGlfam9pbihwZXJmZWN0czE2XzVCQ3NfZ29vZCwgcGVyZmVjdHMxNV81QkNzX2dvb2QsIGJ5ID0gIm11dElEIikpCgojIENvdW50IHRoZSBudW1iZXIgb2YgcGVyZmVjdHMgdW5pcXVlIHRvIG9uZSBsaWJyYXJ5IG9yIHRoZSBvdGhlcgpwZXJmZWN0c18xNV8xNl91bmlxdWVfNUJDcy5nb29kLmNvdW50IDwtIGxlbmd0aCh1bmlxdWUocGVyZmVjdHNfMTVfMTZfdW5pcXVlXzVCQ3NfZ29vZCRtdXRJRCkpCmZvcm1hdChwZXJmZWN0c18xNV8xNl91bmlxdWVfNUJDcy5nb29kLmNvdW50LCBiaWcubWFyayA9ICIsIikKYGBgCgoqKlN1bW1hcml6ZSB0aGUgbnVtYmVyIG9mIHBlcmZlY3RzIHNoYXJlZCBvciB1bmlxdWUgYmV0d2VlbiBib3RoIGxpYnJhcmllcyBiYXNlZCBvbiBtaW5pbXVtIEJDIGNvdW50LioqCmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIENvdW50IG51bWJlciBvZiBzaGFyZWQgYW5kIHVuaXF1ZSBwZXJmZWN0cyAoMUJDKQpwZXJmZWN0c18xNV8xNl9hbGwuZ29vZC5jb3VudCA8LSBzdW0ocGVyZmVjdHNfMTVfMTZfc2hhcmVkLmdvb2QuY291bnQgKyBwZXJmZWN0c18xNV8xNl91bmlxdWUuZ29vZC5jb3VudCkKZm9ybWF0KHBlcmZlY3RzXzE1XzE2X2FsbC5nb29kLmNvdW50LCBiaWcubWFyayA9ICIsIikKCiMgQ291bnQgbnVtYmVyIG9mIHNoYXJlZCBhbmQgdW5pcXVlIHBlcmZlY3RzICgyQkMpCnBlcmZlY3RzXzE1XzE2X2FsbF8yQkNzLmdvb2QuY291bnQgPC0gc3VtKHBlcmZlY3RzXzE1XzE2X3NoYXJlZF8yQkNzLmdvb2QuY291bnQgKyBwZXJmZWN0c18xNV8xNl91bmlxdWVfMkJDcy5nb29kLmNvdW50KQpmb3JtYXQocGVyZmVjdHNfMTVfMTZfYWxsXzJCQ3MuZ29vZC5jb3VudCwgYmlnLm1hcmsgPSAiLCIpCgojIENvdW50IG51bWJlciBvZiBzaGFyZWQgYW5kIHVuaXF1ZSBwZXJmZWN0cyAoNUJDKQpwZXJmZWN0c18xNV8xNl9hbGxfNUJDcy5nb29kLmNvdW50IDwtIHN1bShwZXJmZWN0c18xNV8xNl9zaGFyZWRfNUJDcy5nb29kLmNvdW50ICsgcGVyZmVjdHNfMTVfMTZfdW5pcXVlXzVCQ3MuZ29vZC5jb3VudCkKZm9ybWF0KHBlcmZlY3RzXzE1XzE2X2FsbF81QkNzLmdvb2QuY291bnQsIGJpZy5tYXJrID0gIiwiKQpgYGAKCiMjIENvcnJlbGF0aW9ucwoKIyMjIENvZG9uIDEgdnMuIENvZG9uIDIKCiMjIyMgU3Vic2V0IGZvciBQbG90dGluZwoKQmVjYXVzZSBtYW55IG9mIHRoZSBzaGFyZWQgbXV0SURzIGhhdmUgIk5BIiB2YWx1ZXMgZm9yIG9uZSBsaWJyYXJ5IG9yIHRoZSBvdGhlciBhdCBjZXJ0YWluIHRyZWF0bWVudCBjb25kaXRpb25zLCB3ZSBuZWVkIHRvIHN1YnNldCB0aGUgZGF0YWZyYW1lIGludG8gc21hbGxlciBkYXRhc2V0cyByZWxldmFudCBmb3IgZWFjaCBjb25kaXRpb24gd2Ugd2FudCB0byBwbG90IGFuZCByZW1vdmUgcm93cyBjb250YWluaW5nICJOQSIgdmFsdWVzLiAqKkFsbCBjb3JyZWxhdGlvbnMgYXJlIGJhc2VkIG9uIHRoZSAiQkNzLjE1LjE2Lm11dElELmZpdG5lc3MucGVyZmVjdHMuc2hhcmVkIiBkYXRhc2V0LioqCgpTdWJzZXQgcmVsZXZhbnQgZGF0YSBjb2x1bW5zIGFuZCByZW1vdmUgcm93cyBjb250YWluaW5nICJOQSIgdmFsdWVzOgpgYGB7cn0KIyBDb21wbGVtZW50YXRpb24gLSAwLVRNUApMMTUuTDE2LkNvdW50cy4wLlRNUCA8LSBwZXJmZWN0c18xNV8xNl9zaGFyZWRfNUJDc1ssIGMoIm11dElEIiwgIm51bXBydW5lZEJDcy54IiwgIm51bXBydW5lZEJDcy55IiwgImZpdEQwNUQwMyIsICJmaXREMTJEMDQiKV0gJT4lCiAgbmEub21pdChMMTUuTDE2LkNvdW50cy4wLlRNUCkKCiMgVHJpbWV0aG9wcmltIC0gMC4wNTgtVE1QCkwxNS5MMTYuQ291bnRzLjAuMDU4LlRNUCA8LSBwZXJmZWN0c18xNV8xNl9zaGFyZWRfNUJDc19nb29kWywgYygibXV0SUQiLCAibnVtcHJ1bmVkQkNzLngiLCAibnVtcHJ1bmVkQkNzLnkiLCAiZml0RDA2RDAzIiwgImZpdEUwMUQwNCIpXSAlPiUKICBuYS5vbWl0KEwxNS5MMTYuQ291bnRzLjAuMDU4LlRNUCkKCiMgVHJpbWV0aG9wcmltIC0gMC41LVRNUApMMTUuTDE2LkNvdW50cy4wLjUuVE1QIDwtIHBlcmZlY3RzXzE1XzE2X3NoYXJlZF81QkNzX2dvb2RbLCBjKCJtdXRJRCIsICJudW1wcnVuZWRCQ3MueCIsICJudW1wcnVuZWRCQ3MueSIsICJmaXREMDdEMDMiLCAiZml0RTAyRDA0IildICU+JQogIG5hLm9taXQoTDE1LkwxNi5Db3VudHMuMC41LlRNUCkKCiMgVHJpbWV0aG9wcmltIC0gMS4wLVRNUApMMTUuTDE2LkNvdW50cy4xLjAuVE1QIDwtIHBlcmZlY3RzXzE1XzE2X3NoYXJlZF81QkNzX2dvb2RbLCBjKCJtdXRJRCIsICJudW1wcnVuZWRCQ3MueCIsICJudW1wcnVuZWRCQ3MueSIsICJmaXREMDhEMDMiLCAiZml0RTAzRDA0IildICU+JQogIG5hLm9taXQoTDE1LkwxNi5Db3VudHMuMS4wLlRNUCkKCiMgVHJpbWV0aG9wcmltIC0gMTAtVE1QCkwxNS5MMTYuQ291bnRzLjEwLlRNUCA8LSBwZXJmZWN0c18xNV8xNl9zaGFyZWRfNUJDc19nb29kWywgYygibXV0SUQiLCAibnVtcHJ1bmVkQkNzLngiLCAibnVtcHJ1bmVkQkNzLnkiLCAiZml0RDA5RDAzIiwgImZpdEUwNEQwNCIpXSAlPiUKICBuYS5vbWl0KEwxNS5MMTYuQ291bnRzLjEwLlRNUCkKCiMgVHJpbWV0aG9wcmltIC0gNTAtVE1QCkwxNS5MMTYuQ291bnRzLjUwLlRNUCA8LSBwZXJmZWN0c18xNV8xNl9zaGFyZWRfNUJDc19nb29kWywgYygibXV0SUQiLCAibnVtcHJ1bmVkQkNzLngiLCAibnVtcHJ1bmVkQkNzLnkiLCAiZml0RDEwRDAzIiwgImZpdEUwNUQwNCIpXSAlPiUKICBuYS5vbWl0KEwxNS5MMTYuQ291bnRzLjUwLlRNUCkKCiMgVHJpbWV0aG9wcmltIC0gMjAwLVRNUApMMTUuTDE2LkNvdW50cy4yMDAuVE1QIDwtIHBlcmZlY3RzXzE1XzE2X3NoYXJlZF81QkNzX2dvb2RbLCBjKCJtdXRJRCIsICJudW1wcnVuZWRCQ3MueCIsICJudW1wcnVuZWRCQ3MueSIsICJmaXREMTFEMDMiLCAiZml0RTA2RDA0IildJT4lCiAgbmEub21pdChMMTUuTDE2LkNvdW50cy4yMDAuVE1QKQpgYGAKCiMjIyMgUGVhcnNvbiBDb3JyZWxhdGlvbnMKClRoZSBmb2xsb3dpbmcgc2VjdGlvbiBjYWxjdWxhdGVzIFBlYXJzb24ncyBjb3JyZWxhdGlvbnMgYmFzZWQgb24gdGhlIGZpdG5lc3Mgc2NvcmVzIG9mIHVuaXF1ZSBtdXRJRHMgc2hhcmVkIGJldHdlZW4gTGliMTUgYW5kIExpYjE2IGZvciBUaW1lIFBvaW50IDEuCgoqKlBlYXJzb24ncyBDb3JyZWxhdGlvbjoqKiBEZXRlcm1pbmUgY29ycmVsYXRpb25zIHVzaW5nIGZpdG5lc3MgdmFsdWVzIGZyb20gTGlicmFyaWVzIDE1IGFuZCAxNiBhdCBUaW1lIFBvaW50IDE6CmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIFBlYXJzb24ncyBDb3JyZWxhdGlvbjogMC1UTVAgKENvbXBsZW1lbnRhdGlvbikKY29yX3Rlc3RfQ291bnRzX3NoYXJlZF8wX3RtcCA8LSBjb3IudGVzdChMMTUuTDE2LkNvdW50cy4wLlRNUCRmaXREMDVEMDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTDE1LkwxNi5Db3VudHMuMC5UTVAkZml0RDEyRDA0KQoKIyBQZWFyc29uJ3MgQ29ycmVsYXRpb246IDAuMDU4LVRNUApjb3JfdGVzdF9Db3VudHNfc2hhcmVkXzAuMDU4X3RtcCA8LSBjb3IudGVzdChMMTUuTDE2LkNvdW50cy4wLjA1OC5UTVAkZml0RDA2RDAzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMMTUuTDE2LkNvdW50cy4wLjA1OC5UTVAkZml0RTAxRDA0KQoKIyBQZWFyc29uJ3MgQ29ycmVsYXRpb246IDAuNS1UTVAKY29yX3Rlc3RfQ291bnRzX3NoYXJlZF8wLjVfdG1wIDwtIGNvci50ZXN0KEwxNS5MMTYuQ291bnRzLjAuNS5UTVAkZml0RDA3RDAzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTDE1LkwxNi5Db3VudHMuMC41LlRNUCRmaXRFMDJEMDQpCgojIFBlYXJzb24ncyBDb3JyZWxhdGlvbjogMS4wLVRNUApjb3JfdGVzdF9Db3VudHNfc2hhcmVkXzEuMF90bXAgPC0gY29yLnRlc3QoTDE1LkwxNi5Db3VudHMuMS4wLlRNUCRmaXREMDhEMDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMMTUuTDE2LkNvdW50cy4xLjAuVE1QJGZpdEUwM0QwNCkKCiMgUGVhcnNvbidzIENvcnJlbGF0aW9uOiAxMC1UTVAKY29yX3Rlc3RfQ291bnRzX3NoYXJlZF8xMF90bXAgPC0gY29yLnRlc3QoTDE1LkwxNi5Db3VudHMuMTAuVE1QJGZpdEQwOUQwMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTDE1LkwxNi5Db3VudHMuMTAuVE1QJGZpdEUwNEQwNCkKCiMgUGVhcnNvbidzIENvcnJlbGF0aW9uOiA1MC1UTVAKY29yX3Rlc3RfQ291bnRzX3NoYXJlZF81MF90bXAgPC0gY29yLnRlc3QoTDE1LkwxNi5Db3VudHMuNTAuVE1QJGZpdEQxMEQwMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTDE1LkwxNi5Db3VudHMuNTAuVE1QJGZpdEUwNUQwNCkKCiMgUGVhcnNvbidzIENvcnJlbGF0aW9uOiAyMDAtVE1QCmNvcl90ZXN0X0NvdW50c19zaGFyZWRfMjAwX3RtcCA8LSBjb3IudGVzdChMMTUuTDE2LkNvdW50cy4yMDAuVE1QJGZpdEQxMUQwMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEwxNS5MMTYuQ291bnRzLjIwMC5UTVAkZml0RTA2RDA0KQpgYGAKCmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIFByaW50IHRoZSBmdWxsIHN0YXRpc3RpY2FsIG91dHB1dDoKcHJpbnQoY29yX3Rlc3RfQ291bnRzX3NoYXJlZF8wX3RtcCkKcHJpbnQoY29yX3Rlc3RfQ291bnRzX3NoYXJlZF8wLjA1OF90bXApCnByaW50KGNvcl90ZXN0X0NvdW50c19zaGFyZWRfMC41X3RtcCkKcHJpbnQoY29yX3Rlc3RfQ291bnRzX3NoYXJlZF8xLjBfdG1wKQpwcmludChjb3JfdGVzdF9Db3VudHNfc2hhcmVkXzEwX3RtcCkKcHJpbnQoY29yX3Rlc3RfQ291bnRzX3NoYXJlZF81MF90bXApCnByaW50KGNvcl90ZXN0X0NvdW50c19zaGFyZWRfMjAwX3RtcCkKYGBgCgojIyMjIENvcnJlbGF0aW9uIFBsb3RzCgpQbG90IG1lZGlhbiBmaXRuZXNzIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIExpYjE1ICYgTGliMTYgKHBlcmZlY3RzKSBmb3IgQ29tcGxlbWVudGF0aW9uOgpgYGB7cn0KIyBFeHRyYWN0IGNvcnJlbGF0aW9uIHZhbHVlIGZyb20gY29yX3ZhbHVlX3NoYXJlZCBvYmplY3QKY29yX3ZhbHVlX3NoYXJlZF8wX3RtcCA8LSBjb3JfdGVzdF9Db3VudHNfc2hhcmVkXzBfdG1wJGVzdGltYXRlCgojIEZvcm1hdCBwLXZhbHVlIGluIHNjaWVudGlmaWMgbm90YXRpb24KcF92YWx1ZV9zY2llbnRpZmljX3NoYXJlZF8wX3RtcCA8LSBmb3JtYXQoY29yX3Rlc3RfQ291bnRzX3NoYXJlZF8wX3RtcCRwLnZhbHVlLCBzY2llbnRpZmljID0gVFJVRSwgZGlnaXRzID0gNCkKCiMgRXh0cmFjdCBudW1iZXIgb2Ygcm93cwpudW1fcm93cy5jb3VudHMuMC50bXAgPC0gbnJvdyhMMTUuTDE2LkNvdW50cy4wLlRNUCkKCiMgUGxvdCBiYXNlZCBvbiBzaGFyZWQgbXV0SUQKTGliMTVfMTZfMF9UTVAgPC0gZ2dwbG90KEwxNS5MMTYuQ291bnRzLjAuVE1QLCAKICAgICAgICAgICAgICAgICAgIGFlcyh4PWZpdEQwNUQwMywgeT1maXREMTJEMDQpKSArCiAgbGFicyh4ID0gIkNvZG9uIDEgTWVkaWFuIEZpdG5lc3MgKExvZ0ZDKSBcbigwIM68Zy9tTCB0bXApIiwKICAgICAgIHkgPSJDb2RvbiAyIE1lZGlhbiBGaXRuZXNzIChMb2dGQykgXG4oMCDOvGcvbUwgdG1wKSIpICsKICBnZW9tX3Ntb290aChtZXRob2Q9bG0sY29sb3VyPSJibGFjayIpICsKICBnZW9tX2RlbnNpdHkyZChjb2xvdXI9ImJsYWNrIixhbHBoYT0wLjIpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNhc2Vfd2hlbigKICAgIGZpdEQwNUQwMyA+PSAtMSAmIGZpdEQxMkQwNCA+PSAtMSB+ICJsaWdodGJsdWU0IiwKICAgIGZpdEQwNUQwMyA+PSAtMSAmIGZpdEQxMkQwNCA8IC0xIH4gIiMwMDcyQjIiLAogICAgZml0RDEyRDA0ID49IC0xICYgZml0RDA1RDAzIDwgLTEgfiAiI0U2OUYwMCIsCiAgICBUUlVFIH4gImJsYWNrIgogICksCiAgZmlsbCA9IGNhc2Vfd2hlbigKICAgIGZpdEQwNUQwMyA+PSAtMSAmIGZpdEQxMkQwNCA+PSAtMSB+ICJsaWdodGJsdWU0IiwKICAgIGZpdEQwNUQwMyA+PSAtMSAmIGZpdEQxMkQwNCA8IC0xIH4gIiMwMDcyQjIiLAogICAgZml0RDEyRDA0ID49IC0xICYgZml0RDA1RDAzIDwgLTEgfiAiI0U2OUYwMCIsCiAgICBUUlVFIH4gIndoaXRlIgogICksCiAgc2hhcGUgPSBjYXNlX3doZW4oCiAgICBmaXREMDVEMDMgPj0gLTEgJiBmaXREMTJEMDQgPj0gLTEgfiAxNiwKICAgIGZpdEQwNUQwMyA+PSAtMSAmIGZpdEQxMkQwNCA8IC0xIH4gMTYsCiAgICBmaXREMTJEMDQgPj0gLTEgJiBmaXREMDVEMDMgPCAtMSB+IDE2LAogICAgVFJVRSB+IDIxCiAgKSksIAogIGFscGhhID0gMC43NSwgc2l6ZSA9IDIuNSkgKwpzY2FsZV9zaGFwZV9pZGVudGl0eSgpICsKc2NhbGVfY29sb3JfaWRlbnRpdHkoKSArCnNjYWxlX2ZpbGxfaWRlbnRpdHkoKSArCiAgIyBBZGQgYSBuZXcgcG9pbnQgZm9yIFdUIEUuIGNvbGkgbWVkaWFuIGZpdG5lc3MKICBnZW9tX3BvaW50KGRhdGEgPSBCQ2NvbnRyb2xzXzE1XzE2X3NoYXJlZF9tZWRpYW5fV1QsIAogICAgICAgICAgICAgYWVzKHggPSBmaXREMDVEMDMsIHkgPSBmaXREMTJEMDQpLCAKICAgICAgICAgICAgIGZpbGwgPSAicmVkIiwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gNCwgc2hhcGUgPSAyNCkgKwogICMgQWRkIGEgbmV3IHBvaW50IGZvciBOZWcgQ3RybCAoRDI3TiwgbUNoZXJyeSkgbWVkaWFuIGZpdG5lc3MKICBnZW9tX3BvaW50KGRhdGEgPSBCQ2NvbnRyb2xzXzE1XzE2X3NoYXJlZF9tZWRpYW5fTmVnLCAKICAgICAgICAgICAgIGFlcyh4ID0gZml0RDA1RDAzLCB5ID0gZml0RDEyRDA0KSwgCiAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIHNpemUgPSA1LCBzaGFwZSA9IDE4KSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2JsYWNrJywgc2l6ZSA9IDEuMCksCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMS4wKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArICMsCiAgICAgICAgI3BhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiYmxhY2siLCBmaWxsPU5BLCBzaXplPTEuMCkpICsKICBhbm5vdGF0ZSgidGV4dCIsCiAgICAgICAgICAgeCA9IG1heChMMTUuTDE2LkNvdW50cy4wLlRNUCRmaXREMDVEMDMpLAogICAgICAgICAgIHkgPSBtaW4oTDE1LkwxNi5Db3VudHMuMC5UTVAkZml0RDEyRDA0KSwgCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgicC12YWx1ZSA9IiwgcF92YWx1ZV9zY2llbnRpZmljX3NoYXJlZF8wX3RtcCksIGhqdXN0ID0gMSwgdmp1c3QgPSAwKSArCiAgYW5ub3RhdGUoInRleHQiLAogICAgICAgICAgIHggPSBtYXgoTDE1LkwxNi5Db3VudHMuMC5UTVAkZml0RDA1RDAzKSwKICAgICAgICAgICB5ID0gbWluKEwxNS5MMTYuQ291bnRzLjAuVE1QJGZpdEQxMkQwNCksCiAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIkNvcnJlbGF0aW9uID0iLCByb3VuZChjb3JfdmFsdWVfc2hhcmVkXzBfdG1wLCAyKSksIGhqdXN0ID0gMSwgdmp1c3QgPSAtMS41KSArCiAgYW5ub3RhdGUoInRleHQiLAogICAgICAgICAgIHggPSBtaW4oTDE1LkwxNi5Db3VudHMuMC5UTVAkZml0RDA1RDAzKSwKICAgICAgICAgICB5ID0gbWF4KEwxNS5MMTYuQ291bnRzLjAuVE1QJGZpdEQxMkQwNCksCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiU2hhcmVkIEFzc2VtYmxpZXMgPSIsIG51bV9yb3dzLmNvdW50cy4wLnRtcCksIGhqdXN0ID0gMCwgdmp1c3QgPSAxLjUpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKGZsb29yKG1pbihMMTUuTDE2LkNvdW50cy4wLlRNUCRmaXREMDVEMDMpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZWlsaW5nKG1heChMMTUuTDE2LkNvdW50cy4wLlRNUCRmaXREMDVEMDMpKSwgYnkgPSAxKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoZmxvb3IobWluKEwxNS5MMTYuQ291bnRzLjAuVE1QJGZpdEQxMkQwNCkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNlaWxpbmcobWF4KEwxNS5MMTYuQ291bnRzLjAuVE1QJGZpdEQxMkQwNCkpLCBieSA9IDEpKQoKTGliMTVfMTZfMF9UTVBfcDAxIDwtIGdnTWFyZ2luYWwoTGliMTVfMTZfMF9UTVAsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbCA9ICJsaWdodGJsdWU0IiwgYWxwaGEgPSAwLjUpCgpMaWIxNV8xNl8wX1RNUF9wMDEKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iUGVyZmVjdHMvUExPVFMvQ29ycmVsYXRpb24vTGliMTUuMTYuY29ycmVsYXRpb24ucGVyZmVjdHMubWVkaWFuLjVCQy5maXRuZXNzLmNvbXBsZW1lbnRhdGlvbi5wbmciLAogICAgICAgcGxvdD1MaWIxNV8xNl8wX1RNUF9wMDEsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYsIHVuaXRzID0gImluIikKYGBgCgoqKkNvdW50IENvbXBsZW1lbnRpbmcgSG9tb2xvZ3MgVW5pcXVlIHRvIENvZG9uIFZlcnNpb246KiogQ291bnQgdGhlIG51bWJlciBvZiB1bmlxdWUgaG9tb2xvZ3MgY2FwYWJsZSBvZiBjb21wbGVtZW50YXRpb24gaW4gYm90aCBjb2RvbiB2ZXJzaW9ucyBhbmQgaW4gb25lIGNvZG9uIHZlcnNpb24gb3IgdGhlIG90aGVyLgpgYGB7cn0KbGlicmFyeShkcGx5cikKCiMgQ291bnQgdW5pcXVlIG11dElEcyBmb3IgZWFjaCBjb25kaXRpb24KY291bnRzIDwtIEwxNS5MMTYuQ291bnRzLjAuVE1QICU+JQogIHN1bW1hcmlzZSgKICAgIGJvdGhfZ2VfbmVnMSA9IG5fZGlzdGluY3QobXV0SURbZml0RDA1RDAzID49IC0xICYgZml0RDEyRDA0ID49IC0xXSksCiAgICBEMDVEMDNfZ2VfbmVnMV9EMTJEMDRfbHRfbmVnMSA9IG5fZGlzdGluY3QobXV0SURbZml0RDA1RDAzID49IC0xICYgZml0RDEyRDA0IDwgLTFdKSwKICAgIEQxMkQwNF9nZV9uZWcxX0QwNUQwM19sdF9uZWcxID0gbl9kaXN0aW5jdChtdXRJRFtmaXREMTJEMDQgPj0gLTEgJiBmaXREMDVEMDMgPCAtMV0pLAogICAgYm90aF9sdF9uZWcxID0gbl9kaXN0aW5jdChtdXRJRFtmaXREMDVEMDMgPCAtMSAmIGZpdEQxMkQwNCA8IC0xXSkKICApCgojIFByaW50IHRoZSByZXN1bHRzCnByaW50KCJOdW1iZXIgb2YgdW5pcXVlIG11dElEcyB3aGVyZToiKQpwcmludChwYXN0ZSgiMS4gQm90aCBmaXREMDVEMDMgYW5kIGZpdEQxMkQwNCBhcmUgPj0gLTE6IiwgY291bnRzJGJvdGhfZ2VfbmVnMSkpCnByaW50KHBhc3RlKCIyLiBmaXREMDVEMDMgaXMgPj0gLTEgYnV0IGZpdEQxMkQwNCBpcyA8IC0xOiIsIGNvdW50cyREMDVEMDNfZ2VfbmVnMV9EMTJEMDRfbHRfbmVnMSkpCnByaW50KHBhc3RlKCIzLiBmaXREMTJEMDQgaXMgPj0gLTEgYnV0IGZpdEQwNUQwMyBpcyA8IC0xOiIsIGNvdW50cyREMTJEMDRfZ2VfbmVnMV9EMDVEMDNfbHRfbmVnMSkpCnByaW50KHBhc3RlKCI0LiBCb3RoIGZpdEQwNUQwMyBhbmQgZml0RDEyRDA0IGFyZSA8IC0xOiIsIGNvdW50cyRib3RoX2x0X25lZzEpKQoKIyBPcHRpb25hbDogQ2FsY3VsYXRlIHRvdGFsIG51bWJlciBvZiB1bmlxdWUgbXV0SURzCnRvdGFsX3VuaXF1ZV9tdXRJRHMgPC0gbl9kaXN0aW5jdChMMTUuTDE2LkNvdW50cy4wLlRNUCRtdXRJRCkKcHJpbnQocGFzdGUoIlRvdGFsIG51bWJlciBvZiB1bmlxdWUgbXV0SURzOiIsIHRvdGFsX3VuaXF1ZV9tdXRJRHMpKQpgYGAKCgpQbG90IHNoYXJlZCBmaXRuZXNzIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIExpYjE1ICYgTGliMTYgKHBlcmZlY3RzKSBmb3IgMC4wNTggVE1QOgpgYGB7cn0KIyBFeHRyYWN0IGNvcnJlbGF0aW9uIHZhbHVlIGZyb20gY29yX3ZhbHVlX3NoYXJlZCBvYmplY3QKY29yX3ZhbHVlX3NoYXJlZF8wLjA1OF90bXAgPC0gY29yX3Rlc3RfQ291bnRzX3NoYXJlZF8wLjA1OF90bXAkZXN0aW1hdGUKCiMgRm9ybWF0IHAtdmFsdWUgaW4gc2NpZW50aWZpYyBub3RhdGlvbgpwX3ZhbHVlX3NjaWVudGlmaWNfc2hhcmVkXzAuMDU4X3RtcCA8LSBmb3JtYXQoY29yX3Rlc3RfQ291bnRzX3NoYXJlZF8wLjA1OF90bXAkcC52YWx1ZSwgc2NpZW50aWZpYyA9IFRSVUUsIGRpZ2l0cyA9IDQpCgojIEV4dHJhY3QgbnVtYmVyIG9mIHJvd3MKbnVtX3Jvd3MuY291bnRzLjAuMDU4LnRtcCA8LSBucm93KEwxNS5MMTYuQ291bnRzLjAuMDU4LlRNUCkKCiMgUGxvdCBiYXNlZCBvbiBzaGFyZWQgbXV0SUQKTGliMTVfMTZfMC4wNThfVE1QIDwtIGdncGxvdChMMTUuTDE2LkNvdW50cy4wLjA1OC5UTVAsIAogICAgICAgICAgICAgICAgICAgYWVzKHg9Zml0RDA2RDAzLCB5PWZpdEUwMUQwNCkpICsKICBsYWJzKHggPSAiQ29kb24gMSBNZWRpYW4gRml0bmVzcyAoTG9nRkMpIFxuKDAuMDU4IM68Zy9tTCB0bXApIiwgeSA9IkNvZG9uIDIgTWVkaWFuIEZpdG5lc3MgKExvZ0ZDKSBcbigwLjA1OCDOvGcvbUwgdG1wKSIpICsKICBnZW9tX3Ntb290aChtZXRob2Q9bG0sY29sb3VyPSJibGFjayIpICsKICBnZW9tX2RlbnNpdHkyZChjb2xvdXI9ImJsYWNrIixhbHBoYT0wLjIpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNhc2Vfd2hlbigKICAgIGZpdEQwNkQwMyA+PSAtMSAmIGZpdEUwMUQwNCA+PSAtMSB+ICJsaWdodGJsdWU0IiwKICAgIGZpdEQwNkQwMyA+PSAtMSAmIGZpdEUwMUQwNCA8IC0xIH4gIiMwMDcyQjIiLAogICAgZml0RTAxRDA0ID49IC0xICYgZml0RDA2RDAzIDwgLTEgfiAiI0U2OUYwMCIsCiAgICBUUlVFIH4gImJsYWNrIgogICksCiAgZmlsbCA9IGNhc2Vfd2hlbigKICAgIGZpdEQwNkQwMyA+PSAtMSAmIGZpdEUwMUQwNCA+PSAtMSB+ICJsaWdodGJsdWU0IiwKICAgIGZpdEQwNkQwMyA+PSAtMSAmIGZpdEUwMUQwNCA8IC0xIH4gIiMwMDcyQjIiLAogICAgZml0RTAxRDA0ID49IC0xICYgZml0RDA2RDAzIDwgLTEgfiAiI0U2OUYwMCIsCiAgICBUUlVFIH4gIndoaXRlIgogICksCiAgc2hhcGUgPSBjYXNlX3doZW4oCiAgICBmaXREMDZEMDMgPj0gLTEgJiBmaXRFMDFEMDQgPj0gLTEgfiAxNiwKICAgIGZpdEQwNkQwMyA+PSAtMSAmIGZpdEUwMUQwNCA8IC0xIH4gMTYsCiAgICBmaXRFMDFEMDQgPj0gLTEgJiBmaXREMDZEMDMgPCAtMSB+IDE2LAogICAgVFJVRSB+IDIxCiAgKSksIAogIGFscGhhID0gMC43NSwgc2l6ZSA9IDQpICsKc2NhbGVfc2hhcGVfaWRlbnRpdHkoKSArCnNjYWxlX2NvbG9yX2lkZW50aXR5KCkgKwpzY2FsZV9maWxsX2lkZW50aXR5KCkgKwogICMgQWRkIGEgbmV3IHBvaW50IGZvciBXVCBFLiBjb2xpIG1lZGlhbiBmaXRuZXNzCiAgZ2VvbV9wb2ludChkYXRhID0gQkNjb250cm9sc18xNV8xNl9zaGFyZWRfbWVkaWFuX1dULCAKICAgICAgICAgICAgIGFlcyh4ID0gZml0RDA2RDAzLCB5ID0gZml0RTAxRDA0KSwgCiAgICAgICAgICAgICBmaWxsID0gInJlZCIsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDMsIHNoYXBlID0gMjQpICsKICAjIEFkZCBhIG5ldyBwb2ludCBmb3IgTmVnIEN0cmwgKEQyN04sIG1DaGVycnkpIG1lZGlhbiBmaXRuZXNzCiAgZ2VvbV9wb2ludChkYXRhID0gQkNjb250cm9sc18xNV8xNl9zaGFyZWRfbWVkaWFuX05lZywgCiAgICAgICAgICAgICBhZXMoeCA9IGZpdEQwNkQwMywgeSA9IGZpdEUwMUQwNCksIAogICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBzaXplID0gNSwgc2hhcGUgPSAxOCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICdibGFjaycsIHNpemUgPSAxLjApLAogICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDEuMCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAjLAogICAgICAgICNwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgZmlsbD1OQSwgc2l6ZT0xLjApKSArCiAgYW5ub3RhdGUoInRleHQiLAogICAgICAgICAgIHggPSBtYXgoTDE1LkwxNi5Db3VudHMuMC4wNTguVE1QJGZpdEQwNkQwMyksCiAgICAgICAgICAgeSA9IG1pbihMMTUuTDE2LkNvdW50cy4wLjA1OC5UTVAkZml0RTAxRDA0KSwgCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgicC12YWx1ZSA9IiwgcF92YWx1ZV9zY2llbnRpZmljX3NoYXJlZF8wLjA1OF90bXApLCBoanVzdCA9IDEsIHZqdXN0ID0gMCkgKwogIGFubm90YXRlKCJ0ZXh0IiwKICAgICAgICAgICB4ID0gbWF4KEwxNS5MMTYuQ291bnRzLjAuMDU4LlRNUCRmaXREMDZEMDMpLAogICAgICAgICAgIHkgPSBtaW4oTDE1LkwxNi5Db3VudHMuMC4wNTguVE1QJGZpdEUwMUQwNCksCiAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIkNvcnJlbGF0aW9uID0iLCByb3VuZChjb3JfdmFsdWVfc2hhcmVkXzAuMDU4X3RtcCwgMikpLCBoanVzdCA9IDEsIHZqdXN0ID0gLTEuNSkgKwogIGFubm90YXRlKCJ0ZXh0IiwKICAgICAgICAgICB4ID0gbWluKEwxNS5MMTYuQ291bnRzLjAuMDU4LlRNUCRmaXREMDZEMDMpLAogICAgICAgICAgIHkgPSBtYXgoTDE1LkwxNi5Db3VudHMuMC4wNTguVE1QJGZpdEUwMUQwNCksCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiU2hhcmVkIEFzc2VtYmxpZXMgPSIsIG51bV9yb3dzLmNvdW50cy4wLjA1OC50bXApLCBoanVzdCA9IDAsIHZqdXN0ID0gMS41KSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShmbG9vcihtaW4oTDE1LkwxNi5Db3VudHMuMC4wNTguVE1QJGZpdEQwNkQwMykpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNlaWxpbmcobWF4KEwxNS5MMTYuQ291bnRzLjAuMDU4LlRNUCRmaXREMDZEMDMpKSwgYnkgPSAxKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoZmxvb3IobWluKEwxNS5MMTYuQ291bnRzLjAuMDU4LlRNUCRmaXRFMDFEMDQpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZWlsaW5nKG1heChMMTUuTDE2LkNvdW50cy4wLjA1OC5UTVAkZml0RTAxRDA0KSksIGJ5ID0gMSkpCgpMaWIxNV8xNl8wLjA1OF9UTVBfcDAxIDwtIGdnTWFyZ2luYWwoTGliMTVfMTZfMC4wNThfVE1QLCB0eXBlID0gImhpc3RvZ3JhbSIsIGZpbGwgPSAibGlnaHRibHVlNCIsIGFscGhhID0gMC41KQoKTGliMTVfMTZfMC4wNThfVE1QX3AwMQpgYGAKCmBgYHtyIGVjaG89RkFMU0V9Cmdnc2F2ZShmaWxlPSJQZXJmZWN0cy9QTE9UUy9Db3JyZWxhdGlvbi9MaWIxNS4xNi5jb3JyZWxhdGlvbi5wZXJmZWN0cy5tZWRpYW4uNUJDLmdvb2QuZml0bmVzcy4wLjA1OC50bXAucG5nIiwKICAgICAgIHBsb3Q9TGliMTVfMTZfMC4wNThfVE1QX3AwMSwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iKQpgYGAKClBsb3Qgc2hhcmVkIGZpdG5lc3MgY29ycmVsYXRpb25zIGJldHdlZW4gTGliMTUgJiBMaWIxNiAocGVyZmVjdHMpIGZvciAwLjUgVE1QOgpgYGB7cn0KIyBFeHRyYWN0IGNvcnJlbGF0aW9uIHZhbHVlIGZyb20gY29yX3ZhbHVlX3NoYXJlZCBvYmplY3QKY29yX3ZhbHVlX3NoYXJlZF8wLjVfdG1wIDwtIGNvcl90ZXN0X0NvdW50c19zaGFyZWRfMC41X3RtcCRlc3RpbWF0ZQoKIyBGb3JtYXQgcC12YWx1ZSBpbiBzY2llbnRpZmljIG5vdGF0aW9uCnBfdmFsdWVfc2NpZW50aWZpY19zaGFyZWRfMC41X3RtcCA8LSBmb3JtYXQoY29yX3Rlc3RfQ291bnRzX3NoYXJlZF8wLjVfdG1wJHAudmFsdWUsIHNjaWVudGlmaWMgPSBUUlVFLCBkaWdpdHMgPSA0KQoKIyBFeHRyYWN0IG51bWJlciBvZiByb3dzCm51bV9yb3dzLmNvdW50cy4wLjUudG1wIDwtIG5yb3coTDE1LkwxNi5Db3VudHMuMC41LlRNUCkKCiMgUGxvdCBiYXNlZCBvbiBzaGFyZWQgbXV0SUQKTGliMTVfMTZfMC41X1RNUCA8LSBnZ3Bsb3QoTDE1LkwxNi5Db3VudHMuMC41LlRNUCwgCiAgICAgICAgICAgICAgICAgICBhZXMoeD1maXREMDdEMDMsIHk9Zml0RTAyRDA0KSkgKwogIGxhYnMoeCA9ICJDb2RvbiAxIE1lZGlhbiBGaXRuZXNzIChMb2dGQykgXG5NSUMgKDAuNSDOvGcvbUwgdG1wKSIsIHkgPSJDb2RvbiAyIE1lZGlhbiBGaXRuZXNzIChMb2dGQykgXG5NSUMgKDAuNSDOvGcvbUwgdG1wKSIpICsKICBnZW9tX3Ntb290aChtZXRob2Q9bG0sY29sb3VyPSJibGFjayIpICsKICBnZW9tX2RlbnNpdHkyZChjb2xvdXI9ImJsYWNrIixhbHBoYT0wLjIpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNhc2Vfd2hlbigKICAgIGZpdEQwN0QwMyA+PSAtMSAmIGZpdEUwMkQwNCA+PSAtMSB+ICJsaWdodGJsdWU0IiwKICAgIGZpdEQwN0QwMyA+PSAtMSAmIGZpdEUwMkQwNCA8IC0xIH4gIiMwMDcyQjIiLAogICAgZml0RTAyRDA0ID49IC0xICYgZml0RDA3RDAzIDwgLTEgfiAiI0U2OUYwMCIsCiAgICBUUlVFIH4gImJsYWNrIgogICksCiAgZmlsbCA9IGNhc2Vfd2hlbigKICAgIGZpdEQwN0QwMyA+PSAtMSAmIGZpdEUwMkQwNCA+PSAtMSB+ICJsaWdodGJsdWU0IiwKICAgIGZpdEQwN0QwMyA+PSAtMSAmIGZpdEUwMkQwNCA8IC0xIH4gIiMwMDcyQjIiLAogICAgZml0RTAyRDA0ID49IC0xICYgZml0RDA3RDAzIDwgLTEgfiAiI0U2OUYwMCIsCiAgICBUUlVFIH4gIndoaXRlIgogICksCiAgc2hhcGUgPSBjYXNlX3doZW4oCiAgICBmaXREMDdEMDMgPj0gLTEgJiBmaXRFMDJEMDQgPj0gLTEgfiAxNiwKICAgIGZpdEQwN0QwMyA+PSAtMSAmIGZpdEUwMkQwNCA8IC0xIH4gMTYsCiAgICBmaXRFMDJEMDQgPj0gLTEgJiBmaXREMDdEMDMgPCAtMSB+IDE2LAogICAgVFJVRSB+IDIxCiAgKSksIAogIGFscGhhID0gMC43NSwgc2l6ZSA9IDQpICsKc2NhbGVfc2hhcGVfaWRlbnRpdHkoKSArCnNjYWxlX2NvbG9yX2lkZW50aXR5KCkgKwpzY2FsZV9maWxsX2lkZW50aXR5KCkgKwogICMgQWRkIGEgbmV3IHBvaW50IGZvciBXVCBFLiBjb2xpIG1lZGlhbiBmaXRuZXNzCiAgZ2VvbV9wb2ludChkYXRhID0gQkNjb250cm9sc18xNV8xNl9zaGFyZWRfbWVkaWFuX1dULCAKICAgICAgICAgICAgIGFlcyh4ID0gZml0RDA3RDAzLCB5ID0gZml0RTAyRDA0KSwgCiAgICAgICAgICAgICBmaWxsID0gInJlZCIsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDQsIHNoYXBlID0gMjQpICsKICAjIEFkZCBhIG5ldyBwb2ludCBmb3IgTmVnIEN0cmwgKEQyN04sIG1DaGVycnkpIG1lZGlhbiBmaXRuZXNzCiAgZ2VvbV9wb2ludChkYXRhID0gQkNjb250cm9sc18xNV8xNl9zaGFyZWRfbWVkaWFuX05lZywgCiAgICAgICAgICAgICBhZXMoeCA9IGZpdEQwN0QwMywgeSA9IGZpdEUwMkQwNCksIAogICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBzaXplID0gNSwgc2hhcGUgPSAxOCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICdibGFjaycsIHNpemUgPSAxLjApLAogICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDEuMCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAjLAogICAgICAgICNwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgZmlsbD1OQSwgc2l6ZT0xLjApKSArCiAgYW5ub3RhdGUoInRleHQiLAogICAgICAgICAgIHggPSBtYXgoTDE1LkwxNi5Db3VudHMuMC41LlRNUCRmaXREMDdEMDMpLAogICAgICAgICAgIHkgPSBtaW4oTDE1LkwxNi5Db3VudHMuMC41LlRNUCRmaXRFMDJEMDQpLCAKICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJwLXZhbHVlID0iLCBwX3ZhbHVlX3NjaWVudGlmaWNfc2hhcmVkXzAuNV90bXApLCBoanVzdCA9IDEsIHZqdXN0ID0gMCwgc2l6ZSA9IDUpICsKICBhbm5vdGF0ZSgidGV4dCIsCiAgICAgICAgICAgeCA9IG1heChMMTUuTDE2LkNvdW50cy4wLjUuVE1QJGZpdEQwN0QwMyksCiAgICAgICAgICAgeSA9IG1pbihMMTUuTDE2LkNvdW50cy4wLjUuVE1QJGZpdEUwMkQwNCksCiAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIkNvcnJlbGF0aW9uID0iLCByb3VuZChjb3JfdmFsdWVfc2hhcmVkXzAuNV90bXAsIDIpKSwgaGp1c3QgPSAxLCB2anVzdCA9IC0xLjUsIHNpemUgPSA1KSArCiAgYW5ub3RhdGUoInRleHQiLAogICAgICAgICAgIHggPSBtaW4oTDE1LkwxNi5Db3VudHMuMC41LlRNUCRmaXREMDdEMDMpLAogICAgICAgICAgIHkgPSBtYXgoTDE1LkwxNi5Db3VudHMuMC41LlRNUCRmaXRFMDJEMDQpLAogICAgICAgICAgIGxhYmVsID0gcGFzdGUoIlNoYXJlZCBBc3NlbWJsaWVzID0iLCBudW1fcm93cy5jb3VudHMuMC41LnRtcCksIGhqdXN0ID0gMCwgdmp1c3QgPSAxLjUsIHNpemUgPSA1KSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShmbG9vcihtaW4oTDE1LkwxNi5Db3VudHMuMC41LlRNUCRmaXREMDdEMDMpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZWlsaW5nKG1heChMMTUuTDE2LkNvdW50cy4wLjUuVE1QJGZpdEQwN0QwMykpLCBieSA9IDEpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcShmbG9vcihtaW4oTDE1LkwxNi5Db3VudHMuMC41LlRNUCRmaXRFMDJEMDQpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZWlsaW5nKG1heChMMTUuTDE2LkNvdW50cy4wLjUuVE1QJGZpdEUwMkQwNCkpLCBieSA9IDEpKQoKTGliMTVfMTZfMC41X1RNUF9wMDEgPC0gZ2dNYXJnaW5hbChMaWIxNV8xNl8wLjVfVE1QLCB0eXBlID0gImhpc3RvZ3JhbSIsIGZpbGwgPSAibGlnaHRibHVlNCIsIGFscGhhID0gMC41KQoKTGliMTVfMTZfMC41X1RNUF9wMDEKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iUGVyZmVjdHMvUExPVFMvQ29ycmVsYXRpb24vTGliMTUuMTYuY29ycmVsYXRpb24ucGVyZmVjdHMubWVkaWFuLjVCQy5nb29kLmZpdG5lc3MuMC41LnRtcC5wbmciLAogICAgICAgcGxvdD1MaWIxNV8xNl8wLjVfVE1QX3AwMSwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iKQpgYGAKClBsb3Qgc2hhcmVkIGZpdG5lc3MgY29ycmVsYXRpb25zIGJldHdlZW4gTGliMTUgJiBMaWIxNiAocGVyZmVjdHMpIGZvciAxLjAgVE1QOgpgYGB7cn0KIyBFeHRyYWN0IGNvcnJlbGF0aW9uIHZhbHVlIGZyb20gY29yX3ZhbHVlX3NoYXJlZCBvYmplY3QKY29yX3ZhbHVlX3NoYXJlZF8xLjBfdG1wIDwtIGNvcl90ZXN0X0NvdW50c19zaGFyZWRfMS4wX3RtcCRlc3RpbWF0ZQoKIyBGb3JtYXQgcC12YWx1ZSBpbiBzY2llbnRpZmljIG5vdGF0aW9uCnBfdmFsdWVfc2NpZW50aWZpY19zaGFyZWRfMS4wX3RtcCA8LSBmb3JtYXQoY29yX3Rlc3RfQ291bnRzX3NoYXJlZF8xLjBfdG1wJHAudmFsdWUsIHNjaWVudGlmaWMgPSBUUlVFLCBkaWdpdHMgPSA0KQoKIyBFeHRyYWN0IG51bWJlciBvZiByb3dzCm51bV9yb3dzLmNvdW50cy4xLjAudG1wIDwtIG5yb3coTDE1LkwxNi5Db3VudHMuMS4wLlRNUCkKCiMgUGxvdCBiYXNlZCBvbiBzaGFyZWQgbXV0SUQKTGliMTVfMTZfMS4wX1RNUCA8LSBnZ3Bsb3QoTDE1LkwxNi5Db3VudHMuMS4wLlRNUCwgCiAgICAgICAgICAgICAgICAgICBhZXMoeD1maXREMDhEMDMsIHk9Zml0RTAzRDA0KSkgKwogIGxhYnMoeCA9ICJDb2RvbiAxIE1lZGlhbiBGaXRuZXNzIChMb2dGQykgXG4oMS4wIM68Zy9tTCB0bXApIiwgeSA9IkNvZG9uIDIgTWVkaWFuIEZpdG5lc3MgKExvZ0ZDKSBcbigxLjAgzrxnL21MIHRtcCkiKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtLGNvbG91cj0iYmxhY2siKSArCiAgZ2VvbV9kZW5zaXR5MmQoY29sb3VyPSJibGFjayIsYWxwaGE9MC4yKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBjYXNlX3doZW4oCiAgICBmaXREMDhEMDMgPj0gLTEgJiBmaXRFMDNEMDQgPj0gLTEgfiAibGlnaHRibHVlNCIsCiAgICBmaXREMDhEMDMgPj0gLTEgJiBmaXRFMDNEMDQgPCAtMSB+ICIjMDA3MkIyIiwKICAgIGZpdEUwM0QwNCA+PSAtMSAmIGZpdEQwOEQwMyA8IC0xIH4gIiNFNjlGMDAiLAogICAgVFJVRSB+ICJibGFjayIKICApLAogIGZpbGwgPSBjYXNlX3doZW4oCiAgICBmaXREMDhEMDMgPj0gLTEgJiBmaXRFMDNEMDQgPj0gLTEgfiAibGlnaHRibHVlNCIsCiAgICBmaXREMDhEMDMgPj0gLTEgJiBmaXRFMDNEMDQgPCAtMSB+ICIjMDA3MkIyIiwKICAgIGZpdEUwM0QwNCA+PSAtMSAmIGZpdEQwOEQwMyA8IC0xIH4gIiNFNjlGMDAiLAogICAgVFJVRSB+ICJ3aGl0ZSIKICApLAogIHNoYXBlID0gY2FzZV93aGVuKAogICAgZml0RDA4RDAzID49IC0xICYgZml0RTAzRDA0ID49IC0xIH4gMTYsCiAgICBmaXREMDhEMDMgPj0gLTEgJiBmaXRFMDNEMDQgPCAtMSB+IDE2LAogICAgZml0RTAzRDA0ID49IC0xICYgZml0RDA4RDAzIDwgLTEgfiAxNiwKICAgIFRSVUUgfiAyMQogICkpLCAKICBhbHBoYSA9IDAuNzUsIHNpemUgPSA0KSArCnNjYWxlX3NoYXBlX2lkZW50aXR5KCkgKwpzY2FsZV9jb2xvcl9pZGVudGl0eSgpICsKc2NhbGVfZmlsbF9pZGVudGl0eSgpICsKICAjIEFkZCBhIG5ldyBwb2ludCBmb3IgV1QgRS4gY29saSBtZWRpYW4gZml0bmVzcwogIGdlb21fcG9pbnQoZGF0YSA9IEJDY29udHJvbHNfMTVfMTZfc2hhcmVkX21lZGlhbl9XVCwgCiAgICAgICAgICAgICBhZXMoeCA9IGZpdEQwOEQwMywgeSA9IGZpdEUwM0QwNCksIAogICAgICAgICAgICAgZmlsbCA9ICJyZWQiLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAzLCBzaGFwZSA9IDI0KSArCiAgIyBBZGQgYSBuZXcgcG9pbnQgZm9yIE5lZyBDdHJsIChEMjdOLCBtQ2hlcnJ5KSBtZWRpYW4gZml0bmVzcwogIGdlb21fcG9pbnQoZGF0YSA9IEJDY29udHJvbHNfMTVfMTZfc2hhcmVkX21lZGlhbl9OZWcsIAogICAgICAgICAgICAgYWVzKHggPSBmaXREMDhEMDMsIHkgPSBmaXRFMDNEMDQpLCAKICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDUsIHNoYXBlID0gMTgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpLAogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMS4wKSwKICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAxLjApLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIywKICAgICAgICAjcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJibGFjayIsIGZpbGw9TkEsIHNpemU9MS4wKSkgKwogIGFubm90YXRlKCJ0ZXh0IiwKICAgICAgICAgICB4ID0gbWF4KEwxNS5MMTYuQ291bnRzLjEuMC5UTVAkZml0RDA4RDAzKSwKICAgICAgICAgICB5ID0gbWluKEwxNS5MMTYuQ291bnRzLjEuMC5UTVAkZml0RTAzRDA0KSwgCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgicC12YWx1ZSA9IiwgcF92YWx1ZV9zY2llbnRpZmljX3NoYXJlZF8xLjBfdG1wKSwgaGp1c3QgPSAxLCB2anVzdCA9IDApICsKICBhbm5vdGF0ZSgidGV4dCIsCiAgICAgICAgICAgeCA9IG1heChMMTUuTDE2LkNvdW50cy4xLjAuVE1QJGZpdEQwOEQwMyksCiAgICAgICAgICAgeSA9IG1pbihMMTUuTDE2LkNvdW50cy4xLjAuVE1QJGZpdEUwM0QwNCksCiAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIkNvcnJlbGF0aW9uID0iLCByb3VuZChjb3JfdmFsdWVfc2hhcmVkXzEuMF90bXAsIDIpKSwgaGp1c3QgPSAxLCB2anVzdCA9IC0xLjUpICsKICBhbm5vdGF0ZSgidGV4dCIsCiAgICAgICAgICAgeCA9IG1pbihMMTUuTDE2LkNvdW50cy4xLjAuVE1QJGZpdEQwOEQwMyksCiAgICAgICAgICAgeSA9IG1heChMMTUuTDE2LkNvdW50cy4xLjAuVE1QJGZpdEUwM0QwNCksCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiU2hhcmVkIEFzc2VtYmxpZXMgPSIsIG51bV9yb3dzLmNvdW50cy4xLjAudG1wKSwgaGp1c3QgPSAwLCB2anVzdCA9IDEuNSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoZmxvb3IobWluKEwxNS5MMTYuQ291bnRzLjEuMC5UTVAkZml0RDA4RDAzKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2VpbGluZyhtYXgoTDE1LkwxNi5Db3VudHMuMS4wLlRNUCRmaXREMDhEMDMpKSwgYnkgPSAxKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoZmxvb3IobWluKEwxNS5MMTYuQ291bnRzLjEuMC5UTVAkZml0RTAzRDA0KSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2VpbGluZyhtYXgoTDE1LkwxNi5Db3VudHMuMS4wLlRNUCRmaXRFMDNEMDQpKSwgYnkgPSAxKSkKCkxpYjE1XzE2XzEuMF9UTVBfcDAxIDwtIGdnTWFyZ2luYWwoTGliMTVfMTZfMS4wX1RNUCwgdHlwZSA9ICJoaXN0b2dyYW0iLCBmaWxsID0gImxpZ2h0Ymx1ZTQiLCBhbHBoYSA9IDAuNSkKCkxpYjE1XzE2XzEuMF9UTVBfcDAxCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL0NvcnJlbGF0aW9uL0xpYjE1LjE2LmNvcnJlbGF0aW9uLnBlcmZlY3RzLm1lZGlhbi41QkMuZ29vZC5maXRuZXNzLjEuMC50bXAucG5nIiwKICAgICAgIHBsb3Q9TGliMTVfMTZfMS4wX1RNUF9wMDEsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYsIHVuaXRzID0gImluIikKYGBgCgpQbG90IHNoYXJlZCBmaXRuZXNzIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIExpYjE1ICYgTGliMTYgKHBlcmZlY3RzKSBmb3IgMTAgVE1QOgpgYGB7cn0KIyBFeHRyYWN0IGNvcnJlbGF0aW9uIHZhbHVlIGZyb20gY29yX3ZhbHVlX3NoYXJlZCBvYmplY3QKY29yX3ZhbHVlX3NoYXJlZF8xMF90bXAgPC0gY29yX3Rlc3RfQ291bnRzX3NoYXJlZF8xMF90bXAkZXN0aW1hdGUKCiMgRm9ybWF0IHAtdmFsdWUgaW4gc2NpZW50aWZpYyBub3RhdGlvbgpwX3ZhbHVlX3NjaWVudGlmaWNfc2hhcmVkXzEwX3RtcCA8LSBmb3JtYXQoY29yX3Rlc3RfQ291bnRzX3NoYXJlZF8xMF90bXAkcC52YWx1ZSwgc2NpZW50aWZpYyA9IFRSVUUsIGRpZ2l0cyA9IDQpCgojIEV4dHJhY3QgbnVtYmVyIG9mIHJvd3MKbnVtX3Jvd3MuY291bnRzLjEwLnRtcCA8LSBucm93KEwxNS5MMTYuQ291bnRzLjEwLlRNUCkKCiMgUGxvdCBiYXNlZCBvbiBzaGFyZWQgbXV0SUQKTGliMTVfMTZfMTBfVE1QIDwtIGdncGxvdChMMTUuTDE2LkNvdW50cy4xMC5UTVAsIAogICAgICAgICAgICAgICAgICAgYWVzKHg9Zml0RDA5RDAzLCB5PWZpdEUwNEQwNCkpICsKICBsYWJzKHggPSAiQ29kb24gMSBNZWRpYW4gRml0bmVzcyAoTG9nRkMpIFxuKDEwIM68Zy9tTCB0bXApIiwgeSA9IkNvZG9uIDIgTWVkaWFuIEZpdG5lc3MgKExvZ0ZDKSBcbigxMCDOvGcvbUwgdG1wKSIpICsKICBnZW9tX3Ntb290aChtZXRob2Q9bG0sY29sb3VyPSJibGFjayIpICsKICBnZW9tX2RlbnNpdHkyZChjb2xvdXI9ImJsYWNrIixhbHBoYT0wLjIpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNhc2Vfd2hlbigKICAgIGZpdEQwOUQwMyA+PSAtMSAmIGZpdEUwNEQwNCA+PSAtMSB+ICJsaWdodGJsdWU0IiwKICAgIGZpdEQwOUQwMyA+PSAtMSAmIGZpdEUwNEQwNCA8IC0xIH4gIiMwMDcyQjIiLAogICAgZml0RTA0RDA0ID49IC0xICYgZml0RDA5RDAzIDwgLTEgfiAiI0U2OUYwMCIsCiAgICBUUlVFIH4gImJsYWNrIgogICksCiAgZmlsbCA9IGNhc2Vfd2hlbigKICAgIGZpdEQwOUQwMyA+PSAtMSAmIGZpdEUwNEQwNCA+PSAtMSB+ICJsaWdodGJsdWU0IiwKICAgIGZpdEQwOUQwMyA+PSAtMSAmIGZpdEUwNEQwNCA8IC0xIH4gIiMwMDcyQjIiLAogICAgZml0RTA0RDA0ID49IC0xICYgZml0RDA5RDAzIDwgLTEgfiAiI0U2OUYwMCIsCiAgICBUUlVFIH4gIndoaXRlIgogICksCiAgc2hhcGUgPSBjYXNlX3doZW4oCiAgICBmaXREMDlEMDMgPj0gLTEgJiBmaXRFMDREMDQgPj0gLTEgfiAxNiwKICAgIGZpdEQwOUQwMyA+PSAtMSAmIGZpdEUwNEQwNCA8IC0xIH4gMTYsCiAgICBmaXRFMDREMDQgPj0gLTEgJiBmaXREMDlEMDMgPCAtMSB+IDE2LAogICAgVFJVRSB+IDIxCiAgKSksIAogIGFscGhhID0gMC43NSwgc2l6ZSA9IDQpICsKc2NhbGVfc2hhcGVfaWRlbnRpdHkoKSArCnNjYWxlX2NvbG9yX2lkZW50aXR5KCkgKwpzY2FsZV9maWxsX2lkZW50aXR5KCkgKwogICMgQWRkIGEgbmV3IHBvaW50IGZvciBXVCBFLiBjb2xpIG1lZGlhbiBmaXRuZXNzCiAgZ2VvbV9wb2ludChkYXRhID0gQkNjb250cm9sc18xNV8xNl9zaGFyZWRfbWVkaWFuX1dULCAKICAgICAgICAgICAgIGFlcyh4ID0gZml0RDA5RDAzLCB5ID0gZml0RTA0RDA0KSwgCiAgICAgICAgICAgICBmaWxsID0gInJlZCIsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDMsIHNoYXBlID0gMjQpICsKICAjIEFkZCBhIG5ldyBwb2ludCBmb3IgTmVnIEN0cmwgKEQyN04sIG1DaGVycnkpIG1lZGlhbiBmaXRuZXNzCiAgZ2VvbV9wb2ludChkYXRhID0gQkNjb250cm9sc18xNV8xNl9zaGFyZWRfbWVkaWFuX05lZywgCiAgICAgICAgICAgICBhZXMoeCA9IGZpdEQwOUQwMywgeSA9IGZpdEUwNEQwNCksIAogICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBzaXplID0gNSwgc2hhcGUgPSAxOCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICdibGFjaycsIHNpemUgPSAxLjApLAogICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDEuMCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAjLAogICAgICAgICNwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgZmlsbD1OQSwgc2l6ZT0xLjApKSArCiAgYW5ub3RhdGUoInRleHQiLAogICAgICAgICAgIHggPSBtYXgoTDE1LkwxNi5Db3VudHMuMTAuVE1QJGZpdEQwOUQwMyksCiAgICAgICAgICAgeSA9IG1pbihMMTUuTDE2LkNvdW50cy4xMC5UTVAkZml0RTA0RDA0KSwgCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgicC12YWx1ZSA9IiwgcF92YWx1ZV9zY2llbnRpZmljX3NoYXJlZF8xMF90bXApLCBoanVzdCA9IDEsIHZqdXN0ID0gMCkgKwogIGFubm90YXRlKCJ0ZXh0IiwKICAgICAgICAgICB4ID0gbWF4KEwxNS5MMTYuQ291bnRzLjEwLlRNUCRmaXREMDlEMDMpLAogICAgICAgICAgIHkgPSBtaW4oTDE1LkwxNi5Db3VudHMuMTAuVE1QJGZpdEUwNEQwNCksCiAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIkNvcnJlbGF0aW9uID0iLCByb3VuZChjb3JfdmFsdWVfc2hhcmVkXzEwX3RtcCwgMikpLCBoanVzdCA9IDEsIHZqdXN0ID0gLTEuNSkgKwogIGFubm90YXRlKCJ0ZXh0IiwKICAgICAgICAgICB4ID0gbWluKEwxNS5MMTYuQ291bnRzLjEwLlRNUCRmaXREMDlEMDMpLAogICAgICAgICAgIHkgPSBtYXgoTDE1LkwxNi5Db3VudHMuMTAuVE1QJGZpdEUwNEQwNCksCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiU2hhcmVkIEFzc2VtYmxpZXMgPSIsIG51bV9yb3dzLmNvdW50cy4xMC50bXApLCBoanVzdCA9IDAsIHZqdXN0ID0gMS41KSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShmbG9vcihtaW4oTDE1LkwxNi5Db3VudHMuMTAuVE1QJGZpdEQwOUQwMykpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNlaWxpbmcobWF4KEwxNS5MMTYuQ291bnRzLjEwLlRNUCRmaXREMDlEMDMpKSwgYnkgPSAxKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoZmxvb3IobWluKEwxNS5MMTYuQ291bnRzLjEwLlRNUCRmaXRFMDREMDQpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZWlsaW5nKG1heChMMTUuTDE2LkNvdW50cy4xMC5UTVAkZml0RTA0RDA0KSksIGJ5ID0gMSkpCgpMaWIxNV8xNl8xMF9UTVBfcDAxIDwtIGdnTWFyZ2luYWwoTGliMTVfMTZfMTBfVE1QLCB0eXBlID0gImhpc3RvZ3JhbSIsIGZpbGwgPSAibGlnaHRibHVlNCIsIGFscGhhID0gMC41KQoKTGliMTVfMTZfMTBfVE1QX3AwMQpgYGAKCmBgYHtyIGVjaG89RkFMU0V9Cmdnc2F2ZShmaWxlPSJQZXJmZWN0cy9QTE9UUy9Db3JyZWxhdGlvbi9MaWIxNS4xNi5jb3JyZWxhdGlvbi5wZXJmZWN0cy5tZWRpYW4uNUJDLmdvb2QuZml0bmVzcy4xMC50bXAucG5nIiwKICAgICAgIHBsb3Q9TGliMTVfMTZfMTBfVE1QX3AwMSwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iKQpgYGAKClBsb3Qgc2hhcmVkIGZpdG5lc3MgY29ycmVsYXRpb25zIGJldHdlZW4gTGliMTUgJiBMaWIxNiAocGVyZmVjdHMpIGZvciA1MCBUTVA6CmBgYHtyfQojIEV4dHJhY3QgY29ycmVsYXRpb24gdmFsdWUgZnJvbSBjb3JfdmFsdWVfc2hhcmVkIG9iamVjdApjb3JfdmFsdWVfc2hhcmVkXzUwX3RtcCA8LSBjb3JfdGVzdF9Db3VudHNfc2hhcmVkXzUwX3RtcCRlc3RpbWF0ZQoKIyBGb3JtYXQgcC12YWx1ZSBpbiBzY2llbnRpZmljIG5vdGF0aW9uCnBfdmFsdWVfc2NpZW50aWZpY19zaGFyZWRfNTBfdG1wIDwtIGZvcm1hdChjb3JfdGVzdF9Db3VudHNfc2hhcmVkXzUwX3RtcCRwLnZhbHVlLCBzY2llbnRpZmljID0gVFJVRSwgZGlnaXRzID0gNCkKCiMgRXh0cmFjdCBudW1iZXIgb2Ygcm93cwpudW1fcm93cy5jb3VudHMuNTAudG1wIDwtIG5yb3coTDE1LkwxNi5Db3VudHMuNTAuVE1QKQoKIyBQbG90IGJhc2VkIG9uIHNoYXJlZCBtdXRJRApMaWIxNV8xNl81MF9UTVAgPC0gZ2dwbG90KEwxNS5MMTYuQ291bnRzLjUwLlRNUCwgCiAgICAgICAgICAgICAgICAgICBhZXMoeD1maXREMTBEMDMsIHk9Zml0RTA1RDA0KSkgKwogIGxhYnMoeCA9ICJDb2RvbiAxIE1lZGlhbiBGaXRuZXNzIChMb2dGQykgXG4oNTAgzrxnL21MIHRtcCkiLCB5ID0iQ29kb24gMiBNZWRpYW4gRml0bmVzcyAoTG9nRkMpIFxuKDUwIM68Zy9tTCB0bXApIikgKwogIGdlb21fc21vb3RoKG1ldGhvZD1sbSxjb2xvdXI9ImJsYWNrIikgKwogIGdlb21fZGVuc2l0eTJkKGNvbG91cj0iYmxhY2siLGFscGhhPTAuMikgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gY2FzZV93aGVuKAogICAgZml0RDEwRDAzID49IC0xICYgZml0RTA1RDA0ID49IC0xIH4gImxpZ2h0Ymx1ZTQiLAogICAgZml0RDEwRDAzID49IC0xICYgZml0RTA1RDA0IDwgLTEgfiAiIzAwNzJCMiIsCiAgICBmaXRFMDVEMDQgPj0gLTEgJiBmaXREMTBEMDMgPCAtMSB+ICIjRTY5RjAwIiwKICAgIFRSVUUgfiAiYmxhY2siCiAgKSwKICBmaWxsID0gY2FzZV93aGVuKAogICAgZml0RDEwRDAzID49IC0xICYgZml0RTA1RDA0ID49IC0xIH4gImxpZ2h0Ymx1ZTQiLAogICAgZml0RDEwRDAzID49IC0xICYgZml0RTA1RDA0IDwgLTEgfiAiIzAwNzJCMiIsCiAgICBmaXRFMDVEMDQgPj0gLTEgJiBmaXREMTBEMDMgPCAtMSB+ICIjRTY5RjAwIiwKICAgIFRSVUUgfiAid2hpdGUiCiAgKSwKICBzaGFwZSA9IGNhc2Vfd2hlbigKICAgIGZpdEQxMEQwMyA+PSAtMSAmIGZpdEUwNUQwNCA+PSAtMSB+IDE2LAogICAgZml0RDEwRDAzID49IC0xICYgZml0RTA1RDA0IDwgLTEgfiAxNiwKICAgIGZpdEUwNUQwNCA+PSAtMSAmIGZpdEQxMEQwMyA8IC0xIH4gMTYsCiAgICBUUlVFIH4gMjEKICApKSwgCiAgYWxwaGEgPSAwLjc1LCBzaXplID0gNCkgKwpzY2FsZV9zaGFwZV9pZGVudGl0eSgpICsKc2NhbGVfY29sb3JfaWRlbnRpdHkoKSArCnNjYWxlX2ZpbGxfaWRlbnRpdHkoKSArCiAgIyBBZGQgYSBuZXcgcG9pbnQgZm9yIFdUIEUuIGNvbGkgbWVkaWFuIGZpdG5lc3MKICBnZW9tX3BvaW50KGRhdGEgPSBCQ2NvbnRyb2xzXzE1XzE2X3NoYXJlZF9tZWRpYW5fV1QsIAogICAgICAgICAgICAgYWVzKHggPSBmaXREMTBEMDMsIHkgPSBmaXRFMDVEMDQpLCAKICAgICAgICAgICAgIGZpbGwgPSAicmVkIiwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMywgc2hhcGUgPSAyNCkgKwogICMgQWRkIGEgbmV3IHBvaW50IGZvciBOZWcgQ3RybCAoRDI3TiwgbUNoZXJyeSkgbWVkaWFuIGZpdG5lc3MKICBnZW9tX3BvaW50KGRhdGEgPSBCQ2NvbnRyb2xzXzE1XzE2X3NoYXJlZF9tZWRpYW5fTmVnLCAKICAgICAgICAgICAgIGFlcyh4ID0gZml0RDEwRDAzLCB5ID0gZml0RTA1RDA0KSwgCiAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIHNpemUgPSA1LCBzaGFwZSA9IDE4KSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2JsYWNrJywgc2l6ZSA9IDEuMCksCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMS4wKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArICMsCiAgICAgICAgI3BhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiYmxhY2siLCBmaWxsPU5BLCBzaXplPTEuMCkpICsKICBhbm5vdGF0ZSgidGV4dCIsCiAgICAgICAgICAgeCA9IG1heChMMTUuTDE2LkNvdW50cy41MC5UTVAkZml0RDEwRDAzKSwKICAgICAgICAgICB5ID0gbWluKEwxNS5MMTYuQ291bnRzLjUwLlRNUCRmaXRFMDVEMDQpLCAKICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJwLXZhbHVlID0iLCBwX3ZhbHVlX3NjaWVudGlmaWNfc2hhcmVkXzUwX3RtcCksIGhqdXN0ID0gMSwgdmp1c3QgPSAwKSArCiAgYW5ub3RhdGUoInRleHQiLAogICAgICAgICAgIHggPSBtYXgoTDE1LkwxNi5Db3VudHMuNTAuVE1QJGZpdEQxMEQwMyksCiAgICAgICAgICAgeSA9IG1pbihMMTUuTDE2LkNvdW50cy41MC5UTVAkZml0RTA1RDA0KSwKICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiQ29ycmVsYXRpb24gPSIsIHJvdW5kKGNvcl92YWx1ZV9zaGFyZWRfNTBfdG1wLCAyKSksIGhqdXN0ID0gMSwgdmp1c3QgPSAtMS41KSArCiAgYW5ub3RhdGUoInRleHQiLAogICAgICAgICAgIHggPSBtaW4oTDE1LkwxNi5Db3VudHMuNTAuVE1QJGZpdEQxMEQwMyksCiAgICAgICAgICAgeSA9IG1heChMMTUuTDE2LkNvdW50cy41MC5UTVAkZml0RTA1RDA0KSwKICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJTaGFyZWQgQXNzZW1ibGllcyA9IiwgbnVtX3Jvd3MuY291bnRzLjUwLnRtcCksIGhqdXN0ID0gMCwgdmp1c3QgPSAxLjUpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKGZsb29yKG1pbihMMTUuTDE2LkNvdW50cy41MC5UTVAkZml0RDEwRDAzKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2VpbGluZyhtYXgoTDE1LkwxNi5Db3VudHMuNTAuVE1QJGZpdEQxMEQwMykpLCBieSA9IDEpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcShmbG9vcihtaW4oTDE1LkwxNi5Db3VudHMuNTAuVE1QJGZpdEUwNUQwNCkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNlaWxpbmcobWF4KEwxNS5MMTYuQ291bnRzLjUwLlRNUCRmaXRFMDVEMDQpKSwgYnkgPSAxKSkKCkxpYjE1XzE2XzUwX1RNUF9wMDEgPC0gZ2dNYXJnaW5hbChMaWIxNV8xNl81MF9UTVAsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbCA9ICJsaWdodGJsdWU0IiwgYWxwaGEgPSAwLjUpCgpMaWIxNV8xNl81MF9UTVBfcDAxCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL0NvcnJlbGF0aW9uL0xpYjE1LjE2LmNvcnJlbGF0aW9uLnBlcmZlY3RzLm1lZGlhbi41QkMuZ29vZC5maXRuZXNzLjUwLnRtcC5wbmciLAogICAgICAgcGxvdD1MaWIxNV8xNl81MF9UTVBfcDAxLAogICAgICAgZHBpPTYwMCwgd2lkdGggPSA4LCBoZWlnaHQgPSA2LCB1bml0cyA9ICJpbiIpCmBgYAoKUGxvdCBzaGFyZWQgZml0bmVzcyBjb3JyZWxhdGlvbnMgYmV0d2VlbiBMaWIxNSAmIExpYjE2IChwZXJmZWN0cykgZm9yIDIwMCBUTVA6CmBgYHtyfQojIEV4dHJhY3QgY29ycmVsYXRpb24gdmFsdWUgZnJvbSBjb3JfdmFsdWVfc2hhcmVkIG9iamVjdApjb3JfdmFsdWVfc2hhcmVkXzIwMF90bXAgPC0gY29yX3Rlc3RfQ291bnRzX3NoYXJlZF8yMDBfdG1wJGVzdGltYXRlCgojIEZvcm1hdCBwLXZhbHVlIGluIHNjaWVudGlmaWMgbm90YXRpb24KcF92YWx1ZV9zY2llbnRpZmljX3NoYXJlZF8yMDBfdG1wIDwtIGZvcm1hdChjb3JfdGVzdF9Db3VudHNfc2hhcmVkXzIwMF90bXAkcC52YWx1ZSwgc2NpZW50aWZpYyA9IFRSVUUsIGRpZ2l0cyA9IDQpCgojIEV4dHJhY3QgbnVtYmVyIG9mIHJvd3MKbnVtX3Jvd3MuY291bnRzLjIwMC50bXAgPC0gbnJvdyhMMTUuTDE2LkNvdW50cy4yMDAuVE1QKQoKIyBQbG90IGJhc2VkIG9uIHNoYXJlZCBtdXRJRApMaWIxNV8xNl8yMDBfVE1QIDwtIGdncGxvdChMMTUuTDE2LkNvdW50cy4yMDAuVE1QLCAKICAgICAgICAgICAgICAgICAgIGFlcyh4PWZpdEQxMUQwMywgeT1maXRFMDZEMDQpKSArCiAgbGFicyh4ID0gIkNvZG9uIDEgTWVkaWFuIEZpdG5lc3MgKExvZ0ZDKSBcbjQwMHggTUlDICgyMDAgzrxnL21MIHRtcCkiLAogICAgICAgeSA9IkNvZG9uIDIgTWVkaWFuIEZpdG5lc3MgKExvZ0ZDKSBcbjQwMHggTUlDICgyMDAgzrxnL21MIHRtcCkiKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtLGNvbG91cj0iYmxhY2siKSArCiAgZ2VvbV9kZW5zaXR5MmQoY29sb3VyPSJibGFjayIsYWxwaGE9MC4yKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBjYXNlX3doZW4oCiAgICBmaXREMTFEMDMgPj0gLTEgJiBmaXRFMDZEMDQgPj0gLTEgfiAibGlnaHRibHVlNCIsCiAgICBmaXREMTFEMDMgPj0gLTEgJiBmaXRFMDZEMDQgPCAtMSB+ICIjMDA3MkIyIiwKICAgIGZpdEUwNkQwNCA+PSAtMSAmIGZpdEQxMUQwMyA8IC0xIH4gIiNFNjlGMDAiLAogICAgVFJVRSB+ICJibGFjayIKICApLAogIGZpbGwgPSBjYXNlX3doZW4oCiAgICBmaXREMTFEMDMgPj0gLTEgJiBmaXRFMDZEMDQgPj0gLTEgfiAibGlnaHRibHVlNCIsCiAgICBmaXREMTFEMDMgPj0gLTEgJiBmaXRFMDZEMDQgPCAtMSB+ICIjMDA3MkIyIiwKICAgIGZpdEUwNkQwNCA+PSAtMSAmIGZpdEQxMUQwMyA8IC0xIH4gIiNFNjlGMDAiLAogICAgVFJVRSB+ICJ3aGl0ZSIKICApLAogIHNoYXBlID0gY2FzZV93aGVuKAogICAgZml0RDExRDAzID49IC0xICYgZml0RTA2RDA0ID49IC0xIH4gMTYsCiAgICBmaXREMTFEMDMgPj0gLTEgJiBmaXRFMDZEMDQgPCAtMSB+IDE2LAogICAgZml0RTA2RDA0ID49IC0xICYgZml0RDExRDAzIDwgLTEgfiAxNiwKICAgIFRSVUUgfiAyMQogICkpLCAKICBhbHBoYSA9IDAuNzUsIHNpemUgPSA0KSArCnNjYWxlX3NoYXBlX2lkZW50aXR5KCkgKwpzY2FsZV9jb2xvcl9pZGVudGl0eSgpICsKc2NhbGVfZmlsbF9pZGVudGl0eSgpICsKICAjIEFkZCBhIG5ldyBwb2ludCBmb3IgV1QgRS4gY29saSBtZWRpYW4gZml0bmVzcwogIGdlb21fcG9pbnQoZGF0YSA9IEJDY29udHJvbHNfMTVfMTZfc2hhcmVkX21lZGlhbl9XVCwgCiAgICAgICAgICAgICBhZXMoeCA9IGZpdEQxMUQwMywgeSA9IGZpdEUwNkQwNCksIAogICAgICAgICAgICAgZmlsbCA9ICJyZWQiLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSA0LCBzaGFwZSA9IDI0KSArCiAgIyBBZGQgYSBuZXcgcG9pbnQgZm9yIE5lZyBDdHJsIChEMjdOLCBtQ2hlcnJ5KSBtZWRpYW4gZml0bmVzcwogIGdlb21fcG9pbnQoZGF0YSA9IEJDY29udHJvbHNfMTVfMTZfc2hhcmVkX21lZGlhbl9OZWcsIAogICAgICAgICAgICAgYWVzKHggPSBmaXREMTFEMDMsIHkgPSBmaXRFMDZEMDQpLCAKICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDUsIHNoYXBlID0gMTgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpLAogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMS4wKSwKICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAxLjApLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIywKICAgICAgICAjcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJibGFjayIsIGZpbGw9TkEsIHNpemU9MS4wKSkgKwogIGFubm90YXRlKCJ0ZXh0IiwKICAgICAgICAgICB4ID0gbWF4KEwxNS5MMTYuQ291bnRzLjIwMC5UTVAkZml0RDExRDAzKSwKICAgICAgICAgICB5ID0gbWluKEwxNS5MMTYuQ291bnRzLjIwMC5UTVAkZml0RTA2RDA0KSwgCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgicC12YWx1ZSA9IiwgcF92YWx1ZV9zY2llbnRpZmljX3NoYXJlZF8yMDBfdG1wKSwgaGp1c3QgPSAxLCB2anVzdCA9IDAsIHNpemUgPSA1KSArCiAgYW5ub3RhdGUoInRleHQiLAogICAgICAgICAgIHggPSBtYXgoTDE1LkwxNi5Db3VudHMuMjAwLlRNUCRmaXREMTFEMDMpLAogICAgICAgICAgIHkgPSBtaW4oTDE1LkwxNi5Db3VudHMuMjAwLlRNUCRmaXRFMDZEMDQpLAogICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJDb3JyZWxhdGlvbiA9Iiwgcm91bmQoY29yX3ZhbHVlX3NoYXJlZF8yMDBfdG1wLCAyKSksIGhqdXN0ID0gMSwgdmp1c3QgPSAtMS41LCBzaXplID0gNSkgKwogIGFubm90YXRlKCJ0ZXh0IiwKICAgICAgICAgICB4ID0gbWluKEwxNS5MMTYuQ291bnRzLjIwMC5UTVAkZml0RDExRDAzKSwKICAgICAgICAgICB5ID0gbWF4KEwxNS5MMTYuQ291bnRzLjIwMC5UTVAkZml0RTA2RDA0KSwKICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJTaGFyZWQgQXNzZW1ibGllcyA9IiwgbnVtX3Jvd3MuY291bnRzLjIwMC50bXApLCBoanVzdCA9IDAsIHZqdXN0ID0gMS41LCBzaXplID0gNSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoZmxvb3IobWluKEwxNS5MMTYuQ291bnRzLjIwMC5UTVAkZml0RDExRDAzKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2VpbGluZyhtYXgoTDE1LkwxNi5Db3VudHMuMjAwLlRNUCRmaXREMTFEMDMpKSwgYnkgPSAxKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoZmxvb3IobWluKEwxNS5MMTYuQ291bnRzLjIwMC5UTVAkZml0RTA2RDA0KSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2VpbGluZyhtYXgoTDE1LkwxNi5Db3VudHMuMjAwLlRNUCRmaXRFMDZEMDQpKSwgYnkgPSAxKSkKCkxpYjE1XzE2XzIwMF9UTVBfcDAxIDwtIGdnTWFyZ2luYWwoTGliMTVfMTZfMjAwX1RNUCwgdHlwZSA9ICJoaXN0b2dyYW0iLCBmaWxsID0gImxpZ2h0Ymx1ZTQiLCBhbHBoYSA9IDAuNSkKCkxpYjE1XzE2XzIwMF9UTVBfcDAxCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL0NvcnJlbGF0aW9uL0xpYjE1LjE2LmNvcnJlbGF0aW9uLnBlcmZlY3RzLm1lZGlhbi41QkMuZ29vZC5maXRuZXNzLjIwMC50bXAucG5nIiwKICAgICAgIHBsb3Q9TGliMTVfMTZfMjAwX1RNUF9wMDEsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYsIHVuaXRzID0gImluIikKYGBgCgojIyBQZXJmZWN0cyBQbG90cwpUaGUgZm9sbG93aW5nIHNlY3Rpb24gcGxvdHMgdmFyaW91cyBzdW1tYXJpemVzIG9mIHRoZSBQZXJmZWN0cyBCQ3MgKG1pbmltdW0gNUJDIGNvdW50L211dElEKToKClJlLW9yZGVyIHRoZSAicGVyZmVjdHMiIGRhdGFzZXQgYnkgIm51bXBydW5lZEJDcyIgaW4gYXNjZW5kaW5nIG9yZGVyOgpgYGB7cn0KIyBSYW5rIG9yZGVyIGJhc2VkIG9uIG51bXBydW5lZEJDcyBpbiBMaWIxNQpwZXJmZWN0c19zaGFyZWQucmFuay4xNSA8LSBwZXJmZWN0c18xNV8xNl9zaGFyZWQgJT4lCiAgYXJyYW5nZShudW1wcnVuZWRCQ3MueCkKCiMgUmFuayBvcmRlciBiYXNlZCBvbiBudW1wcnVuZWRCQ3MgaW4gTGliMTYKcGVyZmVjdHNfc2hhcmVkLnJhbmsuMTYgPC0gcGVyZmVjdHNfMTVfMTZfc2hhcmVkICU+JQogIGFycmFuZ2UobnVtcHJ1bmVkQkNzLnkpCmBgYAoKTWFrZSBwbG90dGluZyBrZXkgYmFzZWQgb24gIm51bXBydW5lZEJDcyIKYGBge3J9CiMgUGxvdHRpbmcga2V5IGJhc2VkIG9uIG51bXBydW5lZEJDcyBpbiBMaWIxNQpwZXJmZWN0c19zaGFyZWQucmFuay4xNSRudW1wcnVuZWRCQ3MueC5rZXkgPC0gMTpsZW5ndGgocGVyZmVjdHNfc2hhcmVkLnJhbmsuMTUkbXV0SUQpCgojIFBsb3R0aW5nIGtleSBiYXNlZCBvbiBudW1wcnVuZWRCQ3MgaW4gTGliMTYKcGVyZmVjdHNfc2hhcmVkLnJhbmsuMTYkbnVtcHJ1bmVkQkNzLnkua2V5IDwtIDE6bGVuZ3RoKHBlcmZlY3RzX3NoYXJlZC5yYW5rLjE2JG11dElEKQpgYGAKCiMjIyBIb21vbG9nIEZpdG5lc3MgUGxvdHMKCiMjIyMgUmFuayBPcmRlciBGaXRuZXNzIFBsb3RzClBsb3QgdGhlIG51bWJlciBvZiBwZXJmZWN0cyBiYXJjb2RlcyByZWNvdmVyZWQgYmFzZWQgb24gdGhlIHJhbmsgb3JkZXIgb2YgaG9tb2xvZ3M6CgoqKlNoYXJlZCBtdXRJRHMqKgpgYGB7cn0KIyBSZW5hbWUgY29sdW1ucyBpbiBvbmUgb2YgdGhlIGRhdGEgZnJhbWVzCmNvbG5hbWVzKHBlcmZlY3RzX3NoYXJlZC5yYW5rLjE2KSA8LSBwYXN0ZTAoY29sbmFtZXMocGVyZmVjdHNfc2hhcmVkLnJhbmsuMTYpLCAiXzE2IikKCiMgQ29tYmluZSB0aGUgZGF0YSBmcmFtZXMKcGVyZmVjdHNfc2hhcmVkLnJhbmsuMTUuMTYgPC0gY2JpbmQocGVyZmVjdHNfc2hhcmVkLnJhbmsuMTUsIHBlcmZlY3RzX3NoYXJlZC5yYW5rLjE2KQpgYGAKCmBgYHtyfQojIENyZWF0ZSB0aGUgcGxvdApwZXJmZWN0c19zaGFyZWQucmFuay4xNS4xNl9wbG90IDwtIGdncGxvdChwZXJmZWN0c19zaGFyZWQucmFuay4xNS4xNiwgYWVzKHggPSBudW1wcnVuZWRCQ3MueC5rZXkpKSArCiAgZ2VvbV9wb2ludChhZXMoeSA9IG51bXBydW5lZEJDcy54LCBjb2xvciA9ICJMaWIxNSIpLCBzaXplID0gMikgKwogIGdlb21fcG9pbnQoYWVzKHkgPSBudW1wcnVuZWRCQ3MueV8xNiwgY29sb3IgPSAiTGliMTYiKSwgc2l6ZSA9IDIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZSA9ICJMaWJyYXJ5IiwgdmFsdWVzID0gYygiTGliMTUiID0gIiMwMDcyQjIiLCAiTGliMTYiID0gIiNFNjlGMDAiKSkgKwogIHlsYWIoIk51bWJlciBvZiBCYXJjb2RlcyIpICsKICB4bGFiKCJSYW5rIE9yZGVyZWQgSG9tb2xvZyIpICsKICBzY2FsZV95X2xvZzEwKGxpbWl0cyA9IGMoMSwgMjAwMCkpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMC41KSwgCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCnBlcmZlY3RzX3NoYXJlZC5yYW5rLjE1LjE2X3Bsb3QKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iUGVyZmVjdHMvUExPVFMvcGVyZmVjdHMxNV8xNl9yYW5rX29yZGVyZWRfbnVtXzFCQ3MucG5nIiwgcGxvdD1wZXJmZWN0c19zaGFyZWQucmFuay4xNS4xNl9wbG90LAogICAgICAgZHBpPTYwMCwgd2lkdGggPSA4LCBoZWlnaHQgPSA2LCB1bml0cyA9ICJpbiIpCmBgYAoKIyMjIyMgUGVyZmVjdHMgKD4xIEJDKSBIaXN0b2dyYW0KClBsb3QgdGhlIFBlcmZlY3RzIGNvdW50cyBiYXNlZCBvbiBmaXRuZXNzIHZhbHVlcyBiZXR3ZWVuIEQwNSB2cy4gRDAzIChMaWIxNSkgb3IgRDEyIHZzLiBEMDQgKExpYjE2KToKYGBge3J9CnJhbmtfaGlzdG9ncmFtXzFCQ18xNV8xNiA8LSBnZ3Bsb3QocGVyZmVjdHNfc2hhcmVkLnJhbmsuMTUuMTYsIGFlcyh4ID0gZml0RDA1RDAzLCBmaWxsID0gIkxpYjE1IikpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMSwgYWxwaGEgPSAwLjUsIGNvbG9yID0gImJsYWNrIikgKwogIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gZml0RDEyRDA0LCBmaWxsID0gIkxpYjE2IiksIGJpbndpZHRoID0gMC4xLCBhbHBoYSA9IDAuNSwgY29sb3IgPSAiYmxhY2siKSArCiAgeGxhYigiTWVkaWFuIEZpdG5lc3MgXG4oTG9nMiBGb2xkIENoYW5nZSkiKSArCiAgeWxhYigiQ291bnRzIikgKwogIGdndGl0bGUoIkNvbXBsZW1lbnRhdGlvbiBSYW5rIENvdW50IFxuKFBlcmZlY3RzID4xIEJDKSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKAogICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICdibGFjaycsIHNpemUgPSAwLjUpLAogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiksCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoLTcsIDMpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoMCwgNTApKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiTGliMTUiID0gIiMwMDcyQjIiLCAiTGliMTYiID0gIiNFNjlGMDAiKSwgbmFtZSA9ICJMaWJyYXJ5IikKCnJhbmtfaGlzdG9ncmFtXzFCQ18xNV8xNgpgYGAKCmBgYHtyIGVjaG89RkFMU0V9Cmdnc2F2ZShmaWxlPSJQZXJmZWN0cy9QTE9UUy9MaWIxNS4xNi5CQy5wZXJmZWN0cy5maXRuZXNzLmNvdW50cy4xQkMucG5nIiwgcGxvdD1yYW5rX2hpc3RvZ3JhbV8xQkNfMTVfMTYsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA0LCB1bml0cyA9ICJpbiIpCmBgYAoKU3VtbWFyaXplIHRoZSB0b3RhbCBudW1iZXIgb2YgdW5pcXVlIFBlcmZlY3RzIHJlZ2FyZGxlc3Mgb2YgZml0bmVzcyB2YWx1ZToKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgTGliMTUKcGVyZmVjdHMxNSAlPiUKICBucm93KC4pCgojIExpYjE2CnBlcmZlY3RzMTYgJT4lCiAgbnJvdyguKQoKIyBTaGFyZWQgUGVyZmVjdHMKcGVyZmVjdHNfMTVfMTZfc2hhcmVkICU+JQogIG5yb3coLikKYGBgCgpTdW1tYXJpemUgdGhlIG51bWJlciBvZiBQZXJmZWN0cyB3aXRoIGZpdG5lc3MgZ3JlYXRlciB0aGFuIC0xLjAgaW4gRDA1IHZzLiBEMDMgKExpYjE1KSBvciBEMTIgdnMuIEQwNCAoTGliMTYpIGNvbmRpdGlvbnM6CmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIFNoYXJlZCBQZXJmZWN0cyAtIExpYjE1CnBlcmZlY3RzXzE1XzE2X3NoYXJlZCAlPiUgZmlsdGVyKGZpdEQwNUQwMz4tMS4wKSAlPiUKICBucm93KC4pCgojIFNoYXJlZCBQZXJmZWN0cyAtIExpYjE2CnBlcmZlY3RzXzE1XzE2X3NoYXJlZCAlPiUgZmlsdGVyKGZpdEQxMkQwND4tMS4wKSAlPiUKICBucm93KC4pCmBgYAoKIyMjIyMgUGVyZmVjdHMgKD4yIEJDcykgSGlzdG9ncmFtCgpQbG90IHRoZSBQZXJmZWN0cyBjb3VudHMgYmFzZWQgb24gZml0bmVzcyB2YWx1ZXMgYmV0d2VlbiBEMDUgdnMuIEQwMyAoTGliMTUpIG9yIEQxMiB2cy4gRDA0IChMaWIxNikgYXQgPiAyIEJDczoKCmBgYHtyfQpyYW5rX2hpc3RvZ3JhbV8yQkNfMTVfMTYgPC0gZ2dwbG90KHBlcmZlY3RzXzE1XzE2X3NoYXJlZF8yQkNzLCBhZXMoeCA9IGZpdEQwNUQwMywgZmlsbCA9ICJMaWIxNSIpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjEsIGFscGhhID0gMC41LCBjb2xvciA9ICJibGFjayIpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IGZpdEQxMkQwNCwgZmlsbCA9ICJMaWIxNiIpLCBiaW53aWR0aCA9IDAuMSwgYWxwaGEgPSAwLjUsIGNvbG9yID0gImJsYWNrIikgKwogIHhsYWIoIkZpdG5lc3MgKExvZzIgRm9sZCBDaGFuZ2UpIikgKwogIHlsYWIoIkNvdW50cyIpICsKICBnZ3RpdGxlKCJDb21wbGVtZW50YXRpb24gUmFuayBDb3VudCBcbihQZXJmZWN0cyA+MiBCQykiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZSgKICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMC41KSwKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKC03LCAzKSkgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKDAsIDUwKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkxpYjE1IiA9ICIjMDA3MkIyIiwgIkxpYjE2IiA9ICIjRTY5RjAwIiksIG5hbWUgPSAiTGlicmFyeSIpCgpyYW5rX2hpc3RvZ3JhbV8yQkNfMTVfMTYKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iUGVyZmVjdHMvUExPVFMvTGliMTUuMTYuQkMucGVyZmVjdHMuZml0bmVzcy5jb3VudHMuMkJDLnBuZyIsIHBsb3Q9cmFua19oaXN0b2dyYW1fMkJDXzE1XzE2LAogICAgICAgZHBpPTYwMCwgd2lkdGggPSA4LCBoZWlnaHQgPSA2LCB1bml0cyA9ICJpbiIpCmBgYAoKU3VtbWFyaXplIHRoZSB0b3RhbCBudW1iZXIgb2YgdW5pcXVlIFBlcmZlY3RzICg+MiBCQ3MpIHJlZ2FyZGxlc3Mgb2YgZml0bmVzcyB2YWx1ZToKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgU2hhcmVkIFBlcmZlY3RzCnBlcmZlY3RzXzE1XzE2X3NoYXJlZF8yQkNzICU+JQogIG5yb3coLikKYGBgCgpTdW1tYXJpemUgdGhlIG51bWJlciBvZiBQZXJmZWN0cyAoPjIgQkNzKSB3aXRoIGZpdG5lc3MgZ3JlYXRlciB0aGFuIC0xLjAgaW4gRDA1IHZzLiBEMDMgKExpYjE1KSBvciBEMTIgdnMuIEQwNCAoTGliMTYpIGNvbmRpdGlvbnM6CmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIExpYjE1CnBlcmZlY3RzXzE1XzE2X3NoYXJlZF8yQkNzICU+JSBmaWx0ZXIoZml0RDA1RDAzPi0xLjApICU+JQogIG5yb3coLikKCiMgTGliMTYKcGVyZmVjdHNfMTVfMTZfc2hhcmVkXzJCQ3MgJT4lIGZpbHRlcihmaXREMTJEMDQ+LTEuMCkgJT4lCiAgbnJvdyguKQpgYGAKCiMjIyMjIFBlcmZlY3RzICg+NSBCQ3MpIEhpc3RvZ3JhbQoKUGxvdCB0aGUgUGVyZmVjdHMgY291bnRzIGJhc2VkIG9uIGZpdG5lc3MgdmFsdWVzIGJldHdlZW4gRDA1IHZzLiBEMDMgKExpYjE1KSBvciBEMTIgdnMuIEQwNCAoTGliMTYpIGF0ID4gNSBCQ3M6CmBgYHtyfQpyYW5rX2hpc3RvZ3JhbV81QkNfMTVfMTYgPC0gZ2dwbG90KHBlcmZlY3RzXzE1XzE2X3NoYXJlZF81QkNzLCBhZXMoeCA9IGZpdEQwNUQwMywgZmlsbCA9ICJMaWIxNSIpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjEsIGFscGhhID0gMC41LCBjb2xvciA9ICJibGFjayIpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IGZpdEQxMkQwNCwgZmlsbCA9ICJMaWIxNiIpLCBiaW53aWR0aCA9IDAuMSwgYWxwaGEgPSAwLjUsIGNvbG9yID0gImJsYWNrIikgKwogIHhsYWIoIkZpdG5lc3MgKExvZzIgRm9sZCBDaGFuZ2UpIikgKwogIHlsYWIoIkNvdW50cyIpICsKICBnZ3RpdGxlKCJDb21wbGVtZW50YXRpb24gUmFuayBDb3VudCBcbihQZXJmZWN0cyA+NSBCQykiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZSgKICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMC41KSwKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoLTcsIDMpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoMCwgNTApKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiTGliMTUiID0gIiMwMDcyQjIiLCAiTGliMTYiID0gIiNFNjlGMDAiKSwgbmFtZSA9ICJMaWJyYXJ5IikKCnJhbmtfaGlzdG9ncmFtXzVCQ18xNV8xNgpgYGAKCmBgYHtyIGVjaG89RkFMU0V9Cmdnc2F2ZShmaWxlPSJQZXJmZWN0cy9QTE9UUy9MaWIxNS4xNi5CQy5wZXJmZWN0cy5maXRuZXNzLmNvdW50cy41QkMucG5nIiwgcGxvdD1yYW5rX2hpc3RvZ3JhbV81QkNfMTVfMTYsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYsIHVuaXRzID0gImluIikKYGBgCgpTdW1tYXJpemUgdGhlIHRvdGFsIG51bWJlciBvZiB1bmlxdWUgUGVyZmVjdHMgKD4yIEJDcykgcmVnYXJkbGVzcyBvZiBmaXRuZXNzIHZhbHVlOgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBTaGFyZWQgUGVyZmVjdHMKcGVyZmVjdHNfMTVfMTZfc2hhcmVkXzVCQ3MgJT4lCiAgbnJvdyguKQpgYGAKClN1bW1hcml6ZSB0aGUgbnVtYmVyIG9mIFBlcmZlY3RzICg+MiBCQ3MpIHdpdGggZml0bmVzcyBncmVhdGVyIHRoYW4gLTEuMCBpbiBEMDUgdnMuIEQwMyAoTGliMTUpIG9yIEQxMiB2cy4gRDA0IChMaWIxNikgY29uZGl0aW9uczoKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgTGliMTUKcGVyZmVjdHNfMTVfMTZfc2hhcmVkXzVCQ3MgJT4lIGZpbHRlcihmaXREMDVEMDM+LTEuMCkgJT4lCiAgbnJvdyguKQoKIyBMaWIxNgpwZXJmZWN0c18xNV8xNl9zaGFyZWRfNUJDcyAlPiUgZmlsdGVyKGZpdEQxMkQwND4tMS4wKSAlPiUKICBucm93KC4pCmBgYAoKKipQbG90IGFsbCBtaW5pbXVtIEJDIGxldmVscyB0b2dldGhlcioqCmBgYHtyIGVjaG89RkFMU0V9CnBhdGNoNCA8LSBwZXJmZWN0c19zaGFyZWQucmFuay4xNS4xNl9wbG90IC8gcmFua19oaXN0b2dyYW1fMUJDXzE1XzE2IC8gcmFua19oaXN0b2dyYW1fMkJDXzE1XzE2IC8gcmFua19oaXN0b2dyYW1fNUJDXzE1XzE2CnBhdGNoNApgYGAKCmBgYHtyIGVjaG89RkFMU0V9Cmdnc2F2ZShmaWxlPSJQZXJmZWN0cy9QTE9UUy9MaWIxNS4xNi5hbGxCQ3MuMkJDcy41QkNzLnBlcmZlY3RzLnJhbmsub3JkZXIuZml0bmVzcy5jb3VudHMucG5nIiwgcGxvdD1wYXRjaDQsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDUsIGhlaWdodCA9IDEyLCB1bml0cyA9ICJpbiIpCmBgYAoKIyMjIyBQZXJmZWN0cyBTY2F0dGVyIFBsb3QgKD41IEJDcykKUGxvdCB0aGUgUGVyZmVjdHMgZml0bmVzcyBzY29yZXMgZm9yIEQwNSB2cy4gRDAzIChMaWIxNSkgb3IgRDEyIHZzLiBEMDQgKExpYjE2KSBiYXNlZCBvbiB0aGUgbnVtYmVyIG9mIHJlY292ZXJlZCBiYXJjb2RlczoKCmBgYHtyfQpCQ19zY2F0dGVyXzVCQ18xNV8xNiA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChkYXRhID0gcGVyZmVjdHNfMTVfMTZfc2hhcmVkXzVCQ3NfZ29vZCwgYWVzKHggPSBudW1wcnVuZWRCQ3MueCwgeSA9IGZpdEQwNUQwMyksIGNvbG9yID0gIiMwMDcyQjIiLCBhbHBoYSA9IDAuOCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHBlcmZlY3RzXzE1XzE2X3NoYXJlZF81QkNzX2dvb2QsIGFlcyh4ID0gbnVtcHJ1bmVkQkNzLnksIHkgPSBmaXREMTJEMDQpLCBjb2xvciA9ICIjRTY5RjAwIiwgYWxwaGEgPSAwLjgpICsKICB4bGFiKCJOdW1iZXIgb2YgQmFyY29kZXMiKSArCiAgeWxhYigiRml0bmVzcyAoTG9nMiBGb2xkIENoYW5nZSkiKSArCiAgZ2d0aXRsZSgiQmFyY29kZSBDb3VudHMgXG4oUGVyZmVjdHMgPjUgQkMpIikgKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZSgKICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMC41KSwKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKQoKQkNfc2NhdHRlcl81QkNfMTVfMTYKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iUGVyZmVjdHMvUExPVFMvTGliMTUuMTYuNUJDcy5nb29kLnBlcmZlY3RzLnNjYXR0ZXIuZml0RDA1RDAzLmZpdEQxMkQwNC5wbmciLCBwbG90PUJDX3NjYXR0ZXJfNUJDXzE1XzE2LAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNy41LCB1bml0cyA9ICJpbiIpCmBgYAoKIyMjIyBQZXJmZWN0cyBEb3NlIFJlc3BvbnNlICg+NSBCQ3MpCgpHYXRoZXIgdGhlIG1lZGlhbiBmaXRuZXNzIG9mIFBlcmZlY3RzIGF0IGVhY2ggVHJpbWV0aG9wcmltIGNvbmNlbnRyYXRpb24gYmFzZWQgb24gZmlsdGVyZWQgMUJDIGRhdGFzZXQuCgoqKkxpYnJhcnkgMTUqKgpgYGB7cn0KIyBGaWx0ZXIgcGVyZmVjdHNfMTVfMTYgZGF0YXNldCBieSBtaW5pbXVtIG9mIG51bXBydW5lZEJDcwpwZXJmZWN0czE1XzVCQ3NkciA8LSBwZXJmZWN0c18xNV8xNl9zaGFyZWRfNUJDc19nb29kICU+JQogIHNlbGVjdChtdXRJRCwgZml0RDA1RDAzLCBmaXREMDZEMDMsIGZpdEQwN0QwMywgZml0RDA4RDAzLCBmaXREMDlEMDMsIGZpdEQxMEQwMywgZml0RDExRDAzLCBudW1wcnVuZWRCQ3MueCkgJT4lIwogIGdhdGhlcigic2VsZWN0aW9uIiwiZml0bmVzcyIsLW11dElELC1udW1wcnVuZWRCQ3MueCkKCiMgQ3JlYXRlIG5hbWluZyBmYWN0b3JzIHRvIGNvbnZlcnQgY29sdW1uIG5hbWVzIGludG8gdHJpbWV0aG9wcmltIGNvbmNlbnRyYXRpb25zIGZvciBwbG90dGluZwpwZXJmZWN0czE1XzVCQ3NkciR0bXBmYWN0b3IgPC0gIjAiCnBlcmZlY3RzMTVfNUJDc2RyJHRtcGZhY3RvcltwZXJmZWN0czE1XzVCQ3NkciRzZWxlY3Rpb249PSAiZml0RDA1RDAzIl0gPC0gIjAiCnBlcmZlY3RzMTVfNUJDc2RyJHRtcGZhY3RvcltwZXJmZWN0czE1XzVCQ3NkciRzZWxlY3Rpb249PSAiZml0RDA2RDAzIl0gPC0gIjAuMDA1OCIKcGVyZmVjdHMxNV81QkNzZHIkdG1wZmFjdG9yW3BlcmZlY3RzMTVfNUJDc2RyJHNlbGVjdGlvbj09ICJmaXREMDdEMDMiXSA8LSAiMC41IgpwZXJmZWN0czE1XzVCQ3NkciR0bXBmYWN0b3JbcGVyZmVjdHMxNV81QkNzZHIkc2VsZWN0aW9uPT0gImZpdEQwOEQwMyJdIDwtICIxIgpwZXJmZWN0czE1XzVCQ3NkciR0bXBmYWN0b3JbcGVyZmVjdHMxNV81QkNzZHIkc2VsZWN0aW9uPT0gImZpdEQwOUQwMyJdIDwtICIxMCIKcGVyZmVjdHMxNV81QkNzZHIkdG1wZmFjdG9yW3BlcmZlY3RzMTVfNUJDc2RyJHNlbGVjdGlvbj09ICJmaXREMTBEMDMiXSA8LSAiNTAiCnBlcmZlY3RzMTVfNUJDc2RyJHRtcGZhY3RvcltwZXJmZWN0czE1XzVCQ3NkciRzZWxlY3Rpb249PSAiZml0RDExRDAzIl0gPC0gIjIwMCIKCnBlcmZlY3RzMTVfNUJDc2RyJHRtcG5hbWVbcGVyZmVjdHMxNV81QkNzZHIkc2VsZWN0aW9uPT0gImZpdEQwNUQwMyJdIDwtICJBIgpwZXJmZWN0czE1XzVCQ3NkciR0bXBuYW1lW3BlcmZlY3RzMTVfNUJDc2RyJHNlbGVjdGlvbj09ICJmaXREMDZEMDMiXSA8LSAiQiIKcGVyZmVjdHMxNV81QkNzZHIkdG1wbmFtZVtwZXJmZWN0czE1XzVCQ3NkciRzZWxlY3Rpb249PSAiZml0RDA3RDAzIl0gPC0gIkMiCnBlcmZlY3RzMTVfNUJDc2RyJHRtcG5hbWVbcGVyZmVjdHMxNV81QkNzZHIkc2VsZWN0aW9uPT0gImZpdEQwOEQwMyJdIDwtICJEIgpwZXJmZWN0czE1XzVCQ3NkciR0bXBuYW1lW3BlcmZlY3RzMTVfNUJDc2RyJHNlbGVjdGlvbj09ICJmaXREMDlEMDMiXSA8LSAiRSIKcGVyZmVjdHMxNV81QkNzZHIkdG1wbmFtZVtwZXJmZWN0czE1XzVCQ3NkciRzZWxlY3Rpb249PSAiZml0RDEwRDAzIl0gPC0gIkYiCnBlcmZlY3RzMTVfNUJDc2RyJHRtcG5hbWVbcGVyZmVjdHMxNV81QkNzZHIkc2VsZWN0aW9uPT0gImZpdEQxMUQwMyJdIDwtICJHIgoKcGVyZmVjdHMxNV81QkNzZHJsYWJzIDwtIGMoIjAiLCIwLjA1OCIsIjAuNSIsIjEiLCIxMCIsIjUwIiwiMjAwIikKYGBgCgojIyMjIyBMaWIxNSBWaW9saW4gUGxvdApgYGB7cn0KVE1QX3Bsb3RfNUJDXzE1IDwtIGdncGxvdChwZXJmZWN0czE1XzVCQ3NkcixhZXMoeD10bXBuYW1lLHk9Zml0bmVzcykpICsgCiAgZ2VvbV92aW9saW4oZmlsbCA9ICIjMDA3MkIyIiwgYWxwaGE9MC43NSkgKwogIGdlb21fYm94cGxvdCh3aWR0aD0wLjEpICsKICB4bGFiKCJUcmltZXRob3ByaW0gKHVnL21MKSIpICsKICB5bGFiKCJNZWRpYW4gRml0bmVzcyIpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz0gcGVyZmVjdHMxNV81QkNzZHJsYWJzKSArCiAgZ2d0aXRsZSgiTGlicmFyeSAxNSBEb3NlIFJlc3BvbnNlIFxuKFBlcmZlY3RzID41IEJDKSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMC41KSwgCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjE2LCAxKSwKICAgICAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9IGMoMSwgMSksCiAgICAgICAgbGVnZW5kLmJveC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJibGFjayIpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoLTEyLDgpKQoKVE1QX3Bsb3RfNUJDXzE1CmBgYAoKYGBge3IgZWNobz1GQUxTRX0KIyBMaWIxNQpnZ3NhdmUoZmlsZT0iUGVyZmVjdHMvUExPVFMvTGliMTUuNUJDcy5nb29kLnBlcmZlY3RzLmRvc2UucmVzcG9uc2UuZGF5LjEudmlvbGluLnBuZyIsIHBsb3Q9VE1QX3Bsb3RfNUJDXzE1LAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNy41LCB1bml0cyA9ICJpbiIpCmBgYAoKIyMjIyMgTGliMTYgVmlvbGluIFBsb3QKYGBge3J9CiMgRmlsdGVyIHBlcmZlY3RzMTZfNUJDcyBkYXRhc2V0IGJ5IG1pbmltdW0gb2YgbnVtcHJ1bmVkQkNzCnBlcmZlY3RzMTZfNUJDc2RyIDwtIHBlcmZlY3RzXzE1XzE2X3NoYXJlZF81QkNzX2dvb2QgJT4lCiAgI2ZpbHRlcihudW1wcnVuZWRCQ3M+MTApICU+JQogIHNlbGVjdChtdXRJRCwgZml0RDEyRDA0LCBmaXRFMDFEMDQsIGZpdEUwMkQwNCwgZml0RTAzRDA0LCBmaXRFMDREMDQsIGZpdEUwNUQwNCwgZml0RTA2RDA0LCBudW1wcnVuZWRCQ3MueSkgJT4lIwogIGdhdGhlcigic2VsZWN0aW9uIiwiZml0bmVzcyIsLW11dElELC1udW1wcnVuZWRCQ3MueSkKCiMgQ3JlYXRlIG5hbWluZyBmYWN0b3JzIHRvIGNvbnZlcnQgY29sdW1uIG5hbWVzIGludG8gdHJpbWV0aG9wcmltIGNvbmNlbnRyYXRpb25zIGZvciBwbG90dGluZwpwZXJmZWN0czE2XzVCQ3NkciR0bXBmYWN0b3IgPC0gIjAiCnBlcmZlY3RzMTZfNUJDc2RyJHRtcGZhY3RvcltwZXJmZWN0czE2XzVCQ3NkciRzZWxlY3Rpb249PSAiZml0RDEyRDA0Il0gPC0gIjAiCnBlcmZlY3RzMTZfNUJDc2RyJHRtcGZhY3RvcltwZXJmZWN0czE2XzVCQ3NkciRzZWxlY3Rpb249PSAiZml0RTAxRDA0Il0gPC0gIjAuMDA1OCIKcGVyZmVjdHMxNl81QkNzZHIkdG1wZmFjdG9yW3BlcmZlY3RzMTZfNUJDc2RyJHNlbGVjdGlvbj09ICJmaXRFMDJEMDQiXSA8LSAiMC41IgpwZXJmZWN0czE2XzVCQ3NkciR0bXBmYWN0b3JbcGVyZmVjdHMxNl81QkNzZHIkc2VsZWN0aW9uPT0gImZpdEUwM0QwNCJdIDwtICIxIgpwZXJmZWN0czE2XzVCQ3NkciR0bXBmYWN0b3JbcGVyZmVjdHMxNl81QkNzZHIkc2VsZWN0aW9uPT0gImZpdEUwNEQwNCJdIDwtICIxMCIKcGVyZmVjdHMxNl81QkNzZHIkdG1wZmFjdG9yW3BlcmZlY3RzMTZfNUJDc2RyJHNlbGVjdGlvbj09ICJmaXRFMDVEMDQiXSA8LSAiNTAiCnBlcmZlY3RzMTZfNUJDc2RyJHRtcGZhY3RvcltwZXJmZWN0czE2XzVCQ3NkciRzZWxlY3Rpb249PSAiZml0RTA2RDA0Il0gPC0gIjIwMCIKCnBlcmZlY3RzMTZfNUJDc2RyJHRtcG5hbWVbcGVyZmVjdHMxNl81QkNzZHIkc2VsZWN0aW9uPT0gImZpdEQxMkQwNCJdIDwtICJBIgpwZXJmZWN0czE2XzVCQ3NkciR0bXBuYW1lW3BlcmZlY3RzMTZfNUJDc2RyJHNlbGVjdGlvbj09ICJmaXRFMDFEMDQiXSA8LSAiQiIKcGVyZmVjdHMxNl81QkNzZHIkdG1wbmFtZVtwZXJmZWN0czE2XzVCQ3NkciRzZWxlY3Rpb249PSAiZml0RTAyRDA0Il0gPC0gIkMiCnBlcmZlY3RzMTZfNUJDc2RyJHRtcG5hbWVbcGVyZmVjdHMxNl81QkNzZHIkc2VsZWN0aW9uPT0gImZpdEUwM0QwNCJdIDwtICJEIgpwZXJmZWN0czE2XzVCQ3NkciR0bXBuYW1lW3BlcmZlY3RzMTZfNUJDc2RyJHNlbGVjdGlvbj09ICJmaXRFMDREMDQiXSA8LSAiRSIKcGVyZmVjdHMxNl81QkNzZHIkdG1wbmFtZVtwZXJmZWN0czE2XzVCQ3NkciRzZWxlY3Rpb249PSAiZml0RTA1RDA0Il0gPC0gIkYiCnBlcmZlY3RzMTZfNUJDc2RyJHRtcG5hbWVbcGVyZmVjdHMxNl81QkNzZHIkc2VsZWN0aW9uPT0gImZpdEUwNkQwNCJdIDwtICJHIgoKcGVyZmVjdHMxNl81QkNzZHJsYWJzIDwtIGMoIjAiLCIwLjA1OCIsIjAuNSIsIjEiLCIxMCIsIjUwIiwiMjAwIikKYGBgCgoqKkxpYjE2IFZpb2xpbiBQbG90OioqIGJhc2VkIG9uIGZpbHRlcmVkIDVCQyBkYXRhc2V0OgpgYGB7cn0KVE1QX3Bsb3RfNUJDXzE2IDwtIGdncGxvdChwZXJmZWN0czE2XzVCQ3NkcixhZXMoeD10bXBuYW1lLHk9Zml0bmVzcykpICsgCiAgZ2VvbV92aW9saW4oZmlsbCA9ICIjRTY5RjAwIikgKwogIGdlb21fYm94cGxvdCh3aWR0aD0wLjEpICsKICB4bGFiKCJUcmltZXRob3ByaW0gKHVnL21MKSIpICsKICB5bGFiKCJNZWRpYW4gRml0bmVzcyIpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz0gcGVyZmVjdHMxNl81QkNzZHJsYWJzKSArCiAgZ2d0aXRsZSgiTGlicmFyeSAxNiBEb3NlIFJlc3BvbnNlIFxuKFBlcmZlY3RzID41IEJDKSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMC41KSwgCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjE2LCAxKSwKICAgICAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9IGMoMSwgMSksCiAgICAgICAgbGVnZW5kLmJveC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJibGFjayIpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoLTEyLDgpKQoKVE1QX3Bsb3RfNUJDXzE2CmBgYAoKYGBge3IgZWNobz1GQUxTRX0KIyBMaWIxNgpnZ3NhdmUoZmlsZT0iUGVyZmVjdHMvUExPVFMvTGliMTYuNUJDcy5nb29kLnBlcmZlY3RzLmRvc2UucmVzcG9uc2UuZGF5LjEudmlvbGluLnBuZyIsIHBsb3Q9VE1QX3Bsb3RfNUJDXzE2LAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNy41LCB1bml0cyA9ICJpbiIpCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KcGF0Y2g1IDwtIChUTVBfcGxvdF81QkNfMTUgfCBUTVBfcGxvdF81QkNfMTYpCnBhdGNoNQpgYGAKCmBgYHtyIGVjaG89RkFMU0V9Cmdnc2F2ZShmaWxlPSJQZXJmZWN0cy9QTE9UUy9MaWIxNS4xNi41QkNzLmdvb2QucGVyZmVjdHMuZG9zZS5yZXNwb25zZS5kYXkuMS52aW9saW4ucG5nIiwgcGxvdD1wYXRjaDUsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA0LCB1bml0cyA9ICJpbiIpCmBgYAoKIyMjIyMgQm90aCBMaWIgQm94cGxvdAoKQ29tYmluZSB0aGUgdHdvIGRhdGEgZnJhbWVzOgpgYGB7cn0KcGVyZmVjdHNfMTVfMTZfNUJDc2RyIDwtIGJpbmRfcm93cygKICBwZXJmZWN0czE1XzVCQ3NkciAlPiUgbXV0YXRlKExpYiA9ICJMaWIxNSIpLCAjIFZhbHVlcyBkZXJpdmVkIGZyb20gTGliMTUgcGVyZmVjdHNfMTVfMTZfc2hhcmVkXzVCQ3MKICBwZXJmZWN0czE2XzVCQ3NkciAlPiUgbXV0YXRlKExpYiA9ICJMaWIxNiIpLCAjIFZhbHVlcyBkZXJpdmVkIGZyb20gTGliMTYgcGVyZmVjdHNfMTVfMTZfc2hhcmVkXzVCQ3MKICAuaWQgPSAiaWQiKQoKcGVyZmVjdHNfMTVfMTZfNUJDc2RybGFicyA8LSBjKCIwIiwiMC4wNTgiLCIwLjUiLCIxIiwiMTAiLCI1MCIsIjIwMCIpCmBgYAoKUGxvdCB0aGUgbWVkaWFuIGZpdG5lc3Mgb2YgUGVyZmVjdHMgYXQgZWFjaCBUcmltZXRob3ByaW0gY29uY2VudHJhdGlvbiBmb3IgRGF5IDEgYmFzZWQgb24gZmlsdGVyZWQgNUJDIGRhdGFzZXQ6CmBgYHtyfQpwZXJmZWN0c18xNV8xNl81QkNzZHJfcGxvdCA8LSBnZ3Bsb3QocGVyZmVjdHNfMTVfMTZfNUJDc2RyLCBhZXMoeCA9IHRtcG5hbWUsIHkgPSBmaXRuZXNzLCBmaWxsID0gTGliKSkgKwogICNnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAwLjUpICsKICBnZW9tX2JveHBsb3QocG9zaXRpb24gPSAiZG9kZ2UiLCBhbHBoYT0wLjgpICsKICB4bGFiKCJUcmltZXRob3ByaW0gKHVnL21MKSIpICsKICB5bGFiKGV4cHJlc3Npb24ocGFzdGUoIk1lZGlhbiBGaXRuZXNzIChMb2dGQykiKSkpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IHBlcmZlY3RzXzE1XzE2XzVCQ3NkcmxhYnMpICsKICAjZ2d0aXRsZSgiTWVkaWFuIEZpdG5lc3MgXG5QZXJmZWN0cyAoPjVCQ3MpIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2JsYWNrJywgc2l6ZSA9IDEuMCksCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAxLjApLAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKC0xMiwgOCkpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJMaWIxNSIgPSAiIzAwNzJCMiIsICJMaWIxNiIgPSAiI0U2OUYwMCIpLAogICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkNvZG9uMSIsICJDb2RvbjIiKSkKCiMgRGlzcGxheSB0aGUgcGxvdApwZXJmZWN0c18xNV8xNl81QkNzZHJfcGxvdApgYGAKCmBgYHtyIGVjaG89RkFMU0V9Cmdnc2F2ZShmaWxlPSJQZXJmZWN0cy9QTE9UUy9MaWIxNS4xNi41QkNzLnBlcmZlY3RzLmRvc2UucmVzcG9uc2UuZGF5LjEuYm94cGxvdC5wbmciLCBwbG90PXBlcmZlY3RzXzE1XzE2XzVCQ3Nkcl9wbG90LAogICAgICAgZHBpPTYwMCwgd2lkdGggPSA2LCBoZWlnaHQgPSA0LCB1bml0cyA9ICJpbiIpCmBgYAoKIyMgREhGUiBHZW5lcwpMb2FkIGluIHRoZSBvcmdhbmlzbSBkYXRhc2V0IGFzc29jaWF0ZWQgd2l0aCBESEZSIGdlbmVzIGZvciBtYXBwaW5nIHRheG9ub215IHRvIHRoZSBwaHlsb2dlbmV0aWMgdHJlZS4KYGBge3J9CiNsb2FkIGluIGFsbCB0aGUgb3JnYW5pc21zIGluZm8gZm9yIHVuaXF1ZSBCQ3MgYXNzb2NpYXRlZCB3aXRoIERIRlIgZ2VuZXM6IApvcmdpbmZvID0gcmVhZC5jc3YoIlBlcmZlY3RzL0lOUFVUL0RIRlJfY2hpcF8zX3Byb3RlaW5zX2FkZGVkc2V2ZXJhbC5jc3YiLCBoZWFkPVRSVUUpICAjIHJlYWQgY3N2IGZpbGUKYGBgCgpTdWJzZXQgdGhlIGRhdGFzZXQ6CmBgYHtyfQpvcmdpbmZvIDwtIG9yZ2luZm8gJT4lCiAgc2VsZWN0KC1MaWIuY29kb24xLCAtTGliLmNvZG9uMiwgLUNvbnN0cnVjdC4sIC1CYXJjb2RlKSAlPiUKICBkcGx5cjo6cmVuYW1lKElEZnVsbD1BY2Nlc3Npb24pICU+JQogICNtdXRhdGUoSUQ9c3Ryc3BsaXQoSURmdWxsLCBzcGxpdCA9ICIuIilbMV0pCiAgdGlkeXI6OnNlcGFyYXRlKElEZnVsbCwgYygiSUQiLCAidmVyIiksICJcXC4iLHJlbW92ZSA9IEZBTFNFKSAlPiUKICBzZWxlY3QoLXZlcikKbmFtZXMob3JnaW5mbykKYGBgCgpBZGQgdGhlIG9yZ2FuaXNtIGluZm8gdG8gdGhlIHBlcmZlY3RzIGluZm8gYXMgYSBuZXcgb2JqZWN0IGNhbGxlZCAicGVyZmVjdHNfdHJlZSI6CmBgYHtyfQojIE1ha2UgYSBjb3B5IG9mICJtdXRJRCIgY29sdW1uIGNhbGxlZCAiSUQiIGZvciBtYXRjaGluZyB3aXRoIG9yZ2luZm8gb2JqZWN0OgpwZXJmZWN0c18xNV8xNl9zaGFyZWRfNUJDcyA8LSBwZXJmZWN0c18xNV8xNl9zaGFyZWRfNUJDcyAlPiUKICBtdXRhdGUoSUQgPSBtdXRJRCkKCiMgQWRkIG9yZ2luZm8gdG8gcGVyZmVjdHNfNUJDc190cmVlCnBlcmZlY3RzXzE1XzE2XzVCQ3NfdHJlZSA8LSByaWdodF9qb2luKG9yZ2luZm8scGVyZmVjdHNfMTVfMTZfc2hhcmVkXzVCQ3MsYnk9IklEIikgJT4lCiAgc2VsZWN0KC1JRGZ1bGwsLUlELngsLXNlcS54LC1wY3RfaWRlbnQueCwtSUQueSwtc2VxLnksLXBjdF9pZGVudC55KQpgYGAKCiMjIyBQZXJmZWN0cyAlIFNpbWlsYXIgRS4gY29saSBQbG90CgojIyMjIFNjYXR0ZXIgUGxvdCAoPjUgQkNzKQoKUGxvdCB0aGUgUGVyZmVjdHMgZml0bmVzcyBzY29yZXMgZm9yIEQwNSB2cy4gRDAzIChMaWIxNSkgb3IgRDEyIHZzLiBEMDQgKExpYjE2KSBiYXNlZCBvbiBwZXJjZW50IHNpbWlsYXJpdHkgb2YgdGhlIGVhY2ggdmFyaWFudCdzIGdlbmUgc2VxdWVuY2VzIHRvIHRoZSBFLiBjb2xpIERIRlIgZ2VuZSBzZXF1ZW5jZToKYGBge3J9CnBlcmZlY3RzXzE1XzE2XzVCQ3Nfc2NhdHRlcl9wbG90IDwtIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBwZXJmZWN0c18xNV8xNl81QkNzX3RyZWUsIGFlcyh4ID0gUGN0SWRlbnRFY29saSwgeSA9IGZpdEQwNUQwMyksIGNvbG9yID0gIiMwMDcyQjIiLCBhbHBoYSA9IDAuOCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHBlcmZlY3RzXzE1XzE2XzVCQ3NfdHJlZSwgYWVzKHggPSBQY3RJZGVudEVjb2xpLCB5ID0gZml0RDEyRDA0KSwgY29sb3IgPSAiI0U2OUYwMCIsIGFscGhhID0gMC44KSArCiAgeGxhYigiUGVyY2VudCBTaW1pbGFyaXR5IHRvIEUuIGNvbGkgREhGUiIpICsKICB5bGFiKCJGaXRuZXNzIChMb2cyIEZvbGQgQ2hhbmdlKSIpICsKICBnZ3RpdGxlKCJNZWRpYW4gRml0bmVzcyBiYXNlZCBvbiBcbiUgU2ltaWxhcml0eSB0byBFLiBjb2xpIikgKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZSgKICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMC41KSwKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSAjKwogICNzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSwgbGltaXRzID0gYygtNywgMSkpCgpwZXJmZWN0c18xNV8xNl81QkNzX3NjYXR0ZXJfcGxvdApgYGAKCiMjIyMgQmlubmVkIFBsb3QgKD41IEJDcykKCkJpbiB0aGUgJSBTaW1pbGFyaXR5IHZhbHVlcyBpbiAxMCUgaW5jcmVtZW50cyBhbmQgcmVwbG90IGFzIGJveHBsb3RzOgpgYGB7cn0KIyBDb252ZXJ0IGRhdGEgdG8gbG9uZyBmb3JtYXQKZGF0YV9sb25nIDwtIHBlcmZlY3RzXzE1XzE2XzVCQ3NfdHJlZSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IGMoZml0RDA1RDAzLCBmaXREMTJEMDQpLCBuYW1lc190byA9ICJ2YXJpYWJsZSIsIHZhbHVlc190byA9ICJ2YWx1ZSIpCgojIERlZmluZSBuZXcgeC1heGlzIGxhYmVscwpuZXdfeF9sYWJlbHMgPC0gYygiMzAtNDAlIiwgIjQwLTUwJSIsICI1MC02MCUiLCAiNjAtNzAlIiwgIjcwLTgwJSIsICI4MC05MCUiLCAiOTAtMTAwJSIpCgpwZXJmZWN0c18xNV8xNl81QkNzX2Jpbm5lZF9wbG90IDwtIGdncGxvdChkYXRhX2xvbmcsIGFlcyh4ID0gY3V0KFBjdElkZW50RWNvbGksIGJyZWFrcyA9IHNlcSgwLCAxLjAsIGJ5ID0gMC4xKSksIHkgPSB2YWx1ZSwgZmlsbCA9IHZhcmlhYmxlKSkgKwogIGdlb21fYm94cGxvdChwb3NpdGlvbiA9ICJkb2RnZSIsIGFscGhhID0gMC44KSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLTEsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsKICB4bGFiKCJQZXJjZW50IFNpbWlsYXJpdHkgdG8gRS4gY29saSBESEZSIChCaW5uZWQpIikgKwogIHlsYWIoIk1lZGlhbiBGaXRuZXNzIFxuKExvZzIgRm9sZCBDaGFuZ2UpIikgKwogIGdndGl0bGUoIlBlcmNlbnQgU2ltaWxhcml0eSB0byBFLiBjb2xpIFxuKFBlcmZlY3RzID41IEJDKSIpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IG5ld194X2xhYmVscykgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2JsYWNrJywgc2l6ZSA9IDAuNSksCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpLAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiMwMDcyQjIiLCAiI0U2OUYwMCIpLCBsYWJlbHMgPSBjKCJDb2RvbiAxIiwgIkNvZG9uIDIiKSkKCnBlcmZlY3RzXzE1XzE2XzVCQ3NfYmlubmVkX3Bsb3QKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIFNjYXR0ZXJwbG90Cmdnc2F2ZShmaWxlPSJQZXJmZWN0cy9QTE9UUy9MaWIxNS4xNi41QkNzLnBlcmZlY3RzLnBjdHNpbS52cy5maXREMDVEMDMuZml0RDEyRDA0LnNjYXR0ZXIucG5nIiwKICAgICAgIHBsb3Q9cGVyZmVjdHNfMTVfMTZfNUJDc19zY2F0dGVyX3Bsb3QsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA3LjUsIHVuaXRzID0gImluIikKCiMgQm94cGxvdApnZ3NhdmUoZmlsZT0iUGVyZmVjdHMvUExPVFMvTGliMTUuMTYuNUJDcy5wZXJmZWN0cy5wY3RzaW0udnMuZml0RDA1RDAzLmZpdEQxMkQwNC5ib3hwbG90LnBuZyIsCiAgICAgICBwbG90PXBlcmZlY3RzXzE1XzE2XzVCQ3NfYmlubmVkX3Bsb3QsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA3LjUsIHVuaXRzID0gImluIikKYGBgCgpDYWxjdWxhdGUgdGhlIG1lZGlhbiBwZXJjZW50IHNpbWlsYXJpdHkgdG8gRS4gY29saSBhY3Jvc3MgYWxsIGhvbW9sb2dzICg1IEJDcyk6CmBgYHtyfQojIE1ha2UgYSBjb3B5IG9mICJtdXRJRCIgY29sdW1uIGNhbGxlZCAiSUQiIGZvciBtYXRjaGluZyB3aXRoIG9yZ2luZm8gb2JqZWN0OgpwZXJmZWN0czE1XzVCQ3NfUGN0SWRlbnRFY29saSA8LSBwZXJmZWN0czE1XzVCQ3MKCiMgQWRkIG9yZ2luZm8gdG8gcGVyZmVjdHNfNUJDc190cmVlCnBlcmZlY3RzMTVfNUJDc190cmVlIDwtIHJpZ2h0X2pvaW4ob3JnaW5mbyxwZXJmZWN0czE1XzVCQ3NfUGN0SWRlbnRFY29saSxieT0iSUQiKSAjJT4lCiAgI3NlbGVjdCgtSURmdWxsLC1JRC54LC1zZXEueCwtcGN0X2lkZW50LngsLUlELnksLXNlcS55LC1wY3RfaWRlbnQueSkKYGBgCgpgYGB7cn0KIyBDYWxjdWxhdGUgbWVhbiBhbmQgbWVkaWFuIFBjdElkZW50RWNvbGkgdmFsdWUgYWNyb3NzIGFsbCBob21vbG9ncyBmb3IgTGliMTUKbWVhbihwZXJmZWN0czE1XzVCQ3NfdHJlZSRQY3RJZGVudEVjb2xpKQptZWRpYW4ocGVyZmVjdHMxNV81QkNzX3RyZWUkUGN0SWRlbnRFY29saSkKYGBgCgoKCiMjIyBQZXJmZWN0cyBGaXRuZXNzIFJpZGdlIFBsb3RzCgoqKlRoaXMgc2VjdGlvbiB1c2VzIHRoZSBgbGlicmFyeShnZ3JpZGdlcylgIHBhY2thZ2UuKioKClN1YnNldCB0aGUgcGVyZmVjdHNfdHJlZV81QkNzIG9iamVjdCB0byByZXRhaW4gb25seSAiSUQiIGFuZCBmaXRuZXNzIHNjb3JlcyBmb3Igc2VsZWN0IGNvbmRpdGlvbnMuIFRoaXMgY29kZSB3aWxsIHRyYW5zcG9zZSB0aGUgaW5kaXZpZHVhbCBmaXRuZXNzIHNjb3JlIGNvbHVtbnMgaW50byB0d28gY29sdW1ucyB3aXRoIHRoZSBmaXRuZXNzIGxhYmVsIGFuZCB2YWx1ZXMsIHJlc3BlY3RpdmVseSwgZm9yIGVhY2ggdW5pcXVlIElELgoKIyMjIyBEYXkgMSAoPjUgQkNzKQpgYGB7cn0KIyBMaWIxNQpwZXJmZWN0czE1X3RyZWVfNUJDc19kMSA8LSBwZXJmZWN0c18xNV8xNl81QkNzX3RyZWUgJT4lCiAgc2VsZWN0KElELCBmaXREMDVEMDMsIGZpdEQwNkQwMywgZml0RDA3RDAzLCBmaXREMDhEMDMsIGZpdEQwOUQwMywgZml0RDEwRDAzLCBmaXREMTFEMDMpICU+JQogIHBpdm90X2xvbmdlcighSUQsIG5hbWVzX3RvID0gImZjIiwgdmFsdWVzX3RvID0gInZhbCIpICU+JQogIG11dGF0ZShUTVAgPSBjYXNlX3doZW4oCiAgICBmYyA9PSAiZml0RDA1RDAzIiB+ICIwLVRNUCIsCiAgICBmYyA9PSAiZml0RDA2RDAzIiB+ICIwLjA1OC1UTVAiLAogICAgZmMgPT0gImZpdEQwN0QwMyIgfiAiMC41LVRNUCIsCiAgICBmYyA9PSAiZml0RDA4RDAzIiB+ICIxLjAtVE1QIiwKICAgIGZjID09ICJmaXREMDlEMDMiIH4gIjEwLVRNUCIsCiAgICBmYyA9PSAiZml0RDEwRDAzIiB+ICI1MC1UTVAiLAogICAgZmMgPT0gImZpdEQxMUQwMyIgfiAiMjAwLVRNUCIsCiAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXykpCgojIExpYjE2CnBlcmZlY3RzMTZfdHJlZV81QkNzX2QxIDwtIHBlcmZlY3RzXzE1XzE2XzVCQ3NfdHJlZSAlPiUKICBzZWxlY3QoSUQsIGZpdEQxMkQwNCwgZml0RTAxRDA0LCBmaXRFMDJEMDQsIGZpdEUwM0QwNCwgZml0RTA0RDA0LCBmaXRFMDVEMDQsIGZpdEUwNkQwNCkgJT4lCiAgcGl2b3RfbG9uZ2VyKCFJRCwgbmFtZXNfdG8gPSAiZmMiLCB2YWx1ZXNfdG8gPSAidmFsIikgJT4lCiAgbXV0YXRlKFRNUCA9IGNhc2Vfd2hlbigKICAgIGZjID09ICJmaXREMTJEMDQiIH4gIjAtVE1QIiwKICAgIGZjID09ICJmaXRFMDFEMDQiIH4gIjAuMDU4LVRNUCIsCiAgICBmYyA9PSAiZml0RTAyRDA0IiB+ICIwLjUtVE1QIiwKICAgIGZjID09ICJmaXRFMDNEMDQiIH4gIjEuMC1UTVAiLAogICAgZmMgPT0gImZpdEUwNEQwNCIgfiAiMTAtVE1QIiwKICAgIGZjID09ICJmaXRFMDVEMDQiIH4gIjUwLVRNUCIsCiAgICBmYyA9PSAiZml0RTA2RDA0IiB+ICIyMDAtVE1QIiwKICAgIFRSVUUgfiBOQV9jaGFyYWN0ZXJfKSkKYGBgCgpgYGB7cn0KIyBDb21iaW5lIHRoZSB0d28gZGF0YSBmcmFtZXMKcGVyZmVjdHNfMTVfMTZfdHJlZV81QkNzX2QxIDwtIGJpbmRfcm93cygKICBwZXJmZWN0czE1X3RyZWVfNUJDc19kMSAlPiUgbXV0YXRlKExpYiA9ICJMaWIxNSIpLAogIHBlcmZlY3RzMTZfdHJlZV81QkNzX2QxICU+JSBtdXRhdGUoTGliID0gIkxpYjE2IiksCiAgLmlkID0gImlkIikKYGBgCgpQbG90IFBlcmZlY3RzIGZpdG5lc3Mgc2NvcmVzIGJhc2VkIG9uIFN1cHBsZW1lbnRhdGlvbiB0cmVhdG1lbnQgZm9yIGZpcnN0IHNhbXBsaW5nIHRpbWUgcG9pbnQKYGBge3J9CnBlcmZlY3RzXzE1XzE2X3RyZWVfNUJDc19kMV9vcmRlciA8LSBjKCIyMDAtVE1QIiwgIjUwLVRNUCIsICIxMC1UTVAiLCAiMS4wLVRNUCIsICIwLjUtVE1QIiwgIjAuMDU4LVRNUCIsICIwLVRNUCIpCgojIFJlbmFtZSB0aGUgbGV2ZWxzIGluIHRoZSBMaWIgY29sdW1uCnBlcmZlY3RzXzE1XzE2X3RyZWVfNUJDc19kMSRMaWIgPC0gZmFjdG9yKHBlcmZlY3RzXzE1XzE2X3RyZWVfNUJDc19kMSRMaWIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkxpYjE1IiwgIkxpYjE2IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkNvZG9uMSIsICJDb2RvbjIiKSkKCnRtcF9yaWRnZXNfMTVfMTZfZDEgPC0gZ2dwbG90KHBlcmZlY3RzXzE1XzE2X3RyZWVfNUJDc19kMSwgYWVzKHggPSB2YWwsIHkgPSBmYWN0b3IoVE1QLCBsZXZlbCA9IHBlcmZlY3RzXzE1XzE2X3RyZWVfNUJDc19kMV9vcmRlciksIGZpbGwgPSBMaWIpKSArCiAgZ2VvbV9kZW5zaXR5X3JpZGdlcyhhbHBoYSA9IDAuNykgKwogIHNjYWxlX3lfZGlzY3JldGUobGFiZWxzID0gYygnMjAwIM68Zy9tTCBUTVAnLCAnNTAgzrxnL21MIFRNUCcsICcxMCDOvGcvbUwgVE1QJywgJzEgzrxnL21MIFRNUCcsICcwLjUgzrxnL21MIFRNUCcsICcwLjA1OCDOvGcvbUwgVE1QJywgJ0NvbXBsZW1lbnRhdGlvbicpKSArCiAgeGxhYigiTWVkaWFuIEZpdG5lc3MgKExvZ0ZDKSIpICsKICB5bGFiKCJTZWxlY3Rpb24gQ29uZGl0aW9uICh1Zy9tTCBUTVApIikgKwogICNnZ3RpdGxlKCJEb3NlIFJlc3BvbnNlIFxuRGF5IDEiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZSgKICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMC41KSwKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKC0xNSwgMTApKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiQ29kb24xIiA9ICIjMDA3MkIyIiwgIkNvZG9uMiIgPSAiI0U2OUYwMCIpLCBuYW1lID0gIkxpYnJhcnkiKQoKIyBEaXNwbGF5IHRoZSBwbG90CnRtcF9yaWRnZXNfMTVfMTZfZDEKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iUGVyZmVjdHMvUExPVFMvTGliMTUuMTYuNUJDcy5wZXJmZWN0cy5kb3NlLnJlc3BvbnNlLnJpZGdlcy5kYXkuMS5wbmciLCBwbG90PXRtcF9yaWRnZXNfMTVfMTZfZDEsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDYsIGhlaWdodCA9IDgsIHVuaXRzID0gImluIikKYGBgCgpgYGB7cn0KcGF0Y2g2IDwtIChwZXJmZWN0c18xNV8xNl81QkNzX2Jpbm5lZF9wbG90IC8gcGVyZmVjdHNfMTVfMTZfNUJDc2RyX3Bsb3QpIHwgKHRtcF9yaWRnZXNfMTVfMTZfZDEpCnBhdGNoNgpgYGAKCmBgYHtyIGVjaG89RkFMU0V9Cmdnc2F2ZShmaWxlPSJQZXJmZWN0cy9QTE9UUy9MaWIxNS4xNi41QkNzLnBlcmZlY3RzLmRvc2UucmVzcG9uc2UucGN0c2ltLnZzLmZpdC5yaWRnZXMucG5nIiwKICAgICAgIHBsb3Q9cGF0Y2g2LAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAyMCwgaGVpZ2h0ID0gMTIsIHVuaXRzID0gImluIikKYGBgCgojIyBQZXJmZWN0cyBTdW1tYXJ5CgpQbG90IHRoZSBtYXBwZWQgcGVyZmVjdHMgZm9yIGVhY2ggbGlicmFyeSBhbmQgd2hlbiB0aGUgc2hhcmVkIHBlcmZlY3RzIGFyZSBjb21iaW5lZCB3aXRoIHRoZSB1bmlxdWUgcGVyZmVjdHM6CmBgYHtyfQojIENyZWF0ZSB0aGUgZGF0YSBmcmFtZQptYXBwZWQuZGF0YSA8LSBkYXRhLmZyYW1lKAogIExpYnJhcnkgPSBjKCJDb2RvbjEiLCAiQ29kb24yIiwgIkJvdGgiKSwKICBQZXJmZWN0cyA9IGMoMTA0OCwgOTA0LCAxMjA4KSwKICBQZXJjZW50ID0gYyg2OC4yLCA1OC45LCA3OC43KQopCgojIENyZWF0ZSBhIGdncGxvdCBvYmplY3QKTDE1LkwxNi4xMjA4Lk1hcHBlZC5CYXJjaGFydCA8LSBnZ3Bsb3QobWFwcGVkLmRhdGEpICsKICAjIEFkZCBhIGhvcml6b250YWwgbGluZSBhdCB5PTAKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBjb2xvciA9ICJibGFjayIpICsKICAjIFdpZGVyIGJhciBmb3IgIkJvdGgiIHBsb3R0ZWQgYmVoaW5kCiAgZ2VvbV9iYXIoZGF0YSA9IHN1YnNldChtYXBwZWQuZGF0YSwgTGlicmFyeSA9PSAiQm90aCIpLAogICAgICAgICAgIGFlcyh4ID0gMS41LCB5ID0gUGVyZmVjdHMsIGZpbGwgPSBMaWJyYXJ5KSwKICAgICAgICAgICBzdGF0ID0gImlkZW50aXR5IiwgCiAgICAgICAgICAgd2lkdGggPSAwLjc1LCAKICAgICAgICAgICBhbHBoYSA9IDAuNSkgKwogICMgQmFyIHBsb3QgZm9yIENvZG9uMSBhbmQgQ29kb24yCiAgZ2VvbV9iYXIoZGF0YSA9IHN1YnNldChtYXBwZWQuZGF0YSwgTGlicmFyeSAhPSAiQm90aCIpLAogICAgICAgICAgIGFlcyh4ID0gYXMubnVtZXJpYyhmYWN0b3IoTGlicmFyeSwgbGV2ZWxzID0gYygiQ29kb24xIiwgIkNvZG9uMiIpKSksIHkgPSBQZXJmZWN0cywgZmlsbCA9IExpYnJhcnkpLCAKICAgICAgICAgICBzdGF0ID0gImlkZW50aXR5IiwgCiAgICAgICAgICAgd2lkdGggPSAwLjYpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYygxLCAyKSwgbGFiZWxzID0gYygiQ29kb24xIiwgIkNvZG9uMiIpLCAKICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYygwLjUsIDIuNSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoCiAgICBuYW1lID0gIkFzc2VtYmxpZXMgUmVwcmVzZW50ZWQgXG4oMTUzNiBEZXNpZ25zKSIsIAogICAgbGltaXRzID0gYygwLCAxNTM2KSwKICAgIGV4cGFuZCA9IGMoMCwgMCksICAjIFJlbW92ZSBwYWRkaW5nCiAgICBzZWMuYXhpcyA9IHNlY19heGlzKH4gLiAqIDEwMCAvIDE1MzYsIG5hbWUgPSAiTGlicmFyeSBSZXByZXNlbnRhdGlvbiAoJSkiLCBicmVha3MgPSBzZXEoMCwgMTAwLCBieSA9IDIwKSkKICApICsKICAjIEFkZCBhbiBpbnZpc2libGUgbGF5ZXIgdG8gZW5zdXJlIHRoZSBzZWNvbmRhcnkgeS1heGlzIGlzIHBsb3R0ZWQKICBnZW9tX3BvaW50KGRhdGEgPSBzdWJzZXQobWFwcGVkLmRhdGEsIExpYnJhcnkgIT0gIkJvdGgiKSwKICAgICAgICAgICAgIGFlcyh4ID0gYXMubnVtZXJpYyhmYWN0b3IoTGlicmFyeSwgbGV2ZWxzID0gYygiQ29kb24xIiwgIkNvZG9uMiIpKSksIHkgPSBQZXJjZW50ICogMTUzNiAvIDEwMCksIAogICAgICAgICAgICAgY29sb3IgPSBOQSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoCiAgICBheGlzLnRpdGxlLnkucmlnaHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAicmVkIiwgc2l6ZSA9IDE0KSwKICAgIGF4aXMudGV4dC55LnJpZ2h0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gInJlZCIsIHNpemUgPSAxMiksCiAgICBheGlzLnRpY2tzLnkucmlnaHQgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAicmVkIiksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yID0gImJsYWNrIiwgc2l6ZT0xLjApLAogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJibGFjayIsIHNpemU9MS4wKSwKICAgIGF4aXMubGluZS54LnRvcCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueS5sZWZ0ID0gZWxlbWVudF9saW5lKGNvbG9yID0gImJsYWNrIiksCiAgICBheGlzLnRpY2tzLmxlbmd0aCA9IHVuaXQoMC4yLCAiY20iKQogICkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkNvZG9uMSIgPSAiIzAwNzJCMiIsICJDb2RvbjIiID0gIiNFNjlGMDAiLCAiQm90aCIgPSAibGlnaHRibHVlNCIpLAogICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoIkNvZG9uMSIsICJDb2RvbjIiLCAiQm90aCIpKSArCiAgbGFicyh4ID0gIk1hcHBlZCBcbkFzc2VtYmxpZXMiKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAuNSwgMi41KSwgeWxpbSA9IGMoMCwgMTUzNikpICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KGFscGhhID0gMSkpKQoKIyBEaXNwbGF5IHRoZSBwbG90CnByaW50KEwxNS5MMTYuMTIwOC5NYXBwZWQuQmFyY2hhcnQpCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL0RIRlIuTGliMTUuMTYuMTIwOC5tYXBwZWQucGVyZmVjdHMuYmFyY2hhcnQucG5nIiwgcGxvdD1MMTUuTDE2LjEyMDguTWFwcGVkLkJhcmNoYXJ0LAogICAgICAgZHBpPTYwMCwgd2lkdGggPSA1LCBoZWlnaHQgPSA1LCB1bml0cyA9ICJpbiIpCmBgYAoKUGxvdCB0aGUgcGVyZmVjdHMgcmVjb3ZlcmVkIGluIHRoZSBLTyBtb2RlbCBmb3IgZWFjaCBsaWJyYXJ5IGFuZCB0aGUgY29tYmluZWQgcGVyZmVjdHMgKHNoYXJlZCBwZXJmZWN0cyArIHVuaXF1ZSBwZXJmZWN0cyk6CmBgYHtyfQojIENyZWF0ZSB0aGUgZGF0YSBmcmFtZQpyZWNvdmVyZWQuZGF0YSA8LSBkYXRhLmZyYW1lKAogIExpYnJhcnkgPSBjKCJDb2RvbjEiLCAiQ29kb24yIiwgIkJvdGgiKSwKICBQZXJmZWN0cyA9IGMoOTYxLCA4MTgsIDExMzYpLAogIFBlcmNlbnQgPSBjKDgwLCA2OCwgOTQpCikKCiMgQ3JlYXRlIGEgZ2dwbG90IG9iamVjdApMMTUuTDE2LjExNTAuUmVjb3ZlcmVkLkJhcmNoYXJ0IDwtIGdncGxvdChyZWNvdmVyZWQuZGF0YSkgKwogICMgQWRkIGEgaG9yaXpvbnRhbCBsaW5lIGF0IHk9MAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGNvbG9yID0gImJsYWNrIikgKwogICMgV2lkZXIgYmFyIGZvciAiQm90aCIgcGxvdHRlZCBiZWhpbmQKICBnZW9tX2JhcihkYXRhID0gc3Vic2V0KHJlY292ZXJlZC5kYXRhLCBMaWJyYXJ5ID09ICJCb3RoIiksCiAgICAgICAgICAgYWVzKHggPSAxLjUsIHkgPSBQZXJmZWN0cywgZmlsbCA9IExpYnJhcnkpLAogICAgICAgICAgIHN0YXQgPSAiaWRlbnRpdHkiLCAKICAgICAgICAgICB3aWR0aCA9IDAuNzUsIAogICAgICAgICAgIGFscGhhID0gMC41KSArCiAgIyBCYXIgcGxvdCBmb3IgQ29kb24xIGFuZCBDb2RvbjIKICBnZW9tX2JhcihkYXRhID0gc3Vic2V0KHJlY292ZXJlZC5kYXRhLCBMaWJyYXJ5ICE9ICJCb3RoIiksCiAgICAgICAgICAgYWVzKHggPSBhcy5udW1lcmljKGZhY3RvcihMaWJyYXJ5LCBsZXZlbHMgPSBjKCJDb2RvbjEiLCAiQ29kb24yIikpKSwgeSA9IFBlcmZlY3RzLCBmaWxsID0gTGlicmFyeSksIAogICAgICAgICAgIHN0YXQgPSAiaWRlbnRpdHkiLCAKICAgICAgICAgICB3aWR0aCA9IDAuNikgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBjKDEsIDIpLCBsYWJlbHMgPSBjKCJDb2RvbjEiLCAiQ29kb24yIiksIAogICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDAuNSwgMi41KSkgKwogIHNjYWxlX3lfY29udGludW91cygKICAgIG5hbWUgPSAiSG9tb2xvZ3MgUmVwcmVzZW50ZWQgXG4oMTIwOCBBc3NlbWJsZWQpIiwgCiAgICBsaW1pdHMgPSBjKDAsIDEyMDgpLAogICAgZXhwYW5kID0gYygwLCAwKSwgICMgUmVtb3ZlIHBhZGRpbmcKICAgIHNlYy5heGlzID0gc2VjX2F4aXMofiAuICogMTAwIC8gMTIwOCwgbmFtZSA9ICJMaWJyYXJ5IFJlcHJlc2VudGF0aW9uICglKSIsIGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMjApKQogICkgKwogICMgQWRkIGFuIGludmlzaWJsZSBsYXllciB0byBlbnN1cmUgdGhlIHNlY29uZGFyeSB5LWF4aXMgaXMgcGxvdHRlZAogIGdlb21fcG9pbnQoZGF0YSA9IHN1YnNldChyZWNvdmVyZWQuZGF0YSwgTGlicmFyeSAhPSAiQm90aCIpLAogICAgICAgICAgICAgYWVzKHggPSBhcy5udW1lcmljKGZhY3RvcihMaWJyYXJ5LCBsZXZlbHMgPSBjKCJDb2RvbjEiLCAiQ29kb24yIikpKSwgeSA9IFBlcmNlbnQgKiAxMjA4IC8gMTAwKSwgCiAgICAgICAgICAgICBjb2xvciA9IE5BKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZSgKICAgIGF4aXMudGl0bGUueS5yaWdodCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGV4dC55LnJpZ2h0ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aWNrcy55LnJpZ2h0ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy5saW5lLnkucmlnaHQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpLAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yID0gImJsYWNrIiwgc2l6ZT0xLjApLAogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJibGFjayIsIHNpemU9MS4wKSwKICAgIGF4aXMubGluZS54LnRvcCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueS5sZWZ0ID0gZWxlbWVudF9saW5lKGNvbG9yID0gImJsYWNrIiksCiAgICBheGlzLnRpY2tzLmxlbmd0aCA9IHVuaXQoMC4yLCAiY20iKQogICkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkNvZG9uMSIgPSAiIzAwNzJCMiIsICJDb2RvbjIiID0gIiNFNjlGMDAiLCAiQm90aCIgPSAibGlnaHRibHVlNCIpLAogICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoIkNvZG9uMSIsICJDb2RvbjIiLCAiQm90aCIpKSArCiAgbGFicyh4ID0gIlJlY292ZXJlZCBcbkhvbW9sb2dzIikgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLjUsIDIuNSksIHlsaW0gPSBjKDAsIDEyNTApKSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChhbHBoYSA9IDEpKSkKCiMgRGlzcGxheSB0aGUgcGxvdApwcmludChMMTUuTDE2LjExNTAuUmVjb3ZlcmVkLkJhcmNoYXJ0KQpgYGAKCmBgYHtyIGVjaG89RkFMU0V9Cmdnc2F2ZShmaWxlPSJQZXJmZWN0cy9QTE9UUy9ESEZSLkxpYjE1LjE2LjExNTAucmVjb3ZlcmVkLnBlcmZlY3RzLmJhcmNoYXJ0LnBuZyIsIHBsb3Q9TDE1LkwxNi4xMTUwLlJlY292ZXJlZC5CYXJjaGFydCwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gNSwgaGVpZ2h0ID0gNSwgdW5pdHMgPSAiaW4iKQpgYGAKClBsb3QgdGhlIHBlcmZlY3RzIHRoYXQgY29tcGxlbWVudCBpbiB0aGUgS08gbW9kZWwgZm9yIGVhY2ggbGlicmFyeSBhbmQgdGhlIGNvbWJpbmVkIHBlcmZlY3RzIChzaGFyZWQgcGVyZmVjdHMgKyB1bmlxdWUgcGVyZmVjdHMpOgpgYGB7cn0KIyBDcmVhdGUgdGhlIGRhdGEgZnJhbWUKY29tcGxlbWVudC5kYXRhIDwtIGRhdGEuZnJhbWUoCiAgTGlicmFyeSA9IGMoIkNvZG9uMSIsICJDb2RvbjIiLCAiQm90aCIpLAogIFBlcmZlY3RzID0gYyg0MTcsIDM3NywgNjAwKSwKICBQZXJjZW50ID0gYygzNSwgMzEsIDUwKQopCgojIENyZWF0ZSBhIGdncGxvdCBvYmplY3QKTDE1LkwxNi4xMTUwLkNvbXBsZW1lbnQuQmFyY2hhcnQgPC0gZ2dwbG90KGNvbXBsZW1lbnQuZGF0YSkgKwogICMgQWRkIGEgaG9yaXpvbnRhbCBsaW5lIGF0IHk9MAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGNvbG9yID0gImJsYWNrIikgKwogICMgV2lkZXIgYmFyIGZvciAiQm90aCIgcGxvdHRlZCBiZWhpbmQKICBnZW9tX2JhcihkYXRhID0gc3Vic2V0KGNvbXBsZW1lbnQuZGF0YSwgTGlicmFyeSA9PSAiQm90aCIpLAogICAgICAgICAgIGFlcyh4ID0gMS41LCB5ID0gUGVyZmVjdHMsIGZpbGwgPSBMaWJyYXJ5KSwKICAgICAgICAgICBzdGF0ID0gImlkZW50aXR5IiwgCiAgICAgICAgICAgd2lkdGggPSAwLjc1LCAKICAgICAgICAgICBhbHBoYSA9IDAuNSkgKwogICMgQmFyIHBsb3QgZm9yIENvZG9uMSBhbmQgQ29kb24yCiAgZ2VvbV9iYXIoZGF0YSA9IHN1YnNldChjb21wbGVtZW50LmRhdGEsIExpYnJhcnkgIT0gIkJvdGgiKSwKICAgICAgICAgICBhZXMoeCA9IGFzLm51bWVyaWMoZmFjdG9yKExpYnJhcnksIGxldmVscyA9IGMoIkNvZG9uMSIsICJDb2RvbjIiKSkpLCB5ID0gUGVyZmVjdHMsIGZpbGwgPSBMaWJyYXJ5KSwgCiAgICAgICAgICAgc3RhdCA9ICJpZGVudGl0eSIsIAogICAgICAgICAgIHdpZHRoID0gMC42KSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGMoMSwgMiksIGxhYmVscyA9IGMoIkNvZG9uMSIsICJDb2RvbjIiKSwgCiAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMC41LCAyLjUpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKAogICAgbmFtZSA9ICIgIiwgCiAgICBsaW1pdHMgPSBjKDAsIDEyMDgpLAogICAgZXhwYW5kID0gYygwLCAwKSwgICMgUmVtb3ZlIHBhZGRpbmcKICAgIHNlYy5heGlzID0gc2VjX2F4aXMofiAuICogMTAwIC8gMTIwOCwgbmFtZSA9ICJMaWJyYXJ5IFJlcHJlc2VudGF0aW9uICglKSIsIGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMjApKQogICkgKwogICMgQWRkIGFuIGludmlzaWJsZSBsYXllciB0byBlbnN1cmUgdGhlIHNlY29uZGFyeSB5LWF4aXMgaXMgcGxvdHRlZAogIGdlb21fcG9pbnQoZGF0YSA9IHN1YnNldChjb21wbGVtZW50LmRhdGEsIExpYnJhcnkgIT0gIkJvdGgiKSwKICAgICAgICAgICAgIGFlcyh4ID0gYXMubnVtZXJpYyhmYWN0b3IoTGlicmFyeSwgbGV2ZWxzID0gYygiQ29kb24xIiwgIkNvZG9uMiIpKSksIHkgPSBQZXJjZW50ICogMTIwOCAvIDgwKSwgCiAgICAgICAgICAgICBjb2xvciA9IE5BKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZSgKICAgIGF4aXMudGl0bGUueS5yaWdodCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJyZWQiLCBzaXplID0gMTgpLAogICAgYXhpcy50ZXh0LnkucmlnaHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAicmVkIiwgc2l6ZSA9IDE2KSwKICAgIGF4aXMudGlja3MueS5yaWdodCA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJibGFjayIpLAogICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJibGFjayIsIHNpemU9MS4wKSwKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmxhY2siLCBzaXplPTEuMCksCiAgICBheGlzLmxpbmUueS5yaWdodCA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJibGFjayIpLAogICAgYXhpcy5saW5lLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLmxpbmUueC50b3AgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLmxlbmd0aCA9IHVuaXQoMC4yLCAiY20iKQogICkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkNvZG9uMSIgPSAiIzAwNzJCMiIsICJDb2RvbjIiID0gIiNFNjlGMDAiLCAiQm90aCIgPSAibGlnaHRibHVlNCIpLAogICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoIkNvZG9uMSIsICJDb2RvbjIiLCAiQm90aCIpKSArCiAgbGFicyh4ID0gIkNvbXBsZW1lbnRpbmcgXG5Ib21vbG9ncyIpICsgICMgQ2hhbmdlZCB4LWF4aXMgbGFiZWwKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMC41LCAyLjUpLCB5bGltID0gYygwLCAxMjUwKSkgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3QoYWxwaGEgPSAxKSkpCgojIERpc3BsYXkgdGhlIHBsb3QKcHJpbnQoTDE1LkwxNi4xMTUwLkNvbXBsZW1lbnQuQmFyY2hhcnQpCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL0RIRlIuTGliMTUuMTYuMTE1MC5jb21wbGVtZW50LnBlcmZlY3RzLmJhcmNoYXJ0LnBuZyIsIHBsb3Q9TDE1LkwxNi4xMTUwLkNvbXBsZW1lbnQuQmFyY2hhcnQsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDUsIGhlaWdodCA9IDUsIHVuaXRzID0gImluIikKYGBgCgpgYGB7cn0KcGF0Y2g3IDwtIEwxNS5MMTYuMTE1MC5SZWNvdmVyZWQuQmFyY2hhcnQgfCBMMTUuTDE2LjExNTAuQ29tcGxlbWVudC5CYXJjaGFydApwYXRjaDcKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iUGVyZmVjdHMvUExPVFMvREhGUi5MaWIxNS4xNi5yZWNvdmVyZWQuY29tcGxlbWVudC5wZXJmZWN0cy5iYXJjaGFydC5wbmciLCBwbG90PXBhdGNoNywKICAgICAgIGRwaT02MDAsIHdpZHRoID0gNSwgaGVpZ2h0ID0gNSwgdW5pdHMgPSAiaW4iKQpgYGAKCiMjIFBoeWxvZ2VuZXRpYyBUcmVlcwoKIyMjIEZ1bGwgVHJlZSBBbGlnbm1lbnQgKDEsMjA4KQoKKipUaGlzIHNlY3Rpb24gdXNlcyB0aGUgYGxpYnJhcnkoZ2d0cmVlKWAgYW5kIGBsaWJyYXJ5KGNhc3RvcilgIHBhY2thZ2VzLioqCgpDcmVhdGUgYSBmdWxsIHVuaXF1ZSBwZXJmZWN0cyBtdXRJRCBkYXRhZnJhbWUgYmFzZWQgb24gYG11dElEaW5mby4xNS4xNi56ZXJvcy5zaGFyZWRgIGFuZCBgbXV0SURpbmZvLjE1LjE2Lnplcm9zLnVuaXF1ZWAgdG8gZ2VuZXJhdGUgYSBwaHlsb2dlbmV0aWMgdHJlZSBjb21wb3NlZCBvbiAxLDIwOCBtdXRJRCBzZXF1ZW5jZXMuCmBgYHtyfQojIFN1YnNldCByZWxldmFudCBjb2x1bW5zIGJlZm9yZSBjb21iaW5pbmcgZGF0YXNldHM6CgojIFNoYXJlZCBtdXRJRHMKbXV0SURpbmZvLjE1LjE2Lnplcm9zLnNoYXJlZC5wZXJmZWN0cyA8LSBtdXRJRGluZm8uMTUuMTYuemVyb3Muc2hhcmVkICU+JQogIGZpbHRlcihtdXRhdGlvbnMueCA9PSAwKSAlPiUKICBzZWxlY3QobXV0SUQsIHNlcSA9IHNlcS54KQoKIyBVbmlxdWUgbXV0SURzCm11dElEaW5mby4xNS4xNi56ZXJvcy51bmlxdWUucGVyZmVjdHMgPC0gbXV0SURpbmZvLjE1LjE2Lnplcm9zLnVuaXF1ZSAlPiUKICBmaWx0ZXIobXV0YXRpb25zID09IDApICU+JQogIHNlbGVjdChtdXRJRCwgc2VxKQoKIyBDb21iaW5lIGJvdGggZGF0YXNldHMKbXV0SURpbmZvLjE1LjE2Lnplcm9zLmFsbC5wZXJmZWN0cyA8LSByYmluZChtdXRJRGluZm8uMTUuMTYuemVyb3Muc2hhcmVkLnBlcmZlY3RzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0SURpbmZvLjE1LjE2Lnplcm9zLnVuaXF1ZS5wZXJmZWN0cykKYGBgCgpBZGQgInRheElEIiBhbmQgIlBjdElkZW50RWNvbGkiIHRvIGVhY2ggIm11dElEIgpgYGB7cn0KIyBSZW5hbWUgIm11dElEIiB0byAiSUQiIHRvIG1lcmdlIHdpdGggYG9yZ2luZm9gIGRmOgptdXRJRGluZm8uMTUuMTYuemVyb3MuYWxsLnBlcmZlY3RzIDwtIG11dElEaW5mby4xNS4xNi56ZXJvcy5hbGwucGVyZmVjdHMgJT4lIHJlbmFtZShJRCA9IG11dElEKQoKIyBNZXJnZSAiVGF4SUQiIGFuZCAiUGN0SWRlbnRFY29saSIgZnJvbSBgb3JnaW5mb2AgdG8gYG11dElEaW5mby4xNS4xNi56ZXJvcy5hbGwucGVyZmVjdHNgOgptdXRJRGluZm8uMTUuMTYuemVyb3MuYWxsLnBlcmZlY3RzIDwtIG11dElEaW5mby4xNS4xNi56ZXJvcy5hbGwucGVyZmVjdHMgJT4lCiAgbGVmdF9qb2luKG9yZ2luZm8gJT4lIHNlbGVjdChJRCwgVGF4SUQsIFBjdElkZW50RWNvbGkpLCBieSA9ICJJRCIpCmBgYAoKQ3JlYXRlIGEgRkFTVEEgZmlsZSBjb250YWluaW5nIHRoZSBJRCBhbmQgaXRzIGFzc29jaWF0ZWQgcHJvdGVpbiBzZXF1ZW5jZSBmb3IgYWxpZ25tZW50CmBgYHtyIGV2YWw9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQojIENvbGxlY3QgdGhlIHNlcXVlbmNlcyBpbiBGQVNUQSBmb3JtYXQKbXV0SURpbmZvLjE1LjE2Lnplcm9zLmFsbC5wZXJmZWN0c19mYXN0YV9jb250ZW50IDwtIHBhc3RlKCI+IixtdXRJRGluZm8uMTUuMTYuemVyb3MuYWxsLnBlcmZlY3RzJElELCAiXG4iLCBtdXRJRGluZm8uMTUuMTYuemVyb3MuYWxsLnBlcmZlY3RzJHNlcSwgIlxuIiwgc2VwID0gIiIsIGNvbGxhcHNlID0gIiIpCgojIERlZmluZSB0aGUgZmlsZSBwYXRoIGluIHRoZSB3b3JraW5nIGRpcmVjdG9yeQptdXRJRGluZm8uMTUuMTYuemVyb3MuYWxsLnBlcmZlY3RzX2Zhc3RhX3BhdGggPC0gZmlsZS5wYXRoKGdldHdkKCksICJQZXJmZWN0cy9UUkVFUy9ESEZSLkxpYi4xNS4xNi5JRC5hbGwucGVyZmVjdHMubWFwcGVkLmZhc3RhIikKCiMgV3JpdGUgdGhlIEZBU1RBIGNvbnRlbnQgdG8gdGhlIGZpbGUKd3JpdGVMaW5lcyhtdXRJRGluZm8uMTUuMTYuemVyb3MuYWxsLnBlcmZlY3RzX2Zhc3RhX2NvbnRlbnQsIGNvbiA9IG11dElEaW5mby4xNS4xNi56ZXJvcy5hbGwucGVyZmVjdHNfZmFzdGFfcGF0aCkKYGBgCgpQZXJmb3JtIGEgbXVsdGlwbGUgc2VxdWVuY2UgYWxpZ25tZW50IG9uIHRoZSBGQVNUQSBmaWxlIHVzaW5nIENMVVNUQUwgT21lZ2E6CmBgYHtiYXNoIGV2YWwgPSBGQUxTRSwgcmVzdWx0cyA9ICdoaWRlJ30KIyBNYXkgbmVlZCB0byBlbmFibGUgcGVybWlzc2lvbnMgdG8gcnVuIHRoZSBleGVjdXRhYmxlOgojY2htb2QgK3ggY2x1c3RhbG8KLi9TY3JpcHRzL2NsdXN0YWxvIC1pIFBlcmZlY3RzL1RSRUVTL0RIRlIuTGliLjE1LjE2LklELmFsbC5wZXJmZWN0cy5tYXBwZWQuZmFzdGEgLW8gUGVyZmVjdHMvVFJFRVMvREhGUi5MaWIuMTUuMTYuSUQuYWxsLnBlcmZlY3RzLm1hcHBlZC5hbGlnbmVkLmZhc3RhIC0tb3V0Zm10PWZhIC0tZm9yY2UKYGBgCgpVc2UgRmFzdFRyZWUgKHBoeWxvZ2VuZXRpYyB0cmVlIGJ1aWxkaW5nIHByb2dyYW0pIHRvIGluZmVyIHRoZSB0cmVlIGZyb20gdGhlIGFsaWduZWQgYW1pbm8gYWNpZCBzZXF1ZW5jZXM6CmBgYHtiYXNoIGV2YWwgPSBGQUxTRX0KIyBNTCBNb2RlbDogSm9uZXMtVGF5bG9yLVRob3J0b24KIyBjaG1vZCAreCBGYXN0VHJlZQouL1NjcmlwdHMvRmFzdFRyZWUgUGVyZmVjdHMvVFJFRVMvREhGUi5MaWIuMTUuMTYuSUQuYWxsLnBlcmZlY3RzLm1hcHBlZC5hbGlnbmVkLmZhc3RhID4gUGVyZmVjdHMvVFJFRVMvREhGUi5MaWIuMTUuMTYuSUQuYWxsLnBlcmZlY3RzLm1hcHBlZC50cmVlLm5ld2ljawpgYGAKCiMjIyMgTmV3aWNrIFRyZWUgRmlsZQpJbXBvcnQgdGhlIG5ld2ljayB0cmVlIGZpbGUgYmFzZWQgb24gdGhlIHNlcXVlbmNlIGFsaWdubWVudCBvZiBzaGFyZWQgcGVyZmVjdHMgZGVyaXZlZCBmcm9tIExpYjE1ICYgTGliMTYgbWFwcGluZyBmaWxlOgpgYGB7cn0KIyBGdWxsIHRyZWUgYWxpZ25tZW50ICgxLDIwOCBtdXRJRCkKQWxsdHJlZTE1IDwtIHJlYWQudHJlZSgiUGVyZmVjdHMvVFJFRVMvREhGUi5MaWIuMTUuMTYuSUQuYWxsLnBlcmZlY3RzLm1hcHBlZC50cmVlLm5ld2ljayIpICAgIyBuZXdpY2sgZm9ybWF0CkFsbHRyZWUxNQpgYGAKCkV4dHJhY3QgdGhlIHRpcCBsYWJlbHMgZnJvbSB0aGUgbmV3aWNrIHRyZWUgZmlsZSB0byBtYXRjaCB3aXRoIE5DQkkgdGF4b25vbXkgZm9yIGRvd25zdHJlYW0gcGxvdHRpbmc6CmBgYHtyfQojIEV4dHJhY3QgdGlwIGxhYmVscyBmcm9tIEFsbHRyZWUxNQpBbGx0cmVlMTVfdGlwX2xhYmVscyA8LSBBbGx0cmVlMTUkdGlwLmxhYmVsCgojIENyZWF0ZSBhIG5ldyBkYXRhIGZyYW1lIHdpdGggdW5pcXVlIHRpcCBsYWJlbHMKQWxsdHJlZTE1X3RpcF9sYWJlbHNfZGYgPC0gZGF0YS5mcmFtZSh0aXAubGFiZWwgPSB1bmlxdWUoQWxsdHJlZTE1X3RpcF9sYWJlbHMpKQoKIyBQcmludCB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgdGhlIG5ldyBkYXRhIGZyYW1lCmhlYWQoQWxsdHJlZTE1X3RpcF9sYWJlbHNfZGYpCmBgYAoKTWF0Y2ggZWFjaCB0aXAubGFiZWwgSUQgaW4gYEFsbHRyZWUxNWAgd2l0aCBpdCdzIGFzc29jaWF0ZWQgVGF4SUQgZnJvbSBgb3JnaW5mb2AgZGF0YWZyYW1lOgpgYGB7cn0KIyBSZW5hbWUgY29sdW1uICJ0aXAubGFiZWwiIHRvICJJRCIKY29sbmFtZXMoQWxsdHJlZTE1X3RpcF9sYWJlbHNfZGYpIDwtIGMoIklEIikKCiMgTWVyZ2Ugb3JnaW5mbyB3aXRoIEFsbHRyZWUxNV90aXBfbGFiZWxzX2RmIGJhc2VkIG9uIHRoZSBzaGFyZWQgSUQKQWxsdHJlZTE1X3RheGEgPC0gbWVyZ2UoQWxsdHJlZTE1X3RpcF9sYWJlbHNfZGYsIG9yZ2luZm8sIGJ5ID0gIklEIiwgYWxsLnggPSBUUlVFKQoKIyBQcmludCB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgdGhlIG1lcmdlZCBkYXRhIGZyYW1lCmhlYWQoQWxsdHJlZTE1X3RheGEpCmBgYAoKIyMjIyBOQ0JJIFRheG9ub215IEZpbGUKCkltcG9ydCB0aGUgZnVsbCB1cC10by1kYXRlIE5DQkkgdGF4b25vbXkgZGF0YXNldCBjb250YWluaW5nIDIsNTgwLDM4OCB1bmlxdWUgVGF4SUQuCmBgYHtyfQojIEltcG9ydCB1cGRhdGVkIE5DQkkgdGF4b25vbXkgbWFwcGluZyBmaWxlCm5jYmlfdGF4YSA9IHJlYWQuY3N2KCJQZXJmZWN0cy9JTlBVVC9hbGwubmNiaS50YXhhLmxpbmVhZ2UuY3N2IiwgaGVhZD1UUlVFKSAgIyByZWFkIGNzdiBmaWxlCgojIENvbnZlcnQgVGF4SUQgY29sdW1uIGZyb20gY2hyIHRvIGludApuY2JpX3RheGEkVGF4SUQgPC0gYXMuaW50ZWdlcihuY2JpX3RheGEkVGF4SUQpCgojIFByaW50IHRoZSBmaXJzdCBmZXcgcm93cyBvZiB0aGUgTkNCSSB0YXhvbm9teSBkYXRhIGZyYW1lCmhlYWQobmNiaV90YXhhKQpgYGAKCmBgYHtyfQojIE1lcmdlIHRoZSBOQ0JJIHRheG9ub215IGNvbHVtbnMgdG8gQWxsdHJlZTE1X3RheGEgYmFzZWQgb24gc2hhcmVkIFRheElECkFsbHRyZWUxNV90YXhhX21lcmdlZCA8LSBBbGx0cmVlMTVfdGF4YQpBbGx0cmVlMTVfdGF4YV9tZXJnZWQkTkNCSS5uYW1lIDwtIE5BCkFsbHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLnN1cGVya2luZ2RvbSA8LSBOQQpBbGx0cmVlMTVfdGF4YV9tZXJnZWQkTkNCSS5waHlsdW0gPC0gTkEKQWxsdHJlZTE1X3RheGFfbWVyZ2VkJE5DQkkuY2xhc3MgPC0gTkEKQWxsdHJlZTE1X3RheGFfbWVyZ2VkJE5DQkkub3JkZXIgPC0gTkEKQWxsdHJlZTE1X3RheGFfbWVyZ2VkJE5DQkkuZmFtaWx5IDwtIE5BCkFsbHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLmdlbnVzIDwtIE5BCkFsbHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLnNwZWNpZXMgPC0gTkEKCiMgTkNCSS5uYW1lCkFsbHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLm5hbWVbQWxsdHJlZTE1X3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSA8LSBuY2JpX3RheGEkTkNCSS5uYW1lW21hdGNoKEFsbHRyZWUxNV90YXhhX21lcmdlZCRUYXhJRFtBbGx0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdLCBuY2JpX3RheGEkVGF4SUQpXQoKIyBOQ0JJLnN1cGVya2luZ2RvbQpBbGx0cmVlMTVfdGF4YV9tZXJnZWQkTkNCSS5zdXBlcmtpbmdkb21bQWxsdHJlZTE1X3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSA8LSBuY2JpX3RheGEkTkNCSS5zdXBlcmtpbmdkb21bbWF0Y2goQWxsdHJlZTE1X3RheGFfbWVyZ2VkJFRheElEW0FsbHRyZWUxNV90YXhhX21lcmdlZCRUYXhJRCAlaW4lIG5jYmlfdGF4YSRUYXhJRF0sIG5jYmlfdGF4YSRUYXhJRCldCgojIE5DQkkucGh5bHVtCkFsbHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLnBoeWx1bVtBbGx0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdIDwtIG5jYmlfdGF4YSROQ0JJLnBoeWx1bVttYXRjaChBbGx0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SURbQWxsdHJlZTE1X3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSwgbmNiaV90YXhhJFRheElEKV0KCiMgTkNCSS5jbGFzcwpBbGx0cmVlMTVfdGF4YV9tZXJnZWQkTkNCSS5jbGFzc1tBbGx0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdIDwtIG5jYmlfdGF4YSROQ0JJLmNsYXNzW21hdGNoKEFsbHRyZWUxNV90YXhhX21lcmdlZCRUYXhJRFtBbGx0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdLCBuY2JpX3RheGEkVGF4SUQpXQoKIyBOQ0JJLm9yZGVyCkFsbHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLm9yZGVyW0FsbHRyZWUxNV90YXhhX21lcmdlZCRUYXhJRCAlaW4lIG5jYmlfdGF4YSRUYXhJRF0gPC0gbmNiaV90YXhhJE5DQkkub3JkZXJbbWF0Y2goQWxsdHJlZTE1X3RheGFfbWVyZ2VkJFRheElEW0FsbHRyZWUxNV90YXhhX21lcmdlZCRUYXhJRCAlaW4lIG5jYmlfdGF4YSRUYXhJRF0sIG5jYmlfdGF4YSRUYXhJRCldCgojIE5DQkkuZmFtaWx5CkFsbHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLmZhbWlseVtBbGx0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdIDwtIG5jYmlfdGF4YSROQ0JJLmZhbWlseVttYXRjaChBbGx0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SURbQWxsdHJlZTE1X3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSwgbmNiaV90YXhhJFRheElEKV0KCiMgTkNCSS5mYW1pbHkKQWxsdHJlZTE1X3RheGFfbWVyZ2VkJE5DQkkuZ2VudXNbQWxsdHJlZTE1X3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSA8LSBuY2JpX3RheGEkTkNCSS5nZW51c1ttYXRjaChBbGx0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SURbQWxsdHJlZTE1X3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSwgbmNiaV90YXhhJFRheElEKV0KCiMgTkNCSS5mYW1pbHkKQWxsdHJlZTE1X3RheGFfbWVyZ2VkJE5DQkkuc3BlY2llc1tBbGx0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdIDwtIG5jYmlfdGF4YSROQ0JJLnNwZWNpZXNbbWF0Y2goQWxsdHJlZTE1X3RheGFfbWVyZ2VkJFRheElEW0FsbHRyZWUxNV90YXhhX21lcmdlZCRUYXhJRCAlaW4lIG5jYmlfdGF4YSRUYXhJRF0sIG5jYmlfdGF4YSRUYXhJRCldCmBgYAoKYGBge3J9CiMgUmVwbGFjZSB0aGUgdmFsdWUgaW4gIk5DQkkucGh5bHVtIiBjb2x1bW4gd2l0aCB0aGUgdmFsdWUgZnJvbSAiTkNCSS5jbGFzcyIgaWYgIk5DQkkucGh5bHVtIiBpcyAiUHNldWRvbW9uYWRvdGEiCkFsbHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLnBoeWx1bSA8LSBpZmVsc2UoQWxsdHJlZTE1X3RheGFfbWVyZ2VkJE5DQkkucGh5bHVtID09ICJQc2V1ZG9tb25hZG90YSIsIEFsbHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLmNsYXNzLCBBbGx0cmVlMTVfdGF4YV9tZXJnZWQkTkNCSS5waHlsdW0pCgojIFJlbW92ZSByb3dzIHdpdGggTkEgaW4gTkNCSS5waHlsdW0gY29sdW1uCkFsbHRyZWUxNV90YXhhX21lcmdlZCA8LSBBbGx0cmVlMTVfdGF4YV9tZXJnZWRbIWlzLm5hKEFsbHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLnBoeWx1bSkgJiBBbGx0cmVlMTVfdGF4YV9tZXJnZWQkTkNCSS5waHlsdW0gIT0gIk5BIiwgXQoKIyBQcmludCB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgdGhlIEFsbHRyZWUxNV90YXhhX21lcmdlZCBkYXRhIGZyYW1lCmhlYWQoQWxsdHJlZTE1X3RheGFfbWVyZ2VkKQpgYGAKCiMjIyMgREhGUiBEaXZlcnNpdHkgVHJlZXMKCiMjIyMjIFBjdCBTaW1pbGFyaXR5IEUgY29saQoKUGxvdCBhIGNpcmN1bGFyIHBoeWxvZ2VuZXRpYyB0cmVlIHdpdGggY29sb3Itc2NhbGUgYmFzZWQgb24gcGh5bG9nZW5ldGljIGRpc3RhbmNlIGJldHdlZW4gZWFjaCBESEZSIGhvbW9sb2cgcmVsYXRpdmUgdG8gdGhlIEUuIGNvbGkgREhGUiBnZW5lLgpgYGB7cn0KcGh5bG90cmVlX05DQklfZWNvbGlfaWRlbnQgPC0gZ2d0cmVlKEFsbHRyZWUxNSwgbGF5b3V0PSJjaXJjdWxhciIsIGJyYW5jaC5sZW5ndGg9Im5vbmUiKSAlPCslIAogIEFsbHRyZWUxNV90YXhhX21lcmdlZCArIAogIGFlcyhjb2xvcj1QY3RJZGVudEVjb2xpKjEwMCkgKwogIGdlb21fdGlwcG9pbnQoYWVzKGNvbG9yPVBjdElkZW50RWNvbGkqMTAwKSwgc2l6ZT0xLCBhbHBoYT0wLjgpICsKICBnZW9tX3RpcGxhYjIoYWVzKGNvbG9yPVBjdElkZW50RWNvbGkqMTAwLCBsYWJlbD1OQ0JJLm5hbWUsIGFuZ2xlPWFuZ2xlKSwgc2l6ZT0xKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJsZWZ0IikgKyAKICBzY2FsZV9jb2xvdXJfZ3JhZGllbnQyKCJTZXF1ZW5jZVxuSWRlbnRpdHkgKCUpIiwgbG93PSJibHVlIiwgbWlkPSJncmVlbiIsIGhpZ2g9InJlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICBtaWRwb2ludD0oMTAwK21pbihBbGx0cmVlMTVfdGF4YV9tZXJnZWQkUGN0SWRlbnRFY29saSkqMTAwKS8yKQoKcGh5bG90cmVlX05DQklfZWNvbGlfaWRlbnQKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iUGVyZmVjdHMvUExPVFMvVHJlZXMvQWxsMTIwOC9ESEZSLkxpYjE1LjE2LkZhc3RUcmVlLjEyMDgubWFwcGVkLnBlcmZlY3RzLk5DQkkuZWNvbGkucGN0aWRlbnQubmFtZXMucG5nIiwgcGxvdD1waHlsb3RyZWVfTkNCSV9lY29saV9pZGVudCwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gMTIsIGhlaWdodCA9IDcuNSwgdW5pdHMgPSAiaW4iKQpgYGAKClBsb3Qgd2l0aCBvbmx5IHRoZSBwYXRob2dlbmljIGhvbW9sb2dzIGxhYmVsbGVkOgpgYGB7cn0KIyBEZWZpbmUgdGhlIHNwZWNpZmljIE5DQkkubmFtZSB2YWx1ZXMgeW91IHdhbnQgdG8gbGFiZWwKcGF0aG9nZW5pY19sYWJlbHMgPC0gYygiQW5hZXJvY29jY3VzIHRldHJhZGl1cyIsICJCYWNpbGx1cyBjZXJldXMiLCAiRXNjaGVyaWNoaWEgY29saSIsICJIYWVtb3BoaWx1cyBpbmZsdWVuemFlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIlNhbG1vbmVsbGEgZW50ZXJpY2EiLCAiU3RhcGh5bG9jb2NjdXMgYXVyZXVzIiwgIlN0cmVwdG9jb2NjdXMgcG5ldW1vbmlhZSIsICJWaWJyaW8gY2hvbGVyYWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAiRW50ZXJvY29jY3VzIGZhZWNpdW0iLCAiS2xlYnNpZWxsYSBwbmV1bW9uaWFlIiwgIkFjaW5ldG9iYWN0ZXIgYmF1bWFubmlpIiwgIlBzZXVkb21vbmFzIGFlcnVnaW5vc2EiLCAKICAgICAgICAgICAgICAgICAgICAgICAiUHJvdGV1cyBtaXJhYmlsaXMiLCAiQm9yZGV0ZWxsYSBob2xtZXNpaSIsICJNeWNvcGxhc21vaWRlcyBwbmV1bW9uaWFlIiwgIkNobGFteWRpYSBwbmV1bW9uaWFlIikKCnBoeWxvdHJlZV9OQ0JJX2Vjb2xpX2lkZW50X3BhdGhvZ2VuaWMgPC0gZ2d0cmVlKEFsbHRyZWUxNSwgbGF5b3V0PSJjaXJjdWxhciIsIGJyYW5jaC5sZW5ndGg9Im5vbmUiKSAlPCslIAogIEFsbHRyZWUxNV90YXhhX21lcmdlZCArIAogIGFlcyhjb2xvcj1QY3RJZGVudEVjb2xpKjEwMCkgKwogIGdlb21fdGlwcG9pbnQoYWVzKGNvbG9yPVBjdElkZW50RWNvbGkqMTAwKSwgc2l6ZT0xLCBhbHBoYT0wLjgpICsKICBnZW9tX3RpcGxhYjIoYWVzKGNvbG9yPVBjdElkZW50RWNvbGkqMTAwLCAKICAgICAgICAgICAgICAgICAgIGxhYmVsPWlmZWxzZShOQ0JJLm5hbWUgJWluJSBwYXRob2dlbmljX2xhYmVscywgTkNCSS5uYW1lLCAiIiksIAogICAgICAgICAgICAgICAgICAgYW5nbGU9YW5nbGUpLCAKICAgICAgICAgICAgICAgc2l6ZT0xKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJsZWZ0IikgKyAKICBzY2FsZV9jb2xvdXJfZ3JhZGllbnQyKCJTZXF1ZW5jZVxuSWRlbnRpdHkgKCUpIiwgbG93PSJibHVlIiwgbWlkPSJncmVlbiIsIGhpZ2g9InJlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICBtaWRwb2ludD0oMTAwK21pbihBbGx0cmVlMTVfdGF4YV9tZXJnZWQkUGN0SWRlbnRFY29saSkqMTAwKS8yKQoKcGh5bG90cmVlX05DQklfZWNvbGlfaWRlbnRfcGF0aG9nZW5pYwpgYGAKCmBgYHtyIGVjaG89RkFMU0V9Cmdnc2F2ZShmaWxlPSJQZXJmZWN0cy9QTE9UUy9UcmVlcy9BbGwxMjA4L0RIRlIuTGliMTUuMTYuRmFzdFRyZWUuMTIwOC5tYXBwZWQucGVyZmVjdHMuTkNCSS5lY29saS5wY3RpZGVudC5QQVRIT0dFTlMucG5nIiwgCiAgICAgICBwbG90PXBoeWxvdHJlZV9OQ0JJX2Vjb2xpX2lkZW50X3BhdGhvZ2VuaWMsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA3LjUsIHVuaXRzID0gImluIikKYGBgCgojIyMjIyBUYXhvbm9taWMgUmluZ3MKClRoZSBmb2xsb3dpbmcgc2VjdGlvbiBidWlsZHMgb24gdGhlIGluaXRpYWwgREhGUiBwaHlsb2dlbmV0aWMgdHJlZSBhbmQgYWRkcyB0aGUgTkNCSSB0YXhvbm9taWMgbGluZWFnZXMgYXMgYW4gb3V0ZXIgcmluZyB0byBkaXNwbGF5IHRoZSBicmVhZHRoIG9mIHNlcXVlbmNlIGRpdmVyc2l0eSBpbiB0aGUgMSw1MzYtREhGUiBkZXNpZ25lZCBsaWJyYXJ5LiBIb3dldmVyLCB0aGlzIHRyZWUgb25seSBjb250YWlucyA2NDMgdW5pcXVlIGJyYW5jaCB0aXBzICg0MSUgb2YgZnVsbCBsaWJyYXJ5IGRpdmVyc2l0eSkgZnJvbSB0aGUgcmVjb3ZlcmVkIHNoYXJlZCBwZXJmZWN0cyBiZXR3ZWVuIGxpYnJhcmllcy4KCioqRGlzdGluY3QgUGh5bHVtIENvbG9ycyBmb3IgUGxvdHRpbmcqKgpgYGB7cn0KIyBHZW5lcmF0ZSBkaXN0aW5jdCBjb2xvcnMgZm9yIGVhY2ggdW5pcXVlIHZhbHVlIGluIHRoZSAiTkNCSS5waHlsdW0iIGNvbHVtbgpkaXN0aW5jdF9jb2xvcnNfbmNiaSA8LSByYWluYm93KGxlbmd0aCh1bmlxdWUoQWxsdHJlZTE1X3RheGFfbWVyZ2VkJE5DQkkucGh5bHVtKSkpCgojIFJldmVyc2UgdGhlIG9yZGVyIG9mIGNvbG9ycwojZGlzdGluY3RfY29sb3JzX25jYmkgPC0gcmV2KGRpc3RpbmN0X2NvbG9yc19uY2JpKQoKIyBDcmVhdGUgYSBuZXcgY29sdW1uICJOQ0JJLnBoeWx1bV9jb2xvcnMiIGFuZCBhc3NpZ24gY29sb3JzIGJhc2VkIG9uIHRoZSB1bmlxdWUgdmFsdWVzIGluICJOQ0JJLnBoeWx1bSIKQWxsdHJlZTE1X3RheGFfbWVyZ2VkIDwtIEFsbHRyZWUxNV90YXhhX21lcmdlZCAlPiUKICBtdXRhdGUoTkNCSS5waHlsdW1fY29sb3JzID0gZGlzdGluY3RfY29sb3JzX25jYmlbYXMuaW50ZWdlcihmYWN0b3IoTkNCSS5waHlsdW0pKV0pCgojRXN0YWJsaXNoIGNvbG9yIHNjaGVtZSBmb3IgcGxvdHRpbmcKTkNCSXBoeWxvQ29sb3IgPC0gQWxsdHJlZTE1X3RheGFfbWVyZ2VkICU+JQogIHNlbGVjdChjKCJOQ0JJLnBoeWx1bSIsICJOQ0JJLnBoeWx1bV9jb2xvcnMiKSkgJT4lCiAgZGlzdGluY3QoKQpOQ0JJcGh5bG9Db2xvciA8LSBOQ0JJcGh5bG9Db2xvcltvcmRlcihOQ0JJcGh5bG9Db2xvciROQ0JJLnBoeWx1bSwgZGVjcmVhc2luZz1GQUxTRSksXQoKIyBMaXN0IHBoeWx1bSBpbiBhbHBoYWJldGljYWwgb3JkZXIgZm9yIGxlZ2VuZCBhbmQgcGxvdHRpbmcKQWxsdHJlZTE1X3RheGFfbWVyZ2VkJE5DQkkucGh5bHVtIDwtIGZhY3RvcihBbGx0cmVlMTVfdGF4YV9tZXJnZWQkTkNCSS5waHlsdW0sIGxldmVscz1OQ0JJcGh5bG9Db2xvciROQ0JJLnBoeWx1bSkKYGBgCgoqKkRpc3RpbmN0IENsYXNzIENvbG9ycyBmb3IgUGxvdHRpbmcqKgpgYGB7cn0KIyBHZW5lcmF0ZSBkaXN0aW5jdCBjb2xvcnMgZm9yIGVhY2ggdW5pcXVlIHZhbHVlIGluIHRoZSAiTkNCSS5jbGFzcyIgY29sdW1uCmRpc3RpbmN0X2NvbG9yc19uY2JpX2NsYXNzIDwtIHJhaW5ib3cobGVuZ3RoKHVuaXF1ZShBbGx0cmVlMTVfdGF4YV9tZXJnZWQkTkNCSS5jbGFzcykpKQoKIyBDcmVhdGUgYSBuZXcgY29sdW1uICJOQ0JJLmNsYXNzX2NvbG9ycyIgYW5kIGFzc2lnbiBjb2xvcnMgYmFzZWQgb24gdGhlIHVuaXF1ZSB2YWx1ZXMgaW4gIk5DQkkuY2xhc3MiCkFsbHRyZWUxNV90YXhhX21lcmdlZCA8LSBBbGx0cmVlMTVfdGF4YV9tZXJnZWQgJT4lCiAgbXV0YXRlKE5DQkkuY2xhc3NfY29sb3JzID0gZGlzdGluY3RfY29sb3JzX25jYmlfY2xhc3NbYXMuaW50ZWdlcihmYWN0b3IoTkNCSS5jbGFzcykpXSkKCiNFc3RhYmxpc2ggY29sb3Igc2NoZW1lIGZvciBwbG90dGluZwpOQ0JJY2xhc3NDb2xvciA8LSBBbGx0cmVlMTVfdGF4YV9tZXJnZWQgJT4lCiAgc2VsZWN0KGMoIk5DQkkuY2xhc3MiLCAiTkNCSS5jbGFzc19jb2xvcnMiKSkgJT4lCiAgZGlzdGluY3QoKQpOQ0JJY2xhc3NDb2xvciA8LSBOQ0JJY2xhc3NDb2xvcltvcmRlcihOQ0JJY2xhc3NDb2xvciROQ0JJLmNsYXNzLCBkZWNyZWFzaW5nPUZBTFNFKSxdCgojIExpc3QgcGh5bHVtIGluIGFscGhhYmV0aWNhbCBvcmRlciBmb3IgbGVnZW5kIGFuZCBwbG90dGluZwpBbGx0cmVlMTVfdGF4YV9tZXJnZWQkTkNCSS5jbGFzcyA8LSBmYWN0b3IoQWxsdHJlZTE1X3RheGFfbWVyZ2VkJE5DQkkuY2xhc3MsIGxldmVscz1OQ0JJY2xhc3NDb2xvciROQ0JJLmNsYXNzKQpgYGAKCkJ1aWxkIHRoZSBpbml0aWFsIERIRlIgdHJlZSB3aXRoIGNvbG9yLXNjYWxlIGJhc2VkIG9uIHBoeWxvZ2VuZXRpYyBkaXN0YW5jZSBiZXR3ZWVuIGVhY2ggREhGUiBob21vbG9nIHJlbGF0aXZlIHRvIHRoZSBFLiBjb2xpIERIRlIgZ2VuZSBhbmQgYWRkIGFuIGFkZGl0aW9uYWwgcmluZyByZXByZXNlbnRpbmcgdGF4b25vbWljIGxpbmVhZ2VzIGF0IHRoZSAqKlBoeWx1bS1sZXZlbCoqLgoKPGZvbnQgY29sb3I9ImJsdWUiPk5vdGUgdGhhdCB0aGUgcGh5bHVtICJQc2V1ZG9tb25hZG90YSIgaGFzIGJlZW4gcmVwbGFjZWQgd2l0aCBpdCdzIGFzc29jaWF0ZWQgY2xhc3NlcyAoIkFscGhhcHJvdGVvYmFjdGVyaWEiLCAiQmV0YXByb3Rlb2JhY3RlcmlhIiwgZXRjLikuIFRoaXMgdHJlZSBpbmNsdWRlcyBBcmNoYWVhIChFdXJ5YXJjaGFlb3RhKSBhbmQgZHNETkEgVmlydXNlcyAoVXJvdmlyaWNvdGEpLCBidXQgbm8gRXVrYXJ5b3RhIGZyb20gdGhlIG9yaWdpbmFsIDEsNTM2LURIRlIgbGlicmFyeS48L2ZvbnQ+CmBgYHtyfQpwaHlsb3RyZWVfTkNCSV9lY29saV9waHlsdW1fcDEgPC0gZ2d0cmVlKEFsbHRyZWUxNSwgbGF5b3V0PSJjaXJjdWxhciIsIGJyYW5jaC5sZW5ndGg9Im5vbmUiKSAlPCslIAogIEFsbHRyZWUxNV90YXhhX21lcmdlZCArIAogIGFlcyhjb2xvcj1QY3RJZGVudEVjb2xpKjEwMCkgKwogIGdlb21fdGlwcG9pbnQoYWVzKGNvbG9yPVBjdElkZW50RWNvbGkqMTAwKSwgc2l6ZT0xLCBhbHBoYT0wLjgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249ImxlZnQiKSArIAogIHNjYWxlX2NvbG91cl9ncmFkaWVudDIoIlNlcXVlbmNlXG5JZGVudGl0eSAoJSkiLCBsb3c9ImJsdWUiLCBtaWQ9ImdyZWVuIiwgaGlnaD0icmVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgIG1pZHBvaW50PSgxMDArbWluKEFsbHRyZWUxNV90YXhhX21lcmdlZCRQY3RJZGVudEVjb2xpKSoxMDApLzIpCgpwaHlsb3RyZWVfTkNCSV9lY29saV9waHlsdW1fcDIgPC0gcGh5bG90cmVlX05DQklfZWNvbGlfcGh5bHVtX3AxICsKICBuZXdfc2NhbGVfZmlsbCgpICsKICBnZW9tX2ZydWl0KAogICAgZ2VvbT1nZW9tX3RpbGUsCiAgICBtYXBwaW5nPWFlcyhmaWxsPU5DQkkucGh5bHVtKSwKICAgIHdpZHRoPTQsCiAgICBvZmZzZXQ9MC4xCiAgKSArCiAgc2NhbGVfZmlsbF9tYW51YWwoCiAgICBuYW1lPSJQaHlsdW0iLAogICAgdmFsdWVzPU5DQklwaHlsb0NvbG9yJE5DQkkucGh5bHVtX2NvbG9ycywKICAgIGd1aWRlPWd1aWRlX2xlZ2VuZChrZXl3aWR0aD0wLjMsIGtleWhlaWdodD0wLjMsIG5jb2w9MSkKICApICsKICB0aGVtZSgKICAgIGxlZ2VuZC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksIAogICAgICAgICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgICAgIGxlZ2VuZC5zcGFjaW5nLnkgPSB1bml0KDAuMiwgImNtIikKICApCgpwaHlsb3RyZWVfTkNCSV9lY29saV9waHlsdW1fcDIKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iUGVyZmVjdHMvUExPVFMvVHJlZXMvQWxsMTIwOC9ESEZSLkxpYjE1LjE2LkZhc3RUcmVlLjEyMDgubWFwcGVkLnBlcmZlY3RzLk5DQkkuZWNvbGkucGN0aWRlbnQucGh5bHVtLnJpbmcucG5nIiwgcGxvdD1waHlsb3RyZWVfTkNCSV9lY29saV9waHlsdW1fcDIsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA3LjUsIHVuaXRzID0gImluIikKYGBgCgpCdWlsZCB0aGUgaW5pdGlhbCBESEZSIHRyZWUgd2l0aCBjb2xvci1zY2FsZSBiYXNlZCBvbiBwaHlsb2dlbmV0aWMgZGlzdGFuY2UgYmV0d2VlbiBlYWNoIERIRlIgaG9tb2xvZyByZWxhdGl2ZSB0byB0aGUgRS4gY29saSBESEZSIGdlbmUgYW5kIGFkZCBhbiBhZGRpdGlvbmFsIHJpbmcgcmVwcmVzZW50aW5nIHRheG9ub21pYyBsaW5lYWdlcyBhdCB0aGUgKipDbGFzcy1sZXZlbC4qKgpgYGB7cn0KcGh5bG90cmVlX05DQklfZWNvbGlfY2xhc3NfcDEgPC0gZ2d0cmVlKEFsbHRyZWUxNSwgbGF5b3V0PSJjaXJjdWxhciIsIGJyYW5jaC5sZW5ndGg9Im5vbmUiKSAlPCslIAogIEFsbHRyZWUxNV90YXhhX21lcmdlZCArIAogIGFlcyhjb2xvcj1QY3RJZGVudEVjb2xpKjEwMCkgKwogIGdlb21fdGlwcG9pbnQoYWVzKGNvbG9yPVBjdElkZW50RWNvbGkqMTAwKSwgc2l6ZT0xLCBhbHBoYT0wLjgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249ImxlZnQiKSArIAogIHNjYWxlX2NvbG91cl9ncmFkaWVudDIoIlNlcXVlbmNlXG5JZGVudGl0eSAoJSkiLCBsb3c9ImJsdWUiLCBtaWQ9ImdyZWVuIiwgaGlnaD0icmVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgIG1pZHBvaW50PSgxMDArbWluKEFsbHRyZWUxNV90YXhhX21lcmdlZCRQY3RJZGVudEVjb2xpKSoxMDApLzIpCgpwaHlsb3RyZWVfTkNCSV9lY29saV9jbGFzc19wMiA8LSBwaHlsb3RyZWVfTkNCSV9lY29saV9jbGFzc19wMSArCiAgbmV3X3NjYWxlX2ZpbGwoKSArCiAgZ2VvbV9mcnVpdCgKICAgIGdlb209Z2VvbV90aWxlLAogICAgbWFwcGluZz1hZXMoZmlsbD1OQ0JJLmNsYXNzKSwKICAgIHdpZHRoPTQsCiAgICBvZmZzZXQ9MC4xCiAgKSArCiAgc2NhbGVfZmlsbF9tYW51YWwoCiAgICBuYW1lPSJDbGFzcyIsCiAgICB2YWx1ZXM9TkNCSWNsYXNzQ29sb3IkTkNCSS5jbGFzc19jb2xvcnMsCiAgICBndWlkZT1ndWlkZV9sZWdlbmQoa2V5d2lkdGg9MC4zLCBrZXloZWlnaHQ9MC4zLCBuY29sPTEpCiAgKSArCiAgdGhlbWUoCiAgICBsZWdlbmQudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTApLCAKICAgICAgICAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICAgICBsZWdlbmQuc3BhY2luZy55ID0gdW5pdCgwLjIsICJjbSIpCiAgKQoKcGh5bG90cmVlX05DQklfZWNvbGlfY2xhc3NfcDIKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iUGVyZmVjdHMvUExPVFMvVHJlZXMvQWxsMTIwOC9ESEZSLkxpYjE1LjE2LkZhc3RUcmVlLjEyMDgubWFwcGVkLnBlcmZlY3RzLk5DQkkuZWNvbGkucGN0aWRlbnQuY2xhc3MucmluZy5wbmciLCBwbG90PXBoeWxvdHJlZV9OQ0JJX2Vjb2xpX2NsYXNzX3AyLAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNy41LCB1bml0cyA9ICJpbiIpCmBgYAoKU2hvdyBib3RoIHZlcnNpb24gb2YgdGhlIHBoeWxvZ2VueSB0cmVlIChQaHlsdW0gYW5kIENsYXNzIGxldmVsIGFubm90YXRpb25zKToKYGBge3J9CnBhdGNoOCA8LSAocGh5bG90cmVlX05DQklfZWNvbGlfcGh5bHVtX3AyIHwgcGh5bG90cmVlX05DQklfZWNvbGlfY2xhc3NfcDIpCnBhdGNoOApgYGAKCmBgYHtyIGVjaG89RkFMU0V9Cmdnc2F2ZShmaWxlPSJQZXJmZWN0cy9QTE9UUy9UcmVlcy9BbGwxMjA4L0RIRlIuTGliMTUuMTYuRmFzdFRyZWUuMTIwOC5tYXBwZWQucGVyZmVjdHMuTkNCSS5lY29saS5wY3RpZGVudC5waHlsdW0uY2xhc3MucmluZy5wbmciLCBwbG90PXBhdGNoOCwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gMTIsIGhlaWdodCA9IDcuNSwgdW5pdHMgPSAiaW4iKQpgYGAKCiMjIyMjIFRheG9ub21pYyBUaXAgTGFiZWxzCgpSZS1idWlsZCB0aGUgcGh5bG9nZW5ldGljIHRyZWUgYnV0IGNvbG9yIHRoZSBicmFuY2hlcyBieSB0aGUgTkNCSSAqKnBoeWx1bS1sZXZlbCoqIHRheG9ub215IChpbnN0ZWFkIG9mIGJ5IHBjdElkZW50IEUuIGNvbGkpLiBMZWF2ZSB0aGUgcGh5bHVtIG5hbWUgbGFiZWxzIG9uIHRoZSB0cmVlIGFzIHJlZmVyZW5jZS4KYGBge3J9CnBoeWxvdHJlZV9OQ0JJX2Vjb2xpX3BoeWx1bV9uYW1lc19wMSA8LSBnZ3RyZWUoQWxsdHJlZTE1LCBsYXlvdXQ9ImNpcmN1bGFyIiwgYnJhbmNoLmxlbmd0aD0ibm9uZSIpICU8KyUKICBBbGx0cmVlMTVfdGF4YV9tZXJnZWQgKyAKICBhZXMoY29sb3I9TkNCSS5waHlsdW0pICsKICBnZW9tX3RpcHBvaW50KGFlcyhjb2xvcj1OQ0JJLnBoeWx1bSksIHNpemU9MSwgYWxwaGE9MC44KSArCiAgZ2VvbV90aXBsYWIyKGFlcyhjb2xvcj1OQ0JJLnBoeWx1bSwgbGFiZWw9TkNCSS5waHlsdW0sIGFuZ2xlPWFuZ2xlKSwgc2l6ZT0xKQoKI3AxM05DQklfMTV0YXhhMgoKcGh5bG90cmVlX05DQklfZWNvbGlfcGh5bHVtX25hbWVzX3AyIDwtIHBoeWxvdHJlZV9OQ0JJX2Vjb2xpX3BoeWx1bV9uYW1lc19wMSArCiAgZ2VvbV90aXBwb2ludCgKICAgIG1hcHBpbmc9YWVzKGNvbG91cj1OQ0JJLnBoeWx1bSksCiAgICBzaXplPTEuNSwKICAgIHN0cm9rZT0wLAogICAgYWxwaGE9MC40CiAgKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCgKICAgIG5hbWU9IlBoeWx1bSIsCiAgICB2YWx1ZXM9TkNCSXBoeWxvQ29sb3IkTkNCSS5waHlsdW1fY29sb3JzLAogICAgZ3VpZGU9Z3VpZGVfbGVnZW5kKGtleXdpZHRoPTAuMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtleWhlaWdodD0wLjMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuY29sPTEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvdmVycmlkZS5hZXM9bGlzdChzaXplPTIsYWxwaGE9MSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcj0xKQogICkgKwogIHRoZW1lKAogICAgbGVnZW5kLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTApLAogICAgICAgICBsZWdlbmQuc3BhY2luZy55ID0gdW5pdCgwLjEsICJjbSIpCiAgKQoKcGh5bG90cmVlX05DQklfZWNvbGlfcGh5bHVtX25hbWVzX3AyCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL1RyZWVzL0FsbDEyMDgvREhGUi5MaWIxNS4xNi5GYXN0VHJlZS4xMjA4Lm1hcHBlZC5wZXJmZWN0cy5OQ0JJLnBoeWx1bS50aXAubmFtZXMucG5nIiwKICAgICAgIHBsb3Q9cGh5bG90cmVlX05DQklfZWNvbGlfcGh5bHVtX25hbWVzX3AyLAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNy41LCB1bml0cyA9ICJpbiIpCmBgYAoKUmUtYnVpbGQgdGhlIHBoeWxvZ2VuZXRpYyB0cmVlIGJ1dCBjb2xvciB0aGUgYnJhbmNoZXMgYnkgdGhlIE5DQkkgKipjbGFzcy1sZXZlbCoqIHRheG9ub215IChpbnN0ZWFkIG9mIGJ5IHBjdElkZW50IEUuIGNvbGkpLiBMZWF2ZSB0aGUgY2xhc3MgbmFtZSBsYWJlbHMgb24gdGhlIHRyZWUgYXMgcmVmZXJlbmNlLgpgYGB7cn0KcGh5bG90cmVlX05DQklfZWNvbGlfY2xhc3NfbmFtZXNfcDEgPC0gZ2d0cmVlKEFsbHRyZWUxNSwgbGF5b3V0PSJjaXJjdWxhciIsIGJyYW5jaC5sZW5ndGg9Im5vbmUiKSAlPCslCiAgQWxsdHJlZTE1X3RheGFfbWVyZ2VkICsgCiAgYWVzKGNvbG9yPU5DQkkuY2xhc3MpICsKICBnZW9tX3RpcHBvaW50KGFlcyhjb2xvcj1OQ0JJLmNsYXNzKSwgc2l6ZT0xLCBhbHBoYT0wLjgpICsKICBnZW9tX3RpcGxhYjIoYWVzKGNvbG9yPU5DQkkuY2xhc3MsIGxhYmVsPU5DQkkuY2xhc3MsIGFuZ2xlPWFuZ2xlKSwgc2l6ZT0xKQoKI3AxM05DQklfMTV0YXhhMgoKcGh5bG90cmVlX05DQklfZWNvbGlfY2xhc3NfbmFtZXNfcDIgPC0gcGh5bG90cmVlX05DQklfZWNvbGlfY2xhc3NfbmFtZXNfcDEgKwogIGdlb21fdGlwcG9pbnQoCiAgICBtYXBwaW5nPWFlcyhjb2xvdXI9TkNCSS5jbGFzcyksCiAgICBzaXplPTEuNSwKICAgIHN0cm9rZT0wLAogICAgYWxwaGE9MC40KSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCgKICAgIG5hbWU9IkNsYXNzIiwKICAgIHZhbHVlcz1OQ0JJY2xhc3NDb2xvciROQ0JJLmNsYXNzX2NvbG9ycywKICAgIGd1aWRlPWd1aWRlX2xlZ2VuZChrZXl3aWR0aD0wLjMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZXloZWlnaHQ9MC4zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbD0xLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgb3ZlcnJpZGUuYWVzPWxpc3Qoc2l6ZT0yLGFscGhhPTEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXI9MSkpICsKICB0aGVtZSgKICAgIGxlZ2VuZC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICAgICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEwKSwKICAgICAgICAgbGVnZW5kLnNwYWNpbmcueSA9IHVuaXQoMC4xLCAiY20iKSkKCnBoeWxvdHJlZV9OQ0JJX2Vjb2xpX2NsYXNzX25hbWVzX3AyCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL1RyZWVzL0FsbDEyMDgvREhGUi5MaWIxNS4xNi5GYXN0VHJlZS4xMjA4Lm1hcHBlZC5wZXJmZWN0cy5OQ0JJLmNsYXNzLnRpcC5uYW1lcy5wbmciLCAKICAgICAgIHBsb3Q9cGh5bG90cmVlX05DQklfZWNvbGlfY2xhc3NfbmFtZXNfcDIsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA3LjUsIHVuaXRzID0gImluIikKYGBgCgojIyMjIyBUYXhhIFN1bW1hcnkgVGFibGVzCgoqKlRheG9ub215OioqIENvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIElEcyBhc3NvY2lhdGVkIHdpdGggZWFjaCB0YXhvbm9taWMgbGV2ZWw6CmBgYHtyfQojIENvdW50IHVuaXF1ZSB2YWx1ZXMgaW4gZWFjaCBjb2x1bW4KdW5pcXVlX3RheGFfY291bnRzIDwtIEFsbHRyZWUxNV90YXhhX21lcmdlZCAlPiUKICBzdW1tYXJpc2UoYWNyb3NzKGMoTkNCSS5zdXBlcmtpbmdkb20sIE5DQkkucGh5bHVtLCBOQ0JJLmNsYXNzLCBOQ0JJLm9yZGVyLCBOQ0JJLmZhbWlseSwgTkNCSS5nZW51cywgTkNCSS5zcGVjaWVzKSwKICAgICAgfiBuX2Rpc3RpbmN0KC4pKSkKCiMgUHJpbnQgdGhlIHJlc3VsdApwcmludCh1bmlxdWVfdGF4YV9jb3VudHMpCmBgYAoqKkRvbWFpbjoqKiBDb3VudCB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBJRHMgYXNzb2NpYXRlZCB3aXRoIGVhY2ggc3VwZXJraW5nZG9tIChkb21haW4pOgpgYGB7cn0KIyBTdW0gdGhlIG51bWJlciBvZiByb3dzIGZvciBlYWNoIHVuaXF1ZSBOQ0JJLnBoeWx1bQpkb21haW5fY291bnRzIDwtIEFsbHRyZWUxNV90YXhhX21lcmdlZCAlPiUKICBncm91cF9ieShOQ0JJLnN1cGVya2luZ2RvbSkgJT4lCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUKICBhcnJhbmdlKGRlc2MoY291bnQpKQoKIyBQcmludCB0aGUgdGFibGUKcHJpbnQoZG9tYWluX2NvdW50cykKYGBgCgoqKlBoeWx1bToqKiBDb3VudCB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBJRHMgYXNzb2NpYXRlZCB3aXRoIGVhY2ggUGh5bHVtOgpgYGB7cn0KIyBTdW0gdGhlIG51bWJlciBvZiByb3dzIGZvciBlYWNoIHVuaXF1ZSBOQ0JJLnBoeWx1bQpwaHlsdW1fY291bnRzIDwtIEFsbHRyZWUxNV90YXhhX21lcmdlZCAlPiUKICBncm91cF9ieShOQ0JJLnBoeWx1bSkgJT4lCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUKICBhcnJhbmdlKGRlc2MoY291bnQpKQoKIyBQcmludCB0aGUgdGFibGUKcHJpbnQocGh5bHVtX2NvdW50cykKYGBgCgoqKkNsYXNzOioqIENvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIElEcyBhc3NvY2lhdGVkIHdpdGggZWFjaCBDbGFzczoKYGBge3J9CiMgU3VtIHRoZSBudW1iZXIgb2Ygcm93cyBmb3IgZWFjaCB1bmlxdWUgTkNCSS5jbGFzcwpjbGFzc19jb3VudHMgPC0gQWxsdHJlZTE1X3RheGFfbWVyZ2VkICU+JQogIGdyb3VwX2J5KE5DQkkuY2xhc3MpICU+JQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lCiAgYXJyYW5nZShkZXNjKGNvdW50KSkKCiMgUHJpbnQgdGhlIHRhYmxlCnByaW50KGNsYXNzX2NvdW50cykKYGBgCgoqKk9yZGVyOioqIENvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIElEcyBhc3NvY2lhdGVkIHdpdGggZWFjaCBPcmRlcjoKYGBge3J9CiMgU3VtIHRoZSBudW1iZXIgb2Ygcm93cyBmb3IgZWFjaCB1bmlxdWUgTkNCSS5jbGFzcwpvcmRlcl9jb3VudHMgPC0gQWxsdHJlZTE1X3RheGFfbWVyZ2VkICU+JQogIGdyb3VwX2J5KE5DQkkub3JkZXIpICU+JQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lCiAgYXJyYW5nZShkZXNjKGNvdW50KSkKCiMgUHJpbnQgdGhlIHRhYmxlCnByaW50KG9yZGVyX2NvdW50cykKYGBgCgoqKkZhbWlseToqKiBDb3VudCB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBJRHMgYXNzb2NpYXRlZCB3aXRoIGVhY2ggRmFtaWx5OgpgYGB7cn0KIyBTdW0gdGhlIG51bWJlciBvZiByb3dzIGZvciBlYWNoIHVuaXF1ZSBOQ0JJLmNsYXNzCmZhbWlseV9jb3VudHMgPC0gQWxsdHJlZTE1X3RheGFfbWVyZ2VkICU+JQogIGdyb3VwX2J5KE5DQkkuZmFtaWx5KSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQogIGFycmFuZ2UoZGVzYyhjb3VudCkpCgojIFByaW50IHRoZSB0YWJsZQpwcmludChmYW1pbHlfY291bnRzKQpgYGAKCioqR2VudXM6KiogQ291bnQgdGhlIG51bWJlciBvZiB1bmlxdWUgSURzIGFzc29jaWF0ZWQgd2l0aCBlYWNoIEdlbnVzOgpgYGB7cn0KIyBTdW0gdGhlIG51bWJlciBvZiByb3dzIGZvciBlYWNoIHVuaXF1ZSBOQ0JJLmNsYXNzCmdlbnVzX2NvdW50cyA8LSBBbGx0cmVlMTVfdGF4YV9tZXJnZWQgJT4lCiAgZ3JvdXBfYnkoTkNCSS5nZW51cykgJT4lCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUKICBhcnJhbmdlKGRlc2MoY291bnQpKQoKIyBQcmludCB0aGUgdGFibGUKcHJpbnQoZ2VudXNfY291bnRzKQpgYGAKCioqU3BlY2llczoqKiBDb3VudCB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBJRHMgYXNzb2NpYXRlZCB3aXRoIGVhY2ggU3BlY2llczoKYGBge3J9CiMgU3VtIHRoZSBudW1iZXIgb2Ygcm93cyBmb3IgZWFjaCB1bmlxdWUgTkNCSS5jbGFzcwpzcGVjaWVzX2NvdW50cyA8LSBBbGx0cmVlMTVfdGF4YV9tZXJnZWQgJT4lCiAgZ3JvdXBfYnkoTkNCSS5zcGVjaWVzKSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQogIGFycmFuZ2UoZGVzYyhjb3VudCkpCgojIFByaW50IHRoZSB0YWJsZQpwcmludChzcGVjaWVzX2NvdW50cykKYGBgCgojIyMgTGliMTUgRml0bmVzcyBUcmVlICg3OTcpCgojIyMjIFN1YnNldHRpbmcgRGF0YQoKUmV0YWluIHVuaXF1ZSBESEZSIHBlcmZlY3RzIHdpdGggYSBmaXRuZXNzIHNjb3JlID49IC0xIGFuZCBhIG1pbmltdW0gb2YgNSBCQ3MgKG51bXBydW5lZEJDcyA+IDQpIGluIExpYjE1IChmaXREMDVEMDMpOgpgYGB7cn0KIyBTdWJzZXQgZGF0YXNldCBmb3IgdHJlZSBidWlsZGluZwpMMTVfcGVyZmVjdHNfY29tcGxlbWVudGF0aW9uX3RyZWUgPC0gcGVyZmVjdHMxNV81QkNzICU+JQogIHNlbGVjdChtdXRJRCwgc2VxLAogICAgICAgICBmaXREMDVEMDMsIGZpdEQwNkQwMywgZml0RDA3RDAzLCBmaXREMDhEMDMsIGZpdEQwOUQwMywgZml0RDEwRDAzLCBmaXREMTFEMDMpCmBgYAoKQ291bnQgcGVyZmVjdHMgYXNzb2NpYXRlZCB3aXRoIGZpdEQwNUQwMyAoQ29tcGxlbWVudGF0aW9uKToKYGBge3J9CiMgQ291bnQgdW5pcXVlIHBlcmZlY3RzIGluIGRhdGFzZXQKY291bnRfdW5pcXVlX2NvbXBfcGVyZmVjdHMgPC0gTDE1X3BlcmZlY3RzX2NvbXBsZW1lbnRhdGlvbl90cmVlICU+JQogIGZpbHRlcighaXMubmEoZml0RDA1RDAzKSkgJT4lCiAgZGlzdGluY3QoZml0RDA1RDAzKSAlPiUKICBucm93KCkKcHJpbnQocGFzdGUoIk51bWJlciBvZiB1bmlxdWUgcGVyZmVjdHM6IiwgY291bnRfdW5pcXVlX2NvbXBfcGVyZmVjdHMpKQoKIyBDb3VudCB1bmlxdWUgcGVyZmVjdHMgd2l0aCBmaXRuZXNzIGdyZWF0ZXIgdGhhbiAtMSBpbiBmaXREMDVEMDMgKENvbXBsZW1lbnRhdGlvbik6CmNvdW50X3VuaXF1ZV9maXRfY29tcF9oaWdoIDwtIEwxNV9wZXJmZWN0c19jb21wbGVtZW50YXRpb25fdHJlZSAlPiUKICBmaWx0ZXIoIWlzLm5hKGZpdEQwNUQwMykgJiBmaXREMDVEMDMgPj0gLTEpICU+JQogIGRpc3RpbmN0KGZpdEQwNUQwMykgJT4lCiAgbnJvdygpCnByaW50KHBhc3RlKCJOdW1iZXIgb2YgdW5pcXVlIHBlcmZlY3RzIHdpdGggZml0RDA1RDAzIHZhbHVlcyBncmVhdGVyIHRoYW4gLTE6IiwgY291bnRfdW5pcXVlX2ZpdF9jb21wX2hpZ2gpKQoKIyBDb3VudCB1bmlxdWUgcGVyZmVjdHMgd2l0aCBmaXRuZXNzIGxlc3MgdGhhbiAtMSBpbiBmaXREMDVEMDMgKENvbXBsZW1lbnRhdGlvbik6CmNvdW50X3VuaXF1ZV9maXRfY29tcF9sb3cgPC0gTDE1X3BlcmZlY3RzX2NvbXBsZW1lbnRhdGlvbl90cmVlICU+JQogIGZpbHRlcighaXMubmEoZml0RDA1RDAzKSAmIGZpdEQwNUQwMyA8PSAtMSkgJT4lCiAgZGlzdGluY3QoZml0RDA1RDAzKSAlPiUKICBucm93KCkKcHJpbnQocGFzdGUoIk51bWJlciBvZiB1bmlxdWUgcGVyZmVjdHMgd2l0aCBmaXREMDVEMDMgdmFsdWVzIGxlc3MgdGhhbiAtMToiLCBjb3VudF91bmlxdWVfZml0X2NvbXBfbG93KSkKYGBgCgpBZGQgInRheElEIiBhbmQgIlBjdElkZW50RWNvbGkiIHRvIGVhY2ggIm11dElEIgpgYGB7cn0KIyBSZW5hbWUgIm11dElEIiB0byAiSUQiIHRvIG1lcmdlIHdpdGggYG9yZ2luZm9gIGRmOgpMMTVfcGVyZmVjdHNfY29tcGxlbWVudGF0aW9uX3RyZWUgPC0gTDE1X3BlcmZlY3RzX2NvbXBsZW1lbnRhdGlvbl90cmVlICU+JSByZW5hbWUoSUQgPSBtdXRJRCkKCiMgTWVyZ2UgIlRheElEIiBhbmQgIlBjdElkZW50RWNvbGkiIGZyb20gYG9yZ2luZm9gIHRvIGBMMTVfcGVyZmVjdHNfY29tcGxlbWVudGF0aW9uX3RyZWVgOgpMMTVfcGVyZmVjdHNfY29tcGxlbWVudGF0aW9uX3RyZWUgPC0gTDE1X3BlcmZlY3RzX2NvbXBsZW1lbnRhdGlvbl90cmVlICU+JQogIGxlZnRfam9pbihvcmdpbmZvICU+JSBzZWxlY3QoSUQsIFRheElELCBQY3RJZGVudEVjb2xpKSwgYnkgPSAiSUQiKQpgYGAKCkNyZWF0ZSBhIEZBU1RBIGZpbGUgY29udGFpbmluZyB0aGUgSUQgYW5kIGl0cyBhc3NvY2lhdGVkIHByb3RlaW4gc2VxdWVuY2UgZm9yIGFsaWdubWVudApgYGB7ciwgcmVzdWx0cz0naGlkZSd9CiMgQ29sbGVjdCB0aGUgc2VxdWVuY2VzIGluIEZBU1RBIGZvcm1hdApMMTVfcGVyZmVjdHNfY29tcGxlbWVudGF0aW9uX3RyZWVfZmFzdGFfY29udGVudCA8LSBwYXN0ZSgiPiIsTDE1X3BlcmZlY3RzX2NvbXBsZW1lbnRhdGlvbl90cmVlJElELCAiXG4iLCBMMTVfcGVyZmVjdHNfY29tcGxlbWVudGF0aW9uX3RyZWUkc2VxLCAiXG4iLCBzZXAgPSAiIiwgY29sbGFwc2UgPSAiIikKCiMgRGVmaW5lIHRoZSBmaWxlIHBhdGggaW4gdGhlIHdvcmtpbmcgZGlyZWN0b3J5CkwxNV9wZXJmZWN0c19jb21wbGVtZW50YXRpb25fdHJlZV9mYXN0YV9wYXRoIDwtIGZpbGUucGF0aChnZXR3ZCgpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQZXJmZWN0cy9UUkVFUy9MaWIuMTUuNUJDcy5wZXJmZWN0cy5jb21wbGVtZW50LmZhc3RhIikKCiMgV3JpdGUgdGhlIEZBU1RBIGNvbnRlbnQgdG8gdGhlIGZpbGUKd3JpdGVMaW5lcyhMMTVfcGVyZmVjdHNfY29tcGxlbWVudGF0aW9uX3RyZWVfZmFzdGFfY29udGVudCwgY29uID0gTDE1X3BlcmZlY3RzX2NvbXBsZW1lbnRhdGlvbl90cmVlX2Zhc3RhX3BhdGgpCmBgYAoKUGVyZm9ybSBhIG11bHRpcGxlIHNlcXVlbmNlIGFsaWdubWVudCBvbiB0aGUgRkFTVEEgZmlsZSB1c2luZyBDTFVTVEFMIE9tZWdhOgpgYGB7YmFzaCByZXN1bHRzID0gJ2hpZGUnfQojIE1heSBuZWVkIHRvIGVuYWJsZSBwZXJtaXNzaW9ucyB0byBydW4gdGhlIGV4ZWN1dGFibGU6CiNjaG1vZCAreCBjbHVzdGFsbwouL1NjcmlwdHMvY2x1c3RhbG8gLWkgUGVyZmVjdHMvVFJFRVMvTGliLjE1LjVCQ3MucGVyZmVjdHMuY29tcGxlbWVudC5mYXN0YSAtbyBQZXJmZWN0cy9UUkVFUy9MaWIuMTUuNUJDcy5wZXJmZWN0cy5jb21wbGVtZW50LmFsaWduZWQuZmFzdGEgLS1vdXRmbXQ9ZmEgLS1mb3JjZQpgYGAKClVzZSBGYXN0VHJlZSAocGh5bG9nZW5ldGljIHRyZWUgYnVpbGRpbmcgcHJvZ3JhbSkgdG8gaW5mZXIgdGhlIHRyZWUgZnJvbSB0aGUgYWxpZ25lZCBhbWlubyBhY2lkIHNlcXVlbmNlczoKYGBge2Jhc2h9CiMgTUwgTW9kZWw6IEpvbmVzLVRheWxvci1UaG9ydG9uCiMgY2htb2QgK3ggRmFzdFRyZWUKLi9TY3JpcHRzL0Zhc3RUcmVlIFBlcmZlY3RzL1RSRUVTL0xpYi4xNS41QkNzLnBlcmZlY3RzLmNvbXBsZW1lbnQuYWxpZ25lZC5mYXN0YSA+IFBlcmZlY3RzL1RSRUVTL0xpYi4xNS41QkNzLnBlcmZlY3RzLmNvbXBsZW1lbnQudHJlZS5uZXdpY2sKYGBgCgojIyMjIyBOZXdpY2sgVHJlZSBGaWxlCkltcG9ydCB0aGUgbmV3aWNrIHRyZWUgZmlsZSBiYXNlZCBvbiB0aGUgc2VxdWVuY2UgYWxpZ25tZW50IG9mIHNoYXJlZCBwZXJmZWN0cyBkZXJpdmVkIGZyb20gTGliMTUgJiBMaWIxNiBtYXBwaW5nIGZpbGU6CmBgYHtyfQojIEZ1bGwgdHJlZSBhbGlnbm1lbnQgKDQxNyBtdXRJRCkKRml0dHJlZTE1IDwtIHJlYWQudHJlZSgiUGVyZmVjdHMvVFJFRVMvTGliLjE1LjVCQ3MucGVyZmVjdHMuY29tcGxlbWVudC50cmVlLm5ld2ljayIpICAgIyBuZXdpY2sgZm9ybWF0CkZpdHRyZWUxNQpgYGAKCkV4dHJhY3QgdGhlIHRpcCBsYWJlbHMgZnJvbSB0aGUgbmV3aWNrIHRyZWUgZmlsZSB0byBtYXRjaCB3aXRoIE5DQkkgdGF4b25vbXkgZm9yIGRvd25zdHJlYW0gcGxvdHRpbmc6CmBgYHtyfQojIEV4dHJhY3QgdGlwIGxhYmVscyBmcm9tIEZpdHRyZWUxNQpGaXR0cmVlMTVfdGlwX2xhYmVscyA8LSBGaXR0cmVlMTUkdGlwLmxhYmVsCgojIENyZWF0ZSBhIG5ldyBkYXRhIGZyYW1lIHdpdGggdW5pcXVlIHRpcCBsYWJlbHMKRml0dHJlZTE1X3RpcF9sYWJlbHNfZGYgPC0gZGF0YS5mcmFtZSh0aXAubGFiZWwgPSB1bmlxdWUoRml0dHJlZTE1X3RpcF9sYWJlbHMpKQoKIyBQcmludCB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgdGhlIG5ldyBkYXRhIGZyYW1lCmhlYWQoRml0dHJlZTE1X3RpcF9sYWJlbHNfZGYpCmBgYAoKTWF0Y2ggZWFjaCB0aXAubGFiZWwgSUQgaW4gYEFsbHRyZWUxNWAgd2l0aCBpdCdzIGFzc29jaWF0ZWQgVGF4SUQgZnJvbSBgb3JnaW5mb2AgZGF0YWZyYW1lOgpgYGB7cn0KIyBSZW5hbWUgY29sdW1uICJ0aXAubGFiZWwiIHRvICJJRCIKY29sbmFtZXMoRml0dHJlZTE1X3RpcF9sYWJlbHNfZGYpIDwtIGMoIklEIikKCiMgTWVyZ2Ugb3JnaW5mbyB3aXRoIEFsbHRyZWUxNV90aXBfbGFiZWxzX2RmIGJhc2VkIG9uIHRoZSBzaGFyZWQgSUQKRml0dHJlZTE1X3RheGEgPC0gbWVyZ2UoRml0dHJlZTE1X3RpcF9sYWJlbHNfZGYsIG9yZ2luZm8sIGJ5ID0gIklEIiwgYWxsLnggPSBUUlVFKQoKIyBQcmludCB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgdGhlIG1lcmdlZCBkYXRhIGZyYW1lCmhlYWQoRml0dHJlZTE1X3RheGEpCmBgYAoKQWRkIGZpdG5lc3Mgc2NvcmVzIHRvIHRoZSBgRml0dHJlZTE1X3RheGFfbWVyZ2VkYCBvYmplY3QgcHJpb3IgdG8gcGxvdHRpbmcKYGBge3J9CkZpdHRyZWUxNV90YXhhX21lcmdlZCA8LSBGaXR0cmVlMTVfdGF4YSAlPiUKICBsZWZ0X2pvaW4oTDE1X3BlcmZlY3RzX2NvbXBsZW1lbnRhdGlvbl90cmVlICU+JSBzZWxlY3QoSUQsIGZpdEQwNUQwMywgZml0RDA2RDAzLCBmaXREMDdEMDMsIGZpdEQwOEQwMywgZml0RDA5RDAzLCBmaXREMTBEMDMsIGZpdEQxMUQwMyksIGJ5ID0gIklEIikKYGBgCgpNZXJnZSB0aGUgTkNCSSB0YXhvbm9teSBjb2x1bW5zIHRvIGBGaXR0cmVlMTVfdGF4YWAgYmFzZWQgb24gc2hhcmVkIFRheElECmBgYHtyfQojIE1lcmdlIHRoZSBOQ0JJIHRheG9ub215IGNvbHVtbnMgdG8gRml0dHJlZTE1X3RheGEgYmFzZWQgb24gc2hhcmVkIFRheElECkZpdHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLm5hbWUgPC0gTkEKRml0dHJlZTE1X3RheGFfbWVyZ2VkJE5DQkkuc3VwZXJraW5nZG9tIDwtIE5BCkZpdHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLnBoeWx1bSA8LSBOQQpGaXR0cmVlMTVfdGF4YV9tZXJnZWQkTkNCSS5jbGFzcyA8LSBOQQpGaXR0cmVlMTVfdGF4YV9tZXJnZWQkTkNCSS5vcmRlciA8LSBOQQpGaXR0cmVlMTVfdGF4YV9tZXJnZWQkTkNCSS5mYW1pbHkgPC0gTkEKRml0dHJlZTE1X3RheGFfbWVyZ2VkJE5DQkkuZ2VudXMgPC0gTkEKRml0dHJlZTE1X3RheGFfbWVyZ2VkJE5DQkkuc3BlY2llcyA8LSBOQQoKIyBOQ0JJLm5hbWUKRml0dHJlZTE1X3RheGFfbWVyZ2VkJE5DQkkubmFtZVtGaXR0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdIDwtIG5jYmlfdGF4YSROQ0JJLm5hbWVbbWF0Y2goRml0dHJlZTE1X3RheGFfbWVyZ2VkJFRheElEW0ZpdHRyZWUxNV90YXhhX21lcmdlZCRUYXhJRCAlaW4lIG5jYmlfdGF4YSRUYXhJRF0sIG5jYmlfdGF4YSRUYXhJRCldCgojIE5DQkkuc3VwZXJraW5nZG9tCkZpdHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLnN1cGVya2luZ2RvbVtGaXR0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdIDwtIG5jYmlfdGF4YSROQ0JJLnN1cGVya2luZ2RvbVttYXRjaChGaXR0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SURbRml0dHJlZTE1X3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSwgbmNiaV90YXhhJFRheElEKV0KCiMgTkNCSS5waHlsdW0KRml0dHJlZTE1X3RheGFfbWVyZ2VkJE5DQkkucGh5bHVtW0ZpdHRyZWUxNV90YXhhX21lcmdlZCRUYXhJRCAlaW4lIG5jYmlfdGF4YSRUYXhJRF0gPC0gbmNiaV90YXhhJE5DQkkucGh5bHVtW21hdGNoKEZpdHRyZWUxNV90YXhhX21lcmdlZCRUYXhJRFtGaXR0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdLCBuY2JpX3RheGEkVGF4SUQpXQoKIyBOQ0JJLmNsYXNzCkZpdHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLmNsYXNzW0ZpdHRyZWUxNV90YXhhX21lcmdlZCRUYXhJRCAlaW4lIG5jYmlfdGF4YSRUYXhJRF0gPC0gbmNiaV90YXhhJE5DQkkuY2xhc3NbbWF0Y2goRml0dHJlZTE1X3RheGFfbWVyZ2VkJFRheElEW0ZpdHRyZWUxNV90YXhhX21lcmdlZCRUYXhJRCAlaW4lIG5jYmlfdGF4YSRUYXhJRF0sIG5jYmlfdGF4YSRUYXhJRCldCgojIE5DQkkub3JkZXIKRml0dHJlZTE1X3RheGFfbWVyZ2VkJE5DQkkub3JkZXJbRml0dHJlZTE1X3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSA8LSBuY2JpX3RheGEkTkNCSS5vcmRlclttYXRjaChGaXR0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SURbRml0dHJlZTE1X3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSwgbmNiaV90YXhhJFRheElEKV0KCiMgTkNCSS5mYW1pbHkKRml0dHJlZTE1X3RheGFfbWVyZ2VkJE5DQkkuZmFtaWx5W0ZpdHRyZWUxNV90YXhhX21lcmdlZCRUYXhJRCAlaW4lIG5jYmlfdGF4YSRUYXhJRF0gPC0gbmNiaV90YXhhJE5DQkkuZmFtaWx5W21hdGNoKEZpdHRyZWUxNV90YXhhX21lcmdlZCRUYXhJRFtGaXR0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdLCBuY2JpX3RheGEkVGF4SUQpXQoKIyBOQ0JJLmZhbWlseQpGaXR0cmVlMTVfdGF4YV9tZXJnZWQkTkNCSS5nZW51c1tGaXR0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdIDwtIG5jYmlfdGF4YSROQ0JJLmdlbnVzW21hdGNoKEZpdHRyZWUxNV90YXhhX21lcmdlZCRUYXhJRFtGaXR0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdLCBuY2JpX3RheGEkVGF4SUQpXQoKIyBOQ0JJLmZhbWlseQpGaXR0cmVlMTVfdGF4YV9tZXJnZWQkTkNCSS5zcGVjaWVzW0ZpdHRyZWUxNV90YXhhX21lcmdlZCRUYXhJRCAlaW4lIG5jYmlfdGF4YSRUYXhJRF0gPC0gbmNiaV90YXhhJE5DQkkuc3BlY2llc1ttYXRjaChGaXR0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SURbRml0dHJlZTE1X3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSwgbmNiaV90YXhhJFRheElEKV0KYGBgCgojIyMjIyBQY3QgU2ltaWxhcml0eSBFIGNvbGkKClBsb3QgYSBjaXJjdWxhciBwaHlsb2dlbmV0aWMgdHJlZSB3aXRoIGNvbG9yLXNjYWxlIGJhc2VkIG9uIHBoeWxvZ2VuZXRpYyBkaXN0YW5jZSBiZXR3ZWVuIGVhY2ggREhGUiBob21vbG9nIHJlbGF0aXZlIHRvIHRoZSBFLiBjb2xpIERIRlIgZ2VuZS4KYGBge3J9CmZpdHRyZWUxNV9OQ0JJX2Vjb2xpX2lkZW50IDwtIGdndHJlZShGaXR0cmVlMTUsIGxheW91dD0iY2lyY3VsYXIiLCBicmFuY2gubGVuZ3RoPSJub25lIikgJTwrJSAKICBGaXR0cmVlMTVfdGF4YV9tZXJnZWQgKyAKICBhZXMoY29sb3I9UGN0SWRlbnRFY29saSoxMDApICsKICBnZW9tX3RpcHBvaW50KGFlcyhjb2xvcj1QY3RJZGVudEVjb2xpKjEwMCksIHNpemU9MSwgYWxwaGE9MC44KSArCiAgZ2VvbV90aXBsYWIyKGFlcyhjb2xvcj1QY3RJZGVudEVjb2xpKjEwMCwgbGFiZWw9TkNCSS5uYW1lLCBhbmdsZT1hbmdsZSksIHNpemU9MSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibGVmdCIpICsgCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50MigiU2VxdWVuY2VcbklkZW50aXR5ICglKSIsIGxvdz0iYmx1ZSIsIG1pZD0iZ3JlZW4iLCBoaWdoPSJyZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgbWlkcG9pbnQ9KDEwMCttaW4oRml0dHJlZTE1X3RheGFfbWVyZ2VkJFBjdElkZW50RWNvbGkpKjEwMCkvMikKCmZpdHRyZWUxNV9OQ0JJX2Vjb2xpX2lkZW50CmBgYAoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL1RyZWVzL0NPTVAvTGliMTUuRmFzdFRyZWUucGVyZmVjdHMuY29tcGxlbWVudGF0aW9uLjVCQ3MuTkNCSS5lY29saS5wY3RpZGVudC5uYW1lcy5wbmciLCBwbG90PWZpdHRyZWUxNV9OQ0JJX2Vjb2xpX2lkZW50LAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNy41LCB1bml0cyA9ICJpbiIpCmBgYAoKIyMjIyMgVGF4b25vbWljIFJpbmdzCgpUaGUgZm9sbG93aW5nIHNlY3Rpb24gYnVpbGRzIG9uIHRoZSBpbml0aWFsIERIRlIgcGh5bG9nZW5ldGljIHRyZWUgYW5kIGFkZHMgdGhlIE5DQkkgdGF4b25vbWljIGxpbmVhZ2VzIGFzIGFuIG91dGVyIHJpbmcgdG8gZGlzcGxheSB0aGUgYnJlYWR0aCBvZiBzZXF1ZW5jZSBkaXZlcnNpdHkgaW4gdGhlIDQxNyB1bmlxdWUgYnJhbmNoIHRpcHMgKDQxJSBvZiBmdWxsIGxpYnJhcnkgZGl2ZXJzaXR5KSBmcm9tIHRoZSByZWNvdmVyZWQgcGVyZmVjdHMgd2l0aCBmaXRuZXNzID4gLTEgaW4gTGlicmFyeSAxNS4KClJlcGxhY2UgdGhlIHZhbHVlIGluICJOQ0JJLnBoeWx1bSIgY29sdW1uIHdpdGggdGhlIHZhbHVlIGZyb20gIk5DQkkuY2xhc3MiIGlmICJOQ0JJLnBoeWx1bSIgaXMgIlBzZXVkb21vbmFkb3RhIgpgYGB7cn0KIyBSZXBsYWNlIHRoZSB2YWx1ZSBpbiAiTkNCSS5waHlsdW0iIGNvbHVtbiB3aXRoIHRoZSB2YWx1ZSBmcm9tICJOQ0JJLmNsYXNzIiBpZiAiTkNCSS5waHlsdW0iIGlzICJQc2V1ZG9tb25hZG90YSIKRml0dHJlZTE1X3RheGFfbWVyZ2VkJE5DQkkucGh5bHVtIDwtIGlmZWxzZShGaXR0cmVlMTVfdGF4YV9tZXJnZWQkTkNCSS5waHlsdW0gPT0gIlBzZXVkb21vbmFkb3RhIiwgRml0dHJlZTE1X3RheGFfbWVyZ2VkJE5DQkkuY2xhc3MsIEZpdHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLnBoeWx1bSkKCiMgUmVtb3ZlIHJvd3Mgd2l0aCBOQSBpbiBOQ0JJLnBoeWx1bSBjb2x1bW4KRml0dHJlZTE1X3RheGFfbWVyZ2VkIDwtIEZpdHRyZWUxNV90YXhhX21lcmdlZFshaXMubmEoRml0dHJlZTE1X3RheGFfbWVyZ2VkJE5DQkkucGh5bHVtKSAmIEZpdHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLnBoeWx1bSAhPSAiTkEiLCBdCmBgYAoKKipEaXN0aW5jdCBQaHlsdW0gQ29sb3JzIGZvciBQbG90dGluZyoqCmBgYHtyfQojIE1lcmdlIGRpc3RpbmN0IHBoeWx1bSBjb2xvcnMgZnJvbSBBbGx0cmVlMTVfdGF4YV9tZXJnZWQgZGF0YWZyYW1lOgpGaXR0cmVlMTVfdGF4YV9tZXJnZWQkTkNCSS5waHlsdW1fY29sb3JzIDwtIE5BCkZpdHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLmNsYXNzX2NvbG9ycyA8LSBOQQoKIyBOQ0JJLnBoeWx1bV9jb2xvcnMKRml0dHJlZTE1X3RheGFfbWVyZ2VkJE5DQkkucGh5bHVtX2NvbG9yc1tGaXR0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBBbGx0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SURdIDwtIEFsbHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLnBoeWx1bV9jb2xvcnNbbWF0Y2goRml0dHJlZTE1X3RheGFfbWVyZ2VkJFRheElEW0ZpdHRyZWUxNV90YXhhX21lcmdlZCRUYXhJRCAlaW4lIEFsbHRyZWUxNV90YXhhX21lcmdlZCRUYXhJRF0sIEFsbHRyZWUxNV90YXhhX21lcmdlZCRUYXhJRCldCgojIE5DQkkuY2xhc3NfY29sb3JzCkZpdHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLmNsYXNzX2NvbG9yc1tGaXR0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBBbGx0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SURdIDwtIEFsbHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLmNsYXNzX2NvbG9yc1ttYXRjaChGaXR0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SURbRml0dHJlZTE1X3RheGFfbWVyZ2VkJFRheElEICVpbiUgQWxsdHJlZTE1X3RheGFfbWVyZ2VkJFRheElEXSwgQWxsdHJlZTE1X3RheGFfbWVyZ2VkJFRheElEKV0KCiMgTGlzdCBwaHlsdW0gaW4gYWxwaGFiZXRpY2FsIG9yZGVyIGZvciBsZWdlbmQgYW5kIHBsb3R0aW5nCkZpdHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLnBoeWx1bSA8LSBmYWN0b3IoRml0dHJlZTE1X3RheGFfbWVyZ2VkJE5DQkkucGh5bHVtLCBsZXZlbHM9TkNCSXBoeWxvQ29sb3IkTkNCSS5waHlsdW0pCgojIExpc3QgcGh5bHVtIGluIGFscGhhYmV0aWNhbCBvcmRlciBmb3IgbGVnZW5kIGFuZCBwbG90dGluZwpGaXR0cmVlMTVfdGF4YV9tZXJnZWQkTkNCSS5jbGFzcyA8LSBmYWN0b3IoRml0dHJlZTE1X3RheGFfbWVyZ2VkJE5DQkkuY2xhc3MsIGxldmVscz1OQ0JJY2xhc3NDb2xvciROQ0JJLmNsYXNzKQpgYGAKCiMjIyMgQ29tcGxlbWVudCBUcmVlICg3OTcpCgpTaG93IHRoZSBtaW5pbXVtIGFuZCBtYXhpbXVtIGZpdG5lc3MgdmFsdWVzIGJldHdlZW4gRDA1IChNOSBubyBzdXBwKSB2cy4gRDAzIChNOSBmdWxsIHN1cHApIGRhdGFzZXQ6CmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQptaW4oRml0dHJlZTE1X3RheGFfbWVyZ2VkJGZpdEQwNUQwMyxuYS5ybT1UKQptYXgoRml0dHJlZTE1X3RheGFfbWVyZ2VkJGZpdEQwNUQwMyxuYS5ybT1UKQpgYGAKCkxpYjE1OiBDb21wbGVtZW50YXRpb24gUGxvdApgYGB7cn0KdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8wdG1wX2ZpdG5lc3NfbGFiZWxsZWQgPC0gZ2d0cmVlKEZpdHRyZWUxNSwgbGF5b3V0PSJjaXJjdWxhciIsIGJyYW5jaC5sZW5ndGg9Im5vbmUiKSAlPCslCiAgRml0dHJlZTE1X3RheGFfbWVyZ2VkICsKICBhZXMoY29sb3IgPSBpZmVsc2UoZml0RDA1RDAzIDwgLTEsICJMb3cgZml0bmVzcyIsICJIaWdoIGZpdG5lc3MiKSkgKwogIGdlb21fdGlwcG9pbnQoYWVzKGNvbG9yID0gaWZlbHNlKGZpdEQwNUQwMyA8IC0xLCAiTG93IGZpdG5lc3MiLCAiSGlnaCBmaXRuZXNzIikpLCBzaXplPTEsIGFscGhhPTAuOCkgKwogIGdlb21fdGlwbGFiMihhZXMobGFiZWw9TkNCSS5zcGVjaWVzLCBhbmdsZT1hbmdsZSksIHNpemU9MSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKAogICAgIkZpdG5lc3MiLAogICAgdmFsdWVzID0gYygiTG93IGZpdG5lc3MiID0gImRhcmtibHVlIiwgIkhpZ2ggZml0bmVzcyIgPSAiZ29sZCIpLAogICAgbmEudmFsdWUgPSAiZ3JheSIKICApICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiRml0bmVzcyIpKQoKdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8wdG1wX2ZpdG5lc3NfbGFiZWxsZWQKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIENvbXBsZW1lbnRhdGlvbiBGaXRuZXNzIFRyZWUgKHRpcHMgbGFiZWxlZCkKZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL1RyZWVzL0NPTVAvTGliMTUuRmFzdFRyZWUuNUJDcy5wZXJmZWN0cy5maXREMDVEMDMuQ09NUExFTUVOVEFUSU9OLnRpcHMubGFiZWxsZWQuc3BlY2llcy5wbmciLAogICAgICAgcGxvdD10cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzB0bXBfZml0bmVzc19sYWJlbGxlZCwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gMTIsIGhlaWdodCA9IDcuNSwgdW5pdHMgPSAiaW4iKQpgYGAKCkJ1aWxkIHRoZSBpbml0aWFsIERIRlIgdHJlZSB3aXRoIGNvbG9yLXNjYWxlIGJhc2VkIG9uIGZpdG5lc3MgYXQgQ09NUExFTUVOVEFUSU9OIGFuZCBhZGQgYW4gYWRkaXRpb25hbCByaW5nIHJlcHJlc2VudGluZyB0YXhvbm9taWMgbGluZWFnZXMgYXQgdGhlICoqUGh5bHVtLWxldmVsKiouCmBgYHtyfQp0cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzB0bXBfZml0bmVzc19waHlsdW1fcDEgPC0gZ2d0cmVlKEZpdHRyZWUxNSwgbGF5b3V0PSJjaXJjdWxhciIsIGJyYW5jaC5sZW5ndGg9Im5vbmUiKSAlPCslCiAgRml0dHJlZTE1X3RheGFfbWVyZ2VkICsKICBhZXMoY29sb3IgPSBpZmVsc2UoZml0RDA1RDAzIDwgLTEsICJMb3cgZml0bmVzcyIsICJIaWdoIGZpdG5lc3MiKSkgKwogIGdlb21fdGlwcG9pbnQoYWVzKGNvbG9yID0gaWZlbHNlKGZpdEQwNUQwMyA8IC0xLCAiTG93IGZpdG5lc3MiLCAiSGlnaCBmaXRuZXNzIikpLCBzaXplPTEsIGFscGhhPTAuOCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiKSArIAogIHNjYWxlX2NvbG9yX21hbnVhbCgKICAgICJGaXRuZXNzIiwKICAgIHZhbHVlcyA9IGMoIkxvdyBmaXRuZXNzIiA9ICJkYXJrYmx1ZSIsICJIaWdoIGZpdG5lc3MiID0gImdvbGQiKSwKICAgIG5hLnZhbHVlID0gImdyYXkiKQoKdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8wdG1wX2ZpdG5lc3NfcGh5bHVtX3AyIDwtIHRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMHRtcF9maXRuZXNzX3BoeWx1bV9wMSArCiAgbmV3X3NjYWxlX2ZpbGwoKSArCiAgZ2VvbV9mcnVpdCgKICAgIGdlb209Z2VvbV90aWxlLAogICAgbWFwcGluZz1hZXMoZmlsbD1OQ0JJLnBoeWx1bSksCiAgICB3aWR0aD00LAogICAgb2Zmc2V0PTAuMQogICkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKAogICAgbmFtZT0iUGh5bHVtIiwKICAgIHZhbHVlcz1OQ0JJcGh5bG9Db2xvciROQ0JJLnBoeWx1bV9jb2xvcnMsCiAgICBndWlkZT1ndWlkZV9sZWdlbmQoa2V5d2lkdGg9MC4zLCBrZXloZWlnaHQ9MC4zLCBuY29sPTEpCiAgKSArCiAgdGhlbWUoCiAgICBsZWdlbmQudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTApLCAKICAgICAgICAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICAgICBsZWdlbmQuc3BhY2luZy55ID0gdW5pdCgwLjIsICJjbSIpCiAgKQoKdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8wdG1wX2ZpdG5lc3NfcGh5bHVtX3AyCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL1RyZWVzL0NPTVAvTGliMTUuRmFzdFRyZWUuNUJDcy5wZXJmZWN0cy5maXREMDVEMDMuQ09NUExFTUVOVEFUSU9OLlBoeWx1bS5SSU5HLnBuZyIsIHBsb3Q9dHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8wdG1wX2ZpdG5lc3NfcGh5bHVtX3AyLAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNy41LCB1bml0cyA9ICJpbiIpCmBgYAoKIyMjIyBUcmltZXRob3ByaW0gVHJlZXMgKDQxNikKCiMjIyMjIERhdGEgU3Vic2V0dGluZwoKUmV0YWluIHVuaXF1ZSBESEZSIHBlcmZlY3RzIHdpdGggYSBmaXRuZXNzIHNjb3JlID49IC0xIGFuZCBhIG1pbmltdW0gb2YgNSBCQ3MgKG51bXBydW5lZEJDcyA+IDQpIGluIExpYjE1IChmaXREMDVEMDMpOgpgYGB7cn0KIyBTdWJzZXQgZGF0YXNldCBmb3IgdHJlZSBidWlsZGluZwpMMTVfcGVyZmVjdHNfY29tcGxlbWVudF90cmVlIDwtIHBlcmZlY3RzMTVfNUJDc19nb29kICU+JQogIHNlbGVjdChtdXRJRCwgc2VxLAogICAgICAgICBmaXREMDVEMDMsIGZpdEQwNkQwMywgZml0RDA3RDAzLCBmaXREMDhEMDMsIGZpdEQwOUQwMywgZml0RDEwRDAzLCBmaXREMTFEMDMpCmBgYAoKQWRkICJ0YXhJRCIgYW5kICJQY3RJZGVudEVjb2xpIiB0byBlYWNoICJtdXRJRCIKYGBge3J9CiMgUmVuYW1lICJtdXRJRCIgdG8gIklEIiB0byBtZXJnZSB3aXRoIGBvcmdpbmZvYCBkZjoKTDE1X3BlcmZlY3RzX2NvbXBsZW1lbnRfdHJlZSA8LSBMMTVfcGVyZmVjdHNfY29tcGxlbWVudF90cmVlICU+JSByZW5hbWUoSUQgPSBtdXRJRCkKCiMgTWVyZ2UgIlRheElEIiBhbmQgIlBjdElkZW50RWNvbGkiIGZyb20gYG9yZ2luZm9gIHRvIGBMMTVfcGVyZmVjdHNfY29tcGxlbWVudF90cmVlYDoKTDE1X3BlcmZlY3RzX2NvbXBsZW1lbnRfdHJlZSA8LSBMMTVfcGVyZmVjdHNfY29tcGxlbWVudF90cmVlICU+JQogIGxlZnRfam9pbihvcmdpbmZvICU+JSBzZWxlY3QoSUQsIFRheElELCBQY3RJZGVudEVjb2xpKSwgYnkgPSAiSUQiKQpgYGAKCkNyZWF0ZSBhIEZBU1RBIGZpbGUgY29udGFpbmluZyB0aGUgSUQgYW5kIGl0cyBhc3NvY2lhdGVkIHByb3RlaW4gc2VxdWVuY2UgZm9yIGFsaWdubWVudApgYGB7ciwgcmVzdWx0cz0naGlkZSd9CiMgQ29sbGVjdCB0aGUgc2VxdWVuY2VzIGluIEZBU1RBIGZvcm1hdApMMTVfcGVyZmVjdHNfY29tcGxlbWVudF90cmVlX2Zhc3RhX2NvbnRlbnQgPC0gcGFzdGUoIj4iLEwxNV9wZXJmZWN0c19jb21wbGVtZW50X3RyZWUkSUQsICJcbiIsIEwxNV9wZXJmZWN0c19jb21wbGVtZW50X3RyZWUkc2VxLCAiXG4iLCBzZXAgPSAiIiwgY29sbGFwc2UgPSAiIikKCiMgRGVmaW5lIHRoZSBmaWxlIHBhdGggaW4gdGhlIHdvcmtpbmcgZGlyZWN0b3J5CkwxNV9wZXJmZWN0c19jb21wbGVtZW50X3RyZWVfZmFzdGFfcGF0aCA8LSBmaWxlLnBhdGgoZ2V0d2QoKSwgIlBlcmZlY3RzL1RSRUVTL0xpYi4xNS41QkNzLmdvb2QucGVyZmVjdHMuY29tcGxlbWVudC5mYXN0YSIpCgojIFdyaXRlIHRoZSBGQVNUQSBjb250ZW50IHRvIHRoZSBmaWxlCndyaXRlTGluZXMoTDE1X3BlcmZlY3RzX2NvbXBsZW1lbnRfdHJlZV9mYXN0YV9jb250ZW50LCBjb24gPSBMMTVfcGVyZmVjdHNfY29tcGxlbWVudF90cmVlX2Zhc3RhX3BhdGgpCmBgYAoKUGVyZm9ybSBhIG11bHRpcGxlIHNlcXVlbmNlIGFsaWdubWVudCBvbiB0aGUgRkFTVEEgZmlsZSB1c2luZyBDTFVTVEFMIE9tZWdhOgpgYGB7YmFzaCByZXN1bHRzID0gJ2hpZGUnfQojIE1heSBuZWVkIHRvIGVuYWJsZSBwZXJtaXNzaW9ucyB0byBydW4gdGhlIGV4ZWN1dGFibGU6CiNjaG1vZCAreCBjbHVzdGFsbwouL1NjcmlwdHMvY2x1c3RhbG8gLWkgUGVyZmVjdHMvVFJFRVMvTGliLjE1LjVCQ3MuZ29vZC5wZXJmZWN0cy5jb21wbGVtZW50LmZhc3RhIC1vIFBlcmZlY3RzL1RSRUVTL0xpYi4xNS41QkNzLmdvb2QucGVyZmVjdHMuY29tcGxlbWVudC5hbGlnbmVkLmZhc3RhIC0tb3V0Zm10PWZhIC0tZm9yY2UKYGBgCgpVc2UgRmFzdFRyZWUgKHBoeWxvZ2VuZXRpYyB0cmVlIGJ1aWxkaW5nIHByb2dyYW0pIHRvIGluZmVyIHRoZSB0cmVlIGZyb20gdGhlIGFsaWduZWQgYW1pbm8gYWNpZCBzZXF1ZW5jZXM6CmBgYHtiYXNofQojIE1MIE1vZGVsOiBKb25lcy1UYXlsb3ItVGhvcnRvbgojIGNobW9kICt4IEZhc3RUcmVlCi4vU2NyaXB0cy9GYXN0VHJlZSBQZXJmZWN0cy9UUkVFUy9MaWIuMTUuNUJDcy5nb29kLnBlcmZlY3RzLmNvbXBsZW1lbnQuYWxpZ25lZC5mYXN0YSA+IFBlcmZlY3RzL1RSRUVTL0xpYi4xNS41QkNzLmdvb2QucGVyZmVjdHMuY29tcGxlbWVudC50cmVlLm5ld2ljawpgYGAKCiMjIyMjIE5ld2ljayBUcmVlIEZpbGUKSW1wb3J0IHRoZSBuZXdpY2sgdHJlZSBmaWxlIGJhc2VkIG9uIHRoZSBzZXF1ZW5jZSBhbGlnbm1lbnQgb2YgY29tcGxlbWVudGluZyBwZXJmZWN0cyBkZXJpdmVkIGZyb20gTGliMTUgbWFwcGluZyBmaWxlOgpgYGB7cn0KIyBGdWxsIHRyZWUgYWxpZ25tZW50ICg0MTcgbXV0SUQpCkZpdHRyZWUxNWdvb2QgPC0gcmVhZC50cmVlKCJQZXJmZWN0cy9UUkVFUy9MaWIuMTUuNUJDcy5nb29kLnBlcmZlY3RzLmNvbXBsZW1lbnQudHJlZS5uZXdpY2siKSAgICMgbmV3aWNrIGZvcm1hdApGaXR0cmVlMTVnb29kCmBgYAoKRXh0cmFjdCB0aGUgdGlwIGxhYmVscyBmcm9tIHRoZSBuZXdpY2sgdHJlZSBmaWxlIHRvIG1hdGNoIHdpdGggTkNCSSB0YXhvbm9teSBmb3IgZG93bnN0cmVhbSBwbG90dGluZzoKYGBge3J9CiMgRXh0cmFjdCB0aXAgbGFiZWxzIGZyb20gRml0dHJlZTE1Z29vZApGaXR0cmVlMTVnb29kX3RpcF9sYWJlbHMgPC0gRml0dHJlZTE1Z29vZCR0aXAubGFiZWwKCiMgQ3JlYXRlIGEgbmV3IGRhdGEgZnJhbWUgd2l0aCB1bmlxdWUgdGlwIGxhYmVscwpGaXR0cmVlMTVnb29kX3RpcF9sYWJlbHNfZGYgPC0gZGF0YS5mcmFtZSh0aXAubGFiZWwgPSB1bmlxdWUoRml0dHJlZTE1Z29vZF90aXBfbGFiZWxzKSkKCiMgUHJpbnQgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSBuZXcgZGF0YSBmcmFtZQpoZWFkKEZpdHRyZWUxNWdvb2RfdGlwX2xhYmVsc19kZikKYGBgCgpNYXRjaCBlYWNoIHRpcC5sYWJlbCBJRCBpbiBgRml0dHJlZTE1Z29vZGAgd2l0aCBpdCdzIGFzc29jaWF0ZWQgVGF4SUQgZnJvbSBgb3JnaW5mb2AgZGF0YWZyYW1lOgpgYGB7cn0KIyBSZW5hbWUgY29sdW1uICJ0aXAubGFiZWwiIHRvICJJRCIKY29sbmFtZXMoRml0dHJlZTE1Z29vZF90aXBfbGFiZWxzX2RmKSA8LSBjKCJJRCIpCgojIE1lcmdlIG9yZ2luZm8gd2l0aCBBbGx0cmVlMTVfdGlwX2xhYmVsc19kZiBiYXNlZCBvbiB0aGUgc2hhcmVkIElECkZpdHRyZWUxNWdvb2RfdGF4YSA8LSBtZXJnZShGaXR0cmVlMTVnb29kX3RpcF9sYWJlbHNfZGYsIG9yZ2luZm8sIGJ5ID0gIklEIiwgYWxsLnggPSBUUlVFKQoKIyBQcmludCB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgdGhlIG1lcmdlZCBkYXRhIGZyYW1lCmhlYWQoRml0dHJlZTE1Z29vZF90YXhhKQpgYGAKCkFkZCBmaXRuZXNzIHNjb3JlcyB0byB0aGUgYEZpdHRyZWUxNV90YXhhX21lcmdlZGAgb2JqZWN0IHByaW9yIHRvIHBsb3R0aW5nCmBgYHtyfQpGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkIDwtIEZpdHRyZWUxNWdvb2RfdGF4YSAlPiUKICBsZWZ0X2pvaW4oTDE1X3BlcmZlY3RzX2NvbXBsZW1lbnRfdHJlZSAlPiUgc2VsZWN0KElELCBmaXREMDVEMDMsIGZpdEQwNkQwMywgZml0RDA3RDAzLCBmaXREMDhEMDMsIGZpdEQwOUQwMywgZml0RDEwRDAzLCBmaXREMTFEMDMpLCBieSA9ICJJRCIpCmBgYAoKTWVyZ2UgdGhlIE5DQkkgdGF4b25vbXkgY29sdW1ucyB0byBgRml0dHJlZTE1X3RheGFgIGJhc2VkIG9uIHNoYXJlZCBUYXhJRApgYGB7cn0KIyBNZXJnZSB0aGUgTkNCSSB0YXhvbm9teSBjb2x1bW5zIHRvIEZpdHRyZWUxNWdvb2RfdGF4YSBiYXNlZCBvbiBzaGFyZWQgVGF4SUQKRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCROQ0JJLm5hbWUgPC0gTkEKRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCROQ0JJLnN1cGVya2luZ2RvbSA8LSBOQQpGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJE5DQkkucGh5bHVtIDwtIE5BCkZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQkTkNCSS5jbGFzcyA8LSBOQQpGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJE5DQkkub3JkZXIgPC0gTkEKRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCROQ0JJLmZhbWlseSA8LSBOQQpGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJE5DQkkuZ2VudXMgPC0gTkEKRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCROQ0JJLnNwZWNpZXMgPC0gTkEKCiMgTkNCSS5uYW1lCkZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQkTkNCSS5uYW1lW0ZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdIDwtIG5jYmlfdGF4YSROQ0JJLm5hbWVbbWF0Y2goRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCRUYXhJRFtGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSwgbmNiaV90YXhhJFRheElEKV0KCiMgTkNCSS5zdXBlcmtpbmdkb20KRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCROQ0JJLnN1cGVya2luZ2RvbVtGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSA8LSBuY2JpX3RheGEkTkNCSS5zdXBlcmtpbmdkb21bbWF0Y2goRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCRUYXhJRFtGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSwgbmNiaV90YXhhJFRheElEKV0KCiMgTkNCSS5waHlsdW0KRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCROQ0JJLnBoeWx1bVtGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSA8LSBuY2JpX3RheGEkTkNCSS5waHlsdW1bbWF0Y2goRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCRUYXhJRFtGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSwgbmNiaV90YXhhJFRheElEKV0KCiMgTkNCSS5jbGFzcwpGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJE5DQkkuY2xhc3NbRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCRUYXhJRCAlaW4lIG5jYmlfdGF4YSRUYXhJRF0gPC0gbmNiaV90YXhhJE5DQkkuY2xhc3NbbWF0Y2goRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCRUYXhJRFtGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSwgbmNiaV90YXhhJFRheElEKV0KCiMgTkNCSS5vcmRlcgpGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJE5DQkkub3JkZXJbRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCRUYXhJRCAlaW4lIG5jYmlfdGF4YSRUYXhJRF0gPC0gbmNiaV90YXhhJE5DQkkub3JkZXJbbWF0Y2goRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCRUYXhJRFtGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSwgbmNiaV90YXhhJFRheElEKV0KCiMgTkNCSS5mYW1pbHkKRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCROQ0JJLmZhbWlseVtGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSA8LSBuY2JpX3RheGEkTkNCSS5mYW1pbHlbbWF0Y2goRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCRUYXhJRFtGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSwgbmNiaV90YXhhJFRheElEKV0KCiMgTkNCSS5mYW1pbHkKRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCROQ0JJLmdlbnVzW0ZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdIDwtIG5jYmlfdGF4YSROQ0JJLmdlbnVzW21hdGNoKEZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQkVGF4SURbRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCRUYXhJRCAlaW4lIG5jYmlfdGF4YSRUYXhJRF0sIG5jYmlfdGF4YSRUYXhJRCldCgojIE5DQkkuZmFtaWx5CkZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQkTkNCSS5zcGVjaWVzW0ZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdIDwtIG5jYmlfdGF4YSROQ0JJLnNwZWNpZXNbbWF0Y2goRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCRUYXhJRFtGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSwgbmNiaV90YXhhJFRheElEKV0KYGBgCgojIyMjIyBUYXhvbm9taWMgUmluZ3MKClRoZSBmb2xsb3dpbmcgc2VjdGlvbiBidWlsZHMgb24gdGhlIGluaXRpYWwgREhGUiBwaHlsb2dlbmV0aWMgdHJlZSBhbmQgYWRkcyB0aGUgTkNCSSB0YXhvbm9taWMgbGluZWFnZXMgYXMgYW4gb3V0ZXIgcmluZyB0byBkaXNwbGF5IHRoZSBicmVhZHRoIG9mIHNlcXVlbmNlIGRpdmVyc2l0eSBpbiB0aGUgNDE3IHVuaXF1ZSBicmFuY2ggdGlwcyAoNDElIG9mIGZ1bGwgbGlicmFyeSBkaXZlcnNpdHkpIGZyb20gdGhlIHJlY292ZXJlZCBwZXJmZWN0cyB3aXRoIGZpdG5lc3MgPiAtMSBpbiBMaWJyYXJ5IDE1LgoKUmVwbGFjZSB0aGUgdmFsdWUgaW4gIk5DQkkucGh5bHVtIiBjb2x1bW4gd2l0aCB0aGUgdmFsdWUgZnJvbSAiTkNCSS5jbGFzcyIgaWYgIk5DQkkucGh5bHVtIiBpcyAiUHNldWRvbW9uYWRvdGEiCmBgYHtyfQojIFJlcGxhY2UgdGhlIHZhbHVlIGluICJOQ0JJLnBoeWx1bSIgY29sdW1uIHdpdGggdGhlIHZhbHVlIGZyb20gIk5DQkkuY2xhc3MiIGlmICJOQ0JJLnBoeWx1bSIgaXMgIlBzZXVkb21vbmFkb3RhIgpGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJE5DQkkucGh5bHVtIDwtIGlmZWxzZShGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJE5DQkkucGh5bHVtID09ICJQc2V1ZG9tb25hZG90YSIsIEZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQkTkNCSS5jbGFzcywgRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCROQ0JJLnBoeWx1bSkKCiMgUmVtb3ZlIHJvd3Mgd2l0aCBOQSBpbiBOQ0JJLnBoeWx1bSBjb2x1bW4KRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCA8LSBGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkWyFpcy5uYShGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJE5DQkkucGh5bHVtKSAmIEZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQkTkNCSS5waHlsdW0gIT0gIk5BIiwgXQpgYGAKCioqRGlzdGluY3QgUGh5bHVtIENvbG9ycyBmb3IgUGxvdHRpbmcqKgpgYGB7cn0KIyBNZXJnZSBkaXN0aW5jdCBwaHlsdW0gY29sb3JzIGZyb20gRml0dHJlZTE1X3RheGFfbWVyZ2VkIGRhdGFmcmFtZToKRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCROQ0JJLnBoeWx1bV9jb2xvcnMgPC0gTkEKCiMgQWRkIE5DQkkucGh5bHVtX2NvbG9ycyBmcm9tIEZpdHRyZWUxNV90YXhhX21lcmdlZCBiYXNlZCBvbiBtYXRjaGluZyBOQ0JJLnBoeWx1bQpGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJE5DQkkucGh5bHVtX2NvbG9ycyA8LSBGaXR0cmVlMTVfdGF4YV9tZXJnZWQkTkNCSS5waHlsdW1fY29sb3JzW21hdGNoKEZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQkTkNCSS5waHlsdW0sIEZpdHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLnBoeWx1bSldCgojIExpc3QgcGh5bHVtIGluIGFscGhhYmV0aWNhbCBvcmRlciBmb3IgbGVnZW5kIGFuZCBwbG90dGluZwpGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJE5DQkkucGh5bHVtIDwtIGZhY3RvcihGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJE5DQkkucGh5bHVtLCBsZXZlbHM9TkNCSXBoeWxvQ29sb3IkTkNCSS5waHlsdW0pCmBgYAoKIyMjIyAwLjAgdWcvbUwgVHJlZQoKTGliMTU6ICgwLjAgdWcvbUwgVE1QKQpgYGB7cn0KdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8wLjB0bXBfZml0bmVzc19sYWJlbGxlZCA8LSBnZ3RyZWUoRml0dHJlZTE1Z29vZCwgbGF5b3V0PSJjaXJjdWxhciIsIGJyYW5jaC5sZW5ndGg9Im5vbmUiKSAlPCslCiAgRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCArCiAgYWVzKGNvbG9yID0gaWZlbHNlKGZpdEQwNUQwMyA8IC0xLCAiTG93IGZpdG5lc3MiLCAiSGlnaCBmaXRuZXNzIikpICsKICBnZW9tX3RpcHBvaW50KGFlcyhjb2xvciA9IGlmZWxzZShmaXREMDVEMDMgPCAtMSwgIkxvdyBmaXRuZXNzIiwgIkhpZ2ggZml0bmVzcyIpKSwgc2l6ZT0xLCBhbHBoYT0wLjgpICsKICAjZ2VvbV90aXBsYWIyKGFlcyhsYWJlbD1OQ0JJLnBoeWx1bSwgYW5nbGU9YW5nbGUpLCBzaXplPTEpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIAogIHNjYWxlX2NvbG9yX21hbnVhbCgKICAgICJGaXRuZXNzIiwKICAgIHZhbHVlcyA9IGMoIkxvdyBmaXRuZXNzIiA9ICJkYXJrYmx1ZSIsICJIaWdoIGZpdG5lc3MiID0gImdvbGQiKSwKICAgIG5hLnZhbHVlID0gImdyYXkiCiAgKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIkZpdG5lc3MiKSkKCnRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMC4wdG1wX2ZpdG5lc3NfbGFiZWxsZWQKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIE1JQyBGaXRuZXNzIFRyZWUgKHRpcHMgbGFiZWxlZCkKZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL1RyZWVzL1RNUC9MaWIxNS5GYXN0VHJlZS41QkNzLnBlcmZlY3RzLmdvb2QuZml0RDA1RDAzLjAuMHRtcC50aXBzLmxhYmVsbGVkLnBoeWx1bS5wbmciLAogICAgICAgcGxvdD10cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzAuMHRtcF9maXRuZXNzX2xhYmVsbGVkLAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNy41LCB1bml0cyA9ICJpbiIpCmBgYAoKQ291bnQgdGhlIG51bWJlciBvZiBjb21wbGVtZW50aW5nIGhvbW9sb2dzIChmaXQgPiAtMSkgYW5kIGRyb3BvdXQgaG9tb2xvZ3MgKGZpdCA8IC0xKToKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgQ291bnQgdW5pcXVlIElEcyB3aXRoIGZpdEQwNUQwMyA8IC0xCmxvd19maXRuZXNzX2NvdW50IDwtIEZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQgJT4lCiAgZmlsdGVyKGZpdEQwNUQwMyA8IC0xKSAlPiUKICBkaXN0aW5jdChJRCkgJT4lCiAgbnJvdygpCgojIENvdW50IHVuaXF1ZSBJRHMgd2l0aCBmaXREMDZEMDMgPiAtMQpoaWdoX2ZpdG5lc3NfY291bnQgPC0gRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCAlPiUKICBmaWx0ZXIoZml0RDA1RDAzID4gLTEpICU+JQogIGRpc3RpbmN0KElEKSAlPiUKICBucm93KCkKCiMgUHJpbnQgdGhlIHJlc3VsdHMKY2F0KCJOdW1iZXIgb2YgdW5pcXVlIElEcyB3aXRoIGZpdEQwNUQwMyA+IC0xOiIsIGhpZ2hfZml0bmVzc19jb3VudCwgIlxuIikKY2F0KCJOdW1iZXIgb2YgdW5pcXVlIElEcyB3aXRoIGZpdEQwNUQwMyA8IC0xOiIsIGxvd19maXRuZXNzX2NvdW50LCAiXG4iKQpgYGAKCkJ1aWxkIHRoZSBpbml0aWFsIERIRlIgdHJlZSB3aXRoIGNvbG9yLXNjYWxlIGJhc2VkIG9uIGZpdG5lc3MgYXQgQ09NUExFTUVOVEFUSU9OIGFuZCBhZGQgYW4gYWRkaXRpb25hbCByaW5nIHJlcHJlc2VudGluZyB0YXhvbm9taWMgbGluZWFnZXMgYXQgdGhlICoqUGh5bHVtLWxldmVsKiouCmBgYHtyfQp0cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzAuMHRtcF9maXRuZXNzX3BoeWx1bV9wMSA8LSBnZ3RyZWUoRml0dHJlZTE1Z29vZCwgbGF5b3V0PSJjaXJjdWxhciIsIGJyYW5jaC5sZW5ndGg9Im5vbmUiKSAlPCslCiAgRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCArCiAgYWVzKGNvbG9yID0gaWZlbHNlKGZpdEQwNUQwMyA8IC0xLCAiTG93IGZpdG5lc3MiLCAiSGlnaCBmaXRuZXNzIikpICsKICBnZW9tX3RpcHBvaW50KGFlcyhjb2xvciA9IGlmZWxzZShmaXREMDVEMDMgPCAtMSwgIkxvdyBmaXRuZXNzIiwgIkhpZ2ggZml0bmVzcyIpKSwgc2l6ZT0xLCBhbHBoYT0wLjgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249InJpZ2h0IikgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwoCiAgICAiRml0bmVzcyIsCiAgICB2YWx1ZXMgPSBjKCJMb3cgZml0bmVzcyIgPSAiZGFya2JsdWUiLCAiSGlnaCBmaXRuZXNzIiA9ICJnb2xkIiksCiAgICBuYS52YWx1ZSA9ICJncmF5IikKCnRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMC4wdG1wX2ZpdG5lc3NfcGh5bHVtX3AyIDwtIHRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMC4wdG1wX2ZpdG5lc3NfcGh5bHVtX3AxICsKICBuZXdfc2NhbGVfZmlsbCgpICsKICBnZW9tX2ZydWl0KAogICAgZ2VvbT1nZW9tX3RpbGUsCiAgICBtYXBwaW5nPWFlcyhmaWxsPU5DQkkucGh5bHVtKSwgICMgQ2hhbmdlZCB0aGlzIGxpbmUKICAgIHdpZHRoPTMsCiAgICBvZmZzZXQ9MC4xCiAgKSArCiAgc2NhbGVfZmlsbF9tYW51YWwoCiAgICBuYW1lPSJQaHlsdW0iLAogICAgdmFsdWVzPXNldE5hbWVzKEZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQkTkNCSS5waHlsdW1fY29sb3JzLCBGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJE5DQkkucGh5bHVtKSwKICAgIGd1aWRlPWd1aWRlX2xlZ2VuZChrZXl3aWR0aD0wLjMsIGtleWhlaWdodD0wLjMsIG5jb2w9MSkKICApICsKICB0aGVtZSgKICAgIGxlZ2VuZC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksIAogICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICBsZWdlbmQuc3BhY2luZy55ID0gdW5pdCgwLjIsICJjbSIpCiAgKQoKdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8wLjB0bXBfZml0bmVzc19waHlsdW1fcDIKYGBgCgoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL1RyZWVzL1RNUC9MaWIxNS5GYXN0VHJlZS41QkNzLnBlcmZlY3RzLmdvb2QuZml0RDA1RDAzLjAuMC5QaHlsdW0uUklORy5wbmciLCBwbG90PXRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMC4wdG1wX2ZpdG5lc3NfcGh5bHVtX3AyLAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNy41LCB1bml0cyA9ICJpbiIpCmBgYAoKIyMjIyAwLjA1OCB1Zy9tTCBUcmVlCgpMaWIxNTogTUlDICgwLjA1OCB1Zy9tTCBUTVApCmBgYHtyfQp0cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzAuMDU4dG1wX2ZpdG5lc3NfbGFiZWxsZWQgPC0gZ2d0cmVlKEZpdHRyZWUxNWdvb2QsIGxheW91dD0iY2lyY3VsYXIiLCBicmFuY2gubGVuZ3RoPSJub25lIikgJTwrJQogIEZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQgKwogIGFlcyhjb2xvciA9IGlmZWxzZShmaXREMDZEMDMgPCAtMSwgIkxvdyBmaXRuZXNzIiwgIkhpZ2ggZml0bmVzcyIpKSArCiAgZ2VvbV90aXBwb2ludChhZXMoY29sb3IgPSBpZmVsc2UoZml0RDA2RDAzIDwgLTEsICJMb3cgZml0bmVzcyIsICJIaWdoIGZpdG5lc3MiKSksIHNpemU9MSwgYWxwaGE9MC44KSArCiAgI2dlb21fdGlwbGFiMihhZXMobGFiZWw9TkNCSS5waHlsdW0sIGFuZ2xlPWFuZ2xlKSwgc2l6ZT0xKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwoCiAgICAiRml0bmVzcyIsCiAgICB2YWx1ZXMgPSBjKCJMb3cgZml0bmVzcyIgPSAiZGFya2JsdWUiLCAiSGlnaCBmaXRuZXNzIiA9ICJnb2xkIiksCiAgICBuYS52YWx1ZSA9ICJncmF5IgogICkgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJGaXRuZXNzIikpCgp0cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzAuMDU4dG1wX2ZpdG5lc3NfbGFiZWxsZWQKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIE1JQyBGaXRuZXNzIFRyZWUgKHRpcHMgbGFiZWxlZCkKZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL1RyZWVzL1RNUC9MaWIxNS5GYXN0VHJlZS41QkNzLnBlcmZlY3RzLmdvb2QuZml0RDA2RDAzLjAuMDU4dG1wLnRpcHMubGFiZWxsZWQucGh5bHVtLnBuZyIsCiAgICAgICBwbG90PXRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMC4wNTh0bXBfZml0bmVzc19sYWJlbGxlZCwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gMTIsIGhlaWdodCA9IDcuNSwgdW5pdHMgPSAiaW4iKQpgYGAKCkNvdW50IHRoZSBudW1iZXIgb2YgY29tcGxlbWVudGluZyBob21vbG9ncyAoZml0ID4gLTEpIGFuZCBkcm9wb3V0IGhvbW9sb2dzIChmaXQgPCAtMSk6CmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIENvdW50IHVuaXF1ZSBJRHMgd2l0aCBmaXREMDZEMDMgPCAtMQpsb3dfZml0bmVzc19jb3VudCA8LSBGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkICU+JQogIGZpbHRlcihmaXREMDZEMDMgPCAtMSkgJT4lCiAgZGlzdGluY3QoSUQpICU+JQogIG5yb3coKQoKIyBDb3VudCB1bmlxdWUgSURzIHdpdGggZml0RDA2RDAzID4gLTEKaGlnaF9maXRuZXNzX2NvdW50IDwtIEZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQgJT4lCiAgZmlsdGVyKGZpdEQwNkQwMyA+IC0xKSAlPiUKICBkaXN0aW5jdChJRCkgJT4lCiAgbnJvdygpCgojIFByaW50IHRoZSByZXN1bHRzCmNhdCgiTnVtYmVyIG9mIHVuaXF1ZSBJRHMgd2l0aCBmaXREMDZEMDMgPiAtMToiLCBoaWdoX2ZpdG5lc3NfY291bnQsICJcbiIpCmNhdCgiTnVtYmVyIG9mIHVuaXF1ZSBJRHMgd2l0aCBmaXREMDZEMDMgPCAtMToiLCBsb3dfZml0bmVzc19jb3VudCwgIlxuIikKYGBgCgoKQnVpbGQgdGhlIGluaXRpYWwgREhGUiB0cmVlIHdpdGggY29sb3Itc2NhbGUgYmFzZWQgb24gZml0bmVzcyBhdCBDT01QTEVNRU5UQVRJT04gYW5kIGFkZCBhbiBhZGRpdGlvbmFsIHJpbmcgcmVwcmVzZW50aW5nIHRheG9ub21pYyBsaW5lYWdlcyBhdCB0aGUgKipQaHlsdW0tbGV2ZWwqKi4KYGBge3J9CnRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMC4wNTh0bXBfZml0bmVzc19waHlsdW1fcDEgPC0gZ2d0cmVlKEZpdHRyZWUxNWdvb2QsIGxheW91dD0iY2lyY3VsYXIiLCBicmFuY2gubGVuZ3RoPSJub25lIikgJTwrJQogIEZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQgKwogIGFlcyhjb2xvciA9IGlmZWxzZShmaXREMDZEMDMgPCAtMSwgIkxvdyBmaXRuZXNzIiwgIkhpZ2ggZml0bmVzcyIpKSArCiAgZ2VvbV90aXBwb2ludChhZXMoY29sb3IgPSBpZmVsc2UoZml0RDA2RDAzIDwgLTEsICJMb3cgZml0bmVzcyIsICJIaWdoIGZpdG5lc3MiKSksIHNpemU9MSwgYWxwaGE9MC44KSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJyaWdodCIpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKAogICAgIkZpdG5lc3MiLAogICAgdmFsdWVzID0gYygiTG93IGZpdG5lc3MiID0gImRhcmtibHVlIiwgIkhpZ2ggZml0bmVzcyIgPSAiZ29sZCIpLAogICAgbmEudmFsdWUgPSAiZ3JheSIpCgp0cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzAuMDU4dG1wX2ZpdG5lc3NfcGh5bHVtX3AyIDwtIHRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMC4wNTh0bXBfZml0bmVzc19waHlsdW1fcDEgKwogIG5ld19zY2FsZV9maWxsKCkgKwogIGdlb21fZnJ1aXQoCiAgICBnZW9tPWdlb21fdGlsZSwKICAgIG1hcHBpbmc9YWVzKGZpbGw9TkNCSS5waHlsdW0pLCAgIyBDaGFuZ2VkIHRoaXMgbGluZQogICAgd2lkdGg9MywKICAgIG9mZnNldD0wLjEKICApICsKICBzY2FsZV9maWxsX21hbnVhbCgKICAgIG5hbWU9IlBoeWx1bSIsCiAgICB2YWx1ZXM9c2V0TmFtZXMoRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCROQ0JJLnBoeWx1bV9jb2xvcnMsIEZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQkTkNCSS5waHlsdW0pLAogICAgZ3VpZGU9Z3VpZGVfbGVnZW5kKGtleXdpZHRoPTAuMywga2V5aGVpZ2h0PTAuMywgbmNvbD0xKQogICkgKwogIHRoZW1lKAogICAgbGVnZW5kLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEwKSwgCiAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwKICAgIGxlZ2VuZC5zcGFjaW5nLnkgPSB1bml0KDAuMiwgImNtIikKICApCgp0cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzAuMDU4dG1wX2ZpdG5lc3NfcGh5bHVtX3AyCmBgYAoKCmBgYHtyIGVjaG89RkFMU0V9Cmdnc2F2ZShmaWxlPSJQZXJmZWN0cy9QTE9UUy9UcmVlcy9UTVAvTGliMTUuRmFzdFRyZWUuNUJDcy5wZXJmZWN0cy5nb29kLmZpdEQwNkQwMy4wLjA1OC5QaHlsdW0uUklORy5wbmciLCBwbG90PXRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMC4wNTh0bXBfZml0bmVzc19waHlsdW1fcDIsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA3LjUsIHVuaXRzID0gImluIikKYGBgCgojIyMjIyBTdW1tYXJ5IFN0YXRzCgpUaGUgZm9sbG93aW5nIHN0YXRpc3RpY2FsIHRlc3RzIHdpbGwgZGV0ZXJtaW5lIGlmIGZpdG5lc3MgY2x1c3RlcnMgYnkgZXZvbHV0aW9uYXJ5IGRpc3RhbmNlIHdpdGhpbiB0aGUgcGh5bG9nZW5ldGljIHRyZWUKYGBge3J9CiMgQ3JlYXRlIGEgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIGRpc3RhbmNlcyBhbmQgZGlmZmVyZW5jZXMgYmV0d2VlbiAKZml0RDA2RDAzX2Rpc3RfZGlmZiA8LSBmdW5jdGlvbihwYWlyd2lzZV9kaXN0YW5jZXMsIHRheGFfbWVyZ2VkKSB7CiAgZGlzdGFuY2VfZGYgPC0gYXMuZGF0YS5mcmFtZShhcy50YWJsZShwYWlyd2lzZV9kaXN0YW5jZXMpKSAlPiUKICAgIHJlbmFtZShUaXAxID0gVmFyMSwgVGlwMiA9IFZhcjIsIERpc3RhbmNlID0gRnJlcSkgJT4lCiAgICBtdXRhdGUoVGlwMSA9IGFzLmNoYXJhY3RlcihUaXAxKSwKICAgICAgICAgICBUaXAyID0gYXMuY2hhcmFjdGVyKFRpcDIpKSAlPiUKICAgIGZpbHRlcihUaXAxICE9IFRpcDIpICAjIFJlbW92ZSBzZWxmLWNvbXBhcmlzb25zCiAgCiAgcmVzdWx0cyA8LSBkaXN0YW5jZV9kZiAlPiUKICAgIGxlZnRfam9pbih0YXhhX21lcmdlZCwgYnkgPSBjKCJUaXAxIiA9ICJJRCIpKSAlPiUKICAgIHJlbmFtZShmaXREMDZEMDN2MSA9IGZpdEQwNkQwMykgJT4lCiAgICBsZWZ0X2pvaW4odGF4YV9tZXJnZWQsIGJ5ID0gYygiVGlwMiIgPSAiSUQiKSkgJT4lCiAgICByZW5hbWUoZml0RDA2RDAzdjIgPSBmaXREMDZEMDMpICU+JQogICAgbXV0YXRlKAogICAgICBBYnNEaXN0YW5jZSA9IGFicyhEaXN0YW5jZSksCiAgICAgIGZpdEQwNkQwM0Fic0RpZmYgPSBhYnMoZml0RDA2RDAzdjEgLSBmaXREMDZEMDN2MikKICAgICkgJT4lCiAgICBzZWxlY3QoVGlwMSwgVGlwMiwgQWJzRGlzdGFuY2UsIGZpdEQwNkQwM0Fic0RpZmYpCiAgCiAgcmV0dXJuKHJlc3VsdHMpfQpgYGAKCioqUGVhcnNvbiBDb3JyZWxhdGlvbjoqKiBDYWxjdWxhdGUgcGFpcndpc2UgZGlzdGFuY2VzIGJldHdlZW4gdGlwcyBhbmQgZGlmZmVyZW5jZXMgaW4gZml0bmVzcyB2YWx1ZXMuIFBsb3QgZGlzdGFuY2VzIGFuZCBkaWZmZXJlbmNlcyBhcyBzY2F0dGVyIHBsb3QgYW5kIHRlc3QgZm9yIHNpZ25pZmljYW50IGNvcnJlbGF0aW9uIHVzaW5nIFBlYXJzb24uCmBgYHtyfQojIENhbGN1bGF0ZSBwYWlyd2lzZSBkaXN0YW5jZXMKZml0RDA2RDAzX3BhaXJ3aXNlX2Rpc3RhbmNlcyA8LSBjb3BoZW5ldGljLnBoeWxvKEZpdHRyZWUxNWdvb2QpCgojIENhbGN1bGF0ZSBkaXN0YW5jZXMgYW5kIGRpZmZlcmVuY2VzCmZpdEQwNkQwM19yZXN1bHRzIDwtIGZpdEQwNkQwM19kaXN0X2RpZmYoZml0RDA2RDAzX3BhaXJ3aXNlX2Rpc3RhbmNlcyxGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkKQoKIyBDcmVhdGUgdGhlIHNjYXR0ZXIgcGxvdApmaXREMDZEMDNfc2NhdHRlcl9wbG90IDwtIGdncGxvdChmaXREMDZEMDNfcmVzdWx0cywgYWVzKHggPSBBYnNEaXN0YW5jZSwgeSA9IGZpdEQwNkQwM0Fic0RpZmYpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKyAgIyBBZGQgdHJhbnNwYXJlbmN5IHRvIHBvaW50cwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbG9yID0gInJlZCIsIHNlID0gRkFMU0UpICsgICMgQWRkIGEgbGluZWFyIHRyZW5kIGxpbmUKICBzdGF0X2NvcihtZXRob2QgPSAicGVhcnNvbiIsIHAuYWNjdXJhY3kgPSAwLjAwMDAwMSwgci5hY2N1cmFjeSA9IDAuMDEpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnModGl0bGUgPSAiQWJzb2x1dGUgRGlzdGFuY2UgdnMgQWJzb2x1dGUgRGlmZmVyZW5jZSBpbiBmaXREMDZEMDMiLAogICAgICAgeCA9ICJBYnNvbHV0ZSBWYWx1ZSBvZiBEaXN0YW5jZSIsCiAgICAgICB5ID0gIkFic29sdXRlIFZhbHVlIG9mIERpZmZlcmVuY2UgaW4gZml0RDA2RDAzIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiMgRGlzcGxheSB0aGUgcGxvdApwcmludChmaXREMDZEMDNfc2NhdHRlcl9wbG90KQoKIyBPcHRpb25hbDogU2F2ZSB0aGUgcGxvdApnZ3NhdmUoIlBlcmZlY3RzL1BMT1RTL1dpbGNveC9maXREMDZEMDNfcGVhcnNvbl9zY2F0dGVyX3Bsb3QucG5nIiwgCiAgICAgICBmaXREMDZEMDNfc2NhdHRlcl9wbG90LCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA4LCBkcGkgPSAzMDApCmBgYAoKKipDbG9zZS1SZWxhdGVkIFBhaXJzOioqIFN1YnNldCB0byBpbmNsdWRlIG9ubHkgcGFpcnMgb2YgdGF4YSB3aXRoIGFuIGFic29sdXRlIHBoeWxvZ2VuZXRpYyBkaXN0YW5jZSBsZXNzIHRoYW4gMS4gR3JhcGhpY2FsIHN1bW1hcmllcyBpbmNsdWRlIGEgUS1RIChRdWFudGlsZS1RdWFudGlsZSkgcGxvdCBhbmQgaGlzdHJvZ3JhbSBvZiB0aGUgYWJzb2x1dGUgZGlmZmVyZW5jZSB2YWx1ZXMuIFRoZSBRLVEgcGxvdCBpcyBncmFwaGljYWwgbWV0aG9kIHRvIGFzc2VzcyBpZiBhIGRhdGFzZXQgZm9sbG93cyBhIG5vcm1hbCBkaXN0cmlidXRpb24uIFRoZSByZWQgbGluZSBhZGRlZCBieSBxcWxpbmUoKSByZXByZXNlbnRzIHRoZSBleHBlY3RlZCBsaW5lIGlmIHRoZSBkYXRhIHdlcmUgcGVyZmVjdGx5IG5vcm1hbGx5IGRpc3RyaWJ1dGVkLiBUaGUgaGlzdG9ncmFtIHByb3ZpZGVzIGEgdmlzdWFsIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGRhdGEuCmBgYHtyfQojIFRoaXMgZm9jdXNlcyB0aGUgYW5hbHlzaXMgb24gY2xvc2VseSByZWxhdGVkIHRheGEKZml0RDA2RDAzX2Nsb3NlIDwtIGZpdEQwNkQwM19yZXN1bHRzICU+JSAKICBmaWx0ZXIoQWJzRGlzdGFuY2UgPCAxKQoKIyBHcmFwaGljYWwgU3VtbWFyaWVzCgojIENyZWF0ZSBhIFEtUSAoUXVhbnRpbGUtUXVhbnRpbGUpIHBsb3QKcXFub3JtKGZpdEQwNkQwM19jbG9zZSRmaXREMDZEMDNBYnNEaWZmKQpxcWxpbmUoZml0RDA2RDAzX2Nsb3NlJGZpdEQwNkQwM0Fic0RpZmYsIGNvbCA9ICJyZWQiKQoKIyBDcmVhdGUgYSBoaXN0b2dyYW0gcGxvdApoaXN0KGZpdEQwNkQwM19jbG9zZSRmaXREMDZEMDNBYnNEaWZmLCBtYWluID0gIkhpc3RvZ3JhbSBvZiBDbG9zZSBEYXRhIikKYGBgCgoqKkZhci1SZWxhdGVkIFBhaXJzOioqIFN1YnNldCB0byBpbmNsdWRlIG9ubHkgcGFpcnMgb2YgdGF4YSB3aXRoIGFuIGFic29sdXRlIHBoeWxvZ2VuZXRpYyBkaXN0YW5jZSBncmVhdGVyIHRoYW4gMS4gR3JhcGhpY2FsIHN1bW1hcmllcyBpbmNsdWRlIGEgUS1RIChRdWFudGlsZS1RdWFudGlsZSkgcGxvdCBhbmQgaGlzdHJvZ3JhbSBvZiB0aGUgYWJzb2x1dGUgZGlmZmVyZW5jZSB2YWx1ZXMuIFRoZSBRLVEgcGxvdCBpcyBncmFwaGljYWwgbWV0aG9kIHRvIGFzc2VzcyBpZiBhIGRhdGFzZXQgZm9sbG93cyBhIG5vcm1hbCBkaXN0cmlidXRpb24uIFRoZSByZWQgbGluZSBhZGRlZCBieSBxcWxpbmUoKSByZXByZXNlbnRzIHRoZSBleHBlY3RlZCBsaW5lIGlmIHRoZSBkYXRhIHdlcmUgcGVyZmVjdGx5IG5vcm1hbGx5IGRpc3RyaWJ1dGVkLiBUaGUgaGlzdG9ncmFtIHByb3ZpZGVzIGEgdmlzdWFsIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGRhdGEuCmBgYHtyfQojIFRoaXMgZm9jdXNlcyB0aGUgYW5hbHlzaXMgb24gZGlzdGFudGx5IHJlbGF0ZWQgdGF4YQpmaXREMDZEMDNfZmFyIDwtIGZpdEQwNkQwM19yZXN1bHRzICU+JSAKICBmaWx0ZXIoQWJzRGlzdGFuY2UgPiAxKQoKIyBHcmFwaGljYWwgU3VtbWFyaWVzCgojIENyZWF0ZSBhIFEtUSAoUXVhbnRpbGUtUXVhbnRpbGUpIHBsb3QKcXFub3JtKGZpdEQwNkQwM19mYXIkZml0RDA2RDAzQWJzRGlmZikKcXFsaW5lKGZpdEQwNkQwM19mYXIkZml0RDA2RDAzQWJzRGlmZiwgY29sID0gInJlZCIpCgojIENyZWF0ZSBhIGhpc3RvZ3JhbSBwbG90Cmhpc3QoZml0RDA2RDAzX2ZhciRmaXREMDZEMDNBYnNEaWZmLCBtYWluID0gIkhpc3RvZ3JhbSBvZiBGYXIgRGF0YSIpCmBgYAoKKipXaWxjb3ggVGVzdDoqKiBQZXJmb3JtIFdpbGNveG9uIFJhbmsgU3VtIFRlc3QgKGFsc28ga25vd24gYXMgTWFubi1XaGl0bmV5IFUgdGVzdCkgY29tcGFyaW5nIHRoZSBhYnNvbHV0ZSBkaXN0YW5jZSB2YWx1ZXMgYmV0d2VlbiB0aGUgY2xvc2UtcmVsYXRlZCBhbmQgZmFyLXJlbGF0ZWQgZ3JvdXBzLgpgYGB7cn0KIyBXaWxjb3ggdGVzdCBiZXR3ZWVuIGNsb3NlIGFuZCBmYXIgcmVsYXRlZCBkaXN0YW5jZXMKd2lsY294X3Jlc3VsdF9maXREMDZEMDMgPC0gd2lsY294LnRlc3QoZml0RDA2RDAzX2Nsb3NlJGZpdEQwNkQwM0Fic0RpZmYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpdEQwNkQwM19mYXIkZml0RDA2RDAzQWJzRGlmZikKCnByaW50KHdpbGNveF9yZXN1bHRfZml0RDA2RDAzKQpgYGAKCioqQ29tYmluZSBEYXRhOioqIEV4YW1pbmUgdGhlIGRpc3RyaWJ1dGlvbnMgb2YgdGhlc2UgZGlmZmVyZW5jZXMgdG8gdW5kZXJzdGFuZCBob3cgdGhleSBkaWZmZXIgYW5kIHdoZXRoZXIgdGhlc2UgZGlmZmVyZW5jZXMgYXJlIGdlbmVyYWxseSBsYXJnZXIgb3Igc21hbGxlciBmb3IgY2xvc2VseSByZWxhdGVkIHRheGEuCmBgYHtyfQojIENvbWJpbmUgdGhlIGRhdGEgYW5kIGFkZCBhIGdyb3VwIGxhYmVsCmZpdEQwNkQwM19jb21iaW5lZF9kYXRhIDwtIGJpbmRfcm93cygKICBtdXRhdGUoZml0RDA2RDAzX2Nsb3NlLCBncm91cCA9ICJDbG9zZSIpLAogIG11dGF0ZShmaXREMDZEMDNfZmFyLCBncm91cCA9ICJGYXIiKSkKCiMgUmVtb3ZlIE5BIHZhbHVlcwpmaXREMDZEMDNfY29tYmluZWRfZGF0YSA8LSBmaXREMDZEMDNfY29tYmluZWRfZGF0YSAlPiUgCiAgZmlsdGVyKCFpcy5uYShmaXREMDZEMDNBYnNEaWZmKSkKCiMgQ3JlYXRlIGEgYm94IHBsb3QKZml0RDA2RDAzX2JveHBsb3QgPC0gZ2dwbG90KGZpdEQwNkQwM19jb21iaW5lZF9kYXRhLCBhZXMoeCA9IGdyb3VwLCB5ID0gZml0RDA2RDAzQWJzRGlmZikpICsKICBnZW9tX2JveHBsb3QoKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjEsIHdpZHRoID0gMC4yKSArICAjIEFkZCBpbmRpdmlkdWFsIHBvaW50cyB3aXRoIHNvbWUgdHJhbnNwYXJlbmN5CiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgZml0RDA2RDAzIERpZmZlcmVuY2VzIiwKICAgICAgIHggPSAiUGh5bG9nZW5ldGljIFJlbGF0aW9uc2hpcCIsCiAgICAgICB5ID0gIkFic29sdXRlIERpZmZlcmVuY2UgaW4gZml0RDA2RDAzIikgKwogIHRoZW1lX21pbmltYWwoKQoKIyBEaXNwbGF5IHRoZSBwbG90CnByaW50KGZpdEQwNkQwM19ib3hwbG90KQoKIyBDYWxjdWxhdGUgc3VtbWFyeSBzdGF0aXN0aWNzCmZpdEQwNkQwM19zdW1tYXJ5X3N0YXRzIDwtIGZpdEQwNkQwM19jb21iaW5lZF9kYXRhICU+JQogIGdyb3VwX2J5KGdyb3VwKSAlPiUKICBzdW1tYXJpc2UoCiAgICBuID0gbigpLAogICAgbWVhbiA9IG1lYW4oZml0RDA2RDAzQWJzRGlmZiksCiAgICBtZWRpYW4gPSBtZWRpYW4oZml0RDA2RDAzQWJzRGlmZiksCiAgICBzZCA9IHNkKGZpdEQwNkQwM0Fic0RpZmYpLAogICAgbWluID0gbWluKGZpdEQwNkQwM0Fic0RpZmYpLAogICAgbWF4ID0gbWF4KGZpdEQwNkQwM0Fic0RpZmYpCiAgKQoKIyBQcmludCBzdW1tYXJ5IHN0YXRpc3RpY3MKcHJpbnQoZml0RDA2RDAzX3N1bW1hcnlfc3RhdHMpCgojIFBlcmZvcm0gYSBmb3JtYWwgdGVzdCBmb3IgZGlmZmVyZW5jZSBpbiBtZWFucwpmaXREMDZEMDNfdF90ZXN0X3Jlc3VsdCA8LSB0LnRlc3QoZml0RDA2RDAzQWJzRGlmZiB+IGdyb3VwLCBkYXRhID0gZml0RDA2RDAzX2NvbWJpbmVkX2RhdGEpCnByaW50KGZpdEQwNkQwM190X3Rlc3RfcmVzdWx0KQpgYGAKCiMjIyMgTUlDIFRyZWUgKDQxNikKCkxpYjE1OiBNSUMgKDAuNSB1Zy9tTCBUTVApCmBgYHtyfQp0cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzAuNXRtcF9maXRuZXNzX2xhYmVsbGVkIDwtIGdndHJlZShGaXR0cmVlMTVnb29kLCBsYXlvdXQ9ImNpcmN1bGFyIiwgYnJhbmNoLmxlbmd0aD0ibm9uZSIpICU8KyUKICBGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkICsKICBhZXMoY29sb3IgPSBpZmVsc2UoZml0RDA3RDAzIDwgLTEsICJMb3cgZml0bmVzcyIsICJIaWdoIGZpdG5lc3MiKSkgKwogIGdlb21fdGlwcG9pbnQoYWVzKGNvbG9yID0gaWZlbHNlKGZpdEQwN0QwMyA8IC0xLCAiTG93IGZpdG5lc3MiLCAiSGlnaCBmaXRuZXNzIikpLCBzaXplPTEsIGFscGhhPTAuOCkgKwogICNnZW9tX3RpcGxhYjIoYWVzKGxhYmVsPU5DQkkucGh5bHVtLCBhbmdsZT1hbmdsZSksIHNpemU9MSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKAogICAgIkZpdG5lc3MiLAogICAgdmFsdWVzID0gYygiTG93IGZpdG5lc3MiID0gImRhcmtibHVlIiwgIkhpZ2ggZml0bmVzcyIgPSAiZ29sZCIpLAogICAgbmEudmFsdWUgPSAiZ3JheSIKICApICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiRml0bmVzcyIpKQoKdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8wLjV0bXBfZml0bmVzc19sYWJlbGxlZApgYGAKCmBgYHtyIGVjaG89RkFMU0V9CiMgTUlDIEZpdG5lc3MgVHJlZSAodGlwcyBsYWJlbGVkKQpnZ3NhdmUoZmlsZT0iUGVyZmVjdHMvUExPVFMvVHJlZXMvVE1QL0xpYjE1LkZhc3RUcmVlLjVCQ3MucGVyZmVjdHMuZ29vZC5maXREMDdEMDMuTUlDLnRpcHMubGFiZWxsZWQucGh5bHVtLnBuZyIsCiAgICAgICBwbG90PXRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMC41dG1wX2ZpdG5lc3NfbGFiZWxsZWQsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA3LjUsIHVuaXRzID0gImluIikKYGBgCgpDb3VudCB0aGUgbnVtYmVyIG9mIGNvbXBsZW1lbnRpbmcgaG9tb2xvZ3MgKGZpdCA+IC0xKSBhbmQgZHJvcG91dCBob21vbG9ncyAoZml0IDwgLTEpOgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBDb3VudCB1bmlxdWUgSURzIHdpdGggZml0RDA3RDAzID4gLTEKaGlnaF9maXRuZXNzX2NvdW50IDwtIEZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQgJT4lCiAgZmlsdGVyKGZpdEQwN0QwMyA+IC0xKSAlPiUKICBkaXN0aW5jdChJRCkgJT4lCiAgbnJvdygpCgojIENvdW50IHVuaXF1ZSBJRHMgd2l0aCBmaXREMDdEMDMgPCAtMQpsb3dfZml0bmVzc19jb3VudCA8LSBGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkICU+JQogIGZpbHRlcihmaXREMDdEMDMgPCAtMSkgJT4lCiAgZGlzdGluY3QoSUQpICU+JQogIG5yb3coKQoKIyBQcmludCB0aGUgcmVzdWx0cwpjYXQoIk51bWJlciBvZiB1bmlxdWUgSURzIHdpdGggZml0RDA3RDAzID4gLTE6IiwgaGlnaF9maXRuZXNzX2NvdW50LCAiXG4iKQpjYXQoIk51bWJlciBvZiB1bmlxdWUgSURzIHdpdGggZml0RDA3RDAzIDwgLTE6IiwgbG93X2ZpdG5lc3NfY291bnQsICJcbiIpCmBgYAoKQnVpbGQgdGhlIGluaXRpYWwgREhGUiB0cmVlIHdpdGggY29sb3Itc2NhbGUgYmFzZWQgb24gZml0bmVzcyBhdCBDT01QTEVNRU5UQVRJT04gYW5kIGFkZCBhbiBhZGRpdGlvbmFsIHJpbmcgcmVwcmVzZW50aW5nIHRheG9ub21pYyBsaW5lYWdlcyBhdCB0aGUgKipQaHlsdW0tbGV2ZWwqKi4KYGBge3J9CnRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMC41dG1wX2ZpdG5lc3NfcGh5bHVtX3AxIDwtIGdndHJlZShGaXR0cmVlMTVnb29kLCBsYXlvdXQ9ImNpcmN1bGFyIiwgYnJhbmNoLmxlbmd0aD0ibm9uZSIpICU8KyUKICBGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkICsKICBhZXMoY29sb3IgPSBpZmVsc2UoZml0RDA3RDAzIDwgLTEsICJMb3cgZml0bmVzcyIsICJIaWdoIGZpdG5lc3MiKSkgKwogIGdlb21fdGlwcG9pbnQoYWVzKGNvbG9yID0gaWZlbHNlKGZpdEQwN0QwMyA8IC0xLCAiTG93IGZpdG5lc3MiLCAiSGlnaCBmaXRuZXNzIikpLCBzaXplPTEsIGFscGhhPTAuOCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiKSArIAogIHNjYWxlX2NvbG9yX21hbnVhbCgKICAgICJGaXRuZXNzIiwKICAgIHZhbHVlcyA9IGMoIkxvdyBmaXRuZXNzIiA9ICJkYXJrYmx1ZSIsICJIaWdoIGZpdG5lc3MiID0gImdvbGQiKSwKICAgIG5hLnZhbHVlID0gImdyYXkiKQoKdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8wLjV0bXBfZml0bmVzc19waHlsdW1fcDIgPC0gdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8wLjV0bXBfZml0bmVzc19waHlsdW1fcDEgKwogIG5ld19zY2FsZV9maWxsKCkgKwogIGdlb21fZnJ1aXQoCiAgICBnZW9tPWdlb21fdGlsZSwKICAgIG1hcHBpbmc9YWVzKGZpbGw9TkNCSS5waHlsdW0pLCAgIyBDaGFuZ2VkIHRoaXMgbGluZQogICAgd2lkdGg9MywKICAgIG9mZnNldD0wLjEKICApICsKICBzY2FsZV9maWxsX21hbnVhbCgKICAgIG5hbWU9IlBoeWx1bSIsCiAgICB2YWx1ZXM9c2V0TmFtZXMoRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCROQ0JJLnBoeWx1bV9jb2xvcnMsIEZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQkTkNCSS5waHlsdW0pLAogICAgZ3VpZGU9Z3VpZGVfbGVnZW5kKGtleXdpZHRoPTAuMywga2V5aGVpZ2h0PTAuMywgbmNvbD0xKQogICkgKwogIHRoZW1lKAogICAgbGVnZW5kLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEwKSwgCiAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwKICAgIGxlZ2VuZC5zcGFjaW5nLnkgPSB1bml0KDAuMiwgImNtIikKICApCgp0cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzAuNXRtcF9maXRuZXNzX3BoeWx1bV9wMgpgYGAKCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iUGVyZmVjdHMvUExPVFMvVHJlZXMvVE1QL0xpYjE1LkZhc3RUcmVlLjVCQ3MucGVyZmVjdHMuZ29vZC5maXREMDdEMDMuTUlDLlBoeWx1bS5SSU5HLnBuZyIsIHBsb3Q9dHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8wLjV0bXBfZml0bmVzc19waHlsdW1fcDIsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA3LjUsIHVuaXRzID0gImluIikKYGBgCgojIyMjIyBTdW1tYXJ5IFN0YXRzCgpUaGUgZm9sbG93aW5nIHN0YXRpc3RpY2FsIHRlc3RzIHdpbGwgZGV0ZXJtaW5lIGlmIGZpdG5lc3MgY2x1c3RlcnMgYnkgZXZvbHV0aW9uYXJ5IGRpc3RhbmNlIHdpdGhpbiB0aGUgcGh5bG9nZW5ldGljIHRyZWUKYGBge3J9CiMgQ3JlYXRlIGEgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIGRpc3RhbmNlcyBhbmQgZGlmZmVyZW5jZXMgYmV0d2VlbiAKZml0RDA3RDAzX2Rpc3RfZGlmZiA8LSBmdW5jdGlvbihwYWlyd2lzZV9kaXN0YW5jZXMsIHRheGFfbWVyZ2VkKSB7CiAgZGlzdGFuY2VfZGYgPC0gYXMuZGF0YS5mcmFtZShhcy50YWJsZShwYWlyd2lzZV9kaXN0YW5jZXMpKSAlPiUKICAgIHJlbmFtZShUaXAxID0gVmFyMSwgVGlwMiA9IFZhcjIsIERpc3RhbmNlID0gRnJlcSkgJT4lCiAgICBtdXRhdGUoVGlwMSA9IGFzLmNoYXJhY3RlcihUaXAxKSwKICAgICAgICAgICBUaXAyID0gYXMuY2hhcmFjdGVyKFRpcDIpKSAlPiUKICAgIGZpbHRlcihUaXAxICE9IFRpcDIpICAjIFJlbW92ZSBzZWxmLWNvbXBhcmlzb25zCiAgCiAgcmVzdWx0cyA8LSBkaXN0YW5jZV9kZiAlPiUKICAgIGxlZnRfam9pbih0YXhhX21lcmdlZCwgYnkgPSBjKCJUaXAxIiA9ICJJRCIpKSAlPiUKICAgIHJlbmFtZShmaXREMDdEMDN2MSA9IGZpdEQwN0QwMykgJT4lCiAgICBsZWZ0X2pvaW4odGF4YV9tZXJnZWQsIGJ5ID0gYygiVGlwMiIgPSAiSUQiKSkgJT4lCiAgICByZW5hbWUoZml0RDA3RDAzdjIgPSBmaXREMDdEMDMpICU+JQogICAgbXV0YXRlKAogICAgICBBYnNEaXN0YW5jZSA9IGFicyhEaXN0YW5jZSksCiAgICAgIGZpdEQwN0QwM0Fic0RpZmYgPSBhYnMoZml0RDA3RDAzdjEgLSBmaXREMDdEMDN2MikKICAgICkgJT4lCiAgICBzZWxlY3QoVGlwMSwgVGlwMiwgQWJzRGlzdGFuY2UsIGZpdEQwN0QwM0Fic0RpZmYpCiAgCiAgcmV0dXJuKHJlc3VsdHMpfQpgYGAKCioqUGVhcnNvbiBDb3JyZWxhdGlvbjoqKiBDYWxjdWxhdGUgcGFpcndpc2UgZGlzdGFuY2VzIGJldHdlZW4gdGlwcyBhbmQgZGlmZmVyZW5jZXMgaW4gZml0bmVzcyB2YWx1ZXMuIFBsb3QgZGlzdGFuY2VzIGFuZCBkaWZmZXJlbmNlcyBhcyBzY2F0dGVyIHBsb3QgYW5kIHRlc3QgZm9yIHNpZ25pZmljYW50IGNvcnJlbGF0aW9uIHVzaW5nIFBlYXJzb24uCmBgYHtyfQojIENhbGN1bGF0ZSBwYWlyd2lzZSBkaXN0YW5jZXMKZml0RDA3RDAzX3BhaXJ3aXNlX2Rpc3RhbmNlcyA8LSBjb3BoZW5ldGljLnBoeWxvKEZpdHRyZWUxNWdvb2QpCgojIENhbGN1bGF0ZSBkaXN0YW5jZXMgYW5kIGRpZmZlcmVuY2VzCmZpdEQwN0QwM19yZXN1bHRzIDwtIGZpdEQwN0QwM19kaXN0X2RpZmYoZml0RDA3RDAzX3BhaXJ3aXNlX2Rpc3RhbmNlcyxGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkKQoKIyBDcmVhdGUgdGhlIHNjYXR0ZXIgcGxvdApmaXREMDdEMDNfc2NhdHRlcl9wbG90IDwtIGdncGxvdChmaXREMDdEMDNfcmVzdWx0cywgYWVzKHggPSBBYnNEaXN0YW5jZSwgeSA9IGZpdEQwN0QwM0Fic0RpZmYpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKyAgIyBBZGQgdHJhbnNwYXJlbmN5IHRvIHBvaW50cwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbG9yID0gInJlZCIsIHNlID0gRkFMU0UpICsgICMgQWRkIGEgbGluZWFyIHRyZW5kIGxpbmUKICBzdGF0X2NvcihtZXRob2QgPSAicGVhcnNvbiIsIHAuYWNjdXJhY3kgPSAwLjAwMDAwMSwgci5hY2N1cmFjeSA9IDAuMDEpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnModGl0bGUgPSAiQWJzb2x1dGUgRGlzdGFuY2UgdnMgQWJzb2x1dGUgRGlmZmVyZW5jZSBpbiBmaXREMDdEMDMiLAogICAgICAgeCA9ICJBYnNvbHV0ZSBWYWx1ZSBvZiBEaXN0YW5jZSIsCiAgICAgICB5ID0gIkFic29sdXRlIFZhbHVlIG9mIERpZmZlcmVuY2UgaW4gZml0RDA3RDAzIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiMgRGlzcGxheSB0aGUgcGxvdApwcmludChmaXREMDdEMDNfc2NhdHRlcl9wbG90KQoKIyBPcHRpb25hbDogU2F2ZSB0aGUgcGxvdApnZ3NhdmUoIlBlcmZlY3RzL1BMT1RTL1dpbGNveC9maXREMDdEMDNfcGVhcnNvbl9zY2F0dGVyX3Bsb3QucG5nIiwgCiAgICAgICBmaXREMDdEMDNfc2NhdHRlcl9wbG90LCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA4LCBkcGkgPSAzMDApCmBgYAoKKipDbG9zZS1SZWxhdGVkIFBhaXJzOioqIFN1YnNldCB0byBpbmNsdWRlIG9ubHkgcGFpcnMgb2YgdGF4YSB3aXRoIGFuIGFic29sdXRlIHBoeWxvZ2VuZXRpYyBkaXN0YW5jZSBsZXNzIHRoYW4gMS4gR3JhcGhpY2FsIHN1bW1hcmllcyBpbmNsdWRlIGEgUS1RIChRdWFudGlsZS1RdWFudGlsZSkgcGxvdCBhbmQgaGlzdHJvZ3JhbSBvZiB0aGUgYWJzb2x1dGUgZGlmZmVyZW5jZSB2YWx1ZXMuIFRoZSBRLVEgcGxvdCBpcyBncmFwaGljYWwgbWV0aG9kIHRvIGFzc2VzcyBpZiBhIGRhdGFzZXQgZm9sbG93cyBhIG5vcm1hbCBkaXN0cmlidXRpb24uIFRoZSByZWQgbGluZSBhZGRlZCBieSBxcWxpbmUoKSByZXByZXNlbnRzIHRoZSBleHBlY3RlZCBsaW5lIGlmIHRoZSBkYXRhIHdlcmUgcGVyZmVjdGx5IG5vcm1hbGx5IGRpc3RyaWJ1dGVkLiBUaGUgaGlzdG9ncmFtIHByb3ZpZGVzIGEgdmlzdWFsIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGRhdGEuCmBgYHtyfQojIFRoaXMgZm9jdXNlcyB0aGUgYW5hbHlzaXMgb24gY2xvc2VseSByZWxhdGVkIHRheGEKZml0RDA3RDAzX2Nsb3NlIDwtIGZpdEQwN0QwM19yZXN1bHRzICU+JSAKICBmaWx0ZXIoQWJzRGlzdGFuY2UgPCAxKQoKIyBHcmFwaGljYWwgU3VtbWFyaWVzCgojIENyZWF0ZSBhIFEtUSAoUXVhbnRpbGUtUXVhbnRpbGUpIHBsb3QKcXFub3JtKGZpdEQwN0QwM19jbG9zZSRmaXREMDdEMDNBYnNEaWZmKQpxcWxpbmUoZml0RDA3RDAzX2Nsb3NlJGZpdEQwN0QwM0Fic0RpZmYsIGNvbCA9ICJyZWQiKQoKIyBDcmVhdGUgYSBoaXN0b2dyYW0gcGxvdApoaXN0KGZpdEQwN0QwM19jbG9zZSRmaXREMDdEMDNBYnNEaWZmLCBtYWluID0gIkhpc3RvZ3JhbSBvZiBDbG9zZSBEYXRhIikKYGBgCgoqKkZhci1SZWxhdGVkIFBhaXJzOioqIFN1YnNldCB0byBpbmNsdWRlIG9ubHkgcGFpcnMgb2YgdGF4YSB3aXRoIGFuIGFic29sdXRlIHBoeWxvZ2VuZXRpYyBkaXN0YW5jZSBncmVhdGVyIHRoYW4gMS4gR3JhcGhpY2FsIHN1bW1hcmllcyBpbmNsdWRlIGEgUS1RIChRdWFudGlsZS1RdWFudGlsZSkgcGxvdCBhbmQgaGlzdHJvZ3JhbSBvZiB0aGUgYWJzb2x1dGUgZGlmZmVyZW5jZSB2YWx1ZXMuIFRoZSBRLVEgcGxvdCBpcyBncmFwaGljYWwgbWV0aG9kIHRvIGFzc2VzcyBpZiBhIGRhdGFzZXQgZm9sbG93cyBhIG5vcm1hbCBkaXN0cmlidXRpb24uIFRoZSByZWQgbGluZSBhZGRlZCBieSBxcWxpbmUoKSByZXByZXNlbnRzIHRoZSBleHBlY3RlZCBsaW5lIGlmIHRoZSBkYXRhIHdlcmUgcGVyZmVjdGx5IG5vcm1hbGx5IGRpc3RyaWJ1dGVkLiBUaGUgaGlzdG9ncmFtIHByb3ZpZGVzIGEgdmlzdWFsIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGRhdGEuCmBgYHtyfQojIFRoaXMgZm9jdXNlcyB0aGUgYW5hbHlzaXMgb24gZGlzdGFudGx5IHJlbGF0ZWQgdGF4YQpmaXREMDdEMDNfZmFyIDwtIGZpdEQwN0QwM19yZXN1bHRzICU+JSAKICBmaWx0ZXIoQWJzRGlzdGFuY2UgPiAxKQoKIyBHcmFwaGljYWwgU3VtbWFyaWVzCgojIENyZWF0ZSBhIFEtUSAoUXVhbnRpbGUtUXVhbnRpbGUpIHBsb3QKcXFub3JtKGZpdEQwN0QwM19mYXIkZml0RDA3RDAzQWJzRGlmZikKcXFsaW5lKGZpdEQwN0QwM19mYXIkZml0RDA3RDAzQWJzRGlmZiwgY29sID0gInJlZCIpCgojIENyZWF0ZSBhIGhpc3RvZ3JhbSBwbG90Cmhpc3QoZml0RDA3RDAzX2ZhciRmaXREMDdEMDNBYnNEaWZmLCBtYWluID0gIkhpc3RvZ3JhbSBvZiBGYXIgRGF0YSIpCmBgYAoKKipXaWxjb3ggVGVzdDoqKiBQZXJmb3JtIFdpbGNveG9uIFJhbmsgU3VtIFRlc3QgKGFsc28ga25vd24gYXMgTWFubi1XaGl0bmV5IFUgdGVzdCkgY29tcGFyaW5nIHRoZSBhYnNvbHV0ZSBkaXN0YW5jZSB2YWx1ZXMgYmV0d2VlbiB0aGUgY2xvc2UtcmVsYXRlZCBhbmQgZmFyLXJlbGF0ZWQgZ3JvdXBzLgpgYGB7cn0KIyBXaWxjb3ggdGVzdCBiZXR3ZWVuIGNsb3NlIGFuZCBmYXIgcmVsYXRlZCBkaXN0YW5jZXMKd2lsY294X3Jlc3VsdF9maXREMDdEMDMgPC0gd2lsY294LnRlc3QoZml0RDA3RDAzX2Nsb3NlJGZpdEQwN0QwM0Fic0RpZmYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpdEQwN0QwM19mYXIkZml0RDA3RDAzQWJzRGlmZikKCnByaW50KHdpbGNveF9yZXN1bHRfZml0RDA3RDAzKQpgYGAKCioqSW50ZXJwcmV0YXRpb246KioKCi0gKipTaWduaWZpY2FudCBEaWZmZXJlbmNlOioqIFRoZSBleHRyZW1lbHkgbG93IHAtdmFsdWUgKDwgMi4yZS0xNikgaW5kaWNhdGVzIGEgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGZpdEQwN0QwM0Fic0RpZmYgdmFsdWVzIG9mIGNsb3NlbHkgcmVsYXRlZCB0YXhhIChmaXRuZXNzRDA3RDAzX2Nsb3NlKSBhbmQgbW9yZSBkaXN0YW50bHkgcmVsYXRlZCB0YXhhIChmaXREMDdEMDNfZmFyKS4KCi0gKipEaXN0cmlidXRpb24gQ29tcGFyaXNvbjoqKiBUaGUgdGVzdCBzdWdnZXN0cyB0aGF0IHRoZSBkaXN0cmlidXRpb24gb2YgZml0RDA3RDAzQWJzRGlmZiB2YWx1ZXMgaXMgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgYmV0d2VlbiBjbG9zZSBhbmQgZmFyIHBoeWxvZ2VuZXRpYyByZWxhdGlvbnNoaXBzLgoKLSAqKk1hZ25pdHVkZSBhbmQgRGlyZWN0aW9uOioqIFdoaWxlIHRoZSB0ZXN0IHRlbGxzIHVzIHRoZXJlJ3MgYSBzaWduaWZpY2FudCBkaWZmZXJlbmNlLCBpdCBkb2Vzbid0IHRlbGwgdXMgYWJvdXQgdGhlIG1hZ25pdHVkZSBvciBkaXJlY3Rpb24gb2YgdGhpcyBkaWZmZXJlbmNlLiBZb3UnZCBuZWVkIHRvIGxvb2sgYXQgZGVzY3JpcHRpdmUgc3RhdGlzdGljcyAobGlrZSBtZWRpYW5zKSBvZiBib3RoIGdyb3VwcyB0byB1bmRlcnN0YW5kIHRoaXMuCgotICoqQmlvbG9naWNhbCBTaWduaWZpY2FuY2U6KiogVGhpcyByZXN1bHQgc3VnZ2VzdHMgdGhhdCB0aGUgYWJzb2x1dGUgZGlmZmVyZW5jZXMgaW4gZml0RDA3RDAzIHZhbHVlcyBhcmUgbm90IHRoZSBzYW1lIGZvciBjbG9zZWx5IHJlbGF0ZWQgdGF4YSBjb21wYXJlZCB0byBtb3JlIGRpc3RhbnRseSByZWxhdGVkIHRheGEuIFRoaXMgY291bGQgaW1wbHkgdGhhdCBldm9sdXRpb25hcnkgZGlzdGFuY2UgZG9lcyBwbGF5IGEgcm9sZSBpbiB0aGUgc2ltaWxhcml0eSAob3IgZGlmZmVyZW5jZSkgb2YgZml0RDA3RDAzIHZhbHVlcywgZGVzcGl0ZSB0aGUgd2VhayBjb3JyZWxhdGlvbiB5b3UgZm91bmQgZWFybGllci4KCi0gKipGdXJ0aGVyIEludmVzdGlnYXRpb246KiogSXQgd291bGQgYmUgd29ydGggZXhhbWluaW5nIHRoZSBhY3R1YWwgZGlzdHJpYnV0aW9ucyBvZiB0aGVzZSBkaWZmZXJlbmNlcyAoZS5nLiwgd2l0aCBib3ggcGxvdHMpIHRvIHVuZGVyc3RhbmQgaG93IHRoZXkgZGlmZmVyLiBBcmUgdGhlIGRpZmZlcmVuY2VzIGdlbmVyYWxseSBsYXJnZXIgb3Igc21hbGxlciBmb3IgY2xvc2VseSByZWxhdGVkIHRheGE/CgotICoqQ2F1dGlvbjoqKiBSZW1lbWJlciB0aGF0IHdpdGggdmVyeSBsYXJnZSBzYW1wbGUgc2l6ZXMsIGV2ZW4gc21hbGwgZGlmZmVyZW5jZXMgY2FuIGJlY29tZSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50LiBJdCdzIGltcG9ydGFudCB0byBjb25zaWRlciB0aGUgcHJhY3RpY2FsIG9yIGJpb2xvZ2ljYWwgc2lnbmlmaWNhbmNlIG9mIHRoZXNlIGRpZmZlcmVuY2VzLCBub3QganVzdCB0aGUgc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlLgoKVGhpcyByZXN1bHQgYWRkcyBudWFuY2UgdG8geW91ciBlYXJsaWVyIGZpbmRpbmdzLCBzdWdnZXN0aW5nIHRoYXQgd2hpbGUgdGhlcmUgbWlnaHQgbm90IGJlIGEgc3Ryb25nIGxpbmVhciBjb3JyZWxhdGlvbiBiZXR3ZWVuIHBoeWxvZ2VuZXRpYyBkaXN0YW5jZSBhbmQgZml0RDA3RDAzIGRpZmZlcmVuY2VzLCB0aGVyZSBpcyBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgaW4gaG93IHRoZXNlIHZhbHVlcyB2YXJ5IGFtb25nIGNsb3NlIHJlbGF0aXZlcyB2ZXJzdXMgbW9yZSBkaXN0YW50IHJlbGF0aXZlcy4KCioqQ29tYmluZSBEYXRhOioqIEV4YW1pbmUgdGhlIGRpc3RyaWJ1dGlvbnMgb2YgdGhlc2UgZGlmZmVyZW5jZXMgdG8gdW5kZXJzdGFuZCBob3cgdGhleSBkaWZmZXIgYW5kIHdoZXRoZXIgdGhlc2UgZGlmZmVyZW5jZXMgYXJlIGdlbmVyYWxseSBsYXJnZXIgb3Igc21hbGxlciBmb3IgY2xvc2VseSByZWxhdGVkIHRheGEuCmBgYHtyfQojIENvbWJpbmUgdGhlIGRhdGEgYW5kIGFkZCBhIGdyb3VwIGxhYmVsCmZpdEQwN0QwM19jb21iaW5lZF9kYXRhIDwtIGJpbmRfcm93cygKICBtdXRhdGUoZml0RDA3RDAzX2Nsb3NlLCBncm91cCA9ICJDbG9zZSIpLAogIG11dGF0ZShmaXREMDdEMDNfZmFyLCBncm91cCA9ICJGYXIiKSkKCiMgUmVtb3ZlIE5BIHZhbHVlcwpmaXREMDdEMDNfY29tYmluZWRfZGF0YSA8LSBmaXREMDdEMDNfY29tYmluZWRfZGF0YSAlPiUgCiAgZmlsdGVyKCFpcy5uYShmaXREMDdEMDNBYnNEaWZmKSkKCiMgQ3JlYXRlIGEgYm94IHBsb3QKZml0RDA3RDAzX2JveHBsb3QgPC0gZ2dwbG90KGZpdEQwN0QwM19jb21iaW5lZF9kYXRhLCBhZXMoeCA9IGdyb3VwLCB5ID0gZml0RDA3RDAzQWJzRGlmZikpICsKICBnZW9tX2JveHBsb3QoKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjEsIHdpZHRoID0gMC4yKSArICAjIEFkZCBpbmRpdmlkdWFsIHBvaW50cyB3aXRoIHNvbWUgdHJhbnNwYXJlbmN5CiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgZml0RDA3RDAzIERpZmZlcmVuY2VzIiwKICAgICAgIHggPSAiUGh5bG9nZW5ldGljIFJlbGF0aW9uc2hpcCIsCiAgICAgICB5ID0gIkFic29sdXRlIERpZmZlcmVuY2UgaW4gZml0RDA3RDAzIikgKwogIHRoZW1lX21pbmltYWwoKQoKIyBEaXNwbGF5IHRoZSBwbG90CnByaW50KGZpdEQwN0QwM19ib3hwbG90KQoKIyBDYWxjdWxhdGUgc3VtbWFyeSBzdGF0aXN0aWNzCmZpdEQwN0QwM19zdW1tYXJ5X3N0YXRzIDwtIGZpdEQwN0QwM19jb21iaW5lZF9kYXRhICU+JQogIGdyb3VwX2J5KGdyb3VwKSAlPiUKICBzdW1tYXJpc2UoCiAgICBuID0gbigpLAogICAgbWVhbiA9IG1lYW4oZml0RDA3RDAzQWJzRGlmZiksCiAgICBtZWRpYW4gPSBtZWRpYW4oZml0RDA3RDAzQWJzRGlmZiksCiAgICBzZCA9IHNkKGZpdEQwN0QwM0Fic0RpZmYpLAogICAgbWluID0gbWluKGZpdEQwN0QwM0Fic0RpZmYpLAogICAgbWF4ID0gbWF4KGZpdEQwN0QwM0Fic0RpZmYpCiAgKQoKIyBQcmludCBzdW1tYXJ5IHN0YXRpc3RpY3MKcHJpbnQoZml0RDA3RDAzX3N1bW1hcnlfc3RhdHMpCgojIFBlcmZvcm0gYSBmb3JtYWwgdGVzdCBmb3IgZGlmZmVyZW5jZSBpbiBtZWFucwpmaXREMDdEMDNfdF90ZXN0X3Jlc3VsdCA8LSB0LnRlc3QoZml0RDA3RDAzQWJzRGlmZiB+IGdyb3VwLCBkYXRhID0gZml0RDA3RDAzX2NvbWJpbmVkX2RhdGEpCnByaW50KGZpdEQwN0QwM190X3Rlc3RfcmVzdWx0KQpgYGAKCioqSW50ZXJwcmV0YXRpb246KioKCi0gKipTaWduaWZpY2FudCBEaWZmZXJlbmNlOioqIFRoZXJlIGlzIGEgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBkaWZmZXJlbmNlIGluIHRoZSBmaXREMDdEMDNBYnNEaWZmIHZhbHVlcyBiZXR3ZWVuIGNsb3NlbHkgcmVsYXRlZCB0YXhhIGFuZCBtb3JlIGRpc3RhbnRseSByZWxhdGVkIHRheGEuCiAgCi0gKipNYWduaXR1ZGUgb2YgRGlmZmVyZW5jZToqKiBPbiBhdmVyYWdlLCB0aGUgYWJzb2x1dGUgZGlmZmVyZW5jZSBpbiBmaXREMDdEMDMgdmFsdWVzIGlzIGFib3V0IDEuMjI2IHVuaXRzIHNtYWxsZXIgZm9yIGNsb3NlbHkgcmVsYXRlZCB0YXhhIGNvbXBhcmVkIHRvIGRpc3RhbnRseSByZWxhdGVkIHRheGEuCiAgCi0gKipDb25zaXN0ZW5jeSBvZiBEaWZmZXJlbmNlOioqIFRoZSBuYXJyb3cgY29uZmlkZW5jZSBpbnRlcnZhbCAoLTEuMjYzODY1IHRvIC0xLjE4ODE3NCkgc3VnZ2VzdHMgdGhhdCB0aGlzIGRpZmZlcmVuY2UgaXMgcXVpdGUgY29uc2lzdGVudCBhY3Jvc3MgdGhlIGRhdGFzZXQuCiAgCi0gKipCaW9sb2dpY2FsIFNpZ25pZmljYW5jZToqKiBDbG9zZWx5IHJlbGF0ZWQgdGF4YSB0ZW5kIHRvIGhhdmUgbW9yZSBzaW1pbGFyIGZpdEQwN0QwMyB2YWx1ZXMgKHNtYWxsZXIgZGlmZmVyZW5jZXMpIGNvbXBhcmVkIHRvIGRpc3RhbnRseSByZWxhdGVkIHRheGEuIFRoaXMgc3VnZ2VzdHMgdGhhdCB0aGUgZml0RDA3RDAzIHRyYWl0IHNob3dzIHNvbWUgZGVncmVlIG9mIHBoeWxvZ2VuZXRpYyBzaWduYWwgb3IgY29uc2VydmF0aW9uLgogIAotICoqRXZvbHV0aW9uYXJ5IEltcGxpY2F0aW9uczoqKiBUaGlzIHJlc3VsdCBpbmRpY2F0ZXMgdGhhdCBldm9sdXRpb25hcnkgcmVsYXRlZG5lc3MgZG9lcyBwbGF5IGEgcm9sZSBpbiB0aGUgc2ltaWxhcml0eSBvZiBmaXREMDdEMDMgdmFsdWVzLiBDbG9zZWx5IHJlbGF0ZWQgc3BlY2llcyB0ZW5kIHRvIGhhdmUgbW9yZSBzaW1pbGFyIHZhbHVlcywgd2hpY2ggY291bGQgYmUgZHVlIHRvIHNoYXJlZCBldm9sdXRpb25hcnkgaGlzdG9yeSBvciBzaW1pbGFyIGVudmlyb25tZW50YWwgYWRhcHRhdGlvbnMuCiAgCi0gKipDb250cmFzdCB3aXRoIEVhcmxpZXIgQ29ycmVsYXRpb246KiogV2hpbGUgZWFybGllciB5b3UgZm91bmQgYSB3ZWFrIGNvcnJlbGF0aW9uIGJldHdlZW4gcGh5bG9nZW5ldGljIGRpc3RhbmNlIGFuZCBmaXREMDdEMDMgZGlmZmVyZW5jZXMsIHRoaXMgdC10ZXN0IHJldmVhbHMgYSBjbGVhciBkaXN0aW5jdGlvbiBiZXR3ZWVuIGNsb3NlIGFuZCBkaXN0YW50IHJlbGF0aW9uc2hpcHMuIFRoaXMgaGlnaGxpZ2h0cyB0aGUgaW1wb3J0YW5jZSBvZiBjb25zaWRlcmluZyBkaWZmZXJlbnQgYW5hbHl0aWNhbCBhcHByb2FjaGVzLCBhcyB0aGV5IGNhbiByZXZlYWwgZGlmZmVyZW50IGFzcGVjdHMgb2YgdGhlIGRhdGEuCiAgCi0gKipQcmFjdGljYWwgU2lnbmlmaWNhbmNlOioqIFRoZSBkaWZmZXJlbmNlIGluIG1lYW5zIChhYm91dCAxLjIyNikgc2hvdWxkIGJlIGNvbnNpZGVyZWQgaW4gdGhlIGNvbnRleHQgb2YgdGhlIG92ZXJhbGwgcmFuZ2UgYW5kIGJpb2xvZ2ljYWwgbWVhbmluZyBvZiBmaXREMDdEMDMgdmFsdWVzIHRvIGRldGVybWluZSBpdHMgcHJhY3RpY2FsIHNpZ25pZmljYW5jZS4KCkluIHN1bW1hcnksIHRoaXMgYW5hbHlzaXMgcHJvdmlkZXMgc3Ryb25nIGV2aWRlbmNlIHRoYXQgY2xvc2VseSByZWxhdGVkIHRheGEgaGF2ZSBtb3JlIHNpbWlsYXIgZml0RDA3RDAzIHZhbHVlcyBjb21wYXJlZCB0byBkaXN0YW50bHkgcmVsYXRlZCB0YXhhLiBUaGlzIHN1cHBvcnRzIHRoZSBpZGVhIHRoYXQgdGhlcmUgaXMgYSBwaHlsb2dlbmV0aWMgY29tcG9uZW50IHRvIHRoZSBmaXREMDdEMDMgdHJhaXQsIGV2ZW4gaWYgaXQncyBub3QgY2FwdHVyZWQgd2VsbCBieSBhIHNpbXBsZSBsaW5lYXIgY29ycmVsYXRpb24gd2l0aCBwaHlsb2dlbmV0aWMgZGlzdGFuY2UuIFRoZSBsYXJnZSBzYW1wbGUgc2l6ZSBhbmQgaGlnaGx5IHNpZ25pZmljYW50IHJlc3VsdCBzdWdnZXN0IHRoYXQgdGhpcyBpcyBhIHJvYnVzdCBmaW5kaW5nLCB0aG91Z2ggeW91IHNob3VsZCBhbHdheXMgY29uc2lkZXIgdGhlIGJpb2xvZ2ljYWwgY29udGV4dCBhbmQgcG90ZW50aWFsIGNvbmZvdW5kaW5nIGZhY3RvcnMgaW4geW91ciBpbnRlcnByZXRhdGlvbi4KCiMjIyMgMS4wIHVnL21MIFRNUCBUcmVlCgpMaWIxNTogTUlDICgxLjAgdWcvbUwgVE1QKQpgYGB7cn0KdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8xLjB0bXBfZml0bmVzc19sYWJlbGxlZCA8LSBnZ3RyZWUoRml0dHJlZTE1Z29vZCwgbGF5b3V0PSJjaXJjdWxhciIsIGJyYW5jaC5sZW5ndGg9Im5vbmUiKSAlPCslCiAgRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCArCiAgYWVzKGNvbG9yID0gaWZlbHNlKGZpdEQwOEQwMyA8IC0xLCAiTG93IGZpdG5lc3MiLCAiSGlnaCBmaXRuZXNzIikpICsKICBnZW9tX3RpcHBvaW50KGFlcyhjb2xvciA9IGlmZWxzZShmaXREMDhEMDMgPCAtMSwgIkxvdyBmaXRuZXNzIiwgIkhpZ2ggZml0bmVzcyIpKSwgc2l6ZT0xLCBhbHBoYT0wLjgpICsKICAjZ2VvbV90aXBsYWIyKGFlcyhsYWJlbD1OQ0JJLnBoeWx1bSwgYW5nbGU9YW5nbGUpLCBzaXplPTEpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIAogIHNjYWxlX2NvbG9yX21hbnVhbCgKICAgICJGaXRuZXNzIiwKICAgIHZhbHVlcyA9IGMoIkxvdyBmaXRuZXNzIiA9ICJkYXJrYmx1ZSIsICJIaWdoIGZpdG5lc3MiID0gImdvbGQiKSwKICAgIG5hLnZhbHVlID0gImdyYXkiCiAgKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIkZpdG5lc3MiKSkKCnRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMS4wdG1wX2ZpdG5lc3NfbGFiZWxsZWQKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIE1JQyBGaXRuZXNzIFRyZWUgKHRpcHMgbGFiZWxlZCkKZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL1RyZWVzL1RNUC9MaWIxNS5GYXN0VHJlZS41QkNzLnBlcmZlY3RzLmdvb2QuZml0RDA4RDAzLjEuMHRtcC50aXBzLmxhYmVsbGVkLnBoeWx1bS5wbmciLAogICAgICAgcGxvdD10cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzEuMHRtcF9maXRuZXNzX2xhYmVsbGVkLAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNy41LCB1bml0cyA9ICJpbiIpCmBgYAoKQ291bnQgdGhlIG51bWJlciBvZiBjb21wbGVtZW50aW5nIGhvbW9sb2dzIChmaXQgPiAtMSkgYW5kIGRyb3BvdXQgaG9tb2xvZ3MgKGZpdCA8IC0xKToKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgQ291bnQgdW5pcXVlIElEcyB3aXRoIGZpdEQwOEQwMyA+IC0xCmhpZ2hfZml0bmVzc19jb3VudCA8LSBGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkICU+JQogIGZpbHRlcihmaXREMDhEMDMgPiAtMSkgJT4lCiAgZGlzdGluY3QoSUQpICU+JQogIG5yb3coKQoKIyBDb3VudCB1bmlxdWUgSURzIHdpdGggZml0RDA4RDAzIDwgLTEKbG93X2ZpdG5lc3NfY291bnQgPC0gRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCAlPiUKICBmaWx0ZXIoZml0RDA4RDAzIDwgLTEpICU+JQogIGRpc3RpbmN0KElEKSAlPiUKICBucm93KCkKCiMgUHJpbnQgdGhlIHJlc3VsdHMKY2F0KCJOdW1iZXIgb2YgdW5pcXVlIElEcyB3aXRoIGZpdEQwOEQwMyA+IC0xOiIsIGhpZ2hfZml0bmVzc19jb3VudCwgIlxuIikKY2F0KCJOdW1iZXIgb2YgdW5pcXVlIElEcyB3aXRoIGZpdEQwOEQwMyA8IC0xOiIsIGxvd19maXRuZXNzX2NvdW50LCAiXG4iKQpgYGAKCkJ1aWxkIHRoZSBpbml0aWFsIERIRlIgdHJlZSB3aXRoIGNvbG9yLXNjYWxlIGJhc2VkIG9uIGZpdG5lc3MgYXQgQ09NUExFTUVOVEFUSU9OIGFuZCBhZGQgYW4gYWRkaXRpb25hbCByaW5nIHJlcHJlc2VudGluZyB0YXhvbm9taWMgbGluZWFnZXMgYXQgdGhlICoqUGh5bHVtLWxldmVsKiouCmBgYHtyfQp0cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzEuMHRtcF9maXRuZXNzX3BoeWx1bV9wMSA8LSBnZ3RyZWUoRml0dHJlZTE1Z29vZCwgbGF5b3V0PSJjaXJjdWxhciIsIGJyYW5jaC5sZW5ndGg9Im5vbmUiKSAlPCslCiAgRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCArCiAgYWVzKGNvbG9yID0gaWZlbHNlKGZpdEQwOEQwMyA8IC0xLCAiTG93IGZpdG5lc3MiLCAiSGlnaCBmaXRuZXNzIikpICsKICBnZW9tX3RpcHBvaW50KGFlcyhjb2xvciA9IGlmZWxzZShmaXREMDhEMDMgPCAtMSwgIkxvdyBmaXRuZXNzIiwgIkhpZ2ggZml0bmVzcyIpKSwgc2l6ZT0xLCBhbHBoYT0wLjgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249InJpZ2h0IikgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwoCiAgICAiRml0bmVzcyIsCiAgICB2YWx1ZXMgPSBjKCJMb3cgZml0bmVzcyIgPSAiZGFya2JsdWUiLCAiSGlnaCBmaXRuZXNzIiA9ICJnb2xkIiksCiAgICBuYS52YWx1ZSA9ICJncmF5IikKCnRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMS4wdG1wX2ZpdG5lc3NfcGh5bHVtX3AyIDwtIHRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMS4wdG1wX2ZpdG5lc3NfcGh5bHVtX3AxICsKICBuZXdfc2NhbGVfZmlsbCgpICsKICBnZW9tX2ZydWl0KAogICAgZ2VvbT1nZW9tX3RpbGUsCiAgICBtYXBwaW5nPWFlcyhmaWxsPU5DQkkucGh5bHVtKSwgICMgQ2hhbmdlZCB0aGlzIGxpbmUKICAgIHdpZHRoPTMsCiAgICBvZmZzZXQ9MC4xCiAgKSArCiAgc2NhbGVfZmlsbF9tYW51YWwoCiAgICBuYW1lPSJQaHlsdW0iLAogICAgdmFsdWVzPXNldE5hbWVzKEZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQkTkNCSS5waHlsdW1fY29sb3JzLCBGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJE5DQkkucGh5bHVtKSwKICAgIGd1aWRlPWd1aWRlX2xlZ2VuZChrZXl3aWR0aD0wLjMsIGtleWhlaWdodD0wLjMsIG5jb2w9MSkKICApICsKICB0aGVtZSgKICAgIGxlZ2VuZC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksIAogICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICBsZWdlbmQuc3BhY2luZy55ID0gdW5pdCgwLjIsICJjbSIpCiAgKQoKdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8xLjB0bXBfZml0bmVzc19waHlsdW1fcDIKYGBgCgoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL1RyZWVzL1RNUC9MaWIxNS5GYXN0VHJlZS41QkNzLnBlcmZlY3RzLmdvb2QuZml0RDA4RDAzLjEuMHRtcC5QaHlsdW0uUklORy5wbmciLCBwbG90PXRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMS4wdG1wX2ZpdG5lc3NfcGh5bHVtX3AyLAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNy41LCB1bml0cyA9ICJpbiIpCmBgYAoKIyMjIyMgU3VtbWFyeSBTdGF0cwoKVGhlIGZvbGxvd2luZyBzdGF0aXN0aWNhbCB0ZXN0cyB3aWxsIGRldGVybWluZSBpZiBmaXRuZXNzIGNsdXN0ZXJzIGJ5IGV2b2x1dGlvbmFyeSBkaXN0YW5jZSB3aXRoaW4gdGhlIHBoeWxvZ2VuZXRpYyB0cmVlCmBgYHtyfQojIENyZWF0ZSBhIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSBkaXN0YW5jZXMgYW5kIGRpZmZlcmVuY2VzIGJldHdlZW4gCmZpdEQwOEQwM19kaXN0X2RpZmYgPC0gZnVuY3Rpb24ocGFpcndpc2VfZGlzdGFuY2VzLCB0YXhhX21lcmdlZCkgewogIGRpc3RhbmNlX2RmIDwtIGFzLmRhdGEuZnJhbWUoYXMudGFibGUocGFpcndpc2VfZGlzdGFuY2VzKSkgJT4lCiAgICByZW5hbWUoVGlwMSA9IFZhcjEsIFRpcDIgPSBWYXIyLCBEaXN0YW5jZSA9IEZyZXEpICU+JQogICAgbXV0YXRlKFRpcDEgPSBhcy5jaGFyYWN0ZXIoVGlwMSksCiAgICAgICAgICAgVGlwMiA9IGFzLmNoYXJhY3RlcihUaXAyKSkgJT4lCiAgICBmaWx0ZXIoVGlwMSAhPSBUaXAyKSAgIyBSZW1vdmUgc2VsZi1jb21wYXJpc29ucwogIAogIHJlc3VsdHMgPC0gZGlzdGFuY2VfZGYgJT4lCiAgICBsZWZ0X2pvaW4odGF4YV9tZXJnZWQsIGJ5ID0gYygiVGlwMSIgPSAiSUQiKSkgJT4lCiAgICByZW5hbWUoZml0RDA4RDAzdjEgPSBmaXREMDhEMDMpICU+JQogICAgbGVmdF9qb2luKHRheGFfbWVyZ2VkLCBieSA9IGMoIlRpcDIiID0gIklEIikpICU+JQogICAgcmVuYW1lKGZpdEQwOEQwM3YyID0gZml0RDA4RDAzKSAlPiUKICAgIG11dGF0ZSgKICAgICAgQWJzRGlzdGFuY2UgPSBhYnMoRGlzdGFuY2UpLAogICAgICBmaXREMDhEMDNBYnNEaWZmID0gYWJzKGZpdEQwOEQwM3YxIC0gZml0RDA4RDAzdjIpCiAgICApICU+JQogICAgc2VsZWN0KFRpcDEsIFRpcDIsIEFic0Rpc3RhbmNlLCBmaXREMDhEMDNBYnNEaWZmKQogIAogIHJldHVybihyZXN1bHRzKX0KYGBgCgoqKlBlYXJzb24gQ29ycmVsYXRpb246KiogQ2FsY3VsYXRlIHBhaXJ3aXNlIGRpc3RhbmNlcyBiZXR3ZWVuIHRpcHMgYW5kIGRpZmZlcmVuY2VzIGluIGZpdG5lc3MgdmFsdWVzLiBQbG90IGRpc3RhbmNlcyBhbmQgZGlmZmVyZW5jZXMgYXMgc2NhdHRlciBwbG90IGFuZCB0ZXN0IGZvciBzaWduaWZpY2FudCBjb3JyZWxhdGlvbiB1c2luZyBQZWFyc29uLgpgYGB7cn0KIyBDYWxjdWxhdGUgcGFpcndpc2UgZGlzdGFuY2VzCmZpdEQwOEQwM19wYWlyd2lzZV9kaXN0YW5jZXMgPC0gY29waGVuZXRpYy5waHlsbyhGaXR0cmVlMTVnb29kKQoKIyBDYWxjdWxhdGUgZGlzdGFuY2VzIGFuZCBkaWZmZXJlbmNlcwpmaXREMDhEMDNfcmVzdWx0cyA8LSBmaXREMDhEMDNfZGlzdF9kaWZmKGZpdEQwOEQwM19wYWlyd2lzZV9kaXN0YW5jZXMsRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCkKCiMgQ3JlYXRlIHRoZSBzY2F0dGVyIHBsb3QKZml0RDA4RDAzX3NjYXR0ZXJfcGxvdCA8LSBnZ3Bsb3QoZml0RDA4RDAzX3Jlc3VsdHMsIGFlcyh4ID0gQWJzRGlzdGFuY2UsIHkgPSBmaXREMDhEMDNBYnNEaWZmKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpICsgICMgQWRkIHRyYW5zcGFyZW5jeSB0byBwb2ludHMKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBjb2xvciA9ICJyZWQiLCBzZSA9IEZBTFNFKSArICAjIEFkZCBhIGxpbmVhciB0cmVuZCBsaW5lCiAgc3RhdF9jb3IobWV0aG9kID0gInBlYXJzb24iLCBwLmFjY3VyYWN5ID0gMC4wMDAwMDEsIHIuYWNjdXJhY3kgPSAwLjAxKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHRpdGxlID0gIkFic29sdXRlIERpc3RhbmNlIHZzIEFic29sdXRlIERpZmZlcmVuY2UgaW4gZml0RDA4RDAzIiwKICAgICAgIHggPSAiQWJzb2x1dGUgVmFsdWUgb2YgRGlzdGFuY2UiLAogICAgICAgeSA9ICJBYnNvbHV0ZSBWYWx1ZSBvZiBEaWZmZXJlbmNlIGluIGZpdEQwOEQwMyIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgojIERpc3BsYXkgdGhlIHBsb3QKcHJpbnQoZml0RDA4RDAzX3NjYXR0ZXJfcGxvdCkKCiMgT3B0aW9uYWw6IFNhdmUgdGhlIHBsb3QKZ2dzYXZlKCJQZXJmZWN0cy9QTE9UUy9XaWxjb3gvZml0RDA4RDAzX3BlYXJzb25fc2NhdHRlcl9wbG90LnBuZyIsIAogICAgICAgZml0RDA4RDAzX3NjYXR0ZXJfcGxvdCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOCwgZHBpID0gMzAwKQpgYGAKCioqQ2xvc2UtUmVsYXRlZCBQYWlyczoqKiBTdWJzZXQgdG8gaW5jbHVkZSBvbmx5IHBhaXJzIG9mIHRheGEgd2l0aCBhbiBhYnNvbHV0ZSBwaHlsb2dlbmV0aWMgZGlzdGFuY2UgbGVzcyB0aGFuIDEuIEdyYXBoaWNhbCBzdW1tYXJpZXMgaW5jbHVkZSBhIFEtUSAoUXVhbnRpbGUtUXVhbnRpbGUpIHBsb3QgYW5kIGhpc3Ryb2dyYW0gb2YgdGhlIGFic29sdXRlIGRpZmZlcmVuY2UgdmFsdWVzLiBUaGUgUS1RIHBsb3QgaXMgZ3JhcGhpY2FsIG1ldGhvZCB0byBhc3Nlc3MgaWYgYSBkYXRhc2V0IGZvbGxvd3MgYSBub3JtYWwgZGlzdHJpYnV0aW9uLiBUaGUgcmVkIGxpbmUgYWRkZWQgYnkgcXFsaW5lKCkgcmVwcmVzZW50cyB0aGUgZXhwZWN0ZWQgbGluZSBpZiB0aGUgZGF0YSB3ZXJlIHBlcmZlY3RseSBub3JtYWxseSBkaXN0cmlidXRlZC4gVGhlIGhpc3RvZ3JhbSBwcm92aWRlcyBhIHZpc3VhbCByZXByZXNlbnRhdGlvbiBvZiB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBkYXRhLgpgYGB7cn0KIyBUaGlzIGZvY3VzZXMgdGhlIGFuYWx5c2lzIG9uIGNsb3NlbHkgcmVsYXRlZCB0YXhhCmZpdEQwOEQwM19jbG9zZSA8LSBmaXREMDhEMDNfcmVzdWx0cyAlPiUgCiAgZmlsdGVyKEFic0Rpc3RhbmNlIDwgMSkKCiMgR3JhcGhpY2FsIFN1bW1hcmllcwoKIyBDcmVhdGUgYSBRLVEgKFF1YW50aWxlLVF1YW50aWxlKSBwbG90CnFxbm9ybShmaXREMDhEMDNfY2xvc2UkZml0RDA4RDAzQWJzRGlmZikKcXFsaW5lKGZpdEQwOEQwM19jbG9zZSRmaXREMDhEMDNBYnNEaWZmLCBjb2wgPSAicmVkIikKCiMgQ3JlYXRlIGEgaGlzdG9ncmFtIHBsb3QKaGlzdChmaXREMDhEMDNfY2xvc2UkZml0RDA4RDAzQWJzRGlmZiwgbWFpbiA9ICJIaXN0b2dyYW0gb2YgQ2xvc2UgRGF0YSIpCmBgYAoKKipGYXItUmVsYXRlZCBQYWlyczoqKiBTdWJzZXQgdG8gaW5jbHVkZSBvbmx5IHBhaXJzIG9mIHRheGEgd2l0aCBhbiBhYnNvbHV0ZSBwaHlsb2dlbmV0aWMgZGlzdGFuY2UgZ3JlYXRlciB0aGFuIDEuIEdyYXBoaWNhbCBzdW1tYXJpZXMgaW5jbHVkZSBhIFEtUSAoUXVhbnRpbGUtUXVhbnRpbGUpIHBsb3QgYW5kIGhpc3Ryb2dyYW0gb2YgdGhlIGFic29sdXRlIGRpZmZlcmVuY2UgdmFsdWVzLiBUaGUgUS1RIHBsb3QgaXMgZ3JhcGhpY2FsIG1ldGhvZCB0byBhc3Nlc3MgaWYgYSBkYXRhc2V0IGZvbGxvd3MgYSBub3JtYWwgZGlzdHJpYnV0aW9uLiBUaGUgcmVkIGxpbmUgYWRkZWQgYnkgcXFsaW5lKCkgcmVwcmVzZW50cyB0aGUgZXhwZWN0ZWQgbGluZSBpZiB0aGUgZGF0YSB3ZXJlIHBlcmZlY3RseSBub3JtYWxseSBkaXN0cmlidXRlZC4gVGhlIGhpc3RvZ3JhbSBwcm92aWRlcyBhIHZpc3VhbCByZXByZXNlbnRhdGlvbiBvZiB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBkYXRhLgpgYGB7cn0KIyBUaGlzIGZvY3VzZXMgdGhlIGFuYWx5c2lzIG9uIGRpc3RhbnRseSByZWxhdGVkIHRheGEKZml0RDA4RDAzX2ZhciA8LSBmaXREMDhEMDNfcmVzdWx0cyAlPiUgCiAgZmlsdGVyKEFic0Rpc3RhbmNlID4gMSkKCiMgR3JhcGhpY2FsIFN1bW1hcmllcwoKIyBDcmVhdGUgYSBRLVEgKFF1YW50aWxlLVF1YW50aWxlKSBwbG90CnFxbm9ybShmaXREMDhEMDNfZmFyJGZpdEQwOEQwM0Fic0RpZmYpCnFxbGluZShmaXREMDhEMDNfZmFyJGZpdEQwOEQwM0Fic0RpZmYsIGNvbCA9ICJyZWQiKQoKIyBDcmVhdGUgYSBoaXN0b2dyYW0gcGxvdApoaXN0KGZpdEQwOEQwM19mYXIkZml0RDA4RDAzQWJzRGlmZiwgbWFpbiA9ICJIaXN0b2dyYW0gb2YgRmFyIERhdGEiKQpgYGAKCioqV2lsY294IFRlc3Q6KiogUGVyZm9ybSBXaWxjb3hvbiBSYW5rIFN1bSBUZXN0IChhbHNvIGtub3duIGFzIE1hbm4tV2hpdG5leSBVIHRlc3QpIGNvbXBhcmluZyB0aGUgYWJzb2x1dGUgZGlzdGFuY2UgdmFsdWVzIGJldHdlZW4gdGhlIGNsb3NlLXJlbGF0ZWQgYW5kIGZhci1yZWxhdGVkIGdyb3Vwcy4KYGBge3J9CiMgV2lsY294IHRlc3QgYmV0d2VlbiBjbG9zZSBhbmQgZmFyIHJlbGF0ZWQgZGlzdGFuY2VzCndpbGNveF9yZXN1bHRfZml0RDA4RDAzIDwtIHdpbGNveC50ZXN0KGZpdEQwOEQwM19jbG9zZSRmaXREMDhEMDNBYnNEaWZmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaXREMDhEMDNfZmFyJGZpdEQwOEQwM0Fic0RpZmYpCgpwcmludCh3aWxjb3hfcmVzdWx0X2ZpdEQwOEQwMykKYGBgCgoqKkNvbWJpbmUgRGF0YToqKiBFeGFtaW5lIHRoZSBkaXN0cmlidXRpb25zIG9mIHRoZXNlIGRpZmZlcmVuY2VzIHRvIHVuZGVyc3RhbmQgaG93IHRoZXkgZGlmZmVyIGFuZCB3aGV0aGVyIHRoZXNlIGRpZmZlcmVuY2VzIGFyZSBnZW5lcmFsbHkgbGFyZ2VyIG9yIHNtYWxsZXIgZm9yIGNsb3NlbHkgcmVsYXRlZCB0YXhhLgpgYGB7cn0KIyBDb21iaW5lIHRoZSBkYXRhIGFuZCBhZGQgYSBncm91cCBsYWJlbApmaXREMDhEMDNfY29tYmluZWRfZGF0YSA8LSBiaW5kX3Jvd3MoCiAgbXV0YXRlKGZpdEQwOEQwM19jbG9zZSwgZ3JvdXAgPSAiQ2xvc2UiKSwKICBtdXRhdGUoZml0RDA4RDAzX2ZhciwgZ3JvdXAgPSAiRmFyIikpCgojIFJlbW92ZSBOQSB2YWx1ZXMKZml0RDA4RDAzX2NvbWJpbmVkX2RhdGEgPC0gZml0RDA4RDAzX2NvbWJpbmVkX2RhdGEgJT4lIAogIGZpbHRlcighaXMubmEoZml0RDA4RDAzQWJzRGlmZikpCgojIENyZWF0ZSBhIGJveCBwbG90CmZpdEQwOEQwM19ib3hwbG90IDwtIGdncGxvdChmaXREMDhEMDNfY29tYmluZWRfZGF0YSwgYWVzKHggPSBncm91cCwgeSA9IGZpdEQwOEQwM0Fic0RpZmYpKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIGdlb21faml0dGVyKGFscGhhID0gMC4xLCB3aWR0aCA9IDAuMikgKyAgIyBBZGQgaW5kaXZpZHVhbCBwb2ludHMgd2l0aCBzb21lIHRyYW5zcGFyZW5jeQogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIGZpdEQwOEQwMyBEaWZmZXJlbmNlcyIsCiAgICAgICB4ID0gIlBoeWxvZ2VuZXRpYyBSZWxhdGlvbnNoaXAiLAogICAgICAgeSA9ICJBYnNvbHV0ZSBEaWZmZXJlbmNlIGluIGZpdEQwOEQwMyIpICsKICB0aGVtZV9taW5pbWFsKCkKCiMgRGlzcGxheSB0aGUgcGxvdApwcmludChmaXREMDhEMDNfYm94cGxvdCkKCiMgQ2FsY3VsYXRlIHN1bW1hcnkgc3RhdGlzdGljcwpmaXREMDhEMDNfc3VtbWFyeV9zdGF0cyA8LSBmaXREMDhEMDNfY29tYmluZWRfZGF0YSAlPiUKICBncm91cF9ieShncm91cCkgJT4lCiAgc3VtbWFyaXNlKAogICAgbiA9IG4oKSwKICAgIG1lYW4gPSBtZWFuKGZpdEQwOEQwM0Fic0RpZmYpLAogICAgbWVkaWFuID0gbWVkaWFuKGZpdEQwOEQwM0Fic0RpZmYpLAogICAgc2QgPSBzZChmaXREMDhEMDNBYnNEaWZmKSwKICAgIG1pbiA9IG1pbihmaXREMDhEMDNBYnNEaWZmKSwKICAgIG1heCA9IG1heChmaXREMDhEMDNBYnNEaWZmKQogICkKCiMgUHJpbnQgc3VtbWFyeSBzdGF0aXN0aWNzCnByaW50KGZpdEQwOEQwM19zdW1tYXJ5X3N0YXRzKQoKIyBQZXJmb3JtIGEgZm9ybWFsIHRlc3QgZm9yIGRpZmZlcmVuY2UgaW4gbWVhbnMKZml0RDA4RDAzX3RfdGVzdF9yZXN1bHQgPC0gdC50ZXN0KGZpdEQwOEQwM0Fic0RpZmYgfiBncm91cCwgZGF0YSA9IGZpdEQwOEQwM19jb21iaW5lZF9kYXRhKQpwcmludChmaXREMDhEMDNfdF90ZXN0X3Jlc3VsdCkKYGBgCgojIyMjIDEwIHVnL21MIFRNUCBUcmVlCgpMaWIxNTogTUlDICgxMCB1Zy9tTCBUTVApCmBgYHtyfQp0cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzEwdG1wX2ZpdG5lc3NfbGFiZWxsZWQgPC0gZ2d0cmVlKEZpdHRyZWUxNWdvb2QsIGxheW91dD0iY2lyY3VsYXIiLCBicmFuY2gubGVuZ3RoPSJub25lIikgJTwrJQogIEZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQgKwogIGFlcyhjb2xvciA9IGlmZWxzZShmaXREMDlEMDMgPCAtMSwgIkxvdyBmaXRuZXNzIiwgIkhpZ2ggZml0bmVzcyIpKSArCiAgZ2VvbV90aXBwb2ludChhZXMoY29sb3IgPSBpZmVsc2UoZml0RDA5RDAzIDwgLTEsICJMb3cgZml0bmVzcyIsICJIaWdoIGZpdG5lc3MiKSksIHNpemU9MSwgYWxwaGE9MC44KSArCiAgI2dlb21fdGlwbGFiMihhZXMobGFiZWw9TkNCSS5waHlsdW0sIGFuZ2xlPWFuZ2xlKSwgc2l6ZT0xKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwoCiAgICAiRml0bmVzcyIsCiAgICB2YWx1ZXMgPSBjKCJMb3cgZml0bmVzcyIgPSAiZGFya2JsdWUiLCAiSGlnaCBmaXRuZXNzIiA9ICJnb2xkIiksCiAgICBuYS52YWx1ZSA9ICJncmF5IgogICkgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJGaXRuZXNzIikpCgp0cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzEwdG1wX2ZpdG5lc3NfbGFiZWxsZWQKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIE1JQyBGaXRuZXNzIFRyZWUgKHRpcHMgbGFiZWxlZCkKZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL1RyZWVzL1RNUC9MaWIxNS5GYXN0VHJlZS41QkNzLnBlcmZlY3RzLmdvb2QuZml0RDA5RDAzLjEwdG1wLnRpcHMubGFiZWxsZWQucGh5bHVtLnBuZyIsCiAgICAgICBwbG90PXRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMTB0bXBfZml0bmVzc19sYWJlbGxlZCwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gMTIsIGhlaWdodCA9IDcuNSwgdW5pdHMgPSAiaW4iKQpgYGAKCkNvdW50IHRoZSBudW1iZXIgb2YgY29tcGxlbWVudGluZyBob21vbG9ncyAoZml0ID4gLTEpIGFuZCBkcm9wb3V0IGhvbW9sb2dzIChmaXQgPCAtMSk6CmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIENvdW50IHVuaXF1ZSBJRHMgd2l0aCBmaXREMDlEMDMgPiAtMQpoaWdoX2ZpdG5lc3NfY291bnQgPC0gRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCAlPiUKICBmaWx0ZXIoZml0RDA5RDAzID4gLTEpICU+JQogIGRpc3RpbmN0KElEKSAlPiUKICBucm93KCkKCiMgQ291bnQgdW5pcXVlIElEcyB3aXRoIGZpdEQwOUQwMyA8IC0xCmxvd19maXRuZXNzX2NvdW50IDwtIEZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQgJT4lCiAgZmlsdGVyKGZpdEQwOUQwMyA8IC0xKSAlPiUKICBkaXN0aW5jdChJRCkgJT4lCiAgbnJvdygpCgojIFByaW50IHRoZSByZXN1bHRzCmNhdCgiTnVtYmVyIG9mIHVuaXF1ZSBJRHMgd2l0aCBmaXREMDlEMDMgPiAtMToiLCBoaWdoX2ZpdG5lc3NfY291bnQsICJcbiIpCmNhdCgiTnVtYmVyIG9mIHVuaXF1ZSBJRHMgd2l0aCBmaXREMDlEMDMgPCAtMToiLCBsb3dfZml0bmVzc19jb3VudCwgIlxuIikKYGBgCgpCdWlsZCB0aGUgaW5pdGlhbCBESEZSIHRyZWUgd2l0aCBjb2xvci1zY2FsZSBiYXNlZCBvbiBmaXRuZXNzIGF0IENPTVBMRU1FTlRBVElPTiBhbmQgYWRkIGFuIGFkZGl0aW9uYWwgcmluZyByZXByZXNlbnRpbmcgdGF4b25vbWljIGxpbmVhZ2VzIGF0IHRoZSAqKlBoeWx1bS1sZXZlbCoqLgpgYGB7cn0KdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8xMHRtcF9maXRuZXNzX3BoeWx1bV9wMSA8LSBnZ3RyZWUoRml0dHJlZTE1Z29vZCwgbGF5b3V0PSJjaXJjdWxhciIsIGJyYW5jaC5sZW5ndGg9Im5vbmUiKSAlPCslCiAgRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCArCiAgYWVzKGNvbG9yID0gaWZlbHNlKGZpdEQwOUQwMyA8IC0xLCAiTG93IGZpdG5lc3MiLCAiSGlnaCBmaXRuZXNzIikpICsKICBnZW9tX3RpcHBvaW50KGFlcyhjb2xvciA9IGlmZWxzZShmaXREMDlEMDMgPCAtMSwgIkxvdyBmaXRuZXNzIiwgIkhpZ2ggZml0bmVzcyIpKSwgc2l6ZT0xLCBhbHBoYT0wLjgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249InJpZ2h0IikgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwoCiAgICAiRml0bmVzcyIsCiAgICB2YWx1ZXMgPSBjKCJMb3cgZml0bmVzcyIgPSAiZGFya2JsdWUiLCAiSGlnaCBmaXRuZXNzIiA9ICJnb2xkIiksCiAgICBuYS52YWx1ZSA9ICJncmF5IikKCnRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMTB0bXBfZml0bmVzc19waHlsdW1fcDIgPC0gdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8xMHRtcF9maXRuZXNzX3BoeWx1bV9wMSArCiAgbmV3X3NjYWxlX2ZpbGwoKSArCiAgZ2VvbV9mcnVpdCgKICAgIGdlb209Z2VvbV90aWxlLAogICAgbWFwcGluZz1hZXMoZmlsbD1OQ0JJLnBoeWx1bSksICAjIENoYW5nZWQgdGhpcyBsaW5lCiAgICB3aWR0aD0zLAogICAgb2Zmc2V0PTAuMQogICkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKAogICAgbmFtZT0iUGh5bHVtIiwKICAgIHZhbHVlcz1zZXROYW1lcyhGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJE5DQkkucGh5bHVtX2NvbG9ycywgRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCROQ0JJLnBoeWx1bSksCiAgICBndWlkZT1ndWlkZV9sZWdlbmQoa2V5d2lkdGg9MC4zLCBrZXloZWlnaHQ9MC4zLCBuY29sPTEpCiAgKSArCiAgdGhlbWUoCiAgICBsZWdlbmQudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTApLCAKICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgbGVnZW5kLnNwYWNpbmcueSA9IHVuaXQoMC4yLCAiY20iKQogICkKCnRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMTB0bXBfZml0bmVzc19waHlsdW1fcDIKYGBgCgoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL1RyZWVzL1RNUC9MaWIxNS5GYXN0VHJlZS41QkNzLnBlcmZlY3RzLmdvb2QuZml0RDA5RDAzLjEwdG1wLlBoeWx1bS5SSU5HLnBuZyIsIHBsb3Q9dHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8xMHRtcF9maXRuZXNzX3BoeWx1bV9wMiwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gMTIsIGhlaWdodCA9IDcuNSwgdW5pdHMgPSAiaW4iKQpgYGAKCiMjIyMjIFN1bW1hcnkgU3RhdHMKClRoZSBmb2xsb3dpbmcgc3RhdGlzdGljYWwgdGVzdHMgd2lsbCBkZXRlcm1pbmUgaWYgZml0bmVzcyBjbHVzdGVycyBieSBldm9sdXRpb25hcnkgZGlzdGFuY2Ugd2l0aGluIHRoZSBwaHlsb2dlbmV0aWMgdHJlZQpgYGB7cn0KIyBDcmVhdGUgYSBmdW5jdGlvbiB0byBjYWxjdWxhdGUgZGlzdGFuY2VzIGFuZCBkaWZmZXJlbmNlcyBiZXR3ZWVuIApmaXREMDlEMDNfZGlzdF9kaWZmIDwtIGZ1bmN0aW9uKHBhaXJ3aXNlX2Rpc3RhbmNlcywgdGF4YV9tZXJnZWQpIHsKICBkaXN0YW5jZV9kZiA8LSBhcy5kYXRhLmZyYW1lKGFzLnRhYmxlKHBhaXJ3aXNlX2Rpc3RhbmNlcykpICU+JQogICAgcmVuYW1lKFRpcDEgPSBWYXIxLCBUaXAyID0gVmFyMiwgRGlzdGFuY2UgPSBGcmVxKSAlPiUKICAgIG11dGF0ZShUaXAxID0gYXMuY2hhcmFjdGVyKFRpcDEpLAogICAgICAgICAgIFRpcDIgPSBhcy5jaGFyYWN0ZXIoVGlwMikpICU+JQogICAgZmlsdGVyKFRpcDEgIT0gVGlwMikgICMgUmVtb3ZlIHNlbGYtY29tcGFyaXNvbnMKICAKICByZXN1bHRzIDwtIGRpc3RhbmNlX2RmICU+JQogICAgbGVmdF9qb2luKHRheGFfbWVyZ2VkLCBieSA9IGMoIlRpcDEiID0gIklEIikpICU+JQogICAgcmVuYW1lKGZpdEQwOUQwM3YxID0gZml0RDA5RDAzKSAlPiUKICAgIGxlZnRfam9pbih0YXhhX21lcmdlZCwgYnkgPSBjKCJUaXAyIiA9ICJJRCIpKSAlPiUKICAgIHJlbmFtZShmaXREMDlEMDN2MiA9IGZpdEQwOUQwMykgJT4lCiAgICBtdXRhdGUoCiAgICAgIEFic0Rpc3RhbmNlID0gYWJzKERpc3RhbmNlKSwKICAgICAgZml0RDA5RDAzQWJzRGlmZiA9IGFicyhmaXREMDlEMDN2MSAtIGZpdEQwOUQwM3YyKQogICAgKSAlPiUKICAgIHNlbGVjdChUaXAxLCBUaXAyLCBBYnNEaXN0YW5jZSwgZml0RDA5RDAzQWJzRGlmZikKICAKICByZXR1cm4ocmVzdWx0cyl9CmBgYAoKKipQZWFyc29uIENvcnJlbGF0aW9uOioqIENhbGN1bGF0ZSBwYWlyd2lzZSBkaXN0YW5jZXMgYmV0d2VlbiB0aXBzIGFuZCBkaWZmZXJlbmNlcyBpbiBmaXRuZXNzIHZhbHVlcy4gUGxvdCBkaXN0YW5jZXMgYW5kIGRpZmZlcmVuY2VzIGFzIHNjYXR0ZXIgcGxvdCBhbmQgdGVzdCBmb3Igc2lnbmlmaWNhbnQgY29ycmVsYXRpb24gdXNpbmcgUGVhcnNvbi4KYGBge3J9CiMgQ2FsY3VsYXRlIHBhaXJ3aXNlIGRpc3RhbmNlcwpmaXREMDlEMDNfcGFpcndpc2VfZGlzdGFuY2VzIDwtIGNvcGhlbmV0aWMucGh5bG8oRml0dHJlZTE1Z29vZCkKCiMgQ2FsY3VsYXRlIGRpc3RhbmNlcyBhbmQgZGlmZmVyZW5jZXMKZml0RDA5RDAzX3Jlc3VsdHMgPC0gZml0RDA5RDAzX2Rpc3RfZGlmZihmaXREMDlEMDNfcGFpcndpc2VfZGlzdGFuY2VzLEZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQpCgojIENyZWF0ZSB0aGUgc2NhdHRlciBwbG90CmZpdEQwOUQwM19zY2F0dGVyX3Bsb3QgPC0gZ2dwbG90KGZpdEQwOUQwM19yZXN1bHRzLCBhZXMoeCA9IEFic0Rpc3RhbmNlLCB5ID0gZml0RDA5RDAzQWJzRGlmZikpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC41KSArICAjIEFkZCB0cmFuc3BhcmVuY3kgdG8gcG9pbnRzCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3IgPSAicmVkIiwgc2UgPSBGQUxTRSkgKyAgIyBBZGQgYSBsaW5lYXIgdHJlbmQgbGluZQogIHN0YXRfY29yKG1ldGhvZCA9ICJwZWFyc29uIiwgcC5hY2N1cmFjeSA9IDAuMDAwMDAxLCByLmFjY3VyYWN5ID0gMC4wMSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicyh0aXRsZSA9ICJBYnNvbHV0ZSBEaXN0YW5jZSB2cyBBYnNvbHV0ZSBEaWZmZXJlbmNlIGluIGZpdEQwOUQwMyIsCiAgICAgICB4ID0gIkFic29sdXRlIFZhbHVlIG9mIERpc3RhbmNlIiwKICAgICAgIHkgPSAiQWJzb2x1dGUgVmFsdWUgb2YgRGlmZmVyZW5jZSBpbiBmaXREMDlEMDMiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKIyBEaXNwbGF5IHRoZSBwbG90CnByaW50KGZpdEQwOUQwM19zY2F0dGVyX3Bsb3QpCgojIE9wdGlvbmFsOiBTYXZlIHRoZSBwbG90Cmdnc2F2ZSgiUGVyZmVjdHMvUExPVFMvV2lsY294L2ZpdEQwOUQwM19wZWFyc29uX3NjYXR0ZXJfcGxvdC5wbmciLCAKICAgICAgIGZpdEQwOUQwM19zY2F0dGVyX3Bsb3QsIHdpZHRoID0gMTAsIGhlaWdodCA9IDgsIGRwaSA9IDMwMCkKYGBgCgoqKkNsb3NlLVJlbGF0ZWQgUGFpcnM6KiogU3Vic2V0IHRvIGluY2x1ZGUgb25seSBwYWlycyBvZiB0YXhhIHdpdGggYW4gYWJzb2x1dGUgcGh5bG9nZW5ldGljIGRpc3RhbmNlIGxlc3MgdGhhbiAxLiBHcmFwaGljYWwgc3VtbWFyaWVzIGluY2x1ZGUgYSBRLVEgKFF1YW50aWxlLVF1YW50aWxlKSBwbG90IGFuZCBoaXN0cm9ncmFtIG9mIHRoZSBhYnNvbHV0ZSBkaWZmZXJlbmNlIHZhbHVlcy4gVGhlIFEtUSBwbG90IGlzIGdyYXBoaWNhbCBtZXRob2QgdG8gYXNzZXNzIGlmIGEgZGF0YXNldCBmb2xsb3dzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbi4gVGhlIHJlZCBsaW5lIGFkZGVkIGJ5IHFxbGluZSgpIHJlcHJlc2VudHMgdGhlIGV4cGVjdGVkIGxpbmUgaWYgdGhlIGRhdGEgd2VyZSBwZXJmZWN0bHkgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuIFRoZSBoaXN0b2dyYW0gcHJvdmlkZXMgYSB2aXN1YWwgcmVwcmVzZW50YXRpb24gb2YgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZGF0YS4KYGBge3J9CiMgVGhpcyBmb2N1c2VzIHRoZSBhbmFseXNpcyBvbiBjbG9zZWx5IHJlbGF0ZWQgdGF4YQpmaXREMDlEMDNfY2xvc2UgPC0gZml0RDA5RDAzX3Jlc3VsdHMgJT4lIAogIGZpbHRlcihBYnNEaXN0YW5jZSA8IDEpCgojIEdyYXBoaWNhbCBTdW1tYXJpZXMKCiMgQ3JlYXRlIGEgUS1RIChRdWFudGlsZS1RdWFudGlsZSkgcGxvdApxcW5vcm0oZml0RDA5RDAzX2Nsb3NlJGZpdEQwOUQwM0Fic0RpZmYpCnFxbGluZShmaXREMDlEMDNfY2xvc2UkZml0RDA5RDAzQWJzRGlmZiwgY29sID0gInJlZCIpCgojIENyZWF0ZSBhIGhpc3RvZ3JhbSBwbG90Cmhpc3QoZml0RDA5RDAzX2Nsb3NlJGZpdEQwOUQwM0Fic0RpZmYsIG1haW4gPSAiSGlzdG9ncmFtIG9mIENsb3NlIERhdGEiKQpgYGAKCioqRmFyLVJlbGF0ZWQgUGFpcnM6KiogU3Vic2V0IHRvIGluY2x1ZGUgb25seSBwYWlycyBvZiB0YXhhIHdpdGggYW4gYWJzb2x1dGUgcGh5bG9nZW5ldGljIGRpc3RhbmNlIGdyZWF0ZXIgdGhhbiAxLiBHcmFwaGljYWwgc3VtbWFyaWVzIGluY2x1ZGUgYSBRLVEgKFF1YW50aWxlLVF1YW50aWxlKSBwbG90IGFuZCBoaXN0cm9ncmFtIG9mIHRoZSBhYnNvbHV0ZSBkaWZmZXJlbmNlIHZhbHVlcy4gVGhlIFEtUSBwbG90IGlzIGdyYXBoaWNhbCBtZXRob2QgdG8gYXNzZXNzIGlmIGEgZGF0YXNldCBmb2xsb3dzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbi4gVGhlIHJlZCBsaW5lIGFkZGVkIGJ5IHFxbGluZSgpIHJlcHJlc2VudHMgdGhlIGV4cGVjdGVkIGxpbmUgaWYgdGhlIGRhdGEgd2VyZSBwZXJmZWN0bHkgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuIFRoZSBoaXN0b2dyYW0gcHJvdmlkZXMgYSB2aXN1YWwgcmVwcmVzZW50YXRpb24gb2YgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZGF0YS4KYGBge3J9CiMgVGhpcyBmb2N1c2VzIHRoZSBhbmFseXNpcyBvbiBkaXN0YW50bHkgcmVsYXRlZCB0YXhhCmZpdEQwOUQwM19mYXIgPC0gZml0RDA5RDAzX3Jlc3VsdHMgJT4lIAogIGZpbHRlcihBYnNEaXN0YW5jZSA+IDEpCgojIEdyYXBoaWNhbCBTdW1tYXJpZXMKCiMgQ3JlYXRlIGEgUS1RIChRdWFudGlsZS1RdWFudGlsZSkgcGxvdApxcW5vcm0oZml0RDA5RDAzX2ZhciRmaXREMDlEMDNBYnNEaWZmKQpxcWxpbmUoZml0RDA5RDAzX2ZhciRmaXREMDlEMDNBYnNEaWZmLCBjb2wgPSAicmVkIikKCiMgQ3JlYXRlIGEgaGlzdG9ncmFtIHBsb3QKaGlzdChmaXREMDlEMDNfZmFyJGZpdEQwOUQwM0Fic0RpZmYsIG1haW4gPSAiSGlzdG9ncmFtIG9mIEZhciBEYXRhIikKYGBgCgoqKldpbGNveCBUZXN0OioqIFBlcmZvcm0gV2lsY294b24gUmFuayBTdW0gVGVzdCAoYWxzbyBrbm93biBhcyBNYW5uLVdoaXRuZXkgVSB0ZXN0KSBjb21wYXJpbmcgdGhlIGFic29sdXRlIGRpc3RhbmNlIHZhbHVlcyBiZXR3ZWVuIHRoZSBjbG9zZS1yZWxhdGVkIGFuZCBmYXItcmVsYXRlZCBncm91cHMuCmBgYHtyfQojIFdpbGNveCB0ZXN0IGJldHdlZW4gY2xvc2UgYW5kIGZhciByZWxhdGVkIGRpc3RhbmNlcwp3aWxjb3hfcmVzdWx0X2ZpdEQwOUQwMyA8LSB3aWxjb3gudGVzdChmaXREMDlEMDNfY2xvc2UkZml0RDA5RDAzQWJzRGlmZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZml0RDA5RDAzX2ZhciRmaXREMDlEMDNBYnNEaWZmKQoKcHJpbnQod2lsY294X3Jlc3VsdF9maXREMDlEMDMpCmBgYAoKKipDb21iaW5lIERhdGE6KiogRXhhbWluZSB0aGUgZGlzdHJpYnV0aW9ucyBvZiB0aGVzZSBkaWZmZXJlbmNlcyB0byB1bmRlcnN0YW5kIGhvdyB0aGV5IGRpZmZlciBhbmQgd2hldGhlciB0aGVzZSBkaWZmZXJlbmNlcyBhcmUgZ2VuZXJhbGx5IGxhcmdlciBvciBzbWFsbGVyIGZvciBjbG9zZWx5IHJlbGF0ZWQgdGF4YS4KYGBge3J9CiMgQ29tYmluZSB0aGUgZGF0YSBhbmQgYWRkIGEgZ3JvdXAgbGFiZWwKZml0RDA5RDAzX2NvbWJpbmVkX2RhdGEgPC0gYmluZF9yb3dzKAogIG11dGF0ZShmaXREMDlEMDNfY2xvc2UsIGdyb3VwID0gIkNsb3NlIiksCiAgbXV0YXRlKGZpdEQwOUQwM19mYXIsIGdyb3VwID0gIkZhciIpKQoKIyBSZW1vdmUgTkEgdmFsdWVzCmZpdEQwOUQwM19jb21iaW5lZF9kYXRhIDwtIGZpdEQwOUQwM19jb21iaW5lZF9kYXRhICU+JSAKICBmaWx0ZXIoIWlzLm5hKGZpdEQwOUQwM0Fic0RpZmYpKQoKIyBDcmVhdGUgYSBib3ggcGxvdApmaXREMDlEMDNfYm94cGxvdCA8LSBnZ3Bsb3QoZml0RDA5RDAzX2NvbWJpbmVkX2RhdGEsIGFlcyh4ID0gZ3JvdXAsIHkgPSBmaXREMDlEMDNBYnNEaWZmKSkgKwogIGdlb21fYm94cGxvdCgpICsKICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuMSwgd2lkdGggPSAwLjIpICsgICMgQWRkIGluZGl2aWR1YWwgcG9pbnRzIHdpdGggc29tZSB0cmFuc3BhcmVuY3kKICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBmaXREMDlEMDMgRGlmZmVyZW5jZXMiLAogICAgICAgeCA9ICJQaHlsb2dlbmV0aWMgUmVsYXRpb25zaGlwIiwKICAgICAgIHkgPSAiQWJzb2x1dGUgRGlmZmVyZW5jZSBpbiBmaXREMDlEMDMiKSArCiAgdGhlbWVfbWluaW1hbCgpCgojIERpc3BsYXkgdGhlIHBsb3QKcHJpbnQoZml0RDA5RDAzX2JveHBsb3QpCgojIENhbGN1bGF0ZSBzdW1tYXJ5IHN0YXRpc3RpY3MKZml0RDA5RDAzX3N1bW1hcnlfc3RhdHMgPC0gZml0RDA5RDAzX2NvbWJpbmVkX2RhdGEgJT4lCiAgZ3JvdXBfYnkoZ3JvdXApICU+JQogIHN1bW1hcmlzZSgKICAgIG4gPSBuKCksCiAgICBtZWFuID0gbWVhbihmaXREMDlEMDNBYnNEaWZmKSwKICAgIG1lZGlhbiA9IG1lZGlhbihmaXREMDlEMDNBYnNEaWZmKSwKICAgIHNkID0gc2QoZml0RDA5RDAzQWJzRGlmZiksCiAgICBtaW4gPSBtaW4oZml0RDA5RDAzQWJzRGlmZiksCiAgICBtYXggPSBtYXgoZml0RDA5RDAzQWJzRGlmZikKICApCgojIFByaW50IHN1bW1hcnkgc3RhdGlzdGljcwpwcmludChmaXREMDlEMDNfc3VtbWFyeV9zdGF0cykKCiMgUGVyZm9ybSBhIGZvcm1hbCB0ZXN0IGZvciBkaWZmZXJlbmNlIGluIG1lYW5zCmZpdEQwOUQwM190X3Rlc3RfcmVzdWx0IDwtIHQudGVzdChmaXREMDlEMDNBYnNEaWZmIH4gZ3JvdXAsIGRhdGEgPSBmaXREMDlEMDNfY29tYmluZWRfZGF0YSkKcHJpbnQoZml0RDA5RDAzX3RfdGVzdF9yZXN1bHQpCmBgYAoKIyMjIyA1MCB1Zy9tTCBUTVAgVHJlZQoKTGliMTU6IE1JQyAoNTAgdWcvbUwgVE1QKQpgYGB7cn0KdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF81MHRtcF9maXRuZXNzX2xhYmVsbGVkIDwtIGdndHJlZShGaXR0cmVlMTVnb29kLCBsYXlvdXQ9ImNpcmN1bGFyIiwgYnJhbmNoLmxlbmd0aD0ibm9uZSIpICU8KyUKICBGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkICsKICBhZXMoY29sb3IgPSBpZmVsc2UoZml0RDEwRDAzIDwgLTEsICJMb3cgZml0bmVzcyIsICJIaWdoIGZpdG5lc3MiKSkgKwogIGdlb21fdGlwcG9pbnQoYWVzKGNvbG9yID0gaWZlbHNlKGZpdEQxMEQwMyA8IC0xLCAiTG93IGZpdG5lc3MiLCAiSGlnaCBmaXRuZXNzIikpLCBzaXplPTEsIGFscGhhPTAuOCkgKwogICNnZW9tX3RpcGxhYjIoYWVzKGxhYmVsPU5DQkkucGh5bHVtLCBhbmdsZT1hbmdsZSksIHNpemU9MSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKAogICAgIkZpdG5lc3MiLAogICAgdmFsdWVzID0gYygiTG93IGZpdG5lc3MiID0gImRhcmtibHVlIiwgIkhpZ2ggZml0bmVzcyIgPSAiZ29sZCIpLAogICAgbmEudmFsdWUgPSAiZ3JheSIKICApICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiRml0bmVzcyIpKQoKdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF81MHRtcF9maXRuZXNzX2xhYmVsbGVkCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KIyBNSUMgRml0bmVzcyBUcmVlICh0aXBzIGxhYmVsZWQpCmdnc2F2ZShmaWxlPSJQZXJmZWN0cy9QTE9UUy9UcmVlcy9UTVAvTGliMTUuRmFzdFRyZWUuNUJDcy5wZXJmZWN0cy5nb29kLmZpdEQxMEQwMy41MHRtcC50aXBzLmxhYmVsbGVkLnBoeWx1bS5wbmciLAogICAgICAgcGxvdD10cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzUwdG1wX2ZpdG5lc3NfbGFiZWxsZWQsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA3LjUsIHVuaXRzID0gImluIikKYGBgCgpDb3VudCB0aGUgbnVtYmVyIG9mIGNvbXBsZW1lbnRpbmcgaG9tb2xvZ3MgKGZpdCA+IC0xKSBhbmQgZHJvcG91dCBob21vbG9ncyAoZml0IDwgLTEpOgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBDb3VudCB1bmlxdWUgSURzIHdpdGggZml0RDEwRDAzID4gLTEKaGlnaF9maXRuZXNzX2NvdW50IDwtIEZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQgJT4lCiAgZmlsdGVyKGZpdEQxMEQwMyA+IC0xKSAlPiUKICBkaXN0aW5jdChJRCkgJT4lCiAgbnJvdygpCgojIENvdW50IHVuaXF1ZSBJRHMgd2l0aCBmaXREMTBEMDMgPCAtMQpsb3dfZml0bmVzc19jb3VudCA8LSBGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkICU+JQogIGZpbHRlcihmaXREMTBEMDMgPCAtMSkgJT4lCiAgZGlzdGluY3QoSUQpICU+JQogIG5yb3coKQoKIyBQcmludCB0aGUgcmVzdWx0cwpjYXQoIk51bWJlciBvZiB1bmlxdWUgSURzIHdpdGggZml0RDEwRDAzID4gLTE6IiwgaGlnaF9maXRuZXNzX2NvdW50LCAiXG4iKQpjYXQoIk51bWJlciBvZiB1bmlxdWUgSURzIHdpdGggZml0RDEwRDAzIDwgLTE6IiwgbG93X2ZpdG5lc3NfY291bnQsICJcbiIpCmBgYAoKQnVpbGQgdGhlIGluaXRpYWwgREhGUiB0cmVlIHdpdGggY29sb3Itc2NhbGUgYmFzZWQgb24gZml0bmVzcyBhdCBDT01QTEVNRU5UQVRJT04gYW5kIGFkZCBhbiBhZGRpdGlvbmFsIHJpbmcgcmVwcmVzZW50aW5nIHRheG9ub21pYyBsaW5lYWdlcyBhdCB0aGUgKipQaHlsdW0tbGV2ZWwqKi4KYGBge3J9CnRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfNTB0bXBfZml0bmVzc19waHlsdW1fcDEgPC0gZ2d0cmVlKEZpdHRyZWUxNWdvb2QsIGxheW91dD0iY2lyY3VsYXIiLCBicmFuY2gubGVuZ3RoPSJub25lIikgJTwrJQogIEZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQgKwogIGFlcyhjb2xvciA9IGlmZWxzZShmaXREMTBEMDMgPCAtMSwgIkxvdyBmaXRuZXNzIiwgIkhpZ2ggZml0bmVzcyIpKSArCiAgZ2VvbV90aXBwb2ludChhZXMoY29sb3IgPSBpZmVsc2UoZml0RDEwRDAzIDwgLTEsICJMb3cgZml0bmVzcyIsICJIaWdoIGZpdG5lc3MiKSksIHNpemU9MSwgYWxwaGE9MC44KSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJyaWdodCIpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKAogICAgIkZpdG5lc3MiLAogICAgdmFsdWVzID0gYygiTG93IGZpdG5lc3MiID0gImRhcmtibHVlIiwgIkhpZ2ggZml0bmVzcyIgPSAiZ29sZCIpLAogICAgbmEudmFsdWUgPSAiZ3JheSIpCgp0cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzUwdG1wX2ZpdG5lc3NfcGh5bHVtX3AyIDwtIHRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfNTB0bXBfZml0bmVzc19waHlsdW1fcDEgKwogIG5ld19zY2FsZV9maWxsKCkgKwogIGdlb21fZnJ1aXQoCiAgICBnZW9tPWdlb21fdGlsZSwKICAgIG1hcHBpbmc9YWVzKGZpbGw9TkNCSS5waHlsdW0pLCAgIyBDaGFuZ2VkIHRoaXMgbGluZQogICAgd2lkdGg9MywKICAgIG9mZnNldD0wLjEKICApICsKICBzY2FsZV9maWxsX21hbnVhbCgKICAgIG5hbWU9IlBoeWx1bSIsCiAgICB2YWx1ZXM9c2V0TmFtZXMoRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCROQ0JJLnBoeWx1bV9jb2xvcnMsIEZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQkTkNCSS5waHlsdW0pLAogICAgZ3VpZGU9Z3VpZGVfbGVnZW5kKGtleXdpZHRoPTAuMywga2V5aGVpZ2h0PTAuMywgbmNvbD0xKQogICkgKwogIHRoZW1lKAogICAgbGVnZW5kLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEwKSwgCiAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwKICAgIGxlZ2VuZC5zcGFjaW5nLnkgPSB1bml0KDAuMiwgImNtIikKICApCgp0cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzUwdG1wX2ZpdG5lc3NfcGh5bHVtX3AyCmBgYAoKCmBgYHtyIGVjaG89RkFMU0V9Cmdnc2F2ZShmaWxlPSJQZXJmZWN0cy9QTE9UUy9UcmVlcy9UTVAvTGliMTUuRmFzdFRyZWUuNUJDcy5wZXJmZWN0cy5nb29kLmZpdEQxMEQwMy41MHRtcC5QaHlsdW0uUklORy5wbmciLCBwbG90PXRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfNTB0bXBfZml0bmVzc19waHlsdW1fcDIsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA3LjUsIHVuaXRzID0gImluIikKYGBgCgojIyMjIyBTdW1tYXJ5IFN0YXRzCgpUaGUgZm9sbG93aW5nIHN0YXRpc3RpY2FsIHRlc3RzIHdpbGwgZGV0ZXJtaW5lIGlmIGZpdG5lc3MgY2x1c3RlcnMgYnkgZXZvbHV0aW9uYXJ5IGRpc3RhbmNlIHdpdGhpbiB0aGUgcGh5bG9nZW5ldGljIHRyZWUKYGBge3J9CiMgQ3JlYXRlIGEgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIGRpc3RhbmNlcyBhbmQgZGlmZmVyZW5jZXMgYmV0d2VlbiAKZml0RDEwRDAzX2Rpc3RfZGlmZiA8LSBmdW5jdGlvbihwYWlyd2lzZV9kaXN0YW5jZXMsIHRheGFfbWVyZ2VkKSB7CiAgZGlzdGFuY2VfZGYgPC0gYXMuZGF0YS5mcmFtZShhcy50YWJsZShwYWlyd2lzZV9kaXN0YW5jZXMpKSAlPiUKICAgIHJlbmFtZShUaXAxID0gVmFyMSwgVGlwMiA9IFZhcjIsIERpc3RhbmNlID0gRnJlcSkgJT4lCiAgICBtdXRhdGUoVGlwMSA9IGFzLmNoYXJhY3RlcihUaXAxKSwKICAgICAgICAgICBUaXAyID0gYXMuY2hhcmFjdGVyKFRpcDIpKSAlPiUKICAgIGZpbHRlcihUaXAxICE9IFRpcDIpICAjIFJlbW92ZSBzZWxmLWNvbXBhcmlzb25zCiAgCiAgcmVzdWx0cyA8LSBkaXN0YW5jZV9kZiAlPiUKICAgIGxlZnRfam9pbih0YXhhX21lcmdlZCwgYnkgPSBjKCJUaXAxIiA9ICJJRCIpKSAlPiUKICAgIHJlbmFtZShmaXREMTBEMDN2MSA9IGZpdEQxMEQwMykgJT4lCiAgICBsZWZ0X2pvaW4odGF4YV9tZXJnZWQsIGJ5ID0gYygiVGlwMiIgPSAiSUQiKSkgJT4lCiAgICByZW5hbWUoZml0RDEwRDAzdjIgPSBmaXREMTBEMDMpICU+JQogICAgbXV0YXRlKAogICAgICBBYnNEaXN0YW5jZSA9IGFicyhEaXN0YW5jZSksCiAgICAgIGZpdEQxMEQwM0Fic0RpZmYgPSBhYnMoZml0RDEwRDAzdjEgLSBmaXREMTBEMDN2MikKICAgICkgJT4lCiAgICBzZWxlY3QoVGlwMSwgVGlwMiwgQWJzRGlzdGFuY2UsIGZpdEQxMEQwM0Fic0RpZmYpCiAgCiAgcmV0dXJuKHJlc3VsdHMpfQpgYGAKCioqUGVhcnNvbiBDb3JyZWxhdGlvbjoqKiBDYWxjdWxhdGUgcGFpcndpc2UgZGlzdGFuY2VzIGJldHdlZW4gdGlwcyBhbmQgZGlmZmVyZW5jZXMgaW4gZml0bmVzcyB2YWx1ZXMuIFBsb3QgZGlzdGFuY2VzIGFuZCBkaWZmZXJlbmNlcyBhcyBzY2F0dGVyIHBsb3QgYW5kIHRlc3QgZm9yIHNpZ25pZmljYW50IGNvcnJlbGF0aW9uIHVzaW5nIFBlYXJzb24uCmBgYHtyfQojIENhbGN1bGF0ZSBwYWlyd2lzZSBkaXN0YW5jZXMKZml0RDEwRDAzX3BhaXJ3aXNlX2Rpc3RhbmNlcyA8LSBjb3BoZW5ldGljLnBoeWxvKEZpdHRyZWUxNWdvb2QpCgojIENhbGN1bGF0ZSBkaXN0YW5jZXMgYW5kIGRpZmZlcmVuY2VzCmZpdEQxMEQwM19yZXN1bHRzIDwtIGZpdEQxMEQwM19kaXN0X2RpZmYoZml0RDEwRDAzX3BhaXJ3aXNlX2Rpc3RhbmNlcyxGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkKQoKIyBDcmVhdGUgdGhlIHNjYXR0ZXIgcGxvdApmaXREMTBEMDNfc2NhdHRlcl9wbG90IDwtIGdncGxvdChmaXREMTBEMDNfcmVzdWx0cywgYWVzKHggPSBBYnNEaXN0YW5jZSwgeSA9IGZpdEQxMEQwM0Fic0RpZmYpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKyAgIyBBZGQgdHJhbnNwYXJlbmN5IHRvIHBvaW50cwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbG9yID0gInJlZCIsIHNlID0gRkFMU0UpICsgICMgQWRkIGEgbGluZWFyIHRyZW5kIGxpbmUKICBzdGF0X2NvcihtZXRob2QgPSAicGVhcnNvbiIsIHAuYWNjdXJhY3kgPSAwLjAwMDAwMSwgci5hY2N1cmFjeSA9IDAuMDEpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnModGl0bGUgPSAiQWJzb2x1dGUgRGlzdGFuY2UgdnMgQWJzb2x1dGUgRGlmZmVyZW5jZSBpbiBmaXREMTBEMDMiLAogICAgICAgeCA9ICJBYnNvbHV0ZSBWYWx1ZSBvZiBEaXN0YW5jZSIsCiAgICAgICB5ID0gIkFic29sdXRlIFZhbHVlIG9mIERpZmZlcmVuY2UgaW4gZml0RDEwRDAzIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiMgRGlzcGxheSB0aGUgcGxvdApwcmludChmaXREMTBEMDNfc2NhdHRlcl9wbG90KQoKIyBPcHRpb25hbDogU2F2ZSB0aGUgcGxvdApnZ3NhdmUoIlBlcmZlY3RzL1BMT1RTL1dpbGNveC9maXREMTBEMDNfcGVhcnNvbl9zY2F0dGVyX3Bsb3QucG5nIiwgCiAgICAgICBmaXREMTBEMDNfc2NhdHRlcl9wbG90LCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA4LCBkcGkgPSAzMDApCmBgYAoKKipDbG9zZS1SZWxhdGVkIFBhaXJzOioqIFN1YnNldCB0byBpbmNsdWRlIG9ubHkgcGFpcnMgb2YgdGF4YSB3aXRoIGFuIGFic29sdXRlIHBoeWxvZ2VuZXRpYyBkaXN0YW5jZSBsZXNzIHRoYW4gMS4gR3JhcGhpY2FsIHN1bW1hcmllcyBpbmNsdWRlIGEgUS1RIChRdWFudGlsZS1RdWFudGlsZSkgcGxvdCBhbmQgaGlzdHJvZ3JhbSBvZiB0aGUgYWJzb2x1dGUgZGlmZmVyZW5jZSB2YWx1ZXMuIFRoZSBRLVEgcGxvdCBpcyBncmFwaGljYWwgbWV0aG9kIHRvIGFzc2VzcyBpZiBhIGRhdGFzZXQgZm9sbG93cyBhIG5vcm1hbCBkaXN0cmlidXRpb24uIFRoZSByZWQgbGluZSBhZGRlZCBieSBxcWxpbmUoKSByZXByZXNlbnRzIHRoZSBleHBlY3RlZCBsaW5lIGlmIHRoZSBkYXRhIHdlcmUgcGVyZmVjdGx5IG5vcm1hbGx5IGRpc3RyaWJ1dGVkLiBUaGUgaGlzdG9ncmFtIHByb3ZpZGVzIGEgdmlzdWFsIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGRhdGEuCmBgYHtyfQojIFRoaXMgZm9jdXNlcyB0aGUgYW5hbHlzaXMgb24gY2xvc2VseSByZWxhdGVkIHRheGEKZml0RDEwRDAzX2Nsb3NlIDwtIGZpdEQxMEQwM19yZXN1bHRzICU+JSAKICBmaWx0ZXIoQWJzRGlzdGFuY2UgPCAxKQoKIyBHcmFwaGljYWwgU3VtbWFyaWVzCgojIENyZWF0ZSBhIFEtUSAoUXVhbnRpbGUtUXVhbnRpbGUpIHBsb3QKcXFub3JtKGZpdEQxMEQwM19jbG9zZSRmaXREMTBEMDNBYnNEaWZmKQpxcWxpbmUoZml0RDEwRDAzX2Nsb3NlJGZpdEQxMEQwM0Fic0RpZmYsIGNvbCA9ICJyZWQiKQoKIyBDcmVhdGUgYSBoaXN0b2dyYW0gcGxvdApoaXN0KGZpdEQxMEQwM19jbG9zZSRmaXREMTBEMDNBYnNEaWZmLCBtYWluID0gIkhpc3RvZ3JhbSBvZiBDbG9zZSBEYXRhIikKYGBgCgoqKkZhci1SZWxhdGVkIFBhaXJzOioqIFN1YnNldCB0byBpbmNsdWRlIG9ubHkgcGFpcnMgb2YgdGF4YSB3aXRoIGFuIGFic29sdXRlIHBoeWxvZ2VuZXRpYyBkaXN0YW5jZSBncmVhdGVyIHRoYW4gMS4gR3JhcGhpY2FsIHN1bW1hcmllcyBpbmNsdWRlIGEgUS1RIChRdWFudGlsZS1RdWFudGlsZSkgcGxvdCBhbmQgaGlzdHJvZ3JhbSBvZiB0aGUgYWJzb2x1dGUgZGlmZmVyZW5jZSB2YWx1ZXMuIFRoZSBRLVEgcGxvdCBpcyBncmFwaGljYWwgbWV0aG9kIHRvIGFzc2VzcyBpZiBhIGRhdGFzZXQgZm9sbG93cyBhIG5vcm1hbCBkaXN0cmlidXRpb24uIFRoZSByZWQgbGluZSBhZGRlZCBieSBxcWxpbmUoKSByZXByZXNlbnRzIHRoZSBleHBlY3RlZCBsaW5lIGlmIHRoZSBkYXRhIHdlcmUgcGVyZmVjdGx5IG5vcm1hbGx5IGRpc3RyaWJ1dGVkLiBUaGUgaGlzdG9ncmFtIHByb3ZpZGVzIGEgdmlzdWFsIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGRhdGEuCmBgYHtyfQojIFRoaXMgZm9jdXNlcyB0aGUgYW5hbHlzaXMgb24gZGlzdGFudGx5IHJlbGF0ZWQgdGF4YQpmaXREMTBEMDNfZmFyIDwtIGZpdEQxMEQwM19yZXN1bHRzICU+JSAKICBmaWx0ZXIoQWJzRGlzdGFuY2UgPiAxKQoKIyBHcmFwaGljYWwgU3VtbWFyaWVzCgojIENyZWF0ZSBhIFEtUSAoUXVhbnRpbGUtUXVhbnRpbGUpIHBsb3QKcXFub3JtKGZpdEQxMEQwM19mYXIkZml0RDEwRDAzQWJzRGlmZikKcXFsaW5lKGZpdEQxMEQwM19mYXIkZml0RDEwRDAzQWJzRGlmZiwgY29sID0gInJlZCIpCgojIENyZWF0ZSBhIGhpc3RvZ3JhbSBwbG90Cmhpc3QoZml0RDEwRDAzX2ZhciRmaXREMTBEMDNBYnNEaWZmLCBtYWluID0gIkhpc3RvZ3JhbSBvZiBGYXIgRGF0YSIpCmBgYAoKKipXaWxjb3ggVGVzdDoqKiBQZXJmb3JtIFdpbGNveG9uIFJhbmsgU3VtIFRlc3QgKGFsc28ga25vd24gYXMgTWFubi1XaGl0bmV5IFUgdGVzdCkgY29tcGFyaW5nIHRoZSBhYnNvbHV0ZSBkaXN0YW5jZSB2YWx1ZXMgYmV0d2VlbiB0aGUgY2xvc2UtcmVsYXRlZCBhbmQgZmFyLXJlbGF0ZWQgZ3JvdXBzLgpgYGB7cn0KIyBXaWxjb3ggdGVzdCBiZXR3ZWVuIGNsb3NlIGFuZCBmYXIgcmVsYXRlZCBkaXN0YW5jZXMKd2lsY294X3Jlc3VsdF9maXREMTBEMDMgPC0gd2lsY294LnRlc3QoZml0RDEwRDAzX2Nsb3NlJGZpdEQxMEQwM0Fic0RpZmYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpdEQxMEQwM19mYXIkZml0RDEwRDAzQWJzRGlmZikKCnByaW50KHdpbGNveF9yZXN1bHRfZml0RDEwRDAzKQpgYGAKCioqQ29tYmluZSBEYXRhOioqIEV4YW1pbmUgdGhlIGRpc3RyaWJ1dGlvbnMgb2YgdGhlc2UgZGlmZmVyZW5jZXMgdG8gdW5kZXJzdGFuZCBob3cgdGhleSBkaWZmZXIgYW5kIHdoZXRoZXIgdGhlc2UgZGlmZmVyZW5jZXMgYXJlIGdlbmVyYWxseSBsYXJnZXIgb3Igc21hbGxlciBmb3IgY2xvc2VseSByZWxhdGVkIHRheGEuCmBgYHtyfQojIENvbWJpbmUgdGhlIGRhdGEgYW5kIGFkZCBhIGdyb3VwIGxhYmVsCmZpdEQxMEQwM19jb21iaW5lZF9kYXRhIDwtIGJpbmRfcm93cygKICBtdXRhdGUoZml0RDEwRDAzX2Nsb3NlLCBncm91cCA9ICJDbG9zZSIpLAogIG11dGF0ZShmaXREMTBEMDNfZmFyLCBncm91cCA9ICJGYXIiKSkKCiMgUmVtb3ZlIE5BIHZhbHVlcwpmaXREMTBEMDNfY29tYmluZWRfZGF0YSA8LSBmaXREMTBEMDNfY29tYmluZWRfZGF0YSAlPiUgCiAgZmlsdGVyKCFpcy5uYShmaXREMTBEMDNBYnNEaWZmKSkKCiMgQ3JlYXRlIGEgYm94IHBsb3QKZml0RDEwRDAzX2JveHBsb3QgPC0gZ2dwbG90KGZpdEQxMEQwM19jb21iaW5lZF9kYXRhLCBhZXMoeCA9IGdyb3VwLCB5ID0gZml0RDEwRDAzQWJzRGlmZikpICsKICBnZW9tX2JveHBsb3QoKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjEsIHdpZHRoID0gMC4yKSArICAjIEFkZCBpbmRpdmlkdWFsIHBvaW50cyB3aXRoIHNvbWUgdHJhbnNwYXJlbmN5CiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgZml0RDEwRDAzIERpZmZlcmVuY2VzIiwKICAgICAgIHggPSAiUGh5bG9nZW5ldGljIFJlbGF0aW9uc2hpcCIsCiAgICAgICB5ID0gIkFic29sdXRlIERpZmZlcmVuY2UgaW4gZml0RDEwRDAzIikgKwogIHRoZW1lX21pbmltYWwoKQoKIyBEaXNwbGF5IHRoZSBwbG90CnByaW50KGZpdEQxMEQwM19ib3hwbG90KQoKIyBDYWxjdWxhdGUgc3VtbWFyeSBzdGF0aXN0aWNzCmZpdEQxMEQwM19zdW1tYXJ5X3N0YXRzIDwtIGZpdEQxMEQwM19jb21iaW5lZF9kYXRhICU+JQogIGdyb3VwX2J5KGdyb3VwKSAlPiUKICBzdW1tYXJpc2UoCiAgICBuID0gbigpLAogICAgbWVhbiA9IG1lYW4oZml0RDEwRDAzQWJzRGlmZiksCiAgICBtZWRpYW4gPSBtZWRpYW4oZml0RDEwRDAzQWJzRGlmZiksCiAgICBzZCA9IHNkKGZpdEQxMEQwM0Fic0RpZmYpLAogICAgbWluID0gbWluKGZpdEQxMEQwM0Fic0RpZmYpLAogICAgbWF4ID0gbWF4KGZpdEQxMEQwM0Fic0RpZmYpCiAgKQoKIyBQcmludCBzdW1tYXJ5IHN0YXRpc3RpY3MKcHJpbnQoZml0RDEwRDAzX3N1bW1hcnlfc3RhdHMpCgojIFBlcmZvcm0gYSBmb3JtYWwgdGVzdCBmb3IgZGlmZmVyZW5jZSBpbiBtZWFucwpmaXREMTBEMDNfdF90ZXN0X3Jlc3VsdCA8LSB0LnRlc3QoZml0RDEwRDAzQWJzRGlmZiB+IGdyb3VwLCBkYXRhID0gZml0RDEwRDAzX2NvbWJpbmVkX2RhdGEpCnByaW50KGZpdEQxMEQwM190X3Rlc3RfcmVzdWx0KQpgYGAKCiMjIyMgNDAweCBNSUMgVHJlZSAoNDE3KQoKTGliMTU6IDQwMHggTUlDICgyMDAgdWcvbUwgVE1QKQpgYGB7cn0KdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8yMDB0bXBfZml0bmVzc19sYWJlbGxlZCA8LSBnZ3RyZWUoRml0dHJlZTE1Z29vZCwgbGF5b3V0PSJjaXJjdWxhciIsIGJyYW5jaC5sZW5ndGg9Im5vbmUiKSAlPCslCiAgRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCArCiAgYWVzKGNvbG9yID0gaWZlbHNlKGZpdEQxMUQwMyA8IC0xLCAiTG93IGZpdG5lc3MiLCAiSGlnaCBmaXRuZXNzIikpICsKICBnZW9tX3RpcHBvaW50KGFlcyhjb2xvciA9IGlmZWxzZShmaXREMTFEMDMgPCAtMSwgIkxvdyBmaXRuZXNzIiwgIkhpZ2ggZml0bmVzcyIpKSwgc2l6ZT0xLCBhbHBoYT0wLjgpICsKICAjZ2VvbV90aXBsYWIyKGFlcyhsYWJlbD1OQ0JJLnBoeWx1bSwgYW5nbGU9YW5nbGUpLCBzaXplPTEpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIAogIHNjYWxlX2NvbG9yX21hbnVhbCgKICAgICJGaXRuZXNzIiwKICAgIHZhbHVlcyA9IGMoIkxvdyBmaXRuZXNzIiA9ICJkYXJrYmx1ZSIsICJIaWdoIGZpdG5lc3MiID0gImdvbGQiKSwKICAgIG5hLnZhbHVlID0gImdyYXkiCiAgKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIkZpdG5lc3MiKSkKCnRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMjAwdG1wX2ZpdG5lc3NfbGFiZWxsZWQKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIDQwMHggTUlDIEZpdG5lc3MgVHJlZSAodGlwcyBsYWJlbGVkKQpnZ3NhdmUoZmlsZT0iUGVyZmVjdHMvUExPVFMvVHJlZXMvVE1QL0xpYjE1LkZhc3RUcmVlLjVCQ3MucGVyZmVjdHMuZ29vZC5maXREMTFEMDMuNDAweE1JQy50aXBzLmxhYmVsbGVkLnBoeWx1bS5wbmciLAogICAgICAgcGxvdD10cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzIwMHRtcF9maXRuZXNzX2xhYmVsbGVkLAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNy41LCB1bml0cyA9ICJpbiIpCmBgYAoKQ291bnQgdGhlIG51bWJlciBvZiBjb21wbGVtZW50aW5nIGhvbW9sb2dzIChmaXQgPiAtMSkgYW5kIGRyb3BvdXQgaG9tb2xvZ3MgKGZpdCA8IC0xKToKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgQ291bnQgdW5pcXVlIElEcyB3aXRoIGZpdEQxMUQwMyA+IC0xCmhpZ2hfZml0bmVzc19jb3VudCA8LSBGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkICU+JQogIGZpbHRlcihmaXREMTFEMDMgPiAtMSkgJT4lCiAgZGlzdGluY3QoSUQpICU+JQogIG5yb3coKQoKIyBDb3VudCB1bmlxdWUgSURzIHdpdGggZml0RDExRDAzIDwgLTEKbG93X2ZpdG5lc3NfY291bnQgPC0gRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCAlPiUKICBmaWx0ZXIoZml0RDExRDAzIDwgLTEpICU+JQogIGRpc3RpbmN0KElEKSAlPiUKICBucm93KCkKCiMgUHJpbnQgdGhlIHJlc3VsdHMKY2F0KCJOdW1iZXIgb2YgdW5pcXVlIElEcyB3aXRoIGZpdEQxMUQwMyA+IC0xOiIsIGhpZ2hfZml0bmVzc19jb3VudCwgIlxuIikKY2F0KCJOdW1iZXIgb2YgdW5pcXVlIElEcyB3aXRoIGZpdEQxMUQwMyA8IC0xOiIsIGxvd19maXRuZXNzX2NvdW50LCAiXG4iKQpgYGAKCkJ1aWxkIHRoZSBDb21wbGVtZW50YXRpb24tY2FwYWJsZSBESEZSIHRyZWUgd2l0aCBjb2xvci1zY2FsZSBiYXNlZCBvbiBmaXRuZXNzIGF0IDQwMHggTUlDIGFuZCBhZGQgYW4gYWRkaXRpb25hbCByaW5nIHJlcHJlc2VudGluZyB0YXhvbm9taWMgbGluZWFnZXMgYXQgdGhlICoqUGh5bHVtLWxldmVsKiouCmBgYHtyfQp0cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzIwMHRtcF9maXRuZXNzX3BoeWx1bV9wMSA8LSBnZ3RyZWUoRml0dHJlZTE1Z29vZCwgbGF5b3V0PSJjaXJjdWxhciIsIGJyYW5jaC5sZW5ndGg9Im5vbmUiKSAlPCslCiAgRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCArCiAgYWVzKGNvbG9yID0gaWZlbHNlKGZpdEQxMUQwMyA8IC0xLCAiTG93IGZpdG5lc3MiLCAiSGlnaCBmaXRuZXNzIikpICsKICBnZW9tX3RpcHBvaW50KGFlcyhjb2xvciA9IGlmZWxzZShmaXREMTFEMDMgPCAtMSwgIkxvdyBmaXRuZXNzIiwgIkhpZ2ggZml0bmVzcyIpKSwgc2l6ZT0xLCBhbHBoYT0wLjgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249InJpZ2h0IikgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwoCiAgICAiRml0bmVzcyIsCiAgICB2YWx1ZXMgPSBjKCJMb3cgZml0bmVzcyIgPSAiZGFya2JsdWUiLCAiSGlnaCBmaXRuZXNzIiA9ICJnb2xkIiksCiAgICBuYS52YWx1ZSA9ICJncmF5IikKCnRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMjAwdG1wX2ZpdG5lc3NfcGh5bHVtX3AyIDwtIHRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMjAwdG1wX2ZpdG5lc3NfcGh5bHVtX3AxICsKICBuZXdfc2NhbGVfZmlsbCgpICsKICBnZW9tX2ZydWl0KAogICAgZ2VvbT1nZW9tX3RpbGUsCiAgICBtYXBwaW5nPWFlcyhmaWxsPU5DQkkucGh5bHVtKSwgICMgQ2hhbmdlZCB0aGlzIGxpbmUKICAgIHdpZHRoPTMsCiAgICBvZmZzZXQ9MC4xCiAgKSArCiAgc2NhbGVfZmlsbF9tYW51YWwoCiAgICBuYW1lPSJQaHlsdW0iLAogICAgdmFsdWVzPXNldE5hbWVzKEZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQkTkNCSS5waHlsdW1fY29sb3JzLCBGaXR0cmVlMTVnb29kX3RheGFfbWVyZ2VkJE5DQkkucGh5bHVtKSwKICAgIGd1aWRlPWd1aWRlX2xlZ2VuZChrZXl3aWR0aD0wLjMsIGtleWhlaWdodD0wLjMsIG5jb2w9MSkKICApICsKICB0aGVtZSgKICAgIGxlZ2VuZC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksIAogICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICBsZWdlbmQuc3BhY2luZy55ID0gdW5pdCgwLjIsICJjbSIpCiAgKQoKdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8yMDB0bXBfZml0bmVzc19waHlsdW1fcDIKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iUGVyZmVjdHMvUExPVFMvVHJlZXMvVE1QL0xpYjE1LkZhc3RUcmVlLjVCQ3MucGVyZmVjdHMuZ29vZC5maXREMTFEMDMuNDAweE1JQy5QaHlsdW0uUklORy5wbmciLCBwbG90PXRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMjAwdG1wX2ZpdG5lc3NfcGh5bHVtX3AyLAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNy41LCB1bml0cyA9ICJpbiIpCmBgYAoKIyMjIyMgU3VtbWFyeSBTdGF0cwoKVGhlIGZvbGxvd2luZyBzdGF0aXN0aWNhbCB0ZXN0cyB3aWxsIGRldGVybWluZSBpZiBmaXRuZXNzIGNsdXN0ZXJzIGJ5IGV2b2x1dGlvbmFyeSBkaXN0YW5jZSB3aXRoaW4gdGhlIHBoeWxvZ2VuZXRpYyB0cmVlCmBgYHtyfQojIENyZWF0ZSBhIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSBkaXN0YW5jZXMgYW5kIGRpZmZlcmVuY2VzIGJldHdlZW4gCmZpdEQxMUQwM19kaXN0X2RpZmYgPC0gZnVuY3Rpb24ocGFpcndpc2VfZGlzdGFuY2VzLCB0YXhhX21lcmdlZCkgewogIGRpc3RhbmNlX2RmIDwtIGFzLmRhdGEuZnJhbWUoYXMudGFibGUocGFpcndpc2VfZGlzdGFuY2VzKSkgJT4lCiAgICByZW5hbWUoVGlwMSA9IFZhcjEsIFRpcDIgPSBWYXIyLCBEaXN0YW5jZSA9IEZyZXEpICU+JQogICAgbXV0YXRlKFRpcDEgPSBhcy5jaGFyYWN0ZXIoVGlwMSksCiAgICAgICAgICAgVGlwMiA9IGFzLmNoYXJhY3RlcihUaXAyKSkgJT4lCiAgICBmaWx0ZXIoVGlwMSAhPSBUaXAyKSAgIyBSZW1vdmUgc2VsZi1jb21wYXJpc29ucwogIAogIHJlc3VsdHMgPC0gZGlzdGFuY2VfZGYgJT4lCiAgICBsZWZ0X2pvaW4odGF4YV9tZXJnZWQsIGJ5ID0gYygiVGlwMSIgPSAiSUQiKSkgJT4lCiAgICByZW5hbWUoZml0RDExRDAzdjEgPSBmaXREMTFEMDMpICU+JQogICAgbGVmdF9qb2luKHRheGFfbWVyZ2VkLCBieSA9IGMoIlRpcDIiID0gIklEIikpICU+JQogICAgcmVuYW1lKGZpdEQxMUQwM3YyID0gZml0RDExRDAzKSAlPiUKICAgIG11dGF0ZSgKICAgICAgQWJzRGlzdGFuY2UgPSBhYnMoRGlzdGFuY2UpLAogICAgICBmaXREMTFEMDNBYnNEaWZmID0gYWJzKGZpdEQxMUQwM3YxIC0gZml0RDExRDAzdjIpCiAgICApICU+JQogICAgc2VsZWN0KFRpcDEsIFRpcDIsIEFic0Rpc3RhbmNlLCBmaXREMTFEMDNBYnNEaWZmKQogIAogIHJldHVybihyZXN1bHRzKX0KYGBgCgoqKlBlYXJzb24gQ29ycmVsYXRpb246KiogQ2FsY3VsYXRlIHBhaXJ3aXNlIGRpc3RhbmNlcyBiZXR3ZWVuIHRpcHMgYW5kIGRpZmZlcmVuY2VzIGluIGZpdG5lc3MgdmFsdWVzLiBQbG90IGRpc3RhbmNlcyBhbmQgZGlmZmVyZW5jZXMgYXMgc2NhdHRlciBwbG90IGFuZCB0ZXN0IGZvciBzaWduaWZpY2FudCBjb3JyZWxhdGlvbiB1c2luZyBQZWFyc29uLgpgYGB7cn0KIyBDYWxjdWxhdGUgcGFpcndpc2UgZGlzdGFuY2VzCmZpdEQxMUQwM19wYWlyd2lzZV9kaXN0YW5jZXMgPC0gY29waGVuZXRpYy5waHlsbyhGaXR0cmVlMTVnb29kKQoKIyBDYWxjdWxhdGUgZGlzdGFuY2VzIGFuZCBkaWZmZXJlbmNlcwpmaXREMTFEMDNfcmVzdWx0cyA8LSBmaXREMTFEMDNfZGlzdF9kaWZmKGZpdEQxMUQwM19wYWlyd2lzZV9kaXN0YW5jZXMsRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCkKCiMgQ3JlYXRlIHRoZSBzY2F0dGVyIHBsb3QKZml0RDExRDAzX3NjYXR0ZXJfcGxvdCA8LSBnZ3Bsb3QoZml0RDExRDAzX3Jlc3VsdHMsIGFlcyh4ID0gQWJzRGlzdGFuY2UsIHkgPSBmaXREMTFEMDNBYnNEaWZmKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpICsgICMgQWRkIHRyYW5zcGFyZW5jeSB0byBwb2ludHMKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBjb2xvciA9ICJyZWQiLCBzZSA9IEZBTFNFKSArICAjIEFkZCBhIGxpbmVhciB0cmVuZCBsaW5lCiAgc3RhdF9jb3IobWV0aG9kID0gInBlYXJzb24iLCBwLmFjY3VyYWN5ID0gMC4wMDAwMDEsIHIuYWNjdXJhY3kgPSAwLjAxKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHRpdGxlID0gIkFic29sdXRlIERpc3RhbmNlIHZzIEFic29sdXRlIERpZmZlcmVuY2UgaW4gZml0RDExRDAzIiwKICAgICAgIHggPSAiQWJzb2x1dGUgVmFsdWUgb2YgRGlzdGFuY2UiLAogICAgICAgeSA9ICJBYnNvbHV0ZSBWYWx1ZSBvZiBEaWZmZXJlbmNlIGluIGZpdEQxMUQwMyIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgojIERpc3BsYXkgdGhlIHBsb3QKcHJpbnQoZml0RDExRDAzX3NjYXR0ZXJfcGxvdCkKCiMgT3B0aW9uYWw6IFNhdmUgdGhlIHBsb3QKZ2dzYXZlKCJQZXJmZWN0cy9QTE9UUy9XaWxjb3gvZml0RDExRDAzX3BlYXJzb25fc2NhdHRlcl9wbG90LnBuZyIsIAogICAgICAgZml0RDExRDAzX3NjYXR0ZXJfcGxvdCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOCwgZHBpID0gMzAwKQpgYGAKCioqQ2xvc2UtUmVsYXRlZCBQYWlyczoqKiBTdWJzZXQgdG8gaW5jbHVkZSBvbmx5IHBhaXJzIG9mIHRheGEgd2l0aCBhbiBhYnNvbHV0ZSBwaHlsb2dlbmV0aWMgZGlzdGFuY2UgbGVzcyB0aGFuIDEuIEdyYXBoaWNhbCBzdW1tYXJpZXMgaW5jbHVkZSBhIFEtUSAoUXVhbnRpbGUtUXVhbnRpbGUpIHBsb3QgYW5kIGhpc3Ryb2dyYW0gb2YgdGhlIGFic29sdXRlIGRpZmZlcmVuY2UgdmFsdWVzLiBUaGUgUS1RIHBsb3QgaXMgZ3JhcGhpY2FsIG1ldGhvZCB0byBhc3Nlc3MgaWYgYSBkYXRhc2V0IGZvbGxvd3MgYSBub3JtYWwgZGlzdHJpYnV0aW9uLiBUaGUgcmVkIGxpbmUgYWRkZWQgYnkgcXFsaW5lKCkgcmVwcmVzZW50cyB0aGUgZXhwZWN0ZWQgbGluZSBpZiB0aGUgZGF0YSB3ZXJlIHBlcmZlY3RseSBub3JtYWxseSBkaXN0cmlidXRlZC4gVGhlIGhpc3RvZ3JhbSBwcm92aWRlcyBhIHZpc3VhbCByZXByZXNlbnRhdGlvbiBvZiB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBkYXRhLgpgYGB7cn0KIyBUaGlzIGZvY3VzZXMgdGhlIGFuYWx5c2lzIG9uIGNsb3NlbHkgcmVsYXRlZCB0YXhhCmZpdEQxMUQwM19jbG9zZSA8LSBmaXREMTFEMDNfcmVzdWx0cyAlPiUgCiAgZmlsdGVyKEFic0Rpc3RhbmNlIDwgMSkKCiMgR3JhcGhpY2FsIFN1bW1hcmllcwoKIyBDcmVhdGUgYSBRLVEgKFF1YW50aWxlLVF1YW50aWxlKSBwbG90CnFxbm9ybShmaXREMTFEMDNfY2xvc2UkZml0RDExRDAzQWJzRGlmZikKcXFsaW5lKGZpdEQxMUQwM19jbG9zZSRmaXREMTFEMDNBYnNEaWZmLCBjb2wgPSAicmVkIikKCiMgQ3JlYXRlIGEgaGlzdG9ncmFtIHBsb3QKaGlzdChmaXREMTFEMDNfY2xvc2UkZml0RDExRDAzQWJzRGlmZiwgbWFpbiA9ICJIaXN0b2dyYW0gb2YgQ2xvc2UgRGF0YSIpCmBgYAoKKipGYXItUmVsYXRlZCBQYWlyczoqKiBTdWJzZXQgdG8gaW5jbHVkZSBvbmx5IHBhaXJzIG9mIHRheGEgd2l0aCBhbiBhYnNvbHV0ZSBwaHlsb2dlbmV0aWMgZGlzdGFuY2UgZ3JlYXRlciB0aGFuIDEuIEdyYXBoaWNhbCBzdW1tYXJpZXMgaW5jbHVkZSBhIFEtUSAoUXVhbnRpbGUtUXVhbnRpbGUpIHBsb3QgYW5kIGhpc3Ryb2dyYW0gb2YgdGhlIGFic29sdXRlIGRpZmZlcmVuY2UgdmFsdWVzLiBUaGUgUS1RIHBsb3QgaXMgZ3JhcGhpY2FsIG1ldGhvZCB0byBhc3Nlc3MgaWYgYSBkYXRhc2V0IGZvbGxvd3MgYSBub3JtYWwgZGlzdHJpYnV0aW9uLiBUaGUgcmVkIGxpbmUgYWRkZWQgYnkgcXFsaW5lKCkgcmVwcmVzZW50cyB0aGUgZXhwZWN0ZWQgbGluZSBpZiB0aGUgZGF0YSB3ZXJlIHBlcmZlY3RseSBub3JtYWxseSBkaXN0cmlidXRlZC4gVGhlIGhpc3RvZ3JhbSBwcm92aWRlcyBhIHZpc3VhbCByZXByZXNlbnRhdGlvbiBvZiB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBkYXRhLgpgYGB7cn0KIyBUaGlzIGZvY3VzZXMgdGhlIGFuYWx5c2lzIG9uIGRpc3RhbnRseSByZWxhdGVkIHRheGEKZml0RDExRDAzX2ZhciA8LSBmaXREMTFEMDNfcmVzdWx0cyAlPiUgCiAgZmlsdGVyKEFic0Rpc3RhbmNlID4gMSkKCiMgR3JhcGhpY2FsIFN1bW1hcmllcwoKIyBDcmVhdGUgYSBRLVEgKFF1YW50aWxlLVF1YW50aWxlKSBwbG90CnFxbm9ybShmaXREMTFEMDNfZmFyJGZpdEQxMUQwM0Fic0RpZmYpCnFxbGluZShmaXREMTFEMDNfZmFyJGZpdEQxMUQwM0Fic0RpZmYsIGNvbCA9ICJyZWQiKQoKIyBDcmVhdGUgYSBoaXN0b2dyYW0gcGxvdApoaXN0KGZpdEQxMUQwM19mYXIkZml0RDExRDAzQWJzRGlmZiwgbWFpbiA9ICJIaXN0b2dyYW0gb2YgRmFyIERhdGEiKQpgYGAKCioqV2lsY294IFRlc3Q6KiogUGVyZm9ybSBXaWxjb3hvbiBSYW5rIFN1bSBUZXN0IChhbHNvIGtub3duIGFzIE1hbm4tV2hpdG5leSBVIHRlc3QpIGNvbXBhcmluZyB0aGUgYWJzb2x1dGUgZGlzdGFuY2UgdmFsdWVzIGJldHdlZW4gdGhlIGNsb3NlLXJlbGF0ZWQgYW5kIGZhci1yZWxhdGVkIGdyb3Vwcy4KYGBge3J9CiMgV2lsY294IHRlc3QgYmV0d2VlbiBjbG9zZSBhbmQgZmFyIHJlbGF0ZWQgZGlzdGFuY2VzCndpbGNveF9yZXN1bHRfZml0RDExRDAzIDwtIHdpbGNveC50ZXN0KGZpdEQxMUQwM19jbG9zZSRmaXREMTFEMDNBYnNEaWZmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaXREMTFEMDNfZmFyJGZpdEQxMUQwM0Fic0RpZmYpCgpwcmludCh3aWxjb3hfcmVzdWx0X2ZpdEQxMUQwMykKYGBgCgoqKkludGVycHJldGF0aW9uOioqCgotICoqU2lnbmlmaWNhbnQgRGlmZmVyZW5jZToqKiBUaGUgZXh0cmVtZWx5IGxvdyBwLXZhbHVlICg8IDIuMmUtMTYpIGluZGljYXRlcyBhIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBmaXREMTFEMDNBYnNEaWZmIHZhbHVlcyBvZiBjbG9zZWx5IHJlbGF0ZWQgdGF4YSAoZml0bmVzc0QwN0QwM19jbG9zZSkgYW5kIG1vcmUgZGlzdGFudGx5IHJlbGF0ZWQgdGF4YSAoZml0RDExRDAzX2ZhcikuCgotICoqRGlzdHJpYnV0aW9uIENvbXBhcmlzb246KiogVGhlIHRlc3Qgc3VnZ2VzdHMgdGhhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGZpdEQxMUQwM0Fic0RpZmYgdmFsdWVzIGlzIHNpZ25pZmljYW50bHkgZGlmZmVyZW50IGJldHdlZW4gY2xvc2UgYW5kIGZhciBwaHlsb2dlbmV0aWMgcmVsYXRpb25zaGlwcy4KCi0gKipNYWduaXR1ZGUgYW5kIERpcmVjdGlvbjoqKiBXaGlsZSB0aGUgdGVzdCB0ZWxscyB1cyB0aGVyZSdzIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSwgaXQgZG9lc24ndCB0ZWxsIHVzIGFib3V0IHRoZSBtYWduaXR1ZGUgb3IgZGlyZWN0aW9uIG9mIHRoaXMgZGlmZmVyZW5jZS4gWW91J2QgbmVlZCB0byBsb29rIGF0IGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MgKGxpa2UgbWVkaWFucykgb2YgYm90aCBncm91cHMgdG8gdW5kZXJzdGFuZCB0aGlzLgoKLSAqKkJpb2xvZ2ljYWwgU2lnbmlmaWNhbmNlOioqIFRoaXMgcmVzdWx0IHN1Z2dlc3RzIHRoYXQgdGhlIGFic29sdXRlIGRpZmZlcmVuY2VzIGluIGZpdEQxMUQwMyB2YWx1ZXMgYXJlIG5vdCB0aGUgc2FtZSBmb3IgY2xvc2VseSByZWxhdGVkIHRheGEgY29tcGFyZWQgdG8gbW9yZSBkaXN0YW50bHkgcmVsYXRlZCB0YXhhLiBUaGlzIGNvdWxkIGltcGx5IHRoYXQgZXZvbHV0aW9uYXJ5IGRpc3RhbmNlIGRvZXMgcGxheSBhIHJvbGUgaW4gdGhlIHNpbWlsYXJpdHkgKG9yIGRpZmZlcmVuY2UpIG9mIGZpdEQxMUQwMyB2YWx1ZXMsIGRlc3BpdGUgdGhlIHdlYWsgY29ycmVsYXRpb24geW91IGZvdW5kIGVhcmxpZXIuCgotICoqRnVydGhlciBJbnZlc3RpZ2F0aW9uOioqIEl0IHdvdWxkIGJlIHdvcnRoIGV4YW1pbmluZyB0aGUgYWN0dWFsIGRpc3RyaWJ1dGlvbnMgb2YgdGhlc2UgZGlmZmVyZW5jZXMgKGUuZy4sIHdpdGggYm94IHBsb3RzKSB0byB1bmRlcnN0YW5kIGhvdyB0aGV5IGRpZmZlci4gQXJlIHRoZSBkaWZmZXJlbmNlcyBnZW5lcmFsbHkgbGFyZ2VyIG9yIHNtYWxsZXIgZm9yIGNsb3NlbHkgcmVsYXRlZCB0YXhhPwoKLSAqKkNhdXRpb246KiogUmVtZW1iZXIgdGhhdCB3aXRoIHZlcnkgbGFyZ2Ugc2FtcGxlIHNpemVzLCBldmVuIHNtYWxsIGRpZmZlcmVuY2VzIGNhbiBiZWNvbWUgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudC4gSXQncyBpbXBvcnRhbnQgdG8gY29uc2lkZXIgdGhlIHByYWN0aWNhbCBvciBiaW9sb2dpY2FsIHNpZ25pZmljYW5jZSBvZiB0aGVzZSBkaWZmZXJlbmNlcywgbm90IGp1c3QgdGhlIHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZS4KClRoaXMgcmVzdWx0IGFkZHMgbnVhbmNlIHRvIHlvdXIgZWFybGllciBmaW5kaW5ncywgc3VnZ2VzdGluZyB0aGF0IHdoaWxlIHRoZXJlIG1pZ2h0IG5vdCBiZSBhIHN0cm9uZyBsaW5lYXIgY29ycmVsYXRpb24gYmV0d2VlbiBwaHlsb2dlbmV0aWMgZGlzdGFuY2UgYW5kIGZpdEQxMUQwMyBkaWZmZXJlbmNlcywgdGhlcmUgaXMgYSBzaWduaWZpY2FudCBkaWZmZXJlbmNlIGluIGhvdyB0aGVzZSB2YWx1ZXMgdmFyeSBhbW9uZyBjbG9zZSByZWxhdGl2ZXMgdmVyc3VzIG1vcmUgZGlzdGFudCByZWxhdGl2ZXMuCgoqKkNvbWJpbmUgRGF0YToqKiBFeGFtaW5lIHRoZSBkaXN0cmlidXRpb25zIG9mIHRoZXNlIGRpZmZlcmVuY2VzIHRvIHVuZGVyc3RhbmQgaG93IHRoZXkgZGlmZmVyIGFuZCB3aGV0aGVyIHRoZXNlIGRpZmZlcmVuY2VzIGFyZSBnZW5lcmFsbHkgbGFyZ2VyIG9yIHNtYWxsZXIgZm9yIGNsb3NlbHkgcmVsYXRlZCB0YXhhLgpgYGB7cn0KIyBDb21iaW5lIHRoZSBkYXRhIGFuZCBhZGQgYSBncm91cCBsYWJlbApmaXREMTFEMDNfY29tYmluZWRfZGF0YSA8LSBiaW5kX3Jvd3MoCiAgbXV0YXRlKGZpdEQxMUQwM19jbG9zZSwgZ3JvdXAgPSAiQ2xvc2UiKSwKICBtdXRhdGUoZml0RDExRDAzX2ZhciwgZ3JvdXAgPSAiRmFyIikpCgojIFJlbW92ZSBOQSB2YWx1ZXMKZml0RDExRDAzX2NvbWJpbmVkX2RhdGEgPC0gZml0RDExRDAzX2NvbWJpbmVkX2RhdGEgJT4lIAogIGZpbHRlcighaXMubmEoZml0RDExRDAzQWJzRGlmZikpCgojIENyZWF0ZSBhIGJveCBwbG90CmZpdEQxMUQwM19ib3hwbG90IDwtIGdncGxvdChmaXREMTFEMDNfY29tYmluZWRfZGF0YSwgYWVzKHggPSBncm91cCwgeSA9IGZpdEQxMUQwM0Fic0RpZmYpKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIGdlb21faml0dGVyKGFscGhhID0gMC4xLCB3aWR0aCA9IDAuMikgKyAgIyBBZGQgaW5kaXZpZHVhbCBwb2ludHMgd2l0aCBzb21lIHRyYW5zcGFyZW5jeQogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIGZpdEQxMUQwMyBEaWZmZXJlbmNlcyIsCiAgICAgICB4ID0gIlBoeWxvZ2VuZXRpYyBSZWxhdGlvbnNoaXAiLAogICAgICAgeSA9ICJBYnNvbHV0ZSBEaWZmZXJlbmNlIGluIGZpdEQxMUQwMyIpICsKICB0aGVtZV9taW5pbWFsKCkKCiMgRGlzcGxheSB0aGUgcGxvdApwcmludChmaXREMTFEMDNfYm94cGxvdCkKCiMgQ2FsY3VsYXRlIHN1bW1hcnkgc3RhdGlzdGljcwpmaXREMTFEMDNfc3VtbWFyeV9zdGF0cyA8LSBmaXREMTFEMDNfY29tYmluZWRfZGF0YSAlPiUKICBncm91cF9ieShncm91cCkgJT4lCiAgc3VtbWFyaXNlKAogICAgbiA9IG4oKSwKICAgIG1lYW4gPSBtZWFuKGZpdEQxMUQwM0Fic0RpZmYpLAogICAgbWVkaWFuID0gbWVkaWFuKGZpdEQxMUQwM0Fic0RpZmYpLAogICAgc2QgPSBzZChmaXREMTFEMDNBYnNEaWZmKSwKICAgIG1pbiA9IG1pbihmaXREMTFEMDNBYnNEaWZmKSwKICAgIG1heCA9IG1heChmaXREMTFEMDNBYnNEaWZmKQogICkKCiMgUHJpbnQgc3VtbWFyeSBzdGF0aXN0aWNzCnByaW50KGZpdEQxMUQwM19zdW1tYXJ5X3N0YXRzKQoKIyBQZXJmb3JtIGEgZm9ybWFsIHRlc3QgZm9yIGRpZmZlcmVuY2UgaW4gbWVhbnMKZml0RDExRDAzX3RfdGVzdF9yZXN1bHQgPC0gdC50ZXN0KGZpdEQxMUQwM0Fic0RpZmYgfiBncm91cCwgZGF0YSA9IGZpdEQxMUQwM19jb21iaW5lZF9kYXRhKQpwcmludChmaXREMTFEMDNfdF90ZXN0X3Jlc3VsdCkKYGBgCgoqKkludGVycHJldGF0aW9uOioqCgotICoqU2lnbmlmaWNhbnQgRGlmZmVyZW5jZToqKiBUaGVyZSBpcyBhIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBpbiB0aGUgZml0RDExRDAzQWJzRGlmZiB2YWx1ZXMgYmV0d2VlbiBjbG9zZWx5IHJlbGF0ZWQgdGF4YSBhbmQgbW9yZSBkaXN0YW50bHkgcmVsYXRlZCB0YXhhLgogIAotICoqTWFnbml0dWRlIG9mIERpZmZlcmVuY2U6KiogT24gYXZlcmFnZSwgdGhlIGFic29sdXRlIGRpZmZlcmVuY2UgaW4gZml0RDExRDAzIHZhbHVlcyBpcyBhYm91dCAxLjIyNiB1bml0cyBzbWFsbGVyIGZvciBjbG9zZWx5IHJlbGF0ZWQgdGF4YSBjb21wYXJlZCB0byBkaXN0YW50bHkgcmVsYXRlZCB0YXhhLgogIAotICoqQ29uc2lzdGVuY3kgb2YgRGlmZmVyZW5jZToqKiBUaGUgbmFycm93IGNvbmZpZGVuY2UgaW50ZXJ2YWwgKC0xLjI2Mzg2NSB0byAtMS4xODgxNzQpIHN1Z2dlc3RzIHRoYXQgdGhpcyBkaWZmZXJlbmNlIGlzIHF1aXRlIGNvbnNpc3RlbnQgYWNyb3NzIHRoZSBkYXRhc2V0LgogIAotICoqQmlvbG9naWNhbCBTaWduaWZpY2FuY2U6KiogQ2xvc2VseSByZWxhdGVkIHRheGEgdGVuZCB0byBoYXZlIG1vcmUgc2ltaWxhciBmaXREMTFEMDMgdmFsdWVzIChzbWFsbGVyIGRpZmZlcmVuY2VzKSBjb21wYXJlZCB0byBkaXN0YW50bHkgcmVsYXRlZCB0YXhhLiBUaGlzIHN1Z2dlc3RzIHRoYXQgdGhlIGZpdEQxMUQwMyB0cmFpdCBzaG93cyBzb21lIGRlZ3JlZSBvZiBwaHlsb2dlbmV0aWMgc2lnbmFsIG9yIGNvbnNlcnZhdGlvbi4KICAKLSAqKkV2b2x1dGlvbmFyeSBJbXBsaWNhdGlvbnM6KiogVGhpcyByZXN1bHQgaW5kaWNhdGVzIHRoYXQgZXZvbHV0aW9uYXJ5IHJlbGF0ZWRuZXNzIGRvZXMgcGxheSBhIHJvbGUgaW4gdGhlIHNpbWlsYXJpdHkgb2YgZml0RDExRDAzIHZhbHVlcy4gQ2xvc2VseSByZWxhdGVkIHNwZWNpZXMgdGVuZCB0byBoYXZlIG1vcmUgc2ltaWxhciB2YWx1ZXMsIHdoaWNoIGNvdWxkIGJlIGR1ZSB0byBzaGFyZWQgZXZvbHV0aW9uYXJ5IGhpc3Rvcnkgb3Igc2ltaWxhciBlbnZpcm9ubWVudGFsIGFkYXB0YXRpb25zLgogIAotICoqQ29udHJhc3Qgd2l0aCBFYXJsaWVyIENvcnJlbGF0aW9uOioqIFdoaWxlIGVhcmxpZXIgeW91IGZvdW5kIGEgd2VhayBjb3JyZWxhdGlvbiBiZXR3ZWVuIHBoeWxvZ2VuZXRpYyBkaXN0YW5jZSBhbmQgZml0RDExRDAzIGRpZmZlcmVuY2VzLCB0aGlzIHQtdGVzdCByZXZlYWxzIGEgY2xlYXIgZGlzdGluY3Rpb24gYmV0d2VlbiBjbG9zZSBhbmQgZGlzdGFudCByZWxhdGlvbnNoaXBzLiBUaGlzIGhpZ2hsaWdodHMgdGhlIGltcG9ydGFuY2Ugb2YgY29uc2lkZXJpbmcgZGlmZmVyZW50IGFuYWx5dGljYWwgYXBwcm9hY2hlcywgYXMgdGhleSBjYW4gcmV2ZWFsIGRpZmZlcmVudCBhc3BlY3RzIG9mIHRoZSBkYXRhLgogIAotICoqUHJhY3RpY2FsIFNpZ25pZmljYW5jZToqKiBUaGUgZGlmZmVyZW5jZSBpbiBtZWFucyAoYWJvdXQgMS4yMjYpIHNob3VsZCBiZSBjb25zaWRlcmVkIGluIHRoZSBjb250ZXh0IG9mIHRoZSBvdmVyYWxsIHJhbmdlIGFuZCBiaW9sb2dpY2FsIG1lYW5pbmcgb2YgZml0RDExRDAzIHZhbHVlcyB0byBkZXRlcm1pbmUgaXRzIHByYWN0aWNhbCBzaWduaWZpY2FuY2UuCgpJbiBzdW1tYXJ5LCB0aGlzIGFuYWx5c2lzIHByb3ZpZGVzIHN0cm9uZyBldmlkZW5jZSB0aGF0IGNsb3NlbHkgcmVsYXRlZCB0YXhhIGhhdmUgbW9yZSBzaW1pbGFyIGZpdEQxMUQwMyB2YWx1ZXMgY29tcGFyZWQgdG8gZGlzdGFudGx5IHJlbGF0ZWQgdGF4YS4gVGhpcyBzdXBwb3J0cyB0aGUgaWRlYSB0aGF0IHRoZXJlIGlzIGEgcGh5bG9nZW5ldGljIGNvbXBvbmVudCB0byB0aGUgZml0RDExRDAzIHRyYWl0LCBldmVuIGlmIGl0J3Mgbm90IGNhcHR1cmVkIHdlbGwgYnkgYSBzaW1wbGUgbGluZWFyIGNvcnJlbGF0aW9uIHdpdGggcGh5bG9nZW5ldGljIGRpc3RhbmNlLiBUaGUgbGFyZ2Ugc2FtcGxlIHNpemUgYW5kIGhpZ2hseSBzaWduaWZpY2FudCByZXN1bHQgc3VnZ2VzdCB0aGF0IHRoaXMgaXMgYSByb2J1c3QgZmluZGluZywgdGhvdWdoIHlvdSBzaG91bGQgYWx3YXlzIGNvbnNpZGVyIHRoZSBiaW9sb2dpY2FsIGNvbnRleHQgYW5kIHBvdGVudGlhbCBjb25mb3VuZGluZyBmYWN0b3JzIGluIHlvdXIgaW50ZXJwcmV0YXRpb24uCgojIyMjIFBsb3QgVG9nZXRoZXIKCmBgYHtyfQojIFJlbW92ZSBsZWdlbmRzCnRtcDAuMC50cmVlIDwtIHRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMC4wdG1wX2ZpdG5lc3NfcGh5bHVtX3AyICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnbm9uZScpICsKICBnZ3RpdGxlKCIwLjAgVE1QIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKdG1wMC4wNTgudHJlZSA8LSB0cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzAuMDU4dG1wX2ZpdG5lc3NfcGh5bHVtX3AyICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnbm9uZScpICsKICBnZ3RpdGxlKCIwLjA1OCBUTVAiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCgp0bXAwLjUudHJlZSA8LSB0cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzAuNXRtcF9maXRuZXNzX3BoeWx1bV9wMiArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKSArCiAgZ2d0aXRsZSgiMC41IFRNUCBcbihNSUMpIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKdG1wMS4wLnRyZWUgPC0gdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8xLjB0bXBfZml0bmVzc19waHlsdW1fcDIgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJykgKwogIGdndGl0bGUoIjEuMCBUTVAiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCgp0bXAxMC50cmVlIDwtIHRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMTB0bXBfZml0bmVzc19waHlsdW1fcDIgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJykgKwogIGdndGl0bGUoIjEwIFRNUCIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCnRtcDUwLnRyZWUgPC0gdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF81MHRtcF9maXRuZXNzX3BoeWx1bV9wMiArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKSArCiAgZ2d0aXRsZSgiNTAgVE1QIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKdG1wMjAwLnRyZWUgPC0gdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8yMDB0bXBfZml0bmVzc19waHlsdW1fcDIgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJykgKwogIGdndGl0bGUoIjIwMCBUTVAgXG4oNDAweCBNSUMpIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpgYGAKCmBgYHtyfQojIENyZWF0ZSB0aGUgcGF0Y2h3b3JrIHdpdGggdGhlIGxlZ2VuZCBvbiB0aGUgcmlnaHQKcGF0Y2g5IDwtICh0bXAwLjAudHJlZSkgLwogICAgICAgICAgICh0bXAwLjA1OC50cmVlIHwgdG1wMC41LnRyZWUgfCB0bXAxLjAudHJlZSkgLwogICAgICAgICAgICh0bXAxMC50cmVlIHwgdG1wNTAudHJlZSB8IHRtcDIwMC50cmVlKSArCiAgICAgICAgICAgcGxvdF9sYXlvdXQoaGVpZ2h0cyA9IGMoMSwgMSwgMSkpCgpwYXRjaDkKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iUGVyZmVjdHMvUExPVFMvVHJlZXMvVE1QL0xpYjE1LlBlcmZlY3RzLkdvb2QuNUJDcy5UTVAuR3JhZGllbnQuUklORy5wZGYiLCBwbG90PXBhdGNoOSwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gMTIsIGhlaWdodCA9IDcuNSwgdW5pdHMgPSAiaW4iKQpgYGAKClBsb3QganVzdCB0aGUgY29sb3JlZCB0cmVlIHRpcHMgKG5vIHBoeWx1bSByaW5nKQpgYGB7cn0KIyBSZW1vdmUgbGVnZW5kcwp0bXAwLjAudHJlZS50aXBzIDwtIHRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMC4wdG1wX2ZpdG5lc3NfbGFiZWxsZWQgKwogIGdndGl0bGUoIjAuMCBUTVAgXG4oQ29tcGxlbWVudGF0aW9uKSIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCnRtcDAuMDU4LnRyZWUudGlwcyA8LSB0cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzAuMDU4dG1wX2ZpdG5lc3NfbGFiZWxsZWQgKwogIGdndGl0bGUoIjAuMDU4IFRNUCIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCnRtcDAuNS50cmVlLnRpcHMgPC0gdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8wLjV0bXBfZml0bmVzc19sYWJlbGxlZCArCiAgZ2d0aXRsZSgiMC4wNSBUTVAgXG4oTUlDKSIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCnRtcDEuMC50cmVlLnRpcHMgPC0gdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8xLjB0bXBfZml0bmVzc19sYWJlbGxlZCArCiAgZ2d0aXRsZSgiMS4wIFRNUCIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCnRtcDEwLnRyZWUudGlwcyA8LSB0cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzEwdG1wX2ZpdG5lc3NfbGFiZWxsZWQgKwogIGdndGl0bGUoIjEwIFRNUCIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCnRtcDUwLnRyZWUudGlwcyA8LSB0cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzUwdG1wX2ZpdG5lc3NfbGFiZWxsZWQgKwogIGdndGl0bGUoIjUwIFRNUCIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCnRtcDIwMC50cmVlLnRpcHMgPC0gdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8yMDB0bXBfZml0bmVzc19sYWJlbGxlZCArCiAgZ2d0aXRsZSgiMjAwIFRNUCBcbig0MDB4IE1JQykiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCmBgYAoKYGBge3J9CiMgUGxvdCB0b2dldGhlcgpwYXRjaDEwIDwtICh0bXAwLjAudHJlZS50aXBzKSAvCiAgICAgICAgICAgKHRtcDAuMDU4LnRyZWUudGlwcyB8IHRtcDAuNS50cmVlLnRpcHMgfCB0bXAxLjAudHJlZS50aXBzKSAvCiAgICAgICAgICAgKHRtcDEwLnRyZWUudGlwcyB8IHRtcDUwLnRyZWUudGlwcyB8IHRtcDIwMC50cmVlLnRpcHMpICsKICAgICAgICAgICBwbG90X2xheW91dChoZWlnaHRzID0gYygxLCAxLCAxKSkKCnBhdGNoMTAKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iUGVyZmVjdHMvUExPVFMvVHJlZXMvVE1QL0xpYjE1LlBlcmZlY3RzLkdvb2QuNUJDcy5UTVAuR3JhZGllbnQuVElQUy5wZGYiLCBwbG90PXBhdGNoMTAsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA3LjUsIHVuaXRzID0gImluIikKYGBgCgpQbG90IHRoZSBNSUMgYW5kIDQwMHggTUlDIHRyZWVzIHNpZGUtYnktc2lkZToKYGBge3J9CiMgRm9yIHRoZSBmaXJzdCB0cmVlCnRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMC41dG1wX2ZpdG5lc3NfcGh5bHVtX3AyX25vX2xlZ2VuZCA8LSB0cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzAuNXRtcF9maXRuZXNzX3BoeWx1bV9wMiArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKwogIGdndGl0bGUoIkNvZG9uIDEgTGlicmFyeSBcbk1JQyAoMC41IHVnL21MIFRNUCkiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCgojIEZvciB0aGUgc2Vjb25kIHRyZWUgKHRoaXMgc2VlbXMgY29ycmVjdCwgYnV0IEknbGwgaW5jbHVkZSBpdCBmb3IgY29tcGxldGVuZXNzKQp0cmVlX3BlcmZlY3RzMTVfNUJDc19nb29kXzIwMHRtcF9maXRuZXNzX3BoeWx1bV9wMl9ub19sZWdlbmQgPC0gdHJlZV9wZXJmZWN0czE1XzVCQ3NfZ29vZF8yMDB0bXBfZml0bmVzc19waHlsdW1fcDIgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiKSArCiAgZ2d0aXRsZSgiQ29kb24gMSBMaWJyYXJ5IFxuNDAweCBNSUMgKDIwMCB1Zy9tTCBUTVApIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKIyBDb21iaW5lIHRoZSBwbG90cwpwYXRjaDExIDwtIHRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMC41dG1wX2ZpdG5lc3NfcGh5bHVtX3AyX25vX2xlZ2VuZCB8IHRyZWVfcGVyZmVjdHMxNV81QkNzX2dvb2RfMjAwdG1wX2ZpdG5lc3NfcGh5bHVtX3AyX25vX2xlZ2VuZApwYXRjaDExCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL1RyZWVzL1RNUC9MaWIxNS5GYXN0VHJlZS41QkNzLnBlcmZlY3RzLmdvb2QuZml0RDA3RDAzLmZpdEQxMUQwMy5NSUMuNDAweE1JQy5QaHlsdW0uUklORy5wZGYiLCBwbG90PXBhdGNoMTEsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA3LjUsIHVuaXRzID0gImluIikKYGBgCgojIyMgU2hhcmVkIFBlcmZlY3RzIFRyZWVzClJldGFpbiBvbmx5IHBlcmZlY3RzIHdpdGggYSBmaXRuZXNzIHNjb3JlID49IC0xIGFuZCBhIG1pbmltdW0gb2YgNSBCQ3MgKG51bXBydW5lZEJDcyA+IDQpIHNoYXJlZCBiZXR3ZWVuIExpYjE1IChmaXREMDVEMDMpIGFuZCBMaWIxNiAoZml0RDEyRDA0KS4gVGhpcyBsaXN0IGluY2x1ZGVzIDE5NCB1bmlxdWUgREhGUiBwZXJmZWN0cy4gVXNlIHRoZSBgcGVyZmVjdHNfMTVfMTZfc2hhcmVkXzVCQ3NfZ29vZGAgZGF0YXNldCB0byBjb25zdHJ1Y3QgYSBwaHlsb2dlbmV0aWMgdHJlZSBmb3IgZGlzcGxheWluZyBjb21wbGVtZW50YXRpb24gZml0bmVzcyBhbmQgcmVzaXN0YW5jZSB0byB0cmltZXRob3ByaW0gZml0bmVzcy4KCkZpcnN0LCBjYWxjdWxhdGUgbWluIGFuZCBtYXggZml0bmVzcyBieSBsaWJyYXJ5OgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBNaW5pbXVtIGZpdG5lc3Mgb2YgU2hhcmVkIFBlcmZlY3RzIGluIExpYjE1IGF0IENvbXBsZW1lbnRhdGlvbiAoNUJDcykKbWluKHBlcmZlY3RzXzE1XzE2X3NoYXJlZF81QkNzJGZpdEQwNUQwMywgbmEucm0gPSBUUlVFKQoKIyBNYXhpbXVtIGZpdG5lc3Mgb2YgU2hhcmVkIFBlcmZlY3RzIGluIExpYjE1IGF0IENvbXBsZW1lbnRhdGlvbiAoNUJDcykKbWF4KHBlcmZlY3RzXzE1XzE2X3NoYXJlZF81QkNzJGZpdEQwNUQwMywgbmEucm0gPSBUUlVFKQoKIyBNaW5pbXVtIGZpdG5lc3Mgb2YgU2hhcmVkIFBlcmZlY3RzIGluIExpYjE2IGF0IENvbXBsZW1lbnRhdGlvbiAoNUJDcykKbWluKHBlcmZlY3RzXzE1XzE2X3NoYXJlZF81QkNzJGZpdEQxMkQwNCwgbmEucm0gPSBUUlVFKQoKIyBNYXhpbXVtIGZpdG5lc3Mgb2YgU2hhcmVkIFBlcmZlY3RzIGluIExpYjE2IGF0IENvbXBsZW1lbnRhdGlvbiAoNUJDcykKbWF4KHBlcmZlY3RzXzE1XzE2X3NoYXJlZF81QkNzJGZpdEQxMkQwNCwgbmEucm0gPSBUUlVFKQoKIyBBdmVyYWdlIGZpdG5lc3Mgb2YgU2hhcmVkIFBlcmZlY3RzIGluIExpYjE1IGF0IENvbXBsZW1lbnRhdGlvbiAoNUJDcykKbWVhbihwZXJmZWN0c18xNV8xNl9zaGFyZWRfNUJDcyRmaXREMDVEMDMsIG5hLnJtID0gVFJVRSkKCiMgQXZlcmFnZSBmaXRuZXNzIG9mIFNoYXJlZCBQZXJmZWN0cyBpbiBMaWIxNiBhdCBDb21wbGVtZW50YXRpb24gKDVCQ3MpCm1lYW4ocGVyZmVjdHNfMTVfMTZfc2hhcmVkXzVCQ3MkZml0RDEyRDA0LCBuYS5ybSA9IFRSVUUpCmBgYAoKUmV0YWluIDE5NCB1bmlxdWUgREhGUiBwZXJmZWN0cyB3aXRoIGEgZml0bmVzcyBzY29yZSA+PSAtMSBhbmQgYSBtaW5pbXVtIG9mIDUgQkNzIChudW1wcnVuZWRCQ3MgPiA0KSBzaGFyZWQgYmV0d2VlbiBMaWIxNSAoZml0RDA1RDAzKSBhbmQgTGliMTYgKGZpdEQxMkQwNCkuCmBgYHtyfQojIFN1YnNldCBkYXRhc2V0IGZvciB0cmVlIGJ1aWxkaW5nCnBlcmZlY3RzXzE1XzE2X3NoYXJlZF81QkNzX2dvb2RfdHJlZSA8LSBwZXJmZWN0c18xNV8xNl9zaGFyZWRfNUJDc19nb29kICU+JQogIHNlbGVjdChtdXRJRCwgc2VxLngsCiAgICAgICAgIGZpdEQwNUQwMywgZml0RDA2RDAzLCBmaXREMDdEMDMsIGZpdEQwOEQwMywgZml0RDA5RDAzLCBmaXREMTBEMDMsIGZpdEQxMUQwMywKICAgICAgICAgZml0RDEyRDA0LCBmaXRFMDFEMDQsIGZpdEUwMkQwNCwgZml0RTAzRDA0LCBmaXRFMDREMDQsIGZpdEUwNUQwNCwgZml0RTA2RDA0KQpgYGAKCkFkZCAidGF4SUQiIGFuZCAiUGN0SWRlbnRFY29saSIgdG8gZWFjaCAibXV0SUQiCmBgYHtyfQojIFJlbmFtZSAibXV0SUQiIHRvICJJRCIgdG8gbWVyZ2Ugd2l0aCBgb3JnaW5mb2AgZGY6CnBlcmZlY3RzXzE1XzE2X3NoYXJlZF81QkNzX2dvb2RfdHJlZSA8LSBwZXJmZWN0c18xNV8xNl9zaGFyZWRfNUJDc19nb29kX3RyZWUgJT4lIHJlbmFtZShJRCA9IG11dElEKQoKIyBNZXJnZSAiVGF4SUQiIGFuZCAiUGN0SWRlbnRFY29saSIgZnJvbSBgb3JnaW5mb2AgdG8gYHBlcmZlY3RzXzE1XzE2X3NoYXJlZF81QkNzX2dvb2RfdHJlZWA6CnBlcmZlY3RzXzE1XzE2X3NoYXJlZF81QkNzX2dvb2RfdHJlZSA8LSBwZXJmZWN0c18xNV8xNl9zaGFyZWRfNUJDc19nb29kX3RyZWUgJT4lCiAgbGVmdF9qb2luKG9yZ2luZm8gJT4lIHNlbGVjdChJRCwgVGF4SUQsIFBjdElkZW50RWNvbGkpLCBieSA9ICJJRCIpCmBgYAoKQ3JlYXRlIGEgRkFTVEEgZmlsZSBjb250YWluaW5nIHRoZSBJRCBhbmQgaXRzIGFzc29jaWF0ZWQgcHJvdGVpbiBzZXF1ZW5jZSBmb3IgYWxpZ25tZW50CmBgYHtyLCByZXN1bHRzPSdoaWRlJ30KIyBDb2xsZWN0IHRoZSBzZXF1ZW5jZXMgaW4gRkFTVEEgZm9ybWF0CnBlcmZlY3RzXzE1XzE2X3NoYXJlZF81QkNzX2dvb2RfdHJlZV9mYXN0YV9jb250ZW50IDwtIHBhc3RlKCI+IixwZXJmZWN0c18xNV8xNl9zaGFyZWRfNUJDc19nb29kX3RyZWUkSUQsICJcbiIsIHBlcmZlY3RzXzE1XzE2X3NoYXJlZF81QkNzX2dvb2RfdHJlZSRzZXEsICJcbiIsIHNlcCA9ICIiLCBjb2xsYXBzZSA9ICIiKQoKIyBEZWZpbmUgdGhlIGZpbGUgcGF0aCBpbiB0aGUgd29ya2luZyBkaXJlY3RvcnkKcGVyZmVjdHNfMTVfMTZfc2hhcmVkXzVCQ3NfZ29vZF90cmVlX2Zhc3RhX3BhdGggPC0gZmlsZS5wYXRoKGdldHdkKCksICJQZXJmZWN0cy9UUkVFUy9MaWIuMTUuMTYuc2hhcmVkLjVCQ3MuZ29vZC5wZXJmZWN0cy5jb21wbGVtZW50LmZhc3RhIikKCiMgV3JpdGUgdGhlIEZBU1RBIGNvbnRlbnQgdG8gdGhlIGZpbGUKd3JpdGVMaW5lcyhwZXJmZWN0c18xNV8xNl9zaGFyZWRfNUJDc19nb29kX3RyZWVfZmFzdGFfY29udGVudCwgY29uID0gcGVyZmVjdHNfMTVfMTZfc2hhcmVkXzVCQ3NfZ29vZF90cmVlX2Zhc3RhX3BhdGgpCmBgYAoKUGVyZm9ybSBhIG11bHRpcGxlIHNlcXVlbmNlIGFsaWdubWVudCBvbiB0aGUgRkFTVEEgZmlsZSB1c2luZyBDTFVTVEFMIE9tZWdhOgpgYGB7YmFzaCByZXN1bHRzID0gJ2hpZGUnfQojIE1heSBuZWVkIHRvIGVuYWJsZSBwZXJtaXNzaW9ucyB0byBydW4gdGhlIGV4ZWN1dGFibGU6CiNjaG1vZCAreCBjbHVzdGFsbwouL1NjcmlwdHMvY2x1c3RhbG8gLWkgUGVyZmVjdHMvVFJFRVMvTGliLjE1LjE2LnNoYXJlZC41QkNzLmdvb2QucGVyZmVjdHMuY29tcGxlbWVudC5mYXN0YSAtbyBQZXJmZWN0cy9UUkVFUy9MaWIuMTUuMTYuc2hhcmVkLjVCQ3MuZ29vZC5wZXJmZWN0cy5jb21wbGVtZW50LmFsaWduZWQuZmFzdGEgLS1vdXRmbXQ9ZmEgLS1mb3JjZQpgYGAKClVzZSBGYXN0VHJlZSAocGh5bG9nZW5ldGljIHRyZWUgYnVpbGRpbmcgcHJvZ3JhbSkgdG8gaW5mZXIgdGhlIHRyZWUgZnJvbSB0aGUgYWxpZ25lZCBhbWlubyBhY2lkIHNlcXVlbmNlczoKYGBge2Jhc2h9CiMgTUwgTW9kZWw6IEpvbmVzLVRheWxvci1UaG9ydG9uCiMgY2htb2QgK3ggRmFzdFRyZWUKLi9TY3JpcHRzL0Zhc3RUcmVlIFBlcmZlY3RzL1RSRUVTL0xpYi4xNS4xNi5zaGFyZWQuNUJDcy5nb29kLnBlcmZlY3RzLmNvbXBsZW1lbnQuYWxpZ25lZC5mYXN0YSA+IFBlcmZlY3RzL1RSRUVTL0xpYi4xNS4xNi5zaGFyZWQuNUJDcy5nb29kLnBlcmZlY3RzLmNvbXBsZW1lbnQudHJlZS5uZXdpY2sKYGBgCgojIyMjIE5ld2ljayBUcmVlIEZpbGUKSW1wb3J0IHRoZSBuZXdpY2sgdHJlZSBmaWxlIGJhc2VkIG9uIHRoZSBzZXF1ZW5jZSBhbGlnbm1lbnQgb2Ygc2hhcmVkIHBlcmZlY3RzIGRlcml2ZWQgZnJvbSBMaWIxNSAmIExpYjE2IG1hcHBpbmcgZmlsZToKYGBge3J9CiMgRnVsbCB0cmVlIGFsaWdubWVudCAoMTk0IG11dElEKQpGaXR0cmVlMTUuMTYgPC0gcmVhZC50cmVlKCJQZXJmZWN0cy9UUkVFUy9MaWIuMTUuMTYuc2hhcmVkLjVCQ3MuZ29vZC5wZXJmZWN0cy5jb21wbGVtZW50LnRyZWUubmV3aWNrIikgICAjIG5ld2ljayBmb3JtYXQKRml0dHJlZTE1LjE2CmBgYAoKRXh0cmFjdCB0aGUgdGlwIGxhYmVscyBmcm9tIHRoZSBuZXdpY2sgdHJlZSBmaWxlIHRvIG1hdGNoIHdpdGggTkNCSSB0YXhvbm9teSBmb3IgZG93bnN0cmVhbSBwbG90dGluZzoKYGBge3J9CiMgRXh0cmFjdCB0aXAgbGFiZWxzIGZyb20gQWxsdHJlZTE1CkZpdHRyZWUxNS4xNl90aXBfbGFiZWxzIDwtIEZpdHRyZWUxNS4xNiR0aXAubGFiZWwKCiMgQ3JlYXRlIGEgbmV3IGRhdGEgZnJhbWUgd2l0aCB1bmlxdWUgdGlwIGxhYmVscwpGaXR0cmVlMTUuMTZfdGlwX2xhYmVsc19kZiA8LSBkYXRhLmZyYW1lKHRpcC5sYWJlbCA9IHVuaXF1ZShGaXR0cmVlMTUuMTZfdGlwX2xhYmVscykpCgojIFByaW50IHRoZSBmaXJzdCBmZXcgcm93cyBvZiB0aGUgbmV3IGRhdGEgZnJhbWUKaGVhZChGaXR0cmVlMTUuMTZfdGlwX2xhYmVsc19kZikKYGBgCgpNYXRjaCBlYWNoIHRpcC5sYWJlbCBJRCBpbiBgQWxsdHJlZTE1YCB3aXRoIGl0J3MgYXNzb2NpYXRlZCBUYXhJRCBmcm9tIGBvcmdpbmZvYCBkYXRhZnJhbWU6CmBgYHtyfQojIFJlbmFtZSBjb2x1bW4gInRpcC5sYWJlbCIgdG8gIklEIgpjb2xuYW1lcyhGaXR0cmVlMTUuMTZfdGlwX2xhYmVsc19kZikgPC0gYygiSUQiKQoKIyBNZXJnZSBvcmdpbmZvIHdpdGggQWxsdHJlZTE1X3RpcF9sYWJlbHNfZGYgYmFzZWQgb24gdGhlIHNoYXJlZCBJRApGaXR0cmVlMTUuMTZfdGF4YSA8LSBtZXJnZShGaXR0cmVlMTUuMTZfdGlwX2xhYmVsc19kZiwgb3JnaW5mbywgYnkgPSAiSUQiLCBhbGwueCA9IFRSVUUpCgojIFByaW50IHRoZSBmaXJzdCBmZXcgcm93cyBvZiB0aGUgbWVyZ2VkIGRhdGEgZnJhbWUKaGVhZChGaXR0cmVlMTUuMTZfdGF4YSkKYGBgCgpBZGQgZml0bmVzcyBzY29yZXMgdG8gdGhlIGBGaXR0cmVlMTVfdGF4YV9tZXJnZWRgIG9iamVjdCBwcmlvciB0byBwbG90dGluZwpgYGB7cn0KRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkIDwtIEZpdHRyZWUxNS4xNl90YXhhICU+JQogIGxlZnRfam9pbihwZXJmZWN0c18xNV8xNl9zaGFyZWRfNUJDc19nb29kX3RyZWUgJT4lIHNlbGVjdChJRCwgZml0RDA1RDAzLCBmaXREMDZEMDMsIGZpdEQwN0QwMywgZml0RDA4RDAzLCBmaXREMDlEMDMsIGZpdEQxMEQwMywgZml0RDExRDAzLCBmaXREMTJEMDQsIGZpdEUwMUQwNCwgZml0RTAyRDA0LCBmaXRFMDNEMDQsIGZpdEUwNEQwNCwgZml0RTA1RDA0LCBmaXRFMDZEMDQpLCBieSA9ICJJRCIpCmBgYAoKTWVyZ2UgdGhlIE5DQkkgdGF4b25vbXkgY29sdW1ucyB0byBgRml0dHJlZTE1LjE2X3RheGFgIGJhc2VkIG9uIHNoYXJlZCBUYXhJRApgYGB7cn0KIyBNZXJnZSB0aGUgTkNCSSB0YXhvbm9teSBjb2x1bW5zIHRvIEFsbHRyZWUxNV90YXhhIGJhc2VkIG9uIHNoYXJlZCBUYXhJRApGaXR0cmVlMTUuMTZfdGF4YV9tZXJnZWQkTkNCSS5uYW1lIDwtIE5BCkZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCROQ0JJLnN1cGVya2luZ2RvbSA8LSBOQQpGaXR0cmVlMTUuMTZfdGF4YV9tZXJnZWQkTkNCSS5waHlsdW0gPC0gTkEKRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJE5DQkkuY2xhc3MgPC0gTkEKRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJE5DQkkub3JkZXIgPC0gTkEKRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJE5DQkkuZmFtaWx5IDwtIE5BCkZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCROQ0JJLmdlbnVzIDwtIE5BCkZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCROQ0JJLnNwZWNpZXMgPC0gTkEKCiMgTkNCSS5uYW1lCkZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCROQ0JJLm5hbWVbRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSA8LSBuY2JpX3RheGEkTkNCSS5uYW1lW21hdGNoKEZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCRUYXhJRFtGaXR0cmVlMTUuMTZfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdLCBuY2JpX3RheGEkVGF4SUQpXQoKIyBOQ0JJLnN1cGVya2luZ2RvbQpGaXR0cmVlMTUuMTZfdGF4YV9tZXJnZWQkTkNCSS5zdXBlcmtpbmdkb21bRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSA8LSBuY2JpX3RheGEkTkNCSS5zdXBlcmtpbmdkb21bbWF0Y2goRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJFRheElEW0ZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCRUYXhJRCAlaW4lIG5jYmlfdGF4YSRUYXhJRF0sIG5jYmlfdGF4YSRUYXhJRCldCgojIE5DQkkucGh5bHVtCkZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCROQ0JJLnBoeWx1bVtGaXR0cmVlMTUuMTZfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdIDwtIG5jYmlfdGF4YSROQ0JJLnBoeWx1bVttYXRjaChGaXR0cmVlMTUuMTZfdGF4YV9tZXJnZWQkVGF4SURbRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSwgbmNiaV90YXhhJFRheElEKV0KCiMgTkNCSS5jbGFzcwpGaXR0cmVlMTUuMTZfdGF4YV9tZXJnZWQkTkNCSS5jbGFzc1tGaXR0cmVlMTUuMTZfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdIDwtIG5jYmlfdGF4YSROQ0JJLmNsYXNzW21hdGNoKEZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCRUYXhJRFtGaXR0cmVlMTUuMTZfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdLCBuY2JpX3RheGEkVGF4SUQpXQoKIyBOQ0JJLm9yZGVyCkZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCROQ0JJLm9yZGVyW0ZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCRUYXhJRCAlaW4lIG5jYmlfdGF4YSRUYXhJRF0gPC0gbmNiaV90YXhhJE5DQkkub3JkZXJbbWF0Y2goRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJFRheElEW0ZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCRUYXhJRCAlaW4lIG5jYmlfdGF4YSRUYXhJRF0sIG5jYmlfdGF4YSRUYXhJRCldCgojIE5DQkkuZmFtaWx5CkZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCROQ0JJLmZhbWlseVtGaXR0cmVlMTUuMTZfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdIDwtIG5jYmlfdGF4YSROQ0JJLmZhbWlseVttYXRjaChGaXR0cmVlMTUuMTZfdGF4YV9tZXJnZWQkVGF4SURbRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSwgbmNiaV90YXhhJFRheElEKV0KCiMgTkNCSS5mYW1pbHkKRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJE5DQkkuZ2VudXNbRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSA8LSBuY2JpX3RheGEkTkNCSS5nZW51c1ttYXRjaChGaXR0cmVlMTUuMTZfdGF4YV9tZXJnZWQkVGF4SURbRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJFRheElEICVpbiUgbmNiaV90YXhhJFRheElEXSwgbmNiaV90YXhhJFRheElEKV0KCiMgTkNCSS5mYW1pbHkKRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJE5DQkkuc3BlY2llc1tGaXR0cmVlMTUuMTZfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBuY2JpX3RheGEkVGF4SURdIDwtIG5jYmlfdGF4YSROQ0JJLnNwZWNpZXNbbWF0Y2goRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJFRheElEW0ZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCRUYXhJRCAlaW4lIG5jYmlfdGF4YSRUYXhJRF0sIG5jYmlfdGF4YSRUYXhJRCldCmBgYAoKIyMjIyBQY3QgU2ltaWxhcml0eSBFIGNvbGkKClBsb3QgYSBjaXJjdWxhciBwaHlsb2dlbmV0aWMgdHJlZSB3aXRoIGNvbG9yLXNjYWxlIGJhc2VkIG9uIHBoeWxvZ2VuZXRpYyBkaXN0YW5jZSBiZXR3ZWVuIGVhY2ggREhGUiBob21vbG9nIHJlbGF0aXZlIHRvIHRoZSBFLiBjb2xpIERIRlIgZ2VuZS4KYGBge3J9CmZpdHRyZWUxNS4xNl9OQ0JJX2Vjb2xpX2lkZW50IDwtIGdndHJlZShGaXR0cmVlMTUuMTYsIGxheW91dD0iY2lyY3VsYXIiLCBicmFuY2gubGVuZ3RoPSJub25lIikgJTwrJSAKICBGaXR0cmVlMTUuMTZfdGF4YV9tZXJnZWQgKyAKICBhZXMoY29sb3I9UGN0SWRlbnRFY29saSoxMDApICsKICBnZW9tX3RpcHBvaW50KGFlcyhjb2xvcj1QY3RJZGVudEVjb2xpKjEwMCksIHNpemU9MSwgYWxwaGE9MC44KSArCiAgZ2VvbV90aXBsYWIyKGFlcyhjb2xvcj1QY3RJZGVudEVjb2xpKjEwMCwgbGFiZWw9TkNCSS5uYW1lLCBhbmdsZT1hbmdsZSksIHNpemU9MSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibGVmdCIpICsgCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50MigiU2VxdWVuY2VcbklkZW50aXR5ICglKSIsIGxvdz0iYmx1ZSIsIG1pZD0iZ3JlZW4iLCBoaWdoPSJyZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgbWlkcG9pbnQ9KDEwMCttaW4oRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJFBjdElkZW50RWNvbGkpKjEwMCkvMikKCmZpdHRyZWUxNS4xNl9OQ0JJX2Vjb2xpX2lkZW50CmBgYAoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL1RyZWVzL1NoYXJlZC9MaWIxNS4xNi5GYXN0VHJlZS4xOTQucGVyZmVjdHMuY29tcGxlbWVudGF0aW9uLjVCQ3MuTkNCSS5lY29saS5wY3RpZGVudC5uYW1lcy5wbmciLCBwbG90PWZpdHRyZWUxNS4xNl9OQ0JJX2Vjb2xpX2lkZW50LAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNy41LCB1bml0cyA9ICJpbiIpCmBgYAoKIyMjIyBUYXhvbm9taWMgUmluZ3MKClRoZSBmb2xsb3dpbmcgc2VjdGlvbiBidWlsZHMgb24gdGhlIGluaXRpYWwgREhGUiBwaHlsb2dlbmV0aWMgdHJlZSBhbmQgYWRkcyB0aGUgTkNCSSB0YXhvbm9taWMgbGluZWFnZXMgYXMgYW4gb3V0ZXIgcmluZyB0byBkaXNwbGF5IHRoZSBicmVhZHRoIG9mIHNlcXVlbmNlIGRpdmVyc2l0eSBpbiB0aGUgMSw1MzYtREhGUiBkZXNpZ25lZCBsaWJyYXJ5LiBIb3dldmVyLCB0aGlzIHRyZWUgb25seSBjb250YWlucyA2NDMgdW5pcXVlIGJyYW5jaCB0aXBzICg0MSUgb2YgZnVsbCBsaWJyYXJ5IGRpdmVyc2l0eSkgZnJvbSB0aGUgcmVjb3ZlcmVkIHNoYXJlZCBwZXJmZWN0cyBiZXR3ZWVuIGxpYnJhcmllcy4KClJlcGxhY2UgdGhlIHZhbHVlIGluICJOQ0JJLnBoeWx1bSIgY29sdW1uIHdpdGggdGhlIHZhbHVlIGZyb20gIk5DQkkuY2xhc3MiIGlmICJOQ0JJLnBoeWx1bSIgaXMgIlBzZXVkb21vbmFkb3RhIgpgYGB7cn0KIyBSZXBsYWNlIHRoZSB2YWx1ZSBpbiAiTkNCSS5waHlsdW0iIGNvbHVtbiB3aXRoIHRoZSB2YWx1ZSBmcm9tICJOQ0JJLmNsYXNzIiBpZiAiTkNCSS5waHlsdW0iIGlzICJQc2V1ZG9tb25hZG90YSIKRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJE5DQkkucGh5bHVtIDwtIGlmZWxzZShGaXR0cmVlMTUuMTZfdGF4YV9tZXJnZWQkTkNCSS5waHlsdW0gPT0gIlBzZXVkb21vbmFkb3RhIiwgRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJE5DQkkuY2xhc3MsIEZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCROQ0JJLnBoeWx1bSkKCiMgUmVtb3ZlIHJvd3Mgd2l0aCBOQSBpbiBOQ0JJLnBoeWx1bSBjb2x1bW4KRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkIDwtIEZpdHRyZWUxNS4xNl90YXhhX21lcmdlZFshaXMubmEoRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJE5DQkkucGh5bHVtKSAmIEZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCROQ0JJLnBoeWx1bSAhPSAiTkEiLCBdCmBgYAoKKipEaXN0aW5jdCBQaHlsdW0gQ29sb3JzIGZvciBQbG90dGluZyoqCmBgYHtyfQojIE1lcmdlIGRpc3RpbmN0IHBoeWx1bSBjb2xvcnMgZnJvbSBBbGx0cmVlMTVfdGF4YV9tZXJnZWQgZGF0YWZyYW1lOgpGaXR0cmVlMTUuMTZfdGF4YV9tZXJnZWQkTkNCSS5waHlsdW1fY29sb3JzIDwtIE5BCkZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCROQ0JJLmNsYXNzX2NvbG9ycyA8LSBOQQoKIyBOQ0JJLnBoeWx1bV9jb2xvcnMKRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJE5DQkkucGh5bHVtX2NvbG9yc1tGaXR0cmVlMTUuMTZfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBBbGx0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SURdIDwtIEFsbHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLnBoeWx1bV9jb2xvcnNbbWF0Y2goRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJFRheElEW0ZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCRUYXhJRCAlaW4lIEFsbHRyZWUxNV90YXhhX21lcmdlZCRUYXhJRF0sIEFsbHRyZWUxNV90YXhhX21lcmdlZCRUYXhJRCldCgojIE5DQkkuY2xhc3NfY29sb3JzCkZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCROQ0JJLmNsYXNzX2NvbG9yc1tGaXR0cmVlMTUuMTZfdGF4YV9tZXJnZWQkVGF4SUQgJWluJSBBbGx0cmVlMTVfdGF4YV9tZXJnZWQkVGF4SURdIDwtIEFsbHRyZWUxNV90YXhhX21lcmdlZCROQ0JJLmNsYXNzX2NvbG9yc1ttYXRjaChGaXR0cmVlMTUuMTZfdGF4YV9tZXJnZWQkVGF4SURbRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJFRheElEICVpbiUgQWxsdHJlZTE1X3RheGFfbWVyZ2VkJFRheElEXSwgQWxsdHJlZTE1X3RheGFfbWVyZ2VkJFRheElEKV0KCiMgTGlzdCBwaHlsdW0gaW4gYWxwaGFiZXRpY2FsIG9yZGVyIGZvciBsZWdlbmQgYW5kIHBsb3R0aW5nCkZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCROQ0JJLnBoeWx1bSA8LSBmYWN0b3IoRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJE5DQkkucGh5bHVtLCBsZXZlbHM9TkNCSXBoeWxvQ29sb3IkTkNCSS5waHlsdW0pCgojIExpc3QgcGh5bHVtIGluIGFscGhhYmV0aWNhbCBvcmRlciBmb3IgbGVnZW5kIGFuZCBwbG90dGluZwpGaXR0cmVlMTUuMTZfdGF4YV9tZXJnZWQkTkNCSS5jbGFzcyA8LSBmYWN0b3IoRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJE5DQkkuY2xhc3MsIGxldmVscz1OQ0JJY2xhc3NDb2xvciROQ0JJLmNsYXNzKQpgYGAKCiMjIyMgQ29tcGxlbWVudGF0aW9uIFRyZWUKCioqTGliMTU6KiogU2hvdyB0aGUgbWluaW11bSBhbmQgbWF4aW11bSBmaXRuZXNzIHZhbHVlcyBiZXR3ZWVuIEQwNSAoTTkgbm8gc3VwcCkgdnMuIEQwMyAoTTkgZnVsbCBzdXBwKSBkYXRhc2V0OgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KbWluKEZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCRmaXREMDVEMDMsbmEucm09VCkKbWF4KEZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCRmaXREMDVEMDMsbmEucm09VCkKYGBgCgoqKkxpYjE1OioqIFBsb3QgYSBjaXJjdWxhciBwaHlsb2dlbmV0aWMgdHJlZSBmb3IgY29tcGxlbWVudGF0aW9uIHdpdGggY29sb3Itc2NhbGUgYmFzZWQgb24gZml0bmVzcyB2YWx1ZXMgYmV0d2VlbiBEMDUgdnMuIEQwMyBmb3IgZWFjaCBESEZSIGhvbW9sb2cKYGBge3J9CnRyZWVfcGVyZmVjdHMxNS4xNl81QkNzX2dvb2RfMHRtcF9maXRuZXNzX2xhYmVsbGVkIDwtIGdndHJlZShGaXR0cmVlMTUuMTYsIGxheW91dD0iY2lyY3VsYXIiLCBicmFuY2gubGVuZ3RoPSJub25lIikgJTwrJQogIEZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCArCiAgYWVzKGNvbG9yPWZpdEQwNUQwMykgKwogIGdlb21fdGlwcG9pbnQoYWVzKGNvbG9yPWZpdEQwNUQwMyksIHNpemU9MSwgYWxwaGE9MC44KSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJsZWZ0IikgKyAKICBzY2FsZV9jb2xvdXJfZ3JhZGllbnRuKAogICAgImZpdG5lc3MiLAogICAgY29sb3JzID0gYygiZGFya2JsdWUiLCAid2hpdGUiLCAiZ29sZCIpLAogICAgdmFsdWVzID0gc2NhbGVzOjpyZXNjYWxlKGMoLTEsIDAsIDEpKSwKICAgIG5hLnZhbHVlID0gImdyYXkiLAogICAgbGltaXRzID0gYygtMSwgMSkKICApCgp0cmVlX3BlcmZlY3RzMTUuMTZfNUJDc19nb29kXzB0bXBfZml0bmVzc19sYWJlbGxlZApgYGAKCmBgYHtyIGVjaG89RkFMU0V9CiMgQ29tcGxlbWVudGF0aW9uIEZpdG5lc3MgVHJlZSAodGlwcyBsYWJlbGVkKQpnZ3NhdmUoZmlsZT0iUGVyZmVjdHMvUExPVFMvVHJlZXMvU2hhcmVkL0xpYjE1LjE2LkZhc3RUcmVlLjVCQ3MucGVyZmVjdHMuZml0RDA1RDAzLkNPTVBMRU1FTlRBVElPTi50aXBzLmxhYmVsbGVkLnBoeWx1bS5wbmciLAogICAgICAgcGxvdD10cmVlX3BlcmZlY3RzMTUuMTZfNUJDc19nb29kXzB0bXBfZml0bmVzc19sYWJlbGxlZCwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gMTIsIGhlaWdodCA9IDcuNSwgdW5pdHMgPSAiaW4iKQpgYGAKCioqTGliMTY6KiogTWluIGFuZCBNYXggZml0bmVzcyBmb3IgY29tcGxlbWVudGF0aW9uCmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQptaW4oRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJGZpdEQxMkQwNCxuYS5ybT1UKQptYXgoRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkJGZpdEQxMkQwNCxuYS5ybT1UKQpgYGAKCioqTGliMTY6KiogUGxvdCBhIGNpcmN1bGFyIHBoeWxvZ2VuZXRpYyB0cmVlIGZvciBjb21wbGVtZW50YXRpb24gd2l0aCBjb2xvci1zY2FsZSBiYXNlZCBvbiBmaXRuZXNzIHZhbHVlcyBiZXR3ZWVuIEQwNSB2cy4gRDAzIGZvciBlYWNoIERIRlIgaG9tb2xvZwpgYGB7cn0KdHJlZV9wZXJmZWN0czE2LjE1XzVCQ3NfZ29vZF8wdG1wX2ZpdG5lc3NfbGFiZWxsZWQgPC0gZ2d0cmVlKEZpdHRyZWUxNS4xNiwgbGF5b3V0PSJjaXJjdWxhciIsIGJyYW5jaC5sZW5ndGg9Im5vbmUiKSAlPCslCiAgRml0dHJlZTE1LjE2X3RheGFfbWVyZ2VkICsKICBhZXMoY29sb3I9Zml0RDEyRDA0KSArCiAgZ2VvbV90aXBwb2ludChhZXMoY29sb3I9Zml0RDEyRDA0KSwgc2l6ZT0xLCBhbHBoYT0wLjgpICsKICAjZ2VvbV90aXBsYWIyKGFlcyhjb2xvcj1maXREMTJEMDQsIGxhYmVsPU5DQkkuc3BlY2llcywgYW5nbGU9YW5nbGUpLCBzaXplPTEpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249ImxlZnQiKSArIAogICNnZ3RpdGxlKCJMaWJyYXJ5IDE1IE1lZGlhbiBGaXRuZXNzIGF0IENvbXBsZW1lbnRhdGlvbiIpICsKICBzY2FsZV9jb2xvdXJfZ3JhZGllbnQyKCJmaXRuZXNzIiwKICAgICAgICAgICAgICAgICAgICAgICAgIGxvdz0iZGFya2JsdWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIG1pZD0id2hpdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgaGlnaD0iZ29sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAjbWlkPSJnb2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICNoaWdoPSJyZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgbmEudmFsdWU9ImdyYXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgbGltaXQgPSBjKC0xLDEpKQoKdHJlZV9wZXJmZWN0czE2LjE1XzVCQ3NfZ29vZF8wdG1wX2ZpdG5lc3NfbGFiZWxsZWQKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIENvbXBsZW1lbnRhdGlvbiBGaXRuZXNzIFRyZWUgKHRpcHMgbGFiZWxlZCkKZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL1RyZWVzL1NoYXJlZC9MaWIxNS4xNi5GYXN0VHJlZS41QkNzLnBlcmZlY3RzLmZpdEQxMkQwNC5DT01QTEVNRU5UQVRJT04udGlwcy5sYWJlbGxlZC5waHlsdW0ucG5nIiwKICAgICAgIHBsb3Q9dHJlZV9wZXJmZWN0czE2LjE1XzVCQ3NfZ29vZF8wdG1wX2ZpdG5lc3NfbGFiZWxsZWQsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA3LjUsIHVuaXRzID0gImluIikKYGBgCgpgYGB7cn0KcGF0Y2gxMiA8LSB0cmVlX3BlcmZlY3RzMTUuMTZfNUJDc19nb29kXzB0bXBfZml0bmVzc19sYWJlbGxlZCB8IHRyZWVfcGVyZmVjdHMxNi4xNV81QkNzX2dvb2RfMHRtcF9maXRuZXNzX2xhYmVsbGVkCnBhdGNoMTIKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIENvbXBsZW1lbnRhdGlvbiBGaXRuZXNzIFRyZWUgKHRpcHMgbGFiZWxlZCkKZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL1RyZWVzL1NoYXJlZC9MaWIxNS4xNi5GYXN0VHJlZS41QkNzLnBlcmZlY3RzLmZpdEQwNUQwMy5maXREMTJEMDQuQ09NUExFTUVOVEFUSU9OLnRpcHMubGFiZWxsZWQucGh5bHVtLnBuZyIsCiAgICAgICBwbG90PXBhdGNoMTIsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA3LjUsIHVuaXRzID0gImluIikKYGBgCgojIyMjIDAuNS1UTVAgKE1JQykgVHJlZSBQbG90CgoqKkxpYjE1OioqIFNob3cgdGhlIG1pbmltdW0gYW5kIG1heGltdW0gZml0bmVzcyB2YWx1ZXMgYmV0d2VlbiBEMDcgKDAuNSBtZy91TCBEYXkgMSkgdnMuIEQwMyAoTTkgbm8gc3VwcC4pIGluIHRoZSBkYXRhc2V0OgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KbWluKEZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCRmaXREMDdEMDMsbmEucm09VCkKbWF4KEZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCRmaXREMDdEMDMsbmEucm09VCkKYGBgCgoqKkxpYjE1OioqIFBsb3QgYSBjaXJjdWxhciBwaHlsb2dlbmV0aWMgdHJlZSBmb3IgcmVzaXN0YW5jZSB3aXRoIGNvbG9yLXNjYWxlIGJhc2VkIG9uIGZpdG5lc3MgdmFsdWVzIGJldHdlZW4gRDA3IHZzLiBEMDMgZm9yIGVhY2ggREhGUiBob21vbG9nCmBgYHtyfQp0cmVlX3BlcmZlY3RzMTUuMTZfNUJDc19nb29kXzAuNXRtcF9maXRuZXNzX2xhYmVsbGVkIDwtIGdndHJlZShGaXR0cmVlMTUuMTYsIGxheW91dD0iY2lyY3VsYXIiLCBicmFuY2gubGVuZ3RoPSJub25lIikgJTwrJQogIEZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCArCiAgYWVzKGNvbG9yPWZpdEQwN0QwMykgKwogIGdlb21fdGlwcG9pbnQoYWVzKGNvbG9yPWZpdEQwN0QwMyksIHNpemU9MSwgYWxwaGE9MC44KSArCiAgZ2VvbV90aXBsYWIyKGFlcyhjb2xvcj1maXREMDdEMDMsIGxhYmVsPU5DQkkuc3BlY2llcywgYW5nbGU9YW5nbGUpLCBzaXplPTEpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249ImxlZnQiKSArIAogIHNjYWxlX2NvbG91cl9ncmFkaWVudDIoImZpdG5lc3MiLAogICAgICAgICAgICAgICAgICAgICAgICAgbG93PSJibGFjayIsIAogICAgICAgICAgICAgICAgICAgICAgICAgbWlkPSJnb2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgIGhpZ2g9InJlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICBuYS52YWx1ZT0iZ3JheSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBsaW1pdCA9IGMoLTguNSwyLjUpKSArCiAgZ2d0aXRsZSgiQ29kb24gMSBMaWJyYXJ5IE1JQyBcbigwLjUgdWcvbUwgVE1QKSIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCnRyZWVfcGVyZmVjdHMxNS4xNl81QkNzX2dvb2RfMC41dG1wX2ZpdG5lc3NfbGFiZWxsZWQKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIE1JQyBGaXRuZXNzIFRyZWUgKHRpcHMgbGFiZWxlZCkKZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL1RyZWVzL1NoYXJlZC9MaWIxNS5GYXN0VHJlZS41QkNzLnBlcmZlY3RzLmZpdEQwN0QwMy5SRVNJU1RBTkNFLnRpcHMubGFiZWxsZWQucGh5bHVtLnBuZyIsCiAgICAgICBwbG90PXRyZWVfcGVyZmVjdHMxNS4xNl81QkNzX2dvb2RfMC41dG1wX2ZpdG5lc3NfbGFiZWxsZWQsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA3LjUsIHVuaXRzID0gImluIikKYGBgCgoqKkxpYjE2OioqIFNob3cgdGhlIG1pbmltdW0gYW5kIG1heGltdW0gZml0bmVzcyB2YWx1ZXMgYmV0d2VlbiBFMDIgKDAuNSBtZy91TCBEYXkgMSkgdnMuIEQwNCAoTTkgbm8gc3VwcC4pIGluIHRoZSBkYXRhc2V0OgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KbWluKEZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCRmaXRFMDJEMDQsbmEucm09VCkKbWF4KEZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCRmaXRFMDJEMDQsbmEucm09VCkKYGBgCgoqKkxpYjE2OioqIFBsb3QgYSBjaXJjdWxhciBwaHlsb2dlbmV0aWMgdHJlZSBmb3IgcmVzaXN0YW5jZSB3aXRoIGNvbG9yLXNjYWxlIGJhc2VkIG9uIGZpdG5lc3MgdmFsdWVzIGJldHdlZW4gRDA3IHZzLiBEMDMgZm9yIGVhY2ggREhGUiBob21vbG9nCmBgYHtyfQp0cmVlX3BlcmZlY3RzMTYuMTVfNUJDc19nb29kXzAuNXRtcF9maXRuZXNzX2xhYmVsbGVkIDwtIGdndHJlZShGaXR0cmVlMTUuMTYsIGxheW91dD0iY2lyY3VsYXIiLCBicmFuY2gubGVuZ3RoPSJub25lIikgJTwrJQogIEZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCArCiAgYWVzKGNvbG9yPWZpdEUwMkQwNCkgKwogIGdlb21fdGlwcG9pbnQoYWVzKGNvbG9yPWZpdEUwMkQwNCksIHNpemU9MSwgYWxwaGE9MC44KSArCiAgZ2VvbV90aXBsYWIyKGFlcyhjb2xvcj1maXRFMDJEMDQsIGxhYmVsPU5DQkkuc3BlY2llcywgYW5nbGU9YW5nbGUpLCBzaXplPTEpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249InJpZ2h0IikgKyAKICBzY2FsZV9jb2xvdXJfZ3JhZGllbnQyKCJmaXRuZXNzIiwKICAgICAgICAgICAgICAgICAgICAgICAgIGxvdz0iYmxhY2siLCAKICAgICAgICAgICAgICAgICAgICAgICAgIG1pZD0iZ29sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICBoaWdoPSJyZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgbmEudmFsdWU9ImdyYXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgbGltaXQgPSBjKC0xMC41LDIuNSkpICsKICBnZ3RpdGxlKCJDb2RvbiAyIExpYnJhcnkgTUlDIFxuKDAuNSB1Zy9tTCBUTVApIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKdHJlZV9wZXJmZWN0czE2LjE1XzVCQ3NfZ29vZF8wLjV0bXBfZml0bmVzc19sYWJlbGxlZApgYGAKCmBgYHtyIGVjaG89RkFMU0V9CiMgTUlDIEZpdG5lc3MgVHJlZSAodGlwcyBsYWJlbGVkKQpnZ3NhdmUoZmlsZT0iUGVyZmVjdHMvUExPVFMvVHJlZXMvU2hhcmVkL0xpYjE1LkZhc3RUcmVlLjVCQ3MucGVyZmVjdHMuZml0RTAyRDA0LlJFU0lTVEFOQ0UudGlwcy5sYWJlbGxlZC5waHlsdW0ucG5nIiwKICAgICAgIHBsb3Q9dHJlZV9wZXJmZWN0czE2LjE1XzVCQ3NfZ29vZF8wLjV0bXBfZml0bmVzc19sYWJlbGxlZCwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gMTIsIGhlaWdodCA9IDcuNSwgdW5pdHMgPSAiaW4iKQpgYGAKCmBgYHtyfQpwYXRjaDEzIDwtIHRyZWVfcGVyZmVjdHMxNS4xNl81QkNzX2dvb2RfMC41dG1wX2ZpdG5lc3NfbGFiZWxsZWQgfCB0cmVlX3BlcmZlY3RzMTYuMTVfNUJDc19nb29kXzAuNXRtcF9maXRuZXNzX2xhYmVsbGVkCnBhdGNoMTMKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIE1JQyBGaXRuZXNzIFRyZWUgKHRpcHMgbGFiZWxlZCkKZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL1RyZWVzL1NoYXJlZC9MaWIxNS4xNi5GYXN0VHJlZS41QkNzLnBlcmZlY3RzLmZpdEQwN0QwMy5maXRFMDJEMDQuUkVTSVNUQU5DRS5sYWJlbGVkLnBuZyIsCiAgICAgICBwbG90PXBhdGNoMTMsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA3LjUsIHVuaXRzID0gImluIikKYGBgCgojIyMjIDIwMC1UTVAgVHJlZSBQbG90CgoqKkxpYjE1OioqIFNob3cgdGhlIG1pbmltdW0gYW5kIG1heGltdW0gZml0bmVzcyB2YWx1ZXMgYmV0d2VlbiBEMTEgKDIwMCBtZy91TCBEYXkgMSkgdnMuIEQwMyAoTTkgbm8gc3VwcC4pIGluIHRoZSBkYXRhc2V0OgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KbWluKEZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCRmaXREMTFEMDMsbmEucm09VCkKbWF4KEZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCRmaXREMTFEMDMsbmEucm09VCkKYGBgCgoqKkxpYjE1OioqIFBsb3QgYSBjaXJjdWxhciBwaHlsb2dlbmV0aWMgdHJlZSBmb3IgcmVzaXN0YW5jZSB3aXRoIGNvbG9yLXNjYWxlIGJhc2VkIG9uIGZpdG5lc3MgdmFsdWVzIGJldHdlZW4gRDA3IHZzLiBEMDMgZm9yIGVhY2ggREhGUiBob21vbG9nCmBgYHtyfQp0cmVlX3BlcmZlY3RzMTUuMTZfNUJDc19nb29kXzIwMHRtcF9maXRuZXNzX2xhYmVsbGVkIDwtIGdndHJlZShGaXR0cmVlMTUuMTYsIGxheW91dD0iY2lyY3VsYXIiLCBicmFuY2gubGVuZ3RoPSJub25lIikgJTwrJQogIEZpdHRyZWUxNS4xNl90YXhhX21lcmdlZCArCiAgYWVzKGNvbG9yPWZpdEQxMUQwMykgKwogIGdlb21fdGlwcG9pbnQoYWVzKGNvbG9yPWZpdEQxMUQwMyksIHNpemU9MSwgYWxwaGE9MC44KSArCiAgZ2VvbV90aXBsYWIyKGFlcyhjb2xvcj1maXREMTFEMDMsIGxhYmVsPU5DQkkuc3BlY2llcywgYW5nbGU9YW5nbGUpLCBzaXplPTEpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249ImxlZnQiKSArIAogIHNjYWxlX2NvbG91cl9ncmFkaWVudDIoImZpdG5lc3MiLAogICAgICAgICAgICAgICAgICAgICAgICAgbG93PSJibGFjayIsIAogICAgICAgICAgICAgICAgICAgICAgICAgbWlkPSJnb2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgIGhpZ2g9InJlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICBuYS52YWx1ZT0iZ3JheSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBsaW1pdCA9IGMoLTExLDcpKQoKdHJlZV9wZXJmZWN0czE1LjE2XzVCQ3NfZ29vZF8yMDB0bXBfZml0bmVzc19sYWJlbGxlZApgYGAKCmBgYHtyIGVjaG89RkFMU0V9CiMgNDAweCBNSUMgRml0bmVzcyBUcmVlICh0aXBzIGxhYmVsZWQpCmdnc2F2ZShmaWxlPSJQZXJmZWN0cy9QTE9UUy9UcmVlcy9TaGFyZWQvTGliMTUuMTYuRmFzdFRyZWUuNUJDcy5wZXJmZWN0cy5maXREMTFEMDMuNDAweC5NSUMuUkVTSVNUQU5DRS50aXBzLmxhYmVsbGVkLnNvdXJjZS5wbmciLAogICAgICAgcGxvdD10cmVlX3BlcmZlY3RzMTUuMTZfNUJDc19nb29kXzIwMHRtcF9maXRuZXNzX2xhYmVsbGVkLAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNy41LCB1bml0cyA9ICJpbiIpCmBgYAoKKipMaWIxNjoqKiBTaG93IHRoZSBtaW5pbXVtIGFuZCBtYXhpbXVtIGZpdG5lc3MgdmFsdWVzIGJldHdlZW4gRTA2ICgyMDAgbWcvdUwgRGF5IDEpIHZzLiBEMDQgKE05IG5vIHN1cHAuKSBpbiB0aGUgZGF0YXNldDoKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9Cm1pbihGaXR0cmVlMTUuMTZfdGF4YV9tZXJnZWQkZml0RTA2RDA0LG5hLnJtPVQpCm1heChGaXR0cmVlMTUuMTZfdGF4YV9tZXJnZWQkZml0RTA2RDA0LG5hLnJtPVQpCmBgYAoKKipMaWIxNjoqKiBQbG90IGEgY2lyY3VsYXIgcGh5bG9nZW5ldGljIHRyZWUgZm9yIHJlc2lzdGFuY2Ugd2l0aCBjb2xvci1zY2FsZSBiYXNlZCBvbiBmaXRuZXNzIHZhbHVlcyBiZXR3ZWVuIEUwNiB2cy4gRDA0IGZvciBlYWNoIERIRlIgaG9tb2xvZwpgYGB7cn0KdHJlZV9wZXJmZWN0czE2LjE1XzVCQ3NfZ29vZF8yMDB0bXBfZml0bmVzc19sYWJlbGxlZCA8LSBnZ3RyZWUoRml0dHJlZTE1LjE2LCBsYXlvdXQ9ImNpcmN1bGFyIiwgYnJhbmNoLmxlbmd0aD0ibm9uZSIpICU8KyUKICBGaXR0cmVlMTUuMTZfdGF4YV9tZXJnZWQgKwogIGFlcyhjb2xvcj1maXRFMDZEMDQpICsKICBnZW9tX3RpcHBvaW50KGFlcyhjb2xvcj1maXRFMDZEMDQpLCBzaXplPTEsIGFscGhhPTAuOCkgKwogIGdlb21fdGlwbGFiMihhZXMoY29sb3I9Zml0RTA2RDA0LCBsYWJlbD1OQ0JJLnNwZWNpZXMsIGFuZ2xlPWFuZ2xlKSwgc2l6ZT0xKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJyaWdodCIpICsgCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50MigiZml0bmVzcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICBsb3c9ImJsYWNrIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICBtaWQ9ImdvbGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgaGlnaD0icmVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnZhbHVlPSJncmF5IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGxpbWl0ID0gYygtOCw2KSkKCnRyZWVfcGVyZmVjdHMxNi4xNV81QkNzX2dvb2RfMjAwdG1wX2ZpdG5lc3NfbGFiZWxsZWQKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIDQwMHggTUlDIEZpdG5lc3MgVHJlZSAodGlwcyBsYWJlbGVkKQpnZ3NhdmUoZmlsZT0iUGVyZmVjdHMvUExPVFMvVHJlZXMvU2hhcmVkL0xpYjE1LjE2LkZhc3RUcmVlLjVCQ3MucGVyZmVjdHMuZml0RTA2RDA0LjQwMHguTUlDLlJFU0lTVEFOQ0UudGlwcy5sYWJlbGxlZC5zb3VyY2UucG5nIiwKICAgICAgIHBsb3Q9dHJlZV9wZXJmZWN0czE2LjE1XzVCQ3NfZ29vZF8yMDB0bXBfZml0bmVzc19sYWJlbGxlZCwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gMTIsIGhlaWdodCA9IDcuNSwgdW5pdHMgPSAiaW4iKQpgYGAKCmBgYHtyfQpwYXRjaDE0IDwtIHRyZWVfcGVyZmVjdHMxNS4xNl81QkNzX2dvb2RfMjAwdG1wX2ZpdG5lc3NfbGFiZWxsZWQgfCB0cmVlX3BlcmZlY3RzMTYuMTVfNUJDc19nb29kXzIwMHRtcF9maXRuZXNzX2xhYmVsbGVkCnBhdGNoMTQKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIE1JQyBGaXRuZXNzIFRyZWUgKHRpcHMgbGFiZWxlZCkKZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL1RyZWVzL1NoYXJlZC9MaWIxNS4xNi5GYXN0VHJlZS41QkNzLnBlcmZlY3RzLmZpdEQxMUQwMy5maXRFMDZEMDQuNDAweC5NSUMuUkVTSVNUQU5DRS50aXBzLmxhYmVsbGVkLnBoeWx1bS5wbmciLAogICAgICAgcGxvdD1wYXRjaDE0LAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNy41LCB1bml0cyA9ICJpbiIpCmBgYAoKUGxvdCBNSUMgYW5kIDQwMHggTUlDIGZvciBMaWIxNSBzaWRlLWJ5LXNpZGU6CmBgYHtyfQpwYXRjaDE1IDwtIHRyZWVfcGVyZmVjdHMxNS4xNl81QkNzX2dvb2RfMC41dG1wX2ZpdG5lc3NfbGFiZWxsZWQgfCB0cmVlX3BlcmZlY3RzMTYuMTVfNUJDc19nb29kXzIwMHRtcF9maXRuZXNzX2xhYmVsbGVkCnBhdGNoMTUKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIE1JQyBGaXRuZXNzIFRyZWUgKHRpcHMgbGFiZWxlZCkKZ2dzYXZlKGZpbGU9IlBlcmZlY3RzL1BMT1RTL1RyZWVzL1NoYXJlZC9MaWIxNS5GYXN0VHJlZS41QkNzLnBlcmZlY3RzLmZpdEQwN0QwMy5maXREMTFEMDMuTUlDLjQwMHhNSUMuUkVTSVNUQU5DRS50aXBzLmxhYmVsbGVkLnBoeWx1bS5wbmciLAogICAgICAgcGxvdD1wYXRjaDE1LAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNy41LCB1bml0cyA9ICJpbiIpCmBgYAoKIyBTYXZlIFBlcmZlY3RzIEZpbGVzCgpTYXZlIHRoZSBmb3JtYXR0ZWQgcGVyZmVjdHMgZmlsZXMgdG8gaW1wb3J0IGZvciBkb3duc3RyZWFtIGFuYWx5c2VzCmBgYHtyfQojIEJDczE1X21hcCAoNjIuNiBNQikKd3JpdGUuY3N2KEJDczE1X21hcCwgIlBlcmZlY3RzL3BlcmZlY3RzX2ZpbGVzX2Zvcm1hdHRlZC9CQ3MxNV9tYXAuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCgojIEJDczE2X21hcCAoODAuNSBNQikKd3JpdGUuY3N2KEJDczE2X21hcCwgIlBlcmZlY3RzL3BlcmZlY3RzX2ZpbGVzX2Zvcm1hdHRlZC9CQ3MxNl9tYXAuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCgojIyMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgbXV0SURpbmZvMTUgKDE3IE1CKQp3cml0ZS5jc3YobXV0SURpbmZvMTUsICJQZXJmZWN0cy9wZXJmZWN0c19maWxlc19mb3JtYXR0ZWQvbXV0SURpbmZvMTUuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCgojIG11dElEaW5mbzE2ICgxNC43IE1CKQp3cml0ZS5jc3YobXV0SURpbmZvMTYsICJQZXJmZWN0cy9wZXJmZWN0c19maWxlc19mb3JtYXR0ZWQvbXV0SURpbmZvMTYuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCgojIyMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgcGVyZmVjdHMxNV81QkNzICgyNzUgS0IpCndyaXRlLmNzdihwZXJmZWN0czE1XzVCQ3MsICJQZXJmZWN0cy9wZXJmZWN0c19maWxlc19mb3JtYXR0ZWQvcGVyZmVjdHMxNV81QkNzLmNzdiIsIAogICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpCgojIHBlcmZlY3RzMTZfNUJDcyAoMjMxIEtCKQp3cml0ZS5jc3YocGVyZmVjdHMxNl81QkNzLCAiUGVyZmVjdHMvcGVyZmVjdHNfZmlsZXNfZm9ybWF0dGVkL3BlcmZlY3RzMTZfNUJDcy5jc3YiLCAKICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFKQoKIyBwZXJmZWN0c18xNV8xNl81QkNzX3RyZWUgKDMxOSBLQikKd3JpdGUuY3N2KHBlcmZlY3RzXzE1XzE2XzVCQ3NfdHJlZSwgIlBlcmZlY3RzL3BlcmZlY3RzX2ZpbGVzX2Zvcm1hdHRlZC9wZXJmZWN0c18xNV8xNl81QkNzX3RyZWUuY3N2IiwgCiAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkKCiMjIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBvcmdpbmZvICgyLjEgTUIpCndyaXRlLmNzdihvcmdpbmZvLCAiUGVyZmVjdHMvcGVyZmVjdHNfZmlsZXNfZm9ybWF0dGVkL29yZ2luZm8uY3N2IiwgCiAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkKCiMjIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBBbGx0cmVlMTVfdGF4YV9tZXJnZWQgKDYxMSBLQikKd3JpdGUuY3N2KEFsbHRyZWUxNV90YXhhX21lcmdlZCwgIlBlcmZlY3RzL3BlcmZlY3RzX2ZpbGVzX2Zvcm1hdHRlZC9BbGx0cmVlMTVfdGF4YV9tZXJnZWQuY3N2IiwgCiAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkKCiMjIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBCQ2NvbnRyb2xzXzE1XzE2X3NoYXJlZF9tZWRpYW5fV1QgKDQzNSBCeXRlcykKd3JpdGUuY3N2KEJDY29udHJvbHNfMTVfMTZfc2hhcmVkX21lZGlhbl9XVCwgIlBlcmZlY3RzL3BlcmZlY3RzX2ZpbGVzX2Zvcm1hdHRlZC9CQ2NvbnRyb2xzXzE1XzE2X3NoYXJlZF9tZWRpYW5fV1QuY3N2IiwgCiAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkKCiMgQkNjb250cm9sc18xNV8xNl9zaGFyZWRfbWVkaWFuX05lZyAoNjk3IEJ5dGVzKQp3cml0ZS5jc3YoQkNjb250cm9sc18xNV8xNl9zaGFyZWRfbWVkaWFuX05lZywgIlBlcmZlY3RzL3BlcmZlY3RzX2ZpbGVzX2Zvcm1hdHRlZC9CQ2NvbnRyb2xzXzE1XzE2X3NoYXJlZF9tZWRpYW5fTmVnLmNzdiIsIAogICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpCgojIyMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCAoMjU2IEtCKQp3cml0ZS5jc3YoRml0dHJlZTE1Z29vZF90YXhhX21lcmdlZCwgCiAgICAgICAgICAiUGVyZmVjdHMvcGVyZmVjdHNfZmlsZXNfZm9ybWF0dGVkL0ZpdHRyZWUxNWdvb2RfdGF4YV9tZXJnZWQuY3N2IiwgCiAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkKCiMjIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBuY2JpX3RheGEgKDM1My4xIE1CKQp3cml0ZS5jc3YobmNiaV90YXhhLCAKICAgICAgICAgICJQZXJmZWN0cy9wZXJmZWN0c19maWxlc19mb3JtYXR0ZWQvbmNiaV90YXhhLmNzdiIsIAogICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpCmBgYAoKIyBSZXByb2R1Y2liaWxpdHkKClRoZSBzZXNzaW9uIGluZm9ybWF0aW9uIGlzIHByb3ZpZGVkIGZvciBmdWxsIHJlcHJvZHVjaWJpbGl0eS4KYGBge3J9CmRldnRvb2xzOjpzZXNzaW9uX2luZm8oKQpgYGA=