Goal & Background
update 2025-04-03
Veena has reorganized the gene sets. Here are the five groups:
Group 1: All pathways combined
Group 2: pathways involved in DNA metabolism and gene expression
Group 3: pathways involved in energy metabolism/mitochondria
Group 4: signal transduction pathways
Group 5: morphological features
Group 6: metabolic pathways.
Original goal and background
The goal is to calculate a number of derived values to characterize
the level of conservation or divergence among the Dauers / GIDs and
normalize the value by both the number of genes in the set and relative
to the distance from the average of Dauers to the WT L2/L3.
The transcript counts are first library size normalized and
transformed by the regularized log transformation into
counts_rlog. The resulting log transformed counts are used
as the input for downstream analyses. The rlog transformation moderates
the log2FC - log transformation is known to make lowly expressed genes
appear more variable (noise amplified). What this measure doesn’t
account for is gene length. Notably, the rlog counts are still read
counts, rather than transcript counts.
How can gene length affect the downstream analyses? I initially
thought it would give more weight in the distance calculation to long
genes with higher read counts when expressed at the same level as a
shorter genes. But I then realized that on a log scale, correcting for
gene length is equivalent to subtracting a constant from each row
(gene), where the constant is log2(gene_length/1000) (per
kilo base feature length). Location shifts such as this doesn’t affect
distance measures.
In a different analysis
(../../90-archived-analyses/output/not_in_use_within.sum.squares.zscore),
I transformed the rlog counts to a z-score on a per gene basis. The
rationale is that this give every gene equal weight and thus avoid
having variable genes contributing too much to the distance measure. The
potential caveat of the z-score is that lowly expressed genes that do
not actually have meaningful changes in expression can contribute to
distance measures if they have a high variance. This is a trade-off we
are making to have a more balanced measure of distance.
Overview by PCA
We use PCA to examine the overall structure of the data and
relationship between the dauers and the WT L2/L3 and adults.
I don’t expect the lowly expressed genes to affect the PCA.
Nonetheless, we will try both and see how their results differ. We will
plot all replicates for additional information about sample variability
vs true separation between samples.
All genes
pca_all <- pca(counts_rlog, metadata = sample_df)
biplot(pca_all, colby = "condition", title = "PCA for all genes")

Filtered expression matrix
pca_filtered <- pca(counts_rlog[keep, , drop = FALSE], metadata = sample_df)
biplot(pca_filtered, colby = "condition", title = "PCA for all genes")

DESeq2 provides a plotPCA() function, which by default
uses the top 500 most variable genes
plotPCA(rlog, intgroup = "condition", ntop = 10000)
using ntop=10000 top features by variance

