R Notebook: Provides reproducible analysis for Dial-Out Variant Fitness in the following manuscript:

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

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", "broom", "castor", "cowplot", "devtools", "dplyr", "ggExtra", "ggnewscale", "ggplot2", "ggridges", "ggtree", "ggtreeExtra", "glmnet", "gridExtra","igraph", "knitr", "lubridate", "matrixStats", "patchwork", "pheatmap", "purrr", "pscl", "RColorBrewer", "readxl", "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, broom, castor, cowplot, devtools, dplyr, ggExtra, ggnewscale, ggplot2, ggridges, ggtree, ggtreeExtra, glmnet, gridExtra, igraph, knitr, lubridate, matrixStats, patchwork, pheatmap, purrr, pscl, RColorBrewer, readxl, reshape, reshape2, ROCR, seqinr, scales, stringr, stringi, tidyr, tidytree, viridis 

Import Data Files

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

# BCcontrols_15_median
BCcontrols_15_median <- read.csv("Count/count_files_formatted/BCcontrols_15_median.csv", 
                         header = TRUE, stringsAsFactors = FALSE)

Import PERFECTS files generated from DHFR.3.Perfects.RMD relevant for downstream analysis.

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

Dialout Analysis

Import Excel Files

This is the file with the plate reader data:

# REPLACE THIS FILE NAME FOR EACH ANALYSIS
filename_of_plate_reader_data = "Dialout/RAW/02-08-24_KR_DHFR_Fitness_Assay_M9_TMP.Plate4.Rnd2.xlsx"

#M9-Full Plate 1: "01-24-24_KR_DHFR_Fitness_Assay_M9_Full_Supp.Plate1.xlsx"
#M9-TMP Plate 2: "01-31-24_KR_DHFR_Fitness_Assay_M9_TMP.Plate2.xlsx"
#M9-TMP Plate 3: "02-01-24_KR_DHFR_Fitness_Assay_M9_TMP.Plate3.xlsx"
#M9-TMP Plate 4: "02-02-24_KR_DHFR_Fitness_Assay_M9_TMP.Plate4.xlsx"

#Re-do of M9-TMP Plates:
#M9-TMP Plate 2: "02-06-24_KR_DHFR_Fitness_Assay_M9_TMP.Plate2.Rnd2.xlsx"
#M9-TMP Plate 3: "02-07-24_KR_DHFR_Fitness_Assay_M9_TMP.Plate3.Rnd2.xlsx"
#M9-TMP Plate 4: "02-08-24_KR_DHFR_Fitness_Assay_M9_TMP.Plate4.Rnd2.xlsx"

This is the range of data in the excel file to import

# UPDATE THIS DATA RANGE FOR EACH EXCEL FILE YOU IMPORT
range_od = "B41:CI282"

#M9-Full Plate 1 OD-range: B41:AM282
#M9-TMP Plate 2 OD-range: B41:CI276
#M9-TMP Plate 3 OD-range: B41:CI279
#M9-TMP Plate 4 OD-range: B41:CI282

#Re-do of M9-TMP Plates:
#M9-TMP Plate 2 OD-range: B41:CI282
#M9-TMP Plate 3 OD-range: B41:CI276
#M9-TMP Plate 4 OD-range: B41:CI282
#get the data
od_data1 <- as.data.frame(read_excel(filename_of_plate_reader_data, sheet = 1, range = range_od))
#fix the time (makes minutes from start)
od_data <- od_data1 %>%
  mutate(time_int=as.duration(interval(start = od_data1$Time[1], end=Time))) %>%
  mutate(round_time=ceiling(as.numeric(time_int)/60)) %>%
  subset(select = -Time) %>%
  subset(select = -time_int) %>%
  relocate(round_time) %>%
  dplyr::rename(Time=round_time) %>%
  t() %>%
  as.data.frame(.)
#make vector variables for plotting
M = nrow(od_data)
N = ncol(od_data)
#make a vector of your time points
ODtime <- od_data[1,1:N]

Sample Growth Rate Plots

This command loads the script (find_gr.R) that contains the analysis functions. This file needs to be in the present working directory.

source("Dialout/Scripts/find_gr.R")
#define initial growth rates
growth.rates = NULL
#this variable will store all the data
gr_table <- data.frame(sample_name=character(), 
                       doubling.time=double(), 
                       m=double(),
                       r2=double(),
                       lag.t=double())
#convert time_vector to numeric variable
time_vector = as.numeric(ODtime[1,])

Generate growth rate plots from each sampling well and save as .png files:

#run through all samples and fit
for (i in 2:M) {
  print(i)
  sample_nm <- row.names(od_data)[i]
  
  # Clean the sample name to remove or replace problematic characters
  clean_sample_nm <- gsub("[^[:alnum:]_-]", "_", sample_nm)
  
  # Open a PNG device with the cleaned filename
  png_filename <- file.path("Dialout/PLOTS", paste0(clean_sample_nm, "_growth_rate_plot.png"))
  png(filename = png_filename, width = 800, height = 600)
  
  #this is where the fit happens:
  gr <- findgr(od_data[i, 1:N], time_vector, sample_nm, int=30, r2=0.6) 
  
  de <- list(sample_name=sample_nm, 
             doubling.time=log2(2)/gr["m"], 
             m=gr["m"],
             r2=gr["r2"],
             lag.t=gr["lag.t"])
  
  #add this to previously calculated wells
  gr_table <- rbind(gr_table, de, stringsAsFactors=FALSE)
  growth.rates <- rbind(growth.rates, gr)
  
  # Close the PNG device
  dev.off()
}
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
[1] 11
[1] 12
[1] 13
[1] 14
[1] 15
[1] 16
[1] 17
[1] 18
[1] 19
[1] 20
[1] 21
[1] 22
[1] 23
[1] 24
[1] 25
[1] 26
[1] 27
[1] 28
[1] 29
[1] 30
[1] 31
[1] 32
[1] 33
[1] 34
[1] 35
[1] 36
[1] 37
[1] 38
[1] 39
[1] 40
[1] 41
[1] 42
[1] 43
[1] 44
[1] 45
[1] 46
[1] 47
[1] 48
[1] 49
[1] 50
[1] 51
[1] 52
[1] 53
[1] 54
[1] 55
[1] 56
[1] 57
[1] 58
[1] 59
[1] 60
[1] 61
[1] 62
[1] 63
[1] 64
[1] 65
[1] 66
[1] 67
[1] 68
[1] 69
[1] 70
[1] 71
[1] 72
[1] 73
[1] 74
[1] 75
[1] 76
[1] 77
[1] 78
[1] 79
[1] 80
[1] 81
[1] 82
[1] 83
[1] 84
[1] 85
[1] 86

Display the plots in the RMD:

#run through all samples and fit
for (i in 2:M) {
  print(i)
  sample_nm=row.names(od_data)[i]
  
  #this is where the fit happens:
  gr <- findgr(od_data[i, 1:N], time_vector, sample_nm, int=30, r2=0.6) 
  #1 in od_data[i, 1:N] is the column number where the data starts;  
  #int is number of points taken at one time as an interval to find the highest slope; 
  #vary (i.e. lower) r2, i.e. rsquared as needed, blanks can be a problem here
  
  #print(gr["m"])
  de <- list(sample_name=sample_nm, 
             doubling.time=log2(2)/gr["m"], 
             m=gr["m"],
             r2=gr["r2"],
             lag.t=gr["lag.t"])
  #print(de)
  
  #add this to previously calculated wells
  gr_table = rbind(gr_table,de, stringsAsFactors=FALSE)
  growth.rates <- rbind(growth.rates, gr)
}
[1] 2
[1] 3

[1] 4

[1] 5

[1] 6

[1] 7

[1] 8

[1] 9

[1] 10

[1] 11

[1] 12

[1] 13

[1] 14

[1] 15

[1] 16

[1] 17

[1] 18

[1] 19

[1] 20

[1] 21

[1] 22

[1] 23

[1] 24

[1] 25

[1] 26

[1] 27

[1] 28

[1] 29

[1] 30

[1] 31

[1] 32

[1] 33

[1] 34

[1] 35
Warning: essentially perfect fit: summary may be unreliableWarning: essentially perfect fit: summary may be unreliableWarning: essentially perfect fit: summary may be unreliableWarning: essentially perfect fit: summary may be unreliableWarning: essentially perfect fit: summary may be unreliableWarning: essentially perfect fit: summary may be unreliableWarning: essentially perfect fit: summary may be unreliable

[1] 36

[1] 37

[1] 38

[1] 39

[1] 40

[1] 41

[1] 42

[1] 43

[1] 44

[1] 45

[1] 46

[1] 47

[1] 48

[1] 49

[1] 50

[1] 51

[1] 52

[1] 53

[1] 54

[1] 55

[1] 56

[1] 57

[1] 58

[1] 59

[1] 60

[1] 61

[1] 62

[1] 63

[1] 64

[1] 65

[1] 66

[1] 67

[1] 68

[1] 69

[1] 70

[1] 71

[1] 72

[1] 73

[1] 74

[1] 75

[1] 76

[1] 77

[1] 78

[1] 79

[1] 80

[1] 81

[1] 82

[1] 83

[1] 84

[1] 85

[1] 86

dev.off()
null device 
          1 

Write the data to a file

# UPDATE THE TABLE NAME IF YOU RE-RUN MULTIPLE EXCEL FILES

# Substitute your desired file name for 'growth_rates'
write.table(gr_table, "Dialout/OUTPUT/growth_rates_plate4.txt", sep="\t", quote=F, row.names=F) 

Full Growth Rate Plot

Load the final fitness data for all 12 Dialout samples based on calculated growth rates:

# For 12 samples (including WT and mCherry controls)
grwell <- read.table(file = 'Dialout/Raw/growth_rates_all.v2.csv', sep =',', header = TRUE)

Reshape the data for plotting:

# Convert Construct to a factor with desired levels
grwell$Media <- factor(grwell$Media, levels = c("M9-Supp", "0-tmp", "0.058-tmp", "0.5-tmp", "1.0-tmp", "10-tmp", "50-tmp", "200-tmp"))

# Define the 12 samples
grwell$Construct <- factor(grwell$Construct, levels = c("NP_267306", "WP_000637209", "WP_008976421",
                                                        "WP_003776922", "WP_008578924", "WP_002897636",
                                                        "WP_000162453", "WP_003012456", "WP_000162462",
                                                        "WP_003027976", "WT-DHFR", "mCherry"))

Growth Rate Plot for DHFR Dialout Variants:

# Convert Media to a factor with desired levels
grwell$Media <- factor(grwell$Media, levels = c("M9-Supp", "0-tmp", "0.058-tmp", "0.5-tmp", "1.0-tmp", "10-tmp", "50-tmp", "200-tmp"))

# Define the 12 samples in the desired order
construct_order <- c("NP_267306", "WP_000637209", "WP_008976421", "WP_003776922", "WP_008578924", "WP_002897636", "WP_000162453", "WP_003012456", "WP_000162462", "WP_003027976", "WT-DHFR", "mCherry")

grwell$Construct <- factor(grwell$Construct, levels = construct_order)

# Create a named vector for facet colors
facet_colors <- setNames(c(rep("lightgray", 10), "blue", "red"), construct_order)

# Custom function to reorder facets
reorder_facets <- function(x) factor(x, levels = construct_order)

# Calculate the overall y-axis range
y_min <- min(grwell$m, na.rm = TRUE)
y_max <- max(grwell$m, na.rm = TRUE)

# Calculate mean growth rate for each Construct-Media combination
grwell_mean <- grwell %>%
  group_by(Construct, Media) %>%
  summarize(mean_m = mean(m, na.rm = TRUE), .groups = "drop")

Growth Rate Plot

# Growth rate plot
fitness.plot.v3 <- ggplot(grwell_mean, aes(x = Media, y = mean_m, group = Construct)) +
  geom_point() +
  geom_line() +
  labs(title = "Growth Rate Assay for DHFR Dial-Out Variants") +
  facet_wrap(~ reorder_facets(Construct), scales = "fixed", ncol = 4) +
  theme_cowplot(16) +
  theme(
    title = element_text(color = "black", face = "bold", size=14),
    axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1, size=10),
    strip.background = element_blank(),
    strip.text = element_text(color = "black", face = "bold", size=10),
    panel.spacing = unit(0.5, "lines"),
    axis.text.y = element_text(size = 10),
    axis.title.y = element_text(size = 12),
    panel.border = element_blank(),
    axis.line = element_line(color = "black"),
    legend.position = "none"
  ) +
  ylab(expression("Mean Growth Rate (min"^"-1"*")")) +
  xlab("Media") +
  coord_cartesian(ylim = c(y_min, y_max))

# Add colored backgrounds for all facets
fitness.plot.v3 <- fitness.plot.v3 +
  geom_rect(data = data.frame(Construct = construct_order),
            aes(fill = Construct),
            xmin = -Inf, xmax = Inf, ymin = -Inf, ymax = Inf,
            alpha = 0.2, inherit.aes = FALSE) +
  scale_fill_manual(values = facet_colors, guide = "none")

# Remove y-axis labels for all but the left-most facets
fitness.plot.v3 <- fitness.plot.v3 +
  theme(
    axis.text.y = element_blank(),
    axis.ticks.y = element_blank(),
    axis.text.y.left = element_text(size = 10),
    axis.ticks.y.left = element_line(),
    strip.text.x = element_text(size = 10, margin = margin(b = 5, t = 5))
  )

# Print the plot
print(fitness.plot.v3)

Calculate a Mean growth rate value at each TMP concentration for each dialout variant:

# Create the summary table
grwell_summary <- grwell_mean %>%
  pivot_wider(
    names_from = Media,
    values_from = mean_m
  ) %>%
  select(Construct, `M9-Supp`, `0-tmp`, `0.058-tmp`, `0.5-tmp`, `1.0-tmp`, `10-tmp`, `50-tmp`, `200-tmp`) %>%
  arrange(factor(Construct, levels = construct_order))

# Round all numeric columns to 6 decimal places
grwell_summary <- grwell_summary %>%
  mutate(across(where(is.numeric), ~round(., 6)))

# Display the table
kable(grwell_summary, format = "markdown", caption = "Summary of Mean Growth Rates (1/min) for DHFR Variants")
Summary of Mean Growth Rates (1/min) for DHFR Variants
Construct M9-Supp 0-tmp 0.058-tmp 0.5-tmp 1.0-tmp 10-tmp 50-tmp 200-tmp
NP_267306 0.004930 0.003574 0.003481 0.003474 0.003458 0.003451 0.003261 0.002857
WP_000637209 0.005100 0.003875 0.003450 0.003535 0.003430 0.003372 0.003391 0.002943
WP_008976421 0.005272 0.003704 0.003665 0.003435 0.003079 0.000975 0.000456 0.000000
WP_003776922 0.002708 0.000623 0.000280 0.000072 0.000000 0.000104 0.000000 0.000000
WP_008578924 0.004743 0.003655 0.003617 0.003613 0.003576 0.003520 0.003415 0.003176
WP_002897636 0.004477 0.003861 0.003834 0.003860 0.003842 0.003850 0.003693 0.003384
WP_000162453 0.004793 0.003854 0.003811 0.003811 0.003695 0.003689 0.003466 0.002736
WP_003012456 0.005081 0.003778 0.003558 0.003158 0.002784 0.000652 0.000390 0.000107
WP_000162462 0.004432 0.003734 0.003724 0.003697 0.003642 0.003245 0.002384 0.001157
WP_003027976 0.005033 0.003697 0.003533 0.003527 0.003564 0.003417 0.003113 0.002163
WT-DHFR 0.004638 0.003346 0.001960 0.000526 0.000311 0.000103 0.000000 0.000000
mCherry 0.000000 0.000000 0.000000 0.000000 0.000083 0.000275 0.000000 0.000000

Dialout Variant Fitness

Start by pulling out the Dialout variants from the perfects15_5BCs dataset:

# Specify the mutID values you want to keep
dialout_mutIDs <- c("NP_267306", "WP_000637209", "WP_008976421", "WP_003776922", "WP_008578924",
                    "WP_002897636", "WP_000162453", "WP_003012456", "WP_000162462", "WP_003027976")

# Subset the dataframe, keeping only columns 1:9
dialout_perfects15_5BCs <- perfects15_5BCs[perfects15_5BCs$mutID %in% dialout_mutIDs, 1:9]

