library(DESeq2)
#library(tximport)
library(tidyverse)
library(plotly)
library(PCAtools)
#library(patchwork)
#library(wbData)
#library(clusterProfiler)
#library(org.Ce.eg.db)
#library(DOSE)

Common settings

# enrichment color
options(enrichplot.colours = c("red","blue"))
# plotting theme
old <- theme_set(theme_bw(base_size = 16))

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.

Import data

The preprocessing of the quant data were performed in an early analysis (../../90-archived-analyses/Analysis/20250226-HBZ-within.sumsquares.Rmd) and saved to an RData file. We will load that file here.

load("../input/20250310-Dauer-project-DESeq2-objects.rdata")

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==