By: Owen Lo

# Install and load required packages
if(!requireNamespace("readxl", quietly = TRUE)) {
  install.packages("readxl")
}
if (!requireNamespace("knitr", quietly = TRUE)) {
  install.packages("knitr")
}

library(GEOquery)
library(tibble)
library(readxl)
library(dplyr)
library(tidyr)
library(knitr)
library(biomaRt)
library(DESeq2)
library(edgeR)
library(SummarizedExperiment)
library(ggplot2)
library(ComplexHeatmap)
library(circlize)

This workbook uses the GEOQuery (Davis and Meltzer 2007), biomaRt (Durinck et al. 2005), tibble (Müller and Wickham 2025), readxl(Wickham and Bryan 2025), dplyr (Wickham et al. 2023), tidyr(Wickham et al. 2025), knitr (Xie 2014), DESeq2(Love et al. 2014), edgeR (Chen et al. 2025), SummarizedExperiment(Morgan et al. 2025), ggplot2 (Wickham 2016), ComplexHeatmap (Gu 2022) and circlize (Gu et al. 2014) packages in its code chunks.

Introduction

Septic shock is a potentially fatal complication of sepsis, and treatment largely depends on early supportive hemodynamic therapy. Patient response to hemodynamic therapy varies, and the NCBI Gene Expression Omnibus (GEO) (Barrett et al. 2012) series GSE110487 is a dataset from 2018 consisting of RNA sequencing read counts of whole blood samples taken from 14 septic shock patients who responded to hemodynamic therapy and 17 non-responders, at two time points: immediately after ICU admission, and 48 hours later, after hemodynamic therapy. (Barcella et al. 2018)

Fetching and assessing data

Downloading and formatting

We’ll use the GEOQuery package to retrieve the supplementary files and metadata pertaining to the experiment from the GEO with its series accession number, and then look at how it’s formatted.

geo_id <- "GSE110487"

# get metadata associated with gse and supplementary files
gse <- GEOquery::getGEO(geo_id, GSEMatrix=FALSE)
supp <- GEOquery::getGEOSuppFiles(geo_id, fetch_files=FALSE)

# download data if not already downloaded
current_dir <- file.path(getwd())
supp_file_path <- file.path(current_dir, geo_id, supp$fname[1])
if (!file.exists(supp_file_path)) {
  data_file <- GEOquery::getGEOSuppFiles(GEO = geo_id)
}

# read data into a dataframe
raw_counts <- readxl::read_xlsx(supp_file_path)

# take a peek
knitr::kable(raw_counts[1:5, 1:10], format = "html", align = "c")
Geneid E04T1 E04T2 E07T1 E07T2 E15T1 E15T2 E16T1 E16T2 E17T1
ENSG00000000003 1 0 1 1 2 4 1 4 4
ENSG00000000005 0 0 1 1 0 0 0 0 0
ENSG00000000419 259 208 599 576 734 534 630 653 283
ENSG00000000457 184 332 370 400 410 345 444 438 263
ENSG00000000460 89 81 245 185 117 167 177 134 127
# count genes and samples
cat(paste("Number of genes: ", nrow(raw_counts),
          "\nNumber of samples: ", ncol(raw_counts)-1))
Number of genes:  58096 
Number of samples:  62

Samples in the raw data are named according to patient identity and time point, but don’t contain any information on whether the patient was a responder or a non-responder, so we’ll map sample identifiers to both response and time point.

# retrieve list of samples in the series, and compile condition data for each
samples <- gse@gsms
sample_sources <- do.call(rbind, 
                             lapply(samples, FUN=function(x){
                               c(x@header$title,
                                 x@header$source_name_ch1)}))

# Use this data to create a dataframe mapping sample titles to responder status and time point
sample_metadata <- as.data.frame(sample_sources, stringsAsFactors = FALSE)
colnames(sample_metadata) = c("sample", "source")
sample_metadata <- sample_metadata %>% 
  magrittr::set_rownames(.$sample) %>% 
  mutate(
    response = ifelse(grepl("^Responder", source), "R", "NR"),
    time_point = ifelse(grepl("T1", source), "T1", "T2"),
  ) %>% 
  dplyr::select(response, time_point)

# take a peek
knitr::kable(sample_metadata[1:5,], format = "html", align = "c")
response time_point
E04T1 R T1
E04T2 R T2
E07T1 NR T1
E07T2 NR T2
E15T1 NR T1

Quality assessment

Now, we’ll determine whether data quality varies across conditions

# calculating summary statistics for each sample
sample_summaries <- data.frame(do.call(cbind, lapply(raw_counts[, 2:ncol(raw_counts)], summary)))
qual_overview <- as.data.frame(t(sample_summaries))

# Adding extra statistics for total reads, number of genes with zero expression, number of genes with low expression
qual_overview <- qual_overview %>% 
  dplyr::mutate(total_reads = colSums(raw_counts[2:ncol(raw_counts)]),
                zero_exp = colSums(raw_counts[2:ncol(raw_counts)] == 0),
                low_exp = colSums(raw_counts[2:ncol(raw_counts)]<10),
                )

# adding metadata to sort by conditions
qual_overview <- merge(qual_overview, sample_metadata, by = 0)

# overview stats for samples from non-responders and responders
stats_by_response <- qual_overview %>%
  dplyr::group_by(response,) %>%
  summarize(
    NumSamples = n(),
    MeanLibSize = mean(total_reads),
    SDLibSize = sd(total_reads),
    PropZeroExp = mean(zero_exp) / nrow(raw_counts),
    PropLowExp = mean(low_exp) / nrow(raw_counts),
  )

# overview stats for samples from time point 1 and time point 2
stats_by_time <- qual_overview %>%
  dplyr::group_by(time_point,) %>%
  summarize(
    NumSamples = n(),
    MeanLibSize = mean(total_reads),
    SDLibSize = sd(total_reads),
    PropZeroExp = mean(zero_exp) / nrow(raw_counts),
    PropLowExp = mean(low_exp) / nrow(raw_counts),
  )

# merging tables and displaying
stats_by_condition <- bind_rows(
    stats_by_response %>% 
        dplyr::mutate(group_type = "response") %>%
        dplyr::rename(condition = response),
    stats_by_time %>% 
        dplyr::mutate(group_type = "time_point") %>%
        dplyr::rename(condition = time_point)
    )
knitr::kable(stats_by_condition[,1:6], format = "html", align = "c")
condition NumSamples MeanLibSize SDLibSize PropZeroExp PropLowExp
NR 28 13414209 3691198 0.5809215 0.7772332
R 34 13675120 3772593 0.5990009 0.7761170
T1 31 13725546 3630149 0.5940995 0.7795584
T2 31 13389033 3836210 0.5875725 0.7736838

Mean, standard deviation, and proportions of unexpressed and lowly expressed genes are similar across patient responder status and time, so special correction for data quality won’t be necessary.

Identifier mapping and cleaning

Mapping to HUGO symbols

The data uses Ensembl Gene IDs beginning with ENSG.

# get ensembl version 100 and the human gene dataset
ensembl <- biomaRt::useEnsembl(biomart = "ensembl")
ensembl <- biomaRt::useDataset("hsapiens_gene_ensembl", mart = ensembl)

Rather than Ensembl Gene IDs, we want to use HUGO Gene Nomenclature Committee (HGNC) symbols to identify the genes in our dataset. We’ll use the Ensembl human dataset to map as many ENSG IDs as possible to HGNC symbols, and add those mappings to our read data.

# store ensembl IDs from the data as a vector
ensembl_ids <- raw_counts$Geneid

# check if an ensembl to hgnc conversion file exists already
conversion_stash <- "ensembl_to_hgnc.rds"
if (file.exists(conversion_stash)) {
  
  # if the file exists, read conversion data from that
  ensembl_to_hgnc <- readRDS(conversion_stash)
} else {
  
  # if the file does not exist, get conversion data from biomaRt and cache it
  ensembl_to_hgnc <- biomaRt::getBM(attributes = c("ensembl_gene_id", "hgnc_symbol"),
                         filters = c("ensembl_gene_id"),
                         values = ensembl_ids,
                         mart = ensembl)
  saveRDS(ensembl_to_hgnc, conversion_stash)
}

# merge conversion df with data and rearrange such that both id columns come before samples
raw_counts_annot <- dplyr::left_join(raw_counts, ensembl_to_hgnc, 
                                     by = c("Geneid" = "ensembl_gene_id"))
raw_counts_cols <- dplyr::setdiff(names(raw_counts), "Geneid")
colnames(raw_counts_annot)[1] = "ensembl_gene_id"
raw_counts_annot <- raw_counts_annot[, c("ensembl_gene_id", "hgnc_symbol", raw_counts_cols)]

# peek at annotated counts
knitr::kable(raw_counts_annot[1:5, 1:10], format = "html", align = "c")
ensembl_gene_id hgnc_symbol E04T1 E04T2 E07T1 E07T2 E15T1 E15T2 E16T1 E16T2
ENSG00000000003 TSPAN6 1 0 1 1 2 4 1 4
ENSG00000000005 TNMD 0 0 1 1 0 0 0 0
ENSG00000000419 DPM1 259 208 599 576 734 534 630 653
ENSG00000000457 SCYL3 184 332 370 400 410 345 444 438
ENSG00000000460 C1orf112 89 81 245 185 117 167 177 134

Now rows are identified by both ENSG ID and HGNC symbol.

Cleaning out low expression and unmapped rows

Assaying for rows where mapping one ENSG ID to one HGNC symbol failed:

# subset rows where one ensembl id maps to multiple hgnc symbols
ensembl_dups <- raw_counts_annot$ensembl_gene_id[duplicated(raw_counts_annot$ensembl_gene_id)]
multiple_hgnc <- raw_counts_annot %>% 
  filter(ensembl_gene_id %in% ensembl_dups)

# subset rows where multiple ensembl ids map to one hgnc symbol
hgnc_dups <- raw_counts_annot$hgnc_symbol[duplicated(raw_counts_annot$hgnc_symbol)]
same_hgnc <- raw_counts_annot %>% 
  filter(hgnc_symbol %in% hgnc_dups & hgnc_symbol != "")

# subset rows where ensembl id does not map to a hgnc symbol 
unmapped <- raw_counts_annot %>% 
  filter(is.na(hgnc_symbol) | hgnc_symbol == "")

cat(paste(nrow(unmapped), "unmapped rows\n",
          nrow(multiple_hgnc), 
          "rows where one ENSG ID maps to multiple HGNC symbols \n",
          nrow(same_hgnc), 
          "rows where multiple ENSG IDs map to the same HGNC symbol"))
20178 unmapped rows
 8 rows where one ENSG ID maps to multiple HGNC symbols 
 28 rows where multiple ENSG IDs map to the same HGNC symbol

A high proportion of the dataset’s rows do not map to any HGNC symbol. These are likely to correspond to outdated aliases of current symbols, or deprecated ORFs. By filtering low expression genes from the data and comparing the proportions of mapped and unmapped genes that remain, we can assess the significance of the unmapped rows.

# size of smallest condition group in the dataset: non-responders at time point 1/2
smallest_group_size <- 11

# keep only unmapped rows that have a count of at least 10 in 11 samples
rows_to_keep <- rowSums(unmapped >= 10) >= smallest_group_size
clean_unmapped <- unmapped[rows_to_keep,]

# subset mapped counts 
mapped <- raw_counts_annot %>% 
  filter(!is.na(hgnc_symbol) & hgnc_symbol != "")

# apply same expression filter to mapped counts 
rows_to_keep <- rowSums(mapped >= 10) >= smallest_group_size
clean_mapped <- mapped[rows_to_keep,]

# collate and display results of filtering
cleaning_results <- data.frame(
  Type = c("mapped genes", "unmapped genes"),
  PreCleanCount = c(nrow(mapped), nrow(unmapped)),
  PostCleanCount = c(nrow(clean_mapped), nrow(clean_unmapped)),
  SignificantProportion = c(nrow(clean_mapped)/nrow(mapped), 
                     nrow(clean_unmapped)/nrow(unmapped))
)
knitr::kable(cleaning_results, format = "html", align = "c")
Type PreCleanCount PostCleanCount SignificantProportion
mapped genes 37922 13673 0.3605559
unmapped genes 20178 1907 0.0945089