# Rename the columns
colnames(dialout_perfects15_5BCs) <- c("mutID","M9-Supp", "0-tmp", "0.058-tmp", "0.5-tmp", "1.0-tmp", "10-tmp", "50-tmp", "200-tmp")

Add the Controls fitness to the Dialout fitness dataset:

# Rename controls columns
colnames(BCcontrols_15_median) <- c("mutID", "0-tmp", "0.058-tmp", "0.5-tmp", "1.0-tmp", "10-tmp", "50-tmp", "200-tmp")

# First, add the M9-Supp column with NA values to BCcontrols_15_median
BCcontrols_15_median <- BCcontrols_15_median %>%
  mutate(`M9-Supp` = NA)

# Now merge the two dataframes
dialout_ctrls_fitness <- bind_rows(
  dialout_perfects15_5BCs,
  BCcontrols_15_median)

# Reorder columns to match the original order
column_order <- c("mutID", "M9-Supp", "0-tmp", "0.058-tmp", "0.5-tmp", "1.0-tmp", "10-tmp", "50-tmp", "200-tmp")
dialout_ctrls_fitness <- dialout_ctrls_fitness %>%
  select(all_of(column_order))

# Remove the row where mutID is "D27N"
dialout_ctrls_fitness <- dialout_ctrls_fitness %>%
  filter(mutID != "D27N")

# Rename "WT" to "WT-DHFR"
dialout_ctrls_fitness <- dialout_ctrls_fitness %>%
  mutate(mutID = ifelse(mutID == "WT", "WT-DHFR", mutID))

# Round all numeric columns to 6 decimal places
dialout_ctrls_fitness_summary <- dialout_ctrls_fitness %>%
  mutate(across(where(is.numeric), ~round(., 6)))

# Display the table
kable(dialout_ctrls_fitness_summary, format = "markdown", caption = "Summary of Median Fitness for Pooled DHFR Variants")
Summary of Median Fitness for Pooled DHFR Variants
mutID M9-Supp 0-tmp 0.058-tmp 0.5-tmp 1.0-tmp 10-tmp 50-tmp 200-tmp
NP_267306 -0.577521 -1.615358 -1.380216 -0.925626 -0.766846 0.545330 1.742130 1.439774
WP_000162453 0.863386 0.224312 0.567447 1.064629 1.210018 0.702456 -2.305182 -7.805952
WP_000162462 0.911336 0.505794 0.747209 0.525657 0.229753 -4.137980 -4.706285 -8.743955
WP_000637209 0.584842 0.329937 0.663696 1.228697 1.532788 2.329915 0.408856 -7.660775
WP_002897636 1.022996 0.691549 1.053154 1.701834 2.053830 3.327449 2.658230 -4.688964
WP_003012456 0.785658 0.001135 -0.563819 -2.483255 -3.497845 -6.975202 -6.195765 -9.118302
WP_003027976 0.763870 0.260849 -0.776827 -4.106876 -5.236413 -8.447001 -6.917008 -8.570287
WP_003776922 -3.892368 -2.031637 -2.108692 -2.291793 -2.056731 -3.028958 -3.668410 -3.563152
WP_008578924 0.967376 0.274501 0.642541 1.351745 1.715205 3.117568 4.696570 6.159919
WP_008976421 0.938040 0.501459 0.791208 1.020815 0.950342 -0.341536 -1.460952 -6.890267
WT-DHFR NA -1.000283 -4.530458 -6.543512 -6.415804 -7.986620 -6.272670 -9.817412
mCherry NA -1.290412 -1.566887 -1.748018 -1.541840 -2.259923 -2.158114 -4.440523

Fitness Plot

Reshape data prior to plotting

# Reshape the data from wide to long format
long_data <- dialout_ctrls_fitness %>%
  pivot_longer(
    cols = c("M9-Supp", "0-tmp", "0.058-tmp", "0.5-tmp", "1.0-tmp", "10-tmp", "50-tmp", "200-tmp"),
    names_to = "Media",
    values_to = "fitness"
  )

# Convert Media to a factor with desired levels
long_data$Media <- factor(long_data$Media, levels = c("M9-Supp", "0-tmp", "0.058-tmp", "0.5-tmp", "1.0-tmp", "10-tmp", "50-tmp", "200-tmp"))

# Define the 12 samples in the desired order
construct_order <- c("NP_267306", "WP_000637209", "WP_008976421", "WP_003776922", "WP_008578924", "WP_002897636", "WP_000162453", "WP_003012456", "WP_000162462", "WP_003027976", "WT-DHFR", "mCherry")

long_data$mutID <- factor(long_data$mutID, levels = construct_order)

# Create a named vector for facet colors
facet_colors <- setNames(c(rep("lightgray", 10), "blue", "red"), construct_order)

# Custom function to reorder facets
reorder_facets <- function(x) factor(x, levels = construct_order)

# Calculate the overall y-axis range
y_min <- min(long_data$fitness, na.rm = TRUE)
y_max <- max(long_data$fitness, na.rm = TRUE)

Plot the fitness values across the TMP gradient for each dialout variant:

# Fitness plot
fitness.plot.v4 <- ggplot(long_data, aes(x = Media, y = fitness, group = mutID)) +
  geom_hline(yintercept = -1, linetype = "dashed", color = "red", size = 0.5) +
  geom_point() +
  geom_line() +
  labs(title = "Pooled Fitness Assay for DHFR Dial-Out Variants") +
  facet_wrap(~ reorder_facets(mutID), scales = "fixed", ncol = 4) +
  theme_cowplot(16) +
  theme(
    title = element_text(color = "black", face = "bold", size=14),
    axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1, size=10),
    strip.background = element_blank(),
    strip.text = element_text(color = "black", face = "bold", size=10),
    panel.spacing = unit(0.5, "lines"),
    axis.text.y = element_text(size = 10),
    axis.title.y = element_text(size = 12),
    panel.border = element_blank(),
    axis.line = element_line(color = "black"),
    legend.position = "none"
  ) +
  ylab("Median Fitness (LogFC)") +
  xlab("Media") +
  coord_cartesian(ylim = c(y_min, y_max))

# Add colored backgrounds for all facets
fitness.plot.v4 <- fitness.plot.v4 +
  geom_rect(data = data.frame(mutID = construct_order),
            aes(fill = mutID),
            xmin = -Inf, xmax = Inf, ymin = -Inf, ymax = Inf,
            alpha = 0.2, inherit.aes = FALSE) +
  scale_fill_manual(values = facet_colors, guide = "none")

# Remove y-axis labels for all but the left-most facets
fitness.plot.v4 <- fitness.plot.v4 +
  theme(
    axis.text.y = element_blank(),
    axis.ticks.y = element_blank(),
    axis.text.y.left = element_text(size = 10),
    axis.ticks.y.left = element_line(),
    strip.text.x = element_text(size = 10, margin = margin(b = 5, t = 5))
  )

# Print the plot
print(fitness.plot.v4)

Side-by-Side Plots

Plot Fitness and Growth Rates side-by-side

patch1 <- fitness.plot.v4 | fitness.plot.v3
patch1

Combined Datasets for Plotting Together

# Prepare long_data (Fitness data)
long_data_renamed <- long_data %>%
  rename(Construct = mutID, value = fitness) %>%
  mutate(assay_type = "Fitness")

# Check the range of Fitness values
print("Range of Fitness values:")
[1] "Range of Fitness values:"
print(range(long_data_renamed$value, na.rm = TRUE))
[1] -9.817412  6.159919
# Prepare grwell_mean data (Growth Rate data)
grwell_mean_prepared <- grwell_mean %>%
  rename(value = mean_m) %>%
  mutate(
    Type = case_when(
      Construct %in% c("WT-DHFR", "mCherry") ~ "Control",
      TRUE ~ "Variant"
    ),
    assay_type = "Growth Rate"
  )
# Combine the datasets
combined_data <- bind_rows(long_data_renamed, grwell_mean_prepared)

# Verify the data
print("Combined data summary:")
[1] "Combined data summary:"
print(table(combined_data$assay_type, combined_data$Type, useNA = "ifany"))
             
              Control Variant <NA>
  Fitness           0       0   96
  Growth Rate      16      80    0
# Check for any extreme values or outliers
print("Summary of 'value' column:")
[1] "Summary of 'value' column:"
print(summary(combined_data$value))
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max.      NA's 
-9.817412 -0.508248  0.003403 -0.742356  0.005007  6.159919         2 
# Create the combined plot
combined_plot <- ggplot(combined_data, aes(x = Media, y = value, group = interaction(Construct, assay_type), color = assay_type)) +
  geom_point() +
  geom_line() +
  geom_hline(data = filter(combined_data, assay_type == "Fitness"), 
             aes(yintercept = -1), linetype = "dashed", color = "red", size = 0.5) +
  facet_grid(assay_type ~ reorder_facets(Construct), scales = "free_y") +
  theme_cowplot(16) +
  theme(
    strip.background = element_blank(),
    strip.text = element_text(color = "black", face = "bold", size = 10),
    axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1, size = 8),
    panel.spacing = unit(0.5, "lines"),
    axis.text.y = element_text(size = 8),
    axis.title = element_text(size = 10),
    panel.border = element_blank(),
    axis.line = element_line(color = "black"),
    legend.position = "none"
  ) +
  labs(title = "Growth Rate and Pooled Fitness Assays for DHFR Dial-Out Variants",
       x = "Media",
       y = "Value") +
  scale_color_manual(values = c("Fitness" = "blue", "Growth Rate" = "red"))

# Add colored backgrounds for all facets
combined_plot <- combined_plot +
  geom_rect(data = data.frame(Construct = construct_order),
            aes(fill = Construct),
            xmin = -Inf, xmax = Inf, ymin = -Inf, ymax = Inf,
            alpha = 0.2, inherit.aes = FALSE) +
  scale_fill_manual(values = facet_colors, guide = "none")

# Print the plot
print(combined_plot)

Combined Plots

All Dial-outs

Plot data together with separate y-axes

# Function to create a dual-axis plot for each Construct
create_dual_axis_plot <- function(data, construct) {
  # Remove M9-Supp media for mCherry and WT-DHFR
  if (construct %in% c("mCherry", "WT-DHFR")) {
    data <- data %>% filter(!(Media == "M9-Supp"))
  }
  
  fitness_data <- filter(data, assay_type == "Fitness")
  growth_rate_data <- filter(data, assay_type == "Growth Rate")
  
  # Calculate the ratio for the secondary axis
  ratio <- diff(range(fitness_data$value)) / diff(range(growth_rate_data$value))
  
  ggplot() +
    # Fitness data
    geom_line(data = fitness_data, aes(x = Media, y = value, group = 1), color = "blue") +
    geom_point(data = fitness_data, aes(x = Media, y = value), color = "blue") +
    # Growth Rate data
    geom_line(data = growth_rate_data, aes(x = Media, y = value * ratio, group = 1), color = "red") +
    geom_point(data = growth_rate_data, aes(x = Media, y = value * ratio), color = "red") +
    # Horizontal line at y = -1 for Fitness
    geom_hline(yintercept = -1, linetype = "dashed", color = "gray", size = 0.5) +
    # Axes and labels
    scale_y_continuous(
      name = "Fitness",
      sec.axis = sec_axis(~./ratio, name = "Growth Rate")
    ) +
    theme_classic() +
    theme(
      axis.line.y.right = element_line(color = "red"),
      axis.ticks.y.right = element_line(color = "red"),
      axis.text.y.right = element_text(color = "red"),
      axis.title.y.right = element_text(color = "red"),
      axis.line.y.left = element_line(color = "blue"),
      axis.ticks.y.left = element_line(color = "blue"),
      axis.text.y.left = element_text(color = "blue"),
      axis.title.y.left = element_text(color = "blue"),
      axis.text.x = element_text(angle=45, hjust=1),
      axis.title.x = element_blank(),
      plot.title = element_text(size=10, face="bold", hjust=0.5)
    ) +
    labs(title=paste(construct),
         x="Media")
}

# Generate a list of plots, one for each Construct
plot_list <- combined_data %>%
  group_by(Construct) %>%
  group_map(~ create_dual_axis_plot(.x, .y$Construct))

# Arrange all plots in a grid
grid_plot <- do.call(grid.arrange, c(plot_list, ncol=3))


# Print the grid plot
print(grid_plot)
TableGrob (4 x 3) "arrange": 12 grobs

Pathogenic Dial-outs

# Function to create a dual-axis plot for each Construct
create_dual_axis_plot <- function(data, construct) {
  # Remove M9-Supp media for WT-DHFR
  if (construct == "WT-DHFR") {
    data <- data %>% filter(!(Media == "M9-Supp"))
  }
  
  fitness_data <- filter(data, assay_type == "Fitness")
  growth_rate_data <- filter(data, assay_type == "Growth Rate")
  
  # Calculate the ratio for the secondary axis
  ratio <- diff(range(fitness_data$value)) / diff(range(growth_rate_data$value))
  
  ggplot() +
    # Fitness data
    geom_line(data = fitness_data, aes(x = Media, y = value, group = 1), color = "blue") +
    geom_point(data = fitness_data, aes(x = Media, y = value), color = "blue") +
    # Growth Rate data
    geom_line(data = growth_rate_data, aes(x = Media, y = value * ratio, group = 1), color = "red") +
    geom_point(data = growth_rate_data, aes(x = Media, y = value * ratio), color = "red") +
    # Horizontal line at y = -1 for Fitness
    geom_hline(yintercept = -1, linetype = "dashed", color = "gray", size = 0.5) +
    # Axes and labels
    scale_y_continuous(
      name = "Fitness",
      sec.axis = sec_axis(~./ratio, name = "Growth Rate")
    ) +
    theme_classic() +
    theme(
      axis.line.y.right = element_line(color = "red"),
      axis.ticks.y.right = element_line(color = "red"),
      axis.text.y.right = element_text(color = "red"),
      axis.title.y.right = element_text(color = "red"),
      axis.line.y.left = element_line(color = "blue"),
      axis.ticks.y.left = element_line(color = "blue"),
      axis.text.y.left = element_text(color = "blue"),
      axis.title.y.left = element_text(color = "blue"),
      axis.text.x = element_text(angle=45, hjust=1),
      axis.title.x = element_blank(),
      plot.title = element_text(size=10, face="bold", hjust=0.5)
    ) +
    labs(title=paste(construct),
         x="Media")
}

# Filter combined_data for the specific constructs and rename
specific_constructs <- c("Bacillus cereus", "Streptococcus pneumoniae", "WT-DHFR")
filtered_data <- combined_data %>%
  filter(Construct %in% c("WP_000637209", "WP_000162453", "WT-DHFR")) %>%
  mutate(Construct = case_when(
    Construct == "WP_000637209" ~ "Bacillus cereus",
    Construct == "WP_000162453" ~ "Streptococcus pneumoniae",
    TRUE ~ Construct
  ))

# Generate a list of plots, one for each specified Construct
plot_list <- filtered_data %>%
  group_by(Construct) %>%
  group_map(~ create_dual_axis_plot(.x, .y$Construct))

# Arrange all plots in a grid
grid_plot <- do.call(grid.arrange, c(plot_list, ncol=3))


# Print the grid plot
print(grid_plot)
TableGrob (1 x 3) "arrange": 3 grobs

Correlations

# First, let's reshape the data to have Fitness and Growth Rate side by side
wide_data <- combined_data %>%
  select(Construct, Media, assay_type, value) %>%
  pivot_wider(names_from = assay_type, values_from = value) %>%
  filter(!is.na(Fitness) & !is.na(`Growth Rate`))

# Function to calculate Spearman correlation and p-value
spearman_cor <- function(data) {
  cor_test <- cor.test(data$Fitness, data$`Growth Rate`, method = "spearman")
  tibble(
    correlation = cor_test$estimate,
    p_value = cor_test$p.value
  )
}

# Calculate correlations for each Media type
correlations <- wide_data %>%
  group_by(Media) %>%
  do(spearman_cor(.)) %>%
  ungroup()
Warning: Cannot compute exact p-value with tiesWarning: Cannot compute exact p-value with ties
# Print the results
print(correlations)

