\(\\\) \(\\\)
This project is part of my PhD dissertation project.
This section follows from the first section dedicated to the Descriptive Analysis of our co-authorship network (available HERE).
The purpose of this section is the use of mathematical models to describe our malaria co-authorship network.
According to Kolaczyk and Csardi (2009), a model for a network graph is a collection of possible graphs \(\mathscr{G}\) with a probability distribution \(\mathbb{P}_\theta\) defined as: \[\{ \mathbb{P}_\theta\ (G), G \in \mathscr{G} : \theta \in \Theta \}\] where \(\theta\) is a vector of parameters ranging over values in \(\Theta\).
The use of network models is to test significance of the characteristics of our co-authorship network and unravel the mechanisms underlying the observed structure of our malaria co-authorship network. It is important to note that mathematical models tend to be simpler than statistical models (which will be next) and easier for mathematical analysis. But in contrast to statistical models, they do not permit model fitting and assessment.
They are different mathematical models for network graphs:
Classical Random Graph Models: First established by Erdos and Renyi (ref), it specifies a collection of graphs \(\mathscr{G}\) with a uniform probability \(\mathbb{P}(\cdot)\) over \(\mathscr{G}\). A variant of this model called the Bernoulli Random Graph Model was also defined by Gilbert (ref).
Generalized Random Graph Models: These models emanated from the generalization of Erdos and Renyi’s formulation, defining a collection of graphs \(\mathscr{G}\) with prespecified degree sequence.
Mechanistic Network Graph Models: These models mimic real-world phenomena and include Small-World Models often referred to as “six degree separation”. It was introduced by Watts and Strogatz (ref) and have since received a lot of interests in the existing literature. Small-world networks usually exhibit high levels of clustering and small distances between nodes. Classical models are not fit to better represent such behaviors since they usually display low levels of clustering and small distance between nodes. Examples of known small-world networks include the network of connected proteins or the transcriptional networks of genes. A variant of Small-World models is the Preferential Attachment Models defined based on the popular principle of “the rich get richer”. Preferential attachment models gained fascination after the work of Barabasi and Albert (ref) who studied the growth of the World Wide Web. Examples of Preferential Attachment networks include that of World Wide Web and the scientific citation network. An important characteristic of these models is that as time tend to infinity, there degree distribution tend to follow a power law.
Now that we have defined the main mathematical models for Network modeling, let’s use Monte Carlo based simulations methods to assess the significance of our network characteristics.
Definition: Given an observed graph \(G^{obs}\) and some structural characteristics \(\eta (\cdot)\). Our goal is to assess if \(\eta (G^{obs})\) is unusal i.e is significant. We then compare \(\eta (G^{obs})\) to collection of values \(\{\eta(G):G \in \mathscr{G}\}\). If \(\eta (G^{obs})\) is too extreme with respect to this collection, then we have enough evidence to assert that \(\eta (G^{obs})\) is not a uniform draw from \(\mathscr{G}\).
Let’s recall that our hierarchical clustering method of community detection algorithm has identified 23 different clusters/communities in our co-authorship network out of which 7 form a giant component (ref. Descriptive Analysis). The question of interest here is whether the number of communities detected is unexpected or not. Let’s use Monte Carlo simulations to test the significance of this observed characteristics on our malaria co-authorship network. Let’s also test the significance of other characteristics such as the clustering coefficient and the average shortest path length. We run 1000 Monte-Carlo simulations on all the above described models, assess significance and small-world properties based on the results.
Attention: Given the size of our network, the simulations are expected to last several hours to complete. We trace the processing time on our computer, a 16GB 12-core CPU and 2-core GPU powered desktop. We decide to parallelize the processing in order to speed-up the processing.
We first load the list of the igraph objects we saved from our previous tutorial:
load('./Rdata/mnet.rda')
load('./Rdata/mnet2.rda')
load('./Rdata/mnet2.gc.rda')
load('./Rdata/auth_data.rda')
load('./Rdata/edges.rda')
\(\\\)
Loading required packages:
library(igraph)
library(foreach)
library(doParallel)
library(lubridate)
\(\\\)
Let’s first set up parallel backend
cores=detectCores() # get number of cores=12
cl <- makeCluster(cores[1]-2) #I decide to run leave 2 cores out
registerDoParallel(cl)
nv <- vcount(mnet2) # number of vertices in mnet2
ne <- ecount(mnet2) # number of edges in mnet2
degs <- degree(mnet2) # degree distribution in mnet2
# For the small-world properties we estimate the probability and the average neighborhood size from the observed network
p <- ne / (ne * (ne -1)) # Estimated probability
r <- mean(neighborhood.size(mnet2, 1)) # Estimated average neighborhood size
# 1000 trials
ntrials <- 1000
# Defining function to combine results
comb <- function(x, ...) {
lapply(seq_along(x),
function(i) c(x[[i]], lapply(list(...), function(y) y[[i]])))
}
start.time <- Sys.time() # Capturing Parallel Computing processing start time
results <- foreach(1:ntrials, .combine='comb', .multicombine=TRUE,
.init=list(list(),list(),list(),list(),list(),list(),
list(),list(),list(),list(),list(),list())) %dopar% {
library(igraph)
# Erdos-Renyi RGM
g.rg <- erdos.renyi.game(nv, ne, type="gnm")
c.rg <- fastgreedy.community(g.rg)
# num.comm.rg[i] <- length(c.rg)
# av.path.len.rg[i] <- average.path.length(g.rg, directed = F)
# av.clus.rg[i] <- transitivity(g.rg)
num.comm.rg <- length(c.rg)
av.path.len.rg <- average.path.length(g.rg, directed = F)
av.clus.rg <- transitivity(g.rg)
# Generalized RGM
g.grg <- degree.sequence.game(degs, method="vl")
c.grg <- fastgreedy.community(g.grg)
num.comm.grg <- length(c.grg)
av.path.len.grg <- average.path.length(g.grg, directed = F)
av.clus.grg <- transitivity(g.grg)
# Watts and Strogatz small-world Model
g.ws <- watts.strogatz.game(1, nv, r, p)
c.ws <- fastgreedy.community(g.ws)
num.comm.ws <- length(c.ws)
av.path.len.ws <- average.path.length(g.ws, directed = F)
av.clus.ws <- transitivity(g.ws)
# Barabasi-Albert Preferential Attachement Model
g.ba <- barabasi.game(nv, directed = FALSE)
c.ba <- fastgreedy.community(g.ba)
num.comm.ba <- length(c.ba)
av.path.len.ba <- average.path.length(g.ba, directed = F)
av.clus.ba <- transitivity(g.ba)
list(
num.comm.rg, av.path.len.rg, av.clus.rg,
num.comm.grg, av.path.len.grg, av.clus.grg,
num.comm.ws, av.path.len.ws, av.clus.ws,
num.comm.ba, av.path.len.ba, av.clus.ba
)
}
stopCluster(cl) # Stop cluster
end.time <- Sys.time() # Capturing Parallel Computing processing end time
time.taken <- end.time - start.time
time.taken
Time difference of 6.194901 mins
Let’s now reassign each element of the results to a variable:
num.comm.rg <- unlist(results[[1]]);
av.path.len.rg <- unlist(results[[2]])
av.clus.rg <- unlist(results[[3]])
num.comm.grg <- unlist(results[[4]])
av.path.len.grg <- unlist(results[[5]])
av.clus.grg <- unlist(results[[6]])
num.comm.ws <- unlist(results[[7]])
av.path.len.ws <- unlist(results[[8]])
av.clus.ws <- unlist(results[[9]])
num.comm.ba <- unlist(results[[10]])
av.path.len.ba <- unlist(results[[11]])
av.clus.ba <- unlist(results[[12]])
Let’s visualize the results:
Number of detected communities for the random graph models
# Barplot of the number of communities for the Random Models
rslts <- c(num.comm.rg,num.comm.grg)
indx <- c(rep(0, ntrials), rep(1, ntrials))
counts <- table(indx, rslts)/ntrials
barplot(counts, beside=TRUE, col=c("blue", "red"),
xlab="Number of Communities",
ylab="Relative Frequency",
legend=c("Fixed Size", "Fixed Degree Sequence"))

