Goals

Analyze microbiome experimental data as a phyloseq object - explore ecological metrics and identify differentially abundant taxa.


Before We Get Started

Notes

  • This is a slightly modified version of a document created by Matthew Willman.

  • To convert an Rmd (R Markdown) file to an R script, type the following in an R console: knitr::purl(input="<filename>.Rmd"). (You can download this Rmd file by clicking the Code button in the top-right of this page – but we will open it at OSC.)

  • This document deliberately does not have any R output. In case you want to check your output against a reference, you can find a document with R output here.

Open this file in RStudio Server at OSC

  • This assumes you still have an active RStudio Server job at OSC; if not see the instructions on the previous page to start one.

  • In the Files pane, in the markdown directory, open the file 08-postASV-analysis.Rmd. That’s this file!


Getting Started

Install and Load Packages

Add our custom library again if you restarted your R session:

.libPaths(new = '/fs/project/PAS0471/.R/4.0/')

And load the necessary packages:

packages <- c('tidyverse', 'vegan', 'phyloseq', 'decontam', 'ape',
              'DESeq2', 'microbiome', 'metagenomeSeq', 'remotes',
              'ampvis2', 'breakaway')
pacman::p_load(char = packages)

# If you wanted to install and load these packages yourself,
# first make sure you have the pacman package installed:
## install.packages('pacman')
# Then, the code above would work except for the last two packages,
# which are available from Github only. To install these, you would run:
## remotes::install_github("MadsAlbertsen/ampvis2")
## remotes::install_github("adw96/breakaway")

Set Directories

# Dir with input files:
indir <- 'analysis/ASV_inference/'

# Dir for output:
outdir <- 'analysis/postASV_analysis/'

Load the Data

This session starts with a phyloseq object similar to the one generated using DADA2 in the previous session.

ps_raw <- readRDS(file.path(indir, 'ps_16S_V4_withtree.rds'))

# This would load the object from the previous session, which doesn't contain a tree:
# ps_raw <- readRDS(file.path(indir, 'ps_V4.rds'))

Our phyloseq object is made up of the following components (slots):

  • otu_table
  • sample_data
  • tax_table
  • phy_tree

Let’s have a look at the object:

ps_raw

# And the assigned taxon names:
taxa_names(ps_raw)[1:3]

As we can see, the taxon names are currently the associated sequences. We can create a new phyloseq component to store these sequences, then rename the ASVs so something shorter (“ASV_1”, “ASV_2”, etc.).

dna <- Biostrings::DNAStringSet(taxa_names(ps_raw))
names(dna) <- taxa_names(ps_raw)

# Merge sequence object into the phyloseq object:
ps_raw <- merge_phyloseq(ps_raw, dna)
ps_raw

# Rename ASVs:
taxa_names(ps_raw) <- paste("ASV", 1:ntaxa(ps_raw), sep = "_")
taxa_names(ps_raw)[1:3]

Filter taxa

Identify and Remove Contaminants

It is possible to introduce contaminating microbes during sample preparation. Before analyzing the data, we will identify and remove probable contaminants using the decontam package.

In this case, we will define a conataminant as an ASV whose abundance correlates with DNA concentration (post-PCR). Our assumption here is that each soil taxon’s abundance should be independant of DNA concentration. However, if we were to spike each sample with a contaminant, that contaminant would show a greater abundance in samples with lower DNA concentrations. To do this, we need DNA concentration measured after PCR and before pooling of the samples.

These data are in column 7 of the sample data component:

head(sample_data(ps_raw))

MCIC measured our DNA concentration by comparing band intensities to a single reference band. MCIC’s DNA concentration measurements range from 0 to 1 (the reference). Zero in this case is coded as NA. The decontam package can’t handle NA’s or 0’s, so we need to change them to a small value (e.g. 0.01).

# First, we identify the NAs in the DNA measurement column:
NAs <- is.na(sample_data(ps_raw)$PCR_prod_V4.V5)

# Then, we replace NAs with 0.01:
sample_data(ps_raw)$PCR_prod_V4.V5[NAs] <- 0.01

We will use decontam::isContaminant(method = "frequency") to test each taxon against the null hypothesis: abundance is not associated with DNA concentration. Because the DNA concentration measurements are not very accurate, and we want to make sure to remove possible contaminants, we use a relaxed p-value (0.2) to reject the null hypothesis:

contam_df_freq <- isContaminant(ps_raw,
                                method = "frequency",
                                conc = "PCR_prod_V4.V5",
                                threshold = 0.2)
head(contam_df_freq)

As we can see, the top 3 ASVs are identified as probable contaminants. How many contaminants were identified (TRUE in the contaminant column) in total?

table(contam_df_freq$contaminant)

What are the most abundant contaminant taxa?

# Get abundance ranks for contaminant ASVs:
head(which(contam_df_freq$contaminant))

# Check which taxa are contaminants:
ps_contam <- prune_taxa(contam_df_freq$contaminant, ps_raw)
head(tax_table(ps_contam))

Let’s take a look at our tested associations for several taxa:

plot_frequency(ps_raw,
               taxa_names(ps_raw)[c(1, 2, 3, 4, 5, 11)],
               conc = "PCR_prod_V4.V5") +
  xlab("DNA Concentration (relative to single marker)")

As we can see, abundance of ASVs 1, 2, 3, and 11 are particularly high in samples with low DNA concentration.

Which samples have highest abundances of the contaminants?

asv1 <- otu_table(ps_raw)[, 1]
head(asv1[order(asv1, decreasing = TRUE)])

asv2 <- otu_table(ps_raw)[, 2]
head(asv2[order(asv2, decreasing = TRUE)])

How do these contaminants overlap with ASVs present in the negative control?

otu_table(ps_raw)[70:75, 1:11]

ASVs have high read counts in the extraction negative control. Thus, we can be fairly certain these are contaminants and not representative of our experimental samples.

Remove the contaminants that we identified:

ps_noncontam <- prune_taxa(!contam_df_freq$contaminant, ps_raw)

ps_noncontam

What proportion of our count data were removed as contaminants?

pre <- sum(sample_sums(ps_raw))
post <- sum(sample_sums(ps_noncontam))

(pre-post) / pre

Remove non-bacterial, non-archaeal ASVs

The V4 region of 16S rRNA is conserved within certain bacteria, archaea, chloroplasts, mitochondria, and eukaryotes. Since we are only interested in bacteria and archaea here, we’ll need to remove other taxa from our object.

# Create ps subsets for chloroplast, mitochondria, and eukaryotes:
chlr <- subset_taxa(ps_noncontam, Order == "Chloroplast")
mit <- subset_taxa(ps_noncontam, Family == "Mitochondria")
euk <- subset_taxa(ps_noncontam, Kingdom == "Eukaryota")

# Get all taxon IDs that we want to remove:
bad_taxa <- c(taxa_names(chlr), taxa_names(mit), taxa_names(euk))

# Get all taxon IDs that we want to keep:
all_taxa <- taxa_names(ps_noncontam)
good_taxa <- all_taxa[!(all_taxa %in% bad_taxa)]

# Subset phyloseq object:
ps_bac_arc <- prune_taxa(good_taxa, ps_noncontam)

What proportion of ASVs were kept?

pre <- sum(sample_sums(ps_noncontam))
post <- sum(sample_sums(ps_bac_arc))
post / pre

Finally, we’ll also remove non-experimental samples (e.g. negative controls). Note, though, that non-experimental samples are important for evaluating the quality of your reagents, consistency across sequencing runs, and contamination.

ps <- subset_samples(ps_bac_arc, Experiment == "HCC")

ps

Filter samples

After having filtered out unwanted taxa, we now need to filter out uninformative samples – those with low total taxon counts.

First, how many counts do we have for each sample?

sums <- sample_sums(ps)
sums[order(sums)]

# Lowest total count:
min(sums)

# Sample with lowest counts:
names(sums[order(sums)][1])

To remove uninformative samples, we will only keep those with over 1,000 counts. We will also remove a technical replicate, though note that technical replicates are important for quality control of runs and for understanding the reproducibility of the methodology.

# Remove samples with low counts:
ps <- subset_samples(ps, sample_sums(ps) > 1000)

# Remove the technical replicate:
ps <- subset_samples(ps, SampleID != "501S4")

Let’s take a look at our current phyloseq object:

sample_data(ps)[1:10]

head(t(otu_table(ps)[1:10]))

head(tax_table(ps))

Normalization

Normalization is currently a much-discussed issue of microbiome studies. Differences in read depth between samples often need to be corrected before analysis. Several normalization methods have been proposed, and no single method is perfect. It may be that the most appropriate method depends on the analysis.

In this tutorial, we will use:

  • Proportion-normalized data to estimate ecological metrics.

  • Stabilizing transformation normalized data to identify differentially abundant taxa.

However, for your reference, we will also present code for two other normalization methods: rarefaction and cumulative sum scaling.

For more details, read McMurdie and Holmes 2014 and McKnight et al. 2019.


Proportion Normalization

Proportion normalization involves dividing each OTU count by the total sum for each sample. The resulting count data will add up to 1 (100%) for each sample.

The microbiome::transform function can be used to easily normalize count data as proportions in a phyloseq object:

# Proportion normalization:
ps_prop <- transform(ps, "compositional")

# Have a look at the resulting OTU table:
otu_table(ps_prop)[1:5, 1:5]

# Check what the sums for each sample are now:
head(sample_sums(ps_prop))

Variance stabilizing transformation

This transformation method, available in DESeq2, normalizes data with respect to library size as well as dispersion.
Type ?vst or read Anders and Huber 2010 for more information.

# First we convert the phyloseq object to a DESeq object:
dds <- phyloseq_to_deseq2(ps, ~1)

# Then we estimate the size factors:
dds <- estimateSizeFactors(dds)

# Now can do the transformation:
dds_vst <- vst(dds, blind = TRUE)

# Have a look at the resulting counts:
vst_counts <- assay(dds_vst)
t(vst_counts)[1:5, 1:5]

Rarefaction

Rarefaction can be used to subset data such that the library depth is the same for each sample. Because sampling of the data is random, rarefaction can account for an effect of total read count on taxa richness. However, rarefaction is no longer considered to be a good way to normalize amplicon sequencing data (see McMurdie & Holmes 2014).

ps_rarefied <- rarefy_even_depth(ps,
                                 rngseed = 1,
                                 sample.size = min(sample_sums(ps)),
                                 replace = FALSE)

# Have a look at the result:
otu_table(ps_rarefied)[1:5, 1:5]

head(sample_sums(ps_rarefied))

Compare these sample sums to the non-rarified data sums:

head(sample_sums(ps))

Cumulative Sum Scaling

The metagenomeSeq Cumulative Sum Scaling (CSS) normalization is another option developed for microbiome data. For more information, read Paulson et al. 2013.

# Convert the phyloseq object to a metagenomeseq object:
mgs_css <- phyloseq_to_metagenomeSeq(ps)

# Perform the Cumulative Sum Scaling:
mgs_css <- cumNorm(mgs_css)

# Extract the counts and add them to a separate phyloseq object:
css_counts <- MRcounts(mgs_css, norm = TRUE)
ps_css <- ps
otu_table(ps_css) <- otu_table(t(css_counts), taxa_are_rows = FALSE)

Now lets compare the original data to the CSS normalized data:

otu_table(ps)[1:5, 1:5]
head(sample_sums(ps))

otu_table(ps_css)[1:5, 1:5]
head(sample_sums(ps_css))

Plot abundances

In this and following the examples we will use the proportions data.

# Subset to the T1 treatment
T1 <- subset_samples(ps_prop, Treatment == "T1")

# Agglomerate taxa to phylum level:
T1_phylum <- tax_glom(T1, taxrank = "Phylum", NArm = FALSE)

# Remove uncommon phyla:
T1_phylum <- subset_taxa(T1_phylum, taxa_sums(T1_phylum) > 0.1)

# Plot:
plot_bar(T1_phylum, fill = "Phylum") +
   facet_wrap(~Timepoint, scales = "free_x", nrow = 1)

Alpha diversity

Alpha diversity measures taxonomic diversity within a single population. Measures of alpha diversity include taxonomic richness (i.e. number of taxa), and indices which combine taxonomic richness with some measure of evenness.

Note: Many of these methods are greatly influenced by singleton data (i.e. taxa represented by a single count), meaning they may be unreliable if your data excludes singletons. DADA2 does not output singletons if it is run individually on each sample. Thus, if using dada2 to infer ASVs, it is advisable to use pool = TRUE when running the dada algorithm. This will allow calling per-sample singletons, but not per-study singletons.

Taxonomic richness: Rarefaction plot

Our goal in defining richness is to determine the number of unique taxa within each population (in our case, each plot). This is difficult, as our samples contain only a portion of the total richness within our plots. As sequence depth increases for each sample, so does the number of taxa. This can be illustrated with a rarefaction plot.

To make a rarefaction plot, we draw random samples from our data and count the number of unique ASVs as samples are drawn. The resulting rarefaction curve is expected to rise quickly then plateu as the most abundant taxa are represented. We can make a quick rarefaction curve plot directly from our phyloseq object of all samples using the vegan package:

rarecurve(otu_table(ps), step = 500, xlab = "Sample Size", ylab = "Taxa")

We can also split rarefaction curves by group using the ampvis2 package.