# Optionally, create a plot to visualize the correlations
correlation_plot <- ggplot(correlations, aes(x = Media, y = correlation)) +
  geom_bar(stat = "identity", fill = "skyblue") +
  geom_text(aes(label = sprintf("%.3f", correlation), 
                y = ifelse(correlation > 0, correlation + 0.05, correlation - 0.05)),
            vjust = ifelse(correlations$correlation > 0, 0, 1)) +
  geom_text(aes(label = sprintf("p = %.3f", p_value), y = 0), 
            vjust = ifelse(correlations$correlation > 0, 1.5, -0.5)) +
  theme_classic() +  # This removes the background grid and adds axis lines
  theme(
    axis.line = element_line(color = "black"),  # Ensure axis lines are black
    axis.text.x = element_text(angle = 45, hjust = 1, size = 12),
    axis.text.y = element_text(size = 12),
    axis.title.x = element_text(size = 14),
    axis.title.y = element_text(size = 14),
    panel.grid.major = element_blank(),  # Remove major grid lines
    panel.grid.minor = element_blank()   # Remove minor grid lines
  ) +
  labs(title = "Spearman Correlations between Fitness and Growth Rate by Media",
       y = "Correlation Coefficient")

print(correlation_plot)

Correlation Plots

# Function to create a scatter plot for each media type
create_scatter_plot <- function(data, media_type) {
  # Remove rows where Construct is "mCherry"
  data <- data %>% filter(Construct != "mCherry")
  
  # Perform Spearman correlation test
  cor_test <- cor.test(data$Fitness, data$`Growth Rate`, method = "spearman")
  
  ggplot(data, aes(x = `Growth Rate`, y = Fitness)) +
    geom_point(alpha = 0.6) +
    geom_smooth(method = "lm", color = "red", fill = "skyblue", se = TRUE) +  # Set se = TRUE to include confidence interval
    annotate("text", x = min(data$`Growth Rate`, na.rm = TRUE), y = max(data$Fitness, na.rm = TRUE),
             label = sprintf("Correlation = %.2f\np-value = %.3f", cor_test$estimate, cor_test$p.value),
             hjust = 0, vjust = 1) +
    theme_classic() +  # This removes the background grid and adds axis lines
    theme(
      axis.line = element_line(color = "black"),
      plot.title = element_text(size = 14, hjust = 0.5),
      axis.title = element_text(size = 12),
      axis.text = element_text(size = 10),
      plot.margin = margin(10, 30, 10, 10)  # Add right margin for x-axis labels
    ) +
    labs(
      title = NULL,  # Remove the title
      x = paste("Growth Rate in", media_type),  # Set x-axis title
      y = paste("Fitness in", media_type)        # Set y-axis title
    ) +
    scale_x_continuous(expand = expansion(mult = c(0.05, 0.05)))  # Add padding to x-axis limits
}

# Generate a list of plots, one for each media type
corr_plot_list <- wide_data %>%
  group_by(Media) %>%
  group_map(~ create_scatter_plot(.x, .y$Media))
Warning: Cannot compute exact p-value with tiesWarning: Cannot compute exact p-value with ties
# Arrange all plots in a grid
corr_grid_plot <- do.call(grid.arrange, c(corr_plot_list, ncol = 2))


# Print the grid plot
print(corr_grid_plot)
TableGrob (4 x 2) "arrange": 8 grobs
# Function to create a scatter plot for each media type
create_scatter_plot <- function(data, media_type) {
  # Remove rows where Construct is "mCherry"
  data <- data %>% filter(Construct != "mCherry")
  
  # Perform Spearman correlation test (if needed for other purposes)
  cor_test <- cor.test(data$Fitness, data$`Growth Rate`, method = "spearman")
  
  ggplot(data, aes(x = `Growth Rate`, y = Fitness)) +
    geom_point(alpha = 0.6) +
    geom_smooth(method = "lm", color = "red", fill = "skyblue", se = TRUE) +  # Set fill color for confidence interval to blue
    theme_classic() + 
    theme(
      axis.line = element_line(color = "black"),
      plot.title = element_text(size = 14, hjust = 0.5),
      axis.title = element_text(size = 12),
      axis.text = element_text(size = 10),
      plot.margin = margin(10, 30, 10, 10)
    ) +
    labs(
      title = NULL,
      x = paste("Growth Rate in", media_type),  # Set x-axis title
      y = paste("Fitness in", media_type)        # Set y-axis title
    ) +
    scale_x_continuous(expand = expansion(mult = c(0.05, 0.05)))  
}

# Generate a list of plots, one for each media type
corr_plot_list <- wide_data %>%
  group_by(Media) %>%
  group_map(~ create_scatter_plot(.x, .y$Media))
Warning: Cannot compute exact p-value with tiesWarning: Cannot compute exact p-value with ties
# Arrange all plots in a grid
corr_grid_plot <- do.call(grid.arrange, c(corr_plot_list, ncol = 2))


