In this lesson we will be generating a basic statistical association network form the Tara Oceans data that we can load into cytoscape.

We will generate three sets of statistics: Spearman correlations, spearman correlations on centered-log-ratio transformed data, and sparcc associations.

We will first apply a simple spearman correlation. A limitation of spearman correlations is that they don’t address the compositional nature of the data.

Compositional data are data where the different variables all sum to one. This paper by Lovell explains why this can be a problem.

Lovell D, Pawlowsky-Glahn V, Egozcue JJ, Marguerat S, Bähler J. Proportionality: A Valid Alternative to Correlation for Relative Data. PLOS Computational Biology. 2015 Mar 16;11(3):e1004075.

Essentially changes in the abundance of one variable makes all of the other values seem to be positively correlated with eachother.

To address this, we will first do a centered log ratio transform on our data, which convert them from compositions to ratios, and redo the spearman analyis.

Then we will use the sparcc algorithm that is specifically designed to handle these sorts of data.

Installation

We’ll need a few packages for this tutorial. Lets load them in. If you don’t have any of these, you can install them using the install.packages function.

eg install.packages("tidyverse")

If you don’t have SpiecEasi, you can dowload it as follows. Note, you need to install.packages("devtools") if you haven’t ever done that before.

I commented the following lines out because I don’t want to re-run them every time I test this notebook out. Uncomment them and run if you need them.

#library(devtools)
#install_github("zdk123/SpiecEasi")

This took a while on my computer, you may want to get a cup of coffee while this runs.

Loading Libraries

library(tidyverse) # for plotting and wrangling data
library(SpiecEasi) # Has sparcc and also does clr transforms
library(otuSummary)
library(reshape2) # has the melt funct  ion, which I use to wrangle data
library(psych) # for calculating regular correlations with p values
pass <- function(x){x}

Gathering data

First, lets dowload the Tara Oceans data from the internets. This data are read numbers of different phyla level bacterial and archaeal groups. The data were collected from all over the world. There are exciting environmental data collected as part of tara, but we will ignore those for now.

These data are read numbers of 16s genes but are from shotgun metagenomic sequencing.

TaraPhyla <- read_tsv("https://www.ebi.ac.uk/metagenomics/api/v1/studies/MGYS00000410/pipelines/2.0/file/ERP001736_phylum_taxonomy_abundances_v2.0.tsv")
Parsed with column specification:
cols(
  .default = col_double(),
  kingdom = col_character(),
  phylum = col_character()
)
See spec(...) for full column specifications.

Please look over the new TaraPhyla object in your global environment. You can see that the first two columns are the Kindom and Phylum information for a bunch of bacterial and archaeal phyla. The rest of the columns are the number of reads associated with those phyla at each station. The station names are at the top.

Pre-processing

While I’m a tidy-verse fanatic and like working with data in dataframes. If you want to correlate everything vs everything, its often necessary to transmute our data into a data frame.

TaraPhylaMtx0 <- TaraPhyla %>%
  select(-kingdom)  %>% # remove kingdom column
  filter(phylum != "unassigned") %>%
  mutate(phylum = make.unique(phylum)) %>% # if you don't remove "unassigned, there are two phyla with this name and you have problems without this line
  column_to_rownames(var = "phylum") %>% # turn phylum into row.names of data frame
  as.matrix() %>% # transmogrify data.frame into matrix
  t() %>% # transpose, so the OTUs are the columns and samples are the rows
  
  pass
  
TaraPhylaMtx0[1:5, 1:5] # Take a look at part of the matrix
          Crenarchaeota Euryarchaeota Parvarchaeota AC1 AD3
ERR164407             0             0             0   0   0
ERR164408             1             0             0   0   0
ERR164409             0             0             0   0   0
ERR315858            38           149             0   0   0
ERR315859           178           478             0   0   0

It is is recommended to remove any species with fewer than two reads on average.

SpeciesToKeep <- apply(TaraPhylaMtx0, 2, mean) > 2

TaraPhylaMtx <- TaraPhylaMtx0[,SpeciesToKeep]

Also Also, lets normalize evertying to total read depth.

TaraPhylaMtxRA <- TaraPhylaMtx %>% sweep(2, colSums(.), "/") # RA for relative abundance
TaraPhylaMtxRA[1:5, 1:5]
          Crenarchaeota Euryarchaeota Parvarchaeota Acidobacteria Actinobacteria
ERR164407  0.000000e+00  0.0000000000             0  0.000000e+00   1.330922e-05
ERR164408  2.180074e-06  0.0000000000             0  0.000000e+00   2.994575e-05
ERR164409  0.000000e+00  0.0000000000             0  0.000000e+00   9.981916e-06
ERR315858  8.284282e-05  0.0003854302             0  3.341241e-05   1.620398e-03
ERR315859  3.880532e-04  0.0012364808             0  3.341241e-05   4.124195e-03

Simple correlation analysis

The simplist way to look for associations in data is through correlation analyis. There are some problems with applying correlations to this kind of data and we’ll come back to that. For now though, lets calculate the spearman correlations on the data

I’m using the corr.test function from the psych library, because it returns p values. I’m not adjusting for multiple comparasons up front. We’ll do that later.

#spearCor <- cor(TaraPhylaMtx, method = "spearman")
spearCorTest <- corr.test(TaraPhylaMtxRA, method = "spearman", adjust = "none")
spearCor <- spearCorTest$r
spearP <- spearCorTest$p

Now we have a matrix of spearman correlations and a matrix of p values.

Processing the results

I’m using a solution from here to get a nice plot. http://www.sthda.com/english/wiki/ggplot2-quick-correlation-matrix-heatmap-r-software-and-data-visualization

## Helper functions
# Get lower triangle of the correlation matrix
  get_lower_tri<-function(cormat){
    cormat[upper.tri(cormat)] <- NA
    return(cormat)
  }
  # Get upper triangle of the correlation matrix
  get_upper_tri <- function(cormat){
    cormat[lower.tri(cormat)]<- NA
    return(cormat)
  }

reorder_cormat <- function(cormat){
# Use correlation between variables as distance
dd <- as.dist((1-cormat)/2)
hc <- hclust(dd)
cormat <-cormat[hc$order, hc$order]
}

reorder_cor_and_p <- function(cormat, pmat){
  dd <- as.dist((1-cormat)/2)
  hc <- hclust(dd)
  cormat <-cormat[hc$order, hc$order]
  pmat <- pmat[hc$order, hc$order]
  list(r = cormat, p = pmat)
}

## Make sure that the data are ordered so that more related species   are closer together.
## Also ensure that the p values and correlations are in the same order
reordered_all <- reorder_cor_and_p(spearCor, spearP)
reordered_spearCor <- reordered_all$r
reordered_spearP <- reordered_all$p

## Just take the upper triangle of the correlation matrix, reshape it into a data frame, and improve the names of the variables
spearCor_processed <- reordered_spearCor  %>% get_upper_tri() %>% reshape2::melt() %>% na.omit() %>% rename(rho = value)
spearP_processed <- reordered_spearP  %>% get_upper_tri() %>% reshape2::melt() %>% na.omit() %>% rename(p = value)

# join the correlation and pvalue data frames
spearRhoP <- left_join(spearCor_processed, spearP_processed, by = c("Var1", "Var2")) %>%
# calculate the false discovery rate to adjust for multiple p values
  mutate(fdr = p.adjust(p, method = "BH"))

spearRhoP

So there’s are data that we will use later and plot.

Plotting the results


# Identify which pairs are "statistically significant, given our fdr threshold"
  fdrThresh <- 0.01 # fdr threshold
spearOkP <- spearRhoP%>% filter(fdr < fdrThresh) 

spearRhoP_plot <- spearRhoP %>% ggplot(aes(x = Var2, y = Var1, fill = rho)) + geom_tile() + scale_fill_gradient2() + theme(axis.text.x = element_text(angle = 90, hjust = 1)) + geom_point(data = spearOkP, shape = 1)
spearRhoP_plot

The little circles indicate statistical significance.

As above, but using Clr to adjust for compositionality

The centered log ratio converts relative abundance data to ratio data. Here every species is reported as the log of its ratio of the average (actually geometric average, rather than additive average) bacterium in that column

TaraPhylaMtxClr <- clr(TaraPhylaMtx)

Then we just do spearman correlation on the clr transformed data.

# spearCorClr <- stats::cor(TaraPhylaMtxClr, method = "spearman")

spearCorTestClr <- corr.test(TaraPhylaMtxClr, method = "spearman", adjust = "none")
spearCorClr <- spearCorTestClr$r
spearPClr <- spearCorTestClr$p

Processing

