R Notebook: Provides reproducible analysis for Mutant Variants in the following manuscript:

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

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

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

Experiment

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

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

Packages

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

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

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

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

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

Import Data Files

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

### BCs_map------------------------------

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

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

### mutIDinfo------------------------------

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

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

### perfects_5BCs--------------------------

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

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

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

### BCcontrols_shared_median---------------

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

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

### Miscellaneous-------------------------

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

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

Mutants Data Analysis

Fitness vs Distance

This section is based on the R file: “R_change_in_fitness_vs_distance.R”. It determines how fitness changes with mutation distance.

Mutant Summary

The first thing we need to do is summarize the mutant dataset associated with each library. Using the BC_map datasets, summarize the number of unique mutants (mutID) at each sampling condition. Second, we’ll calculate the median number of unique mutants associated with unique homologs at every sampling condition. Then, we’ll calculate the number of raw sequence counts associated with mutants at numerous mutation levels and their percentage of total mutants for each codon version for the following levels: 0 mutants, 1 mutant, 2-5 mutants, 6-50 mutants, 51-100 mutants, 100+ mutants.

Lib15

Unique Mutants: Calculate the number of unique mutants mapped across all nine conditions. Then re-calculate to only include mutants with 1-5 amino acid changes:

# Unique Mutants
length(unique(BCs15_map$mutID[BCs15_map$mutations > 0]))
[1] 59763
# Unique Mutants (with 1-5 mutations)
length(unique(BCs15_map$mutID[BCs15_map$mutations > 0 & BCs15_map$mutations < 6]))
[1] 12274

Mutants per Treatment: Calculate the number of unique mutants (mutID) recovered from each sampling condition:

# Define the treatments
L15.mutID.mutants.treatments <- c("LB", "M9, Full Supplement", "M9, Complementation", "M9, 0.058-TMP", 
                "M9, 0.5-TMP", "M9, 1.0-TMP", "M9, 10-TMP", "M9, 50-TMP", "M9, 200-TMP")

# Calculate unique IDalign counts
L15.mutID.mutants.result <- BCs15_map %>%
  filter(mutations > 0) %>%
  summarise(
    D01 = n_distinct(mutID[!is.na(D01)]),
    D03 = n_distinct(mutID[!is.na(D03)]),
    D05 = n_distinct(mutID[!is.na(D05)]),
    D06 = n_distinct(mutID[!is.na(D06)]),
    D07 = n_distinct(mutID[!is.na(D07)]),
    D08 = n_distinct(mutID[!is.na(D08)]),
    D09 = n_distinct(mutID[!is.na(D09)]),
    D10 = n_distinct(mutID[!is.na(D10)]),
    D11 = n_distinct(mutID[!is.na(D11)]))

# Transform the result to a more readable format
L15.mutID.mutants.result_table <- tibble(
  Treatment = L15.mutID.mutants.treatments,
  `Unique mutID Count` = as.numeric(L15.mutID.mutants.result[1,]))

# Print the table
print(L15.mutID.mutants.result_table, n = Inf)

Median Mutants per Homolog: Calculate the median number of unique mutants (mutID) associated with unique homologs (IDalign) recovered from each sampling condition:

# Define the treatments
L15.mutID.mutants.median.treatments <- c("LB", "M9, Full Supplement", "M9, Complementation", "M9, 0.058-TMP", 
                "M9, 0.5-TMP", "M9, 1.0-TMP", "M9, 10-TMP", "M9, 50-TMP", "M9, 200-TMP")

L15.mutID.mutants.median.sampleID <- c("D01", "D03", "D05", "D06", "D07", "D08", "D09", "D10", "D11")

# Calculate median unique mutID (mutations > 0) per unique IDalign (mutations = 0)
L15.mutID.mutants.median.result <- BCs15_map %>%
  group_by(IDalign) %>%
  summarise(
    D01=if(any(mutations==0 & !is.na(D01))) median(n_distinct(mutID[mutations>0 & !is.na(D01)])) else NA_real_,
    D03=if(any(mutations==0 & !is.na(D03))) median(n_distinct(mutID[mutations>0 & !is.na(D03)])) else NA_real_,
    D05=if(any(mutations==0 & !is.na(D05))) median(n_distinct(mutID[mutations>0 & !is.na(D05)])) else NA_real_,
    D06=if(any(mutations==0 & !is.na(D06))) median(n_distinct(mutID[mutations>0 & !is.na(D06)])) else NA_real_,
    D07=if(any(mutations==0 & !is.na(D07))) median(n_distinct(mutID[mutations>0 & !is.na(D07)])) else NA_real_,
    D08=if(any(mutations==0 & !is.na(D08))) median(n_distinct(mutID[mutations>0 & !is.na(D08)])) else NA_real_,
    D09=if(any(mutations==0 & !is.na(D09))) median(n_distinct(mutID[mutations>0 & !is.na(D09)])) else NA_real_,
    D10=if(any(mutations==0 & !is.na(D10))) median(n_distinct(mutID[mutations>0 & !is.na(D10)])) else NA_real_,
    D11=if(any(mutations==0 & !is.na(D11))) median(n_distinct(mutID[mutations>0 & !is.na(D11)])) else NA_real_) %>%
  summarise(across(starts_with("D"), ~median(., na.rm = TRUE)))  # Only summarize D* columns

# Transform the result to a more readable format
L15.mutID.mutants.median.result_table <- tibble(
  SampleID = L15.mutID.mutants.median.sampleID,
  Treatment = L15.mutID.mutants.median.treatments,
  `Median Unique mutID per Unique IDalign` = as.numeric(L15.mutID.mutants.median.result[1,]))

# Print the table
print(L15.mutID.mutants.median.result_table, n = Inf)

Validate the median mutants (mutID) per homology (IDalign) for Complementation (D05):

# Calculate intermediate results
D05_validate_result <- BCs15_map %>%
  group_by(IDalign) %>%
  summarise(
    D05_mutants_count = sum(mutations > 0 & !is.na(D05)),
    D05_non_mutants_count = sum(mutations == 0 & !is.na(D05)),
    D05_unique_mutID_count = n_distinct(mutID[mutations > 0 & !is.na(D05)]))

# Remove rows where D01_non_mutants_count == 0
D05_filtered <- D05_validate_result %>%
  filter(D05_non_mutants_count > 0)

# Print full results showing median mutID for each IDalign in D05:
print("Full IDalign results for D05:")
[1] "Full IDalign results for D05:"
print(D05_filtered, n = Inf)

# Save a copy as a spreadsheet
write.csv(D05_filtered, "Mutants/OUTPUT/L15.D05.median.mutID.per.IDalign.csv", row.names = FALSE)

# Print summary results of median mutID per IDalign in D05:
print("Summary of filtered results:")
[1] "Summary of filtered results:"
print(summary(D05_filtered))
   IDalign          D05_mutants_count D05_non_mutants_count D05_unique_mutID_count
 Length:932         Min.   :  0.00    Min.   :  1.0         Min.   :  0.00        
 Class :character   1st Qu.:  5.00    1st Qu.:  6.0         1st Qu.:  5.00        
 Mode  :character   Median : 19.50    Median : 24.0         Median : 18.00        
                    Mean   : 35.55    Mean   : 63.1         Mean   : 32.01        
                    3rd Qu.: 50.25    3rd Qu.: 82.5         3rd Qu.: 47.00        
                    Max.   :308.00    Max.   :799.0         Max.   :246.00        
# Calculate median using the filtered data
median_mutants_D05 <- median(D05_filtered$D05_unique_mutID_count, na.rm = TRUE)

print(paste("Median number of unique mutants per IDalign for D05:", median_mutants_D05))
[1] "Median number of unique mutants per IDalign for D05: 18"

Mutant Counts by Distance: Summarize the raw sequence counts across mapped barcodes at numerous mutation levels:

# Define the columns we want to summarize
L15.columns_to_summarize <- c("D01", "D03", "D05", "D06", "D07", "D08", "D09", "D10", "D11")

# Create a function to sum values for multiple columns
L15.sum_columns <- function(data, condition_name) {
  data %>%
    summarise(across(all_of(L15.columns_to_summarize), ~sum(., na.rm = TRUE))) %>%
    mutate(condition = condition_name) %>%
    select(condition, everything())
}

# Sum values for each condition
L15.summary_all <- bind_rows(
  BCs15_map %>% filter(mutations == 0) %>% L15.sum_columns("mutations == 0"),
  BCs15_map %>% filter(mutations == 1) %>% L15.sum_columns("mutations == 1"),
  BCs15_map %>% filter(mutations >= 2 & mutations <= 5) %>% L15.sum_columns("mutations 2-5"),
  BCs15_map %>% filter(mutations >= 6 & mutations <= 50) %>% L15.sum_columns("mutations 6-50"),
  BCs15_map %>% filter(mutations >= 51 & mutations <= 100) %>% L15.sum_columns("mutations 51-100"),
  BCs15_map %>% filter(mutations > 100) %>% L15.sum_columns("mutations > 100")
)

# Add a total row to the sum table
L15.summary_all_with_total <- L15.summary_all %>%
  bind_rows(summarise(., across(where(is.numeric), sum), condition = "Total"))

# Calculate the percentage of total sum for each column
L15.summary_percentage <- L15.summary_all %>%
  mutate(across(all_of(L15.columns_to_summarize), 
                ~. / sum(., na.rm = TRUE) * 100, 
                .names = "{col}_pct"))

# Add a total row to the percentage table (will sum to 100 for each column)
L15.summary_percentage_with_total <- L15.summary_percentage %>%
  select(condition, ends_with("_pct")) %>%
  bind_rows(summarise(., across(where(is.numeric), sum), condition = "Total"))

# Round the values for better readability
L15.summary_all_rounded <- L15.summary_all_with_total %>%
  mutate(across(where(is.numeric), ~round(., 2)))

L15.summary_percentage_rounded <- L15.summary_percentage_with_total %>%
  mutate(across(where(is.numeric), ~round(., 2)))

# Print the sum table
cat("Table 1: Sum of values for each condition\n")
Table 1: Sum of values for each condition
print(L15.summary_all_rounded, n = Inf, width = Inf)

# Print the percentage table
cat("\nTable 2: Percentage of total sum for each condition\n")

Table 2: Percentage of total sum for each condition
print(L15.summary_percentage_rounded, n = Inf, width = Inf)

# Optionally, save the tables to CSV files
write.csv(L15.summary_all_rounded, "Mutants/OUTPUT/L15.sum_by_mutations_with_total.csv", row.names = FALSE)
write.csv(L15.summary_percentage_rounded, "Mutants/OUTPUT/L15.percentage_by_mutations_with_total.csv", row.names = FALSE)

Calculate the sum of raw sequence reads for each treatment condition from the original BCs15_map object to verify sum totals in “summary_all_rounded” (above).

# Define the columns we want to summarize
BCs15.columns_to_summarize <- c("D01", "D03", "D05", "D06", "D07", "D08", "D09", "D10", "D11")

# Calculate the sums for each column
BCs15.sums_table <- BCs15_map %>%
  summarise(across(all_of(BCs15.columns_to_summarize), ~sum(., na.rm = TRUE)))

# Convert to a more readable format
BCs15.sums_table_long <- BCs15.sums_table %>%
  pivot_longer(cols = everything(), 
               names_to = "Column", 
               values_to = "Sum")

# Round the sums for better readability
BCs15.sums_table_long$Sum <- round(BCs15.sums_table_long$Sum, 2)

# Print the table
cat("Table: Sums for specified columns in BCs15_map\n")
Table: Sums for specified columns in BCs15_map
print(BCs15.sums_table_long, n = Inf)

Piechart: Plot the percent sums as a pie chart to show distribution of mutations in mapped barcodes:

# Prepare data for the pie chart
L15.pie_data <- L15.summary_percentage_rounded %>%
  filter(condition != "Total") %>%  # Remove the "Total" category
  select(condition, D05_pct) %>%
  arrange(desc(D05_pct))  # Sort in descending order for better visualization

# Ensure condition is a factor with the correct order
L15.mutation_order <- 
  c("mutations == 0", "mutations == 1", "mutations 2-5", "mutations 6-50", "mutations 51-100", "mutations > 100")

L15.pie_data$condition <- factor(L15.pie_data$condition, levels = L15.mutation_order)

# Create labels with percentages
L15.pie_data$label <- paste0(L15.pie_data$condition, " (", round(L15.pie_data$D05_pct, 1), "%)")

# Calculate the positions for the labels
L15.pie_data <- L15.pie_data %>%
  arrange(condition) %>%
  mutate(
    prop = D05_pct / sum(D05_pct),
    ypos = cumsum(prop) - 0.5 * prop,
    label_position = cumsum(prop) - prop / 2
  )

# Create a custom blue color palette
L15.n_colors <- nrow(L15.pie_data)
L15.blue_palette <- colorRampPalette(c("lightblue", "darkblue"))(L15.n_colors)

# Create the pie chart
L15.pie_chart <- ggplot(L15.pie_data, aes(x = 1, y = D05_pct, fill = condition)) +
  geom_col(width = 1) +
  coord_polar(theta = "y", start = 0) +
  labs(title = "Distribution of Mutation Groups \nfor Complementation (Codon 1)",
       fill = "Mutation Group") +
  theme_void() +
  theme(plot.title = element_text(size = 24),
        legend.title = element_text(size = 24),
        legend.text = element_text(size = 22)) +
  scale_fill_manual(values = L15.blue_palette, labels = L15.pie_data$label) +
  scale_y_continuous(labels = percent_format())

# Display the pie chart
print(L15.pie_chart)

Lib16

Unique Mutants: Calculate the number of unique mutants mapped across all nine conditions. Then re-calculate to only include mutants with 1-5 amino acid changes:

# Unique Mutants
length(unique(BCs16_map$mutID[BCs16_map$mutations > 0]))
[1] 49691
# Unique Mutants (with 1-5 mutations)
length(unique(BCs16_map$mutID[BCs16_map$mutations > 0 & BCs16_map$mutations < 6]))
[1] 16060

Mutants per Treatment: Calculate the number of unique mutants (mutID) recovered from each sampling condition:

# Define the treatments
L16.mutID.mutants.treatments <- c("LB", "M9, Full Supplement", "M9, Complementation", "M9, 0.058-TMP", 
                "M9, 0.5-TMP", "M9, 1.0-TMP", "M9, 10-TMP", "M9, 50-TMP", "M9, 200-TMP")

# Calculate unique IDalign counts
L16.mutID.mutants.result <- BCs16_map %>%
  filter(mutations > 0) %>%
  summarise(
    D02 = n_distinct(mutID[!is.na(D02)]),
    D04 = n_distinct(mutID[!is.na(D04)]),
    D12 = n_distinct(mutID[!is.na(D12)]),
    E01 = n_distinct(mutID[!is.na(E01)]),
    E02 = n_distinct(mutID[!is.na(E02)]),
    E03 = n_distinct(mutID[!is.na(E03)]),
    E04 = n_distinct(mutID[!is.na(E04)]),
    E05 = n_distinct(mutID[!is.na(E05)]),
    E06 = n_distinct(mutID[!is.na(E06)]))

# Transform the result to a more readable format
L16.mutID.mutants.result_table <- tibble(
  Treatment = L16.mutID.mutants.treatments,
  `Unique mutID Count` = as.numeric(L16.mutID.mutants.result[1,]))

# Print the table
print(L16.mutID.mutants.result_table, n = Inf)

Median Mutants per Homolog: Calculate the median number of unique mutants (mutID) associated with unique homologs (IDalign) recovered from each sampling condition:

# Define the treatments
L16.mutID.mutants.median.treatments <- c("LB", "M9, Full Supplement", "M9, Complementation", "M9, 0.058-TMP", 
                "M9, 0.5-TMP", "M9, 1.0-TMP", "M9, 10-TMP", "M9, 50-TMP", "M9, 200-TMP")

L16.mutID.mutants.median.sampleID <- c("D02", "D04", "D12", "E01", "E02", "E03", "E04", "E05", "E06")

# Calculate median unique mutID (mutations > 0) per unique IDalign (mutations = 0)
L16.mutID.mutants.median.result <- BCs16_map %>%
  group_by(IDalign) %>%
  summarise(
    D02=if(any(mutations==0 & !is.na(D02))) median(n_distinct(mutID[mutations>0 & !is.na(D02)])) else NA_real_,
    D04=if(any(mutations==0 & !is.na(D04))) median(n_distinct(mutID[mutations>0 & !is.na(D04)])) else NA_real_,
    D12=if(any(mutations==0 & !is.na(D12))) median(n_distinct(mutID[mutations>0 & !is.na(D12)])) else NA_real_,
    E01=if(any(mutations==0 & !is.na(E01))) median(n_distinct(mutID[mutations>0 & !is.na(E01)])) else NA_real_,
    E02=if(any(mutations==0 & !is.na(E02))) median(n_distinct(mutID[mutations>0 & !is.na(E02)])) else NA_real_,
    E03=if(any(mutations==0 & !is.na(E03))) median(n_distinct(mutID[mutations>0 & !is.na(E03)])) else NA_real_,
    E04=if(any(mutations==0 & !is.na(E04))) median(n_distinct(mutID[mutations>0 & !is.na(E04)])) else NA_real_,
    E05=if(any(mutations==0 & !is.na(E05))) median(n_distinct(mutID[mutations>0 & !is.na(E05)])) else NA_real_,
    E06=if(any(mutations==0 & !is.na(E06))) median(n_distinct(mutID[mutations>0 & !is.na(E06)])) else NA_real_) %>%
  summarise(across(starts_with(c("D", "E")), ~median(., na.rm = TRUE)))  # Only summarize D* and E* columns

# Transform the result to a more readable format
L16.mutID.mutants.median.result_table <- tibble(
  SampleID = L16.mutID.mutants.median.sampleID,
  Treatment = L16.mutID.mutants.median.treatments,
  `Median Unique mutID per Unique IDalign` = as.numeric(L16.mutID.mutants.median.result[1,]))

# Print the table
print(L16.mutID.mutants.median.result_table, n = Inf)

Validate the median mutants (mutID) per homology (IDalign) for Complementation (D12):

# Calculate intermediate results
D12_validate_result <- BCs16_map %>%
  group_by(IDalign) %>%
  summarise(
    D12_mutants_count = sum(mutations > 0 & !is.na(D12)),
    D12_non_mutants_count = sum(mutations == 0 & !is.na(D12)),
    D12_unique_mutID_count = n_distinct(mutID[mutations > 0 & !is.na(D12)]))

# Remove rows where D12_non_mutants_count == 0
D12_filtered <- D12_validate_result %>%
  filter(D12_non_mutants_count > 0)

# Print full results showing median mutID for each IDalign in D12:
print("Full IDalign results for D12:")
[1] "Full IDalign results for D12:"
print(D12_filtered, n = Inf)

# Save a copy as a spreadsheet
write.csv(D12_filtered, "Mutants/OUTPUT/L16.D12.median.mutID.per.IDalign.csv", row.names = FALSE)

# Print summary results of median mutID per IDalign in D12:
print("Summary of filtered results:")
[1] "Summary of filtered results:"
print(summary(D12_filtered))
   IDalign          D12_mutants_count D12_non_mutants_count D12_unique_mutID_count
 Length:786         Min.   :   0.00   Min.   :   1.00       Min.   :  0.00        
 Class :character   1st Qu.:   4.00   1st Qu.:   5.25       1st Qu.:  4.00        
 Mode  :character   Median :  20.00   Median :  29.00       Median : 18.00        
                    Mean   :  58.07   Mean   : 113.32       Mean   : 39.82        
                    3rd Qu.:  56.00   3rd Qu.: 135.00       3rd Qu.: 48.00        
                    Max.   :3339.00   Max.   :1653.00       Max.   :396.00        
# Calculate median using the filtered data
median_mutants_D12 <- median(D12_filtered$D12_unique_mutID_count, na.rm = TRUE)

print(paste("Median number of unique mutants per IDalign for D12:", median_mutants_D12))
[1] "Median number of unique mutants per IDalign for D12: 18"

Mutant Counts by Distance: Summarize the raw sequence counts across mapped barcodes at numerous mutation levels:

# Define the columns we want to summarize
L16.columns_to_summarize <- c("D02", "D04", "D12", "E01", "E02", "E03", "E04", "E05", "E06")

# Create a function to sum values for multiple columns
L16.sum_columns <- function(data, condition_name) {
  data %>%
    summarise(across(all_of(L16.columns_to_summarize), ~sum(., na.rm = TRUE))) %>%
    mutate(condition = condition_name) %>%
    select(condition, everything())
}

# Sum values for each condition
L16.summary_all <- bind_rows(
  BCs16_map %>% filter(mutations == 0) %>% L16.sum_columns("mutations == 0"),
  BCs16_map %>% filter(mutations == 1) %>% L16.sum_columns("mutations == 1"),
  BCs16_map %>% filter(mutations >= 2 & mutations <= 5) %>% L16.sum_columns("mutations 2-5"),
  BCs16_map %>% filter(mutations >= 6 & mutations <= 50) %>% L16.sum_columns("mutations 6-50"),
  BCs16_map %>% filter(mutations >= 51 & mutations <= 100) %>% L16.sum_columns("mutations 51-100"),
  BCs16_map %>% filter(mutations > 100) %>% L16.sum_columns("mutations > 100")
)

# Add a total row to the sum table
L16.summary_all_with_total <- L16.summary_all %>%
  bind_rows(summarise(., across(where(is.numeric), sum), condition = "Total"))

# Calculate the percentage of total sum for each column
L16.summary_percentage <- L16.summary_all %>%
  mutate(across(all_of(L16.columns_to_summarize), 
                ~. / sum(., na.rm = TRUE) * 100, 
                .names = "{col}_pct"))

# Add a total row to the percentage table (will sum to 100 for each column)
L16.summary_percentage_with_total <- L16.summary_percentage %>%
  select(condition, ends_with("_pct")) %>%
  bind_rows(summarise(., across(where(is.numeric), sum), condition = "Total"))

# Round the values for better readability
L16.summary_all_rounded <- L16.summary_all_with_total %>%
  mutate(across(where(is.numeric), ~round(., 2)))

L16.summary_percentage_rounded <- L16.summary_percentage_with_total %>%
  mutate(across(where(is.numeric), ~round(., 2)))

# Print the sum table
cat("Table 1: Sum of values for each condition\n")
Table 1: Sum of values for each condition
print(L16.summary_all_rounded, n = Inf, width = Inf)

# Print the percentage table
cat("\nTable 2: Percentage of total sum for each condition\n")

Table 2: Percentage of total sum for each condition
print(L16.summary_percentage_rounded, n = Inf, width = Inf)

# Optionally, save the tables to CSV files
write.csv(L16.summary_all_rounded, "Mutants/OUTPUT/L16.sum_by_mutations_with_total.csv", row.names = FALSE)
write.csv(L16.summary_percentage_rounded, "Mutants/OUTPUT/L16.percentage_by_mutations_with_total.csv", row.names = FALSE)

Calculate the sum of raw sequence reads for each treatment condition from the original BCs16_map object to verify sum totals in “summary_all_rounded” (above).

# Define the columns we want to summarize
BCs16.columns_to_summarize <- c("D02", "D04", "D12", "E01", "E02", "E03", "E04", "E05", "E06")

# Calculate the sums for each column
BCs16.sums_table <- BCs16_map %>%
  summarise(across(all_of(BCs16.columns_to_summarize), ~sum(., na.rm = TRUE)))

# Convert to a more readable format
BCs16.sums_table_long <- BCs16.sums_table %>%
  pivot_longer(cols = everything(), 
               names_to = "Column", 
               values_to = "Sum")

# Round the sums for better readability
BCs16.sums_table_long$Sum <- round(BCs16.sums_table_long$Sum, 2)

# Print the table
cat("Table: Sums for specified columns in BCs16_map\n")
Table: Sums for specified columns in BCs16_map
print(BCs16.sums_table_long, n = Inf)

Piechart: Plot the percent sums as a pie chart to show distribution of mutations in mapped barcodes:

# Prepare data for the pie chart
L16.pie_data <- L16.summary_percentage_rounded %>%
  filter(condition != "Total") %>%  # Remove the "Total" category
  select(condition, D12_pct) %>%
  arrange(desc(D12_pct))  # Sort in descending order for better visualization

# Ensure condition is a factor with the correct order
L16.mutation_order <- 
  c("mutations == 0", "mutations == 1", "mutations 2-5", "mutations 6-50", "mutations 51-100", "mutations > 100")

L16.pie_data$condition <- factor(L16.pie_data$condition, levels = L16.mutation_order)

# Create labels with percentages
L16.pie_data$label <- paste0(L16.pie_data$condition, " (", round(L16.pie_data$D12_pct, 1), "%)")

# Calculate the positions for the labels
L16.pie_data <- L16.pie_data %>%
  arrange(condition) %>%
  mutate(
    prop = D12_pct / sum(D12_pct),
    ypos = cumsum(prop) - 0.5 * prop,
    label_position = cumsum(prop) - prop / 2
  )

# Create a custom blue color palette
L16.n_colors <- nrow(L16.pie_data)
L16.orange_palette <- colorRampPalette(c("orange", "darkorange4"))(L16.n_colors)

# Create the pie chart
L16.pie_chart <- ggplot(L16.pie_data, aes(x = 1, y = D12_pct, fill = condition)) +
  geom_col(width = 1) +
  coord_polar(theta = "y", start = 0) +
  labs(title = "Distribution of Mutation Groups \nfor Complementation (Codon 2)",
       fill = "Mutation Group") +
  theme_void() +
  theme(plot.title = element_text(size = 24),
        legend.title = element_text(size = 24),
        legend.text = element_text(size = 22)) +
  scale_fill_manual(values = L16.orange_palette, labels = L16.pie_data$label) +
  scale_y_continuous(labels = percent_format())

# Display the pie chart
print(L16.pie_chart)

Both Codons

patch1 <- L15.pie_chart | L16.pie_chart
patch1

Plot the two percentage datasets as a boxplot showing differences in mutation groups between codon versions:

# Combine the two dataframes
L15.16.combined.summary_percentage_rounded <- bind_rows(
  L15.summary_percentage_rounded %>% 
    pivot_longer(cols = ends_with("_pct"), names_to = "sample", values_to = "percentage") %>% 
    mutate(group = "Codon1"),
  L16.summary_percentage_rounded %>% 
    pivot_longer(cols = ends_with("_pct"), names_to = "sample", values_to = "percentage") %>% 
    mutate(group = "Codon2")
)

# Ensure the condition column is a factor with levels in the desired order
L15.16.combined.summary_percentage_rounded <- L15.16.combined.summary_percentage_rounded %>%
  filter(condition != "Total") %>%
  mutate(condition = factor(condition, levels = unique(condition)))

# Create a named vector for the new labels
L15.16.new_labels <- c(
  "mutations == 0" = "0",
  "mutations == 1" = "1",
  "mutations 2-5" = "2-5",
  "mutations 6-50" = "6-50",
  "mutations 51-100" = "51-100",
  "mutations > 100" = ">100"
)

# Create barplot of percentages
L15.16.combined.summary_percentage.plot <- 
  ggplot(L15.16.combined.summary_percentage_rounded, aes(x = condition, y = percentage, fill = group)) +
  stat_summary(fun = mean, geom = "bar", position = position_dodge(width = 0.9), width = 0.8) +
  stat_summary(fun.data = mean_se, geom = "errorbar", position = position_dodge(width = 0.9), width = 0.2) +
  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), 
        axis.text.x = element_text(size = 12), 
        axis.text.y = element_text(size = 12), 
        panel.background = element_blank(), 
        axis.title.x = element_text(size = 14), 
        axis.title.y = element_text(size = 14),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        legend.title = element_blank(),
        legend.text = element_text(size = 12),
        legend.position = "bottom") +
  labs(x = "Mutation Distance from Homolog (a.a.)", y = "Median Percentage (%)", fill = "Codon",
       title = "Mean Mutation Percentages for both Codon Versions") +
  scale_fill_manual(values = c("Codon1" = "#0072B2", "Codon2" = "#E69F00")) +  # Custom colors
  coord_cartesian(ylim = c(0, 100)) +  # Set y-axis limits from 0 to 100%
  scale_x_discrete(labels = L15.16.new_labels) +
  scale_y_continuous(expand = c(0, 0), limits = c(0, 100))
Warning: The `size` argument of `element_line()` is deprecated as of ggplot2 3.4.0.
Please use the `linewidth` argument instead.
print(L15.16.combined.summary_percentage.plot)

Plot together:

patch11 <- (L15.pie_chart | L16.pie_chart) / 
           L15.16.combined.summary_percentage.plot +
           plot_layout(heights = c(1, 1))
patch11

Filter Data by Distance

Grab only the BCs with up to 5 mutations (also need to ensure it has greater than 0 mutations since some BCs have negative values):

BCs5_15 <- BCs15_map %>%
  filter(mutations >= 0 & mutations <= 5) %>%
  left_join(mutIDinfo15 %>% select(mutID), by="mutID") %>%
  select(BC,IDalign,mutID,mutations,D05D03fc)

Retain only the homologs with good data (>5BCs):

fitness_distance_15 <- perfects_15_16_5BCs_tree %>%
  select(ID,fitD05D03) %>%
  dplyr::rename(IDalign=ID)

Determine median and sd for 1 mutation:

fitness_distance_15 <- BCs5_15 %>%
    filter(mutations==1) %>%
    group_by(IDalign) %>%
    summarise(mut1fit=median(D05D03fc),
              mut1sd=sd(D05D03fc),
              num1points=n()) %>%
    right_join(fitness_distance_15,by="IDalign")

Determine median and sd for 2 mutation:

fitness_distance_15 <-  BCs5_15 %>%
  filter(mutations==2) %>%
  group_by(IDalign) %>%
  summarise(mut2fit=median(D05D03fc),
            mut2sd=sd(D05D03fc),
            num2points=n()) %>%
  right_join(fitness_distance_15,by="IDalign") 

Determine median and sd for 3 mutations:

fitness_distance_15 <-  BCs5_15 %>%
  filter(mutations==3) %>%
  group_by(IDalign) %>%
  summarise(mut3fit=median(D05D03fc),
            mut3sd=sd(D05D03fc),
            num3points=n()) %>%
  right_join(fitness_distance_15,by="IDalign")

Determine median and sd for 4 mutations:

fitness_distance_15 <-  BCs5_15 %>%
  filter(mutations==4) %>%
  group_by(IDalign) %>%
  summarise(mut4fit=median(D05D03fc),
            mut4sd=sd(D05D03fc),
            num4points=n()) %>%
  right_join(fitness_distance_15,by="IDalign")

Determine median and sd for 5 mutations:

fitness_distance_15 <-  BCs5_15 %>%
  filter(mutations==5) %>%
  group_by(IDalign) %>%
  summarise(mut5fit=median(D05D03fc),
            mut5sd=sd(D05D03fc),
            num5points=n()) %>%
  right_join(fitness_distance_15,by="IDalign")

Determine change in fitness:

fitness_distance_nu_15 <- fitness_distance_15 %>%
  mutate(mut1fitn=(mut1fit-fitD05D03),
         mut2fitn=(mut2fit-fitD05D03),
         mut3fitn=(mut3fit-fitD05D03),
         mut4fitn=(mut4fit-fitD05D03),
         mut5fitn=(mut5fit-fitD05D03),
         mut0fitn=0)

Melt data on number of mutations:

fitness_distance_m_15 <- fitness_distance_nu_15 %>%
  select(IDalign,fitD05D03,mut0fitn,mut1fitn,mut2fitn,mut3fitn,mut4fitn,mut5fitn) %>%
  gather(mutations,fitness,mut0fitn,mut1fitn,mut2fitn,mut3fitn,mut4fitn,mut5fitn)

Replace names with numbers:

fitness_distance_m_15$mutations[which(fitness_distance_m_15$mutations=="mut0fitn")] <- as.numeric(0)
fitness_distance_m_15$mutations[which(fitness_distance_m_15$mutations=="mut1fitn")] <- as.numeric(1)
fitness_distance_m_15$mutations[which(fitness_distance_m_15$mutations=="mut2fitn")] <- as.numeric(2)
fitness_distance_m_15$mutations[which(fitness_distance_m_15$mutations=="mut3fitn")] <- as.numeric(3)
fitness_distance_m_15$mutations[which(fitness_distance_m_15$mutations=="mut4fitn")] <- as.numeric(4)
fitness_distance_m_15$mutations[which(fitness_distance_m_15$mutations=="mut5fitn")] <- as.numeric(5)

Remove those with NA fitness:

fitness_distance_m_15 <- fitness_distance_m_15 %>%
  filter(!is.na(fitness))

Fitness vs. Distance Plots

The first plot version uses traces to display results:

lib15_fit_dist_5muts_line <- ggplot(fitness_distance_m_15, aes(x=mutations, y=fitness, group=IDalign, color=IDalign)) +
  geom_point() +
  geom_line() +
  xlab("Distance from homolog (a.a.)") +
  ylab("Change in fitness relative to homolog") +
  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 = 10),
        axis.text.y = element_text(size = 10),
        axis.title.x = element_text(size = 12),
        axis.title.y = element_text(size = 12),
        panel.background = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        legend.position = "none")

lib15_fit_dist_5muts_line

The second plot version uses a boxplot to display results:

lib15_fit_dist_5muts_boxplot <- ggplot(fitness_distance_m_15, aes(x=mutations, y=fitness)) +
  geom_boxplot(color="black", fill="#0072B2", alpha=0.8) +
  xlab("Distance from homolog (a.a.)") +
  ylab("Change in fitness relative to homolog") +
  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 = 10),
        axis.text.y = element_text(size = 10),
        axis.title.x = element_text(size = 12),
        axis.title.y = element_text(size = 12),
        panel.background = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        legend.position = "none") +
  scale_y_continuous(expand = c(0, 0), limits = c(-10,10))

lib15_fit_dist_5muts_boxplot

Calculate Spearman correlations between fitness and distance from homolog:

# Calculate Spearman coefficient:
cor(as.numeric(fitness_distance_m_15$mutations),fitness_distance_m_15$fitness,
    method=c("spearman"))
[1] -0.2309586
# Run correlation test:
cor.test(as.numeric(fitness_distance_m_15$mutations),fitness_distance_m_15$fitness,
         method=c("spearman"))

    Spearman's rank correlation rho

data:  as.numeric(fitness_distance_m_15$mutations) and fitness_distance_m_15$fitness
S = 175343781, p-value = 5.886e-13
alternative hypothesis: true rho is not equal to 0
sample estimates:
       rho 
-0.2309586 

Plot Mutants per Homolog

This section is based on the R file: “R_plot_all_mutants.R”. It describes how to plot all mutants per homolog independently.

Determine the number of mutants per homolog:

# Lib15
mutantsperhomolog15 <- mutIDinfo15 %>%
  filter(mutations != 0) %>%
  select(mutID,IDalign) %>%
  distinct() %>%
  group_by(IDalign) %>%
  summarise(count=n())

# Lib16
mutantsperhomolog16 <- mutIDinfo16 %>%
  filter(mutations != 0) %>%
  select(mutID,IDalign) %>%
  distinct() %>%
  group_by(IDalign) %>%
  summarise(count=n())

Add a column and label each ID for the library it comes from to keep track of the data source:

# Lib15
mutantsperhomolog15$lib <- "Lib15"

# Lib16
mutantsperhomolog16$lib <- "Lib16"

Combine both library datasets for plotting:

mutantsperhomolog_15_16 <- bind_rows(mutantsperhomolog15, mutantsperhomolog16, .id = "library")

Plot the mutant count for both libraries:

mutantsperhomolog_15_16_plot <- ggplot(mutantsperhomolog_15_16, aes(x = library, y = count, fill = library)) +
  geom_violin(color = "black", alpha = 0.75) +
  xlab("Library") +
  ylab("Mutants per homolog") +
  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 = 10),
    axis.title.x = element_blank(),
    axis.title.y = element_text(size = 12),
    panel.background = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    legend.position = "none") +
  scale_y_continuous(expand = c(0, 0), limits = c(0, 600)) +
  scale_fill_manual(values = c("#0072B2", "#E69F00")) +
  scale_x_discrete(labels = c("Library 15", "Library 16"))

mutantsperhomolog_15_16_plot

Calculate the median mutant counts for each distinct homolog:

#Lib15

#Median mutant count per homolog
median(mutantsperhomolog15$count)
[1] 30
#Lib16

#Median mutant count per homolog
median(mutantsperhomolog16$count)
[1] 22

Calculate the mean mutant counts for each distinct homolog:

#Lib15

#Mean mutant count per homolog
mean(mutantsperhomolog15$count)
[1] 57.40922
#Lib16

#Mean mutant count per homolog
mean(mutantsperhomolog16$count)
[1] 54.78611

If both measures (mean and median) are considerably different, this indicates that the data are skewed (i.e. they are far from being normally distributed) and the MEDIAN generally gives a more appropriate idea of the data distribution.

Homologs w/ Most Mutants

Determine the top 10 homologs with the greatest number of unique mutants. Arrange by counts (greatest to least):

#Lib15
mutantsperhomolog15 <- mutantsperhomolog15 %>%
  arrange(-count)

#Lib16
mutantsperhomolog16 <- mutantsperhomolog16 %>%
  arrange(-count)

Select the top 10 homologs based on greatest mutant counts

ID Align Count Library
WP_004836669 425 Lib15
WP_008976421 401 Lib15
WP_005758378 389 Lib15
WP_002329360 326 Lib15
WP_003776922 317 Lib15
WP_004826920 312 Lib15
WP_009161670 305 Lib15
WP_009778768 305 Lib15
WP_002820451 300 Lib15
WP_002641668 280 Lib15
ID Align Count Library
WP_006784524 514 Lib16
WP_000175745 493 Lib16
WP_008826323 473 Lib16
WP_008823150 472 Lib16
WP_000175741 459 Lib16
WP_004368566 443 Lib16
WP_000637204 415 Lib16
WP_006318421 408 Lib16
WP_002839715 395 Lib16
WP_011275775 376 Lib16

Make keys for plotting:

#Lib15

mutantsperhomolog15$key <- 1:length(mutantsperhomolog15$count)

mutantsperhomolog15_10 <- mutantsperhomolog15 %>%
  filter(key<11)

mutantsdist15 <- mutIDinfo15 %>%
  filter(IDalign %in% mutantsperhomolog15_10$IDalign) %>%
  filter(mutations>-1)

#Lib16

mutantsperhomolog16$key <- 1:length(mutantsperhomolog16$count)

mutantsperhomolog16_10 <- mutantsperhomolog16 %>%
  filter(key<11)

mutantsdist16 <- mutIDinfo16 %>%
  filter(IDalign %in% mutantsperhomolog16_10$IDalign) %>%
  filter(mutations>-1)

Plot the top 10 homologs with the greatest number of mutants. Calculate mean mutants and SD based on total counts:

Lib15_top10_muts <- ggplot(mutantsdist15, aes(x=IDalign, y=mutations))+
  geom_boxplot(color="black", fill="#0072B2", alpha=0.75) +
  ggtitle("Library 15") +
  xlab("") +
  ylab("Distribution of Mutants at Distance (a.a.)") +
  coord_flip() +
  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 = 10),
        axis.text.y = element_text(size = 10),
        axis.title.x = element_text(size = 12),
        axis.title.y = element_text(size = 12),
        panel.background = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        legend.position = "none")

Lib15_top10_muts

Lib16_top10_muts <- ggplot(mutantsdist16, aes(x=IDalign, y=mutations))+
  geom_boxplot(color="black", fill="#E69F00") +
  ggtitle("Library 16") +
  xlab("") +
  ylab("Distribution of Mutants at Distance (a.a.)") +
  coord_flip() +
  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 = 10),
        axis.text.y = element_text(size = 10),
        axis.title.x = element_text(size = 12),
        axis.title.y = element_text(size = 12),
        panel.background = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        legend.position = "none")

Lib16_top10_muts

patch2 <- (Lib15_top10_muts | Lib16_top10_muts)
patch2

Mutant Fitness

Homolog Mutant Counts

Summarize the number of unique “IDalign” recovered in the mutIDinfo object with 0 mutations, 0+1 mutations, 0+1+2 mutations, 0+1+2+3 mutations, 0+1+2+3+4 mutations, and 0+1+2+3+4+5 mutations that can complement DHFR function (fitness > -1). Also, only retain perfects (mutations = 0) if numprunedBCs > 5. Ignore numprunedBCs for mutations = 1,2,3,4,5:

Lib15

# Unique IDalign with 0 mutations for Complementation (fitD05D03)
Lib15.mut.0.result <- mutIDinfo15 %>%
  filter(!is.na(fitD05D03) & 
         fitD05D03 >= -1 & 
         mutations == 0 &
         numprunedBCs >= 5) %>%
  distinct(IDalign) %>%
  nrow()

print(Lib15.mut.0.result)
[1] 416
# Unique IDalign with 0+1 mutations for Complementation (fitD05D03)
Lib15.mut.0.1.result <- mutIDinfo15 %>%
  filter(!is.na(fitD05D03) & 
         fitD05D03 >= -1 & 
         ((mutations == 0 & numprunedBCs >= 5) | mutations == 1)) %>%
  distinct(IDalign) %>%
  nrow()

print(Lib15.mut.0.1.result)
[1] 643
# Unique IDalign with 0+1+2 mutations for Complementation (fitD05D03)
Lib15.mut.0.1.2.result <- mutIDinfo15 %>%
  filter(!is.na(fitD05D03) & 
         fitD05D03 >= -1 & 
         ((mutations == 0 & numprunedBCs >= 5) | 
          mutations == 1 | 
          mutations == 2)) %>%
  distinct(IDalign) %>%
  nrow()

print(Lib15.mut.0.1.2.result)
[1] 665
# Unique IDalign with 0+1+2+3 mutations for Complementation (fitD05D03)
Lib15.mut.0.1.2.3.result <- mutIDinfo15 %>%
  filter(!is.na(fitD05D03) & 
         fitD05D03 >= -1 & 
         ((mutations == 0 & numprunedBCs >= 5) | 
          mutations == 1 | 
          mutations == 2 |
          mutations == 3)) %>%
  distinct(IDalign) %>%
  nrow()

print(Lib15.mut.0.1.2.3.result)
[1] 679
# Unique IDalign with 0+1+2+3+4 mutations for Complementation (fitD05D03)
Lib15.mut.0.1.2.3.4.result <- mutIDinfo15 %>%
  filter(!is.na(fitD05D03) & 
         fitD05D03 >= -1 & 
         ((mutations == 0 & numprunedBCs >= 5) | 
          mutations == 1 | 
          mutations == 2 |
          mutations == 3 |
          mutations == 4)) %>%
  distinct(IDalign) %>%
  nrow()

print(Lib15.mut.0.1.2.3.4.result)
[1] 685
# Unique IDalign with 0+1+2+3+4+5 mutations for Complementation (fitD05D03)
Lib15.mut.0.1.2.3.4.5.result <- mutIDinfo15 %>%
  filter(!is.na(fitD05D03) & 
         fitD05D03 >= -1 & 
         ((mutations == 0 & numprunedBCs >= 5) | 
          mutations == 1 | 
          mutations == 2 |
          mutations == 3 |
          mutations == 4 |
          mutations == 5)) %>%
  distinct(IDalign) %>%
  nrow()

print(Lib15.mut.0.1.2.3.4.5.result)
[1] 688

Lib16

# Unique IDalign with 0 mutations for Complementation (fitD12D04)
Lib16.mut.0.result <- mutIDinfo16 %>%
  filter(!is.na(fitD12D04) & 
         fitD12D04 >= -1 & 
         mutations == 0 &
         numprunedBCs >= 5) %>%
  distinct(IDalign) %>%
  nrow()

print(Lib16.mut.0.result)
[1] 377
# Unique IDalign with 0+1 mutations for Complementation (fitD12D04)
Lib16.mut.0.1.result <- mutIDinfo16 %>%
  filter(!is.na(fitD12D04) & 
         fitD12D04 >= -1 & 
         ((mutations == 0 & numprunedBCs >= 5) | mutations == 1)) %>%
  distinct(IDalign) %>%
  nrow()

print(Lib16.mut.0.1.result)
[1] 568
# Unique IDalign with 0+1+2 mutations for Complementation (fitD12D04)
Lib16.mut.0.1.2.result <- mutIDinfo16 %>%
  filter(!is.na(fitD12D04) & 
         fitD12D04 >= -1 & 
         ((mutations == 0 & numprunedBCs >= 5) | 
          mutations == 1 | 
          mutations == 2)) %>%
  distinct(IDalign) %>%
  nrow()

print(Lib16.mut.0.1.2.result)
[1] 596
# Unique IDalign with 0+1+2+3 mutations for Complementation (fitD12D04)
Lib16.mut.0.1.2.3.result <- mutIDinfo16 %>%
  filter(!is.na(fitD12D04) & 
         fitD12D04 >= -1 & 
         ((mutations == 0 & numprunedBCs >= 5) | 
          mutations == 1 | 
          mutations == 2 |
          mutations == 3)) %>%
  distinct(IDalign) %>%
  nrow()

print(Lib16.mut.0.1.2.3.result)
[1] 601
# Unique IDalign with 0+1+2+3+4 mutations for Complementation (fitD12D04)
Lib16.mut.0.1.2.3.4.result <- mutIDinfo16 %>%
  filter(!is.na(fitD12D04) & 
         fitD12D04 >= -1 & 
         ((mutations == 0 & numprunedBCs >= 5) | 
          mutations == 1 | 
          mutations == 2 |
          mutations == 3 |
          mutations == 4)) %>%
  distinct(IDalign) %>%
  nrow()

print(Lib16.mut.0.1.2.3.4.result)
[1] 602
# Unique IDalign with 0+1+2+3+4+5 mutations for Complementation (fitD12D04)
Lib16.mut.0.1.2.3.4.5.result <- mutIDinfo16 %>%
  filter(!is.na(fitD12D04) & 
         fitD12D04 >= -1 & 
         ((mutations == 0 & numprunedBCs >= 5) | 
          mutations == 1 | 
          mutations == 2 |
          mutations == 3 |
          mutations == 4 |
          mutations == 5)) %>%
  distinct(IDalign) %>%
  nrow()

print(Lib16.mut.0.1.2.3.4.5.result)
[1] 604

Combined Codons

Filter both mutIDinfo datasets to retain only the relevant columns prior to merging:

# Lib15
mutIDinfo15.subset <- mutIDinfo15 %>%
  filter(mutations <= 5,
         fitD05D03 >= -1,
         (mutations != 0 | (mutations == 0 & numprunedBCs >= 5))) %>%
  select(mutID, IDalign, numprunedBCs, mutations, fitD05D03)

# Lib16
mutIDinfo16.subset <- mutIDinfo16 %>%
  filter(mutations <= 5,
         fitD12D04 >= -1,
         (mutations != 0 | (mutations == 0 & numprunedBCs >= 5))) %>%
  select(mutID, IDalign, numprunedBCs, mutations, fitD12D04)

Combine the shared mutIDs between datasets:

mutIDinfo15.16.shared.subset <- inner_join(mutIDinfo15.subset, mutIDinfo16.subset, by = "mutID", suffix = c(".15", ".16"))

Combine the unique mutIDs between datasets:

# Rows unique to mutIDinfo15.subset
mutIDinfo.unique_to_15 <- anti_join(mutIDinfo15.subset, mutIDinfo16.subset, by = "mutID")

# Rows unique to mutIDinfo16.subset
mutIDinfo.unique_to_16 <- anti_join(mutIDinfo16.subset, mutIDinfo15.subset, by = "mutID")

# Combine the unique rows
mutIDinfo.15.16.unique.subset <- bind_rows(
  mutIDinfo.unique_to_15 %>% mutate(source = "Lib15"),
  mutIDinfo.unique_to_16 %>% mutate(source = "Lib16"))

Mutations = 0: Count the number of unique IDalign in the shared and unique datasets:

# Shared: Unique IDalign with 0 mutations for Complementation (fitD05D03 and fitD12D04)
Lib15.16.mut.0.shared.result <- mutIDinfo15.16.shared.subset %>%
  filter(mutations.15 == 0 | mutations.16 == 0) %>%
  distinct(IDalign.15, IDalign.16) %>%
  nrow()

print(Lib15.16.mut.0.shared.result)
[1] 194
# Unique: Unique IDalign with 0 mutations for Complementation (fitD05D03 and fitD12D04)
Lib15.16.mut.0.unique.result <- mutIDinfo.15.16.unique.subset %>%
  filter(mutations == 0) %>%
  distinct(IDalign) %>%
  nrow()

print(Lib15.16.mut.0.unique.result)
[1] 405
# Sum of both Codon version for 0 mutations
Lib15.16.mut.0.shared.unique.result <- Lib15.16.mut.0.shared.result + Lib15.16.mut.0.unique.result

print(Lib15.16.mut.0.shared.unique.result)
[1] 599

Mutations = 0+1: Count the number of unique IDalign in the shared and unique datasets:

# Shared: Unique IDalign with 0 or 1 mutations for Complementation (fitD05D03 and fitD12D04)
Lib15.16.mut.0.1.shared.result <- mutIDinfo15.16.shared.subset %>%
  filter(mutations.15 %in% c(0, 1) | mutations.16 %in% c(0, 1)) %>%
  distinct(IDalign.15, IDalign.16) %>%
  nrow()

print(Lib15.16.mut.0.1.shared.result)
[1] 205
# Unique: Unique IDalign with 0 or 1 mutations for Complementation (fitD05D03 and fitD12D04)
Lib15.16.mut.0.1.unique.result <- mutIDinfo.15.16.unique.subset %>%
  filter(mutations %in% c(0, 1)) %>%
  distinct(IDalign) %>%
  nrow()

print(Lib15.16.mut.0.1.unique.result)
[1] 847
# Sum of both Codon versions for 0 or 1 mutations
Lib15.16.mut.0.1.shared.unique.result <- Lib15.16.mut.0.1.shared.result + Lib15.16.mut.0.1.unique.result

print(Lib15.16.mut.0.1.shared.unique.result)
[1] 1052

Mutations = 0+1+2: Count the number of unique IDalign in the shared and unique datasets:

# Shared: Unique IDalign with 0 or 1 mutations for Complementation (fitD05D03 and fitD12D04)
Lib15.16.mut.0.1.2.shared.result <- mutIDinfo15.16.shared.subset %>%
  filter(mutations.15 %in% c(0, 1, 2) | mutations.16 %in% c(0, 1, 2)) %>%
  distinct(IDalign.15, IDalign.16) %>%
  nrow()

print(Lib15.16.mut.0.1.2.shared.result)
[1] 205
# Unique: Unique IDalign with 0 or 1 mutations for Complementation (fitD05D03 and fitD12D04)
Lib15.16.mut.0.1.2.unique.result <- mutIDinfo.15.16.unique.subset %>%
  filter(mutations %in% c(0, 1, 2)) %>%
  distinct(IDalign) %>%
  nrow()

print(Lib15.16.mut.0.1.2.unique.result)
[1] 877
# Sum of both Codon versions for 0 or 1 mutations
Lib15.16.mut.0.1.2.shared.unique.result <- Lib15.16.mut.0.1.2.shared.result + Lib15.16.mut.0.1.2.unique.result

print(Lib15.16.mut.0.1.2.shared.unique.result)
[1] 1082

Mutations = 0+1+2+3: Count the number of unique IDalign in the shared and unique datasets:

# Shared: Unique IDalign with 0 or 1 mutations for Complementation (fitD05D03 and fitD12D04)
Lib15.16.mut.0.1.2.3.shared.result <- mutIDinfo15.16.shared.subset %>%
  filter(mutations.15 %in% c(0, 1, 2, 3) | mutations.16 %in% c(0, 1, 2, 3)) %>%
  distinct(IDalign.15, IDalign.16) %>%
  nrow()

print(Lib15.16.mut.0.1.2.3.shared.result)
[1] 205
# Unique: Unique IDalign with 0 or 1 mutations for Complementation (fitD05D03 and fitD12D04)
Lib15.16.mut.0.1.2.3.unique.result <- mutIDinfo.15.16.unique.subset %>%
  filter(mutations %in% c(0, 1, 2, 3)) %>%
  distinct(IDalign) %>%
  nrow()

print(Lib15.16.mut.0.1.2.3.unique.result)
[1] 888
# Sum of both Codon versions for 0 or 1 mutations
Lib15.16.mut.0.1.2.3.shared.unique.result <- Lib15.16.mut.0.1.2.3.shared.result + Lib15.16.mut.0.1.2.3.unique.result

print(Lib15.16.mut.0.1.2.3.shared.unique.result)
[1] 1093

Mutations = 0+1+2+3+4: Count the number of unique IDalign in the shared and unique datasets:

# Shared: Unique IDalign with 0 or 1 mutations for Complementation (fitD05D03 and fitD12D04)
Lib15.16.mut.0.1.2.3.4.shared.result <- mutIDinfo15.16.shared.subset %>%
  filter(mutations.15 %in% c(0, 1, 2, 3, 4) | mutations.16 %in% c(0, 1, 2, 3, 4)) %>%
  distinct(IDalign.15, IDalign.16) %>%
  nrow()

print(Lib15.16.mut.0.1.2.3.4.shared.result)
[1] 205
# Unique: Unique IDalign with 0 or 1 mutations for Complementation (fitD05D03 and fitD12D04)
Lib15.16.mut.0.1.2.3.4.unique.result <- mutIDinfo.15.16.unique.subset %>%
  filter(mutations %in% c(0, 1, 2, 3, 4)) %>%
  distinct(IDalign) %>%
  nrow()

print(Lib15.16.mut.0.1.2.3.4.unique.result)
[1] 893
# Sum of both Codon versions for 0 or 1 mutations
Lib15.16.mut.0.1.2.3.4.shared.unique.result <- Lib15.16.mut.0.1.2.3.4.shared.result + Lib15.16.mut.0.1.2.3.4.unique.result

print(Lib15.16.mut.0.1.2.3.4.shared.unique.result)
[1] 1098

Mutations = 0+1+2+3+4+5: Count the number of unique IDalign in the shared and unique datasets:

# Shared: Unique IDalign with 0 or 1 mutations for Complementation (fitD05D03 and fitD12D04)
Lib15.16.mut.0.1.2.3.4.5.shared.result <- mutIDinfo15.16.shared.subset %>%
  filter(mutations.15 %in% c(0, 1, 2, 3, 4, 5) | mutations.16 %in% c(0, 1, 2, 3, 4, 5)) %>%
  distinct(IDalign.15, IDalign.16) %>%
  nrow()

print(Lib15.16.mut.0.1.2.3.4.5.shared.result)
[1] 205
# Unique: Unique IDalign with 0 or 1 mutations for Complementation (fitD05D03 and fitD12D04)
Lib15.16.mut.0.1.2.3.4.5.unique.result <- mutIDinfo.15.16.unique.subset %>%
  filter(mutations %in% c(0, 1, 2, 3, 4, 5)) %>%
  distinct(IDalign) %>%
  nrow()

print(Lib15.16.mut.0.1.2.3.4.5.unique.result)
[1] 894
# Sum of both Codon versions for 0 or 1 mutations
Lib15.16.mut.0.1.2.3.4.5.shared.unique.result <- Lib15.16.mut.0.1.2.3.4.5.shared.result + Lib15.16.mut.0.1.2.3.4.5.unique.result

print(Lib15.16.mut.0.1.2.3.4.5.shared.unique.result)
[1] 1099

Plot Codon Mutants

Organize the codon homolog + mutant counts into a new dataframe:

Lib15.16.homolog.mutants <- tibble(
  Mutations = c(0, 1, 2, 3, 4, 5),
  Codon1 = c(417, 644, 665, 679, 685, 688),
  Codon2 = c(377, 568, 597, 602, 604, 605),
  Both = c(600, 1053, 1082, 1093, 1098, 1099))

# View the data frame
print(Lib15.16.homolog.mutants)
# Reshape the data from wide to long format
Lib15.16.homolog.mutants_long <- Lib15.16.homolog.mutants %>%
  pivot_longer(cols = c(Codon1, Codon2, Both), 
               names_to = "Category", 
               values_to = "Assemblies")

# Set the maximum value for scaling to 1208
max_assemblies <- 1208

# Create the plot with smoothed lines and second y-axis
Lib15.16.homolog.mutants_plot <- ggplot(Lib15.16.homolog.mutants_long,
                                        aes(x = Mutations, y = Assemblies, color = Category)) +
  geom_line(size = 1) +
  geom_point(size = 7) +
  scale_color_manual(values = c("Codon1" = "#0072B2", "Codon2" = "#E69F00", "Both" = "lightblue4"),
                     breaks = c("Codon1", "Codon2", "Both")) +  # This line sets the legend order
  scale_y_continuous(
    name = "Homologs Represented \n(1208 Assembled)", 
    limits = c(0, max_assemblies),
    expand = c(0, 0),
    sec.axis = sec_axis(~ . * 100 / max_assemblies, 
                        name = "Library Representation (%)", 
                        breaks = seq(0, 100, by = 20))
  ) +
  labs(x = "Max. Distance from Homolog (a.a.)",
       color = "Category") +
  theme_minimal() +
  theme(
    axis.title.y.right = element_text(size = 24, color = "red"),
    axis.text.y.right = element_text(size = 21, color = "red"),
    axis.title.x = element_text(size = 21),
    axis.text.x = element_text(size = 21),
    axis.title.y = element_text(size = 24),
    axis.text.y = element_text(size = 21),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    panel.border = element_blank(),
    legend.title = element_blank(),
    legend.text = element_text(size = 20),
    legend.position = "bottom",
    axis.line = element_line(color = "black", size = 1.0),
    axis.ticks = element_line(color = "black", size = 1.0),
    axis.ticks.length = unit(0.2, "cm")
  ) +
  coord_cartesian(ylim = c(0, max_assemblies * 1.05))  # Add 5% padding to the top
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
Please use `linewidth` instead.
# Display the plot
print(Lib15.16.homolog.mutants_plot)

Mutants Only

First, create a mutant object that filters the mutIDinfo object to retain mutIDs only if they have > 0 mutations:

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

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

Add the actual pct_ident to E. coli score from the orginfo object

# Lib15
mutants15 <- merge(mutants15, orginfo[, c("ID", "PctIdentEcoli")], by = "ID", all.x = TRUE)

# Lib16
mutants16 <- merge(mutants16, orginfo[, c("ID", "PctIdentEcoli")], by = "ID", all.x = TRUE)

Count the number of unique mutIDs:

# Lib15
mutants15.count <- length(unique(mutants15$mutID))
format(mutants15.count, big.mark = ",")
[1] "59,763"
# Lib16
mutants16.count <- length(unique(mutants16$mutID))
format(mutants16.count, big.mark = ",")
[1] "49,691"

Now, count the number of unique IDaligns that each mutID is associated with:

# Lib15
mutants15.ID.count <- length(unique(mutants15$ID))
format(mutants15.ID.count, big.mark = ",")
[1] "1,041"
# Lib16
mutants16.ID.count <- length(unique(mutants16$ID))
format(mutants16.ID.count, big.mark = ",")
[1] "907"

Bin mutants by the percent similarity to their designed homologs:

#Lib15
mutants15$identbins <- cut(mutants15$pct_ident,
                         breaks = seq(0,1.005,1/100),
                         labels=as.character(seq(0.005,0.995,1/100)))

#Lib16
mutants16$identbins <- cut(mutants16$pct_ident,
                         breaks = seq(0,1.005,1/100),
                         labels=as.character(seq(0.005,0.995,1/100)))

Determine the minimum percent similarity mutant in the dataset (most different from designed homolog):

#Lib15
min(mutants15$pct_ident)
[1] 0.005847953
#Lib16
min(mutants16$pct_ident)
[1] 0.005847953

Determine the maximum percent similarity mutant in the dataset (most similar to designed homolog):

#Lib15
max(mutants15$pct_ident)
[1] 0.9942857
#Lib16
max(mutants16$pct_ident)
[1] 0.9942857

Calculate the total number of mutants with at least 1 barcode recovered:

#Lib15
mut15.1BCs.count <- nrow(mutants15 %>% filter(numprunedBCs >= 1))
format(mut15.1BCs.count, big.mark = ",")
[1] "59,763"
#Lib16
mut16.1BCs.count <- nrow(mutants16 %>% filter(numprunedBCs >= 1))
format(mut16.1BCs.count, big.mark = ",")
[1] "49,691"

Calculate the total number of mutants with at least 5 barcode recovered:

#Lib15
mut15.5BCs.count <- nrow(mutants15 %>% filter(numprunedBCs >= 5))
format(mut15.5BCs.count, big.mark = ",")
[1] "369"
#Lib16
mut16.5BCs.count <- nrow(mutants16 %>% filter(numprunedBCs >= 5))
format(mut16.5BCs.count, big.mark = ",")
[1] "602"

Histogram Plots

Plot these mutants (>1 BCs recovered) with histograms:

#Lib15
mutant_hist15_plot <- mutants15 %>%
  filter(numprunedBCs >= 1) %>%
  ggplot(aes(x=pct_ident, y=fitD05D03)) +
  labs(x = "Fractional Sequence Identity", y ="Fitness",color="") +
  geom_point(alpha=0.3,color='#0072B2') +
  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 = 10),
        axis.text.y = element_text(size = 10),
        axis.title.x = element_text(size = 12),
        axis.title.y = element_text(size = 12),
        panel.background = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        legend.position = "none")

mutant_hist15_plot2 <- ggMarginal(mutant_hist15_plot, type = "histogram", fill = "#0072B2", bins=40)
mutant_hist15_plot2

#Lib16
mutant_hist16_plot <- mutants16 %>%
  filter(numprunedBCs >= 1) %>%
  ggplot(aes(x=pct_ident, y=fitD12D04)) +
  labs(x = "Fractional Sequence Identity", y ="Fitness",color="") +
  geom_point(alpha=0.3,color='#E69F00') +
  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 = 10),
        axis.text.y = element_text(size = 10),
        axis.title.x = element_text(size = 12),
        axis.title.y = element_text(size = 12),
        panel.background = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        legend.position = "none")

mutant_hist16_plot2 <- ggMarginal(mutant_hist16_plot, type = "histogram", fill = "#E69F00", bins=40)
mutant_hist16_plot2

Boxplots

Plot boxplots based on mutant percent similarity to their designed homologs for fitness between M9 (No Supp) vs. M9 (Full Supp)

#Lib15
mutant_box15_plot <- mutants15 %>%
  filter(numprunedBCs >= 1) %>%
  ggplot(aes(x=as.numeric(as.character(identbins))*100,y=fitD05D03,color=identbins)) +
  labs(x = "Sequence Identity to Homolog (%)", y ="Fitness",color="") +
  ggtitle("Lib15: Complementation") +
  geom_boxplot() +
  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 = 10),
        axis.text.y = element_text(size = 10),
        axis.title.x = element_text(size = 12),
        axis.title.y = element_text(size = 12),
        panel.background = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        legend.position = "none") +
  scale_y_continuous(expand = c(0, 0), limits = c(-10,5))

mutant_box15_plot

#Lib16
mutant_box16_plot <- mutants16 %>%
  filter(numprunedBCs >= 1) %>%
  ggplot(aes(x=as.numeric(as.character(identbins))*100,y=fitD12D04,color=identbins)) +
  labs(x = "Sequence Identity to Homolog (%)", y ="Fitness",color="") +
  ggtitle("Lib16: Complementation") +
  geom_boxplot() +
  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 = 10),
        axis.text.y = element_text(size = 10),
        axis.title.x = element_text(size = 12),
        axis.title.y = element_text(size = 12),
        panel.background = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        legend.position = "none") +
  scale_y_continuous(expand = c(0, 0), limits = c(-10,5))

mutant_box16_plot

patch3 <- (mutant_box15_plot | mutant_box16_plot)
patch3

Plot boxplots based on mutant percent similarity to their designed homologs for fitness between 50 ug/ml TMP

#Lib15
mutant_box15_50tmp <- mutants15 %>%
  filter(numprunedBCs >= 1) %>%
  ggplot(aes(x=as.numeric(as.character(identbins))*100,y=fitD10D03,color=identbins)) +
  labs(x = "Sequence Identity to Homolog (%)", y ="Fitness",color="") +
  ggtitle("Lib15: 50 TMP") +
  geom_boxplot() +
  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 = 10),
        axis.text.y = element_text(size = 10),
        axis.title.x = element_text(size = 12),
        axis.title.y = element_text(size = 12),
        panel.background = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        legend.position = "none") +
  scale_y_continuous(expand = c(0, 0), limits = c(-15,10))

mutant_box15_50tmp

#Lib16
mutant_box16_50tmp <- mutants16 %>%
  filter(numprunedBCs >= 1) %>%
  ggplot(aes(x=as.numeric(as.character(identbins))*100,y=fitE05D04,color=identbins)) +
  labs(x = "Sequence Identity to Homolog (%)", y ="Fitness",color="") +
  ggtitle("Lib16: 50 TMP") +
  geom_boxplot() +
  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 = 10),
        axis.text.y = element_text(size = 10),
        axis.title.x = element_text(size = 12),
        axis.title.y = element_text(size = 12),
        panel.background = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        legend.position = "none") +
  scale_y_continuous(expand = c(0, 0), limits = c(-15,10))

mutant_box16_50tmp

patch4 <- (mutant_box15_50tmp | mutant_box16_50tmp)
patch4

Plot boxplots based on mutant percent similarity to their designed homologs for fitness between 200 ug/ml TMP

#Lib15
mutant_box15_200tmp <- mutants15 %>%
  filter(numprunedBCs >= 1) %>%
  ggplot(aes(x=as.numeric(as.character(identbins))*100,y=fitD11D03,color=identbins)) +
  labs(x = "Sequence Identity to Homolog (%)", y ="Fitness",color="") +
  ggtitle("Lib15: 200 TMP") +
  geom_boxplot() +
  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 = 10),
        axis.text.y = element_text(size = 10),
        axis.title.x = element_text(size = 12),
        axis.title.y = element_text(size = 12),
        panel.background = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        legend.position = "none") +
  scale_y_continuous(expand = c(0, 0), limits = c(-15,10))

mutant_box15_200tmp

#Lib16
mutant_box16_200tmp <- mutants16 %>%
  filter(numprunedBCs >= 1) %>%
  ggplot(aes(x=as.numeric(as.character(identbins))*100,y=fitE06D04,color=identbins)) +
  labs(x = "Sequence Identity to Homolog (%)", y ="Fitness",color="") +
  ggtitle("Lib16: 200 TMP") +
  geom_boxplot() +
  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 = 10),
        axis.text.y = element_text(size = 10),
        axis.title.x = element_text(size = 12),
        axis.title.y = element_text(size = 12),
        panel.background = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        legend.position = "none") +
  scale_y_continuous(expand = c(0, 0), limits = c(-15,10))

mutant_box16_200tmp

patch5 <- (mutant_box15_200tmp | mutant_box16_200tmp)
patch5

Combine them all:

patch6 <- (mutant_box15_plot | mutant_box16_plot) / (mutant_box15_50tmp | mutant_box16_50tmp) / (mutant_box15_200tmp | mutant_box16_200tmp)
patch6

Collapse Mutants

This section is based on the R file: “R_collapse_mutants.R”. The following code describes how to collapse mutant BCs onto their designed homologs within a distance of 5 amino acids.

Lib15

Begin by selecting all mutants within a distance of 5 AA from their designed homologs

mut_collapse_15 <- mutants15 %>%
  filter(mutations >= 0 & mutations < 6) %>%
  group_by(ID)

Merge these mutants with their designed homologs (perfects >5BCs) if they have a matching “ID”. Use the shared perfects mutIDs for merging and downstream library comparisons:

# Add perfects to mut_collapse by shared columns:
mut_collapse_15 <- full_join(mut_collapse_15, perfects15_5BCs)
Joining with `by = join_by(ID, mutID, fitD03D01, fitD05D03, fitD06D03, fitD07D03, fitD08D03, fitD09D03, fitD10D03, fitD11D03, numprunedBCs, numBCs, mutations, seq, pct_ident)`

Only retain mutIDs if the “ID” contains a designed variant (mutations == 0):

mut_collapse_15 <- mut_collapse_15 %>%
  group_by(ID) %>%
  filter(0 %in% mutations) %>%
  ungroup()

Summarize the number of unique “ID” with mutations==0 (perfects) after filtering:

mut_collapse_15.count <- mut_collapse_15 %>%
  filter(mutations == 0) %>%
  summarise(unique_rows = n_distinct(ID))

print(mut_collapse_15.count)

Summarize the number of unique “ID” at each mutation level:

mut_collapse_15.summary <- mut_collapse_15 %>%
  group_by(mutations) %>%
  summarise(unique_IDs = n_distinct(ID))

# View the summary table
print(mut_collapse_15.summary)

Summarize the number of collapsed homologs after filtering

# Count the number of unique designed homologs retained in the filtered dataset:
format(length(unique(mut_collapse_15$ID)), big.mark = ",")
[1] "797"
# Count the number of unique "mutID" after excluding rows with mutations==0 (retains mutants only)
format(length(unique(mut_collapse_15$mutID[mut_collapse_15$mutations != 0])), big.mark = ",")
[1] "12,146"
# Now, count the number of unique "mutID" (this includes designed homologs and their mutants)
format(length(unique(mut_collapse_15$mutID)), big.mark = ",")
[1] "12,943"

Mutation Similarity

Next, run correlation analyses between designed homologs and their corresponding mutant versions (1, 2, 3, 4, or 5 mutations) to determine if mutations share similar fitness values with designed variants

# Lib15

# Filter the dataframe for mutations at each level (0,1,2,3,4,5)
mutations15_0 <- subset(mut_collapse_15, mutations == 0)
mutations15_1 <- subset(mut_collapse_15, mutations == 1)
mutations15_2 <- subset(mut_collapse_15, mutations == 2)
mutations15_3 <- subset(mut_collapse_15, mutations == 3)
mutations15_4 <- subset(mut_collapse_15, mutations == 4)
mutations15_5 <- subset(mut_collapse_15, mutations == 5)

# Merge the dataframes based on shared "ID" for each mutation level against perfects
Mut0vsMut1_15 <- merge(mutations15_0, mutations15_1, by = "ID", suffixes = c("_mutations_0", "_mutations_1"))
Mut0vsMut2_15 <- merge(mutations15_0, mutations15_2, by = "ID", suffixes = c("_mutations_0", "_mutations_2"))
Mut0vsMut3_15 <- merge(mutations15_0, mutations15_3, by = "ID", suffixes = c("_mutations_0", "_mutations_3"))
Mut0vsMut4_15 <- merge(mutations15_0, mutations15_4, by = "ID", suffixes = c("_mutations_0", "_mutations_4"))
Mut0vsMut5_15 <- merge(mutations15_0, mutations15_5, by = "ID", suffixes = c("_mutations_0", "_mutations_5"))

# Subset relevant data columns and remove rows containing "NA" values
Mut0vsMut1_15 <- Mut0vsMut1_15[, c("ID", "numprunedBCs_mutations_0", "numprunedBCs_mutations_1", "fitD05D03_mutations_0", "fitD05D03_mutations_1")] %>% na.omit(Mut0vsMut1_15)
Mut0vsMut2_15 <- Mut0vsMut2_15[, c("ID", "numprunedBCs_mutations_0", "numprunedBCs_mutations_2", "fitD05D03_mutations_0", "fitD05D03_mutations_2")] %>% na.omit(Mut0vsMut2_15)
Mut0vsMut3_15 <- Mut0vsMut3_15[, c("ID", "numprunedBCs_mutations_0", "numprunedBCs_mutations_3", "fitD05D03_mutations_0", "fitD05D03_mutations_3")] %>% na.omit(Mut0vsMut3_15)
Mut0vsMut4_15 <- Mut0vsMut4_15[, c("ID", "numprunedBCs_mutations_0", "numprunedBCs_mutations_4", "fitD05D03_mutations_0", "fitD05D03_mutations_4")] %>% na.omit(Mut0vsMut4_15)
Mut0vsMut5_15 <- Mut0vsMut5_15[, c("ID", "numprunedBCs_mutations_0", "numprunedBCs_mutations_5", "fitD05D03_mutations_0", "fitD05D03_mutations_5")] %>% na.omit(Mut0vsMut5_15)

# Calculate correlation and p-value
cor_test15_Mut0vsMut1 <- cor.test(Mut0vsMut1_15$fitD05D03_mutations_0, Mut0vsMut1_15$fitD05D03_mutations_1)
cor_test15_Mut0vsMut2 <- cor.test(Mut0vsMut2_15$fitD05D03_mutations_0, Mut0vsMut2_15$fitD05D03_mutations_2)
cor_test15_Mut0vsMut3 <- cor.test(Mut0vsMut3_15$fitD05D03_mutations_0, Mut0vsMut3_15$fitD05D03_mutations_3)
cor_test15_Mut0vsMut4 <- cor.test(Mut0vsMut4_15$fitD05D03_mutations_0, Mut0vsMut4_15$fitD05D03_mutations_4)
cor_test15_Mut0vsMut5 <- cor.test(Mut0vsMut5_15$fitD05D03_mutations_0, Mut0vsMut5_15$fitD05D03_mutations_5)

cor_test15_Mut0vsMut1

    Pearson's product-moment correlation

data:  Mut0vsMut1_15$fitD05D03_mutations_0 and Mut0vsMut1_15$fitD05D03_mutations_1
t = 67.797, df = 6501, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.6291126 0.6575964
sample estimates:
      cor 
0.6435773 
cor_test15_Mut0vsMut2

    Pearson's product-moment correlation

data:  Mut0vsMut2_15$fitD05D03_mutations_0 and Mut0vsMut2_15$fitD05D03_mutations_2
t = 21.559, df = 1114, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.4998110 0.5827073
sample estimates:
      cor 
0.5425788 
cor_test15_Mut0vsMut3

    Pearson's product-moment correlation

data:  Mut0vsMut3_15$fitD05D03_mutations_0 and Mut0vsMut3_15$fitD05D03_mutations_3
t = 9.7447, df = 417, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.3492868 0.5056153
sample estimates:
     cor 
0.430676 
cor_test15_Mut0vsMut4

    Pearson's product-moment correlation

data:  Mut0vsMut4_15$fitD05D03_mutations_0 and Mut0vsMut4_15$fitD05D03_mutations_4
t = 7.4551, df = 270, p-value = 1.224e-12
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.3094384 0.5071805
sample estimates:
     cor 
0.413168 
cor_test15_Mut0vsMut5

    Pearson's product-moment correlation

data:  Mut0vsMut5_15$fitD05D03_mutations_0 and Mut0vsMut5_15$fitD05D03_mutations_5
t = 3.4306, df = 217, p-value = 0.0007209
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.09716059 0.34889530
sample estimates:
      cor 
0.2268127 

Plot Correlations

# Extract correlation value from cor_result15_Mut0vsMut1 object
cor_value_Mut0vsMut1 <- cor_test15_Mut0vsMut1$estimate
cor_value_Mut0vsMut2 <- cor_test15_Mut0vsMut2$estimate
cor_value_Mut0vsMut3 <- cor_test15_Mut0vsMut3$estimate
cor_value_Mut0vsMut4 <- cor_test15_Mut0vsMut4$estimate
cor_value_Mut0vsMut5 <- cor_test15_Mut0vsMut5$estimate


# Format p-value in scientific notation
p_value_scientific15_v1 <- format(cor_test15_Mut0vsMut1$p.value, scientific = TRUE, digits = 4)
p_value_scientific15_v2 <- format(cor_test15_Mut0vsMut2$p.value, scientific = TRUE, digits = 4)
p_value_scientific15_v3 <- format(cor_test15_Mut0vsMut3$p.value, scientific = TRUE, digits = 4)
p_value_scientific15_v4 <- format(cor_test15_Mut0vsMut4$p.value, scientific = TRUE, digits = 4)
p_value_scientific15_v5 <- format(cor_test15_Mut0vsMut5$p.value, scientific = TRUE, digits = 4)

# Extract number of rows
num_rows15.mut0v1 <- nrow(Mut0vsMut1_15)
num_rows15.mut0v2 <- nrow(Mut0vsMut2_15)
num_rows15.mut0v3 <- nrow(Mut0vsMut3_15)
num_rows15.mut0v4 <- nrow(Mut0vsMut4_15)
num_rows15.mut0v5 <- nrow(Mut0vsMut5_15)

# Plot the correlation (Mut0vsMut1)
mut0v1_15plot <- ggplot(Mut0vsMut1_15, 
             aes(x = fitD05D03_mutations_0, y = fitD05D03_mutations_1, color=numprunedBCs_mutations_0)) +
  labs(x = "fitD05D03 (mutations = 0)",
       y = "fitD05D03 (mutations = 1)", color="",
       title = "Lib15 Complementation (Mut=0 vs Mut=1)") +
  geom_smooth(method=lm,colour="black") +
  geom_density2d(colour="black",alpha=0.2) +
  geom_point(alpha=0.7) +
  scale_colour_gradient2("BCs",low="blue", high="red", mid="blue") +
  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 = 10),
        axis.text.y = element_text(size = 10),
        axis.title.x = element_text(size = 12),
        axis.title.y = element_text(size = 12),
        panel.background = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        legend.position="left") +
  annotate("text", x = max(Mut0vsMut1_15$fitD05D03_mutations_0), y = min(Mut0vsMut1_15$fitD05D03_mutations_1), 
           label = paste("p-value =", p_value_scientific15_v1), hjust = 1, vjust = 0) +
  annotate("text", x = max(Mut0vsMut1_15$fitD05D03_mutations_0), y = min(Mut0vsMut1_15$fitD05D03_mutations_1),
            label = paste("Correlation =", round(cor_value_Mut0vsMut1, 2)), hjust = 1, vjust = -1.5) +
  annotate("text", x = min(Mut0vsMut1_15$fitD05D03_mutations_0), y = max(Mut0vsMut1_15$fitD05D03_mutations_1),
           label = paste("Mutants =", num_rows15.mut0v1), hjust = 0, vjust = 1.5)

mut0v1_15plot2 <- ggMarginal(mut0v1_15plot, type = "histogram", fill = "#0072B2", alpha=0.75) #add side histograms
`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'
mut0v1_15plot2


# Plot the correlation (Mut0vsMut2)
mut0v2_15plot <- ggplot(Mut0vsMut2_15, 
             aes(x = fitD05D03_mutations_0, y = fitD05D03_mutations_2, color=numprunedBCs_mutations_0)) +
  labs(x = "fitD05D03 (mutations = 0)",
       y = "fitD05D03 (mutations = 2)", color="",
       title = "Lib15 Complementation (Mut=0 vs Mut=2)") +
  geom_smooth(method=lm,colour="black") +
  geom_density2d(colour="black",alpha=0.2) +
  geom_point(alpha=0.7) +
  scale_colour_gradient2("BCs",low="blue", high="red", mid="blue") +
  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 = 10),
        axis.text.y = element_text(size = 10),
        axis.title.x = element_text(size = 12),
        axis.title.y = element_text(size = 12),
        panel.background = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        legend.position="left") +
  annotate("text", x = max(Mut0vsMut2_15$fitD05D03_mutations_0), y = min(Mut0vsMut2_15$fitD05D03_mutations_2), 
           label = paste("p-value =", p_value_scientific15_v2), hjust = 1, vjust = 0)+
  annotate("text", x = max(Mut0vsMut2_15$fitD05D03_mutations_0), y = min(Mut0vsMut2_15$fitD05D03_mutations_2),
            label = paste("Correlation =", round(cor_value_Mut0vsMut2, 2)), hjust = 1, vjust = -1.5) +
  annotate("text", x = min(Mut0vsMut2_15$fitD05D03_mutations_0), y = max(Mut0vsMut2_15$fitD05D03_mutations_2),
           label = paste("Mutants =", num_rows15.mut0v2), hjust = 0, vjust = 1.5)

mut0v2_15plot2 <- ggMarginal(mut0v2_15plot, type = "histogram", fill = "#0072B2", alpha=0.75) #add side histograms
`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'
mut0v2_15plot2


# Plot the correlation (Mut0vsMut3)
mut0v3_15plot <- ggplot(Mut0vsMut3_15, 
             aes(x = fitD05D03_mutations_0, y = fitD05D03_mutations_3, color=numprunedBCs_mutations_0)) +
  labs(x = "fitD05D03 (mutations = 0)",
       y = "fitD05D03 (mutations = 3)", color="",
       title = "Lib15 Complementation (Mut=0 vs Mut=3)") +
  geom_smooth(method=lm,colour="black") +
  geom_density2d(colour="black",alpha=0.2) +
  geom_point(alpha=0.7) +
  scale_colour_gradient2("BCs",low="blue", high="red", mid="blue") +
  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 = 10),
        axis.text.y = element_text(size = 10),
        axis.title.x = element_text(size = 12),
        axis.title.y = element_text(size = 12),
        panel.background = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        legend.position="left") +
  annotate("text", x = max(Mut0vsMut3_15$fitD05D03_mutations_0), y = min(Mut0vsMut3_15$fitD05D03_mutations_3), 
           label = paste("p-value =", p_value_scientific15_v3), hjust = 1, vjust = 0)+
  annotate("text", x = max(Mut0vsMut3_15$fitD05D03_mutations_0), y = min(Mut0vsMut3_15$fitD05D03_mutations_3),
            label = paste("Correlation =", round(cor_value_Mut0vsMut3, 2)), hjust = 1, vjust = -1.5) +
  annotate("text", x = min(Mut0vsMut3_15$fitD05D03_mutations_0), y = max(Mut0vsMut3_15$fitD05D03_mutations_3),
           label = paste("Mutants =", num_rows15.mut0v3), hjust = 0, vjust = 1.5)

mut0v3_15plot2 <- ggMarginal(mut0v3_15plot, type = "histogram", fill = "#0072B2", alpha=0.75) #add side histograms
`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'
mut0v3_15plot2


# Plot the correlation (Mut0vsMut4)
mut0v4_15plot <- ggplot(Mut0vsMut4_15, 
             aes(x = fitD05D03_mutations_0, y = fitD05D03_mutations_4, color=numprunedBCs_mutations_0)) +
  labs(x = "fitD05D03 (mutations = 0)",
       y = "fitD05D03 (mutations = 4)", color="",
       title = "Lib15 Complementation (Mut=0 vs Mut=4)") +
  geom_smooth(method=lm,colour="black") +
  geom_density2d(colour="black",alpha=0.2) +
  geom_point(alpha=0.7) +
  scale_colour_gradient2("BCs",low="blue", high="red", mid="blue") +
  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 = 10),
        axis.text.y = element_text(size = 10),
        axis.title.x = element_text(size = 12),
        axis.title.y = element_text(size = 12),
        panel.background = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        legend.position="left") +
  annotate("text", x = max(Mut0vsMut4_15$fitD05D03_mutations_0), y = min(Mut0vsMut4_15$fitD05D03_mutations_4), 
           label = paste("p-value =", p_value_scientific15_v4), hjust = 1, vjust = 0)+
  annotate("text", x = max(Mut0vsMut4_15$fitD05D03_mutations_0), y = min(Mut0vsMut4_15$fitD05D03_mutations_4),
            label = paste("Correlation =", round(cor_value_Mut0vsMut4, 2)), hjust = 1, vjust = -1.5) +
  annotate("text", x = min(Mut0vsMut4_15$fitD05D03_mutations_0), y = max(Mut0vsMut4_15$fitD05D03_mutations_4),
           label = paste("Mutants =", num_rows15.mut0v4), hjust = 0, vjust = 1.5)

mut0v4_15plot2 <- ggMarginal(mut0v4_15plot, type = "histogram", fill = "#0072B2", alpha=0.75) #add side histograms
`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'
mut0v4_15plot2


# Plot the correlation (Mut0vsMut5)
mut0v5_15plot <- ggplot(Mut0vsMut5_15, 
             aes(x = fitD05D03_mutations_0, y = fitD05D03_mutations_5, color=numprunedBCs_mutations_0)) +
  labs(x = "fitD05D03 (mutations = 0)",
       y = "fitD05D03 (mutations = 5)", color="",
       title = "Lib15 Complementation (Mut=0 vs Mut=5)") +
  geom_smooth(method=lm,colour="black") +
  geom_density2d(colour="black",alpha=0.2) +
  geom_point(alpha=0.7) +
  scale_colour_gradient2("BCs",low="blue", high="red", mid="blue") +
  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 = 10),
        axis.text.y = element_text(size = 10),
        axis.title.x = element_text(size = 12),
        axis.title.y = element_text(size = 12),
        panel.background = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        legend.position="left") +
  annotate("text", x = max(Mut0vsMut5_15$fitD05D03_mutations_0), y = min(Mut0vsMut5_15$fitD05D03_mutations_5), 
           label = paste("p-value =", p_value_scientific15_v5), hjust = 1, vjust = 0)+
  annotate("text", x = max(Mut0vsMut5_15$fitD05D03_mutations_0), y = min(Mut0vsMut5_15$fitD05D03_mutations_5),
            label = paste("Correlation =", round(cor_value_Mut0vsMut5, 2)), hjust = 1, vjust = -1.5) +
  annotate("text", x = min(Mut0vsMut5_15$fitD05D03_mutations_0), y = max(Mut0vsMut5_15$fitD05D03_mutations_5),
           label = paste("Mutants =", num_rows15.mut0v5), hjust = 0, vjust = 1.5)

mut0v5_15plot2 <- ggMarginal(mut0v5_15plot, type = "histogram", fill = "#0072B2", alpha=0.75) #add side histograms
`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'
mut0v5_15plot2

Lib16

Begin by selecting all mutants within a distance of 5 AA from their designed homologs

mut_collapse_16 <- mutants16 %>%
  filter(mutations >= 0 & mutations < 6) %>%
  group_by(ID)

Merge these mutants with their designed homologs (perfects) if they have a matching “ID”

# Add perfects to mut_collapse by shared columns:
mut_collapse_16 <- full_join(mut_collapse_16, perfects16_5BCs)
Joining with `by = join_by(ID, mutID, fitD04D02, fitD12D04, fitE01D04, fitE02D04, fitE03D04, fitE04D04, fitE05D04, fitE06D04, numprunedBCs, numBCs, mutations, seq, pct_ident)`

Only retain mutIDs if the “ID” contains a designed variant (mutations == 0):

mut_collapse_16 <- mut_collapse_16 %>%
  group_by(ID) %>%
  filter(0 %in% mutations) %>%
  ungroup()

Summarize the number of unique “ID” with mutations==0 (perfects) after filtering:

mut_collapse_16.count <- mut_collapse_16 %>%
  filter(mutations == 0) %>%
  summarise(unique_rows = n_distinct(ID))

print(mut_collapse_16.count)

Summarize the number of unique “ID” at each mutation level:

mut_collapse_16.summary <- mut_collapse_16 %>%
  group_by(mutations) %>%
  summarise(unique_IDs = n_distinct(ID))

# View the summary table
print(mut_collapse_16.summary)

Summarize the number of collapsed homologs after filtering

# Count the number of unique designed homologs retained in the filtered dataset:
format(length(unique(mut_collapse_16$ID)), big.mark = ",")
[1] "666"
# Count the number of unique "mutID" after excluding rows with mutations==0 (retains mutants only)
format(length(unique(mut_collapse_16$mutID[mut_collapse_15$mutations != 0])), big.mark = ",")
[1] "15,801"
# Now, count the number of unique "mutID" (this includes designed homologs and their mutants)
format(length(unique(mut_collapse_16$mutID)), big.mark = ",")
[1] "16,598"

Mutation Similarity

Next, run correlation analyses between designed homologs and their corresponding mutant versions (1, 2, or 3 mutations) to determine if mutations share similar fitness values with designed variants

# Lib16

# Filter the dataframe for perfects = 0 and subsequent mutations = 1,2,3,4,5
mutations16_0 <- subset(mut_collapse_16, mutations == 0)
mutations16_1 <- subset(mut_collapse_16, mutations == 1)
mutations16_2 <- subset(mut_collapse_16, mutations == 2)
mutations16_3 <- subset(mut_collapse_16, mutations == 3)
mutations16_4 <- subset(mut_collapse_16, mutations == 4)
mutations16_5 <- subset(mut_collapse_16, mutations == 5)

# Merge the dataframes based on shared "ID"
Mut0vsMut1_16 <- merge(mutations16_0, mutations16_1, by = "ID", suffixes = c("_mutations_0", "_mutations_1"))
Mut0vsMut2_16 <- merge(mutations16_0, mutations16_2, by = "ID", suffixes = c("_mutations_0", "_mutations_2"))
Mut0vsMut3_16 <- merge(mutations16_0, mutations16_3, by = "ID", suffixes = c("_mutations_0", "_mutations_3"))
Mut0vsMut4_16 <- merge(mutations16_0, mutations16_4, by = "ID", suffixes = c("_mutations_0", "_mutations_4"))
Mut0vsMut5_16 <- merge(mutations16_0, mutations16_5, by = "ID", suffixes = c("_mutations_0", "_mutations_5"))

# Subset relevant data columns and remove rows containing "NA" values
Mut0vsMut1_16 <- Mut0vsMut1_16[, c("ID", "numprunedBCs_mutations_0", "numprunedBCs_mutations_1", "fitD12D04_mutations_0", "fitD12D04_mutations_1")] %>% na.omit(Mut0vsMut1_16)
Mut0vsMut2_16 <- Mut0vsMut2_16[, c("ID", "numprunedBCs_mutations_0", "numprunedBCs_mutations_2", "fitD12D04_mutations_0", "fitD12D04_mutations_2")] %>% na.omit(Mut0vsMut2_16)
Mut0vsMut3_16 <- Mut0vsMut3_16[, c("ID", "numprunedBCs_mutations_0", "numprunedBCs_mutations_3", "fitD12D04_mutations_0", "fitD12D04_mutations_3")] %>% na.omit(Mut0vsMut3_16)
Mut0vsMut4_16 <- Mut0vsMut4_16[, c("ID", "numprunedBCs_mutations_0", "numprunedBCs_mutations_4", "fitD12D04_mutations_0", "fitD12D04_mutations_4")] %>% na.omit(Mut0vsMut4_16)
Mut0vsMut5_16 <- Mut0vsMut5_16[, c("ID", "numprunedBCs_mutations_0", "numprunedBCs_mutations_5", "fitD12D04_mutations_0", "fitD12D04_mutations_5")] %>% na.omit(Mut0vsMut5_16)


# Calculate correlation and p-value
cor_test16_Mut0vsMut1 <- cor.test(Mut0vsMut1_16$fitD12D04_mutations_0, Mut0vsMut1_16$fitD12D04_mutations_1)
cor_test16_Mut0vsMut2 <- cor.test(Mut0vsMut2_16$fitD12D04_mutations_0, Mut0vsMut2_16$fitD12D04_mutations_2)
cor_test16_Mut0vsMut3 <- cor.test(Mut0vsMut3_16$fitD12D04_mutations_0, Mut0vsMut3_16$fitD12D04_mutations_3)
cor_test16_Mut0vsMut4 <- cor.test(Mut0vsMut4_16$fitD12D04_mutations_0, Mut0vsMut4_16$fitD12D04_mutations_4)
cor_test16_Mut0vsMut5 <- cor.test(Mut0vsMut5_16$fitD12D04_mutations_0, Mut0vsMut5_16$fitD12D04_mutations_5)

cor_test16_Mut0vsMut1

    Pearson's product-moment correlation

data:  Mut0vsMut1_16$fitD12D04_mutations_0 and Mut0vsMut1_16$fitD12D04_mutations_1
t = 83.548, df = 9512, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.6388313 0.6620144
sample estimates:
      cor 
0.6505744 
cor_test16_Mut0vsMut2

    Pearson's product-moment correlation

data:  Mut0vsMut2_16$fitD12D04_mutations_0 and Mut0vsMut2_16$fitD12D04_mutations_2
t = 28.163, df = 1569, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.5456394 0.6113937
sample estimates:
      cor 
0.5794587 
cor_test16_Mut0vsMut3

    Pearson's product-moment correlation

data:  Mut0vsMut3_16$fitD12D04_mutations_0 and Mut0vsMut3_16$fitD12D04_mutations_3
t = 14.016, df = 569, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.4429621 0.5651627
sample estimates:
      cor 
0.5066023 
cor_test16_Mut0vsMut4

    Pearson's product-moment correlation

data:  Mut0vsMut4_16$fitD12D04_mutations_0 and Mut0vsMut4_16$fitD12D04_mutations_4
t = 13.592, df = 317, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.5324782 0.6718518
sample estimates:
      cor 
0.6068086 
cor_test16_Mut0vsMut5

    Pearson's product-moment correlation

data:  Mut0vsMut5_16$fitD12D04_mutations_0 and Mut0vsMut5_16$fitD12D04_mutations_5
t = 7.3086, df = 213, p-value = 5.355e-12
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.3339752 0.5486988
sample estimates:
      cor 
0.4477694 

Plot Correlations

# Extract correlation value from cor_result16_Mut0vsMut1 object
cor_value_Mut0vsMut1 <- cor_test16_Mut0vsMut1$estimate
cor_value_Mut0vsMut2 <- cor_test16_Mut0vsMut2$estimate
cor_value_Mut0vsMut3 <- cor_test16_Mut0vsMut3$estimate
cor_value_Mut0vsMut4 <- cor_test16_Mut0vsMut4$estimate
cor_value_Mut0vsMut5 <- cor_test16_Mut0vsMut5$estimate


# Format p-value in scientific notation
p_value_scientific16_v1 <- format(cor_test16_Mut0vsMut1$p.value, scientific = TRUE, digits = 4)
p_value_scientific16_v2 <- format(cor_test16_Mut0vsMut2$p.value, scientific = TRUE, digits = 4)
p_value_scientific16_v3 <- format(cor_test16_Mut0vsMut3$p.value, scientific = TRUE, digits = 4)
p_value_scientific16_v4 <- format(cor_test16_Mut0vsMut4$p.value, scientific = TRUE, digits = 4)
p_value_scientific16_v5 <- format(cor_test16_Mut0vsMut5$p.value, scientific = TRUE, digits = 4)

# Extract number of rows
num_rows16.mut0v1 <- nrow(Mut0vsMut1_16)
num_rows16.mut0v2 <- nrow(Mut0vsMut2_16)
num_rows16.mut0v3 <- nrow(Mut0vsMut3_16)
num_rows16.mut0v4 <- nrow(Mut0vsMut4_16)
num_rows16.mut0v5 <- nrow(Mut0vsMut5_16)

# Plot the correlation (Mut0vsMut1)
mut0v1_16plot <- ggplot(Mut0vsMut1_16, 
             aes(x = fitD12D04_mutations_0, y = fitD12D04_mutations_1, color=numprunedBCs_mutations_0)) +
  labs(x = "fitD12D04 (mutations = 0)",
       y = "fitD12D04 (mutations = 1)", color="",
       title = "Lib16 Complementation (Mut=0 vs Mut=1)") +
  geom_smooth(method=lm,colour="black") +
  geom_density2d(colour="black",alpha=0.2) +
  geom_point(alpha=0.7) +
  scale_colour_gradient2("BCs",low="blue", high="red", mid="blue") +
  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 = 10),
        axis.text.y = element_text(size = 10),
        axis.title.x = element_text(size = 12),
        axis.title.y = element_text(size = 12),
        panel.background = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        legend.position="left") +
  annotate("text", x = max(Mut0vsMut1_16$fitD12D04_mutations_0), y = min(Mut0vsMut1_16$fitD12D04_mutations_1), 
           label = paste("p-value =", p_value_scientific16_v1), hjust = 1, vjust = 0) +
  annotate("text", x = max(Mut0vsMut1_16$fitD12D04_mutations_0), y = min(Mut0vsMut1_16$fitD12D04_mutations_1),
            label = paste("Correlation =", round(cor_value_Mut0vsMut1, 2)), hjust = 1, vjust = -1.5) +
  annotate("text", x = min(Mut0vsMut1_16$fitD12D04_mutations_0), y = max(Mut0vsMut1_16$fitD12D04_mutations_1),
           label = paste("Mutants =", num_rows16.mut0v1), hjust = 0, vjust = 1.5)

mut0v1_16plot2 <- ggMarginal(mut0v1_16plot, type = "histogram", fill = "#E69F00", alpha=0.75) #add side histograms
`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'
mut0v1_16plot2


# Plot the correlation (Mut0vsMut2)
mut0v2_16plot <- ggplot(Mut0vsMut2_16, 
             aes(x = fitD12D04_mutations_0, y = fitD12D04_mutations_2, color=numprunedBCs_mutations_0)) +
  labs(x = "fitD12D04 (mutations = 0)",
       y = "fitD12D04 (mutations = 2)", color="",
       title = "Lib16 Complementation (Mut=0 vs Mut=2)") +
  geom_smooth(method=lm,colour="black") +
  geom_density2d(colour="black",alpha=0.2) +
  geom_point(alpha=0.7) +
  scale_colour_gradient2("BCs",low="blue", high="red", mid="blue") +
  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 = 10),
        axis.text.y = element_text(size = 10),
        axis.title.x = element_text(size = 12),
        axis.title.y = element_text(size = 12),
        panel.background = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        legend.position="left") +
  annotate("text", x = max(Mut0vsMut2_16$fitD12D04_mutations_0), y = min(Mut0vsMut2_16$fitD12D04_mutations_2), 
           label = paste("p-value =", p_value_scientific16_v2), hjust = 1, vjust = 0)+
  annotate("text", x = max(Mut0vsMut2_16$fitD12D04_mutations_0), y = min(Mut0vsMut2_16$fitD12D04_mutations_2),
            label = paste("Correlation =", round(cor_value_Mut0vsMut2, 2)), hjust = 1, vjust = -1.5) +
  annotate("text", x = min(Mut0vsMut2_16$fitD12D04_mutations_0), y = max(Mut0vsMut2_16$fitD12D04_mutations_2),
           label = paste("Mutants =", num_rows16.mut0v2), hjust = 0, vjust = 1.5)

mut0v2_16plot2 <- ggMarginal(mut0v2_16plot, type = "histogram", fill = "#E69F00", alpha=0.75) #add side histograms
`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'
mut0v2_16plot2


# Plot the correlation (Mut0vsMut3)
mut0v3_16plot <- ggplot(Mut0vsMut3_16, 
             aes(x = fitD12D04_mutations_0, y = fitD12D04_mutations_3, color=numprunedBCs_mutations_0)) +
  labs(x = "fitD12D04 (mutations = 0)",
       y = "fitD12D04 (mutations = 3)", color="",
       title = "Lib16 Complementation (Mut=0 vs Mut=3)") +
  geom_smooth(method=lm,colour="black") +
  geom_density2d(colour="black",alpha=0.2) +
  geom_point(alpha=0.7) +
  scale_colour_gradient2("BCs",low="blue", high="red", mid="blue") +
  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 = 10),
        axis.text.y = element_text(size = 10),
        axis.title.x = element_text(size = 12),
        axis.title.y = element_text(size = 12),
        panel.background = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        legend.position="left") +
  annotate("text", x = max(Mut0vsMut3_16$fitD12D04_mutations_0), y = min(Mut0vsMut3_16$fitD12D04_mutations_3), 
           label = paste("p-value =", p_value_scientific16_v3), hjust = 1, vjust = 0)+
  annotate("text", x = max(Mut0vsMut3_16$fitD12D04_mutations_0), y = min(Mut0vsMut3_16$fitD12D04_mutations_3),
            label = paste("Correlation =", round(cor_value_Mut0vsMut3, 2)), hjust = 1, vjust = -1.5) +
  annotate("text", x = min(Mut0vsMut3_16$fitD12D04_mutations_0), y = max(Mut0vsMut3_16$fitD12D04_mutations_3),
           label = paste("Mutants =", num_rows16.mut0v3), hjust = 0, vjust = 1.5)

mut0v3_16plot2 <- ggMarginal(mut0v3_16plot, type = "histogram", fill = "#E69F00", alpha=0.75) #add side histograms
`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'
mut0v3_16plot2


# Plot the correlation (Mut0vsMut4)
mut0v4_16plot <- ggplot(Mut0vsMut4_16, 
             aes(x = fitD12D04_mutations_0, y = fitD12D04_mutations_4, color=numprunedBCs_mutations_0)) +
  labs(x = "fitD12D04 (mutations = 0)",
       y = "fitD12D04 (mutations = 4)", color="",
       title = "Lib16 Complementation (Mut=0 vs Mut=4)") +
  geom_smooth(method=lm,colour="black") +
  geom_density2d(colour="black",alpha=0.2) +
  geom_point(alpha=0.7) +
  scale_colour_gradient2("BCs",low="blue", high="red", mid="blue") +
  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 = 10),
        axis.text.y = element_text(size = 10),
        axis.title.x = element_text(size = 12),
        axis.title.y = element_text(size = 12),
        panel.background = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        legend.position="left") +
  annotate("text", x = max(Mut0vsMut4_16$fitD12D04_mutations_0), y = min(Mut0vsMut4_16$fitD12D04_mutations_4), 
           label = paste("p-value =", p_value_scientific16_v4), hjust = 1, vjust = 0)+
  annotate("text", x = max(Mut0vsMut4_16$fitD12D04_mutations_0), y = min(Mut0vsMut4_16$fitD12D04_mutations_4),
            label = paste("Correlation =", round(cor_value_Mut0vsMut4, 2)), hjust = 1, vjust = -1.5) +
  annotate("text", x = min(Mut0vsMut4_16$fitD12D04_mutations_0), y = max(Mut0vsMut4_16$fitD12D04_mutations_4),
           label = paste("Mutants =", num_rows16.mut0v4), hjust = 0, vjust = 1.5)

mut0v4_16plot2 <- ggMarginal(mut0v4_16plot, type = "histogram", fill = "#E69F00", alpha=0.75) #add side histograms
`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'
mut0v4_16plot2


# Plot the correlation (Mut0vsMut5)
mut0v5_16plot <- ggplot(Mut0vsMut5_16, 
             aes(x = fitD12D04_mutations_0, y = fitD12D04_mutations_5, color=numprunedBCs_mutations_0)) +
  labs(x = "fitD12D04 (mutations = 0)",
       y = "fitD12D04 (mutations = 5)", color="",
       title = "Lib16 Complementation (Mut=0 vs Mut=5)") +
  geom_smooth(method=lm,colour="black") +
  geom_density2d(colour="black",alpha=0.2) +
  geom_point(alpha=0.7) +
  scale_colour_gradient2("BCs",low="blue", high="red", mid="blue") +
  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 = 10),
        axis.text.y = element_text(size = 10),
        axis.title.x = element_text(size = 12),
        axis.title.y = element_text(size = 12),
        panel.background = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        legend.position="left") +
  annotate("text", x = max(Mut0vsMut5_16$fitD12D04_mutations_0), y = min(Mut0vsMut5_16$fitD12D04_mutations_5), 
           label = paste("p-value =", p_value_scientific16_v5), hjust = 1, vjust = 0)+
  annotate("text", x = max(Mut0vsMut5_16$fitD12D04_mutations_0), y = min(Mut0vsMut5_16$fitD12D04_mutations_5),
            label = paste("Correlation =", round(cor_value_Mut0vsMut5, 2)), hjust = 1, vjust = -1.5) +
  annotate("text", x = min(Mut0vsMut5_16$fitD12D04_mutations_0), y = max(Mut0vsMut5_16$fitD12D04_mutations_5),
           label = paste("Mutants =", num_rows16.mut0v5), hjust = 0, vjust = 1.5)

mut0v5_16plot2 <- ggMarginal(mut0v5_16plot, type = "histogram", fill = "#E69F00", alpha=0.75) #add side histograms
`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'
mut0v5_16plot2

Homolog Mutant Fitness

Create ridge plots for homolog fitness across the TMP gradient using homologs with 0 mutations, 1 associated mutation, and 5 associated mutations:

This section uses the library(ggridges) package.

First, subset the mut_collapse_15 and mut_collapse_16 objects to retain only “ID”, “mutID”, “numprunedBCs”, “mutations”, “seq”, “pct_ident”, and fitness values for first time point.

# Lib15
mut_collapse_15_subset <- mut_collapse_15 %>% select(ID, mutID, mutations, numprunedBCs, fitD05D03, fitD06D03, fitD07D03, fitD08D03, fitD09D03, fitD10D03, fitD11D03, seq, pct_ident)

# Lib16
mut_collapse_16_subset <- mut_collapse_16 %>% select(ID, mutID, mutations, numprunedBCs, fitD12D04, fitE01D04, fitE02D04, fitE03D04, fitE04D04, fitE05D04, fitE06D04, seq, pct_ident)

Transform both datasets prior to plotting ridge plots for fitness:

Perfects (>5 BCs, 0 mutations)

# Lib15
mut_collapse_15_subset_0mut <- mut_collapse_15_subset %>%
  filter(mutations == 0) %>%  # Add this line to filter for mutations == 0
  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
mut_collapse_16_subset_0mut <- mut_collapse_16_subset %>%
  filter(mutations == 0) %>%  # Add this line to filter for mutations == 0
  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
mut_collapse_15_16_5BCs_0mut <- bind_rows(
  mut_collapse_15_subset_0mut %>% mutate(Lib = "Codon1"),
  mut_collapse_16_subset_0mut %>% mutate(Lib = "Codon2"),
  .id = "id")

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

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

tmp_ridges_15_16_0mut <- ggplot(mut_collapse_15_16_5BCs_0mut, aes(x = val, y = factor(TMP, level = mut_collapse_15_16_5BCs_0mut_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("Distance from Homolog \nMutations = 0 a.a.") +
  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 = 14, hjust = 0.5),
    axis.text.x = element_text(size = 12),
    axis.text.y = element_text(size = 12, face = "bold"),
    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 = "none",
    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_0mut

Homologs w/ Muts (1 a.a. mutation)

# Lib15
mut_collapse_15_subset_0.1mut <- mut_collapse_15_subset %>%
  #filter(mutations %in% c(0, 1)) %>%
  filter(mutations == 1) %>%
  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
mut_collapse_16_subset_0.1mut <- mut_collapse_16_subset %>%
  #filter(mutations %in% c(0, 1)) %>%
  filter(mutations == 1) %>%
  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
mut_collapse_15_16_5BCs_0.1mut <- bind_rows(
  mut_collapse_15_subset_0.1mut %>% mutate(Lib = "Codon1"),
  mut_collapse_16_subset_0.1mut %>% mutate(Lib = "Codon2"),
  .id = "id")

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

mut_collapse_15_16_5BCs_0.1mut_order <- c("200-TMP", "50-TMP", "10-TMP", "1.0-TMP", "0.5-TMP", "0.058-TMP", "0-TMP")

tmp_ridges_15_16_0.1mut <- ggplot(mut_collapse_15_16_5BCs_0.1mut, aes(x = val, y = factor(TMP, level = mut_collapse_15_16_5BCs_0.1mut_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("Distance from Homolog \nMutations = 1 a.a.") +
  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 = 14, hjust = 0.5),
    axis.text.x = element_text(size = 12),
    axis.text.y = element_blank(),
    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_0.1mut

Homologs w/ Muts (2 a.a. mutation)

# Lib15
mut_collapse_15_subset_0.2mut <- mut_collapse_15_subset %>%
  #filter(mutations %in% c(0, 1, 2)) %>%
  filter(mutations == 2) %>%
  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
mut_collapse_16_subset_0.2mut <- mut_collapse_16_subset %>%
  #filter(mutations %in% c(0, 1, 2)) %>%
  filter(mutations == 2) %>%
  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
mut_collapse_15_16_5BCs_0.2mut <- bind_rows(
  mut_collapse_15_subset_0.2mut %>% mutate(Lib = "Codon1"),
  mut_collapse_16_subset_0.2mut %>% mutate(Lib = "Codon2"),
  .id = "id")

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

mut_collapse_15_16_5BCs_0.2mut_order <- c("200-TMP", "50-TMP", "10-TMP", "1.0-TMP", "0.5-TMP", "0.058-TMP", "0-TMP")

tmp_ridges_15_16_0.2mut <- ggplot(mut_collapse_15_16_5BCs_0.2mut, aes(x = val, y = factor(TMP, level = mut_collapse_15_16_5BCs_0.2mut_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("Distance from Homolog \nMutations = 2 a.a.") +
  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 = 14, hjust = 0.5),
    axis.text.x = element_text(size = 12),
    axis.text.y = element_blank(),
    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 = "none",
    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_0.2mut

Homologs w/ Muts (3 a.a. mutation)

# Lib15
mut_collapse_15_subset_0.3mut <- mut_collapse_15_subset %>%
  #filter(mutations %in% c(0, 1, 2, 3)) %>%
  filter(mutations == 3) %>%
  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
mut_collapse_16_subset_0.3mut <- mut_collapse_16_subset %>%
  #filter(mutations %in% c(0, 1, 2, 3)) %>%
  filter(mutations == 3) %>%
  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
mut_collapse_15_16_5BCs_0.3mut <- bind_rows(
  mut_collapse_15_subset_0.3mut %>% mutate(Lib = "Codon1"),
  mut_collapse_16_subset_0.3mut %>% mutate(Lib = "Codon2"),
  .id = "id")

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

mut_collapse_15_16_5BCs_0.3mut_order <- c("200-TMP", "50-TMP", "10-TMP", "1.0-TMP", "0.5-TMP", "0.058-TMP", "0-TMP")

tmp_ridges_15_16_0.3mut <- ggplot(mut_collapse_15_16_5BCs_0.3mut, aes(x = val, y = factor(TMP, level = mut_collapse_15_16_5BCs_0.3mut_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("Distance from Homolog \nMutations = 3 a.a.") +
  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 = 14, hjust = 0.5),
    axis.text.x = element_text(size = 12),
    axis.text.y = element_blank(),
    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 = "none",
    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_0.3mut

Homologs w/ Muts (4 a.a. mutation)

# Lib15
mut_collapse_15_subset_0.4mut <- mut_collapse_15_subset %>%
  #filter(mutations %in% c(0, 1, 2, 3, 4)) %>%
  filter(mutations == 4) %>%
  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
mut_collapse_16_subset_0.4mut <- mut_collapse_16_subset %>%
  #filter(mutations %in% c(0, 1, 2, 3, 4)) %>%
  filter(mutations == 4) %>%
  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
mut_collapse_15_16_5BCs_0.4mut <- bind_rows(
  mut_collapse_15_subset_0.4mut %>% mutate(Lib = "Codon1"),
  mut_collapse_16_subset_0.4mut %>% mutate(Lib = "Codon2"),
  .id = "id")

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

mut_collapse_15_16_5BCs_0.4mut_order <- c("200-TMP", "50-TMP", "10-TMP", "1.0-TMP", "0.5-TMP", "0.058-TMP", "0-TMP")

tmp_ridges_15_16_0.4mut <- ggplot(mut_collapse_15_16_5BCs_0.4mut, aes(x = val, y = factor(TMP, level = mut_collapse_15_16_5BCs_0.4mut_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("Distance from Homolog \nMutations = 4 a.a.") +
  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 = 14, hjust = 0.5),
    axis.text.x = element_text(size = 12),
    axis.text.y = element_blank(),
    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 = "none",
    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_0.4mut

Homologs w/ Muts (5 a.a. mutations)

# Lib15
mut_collapse_15_subset_0.5mut <- mut_collapse_15_subset %>%
  #filter(mutations %in% c(0, 1, 2, 3, 4, 5)) %>%
  filter(mutations == 5) %>%
  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
mut_collapse_16_subset_0.5mut <- mut_collapse_16_subset %>%
  #filter(mutations %in% c(0, 1, 2, 3, 4, 5)) %>%
  filter(mutations == 5) %>%
  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
mut_collapse_15_16_5BCs_0.5mut <- bind_rows(
  mut_collapse_15_subset_0.5mut %>% mutate(Lib = "Codon1"),
  mut_collapse_16_subset_0.5mut %>% mutate(Lib = "Codon2"),
  .id = "id")

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

mut_collapse_15_16_5BCs_0.5mut_order <- c("200-TMP", "50-TMP", "10-TMP", "1.0-TMP", "0.5-TMP", "0.058-TMP", "0-TMP")

tmp_ridges_15_16_0.5mut <- ggplot(mut_collapse_15_16_5BCs_0.5mut, aes(x = val, y = factor(TMP, level = mut_collapse_15_16_5BCs_0.5mut_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("Distance from Homolog \nMutations = 5 a.a.") +
  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 = 14, hjust = 0.5),
    axis.text.x = element_text(size = 12),
    axis.text.y = element_blank(),
    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 = "none",
    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_0.5mut

patch7 <- tmp_ridges_15_16_0mut | tmp_ridges_15_16_0.1mut | tmp_ridges_15_16_0.5mut
patch7

Mutant Fitness Gains

Lib15

5 a.a. Distance Only

Test the significance of fitness changes between perfect assemblies (mutations = 0) and associated mutants up to 5 a.a. distance for each TMP treatment within both libraries. The following code applied to Lib15 (Codon 1) testing fitness differences across mutations at the 200-TMP (400x MIC) treatment.

# Step 1: Prepare the data
Lib15.mut.5aa.differences <- mut_collapse_15_subset %>%
  filter(mutations %in% c(0, 5)) %>%
  group_by(ID, mutations) %>%
  summarise(fitD11D03 = mean(fitD11D03, na.rm = TRUE), .groups = "drop") %>%
  pivot_wider(names_from = mutations, values_from = fitD11D03, names_prefix = "mut_") %>%
  filter(!is.na(mut_0) & !is.na(mut_5)) %>%
  mutate(difference = mut_5 - mut_0)

# Step 2: Plot the distribution
Lib15.mut.200.tmp.5aa.plot <- ggplot(Lib15.mut.5aa.differences, aes(x = difference)) +
  geom_histogram(binwidth = 0.1, fill = "skyblue", color = "black") +
  geom_vline(aes(xintercept = mean(difference, na.rm = TRUE)), 
             color = "red", linetype = "dashed", size = 1) +
  labs(title = "Distribution of Differences in fitD11D03",
       subtitle = "5 a.a. distance (mutants) minus 0 a.a. distance (perfects)",
       x = "Difference",
       y = "Count") +
  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),
    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(0,2),
                     breaks = seq(0, 2, by = 1)) +
  scale_x_continuous(expand = c(0, 0), limits = c(-4,12),
                     breaks = seq(-4, 12, by = 2))

print(Lib15.mut.200.tmp.5aa.plot)

Add NCBI taxonomy to each homolog “ID” in the “Lib15.mut.5aa.differences” dataset:

Lib15.mut.5aa.differences.columns <- c("ID", "PctIdentEcoli", "TaxID", "NCBI.name", "NCBI.superkingdom", "NCBI.phylum", "NCBI.class", "NCBI.order", "NCBI.family", "NCBI.genus", "NCBI.species")

Lib15.mut.5aa.differences_merged <- Lib15.mut.5aa.differences %>%
  left_join(Alltree15_taxa_merged %>% select(all_of(Lib15.mut.5aa.differences.columns)), by = "ID")

# View the merged dataframe
print(Lib15.mut.5aa.differences_merged)

# Save the Lib15.mut.5aa.differences data frame
write.csv(Lib15.mut.5aa.differences_merged, 
          "Mutants/OUTPUT/mut15.0.5.differences.200tmp.csv", row.names = FALSE)

All a.a. Distance (1-5)

Summarize the effects of mutational changes at 5 a.a. distance from recovered perfect homologs:

# Step 1: Prepare the data (same as before)
Lib15.mut.5aa.summary.differences <- mut_collapse_15_subset %>%
  filter(mutations %in% c(0, 1, 2, 3, 4, 5)) %>%
  group_by(ID, mutations) %>%
  summarise(fitD11D03 = mean(fitD11D03, na.rm = TRUE), .groups = "drop") %>%
  pivot_wider(names_from = mutations, values_from = fitD11D03, names_prefix = "mut_") %>%
  filter(!is.na(mut_0)) %>%
  mutate(
    diff_1_0 = mut_1 - mut_0,
    diff_2_0 = mut_2 - mut_0,
    diff_3_0 = mut_3 - mut_0,
    diff_4_0 = mut_4 - mut_0,
    diff_5_0 = mut_5 - mut_0
  ) %>%
  select(ID, starts_with("diff_"))

# Step 2: Reshape the data for plotting
Lib15.mut.5aa.summary.differences_long <- Lib15.mut.5aa.summary.differences %>%
  pivot_longer(cols = starts_with("diff_"), 
               names_to = "comparison", 
               values_to = "difference") %>%
  mutate(num_mutations = as.integer(substr(comparison, 6, 6)))

# Step 3: Perform statistical tests
Lib15.mut.5aa.summary.differences.stat_tests <- Lib15.mut.5aa.summary.differences_long %>%
  group_by(comparison) %>%
  summarise(
    mean_diff = mean(difference, na.rm = TRUE),
    p_value = t.test(difference)$p.value,
    .groups = "drop"
  ) %>%
  mutate(p_value_label = ifelse(p_value < 0.001, "p < 0.001",
                                ifelse(p_value < 0.01, "p < 0.01",
                                       ifelse(p_value < 0.05, "p < 0.05",
                                              paste("p =", round(p_value, 3))))))

print(Lib15.mut.5aa.summary.differences.stat_tests)

Next, we’ll track the mutational fitness gains at each a.a. distance (from 1-5 a.a.):

# Step 4: Histogram plot with statistical test results
Lib15.mut.5aa.summary.histogram_plot <- ggplot(Lib15.mut.5aa.summary.differences_long,
                                               aes(x = difference, fill = comparison)) +
  geom_histogram(binwidth = 0.1, position = "identity", alpha = 0.6) +
  geom_vline(data = Lib15.mut.5aa.summary.differences.stat_tests,
             aes(xintercept = mean_diff, color = comparison),
             linetype = "dashed", size = 1) +
  geom_text(data = Lib15.mut.5aa.summary.differences.stat_tests,
            aes(x = Inf, y = Inf, label = p_value_label),
            hjust = 1.1, vjust = 1.1, size = 3) +
  labs(title = "Distribution of Codon 1 Fitness Differences at 200 TMP",
       subtitle = "Mutations 1, 2, 3, 4, 5 minus Mutation 0",
       x = "Difference",
       y = "Count") +
  facet_wrap(~comparison, scales = "free_y", nrow = 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),
    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_fill_brewer(palette = "Set1") +
  scale_color_brewer(palette = "Set1") +
  theme(legend.position = "none",
        axis.text.x = element_text(angle = 45, hjust = 1),
        plot.title = element_text(hjust = 0.5),
        plot.subtitle = element_text(hjust = 0.5))

Lib15.mut.5aa.summary.histogram_plot


# Step 4: Line plot of distribution vs number of mutations
Lib15.mut.5aa.summary.line_plot <- ggplot(Lib15.mut.5aa.summary.differences_long, 
                                          aes(x = num_mutations, y = difference)) +
  geom_jitter(alpha = 0.1, width = 0.2) +
  geom_boxplot(aes(group = num_mutations), alpha = 0.5, outlier.shape = NA) +
  geom_smooth(method = "loess", se = TRUE, color = "red") +
  labs(title = "Distribution of Differences vs Number of Mutations",
       x = "Number of Mutations",
       y = "Fitness Difference at 200 TMP \n(Codon 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),
    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())

Lib15.mut.5aa.summary.line_plot

Lib15.mut.5aa.summary.differences.columns <- c("ID", "PctIdentEcoli", "TaxID", "NCBI.name", "NCBI.superkingdom", "NCBI.phylum", "NCBI.class", "NCBI.order", "NCBI.family", "NCBI.genus", "NCBI.species")

Lib15.mut.5aa.summary.differences_merged <- Lib15.mut.5aa.summary.differences %>%
  left_join(Alltree15_taxa_merged %>% select(all_of(Lib15.mut.5aa.summary.differences.columns)), by = "ID")

# View the merged dataframe
print(Lib15.mut.5aa.summary.differences_merged)

# Save the Lib15.mut.5aa.differences data frame
write.csv(Lib15.mut.5aa.summary.differences_merged, 
          "Mutants/OUTPUT/mut15.0.1.2.3.4.5.differences.200tmp.csv", row.names = FALSE)
patch8 <- Lib15.mut.5aa.summary.histogram_plot / Lib15.mut.5aa.summary.line_plot
patch8

Lib16

5 a.a. Distance Only

Test the significance of fitness changes between perfect assemblies (mutations = 0) and associated mutants up to 5 a.a. distance for each TMP treatment within both libraries. The following code applied to Lib16 (Codon 2) testing fitness differences across mutations at the 200-TMP (400x MIC) treatment.

# Step 1: Prepare the data
Lib16.mut.5aa.differences <- mut_collapse_16_subset %>%
  filter(mutations %in% c(0, 5)) %>%
  group_by(ID, mutations) %>%
  summarise(fitE06D04 = mean(fitE06D04, na.rm = TRUE), .groups = "drop") %>%
  pivot_wider(names_from = mutations, values_from = fitE06D04, names_prefix = "mut_") %>%
  filter(!is.na(mut_0) & !is.na(mut_5)) %>%
  mutate(difference = mut_5 - mut_0)

# Step 2: Plot the distribution
Lib16.mut.200.tmp.5aa.plot <- ggplot(Lib16.mut.5aa.differences, aes(x = difference)) +
  geom_histogram(binwidth = 0.1, fill = "skyblue", color = "black") +
  geom_vline(aes(xintercept = mean(difference, na.rm = TRUE)), 
             color = "red", linetype = "dashed", size = 1) +
  labs(title = "Codon 2 Distribution of Differences at 400x MIC",
       subtitle = "5 a.a. distance (mutants) minus 0 a.a. distance (perfects)",
       x = "Difference",
       y = "Count") +
  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),
    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(0,4),
                     breaks = seq(0, 4, by = 1)) +
  scale_x_continuous(expand = c(0, 0), limits = c(-8,14),
                     breaks = seq(-8, 14, by = 2))

print(Lib16.mut.200.tmp.5aa.plot)

Add NCBI taxonomy to each homolog “ID” in the “Lib16.mut.5aa.differences” dataset:

Lib16.mut.5aa.differences.columns <- c("ID", "PctIdentEcoli", "TaxID", "NCBI.name", "NCBI.superkingdom", "NCBI.phylum", "NCBI.class", "NCBI.order", "NCBI.family", "NCBI.genus", "NCBI.species")

Lib16.mut.5aa.differences_merged <- Lib16.mut.5aa.differences %>%
  left_join(Alltree15_taxa_merged %>% select(all_of(Lib16.mut.5aa.differences.columns)), by = "ID")

# View the merged dataframe
print(Lib16.mut.5aa.differences_merged)

# Save the Lib16.mut.5aa.differences data frame
write.csv(Lib16.mut.5aa.differences_merged, 
          "Mutants/OUTPUT/mut16.0.5.differences.200tmp.csv", row.names = FALSE)

All a.a. Distances (1-5)

Summarize the effects of mutational changes at 5 a.a. distance from recovered perfect homologs:

# Step 1: Prepare the data (same as before)
Lib16.mut.5aa.summary.differences <- mut_collapse_16_subset %>%
  filter(mutations %in% c(0, 1, 2, 3, 4, 5)) %>%
  group_by(ID, mutations) %>%
  summarise(fitE06D04 = mean(fitE06D04, na.rm = TRUE), .groups = "drop") %>%
  pivot_wider(names_from = mutations, values_from = fitE06D04, names_prefix = "mut_") %>%
  filter(!is.na(mut_0)) %>%
  mutate(
    diff_1_0 = mut_1 - mut_0,
    diff_2_0 = mut_2 - mut_0,
    diff_3_0 = mut_3 - mut_0,
    diff_4_0 = mut_4 - mut_0,
    diff_5_0 = mut_5 - mut_0
  ) %>%
  select(ID, starts_with("diff_"))

# Step 2: Reshape the data for plotting
Lib16.mut.5aa.summary.differences_long <- Lib16.mut.5aa.summary.differences %>%
  pivot_longer(cols = starts_with("diff_"), 
               names_to = "comparison", 
               values_to = "difference") %>%
  mutate(num_mutations = as.integer(substr(comparison, 6, 6)))

# Step 3: Perform statistical tests
Lib16.mut.5aa.summary.differences.stat_tests <- Lib16.mut.5aa.summary.differences_long %>%
  group_by(comparison) %>%
  summarise(
    mean_diff = mean(difference, na.rm = TRUE),
    p_value = t.test(difference)$p.value,
    .groups = "drop"
  ) %>%
  mutate(p_value_label = ifelse(p_value < 0.001, "p < 0.001",
                                ifelse(p_value < 0.01, "p < 0.01",
                                       ifelse(p_value < 0.05, "p < 0.05",
                                              paste("p =", round(p_value, 3))))))

print(Lib16.mut.5aa.summary.differences.stat_tests)

Next, we’ll track the mutational fitness gains at each a.a. distance (from 1-5 a.a.):

# Step 4: Histogram plot with statistical test results
Lib16.mut.5aa.summary.histogram_plot <- ggplot(Lib16.mut.5aa.summary.differences_long,
                                               aes(x = difference, fill = comparison)) +
  geom_histogram(binwidth = 0.1, position = "identity", alpha = 0.6) +
  geom_vline(data = Lib16.mut.5aa.summary.differences.stat_tests,
             aes(xintercept = mean_diff, color = comparison),
             linetype = "dashed", size = 1) +
  geom_text(data = Lib16.mut.5aa.summary.differences.stat_tests,
            aes(x = Inf, y = Inf, label = p_value_label),
            hjust = 1.1, vjust = 1.1, size = 3) +
  labs(title = "Distribution of Codon 2 Fitness Differences at 200 TMP",
       subtitle = "Mutations 1, 2, 3, 4, 5 minus Mutation 0",
       x = "Difference",
       y = "Count") +
  facet_wrap(~comparison, scales = "free_y", nrow = 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),
    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_fill_brewer(palette = "Set1") +
  scale_color_brewer(palette = "Set1") +
  theme(legend.position = "none",
        axis.text.x = element_text(angle = 45, hjust = 1),
        plot.title = element_text(hjust = 0.5),
        plot.subtitle = element_text(hjust = 0.5))

Lib16.mut.5aa.summary.histogram_plot


# Step 4: Line plot of distribution vs number of mutations
Lib16.mut.5aa.summary.line_plot <- ggplot(Lib16.mut.5aa.summary.differences_long, 
                                          aes(x = num_mutations, y = difference)) +
  geom_jitter(alpha = 0.1, width = 0.2) +
  geom_boxplot(aes(group = num_mutations), alpha = 0.5, outlier.shape = NA) +
  geom_smooth(method = "loess", se = TRUE, color = "red") +
  labs(title = "Distribution of Differences vs Number of Mutations",
       x = "Number of Mutations",
       y = "Fitness Difference at 200 TMP \n(Codon 2)") +
  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),
    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())

Lib16.mut.5aa.summary.line_plot

Lib16.mut.5aa.summary.differences.columns <- c("ID", "PctIdentEcoli", "TaxID", "NCBI.name", "NCBI.superkingdom", "NCBI.phylum", "NCBI.class", "NCBI.order", "NCBI.family", "NCBI.genus", "NCBI.species")

Lib16.mut.5aa.summary.differences_merged <- Lib16.mut.5aa.summary.differences %>%
  left_join(Alltree15_taxa_merged %>% select(all_of(Lib16.mut.5aa.summary.differences.columns)), by = "ID")

# View the merged dataframe
print(Lib16.mut.5aa.summary.differences_merged)

# Save the Lib16.mut.5aa.differences data frame
write.csv(Lib16.mut.5aa.summary.differences_merged, 
          "Mutants/OUTPUT/mut16.0.1.2.3.4.5.differences.200tmp.csv", row.names = FALSE)
patch9 <- Lib16.mut.5aa.summary.histogram_plot / Lib16.mut.5aa.summary.line_plot
patch9

Combined Codon Plots

Plot both Codon versions together in single plot:

patch10 <- Lib15.mut.5aa.summary.histogram_plot / Lib15.mut.5aa.summary.line_plot | Lib16.mut.5aa.summary.histogram_plot / Lib16.mut.5aa.summary.line_plot
patch10

Mutant Median Fitness

The mut_collapse datasets contain unique IDs and all associated mutIDs up to 5 A.A. distance. Start by grouping mutIDs by ID and calculating the median fitness value for each unique ID within each TMP treatment. The number of unique IDs for each codon version should still be: Codon 1 = 797 and **Codon 2 = 666*. However, the fitness values should be different from those calculated based only on perfects (mutations == 0). Plot the change in median fitness value by TMP treatment.

Calculate Median Fitness

Lib15: Group mutIDs by ID and calculate the median fitness value for each unique ID within each TMP treatment.

#Calculate median fitness for each homolog and associated mutants and sum the total number of BCs (numBCs and numprunedBCs)
mut_collapse_15info <- mut_collapse_15 %>%
  group_by(ID) %>%
  summarise(medD05D03=median(fitD05D03, na.rm=T),
            medD06D03=median(fitD06D03, na.rm=T),
            medD07D03=median(fitD07D03, na.rm=T),
            medD08D03=median(fitD08D03, na.rm=T),
            medD09D03=median(fitD09D03, na.rm=T),
            medD10D03=median(fitD10D03, na.rm=T),
            medD11D03=median(fitD11D03, na.rm=T),
            totalnumBCs.L15=sum(numBCs),
            totalnumprunedBCs.L15=sum(numprunedBCs))

Count the number of unique IDs after collapsing mutants up to 5 A.A. distance:

format(length(unique(mut_collapse_15info$ID)), big.mark = ",")
[1] "797"

Lib16: Group mutIDs by ID and calculate the median fitness value for each unique ID within each TMP treatment.

#Calculate median fitness for each homolog and associated mutants and sum the total number of BCs (numBCs and numprunedBCs)
mut_collapse_16info <- mut_collapse_16 %>%
  group_by(ID) %>%
  summarise(medD12D04=median(fitD12D04, na.rm=T),
            medE01D04=median(fitE01D04, na.rm=T),
            medE02D04=median(fitE02D04, na.rm=T),
            medE03D04=median(fitE03D04, na.rm=T),
            medE04D04=median(fitE04D04, na.rm=T),
            medE05D04=median(fitE05D04, na.rm=T),
            medE06D04=median(fitE06D04, na.rm=T),
            totalnumBCs.L16=sum(numBCs),
            totalnumprunedBCs.L16=sum(numprunedBCs))

Count the number of unique IDs after collapsing mutants up to 5 A.A. distance:

format(length(unique(mut_collapse_16info$ID)), big.mark = ",")
[1] "666"

Plot Median TMP Fitness

Combine both datasets and assign labels for the plot:

# Combine the dataframes
mut_collapse_15.16_info_combined_df <- full_join(mut_collapse_15info, mut_collapse_16info, by = "ID")

# Create a mapping for the new labels
mut_collapse_15.16_info_label_map <- c(
  "medD05D03" = "0", "medD06D03" = "0.058", "medD07D03" = "0.5", "medD08D03" = "1",
  "medD09D03" = "10", "medD10D03" = "50", "medD11D03" = "200",
  "medD12D04" = "0", "medE01D04" = "0.058", "medE02D04" = "0.5", "medE03D04" = "1",
  "medE04D04" = "10", "medE05D04" = "50", "medE06D04" = "200"
)

# Reshape and relabel the data
mut_collapse_15.16_info_plot_data <- mut_collapse_15.16_info_combined_df %>%
  pivot_longer(
    cols = starts_with("med"),
    names_to = "condition",
    values_to = "fitness") %>%
  mutate(
    library = case_when(
      startsWith(condition, "medD") & condition != "medD12D04" ~ "Codon1",
      condition == "medD12D04" | startsWith(condition, "medE") ~ "Codon2",
      TRUE ~ NA_character_),
    treatment = mut_collapse_15.16_info_label_map[condition],
    treatment = factor(treatment, levels = c("0", "0.058", "0.5", "1", "10", "50", "200")))

Plot as boxplot:

# Create the plot
mut_collapse_15.16_info_plot <- ggplot(mut_collapse_15.16_info_plot_data, 
                                       aes(x = treatment, y = fitness, fill = library)) +
  geom_boxplot(position = position_dodge(width = 0.8), alpha = 0.8) +
  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") +
  labs(
    title = "Median Fitness \nCollapsed to 5 a.a. Distance",
    x = "Trimethoprim (ug/mL)",
    y = "Median Fitness (LogFC)",
    fill = "Library") +
  scale_fill_manual(values = c("Codon1" = "#0072B2", "Codon2" = "#E69F00")) +
  scale_x_discrete(drop = FALSE)

mut_collapse_15.16_info_plot

Merge Libraries

Generate a new dataframe retaining only the unique IDs shared between libraries:

shared_mut_collapse_15.16info <- merge(mut_collapse_15info, mut_collapse_16info, by = "ID")

Count the number of unique IDs shared between libraries:

format(length(unique(shared_mut_collapse_15.16info$ID)), big.mark = ",")
[1] "493"

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

# Complementation - 0-TMP
Shared.Mut.Collapse.counts.0.tmp <- shared_mut_collapse_15.16info[, c("ID", "totalnumprunedBCs.L15", "totalnumprunedBCs.L16", "medD05D03","medD12D04")] %>% na.omit(Shared.Mut.Collapse.counts.0.tmp)

Calculate correlation between median fitness values for unique IDs shared between libraries:

# Calculate correlation and p-value between Lib15 and Lib16 Complementation
cor_test_shared_mut_collapse_15.16info <- cor.test(Shared.Mut.Collapse.counts.0.tmp$medD05D03,
                                                   Shared.Mut.Collapse.counts.0.tmp$medD12D04)

cor_test_shared_mut_collapse_15.16info

    Pearson's product-moment correlation

data:  Shared.Mut.Collapse.counts.0.tmp$medD05D03 and Shared.Mut.Collapse.counts.0.tmp$medD12D04
t = 11.13, df = 487, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.3766928 0.5182993
sample estimates:
      cor 
0.4503233 

Plot Correlation

Plot the median fitness correlation between unique IDs shared between Lib15 and Lib16 (median with collapsed mutants):

# Extract correlation value from cor_test_shared_mut_collapse_15.16info object
cor_value_shared <- cor_test_shared_mut_collapse_15.16info$estimate

# Format p-value in scientific notation
p_value_scientific_shared <- format(cor_test_shared_mut_collapse_15.16info$p.value, 
                                                    scientific = TRUE, digits = 4)

# Extract number of rows
num_rows.counts.5aa.0.tmp <- nrow(Shared.Mut.Collapse.counts.0.tmp)

Lib15_16_0_TMP_5AA <- ggplot(Shared.Mut.Collapse.counts.0.tmp, 
             aes(x = medD05D03, y = medD12D04)) +
  labs(x = "Codon 1 Median Fitness w/ 5 A.A. Distance (LogFC) \n(0 μg/mL tmp)",
       y ="Codon 2 Median Fitness w/ 5 A.A. Distance (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(
    medD05D03 >= -1 & medD12D04 >= -1 ~ "lightblue4",
    medD05D03 >= -1 & medD12D04 < -1 ~ "#0072B2",
    medD12D04 >= -1 & medD05D03 < -1 ~ "#E69F00",
    TRUE ~ "black"
  ),
  fill = case_when(
    medD05D03 >= -1 & medD12D04 >= -1 ~ "lightblue4",
    medD05D03 >= -1 & medD12D04 < -1 ~ "#0072B2",
    medD12D04 >= -1 & medD05D03 < -1 ~ "#E69F00",
    TRUE ~ "white"
  ),
  shape = case_when(
    medD05D03 >= -1 & medD12D04 >= -1 ~ 16,
    medD05D03 >= -1 & medD12D04 < -1 ~ 16,
    medD12D04 >= -1 & medD05D03 < -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),
        panel.background = element_blank()) +
  panel_border(color = "black") +
  annotate("text", 
           x = max(Shared.Mut.Collapse.counts.0.tmp$medD05D03), 
           y = min(Shared.Mut.Collapse.counts.0.tmp$medD12D04), 
           label = paste("p-value =", p_value_scientific_shared), hjust = 1, vjust = 0) +
  annotate("text", 
           x = max(Shared.Mut.Collapse.counts.0.tmp$medD05D03), 
           y = min(Shared.Mut.Collapse.counts.0.tmp$medD12D04),
           label = paste("Correlation =", round(cor_value_shared, 2)), hjust = 1, vjust = -1.5) +
  annotate("text",
           x = min(Shared.Mut.Collapse.counts.0.tmp$medD05D03),
           y = max(Shared.Mut.Collapse.counts.0.tmp$medD12D04),
           label = paste("Shared Perfects =", num_rows.counts.5aa.0.tmp), hjust = 0, vjust = 1.5) +
  scale_x_continuous(breaks = seq(floor(min(Shared.Mut.Collapse.counts.0.tmp$medD05D03)), 
                                  ceiling(max(Shared.Mut.Collapse.counts.0.tmp$medD05D03)), by = 1)) +
  scale_y_continuous(breaks = seq(floor(min(Shared.Mut.Collapse.counts.0.tmp$medD12D04)), 
                                  ceiling(max(Shared.Mut.Collapse.counts.0.tmp$medD12D04)), by = 1))

# Add side histograms
Lib15_16_0_TMP_5AA_p01 <- ggMarginal(Lib15_16_0_TMP_5AA, type = "histogram", fill = "lightblue4", alpha=0.5) 
`geom_smooth()` using formula = 'y ~ x'`geom_smooth()` using formula = 'y ~ x'
# View plot
Lib15_16_0_TMP_5AA_p01

Generate Mutant FASTA

Generate a FASTA file for each library containing each unique mutID (designed homologs (ID) and mutants (mutID) up to 5 AA difference) and their corresponding protein sequence for use in broad mutational scanning (BMS) and gain-of-function (GOF) analysis. Use the mut_collapse_15 dataset for Lib15.

Keep Complementing Perfects

First, we need to remove any unique ID with mutations == 0 and fitD05D03 < -1 to retain only those unique perfect IDs capable of complementation.

# Filter dataset to remove fitness < -1
mut_collapse_15_good <- mut_collapse_15 %>%
  # Group by ID
  group_by(ID) %>%
  # Filter out rows where mutations == 0 and fitD05D03 < -1
  filter(!(mutations == 0 & fitD05D03 < -1)) %>%
  # Ungroup the data frame
  ungroup()

# Step to add back all rows where ID == "NP_414590"
mut_collapse_15_good_rows_to_add <- mut_collapse_15 %>%
  filter(ID == "NP_414590")  # Get all rows with ID NP_414590

# Combine the filtered data frame with the rows to add
mut_collapse_15_good <- bind_rows(mut_collapse_15_good, mut_collapse_15_good_rows_to_add) %>%
  distinct()  # Optional: Remove any duplicate rows that may have been introduced

# Create subset to retain only perfects (mutations == 0) for initial BMS FASTA:
mut_collapse_15_good_0_muts <- mut_collapse_15_good %>%
  filter(mutations == 0)  # Keep all rows where mutations == 0

Keep Associated Mutants

Next, we’ll identify which IDs have at least one example of mutations = 0 and then filter out all mutIDs that don’t correspond to a perfect ID:

# Step 1: Identify IDs that have at least one row with mutations == 0
mut_collapse_15_good_valid_ids <- mut_collapse_15_good_0_muts %>%
  filter(mutations == 0) %>%
  select(ID) %>%
  distinct()

# Step 2: Filter the original data frame to keep only rows with valid IDs
mut_collapse_15_good_filtered <- mut_collapse_15_good %>%
  filter(ID %in% mut_collapse_15_good_valid_ids$ID)

Generate FASTA file

Now we’ll generate the FASTA file from the filtered mut_collapse_15_good_0_muts dataset for BMS analysis.

You will have to open the .fasta file and add the WT E. coli DHFR homolog (reference sequence) manually since it’s fitD05D03 values was less than -1 and subsequently removed during the previous filtering step.

# Lib15

# Create the sequences in FASTA format
mut_collapse_15_good_0_muts_fasta_content <- paste(">", mut_collapse_15_good_0_muts$mutID, "\n", mut_collapse_15_good_0_muts$seq, "\n", sep = "", collapse = "")

# Define the file path in the working directory
mut_collapse_15_good_0_muts_fasta_file_path <- file.path(getwd(), "Mutants/mutants_files_formatted/Lib15.mutant.collapse.good.5AA.fasta")

# Write the FASTA content to the file
writeLines(mut_collapse_15_good_0_muts_fasta_content, 
           con = mut_collapse_15_good_0_muts_fasta_file_path)

False-Positive Rate

Codon 1 Library

# All mutants within 5 AA distance at Complementation
filtered_mutant_data_comp_full <- mutants15 %>%
  filter(mutations < 6, !is.na(fitD05D03))
print(paste("Number of rows with mutations < 6 at Complementation:", nrow(filtered_mutant_data_comp_full)))
[1] "Number of rows with mutations < 6 at Complementation: 8614"
# Filtered mutants within 5 AA distance to retain only those with > 1 numprunedBCs at Complementation
filtered_mutant_data_comp_good <- mutants15 %>%
  filter(mutations < 6, numprunedBCs > 1, !is.na(fitD05D03))
print(paste("Number of rows with mutations < 6 and numprunedBCs > 1 at Complementation:", nrow(filtered_mutant_data_comp_good)))
[1] "Number of rows with mutations < 6 and numprunedBCs > 1 at Complementation: 724"

Complementation: Calculate false positive rate for mutant variants > 50 with fitD05D03 > -1 in Codon 1:

# Filter the data for mutations > 49 and remove rows where fitD05D03 is NA
filtered_mutant_data_comp <- mutants15 %>%
  filter(mutations > 49, !is.na(fitD05D03))

# Print the number of rows after filtering
print(paste("Number of rows with mutations > 49:", nrow(filtered_mutant_data_comp)))
[1] "Number of rows with mutations > 49: 16239"
# Define a logical condition for false positives (fitD05D03 > -1 are false positives)
false_positives_comp <- filtered_mutant_data_comp %>%
  filter(fitD05D03 > -1, numprunedBCs > 5)
print(paste("Number of false positives at Complementation (fitD05D03 > -1):", nrow(false_positives_comp)))
[1] "Number of false positives at Complementation (fitD05D03 > -1): 96"
# Calculate the number of false positives
num_false_positives_comp <- nrow(false_positives_comp)
#print(paste("Number of false positives at Complementation:", num_false_positives_comp))

# Calculate the total number of entries that meet the criteria
total_criteria_met_comp <- nrow(filtered_mutant_data_comp)
#print(paste("Total number of entries meeting initial criteria (> 49 mutations) at Complementation:", total_criteria_met_comp))

# Calculate the false positive rate
false_positive_rate_comp <- (num_false_positives_comp / total_criteria_met_comp) * 100

# Print the false positive rate
print(paste("False positive rate at Complementation:", round(false_positive_rate_comp, 2), "%"))
[1] "False positive rate at Complementation: 0.59 %"

MIC: Calculate false positive rate for mutant variants > 50 with fitD07D03 > -1 in Codon 1:

# Filter the data for mutations > 49 and remove rows where fitD05D03 is NA
filtered_mutant_data_MIC <- mutants15 %>%
  filter(mutations > 49, !is.na(fitD07D03))
print(paste("Number of rows with mutations > 49:", nrow(filtered_mutant_data_MIC)))
[1] "Number of rows with mutations > 49: 12160"
# Define a logical condition for false positives (fitD05D03 > -1 are false positives)
false_positives_MIC <- filtered_mutant_data_MIC %>%
  filter(fitD07D03 > -1, numprunedBCs > 5)
print(paste("Number of false positives at MIC (fitD07D03 > -1):", nrow(false_positives_MIC)))
[1] "Number of false positives at MIC (fitD07D03 > -1): 81"
# Calculate the number of false positives
num_false_positives_MIC <- nrow(false_positives_MIC)
#print(paste("Number of false positives at MIC:", num_false_positives_MIC))

# Calculate the total number of entries that meet the criteria
total_criteria_met_MIC <- nrow(filtered_mutant_data_MIC)
#print(paste("Total number of entries meeting initial criteria (> 49 mutations) at MIC:", total_criteria_met_MIC))

# Calculate the false positive rate
false_positive_rate_MIC <- (num_false_positives_MIC / total_criteria_met_MIC) * 100

# Print the false positive rate
print(paste("False positive rate at MIC:", round(false_positive_rate_MIC, 2), "%"))
[1] "False positive rate at MIC: 0.67 %"

400x MIC: Calculate false positive rate for mutant variants > 50 with fitD11D03 > -1 in Codon 1:

# Filter the data for mutations > 49 and remove rows where fitD05D03 is NA
filtered_mutant_data_400xMIC <- mutants15 %>%
  filter(mutations > 49, !is.na(fitD11D03))
print(paste("Number of rows with mutations > 49:", nrow(filtered_mutant_data_400xMIC)))
[1] "Number of rows with mutations > 49: 1068"
# Define a logical condition for false positives (fitD05D03 > -1 are false positives)
false_positives_400xMIC <- filtered_mutant_data_400xMIC %>%
  filter(fitD11D03 > -1, numprunedBCs > 5)
print(paste("Number of false positives at 400x MIC (fitD11D03 > -1):", nrow(false_positives_400xMIC)))
[1] "Number of false positives at 400x MIC (fitD11D03 > -1): 13"
# Calculate the number of false positives
num_false_positives_400xMIC <- nrow(false_positives_400xMIC)
#print(paste("Number of false positives at 400x MIC:", num_false_positives_400xMIC))

# Calculate the total number of entries that meet the criteria
total_criteria_met_400xMIC <- nrow(filtered_mutant_data_400xMIC)
#print(paste("Total number of entries meeting initial criteria (> 49 mutations) at 400x MIC:", total_criteria_met_400xMIC))

# Calculate the false positive rate
false_positive_rate_400xMIC <- (num_false_positives_400xMIC / total_criteria_met_400xMIC) * 100

# Print the false positive rate
print(paste("False positive rate at 400x MIC:", round(false_positive_rate_400xMIC, 2), "%"))
[1] "False positive rate at 400x MIC: 1.22 %"

M9-Supp: Calculate false positive rate for mutant variants > 50 with fitD03D01 > -1 in Codon 1:

# Filter the data for mutations > 49 and remove rows where fitD05D03 is NA
filtered_mutant_data_M9 <- mutants15 %>%
  filter(mutations > 49, !is.na(fitD03D01))
print(paste("Number of rows with mutations > 49:", nrow(filtered_mutant_data_M9)))
[1] "Number of rows with mutations > 49: 30491"
# Define a logical condition for false positives (fitD05D03 > -1 are false positives)
false_positives_M9 <- filtered_mutant_data_M9 %>%
  filter(fitD03D01 > -1, numprunedBCs > 5)
print(paste("Number of false positives at M9-Supp (fitD03D01 > -1):", nrow(false_positives_M9)))
[1] "Number of false positives at M9-Supp (fitD03D01 > -1): 11"
# Calculate the number of false positives
num_false_positives_M9 <- nrow(false_positives_M9)
#print(paste("Number of false positives at M9-Supp:", num_false_positives_M9))

# Calculate the total number of entries that meet the criteria
total_criteria_met_M9 <- nrow(filtered_mutant_data_M9)
#print(paste("Total number of entries meeting initial criteria (> 49 mutations) at M9-Supp:", total_criteria_met_M9))

# Calculate the false positive rate
false_positive_rate_M9 <- (num_false_positives_M9 / total_criteria_met_M9) * 100

# Print the false positive rate
print(paste("False positive rate at M9-Supp:", round(false_positive_rate_M9, 2), "%"))
[1] "False positive rate at M9-Supp: 0.04 %"

Codon 2 Library

Complementation: Calculate false positive rate for mutant variants > 50 with fitD12D04 > -1 in Codon 1:

# Filter the data for mutations > 49 and remove rows where fitD05D03 is NA
filtered_mutant_data_comp_16 <- mutants16 %>%
  filter(mutations > 49, !is.na(fitD12D04))

# Print the number of rows after filtering
print(paste("Number of rows with mutations > 49:", nrow(filtered_mutant_data_comp_16)))
[1] "Number of rows with mutations > 49: 15121"
# Define a logical condition for false positives (fitD05D03 > -1 are false positives)
false_positives_comp_16 <- filtered_mutant_data_comp_16 %>%
  filter(fitD12D04 > -1, numprunedBCs > 5)
print(paste("Number of false positives at Complementation (fitD12D04 > -1):", nrow(false_positives_comp_16)))
[1] "Number of false positives at Complementation (fitD12D04 > -1): 197"
# Calculate the number of false positives
num_false_positives_comp_16 <- nrow(false_positives_comp_16)
#print(paste("Number of false positives at Complementation:", num_false_positives_comp_16))

# Calculate the total number of entries that meet the criteria
total_criteria_met_comp_16 <- nrow(filtered_mutant_data_comp_16)
#print(paste("Total number of entries meeting initial criteria (> 49 mutations) at Complementation:", total_criteria_met_comp_16))

# Calculate the false positive rate
false_positive_rate_comp_16 <- (num_false_positives_comp_16 / total_criteria_met_comp_16) * 100

# Print the false positive rate
print(paste("False positive rate at Complementation:", round(false_positive_rate_comp_16, 2), "%"))
[1] "False positive rate at Complementation: 1.3 %"

MIC: Calculate false positive rate for mutant variants > 50 with fitD07D03 > -1 in Codon 1:

# Filter the data for mutations > 49 and remove rows where fitD05D03 is NA
filtered_mutant_data_MIC_16 <- mutants16 %>%
  filter(mutations > 49, !is.na(fitE02D04))
print(paste("Number of rows with mutations > 49:", nrow(filtered_mutant_data_MIC_16)))
[1] "Number of rows with mutations > 49: 11321"
# Define a logical condition for false positives (fitE02D04 > -1 are false positives)
false_positives_MIC_16 <- filtered_mutant_data_MIC_16 %>%
  filter(fitE02D04 > -1, numprunedBCs > 5)
print(paste("Number of false positives at MIC (fitE02D04 > -1):", nrow(false_positives_MIC_16)))
[1] "Number of false positives at MIC (fitE02D04 > -1): 159"
# Calculate the number of false positives
num_false_positives_MIC_16 <- nrow(false_positives_MIC_16)
#print(paste("Number of false positives at MIC:", num_false_positives_MIC_16))

# Calculate the total number of entries that meet the criteria
total_criteria_met_MIC_16 <- nrow(filtered_mutant_data_MIC_16)
#print(paste("Total number of entries meeting initial criteria (> 49 mutations) at MIC:", total_criteria_met_MIC_16))

# Calculate the false positive rate
false_positive_rate_MIC_16 <- (num_false_positives_MIC_16 / total_criteria_met_MIC_16) * 100

# Print the false positive rate
print(paste("False positive rate at MIC:", round(false_positive_rate_MIC_16, 2), "%"))
[1] "False positive rate at MIC: 1.4 %"

400x MIC: Calculate false positive rate for mutant variants > 50 with fitD11D03 > -1 in Codon 1:

# Filter the data for mutations > 49 and remove rows where fitD05D03 is NA
filtered_mutant_data_400xMIC_16 <- mutants16 %>%
  filter(mutations > 49, !is.na(fitE06D04))
print(paste("Number of rows with mutations > 49:", nrow(filtered_mutant_data_400xMIC_16)))
[1] "Number of rows with mutations > 49: 5222"
# Define a logical condition for false positives (fitE06D04 > -1 are false positives)
false_positives_400xMIC_16 <- filtered_mutant_data_400xMIC_16 %>%
  filter(fitE06D04 > -1, numprunedBCs > 5)
print(paste("Number of false positives at 400x MIC (fitE06D04 > -1):", nrow(false_positives_400xMIC_16)))
[1] "Number of false positives at 400x MIC (fitE06D04 > -1): 30"
# Calculate the number of false positives
num_false_positives_400xMIC_16 <- nrow(false_positives_400xMIC_16)
#print(paste("Number of false positives at 400x MIC:", num_false_positives_400xMIC_16))

# Calculate the total number of entries that meet the criteria
total_criteria_met_400xMIC_16 <- nrow(filtered_mutant_data_400xMIC_16)
#print(paste("Total number of entries meeting initial criteria (> 49 mutations) at 400x MIC:", total_criteria_met_400xMIC_16))

# Calculate the false positive rate
false_positive_rate_400xMIC_16 <- (num_false_positives_400xMIC_16 / total_criteria_met_400xMIC_16) * 100

# Print the false positive rate
print(paste("False positive rate at 400x MIC:", round(false_positive_rate_400xMIC_16, 2), "%"))
[1] "False positive rate at 400x MIC: 0.57 %"

Resistance Mutations

Median Mutations

Here we determine if the resistant mutants at the highest TMP concentrations tend to have more mutations compared to mutants at lower TMP levels. What does the relationship of median number of mutations versus TMP concentration look like?

### Lib15 - Codon 1

## Complementation

# Subset the 'mutants15' dataset to retain only those unique mutants (mutID) with fitness > -1 at fitD05D03. Filter out perfects:
L15_resist_mutants_comp <- mutants15 %>%
  filter(fitD05D03 > -1) %>%
  filter(mutations > 0) %>%
  distinct(mutID, .keep_all = TRUE)

# Count the number of unique mutID retained
L15_resist_mutants_comp_unique <- n_distinct(L15_resist_mutants_comp$mutID)

# Now, calculate the median mutations
L15_resist_mutants_comp_median <- median(L15_resist_mutants_comp$mutations, na.rm = TRUE)

## MIC

# Subset the 'mutants15' dataset to retain only those unique mutants (mutID) with fitness > -1 at fitD05D03. Filter out perfects:
L15_resist_mutants_mic <- mutants15 %>%
  filter(fitD07D03 > -1) %>%
  filter(mutations > 0) %>%
  distinct(mutID, .keep_all = TRUE)

# Count the number of unique mutID retained
L15_resist_mutants_mic_unique <- n_distinct(L15_resist_mutants_mic$mutID)

# Now, calculate the median mutations
L15_resist_mutants_mic_median <- median(L15_resist_mutants_mic$mutations, na.rm = TRUE)

## 400x MIC

# Subset the 'mutants15' dataset to retain only those unique mutants (mutID) with fitness > -1 at fitD05D03. Filter out perfects:
L15_resist_mutants_400xmic <- mutants15 %>%
  filter(fitD11D03 > -1) %>%
  filter(mutations > 0) %>%
  distinct(mutID, .keep_all = TRUE)

# Count the number of unique mutID retained
L15_resist_mutants_400xmic_unique <- n_distinct(L15_resist_mutants_400xmic$mutID)

# Now, calculate the median mutations
L15_resist_mutants_400xmic_median <- median(L15_resist_mutants_400xmic$mutations, na.rm = TRUE)

# Print the result

print(paste("The number of unique resistant mutID at Complementation is:", L15_resist_mutants_comp_unique))
[1] "The number of unique resistant mutID at Complementation is: 15788"
print(paste("The number of unique resistant mutID at MIC is:", L15_resist_mutants_mic_unique))
[1] "The number of unique resistant mutID at MIC is: 10984"
print(paste("The number of unique resistant mutID at 400x is:", L15_resist_mutants_400xmic_unique))
[1] "The number of unique resistant mutID at 400x is: 857"
print(paste("The median number of mutations at Complementation is:", L15_resist_mutants_comp_median))
[1] "The median number of mutations at Complementation is: 57"
print(paste("The median number of mutations at MIC is:", L15_resist_mutants_mic_median))
[1] "The median number of mutations at MIC is: 58"
print(paste("The median number of mutations at 400x MIC is:", L15_resist_mutants_400xmic_median))
[1] "The median number of mutations at 400x MIC is: 59"

Plotting Mutation Distributions

# Create bins for mutations
max_mutations <- max(L15_resist_mutants_comp$mutations, na.rm = TRUE)
breaks <- seq(0, max_mutations + 10, by = 10)

# Adjust labels to match the number of bins
labels <- paste(head(breaks, -1) + 1, tail(breaks, -1), sep = "-")

L15_resist_mutants_comp_bins <- L15_resist_mutants_comp %>%
  mutate(mutation_bins = cut(mutations, breaks = breaks, 
                              right = FALSE, 
                              labels = labels))

# Plot the distribution of mutations
L15_resist_mutants_comp_bins_plot <- ggplot(L15_resist_mutants_comp_bins, aes(x = mutation_bins)) +
  geom_bar(fill = "skyblue", color = "black") +
  labs(title = "Distribution of Mutations After Filtering",
       x = "Number of Mutations (Binned)",
       y = "Count of Unique mutID") +
  theme_minimal()

print(L15_resist_mutants_comp_bins_plot)

# Filter for mutations between 1 and 10
L15_resist_mutants_comp_1_10_filtered <- L15_resist_mutants_comp %>%
  filter(mutations >= 1 & mutations <= 10)

# Plot the distribution of mutations between 1 and 10
L15_resist_mutants_comp_1_10_filtered_plot <- ggplot(L15_resist_mutants_comp_1_10_filtered, aes(x = mutations)) +
  geom_bar(fill = "skyblue", color = "black", width = 0.7) +
  labs(title = "Distribution of Mutations (1 to 10)",
       x = "Number of Mutations",
       y = "Count of Unique mutID") +
  scale_x_continuous(breaks = seq(1, 10, by = 1)) + # Set x-axis breaks for clarity
  theme_minimal()

print(L15_resist_mutants_comp_1_10_filtered_plot)

patch12 <- L15_resist_mutants_comp_bins_plot / L15_resist_mutants_comp_1_10_filtered_plot
patch12

Mutation Ratio

To calculate the ratio of single mutations (where mutations == 1) to the number of mutants with 2 to 10 mutations, you can follow these steps in R: - Count the number of unique mutants with exactly one mutation. - Count the number of unique mutants with mutations between 2 and 10. - Calculate the ratio.

### Comp

# Count single mutations (mutations == 1)
single_mutations_comp_count <- L15_resist_mutants_comp %>%
  filter(mutations == 1) %>%
  pull(mutID) %>%
  n_distinct()

# Count mutants with mutations between 2 and 10
mutants_2_to_10_comp_count <- L15_resist_mutants_comp %>%
  filter(mutations >= 2 & mutations <= 10) %>%
  pull(mutID) %>%
  n_distinct()

# Calculate the ratio
if (mutants_2_to_10_comp_count > 0) {
  mutation_ratio_comp <- single_mutations_comp_count / mutants_2_to_10_comp_count
} else {
  mutation_ratio_comp <- NA # Avoid division by zero if there are no mutants with 2-10 mutations
}

### MIC

# Count single mutations (mutations == 1)
single_mutations_mic_count <- L15_resist_mutants_mic %>%
  filter(mutations == 1) %>%
  pull(mutID) %>%
  n_distinct()

# Count mutants with mutations between 2 and 10
mutants_2_to_10_mic_count <- L15_resist_mutants_mic %>%
  filter(mutations >= 2 & mutations <= 10) %>%
  pull(mutID) %>%
  n_distinct()

# Calculate the ratio
if (mutants_2_to_10_mic_count > 0) {
  mutation_ratio_mic <- single_mutations_mic_count / mutants_2_to_10_mic_count
} else {
  mutation_ratio_mic <- NA # Avoid division by zero if there are no mutants with 2-10 mutations
}

### 400x MIC

# Count single mutations (mutations == 1)
single_mutations_400xmic_count <- L15_resist_mutants_400xmic %>%
  filter(mutations == 1) %>%
  pull(mutID) %>%
  n_distinct()

# Count mutants with mutations between 2 and 10
mutants_2_to_10_400xmic_count <- L15_resist_mutants_400xmic %>%
  filter(mutations >= 2 & mutations <= 10) %>%
  pull(mutID) %>%
  n_distinct()

# Calculate the ratio
if (mutants_2_to_10_400xmic_count > 0) {
  mutation_ratio_400xmic <- single_mutations_400xmic_count / mutants_2_to_10_400xmic_count
} else {
  mutation_ratio_400xmic <- NA # Avoid division by zero if there are no mutants with 2-10 mutations
}



# Print the result
print(paste("The ratio of single mutations to mutants with 2-10 mutations at Complementation is:", mutation_ratio_comp))
[1] "The ratio of single mutations to mutants with 2-10 mutations at Complementation is: 2.53315824031517"
print(paste("The ratio of single mutations to mutants with 2-10 mutations at MIC is:", mutation_ratio_mic))
[1] "The ratio of single mutations to mutants with 2-10 mutations at MIC is: 2.48011363636364"
print(paste("The ratio of single mutations to mutants with 2-10 mutations at 400x MIC is:", mutation_ratio_400xmic))
[1] "The ratio of single mutations to mutants with 2-10 mutations at 400x MIC is: 2.93589743589744"

Save Mutants Files

Save the formatted mutants files to import for downstream analyses

# mut_collapse_15
write.csv(mut_collapse_15, 
          "Mutants/mutants_files_formatted/mut_collapse_15.csv", row.names = FALSE)

# mut_collapse_15_good_filtered (2.5 MB)
write.csv(mut_collapse_15_good_filtered, 
          "Mutants/mutants_files_formatted/mut_collapse_15_good_filtered.csv", row.names = FALSE)

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

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

# orginfo (2.1 MB)
write.csv(orginfo, 
          "Mutants/mutants_files_formatted/orginfo.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
 ade4               1.7-22     2023-02-06 [1] CRAN (R 4.3.0)
 ape              * 5.8        2024-04-11 [1] CRAN (R 4.3.1)
 aplot              0.2.2      2023-10-06 [1] CRAN (R 4.3.1)
 bio3d            * 2.4-5      2024-10-29 [1] CRAN (R 4.3.3)
 BiocGenerics     * 0.46.0     2023-06-04 [1] Bioconductor
 Biostrings       * 2.68.1     2023-05-21 [1] Bioconductor
 bitops             1.0-7      2021-04-24 [1] CRAN (R 4.3.0)
 cachem             1.0.8      2023-05-01 [1] CRAN (R 4.3.0)
 castor           * 1.8.0      2024-01-09 [1] CRAN (R 4.3.1)
 cli                3.6.2      2023-12-11 [1] CRAN (R 4.3.1)
 codetools          0.2-20     2024-03-31 [1] CRAN (R 4.3.1)
 colorspace         2.1-0      2023-01-23 [1] CRAN (R 4.3.0)
 cowplot          * 1.1.3      2024-01-22 [1] CRAN (R 4.3.1)
 crayon             1.5.2      2022-09-29 [1] CRAN (R 4.3.0)
 devtools         * 2.4.5      2022-10-11 [1] CRAN (R 4.3.0)
 digest             0.6.35     2024-03-11 [1] CRAN (R 4.3.1)
 dplyr            * 1.1.4      2023-11-17 [1] CRAN (R 4.3.1)
 ellipsis           0.3.2      2021-04-29 [1] CRAN (R 4.3.0)
 evaluate           0.23       2023-11-01 [1] CRAN (R 4.3.1)
 fansi              1.0.6      2023-12-08 [1] CRAN (R 4.3.1)
 farver             2.1.1      2022-07-06 [1] CRAN (R 4.3.0)
 fastmap            1.1.1      2023-02-24 [1] CRAN (R 4.3.0)
 foreach            1.5.2      2022-02-02 [1] CRAN (R 4.3.0)
 fs                 1.6.3      2023-07-20 [1] CRAN (R 4.3.0)
 generics           0.1.3      2022-07-05 [1] CRAN (R 4.3.0)
 GenomeInfoDb     * 1.36.4     2023-10-08 [1] Bioconductor
 GenomeInfoDbData   1.2.10     2023-09-13 [1] Bioconductor
 ggExtra          * 0.10.1     2023-08-21 [1] CRAN (R 4.3.0)
 ggfun              0.1.4      2024-01-19 [1] CRAN (R 4.3.1)
 ggnewscale       * 0.4.10     2024-02-08 [1] CRAN (R 4.3.1)
 ggplot2          * 3.5.1      2024-04-23 [1] CRAN (R 4.3.1)
 ggplotify          0.1.2      2023-08-09 [1] CRAN (R 4.3.0)
 ggridges         * 0.5.6      2024-01-23 [1] CRAN (R 4.3.1)
 ggtree           * 3.8.2      2023-07-30 [1] Bioconductor
 ggtreeExtra      * 1.10.0     2023-04-25 [1] Bioconductor
 glmnet           * 4.1-8      2023-08-22 [1] CRAN (R 4.3.0)
 glue               1.7.0      2024-01-09 [1] CRAN (R 4.3.1)
 gridExtra        * 2.3        2017-09-09 [1] CRAN (R 4.3.0)
 gridGraphics       0.5-1      2020-12-13 [1] CRAN (R 4.3.0)
 gtable             0.3.5      2024-04-22 [1] CRAN (R 4.3.1)
 htmltools          0.5.8.1    2024-04-04 [1] CRAN (R 4.3.1)
 htmlwidgets        1.6.4      2023-12-06 [1] CRAN (R 4.3.1)
 httpuv             1.6.15     2024-03-26 [1] CRAN (R 4.3.1)
 igraph           * 2.0.3      2024-03-13 [1] CRAN (R 4.3.1)
 IRanges          * 2.34.1     2023-07-02 [1] Bioconductor
 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)
 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

────────────────────────────────────────────────────────────────────────────────────────────────────────────
LS0tCnRpdGxlOiAiTXV0YW50IEhvbW9sb2dzIEFuYWx5c2lzIgphdXRob3I6ICdBdXRob3JzOiBbS2FybCBKLiBSb21hbm93aWN6XShodHRwczovL2tyb21hbm93aWN6LmdpdGh1Yi5pby8pLCBDYXJtZW4gUmVzbmljaywgU2FtdWVsIFIuIEhpbnRvbiwgQ2FsaW4gUGxlc2EnCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdGhlbWU6IHNwYWNlbGFiCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA1CiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogeWVzCiAgICAgIHNtb290aF9zY3JvbGw6IHllcwogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAnNScKICAgIGRmX3ByaW50OiBwYWdlZAogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6ICc1JwotLS0KCioqUiBOb3RlYm9vazoqKiA8Zm9udCBjb2xvcj0iZ3JlZW4iPlByb3ZpZGVzIHJlcHJvZHVjaWJsZSBhbmFseXNpcyBmb3IgKipNdXRhbnQgVmFyaWFudHMqKiBpbiB0aGUgZm9sbG93aW5nIG1hbnVzY3JpcHQ6PC9mb250PgoKKipDaXRhdGlvbjoqKiBSb21hbm93aWN6IEtKLCBSZXNuaWNrIEMsIEhpbnRvbiBTUiwgUGxlc2EgQy4gRXhwbG9yaW5nIGFudGliaW90aWMgcmVzaXN0YW5jZSBpbiBkaXZlcnNlIGhvbW9sb2dzIG9mIHRoZSBkaWh5ZHJvZm9sYXRlIHJlZHVjdGFzZSBwcm90ZWluIGZhbWlseSB0aHJvdWdoIGJyb2FkIG11dGF0aW9uYWwgc2Nhbm5pbmcuICoqKmJpb1J4aXYqKiosIDIwMjUuIFtdKCkKCioqR2l0SHViIFJlcG9zaXRvcnk6KiogW2h0dHBzOi8vZ2l0aHViLmNvbS9QbGVzYUxhYi9ESEZSXShodHRwczovL2dpdGh1Yi5jb20vUGxlc2FMYWIvREhGUikKCioqTkNCSSBCaW9Qcm9qZWN0OioqIFtodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L2Jpb3Byb2plY3QvMTE4OTQ3OF0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9iaW9wcm9qZWN0LzExODk0NzgpCgojIEV4cGVyaW1lbnQKClRoaXMgcGlwZWxpbmUgcHJvY2Vzc2VzIGEgbGlicmFyeSBvZiAxLDUzNiBESEZSIGhvbW9sb2dzIGFuZCB0aGVpciBhc3NvY2lhdGVkIG11dGFudHMsIHdpdGggdHdvLWZvbGQgcmVkdW5kYW5jeSAodHdvIGNvZG9uIHZhcmlhbnRzIHBlciBzZXF1ZW5jZSkuIEZpdG5lc3Mgc2NvcmVzIGFyZSBkZXJpdmVkIGZyb20gYSBtdWx0aXBsZXhlZCBpbi12aXZvIGFzc2F5IHVzaW5nIGEgdHJpbWV0aG9wcmltIGNvbmNlbnRyYXRpb24gZ3JhZGllbnQsIGFzc2Vzc2luZyB0aGUgYWJpbGl0eSBvZiB0aGVzZSBob21vbG9ncyBhbmQgdGhlaXIgbXV0YW50cyB0byBjb21wbGVtZW50IGZ1bmN0aW9uYWxpdHkgaW4gYW4gKkUuIGNvbGkqIGtub2Nrb3V0IHN0cmFpbiBhbmQgdGhlaXIgdG9sZXJhbmNlIHRvIHRyaW1ldGhvcHJpbSB0cmVhdG1lbnQuIFRoaXMgYW5hbHlzaXMgcHJvdmlkZXMgaW5zaWdodHMgaW50byBob3cgYW50aWJpb3RpYyByZXNpc3RhbmNlIGV2b2x2ZXMgYWNyb3NzIGEgcmFuZ2Ugb2YgZXZvbHV0aW9uYXJ5IHN0YXJ0aW5nIHBvaW50cy4gU2VxdWVuY2UgZGF0YSB3ZXJlIGdlbmVyYXRlZCB1c2luZyB0aGUgSWxsdW1pbmEgTm92YVNlcSBwbGF0Zm9ybSB3aXRoIDEwMCBicCBwYWlyZWQtZW5kIHNlcXVlbmNpbmcgb2YgYW1wbGljb25zLgoKIVtNZXRob2RzIG92ZXJ2aWV3IHRvIGFjaGlldmUgYSBicm9hZC1tdXRhdGlvbmFsIHNjYW4gZm9yIERIRlIgaG9tb2xvZ3MuXShJbWFnZXMvREhGUi5EaWFncmFtLnBuZykKCmBgYHtjc3N9Ci5iYWRDb2RlIHsKYmFja2dyb3VuZC1jb2xvcjogbGlnaHRwaW5rOwpmb250LXdlaWdodDogYm9sZDsKfQoKLmdvb2RDb2RlIHsKYmFja2dyb3VuZC1jb2xvcjogbGlnaHRncmVlbjsKZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KCi5zaGFyZWRDb2RlIHsKYmFja2dyb3VuZC1jb2xvcjogbGlnaHRibHVlOwpmb250LXdlaWdodDogYm9sZDsKfQoKdGFibGUgewogIG1hcmdpbjogYXV0bzsKICBib3JkZXItdG9wOiAxcHggc29saWQgIzY2NjsKICBib3JkZXItYm90dG9tOiAxcHggc29saWQgIzY2NjsKfQp0YWJsZSB0aGVhZCB0aCB7IGJvcmRlci1ib3R0b206IDFweCBzb2xpZCAjZGRkOyB9CnRoLCB0ZCB7IHBhZGRpbmc6IDVweDsgfQp0aGVhZCwgdGZvb3QsIHRyOm50aC1jaGlsZChldmVuKSB7IGJhY2tncm91bmQ6ICNlZWU7IH0KYGBgCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KIyBTZXQgZ2xvYmFsIG9wdGlvbnMgZm9yIG5vdGVib29rCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KHdhcm5pbmcgPSBUUlVFLCBtZXNzYWdlID0gVFJVRSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBjbGFzcy5zb3VyY2UgPSAiYmctc3VjY2VzcyIpCgojIEdldHRpbmcgdGhlIHBhdGggb2YgeW91ciBjdXJyZW50IG9wZW4gZmlsZSBhbmQgc2V0IGFzIHdkCmN1cnJlbnRfcGF0aCA9IHJzdHVkaW9hcGk6OmdldEFjdGl2ZURvY3VtZW50Q29udGV4dCgpJHBhdGggCnNldHdkKGRpcm5hbWUoY3VycmVudF9wYXRoKSkKcHJpbnQoZ2V0d2QoKSkKYGBgCgojIFBhY2thZ2VzClRoZSBmb2xsb3dpbmcgUiBwYWNrYWdlcyBtdXN0IGJlIGluc3RhbGxlZCBwcmlvciB0byBsb2FkaW5nIGludG8gdGhlIFIgc2Vzc2lvbi4gU2VlIHRoZSAqKlJlcHJvZHVjaWJpbGl0eSoqIHRhYiBmb3IgYSBjb21wbGV0ZSBsaXN0IG9mIHBhY2thZ2VzIGFuZCB0aGVpciB2ZXJzaW9ucyB1c2VkIGluIHRoaXMgd29ya2Zsb3cuCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQojIExvYWQgdGhlIGxhdGVzdCB2ZXJzaW9uIG9mIHB5dGhvbiAoMy4xMC4xNCkgZm9yIGRvd25zdHJlYW0gdXNlOgpsaWJyYXJ5KHJldGljdWxhdGUpCnVzZV9weXRob24oIi9Vc2Vycy9rcm9tL21pbmlmb3JnZTMvYmluL3B5dGhvbjMiKQoKIyBNYWtlIGEgdmVjdG9yIG9mIHJlcXVpcmVkIHBhY2thZ2VzCnJlcXVpcmVkLnBhY2thZ2VzIDwtIGMoImFwZSIsICJiaW8zZCIsICJCaW9zdHJpbmdzIiwgImNhc3RvciIsICJjb3dwbG90IiwgImRldnRvb2xzIiwgImRwbHlyIiwgImdnRXh0cmEiLCAiZ2duZXdzY2FsZSIsICJnZ3Bsb3QyIiwgImdncmlkZ2VzIiwgImdndHJlZSIsICJnZ3RyZWVFeHRyYSIsICJnbG1uZXQiLCAiZ3JpZEV4dHJhIiwiaWdyYXBoIiwgImtuaXRyIiwgIm1hdHJpeFN0YXRzIiwgInBhdGNod29yayIsICJwaGVhdG1hcCIsICJwdXJyciIsICJwc2NsIiwgIlJDb2xvckJyZXdlciIsICJyZXNoYXBlIiwicmVzaGFwZTIiLCAiUk9DUiIsICJzZXFpbnIiLCAic2NhbGVzIiwgInN0cmluZ3IiLCAic3RyaW5naSIsICJ0aWR5ciIsICJ0aWR5dHJlZSIsICJ2aXJpZGlzIikKCiMgTG9hZCByZXF1aXJlZCBwYWNrYWdlcyB3aXRoIGVycm9yIGhhbmRsaW5nCmxvYWRlZC5wYWNrYWdlcyA8LSBsYXBwbHkocmVxdWlyZWQucGFja2FnZXMsIGZ1bmN0aW9uKHBhY2thZ2UpIHsKICBpZiAoIXJlcXVpcmUocGFja2FnZSwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKSkgewogICAgaW5zdGFsbC5wYWNrYWdlcyhwYWNrYWdlLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQogICAgaWYgKCFyZXF1aXJlKHBhY2thZ2UsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpIHsKICAgICAgbWVzc2FnZSgiUGFja2FnZSAiLCBwYWNrYWdlLCAiIGNvdWxkIG5vdCBiZSBpbnN0YWxsZWQgYW5kIGxvYWRlZC4iKQogICAgICByZXR1cm4oTlVMTCkKICAgIH0KICB9CiAgcmV0dXJuKHBhY2thZ2UpCn0pCgojIFJlbW92ZSBOVUxMIGVudHJpZXMgZnJvbSBsb2FkZWQgcGFja2FnZXMKbG9hZGVkLnBhY2thZ2VzIDwtIGxvYWRlZC5wYWNrYWdlc1shc2FwcGx5KGxvYWRlZC5wYWNrYWdlcywgaXMubnVsbCldCmBgYAoKYGBge3IgY2xhc3Mub3V0cHV0PSJzaGFyZWRDb2RlIiwgZWNobz1GQUxTRX0KIyBQcmludCBsb2FkZWQgcGFja2FnZXMKY2F0KCJMb2FkZWQgcGFja2FnZXM6IiwgcGFzdGUobG9hZGVkLnBhY2thZ2VzLCBjb2xsYXBzZSA9ICIsICIpLCAiXG4iKQpgYGAKCmBgYHtyIGluY2x1ZGU9RkFMU0V9CiMgc2V0LnNlZWQgaXMgdXNlZCB0byBmaXggdGhlIHJhbmRvbSBudW1iZXIgZ2VuZXJhdGlvbiB0byBtYWtlIHRoZSByZXN1bHRzIHJlcGVhdGFibGUKc2V0LnNlZWQoMTIzKQpgYGAKCiMgSW1wb3J0IERhdGEgRmlsZXMKCkltcG9ydCAqKlBFUkZFQ1RTKiogZmlsZXMgZ2VuZXJhdGVkIGZyb20gW0RIRlIuMy5QZXJmZWN0cy5STURdKGh0dHBzOi8vZ2l0aHViLmNvbS9QbGVzYUxhYi9ESEZSKQpgYGB7cn0KIyMjIEJDc19tYXAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgQkNzMTVfbWFwCkJDczE1X21hcCA8LSByZWFkLmNzdigiUGVyZmVjdHMvcGVyZmVjdHNfZmlsZXNfZm9ybWF0dGVkL0JDczE1X21hcC5jc3YiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCiMgQkNzMTZfbWFwCkJDczE2X21hcCA8LSByZWFkLmNzdigiUGVyZmVjdHMvcGVyZmVjdHNfZmlsZXNfZm9ybWF0dGVkL0JDczE2X21hcC5jc3YiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCiMjIyBtdXRJRGluZm8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgbXV0SURpbmZvMTUKbXV0SURpbmZvMTUgPC0gcmVhZC5jc3YoIlBlcmZlY3RzL3BlcmZlY3RzX2ZpbGVzX2Zvcm1hdHRlZC9tdXRJRGluZm8xNS5jc3YiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCiMgbXV0SURpbmZvMTYKbXV0SURpbmZvMTYgPC0gcmVhZC5jc3YoIlBlcmZlY3RzL3BlcmZlY3RzX2ZpbGVzX2Zvcm1hdHRlZC9tdXRJRGluZm8xNi5jc3YiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCiMjIyBwZXJmZWN0c181QkNzLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgcGVyZmVjdHMxNV81QkNzCnBlcmZlY3RzMTVfNUJDcyA8LSByZWFkLmNzdigiUGVyZmVjdHMvcGVyZmVjdHNfZmlsZXNfZm9ybWF0dGVkL3BlcmZlY3RzMTVfNUJDcy5jc3YiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCiMgcGVyZmVjdHMxNl81QkNzCnBlcmZlY3RzMTZfNUJDcyA8LSByZWFkLmNzdigiUGVyZmVjdHMvcGVyZmVjdHNfZmlsZXNfZm9ybWF0dGVkL3BlcmZlY3RzMTZfNUJDcy5jc3YiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCiMgcGVyZmVjdHNfMTVfMTZfNUJDc190cmVlCnBlcmZlY3RzXzE1XzE2XzVCQ3NfdHJlZSA8LSByZWFkLmNzdigiUGVyZmVjdHMvcGVyZmVjdHNfZmlsZXNfZm9ybWF0dGVkL3BlcmZlY3RzXzE1XzE2XzVCQ3NfdHJlZS5jc3YiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCiMjIyBCQ2NvbnRyb2xzX3NoYXJlZF9tZWRpYW4tLS0tLS0tLS0tLS0tLS0KCiMgQkNjb250cm9sc18xNV8xNl9zaGFyZWRfbWVkaWFuX1dUCkJDY29udHJvbHNfMTVfMTZfc2hhcmVkX21lZGlhbl9XVCA8LSByZWFkLmNzdigiUGVyZmVjdHMvcGVyZmVjdHNfZmlsZXNfZm9ybWF0dGVkL0JDY29udHJvbHNfMTVfMTZfc2hhcmVkX21lZGlhbl9XVC5jc3YiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCiMgQkNjb250cm9sc18xNV8xNl9zaGFyZWRfbWVkaWFuX05lZwpCQ2NvbnRyb2xzXzE1XzE2X3NoYXJlZF9tZWRpYW5fTmVnIDwtIHJlYWQuY3N2KCJQZXJmZWN0cy9wZXJmZWN0c19maWxlc19mb3JtYXR0ZWQvQkNjb250cm9sc18xNV8xNl9zaGFyZWRfbWVkaWFuX05lZy5jc3YiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCiMjIyBNaXNjZWxsYW5lb3VzLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBvcmdpbmZvCm9yZ2luZm8gPC0gcmVhZC5jc3YoIlBlcmZlY3RzL3BlcmZlY3RzX2ZpbGVzX2Zvcm1hdHRlZC9vcmdpbmZvLmNzdiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKIyBBbGx0cmVlMTVfdGF4YV9tZXJnZWQKQWxsdHJlZTE1X3RheGFfbWVyZ2VkIDwtIHJlYWQuY3N2KCJQZXJmZWN0cy9wZXJmZWN0c19maWxlc19mb3JtYXR0ZWQvQWxsdHJlZTE1X3RheGFfbWVyZ2VkLmNzdiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpgYGAKCiMgTXV0YW50cyBEYXRhIEFuYWx5c2lzCgojIyBGaXRuZXNzIHZzIERpc3RhbmNlCgo8Zm9udCBjb2xvcj0iYmx1ZSI+KipUaGlzIHNlY3Rpb24gaXMgYmFzZWQgb24gdGhlIFIgZmlsZTogIlJfY2hhbmdlX2luX2ZpdG5lc3NfdnNfZGlzdGFuY2UuUiIuKio8L2ZvbnQ+IEl0IGRldGVybWluZXMgaG93IGZpdG5lc3MgY2hhbmdlcyB3aXRoIG11dGF0aW9uIGRpc3RhbmNlLgoKIyMjIE11dGFudCBTdW1tYXJ5CgpUaGUgZmlyc3QgdGhpbmcgd2UgbmVlZCB0byBkbyBpcyBzdW1tYXJpemUgdGhlIG11dGFudCBkYXRhc2V0IGFzc29jaWF0ZWQgd2l0aCBlYWNoIGxpYnJhcnkuIFVzaW5nIHRoZSBgQkNfbWFwYCBkYXRhc2V0cywgc3VtbWFyaXplIHRoZSBudW1iZXIgb2YgdW5pcXVlIG11dGFudHMgKG11dElEKSBhdCBlYWNoIHNhbXBsaW5nIGNvbmRpdGlvbi4gU2Vjb25kLCB3ZSdsbCBjYWxjdWxhdGUgdGhlIG1lZGlhbiBudW1iZXIgb2YgdW5pcXVlIG11dGFudHMgYXNzb2NpYXRlZCB3aXRoIHVuaXF1ZSBob21vbG9ncyBhdCBldmVyeSBzYW1wbGluZyBjb25kaXRpb24uIFRoZW4sIHdlJ2xsIGNhbGN1bGF0ZSB0aGUgbnVtYmVyIG9mIHJhdyBzZXF1ZW5jZSBjb3VudHMgYXNzb2NpYXRlZCB3aXRoIG11dGFudHMgYXQgbnVtZXJvdXMgbXV0YXRpb24gbGV2ZWxzIGFuZCB0aGVpciBwZXJjZW50YWdlIG9mIHRvdGFsIG11dGFudHMgZm9yIGVhY2ggY29kb24gdmVyc2lvbiBmb3IgdGhlIGZvbGxvd2luZyBsZXZlbHM6IDAgbXV0YW50cywgMSBtdXRhbnQsIDItNSBtdXRhbnRzLCA2LTUwIG11dGFudHMsIDUxLTEwMCBtdXRhbnRzLCAxMDArIG11dGFudHMuCgojIyMjIExpYjE1CgoqKlVuaXF1ZSBNdXRhbnRzOioqIENhbGN1bGF0ZSB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBtdXRhbnRzIG1hcHBlZCBhY3Jvc3MgYWxsIG5pbmUgY29uZGl0aW9ucy4gVGhlbiByZS1jYWxjdWxhdGUgdG8gb25seSBpbmNsdWRlIG11dGFudHMgd2l0aCAxLTUgYW1pbm8gYWNpZCBjaGFuZ2VzOgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBVbmlxdWUgTXV0YW50cwpsZW5ndGgodW5pcXVlKEJDczE1X21hcCRtdXRJRFtCQ3MxNV9tYXAkbXV0YXRpb25zID4gMF0pKQoKIyBVbmlxdWUgTXV0YW50cyAod2l0aCAxLTUgbXV0YXRpb25zKQpsZW5ndGgodW5pcXVlKEJDczE1X21hcCRtdXRJRFtCQ3MxNV9tYXAkbXV0YXRpb25zID4gMCAmIEJDczE1X21hcCRtdXRhdGlvbnMgPCA2XSkpCmBgYAoKCioqTXV0YW50cyBwZXIgVHJlYXRtZW50OioqIENhbGN1bGF0ZSB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBtdXRhbnRzIChtdXRJRCkgcmVjb3ZlcmVkIGZyb20gZWFjaCBzYW1wbGluZyBjb25kaXRpb246CmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIERlZmluZSB0aGUgdHJlYXRtZW50cwpMMTUubXV0SUQubXV0YW50cy50cmVhdG1lbnRzIDwtIGMoIkxCIiwgIk05LCBGdWxsIFN1cHBsZW1lbnQiLCAiTTksIENvbXBsZW1lbnRhdGlvbiIsICJNOSwgMC4wNTgtVE1QIiwgCiAgICAgICAgICAgICAgICAiTTksIDAuNS1UTVAiLCAiTTksIDEuMC1UTVAiLCAiTTksIDEwLVRNUCIsICJNOSwgNTAtVE1QIiwgIk05LCAyMDAtVE1QIikKCiMgQ2FsY3VsYXRlIHVuaXF1ZSBJRGFsaWduIGNvdW50cwpMMTUubXV0SUQubXV0YW50cy5yZXN1bHQgPC0gQkNzMTVfbWFwICU+JQogIGZpbHRlcihtdXRhdGlvbnMgPiAwKSAlPiUKICBzdW1tYXJpc2UoCiAgICBEMDEgPSBuX2Rpc3RpbmN0KG11dElEWyFpcy5uYShEMDEpXSksCiAgICBEMDMgPSBuX2Rpc3RpbmN0KG11dElEWyFpcy5uYShEMDMpXSksCiAgICBEMDUgPSBuX2Rpc3RpbmN0KG11dElEWyFpcy5uYShEMDUpXSksCiAgICBEMDYgPSBuX2Rpc3RpbmN0KG11dElEWyFpcy5uYShEMDYpXSksCiAgICBEMDcgPSBuX2Rpc3RpbmN0KG11dElEWyFpcy5uYShEMDcpXSksCiAgICBEMDggPSBuX2Rpc3RpbmN0KG11dElEWyFpcy5uYShEMDgpXSksCiAgICBEMDkgPSBuX2Rpc3RpbmN0KG11dElEWyFpcy5uYShEMDkpXSksCiAgICBEMTAgPSBuX2Rpc3RpbmN0KG11dElEWyFpcy5uYShEMTApXSksCiAgICBEMTEgPSBuX2Rpc3RpbmN0KG11dElEWyFpcy5uYShEMTEpXSkpCgojIFRyYW5zZm9ybSB0aGUgcmVzdWx0IHRvIGEgbW9yZSByZWFkYWJsZSBmb3JtYXQKTDE1Lm11dElELm11dGFudHMucmVzdWx0X3RhYmxlIDwtIHRpYmJsZSgKICBUcmVhdG1lbnQgPSBMMTUubXV0SUQubXV0YW50cy50cmVhdG1lbnRzLAogIGBVbmlxdWUgbXV0SUQgQ291bnRgID0gYXMubnVtZXJpYyhMMTUubXV0SUQubXV0YW50cy5yZXN1bHRbMSxdKSkKCiMgUHJpbnQgdGhlIHRhYmxlCnByaW50KEwxNS5tdXRJRC5tdXRhbnRzLnJlc3VsdF90YWJsZSwgbiA9IEluZikKYGBgCgoqKk1lZGlhbiBNdXRhbnRzIHBlciBIb21vbG9nOioqIENhbGN1bGF0ZSB0aGUgbWVkaWFuIG51bWJlciBvZiB1bmlxdWUgbXV0YW50cyAobXV0SUQpIGFzc29jaWF0ZWQgd2l0aCB1bmlxdWUgaG9tb2xvZ3MgKElEYWxpZ24pIHJlY292ZXJlZCBmcm9tIGVhY2ggc2FtcGxpbmcgY29uZGl0aW9uOgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBEZWZpbmUgdGhlIHRyZWF0bWVudHMKTDE1Lm11dElELm11dGFudHMubWVkaWFuLnRyZWF0bWVudHMgPC0gYygiTEIiLCAiTTksIEZ1bGwgU3VwcGxlbWVudCIsICJNOSwgQ29tcGxlbWVudGF0aW9uIiwgIk05LCAwLjA1OC1UTVAiLCAKICAgICAgICAgICAgICAgICJNOSwgMC41LVRNUCIsICJNOSwgMS4wLVRNUCIsICJNOSwgMTAtVE1QIiwgIk05LCA1MC1UTVAiLCAiTTksIDIwMC1UTVAiKQoKTDE1Lm11dElELm11dGFudHMubWVkaWFuLnNhbXBsZUlEIDwtIGMoIkQwMSIsICJEMDMiLCAiRDA1IiwgIkQwNiIsICJEMDciLCAiRDA4IiwgIkQwOSIsICJEMTAiLCAiRDExIikKCiMgQ2FsY3VsYXRlIG1lZGlhbiB1bmlxdWUgbXV0SUQgKG11dGF0aW9ucyA+IDApIHBlciB1bmlxdWUgSURhbGlnbiAobXV0YXRpb25zID0gMCkKTDE1Lm11dElELm11dGFudHMubWVkaWFuLnJlc3VsdCA8LSBCQ3MxNV9tYXAgJT4lCiAgZ3JvdXBfYnkoSURhbGlnbikgJT4lCiAgc3VtbWFyaXNlKAogICAgRDAxPWlmKGFueShtdXRhdGlvbnM9PTAgJiAhaXMubmEoRDAxKSkpIG1lZGlhbihuX2Rpc3RpbmN0KG11dElEW211dGF0aW9ucz4wICYgIWlzLm5hKEQwMSldKSkgZWxzZSBOQV9yZWFsXywKICAgIEQwMz1pZihhbnkobXV0YXRpb25zPT0wICYgIWlzLm5hKEQwMykpKSBtZWRpYW4obl9kaXN0aW5jdChtdXRJRFttdXRhdGlvbnM+MCAmICFpcy5uYShEMDMpXSkpIGVsc2UgTkFfcmVhbF8sCiAgICBEMDU9aWYoYW55KG11dGF0aW9ucz09MCAmICFpcy5uYShEMDUpKSkgbWVkaWFuKG5fZGlzdGluY3QobXV0SURbbXV0YXRpb25zPjAgJiAhaXMubmEoRDA1KV0pKSBlbHNlIE5BX3JlYWxfLAogICAgRDA2PWlmKGFueShtdXRhdGlvbnM9PTAgJiAhaXMubmEoRDA2KSkpIG1lZGlhbihuX2Rpc3RpbmN0KG11dElEW211dGF0aW9ucz4wICYgIWlzLm5hKEQwNildKSkgZWxzZSBOQV9yZWFsXywKICAgIEQwNz1pZihhbnkobXV0YXRpb25zPT0wICYgIWlzLm5hKEQwNykpKSBtZWRpYW4obl9kaXN0aW5jdChtdXRJRFttdXRhdGlvbnM+MCAmICFpcy5uYShEMDcpXSkpIGVsc2UgTkFfcmVhbF8sCiAgICBEMDg9aWYoYW55KG11dGF0aW9ucz09MCAmICFpcy5uYShEMDgpKSkgbWVkaWFuKG5fZGlzdGluY3QobXV0SURbbXV0YXRpb25zPjAgJiAhaXMubmEoRDA4KV0pKSBlbHNlIE5BX3JlYWxfLAogICAgRDA5PWlmKGFueShtdXRhdGlvbnM9PTAgJiAhaXMubmEoRDA5KSkpIG1lZGlhbihuX2Rpc3RpbmN0KG11dElEW211dGF0aW9ucz4wICYgIWlzLm5hKEQwOSldKSkgZWxzZSBOQV9yZWFsXywKICAgIEQxMD1pZihhbnkobXV0YXRpb25zPT0wICYgIWlzLm5hKEQxMCkpKSBtZWRpYW4obl9kaXN0aW5jdChtdXRJRFttdXRhdGlvbnM+MCAmICFpcy5uYShEMTApXSkpIGVsc2UgTkFfcmVhbF8sCiAgICBEMTE9aWYoYW55KG11dGF0aW9ucz09MCAmICFpcy5uYShEMTEpKSkgbWVkaWFuKG5fZGlzdGluY3QobXV0SURbbXV0YXRpb25zPjAgJiAhaXMubmEoRDExKV0pKSBlbHNlIE5BX3JlYWxfKSAlPiUKICBzdW1tYXJpc2UoYWNyb3NzKHN0YXJ0c193aXRoKCJEIiksIH5tZWRpYW4oLiwgbmEucm0gPSBUUlVFKSkpICAjIE9ubHkgc3VtbWFyaXplIEQqIGNvbHVtbnMKCiMgVHJhbnNmb3JtIHRoZSByZXN1bHQgdG8gYSBtb3JlIHJlYWRhYmxlIGZvcm1hdApMMTUubXV0SUQubXV0YW50cy5tZWRpYW4ucmVzdWx0X3RhYmxlIDwtIHRpYmJsZSgKICBTYW1wbGVJRCA9IEwxNS5tdXRJRC5tdXRhbnRzLm1lZGlhbi5zYW1wbGVJRCwKICBUcmVhdG1lbnQgPSBMMTUubXV0SUQubXV0YW50cy5tZWRpYW4udHJlYXRtZW50cywKICBgTWVkaWFuIFVuaXF1ZSBtdXRJRCBwZXIgVW5pcXVlIElEYWxpZ25gID0gYXMubnVtZXJpYyhMMTUubXV0SUQubXV0YW50cy5tZWRpYW4ucmVzdWx0WzEsXSkpCgojIFByaW50IHRoZSB0YWJsZQpwcmludChMMTUubXV0SUQubXV0YW50cy5tZWRpYW4ucmVzdWx0X3RhYmxlLCBuID0gSW5mKQpgYGAKClZhbGlkYXRlIHRoZSBtZWRpYW4gbXV0YW50cyAobXV0SUQpIHBlciBob21vbG9neSAoSURhbGlnbikgZm9yIENvbXBsZW1lbnRhdGlvbiAoRDA1KToKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgQ2FsY3VsYXRlIGludGVybWVkaWF0ZSByZXN1bHRzCkQwNV92YWxpZGF0ZV9yZXN1bHQgPC0gQkNzMTVfbWFwICU+JQogIGdyb3VwX2J5KElEYWxpZ24pICU+JQogIHN1bW1hcmlzZSgKICAgIEQwNV9tdXRhbnRzX2NvdW50ID0gc3VtKG11dGF0aW9ucyA+IDAgJiAhaXMubmEoRDA1KSksCiAgICBEMDVfbm9uX211dGFudHNfY291bnQgPSBzdW0obXV0YXRpb25zID09IDAgJiAhaXMubmEoRDA1KSksCiAgICBEMDVfdW5pcXVlX211dElEX2NvdW50ID0gbl9kaXN0aW5jdChtdXRJRFttdXRhdGlvbnMgPiAwICYgIWlzLm5hKEQwNSldKSkKCiMgUmVtb3ZlIHJvd3Mgd2hlcmUgRDAxX25vbl9tdXRhbnRzX2NvdW50ID09IDAKRDA1X2ZpbHRlcmVkIDwtIEQwNV92YWxpZGF0ZV9yZXN1bHQgJT4lCiAgZmlsdGVyKEQwNV9ub25fbXV0YW50c19jb3VudCA+IDApCgojIFByaW50IGZ1bGwgcmVzdWx0cyBzaG93aW5nIG1lZGlhbiBtdXRJRCBmb3IgZWFjaCBJRGFsaWduIGluIEQwNToKcHJpbnQoIkZ1bGwgSURhbGlnbiByZXN1bHRzIGZvciBEMDU6IikKcHJpbnQoRDA1X2ZpbHRlcmVkLCBuID0gSW5mKQoKIyBTYXZlIGEgY29weSBhcyBhIHNwcmVhZHNoZWV0CndyaXRlLmNzdihEMDVfZmlsdGVyZWQsICJNdXRhbnRzL09VVFBVVC9MMTUuRDA1Lm1lZGlhbi5tdXRJRC5wZXIuSURhbGlnbi5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCiMgUHJpbnQgc3VtbWFyeSByZXN1bHRzIG9mIG1lZGlhbiBtdXRJRCBwZXIgSURhbGlnbiBpbiBEMDU6CnByaW50KCJTdW1tYXJ5IG9mIGZpbHRlcmVkIHJlc3VsdHM6IikKcHJpbnQoc3VtbWFyeShEMDVfZmlsdGVyZWQpKQoKIyBDYWxjdWxhdGUgbWVkaWFuIHVzaW5nIHRoZSBmaWx0ZXJlZCBkYXRhCm1lZGlhbl9tdXRhbnRzX0QwNSA8LSBtZWRpYW4oRDA1X2ZpbHRlcmVkJEQwNV91bmlxdWVfbXV0SURfY291bnQsIG5hLnJtID0gVFJVRSkKCnByaW50KHBhc3RlKCJNZWRpYW4gbnVtYmVyIG9mIHVuaXF1ZSBtdXRhbnRzIHBlciBJRGFsaWduIGZvciBEMDU6IiwgbWVkaWFuX211dGFudHNfRDA1KSkKYGBgCgoqKk11dGFudCBDb3VudHMgYnkgRGlzdGFuY2U6KiogU3VtbWFyaXplIHRoZSByYXcgc2VxdWVuY2UgY291bnRzIGFjcm9zcyBtYXBwZWQgYmFyY29kZXMgYXQgbnVtZXJvdXMgbXV0YXRpb24gbGV2ZWxzOgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBEZWZpbmUgdGhlIGNvbHVtbnMgd2Ugd2FudCB0byBzdW1tYXJpemUKTDE1LmNvbHVtbnNfdG9fc3VtbWFyaXplIDwtIGMoIkQwMSIsICJEMDMiLCAiRDA1IiwgIkQwNiIsICJEMDciLCAiRDA4IiwgIkQwOSIsICJEMTAiLCAiRDExIikKCiMgQ3JlYXRlIGEgZnVuY3Rpb24gdG8gc3VtIHZhbHVlcyBmb3IgbXVsdGlwbGUgY29sdW1ucwpMMTUuc3VtX2NvbHVtbnMgPC0gZnVuY3Rpb24oZGF0YSwgY29uZGl0aW9uX25hbWUpIHsKICBkYXRhICU+JQogICAgc3VtbWFyaXNlKGFjcm9zcyhhbGxfb2YoTDE1LmNvbHVtbnNfdG9fc3VtbWFyaXplKSwgfnN1bSguLCBuYS5ybSA9IFRSVUUpKSkgJT4lCiAgICBtdXRhdGUoY29uZGl0aW9uID0gY29uZGl0aW9uX25hbWUpICU+JQogICAgc2VsZWN0KGNvbmRpdGlvbiwgZXZlcnl0aGluZygpKQp9CgojIFN1bSB2YWx1ZXMgZm9yIGVhY2ggY29uZGl0aW9uCkwxNS5zdW1tYXJ5X2FsbCA8LSBiaW5kX3Jvd3MoCiAgQkNzMTVfbWFwICU+JSBmaWx0ZXIobXV0YXRpb25zID09IDApICU+JSBMMTUuc3VtX2NvbHVtbnMoIm11dGF0aW9ucyA9PSAwIiksCiAgQkNzMTVfbWFwICU+JSBmaWx0ZXIobXV0YXRpb25zID09IDEpICU+JSBMMTUuc3VtX2NvbHVtbnMoIm11dGF0aW9ucyA9PSAxIiksCiAgQkNzMTVfbWFwICU+JSBmaWx0ZXIobXV0YXRpb25zID49IDIgJiBtdXRhdGlvbnMgPD0gNSkgJT4lIEwxNS5zdW1fY29sdW1ucygibXV0YXRpb25zIDItNSIpLAogIEJDczE1X21hcCAlPiUgZmlsdGVyKG11dGF0aW9ucyA+PSA2ICYgbXV0YXRpb25zIDw9IDUwKSAlPiUgTDE1LnN1bV9jb2x1bW5zKCJtdXRhdGlvbnMgNi01MCIpLAogIEJDczE1X21hcCAlPiUgZmlsdGVyKG11dGF0aW9ucyA+PSA1MSAmIG11dGF0aW9ucyA8PSAxMDApICU+JSBMMTUuc3VtX2NvbHVtbnMoIm11dGF0aW9ucyA1MS0xMDAiKSwKICBCQ3MxNV9tYXAgJT4lIGZpbHRlcihtdXRhdGlvbnMgPiAxMDApICU+JSBMMTUuc3VtX2NvbHVtbnMoIm11dGF0aW9ucyA+IDEwMCIpCikKCiMgQWRkIGEgdG90YWwgcm93IHRvIHRoZSBzdW0gdGFibGUKTDE1LnN1bW1hcnlfYWxsX3dpdGhfdG90YWwgPC0gTDE1LnN1bW1hcnlfYWxsICU+JQogIGJpbmRfcm93cyhzdW1tYXJpc2UoLiwgYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCBzdW0pLCBjb25kaXRpb24gPSAiVG90YWwiKSkKCiMgQ2FsY3VsYXRlIHRoZSBwZXJjZW50YWdlIG9mIHRvdGFsIHN1bSBmb3IgZWFjaCBjb2x1bW4KTDE1LnN1bW1hcnlfcGVyY2VudGFnZSA8LSBMMTUuc3VtbWFyeV9hbGwgJT4lCiAgbXV0YXRlKGFjcm9zcyhhbGxfb2YoTDE1LmNvbHVtbnNfdG9fc3VtbWFyaXplKSwgCiAgICAgICAgICAgICAgICB+LiAvIHN1bSguLCBuYS5ybSA9IFRSVUUpICogMTAwLCAKICAgICAgICAgICAgICAgIC5uYW1lcyA9ICJ7Y29sfV9wY3QiKSkKCiMgQWRkIGEgdG90YWwgcm93IHRvIHRoZSBwZXJjZW50YWdlIHRhYmxlICh3aWxsIHN1bSB0byAxMDAgZm9yIGVhY2ggY29sdW1uKQpMMTUuc3VtbWFyeV9wZXJjZW50YWdlX3dpdGhfdG90YWwgPC0gTDE1LnN1bW1hcnlfcGVyY2VudGFnZSAlPiUKICBzZWxlY3QoY29uZGl0aW9uLCBlbmRzX3dpdGgoIl9wY3QiKSkgJT4lCiAgYmluZF9yb3dzKHN1bW1hcmlzZSguLCBhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIHN1bSksIGNvbmRpdGlvbiA9ICJUb3RhbCIpKQoKIyBSb3VuZCB0aGUgdmFsdWVzIGZvciBiZXR0ZXIgcmVhZGFiaWxpdHkKTDE1LnN1bW1hcnlfYWxsX3JvdW5kZWQgPC0gTDE1LnN1bW1hcnlfYWxsX3dpdGhfdG90YWwgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSwgfnJvdW5kKC4sIDIpKSkKCkwxNS5zdW1tYXJ5X3BlcmNlbnRhZ2Vfcm91bmRlZCA8LSBMMTUuc3VtbWFyeV9wZXJjZW50YWdlX3dpdGhfdG90YWwgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSwgfnJvdW5kKC4sIDIpKSkKCiMgUHJpbnQgdGhlIHN1bSB0YWJsZQpjYXQoIlRhYmxlIDE6IFN1bSBvZiB2YWx1ZXMgZm9yIGVhY2ggY29uZGl0aW9uXG4iKQpwcmludChMMTUuc3VtbWFyeV9hbGxfcm91bmRlZCwgbiA9IEluZiwgd2lkdGggPSBJbmYpCgojIFByaW50IHRoZSBwZXJjZW50YWdlIHRhYmxlCmNhdCgiXG5UYWJsZSAyOiBQZXJjZW50YWdlIG9mIHRvdGFsIHN1bSBmb3IgZWFjaCBjb25kaXRpb25cbiIpCnByaW50KEwxNS5zdW1tYXJ5X3BlcmNlbnRhZ2Vfcm91bmRlZCwgbiA9IEluZiwgd2lkdGggPSBJbmYpCgojIE9wdGlvbmFsbHksIHNhdmUgdGhlIHRhYmxlcyB0byBDU1YgZmlsZXMKd3JpdGUuY3N2KEwxNS5zdW1tYXJ5X2FsbF9yb3VuZGVkLCAiTXV0YW50cy9PVVRQVVQvTDE1LnN1bV9ieV9tdXRhdGlvbnNfd2l0aF90b3RhbC5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKd3JpdGUuY3N2KEwxNS5zdW1tYXJ5X3BlcmNlbnRhZ2Vfcm91bmRlZCwgIk11dGFudHMvT1VUUFVUL0wxNS5wZXJjZW50YWdlX2J5X211dGF0aW9uc193aXRoX3RvdGFsLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKCkNhbGN1bGF0ZSB0aGUgc3VtIG9mIHJhdyBzZXF1ZW5jZSByZWFkcyBmb3IgZWFjaCB0cmVhdG1lbnQgY29uZGl0aW9uIGZyb20gdGhlIG9yaWdpbmFsIGBCQ3MxNV9tYXBgIG9iamVjdCB0byB2ZXJpZnkgc3VtIHRvdGFscyBpbiAic3VtbWFyeV9hbGxfcm91bmRlZCIgKGFib3ZlKS4KYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgRGVmaW5lIHRoZSBjb2x1bW5zIHdlIHdhbnQgdG8gc3VtbWFyaXplCkJDczE1LmNvbHVtbnNfdG9fc3VtbWFyaXplIDwtIGMoIkQwMSIsICJEMDMiLCAiRDA1IiwgIkQwNiIsICJEMDciLCAiRDA4IiwgIkQwOSIsICJEMTAiLCAiRDExIikKCiMgQ2FsY3VsYXRlIHRoZSBzdW1zIGZvciBlYWNoIGNvbHVtbgpCQ3MxNS5zdW1zX3RhYmxlIDwtIEJDczE1X21hcCAlPiUKICBzdW1tYXJpc2UoYWNyb3NzKGFsbF9vZihCQ3MxNS5jb2x1bW5zX3RvX3N1bW1hcml6ZSksIH5zdW0oLiwgbmEucm0gPSBUUlVFKSkpCgojIENvbnZlcnQgdG8gYSBtb3JlIHJlYWRhYmxlIGZvcm1hdApCQ3MxNS5zdW1zX3RhYmxlX2xvbmcgPC0gQkNzMTUuc3Vtc190YWJsZSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IGV2ZXJ5dGhpbmcoKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkNvbHVtbiIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiU3VtIikKCiMgUm91bmQgdGhlIHN1bXMgZm9yIGJldHRlciByZWFkYWJpbGl0eQpCQ3MxNS5zdW1zX3RhYmxlX2xvbmckU3VtIDwtIHJvdW5kKEJDczE1LnN1bXNfdGFibGVfbG9uZyRTdW0sIDIpCgojIFByaW50IHRoZSB0YWJsZQpjYXQoIlRhYmxlOiBTdW1zIGZvciBzcGVjaWZpZWQgY29sdW1ucyBpbiBCQ3MxNV9tYXBcbiIpCnByaW50KEJDczE1LnN1bXNfdGFibGVfbG9uZywgbiA9IEluZikKYGBgCgoqKlBpZWNoYXJ0OioqIFBsb3QgdGhlIHBlcmNlbnQgc3VtcyBhcyBhIHBpZSBjaGFydCB0byBzaG93IGRpc3RyaWJ1dGlvbiBvZiBtdXRhdGlvbnMgaW4gbWFwcGVkIGJhcmNvZGVzOgpgYGB7cn0KIyBQcmVwYXJlIGRhdGEgZm9yIHRoZSBwaWUgY2hhcnQKTDE1LnBpZV9kYXRhIDwtIEwxNS5zdW1tYXJ5X3BlcmNlbnRhZ2Vfcm91bmRlZCAlPiUKICBmaWx0ZXIoY29uZGl0aW9uICE9ICJUb3RhbCIpICU+JSAgIyBSZW1vdmUgdGhlICJUb3RhbCIgY2F0ZWdvcnkKICBzZWxlY3QoY29uZGl0aW9uLCBEMDVfcGN0KSAlPiUKICBhcnJhbmdlKGRlc2MoRDA1X3BjdCkpICAjIFNvcnQgaW4gZGVzY2VuZGluZyBvcmRlciBmb3IgYmV0dGVyIHZpc3VhbGl6YXRpb24KCiMgRW5zdXJlIGNvbmRpdGlvbiBpcyBhIGZhY3RvciB3aXRoIHRoZSBjb3JyZWN0IG9yZGVyCkwxNS5tdXRhdGlvbl9vcmRlciA8LSAKICBjKCJtdXRhdGlvbnMgPT0gMCIsICJtdXRhdGlvbnMgPT0gMSIsICJtdXRhdGlvbnMgMi01IiwgIm11dGF0aW9ucyA2LTUwIiwgIm11dGF0aW9ucyA1MS0xMDAiLCAibXV0YXRpb25zID4gMTAwIikKCkwxNS5waWVfZGF0YSRjb25kaXRpb24gPC0gZmFjdG9yKEwxNS5waWVfZGF0YSRjb25kaXRpb24sIGxldmVscyA9IEwxNS5tdXRhdGlvbl9vcmRlcikKCiMgQ3JlYXRlIGxhYmVscyB3aXRoIHBlcmNlbnRhZ2VzCkwxNS5waWVfZGF0YSRsYWJlbCA8LSBwYXN0ZTAoTDE1LnBpZV9kYXRhJGNvbmRpdGlvbiwgIiAoIiwgcm91bmQoTDE1LnBpZV9kYXRhJEQwNV9wY3QsIDEpLCAiJSkiKQoKIyBDYWxjdWxhdGUgdGhlIHBvc2l0aW9ucyBmb3IgdGhlIGxhYmVscwpMMTUucGllX2RhdGEgPC0gTDE1LnBpZV9kYXRhICU+JQogIGFycmFuZ2UoY29uZGl0aW9uKSAlPiUKICBtdXRhdGUoCiAgICBwcm9wID0gRDA1X3BjdCAvIHN1bShEMDVfcGN0KSwKICAgIHlwb3MgPSBjdW1zdW0ocHJvcCkgLSAwLjUgKiBwcm9wLAogICAgbGFiZWxfcG9zaXRpb24gPSBjdW1zdW0ocHJvcCkgLSBwcm9wIC8gMgogICkKCiMgQ3JlYXRlIGEgY3VzdG9tIGJsdWUgY29sb3IgcGFsZXR0ZQpMMTUubl9jb2xvcnMgPC0gbnJvdyhMMTUucGllX2RhdGEpCkwxNS5ibHVlX3BhbGV0dGUgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCJsaWdodGJsdWUiLCAiZGFya2JsdWUiKSkoTDE1Lm5fY29sb3JzKQoKIyBDcmVhdGUgdGhlIHBpZSBjaGFydApMMTUucGllX2NoYXJ0IDwtIGdncGxvdChMMTUucGllX2RhdGEsIGFlcyh4ID0gMSwgeSA9IEQwNV9wY3QsIGZpbGwgPSBjb25kaXRpb24pKSArCiAgZ2VvbV9jb2wod2lkdGggPSAxKSArCiAgY29vcmRfcG9sYXIodGhldGEgPSAieSIsIHN0YXJ0ID0gMCkgKwogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIE11dGF0aW9uIEdyb3VwcyBcbmZvciBDb21wbGVtZW50YXRpb24gKENvZG9uIDEpIiwKICAgICAgIGZpbGwgPSAiTXV0YXRpb24gR3JvdXAiKSArCiAgdGhlbWVfdm9pZCgpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAyNCksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAyNCksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIyKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IEwxNS5ibHVlX3BhbGV0dGUsIGxhYmVscyA9IEwxNS5waWVfZGF0YSRsYWJlbCkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBwZXJjZW50X2Zvcm1hdCgpKQoKIyBEaXNwbGF5IHRoZSBwaWUgY2hhcnQKcHJpbnQoTDE1LnBpZV9jaGFydCkKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIFNhdmUgdGhlIHBpZSBjaGFydApnZ3NhdmUoIk11dGFudHMvUExPVFMvTDE1LnBlcmNlbnRfc3VtX0QwNV9waWVfY2hhcnRfYmx1ZS52Mi5wbmciLCAKICAgICAgIEwxNS5waWVfY2hhcnQsIHdpZHRoID0gMTAsIGhlaWdodCA9IDgsIGRwaSA9IDMwMCkKYGBgCgojIyMjIExpYjE2CgoqKlVuaXF1ZSBNdXRhbnRzOioqIENhbGN1bGF0ZSB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBtdXRhbnRzIG1hcHBlZCBhY3Jvc3MgYWxsIG5pbmUgY29uZGl0aW9ucy4gVGhlbiByZS1jYWxjdWxhdGUgdG8gb25seSBpbmNsdWRlIG11dGFudHMgd2l0aCAxLTUgYW1pbm8gYWNpZCBjaGFuZ2VzOgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBVbmlxdWUgTXV0YW50cwpsZW5ndGgodW5pcXVlKEJDczE2X21hcCRtdXRJRFtCQ3MxNl9tYXAkbXV0YXRpb25zID4gMF0pKQoKIyBVbmlxdWUgTXV0YW50cyAod2l0aCAxLTUgbXV0YXRpb25zKQpsZW5ndGgodW5pcXVlKEJDczE2X21hcCRtdXRJRFtCQ3MxNl9tYXAkbXV0YXRpb25zID4gMCAmIEJDczE2X21hcCRtdXRhdGlvbnMgPCA2XSkpCmBgYAoKKipNdXRhbnRzIHBlciBUcmVhdG1lbnQ6KiogQ2FsY3VsYXRlIHRoZSBudW1iZXIgb2YgdW5pcXVlIG11dGFudHMgKG11dElEKSByZWNvdmVyZWQgZnJvbSBlYWNoIHNhbXBsaW5nIGNvbmRpdGlvbjoKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgRGVmaW5lIHRoZSB0cmVhdG1lbnRzCkwxNi5tdXRJRC5tdXRhbnRzLnRyZWF0bWVudHMgPC0gYygiTEIiLCAiTTksIEZ1bGwgU3VwcGxlbWVudCIsICJNOSwgQ29tcGxlbWVudGF0aW9uIiwgIk05LCAwLjA1OC1UTVAiLCAKICAgICAgICAgICAgICAgICJNOSwgMC41LVRNUCIsICJNOSwgMS4wLVRNUCIsICJNOSwgMTAtVE1QIiwgIk05LCA1MC1UTVAiLCAiTTksIDIwMC1UTVAiKQoKIyBDYWxjdWxhdGUgdW5pcXVlIElEYWxpZ24gY291bnRzCkwxNi5tdXRJRC5tdXRhbnRzLnJlc3VsdCA8LSBCQ3MxNl9tYXAgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyA+IDApICU+JQogIHN1bW1hcmlzZSgKICAgIEQwMiA9IG5fZGlzdGluY3QobXV0SURbIWlzLm5hKEQwMildKSwKICAgIEQwNCA9IG5fZGlzdGluY3QobXV0SURbIWlzLm5hKEQwNCldKSwKICAgIEQxMiA9IG5fZGlzdGluY3QobXV0SURbIWlzLm5hKEQxMildKSwKICAgIEUwMSA9IG5fZGlzdGluY3QobXV0SURbIWlzLm5hKEUwMSldKSwKICAgIEUwMiA9IG5fZGlzdGluY3QobXV0SURbIWlzLm5hKEUwMildKSwKICAgIEUwMyA9IG5fZGlzdGluY3QobXV0SURbIWlzLm5hKEUwMyldKSwKICAgIEUwNCA9IG5fZGlzdGluY3QobXV0SURbIWlzLm5hKEUwNCldKSwKICAgIEUwNSA9IG5fZGlzdGluY3QobXV0SURbIWlzLm5hKEUwNSldKSwKICAgIEUwNiA9IG5fZGlzdGluY3QobXV0SURbIWlzLm5hKEUwNildKSkKCiMgVHJhbnNmb3JtIHRoZSByZXN1bHQgdG8gYSBtb3JlIHJlYWRhYmxlIGZvcm1hdApMMTYubXV0SUQubXV0YW50cy5yZXN1bHRfdGFibGUgPC0gdGliYmxlKAogIFRyZWF0bWVudCA9IEwxNi5tdXRJRC5tdXRhbnRzLnRyZWF0bWVudHMsCiAgYFVuaXF1ZSBtdXRJRCBDb3VudGAgPSBhcy5udW1lcmljKEwxNi5tdXRJRC5tdXRhbnRzLnJlc3VsdFsxLF0pKQoKIyBQcmludCB0aGUgdGFibGUKcHJpbnQoTDE2Lm11dElELm11dGFudHMucmVzdWx0X3RhYmxlLCBuID0gSW5mKQpgYGAKCioqTWVkaWFuIE11dGFudHMgcGVyIEhvbW9sb2c6KiogQ2FsY3VsYXRlIHRoZSBtZWRpYW4gbnVtYmVyIG9mIHVuaXF1ZSBtdXRhbnRzIChtdXRJRCkgYXNzb2NpYXRlZCB3aXRoIHVuaXF1ZSBob21vbG9ncyAoSURhbGlnbikgcmVjb3ZlcmVkIGZyb20gZWFjaCBzYW1wbGluZyBjb25kaXRpb246CmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIERlZmluZSB0aGUgdHJlYXRtZW50cwpMMTYubXV0SUQubXV0YW50cy5tZWRpYW4udHJlYXRtZW50cyA8LSBjKCJMQiIsICJNOSwgRnVsbCBTdXBwbGVtZW50IiwgIk05LCBDb21wbGVtZW50YXRpb24iLCAiTTksIDAuMDU4LVRNUCIsIAogICAgICAgICAgICAgICAgIk05LCAwLjUtVE1QIiwgIk05LCAxLjAtVE1QIiwgIk05LCAxMC1UTVAiLCAiTTksIDUwLVRNUCIsICJNOSwgMjAwLVRNUCIpCgpMMTYubXV0SUQubXV0YW50cy5tZWRpYW4uc2FtcGxlSUQgPC0gYygiRDAyIiwgIkQwNCIsICJEMTIiLCAiRTAxIiwgIkUwMiIsICJFMDMiLCAiRTA0IiwgIkUwNSIsICJFMDYiKQoKIyBDYWxjdWxhdGUgbWVkaWFuIHVuaXF1ZSBtdXRJRCAobXV0YXRpb25zID4gMCkgcGVyIHVuaXF1ZSBJRGFsaWduIChtdXRhdGlvbnMgPSAwKQpMMTYubXV0SUQubXV0YW50cy5tZWRpYW4ucmVzdWx0IDwtIEJDczE2X21hcCAlPiUKICBncm91cF9ieShJRGFsaWduKSAlPiUKICBzdW1tYXJpc2UoCiAgICBEMDI9aWYoYW55KG11dGF0aW9ucz09MCAmICFpcy5uYShEMDIpKSkgbWVkaWFuKG5fZGlzdGluY3QobXV0SURbbXV0YXRpb25zPjAgJiAhaXMubmEoRDAyKV0pKSBlbHNlIE5BX3JlYWxfLAogICAgRDA0PWlmKGFueShtdXRhdGlvbnM9PTAgJiAhaXMubmEoRDA0KSkpIG1lZGlhbihuX2Rpc3RpbmN0KG11dElEW211dGF0aW9ucz4wICYgIWlzLm5hKEQwNCldKSkgZWxzZSBOQV9yZWFsXywKICAgIEQxMj1pZihhbnkobXV0YXRpb25zPT0wICYgIWlzLm5hKEQxMikpKSBtZWRpYW4obl9kaXN0aW5jdChtdXRJRFttdXRhdGlvbnM+MCAmICFpcy5uYShEMTIpXSkpIGVsc2UgTkFfcmVhbF8sCiAgICBFMDE9aWYoYW55KG11dGF0aW9ucz09MCAmICFpcy5uYShFMDEpKSkgbWVkaWFuKG5fZGlzdGluY3QobXV0SURbbXV0YXRpb25zPjAgJiAhaXMubmEoRTAxKV0pKSBlbHNlIE5BX3JlYWxfLAogICAgRTAyPWlmKGFueShtdXRhdGlvbnM9PTAgJiAhaXMubmEoRTAyKSkpIG1lZGlhbihuX2Rpc3RpbmN0KG11dElEW211dGF0aW9ucz4wICYgIWlzLm5hKEUwMildKSkgZWxzZSBOQV9yZWFsXywKICAgIEUwMz1pZihhbnkobXV0YXRpb25zPT0wICYgIWlzLm5hKEUwMykpKSBtZWRpYW4obl9kaXN0aW5jdChtdXRJRFttdXRhdGlvbnM+MCAmICFpcy5uYShFMDMpXSkpIGVsc2UgTkFfcmVhbF8sCiAgICBFMDQ9aWYoYW55KG11dGF0aW9ucz09MCAmICFpcy5uYShFMDQpKSkgbWVkaWFuKG5fZGlzdGluY3QobXV0SURbbXV0YXRpb25zPjAgJiAhaXMubmEoRTA0KV0pKSBlbHNlIE5BX3JlYWxfLAogICAgRTA1PWlmKGFueShtdXRhdGlvbnM9PTAgJiAhaXMubmEoRTA1KSkpIG1lZGlhbihuX2Rpc3RpbmN0KG11dElEW211dGF0aW9ucz4wICYgIWlzLm5hKEUwNSldKSkgZWxzZSBOQV9yZWFsXywKICAgIEUwNj1pZihhbnkobXV0YXRpb25zPT0wICYgIWlzLm5hKEUwNikpKSBtZWRpYW4obl9kaXN0aW5jdChtdXRJRFttdXRhdGlvbnM+MCAmICFpcy5uYShFMDYpXSkpIGVsc2UgTkFfcmVhbF8pICU+JQogIHN1bW1hcmlzZShhY3Jvc3Moc3RhcnRzX3dpdGgoYygiRCIsICJFIikpLCB+bWVkaWFuKC4sIG5hLnJtID0gVFJVRSkpKSAgIyBPbmx5IHN1bW1hcml6ZSBEKiBhbmQgRSogY29sdW1ucwoKIyBUcmFuc2Zvcm0gdGhlIHJlc3VsdCB0byBhIG1vcmUgcmVhZGFibGUgZm9ybWF0CkwxNi5tdXRJRC5tdXRhbnRzLm1lZGlhbi5yZXN1bHRfdGFibGUgPC0gdGliYmxlKAogIFNhbXBsZUlEID0gTDE2Lm11dElELm11dGFudHMubWVkaWFuLnNhbXBsZUlELAogIFRyZWF0bWVudCA9IEwxNi5tdXRJRC5tdXRhbnRzLm1lZGlhbi50cmVhdG1lbnRzLAogIGBNZWRpYW4gVW5pcXVlIG11dElEIHBlciBVbmlxdWUgSURhbGlnbmAgPSBhcy5udW1lcmljKEwxNi5tdXRJRC5tdXRhbnRzLm1lZGlhbi5yZXN1bHRbMSxdKSkKCiMgUHJpbnQgdGhlIHRhYmxlCnByaW50KEwxNi5tdXRJRC5tdXRhbnRzLm1lZGlhbi5yZXN1bHRfdGFibGUsIG4gPSBJbmYpCmBgYAoKVmFsaWRhdGUgdGhlIG1lZGlhbiBtdXRhbnRzIChtdXRJRCkgcGVyIGhvbW9sb2d5IChJRGFsaWduKSBmb3IgQ29tcGxlbWVudGF0aW9uIChEMTIpOgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBDYWxjdWxhdGUgaW50ZXJtZWRpYXRlIHJlc3VsdHMKRDEyX3ZhbGlkYXRlX3Jlc3VsdCA8LSBCQ3MxNl9tYXAgJT4lCiAgZ3JvdXBfYnkoSURhbGlnbikgJT4lCiAgc3VtbWFyaXNlKAogICAgRDEyX211dGFudHNfY291bnQgPSBzdW0obXV0YXRpb25zID4gMCAmICFpcy5uYShEMTIpKSwKICAgIEQxMl9ub25fbXV0YW50c19jb3VudCA9IHN1bShtdXRhdGlvbnMgPT0gMCAmICFpcy5uYShEMTIpKSwKICAgIEQxMl91bmlxdWVfbXV0SURfY291bnQgPSBuX2Rpc3RpbmN0KG11dElEW211dGF0aW9ucyA+IDAgJiAhaXMubmEoRDEyKV0pKQoKIyBSZW1vdmUgcm93cyB3aGVyZSBEMTJfbm9uX211dGFudHNfY291bnQgPT0gMApEMTJfZmlsdGVyZWQgPC0gRDEyX3ZhbGlkYXRlX3Jlc3VsdCAlPiUKICBmaWx0ZXIoRDEyX25vbl9tdXRhbnRzX2NvdW50ID4gMCkKCiMgUHJpbnQgZnVsbCByZXN1bHRzIHNob3dpbmcgbWVkaWFuIG11dElEIGZvciBlYWNoIElEYWxpZ24gaW4gRDEyOgpwcmludCgiRnVsbCBJRGFsaWduIHJlc3VsdHMgZm9yIEQxMjoiKQpwcmludChEMTJfZmlsdGVyZWQsIG4gPSBJbmYpCgojIFNhdmUgYSBjb3B5IGFzIGEgc3ByZWFkc2hlZXQKd3JpdGUuY3N2KEQxMl9maWx0ZXJlZCwgIk11dGFudHMvT1VUUFVUL0wxNi5EMTIubWVkaWFuLm11dElELnBlci5JRGFsaWduLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQoKIyBQcmludCBzdW1tYXJ5IHJlc3VsdHMgb2YgbWVkaWFuIG11dElEIHBlciBJRGFsaWduIGluIEQxMjoKcHJpbnQoIlN1bW1hcnkgb2YgZmlsdGVyZWQgcmVzdWx0czoiKQpwcmludChzdW1tYXJ5KEQxMl9maWx0ZXJlZCkpCgojIENhbGN1bGF0ZSBtZWRpYW4gdXNpbmcgdGhlIGZpbHRlcmVkIGRhdGEKbWVkaWFuX211dGFudHNfRDEyIDwtIG1lZGlhbihEMTJfZmlsdGVyZWQkRDEyX3VuaXF1ZV9tdXRJRF9jb3VudCwgbmEucm0gPSBUUlVFKQoKcHJpbnQocGFzdGUoIk1lZGlhbiBudW1iZXIgb2YgdW5pcXVlIG11dGFudHMgcGVyIElEYWxpZ24gZm9yIEQxMjoiLCBtZWRpYW5fbXV0YW50c19EMTIpKQpgYGAKCioqTXV0YW50IENvdW50cyBieSBEaXN0YW5jZToqKiBTdW1tYXJpemUgdGhlIHJhdyBzZXF1ZW5jZSBjb3VudHMgYWNyb3NzIG1hcHBlZCBiYXJjb2RlcyBhdCBudW1lcm91cyBtdXRhdGlvbiBsZXZlbHM6CmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIERlZmluZSB0aGUgY29sdW1ucyB3ZSB3YW50IHRvIHN1bW1hcml6ZQpMMTYuY29sdW1uc190b19zdW1tYXJpemUgPC0gYygiRDAyIiwgIkQwNCIsICJEMTIiLCAiRTAxIiwgIkUwMiIsICJFMDMiLCAiRTA0IiwgIkUwNSIsICJFMDYiKQoKIyBDcmVhdGUgYSBmdW5jdGlvbiB0byBzdW0gdmFsdWVzIGZvciBtdWx0aXBsZSBjb2x1bW5zCkwxNi5zdW1fY29sdW1ucyA8LSBmdW5jdGlvbihkYXRhLCBjb25kaXRpb25fbmFtZSkgewogIGRhdGEgJT4lCiAgICBzdW1tYXJpc2UoYWNyb3NzKGFsbF9vZihMMTYuY29sdW1uc190b19zdW1tYXJpemUpLCB+c3VtKC4sIG5hLnJtID0gVFJVRSkpKSAlPiUKICAgIG11dGF0ZShjb25kaXRpb24gPSBjb25kaXRpb25fbmFtZSkgJT4lCiAgICBzZWxlY3QoY29uZGl0aW9uLCBldmVyeXRoaW5nKCkpCn0KCiMgU3VtIHZhbHVlcyBmb3IgZWFjaCBjb25kaXRpb24KTDE2LnN1bW1hcnlfYWxsIDwtIGJpbmRfcm93cygKICBCQ3MxNl9tYXAgJT4lIGZpbHRlcihtdXRhdGlvbnMgPT0gMCkgJT4lIEwxNi5zdW1fY29sdW1ucygibXV0YXRpb25zID09IDAiKSwKICBCQ3MxNl9tYXAgJT4lIGZpbHRlcihtdXRhdGlvbnMgPT0gMSkgJT4lIEwxNi5zdW1fY29sdW1ucygibXV0YXRpb25zID09IDEiKSwKICBCQ3MxNl9tYXAgJT4lIGZpbHRlcihtdXRhdGlvbnMgPj0gMiAmIG11dGF0aW9ucyA8PSA1KSAlPiUgTDE2LnN1bV9jb2x1bW5zKCJtdXRhdGlvbnMgMi01IiksCiAgQkNzMTZfbWFwICU+JSBmaWx0ZXIobXV0YXRpb25zID49IDYgJiBtdXRhdGlvbnMgPD0gNTApICU+JSBMMTYuc3VtX2NvbHVtbnMoIm11dGF0aW9ucyA2LTUwIiksCiAgQkNzMTZfbWFwICU+JSBmaWx0ZXIobXV0YXRpb25zID49IDUxICYgbXV0YXRpb25zIDw9IDEwMCkgJT4lIEwxNi5zdW1fY29sdW1ucygibXV0YXRpb25zIDUxLTEwMCIpLAogIEJDczE2X21hcCAlPiUgZmlsdGVyKG11dGF0aW9ucyA+IDEwMCkgJT4lIEwxNi5zdW1fY29sdW1ucygibXV0YXRpb25zID4gMTAwIikKKQoKIyBBZGQgYSB0b3RhbCByb3cgdG8gdGhlIHN1bSB0YWJsZQpMMTYuc3VtbWFyeV9hbGxfd2l0aF90b3RhbCA8LSBMMTYuc3VtbWFyeV9hbGwgJT4lCiAgYmluZF9yb3dzKHN1bW1hcmlzZSguLCBhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIHN1bSksIGNvbmRpdGlvbiA9ICJUb3RhbCIpKQoKIyBDYWxjdWxhdGUgdGhlIHBlcmNlbnRhZ2Ugb2YgdG90YWwgc3VtIGZvciBlYWNoIGNvbHVtbgpMMTYuc3VtbWFyeV9wZXJjZW50YWdlIDwtIEwxNi5zdW1tYXJ5X2FsbCAlPiUKICBtdXRhdGUoYWNyb3NzKGFsbF9vZihMMTYuY29sdW1uc190b19zdW1tYXJpemUpLCAKICAgICAgICAgICAgICAgIH4uIC8gc3VtKC4sIG5hLnJtID0gVFJVRSkgKiAxMDAsIAogICAgICAgICAgICAgICAgLm5hbWVzID0gIntjb2x9X3BjdCIpKQoKIyBBZGQgYSB0b3RhbCByb3cgdG8gdGhlIHBlcmNlbnRhZ2UgdGFibGUgKHdpbGwgc3VtIHRvIDEwMCBmb3IgZWFjaCBjb2x1bW4pCkwxNi5zdW1tYXJ5X3BlcmNlbnRhZ2Vfd2l0aF90b3RhbCA8LSBMMTYuc3VtbWFyeV9wZXJjZW50YWdlICU+JQogIHNlbGVjdChjb25kaXRpb24sIGVuZHNfd2l0aCgiX3BjdCIpKSAlPiUKICBiaW5kX3Jvd3Moc3VtbWFyaXNlKC4sIGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSwgc3VtKSwgY29uZGl0aW9uID0gIlRvdGFsIikpCgojIFJvdW5kIHRoZSB2YWx1ZXMgZm9yIGJldHRlciByZWFkYWJpbGl0eQpMMTYuc3VtbWFyeV9hbGxfcm91bmRlZCA8LSBMMTYuc3VtbWFyeV9hbGxfd2l0aF90b3RhbCAlPiUKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCB+cm91bmQoLiwgMikpKQoKTDE2LnN1bW1hcnlfcGVyY2VudGFnZV9yb3VuZGVkIDwtIEwxNi5zdW1tYXJ5X3BlcmNlbnRhZ2Vfd2l0aF90b3RhbCAlPiUKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCB+cm91bmQoLiwgMikpKQoKIyBQcmludCB0aGUgc3VtIHRhYmxlCmNhdCgiVGFibGUgMTogU3VtIG9mIHZhbHVlcyBmb3IgZWFjaCBjb25kaXRpb25cbiIpCnByaW50KEwxNi5zdW1tYXJ5X2FsbF9yb3VuZGVkLCBuID0gSW5mLCB3aWR0aCA9IEluZikKCiMgUHJpbnQgdGhlIHBlcmNlbnRhZ2UgdGFibGUKY2F0KCJcblRhYmxlIDI6IFBlcmNlbnRhZ2Ugb2YgdG90YWwgc3VtIGZvciBlYWNoIGNvbmRpdGlvblxuIikKcHJpbnQoTDE2LnN1bW1hcnlfcGVyY2VudGFnZV9yb3VuZGVkLCBuID0gSW5mLCB3aWR0aCA9IEluZikKCiMgT3B0aW9uYWxseSwgc2F2ZSB0aGUgdGFibGVzIHRvIENTViBmaWxlcwp3cml0ZS5jc3YoTDE2LnN1bW1hcnlfYWxsX3JvdW5kZWQsICJNdXRhbnRzL09VVFBVVC9MMTYuc3VtX2J5X211dGF0aW9uc193aXRoX3RvdGFsLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQp3cml0ZS5jc3YoTDE2LnN1bW1hcnlfcGVyY2VudGFnZV9yb3VuZGVkLCAiTXV0YW50cy9PVVRQVVQvTDE2LnBlcmNlbnRhZ2VfYnlfbXV0YXRpb25zX3dpdGhfdG90YWwuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCmBgYAoKQ2FsY3VsYXRlIHRoZSBzdW0gb2YgcmF3IHNlcXVlbmNlIHJlYWRzIGZvciBlYWNoIHRyZWF0bWVudCBjb25kaXRpb24gZnJvbSB0aGUgb3JpZ2luYWwgYEJDczE2X21hcGAgb2JqZWN0IHRvIHZlcmlmeSBzdW0gdG90YWxzIGluICJzdW1tYXJ5X2FsbF9yb3VuZGVkIiAoYWJvdmUpLgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBEZWZpbmUgdGhlIGNvbHVtbnMgd2Ugd2FudCB0byBzdW1tYXJpemUKQkNzMTYuY29sdW1uc190b19zdW1tYXJpemUgPC0gYygiRDAyIiwgIkQwNCIsICJEMTIiLCAiRTAxIiwgIkUwMiIsICJFMDMiLCAiRTA0IiwgIkUwNSIsICJFMDYiKQoKIyBDYWxjdWxhdGUgdGhlIHN1bXMgZm9yIGVhY2ggY29sdW1uCkJDczE2LnN1bXNfdGFibGUgPC0gQkNzMTZfbWFwICU+JQogIHN1bW1hcmlzZShhY3Jvc3MoYWxsX29mKEJDczE2LmNvbHVtbnNfdG9fc3VtbWFyaXplKSwgfnN1bSguLCBuYS5ybSA9IFRSVUUpKSkKCiMgQ29udmVydCB0byBhIG1vcmUgcmVhZGFibGUgZm9ybWF0CkJDczE2LnN1bXNfdGFibGVfbG9uZyA8LSBCQ3MxNi5zdW1zX3RhYmxlICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gZXZlcnl0aGluZygpLCAKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiQ29sdW1uIiwgCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJTdW0iKQoKIyBSb3VuZCB0aGUgc3VtcyBmb3IgYmV0dGVyIHJlYWRhYmlsaXR5CkJDczE2LnN1bXNfdGFibGVfbG9uZyRTdW0gPC0gcm91bmQoQkNzMTYuc3Vtc190YWJsZV9sb25nJFN1bSwgMikKCiMgUHJpbnQgdGhlIHRhYmxlCmNhdCgiVGFibGU6IFN1bXMgZm9yIHNwZWNpZmllZCBjb2x1bW5zIGluIEJDczE2X21hcFxuIikKcHJpbnQoQkNzMTYuc3Vtc190YWJsZV9sb25nLCBuID0gSW5mKQpgYGAKCioqUGllY2hhcnQ6KiogUGxvdCB0aGUgcGVyY2VudCBzdW1zIGFzIGEgcGllIGNoYXJ0IHRvIHNob3cgZGlzdHJpYnV0aW9uIG9mIG11dGF0aW9ucyBpbiBtYXBwZWQgYmFyY29kZXM6CmBgYHtyfQojIFByZXBhcmUgZGF0YSBmb3IgdGhlIHBpZSBjaGFydApMMTYucGllX2RhdGEgPC0gTDE2LnN1bW1hcnlfcGVyY2VudGFnZV9yb3VuZGVkICU+JQogIGZpbHRlcihjb25kaXRpb24gIT0gIlRvdGFsIikgJT4lICAjIFJlbW92ZSB0aGUgIlRvdGFsIiBjYXRlZ29yeQogIHNlbGVjdChjb25kaXRpb24sIEQxMl9wY3QpICU+JQogIGFycmFuZ2UoZGVzYyhEMTJfcGN0KSkgICMgU29ydCBpbiBkZXNjZW5kaW5nIG9yZGVyIGZvciBiZXR0ZXIgdmlzdWFsaXphdGlvbgoKIyBFbnN1cmUgY29uZGl0aW9uIGlzIGEgZmFjdG9yIHdpdGggdGhlIGNvcnJlY3Qgb3JkZXIKTDE2Lm11dGF0aW9uX29yZGVyIDwtIAogIGMoIm11dGF0aW9ucyA9PSAwIiwgIm11dGF0aW9ucyA9PSAxIiwgIm11dGF0aW9ucyAyLTUiLCAibXV0YXRpb25zIDYtNTAiLCAibXV0YXRpb25zIDUxLTEwMCIsICJtdXRhdGlvbnMgPiAxMDAiKQoKTDE2LnBpZV9kYXRhJGNvbmRpdGlvbiA8LSBmYWN0b3IoTDE2LnBpZV9kYXRhJGNvbmRpdGlvbiwgbGV2ZWxzID0gTDE2Lm11dGF0aW9uX29yZGVyKQoKIyBDcmVhdGUgbGFiZWxzIHdpdGggcGVyY2VudGFnZXMKTDE2LnBpZV9kYXRhJGxhYmVsIDwtIHBhc3RlMChMMTYucGllX2RhdGEkY29uZGl0aW9uLCAiICgiLCByb3VuZChMMTYucGllX2RhdGEkRDEyX3BjdCwgMSksICIlKSIpCgojIENhbGN1bGF0ZSB0aGUgcG9zaXRpb25zIGZvciB0aGUgbGFiZWxzCkwxNi5waWVfZGF0YSA8LSBMMTYucGllX2RhdGEgJT4lCiAgYXJyYW5nZShjb25kaXRpb24pICU+JQogIG11dGF0ZSgKICAgIHByb3AgPSBEMTJfcGN0IC8gc3VtKEQxMl9wY3QpLAogICAgeXBvcyA9IGN1bXN1bShwcm9wKSAtIDAuNSAqIHByb3AsCiAgICBsYWJlbF9wb3NpdGlvbiA9IGN1bXN1bShwcm9wKSAtIHByb3AgLyAyCiAgKQoKIyBDcmVhdGUgYSBjdXN0b20gYmx1ZSBjb2xvciBwYWxldHRlCkwxNi5uX2NvbG9ycyA8LSBucm93KEwxNi5waWVfZGF0YSkKTDE2Lm9yYW5nZV9wYWxldHRlIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygib3JhbmdlIiwgImRhcmtvcmFuZ2U0IikpKEwxNi5uX2NvbG9ycykKCiMgQ3JlYXRlIHRoZSBwaWUgY2hhcnQKTDE2LnBpZV9jaGFydCA8LSBnZ3Bsb3QoTDE2LnBpZV9kYXRhLCBhZXMoeCA9IDEsIHkgPSBEMTJfcGN0LCBmaWxsID0gY29uZGl0aW9uKSkgKwogIGdlb21fY29sKHdpZHRoID0gMSkgKwogIGNvb3JkX3BvbGFyKHRoZXRhID0gInkiLCBzdGFydCA9IDApICsKICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBNdXRhdGlvbiBHcm91cHMgXG5mb3IgQ29tcGxlbWVudGF0aW9uIChDb2RvbiAyKSIsCiAgICAgICBmaWxsID0gIk11dGF0aW9uIEdyb3VwIikgKwogIHRoZW1lX3ZvaWQoKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjQpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjQpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMikpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBMMTYub3JhbmdlX3BhbGV0dGUsIGxhYmVscyA9IEwxNi5waWVfZGF0YSRsYWJlbCkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBwZXJjZW50X2Zvcm1hdCgpKQoKIyBEaXNwbGF5IHRoZSBwaWUgY2hhcnQKcHJpbnQoTDE2LnBpZV9jaGFydCkKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIFNhdmUgdGhlIHBpZSBjaGFydApnZ3NhdmUoIk11dGFudHMvUExPVFMvTDE2LnBlcmNlbnRfc3VtX0QxMl9waWVfY2hhcnRfb3JhbmdlLnYyLnBuZyIsIAogICAgICAgTDE2LnBpZV9jaGFydCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOCwgZHBpID0gMzAwKQpgYGAKCiMjIyMgQm90aCBDb2RvbnMKCmBgYHtyfQpwYXRjaDEgPC0gTDE1LnBpZV9jaGFydCB8IEwxNi5waWVfY2hhcnQKcGF0Y2gxCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KIyBTYXZlIHRoZSBwaWUgY2hhcnQKZ2dzYXZlKCJNdXRhbnRzL1BMT1RTL0xpYjE1LjE2LnBlcmNlbnQuc3VtLnBpZS5jaGFydC5jb21wbGVtZW50YXRpb24udjIucG5nIiwgCiAgICAgICBwYXRjaDEsIHdpZHRoID0gMTIsIGhlaWdodCA9IDgsIGRwaSA9IDMwMCkKYGBgCgpQbG90IHRoZSB0d28gcGVyY2VudGFnZSBkYXRhc2V0cyBhcyBhIGJveHBsb3Qgc2hvd2luZyBkaWZmZXJlbmNlcyBpbiBtdXRhdGlvbiBncm91cHMgYmV0d2VlbiBjb2RvbiB2ZXJzaW9uczoKYGBge3J9CiMgQ29tYmluZSB0aGUgdHdvIGRhdGFmcmFtZXMKTDE1LjE2LmNvbWJpbmVkLnN1bW1hcnlfcGVyY2VudGFnZV9yb3VuZGVkIDwtIGJpbmRfcm93cygKICBMMTUuc3VtbWFyeV9wZXJjZW50YWdlX3JvdW5kZWQgJT4lIAogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBlbmRzX3dpdGgoIl9wY3QiKSwgbmFtZXNfdG8gPSAic2FtcGxlIiwgdmFsdWVzX3RvID0gInBlcmNlbnRhZ2UiKSAlPiUgCiAgICBtdXRhdGUoZ3JvdXAgPSAiQ29kb24xIiksCiAgTDE2LnN1bW1hcnlfcGVyY2VudGFnZV9yb3VuZGVkICU+JSAKICAgIHBpdm90X2xvbmdlcihjb2xzID0gZW5kc193aXRoKCJfcGN0IiksIG5hbWVzX3RvID0gInNhbXBsZSIsIHZhbHVlc190byA9ICJwZXJjZW50YWdlIikgJT4lIAogICAgbXV0YXRlKGdyb3VwID0gIkNvZG9uMiIpCikKCiMgRW5zdXJlIHRoZSBjb25kaXRpb24gY29sdW1uIGlzIGEgZmFjdG9yIHdpdGggbGV2ZWxzIGluIHRoZSBkZXNpcmVkIG9yZGVyCkwxNS4xNi5jb21iaW5lZC5zdW1tYXJ5X3BlcmNlbnRhZ2Vfcm91bmRlZCA8LSBMMTUuMTYuY29tYmluZWQuc3VtbWFyeV9wZXJjZW50YWdlX3JvdW5kZWQgJT4lCiAgZmlsdGVyKGNvbmRpdGlvbiAhPSAiVG90YWwiKSAlPiUKICBtdXRhdGUoY29uZGl0aW9uID0gZmFjdG9yKGNvbmRpdGlvbiwgbGV2ZWxzID0gdW5pcXVlKGNvbmRpdGlvbikpKQoKIyBDcmVhdGUgYSBuYW1lZCB2ZWN0b3IgZm9yIHRoZSBuZXcgbGFiZWxzCkwxNS4xNi5uZXdfbGFiZWxzIDwtIGMoCiAgIm11dGF0aW9ucyA9PSAwIiA9ICIwIiwKICAibXV0YXRpb25zID09IDEiID0gIjEiLAogICJtdXRhdGlvbnMgMi01IiA9ICIyLTUiLAogICJtdXRhdGlvbnMgNi01MCIgPSAiNi01MCIsCiAgIm11dGF0aW9ucyA1MS0xMDAiID0gIjUxLTEwMCIsCiAgIm11dGF0aW9ucyA+IDEwMCIgPSAiPjEwMCIKKQoKIyBDcmVhdGUgYmFycGxvdCBvZiBwZXJjZW50YWdlcwpMMTUuMTYuY29tYmluZWQuc3VtbWFyeV9wZXJjZW50YWdlLnBsb3QgPC0gCiAgZ2dwbG90KEwxNS4xNi5jb21iaW5lZC5zdW1tYXJ5X3BlcmNlbnRhZ2Vfcm91bmRlZCwgYWVzKHggPSBjb25kaXRpb24sIHkgPSBwZXJjZW50YWdlLCBmaWxsID0gZ3JvdXApKSArCiAgc3RhdF9zdW1tYXJ5KGZ1biA9IG1lYW4sIGdlb20gPSAiYmFyIiwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuOSksIHdpZHRoID0gMC44KSArCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gbWVhbl9zZSwgZ2VvbSA9ICJlcnJvcmJhciIsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjkpLCB3aWR0aCA9IDAuMikgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMC41KSwgCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwgCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41KSwgCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksIAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsKICBsYWJzKHggPSAiTXV0YXRpb24gRGlzdGFuY2UgZnJvbSBIb21vbG9nIChhLmEuKSIsIHkgPSAiTWVkaWFuIFBlcmNlbnRhZ2UgKCUpIiwgZmlsbCA9ICJDb2RvbiIsCiAgICAgICB0aXRsZSA9ICJNZWFuIE11dGF0aW9uIFBlcmNlbnRhZ2VzIGZvciBib3RoIENvZG9uIFZlcnNpb25zIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkNvZG9uMSIgPSAiIzAwNzJCMiIsICJDb2RvbjIiID0gIiNFNjlGMDAiKSkgKyAgIyBDdXN0b20gY29sb3JzCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAsIDEwMCkpICsgICMgU2V0IHktYXhpcyBsaW1pdHMgZnJvbSAwIHRvIDEwMCUKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IEwxNS4xNi5uZXdfbGFiZWxzKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoMCwgMTAwKSkKCnByaW50KEwxNS4xNi5jb21iaW5lZC5zdW1tYXJ5X3BlcmNlbnRhZ2UucGxvdCkKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iTXV0YW50cy9QTE9UUy9MaWIxNS4xNi5wZXJjZW50YWdlLm11dGFudHMucGVyLmRpc3RhbmNlLmdyb3VwLnBuZyIsIAogICAgICAgcGxvdD1MMTUuMTYuY29tYmluZWQuc3VtbWFyeV9wZXJjZW50YWdlLnBsb3QsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDYsIGhlaWdodCA9IDUsIHVuaXRzID0gImluIikKYGBgCgpQbG90IHRvZ2V0aGVyOgpgYGB7cn0KcGF0Y2gxMSA8LSAoTDE1LnBpZV9jaGFydCB8IEwxNi5waWVfY2hhcnQpIC8gCiAgICAgICAgICAgTDE1LjE2LmNvbWJpbmVkLnN1bW1hcnlfcGVyY2VudGFnZS5wbG90ICsKICAgICAgICAgICBwbG90X2xheW91dChoZWlnaHRzID0gYygxLCAxKSkKcGF0Y2gxMQpgYGAKCmBgYHtyIGVjaG89RkFMU0V9CiMgU2F2ZSB0aGUgcGllIGNoYXJ0Cmdnc2F2ZSgiTXV0YW50cy9QTE9UUy9MaWIxNS4xNi5QaWVjaGFydHMuU3VtbWFyeS5Db21wbGVtZW50YXRpb24ucG5nIiwgCiAgICAgICBwYXRjaDExLCB3aWR0aCA9IDgsIGhlaWdodCA9IDgsIGRwaSA9IDMwMCkKYGBgCgojIyMgRmlsdGVyIERhdGEgYnkgRGlzdGFuY2UKCkdyYWIgb25seSB0aGUgQkNzIHdpdGggdXAgdG8gNSBtdXRhdGlvbnMgKGFsc28gbmVlZCB0byBlbnN1cmUgaXQgaGFzIGdyZWF0ZXIgdGhhbiAwIG11dGF0aW9ucyBzaW5jZSBzb21lIEJDcyBoYXZlIG5lZ2F0aXZlIHZhbHVlcyk6CmBgYHtyfQpCQ3M1XzE1IDwtIEJDczE1X21hcCAlPiUKICBmaWx0ZXIobXV0YXRpb25zID49IDAgJiBtdXRhdGlvbnMgPD0gNSkgJT4lCiAgbGVmdF9qb2luKG11dElEaW5mbzE1ICU+JSBzZWxlY3QobXV0SUQpLCBieT0ibXV0SUQiKSAlPiUKICBzZWxlY3QoQkMsSURhbGlnbixtdXRJRCxtdXRhdGlvbnMsRDA1RDAzZmMpCmBgYAoKUmV0YWluIG9ubHkgdGhlIGhvbW9sb2dzIHdpdGggZ29vZCBkYXRhICg+NUJDcyk6CmBgYHtyfQpmaXRuZXNzX2Rpc3RhbmNlXzE1IDwtIHBlcmZlY3RzXzE1XzE2XzVCQ3NfdHJlZSAlPiUKICBzZWxlY3QoSUQsZml0RDA1RDAzKSAlPiUKICBkcGx5cjo6cmVuYW1lKElEYWxpZ249SUQpCmBgYAoKRGV0ZXJtaW5lIG1lZGlhbiBhbmQgc2QgZm9yIDEgbXV0YXRpb246CmBgYHtyfQpmaXRuZXNzX2Rpc3RhbmNlXzE1IDwtIEJDczVfMTUgJT4lCiAgICBmaWx0ZXIobXV0YXRpb25zPT0xKSAlPiUKICAgIGdyb3VwX2J5KElEYWxpZ24pICU+JQogICAgc3VtbWFyaXNlKG11dDFmaXQ9bWVkaWFuKEQwNUQwM2ZjKSwKICAgICAgICAgICAgICBtdXQxc2Q9c2QoRDA1RDAzZmMpLAogICAgICAgICAgICAgIG51bTFwb2ludHM9bigpKSAlPiUKICAgIHJpZ2h0X2pvaW4oZml0bmVzc19kaXN0YW5jZV8xNSxieT0iSURhbGlnbiIpCmBgYAoKRGV0ZXJtaW5lIG1lZGlhbiBhbmQgc2QgZm9yIDIgbXV0YXRpb246CmBgYHtyfQpmaXRuZXNzX2Rpc3RhbmNlXzE1IDwtICBCQ3M1XzE1ICU+JQogIGZpbHRlcihtdXRhdGlvbnM9PTIpICU+JQogIGdyb3VwX2J5KElEYWxpZ24pICU+JQogIHN1bW1hcmlzZShtdXQyZml0PW1lZGlhbihEMDVEMDNmYyksCiAgICAgICAgICAgIG11dDJzZD1zZChEMDVEMDNmYyksCiAgICAgICAgICAgIG51bTJwb2ludHM9bigpKSAlPiUKICByaWdodF9qb2luKGZpdG5lc3NfZGlzdGFuY2VfMTUsYnk9IklEYWxpZ24iKSAKYGBgCgpEZXRlcm1pbmUgbWVkaWFuIGFuZCBzZCBmb3IgMyBtdXRhdGlvbnM6CmBgYHtyfQpmaXRuZXNzX2Rpc3RhbmNlXzE1IDwtICBCQ3M1XzE1ICU+JQogIGZpbHRlcihtdXRhdGlvbnM9PTMpICU+JQogIGdyb3VwX2J5KElEYWxpZ24pICU+JQogIHN1bW1hcmlzZShtdXQzZml0PW1lZGlhbihEMDVEMDNmYyksCiAgICAgICAgICAgIG11dDNzZD1zZChEMDVEMDNmYyksCiAgICAgICAgICAgIG51bTNwb2ludHM9bigpKSAlPiUKICByaWdodF9qb2luKGZpdG5lc3NfZGlzdGFuY2VfMTUsYnk9IklEYWxpZ24iKQpgYGAKCkRldGVybWluZSBtZWRpYW4gYW5kIHNkIGZvciA0IG11dGF0aW9uczoKYGBge3J9CmZpdG5lc3NfZGlzdGFuY2VfMTUgPC0gIEJDczVfMTUgJT4lCiAgZmlsdGVyKG11dGF0aW9ucz09NCkgJT4lCiAgZ3JvdXBfYnkoSURhbGlnbikgJT4lCiAgc3VtbWFyaXNlKG11dDRmaXQ9bWVkaWFuKEQwNUQwM2ZjKSwKICAgICAgICAgICAgbXV0NHNkPXNkKEQwNUQwM2ZjKSwKICAgICAgICAgICAgbnVtNHBvaW50cz1uKCkpICU+JQogIHJpZ2h0X2pvaW4oZml0bmVzc19kaXN0YW5jZV8xNSxieT0iSURhbGlnbiIpCmBgYAoKRGV0ZXJtaW5lIG1lZGlhbiBhbmQgc2QgZm9yIDUgbXV0YXRpb25zOgpgYGB7cn0KZml0bmVzc19kaXN0YW5jZV8xNSA8LSAgQkNzNV8xNSAlPiUKICBmaWx0ZXIobXV0YXRpb25zPT01KSAlPiUKICBncm91cF9ieShJRGFsaWduKSAlPiUKICBzdW1tYXJpc2UobXV0NWZpdD1tZWRpYW4oRDA1RDAzZmMpLAogICAgICAgICAgICBtdXQ1c2Q9c2QoRDA1RDAzZmMpLAogICAgICAgICAgICBudW01cG9pbnRzPW4oKSkgJT4lCiAgcmlnaHRfam9pbihmaXRuZXNzX2Rpc3RhbmNlXzE1LGJ5PSJJRGFsaWduIikKYGBgCgpEZXRlcm1pbmUgY2hhbmdlIGluIGZpdG5lc3M6CmBgYHtyfQpmaXRuZXNzX2Rpc3RhbmNlX251XzE1IDwtIGZpdG5lc3NfZGlzdGFuY2VfMTUgJT4lCiAgbXV0YXRlKG11dDFmaXRuPShtdXQxZml0LWZpdEQwNUQwMyksCiAgICAgICAgIG11dDJmaXRuPShtdXQyZml0LWZpdEQwNUQwMyksCiAgICAgICAgIG11dDNmaXRuPShtdXQzZml0LWZpdEQwNUQwMyksCiAgICAgICAgIG11dDRmaXRuPShtdXQ0Zml0LWZpdEQwNUQwMyksCiAgICAgICAgIG11dDVmaXRuPShtdXQ1Zml0LWZpdEQwNUQwMyksCiAgICAgICAgIG11dDBmaXRuPTApCmBgYAoKTWVsdCBkYXRhIG9uIG51bWJlciBvZiBtdXRhdGlvbnM6CmBgYHtyfQpmaXRuZXNzX2Rpc3RhbmNlX21fMTUgPC0gZml0bmVzc19kaXN0YW5jZV9udV8xNSAlPiUKICBzZWxlY3QoSURhbGlnbixmaXREMDVEMDMsbXV0MGZpdG4sbXV0MWZpdG4sbXV0MmZpdG4sbXV0M2ZpdG4sbXV0NGZpdG4sbXV0NWZpdG4pICU+JQogIGdhdGhlcihtdXRhdGlvbnMsZml0bmVzcyxtdXQwZml0bixtdXQxZml0bixtdXQyZml0bixtdXQzZml0bixtdXQ0Zml0bixtdXQ1Zml0bikKYGBgCgpSZXBsYWNlIG5hbWVzIHdpdGggbnVtYmVyczoKYGBge3J9CmZpdG5lc3NfZGlzdGFuY2VfbV8xNSRtdXRhdGlvbnNbd2hpY2goZml0bmVzc19kaXN0YW5jZV9tXzE1JG11dGF0aW9ucz09Im11dDBmaXRuIildIDwtIGFzLm51bWVyaWMoMCkKZml0bmVzc19kaXN0YW5jZV9tXzE1JG11dGF0aW9uc1t3aGljaChmaXRuZXNzX2Rpc3RhbmNlX21fMTUkbXV0YXRpb25zPT0ibXV0MWZpdG4iKV0gPC0gYXMubnVtZXJpYygxKQpmaXRuZXNzX2Rpc3RhbmNlX21fMTUkbXV0YXRpb25zW3doaWNoKGZpdG5lc3NfZGlzdGFuY2VfbV8xNSRtdXRhdGlvbnM9PSJtdXQyZml0biIpXSA8LSBhcy5udW1lcmljKDIpCmZpdG5lc3NfZGlzdGFuY2VfbV8xNSRtdXRhdGlvbnNbd2hpY2goZml0bmVzc19kaXN0YW5jZV9tXzE1JG11dGF0aW9ucz09Im11dDNmaXRuIildIDwtIGFzLm51bWVyaWMoMykKZml0bmVzc19kaXN0YW5jZV9tXzE1JG11dGF0aW9uc1t3aGljaChmaXRuZXNzX2Rpc3RhbmNlX21fMTUkbXV0YXRpb25zPT0ibXV0NGZpdG4iKV0gPC0gYXMubnVtZXJpYyg0KQpmaXRuZXNzX2Rpc3RhbmNlX21fMTUkbXV0YXRpb25zW3doaWNoKGZpdG5lc3NfZGlzdGFuY2VfbV8xNSRtdXRhdGlvbnM9PSJtdXQ1Zml0biIpXSA8LSBhcy5udW1lcmljKDUpCmBgYAoKUmVtb3ZlIHRob3NlIHdpdGggTkEgZml0bmVzczoKYGBge3J9CmZpdG5lc3NfZGlzdGFuY2VfbV8xNSA8LSBmaXRuZXNzX2Rpc3RhbmNlX21fMTUgJT4lCiAgZmlsdGVyKCFpcy5uYShmaXRuZXNzKSkKYGBgCgojIyMgRml0bmVzcyB2cy4gRGlzdGFuY2UgUGxvdHMKClRoZSBmaXJzdCBwbG90IHZlcnNpb24gdXNlcyB0cmFjZXMgdG8gZGlzcGxheSByZXN1bHRzOgpgYGB7cn0KbGliMTVfZml0X2Rpc3RfNW11dHNfbGluZSA8LSBnZ3Bsb3QoZml0bmVzc19kaXN0YW5jZV9tXzE1LCBhZXMoeD1tdXRhdGlvbnMsIHk9Zml0bmVzcywgZ3JvdXA9SURhbGlnbiwgY29sb3I9SURhbGlnbikpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fbGluZSgpICsKICB4bGFiKCJEaXN0YW5jZSBmcm9tIGhvbW9sb2cgKGEuYS4pIikgKwogIHlsYWIoIkNoYW5nZSBpbiBmaXRuZXNzIHJlbGF0aXZlIHRvIGhvbW9sb2ciKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2JsYWNrJywgc2l6ZSA9IDAuNSksIAogICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCmxpYjE1X2ZpdF9kaXN0XzVtdXRzX2xpbmUKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iTXV0YW50cy9QTE9UUy9MaWIxNS5maXRuZXNzLnZzLmRpc3RhbmNlLmFsbC50cmFjZXMuNWFhLm11dHMuc2NhdHRlci5wbmciLCAKICAgICAgIHBsb3Q9bGliMTVfZml0X2Rpc3RfNW11dHNfbGluZSwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iKQpgYGAKClRoZSBzZWNvbmQgcGxvdCB2ZXJzaW9uIHVzZXMgYSBib3hwbG90IHRvIGRpc3BsYXkgcmVzdWx0czoKYGBge3J9CmxpYjE1X2ZpdF9kaXN0XzVtdXRzX2JveHBsb3QgPC0gZ2dwbG90KGZpdG5lc3NfZGlzdGFuY2VfbV8xNSwgYWVzKHg9bXV0YXRpb25zLCB5PWZpdG5lc3MpKSArCiAgZ2VvbV9ib3hwbG90KGNvbG9yPSJibGFjayIsIGZpbGw9IiMwMDcyQjIiLCBhbHBoYT0wLjgpICsKICB4bGFiKCJEaXN0YW5jZSBmcm9tIGhvbW9sb2cgKGEuYS4pIikgKwogIHlsYWIoIkNoYW5nZSBpbiBmaXRuZXNzIHJlbGF0aXZlIHRvIGhvbW9sb2ciKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2JsYWNrJywgc2l6ZSA9IDAuNSksIAogICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKC0xMCwxMCkpCgpsaWIxNV9maXRfZGlzdF81bXV0c19ib3hwbG90CmBgYAoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9Ik11dGFudHMvUExPVFMvTGliMTUuZml0bmVzcy52cy5kaXN0YW5jZS5hbGwudHJhY2VzLjVhYS5tdXRzLmJveHBsb3QucG5nIiwgCiAgICAgICBwbG90PWxpYjE1X2ZpdF9kaXN0XzVtdXRzX2JveHBsb3QsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYsIHVuaXRzID0gImluIikKYGBgCgpDYWxjdWxhdGUgU3BlYXJtYW4gY29ycmVsYXRpb25zIGJldHdlZW4gZml0bmVzcyBhbmQgZGlzdGFuY2UgZnJvbSBob21vbG9nOgpgYGB7ciwgd2FybmluZz1GQUxTRSwgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgQ2FsY3VsYXRlIFNwZWFybWFuIGNvZWZmaWNpZW50Ogpjb3IoYXMubnVtZXJpYyhmaXRuZXNzX2Rpc3RhbmNlX21fMTUkbXV0YXRpb25zKSxmaXRuZXNzX2Rpc3RhbmNlX21fMTUkZml0bmVzcywKICAgIG1ldGhvZD1jKCJzcGVhcm1hbiIpKQoKIyBSdW4gY29ycmVsYXRpb24gdGVzdDoKY29yLnRlc3QoYXMubnVtZXJpYyhmaXRuZXNzX2Rpc3RhbmNlX21fMTUkbXV0YXRpb25zKSxmaXRuZXNzX2Rpc3RhbmNlX21fMTUkZml0bmVzcywKICAgICAgICAgbWV0aG9kPWMoInNwZWFybWFuIikpCmBgYAoKIyMgUGxvdCBNdXRhbnRzIHBlciBIb21vbG9nCjxmb250IGNvbG9yPSJibHVlIj4qKlRoaXMgc2VjdGlvbiBpcyBiYXNlZCBvbiB0aGUgUiBmaWxlOiAiUl9wbG90X2FsbF9tdXRhbnRzLlIiLioqPC9mb250PiBJdCBkZXNjcmliZXMgaG93IHRvIHBsb3QgYWxsIG11dGFudHMgcGVyIGhvbW9sb2cgaW5kZXBlbmRlbnRseS4KCkRldGVybWluZSB0aGUgbnVtYmVyIG9mIG11dGFudHMgcGVyIGhvbW9sb2c6CmBgYHtyfQojIExpYjE1Cm11dGFudHNwZXJob21vbG9nMTUgPC0gbXV0SURpbmZvMTUgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyAhPSAwKSAlPiUKICBzZWxlY3QobXV0SUQsSURhbGlnbikgJT4lCiAgZGlzdGluY3QoKSAlPiUKICBncm91cF9ieShJRGFsaWduKSAlPiUKICBzdW1tYXJpc2UoY291bnQ9bigpKQoKIyBMaWIxNgptdXRhbnRzcGVyaG9tb2xvZzE2IDwtIG11dElEaW5mbzE2ICU+JQogIGZpbHRlcihtdXRhdGlvbnMgIT0gMCkgJT4lCiAgc2VsZWN0KG11dElELElEYWxpZ24pICU+JQogIGRpc3RpbmN0KCkgJT4lCiAgZ3JvdXBfYnkoSURhbGlnbikgJT4lCiAgc3VtbWFyaXNlKGNvdW50PW4oKSkKYGBgCgpBZGQgYSBjb2x1bW4gYW5kIGxhYmVsIGVhY2ggSUQgZm9yIHRoZSBsaWJyYXJ5IGl0IGNvbWVzIGZyb20gdG8ga2VlcCB0cmFjayBvZiB0aGUgZGF0YSBzb3VyY2U6CmBgYHtyfQojIExpYjE1Cm11dGFudHNwZXJob21vbG9nMTUkbGliIDwtICJMaWIxNSIKCiMgTGliMTYKbXV0YW50c3BlcmhvbW9sb2cxNiRsaWIgPC0gIkxpYjE2IgpgYGAKCkNvbWJpbmUgYm90aCBsaWJyYXJ5IGRhdGFzZXRzIGZvciBwbG90dGluZzoKYGBge3J9Cm11dGFudHNwZXJob21vbG9nXzE1XzE2IDwtIGJpbmRfcm93cyhtdXRhbnRzcGVyaG9tb2xvZzE1LCBtdXRhbnRzcGVyaG9tb2xvZzE2LCAuaWQgPSAibGlicmFyeSIpCmBgYAoKUGxvdCB0aGUgbXV0YW50IGNvdW50IGZvciBib3RoIGxpYnJhcmllczoKYGBge3J9Cm11dGFudHNwZXJob21vbG9nXzE1XzE2X3Bsb3QgPC0gZ2dwbG90KG11dGFudHNwZXJob21vbG9nXzE1XzE2LCBhZXMoeCA9IGxpYnJhcnksIHkgPSBjb3VudCwgZmlsbCA9IGxpYnJhcnkpKSArCiAgZ2VvbV92aW9saW4oY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuNzUpICsKICB4bGFiKCJMaWJyYXJ5IikgKwogIHlsYWIoIk11dGFudHMgcGVyIGhvbW9sb2ciKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZSgKICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMC41KSwKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKDAsIDYwMCkpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjMDA3MkIyIiwgIiNFNjlGMDAiKSkgKwogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gYygiTGlicmFyeSAxNSIsICJMaWJyYXJ5IDE2IikpCgptdXRhbnRzcGVyaG9tb2xvZ18xNV8xNl9wbG90CmBgYAoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9Ik11dGFudHMvUExPVFMvTGliMTUuMTYubXV0YW50cy5wZXIuaG9tb2xvZy52aW9saW4ucG5nIiwgcGxvdD1tdXRhbnRzcGVyaG9tb2xvZ18xNV8xNl9wbG90LAogICAgICAgZHBpPTYwMCwgd2lkdGggPSA2LCBoZWlnaHQgPSA2LCB1bml0cyA9ICJpbiIpCmBgYAoKQ2FsY3VsYXRlIHRoZSBtZWRpYW4gbXV0YW50IGNvdW50cyBmb3IgZWFjaCBkaXN0aW5jdCBob21vbG9nOgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KI0xpYjE1CgojTWVkaWFuIG11dGFudCBjb3VudCBwZXIgaG9tb2xvZwptZWRpYW4obXV0YW50c3BlcmhvbW9sb2cxNSRjb3VudCkKCiNMaWIxNgoKI01lZGlhbiBtdXRhbnQgY291bnQgcGVyIGhvbW9sb2cKbWVkaWFuKG11dGFudHNwZXJob21vbG9nMTYkY291bnQpCmBgYAoKQ2FsY3VsYXRlIHRoZSBtZWFuIG11dGFudCBjb3VudHMgZm9yIGVhY2ggZGlzdGluY3QgaG9tb2xvZzoKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiNMaWIxNQoKI01lYW4gbXV0YW50IGNvdW50IHBlciBob21vbG9nCm1lYW4obXV0YW50c3BlcmhvbW9sb2cxNSRjb3VudCkKCiNMaWIxNgoKI01lYW4gbXV0YW50IGNvdW50IHBlciBob21vbG9nCm1lYW4obXV0YW50c3BlcmhvbW9sb2cxNiRjb3VudCkKYGBgCgo8Zm9udCBjb2xvcj0iZ3JlZW4iPklmIGJvdGggbWVhc3VyZXMgKG1lYW4gYW5kIG1lZGlhbikgYXJlIGNvbnNpZGVyYWJseSBkaWZmZXJlbnQsIHRoaXMgaW5kaWNhdGVzIHRoYXQgdGhlIGRhdGEgYXJlIHNrZXdlZCAoaS5lLiB0aGV5IGFyZSBmYXIgZnJvbSBiZWluZyBub3JtYWxseSBkaXN0cmlidXRlZCkgYW5kIHRoZSAqKk1FRElBTioqIGdlbmVyYWxseSBnaXZlcyBhIG1vcmUgYXBwcm9wcmlhdGUgaWRlYSBvZiB0aGUgZGF0YSBkaXN0cmlidXRpb24uPC9mb250PgoKIyMjIEhvbW9sb2dzIHcvIE1vc3QgTXV0YW50cwoKRGV0ZXJtaW5lIHRoZSB0b3AgMTAgaG9tb2xvZ3Mgd2l0aCB0aGUgZ3JlYXRlc3QgbnVtYmVyIG9mIHVuaXF1ZSBtdXRhbnRzLiBBcnJhbmdlIGJ5IGNvdW50cyAoZ3JlYXRlc3QgdG8gbGVhc3QpOgpgYGB7cn0KI0xpYjE1Cm11dGFudHNwZXJob21vbG9nMTUgPC0gbXV0YW50c3BlcmhvbW9sb2cxNSAlPiUKICBhcnJhbmdlKC1jb3VudCkKCiNMaWIxNgptdXRhbnRzcGVyaG9tb2xvZzE2IDwtIG11dGFudHNwZXJob21vbG9nMTYgJT4lCiAgYXJyYW5nZSgtY291bnQpCmBgYAoKPGZvbnQgY29sb3I9ImdyZWVuIj4qKlNlbGVjdCB0aGUgdG9wIDEwIGhvbW9sb2dzIGJhc2VkIG9uIGdyZWF0ZXN0IG11dGFudCBjb3VudHMqKjwvZm9udD4KYGBge3IgZWNobz1GQUxTRX0KI0xpYjE1CgojIERpc3BsYXkgZGF0YWZyYW1lIHdpdGgga2FibGUgZm9ybWF0dGluZwptdXRhbnRzcGVyaG9tb2xvZzE1XzEwLmNvdW50IDwtIGhlYWQobXV0YW50c3BlcmhvbW9sb2cxNSwgMTApCgprbml0cjo6a2FibGUobXV0YW50c3BlcmhvbW9sb2cxNV8xMC5jb3VudCwKICBjb2wubmFtZXMgPSBjKCdJRCBBbGlnbicsICdDb3VudCcsICdMaWJyYXJ5JyksCiAgYWxpZ24gPSAibGxsIiwKICBmb3JtYXQuYXJncyA9IGxpc3QoYmlnLm1hcmsgPSAiLCIpKQoKI0xpYjE2CgojIERpc3BsYXkgZGF0YWZyYW1lIHdpdGgga2FibGUgZm9ybWF0dGluZwptdXRhbnRzcGVyaG9tb2xvZzE2XzEwLmNvdW50IDwtIGhlYWQobXV0YW50c3BlcmhvbW9sb2cxNiwgMTApCgprbml0cjo6a2FibGUobXV0YW50c3BlcmhvbW9sb2cxNl8xMC5jb3VudCwKICBjb2wubmFtZXMgPSBjKCdJRCBBbGlnbicsICdDb3VudCcsICdMaWJyYXJ5JyksCiAgYWxpZ24gPSAibGxsIiwKICBmb3JtYXQuYXJncyA9IGxpc3QoYmlnLm1hcmsgPSAiLCIpKQpgYGAKCk1ha2Uga2V5cyBmb3IgcGxvdHRpbmc6CmBgYHtyfQojTGliMTUKCm11dGFudHNwZXJob21vbG9nMTUka2V5IDwtIDE6bGVuZ3RoKG11dGFudHNwZXJob21vbG9nMTUkY291bnQpCgptdXRhbnRzcGVyaG9tb2xvZzE1XzEwIDwtIG11dGFudHNwZXJob21vbG9nMTUgJT4lCiAgZmlsdGVyKGtleTwxMSkKCm11dGFudHNkaXN0MTUgPC0gbXV0SURpbmZvMTUgJT4lCiAgZmlsdGVyKElEYWxpZ24gJWluJSBtdXRhbnRzcGVyaG9tb2xvZzE1XzEwJElEYWxpZ24pICU+JQogIGZpbHRlcihtdXRhdGlvbnM+LTEpCgojTGliMTYKCm11dGFudHNwZXJob21vbG9nMTYka2V5IDwtIDE6bGVuZ3RoKG11dGFudHNwZXJob21vbG9nMTYkY291bnQpCgptdXRhbnRzcGVyaG9tb2xvZzE2XzEwIDwtIG11dGFudHNwZXJob21vbG9nMTYgJT4lCiAgZmlsdGVyKGtleTwxMSkKCm11dGFudHNkaXN0MTYgPC0gbXV0SURpbmZvMTYgJT4lCiAgZmlsdGVyKElEYWxpZ24gJWluJSBtdXRhbnRzcGVyaG9tb2xvZzE2XzEwJElEYWxpZ24pICU+JQogIGZpbHRlcihtdXRhdGlvbnM+LTEpCmBgYAoKUGxvdCB0aGUgdG9wIDEwIGhvbW9sb2dzIHdpdGggdGhlIGdyZWF0ZXN0IG51bWJlciBvZiBtdXRhbnRzLiBDYWxjdWxhdGUgbWVhbiBtdXRhbnRzIGFuZCBTRCBiYXNlZCBvbiB0b3RhbCBjb3VudHM6CmBgYHtyfQpMaWIxNV90b3AxMF9tdXRzIDwtIGdncGxvdChtdXRhbnRzZGlzdDE1LCBhZXMoeD1JRGFsaWduLCB5PW11dGF0aW9ucykpKwogIGdlb21fYm94cGxvdChjb2xvcj0iYmxhY2siLCBmaWxsPSIjMDA3MkIyIiwgYWxwaGE9MC43NSkgKwogIGdndGl0bGUoIkxpYnJhcnkgMTUiKSArCiAgeGxhYigiIikgKwogIHlsYWIoIkRpc3RyaWJ1dGlvbiBvZiBNdXRhbnRzIGF0IERpc3RhbmNlIChhLmEuKSIpICsKICBjb29yZF9mbGlwKCkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICdibGFjaycsIHNpemUgPSAwLjUpLCAKICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpMaWIxNV90b3AxMF9tdXRzCmBgYAoKYGBge3J9CkxpYjE2X3RvcDEwX211dHMgPC0gZ2dwbG90KG11dGFudHNkaXN0MTYsIGFlcyh4PUlEYWxpZ24sIHk9bXV0YXRpb25zKSkrCiAgZ2VvbV9ib3hwbG90KGNvbG9yPSJibGFjayIsIGZpbGw9IiNFNjlGMDAiKSArCiAgZ2d0aXRsZSgiTGlicmFyeSAxNiIpICsKICB4bGFiKCIiKSArCiAgeWxhYigiRGlzdHJpYnV0aW9uIG9mIE11dGFudHMgYXQgRGlzdGFuY2UgKGEuYS4pIikgKwogIGNvb3JkX2ZsaXAoKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2JsYWNrJywgc2l6ZSA9IDAuNSksIAogICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCkxpYjE2X3RvcDEwX211dHMKYGBgCgpgYGB7cn0KcGF0Y2gyIDwtIChMaWIxNV90b3AxMF9tdXRzIHwgTGliMTZfdG9wMTBfbXV0cykKcGF0Y2gyCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9Ik11dGFudHMvUExPVFMvTGliMTUuMTYubXV0YW50cy5hdC5kaXN0YW5jZS50b3AxMC5wbmciLCBwbG90PXBhdGNoMiwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gMTYsIGhlaWdodCA9IDEyLCB1bml0cyA9ICJpbiIpCmBgYAoKIyMgTXV0YW50IEZpdG5lc3MKCiMjIyBIb21vbG9nIE11dGFudCBDb3VudHMKClN1bW1hcml6ZSB0aGUgbnVtYmVyIG9mIHVuaXF1ZSAiSURhbGlnbiIgcmVjb3ZlcmVkIGluIHRoZSBgbXV0SURpbmZvYCBvYmplY3Qgd2l0aCAwIG11dGF0aW9ucywgMCsxIG11dGF0aW9ucywgMCsxKzIgbXV0YXRpb25zLCAwKzErMiszIG11dGF0aW9ucywgMCsxKzIrMys0IG11dGF0aW9ucywgYW5kIDArMSsyKzMrNCs1IG11dGF0aW9ucyB0aGF0IGNhbiBjb21wbGVtZW50IERIRlIgZnVuY3Rpb24gKGZpdG5lc3MgPiAtMSkuIEFsc28sIG9ubHkgcmV0YWluIHBlcmZlY3RzIChtdXRhdGlvbnMgPSAwKSBpZiBudW1wcnVuZWRCQ3MgPiA1LiBJZ25vcmUgbnVtcHJ1bmVkQkNzIGZvciBtdXRhdGlvbnMgPSAxLDIsMyw0LDU6CgojIyMjIExpYjE1CmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIFVuaXF1ZSBJRGFsaWduIHdpdGggMCBtdXRhdGlvbnMgZm9yIENvbXBsZW1lbnRhdGlvbiAoZml0RDA1RDAzKQpMaWIxNS5tdXQuMC5yZXN1bHQgPC0gbXV0SURpbmZvMTUgJT4lCiAgZmlsdGVyKCFpcy5uYShmaXREMDVEMDMpICYgCiAgICAgICAgIGZpdEQwNUQwMyA+PSAtMSAmIAogICAgICAgICBtdXRhdGlvbnMgPT0gMCAmCiAgICAgICAgIG51bXBydW5lZEJDcyA+PSA1KSAlPiUKICBkaXN0aW5jdChJRGFsaWduKSAlPiUKICBucm93KCkKCnByaW50KExpYjE1Lm11dC4wLnJlc3VsdCkKCiMgVW5pcXVlIElEYWxpZ24gd2l0aCAwKzEgbXV0YXRpb25zIGZvciBDb21wbGVtZW50YXRpb24gKGZpdEQwNUQwMykKTGliMTUubXV0LjAuMS5yZXN1bHQgPC0gbXV0SURpbmZvMTUgJT4lCiAgZmlsdGVyKCFpcy5uYShmaXREMDVEMDMpICYgCiAgICAgICAgIGZpdEQwNUQwMyA+PSAtMSAmIAogICAgICAgICAoKG11dGF0aW9ucyA9PSAwICYgbnVtcHJ1bmVkQkNzID49IDUpIHwgbXV0YXRpb25zID09IDEpKSAlPiUKICBkaXN0aW5jdChJRGFsaWduKSAlPiUKICBucm93KCkKCnByaW50KExpYjE1Lm11dC4wLjEucmVzdWx0KQoKIyBVbmlxdWUgSURhbGlnbiB3aXRoIDArMSsyIG11dGF0aW9ucyBmb3IgQ29tcGxlbWVudGF0aW9uIChmaXREMDVEMDMpCkxpYjE1Lm11dC4wLjEuMi5yZXN1bHQgPC0gbXV0SURpbmZvMTUgJT4lCiAgZmlsdGVyKCFpcy5uYShmaXREMDVEMDMpICYgCiAgICAgICAgIGZpdEQwNUQwMyA+PSAtMSAmIAogICAgICAgICAoKG11dGF0aW9ucyA9PSAwICYgbnVtcHJ1bmVkQkNzID49IDUpIHwgCiAgICAgICAgICBtdXRhdGlvbnMgPT0gMSB8IAogICAgICAgICAgbXV0YXRpb25zID09IDIpKSAlPiUKICBkaXN0aW5jdChJRGFsaWduKSAlPiUKICBucm93KCkKCnByaW50KExpYjE1Lm11dC4wLjEuMi5yZXN1bHQpCgojIFVuaXF1ZSBJRGFsaWduIHdpdGggMCsxKzIrMyBtdXRhdGlvbnMgZm9yIENvbXBsZW1lbnRhdGlvbiAoZml0RDA1RDAzKQpMaWIxNS5tdXQuMC4xLjIuMy5yZXN1bHQgPC0gbXV0SURpbmZvMTUgJT4lCiAgZmlsdGVyKCFpcy5uYShmaXREMDVEMDMpICYgCiAgICAgICAgIGZpdEQwNUQwMyA+PSAtMSAmIAogICAgICAgICAoKG11dGF0aW9ucyA9PSAwICYgbnVtcHJ1bmVkQkNzID49IDUpIHwgCiAgICAgICAgICBtdXRhdGlvbnMgPT0gMSB8IAogICAgICAgICAgbXV0YXRpb25zID09IDIgfAogICAgICAgICAgbXV0YXRpb25zID09IDMpKSAlPiUKICBkaXN0aW5jdChJRGFsaWduKSAlPiUKICBucm93KCkKCnByaW50KExpYjE1Lm11dC4wLjEuMi4zLnJlc3VsdCkKCiMgVW5pcXVlIElEYWxpZ24gd2l0aCAwKzErMiszKzQgbXV0YXRpb25zIGZvciBDb21wbGVtZW50YXRpb24gKGZpdEQwNUQwMykKTGliMTUubXV0LjAuMS4yLjMuNC5yZXN1bHQgPC0gbXV0SURpbmZvMTUgJT4lCiAgZmlsdGVyKCFpcy5uYShmaXREMDVEMDMpICYgCiAgICAgICAgIGZpdEQwNUQwMyA+PSAtMSAmIAogICAgICAgICAoKG11dGF0aW9ucyA9PSAwICYgbnVtcHJ1bmVkQkNzID49IDUpIHwgCiAgICAgICAgICBtdXRhdGlvbnMgPT0gMSB8IAogICAgICAgICAgbXV0YXRpb25zID09IDIgfAogICAgICAgICAgbXV0YXRpb25zID09IDMgfAogICAgICAgICAgbXV0YXRpb25zID09IDQpKSAlPiUKICBkaXN0aW5jdChJRGFsaWduKSAlPiUKICBucm93KCkKCnByaW50KExpYjE1Lm11dC4wLjEuMi4zLjQucmVzdWx0KQoKIyBVbmlxdWUgSURhbGlnbiB3aXRoIDArMSsyKzMrNCs1IG11dGF0aW9ucyBmb3IgQ29tcGxlbWVudGF0aW9uIChmaXREMDVEMDMpCkxpYjE1Lm11dC4wLjEuMi4zLjQuNS5yZXN1bHQgPC0gbXV0SURpbmZvMTUgJT4lCiAgZmlsdGVyKCFpcy5uYShmaXREMDVEMDMpICYgCiAgICAgICAgIGZpdEQwNUQwMyA+PSAtMSAmIAogICAgICAgICAoKG11dGF0aW9ucyA9PSAwICYgbnVtcHJ1bmVkQkNzID49IDUpIHwgCiAgICAgICAgICBtdXRhdGlvbnMgPT0gMSB8IAogICAgICAgICAgbXV0YXRpb25zID09IDIgfAogICAgICAgICAgbXV0YXRpb25zID09IDMgfAogICAgICAgICAgbXV0YXRpb25zID09IDQgfAogICAgICAgICAgbXV0YXRpb25zID09IDUpKSAlPiUKICBkaXN0aW5jdChJRGFsaWduKSAlPiUKICBucm93KCkKCnByaW50KExpYjE1Lm11dC4wLjEuMi4zLjQuNS5yZXN1bHQpCmBgYAoKIyMjIyBMaWIxNgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBVbmlxdWUgSURhbGlnbiB3aXRoIDAgbXV0YXRpb25zIGZvciBDb21wbGVtZW50YXRpb24gKGZpdEQxMkQwNCkKTGliMTYubXV0LjAucmVzdWx0IDwtIG11dElEaW5mbzE2ICU+JQogIGZpbHRlcighaXMubmEoZml0RDEyRDA0KSAmIAogICAgICAgICBmaXREMTJEMDQgPj0gLTEgJiAKICAgICAgICAgbXV0YXRpb25zID09IDAgJgogICAgICAgICBudW1wcnVuZWRCQ3MgPj0gNSkgJT4lCiAgZGlzdGluY3QoSURhbGlnbikgJT4lCiAgbnJvdygpCgpwcmludChMaWIxNi5tdXQuMC5yZXN1bHQpCgojIFVuaXF1ZSBJRGFsaWduIHdpdGggMCsxIG11dGF0aW9ucyBmb3IgQ29tcGxlbWVudGF0aW9uIChmaXREMTJEMDQpCkxpYjE2Lm11dC4wLjEucmVzdWx0IDwtIG11dElEaW5mbzE2ICU+JQogIGZpbHRlcighaXMubmEoZml0RDEyRDA0KSAmIAogICAgICAgICBmaXREMTJEMDQgPj0gLTEgJiAKICAgICAgICAgKChtdXRhdGlvbnMgPT0gMCAmIG51bXBydW5lZEJDcyA+PSA1KSB8IG11dGF0aW9ucyA9PSAxKSkgJT4lCiAgZGlzdGluY3QoSURhbGlnbikgJT4lCiAgbnJvdygpCgpwcmludChMaWIxNi5tdXQuMC4xLnJlc3VsdCkKCiMgVW5pcXVlIElEYWxpZ24gd2l0aCAwKzErMiBtdXRhdGlvbnMgZm9yIENvbXBsZW1lbnRhdGlvbiAoZml0RDEyRDA0KQpMaWIxNi5tdXQuMC4xLjIucmVzdWx0IDwtIG11dElEaW5mbzE2ICU+JQogIGZpbHRlcighaXMubmEoZml0RDEyRDA0KSAmIAogICAgICAgICBmaXREMTJEMDQgPj0gLTEgJiAKICAgICAgICAgKChtdXRhdGlvbnMgPT0gMCAmIG51bXBydW5lZEJDcyA+PSA1KSB8IAogICAgICAgICAgbXV0YXRpb25zID09IDEgfCAKICAgICAgICAgIG11dGF0aW9ucyA9PSAyKSkgJT4lCiAgZGlzdGluY3QoSURhbGlnbikgJT4lCiAgbnJvdygpCgpwcmludChMaWIxNi5tdXQuMC4xLjIucmVzdWx0KQoKIyBVbmlxdWUgSURhbGlnbiB3aXRoIDArMSsyKzMgbXV0YXRpb25zIGZvciBDb21wbGVtZW50YXRpb24gKGZpdEQxMkQwNCkKTGliMTYubXV0LjAuMS4yLjMucmVzdWx0IDwtIG11dElEaW5mbzE2ICU+JQogIGZpbHRlcighaXMubmEoZml0RDEyRDA0KSAmIAogICAgICAgICBmaXREMTJEMDQgPj0gLTEgJiAKICAgICAgICAgKChtdXRhdGlvbnMgPT0gMCAmIG51bXBydW5lZEJDcyA+PSA1KSB8IAogICAgICAgICAgbXV0YXRpb25zID09IDEgfCAKICAgICAgICAgIG11dGF0aW9ucyA9PSAyIHwKICAgICAgICAgIG11dGF0aW9ucyA9PSAzKSkgJT4lCiAgZGlzdGluY3QoSURhbGlnbikgJT4lCiAgbnJvdygpCgpwcmludChMaWIxNi5tdXQuMC4xLjIuMy5yZXN1bHQpCgojIFVuaXF1ZSBJRGFsaWduIHdpdGggMCsxKzIrMys0IG11dGF0aW9ucyBmb3IgQ29tcGxlbWVudGF0aW9uIChmaXREMTJEMDQpCkxpYjE2Lm11dC4wLjEuMi4zLjQucmVzdWx0IDwtIG11dElEaW5mbzE2ICU+JQogIGZpbHRlcighaXMubmEoZml0RDEyRDA0KSAmIAogICAgICAgICBmaXREMTJEMDQgPj0gLTEgJiAKICAgICAgICAgKChtdXRhdGlvbnMgPT0gMCAmIG51bXBydW5lZEJDcyA+PSA1KSB8IAogICAgICAgICAgbXV0YXRpb25zID09IDEgfCAKICAgICAgICAgIG11dGF0aW9ucyA9PSAyIHwKICAgICAgICAgIG11dGF0aW9ucyA9PSAzIHwKICAgICAgICAgIG11dGF0aW9ucyA9PSA0KSkgJT4lCiAgZGlzdGluY3QoSURhbGlnbikgJT4lCiAgbnJvdygpCgpwcmludChMaWIxNi5tdXQuMC4xLjIuMy40LnJlc3VsdCkKCiMgVW5pcXVlIElEYWxpZ24gd2l0aCAwKzErMiszKzQrNSBtdXRhdGlvbnMgZm9yIENvbXBsZW1lbnRhdGlvbiAoZml0RDEyRDA0KQpMaWIxNi5tdXQuMC4xLjIuMy40LjUucmVzdWx0IDwtIG11dElEaW5mbzE2ICU+JQogIGZpbHRlcighaXMubmEoZml0RDEyRDA0KSAmIAogICAgICAgICBmaXREMTJEMDQgPj0gLTEgJiAKICAgICAgICAgKChtdXRhdGlvbnMgPT0gMCAmIG51bXBydW5lZEJDcyA+PSA1KSB8IAogICAgICAgICAgbXV0YXRpb25zID09IDEgfCAKICAgICAgICAgIG11dGF0aW9ucyA9PSAyIHwKICAgICAgICAgIG11dGF0aW9ucyA9PSAzIHwKICAgICAgICAgIG11dGF0aW9ucyA9PSA0IHwKICAgICAgICAgIG11dGF0aW9ucyA9PSA1KSkgJT4lCiAgZGlzdGluY3QoSURhbGlnbikgJT4lCiAgbnJvdygpCgpwcmludChMaWIxNi5tdXQuMC4xLjIuMy40LjUucmVzdWx0KQpgYGAKCiMjIyMgQ29tYmluZWQgQ29kb25zCgpGaWx0ZXIgYm90aCBtdXRJRGluZm8gZGF0YXNldHMgdG8gcmV0YWluIG9ubHkgdGhlIHJlbGV2YW50IGNvbHVtbnMgcHJpb3IgdG8gbWVyZ2luZzoKYGBge3J9CiMgTGliMTUKbXV0SURpbmZvMTUuc3Vic2V0IDwtIG11dElEaW5mbzE1ICU+JQogIGZpbHRlcihtdXRhdGlvbnMgPD0gNSwKICAgICAgICAgZml0RDA1RDAzID49IC0xLAogICAgICAgICAobXV0YXRpb25zICE9IDAgfCAobXV0YXRpb25zID09IDAgJiBudW1wcnVuZWRCQ3MgPj0gNSkpKSAlPiUKICBzZWxlY3QobXV0SUQsIElEYWxpZ24sIG51bXBydW5lZEJDcywgbXV0YXRpb25zLCBmaXREMDVEMDMpCgojIExpYjE2Cm11dElEaW5mbzE2LnN1YnNldCA8LSBtdXRJRGluZm8xNiAlPiUKICBmaWx0ZXIobXV0YXRpb25zIDw9IDUsCiAgICAgICAgIGZpdEQxMkQwNCA+PSAtMSwKICAgICAgICAgKG11dGF0aW9ucyAhPSAwIHwgKG11dGF0aW9ucyA9PSAwICYgbnVtcHJ1bmVkQkNzID49IDUpKSkgJT4lCiAgc2VsZWN0KG11dElELCBJRGFsaWduLCBudW1wcnVuZWRCQ3MsIG11dGF0aW9ucywgZml0RDEyRDA0KQpgYGAKCkNvbWJpbmUgdGhlIHNoYXJlZCBtdXRJRHMgYmV0d2VlbiBkYXRhc2V0czoKYGBge3J9Cm11dElEaW5mbzE1LjE2LnNoYXJlZC5zdWJzZXQgPC0gaW5uZXJfam9pbihtdXRJRGluZm8xNS5zdWJzZXQsIG11dElEaW5mbzE2LnN1YnNldCwgYnkgPSAibXV0SUQiLCBzdWZmaXggPSBjKCIuMTUiLCAiLjE2IikpCmBgYAoKQ29tYmluZSB0aGUgdW5pcXVlIG11dElEcyBiZXR3ZWVuIGRhdGFzZXRzOgpgYGB7cn0KIyBSb3dzIHVuaXF1ZSB0byBtdXRJRGluZm8xNS5zdWJzZXQKbXV0SURpbmZvLnVuaXF1ZV90b18xNSA8LSBhbnRpX2pvaW4obXV0SURpbmZvMTUuc3Vic2V0LCBtdXRJRGluZm8xNi5zdWJzZXQsIGJ5ID0gIm11dElEIikKCiMgUm93cyB1bmlxdWUgdG8gbXV0SURpbmZvMTYuc3Vic2V0Cm11dElEaW5mby51bmlxdWVfdG9fMTYgPC0gYW50aV9qb2luKG11dElEaW5mbzE2LnN1YnNldCwgbXV0SURpbmZvMTUuc3Vic2V0LCBieSA9ICJtdXRJRCIpCgojIENvbWJpbmUgdGhlIHVuaXF1ZSByb3dzCm11dElEaW5mby4xNS4xNi51bmlxdWUuc3Vic2V0IDwtIGJpbmRfcm93cygKICBtdXRJRGluZm8udW5pcXVlX3RvXzE1ICU+JSBtdXRhdGUoc291cmNlID0gIkxpYjE1IiksCiAgbXV0SURpbmZvLnVuaXF1ZV90b18xNiAlPiUgbXV0YXRlKHNvdXJjZSA9ICJMaWIxNiIpKQpgYGAKCk11dGF0aW9ucyA9IDA6IENvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIElEYWxpZ24gaW4gdGhlIHNoYXJlZCBhbmQgdW5pcXVlIGRhdGFzZXRzOgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBTaGFyZWQ6IFVuaXF1ZSBJRGFsaWduIHdpdGggMCBtdXRhdGlvbnMgZm9yIENvbXBsZW1lbnRhdGlvbiAoZml0RDA1RDAzIGFuZCBmaXREMTJEMDQpCkxpYjE1LjE2Lm11dC4wLnNoYXJlZC5yZXN1bHQgPC0gbXV0SURpbmZvMTUuMTYuc2hhcmVkLnN1YnNldCAlPiUKICBmaWx0ZXIobXV0YXRpb25zLjE1ID09IDAgfCBtdXRhdGlvbnMuMTYgPT0gMCkgJT4lCiAgZGlzdGluY3QoSURhbGlnbi4xNSwgSURhbGlnbi4xNikgJT4lCiAgbnJvdygpCgpwcmludChMaWIxNS4xNi5tdXQuMC5zaGFyZWQucmVzdWx0KQoKIyBVbmlxdWU6IFVuaXF1ZSBJRGFsaWduIHdpdGggMCBtdXRhdGlvbnMgZm9yIENvbXBsZW1lbnRhdGlvbiAoZml0RDA1RDAzIGFuZCBmaXREMTJEMDQpCkxpYjE1LjE2Lm11dC4wLnVuaXF1ZS5yZXN1bHQgPC0gbXV0SURpbmZvLjE1LjE2LnVuaXF1ZS5zdWJzZXQgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyA9PSAwKSAlPiUKICBkaXN0aW5jdChJRGFsaWduKSAlPiUKICBucm93KCkKCnByaW50KExpYjE1LjE2Lm11dC4wLnVuaXF1ZS5yZXN1bHQpCgojIFN1bSBvZiBib3RoIENvZG9uIHZlcnNpb24gZm9yIDAgbXV0YXRpb25zCkxpYjE1LjE2Lm11dC4wLnNoYXJlZC51bmlxdWUucmVzdWx0IDwtIExpYjE1LjE2Lm11dC4wLnNoYXJlZC5yZXN1bHQgKyBMaWIxNS4xNi5tdXQuMC51bmlxdWUucmVzdWx0CgpwcmludChMaWIxNS4xNi5tdXQuMC5zaGFyZWQudW5pcXVlLnJlc3VsdCkKYGBgCgpNdXRhdGlvbnMgPSAwKzE6IENvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIElEYWxpZ24gaW4gdGhlIHNoYXJlZCBhbmQgdW5pcXVlIGRhdGFzZXRzOgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBTaGFyZWQ6IFVuaXF1ZSBJRGFsaWduIHdpdGggMCBvciAxIG11dGF0aW9ucyBmb3IgQ29tcGxlbWVudGF0aW9uIChmaXREMDVEMDMgYW5kIGZpdEQxMkQwNCkKTGliMTUuMTYubXV0LjAuMS5zaGFyZWQucmVzdWx0IDwtIG11dElEaW5mbzE1LjE2LnNoYXJlZC5zdWJzZXQgJT4lCiAgZmlsdGVyKG11dGF0aW9ucy4xNSAlaW4lIGMoMCwgMSkgfCBtdXRhdGlvbnMuMTYgJWluJSBjKDAsIDEpKSAlPiUKICBkaXN0aW5jdChJRGFsaWduLjE1LCBJRGFsaWduLjE2KSAlPiUKICBucm93KCkKCnByaW50KExpYjE1LjE2Lm11dC4wLjEuc2hhcmVkLnJlc3VsdCkKCiMgVW5pcXVlOiBVbmlxdWUgSURhbGlnbiB3aXRoIDAgb3IgMSBtdXRhdGlvbnMgZm9yIENvbXBsZW1lbnRhdGlvbiAoZml0RDA1RDAzIGFuZCBmaXREMTJEMDQpCkxpYjE1LjE2Lm11dC4wLjEudW5pcXVlLnJlc3VsdCA8LSBtdXRJRGluZm8uMTUuMTYudW5pcXVlLnN1YnNldCAlPiUKICBmaWx0ZXIobXV0YXRpb25zICVpbiUgYygwLCAxKSkgJT4lCiAgZGlzdGluY3QoSURhbGlnbikgJT4lCiAgbnJvdygpCgpwcmludChMaWIxNS4xNi5tdXQuMC4xLnVuaXF1ZS5yZXN1bHQpCgojIFN1bSBvZiBib3RoIENvZG9uIHZlcnNpb25zIGZvciAwIG9yIDEgbXV0YXRpb25zCkxpYjE1LjE2Lm11dC4wLjEuc2hhcmVkLnVuaXF1ZS5yZXN1bHQgPC0gTGliMTUuMTYubXV0LjAuMS5zaGFyZWQucmVzdWx0ICsgTGliMTUuMTYubXV0LjAuMS51bmlxdWUucmVzdWx0CgpwcmludChMaWIxNS4xNi5tdXQuMC4xLnNoYXJlZC51bmlxdWUucmVzdWx0KQpgYGAKCk11dGF0aW9ucyA9IDArMSsyOiBDb3VudCB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBJRGFsaWduIGluIHRoZSBzaGFyZWQgYW5kIHVuaXF1ZSBkYXRhc2V0czoKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgU2hhcmVkOiBVbmlxdWUgSURhbGlnbiB3aXRoIDAgb3IgMSBtdXRhdGlvbnMgZm9yIENvbXBsZW1lbnRhdGlvbiAoZml0RDA1RDAzIGFuZCBmaXREMTJEMDQpCkxpYjE1LjE2Lm11dC4wLjEuMi5zaGFyZWQucmVzdWx0IDwtIG11dElEaW5mbzE1LjE2LnNoYXJlZC5zdWJzZXQgJT4lCiAgZmlsdGVyKG11dGF0aW9ucy4xNSAlaW4lIGMoMCwgMSwgMikgfCBtdXRhdGlvbnMuMTYgJWluJSBjKDAsIDEsIDIpKSAlPiUKICBkaXN0aW5jdChJRGFsaWduLjE1LCBJRGFsaWduLjE2KSAlPiUKICBucm93KCkKCnByaW50KExpYjE1LjE2Lm11dC4wLjEuMi5zaGFyZWQucmVzdWx0KQoKIyBVbmlxdWU6IFVuaXF1ZSBJRGFsaWduIHdpdGggMCBvciAxIG11dGF0aW9ucyBmb3IgQ29tcGxlbWVudGF0aW9uIChmaXREMDVEMDMgYW5kIGZpdEQxMkQwNCkKTGliMTUuMTYubXV0LjAuMS4yLnVuaXF1ZS5yZXN1bHQgPC0gbXV0SURpbmZvLjE1LjE2LnVuaXF1ZS5zdWJzZXQgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyAlaW4lIGMoMCwgMSwgMikpICU+JQogIGRpc3RpbmN0KElEYWxpZ24pICU+JQogIG5yb3coKQoKcHJpbnQoTGliMTUuMTYubXV0LjAuMS4yLnVuaXF1ZS5yZXN1bHQpCgojIFN1bSBvZiBib3RoIENvZG9uIHZlcnNpb25zIGZvciAwIG9yIDEgbXV0YXRpb25zCkxpYjE1LjE2Lm11dC4wLjEuMi5zaGFyZWQudW5pcXVlLnJlc3VsdCA8LSBMaWIxNS4xNi5tdXQuMC4xLjIuc2hhcmVkLnJlc3VsdCArIExpYjE1LjE2Lm11dC4wLjEuMi51bmlxdWUucmVzdWx0CgpwcmludChMaWIxNS4xNi5tdXQuMC4xLjIuc2hhcmVkLnVuaXF1ZS5yZXN1bHQpCmBgYAoKTXV0YXRpb25zID0gMCsxKzIrMzogQ291bnQgdGhlIG51bWJlciBvZiB1bmlxdWUgSURhbGlnbiBpbiB0aGUgc2hhcmVkIGFuZCB1bmlxdWUgZGF0YXNldHM6CmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIFNoYXJlZDogVW5pcXVlIElEYWxpZ24gd2l0aCAwIG9yIDEgbXV0YXRpb25zIGZvciBDb21wbGVtZW50YXRpb24gKGZpdEQwNUQwMyBhbmQgZml0RDEyRDA0KQpMaWIxNS4xNi5tdXQuMC4xLjIuMy5zaGFyZWQucmVzdWx0IDwtIG11dElEaW5mbzE1LjE2LnNoYXJlZC5zdWJzZXQgJT4lCiAgZmlsdGVyKG11dGF0aW9ucy4xNSAlaW4lIGMoMCwgMSwgMiwgMykgfCBtdXRhdGlvbnMuMTYgJWluJSBjKDAsIDEsIDIsIDMpKSAlPiUKICBkaXN0aW5jdChJRGFsaWduLjE1LCBJRGFsaWduLjE2KSAlPiUKICBucm93KCkKCnByaW50KExpYjE1LjE2Lm11dC4wLjEuMi4zLnNoYXJlZC5yZXN1bHQpCgojIFVuaXF1ZTogVW5pcXVlIElEYWxpZ24gd2l0aCAwIG9yIDEgbXV0YXRpb25zIGZvciBDb21wbGVtZW50YXRpb24gKGZpdEQwNUQwMyBhbmQgZml0RDEyRDA0KQpMaWIxNS4xNi5tdXQuMC4xLjIuMy51bmlxdWUucmVzdWx0IDwtIG11dElEaW5mby4xNS4xNi51bmlxdWUuc3Vic2V0ICU+JQogIGZpbHRlcihtdXRhdGlvbnMgJWluJSBjKDAsIDEsIDIsIDMpKSAlPiUKICBkaXN0aW5jdChJRGFsaWduKSAlPiUKICBucm93KCkKCnByaW50KExpYjE1LjE2Lm11dC4wLjEuMi4zLnVuaXF1ZS5yZXN1bHQpCgojIFN1bSBvZiBib3RoIENvZG9uIHZlcnNpb25zIGZvciAwIG9yIDEgbXV0YXRpb25zCkxpYjE1LjE2Lm11dC4wLjEuMi4zLnNoYXJlZC51bmlxdWUucmVzdWx0IDwtIExpYjE1LjE2Lm11dC4wLjEuMi4zLnNoYXJlZC5yZXN1bHQgKyBMaWIxNS4xNi5tdXQuMC4xLjIuMy51bmlxdWUucmVzdWx0CgpwcmludChMaWIxNS4xNi5tdXQuMC4xLjIuMy5zaGFyZWQudW5pcXVlLnJlc3VsdCkKYGBgCgpNdXRhdGlvbnMgPSAwKzErMiszKzQ6IENvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIElEYWxpZ24gaW4gdGhlIHNoYXJlZCBhbmQgdW5pcXVlIGRhdGFzZXRzOgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBTaGFyZWQ6IFVuaXF1ZSBJRGFsaWduIHdpdGggMCBvciAxIG11dGF0aW9ucyBmb3IgQ29tcGxlbWVudGF0aW9uIChmaXREMDVEMDMgYW5kIGZpdEQxMkQwNCkKTGliMTUuMTYubXV0LjAuMS4yLjMuNC5zaGFyZWQucmVzdWx0IDwtIG11dElEaW5mbzE1LjE2LnNoYXJlZC5zdWJzZXQgJT4lCiAgZmlsdGVyKG11dGF0aW9ucy4xNSAlaW4lIGMoMCwgMSwgMiwgMywgNCkgfCBtdXRhdGlvbnMuMTYgJWluJSBjKDAsIDEsIDIsIDMsIDQpKSAlPiUKICBkaXN0aW5jdChJRGFsaWduLjE1LCBJRGFsaWduLjE2KSAlPiUKICBucm93KCkKCnByaW50KExpYjE1LjE2Lm11dC4wLjEuMi4zLjQuc2hhcmVkLnJlc3VsdCkKCiMgVW5pcXVlOiBVbmlxdWUgSURhbGlnbiB3aXRoIDAgb3IgMSBtdXRhdGlvbnMgZm9yIENvbXBsZW1lbnRhdGlvbiAoZml0RDA1RDAzIGFuZCBmaXREMTJEMDQpCkxpYjE1LjE2Lm11dC4wLjEuMi4zLjQudW5pcXVlLnJlc3VsdCA8LSBtdXRJRGluZm8uMTUuMTYudW5pcXVlLnN1YnNldCAlPiUKICBmaWx0ZXIobXV0YXRpb25zICVpbiUgYygwLCAxLCAyLCAzLCA0KSkgJT4lCiAgZGlzdGluY3QoSURhbGlnbikgJT4lCiAgbnJvdygpCgpwcmludChMaWIxNS4xNi5tdXQuMC4xLjIuMy40LnVuaXF1ZS5yZXN1bHQpCgojIFN1bSBvZiBib3RoIENvZG9uIHZlcnNpb25zIGZvciAwIG9yIDEgbXV0YXRpb25zCkxpYjE1LjE2Lm11dC4wLjEuMi4zLjQuc2hhcmVkLnVuaXF1ZS5yZXN1bHQgPC0gTGliMTUuMTYubXV0LjAuMS4yLjMuNC5zaGFyZWQucmVzdWx0ICsgTGliMTUuMTYubXV0LjAuMS4yLjMuNC51bmlxdWUucmVzdWx0CgpwcmludChMaWIxNS4xNi5tdXQuMC4xLjIuMy40LnNoYXJlZC51bmlxdWUucmVzdWx0KQpgYGAKCk11dGF0aW9ucyA9IDArMSsyKzMrNCs1OiBDb3VudCB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBJRGFsaWduIGluIHRoZSBzaGFyZWQgYW5kIHVuaXF1ZSBkYXRhc2V0czoKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgU2hhcmVkOiBVbmlxdWUgSURhbGlnbiB3aXRoIDAgb3IgMSBtdXRhdGlvbnMgZm9yIENvbXBsZW1lbnRhdGlvbiAoZml0RDA1RDAzIGFuZCBmaXREMTJEMDQpCkxpYjE1LjE2Lm11dC4wLjEuMi4zLjQuNS5zaGFyZWQucmVzdWx0IDwtIG11dElEaW5mbzE1LjE2LnNoYXJlZC5zdWJzZXQgJT4lCiAgZmlsdGVyKG11dGF0aW9ucy4xNSAlaW4lIGMoMCwgMSwgMiwgMywgNCwgNSkgfCBtdXRhdGlvbnMuMTYgJWluJSBjKDAsIDEsIDIsIDMsIDQsIDUpKSAlPiUKICBkaXN0aW5jdChJRGFsaWduLjE1LCBJRGFsaWduLjE2KSAlPiUKICBucm93KCkKCnByaW50KExpYjE1LjE2Lm11dC4wLjEuMi4zLjQuNS5zaGFyZWQucmVzdWx0KQoKIyBVbmlxdWU6IFVuaXF1ZSBJRGFsaWduIHdpdGggMCBvciAxIG11dGF0aW9ucyBmb3IgQ29tcGxlbWVudGF0aW9uIChmaXREMDVEMDMgYW5kIGZpdEQxMkQwNCkKTGliMTUuMTYubXV0LjAuMS4yLjMuNC41LnVuaXF1ZS5yZXN1bHQgPC0gbXV0SURpbmZvLjE1LjE2LnVuaXF1ZS5zdWJzZXQgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyAlaW4lIGMoMCwgMSwgMiwgMywgNCwgNSkpICU+JQogIGRpc3RpbmN0KElEYWxpZ24pICU+JQogIG5yb3coKQoKcHJpbnQoTGliMTUuMTYubXV0LjAuMS4yLjMuNC41LnVuaXF1ZS5yZXN1bHQpCgojIFN1bSBvZiBib3RoIENvZG9uIHZlcnNpb25zIGZvciAwIG9yIDEgbXV0YXRpb25zCkxpYjE1LjE2Lm11dC4wLjEuMi4zLjQuNS5zaGFyZWQudW5pcXVlLnJlc3VsdCA8LSBMaWIxNS4xNi5tdXQuMC4xLjIuMy40LjUuc2hhcmVkLnJlc3VsdCArIExpYjE1LjE2Lm11dC4wLjEuMi4zLjQuNS51bmlxdWUucmVzdWx0CgpwcmludChMaWIxNS4xNi5tdXQuMC4xLjIuMy40LjUuc2hhcmVkLnVuaXF1ZS5yZXN1bHQpCmBgYAoKIyMjIyBQbG90IENvZG9uIE11dGFudHMKCk9yZ2FuaXplIHRoZSBjb2RvbiBob21vbG9nICsgbXV0YW50IGNvdW50cyBpbnRvIGEgbmV3IGRhdGFmcmFtZToKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CkxpYjE1LjE2LmhvbW9sb2cubXV0YW50cyA8LSB0aWJibGUoCiAgTXV0YXRpb25zID0gYygwLCAxLCAyLCAzLCA0LCA1KSwKICBDb2RvbjEgPSBjKDQxNywgNjQ0LCA2NjUsIDY3OSwgNjg1LCA2ODgpLAogIENvZG9uMiA9IGMoMzc3LCA1NjgsIDU5NywgNjAyLCA2MDQsIDYwNSksCiAgQm90aCA9IGMoNjAwLCAxMDUzLCAxMDgyLCAxMDkzLCAxMDk4LCAxMDk5KSkKCiMgVmlldyB0aGUgZGF0YSBmcmFtZQpwcmludChMaWIxNS4xNi5ob21vbG9nLm11dGFudHMpCmBgYAoKYGBge3J9CiMgUmVzaGFwZSB0aGUgZGF0YSBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQKTGliMTUuMTYuaG9tb2xvZy5tdXRhbnRzX2xvbmcgPC0gTGliMTUuMTYuaG9tb2xvZy5tdXRhbnRzICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gYyhDb2RvbjEsIENvZG9uMiwgQm90aCksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJDYXRlZ29yeSIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiQXNzZW1ibGllcyIpCgojIFNldCB0aGUgbWF4aW11bSB2YWx1ZSBmb3Igc2NhbGluZyB0byAxMjA4Cm1heF9hc3NlbWJsaWVzIDwtIDEyMDgKCiMgQ3JlYXRlIHRoZSBwbG90IHdpdGggc21vb3RoZWQgbGluZXMgYW5kIHNlY29uZCB5LWF4aXMKTGliMTUuMTYuaG9tb2xvZy5tdXRhbnRzX3Bsb3QgPC0gZ2dwbG90KExpYjE1LjE2LmhvbW9sb2cubXV0YW50c19sb25nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBNdXRhdGlvbnMsIHkgPSBBc3NlbWJsaWVzLCBjb2xvciA9IENhdGVnb3J5KSkgKwogIGdlb21fbGluZShzaXplID0gMSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDcpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiQ29kb24xIiA9ICIjMDA3MkIyIiwgIkNvZG9uMiIgPSAiI0U2OUYwMCIsICJCb3RoIiA9ICJsaWdodGJsdWU0IiksCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoIkNvZG9uMSIsICJDb2RvbjIiLCAiQm90aCIpKSArICAjIFRoaXMgbGluZSBzZXRzIHRoZSBsZWdlbmQgb3JkZXIKICBzY2FsZV95X2NvbnRpbnVvdXMoCiAgICBuYW1lID0gIkhvbW9sb2dzIFJlcHJlc2VudGVkIFxuKDEyMDggQXNzZW1ibGVkKSIsIAogICAgbGltaXRzID0gYygwLCBtYXhfYXNzZW1ibGllcyksCiAgICBleHBhbmQgPSBjKDAsIDApLAogICAgc2VjLmF4aXMgPSBzZWNfYXhpcyh+IC4gKiAxMDAgLyBtYXhfYXNzZW1ibGllcywgCiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiTGlicmFyeSBSZXByZXNlbnRhdGlvbiAoJSkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAyMCkpCiAgKSArCiAgbGFicyh4ID0gIk1heC4gRGlzdGFuY2UgZnJvbSBIb21vbG9nIChhLmEuKSIsCiAgICAgICBjb2xvciA9ICJDYXRlZ29yeSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKAogICAgYXhpcy50aXRsZS55LnJpZ2h0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyNCwgY29sb3IgPSAicmVkIiksCiAgICBheGlzLnRleHQueS5yaWdodCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjEsIGNvbG9yID0gInJlZCIpLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMSksCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjEpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyNCksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjEpLAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApLAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmxhY2siLCBzaXplID0gMS4wKSwKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmxhY2siLCBzaXplID0gMS4wKSwKICAgIGF4aXMudGlja3MubGVuZ3RoID0gdW5pdCgwLjIsICJjbSIpCiAgKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAsIG1heF9hc3NlbWJsaWVzICogMS4wNSkpICAjIEFkZCA1JSBwYWRkaW5nIHRvIHRoZSB0b3AKCiMgRGlzcGxheSB0aGUgcGxvdApwcmludChMaWIxNS4xNi5ob21vbG9nLm11dGFudHNfcGxvdCkKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iTXV0YW50cy9QTE9UUy9ESEZSLkxpYjE1LjE2LmhvbW9sb2dzLm11dGFudHMuY29tcGxlbWVudC5saW5lY2hhcnQucG5nIiwgCiAgICAgICBwbG90PUxpYjE1LjE2LmhvbW9sb2cubXV0YW50c19wbG90LAogICAgICAgZHBpPTYwMCwgd2lkdGggPSA3LCBoZWlnaHQgPSA3LCB1bml0cyA9ICJpbiIpCmBgYAoKIyMgTXV0YW50cyBPbmx5CgpGaXJzdCwgY3JlYXRlIGEgYG11dGFudGAgb2JqZWN0IHRoYXQgZmlsdGVycyB0aGUgYG11dElEaW5mb2Agb2JqZWN0IHRvIHJldGFpbiBtdXRJRHMgb25seSBpZiB0aGV5IGhhdmUgPiAwIG11dGF0aW9uczoKYGBge3J9CiMgTGliMTUKbXV0YW50czE1IDwtIG11dElEaW5mbzE1ICU+JQogIGZpbHRlcihtdXRhdGlvbnMhPTApICU+JQogIGRwbHlyOjpyZW5hbWUoSUQgPSBJRGFsaWduKQoKIyBMaWIxNgptdXRhbnRzMTYgPC0gbXV0SURpbmZvMTYgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyE9MCkgJT4lCiAgZHBseXI6OnJlbmFtZShJRCA9IElEYWxpZ24pCmBgYAoKQWRkIHRoZSBhY3R1YWwgcGN0X2lkZW50IHRvIEUuIGNvbGkgc2NvcmUgZnJvbSB0aGUgYG9yZ2luZm9gIG9iamVjdApgYGB7cn0KIyBMaWIxNQptdXRhbnRzMTUgPC0gbWVyZ2UobXV0YW50czE1LCBvcmdpbmZvWywgYygiSUQiLCAiUGN0SWRlbnRFY29saSIpXSwgYnkgPSAiSUQiLCBhbGwueCA9IFRSVUUpCgojIExpYjE2Cm11dGFudHMxNiA8LSBtZXJnZShtdXRhbnRzMTYsIG9yZ2luZm9bLCBjKCJJRCIsICJQY3RJZGVudEVjb2xpIildLCBieSA9ICJJRCIsIGFsbC54ID0gVFJVRSkKYGBgCgoqKkNvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIG11dElEczoqKgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBMaWIxNQptdXRhbnRzMTUuY291bnQgPC0gbGVuZ3RoKHVuaXF1ZShtdXRhbnRzMTUkbXV0SUQpKQpmb3JtYXQobXV0YW50czE1LmNvdW50LCBiaWcubWFyayA9ICIsIikKCiMgTGliMTYKbXV0YW50czE2LmNvdW50IDwtIGxlbmd0aCh1bmlxdWUobXV0YW50czE2JG11dElEKSkKZm9ybWF0KG11dGFudHMxNi5jb3VudCwgYmlnLm1hcmsgPSAiLCIpCmBgYAoKKipOb3csIGNvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIElEYWxpZ25zIHRoYXQgZWFjaCBtdXRJRCBpcyBhc3NvY2lhdGVkIHdpdGg6KioKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgTGliMTUKbXV0YW50czE1LklELmNvdW50IDwtIGxlbmd0aCh1bmlxdWUobXV0YW50czE1JElEKSkKZm9ybWF0KG11dGFudHMxNS5JRC5jb3VudCwgYmlnLm1hcmsgPSAiLCIpCgojIExpYjE2Cm11dGFudHMxNi5JRC5jb3VudCA8LSBsZW5ndGgodW5pcXVlKG11dGFudHMxNiRJRCkpCmZvcm1hdChtdXRhbnRzMTYuSUQuY291bnQsIGJpZy5tYXJrID0gIiwiKQpgYGAKCkJpbiBtdXRhbnRzIGJ5IHRoZSBwZXJjZW50IHNpbWlsYXJpdHkgdG8gdGhlaXIgZGVzaWduZWQgaG9tb2xvZ3M6CmBgYHtyfQojTGliMTUKbXV0YW50czE1JGlkZW50YmlucyA8LSBjdXQobXV0YW50czE1JHBjdF9pZGVudCwKICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgwLDEuMDA1LDEvMTAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1hcy5jaGFyYWN0ZXIoc2VxKDAuMDA1LDAuOTk1LDEvMTAwKSkpCgojTGliMTYKbXV0YW50czE2JGlkZW50YmlucyA8LSBjdXQobXV0YW50czE2JHBjdF9pZGVudCwKICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgwLDEuMDA1LDEvMTAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1hcy5jaGFyYWN0ZXIoc2VxKDAuMDA1LDAuOTk1LDEvMTAwKSkpCmBgYAoKRGV0ZXJtaW5lIHRoZSAqKm1pbmltdW0qKiBwZXJjZW50IHNpbWlsYXJpdHkgbXV0YW50IGluIHRoZSBkYXRhc2V0IChtb3N0IGRpZmZlcmVudCBmcm9tIGRlc2lnbmVkIGhvbW9sb2cpOgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KI0xpYjE1Cm1pbihtdXRhbnRzMTUkcGN0X2lkZW50KQoKI0xpYjE2Cm1pbihtdXRhbnRzMTYkcGN0X2lkZW50KQpgYGAKCkRldGVybWluZSB0aGUgKiptYXhpbXVtKiogcGVyY2VudCBzaW1pbGFyaXR5IG11dGFudCBpbiB0aGUgZGF0YXNldCAobW9zdCBzaW1pbGFyIHRvIGRlc2lnbmVkIGhvbW9sb2cpOgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KI0xpYjE1Cm1heChtdXRhbnRzMTUkcGN0X2lkZW50KQoKI0xpYjE2Cm1heChtdXRhbnRzMTYkcGN0X2lkZW50KQpgYGAKCkNhbGN1bGF0ZSB0aGUgdG90YWwgbnVtYmVyIG9mIG11dGFudHMgd2l0aCBhdCBsZWFzdCAxIGJhcmNvZGUgcmVjb3ZlcmVkOgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KI0xpYjE1Cm11dDE1LjFCQ3MuY291bnQgPC0gbnJvdyhtdXRhbnRzMTUgJT4lIGZpbHRlcihudW1wcnVuZWRCQ3MgPj0gMSkpCmZvcm1hdChtdXQxNS4xQkNzLmNvdW50LCBiaWcubWFyayA9ICIsIikKCiNMaWIxNgptdXQxNi4xQkNzLmNvdW50IDwtIG5yb3cobXV0YW50czE2ICU+JSBmaWx0ZXIobnVtcHJ1bmVkQkNzID49IDEpKQpmb3JtYXQobXV0MTYuMUJDcy5jb3VudCwgYmlnLm1hcmsgPSAiLCIpCmBgYAoKQ2FsY3VsYXRlIHRoZSB0b3RhbCBudW1iZXIgb2YgbXV0YW50cyB3aXRoIGF0IGxlYXN0IDUgYmFyY29kZSByZWNvdmVyZWQ6CmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojTGliMTUKbXV0MTUuNUJDcy5jb3VudCA8LSBucm93KG11dGFudHMxNSAlPiUgZmlsdGVyKG51bXBydW5lZEJDcyA+PSA1KSkKZm9ybWF0KG11dDE1LjVCQ3MuY291bnQsIGJpZy5tYXJrID0gIiwiKQoKI0xpYjE2Cm11dDE2LjVCQ3MuY291bnQgPC0gbnJvdyhtdXRhbnRzMTYgJT4lIGZpbHRlcihudW1wcnVuZWRCQ3MgPj0gNSkpCmZvcm1hdChtdXQxNi41QkNzLmNvdW50LCBiaWcubWFyayA9ICIsIikKYGBgCgojIyMgSGlzdG9ncmFtIFBsb3RzCgpQbG90IHRoZXNlIG11dGFudHMgKD4xIEJDcyByZWNvdmVyZWQpIHdpdGggaGlzdG9ncmFtczoKYGBge3Igd2FybmluZz1GQUxTRX0KI0xpYjE1Cm11dGFudF9oaXN0MTVfcGxvdCA8LSBtdXRhbnRzMTUgJT4lCiAgZmlsdGVyKG51bXBydW5lZEJDcyA+PSAxKSAlPiUKICBnZ3Bsb3QoYWVzKHg9cGN0X2lkZW50LCB5PWZpdEQwNUQwMykpICsKICBsYWJzKHggPSAiRnJhY3Rpb25hbCBTZXF1ZW5jZSBJZGVudGl0eSIsIHkgPSJGaXRuZXNzIixjb2xvcj0iIikgKwogIGdlb21fcG9pbnQoYWxwaGE9MC4zLGNvbG9yPScjMDA3MkIyJykgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICdibGFjaycsIHNpemUgPSAwLjUpLCAKICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgptdXRhbnRfaGlzdDE1X3Bsb3QyIDwtIGdnTWFyZ2luYWwobXV0YW50X2hpc3QxNV9wbG90LCB0eXBlID0gImhpc3RvZ3JhbSIsIGZpbGwgPSAiIzAwNzJCMiIsIGJpbnM9NDApCm11dGFudF9oaXN0MTVfcGxvdDIKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojTGliMTUKZ2dzYXZlKGZpbGU9Ik11dGFudHMvUExPVFMvTGliMTUubXV0YW50cy5wY3RzaW1ob21vLmZpdEQwNUQwMy5taW4xQkNzLnBuZyIsIAogICAgICAgcGxvdD1tdXRhbnRfaGlzdDE1X3Bsb3QyLAogICAgICAgZHBpPTYwMCwgd2lkdGggPSA4LCBoZWlnaHQgPSA2LCB1bml0cyA9ICJpbiIpCmBgYAoKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CiNMaWIxNgptdXRhbnRfaGlzdDE2X3Bsb3QgPC0gbXV0YW50czE2ICU+JQogIGZpbHRlcihudW1wcnVuZWRCQ3MgPj0gMSkgJT4lCiAgZ2dwbG90KGFlcyh4PXBjdF9pZGVudCwgeT1maXREMTJEMDQpKSArCiAgbGFicyh4ID0gIkZyYWN0aW9uYWwgU2VxdWVuY2UgSWRlbnRpdHkiLCB5ID0iRml0bmVzcyIsY29sb3I9IiIpICsKICBnZW9tX3BvaW50KGFscGhhPTAuMyxjb2xvcj0nI0U2OUYwMCcpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMC41KSwgCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKbXV0YW50X2hpc3QxNl9wbG90MiA8LSBnZ01hcmdpbmFsKG11dGFudF9oaXN0MTZfcGxvdCwgdHlwZSA9ICJoaXN0b2dyYW0iLCBmaWxsID0gIiNFNjlGMDAiLCBiaW5zPTQwKQptdXRhbnRfaGlzdDE2X3Bsb3QyCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KI0xpYjE2Cmdnc2F2ZShmaWxlPSJNdXRhbnRzL1BMT1RTL0xpYjE2Lm11dGFudHMucGN0c2ltaG9tby5maXREMDVEMDMubWluMUJDcy5wbmciLCAKICAgICAgIHBsb3Q9bXV0YW50X2hpc3QxNl9wbG90MiwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iKQpgYGAKCiMjIyBCb3hwbG90cwoKUGxvdCBib3hwbG90cyBiYXNlZCBvbiBtdXRhbnQgcGVyY2VudCBzaW1pbGFyaXR5IHRvIHRoZWlyIGRlc2lnbmVkIGhvbW9sb2dzIGZvciBmaXRuZXNzIGJldHdlZW4gTTkgKE5vIFN1cHApIHZzLiBNOSAoRnVsbCBTdXBwKQpgYGB7ciB3YXJuaW5nPUZBTFNFfQojTGliMTUKbXV0YW50X2JveDE1X3Bsb3QgPC0gbXV0YW50czE1ICU+JQogIGZpbHRlcihudW1wcnVuZWRCQ3MgPj0gMSkgJT4lCiAgZ2dwbG90KGFlcyh4PWFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGlkZW50YmlucykpKjEwMCx5PWZpdEQwNUQwMyxjb2xvcj1pZGVudGJpbnMpKSArCiAgbGFicyh4ID0gIlNlcXVlbmNlIElkZW50aXR5IHRvIEhvbW9sb2cgKCUpIiwgeSA9IkZpdG5lc3MiLGNvbG9yPSIiKSArCiAgZ2d0aXRsZSgiTGliMTU6IENvbXBsZW1lbnRhdGlvbiIpICsKICBnZW9tX2JveHBsb3QoKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2JsYWNrJywgc2l6ZSA9IDAuNSksIAogICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKC0xMCw1KSkKCm11dGFudF9ib3gxNV9wbG90CmBgYAoKYGBge3Igd2FybmluZz1GQUxTRX0KI0xpYjE2Cm11dGFudF9ib3gxNl9wbG90IDwtIG11dGFudHMxNiAlPiUKICBmaWx0ZXIobnVtcHJ1bmVkQkNzID49IDEpICU+JQogIGdncGxvdChhZXMoeD1hcy5udW1lcmljKGFzLmNoYXJhY3RlcihpZGVudGJpbnMpKSoxMDAseT1maXREMTJEMDQsY29sb3I9aWRlbnRiaW5zKSkgKwogIGxhYnMoeCA9ICJTZXF1ZW5jZSBJZGVudGl0eSB0byBIb21vbG9nICglKSIsIHkgPSJGaXRuZXNzIixjb2xvcj0iIikgKwogIGdndGl0bGUoIkxpYjE2OiBDb21wbGVtZW50YXRpb24iKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICdibGFjaycsIHNpemUgPSAwLjUpLCAKICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSwgbGltaXRzID0gYygtMTAsNSkpCgptdXRhbnRfYm94MTZfcGxvdApgYGAKCmBgYHtyfQpwYXRjaDMgPC0gKG11dGFudF9ib3gxNV9wbG90IHwgbXV0YW50X2JveDE2X3Bsb3QpCnBhdGNoMwpgYGAKCmBgYHtyIGVjaG89RkFMU0V9Cmdnc2F2ZShmaWxlPSJNdXRhbnRzL1BMT1RTL0xpYjE1LjE2Lm11dGFudHMuYnkuc2VxLmlkZW50Lm1pbjFCQy5Db21wbGVtZW50YXRpb24ucG5nIiwgCiAgICAgICBwbG90PXBhdGNoMywKICAgICAgIGRwaT02MDAsIHdpZHRoID0gMTgsIGhlaWdodCA9IDgsIHVuaXRzID0gImluIikKYGBgCgpQbG90IGJveHBsb3RzIGJhc2VkIG9uIG11dGFudCBwZXJjZW50IHNpbWlsYXJpdHkgdG8gdGhlaXIgZGVzaWduZWQgaG9tb2xvZ3MgZm9yIGZpdG5lc3MgYmV0d2VlbiA1MCB1Zy9tbCBUTVAKYGBge3J9CiNMaWIxNQptdXRhbnRfYm94MTVfNTB0bXAgPC0gbXV0YW50czE1ICU+JQogIGZpbHRlcihudW1wcnVuZWRCQ3MgPj0gMSkgJT4lCiAgZ2dwbG90KGFlcyh4PWFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGlkZW50YmlucykpKjEwMCx5PWZpdEQxMEQwMyxjb2xvcj1pZGVudGJpbnMpKSArCiAgbGFicyh4ID0gIlNlcXVlbmNlIElkZW50aXR5IHRvIEhvbW9sb2cgKCUpIiwgeSA9IkZpdG5lc3MiLGNvbG9yPSIiKSArCiAgZ2d0aXRsZSgiTGliMTU6IDUwIFRNUCIpICsKICBnZW9tX2JveHBsb3QoKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2JsYWNrJywgc2l6ZSA9IDAuNSksIAogICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKC0xNSwxMCkpCgptdXRhbnRfYm94MTVfNTB0bXAKYGBgCgpgYGB7cn0KI0xpYjE2Cm11dGFudF9ib3gxNl81MHRtcCA8LSBtdXRhbnRzMTYgJT4lCiAgZmlsdGVyKG51bXBydW5lZEJDcyA+PSAxKSAlPiUKICBnZ3Bsb3QoYWVzKHg9YXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoaWRlbnRiaW5zKSkqMTAwLHk9Zml0RTA1RDA0LGNvbG9yPWlkZW50YmlucykpICsKICBsYWJzKHggPSAiU2VxdWVuY2UgSWRlbnRpdHkgdG8gSG9tb2xvZyAoJSkiLCB5ID0iRml0bmVzcyIsY29sb3I9IiIpICsKICBnZ3RpdGxlKCJMaWIxNjogNTAgVE1QIikgKwogIGdlb21fYm94cGxvdCgpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMC41KSwgCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoLTE1LDEwKSkKCm11dGFudF9ib3gxNl81MHRtcApgYGAKCmBgYHtyfQpwYXRjaDQgPC0gKG11dGFudF9ib3gxNV81MHRtcCB8IG11dGFudF9ib3gxNl81MHRtcCkKcGF0Y2g0CmBgYAoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9Ik11dGFudHMvUExPVFMvTGliMTUuMTYubXV0YW50cy5ieS5zZXEuaWRlbnQubWluMUJDLjUwVE1QLnBuZyIsIAogICAgICAgcGxvdD1wYXRjaDQsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDE4LCBoZWlnaHQgPSAxMiwgdW5pdHMgPSAiaW4iKQpgYGAKClBsb3QgYm94cGxvdHMgYmFzZWQgb24gbXV0YW50IHBlcmNlbnQgc2ltaWxhcml0eSB0byB0aGVpciBkZXNpZ25lZCBob21vbG9ncyBmb3IgZml0bmVzcyBiZXR3ZWVuIDIwMCB1Zy9tbCBUTVAKYGBge3J9CiNMaWIxNQptdXRhbnRfYm94MTVfMjAwdG1wIDwtIG11dGFudHMxNSAlPiUKICBmaWx0ZXIobnVtcHJ1bmVkQkNzID49IDEpICU+JQogIGdncGxvdChhZXMoeD1hcy5udW1lcmljKGFzLmNoYXJhY3RlcihpZGVudGJpbnMpKSoxMDAseT1maXREMTFEMDMsY29sb3I9aWRlbnRiaW5zKSkgKwogIGxhYnMoeCA9ICJTZXF1ZW5jZSBJZGVudGl0eSB0byBIb21vbG9nICglKSIsIHkgPSJGaXRuZXNzIixjb2xvcj0iIikgKwogIGdndGl0bGUoIkxpYjE1OiAyMDAgVE1QIikgKwogIGdlb21fYm94cGxvdCgpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMC41KSwgCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoLTE1LDEwKSkKCm11dGFudF9ib3gxNV8yMDB0bXAKYGBgCgpgYGB7cn0KI0xpYjE2Cm11dGFudF9ib3gxNl8yMDB0bXAgPC0gbXV0YW50czE2ICU+JQogIGZpbHRlcihudW1wcnVuZWRCQ3MgPj0gMSkgJT4lCiAgZ2dwbG90KGFlcyh4PWFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGlkZW50YmlucykpKjEwMCx5PWZpdEUwNkQwNCxjb2xvcj1pZGVudGJpbnMpKSArCiAgbGFicyh4ID0gIlNlcXVlbmNlIElkZW50aXR5IHRvIEhvbW9sb2cgKCUpIiwgeSA9IkZpdG5lc3MiLGNvbG9yPSIiKSArCiAgZ2d0aXRsZSgiTGliMTY6IDIwMCBUTVAiKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICdibGFjaycsIHNpemUgPSAwLjUpLCAKICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSwgbGltaXRzID0gYygtMTUsMTApKQoKbXV0YW50X2JveDE2XzIwMHRtcApgYGAKCmBgYHtyfQpwYXRjaDUgPC0gKG11dGFudF9ib3gxNV8yMDB0bXAgfCBtdXRhbnRfYm94MTZfMjAwdG1wKQpwYXRjaDUKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iTXV0YW50cy9QTE9UUy9MaWIxNS4xNi5tdXRhbnRzLmJ5LnNlcS5pZGVudC5taW4xQkMuMjAwVE1QLnBuZyIsCiAgICAgICBwbG90PXBhdGNoNSwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gMTgsIGhlaWdodCA9IDgsIHVuaXRzID0gImluIikKYGBgCgpDb21iaW5lIHRoZW0gYWxsOgpgYGB7cn0KcGF0Y2g2IDwtIChtdXRhbnRfYm94MTVfcGxvdCB8IG11dGFudF9ib3gxNl9wbG90KSAvIChtdXRhbnRfYm94MTVfNTB0bXAgfCBtdXRhbnRfYm94MTZfNTB0bXApIC8gKG11dGFudF9ib3gxNV8yMDB0bXAgfCBtdXRhbnRfYm94MTZfMjAwdG1wKQpwYXRjaDYKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iTXV0YW50cy9QTE9UUy9MaWIxNS4xNi5tdXRhbnRzLmJ5LnNlcS5pZGVudC5taW4xQkMuQWxsLkNvbWJpbmVkLnBuZyIsCiAgICAgICBwbG90PXBhdGNoNiwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gMTIsIGhlaWdodCA9IDE0LCB1bml0cyA9ICJpbiIpCmBgYAoKIyMgQ29sbGFwc2UgTXV0YW50cwo8Zm9udCBjb2xvcj0iYmx1ZSI+KipUaGlzIHNlY3Rpb24gaXMgYmFzZWQgb24gdGhlIFIgZmlsZTogIlJfY29sbGFwc2VfbXV0YW50cy5SIi4qKjwvZm9udD4gVGhlIGZvbGxvd2luZyBjb2RlIGRlc2NyaWJlcyBob3cgdG8gY29sbGFwc2UgbXV0YW50IEJDcyBvbnRvIHRoZWlyIGRlc2lnbmVkIGhvbW9sb2dzIHdpdGhpbiBhIGRpc3RhbmNlIG9mIDUgYW1pbm8gYWNpZHMuCgojIyMgTGliMTUKCkJlZ2luIGJ5IHNlbGVjdGluZyBhbGwgbXV0YW50cyB3aXRoaW4gYSBkaXN0YW5jZSBvZiA1IEFBIGZyb20gdGhlaXIgZGVzaWduZWQgaG9tb2xvZ3MKYGBge3J9Cm11dF9jb2xsYXBzZV8xNSA8LSBtdXRhbnRzMTUgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyA+PSAwICYgbXV0YXRpb25zIDwgNikgJT4lCiAgZ3JvdXBfYnkoSUQpCmBgYAoKTWVyZ2UgdGhlc2UgbXV0YW50cyB3aXRoIHRoZWlyIGRlc2lnbmVkIGhvbW9sb2dzIChwZXJmZWN0cyA+NUJDcykgaWYgdGhleSBoYXZlIGEgbWF0Y2hpbmcgIklEIi4gVXNlIHRoZSBzaGFyZWQgcGVyZmVjdHMgbXV0SURzIGZvciBtZXJnaW5nIGFuZCBkb3duc3RyZWFtIGxpYnJhcnkgY29tcGFyaXNvbnM6CmBgYHtyfQojIEFkZCBwZXJmZWN0cyB0byBtdXRfY29sbGFwc2UgYnkgc2hhcmVkIGNvbHVtbnM6Cm11dF9jb2xsYXBzZV8xNSA8LSBmdWxsX2pvaW4obXV0X2NvbGxhcHNlXzE1LCBwZXJmZWN0czE1XzVCQ3MpCmBgYAoKT25seSByZXRhaW4gbXV0SURzIGlmIHRoZSAiSUQiIGNvbnRhaW5zIGEgZGVzaWduZWQgdmFyaWFudCAobXV0YXRpb25zID09IDApOgpgYGB7cn0KbXV0X2NvbGxhcHNlXzE1IDwtIG11dF9jb2xsYXBzZV8xNSAlPiUKICBncm91cF9ieShJRCkgJT4lCiAgZmlsdGVyKDAgJWluJSBtdXRhdGlvbnMpICU+JQogIHVuZ3JvdXAoKQpgYGAKClN1bW1hcml6ZSB0aGUgbnVtYmVyIG9mIHVuaXF1ZSAiSUQiIHdpdGggbXV0YXRpb25zPT0wIChwZXJmZWN0cykgYWZ0ZXIgZmlsdGVyaW5nOgpgYGB7cn0KbXV0X2NvbGxhcHNlXzE1LmNvdW50IDwtIG11dF9jb2xsYXBzZV8xNSAlPiUKICBmaWx0ZXIobXV0YXRpb25zID09IDApICU+JQogIHN1bW1hcmlzZSh1bmlxdWVfcm93cyA9IG5fZGlzdGluY3QoSUQpKQoKcHJpbnQobXV0X2NvbGxhcHNlXzE1LmNvdW50KQpgYGAKClN1bW1hcml6ZSB0aGUgbnVtYmVyIG9mIHVuaXF1ZSAiSUQiIGF0IGVhY2ggbXV0YXRpb24gbGV2ZWw6CmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQptdXRfY29sbGFwc2VfMTUuc3VtbWFyeSA8LSBtdXRfY29sbGFwc2VfMTUgJT4lCiAgZ3JvdXBfYnkobXV0YXRpb25zKSAlPiUKICBzdW1tYXJpc2UodW5pcXVlX0lEcyA9IG5fZGlzdGluY3QoSUQpKQoKIyBWaWV3IHRoZSBzdW1tYXJ5IHRhYmxlCnByaW50KG11dF9jb2xsYXBzZV8xNS5zdW1tYXJ5KQpgYGAKClN1bW1hcml6ZSB0aGUgbnVtYmVyIG9mIGNvbGxhcHNlZCBob21vbG9ncyBhZnRlciBmaWx0ZXJpbmcKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgQ291bnQgdGhlIG51bWJlciBvZiB1bmlxdWUgZGVzaWduZWQgaG9tb2xvZ3MgcmV0YWluZWQgaW4gdGhlIGZpbHRlcmVkIGRhdGFzZXQ6CmZvcm1hdChsZW5ndGgodW5pcXVlKG11dF9jb2xsYXBzZV8xNSRJRCkpLCBiaWcubWFyayA9ICIsIikKCiMgQ291bnQgdGhlIG51bWJlciBvZiB1bmlxdWUgIm11dElEIiBhZnRlciBleGNsdWRpbmcgcm93cyB3aXRoIG11dGF0aW9ucz09MCAocmV0YWlucyBtdXRhbnRzIG9ubHkpCmZvcm1hdChsZW5ndGgodW5pcXVlKG11dF9jb2xsYXBzZV8xNSRtdXRJRFttdXRfY29sbGFwc2VfMTUkbXV0YXRpb25zICE9IDBdKSksIGJpZy5tYXJrID0gIiwiKQoKIyBOb3csIGNvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlICJtdXRJRCIgKHRoaXMgaW5jbHVkZXMgZGVzaWduZWQgaG9tb2xvZ3MgYW5kIHRoZWlyIG11dGFudHMpCmZvcm1hdChsZW5ndGgodW5pcXVlKG11dF9jb2xsYXBzZV8xNSRtdXRJRCkpLCBiaWcubWFyayA9ICIsIikKYGBgCgojIyMjIE11dGF0aW9uIFNpbWlsYXJpdHkKCk5leHQsIHJ1biBjb3JyZWxhdGlvbiBhbmFseXNlcyBiZXR3ZWVuIGRlc2lnbmVkIGhvbW9sb2dzIGFuZCB0aGVpciBjb3JyZXNwb25kaW5nIG11dGFudCB2ZXJzaW9ucyAoMSwgMiwgMywgNCwgb3IgNSBtdXRhdGlvbnMpIHRvIGRldGVybWluZSBpZiBtdXRhdGlvbnMgc2hhcmUgc2ltaWxhciBmaXRuZXNzIHZhbHVlcyB3aXRoIGRlc2lnbmVkIHZhcmlhbnRzCgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBMaWIxNQoKIyBGaWx0ZXIgdGhlIGRhdGFmcmFtZSBmb3IgbXV0YXRpb25zIGF0IGVhY2ggbGV2ZWwgKDAsMSwyLDMsNCw1KQptdXRhdGlvbnMxNV8wIDwtIHN1YnNldChtdXRfY29sbGFwc2VfMTUsIG11dGF0aW9ucyA9PSAwKQptdXRhdGlvbnMxNV8xIDwtIHN1YnNldChtdXRfY29sbGFwc2VfMTUsIG11dGF0aW9ucyA9PSAxKQptdXRhdGlvbnMxNV8yIDwtIHN1YnNldChtdXRfY29sbGFwc2VfMTUsIG11dGF0aW9ucyA9PSAyKQptdXRhdGlvbnMxNV8zIDwtIHN1YnNldChtdXRfY29sbGFwc2VfMTUsIG11dGF0aW9ucyA9PSAzKQptdXRhdGlvbnMxNV80IDwtIHN1YnNldChtdXRfY29sbGFwc2VfMTUsIG11dGF0aW9ucyA9PSA0KQptdXRhdGlvbnMxNV81IDwtIHN1YnNldChtdXRfY29sbGFwc2VfMTUsIG11dGF0aW9ucyA9PSA1KQoKIyBNZXJnZSB0aGUgZGF0YWZyYW1lcyBiYXNlZCBvbiBzaGFyZWQgIklEIiBmb3IgZWFjaCBtdXRhdGlvbiBsZXZlbCBhZ2FpbnN0IHBlcmZlY3RzCk11dDB2c011dDFfMTUgPC0gbWVyZ2UobXV0YXRpb25zMTVfMCwgbXV0YXRpb25zMTVfMSwgYnkgPSAiSUQiLCBzdWZmaXhlcyA9IGMoIl9tdXRhdGlvbnNfMCIsICJfbXV0YXRpb25zXzEiKSkKTXV0MHZzTXV0Ml8xNSA8LSBtZXJnZShtdXRhdGlvbnMxNV8wLCBtdXRhdGlvbnMxNV8yLCBieSA9ICJJRCIsIHN1ZmZpeGVzID0gYygiX211dGF0aW9uc18wIiwgIl9tdXRhdGlvbnNfMiIpKQpNdXQwdnNNdXQzXzE1IDwtIG1lcmdlKG11dGF0aW9uczE1XzAsIG11dGF0aW9uczE1XzMsIGJ5ID0gIklEIiwgc3VmZml4ZXMgPSBjKCJfbXV0YXRpb25zXzAiLCAiX211dGF0aW9uc18zIikpCk11dDB2c011dDRfMTUgPC0gbWVyZ2UobXV0YXRpb25zMTVfMCwgbXV0YXRpb25zMTVfNCwgYnkgPSAiSUQiLCBzdWZmaXhlcyA9IGMoIl9tdXRhdGlvbnNfMCIsICJfbXV0YXRpb25zXzQiKSkKTXV0MHZzTXV0NV8xNSA8LSBtZXJnZShtdXRhdGlvbnMxNV8wLCBtdXRhdGlvbnMxNV81LCBieSA9ICJJRCIsIHN1ZmZpeGVzID0gYygiX211dGF0aW9uc18wIiwgIl9tdXRhdGlvbnNfNSIpKQoKIyBTdWJzZXQgcmVsZXZhbnQgZGF0YSBjb2x1bW5zIGFuZCByZW1vdmUgcm93cyBjb250YWluaW5nICJOQSIgdmFsdWVzCk11dDB2c011dDFfMTUgPC0gTXV0MHZzTXV0MV8xNVssIGMoIklEIiwgIm51bXBydW5lZEJDc19tdXRhdGlvbnNfMCIsICJudW1wcnVuZWRCQ3NfbXV0YXRpb25zXzEiLCAiZml0RDA1RDAzX211dGF0aW9uc18wIiwgImZpdEQwNUQwM19tdXRhdGlvbnNfMSIpXSAlPiUgbmEub21pdChNdXQwdnNNdXQxXzE1KQpNdXQwdnNNdXQyXzE1IDwtIE11dDB2c011dDJfMTVbLCBjKCJJRCIsICJudW1wcnVuZWRCQ3NfbXV0YXRpb25zXzAiLCAibnVtcHJ1bmVkQkNzX211dGF0aW9uc18yIiwgImZpdEQwNUQwM19tdXRhdGlvbnNfMCIsICJmaXREMDVEMDNfbXV0YXRpb25zXzIiKV0gJT4lIG5hLm9taXQoTXV0MHZzTXV0Ml8xNSkKTXV0MHZzTXV0M18xNSA8LSBNdXQwdnNNdXQzXzE1WywgYygiSUQiLCAibnVtcHJ1bmVkQkNzX211dGF0aW9uc18wIiwgIm51bXBydW5lZEJDc19tdXRhdGlvbnNfMyIsICJmaXREMDVEMDNfbXV0YXRpb25zXzAiLCAiZml0RDA1RDAzX211dGF0aW9uc18zIildICU+JSBuYS5vbWl0KE11dDB2c011dDNfMTUpCk11dDB2c011dDRfMTUgPC0gTXV0MHZzTXV0NF8xNVssIGMoIklEIiwgIm51bXBydW5lZEJDc19tdXRhdGlvbnNfMCIsICJudW1wcnVuZWRCQ3NfbXV0YXRpb25zXzQiLCAiZml0RDA1RDAzX211dGF0aW9uc18wIiwgImZpdEQwNUQwM19tdXRhdGlvbnNfNCIpXSAlPiUgbmEub21pdChNdXQwdnNNdXQ0XzE1KQpNdXQwdnNNdXQ1XzE1IDwtIE11dDB2c011dDVfMTVbLCBjKCJJRCIsICJudW1wcnVuZWRCQ3NfbXV0YXRpb25zXzAiLCAibnVtcHJ1bmVkQkNzX211dGF0aW9uc181IiwgImZpdEQwNUQwM19tdXRhdGlvbnNfMCIsICJmaXREMDVEMDNfbXV0YXRpb25zXzUiKV0gJT4lIG5hLm9taXQoTXV0MHZzTXV0NV8xNSkKCiMgQ2FsY3VsYXRlIGNvcnJlbGF0aW9uIGFuZCBwLXZhbHVlCmNvcl90ZXN0MTVfTXV0MHZzTXV0MSA8LSBjb3IudGVzdChNdXQwdnNNdXQxXzE1JGZpdEQwNUQwM19tdXRhdGlvbnNfMCwgTXV0MHZzTXV0MV8xNSRmaXREMDVEMDNfbXV0YXRpb25zXzEpCmNvcl90ZXN0MTVfTXV0MHZzTXV0MiA8LSBjb3IudGVzdChNdXQwdnNNdXQyXzE1JGZpdEQwNUQwM19tdXRhdGlvbnNfMCwgTXV0MHZzTXV0Ml8xNSRmaXREMDVEMDNfbXV0YXRpb25zXzIpCmNvcl90ZXN0MTVfTXV0MHZzTXV0MyA8LSBjb3IudGVzdChNdXQwdnNNdXQzXzE1JGZpdEQwNUQwM19tdXRhdGlvbnNfMCwgTXV0MHZzTXV0M18xNSRmaXREMDVEMDNfbXV0YXRpb25zXzMpCmNvcl90ZXN0MTVfTXV0MHZzTXV0NCA8LSBjb3IudGVzdChNdXQwdnNNdXQ0XzE1JGZpdEQwNUQwM19tdXRhdGlvbnNfMCwgTXV0MHZzTXV0NF8xNSRmaXREMDVEMDNfbXV0YXRpb25zXzQpCmNvcl90ZXN0MTVfTXV0MHZzTXV0NSA8LSBjb3IudGVzdChNdXQwdnNNdXQ1XzE1JGZpdEQwNUQwM19tdXRhdGlvbnNfMCwgTXV0MHZzTXV0NV8xNSRmaXREMDVEMDNfbXV0YXRpb25zXzUpCgpjb3JfdGVzdDE1X011dDB2c011dDEKY29yX3Rlc3QxNV9NdXQwdnNNdXQyCmNvcl90ZXN0MTVfTXV0MHZzTXV0Mwpjb3JfdGVzdDE1X011dDB2c011dDQKY29yX3Rlc3QxNV9NdXQwdnNNdXQ1CmBgYAoKIyMjIyBQbG90IENvcnJlbGF0aW9ucwpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBFeHRyYWN0IGNvcnJlbGF0aW9uIHZhbHVlIGZyb20gY29yX3Jlc3VsdDE1X011dDB2c011dDEgb2JqZWN0CmNvcl92YWx1ZV9NdXQwdnNNdXQxIDwtIGNvcl90ZXN0MTVfTXV0MHZzTXV0MSRlc3RpbWF0ZQpjb3JfdmFsdWVfTXV0MHZzTXV0MiA8LSBjb3JfdGVzdDE1X011dDB2c011dDIkZXN0aW1hdGUKY29yX3ZhbHVlX011dDB2c011dDMgPC0gY29yX3Rlc3QxNV9NdXQwdnNNdXQzJGVzdGltYXRlCmNvcl92YWx1ZV9NdXQwdnNNdXQ0IDwtIGNvcl90ZXN0MTVfTXV0MHZzTXV0NCRlc3RpbWF0ZQpjb3JfdmFsdWVfTXV0MHZzTXV0NSA8LSBjb3JfdGVzdDE1X011dDB2c011dDUkZXN0aW1hdGUKCgojIEZvcm1hdCBwLXZhbHVlIGluIHNjaWVudGlmaWMgbm90YXRpb24KcF92YWx1ZV9zY2llbnRpZmljMTVfdjEgPC0gZm9ybWF0KGNvcl90ZXN0MTVfTXV0MHZzTXV0MSRwLnZhbHVlLCBzY2llbnRpZmljID0gVFJVRSwgZGlnaXRzID0gNCkKcF92YWx1ZV9zY2llbnRpZmljMTVfdjIgPC0gZm9ybWF0KGNvcl90ZXN0MTVfTXV0MHZzTXV0MiRwLnZhbHVlLCBzY2llbnRpZmljID0gVFJVRSwgZGlnaXRzID0gNCkKcF92YWx1ZV9zY2llbnRpZmljMTVfdjMgPC0gZm9ybWF0KGNvcl90ZXN0MTVfTXV0MHZzTXV0MyRwLnZhbHVlLCBzY2llbnRpZmljID0gVFJVRSwgZGlnaXRzID0gNCkKcF92YWx1ZV9zY2llbnRpZmljMTVfdjQgPC0gZm9ybWF0KGNvcl90ZXN0MTVfTXV0MHZzTXV0NCRwLnZhbHVlLCBzY2llbnRpZmljID0gVFJVRSwgZGlnaXRzID0gNCkKcF92YWx1ZV9zY2llbnRpZmljMTVfdjUgPC0gZm9ybWF0KGNvcl90ZXN0MTVfTXV0MHZzTXV0NSRwLnZhbHVlLCBzY2llbnRpZmljID0gVFJVRSwgZGlnaXRzID0gNCkKCiMgRXh0cmFjdCBudW1iZXIgb2Ygcm93cwpudW1fcm93czE1Lm11dDB2MSA8LSBucm93KE11dDB2c011dDFfMTUpCm51bV9yb3dzMTUubXV0MHYyIDwtIG5yb3coTXV0MHZzTXV0Ml8xNSkKbnVtX3Jvd3MxNS5tdXQwdjMgPC0gbnJvdyhNdXQwdnNNdXQzXzE1KQpudW1fcm93czE1Lm11dDB2NCA8LSBucm93KE11dDB2c011dDRfMTUpCm51bV9yb3dzMTUubXV0MHY1IDwtIG5yb3coTXV0MHZzTXV0NV8xNSkKCiMgUGxvdCB0aGUgY29ycmVsYXRpb24gKE11dDB2c011dDEpCm11dDB2MV8xNXBsb3QgPC0gZ2dwbG90KE11dDB2c011dDFfMTUsIAogICAgICAgICAgICAgYWVzKHggPSBmaXREMDVEMDNfbXV0YXRpb25zXzAsIHkgPSBmaXREMDVEMDNfbXV0YXRpb25zXzEsIGNvbG9yPW51bXBydW5lZEJDc19tdXRhdGlvbnNfMCkpICsKICBsYWJzKHggPSAiZml0RDA1RDAzIChtdXRhdGlvbnMgPSAwKSIsCiAgICAgICB5ID0gImZpdEQwNUQwMyAobXV0YXRpb25zID0gMSkiLCBjb2xvcj0iIiwKICAgICAgIHRpdGxlID0gIkxpYjE1IENvbXBsZW1lbnRhdGlvbiAoTXV0PTAgdnMgTXV0PTEpIikgKwogIGdlb21fc21vb3RoKG1ldGhvZD1sbSxjb2xvdXI9ImJsYWNrIikgKwogIGdlb21fZGVuc2l0eTJkKGNvbG91cj0iYmxhY2siLGFscGhhPTAuMikgKwogIGdlb21fcG9pbnQoYWxwaGE9MC43KSArCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50MigiQkNzIixsb3c9ImJsdWUiLCBoaWdoPSJyZWQiLCBtaWQ9ImJsdWUiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2JsYWNrJywgc2l6ZSA9IDAuNSksIAogICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbj0ibGVmdCIpICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSBtYXgoTXV0MHZzTXV0MV8xNSRmaXREMDVEMDNfbXV0YXRpb25zXzApLCB5ID0gbWluKE11dDB2c011dDFfMTUkZml0RDA1RDAzX211dGF0aW9uc18xKSwgCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgicC12YWx1ZSA9IiwgcF92YWx1ZV9zY2llbnRpZmljMTVfdjEpLCBoanVzdCA9IDEsIHZqdXN0ID0gMCkgKwogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IG1heChNdXQwdnNNdXQxXzE1JGZpdEQwNUQwM19tdXRhdGlvbnNfMCksIHkgPSBtaW4oTXV0MHZzTXV0MV8xNSRmaXREMDVEMDNfbXV0YXRpb25zXzEpLAogICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJDb3JyZWxhdGlvbiA9Iiwgcm91bmQoY29yX3ZhbHVlX011dDB2c011dDEsIDIpKSwgaGp1c3QgPSAxLCB2anVzdCA9IC0xLjUpICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSBtaW4oTXV0MHZzTXV0MV8xNSRmaXREMDVEMDNfbXV0YXRpb25zXzApLCB5ID0gbWF4KE11dDB2c011dDFfMTUkZml0RDA1RDAzX211dGF0aW9uc18xKSwKICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJNdXRhbnRzID0iLCBudW1fcm93czE1Lm11dDB2MSksIGhqdXN0ID0gMCwgdmp1c3QgPSAxLjUpCgptdXQwdjFfMTVwbG90MiA8LSBnZ01hcmdpbmFsKG11dDB2MV8xNXBsb3QsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbCA9ICIjMDA3MkIyIiwgYWxwaGE9MC43NSkgI2FkZCBzaWRlIGhpc3RvZ3JhbXMKbXV0MHYxXzE1cGxvdDIKCiMgUGxvdCB0aGUgY29ycmVsYXRpb24gKE11dDB2c011dDIpCm11dDB2Ml8xNXBsb3QgPC0gZ2dwbG90KE11dDB2c011dDJfMTUsIAogICAgICAgICAgICAgYWVzKHggPSBmaXREMDVEMDNfbXV0YXRpb25zXzAsIHkgPSBmaXREMDVEMDNfbXV0YXRpb25zXzIsIGNvbG9yPW51bXBydW5lZEJDc19tdXRhdGlvbnNfMCkpICsKICBsYWJzKHggPSAiZml0RDA1RDAzIChtdXRhdGlvbnMgPSAwKSIsCiAgICAgICB5ID0gImZpdEQwNUQwMyAobXV0YXRpb25zID0gMikiLCBjb2xvcj0iIiwKICAgICAgIHRpdGxlID0gIkxpYjE1IENvbXBsZW1lbnRhdGlvbiAoTXV0PTAgdnMgTXV0PTIpIikgKwogIGdlb21fc21vb3RoKG1ldGhvZD1sbSxjb2xvdXI9ImJsYWNrIikgKwogIGdlb21fZGVuc2l0eTJkKGNvbG91cj0iYmxhY2siLGFscGhhPTAuMikgKwogIGdlb21fcG9pbnQoYWxwaGE9MC43KSArCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50MigiQkNzIixsb3c9ImJsdWUiLCBoaWdoPSJyZWQiLCBtaWQ9ImJsdWUiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2JsYWNrJywgc2l6ZSA9IDAuNSksIAogICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbj0ibGVmdCIpICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSBtYXgoTXV0MHZzTXV0Ml8xNSRmaXREMDVEMDNfbXV0YXRpb25zXzApLCB5ID0gbWluKE11dDB2c011dDJfMTUkZml0RDA1RDAzX211dGF0aW9uc18yKSwgCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgicC12YWx1ZSA9IiwgcF92YWx1ZV9zY2llbnRpZmljMTVfdjIpLCBoanVzdCA9IDEsIHZqdXN0ID0gMCkrCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gbWF4KE11dDB2c011dDJfMTUkZml0RDA1RDAzX211dGF0aW9uc18wKSwgeSA9IG1pbihNdXQwdnNNdXQyXzE1JGZpdEQwNUQwM19tdXRhdGlvbnNfMiksCiAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIkNvcnJlbGF0aW9uID0iLCByb3VuZChjb3JfdmFsdWVfTXV0MHZzTXV0MiwgMikpLCBoanVzdCA9IDEsIHZqdXN0ID0gLTEuNSkgKwogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IG1pbihNdXQwdnNNdXQyXzE1JGZpdEQwNUQwM19tdXRhdGlvbnNfMCksIHkgPSBtYXgoTXV0MHZzTXV0Ml8xNSRmaXREMDVEMDNfbXV0YXRpb25zXzIpLAogICAgICAgICAgIGxhYmVsID0gcGFzdGUoIk11dGFudHMgPSIsIG51bV9yb3dzMTUubXV0MHYyKSwgaGp1c3QgPSAwLCB2anVzdCA9IDEuNSkKCm11dDB2Ml8xNXBsb3QyIDwtIGdnTWFyZ2luYWwobXV0MHYyXzE1cGxvdCwgdHlwZSA9ICJoaXN0b2dyYW0iLCBmaWxsID0gIiMwMDcyQjIiLCBhbHBoYT0wLjc1KSAjYWRkIHNpZGUgaGlzdG9ncmFtcwptdXQwdjJfMTVwbG90MgoKIyBQbG90IHRoZSBjb3JyZWxhdGlvbiAoTXV0MHZzTXV0MykKbXV0MHYzXzE1cGxvdCA8LSBnZ3Bsb3QoTXV0MHZzTXV0M18xNSwgCiAgICAgICAgICAgICBhZXMoeCA9IGZpdEQwNUQwM19tdXRhdGlvbnNfMCwgeSA9IGZpdEQwNUQwM19tdXRhdGlvbnNfMywgY29sb3I9bnVtcHJ1bmVkQkNzX211dGF0aW9uc18wKSkgKwogIGxhYnMoeCA9ICJmaXREMDVEMDMgKG11dGF0aW9ucyA9IDApIiwKICAgICAgIHkgPSAiZml0RDA1RDAzIChtdXRhdGlvbnMgPSAzKSIsIGNvbG9yPSIiLAogICAgICAgdGl0bGUgPSAiTGliMTUgQ29tcGxlbWVudGF0aW9uIChNdXQ9MCB2cyBNdXQ9MykiKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtLGNvbG91cj0iYmxhY2siKSArCiAgZ2VvbV9kZW5zaXR5MmQoY29sb3VyPSJibGFjayIsYWxwaGE9MC4yKSArCiAgZ2VvbV9wb2ludChhbHBoYT0wLjcpICsKICBzY2FsZV9jb2xvdXJfZ3JhZGllbnQyKCJCQ3MiLGxvdz0iYmx1ZSIsIGhpZ2g9InJlZCIsIG1pZD0iYmx1ZSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMC41KSwgCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSJsZWZ0IikgKwogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IG1heChNdXQwdnNNdXQzXzE1JGZpdEQwNUQwM19tdXRhdGlvbnNfMCksIHkgPSBtaW4oTXV0MHZzTXV0M18xNSRmaXREMDVEMDNfbXV0YXRpb25zXzMpLCAKICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJwLXZhbHVlID0iLCBwX3ZhbHVlX3NjaWVudGlmaWMxNV92MyksIGhqdXN0ID0gMSwgdmp1c3QgPSAwKSsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSBtYXgoTXV0MHZzTXV0M18xNSRmaXREMDVEMDNfbXV0YXRpb25zXzApLCB5ID0gbWluKE11dDB2c011dDNfMTUkZml0RDA1RDAzX211dGF0aW9uc18zKSwKICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiQ29ycmVsYXRpb24gPSIsIHJvdW5kKGNvcl92YWx1ZV9NdXQwdnNNdXQzLCAyKSksIGhqdXN0ID0gMSwgdmp1c3QgPSAtMS41KSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gbWluKE11dDB2c011dDNfMTUkZml0RDA1RDAzX211dGF0aW9uc18wKSwgeSA9IG1heChNdXQwdnNNdXQzXzE1JGZpdEQwNUQwM19tdXRhdGlvbnNfMyksCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiTXV0YW50cyA9IiwgbnVtX3Jvd3MxNS5tdXQwdjMpLCBoanVzdCA9IDAsIHZqdXN0ID0gMS41KQoKbXV0MHYzXzE1cGxvdDIgPC0gZ2dNYXJnaW5hbChtdXQwdjNfMTVwbG90LCB0eXBlID0gImhpc3RvZ3JhbSIsIGZpbGwgPSAiIzAwNzJCMiIsIGFscGhhPTAuNzUpICNhZGQgc2lkZSBoaXN0b2dyYW1zCm11dDB2M18xNXBsb3QyCgojIFBsb3QgdGhlIGNvcnJlbGF0aW9uIChNdXQwdnNNdXQ0KQptdXQwdjRfMTVwbG90IDwtIGdncGxvdChNdXQwdnNNdXQ0XzE1LCAKICAgICAgICAgICAgIGFlcyh4ID0gZml0RDA1RDAzX211dGF0aW9uc18wLCB5ID0gZml0RDA1RDAzX211dGF0aW9uc180LCBjb2xvcj1udW1wcnVuZWRCQ3NfbXV0YXRpb25zXzApKSArCiAgbGFicyh4ID0gImZpdEQwNUQwMyAobXV0YXRpb25zID0gMCkiLAogICAgICAgeSA9ICJmaXREMDVEMDMgKG11dGF0aW9ucyA9IDQpIiwgY29sb3I9IiIsCiAgICAgICB0aXRsZSA9ICJMaWIxNSBDb21wbGVtZW50YXRpb24gKE11dD0wIHZzIE11dD00KSIpICsKICBnZW9tX3Ntb290aChtZXRob2Q9bG0sY29sb3VyPSJibGFjayIpICsKICBnZW9tX2RlbnNpdHkyZChjb2xvdXI9ImJsYWNrIixhbHBoYT0wLjIpICsKICBnZW9tX3BvaW50KGFscGhhPTAuNykgKwogIHNjYWxlX2NvbG91cl9ncmFkaWVudDIoIkJDcyIsbG93PSJibHVlIiwgaGlnaD0icmVkIiwgbWlkPSJibHVlIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICdibGFjaycsIHNpemUgPSAwLjUpLCAKICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb249ImxlZnQiKSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gbWF4KE11dDB2c011dDRfMTUkZml0RDA1RDAzX211dGF0aW9uc18wKSwgeSA9IG1pbihNdXQwdnNNdXQ0XzE1JGZpdEQwNUQwM19tdXRhdGlvbnNfNCksIAogICAgICAgICAgIGxhYmVsID0gcGFzdGUoInAtdmFsdWUgPSIsIHBfdmFsdWVfc2NpZW50aWZpYzE1X3Y0KSwgaGp1c3QgPSAxLCB2anVzdCA9IDApKwogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IG1heChNdXQwdnNNdXQ0XzE1JGZpdEQwNUQwM19tdXRhdGlvbnNfMCksIHkgPSBtaW4oTXV0MHZzTXV0NF8xNSRmaXREMDVEMDNfbXV0YXRpb25zXzQpLAogICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJDb3JyZWxhdGlvbiA9Iiwgcm91bmQoY29yX3ZhbHVlX011dDB2c011dDQsIDIpKSwgaGp1c3QgPSAxLCB2anVzdCA9IC0xLjUpICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSBtaW4oTXV0MHZzTXV0NF8xNSRmaXREMDVEMDNfbXV0YXRpb25zXzApLCB5ID0gbWF4KE11dDB2c011dDRfMTUkZml0RDA1RDAzX211dGF0aW9uc180KSwKICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJNdXRhbnRzID0iLCBudW1fcm93czE1Lm11dDB2NCksIGhqdXN0ID0gMCwgdmp1c3QgPSAxLjUpCgptdXQwdjRfMTVwbG90MiA8LSBnZ01hcmdpbmFsKG11dDB2NF8xNXBsb3QsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbCA9ICIjMDA3MkIyIiwgYWxwaGE9MC43NSkgI2FkZCBzaWRlIGhpc3RvZ3JhbXMKbXV0MHY0XzE1cGxvdDIKCiMgUGxvdCB0aGUgY29ycmVsYXRpb24gKE11dDB2c011dDUpCm11dDB2NV8xNXBsb3QgPC0gZ2dwbG90KE11dDB2c011dDVfMTUsIAogICAgICAgICAgICAgYWVzKHggPSBmaXREMDVEMDNfbXV0YXRpb25zXzAsIHkgPSBmaXREMDVEMDNfbXV0YXRpb25zXzUsIGNvbG9yPW51bXBydW5lZEJDc19tdXRhdGlvbnNfMCkpICsKICBsYWJzKHggPSAiZml0RDA1RDAzIChtdXRhdGlvbnMgPSAwKSIsCiAgICAgICB5ID0gImZpdEQwNUQwMyAobXV0YXRpb25zID0gNSkiLCBjb2xvcj0iIiwKICAgICAgIHRpdGxlID0gIkxpYjE1IENvbXBsZW1lbnRhdGlvbiAoTXV0PTAgdnMgTXV0PTUpIikgKwogIGdlb21fc21vb3RoKG1ldGhvZD1sbSxjb2xvdXI9ImJsYWNrIikgKwogIGdlb21fZGVuc2l0eTJkKGNvbG91cj0iYmxhY2siLGFscGhhPTAuMikgKwogIGdlb21fcG9pbnQoYWxwaGE9MC43KSArCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50MigiQkNzIixsb3c9ImJsdWUiLCBoaWdoPSJyZWQiLCBtaWQ9ImJsdWUiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2JsYWNrJywgc2l6ZSA9IDAuNSksIAogICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbj0ibGVmdCIpICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSBtYXgoTXV0MHZzTXV0NV8xNSRmaXREMDVEMDNfbXV0YXRpb25zXzApLCB5ID0gbWluKE11dDB2c011dDVfMTUkZml0RDA1RDAzX211dGF0aW9uc181KSwgCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgicC12YWx1ZSA9IiwgcF92YWx1ZV9zY2llbnRpZmljMTVfdjUpLCBoanVzdCA9IDEsIHZqdXN0ID0gMCkrCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gbWF4KE11dDB2c011dDVfMTUkZml0RDA1RDAzX211dGF0aW9uc18wKSwgeSA9IG1pbihNdXQwdnNNdXQ1XzE1JGZpdEQwNUQwM19tdXRhdGlvbnNfNSksCiAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIkNvcnJlbGF0aW9uID0iLCByb3VuZChjb3JfdmFsdWVfTXV0MHZzTXV0NSwgMikpLCBoanVzdCA9IDEsIHZqdXN0ID0gLTEuNSkgKwogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IG1pbihNdXQwdnNNdXQ1XzE1JGZpdEQwNUQwM19tdXRhdGlvbnNfMCksIHkgPSBtYXgoTXV0MHZzTXV0NV8xNSRmaXREMDVEMDNfbXV0YXRpb25zXzUpLAogICAgICAgICAgIGxhYmVsID0gcGFzdGUoIk11dGFudHMgPSIsIG51bV9yb3dzMTUubXV0MHY1KSwgaGp1c3QgPSAwLCB2anVzdCA9IDEuNSkKCm11dDB2NV8xNXBsb3QyIDwtIGdnTWFyZ2luYWwobXV0MHY1XzE1cGxvdCwgdHlwZSA9ICJoaXN0b2dyYW0iLCBmaWxsID0gIiMwMDcyQjIiLCBhbHBoYT0wLjc1KSAjYWRkIHNpZGUgaGlzdG9ncmFtcwptdXQwdjVfMTVwbG90MgpgYGAKCmBgYHtyIGVjaG89RkFMU0V9Cmdnc2F2ZShmaWxlPSJNdXRhbnRzL1BMT1RTL0NvcnJlbGF0aW9ucy9MMTUubXV0YW50LmNvcnJlbGF0aW9uLm11dDAudnMubXV0MS5jb21wbGVtZW50YXRpb24ucG5nIiwKICAgICAgIHBsb3Q9bXV0MHYxXzE1cGxvdDIsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYsIHVuaXRzID0gImluIikKCmdnc2F2ZShmaWxlPSJNdXRhbnRzL1BMT1RTL0NvcnJlbGF0aW9ucy9MMTUubXV0YW50LmNvcnJlbGF0aW9uLm11dDAudnMubXV0Mi5jb21wbGVtZW50YXRpb24ucG5nIiwKICAgICAgIHBsb3Q9bXV0MHYyXzE1cGxvdDIsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYsIHVuaXRzID0gImluIikKCmdnc2F2ZShmaWxlPSJNdXRhbnRzL1BMT1RTL0NvcnJlbGF0aW9ucy9MMTUubXV0YW50LmNvcnJlbGF0aW9uLm11dDAudnMubXV0My5jb21wbGVtZW50YXRpb24ucG5nIiwKICAgICAgIHBsb3Q9bXV0MHYzXzE1cGxvdDIsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYsIHVuaXRzID0gImluIikKCmdnc2F2ZShmaWxlPSJNdXRhbnRzL1BMT1RTL0NvcnJlbGF0aW9ucy9MMTUubXV0YW50LmNvcnJlbGF0aW9uLm11dDAudnMubXV0NC5jb21wbGVtZW50YXRpb24ucG5nIiwKICAgICAgIHBsb3Q9bXV0MHY0XzE1cGxvdDIsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYsIHVuaXRzID0gImluIikKCmdnc2F2ZShmaWxlPSJNdXRhbnRzL1BMT1RTL0NvcnJlbGF0aW9ucy9MMTUubXV0YW50LmNvcnJlbGF0aW9uLm11dDAudnMubXV0NS5jb21wbGVtZW50YXRpb24ucG5nIiwKICAgICAgIHBsb3Q9bXV0MHY1XzE1cGxvdDIsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYsIHVuaXRzID0gImluIikKYGBgCgojIyMgTGliMTYKCkJlZ2luIGJ5IHNlbGVjdGluZyBhbGwgbXV0YW50cyB3aXRoaW4gYSBkaXN0YW5jZSBvZiA1IEFBIGZyb20gdGhlaXIgZGVzaWduZWQgaG9tb2xvZ3MKYGBge3J9Cm11dF9jb2xsYXBzZV8xNiA8LSBtdXRhbnRzMTYgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyA+PSAwICYgbXV0YXRpb25zIDwgNikgJT4lCiAgZ3JvdXBfYnkoSUQpCmBgYAoKTWVyZ2UgdGhlc2UgbXV0YW50cyB3aXRoIHRoZWlyIGRlc2lnbmVkIGhvbW9sb2dzIChwZXJmZWN0cykgaWYgdGhleSBoYXZlIGEgbWF0Y2hpbmcgIklEIgpgYGB7cn0KIyBBZGQgcGVyZmVjdHMgdG8gbXV0X2NvbGxhcHNlIGJ5IHNoYXJlZCBjb2x1bW5zOgptdXRfY29sbGFwc2VfMTYgPC0gZnVsbF9qb2luKG11dF9jb2xsYXBzZV8xNiwgcGVyZmVjdHMxNl81QkNzKQpgYGAKCk9ubHkgcmV0YWluIG11dElEcyBpZiB0aGUgIklEIiBjb250YWlucyBhIGRlc2lnbmVkIHZhcmlhbnQgKG11dGF0aW9ucyA9PSAwKToKYGBge3J9Cm11dF9jb2xsYXBzZV8xNiA8LSBtdXRfY29sbGFwc2VfMTYgJT4lCiAgZ3JvdXBfYnkoSUQpICU+JQogIGZpbHRlcigwICVpbiUgbXV0YXRpb25zKSAlPiUKICB1bmdyb3VwKCkKYGBgCgpTdW1tYXJpemUgdGhlIG51bWJlciBvZiB1bmlxdWUgIklEIiB3aXRoIG11dGF0aW9ucz09MCAocGVyZmVjdHMpIGFmdGVyIGZpbHRlcmluZzoKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9Cm11dF9jb2xsYXBzZV8xNi5jb3VudCA8LSBtdXRfY29sbGFwc2VfMTYgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyA9PSAwKSAlPiUKICBzdW1tYXJpc2UodW5pcXVlX3Jvd3MgPSBuX2Rpc3RpbmN0KElEKSkKCnByaW50KG11dF9jb2xsYXBzZV8xNi5jb3VudCkKYGBgCgpTdW1tYXJpemUgdGhlIG51bWJlciBvZiB1bmlxdWUgIklEIiBhdCBlYWNoIG11dGF0aW9uIGxldmVsOgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KbXV0X2NvbGxhcHNlXzE2LnN1bW1hcnkgPC0gbXV0X2NvbGxhcHNlXzE2ICU+JQogIGdyb3VwX2J5KG11dGF0aW9ucykgJT4lCiAgc3VtbWFyaXNlKHVuaXF1ZV9JRHMgPSBuX2Rpc3RpbmN0KElEKSkKCiMgVmlldyB0aGUgc3VtbWFyeSB0YWJsZQpwcmludChtdXRfY29sbGFwc2VfMTYuc3VtbWFyeSkKYGBgCgpTdW1tYXJpemUgdGhlIG51bWJlciBvZiBjb2xsYXBzZWQgaG9tb2xvZ3MgYWZ0ZXIgZmlsdGVyaW5nCmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIENvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIGRlc2lnbmVkIGhvbW9sb2dzIHJldGFpbmVkIGluIHRoZSBmaWx0ZXJlZCBkYXRhc2V0Ogpmb3JtYXQobGVuZ3RoKHVuaXF1ZShtdXRfY29sbGFwc2VfMTYkSUQpKSwgYmlnLm1hcmsgPSAiLCIpCgojIENvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlICJtdXRJRCIgYWZ0ZXIgZXhjbHVkaW5nIHJvd3Mgd2l0aCBtdXRhdGlvbnM9PTAgKHJldGFpbnMgbXV0YW50cyBvbmx5KQpmb3JtYXQobGVuZ3RoKHVuaXF1ZShtdXRfY29sbGFwc2VfMTYkbXV0SURbbXV0X2NvbGxhcHNlXzE1JG11dGF0aW9ucyAhPSAwXSkpLCBiaWcubWFyayA9ICIsIikKCiMgTm93LCBjb3VudCB0aGUgbnVtYmVyIG9mIHVuaXF1ZSAibXV0SUQiICh0aGlzIGluY2x1ZGVzIGRlc2lnbmVkIGhvbW9sb2dzIGFuZCB0aGVpciBtdXRhbnRzKQpmb3JtYXQobGVuZ3RoKHVuaXF1ZShtdXRfY29sbGFwc2VfMTYkbXV0SUQpKSwgYmlnLm1hcmsgPSAiLCIpCmBgYAoKIyMjIyBNdXRhdGlvbiBTaW1pbGFyaXR5CgpOZXh0LCBydW4gY29ycmVsYXRpb24gYW5hbHlzZXMgYmV0d2VlbiBkZXNpZ25lZCBob21vbG9ncyBhbmQgdGhlaXIgY29ycmVzcG9uZGluZyBtdXRhbnQgdmVyc2lvbnMgKDEsIDIsIG9yIDMgbXV0YXRpb25zKSB0byBkZXRlcm1pbmUgaWYgbXV0YXRpb25zIHNoYXJlIHNpbWlsYXIgZml0bmVzcyB2YWx1ZXMgd2l0aCBkZXNpZ25lZCB2YXJpYW50cwpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBMaWIxNgoKIyBGaWx0ZXIgdGhlIGRhdGFmcmFtZSBmb3IgcGVyZmVjdHMgPSAwIGFuZCBzdWJzZXF1ZW50IG11dGF0aW9ucyA9IDEsMiwzLDQsNQptdXRhdGlvbnMxNl8wIDwtIHN1YnNldChtdXRfY29sbGFwc2VfMTYsIG11dGF0aW9ucyA9PSAwKQptdXRhdGlvbnMxNl8xIDwtIHN1YnNldChtdXRfY29sbGFwc2VfMTYsIG11dGF0aW9ucyA9PSAxKQptdXRhdGlvbnMxNl8yIDwtIHN1YnNldChtdXRfY29sbGFwc2VfMTYsIG11dGF0aW9ucyA9PSAyKQptdXRhdGlvbnMxNl8zIDwtIHN1YnNldChtdXRfY29sbGFwc2VfMTYsIG11dGF0aW9ucyA9PSAzKQptdXRhdGlvbnMxNl80IDwtIHN1YnNldChtdXRfY29sbGFwc2VfMTYsIG11dGF0aW9ucyA9PSA0KQptdXRhdGlvbnMxNl81IDwtIHN1YnNldChtdXRfY29sbGFwc2VfMTYsIG11dGF0aW9ucyA9PSA1KQoKIyBNZXJnZSB0aGUgZGF0YWZyYW1lcyBiYXNlZCBvbiBzaGFyZWQgIklEIgpNdXQwdnNNdXQxXzE2IDwtIG1lcmdlKG11dGF0aW9uczE2XzAsIG11dGF0aW9uczE2XzEsIGJ5ID0gIklEIiwgc3VmZml4ZXMgPSBjKCJfbXV0YXRpb25zXzAiLCAiX211dGF0aW9uc18xIikpCk11dDB2c011dDJfMTYgPC0gbWVyZ2UobXV0YXRpb25zMTZfMCwgbXV0YXRpb25zMTZfMiwgYnkgPSAiSUQiLCBzdWZmaXhlcyA9IGMoIl9tdXRhdGlvbnNfMCIsICJfbXV0YXRpb25zXzIiKSkKTXV0MHZzTXV0M18xNiA8LSBtZXJnZShtdXRhdGlvbnMxNl8wLCBtdXRhdGlvbnMxNl8zLCBieSA9ICJJRCIsIHN1ZmZpeGVzID0gYygiX211dGF0aW9uc18wIiwgIl9tdXRhdGlvbnNfMyIpKQpNdXQwdnNNdXQ0XzE2IDwtIG1lcmdlKG11dGF0aW9uczE2XzAsIG11dGF0aW9uczE2XzQsIGJ5ID0gIklEIiwgc3VmZml4ZXMgPSBjKCJfbXV0YXRpb25zXzAiLCAiX211dGF0aW9uc180IikpCk11dDB2c011dDVfMTYgPC0gbWVyZ2UobXV0YXRpb25zMTZfMCwgbXV0YXRpb25zMTZfNSwgYnkgPSAiSUQiLCBzdWZmaXhlcyA9IGMoIl9tdXRhdGlvbnNfMCIsICJfbXV0YXRpb25zXzUiKSkKCiMgU3Vic2V0IHJlbGV2YW50IGRhdGEgY29sdW1ucyBhbmQgcmVtb3ZlIHJvd3MgY29udGFpbmluZyAiTkEiIHZhbHVlcwpNdXQwdnNNdXQxXzE2IDwtIE11dDB2c011dDFfMTZbLCBjKCJJRCIsICJudW1wcnVuZWRCQ3NfbXV0YXRpb25zXzAiLCAibnVtcHJ1bmVkQkNzX211dGF0aW9uc18xIiwgImZpdEQxMkQwNF9tdXRhdGlvbnNfMCIsICJmaXREMTJEMDRfbXV0YXRpb25zXzEiKV0gJT4lIG5hLm9taXQoTXV0MHZzTXV0MV8xNikKTXV0MHZzTXV0Ml8xNiA8LSBNdXQwdnNNdXQyXzE2WywgYygiSUQiLCAibnVtcHJ1bmVkQkNzX211dGF0aW9uc18wIiwgIm51bXBydW5lZEJDc19tdXRhdGlvbnNfMiIsICJmaXREMTJEMDRfbXV0YXRpb25zXzAiLCAiZml0RDEyRDA0X211dGF0aW9uc18yIildICU+JSBuYS5vbWl0KE11dDB2c011dDJfMTYpCk11dDB2c011dDNfMTYgPC0gTXV0MHZzTXV0M18xNlssIGMoIklEIiwgIm51bXBydW5lZEJDc19tdXRhdGlvbnNfMCIsICJudW1wcnVuZWRCQ3NfbXV0YXRpb25zXzMiLCAiZml0RDEyRDA0X211dGF0aW9uc18wIiwgImZpdEQxMkQwNF9tdXRhdGlvbnNfMyIpXSAlPiUgbmEub21pdChNdXQwdnNNdXQzXzE2KQpNdXQwdnNNdXQ0XzE2IDwtIE11dDB2c011dDRfMTZbLCBjKCJJRCIsICJudW1wcnVuZWRCQ3NfbXV0YXRpb25zXzAiLCAibnVtcHJ1bmVkQkNzX211dGF0aW9uc180IiwgImZpdEQxMkQwNF9tdXRhdGlvbnNfMCIsICJmaXREMTJEMDRfbXV0YXRpb25zXzQiKV0gJT4lIG5hLm9taXQoTXV0MHZzTXV0NF8xNikKTXV0MHZzTXV0NV8xNiA8LSBNdXQwdnNNdXQ1XzE2WywgYygiSUQiLCAibnVtcHJ1bmVkQkNzX211dGF0aW9uc18wIiwgIm51bXBydW5lZEJDc19tdXRhdGlvbnNfNSIsICJmaXREMTJEMDRfbXV0YXRpb25zXzAiLCAiZml0RDEyRDA0X211dGF0aW9uc181IildICU+JSBuYS5vbWl0KE11dDB2c011dDVfMTYpCgoKIyBDYWxjdWxhdGUgY29ycmVsYXRpb24gYW5kIHAtdmFsdWUKY29yX3Rlc3QxNl9NdXQwdnNNdXQxIDwtIGNvci50ZXN0KE11dDB2c011dDFfMTYkZml0RDEyRDA0X211dGF0aW9uc18wLCBNdXQwdnNNdXQxXzE2JGZpdEQxMkQwNF9tdXRhdGlvbnNfMSkKY29yX3Rlc3QxNl9NdXQwdnNNdXQyIDwtIGNvci50ZXN0KE11dDB2c011dDJfMTYkZml0RDEyRDA0X211dGF0aW9uc18wLCBNdXQwdnNNdXQyXzE2JGZpdEQxMkQwNF9tdXRhdGlvbnNfMikKY29yX3Rlc3QxNl9NdXQwdnNNdXQzIDwtIGNvci50ZXN0KE11dDB2c011dDNfMTYkZml0RDEyRDA0X211dGF0aW9uc18wLCBNdXQwdnNNdXQzXzE2JGZpdEQxMkQwNF9tdXRhdGlvbnNfMykKY29yX3Rlc3QxNl9NdXQwdnNNdXQ0IDwtIGNvci50ZXN0KE11dDB2c011dDRfMTYkZml0RDEyRDA0X211dGF0aW9uc18wLCBNdXQwdnNNdXQ0XzE2JGZpdEQxMkQwNF9tdXRhdGlvbnNfNCkKY29yX3Rlc3QxNl9NdXQwdnNNdXQ1IDwtIGNvci50ZXN0KE11dDB2c011dDVfMTYkZml0RDEyRDA0X211dGF0aW9uc18wLCBNdXQwdnNNdXQ1XzE2JGZpdEQxMkQwNF9tdXRhdGlvbnNfNSkKCmNvcl90ZXN0MTZfTXV0MHZzTXV0MQpjb3JfdGVzdDE2X011dDB2c011dDIKY29yX3Rlc3QxNl9NdXQwdnNNdXQzCmNvcl90ZXN0MTZfTXV0MHZzTXV0NApjb3JfdGVzdDE2X011dDB2c011dDUKYGBgCgojIyMjIFBsb3QgQ29ycmVsYXRpb25zCmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIEV4dHJhY3QgY29ycmVsYXRpb24gdmFsdWUgZnJvbSBjb3JfcmVzdWx0MTZfTXV0MHZzTXV0MSBvYmplY3QKY29yX3ZhbHVlX011dDB2c011dDEgPC0gY29yX3Rlc3QxNl9NdXQwdnNNdXQxJGVzdGltYXRlCmNvcl92YWx1ZV9NdXQwdnNNdXQyIDwtIGNvcl90ZXN0MTZfTXV0MHZzTXV0MiRlc3RpbWF0ZQpjb3JfdmFsdWVfTXV0MHZzTXV0MyA8LSBjb3JfdGVzdDE2X011dDB2c011dDMkZXN0aW1hdGUKY29yX3ZhbHVlX011dDB2c011dDQgPC0gY29yX3Rlc3QxNl9NdXQwdnNNdXQ0JGVzdGltYXRlCmNvcl92YWx1ZV9NdXQwdnNNdXQ1IDwtIGNvcl90ZXN0MTZfTXV0MHZzTXV0NSRlc3RpbWF0ZQoKCiMgRm9ybWF0IHAtdmFsdWUgaW4gc2NpZW50aWZpYyBub3RhdGlvbgpwX3ZhbHVlX3NjaWVudGlmaWMxNl92MSA8LSBmb3JtYXQoY29yX3Rlc3QxNl9NdXQwdnNNdXQxJHAudmFsdWUsIHNjaWVudGlmaWMgPSBUUlVFLCBkaWdpdHMgPSA0KQpwX3ZhbHVlX3NjaWVudGlmaWMxNl92MiA8LSBmb3JtYXQoY29yX3Rlc3QxNl9NdXQwdnNNdXQyJHAudmFsdWUsIHNjaWVudGlmaWMgPSBUUlVFLCBkaWdpdHMgPSA0KQpwX3ZhbHVlX3NjaWVudGlmaWMxNl92MyA8LSBmb3JtYXQoY29yX3Rlc3QxNl9NdXQwdnNNdXQzJHAudmFsdWUsIHNjaWVudGlmaWMgPSBUUlVFLCBkaWdpdHMgPSA0KQpwX3ZhbHVlX3NjaWVudGlmaWMxNl92NCA8LSBmb3JtYXQoY29yX3Rlc3QxNl9NdXQwdnNNdXQ0JHAudmFsdWUsIHNjaWVudGlmaWMgPSBUUlVFLCBkaWdpdHMgPSA0KQpwX3ZhbHVlX3NjaWVudGlmaWMxNl92NSA8LSBmb3JtYXQoY29yX3Rlc3QxNl9NdXQwdnNNdXQ1JHAudmFsdWUsIHNjaWVudGlmaWMgPSBUUlVFLCBkaWdpdHMgPSA0KQoKIyBFeHRyYWN0IG51bWJlciBvZiByb3dzCm51bV9yb3dzMTYubXV0MHYxIDwtIG5yb3coTXV0MHZzTXV0MV8xNikKbnVtX3Jvd3MxNi5tdXQwdjIgPC0gbnJvdyhNdXQwdnNNdXQyXzE2KQpudW1fcm93czE2Lm11dDB2MyA8LSBucm93KE11dDB2c011dDNfMTYpCm51bV9yb3dzMTYubXV0MHY0IDwtIG5yb3coTXV0MHZzTXV0NF8xNikKbnVtX3Jvd3MxNi5tdXQwdjUgPC0gbnJvdyhNdXQwdnNNdXQ1XzE2KQoKIyBQbG90IHRoZSBjb3JyZWxhdGlvbiAoTXV0MHZzTXV0MSkKbXV0MHYxXzE2cGxvdCA8LSBnZ3Bsb3QoTXV0MHZzTXV0MV8xNiwgCiAgICAgICAgICAgICBhZXMoeCA9IGZpdEQxMkQwNF9tdXRhdGlvbnNfMCwgeSA9IGZpdEQxMkQwNF9tdXRhdGlvbnNfMSwgY29sb3I9bnVtcHJ1bmVkQkNzX211dGF0aW9uc18wKSkgKwogIGxhYnMoeCA9ICJmaXREMTJEMDQgKG11dGF0aW9ucyA9IDApIiwKICAgICAgIHkgPSAiZml0RDEyRDA0IChtdXRhdGlvbnMgPSAxKSIsIGNvbG9yPSIiLAogICAgICAgdGl0bGUgPSAiTGliMTYgQ29tcGxlbWVudGF0aW9uIChNdXQ9MCB2cyBNdXQ9MSkiKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtLGNvbG91cj0iYmxhY2siKSArCiAgZ2VvbV9kZW5zaXR5MmQoY29sb3VyPSJibGFjayIsYWxwaGE9MC4yKSArCiAgZ2VvbV9wb2ludChhbHBoYT0wLjcpICsKICBzY2FsZV9jb2xvdXJfZ3JhZGllbnQyKCJCQ3MiLGxvdz0iYmx1ZSIsIGhpZ2g9InJlZCIsIG1pZD0iYmx1ZSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMC41KSwgCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSJsZWZ0IikgKwogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IG1heChNdXQwdnNNdXQxXzE2JGZpdEQxMkQwNF9tdXRhdGlvbnNfMCksIHkgPSBtaW4oTXV0MHZzTXV0MV8xNiRmaXREMTJEMDRfbXV0YXRpb25zXzEpLCAKICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJwLXZhbHVlID0iLCBwX3ZhbHVlX3NjaWVudGlmaWMxNl92MSksIGhqdXN0ID0gMSwgdmp1c3QgPSAwKSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gbWF4KE11dDB2c011dDFfMTYkZml0RDEyRDA0X211dGF0aW9uc18wKSwgeSA9IG1pbihNdXQwdnNNdXQxXzE2JGZpdEQxMkQwNF9tdXRhdGlvbnNfMSksCiAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIkNvcnJlbGF0aW9uID0iLCByb3VuZChjb3JfdmFsdWVfTXV0MHZzTXV0MSwgMikpLCBoanVzdCA9IDEsIHZqdXN0ID0gLTEuNSkgKwogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IG1pbihNdXQwdnNNdXQxXzE2JGZpdEQxMkQwNF9tdXRhdGlvbnNfMCksIHkgPSBtYXgoTXV0MHZzTXV0MV8xNiRmaXREMTJEMDRfbXV0YXRpb25zXzEpLAogICAgICAgICAgIGxhYmVsID0gcGFzdGUoIk11dGFudHMgPSIsIG51bV9yb3dzMTYubXV0MHYxKSwgaGp1c3QgPSAwLCB2anVzdCA9IDEuNSkKCm11dDB2MV8xNnBsb3QyIDwtIGdnTWFyZ2luYWwobXV0MHYxXzE2cGxvdCwgdHlwZSA9ICJoaXN0b2dyYW0iLCBmaWxsID0gIiNFNjlGMDAiLCBhbHBoYT0wLjc1KSAjYWRkIHNpZGUgaGlzdG9ncmFtcwptdXQwdjFfMTZwbG90MgoKIyBQbG90IHRoZSBjb3JyZWxhdGlvbiAoTXV0MHZzTXV0MikKbXV0MHYyXzE2cGxvdCA8LSBnZ3Bsb3QoTXV0MHZzTXV0Ml8xNiwgCiAgICAgICAgICAgICBhZXMoeCA9IGZpdEQxMkQwNF9tdXRhdGlvbnNfMCwgeSA9IGZpdEQxMkQwNF9tdXRhdGlvbnNfMiwgY29sb3I9bnVtcHJ1bmVkQkNzX211dGF0aW9uc18wKSkgKwogIGxhYnMoeCA9ICJmaXREMTJEMDQgKG11dGF0aW9ucyA9IDApIiwKICAgICAgIHkgPSAiZml0RDEyRDA0IChtdXRhdGlvbnMgPSAyKSIsIGNvbG9yPSIiLAogICAgICAgdGl0bGUgPSAiTGliMTYgQ29tcGxlbWVudGF0aW9uIChNdXQ9MCB2cyBNdXQ9MikiKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtLGNvbG91cj0iYmxhY2siKSArCiAgZ2VvbV9kZW5zaXR5MmQoY29sb3VyPSJibGFjayIsYWxwaGE9MC4yKSArCiAgZ2VvbV9wb2ludChhbHBoYT0wLjcpICsKICBzY2FsZV9jb2xvdXJfZ3JhZGllbnQyKCJCQ3MiLGxvdz0iYmx1ZSIsIGhpZ2g9InJlZCIsIG1pZD0iYmx1ZSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMC41KSwgCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSJsZWZ0IikgKwogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IG1heChNdXQwdnNNdXQyXzE2JGZpdEQxMkQwNF9tdXRhdGlvbnNfMCksIHkgPSBtaW4oTXV0MHZzTXV0Ml8xNiRmaXREMTJEMDRfbXV0YXRpb25zXzIpLCAKICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJwLXZhbHVlID0iLCBwX3ZhbHVlX3NjaWVudGlmaWMxNl92MiksIGhqdXN0ID0gMSwgdmp1c3QgPSAwKSsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSBtYXgoTXV0MHZzTXV0Ml8xNiRmaXREMTJEMDRfbXV0YXRpb25zXzApLCB5ID0gbWluKE11dDB2c011dDJfMTYkZml0RDEyRDA0X211dGF0aW9uc18yKSwKICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiQ29ycmVsYXRpb24gPSIsIHJvdW5kKGNvcl92YWx1ZV9NdXQwdnNNdXQyLCAyKSksIGhqdXN0ID0gMSwgdmp1c3QgPSAtMS41KSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gbWluKE11dDB2c011dDJfMTYkZml0RDEyRDA0X211dGF0aW9uc18wKSwgeSA9IG1heChNdXQwdnNNdXQyXzE2JGZpdEQxMkQwNF9tdXRhdGlvbnNfMiksCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiTXV0YW50cyA9IiwgbnVtX3Jvd3MxNi5tdXQwdjIpLCBoanVzdCA9IDAsIHZqdXN0ID0gMS41KQoKbXV0MHYyXzE2cGxvdDIgPC0gZ2dNYXJnaW5hbChtdXQwdjJfMTZwbG90LCB0eXBlID0gImhpc3RvZ3JhbSIsIGZpbGwgPSAiI0U2OUYwMCIsIGFscGhhPTAuNzUpICNhZGQgc2lkZSBoaXN0b2dyYW1zCm11dDB2Ml8xNnBsb3QyCgojIFBsb3QgdGhlIGNvcnJlbGF0aW9uIChNdXQwdnNNdXQzKQptdXQwdjNfMTZwbG90IDwtIGdncGxvdChNdXQwdnNNdXQzXzE2LCAKICAgICAgICAgICAgIGFlcyh4ID0gZml0RDEyRDA0X211dGF0aW9uc18wLCB5ID0gZml0RDEyRDA0X211dGF0aW9uc18zLCBjb2xvcj1udW1wcnVuZWRCQ3NfbXV0YXRpb25zXzApKSArCiAgbGFicyh4ID0gImZpdEQxMkQwNCAobXV0YXRpb25zID0gMCkiLAogICAgICAgeSA9ICJmaXREMTJEMDQgKG11dGF0aW9ucyA9IDMpIiwgY29sb3I9IiIsCiAgICAgICB0aXRsZSA9ICJMaWIxNiBDb21wbGVtZW50YXRpb24gKE11dD0wIHZzIE11dD0zKSIpICsKICBnZW9tX3Ntb290aChtZXRob2Q9bG0sY29sb3VyPSJibGFjayIpICsKICBnZW9tX2RlbnNpdHkyZChjb2xvdXI9ImJsYWNrIixhbHBoYT0wLjIpICsKICBnZW9tX3BvaW50KGFscGhhPTAuNykgKwogIHNjYWxlX2NvbG91cl9ncmFkaWVudDIoIkJDcyIsbG93PSJibHVlIiwgaGlnaD0icmVkIiwgbWlkPSJibHVlIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICdibGFjaycsIHNpemUgPSAwLjUpLCAKICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb249ImxlZnQiKSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gbWF4KE11dDB2c011dDNfMTYkZml0RDEyRDA0X211dGF0aW9uc18wKSwgeSA9IG1pbihNdXQwdnNNdXQzXzE2JGZpdEQxMkQwNF9tdXRhdGlvbnNfMyksIAogICAgICAgICAgIGxhYmVsID0gcGFzdGUoInAtdmFsdWUgPSIsIHBfdmFsdWVfc2NpZW50aWZpYzE2X3YzKSwgaGp1c3QgPSAxLCB2anVzdCA9IDApKwogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IG1heChNdXQwdnNNdXQzXzE2JGZpdEQxMkQwNF9tdXRhdGlvbnNfMCksIHkgPSBtaW4oTXV0MHZzTXV0M18xNiRmaXREMTJEMDRfbXV0YXRpb25zXzMpLAogICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJDb3JyZWxhdGlvbiA9Iiwgcm91bmQoY29yX3ZhbHVlX011dDB2c011dDMsIDIpKSwgaGp1c3QgPSAxLCB2anVzdCA9IC0xLjUpICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSBtaW4oTXV0MHZzTXV0M18xNiRmaXREMTJEMDRfbXV0YXRpb25zXzApLCB5ID0gbWF4KE11dDB2c011dDNfMTYkZml0RDEyRDA0X211dGF0aW9uc18zKSwKICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJNdXRhbnRzID0iLCBudW1fcm93czE2Lm11dDB2MyksIGhqdXN0ID0gMCwgdmp1c3QgPSAxLjUpCgptdXQwdjNfMTZwbG90MiA8LSBnZ01hcmdpbmFsKG11dDB2M18xNnBsb3QsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbCA9ICIjRTY5RjAwIiwgYWxwaGE9MC43NSkgI2FkZCBzaWRlIGhpc3RvZ3JhbXMKbXV0MHYzXzE2cGxvdDIKCiMgUGxvdCB0aGUgY29ycmVsYXRpb24gKE11dDB2c011dDQpCm11dDB2NF8xNnBsb3QgPC0gZ2dwbG90KE11dDB2c011dDRfMTYsIAogICAgICAgICAgICAgYWVzKHggPSBmaXREMTJEMDRfbXV0YXRpb25zXzAsIHkgPSBmaXREMTJEMDRfbXV0YXRpb25zXzQsIGNvbG9yPW51bXBydW5lZEJDc19tdXRhdGlvbnNfMCkpICsKICBsYWJzKHggPSAiZml0RDEyRDA0IChtdXRhdGlvbnMgPSAwKSIsCiAgICAgICB5ID0gImZpdEQxMkQwNCAobXV0YXRpb25zID0gNCkiLCBjb2xvcj0iIiwKICAgICAgIHRpdGxlID0gIkxpYjE2IENvbXBsZW1lbnRhdGlvbiAoTXV0PTAgdnMgTXV0PTQpIikgKwogIGdlb21fc21vb3RoKG1ldGhvZD1sbSxjb2xvdXI9ImJsYWNrIikgKwogIGdlb21fZGVuc2l0eTJkKGNvbG91cj0iYmxhY2siLGFscGhhPTAuMikgKwogIGdlb21fcG9pbnQoYWxwaGE9MC43KSArCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50MigiQkNzIixsb3c9ImJsdWUiLCBoaWdoPSJyZWQiLCBtaWQ9ImJsdWUiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2JsYWNrJywgc2l6ZSA9IDAuNSksIAogICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbj0ibGVmdCIpICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSBtYXgoTXV0MHZzTXV0NF8xNiRmaXREMTJEMDRfbXV0YXRpb25zXzApLCB5ID0gbWluKE11dDB2c011dDRfMTYkZml0RDEyRDA0X211dGF0aW9uc180KSwgCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgicC12YWx1ZSA9IiwgcF92YWx1ZV9zY2llbnRpZmljMTZfdjQpLCBoanVzdCA9IDEsIHZqdXN0ID0gMCkrCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gbWF4KE11dDB2c011dDRfMTYkZml0RDEyRDA0X211dGF0aW9uc18wKSwgeSA9IG1pbihNdXQwdnNNdXQ0XzE2JGZpdEQxMkQwNF9tdXRhdGlvbnNfNCksCiAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIkNvcnJlbGF0aW9uID0iLCByb3VuZChjb3JfdmFsdWVfTXV0MHZzTXV0NCwgMikpLCBoanVzdCA9IDEsIHZqdXN0ID0gLTEuNSkgKwogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IG1pbihNdXQwdnNNdXQ0XzE2JGZpdEQxMkQwNF9tdXRhdGlvbnNfMCksIHkgPSBtYXgoTXV0MHZzTXV0NF8xNiRmaXREMTJEMDRfbXV0YXRpb25zXzQpLAogICAgICAgICAgIGxhYmVsID0gcGFzdGUoIk11dGFudHMgPSIsIG51bV9yb3dzMTYubXV0MHY0KSwgaGp1c3QgPSAwLCB2anVzdCA9IDEuNSkKCm11dDB2NF8xNnBsb3QyIDwtIGdnTWFyZ2luYWwobXV0MHY0XzE2cGxvdCwgdHlwZSA9ICJoaXN0b2dyYW0iLCBmaWxsID0gIiNFNjlGMDAiLCBhbHBoYT0wLjc1KSAjYWRkIHNpZGUgaGlzdG9ncmFtcwptdXQwdjRfMTZwbG90MgoKIyBQbG90IHRoZSBjb3JyZWxhdGlvbiAoTXV0MHZzTXV0NSkKbXV0MHY1XzE2cGxvdCA8LSBnZ3Bsb3QoTXV0MHZzTXV0NV8xNiwgCiAgICAgICAgICAgICBhZXMoeCA9IGZpdEQxMkQwNF9tdXRhdGlvbnNfMCwgeSA9IGZpdEQxMkQwNF9tdXRhdGlvbnNfNSwgY29sb3I9bnVtcHJ1bmVkQkNzX211dGF0aW9uc18wKSkgKwogIGxhYnMoeCA9ICJmaXREMTJEMDQgKG11dGF0aW9ucyA9IDApIiwKICAgICAgIHkgPSAiZml0RDEyRDA0IChtdXRhdGlvbnMgPSA1KSIsIGNvbG9yPSIiLAogICAgICAgdGl0bGUgPSAiTGliMTYgQ29tcGxlbWVudGF0aW9uIChNdXQ9MCB2cyBNdXQ9NSkiKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtLGNvbG91cj0iYmxhY2siKSArCiAgZ2VvbV9kZW5zaXR5MmQoY29sb3VyPSJibGFjayIsYWxwaGE9MC4yKSArCiAgZ2VvbV9wb2ludChhbHBoYT0wLjcpICsKICBzY2FsZV9jb2xvdXJfZ3JhZGllbnQyKCJCQ3MiLGxvdz0iYmx1ZSIsIGhpZ2g9InJlZCIsIG1pZD0iYmx1ZSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMC41KSwgCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSJsZWZ0IikgKwogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IG1heChNdXQwdnNNdXQ1XzE2JGZpdEQxMkQwNF9tdXRhdGlvbnNfMCksIHkgPSBtaW4oTXV0MHZzTXV0NV8xNiRmaXREMTJEMDRfbXV0YXRpb25zXzUpLCAKICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJwLXZhbHVlID0iLCBwX3ZhbHVlX3NjaWVudGlmaWMxNl92NSksIGhqdXN0ID0gMSwgdmp1c3QgPSAwKSsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSBtYXgoTXV0MHZzTXV0NV8xNiRmaXREMTJEMDRfbXV0YXRpb25zXzApLCB5ID0gbWluKE11dDB2c011dDVfMTYkZml0RDEyRDA0X211dGF0aW9uc181KSwKICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiQ29ycmVsYXRpb24gPSIsIHJvdW5kKGNvcl92YWx1ZV9NdXQwdnNNdXQ1LCAyKSksIGhqdXN0ID0gMSwgdmp1c3QgPSAtMS41KSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gbWluKE11dDB2c011dDVfMTYkZml0RDEyRDA0X211dGF0aW9uc18wKSwgeSA9IG1heChNdXQwdnNNdXQ1XzE2JGZpdEQxMkQwNF9tdXRhdGlvbnNfNSksCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiTXV0YW50cyA9IiwgbnVtX3Jvd3MxNi5tdXQwdjUpLCBoanVzdCA9IDAsIHZqdXN0ID0gMS41KQoKbXV0MHY1XzE2cGxvdDIgPC0gZ2dNYXJnaW5hbChtdXQwdjVfMTZwbG90LCB0eXBlID0gImhpc3RvZ3JhbSIsIGZpbGwgPSAiI0U2OUYwMCIsIGFscGhhPTAuNzUpICNhZGQgc2lkZSBoaXN0b2dyYW1zCm11dDB2NV8xNnBsb3QyCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9Ik11dGFudHMvUExPVFMvQ29ycmVsYXRpb25zL0wxNi5tdXRhbnQuY29ycmVsYXRpb24ubXV0MC52cy5tdXQxLmNvbXBsZW1lbnRhdGlvbi5wbmciLAogICAgICAgcGxvdD1tdXQwdjFfMTZwbG90MiwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iKQoKZ2dzYXZlKGZpbGU9Ik11dGFudHMvUExPVFMvQ29ycmVsYXRpb25zL0wxNi5tdXRhbnQuY29ycmVsYXRpb24ubXV0MC52cy5tdXQyLmNvbXBsZW1lbnRhdGlvbi5wbmciLAogICAgICAgcGxvdD1tdXQwdjJfMTZwbG90MiwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iKQoKZ2dzYXZlKGZpbGU9Ik11dGFudHMvUExPVFMvQ29ycmVsYXRpb25zL0wxNi5tdXRhbnQuY29ycmVsYXRpb24ubXV0MC52cy5tdXQzLmNvbXBsZW1lbnRhdGlvbi5wbmciLAogICAgICAgcGxvdD1tdXQwdjNfMTZwbG90MiwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iKQoKZ2dzYXZlKGZpbGU9Ik11dGFudHMvUExPVFMvQ29ycmVsYXRpb25zL0wxNi5tdXRhbnQuY29ycmVsYXRpb24ubXV0MC52cy5tdXQ0LmNvbXBsZW1lbnRhdGlvbi5wbmciLAogICAgICAgcGxvdD1tdXQwdjRfMTZwbG90MiwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iKQoKZ2dzYXZlKGZpbGU9Ik11dGFudHMvUExPVFMvQ29ycmVsYXRpb25zL0wxNi5tdXRhbnQuY29ycmVsYXRpb24ubXV0MC52cy5tdXQ1LmNvbXBsZW1lbnRhdGlvbi5wbmciLAogICAgICAgcGxvdD1tdXQwdjVfMTZwbG90MiwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iKQpgYGAKCiMjIEhvbW9sb2cgTXV0YW50IEZpdG5lc3MKCkNyZWF0ZSByaWRnZSBwbG90cyBmb3IgaG9tb2xvZyBmaXRuZXNzIGFjcm9zcyB0aGUgVE1QIGdyYWRpZW50IHVzaW5nIGhvbW9sb2dzIHdpdGggMCBtdXRhdGlvbnMsIDEgYXNzb2NpYXRlZCBtdXRhdGlvbiwgYW5kIDUgYXNzb2NpYXRlZCBtdXRhdGlvbnM6CgoqKlRoaXMgc2VjdGlvbiB1c2VzIHRoZSBgbGlicmFyeShnZ3JpZGdlcylgIHBhY2thZ2UuKioKCkZpcnN0LCBzdWJzZXQgdGhlIGBtdXRfY29sbGFwc2VfMTVgIGFuZCBgbXV0X2NvbGxhcHNlXzE2YCBvYmplY3RzIHRvIHJldGFpbiBvbmx5ICJJRCIsICJtdXRJRCIsICJudW1wcnVuZWRCQ3MiLCAibXV0YXRpb25zIiwgInNlcSIsICJwY3RfaWRlbnQiLCBhbmQgZml0bmVzcyB2YWx1ZXMgZm9yIGZpcnN0IHRpbWUgcG9pbnQuCmBgYHtyfQojIExpYjE1Cm11dF9jb2xsYXBzZV8xNV9zdWJzZXQgPC0gbXV0X2NvbGxhcHNlXzE1ICU+JSBzZWxlY3QoSUQsIG11dElELCBtdXRhdGlvbnMsIG51bXBydW5lZEJDcywgZml0RDA1RDAzLCBmaXREMDZEMDMsIGZpdEQwN0QwMywgZml0RDA4RDAzLCBmaXREMDlEMDMsIGZpdEQxMEQwMywgZml0RDExRDAzLCBzZXEsIHBjdF9pZGVudCkKCiMgTGliMTYKbXV0X2NvbGxhcHNlXzE2X3N1YnNldCA8LSBtdXRfY29sbGFwc2VfMTYgJT4lIHNlbGVjdChJRCwgbXV0SUQsIG11dGF0aW9ucywgbnVtcHJ1bmVkQkNzLCBmaXREMTJEMDQsIGZpdEUwMUQwNCwgZml0RTAyRDA0LCBmaXRFMDNEMDQsIGZpdEUwNEQwNCwgZml0RTA1RDA0LCBmaXRFMDZEMDQsIHNlcSwgcGN0X2lkZW50KQpgYGAKClRyYW5zZm9ybSBib3RoIGRhdGFzZXRzIHByaW9yIHRvIHBsb3R0aW5nIHJpZGdlIHBsb3RzIGZvciBmaXRuZXNzOgoKIyMjIFBlcmZlY3RzICg+NSBCQ3MsIDAgbXV0YXRpb25zKQpgYGB7cn0KIyBMaWIxNQptdXRfY29sbGFwc2VfMTVfc3Vic2V0XzBtdXQgPC0gbXV0X2NvbGxhcHNlXzE1X3N1YnNldCAlPiUKICBmaWx0ZXIobXV0YXRpb25zID09IDApICU+JSAgIyBBZGQgdGhpcyBsaW5lIHRvIGZpbHRlciBmb3IgbXV0YXRpb25zID09IDAKICBzZWxlY3QoSUQsIGZpdEQwNUQwMywgZml0RDA2RDAzLCBmaXREMDdEMDMsIGZpdEQwOEQwMywgZml0RDA5RDAzLCBmaXREMTBEMDMsIGZpdEQxMUQwMykgJT4lCiAgcGl2b3RfbG9uZ2VyKCFJRCwgbmFtZXNfdG8gPSAiZmMiLCB2YWx1ZXNfdG8gPSAidmFsIikgJT4lCiAgbXV0YXRlKFRNUCA9IGNhc2Vfd2hlbigKICAgIGZjID09ICJmaXREMDVEMDMiIH4gIjAtVE1QIiwKICAgIGZjID09ICJmaXREMDZEMDMiIH4gIjAuMDU4LVRNUCIsCiAgICBmYyA9PSAiZml0RDA3RDAzIiB+ICIwLjUtVE1QIiwKICAgIGZjID09ICJmaXREMDhEMDMiIH4gIjEuMC1UTVAiLAogICAgZmMgPT0gImZpdEQwOUQwMyIgfiAiMTAtVE1QIiwKICAgIGZjID09ICJmaXREMTBEMDMiIH4gIjUwLVRNUCIsCiAgICBmYyA9PSAiZml0RDExRDAzIiB+ICIyMDAtVE1QIiwKICAgIFRSVUUgfiBOQV9jaGFyYWN0ZXJfKSkKCiMgTGliMTYKbXV0X2NvbGxhcHNlXzE2X3N1YnNldF8wbXV0IDwtIG11dF9jb2xsYXBzZV8xNl9zdWJzZXQgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyA9PSAwKSAlPiUgICMgQWRkIHRoaXMgbGluZSB0byBmaWx0ZXIgZm9yIG11dGF0aW9ucyA9PSAwCiAgc2VsZWN0KElELCBmaXREMTJEMDQsIGZpdEUwMUQwNCwgZml0RTAyRDA0LCBmaXRFMDNEMDQsIGZpdEUwNEQwNCwgZml0RTA1RDA0LCBmaXRFMDZEMDQpICU+JQogIHBpdm90X2xvbmdlcighSUQsIG5hbWVzX3RvID0gImZjIiwgdmFsdWVzX3RvID0gInZhbCIpICU+JQogIG11dGF0ZShUTVAgPSBjYXNlX3doZW4oCiAgICBmYyA9PSAiZml0RDEyRDA0IiB+ICIwLVRNUCIsCiAgICBmYyA9PSAiZml0RTAxRDA0IiB+ICIwLjA1OC1UTVAiLAogICAgZmMgPT0gImZpdEUwMkQwNCIgfiAiMC41LVRNUCIsCiAgICBmYyA9PSAiZml0RTAzRDA0IiB+ICIxLjAtVE1QIiwKICAgIGZjID09ICJmaXRFMDREMDQiIH4gIjEwLVRNUCIsCiAgICBmYyA9PSAiZml0RTA1RDA0IiB+ICI1MC1UTVAiLAogICAgZmMgPT0gImZpdEUwNkQwNCIgfiAiMjAwLVRNUCIsCiAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXykpCmBgYAoKYGBge3J9CiMgQ29tYmluZSB0aGUgdHdvIGRhdGEgZnJhbWVzCm11dF9jb2xsYXBzZV8xNV8xNl81QkNzXzBtdXQgPC0gYmluZF9yb3dzKAogIG11dF9jb2xsYXBzZV8xNV9zdWJzZXRfMG11dCAlPiUgbXV0YXRlKExpYiA9ICJDb2RvbjEiKSwKICBtdXRfY29sbGFwc2VfMTZfc3Vic2V0XzBtdXQgJT4lIG11dGF0ZShMaWIgPSAiQ29kb24yIiksCiAgLmlkID0gImlkIikKYGBgCgpQbG90IFBlcmZlY3RzIGZpdG5lc3Mgc2NvcmVzIGJhc2VkIG9uIFN1cHBsZW1lbnRhdGlvbiB0cmVhdG1lbnQgZm9yIGZpcnN0IHNhbXBsaW5nIHRpbWUgcG9pbnQKYGBge3J9Cm11dF9jb2xsYXBzZV8xNV8xNl81QkNzXzBtdXRfb3JkZXIgPC0gYygiMjAwLVRNUCIsICI1MC1UTVAiLCAiMTAtVE1QIiwgIjEuMC1UTVAiLCAiMC41LVRNUCIsICIwLjA1OC1UTVAiLCAiMC1UTVAiKQoKdG1wX3JpZGdlc18xNV8xNl8wbXV0IDwtIGdncGxvdChtdXRfY29sbGFwc2VfMTVfMTZfNUJDc18wbXV0LCBhZXMoeCA9IHZhbCwgeSA9IGZhY3RvcihUTVAsIGxldmVsID0gbXV0X2NvbGxhcHNlXzE1XzE2XzVCQ3NfMG11dF9vcmRlciksIGZpbGwgPSBMaWIpKSArCiAgZ2VvbV9kZW5zaXR5X3JpZGdlcyhhbHBoYSA9IDAuNykgKwogIHNjYWxlX3lfZGlzY3JldGUobGFiZWxzID0gYygnMjAwIM68Zy9tTCBUTVAnLCAnNTAgzrxnL21MIFRNUCcsICcxMCDOvGcvbUwgVE1QJywgJzEgzrxnL21MIFRNUCcsICcwLjUgzrxnL21MIFRNUCcsICcwLjA1OCDOvGcvbUwgVE1QJywgJ0NvbXBsZW1lbnRhdGlvbicpKSArCiAgeGxhYigiTWVkaWFuIEZpdG5lc3MgKExvZ0ZDKSIpICsKICB5bGFiKCJTZWxlY3Rpb24gQ29uZGl0aW9uICh1Zy9tTCBUTVApIikgKwogIGdndGl0bGUoIkRpc3RhbmNlIGZyb20gSG9tb2xvZyBcbk11dGF0aW9ucyA9IDAgYS5hLiIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKAogICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICdibGFjaycsIHNpemUgPSAxLjApLAogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMS4wKSwKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBoanVzdCA9IDAuNSksCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKC0xNSwgMTApKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiQ29kb24xIiA9ICIjMDA3MkIyIiwgIkNvZG9uMiIgPSAiI0U2OUYwMCIpLCBuYW1lID0gIkxpYnJhcnkiKQoKIyBEaXNwbGF5IHRoZSBwbG90CnRtcF9yaWRnZXNfMTVfMTZfMG11dApgYGAKCmBgYHtyIGVjaG89RkFMU0V9Cmdnc2F2ZShmaWxlPSJNdXRhbnRzL1BMT1RTL0ZpdG5lc3MvTGliMTUuMTYuNUJDcy5tdXRhbnRzLmRvc2UucmVzcG9uc2UucmlkZ2VzLjAubXV0YW50cy5wbmciLAogICAgICAgcGxvdD10bXBfcmlkZ2VzXzE1XzE2XzBtdXQsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDYsIGhlaWdodCA9IDgsIHVuaXRzID0gImluIikKYGBgCgojIyMgSG9tb2xvZ3Mgdy8gTXV0cyAoMSBhLmEuIG11dGF0aW9uKQpgYGB7cn0KIyBMaWIxNQptdXRfY29sbGFwc2VfMTVfc3Vic2V0XzAuMW11dCA8LSBtdXRfY29sbGFwc2VfMTVfc3Vic2V0ICU+JQogICNmaWx0ZXIobXV0YXRpb25zICVpbiUgYygwLCAxKSkgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyA9PSAxKSAlPiUKICBzZWxlY3QoSUQsIGZpdEQwNUQwMywgZml0RDA2RDAzLCBmaXREMDdEMDMsIGZpdEQwOEQwMywgZml0RDA5RDAzLCBmaXREMTBEMDMsIGZpdEQxMUQwMykgJT4lCiAgcGl2b3RfbG9uZ2VyKCFJRCwgbmFtZXNfdG8gPSAiZmMiLCB2YWx1ZXNfdG8gPSAidmFsIikgJT4lCiAgbXV0YXRlKFRNUCA9IGNhc2Vfd2hlbigKICAgIGZjID09ICJmaXREMDVEMDMiIH4gIjAtVE1QIiwKICAgIGZjID09ICJmaXREMDZEMDMiIH4gIjAuMDU4LVRNUCIsCiAgICBmYyA9PSAiZml0RDA3RDAzIiB+ICIwLjUtVE1QIiwKICAgIGZjID09ICJmaXREMDhEMDMiIH4gIjEuMC1UTVAiLAogICAgZmMgPT0gImZpdEQwOUQwMyIgfiAiMTAtVE1QIiwKICAgIGZjID09ICJmaXREMTBEMDMiIH4gIjUwLVRNUCIsCiAgICBmYyA9PSAiZml0RDExRDAzIiB+ICIyMDAtVE1QIiwKICAgIFRSVUUgfiBOQV9jaGFyYWN0ZXJfKSkKCiMgTGliMTYKbXV0X2NvbGxhcHNlXzE2X3N1YnNldF8wLjFtdXQgPC0gbXV0X2NvbGxhcHNlXzE2X3N1YnNldCAlPiUKICAjZmlsdGVyKG11dGF0aW9ucyAlaW4lIGMoMCwgMSkpICU+JQogIGZpbHRlcihtdXRhdGlvbnMgPT0gMSkgJT4lCiAgc2VsZWN0KElELCBmaXREMTJEMDQsIGZpdEUwMUQwNCwgZml0RTAyRDA0LCBmaXRFMDNEMDQsIGZpdEUwNEQwNCwgZml0RTA1RDA0LCBmaXRFMDZEMDQpICU+JQogIHBpdm90X2xvbmdlcighSUQsIG5hbWVzX3RvID0gImZjIiwgdmFsdWVzX3RvID0gInZhbCIpICU+JQogIG11dGF0ZShUTVAgPSBjYXNlX3doZW4oCiAgICBmYyA9PSAiZml0RDEyRDA0IiB+ICIwLVRNUCIsCiAgICBmYyA9PSAiZml0RTAxRDA0IiB+ICIwLjA1OC1UTVAiLAogICAgZmMgPT0gImZpdEUwMkQwNCIgfiAiMC41LVRNUCIsCiAgICBmYyA9PSAiZml0RTAzRDA0IiB+ICIxLjAtVE1QIiwKICAgIGZjID09ICJmaXRFMDREMDQiIH4gIjEwLVRNUCIsCiAgICBmYyA9PSAiZml0RTA1RDA0IiB+ICI1MC1UTVAiLAogICAgZmMgPT0gImZpdEUwNkQwNCIgfiAiMjAwLVRNUCIsCiAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXykpCmBgYAoKYGBge3J9CiMgQ29tYmluZSB0aGUgdHdvIGRhdGEgZnJhbWVzCm11dF9jb2xsYXBzZV8xNV8xNl81QkNzXzAuMW11dCA8LSBiaW5kX3Jvd3MoCiAgbXV0X2NvbGxhcHNlXzE1X3N1YnNldF8wLjFtdXQgJT4lIG11dGF0ZShMaWIgPSAiQ29kb24xIiksCiAgbXV0X2NvbGxhcHNlXzE2X3N1YnNldF8wLjFtdXQgJT4lIG11dGF0ZShMaWIgPSAiQ29kb24yIiksCiAgLmlkID0gImlkIikKYGBgCgpQbG90IFBlcmZlY3RzIGZpdG5lc3Mgc2NvcmVzIGJhc2VkIG9uIFN1cHBsZW1lbnRhdGlvbiB0cmVhdG1lbnQgZm9yIGZpcnN0IHNhbXBsaW5nIHRpbWUgcG9pbnQKYGBge3J9Cm11dF9jb2xsYXBzZV8xNV8xNl81QkNzXzAuMW11dF9vcmRlciA8LSBjKCIyMDAtVE1QIiwgIjUwLVRNUCIsICIxMC1UTVAiLCAiMS4wLVRNUCIsICIwLjUtVE1QIiwgIjAuMDU4LVRNUCIsICIwLVRNUCIpCgp0bXBfcmlkZ2VzXzE1XzE2XzAuMW11dCA8LSBnZ3Bsb3QobXV0X2NvbGxhcHNlXzE1XzE2XzVCQ3NfMC4xbXV0LCBhZXMoeCA9IHZhbCwgeSA9IGZhY3RvcihUTVAsIGxldmVsID0gbXV0X2NvbGxhcHNlXzE1XzE2XzVCQ3NfMC4xbXV0X29yZGVyKSwgZmlsbCA9IExpYikpICsKICBnZW9tX2RlbnNpdHlfcmlkZ2VzKGFscGhhID0gMC43KSArCiAgc2NhbGVfeV9kaXNjcmV0ZShsYWJlbHMgPSBjKCcyMDAgzrxnL21MIFRNUCcsICc1MCDOvGcvbUwgVE1QJywgJzEwIM68Zy9tTCBUTVAnLCAnMSDOvGcvbUwgVE1QJywgJzAuNSDOvGcvbUwgVE1QJywgJzAuMDU4IM68Zy9tTCBUTVAnLCAnQ29tcGxlbWVudGF0aW9uJykpICsKICB4bGFiKCJNZWRpYW4gRml0bmVzcyAoTG9nRkMpIikgKwogIHlsYWIoIlNlbGVjdGlvbiBDb25kaXRpb24gKHVnL21MIFRNUCkiKSArCiAgZ2d0aXRsZSgiRGlzdGFuY2UgZnJvbSBIb21vbG9nIFxuTXV0YXRpb25zID0gMSBhLmEuIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2JsYWNrJywgc2l6ZSA9IDEuMCksCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAxLjApLAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41KSwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoLTE1LCAxMCkpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJDb2RvbjEiID0gIiMwMDcyQjIiLCAiQ29kb24yIiA9ICIjRTY5RjAwIiksIG5hbWUgPSAiTGlicmFyeSIpCgojIERpc3BsYXkgdGhlIHBsb3QKdG1wX3JpZGdlc18xNV8xNl8wLjFtdXQKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iTXV0YW50cy9QTE9UUy9GaXRuZXNzL0xpYjE1LjE2LjVCQ3MubXV0YW50cy5kb3NlLnJlc3BvbnNlLnJpZGdlcy4xLm11dGFudHMucG5nIiwKICAgICAgIHBsb3Q9dG1wX3JpZGdlc18xNV8xNl8wLjFtdXQsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDYsIGhlaWdodCA9IDgsIHVuaXRzID0gImluIikKYGBgCgojIyMgSG9tb2xvZ3Mgdy8gTXV0cyAoMiBhLmEuIG11dGF0aW9uKQpgYGB7cn0KIyBMaWIxNQptdXRfY29sbGFwc2VfMTVfc3Vic2V0XzAuMm11dCA8LSBtdXRfY29sbGFwc2VfMTVfc3Vic2V0ICU+JQogICNmaWx0ZXIobXV0YXRpb25zICVpbiUgYygwLCAxLCAyKSkgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyA9PSAyKSAlPiUKICBzZWxlY3QoSUQsIGZpdEQwNUQwMywgZml0RDA2RDAzLCBmaXREMDdEMDMsIGZpdEQwOEQwMywgZml0RDA5RDAzLCBmaXREMTBEMDMsIGZpdEQxMUQwMykgJT4lCiAgcGl2b3RfbG9uZ2VyKCFJRCwgbmFtZXNfdG8gPSAiZmMiLCB2YWx1ZXNfdG8gPSAidmFsIikgJT4lCiAgbXV0YXRlKFRNUCA9IGNhc2Vfd2hlbigKICAgIGZjID09ICJmaXREMDVEMDMiIH4gIjAtVE1QIiwKICAgIGZjID09ICJmaXREMDZEMDMiIH4gIjAuMDU4LVRNUCIsCiAgICBmYyA9PSAiZml0RDA3RDAzIiB+ICIwLjUtVE1QIiwKICAgIGZjID09ICJmaXREMDhEMDMiIH4gIjEuMC1UTVAiLAogICAgZmMgPT0gImZpdEQwOUQwMyIgfiAiMTAtVE1QIiwKICAgIGZjID09ICJmaXREMTBEMDMiIH4gIjUwLVRNUCIsCiAgICBmYyA9PSAiZml0RDExRDAzIiB+ICIyMDAtVE1QIiwKICAgIFRSVUUgfiBOQV9jaGFyYWN0ZXJfKSkKCiMgTGliMTYKbXV0X2NvbGxhcHNlXzE2X3N1YnNldF8wLjJtdXQgPC0gbXV0X2NvbGxhcHNlXzE2X3N1YnNldCAlPiUKICAjZmlsdGVyKG11dGF0aW9ucyAlaW4lIGMoMCwgMSwgMikpICU+JQogIGZpbHRlcihtdXRhdGlvbnMgPT0gMikgJT4lCiAgc2VsZWN0KElELCBmaXREMTJEMDQsIGZpdEUwMUQwNCwgZml0RTAyRDA0LCBmaXRFMDNEMDQsIGZpdEUwNEQwNCwgZml0RTA1RDA0LCBmaXRFMDZEMDQpICU+JQogIHBpdm90X2xvbmdlcighSUQsIG5hbWVzX3RvID0gImZjIiwgdmFsdWVzX3RvID0gInZhbCIpICU+JQogIG11dGF0ZShUTVAgPSBjYXNlX3doZW4oCiAgICBmYyA9PSAiZml0RDEyRDA0IiB+ICIwLVRNUCIsCiAgICBmYyA9PSAiZml0RTAxRDA0IiB+ICIwLjA1OC1UTVAiLAogICAgZmMgPT0gImZpdEUwMkQwNCIgfiAiMC41LVRNUCIsCiAgICBmYyA9PSAiZml0RTAzRDA0IiB+ICIxLjAtVE1QIiwKICAgIGZjID09ICJmaXRFMDREMDQiIH4gIjEwLVRNUCIsCiAgICBmYyA9PSAiZml0RTA1RDA0IiB+ICI1MC1UTVAiLAogICAgZmMgPT0gImZpdEUwNkQwNCIgfiAiMjAwLVRNUCIsCiAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXykpCmBgYAoKYGBge3J9CiMgQ29tYmluZSB0aGUgdHdvIGRhdGEgZnJhbWVzCm11dF9jb2xsYXBzZV8xNV8xNl81QkNzXzAuMm11dCA8LSBiaW5kX3Jvd3MoCiAgbXV0X2NvbGxhcHNlXzE1X3N1YnNldF8wLjJtdXQgJT4lIG11dGF0ZShMaWIgPSAiQ29kb24xIiksCiAgbXV0X2NvbGxhcHNlXzE2X3N1YnNldF8wLjJtdXQgJT4lIG11dGF0ZShMaWIgPSAiQ29kb24yIiksCiAgLmlkID0gImlkIikKYGBgCgpQbG90IFBlcmZlY3RzIGZpdG5lc3Mgc2NvcmVzIGJhc2VkIG9uIFN1cHBsZW1lbnRhdGlvbiB0cmVhdG1lbnQgZm9yIGZpcnN0IHNhbXBsaW5nIHRpbWUgcG9pbnQKYGBge3J9Cm11dF9jb2xsYXBzZV8xNV8xNl81QkNzXzAuMm11dF9vcmRlciA8LSBjKCIyMDAtVE1QIiwgIjUwLVRNUCIsICIxMC1UTVAiLCAiMS4wLVRNUCIsICIwLjUtVE1QIiwgIjAuMDU4LVRNUCIsICIwLVRNUCIpCgp0bXBfcmlkZ2VzXzE1XzE2XzAuMm11dCA8LSBnZ3Bsb3QobXV0X2NvbGxhcHNlXzE1XzE2XzVCQ3NfMC4ybXV0LCBhZXMoeCA9IHZhbCwgeSA9IGZhY3RvcihUTVAsIGxldmVsID0gbXV0X2NvbGxhcHNlXzE1XzE2XzVCQ3NfMC4ybXV0X29yZGVyKSwgZmlsbCA9IExpYikpICsKICBnZW9tX2RlbnNpdHlfcmlkZ2VzKGFscGhhID0gMC43KSArCiAgc2NhbGVfeV9kaXNjcmV0ZShsYWJlbHMgPSBjKCcyMDAgzrxnL21MIFRNUCcsICc1MCDOvGcvbUwgVE1QJywgJzEwIM68Zy9tTCBUTVAnLCAnMSDOvGcvbUwgVE1QJywgJzAuNSDOvGcvbUwgVE1QJywgJzAuMDU4IM68Zy9tTCBUTVAnLCAnQ29tcGxlbWVudGF0aW9uJykpICsKICB4bGFiKCJNZWRpYW4gRml0bmVzcyAoTG9nRkMpIikgKwogIHlsYWIoIlNlbGVjdGlvbiBDb25kaXRpb24gKHVnL21MIFRNUCkiKSArCiAgZ2d0aXRsZSgiRGlzdGFuY2UgZnJvbSBIb21vbG9nIFxuTXV0YXRpb25zID0gMiBhLmEuIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2JsYWNrJywgc2l6ZSA9IDEuMCksCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAxLjApLAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41KSwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKC0xNSwgMTApKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiQ29kb24xIiA9ICIjMDA3MkIyIiwgIkNvZG9uMiIgPSAiI0U2OUYwMCIpLCBuYW1lID0gIkxpYnJhcnkiKQoKIyBEaXNwbGF5IHRoZSBwbG90CnRtcF9yaWRnZXNfMTVfMTZfMC4ybXV0CmBgYAoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9Ik11dGFudHMvUExPVFMvRml0bmVzcy9MaWIxNS4xNi41QkNzLm11dGFudHMuZG9zZS5yZXNwb25zZS5yaWRnZXMuMi5tdXRhbnRzLnBuZyIsCiAgICAgICBwbG90PXRtcF9yaWRnZXNfMTVfMTZfMC4ybXV0LAogICAgICAgZHBpPTYwMCwgd2lkdGggPSA2LCBoZWlnaHQgPSA4LCB1bml0cyA9ICJpbiIpCmBgYAoKIyMjIEhvbW9sb2dzIHcvIE11dHMgKDMgYS5hLiBtdXRhdGlvbikKYGBge3J9CiMgTGliMTUKbXV0X2NvbGxhcHNlXzE1X3N1YnNldF8wLjNtdXQgPC0gbXV0X2NvbGxhcHNlXzE1X3N1YnNldCAlPiUKICAjZmlsdGVyKG11dGF0aW9ucyAlaW4lIGMoMCwgMSwgMiwgMykpICU+JQogIGZpbHRlcihtdXRhdGlvbnMgPT0gMykgJT4lCiAgc2VsZWN0KElELCBmaXREMDVEMDMsIGZpdEQwNkQwMywgZml0RDA3RDAzLCBmaXREMDhEMDMsIGZpdEQwOUQwMywgZml0RDEwRDAzLCBmaXREMTFEMDMpICU+JQogIHBpdm90X2xvbmdlcighSUQsIG5hbWVzX3RvID0gImZjIiwgdmFsdWVzX3RvID0gInZhbCIpICU+JQogIG11dGF0ZShUTVAgPSBjYXNlX3doZW4oCiAgICBmYyA9PSAiZml0RDA1RDAzIiB+ICIwLVRNUCIsCiAgICBmYyA9PSAiZml0RDA2RDAzIiB+ICIwLjA1OC1UTVAiLAogICAgZmMgPT0gImZpdEQwN0QwMyIgfiAiMC41LVRNUCIsCiAgICBmYyA9PSAiZml0RDA4RDAzIiB+ICIxLjAtVE1QIiwKICAgIGZjID09ICJmaXREMDlEMDMiIH4gIjEwLVRNUCIsCiAgICBmYyA9PSAiZml0RDEwRDAzIiB+ICI1MC1UTVAiLAogICAgZmMgPT0gImZpdEQxMUQwMyIgfiAiMjAwLVRNUCIsCiAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXykpCgojIExpYjE2Cm11dF9jb2xsYXBzZV8xNl9zdWJzZXRfMC4zbXV0IDwtIG11dF9jb2xsYXBzZV8xNl9zdWJzZXQgJT4lCiAgI2ZpbHRlcihtdXRhdGlvbnMgJWluJSBjKDAsIDEsIDIsIDMpKSAlPiUKICBmaWx0ZXIobXV0YXRpb25zID09IDMpICU+JQogIHNlbGVjdChJRCwgZml0RDEyRDA0LCBmaXRFMDFEMDQsIGZpdEUwMkQwNCwgZml0RTAzRDA0LCBmaXRFMDREMDQsIGZpdEUwNUQwNCwgZml0RTA2RDA0KSAlPiUKICBwaXZvdF9sb25nZXIoIUlELCBuYW1lc190byA9ICJmYyIsIHZhbHVlc190byA9ICJ2YWwiKSAlPiUKICBtdXRhdGUoVE1QID0gY2FzZV93aGVuKAogICAgZmMgPT0gImZpdEQxMkQwNCIgfiAiMC1UTVAiLAogICAgZmMgPT0gImZpdEUwMUQwNCIgfiAiMC4wNTgtVE1QIiwKICAgIGZjID09ICJmaXRFMDJEMDQiIH4gIjAuNS1UTVAiLAogICAgZmMgPT0gImZpdEUwM0QwNCIgfiAiMS4wLVRNUCIsCiAgICBmYyA9PSAiZml0RTA0RDA0IiB+ICIxMC1UTVAiLAogICAgZmMgPT0gImZpdEUwNUQwNCIgfiAiNTAtVE1QIiwKICAgIGZjID09ICJmaXRFMDZEMDQiIH4gIjIwMC1UTVAiLAogICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8pKQpgYGAKCmBgYHtyfQojIENvbWJpbmUgdGhlIHR3byBkYXRhIGZyYW1lcwptdXRfY29sbGFwc2VfMTVfMTZfNUJDc18wLjNtdXQgPC0gYmluZF9yb3dzKAogIG11dF9jb2xsYXBzZV8xNV9zdWJzZXRfMC4zbXV0ICU+JSBtdXRhdGUoTGliID0gIkNvZG9uMSIpLAogIG11dF9jb2xsYXBzZV8xNl9zdWJzZXRfMC4zbXV0ICU+JSBtdXRhdGUoTGliID0gIkNvZG9uMiIpLAogIC5pZCA9ICJpZCIpCmBgYAoKUGxvdCBQZXJmZWN0cyBmaXRuZXNzIHNjb3JlcyBiYXNlZCBvbiBTdXBwbGVtZW50YXRpb24gdHJlYXRtZW50IGZvciBmaXJzdCBzYW1wbGluZyB0aW1lIHBvaW50CmBgYHtyfQptdXRfY29sbGFwc2VfMTVfMTZfNUJDc18wLjNtdXRfb3JkZXIgPC0gYygiMjAwLVRNUCIsICI1MC1UTVAiLCAiMTAtVE1QIiwgIjEuMC1UTVAiLCAiMC41LVRNUCIsICIwLjA1OC1UTVAiLCAiMC1UTVAiKQoKdG1wX3JpZGdlc18xNV8xNl8wLjNtdXQgPC0gZ2dwbG90KG11dF9jb2xsYXBzZV8xNV8xNl81QkNzXzAuM211dCwgYWVzKHggPSB2YWwsIHkgPSBmYWN0b3IoVE1QLCBsZXZlbCA9IG11dF9jb2xsYXBzZV8xNV8xNl81QkNzXzAuM211dF9vcmRlciksIGZpbGwgPSBMaWIpKSArCiAgZ2VvbV9kZW5zaXR5X3JpZGdlcyhhbHBoYSA9IDAuNykgKwogIHNjYWxlX3lfZGlzY3JldGUobGFiZWxzID0gYygnMjAwIM68Zy9tTCBUTVAnLCAnNTAgzrxnL21MIFRNUCcsICcxMCDOvGcvbUwgVE1QJywgJzEgzrxnL21MIFRNUCcsICcwLjUgzrxnL21MIFRNUCcsICcwLjA1OCDOvGcvbUwgVE1QJywgJ0NvbXBsZW1lbnRhdGlvbicpKSArCiAgeGxhYigiTWVkaWFuIEZpdG5lc3MgKExvZ0ZDKSIpICsKICB5bGFiKCJTZWxlY3Rpb24gQ29uZGl0aW9uICh1Zy9tTCBUTVApIikgKwogIGdndGl0bGUoIkRpc3RhbmNlIGZyb20gSG9tb2xvZyBcbk11dGF0aW9ucyA9IDMgYS5hLiIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKAogICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICdibGFjaycsIHNpemUgPSAxLjApLAogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMS4wKSwKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBoanVzdCA9IDAuNSksCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygtMTUsIDEwKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkNvZG9uMSIgPSAiIzAwNzJCMiIsICJDb2RvbjIiID0gIiNFNjlGMDAiKSwgbmFtZSA9ICJMaWJyYXJ5IikKCiMgRGlzcGxheSB0aGUgcGxvdAp0bXBfcmlkZ2VzXzE1XzE2XzAuM211dApgYGAKCmBgYHtyIGVjaG89RkFMU0V9Cmdnc2F2ZShmaWxlPSJNdXRhbnRzL1BMT1RTL0ZpdG5lc3MvTGliMTUuMTYuNUJDcy5tdXRhbnRzLmRvc2UucmVzcG9uc2UucmlkZ2VzLjMubXV0YW50cy5wbmciLAogICAgICAgcGxvdD10bXBfcmlkZ2VzXzE1XzE2XzAuM211dCwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gNiwgaGVpZ2h0ID0gOCwgdW5pdHMgPSAiaW4iKQpgYGAKCiMjIyBIb21vbG9ncyB3LyBNdXRzICg0IGEuYS4gbXV0YXRpb24pCmBgYHtyfQojIExpYjE1Cm11dF9jb2xsYXBzZV8xNV9zdWJzZXRfMC40bXV0IDwtIG11dF9jb2xsYXBzZV8xNV9zdWJzZXQgJT4lCiAgI2ZpbHRlcihtdXRhdGlvbnMgJWluJSBjKDAsIDEsIDIsIDMsIDQpKSAlPiUKICBmaWx0ZXIobXV0YXRpb25zID09IDQpICU+JQogIHNlbGVjdChJRCwgZml0RDA1RDAzLCBmaXREMDZEMDMsIGZpdEQwN0QwMywgZml0RDA4RDAzLCBmaXREMDlEMDMsIGZpdEQxMEQwMywgZml0RDExRDAzKSAlPiUKICBwaXZvdF9sb25nZXIoIUlELCBuYW1lc190byA9ICJmYyIsIHZhbHVlc190byA9ICJ2YWwiKSAlPiUKICBtdXRhdGUoVE1QID0gY2FzZV93aGVuKAogICAgZmMgPT0gImZpdEQwNUQwMyIgfiAiMC1UTVAiLAogICAgZmMgPT0gImZpdEQwNkQwMyIgfiAiMC4wNTgtVE1QIiwKICAgIGZjID09ICJmaXREMDdEMDMiIH4gIjAuNS1UTVAiLAogICAgZmMgPT0gImZpdEQwOEQwMyIgfiAiMS4wLVRNUCIsCiAgICBmYyA9PSAiZml0RDA5RDAzIiB+ICIxMC1UTVAiLAogICAgZmMgPT0gImZpdEQxMEQwMyIgfiAiNTAtVE1QIiwKICAgIGZjID09ICJmaXREMTFEMDMiIH4gIjIwMC1UTVAiLAogICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8pKQoKIyBMaWIxNgptdXRfY29sbGFwc2VfMTZfc3Vic2V0XzAuNG11dCA8LSBtdXRfY29sbGFwc2VfMTZfc3Vic2V0ICU+JQogICNmaWx0ZXIobXV0YXRpb25zICVpbiUgYygwLCAxLCAyLCAzLCA0KSkgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyA9PSA0KSAlPiUKICBzZWxlY3QoSUQsIGZpdEQxMkQwNCwgZml0RTAxRDA0LCBmaXRFMDJEMDQsIGZpdEUwM0QwNCwgZml0RTA0RDA0LCBmaXRFMDVEMDQsIGZpdEUwNkQwNCkgJT4lCiAgcGl2b3RfbG9uZ2VyKCFJRCwgbmFtZXNfdG8gPSAiZmMiLCB2YWx1ZXNfdG8gPSAidmFsIikgJT4lCiAgbXV0YXRlKFRNUCA9IGNhc2Vfd2hlbigKICAgIGZjID09ICJmaXREMTJEMDQiIH4gIjAtVE1QIiwKICAgIGZjID09ICJmaXRFMDFEMDQiIH4gIjAuMDU4LVRNUCIsCiAgICBmYyA9PSAiZml0RTAyRDA0IiB+ICIwLjUtVE1QIiwKICAgIGZjID09ICJmaXRFMDNEMDQiIH4gIjEuMC1UTVAiLAogICAgZmMgPT0gImZpdEUwNEQwNCIgfiAiMTAtVE1QIiwKICAgIGZjID09ICJmaXRFMDVEMDQiIH4gIjUwLVRNUCIsCiAgICBmYyA9PSAiZml0RTA2RDA0IiB+ICIyMDAtVE1QIiwKICAgIFRSVUUgfiBOQV9jaGFyYWN0ZXJfKSkKYGBgCgpgYGB7cn0KIyBDb21iaW5lIHRoZSB0d28gZGF0YSBmcmFtZXMKbXV0X2NvbGxhcHNlXzE1XzE2XzVCQ3NfMC40bXV0IDwtIGJpbmRfcm93cygKICBtdXRfY29sbGFwc2VfMTVfc3Vic2V0XzAuNG11dCAlPiUgbXV0YXRlKExpYiA9ICJDb2RvbjEiKSwKICBtdXRfY29sbGFwc2VfMTZfc3Vic2V0XzAuNG11dCAlPiUgbXV0YXRlKExpYiA9ICJDb2RvbjIiKSwKICAuaWQgPSAiaWQiKQpgYGAKClBsb3QgUGVyZmVjdHMgZml0bmVzcyBzY29yZXMgYmFzZWQgb24gU3VwcGxlbWVudGF0aW9uIHRyZWF0bWVudCBmb3IgZmlyc3Qgc2FtcGxpbmcgdGltZSBwb2ludApgYGB7cn0KbXV0X2NvbGxhcHNlXzE1XzE2XzVCQ3NfMC40bXV0X29yZGVyIDwtIGMoIjIwMC1UTVAiLCAiNTAtVE1QIiwgIjEwLVRNUCIsICIxLjAtVE1QIiwgIjAuNS1UTVAiLCAiMC4wNTgtVE1QIiwgIjAtVE1QIikKCnRtcF9yaWRnZXNfMTVfMTZfMC40bXV0IDwtIGdncGxvdChtdXRfY29sbGFwc2VfMTVfMTZfNUJDc18wLjRtdXQsIGFlcyh4ID0gdmFsLCB5ID0gZmFjdG9yKFRNUCwgbGV2ZWwgPSBtdXRfY29sbGFwc2VfMTVfMTZfNUJDc18wLjRtdXRfb3JkZXIpLCBmaWxsID0gTGliKSkgKwogIGdlb21fZGVuc2l0eV9yaWRnZXMoYWxwaGEgPSAwLjcpICsKICBzY2FsZV95X2Rpc2NyZXRlKGxhYmVscyA9IGMoJzIwMCDOvGcvbUwgVE1QJywgJzUwIM68Zy9tTCBUTVAnLCAnMTAgzrxnL21MIFRNUCcsICcxIM68Zy9tTCBUTVAnLCAnMC41IM68Zy9tTCBUTVAnLCAnMC4wNTggzrxnL21MIFRNUCcsICdDb21wbGVtZW50YXRpb24nKSkgKwogIHhsYWIoIk1lZGlhbiBGaXRuZXNzIChMb2dGQykiKSArCiAgeWxhYigiU2VsZWN0aW9uIENvbmRpdGlvbiAodWcvbUwgVE1QKSIpICsKICBnZ3RpdGxlKCJEaXN0YW5jZSBmcm9tIEhvbW9sb2cgXG5NdXRhdGlvbnMgPSA0IGEuYS4iKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZSgKICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMS4wKSwKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDEuMCksCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUpLAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoLTE1LCAxMCkpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJDb2RvbjEiID0gIiMwMDcyQjIiLCAiQ29kb24yIiA9ICIjRTY5RjAwIiksIG5hbWUgPSAiTGlicmFyeSIpCgojIERpc3BsYXkgdGhlIHBsb3QKdG1wX3JpZGdlc18xNV8xNl8wLjRtdXQKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpnZ3NhdmUoZmlsZT0iTXV0YW50cy9QTE9UUy9GaXRuZXNzL0xpYjE1LjE2LjVCQ3MubXV0YW50cy5kb3NlLnJlc3BvbnNlLnJpZGdlcy40Lm11dGFudHMucG5nIiwKICAgICAgIHBsb3Q9dG1wX3JpZGdlc18xNV8xNl8wLjRtdXQsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDYsIGhlaWdodCA9IDgsIHVuaXRzID0gImluIikKYGBgCgojIyMgSG9tb2xvZ3Mgdy8gTXV0cyAoNSBhLmEuIG11dGF0aW9ucykKYGBge3J9CiMgTGliMTUKbXV0X2NvbGxhcHNlXzE1X3N1YnNldF8wLjVtdXQgPC0gbXV0X2NvbGxhcHNlXzE1X3N1YnNldCAlPiUKICAjZmlsdGVyKG11dGF0aW9ucyAlaW4lIGMoMCwgMSwgMiwgMywgNCwgNSkpICU+JQogIGZpbHRlcihtdXRhdGlvbnMgPT0gNSkgJT4lCiAgc2VsZWN0KElELCBmaXREMDVEMDMsIGZpdEQwNkQwMywgZml0RDA3RDAzLCBmaXREMDhEMDMsIGZpdEQwOUQwMywgZml0RDEwRDAzLCBmaXREMTFEMDMpICU+JQogIHBpdm90X2xvbmdlcighSUQsIG5hbWVzX3RvID0gImZjIiwgdmFsdWVzX3RvID0gInZhbCIpICU+JQogIG11dGF0ZShUTVAgPSBjYXNlX3doZW4oCiAgICBmYyA9PSAiZml0RDA1RDAzIiB+ICIwLVRNUCIsCiAgICBmYyA9PSAiZml0RDA2RDAzIiB+ICIwLjA1OC1UTVAiLAogICAgZmMgPT0gImZpdEQwN0QwMyIgfiAiMC41LVRNUCIsCiAgICBmYyA9PSAiZml0RDA4RDAzIiB+ICIxLjAtVE1QIiwKICAgIGZjID09ICJmaXREMDlEMDMiIH4gIjEwLVRNUCIsCiAgICBmYyA9PSAiZml0RDEwRDAzIiB+ICI1MC1UTVAiLAogICAgZmMgPT0gImZpdEQxMUQwMyIgfiAiMjAwLVRNUCIsCiAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXykpCgojIExpYjE2Cm11dF9jb2xsYXBzZV8xNl9zdWJzZXRfMC41bXV0IDwtIG11dF9jb2xsYXBzZV8xNl9zdWJzZXQgJT4lCiAgI2ZpbHRlcihtdXRhdGlvbnMgJWluJSBjKDAsIDEsIDIsIDMsIDQsIDUpKSAlPiUKICBmaWx0ZXIobXV0YXRpb25zID09IDUpICU+JQogIHNlbGVjdChJRCwgZml0RDEyRDA0LCBmaXRFMDFEMDQsIGZpdEUwMkQwNCwgZml0RTAzRDA0LCBmaXRFMDREMDQsIGZpdEUwNUQwNCwgZml0RTA2RDA0KSAlPiUKICBwaXZvdF9sb25nZXIoIUlELCBuYW1lc190byA9ICJmYyIsIHZhbHVlc190byA9ICJ2YWwiKSAlPiUKICBtdXRhdGUoVE1QID0gY2FzZV93aGVuKAogICAgZmMgPT0gImZpdEQxMkQwNCIgfiAiMC1UTVAiLAogICAgZmMgPT0gImZpdEUwMUQwNCIgfiAiMC4wNTgtVE1QIiwKICAgIGZjID09ICJmaXRFMDJEMDQiIH4gIjAuNS1UTVAiLAogICAgZmMgPT0gImZpdEUwM0QwNCIgfiAiMS4wLVRNUCIsCiAgICBmYyA9PSAiZml0RTA0RDA0IiB+ICIxMC1UTVAiLAogICAgZmMgPT0gImZpdEUwNUQwNCIgfiAiNTAtVE1QIiwKICAgIGZjID09ICJmaXRFMDZEMDQiIH4gIjIwMC1UTVAiLAogICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8pKQpgYGAKCmBgYHtyfQojIENvbWJpbmUgdGhlIHR3byBkYXRhIGZyYW1lcwptdXRfY29sbGFwc2VfMTVfMTZfNUJDc18wLjVtdXQgPC0gYmluZF9yb3dzKAogIG11dF9jb2xsYXBzZV8xNV9zdWJzZXRfMC41bXV0ICU+JSBtdXRhdGUoTGliID0gIkNvZG9uMSIpLAogIG11dF9jb2xsYXBzZV8xNl9zdWJzZXRfMC41bXV0ICU+JSBtdXRhdGUoTGliID0gIkNvZG9uMiIpLAogIC5pZCA9ICJpZCIpCmBgYAoKUGxvdCBQZXJmZWN0cyBmaXRuZXNzIHNjb3JlcyBiYXNlZCBvbiBTdXBwbGVtZW50YXRpb24gdHJlYXRtZW50IGZvciBmaXJzdCBzYW1wbGluZyB0aW1lIHBvaW50CmBgYHtyfQptdXRfY29sbGFwc2VfMTVfMTZfNUJDc18wLjVtdXRfb3JkZXIgPC0gYygiMjAwLVRNUCIsICI1MC1UTVAiLCAiMTAtVE1QIiwgIjEuMC1UTVAiLCAiMC41LVRNUCIsICIwLjA1OC1UTVAiLCAiMC1UTVAiKQoKdG1wX3JpZGdlc18xNV8xNl8wLjVtdXQgPC0gZ2dwbG90KG11dF9jb2xsYXBzZV8xNV8xNl81QkNzXzAuNW11dCwgYWVzKHggPSB2YWwsIHkgPSBmYWN0b3IoVE1QLCBsZXZlbCA9IG11dF9jb2xsYXBzZV8xNV8xNl81QkNzXzAuNW11dF9vcmRlciksIGZpbGwgPSBMaWIpKSArCiAgZ2VvbV9kZW5zaXR5X3JpZGdlcyhhbHBoYSA9IDAuNykgKwogIHNjYWxlX3lfZGlzY3JldGUobGFiZWxzID0gYygnMjAwIM68Zy9tTCBUTVAnLCAnNTAgzrxnL21MIFRNUCcsICcxMCDOvGcvbUwgVE1QJywgJzEgzrxnL21MIFRNUCcsICcwLjUgzrxnL21MIFRNUCcsICcwLjA1OCDOvGcvbUwgVE1QJywgJ0NvbXBsZW1lbnRhdGlvbicpKSArCiAgeGxhYigiTWVkaWFuIEZpdG5lc3MgKExvZ0ZDKSIpICsKICB5bGFiKCJTZWxlY3Rpb24gQ29uZGl0aW9uICh1Zy9tTCBUTVApIikgKwogIGdndGl0bGUoIkRpc3RhbmNlIGZyb20gSG9tb2xvZyBcbk11dGF0aW9ucyA9IDUgYS5hLiIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKAogICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICdibGFjaycsIHNpemUgPSAxLjApLAogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMS4wKSwKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBoanVzdCA9IDAuNSksCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygtMTUsIDEwKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkNvZG9uMSIgPSAiIzAwNzJCMiIsICJDb2RvbjIiID0gIiNFNjlGMDAiKSwgbmFtZSA9ICJMaWJyYXJ5IikKCiMgRGlzcGxheSB0aGUgcGxvdAp0bXBfcmlkZ2VzXzE1XzE2XzAuNW11dApgYGAKCmBgYHtyIGVjaG89RkFMU0V9Cmdnc2F2ZShmaWxlPSJNdXRhbnRzL1BMT1RTL0ZpdG5lc3MvTGliMTUuMTYuNUJDcy5tdXRhbnRzLmRvc2UucmVzcG9uc2UucmlkZ2VzLjUubXV0YW50cy5wbmciLAogICAgICAgcGxvdD10bXBfcmlkZ2VzXzE1XzE2XzAuNW11dCwKICAgICAgIGRwaT02MDAsIHdpZHRoID0gNiwgaGVpZ2h0ID0gOCwgdW5pdHMgPSAiaW4iKQpgYGAKCmBgYHtyfQpwYXRjaDcgPC0gdG1wX3JpZGdlc18xNV8xNl8wbXV0IHwgdG1wX3JpZGdlc18xNV8xNl8wLjFtdXQgfCB0bXBfcmlkZ2VzXzE1XzE2XzAuNW11dApwYXRjaDcKYGBgCgpgYGB7ciBlY2hvID0gRkFMU0V9Cmdnc2F2ZShmaWxlPSJNdXRhbnRzL1BMT1RTL0ZpdG5lc3MvTGliMTUuMTYuNUJDcy5tdXRhbnRzLmRvc2UucmVzcG9uc2UucmlkZ2VzLmNvbWJpbmVkLnBuZyIsIAogICAgICAgcGxvdD1wYXRjaDcsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA4LCB1bml0cyA9ICJpbiIpCmBgYAoKIyMgTXV0YW50IEZpdG5lc3MgR2FpbnMKCiMjIyBMaWIxNQoKIyMjIyA1IGEuYS4gRGlzdGFuY2UgT25seQoKVGVzdCB0aGUgc2lnbmlmaWNhbmNlIG9mIGZpdG5lc3MgY2hhbmdlcyBiZXR3ZWVuIHBlcmZlY3QgYXNzZW1ibGllcyAobXV0YXRpb25zID0gMCkgYW5kIGFzc29jaWF0ZWQgbXV0YW50cyB1cCB0byA1IGEuYS4gZGlzdGFuY2UgZm9yIGVhY2ggVE1QIHRyZWF0bWVudCB3aXRoaW4gYm90aCBsaWJyYXJpZXMuIFRoZSBmb2xsb3dpbmcgY29kZSBhcHBsaWVkIHRvIExpYjE1IChDb2RvbiAxKSB0ZXN0aW5nIGZpdG5lc3MgZGlmZmVyZW5jZXMgYWNyb3NzIG11dGF0aW9ucyBhdCB0aGUgMjAwLVRNUCAoNDAweCBNSUMpIHRyZWF0bWVudC4KYGBge3J9CiMgU3RlcCAxOiBQcmVwYXJlIHRoZSBkYXRhCkxpYjE1Lm11dC41YWEuZGlmZmVyZW5jZXMgPC0gbXV0X2NvbGxhcHNlXzE1X3N1YnNldCAlPiUKICBmaWx0ZXIobXV0YXRpb25zICVpbiUgYygwLCA1KSkgJT4lCiAgZ3JvdXBfYnkoSUQsIG11dGF0aW9ucykgJT4lCiAgc3VtbWFyaXNlKGZpdEQxMUQwMyA9IG1lYW4oZml0RDExRDAzLCBuYS5ybSA9IFRSVUUpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gbXV0YXRpb25zLCB2YWx1ZXNfZnJvbSA9IGZpdEQxMUQwMywgbmFtZXNfcHJlZml4ID0gIm11dF8iKSAlPiUKICBmaWx0ZXIoIWlzLm5hKG11dF8wKSAmICFpcy5uYShtdXRfNSkpICU+JQogIG11dGF0ZShkaWZmZXJlbmNlID0gbXV0XzUgLSBtdXRfMCkKCiMgU3RlcCAyOiBQbG90IHRoZSBkaXN0cmlidXRpb24KTGliMTUubXV0LjIwMC50bXAuNWFhLnBsb3QgPC0gZ2dwbG90KExpYjE1Lm11dC41YWEuZGlmZmVyZW5jZXMsIGFlcyh4ID0gZGlmZmVyZW5jZSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMSwgZmlsbCA9ICJza3libHVlIiwgY29sb3IgPSAiYmxhY2siKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oZGlmZmVyZW5jZSwgbmEucm0gPSBUUlVFKSksIAogICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBEaWZmZXJlbmNlcyBpbiBmaXREMTFEMDMiLAogICAgICAgc3VidGl0bGUgPSAiNSBhLmEuIGRpc3RhbmNlIChtdXRhbnRzKSBtaW51cyAwIGEuYS4gZGlzdGFuY2UgKHBlcmZlY3RzKSIsCiAgICAgICB4ID0gIkRpZmZlcmVuY2UiLAogICAgICAgeSA9ICJDb3VudCIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogICAgdGhlbWUoCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2JsYWNrJywgc2l6ZSA9IDAuNSksCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpLAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41KSwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKDAsMiksCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgwLCAyLCBieSA9IDEpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoLTQsMTIpLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoLTQsIDEyLCBieSA9IDIpKQoKcHJpbnQoTGliMTUubXV0LjIwMC50bXAuNWFhLnBsb3QpCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KIyBTYXZlIHRoZSBwbG90Cmdnc2F2ZSgiTXV0YW50cy9QTE9UUy9maXREMTFEMDNfZGlmZmVyZW5jZXNfaGlzdG9ncmFtXzV2czAucG5nIiwgCiAgICAgICBwbG90ID0gTGliMTUubXV0LjIwMC50bXAuNWFhLnBsb3QsIAogICAgICAgd2lkdGggPSA1LCBoZWlnaHQgPSA0KQpgYGAKCkFkZCBOQ0JJIHRheG9ub215IHRvIGVhY2ggaG9tb2xvZyAiSUQiIGluIHRoZSAiTGliMTUubXV0LjVhYS5kaWZmZXJlbmNlcyIgZGF0YXNldDoKYGBge3J9CkxpYjE1Lm11dC41YWEuZGlmZmVyZW5jZXMuY29sdW1ucyA8LSBjKCJJRCIsICJQY3RJZGVudEVjb2xpIiwgIlRheElEIiwgIk5DQkkubmFtZSIsICJOQ0JJLnN1cGVya2luZ2RvbSIsICJOQ0JJLnBoeWx1bSIsICJOQ0JJLmNsYXNzIiwgIk5DQkkub3JkZXIiLCAiTkNCSS5mYW1pbHkiLCAiTkNCSS5nZW51cyIsICJOQ0JJLnNwZWNpZXMiKQoKTGliMTUubXV0LjVhYS5kaWZmZXJlbmNlc19tZXJnZWQgPC0gTGliMTUubXV0LjVhYS5kaWZmZXJlbmNlcyAlPiUKICBsZWZ0X2pvaW4oQWxsdHJlZTE1X3RheGFfbWVyZ2VkICU+JSBzZWxlY3QoYWxsX29mKExpYjE1Lm11dC41YWEuZGlmZmVyZW5jZXMuY29sdW1ucykpLCBieSA9ICJJRCIpCgojIFZpZXcgdGhlIG1lcmdlZCBkYXRhZnJhbWUKcHJpbnQoTGliMTUubXV0LjVhYS5kaWZmZXJlbmNlc19tZXJnZWQpCgojIFNhdmUgdGhlIExpYjE1Lm11dC41YWEuZGlmZmVyZW5jZXMgZGF0YSBmcmFtZQp3cml0ZS5jc3YoTGliMTUubXV0LjVhYS5kaWZmZXJlbmNlc19tZXJnZWQsIAogICAgICAgICAgIk11dGFudHMvT1VUUFVUL211dDE1LjAuNS5kaWZmZXJlbmNlcy4yMDB0bXAuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCmBgYAoKIyMjIyBBbGwgYS5hLiBEaXN0YW5jZSAoMS01KQoKU3VtbWFyaXplIHRoZSBlZmZlY3RzIG9mIG11dGF0aW9uYWwgY2hhbmdlcyBhdCA1IGEuYS4gZGlzdGFuY2UgZnJvbSByZWNvdmVyZWQgcGVyZmVjdCBob21vbG9nczoKYGBge3J9CiMgU3RlcCAxOiBQcmVwYXJlIHRoZSBkYXRhIChzYW1lIGFzIGJlZm9yZSkKTGliMTUubXV0LjVhYS5zdW1tYXJ5LmRpZmZlcmVuY2VzIDwtIG11dF9jb2xsYXBzZV8xNV9zdWJzZXQgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyAlaW4lIGMoMCwgMSwgMiwgMywgNCwgNSkpICU+JQogIGdyb3VwX2J5KElELCBtdXRhdGlvbnMpICU+JQogIHN1bW1hcmlzZShmaXREMTFEMDMgPSBtZWFuKGZpdEQxMUQwMywgbmEucm0gPSBUUlVFKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IG11dGF0aW9ucywgdmFsdWVzX2Zyb20gPSBmaXREMTFEMDMsIG5hbWVzX3ByZWZpeCA9ICJtdXRfIikgJT4lCiAgZmlsdGVyKCFpcy5uYShtdXRfMCkpICU+JQogIG11dGF0ZSgKICAgIGRpZmZfMV8wID0gbXV0XzEgLSBtdXRfMCwKICAgIGRpZmZfMl8wID0gbXV0XzIgLSBtdXRfMCwKICAgIGRpZmZfM18wID0gbXV0XzMgLSBtdXRfMCwKICAgIGRpZmZfNF8wID0gbXV0XzQgLSBtdXRfMCwKICAgIGRpZmZfNV8wID0gbXV0XzUgLSBtdXRfMAogICkgJT4lCiAgc2VsZWN0KElELCBzdGFydHNfd2l0aCgiZGlmZl8iKSkKCiMgU3RlcCAyOiBSZXNoYXBlIHRoZSBkYXRhIGZvciBwbG90dGluZwpMaWIxNS5tdXQuNWFhLnN1bW1hcnkuZGlmZmVyZW5jZXNfbG9uZyA8LSBMaWIxNS5tdXQuNWFhLnN1bW1hcnkuZGlmZmVyZW5jZXMgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgiZGlmZl8iKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gImNvbXBhcmlzb24iLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gImRpZmZlcmVuY2UiKSAlPiUKICBtdXRhdGUobnVtX211dGF0aW9ucyA9IGFzLmludGVnZXIoc3Vic3RyKGNvbXBhcmlzb24sIDYsIDYpKSkKCiMgU3RlcCAzOiBQZXJmb3JtIHN0YXRpc3RpY2FsIHRlc3RzCkxpYjE1Lm11dC41YWEuc3VtbWFyeS5kaWZmZXJlbmNlcy5zdGF0X3Rlc3RzIDwtIExpYjE1Lm11dC41YWEuc3VtbWFyeS5kaWZmZXJlbmNlc19sb25nICU+JQogIGdyb3VwX2J5KGNvbXBhcmlzb24pICU+JQogIHN1bW1hcmlzZSgKICAgIG1lYW5fZGlmZiA9IG1lYW4oZGlmZmVyZW5jZSwgbmEucm0gPSBUUlVFKSwKICAgIHBfdmFsdWUgPSB0LnRlc3QoZGlmZmVyZW5jZSkkcC52YWx1ZSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApICU+JQogIG11dGF0ZShwX3ZhbHVlX2xhYmVsID0gaWZlbHNlKHBfdmFsdWUgPCAwLjAwMSwgInAgPCAwLjAwMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHBfdmFsdWUgPCAwLjAxLCAicCA8IDAuMDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UocF92YWx1ZSA8IDAuMDUsICJwIDwgMC4wNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgicCA9Iiwgcm91bmQocF92YWx1ZSwgMykpKSkpKQoKcHJpbnQoTGliMTUubXV0LjVhYS5zdW1tYXJ5LmRpZmZlcmVuY2VzLnN0YXRfdGVzdHMpCmBgYAoKTmV4dCwgd2UnbGwgdHJhY2sgdGhlIG11dGF0aW9uYWwgZml0bmVzcyBnYWlucyBhdCBlYWNoIGEuYS4gZGlzdGFuY2UgKGZyb20gMS01IGEuYS4pOgpgYGB7cn0KIyBTdGVwIDQ6IEhpc3RvZ3JhbSBwbG90IHdpdGggc3RhdGlzdGljYWwgdGVzdCByZXN1bHRzCkxpYjE1Lm11dC41YWEuc3VtbWFyeS5oaXN0b2dyYW1fcGxvdCA8LSBnZ3Bsb3QoTGliMTUubXV0LjVhYS5zdW1tYXJ5LmRpZmZlcmVuY2VzX2xvbmcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBkaWZmZXJlbmNlLCBmaWxsID0gY29tcGFyaXNvbikpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMSwgcG9zaXRpb24gPSAiaWRlbnRpdHkiLCBhbHBoYSA9IDAuNikgKwogIGdlb21fdmxpbmUoZGF0YSA9IExpYjE1Lm11dC41YWEuc3VtbWFyeS5kaWZmZXJlbmNlcy5zdGF0X3Rlc3RzLAogICAgICAgICAgICAgYWVzKHhpbnRlcmNlcHQgPSBtZWFuX2RpZmYsIGNvbG9yID0gY29tcGFyaXNvbiksCiAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdGV4dChkYXRhID0gTGliMTUubXV0LjVhYS5zdW1tYXJ5LmRpZmZlcmVuY2VzLnN0YXRfdGVzdHMsCiAgICAgICAgICAgIGFlcyh4ID0gSW5mLCB5ID0gSW5mLCBsYWJlbCA9IHBfdmFsdWVfbGFiZWwpLAogICAgICAgICAgICBoanVzdCA9IDEuMSwgdmp1c3QgPSAxLjEsIHNpemUgPSAzKSArCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgQ29kb24gMSBGaXRuZXNzIERpZmZlcmVuY2VzIGF0IDIwMCBUTVAiLAogICAgICAgc3VidGl0bGUgPSAiTXV0YXRpb25zIDEsIDIsIDMsIDQsIDUgbWludXMgTXV0YXRpb24gMCIsCiAgICAgICB4ID0gIkRpZmZlcmVuY2UiLAogICAgICAgeSA9ICJDb3VudCIpICsKICBmYWNldF93cmFwKH5jb21wYXJpc29uLCBzY2FsZXMgPSAiZnJlZV95IiwgbnJvdyA9IDEpICsKICB0aGVtZV9taW5pbWFsKCkgKwogICAgdGhlbWUoCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2JsYWNrJywgc2l6ZSA9IDAuNSksCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpLAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41KSwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKTGliMTUubXV0LjVhYS5zdW1tYXJ5Lmhpc3RvZ3JhbV9wbG90CgojIFN0ZXAgNDogTGluZSBwbG90IG9mIGRpc3RyaWJ1dGlvbiB2cyBudW1iZXIgb2YgbXV0YXRpb25zCkxpYjE1Lm11dC41YWEuc3VtbWFyeS5saW5lX3Bsb3QgPC0gZ2dwbG90KExpYjE1Lm11dC41YWEuc3VtbWFyeS5kaWZmZXJlbmNlc19sb25nLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBudW1fbXV0YXRpb25zLCB5ID0gZGlmZmVyZW5jZSkpICsKICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuMSwgd2lkdGggPSAwLjIpICsKICBnZW9tX2JveHBsb3QoYWVzKGdyb3VwID0gbnVtX211dGF0aW9ucyksIGFscGhhID0gMC41LCBvdXRsaWVyLnNoYXBlID0gTkEpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBzZSA9IFRSVUUsIGNvbG9yID0gInJlZCIpICsKICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBEaWZmZXJlbmNlcyB2cyBOdW1iZXIgb2YgTXV0YXRpb25zIiwKICAgICAgIHggPSAiTnVtYmVyIG9mIE11dGF0aW9ucyIsCiAgICAgICB5ID0gIkZpdG5lc3MgRGlmZmVyZW5jZSBhdCAyMDAgVE1QIFxuKENvZG9uIDEpIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgICB0aGVtZSgKICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAnYmxhY2snLCBzaXplID0gMC41KSwKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUpLAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKQoKTGliMTUubXV0LjVhYS5zdW1tYXJ5LmxpbmVfcGxvdApgYGAKCmBgYHtyfQpMaWIxNS5tdXQuNWFhLnN1bW1hcnkuZGlmZmVyZW5jZXMuY29sdW1ucyA8LSBjKCJJRCIsICJQY3RJZGVudEVjb2xpIiwgIlRheElEIiwgIk5DQkkubmFtZSIsICJOQ0JJLnN1cGVya2luZ2RvbSIsICJOQ0JJLnBoeWx1bSIsICJOQ0JJLmNsYXNzIiwgIk5DQkkub3JkZXIiLCAiTkNCSS5mYW1pbHkiLCAiTkNCSS5nZW51cyIsICJOQ0JJLnNwZWNpZXMiKQoKTGliMTUubXV0LjVhYS5zdW1tYXJ5LmRpZmZlcmVuY2VzX21lcmdlZCA8LSBMaWIxNS5tdXQuNWFhLnN1bW1hcnkuZGlmZmVyZW5jZXMgJT4lCiAgbGVmdF9qb2luKEFsbHRyZWUxNV90YXhhX21lcmdlZCAlPiUgc2VsZWN0KGFsbF9vZihMaWIxNS5tdXQuNWFhLnN1bW1hcnkuZGlmZmVyZW5jZXMuY29sdW1ucykpLCBieSA9ICJJRCIpCgojIFZpZXcgdGhlIG1lcmdlZCBkYXRhZnJhbWUKcHJpbnQoTGliMTUubXV0LjVhYS5zdW1tYXJ5LmRpZmZlcmVuY2VzX21lcmdlZCkKCiMgU2F2ZSB0aGUgTGliMTUubXV0LjVhYS5kaWZmZXJlbmNlcyBkYXRhIGZyYW1lCndyaXRlLmNzdihMaWIxNS5tdXQuNWFhLnN1bW1hcnkuZGlmZmVyZW5jZXNfbWVyZ2VkLCAKICAgICAgICAgICJNdXRhbnRzL09VVFBVVC9tdXQxNS4wLjEuMi4zLjQuNS5kaWZmZXJlbmNlcy4yMDB0bXAuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCmBgYAoKYGBge3J9CnBhdGNoOCA8LSBMaWIxNS5tdXQuNWFhLnN1bW1hcnkuaGlzdG9ncmFtX3Bsb3QgLyBMaWIxNS5tdXQuNWFhLnN1bW1hcnkubGluZV9wbG90CnBhdGNoOApgYGAKCmBgYHtyIGVjaG89RkFMU0V9CiMgU2F2ZSB0aGUgcGxvdHMKZ2dzYXZlKCJNdXRhbnRzL1BMT1RTL2ZpdEQxMUQwM19kaWZmZXJlbmNlc19oaXN0b2dyYW1fMTIzNDV2czAucG5nIiwgCiAgICAgICBwbG90ID0gTGliMTUubXV0LjVhYS5zdW1tYXJ5Lmhpc3RvZ3JhbV9wbG90LCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA4KQpnZ3NhdmUoIk11dGFudHMvUExPVFMvZml0RDExRDAzX2RpZmZlcmVuY2VzX3ZzX211dGF0aW9uc19saW5lLnBuZyIsIAogICAgICAgcGxvdCA9IExpYjE1Lm11dC41YWEuc3VtbWFyeS5saW5lX3Bsb3QsIHdpZHRoID0gMTAsIGhlaWdodCA9IDYpCmdnc2F2ZSgiTXV0YW50cy9QTE9UUy9pdEQxMUQwM19kaWZmZXJlbmNlc192c19tdXRhdGlvbnNfaGlzdG9ncmFtLmxpbmVwbG90LnBuZyIsIAogICAgICAgcGxvdCA9IHBhdGNoOCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gMTApCmBgYAoKIyMjIExpYjE2CgojIyMjIDUgYS5hLiBEaXN0YW5jZSBPbmx5CgpUZXN0IHRoZSBzaWduaWZpY2FuY2Ugb2YgZml0bmVzcyBjaGFuZ2VzIGJldHdlZW4gcGVyZmVjdCBhc3NlbWJsaWVzIChtdXRhdGlvbnMgPSAwKSBhbmQgYXNzb2NpYXRlZCBtdXRhbnRzIHVwIHRvIDUgYS5hLiBkaXN0YW5jZSBmb3IgZWFjaCBUTVAgdHJlYXRtZW50IHdpdGhpbiBib3RoIGxpYnJhcmllcy4gVGhlIGZvbGxvd2luZyBjb2RlIGFwcGxpZWQgdG8gTGliMTYgKENvZG9uIDIpIHRlc3RpbmcgZml0bmVzcyBkaWZmZXJlbmNlcyBhY3Jvc3MgbXV0YXRpb25zIGF0IHRoZSAyMDAtVE1QICg0MDB4IE1JQykgdHJlYXRtZW50LgpgYGB7cn0KIyBTdGVwIDE6IFByZXBhcmUgdGhlIGRhdGEKTGliMTYubXV0LjVhYS5kaWZmZXJlbmNlcyA8LSBtdXRfY29sbGFwc2VfMTZfc3Vic2V0ICU+JQogIGZpbHRlcihtdXRhdGlvbnMgJWluJSBjKDAsIDUpKSAlPiUKICBncm91cF9ieShJRCwgbXV0YXRpb25zKSAlPiUKICBzdW1tYXJpc2UoZml0RTA2RDA0ID0gbWVhbihmaXRFMDZEMDQsIG5hLnJtID0gVFJVRSksIC5ncm91cHMgPSAiZHJvcCIpICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBtdXRhdGlvbnMsIHZhbHVlc19mcm9tID0gZml0RTA2RDA0LCBuYW1lc19wcmVmaXggPSAibXV0XyIpICU+JQogIGZpbHRlcighaXMubmEobXV0XzApICYgIWlzLm5hKG11dF81KSkgJT4lCiAgbXV0YXRlKGRpZmZlcmVuY2UgPSBtdXRfNSAtIG11dF8wKQoKIyBTdGVwIDI6IFBsb3QgdGhlIGRpc3RyaWJ1dGlvbgpMaWIxNi5tdXQuMjAwLnRtcC41YWEucGxvdCA8LSBnZ3Bsb3QoTGliMTYubXV0LjVhYS5kaWZmZXJlbmNlcywgYWVzKHggPSBkaWZmZXJlbmNlKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4xLCBmaWxsID0gInNreWJsdWUiLCBjb2xvciA9ICJibGFjayIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihkaWZmZXJlbmNlLCBuYS5ybSA9IFRSVUUpKSwgCiAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnModGl0bGUgPSAiQ29kb24gMiBEaXN0cmlidXRpb24gb2YgRGlmZmVyZW5jZXMgYXQgNDAweCBNSUMiLAogICAgICAgc3VidGl0bGUgPSAiNSBhLmEuIGRpc3RhbmNlIChtdXRhbnRzKSBtaW51cyAwIGEuYS4gZGlzdGFuY2UgKHBlcmZlY3RzKSIsCiAgICAgICB4ID0gIkRpZmZlcmVuY2UiLAogICAgICAgeSA9ICJDb3VudCIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogICAgdGhlbWUoCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2JsYWNrJywgc2l6ZSA9IDAuNSksCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpLAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41KSwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKDAsNCksCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgwLCA0LCBieSA9IDEpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoLTgsMTQpLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoLTgsIDE0LCBieSA9IDIpKQoKcHJpbnQoTGliMTYubXV0LjIwMC50bXAuNWFhLnBsb3QpCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KIyBTYXZlIHRoZSBwbG90Cmdnc2F2ZSgiTXV0YW50cy9QTE9UUy9maXRFMDZEMDRfZGlmZmVyZW5jZXNfaGlzdG9ncmFtXzV2czAucG5nIiwgCiAgICAgICBwbG90ID0gTGliMTYubXV0LjIwMC50bXAuNWFhLnBsb3QsIAogICAgICAgd2lkdGggPSA1LCBoZWlnaHQgPSA0KQpgYGAKCkFkZCBOQ0JJIHRheG9ub215IHRvIGVhY2ggaG9tb2xvZyAiSUQiIGluIHRoZSAiTGliMTYubXV0LjVhYS5kaWZmZXJlbmNlcyIgZGF0YXNldDoKYGBge3J9CkxpYjE2Lm11dC41YWEuZGlmZmVyZW5jZXMuY29sdW1ucyA8LSBjKCJJRCIsICJQY3RJZGVudEVjb2xpIiwgIlRheElEIiwgIk5DQkkubmFtZSIsICJOQ0JJLnN1cGVya2luZ2RvbSIsICJOQ0JJLnBoeWx1bSIsICJOQ0JJLmNsYXNzIiwgIk5DQkkub3JkZXIiLCAiTkNCSS5mYW1pbHkiLCAiTkNCSS5nZW51cyIsICJOQ0JJLnNwZWNpZXMiKQoKTGliMTYubXV0LjVhYS5kaWZmZXJlbmNlc19tZXJnZWQgPC0gTGliMTYubXV0LjVhYS5kaWZmZXJlbmNlcyAlPiUKICBsZWZ0X2pvaW4oQWxsdHJlZTE1X3RheGFfbWVyZ2VkICU+JSBzZWxlY3QoYWxsX29mKExpYjE2Lm11dC41YWEuZGlmZmVyZW5jZXMuY29sdW1ucykpLCBieSA9ICJJRCIpCgojIFZpZXcgdGhlIG1lcmdlZCBkYXRhZnJhbWUKcHJpbnQoTGliMTYubXV0LjVhYS5kaWZmZXJlbmNlc19tZXJnZWQpCgojIFNhdmUgdGhlIExpYjE2Lm11dC41YWEuZGlmZmVyZW5jZXMgZGF0YSBmcmFtZQp3cml0ZS5jc3YoTGliMTYubXV0LjVhYS5kaWZmZXJlbmNlc19tZXJnZWQsIAogICAgICAgICAgIk11dGFudHMvT1VUUFVUL211dDE2LjAuNS5kaWZmZXJlbmNlcy4yMDB0bXAuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCmBgYAoKIyMjIyBBbGwgYS5hLiBEaXN0YW5jZXMgKDEtNSkKClN1bW1hcml6ZSB0aGUgZWZmZWN0cyBvZiBtdXRhdGlvbmFsIGNoYW5nZXMgYXQgNSBhLmEuIGRpc3RhbmNlIGZyb20gcmVjb3ZlcmVkIHBlcmZlY3QgaG9tb2xvZ3M6CmBgYHtyfQojIFN0ZXAgMTogUHJlcGFyZSB0aGUgZGF0YSAoc2FtZSBhcyBiZWZvcmUpCkxpYjE2Lm11dC41YWEuc3VtbWFyeS5kaWZmZXJlbmNlcyA8LSBtdXRfY29sbGFwc2VfMTZfc3Vic2V0ICU+JQogIGZpbHRlcihtdXRhdGlvbnMgJWluJSBjKDAsIDEsIDIsIDMsIDQsIDUpKSAlPiUKICBncm91cF9ieShJRCwgbXV0YXRpb25zKSAlPiUKICBzdW1tYXJpc2UoZml0RTA2RDA0ID0gbWVhbihmaXRFMDZEMDQsIG5hLnJtID0gVFJVRSksIC5ncm91cHMgPSAiZHJvcCIpICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBtdXRhdGlvbnMsIHZhbHVlc19mcm9tID0gZml0RTA2RDA0LCBuYW1lc19wcmVmaXggPSAibXV0XyIpICU+JQogIGZpbHRlcighaXMubmEobXV0XzApKSAlPiUKICBtdXRhdGUoCiAgICBkaWZmXzFfMCA9IG11dF8xIC0gbXV0XzAsCiAgICBkaWZmXzJfMCA9IG11dF8yIC0gbXV0XzAsCiAgICBkaWZmXzNfMCA9IG11dF8zIC0gbXV0XzAsCiAgICBkaWZmXzRfMCA9IG11dF80IC0gbXV0XzAsCiAgICBkaWZmXzVfMCA9IG11dF81IC0gbXV0XzAKICApICU+JQogIHNlbGVjdChJRCwgc3RhcnRzX3dpdGgoImRpZmZfIikpCgojIFN0ZXAgMjogUmVzaGFwZSB0aGUgZGF0YSBmb3IgcGxvdHRpbmcKTGliMTYubXV0LjVhYS5zdW1tYXJ5LmRpZmZlcmVuY2VzX2xvbmcgPC0gTGliMTYubXV0LjVhYS5zdW1tYXJ5LmRpZmZlcmVuY2VzICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoImRpZmZfIiksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJjb21wYXJpc29uIiwgCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJkaWZmZXJlbmNlIikgJT4lCiAgbXV0YXRlKG51bV9tdXRhdGlvbnMgPSBhcy5pbnRlZ2VyKHN1YnN0cihjb21wYXJpc29uLCA2LCA2KSkpCgojIFN0ZXAgMzogUGVyZm9ybSBzdGF0aXN0aWNhbCB0ZXN0cwpMaWIxNi5tdXQuNWFhLnN1bW1hcnkuZGlmZmVyZW5jZXMuc3RhdF90ZXN0cyA8LSBMaWIxNi5tdXQuNWFhLnN1bW1hcnkuZGlmZmVyZW5jZXNfbG9uZyAlPiUKICBncm91cF9ieShjb21wYXJpc29uKSAlPiUKICBzdW1tYXJpc2UoCiAgICBtZWFuX2RpZmYgPSBtZWFuKGRpZmZlcmVuY2UsIG5hLnJtID0gVFJVRSksCiAgICBwX3ZhbHVlID0gdC50ZXN0KGRpZmZlcmVuY2UpJHAudmFsdWUsCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKSAlPiUKICBtdXRhdGUocF92YWx1ZV9sYWJlbCA9IGlmZWxzZShwX3ZhbHVlIDwgMC4wMDEsICJwIDwgMC4wMDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShwX3ZhbHVlIDwgMC4wMSwgInAgPCAwLjAxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHBfdmFsdWUgPCAwLjA1LCAicCA8IDAuMDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoInAgPSIsIHJvdW5kKHBfdmFsdWUsIDMpKSkpKSkKCnByaW50KExpYjE2Lm11dC41YWEuc3VtbWFyeS5kaWZmZXJlbmNlcy5zdGF0X3Rlc3RzKQpgYGAKCk5leHQsIHdlJ2xsIHRyYWNrIHRoZSBtdXRhdGlvbmFsIGZpdG5lc3MgZ2FpbnMgYXQgZWFjaCBhLmEuIGRpc3RhbmNlIChmcm9tIDEtNSBhLmEuKToKYGBge3J9CiMgU3RlcCA0OiBIaXN0b2dyYW0gcGxvdCB3aXRoIHN0YXRpc3RpY2FsIHRlc3QgcmVzdWx0cwpMaWIxNi5tdXQuNWFhLnN1bW1hcnkuaGlzdG9ncmFtX3Bsb3QgPC0gZ2dwbG90KExpYjE2Lm11dC41YWEuc3VtbWFyeS5kaWZmZXJlbmNlc19sb25nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gZGlmZmVyZW5jZSwgZmlsbCA9IGNvbXBhcmlzb24pKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjEsIHBvc2l0aW9uID0gImlkZW50aXR5IiwgYWxwaGEgPSAwLjYpICsKICBnZW9tX3ZsaW5lKGRhdGEgPSBMaWIxNi5tdXQuNWFhLnN1bW1hcnkuZGlmZmVyZW5jZXMuc3RhdF90ZXN0cywKICAgICAgICAgICAgIGFlcyh4aW50ZXJjZXB0ID0gbWVhbl9kaWZmLCBjb2xvciA9IGNvbXBhcmlzb24pLAogICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX3RleHQoZGF0YSA9IExpYjE2Lm11dC41YWEuc3VtbWFyeS5kaWZmZXJlbmNlcy5zdGF0X3Rlc3RzLAogICAgICAgICAgICBhZXMoeCA9IEluZiwgeSA9IEluZiwgbGFiZWwgPSBwX3ZhbHVlX2xhYmVsKSwKICAgICAgICAgICAgaGp1c3QgPSAxLjEsIHZqdXN0ID0gMS4xLCBzaXplID0gMykgKwogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIENvZG9uIDIgRml0bmVzcyBEaWZmZXJlbmNlcyBhdCAyMDAgVE1QIiwKICAgICAgIHN1YnRpdGxlID0gIk11dGF0aW9ucyAxLCAyLCAzLCA0LCA1IG1pbnVzIE11dGF0aW9uIDAiLAogICAgICAgeCA9ICJEaWZmZXJlbmNlIiwKICAgICAgIHkgPSAiQ291bnQiKSArCiAgZmFjZXRfd3JhcCh+Y29tcGFyaXNvbiwgc2NhbGVzID0gImZyZWVfeSIsIG5yb3cgPSAxKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICAgIHRoZW1lKAogICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICdibGFjaycsIHNpemUgPSAwLjUpLAogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBoanVzdCA9IDAuNSksCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDEiKSArCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCkxpYjE2Lm11dC41YWEuc3VtbWFyeS5oaXN0b2dyYW1fcGxvdAoKIyBTdGVwIDQ6IExpbmUgcGxvdCBvZiBkaXN0cmlidXRpb24gdnMgbnVtYmVyIG9mIG11dGF0aW9ucwpMaWIxNi5tdXQuNWFhLnN1bW1hcnkubGluZV9wbG90IDwtIGdncGxvdChMaWIxNi5tdXQuNWFhLnN1bW1hcnkuZGlmZmVyZW5jZXNfbG9uZywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gbnVtX211dGF0aW9ucywgeSA9IGRpZmZlcmVuY2UpKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjEsIHdpZHRoID0gMC4yKSArCiAgZ2VvbV9ib3hwbG90KGFlcyhncm91cCA9IG51bV9tdXRhdGlvbnMpLCBhbHBoYSA9IDAuNSwgb3V0bGllci5zaGFwZSA9IE5BKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIiwgc2UgPSBUUlVFLCBjb2xvciA9ICJyZWQiKSArCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgRGlmZmVyZW5jZXMgdnMgTnVtYmVyIG9mIE11dGF0aW9ucyIsCiAgICAgICB4ID0gIk51bWJlciBvZiBNdXRhdGlvbnMiLAogICAgICAgeSA9ICJGaXRuZXNzIERpZmZlcmVuY2UgYXQgMjAwIFRNUCBcbihDb2RvbiAyKSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogICAgdGhlbWUoCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2JsYWNrJywgc2l6ZSA9IDAuNSksCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpLAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41KSwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkKCkxpYjE2Lm11dC41YWEuc3VtbWFyeS5saW5lX3Bsb3QKYGBgCgpgYGB7cn0KTGliMTYubXV0LjVhYS5zdW1tYXJ5LmRpZmZlcmVuY2VzLmNvbHVtbnMgPC0gYygiSUQiLCAiUGN0SWRlbnRFY29saSIsICJUYXhJRCIsICJOQ0JJLm5hbWUiLCAiTkNCSS5zdXBlcmtpbmdkb20iLCAiTkNCSS5waHlsdW0iLCAiTkNCSS5jbGFzcyIsICJOQ0JJLm9yZGVyIiwgIk5DQkkuZmFtaWx5IiwgIk5DQkkuZ2VudXMiLCAiTkNCSS5zcGVjaWVzIikKCkxpYjE2Lm11dC41YWEuc3VtbWFyeS5kaWZmZXJlbmNlc19tZXJnZWQgPC0gTGliMTYubXV0LjVhYS5zdW1tYXJ5LmRpZmZlcmVuY2VzICU+JQogIGxlZnRfam9pbihBbGx0cmVlMTVfdGF4YV9tZXJnZWQgJT4lIHNlbGVjdChhbGxfb2YoTGliMTYubXV0LjVhYS5zdW1tYXJ5LmRpZmZlcmVuY2VzLmNvbHVtbnMpKSwgYnkgPSAiSUQiKQoKIyBWaWV3IHRoZSBtZXJnZWQgZGF0YWZyYW1lCnByaW50KExpYjE2Lm11dC41YWEuc3VtbWFyeS5kaWZmZXJlbmNlc19tZXJnZWQpCgojIFNhdmUgdGhlIExpYjE2Lm11dC41YWEuZGlmZmVyZW5jZXMgZGF0YSBmcmFtZQp3cml0ZS5jc3YoTGliMTYubXV0LjVhYS5zdW1tYXJ5LmRpZmZlcmVuY2VzX21lcmdlZCwgCiAgICAgICAgICAiTXV0YW50cy9PVVRQVVQvbXV0MTYuMC4xLjIuMy40LjUuZGlmZmVyZW5jZXMuMjAwdG1wLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKCmBgYHtyfQpwYXRjaDkgPC0gTGliMTYubXV0LjVhYS5zdW1tYXJ5Lmhpc3RvZ3JhbV9wbG90IC8gTGliMTYubXV0LjVhYS5zdW1tYXJ5LmxpbmVfcGxvdApwYXRjaDkKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIFNhdmUgdGhlIHBsb3RzCmdnc2F2ZSgiTXV0YW50cy9QTE9UUy9maXRFMDZEMDRfZGlmZmVyZW5jZXNfaGlzdG9ncmFtXzEyMzQ1dnMwLnBuZyIsIAogICAgICAgcGxvdCA9IExpYjE2Lm11dC41YWEuc3VtbWFyeS5oaXN0b2dyYW1fcGxvdCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gOCkKZ2dzYXZlKCJNdXRhbnRzL1BMT1RTL2ZpdEUwNkQwNF9kaWZmZXJlbmNlc192c19tdXRhdGlvbnNfbGluZS5wbmciLCAKICAgICAgIHBsb3QgPSBMaWIxNi5tdXQuNWFhLnN1bW1hcnkubGluZV9wbG90LCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA2KQpnZ3NhdmUoIk11dGFudHMvUExPVFMvZml0RTA2RDA0X2RpZmZlcmVuY2VzX3ZzX211dGF0aW9uc19oaXN0b2dyYW0ubGluZXBsb3QucG5nIiwgCiAgICAgICBwbG90ID0gcGF0Y2g5LCB3aWR0aCA9IDEwLCBoZWlnaHQgPSAxMCkKYGBgCgojIyMgQ29tYmluZWQgQ29kb24gUGxvdHMKClBsb3QgYm90aCBDb2RvbiB2ZXJzaW9ucyB0b2dldGhlciBpbiBzaW5nbGUgcGxvdDoKYGBge3J9CnBhdGNoMTAgPC0gTGliMTUubXV0LjVhYS5zdW1tYXJ5Lmhpc3RvZ3JhbV9wbG90IC8gTGliMTUubXV0LjVhYS5zdW1tYXJ5LmxpbmVfcGxvdCB8IExpYjE2Lm11dC41YWEuc3VtbWFyeS5oaXN0b2dyYW1fcGxvdCAvIExpYjE2Lm11dC41YWEuc3VtbWFyeS5saW5lX3Bsb3QKcGF0Y2gxMApgYGAKCmBgYHtyIGVjaG89RkFMU0V9CiMgU2F2ZSB0aGUgcGxvdApnZ3NhdmUoIk11dGFudHMvUExPVFMvQ29kb24xX0NvZG9uMl9kaWZmZXJlbmNlc192c19tdXRhdGlvbnNfaGlzdG9ncmFtLmxpbmVwbG90LnBuZyIsIAogICAgICAgcGxvdCA9IHBhdGNoMTAsIHdpZHRoID0gMTQsIGhlaWdodCA9IDEwKQpgYGAKCiMjIE11dGFudCBNZWRpYW4gRml0bmVzcwpUaGUgYG11dF9jb2xsYXBzZWAgZGF0YXNldHMgY29udGFpbiB1bmlxdWUgSURzIGFuZCBhbGwgYXNzb2NpYXRlZCBtdXRJRHMgdXAgdG8gNSBBLkEuIGRpc3RhbmNlLiBTdGFydCBieSBncm91cGluZyBtdXRJRHMgYnkgSUQgYW5kIGNhbGN1bGF0aW5nIHRoZSBtZWRpYW4gZml0bmVzcyB2YWx1ZSBmb3IgZWFjaCB1bmlxdWUgSUQgd2l0aGluIGVhY2ggVE1QIHRyZWF0bWVudC4gVGhlIG51bWJlciBvZiB1bmlxdWUgSURzIGZvciBlYWNoIGNvZG9uIHZlcnNpb24gc2hvdWxkIHN0aWxsIGJlOiAqKkNvZG9uIDEgPSA3OTcqKiBhbmQgKipDb2RvbiAyID0gNjY2Ki4gSG93ZXZlciwgdGhlIGZpdG5lc3MgdmFsdWVzIHNob3VsZCBiZSBkaWZmZXJlbnQgZnJvbSB0aG9zZSBjYWxjdWxhdGVkIGJhc2VkIG9ubHkgb24gcGVyZmVjdHMgKG11dGF0aW9ucyA9PSAwKS4gUGxvdCB0aGUgY2hhbmdlIGluIG1lZGlhbiBmaXRuZXNzIHZhbHVlIGJ5IFRNUCB0cmVhdG1lbnQuCgojIyMgQ2FsY3VsYXRlIE1lZGlhbiBGaXRuZXNzCgoqKkxpYjE1OioqIEdyb3VwIG11dElEcyBieSBJRCBhbmQgY2FsY3VsYXRlIHRoZSBtZWRpYW4gZml0bmVzcyB2YWx1ZSBmb3IgZWFjaCB1bmlxdWUgSUQgd2l0aGluIGVhY2ggVE1QIHRyZWF0bWVudC4KYGBge3J9CiNDYWxjdWxhdGUgbWVkaWFuIGZpdG5lc3MgZm9yIGVhY2ggaG9tb2xvZyBhbmQgYXNzb2NpYXRlZCBtdXRhbnRzIGFuZCBzdW0gdGhlIHRvdGFsIG51bWJlciBvZiBCQ3MgKG51bUJDcyBhbmQgbnVtcHJ1bmVkQkNzKQptdXRfY29sbGFwc2VfMTVpbmZvIDwtIG11dF9jb2xsYXBzZV8xNSAlPiUKICBncm91cF9ieShJRCkgJT4lCiAgc3VtbWFyaXNlKG1lZEQwNUQwMz1tZWRpYW4oZml0RDA1RDAzLCBuYS5ybT1UKSwKICAgICAgICAgICAgbWVkRDA2RDAzPW1lZGlhbihmaXREMDZEMDMsIG5hLnJtPVQpLAogICAgICAgICAgICBtZWREMDdEMDM9bWVkaWFuKGZpdEQwN0QwMywgbmEucm09VCksCiAgICAgICAgICAgIG1lZEQwOEQwMz1tZWRpYW4oZml0RDA4RDAzLCBuYS5ybT1UKSwKICAgICAgICAgICAgbWVkRDA5RDAzPW1lZGlhbihmaXREMDlEMDMsIG5hLnJtPVQpLAogICAgICAgICAgICBtZWREMTBEMDM9bWVkaWFuKGZpdEQxMEQwMywgbmEucm09VCksCiAgICAgICAgICAgIG1lZEQxMUQwMz1tZWRpYW4oZml0RDExRDAzLCBuYS5ybT1UKSwKICAgICAgICAgICAgdG90YWxudW1CQ3MuTDE1PXN1bShudW1CQ3MpLAogICAgICAgICAgICB0b3RhbG51bXBydW5lZEJDcy5MMTU9c3VtKG51bXBydW5lZEJDcykpCmBgYAoKQ291bnQgdGhlIG51bWJlciBvZiB1bmlxdWUgSURzIGFmdGVyIGNvbGxhcHNpbmcgbXV0YW50cyB1cCB0byA1IEEuQS4gZGlzdGFuY2U6CmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQpmb3JtYXQobGVuZ3RoKHVuaXF1ZShtdXRfY29sbGFwc2VfMTVpbmZvJElEKSksIGJpZy5tYXJrID0gIiwiKQpgYGAKCioqTGliMTY6KiogR3JvdXAgbXV0SURzIGJ5IElEIGFuZCBjYWxjdWxhdGUgdGhlIG1lZGlhbiBmaXRuZXNzIHZhbHVlIGZvciBlYWNoIHVuaXF1ZSBJRCB3aXRoaW4gZWFjaCBUTVAgdHJlYXRtZW50LgpgYGB7cn0KI0NhbGN1bGF0ZSBtZWRpYW4gZml0bmVzcyBmb3IgZWFjaCBob21vbG9nIGFuZCBhc3NvY2lhdGVkIG11dGFudHMgYW5kIHN1bSB0aGUgdG90YWwgbnVtYmVyIG9mIEJDcyAobnVtQkNzIGFuZCBudW1wcnVuZWRCQ3MpCm11dF9jb2xsYXBzZV8xNmluZm8gPC0gbXV0X2NvbGxhcHNlXzE2ICU+JQogIGdyb3VwX2J5KElEKSAlPiUKICBzdW1tYXJpc2UobWVkRDEyRDA0PW1lZGlhbihmaXREMTJEMDQsIG5hLnJtPVQpLAogICAgICAgICAgICBtZWRFMDFEMDQ9bWVkaWFuKGZpdEUwMUQwNCwgbmEucm09VCksCiAgICAgICAgICAgIG1lZEUwMkQwND1tZWRpYW4oZml0RTAyRDA0LCBuYS5ybT1UKSwKICAgICAgICAgICAgbWVkRTAzRDA0PW1lZGlhbihmaXRFMDNEMDQsIG5hLnJtPVQpLAogICAgICAgICAgICBtZWRFMDREMDQ9bWVkaWFuKGZpdEUwNEQwNCwgbmEucm09VCksCiAgICAgICAgICAgIG1lZEUwNUQwND1tZWRpYW4oZml0RTA1RDA0LCBuYS5ybT1UKSwKICAgICAgICAgICAgbWVkRTA2RDA0PW1lZGlhbihmaXRFMDZEMDQsIG5hLnJtPVQpLAogICAgICAgICAgICB0b3RhbG51bUJDcy5MMTY9c3VtKG51bUJDcyksCiAgICAgICAgICAgIHRvdGFsbnVtcHJ1bmVkQkNzLkwxNj1zdW0obnVtcHJ1bmVkQkNzKSkKYGBgCgpDb3VudCB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBJRHMgYWZ0ZXIgY29sbGFwc2luZyBtdXRhbnRzIHVwIHRvIDUgQS5BLiBkaXN0YW5jZToKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CmZvcm1hdChsZW5ndGgodW5pcXVlKG11dF9jb2xsYXBzZV8xNmluZm8kSUQpKSwgYmlnLm1hcmsgPSAiLCIpCmBgYAoKIyMjIFBsb3QgTWVkaWFuIFRNUCBGaXRuZXNzCkNvbWJpbmUgYm90aCBkYXRhc2V0cyBhbmQgYXNzaWduIGxhYmVscyBmb3IgdGhlIHBsb3Q6CmBgYHtyfQojIENvbWJpbmUgdGhlIGRhdGFmcmFtZXMKbXV0X2NvbGxhcHNlXzE1LjE2X2luZm9fY29tYmluZWRfZGYgPC0gZnVsbF9qb2luKG11dF9jb2xsYXBzZV8xNWluZm8sIG11dF9jb2xsYXBzZV8xNmluZm8sIGJ5ID0gIklEIikKCiMgQ3JlYXRlIGEgbWFwcGluZyBmb3IgdGhlIG5ldyBsYWJlbHMKbXV0X2NvbGxhcHNlXzE1LjE2X2luZm9fbGFiZWxfbWFwIDwtIGMoCiAgIm1lZEQwNUQwMyIgPSAiMCIsICJtZWREMDZEMDMiID0gIjAuMDU4IiwgIm1lZEQwN0QwMyIgPSAiMC41IiwgIm1lZEQwOEQwMyIgPSAiMSIsCiAgIm1lZEQwOUQwMyIgPSAiMTAiLCAibWVkRDEwRDAzIiA9ICI1MCIsICJtZWREMTFEMDMiID0gIjIwMCIsCiAgIm1lZEQxMkQwNCIgPSAiMCIsICJtZWRFMDFEMDQiID0gIjAuMDU4IiwgIm1lZEUwMkQwNCIgPSAiMC41IiwgIm1lZEUwM0QwNCIgPSAiMSIsCiAgIm1lZEUwNEQwNCIgPSAiMTAiLCAibWVkRTA1RDA0IiA9ICI1MCIsICJtZWRFMDZEMDQiID0gIjIwMCIKKQoKIyBSZXNoYXBlIGFuZCByZWxhYmVsIHRoZSBkYXRhCm11dF9jb2xsYXBzZV8xNS4xNl9pbmZvX3Bsb3RfZGF0YSA8LSBtdXRfY29sbGFwc2VfMTUuMTZfaW5mb19jb21iaW5lZF9kZiAlPiUKICBwaXZvdF9sb25nZXIoCiAgICBjb2xzID0gc3RhcnRzX3dpdGgoIm1lZCIpLAogICAgbmFtZXNfdG8gPSAiY29uZGl0aW9uIiwKICAgIHZhbHVlc190byA9ICJmaXRuZXNzIikgJT4lCiAgbXV0YXRlKAogICAgbGlicmFyeSA9IGNhc2Vfd2hlbigKICAgICAgc3RhcnRzV2l0aChjb25kaXRpb24sICJtZWREIikgJiBjb25kaXRpb24gIT0gIm1lZEQxMkQwNCIgfiAiQ29kb24xIiwKICAgICAgY29uZGl0aW9uID09ICJtZWREMTJEMDQiIHwgc3RhcnRzV2l0aChjb25kaXRpb24sICJtZWRFIikgfiAiQ29kb24yIiwKICAgICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8pLAogICAgdHJlYXRtZW50ID0gbXV0X2NvbGxhcHNlXzE1LjE2X2luZm9fbGFiZWxfbWFwW2NvbmRpdGlvbl0sCiAgICB0cmVhdG1lbnQgPSBmYWN0b3IodHJlYXRtZW50LCBsZXZlbHMgPSBjKCIwIiwgIjAuMDU4IiwgIjAuNSIsICIxIiwgIjEwIiwgIjUwIiwgIjIwMCIpKSkKYGBgCgpQbG90IGFzIGJveHBsb3Q6CmBgYHtyfQojIENyZWF0ZSB0aGUgcGxvdAptdXRfY29sbGFwc2VfMTUuMTZfaW5mb19wbG90IDwtIGdncGxvdChtdXRfY29sbGFwc2VfMTUuMTZfaW5mb19wbG90X2RhdGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IHRyZWF0bWVudCwgeSA9IGZpdG5lc3MsIGZpbGwgPSBsaWJyYXJ5KSkgKwogIGdlb21fYm94cGxvdChwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC44KSwgYWxwaGEgPSAwLjgpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKAogICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICdibGFjaycsIHNpemUgPSAwLjUpLAogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiksCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsKICBsYWJzKAogICAgdGl0bGUgPSAiTWVkaWFuIEZpdG5lc3MgXG5Db2xsYXBzZWQgdG8gNSBhLmEuIERpc3RhbmNlIiwKICAgIHggPSAiVHJpbWV0aG9wcmltICh1Zy9tTCkiLAogICAgeSA9ICJNZWRpYW4gRml0bmVzcyAoTG9nRkMpIiwKICAgIGZpbGwgPSAiTGlicmFyeSIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJDb2RvbjEiID0gIiMwMDcyQjIiLCAiQ29kb24yIiA9ICIjRTY5RjAwIikpICsKICBzY2FsZV94X2Rpc2NyZXRlKGRyb3AgPSBGQUxTRSkKCm11dF9jb2xsYXBzZV8xNS4xNl9pbmZvX3Bsb3QKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojIFNhdmUgdGhlIHBsb3QKZ2dzYXZlKCJNdXRhbnRzL1BMT1RTL0NvZG9uMV9Db2RvbjJfbWVkaWFuLmZpdG5lc3MudG1wLmdyYWRpZW50LnBuZyIsIAogICAgICAgcGxvdCA9IG11dF9jb2xsYXBzZV8xNS4xNl9pbmZvX3Bsb3QsIHdpZHRoID0gNiwgaGVpZ2h0ID0gNikKYGBgCgojIyMgTWVyZ2UgTGlicmFyaWVzCgpHZW5lcmF0ZSBhIG5ldyBkYXRhZnJhbWUgcmV0YWluaW5nIG9ubHkgdGhlIHVuaXF1ZSBJRHMgc2hhcmVkIGJldHdlZW4gbGlicmFyaWVzOgpgYGB7cn0Kc2hhcmVkX211dF9jb2xsYXBzZV8xNS4xNmluZm8gPC0gbWVyZ2UobXV0X2NvbGxhcHNlXzE1aW5mbywgbXV0X2NvbGxhcHNlXzE2aW5mbywgYnkgPSAiSUQiKQpgYGAKCkNvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIElEcyBzaGFyZWQgYmV0d2VlbiBsaWJyYXJpZXM6CmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQpmb3JtYXQobGVuZ3RoKHVuaXF1ZShzaGFyZWRfbXV0X2NvbGxhcHNlXzE1LjE2aW5mbyRJRCkpLCBiaWcubWFyayA9ICIsIikKYGBgCgpTdWJzZXQgcmVsZXZhbnQgZGF0YSBjb2x1bW5zIGZvciBjb3JyZWxhdGlvbnMgYW5kIHJlbW92ZSByb3dzIGNvbnRhaW5pbmcgIk5BIiB2YWx1ZXM6CmBgYHtyfQojIENvbXBsZW1lbnRhdGlvbiAtIDAtVE1QClNoYXJlZC5NdXQuQ29sbGFwc2UuY291bnRzLjAudG1wIDwtIHNoYXJlZF9tdXRfY29sbGFwc2VfMTUuMTZpbmZvWywgYygiSUQiLCAidG90YWxudW1wcnVuZWRCQ3MuTDE1IiwgInRvdGFsbnVtcHJ1bmVkQkNzLkwxNiIsICJtZWREMDVEMDMiLCJtZWREMTJEMDQiKV0gJT4lIG5hLm9taXQoU2hhcmVkLk11dC5Db2xsYXBzZS5jb3VudHMuMC50bXApCmBgYAoKQ2FsY3VsYXRlIGNvcnJlbGF0aW9uIGJldHdlZW4gbWVkaWFuIGZpdG5lc3MgdmFsdWVzIGZvciB1bmlxdWUgSURzIHNoYXJlZCBiZXR3ZWVuIGxpYnJhcmllczoKYGBge3J9CiMgQ2FsY3VsYXRlIGNvcnJlbGF0aW9uIGFuZCBwLXZhbHVlIGJldHdlZW4gTGliMTUgYW5kIExpYjE2IENvbXBsZW1lbnRhdGlvbgpjb3JfdGVzdF9zaGFyZWRfbXV0X2NvbGxhcHNlXzE1LjE2aW5mbyA8LSBjb3IudGVzdChTaGFyZWQuTXV0LkNvbGxhcHNlLmNvdW50cy4wLnRtcCRtZWREMDVEMDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNoYXJlZC5NdXQuQ29sbGFwc2UuY291bnRzLjAudG1wJG1lZEQxMkQwNCkKCmNvcl90ZXN0X3NoYXJlZF9tdXRfY29sbGFwc2VfMTUuMTZpbmZvCmBgYAoKIyMjIFBsb3QgQ29ycmVsYXRpb24KClBsb3QgdGhlIG1lZGlhbiBmaXRuZXNzIGNvcnJlbGF0aW9uIGJldHdlZW4gdW5pcXVlIElEcyBzaGFyZWQgYmV0d2VlbiBMaWIxNSBhbmQgTGliMTYgKG1lZGlhbiB3aXRoIGNvbGxhcHNlZCBtdXRhbnRzKToKYGBge3J9CiMgRXh0cmFjdCBjb3JyZWxhdGlvbiB2YWx1ZSBmcm9tIGNvcl90ZXN0X3NoYXJlZF9tdXRfY29sbGFwc2VfMTUuMTZpbmZvIG9iamVjdApjb3JfdmFsdWVfc2hhcmVkIDwtIGNvcl90ZXN0X3NoYXJlZF9tdXRfY29sbGFwc2VfMTUuMTZpbmZvJGVzdGltYXRlCgojIEZvcm1hdCBwLXZhbHVlIGluIHNjaWVudGlmaWMgbm90YXRpb24KcF92YWx1ZV9zY2llbnRpZmljX3NoYXJlZCA8LSBmb3JtYXQoY29yX3Rlc3Rfc2hhcmVkX211dF9jb2xsYXBzZV8xNS4xNmluZm8kcC52YWx1ZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY2llbnRpZmljID0gVFJVRSwgZGlnaXRzID0gNCkKCiMgRXh0cmFjdCBudW1iZXIgb2Ygcm93cwpudW1fcm93cy5jb3VudHMuNWFhLjAudG1wIDwtIG5yb3coU2hhcmVkLk11dC5Db2xsYXBzZS5jb3VudHMuMC50bXApCgpMaWIxNV8xNl8wX1RNUF81QUEgPC0gZ2dwbG90KFNoYXJlZC5NdXQuQ29sbGFwc2UuY291bnRzLjAudG1wLCAKICAgICAgICAgICAgIGFlcyh4ID0gbWVkRDA1RDAzLCB5ID0gbWVkRDEyRDA0KSkgKwogIGxhYnMoeCA9ICJDb2RvbiAxIE1lZGlhbiBGaXRuZXNzIHcvIDUgQS5BLiBEaXN0YW5jZSAoTG9nRkMpIFxuKDAgzrxnL21MIHRtcCkiLAogICAgICAgeSA9IkNvZG9uIDIgTWVkaWFuIEZpdG5lc3Mgdy8gNSBBLkEuIERpc3RhbmNlIChMb2dGQykgXG4oMCDOvGcvbUwgdG1wKSIpICsKICBnZW9tX3Ntb290aChtZXRob2Q9bG0sY29sb3VyPSJibGFjayIpICsKICBnZW9tX2RlbnNpdHkyZChjb2xvdXI9ImJsYWNrIixhbHBoYT0wLjIpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNhc2Vfd2hlbigKICAgIG1lZEQwNUQwMyA+PSAtMSAmIG1lZEQxMkQwNCA+PSAtMSB+ICJsaWdodGJsdWU0IiwKICAgIG1lZEQwNUQwMyA+PSAtMSAmIG1lZEQxMkQwNCA8IC0xIH4gIiMwMDcyQjIiLAogICAgbWVkRDEyRDA0ID49IC0xICYgbWVkRDA1RDAzIDwgLTEgfiAiI0U2OUYwMCIsCiAgICBUUlVFIH4gImJsYWNrIgogICksCiAgZmlsbCA9IGNhc2Vfd2hlbigKICAgIG1lZEQwNUQwMyA+PSAtMSAmIG1lZEQxMkQwNCA+PSAtMSB+ICJsaWdodGJsdWU0IiwKICAgIG1lZEQwNUQwMyA+PSAtMSAmIG1lZEQxMkQwNCA8IC0xIH4gIiMwMDcyQjIiLAogICAgbWVkRDEyRDA0ID49IC0xICYgbWVkRDA1RDAzIDwgLTEgfiAiI0U2OUYwMCIsCiAgICBUUlVFIH4gIndoaXRlIgogICksCiAgc2hhcGUgPSBjYXNlX3doZW4oCiAgICBtZWREMDVEMDMgPj0gLTEgJiBtZWREMTJEMDQgPj0gLTEgfiAxNiwKICAgIG1lZEQwNUQwMyA+PSAtMSAmIG1lZEQxMkQwNCA8IC0xIH4gMTYsCiAgICBtZWREMTJEMDQgPj0gLTEgJiBtZWREMDVEMDMgPCAtMSB+IDE2LAogICAgVFJVRSB+IDIxCiAgKSksIAogIGFscGhhID0gMC43NSwgc2l6ZSA9IDIuNSkgKwpzY2FsZV9zaGFwZV9pZGVudGl0eSgpICsKc2NhbGVfY29sb3JfaWRlbnRpdHkoKSArCnNjYWxlX2ZpbGxfaWRlbnRpdHkoKSArCiAgIyBBZGQgYSBuZXcgcG9pbnQgZm9yIFdUIEUuIGNvbGkgbWVkaWFuIGZpdG5lc3MKICBnZW9tX3BvaW50KGRhdGEgPSBCQ2NvbnRyb2xzXzE1XzE2X3NoYXJlZF9tZWRpYW5fV1QsIAogICAgICAgICAgICAgYWVzKHggPSBmaXREMDVEMDMsIHkgPSBmaXREMTJEMDQpLCAKICAgICAgICAgICAgIGZpbGwgPSAicmVkIiwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gNCwgc2hhcGUgPSAyNCkgKwogICMgQWRkIGEgbmV3IHBvaW50IGZvciBOZWcgQ3RybCAoRDI3TiwgbUNoZXJyeSkgbWVkaWFuIGZpdG5lc3MKICBnZW9tX3BvaW50KGRhdGEgPSBCQ2NvbnRyb2xzXzE1XzE2X3NoYXJlZF9tZWRpYW5fTmVnLCAKICAgICAgICAgICAgIGFlcyh4ID0gZml0RDA1RDAzLCB5ID0gZml0RDEyRDA0KSwgCiAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIHNpemUgPSA1LCBzaGFwZSA9IDE4KSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArCiAgcGFuZWxfYm9yZGVyKGNvbG9yID0gImJsYWNrIikgKwogIGFubm90YXRlKCJ0ZXh0IiwgCiAgICAgICAgICAgeCA9IG1heChTaGFyZWQuTXV0LkNvbGxhcHNlLmNvdW50cy4wLnRtcCRtZWREMDVEMDMpLCAKICAgICAgICAgICB5ID0gbWluKFNoYXJlZC5NdXQuQ29sbGFwc2UuY291bnRzLjAudG1wJG1lZEQxMkQwNCksIAogICAgICAgICAgIGxhYmVsID0gcGFzdGUoInAtdmFsdWUgPSIsIHBfdmFsdWVfc2NpZW50aWZpY19zaGFyZWQpLCBoanVzdCA9IDEsIHZqdXN0ID0gMCkgKwogIGFubm90YXRlKCJ0ZXh0IiwgCiAgICAgICAgICAgeCA9IG1heChTaGFyZWQuTXV0LkNvbGxhcHNlLmNvdW50cy4wLnRtcCRtZWREMDVEMDMpLCAKICAgICAgICAgICB5ID0gbWluKFNoYXJlZC5NdXQuQ29sbGFwc2UuY291bnRzLjAudG1wJG1lZEQxMkQwNCksCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiQ29ycmVsYXRpb24gPSIsIHJvdW5kKGNvcl92YWx1ZV9zaGFyZWQsIDIpKSwgaGp1c3QgPSAxLCB2anVzdCA9IC0xLjUpICsKICBhbm5vdGF0ZSgidGV4dCIsCiAgICAgICAgICAgeCA9IG1pbihTaGFyZWQuTXV0LkNvbGxhcHNlLmNvdW50cy4wLnRtcCRtZWREMDVEMDMpLAogICAgICAgICAgIHkgPSBtYXgoU2hhcmVkLk11dC5Db2xsYXBzZS5jb3VudHMuMC50bXAkbWVkRDEyRDA0KSwKICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJTaGFyZWQgUGVyZmVjdHMgPSIsIG51bV9yb3dzLmNvdW50cy41YWEuMC50bXApLCBoanVzdCA9IDAsIHZqdXN0ID0gMS41KSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShmbG9vcihtaW4oU2hhcmVkLk11dC5Db2xsYXBzZS5jb3VudHMuMC50bXAkbWVkRDA1RDAzKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2VpbGluZyhtYXgoU2hhcmVkLk11dC5Db2xsYXBzZS5jb3VudHMuMC50bXAkbWVkRDA1RDAzKSksIGJ5ID0gMSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKGZsb29yKG1pbihTaGFyZWQuTXV0LkNvbGxhcHNlLmNvdW50cy4wLnRtcCRtZWREMTJEMDQpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZWlsaW5nKG1heChTaGFyZWQuTXV0LkNvbGxhcHNlLmNvdW50cy4wLnRtcCRtZWREMTJEMDQpKSwgYnkgPSAxKSkKCiMgQWRkIHNpZGUgaGlzdG9ncmFtcwpMaWIxNV8xNl8wX1RNUF81QUFfcDAxIDwtIGdnTWFyZ2luYWwoTGliMTVfMTZfMF9UTVBfNUFBLCB0eXBlID0gImhpc3RvZ3JhbSIsIGZpbGwgPSAibGlnaHRibHVlNCIsIGFscGhhPTAuNSkgCgojIFZpZXcgcGxvdApMaWIxNV8xNl8wX1RNUF81QUFfcDAxCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9Ik11dGFudHMvUExPVFMvQ29ycmVsYXRpb25zL0xpYjE1LjE2LnNoYXJlZC5jb2xsYXBzZWQubXV0YW50cy41QUEubWVkaWFuLmNvbXBsZW1lbnRhdGlvbi5wbmciLAogICAgICAgcGxvdD1MaWIxNV8xNl8wX1RNUF81QUFfcDAxLAogICAgICAgZHBpPTYwMCwgd2lkdGggPSA4LCBoZWlnaHQgPSA2LCB1bml0cyA9ICJpbiIpCmBgYAoKIyMgR2VuZXJhdGUgTXV0YW50IEZBU1RBCgpHZW5lcmF0ZSBhIEZBU1RBIGZpbGUgZm9yIGVhY2ggbGlicmFyeSBjb250YWluaW5nIGVhY2ggdW5pcXVlIG11dElEIChkZXNpZ25lZCBob21vbG9ncyAoSUQpIGFuZCBtdXRhbnRzIChtdXRJRCkgdXAgdG8gNSBBQSBkaWZmZXJlbmNlKSBhbmQgdGhlaXIgY29ycmVzcG9uZGluZyBwcm90ZWluIHNlcXVlbmNlIGZvciB1c2UgaW4gYnJvYWQgbXV0YXRpb25hbCBzY2FubmluZyAoQk1TKSBhbmQgZ2Fpbi1vZi1mdW5jdGlvbiAoR09GKSBhbmFseXNpcy4gVXNlIHRoZSBgbXV0X2NvbGxhcHNlXzE1YCBkYXRhc2V0IGZvciBMaWIxNS4KCiMjIyBLZWVwIENvbXBsZW1lbnRpbmcgUGVyZmVjdHMKCkZpcnN0LCB3ZSBuZWVkIHRvIHJlbW92ZSBhbnkgdW5pcXVlIElEIHdpdGggbXV0YXRpb25zID09IDAgYW5kIGZpdEQwNUQwMyA8IC0xIHRvIHJldGFpbiBvbmx5IHRob3NlIHVuaXF1ZSBwZXJmZWN0IElEcyBjYXBhYmxlIG9mIGNvbXBsZW1lbnRhdGlvbi4KYGBge3J9CiMgRmlsdGVyIGRhdGFzZXQgdG8gcmVtb3ZlIGZpdG5lc3MgPCAtMQptdXRfY29sbGFwc2VfMTVfZ29vZCA8LSBtdXRfY29sbGFwc2VfMTUgJT4lCiAgIyBHcm91cCBieSBJRAogIGdyb3VwX2J5KElEKSAlPiUKICAjIEZpbHRlciBvdXQgcm93cyB3aGVyZSBtdXRhdGlvbnMgPT0gMCBhbmQgZml0RDA1RDAzIDwgLTEKICBmaWx0ZXIoIShtdXRhdGlvbnMgPT0gMCAmIGZpdEQwNUQwMyA8IC0xKSkgJT4lCiAgIyBVbmdyb3VwIHRoZSBkYXRhIGZyYW1lCiAgdW5ncm91cCgpCgojIFN0ZXAgdG8gYWRkIGJhY2sgYWxsIHJvd3Mgd2hlcmUgSUQgPT0gIk5QXzQxNDU5MCIKbXV0X2NvbGxhcHNlXzE1X2dvb2Rfcm93c190b19hZGQgPC0gbXV0X2NvbGxhcHNlXzE1ICU+JQogIGZpbHRlcihJRCA9PSAiTlBfNDE0NTkwIikgICMgR2V0IGFsbCByb3dzIHdpdGggSUQgTlBfNDE0NTkwCgojIENvbWJpbmUgdGhlIGZpbHRlcmVkIGRhdGEgZnJhbWUgd2l0aCB0aGUgcm93cyB0byBhZGQKbXV0X2NvbGxhcHNlXzE1X2dvb2QgPC0gYmluZF9yb3dzKG11dF9jb2xsYXBzZV8xNV9nb29kLCBtdXRfY29sbGFwc2VfMTVfZ29vZF9yb3dzX3RvX2FkZCkgJT4lCiAgZGlzdGluY3QoKSAgIyBPcHRpb25hbDogUmVtb3ZlIGFueSBkdXBsaWNhdGUgcm93cyB0aGF0IG1heSBoYXZlIGJlZW4gaW50cm9kdWNlZAoKIyBDcmVhdGUgc3Vic2V0IHRvIHJldGFpbiBvbmx5IHBlcmZlY3RzIChtdXRhdGlvbnMgPT0gMCkgZm9yIGluaXRpYWwgQk1TIEZBU1RBOgptdXRfY29sbGFwc2VfMTVfZ29vZF8wX211dHMgPC0gbXV0X2NvbGxhcHNlXzE1X2dvb2QgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyA9PSAwKSAgIyBLZWVwIGFsbCByb3dzIHdoZXJlIG11dGF0aW9ucyA9PSAwCmBgYAoKIyMjIEtlZXAgQXNzb2NpYXRlZCBNdXRhbnRzCgpOZXh0LCB3ZSdsbCBpZGVudGlmeSB3aGljaCBJRHMgaGF2ZSBhdCBsZWFzdCBvbmUgZXhhbXBsZSBvZiBtdXRhdGlvbnMgPSAwIGFuZCB0aGVuIGZpbHRlciBvdXQgYWxsIG11dElEcyB0aGF0IGRvbid0IGNvcnJlc3BvbmQgdG8gYSBwZXJmZWN0IElEOgpgYGB7cn0KIyBTdGVwIDE6IElkZW50aWZ5IElEcyB0aGF0IGhhdmUgYXQgbGVhc3Qgb25lIHJvdyB3aXRoIG11dGF0aW9ucyA9PSAwCm11dF9jb2xsYXBzZV8xNV9nb29kX3ZhbGlkX2lkcyA8LSBtdXRfY29sbGFwc2VfMTVfZ29vZF8wX211dHMgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyA9PSAwKSAlPiUKICBzZWxlY3QoSUQpICU+JQogIGRpc3RpbmN0KCkKCiMgU3RlcCAyOiBGaWx0ZXIgdGhlIG9yaWdpbmFsIGRhdGEgZnJhbWUgdG8ga2VlcCBvbmx5IHJvd3Mgd2l0aCB2YWxpZCBJRHMKbXV0X2NvbGxhcHNlXzE1X2dvb2RfZmlsdGVyZWQgPC0gbXV0X2NvbGxhcHNlXzE1X2dvb2QgJT4lCiAgZmlsdGVyKElEICVpbiUgbXV0X2NvbGxhcHNlXzE1X2dvb2RfdmFsaWRfaWRzJElEKQpgYGAKCiMjIyBHZW5lcmF0ZSBGQVNUQSBmaWxlCgpOb3cgd2UnbGwgZ2VuZXJhdGUgdGhlIEZBU1RBIGZpbGUgZnJvbSB0aGUgZmlsdGVyZWQgYG11dF9jb2xsYXBzZV8xNV9nb29kXzBfbXV0c2AgZGF0YXNldCBmb3IgQk1TIGFuYWx5c2lzLgoKPGZvbnQgY29sb3I9InJlZCI+WW91IHdpbGwgaGF2ZSB0byBvcGVuIHRoZSAuZmFzdGEgZmlsZSBhbmQgYWRkIHRoZSBXVCBFLiBjb2xpIERIRlIgaG9tb2xvZyAocmVmZXJlbmNlIHNlcXVlbmNlKSBtYW51YWxseSBzaW5jZSBpdCdzIGZpdEQwNUQwMyB2YWx1ZXMgd2FzIGxlc3MgdGhhbiAtMSBhbmQgc3Vic2VxdWVudGx5IHJlbW92ZWQgZHVyaW5nIHRoZSBwcmV2aW91cyBmaWx0ZXJpbmcgc3RlcC48L2ZvbnQ+CmBgYHtyfQojIExpYjE1CgojIENyZWF0ZSB0aGUgc2VxdWVuY2VzIGluIEZBU1RBIGZvcm1hdAptdXRfY29sbGFwc2VfMTVfZ29vZF8wX211dHNfZmFzdGFfY29udGVudCA8LSBwYXN0ZSgiPiIsIG11dF9jb2xsYXBzZV8xNV9nb29kXzBfbXV0cyRtdXRJRCwgIlxuIiwgbXV0X2NvbGxhcHNlXzE1X2dvb2RfMF9tdXRzJHNlcSwgIlxuIiwgc2VwID0gIiIsIGNvbGxhcHNlID0gIiIpCgojIERlZmluZSB0aGUgZmlsZSBwYXRoIGluIHRoZSB3b3JraW5nIGRpcmVjdG9yeQptdXRfY29sbGFwc2VfMTVfZ29vZF8wX211dHNfZmFzdGFfZmlsZV9wYXRoIDwtIGZpbGUucGF0aChnZXR3ZCgpLCAiTXV0YW50cy9tdXRhbnRzX2ZpbGVzX2Zvcm1hdHRlZC9MaWIxNS5tdXRhbnQuY29sbGFwc2UuZ29vZC41QUEuZmFzdGEiKQoKIyBXcml0ZSB0aGUgRkFTVEEgY29udGVudCB0byB0aGUgZmlsZQp3cml0ZUxpbmVzKG11dF9jb2xsYXBzZV8xNV9nb29kXzBfbXV0c19mYXN0YV9jb250ZW50LCAKICAgICAgICAgICBjb24gPSBtdXRfY29sbGFwc2VfMTVfZ29vZF8wX211dHNfZmFzdGFfZmlsZV9wYXRoKQpgYGAKCiMjIEZhbHNlLVBvc2l0aXZlIFJhdGUKCiMjIyBDb2RvbiAxIExpYnJhcnkKCmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIEFsbCBtdXRhbnRzIHdpdGhpbiA1IEFBIGRpc3RhbmNlIGF0IENvbXBsZW1lbnRhdGlvbgpmaWx0ZXJlZF9tdXRhbnRfZGF0YV9jb21wX2Z1bGwgPC0gbXV0YW50czE1ICU+JQogIGZpbHRlcihtdXRhdGlvbnMgPCA2LCAhaXMubmEoZml0RDA1RDAzKSkKcHJpbnQocGFzdGUoIk51bWJlciBvZiByb3dzIHdpdGggbXV0YXRpb25zIDwgNiBhdCBDb21wbGVtZW50YXRpb246IiwgbnJvdyhmaWx0ZXJlZF9tdXRhbnRfZGF0YV9jb21wX2Z1bGwpKSkKCiMgRmlsdGVyZWQgbXV0YW50cyB3aXRoaW4gNSBBQSBkaXN0YW5jZSB0byByZXRhaW4gb25seSB0aG9zZSB3aXRoID4gMSBudW1wcnVuZWRCQ3MgYXQgQ29tcGxlbWVudGF0aW9uCmZpbHRlcmVkX211dGFudF9kYXRhX2NvbXBfZ29vZCA8LSBtdXRhbnRzMTUgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyA8IDYsIG51bXBydW5lZEJDcyA+IDEsICFpcy5uYShmaXREMDVEMDMpKQpwcmludChwYXN0ZSgiTnVtYmVyIG9mIHJvd3Mgd2l0aCBtdXRhdGlvbnMgPCA2IGFuZCBudW1wcnVuZWRCQ3MgPiAxIGF0IENvbXBsZW1lbnRhdGlvbjoiLCBucm93KGZpbHRlcmVkX211dGFudF9kYXRhX2NvbXBfZ29vZCkpKQoKYGBgCgoqKkNvbXBsZW1lbnRhdGlvbjoqKiBDYWxjdWxhdGUgZmFsc2UgcG9zaXRpdmUgcmF0ZSBmb3IgbXV0YW50IHZhcmlhbnRzID4gNTAgd2l0aCBmaXREMDVEMDMgPiAtMSBpbiBDb2RvbiAxOgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBGaWx0ZXIgdGhlIGRhdGEgZm9yIG11dGF0aW9ucyA+IDQ5IGFuZCByZW1vdmUgcm93cyB3aGVyZSBmaXREMDVEMDMgaXMgTkEKZmlsdGVyZWRfbXV0YW50X2RhdGFfY29tcCA8LSBtdXRhbnRzMTUgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyA+IDQ5LCAhaXMubmEoZml0RDA1RDAzKSkKCiMgUHJpbnQgdGhlIG51bWJlciBvZiByb3dzIGFmdGVyIGZpbHRlcmluZwpwcmludChwYXN0ZSgiTnVtYmVyIG9mIHJvd3Mgd2l0aCBtdXRhdGlvbnMgPiA0OToiLCBucm93KGZpbHRlcmVkX211dGFudF9kYXRhX2NvbXApKSkKCiMgRGVmaW5lIGEgbG9naWNhbCBjb25kaXRpb24gZm9yIGZhbHNlIHBvc2l0aXZlcyAoZml0RDA1RDAzID4gLTEgYXJlIGZhbHNlIHBvc2l0aXZlcykKZmFsc2VfcG9zaXRpdmVzX2NvbXAgPC0gZmlsdGVyZWRfbXV0YW50X2RhdGFfY29tcCAlPiUKICBmaWx0ZXIoZml0RDA1RDAzID4gLTEsIG51bXBydW5lZEJDcyA+IDUpCnByaW50KHBhc3RlKCJOdW1iZXIgb2YgZmFsc2UgcG9zaXRpdmVzIGF0IENvbXBsZW1lbnRhdGlvbiAoZml0RDA1RDAzID4gLTEpOiIsIG5yb3coZmFsc2VfcG9zaXRpdmVzX2NvbXApKSkKCiMgQ2FsY3VsYXRlIHRoZSBudW1iZXIgb2YgZmFsc2UgcG9zaXRpdmVzCm51bV9mYWxzZV9wb3NpdGl2ZXNfY29tcCA8LSBucm93KGZhbHNlX3Bvc2l0aXZlc19jb21wKQojcHJpbnQocGFzdGUoIk51bWJlciBvZiBmYWxzZSBwb3NpdGl2ZXMgYXQgQ29tcGxlbWVudGF0aW9uOiIsIG51bV9mYWxzZV9wb3NpdGl2ZXNfY29tcCkpCgojIENhbGN1bGF0ZSB0aGUgdG90YWwgbnVtYmVyIG9mIGVudHJpZXMgdGhhdCBtZWV0IHRoZSBjcml0ZXJpYQp0b3RhbF9jcml0ZXJpYV9tZXRfY29tcCA8LSBucm93KGZpbHRlcmVkX211dGFudF9kYXRhX2NvbXApCiNwcmludChwYXN0ZSgiVG90YWwgbnVtYmVyIG9mIGVudHJpZXMgbWVldGluZyBpbml0aWFsIGNyaXRlcmlhICg+IDQ5IG11dGF0aW9ucykgYXQgQ29tcGxlbWVudGF0aW9uOiIsIHRvdGFsX2NyaXRlcmlhX21ldF9jb21wKSkKCiMgQ2FsY3VsYXRlIHRoZSBmYWxzZSBwb3NpdGl2ZSByYXRlCmZhbHNlX3Bvc2l0aXZlX3JhdGVfY29tcCA8LSAobnVtX2ZhbHNlX3Bvc2l0aXZlc19jb21wIC8gdG90YWxfY3JpdGVyaWFfbWV0X2NvbXApICogMTAwCgojIFByaW50IHRoZSBmYWxzZSBwb3NpdGl2ZSByYXRlCnByaW50KHBhc3RlKCJGYWxzZSBwb3NpdGl2ZSByYXRlIGF0IENvbXBsZW1lbnRhdGlvbjoiLCByb3VuZChmYWxzZV9wb3NpdGl2ZV9yYXRlX2NvbXAsIDIpLCAiJSIpKQpgYGAKCioqTUlDOioqIENhbGN1bGF0ZSBmYWxzZSBwb3NpdGl2ZSByYXRlIGZvciBtdXRhbnQgdmFyaWFudHMgPiA1MCB3aXRoIGZpdEQwN0QwMyA+IC0xIGluIENvZG9uIDE6CmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIEZpbHRlciB0aGUgZGF0YSBmb3IgbXV0YXRpb25zID4gNDkgYW5kIHJlbW92ZSByb3dzIHdoZXJlIGZpdEQwNUQwMyBpcyBOQQpmaWx0ZXJlZF9tdXRhbnRfZGF0YV9NSUMgPC0gbXV0YW50czE1ICU+JQogIGZpbHRlcihtdXRhdGlvbnMgPiA0OSwgIWlzLm5hKGZpdEQwN0QwMykpCnByaW50KHBhc3RlKCJOdW1iZXIgb2Ygcm93cyB3aXRoIG11dGF0aW9ucyA+IDQ5OiIsIG5yb3coZmlsdGVyZWRfbXV0YW50X2RhdGFfTUlDKSkpCgojIERlZmluZSBhIGxvZ2ljYWwgY29uZGl0aW9uIGZvciBmYWxzZSBwb3NpdGl2ZXMgKGZpdEQwNUQwMyA+IC0xIGFyZSBmYWxzZSBwb3NpdGl2ZXMpCmZhbHNlX3Bvc2l0aXZlc19NSUMgPC0gZmlsdGVyZWRfbXV0YW50X2RhdGFfTUlDICU+JQogIGZpbHRlcihmaXREMDdEMDMgPiAtMSwgbnVtcHJ1bmVkQkNzID4gNSkKcHJpbnQocGFzdGUoIk51bWJlciBvZiBmYWxzZSBwb3NpdGl2ZXMgYXQgTUlDIChmaXREMDdEMDMgPiAtMSk6IiwgbnJvdyhmYWxzZV9wb3NpdGl2ZXNfTUlDKSkpCgojIENhbGN1bGF0ZSB0aGUgbnVtYmVyIG9mIGZhbHNlIHBvc2l0aXZlcwpudW1fZmFsc2VfcG9zaXRpdmVzX01JQyA8LSBucm93KGZhbHNlX3Bvc2l0aXZlc19NSUMpCiNwcmludChwYXN0ZSgiTnVtYmVyIG9mIGZhbHNlIHBvc2l0aXZlcyBhdCBNSUM6IiwgbnVtX2ZhbHNlX3Bvc2l0aXZlc19NSUMpKQoKIyBDYWxjdWxhdGUgdGhlIHRvdGFsIG51bWJlciBvZiBlbnRyaWVzIHRoYXQgbWVldCB0aGUgY3JpdGVyaWEKdG90YWxfY3JpdGVyaWFfbWV0X01JQyA8LSBucm93KGZpbHRlcmVkX211dGFudF9kYXRhX01JQykKI3ByaW50KHBhc3RlKCJUb3RhbCBudW1iZXIgb2YgZW50cmllcyBtZWV0aW5nIGluaXRpYWwgY3JpdGVyaWEgKD4gNDkgbXV0YXRpb25zKSBhdCBNSUM6IiwgdG90YWxfY3JpdGVyaWFfbWV0X01JQykpCgojIENhbGN1bGF0ZSB0aGUgZmFsc2UgcG9zaXRpdmUgcmF0ZQpmYWxzZV9wb3NpdGl2ZV9yYXRlX01JQyA8LSAobnVtX2ZhbHNlX3Bvc2l0aXZlc19NSUMgLyB0b3RhbF9jcml0ZXJpYV9tZXRfTUlDKSAqIDEwMAoKIyBQcmludCB0aGUgZmFsc2UgcG9zaXRpdmUgcmF0ZQpwcmludChwYXN0ZSgiRmFsc2UgcG9zaXRpdmUgcmF0ZSBhdCBNSUM6Iiwgcm91bmQoZmFsc2VfcG9zaXRpdmVfcmF0ZV9NSUMsIDIpLCAiJSIpKQpgYGAKCioqNDAweCBNSUM6KiogQ2FsY3VsYXRlIGZhbHNlIHBvc2l0aXZlIHJhdGUgZm9yIG11dGFudCB2YXJpYW50cyA+IDUwIHdpdGggZml0RDExRDAzID4gLTEgaW4gQ29kb24gMToKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgRmlsdGVyIHRoZSBkYXRhIGZvciBtdXRhdGlvbnMgPiA0OSBhbmQgcmVtb3ZlIHJvd3Mgd2hlcmUgZml0RDA1RDAzIGlzIE5BCmZpbHRlcmVkX211dGFudF9kYXRhXzQwMHhNSUMgPC0gbXV0YW50czE1ICU+JQogIGZpbHRlcihtdXRhdGlvbnMgPiA0OSwgIWlzLm5hKGZpdEQxMUQwMykpCnByaW50KHBhc3RlKCJOdW1iZXIgb2Ygcm93cyB3aXRoIG11dGF0aW9ucyA+IDQ5OiIsIG5yb3coZmlsdGVyZWRfbXV0YW50X2RhdGFfNDAweE1JQykpKQoKIyBEZWZpbmUgYSBsb2dpY2FsIGNvbmRpdGlvbiBmb3IgZmFsc2UgcG9zaXRpdmVzIChmaXREMDVEMDMgPiAtMSBhcmUgZmFsc2UgcG9zaXRpdmVzKQpmYWxzZV9wb3NpdGl2ZXNfNDAweE1JQyA8LSBmaWx0ZXJlZF9tdXRhbnRfZGF0YV80MDB4TUlDICU+JQogIGZpbHRlcihmaXREMTFEMDMgPiAtMSwgbnVtcHJ1bmVkQkNzID4gNSkKcHJpbnQocGFzdGUoIk51bWJlciBvZiBmYWxzZSBwb3NpdGl2ZXMgYXQgNDAweCBNSUMgKGZpdEQxMUQwMyA+IC0xKToiLCBucm93KGZhbHNlX3Bvc2l0aXZlc180MDB4TUlDKSkpCgojIENhbGN1bGF0ZSB0aGUgbnVtYmVyIG9mIGZhbHNlIHBvc2l0aXZlcwpudW1fZmFsc2VfcG9zaXRpdmVzXzQwMHhNSUMgPC0gbnJvdyhmYWxzZV9wb3NpdGl2ZXNfNDAweE1JQykKI3ByaW50KHBhc3RlKCJOdW1iZXIgb2YgZmFsc2UgcG9zaXRpdmVzIGF0IDQwMHggTUlDOiIsIG51bV9mYWxzZV9wb3NpdGl2ZXNfNDAweE1JQykpCgojIENhbGN1bGF0ZSB0aGUgdG90YWwgbnVtYmVyIG9mIGVudHJpZXMgdGhhdCBtZWV0IHRoZSBjcml0ZXJpYQp0b3RhbF9jcml0ZXJpYV9tZXRfNDAweE1JQyA8LSBucm93KGZpbHRlcmVkX211dGFudF9kYXRhXzQwMHhNSUMpCiNwcmludChwYXN0ZSgiVG90YWwgbnVtYmVyIG9mIGVudHJpZXMgbWVldGluZyBpbml0aWFsIGNyaXRlcmlhICg+IDQ5IG11dGF0aW9ucykgYXQgNDAweCBNSUM6IiwgdG90YWxfY3JpdGVyaWFfbWV0XzQwMHhNSUMpKQoKIyBDYWxjdWxhdGUgdGhlIGZhbHNlIHBvc2l0aXZlIHJhdGUKZmFsc2VfcG9zaXRpdmVfcmF0ZV80MDB4TUlDIDwtIChudW1fZmFsc2VfcG9zaXRpdmVzXzQwMHhNSUMgLyB0b3RhbF9jcml0ZXJpYV9tZXRfNDAweE1JQykgKiAxMDAKCiMgUHJpbnQgdGhlIGZhbHNlIHBvc2l0aXZlIHJhdGUKcHJpbnQocGFzdGUoIkZhbHNlIHBvc2l0aXZlIHJhdGUgYXQgNDAweCBNSUM6Iiwgcm91bmQoZmFsc2VfcG9zaXRpdmVfcmF0ZV80MDB4TUlDLCAyKSwgIiUiKSkKYGBgCgoqKk05LVN1cHA6KiogQ2FsY3VsYXRlIGZhbHNlIHBvc2l0aXZlIHJhdGUgZm9yIG11dGFudCB2YXJpYW50cyA+IDUwIHdpdGggZml0RDAzRDAxID4gLTEgaW4gQ29kb24gMToKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgRmlsdGVyIHRoZSBkYXRhIGZvciBtdXRhdGlvbnMgPiA0OSBhbmQgcmVtb3ZlIHJvd3Mgd2hlcmUgZml0RDA1RDAzIGlzIE5BCmZpbHRlcmVkX211dGFudF9kYXRhX005IDwtIG11dGFudHMxNSAlPiUKICBmaWx0ZXIobXV0YXRpb25zID4gNDksICFpcy5uYShmaXREMDNEMDEpKQpwcmludChwYXN0ZSgiTnVtYmVyIG9mIHJvd3Mgd2l0aCBtdXRhdGlvbnMgPiA0OToiLCBucm93KGZpbHRlcmVkX211dGFudF9kYXRhX005KSkpCgojIERlZmluZSBhIGxvZ2ljYWwgY29uZGl0aW9uIGZvciBmYWxzZSBwb3NpdGl2ZXMgKGZpdEQwNUQwMyA+IC0xIGFyZSBmYWxzZSBwb3NpdGl2ZXMpCmZhbHNlX3Bvc2l0aXZlc19NOSA8LSBmaWx0ZXJlZF9tdXRhbnRfZGF0YV9NOSAlPiUKICBmaWx0ZXIoZml0RDAzRDAxID4gLTEsIG51bXBydW5lZEJDcyA+IDUpCnByaW50KHBhc3RlKCJOdW1iZXIgb2YgZmFsc2UgcG9zaXRpdmVzIGF0IE05LVN1cHAgKGZpdEQwM0QwMSA+IC0xKToiLCBucm93KGZhbHNlX3Bvc2l0aXZlc19NOSkpKQoKIyBDYWxjdWxhdGUgdGhlIG51bWJlciBvZiBmYWxzZSBwb3NpdGl2ZXMKbnVtX2ZhbHNlX3Bvc2l0aXZlc19NOSA8LSBucm93KGZhbHNlX3Bvc2l0aXZlc19NOSkKI3ByaW50KHBhc3RlKCJOdW1iZXIgb2YgZmFsc2UgcG9zaXRpdmVzIGF0IE05LVN1cHA6IiwgbnVtX2ZhbHNlX3Bvc2l0aXZlc19NOSkpCgojIENhbGN1bGF0ZSB0aGUgdG90YWwgbnVtYmVyIG9mIGVudHJpZXMgdGhhdCBtZWV0IHRoZSBjcml0ZXJpYQp0b3RhbF9jcml0ZXJpYV9tZXRfTTkgPC0gbnJvdyhmaWx0ZXJlZF9tdXRhbnRfZGF0YV9NOSkKI3ByaW50KHBhc3RlKCJUb3RhbCBudW1iZXIgb2YgZW50cmllcyBtZWV0aW5nIGluaXRpYWwgY3JpdGVyaWEgKD4gNDkgbXV0YXRpb25zKSBhdCBNOS1TdXBwOiIsIHRvdGFsX2NyaXRlcmlhX21ldF9NOSkpCgojIENhbGN1bGF0ZSB0aGUgZmFsc2UgcG9zaXRpdmUgcmF0ZQpmYWxzZV9wb3NpdGl2ZV9yYXRlX005IDwtIChudW1fZmFsc2VfcG9zaXRpdmVzX005IC8gdG90YWxfY3JpdGVyaWFfbWV0X005KSAqIDEwMAoKIyBQcmludCB0aGUgZmFsc2UgcG9zaXRpdmUgcmF0ZQpwcmludChwYXN0ZSgiRmFsc2UgcG9zaXRpdmUgcmF0ZSBhdCBNOS1TdXBwOiIsIHJvdW5kKGZhbHNlX3Bvc2l0aXZlX3JhdGVfTTksIDIpLCAiJSIpKQpgYGAKCiMjIyBDb2RvbiAyIExpYnJhcnkKCioqQ29tcGxlbWVudGF0aW9uOioqIENhbGN1bGF0ZSBmYWxzZSBwb3NpdGl2ZSByYXRlIGZvciBtdXRhbnQgdmFyaWFudHMgPiA1MCB3aXRoIGZpdEQxMkQwNCA+IC0xIGluIENvZG9uIDE6CmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIEZpbHRlciB0aGUgZGF0YSBmb3IgbXV0YXRpb25zID4gNDkgYW5kIHJlbW92ZSByb3dzIHdoZXJlIGZpdEQwNUQwMyBpcyBOQQpmaWx0ZXJlZF9tdXRhbnRfZGF0YV9jb21wXzE2IDwtIG11dGFudHMxNiAlPiUKICBmaWx0ZXIobXV0YXRpb25zID4gNDksICFpcy5uYShmaXREMTJEMDQpKQoKIyBQcmludCB0aGUgbnVtYmVyIG9mIHJvd3MgYWZ0ZXIgZmlsdGVyaW5nCnByaW50KHBhc3RlKCJOdW1iZXIgb2Ygcm93cyB3aXRoIG11dGF0aW9ucyA+IDQ5OiIsIG5yb3coZmlsdGVyZWRfbXV0YW50X2RhdGFfY29tcF8xNikpKQoKIyBEZWZpbmUgYSBsb2dpY2FsIGNvbmRpdGlvbiBmb3IgZmFsc2UgcG9zaXRpdmVzIChmaXREMDVEMDMgPiAtMSBhcmUgZmFsc2UgcG9zaXRpdmVzKQpmYWxzZV9wb3NpdGl2ZXNfY29tcF8xNiA8LSBmaWx0ZXJlZF9tdXRhbnRfZGF0YV9jb21wXzE2ICU+JQogIGZpbHRlcihmaXREMTJEMDQgPiAtMSwgbnVtcHJ1bmVkQkNzID4gNSkKcHJpbnQocGFzdGUoIk51bWJlciBvZiBmYWxzZSBwb3NpdGl2ZXMgYXQgQ29tcGxlbWVudGF0aW9uIChmaXREMTJEMDQgPiAtMSk6IiwgbnJvdyhmYWxzZV9wb3NpdGl2ZXNfY29tcF8xNikpKQoKIyBDYWxjdWxhdGUgdGhlIG51bWJlciBvZiBmYWxzZSBwb3NpdGl2ZXMKbnVtX2ZhbHNlX3Bvc2l0aXZlc19jb21wXzE2IDwtIG5yb3coZmFsc2VfcG9zaXRpdmVzX2NvbXBfMTYpCiNwcmludChwYXN0ZSgiTnVtYmVyIG9mIGZhbHNlIHBvc2l0aXZlcyBhdCBDb21wbGVtZW50YXRpb246IiwgbnVtX2ZhbHNlX3Bvc2l0aXZlc19jb21wXzE2KSkKCiMgQ2FsY3VsYXRlIHRoZSB0b3RhbCBudW1iZXIgb2YgZW50cmllcyB0aGF0IG1lZXQgdGhlIGNyaXRlcmlhCnRvdGFsX2NyaXRlcmlhX21ldF9jb21wXzE2IDwtIG5yb3coZmlsdGVyZWRfbXV0YW50X2RhdGFfY29tcF8xNikKI3ByaW50KHBhc3RlKCJUb3RhbCBudW1iZXIgb2YgZW50cmllcyBtZWV0aW5nIGluaXRpYWwgY3JpdGVyaWEgKD4gNDkgbXV0YXRpb25zKSBhdCBDb21wbGVtZW50YXRpb246IiwgdG90YWxfY3JpdGVyaWFfbWV0X2NvbXBfMTYpKQoKIyBDYWxjdWxhdGUgdGhlIGZhbHNlIHBvc2l0aXZlIHJhdGUKZmFsc2VfcG9zaXRpdmVfcmF0ZV9jb21wXzE2IDwtIChudW1fZmFsc2VfcG9zaXRpdmVzX2NvbXBfMTYgLyB0b3RhbF9jcml0ZXJpYV9tZXRfY29tcF8xNikgKiAxMDAKCiMgUHJpbnQgdGhlIGZhbHNlIHBvc2l0aXZlIHJhdGUKcHJpbnQocGFzdGUoIkZhbHNlIHBvc2l0aXZlIHJhdGUgYXQgQ29tcGxlbWVudGF0aW9uOiIsIHJvdW5kKGZhbHNlX3Bvc2l0aXZlX3JhdGVfY29tcF8xNiwgMiksICIlIikpCmBgYAoKKipNSUM6KiogQ2FsY3VsYXRlIGZhbHNlIHBvc2l0aXZlIHJhdGUgZm9yIG11dGFudCB2YXJpYW50cyA+IDUwIHdpdGggZml0RDA3RDAzID4gLTEgaW4gQ29kb24gMToKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgRmlsdGVyIHRoZSBkYXRhIGZvciBtdXRhdGlvbnMgPiA0OSBhbmQgcmVtb3ZlIHJvd3Mgd2hlcmUgZml0RDA1RDAzIGlzIE5BCmZpbHRlcmVkX211dGFudF9kYXRhX01JQ18xNiA8LSBtdXRhbnRzMTYgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyA+IDQ5LCAhaXMubmEoZml0RTAyRDA0KSkKcHJpbnQocGFzdGUoIk51bWJlciBvZiByb3dzIHdpdGggbXV0YXRpb25zID4gNDk6IiwgbnJvdyhmaWx0ZXJlZF9tdXRhbnRfZGF0YV9NSUNfMTYpKSkKCiMgRGVmaW5lIGEgbG9naWNhbCBjb25kaXRpb24gZm9yIGZhbHNlIHBvc2l0aXZlcyAoZml0RTAyRDA0ID4gLTEgYXJlIGZhbHNlIHBvc2l0aXZlcykKZmFsc2VfcG9zaXRpdmVzX01JQ18xNiA8LSBmaWx0ZXJlZF9tdXRhbnRfZGF0YV9NSUNfMTYgJT4lCiAgZmlsdGVyKGZpdEUwMkQwNCA+IC0xLCBudW1wcnVuZWRCQ3MgPiA1KQpwcmludChwYXN0ZSgiTnVtYmVyIG9mIGZhbHNlIHBvc2l0aXZlcyBhdCBNSUMgKGZpdEUwMkQwNCA+IC0xKToiLCBucm93KGZhbHNlX3Bvc2l0aXZlc19NSUNfMTYpKSkKCiMgQ2FsY3VsYXRlIHRoZSBudW1iZXIgb2YgZmFsc2UgcG9zaXRpdmVzCm51bV9mYWxzZV9wb3NpdGl2ZXNfTUlDXzE2IDwtIG5yb3coZmFsc2VfcG9zaXRpdmVzX01JQ18xNikKI3ByaW50KHBhc3RlKCJOdW1iZXIgb2YgZmFsc2UgcG9zaXRpdmVzIGF0IE1JQzoiLCBudW1fZmFsc2VfcG9zaXRpdmVzX01JQ18xNikpCgojIENhbGN1bGF0ZSB0aGUgdG90YWwgbnVtYmVyIG9mIGVudHJpZXMgdGhhdCBtZWV0IHRoZSBjcml0ZXJpYQp0b3RhbF9jcml0ZXJpYV9tZXRfTUlDXzE2IDwtIG5yb3coZmlsdGVyZWRfbXV0YW50X2RhdGFfTUlDXzE2KQojcHJpbnQocGFzdGUoIlRvdGFsIG51bWJlciBvZiBlbnRyaWVzIG1lZXRpbmcgaW5pdGlhbCBjcml0ZXJpYSAoPiA0OSBtdXRhdGlvbnMpIGF0IE1JQzoiLCB0b3RhbF9jcml0ZXJpYV9tZXRfTUlDXzE2KSkKCiMgQ2FsY3VsYXRlIHRoZSBmYWxzZSBwb3NpdGl2ZSByYXRlCmZhbHNlX3Bvc2l0aXZlX3JhdGVfTUlDXzE2IDwtIChudW1fZmFsc2VfcG9zaXRpdmVzX01JQ18xNiAvIHRvdGFsX2NyaXRlcmlhX21ldF9NSUNfMTYpICogMTAwCgojIFByaW50IHRoZSBmYWxzZSBwb3NpdGl2ZSByYXRlCnByaW50KHBhc3RlKCJGYWxzZSBwb3NpdGl2ZSByYXRlIGF0IE1JQzoiLCByb3VuZChmYWxzZV9wb3NpdGl2ZV9yYXRlX01JQ18xNiwgMiksICIlIikpCmBgYAoKKio0MDB4IE1JQzoqKiBDYWxjdWxhdGUgZmFsc2UgcG9zaXRpdmUgcmF0ZSBmb3IgbXV0YW50IHZhcmlhbnRzID4gNTAgd2l0aCBmaXREMTFEMDMgPiAtMSBpbiBDb2RvbiAxOgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBGaWx0ZXIgdGhlIGRhdGEgZm9yIG11dGF0aW9ucyA+IDQ5IGFuZCByZW1vdmUgcm93cyB3aGVyZSBmaXREMDVEMDMgaXMgTkEKZmlsdGVyZWRfbXV0YW50X2RhdGFfNDAweE1JQ18xNiA8LSBtdXRhbnRzMTYgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyA+IDQ5LCAhaXMubmEoZml0RTA2RDA0KSkKcHJpbnQocGFzdGUoIk51bWJlciBvZiByb3dzIHdpdGggbXV0YXRpb25zID4gNDk6IiwgbnJvdyhmaWx0ZXJlZF9tdXRhbnRfZGF0YV80MDB4TUlDXzE2KSkpCgojIERlZmluZSBhIGxvZ2ljYWwgY29uZGl0aW9uIGZvciBmYWxzZSBwb3NpdGl2ZXMgKGZpdEUwNkQwNCA+IC0xIGFyZSBmYWxzZSBwb3NpdGl2ZXMpCmZhbHNlX3Bvc2l0aXZlc180MDB4TUlDXzE2IDwtIGZpbHRlcmVkX211dGFudF9kYXRhXzQwMHhNSUNfMTYgJT4lCiAgZmlsdGVyKGZpdEUwNkQwNCA+IC0xLCBudW1wcnVuZWRCQ3MgPiA1KQpwcmludChwYXN0ZSgiTnVtYmVyIG9mIGZhbHNlIHBvc2l0aXZlcyBhdCA0MDB4IE1JQyAoZml0RTA2RDA0ID4gLTEpOiIsIG5yb3coZmFsc2VfcG9zaXRpdmVzXzQwMHhNSUNfMTYpKSkKCiMgQ2FsY3VsYXRlIHRoZSBudW1iZXIgb2YgZmFsc2UgcG9zaXRpdmVzCm51bV9mYWxzZV9wb3NpdGl2ZXNfNDAweE1JQ18xNiA8LSBucm93KGZhbHNlX3Bvc2l0aXZlc180MDB4TUlDXzE2KQojcHJpbnQocGFzdGUoIk51bWJlciBvZiBmYWxzZSBwb3NpdGl2ZXMgYXQgNDAweCBNSUM6IiwgbnVtX2ZhbHNlX3Bvc2l0aXZlc180MDB4TUlDXzE2KSkKCiMgQ2FsY3VsYXRlIHRoZSB0b3RhbCBudW1iZXIgb2YgZW50cmllcyB0aGF0IG1lZXQgdGhlIGNyaXRlcmlhCnRvdGFsX2NyaXRlcmlhX21ldF80MDB4TUlDXzE2IDwtIG5yb3coZmlsdGVyZWRfbXV0YW50X2RhdGFfNDAweE1JQ18xNikKI3ByaW50KHBhc3RlKCJUb3RhbCBudW1iZXIgb2YgZW50cmllcyBtZWV0aW5nIGluaXRpYWwgY3JpdGVyaWEgKD4gNDkgbXV0YXRpb25zKSBhdCA0MDB4IE1JQzoiLCB0b3RhbF9jcml0ZXJpYV9tZXRfNDAweE1JQ18xNikpCgojIENhbGN1bGF0ZSB0aGUgZmFsc2UgcG9zaXRpdmUgcmF0ZQpmYWxzZV9wb3NpdGl2ZV9yYXRlXzQwMHhNSUNfMTYgPC0gKG51bV9mYWxzZV9wb3NpdGl2ZXNfNDAweE1JQ18xNiAvIHRvdGFsX2NyaXRlcmlhX21ldF80MDB4TUlDXzE2KSAqIDEwMAoKIyBQcmludCB0aGUgZmFsc2UgcG9zaXRpdmUgcmF0ZQpwcmludChwYXN0ZSgiRmFsc2UgcG9zaXRpdmUgcmF0ZSBhdCA0MDB4IE1JQzoiLCByb3VuZChmYWxzZV9wb3NpdGl2ZV9yYXRlXzQwMHhNSUNfMTYsIDIpLCAiJSIpKQpgYGAKCiMjIFJlc2lzdGFuY2UgTXV0YXRpb25zCgojIyMgTWVkaWFuIE11dGF0aW9ucwpIZXJlIHdlIGRldGVybWluZSBpZiB0aGUgcmVzaXN0YW50IG11dGFudHMgYXQgdGhlIGhpZ2hlc3QgVE1QIGNvbmNlbnRyYXRpb25zIHRlbmQgdG8gaGF2ZSBtb3JlIG11dGF0aW9ucyBjb21wYXJlZCB0byBtdXRhbnRzIGF0IGxvd2VyIFRNUCBsZXZlbHMuIFdoYXQgZG9lcyB0aGUgcmVsYXRpb25zaGlwIG9mIG1lZGlhbiBudW1iZXIgb2YgbXV0YXRpb25zIHZlcnN1cyBUTVAgY29uY2VudHJhdGlvbiBsb29rIGxpa2U/CmBgYHtyfQojIyMgTGliMTUgLSBDb2RvbiAxCgojIyBDb21wbGVtZW50YXRpb24KCiMgU3Vic2V0IHRoZSAnbXV0YW50czE1JyBkYXRhc2V0IHRvIHJldGFpbiBvbmx5IHRob3NlIHVuaXF1ZSBtdXRhbnRzIChtdXRJRCkgd2l0aCBmaXRuZXNzID4gLTEgYXQgZml0RDA1RDAzLiBGaWx0ZXIgb3V0IHBlcmZlY3RzOgpMMTVfcmVzaXN0X211dGFudHNfY29tcCA8LSBtdXRhbnRzMTUgJT4lCiAgZmlsdGVyKGZpdEQwNUQwMyA+IC0xKSAlPiUKICBmaWx0ZXIobXV0YXRpb25zID4gMCkgJT4lCiAgZGlzdGluY3QobXV0SUQsIC5rZWVwX2FsbCA9IFRSVUUpCgojIENvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIG11dElEIHJldGFpbmVkCkwxNV9yZXNpc3RfbXV0YW50c19jb21wX3VuaXF1ZSA8LSBuX2Rpc3RpbmN0KEwxNV9yZXNpc3RfbXV0YW50c19jb21wJG11dElEKQoKIyBOb3csIGNhbGN1bGF0ZSB0aGUgbWVkaWFuIG11dGF0aW9ucwpMMTVfcmVzaXN0X211dGFudHNfY29tcF9tZWRpYW4gPC0gbWVkaWFuKEwxNV9yZXNpc3RfbXV0YW50c19jb21wJG11dGF0aW9ucywgbmEucm0gPSBUUlVFKQoKIyMgTUlDCgojIFN1YnNldCB0aGUgJ211dGFudHMxNScgZGF0YXNldCB0byByZXRhaW4gb25seSB0aG9zZSB1bmlxdWUgbXV0YW50cyAobXV0SUQpIHdpdGggZml0bmVzcyA+IC0xIGF0IGZpdEQwNUQwMy4gRmlsdGVyIG91dCBwZXJmZWN0czoKTDE1X3Jlc2lzdF9tdXRhbnRzX21pYyA8LSBtdXRhbnRzMTUgJT4lCiAgZmlsdGVyKGZpdEQwN0QwMyA+IC0xKSAlPiUKICBmaWx0ZXIobXV0YXRpb25zID4gMCkgJT4lCiAgZGlzdGluY3QobXV0SUQsIC5rZWVwX2FsbCA9IFRSVUUpCgojIENvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIG11dElEIHJldGFpbmVkCkwxNV9yZXNpc3RfbXV0YW50c19taWNfdW5pcXVlIDwtIG5fZGlzdGluY3QoTDE1X3Jlc2lzdF9tdXRhbnRzX21pYyRtdXRJRCkKCiMgTm93LCBjYWxjdWxhdGUgdGhlIG1lZGlhbiBtdXRhdGlvbnMKTDE1X3Jlc2lzdF9tdXRhbnRzX21pY19tZWRpYW4gPC0gbWVkaWFuKEwxNV9yZXNpc3RfbXV0YW50c19taWMkbXV0YXRpb25zLCBuYS5ybSA9IFRSVUUpCgojIyA0MDB4IE1JQwoKIyBTdWJzZXQgdGhlICdtdXRhbnRzMTUnIGRhdGFzZXQgdG8gcmV0YWluIG9ubHkgdGhvc2UgdW5pcXVlIG11dGFudHMgKG11dElEKSB3aXRoIGZpdG5lc3MgPiAtMSBhdCBmaXREMDVEMDMuIEZpbHRlciBvdXQgcGVyZmVjdHM6CkwxNV9yZXNpc3RfbXV0YW50c180MDB4bWljIDwtIG11dGFudHMxNSAlPiUKICBmaWx0ZXIoZml0RDExRDAzID4gLTEpICU+JQogIGZpbHRlcihtdXRhdGlvbnMgPiAwKSAlPiUKICBkaXN0aW5jdChtdXRJRCwgLmtlZXBfYWxsID0gVFJVRSkKCiMgQ291bnQgdGhlIG51bWJlciBvZiB1bmlxdWUgbXV0SUQgcmV0YWluZWQKTDE1X3Jlc2lzdF9tdXRhbnRzXzQwMHhtaWNfdW5pcXVlIDwtIG5fZGlzdGluY3QoTDE1X3Jlc2lzdF9tdXRhbnRzXzQwMHhtaWMkbXV0SUQpCgojIE5vdywgY2FsY3VsYXRlIHRoZSBtZWRpYW4gbXV0YXRpb25zCkwxNV9yZXNpc3RfbXV0YW50c180MDB4bWljX21lZGlhbiA8LSBtZWRpYW4oTDE1X3Jlc2lzdF9tdXRhbnRzXzQwMHhtaWMkbXV0YXRpb25zLCBuYS5ybSA9IFRSVUUpCgojIFByaW50IHRoZSByZXN1bHQKCnByaW50KHBhc3RlKCJUaGUgbnVtYmVyIG9mIHVuaXF1ZSByZXNpc3RhbnQgbXV0SUQgYXQgQ29tcGxlbWVudGF0aW9uIGlzOiIsIEwxNV9yZXNpc3RfbXV0YW50c19jb21wX3VuaXF1ZSkpCnByaW50KHBhc3RlKCJUaGUgbnVtYmVyIG9mIHVuaXF1ZSByZXNpc3RhbnQgbXV0SUQgYXQgTUlDIGlzOiIsIEwxNV9yZXNpc3RfbXV0YW50c19taWNfdW5pcXVlKSkKcHJpbnQocGFzdGUoIlRoZSBudW1iZXIgb2YgdW5pcXVlIHJlc2lzdGFudCBtdXRJRCBhdCA0MDB4IGlzOiIsIEwxNV9yZXNpc3RfbXV0YW50c180MDB4bWljX3VuaXF1ZSkpCgpwcmludChwYXN0ZSgiVGhlIG1lZGlhbiBudW1iZXIgb2YgbXV0YXRpb25zIGF0IENvbXBsZW1lbnRhdGlvbiBpczoiLCBMMTVfcmVzaXN0X211dGFudHNfY29tcF9tZWRpYW4pKQpwcmludChwYXN0ZSgiVGhlIG1lZGlhbiBudW1iZXIgb2YgbXV0YXRpb25zIGF0IE1JQyBpczoiLCBMMTVfcmVzaXN0X211dGFudHNfbWljX21lZGlhbikpCnByaW50KHBhc3RlKCJUaGUgbWVkaWFuIG51bWJlciBvZiBtdXRhdGlvbnMgYXQgNDAweCBNSUMgaXM6IiwgTDE1X3Jlc2lzdF9tdXRhbnRzXzQwMHhtaWNfbWVkaWFuKSkKYGBgCgojIyMgUGxvdHRpbmcgTXV0YXRpb24gRGlzdHJpYnV0aW9ucwpgYGB7cn0KIyBDcmVhdGUgYmlucyBmb3IgbXV0YXRpb25zCm1heF9tdXRhdGlvbnMgPC0gbWF4KEwxNV9yZXNpc3RfbXV0YW50c19jb21wJG11dGF0aW9ucywgbmEucm0gPSBUUlVFKQpicmVha3MgPC0gc2VxKDAsIG1heF9tdXRhdGlvbnMgKyAxMCwgYnkgPSAxMCkKCiMgQWRqdXN0IGxhYmVscyB0byBtYXRjaCB0aGUgbnVtYmVyIG9mIGJpbnMKbGFiZWxzIDwtIHBhc3RlKGhlYWQoYnJlYWtzLCAtMSkgKyAxLCB0YWlsKGJyZWFrcywgLTEpLCBzZXAgPSAiLSIpCgpMMTVfcmVzaXN0X211dGFudHNfY29tcF9iaW5zIDwtIEwxNV9yZXNpc3RfbXV0YW50c19jb21wICU+JQogIG11dGF0ZShtdXRhdGlvbl9iaW5zID0gY3V0KG11dGF0aW9ucywgYnJlYWtzID0gYnJlYWtzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmlnaHQgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGxhYmVscykpCgojIFBsb3QgdGhlIGRpc3RyaWJ1dGlvbiBvZiBtdXRhdGlvbnMKTDE1X3Jlc2lzdF9tdXRhbnRzX2NvbXBfYmluc19wbG90IDwtIGdncGxvdChMMTVfcmVzaXN0X211dGFudHNfY29tcF9iaW5zLCBhZXMoeCA9IG11dGF0aW9uX2JpbnMpKSArCiAgZ2VvbV9iYXIoZmlsbCA9ICJza3libHVlIiwgY29sb3IgPSAiYmxhY2siKSArCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgTXV0YXRpb25zIEFmdGVyIEZpbHRlcmluZyIsCiAgICAgICB4ID0gIk51bWJlciBvZiBNdXRhdGlvbnMgKEJpbm5lZCkiLAogICAgICAgeSA9ICJDb3VudCBvZiBVbmlxdWUgbXV0SUQiKSArCiAgdGhlbWVfbWluaW1hbCgpCgpwcmludChMMTVfcmVzaXN0X211dGFudHNfY29tcF9iaW5zX3Bsb3QpCmBgYAoKYGBge3J9CiMgRmlsdGVyIGZvciBtdXRhdGlvbnMgYmV0d2VlbiAxIGFuZCAxMApMMTVfcmVzaXN0X211dGFudHNfY29tcF8xXzEwX2ZpbHRlcmVkIDwtIEwxNV9yZXNpc3RfbXV0YW50c19jb21wICU+JQogIGZpbHRlcihtdXRhdGlvbnMgPj0gMSAmIG11dGF0aW9ucyA8PSAxMCkKCiMgUGxvdCB0aGUgZGlzdHJpYnV0aW9uIG9mIG11dGF0aW9ucyBiZXR3ZWVuIDEgYW5kIDEwCkwxNV9yZXNpc3RfbXV0YW50c19jb21wXzFfMTBfZmlsdGVyZWRfcGxvdCA8LSBnZ3Bsb3QoTDE1X3Jlc2lzdF9tdXRhbnRzX2NvbXBfMV8xMF9maWx0ZXJlZCwgYWVzKHggPSBtdXRhdGlvbnMpKSArCiAgZ2VvbV9iYXIoZmlsbCA9ICJza3libHVlIiwgY29sb3IgPSAiYmxhY2siLCB3aWR0aCA9IDAuNykgKwogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIE11dGF0aW9ucyAoMSB0byAxMCkiLAogICAgICAgeCA9ICJOdW1iZXIgb2YgTXV0YXRpb25zIiwKICAgICAgIHkgPSAiQ291bnQgb2YgVW5pcXVlIG11dElEIikgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMSwgMTAsIGJ5ID0gMSkpICsgIyBTZXQgeC1heGlzIGJyZWFrcyBmb3IgY2xhcml0eQogIHRoZW1lX21pbmltYWwoKQoKcHJpbnQoTDE1X3Jlc2lzdF9tdXRhbnRzX2NvbXBfMV8xMF9maWx0ZXJlZF9wbG90KQpgYGAKCmBgYHtyfQpwYXRjaDEyIDwtIEwxNV9yZXNpc3RfbXV0YW50c19jb21wX2JpbnNfcGxvdCAvIEwxNV9yZXNpc3RfbXV0YW50c19jb21wXzFfMTBfZmlsdGVyZWRfcGxvdApwYXRjaDEyCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KZ2dzYXZlKGZpbGU9Ik11dGFudHMvUExPVFMvTGliMTUucmVzaXN0YW50Lm11dGF0aW9ucy5kaXN0cmlidXRpb24uY29tcGxlbWVudGF0aW9uLnBuZyIsCiAgICAgICBwbG90PXBhdGNoMTIsCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSAxMCwgdW5pdHMgPSAiaW4iKQpgYGAKCiMjIyBNdXRhdGlvbiBSYXRpbwpUbyBjYWxjdWxhdGUgdGhlIHJhdGlvIG9mIHNpbmdsZSBtdXRhdGlvbnMgKHdoZXJlIG11dGF0aW9ucyA9PSAxKSB0byB0aGUgbnVtYmVyIG9mIG11dGFudHMgd2l0aCAyIHRvIDEwIG11dGF0aW9ucywgeW91IGNhbiBmb2xsb3cgdGhlc2Ugc3RlcHMgaW4gUjoKICAtIENvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIG11dGFudHMgd2l0aCBleGFjdGx5IG9uZSBtdXRhdGlvbi4KICAtIENvdW50IHRoZSBudW1iZXIgb2YgdW5pcXVlIG11dGFudHMgd2l0aCBtdXRhdGlvbnMgYmV0d2VlbiAyIGFuZCAxMC4KICAtIENhbGN1bGF0ZSB0aGUgcmF0aW8uCgpgYGB7cn0KIyMjIENvbXAKCiMgQ291bnQgc2luZ2xlIG11dGF0aW9ucyAobXV0YXRpb25zID09IDEpCnNpbmdsZV9tdXRhdGlvbnNfY29tcF9jb3VudCA8LSBMMTVfcmVzaXN0X211dGFudHNfY29tcCAlPiUKICBmaWx0ZXIobXV0YXRpb25zID09IDEpICU+JQogIHB1bGwobXV0SUQpICU+JQogIG5fZGlzdGluY3QoKQoKIyBDb3VudCBtdXRhbnRzIHdpdGggbXV0YXRpb25zIGJldHdlZW4gMiBhbmQgMTAKbXV0YW50c18yX3RvXzEwX2NvbXBfY291bnQgPC0gTDE1X3Jlc2lzdF9tdXRhbnRzX2NvbXAgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyA+PSAyICYgbXV0YXRpb25zIDw9IDEwKSAlPiUKICBwdWxsKG11dElEKSAlPiUKICBuX2Rpc3RpbmN0KCkKCiMgQ2FsY3VsYXRlIHRoZSByYXRpbwppZiAobXV0YW50c18yX3RvXzEwX2NvbXBfY291bnQgPiAwKSB7CiAgbXV0YXRpb25fcmF0aW9fY29tcCA8LSBzaW5nbGVfbXV0YXRpb25zX2NvbXBfY291bnQgLyBtdXRhbnRzXzJfdG9fMTBfY29tcF9jb3VudAp9IGVsc2UgewogIG11dGF0aW9uX3JhdGlvX2NvbXAgPC0gTkEgIyBBdm9pZCBkaXZpc2lvbiBieSB6ZXJvIGlmIHRoZXJlIGFyZSBubyBtdXRhbnRzIHdpdGggMi0xMCBtdXRhdGlvbnMKfQoKIyMjIE1JQwoKIyBDb3VudCBzaW5nbGUgbXV0YXRpb25zIChtdXRhdGlvbnMgPT0gMSkKc2luZ2xlX211dGF0aW9uc19taWNfY291bnQgPC0gTDE1X3Jlc2lzdF9tdXRhbnRzX21pYyAlPiUKICBmaWx0ZXIobXV0YXRpb25zID09IDEpICU+JQogIHB1bGwobXV0SUQpICU+JQogIG5fZGlzdGluY3QoKQoKIyBDb3VudCBtdXRhbnRzIHdpdGggbXV0YXRpb25zIGJldHdlZW4gMiBhbmQgMTAKbXV0YW50c18yX3RvXzEwX21pY19jb3VudCA8LSBMMTVfcmVzaXN0X211dGFudHNfbWljICU+JQogIGZpbHRlcihtdXRhdGlvbnMgPj0gMiAmIG11dGF0aW9ucyA8PSAxMCkgJT4lCiAgcHVsbChtdXRJRCkgJT4lCiAgbl9kaXN0aW5jdCgpCgojIENhbGN1bGF0ZSB0aGUgcmF0aW8KaWYgKG11dGFudHNfMl90b18xMF9taWNfY291bnQgPiAwKSB7CiAgbXV0YXRpb25fcmF0aW9fbWljIDwtIHNpbmdsZV9tdXRhdGlvbnNfbWljX2NvdW50IC8gbXV0YW50c18yX3RvXzEwX21pY19jb3VudAp9IGVsc2UgewogIG11dGF0aW9uX3JhdGlvX21pYyA8LSBOQSAjIEF2b2lkIGRpdmlzaW9uIGJ5IHplcm8gaWYgdGhlcmUgYXJlIG5vIG11dGFudHMgd2l0aCAyLTEwIG11dGF0aW9ucwp9CgojIyMgNDAweCBNSUMKCiMgQ291bnQgc2luZ2xlIG11dGF0aW9ucyAobXV0YXRpb25zID09IDEpCnNpbmdsZV9tdXRhdGlvbnNfNDAweG1pY19jb3VudCA8LSBMMTVfcmVzaXN0X211dGFudHNfNDAweG1pYyAlPiUKICBmaWx0ZXIobXV0YXRpb25zID09IDEpICU+JQogIHB1bGwobXV0SUQpICU+JQogIG5fZGlzdGluY3QoKQoKIyBDb3VudCBtdXRhbnRzIHdpdGggbXV0YXRpb25zIGJldHdlZW4gMiBhbmQgMTAKbXV0YW50c18yX3RvXzEwXzQwMHhtaWNfY291bnQgPC0gTDE1X3Jlc2lzdF9tdXRhbnRzXzQwMHhtaWMgJT4lCiAgZmlsdGVyKG11dGF0aW9ucyA+PSAyICYgbXV0YXRpb25zIDw9IDEwKSAlPiUKICBwdWxsKG11dElEKSAlPiUKICBuX2Rpc3RpbmN0KCkKCiMgQ2FsY3VsYXRlIHRoZSByYXRpbwppZiAobXV0YW50c18yX3RvXzEwXzQwMHhtaWNfY291bnQgPiAwKSB7CiAgbXV0YXRpb25fcmF0aW9fNDAweG1pYyA8LSBzaW5nbGVfbXV0YXRpb25zXzQwMHhtaWNfY291bnQgLyBtdXRhbnRzXzJfdG9fMTBfNDAweG1pY19jb3VudAp9IGVsc2UgewogIG11dGF0aW9uX3JhdGlvXzQwMHhtaWMgPC0gTkEgIyBBdm9pZCBkaXZpc2lvbiBieSB6ZXJvIGlmIHRoZXJlIGFyZSBubyBtdXRhbnRzIHdpdGggMi0xMCBtdXRhdGlvbnMKfQoKCgojIFByaW50IHRoZSByZXN1bHQKcHJpbnQocGFzdGUoIlRoZSByYXRpbyBvZiBzaW5nbGUgbXV0YXRpb25zIHRvIG11dGFudHMgd2l0aCAyLTEwIG11dGF0aW9ucyBhdCBDb21wbGVtZW50YXRpb24gaXM6IiwgbXV0YXRpb25fcmF0aW9fY29tcCkpCnByaW50KHBhc3RlKCJUaGUgcmF0aW8gb2Ygc2luZ2xlIG11dGF0aW9ucyB0byBtdXRhbnRzIHdpdGggMi0xMCBtdXRhdGlvbnMgYXQgTUlDIGlzOiIsIG11dGF0aW9uX3JhdGlvX21pYykpCnByaW50KHBhc3RlKCJUaGUgcmF0aW8gb2Ygc2luZ2xlIG11dGF0aW9ucyB0byBtdXRhbnRzIHdpdGggMi0xMCBtdXRhdGlvbnMgYXQgNDAweCBNSUMgaXM6IiwgbXV0YXRpb25fcmF0aW9fNDAweG1pYykpCmBgYAogIAojIFNhdmUgTXV0YW50cyBGaWxlcwoKU2F2ZSB0aGUgZm9ybWF0dGVkIG11dGFudHMgZmlsZXMgdG8gaW1wb3J0IGZvciBkb3duc3RyZWFtIGFuYWx5c2VzCmBgYHtyfQojIG11dF9jb2xsYXBzZV8xNQp3cml0ZS5jc3YobXV0X2NvbGxhcHNlXzE1LCAKICAgICAgICAgICJNdXRhbnRzL211dGFudHNfZmlsZXNfZm9ybWF0dGVkL211dF9jb2xsYXBzZV8xNS5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCiMgbXV0X2NvbGxhcHNlXzE1X2dvb2RfZmlsdGVyZWQgKDIuNSBNQikKd3JpdGUuY3N2KG11dF9jb2xsYXBzZV8xNV9nb29kX2ZpbHRlcmVkLCAKICAgICAgICAgICJNdXRhbnRzL211dGFudHNfZmlsZXNfZm9ybWF0dGVkL211dF9jb2xsYXBzZV8xNV9nb29kX2ZpbHRlcmVkLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQoKIyBBbGx0cmVlMTVfdGF4YV9tZXJnZWQgKDYxMSBLQikKd3JpdGUuY3N2KEFsbHRyZWUxNV90YXhhX21lcmdlZCwgCiAgICAgICAgICAiTXV0YW50cy9tdXRhbnRzX2ZpbGVzX2Zvcm1hdHRlZC9BbGx0cmVlMTVfdGF4YV9tZXJnZWQuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCgojIHBlcmZlY3RzXzE1XzE2XzVCQ3NfdHJlZSAoMzE5IEtCKQp3cml0ZS5jc3YocGVyZmVjdHNfMTVfMTZfNUJDc190cmVlLCAKICAgICAgICAgICJNdXRhbnRzL211dGFudHNfZmlsZXNfZm9ybWF0dGVkL3BlcmZlY3RzXzE1XzE2XzVCQ3NfdHJlZS5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCiMgb3JnaW5mbyAoMi4xIE1CKQp3cml0ZS5jc3Yob3JnaW5mbywgCiAgICAgICAgICAiTXV0YW50cy9tdXRhbnRzX2ZpbGVzX2Zvcm1hdHRlZC9vcmdpbmZvLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKCiMgUmVwcm9kdWNpYmlsaXR5CgpUaGUgc2Vzc2lvbiBpbmZvcm1hdGlvbiBpcyBwcm92aWRlZCBmb3IgZnVsbCByZXByb2R1Y2liaWxpdHkuCmBgYHtyfQpkZXZ0b29sczo6c2Vzc2lvbl9pbmZvKCkKYGBg