Let’s recall that our hierarchical clustering algorithm detected 23 communities in our co-authorship network. The graph above clearly demonstrates that the number of communities detected would be considered unusual from the perspective of both Classical random graphs and generalized random graphs.
This can be further quantified by a one-sample t-test comparing the observed number of communities to the mean number of communities from the Monte-Carlo simulations. Given that t-test assumes normality of the data, a simple histogram or a test of normality (Ryan-Joiner, Kolmogorov-Smirnov tests) may be performed although any simulation output from the random graphs simulation would be expected to follow a normal distribution. The following code may help get a p-value or a confidence interval to boldly claim the sufficient evidence that the observed number of communities is extreme or unexpected per the random graph models:
t.test(num.comm.rg, mu=23)
One Sample t-test
data: num.comm.rg
t = -1040.2, df = 999, p-value < 2.2e-16
alternative hypothesis: true mean is not equal to 23
95 percent confidence interval:
3.898031 3.969969
sample estimates:
mean of x
3.934
t.test(num.comm.grg, mu=23)
One Sample t-test
data: num.comm.grg
t = -276.63, df = 999, p-value < 2.2e-16
alternative hypothesis: true mean is not equal to 23
95 percent confidence interval:
7.391054 7.610946
sample estimates:
mean of x
7.501
As you can see from the output, p-value < 0.0001 and the 95% CIs for both random graph models do not contain 23. We therefore have over 99% certitude that the observed number of communities is extreme per the random graph models.
Number of detected communities for the small-world models
# Barplot of the number of communities for the Random Models
rslts <- c(num.comm.ws,num.comm.ba)
indx <- c(rep(0, ntrials), rep(1, ntrials))
counts <- table(indx, rslts)/ntrials
barplot(counts, beside=TRUE, col=c("blue", "red"),
xlab="Number of Communities",
ylab="Relative Frequency",
legend=c("Watts-Strogatz SW", "Barbasi-Albert PA"))