Less than a tenth of the unmapped rows had sufficient levels of expression to not be filtered in early data cleaning, compared to more than a third of the mapped rows.

Since unmapped rows are largely lowly expressed, we can discard them without losing a great amount of information. ENSG IDs mapping to multiple HGNC symbols may be transcripts that hybridize to more than one gene, and since they are in such small proportion in the dataset, we will treat them as multiple genes without significantly impacting analysis. HGNC symbols mapped to by multiple ENSG IDs may be genes with multiple splice variants. Since they are likewise in minuscule proportion in the dataset, we will sum together rows that map to the same HUGO gene.

# create a new dataframe where rows correspond to unique hgnc symbols
clean_counts_byhgnc <- clean_mapped %>% 
  dplyr::group_by(hgnc_symbol) %>% 
  # sum rows that map to the same hgnc symbol
  dplyr::summarise(dplyr::across(dplyr::where(is.numeric), ~ sum(.x, na.rm = TRUE)))

knitr::kable(clean_counts_byhgnc[1:5, 1:10], format = "html", align = "c")
hgnc_symbol E04T1 E04T2 E07T1 E07T2 E15T1 E15T2 E16T1 E16T2 E17T1
A1BG-AS1 6 7 19 11 20 9 13 20 7
A2M-AS1 2 1 2 2 2 5 2 2 3
AAAS 22 21 25 35 45 18 37 23 33
AACS 28 10 27 60 133 50 96 64 52
AAGAB 177 202 458 520 644 476 475 687 278
cat(paste("Number of genes: ", nrow(clean_counts_byhgnc),
          "\nNumber of samples: ", ncol(clean_counts_byhgnc)-1))
Number of genes:  13673 
Number of samples:  62

We now have a dataframe of read counts for genes with expression levels above a baseline, identified by unique HUGO symbols.

Normalization

Converting data for DESeq2

Let’s create a DESeqDataSet object from our data, and continue our analysis with the DESeq2 package, adapting methods from Love et al’s RNASeq workflow (Love et al. 2015).

# extract counts as a matrix and set row and column names
clean_count_matrix <- as.matrix(clean_counts_byhgnc[,2:ncol(clean_counts_byhgnc)])
rownames(clean_count_matrix) <- clean_counts_byhgnc$hgnc_symbol
colnames(clean_count_matrix) <- colnames(clean_counts_byhgnc)[2:ncol(clean_counts_byhgnc)]

#create a variable in the metadata from the interaction of response and time_point
sample_metadata$group = interaction(sample_metadata$response, 
                                      sample_metadata$time_point, 
                                      sep = "_")

# create a DESeqDataSet object from the counts matrix and the sample metadata, using the interaction of response and time point as the design. 
counts_dds <- DESeq2::DESeqDataSetFromMatrix(countData = clean_count_matrix,
                                             colData = sample_metadata,
                                             design = ~group)

Normalizing counts

We’ll implement relative log expression (RLE) normalization on the counts using the DESeq2 package. RLE normalization is based on the hypothesis that most genes are not differentially expressed, which is suitable for this dataset, as the associated publication identifies a low proportion of DE genes.

# estimate normalization factors and apply them to counts
counts_dds <- DESeq2::estimateSizeFactors(counts_dds)
norm_counts <- DESeq2::counts(counts_dds, normalized = TRUE)

Plotting sample clustering

In order to visualize the effects of normalization, we’ll generate Multi-Dimensional Scaling (MDS) plots from the un-normalized and normalized data.

# create SummarizedExperiment objects with un-normalized and normalized counts
unnorm_counts <- DESeq2::counts(counts_dds, normalized = FALSE)
unnorm_counts_se <- SummarizedExperiment(assays = list(counts = unnorm_counts),
                                         colData = sample_metadata)
norm_counts_se <- SummarizedExperiment(assays = list(counts = norm_counts), 
                                       colData = sample_metadata)

# generate MDS data with un-normalized counts
unnorm_mds <- edgeR::plotMDS.SummarizedExperiment(unnorm_counts_se, plot = FALSE)
unnorm_mds_df <- data.frame(
  Dim1 = unnorm_mds$x,
  Dim2 = unnorm_mds$y,
  Group = unnorm_counts_se$group,
  Sample = colnames(unnorm_counts_se)
)

# generate MDS data with normalized counts
norm_mds <- edgeR::plotMDS.SummarizedExperiment(norm_counts_se, plot = FALSE)
norm_mds_df <- data.frame(
  Dim1 = norm_mds$x,
  Dim2 = norm_mds$y,
  Group = norm_counts_se$group,
  Sample = colnames(norm_counts_se)
)

# plot un-normalized MDS  
ggplot2::ggplot(unnorm_mds_df, aes(x = Dim1, y = Dim2, color = Group)) +
  geom_point(size = 3) +
  theme_minimal() +
  labs(
    x = paste("Dim1 (", round(unnorm_mds$var.explained[1]*100, 1), "%)"),
    y = paste("Dim2 (", round(unnorm_mds$var.explained[2]*100, 1), "%)"),
    title = "MDS plot: un-normalized counts"
  )

Figure 1: MDS plot generated from filtered RNA sequencing read counts of whole blood samples from septic shock patients who were responsive (R) and non-responsive (NR) to hemodynamic therapy, taken at time points before and after treatment (T1 and T2), without normalization.

# plot normalized MDS
ggplot2::ggplot(norm_mds_df, aes(x = Dim1, y = Dim2, color = Group)) +
  geom_point(size = 3) +
  theme_minimal() +
  labs(
    x = paste("Dim1 (", round(norm_mds$var.explained[1]*100, 1), "%)"),
    y = paste("Dim2 (", round(norm_mds$var.explained[2]*100, 1), "%)"),
    title = "MDS plot: normalized counts"
  )

Figure 2: MDS plot generated from filtered RNA sequencing read counts of whole blood samples from septic shock patients who were responsive (R) and non-responsive (NR) to hemodynamic therapy, taken at time points before and after treatment (T1 and T2), normalized by relative log expression.

The MDS plots generated from the data pre and post normalization are nearly identical. The low impact of normalization indicates little technical bias in the dataset, and suggests that msot variation is biological

Differential gene expression

Evaluating differential expression

From Fig. 1 and Fig. 2, some grouping is visible of samples within the same set of conditions. Samples taken at the same time point from patients with the same response behaviour loosely cluster together. As such, we’ll use the four combinations of response and time point as factors to evaluate differential expression.

# evaluate differential expression. Appropriate design already specified in counts_dds
counts_dds <- DESeq2::DESeq(counts_dds)

We’ll look for genes that are differentially expressed based on patient response at each of the two time points, and look for genes that are differentially expressed over time within each of the two patient response groups.

# DE between R and NR at T1
response_T1 <- DESeq2::results(counts_dds, contrast = c("group", "NR_T1", "R_T1"), alpha = 0.05)
summary(response_T1)

# DE between R and NR at T2 
response_T2 <- DESeq2::results(counts_dds, contrast = c("group", "NR_T2", "R_T2"), alpha = 0.05)
summary(response_T2)

# DE between T1 and T2 for NR patients
time_NR <- DESeq2::results(counts_dds, contrast = c("group", "NR_T1", "NR_T2"), alpha =0.05)
summary(time_NR)

# DE between T1 and T2 for R patients
time_R <- DESeq2::results(counts_dds, contrast = c("group", "R_T1", "R_T2"), alpha = 0.05)
summary(time_R)
# get genes with adjusted p-value < 0.05 for each test
sig_T1 <- response_T1[response_T1$padj < 0.05,]
sig_T2 <- response_T2[response_T2$padj < 0.05,]
sig_NR <- time_NR[time_NR$padj < 0.05,]
sig_R <- time_R[time_R$padj < 0.05,]

cat(paste(nrow(sig_T1), "DE genes between R and NR at T1\n",
          nrow(sig_T2), "DE genes between R and NR at T2\n",
          nrow(sig_NR), "DE genes from T1 to T2 in NR\n",
          nrow(sig_R), "DE genes from T1 to T2 in R"))
0 DE genes between R and NR at T1
 67 DE genes between R and NR at T2
 846 DE genes from T1 to T2 in NR
 1613 DE genes from T1 to T2 in R

DESeq2 implements multiple hypothesis testing via Benjamini-Hochberg false discovery rate correction on p-values, producing adjusted p-values that more accurately reflect significance in bulk RNA-sequencing data than the original p-values given by the Wald test for differential expression. The threshold we’ve used for differentially expressed genes is genes with adjusted p-value < 0.05, a commonly used threshold [cite cite].

In this analysis, we’ll work with the genes that were differentially expressed between the R and NR groups at T2. Since no genes are significantly differentially expressed between the R and NR groups at T1, analyzing the genes that are differentially expressed between the groups at T2 will tell us which genes were regulated differently between responder and non-responder patients as a result of the same conditions, yielding information on the differences between the two patient group’s biological responses to the therapy.

Visualizing differentially expressed genes

We’ll create a volcano plot of the genes using the differential expression statistics calculated between the R and NR groups at T2.

# convert DE results to data frame
difexp_info <- as.data.frame(response_T2)

# add column indicating whether genes are significantly up or downregulated
difexp_info$DE <- "no"
difexp_info$DE[difexp_info$log2FoldChange > 0 & difexp_info$padj < 0.05] <- "up"
difexp_info$DE[difexp_info$log2FoldChange < 0 & difexp_info$padj < 0.05] <- "down"

# generate volcano plot, -log10 of adjusted p-val vs log2 of FC
ggplot2::ggplot(data = difexp_info, mapping = aes(x = log2FoldChange, y = -log10(padj), col = DE)) + 
  geom_point() +
  theme_minimal() + 
  geom_hline(yintercept = -log10(0.05), col = "black", linetype = 'dashed') + 
  scale_color_manual(values = c("blue", "grey", "red"),
                     labels = c("Downregulated in R group", "Not DE", "Upregulated in R group")) +
  labs(color = "Expression", 
       x = "log 2 fold change",
       y = "log 10 corrected p-value",
       title = "Expression of genes in R vs. NR samples at T2")

Figure 3: FDR-corrected p-value (-log10) vs. fold change (log2) in expression of genes in whole blood samples from septic shock patients who responded to early supportive hemodynamic therapy (R samples) and patients who did not respond (NR), taken 48 hours after treatment. Threshold for differential expression was corrected p-value < 0.05.

Next, we’ll create a heatmap of the genes differentially expressed between the R and NR groups at T2, and see how they cluster according to sample conditions.

# use normalized counts for heatmap matrix, filter for DE genes
normcounts_heatmap <- norm_counts[rownames(norm_counts) %in% rownames(sig_T2),]

# normalize rows
normcounts_heatmap <- t(scale(t(normcounts_heatmap)))

# set colours to scale from blue at min values to white at 0 to red at max values
heatmap_col = circlize::colorRamp2(c(min(normcounts_heatmap), 0, max(normcounts_heatmap)),
                                   c("blue", "white", "red"))

# set colours to correspond to experimental conditions
group_colours <- rainbow(n = 4)
names(group_colours) <- unique(sample_metadata$group)

# add annotation with experimental conditions and corresponding colours
hannot <- ComplexHeatmap::HeatmapAnnotation(df = data.frame(
  Group = sample_metadata$group),
  col = list(
    Group = group_colours
  ),
show_legend = TRUE)

# generate heatmap
DE_heatmap <- ComplexHeatmap::Heatmap(normcounts_heatmap, 
                                   col = heatmap_col,
                                   show_column_names = FALSE,
                                   show_row_names = FALSE,
                                   top_annotation = hannot,
                                   show_heatmap_legend = FALSE,
                                   column_title = "Genes differentially expressed in R vs. NR groups at T1")

DE_heatmap

Figure 4: Heatmap of genes differentially expressed in septic shock patients who responded to hemodynamic therapy and patients who did not respond 48 hours after treatment (groups R_T2 and NR_T2, respectively). Blue denotes low gene expression and red denotes high gene expression.

The R_T2 and NR_T2 samples largely clustered apart from each other, while the R_T1 and NR_T1 samples are not. This is as expected, since the genes being plotted are differentially expressed between R and NR at T2, while no genes are differentially expressed between R and NR at T1. There is also significant separation between the T1 and T2 groups for R, but not for NR, indicating that many of these genes change in expression in responders over the 48 hours after hemodynamic therapy, but not in non-responders.