reordered_all_Clr <- reorder_cor_and_p(spearCorClr, spearPClr)
reordered_spearCor_Clr <- reordered_all_Clr$r
reordered_spearP_Clr <- reordered_all_Clr$p


spearCor_processed_Clr <- reordered_spearCor_Clr  %>% get_upper_tri() %>% reshape2::melt() %>% na.omit() %>% rename(rho = value)
spearP_processed_Clr <- reordered_spearP_Clr  %>% get_upper_tri() %>% reshape2::melt() %>% na.omit() %>% rename(p = value)

# join the two data frames

spearRhoP_Clr <- left_join(spearCor_processed_Clr, spearP_processed_Clr, by = c("Var1", "Var2")) %>%
  # # remove self correlations
  # filter(Var1 != Var2) %>% 
  # calculate the false discovery rate to adjust for multiple p values
  mutate(fdr = p.adjust(p, method = "BH"))

Plotting


spearOkP_Clr <- spearRhoP_Clr%>% filter(fdr < fdrThresh) 

spearRhoPClr_plot <- spearRhoP_Clr %>% ggplot(aes(x = Var2, y = Var1, fill = rho)) + geom_tile() + scale_fill_gradient2() + theme(axis.text.x = element_text(angle = 90, hjust = 1)) + geom_point(data = spearOkP_Clr, shape = 1)

spearRhoPClr_plot

Analysisis with sparcc

At this point, I found this torial helpful. https://rachaellappan.github.io/16S-analysis/correlation-between-otus-with-sparcc.html

First, lets calculate the sparcc correlation values. At this point, we won’t have p-values yet.

out <- sparcc(TaraPhylaMtx)

Out is the output of sparcc. It contains a correlation and covariance matrix. Lets look at the first part of the correlation matrix.

out$Cor[1:5, 1:5]
            [,1]       [,2]        [,3]       [,4]       [,5]
[1,]  1.00000000  0.1419885  0.03220802  0.5972360 -0.2126988
[2,]  0.14198851  1.0000000 -0.39138137 -0.2618167  0.4276109
[3,]  0.03220802 -0.3913814  1.00000000  0.1978761 -0.2784589
[4,]  0.59723601 -0.2618167  0.19787609  1.0000000 -0.4904111
[5,] -0.21269880  0.4276109 -0.27845885 -0.4904111  1.0000000

I’m kind of annoyed that it threw away my site names. Lets add them back

rownames(out$Cor) <- colnames(TaraPhylaMtx)
colnames(out$Cor) <- colnames(TaraPhylaMtx)
rownames(out$Cov) <- colnames(TaraPhylaMtx)
colnames(out$Cov) <- colnames(TaraPhylaMtx)
#cout <- as.data.frame(out$Cor)
out$Cor[1:5, 1:5]
               Crenarchaeota Euryarchaeota Parvarchaeota Acidobacteria Actinobacteria
Crenarchaeota     1.00000000     0.1419885    0.03220802     0.5972360     -0.2126988
Euryarchaeota     0.14198851     1.0000000   -0.39138137    -0.2618167      0.4276109
Parvarchaeota     0.03220802    -0.3913814    1.00000000     0.1978761     -0.2784589
Acidobacteria     0.59723601    -0.2618167    0.19787609     1.0000000     -0.4904111
Actinobacteria   -0.21269880     0.4276109   -0.27845885    -0.4904111      1.0000000

Better.

Plotting Sparcc correlation

plotableSparcc <- out$Cor %>% reorder_cormat %>% get_upper_tri() %>% reshape2::melt() %>% na.omit()

Sparcc_plot <- plotableSparcc %>% ggplot(aes(x = Var2, y = Var1, fill = value)) + geom_tile() + scale_fill_gradient2() + theme(axis.text.x = element_text(angle = 90, hjust = 1))
Sparcc_plot

Calculating Sparcc Pvalues

Next, we need to calculate p-values. We do that by bootstrapping the sparcc values and then calculating p-values from those.

The code below bootstraps things.

tp0 <- proc.time()
out2 <- sparccboot(TaraPhylaMtx, R = 100)
tp1 <- proc.time()
tp1 - tp0
   user  system elapsed 
 83.900   0.628  84.523 

This took just over a minute on my laptop. So in principal, running 1000 permutations (recommended) would take about an 10 minutes or I could use the multicore option. Lets proceed with this 100 permutation version for now.

Here’s a multicore version, if you have multiple cores on your computer you can use it and things will go faster. Doesn’t work in RStudio Cloud. That said, if you wan’t to use the output with 1000 permutations, I’ve saved that out for you already as a .csv file.

# tp0 <- proc.time()
# out2 <- sparccboot(TaraPhylaMtx, R = 1000, ncpus = 7)
# tp1 <- proc.time()
# tp1 - tp0

Then we calculate the p values form the bootstrapped values.

outP <- pval.sparccboot(out2)
data.frame(outP$cors, outP$pvals) %>% head

You’ll notice these values are hard to use because they don’t tell you which p-values correspond to which species. I dug around online and found that these are the lower part of a diagnonal matrix. https://github.com/zdk123/SpiecEasi/issues/17 You can rescue the data as follows.

cors <- outP$cors
pvals <- outP$pvals
sparCCpcors <- diag(0.5, nrow = dim(out$Cor)[1], ncol = dim(out$Cor)[1])
sparCCpcors[upper.tri(sparCCpcors, diag=FALSE)] <- cors
sparCCpcors <- sparCCpcors + t(sparCCpcors)

sparCCpval <- diag(0.5, nrow = dim(out$Cor)[1], ncol = dim(out$Cor)[1])
sparCCpval[upper.tri(sparCCpval, diag=FALSE)] <- pvals
sparCCpval <- sparCCpval + t(sparCCpval)

rownames(sparCCpcors) <- colnames(TaraPhylaMtx)
colnames(sparCCpcors) <- colnames(TaraPhylaMtx)
rownames(sparCCpval) <- colnames(TaraPhylaMtx)
colnames(sparCCpval) <- colnames(TaraPhylaMtx)

sparCCpcors[1:5, 1:5]
               Crenarchaeota Euryarchaeota Parvarchaeota Acidobacteria Actinobacteria
Crenarchaeota     1.00000000     0.1447248    0.02465638     0.5970548     -0.2075595
Euryarchaeota     0.14472476     1.0000000   -0.39476664    -0.2897813      0.4471543
Parvarchaeota     0.02465638    -0.3947666    1.00000000     0.1994591     -0.2883313
Acidobacteria     0.59705483    -0.2897813    0.19945914     1.0000000     -0.5176728
Actinobacteria   -0.20755946     0.4471543   -0.28833127    -0.5176728      1.0000000
sparCCpval[1:5, 1:5]
               Crenarchaeota Euryarchaeota Parvarchaeota Acidobacteria Actinobacteria
Crenarchaeota           1.00          0.02          0.76             0              0
Euryarchaeota           0.02          1.00          0.00             0              0
Parvarchaeota           0.76          0.00          1.00             0              0
Acidobacteria           0.00          0.00          0.00             1              0
Actinobacteria          0.00          0.00          0.00             0              1

For reasons that are unclear to me, the correlation values saved by the sparcc pval program are a little different than the ones from sparcc. We’ll use these ones, for no particular reason. I’ve submitted an issue report here, which you can follow, in case anything happens: https://github.com/zdk123/SpiecEasi/issues/121

Processing the sparcc matrix and adding fdr rates

Processing

reordered_all_sparcc <- reorder_cor_and_p(sparCCpcors, sparCCpval)
reordered_sparccCor <- reordered_all_sparcc$r
reordered_sparccP<- reordered_all_sparcc$p


sparccCor_processed <- reordered_sparccCor  %>% get_upper_tri() %>% reshape2::melt() %>% na.omit() %>% rename(cor = value)
sparccP_processed <- reordered_sparccP  %>% get_upper_tri() %>% reshape2::melt() %>% na.omit() %>% rename(p = value)

# join the two data frames

SparccP <- left_join(sparccCor_processed, sparccP_processed, by = c("Var1", "Var2")) %>%
  # # remove self correlations
  # filter(Var1 != Var2) %>% 
  # calculate the false discovery rate to adjust for multiple p values
  mutate(fdr = p.adjust(p, method = "BH"))

plot the data


sparccOkP <- SparccP%>% filter(fdr < fdrThresh) 

SparccP_plot <- SparccP %>% ggplot(aes(x = Var2, y = Var1, fill = cor)) + geom_tile() + scale_fill_gradient2() + theme(axis.text.x = element_text(angle = 90, hjust = 1)) + geom_point(data = sparccOkP, shape = 1)

SparccP_plot