Supprisingly enough, the observed number of communities is also extreme per both small-world models. This suggests that the obsrved graph may not be a small-world. But we cannot reach a definitive answer before looking at the average clustering and the average shortes-path length. \(\\\)
Summarizing the resulting distribution of clustering coefficient:
summary(av.clus.rg)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.05913 0.05955 0.05963 0.05963 0.05972 0.06000
summary(av.clus.grg)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.4318 0.4331 0.4334 0.4334 0.4337 0.4351
The two above summaries confirm that the clustering is significantly different from what would be seen had the observed graph been a random graph (t-test displays p-value < 0.0001)
summary(av.clus.ws)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.7463 0.7464 0.7464 0.7464 0.7465 0.7465
summary(av.clus.ba)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0 0 0 0 0 0
Recall the clustering coefficient for our network is 0.9645148
Supprisingly, there is substantially more clustering in our malaria co-authorship network than expected from both the random graph models and the two small-world models (t.test displays p-value < 0.0001).
Summarizing the resulting distribution of average path length:
summary(av.path.len.rg)
Min. 1st Qu. Median Mean 3rd Qu. Max.
1.942 1.942 1.942 1.942 1.942 1.942
summary(av.path.len.grg)
Min. 1st Qu. Median Mean 3rd Qu. Max.
2.257 2.259 2.260 2.260 2.260 2.263
summary(av.path.len.ws)
Min. 1st Qu. Median Mean 3rd Qu. Max.
3.090 3.566 3.779 3.822 4.038 4.698
summary(av.path.len.ba)
Min. 1st Qu. Median Mean 3rd Qu. Max.
7.794 8.843 9.168 9.207 9.551 11.150
Recall the clustering coefficient for our network is 2.9856151
With respect to the average shortest-path length, the observed shortest-path length is significantly larger than what is expected from the random graph models (t.test p-value < 0.0001) and significantly lower than what is expected from the small-world models (t.test p-value < 0.0001).
CONCLUSION: This analysis disproves the evidence for small-world behavior suspected in our previous tutorial. Our observed malaria co-authorship network has some behaviors that the mathematical graph models explored in this tutorial cannot explain. We therefore suggest the application of other models such as Statistical models for Network Graphs to better explain the observed malaria co-authorship network. This will be the focus of our next tutorial. \(\\\)
NOTICE: We repeat this analysis with the giant component of our co-authorship network and reach the same general conclusion. The reader might want to repeat this analysis on his own. \(\\\)
Let’s save the output from the Monte-Carlo simulations for probable future use:
save(results, file = './Rdata/results.rda')
\(\\\)
NEXT TUTORIAL: Statistical Modeling for Network Graphs
LS0tCnRpdGxlOiAiU29jaWFsIE5ldHdvcmsgQW5hbHlzaXMgb2YgdGhlIE1hbGFyaWEgQ28tYXV0aG9yc2hpcCBuZXR3b3JrIGZyb20gQmVuaW4iCm91dHB1dDogaHRtbF9ub3RlYm9vawphdXRob3I6ICJSb3NlcmljIEF6b25kZWtvbiIKLS0tCiRcXCQKJFxcJAoKVGhpcyBwcm9qZWN0IGlzIHBhcnQgb2YgbXkgUGhEIGRpc3NlcnRhdGlvbiBwcm9qZWN0LgoKVGhpcyBzZWN0aW9uIGZvbGxvd3MgZnJvbSB0aGUgZmlyc3Qgc2VjdGlvbiBkZWRpY2F0ZWQgdG8gdGhlIERlc2NyaXB0aXZlIEFuYWx5c2lzIG9mIG91ciBjby1hdXRob3JzaGlwIG5ldHdvcmsgKGF2YWlsYWJsZSBbSEVSRV0oaHR0cDovL3JwdWJzLmNvbS9yb3NlcmljYXpvbmRla29uL2Rlc2NzdGF0KSkuCgpUaGUgcHVycG9zZSBvZiB0aGlzIHNlY3Rpb24gaXMgdGhlIHVzZSBvZiBtYXRoZW1hdGljYWwgbW9kZWxzIHRvIGRlc2NyaWJlIG91ciBtYWxhcmlhIGNvLWF1dGhvcnNoaXAgbmV0d29yay4KCkFjY29yZGluZyB0byBLb2xhY3p5ayBhbmQgQ3NhcmRpICgyMDA5KSwgYSBtb2RlbCBmb3IgYSBuZXR3b3JrIGdyYXBoIGlzIGEgY29sbGVjdGlvbiBvZiBwb3NzaWJsZSBncmFwaHMgJFxtYXRoc2Nye0d9JCB3aXRoIGEgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uICRcbWF0aGJie1B9X1x0aGV0YSQgZGVmaW5lZCBhczoKJCRceyBcbWF0aGJie1B9X1x0aGV0YVwgKEcpLCBHIFxpbiBcbWF0aHNjcntHfSA6IFx0aGV0YSBcaW4gXFRoZXRhIFx9JCQKd2hlcmUgJFx0aGV0YSQgaXMgYSB2ZWN0b3Igb2YgcGFyYW1ldGVycyByYW5naW5nIG92ZXIgdmFsdWVzIGluICRcVGhldGEkLgoKVGhlIHVzZSBvZiBuZXR3b3JrIG1vZGVscyBpcyB0byB0ZXN0IHNpZ25pZmljYW5jZSBvZiB0aGUgY2hhcmFjdGVyaXN0aWNzIG9mIG91ciBjby1hdXRob3JzaGlwIG5ldHdvcmsgYW5kIHVucmF2ZWwgdGhlIG1lY2hhbmlzbXMgdW5kZXJseWluZyB0aGUgb2JzZXJ2ZWQgc3RydWN0dXJlIG9mIG91ciBtYWxhcmlhIGNvLWF1dGhvcnNoaXAgbmV0d29yay4gSXQgaXMgaW1wb3J0YW50IHRvIG5vdGUgdGhhdCBtYXRoZW1hdGljYWwgbW9kZWxzIHRlbmQgdG8gYmUgc2ltcGxlciB0aGFuIHN0YXRpc3RpY2FsIG1vZGVscyAod2hpY2ggd2lsbCBiZSBuZXh0KSBhbmQgZWFzaWVyIGZvciBtYXRoZW1hdGljYWwgYW5hbHlzaXMuIEJ1dCBpbiBjb250cmFzdCB0byBzdGF0aXN0aWNhbCBtb2RlbHMsIHRoZXkgZG8gbm90IHBlcm1pdCBtb2RlbCBmaXR0aW5nIGFuZCBhc3Nlc3NtZW50LgoKVGhleSBhcmUgZGlmZmVyZW50IG1hdGhlbWF0aWNhbCBtb2RlbHMgZm9yIG5ldHdvcmsgZ3JhcGhzOgoKLSAqKkNsYXNzaWNhbCBSYW5kb20gR3JhcGggTW9kZWxzKio6IEZpcnN0IGVzdGFibGlzaGVkIGJ5IEVyZG9zIGFuZCBSZW55aSAgKFtyZWZdKGh0dHA6Ly9jaXRlc2VlcnguaXN0LnBzdS5lZHUvdmlld2RvYy9kb3dubG9hZD9kb2k9MTAuMS4xLjM0OC41MzAmcmVwPXJlcDEmdHlwZT1wZGYpKSwgaXQgc3BlY2lmaWVzIGEgY29sbGVjdGlvbiBvZiBncmFwaHMgJFxtYXRoc2Nye0d9JCB3aXRoIGEgdW5pZm9ybSBwcm9iYWJpbGl0eSAkXG1hdGhiYntQfShcY2RvdCkkIG92ZXIgJFxtYXRoc2Nye0d9JC4gQSB2YXJpYW50IG9mIHRoaXMgbW9kZWwgY2FsbGVkIHRoZSAqQmVybm91bGxpIFJhbmRvbSBHcmFwaCBNb2RlbCogd2FzIGFsc28gZGVmaW5lZCBieSBHaWxiZXJ0IChbcmVmXShodHRwOi8vc29jcmVzb25saW5lLm9yZy51ay8yLzIvMy5odG1sKSkuCgotICoqR2VuZXJhbGl6ZWQgUmFuZG9tIEdyYXBoIE1vZGVscyoqOiBUaGVzZSBtb2RlbHMgZW1hbmF0ZWQgZnJvbSB0aGUgZ2VuZXJhbGl6YXRpb24gb2YgRXJkb3MgYW5kIFJlbnlpJ3MgZm9ybXVsYXRpb24sIGRlZmluaW5nIGEgY29sbGVjdGlvbiBvZiBncmFwaHMgJFxtYXRoc2Nye0d9JCB3aXRoIHByZXNwZWNpZmllZCBkZWdyZWUgc2VxdWVuY2UuCgotICoqTWVjaGFuaXN0aWMgTmV0d29yayBHcmFwaCBNb2RlbHMqKjogVGhlc2UgbW9kZWxzIG1pbWljIHJlYWwtd29ybGQgcGhlbm9tZW5hIGFuZCBpbmNsdWRlICoqU21hbGwtV29ybGQgTW9kZWxzKiogb2Z0ZW4gcmVmZXJyZWQgdG8gYXMgInNpeCBkZWdyZWUgc2VwYXJhdGlvbiIuIEl0IHdhcyBpbnRyb2R1Y2VkIGJ5IFdhdHRzIGFuZCBTdHJvZ2F0eiAoW3JlZl0oaHR0cDovL3d3dy5uYXR1cmUuY29tL25hdHVyZS9qb3VybmFsL3YzOTMvbjY2ODQvYWJzLzM5MzQ0MGEwLmh0bWwpKSBhbmQgaGF2ZSBzaW5jZSByZWNlaXZlZCBhIGxvdCBvZiBpbnRlcmVzdHMgaW4gdGhlIGV4aXN0aW5nIGxpdGVyYXR1cmUuIFNtYWxsLXdvcmxkIG5ldHdvcmtzIHVzdWFsbHkgZXhoaWJpdCBoaWdoIGxldmVscyBvZiBjbHVzdGVyaW5nIGFuZCBzbWFsbCBkaXN0YW5jZXMgYmV0d2VlbiBub2Rlcy4gQ2xhc3NpY2FsIG1vZGVscyBhcmUgbm90IGZpdCB0byBiZXR0ZXIgcmVwcmVzZW50IHN1Y2ggYmVoYXZpb3JzIHNpbmNlIHRoZXkgdXN1YWxseSBkaXNwbGF5IGxvdyBsZXZlbHMgb2YgY2x1c3RlcmluZyBhbmQgc21hbGwgZGlzdGFuY2UgYmV0d2VlbiBub2Rlcy4gRXhhbXBsZXMgb2Yga25vd24gc21hbGwtd29ybGQgbmV0d29ya3MgaW5jbHVkZSB0aGUgKm5ldHdvcmsgb2YgY29ubmVjdGVkIHByb3RlaW5zKiBvciB0aGUgKnRyYW5zY3JpcHRpb25hbCBuZXR3b3Jrcyogb2YgZ2VuZXMuIEEgdmFyaWFudCBvZiBTbWFsbC1Xb3JsZCBtb2RlbHMgaXMgdGhlICoqUHJlZmVyZW50aWFsIEF0dGFjaG1lbnQgTW9kZWxzKiogZGVmaW5lZCBiYXNlZCBvbiB0aGUgcG9wdWxhciBwcmluY2lwbGUgb2YgInRoZSByaWNoIGdldCByaWNoZXIiLiBQcmVmZXJlbnRpYWwgYXR0YWNobWVudCBtb2RlbHMgZ2FpbmVkIGZhc2NpbmF0aW9uIGFmdGVyIHRoZSB3b3JrIG9mIEJhcmFiYXNpIGFuZCBBbGJlcnQgIChbcmVmXShodHRwOi8vc2NpZW5jZS5zY2llbmNlbWFnLm9yZy9jb250ZW50LzI4Ni81NDM5LzUwOSkpIHdobyBzdHVkaWVkIHRoZSBncm93dGggb2YgdGhlIFdvcmxkIFdpZGUgV2ViLiBFeGFtcGxlcyBvZiBQcmVmZXJlbnRpYWwgQXR0YWNobWVudCBuZXR3b3JrcyBpbmNsdWRlIHRoYXQgb2YgV29ybGQgV2lkZSBXZWIgYW5kIHRoZSBzY2llbnRpZmljIGNpdGF0aW9uIG5ldHdvcmsuIEFuIGltcG9ydGFudCBjaGFyYWN0ZXJpc3RpYyBvZiB0aGVzZSBtb2RlbHMgaXMgdGhhdCBhcyB0aW1lIHRlbmQgdG8gaW5maW5pdHksIHRoZXJlIGRlZ3JlZSBkaXN0cmlidXRpb24gdGVuZCB0byBmb2xsb3cgYSBwb3dlciBsYXcuCgpOb3cgdGhhdCB3ZSBoYXZlIGRlZmluZWQgdGhlIG1haW4gbWF0aGVtYXRpY2FsIG1vZGVscyBmb3IgTmV0d29yayBtb2RlbGluZywgbGV0J3MgdXNlICpNb250ZSBDYXJsbyogYmFzZWQgc2ltdWxhdGlvbnMgbWV0aG9kcyB0byBhc3Nlc3MgdGhlIHNpZ25pZmljYW5jZSBvZiBvdXIgbmV0d29yayBjaGFyYWN0ZXJpc3RpY3MuCgoqKkRlZmluaXRpb246KiogR2l2ZW4gYW4gb2JzZXJ2ZWQgZ3JhcGggJEdee29ic30kIGFuZCBzb21lIHN0cnVjdHVyYWwgY2hhcmFjdGVyaXN0aWNzICRcZXRhIChcY2RvdCkkLiBPdXIgZ29hbCBpcyB0byBhc3Nlc3MgaWYgJFxldGEgKEdee29ic30pJCBpcyB1bnVzYWwgaS5lIGlzIHNpZ25pZmljYW50LiBXZSB0aGVuIGNvbXBhcmUgJFxldGEgKEdee29ic30pJCB0byBjb2xsZWN0aW9uIG9mIHZhbHVlcyAkXHtcZXRhKEcpOkcgXGluIFxtYXRoc2Nye0d9XH0kLiBJZiAkXGV0YSAoR157b2JzfSkkIGlzIHRvbyBleHRyZW1lIHdpdGggcmVzcGVjdCB0byB0aGlzIGNvbGxlY3Rpb24sIHRoZW4gd2UgaGF2ZSBlbm91Z2ggZXZpZGVuY2UgdG8gYXNzZXJ0IHRoYXQgJFxldGEgKEdee29ic30pJCBpcyBub3QgYSB1bmlmb3JtIGRyYXcgZnJvbSAkXG1hdGhzY3J7R30kLgoKTGV0J3MgcmVjYWxsIHRoYXQgb3VyIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIG1ldGhvZCBvZiBjb21tdW5pdHkgZGV0ZWN0aW9uIGFsZ29yaXRobSBoYXMgaWRlbnRpZmllZCAyMyBkaWZmZXJlbnQgY2x1c3RlcnMvY29tbXVuaXRpZXMgaW4gb3VyIGNvLWF1dGhvcnNoaXAgbmV0d29yayBvdXQgb2Ygd2hpY2ggNyBmb3JtIGEgZ2lhbnQgY29tcG9uZW50IChbcmVmLiBEZXNjcmlwdGl2ZSBBbmFseXNpc10oaHR0cDovL3JwdWJzLmNvbS9yb3NlcmljYXpvbmRla29uL2Rlc2NzdGF0KSkuIFRoZSBxdWVzdGlvbiBvZiBpbnRlcmVzdCBoZXJlIGlzIHdoZXRoZXIgdGhlIG51bWJlciBvZiBjb21tdW5pdGllcyBkZXRlY3RlZCBpcyB1bmV4cGVjdGVkIG9yIG5vdC4gTGV0J3MgdXNlIE1vbnRlIENhcmxvIHNpbXVsYXRpb25zIHRvIHRlc3QgdGhlIHNpZ25pZmljYW5jZSBvZiB0aGlzIG9ic2VydmVkIGNoYXJhY3RlcmlzdGljcyBvbiBvdXIgbWFsYXJpYSBjby1hdXRob3JzaGlwIG5ldHdvcmsuIExldCdzIGFsc28gdGVzdCB0aGUgc2lnbmlmaWNhbmNlIG9mIG90aGVyIGNoYXJhY3RlcmlzdGljcyBzdWNoIGFzIHRoZSBjbHVzdGVyaW5nIGNvZWZmaWNpZW50IGFuZCB0aGUgYXZlcmFnZSBzaG9ydGVzdCBwYXRoIGxlbmd0aC4KV2UgcnVuICoxMDAwKiBNb250ZS1DYXJsbyBzaW11bGF0aW9ucyBvbiBhbGwgdGhlIGFib3ZlIGRlc2NyaWJlZCBtb2RlbHMsIGFzc2VzcyBzaWduaWZpY2FuY2UgYW5kIHNtYWxsLXdvcmxkIHByb3BlcnRpZXMgYmFzZWQgb24gdGhlIHJlc3VsdHMuCgoqKkF0dGVudGlvbjoqKiBHaXZlbiB0aGUgc2l6ZSBvZiBvdXIgbmV0d29yaywgdGhlIHNpbXVsYXRpb25zIGFyZSBleHBlY3RlZCB0byBsYXN0IHNldmVyYWwgaG91cnMgdG8gY29tcGxldGUuIFdlIHRyYWNlIHRoZSBwcm9jZXNzaW5nIHRpbWUgb24gb3VyIGNvbXB1dGVyLCBhIDE2R0IgMTItY29yZSBDUFUgYW5kIDItY29yZSBHUFUgcG93ZXJlZCBkZXNrdG9wLiBXZSBkZWNpZGUgdG8gcGFyYWxsZWxpemUgdGhlIHByb2Nlc3NpbmcgaW4gb3JkZXIgdG8gc3BlZWQtdXAgdGhlIHByb2Nlc3NpbmcuCgpXZSBmaXJzdCBsb2FkIHRoZSBsaXN0IG9mIHRoZSBpZ3JhcGggb2JqZWN0cyB3ZSBzYXZlZCBmcm9tIG91ciBwcmV2aW91cyB0dXRvcmlhbDoKYGBge3IsZXJyb3I9RkFMU0Usd2FybmluZz1GQUxTRX0KbG9hZCgnLi9SZGF0YS9tbmV0LnJkYScpCmxvYWQoJy4vUmRhdGEvbW5ldDIucmRhJykKbG9hZCgnLi9SZGF0YS9tbmV0Mi5nYy5yZGEnKQpsb2FkKCcuL1JkYXRhL2F1dGhfZGF0YS5yZGEnKQpsb2FkKCcuL1JkYXRhL2VkZ2VzLnJkYScpCmBgYAokXFwkCgpMb2FkaW5nIHJlcXVpcmVkIHBhY2thZ2VzOgpgYGB7cixlcnJvcj1GQUxTRSx3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGlncmFwaCkKbGlicmFyeShmb3JlYWNoKQpsaWJyYXJ5KGRvUGFyYWxsZWwpCmxpYnJhcnkobHVicmlkYXRlKQpgYGAKJFxcJAoKTGV0J3MgZmlyc3Qgc2V0IHVwIHBhcmFsbGVsIGJhY2tlbmQKYGBge3J9CmNvcmVzPWRldGVjdENvcmVzKCkgIyBnZXQgbnVtYmVyIG9mIGNvcmVzPTEyCmNsIDwtIG1ha2VDbHVzdGVyKGNvcmVzWzFdLTIpICNJIGRlY2lkZSB0byBydW4gbGVhdmUgMiBjb3JlcyBvdXQKcmVnaXN0ZXJEb1BhcmFsbGVsKGNsKQpgYGAKCgpgYGB7cn0KbnYgPC0gdmNvdW50KG1uZXQyKSAjIG51bWJlciBvZiB2ZXJ0aWNlcyBpbiBtbmV0MgpuZSA8LSBlY291bnQobW5ldDIpICMgbnVtYmVyIG9mIGVkZ2VzIGluIG1uZXQyCmRlZ3MgPC0gZGVncmVlKG1uZXQyKSAjIGRlZ3JlZSBkaXN0cmlidXRpb24gaW4gbW5ldDIKIyBGb3IgdGhlIHNtYWxsLXdvcmxkIHByb3BlcnRpZXMgd2UgZXN0aW1hdGUgdGhlIHByb2JhYmlsaXR5IGFuZCB0aGUgYXZlcmFnZSBuZWlnaGJvcmhvb2Qgc2l6ZSBmcm9tIHRoZSBvYnNlcnZlZCBuZXR3b3JrCnAgPC0gbmUgLyAobmUgKiAobmUgLTEpKSAjIEVzdGltYXRlZCBwcm9iYWJpbGl0eQpyIDwtIG1lYW4obmVpZ2hib3Job29kLnNpemUobW5ldDIsIDEpKSAjIEVzdGltYXRlZCBhdmVyYWdlIG5laWdoYm9yaG9vZCBzaXplCgojIDEwMDAgdHJpYWxzCm50cmlhbHMgPC0gMTAwMAoKIyBEZWZpbmluZyBmdW5jdGlvbiB0byBjb21iaW5lIHJlc3VsdHMKY29tYiA8LSBmdW5jdGlvbih4LCAuLi4pIHsKICBsYXBwbHkoc2VxX2Fsb25nKHgpLAogICAgZnVuY3Rpb24oaSkgYyh4W1tpXV0sIGxhcHBseShsaXN0KC4uLiksIGZ1bmN0aW9uKHkpIHlbW2ldXSkpKQp9CgpzdGFydC50aW1lIDwtIFN5cy50aW1lKCkgIyBDYXB0dXJpbmcgUGFyYWxsZWwgQ29tcHV0aW5nIHByb2Nlc3Npbmcgc3RhcnQgdGltZQpyZXN1bHRzIDwtIGZvcmVhY2goMTpudHJpYWxzLCAuY29tYmluZT0nY29tYicsIC5tdWx0aWNvbWJpbmU9VFJVRSwKICAgICAgICAgICAgICAgICAgIC5pbml0PWxpc3QobGlzdCgpLGxpc3QoKSxsaXN0KCksbGlzdCgpLGxpc3QoKSxsaXN0KCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QoKSxsaXN0KCksbGlzdCgpLGxpc3QoKSxsaXN0KCksbGlzdCgpKSkgJWRvcGFyJSB7CiAgbGlicmFyeShpZ3JhcGgpCiAgIyBFcmRvcy1SZW55aSBSR00KICBnLnJnIDwtIGVyZG9zLnJlbnlpLmdhbWUobnYsIG5lLCB0eXBlPSJnbm0iKQogIGMucmcgPC0gZmFzdGdyZWVkeS5jb21tdW5pdHkoZy5yZykKICAjIG51bS5jb21tLnJnW2ldIDwtIGxlbmd0aChjLnJnKQogICMgYXYucGF0aC5sZW4ucmdbaV0gPC0gYXZlcmFnZS5wYXRoLmxlbmd0aChnLnJnLCBkaXJlY3RlZCA9IEYpCiAgIyBhdi5jbHVzLnJnW2ldIDwtIHRyYW5zaXRpdml0eShnLnJnKQogIG51bS5jb21tLnJnIDwtIGxlbmd0aChjLnJnKQogIGF2LnBhdGgubGVuLnJnIDwtIGF2ZXJhZ2UucGF0aC5sZW5ndGgoZy5yZywgZGlyZWN0ZWQgPSBGKQogIGF2LmNsdXMucmcgPC0gdHJhbnNpdGl2aXR5KGcucmcpCiAgCiAgIyBHZW5lcmFsaXplZCBSR00KICBnLmdyZyA8LSBkZWdyZWUuc2VxdWVuY2UuZ2FtZShkZWdzLCBtZXRob2Q9InZsIikKICBjLmdyZyA8LSBmYXN0Z3JlZWR5LmNvbW11bml0eShnLmdyZykKICBudW0uY29tbS5ncmcgPC0gbGVuZ3RoKGMuZ3JnKQogIGF2LnBhdGgubGVuLmdyZyA8LSBhdmVyYWdlLnBhdGgubGVuZ3RoKGcuZ3JnLCBkaXJlY3RlZCA9IEYpCiAgYXYuY2x1cy5ncmcgPC0gdHJhbnNpdGl2aXR5KGcuZ3JnKQogIAogICMgV2F0dHMgYW5kIFN0cm9nYXR6IHNtYWxsLXdvcmxkIE1vZGVsCiAgZy53cyA8LSB3YXR0cy5zdHJvZ2F0ei5nYW1lKDEsIG52LCByLCBwKQogIGMud3MgPC0gZmFzdGdyZWVkeS5jb21tdW5pdHkoZy53cykKICBudW0uY29tbS53cyA8LSBsZW5ndGgoYy53cykKICBhdi5wYXRoLmxlbi53cyA8LSBhdmVyYWdlLnBhdGgubGVuZ3RoKGcud3MsIGRpcmVjdGVkID0gRikKICBhdi5jbHVzLndzIDwtIHRyYW5zaXRpdml0eShnLndzKQogIAogICMgQmFyYWJhc2ktQWxiZXJ0IFByZWZlcmVudGlhbCBBdHRhY2hlbWVudCBNb2RlbAogIGcuYmEgPC0gYmFyYWJhc2kuZ2FtZShudiwgZGlyZWN0ZWQgPSBGQUxTRSkKICBjLmJhIDwtIGZhc3RncmVlZHkuY29tbXVuaXR5KGcuYmEpCiAgbnVtLmNvbW0uYmEgPC0gbGVuZ3RoKGMuYmEpCiAgYXYucGF0aC5sZW4uYmEgPC0gYXZlcmFnZS5wYXRoLmxlbmd0aChnLmJhLCBkaXJlY3RlZCA9IEYpCiAgYXYuY2x1cy5iYSA8LSB0cmFuc2l0aXZpdHkoZy5iYSkKICAKICBsaXN0KAogICAgbnVtLmNvbW0ucmcsIGF2LnBhdGgubGVuLnJnLCBhdi5jbHVzLnJnLAogICAgbnVtLmNvbW0uZ3JnLCBhdi5wYXRoLmxlbi5ncmcsIGF2LmNsdXMuZ3JnLAogICAgbnVtLmNvbW0ud3MsIGF2LnBhdGgubGVuLndzLCBhdi5jbHVzLndzLAogICAgbnVtLmNvbW0uYmEsIGF2LnBhdGgubGVuLmJhLCBhdi5jbHVzLmJhCiAgKQp9CnN0b3BDbHVzdGVyKGNsKSAjIFN0b3AgY2x1c3RlcgplbmQudGltZSA8LSBTeXMudGltZSgpICMgQ2FwdHVyaW5nIFBhcmFsbGVsIENvbXB1dGluZyBwcm9jZXNzaW5nIGVuZCB0aW1lCnRpbWUudGFrZW4gPC0gZW5kLnRpbWUgLSBzdGFydC50aW1lCnRpbWUudGFrZW4KYGBgCgoKTGV0J3Mgbm93IHJlYXNzaWduIGVhY2ggZWxlbWVudCBvZiB0aGUgcmVzdWx0cyB0byBhIHZhcmlhYmxlOgpgYGB7cn0KbnVtLmNvbW0ucmcgPC0gdW5saXN0KHJlc3VsdHNbWzFdXSk7CmF2LnBhdGgubGVuLnJnIDwtIHVubGlzdChyZXN1bHRzW1syXV0pCmF2LmNsdXMucmcgPC0gdW5saXN0KHJlc3VsdHNbWzNdXSkKbnVtLmNvbW0uZ3JnIDwtIHVubGlzdChyZXN1bHRzW1s0XV0pCmF2LnBhdGgubGVuLmdyZyA8LSB1bmxpc3QocmVzdWx0c1tbNV1dKQphdi5jbHVzLmdyZyA8LSB1bmxpc3QocmVzdWx0c1tbNl1dKQpudW0uY29tbS53cyA8LSB1bmxpc3QocmVzdWx0c1tbN11dKQphdi5wYXRoLmxlbi53cyA8LSB1bmxpc3QocmVzdWx0c1tbOF1dKQphdi5jbHVzLndzIDwtIHVubGlzdChyZXN1bHRzW1s5XV0pCm51bS5jb21tLmJhIDwtIHVubGlzdChyZXN1bHRzW1sxMF1dKQphdi5wYXRoLmxlbi5iYSA8LSB1bmxpc3QocmVzdWx0c1tbMTFdXSkKYXYuY2x1cy5iYSA8LSB1bmxpc3QocmVzdWx0c1tbMTJdXSkKYGBgCgpMZXQncyB2aXN1YWxpemUgdGhlIHJlc3VsdHM6CgpOdW1iZXIgb2YgZGV0ZWN0ZWQgY29tbXVuaXRpZXMgZm9yIHRoZSByYW5kb20gZ3JhcGggbW9kZWxzCmBgYHtyfQojIEJhcnBsb3Qgb2YgdGhlIG51bWJlciBvZiBjb21tdW5pdGllcyBmb3IgdGhlIFJhbmRvbSBNb2RlbHMKcnNsdHMgPC0gYyhudW0uY29tbS5yZyxudW0uY29tbS5ncmcpCmluZHggPC0gYyhyZXAoMCwgbnRyaWFscyksIHJlcCgxLCBudHJpYWxzKSkKY291bnRzIDwtIHRhYmxlKGluZHgsIHJzbHRzKS9udHJpYWxzCmJhcnBsb3QoY291bnRzLCBiZXNpZGU9VFJVRSwgY29sPWMoImJsdWUiLCAicmVkIiksCiAgICAgICAgeGxhYj0iTnVtYmVyIG9mIENvbW11bml0aWVzIiwgCiAgICAgICAgeWxhYj0iUmVsYXRpdmUgRnJlcXVlbmN5IiwKICAgICAgICBsZWdlbmQ9YygiRml4ZWQgU2l6ZSIsICJGaXhlZCBEZWdyZWUgU2VxdWVuY2UiKSkKYGBgCgpMZXQncyByZWNhbGwgdGhhdCBvdXIgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgYWxnb3JpdGhtIGRldGVjdGVkIDIzIGNvbW11bml0aWVzIGluIG91ciBjby1hdXRob3JzaGlwIG5ldHdvcmsuIFRoZSBncmFwaCBhYm92ZSBjbGVhcmx5IGRlbW9uc3RyYXRlcyB0aGF0IHRoZSBudW1iZXIgb2YgY29tbXVuaXRpZXMgZGV0ZWN0ZWQgd291bGQgYmUgY29uc2lkZXJlZCB1bnVzdWFsIGZyb20gdGhlIHBlcnNwZWN0aXZlIG9mIGJvdGggQ2xhc3NpY2FsIHJhbmRvbSBncmFwaHMgYW5kIGdlbmVyYWxpemVkIHJhbmRvbSBncmFwaHMuCgpUaGlzIGNhbiBiZSBmdXJ0aGVyIHF1YW50aWZpZWQgYnkgYSBvbmUtc2FtcGxlIHQtdGVzdCBjb21wYXJpbmcgdGhlIG9ic2VydmVkIG51bWJlciBvZiBjb21tdW5pdGllcyB0byB0aGUgbWVhbiBudW1iZXIgb2YgY29tbXVuaXRpZXMgZnJvbSB0aGUgTW9udGUtQ2FybG8gc2ltdWxhdGlvbnMuIEdpdmVuIHRoYXQgdC10ZXN0IGFzc3VtZXMgbm9ybWFsaXR5IG9mIHRoZSBkYXRhLCBhIHNpbXBsZSBoaXN0b2dyYW0gb3IgYSB0ZXN0IG9mIG5vcm1hbGl0eSAoUnlhbi1Kb2luZXIsIEtvbG1vZ29yb3YtU21pcm5vdiB0ZXN0cykgbWF5IGJlIHBlcmZvcm1lZCBhbHRob3VnaCBhbnkgc2ltdWxhdGlvbiBvdXRwdXQgZnJvbSB0aGUgcmFuZG9tIGdyYXBocyBzaW11bGF0aW9uIHdvdWxkIGJlIGV4cGVjdGVkIHRvIGZvbGxvdyBhIG5vcm1hbCBkaXN0cmlidXRpb24uIFRoZSBmb2xsb3dpbmcgY29kZSBtYXkgaGVscCBnZXQgYSBwLXZhbHVlIG9yIGEgY29uZmlkZW5jZSBpbnRlcnZhbCB0byBib2xkbHkgY2xhaW0gdGhlIHN1ZmZpY2llbnQgZXZpZGVuY2UgdGhhdCB0aGUgb2JzZXJ2ZWQgbnVtYmVyIG9mIGNvbW11bml0aWVzIGlzIGV4dHJlbWUgb3IgdW5leHBlY3RlZCBwZXIgdGhlIHJhbmRvbSBncmFwaCBtb2RlbHM6CgpgYGB7cn0KdC50ZXN0KG51bS5jb21tLnJnLCBtdT0yMykKdC50ZXN0KG51bS5jb21tLmdyZywgbXU9MjMpCmBgYApBcyB5b3UgY2FuIHNlZSBmcm9tIHRoZSBvdXRwdXQsIHAtdmFsdWUgPCAwLjAwMDEgYW5kIHRoZSA5NSUgQ0lzIGZvciBib3RoIHJhbmRvbSBncmFwaCBtb2RlbHMgZG8gbm90IGNvbnRhaW4gMjMuIFdlIHRoZXJlZm9yZSBoYXZlIG92ZXIgOTklIGNlcnRpdHVkZSB0aGF0IHRoZSBvYnNlcnZlZCBudW1iZXIgb2YgY29tbXVuaXRpZXMgaXMgZXh0cmVtZSBwZXIgdGhlIHJhbmRvbSBncmFwaCBtb2RlbHMuCgoKTnVtYmVyIG9mIGRldGVjdGVkIGNvbW11bml0aWVzIGZvciB0aGUgc21hbGwtd29ybGQgbW9kZWxzCmBgYHtyfQojIEJhcnBsb3Qgb2YgdGhlIG51bWJlciBvZiBjb21tdW5pdGllcyBmb3IgdGhlIFJhbmRvbSBNb2RlbHMKcnNsdHMgPC0gYyhudW0uY29tbS53cyxudW0uY29tbS5iYSkKaW5keCA8LSBjKHJlcCgwLCBudHJpYWxzKSwgcmVwKDEsIG50cmlhbHMpKQpjb3VudHMgPC0gdGFibGUoaW5keCwgcnNsdHMpL250cmlhbHMKYmFycGxvdChjb3VudHMsIGJlc2lkZT1UUlVFLCBjb2w9YygiYmx1ZSIsICJyZWQiKSwKICAgICAgICB4bGFiPSJOdW1iZXIgb2YgQ29tbXVuaXRpZXMiLCAKICAgICAgICB5bGFiPSJSZWxhdGl2ZSBGcmVxdWVuY3kiLAogICAgICAgIGxlZ2VuZD1jKCJXYXR0cy1TdHJvZ2F0eiBTVyIsICJCYXJiYXNpLUFsYmVydCBQQSIpKQpgYGAKClN1cHByaXNpbmdseSBlbm91Z2gsIHRoZSBvYnNlcnZlZCBudW1iZXIgb2YgY29tbXVuaXRpZXMgaXMgYWxzbyBleHRyZW1lIHBlciBib3RoIHNtYWxsLXdvcmxkIG1vZGVscy4gVGhpcyBzdWdnZXN0cyB0aGF0IHRoZSBvYnNydmVkIGdyYXBoIG1heSBub3QgYmUgYSBzbWFsbC13b3JsZC4gQnV0IHdlIGNhbm5vdCByZWFjaCBhIGRlZmluaXRpdmUgYW5zd2VyIGJlZm9yZSBsb29raW5nIGF0IHRoZSBhdmVyYWdlIGNsdXN0ZXJpbmcgYW5kIHRoZSBhdmVyYWdlIHNob3J0ZXMtcGF0aCBsZW5ndGguCiRcXCQKClN1bW1hcml6aW5nIHRoZSByZXN1bHRpbmcgZGlzdHJpYnV0aW9uIG9mIGNsdXN0ZXJpbmcgY29lZmZpY2llbnQ6CmBgYHtyfQpzdW1tYXJ5KGF2LmNsdXMucmcpCmBgYApgYGB7cn0Kc3VtbWFyeShhdi5jbHVzLmdyZykKYGBgClRoZSB0d28gYWJvdmUgc3VtbWFyaWVzIGNvbmZpcm0gdGhhdCB0aGUgY2x1c3RlcmluZyBpcyBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBmcm9tIHdoYXQgd291bGQgYmUgc2VlbiBoYWQgdGhlIG9ic2VydmVkIGdyYXBoIGJlZW4gYSByYW5kb20gZ3JhcGggKHQtdGVzdCBkaXNwbGF5cyBwLXZhbHVlIDwgMC4wMDAxKQoKYGBge3J9CnN1bW1hcnkoYXYuY2x1cy53cykKYGBgCmBgYHtyfQpzdW1tYXJ5KGF2LmNsdXMuYmEpCmBgYApSZWNhbGwgdGhlIGNsdXN0ZXJpbmcgY29lZmZpY2llbnQgZm9yIG91ciBuZXR3b3JrIGlzIGByIEkodHJhbnNpdGl2aXR5KG1uZXQyKSlgCgpTdXBwcmlzaW5nbHksIHRoZXJlIGlzIHN1YnN0YW50aWFsbHkgbW9yZSBjbHVzdGVyaW5nIGluIG91ciBtYWxhcmlhIGNvLWF1dGhvcnNoaXAgbmV0d29yayB0aGFuIGV4cGVjdGVkIGZyb20gYm90aCB0aGUgcmFuZG9tIGdyYXBoIG1vZGVscyBhbmQgdGhlIHR3byBzbWFsbC13b3JsZCBtb2RlbHMgKHQudGVzdCBkaXNwbGF5cyBwLXZhbHVlIDwgMC4wMDAxKS4KCgpTdW1tYXJpemluZyB0aGUgcmVzdWx0aW5nIGRpc3RyaWJ1dGlvbiBvZiBhdmVyYWdlIHBhdGggbGVuZ3RoOgpgYGB7cn0Kc3VtbWFyeShhdi5wYXRoLmxlbi5yZykKYGBgCmBgYHtyfQpzdW1tYXJ5KGF2LnBhdGgubGVuLmdyZykKYGBgCmBgYHtyfQpzdW1tYXJ5KGF2LnBhdGgubGVuLndzKQpgYGAKYGBge3J9CnN1bW1hcnkoYXYucGF0aC5sZW4uYmEpCmBgYApSZWNhbGwgdGhlIGNsdXN0ZXJpbmcgY29lZmZpY2llbnQgZm9yIG91ciBuZXR3b3JrIGlzIGByIEkoYXZlcmFnZS5wYXRoLmxlbmd0aChtbmV0MikpYAoKV2l0aCByZXNwZWN0IHRvIHRoZSBhdmVyYWdlIHNob3J0ZXN0LXBhdGggbGVuZ3RoLCB0aGUgb2JzZXJ2ZWQgc2hvcnRlc3QtcGF0aCBsZW5ndGggaXMgc2lnbmlmaWNhbnRseSBsYXJnZXIgdGhhbiB3aGF0IGlzIGV4cGVjdGVkIGZyb20gdGhlIHJhbmRvbSBncmFwaCBtb2RlbHMgKHQudGVzdCBwLXZhbHVlIDwgMC4wMDAxKSBhbmQgc2lnbmlmaWNhbnRseSBsb3dlciB0aGFuIHdoYXQgaXMgZXhwZWN0ZWQgZnJvbSB0aGUgc21hbGwtd29ybGQgbW9kZWxzICh0LnRlc3QgcC12YWx1ZSA8IDAuMDAwMSkuCgoqKkNPTkNMVVNJT046KiogVGhpcyBhbmFseXNpcyBkaXNwcm92ZXMgdGhlIGV2aWRlbmNlIGZvciBzbWFsbC13b3JsZCBiZWhhdmlvciBzdXNwZWN0ZWQgaW4gb3VyIHByZXZpb3VzIHR1dG9yaWFsLiBPdXIgb2JzZXJ2ZWQgbWFsYXJpYSBjby1hdXRob3JzaGlwIG5ldHdvcmsgaGFzIHNvbWUgYmVoYXZpb3JzIHRoYXQgdGhlIG1hdGhlbWF0aWNhbCBncmFwaCBtb2RlbHMgZXhwbG9yZWQgaW4gdGhpcyB0dXRvcmlhbCBjYW5ub3QgZXhwbGFpbi4gV2UgdGhlcmVmb3JlIHN1Z2dlc3QgdGhlIGFwcGxpY2F0aW9uIG9mIG90aGVyIG1vZGVscyBzdWNoIGFzIFN0YXRpc3RpY2FsIG1vZGVscyBmb3IgTmV0d29yayBHcmFwaHMgdG8gYmV0dGVyIGV4cGxhaW4gdGhlIG9ic2VydmVkIG1hbGFyaWEgY28tYXV0aG9yc2hpcCBuZXR3b3JrLiBUaGlzIHdpbGwgYmUgdGhlIGZvY3VzIG9mIG91ciBuZXh0IHR1dG9yaWFsLgokXFwkCgoKKipOT1RJQ0U6KiogV2UgcmVwZWF0IHRoaXMgYW5hbHlzaXMgd2l0aCB0aGUgZ2lhbnQgY29tcG9uZW50IG9mIG91ciBjby1hdXRob3JzaGlwIG5ldHdvcmsgYW5kIHJlYWNoIHRoZSBzYW1lIGdlbmVyYWwgY29uY2x1c2lvbi4gVGhlIHJlYWRlciBtaWdodCB3YW50IHRvIHJlcGVhdCB0aGlzIGFuYWx5c2lzIG9uIGhpcyBvd24uCiRcXCQKCkxldCdzIHNhdmUgdGhlIG91dHB1dCBmcm9tIHRoZSBNb250ZS1DYXJsbyBzaW11bGF0aW9ucyBmb3IgcHJvYmFibGUgZnV0dXJlIHVzZToKYGBge3J9CnNhdmUocmVzdWx0cywgZmlsZSA9ICcuL1JkYXRhL3Jlc3VsdHMucmRhJykKYGBgCiRcXCQKCgoKKipORVhUIFRVVE9SSUFMOioqIFN0YXRpc3RpY2FsIE1vZGVsaW5nIGZvciBOZXR3b3JrIEdyYXBocw==