\(\\\) \(\\\)

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:

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==