# Print the grid plot
print(corr_grid_plot)
TableGrob (4 x 2) "arrange": 8 grobs

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)
 backports          1.4.1      2021-12-13 [1] CRAN (R 4.3.0)
 bio3d            * 2.4-5      2024-10-29 [1] CRAN (R 4.3.3)
 BiocGenerics     * 0.46.0     2023-06-04 [1] Bioconductor
 Biostrings       * 2.68.1     2023-05-21 [1] Bioconductor
 bitops             1.0-7      2021-04-24 [1] CRAN (R 4.3.0)
 broom            * 1.0.7      2024-09-26 [1] CRAN (R 4.3.3)
 cachem             1.0.8      2023-05-01 [1] CRAN (R 4.3.0)
 castor           * 1.8.0      2024-01-09 [1] CRAN (R 4.3.1)
 cellranger         1.1.0      2016-07-27 [1] CRAN (R 4.3.0)
 cli                3.6.2      2023-12-11 [1] CRAN (R 4.3.1)
 codetools          0.2-20     2024-03-31 [1] CRAN (R 4.3.1)
 colorspace         2.1-0      2023-01-23 [1] CRAN (R 4.3.0)
 cowplot          * 1.1.3      2024-01-22 [1] CRAN (R 4.3.1)
 crayon             1.5.2      2022-09-29 [1] CRAN (R 4.3.0)
 devtools         * 2.4.5      2022-10-11 [1] CRAN (R 4.3.0)
 digest             0.6.35     2024-03-11 [1] CRAN (R 4.3.1)
 dplyr            * 1.1.4      2023-11-17 [1] CRAN (R 4.3.1)
 ellipsis           0.3.2      2021-04-29 [1] CRAN (R 4.3.0)
 evaluate           0.23       2023-11-01 [1] CRAN (R 4.3.1)
 fansi              1.0.6      2023-12-08 [1] CRAN (R 4.3.1)
 farver             2.1.1      2022-07-06 [1] CRAN (R 4.3.0)
 fastmap            1.1.1      2023-02-24 [1] CRAN (R 4.3.0)
 foreach            1.5.2      2022-02-02 [1] CRAN (R 4.3.0)
 fs                 1.6.3      2023-07-20 [1] CRAN (R 4.3.0)
 generics           0.1.3      2022-07-05 [1] CRAN (R 4.3.0)
 GenomeInfoDb     * 1.36.4     2023-10-08 [1] Bioconductor
 GenomeInfoDbData   1.2.10     2023-09-13 [1] Bioconductor
 ggExtra          * 0.10.1     2023-08-21 [1] CRAN (R 4.3.0)
 ggfun              0.1.4      2024-01-19 [1] CRAN (R 4.3.1)
 ggnewscale       * 0.4.10     2024-02-08 [1] CRAN (R 4.3.1)
 ggplot2          * 3.5.1      2024-04-23 [1] CRAN (R 4.3.1)
 ggplotify          0.1.2      2023-08-09 [1] CRAN (R 4.3.0)
 ggridges         * 0.5.6      2024-01-23 [1] CRAN (R 4.3.1)
 ggtree           * 3.8.2      2023-07-30 [1] Bioconductor
 ggtreeExtra      * 1.10.0     2023-04-25 [1] Bioconductor
 glmnet           * 4.1-8      2023-08-22 [1] CRAN (R 4.3.0)
 glue               1.7.0      2024-01-09 [1] CRAN (R 4.3.1)
 gridExtra        * 2.3        2017-09-09 [1] CRAN (R 4.3.0)
 gridGraphics       0.5-1      2020-12-13 [1] CRAN (R 4.3.0)
 gtable             0.3.5      2024-04-22 [1] CRAN (R 4.3.1)
 htmltools          0.5.8.1    2024-04-04 [1] CRAN (R 4.3.1)
 htmlwidgets        1.6.4      2023-12-06 [1] CRAN (R 4.3.1)
 httpuv             1.6.15     2024-03-26 [1] CRAN (R 4.3.1)
 igraph           * 2.0.3      2024-03-13 [1] CRAN (R 4.3.1)
 IRanges          * 2.34.1     2023-07-02 [1] Bioconductor
 iterators          1.0.14     2022-02-05 [1] CRAN (R 4.3.0)
 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)
 lubridate        * 1.9.3      2023-09-27 [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)
 readxl           * 1.4.3      2023-07-06 [1] CRAN (R 4.3.0)
 rematch            2.0.0      2023-08-30 [1] CRAN (R 4.3.0)
 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)
 timechange         0.3.0      2024-01-18 [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

─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
LS0tCnRpdGxlOiAiRGlhbG91dCBWYXJpYW50IEZpdG5lc3MgQW5hbHlzaXMiCmF1dGhvcjogJ0F1dGhvcnM6IFtLYXJsIEouIFJvbWFub3dpY3pdKGh0dHBzOi8va3JvbWFub3dpY3ouZ2l0aHViLmlvLyksIENhcm1lbiBSZXNuaWNrLCBTYW11ZWwgUi4gSGludG9uLCBDYWxpbiBQbGVzYScKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0aGVtZTogc3BhY2VsYWIKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDUKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiB5ZXMKICAgICAgc21vb3RoX3Njcm9sbDogeWVzCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6ICc1JwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgcGRmX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogJzUnCi0tLQoKKipSIE5vdGVib29rOioqIDxmb250IGNvbG9yPSJncmVlbiI+UHJvdmlkZXMgcmVwcm9kdWNpYmxlIGFuYWx5c2lzIGZvciAqKkRpYWwtT3V0IFZhcmlhbnQgRml0bmVzcyoqIGluIHRoZSBmb2xsb3dpbmcgbWFudXNjcmlwdDo8L2ZvbnQ+CgoqKkNpdGF0aW9uOioqIFJvbWFub3dpY3ogS0osIFJlc25pY2sgQywgSGludG9uIFNSLCBQbGVzYSBDLiAoMjAyNSkgRXhwbG9yaW5nIGFudGliaW90aWMgcmVzaXN0YW5jZSBpbiBkaXZlcnNlIGhvbW9sb2dzIG9mIHRoZSBkaWh5ZHJvZm9sYXRlIHJlZHVjdGFzZSBwcm90ZWluIGZhbWlseSB0aHJvdWdoIGJyb2FkIG11dGF0aW9uYWwgc2Nhbm5pbmcuICoqKmJpb1J4aXYqKiouIFtdKCkKCioqR2l0SHViIFJlcG9zaXRvcnk6KiogW2h0dHBzOi8vZ2l0aHViLmNvbS9QbGVzYUxhYi9ESEZSXShodHRwczovL2dpdGh1Yi5jb20vUGxlc2FMYWIvREhGUikKCioqTkNCSSBCaW9Qcm9qZWN0OioqIFtodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L2Jpb3Byb2plY3QvMTE4OTQ3OF0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9iaW9wcm9qZWN0LzExODk0NzgpCgojIEV4cGVyaW1lbnQKClRoaXMgcGlwZWxpbmUgcHJvY2Vzc2VzIGEgbGlicmFyeSBvZiAxLDUzNiBESEZSIGhvbW9sb2dzIGFuZCB0aGVpciBhc3NvY2lhdGVkIG11dGFudHMsIHdpdGggdHdvLWZvbGQgcmVkdW5kYW5jeSAodHdvIGNvZG9uIHZhcmlhbnRzIHBlciBzZXF1ZW5jZSkuIEZpdG5lc3Mgc2NvcmVzIGFyZSBkZXJpdmVkIGZyb20gYSBtdWx0aXBsZXhlZCBpbi12aXZvIGFzc2F5IHVzaW5nIGEgdHJpbWV0aG9wcmltIGNvbmNlbnRyYXRpb24gZ3JhZGllbnQsIGFzc2Vzc2luZyB0aGUgYWJpbGl0eSBvZiB0aGVzZSBob21vbG9ncyBhbmQgdGhlaXIgbXV0YW50cyB0byBjb21wbGVtZW50IGZ1bmN0aW9uYWxpdHkgaW4gYW4gKkUuIGNvbGkqIGtub2Nrb3V0IHN0cmFpbiBhbmQgdGhlaXIgdG9sZXJhbmNlIHRvIHRyaW1ldGhvcHJpbSB0cmVhdG1lbnQuIFRoaXMgYW5hbHlzaXMgcHJvdmlkZXMgaW5zaWdodHMgaW50byBob3cgYW50aWJpb3RpYyByZXNpc3RhbmNlIGV2b2x2ZXMgYWNyb3NzIGEgcmFuZ2Ugb2YgZXZvbHV0aW9uYXJ5IHN0YXJ0aW5nIHBvaW50cy4gU2VxdWVuY2UgZGF0YSB3ZXJlIGdlbmVyYXRlZCB1c2luZyB0aGUgSWxsdW1pbmEgTm92YVNlcSBwbGF0Zm9ybSB3aXRoIDEwMCBicCBwYWlyZWQtZW5kIHNlcXVlbmNpbmcgb2YgYW1wbGljb25zLgoKIVtNZXRob2RzIG92ZXJ2aWV3IHRvIGFjaGlldmUgYSBicm9hZC1tdXRhdGlvbmFsIHNjYW4gZm9yIERIRlIgaG9tb2xvZ3MuXShJbWFnZXMvREhGUi5EaWFncmFtLnBuZykKCmBgYHtjc3N9Ci5iYWRDb2RlIHsKYmFja2dyb3VuZC1jb2xvcjogbGlnaHRwaW5rOwpmb250LXdlaWdodDogYm9sZDsKfQoKLmdvb2RDb2RlIHsKYmFja2dyb3VuZC1jb2xvcjogbGlnaHRncmVlbjsKZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KCi5zaGFyZWRDb2RlIHsKYmFja2dyb3VuZC1jb2xvcjogbGlnaHRibHVlOwpmb250LXdlaWdodDogYm9sZDsKfQoKdGFibGUgewogIG1hcmdpbjogYXV0bzsKICBib3JkZXItdG9wOiAxcHggc29saWQgIzY2NjsKICBib3JkZXItYm90dG9tOiAxcHggc29saWQgIzY2NjsKfQp0YWJsZSB0aGVhZCB0aCB7IGJvcmRlci1ib3R0b206IDFweCBzb2xpZCAjZGRkOyB9CnRoLCB0ZCB7IHBhZGRpbmc6IDVweDsgfQp0aGVhZCwgdGZvb3QsIHRyOm50aC1jaGlsZChldmVuKSB7IGJhY2tncm91bmQ6ICNlZWU7IH0KYGBgCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KIyBTZXQgZ2xvYmFsIG9wdGlvbnMgZm9yIG5vdGVib29rCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KHdhcm5pbmcgPSBUUlVFLCBtZXNzYWdlID0gVFJVRSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBjbGFzcy5zb3VyY2UgPSAiYmctc3VjY2VzcyIpCgojIEdldHRpbmcgdGhlIHBhdGggb2YgeW91ciBjdXJyZW50IG9wZW4gZmlsZSBhbmQgc2V0IGFzIHdkCmN1cnJlbnRfcGF0aCA9IHJzdHVkaW9hcGk6OmdldEFjdGl2ZURvY3VtZW50Q29udGV4dCgpJHBhdGggCnNldHdkKGRpcm5hbWUoY3VycmVudF9wYXRoKSkKcHJpbnQoZ2V0d2QoKSkKYGBgCgojIFBhY2thZ2VzClRoZSBmb2xsb3dpbmcgUiBwYWNrYWdlcyBtdXN0IGJlIGluc3RhbGxlZCBwcmlvciB0byBsb2FkaW5nIGludG8gdGhlIFIgc2Vzc2lvbi4gU2VlIHRoZSAqKlJlcHJvZHVjaWJpbGl0eSoqIHRhYiBmb3IgYSBjb21wbGV0ZSBsaXN0IG9mIHBhY2thZ2VzIGFuZCB0aGVpciB2ZXJzaW9ucyB1c2VkIGluIHRoaXMgd29ya2Zsb3cuCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQojIExvYWQgdGhlIGxhdGVzdCB2ZXJzaW9uIG9mIHB5dGhvbiAoMy4xMC4xNCkgZm9yIGRvd25zdHJlYW0gdXNlOgpsaWJyYXJ5KHJldGljdWxhdGUpCnVzZV9weXRob24oIi9Vc2Vycy9rcm9tL21pbmlmb3JnZTMvYmluL3B5dGhvbjMiKQoKIyBNYWtlIGEgdmVjdG9yIG9mIHJlcXVpcmVkIHBhY2thZ2VzCnJlcXVpcmVkLnBhY2thZ2VzIDwtIGMoImFwZSIsICJiaW8zZCIsICJCaW9zdHJpbmdzIiwgImJyb29tIiwgImNhc3RvciIsICJjb3dwbG90IiwgImRldnRvb2xzIiwgImRwbHlyIiwgImdnRXh0cmEiLCAiZ2duZXdzY2FsZSIsICJnZ3Bsb3QyIiwgImdncmlkZ2VzIiwgImdndHJlZSIsICJnZ3RyZWVFeHRyYSIsICJnbG1uZXQiLCAiZ3JpZEV4dHJhIiwiaWdyYXBoIiwgImtuaXRyIiwgImx1YnJpZGF0ZSIsICJtYXRyaXhTdGF0cyIsICJwYXRjaHdvcmsiLCAicGhlYXRtYXAiLCAicHVycnIiLCAicHNjbCIsICJSQ29sb3JCcmV3ZXIiLCAicmVhZHhsIiwgInJlc2hhcGUiLCJyZXNoYXBlMiIsICJST0NSIiwgInNlcWluciIsICJzY2FsZXMiLCAic3RyaW5nciIsICJzdHJpbmdpIiwgInRpZHlyIiwgInRpZHl0cmVlIiwgInZpcmlkaXMiKQoKIyBMb2FkIHJlcXVpcmVkIHBhY2thZ2VzIHdpdGggZXJyb3IgaGFuZGxpbmcKbG9hZGVkLnBhY2thZ2VzIDwtIGxhcHBseShyZXF1aXJlZC5wYWNrYWdlcywgZnVuY3Rpb24ocGFja2FnZSkgewogIGlmICghcmVxdWlyZShwYWNrYWdlLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKSB7CiAgICBpbnN0YWxsLnBhY2thZ2VzKHBhY2thZ2UsIGRlcGVuZGVuY2llcyA9IFRSVUUpCiAgICBpZiAoIXJlcXVpcmUocGFja2FnZSwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKSkgewogICAgICBtZXNzYWdlKCJQYWNrYWdlICIsIHBhY2thZ2UsICIgY291bGQgbm90IGJlIGluc3RhbGxlZCBhbmQgbG9hZGVkLiIpCiAgICAgIHJldHVybihOVUxMKQogICAgfQogIH0KICByZXR1cm4ocGFja2FnZSkKfSkKCiMgUmVtb3ZlIE5VTEwgZW50cmllcyBmcm9tIGxvYWRlZCBwYWNrYWdlcwpsb2FkZWQucGFja2FnZXMgPC0gbG9hZGVkLnBhY2thZ2VzWyFzYXBwbHkobG9hZGVkLnBhY2thZ2VzLCBpcy5udWxsKV0KYGBgCgpgYGB7ciBjbGFzcy5vdXRwdXQ9InNoYXJlZENvZGUiLCBlY2hvPUZBTFNFfQojIFByaW50IGxvYWRlZCBwYWNrYWdlcwpjYXQoIkxvYWRlZCBwYWNrYWdlczoiLCBwYXN0ZShsb2FkZWQucGFja2FnZXMsIGNvbGxhcHNlID0gIiwgIiksICJcbiIpCmBgYAoKYGBge3IgaW5jbHVkZT1GQUxTRX0KIyBzZXQuc2VlZCBpcyB1c2VkIHRvIGZpeCB0aGUgcmFuZG9tIG51bWJlciBnZW5lcmF0aW9uIHRvIG1ha2UgdGhlIHJlc3VsdHMgcmVwZWF0YWJsZQpzZXQuc2VlZCgxMjMpCmBgYAoKIyBJbXBvcnQgRGF0YSBGaWxlcwoKSW1wb3J0ICoqQ09VTlQqKiBmaWxlcyBnZW5lcmF0ZWQgZnJvbSBbREhGUi4yLkNvdW50cy5STURdKGh0dHBzOi8vZ2l0aHViLmNvbS9QbGVzYUxhYi9ESEZSKSByZWxldmFudCBmb3IgZG93bnN0cmVhbSBhbmFseXNpcy4KYGBge3J9CiMgQkNjb250cm9sc18xNV9tZWRpYW4KQkNjb250cm9sc18xNV9tZWRpYW4gPC0gcmVhZC5jc3YoIkNvdW50L2NvdW50X2ZpbGVzX2Zvcm1hdHRlZC9CQ2NvbnRyb2xzXzE1X21lZGlhbi5jc3YiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKYGBgCgpJbXBvcnQgKipQRVJGRUNUUyoqIGZpbGVzIGdlbmVyYXRlZCBmcm9tIFtESEZSLjMuUGVyZmVjdHMuUk1EXShodHRwczovL2dpdGh1Yi5jb20vUGxlc2FMYWIvREhGUikgcmVsZXZhbnQgZm9yIGRvd25zdHJlYW0gYW5hbHlzaXMuCmBgYHtyfQojIHBlcmZlY3RzMTVfNUJDcwpwZXJmZWN0czE1XzVCQ3MgPC0gcmVhZC5jc3YoIlBlcmZlY3RzL3BlcmZlY3RzX2ZpbGVzX2Zvcm1hdHRlZC9wZXJmZWN0czE1XzVCQ3MuY3N2IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmBgYAoKIyBEaWFsb3V0IEFuYWx5c2lzCgojIyBJbXBvcnQgRXhjZWwgRmlsZXMKClRoaXMgaXMgdGhlIGZpbGUgd2l0aCB0aGUgcGxhdGUgcmVhZGVyIGRhdGE6CmBgYHtyfQojIFJFUExBQ0UgVEhJUyBGSUxFIE5BTUUgRk9SIEVBQ0ggQU5BTFlTSVMKZmlsZW5hbWVfb2ZfcGxhdGVfcmVhZGVyX2RhdGEgPSAiRGlhbG91dC9SQVcvMDItMDgtMjRfS1JfREhGUl9GaXRuZXNzX0Fzc2F5X005X1RNUC5QbGF0ZTQuUm5kMi54bHN4IgoKI005LUZ1bGwgUGxhdGUgMTogIjAxLTI0LTI0X0tSX0RIRlJfRml0bmVzc19Bc3NheV9NOV9GdWxsX1N1cHAuUGxhdGUxLnhsc3giCiNNOS1UTVAgUGxhdGUgMjogIjAxLTMxLTI0X0tSX0RIRlJfRml0bmVzc19Bc3NheV9NOV9UTVAuUGxhdGUyLnhsc3giCiNNOS1UTVAgUGxhdGUgMzogIjAyLTAxLTI0X0tSX0RIRlJfRml0bmVzc19Bc3NheV9NOV9UTVAuUGxhdGUzLnhsc3giCiNNOS1UTVAgUGxhdGUgNDogIjAyLTAyLTI0X0tSX0RIRlJfRml0bmVzc19Bc3NheV9NOV9UTVAuUGxhdGU0Lnhsc3giCgojUmUtZG8gb2YgTTktVE1QIFBsYXRlczoKI005LVRNUCBQbGF0ZSAyOiAiMDItMDYtMjRfS1JfREhGUl9GaXRuZXNzX0Fzc2F5X005X1RNUC5QbGF0ZTIuUm5kMi54bHN4IgojTTktVE1QIFBsYXRlIDM6ICIwMi0wNy0yNF9LUl9ESEZSX0ZpdG5lc3NfQXNzYXlfTTlfVE1QLlBsYXRlMy5SbmQyLnhsc3giCiNNOS1UTVAgUGxhdGUgNDogIjAyLTA4LTI0X0tSX0RIRlJfRml0bmVzc19Bc3NheV9NOV9UTVAuUGxhdGU0LlJuZDIueGxzeCIKYGBgCgpUaGlzIGlzIHRoZSByYW5nZSBvZiBkYXRhIGluIHRoZSBleGNlbCBmaWxlIHRvIGltcG9ydApgYGB7cn0KIyBVUERBVEUgVEhJUyBEQVRBIFJBTkdFIEZPUiBFQUNIIEVYQ0VMIEZJTEUgWU9VIElNUE9SVApyYW5nZV9vZCA9ICJCNDE6Q0kyODIiCgojTTktRnVsbCBQbGF0ZSAxIE9ELXJhbmdlOiBCNDE6QU0yODIKI005LVRNUCBQbGF0ZSAyIE9ELXJhbmdlOiBCNDE6Q0kyNzYKI005LVRNUCBQbGF0ZSAzIE9ELXJhbmdlOiBCNDE6Q0kyNzkKI005LVRNUCBQbGF0ZSA0IE9ELXJhbmdlOiBCNDE6Q0kyODIKCiNSZS1kbyBvZiBNOS1UTVAgUGxhdGVzOgojTTktVE1QIFBsYXRlIDIgT0QtcmFuZ2U6IEI0MTpDSTI4MgojTTktVE1QIFBsYXRlIDMgT0QtcmFuZ2U6IEI0MTpDSTI3NgojTTktVE1QIFBsYXRlIDQgT0QtcmFuZ2U6IEI0MTpDSTI4MgpgYGAKCmBgYHtyfQojZ2V0IHRoZSBkYXRhCm9kX2RhdGExIDwtIGFzLmRhdGEuZnJhbWUocmVhZF9leGNlbChmaWxlbmFtZV9vZl9wbGF0ZV9yZWFkZXJfZGF0YSwgc2hlZXQgPSAxLCByYW5nZSA9IHJhbmdlX29kKSkKYGBgCgpgYGB7cn0KI2ZpeCB0aGUgdGltZSAobWFrZXMgbWludXRlcyBmcm9tIHN0YXJ0KQpvZF9kYXRhIDwtIG9kX2RhdGExICU+JQogIG11dGF0ZSh0aW1lX2ludD1hcy5kdXJhdGlvbihpbnRlcnZhbChzdGFydCA9IG9kX2RhdGExJFRpbWVbMV0sIGVuZD1UaW1lKSkpICU+JQogIG11dGF0ZShyb3VuZF90aW1lPWNlaWxpbmcoYXMubnVtZXJpYyh0aW1lX2ludCkvNjApKSAlPiUKICBzdWJzZXQoc2VsZWN0ID0gLVRpbWUpICU+JQogIHN1YnNldChzZWxlY3QgPSAtdGltZV9pbnQpICU+JQogIHJlbG9jYXRlKHJvdW5kX3RpbWUpICU+JQogIGRwbHlyOjpyZW5hbWUoVGltZT1yb3VuZF90aW1lKSAlPiUKICB0KCkgJT4lCiAgYXMuZGF0YS5mcmFtZSguKQpgYGAKCmBgYHtyfQojbWFrZSB2ZWN0b3IgdmFyaWFibGVzIGZvciBwbG90dGluZwpNID0gbnJvdyhvZF9kYXRhKQpOID0gbmNvbChvZF9kYXRhKQpgYGAKCmBgYHtyfQojbWFrZSBhIHZlY3RvciBvZiB5b3VyIHRpbWUgcG9pbnRzCk9EdGltZSA8LSBvZF9kYXRhWzEsMTpOXQpgYGAKCiMjIFNhbXBsZSBHcm93dGggUmF0ZSBQbG90cwoKVGhpcyBjb21tYW5kIGxvYWRzIHRoZSBzY3JpcHQgKGZpbmRfZ3IuUikgdGhhdCBjb250YWlucyB0aGUgYW5hbHlzaXMgZnVuY3Rpb25zLiBUaGlzIGZpbGUgbmVlZHMgdG8gYmUgaW4gdGhlIHByZXNlbnQgd29ya2luZyBkaXJlY3RvcnkuCmBgYHtyfQpzb3VyY2UoIkRpYWxvdXQvU2NyaXB0cy9maW5kX2dyLlIiKQpgYGAKCmBgYHtyfQojZGVmaW5lIGluaXRpYWwgZ3Jvd3RoIHJhdGVzCmdyb3d0aC5yYXRlcyA9IE5VTEwKYGBgCgpgYGB7cn0KI3RoaXMgdmFyaWFibGUgd2lsbCBzdG9yZSBhbGwgdGhlIGRhdGEKZ3JfdGFibGUgPC0gZGF0YS5mcmFtZShzYW1wbGVfbmFtZT1jaGFyYWN0ZXIoKSwgCiAgICAgICAgICAgICAgICAgICAgICAgZG91YmxpbmcudGltZT1kb3VibGUoKSwgCiAgICAgICAgICAgICAgICAgICAgICAgbT1kb3VibGUoKSwKICAgICAgICAgICAgICAgICAgICAgICByMj1kb3VibGUoKSwKICAgICAgICAgICAgICAgICAgICAgICBsYWcudD1kb3VibGUoKSkKYGBgCgpgYGB7cn0KI2NvbnZlcnQgdGltZV92ZWN0b3IgdG8gbnVtZXJpYyB2YXJpYWJsZQp0aW1lX3ZlY3RvciA9IGFzLm51bWVyaWMoT0R0aW1lWzEsXSkKYGBgCgpHZW5lcmF0ZSBncm93dGggcmF0ZSBwbG90cyBmcm9tIGVhY2ggc2FtcGxpbmcgd2VsbCBhbmQgc2F2ZSBhcyAucG5nIGZpbGVzOgpgYGB7cn0KI3J1biB0aHJvdWdoIGFsbCBzYW1wbGVzIGFuZCBmaXQKZm9yIChpIGluIDI6TSkgewogIHByaW50KGkpCiAgc2FtcGxlX25tIDwtIHJvdy5uYW1lcyhvZF9kYXRhKVtpXQogIAogICMgQ2xlYW4gdGhlIHNhbXBsZSBuYW1lIHRvIHJlbW92ZSBvciByZXBsYWNlIHByb2JsZW1hdGljIGNoYXJhY3RlcnMKICBjbGVhbl9zYW1wbGVfbm0gPC0gZ3N1YigiW15bOmFsbnVtOl1fLV0iLCAiXyIsIHNhbXBsZV9ubSkKICAKICAjIE9wZW4gYSBQTkcgZGV2aWNlIHdpdGggdGhlIGNsZWFuZWQgZmlsZW5hbWUKICBwbmdfZmlsZW5hbWUgPC0gZmlsZS5wYXRoKCJEaWFsb3V0L1BMT1RTIiwgcGFzdGUwKGNsZWFuX3NhbXBsZV9ubSwgIl9ncm93dGhfcmF0ZV9wbG90LnBuZyIpKQogIHBuZyhmaWxlbmFtZSA9IHBuZ19maWxlbmFtZSwgd2lkdGggPSA4MDAsIGhlaWdodCA9IDYwMCkKICAKICAjdGhpcyBpcyB3aGVyZSB0aGUgZml0IGhhcHBlbnM6CiAgZ3IgPC0gZmluZGdyKG9kX2RhdGFbaSwgMTpOXSwgdGltZV92ZWN0b3IsIHNhbXBsZV9ubSwgaW50PTMwLCByMj0wLjYpIAogIAogIGRlIDwtIGxpc3Qoc2FtcGxlX25hbWU9c2FtcGxlX25tLCAKICAgICAgICAgICAgIGRvdWJsaW5nLnRpbWU9bG9nMigyKS9nclsibSJdLCAKICAgICAgICAgICAgIG09Z3JbIm0iXSwKICAgICAgICAgICAgIHIyPWdyWyJyMiJdLAogICAgICAgICAgICAgbGFnLnQ9Z3JbImxhZy50Il0pCiAgCiAgI2FkZCB0aGlzIHRvIHByZXZpb3VzbHkgY2FsY3VsYXRlZCB3ZWxscwogIGdyX3RhYmxlIDwtIHJiaW5kKGdyX3RhYmxlLCBkZSwgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSkKICBncm93dGgucmF0ZXMgPC0gcmJpbmQoZ3Jvd3RoLnJhdGVzLCBncikKICAKICAjIENsb3NlIHRoZSBQTkcgZGV2aWNlCiAgZGV2Lm9mZigpCn0KYGBgCgpEaXNwbGF5IHRoZSBwbG90cyBpbiB0aGUgUk1EOgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KI3J1biB0aHJvdWdoIGFsbCBzYW1wbGVzIGFuZCBmaXQKZm9yIChpIGluIDI6TSkgewogIHByaW50KGkpCiAgc2FtcGxlX25tPXJvdy5uYW1lcyhvZF9kYXRhKVtpXQogIAogICN0aGlzIGlzIHdoZXJlIHRoZSBmaXQgaGFwcGVuczoKICBnciA8LSBmaW5kZ3Iob2RfZGF0YVtpLCAxOk5dLCB0aW1lX3ZlY3Rvciwgc2FtcGxlX25tLCBpbnQ9MzAsIHIyPTAuNikgCiAgIzEgaW4gb2RfZGF0YVtpLCAxOk5dIGlzIHRoZSBjb2x1bW4gbnVtYmVyIHdoZXJlIHRoZSBkYXRhIHN0YXJ0czsgIAogICNpbnQgaXMgbnVtYmVyIG9mIHBvaW50cyB0YWtlbiBhdCBvbmUgdGltZSBhcyBhbiBpbnRlcnZhbCB0byBmaW5kIHRoZSBoaWdoZXN0IHNsb3BlOyAKICAjdmFyeSAoaS5lLiBsb3dlcikgcjIsIGkuZS4gcnNxdWFyZWQgYXMgbmVlZGVkLCBibGFua3MgY2FuIGJlIGEgcHJvYmxlbSBoZXJlCiAgCiAgI3ByaW50KGdyWyJtIl0pCiAgZGUgPC0gbGlzdChzYW1wbGVfbmFtZT1zYW1wbGVfbm0sIAogICAgICAgICAgICAgZG91YmxpbmcudGltZT1sb2cyKDIpL2dyWyJtIl0sIAogICAgICAgICAgICAgbT1nclsibSJdLAogICAgICAgICAgICAgcjI9Z3JbInIyIl0sCiAgICAgICAgICAgICBsYWcudD1nclsibGFnLnQiXSkKICAjcHJpbnQoZGUpCiAgCiAgI2FkZCB0aGlzIHRvIHByZXZpb3VzbHkgY2FsY3VsYXRlZCB3ZWxscwogIGdyX3RhYmxlID0gcmJpbmQoZ3JfdGFibGUsZGUsIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UpCiAgZ3Jvd3RoLnJhdGVzIDwtIHJiaW5kKGdyb3d0aC5yYXRlcywgZ3IpCn0KCmRldi5vZmYoKQpgYGAKCldyaXRlIHRoZSBkYXRhIHRvIGEgZmlsZQpgYGB7cn0KIyBVUERBVEUgVEhFIFRBQkxFIE5BTUUgSUYgWU9VIFJFLVJVTiBNVUxUSVBMRSBFWENFTCBGSUxFUwoKIyBTdWJzdGl0dXRlIHlvdXIgZGVzaXJlZCBmaWxlIG5hbWUgZm9yICdncm93dGhfcmF0ZXMnCndyaXRlLnRhYmxlKGdyX3RhYmxlLCAiRGlhbG91dC9PVVRQVVQvZ3Jvd3RoX3JhdGVzX3BsYXRlNC50eHQiLCBzZXA9Ilx0IiwgcXVvdGU9Riwgcm93Lm5hbWVzPUYpIApgYGAKCiMjIEZ1bGwgR3Jvd3RoIFJhdGUgUGxvdAoKTG9hZCB0aGUgZmluYWwgZml0bmVzcyBkYXRhIGZvciBhbGwgMTIgRGlhbG91dCBzYW1wbGVzIGJhc2VkIG9uIGNhbGN1bGF0ZWQgZ3Jvd3RoIHJhdGVzOgpgYGB7cn0KIyBGb3IgMTIgc2FtcGxlcyAoaW5jbHVkaW5nIFdUIGFuZCBtQ2hlcnJ5IGNvbnRyb2xzKQpncndlbGwgPC0gcmVhZC50YWJsZShmaWxlID0gJ0RpYWxvdXQvUmF3L2dyb3d0aF9yYXRlc19hbGwudjIuY3N2Jywgc2VwID0nLCcsIGhlYWRlciA9IFRSVUUpCmBgYAoKUmVzaGFwZSB0aGUgZGF0YSBmb3IgcGxvdHRpbmc6CmBgYHtyfQojIENvbnZlcnQgQ29uc3RydWN0IHRvIGEgZmFjdG9yIHdpdGggZGVzaXJlZCBsZXZlbHMKZ3J3ZWxsJE1lZGlhIDwtIGZhY3RvcihncndlbGwkTWVkaWEsIGxldmVscyA9IGMoIk05LVN1cHAiLCAiMC10bXAiLCAiMC4wNTgtdG1wIiwgIjAuNS10bXAiLCAiMS4wLXRtcCIsICIxMC10bXAiLCAiNTAtdG1wIiwgIjIwMC10bXAiKSkKCiMgRGVmaW5lIHRoZSAxMiBzYW1wbGVzCmdyd2VsbCRDb25zdHJ1Y3QgPC0gZmFjdG9yKGdyd2VsbCRDb25zdHJ1Y3QsIGxldmVscyA9IGMoIk5QXzI2NzMwNiIsICJXUF8wMDA2MzcyMDkiLCAiV1BfMDA4OTc2NDIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiV1BfMDAzNzc2OTIyIiwgIldQXzAwODU3ODkyNCIsICJXUF8wMDI4OTc2MzYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJXUF8wMDAxNjI0NTMiLCAiV1BfMDAzMDEyNDU2IiwgIldQXzAwMDE2MjQ2MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIldQXzAwMzAyNzk3NiIsICJXVC1ESEZSIiwgIm1DaGVycnkiKSkKYGBgCgpHcm93dGggUmF0ZSBQbG90IGZvciBESEZSIERpYWxvdXQgVmFyaWFudHM6CmBgYHtyfQojIENvbnZlcnQgTWVkaWEgdG8gYSBmYWN0b3Igd2l0aCBkZXNpcmVkIGxldmVscwpncndlbGwkTWVkaWEgPC0gZmFjdG9yKGdyd2VsbCRNZWRpYSwgbGV2ZWxzID0gYygiTTktU3VwcCIsICIwLXRtcCIsICIwLjA1OC10bXAiLCAiMC41LXRtcCIsICIxLjAtdG1wIiwgIjEwLXRtcCIsICI1MC10bXAiLCAiMjAwLXRtcCIpKQoKIyBEZWZpbmUgdGhlIDEyIHNhbXBsZXMgaW4gdGhlIGRlc2lyZWQgb3JkZXIKY29uc3RydWN0X29yZGVyIDwtIGMoIk5QXzI2NzMwNiIsICJXUF8wMDA2MzcyMDkiLCAiV1BfMDA4OTc2NDIxIiwgIldQXzAwMzc3NjkyMiIsICJXUF8wMDg1Nzg5MjQiLCAiV1BfMDAyODk3NjM2IiwgIldQXzAwMDE2MjQ1MyIsICJXUF8wMDMwMTI0NTYiLCAiV1BfMDAwMTYyNDYyIiwgIldQXzAwMzAyNzk3NiIsICJXVC1ESEZSIiwgIm1DaGVycnkiKQoKZ3J3ZWxsJENvbnN0cnVjdCA8LSBmYWN0b3IoZ3J3ZWxsJENvbnN0cnVjdCwgbGV2ZWxzID0gY29uc3RydWN0X29yZGVyKQoKIyBDcmVhdGUgYSBuYW1lZCB2ZWN0b3IgZm9yIGZhY2V0IGNvbG9ycwpmYWNldF9jb2xvcnMgPC0gc2V0TmFtZXMoYyhyZXAoImxpZ2h0Z3JheSIsIDEwKSwgImJsdWUiLCAicmVkIiksIGNvbnN0cnVjdF9vcmRlcikKCiMgQ3VzdG9tIGZ1bmN0aW9uIHRvIHJlb3JkZXIgZmFjZXRzCnJlb3JkZXJfZmFjZXRzIDwtIGZ1bmN0aW9uKHgpIGZhY3Rvcih4LCBsZXZlbHMgPSBjb25zdHJ1Y3Rfb3JkZXIpCgojIENhbGN1bGF0ZSB0aGUgb3ZlcmFsbCB5LWF4aXMgcmFuZ2UKeV9taW4gPC0gbWluKGdyd2VsbCRtLCBuYS5ybSA9IFRSVUUpCnlfbWF4IDwtIG1heChncndlbGwkbSwgbmEucm0gPSBUUlVFKQoKIyBDYWxjdWxhdGUgbWVhbiBncm93dGggcmF0ZSBmb3IgZWFjaCBDb25zdHJ1Y3QtTWVkaWEgY29tYmluYXRpb24KZ3J3ZWxsX21lYW4gPC0gZ3J3ZWxsICU+JQogIGdyb3VwX2J5KENvbnN0cnVjdCwgTWVkaWEpICU+JQogIHN1bW1hcml6ZShtZWFuX20gPSBtZWFuKG0sIG5hLnJtID0gVFJVRSksIC5ncm91cHMgPSAiZHJvcCIpCmBgYAoKR3Jvd3RoIFJhdGUgUGxvdApgYGB7cn0KIyBHcm93dGggcmF0ZSBwbG90CmZpdG5lc3MucGxvdC52MyA8LSBnZ3Bsb3QoZ3J3ZWxsX21lYW4sIGFlcyh4ID0gTWVkaWEsIHkgPSBtZWFuX20sIGdyb3VwID0gQ29uc3RydWN0KSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9saW5lKCkgKwogIGxhYnModGl0bGUgPSAiR3Jvd3RoIFJhdGUgQXNzYXkgZm9yIERIRlIgRGlhbC1PdXQgVmFyaWFudHMiKSArCiAgZmFjZXRfd3JhcCh+IHJlb3JkZXJfZmFjZXRzKENvbnN0cnVjdCksIHNjYWxlcyA9ICJmaXhlZCIsIG5jb2wgPSA0KSArCiAgdGhlbWVfY293cGxvdCgxNikgKwogIHRoZW1lKAogICAgdGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBmYWNlID0gImJvbGQiLCBzaXplPTE0KSwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdCA9IDEsIHNpemU9MTApLAogICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBmYWNlID0gImJvbGQiLCBzaXplPTEwKSwKICAgIHBhbmVsLnNwYWNpbmcgPSB1bml0KDAuNSwgImxpbmVzIiksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmxhY2siKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIgogICkgKwogIHlsYWIoZXhwcmVzc2lvbigiTWVhbiBHcm93dGggUmF0ZSAobWluIl4iLTEiKiIpIikpICsKICB4bGFiKCJNZWRpYSIpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoeV9taW4sIHlfbWF4KSkKCiMgQWRkIGNvbG9yZWQgYmFja2dyb3VuZHMgZm9yIGFsbCBmYWNldHMKZml0bmVzcy5wbG90LnYzIDwtIGZpdG5lc3MucGxvdC52MyArCiAgZ2VvbV9yZWN0KGRhdGEgPSBkYXRhLmZyYW1lKENvbnN0cnVjdCA9IGNvbnN0cnVjdF9vcmRlciksCiAgICAgICAgICAgIGFlcyhmaWxsID0gQ29uc3RydWN0KSwKICAgICAgICAgICAgeG1pbiA9IC1JbmYsIHhtYXggPSBJbmYsIHltaW4gPSAtSW5mLCB5bWF4ID0gSW5mLAogICAgICAgICAgICBhbHBoYSA9IDAuMiwgaW5oZXJpdC5hZXMgPSBGQUxTRSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGZhY2V0X2NvbG9ycywgZ3VpZGUgPSAibm9uZSIpCgojIFJlbW92ZSB5LWF4aXMgbGFiZWxzIGZvciBhbGwgYnV0IHRoZSBsZWZ0LW1vc3QgZmFjZXRzCmZpdG5lc3MucGxvdC52MyA8LSBmaXRuZXNzLnBsb3QudjMgKwogIHRoZW1lKAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRleHQueS5sZWZ0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICBheGlzLnRpY2tzLnkubGVmdCA9IGVsZW1lbnRfbGluZSgpLAogICAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgbWFyZ2luID0gbWFyZ2luKGIgPSA1LCB0ID0gNSkpCiAgKQoKIyBQcmludCB0aGUgcGxvdApwcmludChmaXRuZXNzLnBsb3QudjMpCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KI3NhdmUgcGxvdAojIEZvciAxMiBzYW1wbGVzCmdnc2F2ZShmaWxlPSJEaWFsb3V0L0RIRlJfRGlhbG91dF9Hcm93dGhfUmF0ZXMucGRmIiwgCiAgICAgICBwbG90ID0gZml0bmVzcy5wbG90LnYzLCAKICAgICAgIGRwaT02MDAsIHdpZHRoID0gNiwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iKQpgYGAKCkNhbGN1bGF0ZSBhIE1lYW4gZ3Jvd3RoIHJhdGUgdmFsdWUgYXQgZWFjaCBUTVAgY29uY2VudHJhdGlvbiBmb3IgZWFjaCBkaWFsb3V0IHZhcmlhbnQ6CmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIENyZWF0ZSB0aGUgc3VtbWFyeSB0YWJsZQpncndlbGxfc3VtbWFyeSA8LSBncndlbGxfbWVhbiAlPiUKICBwaXZvdF93aWRlcigKICAgIG5hbWVzX2Zyb20gPSBNZWRpYSwKICAgIHZhbHVlc19mcm9tID0gbWVhbl9tCiAgKSAlPiUKICBzZWxlY3QoQ29uc3RydWN0LCBgTTktU3VwcGAsIGAwLXRtcGAsIGAwLjA1OC10bXBgLCBgMC41LXRtcGAsIGAxLjAtdG1wYCwgYDEwLXRtcGAsIGA1MC10bXBgLCBgMjAwLXRtcGApICU+JQogIGFycmFuZ2UoZmFjdG9yKENvbnN0cnVjdCwgbGV2ZWxzID0gY29uc3RydWN0X29yZGVyKSkKCiMgUm91bmQgYWxsIG51bWVyaWMgY29sdW1ucyB0byA2IGRlY2ltYWwgcGxhY2VzCmdyd2VsbF9zdW1tYXJ5IDwtIGdyd2VsbF9zdW1tYXJ5ICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIH5yb3VuZCguLCA2KSkpCgojIERpc3BsYXkgdGhlIHRhYmxlCmthYmxlKGdyd2VsbF9zdW1tYXJ5LCBmb3JtYXQgPSAibWFya2Rvd24iLCBjYXB0aW9uID0gIlN1bW1hcnkgb2YgTWVhbiBHcm93dGggUmF0ZXMgKDEvbWluKSBmb3IgREhGUiBWYXJpYW50cyIpCmBgYAoKIyMgRGlhbG91dCBWYXJpYW50IEZpdG5lc3MKCmBgYHtyIGVjaG89RkFMU0V9CiMgUmVsb2FkIEJDY29udHJvbHNfMTVfbWVkaWFuCkJDY29udHJvbHNfMTVfbWVkaWFuIDwtIHJlYWQuY3N2KCJDb3VudC9jb3VudF9maWxlc19mb3JtYXR0ZWQvQkNjb250cm9sc18xNV9tZWRpYW4uY3N2IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgojIFJlLWxvYWQgcGVyZmVjdHMxNV81QkNzCnBlcmZlY3RzMTVfNUJDcyA8LSByZWFkLmNzdigiUGVyZmVjdHMvcGVyZmVjdHNfZmlsZXNfZm9ybWF0dGVkL3BlcmZlY3RzMTVfNUJDcy5jc3YiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKYGBgCgpTdGFydCBieSBwdWxsaW5nIG91dCB0aGUgRGlhbG91dCB2YXJpYW50cyBmcm9tIHRoZSBwZXJmZWN0czE1XzVCQ3MgZGF0YXNldDoKYGBge3J9CiMgU3BlY2lmeSB0aGUgbXV0SUQgdmFsdWVzIHlvdSB3YW50IHRvIGtlZXAKZGlhbG91dF9tdXRJRHMgPC0gYygiTlBfMjY3MzA2IiwgIldQXzAwMDYzNzIwOSIsICJXUF8wMDg5NzY0MjEiLCAiV1BfMDAzNzc2OTIyIiwgIldQXzAwODU3ODkyNCIsCiAgICAgICAgICAgICAgICAgICAgIldQXzAwMjg5NzYzNiIsICJXUF8wMDAxNjI0NTMiLCAiV1BfMDAzMDEyNDU2IiwgIldQXzAwMDE2MjQ2MiIsICJXUF8wMDMwMjc5NzYiKQoKIyBTdWJzZXQgdGhlIGRhdGFmcmFtZSwga2VlcGluZyBvbmx5IGNvbHVtbnMgMTo5CmRpYWxvdXRfcGVyZmVjdHMxNV81QkNzIDwtIHBlcmZlY3RzMTVfNUJDc1twZXJmZWN0czE1XzVCQ3MkbXV0SUQgJWluJSBkaWFsb3V0X211dElEcywgMTo5XQoKIyBSZW5hbWUgdGhlIGNvbHVtbnMKY29sbmFtZXMoZGlhbG91dF9wZXJmZWN0czE1XzVCQ3MpIDwtIGMoIm11dElEIiwiTTktU3VwcCIsICIwLXRtcCIsICIwLjA1OC10bXAiLCAiMC41LXRtcCIsICIxLjAtdG1wIiwgIjEwLXRtcCIsICI1MC10bXAiLCAiMjAwLXRtcCIpCmBgYAoKQWRkIHRoZSBDb250cm9scyBmaXRuZXNzIHRvIHRoZSBEaWFsb3V0IGZpdG5lc3MgZGF0YXNldDoKYGBge3J9CiMgUmVuYW1lIGNvbnRyb2xzIGNvbHVtbnMKY29sbmFtZXMoQkNjb250cm9sc18xNV9tZWRpYW4pIDwtIGMoIm11dElEIiwgIjAtdG1wIiwgIjAuMDU4LXRtcCIsICIwLjUtdG1wIiwgIjEuMC10bXAiLCAiMTAtdG1wIiwgIjUwLXRtcCIsICIyMDAtdG1wIikKCiMgRmlyc3QsIGFkZCB0aGUgTTktU3VwcCBjb2x1bW4gd2l0aCBOQSB2YWx1ZXMgdG8gQkNjb250cm9sc18xNV9tZWRpYW4KQkNjb250cm9sc18xNV9tZWRpYW4gPC0gQkNjb250cm9sc18xNV9tZWRpYW4gJT4lCiAgbXV0YXRlKGBNOS1TdXBwYCA9IE5BKQoKIyBOb3cgbWVyZ2UgdGhlIHR3byBkYXRhZnJhbWVzCmRpYWxvdXRfY3RybHNfZml0bmVzcyA8LSBiaW5kX3Jvd3MoCiAgZGlhbG91dF9wZXJmZWN0czE1XzVCQ3MsCiAgQkNjb250cm9sc18xNV9tZWRpYW4pCgojIFJlb3JkZXIgY29sdW1ucyB0byBtYXRjaCB0aGUgb3JpZ2luYWwgb3JkZXIKY29sdW1uX29yZGVyIDwtIGMoIm11dElEIiwgIk05LVN1cHAiLCAiMC10bXAiLCAiMC4wNTgtdG1wIiwgIjAuNS10bXAiLCAiMS4wLXRtcCIsICIxMC10bXAiLCAiNTAtdG1wIiwgIjIwMC10bXAiKQpkaWFsb3V0X2N0cmxzX2ZpdG5lc3MgPC0gZGlhbG91dF9jdHJsc19maXRuZXNzICU+JQogIHNlbGVjdChhbGxfb2YoY29sdW1uX29yZGVyKSkKCiMgUmVtb3ZlIHRoZSByb3cgd2hlcmUgbXV0SUQgaXMgIkQyN04iCmRpYWxvdXRfY3RybHNfZml0bmVzcyA8LSBkaWFsb3V0X2N0cmxzX2ZpdG5lc3MgJT4lCiAgZmlsdGVyKG11dElEICE9ICJEMjdOIikKCiMgUmVuYW1lICJXVCIgdG8gIldULURIRlIiCmRpYWxvdXRfY3RybHNfZml0bmVzcyA8LSBkaWFsb3V0X2N0cmxzX2ZpdG5lc3MgJT4lCiAgbXV0YXRlKG11dElEID0gaWZlbHNlKG11dElEID09ICJXVCIsICJXVC1ESEZSIiwgbXV0SUQpKQoKIyBSb3VuZCBhbGwgbnVtZXJpYyBjb2x1bW5zIHRvIDYgZGVjaW1hbCBwbGFjZXMKZGlhbG91dF9jdHJsc19maXRuZXNzX3N1bW1hcnkgPC0gZGlhbG91dF9jdHJsc19maXRuZXNzICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIH5yb3VuZCguLCA2KSkpCgojIERpc3BsYXkgdGhlIHRhYmxlCmthYmxlKGRpYWxvdXRfY3RybHNfZml0bmVzc19zdW1tYXJ5LCBmb3JtYXQgPSAibWFya2Rvd24iLCBjYXB0aW9uID0gIlN1bW1hcnkgb2YgTWVkaWFuIEZpdG5lc3MgZm9yIFBvb2xlZCBESEZSIFZhcmlhbnRzIikKYGBgCgojIyMgRml0bmVzcyBQbG90CgpSZXNoYXBlIGRhdGEgcHJpb3IgdG8gcGxvdHRpbmcKYGBge3J9CiMgUmVzaGFwZSB0aGUgZGF0YSBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQKbG9uZ19kYXRhIDwtIGRpYWxvdXRfY3RybHNfZml0bmVzcyAlPiUKICBwaXZvdF9sb25nZXIoCiAgICBjb2xzID0gYygiTTktU3VwcCIsICIwLXRtcCIsICIwLjA1OC10bXAiLCAiMC41LXRtcCIsICIxLjAtdG1wIiwgIjEwLXRtcCIsICI1MC10bXAiLCAiMjAwLXRtcCIpLAogICAgbmFtZXNfdG8gPSAiTWVkaWEiLAogICAgdmFsdWVzX3RvID0gImZpdG5lc3MiCiAgKQoKIyBDb252ZXJ0IE1lZGlhIHRvIGEgZmFjdG9yIHdpdGggZGVzaXJlZCBsZXZlbHMKbG9uZ19kYXRhJE1lZGlhIDwtIGZhY3Rvcihsb25nX2RhdGEkTWVkaWEsIGxldmVscyA9IGMoIk05LVN1cHAiLCAiMC10bXAiLCAiMC4wNTgtdG1wIiwgIjAuNS10bXAiLCAiMS4wLXRtcCIsICIxMC10bXAiLCAiNTAtdG1wIiwgIjIwMC10bXAiKSkKCiMgRGVmaW5lIHRoZSAxMiBzYW1wbGVzIGluIHRoZSBkZXNpcmVkIG9yZGVyCmNvbnN0cnVjdF9vcmRlciA8LSBjKCJOUF8yNjczMDYiLCAiV1BfMDAwNjM3MjA5IiwgIldQXzAwODk3NjQyMSIsICJXUF8wMDM3NzY5MjIiLCAiV1BfMDA4NTc4OTI0IiwgIldQXzAwMjg5NzYzNiIsICJXUF8wMDAxNjI0NTMiLCAiV1BfMDAzMDEyNDU2IiwgIldQXzAwMDE2MjQ2MiIsICJXUF8wMDMwMjc5NzYiLCAiV1QtREhGUiIsICJtQ2hlcnJ5IikKCmxvbmdfZGF0YSRtdXRJRCA8LSBmYWN0b3IobG9uZ19kYXRhJG11dElELCBsZXZlbHMgPSBjb25zdHJ1Y3Rfb3JkZXIpCgojIENyZWF0ZSBhIG5hbWVkIHZlY3RvciBmb3IgZmFjZXQgY29sb3JzCmZhY2V0X2NvbG9ycyA8LSBzZXROYW1lcyhjKHJlcCgibGlnaHRncmF5IiwgMTApLCAiYmx1ZSIsICJyZWQiKSwgY29uc3RydWN0X29yZGVyKQoKIyBDdXN0b20gZnVuY3Rpb24gdG8gcmVvcmRlciBmYWNldHMKcmVvcmRlcl9mYWNldHMgPC0gZnVuY3Rpb24oeCkgZmFjdG9yKHgsIGxldmVscyA9IGNvbnN0cnVjdF9vcmRlcikKCiMgQ2FsY3VsYXRlIHRoZSBvdmVyYWxsIHktYXhpcyByYW5nZQp5X21pbiA8LSBtaW4obG9uZ19kYXRhJGZpdG5lc3MsIG5hLnJtID0gVFJVRSkKeV9tYXggPC0gbWF4KGxvbmdfZGF0YSRmaXRuZXNzLCBuYS5ybSA9IFRSVUUpCmBgYAoKUGxvdCB0aGUgZml0bmVzcyB2YWx1ZXMgYWNyb3NzIHRoZSBUTVAgZ3JhZGllbnQgZm9yIGVhY2ggZGlhbG91dCB2YXJpYW50OgpgYGB7ciB3YXJuaW5nPUZBTFNFfQojIEZpdG5lc3MgcGxvdApmaXRuZXNzLnBsb3QudjQgPC0gZ2dwbG90KGxvbmdfZGF0YSwgYWVzKHggPSBNZWRpYSwgeSA9IGZpdG5lc3MsIGdyb3VwID0gbXV0SUQpKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLTEsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIsIHNpemUgPSAwLjUpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fbGluZSgpICsKICBsYWJzKHRpdGxlID0gIlBvb2xlZCBGaXRuZXNzIEFzc2F5IGZvciBESEZSIERpYWwtT3V0IFZhcmlhbnRzIikgKwogIGZhY2V0X3dyYXAofiByZW9yZGVyX2ZhY2V0cyhtdXRJRCksIHNjYWxlcyA9ICJmaXhlZCIsIG5jb2wgPSA0KSArCiAgdGhlbWVfY293cGxvdCgxNikgKwogIHRoZW1lKAogICAgdGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBmYWNlID0gImJvbGQiLCBzaXplPTE0KSwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdCA9IDEsIHNpemU9MTApLAogICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBmYWNlID0gImJvbGQiLCBzaXplPTEwKSwKICAgIHBhbmVsLnNwYWNpbmcgPSB1bml0KDAuNSwgImxpbmVzIiksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmxhY2siKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIgogICkgKwogIHlsYWIoIk1lZGlhbiBGaXRuZXNzIChMb2dGQykiKSArCiAgeGxhYigiTWVkaWEiKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKHlfbWluLCB5X21heCkpCgojIEFkZCBjb2xvcmVkIGJhY2tncm91bmRzIGZvciBhbGwgZmFjZXRzCmZpdG5lc3MucGxvdC52NCA8LSBmaXRuZXNzLnBsb3QudjQgKwogIGdlb21fcmVjdChkYXRhID0gZGF0YS5mcmFtZShtdXRJRCA9IGNvbnN0cnVjdF9vcmRlciksCiAgICAgICAgICAgIGFlcyhmaWxsID0gbXV0SUQpLAogICAgICAgICAgICB4bWluID0gLUluZiwgeG1heCA9IEluZiwgeW1pbiA9IC1JbmYsIHltYXggPSBJbmYsCiAgICAgICAgICAgIGFscGhhID0gMC4yLCBpbmhlcml0LmFlcyA9IEZBTFNFKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gZmFjZXRfY29sb3JzLCBndWlkZSA9ICJub25lIikKCiMgUmVtb3ZlIHktYXhpcyBsYWJlbHMgZm9yIGFsbCBidXQgdGhlIGxlZnQtbW9zdCBmYWNldHMKZml0bmVzcy5wbG90LnY0IDwtIGZpdG5lc3MucGxvdC52NCArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGV4dC55LmxlZnQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgIGF4aXMudGlja3MueS5sZWZ0ID0gZWxlbWVudF9saW5lKCksCiAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBtYXJnaW4gPSBtYXJnaW4oYiA9IDUsIHQgPSA1KSkKICApCgojIFByaW50IHRoZSBwbG90CnByaW50KGZpdG5lc3MucGxvdC52NCkKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojc2F2ZSBwbG90CiMgRm9yIDEyIHNhbXBsZXMKZ2dzYXZlKGZpbGU9IkRpYWxvdXQvREhGUl9EaWFsb3V0X01lZGlhbl9GaXRuZXNzLnBkZiIsCiAgICAgICBwbG90ID0gZml0bmVzcy5wbG90LnY0LCAKICAgICAgIGRwaT02MDAsIHdpZHRoID0gNiwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iKQpgYGAKCiMjIFNpZGUtYnktU2lkZSBQbG90cwoKUGxvdCBGaXRuZXNzIGFuZCBHcm93dGggUmF0ZXMgc2lkZS1ieS1zaWRlCmBgYHtyIHdhcm5pbmc9RkFMU0V9CnBhdGNoMSA8LSBmaXRuZXNzLnBsb3QudjQgfCBmaXRuZXNzLnBsb3QudjMKcGF0Y2gxCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KI3NhdmUgcGxvdAojIEZvciAxMiBzYW1wbGVzCmdnc2F2ZShmaWxlPSJEaWFsb3V0L0RIRlJfRGlhbG91dF9Hcm93dGhfUmF0ZV9GaXRuZXNzX0NvbXBhcmlzb24ucGRmIiwKICAgICAgIHBsb3QgPSBwYXRjaDEsIAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iKQpgYGAKCkNvbWJpbmVkIERhdGFzZXRzIGZvciBQbG90dGluZyBUb2dldGhlcgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBQcmVwYXJlIGxvbmdfZGF0YSAoRml0bmVzcyBkYXRhKQpsb25nX2RhdGFfcmVuYW1lZCA8LSBsb25nX2RhdGEgJT4lCiAgcmVuYW1lKENvbnN0cnVjdCA9IG11dElELCB2YWx1ZSA9IGZpdG5lc3MpICU+JQogIG11dGF0ZShhc3NheV90eXBlID0gIkZpdG5lc3MiKQoKIyBDaGVjayB0aGUgcmFuZ2Ugb2YgRml0bmVzcyB2YWx1ZXMKcHJpbnQoIlJhbmdlIG9mIEZpdG5lc3MgdmFsdWVzOiIpCnByaW50KHJhbmdlKGxvbmdfZGF0YV9yZW5hbWVkJHZhbHVlLCBuYS5ybSA9IFRSVUUpKQoKIyBQcmVwYXJlIGdyd2VsbF9tZWFuIGRhdGEgKEdyb3d0aCBSYXRlIGRhdGEpCmdyd2VsbF9tZWFuX3ByZXBhcmVkIDwtIGdyd2VsbF9tZWFuICU+JQogIHJlbmFtZSh2YWx1ZSA9IG1lYW5fbSkgJT4lCiAgbXV0YXRlKAogICAgVHlwZSA9IGNhc2Vfd2hlbigKICAgICAgQ29uc3RydWN0ICVpbiUgYygiV1QtREhGUiIsICJtQ2hlcnJ5IikgfiAiQ29udHJvbCIsCiAgICAgIFRSVUUgfiAiVmFyaWFudCIKICAgICksCiAgICBhc3NheV90eXBlID0gIkdyb3d0aCBSYXRlIgogICkKYGBgCgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBDb21iaW5lIHRoZSBkYXRhc2V0cwpjb21iaW5lZF9kYXRhIDwtIGJpbmRfcm93cyhsb25nX2RhdGFfcmVuYW1lZCwgZ3J3ZWxsX21lYW5fcHJlcGFyZWQpCgojIFZlcmlmeSB0aGUgZGF0YQpwcmludCgiQ29tYmluZWQgZGF0YSBzdW1tYXJ5OiIpCnByaW50KHRhYmxlKGNvbWJpbmVkX2RhdGEkYXNzYXlfdHlwZSwgY29tYmluZWRfZGF0YSRUeXBlLCB1c2VOQSA9ICJpZmFueSIpKQoKIyBDaGVjayBmb3IgYW55IGV4dHJlbWUgdmFsdWVzIG9yIG91dGxpZXJzCnByaW50KCJTdW1tYXJ5IG9mICd2YWx1ZScgY29sdW1uOiIpCnByaW50KHN1bW1hcnkoY29tYmluZWRfZGF0YSR2YWx1ZSkpCgojIENyZWF0ZSB0aGUgY29tYmluZWQgcGxvdApjb21iaW5lZF9wbG90IDwtIGdncGxvdChjb21iaW5lZF9kYXRhLCBhZXMoeCA9IE1lZGlhLCB5ID0gdmFsdWUsIGdyb3VwID0gaW50ZXJhY3Rpb24oQ29uc3RydWN0LCBhc3NheV90eXBlKSwgY29sb3IgPSBhc3NheV90eXBlKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21faGxpbmUoZGF0YSA9IGZpbHRlcihjb21iaW5lZF9kYXRhLCBhc3NheV90eXBlID09ICJGaXRuZXNzIiksIAogICAgICAgICAgICAgYWVzKHlpbnRlcmNlcHQgPSAtMSksIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIsIHNpemUgPSAwLjUpICsKICBmYWNldF9ncmlkKGFzc2F5X3R5cGUgfiByZW9yZGVyX2ZhY2V0cyhDb25zdHJ1Y3QpLCBzY2FsZXMgPSAiZnJlZV95IikgKwogIHRoZW1lX2Nvd3Bsb3QoMTYpICsKICB0aGVtZSgKICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiwgZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDEwKSwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdCA9IDEsIHNpemUgPSA4KSwKICAgIHBhbmVsLnNwYWNpbmcgPSB1bml0KDAuNSwgImxpbmVzIiksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gOCksCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmxhY2siKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIgogICkgKwogIGxhYnModGl0bGUgPSAiR3Jvd3RoIFJhdGUgYW5kIFBvb2xlZCBGaXRuZXNzIEFzc2F5cyBmb3IgREhGUiBEaWFsLU91dCBWYXJpYW50cyIsCiAgICAgICB4ID0gIk1lZGlhIiwKICAgICAgIHkgPSAiVmFsdWUiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIkZpdG5lc3MiID0gImJsdWUiLCAiR3Jvd3RoIFJhdGUiID0gInJlZCIpKQoKIyBBZGQgY29sb3JlZCBiYWNrZ3JvdW5kcyBmb3IgYWxsIGZhY2V0cwpjb21iaW5lZF9wbG90IDwtIGNvbWJpbmVkX3Bsb3QgKwogIGdlb21fcmVjdChkYXRhID0gZGF0YS5mcmFtZShDb25zdHJ1Y3QgPSBjb25zdHJ1Y3Rfb3JkZXIpLAogICAgICAgICAgICBhZXMoZmlsbCA9IENvbnN0cnVjdCksCiAgICAgICAgICAgIHhtaW4gPSAtSW5mLCB4bWF4ID0gSW5mLCB5bWluID0gLUluZiwgeW1heCA9IEluZiwKICAgICAgICAgICAgYWxwaGEgPSAwLjIsIGluaGVyaXQuYWVzID0gRkFMU0UpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBmYWNldF9jb2xvcnMsIGd1aWRlID0gIm5vbmUiKQoKIyBQcmludCB0aGUgcGxvdApwcmludChjb21iaW5lZF9wbG90KQpgYGAKCmBgYHtyIGVjaG89RkFMU0V9CiNzYXZlIHBsb3QKIyBGb3IgMTIgc2FtcGxlcwpnZ3NhdmUoZmlsZT0iRGlhbG91dC9ESEZSX0RpYWxvdXRfRml0bmVzc19Hcm93dGhfU3RhY2tlZF9QbG90cy5wZGYiLAogICAgICAgcGxvdCA9IGNvbWJpbmVkX3Bsb3QsIAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxNiwgaGVpZ2h0ID0gNCwgdW5pdHMgPSAiaW4iKQpgYGAKCiMjIENvbWJpbmVkIFBsb3RzCgojIyMgQWxsIERpYWwtb3V0cwoKUGxvdCBkYXRhIHRvZ2V0aGVyIHdpdGggc2VwYXJhdGUgeS1heGVzCmBgYHtyIGNsYXNzLm91dHB1dD0iZ29vZENvZGUifQojIEZ1bmN0aW9uIHRvIGNyZWF0ZSBhIGR1YWwtYXhpcyBwbG90IGZvciBlYWNoIENvbnN0cnVjdApjcmVhdGVfZHVhbF9heGlzX3Bsb3QgPC0gZnVuY3Rpb24oZGF0YSwgY29uc3RydWN0KSB7CiAgIyBSZW1vdmUgTTktU3VwcCBtZWRpYSBmb3IgbUNoZXJyeSBhbmQgV1QtREhGUgogIGlmIChjb25zdHJ1Y3QgJWluJSBjKCJtQ2hlcnJ5IiwgIldULURIRlIiKSkgewogICAgZGF0YSA8LSBkYXRhICU+JSBmaWx0ZXIoIShNZWRpYSA9PSAiTTktU3VwcCIpKQogIH0KICAKICBmaXRuZXNzX2RhdGEgPC0gZmlsdGVyKGRhdGEsIGFzc2F5X3R5cGUgPT0gIkZpdG5lc3MiKQogIGdyb3d0aF9yYXRlX2RhdGEgPC0gZmlsdGVyKGRhdGEsIGFzc2F5X3R5cGUgPT0gIkdyb3d0aCBSYXRlIikKICAKICAjIENhbGN1bGF0ZSB0aGUgcmF0aW8gZm9yIHRoZSBzZWNvbmRhcnkgYXhpcwogIHJhdGlvIDwtIGRpZmYocmFuZ2UoZml0bmVzc19kYXRhJHZhbHVlKSkgLyBkaWZmKHJhbmdlKGdyb3d0aF9yYXRlX2RhdGEkdmFsdWUpKQogIAogIGdncGxvdCgpICsKICAgICMgRml0bmVzcyBkYXRhCiAgICBnZW9tX2xpbmUoZGF0YSA9IGZpdG5lc3NfZGF0YSwgYWVzKHggPSBNZWRpYSwgeSA9IHZhbHVlLCBncm91cCA9IDEpLCBjb2xvciA9ICJibHVlIikgKwogICAgZ2VvbV9wb2ludChkYXRhID0gZml0bmVzc19kYXRhLCBhZXMoeCA9IE1lZGlhLCB5ID0gdmFsdWUpLCBjb2xvciA9ICJibHVlIikgKwogICAgIyBHcm93dGggUmF0ZSBkYXRhCiAgICBnZW9tX2xpbmUoZGF0YSA9IGdyb3d0aF9yYXRlX2RhdGEsIGFlcyh4ID0gTWVkaWEsIHkgPSB2YWx1ZSAqIHJhdGlvLCBncm91cCA9IDEpLCBjb2xvciA9ICJyZWQiKSArCiAgICBnZW9tX3BvaW50KGRhdGEgPSBncm93dGhfcmF0ZV9kYXRhLCBhZXMoeCA9IE1lZGlhLCB5ID0gdmFsdWUgKiByYXRpbyksIGNvbG9yID0gInJlZCIpICsKICAgICMgSG9yaXpvbnRhbCBsaW5lIGF0IHkgPSAtMSBmb3IgRml0bmVzcwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLTEsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyYXkiLCBzaXplID0gMC41KSArCiAgICAjIEF4ZXMgYW5kIGxhYmVscwogICAgc2NhbGVfeV9jb250aW51b3VzKAogICAgICBuYW1lID0gIkZpdG5lc3MiLAogICAgICBzZWMuYXhpcyA9IHNlY19heGlzKH4uL3JhdGlvLCBuYW1lID0gIkdyb3d0aCBSYXRlIikKICAgICkgKwogICAgdGhlbWVfY2xhc3NpYygpICsKICAgIHRoZW1lKAogICAgICBheGlzLmxpbmUueS5yaWdodCA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJyZWQiKSwKICAgICAgYXhpcy50aWNrcy55LnJpZ2h0ID0gZWxlbWVudF9saW5lKGNvbG9yID0gInJlZCIpLAogICAgICBheGlzLnRleHQueS5yaWdodCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJyZWQiKSwKICAgICAgYXhpcy50aXRsZS55LnJpZ2h0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gInJlZCIpLAogICAgICBheGlzLmxpbmUueS5sZWZ0ID0gZWxlbWVudF9saW5lKGNvbG9yID0gImJsdWUiKSwKICAgICAgYXhpcy50aWNrcy55LmxlZnQgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmx1ZSIpLAogICAgICBheGlzLnRleHQueS5sZWZ0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsdWUiKSwKICAgICAgYXhpcy50aXRsZS55LmxlZnQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmx1ZSIpLAogICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSksCiAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEwLCBmYWNlPSJib2xkIiwgaGp1c3Q9MC41KQogICAgKSArCiAgICBsYWJzKHRpdGxlPXBhc3RlKGNvbnN0cnVjdCksCiAgICAgICAgIHg9Ik1lZGlhIikKfQoKIyBHZW5lcmF0ZSBhIGxpc3Qgb2YgcGxvdHMsIG9uZSBmb3IgZWFjaCBDb25zdHJ1Y3QKcGxvdF9saXN0IDwtIGNvbWJpbmVkX2RhdGEgJT4lCiAgZ3JvdXBfYnkoQ29uc3RydWN0KSAlPiUKICBncm91cF9tYXAofiBjcmVhdGVfZHVhbF9heGlzX3Bsb3QoLngsIC55JENvbnN0cnVjdCkpCgojIEFycmFuZ2UgYWxsIHBsb3RzIGluIGEgZ3JpZApncmlkX3Bsb3QgPC0gZG8uY2FsbChncmlkLmFycmFuZ2UsIGMocGxvdF9saXN0LCBuY29sPTMpKQoKIyBQcmludCB0aGUgZ3JpZCBwbG90CnByaW50KGdyaWRfcGxvdCkKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojc2F2ZSBwbG90CiMgRm9yIDEyIHNhbXBsZXMKZ2dzYXZlKGZpbGU9IkRpYWxvdXQvREhGUl9EaWFsb3V0X0ZpdG5lc3NfR3Jvd3RoX0NvbWJpbmVkX1Bsb3RzLnBkZiIsCiAgICAgICBwbG90ID0gZ3JpZF9wbG90LCAKICAgICAgIGRwaT02MDAsIHdpZHRoID0gMTAsIGhlaWdodCA9IDEwLCB1bml0cyA9ICJpbiIpCmBgYAoKIyMjIFBhdGhvZ2VuaWMgRGlhbC1vdXRzCgpgYGB7cn0KIyBGdW5jdGlvbiB0byBjcmVhdGUgYSBkdWFsLWF4aXMgcGxvdCBmb3IgZWFjaCBDb25zdHJ1Y3QKY3JlYXRlX2R1YWxfYXhpc19wbG90IDwtIGZ1bmN0aW9uKGRhdGEsIGNvbnN0cnVjdCkgewogICMgUmVtb3ZlIE05LVN1cHAgbWVkaWEgZm9yIFdULURIRlIKICBpZiAoY29uc3RydWN0ID09ICJXVC1ESEZSIikgewogICAgZGF0YSA8LSBkYXRhICU+JSBmaWx0ZXIoIShNZWRpYSA9PSAiTTktU3VwcCIpKQogIH0KICAKICBmaXRuZXNzX2RhdGEgPC0gZmlsdGVyKGRhdGEsIGFzc2F5X3R5cGUgPT0gIkZpdG5lc3MiKQogIGdyb3d0aF9yYXRlX2RhdGEgPC0gZmlsdGVyKGRhdGEsIGFzc2F5X3R5cGUgPT0gIkdyb3d0aCBSYXRlIikKICAKICAjIENhbGN1bGF0ZSB0aGUgcmF0aW8gZm9yIHRoZSBzZWNvbmRhcnkgYXhpcwogIHJhdGlvIDwtIGRpZmYocmFuZ2UoZml0bmVzc19kYXRhJHZhbHVlKSkgLyBkaWZmKHJhbmdlKGdyb3d0aF9yYXRlX2RhdGEkdmFsdWUpKQogIAogIGdncGxvdCgpICsKICAgICMgRml0bmVzcyBkYXRhCiAgICBnZW9tX2xpbmUoZGF0YSA9IGZpdG5lc3NfZGF0YSwgYWVzKHggPSBNZWRpYSwgeSA9IHZhbHVlLCBncm91cCA9IDEpLCBjb2xvciA9ICJibHVlIikgKwogICAgZ2VvbV9wb2ludChkYXRhID0gZml0bmVzc19kYXRhLCBhZXMoeCA9IE1lZGlhLCB5ID0gdmFsdWUpLCBjb2xvciA9ICJibHVlIikgKwogICAgIyBHcm93dGggUmF0ZSBkYXRhCiAgICBnZW9tX2xpbmUoZGF0YSA9IGdyb3d0aF9yYXRlX2RhdGEsIGFlcyh4ID0gTWVkaWEsIHkgPSB2YWx1ZSAqIHJhdGlvLCBncm91cCA9IDEpLCBjb2xvciA9ICJyZWQiKSArCiAgICBnZW9tX3BvaW50KGRhdGEgPSBncm93dGhfcmF0ZV9kYXRhLCBhZXMoeCA9IE1lZGlhLCB5ID0gdmFsdWUgKiByYXRpbyksIGNvbG9yID0gInJlZCIpICsKICAgICMgSG9yaXpvbnRhbCBsaW5lIGF0IHkgPSAtMSBmb3IgRml0bmVzcwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLTEsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyYXkiLCBzaXplID0gMC41KSArCiAgICAjIEF4ZXMgYW5kIGxhYmVscwogICAgc2NhbGVfeV9jb250aW51b3VzKAogICAgICBuYW1lID0gIkZpdG5lc3MiLAogICAgICBzZWMuYXhpcyA9IHNlY19heGlzKH4uL3JhdGlvLCBuYW1lID0gIkdyb3d0aCBSYXRlIikKICAgICkgKwogICAgdGhlbWVfY2xhc3NpYygpICsKICAgIHRoZW1lKAogICAgICBheGlzLmxpbmUueS5yaWdodCA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJyZWQiKSwKICAgICAgYXhpcy50aWNrcy55LnJpZ2h0ID0gZWxlbWVudF9saW5lKGNvbG9yID0gInJlZCIpLAogICAgICBheGlzLnRleHQueS5yaWdodCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJyZWQiKSwKICAgICAgYXhpcy50aXRsZS55LnJpZ2h0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gInJlZCIpLAogICAgICBheGlzLmxpbmUueS5sZWZ0ID0gZWxlbWVudF9saW5lKGNvbG9yID0gImJsdWUiKSwKICAgICAgYXhpcy50aWNrcy55LmxlZnQgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmx1ZSIpLAogICAgICBheGlzLnRleHQueS5sZWZ0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsdWUiKSwKICAgICAgYXhpcy50aXRsZS55LmxlZnQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmx1ZSIpLAogICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSksCiAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEwLCBmYWNlPSJib2xkIiwgaGp1c3Q9MC41KQogICAgKSArCiAgICBsYWJzKHRpdGxlPXBhc3RlKGNvbnN0cnVjdCksCiAgICAgICAgIHg9Ik1lZGlhIikKfQoKIyBGaWx0ZXIgY29tYmluZWRfZGF0YSBmb3IgdGhlIHNwZWNpZmljIGNvbnN0cnVjdHMgYW5kIHJlbmFtZQpzcGVjaWZpY19jb25zdHJ1Y3RzIDwtIGMoIkJhY2lsbHVzIGNlcmV1cyIsICJTdHJlcHRvY29jY3VzIHBuZXVtb25pYWUiLCAiV1QtREhGUiIpCmZpbHRlcmVkX2RhdGEgPC0gY29tYmluZWRfZGF0YSAlPiUKICBmaWx0ZXIoQ29uc3RydWN0ICVpbiUgYygiV1BfMDAwNjM3MjA5IiwgIldQXzAwMDE2MjQ1MyIsICJXVC1ESEZSIikpICU+JQogIG11dGF0ZShDb25zdHJ1Y3QgPSBjYXNlX3doZW4oCiAgICBDb25zdHJ1Y3QgPT0gIldQXzAwMDYzNzIwOSIgfiAiQmFjaWxsdXMgY2VyZXVzIiwKICAgIENvbnN0cnVjdCA9PSAiV1BfMDAwMTYyNDUzIiB+ICJTdHJlcHRvY29jY3VzIHBuZXVtb25pYWUiLAogICAgVFJVRSB+IENvbnN0cnVjdAogICkpCgojIEdlbmVyYXRlIGEgbGlzdCBvZiBwbG90cywgb25lIGZvciBlYWNoIHNwZWNpZmllZCBDb25zdHJ1Y3QKcGxvdF9saXN0IDwtIGZpbHRlcmVkX2RhdGEgJT4lCiAgZ3JvdXBfYnkoQ29uc3RydWN0KSAlPiUKICBncm91cF9tYXAofiBjcmVhdGVfZHVhbF9heGlzX3Bsb3QoLngsIC55JENvbnN0cnVjdCkpCgojIEFycmFuZ2UgYWxsIHBsb3RzIGluIGEgZ3JpZApncmlkX3Bsb3QgPC0gZG8uY2FsbChncmlkLmFycmFuZ2UsIGMocGxvdF9saXN0LCBuY29sPTMpKQoKIyBQcmludCB0aGUgZ3JpZCBwbG90CnByaW50KGdyaWRfcGxvdCkKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojc2F2ZSBwbG90CiMgRm9yIDEyIHNhbXBsZXMKZ2dzYXZlKGZpbGU9IkRpYWxvdXQvREhGUl9QYXRob2dlbmljX0RpYWxvdXRfRml0bmVzc19Hcm93dGhfQ29tYmluZWRfUGxvdHMucGRmIiwKICAgICAgIHBsb3QgPSBncmlkX3Bsb3QsIAogICAgICAgZHBpPTYwMCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gMywgdW5pdHMgPSAiaW4iKQpgYGAKCiMjIENvcnJlbGF0aW9ucwoKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgRmlyc3QsIGxldCdzIHJlc2hhcGUgdGhlIGRhdGEgdG8gaGF2ZSBGaXRuZXNzIGFuZCBHcm93dGggUmF0ZSBzaWRlIGJ5IHNpZGUKd2lkZV9kYXRhIDwtIGNvbWJpbmVkX2RhdGEgJT4lCiAgc2VsZWN0KENvbnN0cnVjdCwgTWVkaWEsIGFzc2F5X3R5cGUsIHZhbHVlKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gYXNzYXlfdHlwZSwgdmFsdWVzX2Zyb20gPSB2YWx1ZSkgJT4lCiAgZmlsdGVyKCFpcy5uYShGaXRuZXNzKSAmICFpcy5uYShgR3Jvd3RoIFJhdGVgKSkKCiMgRnVuY3Rpb24gdG8gY2FsY3VsYXRlIFNwZWFybWFuIGNvcnJlbGF0aW9uIGFuZCBwLXZhbHVlCnNwZWFybWFuX2NvciA8LSBmdW5jdGlvbihkYXRhKSB7CiAgY29yX3Rlc3QgPC0gY29yLnRlc3QoZGF0YSRGaXRuZXNzLCBkYXRhJGBHcm93dGggUmF0ZWAsIG1ldGhvZCA9ICJzcGVhcm1hbiIpCiAgdGliYmxlKAogICAgY29ycmVsYXRpb24gPSBjb3JfdGVzdCRlc3RpbWF0ZSwKICAgIHBfdmFsdWUgPSBjb3JfdGVzdCRwLnZhbHVlCiAgKQp9CgojIENhbGN1bGF0ZSBjb3JyZWxhdGlvbnMgZm9yIGVhY2ggTWVkaWEgdHlwZQpjb3JyZWxhdGlvbnMgPC0gd2lkZV9kYXRhICU+JQogIGdyb3VwX2J5KE1lZGlhKSAlPiUKICBkbyhzcGVhcm1hbl9jb3IoLikpICU+JQogIHVuZ3JvdXAoKQoKIyBQcmludCB0aGUgcmVzdWx0cwpwcmludChjb3JyZWxhdGlvbnMpCgojIE9wdGlvbmFsbHksIGNyZWF0ZSBhIHBsb3QgdG8gdmlzdWFsaXplIHRoZSBjb3JyZWxhdGlvbnMKY29ycmVsYXRpb25fcGxvdCA8LSBnZ3Bsb3QoY29ycmVsYXRpb25zLCBhZXMoeCA9IE1lZGlhLCB5ID0gY29ycmVsYXRpb24pKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAic2t5Ymx1ZSIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc3ByaW50ZigiJS4zZiIsIGNvcnJlbGF0aW9uKSwgCiAgICAgICAgICAgICAgICB5ID0gaWZlbHNlKGNvcnJlbGF0aW9uID4gMCwgY29ycmVsYXRpb24gKyAwLjA1LCBjb3JyZWxhdGlvbiAtIDAuMDUpKSwKICAgICAgICAgICAgdmp1c3QgPSBpZmVsc2UoY29ycmVsYXRpb25zJGNvcnJlbGF0aW9uID4gMCwgMCwgMSkpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc3ByaW50ZigicCA9ICUuM2YiLCBwX3ZhbHVlKSwgeSA9IDApLCAKICAgICAgICAgICAgdmp1c3QgPSBpZmVsc2UoY29ycmVsYXRpb25zJGNvcnJlbGF0aW9uID4gMCwgMS41LCAtMC41KSkgKwogIHRoZW1lX2NsYXNzaWMoKSArICAjIFRoaXMgcmVtb3ZlcyB0aGUgYmFja2dyb3VuZCBncmlkIGFuZCBhZGRzIGF4aXMgbGluZXMKICB0aGVtZSgKICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJibGFjayIpLCAgIyBFbnN1cmUgYXhpcyBsaW5lcyBhcmUgYmxhY2sKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgc2l6ZSA9IDEyKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgICMgUmVtb3ZlIG1ham9yIGdyaWQgbGluZXMKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkgICAjIFJlbW92ZSBtaW5vciBncmlkIGxpbmVzCiAgKSArCiAgbGFicyh0aXRsZSA9ICJTcGVhcm1hbiBDb3JyZWxhdGlvbnMgYmV0d2VlbiBGaXRuZXNzIGFuZCBHcm93dGggUmF0ZSBieSBNZWRpYSIsCiAgICAgICB5ID0gIkNvcnJlbGF0aW9uIENvZWZmaWNpZW50IikKCnByaW50KGNvcnJlbGF0aW9uX3Bsb3QpCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KI3NhdmUgcGxvdAojIEZvciAxMiBzYW1wbGVzCmdnc2F2ZShmaWxlPSJEaWFsb3V0L0RIRlJfRGlhbG91dF9TcGVhcm1hbl9Db3JyZWxhdGlvbnNfQmFycGxvdC5wZGYiLAogICAgICAgcGxvdCA9IGNvcnJlbGF0aW9uX3Bsb3QsIAogICAgICAgZHBpPTYwMCwgd2lkdGggPSA4LCBoZWlnaHQgPSA0LCB1bml0cyA9ICJpbiIpCmBgYAoKIyMjIENvcnJlbGF0aW9uIFBsb3RzCgpgYGB7ciBjbGFzcy5vdXRwdXQ9Imdvb2RDb2RlIn0KIyBGdW5jdGlvbiB0byBjcmVhdGUgYSBzY2F0dGVyIHBsb3QgZm9yIGVhY2ggbWVkaWEgdHlwZQpjcmVhdGVfc2NhdHRlcl9wbG90IDwtIGZ1bmN0aW9uKGRhdGEsIG1lZGlhX3R5cGUpIHsKICAjIFJlbW92ZSByb3dzIHdoZXJlIENvbnN0cnVjdCBpcyAibUNoZXJyeSIKICBkYXRhIDwtIGRhdGEgJT4lIGZpbHRlcihDb25zdHJ1Y3QgIT0gIm1DaGVycnkiKQogIAogICMgUGVyZm9ybSBTcGVhcm1hbiBjb3JyZWxhdGlvbiB0ZXN0CiAgY29yX3Rlc3QgPC0gY29yLnRlc3QoZGF0YSRGaXRuZXNzLCBkYXRhJGBHcm93dGggUmF0ZWAsIG1ldGhvZCA9ICJzcGVhcm1hbiIpCiAgCiAgZ2dwbG90KGRhdGEsIGFlcyh4ID0gYEdyb3d0aCBSYXRlYCwgeSA9IEZpdG5lc3MpKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC42KSArCiAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBjb2xvciA9ICJyZWQiLCBmaWxsID0gInNreWJsdWUiLCBzZSA9IFRSVUUpICsgICMgU2V0IHNlID0gVFJVRSB0byBpbmNsdWRlIGNvbmZpZGVuY2UgaW50ZXJ2YWwKICAgIGFubm90YXRlKCJ0ZXh0IiwgeCA9IG1pbihkYXRhJGBHcm93dGggUmF0ZWAsIG5hLnJtID0gVFJVRSksIHkgPSBtYXgoZGF0YSRGaXRuZXNzLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgbGFiZWwgPSBzcHJpbnRmKCJDb3JyZWxhdGlvbiA9ICUuMmZcbnAtdmFsdWUgPSAlLjNmIiwgY29yX3Rlc3QkZXN0aW1hdGUsIGNvcl90ZXN0JHAudmFsdWUpLAogICAgICAgICAgICAgaGp1c3QgPSAwLCB2anVzdCA9IDEpICsKICAgIHRoZW1lX2NsYXNzaWMoKSArICAjIFRoaXMgcmVtb3ZlcyB0aGUgYmFja2dyb3VuZCBncmlkIGFuZCBhZGRzIGF4aXMgbGluZXMKICAgIHRoZW1lKAogICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmxhY2siKSwKICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41KSwKICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMTAsIDMwLCAxMCwgMTApICAjIEFkZCByaWdodCBtYXJnaW4gZm9yIHgtYXhpcyBsYWJlbHMKICAgICkgKwogICAgbGFicygKICAgICAgdGl0bGUgPSBOVUxMLCAgIyBSZW1vdmUgdGhlIHRpdGxlCiAgICAgIHggPSBwYXN0ZSgiR3Jvd3RoIFJhdGUgaW4iLCBtZWRpYV90eXBlKSwgICMgU2V0IHgtYXhpcyB0aXRsZQogICAgICB5ID0gcGFzdGUoIkZpdG5lc3MgaW4iLCBtZWRpYV90eXBlKSAgICAgICAgIyBTZXQgeS1heGlzIHRpdGxlCiAgICApICsKICAgIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMC4wNSwgMC4wNSkpKSAgIyBBZGQgcGFkZGluZyB0byB4LWF4aXMgbGltaXRzCn0KCiMgR2VuZXJhdGUgYSBsaXN0IG9mIHBsb3RzLCBvbmUgZm9yIGVhY2ggbWVkaWEgdHlwZQpjb3JyX3Bsb3RfbGlzdCA8LSB3aWRlX2RhdGEgJT4lCiAgZ3JvdXBfYnkoTWVkaWEpICU+JQogIGdyb3VwX21hcCh+IGNyZWF0ZV9zY2F0dGVyX3Bsb3QoLngsIC55JE1lZGlhKSkKCiMgQXJyYW5nZSBhbGwgcGxvdHMgaW4gYSBncmlkCmNvcnJfZ3JpZF9wbG90IDwtIGRvLmNhbGwoZ3JpZC5hcnJhbmdlLCBjKGNvcnJfcGxvdF9saXN0LCBuY29sID0gMikpCgojIFByaW50IHRoZSBncmlkIHBsb3QKcHJpbnQoY29ycl9ncmlkX3Bsb3QpCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KI3NhdmUgcGxvdAojIEZvciAxMiBzYW1wbGVzCmdnc2F2ZShmaWxlPSJEaWFsb3V0L0RIRlJfRGlhbG91dF9TcGVhcm1hbl9Db3JyZWxhdGlvbnMucGRmIiwKICAgICAgIHBsb3QgPSBjb3JyX2dyaWRfcGxvdCwgCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDEwLCB1bml0cyA9ICJpbiIpCmBgYAoKYGBge3IgY2xhc3Mub3V0cHV0PSJnb29kQ29kZSJ9CiMgRnVuY3Rpb24gdG8gY3JlYXRlIGEgc2NhdHRlciBwbG90IGZvciBlYWNoIG1lZGlhIHR5cGUKY3JlYXRlX3NjYXR0ZXJfcGxvdCA8LSBmdW5jdGlvbihkYXRhLCBtZWRpYV90eXBlKSB7CiAgIyBSZW1vdmUgcm93cyB3aGVyZSBDb25zdHJ1Y3QgaXMgIm1DaGVycnkiCiAgZGF0YSA8LSBkYXRhICU+JSBmaWx0ZXIoQ29uc3RydWN0ICE9ICJtQ2hlcnJ5IikKICAKICAjIFBlcmZvcm0gU3BlYXJtYW4gY29ycmVsYXRpb24gdGVzdCAoaWYgbmVlZGVkIGZvciBvdGhlciBwdXJwb3NlcykKICBjb3JfdGVzdCA8LSBjb3IudGVzdChkYXRhJEZpdG5lc3MsIGRhdGEkYEdyb3d0aCBSYXRlYCwgbWV0aG9kID0gInNwZWFybWFuIikKICAKICBnZ3Bsb3QoZGF0YSwgYWVzKHggPSBgR3Jvd3RoIFJhdGVgLCB5ID0gRml0bmVzcykpICsKICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjYpICsKICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbG9yID0gInJlZCIsIGZpbGwgPSAic2t5Ymx1ZSIsIHNlID0gVFJVRSkgKyAgIyBTZXQgZmlsbCBjb2xvciBmb3IgY29uZmlkZW5jZSBpbnRlcnZhbCB0byBibHVlCiAgICB0aGVtZV9jbGFzc2ljKCkgKyAKICAgIHRoZW1lKAogICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmxhY2siKSwKICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41KSwKICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMTAsIDMwLCAxMCwgMTApCiAgICApICsKICAgIGxhYnMoCiAgICAgIHRpdGxlID0gTlVMTCwKICAgICAgeCA9IHBhc3RlKCJHcm93dGggUmF0ZSBpbiIsIG1lZGlhX3R5cGUpLCAgIyBTZXQgeC1heGlzIHRpdGxlCiAgICAgIHkgPSBwYXN0ZSgiRml0bmVzcyBpbiIsIG1lZGlhX3R5cGUpICAgICAgICAjIFNldCB5LWF4aXMgdGl0bGUKICAgICkgKwogICAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLjA1LCAwLjA1KSkpICAKfQoKIyBHZW5lcmF0ZSBhIGxpc3Qgb2YgcGxvdHMsIG9uZSBmb3IgZWFjaCBtZWRpYSB0eXBlCmNvcnJfcGxvdF9saXN0IDwtIHdpZGVfZGF0YSAlPiUKICBncm91cF9ieShNZWRpYSkgJT4lCiAgZ3JvdXBfbWFwKH4gY3JlYXRlX3NjYXR0ZXJfcGxvdCgueCwgLnkkTWVkaWEpKQoKIyBBcnJhbmdlIGFsbCBwbG90cyBpbiBhIGdyaWQKY29ycl9ncmlkX3Bsb3QgPC0gZG8uY2FsbChncmlkLmFycmFuZ2UsIGMoY29ycl9wbG90X2xpc3QsIG5jb2wgPSAyKSkKCiMgUHJpbnQgdGhlIGdyaWQgcGxvdApwcmludChjb3JyX2dyaWRfcGxvdCkKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQojc2F2ZSBwbG90CiMgRm9yIDEyIHNhbXBsZXMKZ2dzYXZlKGZpbGU9IkRpYWxvdXQvREhGUl9EaWFsb3V0X1NwZWFybWFuX0NvcnJlbGF0aW9ucy5uby5sYWJlbHMucGRmIiwKICAgICAgIHBsb3QgPSBjb3JyX2dyaWRfcGxvdCwgCiAgICAgICBkcGk9NjAwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDksIHVuaXRzID0gImluIikKYGBgCgojIFJlcHJvZHVjaWJpbGl0eQoKVGhlIHNlc3Npb24gaW5mb3JtYXRpb24gaXMgcHJvdmlkZWQgZm9yIGZ1bGwgcmVwcm9kdWNpYmlsaXR5LgpgYGB7cn0KZGV2dG9vbHM6OnNlc3Npb25faW5mbygpCmBgYA==