Interpretation and documentation

Bibliography

Barcella, Matteo, Bernardo Bollen Pinto, Daniele Braga, et al. 2018. “Identification of a Transcriptome Profile Associated with Improvement of Organ Function in Septic Shock Patients After Early Supportive Therapy.” Critical Care 22 (1). https://doi.org/10.1186/s13054-018-2242-3.
Barrett, Tanya, Stephen E. Wilhite, Pierre Ledoux, et al. 2012. “NCBI GEO: Archive for Functional Genomics Data Sets—Update.” Nucleic Acids Research 41 (D1): D991–95. https://doi.org/10.1093/nar/gks1193.
Chen, Yunshun, Lizhong Chen, Aaron T L Lun, Pedro Baldoni, and Gordon K Smyth. 2025. edgeR V4: Powerful Differential Analysis of Sequencing Data with Expanded Functionality and Improved Support for Small Counts and Larger Datasets.” Nucleic Acids Research 53 (2): gkaf018. https://doi.org/10.1093/nar/gkaf018.
Davis, Sean, and Paul S. Meltzer. 2007. “GEOquery: A Bridge Between the Gene Expression Omnibus (GEO) and BioConductor.” Bioinformatics 23 (14): 1846–47. https://doi.org/10.1093/bioinformatics/btm254.
Durinck, Steffen, Yves Moreau, Arek Kasprzyk, et al. 2005. “BioMart and Bioconductor: A Powerful Link Between Biological Databases and Microarray Data Analysis.” Bioinformatics 21: 3439–40.
Gu, Zuguang. 2022. “Complex Heatmap Visualization.” iMeta, ahead of print. https://doi.org/10.1002/imt2.43.
Gu, Zuguang, Lei Gu, Roland Eils, Matthias Schlesner, and Benedikt Brors. 2014. “Circlize Implements and Enhances Circular Visualization in r.” Bioinformatics, ahead of print. https://doi.org/10.1093/bioinformatics/btu393.
Love, Michael I., Simon Anders, Vladislav Kim, and Wolfgang Huber. 2015. “RNA-Seq Workflow: Gene-Level Exploratory Analysis and Differential Expression.” F1000Research 4 (October): 1070. https://doi.org/10.12688/f1000research.7035.1.
Love, Michael I., Wolfgang Huber, and Simon Anders. 2014. “Moderated Estimation of Fold Change and Dispersion for RNA-Seq Data with DESeq2.” Genome Biology 15: 550. https://doi.org/10.1186/s13059-014-0550-8.
Morgan, Martin, Valerie Obenchain, Jim Hester, and Hervé Pagès. 2025. SummarizedExperiment: A Container (S4 Class) for Matrix-Like Assays. https://doi.org/10.18129/B9.bioc.SummarizedExperiment.
Müller, Kirill, and Hadley Wickham. 2025. Tibble: Simple Data Frames. https://tibble.tidyverse.org/.
Wickham, Hadley. 2016. Ggplot2: Elegant Graphics for Data Analysis. Springer-Verlag New York. https://ggplot2.tidyverse.org.
Wickham, Hadley, and Jennifer Bryan. 2025. Readxl: Read Excel Files. https://readxl.tidyverse.org.
Wickham, Hadley, Romain François, Lionel Henry, Kirill Müller, and Davis Vaughan. 2023. Dplyr: A Grammar of Data Manipulation. https://doi.org/10.32614/CRAN.package.dplyr.
Wickham, Hadley, Davis Vaughan, and Maximilian Girlich. 2025. Tidyr: Tidy Messy Data. https://doi.org/10.32614/CRAN.package.tidyr.
Xie, Yihui. 2014. “Knitr: A Comprehensive Tool for Reproducible Research in R.” In Implementing Reproducible Computational Research, edited by Victoria Stodden, Friedrich Leisch, and Roger D. Peng. Chapman; Hall/CRC.
LS0tCnRpdGxlOiAiRGlmZmVyZW50aWFsIEdlbmUgRXhwcmVzc2lvbiBpbiBTZXB0aWMgU2hvY2sgUGF0aWVudHMgQmFzZWQgb24gUmVzcG9uc2UgdG8gRWFybHkgU3VwcG9ydGl2ZSBIZW1vZHluYW1pYyBUaGVyYXB5IgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgogICAgY29kZV9mb2xkaW5nOiBzaG93CmJpYmxpb2dyYXBoeTogQTFfYmliLmJpYgotLS0KCgoKQnk6ICoqT3dlbiBMbyoqCmBgYHtyIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJ30KIyBJbnN0YWxsIGFuZCBsb2FkIHJlcXVpcmVkIHBhY2thZ2VzCmlmKCFyZXF1aXJlTmFtZXNwYWNlKCJyZWFkeGwiLCBxdWlldGx5ID0gVFJVRSkpIHsKICBpbnN0YWxsLnBhY2thZ2VzKCJyZWFkeGwiKQp9CmlmICghcmVxdWlyZU5hbWVzcGFjZSgia25pdHIiLCBxdWlldGx5ID0gVFJVRSkpIHsKICBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpCn0KCmxpYnJhcnkoR0VPcXVlcnkpCmxpYnJhcnkodGliYmxlKQpsaWJyYXJ5KHJlYWR4bCkKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5cikKbGlicmFyeShrbml0cikKbGlicmFyeShiaW9tYVJ0KQpsaWJyYXJ5KERFU2VxMikKbGlicmFyeShlZGdlUikKbGlicmFyeShTdW1tYXJpemVkRXhwZXJpbWVudCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpsaWJyYXJ5KGNpcmNsaXplKQpgYGAKClRoaXMgd29ya2Jvb2sgdXNlcyB0aGUgR0VPUXVlcnkgW0BEYXZpczIwMDddLCBiaW9tYVJ0IFtARHVyaW5jazIwMDVdLCB0aWJibGUgW0BNdWVsbGVyMjAyNV0sIHJlYWR4bFtAV2lja2hhbTIwMjVdLCBkcGx5ciBbQFdpY2toYW0yMDIzXSwgdGlkeXJbQFdpY2toYW0yMDI1YV0sIGtuaXRyIFtAWGllMjAxNF0sIERFU2VxMltATG92ZTIwMTRdLCBlZGdlUiBbQENoZW4yMDI1XSwgU3VtbWFyaXplZEV4cGVyaW1lbnRbQE1vcmdhbjIwMjVdLCBnZ3Bsb3QyIFtAV2lja2hhbTIwMTZdLCBDb21wbGV4SGVhdG1hcCBbQEd1MjAyMl0gYW5kIGNpcmNsaXplIFtAR3UyMDE0XSBwYWNrYWdlcyBpbiBpdHMgY29kZSBjaHVua3MuCgojIEludHJvZHVjdGlvbgoKU2VwdGljIHNob2NrIGlzIGEgcG90ZW50aWFsbHkgZmF0YWwgY29tcGxpY2F0aW9uIG9mIHNlcHNpcywgYW5kIHRyZWF0bWVudCBsYXJnZWx5IGRlcGVuZHMgb24gZWFybHkgc3VwcG9ydGl2ZSBoZW1vZHluYW1pYyB0aGVyYXB5LiBQYXRpZW50IHJlc3BvbnNlIHRvIGhlbW9keW5hbWljIHRoZXJhcHkgdmFyaWVzLCBhbmQgdGhlIE5DQkkgR2VuZSBFeHByZXNzaW9uIE9tbmlidXMgKEdFTykgW0BCYXJyZXR0MjAxMl0gc2VyaWVzIEdTRTExMDQ4NyBpcyBhIGRhdGFzZXQgZnJvbSAyMDE4IGNvbnNpc3Rpbmcgb2YgUk5BIHNlcXVlbmNpbmcgcmVhZCBjb3VudHMgb2Ygd2hvbGUgYmxvb2Qgc2FtcGxlcyB0YWtlbiBmcm9tIDE0IHNlcHRpYyBzaG9jayBwYXRpZW50cyB3aG8gcmVzcG9uZGVkIHRvIGhlbW9keW5hbWljIHRoZXJhcHkgYW5kIDE3IG5vbi1yZXNwb25kZXJzLCBhdCB0d28gdGltZSBwb2ludHM6IGltbWVkaWF0ZWx5IGFmdGVyIElDVSBhZG1pc3Npb24sIGFuZCA0OCBob3VycyBsYXRlciwgYWZ0ZXIgaGVtb2R5bmFtaWMgdGhlcmFweS4gW0BCYXJjZWxsYTIwMThdCgojIEZldGNoaW5nIGFuZCBhc3Nlc3NpbmcgZGF0YQoKIyMgRG93bmxvYWRpbmcgYW5kIGZvcm1hdHRpbmcKCldlJ2xsIHVzZSB0aGUgR0VPUXVlcnkgcGFja2FnZSB0byByZXRyaWV2ZSB0aGUgc3VwcGxlbWVudGFyeSBmaWxlcyBhbmQgbWV0YWRhdGEgcGVydGFpbmluZyB0byB0aGUgZXhwZXJpbWVudCBmcm9tIHRoZSBHRU8gd2l0aCBpdHMgc2VyaWVzIGFjY2Vzc2lvbiBudW1iZXIsIGFuZCB0aGVuIGxvb2sgYXQgaG93IGl0J3MgZm9ybWF0dGVkLgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30KZ2VvX2lkIDwtICJHU0UxMTA0ODciCgojIGdldCBtZXRhZGF0YSBhc3NvY2lhdGVkIHdpdGggZ3NlIGFuZCBzdXBwbGVtZW50YXJ5IGZpbGVzCmdzZSA8LSBHRU9xdWVyeTo6Z2V0R0VPKGdlb19pZCwgR1NFTWF0cml4PUZBTFNFKQpzdXBwIDwtIEdFT3F1ZXJ5OjpnZXRHRU9TdXBwRmlsZXMoZ2VvX2lkLCBmZXRjaF9maWxlcz1GQUxTRSkKCiMgZG93bmxvYWQgZGF0YSBpZiBub3QgYWxyZWFkeSBkb3dubG9hZGVkCmN1cnJlbnRfZGlyIDwtIGZpbGUucGF0aChnZXR3ZCgpKQpzdXBwX2ZpbGVfcGF0aCA8LSBmaWxlLnBhdGgoY3VycmVudF9kaXIsIGdlb19pZCwgc3VwcCRmbmFtZVsxXSkKaWYgKCFmaWxlLmV4aXN0cyhzdXBwX2ZpbGVfcGF0aCkpIHsKICBkYXRhX2ZpbGUgPC0gR0VPcXVlcnk6OmdldEdFT1N1cHBGaWxlcyhHRU8gPSBnZW9faWQpCn0KCiMgcmVhZCBkYXRhIGludG8gYSBkYXRhZnJhbWUKcmF3X2NvdW50cyA8LSByZWFkeGw6OnJlYWRfeGxzeChzdXBwX2ZpbGVfcGF0aCkKCiMgdGFrZSBhIHBlZWsKa25pdHI6OmthYmxlKHJhd19jb3VudHNbMTo1LCAxOjEwXSwgZm9ybWF0ID0gImh0bWwiLCBhbGlnbiA9ICJjIikKYGBgCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiMgY291bnQgZ2VuZXMgYW5kIHNhbXBsZXMKY2F0KHBhc3RlKCJOdW1iZXIgb2YgZ2VuZXM6ICIsIG5yb3cocmF3X2NvdW50cyksCiAgICAgICAgICAiXG5OdW1iZXIgb2Ygc2FtcGxlczogIiwgbmNvbChyYXdfY291bnRzKS0xKSkKYGBgCgpTYW1wbGVzIGluIHRoZSByYXcgZGF0YSBhcmUgbmFtZWQgYWNjb3JkaW5nIHRvIHBhdGllbnQgaWRlbnRpdHkgYW5kIHRpbWUgcG9pbnQsIGJ1dCBkb24ndCBjb250YWluIGFueSBpbmZvcm1hdGlvbiBvbiB3aGV0aGVyIHRoZSBwYXRpZW50IHdhcyBhIHJlc3BvbmRlciBvciBhIG5vbi1yZXNwb25kZXIsIHNvIHdlJ2xsIG1hcCBzYW1wbGUgaWRlbnRpZmllcnMgdG8gYm90aCByZXNwb25zZSBhbmQgdGltZSBwb2ludC4KCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KIyByZXRyaWV2ZSBsaXN0IG9mIHNhbXBsZXMgaW4gdGhlIHNlcmllcywgYW5kIGNvbXBpbGUgY29uZGl0aW9uIGRhdGEgZm9yIGVhY2gKc2FtcGxlcyA8LSBnc2VAZ3NtcwpzYW1wbGVfc291cmNlcyA8LSBkby5jYWxsKHJiaW5kLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXBwbHkoc2FtcGxlcywgRlVOPWZ1bmN0aW9uKHgpewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYyh4QGhlYWRlciR0aXRsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeEBoZWFkZXIkc291cmNlX25hbWVfY2gxKX0pKQoKIyBVc2UgdGhpcyBkYXRhIHRvIGNyZWF0ZSBhIGRhdGFmcmFtZSBtYXBwaW5nIHNhbXBsZSB0aXRsZXMgdG8gcmVzcG9uZGVyIHN0YXR1cyBhbmQgdGltZSBwb2ludApzYW1wbGVfbWV0YWRhdGEgPC0gYXMuZGF0YS5mcmFtZShzYW1wbGVfc291cmNlcywgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpjb2xuYW1lcyhzYW1wbGVfbWV0YWRhdGEpID0gYygic2FtcGxlIiwgInNvdXJjZSIpCnNhbXBsZV9tZXRhZGF0YSA8LSBzYW1wbGVfbWV0YWRhdGEgJT4lIAogIG1hZ3JpdHRyOjpzZXRfcm93bmFtZXMoLiRzYW1wbGUpICU+JSAKICBtdXRhdGUoCiAgICByZXNwb25zZSA9IGlmZWxzZShncmVwbCgiXlJlc3BvbmRlciIsIHNvdXJjZSksICJSIiwgIk5SIiksCiAgICB0aW1lX3BvaW50ID0gaWZlbHNlKGdyZXBsKCJUMSIsIHNvdXJjZSksICJUMSIsICJUMiIpLAogICkgJT4lIAogIGRwbHlyOjpzZWxlY3QocmVzcG9uc2UsIHRpbWVfcG9pbnQpCgojIHRha2UgYSBwZWVrCmtuaXRyOjprYWJsZShzYW1wbGVfbWV0YWRhdGFbMTo1LF0sIGZvcm1hdCA9ICJodG1sIiwgYWxpZ24gPSAiYyIpCmBgYAoKIyMgUXVhbGl0eSBhc3Nlc3NtZW50CgpOb3csIHdlJ2xsIGRldGVybWluZSB3aGV0aGVyIGRhdGEgcXVhbGl0eSB2YXJpZXMgYWNyb3NzIGNvbmRpdGlvbnMKCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KIyBjYWxjdWxhdGluZyBzdW1tYXJ5IHN0YXRpc3RpY3MgZm9yIGVhY2ggc2FtcGxlCnNhbXBsZV9zdW1tYXJpZXMgPC0gZGF0YS5mcmFtZShkby5jYWxsKGNiaW5kLCBsYXBwbHkocmF3X2NvdW50c1ssIDI6bmNvbChyYXdfY291bnRzKV0sIHN1bW1hcnkpKSkKcXVhbF9vdmVydmlldyA8LSBhcy5kYXRhLmZyYW1lKHQoc2FtcGxlX3N1bW1hcmllcykpCgojIEFkZGluZyBleHRyYSBzdGF0aXN0aWNzIGZvciB0b3RhbCByZWFkcywgbnVtYmVyIG9mIGdlbmVzIHdpdGggemVybyBleHByZXNzaW9uLCBudW1iZXIgb2YgZ2VuZXMgd2l0aCBsb3cgZXhwcmVzc2lvbgpxdWFsX292ZXJ2aWV3IDwtIHF1YWxfb3ZlcnZpZXcgJT4lIAogIGRwbHlyOjptdXRhdGUodG90YWxfcmVhZHMgPSBjb2xTdW1zKHJhd19jb3VudHNbMjpuY29sKHJhd19jb3VudHMpXSksCiAgICAgICAgICAgICAgICB6ZXJvX2V4cCA9IGNvbFN1bXMocmF3X2NvdW50c1syOm5jb2wocmF3X2NvdW50cyldID09IDApLAogICAgICAgICAgICAgICAgbG93X2V4cCA9IGNvbFN1bXMocmF3X2NvdW50c1syOm5jb2wocmF3X2NvdW50cyldPDEwKSwKICAgICAgICAgICAgICAgICkKCiMgYWRkaW5nIG1ldGFkYXRhIHRvIHNvcnQgYnkgY29uZGl0aW9ucwpxdWFsX292ZXJ2aWV3IDwtIG1lcmdlKHF1YWxfb3ZlcnZpZXcsIHNhbXBsZV9tZXRhZGF0YSwgYnkgPSAwKQoKIyBvdmVydmlldyBzdGF0cyBmb3Igc2FtcGxlcyBmcm9tIG5vbi1yZXNwb25kZXJzIGFuZCByZXNwb25kZXJzCnN0YXRzX2J5X3Jlc3BvbnNlIDwtIHF1YWxfb3ZlcnZpZXcgJT4lCiAgZHBseXI6Omdyb3VwX2J5KHJlc3BvbnNlLCkgJT4lCiAgc3VtbWFyaXplKAogICAgTnVtU2FtcGxlcyA9IG4oKSwKICAgIE1lYW5MaWJTaXplID0gbWVhbih0b3RhbF9yZWFkcyksCiAgICBTRExpYlNpemUgPSBzZCh0b3RhbF9yZWFkcyksCiAgICBQcm9wWmVyb0V4cCA9IG1lYW4oemVyb19leHApIC8gbnJvdyhyYXdfY291bnRzKSwKICAgIFByb3BMb3dFeHAgPSBtZWFuKGxvd19leHApIC8gbnJvdyhyYXdfY291bnRzKSwKICApCgojIG92ZXJ2aWV3IHN0YXRzIGZvciBzYW1wbGVzIGZyb20gdGltZSBwb2ludCAxIGFuZCB0aW1lIHBvaW50IDIKc3RhdHNfYnlfdGltZSA8LSBxdWFsX292ZXJ2aWV3ICU+JQogIGRwbHlyOjpncm91cF9ieSh0aW1lX3BvaW50LCkgJT4lCiAgc3VtbWFyaXplKAogICAgTnVtU2FtcGxlcyA9IG4oKSwKICAgIE1lYW5MaWJTaXplID0gbWVhbih0b3RhbF9yZWFkcyksCiAgICBTRExpYlNpemUgPSBzZCh0b3RhbF9yZWFkcyksCiAgICBQcm9wWmVyb0V4cCA9IG1lYW4oemVyb19leHApIC8gbnJvdyhyYXdfY291bnRzKSwKICAgIFByb3BMb3dFeHAgPSBtZWFuKGxvd19leHApIC8gbnJvdyhyYXdfY291bnRzKSwKICApCgojIG1lcmdpbmcgdGFibGVzIGFuZCBkaXNwbGF5aW5nCnN0YXRzX2J5X2NvbmRpdGlvbiA8LSBiaW5kX3Jvd3MoCiAgICBzdGF0c19ieV9yZXNwb25zZSAlPiUgCiAgICAgICAgZHBseXI6Om11dGF0ZShncm91cF90eXBlID0gInJlc3BvbnNlIikgJT4lCiAgICAgICAgZHBseXI6OnJlbmFtZShjb25kaXRpb24gPSByZXNwb25zZSksCiAgICBzdGF0c19ieV90aW1lICU+JSAKICAgICAgICBkcGx5cjo6bXV0YXRlKGdyb3VwX3R5cGUgPSAidGltZV9wb2ludCIpICU+JQogICAgICAgIGRwbHlyOjpyZW5hbWUoY29uZGl0aW9uID0gdGltZV9wb2ludCkKICAgICkKa25pdHI6OmthYmxlKHN0YXRzX2J5X2NvbmRpdGlvblssMTo2XSwgZm9ybWF0ID0gImh0bWwiLCBhbGlnbiA9ICJjIikKYGBgCgpNZWFuLCBzdGFuZGFyZCBkZXZpYXRpb24sIGFuZCBwcm9wb3J0aW9ucyBvZiB1bmV4cHJlc3NlZCBhbmQgbG93bHkgZXhwcmVzc2VkIGdlbmVzIGFyZSBzaW1pbGFyIGFjcm9zcyBwYXRpZW50IHJlc3BvbmRlciBzdGF0dXMgYW5kIHRpbWUsIHNvIHNwZWNpYWwgY29ycmVjdGlvbiBmb3IgZGF0YSBxdWFsaXR5IHdvbid0IGJlIG5lY2Vzc2FyeS4KCiMgSWRlbnRpZmllciBtYXBwaW5nIGFuZCBjbGVhbmluZwoKIyMgTWFwcGluZyB0byBIVUdPIHN5bWJvbHMKClRoZSBkYXRhIHVzZXMgRW5zZW1ibCBHZW5lIElEcyBiZWdpbm5pbmcgd2l0aCBFTlNHLgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30KIyBnZXQgZW5zZW1ibCB2ZXJzaW9uIDEwMCBhbmQgdGhlIGh1bWFuIGdlbmUgZGF0YXNldAplbnNlbWJsIDwtIGJpb21hUnQ6OnVzZUVuc2VtYmwoYmlvbWFydCA9ICJlbnNlbWJsIikKZW5zZW1ibCA8LSBiaW9tYVJ0Ojp1c2VEYXRhc2V0KCJoc2FwaWVuc19nZW5lX2Vuc2VtYmwiLCBtYXJ0ID0gZW5zZW1ibCkKYGBgCgpSYXRoZXIgdGhhbiBFbnNlbWJsIEdlbmUgSURzLCB3ZSB3YW50IHRvIHVzZSBIVUdPIEdlbmUgTm9tZW5jbGF0dXJlIENvbW1pdHRlZSAoSEdOQykgc3ltYm9scyB0byBpZGVudGlmeSB0aGUgZ2VuZXMgaW4gb3VyIGRhdGFzZXQuIFdlJ2xsIHVzZSB0aGUgRW5zZW1ibCBodW1hbiBkYXRhc2V0IHRvIG1hcCBhcyBtYW55IEVOU0cgSURzIGFzIHBvc3NpYmxlIHRvIEhHTkMgc3ltYm9scywgYW5kIGFkZCB0aG9zZSBtYXBwaW5ncyB0byBvdXIgcmVhZCBkYXRhLgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQojIHN0b3JlIGVuc2VtYmwgSURzIGZyb20gdGhlIGRhdGEgYXMgYSB2ZWN0b3IKZW5zZW1ibF9pZHMgPC0gcmF3X2NvdW50cyRHZW5laWQKCiMgY2hlY2sgaWYgYW4gZW5zZW1ibCB0byBoZ25jIGNvbnZlcnNpb24gZmlsZSBleGlzdHMgYWxyZWFkeQpjb252ZXJzaW9uX3N0YXNoIDwtICJlbnNlbWJsX3RvX2hnbmMucmRzIgppZiAoZmlsZS5leGlzdHMoY29udmVyc2lvbl9zdGFzaCkpIHsKICAKICAjIGlmIHRoZSBmaWxlIGV4aXN0cywgcmVhZCBjb252ZXJzaW9uIGRhdGEgZnJvbSB0aGF0CiAgZW5zZW1ibF90b19oZ25jIDwtIHJlYWRSRFMoY29udmVyc2lvbl9zdGFzaCkKfSBlbHNlIHsKICAKICAjIGlmIHRoZSBmaWxlIGRvZXMgbm90IGV4aXN0LCBnZXQgY29udmVyc2lvbiBkYXRhIGZyb20gYmlvbWFSdCBhbmQgY2FjaGUgaXQKICBlbnNlbWJsX3RvX2hnbmMgPC0gYmlvbWFSdDo6Z2V0Qk0oYXR0cmlidXRlcyA9IGMoImVuc2VtYmxfZ2VuZV9pZCIsICJoZ25jX3N5bWJvbCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVycyA9IGMoImVuc2VtYmxfZ2VuZV9pZCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gZW5zZW1ibF9pZHMsCiAgICAgICAgICAgICAgICAgICAgICAgICBtYXJ0ID0gZW5zZW1ibCkKICBzYXZlUkRTKGVuc2VtYmxfdG9faGduYywgY29udmVyc2lvbl9zdGFzaCkKfQoKIyBtZXJnZSBjb252ZXJzaW9uIGRmIHdpdGggZGF0YSBhbmQgcmVhcnJhbmdlIHN1Y2ggdGhhdCBib3RoIGlkIGNvbHVtbnMgY29tZSBiZWZvcmUgc2FtcGxlcwpyYXdfY291bnRzX2Fubm90IDwtIGRwbHlyOjpsZWZ0X2pvaW4ocmF3X2NvdW50cywgZW5zZW1ibF90b19oZ25jLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiR2VuZWlkIiA9ICJlbnNlbWJsX2dlbmVfaWQiKSkKcmF3X2NvdW50c19jb2xzIDwtIGRwbHlyOjpzZXRkaWZmKG5hbWVzKHJhd19jb3VudHMpLCAiR2VuZWlkIikKY29sbmFtZXMocmF3X2NvdW50c19hbm5vdClbMV0gPSAiZW5zZW1ibF9nZW5lX2lkIgpyYXdfY291bnRzX2Fubm90IDwtIHJhd19jb3VudHNfYW5ub3RbLCBjKCJlbnNlbWJsX2dlbmVfaWQiLCAiaGduY19zeW1ib2wiLCByYXdfY291bnRzX2NvbHMpXQoKIyBwZWVrIGF0IGFubm90YXRlZCBjb3VudHMKa25pdHI6OmthYmxlKHJhd19jb3VudHNfYW5ub3RbMTo1LCAxOjEwXSwgZm9ybWF0ID0gImh0bWwiLCBhbGlnbiA9ICJjIikKYGBgCgpOb3cgcm93cyBhcmUgaWRlbnRpZmllZCBieSBib3RoIEVOU0cgSUQgYW5kIEhHTkMgc3ltYm9sLgoKIyMgQ2xlYW5pbmcgb3V0IGxvdyBleHByZXNzaW9uIGFuZCB1bm1hcHBlZCByb3dzCgpBc3NheWluZyBmb3Igcm93cyB3aGVyZSBtYXBwaW5nIG9uZSBFTlNHIElEIHRvIG9uZSBIR05DIHN5bWJvbCBmYWlsZWQ6CgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiMgc3Vic2V0IHJvd3Mgd2hlcmUgb25lIGVuc2VtYmwgaWQgbWFwcyB0byBtdWx0aXBsZSBoZ25jIHN5bWJvbHMKZW5zZW1ibF9kdXBzIDwtIHJhd19jb3VudHNfYW5ub3QkZW5zZW1ibF9nZW5lX2lkW2R1cGxpY2F0ZWQocmF3X2NvdW50c19hbm5vdCRlbnNlbWJsX2dlbmVfaWQpXQptdWx0aXBsZV9oZ25jIDwtIHJhd19jb3VudHNfYW5ub3QgJT4lIAogIGZpbHRlcihlbnNlbWJsX2dlbmVfaWQgJWluJSBlbnNlbWJsX2R1cHMpCgojIHN1YnNldCByb3dzIHdoZXJlIG11bHRpcGxlIGVuc2VtYmwgaWRzIG1hcCB0byBvbmUgaGduYyBzeW1ib2wKaGduY19kdXBzIDwtIHJhd19jb3VudHNfYW5ub3QkaGduY19zeW1ib2xbZHVwbGljYXRlZChyYXdfY291bnRzX2Fubm90JGhnbmNfc3ltYm9sKV0Kc2FtZV9oZ25jIDwtIHJhd19jb3VudHNfYW5ub3QgJT4lIAogIGZpbHRlcihoZ25jX3N5bWJvbCAlaW4lIGhnbmNfZHVwcyAmIGhnbmNfc3ltYm9sICE9ICIiKQoKIyBzdWJzZXQgcm93cyB3aGVyZSBlbnNlbWJsIGlkIGRvZXMgbm90IG1hcCB0byBhIGhnbmMgc3ltYm9sIAp1bm1hcHBlZCA8LSByYXdfY291bnRzX2Fubm90ICU+JSAKICBmaWx0ZXIoaXMubmEoaGduY19zeW1ib2wpIHwgaGduY19zeW1ib2wgPT0gIiIpCgpjYXQocGFzdGUobnJvdyh1bm1hcHBlZCksICJ1bm1hcHBlZCByb3dzXG4iLAogICAgICAgICAgbnJvdyhtdWx0aXBsZV9oZ25jKSwgCiAgICAgICAgICAicm93cyB3aGVyZSBvbmUgRU5TRyBJRCBtYXBzIHRvIG11bHRpcGxlIEhHTkMgc3ltYm9scyBcbiIsCiAgICAgICAgICBucm93KHNhbWVfaGduYyksIAogICAgICAgICAgInJvd3Mgd2hlcmUgbXVsdGlwbGUgRU5TRyBJRHMgbWFwIHRvIHRoZSBzYW1lIEhHTkMgc3ltYm9sIikpCmBgYAoKQSBoaWdoIHByb3BvcnRpb24gb2YgdGhlIGRhdGFzZXQncyByb3dzIGRvIG5vdCBtYXAgdG8gYW55IEhHTkMgc3ltYm9sLiBUaGVzZSBhcmUgbGlrZWx5IHRvIGNvcnJlc3BvbmQgdG8gb3V0ZGF0ZWQgYWxpYXNlcyBvZiBjdXJyZW50IHN5bWJvbHMsIG9yIGRlcHJlY2F0ZWQgT1JGcy4gQnkgZmlsdGVyaW5nIGxvdyBleHByZXNzaW9uIGdlbmVzIGZyb20gdGhlIGRhdGEgYW5kIGNvbXBhcmluZyB0aGUgcHJvcG9ydGlvbnMgb2YgbWFwcGVkIGFuZCB1bm1hcHBlZCBnZW5lcyB0aGF0IHJlbWFpbiwgd2UgY2FuIGFzc2VzcyB0aGUgc2lnbmlmaWNhbmNlIG9mIHRoZSB1bm1hcHBlZCByb3dzLgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30KIyBzaXplIG9mIHNtYWxsZXN0IGNvbmRpdGlvbiBncm91cCBpbiB0aGUgZGF0YXNldDogbm9uLXJlc3BvbmRlcnMgYXQgdGltZSBwb2ludCAxLzIKc21hbGxlc3RfZ3JvdXBfc2l6ZSA8LSAxMQoKIyBrZWVwIG9ubHkgdW5tYXBwZWQgcm93cyB0aGF0IGhhdmUgYSBjb3VudCBvZiBhdCBsZWFzdCAxMCBpbiAxMSBzYW1wbGVzCnJvd3NfdG9fa2VlcCA8LSByb3dTdW1zKHVubWFwcGVkID49IDEwKSA+PSBzbWFsbGVzdF9ncm91cF9zaXplCmNsZWFuX3VubWFwcGVkIDwtIHVubWFwcGVkW3Jvd3NfdG9fa2VlcCxdCgojIHN1YnNldCBtYXBwZWQgY291bnRzIAptYXBwZWQgPC0gcmF3X2NvdW50c19hbm5vdCAlPiUgCiAgZmlsdGVyKCFpcy5uYShoZ25jX3N5bWJvbCkgJiBoZ25jX3N5bWJvbCAhPSAiIikKCiMgYXBwbHkgc2FtZSBleHByZXNzaW9uIGZpbHRlciB0byBtYXBwZWQgY291bnRzIApyb3dzX3RvX2tlZXAgPC0gcm93U3VtcyhtYXBwZWQgPj0gMTApID49IHNtYWxsZXN0X2dyb3VwX3NpemUKY2xlYW5fbWFwcGVkIDwtIG1hcHBlZFtyb3dzX3RvX2tlZXAsXQoKIyBjb2xsYXRlIGFuZCBkaXNwbGF5IHJlc3VsdHMgb2YgZmlsdGVyaW5nCmNsZWFuaW5nX3Jlc3VsdHMgPC0gZGF0YS5mcmFtZSgKICBUeXBlID0gYygibWFwcGVkIGdlbmVzIiwgInVubWFwcGVkIGdlbmVzIiksCiAgUHJlQ2xlYW5Db3VudCA9IGMobnJvdyhtYXBwZWQpLCBucm93KHVubWFwcGVkKSksCiAgUG9zdENsZWFuQ291bnQgPSBjKG5yb3coY2xlYW5fbWFwcGVkKSwgbnJvdyhjbGVhbl91bm1hcHBlZCkpLAogIFNpZ25pZmljYW50UHJvcG9ydGlvbiA9IGMobnJvdyhjbGVhbl9tYXBwZWQpL25yb3cobWFwcGVkKSwgCiAgICAgICAgICAgICAgICAgICAgIG5yb3coY2xlYW5fdW5tYXBwZWQpL25yb3codW5tYXBwZWQpKQopCmtuaXRyOjprYWJsZShjbGVhbmluZ19yZXN1bHRzLCBmb3JtYXQgPSAiaHRtbCIsIGFsaWduID0gImMiKQpgYGAKCkxlc3MgdGhhbiBhIHRlbnRoIG9mIHRoZSB1bm1hcHBlZCByb3dzIGhhZCBzdWZmaWNpZW50IGxldmVscyBvZiBleHByZXNzaW9uIHRvIG5vdCBiZSBmaWx0ZXJlZCBpbiBlYXJseSBkYXRhIGNsZWFuaW5nLCBjb21wYXJlZCB0byBtb3JlIHRoYW4gYSB0aGlyZCBvZiB0aGUgbWFwcGVkIHJvd3MuCgpTaW5jZSB1bm1hcHBlZCByb3dzIGFyZSBsYXJnZWx5IGxvd2x5IGV4cHJlc3NlZCwgd2UgY2FuIGRpc2NhcmQgdGhlbSB3aXRob3V0IGxvc2luZyBhIGdyZWF0IGFtb3VudCBvZiBpbmZvcm1hdGlvbi4gRU5TRyBJRHMgbWFwcGluZyB0byBtdWx0aXBsZSBIR05DIHN5bWJvbHMgbWF5IGJlIHRyYW5zY3JpcHRzIHRoYXQgaHlicmlkaXplIHRvIG1vcmUgdGhhbiBvbmUgZ2VuZSwgYW5kIHNpbmNlIHRoZXkgYXJlIGluIHN1Y2ggc21hbGwgcHJvcG9ydGlvbiBpbiB0aGUgZGF0YXNldCwgd2Ugd2lsbCB0cmVhdCB0aGVtIGFzIG11bHRpcGxlIGdlbmVzIHdpdGhvdXQgc2lnbmlmaWNhbnRseSBpbXBhY3RpbmcgYW5hbHlzaXMuIEhHTkMgc3ltYm9scyBtYXBwZWQgdG8gYnkgbXVsdGlwbGUgRU5TRyBJRHMgbWF5IGJlIGdlbmVzIHdpdGggbXVsdGlwbGUgc3BsaWNlIHZhcmlhbnRzLiBTaW5jZSB0aGV5IGFyZSBsaWtld2lzZSBpbiBtaW51c2N1bGUgcHJvcG9ydGlvbiBpbiB0aGUgZGF0YXNldCwgd2Ugd2lsbCBzdW0gdG9nZXRoZXIgcm93cyB0aGF0IG1hcCB0byB0aGUgc2FtZSBIVUdPIGdlbmUuCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KIyBjcmVhdGUgYSBuZXcgZGF0YWZyYW1lIHdoZXJlIHJvd3MgY29ycmVzcG9uZCB0byB1bmlxdWUgaGduYyBzeW1ib2xzCmNsZWFuX2NvdW50c19ieWhnbmMgPC0gY2xlYW5fbWFwcGVkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoaGduY19zeW1ib2wpICU+JSAKICAjIHN1bSByb3dzIHRoYXQgbWFwIHRvIHRoZSBzYW1lIGhnbmMgc3ltYm9sCiAgZHBseXI6OnN1bW1hcmlzZShkcGx5cjo6YWNyb3NzKGRwbHlyOjp3aGVyZShpcy5udW1lcmljKSwgfiBzdW0oLngsIG5hLnJtID0gVFJVRSkpKQoKa25pdHI6OmthYmxlKGNsZWFuX2NvdW50c19ieWhnbmNbMTo1LCAxOjEwXSwgZm9ybWF0ID0gImh0bWwiLCBhbGlnbiA9ICJjIikKYGBgCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CmNhdChwYXN0ZSgiTnVtYmVyIG9mIGdlbmVzOiAiLCBucm93KGNsZWFuX2NvdW50c19ieWhnbmMpLAogICAgICAgICAgIlxuTnVtYmVyIG9mIHNhbXBsZXM6ICIsIG5jb2woY2xlYW5fY291bnRzX2J5aGduYyktMSkpCmBgYAoKV2Ugbm93IGhhdmUgYSBkYXRhZnJhbWUgb2YgcmVhZCBjb3VudHMgZm9yIGdlbmVzIHdpdGggZXhwcmVzc2lvbiBsZXZlbHMgYWJvdmUgYSBiYXNlbGluZSwgaWRlbnRpZmllZCBieSB1bmlxdWUgSFVHTyBzeW1ib2xzLgoKIyBOb3JtYWxpemF0aW9uCgojIyBDb252ZXJ0aW5nIGRhdGEgZm9yIERFU2VxMgoKTGV0J3MgY3JlYXRlIGEgREVTZXFEYXRhU2V0IG9iamVjdCBmcm9tIG91ciBkYXRhLCBhbmQgY29udGludWUgb3VyIGFuYWx5c2lzIHdpdGggdGhlIERFU2VxMiBwYWNrYWdlLCBhZGFwdGluZyBtZXRob2RzIGZyb20gTG92ZSBldCBhbCdzIFJOQVNlcSB3b3JrZmxvdyBbQExvdmUyMDE1XS4KCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgY2xhc3Muc291cmNlID0gJ2ZvbGQtc2hvdyd9CiMgZXh0cmFjdCBjb3VudHMgYXMgYSBtYXRyaXggYW5kIHNldCByb3cgYW5kIGNvbHVtbiBuYW1lcwpjbGVhbl9jb3VudF9tYXRyaXggPC0gYXMubWF0cml4KGNsZWFuX2NvdW50c19ieWhnbmNbLDI6bmNvbChjbGVhbl9jb3VudHNfYnloZ25jKV0pCnJvd25hbWVzKGNsZWFuX2NvdW50X21hdHJpeCkgPC0gY2xlYW5fY291bnRzX2J5aGduYyRoZ25jX3N5bWJvbApjb2xuYW1lcyhjbGVhbl9jb3VudF9tYXRyaXgpIDwtIGNvbG5hbWVzKGNsZWFuX2NvdW50c19ieWhnbmMpWzI6bmNvbChjbGVhbl9jb3VudHNfYnloZ25jKV0KCiNjcmVhdGUgYSB2YXJpYWJsZSBpbiB0aGUgbWV0YWRhdGEgZnJvbSB0aGUgaW50ZXJhY3Rpb24gb2YgcmVzcG9uc2UgYW5kIHRpbWVfcG9pbnQKc2FtcGxlX21ldGFkYXRhJGdyb3VwID0gaW50ZXJhY3Rpb24oc2FtcGxlX21ldGFkYXRhJHJlc3BvbnNlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVfbWV0YWRhdGEkdGltZV9wb2ludCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIl8iKQoKIyBjcmVhdGUgYSBERVNlcURhdGFTZXQgb2JqZWN0IGZyb20gdGhlIGNvdW50cyBtYXRyaXggYW5kIHRoZSBzYW1wbGUgbWV0YWRhdGEsIHVzaW5nIHRoZSBpbnRlcmFjdGlvbiBvZiByZXNwb25zZSBhbmQgdGltZSBwb2ludCBhcyB0aGUgZGVzaWduLiAKY291bnRzX2RkcyA8LSBERVNlcTI6OkRFU2VxRGF0YVNldEZyb21NYXRyaXgoY291bnREYXRhID0gY2xlYW5fY291bnRfbWF0cml4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gc2FtcGxlX21ldGFkYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSB+Z3JvdXApCmBgYAoKIyMgTm9ybWFsaXppbmcgY291bnRzCgpXZSdsbCBpbXBsZW1lbnQgcmVsYXRpdmUgbG9nIGV4cHJlc3Npb24gKFJMRSkgbm9ybWFsaXphdGlvbiBvbiB0aGUgY291bnRzIHVzaW5nIHRoZSBERVNlcTIgcGFja2FnZS4gUkxFIG5vcm1hbGl6YXRpb24gaXMgYmFzZWQgb24gdGhlIGh5cG90aGVzaXMgdGhhdCBtb3N0IGdlbmVzIGFyZSBub3QgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkLCB3aGljaCBpcyBzdWl0YWJsZSBmb3IgdGhpcyBkYXRhc2V0LCBhcyB0aGUgYXNzb2NpYXRlZCBwdWJsaWNhdGlvbiBpZGVudGlmaWVzIGEgbG93IHByb3BvcnRpb24gb2YgREUgZ2VuZXMuCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLXNob3cnfQoKIyBlc3RpbWF0ZSBub3JtYWxpemF0aW9uIGZhY3RvcnMgYW5kIGFwcGx5IHRoZW0gdG8gY291bnRzCmNvdW50c19kZHMgPC0gREVTZXEyOjplc3RpbWF0ZVNpemVGYWN0b3JzKGNvdW50c19kZHMpCm5vcm1fY291bnRzIDwtIERFU2VxMjo6Y291bnRzKGNvdW50c19kZHMsIG5vcm1hbGl6ZWQgPSBUUlVFKQpgYGAKCiMjIFBsb3R0aW5nIHNhbXBsZSBjbHVzdGVyaW5nCgpJbiBvcmRlciB0byB2aXN1YWxpemUgdGhlIGVmZmVjdHMgb2Ygbm9ybWFsaXphdGlvbiwgd2UnbGwgZ2VuZXJhdGUgTXVsdGktRGltZW5zaW9uYWwgU2NhbGluZyAoTURTKSBwbG90cyBmcm9tIHRoZSB1bi1ub3JtYWxpemVkIGFuZCBub3JtYWxpemVkIGRhdGEuCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiMgY3JlYXRlIFN1bW1hcml6ZWRFeHBlcmltZW50IG9iamVjdHMgd2l0aCB1bi1ub3JtYWxpemVkIGFuZCBub3JtYWxpemVkIGNvdW50cwp1bm5vcm1fY291bnRzIDwtIERFU2VxMjo6Y291bnRzKGNvdW50c19kZHMsIG5vcm1hbGl6ZWQgPSBGQUxTRSkKdW5ub3JtX2NvdW50c19zZSA8LSBTdW1tYXJpemVkRXhwZXJpbWVudChhc3NheXMgPSBsaXN0KGNvdW50cyA9IHVubm9ybV9jb3VudHMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbERhdGEgPSBzYW1wbGVfbWV0YWRhdGEpCm5vcm1fY291bnRzX3NlIDwtIFN1bW1hcml6ZWRFeHBlcmltZW50KGFzc2F5cyA9IGxpc3QoY291bnRzID0gbm9ybV9jb3VudHMpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSA9IHNhbXBsZV9tZXRhZGF0YSkKCiMgZ2VuZXJhdGUgTURTIGRhdGEgd2l0aCB1bi1ub3JtYWxpemVkIGNvdW50cwp1bm5vcm1fbWRzIDwtIGVkZ2VSOjpwbG90TURTLlN1bW1hcml6ZWRFeHBlcmltZW50KHVubm9ybV9jb3VudHNfc2UsIHBsb3QgPSBGQUxTRSkKdW5ub3JtX21kc19kZiA8LSBkYXRhLmZyYW1lKAogIERpbTEgPSB1bm5vcm1fbWRzJHgsCiAgRGltMiA9IHVubm9ybV9tZHMkeSwKICBHcm91cCA9IHVubm9ybV9jb3VudHNfc2UkZ3JvdXAsCiAgU2FtcGxlID0gY29sbmFtZXModW5ub3JtX2NvdW50c19zZSkKKQoKIyBnZW5lcmF0ZSBNRFMgZGF0YSB3aXRoIG5vcm1hbGl6ZWQgY291bnRzCm5vcm1fbWRzIDwtIGVkZ2VSOjpwbG90TURTLlN1bW1hcml6ZWRFeHBlcmltZW50KG5vcm1fY291bnRzX3NlLCBwbG90ID0gRkFMU0UpCm5vcm1fbWRzX2RmIDwtIGRhdGEuZnJhbWUoCiAgRGltMSA9IG5vcm1fbWRzJHgsCiAgRGltMiA9IG5vcm1fbWRzJHksCiAgR3JvdXAgPSBub3JtX2NvdW50c19zZSRncm91cCwKICBTYW1wbGUgPSBjb2xuYW1lcyhub3JtX2NvdW50c19zZSkKKQoKIyBwbG90IHVuLW5vcm1hbGl6ZWQgTURTICAKZ2dwbG90Mjo6Z2dwbG90KHVubm9ybV9tZHNfZGYsIGFlcyh4ID0gRGltMSwgeSA9IERpbTIsIGNvbG9yID0gR3JvdXApKSArCiAgZ2VvbV9wb2ludChzaXplID0gMykgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicygKICAgIHggPSBwYXN0ZSgiRGltMSAoIiwgcm91bmQodW5ub3JtX21kcyR2YXIuZXhwbGFpbmVkWzFdKjEwMCwgMSksICIlKSIpLAogICAgeSA9IHBhc3RlKCJEaW0yICgiLCByb3VuZCh1bm5vcm1fbWRzJHZhci5leHBsYWluZWRbMl0qMTAwLCAxKSwgIiUpIiksCiAgICB0aXRsZSA9ICJNRFMgcGxvdDogdW4tbm9ybWFsaXplZCBjb3VudHMiCiAgKQpgYGAKCjxjZW50ZXI+CgoqKkZpZ3VyZSAxOioqIE1EUyBwbG90IGdlbmVyYXRlZCBmcm9tIGZpbHRlcmVkIFJOQSBzZXF1ZW5jaW5nIHJlYWQgY291bnRzIG9mIHdob2xlIGJsb29kIHNhbXBsZXMgZnJvbSBzZXB0aWMgc2hvY2sgcGF0aWVudHMgd2hvIHdlcmUgcmVzcG9uc2l2ZSAoUikgYW5kIG5vbi1yZXNwb25zaXZlIChOUikgdG8gaGVtb2R5bmFtaWMgdGhlcmFweSwgdGFrZW4gYXQgdGltZSBwb2ludHMgYmVmb3JlIGFuZCBhZnRlciB0cmVhdG1lbnQgKFQxIGFuZCBUMiksIHdpdGhvdXQgbm9ybWFsaXphdGlvbi4KCjwvY2VudGVyPgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQojIHBsb3Qgbm9ybWFsaXplZCBNRFMKZ2dwbG90Mjo6Z2dwbG90KG5vcm1fbWRzX2RmLCBhZXMoeCA9IERpbTEsIHkgPSBEaW0yLCBjb2xvciA9IEdyb3VwKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnMoCiAgICB4ID0gcGFzdGUoIkRpbTEgKCIsIHJvdW5kKG5vcm1fbWRzJHZhci5leHBsYWluZWRbMV0qMTAwLCAxKSwgIiUpIiksCiAgICB5ID0gcGFzdGUoIkRpbTIgKCIsIHJvdW5kKG5vcm1fbWRzJHZhci5leHBsYWluZWRbMl0qMTAwLCAxKSwgIiUpIiksCiAgICB0aXRsZSA9ICJNRFMgcGxvdDogbm9ybWFsaXplZCBjb3VudHMiCiAgKQpgYGAKCjxjZW50ZXI+CgoqKkZpZ3VyZSAyOioqIE1EUyBwbG90IGdlbmVyYXRlZCBmcm9tIGZpbHRlcmVkIFJOQSBzZXF1ZW5jaW5nIHJlYWQgY291bnRzIG9mIHdob2xlIGJsb29kIHNhbXBsZXMgZnJvbSBzZXB0aWMgc2hvY2sgcGF0aWVudHMgd2hvIHdlcmUgcmVzcG9uc2l2ZSAoUikgYW5kIG5vbi1yZXNwb25zaXZlIChOUikgdG8gaGVtb2R5bmFtaWMgdGhlcmFweSwgdGFrZW4gYXQgdGltZSBwb2ludHMgYmVmb3JlIGFuZCBhZnRlciB0cmVhdG1lbnQgKFQxIGFuZCBUMiksIG5vcm1hbGl6ZWQgYnkgcmVsYXRpdmUgbG9nIGV4cHJlc3Npb24uCgo8L2NlbnRlcj4KClRoZSBNRFMgcGxvdHMgZ2VuZXJhdGVkIGZyb20gdGhlIGRhdGEgcHJlIGFuZCBwb3N0IG5vcm1hbGl6YXRpb24gYXJlIG5lYXJseSBpZGVudGljYWwuIFRoZSBsb3cgaW1wYWN0IG9mIG5vcm1hbGl6YXRpb24gaW5kaWNhdGVzIGxpdHRsZSB0ZWNobmljYWwgYmlhcyBpbiB0aGUgZGF0YXNldCwgYW5kIHN1Z2dlc3RzIHRoYXQgbXNvdCB2YXJpYXRpb24gaXMgYmlvbG9naWNhbCAKCiMgRGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbgoKIyMgRXZhbHVhdGluZyBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbgoKRnJvbSBGaWcuIDEgYW5kIEZpZy4gMiwgc29tZSBncm91cGluZyBpcyB2aXNpYmxlIG9mIHNhbXBsZXMgd2l0aGluIHRoZSBzYW1lIHNldCBvZiBjb25kaXRpb25zLiBTYW1wbGVzIHRha2VuIGF0IHRoZSBzYW1lIHRpbWUgcG9pbnQgZnJvbSBwYXRpZW50cyB3aXRoIHRoZSBzYW1lIHJlc3BvbnNlIGJlaGF2aW91ciBsb29zZWx5IGNsdXN0ZXIgdG9nZXRoZXIuIEFzIHN1Y2gsIHdlJ2xsIHVzZSB0aGUgZm91ciBjb21iaW5hdGlvbnMgb2YgcmVzcG9uc2UgYW5kIHRpbWUgcG9pbnQgYXMgZmFjdG9ycyB0byBldmFsdWF0ZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbi4KYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30KIyBldmFsdWF0ZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbi4gQXBwcm9wcmlhdGUgZGVzaWduIGFscmVhZHkgc3BlY2lmaWVkIGluIGNvdW50c19kZHMKY291bnRzX2RkcyA8LSBERVNlcTI6OkRFU2VxKGNvdW50c19kZHMpCmBgYApXZSdsbCBsb29rIGZvciBnZW5lcyB0aGF0IGFyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgYmFzZWQgb24gcGF0aWVudCByZXNwb25zZSBhdCBlYWNoIG9mIHRoZSB0d28gdGltZSBwb2ludHMsIGFuZCBsb29rIGZvciBnZW5lcyB0aGF0IGFyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgb3ZlciB0aW1lIHdpdGhpbiBlYWNoIG9mIHRoZSB0d28gcGF0aWVudCByZXNwb25zZSBncm91cHMuIApgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIHJlc3VsdHMgPSAnaGlkZScsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLXNob3cnfQojIERFIGJldHdlZW4gUiBhbmQgTlIgYXQgVDEKcmVzcG9uc2VfVDEgPC0gREVTZXEyOjpyZXN1bHRzKGNvdW50c19kZHMsIGNvbnRyYXN0ID0gYygiZ3JvdXAiLCAiTlJfVDEiLCAiUl9UMSIpLCBhbHBoYSA9IDAuMDUpCnN1bW1hcnkocmVzcG9uc2VfVDEpCgojIERFIGJldHdlZW4gUiBhbmQgTlIgYXQgVDIgCnJlc3BvbnNlX1QyIDwtIERFU2VxMjo6cmVzdWx0cyhjb3VudHNfZGRzLCBjb250cmFzdCA9IGMoImdyb3VwIiwgIk5SX1QyIiwgIlJfVDIiKSwgYWxwaGEgPSAwLjA1KQpzdW1tYXJ5KHJlc3BvbnNlX1QyKQoKIyBERSBiZXR3ZWVuIFQxIGFuZCBUMiBmb3IgTlIgcGF0aWVudHMKdGltZV9OUiA8LSBERVNlcTI6OnJlc3VsdHMoY291bnRzX2RkcywgY29udHJhc3QgPSBjKCJncm91cCIsICJOUl9UMSIsICJOUl9UMiIpLCBhbHBoYSA9MC4wNSkKc3VtbWFyeSh0aW1lX05SKQoKIyBERSBiZXR3ZWVuIFQxIGFuZCBUMiBmb3IgUiBwYXRpZW50cwp0aW1lX1IgPC0gREVTZXEyOjpyZXN1bHRzKGNvdW50c19kZHMsIGNvbnRyYXN0ID0gYygiZ3JvdXAiLCAiUl9UMSIsICJSX1QyIiksIGFscGhhID0gMC4wNSkKc3VtbWFyeSh0aW1lX1IpCmBgYApgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiMgZ2V0IGdlbmVzIHdpdGggYWRqdXN0ZWQgcC12YWx1ZSA8IDAuMDUgZm9yIGVhY2ggdGVzdApzaWdfVDEgPC0gcmVzcG9uc2VfVDFbcmVzcG9uc2VfVDEkcGFkaiA8IDAuMDUsXQpzaWdfVDIgPC0gcmVzcG9uc2VfVDJbcmVzcG9uc2VfVDIkcGFkaiA8IDAuMDUsXQpzaWdfTlIgPC0gdGltZV9OUlt0aW1lX05SJHBhZGogPCAwLjA1LF0Kc2lnX1IgPC0gdGltZV9SW3RpbWVfUiRwYWRqIDwgMC4wNSxdCgpjYXQocGFzdGUobnJvdyhzaWdfVDEpLCAiREUgZ2VuZXMgYmV0d2VlbiBSIGFuZCBOUiBhdCBUMVxuIiwKICAgICAgICAgIG5yb3coc2lnX1QyKSwgIkRFIGdlbmVzIGJldHdlZW4gUiBhbmQgTlIgYXQgVDJcbiIsCiAgICAgICAgICBucm93KHNpZ19OUiksICJERSBnZW5lcyBmcm9tIFQxIHRvIFQyIGluIE5SXG4iLAogICAgICAgICAgbnJvdyhzaWdfUiksICJERSBnZW5lcyBmcm9tIFQxIHRvIFQyIGluIFIiKSkKYGBgCgoKREVTZXEyIGltcGxlbWVudHMgbXVsdGlwbGUgaHlwb3RoZXNpcyB0ZXN0aW5nIHZpYSBCZW5qYW1pbmktSG9jaGJlcmcgZmFsc2UgZGlzY292ZXJ5IHJhdGUgY29ycmVjdGlvbiBvbiBwLXZhbHVlcywgcHJvZHVjaW5nIGFkanVzdGVkIHAtdmFsdWVzIHRoYXQgbW9yZSBhY2N1cmF0ZWx5IHJlZmxlY3Qgc2lnbmlmaWNhbmNlIGluIGJ1bGsgUk5BLXNlcXVlbmNpbmcgZGF0YSB0aGFuIHRoZSBvcmlnaW5hbCBwLXZhbHVlcyBnaXZlbiBieSB0aGUgV2FsZCB0ZXN0IGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbi4gClRoZSB0aHJlc2hvbGQgd2UndmUgdXNlZCBmb3IgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIGlzIGdlbmVzIHdpdGggYWRqdXN0ZWQgcC12YWx1ZSA8IDAuMDUsIGEgY29tbW9ubHkgdXNlZCB0aHJlc2hvbGQgW2NpdGUgY2l0ZV0uIAoKSW4gdGhpcyBhbmFseXNpcywgd2UnbGwgd29yayB3aXRoIHRoZSBnZW5lcyB0aGF0IHdlcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4gdGhlIFIgYW5kIE5SIGdyb3VwcyBhdCBUMi4gU2luY2Ugbm8gZ2VuZXMgYXJlIHNpZ25pZmljYW50bHkgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4gdGhlIFIgYW5kIE5SIGdyb3VwcyBhdCBUMSwgYW5hbHl6aW5nIHRoZSBnZW5lcyB0aGF0IGFyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgYmV0d2VlbiB0aGUgZ3JvdXBzIGF0IFQyIHdpbGwgdGVsbCB1cyB3aGljaCBnZW5lcyB3ZXJlIHJlZ3VsYXRlZCBkaWZmZXJlbnRseSBiZXR3ZWVuIHJlc3BvbmRlciBhbmQgbm9uLXJlc3BvbmRlciBwYXRpZW50cyBhcyBhIHJlc3VsdCBvZiB0aGUgc2FtZSBjb25kaXRpb25zLCB5aWVsZGluZyBpbmZvcm1hdGlvbiBvbiB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGUgdHdvIHBhdGllbnQgZ3JvdXAncyBiaW9sb2dpY2FsIHJlc3BvbnNlcyB0byB0aGUgdGhlcmFweS4KCiMgVmlzdWFsaXppbmcgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzCgpXZSdsbCBjcmVhdGUgYSB2b2xjYW5vIHBsb3Qgb2YgdGhlIGdlbmVzIHVzaW5nIHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBzdGF0aXN0aWNzIGNhbGN1bGF0ZWQgYmV0d2VlbiB0aGUgUiBhbmQgTlIgZ3JvdXBzIGF0IFQyLgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLXNob3cnfQojIGNvbnZlcnQgREUgcmVzdWx0cyB0byBkYXRhIGZyYW1lCmRpZmV4cF9pbmZvIDwtIGFzLmRhdGEuZnJhbWUocmVzcG9uc2VfVDIpCgojIGFkZCBjb2x1bW4gaW5kaWNhdGluZyB3aGV0aGVyIGdlbmVzIGFyZSBzaWduaWZpY2FudGx5IHVwIG9yIGRvd25yZWd1bGF0ZWQKZGlmZXhwX2luZm8kREUgPC0gIm5vIgpkaWZleHBfaW5mbyRERVtkaWZleHBfaW5mbyRsb2cyRm9sZENoYW5nZSA+IDAgJiBkaWZleHBfaW5mbyRwYWRqIDwgMC4wNV0gPC0gInVwIgpkaWZleHBfaW5mbyRERVtkaWZleHBfaW5mbyRsb2cyRm9sZENoYW5nZSA8IDAgJiBkaWZleHBfaW5mbyRwYWRqIDwgMC4wNV0gPC0gImRvd24iCgojIGdlbmVyYXRlIHZvbGNhbm8gcGxvdCwgLWxvZzEwIG9mIGFkanVzdGVkIHAtdmFsIHZzIGxvZzIgb2YgRkMKZ2dwbG90Mjo6Z2dwbG90KGRhdGEgPSBkaWZleHBfaW5mbywgbWFwcGluZyA9IGFlcyh4ID0gbG9nMkZvbGRDaGFuZ2UsIHkgPSAtbG9nMTAocGFkaiksIGNvbCA9IERFKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMCgwLjA1KSwgY29sID0gImJsYWNrIiwgbGluZXR5cGUgPSAnZGFzaGVkJykgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiYmx1ZSIsICJncmV5IiwgInJlZCIpLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJEb3ducmVndWxhdGVkIGluIFIgZ3JvdXAiLCAiTm90IERFIiwgIlVwcmVndWxhdGVkIGluIFIgZ3JvdXAiKSkgKwogIGxhYnMoY29sb3IgPSAiRXhwcmVzc2lvbiIsIAogICAgICAgeCA9ICJsb2cgMiBmb2xkIGNoYW5nZSIsCiAgICAgICB5ID0gImxvZyAxMCBjb3JyZWN0ZWQgcC12YWx1ZSIsCiAgICAgICB0aXRsZSA9ICJFeHByZXNzaW9uIG9mIGdlbmVzIGluIFIgdnMuIE5SIHNhbXBsZXMgYXQgVDIiKQoKYGBgCjxjZW50ZXI+CgoqKkZpZ3VyZSAzOioqIEZEUi1jb3JyZWN0ZWQgcC12YWx1ZSAoLWxvZzEwKSB2cy4gZm9sZCBjaGFuZ2UgKGxvZzIpIGluIGV4cHJlc3Npb24gb2YgZ2VuZXMgaW4gd2hvbGUgYmxvb2Qgc2FtcGxlcyBmcm9tIHNlcHRpYyBzaG9jayBwYXRpZW50cyB3aG8gcmVzcG9uZGVkIHRvIGVhcmx5IHN1cHBvcnRpdmUgaGVtb2R5bmFtaWMgdGhlcmFweSAoUiBzYW1wbGVzKSBhbmQgcGF0aWVudHMgd2hvIGRpZCBub3QgcmVzcG9uZCAoTlIpLCB0YWtlbiA0OCBob3VycyBhZnRlciB0cmVhdG1lbnQuIFRocmVzaG9sZCBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gd2FzIGNvcnJlY3RlZCBwLXZhbHVlIDwgMC4wNS4KCjwvY2VudGVyPgoKTmV4dCwgd2UnbGwgY3JlYXRlIGEgaGVhdG1hcCBvZiB0aGUgZ2VuZXMgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4gdGhlIFIgYW5kIE5SIGdyb3VwcyBhdCBUMiwgYW5kIHNlZSBob3cgdGhleSBjbHVzdGVyIGFjY29yZGluZyB0byBzYW1wbGUgY29uZGl0aW9ucy4KYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30KIyB1c2Ugbm9ybWFsaXplZCBjb3VudHMgZm9yIGhlYXRtYXAgbWF0cml4LCBmaWx0ZXIgZm9yIERFIGdlbmVzCm5vcm1jb3VudHNfaGVhdG1hcCA8LSBub3JtX2NvdW50c1tyb3duYW1lcyhub3JtX2NvdW50cykgJWluJSByb3duYW1lcyhzaWdfVDIpLF0KCiMgbm9ybWFsaXplIHJvd3MKbm9ybWNvdW50c19oZWF0bWFwIDwtIHQoc2NhbGUodChub3JtY291bnRzX2hlYXRtYXApKSkKCiMgc2V0IGNvbG91cnMgdG8gc2NhbGUgZnJvbSBibHVlIGF0IG1pbiB2YWx1ZXMgdG8gd2hpdGUgYXQgMCB0byByZWQgYXQgbWF4IHZhbHVlcwpoZWF0bWFwX2NvbCA9IGNpcmNsaXplOjpjb2xvclJhbXAyKGMobWluKG5vcm1jb3VudHNfaGVhdG1hcCksIDAsIG1heChub3JtY291bnRzX2hlYXRtYXApKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKQoKIyBzZXQgY29sb3VycyB0byBjb3JyZXNwb25kIHRvIGV4cGVyaW1lbnRhbCBjb25kaXRpb25zCmdyb3VwX2NvbG91cnMgPC0gcmFpbmJvdyhuID0gNCkKbmFtZXMoZ3JvdXBfY29sb3VycykgPC0gdW5pcXVlKHNhbXBsZV9tZXRhZGF0YSRncm91cCkKCiMgYWRkIGFubm90YXRpb24gd2l0aCBleHBlcmltZW50YWwgY29uZGl0aW9ucyBhbmQgY29ycmVzcG9uZGluZyBjb2xvdXJzCmhhbm5vdCA8LSBDb21wbGV4SGVhdG1hcDo6SGVhdG1hcEFubm90YXRpb24oZGYgPSBkYXRhLmZyYW1lKAogIEdyb3VwID0gc2FtcGxlX21ldGFkYXRhJGdyb3VwKSwKICBjb2wgPSBsaXN0KAogICAgR3JvdXAgPSBncm91cF9jb2xvdXJzCiAgKSwKc2hvd19sZWdlbmQgPSBUUlVFKQoKIyBnZW5lcmF0ZSBoZWF0bWFwCkRFX2hlYXRtYXAgPC0gQ29tcGxleEhlYXRtYXA6OkhlYXRtYXAobm9ybWNvdW50c19oZWF0bWFwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBoZWF0bWFwX2NvbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2NvbHVtbl9uYW1lcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfcm93X25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9wX2Fubm90YXRpb24gPSBoYW5ub3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19oZWF0bWFwX2xlZ2VuZCA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl90aXRsZSA9ICJHZW5lcyBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgaW4gUiB2cy4gTlIgZ3JvdXBzIGF0IFQxIikKCkRFX2hlYXRtYXAKYGBgCjxjZW50ZXI+CgoqKkZpZ3VyZSA0OioqIEhlYXRtYXAgb2YgZ2VuZXMgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGluIHNlcHRpYyBzaG9jayBwYXRpZW50cyB3aG8gcmVzcG9uZGVkIHRvIGhlbW9keW5hbWljIHRoZXJhcHkgYW5kIHBhdGllbnRzIHdobyBkaWQgbm90IHJlc3BvbmQgNDggaG91cnMgYWZ0ZXIgdHJlYXRtZW50IChncm91cHMgUl9UMiBhbmQgTlJfVDIsIHJlc3BlY3RpdmVseSkuIEJsdWUgZGVub3RlcyBsb3cgZ2VuZSBleHByZXNzaW9uIGFuZCByZWQgZGVub3RlcyBoaWdoIGdlbmUgZXhwcmVzc2lvbi4KCjwvY2VudGVyPgoKVGhlIFJfVDIgYW5kIE5SX1QyIHNhbXBsZXMgbGFyZ2VseSBjbHVzdGVyZWQgYXBhcnQgZnJvbSBlYWNoIG90aGVyLCB3aGlsZSB0aGUgUl9UMSBhbmQgTlJfVDEgc2FtcGxlcyBhcmUgbm90LiBUaGlzIGlzIGFzIGV4cGVjdGVkLCBzaW5jZSB0aGUgZ2VuZXMgYmVpbmcgcGxvdHRlZCBhcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4gUiBhbmQgTlIgYXQgVDIsIHdoaWxlIG5vIGdlbmVzIGFyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgYmV0d2VlbiBSIGFuZCBOUiBhdCBUMS4gVGhlcmUgaXMgYWxzbyBzaWduaWZpY2FudCBzZXBhcmF0aW9uIGJldHdlZW4gdGhlIFQxIGFuZCBUMiBncm91cHMgZm9yIFIsIGJ1dCBub3QgZm9yIE5SLCBpbmRpY2F0aW5nIHRoYXQgbWFueSBvZiB0aGVzZSBnZW5lcyBjaGFuZ2UgaW4gZXhwcmVzc2lvbiBpbiByZXNwb25kZXJzIG92ZXIgdGhlIDQ4IGhvdXJzIGFmdGVyIGhlbW9keW5hbWljIHRoZXJhcHksIGJ1dCBub3QgaW4gbm9uLXJlc3BvbmRlcnMuCgojIEludGVycHJldGF0aW9uIGFuZCBkb2N1bWVudGF0aW9uCiogVGhpcyBkYXRhc2V0IHdhcyBvZiBpbnRlcmVzdCBiZWNhdXNlIHRoZSBkaXN0aW5jdCBjb25kaXRpb25zIHdpdGhpbiBpdCBhcmUgbm90IGEgcmVzdWx0IG9mIGRpZmZlcmVudGlhbCB0cmVhdG1lbnQgb3IgY2lyY3Vtc3RhbmNlcywgYnV0IG9mIGRpZmZlcmVudGlhbCBiaW9sb2dpY2FsIHJlc3BvbnNlIHRvIGEgdHJlYXRtZW50LCBhbmQgdGhlIGluY2x1c2lvbiBvZiB0d28gZGlmZmVyZW50IHRpbWUgcG9pbnRzIG1lYW5zIHRoYXQgdGhlIGRhdGFzZXQgY2FuIGJlIHVzZWQgdG8gYW5hbHl6ZSBob3cgcHJlLWV4aXN0aW5nIGJpb2xvZ2ljYWwgZGlmZmVyZW5jZXMgbGVhZCB0byBkaWZmZXJlbnQgZXBpZ2VuZXRpYyByZXNwb25zZXMuCgoqIEluIHRoaXMgZGF0YXNldCwgdGhlIGNvbnRyb2wgY29uZGl0aW9uIGlzIHBhdGllbnRzIHdobyB3ZXJlIG5vbi1yZXNwb25kZXJzIHRvIGVhcmx5IHN1cHBvcnRpdmUgaGVtb2R5bmFtaWMgdGhlcmFweSBhcyB0cmVhdG1lbnQgZm9yIHNlcHRpYyBzaG9jayBhbmQgdGhlIHRlc3QgY29uZGl0aW9uIGlzIHBhdGllbnRzIHdobyB3ZXJlIHJlc3BvbmRlcnMuIFNhbXBsZXMgY29sbGVjdGVkIGF0IElDVSBhZG1pc3Npb24gYWxzbyBzZXJ2ZSBhcyBhIGNvbnRyb2wgZm9yIHNhbXBsZXMgY29sbGVjdGVkIDQ4aCBsYXRlci4KCiogVGhlcmUgYXJlIDM0IHNhbXBsZXMgZnJvbSByZXNwb25kZXJzIGFuZCAyOCBzYW1wbGVzIGZyb20gbm9uIHJlc3BvbmRlcnMuIFRoZXJlIGFyZSAzMiBzYW1wbGVzIHRha2VuIGF0IHRpbWUgcG9pbnQgMSBhbmQgMzIgc2FtcGxlIHN0YWtlbiBhdCB0aW1lIHBvaW50IDIuCgoqIFRoZXJlIHdlcmUgZXhwcmVzc2lvbiB2YWx1ZXMgdGhhdCB3ZXJlIG5vdCB1bmlxdWUgZm9yIHNwZWNpZmljIGdlbmVzLCBidXQgdGhleSB3ZXJlIGEgdmVyeSBzbWFsbCBwcm9wb3J0aW9uIG9mIHRoZSBkYXRhLiBXaGVuIG11bHRpcGxlIGV4cHJlc3Npb24gdmFsdWVzIG1hcHBlZCB0byBvbmUgZ2VuZSwgd2UgdXNlZCB0aGVpciBzdW0gYXMgdGhlIGV4cHJlc3Npb24gdmFsdWUgZm9yIHRoYXQgZ2VuZS4gV2hlbiBhbiBleHByZXNzaW9uIHZhbHVlIG1hcHBlZCB0byBtdWx0aXBsZSBnZW5lcywgd2UgZHVwbGljYXRlZCBpdCBhbmQgdXNlZCBpdCBhcyB0aGUgZXhwcmVzc2lvbiB2YWx1ZSBmb3IgZWFjaCBnZW5lIGl0IG1hcHBlZCB0by4gU2luY2UgdGhlcmUgd2VyZSB2ZXJ5IGZldyBzdWNoIGNhc2VzLCB0aGUgaW1wYWN0IG9mIHRoaXMgdHJhbnNmb3JtYXRpb24gb24gbGlicmFyeSBzaXplIHdhcyBuZWdsaWdpYmxlLiAKCiogQSBsYXJnZSBwcm9wb3J0aW9uIG9mIHRoZSBleHByZXNzaW9uIHZhbHVlcyBjb3VsZCBub3QgYmUgbWFwcGVkIHRvIEhVR08gc3ltYm9scywgYnV0IG1vc3Qgb2YgdGhlIHVubWFwcGVkIHZhbHVlcyB3ZXJlIHZlcnkgbG93LCBhbmQgd2VyZSByZW1vdmVkIHdoZW4gZmlsdGVyaW5nIG91dCBsb3cgZXhwcmVzc2lvbiBnZW5lcy4gCgoqIFRoZXJlIHdlcmUgbm8gb3V0bGllcnMgaW4gdGhlIGRhdGFzZXQsIGFjY29yZGluZyB0byB0aGUgdGhyZXNob2xkIG9uIENvb2sncyBkaXN0YW5jZSBhcHBsaWVkIGJ5IERFU2VxMi4gCgoqIEJpb2xvZ2ljYWwgcmVwbGljYXRlcyB3ZXJlIGdyb3VwZWQgdG9nZXRoZXIgaW4gdGhlIGRlc2lnbiBtb2RlbCB1c2VkIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcy4gVGhlcmUgd2VyZSBubyB0ZWNobmljYWwgcmVwbGljYXRlcy4KCiogVGhlIGRhdGFzZXQgY292ZXJzIDEzNjczIGdlbmVzIGFjcm9zcyA2MiBzYW1wbGVzLgoKIyBCaWJsaW9ncmFwaHkK