metadata <- data.frame(sample_data(ps), check.names = FALSE)

asv_table <- data.frame(t(otu_table(ps)),
                       tax_table(ps),
                       check.names = FALSE)

ampvis2 requires a Species column, which must be added to our data. If your table already has a Species column, skip this step:

asv_table$Species <- NA

Load an ampvis2 object and calculate rarefaction:

ps3 <- amp_load(asv_table, metadata)

rar_plot <- amp_rarecurve(ps3,
                          stepsize = 100,
                          facet_by = "Timepoint",
                          color_by = "Treatment") +
   ylab("Number of observed ASVs")

rar_plot

As we can see, as read depth increases for each sample, so does taxonomic richness. In all samples, increased sampling would result in increased richness. Thus, our observed sample richness is lower than the true population richness for every plot.

Further, sequence depth and total richness appear to be correlated across samples:

dp_by_asv <- data.frame(ct_sums = sample_sums(ps),
                        asvs = rowSums(otu_table(ps) != 0))

ggplot(data = dp_by_asv) +
  geom_point(aes(x =  ct_sums, y = asvs)) +
  labs(x = "Sequence depth", y= "ASV count") +
  theme_minimal()

We can test whether this correlation is significant:

cor <- cor.test(dp_by_asv$ct_sums, dp_by_asv$asvs, method = "spearman")
cor

Here we observe a significant correlation between read count and observed taxonomic richness.

Thus, a sample from a population with high actual diversity, but low read depth, could have low observed richness. Fortunately, there are methods to estimate the number of unobserved taxa. For an overview of these methods, read Bunge et al. 2014.

Frequency counts

To illustrate the difficulty of estimating population richness and importance of singletons to alpha diversity metrics, we’ll look at the frequency count distribution for our first sample: 101-S1.

We’ll count the number of singletons, doubletons, etc., then plot frequency as a function of count.

# Get the frequency count distribution for the 1st sample:
frequencytablelist <- build_frequency_count_tables(otu_table(ps))
freq_ct <- frequencytablelist[[1]]
colnames(freq_ct) <- c("Frequency", "Count")

# Plot:
ggplot(freq_ct, aes(Frequency, Count)) + 
  geom_point() + 
  scale_x_continuous(breaks = seq(0, 600, by = 50)) + 
  ggtitle("101-S1") +
  theme_minimal()

We can see there are ~3,200 taxa represented as singletons, and ~1,600 taxa as doubletons. This is a high proportion of the total number taxa are observed in this sample:

# Total number of taxa:
sum(freq_ct$Count)

# Proportion of singletons
freq_ct$Count[1] / sum(freq_ct$Count)

How many more taxa might we observe if we increased our sampling?

Diversity Indices

We can use several indices to explore how evenly species are distributed within each sample. Note that ‘Observed’ is the sample taxonomic richness. The other three indices combine richness and abundance data for each taxon.

plot_richness(ps,
              x = "Timepoint",
              shape = "Treatment",
              color = "Treatment", 
              measures = c("Observed", "Shannon", "Simpson", "InvSimpson"))

Notice that several T2:S3 samples have low measures, suggesting they have low diversity (i.e. abundances are dominated by a few taxa) compared to the other samples.


Beta diversity

Beta diversity measures differences in microbial compositions between populations. If available (use ps_16S_V4_withtree.rds), we can use a phylogenetic tree and otu table to estimate the UniFrac for each sample pair.

The result is a distance matrix with height and width equal to the number of samples. UniFrac may be weighted or unweighted. Selection of weighted or unweighted will depend on the question you want to answer. Unweighted UniFrac uses only presence or absence of a taxon from the otu table (i.e. 1000 will be treated the same as 1). It is appropriate if you want to test qualitative taxa differences between samples. Weighted UniFrac uses presence as well as quantity data (i.e. 1 < 1000).

We are interested to know what changes to taxon abundance may be driven by our treatments, so we will use weighted UniFrac.

Ordination

First we’ll make a weighted UniFrac distance matrix. For UniFrac, you require a tree as part of your phyloseq object.

set.seed(100)
wUF <- phyloseq::distance(ps_prop, method = "wunifrac")

as.matrix(wUF)[1:6, 1:6]

# Note: If your *phyloseq* object lacks a phylogenetic tree,
# you could use a dissimilarity index that does not require one, such as Bray-Curtis:
# bray <- vegan::vegdist(otu_table(ps_prop), method = "bray")
# as.matrix(bray)[1:6,1:6]

Then perform Principal Coordinate Analysis (PCoA) on your favorite distance matrix. In this case, we are using weighted UniFrac.

do <- ordinate(ps_prop, method = "PCoA", distance = wUF)

baseplot <- plot_ordination(ps_prop, do,
                            type = "samples",
                            color = "Treatment",
                            shape = "Timepoint") +
  ggtitle("HCC ordination, all samples\nmethod=PCoA, distance=weighted UniFrac") +
  theme_bw()

# Single plot with all samples:
baseplot +
  geom_point(size = 2)
  
# Idem, but with sample names on the plot: 
baseplot +
  geom_point(size = 1) + 
  geom_label(aes(label = SampleID), size = 3.5, color = "black")

# Facet by time point:
baseplot +
  geom_point(size = 2) + 
  ggtitle("HCC ordination, all samples\nmethod=PCoA, distance=weighted UniFrac") +
  facet_wrap(~Timepoint, 2)

We can see some grouping by timepoint. Depending on your experiment and questions, you might want to consider other ordination or clustering approaches.

Permutational analysis of variance (PERMANOVA)

Finally, we’ll test whether there is a significant ecological level treatment effect. PERMANOVA sounds fancy, but it is just an ANOVA performed using permutations. Permutations are used to determine how data may appear if there is no treatment effect and group differences are due to random chance. Observed data are then compared to the randomized data to calculate a p-value.

Treatments are independent within Timepoints, so we will first subset the data by Timepoint:

ps_S1 <- subset_samples(ps_prop, Timepoint == "S1")
ps_S3 <- subset_samples(ps_prop, Timepoint == "S3")
ps_S4 <- subset_samples(ps_prop, Timepoint == "S4")

Subset the metadata and estimate UniFrac for S3:

metadata <- as(sample_data(ps_S3), "data.frame")
unifrac_dist <- UniFrac(ps_S3, weighted = TRUE)

Then we’ll use vegan::adonis2 to perform the PERMANOVA.

set.seed(100)  # Set the seed to make the analysis reproducible

permanova <- adonis2(unifrac_dist ~ Treatment,
                     data = metadata,
                     permutations = 10000)

permanova

The low p-value suggests that within the S3 timepoint (post-termination), there is a significant treatment effect.


Differential taxon abundance (DESeq)

Here, we will use DESeq2 to identify differentially abundant taxa between time points and treatments. DESeq2 was designed to analyze RNAseq datasets, which are similar to OTU/ASV data sets in that both handle large, sparse contingency tables generated from Illumina sequencing data. For more details, see McMurdie and Holmes 2014.

First, we’ll convert our non-normalized count data to a DESeq object.

dds <- phyloseq_to_deseq2(ps, ~Timepoint + Treatment)

dds
colData(dds)

There are two ways to analyze interaction effects using DESeq2. The first is to fit a multivariate model (e.g. ~A+B+A:B) and explore the model coefficients. The second is to fit a univariate model and explore pairwise contrasts. Here, we will group our two factors (Treatment and Timepoint) and use the latter approach.

# Prepare the data:
dds$Group <- factor(paste0(dds$Treatment, dds$Timepoint))
design(dds) <- formula(~Group)
dds <- dds[, -50]                    # Remove tech_rep sample
dds$Group <- droplevels(dds$Group)   # Drop tech_rep level from design matrix

# Perform the differential abundance analysis:
dds <- DESeq(dds)

results(dds)

Let’s explore the contrast, “T1S4 : T4S4” (no cc, seedling stage : late termination, seedling stage):

res <- results(dds, contrast = c("Group", "T1S4", "T4S4"))
summary(res)

# Number of taxa with significantly higher abundance in T4S4:
sum(res$padj < 0.1 & res$log2FoldChange > 0, na.rm = TRUE)

# Number of taxa with significantly lower abundance in T4S4:
sum(res$padj < 0.1 & res$log2FoldChange < 0, na.rm = TRUE)

We can sort results by p-value and look at the top ASVs:

res.order <- res[order(res$pvalue), ]
head(res.order)[c(1:2, 6)]

And plot counts for the first in the ordered list ASV:

plotCounts(dds, gene = res.order@rownames[1], intgroup = "Group")

We can also plot counts by sample_ID:

plotCounts(dds, gene = "ASV_48", intgroup = "Group")

The plot can also be saved and customized with ggplot:

d <- plotCounts(dds, gene = "ASV_48", intgroup = "Group", returnData = TRUE)

ggplot(d, aes(x = Group, y = count)) +
  geom_point(position = position_jitter(w = 0.1, h = 0), size = 2) +
  scale_y_log10() +
  ggtitle(expression(paste("ASV 48, log"[10], "-scale"))) +
  theme_bw()

We can also subset our differentially abundant taxa to keep those with an adjusted p-value < 0.05:

sig <- subset(res.order, padj <= 0.05)
head(sig)

What taxa are associated with significantly differentially abundant ASVs?

sig_asv <- rownames(sig)
sig_taxa <- tax_table(ps)[sig_asv,]
head(sig_taxa)

sig_write <- cbind(sig, sig_taxa)
write.csv(sig_write, file.path(outdir, 'DATaxa_T1S1_T1S4.csv'))

Report that we are done!

print('Done with ASV inference.')

