1 getting started

Start with clean workspace

rm(list = ls())

1.1 Goal

Our goal is to understand the very basics of RSiena.

For a thorough introduction in RSiena:

  • start with the manual
    • Chapters 2, 5, 12 (chapter 12 is the one I come back to most often)
  • Follow the Rscripts here or for .rmd style here
  • For an explanation how to interpret effects see here
  • Sign up for the mailing lists

See this workshop more as a teaser. I hope that after the workshop you become enthusiastic about the logic of RSiena and are able to understand the manual.

1.2 Custom functions

  • fpackage.check: Check if packages are installed (and install if not) in R (source).
  • fsave: Save to processed data in repository
  • fload: To load the files back after an fsave
  • fshowdf: To print objects (tibbles / data.frame) nicely on screen in .rmd
fpackage.check <- function(packages) {
    lapply(packages, FUN = function(x) {
        if (!require(x, character.only = TRUE)) {
            install.packages(x, dependencies = TRUE)
            library(x, character.only = TRUE)
        }
    })
}

fsave <- function(x, file = NULL, location = "./data/processed/") {
    ifelse(!dir.exists("data"), dir.create("data"), FALSE)
    ifelse(!dir.exists("data/processed"), dir.create("data/processed"), FALSE)
    if (is.null(file))
        file = deparse(substitute(x))
    datename <- substr(gsub("[:-]", "", Sys.time()), 1, 8)
    totalname <- paste(location, datename, file, ".rda", sep = "")
    save(x, file = totalname)  #need to fix if file is reloaded as input name, not as x. 
}

fload <- function(filename) {
    load(filename)
    get(ls()[ls() != "filename"])
}

fshowdf <- function(x, ...) {
    knitr::kable(x, digits = 2, "html", ...) %>%
        kableExtra::kable_styling(bootstrap_options = c("striped", "hover")) %>%
        kableExtra::scroll_box(width = "100%", height = "300px")
}

colorize <- function(x, color) {
    sprintf("<span style='color: %s;'>%s</span>", color, x)
}

1.3 packages

  • RSiena: what do you think?
  • RsienaTwoStep: this packages assesses the ministep assumption of RSiena but is useful for this tutorial
  • devtools: to load from github
  • igraph: plotting tools. For a tutorial on plotting networks, see our chapter in SNASS
packages = c("RSiena", "devtools", "igraph")
fpackage.check(packages)
# devtools::install_github('JochemTolsma/RsienaTwoStep', build_vignettes=TRUE)
packages = c("RsienaTwoStep")
fpackage.check(packages)

2 The logic of SAOMs

See (Ripley et al. 2022) paragraph 2.1

3 RSiena as ABM

RSiena models the evolution of network structures and/or the behavior of the social agents. It takes the current situation \(T_0\) as starting point. It estimates the ‘rules’ for the agents how to change their ties and/or behavior. If the model is specified correctly, these rules have led the situation at \(T_0\) to evolve into the situation observed at \(T_1\).

I would say these ‘rules’ are our micro theory of action.
Please note that our behavior may depend on the situation we are in. Similarly, the ‘rules’ we discover with RSiena are thus conditional on the situation at \(T_0\).

If we know the ‘rules’ of the social agents, we can also simulate future networks. And I think this aspect will help us to understand what the ‘rules’ of the social agents are and to understand what is estimated by RSiena.

3.1 RSiena’s ministep

Before we can start to simulate or understand RSiena we need to know what is meant by the ministep assumption.

Let us quote the manual

“The Stochastic Actor-Oriented Model can be regarded as an agent-based (‘actorbased’) simulation model of the network evolution; where all network changes are decomposed into very small steps, so-called ministeps, in which one actor creates or terminates one outgoing tie.”

“…it does not necessarily reflect a commitment to or belief in any particular theory of action elaborated in the scientific disciplines.”

“It is assumed that actors change their scores on the dependent variable (tie or behavior) such that they improve their total satisfaction […] with her/his local network neighborhood configuration.”

“Actors only evaluate all possible results in the local network neighborhood configurations that result from one ministep.”

The ministep is thus already an important ‘rule’ of how agents are allowed to behave. Does this mean the ministep is already theory, or just a modelling assumption!!??

Okay, but what does the SAOM of RSiena not do??:

  • No re-activity1: The act of re-affirming, making or breaking an outgoing tie does not trigger a response by the involved alter
  • No simultaneity: Changes occur one by one
  • Hence also no cooperation, coordination or negotiation
  • No maximization of total utility:
    • No altruistic behavior: Individual utility is maximized, not total utility
  • No strategic behavior:
    • Very finite time horizon. Agent does not predict how his/her future local network neighborhood may change after:
      • Making another ministep him/herself
      • A ministep of other agents
    • Hence also no investments

This does not mean that RSiena cannot estimate (or better: ‘fit’) the evolution of networks/behavior that are the consequences of these more complex ‘rules’ or micro theories but it assumes actors only make ministeps.

3.2 Simulation Logic

  1. Sample ego
  2. Construct possible alternative future networks based on all possible ministeps of ego
  3. Calculate how sampled ego evaluates these possible networks
  4. Let the ego pick a network, that is, let agent decide on a tie-change
  5. GOTO 1 (STOPPING RULE: until we have made enough ministeps)

3.2.1 Sample an ego

Let us first start with a network. We will use the build in network of RsienaTwoStep.

3.2.1.1 net1

Adjacency matrix

ts_net1
#>       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
#>  [1,]    0    0    0    0    0    0    0    0    0     0
#>  [2,]    0    0    1    0    0    0    0    0    0     0
#>  [3,]    1    0    0    0    0    1    0    0    0     0
#>  [4,]    0    0    0    0    0    0    0    0    1     0
#>  [5,]    0    0    0    0    0    0    0    0    0     0
#>  [6,]    0    0    0    0    0    0    0    0    1     1
#>  [7,]    1    0    0    0    0    0    0    0    0     0
#>  [8,]    0    0    0    0    0    0    0    0    0     1
#>  [9,]    0    0    0    1    0    0    0    1    0     0
#> [10,]    0    0    0    0    0    0    0    1    1     0

3.2.1.2 Plot ts_net1

net1g <- graph_from_adjacency_matrix(ts_net1, mode = "directed")
coords <- layout_(net1g, nicely())  #let us keep the layout
par(mar = c(0.1, 0.1, 0.1, 0.1))
{
    plot.igraph(net1g, layout = coords)
    graphics::box()
}
ts_net1

Figure 3.1: ts_net1

So only one actor is allowed to make one ministep. But who? This is determined by the so-called rate function and it may depend on ego-characteristics of our social agents (e.g. male/female) and/or on structural-characteristics of our social agents (e.g. indegree, outdegree). And all this can be estimated within RSiena. For this course in RSiena it would go way too far to explain in detail the rate function. It is also not that important. More often than note, we simply assume that all actors have an equal chance of being selected to make a ministep.

For more information on the rate function see here.

3.2.1.3 select agent

Okay, we can thus randomly select an agent.

set.seed(24553253)
ego <- ts_select(net = ts_net1)
ego
#> [1] 4

3.2.2 Possible networks after ministep