Comparing the three correlation matrices

cowplot::plot_grid(
spearRhoP_plot,
spearRhoPClr_plot,
SparccP_plot , nrow = 3
)

The three matreces are different, though I have some trouble saying one is objectively better than the others. I guess one pattern is that the speaman alone essentially has two loose modules of highly positivley connected organisms. The sparcc matrix has smaller positively connected modules.

Writing out

Lets write out all three sets of results.

A challenge is that Var1 and Var 2 are sometimes in the opposite order for the sparcc results. Lets fix things so the alphabetically first phylum is always var1

firstVar <- function(Var1, Var2){
  ifelse(Var1 < Var2, Var1, Var2)
}

secondVar <- function(Var1, Var2){
  ifelse(Var1 < Var2, Var2, Var1)
}

SparccP_reVar <- SparccP %>%
  mutate(Var1 = as.character(Var1), Var2 = as.character(Var2),
                   VarA = map2_chr(Var1, Var2, firstVar), 
                   VarB = map2_chr(Var1, Var2, secondVar)
                   ) %>% 
  select(-Var1, -Var2) %>%
  select(VarA, VarB, everything())

spearRhoP_reVar <- spearRhoP %>%
  mutate(Var1 = as.character(Var1), Var2 = as.character(Var2),
                   VarA = map2_chr(Var1, Var2, firstVar), 
                   VarB = map2_chr(Var1, Var2, secondVar)
                   ) %>% 
  select(-Var1, -Var2) %>%
  select(VarA, VarB, everything())

spearRhoP_Clr_reVar <- spearRhoP_Clr %>%
  mutate(Var1 = as.character(Var1), Var2 = as.character(Var2),
                   VarA = map2_chr(Var1, Var2, firstVar), 
                   VarB = map2_chr(Var1, Var2, secondVar)
                   ) %>% 
  select(-Var1, -Var2) %>%
  select(VarA, VarB, everything())

SparccP_ren <- SparccP_reVar %>% dplyr::rename(sparcc = cor, p.sparcc = p, fdr.sparcc = fdr)

Sticking all of the three different analyses together into one data frame

AllTaraNetworkStats <- spearRhoP_reVar %>% 
  left_join(spearRhoP_Clr_reVar, by = c("VarA", "VarB"), suffix = c(".spear", ".clr")) %>%
  left_join(SparccP_ren, by = c("VarA", "VarB"))
AllTaraNetworkStats

Now lets save everything out

