This website converted the following original .R scripts into .rmd
files.
- Rscript01DataFormat.R
- RScriptSNADescriptives.R
- Rscript02SienaVariableFormat.R
- Rscript03SienaRunModel.R
- Rscript04SienaBehaviour.R
Please visit GitHub
for the latest .R files.
Data
All files (data, scripts, etc.) can also be found on Github
Introduction
Let us start by loading the data
library(RSiena)
friend.data.w1 <- s501
friend.data.w2 <- s502
friend.data.w3 <- s503
drink <- s50a
smoke <- s50s
A number of objects need to be created in R, as preparations to
letting siena07 execute the estimation. This will be indicated by:
- A: dependent variables;
- B: explanatory variables;
- C: combination of dependent and explanatory variables;
- D: model specification.
A. dependent variables
First we have to create objects for the dependent variables.
sienaDependent()
creates a sienaDependent object, here a
network, from a matrix or array or list of sparse matrix of triples.
This object will have the role of a dependent variable in the
analysis.
The name of this network object (here: friendship)
will be used in the output file.
friendship <- sienaDependent(array(c(friend.data.w1, friend.data.w2, friend.data.w3), dim = c(50, 50,
3)))
The integers in the dim()
here refer to the number of
nodes (senders, receivers) and the number of waves. This object is an
array of dimension 50 x 50 x 3, representing three adjacency matrices,
with a number of attributes.
You can ask for a brief decription of this object simply by
typing:
print(friendship)
#> Type oneMode
#> Observations 3
#> Nodeset Actors (50 elements)
# for which the shorthand is
friendship
#> Type oneMode
#> Observations 3
#> Nodeset Actors (50 elements)
Note that this is an object of class
class(friendship)
#> [1] "sienaDependent"
With specific attributes and methods associated with it. YYou can get
the detailed information by requesting:
dim(friendship)
#> [1] 50 50 3
attributes(friendship)
#> $dim
#> [1] 50 50 3
#>
#> $class
#> [1] "sienaDependent"
#>
#> $type
#> [1] "oneMode"
#>
#> $sparse
#> [1] FALSE
#>
#> $nodeSet
#> [1] "Actors"
#>
#> $netdims
#> [1] 50 50 3
#>
#> $allowOnly
#> [1] TRUE
If you only are interested in the value of one particular attribute,
you can request this by, e.g.,
attr(friendship, "type")
#> [1] "oneMode"
An extensive description of the friendship data is obtained by
typing:
str(friendship)
#> 'sienaDependent' int [1:50, 1:50, 1:3] 0 0 0 0 0 0 0 0 0 0 ...
#> - attr(*, "type")= chr "oneMode"
#> - attr(*, "sparse")= logi FALSE
#> - attr(*, "nodeSet")= chr "Actors"
#> - attr(*, "netdims")= int [1:3] 50 50 3
#> - attr(*, "allowOnly")= logi TRUE
The function sienaDependent()
can also be used to create
a behavior variable object with the extra argument
type = "behavior"
.
Non-mentioned attributes get the default value, and in this case
oneMode is the default; see below.
The drink
data (created in RscriptDataFormat.R ) is made
available as a dependent behavior variable by the function:
drinkingbeh <- sienaDependent(drink, type = "behavior")
# the class, class(drinkingbeh), is still sienaDependent.
Note: only use the variable in ONE role in a given model: behavior
variable or changing covariate!
The options available for defining a sienaDependent object are
displayed by typing: ?sienaDependent
.
This shows that next to one-mode (unipartite) and behavior dependent
variables, also two-mode (bipartite) dependent variables are
possible.
You can infer that oneMode is the default type from the fact that it is
mentioned first.
To create bipartite network objects you need two node sets and must
create the node sets too.
Please See ?sienaNodeSet
where the Examples section
shows an example of the syntax.
B. explanatory variables
Second we construct objects for the explanatory (independent)
variables.
From the help request: ?sienaDataCreate
We see that these can be of five kinds:
coCovar
: Constant actor covariates
varCovar
: Time-varying actor covariates
coDyadCovar
: Constant dyadic covariates
varDyadCovar
: Time-varying dyadic covariates
compositionChange
Composition change indicators
You can get help about this by the following requests:
?coCovar
?varCovar
?coDyadCovar
?varDyadCovar
?sienaCompositionChange
The variables available for this data set all are changing actor
covariates.
For illustrative purposes, we use smoking as observed at the first wave
as a constant covariate:
smoke1 <- coCovar(smoke[, 1])
This selects the first column of smoke, which contains the first wave
observations, and makes it available as a constant covariate. This is
the pattern for for evey covariate file,
e.g. Attr1 <- coCovar( Covariate1 )
where Covariate1 is
a matrix with dim(Covariate1)
equal to n x 1.
Note, if Covariates is a matrix with dim(Covariates)
equal to n x p you can create constant covariates through:
Attr1 <- coCovar(Covariates[,1])
- …
Attrp <- coCovar(Covariates[,p])
We use the drinking data as a changing covariate. The function
`varCovar() creates a changing covariate object from a matrix; the name
comes from ‘varying covariate’.
alcohol <- varCovar(drink)
You need at least three waves in the data set to define a varying
covariate by the function varCovar()
; the reason is that
the previous wave is used as a predictor of the next wave.
The command…
attributes(alcohol)
#> $dim
#> [1] 50 3
#>
#> $dimnames
#> $dimnames[[1]]
#> [1] "1" "2" "3" "4" "5" "6" "7" "8" "9" "10" "11" "12" "13" "14" "15" "16" "17" "18" "19"
#> [20] "20" "21" "22" "23" "24" "25" "26" "27" "28" "29" "30" "31" "32" "33" "34" "35" "36" "37" "38"
#> [39] "39" "40" "41" "42" "43" "44" "45" "46" "47" "48" "49" "50"
#>
#> $dimnames[[2]]
#> [1] "V1" "V2" "V3"
#>
#>
#> $class
#> [1] "varCovar"
#>
#> $centered
#> [1] TRUE
#>
#> $nodeSet
#> [1] "Actors"
…will tell you the information that RSiena now has added to the drink
data.
C. combination of dependent and explanatory variables
We now combine the dependent and independent variables.
The function sienaDataCreate()
creates a Siena data object
from input networks, covariates and composition change objects; the
objects that earlier were created by sienaDependent()
will
have the role of dependent variables, and similarly the other roles are
predetermined by creation by the functions coCovar()
,
varCovar()
, coDyadCovar()
,
varDyadCovar()
, and
sienaCompositionChange()
.
mydata <- sienaDataCreate(friendship, smoke1, alcohol)
You may check the result by requesting
mydata
#> Dependent variables: friendship
#> Number of observations: 3
#>
#> Nodeset Actors
#> Number of nodes 50
#>
#> Dependent variable friendship
#> Type oneMode
#> Observations 3
#> Nodeset Actors
#> Densities 0.046 0.047 0.05
#>
#> Constant covariates: smoke1
#> Changing covariates: alcohol
You should now understand how this differs from the result of:
mybehdata <- sienaDataCreate( friendship, smoke1, drinkingbeh)
If you would like to use different names, you could request this as
follows:
mydata <- sienaDataCreate(nominations = friendship, smoke1, drinking = alcohol)
Another type of dependent network is a two-mode network, which here
is also called a bipartite network.
To get advice for a first step in the use of two-mode networks, see the
script RscriptSienaBipartite.R on the Siena scripts page.
This finishes the data specification. Now we have to specify the
model.
D. model specification
Defining effects I
The data set as combined in mydata
implies a certain set
of effects that can be included in the specification of the model.
The function getEffects()
creates a dataframe of effects
with a number of extra properties for use in RSiena:
myeff <- getEffects(mydata)
mydata
is needed as an argument as the effects depend on
the number and types of covariates and dependent variables.
Before we explain the object myeff
and how we shall be
going to use it, we first produce a data description which is available
now:
Data description
print01Report(mydata, modelname = "s50_3_init")
This writes a basic report of the data to the fil
s50_3_init.txt in the current working directory. Locate and
open it!
Inspecting this is important because it serves as a check and also
contains a number of basic descriptives.
In this description you can see that the third wave for alcohol is not
used. This is because changing covariates are assumed to be constant
from one wave until immediately before the next wave, so that the values
for the last wave are ignored.
Defining effects II
Let us now consider the myeff
object, which is used to
specify the model.
It is of the class sienaEffects
, and contains the model
specification.
You can inspect the current model specification by simply
requesting…
myeff
#> effectName include fix test initialValue parm
#> 1 constant friendship rate (period 1) TRUE FALSE FALSE 4.69604 0
#> 2 constant friendship rate (period 2) TRUE FALSE FALSE 4.32885 0
#> 3 outdegree (density) TRUE FALSE FALSE -1.46770 0
#> 4 reciprocity TRUE FALSE FALSE 0.00000 0
For starting, the model specification is just a very limited default
(including rates of change, outdegree and reciprocity only). To make a
meaningful analysis, you will need to add to it.
The rows of myeff correspond to the effects.
By requesting…
names(myeff)
#> [1] "name" "effectName" "functionName" "shortName" "interaction1"
#> [6] "interaction2" "type" "basicRate" "include" "randomEffects"
#> [11] "fix" "test" "timeDummy" "initialValue" "parm"
#> [16] "functionType" "period" "rateType" "untrimmedValue" "effect1"
#> [21] "effect2" "effect3" "interactionType" "local" "setting"
#> [26] "effectFn" "statisticFn" "netType" "groupName" "group"
#> [31] "effectNumber"
You see the type of information that is stored about the effects,
i.e., the columns (characteristics) defined for the effects. If desired,
more information about these variables can be obtained from the help
files: ?getEffects
where the characteristics are
described.
Some often used variables are effectName, shortName
, type, and parameter.
The set of available effects and their most used columns can be
inspected as follows:
effectsDocumentation(myeff)
This creates an html file with the list of all effects available in
myeff
; the effects are all defined in Section 12 of the
RSiena manual, but only those that are meaningful for dataset
mydata
are used in myeff
. The include
column defines whether effects are included in the model.
myeff$include
#> [1] TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE
#> [16] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [31] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [46] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [61] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [76] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [91] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [106] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [121] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [136] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [151] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [166] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [181] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [196] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [211] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [226] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [241] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [256] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [271] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [286] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [301] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [316] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [331] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [346] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [361] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [376] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [391] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [406] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [421] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [436] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [451] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [466] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [481] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [496] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [511] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [526] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
Here the TRUE values correspond to the default model specification
which, however, is not meant as a serious model, being too limited.
There are 3 main ways to operate on myeff
:
- Using RSiena functions
includeEffects()
, `setEffects(),
etc;
- Changing
myeff
in spreadsheet form by the function
fix()
;
- Changing
myeff
directly by operating on its
elements.
The first way is most in line with the design philosophy of R, and
allows you to save scripts that also can be used when there will be new
versions of RSiena. Therefore, we suggest that for starting you only
study option 1, Adding/removing effects using
includeEffects.
The other two options are treated only for special and more difficult
occasions; and for looking ‘behind the screens’.
For identifying your effects you need the shortNames, which
can be read in the manual (section “Mathematical definition of
effects”), or obtained from the effectsDocumentation()
function mentioned above.
1. Adding/removing effects using includeEffects()
structural effects
The best way of specifying the model is by the
includeEffects()
function. This function uses short names
instead of full names. The short names are given by the
effectsDocumentation()
function mentioned above, and also
are listed in the descriptions given in Section 12 of the manual.
For illustration, let us start from scratch with a new sienaEffects
object, and add the transitive triples and 3-cycles effects.
myeff <- getEffects(mydata)
myeff <- includeEffects(myeff, transTrip, cycle3)
# To see the current model specification,
myeff
#> effectName include fix test initialValue parm
#> 1 transitive triplets TRUE FALSE FALSE 0 0
#> 2 3-cycles TRUE FALSE FALSE 0 0
#> effectName include fix test initialValue parm
#> 1 constant friendship rate (period 1) TRUE FALSE FALSE 4.69604 0
#> 2 constant friendship rate (period 2) TRUE FALSE FALSE 4.32885 0
#> 3 outdegree (density) TRUE FALSE FALSE -1.46770 0
#> 4 reciprocity TRUE FALSE FALSE 0.00000 0
#> 5 transitive triplets TRUE FALSE FALSE 0.00000 0
#> 6 3-cycles TRUE FALSE FALSE 0.00000 0
Note that we can set several effects in one go! To remove an effect,
e.g., the 3-cycle effects…
myeff <- includeEffects(myeff, cycle3, include = FALSE)
# Check again which effects now are included in the model
myeff
#> [1] effectName include fix test initialValue parm
#> <0 rows> (or 0-length row.names)
#> effectName include fix test initialValue parm
#> 1 constant friendship rate (period 1) TRUE FALSE FALSE 4.69604 0
#> 2 constant friendship rate (period 2) TRUE FALSE FALSE 4.32885 0
#> 3 outdegree (density) TRUE FALSE FALSE -1.46770 0
#> 4 reciprocity TRUE FALSE FALSE 0.00000 0
#> 5 transitive triplets TRUE FALSE FALSE 0.00000 0
creating interaction effects
As a special topic, let us show how interaction effects are
created.
A convenient method to include an interaction is offered by the
includeInteraction()
function. This can be used to interact
two or three effects (if the interactions are allowed, which depends on
their interactionType; see the manual for this).
The interaction between smoke1 ego and
reciprocity, for instance, can be defined by the
command:
myeff <- includeInteraction(myeff, egoX, recip, interaction1 = c("smoke1", ""))
myeff
This shows the interaction as an “unspecified interaction effect”;
but when printing results of the estimation the names of the interacting
effects will be mentioned.
An interaction between smoke1 ego and alcohol ego is defined by:
myeff <- includeInteraction(myeff, egoX, egoX, interaction1 = c("smoke1", "alcohol"))
Note that the keyword ‘interaction1’ used by RSiena is used for
identifying the covariate for which the ego effect is selected, and does
not refer to the interaction effect itself.
If at least one of the interacting effects requires the interaction1
parameter for it specification, then this parameter is also required for
the includeInteraction()
function.
Then the two or three interaction1 parameters must be combined using
c(); the same goes for interaction2, if that also is necessary for the
definition. As shown above for the recip effect, the interaction1 or
interaction2 parameter is “” (i.e., an empty string) for effects where
it is not needed.
accessing other characteristics of effects
A second special topic is how to access other characteristics of
effects. This can be done by the setEffect()
function.
E.g., the dense triads effects counts the number of triplets with at
least xx ties, where xx is the parameter of the effect, which can be 5
or 6 (note that 6 is the maximum number of ties in a triplet). The
default is 5. This is changed to 6 by the command:
myeff <- setEffect(myeff, denseTriads, parameter = 6)
myeff
The ‘parameter’ keyword refers to the effect parameter, described in
Section 12 of the manual.
2. Adding/removing effects using fix()
# fix calls a data editor internal to R, so we can manually edit the effects.
# fix( myeff )
# How to use fix() is presented merely for getting to know what myeff is. In practical analysis it
# is more convenient to use routine 'includeEffects' instead, as explained above. fix() may not be
# usable if you do not have tcl/tk available! Note that the top of the dataframe shows the names
# of the columns: name, effectName, etc. You can edit the 'include' column by changing the TRUE
# and FALSE values as required; when the editor is closed, the new values are stored. When you
# make an error, however, the effects object may be corrupted. Therefore, this way of adding and
# removing effects is more risky.
3. Adding/removing effects by direct manipulation of
myeff
Alternatively we can edit the dataframe directly by using R
functions. You are advised to skip this part (“3.”) at first and second
reading, and read it only if you wish to get more understanding of the
internal structure of the effects object. The commands below are used to
set “include” to TRUE or FALSE, as an alternative to using the data
editor. The “include” column with values TRUE or FALSE will always be
located at the 9th column, but transitive triplets will not always be at
the 16th row as this depends on the number of periods and variables;
further, the list of available effects changes over different versions
of RSiena.
Some examples are the following:
myeff[16, 9] <- TRUE #transitive triples
myeff[34, 9] <- TRUE #3 cycles
myeff[37, 9] <- TRUE #transitive ties
myeff[77, 9] <- TRUE #indegree popularity (sqrt)
myeff[86, 9] <- TRUE #outdegree popularity (sqrt)
myeff[93, 9] <- TRUE #indegree based activity (sqrt)
myeff[102, 9] <- TRUE #outdegree based activity (sqrt)
myeff[155, 9] <- TRUE #indegree-indegree assortativity
myeff[319, 9] <- TRUE #alcohol alter
myeff[331, 9] <- TRUE #alcohol ego
myeff[370, 9] <- TRUE #alcohol similarity
myeff[439, 9] <- TRUE #alcohol ego x alcohol alter
But in other choices of data, the effect numbers will change. This is
a reason why this is not a convenient method.
LS0tDQp0aXRsZTogIlZhcmlhYmxlRm9ybWF0Ig0KYXV0aG9yOiAnW0pvY2hlbSBUb2xzbWFdKGh0dHBzOi8vd3d3LmpvY2hlbXRvbHNtYS5ubCkgLSBSYWRib3VkIFVuaXZlcnNpdHksIHRoZSBOZXRoZXJsYW5kcycNCmJpYmxpb2dyYXBoeTogcmVmZXJlbmNlcy5iaWINCmRhdGU6ICJMYXN0IGNvbXBpbGVkIG9uIGByIGZvcm1hdChTeXMudGltZSgpLCAnJUIsICVZJylgIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6ICB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQ0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KLS0tDQoNCmBgYHtyLCBnbG9iYWxzZXR0aW5ncywgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgcmVzdWx0cz0naGlkZSd9DQpsaWJyYXJ5KGtuaXRyKQ0Kb3B0c19jaHVuayRzZXQodGlkeS5vcHRzPWxpc3Qod2lkdGguY3V0b2ZmPTEwMCksdGlkeT1UUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSxjb21tZW50ID0gIiM+IiwgY2FjaGU9VFJVRSwgY2xhc3Muc291cmNlPWMoInRlc3QiKSwgY2xhc3Mub3V0cHV0PWMoInRlc3QyIikpDQpvcHRpb25zKHdpZHRoID0gMTAwKQ0KcmdsOjpzZXR1cEtuaXRyKCkNCg0KDQpjb2xvcml6ZSA8LSBmdW5jdGlvbih4LCBjb2xvcikgew0KICBpZiAoa25pdHI6OmlzX2xhdGV4X291dHB1dCgpKSB7DQogICAgc3ByaW50ZigiXFx0ZXh0Y29sb3J7JXN9eyVzfSIsIGNvbG9yLCB4KQ0KICB9IGVsc2UgaWYgKGtuaXRyOjppc19odG1sX291dHB1dCgpKSB7DQogICAgc3ByaW50ZigiPHNwYW4gc3R5bGU9J2NvbG9yOiAlczsnPiVzPC9zcGFuPiIsIGNvbG9yLCANCiAgICAgIHgpDQogIH0gZWxzZSB4DQp9DQoNCmBgYA0KDQpgYGB7ciBrbGlwcHksIGVjaG89RkFMU0UsIGluY2x1ZGU9VFJVRX0NCmtsaXBweTo6a2xpcHB5KHBvc2l0aW9uID0gYygndG9wJywgJ3JpZ2h0JykpDQoja2xpcHB5OjprbGlwcHkoY29sb3IgPSAnZGFya3JlZCcpDQoja2xpcHB5OjprbGlwcHkodG9vbHRpcF9tZXNzYWdlID0gJ0NsaWNrIHRvIGNvcHknLCB0b29sdGlwX3N1Y2Nlc3MgPSAnRG9uZScpDQpgYGANCg0KYGBge2NzcywgZWNobz1GQUxTRX0NCnByZS50ZXN0IHsNCiAgbWF4LWhlaWdodDogMzAwcHg7DQogIG92ZXJmbG93LXk6IGF1dG87DQogIG92ZXJmbG93LXg6IGF1dG87DQogIG1hcmdpbjogMTBweDsNCn0NCg0KcHJlLnRlc3QyIHsNCiAgbWF4LWhlaWdodDogMzAwcHg7DQogIG92ZXJmbG93LXk6IGF1dG87DQogIG92ZXJmbG93LXg6IGF1dG87DQogIG1hcmdpbjogMTBweDsNCiAgYmFja2dyb3VuZC1jb2xvcjogd2hpdGUNCn0NCg0KDQpoMSwgLmgxLCBoMiwgLmgyLCBoMywgLmgzIHsNCiAgICBtYXJnaW4tdG9wOiAyNHB4Ow0KfQ0KDQoNCmBgYA0KDQotLS0tDQoNClRoaXMgd2Vic2l0ZSBjb252ZXJ0ZWQgdGhlIGZvbGxvd2luZyBvcmlnaW5hbCAuUiBzY3JpcHRzIGludG8gLnJtZCBmaWxlcy4gDQoNCi0gUnNjcmlwdDAxRGF0YUZvcm1hdC5SIA0KLSBSU2NyaXB0U05BRGVzY3JpcHRpdmVzLlIgDQotIFJzY3JpcHQwMlNpZW5hVmFyaWFibGVGb3JtYXQuUiAgDQotIFJzY3JpcHQwM1NpZW5hUnVuTW9kZWwuUiAgDQotIFJzY3JpcHQwNFNpZW5hQmVoYXZpb3VyLlINCg0KUGxlYXNlIHZpc2l0IFtHaXRIdWJdKGh0dHBzOi8vZ2l0aHViLmNvbS9zbmxhYi1ubC9yc2llbmEvdHJlZS9tYWluL2luc3Qvc2NyaXB0cykgZm9yIHRoZSBsYXRlc3QgLlIgZmlsZXMuIA0KDQotLS0tDQoNCiMjIERhdGENCkFsbCBmaWxlcyAoZGF0YSwgc2NyaXB0cywgZXRjLikgY2FuIGFsc28gYmUgZm91bmQgb24gW0dpdGh1Yl0oaHR0cHM6Ly9naXRodWIuY29tL0pvY2hlbVRvbHNtYS9Sc2llbmEtc2NyaXB0cykNCg0KLS0tLQ0KDQojIyBDb250YWN0DQpTcGVjaWZpYyBxdWVzdGlvbnMgd2l0aCByZXNwZWN0IHRvIHRoZSAucm1kIGZpbGVzIGNhbiBiZSBhZGRyZXNzZWQgdG86IFtKb2NoZW0gVG9sc21hXShtYWlsdG86ai50b2xzbWFAcnUubmwpLiAgDQoNCkZvciBxdWVzdGlvbnMgb24gUlNpZW5hIHBsZWFzZSB2aXNpdCB0aGUgZGVzaWduYXRlZCBbR2l0SHViXShodHRwczovL2dpdGh1Yi5jb20vc25sYWItbmwvcnNpZW5hKSBwYWdlLiANCg0KLS0tLSAgDQoNCiMjIEludHJvZHVjdGlvbiANCg0KTGV0IHVzIHN0YXJ0IGJ5IGxvYWRpbmcgdGhlIGRhdGENCg0KYGBge3J9DQpsaWJyYXJ5KFJTaWVuYSkNCmZyaWVuZC5kYXRhLncxIDwtIHM1MDENCmZyaWVuZC5kYXRhLncyIDwtIHM1MDINCmZyaWVuZC5kYXRhLnczIDwtIHM1MDMNCmRyaW5rIDwtIHM1MGENCnNtb2tlIDwtIHM1MHMNCmBgYA0KDQpBIG51bWJlciBvZiBvYmplY3RzIG5lZWQgdG8gYmUgY3JlYXRlZCBpbiBSLCBhcyBwcmVwYXJhdGlvbnMgdG8gbGV0dGluZyBzaWVuYTA3IGV4ZWN1dGUgdGhlIGVzdGltYXRpb24uIFRoaXMgd2lsbCBiZSBpbmRpY2F0ZWQgYnk6ICANCg0KLSAgQTogZGVwZW5kZW50IHZhcmlhYmxlczsgIA0KLSAgQjogZXhwbGFuYXRvcnkgdmFyaWFibGVzOyAgDQotICBDOiBjb21iaW5hdGlvbiBvZiBkZXBlbmRlbnQgYW5kIGV4cGxhbmF0b3J5IHZhcmlhYmxlczsgIA0KLSAgRDogbW9kZWwgc3BlY2lmaWNhdGlvbi4NCg0KLS0tICANCg0KIyMgQS4gZGVwZW5kZW50IHZhcmlhYmxlcyAgDQoNCg0KIEZpcnN0IHdlIGhhdmUgdG8gY3JlYXRlIG9iamVjdHMgZm9yIHRoZSBkZXBlbmRlbnQgdmFyaWFibGVzLg0KDQpgc2llbmFEZXBlbmRlbnQoKWAgY3JlYXRlcyBhIHNpZW5hRGVwZW5kZW50IG9iamVjdCwgaGVyZSBhIG5ldHdvcmssIGZyb20gYSBtYXRyaXggb3IgYXJyYXkgb3IgbGlzdCBvZiBzcGFyc2UgbWF0cml4IG9mIHRyaXBsZXMuIFRoaXMgb2JqZWN0IHdpbGwgaGF2ZSB0aGUgcm9sZSBvZiBhIGRlcGVuZGVudCB2YXJpYWJsZSBpbiB0aGUgYW5hbHlzaXMuICANCg0KVGhlIG5hbWUgb2YgdGhpcyBuZXR3b3JrIG9iamVjdCAoaGVyZTogKipmcmllbmRzaGlwKiopIHdpbGwgYmUgdXNlZCBpbiB0aGUgb3V0cHV0IGZpbGUuDQoNCmBgYHtyfQ0KZnJpZW5kc2hpcCA8LSBzaWVuYURlcGVuZGVudCgNCiAgYXJyYXkoIGMoIGZyaWVuZC5kYXRhLncxLCBmcmllbmQuZGF0YS53MiwgZnJpZW5kLmRhdGEudzMgKSwNCiAgICAgICAgIGRpbSA9IGMoIDUwLCA1MCwgMyApICkgKQ0KYGBgDQoNClRoZSBpbnRlZ2VycyBpbiB0aGUgYGRpbSgpYCBoZXJlIHJlZmVyIHRvIHRoZSBudW1iZXIgb2Ygbm9kZXMgKHNlbmRlcnMsIHJlY2VpdmVycykgYW5kIHRoZSBudW1iZXIgb2Ygd2F2ZXMuIFRoaXMgb2JqZWN0IGlzIGFuIGFycmF5IG9mIGRpbWVuc2lvbiA1MCB4IDUwIHggMywgcmVwcmVzZW50aW5nIHRocmVlIGFkamFjZW5jeSBtYXRyaWNlcywgd2l0aCBhIG51bWJlciBvZiBhdHRyaWJ1dGVzLiAgDQoNCllvdSBjYW4gYXNrIGZvciBhIGJyaWVmIGRlY3JpcHRpb24gb2YgdGhpcyBvYmplY3Qgc2ltcGx5IGJ5IHR5cGluZzogIA0KDQoNCg0KYGBge3J9DQpwcmludChmcmllbmRzaGlwKQ0KIyBmb3Igd2hpY2ggdGhlIHNob3J0aGFuZCBpcw0KDQpmcmllbmRzaGlwDQpgYGANCg0KTm90ZSB0aGF0IHRoaXMgaXMgYW4gb2JqZWN0IG9mIGNsYXNzICANCmBgYHtyfQ0KY2xhc3MoZnJpZW5kc2hpcCkNCmBgYA0KDQpXaXRoIHNwZWNpZmljIGF0dHJpYnV0ZXMgYW5kIG1ldGhvZHMgYXNzb2NpYXRlZCB3aXRoIGl0Lg0KWVlvdSBjYW4gZ2V0IHRoZSBkZXRhaWxlZCBpbmZvcm1hdGlvbiBieSByZXF1ZXN0aW5nOiANCg0KYGBge3J9DQpkaW0oIGZyaWVuZHNoaXAgKQ0KYXR0cmlidXRlcyggZnJpZW5kc2hpcCApDQpgYGANCg0KDQpJZiB5b3Ugb25seSBhcmUgaW50ZXJlc3RlZCBpbiB0aGUgdmFsdWUgb2Ygb25lIHBhcnRpY3VsYXIgYXR0cmlidXRlLCB5b3UgY2FuIHJlcXVlc3QgdGhpcyBieSwgZS5nLiwNCg0KYGBge3J9DQphdHRyKCBmcmllbmRzaGlwLCAidHlwZSIpDQpgYGANCg0KQW4gZXh0ZW5zaXZlIGRlc2NyaXB0aW9uIG9mIHRoZSBmcmllbmRzaGlwIGRhdGEgaXMgb2J0YWluZWQgYnkgdHlwaW5nOiANCmBgYHtyfQ0Kc3RyKGZyaWVuZHNoaXApDQpgYGANCg0KVGhlIGZ1bmN0aW9uIGBzaWVuYURlcGVuZGVudCgpYCBjYW4gYWxzbyBiZSB1c2VkIHRvIGNyZWF0ZSBhIGJlaGF2aW9yIHZhcmlhYmxlIG9iamVjdCB3aXRoIHRoZSBleHRyYSBhcmd1bWVudCAqKmB0eXBlID0gImJlaGF2aW9yImAqKi4gIA0KDQpOb24tbWVudGlvbmVkIGF0dHJpYnV0ZXMgZ2V0IHRoZSBkZWZhdWx0IHZhbHVlLCBhbmQgaW4gdGhpcyBjYXNlIG9uZU1vZGUgaXMgdGhlIGRlZmF1bHQ7IHNlZSBiZWxvdy4gIA0KDQpUaGUgYGRyaW5rYCBkYXRhIChjcmVhdGVkIGluIFJzY3JpcHREYXRhRm9ybWF0LlIgKSBpcyBtYWRlIGF2YWlsYWJsZSBhcyBhIGRlcGVuZGVudCBiZWhhdmlvciB2YXJpYWJsZSBieSB0aGUgZnVuY3Rpb246IA0KDQpgYGB7cn0NCmRyaW5raW5nYmVoIDwtIHNpZW5hRGVwZW5kZW50KCBkcmluaywgdHlwZSA9ICJiZWhhdmlvciIgKQ0KDQojIHRoZSBjbGFzcywgY2xhc3MoZHJpbmtpbmdiZWgpLCBpcyBzdGlsbCBzaWVuYURlcGVuZGVudC4NCmBgYA0KDQpOb3RlOiBvbmx5IHVzZSB0aGUgdmFyaWFibGUgaW4gT05FIHJvbGUgaW4gYSBnaXZlbiBtb2RlbDogYmVoYXZpb3IgdmFyaWFibGUgb3IgY2hhbmdpbmcgY292YXJpYXRlISAgDQoNClRoZSBvcHRpb25zIGF2YWlsYWJsZSBmb3IgZGVmaW5pbmcgYSBzaWVuYURlcGVuZGVudCBvYmplY3QgYXJlIGRpc3BsYXllZCBieSB0eXBpbmc6ICBgP3NpZW5hRGVwZW5kZW50YC4gDQoNClRoaXMgc2hvd3MgdGhhdCBuZXh0IHRvIG9uZS1tb2RlICh1bmlwYXJ0aXRlKSBhbmQgYmVoYXZpb3IgZGVwZW5kZW50IHZhcmlhYmxlcywgYWxzbyB0d28tbW9kZSAoYmlwYXJ0aXRlKSBkZXBlbmRlbnQgdmFyaWFibGVzIGFyZSBwb3NzaWJsZS4gIA0KWW91IGNhbiBpbmZlciB0aGF0IG9uZU1vZGUgaXMgdGhlIGRlZmF1bHQgdHlwZSBmcm9tIHRoZSBmYWN0IHRoYXQgaXQgaXMgbWVudGlvbmVkIGZpcnN0LiAgDQoNCg0KVG8gY3JlYXRlIGJpcGFydGl0ZSBuZXR3b3JrIG9iamVjdHMgeW91IG5lZWQgdHdvIG5vZGUgc2V0cyBhbmQgbXVzdCBjcmVhdGUgdGhlIG5vZGUgc2V0cyB0b28uDQoNClBsZWFzZSBTZWUgYD9zaWVuYU5vZGVTZXRgIHdoZXJlIHRoZSBFeGFtcGxlcyBzZWN0aW9uIHNob3dzIGFuIGV4YW1wbGUgb2YgdGhlIHN5bnRheC4NCg0KLS0tICAgDQoNCiMjIEIuIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyAgDQoNClNlY29uZCB3ZSBjb25zdHJ1Y3Qgb2JqZWN0cyBmb3IgdGhlIGV4cGxhbmF0b3J5IChpbmRlcGVuZGVudCkgdmFyaWFibGVzLiAgDQpGcm9tIHRoZSBoZWxwIHJlcXVlc3Q6IGA/c2llbmFEYXRhQ3JlYXRlYA0KIA0KV2Ugc2VlIHRoYXQgdGhlc2UgY2FuIGJlIG9mIGZpdmUga2luZHM6ICANCg0KMS4gYGNvQ292YXJgOiBDb25zdGFudCBhY3RvciBjb3ZhcmlhdGVzICANCjIuIGB2YXJDb3ZhcmA6IFRpbWUtdmFyeWluZyBhY3RvciBjb3ZhcmlhdGVzICANCjMuIGBjb0R5YWRDb3ZhcmA6IENvbnN0YW50IGR5YWRpYyBjb3ZhcmlhdGVzICANCjQuIGB2YXJEeWFkQ292YXJgOiBUaW1lLXZhcnlpbmcgZHlhZGljIGNvdmFyaWF0ZXMgIA0KNS4gYGNvbXBvc2l0aW9uQ2hhbmdlYCBDb21wb3NpdGlvbiBjaGFuZ2UgaW5kaWNhdG9ycyAgDQoNCg0KDQpZb3UgY2FuIGdldCBoZWxwIGFib3V0IHRoaXMgYnkgdGhlIGZvbGxvd2luZyByZXF1ZXN0czogIA0KDQoqIGA/Y29Db3ZhcmAgIA0KKiBgP3ZhckNvdmFyYCAgDQoqIGA/Y29EeWFkQ292YXJgICANCiogYD92YXJEeWFkQ292YXJgICANCiogYD9zaWVuYUNvbXBvc2l0aW9uQ2hhbmdlYCAgDQoNCg0KVGhlIHZhcmlhYmxlcyBhdmFpbGFibGUgZm9yIHRoaXMgZGF0YSBzZXQgYWxsIGFyZSBjaGFuZ2luZyBhY3RvciBjb3ZhcmlhdGVzLiAgDQpGb3IgaWxsdXN0cmF0aXZlIHB1cnBvc2VzLCB3ZSB1c2Ugc21va2luZyBhcyBvYnNlcnZlZCBhdCB0aGUgZmlyc3Qgd2F2ZSBhcyBhIGNvbnN0YW50IGNvdmFyaWF0ZToNCg0KDQpgYGB7cn0NCnNtb2tlMSA8LSBjb0NvdmFyKCBzbW9rZVsgLCAxIF0gKQ0KYGBgDQoNClRoaXMgc2VsZWN0cyB0aGUgZmlyc3QgY29sdW1uIG9mIHNtb2tlLCB3aGljaCBjb250YWlucyB0aGUgZmlyc3Qgd2F2ZSBvYnNlcnZhdGlvbnMsIGFuZCBtYWtlcyBpdCBhdmFpbGFibGUgYXMgYSBjb25zdGFudCBjb3ZhcmlhdGUuIFRoaXMgaXMgdGhlIHBhdHRlcm4gZm9yIGZvciBldmV5IGNvdmFyaWF0ZSBmaWxlLCBlLmcuIGBBdHRyMSA8LSBjb0NvdmFyKCBDb3ZhcmlhdGUxIClgIHdoZXJlIENvdmFyaWF0ZTEgaXMgYSBtYXRyaXggd2l0aCBgZGltKENvdmFyaWF0ZTEpYCBlcXVhbCB0byBuIHggMS4gIA0KDQpOb3RlLCBpZiBDb3ZhcmlhdGVzIGlzIGEgbWF0cml4IHdpdGggYGRpbShDb3ZhcmlhdGVzKWAgZXF1YWwgdG8gbiB4IHAgeW91IGNhbiBjcmVhdGUgY29uc3RhbnQgY292YXJpYXRlcyB0aHJvdWdoOiAgDQoNCi0gYEF0dHIxIDwtIGNvQ292YXIoQ292YXJpYXRlc1ssMV0pYCAgDQotIC4uLiAgDQotIGBBdHRycCA8LSBjb0NvdmFyKENvdmFyaWF0ZXNbLHBdKWAgIA0KDQogV2UgdXNlIHRoZSBkcmlua2luZyBkYXRhIGFzIGEgY2hhbmdpbmcgY292YXJpYXRlLiBUaGUgZnVuY3Rpb24gYHZhckNvdmFyKCkgY3JlYXRlcyBhIGNoYW5naW5nIGNvdmFyaWF0ZSBvYmplY3QgZnJvbSBhIG1hdHJpeDsgdGhlIG5hbWUgY29tZXMgZnJvbSAndmFyeWluZyBjb3ZhcmlhdGUnLiAgDQogDQpgYGB7cn0NCmFsY29ob2wgPC0gdmFyQ292YXIoIGRyaW5rICkNCmBgYA0KDQpZb3UgbmVlZCBhdCBsZWFzdCB0aHJlZSB3YXZlcyBpbiB0aGUgZGF0YSBzZXQgdG8gZGVmaW5lIGEgdmFyeWluZyBjb3ZhcmlhdGUgYnkgdGhlIGZ1bmN0aW9uIGB2YXJDb3ZhcigpYDsgdGhlIHJlYXNvbiBpcyB0aGF0IHRoZSBwcmV2aW91cyB3YXZlIGlzIHVzZWQgYXMgYSBwcmVkaWN0b3Igb2YgdGhlIG5leHQgd2F2ZS4gIA0KDQpUaGUgY29tbWFuZC4uLg0KDQpgYGB7cn0NCmF0dHJpYnV0ZXMoIGFsY29ob2wgKQ0KYGBgDQoNCi4uLndpbGwgdGVsbCB5b3UgdGhlIGluZm9ybWF0aW9uIHRoYXQgUlNpZW5hIG5vdyBoYXMgYWRkZWQgdG8gdGhlIGRyaW5rIGRhdGEuICANCg0KLS0tICANCg0KIyMgQy4gY29tYmluYXRpb24gb2YgZGVwZW5kZW50IGFuZCBleHBsYW5hdG9yeSB2YXJpYWJsZXMgIA0KDQpXZSBub3cgY29tYmluZSB0aGUgZGVwZW5kZW50IGFuZCBpbmRlcGVuZGVudCB2YXJpYWJsZXMuICANClRoZSBmdW5jdGlvbiBgc2llbmFEYXRhQ3JlYXRlKClgIGNyZWF0ZXMgYSBTaWVuYSBkYXRhIG9iamVjdCBmcm9tIGlucHV0IG5ldHdvcmtzLCBjb3ZhcmlhdGVzIGFuZCBjb21wb3NpdGlvbiBjaGFuZ2Ugb2JqZWN0czsgdGhlIG9iamVjdHMgdGhhdCBlYXJsaWVyIHdlcmUgY3JlYXRlZCBieSBgc2llbmFEZXBlbmRlbnQoKWAgd2lsbCBoYXZlIHRoZSByb2xlIG9mIGRlcGVuZGVudCB2YXJpYWJsZXMsIGFuZCBzaW1pbGFybHkgdGhlIG90aGVyIHJvbGVzIGFyZSBwcmVkZXRlcm1pbmVkIGJ5IGNyZWF0aW9uIGJ5IHRoZSBmdW5jdGlvbnMgYGNvQ292YXIoKWAsIGB2YXJDb3ZhcigpYCwgYGNvRHlhZENvdmFyKClgLCBgdmFyRHlhZENvdmFyKClgLCBhbmQgYHNpZW5hQ29tcG9zaXRpb25DaGFuZ2UoKWAuDQoNCmBgYHtyfQ0KbXlkYXRhIDwtIHNpZW5hRGF0YUNyZWF0ZSggZnJpZW5kc2hpcCwgc21va2UxLCBhbGNvaG9sICkNCmBgYA0KDQpZb3UgbWF5IGNoZWNrIHRoZSByZXN1bHQgYnkgcmVxdWVzdGluZw0KYGBge3J9DQpteWRhdGENCmBgYA0KDQpZb3Ugc2hvdWxkIG5vdyB1bmRlcnN0YW5kIGhvdyB0aGlzIGRpZmZlcnMgZnJvbSB0aGUgcmVzdWx0IG9mOiANCg0KYG15YmVoZGF0YSA8LSBzaWVuYURhdGFDcmVhdGUoIGZyaWVuZHNoaXAsIHNtb2tlMSwgZHJpbmtpbmdiZWgpYCANCg0KSWYgeW91IHdvdWxkIGxpa2UgdG8gdXNlIGRpZmZlcmVudCBuYW1lcywgeW91IGNvdWxkIHJlcXVlc3QgdGhpcyBhcyBmb2xsb3dzOg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCm15ZGF0YSA8LSBzaWVuYURhdGFDcmVhdGUobm9taW5hdGlvbnMgPSBmcmllbmRzaGlwLCBzbW9rZTEsIGRyaW5raW5nID0gYWxjb2hvbCkNCmBgYA0KDQpBbm90aGVyIHR5cGUgb2YgZGVwZW5kZW50IG5ldHdvcmsgaXMgYSB0d28tbW9kZSBuZXR3b3JrLCB3aGljaCBoZXJlIGlzIGFsc28gY2FsbGVkIGEgYmlwYXJ0aXRlIG5ldHdvcmsuICANClRvIGdldCBhZHZpY2UgZm9yIGEgZmlyc3Qgc3RlcCBpbiB0aGUgdXNlIG9mIHR3by1tb2RlIG5ldHdvcmtzLCBzZWUgdGhlIHNjcmlwdCBSc2NyaXB0U2llbmFCaXBhcnRpdGUuUiBvbiB0aGUgU2llbmEgc2NyaXB0cyBwYWdlLiAgDQoNClRoaXMgZmluaXNoZXMgdGhlIGRhdGEgc3BlY2lmaWNhdGlvbi4gTm93IHdlIGhhdmUgdG8gc3BlY2lmeSB0aGUgbW9kZWwuICANCg0KIyMgRC4gbW9kZWwgc3BlY2lmaWNhdGlvbiAgDQoNCiMjIyBEZWZpbmluZyBlZmZlY3RzIEkgDQoNClRoZSBkYXRhIHNldCBhcyBjb21iaW5lZCBpbiBgbXlkYXRhYCBpbXBsaWVzIGEgY2VydGFpbiBzZXQgb2YgZWZmZWN0cyB0aGF0IGNhbiBiZSBpbmNsdWRlZCBpbiB0aGUgc3BlY2lmaWNhdGlvbiBvZiB0aGUgbW9kZWwuICANClRoZSBmdW5jdGlvbiBgZ2V0RWZmZWN0cygpYCBjcmVhdGVzIGEgZGF0YWZyYW1lIG9mIGVmZmVjdHMgd2l0aCBhIG51bWJlciBvZiBleHRyYSBwcm9wZXJ0aWVzIGZvciB1c2UgaW4gUlNpZW5hOg0KDQpgYGB7cn0NCm15ZWZmIDwtIGdldEVmZmVjdHMoIG15ZGF0YSApDQpgYGANCg0KYG15ZGF0YWAgaXMgbmVlZGVkIGFzIGFuIGFyZ3VtZW50IGFzIHRoZSBlZmZlY3RzIGRlcGVuZCBvbiB0aGUgbnVtYmVyIGFuZCB0eXBlcyBvZiBjb3ZhcmlhdGVzIGFuZCBkZXBlbmRlbnQgdmFyaWFibGVzLiAgDQpCZWZvcmUgd2UgZXhwbGFpbiB0aGUgb2JqZWN0IGBteWVmZmAgYW5kIGhvdyB3ZSBzaGFsbCBiZSBnb2luZyB0byB1c2UgaXQsIHdlIGZpcnN0IHByb2R1Y2UgYSBkYXRhIGRlc2NyaXB0aW9uIHdoaWNoIGlzIGF2YWlsYWJsZSBub3c6DQoNCiMjIyBEYXRhIGRlc2NyaXB0aW9uDQpgYGB7cn0NCnByaW50MDFSZXBvcnQoIG15ZGF0YSwgbW9kZWxuYW1lID0gJ3M1MF8zX2luaXQnICkNCmBgYA0KDQpUaGlzIHdyaXRlcyBhIGJhc2ljIHJlcG9ydCBvZiB0aGUgZGF0YSB0byB0aGUgZmlsICpzNTBfM19pbml0LnR4dCogIGluIHRoZSBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5LiBMb2NhdGUgYW5kIG9wZW4gaXQhICANCkluc3BlY3RpbmcgdGhpcyBpcyBpbXBvcnRhbnQgYmVjYXVzZSBpdCBzZXJ2ZXMgYXMgYSBjaGVjayBhbmQgYWxzbyBjb250YWlucyBhIG51bWJlciBvZiBiYXNpYyBkZXNjcmlwdGl2ZXMuICANCkluIHRoaXMgZGVzY3JpcHRpb24geW91IGNhbiBzZWUgdGhhdCB0aGUgdGhpcmQgd2F2ZSBmb3IgYWxjb2hvbCBpcyBub3QgdXNlZC4gVGhpcyBpcyBiZWNhdXNlIGNoYW5naW5nIGNvdmFyaWF0ZXMgYXJlIGFzc3VtZWQgdG8gYmUgY29uc3RhbnQgZnJvbSBvbmUgd2F2ZSB1bnRpbCBpbW1lZGlhdGVseSBiZWZvcmUgdGhlIG5leHQgd2F2ZSwgc28gdGhhdCB0aGUgdmFsdWVzIGZvciB0aGUgbGFzdCB3YXZlIGFyZSBpZ25vcmVkLiAgDQoNCiMjIyBEZWZpbmluZyBlZmZlY3RzIElJICAgDQoNCkxldCB1cyBub3cgY29uc2lkZXIgdGhlIGBteWVmZmAgb2JqZWN0LCB3aGljaCBpcyB1c2VkIHRvIHNwZWNpZnkgdGhlIG1vZGVsLiAgDQpJdCBpcyBvZiB0aGUgY2xhc3MgYHNpZW5hRWZmZWN0c2AsIGFuZCBjb250YWlucyB0aGUgbW9kZWwgc3BlY2lmaWNhdGlvbi4gIA0KWW91IGNhbiBpbnNwZWN0IHRoZSBjdXJyZW50IG1vZGVsIHNwZWNpZmljYXRpb24gYnkgc2ltcGx5IHJlcXVlc3RpbmcuLi4gDQoNCmBgYHtyfQ0KbXllZmYNCmBgYA0KDQpGb3Igc3RhcnRpbmcsIHRoZSBtb2RlbCBzcGVjaWZpY2F0aW9uIGlzIGp1c3QgYSB2ZXJ5IGxpbWl0ZWQgZGVmYXVsdCAoaW5jbHVkaW5nIHJhdGVzIG9mIGNoYW5nZSwgb3V0ZGVncmVlIGFuZCByZWNpcHJvY2l0eSBvbmx5KS4gIFRvIG1ha2UgYSBtZWFuaW5nZnVsIGFuYWx5c2lzLCB5b3Ugd2lsbCBuZWVkIHRvIGFkZCB0byBpdC4gIA0KDQpUaGUgcm93cyBvZiBteWVmZiBjb3JyZXNwb25kIHRvIHRoZSBlZmZlY3RzLiAgDQpCeSByZXF1ZXN0aW5nLi4uDQpgYGB7cn0NCm5hbWVzKG15ZWZmKQ0KYGBgDQoNCllvdSBzZWUgdGhlIHR5cGUgb2YgaW5mb3JtYXRpb24gdGhhdCBpcyBzdG9yZWQgYWJvdXQgdGhlIGVmZmVjdHMsIGkuZS4sIHRoZSBjb2x1bW5zIChjaGFyYWN0ZXJpc3RpY3MpIGRlZmluZWQgZm9yIHRoZSBlZmZlY3RzLiBJZiBkZXNpcmVkLCBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHRoZXNlIHZhcmlhYmxlcyBjYW4gYmUgb2J0YWluZWQgZnJvbSB0aGUgaGVscCBmaWxlczogYD9nZXRFZmZlY3RzYCB3aGVyZSB0aGUgY2hhcmFjdGVyaXN0aWNzIGFyZSBkZXNjcmliZWQuICANCg0KU29tZSBvZnRlbiB1c2VkIHZhcmlhYmxlcyBhcmUgKmVmZmVjdE5hbWUqLCAqc2hvcnROYW1lKiAsICp0eXBlKiwgYW5kICpwYXJhbWV0ZXIqLiAgDQpUaGUgc2V0IG9mIGF2YWlsYWJsZSBlZmZlY3RzIGFuZCB0aGVpciBtb3N0IHVzZWQgY29sdW1ucyBjYW4gYmUgaW5zcGVjdGVkIGFzIGZvbGxvd3M6DQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KZWZmZWN0c0RvY3VtZW50YXRpb24obXllZmYpDQpgYGANCg0KVGhpcyBjcmVhdGVzIGFuIGh0bWwgZmlsZSB3aXRoIHRoZSBsaXN0IG9mIGFsbCBlZmZlY3RzIGF2YWlsYWJsZSBpbiBgbXllZmZgOyB0aGUgZWZmZWN0cyBhcmUgYWxsIGRlZmluZWQgaW4gU2VjdGlvbiAxMiBvZiB0aGUgUlNpZW5hIG1hbnVhbCwgYnV0IG9ubHkgdGhvc2UgdGhhdCBhcmUgbWVhbmluZ2Z1bCBmb3IgZGF0YXNldCBgbXlkYXRhYCBhcmUgdXNlZCBpbiBgbXllZmZgLiBUaGUgKmluY2x1ZGUqIGNvbHVtbiBkZWZpbmVzIHdoZXRoZXIgZWZmZWN0cyBhcmUgaW5jbHVkZWQgaW4gdGhlIG1vZGVsLiAgDQoNCmBgYHtyfQ0KbXllZmYkaW5jbHVkZQ0KYGBgDQoNCkhlcmUgdGhlIFRSVUUgdmFsdWVzIGNvcnJlc3BvbmQgdG8gdGhlIGRlZmF1bHQgbW9kZWwgc3BlY2lmaWNhdGlvbiB3aGljaCwgaG93ZXZlciwgaXMgbm90IG1lYW50IGFzIGEgc2VyaW91cyBtb2RlbCwgYmVpbmcgdG9vIGxpbWl0ZWQuIFRoZXJlIGFyZSAzIG1haW4gd2F5cyB0byBvcGVyYXRlIG9uIGBteWVmZmA6ICANCg0KMS4gVXNpbmcgUlNpZW5hIGZ1bmN0aW9ucyBgaW5jbHVkZUVmZmVjdHMoKWAsIGBzZXRFZmZlY3RzKCksIGV0YzsgIA0KMi4gQ2hhbmdpbmcgYG15ZWZmYCBpbiBzcHJlYWRzaGVldCBmb3JtIGJ5IHRoZSBmdW5jdGlvbiBgZml4KClgOyAgDQozLiBDaGFuZ2luZyBgbXllZmZgIGRpcmVjdGx5IGJ5IG9wZXJhdGluZyBvbiBpdHMgZWxlbWVudHMuICANCg0KDQpUaGUgZmlyc3Qgd2F5IGlzIG1vc3QgaW4gbGluZSB3aXRoIHRoZSBkZXNpZ24gcGhpbG9zb3BoeSBvZiBSLCBhbmQgYWxsb3dzIHlvdSB0byBzYXZlIHNjcmlwdHMgdGhhdCBhbHNvIGNhbiBiZSB1c2VkIHdoZW4gdGhlcmUgd2lsbCBiZSBuZXcgdmVyc2lvbnMgb2YgUlNpZW5hLiBUaGVyZWZvcmUsIHdlIHN1Z2dlc3QgdGhhdCBmb3Igc3RhcnRpbmcgeW91IG9ubHkgc3R1ZHkgb3B0aW9uIDEsICpBZGRpbmcvcmVtb3ZpbmcgZWZmZWN0cyB1c2luZyBpbmNsdWRlRWZmZWN0cyouICANClRoZSBvdGhlciB0d28gb3B0aW9ucyBhcmUgdHJlYXRlZCBvbmx5IGZvciBzcGVjaWFsIGFuZCBtb3JlIGRpZmZpY3VsdCBvY2Nhc2lvbnM7IGFuZCBmb3IgbG9va2luZyAnYmVoaW5kIHRoZSBzY3JlZW5zJy4gIA0KDQpGb3IgaWRlbnRpZnlpbmcgeW91ciBlZmZlY3RzIHlvdSBuZWVkIHRoZSAqc2hvcnROYW1lKnMsIHdoaWNoIGNhbiBiZSByZWFkIGluIHRoZSBtYW51YWwgKHNlY3Rpb24gIk1hdGhlbWF0aWNhbCBkZWZpbml0aW9uIG9mIGVmZmVjdHMiKSwgb3Igb2J0YWluZWQgZnJvbSB0aGUgYGVmZmVjdHNEb2N1bWVudGF0aW9uKClgIGZ1bmN0aW9uIG1lbnRpb25lZCBhYm92ZS4gIA0KDQojIyMgMS4gQWRkaW5nL3JlbW92aW5nIGVmZmVjdHMgdXNpbmcgYGluY2x1ZGVFZmZlY3RzKClgICANCg0KIyMjIyAqKnN0cnVjdHVyYWwgZWZmZWN0cyoqICANCg0KVGhlIGJlc3Qgd2F5IG9mIHNwZWNpZnlpbmcgdGhlIG1vZGVsIGlzIGJ5IHRoZSBgaW5jbHVkZUVmZmVjdHMoKWAgZnVuY3Rpb24uIFRoaXMgZnVuY3Rpb24gdXNlcyBzaG9ydCBuYW1lcyBpbnN0ZWFkIG9mIGZ1bGwgbmFtZXMuIFRoZSBzaG9ydCBuYW1lcyBhcmUgZ2l2ZW4gYnkgdGhlIGBlZmZlY3RzRG9jdW1lbnRhdGlvbigpYCBmdW5jdGlvbiBtZW50aW9uZWQgYWJvdmUsIGFuZCBhbHNvIGFyZSBsaXN0ZWQgaW4gdGhlIGRlc2NyaXB0aW9ucyBnaXZlbiBpbiBTZWN0aW9uIDEyIG9mIHRoZSBtYW51YWwuICANCg0KRm9yIGlsbHVzdHJhdGlvbiwgbGV0IHVzIHN0YXJ0IGZyb20gc2NyYXRjaCB3aXRoIGEgbmV3IHNpZW5hRWZmZWN0cyBvYmplY3QsIGFuZCBhZGQgdGhlIHRyYW5zaXRpdmUgdHJpcGxlcyBhbmQgMy1jeWNsZXMgZWZmZWN0cy4gDQoNCg0KYGBge3IsIHJlc3VsdHM9J2hvbGQnfQ0KbXllZmYgPC0gZ2V0RWZmZWN0cyggbXlkYXRhICkNCm15ZWZmIDwtIGluY2x1ZGVFZmZlY3RzKCBteWVmZiwgdHJhbnNUcmlwLCBjeWNsZTMgKQ0KIyBUbyBzZWUgdGhlIGN1cnJlbnQgbW9kZWwgc3BlY2lmaWNhdGlvbiwNCm15ZWZmDQpgYGANCg0KDQpOb3RlIHRoYXQgd2UgY2FuIHNldCBzZXZlcmFsIGVmZmVjdHMgaW4gb25lIGdvISBUbyByZW1vdmUgYW4gZWZmZWN0LCBlLmcuLCB0aGUgMy1jeWNsZSBlZmZlY3RzLi4uDQoNCmBgYHtyLCByZXN1bHRzPSdob2xkJ30NCm15ZWZmIDwtIGluY2x1ZGVFZmZlY3RzKCBteWVmZiwgY3ljbGUzLCBpbmNsdWRlPUZBTFNFICkNCg0KIyBDaGVjayBhZ2FpbiB3aGljaCBlZmZlY3RzIG5vdyBhcmUgaW5jbHVkZWQgaW4gdGhlIG1vZGVsDQpteWVmZg0KDQpgYGANCg0KIyMjIyAqKmNvdmFyaWF0ZSByZWxhdGVkIGVmZmVjdHMqKiAgDQoNCkxldCB1cyBkZW1vbnN0cmF0ZSBob3cgdG8gYWRkL3JlbW92ZSBjb3ZhcmlhdGUgcmVsYXRlZCBlZmZlY3RzLiAgDQpUaGUgc2hvcnQgbmFtZXMgZG8gbm90IGRpZmZlcmVudGlhdGUgYmV0d2VlbiB0aGUgY292YXJpYXRlczogIGUuZy4sIHRoZSBlZmZlY3RzICphbGNvaG9sIGVnbyogYW5kICpzbW9rZTEgZWdvKiBib3RoIGhhdmUgc2hvcnROYW1lICplZ29YKiwgYW5kIHRoZSBjb21tYW5kIGBteWVmZiA8LSBpbmNsdWRlRWZmZWN0cyhteWVmZiwgZWdvWClgIHJlc3VsdHMgaW4gYSBtZXNzYWdlIHRoYXQgZG9lcyBub3QgKGxpa2UgdGhlIGVhcmxpZXIgb25lKSBjb25maXJtIHRoZSBuZXdseSBpbmNsdWRlZCBlZmZlY3QuICANCg0KVGhlIGNvdmFyaWF0ZXMgYXJlIGluZGljYXRlZCBieSB0aGUgdmFyaWFibGUgKmludGVyYWN0aW9uMSogaW4gdGhlIHNpZW5hRWZmZWN0cyBvYmplY3QsIGxpc3RlZCBhcyAqaW50ZXIxKiBpbiB0aGUgcmVzdWx0IG9mIGBlZmZlY3RzRG9jdW1lbnRhdGlvbigpYCwgYW5kIHRoaXMgaGFzIHRvIGJlIG1lbnRpb25lZCB0byBpbmNsdWRlIHRoZXNlIGVmZmVjdHM6DQoNCmBgYHtyLCByZXN1bHRzPSdob2xkJ30NCm15ZWZmIDwtIGluY2x1ZGVFZmZlY3RzKCBteWVmZiwgZWdvWCwgYWx0WCwgZWdvWGFsdFgsIGludGVyYWN0aW9uMSA9ICJhbGNvaG9sIiApDQpteWVmZiA8LSBpbmNsdWRlRWZmZWN0cyggbXllZmYsIHNpbVgsIGludGVyYWN0aW9uMSA9ICJzbW9rZTEiICkNCg0KIyBXZSBjaGVjayB0aGUgcmVzdWx0cyBhZ2FpbjoNCm15ZWZmDQpgYGANCg0KDQpCeSBsb29raW5nIGF0IHRoZSBoZWxwIG9mZmVyZWQgYnkgYD9pbmNsdWRlRWZmZWN0c2AuICANCg0KeW91IGNhbiBzZWUgaG93IHRvIGluY2x1ZGUgZW5kb3dtZW50IGFuZCBjcmVhdGlvbiBlZmZlY3RzLiBFZmZlY3RzIHRoYXQgZGVwZW5kIG9uIG90aGVyIHZhcmlhYmxlcywgc3VjaCBhcyBlZ29YLCBhbHRYLCBldGMuIGFib3ZlLCBuZWVkIHRoZSBzcGVjaWZpY2F0aW9uIG9mIHRoZXNlIHZhcmlhYmxlcyB0byBkZWZpbmUgdGhlbS4gVGhpcyBpcyBkb25lIGJ5IHRoZSAqaW50ZXJhY3Rpb24xKiBwYXJhbWV0ZXIgd2hlbiBvbmx5IG9uZSB2YXJpYWJsZSBuYW1lIGlzIG5lZWRlZCwgYW5kIGJ5ICppbnRlcmFjdGlvbjIqIGlmIHRoZXJlIGlzIGEgc2Vjb25kIHZhcmlhYmxlIGludm9sdmVkLCBzdWNoIGFzIEFsdHNBdkFsdCAoc2VlIHRoZSBtYW51YWwpLiAgDQpBbHRob3VnaCB0aGUgbmFtZXMgb2YgdGhlc2UgcGFyYW1ldGVycyBhcmUgKmludGVyYWN0aW9uMSogYW5kICppbnRlcmFjdGlvbjIqLCB0aGlzIGRvZXMgbm90IHJlZmVyIHRvIHRoZSBjb21tb24gY29uY2VwdCBvZiBpbnRlcmFjdGlvbiBpbiBzdGF0aXN0aWNhbCBtb2RlbGxpbmchDQoNCg0KIyMjIyAqKmNyZWF0aW5nIGludGVyYWN0aW9uIGVmZmVjdHMqKiANCg0KDQpBcyBhIHNwZWNpYWwgdG9waWMsIGxldCB1cyBzaG93IGhvdyBpbnRlcmFjdGlvbiBlZmZlY3RzIGFyZSBjcmVhdGVkLiAgDQoNCkEgY29udmVuaWVudCBtZXRob2QgdG8gaW5jbHVkZSBhbiBpbnRlcmFjdGlvbiBpcyBvZmZlcmVkIGJ5IHRoZSBgaW5jbHVkZUludGVyYWN0aW9uKClgIGZ1bmN0aW9uLiBUaGlzIGNhbiBiZSB1c2VkIHRvIGludGVyYWN0IHR3byBvciB0aHJlZSBlZmZlY3RzIChpZiB0aGUgaW50ZXJhY3Rpb25zIGFyZSBhbGxvd2VkLCB3aGljaCBkZXBlbmRzIG9uIHRoZWlyIGludGVyYWN0aW9uVHlwZTsgc2VlIHRoZSBtYW51YWwgZm9yIHRoaXMpLiAgDQoNClRoZSBpbnRlcmFjdGlvbiBiZXR3ZWVuICoqc21va2UxIGVnbyoqIGFuZCAqKnJlY2lwcm9jaXR5KiosIGZvciBpbnN0YW5jZSwgY2FuIGJlIGRlZmluZWQgYnkgdGhlIGNvbW1hbmQ6IA0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCm15ZWZmIDwtIGluY2x1ZGVJbnRlcmFjdGlvbiggbXllZmYsIGVnb1gsIHJlY2lwLCBpbnRlcmFjdGlvbjEgPSBjKCJzbW9rZTEiLCIiKSApDQpteWVmZg0KYGBgDQoNClRoaXMgc2hvd3MgdGhlIGludGVyYWN0aW9uIGFzIGFuICJ1bnNwZWNpZmllZCBpbnRlcmFjdGlvbiBlZmZlY3QiOyBidXQgd2hlbiBwcmludGluZyByZXN1bHRzIG9mIHRoZSBlc3RpbWF0aW9uIHRoZSBuYW1lcyBvZiB0aGUgaW50ZXJhY3RpbmcgZWZmZWN0cyB3aWxsIGJlIG1lbnRpb25lZC4gDQoNCkFuIGludGVyYWN0aW9uIGJldHdlZW4gc21va2UxIGVnbyBhbmQgYWxjb2hvbCBlZ28gaXMgZGVmaW5lZCBieTogIA0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCm15ZWZmIDwtIGluY2x1ZGVJbnRlcmFjdGlvbiggbXllZmYsIGVnb1gsIGVnb1gsIGludGVyYWN0aW9uMSA9IGMoICJzbW9rZTEiLCAiYWxjb2hvbCIgKSApDQpgYGANCg0KDQpOb3RlIHRoYXQgdGhlIGtleXdvcmQgJ2ludGVyYWN0aW9uMScgdXNlZCBieSBSU2llbmEgaXMgdXNlZCBmb3IgaWRlbnRpZnlpbmcgdGhlIGNvdmFyaWF0ZSBmb3Igd2hpY2ggdGhlIGVnbyBlZmZlY3QgaXMgc2VsZWN0ZWQsIGFuZCBkb2VzIG5vdCByZWZlciB0byB0aGUgaW50ZXJhY3Rpb24gZWZmZWN0IGl0c2VsZi4gIA0KSWYgYXQgbGVhc3Qgb25lIG9mIHRoZSBpbnRlcmFjdGluZyBlZmZlY3RzIHJlcXVpcmVzIHRoZSBpbnRlcmFjdGlvbjEgcGFyYW1ldGVyIGZvciBpdCBzcGVjaWZpY2F0aW9uLCB0aGVuIHRoaXMgcGFyYW1ldGVyIGlzIGFsc28gcmVxdWlyZWQgZm9yIHRoZSBgaW5jbHVkZUludGVyYWN0aW9uKClgIGZ1bmN0aW9uLiAgDQoNClRoZW4gdGhlIHR3byBvciB0aHJlZSBpbnRlcmFjdGlvbjEgcGFyYW1ldGVycyBtdXN0IGJlIGNvbWJpbmVkIHVzaW5nIGMoKTsgdGhlIHNhbWUgZ29lcyBmb3IgaW50ZXJhY3Rpb24yLCBpZiB0aGF0IGFsc28gaXMgbmVjZXNzYXJ5IGZvciB0aGUgZGVmaW5pdGlvbi4gQXMgc2hvd24gYWJvdmUgZm9yIHRoZSByZWNpcCBlZmZlY3QsIHRoZSBpbnRlcmFjdGlvbjEgb3IgaW50ZXJhY3Rpb24yIHBhcmFtZXRlciBpcyAiIiAoaS5lLiwgYW4gZW1wdHkgc3RyaW5nKSBmb3IgZWZmZWN0cyB3aGVyZSBpdCBpcyBub3QgbmVlZGVkLiAgDQoNCiMjIyMgKiphY2Nlc3Npbmcgb3RoZXIgY2hhcmFjdGVyaXN0aWNzIG9mIGVmZmVjdHMqKiAgDQoNCkEgc2Vjb25kIHNwZWNpYWwgdG9waWMgaXMgaG93IHRvIGFjY2VzcyBvdGhlciBjaGFyYWN0ZXJpc3RpY3Mgb2YgZWZmZWN0cy4gVGhpcyBjYW4gYmUgZG9uZSBieSB0aGUgYHNldEVmZmVjdCgpYCBmdW5jdGlvbi4gRS5nLiwgdGhlIGRlbnNlIHRyaWFkcyBlZmZlY3RzIGNvdW50cyB0aGUgbnVtYmVyIG9mIHRyaXBsZXRzIHdpdGggYXQgbGVhc3QgeHggdGllcywgIHdoZXJlIHh4IGlzIHRoZSBwYXJhbWV0ZXIgb2YgdGhlIGVmZmVjdCwgd2hpY2ggY2FuIGJlIDUgb3IgNiAobm90ZSB0aGF0IDYgaXMgdGhlIG1heGltdW0gbnVtYmVyIG9mIHRpZXMgaW4gYSB0cmlwbGV0KS4gVGhlIGRlZmF1bHQgaXMgNS4gVGhpcyBpcyBjaGFuZ2VkIHRvIDYgYnkgdGhlIGNvbW1hbmQ6ICANCg0KYGBge3IsIGV2YWw9RkFMU0V9DQpteWVmZiA8LSBzZXRFZmZlY3QobXllZmYsIGRlbnNlVHJpYWRzLCBwYXJhbWV0ZXIgPSA2KQ0KbXllZmYNCmBgYA0KDQpUaGUgJ3BhcmFtZXRlcicga2V5d29yZCByZWZlcnMgdG8gdGhlIGVmZmVjdCBwYXJhbWV0ZXIsIGRlc2NyaWJlZCBpbiBTZWN0aW9uIDEyIG9mIHRoZSBtYW51YWwuDQoNCiMjIyAgMi4gQWRkaW5nL3JlbW92aW5nIGVmZmVjdHMgdXNpbmcgYGZpeCgpYCAgDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KIyBmaXggY2FsbHMgYSBkYXRhIGVkaXRvciBpbnRlcm5hbCB0byBSLCBzbyB3ZSBjYW4gbWFudWFsbHkgZWRpdCB0aGUgZWZmZWN0cy4NCg0KIyAgICAgICAgZml4KCBteWVmZiApDQoNCiMgSG93IHRvIHVzZSBmaXgoKSBpcyBwcmVzZW50ZWQgbWVyZWx5IGZvciBnZXR0aW5nIHRvIGtub3cgd2hhdCBteWVmZiBpcy4NCiMgSW4gcHJhY3RpY2FsIGFuYWx5c2lzIGl0IGlzIG1vcmUgY29udmVuaWVudA0KIyB0byB1c2Ugcm91dGluZSAiaW5jbHVkZUVmZmVjdHMiIGluc3RlYWQsIGFzIGV4cGxhaW5lZCBhYm92ZS4NCiMgZml4KCkgbWF5IG5vdCBiZSB1c2FibGUgaWYgeW91IGRvIG5vdCBoYXZlIHRjbC90ayBhdmFpbGFibGUhDQojIE5vdGUgdGhhdCB0aGUgdG9wIG9mIHRoZSBkYXRhZnJhbWUgc2hvd3MgdGhlIG5hbWVzIG9mIHRoZSBjb2x1bW5zOg0KIyBuYW1lLCBlZmZlY3ROYW1lLCBldGMuDQojIFlvdSBjYW4gZWRpdCB0aGUgImluY2x1ZGUiIGNvbHVtbiBieSBjaGFuZ2luZyB0aGUgVFJVRSBhbmQgRkFMU0UgdmFsdWVzDQojIGFzIHJlcXVpcmVkOyB3aGVuIHRoZSBlZGl0b3IgaXMgY2xvc2VkLCB0aGUgbmV3IHZhbHVlcyBhcmUgc3RvcmVkLg0KIyBXaGVuIHlvdSBtYWtlIGFuIGVycm9yLCBob3dldmVyLCB0aGUgZWZmZWN0cyBvYmplY3QgbWF5IGJlIGNvcnJ1cHRlZC4NCiMgVGhlcmVmb3JlLCB0aGlzIHdheSBvZiBhZGRpbmcgYW5kIHJlbW92aW5nIGVmZmVjdHMgaXMgbW9yZSByaXNreS4NCmBgYA0KDQojIyMgMy4gQWRkaW5nL3JlbW92aW5nIGVmZmVjdHMgYnkgZGlyZWN0IG1hbmlwdWxhdGlvbiBvZiBgbXllZmZgICANCg0KQWx0ZXJuYXRpdmVseSB3ZSBjYW4gZWRpdCB0aGUgZGF0YWZyYW1lIGRpcmVjdGx5IGJ5IHVzaW5nIFIgZnVuY3Rpb25zLiBZb3UgYXJlIGFkdmlzZWQgdG8gc2tpcCB0aGlzIHBhcnQgKCIzLiIpIGF0IGZpcnN0IGFuZCBzZWNvbmQgcmVhZGluZywgYW5kIHJlYWQgaXQgb25seSBpZiB5b3Ugd2lzaCB0byBnZXQgbW9yZSB1bmRlcnN0YW5kaW5nIG9mIHRoZSBpbnRlcm5hbCBzdHJ1Y3R1cmUgb2YgdGhlIGVmZmVjdHMgb2JqZWN0LiBUaGUgY29tbWFuZHMgYmVsb3cgYXJlIHVzZWQgdG8gc2V0ICJpbmNsdWRlIiB0byBUUlVFIG9yIEZBTFNFLCBhcyBhbiBhbHRlcm5hdGl2ZSB0byB1c2luZyB0aGUgZGF0YSBlZGl0b3IuIFRoZSAiaW5jbHVkZSIgY29sdW1uIHdpdGggdmFsdWVzIFRSVUUgb3IgRkFMU0Ugd2lsbCBhbHdheXMgYmUgbG9jYXRlZCBhdCB0aGUgOXRoIGNvbHVtbiwgYnV0IHRyYW5zaXRpdmUgdHJpcGxldHMgd2lsbCBub3QgYWx3YXlzIGJlIGF0IHRoZSAxNnRoIHJvdyBhcyB0aGlzIGRlcGVuZHMgb24gdGhlIG51bWJlciBvZiBwZXJpb2RzIGFuZCB2YXJpYWJsZXM7IGZ1cnRoZXIsIHRoZSBsaXN0IG9mIGF2YWlsYWJsZSBlZmZlY3RzIGNoYW5nZXMgb3ZlciBkaWZmZXJlbnQgdmVyc2lvbnMgb2YgUlNpZW5hLiAgDQoNClNvbWUgZXhhbXBsZXMgYXJlIHRoZSBmb2xsb3dpbmc6IA0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCm15ZWZmWzE2LDldIDwtIFRSVUUgICAjdHJhbnNpdGl2ZSB0cmlwbGVzDQpteWVmZlszNCw5XSA8LSBUUlVFICAgIzMgY3ljbGVzDQpteWVmZlszNyw5XSA8LSBUUlVFICAgI3RyYW5zaXRpdmUgdGllcw0KbXllZmZbNzcsOV0gPC0gVFJVRSAgICNpbmRlZ3JlZSBwb3B1bGFyaXR5IChzcXJ0KQ0KbXllZmZbODYsOV0gPC0gVFJVRSAgICNvdXRkZWdyZWUgcG9wdWxhcml0eSAoc3FydCkNCm15ZWZmWzkzLDldIDwtIFRSVUUgICAjaW5kZWdyZWUgYmFzZWQgYWN0aXZpdHkgKHNxcnQpDQpteWVmZlsxMDIsOV0gPC0gVFJVRSAgI291dGRlZ3JlZSBiYXNlZCBhY3Rpdml0eSAoc3FydCkNCm15ZWZmWzE1NSw5XSA8LSBUUlVFICAgI2luZGVncmVlLWluZGVncmVlIGFzc29ydGF0aXZpdHkNCm15ZWZmWzMxOSw5XSA8LSBUUlVFICAgI2FsY29ob2wgYWx0ZXINCm15ZWZmWzMzMSw5XSA8LSBUUlVFICAgI2FsY29ob2wgZWdvDQpteWVmZlszNzAsOV0gPC0gVFJVRSAgICNhbGNvaG9sIHNpbWlsYXJpdHkNCm15ZWZmWzQzOSw5XSA8LSBUUlVFICAgI2FsY29ob2wgZWdvIHggYWxjb2hvbCBhbHRlcg0KYGBgDQoNCkJ1dCBpbiBvdGhlciBjaG9pY2VzIG9mIGRhdGEsIHRoZSBlZmZlY3QgbnVtYmVycyB3aWxsIGNoYW5nZS4gVGhpcyBpcyBhIHJlYXNvbiB3aHkgdGhpcyBpcyBub3QgYSBjb252ZW5pZW50IG1ldGhvZC4NCg0KIyMgU2F2ZSB5b3VyIGRhdGEgaWYgeW91IHdoaXNoLiAgDQpgYGB7ciwgZXZhbD1GQUxTRX0NCg0Kc2F2ZS5pbWFnZSgiV29ya3NwYWNlUnNjcmlwdDAyLlJEYXRhIikNCg0KYGBgDQoNCg0KLS0tIA0KDQpbKipOZXh0OiBTaWVuYVJ1bk1vZGVsIEZPUiBQQVJBTUVURVIgRVNUSU1BVElPTiBCWSBSU0lFTkEqKl0oL3JzY3JpcHQwMy5odG1sKQ0KDQotLS0NCg0KDQo=
Copyright © 2020 Jochem Tolsma