I ran some preliminary analyses as part of my current research studying mussel larval connectivity (see https://www.intertidalbuffet.com/population-connectivity for details and other analysis). They were interesting enough, and fun enough to code, that I decided to post my process online. Before we get into the analysis though, here’s a bit of back-story.
The Analysis
Data cleaning and prep
So first, we load a bunch of packages we’ll need, and import the data set. You can see that the data, where each line is an individual mussel shell, have a bunch of data on the where that mussel shell was collected, and then the chemistry data of that shell. In this case, the “Site” corresponds to the bay name, and all of the 2 letter codes (e.g., “Mg”, “Mn”, etc) represent different chemical elements.
# Load the necessary packages
library(plyr)
library(reshape2)
library(MASS)
library(stats)
library(ggplot2)
library(ggmap)
# Read in the ICPMS geochemistry data from 2015
ICPMS.all.2015.dat<-read.csv("/Users/scottmorello/Dropbox/Archives/Jobs/Data Science Search/Insight/Insight_Interview_Code/2015.All.Data.csv")
head(ICPMS.all.2015.dat)
Now we subset out just the chemistry data for the larvae, and divide it up between the site level data (.env) and chemistry data (.val). This larval data corresponds to the mussel larvae we raised in each of the bays up and down the coast, and are used to build out map. Thus, we’ll use this data to eventually train our model to classify chemistry signals to the correct bay/site.
# Subset out the data from larvae raised in each bay to train the model
# I'll need to turn Site into a factor, separate out the actual values (".val")
# and data about the sites (".env")
larvae.dat<-subset(ICPMS.all.2015.dat,Type=="Larvae")
larvae.dat$Site<-factor(larvae.dat$Site)
larvae.dat.env<-larvae.dat[,c(1:4)]
larvae.dat.val<-larvae.dat[,-c(1:4)]
head(larvae.dat.env)
head(larvae.dat.val)
I then want to test whether the chemistry data is normally distributed for each group (site). There are certain normality assumptions for the tests I’ll use later, so we should deal with the issue now. I wind up writing a function that will cycle through each unique site and chemical element, and test for normality using a Shapiro-Wilk test. The function will return a list where one table are the p-values associated with Shapiro-Wilk test, and a table of True/False values for whether the test result was significant based on an alpha > 0.05 (True = Normal, False = Not Normal).
I find that the raw chemistry data is pretty non-normal.
# I want test whether data are normally disrtributed, so I make a function to itteratively
# go through each group (site), and test normality of each varaible (geochemical element)
# using a shapiro wilk test
all.norm.test<-function(unique.sites,norm.test.data,norm.test.env){
sites.normtest<-data.frame(site=unique.sites,Mg=NA,Mn=NA,Co=NA,Cu=NA,Sr=NA,Ba=NA,La=NA,Pb=NA)
for (i in 1:length(unique.sites)){
sites.normtest[i,"Mg"]<-shapiro.test(norm.test.data[which(norm.test.env$Site==unique.sites[i]),"Mg"])[2]
sites.normtest[i,"Mn"]<-shapiro.test(norm.test.data[which(norm.test.env$Site==unique.sites[i]),"Mn"])[2]
sites.normtest[i,"Co"]<-shapiro.test(norm.test.data[which(norm.test.env$Site==unique.sites[i]),"Co"])[2]
sites.normtest[i,"Cu"]<-shapiro.test(norm.test.data[which(norm.test.env$Site==unique.sites[i]),"Cu"])[2]
sites.normtest[i,"Sr"]<-shapiro.test(norm.test.data[which(norm.test.env$Site==unique.sites[i]),"Sr"])[2]
sites.normtest[i,"Ba"]<-shapiro.test(norm.test.data[which(norm.test.env$Site==unique.sites[i]),"Ba"])[2]
sites.normtest[i,"La"]<-shapiro.test(norm.test.data[which(norm.test.env$Site==unique.sites[i]),"La"])[2]
sites.normtest[i,"Pb"]<-shapiro.test(norm.test.data[which(norm.test.env$Site==unique.sites[i]),"Pb"])[2]
}
sites.normtest.list<-list(P.Values=cbind(data.frame(Site=sites.normtest$site),round(sites.normtest[,-1],4)),
Significance=cbind(data.frame(Site=sites.normtest$site),sites.normtest[,-1]>0.05))
return(sites.normtest.list)
}
# Now I test for normality, and most variables are not normally distributed
unique.sites.larv<-unique(larvae.dat.env$Site)
all.norm.test(unique.sites.larv,larvae.dat.val,larvae.dat.env)
$P.Values
$Significance
NA
I log transform the chemistry data, and that mostly solves the problem.
# I log transform the data to normalize it, which mostly solves the problem
larvae.dat.val.trans<-log(larvae.dat.val,10)
all.norm.test(unique.sites.larv,larvae.dat.val.trans,larvae.dat.env)
$P.Values
$Significance
NA
The remaining groups/elements that do not pass the Shapiro-Wilk test, I look at their Q-Q Plots to assess normality more subjectively. Generally, they look pretty good, aside from issues such as single outliers and some long tails. I decide that the log transformation is doing all I need it to, and that it’s more important for me to retain as much data as possible to train the model - considering I’ll be dealing with low sample sizes (yes, that becomes an issue later).
# I write a script just to look at the data that failed the Shapiro-Wilk test,
# and plot the Q-Q plot so I can look at them. Generaly, they look ok, except
# for a few which either have a single outlier, or some long tails. Either way, I
# choose to stick with the log transformation
sig.dat<-all.norm.test(unique.sites.larv,larvae.dat.val.trans,larvae.dat.env)$Significance
rownames(sig.dat)<-sig.dat$Site
sig.dat<-sig.dat[,-1]
false.vec <- arrayInd(which(sig.dat==FALSE), dim(sig.dat))
par(mfrow=c(ceiling(sqrt(nrow(false.vec))),floor(sqrt(nrow(false.vec)))))
for (i in 1:nrow(false.vec)){
qqnorm(larvae.dat.val.trans[which(larvae.dat.env$Site==dimnames(sig.dat)[[1]][false.vec[i,1]]),
dimnames(sig.dat)[[2]][false.vec[i,2]]],
main=paste(dimnames(sig.dat)[[1]][false.vec[i,1]],dimnames(sig.dat)[[2]][false.vec[i,2]]))
}
par(mfrow=c(1,1))

So now that I have the training data set, I subset out the test data - chemistry data from the larval shells of wild mussel settlers. I need to correct a few of the site labels since the “TFP” site has multiple names in the data set, but is really only one site. I log transform this data too, which seems to do a decent job of normalizing the data again.
# Now I do the same, subsetting and transformation, but with data from settled mussels
# which we would like to assign to a bay, based on the larval geochemical data I
# imported before. I need to correct one set of names though (2 TFP sites into 1)
settler.dat<-subset(ICPMS.all.2015.dat,Type=="Settler")
settler.dat$Site[which(settler.dat$Site=="TFP_1")]<-"TFP"
settler.dat$Site[which(settler.dat$Site=="TFP_2")]<-"TFP"
settler.dat$Site<-factor(settler.dat$Site)
settler.dat.env<-settler.dat[,c(1:4)]
settler.dat.val<-settler.dat[,-c(1:4)]
settler.dat.val.trans<-log(settler.dat.val,10)
unique.sites.settler<-unique(settler.dat.env$Site)
all.norm.test(unique.sites.settler,settler.dat.val.trans,settler.dat.env)
$P.Values
$Significance
NA
For those data that don’t seem normal, I take a look at the Q-Q plots again, which I’m OK with for the same reasons I stated before.
sig.dat<-all.norm.test(unique.sites.settler,settler.dat.val.trans,settler.dat.env)$Significance
rownames(sig.dat)<-sig.dat$Site
sig.dat<-sig.dat[,-1]
false.vec <- arrayInd(which(sig.dat==FALSE), dim(sig.dat))
par(mfrow=c(ceiling(sqrt(nrow(false.vec))),floor(sqrt(nrow(false.vec)))+1))
for (i in 1:nrow(false.vec)){
qqnorm(settler.dat.val.trans[which(settler.dat.env$Site==dimnames(sig.dat)[[1]][false.vec[i,1]]),
dimnames(sig.dat)[[2]][false.vec[i,2]]],
main=paste(dimnames(sig.dat)[[1]][false.vec[i,1]],dimnames(sig.dat)[[2]][false.vec[i,2]]))
}
par(mfrow=c(1,1))

The model
I choose to use a Quadratic Discriminant Function Analysis (QDA) for a variety of reasons. For one, it’s the most common method for classifying chemical signatures in ecology. That’s not always a good reason to use an analysis though, and so QDA is also relatively interpretable and easy to understand. That, and because it’s been around for a while, are probably why it is so commonly used in ecology. A QDA predicts group membership based on pre-trained groupings by calculating discriminant axes with quadratic combinations of chemical elements. The analysis attempts to minimize the variation within groups and maximize the variation between groups. To be candid, we are working on developing other classification models with our research (e.g., Random Forrest, Infinite Mixture Models), but for today’s preliminary analysis, I thought it best to use something pretty common in the field for doing what we’d like to.
Training the model
So first, we use train the QDA with the chemistry data from larvae we raised - since we know which bays those chemistry signals correspond to. We also use leave-one-out Cross-Validation to test how accurate well the model is at predicting a site based on chemistry data. Looking at the confusion matrix, the model actually doesn’t do so well. It only classifies ~30% of data correctly.
# So now I setup a Quadratic Discriminant Function analysis.
# I chose quadratic becasue the covariance matrices are more than likely
# unequal among groups (sites), based on previous research.
geochem.qda <- qda(larvae.dat.val.trans, larvae.dat.env$Site)
# I'll evaluate the model using Cross Validation
geochem.qda.CV <- qda(larvae.dat.val.trans, larvae.dat.env$Site,CV=TRUE)
# and now look at the confusion matrix, the overall correct assignments (total), and the
# group (site) specific correct assignments
confusion.table<-table(data.frame(Predicted=geochem.qda.CV$class,Actual=larvae.dat.env$Site))
confusion.table.total<-sum(confusion.table[row(confusion.table)==col(confusion.table)])/sum(confusion.table)
confusion.table.total
[1] 0.3007812
When we look at the breakdown by site, there’s a pretty broad range.
confusion.table.diag<-diag(confusion.table/rowSums(confusion.table))
confusion.table.diag
CHR FBE FBW GB MB MBR PHB TFP
0.14814815 0.31034483 0.27777778 0.34883721 0.00000000 0.25000000 0.43373494 0.10000000
WRL
0.05263158
This begs the question: “why are our classifications so bad?”. If we look at how the percent correctly classified to a site relates to how many individual larvae were used to train that site’s signal in the QDA model, there is a pretty direct relationship (i.e., more individuals used to train a site = better classification). This is depicted below with the statistical output from a linear model (and a Q-Q plot to check the fit), and a graphical the output from the linear model showing the fit (black solid line) and 95% CIs (red dotted lines).
# The correct assignments by site seem to be pretty well related to the training sample size
# explaining about 60% of the observed variaiton
train.sizes<-summary(geochem.qda.CV$class)
size.confusion.lm<-lm(confusion.table.diag~train.sizes)
summary(size.confusion.lm)
Call:
lm(formula = confusion.table.diag ~ train.sizes)
Residuals:
Min 1Q Median 3Q Max
-0.11712 -0.05866 -0.02807 0.06792 0.14972
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.081749 0.051575 1.585 0.1570
train.sizes 0.004632 0.001406 3.294 0.0132 *
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.09767 on 7 degrees of freedom
Multiple R-squared: 0.6078, Adjusted R-squared: 0.5518
F-statistic: 10.85 on 1 and 7 DF, p-value: 0.01323
plot(size.confusion.lm,which=2)

size.confusion.predicted<-predict(size.confusion.lm,data.frame(train.sizes=c(0:100)),interval = "confidence")
plot(confusion.table.diag~train.sizes,ylab="Percent Correctly Assigned",xlab="Training Set Sample Size")
lines(c(0:100), size.confusion.predicted[ ,1], lty = "solid", col = "black")
lines(c(0:100), size.confusion.predicted[ ,2], lty = "dashed", col = "red")
lines(c(0:100), size.confusion.predicted[ ,3], lty = "dashed", col = "red")

Unfortunately, there isn’t much we can do with this preliminary analysis. As our lab processes more samples, and builds a larger training data set, the accuracy of our model will hopefully improve. In the meantime, if we want to understand larval connectivity in mussels, this is all we have to work with, so let’s continue.
Predicting The Birthplace of Settled Mussels
I take the data set of wild mussel settler shell chemistry (which the mussels laid down when they were born), and use the QDA to predict the site where that chemistry came from. I implement a flat prior, based on the total number of sites, since we have no underlying assumptions as to which source site produced the most settlers. Below is a plot of the posterior distribution of site assignments, where the x axis are the sites a settler was assigned to based on its shell chemistry, the y axis is the frequency it was assigned to that site, and the panels separate out The site that mussel was found/settled in (i.e., where it traveled to and will grow to be an adult).
# Now I'll try to assign settled mussels to sources based on the trained QDFA of geochemical
# signitures, and use a flat prior
n.sources<-length(unique.sites.larv)
settler.prediction<-predict(geochem.qda,settler.dat.val.trans,prior=(rep(1,n.sources)/n.sources))
# Now let me plot the posterior assignments to see how they're looking. The
# model seems to indicate that the settlers are all coming from one source
settler.prediction.melt<-as.data.frame(settler.prediction$posterior)
settler.prediction.melt$Settled<-settler.dat.env$Site
settler.prediction.melt$Individual<-factor(c(1:nrow(settler.prediction.melt)))
settler.prediction.melt<-melt(settler.prediction.melt)
colnames(settler.prediction.melt)<-c("Location_Settled","Individual","Source","Posterior_Assignment")
settler.posterior.graph<-ggplot(settler.prediction.melt,aes(x=Source,y=Posterior_Assignment,group=Individual))+
geom_line()+
facet_wrap(~Location_Settled,ncol=1)+
theme_bw()
settler.posterior.graph

We can already see that most wild mussel settlers were predicted to have come from the sites CHR or PHB (x axis), no matter where they ended up settling (panels). But, for some settlers, even the site where it was predicted most likely to have come from (i.e., was most frequently assigned - the y axis), the assignment level was pretty low (<50% probability of assignment).
To account for this, we run some Quality Control. For an individual settler, not only do we only keep the best predicted source based on the model, but we also only keep that source assignment if it’s confidence in >50%.
# I summarize the posteriors for each group of settlers, only keeping the predicted source
# with the greatest posterior probability of assignment. Then, I remove any individuals
# where the maximum posterior probability was still <50%. Then I resummarize the data
# by group (settlement site)
settler.prediction.summary<-ddply(settler.prediction.melt,.(Individual,Location_Settled),
summarize,Max.Source=Source[which.max(Posterior_Assignment)],
Max.Val=Posterior_Assignment[which.max(Posterior_Assignment)])
settler.prediction.summary.table<-table(subset(settler.prediction.summary,Max.Val>0.5)[,c(2,3)])/
rowSums(table(subset(settler.prediction.summary,Max.Val>0.5)[,c(2,3)]))
# Now I collapse the data, and attach site coordinates to each settlement point and
# source (I use the match function and another imported dataset)
connect.table<-melt(settler.prediction.summary.table)
colnames(connect.table)<-c("Settled","predicted_source","Connectivity")
site.locs<-read.csv("/Users/scottmorello/Dropbox/Archives/Jobs/Data Science Search/Insight/Insight_Interview_Code/MuLTI-2_Station Information.csv")
connect.table$to.lon<-site.locs$Longitude[match(as.character(connect.table$Settled),
as.character(site.locs$Station.Code))]
connect.table$to.lat<-site.locs$Latitude[match(as.character(connect.table$Settled),
as.character(site.locs$Station.Code))]
connect.table$from.lon<-site.locs$Longitude[match(as.character(connect.table$predicted_source),
as.character(site.locs$Station.Code))]
connect.table$from.lat<-site.locs$Latitude[match(as.character(connect.table$predicted_source),
as.character(site.locs$Station.Code))]
Now I re plot the data as a bar graph, so that it shows for each settlement site along the coast (panels), which birth-site (x axis) settlers were most frequently came from (# of settlers predicted to come from a source/total # of settlers at a site - y axis).
# Now I plot the percent of settlers coming from each larval source by settlement site
# We can see that sites have disproportionately high numbers of larvae coming
# from CHR and PHB
source.graph<-ggplot(connect.table,aes(x=predicted_source,y=Connectivity))+
geom_bar(stat="identity")+
facet_wrap(~Settled,ncol=1)+
xlab("Source")+
theme_bw()
source.graph

Final Answer
This sort of graphical display is pretty difficult to interpret and visualize without some help… so, “ggmap” package to the rescue! Here is a map showing how each site in northern Maine is connected to each other by larval dispersal. Each settlement site (white point) is connected to each birth place by an arrow (from birth place to settlement site). How dense that arrow is corresponds to the percent of settlers that came from that birth place (the darker the arrow, the more settlers were born there). To represent settlers that were born at a site and returned to their birthplace to settle, we use a black circle in the middle of the site (the larger the black circle, the more settlers returned to their birth place).
# Now, I can put all of this info onto a map to better visualize the connections among
# sites (connectivity) and retention within sites.
downeast.map <- get_map(location="Milbridge, Maine",maptype="terrain",zoom=9,crop=FALSE)
connect.map <- ggmap(downeast.map) +
geom_text(data=site.locs[,c(4,5,6)],aes(x=Longitude,y=Latitude,label=Station.Code),
size=4,color="black", vjust=2.75,fontface="bold")+
geom_point(data=site.locs[,c(5,6)],aes(x=Longitude,y=Latitude),size=10,color="black")+
geom_point(data=site.locs[,c(5,6)],aes(x=Longitude,y=Latitude),size=9,color="white")+
geom_point(data=connect.table[-which(as.character(connect.table$Settled)!=as.character(connect.table$predicted_source)),],
aes(x=to.lon,y=to.lat,size=Connectivity),color="black")+
geom_curve(data=connect.table[which(as.character(connect.table$Settled)!=as.character(connect.table$predicted_source)),],
aes(x=to.lon, y=to.lat, xend=from.lon, yend=from.lat,colour=Connectivity,alpha=Connectivity,frame=predicted_source),
arrow=arrow(length=unit(0.4,"cm"), ends="first",angle=15),
color="black",curvature = 0.15,position = "jitter",inherit.aes=TRUE,size=1.5) +
coord_cartesian()+
labs(x="Longitude",y="Latitude")+
scale_x_continuous(limits = c(-68.4, -67.3), expand = c(0, 0)) +
scale_y_continuous(limits = c(44.15, 44.85), expand = c(0, 0))+
scale_size_continuous(name="Self-Seeding")
connect.map

The map does a good job of showing how settlers in the mid-coast area (e.g., GB, PHB, HBR, PLR) mostly come from a site to the northeast (CHR), or from within the mid-coast area (PHB). Settlers in the more southern area (TFP) generally come from the mid-coast (PHB), and occasionally from the area to the far northeast (CHR). This pattern is pretty consistent with other data and analyses we’ve conducted (see https://www.intertidalbuffet.com/population-connectivity for details and other analysis), and tells us that even though our QDA model accuracy needs to be improved (with more training data), the model is probably predicting where settlers come from pretty well!
OK, that was a lot, and much more science-y than the rest of the stuff I include in this section of the webpage. If you have any questions or comments, feel free to email me!
Hope you enjoyed the analysis!
LS0tCnRpdGxlOiAiUXVpY2sgYW5kIFByZWxpbWluYXJ5IEFuYWx5c2lzIG9mIE11c3NlbCBMYXJ2YWwgRGlzcGVyc2FsIFBhdGh3YXlzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpJIHJhbiBzb21lIHByZWxpbWluYXJ5IGFuYWx5c2VzIGFzIHBhcnQgb2YgbXkgY3VycmVudCByZXNlYXJjaCBzdHVkeWluZyBtdXNzZWwgbGFydmFsIGNvbm5lY3Rpdml0eSAoc2VlIGh0dHBzOi8vd3d3LmludGVydGlkYWxidWZmZXQuY29tL3BvcHVsYXRpb24tY29ubmVjdGl2aXR5IGZvciBkZXRhaWxzIGFuZCBvdGhlciBhbmFseXNpcykuIFRoZXkgd2VyZSBpbnRlcmVzdGluZyBlbm91Z2gsIGFuZCBmdW4gZW5vdWdoIHRvIGNvZGUsIHRoYXQgSSBkZWNpZGVkIHRvIHBvc3QgbXkgcHJvY2VzcyBvbmxpbmUuIEJlZm9yZSB3ZSBnZXQgaW50byB0aGUgYW5hbHlzaXMgdGhvdWdoLCBoZXJlJ3MgYSBiaXQgb2YgYmFjay1zdG9yeS4KCiMjIE11c3NlbCBEaXNwZXJzYWwKTXVzc2VscyBhcmUgYmFzaWNhbGx5IHNlZGVudGFyeSBhcyBhZHVsdHMsIGFuZCBkb24ndCBtb3ZlIGZyb20gdGhlIHJvY2svZG9jay93aGF0ZXZlciB0aGV5J3JlIGF0dGFjaGVkIHRvLiBUaGF0IG1lYW5zIHRoYXQgdGhlIG11c3NlbCB5b3Ugb3JkZXIgYXQgYSByZXN0YXVyYW50IGhhcyBhbHdheXMgbGl2ZWQgZXhhY3RseSB3aGVyZSB0aGUgZmlzaGVybWFuIGNhdWdodCBpdCAoYmVmb3JlIGl0IHdvdW5kIHVwIG9uIHlvdXIgcGxhdGUgb2YgY291cnNlKS4gRXZlbiB0aG91Z2ggdGhhdCBtdXNzZWwgaGFzbid0IG1vdmVkIGFzIGFuIGFkdWx0IHRob3VnaCwgdGhlIG11c3NlbCB3YXMgYm9ybiBzb21ld2hlcmUgdmVyeSBkaWZmZXJlbnQsIGFuZCBhcyBhIGJhYnkgKGFrYSAibGFydmFlIiksIGZsb2F0ZWQgdGhyb3VnaCB0aGUgd2F0ZXIgd2l0aCBvY2VhbiBjdXJyZW50cyB1bnRpbCBzZXR0bGluZyBkb3duIHNvbWV3aGVyZSBuZXcgdG8gZ3JvdyB1cCBhbmQgZXZlbnR1YWxseSBiZSBjYXVnaHQgYnkgdGhlIGZpc2hlcm1hbi4gVGhpcyBsYXJ2YWwgZGlzcGVyc2FsIHBoYXNlIGlzIGltcG9ydGFudCB0byBjb25uZWN0aW5nIG11c3NlbCBwb3B1bGF0aW9ucyBzZXBhcmF0ZWQgYWNyb3NzIGxhcmdlIHNwYXRpYWwgc2NhbGVzLCBhbmQgY2FuIGJlIHZlcnkgaW1wb3J0YW50IHRvIG1hbmFnaW5nIG11c3NlbCBwb3B1bGF0aW9ucyBmb3IgZmlzaGVyaWVzLCBvciBwcmVkaWN0aW5nIGhvdyBtdXNzZWwgcG9wdWxhdGlvbnMgbWlnaHQgcmVhY3QgdG8gdGhpbmdzIGxpa2UgY2xpbWF0ZSBjaGFuZ2UgKGFnYWluLCBzZWUgc2VlIHRoZSBibG9nIHNlY3Rpb24gb2YgaHR0cHM6Ly93d3cuaW50ZXJ0aWRhbGJ1ZmZldC5jb20vcG9wdWxhdGlvbi1jb25uZWN0aXZpdHkgZm9yIGRldGFpbHMpLgoKIyMgUmVzZWFyY2ggR29hbApTb21lIGNvbGxlYWd1ZXMgYW5kIEkgYXJlIHRyeWluZyB0byB1bmRlcnN0YW5kIGhvdyBtdXNzZWwgcG9wdWxhdGlvbnMgaW4gTWFpbmUgYXJlIGNvbm5lY3RlZCB0byBlYWNoIG90aGVyIC0gc28sIGlmIHlvdSBzZWUgYW4gYWR1bHQgbXVzc2VsIG9uIGEgcm9jaywgYmUgYWJsZSB0byBwcmVkaWN0IHdoZXJlIHRoYXQgbXVzc2VsIHdhcyBib3JuIGFuZCB0cmF2ZWxlZCBmcm9tIGFzIGEgbGFydmFlLiBPbmUgd2F5IHdlIGFyZSBkb2luZyB0aGlzIGlzIHVzaW5nIHRoZSBjaGVtaWNhbCBjb21wb3NpdGlvbiBvZiBtdXNzZWwgc2hlbGxzLgoKIyMjIyBIb3cgTXVzc2VsIFNoZWxsIENoZW1pc3RyeSBIZWxwcyBVcwpXaGVuIGEgbXVzc2VsIGlzIGJvcm4sIGl0IHN0YXJ0cyBncm93aW5nIGEgc2hlbGwgd2l0aGluIDI0LWhvdXJzLiBUaGUgbXVzc2VsIG5lZWRzIGNhbGNpdW0gdG8gZ3JvdyBpdHMgc2hlbGwsIGFuZCBzbyBleHRyYWN0cyBjYWxjaXVtIGZyb20gdGhlIHdhdGVyIGFyb3VuZCBpdC4gQnkgYWNjaWRlbnQsIHdoZW4gdGFraW5nIHRoaXMgY2FsY2l1bSBmcm9tIHRoZSB3YXRlciwgdGhlIG11c3NlbCBhbHNvIHRha2VzIGFsbCB0aGUgb3RoZXIgY2hlbWljYWxzL2VsZW1lbnRzIGluIHRoZSB3YXRlciBhcm91bmQgaXQgYW5kIGJpbmRzIHRoZW0gdXAgaW4gaXRzIHNoZWxsLiBCZWNhdXNlIHRoZSBjaGVtaXN0cnkgb2YgdGhlIHdhdGVyIGNoYW5nZXMgYXMgeW91IGdvIGZyb20gb25lIHBsYWNlIHRvIGFub3RoZXIgYWxvbmcgdGhlIGNvYXN0LCB0aGF0IG11c3NlbCBpcyB1bmludGVudGlvbmFsbHkgdGFnZ2luZyBpdHNlbGYgd2l0aCBhIGNoZW1pY2FsIHNpZ25hbCBvZiB3aGVyZSBpdCB3YXMgYm9ybi4KCkFzIHNjaWVudGlzdHMsIHdlIHJhaXNlZCBtdXNzZWwgbGFydmFlIHVwIGFuZCBkb3duIHRoZSBjb2FzdCwgbGV0dGluZyB0aGVtIGdyb3cgaW4gdGhlc2UgZGlmZmVyZW50IGNoZW1pc3RyeSB3YXRlcnMuIFdlIHRoZW4gdG9vayB0aG9zZSBtdXNzZWwgbGFydmFlLCBibGFzdGVkIHRoZWlyIHNoZWxscyB3aXRoIGEgbGFzZXIsIGFuZCBzdWNrZWQgdGhlIGFlcm9zb2xpemVkIHNoZWxsIGludG8gYSAibWFzcyBzcGVjdHJvbWV0ZXIiIHRvIG1lYXN1cmUgdGhlIGNoZW1pc3RyeS4gQmFzZWQgb24gdGhpcyBkYXRhLCB3ZSBjYW4gYnVpbGQgYSBtYXAgb2Ygd2hhdCBtdXNzZWwgc2hlbGwgY2hlbWlzdHJ5IHNob3VsZCBsb29rIGxpa2UgaWYgYSBtdXNzZWwgd2FzIGJvcm4gYXQgZGlmZmVyZW50IHBsYWNlcyB1cCBhbmQgZG93biB0aGUgY29hc3QuIFRoZW4sIHdlIGNhbiB0YWtlIGEgd2lsZCBtdXNzZWwgdGhhdCBqdXN0IHNldHRsZWQgZG93biB0byBncm93IHVwIGludG8gYW4gYWR1bHQsIGxvb2sgYXQgdGhlIGNoZW1pc3RyeSBvZiB0aGF0IG11c3NlbCdzIGxhcnZhbCBzaGVsbCwgYW5kIGNvbXBhcmUgaXQgdG8gdGhlIG1hcCB0byBzZWUgd2hlcmUgdGhlIHdpbGQgbXVzc2VsIHdhcyBib3JuLgoKIyMjIyBIb3cgVGhpcyBBbmFseXNpcyBTaG91bGQgV29yawpUaGUgaW50ZW50IG9mIHRoaXMgYW5hbHlzaXMgd2lsbCBiZSB0byB0cmFpbiBhIG1vZGVsIHNvIHdlIGNhbiBmZWVkIGl0IG11c3NlbCBzaGVsbCBjaGVtaXN0cnkgZGF0YSwgYW5kIGl0IHByZWRpY3RzIHdoZXJlIHRoYXQgY2hlbWlzdHJ5IG1hdGNoZXMgb24gb3VyIG1hcC4gV2Ugd2lsbCB0cmFpbiB0aGUgbW9kZWwgd2l0aCB0aGUgY2hlbWlzdHJ5IGRhdGEgZnJvbSBsYXJ2YWUgd2UgcmFpc2VkIGluIGRpZmZlcmVudCBiYXlzIHVwIGFuZCBkb3duIHRoZSBNYWluZSBjb2FzdGxpbmUuIFdlIHdpbGwgdGhlbiBwcmVkaWN0IHRoZSBsYXJ2YWwgc291cmNlcyAoYmlydGhwbGFjZSkgZm9yIHdpbGQgbXVzc2VsIHNldHRsZXJzIHRoYXQgd2UgZ2F0aGVyZWQgaW4gbXVzc2VsIGJlZHMuCgojIyBUaGUgQW5hbHlzaXMKCiMjIyMgRGF0YSBjbGVhbmluZyBhbmQgcHJlcApTbyBmaXJzdCwgd2UgbG9hZCBhIGJ1bmNoIG9mIHBhY2thZ2VzIHdlJ2xsIG5lZWQsIGFuZCBpbXBvcnQgdGhlIGRhdGEgc2V0LiBZb3UgY2FuIHNlZSB0aGF0IHRoZSBkYXRhLCB3aGVyZSBlYWNoIGxpbmUgaXMgYW4gaW5kaXZpZHVhbCBtdXNzZWwgc2hlbGwsIGhhdmUgYSBidW5jaCBvZiBkYXRhIG9uIHRoZSB3aGVyZSB0aGF0IG11c3NlbCBzaGVsbCB3YXMgY29sbGVjdGVkLCBhbmQgdGhlbiB0aGUgY2hlbWlzdHJ5IGRhdGEgb2YgdGhhdCBzaGVsbC4gSW4gdGhpcyBjYXNlLCB0aGUgIlNpdGUiIGNvcnJlc3BvbmRzIHRvIHRoZSBiYXkgbmFtZSwgYW5kIGFsbCBvZiB0aGUgMiBsZXR0ZXIgY29kZXMgKGUuZy4sICJNZyIsICJNbiIsIGV0YykgcmVwcmVzZW50IGRpZmZlcmVudCBjaGVtaWNhbCBlbGVtZW50cy4KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgTG9hZCB0aGUgbmVjZXNzYXJ5IHBhY2thZ2VzCmxpYnJhcnkocGx5cikKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeShNQVNTKQpsaWJyYXJ5KHN0YXRzKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dtYXApCgoKIyBSZWFkIGluIHRoZSBJQ1BNUyBnZW9jaGVtaXN0cnkgZGF0YSBmcm9tIDIwMTUKCklDUE1TLmFsbC4yMDE1LmRhdDwtcmVhZC5jc3YoIi9Vc2Vycy9zY290dG1vcmVsbG8vRHJvcGJveC9BcmNoaXZlcy9Kb2JzL0RhdGEgU2NpZW5jZSBTZWFyY2gvSW5zaWdodC9JbnNpZ2h0X0ludGVydmlld19Db2RlLzIwMTUuQWxsLkRhdGEuY3N2IikKaGVhZChJQ1BNUy5hbGwuMjAxNS5kYXQpCmBgYAoKTm93IHdlIHN1YnNldCBvdXQganVzdCB0aGUgY2hlbWlzdHJ5IGRhdGEgZm9yIHRoZSBsYXJ2YWUsIGFuZCBkaXZpZGUgaXQgdXAgYmV0d2VlbiB0aGUgc2l0ZSBsZXZlbCBkYXRhICguZW52KSBhbmQgY2hlbWlzdHJ5IGRhdGEgKC52YWwpLiBUaGlzIGxhcnZhbCBkYXRhIGNvcnJlc3BvbmRzIHRvIHRoZSBtdXNzZWwgbGFydmFlIHdlIHJhaXNlZCBpbiBlYWNoIG9mIHRoZSBiYXlzIHVwIGFuZCBkb3duIHRoZSBjb2FzdCwgYW5kIGFyZSB1c2VkIHRvIGJ1aWxkIG91dCBtYXAuIFRodXMsIHdlJ2xsIHVzZSB0aGlzIGRhdGEgdG8gZXZlbnR1YWxseSB0cmFpbiBvdXIgbW9kZWwgdG8gY2xhc3NpZnkgY2hlbWlzdHJ5IHNpZ25hbHMgdG8gdGhlIGNvcnJlY3QgYmF5L3NpdGUuCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIFN1YnNldCBvdXQgdGhlIGRhdGEgZnJvbSBsYXJ2YWUgcmFpc2VkIGluIGVhY2ggYmF5IHRvIHRyYWluIHRoZSBtb2RlbAojIEknbGwgbmVlZCB0byB0dXJuIFNpdGUgaW50byBhIGZhY3Rvciwgc2VwYXJhdGUgb3V0IHRoZSBhY3R1YWwgdmFsdWVzICgiLnZhbCIpCiMgYW5kIGRhdGEgYWJvdXQgdGhlIHNpdGVzICgiLmVudiIpCmxhcnZhZS5kYXQ8LXN1YnNldChJQ1BNUy5hbGwuMjAxNS5kYXQsVHlwZT09IkxhcnZhZSIpCmxhcnZhZS5kYXQkU2l0ZTwtZmFjdG9yKGxhcnZhZS5kYXQkU2l0ZSkKbGFydmFlLmRhdC5lbnY8LWxhcnZhZS5kYXRbLGMoMTo0KV0KbGFydmFlLmRhdC52YWw8LWxhcnZhZS5kYXRbLC1jKDE6NCldCmhlYWQobGFydmFlLmRhdC5lbnYpCmhlYWQobGFydmFlLmRhdC52YWwpCmBgYAoKSSB0aGVuIHdhbnQgdG8gdGVzdCB3aGV0aGVyIHRoZSBjaGVtaXN0cnkgZGF0YSBpcyBub3JtYWxseSBkaXN0cmlidXRlZCBmb3IgZWFjaCBncm91cCAoc2l0ZSkuIFRoZXJlIGFyZSBjZXJ0YWluIG5vcm1hbGl0eSBhc3N1bXB0aW9ucyBmb3IgdGhlIHRlc3RzIEknbGwgdXNlIGxhdGVyLCBzbyB3ZSBzaG91bGQgZGVhbCB3aXRoIHRoZSBpc3N1ZSBub3cuIEkgd2luZCB1cCB3cml0aW5nIGEgZnVuY3Rpb24gdGhhdCB3aWxsIGN5Y2xlIHRocm91Z2ggZWFjaCB1bmlxdWUgc2l0ZSBhbmQgY2hlbWljYWwgZWxlbWVudCwgYW5kIHRlc3QgZm9yIG5vcm1hbGl0eSB1c2luZyBhIFNoYXBpcm8tV2lsayB0ZXN0LiBUaGUgZnVuY3Rpb24gd2lsbCByZXR1cm4gYSBsaXN0IHdoZXJlIG9uZSB0YWJsZSBhcmUgdGhlIHAtdmFsdWVzIGFzc29jaWF0ZWQgd2l0aCBTaGFwaXJvLVdpbGsgdGVzdCwgYW5kIGEgdGFibGUgb2YgVHJ1ZS9GYWxzZSB2YWx1ZXMgZm9yIHdoZXRoZXIgdGhlIHRlc3QgcmVzdWx0IHdhcyBzaWduaWZpY2FudCBiYXNlZCBvbiBhbiBhbHBoYSA+IDAuMDUgKFRydWUgPSBOb3JtYWwsIEZhbHNlID0gTm90IE5vcm1hbCkuCgpJIGZpbmQgdGhhdCB0aGUgcmF3IGNoZW1pc3RyeSBkYXRhIGlzIHByZXR0eSBub24tbm9ybWFsLgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBJIHdhbnQgdGVzdCB3aGV0aGVyIGRhdGEgYXJlIG5vcm1hbGx5IGRpc3J0cmlidXRlZCwgc28gSSBtYWtlIGEgZnVuY3Rpb24gdG8gaXR0ZXJhdGl2ZWx5CiMgZ28gdGhyb3VnaCBlYWNoIGdyb3VwIChzaXRlKSwgYW5kIHRlc3Qgbm9ybWFsaXR5IG9mIGVhY2ggdmFyYWlibGUgKGdlb2NoZW1pY2FsIGVsZW1lbnQpCiMgdXNpbmcgYSBzaGFwaXJvIHdpbGsgdGVzdAoKYWxsLm5vcm0udGVzdDwtZnVuY3Rpb24odW5pcXVlLnNpdGVzLG5vcm0udGVzdC5kYXRhLG5vcm0udGVzdC5lbnYpewogIHNpdGVzLm5vcm10ZXN0PC1kYXRhLmZyYW1lKHNpdGU9dW5pcXVlLnNpdGVzLE1nPU5BLE1uPU5BLENvPU5BLEN1PU5BLFNyPU5BLEJhPU5BLExhPU5BLFBiPU5BKQogIGZvciAoaSBpbiAxOmxlbmd0aCh1bmlxdWUuc2l0ZXMpKXsKICAgIHNpdGVzLm5vcm10ZXN0W2ksIk1nIl08LXNoYXBpcm8udGVzdChub3JtLnRlc3QuZGF0YVt3aGljaChub3JtLnRlc3QuZW52JFNpdGU9PXVuaXF1ZS5zaXRlc1tpXSksIk1nIl0pWzJdCiAgICBzaXRlcy5ub3JtdGVzdFtpLCJNbiJdPC1zaGFwaXJvLnRlc3Qobm9ybS50ZXN0LmRhdGFbd2hpY2gobm9ybS50ZXN0LmVudiRTaXRlPT11bmlxdWUuc2l0ZXNbaV0pLCJNbiJdKVsyXQogICAgc2l0ZXMubm9ybXRlc3RbaSwiQ28iXTwtc2hhcGlyby50ZXN0KG5vcm0udGVzdC5kYXRhW3doaWNoKG5vcm0udGVzdC5lbnYkU2l0ZT09dW5pcXVlLnNpdGVzW2ldKSwiQ28iXSlbMl0KICAgIHNpdGVzLm5vcm10ZXN0W2ksIkN1Il08LXNoYXBpcm8udGVzdChub3JtLnRlc3QuZGF0YVt3aGljaChub3JtLnRlc3QuZW52JFNpdGU9PXVuaXF1ZS5zaXRlc1tpXSksIkN1Il0pWzJdCiAgICBzaXRlcy5ub3JtdGVzdFtpLCJTciJdPC1zaGFwaXJvLnRlc3Qobm9ybS50ZXN0LmRhdGFbd2hpY2gobm9ybS50ZXN0LmVudiRTaXRlPT11bmlxdWUuc2l0ZXNbaV0pLCJTciJdKVsyXQogICAgc2l0ZXMubm9ybXRlc3RbaSwiQmEiXTwtc2hhcGlyby50ZXN0KG5vcm0udGVzdC5kYXRhW3doaWNoKG5vcm0udGVzdC5lbnYkU2l0ZT09dW5pcXVlLnNpdGVzW2ldKSwiQmEiXSlbMl0KICAgIHNpdGVzLm5vcm10ZXN0W2ksIkxhIl08LXNoYXBpcm8udGVzdChub3JtLnRlc3QuZGF0YVt3aGljaChub3JtLnRlc3QuZW52JFNpdGU9PXVuaXF1ZS5zaXRlc1tpXSksIkxhIl0pWzJdCiAgICBzaXRlcy5ub3JtdGVzdFtpLCJQYiJdPC1zaGFwaXJvLnRlc3Qobm9ybS50ZXN0LmRhdGFbd2hpY2gobm9ybS50ZXN0LmVudiRTaXRlPT11bmlxdWUuc2l0ZXNbaV0pLCJQYiJdKVsyXQogIH0KICBzaXRlcy5ub3JtdGVzdC5saXN0PC1saXN0KFAuVmFsdWVzPWNiaW5kKGRhdGEuZnJhbWUoU2l0ZT1zaXRlcy5ub3JtdGVzdCRzaXRlKSxyb3VuZChzaXRlcy5ub3JtdGVzdFssLTFdLDQpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNpZ25pZmljYW5jZT1jYmluZChkYXRhLmZyYW1lKFNpdGU9c2l0ZXMubm9ybXRlc3Qkc2l0ZSksc2l0ZXMubm9ybXRlc3RbLC0xXT4wLjA1KSkKICByZXR1cm4oc2l0ZXMubm9ybXRlc3QubGlzdCkKfQoKIyBOb3cgSSB0ZXN0IGZvciBub3JtYWxpdHksIGFuZCBtb3N0IHZhcmlhYmxlcyBhcmUgbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGVkCgp1bmlxdWUuc2l0ZXMubGFydjwtdW5pcXVlKGxhcnZhZS5kYXQuZW52JFNpdGUpCmFsbC5ub3JtLnRlc3QodW5pcXVlLnNpdGVzLmxhcnYsbGFydmFlLmRhdC52YWwsbGFydmFlLmRhdC5lbnYpCmBgYAoKSSBsb2cgdHJhbnNmb3JtIHRoZSBjaGVtaXN0cnkgZGF0YSwgYW5kIHRoYXQgbW9zdGx5IHNvbHZlcyB0aGUgcHJvYmxlbS4KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgSSBsb2cgdHJhbnNmb3JtIHRoZSBkYXRhIHRvIG5vcm1hbGl6ZSBpdCwgd2hpY2ggbW9zdGx5IHNvbHZlcyB0aGUgcHJvYmxlbQoKbGFydmFlLmRhdC52YWwudHJhbnM8LWxvZyhsYXJ2YWUuZGF0LnZhbCwxMCkKYWxsLm5vcm0udGVzdCh1bmlxdWUuc2l0ZXMubGFydixsYXJ2YWUuZGF0LnZhbC50cmFucyxsYXJ2YWUuZGF0LmVudikKYGBgCgpUaGUgcmVtYWluaW5nIGdyb3Vwcy9lbGVtZW50cyB0aGF0IGRvIG5vdCBwYXNzIHRoZSBTaGFwaXJvLVdpbGsgdGVzdCwgSSBsb29rIGF0IHRoZWlyIFEtUSBQbG90cyB0byBhc3Nlc3Mgbm9ybWFsaXR5IG1vcmUgc3ViamVjdGl2ZWx5LiBHZW5lcmFsbHksIHRoZXkgbG9vayBwcmV0dHkgZ29vZCwgYXNpZGUgZnJvbSBpc3N1ZXMgc3VjaCBhcyBzaW5nbGUgb3V0bGllcnMgYW5kIHNvbWUgbG9uZyB0YWlscy4gSSBkZWNpZGUgdGhhdCB0aGUgbG9nIHRyYW5zZm9ybWF0aW9uIGlzIGRvaW5nIGFsbCBJIG5lZWQgaXQgdG8sIGFuZCB0aGF0IGl0J3MgbW9yZSBpbXBvcnRhbnQgZm9yIG1lIHRvIHJldGFpbiBhcyBtdWNoIGRhdGEgYXMgcG9zc2libGUgdG8gdHJhaW4gdGhlIG1vZGVsIC0gY29uc2lkZXJpbmcgSSdsbCBiZSBkZWFsaW5nIHdpdGggbG93IHNhbXBsZSBzaXplcyAoeWVzLCB0aGF0IGJlY29tZXMgYW4gaXNzdWUgbGF0ZXIpLgoKYGBge3IsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgojIEkgd3JpdGUgYSBzY3JpcHQganVzdCB0byBsb29rIGF0IHRoZSBkYXRhIHRoYXQgZmFpbGVkIHRoZSBTaGFwaXJvLVdpbGsgdGVzdCwKIyBhbmQgcGxvdCB0aGUgUS1RIHBsb3Qgc28gSSBjYW4gbG9vayBhdCB0aGVtLiBHZW5lcmFseSwgdGhleSBsb29rIG9rLCBleGNlcHQKIyBmb3IgYSBmZXcgd2hpY2ggZWl0aGVyIGhhdmUgYSBzaW5nbGUgb3V0bGllciwgb3Igc29tZSBsb25nIHRhaWxzLiBFaXRoZXIgd2F5LCBJCiMgY2hvb3NlIHRvIHN0aWNrIHdpdGggdGhlIGxvZyB0cmFuc2Zvcm1hdGlvbgoKc2lnLmRhdDwtYWxsLm5vcm0udGVzdCh1bmlxdWUuc2l0ZXMubGFydixsYXJ2YWUuZGF0LnZhbC50cmFucyxsYXJ2YWUuZGF0LmVudikkU2lnbmlmaWNhbmNlCnJvd25hbWVzKHNpZy5kYXQpPC1zaWcuZGF0JFNpdGUKc2lnLmRhdDwtc2lnLmRhdFssLTFdCmZhbHNlLnZlYyA8LSBhcnJheUluZCh3aGljaChzaWcuZGF0PT1GQUxTRSksIGRpbShzaWcuZGF0KSkKCnBhcihtZnJvdz1jKGNlaWxpbmcoc3FydChucm93KGZhbHNlLnZlYykpKSxmbG9vcihzcXJ0KG5yb3coZmFsc2UudmVjKSkpKSkKZm9yIChpIGluIDE6bnJvdyhmYWxzZS52ZWMpKXsKICBxcW5vcm0obGFydmFlLmRhdC52YWwudHJhbnNbd2hpY2gobGFydmFlLmRhdC5lbnYkU2l0ZT09ZGltbmFtZXMoc2lnLmRhdClbWzFdXVtmYWxzZS52ZWNbaSwxXV0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaW1uYW1lcyhzaWcuZGF0KVtbMl1dW2ZhbHNlLnZlY1tpLDJdXV0sCiAgICAgICAgIG1haW49cGFzdGUoZGltbmFtZXMoc2lnLmRhdClbWzFdXVtmYWxzZS52ZWNbaSwxXV0sZGltbmFtZXMoc2lnLmRhdClbWzJdXVtmYWxzZS52ZWNbaSwyXV0pKQp9CgpwYXIobWZyb3c9YygxLDEpKQpgYGAKClNvIG5vdyB0aGF0IEkgaGF2ZSB0aGUgdHJhaW5pbmcgZGF0YSBzZXQsIEkgc3Vic2V0IG91dCB0aGUgdGVzdCBkYXRhIC0gY2hlbWlzdHJ5IGRhdGEgZnJvbSB0aGUgbGFydmFsIHNoZWxscyBvZiB3aWxkIG11c3NlbCBzZXR0bGVycy4gSSBuZWVkIHRvIGNvcnJlY3QgYSBmZXcgb2YgdGhlIHNpdGUgbGFiZWxzIHNpbmNlIHRoZSAiVEZQIiBzaXRlIGhhcyBtdWx0aXBsZSBuYW1lcyBpbiB0aGUgZGF0YSBzZXQsIGJ1dCBpcyByZWFsbHkgb25seSBvbmUgc2l0ZS4gSSBsb2cgdHJhbnNmb3JtIHRoaXMgZGF0YSB0b28sIHdoaWNoIHNlZW1zIHRvIGRvIGEgZGVjZW50IGpvYiBvZiBub3JtYWxpemluZyB0aGUgZGF0YSBhZ2Fpbi4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIE5vdyBJIGRvIHRoZSBzYW1lLCBzdWJzZXR0aW5nIGFuZCB0cmFuc2Zvcm1hdGlvbiwgYnV0IHdpdGggZGF0YSBmcm9tIHNldHRsZWQgbXVzc2VscwojIHdoaWNoIHdlIHdvdWxkIGxpa2UgdG8gYXNzaWduIHRvIGEgYmF5LCBiYXNlZCBvbiB0aGUgbGFydmFsIGdlb2NoZW1pY2FsIGRhdGEgSQojIGltcG9ydGVkIGJlZm9yZS4gSSBuZWVkIHRvIGNvcnJlY3Qgb25lIHNldCBvZiBuYW1lcyB0aG91Z2ggKDIgVEZQIHNpdGVzIGludG8gMSkKc2V0dGxlci5kYXQ8LXN1YnNldChJQ1BNUy5hbGwuMjAxNS5kYXQsVHlwZT09IlNldHRsZXIiKQpzZXR0bGVyLmRhdCRTaXRlW3doaWNoKHNldHRsZXIuZGF0JFNpdGU9PSJURlBfMSIpXTwtIlRGUCIKc2V0dGxlci5kYXQkU2l0ZVt3aGljaChzZXR0bGVyLmRhdCRTaXRlPT0iVEZQXzIiKV08LSJURlAiCnNldHRsZXIuZGF0JFNpdGU8LWZhY3RvcihzZXR0bGVyLmRhdCRTaXRlKQpzZXR0bGVyLmRhdC5lbnY8LXNldHRsZXIuZGF0WyxjKDE6NCldCnNldHRsZXIuZGF0LnZhbDwtc2V0dGxlci5kYXRbLC1jKDE6NCldCnNldHRsZXIuZGF0LnZhbC50cmFuczwtbG9nKHNldHRsZXIuZGF0LnZhbCwxMCkKCnVuaXF1ZS5zaXRlcy5zZXR0bGVyPC11bmlxdWUoc2V0dGxlci5kYXQuZW52JFNpdGUpCmFsbC5ub3JtLnRlc3QodW5pcXVlLnNpdGVzLnNldHRsZXIsc2V0dGxlci5kYXQudmFsLnRyYW5zLHNldHRsZXIuZGF0LmVudikKYGBgCgpGb3IgdGhvc2UgZGF0YSB0aGF0IGRvbid0IHNlZW0gbm9ybWFsLCBJIHRha2UgYSBsb29rIGF0IHRoZSBRLVEgcGxvdHMgYWdhaW4sIHdoaWNoIEknbSBPSyB3aXRoIGZvciB0aGUgc2FtZSByZWFzb25zIEkgc3RhdGVkIGJlZm9yZS4KCmBgYHtyLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0zLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKc2lnLmRhdDwtYWxsLm5vcm0udGVzdCh1bmlxdWUuc2l0ZXMuc2V0dGxlcixzZXR0bGVyLmRhdC52YWwudHJhbnMsc2V0dGxlci5kYXQuZW52KSRTaWduaWZpY2FuY2UKcm93bmFtZXMoc2lnLmRhdCk8LXNpZy5kYXQkU2l0ZQpzaWcuZGF0PC1zaWcuZGF0WywtMV0KZmFsc2UudmVjIDwtIGFycmF5SW5kKHdoaWNoKHNpZy5kYXQ9PUZBTFNFKSwgZGltKHNpZy5kYXQpKQoKcGFyKG1mcm93PWMoY2VpbGluZyhzcXJ0KG5yb3coZmFsc2UudmVjKSkpLGZsb29yKHNxcnQobnJvdyhmYWxzZS52ZWMpKSkrMSkpCmZvciAoaSBpbiAxOm5yb3coZmFsc2UudmVjKSl7CiAgcXFub3JtKHNldHRsZXIuZGF0LnZhbC50cmFuc1t3aGljaChzZXR0bGVyLmRhdC5lbnYkU2l0ZT09ZGltbmFtZXMoc2lnLmRhdClbWzFdXVtmYWxzZS52ZWNbaSwxXV0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGltbmFtZXMoc2lnLmRhdClbWzJdXVtmYWxzZS52ZWNbaSwyXV1dLAogICAgICAgICBtYWluPXBhc3RlKGRpbW5hbWVzKHNpZy5kYXQpW1sxXV1bZmFsc2UudmVjW2ksMV1dLGRpbW5hbWVzKHNpZy5kYXQpW1syXV1bZmFsc2UudmVjW2ksMl1dKSkKfQoKcGFyKG1mcm93PWMoMSwxKSkKYGBgCgojIyMjIFRoZSBtb2RlbAoKSSBjaG9vc2UgdG8gdXNlIGEgUXVhZHJhdGljIERpc2NyaW1pbmFudCBGdW5jdGlvbiBBbmFseXNpcyAoUURBKSBmb3IgYSB2YXJpZXR5IG9mIHJlYXNvbnMuIEZvciBvbmUsIGl0J3MgdGhlIG1vc3QgY29tbW9uIG1ldGhvZCBmb3IgY2xhc3NpZnlpbmcgY2hlbWljYWwgc2lnbmF0dXJlcyBpbiBlY29sb2d5LiBUaGF0J3Mgbm90IGFsd2F5cyBhIGdvb2QgcmVhc29uIHRvIHVzZSBhbiBhbmFseXNpcyB0aG91Z2gsIGFuZCBzbyBRREEgaXMgYWxzbyByZWxhdGl2ZWx5IGludGVycHJldGFibGUgYW5kIGVhc3kgdG8gdW5kZXJzdGFuZC4gVGhhdCwgYW5kIGJlY2F1c2UgaXQncyBiZWVuIGFyb3VuZCBmb3IgYSB3aGlsZSwgYXJlIHByb2JhYmx5IHdoeSBpdCBpcyBzbyBjb21tb25seSB1c2VkIGluIGVjb2xvZ3kuIEEgUURBIHByZWRpY3RzIGdyb3VwIG1lbWJlcnNoaXAgYmFzZWQgb24gcHJlLXRyYWluZWQgZ3JvdXBpbmdzIGJ5IGNhbGN1bGF0aW5nIGRpc2NyaW1pbmFudCBheGVzIHdpdGggcXVhZHJhdGljIGNvbWJpbmF0aW9ucyBvZiBjaGVtaWNhbCBlbGVtZW50cy4gVGhlIGFuYWx5c2lzIGF0dGVtcHRzIHRvIG1pbmltaXplIHRoZSB2YXJpYXRpb24gd2l0aGluIGdyb3VwcyBhbmQgbWF4aW1pemUgdGhlIHZhcmlhdGlvbiBiZXR3ZWVuIGdyb3Vwcy4gVG8gYmUgY2FuZGlkLCB3ZSBhcmUgd29ya2luZyBvbiBkZXZlbG9waW5nIG90aGVyIGNsYXNzaWZpY2F0aW9uIG1vZGVscyB3aXRoIG91ciByZXNlYXJjaCAoZS5nLiwgUmFuZG9tIEZvcnJlc3QsIEluZmluaXRlIE1peHR1cmUgTW9kZWxzKSwgYnV0IGZvciB0b2RheSdzIHByZWxpbWluYXJ5IGFuYWx5c2lzLCBJIHRob3VnaHQgaXQgYmVzdCB0byB1c2Ugc29tZXRoaW5nIHByZXR0eSBjb21tb24gaW4gdGhlIGZpZWxkIGZvciBkb2luZyB3aGF0IHdlJ2QgbGlrZSB0by4KCiMjIyMgVHJhaW5pbmcgdGhlIG1vZGVsCgpTbyBmaXJzdCwgd2UgdXNlIHRyYWluIHRoZSBRREEgd2l0aCB0aGUgY2hlbWlzdHJ5IGRhdGEgZnJvbSBsYXJ2YWUgd2UgcmFpc2VkIC0gc2luY2Ugd2Uga25vdyB3aGljaCBiYXlzIHRob3NlIGNoZW1pc3RyeSBzaWduYWxzIGNvcnJlc3BvbmQgdG8uIFdlIGFsc28gdXNlIGxlYXZlLW9uZS1vdXQgQ3Jvc3MtVmFsaWRhdGlvbiB0byB0ZXN0IGhvdyBhY2N1cmF0ZSB3ZWxsIHRoZSBtb2RlbCBpcyBhdCBwcmVkaWN0aW5nIGEgc2l0ZSBiYXNlZCBvbiBjaGVtaXN0cnkgZGF0YS4gTG9va2luZyBhdCB0aGUgY29uZnVzaW9uIG1hdHJpeCwgdGhlIG1vZGVsIGFjdHVhbGx5IGRvZXNuJ3QgZG8gc28gd2VsbC4gSXQgb25seSBjbGFzc2lmaWVzIH4zMCUgb2YgZGF0YSBjb3JyZWN0bHkuCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBTbyBub3cgSSBzZXR1cCBhIFF1YWRyYXRpYyBEaXNjcmltaW5hbnQgRnVuY3Rpb24gYW5hbHlzaXMuCiMgSSBjaG9zZSBxdWFkcmF0aWMgYmVjYXN1ZSB0aGUgY292YXJpYW5jZSBtYXRyaWNlcyBhcmUgbW9yZSB0aGFuIGxpa2VseQojIHVuZXF1YWwgYW1vbmcgZ3JvdXBzIChzaXRlcyksIGJhc2VkIG9uIHByZXZpb3VzIHJlc2VhcmNoLgoKZ2VvY2hlbS5xZGEgPC0gcWRhKGxhcnZhZS5kYXQudmFsLnRyYW5zLCBsYXJ2YWUuZGF0LmVudiRTaXRlKQoKIyBJJ2xsIGV2YWx1YXRlIHRoZSBtb2RlbCB1c2luZyBDcm9zcyBWYWxpZGF0aW9uCmdlb2NoZW0ucWRhLkNWIDwtIHFkYShsYXJ2YWUuZGF0LnZhbC50cmFucywgbGFydmFlLmRhdC5lbnYkU2l0ZSxDVj1UUlVFKQoKIyBhbmQgbm93IGxvb2sgYXQgdGhlIGNvbmZ1c2lvbiBtYXRyaXgsIHRoZSBvdmVyYWxsIGNvcnJlY3QgYXNzaWdubWVudHMgKHRvdGFsKSwgYW5kIHRoZQojIGdyb3VwIChzaXRlKSBzcGVjaWZpYyBjb3JyZWN0IGFzc2lnbm1lbnRzCmNvbmZ1c2lvbi50YWJsZTwtdGFibGUoZGF0YS5mcmFtZShQcmVkaWN0ZWQ9Z2VvY2hlbS5xZGEuQ1YkY2xhc3MsQWN0dWFsPWxhcnZhZS5kYXQuZW52JFNpdGUpKQpjb25mdXNpb24udGFibGUudG90YWw8LXN1bShjb25mdXNpb24udGFibGVbcm93KGNvbmZ1c2lvbi50YWJsZSk9PWNvbChjb25mdXNpb24udGFibGUpXSkvc3VtKGNvbmZ1c2lvbi50YWJsZSkKY29uZnVzaW9uLnRhYmxlLnRvdGFsCmBgYAoKV2hlbiB3ZSBsb29rIGF0IHRoZSBicmVha2Rvd24gYnkgc2l0ZSwgdGhlcmUncyBhIHByZXR0eSBicm9hZCByYW5nZS4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpjb25mdXNpb24udGFibGUuZGlhZzwtZGlhZyhjb25mdXNpb24udGFibGUvcm93U3Vtcyhjb25mdXNpb24udGFibGUpKQpjb25mdXNpb24udGFibGUuZGlhZwpgYGAKClRoaXMgYmVncyB0aGUgcXVlc3Rpb246ICJ3aHkgYXJlIG91ciBjbGFzc2lmaWNhdGlvbnMgc28gYmFkPyIuIElmIHdlIGxvb2sgYXQgaG93IHRoZSBwZXJjZW50IGNvcnJlY3RseSBjbGFzc2lmaWVkIHRvIGEgc2l0ZSByZWxhdGVzIHRvIGhvdyBtYW55IGluZGl2aWR1YWwgbGFydmFlIHdlcmUgdXNlZCB0byB0cmFpbiB0aGF0IHNpdGUncyBzaWduYWwgaW4gdGhlIFFEQSBtb2RlbCwgdGhlcmUgaXMgYSBwcmV0dHkgZGlyZWN0IHJlbGF0aW9uc2hpcCAoaS5lLiwgbW9yZSBpbmRpdmlkdWFscyB1c2VkIHRvIHRyYWluIGEgc2l0ZSA9IGJldHRlciBjbGFzc2lmaWNhdGlvbikuIFRoaXMgaXMgZGVwaWN0ZWQgYmVsb3cgd2l0aCB0aGUgc3RhdGlzdGljYWwgb3V0cHV0IGZyb20gYSBsaW5lYXIgbW9kZWwgKGFuZCBhIFEtUSBwbG90IHRvIGNoZWNrIHRoZSBmaXQpLCBhbmQgYSBncmFwaGljYWwgdGhlIG91dHB1dCBmcm9tIHRoZSBsaW5lYXIgbW9kZWwgc2hvd2luZyB0aGUgZml0IChibGFjayBzb2xpZCBsaW5lKSBhbmQgOTUlIENJcyAocmVkIGRvdHRlZCBsaW5lcykuCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIFRoZSBjb3JyZWN0IGFzc2lnbm1lbnRzIGJ5IHNpdGUgc2VlbSB0byBiZSBwcmV0dHkgd2VsbCByZWxhdGVkIHRvIHRoZSB0cmFpbmluZyBzYW1wbGUgc2l6ZQojIGV4cGxhaW5pbmcgYWJvdXQgNjAlIG9mIHRoZSBvYnNlcnZlZCB2YXJpYWl0b24KdHJhaW4uc2l6ZXM8LXN1bW1hcnkoZ2VvY2hlbS5xZGEuQ1YkY2xhc3MpCnNpemUuY29uZnVzaW9uLmxtPC1sbShjb25mdXNpb24udGFibGUuZGlhZ350cmFpbi5zaXplcykKc3VtbWFyeShzaXplLmNvbmZ1c2lvbi5sbSkKcGxvdChzaXplLmNvbmZ1c2lvbi5sbSx3aGljaD0yKQpzaXplLmNvbmZ1c2lvbi5wcmVkaWN0ZWQ8LXByZWRpY3Qoc2l6ZS5jb25mdXNpb24ubG0sZGF0YS5mcmFtZSh0cmFpbi5zaXplcz1jKDA6MTAwKSksaW50ZXJ2YWwgPSAiY29uZmlkZW5jZSIpCnBsb3QoY29uZnVzaW9uLnRhYmxlLmRpYWd+dHJhaW4uc2l6ZXMseWxhYj0iUGVyY2VudCBDb3JyZWN0bHkgQXNzaWduZWQiLHhsYWI9IlRyYWluaW5nIFNldCBTYW1wbGUgU2l6ZSIpCmxpbmVzKGMoMDoxMDApLCBzaXplLmNvbmZ1c2lvbi5wcmVkaWN0ZWRbICwxXSwgbHR5ID0gInNvbGlkIiwgY29sID0gImJsYWNrIikKbGluZXMoYygwOjEwMCksIHNpemUuY29uZnVzaW9uLnByZWRpY3RlZFsgLDJdLCBsdHkgPSAiZGFzaGVkIiwgY29sID0gInJlZCIpCmxpbmVzKGMoMDoxMDApLCBzaXplLmNvbmZ1c2lvbi5wcmVkaWN0ZWRbICwzXSwgbHR5ID0gImRhc2hlZCIsIGNvbCA9ICJyZWQiKQpgYGAKClVuZm9ydHVuYXRlbHksIHRoZXJlIGlzbid0IG11Y2ggd2UgY2FuIGRvIHdpdGggdGhpcyBwcmVsaW1pbmFyeSBhbmFseXNpcy4gQXMgb3VyIGxhYiBwcm9jZXNzZXMgbW9yZSBzYW1wbGVzLCBhbmQgYnVpbGRzIGEgbGFyZ2VyIHRyYWluaW5nIGRhdGEgc2V0LCB0aGUgYWNjdXJhY3kgb2Ygb3VyIG1vZGVsIHdpbGwgaG9wZWZ1bGx5IGltcHJvdmUuIEluIHRoZSBtZWFudGltZSwgaWYgd2Ugd2FudCB0byB1bmRlcnN0YW5kIGxhcnZhbCBjb25uZWN0aXZpdHkgaW4gbXVzc2VscywgdGhpcyBpcyBhbGwgd2UgaGF2ZSB0byB3b3JrIHdpdGgsIHNvIGxldCdzIGNvbnRpbnVlLgoKCiMjIyMgUHJlZGljdGluZyBUaGUgQmlydGhwbGFjZSBvZiBTZXR0bGVkIE11c3NlbHMKCkkgdGFrZSB0aGUgZGF0YSBzZXQgb2Ygd2lsZCBtdXNzZWwgc2V0dGxlciBzaGVsbCBjaGVtaXN0cnkgKHdoaWNoIHRoZSBtdXNzZWxzIGxhaWQgZG93biB3aGVuIHRoZXkgd2VyZSBib3JuKSwgYW5kIHVzZSB0aGUgUURBIHRvIHByZWRpY3QgdGhlIHNpdGUgd2hlcmUgdGhhdCBjaGVtaXN0cnkgY2FtZSBmcm9tLiBJIGltcGxlbWVudCBhIGZsYXQgcHJpb3IsIGJhc2VkIG9uIHRoZSB0b3RhbCBudW1iZXIgb2Ygc2l0ZXMsIHNpbmNlIHdlIGhhdmUgbm8gdW5kZXJseWluZyBhc3N1bXB0aW9ucyBhcyB0byB3aGljaCBzb3VyY2Ugc2l0ZSBwcm9kdWNlZCB0aGUgbW9zdCBzZXR0bGVycy4gQmVsb3cgaXMgYSBwbG90IG9mIHRoZSBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uIG9mIHNpdGUgYXNzaWdubWVudHMsIHdoZXJlIHRoZSB4IGF4aXMgYXJlIHRoZSBzaXRlcyBhIHNldHRsZXIgd2FzIGFzc2lnbmVkIHRvIGJhc2VkIG9uIGl0cyBzaGVsbCBjaGVtaXN0cnksIHRoZSB5IGF4aXMgaXMgdGhlIGZyZXF1ZW5jeSBpdCB3YXMgYXNzaWduZWQgdG8gdGhhdCBzaXRlLCBhbmQgdGhlIHBhbmVscyBzZXBhcmF0ZSBvdXQgVGhlIHNpdGUgdGhhdCBtdXNzZWwgd2FzIGZvdW5kL3NldHRsZWQgaW4gKGkuZS4sIHdoZXJlIGl0IHRyYXZlbGVkIHRvIGFuZCB3aWxsIGdyb3cgdG8gYmUgYW4gYWR1bHQpLgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBOb3cgSSdsbCB0cnkgdG8gYXNzaWduIHNldHRsZWQgbXVzc2VscyB0byBzb3VyY2VzIGJhc2VkIG9uIHRoZSB0cmFpbmVkIFFERkEgb2YgZ2VvY2hlbWljYWwKIyBzaWduaXR1cmVzLCBhbmQgdXNlIGEgZmxhdCBwcmlvcgoKbi5zb3VyY2VzPC1sZW5ndGgodW5pcXVlLnNpdGVzLmxhcnYpCnNldHRsZXIucHJlZGljdGlvbjwtcHJlZGljdChnZW9jaGVtLnFkYSxzZXR0bGVyLmRhdC52YWwudHJhbnMscHJpb3I9KHJlcCgxLG4uc291cmNlcykvbi5zb3VyY2VzKSkKCiMgTm93IGxldCBtZSBwbG90IHRoZSBwb3N0ZXJpb3IgYXNzaWdubWVudHMgdG8gc2VlIGhvdyB0aGV5J3JlIGxvb2tpbmcuIFRoZQojIG1vZGVsIHNlZW1zIHRvIGluZGljYXRlIHRoYXQgdGhlIHNldHRsZXJzIGFyZSBhbGwgY29taW5nIGZyb20gb25lIHNvdXJjZQpzZXR0bGVyLnByZWRpY3Rpb24ubWVsdDwtYXMuZGF0YS5mcmFtZShzZXR0bGVyLnByZWRpY3Rpb24kcG9zdGVyaW9yKQpzZXR0bGVyLnByZWRpY3Rpb24ubWVsdCRTZXR0bGVkPC1zZXR0bGVyLmRhdC5lbnYkU2l0ZQpzZXR0bGVyLnByZWRpY3Rpb24ubWVsdCRJbmRpdmlkdWFsPC1mYWN0b3IoYygxOm5yb3coc2V0dGxlci5wcmVkaWN0aW9uLm1lbHQpKSkKc2V0dGxlci5wcmVkaWN0aW9uLm1lbHQ8LW1lbHQoc2V0dGxlci5wcmVkaWN0aW9uLm1lbHQpCgpjb2xuYW1lcyhzZXR0bGVyLnByZWRpY3Rpb24ubWVsdCk8LWMoIkxvY2F0aW9uX1NldHRsZWQiLCJJbmRpdmlkdWFsIiwiU291cmNlIiwiUG9zdGVyaW9yX0Fzc2lnbm1lbnQiKQoKc2V0dGxlci5wb3N0ZXJpb3IuZ3JhcGg8LWdncGxvdChzZXR0bGVyLnByZWRpY3Rpb24ubWVsdCxhZXMoeD1Tb3VyY2UseT1Qb3N0ZXJpb3JfQXNzaWdubWVudCxncm91cD1JbmRpdmlkdWFsKSkrCiAgZ2VvbV9saW5lKCkrCiAgZmFjZXRfd3JhcCh+TG9jYXRpb25fU2V0dGxlZCxuY29sPTEpKwogIHRoZW1lX2J3KCkKCnNldHRsZXIucG9zdGVyaW9yLmdyYXBoCmBgYAoKV2UgY2FuIGFscmVhZHkgc2VlIHRoYXQgbW9zdCB3aWxkIG11c3NlbCBzZXR0bGVycyB3ZXJlIHByZWRpY3RlZCB0byBoYXZlIGNvbWUgZnJvbSB0aGUgc2l0ZXMgQ0hSCm9yIFBIQiAoeCBheGlzKSwgbm8gbWF0dGVyIHdoZXJlIHRoZXkgZW5kZWQgdXAgc2V0dGxpbmcgKHBhbmVscykuIEJ1dCwgZm9yIHNvbWUgc2V0dGxlcnMsIGV2ZW4gdGhlIHNpdGUgd2hlcmUgaXQgd2FzIHByZWRpY3RlZCBtb3N0IGxpa2VseSB0byBoYXZlIGNvbWUgZnJvbSAoaS5lLiwgd2FzIG1vc3QgZnJlcXVlbnRseSBhc3NpZ25lZCAtIHRoZSB5IGF4aXMpLCB0aGUgYXNzaWdubWVudCBsZXZlbCB3YXMgcHJldHR5IGxvdyAoPDUwJSBwcm9iYWJpbGl0eSBvZiBhc3NpZ25tZW50KS4KClRvIGFjY291bnQgZm9yIHRoaXMsIHdlIHJ1biBzb21lIFF1YWxpdHkgQ29udHJvbC4gRm9yIGFuIGluZGl2aWR1YWwgc2V0dGxlciwgbm90IG9ubHkgZG8gd2Ugb25seSBrZWVwIHRoZSBiZXN0IHByZWRpY3RlZCBzb3VyY2UgYmFzZWQgb24gdGhlIG1vZGVsLCBidXQgd2UgYWxzbyBvbmx5IGtlZXAgdGhhdCBzb3VyY2UgYXNzaWdubWVudCBpZiBpdCdzIGNvbmZpZGVuY2UgaW4gPjUwJS4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIEkgc3VtbWFyaXplIHRoZSBwb3N0ZXJpb3JzIGZvciBlYWNoIGdyb3VwIG9mIHNldHRsZXJzLCBvbmx5IGtlZXBpbmcgdGhlIHByZWRpY3RlZCBzb3VyY2UKIyB3aXRoIHRoZSBncmVhdGVzdCBwb3N0ZXJpb3IgcHJvYmFiaWxpdHkgb2YgYXNzaWdubWVudC4gVGhlbiwgSSByZW1vdmUgYW55IGluZGl2aWR1YWxzCiMgd2hlcmUgdGhlIG1heGltdW0gcG9zdGVyaW9yIHByb2JhYmlsaXR5IHdhcyBzdGlsbCA8NTAlLiBUaGVuIEkgcmVzdW1tYXJpemUgdGhlIGRhdGEKIyBieSBncm91cCAoc2V0dGxlbWVudCBzaXRlKQpzZXR0bGVyLnByZWRpY3Rpb24uc3VtbWFyeTwtZGRwbHkoc2V0dGxlci5wcmVkaWN0aW9uLm1lbHQsLihJbmRpdmlkdWFsLExvY2F0aW9uX1NldHRsZWQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXplLE1heC5Tb3VyY2U9U291cmNlW3doaWNoLm1heChQb3N0ZXJpb3JfQXNzaWdubWVudCldLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTWF4LlZhbD1Qb3N0ZXJpb3JfQXNzaWdubWVudFt3aGljaC5tYXgoUG9zdGVyaW9yX0Fzc2lnbm1lbnQpXSkKc2V0dGxlci5wcmVkaWN0aW9uLnN1bW1hcnkudGFibGU8LXRhYmxlKHN1YnNldChzZXR0bGVyLnByZWRpY3Rpb24uc3VtbWFyeSxNYXguVmFsPjAuNSlbLGMoMiwzKV0pLwogIHJvd1N1bXModGFibGUoc3Vic2V0KHNldHRsZXIucHJlZGljdGlvbi5zdW1tYXJ5LE1heC5WYWw+MC41KVssYygyLDMpXSkpCgojIE5vdyBJIGNvbGxhcHNlIHRoZSBkYXRhLCBhbmQgYXR0YWNoIHNpdGUgY29vcmRpbmF0ZXMgdG8gZWFjaCBzZXR0bGVtZW50IHBvaW50IGFuZAojIHNvdXJjZSAoSSB1c2UgdGhlIG1hdGNoIGZ1bmN0aW9uIGFuZCBhbm90aGVyIGltcG9ydGVkIGRhdGFzZXQpCgpjb25uZWN0LnRhYmxlPC1tZWx0KHNldHRsZXIucHJlZGljdGlvbi5zdW1tYXJ5LnRhYmxlKQpjb2xuYW1lcyhjb25uZWN0LnRhYmxlKTwtYygiU2V0dGxlZCIsInByZWRpY3RlZF9zb3VyY2UiLCJDb25uZWN0aXZpdHkiKQoKc2l0ZS5sb2NzPC1yZWFkLmNzdigiL1VzZXJzL3Njb3R0bW9yZWxsby9Ecm9wYm94L0FyY2hpdmVzL0pvYnMvRGF0YSBTY2llbmNlIFNlYXJjaC9JbnNpZ2h0L0luc2lnaHRfSW50ZXJ2aWV3X0NvZGUvTXVMVEktMl9TdGF0aW9uIEluZm9ybWF0aW9uLmNzdiIpCmNvbm5lY3QudGFibGUkdG8ubG9uPC1zaXRlLmxvY3MkTG9uZ2l0dWRlW21hdGNoKGFzLmNoYXJhY3Rlcihjb25uZWN0LnRhYmxlJFNldHRsZWQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoc2l0ZS5sb2NzJFN0YXRpb24uQ29kZSkpXQpjb25uZWN0LnRhYmxlJHRvLmxhdDwtc2l0ZS5sb2NzJExhdGl0dWRlW21hdGNoKGFzLmNoYXJhY3Rlcihjb25uZWN0LnRhYmxlJFNldHRsZWQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihzaXRlLmxvY3MkU3RhdGlvbi5Db2RlKSldCgpjb25uZWN0LnRhYmxlJGZyb20ubG9uPC1zaXRlLmxvY3MkTG9uZ2l0dWRlW21hdGNoKGFzLmNoYXJhY3Rlcihjb25uZWN0LnRhYmxlJHByZWRpY3RlZF9zb3VyY2UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihzaXRlLmxvY3MkU3RhdGlvbi5Db2RlKSldCmNvbm5lY3QudGFibGUkZnJvbS5sYXQ8LXNpdGUubG9jcyRMYXRpdHVkZVttYXRjaChhcy5jaGFyYWN0ZXIoY29ubmVjdC50YWJsZSRwcmVkaWN0ZWRfc291cmNlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihzaXRlLmxvY3MkU3RhdGlvbi5Db2RlKSldCmBgYAoKTm93IEkgcmUgcGxvdCB0aGUgZGF0YSBhcyBhIGJhciBncmFwaCwgc28gdGhhdCBpdCBzaG93cyBmb3IgZWFjaCBzZXR0bGVtZW50IHNpdGUgYWxvbmcgdGhlIGNvYXN0IChwYW5lbHMpLCB3aGljaCBiaXJ0aC1zaXRlICh4IGF4aXMpIHNldHRsZXJzIHdlcmUgbW9zdCBmcmVxdWVudGx5IGNhbWUgZnJvbSAoIyBvZiBzZXR0bGVycyBwcmVkaWN0ZWQgdG8gY29tZSBmcm9tIGEgc291cmNlL3RvdGFsICMgb2Ygc2V0dGxlcnMgYXQgYSBzaXRlIC0geSBheGlzKS4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIE5vdyBJIHBsb3QgdGhlIHBlcmNlbnQgb2Ygc2V0dGxlcnMgY29taW5nIGZyb20gZWFjaCBsYXJ2YWwgc291cmNlIGJ5IHNldHRsZW1lbnQgc2l0ZQojIFdlIGNhbiBzZWUgdGhhdCBzaXRlcyBoYXZlIGRpc3Byb3BvcnRpb25hdGVseSBoaWdoIG51bWJlcnMgb2YgbGFydmFlIGNvbWluZwojIGZyb20gQ0hSIGFuZCBQSEIKCgpzb3VyY2UuZ3JhcGg8LWdncGxvdChjb25uZWN0LnRhYmxlLGFlcyh4PXByZWRpY3RlZF9zb3VyY2UseT1Db25uZWN0aXZpdHkpKSsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpKwogIGZhY2V0X3dyYXAoflNldHRsZWQsbmNvbD0xKSsKICB4bGFiKCJTb3VyY2UiKSsKICB0aGVtZV9idygpCgpzb3VyY2UuZ3JhcGgKYGBgCgojIyMjIEZpbmFsIEFuc3dlcgoKVGhpcyBzb3J0IG9mIGdyYXBoaWNhbCBkaXNwbGF5IGlzIHByZXR0eSBkaWZmaWN1bHQgdG8gaW50ZXJwcmV0IGFuZCB2aXN1YWxpemUgd2l0aG91dCBzb21lIGhlbHAuLi4gc28sICJnZ21hcCIgcGFja2FnZSB0byB0aGUgcmVzY3VlISBIZXJlIGlzIGEgbWFwIHNob3dpbmcgaG93IGVhY2ggc2l0ZSBpbiBub3J0aGVybiBNYWluZSBpcyBjb25uZWN0ZWQgdG8gZWFjaCBvdGhlciBieSBsYXJ2YWwgZGlzcGVyc2FsLiBFYWNoIHNldHRsZW1lbnQgc2l0ZSAod2hpdGUgcG9pbnQpIGlzIGNvbm5lY3RlZCB0byBlYWNoIGJpcnRoIHBsYWNlIGJ5IGFuIGFycm93IChmcm9tIGJpcnRoIHBsYWNlIHRvIHNldHRsZW1lbnQgc2l0ZSkuIEhvdyBkZW5zZSB0aGF0IGFycm93IGlzIGNvcnJlc3BvbmRzIHRvIHRoZSBwZXJjZW50IG9mIHNldHRsZXJzIHRoYXQgY2FtZSBmcm9tIHRoYXQgYmlydGggcGxhY2UgKHRoZSBkYXJrZXIgdGhlIGFycm93LCB0aGUgbW9yZSBzZXR0bGVycyB3ZXJlIGJvcm4gdGhlcmUpLiBUbyByZXByZXNlbnQgc2V0dGxlcnMgdGhhdCB3ZXJlIGJvcm4gYXQgYSBzaXRlIGFuZCByZXR1cm5lZCB0byB0aGVpciBiaXJ0aHBsYWNlIHRvIHNldHRsZSwgd2UgdXNlIGEgYmxhY2sgY2lyY2xlIGluIHRoZSBtaWRkbGUgb2YgdGhlIHNpdGUgKHRoZSBsYXJnZXIgdGhlIGJsYWNrIGNpcmNsZSwgdGhlIG1vcmUgc2V0dGxlcnMgcmV0dXJuZWQgdG8gdGhlaXIgYmlydGggcGxhY2UpLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgTm93LCBJIGNhbiBwdXQgYWxsIG9mIHRoaXMgaW5mbyBvbnRvIGEgbWFwIHRvIGJldHRlciB2aXN1YWxpemUgdGhlIGNvbm5lY3Rpb25zIGFtb25nCiMgc2l0ZXMgKGNvbm5lY3Rpdml0eSkgYW5kIHJldGVudGlvbiB3aXRoaW4gc2l0ZXMuCgpkb3duZWFzdC5tYXAgPC0gZ2V0X21hcChsb2NhdGlvbj0iTWlsYnJpZGdlLCBNYWluZSIsbWFwdHlwZT0idGVycmFpbiIsem9vbT05LGNyb3A9RkFMU0UpCgpjb25uZWN0Lm1hcCA8LSBnZ21hcChkb3duZWFzdC5tYXApICsgCiAgZ2VvbV90ZXh0KGRhdGE9c2l0ZS5sb2NzWyxjKDQsNSw2KV0sYWVzKHg9TG9uZ2l0dWRlLHk9TGF0aXR1ZGUsbGFiZWw9U3RhdGlvbi5Db2RlKSwKICAgICAgICAgICAgc2l6ZT00LGNvbG9yPSJibGFjayIsIHZqdXN0PTIuNzUsZm9udGZhY2U9ImJvbGQiKSsKICBnZW9tX3BvaW50KGRhdGE9c2l0ZS5sb2NzWyxjKDUsNildLGFlcyh4PUxvbmdpdHVkZSx5PUxhdGl0dWRlKSxzaXplPTEwLGNvbG9yPSJibGFjayIpKwogIGdlb21fcG9pbnQoZGF0YT1zaXRlLmxvY3NbLGMoNSw2KV0sYWVzKHg9TG9uZ2l0dWRlLHk9TGF0aXR1ZGUpLHNpemU9OSxjb2xvcj0id2hpdGUiKSsKICBnZW9tX3BvaW50KGRhdGE9Y29ubmVjdC50YWJsZVstd2hpY2goYXMuY2hhcmFjdGVyKGNvbm5lY3QudGFibGUkU2V0dGxlZCkhPWFzLmNoYXJhY3Rlcihjb25uZWN0LnRhYmxlJHByZWRpY3RlZF9zb3VyY2UpKSxdLAogICAgICAgICAgICAgYWVzKHg9dG8ubG9uLHk9dG8ubGF0LHNpemU9Q29ubmVjdGl2aXR5KSxjb2xvcj0iYmxhY2siKSsKICBnZW9tX2N1cnZlKGRhdGE9Y29ubmVjdC50YWJsZVt3aGljaChhcy5jaGFyYWN0ZXIoY29ubmVjdC50YWJsZSRTZXR0bGVkKSE9YXMuY2hhcmFjdGVyKGNvbm5lY3QudGFibGUkcHJlZGljdGVkX3NvdXJjZSkpLF0sIAogICAgICAgICAgICAgYWVzKHg9dG8ubG9uLCB5PXRvLmxhdCwgeGVuZD1mcm9tLmxvbiwgeWVuZD1mcm9tLmxhdCxjb2xvdXI9Q29ubmVjdGl2aXR5LGFscGhhPUNvbm5lY3Rpdml0eSxmcmFtZT1wcmVkaWN0ZWRfc291cmNlKSwKICAgICAgICAgICAgIGFycm93PWFycm93KGxlbmd0aD11bml0KDAuNCwiY20iKSwgZW5kcz0iZmlyc3QiLGFuZ2xlPTE1KSwKICAgICAgICAgICAgIGNvbG9yPSJibGFjayIsY3VydmF0dXJlID0gMC4xNSxwb3NpdGlvbiA9ICJqaXR0ZXIiLGluaGVyaXQuYWVzPVRSVUUsc2l6ZT0xLjUpICsKICBjb29yZF9jYXJ0ZXNpYW4oKSsKICBsYWJzKHg9IkxvbmdpdHVkZSIseT0iTGF0aXR1ZGUiKSsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygtNjguNCwgLTY3LjMpLCBleHBhbmQgPSBjKDAsIDApKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoNDQuMTUsIDQ0Ljg1KSwgZXhwYW5kID0gYygwLCAwKSkrCiAgc2NhbGVfc2l6ZV9jb250aW51b3VzKG5hbWU9IlNlbGYtU2VlZGluZyIpCgpjb25uZWN0Lm1hcApgYGAKClRoZSBtYXAgZG9lcyBhIGdvb2Qgam9iIG9mIHNob3dpbmcgaG93IHNldHRsZXJzIGluIHRoZSBtaWQtY29hc3QgYXJlYSAoZS5nLiwgR0IsIFBIQiwgSEJSLCBQTFIpIG1vc3RseSBjb21lIGZyb20gYSBzaXRlIHRvIHRoZSBub3J0aGVhc3QgKENIUiksIG9yIGZyb20gd2l0aGluIHRoZSBtaWQtY29hc3QgYXJlYSAoUEhCKS4gU2V0dGxlcnMgaW4gdGhlIG1vcmUgc291dGhlcm4gYXJlYSAoVEZQKSBnZW5lcmFsbHkgY29tZSBmcm9tIHRoZSBtaWQtY29hc3QgKFBIQiksIGFuZCBvY2Nhc2lvbmFsbHkgZnJvbSB0aGUgYXJlYSB0byB0aGUgZmFyIG5vcnRoZWFzdCAoQ0hSKS4gVGhpcyBwYXR0ZXJuIGlzIHByZXR0eSBjb25zaXN0ZW50IHdpdGggb3RoZXIgZGF0YSBhbmQgYW5hbHlzZXMgd2UndmUgY29uZHVjdGVkIChzZWUgaHR0cHM6Ly93d3cuaW50ZXJ0aWRhbGJ1ZmZldC5jb20vcG9wdWxhdGlvbi1jb25uZWN0aXZpdHkgZm9yIGRldGFpbHMgYW5kIG90aGVyIGFuYWx5c2lzKSwgYW5kIHRlbGxzIHVzIHRoYXQgZXZlbiB0aG91Z2ggb3VyIFFEQSBtb2RlbCBhY2N1cmFjeSBuZWVkcyB0byBiZSBpbXByb3ZlZCAod2l0aCBtb3JlIHRyYWluaW5nIGRhdGEpLCB0aGUgbW9kZWwgaXMgcHJvYmFibHkgcHJlZGljdGluZyB3aGVyZSBzZXR0bGVycyBjb21lIGZyb20gcHJldHR5IHdlbGwhCgpPSywgdGhhdCB3YXMgYSBsb3QsIGFuZCBtdWNoIG1vcmUgc2NpZW5jZS15IHRoYW4gdGhlIHJlc3Qgb2YgdGhlIHN0dWZmIEkgaW5jbHVkZSBpbiB0aGlzIHNlY3Rpb24gb2YgdGhlIHdlYnBhZ2UuIElmIHlvdSBoYXZlIGFueSBxdWVzdGlvbnMgb3IgY29tbWVudHMsIGZlZWwgZnJlZSB0byBlbWFpbCBtZSEKCkhvcGUgeW91IGVuam95ZWQgdGhlIGFuYWx5c2lzIQoK