write_csv(AllTaraNetworkStats, "Analysis/TaraOceansSpearmanSparCCAnalysis_p100.csv")
LS0tCnRpdGxlOiAiTmV0d29yayBTY2llbmNlIExlc3NvbiAwMSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKSW4gdGhpcyBsZXNzb24gd2Ugd2lsbCBiZSBnZW5lcmF0aW5nIGEgYmFzaWMgc3RhdGlzdGljYWwgYXNzb2NpYXRpb24gbmV0d29yayBmb3JtIHRoZSBUYXJhIE9jZWFucyBkYXRhIHRoYXQgd2UgY2FuIGxvYWQgaW50byBjeXRvc2NhcGUuCgpXZSB3aWxsIGdlbmVyYXRlIHRocmVlIHNldHMgb2Ygc3RhdGlzdGljczogU3BlYXJtYW4gY29ycmVsYXRpb25zLCBzcGVhcm1hbiBjb3JyZWxhdGlvbnMgb24gY2VudGVyZWQtbG9nLXJhdGlvIHRyYW5zZm9ybWVkIGRhdGEsIGFuZCBzcGFyY2MgYXNzb2NpYXRpb25zLgoKV2Ugd2lsbCBmaXJzdCBhcHBseSBhIHNpbXBsZSBzcGVhcm1hbiBjb3JyZWxhdGlvbi4gQSBsaW1pdGF0aW9uIG9mIHNwZWFybWFuIGNvcnJlbGF0aW9ucyBpcyB0aGF0IHRoZXkgZG9uJ3QgYWRkcmVzcyB0aGUgY29tcG9zaXRpb25hbCBuYXR1cmUgb2YgdGhlIGRhdGEuIAoKQ29tcG9zaXRpb25hbCBkYXRhIGFyZSBkYXRhIHdoZXJlIHRoZSBkaWZmZXJlbnQgdmFyaWFibGVzIGFsbCBzdW0gdG8gb25lLiBUaGlzIHBhcGVyIGJ5IExvdmVsbCBleHBsYWlucyB3aHkgdGhpcyBjYW4gYmUgYSBwcm9ibGVtLgoKTG92ZWxsIEQsIFBhd2xvd3NreS1HbGFobiBWLCBFZ296Y3VlIEpKLCBNYXJndWVyYXQgUywgQsOkaGxlciBKLiBQcm9wb3J0aW9uYWxpdHk6IEEgVmFsaWQgQWx0ZXJuYXRpdmUgdG8gQ29ycmVsYXRpb24gZm9yIFJlbGF0aXZlIERhdGEuIFBMT1MgQ29tcHV0YXRpb25hbCBCaW9sb2d5LiAyMDE1IE1hciAxNjsxMSgzKTplMTAwNDA3NS4gCgpFc3NlbnRpYWxseSBjaGFuZ2VzIGluIHRoZSBhYnVuZGFuY2Ugb2Ygb25lIHZhcmlhYmxlIG1ha2VzIGFsbCBvZiB0aGUgb3RoZXIgdmFsdWVzIHNlZW0gdG8gYmUgcG9zaXRpdmVseSBjb3JyZWxhdGVkIHdpdGggZWFjaG90aGVyLgoKVG8gYWRkcmVzcyB0aGlzLCB3ZSB3aWxsIGZpcnN0IGRvIGEgY2VudGVyZWQgbG9nIHJhdGlvIHRyYW5zZm9ybSBvbiBvdXIgZGF0YSwgd2hpY2ggY29udmVydCB0aGVtIGZyb20gY29tcG9zaXRpb25zIHRvIHJhdGlvcywgYW5kIHJlZG8gdGhlIHNwZWFybWFuIGFuYWx5aXMuCgpUaGVuIHdlIHdpbGwgdXNlIHRoZSBzcGFyY2MgYWxnb3JpdGhtIHRoYXQgaXMgc3BlY2lmaWNhbGx5IGRlc2lnbmVkIHRvIGhhbmRsZSB0aGVzZSBzb3J0cyBvZiBkYXRhLgoKCiMgSW5zdGFsbGF0aW9uCgpXZSdsbCBuZWVkIGEgZmV3IHBhY2thZ2VzIGZvciB0aGlzIHR1dG9yaWFsLiBMZXRzIGxvYWQgdGhlbSBpbi4gSWYgeW91IGRvbid0IGhhdmUgYW55IG9mIHRoZXNlLCB5b3UgY2FuIGluc3RhbGwgdGhlbSB1c2luZyB0aGUgYGluc3RhbGwucGFja2FnZXNgIGZ1bmN0aW9uLgoKZWcgYGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpYAoKSWYgeW91IGRvbid0IGhhdmUgU3BpZWNFYXNpLCB5b3UgY2FuIGRvd2xvYWQgaXQgYXMgZm9sbG93cy4gTm90ZSwgeW91IG5lZWQgdG8gYGluc3RhbGwucGFja2FnZXMoImRldnRvb2xzIilgIGlmIHlvdSBoYXZlbid0IGV2ZXIgZG9uZSB0aGF0IGJlZm9yZS4KCkkgY29tbWVudGVkIHRoZSBmb2xsb3dpbmcgbGluZXMgb3V0IGJlY2F1c2UgSSBkb24ndCB3YW50IHRvIHJlLXJ1biB0aGVtIGV2ZXJ5IHRpbWUgSSB0ZXN0IHRoaXMgbm90ZWJvb2sgb3V0LiBVbmNvbW1lbnQgdGhlbSBhbmQgcnVuIGlmIHlvdSBuZWVkIHRoZW0uCgpgYGB7cn0KI2xpYnJhcnkoZGV2dG9vbHMpCiNpbnN0YWxsX2dpdGh1YigiemRrMTIzL1NwaWVjRWFzaSIpCmBgYAoKVGhpcyB0b29rIGEgd2hpbGUgb24gbXkgY29tcHV0ZXIsIHlvdSBtYXkgd2FudCB0byBnZXQgYSBjdXAgb2YgY29mZmVlIHdoaWxlIHRoaXMgcnVucy4KCiMgTG9hZGluZyBMaWJyYXJpZXMKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkgIyBmb3IgcGxvdHRpbmcgYW5kIHdyYW5nbGluZyBkYXRhCmxpYnJhcnkoU3BpZWNFYXNpKSAjIEhhcyBzcGFyY2MgYW5kIGFsc28gZG9lcyBjbHIgdHJhbnNmb3JtcwpsaWJyYXJ5KG90dVN1bW1hcnkpCmxpYnJhcnkocmVzaGFwZTIpICMgaGFzIHRoZSBtZWx0IGZ1bmN0ICBpb24sIHdoaWNoIEkgdXNlIHRvIHdyYW5nbGUgZGF0YQpsaWJyYXJ5KHBzeWNoKSAjIGZvciBjYWxjdWxhdGluZyByZWd1bGFyIGNvcnJlbGF0aW9ucyB3aXRoIHAgdmFsdWVzCnBhc3MgPC0gZnVuY3Rpb24oeCl7eH0KYGBgCgojIEdhdGhlcmluZyBkYXRhCgpGaXJzdCwgbGV0cyBkb3dsb2FkIHRoZSBUYXJhIE9jZWFucyBkYXRhIGZyb20gdGhlIGludGVybmV0cy4KVGhpcyBkYXRhIGFyZSByZWFkIG51bWJlcnMgb2YgZGlmZmVyZW50IHBoeWxhIGxldmVsIGJhY3RlcmlhbCBhbmQgYXJjaGFlYWwgZ3JvdXBzLiBUaGUgZGF0YSB3ZXJlIGNvbGxlY3RlZCBmcm9tIGFsbCBvdmVyIHRoZSB3b3JsZC4gVGhlcmUgYXJlIGV4Y2l0aW5nIGVudmlyb25tZW50YWwgZGF0YSBjb2xsZWN0ZWQgYXMgcGFydCBvZiB0YXJhLCBidXQgd2Ugd2lsbCBpZ25vcmUgdGhvc2UgZm9yIG5vdy4KClRoZXNlIGRhdGEgYXJlIHJlYWQgbnVtYmVycyBvZiAxNnMgZ2VuZXMgYnV0IGFyZSBmcm9tIHNob3RndW4gbWV0YWdlbm9taWMgc2VxdWVuY2luZy4KCmBgYHtyfQpUYXJhUGh5bGEgPC0gcmVhZF90c3YoImh0dHBzOi8vd3d3LmViaS5hYy51ay9tZXRhZ2Vub21pY3MvYXBpL3YxL3N0dWRpZXMvTUdZUzAwMDAwNDEwL3BpcGVsaW5lcy8yLjAvZmlsZS9FUlAwMDE3MzZfcGh5bHVtX3RheG9ub215X2FidW5kYW5jZXNfdjIuMC50c3YiKQpgYGAKClBsZWFzZSBsb29rIG92ZXIgdGhlIG5ldyBUYXJhUGh5bGEgb2JqZWN0IGluIHlvdXIgZ2xvYmFsIGVudmlyb25tZW50LiBZb3UgY2FuIHNlZSB0aGF0IHRoZSBmaXJzdCB0d28gY29sdW1ucyBhcmUgdGhlIEtpbmRvbSBhbmQgUGh5bHVtIGluZm9ybWF0aW9uIGZvciBhIGJ1bmNoIG9mIGJhY3RlcmlhbCBhbmQgYXJjaGFlYWwgcGh5bGEuIFRoZSByZXN0IG9mIHRoZSBjb2x1bW5zIGFyZSB0aGUgbnVtYmVyIG9mIHJlYWRzIGFzc29jaWF0ZWQgd2l0aCB0aG9zZSBwaHlsYSBhdCBlYWNoIHN0YXRpb24uIFRoZSBzdGF0aW9uIG5hbWVzIGFyZSBhdCB0aGUgdG9wLgoKIyBQcmUtcHJvY2Vzc2luZwoKV2hpbGUgSSdtIGEgdGlkeS12ZXJzZSBmYW5hdGljIGFuZCBsaWtlIHdvcmtpbmcgd2l0aCBkYXRhIGluIGRhdGFmcmFtZXMuIElmIHlvdSB3YW50IHRvIGNvcnJlbGF0ZSBldmVyeXRoaW5nIHZzIGV2ZXJ5dGhpbmcsIGl0cyBvZnRlbiBuZWNlc3NhcnkgdG8gdHJhbnNtdXRlIG91ciBkYXRhIGludG8gYSBkYXRhIGZyYW1lLiAKCmBgYHtyfQpUYXJhUGh5bGFNdHgwIDwtIFRhcmFQaHlsYSAlPiUKICBzZWxlY3QoLWtpbmdkb20pICAlPiUgIyByZW1vdmUga2luZ2RvbSBjb2x1bW4KICBmaWx0ZXIocGh5bHVtICE9ICJ1bmFzc2lnbmVkIikgJT4lCiAgbXV0YXRlKHBoeWx1bSA9IG1ha2UudW5pcXVlKHBoeWx1bSkpICU+JSAjIGlmIHlvdSBkb24ndCByZW1vdmUgInVuYXNzaWduZWQsIHRoZXJlIGFyZSB0d28gcGh5bGEgd2l0aCB0aGlzIG5hbWUgYW5kIHlvdSBoYXZlIHByb2JsZW1zIHdpdGhvdXQgdGhpcyBsaW5lCiAgY29sdW1uX3RvX3Jvd25hbWVzKHZhciA9ICJwaHlsdW0iKSAlPiUgIyB0dXJuIHBoeWx1bSBpbnRvIHJvdy5uYW1lcyBvZiBkYXRhIGZyYW1lCiAgYXMubWF0cml4KCkgJT4lICMgdHJhbnNtb2dyaWZ5IGRhdGEuZnJhbWUgaW50byBtYXRyaXgKICB0KCkgJT4lICMgdHJhbnNwb3NlLCBzbyB0aGUgT1RVcyBhcmUgdGhlIGNvbHVtbnMgYW5kIHNhbXBsZXMgYXJlIHRoZSByb3dzCiAgCiAgcGFzcwogIApUYXJhUGh5bGFNdHgwWzE6NSwgMTo1XSAjIFRha2UgYSBsb29rIGF0IHBhcnQgb2YgdGhlIG1hdHJpeApgYGAKCkl0IGlzIGlzIHJlY29tbWVuZGVkIHRvIHJlbW92ZSBhbnkgc3BlY2llcyAgd2l0aCBmZXdlciB0aGFuIHR3byByZWFkcyBvbiBhdmVyYWdlLgoKYGBge3J9ClNwZWNpZXNUb0tlZXAgPC0gYXBwbHkoVGFyYVBoeWxhTXR4MCwgMiwgbWVhbikgPiAyCgpUYXJhUGh5bGFNdHggPC0gVGFyYVBoeWxhTXR4MFssU3BlY2llc1RvS2VlcF0KYGBgCgpBbHNvIEFsc28sIGxldHMgbm9ybWFsaXplIGV2ZXJ0eWluZyB0byB0b3RhbCByZWFkIGRlcHRoLgoKYGBge3J9ClRhcmFQaHlsYU10eFJBIDwtIFRhcmFQaHlsYU10eCAlPiUgc3dlZXAoMiwgY29sU3VtcyguKSwgIi8iKSAjIFJBIGZvciByZWxhdGl2ZSBhYnVuZGFuY2UKVGFyYVBoeWxhTXR4UkFbMTo1LCAxOjVdCmBgYAoKCgojIFNpbXBsZSBjb3JyZWxhdGlvbiBhbmFseXNpcwpUaGUgc2ltcGxpc3Qgd2F5IHRvIGxvb2sgZm9yIGFzc29jaWF0aW9ucyBpbiBkYXRhIGlzIHRocm91Z2ggY29ycmVsYXRpb24gYW5hbHlpcy4gVGhlcmUgYXJlIHNvbWUgcHJvYmxlbXMgd2l0aCBhcHBseWluZyBjb3JyZWxhdGlvbnMgdG8gdGhpcyBraW5kIG9mIGRhdGEgYW5kIHdlJ2xsIGNvbWUgYmFjayB0byB0aGF0LiBGb3Igbm93IHRob3VnaCwgbGV0cyBjYWxjdWxhdGUgdGhlIHNwZWFybWFuIGNvcnJlbGF0aW9ucyBvbiB0aGUgZGF0YQoKSSdtIHVzaW5nIHRoZSBjb3JyLnRlc3QgZnVuY3Rpb24gZnJvbSB0aGUgYHBzeWNoYCBsaWJyYXJ5LCBiZWNhdXNlIGl0IHJldHVybnMgcCB2YWx1ZXMuIEknbSBub3QgYWRqdXN0aW5nIGZvciBtdWx0aXBsZSBjb21wYXJhc29ucyB1cCBmcm9udC4gV2UnbGwgZG8gdGhhdCBsYXRlci4KCmBgYHtyfQojc3BlYXJDb3IgPC0gY29yKFRhcmFQaHlsYU10eCwgbWV0aG9kID0gInNwZWFybWFuIikKc3BlYXJDb3JUZXN0IDwtIGNvcnIudGVzdChUYXJhUGh5bGFNdHhSQSwgbWV0aG9kID0gInNwZWFybWFuIiwgYWRqdXN0ID0gIm5vbmUiKQpzcGVhckNvciA8LSBzcGVhckNvclRlc3QkcgpzcGVhclAgPC0gc3BlYXJDb3JUZXN0JHAKYGBgCgpOb3cgd2UgaGF2ZSBhIG1hdHJpeCBvZiBzcGVhcm1hbiBjb3JyZWxhdGlvbnMgYW5kIGEgbWF0cml4IG9mIHAgdmFsdWVzLgoKIyMgUHJvY2Vzc2luZyB0aGUgcmVzdWx0cwoKSSdtIHVzaW5nIGEgc29sdXRpb24gZnJvbSBoZXJlIHRvIGdldCBhIG5pY2UgcGxvdC4KaHR0cDovL3d3dy5zdGhkYS5jb20vZW5nbGlzaC93aWtpL2dncGxvdDItcXVpY2stY29ycmVsYXRpb24tbWF0cml4LWhlYXRtYXAtci1zb2Z0d2FyZS1hbmQtZGF0YS12aXN1YWxpemF0aW9uCmBgYHtyfQojIyBIZWxwZXIgZnVuY3Rpb25zCiMgR2V0IGxvd2VyIHRyaWFuZ2xlIG9mIHRoZSBjb3JyZWxhdGlvbiBtYXRyaXgKICBnZXRfbG93ZXJfdHJpPC1mdW5jdGlvbihjb3JtYXQpewogICAgY29ybWF0W3VwcGVyLnRyaShjb3JtYXQpXSA8LSBOQQogICAgcmV0dXJuKGNvcm1hdCkKICB9CiAgIyBHZXQgdXBwZXIgdHJpYW5nbGUgb2YgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeAogIGdldF91cHBlcl90cmkgPC0gZnVuY3Rpb24oY29ybWF0KXsKICAgIGNvcm1hdFtsb3dlci50cmkoY29ybWF0KV08LSBOQQogICAgcmV0dXJuKGNvcm1hdCkKICB9CgpyZW9yZGVyX2Nvcm1hdCA8LSBmdW5jdGlvbihjb3JtYXQpewojIFVzZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHZhcmlhYmxlcyBhcyBkaXN0YW5jZQpkZCA8LSBhcy5kaXN0KCgxLWNvcm1hdCkvMikKaGMgPC0gaGNsdXN0KGRkKQpjb3JtYXQgPC1jb3JtYXRbaGMkb3JkZXIsIGhjJG9yZGVyXQp9CgpyZW9yZGVyX2Nvcl9hbmRfcCA8LSBmdW5jdGlvbihjb3JtYXQsIHBtYXQpewogIGRkIDwtIGFzLmRpc3QoKDEtY29ybWF0KS8yKQogIGhjIDwtIGhjbHVzdChkZCkKICBjb3JtYXQgPC1jb3JtYXRbaGMkb3JkZXIsIGhjJG9yZGVyXQogIHBtYXQgPC0gcG1hdFtoYyRvcmRlciwgaGMkb3JkZXJdCiAgbGlzdChyID0gY29ybWF0LCBwID0gcG1hdCkKfQoKIyMgTWFrZSBzdXJlIHRoYXQgdGhlIGRhdGEgYXJlIG9yZGVyZWQgc28gdGhhdCBtb3JlIHJlbGF0ZWQgc3BlY2llcyAgIGFyZSBjbG9zZXIgdG9nZXRoZXIuCiMjIEFsc28gZW5zdXJlIHRoYXQgdGhlIHAgdmFsdWVzIGFuZCBjb3JyZWxhdGlvbnMgYXJlIGluIHRoZSBzYW1lIG9yZGVyCnJlb3JkZXJlZF9hbGwgPC0gcmVvcmRlcl9jb3JfYW5kX3Aoc3BlYXJDb3IsIHNwZWFyUCkKcmVvcmRlcmVkX3NwZWFyQ29yIDwtIHJlb3JkZXJlZF9hbGwkcgpyZW9yZGVyZWRfc3BlYXJQIDwtIHJlb3JkZXJlZF9hbGwkcAoKIyMgSnVzdCB0YWtlIHRoZSB1cHBlciB0cmlhbmdsZSBvZiB0aGUgY29ycmVsYXRpb24gbWF0cml4LCByZXNoYXBlIGl0IGludG8gYSBkYXRhIGZyYW1lLCBhbmQgaW1wcm92ZSB0aGUgbmFtZXMgb2YgdGhlIHZhcmlhYmxlcwpzcGVhckNvcl9wcm9jZXNzZWQgPC0gcmVvcmRlcmVkX3NwZWFyQ29yICAlPiUgZ2V0X3VwcGVyX3RyaSgpICU+JSByZXNoYXBlMjo6bWVsdCgpICU+JSBuYS5vbWl0KCkgJT4lIHJlbmFtZShyaG8gPSB2YWx1ZSkKc3BlYXJQX3Byb2Nlc3NlZCA8LSByZW9yZGVyZWRfc3BlYXJQICAlPiUgZ2V0X3VwcGVyX3RyaSgpICU+JSByZXNoYXBlMjo6bWVsdCgpICU+JSBuYS5vbWl0KCkgJT4lIHJlbmFtZShwID0gdmFsdWUpCgojIGpvaW4gdGhlIGNvcnJlbGF0aW9uIGFuZCBwdmFsdWUgZGF0YSBmcmFtZXMKc3BlYXJSaG9QIDwtIGxlZnRfam9pbihzcGVhckNvcl9wcm9jZXNzZWQsIHNwZWFyUF9wcm9jZXNzZWQsIGJ5ID0gYygiVmFyMSIsICJWYXIyIikpICU+JQojIGNhbGN1bGF0ZSB0aGUgZmFsc2UgZGlzY292ZXJ5IHJhdGUgdG8gYWRqdXN0IGZvciBtdWx0aXBsZSBwIHZhbHVlcwogIG11dGF0ZShmZHIgPSBwLmFkanVzdChwLCBtZXRob2QgPSAiQkgiKSkKCnNwZWFyUmhvUApgYGAKClNvIHRoZXJlJ3MgYXJlIGRhdGEgdGhhdCB3ZSB3aWxsIHVzZSBsYXRlciBhbmQgcGxvdC4KCiMjIFBsb3R0aW5nIHRoZSByZXN1bHRzCgpgYGB7ciwgZmlnLndpZHRoPSAxMCwgZmlnLmhlaWdodCA9IDEwfQoKIyBJZGVudGlmeSB3aGljaCBwYWlycyBhcmUgInN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQsIGdpdmVuIG91ciBmZHIgdGhyZXNob2xkIgogIGZkclRocmVzaCA8LSAwLjAxICMgZmRyIHRocmVzaG9sZApzcGVhck9rUCA8LSBzcGVhclJob1AlPiUgZmlsdGVyKGZkciA8IGZkclRocmVzaCkgCgpzcGVhclJob1BfcGxvdCA8LSBzcGVhclJob1AgJT4lIGdncGxvdChhZXMoeCA9IFZhcjIsIHkgPSBWYXIxLCBmaWxsID0gcmhvKSkgKyBnZW9tX3RpbGUoKSArIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKCkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArIGdlb21fcG9pbnQoZGF0YSA9IHNwZWFyT2tQLCBzaGFwZSA9IDEpCnNwZWFyUmhvUF9wbG90CmBgYApUaGUgbGl0dGxlIGNpcmNsZXMgaW5kaWNhdGUgc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlLgoKIyBBcyBhYm92ZSwgYnV0IHVzaW5nIENsciB0byBhZGp1c3QgZm9yIGNvbXBvc2l0aW9uYWxpdHkKVGhlIGNlbnRlcmVkIGxvZyByYXRpbyBjb252ZXJ0cyByZWxhdGl2ZSBhYnVuZGFuY2UgZGF0YSB0byByYXRpbyBkYXRhLiBIZXJlIGV2ZXJ5IHNwZWNpZXMgaXMgcmVwb3J0ZWQgYXMgdGhlIGxvZyBvZiBpdHMgcmF0aW8gb2YgdGhlIGF2ZXJhZ2UgKGFjdHVhbGx5IGdlb21ldHJpYyBhdmVyYWdlLCByYXRoZXIgdGhhbiBhZGRpdGl2ZSBhdmVyYWdlKSBiYWN0ZXJpdW0gaW4gdGhhdCBjb2x1bW4KYGBge3J9ClRhcmFQaHlsYU10eENsciA8LSBjbHIoVGFyYVBoeWxhTXR4KQoKYGBgCgpUaGVuIHdlIGp1c3QgZG8gc3BlYXJtYW4gY29ycmVsYXRpb24gb24gdGhlIGNsciB0cmFuc2Zvcm1lZCBkYXRhLgoKYGBge3J9CiMgc3BlYXJDb3JDbHIgPC0gc3RhdHM6OmNvcihUYXJhUGh5bGFNdHhDbHIsIG1ldGhvZCA9ICJzcGVhcm1hbiIpCgpzcGVhckNvclRlc3RDbHIgPC0gY29yci50ZXN0KFRhcmFQaHlsYU10eENsciwgbWV0aG9kID0gInNwZWFybWFuIiwgYWRqdXN0ID0gIm5vbmUiKQpzcGVhckNvckNsciA8LSBzcGVhckNvclRlc3RDbHIkcgpzcGVhclBDbHIgPC0gc3BlYXJDb3JUZXN0Q2xyJHAKYGBgCgojIyBQcm9jZXNzaW5nCgpgYGB7cn0KcmVvcmRlcmVkX2FsbF9DbHIgPC0gcmVvcmRlcl9jb3JfYW5kX3Aoc3BlYXJDb3JDbHIsIHNwZWFyUENscikKcmVvcmRlcmVkX3NwZWFyQ29yX0NsciA8LSByZW9yZGVyZWRfYWxsX0NsciRyCnJlb3JkZXJlZF9zcGVhclBfQ2xyIDwtIHJlb3JkZXJlZF9hbGxfQ2xyJHAKCgpzcGVhckNvcl9wcm9jZXNzZWRfQ2xyIDwtIHJlb3JkZXJlZF9zcGVhckNvcl9DbHIgICU+JSBnZXRfdXBwZXJfdHJpKCkgJT4lIHJlc2hhcGUyOjptZWx0KCkgJT4lIG5hLm9taXQoKSAlPiUgcmVuYW1lKHJobyA9IHZhbHVlKQpzcGVhclBfcHJvY2Vzc2VkX0NsciA8LSByZW9yZGVyZWRfc3BlYXJQX0NsciAgJT4lIGdldF91cHBlcl90cmkoKSAlPiUgcmVzaGFwZTI6Om1lbHQoKSAlPiUgbmEub21pdCgpICU+JSByZW5hbWUocCA9IHZhbHVlKQoKIyBqb2luIHRoZSB0d28gZGF0YSBmcmFtZXMKCnNwZWFyUmhvUF9DbHIgPC0gbGVmdF9qb2luKHNwZWFyQ29yX3Byb2Nlc3NlZF9DbHIsIHNwZWFyUF9wcm9jZXNzZWRfQ2xyLCBieSA9IGMoIlZhcjEiLCAiVmFyMiIpKSAlPiUKICAjICMgcmVtb3ZlIHNlbGYgY29ycmVsYXRpb25zCiAgIyBmaWx0ZXIoVmFyMSAhPSBWYXIyKSAlPiUgCiAgIyBjYWxjdWxhdGUgdGhlIGZhbHNlIGRpc2NvdmVyeSByYXRlIHRvIGFkanVzdCBmb3IgbXVsdGlwbGUgcCB2YWx1ZXMKICBtdXRhdGUoZmRyID0gcC5hZGp1c3QocCwgbWV0aG9kID0gIkJIIikpCmBgYAoKCiMjIFBsb3R0aW5nCgpgYGB7ciwgZmlnLndpZHRoPSAxMCwgZmlnLmhlaWdodCA9IDEwfQoKc3BlYXJPa1BfQ2xyIDwtIHNwZWFyUmhvUF9DbHIlPiUgZmlsdGVyKGZkciA8IGZkclRocmVzaCkgCgpzcGVhclJob1BDbHJfcGxvdCA8LSBzcGVhclJob1BfQ2xyICU+JSBnZ3Bsb3QoYWVzKHggPSBWYXIyLCB5ID0gVmFyMSwgZmlsbCA9IHJobykpICsgZ2VvbV90aWxlKCkgKyBzY2FsZV9maWxsX2dyYWRpZW50MigpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkgKyBnZW9tX3BvaW50KGRhdGEgPSBzcGVhck9rUF9DbHIsIHNoYXBlID0gMSkKCnNwZWFyUmhvUENscl9wbG90CmBgYAoKCgojIEFuYWx5c2lzaXMgd2l0aCBzcGFyY2MKCkF0IHRoaXMgcG9pbnQsIEkgZm91bmQgdGhpcyB0b3JpYWwgaGVscGZ1bC4KaHR0cHM6Ly9yYWNoYWVsbGFwcGFuLmdpdGh1Yi5pby8xNlMtYW5hbHlzaXMvY29ycmVsYXRpb24tYmV0d2Vlbi1vdHVzLXdpdGgtc3BhcmNjLmh0bWwKCkZpcnN0LCBsZXRzIGNhbGN1bGF0ZSB0aGUgc3BhcmNjIGNvcnJlbGF0aW9uIHZhbHVlcy4KQXQgdGhpcyBwb2ludCwgd2Ugd29uJ3QgaGF2ZSBwLXZhbHVlcyB5ZXQuCmBgYHtyfQpvdXQgPC0gc3BhcmNjKFRhcmFQaHlsYU10eCkKYGBgCgpPdXQgaXMgdGhlIG91dHB1dCBvZiBzcGFyY2MuIEl0IGNvbnRhaW5zIGEgY29ycmVsYXRpb24gYW5kIGNvdmFyaWFuY2UgbWF0cml4LiBMZXRzIGxvb2sgYXQgdGhlIGZpcnN0IHBhcnQgb2YgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeC4KCmBgYHtyfQpvdXQkQ29yWzE6NSwgMTo1XQpgYGAKCkknbSBraW5kIG9mIGFubm95ZWQgdGhhdCBpdCB0aHJldyBhd2F5IG15IHNpdGUgbmFtZXMuIExldHMgYWRkIHRoZW0gYmFjawoKYGBge3J9CnJvd25hbWVzKG91dCRDb3IpIDwtIGNvbG5hbWVzKFRhcmFQaHlsYU10eCkKY29sbmFtZXMob3V0JENvcikgPC0gY29sbmFtZXMoVGFyYVBoeWxhTXR4KQpyb3duYW1lcyhvdXQkQ292KSA8LSBjb2xuYW1lcyhUYXJhUGh5bGFNdHgpCmNvbG5hbWVzKG91dCRDb3YpIDwtIGNvbG5hbWVzKFRhcmFQaHlsYU10eCkKI2NvdXQgPC0gYXMuZGF0YS5mcmFtZShvdXQkQ29yKQpvdXQkQ29yWzE6NSwgMTo1XQpgYGAKCkJldHRlci4KCiMjIFBsb3R0aW5nIFNwYXJjYyBjb3JyZWxhdGlvbgoKYGBge3IgLCBmaWcud2lkdGg9IDEwLCBmaWcuaGVpZ2h0ID0gMTB9CnBsb3RhYmxlU3BhcmNjIDwtIG91dCRDb3IgJT4lIHJlb3JkZXJfY29ybWF0ICU+JSBnZXRfdXBwZXJfdHJpKCkgJT4lIHJlc2hhcGUyOjptZWx0KCkgJT4lIG5hLm9taXQoKQoKU3BhcmNjX3Bsb3QgPC0gcGxvdGFibGVTcGFyY2MgJT4lIGdncGxvdChhZXMoeCA9IFZhcjIsIHkgPSBWYXIxLCBmaWxsID0gdmFsdWUpKSArIGdlb21fdGlsZSgpICsgc2NhbGVfZmlsbF9ncmFkaWVudDIoKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpClNwYXJjY19wbG90CmBgYAoKIyMgQ2FsY3VsYXRpbmcgU3BhcmNjIFB2YWx1ZXMKCk5leHQsIHdlIG5lZWQgdG8gY2FsY3VsYXRlIHAtdmFsdWVzLiBXZSBkbyB0aGF0IGJ5IGJvb3RzdHJhcHBpbmcgdGhlIHNwYXJjYyB2YWx1ZXMgYW5kIHRoZW4gY2FsY3VsYXRpbmcgcC12YWx1ZXMgZnJvbSB0aG9zZS4gCgpUaGUgY29kZSBiZWxvdyBib290c3RyYXBzIHRoaW5ncy4KCmBgYHtyfQp0cDAgPC0gcHJvYy50aW1lKCkKb3V0MiA8LSBzcGFyY2Nib290KFRhcmFQaHlsYU10eCwgUiA9IDEwMCkKdHAxIDwtIHByb2MudGltZSgpCnRwMSAtIHRwMApgYGAKClRoaXMgdG9vayBqdXN0IG92ZXIgYSBtaW51dGUgb24gbXkgbGFwdG9wLiBTbyBpbiBwcmluY2lwYWwsIHJ1bm5pbmcgMTAwMCBwZXJtdXRhdGlvbnMgKHJlY29tbWVuZGVkKSB3b3VsZCB0YWtlIGFib3V0IGFuIDEwIG1pbnV0ZXMgb3IgSSBjb3VsZCB1c2UgdGhlIG11bHRpY29yZSBvcHRpb24uIExldHMgcHJvY2VlZCB3aXRoIHRoaXMgMTAwIHBlcm11dGF0aW9uIHZlcnNpb24gZm9yIG5vdy4KCkhlcmUncyBhIG11bHRpY29yZSB2ZXJzaW9uLCBpZiB5b3UgaGF2ZSBtdWx0aXBsZSBjb3JlcyBvbiB5b3VyIGNvbXB1dGVyIHlvdSBjYW4gdXNlIGl0IGFuZCB0aGluZ3Mgd2lsbCBnbyBmYXN0ZXIuCkRvZXNuJ3Qgd29yayBpbiBSU3R1ZGlvIENsb3VkLiBUaGF0IHNhaWQsIGlmIHlvdSB3YW4ndCB0byB1c2UgdGhlIG91dHB1dCB3aXRoIDEwMDAgcGVybXV0YXRpb25zLCBJJ3ZlIHNhdmVkIHRoYXQgb3V0IGZvciB5b3UgYWxyZWFkeSBhcyBhIC5jc3YgZmlsZS4KCmBgYHtyfQojIHRwMCA8LSBwcm9jLnRpbWUoKQojIG91dDIgPC0gc3BhcmNjYm9vdChUYXJhUGh5bGFNdHgsIFIgPSAxMDAwLCBuY3B1cyA9IDcpCiMgdHAxIDwtIHByb2MudGltZSgpCiMgdHAxIC0gdHAwCmBgYAoKClRoZW4gd2UgY2FsY3VsYXRlIHRoZSBwIHZhbHVlcyBmb3JtIHRoZSBib290c3RyYXBwZWQgdmFsdWVzLgoKYGBge3J9Cm91dFAgPC0gcHZhbC5zcGFyY2Nib290KG91dDIpCmRhdGEuZnJhbWUob3V0UCRjb3JzLCBvdXRQJHB2YWxzKSAlPiUgaGVhZApgYGAKCllvdSdsbCBub3RpY2UgdGhlc2UgdmFsdWVzIGFyZSBoYXJkIHRvIHVzZSBiZWNhdXNlIHRoZXkgZG9uJ3QgdGVsbCB5b3Ugd2hpY2ggcC12YWx1ZXMgY29ycmVzcG9uZCB0byB3aGljaCBzcGVjaWVzLiBJIGR1ZyBhcm91bmQgb25saW5lIGFuZCBmb3VuZCB0aGF0IHRoZXNlIGFyZSB0aGUgbG93ZXIgcGFydCBvZiBhIGRpYWdub25hbCBtYXRyaXguIApodHRwczovL2dpdGh1Yi5jb20vemRrMTIzL1NwaWVjRWFzaS9pc3N1ZXMvMTcKWW91IGNhbiByZXNjdWUgdGhlIGRhdGEgYXMgZm9sbG93cy4KCmBgYHtyfQpjb3JzIDwtIG91dFAkY29ycwpwdmFscyA8LSBvdXRQJHB2YWxzCnNwYXJDQ3Bjb3JzIDwtIGRpYWcoMC41LCBucm93ID0gZGltKG91dCRDb3IpWzFdLCBuY29sID0gZGltKG91dCRDb3IpWzFdKQpzcGFyQ0NwY29yc1t1cHBlci50cmkoc3BhckNDcGNvcnMsIGRpYWc9RkFMU0UpXSA8LSBjb3JzCnNwYXJDQ3Bjb3JzIDwtIHNwYXJDQ3Bjb3JzICsgdChzcGFyQ0NwY29ycykKCnNwYXJDQ3B2YWwgPC0gZGlhZygwLjUsIG5yb3cgPSBkaW0ob3V0JENvcilbMV0sIG5jb2wgPSBkaW0ob3V0JENvcilbMV0pCnNwYXJDQ3B2YWxbdXBwZXIudHJpKHNwYXJDQ3B2YWwsIGRpYWc9RkFMU0UpXSA8LSBwdmFscwpzcGFyQ0NwdmFsIDwtIHNwYXJDQ3B2YWwgKyB0KHNwYXJDQ3B2YWwpCgpyb3duYW1lcyhzcGFyQ0NwY29ycykgPC0gY29sbmFtZXMoVGFyYVBoeWxhTXR4KQpjb2xuYW1lcyhzcGFyQ0NwY29ycykgPC0gY29sbmFtZXMoVGFyYVBoeWxhTXR4KQpyb3duYW1lcyhzcGFyQ0NwdmFsKSA8LSBjb2xuYW1lcyhUYXJhUGh5bGFNdHgpCmNvbG5hbWVzKHNwYXJDQ3B2YWwpIDwtIGNvbG5hbWVzKFRhcmFQaHlsYU10eCkKCnNwYXJDQ3Bjb3JzWzE6NSwgMTo1XQpzcGFyQ0NwdmFsWzE6NSwgMTo1XQpgYGAKCkZvciByZWFzb25zIHRoYXQgYXJlIHVuY2xlYXIgdG8gbWUsIHRoZSBjb3JyZWxhdGlvbiB2YWx1ZXMgc2F2ZWQgYnkgdGhlIHNwYXJjYyBwdmFsIHByb2dyYW0gYXJlIGEgbGl0dGxlIGRpZmZlcmVudCB0aGFuIHRoZSBvbmVzIGZyb20gc3BhcmNjLiBXZSdsbCB1c2UgdGhlc2Ugb25lcywgZm9yIG5vIHBhcnRpY3VsYXIgcmVhc29uLiBJJ3ZlIHN1Ym1pdHRlZCBhbiBpc3N1ZSByZXBvcnQgaGVyZSwgd2hpY2ggeW91IGNhbiBmb2xsb3csIGluIGNhc2UgYW55dGhpbmcgaGFwcGVuczoKaHR0cHM6Ly9naXRodWIuY29tL3pkazEyMy9TcGllY0Vhc2kvaXNzdWVzLzEyMQoKIyMgUHJvY2Vzc2luZyB0aGUgc3BhcmNjIG1hdHJpeCBhbmQgYWRkaW5nIGZkciByYXRlcwoKUHJvY2Vzc2luZwoKYGBge3J9CnJlb3JkZXJlZF9hbGxfc3BhcmNjIDwtIHJlb3JkZXJfY29yX2FuZF9wKHNwYXJDQ3Bjb3JzLCBzcGFyQ0NwdmFsKQpyZW9yZGVyZWRfc3BhcmNjQ29yIDwtIHJlb3JkZXJlZF9hbGxfc3BhcmNjJHIKcmVvcmRlcmVkX3NwYXJjY1A8LSByZW9yZGVyZWRfYWxsX3NwYXJjYyRwCgoKc3BhcmNjQ29yX3Byb2Nlc3NlZCA8LSByZW9yZGVyZWRfc3BhcmNjQ29yICAlPiUgZ2V0X3VwcGVyX3RyaSgpICU+JSByZXNoYXBlMjo6bWVsdCgpICU+JSBuYS5vbWl0KCkgJT4lIHJlbmFtZShjb3IgPSB2YWx1ZSkKc3BhcmNjUF9wcm9jZXNzZWQgPC0gcmVvcmRlcmVkX3NwYXJjY1AgICU+JSBnZXRfdXBwZXJfdHJpKCkgJT4lIHJlc2hhcGUyOjptZWx0KCkgJT4lIG5hLm9taXQoKSAlPiUgcmVuYW1lKHAgPSB2YWx1ZSkKCiMgam9pbiB0aGUgdHdvIGRhdGEgZnJhbWVzCgpTcGFyY2NQIDwtIGxlZnRfam9pbihzcGFyY2NDb3JfcHJvY2Vzc2VkLCBzcGFyY2NQX3Byb2Nlc3NlZCwgYnkgPSBjKCJWYXIxIiwgIlZhcjIiKSkgJT4lCiAgIyAjIHJlbW92ZSBzZWxmIGNvcnJlbGF0aW9ucwogICMgZmlsdGVyKFZhcjEgIT0gVmFyMikgJT4lIAogICMgY2FsY3VsYXRlIHRoZSBmYWxzZSBkaXNjb3ZlcnkgcmF0ZSB0byBhZGp1c3QgZm9yIG11bHRpcGxlIHAgdmFsdWVzCiAgbXV0YXRlKGZkciA9IHAuYWRqdXN0KHAsIG1ldGhvZCA9ICJCSCIpKQpgYGAKCgojIyBwbG90IHRoZSBkYXRhCgpgYGB7ciwgZmlnLndpZHRoPSAxMCwgZmlnLmhlaWdodCA9IDEwfQoKc3BhcmNjT2tQIDwtIFNwYXJjY1AlPiUgZmlsdGVyKGZkciA8IGZkclRocmVzaCkgCgpTcGFyY2NQX3Bsb3QgPC0gU3BhcmNjUCAlPiUgZ2dwbG90KGFlcyh4ID0gVmFyMiwgeSA9IFZhcjEsIGZpbGwgPSBjb3IpKSArIGdlb21fdGlsZSgpICsgc2NhbGVfZmlsbF9ncmFkaWVudDIoKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsgZ2VvbV9wb2ludChkYXRhID0gc3BhcmNjT2tQLCBzaGFwZSA9IDEpCgpTcGFyY2NQX3Bsb3QKYGBgCgojIENvbXBhcmluZyB0aGUgdGhyZWUgY29ycmVsYXRpb24gbWF0cmljZXMKYGBge3IgZmlnLndpZHRoPSA4LCBmaWcuaGVpZ2h0ID0gMTV9CmNvd3Bsb3Q6OnBsb3RfZ3JpZCgKc3BlYXJSaG9QX3Bsb3QsCnNwZWFyUmhvUENscl9wbG90LApTcGFyY2NQX3Bsb3QgLCBucm93ID0gMwopCmBgYAoKVGhlIHRocmVlIG1hdHJlY2VzIGFyZSBkaWZmZXJlbnQsIHRob3VnaCBJIGhhdmUgc29tZSB0cm91YmxlIHNheWluZyBvbmUgaXMgb2JqZWN0aXZlbHkgYmV0dGVyIHRoYW4gdGhlIG90aGVycy4KSSBndWVzcyBvbmUgcGF0dGVybiBpcyB0aGF0IHRoZSBzcGVhbWFuIGFsb25lIGVzc2VudGlhbGx5IGhhcyB0d28gbG9vc2UgbW9kdWxlcyBvZiBoaWdobHkgcG9zaXRpdmxleSBjb25uZWN0ZWQgb3JnYW5pc21zLiBUaGUgc3BhcmNjIG1hdHJpeCBoYXMgc21hbGxlciBwb3NpdGl2ZWx5IGNvbm5lY3RlZCBtb2R1bGVzLgoKIyMgV3JpdGluZyBvdXQKCkxldHMgd3JpdGUgb3V0IGFsbCB0aHJlZSBzZXRzIG9mIHJlc3VsdHMuCgpBIGNoYWxsZW5nZSBpcyB0aGF0IFZhcjEgYW5kIFZhciAyIGFyZSBzb21ldGltZXMgaW4gdGhlIG9wcG9zaXRlIG9yZGVyIGZvciB0aGUgc3BhcmNjIHJlc3VsdHMuIExldHMgZml4IHRoaW5ncyBzbyB0aGUgYWxwaGFiZXRpY2FsbHkgZmlyc3QgcGh5bHVtIGlzIGFsd2F5cyB2YXIxCgpgYGB7cn0KZmlyc3RWYXIgPC0gZnVuY3Rpb24oVmFyMSwgVmFyMil7CiAgaWZlbHNlKFZhcjEgPCBWYXIyLCBWYXIxLCBWYXIyKQp9CgpzZWNvbmRWYXIgPC0gZnVuY3Rpb24oVmFyMSwgVmFyMil7CiAgaWZlbHNlKFZhcjEgPCBWYXIyLCBWYXIyLCBWYXIxKQp9CgpTcGFyY2NQX3JlVmFyIDwtIFNwYXJjY1AgJT4lCiAgbXV0YXRlKFZhcjEgPSBhcy5jaGFyYWN0ZXIoVmFyMSksIFZhcjIgPSBhcy5jaGFyYWN0ZXIoVmFyMiksCiAgICAgICAgICAgICAgICAgICBWYXJBID0gbWFwMl9jaHIoVmFyMSwgVmFyMiwgZmlyc3RWYXIpLCAKICAgICAgICAgICAgICAgICAgIFZhckIgPSBtYXAyX2NocihWYXIxLCBWYXIyLCBzZWNvbmRWYXIpCiAgICAgICAgICAgICAgICAgICApICU+JSAKICBzZWxlY3QoLVZhcjEsIC1WYXIyKSAlPiUKICBzZWxlY3QoVmFyQSwgVmFyQiwgZXZlcnl0aGluZygpKQoKc3BlYXJSaG9QX3JlVmFyIDwtIHNwZWFyUmhvUCAlPiUKICBtdXRhdGUoVmFyMSA9IGFzLmNoYXJhY3RlcihWYXIxKSwgVmFyMiA9IGFzLmNoYXJhY3RlcihWYXIyKSwKICAgICAgICAgICAgICAgICAgIFZhckEgPSBtYXAyX2NocihWYXIxLCBWYXIyLCBmaXJzdFZhciksIAogICAgICAgICAgICAgICAgICAgVmFyQiA9IG1hcDJfY2hyKFZhcjEsIFZhcjIsIHNlY29uZFZhcikKICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHNlbGVjdCgtVmFyMSwgLVZhcjIpICU+JQogIHNlbGVjdChWYXJBLCBWYXJCLCBldmVyeXRoaW5nKCkpCgpzcGVhclJob1BfQ2xyX3JlVmFyIDwtIHNwZWFyUmhvUF9DbHIgJT4lCiAgbXV0YXRlKFZhcjEgPSBhcy5jaGFyYWN0ZXIoVmFyMSksIFZhcjIgPSBhcy5jaGFyYWN0ZXIoVmFyMiksCiAgICAgICAgICAgICAgICAgICBWYXJBID0gbWFwMl9jaHIoVmFyMSwgVmFyMiwgZmlyc3RWYXIpLCAKICAgICAgICAgICAgICAgICAgIFZhckIgPSBtYXAyX2NocihWYXIxLCBWYXIyLCBzZWNvbmRWYXIpCiAgICAgICAgICAgICAgICAgICApICU+JSAKICBzZWxlY3QoLVZhcjEsIC1WYXIyKSAlPiUKICBzZWxlY3QoVmFyQSwgVmFyQiwgZXZlcnl0aGluZygpKQoKU3BhcmNjUF9yZW4gPC0gU3BhcmNjUF9yZVZhciAlPiUgZHBseXI6OnJlbmFtZShzcGFyY2MgPSBjb3IsIHAuc3BhcmNjID0gcCwgZmRyLnNwYXJjYyA9IGZkcikKCgoKYGBgCgpTdGlja2luZyBhbGwgb2YgdGhlIHRocmVlIGRpZmZlcmVudCBhbmFseXNlcyB0b2dldGhlciBpbnRvIG9uZSBkYXRhIGZyYW1lCgpgYGB7cn0KQWxsVGFyYU5ldHdvcmtTdGF0cyA8LSBzcGVhclJob1BfcmVWYXIgJT4lIAogIGxlZnRfam9pbihzcGVhclJob1BfQ2xyX3JlVmFyLCBieSA9IGMoIlZhckEiLCAiVmFyQiIpLCBzdWZmaXggPSBjKCIuc3BlYXIiLCAiLmNsciIpKSAlPiUKICBsZWZ0X2pvaW4oU3BhcmNjUF9yZW4sIGJ5ID0gYygiVmFyQSIsICJWYXJCIikpCkFsbFRhcmFOZXR3b3JrU3RhdHMKYGBgCgpOb3cgbGV0cyBzYXZlIGV2ZXJ5dGhpbmcgb3V0CmBgYHtyfQp3cml0ZV9jc3YoQWxsVGFyYU5ldHdvcmtTdGF0cywgIkFuYWx5c2lzL1RhcmFPY2VhbnNTcGVhcm1hblNwYXJDQ0FuYWx5c2lzX3AxMDAuY3N2IikKYGBgCgo=