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


Contact

Specific questions with respect to the .rmd files can be addressed to: Jochem Tolsma.

For questions on RSiena please visit the designated GitHub page.


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:

  1. coCovar: Constant actor covariates
  2. varCovar: Time-varying actor covariates
  3. coDyadCovar: Constant dyadic covariates
  4. varDyadCovar: Time-varying dyadic covariates
  5. 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:

  1. Using RSiena functions includeEffects(), `setEffects(), etc;
  2. Changing myeff in spreadsheet form by the function fix();
  3. 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.

Save your data if you whish.

save.image("WorkspaceRscript02.RData")

Next: SienaRunModel FOR PARAMETER ESTIMATION BY RSIENA


LS0tDQp0aXRsZTogIlZhcmlhYmxlRm9ybWF0Ig0KYXV0aG9yOiAnW0pvY2hlbSBUb2xzbWFdKGh0dHBzOi8vd3d3LmpvY2hlbXRvbHNtYS5ubCkgLSBSYWRib3VkIFVuaXZlcnNpdHksIHRoZSBOZXRoZXJsYW5kcycNCmJpYmxpb2dyYXBoeTogcmVmZXJlbmNlcy5iaWINCmRhdGU6ICJMYXN0IGNvbXBpbGVkIG9uIGByIGZvcm1hdChTeXMudGltZSgpLCAnJUIsICVZJylgIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6ICB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQ0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KLS0tDQoNCmBgYHtyLCBnbG9iYWxzZXR0aW5ncywgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgcmVzdWx0cz0naGlkZSd9DQpsaWJyYXJ5KGtuaXRyKQ0Kb3B0c19jaHVuayRzZXQodGlkeS5vcHRzPWxpc3Qod2lkdGguY3V0b2ZmPTEwMCksdGlkeT1UUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSxjb21tZW50ID0gIiM+IiwgY2FjaGU9VFJVRSwgY2xhc3Muc291cmNlPWMoInRlc3QiKSwgY2xhc3Mub3V0cHV0PWMoInRlc3QyIikpDQpvcHRpb25zKHdpZHRoID0gMTAwKQ0KcmdsOjpzZXR1cEtuaXRyKCkNCg0KDQpjb2xvcml6ZSA8LSBmdW5jdGlvbih4LCBjb2xvcikgew0KICBpZiAoa25pdHI6OmlzX2xhdGV4X291dHB1dCgpKSB7DQogICAgc3ByaW50ZigiXFx0ZXh0Y29sb3J7JXN9eyVzfSIsIGNvbG9yLCB4KQ0KICB9IGVsc2UgaWYgKGtuaXRyOjppc19odG1sX291dHB1dCgpKSB7DQogICAgc3ByaW50ZigiPHNwYW4gc3R5bGU9J2NvbG9yOiAlczsnPiVzPC9zcGFuPiIsIGNvbG9yLCANCiAgICAgIHgpDQogIH0gZWxzZSB4DQp9DQoNCmBgYA0KDQpgYGB7ciBrbGlwcHksIGVjaG89RkFMU0UsIGluY2x1ZGU9VFJVRX0NCmtsaXBweTo6a2xpcHB5KHBvc2l0aW9uID0gYygndG9wJywgJ3JpZ2h0JykpDQoja2xpcHB5OjprbGlwcHkoY29sb3IgPSAnZGFya3JlZCcpDQoja2xpcHB5OjprbGlwcHkodG9vbHRpcF9tZXNzYWdlID0gJ0NsaWNrIHRvIGNvcHknLCB0b29sdGlwX3N1Y2Nlc3MgPSAnRG9uZScpDQpgYGANCg0KYGBge2NzcywgZWNobz1GQUxTRX0NCnByZS50ZXN0IHsNCiAgbWF4LWhlaWdodDogMzAwcHg7DQogIG92ZXJmbG93LXk6IGF1dG87DQogIG92ZXJmbG93LXg6IGF1dG87DQogIG1hcmdpbjogMTBweDsNCn0NCg0KcHJlLnRlc3QyIHsNCiAgbWF4LWhlaWdodDogMzAwcHg7DQogIG92ZXJmbG93LXk6IGF1dG87DQogIG92ZXJmbG93LXg6IGF1dG87DQogIG1hcmdpbjogMTBweDsNCiAgYmFja2dyb3VuZC1jb2xvcjogd2hpdGUNCn0NCg0KDQpoMSwgLmgxLCBoMiwgLmgyLCBoMywgLmgzIHsNCiAgICBtYXJnaW4tdG9wOiAyNHB4Ow0KfQ0KDQoNCmBgYA0KDQotLS0tDQoNClRoaXMgd2Vic2l0ZSBjb252ZXJ0ZWQgdGhlIGZvbGxvd2luZyBvcmlnaW5hbCAuUiBzY3JpcHRzIGludG8gLnJtZCBmaWxlcy4gDQoNCi0gUnNjcmlwdDAxRGF0YUZvcm1hdC5SIA0KLSBSU2NyaXB0U05BRGVzY3JpcHRpdmVzLlIgDQotIFJzY3JpcHQwMlNpZW5hVmFyaWFibGVGb3JtYXQuUiAgDQotIFJzY3JpcHQwM1NpZW5hUnVuTW9kZWwuUiAgDQotIFJzY3JpcHQwNFNpZW5hQmVoYXZpb3VyLlINCg0KUGxlYXNlIHZpc2l0IFtHaXRIdWJdKGh0dHBzOi8vZ2l0aHViLmNvbS9zbmxhYi1ubC9yc2llbmEvdHJlZS9tYWluL2luc3Qvc2NyaXB0cykgZm9yIHRoZSBsYXRlc3QgLlIgZmlsZXMuIA0KDQotLS0tDQoNCiMjIERhdGENCkFsbCBmaWxlcyAoZGF0YSwgc2NyaXB0cywgZXRjLikgY2FuIGFsc28gYmUgZm91bmQgb24gW0dpdGh1Yl0oaHR0cHM6Ly9naXRodWIuY29tL0pvY2hlbVRvbHNtYS9Sc2llbmEtc2NyaXB0cykNCg0KLS0tLQ0KDQojIyBDb250YWN0DQpTcGVjaWZpYyBxdWVzdGlvbnMgd2l0aCByZXNwZWN0IHRvIHRoZSAucm1kIGZpbGVzIGNhbiBiZSBhZGRyZXNzZWQgdG86IFtKb2NoZW0gVG9sc21hXShtYWlsdG86ai50b2xzbWFAcnUubmwpLiAgDQoNCkZvciBxdWVzdGlvbnMgb24gUlNpZW5hIHBsZWFzZSB2aXNpdCB0aGUgZGVzaWduYXRlZCBbR2l0SHViXShodHRwczovL2dpdGh1Yi5jb20vc25sYWItbmwvcnNpZW5hKSBwYWdlLiANCg0KLS0tLSAgDQoNCiMjIEludHJvZHVjdGlvbiANCg0KTGV0IHVzIHN0YXJ0IGJ5IGxvYWRpbmcgdGhlIGRhdGENCg0KYGBge3J9DQpsaWJyYXJ5KFJTaWVuYSkNCmZyaWVuZC5kYXRhLncxIDwtIHM1MDENCmZyaWVuZC5kYXRhLncyIDwtIHM1MDINCmZyaWVuZC5kYXRhLnczIDwtIHM1MDMNCmRyaW5rIDwtIHM1MGENCnNtb2tlIDwtIHM1MHMNCmBgYA0KDQpBIG51bWJlciBvZiBvYmplY3RzIG5lZWQgdG8gYmUgY3JlYXRlZCBpbiBSLCBhcyBwcmVwYXJhdGlvbnMgdG8gbGV0dGluZyBzaWVuYTA3IGV4ZWN1dGUgdGhlIGVzdGltYXRpb24uIFRoaXMgd2lsbCBiZSBpbmRpY2F0ZWQgYnk6ICANCg0KLSAgQTogZGVwZW5kZW50IHZhcmlhYmxlczsgIA0KLSAgQjogZXhwbGFuYXRvcnkgdmFyaWFibGVzOyAgDQotICBDOiBjb21iaW5hdGlvbiBvZiBkZXBlbmRlbnQgYW5kIGV4cGxhbmF0b3J5IHZhcmlhYmxlczsgIA0KLSAgRDogbW9kZWwgc3BlY2lmaWNhdGlvbi4NCg0KLS0tICANCg0KIyMgQS4gZGVwZW5kZW50IHZhcmlhYmxlcyAgDQoNCg0KIEZpcnN0IHdlIGhhdmUgdG8gY3JlYXRlIG9iamVjdHMgZm9yIHRoZSBkZXBlbmRlbnQgdmFyaWFibGVzLg0KDQpgc2llbmFEZXBlbmRlbnQoKWAgY3JlYXRlcyBhIHNpZW5hRGVwZW5kZW50IG9iamVjdCwgaGVyZSBhIG5ldHdvcmssIGZyb20gYSBtYXRyaXggb3IgYXJyYXkgb3IgbGlzdCBvZiBzcGFyc2UgbWF0cml4IG9mIHRyaXBsZXMuIFRoaXMgb2JqZWN0IHdpbGwgaGF2ZSB0aGUgcm9sZSBvZiBhIGRlcGVuZGVudCB2YXJpYWJsZSBpbiB0aGUgYW5hbHlzaXMuICANCg0KVGhlIG5hbWUgb2YgdGhpcyBuZXR3b3JrIG9iamVjdCAoaGVyZTogKipmcmllbmRzaGlwKiopIHdpbGwgYmUgdXNlZCBpbiB0aGUgb3V0cHV0IGZpbGUuDQoNCmBgYHtyfQ0KZnJpZW5kc2hpcCA8LSBzaWVuYURlcGVuZGVudCgNCiAgYXJyYXkoIGMoIGZyaWVuZC5kYXRhLncxLCBmcmllbmQuZGF0YS53MiwgZnJpZW5kLmRhdGEudzMgKSwNCiAgICAgICAgIGRpbSA9IGMoIDUwLCA1MCwgMyApICkgKQ0KYGBgDQoNClRoZSBpbnRlZ2VycyBpbiB0aGUgYGRpbSgpYCBoZXJlIHJlZmVyIHRvIHRoZSBudW1iZXIgb2Ygbm9kZXMgKHNlbmRlcnMsIHJlY2VpdmVycykgYW5kIHRoZSBudW1iZXIgb2Ygd2F2ZXMuIFRoaXMgb2JqZWN0IGlzIGFuIGFycmF5IG9mIGRpbWVuc2lvbiA1MCB4IDUwIHggMywgcmVwcmVzZW50aW5nIHRocmVlIGFkamFjZW5jeSBtYXRyaWNlcywgd2l0aCBhIG51bWJlciBvZiBhdHRyaWJ1dGVzLiAgDQoNCllvdSBjYW4gYXNrIGZvciBhIGJyaWVmIGRlY3JpcHRpb24gb2YgdGhpcyBvYmplY3Qgc2ltcGx5IGJ5IHR5cGluZzogIA0KDQoNCg0KYGBge3J9DQpwcmludChmcmllbmRzaGlwKQ0KIyBmb3Igd2hpY2ggdGhlIHNob3J0aGFuZCBpcw0KDQpmcmllbmRzaGlwDQpgYGANCg0KTm90ZSB0aGF0IHRoaXMgaXMgYW4gb2JqZWN0IG9mIGNsYXNzICANCmBgYHtyfQ0KY2xhc3MoZnJpZW5kc2hpcCkNCmBgYA0KDQpXaXRoIHNwZWNpZmljIGF0dHJpYnV0ZXMgYW5kIG1ldGhvZHMgYXNzb2NpYXRlZCB3aXRoIGl0Lg0KWVlvdSBjYW4gZ2V0IHRoZSBkZXRhaWxlZCBpbmZvcm1hdGlvbiBieSByZXF1ZXN0aW5nOiANCg0KYGBge3J9DQpkaW0oIGZyaWVuZHNoaXAgKQ0KYXR0cmlidXRlcyggZnJpZW5kc2hpcCApDQpgYGANCg0KDQpJZiB5b3Ugb25seSBhcmUgaW50ZXJlc3RlZCBpbiB0aGUgdmFsdWUgb2Ygb25lIHBhcnRpY3VsYXIgYXR0cmlidXRlLCB5b3UgY2FuIHJlcXVlc3QgdGhpcyBieSwgZS5nLiwNCg0KYGBge3J9DQphdHRyKCBmcmllbmRzaGlwLCAidHlwZSIpDQpgYGANCg0KQW4gZXh0ZW5zaXZlIGRlc2NyaXB0aW9uIG9mIHRoZSBmcmllbmRzaGlwIGRhdGEgaXMgb2J0YWluZWQgYnkgdHlwaW5nOiANCmBgYHtyfQ0Kc3RyKGZyaWVuZHNoaXApDQpgYGANCg0KVGhlIGZ1bmN0aW9uIGBzaWVuYURlcGVuZGVudCgpYCBjYW4gYWxzbyBiZSB1c2VkIHRvIGNyZWF0ZSBhIGJlaGF2aW9yIHZhcmlhYmxlIG9iamVjdCB3aXRoIHRoZSBleHRyYSBhcmd1bWVudCAqKmB0eXBlID0gImJlaGF2aW9yImAqKi4gIA0KDQpOb24tbWVudGlvbmVkIGF0dHJpYnV0ZXMgZ2V0IHRoZSBkZWZhdWx0IHZhbHVlLCBhbmQgaW4gdGhpcyBjYXNlIG9uZU1vZGUgaXMgdGhlIGRlZmF1bHQ7IHNlZSBiZWxvdy4gIA0KDQpUaGUgYGRyaW5rYCBkYXRhIChjcmVhdGVkIGluIFJzY3JpcHREYXRhRm9ybWF0LlIgKSBpcyBtYWRlIGF2YWlsYWJsZSBhcyBhIGRlcGVuZGVudCBiZWhhdmlvciB2YXJpYWJsZSBieSB0aGUgZnVuY3Rpb246IA0KDQpgYGB7cn0NCmRyaW5raW5nYmVoIDwtIHNpZW5hRGVwZW5kZW50KCBkcmluaywgdHlwZSA9ICJiZWhhdmlvciIgKQ0KDQojIHRoZSBjbGFzcywgY2xhc3MoZHJpbmtpbmdiZWgpLCBpcyBzdGlsbCBzaWVuYURlcGVuZGVudC4NCmBgYA0KDQpOb3RlOiBvbmx5IHVzZSB0aGUgdmFyaWFibGUgaW4gT05FIHJvbGUgaW4gYSBnaXZlbiBtb2RlbDogYmVoYXZpb3IgdmFyaWFibGUgb3IgY2hhbmdpbmcgY292YXJpYXRlISAgDQoNClRoZSBvcHRpb25zIGF2YWlsYWJsZSBmb3IgZGVmaW5pbmcgYSBzaWVuYURlcGVuZGVudCBvYmplY3QgYXJlIGRpc3BsYXllZCBieSB0eXBpbmc6ICBgP3NpZW5hRGVwZW5kZW50YC4gDQoNClRoaXMgc2hvd3MgdGhhdCBuZXh0IHRvIG9uZS1tb2RlICh1bmlwYXJ0aXRlKSBhbmQgYmVoYXZpb3IgZGVwZW5kZW50IHZhcmlhYmxlcywgYWxzbyB0d28tbW9kZSAoYmlwYXJ0aXRlKSBkZXBlbmRlbnQgdmFyaWFibGVzIGFyZSBwb3NzaWJsZS4gIA0KWW91IGNhbiBpbmZlciB0aGF0IG9uZU1vZGUgaXMgdGhlIGRlZmF1bHQgdHlwZSBmcm9tIHRoZSBmYWN0IHRoYXQgaXQgaXMgbWVudGlvbmVkIGZpcnN0LiAgDQoNCg0KVG8gY3JlYXRlIGJpcGFydGl0ZSBuZXR3b3JrIG9iamVjdHMgeW91IG5lZWQgdHdvIG5vZGUgc2V0cyBhbmQgbXVzdCBjcmVhdGUgdGhlIG5vZGUgc2V0cyB0b28uDQoNClBsZWFzZSBTZWUgYD9zaWVuYU5vZGVTZXRgIHdoZXJlIHRoZSBFeGFtcGxlcyBzZWN0aW9uIHNob3dzIGFuIGV4YW1wbGUgb2YgdGhlIHN5bnRheC4NCg0KLS0tICAgDQoNCiMjIEIuIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyAgDQoNClNlY29uZCB3ZSBjb25zdHJ1Y3Qgb2JqZWN0cyBmb3IgdGhlIGV4cGxhbmF0b3J5IChpbmRlcGVuZGVudCkgdmFyaWFibGVzLiAgDQpGcm9tIHRoZSBoZWxwIHJlcXVlc3Q6IGA/c2llbmFEYXRhQ3JlYXRlYA0KIA0KV2Ugc2VlIHRoYXQgdGhlc2UgY2FuIGJlIG9mIGZpdmUga2luZHM6ICANCg0KMS4gYGNvQ292YXJgOiBDb25zdGFudCBhY3RvciBjb3ZhcmlhdGVzICANCjIuIGB2YXJDb3ZhcmA6IFRpbWUtdmFyeWluZyBhY3RvciBjb3ZhcmlhdGVzICANCjMuIGBjb0R5YWRDb3ZhcmA6IENvbnN0YW50IGR5YWRpYyBjb3ZhcmlhdGVzICANCjQuIGB2YXJEeWFkQ292YXJgOiBUaW1lLXZhcnlpbmcgZHlhZGljIGNvdmFyaWF0ZXMgIA0KNS4gYGNvbXBvc2l0aW9uQ2hhbmdlYCBDb21wb3NpdGlvbiBjaGFuZ2UgaW5kaWNhdG9ycyAgDQoNCg0KDQpZb3UgY2FuIGdldCBoZWxwIGFib3V0IHRoaXMgYnkgdGhlIGZvbGxvd2luZyByZXF1ZXN0czogIA0KDQoqIGA/Y29Db3ZhcmAgIA0KKiBgP3ZhckNvdmFyYCAgDQoqIGA/Y29EeWFkQ292YXJgICANCiogYD92YXJEeWFkQ292YXJgICANCiogYD9zaWVuYUNvbXBvc2l0aW9uQ2hhbmdlYCAgDQoNCg0KVGhlIHZhcmlhYmxlcyBhdmFpbGFibGUgZm9yIHRoaXMgZGF0YSBzZXQgYWxsIGFyZSBjaGFuZ2luZyBhY3RvciBjb3ZhcmlhdGVzLiAgDQpGb3IgaWxsdXN0cmF0aXZlIHB1cnBvc2VzLCB3ZSB1c2Ugc21va2luZyBhcyBvYnNlcnZlZCBhdCB0aGUgZmlyc3Qgd2F2ZSBhcyBhIGNvbnN0YW50IGNvdmFyaWF0ZToNCg0KDQpgYGB7cn0NCnNtb2tlMSA8LSBjb0NvdmFyKCBzbW9rZVsgLCAxIF0gKQ0KYGBgDQoNClRoaXMgc2VsZWN0cyB0aGUgZmlyc3QgY29sdW1uIG9mIHNtb2tlLCB3aGljaCBjb250YWlucyB0aGUgZmlyc3Qgd2F2ZSBvYnNlcnZhdGlvbnMsIGFuZCBtYWtlcyBpdCBhdmFpbGFibGUgYXMgYSBjb25zdGFudCBjb3ZhcmlhdGUuIFRoaXMgaXMgdGhlIHBhdHRlcm4gZm9yIGZvciBldmV5IGNvdmFyaWF0ZSBmaWxlLCBlLmcuIGBBdHRyMSA8LSBjb0NvdmFyKCBDb3ZhcmlhdGUxIClgIHdoZXJlIENvdmFyaWF0ZTEgaXMgYSBtYXRyaXggd2l0aCBgZGltKENvdmFyaWF0ZTEpYCBlcXVhbCB0byBuIHggMS4gIA0KDQpOb3RlLCBpZiBDb3ZhcmlhdGVzIGlzIGEgbWF0cml4IHdpdGggYGRpbShDb3ZhcmlhdGVzKWAgZXF1YWwgdG8gbiB4IHAgeW91IGNhbiBjcmVhdGUgY29uc3RhbnQgY292YXJpYXRlcyB0aHJvdWdoOiAgDQoNCi0gYEF0dHIxIDwtIGNvQ292YXIoQ292YXJpYXRlc1ssMV0pYCAgDQotIC4uLiAgDQotIGBBdHRycCA8LSBjb0NvdmFyKENvdmFyaWF0ZXNbLHBdKWAgIA0KDQogV2UgdXNlIHRoZSBkcmlua2luZyBkYXRhIGFzIGEgY2hhbmdpbmcgY292YXJpYXRlLiBUaGUgZnVuY3Rpb24gYHZhckNvdmFyKCkgY3JlYXRlcyBhIGNoYW5naW5nIGNvdmFyaWF0ZSBvYmplY3QgZnJvbSBhIG1hdHJpeDsgdGhlIG5hbWUgY29tZXMgZnJvbSAndmFyeWluZyBjb3ZhcmlhdGUnLiAgDQogDQpgYGB7cn0NCmFsY29ob2wgPC0gdmFyQ292YXIoIGRyaW5rICkNCmBgYA0KDQpZb3UgbmVlZCBhdCBsZWFzdCB0aHJlZSB3YXZlcyBpbiB0aGUgZGF0YSBzZXQgdG8gZGVmaW5lIGEgdmFyeWluZyBjb3ZhcmlhdGUgYnkgdGhlIGZ1bmN0aW9uIGB2YXJDb3ZhcigpYDsgdGhlIHJlYXNvbiBpcyB0aGF0IHRoZSBwcmV2aW91cyB3YXZlIGlzIHVzZWQgYXMgYSBwcmVkaWN0b3Igb2YgdGhlIG5leHQgd2F2ZS4gIA0KDQpUaGUgY29tbWFuZC4uLg0KDQpgYGB7cn0NCmF0dHJpYnV0ZXMoIGFsY29ob2wgKQ0KYGBgDQoNCi4uLndpbGwgdGVsbCB5b3UgdGhlIGluZm9ybWF0aW9uIHRoYXQgUlNpZW5hIG5vdyBoYXMgYWRkZWQgdG8gdGhlIGRyaW5rIGRhdGEuICANCg0KLS0tICANCg0KIyMgQy4gY29tYmluYXRpb24gb2YgZGVwZW5kZW50IGFuZCBleHBsYW5hdG9yeSB2YXJpYWJsZXMgIA0KDQpXZSBub3cgY29tYmluZSB0aGUgZGVwZW5kZW50IGFuZCBpbmRlcGVuZGVudCB2YXJpYWJsZXMuICANClRoZSBmdW5jdGlvbiBgc2llbmFEYXRhQ3JlYXRlKClgIGNyZWF0ZXMgYSBTaWVuYSBkYXRhIG9iamVjdCBmcm9tIGlucHV0IG5ldHdvcmtzLCBjb3ZhcmlhdGVzIGFuZCBjb21wb3NpdGlvbiBjaGFuZ2Ugb2JqZWN0czsgdGhlIG9iamVjdHMgdGhhdCBlYXJsaWVyIHdlcmUgY3JlYXRlZCBieSBgc2llbmFEZXBlbmRlbnQoKWAgd2lsbCBoYXZlIHRoZSByb2xlIG9mIGRlcGVuZGVudCB2YXJpYWJsZXMsIGFuZCBzaW1pbGFybHkgdGhlIG90aGVyIHJvbGVzIGFyZSBwcmVkZXRlcm1pbmVkIGJ5IGNyZWF0aW9uIGJ5IHRoZSBmdW5jdGlvbnMgYGNvQ292YXIoKWAsIGB2YXJDb3ZhcigpYCwgYGNvRHlhZENvdmFyKClgLCBgdmFyRHlhZENvdmFyKClgLCBhbmQgYHNpZW5hQ29tcG9zaXRpb25DaGFuZ2UoKWAuDQoNCmBgYHtyfQ0KbXlkYXRhIDwtIHNpZW5hRGF0YUNyZWF0ZSggZnJpZW5kc2hpcCwgc21va2UxLCBhbGNvaG9sICkNCmBgYA0KDQpZb3UgbWF5IGNoZWNrIHRoZSByZXN1bHQgYnkgcmVxdWVzdGluZw0KYGBge3J9DQpteWRhdGENCmBgYA0KDQpZb3Ugc2hvdWxkIG5vdyB1bmRlcnN0YW5kIGhvdyB0aGlzIGRpZmZlcnMgZnJvbSB0aGUgcmVzdWx0IG9mOiANCg0KYG15YmVoZGF0YSA8LSBzaWVuYURhdGFDcmVhdGUoIGZyaWVuZHNoaXAsIHNtb2tlMSwgZHJpbmtpbmdiZWgpYCANCg0KSWYgeW91IHdvdWxkIGxpa2UgdG8gdXNlIGRpZmZlcmVudCBuYW1lcywgeW91IGNvdWxkIHJlcXVlc3QgdGhpcyBhcyBmb2xsb3dzOg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCm15ZGF0YSA8LSBzaWVuYURhdGFDcmVhdGUobm9taW5hdGlvbnMgPSBmcmllbmRzaGlwLCBzbW9rZTEsIGRyaW5raW5nID0gYWxjb2hvbCkNCmBgYA0KDQpBbm90aGVyIHR5cGUgb2YgZGVwZW5kZW50IG5ldHdvcmsgaXMgYSB0d28tbW9kZSBuZXR3b3JrLCB3aGljaCBoZXJlIGlzIGFsc28gY2FsbGVkIGEgYmlwYXJ0aXRlIG5ldHdvcmsuICANClRvIGdldCBhZHZpY2UgZm9yIGEgZmlyc3Qgc3RlcCBpbiB0aGUgdXNlIG9mIHR3by1tb2RlIG5ldHdvcmtzLCBzZWUgdGhlIHNjcmlwdCBSc2NyaXB0U2llbmFCaXBhcnRpdGUuUiBvbiB0aGUgU2llbmEgc2NyaXB0cyBwYWdlLiAgDQoNClRoaXMgZmluaXNoZXMgdGhlIGRhdGEgc3BlY2lmaWNhdGlvbi4gTm93IHdlIGhhdmUgdG8gc3BlY2lmeSB0aGUgbW9kZWwuICANCg0KIyMgRC4gbW9kZWwgc3BlY2lmaWNhdGlvbiAgDQoNCiMjIyBEZWZpbmluZyBlZmZlY3RzIEkgDQoNClRoZSBkYXRhIHNldCBhcyBjb21iaW5lZCBpbiBgbXlkYXRhYCBpbXBsaWVzIGEgY2VydGFpbiBzZXQgb2YgZWZmZWN0cyB0aGF0IGNhbiBiZSBpbmNsdWRlZCBpbiB0aGUgc3BlY2lmaWNhdGlvbiBvZiB0aGUgbW9kZWwuICANClRoZSBmdW5jdGlvbiBgZ2V0RWZmZWN0cygpYCBjcmVhdGVzIGEgZGF0YWZyYW1lIG9mIGVmZmVjdHMgd2l0aCBhIG51bWJlciBvZiBleHRyYSBwcm9wZXJ0aWVzIGZvciB1c2UgaW4gUlNpZW5hOg0KDQpgYGB7cn0NCm15ZWZmIDwtIGdldEVmZmVjdHMoIG15ZGF0YSApDQpgYGANCg0KYG15ZGF0YWAgaXMgbmVlZGVkIGFzIGFuIGFyZ3VtZW50IGFzIHRoZSBlZmZlY3RzIGRlcGVuZCBvbiB0aGUgbnVtYmVyIGFuZCB0eXBlcyBvZiBjb3ZhcmlhdGVzIGFuZCBkZXBlbmRlbnQgdmFyaWFibGVzLiAgDQpCZWZvcmUgd2UgZXhwbGFpbiB0aGUgb2JqZWN0IGBteWVmZmAgYW5kIGhvdyB3ZSBzaGFsbCBiZSBnb2luZyB0byB1c2UgaXQsIHdlIGZpcnN0IHByb2R1Y2UgYSBkYXRhIGRlc2NyaXB0aW9uIHdoaWNoIGlzIGF2YWlsYWJsZSBub3c6DQoNCiMjIyBEYXRhIGRlc2NyaXB0aW9uDQpgYGB7cn0NCnByaW50MDFSZXBvcnQoIG15ZGF0YSwgbW9kZWxuYW1lID0gJ3M1MF8zX2luaXQnICkNCmBgYA0KDQpUaGlzIHdyaXRlcyBhIGJhc2ljIHJlcG9ydCBvZiB0aGUgZGF0YSB0byB0aGUgZmlsICpzNTBfM19pbml0LnR4dCogIGluIHRoZSBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5LiBMb2NhdGUgYW5kIG9wZW4gaXQhICANCkluc3BlY3RpbmcgdGhpcyBpcyBpbXBvcnRhbnQgYmVjYXVzZSBpdCBzZXJ2ZXMgYXMgYSBjaGVjayBhbmQgYWxzbyBjb250YWlucyBhIG51bWJlciBvZiBiYXNpYyBkZXNjcmlwdGl2ZXMuICANCkluIHRoaXMgZGVzY3JpcHRpb24geW91IGNhbiBzZWUgdGhhdCB0aGUgdGhpcmQgd2F2ZSBmb3IgYWxjb2hvbCBpcyBub3QgdXNlZC4gVGhpcyBpcyBiZWNhdXNlIGNoYW5naW5nIGNvdmFyaWF0ZXMgYXJlIGFzc3VtZWQgdG8gYmUgY29uc3RhbnQgZnJvbSBvbmUgd2F2ZSB1bnRpbCBpbW1lZGlhdGVseSBiZWZvcmUgdGhlIG5leHQgd2F2ZSwgc28gdGhhdCB0aGUgdmFsdWVzIGZvciB0aGUgbGFzdCB3YXZlIGFyZSBpZ25vcmVkLiAgDQoNCiMjIyBEZWZpbmluZyBlZmZlY3RzIElJICAgDQoNCkxldCB1cyBub3cgY29uc2lkZXIgdGhlIGBteWVmZmAgb2JqZWN0LCB3aGljaCBpcyB1c2VkIHRvIHNwZWNpZnkgdGhlIG1vZGVsLiAgDQpJdCBpcyBvZiB0aGUgY2xhc3MgYHNpZW5hRWZmZWN0c2AsIGFuZCBjb250YWlucyB0aGUgbW9kZWwgc3BlY2lmaWNhdGlvbi4gIA0KWW91IGNhbiBpbnNwZWN0IHRoZSBjdXJyZW50IG1vZGVsIHNwZWNpZmljYXRpb24gYnkgc2ltcGx5IHJlcXVlc3RpbmcuLi4gDQoNCmBgYHtyfQ0KbXllZmYNCmBgYA0KDQpGb3Igc3RhcnRpbmcsIHRoZSBtb2RlbCBzcGVjaWZpY2F0aW9uIGlzIGp1c3QgYSB2ZXJ5IGxpbWl0ZWQgZGVmYXVsdCAoaW5jbHVkaW5nIHJhdGVzIG9mIGNoYW5nZSwgb3V0ZGVncmVlIGFuZCByZWNpcHJvY2l0eSBvbmx5KS4gIFRvIG1ha2UgYSBtZWFuaW5nZnVsIGFuYWx5c2lzLCB5b3Ugd2lsbCBuZWVkIHRvIGFkZCB0byBpdC4gIA0KDQpUaGUgcm93cyBvZiBteWVmZiBjb3JyZXNwb25kIHRvIHRoZSBlZmZlY3RzLiAgDQpCeSByZXF1ZXN0aW5nLi4uDQpgYGB7cn0NCm5hbWVzKG15ZWZmKQ0KYGBgDQoNCllvdSBzZWUgdGhlIHR5cGUgb2YgaW5mb3JtYXRpb24gdGhhdCBpcyBzdG9yZWQgYWJvdXQgdGhlIGVmZmVjdHMsIGkuZS4sIHRoZSBjb2x1bW5zIChjaGFyYWN0ZXJpc3RpY3MpIGRlZmluZWQgZm9yIHRoZSBlZmZlY3RzLiBJZiBkZXNpcmVkLCBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHRoZXNlIHZhcmlhYmxlcyBjYW4gYmUgb2J0YWluZWQgZnJvbSB0aGUgaGVscCBmaWxlczogYD9nZXRFZmZlY3RzYCB3aGVyZSB0aGUgY2hhcmFjdGVyaXN0aWNzIGFyZSBkZXNjcmliZWQuICANCg0KU29tZSBvZnRlbiB1c2VkIHZhcmlhYmxlcyBhcmUgKmVmZmVjdE5hbWUqLCAqc2hvcnROYW1lKiAsICp0eXBlKiwgYW5kICpwYXJhbWV0ZXIqLiAgDQpUaGUgc2V0IG9mIGF2YWlsYWJsZSBlZmZlY3RzIGFuZCB0aGVpciBtb3N0IHVzZWQgY29sdW1ucyBjYW4gYmUgaW5zcGVjdGVkIGFzIGZvbGxvd3M6DQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KZWZmZWN0c0RvY3VtZW50YXRpb24obXllZmYpDQpgYGANCg0KVGhpcyBjcmVhdGVzIGFuIGh0bWwgZmlsZSB3aXRoIHRoZSBsaXN0IG9mIGFsbCBlZmZlY3RzIGF2YWlsYWJsZSBpbiBgbXllZmZgOyB0aGUgZWZmZWN0cyBhcmUgYWxsIGRlZmluZWQgaW4gU2VjdGlvbiAxMiBvZiB0aGUgUlNpZW5hIG1hbnVhbCwgYnV0IG9ubHkgdGhvc2UgdGhhdCBhcmUgbWVhbmluZ2Z1bCBmb3IgZGF0YXNldCBgbXlkYXRhYCBhcmUgdXNlZCBpbiBgbXllZmZgLiBUaGUgKmluY2x1ZGUqIGNvbHVtbiBkZWZpbmVzIHdoZXRoZXIgZWZmZWN0cyBhcmUgaW5jbHVkZWQgaW4gdGhlIG1vZGVsLiAgDQoNCmBgYHtyfQ0KbXllZmYkaW5jbHVkZQ0KYGBgDQoNCkhlcmUgdGhlIFRSVUUgdmFsdWVzIGNvcnJlc3BvbmQgdG8gdGhlIGRlZmF1bHQgbW9kZWwgc3BlY2lmaWNhdGlvbiB3aGljaCwgaG93ZXZlciwgaXMgbm90IG1lYW50IGFzIGEgc2VyaW91cyBtb2RlbCwgYmVpbmcgdG9vIGxpbWl0ZWQuIFRoZXJlIGFyZSAzIG1haW4gd2F5cyB0byBvcGVyYXRlIG9uIGBteWVmZmA6ICANCg0KMS4gVXNpbmcgUlNpZW5hIGZ1bmN0aW9ucyBgaW5jbHVkZUVmZmVjdHMoKWAsIGBzZXRFZmZlY3RzKCksIGV0YzsgIA0KMi4gQ2hhbmdpbmcgYG15ZWZmYCBpbiBzcHJlYWRzaGVldCBmb3JtIGJ5IHRoZSBmdW5jdGlvbiBgZml4KClgOyAgDQozLiBDaGFuZ2luZyBgbXllZmZgIGRpcmVjdGx5IGJ5IG9wZXJhdGluZyBvbiBpdHMgZWxlbWVudHMuICANCg0KDQpUaGUgZmlyc3Qgd2F5IGlzIG1vc3QgaW4gbGluZSB3aXRoIHRoZSBkZXNpZ24gcGhpbG9zb3BoeSBvZiBSLCBhbmQgYWxsb3dzIHlvdSB0byBzYXZlIHNjcmlwdHMgdGhhdCBhbHNvIGNhbiBiZSB1c2VkIHdoZW4gdGhlcmUgd2lsbCBiZSBuZXcgdmVyc2lvbnMgb2YgUlNpZW5hLiBUaGVyZWZvcmUsIHdlIHN1Z2dlc3QgdGhhdCBmb3Igc3RhcnRpbmcgeW91IG9ubHkgc3R1ZHkgb3B0aW9uIDEsICpBZGRpbmcvcmVtb3ZpbmcgZWZmZWN0cyB1c2luZyBpbmNsdWRlRWZmZWN0cyouICANClRoZSBvdGhlciB0d28gb3B0aW9ucyBhcmUgdHJlYXRlZCBvbmx5IGZvciBzcGVjaWFsIGFuZCBtb3JlIGRpZmZpY3VsdCBvY2Nhc2lvbnM7IGFuZCBmb3IgbG9va2luZyAnYmVoaW5kIHRoZSBzY3JlZW5zJy4gIA0KDQpGb3IgaWRlbnRpZnlpbmcgeW91ciBlZmZlY3RzIHlvdSBuZWVkIHRoZSAqc2hvcnROYW1lKnMsIHdoaWNoIGNhbiBiZSByZWFkIGluIHRoZSBtYW51YWwgKHNlY3Rpb24gIk1hdGhlbWF0aWNhbCBkZWZpbml0aW9uIG9mIGVmZmVjdHMiKSwgb3Igb2J0YWluZWQgZnJvbSB0aGUgYGVmZmVjdHNEb2N1bWVudGF0aW9uKClgIGZ1bmN0aW9uIG1lbnRpb25lZCBhYm92ZS4gIA0KDQojIyMgMS4gQWRkaW5nL3JlbW92aW5nIGVmZmVjdHMgdXNpbmcgYGluY2x1ZGVFZmZlY3RzKClgICANCg0KIyMjIyAqKnN0cnVjdHVyYWwgZWZmZWN0cyoqICANCg0KVGhlIGJlc3Qgd2F5IG9mIHNwZWNpZnlpbmcgdGhlIG1vZGVsIGlzIGJ5IHRoZSBgaW5jbHVkZUVmZmVjdHMoKWAgZnVuY3Rpb24uIFRoaXMgZnVuY3Rpb24gdXNlcyBzaG9ydCBuYW1lcyBpbnN0ZWFkIG9mIGZ1bGwgbmFtZXMuIFRoZSBzaG9ydCBuYW1lcyBhcmUgZ2l2ZW4gYnkgdGhlIGBlZmZlY3RzRG9jdW1lbnRhdGlvbigpYCBmdW5jdGlvbiBtZW50aW9uZWQgYWJvdmUsIGFuZCBhbHNvIGFyZSBsaXN0ZWQgaW4gdGhlIGRlc2NyaXB0aW9ucyBnaXZlbiBpbiBTZWN0aW9uIDEyIG9mIHRoZSBtYW51YWwuICANCg0KRm9yIGlsbHVzdHJhdGlvbiwgbGV0IHVzIHN0YXJ0IGZyb20gc2NyYXRjaCB3aXRoIGEgbmV3IHNpZW5hRWZmZWN0cyBvYmplY3QsIGFuZCBhZGQgdGhlIHRyYW5zaXRpdmUgdHJpcGxlcyBhbmQgMy1jeWNsZXMgZWZmZWN0cy4gDQoNCg0KYGBge3IsIHJlc3VsdHM9J2hvbGQnfQ0KbXllZmYgPC0gZ2V0RWZmZWN0cyggbXlkYXRhICkNCm15ZWZmIDwtIGluY2x1ZGVFZmZlY3RzKCBteWVmZiwgdHJhbnNUcmlwLCBjeWNsZTMgKQ0KIyBUbyBzZWUgdGhlIGN1cnJlbnQgbW9kZWwgc3BlY2lmaWNhdGlvbiwNCm15ZWZmDQpgYGANCg0KDQpOb3RlIHRoYXQgd2UgY2FuIHNldCBzZXZlcmFsIGVmZmVjdHMgaW4gb25lIGdvISBUbyByZW1vdmUgYW4gZWZmZWN0LCBlLmcuLCB0aGUgMy1jeWNsZSBlZmZlY3RzLi4uDQoNCmBgYHtyLCByZXN1bHRzPSdob2xkJ30NCm15ZWZmIDwtIGluY2x1ZGVFZmZlY3RzKCBteWVmZiwgY3ljbGUzLCBpbmNsdWRlPUZBTFNFICkNCg0KIyBDaGVjayBhZ2FpbiB3aGljaCBlZmZlY3RzIG5vdyBhcmUgaW5jbHVkZWQgaW4gdGhlIG1vZGVsDQpteWVmZg0KDQpgYGANCg0KIyMjIyAqKmNvdmFyaWF0ZSByZWxhdGVkIGVmZmVjdHMqKiAgDQoNCkxldCB1cyBkZW1vbnN0cmF0ZSBob3cgdG8gYWRkL3JlbW92ZSBjb3ZhcmlhdGUgcmVsYXRlZCBlZmZlY3RzLiAgDQpUaGUgc2hvcnQgbmFtZXMgZG8gbm90IGRpZmZlcmVudGlhdGUgYmV0d2VlbiB0aGUgY292YXJpYXRlczogIGUuZy4sIHRoZSBlZmZlY3RzICphbGNvaG9sIGVnbyogYW5kICpzbW9rZTEgZWdvKiBib3RoIGhhdmUgc2hvcnROYW1lICplZ29YKiwgYW5kIHRoZSBjb21tYW5kIGBteWVmZiA8LSBpbmNsdWRlRWZmZWN0cyhteWVmZiwgZWdvWClgIHJlc3VsdHMgaW4gYSBtZXNzYWdlIHRoYXQgZG9lcyBub3QgKGxpa2UgdGhlIGVhcmxpZXIgb25lKSBjb25maXJtIHRoZSBuZXdseSBpbmNsdWRlZCBlZmZlY3QuICANCg0KVGhlIGNvdmFyaWF0ZXMgYXJlIGluZGljYXRlZCBieSB0aGUgdmFyaWFibGUgKmludGVyYWN0aW9uMSogaW4gdGhlIHNpZW5hRWZmZWN0cyBvYmplY3QsIGxpc3RlZCBhcyAqaW50ZXIxKiBpbiB0aGUgcmVzdWx0IG9mIGBlZmZlY3RzRG9jdW1lbnRhdGlvbigpYCwgYW5kIHRoaXMgaGFzIHRvIGJlIG1lbnRpb25lZCB0byBpbmNsdWRlIHRoZXNlIGVmZmVjdHM6DQoNCmBgYHtyLCByZXN1bHRzPSdob2xkJ30NCm15ZWZmIDwtIGluY2x1ZGVFZmZlY3RzKCBteWVmZiwgZWdvWCwgYWx0WCwgZWdvWGFsdFgsIGludGVyYWN0aW9uMSA9ICJhbGNvaG9sIiApDQpteWVmZiA8LSBpbmNsdWRlRWZmZWN0cyggbXllZmYsIHNpbVgsIGludGVyYWN0aW9uMSA9ICJzbW9rZTEiICkNCg0KIyBXZSBjaGVjayB0aGUgcmVzdWx0cyBhZ2FpbjoNCm15ZWZmDQpgYGANCg0KDQpCeSBsb29raW5nIGF0IHRoZSBoZWxwIG9mZmVyZWQgYnkgYD9pbmNsdWRlRWZmZWN0c2AuICANCg0KeW91IGNhbiBzZWUgaG93IHRvIGluY2x1ZGUgZW5kb3dtZW50IGFuZCBjcmVhdGlvbiBlZmZlY3RzLiBFZmZlY3RzIHRoYXQgZGVwZW5kIG9uIG90aGVyIHZhcmlhYmxlcywgc3VjaCBhcyBlZ29YLCBhbHRYLCBldGMuIGFib3ZlLCBuZWVkIHRoZSBzcGVjaWZpY2F0aW9uIG9mIHRoZXNlIHZhcmlhYmxlcyB0byBkZWZpbmUgdGhlbS4gVGhpcyBpcyBkb25lIGJ5IHRoZSAqaW50ZXJhY3Rpb24xKiBwYXJhbWV0ZXIgd2hlbiBvbmx5IG9uZSB2YXJpYWJsZSBuYW1lIGlzIG5lZWRlZCwgYW5kIGJ5ICppbnRlcmFjdGlvbjIqIGlmIHRoZXJlIGlzIGEgc2Vjb25kIHZhcmlhYmxlIGludm9sdmVkLCBzdWNoIGFzIEFsdHNBdkFsdCAoc2VlIHRoZSBtYW51YWwpLiAgDQpBbHRob3VnaCB0aGUgbmFtZXMgb2YgdGhlc2UgcGFyYW1ldGVycyBhcmUgKmludGVyYWN0aW9uMSogYW5kICppbnRlcmFjdGlvbjIqLCB0aGlzIGRvZXMgbm90IHJlZmVyIHRvIHRoZSBjb21tb24gY29uY2VwdCBvZiBpbnRlcmFjdGlvbiBpbiBzdGF0aXN0aWNhbCBtb2RlbGxpbmchDQoNCg0KIyMjIyAqKmNyZWF0aW5nIGludGVyYWN0aW9uIGVmZmVjdHMqKiANCg0KDQpBcyBhIHNwZWNpYWwgdG9waWMsIGxldCB1cyBzaG93IGhvdyBpbnRlcmFjdGlvbiBlZmZlY3RzIGFyZSBjcmVhdGVkLiAgDQoNCkEgY29udmVuaWVudCBtZXRob2QgdG8gaW5jbHVkZSBhbiBpbnRlcmFjdGlvbiBpcyBvZmZlcmVkIGJ5IHRoZSBgaW5jbHVkZUludGVyYWN0aW9uKClgIGZ1bmN0aW9uLiBUaGlzIGNhbiBiZSB1c2VkIHRvIGludGVyYWN0IHR3byBvciB0aHJlZSBlZmZlY3RzIChpZiB0aGUgaW50ZXJhY3Rpb25zIGFyZSBhbGxvd2VkLCB3aGljaCBkZXBlbmRzIG9uIHRoZWlyIGludGVyYWN0aW9uVHlwZTsgc2VlIHRoZSBtYW51YWwgZm9yIHRoaXMpLiAgDQoNClRoZSBpbnRlcmFjdGlvbiBiZXR3ZWVuICoqc21va2UxIGVnbyoqIGFuZCAqKnJlY2lwcm9jaXR5KiosIGZvciBpbnN0YW5jZSwgY2FuIGJlIGRlZmluZWQgYnkgdGhlIGNvbW1hbmQ6IA0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCm15ZWZmIDwtIGluY2x1ZGVJbnRlcmFjdGlvbiggbXllZmYsIGVnb1gsIHJlY2lwLCBpbnRlcmFjdGlvbjEgPSBjKCJzbW9rZTEiLCIiKSApDQpteWVmZg0KYGBgDQoNClRoaXMgc2hvd3MgdGhlIGludGVyYWN0aW9uIGFzIGFuICJ1bnNwZWNpZmllZCBpbnRlcmFjdGlvbiBlZmZlY3QiOyBidXQgd2hlbiBwcmludGluZyByZXN1bHRzIG9mIHRoZSBlc3RpbWF0aW9uIHRoZSBuYW1lcyBvZiB0aGUgaW50ZXJhY3RpbmcgZWZmZWN0cyB3aWxsIGJlIG1lbnRpb25lZC4gDQoNCkFuIGludGVyYWN0aW9uIGJldHdlZW4gc21va2UxIGVnbyBhbmQgYWxjb2hvbCBlZ28gaXMgZGVmaW5lZCBieTogIA0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCm15ZWZmIDwtIGluY2x1ZGVJbnRlcmFjdGlvbiggbXllZmYsIGVnb1gsIGVnb1gsIGludGVyYWN0aW9uMSA9IGMoICJzbW9rZTEiLCAiYWxjb2hvbCIgKSApDQpgYGANCg0KDQpOb3RlIHRoYXQgdGhlIGtleXdvcmQgJ2ludGVyYWN0aW9uMScgdXNlZCBieSBSU2llbmEgaXMgdXNlZCBmb3IgaWRlbnRpZnlpbmcgdGhlIGNvdmFyaWF0ZSBmb3Igd2hpY2ggdGhlIGVnbyBlZmZlY3QgaXMgc2VsZWN0ZWQsIGFuZCBkb2VzIG5vdCByZWZlciB0byB0aGUgaW50ZXJhY3Rpb24gZWZmZWN0IGl0c2VsZi4gIA0KSWYgYXQgbGVhc3Qgb25lIG9mIHRoZSBpbnRlcmFjdGluZyBlZmZlY3RzIHJlcXVpcmVzIHRoZSBpbnRlcmFjdGlvbjEgcGFyYW1ldGVyIGZvciBpdCBzcGVjaWZpY2F0aW9uLCB0aGVuIHRoaXMgcGFyYW1ldGVyIGlzIGFsc28gcmVxdWlyZWQgZm9yIHRoZSBgaW5jbHVkZUludGVyYWN0aW9uKClgIGZ1bmN0aW9uLiAgDQoNClRoZW4gdGhlIHR3byBvciB0aHJlZSBpbnRlcmFjdGlvbjEgcGFyYW1ldGVycyBtdXN0IGJlIGNvbWJpbmVkIHVzaW5nIGMoKTsgdGhlIHNhbWUgZ29lcyBmb3IgaW50ZXJhY3Rpb24yLCBpZiB0aGF0IGFsc28gaXMgbmVjZXNzYXJ5IGZvciB0aGUgZGVmaW5pdGlvbi4gQXMgc2hvd24gYWJvdmUgZm9yIHRoZSByZWNpcCBlZmZlY3QsIHRoZSBpbnRlcmFjdGlvbjEgb3IgaW50ZXJhY3Rpb24yIHBhcmFtZXRlciBpcyAiIiAoaS5lLiwgYW4gZW1wdHkgc3RyaW5nKSBmb3IgZWZmZWN0cyB3aGVyZSBpdCBpcyBub3QgbmVlZGVkLiAgDQoNCiMjIyMgKiphY2Nlc3Npbmcgb3RoZXIgY2hhcmFjdGVyaXN0aWNzIG9mIGVmZmVjdHMqKiAgDQoNCkEgc2Vjb25kIHNwZWNpYWwgdG9waWMgaXMgaG93IHRvIGFjY2VzcyBvdGhlciBjaGFyYWN0ZXJpc3RpY3Mgb2YgZWZmZWN0cy4gVGhpcyBjYW4gYmUgZG9uZSBieSB0aGUgYHNldEVmZmVjdCgpYCBmdW5jdGlvbi4gRS5nLiwgdGhlIGRlbnNlIHRyaWFkcyBlZmZlY3RzIGNvdW50cyB0aGUgbnVtYmVyIG9mIHRyaXBsZXRzIHdpdGggYXQgbGVhc3QgeHggdGllcywgIHdoZXJlIHh4IGlzIHRoZSBwYXJhbWV0ZXIgb2YgdGhlIGVmZmVjdCwgd2hpY2ggY2FuIGJlIDUgb3IgNiAobm90ZSB0aGF0IDYgaXMgdGhlIG1heGltdW0gbnVtYmVyIG9mIHRpZXMgaW4gYSB0cmlwbGV0KS4gVGhlIGRlZmF1bHQgaXMgNS4gVGhpcyBpcyBjaGFuZ2VkIHRvIDYgYnkgdGhlIGNvbW1hbmQ6ICANCg0KYGBge3IsIGV2YWw9RkFMU0V9DQpteWVmZiA8LSBzZXRFZmZlY3QobXllZmYsIGRlbnNlVHJpYWRzLCBwYXJhbWV0ZXIgPSA2KQ0KbXllZmYNCmBgYA0KDQpUaGUgJ3BhcmFtZXRlcicga2V5d29yZCByZWZlcnMgdG8gdGhlIGVmZmVjdCBwYXJhbWV0ZXIsIGRlc2NyaWJlZCBpbiBTZWN0aW9uIDEyIG9mIHRoZSBtYW51YWwuDQoNCiMjIyAgMi4gQWRkaW5nL3JlbW92aW5nIGVmZmVjdHMgdXNpbmcgYGZpeCgpYCAgDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KIyBmaXggY2FsbHMgYSBkYXRhIGVkaXRvciBpbnRlcm5hbCB0byBSLCBzbyB3ZSBjYW4gbWFudWFsbHkgZWRpdCB0aGUgZWZmZWN0cy4NCg0KIyAgICAgICAgZml4KCBteWVmZiApDQoNCiMgSG93IHRvIHVzZSBmaXgoKSBpcyBwcmVzZW50ZWQgbWVyZWx5IGZvciBnZXR0aW5nIHRvIGtub3cgd2hhdCBteWVmZiBpcy4NCiMgSW4gcHJhY3RpY2FsIGFuYWx5c2lzIGl0IGlzIG1vcmUgY29udmVuaWVudA0KIyB0byB1c2Ugcm91dGluZSAiaW5jbHVkZUVmZmVjdHMiIGluc3RlYWQsIGFzIGV4cGxhaW5lZCBhYm92ZS4NCiMgZml4KCkgbWF5IG5vdCBiZSB1c2FibGUgaWYgeW91IGRvIG5vdCBoYXZlIHRjbC90ayBhdmFpbGFibGUhDQojIE5vdGUgdGhhdCB0aGUgdG9wIG9mIHRoZSBkYXRhZnJhbWUgc2hvd3MgdGhlIG5hbWVzIG9mIHRoZSBjb2x1bW5zOg0KIyBuYW1lLCBlZmZlY3ROYW1lLCBldGMuDQojIFlvdSBjYW4gZWRpdCB0aGUgImluY2x1ZGUiIGNvbHVtbiBieSBjaGFuZ2luZyB0aGUgVFJVRSBhbmQgRkFMU0UgdmFsdWVzDQojIGFzIHJlcXVpcmVkOyB3aGVuIHRoZSBlZGl0b3IgaXMgY2xvc2VkLCB0aGUgbmV3IHZhbHVlcyBhcmUgc3RvcmVkLg0KIyBXaGVuIHlvdSBtYWtlIGFuIGVycm9yLCBob3dldmVyLCB0aGUgZWZmZWN0cyBvYmplY3QgbWF5IGJlIGNvcnJ1cHRlZC4NCiMgVGhlcmVmb3JlLCB0aGlzIHdheSBvZiBhZGRpbmcgYW5kIHJlbW92aW5nIGVmZmVjdHMgaXMgbW9yZSByaXNreS4NCmBgYA0KDQojIyMgMy4gQWRkaW5nL3JlbW92aW5nIGVmZmVjdHMgYnkgZGlyZWN0IG1hbmlwdWxhdGlvbiBvZiBgbXllZmZgICANCg0KQWx0ZXJuYXRpdmVseSB3ZSBjYW4gZWRpdCB0aGUgZGF0YWZyYW1lIGRpcmVjdGx5IGJ5IHVzaW5nIFIgZnVuY3Rpb25zLiBZb3UgYXJlIGFkdmlzZWQgdG8gc2tpcCB0aGlzIHBhcnQgKCIzLiIpIGF0IGZpcnN0IGFuZCBzZWNvbmQgcmVhZGluZywgYW5kIHJlYWQgaXQgb25seSBpZiB5b3Ugd2lzaCB0byBnZXQgbW9yZSB1bmRlcnN0YW5kaW5nIG9mIHRoZSBpbnRlcm5hbCBzdHJ1Y3R1cmUgb2YgdGhlIGVmZmVjdHMgb2JqZWN0LiBUaGUgY29tbWFuZHMgYmVsb3cgYXJlIHVzZWQgdG8gc2V0ICJpbmNsdWRlIiB0byBUUlVFIG9yIEZBTFNFLCBhcyBhbiBhbHRlcm5hdGl2ZSB0byB1c2luZyB0aGUgZGF0YSBlZGl0b3IuIFRoZSAiaW5jbHVkZSIgY29sdW1uIHdpdGggdmFsdWVzIFRSVUUgb3IgRkFMU0Ugd2lsbCBhbHdheXMgYmUgbG9jYXRlZCBhdCB0aGUgOXRoIGNvbHVtbiwgYnV0IHRyYW5zaXRpdmUgdHJpcGxldHMgd2lsbCBub3QgYWx3YXlzIGJlIGF0IHRoZSAxNnRoIHJvdyBhcyB0aGlzIGRlcGVuZHMgb24gdGhlIG51bWJlciBvZiBwZXJpb2RzIGFuZCB2YXJpYWJsZXM7IGZ1cnRoZXIsIHRoZSBsaXN0IG9mIGF2YWlsYWJsZSBlZmZlY3RzIGNoYW5nZXMgb3ZlciBkaWZmZXJlbnQgdmVyc2lvbnMgb2YgUlNpZW5hLiAgDQoNClNvbWUgZXhhbXBsZXMgYXJlIHRoZSBmb2xsb3dpbmc6IA0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCm15ZWZmWzE2LDldIDwtIFRSVUUgICAjdHJhbnNpdGl2ZSB0cmlwbGVzDQpteWVmZlszNCw5XSA8LSBUUlVFICAgIzMgY3ljbGVzDQpteWVmZlszNyw5XSA8LSBUUlVFICAgI3RyYW5zaXRpdmUgdGllcw0KbXllZmZbNzcsOV0gPC0gVFJVRSAgICNpbmRlZ3JlZSBwb3B1bGFyaXR5IChzcXJ0KQ0KbXllZmZbODYsOV0gPC0gVFJVRSAgICNvdXRkZWdyZWUgcG9wdWxhcml0eSAoc3FydCkNCm15ZWZmWzkzLDldIDwtIFRSVUUgICAjaW5kZWdyZWUgYmFzZWQgYWN0aXZpdHkgKHNxcnQpDQpteWVmZlsxMDIsOV0gPC0gVFJVRSAgI291dGRlZ3JlZSBiYXNlZCBhY3Rpdml0eSAoc3FydCkNCm15ZWZmWzE1NSw5XSA8LSBUUlVFICAgI2luZGVncmVlLWluZGVncmVlIGFzc29ydGF0aXZpdHkNCm15ZWZmWzMxOSw5XSA8LSBUUlVFICAgI2FsY29ob2wgYWx0ZXINCm15ZWZmWzMzMSw5XSA8LSBUUlVFICAgI2FsY29ob2wgZWdvDQpteWVmZlszNzAsOV0gPC0gVFJVRSAgICNhbGNvaG9sIHNpbWlsYXJpdHkNCm15ZWZmWzQzOSw5XSA8LSBUUlVFICAgI2FsY29ob2wgZWdvIHggYWxjb2hvbCBhbHRlcg0KYGBgDQoNCkJ1dCBpbiBvdGhlciBjaG9pY2VzIG9mIGRhdGEsIHRoZSBlZmZlY3QgbnVtYmVycyB3aWxsIGNoYW5nZS4gVGhpcyBpcyBhIHJlYXNvbiB3aHkgdGhpcyBpcyBub3QgYSBjb252ZW5pZW50IG1ldGhvZC4NCg0KIyMgU2F2ZSB5b3VyIGRhdGEgaWYgeW91IHdoaXNoLiAgDQpgYGB7ciwgZXZhbD1GQUxTRX0NCg0Kc2F2ZS5pbWFnZSgiV29ya3NwYWNlUnNjcmlwdDAyLlJEYXRhIikNCg0KYGBgDQoNCg0KLS0tIA0KDQpbKipOZXh0OiBTaWVuYVJ1bk1vZGVsIEZPUiBQQVJBTUVURVIgRVNUSU1BVElPTiBCWSBSU0lFTkEqKl0oL3JzY3JpcHQwMy5odG1sKQ0KDQotLS0NCg0KDQo=

Copyright © 2020 Jochem Tolsma