LS0tCnRpdGxlOiAiPGJyPldvcmtmbG93IHBhcnQgSUlJOjxicj5BbmFseXppbmcgQVNWIERhdGEiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IGNlcnVsZWFuCiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY3NzOiBteS5jc3MKICAgIGFuY2hvcl9zZWN0aW9uczogdHJ1ZQplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGNvbnNvbGUKLS0tCgpgYGB7ciBzZXR1cCwgZWNobz1GQUxTRSwgcHVybD1GQUxTRX0Kcm9vdF9kaXIgPC0gJy4uJwoja25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSByb290X2RpcikKCmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBjYWNoZSA9IFRSVUUsCiAgZXZhbCA9IEZBTFNFLCAjIGNoYW5nZSBhcyBuZWVkZWQKICBjbGFzcy5zb3VyY2UgPSAncl9jb2RlJywgY2xhc3Mub3V0cHV0ID0gJ3Jfb3V0cHV0JywgY2xhc3Mud2FybmluZyA9ICdyX3dhcm5pbmcnLCBjbGFzcy5tZXNzYWdlID0gJ3Jfd2FybmluZycsIGNsYXNzLmVycm9yID0gJ3JfZXJyb3InCiAgKQpgYGAKCjxicj4KCi0tLS0tCgojIyBHb2FscwoKQW5hbHl6ZSBtaWNyb2Jpb21lIGV4cGVyaW1lbnRhbCBkYXRhIGFzIGEgcGh5bG9zZXEgb2JqZWN0IC0gZXhwbG9yZSBlY29sb2dpY2FsIG1ldHJpY3MgYW5kIGlkZW50aWZ5IGRpZmZlcmVudGlhbGx5IGFidW5kYW50IHRheGEuCgotLS0tLQoKIyMgQmVmb3JlIFdlIEdldCBTdGFydGVkCgojIyMgTm90ZXMKCi0gVGhpcyBpcyBhIHNsaWdodGx5IG1vZGlmaWVkIHZlcnNpb24gb2YgYSBkb2N1bWVudCBjcmVhdGVkIGJ5IFtNYXR0aGV3IFdpbGxtYW5dKG1haWx0bzp3aWxsbWFuLjE4QG9zdS5lZHUpLgoKLSBUbyBjb252ZXJ0IGFuIGBSbWRgIChSIE1hcmtkb3duKSBmaWxlIHRvIGFuIFIgc2NyaXB0LAogIHR5cGUgdGhlIGZvbGxvd2luZyBpbiBhbiBSIGNvbnNvbGU6CiAgYGtuaXRyOjpwdXJsKGlucHV0PSI8ZmlsZW5hbWU+LlJtZCIpYC4KICAoWW91IGNhbiBkb3dubG9hZCB0aGlzIGBSbWRgIGZpbGUgYnkgY2xpY2tpbmcgdGhlIGBDb2RlYCBidXR0b24KICBpbiB0aGUgdG9wLXJpZ2h0IG9mIHRoaXMgcGFnZSAtLSBidXQgd2Ugd2lsbCBvcGVuIGl0IGF0IE9TQy4pCgotIFRoaXMgZG9jdW1lbnQgZGVsaWJlcmF0ZWx5IGRvZXMgbm90IGhhdmUgYW55IFIgb3V0cHV0LgogIEluIGNhc2UgeW91IHdhbnQgdG8gY2hlY2sgeW91ciBvdXRwdXQgYWdhaW5zdCBhIHJlZmVyZW5jZSwKICB5b3UgY2FuIGZpbmQgYSBkb2N1bWVudCB3aXRoIFIgb3V0cHV0IFtoZXJlXShhc3NldHMvMDgtcG9zdEFTVi1hbmFseXNpcy5odG1sKS4KICAKIyMjIE9wZW4gdGhpcyBmaWxlIGluIFJTdHVkaW8gU2VydmVyIGF0IE9TQwoKLSBUaGlzIGFzc3VtZXMgeW91IHN0aWxsIGhhdmUgYW4gYWN0aXZlIFJTdHVkaW8gU2VydmVyIGpvYiBhdCBPU0M7CiAgIGlmIG5vdCBzZWUgdGhlCiAgIFtpbnN0cnVjdGlvbnMgb24gdGhlIHByZXZpb3VzIHBhZ2VdKDA3LUFTVi1pbmZlcmVuY2UuaHRtbCNzdGFydC1hbi1yc3R1ZGlvLXNlcnZlci1qb2ItYXQtb3NjKSB0byBzdGFydCBvbmUuCgotIEluIHRoZSBGaWxlcyBwYW5lLCBpbiB0aGUgYG1hcmtkb3duYCBkaXJlY3RvcnksCiAgb3BlbiB0aGUgZmlsZSBgMDgtcG9zdEFTVi1hbmFseXNpcy5SbWRgLiBUaGF0J3MgdGhpcyBmaWxlIQoKIAotLS0tLQoKIyMgR2V0dGluZyBTdGFydGVkCgojIyMgSW5zdGFsbCBhbmQgTG9hZCBQYWNrYWdlcwoKQWRkIG91ciBjdXN0b20gbGlicmFyeSBhZ2FpbiBpZiB5b3UgcmVzdGFydGVkIHlvdXIgUiBzZXNzaW9uOgoKYGBge3IgbGlibG9hZH0KLmxpYlBhdGhzKG5ldyA9ICcvZnMvcHJvamVjdC9QQVMwNDcxLy5SLzQuMC8nKQpgYGAKCkFuZCBsb2FkIHRoZSBuZWNlc3NhcnkgcGFja2FnZXM6CgpgYGB7ciBwYWNrYWdlcywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcGFja2FnZXMgPC0gYygndGlkeXZlcnNlJywgJ3ZlZ2FuJywgJ3BoeWxvc2VxJywgJ2RlY29udGFtJywgJ2FwZScsCiAgICAgICAgICAgICAgJ0RFU2VxMicsICdtaWNyb2Jpb21lJywgJ21ldGFnZW5vbWVTZXEnLCAncmVtb3RlcycsCiAgICAgICAgICAgICAgJ2FtcHZpczInLCAnYnJlYWthd2F5JykKcGFjbWFuOjpwX2xvYWQoY2hhciA9IHBhY2thZ2VzKQoKIyBJZiB5b3Ugd2FudGVkIHRvIGluc3RhbGwgYW5kIGxvYWQgdGhlc2UgcGFja2FnZXMgeW91cnNlbGYsCiMgZmlyc3QgbWFrZSBzdXJlIHlvdSBoYXZlIHRoZSBwYWNtYW4gcGFja2FnZSBpbnN0YWxsZWQ6CiMjIGluc3RhbGwucGFja2FnZXMoJ3BhY21hbicpCiMgVGhlbiwgdGhlIGNvZGUgYWJvdmUgd291bGQgd29yayBleGNlcHQgZm9yIHRoZSBsYXN0IHR3byBwYWNrYWdlcywKIyB3aGljaCBhcmUgYXZhaWxhYmxlIGZyb20gR2l0aHViIG9ubHkuIFRvIGluc3RhbGwgdGhlc2UsIHlvdSB3b3VsZCBydW46CiMjIHJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJNYWRzQWxiZXJ0c2VuL2FtcHZpczIiKQojIyByZW1vdGVzOjppbnN0YWxsX2dpdGh1YigiYWR3OTYvYnJlYWthd2F5IikKYGBgCgojIyMgU2V0IERpcmVjdG9yaWVzCgpgYGB7cn0KIyBEaXIgd2l0aCBpbnB1dCBmaWxlczoKaW5kaXIgPC0gJ2FuYWx5c2lzL0FTVl9pbmZlcmVuY2UvJwoKIyBEaXIgZm9yIG91dHB1dDoKb3V0ZGlyIDwtICdhbmFseXNpcy9wb3N0QVNWX2FuYWx5c2lzLycKYGBgCgojIyMgTG9hZCB0aGUgRGF0YQoKVGhpcyBzZXNzaW9uIHN0YXJ0cyB3aXRoIGEgKnBoeWxvc2VxKiBvYmplY3Qgc2ltaWxhciB0byB0aGUgb25lIGdlbmVyYXRlZCB1c2luZwoqREFEQTIqIGluIHRoZSBbcHJldmlvdXMgc2Vzc2lvbl0oMDYtcmVhZHMtdG8tQVNWLlJtZCkuCgpgYGB7ciBkYXRhfQpwc19yYXcgPC0gcmVhZFJEUyhmaWxlLnBhdGgoaW5kaXIsICdwc18xNlNfVjRfd2l0aHRyZWUucmRzJykpCgojIFRoaXMgd291bGQgbG9hZCB0aGUgb2JqZWN0IGZyb20gdGhlIHByZXZpb3VzIHNlc3Npb24sIHdoaWNoIGRvZXNuJ3QgY29udGFpbiBhIHRyZWU6CiMgcHNfcmF3IDwtIHJlYWRSRFMoZmlsZS5wYXRoKGluZGlyLCAncHNfVjQucmRzJykpCmBgYAoKT3VyICpwaHlsb3NlcSogb2JqZWN0IGlzIG1hZGUgdXAgb2YgdGhlIGZvbGxvd2luZyBjb21wb25lbnRzIChzbG90cyk6CgotIGBvdHVfdGFibGVgCi0gYHNhbXBsZV9kYXRhYAotIGB0YXhfdGFibGVgCi0gYHBoeV90cmVlYAoKTGV0J3MgaGF2ZSBhIGxvb2sgYXQgdGhlIG9iamVjdDoKCmBgYHtyIHBzfQpwc19yYXcKCiMgQW5kIHRoZSBhc3NpZ25lZCB0YXhvbiBuYW1lczoKdGF4YV9uYW1lcyhwc19yYXcpWzE6M10KYGBgCgpBcyB3ZSBjYW4gc2VlLCB0aGUgdGF4b24gbmFtZXMgYXJlIGN1cnJlbnRseSB0aGUgYXNzb2NpYXRlZCBzZXF1ZW5jZXMuCldlIGNhbiBjcmVhdGUgYSBuZXcgKnBoeWxvc2VxKiBjb21wb25lbnQgdG8gc3RvcmUgdGhlc2Ugc2VxdWVuY2VzLAp0aGVuIHJlbmFtZSB0aGUgQVNWcyBzbyBzb21ldGhpbmcgc2hvcnRlciAoIkFTVl8xIiwgIkFTVl8yIiwgZXRjLikuCgpgYGB7cn0KZG5hIDwtIEJpb3N0cmluZ3M6OkROQVN0cmluZ1NldCh0YXhhX25hbWVzKHBzX3JhdykpCm5hbWVzKGRuYSkgPC0gdGF4YV9uYW1lcyhwc19yYXcpCgojIE1lcmdlIHNlcXVlbmNlIG9iamVjdCBpbnRvIHRoZSBwaHlsb3NlcSBvYmplY3Q6CnBzX3JhdyA8LSBtZXJnZV9waHlsb3NlcShwc19yYXcsIGRuYSkKcHNfcmF3CgojIFJlbmFtZSBBU1ZzOgp0YXhhX25hbWVzKHBzX3JhdykgPC0gcGFzdGUoIkFTViIsIDE6bnRheGEocHNfcmF3KSwgc2VwID0gIl8iKQp0YXhhX25hbWVzKHBzX3JhdylbMTozXQpgYGAKCi0tLS0tCgojIyBGaWx0ZXIgdGF4YQoKIyMjIElkZW50aWZ5IGFuZCBSZW1vdmUgQ29udGFtaW5hbnRzCgpJdCBpcyBwb3NzaWJsZSB0byBpbnRyb2R1Y2UgY29udGFtaW5hdGluZyBtaWNyb2JlcyBkdXJpbmcgc2FtcGxlIHByZXBhcmF0aW9uLgpCZWZvcmUgYW5hbHl6aW5nIHRoZSBkYXRhLCB3ZSB3aWxsIGlkZW50aWZ5IGFuZCByZW1vdmUgcHJvYmFibGUgY29udGFtaW5hbnRzIHVzaW5nCnRoZSBbZGVjb250YW0gcGFja2FnZV0oaHR0cHM6Ly9iZW5qam5lYi5naXRodWIuaW8vZGVjb250YW0vdmlnbmV0dGVzL2RlY29udGFtX2ludHJvLmh0bWwpLiAgIAoKSW4gdGhpcyBjYXNlLCB3ZSB3aWxsIGRlZmluZSBhIGNvbmF0YW1pbmFudCBhcyBhbiBBU1Ygd2hvc2UgYWJ1bmRhbmNlIGNvcnJlbGF0ZXMKd2l0aCBETkEgY29uY2VudHJhdGlvbiAocG9zdC1QQ1IpLgpPdXIgYXNzdW1wdGlvbiBoZXJlIGlzIHRoYXQgZWFjaCBzb2lsIHRheG9uJ3MgYWJ1bmRhbmNlIHNob3VsZCBiZSBpbmRlcGVuZGFudCBvZgpETkEgY29uY2VudHJhdGlvbi4gSG93ZXZlciwgaWYgd2Ugd2VyZSB0byBzcGlrZSBlYWNoIHNhbXBsZSB3aXRoIGEgY29udGFtaW5hbnQsCnRoYXQgY29udGFtaW5hbnQgd291bGQgc2hvdyBhIGdyZWF0ZXIgYWJ1bmRhbmNlIGluIHNhbXBsZXMgd2l0aCBsb3dlciBETkEgY29uY2VudHJhdGlvbnMuClRvIGRvIHRoaXMsIHdlIG5lZWQgRE5BIGNvbmNlbnRyYXRpb24gbWVhc3VyZWQgYWZ0ZXIgUENSIGFuZCBiZWZvcmUgcG9vbGluZyBvZiB0aGUgc2FtcGxlcy4KClRoZXNlIGRhdGEgYXJlIGluIGNvbHVtbiA3IG9mIHRoZSBzYW1wbGUgZGF0YSBjb21wb25lbnQ6CgpgYGB7ciBETkFfY29uY2VudHJhdGlvbn0KaGVhZChzYW1wbGVfZGF0YShwc19yYXcpKQpgYGAKCk1DSUMgbWVhc3VyZWQgb3VyIEROQSBjb25jZW50cmF0aW9uIGJ5IGNvbXBhcmluZyBiYW5kIGludGVuc2l0aWVzIHRvIGEgc2luZ2xlCnJlZmVyZW5jZSBiYW5kLiBNQ0lDJ3MgRE5BIGNvbmNlbnRyYXRpb24gbWVhc3VyZW1lbnRzIHJhbmdlIGZyb20gMCB0byAxICh0aGUgcmVmZXJlbmNlKS4KWmVybyBpbiB0aGlzIGNhc2UgaXMgY29kZWQgYXMgYE5BYC4KVGhlICpkZWNvbnRhbSogcGFja2FnZSBjYW4ndCBoYW5kbGUgTkEncyBvciAwJ3MsCnNvIHdlIG5lZWQgdG8gY2hhbmdlIHRoZW0gdG8gYSBzbWFsbCB2YWx1ZSAoZS5nLiAwLjAxKS4KCmBgYHtyfQojIEZpcnN0LCB3ZSBpZGVudGlmeSB0aGUgTkFzIGluIHRoZSBETkEgbWVhc3VyZW1lbnQgY29sdW1uOgpOQXMgPC0gaXMubmEoc2FtcGxlX2RhdGEocHNfcmF3KSRQQ1JfcHJvZF9WNC5WNSkKCiMgVGhlbiwgd2UgcmVwbGFjZSBOQXMgd2l0aCAwLjAxOgpzYW1wbGVfZGF0YShwc19yYXcpJFBDUl9wcm9kX1Y0LlY1W05Bc10gPC0gMC4wMQpgYGAKCldlIHdpbGwgdXNlIGBkZWNvbnRhbTo6aXNDb250YW1pbmFudChtZXRob2QgPSAiZnJlcXVlbmN5IilgIHRvIHRlc3QgZWFjaCB0YXhvbgphZ2FpbnN0IHRoZSBudWxsIGh5cG90aGVzaXM6IGFidW5kYW5jZSBpcyBub3QgYXNzb2NpYXRlZCB3aXRoIEROQSBjb25jZW50cmF0aW9uLgpCZWNhdXNlIHRoZSBETkEgY29uY2VudHJhdGlvbiBtZWFzdXJlbWVudHMgYXJlIG5vdCB2ZXJ5IGFjY3VyYXRlLAphbmQgd2Ugd2FudCB0byBtYWtlIHN1cmUgdG8gcmVtb3ZlIHBvc3NpYmxlIGNvbnRhbWluYW50cywKd2UgdXNlIGEgcmVsYXhlZCBwLXZhbHVlICgwLjIpIHRvIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzOgoKYGBge3IsIGNhY2hlPVRSVUV9CmNvbnRhbV9kZl9mcmVxIDwtIGlzQ29udGFtaW5hbnQocHNfcmF3LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJmcmVxdWVuY3kiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmMgPSAiUENSX3Byb2RfVjQuVjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRocmVzaG9sZCA9IDAuMikKaGVhZChjb250YW1fZGZfZnJlcSkKYGBgCgpBcyB3ZSBjYW4gc2VlLCB0aGUgdG9wIDMgQVNWcyBhcmUgaWRlbnRpZmllZCBhcyBwcm9iYWJsZSBjb250YW1pbmFudHMuCkhvdyBtYW55IGNvbnRhbWluYW50cyB3ZXJlIGlkZW50aWZpZWQgKGBUUlVFYCBpbiB0aGUgYGNvbnRhbWluYW50YCBjb2x1bW4pCmluIHRvdGFsPwoKYGBge3J9CnRhYmxlKGNvbnRhbV9kZl9mcmVxJGNvbnRhbWluYW50KQpgYGAKCldoYXQgYXJlIHRoZSBtb3N0IGFidW5kYW50IGNvbnRhbWluYW50IHRheGE/CgpgYGB7cn0KIyBHZXQgYWJ1bmRhbmNlIHJhbmtzIGZvciBjb250YW1pbmFudCBBU1ZzOgpoZWFkKHdoaWNoKGNvbnRhbV9kZl9mcmVxJGNvbnRhbWluYW50KSkKCiMgQ2hlY2sgd2hpY2ggdGF4YSBhcmUgY29udGFtaW5hbnRzOgpwc19jb250YW0gPC0gcHJ1bmVfdGF4YShjb250YW1fZGZfZnJlcSRjb250YW1pbmFudCwgcHNfcmF3KQpoZWFkKHRheF90YWJsZShwc19jb250YW0pKQpgYGAKCkxldCdzIHRha2UgYSBsb29rIGF0IG91ciB0ZXN0ZWQgYXNzb2NpYXRpb25zIGZvciBzZXZlcmFsIHRheGE6CgpgYGB7ciwgd2FybmluZz1GQUxTRX0KcGxvdF9mcmVxdWVuY3kocHNfcmF3LAogICAgICAgICAgICAgICB0YXhhX25hbWVzKHBzX3JhdylbYygxLCAyLCAzLCA0LCA1LCAxMSldLAogICAgICAgICAgICAgICBjb25jID0gIlBDUl9wcm9kX1Y0LlY1IikgKwogIHhsYWIoIkROQSBDb25jZW50cmF0aW9uIChyZWxhdGl2ZSB0byBzaW5nbGUgbWFya2VyKSIpCmBgYAogICAKQXMgd2UgY2FuIHNlZSwgYWJ1bmRhbmNlIG9mIEFTVnMgMSwgMiwgMywgYW5kIDExIGFyZSBwYXJ0aWN1bGFybHkgaGlnaCBpbiBzYW1wbGVzCndpdGggbG93IEROQSBjb25jZW50cmF0aW9uLgoKV2hpY2ggc2FtcGxlcyBoYXZlIGhpZ2hlc3QgYWJ1bmRhbmNlcyBvZiB0aGUgY29udGFtaW5hbnRzPwoKYGBge3J9CmFzdjEgPC0gb3R1X3RhYmxlKHBzX3JhdylbLCAxXQpoZWFkKGFzdjFbb3JkZXIoYXN2MSwgZGVjcmVhc2luZyA9IFRSVUUpXSkKCmFzdjIgPC0gb3R1X3RhYmxlKHBzX3JhdylbLCAyXQpoZWFkKGFzdjJbb3JkZXIoYXN2MiwgZGVjcmVhc2luZyA9IFRSVUUpXSkKYGBgCgpIb3cgZG8gdGhlc2UgY29udGFtaW5hbnRzIG92ZXJsYXAgd2l0aCBBU1ZzIHByZXNlbnQgaW4gdGhlIG5lZ2F0aXZlIGNvbnRyb2w/CgpgYGB7cn0Kb3R1X3RhYmxlKHBzX3JhdylbNzA6NzUsIDE6MTFdCmBgYAoKQVNWcyBoYXZlIGhpZ2ggcmVhZCBjb3VudHMgaW4gdGhlIGV4dHJhY3Rpb24gbmVnYXRpdmUgY29udHJvbC4KVGh1cywgd2UgY2FuIGJlIGZhaXJseSBjZXJ0YWluIHRoZXNlIGFyZSBjb250YW1pbmFudHMgYW5kIG5vdCByZXByZXNlbnRhdGl2ZQpvZiBvdXIgZXhwZXJpbWVudGFsIHNhbXBsZXMuICAgCiAgIApSZW1vdmUgdGhlIGNvbnRhbWluYW50cyB0aGF0IHdlIGlkZW50aWZpZWQ6CgpgYGB7ciBwcnVuZV9kZWNvbnRhbX0KcHNfbm9uY29udGFtIDwtIHBydW5lX3RheGEoIWNvbnRhbV9kZl9mcmVxJGNvbnRhbWluYW50LCBwc19yYXcpCgpwc19ub25jb250YW0KYGBgCgpXaGF0IHByb3BvcnRpb24gb2Ygb3VyIGNvdW50IGRhdGEgd2VyZSByZW1vdmVkIGFzIGNvbnRhbWluYW50cz8KCmBgYHtyIGNvdW50X2RlY29udGFtMX0KcHJlIDwtIHN1bShzYW1wbGVfc3Vtcyhwc19yYXcpKQpwb3N0IDwtIHN1bShzYW1wbGVfc3Vtcyhwc19ub25jb250YW0pKQoKKHByZS1wb3N0KSAvIHByZQpgYGAKCiMjIyBSZW1vdmUgbm9uLWJhY3RlcmlhbCwgbm9uLWFyY2hhZWFsIEFTVnMKClRoZSBWNCByZWdpb24gb2YgMTZTIHJSTkEgaXMgY29uc2VydmVkIHdpdGhpbiBjZXJ0YWluIGJhY3RlcmlhLCBhcmNoYWVhLApjaGxvcm9wbGFzdHMsIG1pdG9jaG9uZHJpYSwgYW5kIGV1a2FyeW90ZXMuClNpbmNlIHdlIGFyZSBvbmx5IGludGVyZXN0ZWQgaW4gYmFjdGVyaWEgYW5kIGFyY2hhZWEgaGVyZSwKd2UnbGwgbmVlZCB0byByZW1vdmUgb3RoZXIgdGF4YSBmcm9tIG91ciBvYmplY3QuCgpgYGB7ciByZW1vdmVfbm9uYmFjfQojIENyZWF0ZSBwcyBzdWJzZXRzIGZvciBjaGxvcm9wbGFzdCwgbWl0b2Nob25kcmlhLCBhbmQgZXVrYXJ5b3RlczoKY2hsciA8LSBzdWJzZXRfdGF4YShwc19ub25jb250YW0sIE9yZGVyID09ICJDaGxvcm9wbGFzdCIpCm1pdCA8LSBzdWJzZXRfdGF4YShwc19ub25jb250YW0sIEZhbWlseSA9PSAiTWl0b2Nob25kcmlhIikKZXVrIDwtIHN1YnNldF90YXhhKHBzX25vbmNvbnRhbSwgS2luZ2RvbSA9PSAiRXVrYXJ5b3RhIikKCiMgR2V0IGFsbCB0YXhvbiBJRHMgdGhhdCB3ZSB3YW50IHRvIHJlbW92ZToKYmFkX3RheGEgPC0gYyh0YXhhX25hbWVzKGNobHIpLCB0YXhhX25hbWVzKG1pdCksIHRheGFfbmFtZXMoZXVrKSkKCiMgR2V0IGFsbCB0YXhvbiBJRHMgdGhhdCB3ZSB3YW50IHRvIGtlZXA6CmFsbF90YXhhIDwtIHRheGFfbmFtZXMocHNfbm9uY29udGFtKQpnb29kX3RheGEgPC0gYWxsX3RheGFbIShhbGxfdGF4YSAlaW4lIGJhZF90YXhhKV0KCiMgU3Vic2V0IHBoeWxvc2VxIG9iamVjdDoKcHNfYmFjX2FyYyA8LSBwcnVuZV90YXhhKGdvb2RfdGF4YSwgcHNfbm9uY29udGFtKQpgYGAKCldoYXQgcHJvcG9ydGlvbiBvZiBBU1ZzIHdlcmUga2VwdD8KCmBgYHtyIGtlcHRfYmFjfQpwcmUgPC0gc3VtKHNhbXBsZV9zdW1zKHBzX25vbmNvbnRhbSkpCnBvc3QgPC0gc3VtKHNhbXBsZV9zdW1zKHBzX2JhY19hcmMpKQpwb3N0IC8gcHJlCmBgYAogICAKRmluYWxseSwgd2UnbGwgYWxzbyByZW1vdmUgbm9uLWV4cGVyaW1lbnRhbCBzYW1wbGVzIChlLmcuIG5lZ2F0aXZlIGNvbnRyb2xzKS4KTm90ZSwgdGhvdWdoLCB0aGF0IG5vbi1leHBlcmltZW50YWwgc2FtcGxlcyBhcmUgaW1wb3J0YW50IGZvciBldmFsdWF0aW5nIHRoZQpxdWFsaXR5IG9mIHlvdXIgcmVhZ2VudHMsIGNvbnNpc3RlbmN5IGFjcm9zcyBzZXF1ZW5jaW5nIHJ1bnMsIGFuZCBjb250YW1pbmF0aW9uLgoKYGBge3Igc3Vic2V0X0hDQ30KcHMgPC0gc3Vic2V0X3NhbXBsZXMocHNfYmFjX2FyYywgRXhwZXJpbWVudCA9PSAiSENDIikKCnBzCmBgYAoKLS0tLS0KCiMjIEZpbHRlciBzYW1wbGVzCgpBZnRlciBoYXZpbmcgZmlsdGVyZWQgb3V0IHVud2FudGVkIHRheGEsCndlIG5vdyBuZWVkIHRvIGZpbHRlciBvdXQgdW5pbmZvcm1hdGl2ZSBzYW1wbGVzIC0tCnRob3NlIHdpdGggbG93IHRvdGFsIHRheG9uIGNvdW50cy4KCkZpcnN0LCBob3cgbWFueSBjb3VudHMgZG8gd2UgaGF2ZSBmb3IgZWFjaCBzYW1wbGU/CgpgYGB7ciBzdW1zfQpzdW1zIDwtIHNhbXBsZV9zdW1zKHBzKQpzdW1zW29yZGVyKHN1bXMpXQoKIyBMb3dlc3QgdG90YWwgY291bnQ6Cm1pbihzdW1zKQoKIyBTYW1wbGUgd2l0aCBsb3dlc3QgY291bnRzOgpuYW1lcyhzdW1zW29yZGVyKHN1bXMpXVsxXSkKYGBgCgpUbyByZW1vdmUgdW5pbmZvcm1hdGl2ZSBzYW1wbGVzLCB3ZSB3aWxsIG9ubHkga2VlcCB0aG9zZSB3aXRoIG92ZXIgMSwwMDAgY291bnRzLgpXZSB3aWxsIGFsc28gcmVtb3ZlIGEgdGVjaG5pY2FsIHJlcGxpY2F0ZSwKdGhvdWdoIG5vdGUgdGhhdCB0ZWNobmljYWwgcmVwbGljYXRlcyBhcmUgaW1wb3J0YW50IGZvciBxdWFsaXR5IGNvbnRyb2wgb2YgcnVucwphbmQgZm9yIHVuZGVyc3RhbmRpbmcgdGhlIHJlcHJvZHVjaWJpbGl0eSBvZiB0aGUgbWV0aG9kb2xvZ3kuIAoKYGBge3Igc3Vic2V0XzFrfQojIFJlbW92ZSBzYW1wbGVzIHdpdGggbG93IGNvdW50czoKcHMgPC0gc3Vic2V0X3NhbXBsZXMocHMsIHNhbXBsZV9zdW1zKHBzKSA+IDEwMDApCgojIFJlbW92ZSB0aGUgdGVjaG5pY2FsIHJlcGxpY2F0ZToKcHMgPC0gc3Vic2V0X3NhbXBsZXMocHMsIFNhbXBsZUlEICE9ICI1MDFTNCIpCmBgYAoKTGV0J3MgdGFrZSBhIGxvb2sgYXQgb3VyIGN1cnJlbnQgYHBoeWxvc2VxYCBvYmplY3Q6CgpgYGB7ciBsb29rX3BzfQpzYW1wbGVfZGF0YShwcylbMToxMF0KCmhlYWQodChvdHVfdGFibGUocHMpWzE6MTBdKSkKCmhlYWQodGF4X3RhYmxlKHBzKSkKYGBgCgotLS0tLQoKIyMgTm9ybWFsaXphdGlvbgoKTm9ybWFsaXphdGlvbiBpcyBjdXJyZW50bHkgYSBtdWNoLWRpc2N1c3NlZCBpc3N1ZSBvZiBtaWNyb2Jpb21lIHN0dWRpZXMuCkRpZmZlcmVuY2VzIGluIHJlYWQgZGVwdGggYmV0d2VlbiBzYW1wbGVzIG9mdGVuIG5lZWQgdG8gYmUgY29ycmVjdGVkIGJlZm9yZSBhbmFseXNpcy4KU2V2ZXJhbCBub3JtYWxpemF0aW9uIG1ldGhvZHMgaGF2ZSBiZWVuIHByb3Bvc2VkLCBhbmQgbm8gc2luZ2xlIG1ldGhvZCBpcyBwZXJmZWN0LgpJdCBtYXkgYmUgdGhhdCB0aGUgbW9zdCBhcHByb3ByaWF0ZSBtZXRob2QgZGVwZW5kcyBvbiB0aGUgYW5hbHlzaXMuCgpJbiB0aGlzIHR1dG9yaWFsLCB3ZSB3aWxsIHVzZToKICAKICAtIFByb3BvcnRpb24tbm9ybWFsaXplZCBkYXRhIHRvIGVzdGltYXRlIGVjb2xvZ2ljYWwgbWV0cmljcy4KICAKICAtIFN0YWJpbGl6aW5nIHRyYW5zZm9ybWF0aW9uIG5vcm1hbGl6ZWQgZGF0YSB0byBpZGVudGlmeSBkaWZmZXJlbnRpYWxseSBhYnVuZGFudCB0YXhhLgoKSG93ZXZlciwgZm9yIHlvdXIgcmVmZXJlbmNlLCB3ZSB3aWxsIGFsc28gcHJlc2VudCBjb2RlIGZvciB0d28gb3RoZXIgbm9ybWFsaXphdGlvbgptZXRob2RzOiByYXJlZmFjdGlvbiBhbmQgY3VtdWxhdGl2ZSBzdW0gc2NhbGluZy4KCkZvciBtb3JlIGRldGFpbHMsIHJlYWQKW01jTXVyZGllIGFuZCBIb2xtZXMgMjAxNF0oaHR0cHM6Ly9keC5wbG9zLm9yZy8xMC4xMzcxL2pvdXJuYWwucGNiaS4xMDAzNTMxKQphbmQgW01jS25pZ2h0IGV0IGFsLiAyMDE5XShodHRwOi8vZG9pLndpbGV5LmNvbS8xMC4xMTExLzIwNDEtMjEwWC4xMzExNSkuCgotLS0tLQoKIyMjIFByb3BvcnRpb24gTm9ybWFsaXphdGlvbgoKUHJvcG9ydGlvbiBub3JtYWxpemF0aW9uIGludm9sdmVzIGRpdmlkaW5nIGVhY2ggT1RVIGNvdW50IGJ5IHRoZSB0b3RhbCBzdW0gZm9yIGVhY2ggc2FtcGxlLgpUaGUgcmVzdWx0aW5nIGNvdW50IGRhdGEgd2lsbCBhZGQgdXAgdG8gMSAoMTAwJSkgZm9yIGVhY2ggc2FtcGxlLgoKVGhlIGBtaWNyb2Jpb21lOjp0cmFuc2Zvcm1gIGZ1bmN0aW9uIGNhbiBiZSB1c2VkIHRvIGVhc2lseSBub3JtYWxpemUgY291bnQgZGF0YSBhcwpwcm9wb3J0aW9ucyBpbiBhICpwaHlsb3NlcSogb2JqZWN0OgoKYGBge3Igbm9ybV9wcm9wb3J0aW9uc30KIyBQcm9wb3J0aW9uIG5vcm1hbGl6YXRpb246CnBzX3Byb3AgPC0gdHJhbnNmb3JtKHBzLCAiY29tcG9zaXRpb25hbCIpCgojIEhhdmUgYSBsb29rIGF0IHRoZSByZXN1bHRpbmcgT1RVIHRhYmxlOgpvdHVfdGFibGUocHNfcHJvcClbMTo1LCAxOjVdCgojIENoZWNrIHdoYXQgdGhlIHN1bXMgZm9yIGVhY2ggc2FtcGxlIGFyZSBub3c6CmhlYWQoc2FtcGxlX3N1bXMocHNfcHJvcCkpCmBgYAoKLS0tLS0KCiMjIyBWYXJpYW5jZSBzdGFiaWxpemluZyB0cmFuc2Zvcm1hdGlvbgoKVGhpcyB0cmFuc2Zvcm1hdGlvbiBtZXRob2QsIGF2YWlsYWJsZSBpbiBbREVTZXEyXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS93b3JrZmxvd3MvdmlnbmV0dGVzL3JuYXNlcUdlbmUvaW5zdC9kb2Mvcm5hc2VxR2VuZS5odG1sKSwKbm9ybWFsaXplcyBkYXRhIHdpdGggcmVzcGVjdCB0byBsaWJyYXJ5IHNpemUgYXMgd2VsbCBhcyBkaXNwZXJzaW9uLiAgIApUeXBlIGA/dnN0YCBvciByZWFkIFtBbmRlcnMgYW5kIEh1YmVyIDIwMTBdKGh0dHA6Ly9keC5kb2kub3JnLzEwLjExODYvZ2ItMjAxMC0xMS0xMC1yMTA2KQpmb3IgbW9yZSBpbmZvcm1hdGlvbi4KCmBgYHtyIG5vcm1fdnN0LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIEZpcnN0IHdlIGNvbnZlcnQgdGhlIHBoeWxvc2VxIG9iamVjdCB0byBhIERFU2VxIG9iamVjdDoKZGRzIDwtIHBoeWxvc2VxX3RvX2Rlc2VxMihwcywgfjEpCgojIFRoZW4gd2UgZXN0aW1hdGUgdGhlIHNpemUgZmFjdG9yczoKZGRzIDwtIGVzdGltYXRlU2l6ZUZhY3RvcnMoZGRzKQoKIyBOb3cgY2FuIGRvIHRoZSB0cmFuc2Zvcm1hdGlvbjoKZGRzX3ZzdCA8LSB2c3QoZGRzLCBibGluZCA9IFRSVUUpCgojIEhhdmUgYSBsb29rIGF0IHRoZSByZXN1bHRpbmcgY291bnRzOgp2c3RfY291bnRzIDwtIGFzc2F5KGRkc192c3QpCnQodnN0X2NvdW50cylbMTo1LCAxOjVdCmBgYAoKLS0tLS0KCiMjIyBSYXJlZmFjdGlvbgoKUmFyZWZhY3Rpb24gY2FuIGJlIHVzZWQgdG8gc3Vic2V0IGRhdGEgc3VjaCB0aGF0IHRoZSBsaWJyYXJ5IGRlcHRoIGlzIHRoZSBzYW1lCmZvciBlYWNoIHNhbXBsZS4KQmVjYXVzZSBzYW1wbGluZyBvZiB0aGUgZGF0YSBpcyByYW5kb20sCnJhcmVmYWN0aW9uIGNhbiBhY2NvdW50IGZvciBhbiBlZmZlY3Qgb2YgdG90YWwgcmVhZCBjb3VudCBvbiB0YXhhIHJpY2huZXNzLgpIb3dldmVyLCByYXJlZmFjdGlvbiBpcyBubyBsb25nZXIgY29uc2lkZXJlZCB0byBiZSBhIGdvb2Qgd2F5IHRvIG5vcm1hbGl6ZQphbXBsaWNvbiBzZXF1ZW5jaW5nIGRhdGEKKHNlZSBbTWNNdXJkaWUgJiBIb2xtZXMgMjAxNF0oaHR0cHM6Ly9keC5wbG9zLm9yZy8xMC4xMzcxL2pvdXJuYWwucGNiaS4xMDAzNTMxKSkuCgpgYGB7ciwgcmFyZWZ5LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwc19yYXJlZmllZCA8LSByYXJlZnlfZXZlbl9kZXB0aChwcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm5nc2VlZCA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZS5zaXplID0gbWluKHNhbXBsZV9zdW1zKHBzKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2UgPSBGQUxTRSkKCiMgSGF2ZSBhIGxvb2sgYXQgdGhlIHJlc3VsdDoKb3R1X3RhYmxlKHBzX3JhcmVmaWVkKVsxOjUsIDE6NV0KCmhlYWQoc2FtcGxlX3N1bXMocHNfcmFyZWZpZWQpKQpgYGAKCkNvbXBhcmUgdGhlc2Ugc2FtcGxlIHN1bXMgdG8gdGhlIG5vbi1yYXJpZmllZCBkYXRhIHN1bXM6CgpgYGB7ciwgbm9uLXJhcl9zdW1zfQpoZWFkKHNhbXBsZV9zdW1zKHBzKSkKYGBgCgotLS0tLQoKIyMjIEN1bXVsYXRpdmUgU3VtIFNjYWxpbmcKClRoZSAqbWV0YWdlbm9tZVNlcSogQ3VtdWxhdGl2ZSBTdW0gU2NhbGluZyAoQ1NTKSBub3JtYWxpemF0aW9uIGlzIGFub3RoZXIgb3B0aW9uCmRldmVsb3BlZCBmb3IgbWljcm9iaW9tZSBkYXRhLgpGb3IgbW9yZSBpbmZvcm1hdGlvbiwgcmVhZApbUGF1bHNvbiBldCBhbC4gMjAxM10oaHR0cDovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL25tZXRoLjI2NTgpLgoKYGBge3Igbm9ybV9jc3MsIG1lc3NhZ2U9RkFMU0V9CiMgQ29udmVydCB0aGUgcGh5bG9zZXEgb2JqZWN0IHRvIGEgbWV0YWdlbm9tZXNlcSBvYmplY3Q6Cm1nc19jc3MgPC0gcGh5bG9zZXFfdG9fbWV0YWdlbm9tZVNlcShwcykKCiMgUGVyZm9ybSB0aGUgQ3VtdWxhdGl2ZSBTdW0gU2NhbGluZzoKbWdzX2NzcyA8LSBjdW1Ob3JtKG1nc19jc3MpCgojIEV4dHJhY3QgdGhlIGNvdW50cyBhbmQgYWRkIHRoZW0gdG8gYSBzZXBhcmF0ZSBwaHlsb3NlcSBvYmplY3Q6CmNzc19jb3VudHMgPC0gTVJjb3VudHMobWdzX2Nzcywgbm9ybSA9IFRSVUUpCnBzX2NzcyA8LSBwcwpvdHVfdGFibGUocHNfY3NzKSA8LSBvdHVfdGFibGUodChjc3NfY291bnRzKSwgdGF4YV9hcmVfcm93cyA9IEZBTFNFKQpgYGAKCk5vdyBsZXRzIGNvbXBhcmUgdGhlIG9yaWdpbmFsIGRhdGEgdG8gdGhlIENTUyBub3JtYWxpemVkIGRhdGE6CgpgYGB7ciBjY3NfY29tcGFyZX0Kb3R1X3RhYmxlKHBzKVsxOjUsIDE6NV0KaGVhZChzYW1wbGVfc3VtcyhwcykpCgpvdHVfdGFibGUocHNfY3NzKVsxOjUsIDE6NV0KaGVhZChzYW1wbGVfc3Vtcyhwc19jc3MpKQpgYGAKCi0tLS0tCgojIyBQbG90IGFidW5kYW5jZXMKCkluIHRoaXMgYW5kIGZvbGxvd2luZyB0aGUgZXhhbXBsZXMgd2Ugd2lsbCB1c2UgdGhlICoqcHJvcG9ydGlvbnMqKiBkYXRhLgoKYGBge3IgcGxvdF9hYnVuZGFuY2V9CiMgU3Vic2V0IHRvIHRoZSBUMSB0cmVhdG1lbnQKVDEgPC0gc3Vic2V0X3NhbXBsZXMocHNfcHJvcCwgVHJlYXRtZW50ID09ICJUMSIpCgojIEFnZ2xvbWVyYXRlIHRheGEgdG8gcGh5bHVtIGxldmVsOgpUMV9waHlsdW0gPC0gdGF4X2dsb20oVDEsIHRheHJhbmsgPSAiUGh5bHVtIiwgTkFybSA9IEZBTFNFKQoKIyBSZW1vdmUgdW5jb21tb24gcGh5bGE6ClQxX3BoeWx1bSA8LSBzdWJzZXRfdGF4YShUMV9waHlsdW0sIHRheGFfc3VtcyhUMV9waHlsdW0pID4gMC4xKQoKIyBQbG90OgpwbG90X2JhcihUMV9waHlsdW0sIGZpbGwgPSAiUGh5bHVtIikgKwogICBmYWNldF93cmFwKH5UaW1lcG9pbnQsIHNjYWxlcyA9ICJmcmVlX3giLCBucm93ID0gMSkKYGBgCiAgCi0tLS0tCgojIyBBbHBoYSBkaXZlcnNpdHkKCkFscGhhIGRpdmVyc2l0eSBtZWFzdXJlcyB0YXhvbm9taWMgZGl2ZXJzaXR5IHdpdGhpbiBhIHNpbmdsZSBwb3B1bGF0aW9uLgpNZWFzdXJlcyBvZiBhbHBoYSBkaXZlcnNpdHkgaW5jbHVkZSB0YXhvbm9taWMgcmljaG5lc3MgKGkuZS4gbnVtYmVyIG9mIHRheGEpLAphbmQgaW5kaWNlcyB3aGljaCBjb21iaW5lIHRheG9ub21pYyByaWNobmVzcyB3aXRoIHNvbWUgbWVhc3VyZSBvZiBldmVubmVzcy4gICAKICAgCipOb3RlOiogTWFueSBvZiB0aGVzZSBtZXRob2RzIGFyZSBncmVhdGx5IGluZmx1ZW5jZWQgYnkgc2luZ2xldG9uIGRhdGEKKGkuZS4gdGF4YSByZXByZXNlbnRlZCBieSBhIHNpbmdsZSBjb3VudCksIG1lYW5pbmcgdGhleSBtYXkgYmUgdW5yZWxpYWJsZSBpZiB5b3VyCmRhdGEgZXhjbHVkZXMgc2luZ2xldG9ucy4KREFEQTIgZG9lcyBub3Qgb3V0cHV0IHNpbmdsZXRvbnMgaWYgaXQgaXMgcnVuIGluZGl2aWR1YWxseSBvbiBlYWNoIHNhbXBsZS4KVGh1cywgaWYgdXNpbmcgZGFkYTIgdG8gaW5mZXIgQVNWcywgaXQgaXMgYWR2aXNhYmxlIHRvIHVzZSBgcG9vbCA9IFRSVUVgIHdoZW4KcnVubmluZyB0aGUgZGFkYSBhbGdvcml0aG0uClRoaXMgd2lsbCBhbGxvdyBjYWxsaW5nIHBlci1zYW1wbGUgc2luZ2xldG9ucywgYnV0IG5vdCBwZXItc3R1ZHkgc2luZ2xldG9ucy4gICAKICAgCiMjIyBUYXhvbm9taWMgcmljaG5lc3M6IFJhcmVmYWN0aW9uIHBsb3QKCk91ciBnb2FsIGluIGRlZmluaW5nIHJpY2huZXNzIGlzIHRvIGRldGVybWluZSB0aGUgbnVtYmVyIG9mIHVuaXF1ZSB0YXhhIHdpdGhpbgplYWNoIHBvcHVsYXRpb24gKGluIG91ciBjYXNlLCBlYWNoIHBsb3QpLgpUaGlzIGlzIGRpZmZpY3VsdCwgYXMgb3VyIHNhbXBsZXMgY29udGFpbiBvbmx5IGEgcG9ydGlvbiBvZiB0aGUgdG90YWwgcmljaG5lc3MKd2l0aGluIG91ciBwbG90cy4KQXMgc2VxdWVuY2UgZGVwdGggaW5jcmVhc2VzIGZvciBlYWNoIHNhbXBsZSwgc28gZG9lcyB0aGUgbnVtYmVyIG9mIHRheGEuClRoaXMgY2FuIGJlIGlsbHVzdHJhdGVkIHdpdGggYSByYXJlZmFjdGlvbiBwbG90LiAgIAogICAKVG8gbWFrZSBhIHJhcmVmYWN0aW9uIHBsb3QsIHdlIGRyYXcgcmFuZG9tIHNhbXBsZXMgZnJvbSBvdXIgZGF0YSBhbmQgY291bnQgdGhlCm51bWJlciBvZiB1bmlxdWUgQVNWcyBhcyBzYW1wbGVzIGFyZSBkcmF3bi4KVGhlIHJlc3VsdGluZyByYXJlZmFjdGlvbiBjdXJ2ZSBpcyBleHBlY3RlZCB0byByaXNlIHF1aWNrbHkgdGhlbiBwbGF0ZXUgYXMgdGhlCm1vc3QgYWJ1bmRhbnQgdGF4YSBhcmUgcmVwcmVzZW50ZWQuCldlIGNhbiBtYWtlIGEgcXVpY2sgcmFyZWZhY3Rpb24gY3VydmUgcGxvdCBkaXJlY3RseSBmcm9tIG91ciBgcGh5bG9zZXFgIG9iamVjdCBvZgphbGwgc2FtcGxlcyB1c2luZyB0aGUKW2B2ZWdhbmAgcGFja2FnZV0oaHR0cHM6Ly9wZWF0LWNsYXJrLmdpdGh1Yi5pby9CSU8zODEvdmVnYW5UdXRvcmlhbC5odG1sKToKCmBgYHtyIHJhcmVjdXJ2ZX0KcmFyZWN1cnZlKG90dV90YWJsZShwcyksIHN0ZXAgPSA1MDAsIHhsYWIgPSAiU2FtcGxlIFNpemUiLCB5bGFiID0gIlRheGEiKQpgYGAKICAgCldlIGNhbiBhbHNvIHNwbGl0IHJhcmVmYWN0aW9uIGN1cnZlcyBieSBncm91cCB1c2luZyB0aGUKW2BhbXB2aXMyYCBwYWNrYWdlXShodHRwczovL21hZHNhbGJlcnRzZW4uZ2l0aHViLmlvL2FtcHZpczIvYXJ0aWNsZXMvYW1wdmlzMi5odG1sKS4KCmBgYHtyIHJhcmVmYWN0aW9uMX0KbWV0YWRhdGEgPC0gZGF0YS5mcmFtZShzYW1wbGVfZGF0YShwcyksIGNoZWNrLm5hbWVzID0gRkFMU0UpCgphc3ZfdGFibGUgPC0gZGF0YS5mcmFtZSh0KG90dV90YWJsZShwcykpLAogICAgICAgICAgICAgICAgICAgICAgIHRheF90YWJsZShwcyksCiAgICAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGQUxTRSkKYGBgCgpgYW1wdmlzMmAgcmVxdWlyZXMgYSBgU3BlY2llc2AgY29sdW1uLCB3aGljaCBtdXN0IGJlIGFkZGVkIHRvIG91ciBkYXRhLgpJZiB5b3VyIHRhYmxlIGFscmVhZHkgaGFzIGEgYFNwZWNpZXNgIGNvbHVtbiwgc2tpcCB0aGlzIHN0ZXA6CgpgYGB7ciBhZGRfU3BlY2llc30KYXN2X3RhYmxlJFNwZWNpZXMgPC0gTkEKYGBgCgpMb2FkIGFuIGBhbXB2aXMyYCBvYmplY3QgYW5kIGNhbGN1bGF0ZSByYXJlZmFjdGlvbjoKCmBgYHtyIHJhcmVjdXJ2ZV9hbXB2aXMyLCBjYWNoZT1UUlVFLCB3YXJuaW5nPUZBTFNFfQpwczMgPC0gYW1wX2xvYWQoYXN2X3RhYmxlLCBtZXRhZGF0YSkKCnJhcl9wbG90IDwtIGFtcF9yYXJlY3VydmUocHMzLAogICAgICAgICAgICAgICAgICAgICAgICAgIHN0ZXBzaXplID0gMTAwLAogICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2V0X2J5ID0gIlRpbWVwb2ludCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JfYnkgPSAiVHJlYXRtZW50IikgKwogICB5bGFiKCJOdW1iZXIgb2Ygb2JzZXJ2ZWQgQVNWcyIpCgpyYXJfcGxvdApgYGAKICAgCkFzIHdlIGNhbiBzZWUsIGFzIHJlYWQgZGVwdGggaW5jcmVhc2VzIGZvciBlYWNoIHNhbXBsZSwgc28gZG9lcyB0YXhvbm9taWMgcmljaG5lc3MuCkluIGFsbCBzYW1wbGVzLCBpbmNyZWFzZWQgc2FtcGxpbmcgd291bGQgcmVzdWx0IGluIGluY3JlYXNlZCByaWNobmVzcy4KVGh1cywgb3VyIG9ic2VydmVkIHNhbXBsZSByaWNobmVzcyBpcyBsb3dlciB0aGFuIHRoZSB0cnVlIHBvcHVsYXRpb24gcmljaG5lc3MgZm9yCmV2ZXJ5IHBsb3QuCgpGdXJ0aGVyLCBzZXF1ZW5jZSBkZXB0aCBhbmQgdG90YWwgcmljaG5lc3MgYXBwZWFyIHRvIGJlIGNvcnJlbGF0ZWQgYWNyb3NzIHNhbXBsZXM6CgpgYGB7ciBjb3JyLCB3YXJuaW5nPUZBTFNFfQpkcF9ieV9hc3YgPC0gZGF0YS5mcmFtZShjdF9zdW1zID0gc2FtcGxlX3N1bXMocHMpLAogICAgICAgICAgICAgICAgICAgICAgICBhc3ZzID0gcm93U3VtcyhvdHVfdGFibGUocHMpICE9IDApKQoKZ2dwbG90KGRhdGEgPSBkcF9ieV9hc3YpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gIGN0X3N1bXMsIHkgPSBhc3ZzKSkgKwogIGxhYnMoeCA9ICJTZXF1ZW5jZSBkZXB0aCIsIHk9ICJBU1YgY291bnQiKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKV2UgY2FuIHRlc3Qgd2hldGhlciB0aGlzIGNvcnJlbGF0aW9uIGlzIHNpZ25pZmljYW50OgoKYGBge3J9CmNvciA8LSBjb3IudGVzdChkcF9ieV9hc3YkY3Rfc3VtcywgZHBfYnlfYXN2JGFzdnMsIG1ldGhvZCA9ICJzcGVhcm1hbiIpCmNvcgpgYGAKCkhlcmUgd2Ugb2JzZXJ2ZSBhIHNpZ25pZmljYW50IGNvcnJlbGF0aW9uIGJldHdlZW4gcmVhZCBjb3VudCBhbmQgb2JzZXJ2ZWQKdGF4b25vbWljIHJpY2huZXNzLiAgIAogICAKVGh1cywgYSBzYW1wbGUgZnJvbSBhIHBvcHVsYXRpb24gd2l0aCBoaWdoIGFjdHVhbCBkaXZlcnNpdHksIGJ1dCBsb3cgcmVhZCBkZXB0aCwKY291bGQgaGF2ZSBsb3cgb2JzZXJ2ZWQgcmljaG5lc3MuCkZvcnR1bmF0ZWx5LCB0aGVyZSBhcmUgbWV0aG9kcyB0byBlc3RpbWF0ZSB0aGUgbnVtYmVyIG9mIHVub2JzZXJ2ZWQgdGF4YS4KRm9yIGFuIG92ZXJ2aWV3IG9mIHRoZXNlIG1ldGhvZHMsIHJlYWQKW0J1bmdlIGV0IGFsLiAyMDE0XShodHRwOi8vd3d3LmFubnVhbHJldmlld3Mub3JnL2RvaS8xMC4xMTQ2L2FubnVyZXYtc3RhdGlzdGljcy0wMjI1MTMtMTE1NjU0KS4KICAgCiMjIyBGcmVxdWVuY3kgY291bnRzCgpUbyBpbGx1c3RyYXRlIHRoZSBkaWZmaWN1bHR5IG9mIGVzdGltYXRpbmcgcG9wdWxhdGlvbiByaWNobmVzcyBhbmQgaW1wb3J0YW5jZSBvZgpzaW5nbGV0b25zIHRvIGFscGhhIGRpdmVyc2l0eSBtZXRyaWNzLAp3ZSdsbCBsb29rIGF0IHRoZSBmcmVxdWVuY3kgY291bnQgZGlzdHJpYnV0aW9uIGZvciBvdXIgZmlyc3Qgc2FtcGxlOiAxMDEtUzEuCgpXZSdsbCBjb3VudCB0aGUgbnVtYmVyIG9mIHNpbmdsZXRvbnMsIGRvdWJsZXRvbnMsIGV0Yy4sCnRoZW4gcGxvdCBmcmVxdWVuY3kgYXMgYSBmdW5jdGlvbiBvZiBjb3VudC4KCmBgYHtyIGZyZXFfY3QsIG1lc3NhZ2U9RkFMU0V9CiMgR2V0IHRoZSBmcmVxdWVuY3kgY291bnQgZGlzdHJpYnV0aW9uIGZvciB0aGUgMXN0IHNhbXBsZToKZnJlcXVlbmN5dGFibGVsaXN0IDwtIGJ1aWxkX2ZyZXF1ZW5jeV9jb3VudF90YWJsZXMob3R1X3RhYmxlKHBzKSkKZnJlcV9jdCA8LSBmcmVxdWVuY3l0YWJsZWxpc3RbWzFdXQpjb2xuYW1lcyhmcmVxX2N0KSA8LSBjKCJGcmVxdWVuY3kiLCAiQ291bnQiKQoKIyBQbG90OgpnZ3Bsb3QoZnJlcV9jdCwgYWVzKEZyZXF1ZW5jeSwgQ291bnQpKSArIAogIGdlb21fcG9pbnQoKSArIAogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgNjAwLCBieSA9IDUwKSkgKyAKICBnZ3RpdGxlKCIxMDEtUzEiKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAogICAKV2UgY2FuIHNlZSB0aGVyZSBhcmUgfjMsMjAwIHRheGEgcmVwcmVzZW50ZWQgYXMgc2luZ2xldG9ucywKYW5kIH4xLDYwMCB0YXhhIGFzIGRvdWJsZXRvbnMuIApUaGlzIGlzIGEgaGlnaCBwcm9wb3J0aW9uIG9mIHRoZSB0b3RhbCBudW1iZXIgdGF4YSBhcmUgb2JzZXJ2ZWQgaW4gdGhpcyBzYW1wbGU6CgpgYGB7cn0KIyBUb3RhbCBudW1iZXIgb2YgdGF4YToKc3VtKGZyZXFfY3QkQ291bnQpCgojIFByb3BvcnRpb24gb2Ygc2luZ2xldG9ucwpmcmVxX2N0JENvdW50WzFdIC8gc3VtKGZyZXFfY3QkQ291bnQpCmBgYAoKSG93IG1hbnkgbW9yZSB0YXhhIG1pZ2h0IHdlIG9ic2VydmUgaWYgd2UgaW5jcmVhc2VkIG91ciBzYW1wbGluZz8gICAKCiMjIyBEaXZlcnNpdHkgSW5kaWNlcwoKV2UgY2FuIHVzZSBzZXZlcmFsIGluZGljZXMgdG8gZXhwbG9yZSBob3cgZXZlbmx5IHNwZWNpZXMgYXJlIGRpc3RyaWJ1dGVkIHdpdGhpbgplYWNoIHNhbXBsZS4gTm90ZSB0aGF0ICdPYnNlcnZlZCcgaXMgdGhlIHNhbXBsZSB0YXhvbm9taWMgcmljaG5lc3MuClRoZSBvdGhlciB0aHJlZSBpbmRpY2VzIGNvbWJpbmUgcmljaG5lc3MgYW5kIGFidW5kYW5jZSBkYXRhIGZvciBlYWNoIHRheG9uLgoKYGBge3IgYWxwaGFfZGl2fQpwbG90X3JpY2huZXNzKHBzLAogICAgICAgICAgICAgIHggPSAiVGltZXBvaW50IiwKICAgICAgICAgICAgICBzaGFwZSA9ICJUcmVhdG1lbnQiLAogICAgICAgICAgICAgIGNvbG9yID0gIlRyZWF0bWVudCIsIAogICAgICAgICAgICAgIG1lYXN1cmVzID0gYygiT2JzZXJ2ZWQiLCAiU2hhbm5vbiIsICJTaW1wc29uIiwgIkludlNpbXBzb24iKSkKYGBgCiAgIApOb3RpY2UgdGhhdCBzZXZlcmFsIFQyOlMzIHNhbXBsZXMgaGF2ZSBsb3cgbWVhc3VyZXMsCnN1Z2dlc3RpbmcgdGhleSBoYXZlIGxvdyBkaXZlcnNpdHkgKGkuZS4gYWJ1bmRhbmNlcyBhcmUgZG9taW5hdGVkIGJ5IGEgZmV3IHRheGEpCmNvbXBhcmVkIHRvIHRoZSBvdGhlciBzYW1wbGVzLgogCi0tLS0tCgojIyBCZXRhIGRpdmVyc2l0eQoKQmV0YSBkaXZlcnNpdHkgbWVhc3VyZXMgZGlmZmVyZW5jZXMgaW4gbWljcm9iaWFsIGNvbXBvc2l0aW9ucyBiZXR3ZWVuIHBvcHVsYXRpb25zLgpJZiBhdmFpbGFibGUgKHVzZSBwc18xNlNfVjRfd2l0aHRyZWUucmRzKSwgd2UgY2FuIHVzZSBhIHBoeWxvZ2VuZXRpYyB0cmVlIGFuZCBvdHUKdGFibGUgdG8gZXN0aW1hdGUgdGhlIFtVbmlGcmFjXShodHRwczovL2RvaS5vcmcvMTAuMTEyOC9BRU0uNzEuMTIuODIyOC04MjM1LjIwMDUpCmZvciBlYWNoIHNhbXBsZSBwYWlyLgoKVGhlIHJlc3VsdCBpcyBhIGRpc3RhbmNlIG1hdHJpeCB3aXRoIGhlaWdodCBhbmQgd2lkdGggZXF1YWwgdG8gdGhlIG51bWJlciBvZiBzYW1wbGVzLgpVbmlGcmFjIG1heSBiZSBbd2VpZ2h0ZWQgb3IgdW53ZWlnaHRlZF0oaHR0cHM6Ly9kb2kub3JnLzEwLjExMjgvQUVNLjAxOTk2LTA2KS4KU2VsZWN0aW9uIG9mIHdlaWdodGVkIG9yIHVud2VpZ2h0ZWQgd2lsbCBkZXBlbmQgb24gdGhlIHF1ZXN0aW9uIHlvdSB3YW50IHRvIGFuc3dlci4KVW53ZWlnaHRlZCBVbmlGcmFjIHVzZXMgb25seSBwcmVzZW5jZSBvciBhYnNlbmNlIG9mIGEgdGF4b24gZnJvbSB0aGUgb3R1IHRhYmxlCihpLmUuIDEwMDAgd2lsbCBiZSB0cmVhdGVkIHRoZSBzYW1lIGFzIDEpLgpJdCBpcyBhcHByb3ByaWF0ZSBpZiB5b3Ugd2FudCB0byB0ZXN0IHF1YWxpdGF0aXZlIHRheGEgZGlmZmVyZW5jZXMgYmV0d2VlbiBzYW1wbGVzLgpXZWlnaHRlZCBVbmlGcmFjIHVzZXMgcHJlc2VuY2UgYXMgd2VsbCBhcyBxdWFudGl0eSBkYXRhIChpLmUuIDEgPCAxMDAwKS4KCldlIGFyZSBpbnRlcmVzdGVkIHRvIGtub3cgd2hhdCBjaGFuZ2VzIHRvIHRheG9uIGFidW5kYW5jZSBtYXkgYmUgZHJpdmVuIGJ5IG91ciB0cmVhdG1lbnRzLCBzbyB3ZSB3aWxsIHVzZSB3ZWlnaHRlZCBVbmlGcmFjLiAgICAKCiMjIyBPcmRpbmF0aW9uCgpGaXJzdCB3ZSdsbCBtYWtlIGEgd2VpZ2h0ZWQgVW5pRnJhYyBkaXN0YW5jZSBtYXRyaXguCkZvciBVbmlGcmFjLCB5b3UgcmVxdWlyZSBhIHRyZWUgYXMgcGFydCBvZiB5b3VyICpwaHlsb3NlcSogb2JqZWN0LgoKYGBge3Igd1VGLCB3YXJuaW5nPUZBTFNFLCBjYWNoZT1GQUxTRX0Kc2V0LnNlZWQoMTAwKQp3VUYgPC0gcGh5bG9zZXE6OmRpc3RhbmNlKHBzX3Byb3AsIG1ldGhvZCA9ICJ3dW5pZnJhYyIpCgphcy5tYXRyaXgod1VGKVsxOjYsIDE6Nl0KCiMgTm90ZTogSWYgeW91ciAqcGh5bG9zZXEqIG9iamVjdCBsYWNrcyBhIHBoeWxvZ2VuZXRpYyB0cmVlLAojIHlvdSBjb3VsZCB1c2UgYSBkaXNzaW1pbGFyaXR5IGluZGV4IHRoYXQgZG9lcyBub3QgcmVxdWlyZSBvbmUsIHN1Y2ggYXMgQnJheS1DdXJ0aXM6CiMgYnJheSA8LSB2ZWdhbjo6dmVnZGlzdChvdHVfdGFibGUocHNfcHJvcCksIG1ldGhvZCA9ICJicmF5IikKIyBhcy5tYXRyaXgoYnJheSlbMTo2LDE6Nl0KYGBgCgpUaGVuIHBlcmZvcm0gUHJpbmNpcGFsIENvb3JkaW5hdGUgQW5hbHlzaXMgKFBDb0EpIG9uIHlvdXIgZmF2b3JpdGUgZGlzdGFuY2UgbWF0cml4LgpJbiB0aGlzIGNhc2UsIHdlIGFyZSB1c2luZyB3ZWlnaHRlZCBVbmlGcmFjLgoKYGBge3Igb3JkaW5hdGUsIHdhcm5pbmc9RkFMU0UsIGNhY2hlPUZBTFNFfQpkbyA8LSBvcmRpbmF0ZShwc19wcm9wLCBtZXRob2QgPSAiUENvQSIsIGRpc3RhbmNlID0gd1VGKQoKYmFzZXBsb3QgPC0gcGxvdF9vcmRpbmF0aW9uKHBzX3Byb3AsIGRvLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJzYW1wbGVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIlRyZWF0bWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGFwZSA9ICJUaW1lcG9pbnQiKSArCiAgZ2d0aXRsZSgiSENDIG9yZGluYXRpb24sIGFsbCBzYW1wbGVzXG5tZXRob2Q9UENvQSwgZGlzdGFuY2U9d2VpZ2h0ZWQgVW5pRnJhYyIpICsKICB0aGVtZV9idygpCgojIFNpbmdsZSBwbG90IHdpdGggYWxsIHNhbXBsZXM6CmJhc2VwbG90ICsKICBnZW9tX3BvaW50KHNpemUgPSAyKQogIAojIElkZW0sIGJ1dCB3aXRoIHNhbXBsZSBuYW1lcyBvbiB0aGUgcGxvdDogCmJhc2VwbG90ICsKICBnZW9tX3BvaW50KHNpemUgPSAxKSArIAogIGdlb21fbGFiZWwoYWVzKGxhYmVsID0gU2FtcGxlSUQpLCBzaXplID0gMy41LCBjb2xvciA9ICJibGFjayIpCgojIEZhY2V0IGJ5IHRpbWUgcG9pbnQ6CmJhc2VwbG90ICsKICBnZW9tX3BvaW50KHNpemUgPSAyKSArIAogIGdndGl0bGUoIkhDQyBvcmRpbmF0aW9uLCBhbGwgc2FtcGxlc1xubWV0aG9kPVBDb0EsIGRpc3RhbmNlPXdlaWdodGVkIFVuaUZyYWMiKSArCiAgZmFjZXRfd3JhcCh+VGltZXBvaW50LCAyKQpgYGAKICAgCldlIGNhbiBzZWUgc29tZSBncm91cGluZyBieSB0aW1lcG9pbnQuCkRlcGVuZGluZyBvbiB5b3VyIGV4cGVyaW1lbnQgYW5kIHF1ZXN0aW9ucywKeW91IG1pZ2h0IHdhbnQgdG8gY29uc2lkZXIgb3RoZXIgb3JkaW5hdGlvbiBvciBjbHVzdGVyaW5nIGFwcHJvYWNoZXMuCgojIyMgUGVybXV0YXRpb25hbCBhbmFseXNpcyBvZiB2YXJpYW5jZSAoUEVSTUFOT1ZBKQoKRmluYWxseSwgd2UnbGwgdGVzdCB3aGV0aGVyIHRoZXJlIGlzIGEgc2lnbmlmaWNhbnQgZWNvbG9naWNhbCBsZXZlbCB0cmVhdG1lbnQgZWZmZWN0LgoqUEVSTUFOT1ZBKiBzb3VuZHMgZmFuY3ksIGJ1dCBpdCBpcyBqdXN0IGFuICpBTk9WQSogcGVyZm9ybWVkIHVzaW5nIHBlcm11dGF0aW9ucy4KUGVybXV0YXRpb25zIGFyZSB1c2VkIHRvIGRldGVybWluZSBob3cgZGF0YSBtYXkgYXBwZWFyIGlmIHRoZXJlIGlzIG5vIHRyZWF0bWVudAplZmZlY3QgYW5kIGdyb3VwIGRpZmZlcmVuY2VzIGFyZSBkdWUgdG8gcmFuZG9tIGNoYW5jZS4gT2JzZXJ2ZWQgZGF0YSBhcmUgdGhlbgpjb21wYXJlZCB0byB0aGUgcmFuZG9taXplZCBkYXRhIHRvIGNhbGN1bGF0ZSBhIHAtdmFsdWUuICAgCiAgIApUcmVhdG1lbnRzIGFyZSBpbmRlcGVuZGVudCB3aXRoaW4gVGltZXBvaW50cywKc28gd2Ugd2lsbCBmaXJzdCBzdWJzZXQgdGhlIGRhdGEgYnkgVGltZXBvaW50OgoKYGBge3Igc3Vic2V0X1RpbWVwb2ludHMsIHdhcm5pbmc9RkFMU0V9CnBzX1MxIDwtIHN1YnNldF9zYW1wbGVzKHBzX3Byb3AsIFRpbWVwb2ludCA9PSAiUzEiKQpwc19TMyA8LSBzdWJzZXRfc2FtcGxlcyhwc19wcm9wLCBUaW1lcG9pbnQgPT0gIlMzIikKcHNfUzQgPC0gc3Vic2V0X3NhbXBsZXMocHNfcHJvcCwgVGltZXBvaW50ID09ICJTNCIpCmBgYAoKU3Vic2V0IHRoZSBtZXRhZGF0YSBhbmQgZXN0aW1hdGUgVW5pRnJhYyBmb3IgUzM6CgpgYGB7ciBzZXRfcGVybSwgd2FybmluZz1GQUxTRX0KbWV0YWRhdGEgPC0gYXMoc2FtcGxlX2RhdGEocHNfUzMpLCAiZGF0YS5mcmFtZSIpCnVuaWZyYWNfZGlzdCA8LSBVbmlGcmFjKHBzX1MzLCB3ZWlnaHRlZCA9IFRSVUUpCmBgYAoKVGhlbiB3ZSdsbCB1c2UgYHZlZ2FuOjphZG9uaXMyYCB0byBwZXJmb3JtIHRoZSAqUEVSTUFOT1ZBKi4KCmBgYHtyIFBFUk1BTk9WQX0Kc2V0LnNlZWQoMTAwKSAgIyBTZXQgdGhlIHNlZWQgdG8gbWFrZSB0aGUgYW5hbHlzaXMgcmVwcm9kdWNpYmxlCgpwZXJtYW5vdmEgPC0gYWRvbmlzMih1bmlmcmFjX2Rpc3QgfiBUcmVhdG1lbnQsCiAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBtZXRhZGF0YSwKICAgICAgICAgICAgICAgICAgICAgcGVybXV0YXRpb25zID0gMTAwMDApCgpwZXJtYW5vdmEKYGBgCgpUaGUgbG93IHAtdmFsdWUgc3VnZ2VzdHMgdGhhdCB3aXRoaW4gdGhlIFMzIHRpbWVwb2ludCAocG9zdC10ZXJtaW5hdGlvbiksCnRoZXJlIGlzIGEgc2lnbmlmaWNhbnQgdHJlYXRtZW50IGVmZmVjdC4gICAKIAotLS0tLQoKIyMgRGlmZmVyZW50aWFsIHRheG9uIGFidW5kYW5jZSAoREVTZXEpCgpIZXJlLCB3ZSB3aWxsIHVzZQpbREVTZXEyXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS93b3JrZmxvd3MvdmlnbmV0dGVzL3JuYXNlcUdlbmUvaW5zdC9kb2Mvcm5hc2VxR2VuZS5odG1sKQp0byBpZGVudGlmeSBkaWZmZXJlbnRpYWxseSBhYnVuZGFudCB0YXhhIGJldHdlZW4gdGltZSBwb2ludHMgYW5kIHRyZWF0bWVudHMuCipERVNlcTIqIHdhcyBkZXNpZ25lZCB0byBhbmFseXplIFJOQXNlcSBkYXRhc2V0cywgd2hpY2ggYXJlIHNpbWlsYXIgdG8gT1RVL0FTViBkYXRhIApzZXRzIGluIHRoYXQgYm90aCBoYW5kbGUgbGFyZ2UsIHNwYXJzZSBjb250aW5nZW5jeSB0YWJsZXMgZ2VuZXJhdGVkIGZyb20gSWxsdW1pbmEKc2VxdWVuY2luZyBkYXRhLgpGb3IgbW9yZSBkZXRhaWxzLCBzZWUKW01jTXVyZGllIGFuZCBIb2xtZXMgMjAxNF0oaHR0cHM6Ly9keC5wbG9zLm9yZy8xMC4xMzcxL2pvdXJuYWwucGNiaS4xMDAzNTMxKS4gIAogIApGaXJzdCwgd2UnbGwgY29udmVydCBvdXIgbm9uLW5vcm1hbGl6ZWQgY291bnQgZGF0YSB0byBhICpERVNlcSogb2JqZWN0LgoKYGBge3IgcHMyZHNzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkZHMgPC0gcGh5bG9zZXFfdG9fZGVzZXEyKHBzLCB+VGltZXBvaW50ICsgVHJlYXRtZW50KQoKZGRzCmNvbERhdGEoZGRzKQpgYGAKClRoZXJlIGFyZSB0d28gd2F5cyB0byBhbmFseXplIGludGVyYWN0aW9uIGVmZmVjdHMgdXNpbmcgKkRFU2VxMiouClRoZSBmaXJzdCBpcyB0byBmaXQgYSBtdWx0aXZhcmlhdGUgbW9kZWwgKGUuZy4gfkErQitBOkIpIGFuZCBleHBsb3JlIHRoZSBtb2RlbApjb2VmZmljaWVudHMuClRoZSBzZWNvbmQgaXMgdG8gZml0IGEgdW5pdmFyaWF0ZSBtb2RlbCBhbmQgZXhwbG9yZSBwYWlyd2lzZSBjb250cmFzdHMuCkhlcmUsIHdlIHdpbGwgZ3JvdXAgb3VyIHR3byBmYWN0b3JzIChUcmVhdG1lbnQgYW5kIFRpbWVwb2ludCkgYW5kIHVzZSB0aGUgbGF0dGVyCmFwcHJvYWNoLgoKYGBge3IgZGVnLCBtZXNzYWdlPUZBTFNFLCBjYWNoZT1GQUxTRX0KIyBQcmVwYXJlIHRoZSBkYXRhOgpkZHMkR3JvdXAgPC0gZmFjdG9yKHBhc3RlMChkZHMkVHJlYXRtZW50LCBkZHMkVGltZXBvaW50KSkKZGVzaWduKGRkcykgPC0gZm9ybXVsYSh+R3JvdXApCmRkcyA8LSBkZHNbLCAtNTBdICAgICAgICAgICAgICAgICAgICAjIFJlbW92ZSB0ZWNoX3JlcCBzYW1wbGUKZGRzJEdyb3VwIDwtIGRyb3BsZXZlbHMoZGRzJEdyb3VwKSAgICMgRHJvcCB0ZWNoX3JlcCBsZXZlbCBmcm9tIGRlc2lnbiBtYXRyaXgKCiMgUGVyZm9ybSB0aGUgZGlmZmVyZW50aWFsIGFidW5kYW5jZSBhbmFseXNpczoKZGRzIDwtIERFU2VxKGRkcykKCnJlc3VsdHMoZGRzKQpgYGAKICAgIApMZXQncyBleHBsb3JlIHRoZSBjb250cmFzdCwgIlQxUzQgOiBUNFM0Igoobm8gY2MsIHNlZWRsaW5nIHN0YWdlIDogbGF0ZSB0ZXJtaW5hdGlvbiwgc2VlZGxpbmcgc3RhZ2UpOgoKYGBge3J9CnJlcyA8LSByZXN1bHRzKGRkcywgY29udHJhc3QgPSBjKCJHcm91cCIsICJUMVM0IiwgIlQ0UzQiKSkKc3VtbWFyeShyZXMpCgojIE51bWJlciBvZiB0YXhhIHdpdGggc2lnbmlmaWNhbnRseSBoaWdoZXIgYWJ1bmRhbmNlIGluIFQ0UzQ6CnN1bShyZXMkcGFkaiA8IDAuMSAmIHJlcyRsb2cyRm9sZENoYW5nZSA+IDAsIG5hLnJtID0gVFJVRSkKCiMgTnVtYmVyIG9mIHRheGEgd2l0aCBzaWduaWZpY2FudGx5IGxvd2VyIGFidW5kYW5jZSBpbiBUNFM0OgpzdW0ocmVzJHBhZGogPCAwLjEgJiByZXMkbG9nMkZvbGRDaGFuZ2UgPCAwLCBuYS5ybSA9IFRSVUUpCmBgYAoKV2UgY2FuIHNvcnQgcmVzdWx0cyBieSBwLXZhbHVlIGFuZCBsb29rIGF0IHRoZSB0b3AgQVNWczoKCmBgYHtyfQpyZXMub3JkZXIgPC0gcmVzW29yZGVyKHJlcyRwdmFsdWUpLCBdCmhlYWQocmVzLm9yZGVyKVtjKDE6MiwgNildCmBgYAoKQW5kIHBsb3QgY291bnRzIGZvciB0aGUgZmlyc3QgaW4gdGhlIG9yZGVyZWQgbGlzdCBBU1Y6CgpgYGB7ciBwbG90X2NvdW50c30KcGxvdENvdW50cyhkZHMsIGdlbmUgPSByZXMub3JkZXJAcm93bmFtZXNbMV0sIGludGdyb3VwID0gIkdyb3VwIikKYGBgCiAgIApXZSBjYW4gYWxzbyBwbG90IGNvdW50cyBieSBzYW1wbGVfSUQ6CgpgYGB7ciBwbG90X2NvdW50czJ9CnBsb3RDb3VudHMoZGRzLCBnZW5lID0gIkFTVl80OCIsIGludGdyb3VwID0gIkdyb3VwIikKYGBgCgpUaGUgcGxvdCBjYW4gYWxzbyBiZSBzYXZlZCBhbmQgY3VzdG9taXplZCB3aXRoIGdncGxvdDoKCmBgYHtyfQpkIDwtIHBsb3RDb3VudHMoZGRzLCBnZW5lID0gIkFTVl80OCIsIGludGdyb3VwID0gIkdyb3VwIiwgcmV0dXJuRGF0YSA9IFRSVUUpCgpnZ3Bsb3QoZCwgYWVzKHggPSBHcm91cCwgeSA9IGNvdW50KSkgKwogIGdlb21fcG9pbnQocG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXIodyA9IDAuMSwgaCA9IDApLCBzaXplID0gMikgKwogIHNjYWxlX3lfbG9nMTAoKSArCiAgZ2d0aXRsZShleHByZXNzaW9uKHBhc3RlKCJBU1YgNDgsIGxvZyJbMTBdLCAiLXNjYWxlIikpKSArCiAgdGhlbWVfYncoKQpgYGAKICAgCldlIGNhbiBhbHNvIHN1YnNldCBvdXIgZGlmZmVyZW50aWFsbHkgYWJ1bmRhbnQgdGF4YSB0byBrZWVwIHRob3NlIHdpdGggYW4KYWRqdXN0ZWQgcC12YWx1ZSA8IDAuMDU6CgpgYGB7ciBmaWx0ZXJfMDV9CnNpZyA8LSBzdWJzZXQocmVzLm9yZGVyLCBwYWRqIDw9IDAuMDUpCmhlYWQoc2lnKQpgYGAKCldoYXQgdGF4YSBhcmUgYXNzb2NpYXRlZCB3aXRoIHNpZ25pZmljYW50bHkgZGlmZmVyZW50aWFsbHkgYWJ1bmRhbnQgQVNWcz8KCmBgYHtyIHNpZ190YXhhfQpzaWdfYXN2IDwtIHJvd25hbWVzKHNpZykKc2lnX3RheGEgPC0gdGF4X3RhYmxlKHBzKVtzaWdfYXN2LF0KaGVhZChzaWdfdGF4YSkKCnNpZ193cml0ZSA8LSBjYmluZChzaWcsIHNpZ190YXhhKQp3cml0ZS5jc3Yoc2lnX3dyaXRlLCBmaWxlLnBhdGgob3V0ZGlyLCAnREFUYXhhX1QxUzFfVDFTNC5jc3YnKSkKYGBgCgpSZXBvcnQgdGhhdCB3ZSBhcmUgZG9uZSEKCmBgYHtyIHJlcG9ydF9kb25lfQpwcmludCgnRG9uZSB3aXRoIEFTViBpbmZlcmVuY2UuJykKYGBgCgotLS0tLQoKIyMgUmVzb3VyY2VzCgotIFtgZGVjb250YW1gIHR1dG9yaWFsXShodHRwczovL2JlbmpqbmViLmdpdGh1Yi5pby9kZWNvbnRhbS92aWduZXR0ZXMvZGVjb250YW1faW50cm8uaHRtbCkKCi0gW2B2ZWdhbmAgdHV0b3JpYWxdKGh0dHBzOi8vcGVhdC1jbGFyay5naXRodWIuaW8vQklPMzgxL3ZlZ2FuVHV0b3JpYWwuaHRtbCkKCi0gW2BERVNlcTJgIHR1dG9yaWFsXShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy9ERVNlcTIvaW5zdC9kb2MvREVTZXEyLmh0bWwpCgotIFtJbnRybyBjb3Vyc2Ugb24gbWljcm9iaW9tZSBhbmFseXNpc10oaHR0cHM6Ly9taWJ3dXJyZXBvLmdpdGh1Yi5pby9NaWNyb2JpYWwtYmlvaW5mb3JtYXRpY3MtaW50cm9kdWN0b3J5LWNvdXJzZS1NYXRlcmlhbC0yMDE4LykKCi0gW1VuaUZyYWMgcHVibGljYXRpb25dKGh0dHBzOi8vZG9pLm9yZy8xMC4xMTI4L0FFTS43MS4xMi44MjI4LTgyMzUuMjAwNSkKLSBbQWRkaXRpb25hbCBVbmlGcmFjIHB1YmxpY2F0aW9uXShodHRwczovL2RvaS5vcmcvMTAuMTEyOC9BRU0uMDE5OTYtMDYpCgotICoqQXJ0aWNsZXMgb24gZGF0YSBub3JtYWxpemF0aW9uOioqICAgCiAgIC0gW1dhc3RlIG5vdCwgd2FudCBub3Q6IHdoeSByYXJpZnlpbmcgbWljcm9iaW9tZSBkYXRhIGlzIGluYWRtaXNzaWJsZV0oaHR0cHM6Ly9keC5wbG9zLm9yZy8xMC4xMzcxL2pvdXJuYWwucGNiaS4xMDAzNTMxKQogICAtIFtNaWNyb2Jpb21lIGRhdGFzZXRzIGFyZSBjb21wb3NpdGlvbmFsOiBhbmQgdGhpcyBpcyBub3Qgb3B0aW9uYWxdKGh0dHA6Ly9qb3VybmFsLmZyb250aWVyc2luLm9yZy9hcnRpY2xlLzEwLjMzODkvZm1pY2IuMjAxNy4wMjIyNC9mdWxsKQogICAtIFtNZXRob2RzIGZvciBub3JtYWxpemluZyBtaWNyb2Jpb21lIGRhdGE6IGFuIGVjb2xvZ2ljYWwgcGVyc3BlY3RpdmVdKGh0dHA6Ly9kb2kud2lsZXkuY29tLzEwLjExMTEvMjA0MS0yMTBYLjEzMTE1KQogICAtIFtEaWZmZXJlbnRpYWwgYWJ1bmRhbmNlIGFuYWx5c2lzIGZvciBtaWNyb2JpYWwgbWFya2VyLWdlbmUgc3VydmV5c10oaHR0cDovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL25tZXRoLjI2NTgpCg==