Let us suppose we want to know what the possible networks are after all possible ministeps of ego who is part of ts_net1. That is, let us assume that it is ego’s turn (ego#: 4) to decide on tie-change. What are the possible networks?

The function ts_alternatives_ministep() returns a list of all possible networks after all possible tie-changes available to an ego given the current state of a network.

options <- ts_alternatives_ministep(net = ts_net1, ego = ego)
options
plots <- lapply(options, graph_from_adjacency_matrix, mode = "directed")
par(mar = c(0, 0, 0, 0) + 0.1)
par(mfrow = c(2, 2))

fplot <- function(x) {
    plot.igraph(x, layout = coords, margin = 0)
    graphics::box()
}

lapply(plots, fplot)

3.2.3 Network statistics

Which option (or ministep) will ego choose? Naturally this will depend on which network characteristics (or so-called statistics) ego finds relevant. Let us suppose that ego bases its decision solely on the number of ties it sends to others and the number of reciprocated ties it has with others.

Let us count the number of ties ego sends to alters.

ts_degree(net = options[[1]], ego = ego)
#> [1] 2

or for all options

sapply(options, ts_degree, ego = ego)
#>  [1] 2 2 2 1 2 2 2 2 0 2

And let us count the number of reciprocated ties

sapply(options, ts_recip, ego = ego)
#>  [1] 1 1 1 1 1 1 1 1 0 1

Assigment 1:
1a: write a function that counts the number of transitive triads for our ego
1b: write a function that counts for all ego’s the transitive triads and sums the result

In the package RsienaTwoStep there are functions for the following network statistics \(s\):

  • degree: ts_degree()
  • reciprocity: ts_recip()
  • outdegree activity: ts_outAct()
  • indegree activity: ts_inAct()
  • outdegree popularity: ts_outPop()
  • indegree popularity: ts_inPop()
  • transitivity: ts_transTrip()
  • mediated transitivity: ts_transMedTrip()
  • transitive reciprocated triplets: ts_transRecTrip()
  • number of three-cycles: ts_cycle3()

Please see ?ts_degree().

Update: there are quite a lot more. But not very relevant for us at this time.

See Ripley et al. (2022) (Chapter 12) for the mathematical formulation. Naturally, you are free to define your own network statistics.

3.2.4 Evaluation function

But what evaluation value does ego attach to these network statistics and consequently to the network (in its vicinity) as a whole? Well these are the parameters, \(\beta\), you will normally estimate with RSiena::siena07(). Let us suppose the importance for:

  • the statistic ‘degree’ is -1
  • for the statistic ‘reciprocity’ is 1.5.

So you could calculate the evaluation of the network saved in options[[4]] by hand:

\[f^{eval}_{n}(\mathbf{s_{i}},\mathbf{\beta}) = \mathbf{s_i}^\mathsf{T}\mathbf{\beta}, \]
with \(f^{eval}_{n}\) the evaluation function for agent \(n\). \(\mathbf{s_{i}}\) are the network statistics for network \(i\) and \(\mathbf{\beta}\) the corresponding parameters (or importance).

option <- 4
ts_degree(options[[option]], ego = ego) * -1 + ts_recip(options[[option]], ego = ego) * 1.5
#> [1] 0.5

Or you could use the ts_eval().

eval <- ts_eval(net = options[[option]], ego = ego, statistics = list(ts_degree, ts_recip), parameters = c(-1,
    1.5))
eval
#> [1] 0.5

Now, let us calculate the evaluation of all possible networks:

eval <- sapply(options, FUN = ts_eval, ego = ego, statistics = list(ts_degree, ts_recip), parameters = c(-1,
    1.5))
eval
print("network with maximum evaluation score:")
which.max(eval)
#>  [1] -0.5 -0.5 -0.5  0.5 -0.5 -0.5 -0.5 -0.5  0.0 -0.5
#> [1] "network with maximum evaluation score:"
#> [1] 4

3.2.5 Choice function

So which option will ego choose? Naturally this will be a stochastic process. But we see that option 4 has the highest evaluation. We use McFadden’s choice function (for more information see wiki), that is let \(P_{ni}\) be the probability that ego \(n\) chooses network/alternative \(i\). The choice function is then given by:

\[P_{n,i} = \frac{exp(\mathbf{s_i}^\mathsf{T}\mathbf{\beta} )}{\Sigma_{j=1}^J exp(\mathbf{s_j}^\mathsf{T}\mathbf{\beta} )},\]

with \(s_i\) a vector of the value of each network statistics for network \(i\) and \(\beta\) is the vector of parameter values. Hence, \(\mathbf{s_i}^\mathsf{T}\mathbf{\beta}\) is the value of the evaluation for network \(i\).

Let us force ego to make a decision.

choice <- sample(1:length(eval), size = 1, prob = exp(eval)/sum(exp(eval)))
print("choice:")
choice
# print('network:') options[[choice]]
#> [1] "choice:"
#> [1] 10

If we repeat this process, that is…:

  1. sample agent
  2. construct possible alternative networks
  3. calculate how sampled agent evaluates the possible networks
  4. Let the agent pick a network, that is, let agent decide on a tie-change
  5. GO BACK TO 1 (STOPPING RULE: until you think we have made enough ministeps)

…we have an agent based model.

3.2.5.1 Interpretation of parameters

Although we have not yet estimated an RSiena model, this is already the time to explain the interpretation of the estimated parameters. Suppose that you want to interpret the estimated parameter for the degree statistic.

Have a look at the possible options our ego has. Can you find two networks that only differ with respect to the number of ties, and more precisely only with respect to 1 (sending) tie?

For example, Option 1, has one more sending tie than option 4.

What is the probability ratio?:

\[ \frac{P_{n,i=1}}{P_{n,i=4}},\]

You will see that the denominator cancels out. Thus:

\[ \frac{P_{n,i=1}}{P_{n,i=4}} = \frac{exp(\mathbf{s_{i=1}}^\mathsf{T}\mathbf{\beta} )}{exp(\mathbf{s_{i=4}}^\mathsf{T}\mathbf{\beta} )} = exp(\mathbf{s_{i=1}}^\mathsf{T}\mathbf{\beta} - \mathbf{s_{i=4}}^\mathsf{T}\mathbf{\beta}) = exp({\beta_{degree}}) ,\]
and thus:

\[ log(\frac{P_{n,i=1}}{P_{n,i=4}}) = {\beta} ,\] …the log probability ratio that an ego will choose a network with one additional sending tie over a network without an additional sending tie is precisely our statistic parameter \({\beta}\).

Hopefully you see the interpretation similarity with a standard (multinomial) logit model.

3.2.6 Stopping rule

But how many ministeps do we allow? Well, normally this is estimated by siena07 by the rate parameter.2 If we do not make this rate parameter conditional on actor covariates or on network characteristics, the rate parameter can be interpreted as the average number of ministeps each actor in the network is allowed to make before time is up. Let us suppose the rate parameter is 2 . Thus in total the number of possible ministeps will be nrow(net1)*rate: 20. For a more detailed - and more correct - interpretation of the rate parameter in siena07 see: www.jochemtolsma.nl.

3.3 Simulation example

Let us now simulate how the network could evolve given:3

  • starting point is ts_net1
  • rate is set to 2
  • we as scientists think only network statistics degree and reciprocity are important
  • RSiena::siena07 has determined the parameters for these statistics are -1 and 1.5 respectively
#>       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
#>  [1,]    0    0    0    0    0    0    0    0    0     0
#>  [2,]    0    0    1    0    0    0    0    0    0     0
#>  [3,]    1    0    0    0    0    1    0    0    0     0
#>  [4,]    0    0    0    0    0    0    0    0    1     0
#>  [5,]    0    0    0    0    0    0    0    0    0     0
#>  [6,]    0    0    0    0    0    0    0    0    1     1
#>  [7,]    1    0    0    0    0    0    0    0    0     0
#>  [8,]    0    0    0    0    0    0    0    0    0     1
#>  [9,]    0    0    0    1    0    0    0    1    0     0
#> [10,]    0    0    0    0    0    0    0    1    1     0
simnet2 <- ts_sim(
        net1=ts_net1, #this is our start netwwork
        statistics=list(ts_degree, ts_recip), #we only include to statistics
        startvalues = c(2,-1,1.5)) # the start values for our rate parameter (automatically included), and our two statistics

simnet2
#>       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
#>  [1,]    0    0    0    0    1    1    0    0    0     0
#>  [2,]    0    0    1    0    0    1    0    0    0     0
#>  [3,]    0    0    0    0    1    1    0    0    0     0
#>  [4,]    0    0    0    0    0    1    0    0    1     1
#>  [5,]    0    0    0    0    0    0    0    0    0     0
#>  [6,]    0    0    0    0    0    0    0    0    1     1
#>  [7,]    0    0    0    0    0    0    0    0    0     0
#>  [8,]    0    0    0    0    0    0    0    0    0     0
#>  [9,]    1    0    0    1    0    0    0    0    0     0
#> [10,]    0    0    0    0    0    0    0    1    0     0

Assignment 2:
2a: Use the same starting values and make 10 simulation runs (save results in a list or something). Hint, you may want to use ts_sims()
2b: tweak the starting values (a couple of times) and for each parameter configuration make 10 simulation runs (once again save your results) 2c: for each of the simulated networks calculate to total number of ties and the total number reciprocated ties
2d: What did you learn from this exercise?

4 Estimation logic

So now you have some grasp of the logic of RSiena. But how does the estimation work?

Well, that is quite complicated. But it goes something like this:4

  1. Define model: researcher includes effects
  2. initial parameter values of effects (commonly ‘0’)
  3. simulate an outcome network based on these parameter values
  4. compare the network statistics of the simulated outcome network with the observed outcome network (i.e. the target values)
    • based on the included effects. Thus the simulated network may contain 10 ties, but the observed network may contain 20 ties. Apparently, with the current parameter values we underestimate the density/degree of the outcome network and we may wish to increase the parameter of our density parameter.
  5. tweak/update parameter values in some smart way
  6. GOTO 3 (BREAK RULE: until parameter values cannot be improved anymore / or reached good fit)
  7. simulate a bunch of outcome networks with the obtained parameter values and compare the expected values of statistics of the outcome networks with the target values.
    • we can assess the fit
    • estimate SE of the parameters

Exercise 3:
Let us start playing with the build-in datasets of RSiena: s501 and s502.
Pick three statistics that you think will be relevant in estimating the network evolution for these datasets.
3a. For each of the above statistics, define the target values and program a function that calculates the target values.
3b. How would you pick smart starting values for the rate and degree parameters?
3c. Do 10 consecutive simulation runs. After each run you tweak your parameters?
Did you get close to your target values?

Some hints:

Calculating targets/outcome values:

# calculate the targets
ts_targets(net1 = s501, net2 = s502, statistics = list(ts_degree, ts_recip))
#>   Rate degree  recip 
#>    115    116     70
# if you set the argument `net2` to be equal to your simulated network, you count the values
# observed in your simulated network`

Defining your own statistics:

You can look at the source code to get inspiration:

ts_transTrip
#> function (net, ego) 
#> {
#>     statistic <- 0
#>     alters <- which(net[ego, ] == 1)
#>     if (length(alters) > 1) {
#>         for (alter1 in alters) {
#>             for (alter2 in alters) {
#>                 statistic <- statistic + net[alter1, alter2]
#>             }
#>         }
#>     }
#>     return(statistic)
#> }
#> <bytecode: 0x00000193e361d580>
#> <environment: namespace:RsienaTwoStep>
#> attr(,"name")
#> [1] "transTrip"

References

Ripley, Ruth M, Tom AB Snijders, Zsófia Boda, András Vörös, and Paulina Preciado. 2022. Manual for SIENA Version 4.0 (Version August 11, 2022). University of Oxford, Department of Statistics, Nuffield College. http://www.stats.ox.ac.uk/~snijders/siena/RSiena_manual.pdf.

  1. The no-reactivity assumption has been relaxed in the latest version of RSiena↩︎

  2. : Naturally, it is a bit more complicated than that. In RSiena we have a choice between unconditional and conditional estimation. My description of the stopping rule refers to the unconditional estimation.↩︎

  3. It is also possible to simulate networks within the RSiena package itself. But let us stick with functions from RsienaTwoStep for now.↩︎

  4. ‘it’ refers to estimation via the ‘Method of Moments’↩︎

LS0tICANCnRpdGxlOiAiTG9naWMgb2YgUlNpZW5hIg0KYmlibGlvZ3JhcGh5OiByZWZlcmVuY2VzLmJpYg0KbGluay1jaXRhdGlvbnM6IHllcw0KLS0tDQoNCmBgYHtyLCBnbG9iYWxzZXR0aW5ncywgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgcmVzdWx0cz0naGlkZSd9DQoNCmxpYnJhcnkoa25pdHIpDQpvcHRzX2NodW5rJHNldCh0aWR5Lm9wdHM9bGlzdCh3aWR0aC5jdXRvZmY9MTAwKSx0aWR5PVRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLGNvbW1lbnQgPSAiIz4iLCBjYWNoZT1UUlVFLCBjbGFzcy5zb3VyY2U9YygidGVzdCIpLCBjbGFzcy5vdXRwdXQ9YygidGVzdDIiKSwgY2FjaGUubGF6eSA9IEZBTFNFKQ0Kb3B0aW9ucyh3aWR0aCA9IDEwMCkgDQpyZ2w6OnNldHVwS25pdHIoKQ0KDQpgYGANCg0KDQoNCmBgYHtyIGtsaXBweSwgZWNobz1GQUxTRSwgaW5jbHVkZT1UUlVFLCBtZXNzYWdlPUZBTFNFfQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJyZW1vdGVzIikNCiNyZW1vdGVzOjppbnN0YWxsX2dpdGh1Yigicmxlc3VyL2tsaXBweSIpDQprbGlwcHk6OmtsaXBweShwb3NpdGlvbiA9IGMoJ3RvcCcsICdyaWdodCcpKQ0KI2tsaXBweTo6a2xpcHB5KGNvbG9yID0gJ2RhcmtyZWQnKQ0KI2tsaXBweTo6a2xpcHB5KHRvb2x0aXBfbWVzc2FnZSA9ICdDbGljayB0byBjb3B5JywgdG9vbHRpcF9zdWNjZXNzID0gJ0RvbmUnKQ0KYGBgDQoNCg0KLS0tLQ0KDQojIGdldHRpbmcgc3RhcnRlZA0KDQpTdGFydCB3aXRoIGNsZWFuIHdvcmtzcGFjZSANCg0KYGBge3J9DQpybShsaXN0PWxzKCkpDQpgYGANCg0KLS0tLQ0KDQojIyBHb2FsDQoNCk91ciBnb2FsIGlzIHRvIHVuZGVyc3RhbmQgdGhlIHZlcnkgYmFzaWNzIG9mIFJTaWVuYS4gIA0KDQpGb3IgYSB0aG9yb3VnaCBpbnRyb2R1Y3Rpb24gaW4gUlNpZW5hOiAgDQoNCi0gc3RhcnQgd2l0aCB0aGUgW21hbnVhbF0oaHR0cHM6Ly93d3cuc3RhdHMub3guYWMudWsvfnNuaWpkZXJzL3NpZW5hL1JTaWVuYV9NYW51YWwucGRmKSAgDQogICAgLSBDaGFwdGVycyAyLCA1LCAxMiAoY2hhcHRlciAxMiBpcyB0aGUgb25lIEkgY29tZSBiYWNrIHRvIG1vc3Qgb2Z0ZW4pDQotIEZvbGxvdyB0aGUgUnNjcmlwdHMgW2hlcmVdKGh0dHBzOi8vd3d3LnN0YXRzLm94LmFjLnVrL35zbmlqZGVycy9zaWVuYS8pIG9yIGZvciAucm1kIHN0eWxlIFtoZXJlXShodHRwczovL2pvY2hlbXRvbHNtYS5naXRodWIuaW8vUlNpZW5hLXNjcmlwdHMvKSAgDQotIEZvciBhbiBleHBsYW5hdGlvbiBob3cgdG8gaW50ZXJwcmV0IGVmZmVjdHMgc2VlIFtoZXJlXShodHRwczovL3d3dy5qb2NoZW10b2xzbWEubmwvY291cnNlcy9jb21wbGV0ZS1uZXR3b3Jrcy9zb2NpbzYvKQ0KLSBTaWduIHVwIGZvciB0aGUgbWFpbGluZyBsaXN0cyAgDQogDQoNClNlZSB0aGlzIHdvcmtzaG9wIG1vcmUgYXMgYSB0ZWFzZXIuIEkgaG9wZSB0aGF0IGFmdGVyIHRoZSB3b3Jrc2hvcCB5b3UgYmVjb21lIGVudGh1c2lhc3RpYyBhYm91dCB0aGUgbG9naWMgb2YgUlNpZW5hIGFuZCBhcmUgYWJsZSB0byB1bmRlcnN0YW5kIHRoZSBtYW51YWwuIA0KDQoNCiMjIEN1c3RvbSBmdW5jdGlvbnMNCg0KLSBgZnBhY2thZ2UuY2hlY2tgOiBDaGVjayBpZiBwYWNrYWdlcyBhcmUgaW5zdGFsbGVkIChhbmQgaW5zdGFsbCBpZiBub3QpIGluIFIgKFtzb3VyY2VdKGh0dHBzOi8vdmJhbGlnYS5naXRodWIuaW8vdmVyaWZ5LXRoYXQtci1wYWNrYWdlcy1hcmUtaW5zdGFsbGVkLWFuZC1sb2FkZWQvKSkuICANCi0gYGZzYXZlYDogU2F2ZSB0byBwcm9jZXNzZWQgZGF0YSBpbiByZXBvc2l0b3J5ICANCi0gYGZsb2FkYDogVG8gbG9hZCB0aGUgZmlsZXMgYmFjayBhZnRlciBhbiBgZnNhdmVgICANCi0gYGZzaG93ZGZgOiBUbyBwcmludCBvYmplY3RzICh0aWJibGVzIC8gZGF0YS5mcmFtZSkgbmljZWx5IG9uIHNjcmVlbiBpbiAucm1kICANCg0KYGBge3IgY3VzdG9tZnVuY3Rpb25zLCByZXN1bHRzPSdoaWRlJ30NCmZwYWNrYWdlLmNoZWNrIDwtIGZ1bmN0aW9uKHBhY2thZ2VzKSB7DQogIGxhcHBseShwYWNrYWdlcywgRlVOID0gZnVuY3Rpb24oeCkgew0KICAgIGlmICghcmVxdWlyZSh4LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKSB7DQogICAgICBpbnN0YWxsLnBhY2thZ2VzKHgsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogICAgICBsaWJyYXJ5KHgsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkNCiAgICB9DQogIH0pDQp9DQoNCmZzYXZlIDwtIGZ1bmN0aW9uKHgsIGZpbGU9TlVMTCwgbG9jYXRpb249Ii4vZGF0YS9wcm9jZXNzZWQvIikgew0KICBpZmVsc2UoIWRpci5leGlzdHMoImRhdGEiKSwgZGlyLmNyZWF0ZSgiZGF0YSIpLCBGQUxTRSkNCiAgaWZlbHNlKCFkaXIuZXhpc3RzKCJkYXRhL3Byb2Nlc3NlZCIpLCBkaXIuY3JlYXRlKCJkYXRhL3Byb2Nlc3NlZCIpLCBGQUxTRSkNCiAgaWYgKGlzLm51bGwoZmlsZSkpIGZpbGU9IGRlcGFyc2Uoc3Vic3RpdHV0ZSh4KSkNCiAgZGF0ZW5hbWUgPC0gc3Vic3RyKGdzdWIoIls6LV0iLCAiIiwgU3lzLnRpbWUoKSksIDEsOCkgIA0KICB0b3RhbG5hbWUgPC0gcGFzdGUobG9jYXRpb24sIGRhdGVuYW1lLCBmaWxlLCAiLnJkYSIsIHNlcD0iIikNCiAgc2F2ZSh4LCBmaWxlID0gdG90YWxuYW1lKSAgI25lZWQgdG8gZml4IGlmIGZpbGUgaXMgcmVsb2FkZWQgYXMgaW5wdXQgbmFtZSwgbm90IGFzIHguIA0KfQ0KDQpmbG9hZCA8LSBmdW5jdGlvbihmaWxlbmFtZSkgew0KICBsb2FkKGZpbGVuYW1lKQ0KICBnZXQobHMoKVtscygpICE9ICJmaWxlbmFtZSJdKQ0KfQ0KDQpmc2hvd2RmIDwtICBmdW5jdGlvbih4LCAuLi4pIHsNCiAga25pdHI6OmthYmxlKHgsIGRpZ2l0cz0yLCAiaHRtbCIsIC4uLikgJT4lDQogIGthYmxlRXh0cmE6OmthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpICU+JQ0KICBrYWJsZUV4dHJhOjpzY3JvbGxfYm94KHdpZHRoPSIxMDAlIiwgaGVpZ2h0PSAiMzAwcHgiKQ0KfSANCg0KY29sb3JpemUgPC0gZnVuY3Rpb24oeCwgY29sb3IpIHtzcHJpbnRmKCI8c3BhbiBzdHlsZT0nY29sb3I6ICVzOyc+JXM8L3NwYW4+IiwgY29sb3IsIHgpIH0NCg0KYGBgDQojIyBwYWNrYWdlcw0KDQotIGBSU2llbmFgOiB3aGF0IGRvIHlvdSB0aGluaz8NCi0gYFJzaWVuYVR3b1N0ZXBgOiB0aGlzIHBhY2thZ2VzIGFzc2Vzc2VzIHRoZSBtaW5pc3RlcCBhc3N1bXB0aW9uIG9mIFJTaWVuYSBidXQgaXMgdXNlZnVsIGZvciB0aGlzIHR1dG9yaWFsICANCi0gYGRldnRvb2xzYDogdG8gbG9hZCBmcm9tIGdpdGh1YiAgDQotIGBpZ3JhcGhgOiBwbG90dGluZyB0b29scy4gRm9yIGEgdHV0b3JpYWwgb24gcGxvdHRpbmcgbmV0d29ya3MsIHNlZSBbb3VyIGNoYXB0ZXIgaW4gU05BU1NdKGh0dHBzOi8vc25hc3MubmV0bGlmeS5hcHAvbmV0d29yay12aXN1YWxpemF0aW9uLmh0bWwpIA0KDQoNCmBgYHtyLCByZXN1bHRzPSdoaWRlJ30NCnBhY2thZ2VzID0gYygiUlNpZW5hIiwgImRldnRvb2xzIiwgImlncmFwaCIpDQpmcGFja2FnZS5jaGVjayhwYWNrYWdlcykNCiNkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoIkpvY2hlbVRvbHNtYS9Sc2llbmFUd29TdGVwIiwgYnVpbGRfdmlnbmV0dGVzPVRSVUUpDQpwYWNrYWdlcyA9IGMoIlJzaWVuYVR3b1N0ZXAiKQ0KZnBhY2thZ2UuY2hlY2socGFja2FnZXMpDQpgYGANCg0KLS0tICANCg0KIyBUaGUgbG9naWMgb2YgU0FPTXMNCg0KU2VlIFtAcmlwbGV5MjAyMm1hbnVhbF0gcGFyYWdyYXBoIDIuMQ0KDQojIFJTaWVuYSBhcyBBQk0gDQoNClJTaWVuYSBtb2RlbHMgKnRoZSBldm9sdXRpb24qIG9mIG5ldHdvcmsgc3RydWN0dXJlcyBhbmQvb3IgdGhlIGJlaGF2aW9yIG9mIHRoZSBzb2NpYWwgYWdlbnRzLiANCkl0IHRha2VzIHRoZSBjdXJyZW50IHNpdHVhdGlvbiAkVF8wJCBhcyBzdGFydGluZyBwb2ludC4gSXQgZXN0aW1hdGVzIHRoZSAncnVsZXMnIGZvciB0aGUgYWdlbnRzIGhvdyB0byBjaGFuZ2UgdGhlaXIgdGllcyBhbmQvb3IgYmVoYXZpb3IuIElmIHRoZSBtb2RlbCBpcyBzcGVjaWZpZWQgY29ycmVjdGx5LCB0aGVzZSBydWxlcyBoYXZlIGxlZCB0aGUgc2l0dWF0aW9uIGF0ICRUXzAkIHRvIGV2b2x2ZSBpbnRvIHRoZSBzaXR1YXRpb24gb2JzZXJ2ZWQgYXQgJFRfMSQuIA0KDQpJIHdvdWxkIHNheSB0aGVzZSAncnVsZXMnIGFyZSBvdXIgKioqbWljcm8gdGhlb3J5IG9mIGFjdGlvbioqKi4gIA0KUGxlYXNlIG5vdGUgdGhhdCBvdXIgYmVoYXZpb3IgbWF5IGRlcGVuZCBvbiB0aGUgc2l0dWF0aW9uIHdlIGFyZSBpbi4gU2ltaWxhcmx5LCB0aGUgJ3J1bGVzJyB3ZSBkaXNjb3ZlciB3aXRoIFJTaWVuYSBhcmUgdGh1cyBjb25kaXRpb25hbCBvbiB0aGUgc2l0dWF0aW9uIGF0ICRUXzAkLiANCg0KSWYgd2Uga25vdyB0aGUgJ3J1bGVzJyBvZiB0aGUgc29jaWFsIGFnZW50cywgd2UgY2FuIGFsc28gc2ltdWxhdGUgZnV0dXJlIG5ldHdvcmtzLiBBbmQgSSB0aGluayB0aGlzIGFzcGVjdCB3aWxsIGhlbHAgdXMgdG8gdW5kZXJzdGFuZCB3aGF0IHRoZSAncnVsZXMnIG9mIHRoZSBzb2NpYWwgYWdlbnRzIGFyZSBhbmQgdG8gdW5kZXJzdGFuZCB3aGF0IGlzIGVzdGltYXRlZCBieSBgUlNpZW5hYC4gDQoNCiMjIFJTaWVuYSdzIG1pbmlzdGVwDQoNCkJlZm9yZSB3ZSBjYW4gc3RhcnQgdG8gc2ltdWxhdGUgb3IgdW5kZXJzdGFuZCBSU2llbmEgd2UgbmVlZCB0byBrbm93IHdoYXQgaXMgbWVhbnQgYnkgdGhlIG1pbmlzdGVwIGFzc3VtcHRpb24uIA0KDQpMZXQgdXMgcXVvdGUgdGhlIG1hbnVhbCANCg0KPiDigJxUaGUgU3RvY2hhc3RpYyBBY3Rvci1PcmllbnRlZCBNb2RlbCBjYW4gYmUgcmVnYXJkZWQgYXMgYW4gYWdlbnQtYmFzZWQgKOKAmGFjdG9yYmFzZWTigJkpIHNpbXVsYXRpb24gbW9kZWwgb2YgdGhlIG5ldHdvcmsgZXZvbHV0aW9uOyB3aGVyZSBhbGwgbmV0d29yayBjaGFuZ2VzIGFyZSBkZWNvbXBvc2VkIGludG8gdmVyeSBzbWFsbCBzdGVwcywgc28tY2FsbGVkIG1pbmlzdGVwcywgaW4gd2hpY2ggb25lIGFjdG9yIGNyZWF0ZXMgb3IgdGVybWluYXRlcyBvbmUgb3V0Z29pbmcgdGllLuKAnSANCj4NCj7igJzigKZpdCBkb2VzIG5vdCBuZWNlc3NhcmlseSByZWZsZWN0IGEgY29tbWl0bWVudCB0byBvciBiZWxpZWYgaW4gYW55IHBhcnRpY3VsYXIgdGhlb3J5IG9mIGFjdGlvbiBlbGFib3JhdGVkIGluIHRoZSBzY2llbnRpZmljIGRpc2NpcGxpbmVzLuKAnQ0KPg0KPuKAnEl0IGlzIGFzc3VtZWQgdGhhdCBhY3RvcnMgY2hhbmdlIHRoZWlyIHNjb3JlcyBvbiB0aGUgZGVwZW5kZW50IHZhcmlhYmxlICh0aWUgb3IgYmVoYXZpb3IpIHN1Y2ggdGhhdCB0aGV5IGltcHJvdmUgdGhlaXIgdG90YWwgc2F0aXNmYWN0aW9uIFvigKZdIHdpdGggaGVyL2hpcyBsb2NhbCBuZXR3b3JrIG5laWdoYm9yaG9vZCBjb25maWd1cmF0aW9uLuKAnQ0KPg0KPuKAnEFjdG9ycyBvbmx5IGV2YWx1YXRlIGFsbCBwb3NzaWJsZSByZXN1bHRzIGluIHRoZSBsb2NhbCBuZXR3b3JrIG5laWdoYm9yaG9vZCBjb25maWd1cmF0aW9ucyB0aGF0IHJlc3VsdCBmcm9tIG9uZSBtaW5pc3RlcC7igJ0gIA0KDQpUaGUgKm1pbmlzdGVwKiBpcyB0aHVzIGFscmVhZHkgYW4gaW1wb3J0YW50ICdydWxlJyBvZiBob3cgYWdlbnRzIGFyZSBhbGxvd2VkIHRvIGJlaGF2ZS4gRG9lcyB0aGlzIG1lYW4gdGhlIG1pbmlzdGVwIGlzIGFscmVhZHkgdGhlb3J5LCBvciBqdXN0IGEgbW9kZWxsaW5nIGFzc3VtcHRpb24hIT8/DQoNCk9rYXksIGJ1dCB3aGF0IGRvZXMgdGhlIFNBT00gb2YgUlNpZW5hIG5vdCBkbz8/OiAgDQoNCi0gTm8gcmUtYWN0aXZpdHleW2ByIGNvbG9yaXplKCJUaGUgbm8tcmVhY3Rpdml0eSBhc3N1bXB0aW9uIGhhcyBiZWVuIHJlbGF4ZWQgaW4gdGhlIGxhdGVzdCB2ZXJzaW9uIG9mIFJTaWVuYSIsICJyZWQiKWBdOiBUaGUgYWN0IG9mIHJlLWFmZmlybWluZywgbWFraW5nIG9yIGJyZWFraW5nIGFuIG91dGdvaW5nIHRpZSBkb2VzIG5vdCB0cmlnZ2VyIGEgcmVzcG9uc2UgYnkgdGhlIGludm9sdmVkIGFsdGVyICANCi0gTm8gc2ltdWx0YW5laXR5OiBDaGFuZ2VzIG9jY3VyIG9uZSBieSBvbmUgIA0KLSBIZW5jZSBhbHNvIG5vIGNvb3BlcmF0aW9uLCBjb29yZGluYXRpb24gb3IgbmVnb3RpYXRpb24gIA0KLSBObyBtYXhpbWl6YXRpb24gb2YgdG90YWwgdXRpbGl0eTogIA0KICAgIC0gTm8gYWx0cnVpc3RpYyBiZWhhdmlvcjogSW5kaXZpZHVhbCB1dGlsaXR5IGlzIG1heGltaXplZCwgbm90IHRvdGFsIHV0aWxpdHkgIA0KLSBObyBzdHJhdGVnaWMgYmVoYXZpb3I6ICANCiAgICAtIFZlcnkgZmluaXRlIHRpbWUgaG9yaXpvbi4gQWdlbnQgZG9lcyBub3QgcHJlZGljdCBob3cgaGlzL2hlciBmdXR1cmUgbG9jYWwgbmV0d29yayBuZWlnaGJvcmhvb2QgbWF5IGNoYW5nZSBhZnRlcjogIA0KICAgICAgICAtIE1ha2luZyBhbm90aGVyIG1pbmlzdGVwIGhpbS9oZXJzZWxmICANCiAgICAgICAgLSBBIG1pbmlzdGVwIG9mIG90aGVyIGFnZW50cw0KICAgIC0gSGVuY2UgYWxzbyBubyBpbnZlc3RtZW50cw0KDQpUaGlzIGRvZXMgbm90IG1lYW4gdGhhdCBSU2llbmEgY2Fubm90IGVzdGltYXRlIChvciBiZXR0ZXI6ICdmaXQnKSB0aGUgZXZvbHV0aW9uIG9mIG5ldHdvcmtzL2JlaGF2aW9yIHRoYXQgYXJlIHRoZSBjb25zZXF1ZW5jZXMgb2YgdGhlc2UgbW9yZSBjb21wbGV4ICdydWxlcycgb3IgbWljcm8gdGhlb3JpZXMgYnV0IGl0IGFzc3VtZXMgYWN0b3JzIG9ubHkgbWFrZSBtaW5pc3RlcHMuICANCg0KDQoNCiMjIFNpbXVsYXRpb24gTG9naWMNCg0KMS4gU2FtcGxlIGVnbyAgDQoyLiBDb25zdHJ1Y3QgcG9zc2libGUgYWx0ZXJuYXRpdmUgZnV0dXJlIG5ldHdvcmtzIGJhc2VkIG9uIGFsbCBwb3NzaWJsZSBtaW5pc3RlcHMgb2YgZWdvICAgIA0KMy4gQ2FsY3VsYXRlIGhvdyBzYW1wbGVkIGVnbyBldmFsdWF0ZXMgdGhlc2UgcG9zc2libGUgbmV0d29ya3MgIA0KNC4gTGV0IHRoZSBlZ28gcGljayBhIG5ldHdvcmssIHRoYXQgaXMsIGxldCBhZ2VudCBkZWNpZGUgb24gYSB0aWUtY2hhbmdlICANCjUuIEdPVE8gMSAoU1RPUFBJTkcgUlVMRTogdW50aWwgd2UgaGF2ZSBtYWRlIGVub3VnaCBtaW5pc3RlcHMpDQoNCiMjIyBTYW1wbGUgYW4gZWdvICANCg0KTGV0IHVzIGZpcnN0IHN0YXJ0IHdpdGggYSBuZXR3b3JrLiBXZSB3aWxsIHVzZSB0aGUgYnVpbGQgaW4gbmV0d29yayBvZiBgUnNpZW5hVHdvU3RlcGAuIA0KDQojIyMjIG5ldDENCg0KQWRqYWNlbmN5IG1hdHJpeA0KDQpgYGB7ciwgZWNobz1GQUxTRSwgZXZhbD1UUlVFfQ0KdHNfbmV0MSA8LSB0c19uZXQxDQpgYGANCg0KDQpgYGB7ciB9DQp0c19uZXQxDQpgYGANCg0KIyMjIyBQbG90IHRzX25ldDENCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTUsIGZpZy5jYXA9InRzX25ldDEifQ0KbmV0MWcgPC0gZ3JhcGhfZnJvbV9hZGphY2VuY3lfbWF0cml4KHRzX25ldDEsIG1vZGU9ImRpcmVjdGVkIikNCmNvb3JkcyA8LSBsYXlvdXRfKG5ldDFnLCBuaWNlbHkoKSkgI2xldCB1cyBrZWVwIHRoZSBsYXlvdXQNCnBhcihtYXI9YyguMSwgLjEsIC4xLCAuMSkpDQp7cGxvdC5pZ3JhcGgobmV0MWcsIGxheW91dD1jb29yZHMpDQpncmFwaGljczo6Ym94KCkNCn0NCmBgYA0KDQoNClNvIG9ubHkgb25lIGFjdG9yIGlzIGFsbG93ZWQgdG8gbWFrZSBvbmUgbWluaXN0ZXAuIEJ1dCB3aG8/IFRoaXMgaXMgZGV0ZXJtaW5lZCBieSB0aGUgc28tY2FsbGVkICoqcmF0ZSBmdW5jdGlvbioqIGFuZCBpdCBtYXkgZGVwZW5kIG9uIGVnby1jaGFyYWN0ZXJpc3RpY3Mgb2Ygb3VyIHNvY2lhbCBhZ2VudHMgKGUuZy4gbWFsZS9mZW1hbGUpIGFuZC9vciBvbiBzdHJ1Y3R1cmFsLWNoYXJhY3RlcmlzdGljcyBvZiBvdXIgc29jaWFsIGFnZW50cyAoZS5nLiBpbmRlZ3JlZSwgb3V0ZGVncmVlKS4gQW5kIGFsbCB0aGlzIGNhbiBiZSBlc3RpbWF0ZWQgd2l0aGluIFJTaWVuYS4gRm9yIHRoaXMgY291cnNlIGluIFJTaWVuYSBpdCB3b3VsZCBnbyB3YXkgdG9vIGZhciB0byBleHBsYWluIGluIGRldGFpbCB0aGUgcmF0ZSBmdW5jdGlvbi4gSXQgaXMgYWxzbyBub3QgdGhhdCBpbXBvcnRhbnQuIE1vcmUgb2Z0ZW4gdGhhbiBub3RlLCB3ZSBzaW1wbHkgYXNzdW1lIHRoYXQgYWxsIGFjdG9ycyBoYXZlIGFuIGVxdWFsIGNoYW5jZSBvZiBiZWluZyBzZWxlY3RlZCB0byBtYWtlIGEgbWluaXN0ZXAuICANCg0KRm9yIG1vcmUgaW5mb3JtYXRpb24gb24gdGhlIHJhdGUgZnVuY3Rpb24gc2VlIFtoZXJlXShodHRwczovL3d3dy5qb2NoZW10b2xzbWEubmwvY291cnNlcy9jb21wbGV0ZS1uZXR3b3Jrcy9zb2NpbzYvKS4gDQoNCiMjIyMgc2VsZWN0IGFnZW50DQoNCk9rYXksIHdlIGNhbiB0aHVzIHJhbmRvbWx5IHNlbGVjdCBhbiBhZ2VudC4gDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMjQ1NTMyNTMpDQplZ28gPC0gdHNfc2VsZWN0KG5ldD10c19uZXQxKSANCmVnbw0KYGBgDQoNCg0KIyMjIFBvc3NpYmxlIG5ldHdvcmtzIGFmdGVyIG1pbmlzdGVwICANCg0KTGV0IHVzIHN1cHBvc2Ugd2Ugd2FudCB0byBrbm93IHdoYXQgdGhlIHBvc3NpYmxlIG5ldHdvcmtzIGFyZSBhZnRlciBhbGwgcG9zc2libGUgbWluaXN0ZXBzIG9mIGBlZ29gIHdobyBpcyBwYXJ0IG9mIGB0c19uZXQxYC4gVGhhdCBpcywgbGV0IHVzIGFzc3VtZSB0aGF0IGl0IGlzIGVnbydzIHR1cm4gKGVnbyM6IGByIGVnb2ApIHRvIGRlY2lkZSBvbiB0aWUtY2hhbmdlLiBXaGF0IGFyZSB0aGUgcG9zc2libGUgbmV0d29ya3M/IA0KDQpUaGUgZnVuY3Rpb24gYHRzX2FsdGVybmF0aXZlc19taW5pc3RlcCgpYCByZXR1cm5zIGEgbGlzdCBvZiBhbGwgcG9zc2libGUgbmV0d29ya3MgYWZ0ZXIgYWxsIHBvc3NpYmxlIHRpZS1jaGFuZ2VzIGF2YWlsYWJsZSB0byBhbiBlZ28gZ2l2ZW4gdGhlIGN1cnJlbnQgc3RhdGUgb2YgYSBuZXR3b3JrLg0KDQpgYGB7ciwgcmVzdWx0cz0naGlkZSd9DQpvcHRpb25zIDwtIHRzX2FsdGVybmF0aXZlc19taW5pc3RlcChuZXQ9dHNfbmV0MSwgZWdvPWVnbykNCm9wdGlvbnMNCnBsb3RzIDwtIGxhcHBseShvcHRpb25zLCBncmFwaF9mcm9tX2FkamFjZW5jeV9tYXRyaXgsIG1vZGU9ImRpcmVjdGVkIikNCnBhcihtYXI9YygwLDAsMCwwKSsuMSkNCnBhcihtZnJvdz1jKDIsMikpDQoNCmZwbG90IDwtIGZ1bmN0aW9uKHgpIHsNCiAgcGxvdC5pZ3JhcGgoeCwgbGF5b3V0PWNvb3JkcywgbWFyZ2luPTApDQogIGdyYXBoaWNzOjpib3goKQ0KfQ0KDQpsYXBwbHkocGxvdHMsIGZwbG90KSANCmBgYA0KDQoNCg0KIyMjIE5ldHdvcmsgc3RhdGlzdGljcyANCg0KV2hpY2ggb3B0aW9uIChvciBtaW5pc3RlcCkgd2lsbCBlZ28gY2hvb3NlPyBOYXR1cmFsbHkgdGhpcyB3aWxsIGRlcGVuZCBvbiB3aGljaCBuZXR3b3JrIGNoYXJhY3RlcmlzdGljcyAob3Igc28tY2FsbGVkIHN0YXRpc3RpY3MpIGVnbyBmaW5kcyByZWxldmFudC4gTGV0IHVzIHN1cHBvc2UgdGhhdCBlZ28gYmFzZXMgaXRzIGRlY2lzaW9uIHNvbGVseSBvbiB0aGUgbnVtYmVyIG9mIHRpZXMgaXQgc2VuZHMgdG8gb3RoZXJzIGFuZCB0aGUgbnVtYmVyIG9mIHJlY2lwcm9jYXRlZCB0aWVzIGl0IGhhcyB3aXRoIG90aGVycy4gDQoNCkxldCB1cyBjb3VudCB0aGUgbnVtYmVyIG9mIHRpZXMgZWdvIHNlbmRzIHRvIGFsdGVycy4gDQoNCmBgYHtyfQ0KdHNfZGVncmVlKG5ldD1vcHRpb25zW1sxXV0sIGVnbz1lZ28pDQpgYGANCg0Kb3IgZm9yIGFsbCBvcHRpb25zDQpgYGB7cn0NCnNhcHBseShvcHRpb25zLCB0c19kZWdyZWUsIGVnbz1lZ28pDQpgYGANCg0KQW5kIGxldCB1cyBjb3VudCB0aGUgbnVtYmVyIG9mIHJlY2lwcm9jYXRlZCB0aWVzDQoNCmBgYHtyfQ0Kc2FwcGx5KG9wdGlvbnMsIHRzX3JlY2lwLCBlZ289ZWdvKQ0KYGBgDQo+ICoqQXNzaWdtZW50IDE6KiogIA0KPiAxYTogd3JpdGUgYSBmdW5jdGlvbiB0aGF0IGNvdW50cyB0aGUgbnVtYmVyIG9mIHRyYW5zaXRpdmUgdHJpYWRzIGZvciBvdXIgZWdvICANCj4gMWI6IHdyaXRlIGEgZnVuY3Rpb24gdGhhdCBjb3VudHMgZm9yIGFsbCBlZ28ncyB0aGUgdHJhbnNpdGl2ZSB0cmlhZHMgYW5kIHN1bXMgdGhlIHJlc3VsdCAgDQoNCg0KSW4gdGhlIHBhY2thZ2UgYFJzaWVuYVR3b1N0ZXBgIHRoZXJlIGFyZSBmdW5jdGlvbnMgZm9yIHRoZSBmb2xsb3dpbmcgbmV0d29yayBzdGF0aXN0aWNzICRzJDogDQoNCiAgLSBkZWdyZWU6IGB0c19kZWdyZWUoKWAgDQogIC0gcmVjaXByb2NpdHk6IGB0c19yZWNpcCgpYCAgDQogIC0gb3V0ZGVncmVlIGFjdGl2aXR5OiBgdHNfb3V0QWN0KClgIA0KICAtIGluZGVncmVlIGFjdGl2aXR5OiBgdHNfaW5BY3QoKWAgDQogIC0gb3V0ZGVncmVlIHBvcHVsYXJpdHk6IGB0c19vdXRQb3AoKWAgDQogIC0gaW5kZWdyZWUgcG9wdWxhcml0eTogYHRzX2luUG9wKClgICANCiAgLSB0cmFuc2l0aXZpdHk6IGB0c190cmFuc1RyaXAoKWAgDQogIC0gbWVkaWF0ZWQgdHJhbnNpdGl2aXR5OiBgdHNfdHJhbnNNZWRUcmlwKClgICANCiAgLSB0cmFuc2l0aXZlIHJlY2lwcm9jYXRlZCB0cmlwbGV0czogYHRzX3RyYW5zUmVjVHJpcCgpYCANCiAgLSBudW1iZXIgb2YgdGhyZWUtY3ljbGVzOiBgdHNfY3ljbGUzKClgICANCg0KUGxlYXNlIHNlZSBgP3RzX2RlZ3JlZSgpYC4gDQoNCmByIGNvbG9yaXplKCJVcGRhdGU6IHRoZXJlIGFyZSBxdWl0ZSBhIGxvdCBtb3JlLiBCdXQgbm90IHZlcnkgcmVsZXZhbnQgZm9yIHVzIGF0IHRoaXMgdGltZS4iLCAicmVkIilgICANCg0KU2VlIEByaXBsZXkyMDIybWFudWFsIChDaGFwdGVyIDEyKSBmb3IgdGhlIG1hdGhlbWF0aWNhbCBmb3JtdWxhdGlvbi4gTmF0dXJhbGx5LCB5b3UgYXJlIGZyZWUgdG8gZGVmaW5lIHlvdXIgb3duIG5ldHdvcmsgc3RhdGlzdGljcy4gDQoNCiMjIyBFdmFsdWF0aW9uIGZ1bmN0aW9uIA0KDQpCdXQgd2hhdCBldmFsdWF0aW9uIHZhbHVlIGRvZXMgZWdvIGF0dGFjaCB0byB0aGVzZSBuZXR3b3JrIHN0YXRpc3RpY3MgYW5kIGNvbnNlcXVlbnRseSB0byB0aGUgbmV0d29yayAoaW4gaXRzIHZpY2luaXR5KSBhcyBhIHdob2xlPyBXZWxsIHRoZXNlIGFyZSB0aGUgcGFyYW1ldGVycywgJFxiZXRhJCwgeW91IHdpbGwgbm9ybWFsbHkgZXN0aW1hdGUgd2l0aCBgUlNpZW5hOjpzaWVuYTA3KClgLiANCkxldCB1cyBzdXBwb3NlIHRoZSBpbXBvcnRhbmNlIGZvcjogIA0KDQotIHRoZSBzdGF0aXN0aWMgJ2RlZ3JlZScgaXMgLTEgIA0KLSBmb3IgdGhlIHN0YXRpc3RpYyAncmVjaXByb2NpdHknIGlzIDEuNS4gDQoNClNvIHlvdSBjb3VsZCBjYWxjdWxhdGUgdGhlIGV2YWx1YXRpb24gb2YgdGhlIG5ldHdvcmsgc2F2ZWQgaW4gYG9wdGlvbnNbWzRdXWAgYnkgaGFuZDogDQoNCiQkZl57ZXZhbH1fe259KFxtYXRoYmZ7c197aX19LFxtYXRoYmZ7XGJldGF9KSA9IFxtYXRoYmZ7c19pfV5cbWF0aHNme1R9XG1hdGhiZntcYmV0YX0sICQkICANCndpdGggJGZee2V2YWx9X3tufSQgdGhlIGV2YWx1YXRpb24gZnVuY3Rpb24gZm9yIGFnZW50ICRuJC4gJFxtYXRoYmZ7c197aX19JCBhcmUgdGhlIG5ldHdvcmsgc3RhdGlzdGljcyBmb3IgbmV0d29yayAkaSQgYW5kICRcbWF0aGJme1xiZXRhfSQgdGhlIGNvcnJlc3BvbmRpbmcgcGFyYW1ldGVycyAob3IgaW1wb3J0YW5jZSkuDQoNCmBgYHtyfQ0Kb3B0aW9uIDwtIDQNCnRzX2RlZ3JlZShvcHRpb25zW1tvcHRpb25dXSwgZWdvPWVnbykqLTEgKyB0c19yZWNpcChvcHRpb25zW1tvcHRpb25dXSwgZWdvPWVnbykqMS41DQpgYGANCg0KT3IgeW91IGNvdWxkIHVzZSB0aGUgYHRzX2V2YWwoKWAuIA0KDQpgYGB7cn0NCmV2YWwgPC0gdHNfZXZhbChuZXQ9b3B0aW9uc1tbb3B0aW9uXV0sIGVnbz1lZ28sIHN0YXRpc3RpY3M9bGlzdCh0c19kZWdyZWUsIHRzX3JlY2lwKSwgcGFyYW1ldGVycz1jKC0xLDEuNSkpDQpldmFsDQpgYGANCk5vdywgbGV0IHVzIGNhbGN1bGF0ZSB0aGUgZXZhbHVhdGlvbiBvZiBhbGwgcG9zc2libGUgbmV0d29ya3M6IA0KYGBge3IgdDEsIHJlc3VsdHM9J2hvbGQnfQ0KZXZhbCA8LSBzYXBwbHkob3B0aW9ucywgRlVOPXRzX2V2YWwsIGVnbz1lZ28sIHN0YXRpc3RpY3M9bGlzdCh0c19kZWdyZWUsIHRzX3JlY2lwKSwgcGFyYW1ldGVycz1jKC0xLDEuNSkpDQpldmFsDQpwcmludCgibmV0d29yayB3aXRoIG1heGltdW0gZXZhbHVhdGlvbiBzY29yZToiKQ0Kd2hpY2gubWF4KGV2YWwpDQpgYGANCg0KIyMjIENob2ljZSBmdW5jdGlvbiANCg0KU28gd2hpY2ggb3B0aW9uIHdpbGwgZWdvIGNob29zZT8gTmF0dXJhbGx5IHRoaXMgd2lsbCBiZSBhIHN0b2NoYXN0aWMgcHJvY2Vzcy4gQnV0IHdlIHNlZSB0aGF0IG9wdGlvbiBgciB3aGljaC5tYXgoZXZhbClgIGhhcyB0aGUgaGlnaGVzdCBldmFsdWF0aW9uLiANCldlIHVzZSBNY0ZhZGRlbidzIGNob2ljZSBmdW5jdGlvbiAoZm9yIG1vcmUgaW5mb3JtYXRpb24gc2VlIFt3aWtpXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9EaXNjcmV0ZV9jaG9pY2UpKSwgdGhhdCBpcyBsZXQgJFBfe25pfSQgYmUgdGhlIHByb2JhYmlsaXR5IHRoYXQgZWdvICRuJCBjaG9vc2VzIG5ldHdvcmsvYWx0ZXJuYXRpdmUgJGkkLiBUaGUgY2hvaWNlIGZ1bmN0aW9uIGlzIHRoZW4gZ2l2ZW4gYnk6ICANCg0KDQokJFBfe24saX0gPSBcZnJhY3tleHAoXG1hdGhiZntzX2l9XlxtYXRoc2Z7VH1cbWF0aGJme1xiZXRhfSApfXtcU2lnbWFfe2o9MX1eSiBleHAoXG1hdGhiZntzX2p9XlxtYXRoc2Z7VH1cbWF0aGJme1xiZXRhfSApfSwkJCAgDQoNCndpdGggJHNfaSQgYSB2ZWN0b3Igb2YgdGhlIHZhbHVlIG9mIGVhY2ggbmV0d29yayBzdGF0aXN0aWNzIGZvciBuZXR3b3JrICRpJCBhbmQgJFxiZXRhJCBpcyB0aGUgdmVjdG9yIG9mIHBhcmFtZXRlciB2YWx1ZXMuIEhlbmNlLCAkXG1hdGhiZntzX2l9XlxtYXRoc2Z7VH1cbWF0aGJme1xiZXRhfSQgaXMgdGhlIHZhbHVlIG9mIHRoZSBldmFsdWF0aW9uIGZvciBuZXR3b3JrICRpJC4NCg0KTGV0IHVzIGZvcmNlIGVnbyB0byBtYWtlIGEgZGVjaXNpb24uIA0KDQpgYGB7ciwgcmVzdWx0cz0naG9sZCd9DQpjaG9pY2UgPC0gc2FtcGxlKDE6bGVuZ3RoKGV2YWwpLCBzaXplPTEsIHByb2I9ZXhwKGV2YWwpL3N1bShleHAoZXZhbCkpKQ0KcHJpbnQoImNob2ljZToiKSANCmNob2ljZQ0KI3ByaW50KCJuZXR3b3JrOiIpDQojb3B0aW9uc1tbY2hvaWNlXV0NCmBgYA0KDQpJZiB3ZSByZXBlYXQgdGhpcyBwcm9jZXNzLCB0aGF0IGlzLi4uOiANCg0KMS4gc2FtcGxlIGFnZW50ICANCjIuIGNvbnN0cnVjdCBwb3NzaWJsZSBhbHRlcm5hdGl2ZSBuZXR3b3JrcyAgDQozLiBjYWxjdWxhdGUgaG93IHNhbXBsZWQgYWdlbnQgZXZhbHVhdGVzIHRoZSBwb3NzaWJsZSBuZXR3b3JrcyAgDQo0LiBMZXQgdGhlIGFnZW50IHBpY2sgYSBuZXR3b3JrLCB0aGF0IGlzLCBsZXQgYWdlbnQgZGVjaWRlIG9uIGEgdGllLWNoYW5nZSAgDQo1LiBHTyBCQUNLIFRPIDEgKFNUT1BQSU5HIFJVTEU6IHVudGlsIHlvdSB0aGluayB3ZSBoYXZlIG1hZGUgZW5vdWdoIG1pbmlzdGVwcykNCg0KLi4ud2UgaGF2ZSBhbiBhZ2VudCBiYXNlZCBtb2RlbC4gICANCg0KDQojIyMjIEludGVycHJldGF0aW9uIG9mIHBhcmFtZXRlcnMgIA0KDQpBbHRob3VnaCB3ZSBoYXZlIG5vdCB5ZXQgZXN0aW1hdGVkIGFuIFJTaWVuYSBtb2RlbCwgdGhpcyBpcyBhbHJlYWR5IHRoZSB0aW1lIHRvIGV4cGxhaW4gdGhlIGludGVycHJldGF0aW9uIG9mIHRoZSBlc3RpbWF0ZWQgcGFyYW1ldGVycy4gDQpTdXBwb3NlIHRoYXQgeW91IHdhbnQgdG8gaW50ZXJwcmV0IHRoZSBlc3RpbWF0ZWQgcGFyYW1ldGVyIGZvciB0aGUgZGVncmVlIHN0YXRpc3RpYy4gDQoNCkhhdmUgYSBsb29rIGF0IHRoZSBwb3NzaWJsZSBvcHRpb25zIG91ciBlZ28gaGFzLiBDYW4geW91IGZpbmQgdHdvIG5ldHdvcmtzIHRoYXQgb25seSBkaWZmZXIgd2l0aCByZXNwZWN0IHRvIHRoZSBudW1iZXIgb2YgdGllcywgYW5kIG1vcmUgcHJlY2lzZWx5IG9ubHkgd2l0aCByZXNwZWN0IHRvIDEgKHNlbmRpbmcpIHRpZT8gDQoNCkZvciBleGFtcGxlLCBPcHRpb24gMSwgaGFzIG9uZSBtb3JlIHNlbmRpbmcgdGllIHRoYW4gb3B0aW9uIDQuIA0KDQpXaGF0IGlzIHRoZSBwcm9iYWJpbGl0eSByYXRpbz86IA0KDQokJCBcZnJhY3tQX3tuLGk9MX19e1Bfe24saT00fX0sJCQgIA0KDQpZb3Ugd2lsbCBzZWUgdGhhdCB0aGUgZGVub21pbmF0b3IgY2FuY2VscyBvdXQuIFRodXM6IA0KDQokJCBcZnJhY3tQX3tuLGk9MX19e1Bfe24saT00fX0gPSBcZnJhY3tleHAoXG1hdGhiZntzX3tpPTF9fV5cbWF0aHNme1R9XG1hdGhiZntcYmV0YX0gKX17ZXhwKFxtYXRoYmZ7c197aT00fX1eXG1hdGhzZntUfVxtYXRoYmZ7XGJldGF9ICl9ID0gZXhwKFxtYXRoYmZ7c197aT0xfX1eXG1hdGhzZntUfVxtYXRoYmZ7XGJldGF9IC0gXG1hdGhiZntzX3tpPTR9fV5cbWF0aHNme1R9XG1hdGhiZntcYmV0YX0pID0gZXhwKHtcYmV0YV97ZGVncmVlfX0pICwkJCAgDQphbmQgdGh1czogIA0KDQokJCBsb2coXGZyYWN7UF97bixpPTF9fXtQX3tuLGk9NH19KSA9IHtcYmV0YX0gLCQkIA0KLi4udGhlIGxvZyBwcm9iYWJpbGl0eSByYXRpbyB0aGF0IGFuIGVnbyB3aWxsIGNob29zZSBhIG5ldHdvcmsgd2l0aCBvbmUgYWRkaXRpb25hbCBzZW5kaW5nIHRpZSBvdmVyIGEgbmV0d29yayB3aXRob3V0IGFuIGFkZGl0aW9uYWwgc2VuZGluZyB0aWUgaXMgcHJlY2lzZWx5IG91ciBzdGF0aXN0aWMgcGFyYW1ldGVyICR7XGJldGF9JC4gDQoNCkhvcGVmdWxseSB5b3Ugc2VlIHRoZSBpbnRlcnByZXRhdGlvbiBzaW1pbGFyaXR5IHdpdGggYSBzdGFuZGFyZCAobXVsdGlub21pYWwpIGxvZ2l0IG1vZGVsLiANCg0KIyMjIFN0b3BwaW5nIHJ1bGUgIA0KDQpCdXQgaG93IG1hbnkgbWluaXN0ZXBzIGRvIHdlIGFsbG93PyBXZWxsLCBub3JtYWxseSB0aGlzIGlzIGVzdGltYXRlZCBieSBgc2llbmEwN2AgYnkgdGhlIGByYXRlYCBwYXJhbWV0ZXIuXls6IE5hdHVyYWxseSwgaXQgaXMgYSBiaXQgbW9yZSBjb21wbGljYXRlZCB0aGFuIHRoYXQuIEluIFJTaWVuYSB3ZSBoYXZlIGEgY2hvaWNlIGJldHdlZW4gdW5jb25kaXRpb25hbCBhbmQgY29uZGl0aW9uYWwgZXN0aW1hdGlvbi4gTXkgZGVzY3JpcHRpb24gb2YgdGhlIHN0b3BwaW5nIHJ1bGUgcmVmZXJzIHRvIHRoZSB1bmNvbmRpdGlvbmFsIGVzdGltYXRpb24uXSBJZiB3ZSBkbyBub3QgbWFrZSB0aGlzIHJhdGUgcGFyYW1ldGVyIGNvbmRpdGlvbmFsIG9uIGFjdG9yIGNvdmFyaWF0ZXMgb3Igb24gbmV0d29yayBjaGFyYWN0ZXJpc3RpY3MsIHRoZSByYXRlIHBhcmFtZXRlciBjYW4gYmUgaW50ZXJwcmV0ZWQgYXMgdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIG1pbmlzdGVwcyBlYWNoIGFjdG9yIGluIHRoZSBuZXR3b3JrIGlzIGFsbG93ZWQgdG8gbWFrZSBiZWZvcmUgdGltZSBpcyB1cC4gTGV0IHVzIHN1cHBvc2UgdGhlIGByYXRlYCBwYXJhbWV0ZXIgaXMgMiBgciByYXRlPC0yYC4gVGh1cyBpbiB0b3RhbCB0aGUgbnVtYmVyIG9mIHBvc3NpYmxlIG1pbmlzdGVwcyB3aWxsIGJlIGBucm93KG5ldDEpKnJhdGVgOiBgciBucm93KHRzX25ldDEpKnJhdGVgLiBGb3IgYSBtb3JlIGRldGFpbGVkIC0gKiphbmQgbW9yZSBjb3JyZWN0KiogLSAgaW50ZXJwcmV0YXRpb24gb2YgdGhlIHJhdGUgcGFyYW1ldGVyIGluIGBzaWVuYTA3YCBzZWU6IFt3d3cuam9jaGVtdG9sc21hLm5sXSh3d3cuam9jaGVtdG9sc21hLm5sL2NvdXJzZXMvY29tcGxldGUtbmV0d29ya3Mvc29jaW82KS4NCg0KIyMgU2ltdWxhdGlvbiBleGFtcGxlICANCg0KTGV0IHVzIG5vdyBzaW11bGF0ZSBob3cgdGhlIG5ldHdvcmsgKipjb3VsZCoqIGV2b2x2ZSBnaXZlbjpeW0l0IGlzIGFsc28gcG9zc2libGUgdG8gc2ltdWxhdGUgbmV0d29ya3Mgd2l0aGluIHRoZSBgUlNpZW5hYCBwYWNrYWdlIGl0c2VsZi4gQnV0IGxldCB1cyBzdGljayB3aXRoIGZ1bmN0aW9ucyBmcm9tIGBSc2llbmFUd29TdGVwYCBmb3Igbm93Ll0gIA0KDQotIHN0YXJ0aW5nIHBvaW50IGlzIGB0c19uZXQxYCANCi0gcmF0ZSBpcyBzZXQgdG8gMiAgDQotIHdlIGFzIHNjaWVudGlzdHMgdGhpbmsgb25seSBuZXR3b3JrIHN0YXRpc3RpY3MgZGVncmVlIGFuZCByZWNpcHJvY2l0eSBhcmUgaW1wb3J0YW50ICANCi0gYFJTaWVuYTo6c2llbmEwN2AgaGFzIGRldGVybWluZWQgdGhlIHBhcmFtZXRlcnMgZm9yIHRoZXNlIHN0YXRpc3RpY3MgYXJlIC0xIGFuZCAxLjUgcmVzcGVjdGl2ZWx5ICANCg0KYGBge3IgdGVzdCwgZWNobz1GQUxTRSwgZXZhbD1UUlVFfQ0KZGF0YSh0c19uZXQxKQ0KdHNfbmV0MQ0KYGBgDQoNCmBgYHtyfQ0Kc2ltbmV0MiA8LSB0c19zaW0oDQogICAgICAgIG5ldDE9dHNfbmV0MSwgI3RoaXMgaXMgb3VyIHN0YXJ0IG5ldHd3b3JrDQogICAgICAgIHN0YXRpc3RpY3M9bGlzdCh0c19kZWdyZWUsIHRzX3JlY2lwKSwgI3dlIG9ubHkgaW5jbHVkZSB0byBzdGF0aXN0aWNzDQogICAgICAgIHN0YXJ0dmFsdWVzID0gYygyLC0xLDEuNSkpICMgdGhlIHN0YXJ0IHZhbHVlcyBmb3Igb3VyIHJhdGUgcGFyYW1ldGVyIChhdXRvbWF0aWNhbGx5IGluY2x1ZGVkKSwgYW5kIG91ciB0d28gc3RhdGlzdGljcw0KDQpzaW1uZXQyDQpgYGANCg0KPiAqKkFzc2lnbm1lbnQgMjoqKiAgDQo+IDJhOiBVc2UgdGhlIHNhbWUgc3RhcnRpbmcgdmFsdWVzIGFuZCBtYWtlIDEwIHNpbXVsYXRpb24gcnVucyAoc2F2ZSByZXN1bHRzIGluIGEgbGlzdCBvciBzb21ldGhpbmcpLiBIaW50LCB5b3UgbWF5IHdhbnQgdG8gdXNlIGB0c19zaW1zKClgICANCj4gMmI6IHR3ZWFrIHRoZSBzdGFydGluZyB2YWx1ZXMgKGEgY291cGxlIG9mIHRpbWVzKSBhbmQgZm9yIGVhY2ggcGFyYW1ldGVyIGNvbmZpZ3VyYXRpb24gbWFrZSAxMCBzaW11bGF0aW9uIHJ1bnMgKG9uY2UgYWdhaW4gc2F2ZSB5b3VyIHJlc3VsdHMpDQo+IDJjOiBmb3IgZWFjaCBvZiB0aGUgc2ltdWxhdGVkIG5ldHdvcmtzIGNhbGN1bGF0ZSB0byB0b3RhbCBudW1iZXIgb2YgdGllcyBhbmQgdGhlIHRvdGFsIG51bWJlciByZWNpcHJvY2F0ZWQgdGllcyAgDQo+IDJkOiBXaGF0IGRpZCB5b3UgbGVhcm4gZnJvbSB0aGlzIGV4ZXJjaXNlPyANCg0KDQojIEVzdGltYXRpb24gbG9naWMgIA0KDQpTbyBub3cgeW91IGhhdmUgc29tZSBncmFzcCBvZiB0aGUgbG9naWMgb2YgUlNpZW5hLiBCdXQgaG93IGRvZXMgdGhlIGVzdGltYXRpb24gd29yaz8gIA0KDQpXZWxsLCB0aGF0IGlzIHF1aXRlIGNvbXBsaWNhdGVkLiBCdXQgaXQgZ29lcyBzb21ldGhpbmcgbGlrZSB0aGlzOl5bJ2l0JyByZWZlcnMgdG8gZXN0aW1hdGlvbiB2aWEgdGhlICdNZXRob2Qgb2YgTW9tZW50cyddIA0KDQoxLiBEZWZpbmUgbW9kZWw6IHJlc2VhcmNoZXIgaW5jbHVkZXMgZWZmZWN0cyAgDQoyLiBpbml0aWFsIHBhcmFtZXRlciB2YWx1ZXMgb2YgZWZmZWN0cyAoY29tbW9ubHkgJzAnKSAgDQozLiBzaW11bGF0ZSBhbiBvdXRjb21lIG5ldHdvcmsgYmFzZWQgb24gdGhlc2UgcGFyYW1ldGVyIHZhbHVlcyAgDQo0LiBjb21wYXJlIHRoZSBuZXR3b3JrIHN0YXRpc3RpY3Mgb2YgdGhlIHNpbXVsYXRlZCBvdXRjb21lIG5ldHdvcmsgd2l0aCB0aGUgb2JzZXJ2ZWQgb3V0Y29tZSBuZXR3b3JrIChpLmUuIHRoZSAqdGFyZ2V0IHZhbHVlcyopICANCiAgICAtIGJhc2VkIG9uIHRoZSBpbmNsdWRlZCBlZmZlY3RzLiBUaHVzIHRoZSBzaW11bGF0ZWQgbmV0d29yayBtYXkgY29udGFpbiAxMCB0aWVzLCBidXQgdGhlIG9ic2VydmVkIG5ldHdvcmsgbWF5IGNvbnRhaW4gMjAgdGllcy4gQXBwYXJlbnRseSwgd2l0aCB0aGUgY3VycmVudCBwYXJhbWV0ZXIgdmFsdWVzIHdlIHVuZGVyZXN0aW1hdGUgdGhlIGRlbnNpdHkvZGVncmVlIG9mIHRoZSBvdXRjb21lIG5ldHdvcmsgYW5kIHdlIG1heSB3aXNoIHRvIGluY3JlYXNlIHRoZSBwYXJhbWV0ZXIgb2Ygb3VyIGRlbnNpdHkgcGFyYW1ldGVyLiAgDQo1LiB0d2Vhay91cGRhdGUgcGFyYW1ldGVyIHZhbHVlcyBpbiBzb21lIHNtYXJ0IHdheSAgDQo2LiBHT1RPIDMgKEJSRUFLIFJVTEU6IHVudGlsIHBhcmFtZXRlciB2YWx1ZXMgY2Fubm90IGJlIGltcHJvdmVkIGFueW1vcmUgLyBvciByZWFjaGVkIGdvb2QgZml0KSAgDQo3LiBzaW11bGF0ZSBhIGJ1bmNoIG9mIG91dGNvbWUgbmV0d29ya3Mgd2l0aCB0aGUgb2J0YWluZWQgcGFyYW1ldGVyIHZhbHVlcyBhbmQgY29tcGFyZSB0aGUgKmV4cGVjdGVkIHZhbHVlcyogb2Ygc3RhdGlzdGljcyBvZiB0aGUgb3V0Y29tZSBuZXR3b3JrcyB3aXRoIHRoZSAqdGFyZ2V0IHZhbHVlcyouICANCiAgICAtIHdlIGNhbiBhc3Nlc3MgdGhlIGZpdCAgDQogICAgLSBlc3RpbWF0ZSBTRSBvZiB0aGUgcGFyYW1ldGVycw0KDQoNCj4gKipFeGVyY2lzZSAzOioqICANCj4gTGV0IHVzIHN0YXJ0IHBsYXlpbmcgd2l0aCB0aGUgYnVpbGQtaW4gZGF0YXNldHMgb2YgYFJTaWVuYWA6IGBzNTAxYCBhbmQgYHM1MDJgLiAgDQo+IFBpY2sgdGhyZWUgc3RhdGlzdGljcyB0aGF0IHlvdSB0aGluayB3aWxsIGJlIHJlbGV2YW50IGluIGVzdGltYXRpbmcgdGhlIG5ldHdvcmsgZXZvbHV0aW9uIGZvciB0aGVzZSBkYXRhc2V0cy4gIA0KPiAzYS4gRm9yIGVhY2ggb2YgdGhlIGFib3ZlIHN0YXRpc3RpY3MsIGRlZmluZSB0aGUgdGFyZ2V0IHZhbHVlcyBhbmQgcHJvZ3JhbSBhIGZ1bmN0aW9uIHRoYXQgY2FsY3VsYXRlcyB0aGUgdGFyZ2V0IHZhbHVlcy4gICANCj4gM2IuIEhvdyB3b3VsZCB5b3UgcGljayBzbWFydCBzdGFydGluZyB2YWx1ZXMgZm9yIHRoZSByYXRlIGFuZCBkZWdyZWUgcGFyYW1ldGVycz8gICAgDQo+IDNjLiBEbyAxMCBjb25zZWN1dGl2ZSBzaW11bGF0aW9uIHJ1bnMuIEFmdGVyIGVhY2ggcnVuIHlvdSB0d2VhayB5b3VyIHBhcmFtZXRlcnM/ICAgIA0KPiBEaWQgeW91IGdldCBjbG9zZSB0byB5b3VyIHRhcmdldCB2YWx1ZXM/ICANCg0KU29tZSBoaW50czogIA0KDQpDYWxjdWxhdGluZyB0YXJnZXRzL291dGNvbWUgdmFsdWVzOiANCg0KYGBge3J9DQojY2FsY3VsYXRlIHRoZSB0YXJnZXRzDQp0c190YXJnZXRzKG5ldDEgPSBzNTAxLCBuZXQyID0gczUwMiwgc3RhdGlzdGljcyA9IGxpc3QodHNfZGVncmVlLCB0c19yZWNpcCkpDQoNCiNpZiB5b3Ugc2V0IHRoZSBhcmd1bWVudCBgbmV0MmAgdG8gYmUgZXF1YWwgdG8geW91ciBzaW11bGF0ZWQgbmV0d29yaywgeW91IGNvdW50IHRoZSB2YWx1ZXMgb2JzZXJ2ZWQgaW4geW91ciBzaW11bGF0ZWQgbmV0d29ya2AgDQoNCmBgYA0KRGVmaW5pbmcgeW91ciBvd24gc3RhdGlzdGljczogDQoNCllvdSBjYW4gbG9vayBhdCB0aGUgc291cmNlIGNvZGUgdG8gZ2V0IGluc3BpcmF0aW9uOiANCg0KYGBge3J9DQp0c190cmFuc1RyaXANCmBgYA0KDQoNCg0KLS0tLQ0KDQojIFJlZmVyZW5jZXMNCg0K


Copyright © 2024 Jochem Tolsma