#ggsave("../output/PCA/20250319-transcriptome-top10k-variable-genes-PCA.png")
Analyze gene sets
function to calculate a number of derived values
source("../script/20250403-within-dauer-compactness-score-functions.R")
load the new six group gene sets
gene_set_files <- list.files("../input/gene-lists",
pattern = "G*.tsv", full.names = TRUE)
# group 1 is just the union of the remaining five groups
all_pathways <- read_tsv("../input/gene-lists/Group.1.geneset.tsv")
gene.lists <- map(gene_set_files[-1], read_tsv)
names(gene.lists) <- c(
"2:DNA_metabolism_txn",
"3:Energy_metabolism_mito",
"4:Signal_transduction",
"5:Morphological_features",
"6:Metabolic_features"
)
gene_list_0 <- list_rbind(gene.lists, names_to = "group")
There are still duplicate rows in the gene list. Let’s examine them
and then remove the duplicated rows.
# check for duplicates
duplicates <- gene_list_0 |>
group_by(across(everything())) |>
filter(n() > 1) |>
arrange(group, pathway, WBGeneID)
duplicates
There are 10 duplicate rows
Remove the duplicates
gene_list <- distinct(gene_list_0) # remove duplicate rows
Are there pathways that belong to multiple groups?
# check for pathways that belong to multiple groups
gene_list |>
group_by(pathway) |>
filter(n_distinct(group) > 1) |>
arrange(group, pathway, WBGeneID)
Good, the five groups have no overlaps. Thus, group ID is just
another label on the pathways and won’t cause ambiguity.
What’s the distribution of the number of genes per pathway?
# check the distribution of the number of genes per pathway
gene_list |>
group_by(pathway) |>
summarise(n = n_distinct(WBGeneID)) |>
pull(n) |>
table()
6 7 8 9 10 11 12 13 14 15 17 18 19 20 21 22
4 3 4 3 2 3 5 7 3 4 2 3 2 4 2 1
23 24 25 26 27 28 29 30 33 34 35 37 38 39 40 42
2 2 3 3 1 1 1 2 1 3 1 2 2 5 1 1
44 45 51 54 55 60 67 68 79 82 88 96 98 99 104 107
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
109 116 128 160 167 302 332 338 352 440 446 732 1319 1431
1 1 1 1 1 1 1 1 1 1 1 1 1 1
The smallest pathways contain at least 6 genes.
Calculate the within cluster distance and other metrics
Now that we have all the pathways in a single file, we can just
iterate through all of them.
Split the gene sets and apply the calculation to each subset, combine
the results
# a list object to store the results of individual gene sets
gene_set_results <- with(gene_list, split(WBGeneID, pathway)) |>
keep(\(x) length(unique(x)) > 1) |> # keep only the groups with more than one gene
map(\(gene_set) within_dauer_compactness_score(sampleMean_rlog, gene_set))
# combine the results into a single data frame
gene_set_table <- bind_rows(gene_set_results, .id = "pathway") |>
left_join(distinct(gene_list, group, pathway), by = "pathway") |>
relocate(group, .before = pathway) |>
arrange(group, R2_dauer)
Export the list to a file
# write output to file, preserves more digits for later calculations
write_tsv(gene_set_table,
file.path("../output/within.sum.squares",
"20250403-geneset-stats-all.tsv"))
# also write a version with fewer digits
gene_set_table %>%
mutate(across(-c(pathway, group, gene_set_size), \(x) round(x, digits = 3))) %>%
write_tsv(file.path("../output/within.sum.squares",
"20250403-geneset-stats-all-rounded.tsv"))
RMSD vs d_L2L3 correlation
We observed a general linear relationship between the two measures
across groups (see below). Here, we would like to quantitative assess
the strength of correlation and test for significance.
gene_set_table %>%
#group_by(group) %>%
nest(.by = group) %>%
mutate(
model = map(data, \(df)
cor.test(~ RMSD_dauer + d_L2L3_to_dauer,
data = df, method = "pearson")),
tidied = map(model, broom::tidy)
) %>%
unnest(tidied) %>%
select(group, Pearson = estimate, p.value, conf.low, conf.high)
Same analysis for the GIDs
gene_set_table %>%
#group_by(group) %>%
nest(.by = group) %>%
mutate(
model = map(data, \(df)
cor.test(~ RMSD_gid + d_L2L3_to_gid,
data = df, method = "pearson")),
tidied = map(model, broom::tidy)
) %>%
unnest(tidied) %>%
select(group, Pearson = estimate, p.value, conf.low, conf.high)
Plots
RMSD vs d_L2L3 for groups 2-5
First, we will plot RMSD_dauer vs d_L2L3_to_dauer for groups 2-4
combined.
set_colors <- RColorBrewer::brewer.pal(5, "Set2")
names(set_colors) <- c(
"DNA_metabolism_txn", "Energy_metabolism_mito", "Signal_transduction",
"Morphological_features", "Metabolic_features"
)
p <- gene_set_table %>%
filter(group != "6:Metabolic_features") %>%
mutate(label = paste0(pathway, " (", gene_set_size, ")")) %>%
ggplot(aes(x = d_L2L3_to_dauer, y = RMSD_dauer,
label = label)) +
#stat_smooth(method = "lm", formula = y~x+0, se = FALSE,
# color = "skyblue2", linetype = 2) +
geom_point() +
#scale_color_manual("Groups", values = set_colors,
# labels = paste(1:5, names(set_colors), sep = ":")) +
xlab("Distance Dauer to L2/L3") + ylab("RMSD within Dauer") +
scale_x_continuous(limits = c(0.5, 2.62), expand = c(0.1, 0.05)) +
scale_y_continuous(limits = c(0.2, 0.92), expand = c(0.1, 0)) +
#guides(color = guide_legend(nrow = 2)) +
facet_wrap(~ group, nrow = 2) +
theme(strip.background = element_blank(),
strip.text = element_text(size = rel(0.8)),
panel.spacing = unit(0.5, "lines"))
#theme(legend.position = "top", legend.text = element_text(size = rel(0.6)))
ggsave(file = file.path("../output/within.sum.squares/img",
paste0("20250404-RMSD-vs-dL2L3-groups2-5.png")),
plot = p + ggrepel::geom_text_repel(size = 2.5, colour = "gray20"),
width = 7, height = 7)
print(ggplotly(p, tooltip = c("label"), width = 600, height = 600))
NULL
Next, we will plot RMSD_gid vs d_L2L3_to_gid for groups 2-4
combined.
p <- gene_set_table %>%
filter(group != "6:Metabolic_features") %>%
mutate(label = paste0(pathway, " (", gene_set_size, ")")) %>%
ggplot(aes(x = d_L2L3_to_gid, y = RMSD_gid,
label = label)) +
#stat_smooth(method = "lm", formula = y~x+0, se = FALSE,
# color = "skyblue2", linetype = 2) +
geom_point() +
#scale_color_manual("Groups", values = set_colors,
# labels = paste(1:5, names(set_colors), sep = ":")) +
xlab("Distance GID to L2/L3") + ylab("RMSD within GIDs") +
scale_x_continuous(limits = c(0.5, 2.62), expand = c(0.1, 0.05)) +
scale_y_continuous(limits = c(0.2, 0.92), expand = c(0.1, 0)) +
#guides(color = guide_legend(nrow = 2)) +
facet_wrap(~group, nrow = 2) +
theme(strip.background = element_blank(),
strip.text = element_text(size = rel(0.8)),
panel.spacing = unit(0.5, "lines"))
#theme(legend.position = "top", legend.text = element_text(size = rel(0.6)))
ggsave(file = file.path("../output/within.sum.squares/img",
paste0("20250404-RMSD-vs-dL2L3-groups2-5-GIDs.png")),
plot = p + ggrepel::geom_text_repel(size = 2.5, colour = "gray20"),
width = 7, height = 7)
print(ggplotly(p, tooltip = c("label"), width = 600, height = 600))
NULL
RMSD vs d_L2L3 for each group separately
We will generate one RMSD_dauer vs d_L2L3_to_dauer plot for each
group
#set_colors <- RColorBrewer::brewer.pal(5, "Accent")
#names(set_colors) <- names(gene.lists)
for (set in names(gene.lists)){
tmp <- gene_set_table %>%
filter(group == set) %>%
mutate(label = paste0(pathway, " (", gene_set_size, ")"))
# if there are more than 15 pathways in a group, use 2.5 for label size
# otherwise, use 3
label_size <- ifelse(nrow(tmp) > 15, 2.5, 3)
# make plot
p <- tmp %>%
ggplot(aes(x = d_L2L3_to_dauer, y = RMSD_dauer,
label = label)) +
#stat_smooth(method = "lm", formula = y~x+0, se = FALSE,
# color = "skyblue2", linetype = 2) +
geom_point(aes()) +
#scale_color_manual(NULL, values = set_colors) +
xlab("Distance Dauer to L2/L3") + ylab("RMSD within Dauer") +
scale_x_continuous(limits = c(0.5, 2.62), expand = c(0.1, 0.05)) +
scale_y_continuous(limits = c(0.2, 0.92), expand = c(0.1, 0)) +
ggtitle(set) + theme(legend.position = "bottom")
ggsave(file = file.path("../output/within.sum.squares/img",
paste0("20250403-RMSD-vs-dL2L3-",
gsub(":", "_", set), ".png")),
plot = p + ggrepel::geom_text_repel(size = label_size, colour = "gray20"),
width = 6, height = 6)
print(ggplotly(p, tooltip = c("label")))
}
Do the same for RMSD_gid vs d_L2L3_to_gid
for (set in names(gene.lists)){
tmp <- gene_set_table %>%
filter(group == set) %>%
mutate(label = paste0(pathway, " (", gene_set_size, ")"))
# if there are more than 15 pathways in a group, use 2.5 for label size
# otherwise, use 3
label_size <- ifelse(nrow(tmp) > 15, 2.5, 3)
# make plot
p <- tmp %>%
ggplot(aes(x = d_L2L3_to_gid, y = RMSD_gid,
label = label)) +
#stat_smooth(method = "lm", formula = y~x+0, se = FALSE,
# color = "skyblue2", linetype = 2) +
geom_point(aes()) +
#scale_color_manual(NULL, values = set_colors) +
xlab("Distance GID to L2/L3") + ylab("RMSD within GIDs") +
scale_x_continuous(limits = c(0.5, 2.62), expand = c(0.1, 0.05)) +
scale_y_continuous(limits = c(0.2, 0.92), expand = c(0.1, 0)) +
ggtitle(set) + theme(legend.position = "bottom")
ggsave(file = file.path("../output/within.sum.squares/img",
paste0("20250403-RMSD-vs-dL2L3-",
gsub(":", "_", set), "-GIDs.png")),
plot = p + ggrepel::geom_text_repel(size = label_size, colour = "gray20"),
width = 6, height = 6)
print(ggplotly(p, tooltip = c("label")))
}
Compare all five groups
Let’s compare the distribution of the metrics between the five
groups. We will focus on RMSD_dauer, d_L2L3_to_dauer, R2_dauer, and
1-avg_Pearson_dauer
List the five groups
2:DNA_metabolism_txn , 3:Energy_metabolism_mito ,
4:Signal_transduction , 5:Morphological_features ,
6:Metabolic_features
tmp <- gene_set_table %>%
mutate(d_PCC_dauer = 1 - avg_Pearson_dauer,
r_toL2L3_vs_toAdult = d_L2L3_to_dauer / d_adult_to_dauer) %>%
select(group, pathway, gene_set_size, RMSD_dauer, d_L2L3_to_dauer,
d_adult_to_dauer, R2_dauer, d_PCC_dauer, r_toL2L3_vs_toAdult) %>%
pivot_longer(cols = -c(group, pathway, gene_set_size),
names_to = "metric", values_to = "value") %>%
mutate(metric = factor(metric,
levels = c("d_L2L3_to_dauer", "d_adult_to_dauer",
"r_toL2L3_vs_toAdult","RMSD_dauer",
"R2_dauer", "d_PCC_dauer"),
labels = c("Distance to L2/L3",
"Distance to adult",
"d_L2L3 / d_adult",
"RMSD", "(R2) RMSD / d_L2L3",
"1 - avg. Pearson")))
p <- tmp %>%
separate(group, into = c("group", "group_name"), sep = ":") %>%
#mutate(label = paste(pathway, round(value, 3), sep = ":")) %>%
ggplot(aes(x = group, y = value)) +
#geom_boxplot(outlier.shape = NA, outlier.color = NA) +
geom_jitter(aes(text = paste(pathway, round(value, 3), sep = ":")),
width = 0.2, size = 1.2, color = "gray40", alpha = 0.8) +
stat_summary(fun.data = "mean_cl_boot", geom = "crossbar",
linewidth = 0.2, width = 0.4, color = "red") +
scale_color_manual(NULL, values = set_colors, guide = "none") +
facet_wrap(~metric, scales = "free_y") +
ggtitle("Distribution of metrics, all dauers") +
theme(strip.background = element_blank(),
strip.text = element_text(size = rel(0.8)),
panel.spacing = unit(0.5, "lines"))
Warning: Ignoring unknown aesthetics: text
print(ggplotly(p, tooltip = "text",
width = 800, height = 500))
NULL
ggsave(file.path("../output/within.sum.squares/img",
"20250405-compare-metric-distribution-groups2-6-dauers.png"),
width = 8, height = 5)
Repeat for RMSD_gid, d_L2L3_to_gid, R2_gid, and 1-avg_Pearson_gid
tmp <- gene_set_table %>%
mutate(d_PCC_gid = 1 - avg_Pearson_gid,
r_toL2L3_vs_toAdult = d_L2L3_to_gid / d_adult_to_gid) %>%
select(group, pathway, gene_set_size, RMSD_gid, d_L2L3_to_gid,
d_adult_to_gid, R2_gid, d_PCC_gid, r_toL2L3_vs_toAdult) %>%
pivot_longer(cols = -c(group, pathway, gene_set_size),
names_to = "metric", values_to = "value") %>%
mutate(metric = factor(metric,
levels = c("d_L2L3_to_gid", "d_adult_to_gid",
"r_toL2L3_vs_toAdult","RMSD_gid",
"R2_gid", "d_PCC_gid"),
labels = c("Distance to L2/L3",
"Distance to adult",
"d_L2L3 / d_adult",
"RMSD", "(R2) RMSD / d_L2L3",
"1 - avg. Pearson")))
p <- tmp %>%
separate(group, into = c("group", "group_name"), sep = ":") %>%
#mutate(label = paste(pathway, round(value, 3), sep = ":")) %>%
ggplot(aes(x = group, y = value)) +
#geom_boxplot(outlier.shape = NA, outlier.color = NA) +
geom_jitter(aes(text = paste(pathway, round(value, 3), sep = ":")),
width = 0.2, size = 1.2, color = "gray40", alpha = 0.8) +
stat_summary(fun.data = "mean_cl_boot", geom = "crossbar",
linewidth = 0.2, width = 0.4, color = "red") +
scale_color_manual(NULL, values = set_colors, guide = "none") +
facet_wrap(~metric, scales = "free_y") +
ggtitle("Distribution of metrics, GIDs") +
theme(strip.background = element_blank(),
strip.text = element_text(size = rel(0.8)),
panel.spacing = unit(0.5, "lines"))
Warning: Ignoring unknown aesthetics: text
print(ggplotly(p, tooltip = "text",
width = 800, height = 500))
NULL
ggsave(file.path("../output/within.sum.squares/img",
"20250405-compare-metric-distribution-groups2-6-GIDs.png"),
width = 8, height = 5)
—– Don’t go below this line !—–
I haven’t updated the code and results below this yet.
plot_individual_pathway <- function(gene_set, pathway_name){
# this function takes a gene set and the name of the pathway within that set
# then plots pairwise scatter plots along with the corresponding PCC or SCC.
# it also plots the PCA plot.
# Gather the data
genes <- dplyr::filter(gene_set, pathway == pathway_name) |> pull(WBGeneID)
# all replicates, for PCA
rep_matrix <- counts_rlog[genes, , drop = FALSE]
# mean expression only, for pairwise correlations and heatmap
mean_matrix <- sampleMean_rlog[genes, , drop = FALSE]
# Examine the pairwise relationships.
png(file.path("../output/within.sum.squares/img",
paste0(pathway_name, "-pairs-PCC-plot.png")),
width = 1000, height = 1000)
# Plotting the correlation matrix
pairs(mean_matrix[, c("WT_CTRL", "WT_L2_L3", all_dauer)],
upper.panel = panel.cor, # Correlation panel
lower.panel = panel.smooth, # Smoothed regression lines
main = paste0("Pairwise PCCs for ", pathway_name))
dev.off()
# Spearman's correlation, rank only.
png(file.path("../output/within.sum.squares/img",
paste0(pathway_name, "-pairs-SCC-plot.png")),
width = 1000, height = 1000)
# Plotting the correlation matrix
pairs(mean_matrix[, c("WT_CTRL", "WT_L2_L3", all_dauer)],
upper.panel = function(x, y, ...) panel.cor(x, y, method = "spearman"),
lower.panel = panel.smooth, # Smoothed regression lines
main = paste0("Pairwise SCCs for ", pathway_name))
dev.off()
# PCA plot
PCA <- pca(rep_matrix, metadata = sample_df)
biplot(PCA, colby = "condition", title = paste0("PCA for ", pathway_name))
ggsave(file.path("../output/within.sum.squares/img",
paste0(pathway_name, "-pairs-PCA-plot.png")),
width = 6, height = 6)
# return the expression matrix
return(mean_matrix)
}
Iterate through all Set 1 pathways
core_dauer_1_expressions <-
lapply(unique(core_dauer_1$pathway),
function(pathway){plot_individual_pathway(core_dauer_1, pathway)}
)
names(core_dauer_1_expressions) <- unique(core_dauer_1$pathway)
Iterate through all Set 2 pathways
core_dauer_2_expressions <-
lapply(unique(core_dauer_2$pathway),
function(pathway){plot_individual_pathway(core_dauer_2, pathway)}
)
names(core_dauer_2_expressions) <- unique(core_dauer_2$pathway)
Set 3: all Kim mountain genes
Read gene list and add gene name column
# load Wormbase gene table for ID conversion
#wormbase.genes <- read_tsv(here("input", "wormbase.genes.names.txt"))
# load Wormbase gene IDs
#gene_IDs <- wb_load_gene_ids("WS269")
# load gene list and add gene name column
kim_mountains <- read_tsv("../input/updated.names.kim.mountains.tsv") |>
# rename and order columns to be consistent with the other sets
select(pathway = Kim_group, WBGeneID, geneName) |>
distinct() # remove duplicate rows
Apply the calculation
# a list object to store the results of individual gene sets
Kim_result <- list(
"all_Kim" = within_dauer_compactness_score(sampleMean_rlog, kim_mountains$WBGeneID)
)
Do the same for each subgroup
Save the output
# make a table and sort by the ratio of average distance to centroid for dauers
# over distance from dauer centroid to L2/L3
# combine with the core dauer pathway results
Kim_core_dauer_result_table <- bind_rows(
"Kim mountain" = bind_rows(Kim_result, .id = "subset"),
"Core dauer set 1" = bind_rows(core_dauer_1_result, .id = "subset"),
"Core dauer set 2" = bind_rows(core_dauer_2_result, .id = "subset"),
.id = "Gene_set"
) |> arrange(R1_dauer)
# write output to file, preserves more digits for later calculations
write_tsv(Kim_core_dauer_result_table,
file.path("../output/within.sum.squares",
"20250307-geneset-stats-Kim-mountain-and-core-dauer.tsv"))
# save a rounded form to make it easier to view
Kim_core_dauer_result_table %>%
mutate(across(-c(Gene_set, subset, gene_set_size), \(x) round(x, digits = 3))) %>%
write_tsv(file.path("../output/within.sum.squares",
"20250307-geneset-stats-Kim-mountain-and-core-dauer-rounded.tsv"))
Plots for Set 1-3
Instead of ranking pathways by R1 or R2, we can plot the pathways by
their RMSD and distance between mean dauer to L2/L3
p1 <- Kim_core_dauer_result_table %>%
#filter(Gene_set == "Kim mountain") %>%
ggplot(aes(x = d_L2L3_to_dauer, y = RMSD_dauer)) +
stat_smooth(method = "lm", formula = y~x+0,
color = "skyblue2", linetype = 2) +
geom_point(aes(color = Gene_set)) +
scale_color_manual(NULL, values = set_colors) +
#scale_color_brewer(NULL, palette = "Dark2") +
ggrepel::geom_text_repel(
aes(label = paste0(subset, " (", gene_set_size, ")")),
size = 3, colour = "gray20") +
xlab("Distance Dauer to L2/L3") + ylab("RMSD within Dauer") +
scale_x_continuous(expand = c(0.1, 0.05)) +
scale_y_continuous(expand = c(0.1, 0)) +
ggtitle("All Dauers") + theme(legend.position = "bottom")
p1
ggsave(file.path("../output/within.sum.squares/img",
"Kim-and-Core-Dauer-RMSD-vs-dL2L3-all-dauers.png"),
width = 6, height = 6)
And for the GIDs
p2 <- Kim_core_dauer_result_table %>%
ggplot(aes(x = d_L2L3_to_gid, y = RMSD_gid)) +
stat_smooth(method = "lm", formula = y~x+0,
color = "skyblue2", linetype = 2) +
geom_point(aes(color = Gene_set)) +
scale_color_manual(NULL, values = set_colors) +
#scale_color_brewer(NULL, palette = "Dark2") +
ggrepel::geom_text_repel(
aes(label = paste0(subset, " (", gene_set_size, ")")),
size = 3, colour = "gray20") +
xlab("Distance Dauer to L2/L3") + ylab("RMSD within Dauer") +
scale_x_continuous(expand = c(0.1, 0.05)) +
scale_y_continuous(expand = c(0.1, 0)) +
ggtitle("GIDs") + theme(legend.position = "bottom")
p2
ggsave(file.path("../output/within.sum.squares/img",
"Kim-and-Core-Dauer-RMSD-vs-dL2L3-GIDs.png"),
width = 6, height = 6)
PCA for Set 1-3
Here, we generate PCA plots for individual pathways as a diagnostic
tool. We will use the counts_rlog object, which means we
will 1. Use all replicates instead of sample means; 2. Include WT adult
3. Unfiltered, include all genes
1 and 2 are to provide additional information for the diagnosis (with
little cost). (1) gives a sense of how variable are the biological
replicates within each dauer / control type; Inclusion of WT adult
provides an additional reference.
3 because lowly expressed genes will make very little contribution to
PCA (since we are going to center but NOT scale the data).
plotPCA(rlog[unique(c(core_dauer_1$WBGeneID, core_dauer_2$WBGeneID,
kim_mountains$WBGeneID)), ,drop=FALSE],
intgroup = "condition", ntop = 10000)
Case study
Retinoblastoma complex
Gather the data
RbC_genes <- filter(kim_mountains, Kim_group == "Retinoblastoma complex") |>
pull(WBGeneID)
# all replicates, for PCA
RbC_rep_matrix <- counts_rlog[RbC_genes, , drop = FALSE]
# mean expression only, for pairwise correlations and heatmap
RbC_mean_matrix <- sampleMean_rlog[RbC_genes, , drop = FALSE]
# display the matrix
round(RbC_mean_matrix, digits = 2)
Set 4: Metabolic Pathways
The pathway gene lists are from https://wormflux.umassmed.edu/
# This code is only run to get geneset names in the same format as the Kim mountains and insulin.
wormflux = xlsx::read.xlsx("../../../Deseq_data/Immune_genes_tables_Outside_tables/wormflux_4.xlsx",sheetIndex = 2,header = T)
list.wormflux = unclass(wormflux)
list.wormflux.not.na = map(list.wormflux, function(a)na.omit(a))
gids = wb_load_gene_ids("WS295")
list.wormflux.df = map(list.wormflux.not.na,.f = function(b)data.frame(genes = b))
bind.wormflux.df = bind_rows(list.wormflux.df,.id = "id") |> dplyr::rename(class=id)
bind.wormflux.df.table = bind.wormflux.df |> mutate(geneName = wbData::i2s(genes,gids) ) |> dplyr::rename(pathway = class,WBGeneID =genes)
all(bind.wormflux.df.table$genes %in% rownames(sampleMean_rlog))
write_tsv(bind.wormflux.df.table, file = here("input","updated.names.wormflux.tsv"))
Read in the gene list and apply the calculation
# How the data will be load
Metabolic_pathways <- read_tsv(file = "../input/updated.names.wormflux.tsv")
# a list object to store the results of individual gene sets
Metabolic_result <- list(
"all_Metabolic" = within_dauer_compactness_score(sampleMean_rlog,
Metabolic_pathways$WBGeneID)
)
subset_Metabolic_result <- with(Metabolic_pathways, split(WBGeneID, pathway)) |>
keep(\(x) length(unique(x)) > 1) |> # keep only the groups with more than one gene
map(\(gene_set) within_dauer_compactness_score(sampleMean_rlog, gene_set))
Metabolic_result <- append(Metabolic_result, subset_Metabolic_result)
Save the output
# make a table and sort by the ratio of average distance to centroid for dauers
# over distance from dauer centroid to L2/L3
# combine with the core dauer pathway results
Metabolic_result_table <- bind_rows(Metabolic_result, .id = "subset") |>
mutate(Gene_set = "Metabolic pathways") |>
relocate(Gene_set, .before = subset) |>
arrange(R1_dauer)
# write output to file, preserves more digits for later calculations
write_tsv(Metabolic_result_table,
here::here("output/within.sum.squares",
"20250307-geneset-stats-Metabolic-pathways.tsv"))
# save a rounded form to make it easier to view
Metabolic_result_table %>%
mutate(across(-c(Gene_set, subset, gene_set_size), \(x) round(x, digits = 3))) %>%
write_tsv(here::here("output/within.sum.squares",
"20250307-geneset-stats-Metabolic-pathways-rounded.tsv"))
Plots for Set 4
Instead of ranking pathways by R1 or R2, we can plot the pathways by
their RMSD and distance between mean dauer to L2/L3
p1 <- Metabolic_result_table %>%
ggplot(aes(x = d_L2L3_to_dauer, y = RMSD_dauer)) +
stat_smooth(method = "lm", formula = y~x+0,
color = "skyblue2", linetype = 2) +
geom_point(aes(color = gene_set_size < 10)) +
scale_color_manual("Fewer than 10 genes", values = c("black", "pink")) +
ggrepel::geom_text_repel(
aes(label = paste0(subset, " (", gene_set_size, ")")),
size = 2, colour = "gray20", min.segment.length = 0) +
xlab("Distance Dauer to L2/L3") + ylab("RMSD within Dauer") +
scale_x_continuous(expand = c(0.1, 0.05)) +
scale_y_continuous(expand = c(0.1, 0)) +
ggtitle("All Dauers") + theme(legend.position = "bottom")
p1
ggsave(file.path("../output/within.sum.squares/img",
"Metabolic-pathways-RMSD-vs-dL2L3-all-dauers.png"),
width = 6, height = 6)
And for the GIDs
p2 <- Metabolic_result_table %>%
ggplot(aes(x = d_L2L3_to_gid, y = RMSD_gid)) +
stat_smooth(method = "lm", formula = y~x+0,
color = "skyblue2", linetype = 2) +
geom_point(aes(color = gene_set_size < 10)) +
scale_color_manual("Fewer than 10 genes", values = c("black", "pink")) +
ggrepel::geom_text_repel(
aes(label = paste0(subset, " (", gene_set_size, ")")),
size = 2, colour = "gray20", min.segment.length = 0) +
#xlab("Distance Dauer to L2/L3") + ylab("RMSD within Dauer") +
scale_x_continuous(expand = c(0.1, 0.05)) +
scale_y_continuous(expand = c(0.1, 0)) +
xlab("Distance Dauer to L2/L3") + ylab("RMSD within GIDs") +
ggtitle("GIDs") + theme(legend.position = "bottom")
p2
ggsave(file.path("../output/within.sum.squares/img",
"Metabolic-pathways-RMSD-vs-dL2L3-GIDs.png"),
width = 6, height = 6)
–>
LS0tCnRpdGxlOiAiV2l0aGluIG1lYXN1cmVtZW50IERhdWVycyIKYXV0aG9yOiAiQmluIFouIEhlIgpkYXRlOiAiMjAyNS0wNC0wMywgdXBkYXRlZCBvbiBgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IFRSVUUKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdGhlbWU6IGZsYXRseQotLS0KCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KERFU2VxMikKI2xpYnJhcnkodHhpbXBvcnQpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShQQ0F0b29scykKI2xpYnJhcnkocGF0Y2h3b3JrKQojbGlicmFyeSh3YkRhdGEpCiNsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKI2xpYnJhcnkob3JnLkNlLmVnLmRiKQojbGlicmFyeShET1NFKQpgYGAKCkNvbW1vbiBzZXR0aW5ncwoKYGBge3J9CiMgZW5yaWNobWVudCBjb2xvcgpvcHRpb25zKGVucmljaHBsb3QuY29sb3VycyA9IGMoInJlZCIsImJsdWUiKSkKIyBwbG90dGluZyB0aGVtZQpvbGQgPC0gdGhlbWVfc2V0KHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE2KSkKYGBgCgojIEdvYWwgJiBCYWNrZ3JvdW5kCjxoMiBzdHlsZT0iY29sb3I6cmVkIj51cGRhdGUgMjAyNS0wNC0wMzwvaDI+ClZlZW5hIGhhcyByZW9yZ2FuaXplZCB0aGUgZ2VuZSBzZXRzLiBIZXJlIGFyZSB0aGUgZml2ZSBncm91cHM6CgpHcm91cCAxOiBBbGwgcGF0aHdheXMgY29tYmluZWQKCkdyb3VwIDI6IHBhdGh3YXlzIGludm9sdmVkIGluIEROQSBtZXRhYm9saXNtIGFuZCBnZW5lIGV4cHJlc3Npb24KCkdyb3VwIDM6IHBhdGh3YXlzIGludm9sdmVkIGluIGVuZXJneSBtZXRhYm9saXNtL21pdG9jaG9uZHJpYQoKR3JvdXAgNDogc2lnbmFsIHRyYW5zZHVjdGlvbiBwYXRod2F5cwoKR3JvdXAgNTogbW9ycGhvbG9naWNhbCBmZWF0dXJlcwoKR3JvdXAgNjogbWV0YWJvbGljIHBhdGh3YXlzLgoKIyMgT3JpZ2luYWwgZ29hbCBhbmQgYmFja2dyb3VuZApUaGUgZ29hbCBpcyB0byBjYWxjdWxhdGUgYSBudW1iZXIgb2YgZGVyaXZlZCB2YWx1ZXMgdG8gY2hhcmFjdGVyaXplIHRoZSBsZXZlbCBvZiBjb25zZXJ2YXRpb24gb3IgZGl2ZXJnZW5jZSBhbW9uZyB0aGUgRGF1ZXJzIC8gR0lEcyBhbmQgbm9ybWFsaXplIHRoZSB2YWx1ZSBieSBib3RoIHRoZSBudW1iZXIgb2YgZ2VuZXMgaW4gdGhlIHNldCBhbmQgcmVsYXRpdmUgdG8gdGhlIGRpc3RhbmNlIGZyb20gdGhlIGF2ZXJhZ2Ugb2YgRGF1ZXJzIHRvIHRoZSBXVCBMMi9MMy4KClRoZSB0cmFuc2NyaXB0IGNvdW50cyBhcmUgZmlyc3QgbGlicmFyeSBzaXplIG5vcm1hbGl6ZWQgYW5kIHRyYW5zZm9ybWVkIGJ5IHRoZSByZWd1bGFyaXplZCBsb2cgdHJhbnNmb3JtYXRpb24gaW50byBgY291bnRzX3Jsb2dgLiBUaGUgcmVzdWx0aW5nIGxvZyB0cmFuc2Zvcm1lZCBjb3VudHMgYXJlIHVzZWQgYXMgdGhlIGlucHV0IGZvciBkb3duc3RyZWFtIGFuYWx5c2VzLiBUaGUgcmxvZyB0cmFuc2Zvcm1hdGlvbiBtb2RlcmF0ZXMgdGhlIGxvZzJGQyAtIGxvZyB0cmFuc2Zvcm1hdGlvbiBpcyBrbm93biB0byBtYWtlIGxvd2x5IGV4cHJlc3NlZCBnZW5lcyBhcHBlYXIgbW9yZSB2YXJpYWJsZSAobm9pc2UgYW1wbGlmaWVkKS4gV2hhdCB0aGlzIG1lYXN1cmUgZG9lc24ndCBhY2NvdW50IGZvciBpcyBnZW5lIGxlbmd0aC4gTm90YWJseSwgdGhlIHJsb2cgY291bnRzIGFyZSBzdGlsbCByZWFkIGNvdW50cywgcmF0aGVyIHRoYW4gdHJhbnNjcmlwdCBjb3VudHMuCgpIb3cgY2FuIGdlbmUgbGVuZ3RoIGFmZmVjdCB0aGUgZG93bnN0cmVhbSBhbmFseXNlcz8gSSBpbml0aWFsbHkgdGhvdWdodCBpdCB3b3VsZCBnaXZlIG1vcmUgd2VpZ2h0IGluIHRoZSBkaXN0YW5jZSBjYWxjdWxhdGlvbiB0byBsb25nIGdlbmVzIHdpdGggaGlnaGVyIHJlYWQgY291bnRzIHdoZW4gZXhwcmVzc2VkIGF0IHRoZSBzYW1lIGxldmVsIGFzIGEgc2hvcnRlciBnZW5lcy4gQnV0IEkgdGhlbiByZWFsaXplZCB0aGF0IG9uIGEgbG9nIHNjYWxlLCBjb3JyZWN0aW5nIGZvciBnZW5lIGxlbmd0aCBpcyBlcXVpdmFsZW50IHRvIHN1YnRyYWN0aW5nIGEgY29uc3RhbnQgZnJvbSBlYWNoIHJvdyAoZ2VuZSksIHdoZXJlIHRoZSBjb25zdGFudCBpcyBgbG9nMihnZW5lX2xlbmd0aC8xMDAwKWAgKHBlciBraWxvIGJhc2UgZmVhdHVyZSBsZW5ndGgpLiBMb2NhdGlvbiBzaGlmdHMgc3VjaCBhcyB0aGlzIGRvZXNuJ3QgYWZmZWN0IGRpc3RhbmNlIG1lYXN1cmVzLgoKSW4gYSBkaWZmZXJlbnQgYW5hbHlzaXMgKGAuLi8uLi85MC1hcmNoaXZlZC1hbmFseXNlcy9vdXRwdXQvbm90X2luX3VzZV93aXRoaW4uc3VtLnNxdWFyZXMuenNjb3JlYCksIEkgdHJhbnNmb3JtZWQgdGhlIHJsb2cgY291bnRzIHRvIGEgei1zY29yZSBvbiBhIHBlciBnZW5lIGJhc2lzLiBUaGUgcmF0aW9uYWxlIGlzIHRoYXQgdGhpcyBnaXZlIGV2ZXJ5IGdlbmUgZXF1YWwgd2VpZ2h0IGFuZCB0aHVzIGF2b2lkIGhhdmluZyB2YXJpYWJsZSBnZW5lcyBjb250cmlidXRpbmcgdG9vIG11Y2ggdG8gdGhlIGRpc3RhbmNlIG1lYXN1cmUuIFRoZSBwb3RlbnRpYWwgY2F2ZWF0IG9mIHRoZSB6LXNjb3JlIGlzIHRoYXQgbG93bHkgZXhwcmVzc2VkIGdlbmVzIHRoYXQgZG8gbm90IGFjdHVhbGx5IGhhdmUgbWVhbmluZ2Z1bCBjaGFuZ2VzIGluIGV4cHJlc3Npb24gY2FuIGNvbnRyaWJ1dGUgdG8gZGlzdGFuY2UgbWVhc3VyZXMgaWYgdGhleSBoYXZlIGEgaGlnaCB2YXJpYW5jZS4gVGhpcyBpcyBhIHRyYWRlLW9mZiB3ZSBhcmUgbWFraW5nIHRvIGhhdmUgYSBtb3JlIGJhbGFuY2VkIG1lYXN1cmUgb2YgZGlzdGFuY2UuCgojIEltcG9ydCBkYXRhClRoZSBwcmVwcm9jZXNzaW5nIG9mIHRoZSBxdWFudCBkYXRhIHdlcmUgcGVyZm9ybWVkIGluIGFuIGVhcmx5IGFuYWx5c2lzIChgLi4vLi4vOTAtYXJjaGl2ZWQtYW5hbHlzZXMvQW5hbHlzaXMvMjAyNTAyMjYtSEJaLXdpdGhpbi5zdW1zcXVhcmVzLlJtZGApIGFuZCBzYXZlZCB0byBhbiBSRGF0YSBmaWxlLiBXZSB3aWxsIGxvYWQgdGhhdCBmaWxlIGhlcmUuCgpgYGB7cn0KbG9hZCgiLi4vaW5wdXQvMjAyNTAzMTAtRGF1ZXItcHJvamVjdC1ERVNlcTItb2JqZWN0cy5yZGF0YSIpCmBgYAoKIyBPdmVydmlldyBieSBQQ0EKV2UgdXNlIFBDQSB0byBleGFtaW5lIHRoZSBvdmVyYWxsIHN0cnVjdHVyZSBvZiB0aGUgZGF0YSBhbmQgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIGRhdWVycyBhbmQgdGhlIFdUIEwyL0wzIGFuZCBhZHVsdHMuCgpJIGRvbid0IGV4cGVjdCB0aGUgbG93bHkgZXhwcmVzc2VkIGdlbmVzIHRvIGFmZmVjdCB0aGUgUENBLiBOb25ldGhlbGVzcywgd2Ugd2lsbCB0cnkgYm90aCBhbmQgc2VlIGhvdyB0aGVpciByZXN1bHRzIGRpZmZlci4gV2Ugd2lsbCBwbG90IGFsbCByZXBsaWNhdGVzIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIGFib3V0IHNhbXBsZSB2YXJpYWJpbGl0eSB2cyB0cnVlIHNlcGFyYXRpb24gYmV0d2VlbiBzYW1wbGVzLgoKQWxsIGdlbmVzCmBgYHtyfQpwY2FfYWxsIDwtIHBjYShjb3VudHNfcmxvZywgbWV0YWRhdGEgPSBzYW1wbGVfZGYpCmJpcGxvdChwY2FfYWxsLCBjb2xieSA9ICJjb25kaXRpb24iLCB0aXRsZSA9ICJQQ0EgZm9yIGFsbCBnZW5lcyIpCmBgYAoKRmlsdGVyZWQgZXhwcmVzc2lvbiBtYXRyaXgKYGBge3J9CnBjYV9maWx0ZXJlZCA8LSBwY2EoY291bnRzX3Jsb2dba2VlcCwgLCBkcm9wID0gRkFMU0VdLCBtZXRhZGF0YSA9IHNhbXBsZV9kZikKYmlwbG90KHBjYV9maWx0ZXJlZCwgY29sYnkgPSAiY29uZGl0aW9uIiwgdGl0bGUgPSAiUENBIGZvciBhbGwgZ2VuZXMiKQpgYGAKCkRFU2VxMiBwcm92aWRlcyBhIGBwbG90UENBKClgIGZ1bmN0aW9uLCB3aGljaCBieSBkZWZhdWx0IHVzZXMgdGhlIHRvcCA1MDAgbW9zdCB2YXJpYWJsZSBnZW5lcwpgYGB7cn0KcGxvdFBDQShybG9nLCBpbnRncm91cCA9ICJjb25kaXRpb24iLCBudG9wID0gMTAwMDApCiNnZ3NhdmUoIi4uL291dHB1dC9QQ0EvMjAyNTAzMTktdHJhbnNjcmlwdG9tZS10b3AxMGstdmFyaWFibGUtZ2VuZXMtUENBLnBuZyIpCmBgYAoKIyBBbmFseXplIGdlbmUgc2V0cwojIyBmdW5jdGlvbiB0byBjYWxjdWxhdGUgYSBudW1iZXIgb2YgZGVyaXZlZCB2YWx1ZXMKYGBge3J9CnNvdXJjZSgiLi4vc2NyaXB0LzIwMjUwNDAzLXdpdGhpbi1kYXVlci1jb21wYWN0bmVzcy1zY29yZS1mdW5jdGlvbnMuUiIpCmBgYAoKIyMgbG9hZCB0aGUgbmV3IHNpeCBncm91cCBnZW5lIHNldHMKYGBge3IgbWVzc2FnZT1GQUxTRX0KZ2VuZV9zZXRfZmlsZXMgPC0gbGlzdC5maWxlcygiLi4vaW5wdXQvZ2VuZS1saXN0cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9ICJHKi50c3YiLCBmdWxsLm5hbWVzID0gVFJVRSkKIyBncm91cCAxIGlzIGp1c3QgdGhlIHVuaW9uIG9mIHRoZSByZW1haW5pbmcgZml2ZSBncm91cHMKYWxsX3BhdGh3YXlzIDwtIHJlYWRfdHN2KCIuLi9pbnB1dC9nZW5lLWxpc3RzL0dyb3VwLjEuZ2VuZXNldC50c3YiKQpnZW5lLmxpc3RzIDwtIG1hcChnZW5lX3NldF9maWxlc1stMV0sIHJlYWRfdHN2KQpuYW1lcyhnZW5lLmxpc3RzKSA8LSBjKAogICIyOkROQV9tZXRhYm9saXNtX3R4biIsCiAgIjM6RW5lcmd5X21ldGFib2xpc21fbWl0byIsCiAgIjQ6U2lnbmFsX3RyYW5zZHVjdGlvbiIsCiAgIjU6TW9ycGhvbG9naWNhbF9mZWF0dXJlcyIsCiAgIjY6TWV0YWJvbGljX2ZlYXR1cmVzIgopCmdlbmVfbGlzdF8wIDwtIGxpc3RfcmJpbmQoZ2VuZS5saXN0cywgbmFtZXNfdG8gPSAiZ3JvdXAiKQpgYGAKClRoZXJlIGFyZSBzdGlsbCBkdXBsaWNhdGUgcm93cyBpbiB0aGUgZ2VuZSBsaXN0LiBMZXQncyBleGFtaW5lIHRoZW0gYW5kIHRoZW4gcmVtb3ZlIHRoZSBkdXBsaWNhdGVkIHJvd3MuCmBgYHtyfQojIGNoZWNrIGZvciBkdXBsaWNhdGVzCmR1cGxpY2F0ZXMgPC0gZ2VuZV9saXN0XzAgfD4gCiAgZ3JvdXBfYnkoYWNyb3NzKGV2ZXJ5dGhpbmcoKSkpIHw+IAogIGZpbHRlcihuKCkgPiAxKSB8PiAKICBhcnJhbmdlKGdyb3VwLCBwYXRod2F5LCBXQkdlbmVJRCkKZHVwbGljYXRlcwpgYGAKPiBUaGVyZSBhcmUgMTAgZHVwbGljYXRlIHJvd3MKClJlbW92ZSB0aGUgZHVwbGljYXRlcwpgYGB7cn0KZ2VuZV9saXN0IDwtIGRpc3RpbmN0KGdlbmVfbGlzdF8wKSAjIHJlbW92ZSBkdXBsaWNhdGUgcm93cwpgYGAKCkFyZSB0aGVyZSBwYXRod2F5cyB0aGF0IGJlbG9uZyB0byBtdWx0aXBsZSBncm91cHM/CmBgYHtyfQojIGNoZWNrIGZvciBwYXRod2F5cyB0aGF0IGJlbG9uZyB0byBtdWx0aXBsZSBncm91cHMKZ2VuZV9saXN0IHw+IAogIGdyb3VwX2J5KHBhdGh3YXkpIHw+IAogIGZpbHRlcihuX2Rpc3RpbmN0KGdyb3VwKSA+IDEpIHw+IAogIGFycmFuZ2UoZ3JvdXAsIHBhdGh3YXksIFdCR2VuZUlEKQpgYGAKPiBHb29kLCB0aGUgZml2ZSBncm91cHMgaGF2ZSBubyBvdmVybGFwcy4gVGh1cywgZ3JvdXAgSUQgaXMganVzdCBhbm90aGVyIGxhYmVsIG9uIHRoZSBwYXRod2F5cyBhbmQgd29uJ3QgY2F1c2UgYW1iaWd1aXR5LgoKV2hhdCdzIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIG51bWJlciBvZiBnZW5lcyBwZXIgcGF0aHdheT8KYGBge3J9CiMgY2hlY2sgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgbnVtYmVyIG9mIGdlbmVzIHBlciBwYXRod2F5CmdlbmVfbGlzdCB8PiAKICBncm91cF9ieShwYXRod2F5KSB8PiAKICBzdW1tYXJpc2UobiA9IG5fZGlzdGluY3QoV0JHZW5lSUQpKSB8PiAKICBwdWxsKG4pIHw+CiAgdGFibGUoKQpgYGAKPiBUaGUgc21hbGxlc3QgcGF0aHdheXMgY29udGFpbiBhdCBsZWFzdCA2IGdlbmVzLgoKIyMgQ2FsY3VsYXRlIHRoZSB3aXRoaW4gY2x1c3RlciBkaXN0YW5jZSBhbmQgb3RoZXIgbWV0cmljcwpOb3cgdGhhdCB3ZSBoYXZlIGFsbCB0aGUgcGF0aHdheXMgaW4gYSBzaW5nbGUgZmlsZSwgd2UgY2FuIGp1c3QgaXRlcmF0ZSB0aHJvdWdoIGFsbCBvZiB0aGVtLgoKU3BsaXQgdGhlIGdlbmUgc2V0cyBhbmQgYXBwbHkgdGhlIGNhbGN1bGF0aW9uIHRvIGVhY2ggc3Vic2V0LCBjb21iaW5lIHRoZSByZXN1bHRzCmBgYHtyfQojIGEgbGlzdCBvYmplY3QgdG8gc3RvcmUgdGhlIHJlc3VsdHMgb2YgaW5kaXZpZHVhbCBnZW5lIHNldHMKZ2VuZV9zZXRfcmVzdWx0cyA8LSB3aXRoKGdlbmVfbGlzdCwgc3BsaXQoV0JHZW5lSUQsIHBhdGh3YXkpKSB8PgogIGtlZXAoXCh4KSBsZW5ndGgodW5pcXVlKHgpKSA+IDEpIHw+ICMga2VlcCBvbmx5IHRoZSBncm91cHMgd2l0aCBtb3JlIHRoYW4gb25lIGdlbmUKICBtYXAoXChnZW5lX3NldCkgd2l0aGluX2RhdWVyX2NvbXBhY3RuZXNzX3Njb3JlKHNhbXBsZU1lYW5fcmxvZywgZ2VuZV9zZXQpKQoKIyBjb21iaW5lIHRoZSByZXN1bHRzIGludG8gYSBzaW5nbGUgZGF0YSBmcmFtZQpnZW5lX3NldF90YWJsZSA8LSBiaW5kX3Jvd3MoZ2VuZV9zZXRfcmVzdWx0cywgLmlkID0gInBhdGh3YXkiKSB8PgogIGxlZnRfam9pbihkaXN0aW5jdChnZW5lX2xpc3QsIGdyb3VwLCBwYXRod2F5KSwgYnkgPSAicGF0aHdheSIpIHw+CiAgcmVsb2NhdGUoZ3JvdXAsIC5iZWZvcmUgPSBwYXRod2F5KSB8PgogIGFycmFuZ2UoZ3JvdXAsIFIyX2RhdWVyKQpgYGAKCkV4cG9ydCB0aGUgbGlzdCB0byBhIGZpbGUKYGBge3J9CiMgd3JpdGUgb3V0cHV0IHRvIGZpbGUsIHByZXNlcnZlcyBtb3JlIGRpZ2l0cyBmb3IgbGF0ZXIgY2FsY3VsYXRpb25zCndyaXRlX3RzdihnZW5lX3NldF90YWJsZSwKICAgICAgICAgIGZpbGUucGF0aCgiLi4vb3V0cHV0L3dpdGhpbi5zdW0uc3F1YXJlcyIsCiAgICAgICAgICAgICAgICAgICAgIjIwMjUwNDAzLWdlbmVzZXQtc3RhdHMtYWxsLnRzdiIpKQoKIyBhbHNvIHdyaXRlIGEgdmVyc2lvbiB3aXRoIGZld2VyIGRpZ2l0cwpnZW5lX3NldF90YWJsZSAlPiUgCiAgbXV0YXRlKGFjcm9zcygtYyhwYXRod2F5LCBncm91cCwgZ2VuZV9zZXRfc2l6ZSksIFwoeCkgcm91bmQoeCwgZGlnaXRzID0gMykpKSAlPiUgCiAgd3JpdGVfdHN2KGZpbGUucGF0aCgiLi4vb3V0cHV0L3dpdGhpbi5zdW0uc3F1YXJlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgIjIwMjUwNDAzLWdlbmVzZXQtc3RhdHMtYWxsLXJvdW5kZWQudHN2IikpCmBgYAoKIyMgUk1TRCB2cyBkX0wyTDMgY29ycmVsYXRpb24KV2Ugb2JzZXJ2ZWQgYSBnZW5lcmFsIGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdHdvIG1lYXN1cmVzIGFjcm9zcyBncm91cHMgKHNlZSBiZWxvdykuIEhlcmUsIHdlIHdvdWxkIGxpa2UgdG8gcXVhbnRpdGF0aXZlIGFzc2VzcyB0aGUgc3RyZW5ndGggb2YgY29ycmVsYXRpb24gYW5kIHRlc3QgZm9yIHNpZ25pZmljYW5jZS4KYGBge3J9CmdlbmVfc2V0X3RhYmxlICU+JSAKICAjZ3JvdXBfYnkoZ3JvdXApICU+JSAKICBuZXN0KC5ieSA9IGdyb3VwKSAlPiUgCiAgbXV0YXRlKAogICAgbW9kZWwgPSBtYXAoZGF0YSwgXChkZikgCiAgICAgICAgICAgICAgICBjb3IudGVzdCh+IFJNU0RfZGF1ZXIgKyBkX0wyTDNfdG9fZGF1ZXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRmLCBtZXRob2QgPSAicGVhcnNvbiIpKSwKICAgIHRpZGllZCA9IG1hcChtb2RlbCwgYnJvb206OnRpZHkpCiAgKSAlPiUgCiAgdW5uZXN0KHRpZGllZCkgJT4lIAogIHNlbGVjdChncm91cCwgUGVhcnNvbiA9IGVzdGltYXRlLCBwLnZhbHVlLCBjb25mLmxvdywgY29uZi5oaWdoKQpgYGAKClNhbWUgYW5hbHlzaXMgZm9yIHRoZSBHSURzCmBgYHtyfQpnZW5lX3NldF90YWJsZSAlPiUgCiAgI2dyb3VwX2J5KGdyb3VwKSAlPiUgCiAgbmVzdCguYnkgPSBncm91cCkgJT4lIAogIG11dGF0ZSgKICAgIG1vZGVsID0gbWFwKGRhdGEsIFwoZGYpIAogICAgICAgICAgICAgICAgY29yLnRlc3QofiBSTVNEX2dpZCArIGRfTDJMM190b19naWQsIAogICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRmLCBtZXRob2QgPSAicGVhcnNvbiIpKSwKICAgIHRpZGllZCA9IG1hcChtb2RlbCwgYnJvb206OnRpZHkpCiAgKSAlPiUgCiAgdW5uZXN0KHRpZGllZCkgJT4lIAogIHNlbGVjdChncm91cCwgUGVhcnNvbiA9IGVzdGltYXRlLCBwLnZhbHVlLCBjb25mLmxvdywgY29uZi5oaWdoKQpgYGAKCiMgUGxvdHN7LnRhYnNldH0KIyMgUk1TRCB2cyBkX0wyTDMgZm9yIGdyb3VwcyAyLTUKRmlyc3QsIHdlIHdpbGwgcGxvdCBSTVNEX2RhdWVyIHZzIGRfTDJMM190b19kYXVlciBmb3IgZ3JvdXBzIDItNCBjb21iaW5lZC4KYGBge3J9CnNldF9jb2xvcnMgPC0gUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDUsICJTZXQyIikKbmFtZXMoc2V0X2NvbG9ycykgPC0gYygKICAiRE5BX21ldGFib2xpc21fdHhuIiwgIkVuZXJneV9tZXRhYm9saXNtX21pdG8iLCAiU2lnbmFsX3RyYW5zZHVjdGlvbiIsCiAgIk1vcnBob2xvZ2ljYWxfZmVhdHVyZXMiLCAiTWV0YWJvbGljX2ZlYXR1cmVzIgopCnAgPC0gZ2VuZV9zZXRfdGFibGUgJT4lIAogIGZpbHRlcihncm91cCAhPSAiNjpNZXRhYm9saWNfZmVhdHVyZXMiKSAlPiUKICBtdXRhdGUobGFiZWwgPSBwYXN0ZTAocGF0aHdheSwgIiAoIiwgZ2VuZV9zZXRfc2l6ZSwgIikiKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gZF9MMkwzX3RvX2RhdWVyLCB5ID0gUk1TRF9kYXVlciwKICAgICAgICAgICAgIGxhYmVsID0gbGFiZWwpKSArIAogICNzdGF0X3Ntb290aChtZXRob2QgPSAibG0iLCBmb3JtdWxhID0geX54KzAsIHNlID0gRkFMU0UsCiAgIyAgICAgICAgICAgIGNvbG9yID0gInNreWJsdWUyIiwgbGluZXR5cGUgPSAyKSArCiAgZ2VvbV9wb2ludCgpICsKICAjc2NhbGVfY29sb3JfbWFudWFsKCJHcm91cHMiLCB2YWx1ZXMgPSBzZXRfY29sb3JzLCAKICAjICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHBhc3RlKDE6NSwgbmFtZXMoc2V0X2NvbG9ycyksIHNlcCA9ICI6IikpICsKICB4bGFiKCJEaXN0YW5jZSBEYXVlciB0byBMMi9MMyIpICsgeWxhYigiUk1TRCB3aXRoaW4gRGF1ZXIiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMC41LCAyLjYyKSwgZXhwYW5kID0gYygwLjEsIDAuMDUpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMC4yLCAwLjkyKSwgZXhwYW5kID0gYygwLjEsIDApKSArCiAgI2d1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChucm93ID0gMikpICsKICBmYWNldF93cmFwKH4gZ3JvdXAsIG5yb3cgPSAyKSArCiAgdGhlbWUoc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC44KSksCiAgICAgICAgcGFuZWwuc3BhY2luZyA9IHVuaXQoMC41LCAibGluZXMiKSkKICAjdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIsIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC42KSkpCmdnc2F2ZShmaWxlID0gZmlsZS5wYXRoKCIuLi9vdXRwdXQvd2l0aGluLnN1bS5zcXVhcmVzL2ltZyIsCiAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiMjAyNTA0MDQtUk1TRC12cy1kTDJMMy1ncm91cHMyLTUucG5nIikpLAogICAgICAgcGxvdCA9IHAgKyBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDIuNSwgY29sb3VyID0gImdyYXkyMCIpLAogICAgICAgd2lkdGggPSA3LCBoZWlnaHQgPSA3KQpwcmludChnZ3Bsb3RseShwLCB0b29sdGlwID0gYygibGFiZWwiKSwgd2lkdGggPSA2MDAsIGhlaWdodCA9IDYwMCkpCmBgYApOZXh0LCB3ZSB3aWxsIHBsb3QgUk1TRF9naWQgdnMgZF9MMkwzX3RvX2dpZCBmb3IgZ3JvdXBzIDItNCBjb21iaW5lZC4KYGBge3J9CnAgPC0gZ2VuZV9zZXRfdGFibGUgJT4lIAogIGZpbHRlcihncm91cCAhPSAiNjpNZXRhYm9saWNfZmVhdHVyZXMiKSAlPiUKICBtdXRhdGUobGFiZWwgPSBwYXN0ZTAocGF0aHdheSwgIiAoIiwgZ2VuZV9zZXRfc2l6ZSwgIikiKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gZF9MMkwzX3RvX2dpZCwgeSA9IFJNU0RfZ2lkLAogICAgICAgICAgICAgbGFiZWwgPSBsYWJlbCkpICsgCiAgI3N0YXRfc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5fngrMCwgc2UgPSBGQUxTRSwKICAjICAgICAgICAgICAgY29sb3IgPSAic2t5Ymx1ZTIiLCBsaW5ldHlwZSA9IDIpICsKICBnZW9tX3BvaW50KCkgKwogICNzY2FsZV9jb2xvcl9tYW51YWwoIkdyb3VwcyIsIHZhbHVlcyA9IHNldF9jb2xvcnMsIAogICMgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcGFzdGUoMTo1LCBuYW1lcyhzZXRfY29sb3JzKSwgc2VwID0gIjoiKSkgKwogIHhsYWIoIkRpc3RhbmNlIEdJRCB0byBMMi9MMyIpICsgeWxhYigiUk1TRCB3aXRoaW4gR0lEcyIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLjUsIDIuNjIpLCBleHBhbmQgPSBjKDAuMSwgMC4wNSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLjIsIDAuOTIpLCBleHBhbmQgPSBjKDAuMSwgMCkpICsKICAjZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG5yb3cgPSAyKSkgKwogIGZhY2V0X3dyYXAofmdyb3VwLCBucm93ID0gMikgKwogIHRoZW1lKHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuOCkpLAogICAgICAgIHBhbmVsLnNwYWNpbmcgPSB1bml0KDAuNSwgImxpbmVzIikpCiAgI3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLCBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuNikpKQpnZ3NhdmUoZmlsZSA9IGZpbGUucGF0aCgiLi4vb3V0cHV0L3dpdGhpbi5zdW0uc3F1YXJlcy9pbWciLAogICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoIjIwMjUwNDA0LVJNU0QtdnMtZEwyTDMtZ3JvdXBzMi01LUdJRHMucG5nIikpLAogICAgICAgcGxvdCA9IHAgKyBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDIuNSwgY29sb3VyID0gImdyYXkyMCIpLAogICAgICAgd2lkdGggPSA3LCBoZWlnaHQgPSA3KQpwcmludChnZ3Bsb3RseShwLCB0b29sdGlwID0gYygibGFiZWwiKSwgd2lkdGggPSA2MDAsIGhlaWdodCA9IDYwMCkpCmBgYAoKIyMgUk1TRCB2cyBkX0wyTDMgZm9yIGVhY2ggZ3JvdXAgc2VwYXJhdGVseQpXZSB3aWxsIGdlbmVyYXRlIG9uZSBSTVNEX2RhdWVyIHZzIGRfTDJMM190b19kYXVlciBwbG90IGZvciBlYWNoIGdyb3VwCgpgYGB7cn0KI3NldF9jb2xvcnMgPC0gUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDUsICJBY2NlbnQiKQojbmFtZXMoc2V0X2NvbG9ycykgPC0gbmFtZXMoZ2VuZS5saXN0cykKZm9yIChzZXQgaW4gbmFtZXMoZ2VuZS5saXN0cykpewogIHRtcCA8LSBnZW5lX3NldF90YWJsZSAlPiUgCiAgICBmaWx0ZXIoZ3JvdXAgPT0gc2V0KSAlPiUKICAgIG11dGF0ZShsYWJlbCA9IHBhc3RlMChwYXRod2F5LCAiICgiLCBnZW5lX3NldF9zaXplLCAiKSIpKQogICMgaWYgdGhlcmUgYXJlIG1vcmUgdGhhbiAxNSBwYXRod2F5cyBpbiBhIGdyb3VwLCB1c2UgMi41IGZvciBsYWJlbCBzaXplCiAgIyBvdGhlcndpc2UsIHVzZSAzCiAgbGFiZWxfc2l6ZSA8LSBpZmVsc2UobnJvdyh0bXApID4gMTUsIDIuNSwgMykKICAjIG1ha2UgcGxvdAogIHAgPC0gdG1wICU+JQogICAgZ2dwbG90KGFlcyh4ID0gZF9MMkwzX3RvX2RhdWVyLCB5ID0gUk1TRF9kYXVlciwKICAgICAgICAgICAgICAgbGFiZWwgPSBsYWJlbCkpICsgCiAgICAjc3RhdF9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9IHl+eCswLCBzZSA9IEZBTFNFLAogICAgIyAgICAgICAgICAgIGNvbG9yID0gInNreWJsdWUyIiwgbGluZXR5cGUgPSAyKSArCiAgICBnZW9tX3BvaW50KGFlcygpKSArCiAgICAjc2NhbGVfY29sb3JfbWFudWFsKE5VTEwsIHZhbHVlcyA9IHNldF9jb2xvcnMpICsKICAgIHhsYWIoIkRpc3RhbmNlIERhdWVyIHRvIEwyL0wzIikgKyB5bGFiKCJSTVNEIHdpdGhpbiBEYXVlciIpICsKICAgIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAuNSwgMi42MiksIGV4cGFuZCA9IGMoMC4xLCAwLjA1KSkgKwogICAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMC4yLCAwLjkyKSwgZXhwYW5kID0gYygwLjEsIDApKSArCiAgICBnZ3RpdGxlKHNldCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKICBnZ3NhdmUoZmlsZSA9IGZpbGUucGF0aCgiLi4vb3V0cHV0L3dpdGhpbi5zdW0uc3F1YXJlcy9pbWciLAogICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiMjAyNTA0MDMtUk1TRC12cy1kTDJMMy0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnc3ViKCI6IiwgIl8iLCBzZXQpLCAiLnBuZyIpKSwKICAgICAgICAgcGxvdCA9IHAgKyBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IGxhYmVsX3NpemUsIGNvbG91ciA9ICJncmF5MjAiKSwKICAgICAgICAgd2lkdGggPSA2LCBoZWlnaHQgPSA2KQogIHByaW50KGdncGxvdGx5KHAsIHRvb2x0aXAgPSBjKCJsYWJlbCIpKSkKfQpgYGAKCkRvIHRoZSBzYW1lIGZvciBSTVNEX2dpZCB2cyBkX0wyTDNfdG9fZ2lkCmBgYHtyfQpmb3IgKHNldCBpbiBuYW1lcyhnZW5lLmxpc3RzKSl7CiAgdG1wIDwtIGdlbmVfc2V0X3RhYmxlICU+JSAKICAgIGZpbHRlcihncm91cCA9PSBzZXQpICU+JQogICAgbXV0YXRlKGxhYmVsID0gcGFzdGUwKHBhdGh3YXksICIgKCIsIGdlbmVfc2V0X3NpemUsICIpIikpCiAgIyBpZiB0aGVyZSBhcmUgbW9yZSB0aGFuIDE1IHBhdGh3YXlzIGluIGEgZ3JvdXAsIHVzZSAyLjUgZm9yIGxhYmVsIHNpemUKICAjIG90aGVyd2lzZSwgdXNlIDMKICBsYWJlbF9zaXplIDwtIGlmZWxzZShucm93KHRtcCkgPiAxNSwgMi41LCAzKQogICMgbWFrZSBwbG90CiAgcCA8LSB0bXAgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBkX0wyTDNfdG9fZ2lkLCB5ID0gUk1TRF9naWQsCiAgICAgICAgICAgICAgIGxhYmVsID0gbGFiZWwpKSArCiAgICAjc3RhdF9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9IHl+eCswLCBzZSA9IEZBTFNFLAogICAgIyAgICAgICAgICAgIGNvbG9yID0gInNreWJsdWUyIiwgbGluZXR5cGUgPSAyKSArCiAgICBnZW9tX3BvaW50KGFlcygpKSArCiAgICAjc2NhbGVfY29sb3JfbWFudWFsKE5VTEwsIHZhbHVlcyA9IHNldF9jb2xvcnMpICsKICAgIHhsYWIoIkRpc3RhbmNlIEdJRCB0byBMMi9MMyIpICsgeWxhYigiUk1TRCB3aXRoaW4gR0lEcyIpICsKICAgIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAuNSwgMi42MiksIGV4cGFuZCA9IGMoMC4xLCAwLjA1KSkgKwogICAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMC4yLCAwLjkyKSwgZXhwYW5kID0gYygwLjEsIDApKSArCiAgICBnZ3RpdGxlKHNldCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKICBnZ3NhdmUoZmlsZSA9IGZpbGUucGF0aCgiLi4vb3V0cHV0L3dpdGhpbi5zdW0uc3F1YXJlcy9pbWciLAogICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiMjAyNTA0MDMtUk1TRC12cy1kTDJMMy0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnc3ViKCI6IiwgIl8iLCBzZXQpLCAiLUdJRHMucG5nIikpLAogICAgICAgICBwbG90ID0gcCArIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChzaXplID0gbGFiZWxfc2l6ZSwgY29sb3VyID0gImdyYXkyMCIpLAogICAgICAgICB3aWR0aCA9IDYsIGhlaWdodCA9IDYpCiAgcHJpbnQoZ2dwbG90bHkocCwgdG9vbHRpcCA9IGMoImxhYmVsIikpKQp9CmBgYAoKIyMgQ29tcGFyZSBhbGwgZml2ZSBncm91cHMKTGV0J3MgY29tcGFyZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBtZXRyaWNzIGJldHdlZW4gdGhlIGZpdmUgZ3JvdXBzLiBXZSB3aWxsIGZvY3VzIG9uIFJNU0RfZGF1ZXIsIGRfTDJMM190b19kYXVlciwgUjJfZGF1ZXIsIGFuZCAxLWF2Z19QZWFyc29uX2RhdWVyCgpMaXN0IHRoZSBmaXZlIGdyb3VwcwoKYHIgcGFzdGUodW5pcXVlKGdlbmVfc2V0X3RhYmxlJGdyb3VwKSwgIlxuIilgCgpgYGB7cn0KdG1wIDwtIGdlbmVfc2V0X3RhYmxlICU+JSAKICBtdXRhdGUoZF9QQ0NfZGF1ZXIgPSAxIC0gYXZnX1BlYXJzb25fZGF1ZXIsCiAgICAgICAgIHJfdG9MMkwzX3ZzX3RvQWR1bHQgPSBkX0wyTDNfdG9fZGF1ZXIgLyBkX2FkdWx0X3RvX2RhdWVyKSAlPiUKICBzZWxlY3QoZ3JvdXAsIHBhdGh3YXksIGdlbmVfc2V0X3NpemUsIFJNU0RfZGF1ZXIsIGRfTDJMM190b19kYXVlciwKICAgICAgICAgZF9hZHVsdF90b19kYXVlciwgUjJfZGF1ZXIsIGRfUENDX2RhdWVyLCByX3RvTDJMM192c190b0FkdWx0KSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IC1jKGdyb3VwLCBwYXRod2F5LCBnZW5lX3NldF9zaXplKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAibWV0cmljIiwgdmFsdWVzX3RvID0gInZhbHVlIikgJT4lCiAgbXV0YXRlKG1ldHJpYyA9IGZhY3RvcihtZXRyaWMsCiAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJkX0wyTDNfdG9fZGF1ZXIiLCAiZF9hZHVsdF90b19kYXVlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyX3RvTDJMM192c190b0FkdWx0IiwiUk1TRF9kYXVlciIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUjJfZGF1ZXIiLCAiZF9QQ0NfZGF1ZXIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkRpc3RhbmNlIHRvIEwyL0wzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRpc3RhbmNlIHRvIGFkdWx0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImRfTDJMMyAvIGRfYWR1bHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUk1TRCIsICIoUjIpIFJNU0QgLyBkX0wyTDMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMSAtIGF2Zy4gUGVhcnNvbiIpKSkKCnAgPC0gdG1wICU+JQogIHNlcGFyYXRlKGdyb3VwLCBpbnRvID0gYygiZ3JvdXAiLCAiZ3JvdXBfbmFtZSIpLCBzZXAgPSAiOiIpICU+JQogICNtdXRhdGUobGFiZWwgPSBwYXN0ZShwYXRod2F5LCByb3VuZCh2YWx1ZSwgMyksIHNlcCA9ICI6IikpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBncm91cCwgeSA9IHZhbHVlKSkgKwogICNnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZSA9IE5BLCBvdXRsaWVyLmNvbG9yID0gTkEpICsKICBnZW9tX2ppdHRlcihhZXModGV4dCA9IHBhc3RlKHBhdGh3YXksIHJvdW5kKHZhbHVlLCAzKSwgc2VwID0gIjoiKSksCiAgICAgICAgICAgICAgd2lkdGggPSAwLjIsIHNpemUgPSAxLjIsIGNvbG9yID0gImdyYXk0MCIsIGFscGhhID0gMC44KSArCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gIm1lYW5fY2xfYm9vdCIsIGdlb20gPSAiY3Jvc3NiYXIiLCAKICAgICAgICAgICAgICAgbGluZXdpZHRoID0gMC4yLCB3aWR0aCA9IDAuNCwgY29sb3IgPSAicmVkIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbChOVUxMLCB2YWx1ZXMgPSBzZXRfY29sb3JzLCBndWlkZSA9ICJub25lIikgKwogIGZhY2V0X3dyYXAofm1ldHJpYywgc2NhbGVzID0gImZyZWVfeSIpICsKICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2YgbWV0cmljcywgYWxsIGRhdWVycyIpICsKICB0aGVtZShzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjgpKSwKICAgICAgICBwYW5lbC5zcGFjaW5nID0gdW5pdCgwLjUsICJsaW5lcyIpKQpwcmludChnZ3Bsb3RseShwLCB0b29sdGlwID0gInRleHQiLAogICAgICAgICAgICAgICB3aWR0aCA9IDgwMCwgaGVpZ2h0ID0gNTAwKSkKZ2dzYXZlKGZpbGUucGF0aCgiLi4vb3V0cHV0L3dpdGhpbi5zdW0uc3F1YXJlcy9pbWciLAogICAgICAgICAgICAgICAgICIyMDI1MDQwNS1jb21wYXJlLW1ldHJpYy1kaXN0cmlidXRpb24tZ3JvdXBzMi02LWRhdWVycy5wbmciKSwKICAgICAgIHdpZHRoID0gOCwgaGVpZ2h0ID0gNSkKYGBgCgpSZXBlYXQgZm9yIFJNU0RfZ2lkLCBkX0wyTDNfdG9fZ2lkLCBSMl9naWQsIGFuZCAxLWF2Z19QZWFyc29uX2dpZApgYGB7cn0KdG1wIDwtIGdlbmVfc2V0X3RhYmxlICU+JSAKICBtdXRhdGUoZF9QQ0NfZ2lkID0gMSAtIGF2Z19QZWFyc29uX2dpZCwKICAgICAgICAgcl90b0wyTDNfdnNfdG9BZHVsdCA9IGRfTDJMM190b19naWQgLyBkX2FkdWx0X3RvX2dpZCkgJT4lCiAgc2VsZWN0KGdyb3VwLCBwYXRod2F5LCBnZW5lX3NldF9zaXplLCBSTVNEX2dpZCwgZF9MMkwzX3RvX2dpZCwKICAgICAgICAgZF9hZHVsdF90b19naWQsIFIyX2dpZCwgZF9QQ0NfZ2lkLCByX3RvTDJMM192c190b0FkdWx0KSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IC1jKGdyb3VwLCBwYXRod2F5LCBnZW5lX3NldF9zaXplKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAibWV0cmljIiwgdmFsdWVzX3RvID0gInZhbHVlIikgJT4lCiAgbXV0YXRlKG1ldHJpYyA9IGZhY3RvcihtZXRyaWMsCiAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJkX0wyTDNfdG9fZ2lkIiwgImRfYWR1bHRfdG9fZ2lkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInJfdG9MMkwzX3ZzX3RvQWR1bHQiLCJSTVNEX2dpZCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUjJfZ2lkIiwgImRfUENDX2dpZCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiRGlzdGFuY2UgdG8gTDIvTDMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRGlzdGFuY2UgdG8gYWR1bHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZF9MMkwzIC8gZF9hZHVsdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSTVNEIiwgIihSMikgUk1TRCAvIGRfTDJMMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIxIC0gYXZnLiBQZWFyc29uIikpKQoKcCA8LSB0bXAgJT4lCiAgc2VwYXJhdGUoZ3JvdXAsIGludG8gPSBjKCJncm91cCIsICJncm91cF9uYW1lIiksIHNlcCA9ICI6IikgJT4lCiAgI211dGF0ZShsYWJlbCA9IHBhc3RlKHBhdGh3YXksIHJvdW5kKHZhbHVlLCAzKSwgc2VwID0gIjoiKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IGdyb3VwLCB5ID0gdmFsdWUpKSArCiAgI2dlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlID0gTkEsIG91dGxpZXIuY29sb3IgPSBOQSkgKwogIGdlb21faml0dGVyKGFlcyh0ZXh0ID0gcGFzdGUocGF0aHdheSwgcm91bmQodmFsdWUsIDMpLCBzZXAgPSAiOiIpKSwKICAgICAgICAgICAgICB3aWR0aCA9IDAuMiwgc2l6ZSA9IDEuMiwgY29sb3IgPSAiZ3JheTQwIiwgYWxwaGEgPSAwLjgpICsKICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSAibWVhbl9jbF9ib290IiwgZ2VvbSA9ICJjcm9zc2JhciIsIAogICAgICAgICAgICAgICBsaW5ld2lkdGggPSAwLjIsIHdpZHRoID0gMC40LCBjb2xvciA9ICJyZWQiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKE5VTEwsIHZhbHVlcyA9IHNldF9jb2xvcnMsIGd1aWRlID0gIm5vbmUiKSArCiAgZmFjZXRfd3JhcCh+bWV0cmljLCBzY2FsZXMgPSAiZnJlZV95IikgKwogIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBtZXRyaWNzLCBHSURzIikgKwogIHRoZW1lKHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuOCkpLAogICAgICAgIHBhbmVsLnNwYWNpbmcgPSB1bml0KDAuNSwgImxpbmVzIikpCnByaW50KGdncGxvdGx5KHAsIHRvb2x0aXAgPSAidGV4dCIsCiAgICAgICAgICAgICAgIHdpZHRoID0gODAwLCBoZWlnaHQgPSA1MDApKQpnZ3NhdmUoZmlsZS5wYXRoKCIuLi9vdXRwdXQvd2l0aGluLnN1bS5zcXVhcmVzL2ltZyIsCiAgICAgICAgICAgICAgICAgIjIwMjUwNDA1LWNvbXBhcmUtbWV0cmljLWRpc3RyaWJ1dGlvbi1ncm91cHMyLTYtR0lEcy5wbmciKSwKICAgICAgIHdpZHRoID0gOCwgaGVpZ2h0ID0gNSkKYGBgCiMgLS0tLS0gRG9uJ3QgZ28gYmVsb3cgdGhpcyBsaW5lICEtLS0tLQojIyBJIGhhdmVuJ3QgdXBkYXRlZCB0aGUgY29kZSBhbmQgcmVzdWx0cyBiZWxvdyB0aGlzIHlldC4KCjwhLS0KCiMjIyBDYXNlIHN0dWR5ClRoZSB0d28gbWVhc3VyZXMgb2Ygc2ltaWxhcml0eSwgaS5lLiwgRXVjbGlkZWFuIGRpc3RhbmNlLWJhc2VkIFJNU0QgYW5kIFBlYXJzb24ncyBjb3JyZWxhdGlvbiwgc2hvdyBvcHBvc2l0ZSByZXN1bHRzIGZvciBUR0ZCIHZzIGRhZi4xNi4gSGVyZSwgd2UgdGFrZSBhIGNsb3NlciBsb29rIGF0IGJvdGguCgojIyMjIGZ1bmN0aW9uIHRvIHBsb3QgaW5kaXZpZHVhbCBwYXRod2F5ClRyeSB0byBjcmVhdGUgYSBmdW5jdGlvbiB0byBhdXRvbWF0ZSB0aGUgY2FzZSBzdHVkeSBhbmFseXNpcy4KYGBge3J9CnBsb3RfaW5kaXZpZHVhbF9wYXRod2F5IDwtIGZ1bmN0aW9uKGdlbmVfc2V0LCBwYXRod2F5X25hbWUpewogICMgdGhpcyBmdW5jdGlvbiB0YWtlcyBhIGdlbmUgc2V0IGFuZCB0aGUgbmFtZSBvZiB0aGUgcGF0aHdheSB3aXRoaW4gdGhhdCBzZXQKICAjIHRoZW4gcGxvdHMgcGFpcndpc2Ugc2NhdHRlciBwbG90cyBhbG9uZyB3aXRoIHRoZSBjb3JyZXNwb25kaW5nIFBDQyBvciBTQ0MuCiAgIyBpdCBhbHNvIHBsb3RzIHRoZSBQQ0EgcGxvdC4KICAKICAjIEdhdGhlciB0aGUgZGF0YQogIGdlbmVzIDwtIGRwbHlyOjpmaWx0ZXIoZ2VuZV9zZXQsIHBhdGh3YXkgPT0gcGF0aHdheV9uYW1lKSB8PiBwdWxsKFdCR2VuZUlEKQogICMgYWxsIHJlcGxpY2F0ZXMsIGZvciBQQ0EKICByZXBfbWF0cml4IDwtIGNvdW50c19ybG9nW2dlbmVzLCAsIGRyb3AgPSBGQUxTRV0KICAjIG1lYW4gZXhwcmVzc2lvbiBvbmx5LCBmb3IgcGFpcndpc2UgY29ycmVsYXRpb25zIGFuZCBoZWF0bWFwCiAgbWVhbl9tYXRyaXggPC0gc2FtcGxlTWVhbl9ybG9nW2dlbmVzLCAsIGRyb3AgPSBGQUxTRV0KICAKICAjIEV4YW1pbmUgdGhlIHBhaXJ3aXNlIHJlbGF0aW9uc2hpcHMuCiAgcG5nKGZpbGUucGF0aCgiLi4vb3V0cHV0L3dpdGhpbi5zdW0uc3F1YXJlcy9pbWciLAogICAgICAgICAgICAgICAgcGFzdGUwKHBhdGh3YXlfbmFtZSwgIi1wYWlycy1QQ0MtcGxvdC5wbmciKSksCiAgICAgIHdpZHRoID0gMTAwMCwgaGVpZ2h0ID0gMTAwMCkKICAjIFBsb3R0aW5nIHRoZSBjb3JyZWxhdGlvbiBtYXRyaXgKICBwYWlycyhtZWFuX21hdHJpeFssIGMoIldUX0NUUkwiLCAiV1RfTDJfTDMiLCBhbGxfZGF1ZXIpXSwKICAgICAgICB1cHBlci5wYW5lbCA9IHBhbmVsLmNvciwgICAgIyBDb3JyZWxhdGlvbiBwYW5lbAogICAgICAgIGxvd2VyLnBhbmVsID0gcGFuZWwuc21vb3RoLCAjIFNtb290aGVkIHJlZ3Jlc3Npb24gbGluZXMKICAgICAgICBtYWluID0gcGFzdGUwKCJQYWlyd2lzZSBQQ0NzIGZvciAiLCBwYXRod2F5X25hbWUpKQogIGRldi5vZmYoKQogIAogICMgU3BlYXJtYW4ncyBjb3JyZWxhdGlvbiwgcmFuayBvbmx5LgogIHBuZyhmaWxlLnBhdGgoIi4uL291dHB1dC93aXRoaW4uc3VtLnNxdWFyZXMvaW1nIiwKICAgICAgICAgICAgICAgIHBhc3RlMChwYXRod2F5X25hbWUsICItcGFpcnMtU0NDLXBsb3QucG5nIikpLAogICAgICB3aWR0aCA9IDEwMDAsIGhlaWdodCA9IDEwMDApCiAgIyBQbG90dGluZyB0aGUgY29ycmVsYXRpb24gbWF0cml4CiAgcGFpcnMobWVhbl9tYXRyaXhbLCBjKCJXVF9DVFJMIiwgIldUX0wyX0wzIiwgYWxsX2RhdWVyKV0sCiAgICAgICAgdXBwZXIucGFuZWwgPSBmdW5jdGlvbih4LCB5LCAuLi4pIHBhbmVsLmNvcih4LCB5LCBtZXRob2QgPSAic3BlYXJtYW4iKSwKICAgICAgICBsb3dlci5wYW5lbCA9IHBhbmVsLnNtb290aCwgIyBTbW9vdGhlZCByZWdyZXNzaW9uIGxpbmVzCiAgICAgICAgbWFpbiA9IHBhc3RlMCgiUGFpcndpc2UgU0NDcyBmb3IgIiwgcGF0aHdheV9uYW1lKSkKICBkZXYub2ZmKCkKICAKICAjIFBDQSBwbG90CiAgUENBIDwtIHBjYShyZXBfbWF0cml4LCBtZXRhZGF0YSA9IHNhbXBsZV9kZikKICBiaXBsb3QoUENBLCBjb2xieSA9ICJjb25kaXRpb24iLCB0aXRsZSA9IHBhc3RlMCgiUENBIGZvciAiLCBwYXRod2F5X25hbWUpKQogIGdnc2F2ZShmaWxlLnBhdGgoIi4uL291dHB1dC93aXRoaW4uc3VtLnNxdWFyZXMvaW1nIiwKICAgICAgICAgICAgICAgICAgIHBhc3RlMChwYXRod2F5X25hbWUsICItcGFpcnMtUENBLXBsb3QucG5nIikpLAogICAgICAgICB3aWR0aCA9IDYsIGhlaWdodCA9IDYpCiAgCiAgIyByZXR1cm4gdGhlIGV4cHJlc3Npb24gbWF0cml4CiAgcmV0dXJuKG1lYW5fbWF0cml4KQp9CmBgYAoKSXRlcmF0ZSB0aHJvdWdoIGFsbCBTZXQgMSBwYXRod2F5cwpgYGB7cn0KY29yZV9kYXVlcl8xX2V4cHJlc3Npb25zIDwtIAogIGxhcHBseSh1bmlxdWUoY29yZV9kYXVlcl8xJHBhdGh3YXkpLAogICAgICAgICBmdW5jdGlvbihwYXRod2F5KXtwbG90X2luZGl2aWR1YWxfcGF0aHdheShjb3JlX2RhdWVyXzEsIHBhdGh3YXkpfQogICkKbmFtZXMoY29yZV9kYXVlcl8xX2V4cHJlc3Npb25zKSA8LSB1bmlxdWUoY29yZV9kYXVlcl8xJHBhdGh3YXkpCmBgYAoKSXRlcmF0ZSB0aHJvdWdoIGFsbCBTZXQgMiBwYXRod2F5cwpgYGB7cn0KY29yZV9kYXVlcl8yX2V4cHJlc3Npb25zIDwtIAogIGxhcHBseSh1bmlxdWUoY29yZV9kYXVlcl8yJHBhdGh3YXkpLAogICAgICAgICBmdW5jdGlvbihwYXRod2F5KXtwbG90X2luZGl2aWR1YWxfcGF0aHdheShjb3JlX2RhdWVyXzIsIHBhdGh3YXkpfQogICkKbmFtZXMoY29yZV9kYXVlcl8yX2V4cHJlc3Npb25zKSA8LSB1bmlxdWUoY29yZV9kYXVlcl8yJHBhdGh3YXkpCmBgYAoKIyMgU2V0IDM6IGFsbCBLaW0gbW91bnRhaW4gZ2VuZXMKUmVhZCBnZW5lIGxpc3QgYW5kIGFkZCBnZW5lIG5hbWUgY29sdW1uCmBgYHtyLGZpZy5mb3JtYXQ9J3N2Zyd9CiMgbG9hZCBXb3JtYmFzZSBnZW5lIHRhYmxlIGZvciBJRCBjb252ZXJzaW9uCiN3b3JtYmFzZS5nZW5lcyA8LSByZWFkX3RzdihoZXJlKCJpbnB1dCIsICJ3b3JtYmFzZS5nZW5lcy5uYW1lcy50eHQiKSkKIyBsb2FkIFdvcm1iYXNlIGdlbmUgSURzCiNnZW5lX0lEcyA8LSB3Yl9sb2FkX2dlbmVfaWRzKCJXUzI2OSIpCgojIGxvYWQgZ2VuZSBsaXN0IGFuZCBhZGQgZ2VuZSBuYW1lIGNvbHVtbgpraW1fbW91bnRhaW5zIDwtIHJlYWRfdHN2KCIuLi9pbnB1dC91cGRhdGVkLm5hbWVzLmtpbS5tb3VudGFpbnMudHN2IikgfD4KICAjIHJlbmFtZSBhbmQgb3JkZXIgY29sdW1ucyB0byBiZSBjb25zaXN0ZW50IHdpdGggdGhlIG90aGVyIHNldHMKICBzZWxlY3QocGF0aHdheSA9IEtpbV9ncm91cCwgV0JHZW5lSUQsIGdlbmVOYW1lKSB8PgogIGRpc3RpbmN0KCkgIyByZW1vdmUgZHVwbGljYXRlIHJvd3MKYGBgCgpBcHBseSB0aGUgY2FsY3VsYXRpb24KYGBge3IsZmlnLmZvcm1hdD0nc3ZnJ30KIyBhIGxpc3Qgb2JqZWN0IHRvIHN0b3JlIHRoZSByZXN1bHRzIG9mIGluZGl2aWR1YWwgZ2VuZSBzZXRzCktpbV9yZXN1bHQgPC0gbGlzdCgKICAiYWxsX0tpbSIgPSB3aXRoaW5fZGF1ZXJfY29tcGFjdG5lc3Nfc2NvcmUoc2FtcGxlTWVhbl9ybG9nLCBraW1fbW91bnRhaW5zJFdCR2VuZUlEKQopCmBgYAoKRG8gdGhlIHNhbWUgZm9yIGVhY2ggc3ViZ3JvdXAKYGBge3IgcmVzdWx0cz0naGlkZSd9CnN1YnNldF9raW1fcmVzdWx0IDwtIHdpdGgoa2ltX21vdW50YWlucywgc3BsaXQoV0JHZW5lSUQsIHBhdGh3YXkpKSB8PgogIGtlZXAoXCh4KSBsZW5ndGgodW5pcXVlKHgpKSA+IDEpIHw+ICMga2VlcCBvbmx5IHRoZSBncm91cHMgd2l0aCBtb3JlIHRoYW4gb25lIGdlbmUKICBtYXAoXChnZW5lX3NldCkgd2l0aGluX2RhdWVyX2NvbXBhY3RuZXNzX3Njb3JlKHNhbXBsZU1lYW5fcmxvZywgZ2VuZV9zZXQpKQpLaW1fcmVzdWx0IDwtIGFwcGVuZChLaW1fcmVzdWx0LCBzdWJzZXRfa2ltX3Jlc3VsdCkKYGBgCgpTYXZlIHRoZSBvdXRwdXQKYGBge3J9CiMgbWFrZSBhIHRhYmxlIGFuZCBzb3J0IGJ5IHRoZSByYXRpbyBvZiBhdmVyYWdlIGRpc3RhbmNlIHRvIGNlbnRyb2lkIGZvciBkYXVlcnMKIyBvdmVyIGRpc3RhbmNlIGZyb20gZGF1ZXIgY2VudHJvaWQgdG8gTDIvTDMKIyBjb21iaW5lIHdpdGggdGhlIGNvcmUgZGF1ZXIgcGF0aHdheSByZXN1bHRzCktpbV9jb3JlX2RhdWVyX3Jlc3VsdF90YWJsZSA8LSBiaW5kX3Jvd3MoCiAgIktpbSBtb3VudGFpbiIgPSBiaW5kX3Jvd3MoS2ltX3Jlc3VsdCwgLmlkID0gInN1YnNldCIpLAogICJDb3JlIGRhdWVyIHNldCAxIiA9IGJpbmRfcm93cyhjb3JlX2RhdWVyXzFfcmVzdWx0LCAuaWQgPSAic3Vic2V0IiksCiAgIkNvcmUgZGF1ZXIgc2V0IDIiID0gYmluZF9yb3dzKGNvcmVfZGF1ZXJfMl9yZXN1bHQsIC5pZCA9ICJzdWJzZXQiKSwKICAuaWQgPSAiR2VuZV9zZXQiCikgfD4gYXJyYW5nZShSMV9kYXVlcikKCiMgd3JpdGUgb3V0cHV0IHRvIGZpbGUsIHByZXNlcnZlcyBtb3JlIGRpZ2l0cyBmb3IgbGF0ZXIgY2FsY3VsYXRpb25zCndyaXRlX3RzdihLaW1fY29yZV9kYXVlcl9yZXN1bHRfdGFibGUsCiAgICAgICAgICBmaWxlLnBhdGgoIi4uL291dHB1dC93aXRoaW4uc3VtLnNxdWFyZXMiLAogICAgICAgICAgICAgICAgICAgICIyMDI1MDMwNy1nZW5lc2V0LXN0YXRzLUtpbS1tb3VudGFpbi1hbmQtY29yZS1kYXVlci50c3YiKSkKCiMgc2F2ZSBhIHJvdW5kZWQgZm9ybSB0byBtYWtlIGl0IGVhc2llciB0byB2aWV3CktpbV9jb3JlX2RhdWVyX3Jlc3VsdF90YWJsZSAlPiUgCiAgbXV0YXRlKGFjcm9zcygtYyhHZW5lX3NldCwgc3Vic2V0LCBnZW5lX3NldF9zaXplKSwgXCh4KSByb3VuZCh4LCBkaWdpdHMgPSAzKSkpICU+JSAKICB3cml0ZV90c3YoZmlsZS5wYXRoKCIuLi9vdXRwdXQvd2l0aGluLnN1bS5zcXVhcmVzIiwKICAgICAgICAgICAgICAgICAgICAgICAiMjAyNTAzMDctZ2VuZXNldC1zdGF0cy1LaW0tbW91bnRhaW4tYW5kLWNvcmUtZGF1ZXItcm91bmRlZC50c3YiKSkKYGBgCgojIyMgUGxvdHMgZm9yIFNldCAxLTMKSW5zdGVhZCBvZiByYW5raW5nIHBhdGh3YXlzIGJ5IFIxIG9yIFIyLCB3ZSBjYW4gcGxvdCB0aGUgcGF0aHdheXMgYnkgdGhlaXIgUk1TRCBhbmQgZGlzdGFuY2UgYmV0d2VlbiBtZWFuIGRhdWVyIHRvIEwyL0wzCmBgYHtyfQpwMSA8LSBLaW1fY29yZV9kYXVlcl9yZXN1bHRfdGFibGUgJT4lIAogICNmaWx0ZXIoR2VuZV9zZXQgPT0gIktpbSBtb3VudGFpbiIpICU+JQogIGdncGxvdChhZXMoeCA9IGRfTDJMM190b19kYXVlciwgeSA9IFJNU0RfZGF1ZXIpKSArIAogIHN0YXRfc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5fngrMCwgCiAgICAgICAgICAgICAgY29sb3IgPSAic2t5Ymx1ZTIiLCBsaW5ldHlwZSA9IDIpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IEdlbmVfc2V0KSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChOVUxMLCB2YWx1ZXMgPSBzZXRfY29sb3JzKSArCiAgI3NjYWxlX2NvbG9yX2JyZXdlcihOVUxMLCBwYWxldHRlID0gIkRhcmsyIikgKwogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbCgKICAgIGFlcyhsYWJlbCA9IHBhc3RlMChzdWJzZXQsICIgKCIsIGdlbmVfc2V0X3NpemUsICIpIikpLAogICAgc2l6ZSA9IDMsIGNvbG91ciA9ICJncmF5MjAiKSArCiAgeGxhYigiRGlzdGFuY2UgRGF1ZXIgdG8gTDIvTDMiKSArIHlsYWIoIlJNU0Qgd2l0aGluIERhdWVyIikgKwogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAuMSwgMC4wNSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLjEsIDApKSArCiAgZ2d0aXRsZSgiQWxsIERhdWVycyIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCnAxCmdnc2F2ZShmaWxlLnBhdGgoIi4uL291dHB1dC93aXRoaW4uc3VtLnNxdWFyZXMvaW1nIiwKICAgICAgICAgICAgICAgICAiS2ltLWFuZC1Db3JlLURhdWVyLVJNU0QtdnMtZEwyTDMtYWxsLWRhdWVycy5wbmciKSwKICAgICAgIHdpZHRoID0gNiwgaGVpZ2h0ID0gNikKYGBgCgpBbmQgZm9yIHRoZSBHSURzCmBgYHtyfQpwMiA8LSBLaW1fY29yZV9kYXVlcl9yZXN1bHRfdGFibGUgJT4lIAogIGdncGxvdChhZXMoeCA9IGRfTDJMM190b19naWQsIHkgPSBSTVNEX2dpZCkpICsgCiAgc3RhdF9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9IHl+eCswLCAKICAgICAgICAgICAgICBjb2xvciA9ICJza3libHVlMiIsIGxpbmV0eXBlID0gMikgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gR2VuZV9zZXQpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKE5VTEwsIHZhbHVlcyA9IHNldF9jb2xvcnMpICsKICAjc2NhbGVfY29sb3JfYnJld2VyKE5VTEwsIHBhbGV0dGUgPSAiRGFyazIiKSArCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKAogICAgYWVzKGxhYmVsID0gcGFzdGUwKHN1YnNldCwgIiAoIiwgZ2VuZV9zZXRfc2l6ZSwgIikiKSksCiAgICBzaXplID0gMywgY29sb3VyID0gImdyYXkyMCIpICsKICB4bGFiKCJEaXN0YW5jZSBEYXVlciB0byBMMi9MMyIpICsgeWxhYigiUk1TRCB3aXRoaW4gRGF1ZXIiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMC4xLCAwLjA1KSkgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAuMSwgMCkpICsKICBnZ3RpdGxlKCJHSURzIikgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKcDIKZ2dzYXZlKGZpbGUucGF0aCgiLi4vb3V0cHV0L3dpdGhpbi5zdW0uc3F1YXJlcy9pbWciLAogICAgICAgICAgICAgICAgICJLaW0tYW5kLUNvcmUtRGF1ZXItUk1TRC12cy1kTDJMMy1HSURzLnBuZyIpLAogICAgICAgd2lkdGggPSA2LCBoZWlnaHQgPSA2KQpgYGAKCiMjIyBQQ0EgZm9yIFNldCAxLTMKSGVyZSwgd2UgZ2VuZXJhdGUgUENBIHBsb3RzIGZvciBpbmRpdmlkdWFsIHBhdGh3YXlzIGFzIGEgZGlhZ25vc3RpYyB0b29sLiBXZSB3aWxsIHVzZSB0aGUgYGNvdW50c19ybG9nYCBvYmplY3QsIHdoaWNoIG1lYW5zIHdlIHdpbGwKMS4gVXNlIGFsbCByZXBsaWNhdGVzIGluc3RlYWQgb2Ygc2FtcGxlIG1lYW5zOwoyLiBJbmNsdWRlIFdUIGFkdWx0CjMuIFVuZmlsdGVyZWQsIGluY2x1ZGUgYWxsIGdlbmVzCgoxIGFuZCAyIGFyZSB0byBwcm92aWRlIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24gZm9yIHRoZSBkaWFnbm9zaXMgKHdpdGggbGl0dGxlIGNvc3QpLiAoMSkgZ2l2ZXMgYSBzZW5zZSBvZiBob3cgdmFyaWFibGUgYXJlIHRoZSBiaW9sb2dpY2FsIHJlcGxpY2F0ZXMgd2l0aGluIGVhY2ggZGF1ZXIgLyBjb250cm9sIHR5cGU7IEluY2x1c2lvbiBvZiBXVCBhZHVsdCBwcm92aWRlcyBhbiBhZGRpdGlvbmFsIHJlZmVyZW5jZS4KCjMgYmVjYXVzZSBsb3dseSBleHByZXNzZWQgZ2VuZXMgd2lsbCBtYWtlIHZlcnkgbGl0dGxlIGNvbnRyaWJ1dGlvbiB0byBQQ0EgKHNpbmNlIHdlIGFyZSBnb2luZyB0byBjZW50ZXIgYnV0IE5PVCBzY2FsZSB0aGUgZGF0YSkuCmBgYHtyfQpwbG90UENBKHJsb2dbdW5pcXVlKGMoY29yZV9kYXVlcl8xJFdCR2VuZUlELCBjb3JlX2RhdWVyXzIkV0JHZW5lSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2ltX21vdW50YWlucyRXQkdlbmVJRCkpLCAsZHJvcD1GQUxTRV0sIAogICAgICAgIGludGdyb3VwID0gImNvbmRpdGlvbiIsIG50b3AgPSAxMDAwMCkKYGBgCgojIyMgQ2FzZSBzdHVkeQojIyMjIFJldGlub2JsYXN0b21hIGNvbXBsZXgKR2F0aGVyIHRoZSBkYXRhCmBgYHtyfQpSYkNfZ2VuZXMgPC0gZmlsdGVyKGtpbV9tb3VudGFpbnMsIEtpbV9ncm91cCA9PSAiUmV0aW5vYmxhc3RvbWEgY29tcGxleCIpIHw+IAogIHB1bGwoV0JHZW5lSUQpCiMgYWxsIHJlcGxpY2F0ZXMsIGZvciBQQ0EKUmJDX3JlcF9tYXRyaXggPC0gY291bnRzX3Jsb2dbUmJDX2dlbmVzLCAsIGRyb3AgPSBGQUxTRV0KIyBtZWFuIGV4cHJlc3Npb24gb25seSwgZm9yIHBhaXJ3aXNlIGNvcnJlbGF0aW9ucyBhbmQgaGVhdG1hcApSYkNfbWVhbl9tYXRyaXggPC0gc2FtcGxlTWVhbl9ybG9nW1JiQ19nZW5lcywgLCBkcm9wID0gRkFMU0VdCiMgZGlzcGxheSB0aGUgbWF0cml4CnJvdW5kKFJiQ19tZWFuX21hdHJpeCwgZGlnaXRzID0gMikKYGBgCgoKIyMgU2V0IDQ6IE1ldGFib2xpYyBQYXRod2F5cwpUaGUgcGF0aHdheSBnZW5lIGxpc3RzIGFyZSBmcm9tIDxodHRwczovL3dvcm1mbHV4LnVtYXNzbWVkLmVkdS8+CmBgYAojIFRoaXMgY29kZSBpcyBvbmx5IHJ1biB0byBnZXQgZ2VuZXNldCBuYW1lcyBpbiB0aGUgc2FtZSBmb3JtYXQgYXMgdGhlIEtpbSBtb3VudGFpbnMgYW5kIGluc3VsaW4uCndvcm1mbHV4ID0geGxzeDo6cmVhZC54bHN4KCIuLi8uLi8uLi9EZXNlcV9kYXRhL0ltbXVuZV9nZW5lc190YWJsZXNfT3V0c2lkZV90YWJsZXMvd29ybWZsdXhfNC54bHN4IixzaGVldEluZGV4ID0gMixoZWFkZXIgPSBUKQpsaXN0Lndvcm1mbHV4ID0gdW5jbGFzcyh3b3JtZmx1eCkKbGlzdC53b3JtZmx1eC5ub3QubmEgPSBtYXAobGlzdC53b3JtZmx1eCwgZnVuY3Rpb24oYSluYS5vbWl0KGEpKQpnaWRzID0gd2JfbG9hZF9nZW5lX2lkcygiV1MyOTUiKQpsaXN0Lndvcm1mbHV4LmRmID0gbWFwKGxpc3Qud29ybWZsdXgubm90Lm5hLC5mID0gZnVuY3Rpb24oYilkYXRhLmZyYW1lKGdlbmVzID0gYikpCmJpbmQud29ybWZsdXguZGYgPSBiaW5kX3Jvd3MobGlzdC53b3JtZmx1eC5kZiwuaWQgPSAiaWQiKSB8PiBkcGx5cjo6cmVuYW1lKGNsYXNzPWlkKQpiaW5kLndvcm1mbHV4LmRmLnRhYmxlID0gYmluZC53b3JtZmx1eC5kZiB8PiBtdXRhdGUoZ2VuZU5hbWUgPSB3YkRhdGE6OmkycyhnZW5lcyxnaWRzKSApIHw+IGRwbHlyOjpyZW5hbWUocGF0aHdheSAgPSBjbGFzcyxXQkdlbmVJRCAgPWdlbmVzKQphbGwoYmluZC53b3JtZmx1eC5kZi50YWJsZSRnZW5lcyAlaW4lIHJvd25hbWVzKHNhbXBsZU1lYW5fcmxvZykpCndyaXRlX3RzdihiaW5kLndvcm1mbHV4LmRmLnRhYmxlLCBmaWxlID0gaGVyZSgiaW5wdXQiLCJ1cGRhdGVkLm5hbWVzLndvcm1mbHV4LnRzdiIpKQpgYGAKClJlYWQgaW4gdGhlIGdlbmUgbGlzdCBhbmQgYXBwbHkgdGhlIGNhbGN1bGF0aW9uCmBgYHtyfQojIEhvdyB0aGUgZGF0YSB3aWxsIGJlIGxvYWQKTWV0YWJvbGljX3BhdGh3YXlzIDwtIHJlYWRfdHN2KGZpbGUgPSAiLi4vaW5wdXQvdXBkYXRlZC5uYW1lcy53b3JtZmx1eC50c3YiKQpgYGAKCmBgYHtyfQojIGEgbGlzdCBvYmplY3QgdG8gc3RvcmUgdGhlIHJlc3VsdHMgb2YgaW5kaXZpZHVhbCBnZW5lIHNldHMKTWV0YWJvbGljX3Jlc3VsdCA8LSBsaXN0KAogICJhbGxfTWV0YWJvbGljIiA9IHdpdGhpbl9kYXVlcl9jb21wYWN0bmVzc19zY29yZShzYW1wbGVNZWFuX3Jsb2csCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE1ldGFib2xpY19wYXRod2F5cyRXQkdlbmVJRCkKKQpgYGAKCmBgYHtyfQpzdWJzZXRfTWV0YWJvbGljX3Jlc3VsdCA8LSB3aXRoKE1ldGFib2xpY19wYXRod2F5cywgc3BsaXQoV0JHZW5lSUQsIHBhdGh3YXkpKSB8PgogIGtlZXAoXCh4KSBsZW5ndGgodW5pcXVlKHgpKSA+IDEpIHw+ICMga2VlcCBvbmx5IHRoZSBncm91cHMgd2l0aCBtb3JlIHRoYW4gb25lIGdlbmUKICBtYXAoXChnZW5lX3NldCkgd2l0aGluX2RhdWVyX2NvbXBhY3RuZXNzX3Njb3JlKHNhbXBsZU1lYW5fcmxvZywgZ2VuZV9zZXQpKQpNZXRhYm9saWNfcmVzdWx0IDwtIGFwcGVuZChNZXRhYm9saWNfcmVzdWx0LCBzdWJzZXRfTWV0YWJvbGljX3Jlc3VsdCkKYGBgCgpTYXZlIHRoZSBvdXRwdXQKYGBge3J9CiMgbWFrZSBhIHRhYmxlIGFuZCBzb3J0IGJ5IHRoZSByYXRpbyBvZiBhdmVyYWdlIGRpc3RhbmNlIHRvIGNlbnRyb2lkIGZvciBkYXVlcnMKIyBvdmVyIGRpc3RhbmNlIGZyb20gZGF1ZXIgY2VudHJvaWQgdG8gTDIvTDMKIyBjb21iaW5lIHdpdGggdGhlIGNvcmUgZGF1ZXIgcGF0aHdheSByZXN1bHRzCk1ldGFib2xpY19yZXN1bHRfdGFibGUgPC0gYmluZF9yb3dzKE1ldGFib2xpY19yZXN1bHQsIC5pZCA9ICJzdWJzZXQiKSB8PgogIG11dGF0ZShHZW5lX3NldCA9ICJNZXRhYm9saWMgcGF0aHdheXMiKSB8PgogIHJlbG9jYXRlKEdlbmVfc2V0LCAuYmVmb3JlID0gc3Vic2V0KSB8PgogIGFycmFuZ2UoUjFfZGF1ZXIpCgojIHdyaXRlIG91dHB1dCB0byBmaWxlLCBwcmVzZXJ2ZXMgbW9yZSBkaWdpdHMgZm9yIGxhdGVyIGNhbGN1bGF0aW9ucwp3cml0ZV90c3YoTWV0YWJvbGljX3Jlc3VsdF90YWJsZSwKICAgICAgICAgIGhlcmU6OmhlcmUoIm91dHB1dC93aXRoaW4uc3VtLnNxdWFyZXMiLAogICAgICAgICAgICAgICAgICAgICAiMjAyNTAzMDctZ2VuZXNldC1zdGF0cy1NZXRhYm9saWMtcGF0aHdheXMudHN2IikpCgojIHNhdmUgYSByb3VuZGVkIGZvcm0gdG8gbWFrZSBpdCBlYXNpZXIgdG8gdmlldwpNZXRhYm9saWNfcmVzdWx0X3RhYmxlICU+JSAKICBtdXRhdGUoYWNyb3NzKC1jKEdlbmVfc2V0LCBzdWJzZXQsIGdlbmVfc2V0X3NpemUpLCBcKHgpIHJvdW5kKHgsIGRpZ2l0cyA9IDMpKSkgJT4lIAogIHdyaXRlX3RzdihoZXJlOjpoZXJlKCJvdXRwdXQvd2l0aGluLnN1bS5zcXVhcmVzIiwKICAgICAgICAgICAgICAgICAgICAgICAiMjAyNTAzMDctZ2VuZXNldC1zdGF0cy1NZXRhYm9saWMtcGF0aHdheXMtcm91bmRlZC50c3YiKSkKYGBgCgojIyMgUGxvdHMgZm9yIFNldCA0Ckluc3RlYWQgb2YgcmFua2luZyBwYXRod2F5cyBieSBSMSBvciBSMiwgd2UgY2FuIHBsb3QgdGhlIHBhdGh3YXlzIGJ5IHRoZWlyIFJNU0QgYW5kIGRpc3RhbmNlIGJldHdlZW4gbWVhbiBkYXVlciB0byBMMi9MMwpgYGB7cn0KcDEgPC0gTWV0YWJvbGljX3Jlc3VsdF90YWJsZSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBkX0wyTDNfdG9fZGF1ZXIsIHkgPSBSTVNEX2RhdWVyKSkgKyAKICBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iLCBmb3JtdWxhID0geX54KzAsIAogICAgICAgICAgICAgIGNvbG9yID0gInNreWJsdWUyIiwgbGluZXR5cGUgPSAyKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBnZW5lX3NldF9zaXplIDwgMTApKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKCJGZXdlciB0aGFuIDEwIGdlbmVzIiwgdmFsdWVzID0gYygiYmxhY2siLCAicGluayIpKSArCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKAogICAgYWVzKGxhYmVsID0gcGFzdGUwKHN1YnNldCwgIiAoIiwgZ2VuZV9zZXRfc2l6ZSwgIikiKSksCiAgICBzaXplID0gMiwgY29sb3VyID0gImdyYXkyMCIsIG1pbi5zZWdtZW50Lmxlbmd0aCA9IDApICsKICB4bGFiKCJEaXN0YW5jZSBEYXVlciB0byBMMi9MMyIpICsgeWxhYigiUk1TRCB3aXRoaW4gRGF1ZXIiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMC4xLCAwLjA1KSkgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAuMSwgMCkpICsKICBnZ3RpdGxlKCJBbGwgRGF1ZXJzIikgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKcDEKZ2dzYXZlKGZpbGUucGF0aCgiLi4vb3V0cHV0L3dpdGhpbi5zdW0uc3F1YXJlcy9pbWciLAogICAgICAgICAgICAgICAgICJNZXRhYm9saWMtcGF0aHdheXMtUk1TRC12cy1kTDJMMy1hbGwtZGF1ZXJzLnBuZyIpLAogICAgICAgd2lkdGggPSA2LCBoZWlnaHQgPSA2KQpgYGAKCkFuZCBmb3IgdGhlIEdJRHMKYGBge3J9CnAyIDwtIE1ldGFib2xpY19yZXN1bHRfdGFibGUgJT4lCiAgZ2dwbG90KGFlcyh4ID0gZF9MMkwzX3RvX2dpZCwgeSA9IFJNU0RfZ2lkKSkgKyAKICBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iLCBmb3JtdWxhID0geX54KzAsIAogICAgICAgICAgICAgIGNvbG9yID0gInNreWJsdWUyIiwgbGluZXR5cGUgPSAyKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBnZW5lX3NldF9zaXplIDwgMTApKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKCJGZXdlciB0aGFuIDEwIGdlbmVzIiwgdmFsdWVzID0gYygiYmxhY2siLCAicGluayIpKSArCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKAogICAgYWVzKGxhYmVsID0gcGFzdGUwKHN1YnNldCwgIiAoIiwgZ2VuZV9zZXRfc2l6ZSwgIikiKSksCiAgICBzaXplID0gMiwgY29sb3VyID0gImdyYXkyMCIsIG1pbi5zZWdtZW50Lmxlbmd0aCA9IDApICsKICAjeGxhYigiRGlzdGFuY2UgRGF1ZXIgdG8gTDIvTDMiKSArIHlsYWIoIlJNU0Qgd2l0aGluIERhdWVyIikgKwogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAuMSwgMC4wNSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLjEsIDApKSArCiAgeGxhYigiRGlzdGFuY2UgRGF1ZXIgdG8gTDIvTDMiKSArIHlsYWIoIlJNU0Qgd2l0aGluIEdJRHMiKSArCiAgZ2d0aXRsZSgiR0lEcyIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCnAyCmdnc2F2ZShmaWxlLnBhdGgoIi4uL291dHB1dC93aXRoaW4uc3VtLnNxdWFyZXMvaW1nIiwKICAgICAgICAgICAgICAgICAiTWV0YWJvbGljLXBhdGh3YXlzLVJNU0QtdnMtZEwyTDMtR0lEcy5wbmciKSwKICAgICAgIHdpZHRoID0gNiwgaGVpZ2h0ID0gNikKYGBgCi0tPg==