Setup
The code for this tutorial can be found at: http://rpubs.com/hywel/lmem_workshop
Before starting the tutorial load the required packages and the data sets we will use for this seminar:
We will be using R version 3.3.0 and Rstudio version 0.99.902. Any recent versions of these applications will do and the syntax should run on all platforms.
###########################################################
#### Load the libraries ###
# install.packages(c("lmerTest","lattice","ez"))
# Use the above command if these packages are not installed.
library(lmerTest)
library(lattice)
library(ez)
#### Load the functions and the datasets ###
pfadu = "http://www.phonetik.uni-muenchen.de/~jmh/lehre/Rdf"
source(file.path(pfadu, "phoc.txt"))
source(file.path(pfadu, "ph.step.R"))
#### Datasets ####
stimm = read.table(file.path(pfadu, "stimm.df.txt"))
stimm2 = read.table(file.path(pfadu, "stimm2.df.txt"))
soa = read.table(file.path(pfadu, "soa.txt"))
int = read.table(file.path(pfadu, "dbwort.df.txt"))
###########################################################
Introduction
This seminar mainly concerns model building for Linear Mixed Effects models (LMEM) and how to construct a dataset that is suitable for use in modern statisistical analyses. These materials have been translated from the German by Hywel (and Google Translate). Most of the variable names remain in German and the expression in some of the harder to translate sections may be a little clumsy. The original can be found here: http://www.phonetik.uni-muenchen.de/~jmh/lehre/sem/ss16/statistik.htm: Gemischte Modelle (mixed models) mmbefehle.R. Suggestions of clearer, more semantically coherent translations are welcomed.
General Linear Mixed Models
In a mixed model (LMM) a dependent variable (continuous or categorical) is examined to see if it is influenced by one or more independent factors. Mixed models (LMM) have a broad application. They partially replace the analysis of variance; They enhance linear and logistic regression analyses.
Some benefits (A-D) that come from a LMM vs. analysis of variance (ANOVA).
A. An LMM is similar to an ANOVA
ANOVAs can be used a mixed design and have random effects included.
But unlike an ANOVA a LMM does not have to be averaged over repetitions. (Don’t worry about the ezANOVA syntax in this example, this is just to show that it is possible to run one on these data).
For example:
head(stimm)
Note: The variables in stimm are:
- Voice Onset Time (
vot)
- Speaker (
Vpn) verschiedene Sprecher or “different speakers”?
- Place of Articulation (
K) a factor with 2 levels “ba” and “pa”.
- Age (
Alter) with 2 levels adults (Erw) and children (Kind)
To what extent is the VOT (vot) influenced by place of articulation and age?
- First step:
stimm.m = aggregate(vot ~ K * Vpn * Alter, mean, data = stimm)
ezANOVA(stimm.m, .(vot), .(Vpn), .(K))
So the ANOVA shows that place of articulation (K) does have a significant effect on VOT (p < 0.05).
B. A Balanced-design is not required in a LMM.
Balanced design: In a balanced design, the number of subjects must be equal to each stage of the between-factor.. That is clearly not given for the between-age factor.
For these data:
with(stimm, table(Vpn, Alter))
In the table above we can see that there are 3 adults and 5 children. Therefore we cannot use an ANOVA as these data are not balanced and the cde below will produce an error:
ezANOVA(stimm.m, .(vot), .(Vpn), .(K), between = .(Alter))
This form of data is no problem for an LMM.
C. Missing Values Are Allowed
In an ANOVA each stage of the within-group-factor must be occupied. This is not true for a Linear Mixed Model.
with(stimm2, table(Vpn, K))
We see both speaker (Vpn) A and speaker D have missing values (“ba” for A and “pa” for D). Therefore an ANOVA can not be performed.
stimm2.m = aggregate(vot ~ K * Vpn * Alter, mean, data = stimm2)
ezANOVA(stimm2.m, .(vot), .(Vpn), .(K), between = .(Alter))
Here too a LMM will have no problems.
D. Exclude the variability within a factor.
In an ANOVA, the variability of factors that are part of a greater population (for example, subjects. OR word) are excluded. There is no such restriction in a LMM (see 2 below).
E. A disadvantage of mixed models:
A LMM is not stable at a small number of observations: less than about 60 the LMM often does not converge (although this also depends on the number of factors).
What are Fixed Factors and Random Factors?
This concept is central to the model design and this is what makes this a ‘mixed’ model.
- Fixed Factors: One or more factors that are to be tested
- Random Factors: One or more factors, for which the variability is to be excluded or reduced.
In phonetics are often subject and / or Item (materials such as Words) Random Factors.
An Example:
20 speaker produced several Words, in phrase medial or phrase final position and vowel duration was measured. To what extent the vowel duration affected by the phrase position?
Dataset: soa
- Dependent variable: time
- Fixed Factor: Phrase item (medial vs. final).
- Random Factors: subjects, word (we want to remove the variability that arises from this variable).
What is calculated in an LMM?
A LMM is similar to the linear regression in some respects. We won’t go through this in more detail but a very good introduction by Bodo Winter can be found here http://arxiv.org/pdf/1308.5499.pdf
More about random factors and the choice between (FF | RF) and (1 | RF)
There are two ways to declare a random factor: (FF | RF) or (1 | RF), where FF is a fixed factor and RF is a random factor
head(soa)
Three speakers produced “Bad”, “Pfad”, “Start” either in phrases medial or final position. To what extent does F1 affects the vowel of the phrase item?
- Dependent variable: F1
- Fixed Factor (FF): Pos (medial/final)
- Random Factors (RF): Vpn (Speaker), W (Word)
Therefore there are 4 options when constructing a model:
# option 1.
soa.lmer.1 = lmer(F1 ~ Pos + (Pos| Vpn) + (Pos | W), data = soa)
# option 2.
soa.lmer.2 = lmer(F1 ~ Pos + (1| Vpn) + (Pos | W), data = soa)
# option 3.
soa.lmer.3 = lmer(F1 ~ Pos + (Pos| Vpn) + (1 | W), data = soa)
# option 4.
soa.lmer.4 = lmer(F1 ~ Pos + (1| Vpn) + (1 | W), data = soa)
Basically we will use (FF|RF) (as shown in option 1.), unless the FF is clearly ‘between’ in relation to the RF. For the example above, FF (Pos) is ‘within’ with respect to the RF, speaker (Vpn):
# FF (Pos) is 'within' with respect to the RF, speaker (Vpn):
with(soa, table(Vpn, Pos))
FF is also ‘within’ with regard to word (Wort)
# FF is also 'within' with regard to word (Wort)
with(soa, table(W, Pos))
The importance of (FF | RF)
When constructing the models if we include (FF | RF)
- A slope RF.m and Intercept RF.k will be calculated
- (Important): the variability of the RF is excluded, assuming that RF and FF could interact.
i.e. with (Pos | Vpn), examines the extent Position and Speaker (Vpn) interact with each other:
bwplot(F1 ~ Pos|Vpn, data = soa)
Also with (Pos | W), examines the extent Pos and W (word) interact.
bwplot(F1 ~ Pos|W, data = soa)
The importance of (1 | RF)
- An Intercept RF.k (but no slope) is calculated.
- (Important): the variability of the RF is excluded without the interaction between RF and FF.
- (1 | RF) to be used when the FF ‘between’ is related to RF. e.g.
head(stimm)
To what extent is vot affected by age? * Fixed factor: age (Alter) * Random factor: Speaker (Vpn) * Age is between the reference to Vpn (speaker):
with(stimm, table(Vpn, Alter))
Age and speaker can not interact because each speaker is either a child or adult:
bwplot(vot ~ Alter|Vpn, data = stimm)
Therefore (Alter | Vpn) would be meaningless - since this requires that age (Alter) and Vpn (speaker) can interact, which is not possible when FF is ‘between’ with regard to the RF.
Therefore:
stimm3 = lmer(vot ~ Alter + (1|Vpn), data = stimm)
and not (Age | Vpn) is used in the final model.
Generally: always use (FF | RF) except when the FF is ‘between’ in relation to RF.
The Test Statistics
This is probably the most important part of the tutorial snd the part that needs the most explanation.
- The
step () function
- simplifying the model if possible
- examine whether the factors are significant in the simplified model.
The dataframe stimm measures the VOT for a number of syllables (/ ba, pa /) produced by both children and adults (encoded in an age variable (Alter)). To what extent is VOT affected by the place of articulation (K factor) and / or age of speaker (whether the speaker is a child or an adult: Alter factor) ?
Part A: Simplifying the model
First Plot the Interactions
bwplot(vot ~ K | Alter, data = stimm)
# Linear Mixed Model
stimm.lmer = lmer(vot ~ K * Alter + (K|Vpn), data = stimm)
stimm.step = step(stimm.lmer)
stimm.step
# ph.step(stimm.step)
# Part (a): Model simplification
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Do we really need K + Alter + K: Alter + (K | Vpn)?
Do we really need K + Alter + K: Alter + (K | Vpn)?
Note: this is equivalent notation to K * Alter + (K | Vpn).
If we look at the significance of both the fixed effects and the random effects we see that some of the interactions are not contributing to the model.
Random effects:
Fixed effects:
| K:Alter |
1.5466 |
1.5466 |
1 |
6 |
0.3511 |
1 |
| Alter |
3.3045 |
3.3045 |
1 |
6 |
0.7501 |
2 |
| K |
180.8614 |
180.8614 |
1 |
7 |
41.0563 |
kept |
Random effects is almost significant (p = 0.08). Hence (K | Vpn) is retained. Therefore kept i.e. (K | Vpn) was not, so (1 | Vpn) replaced.
In the fixed-effects. K:Alter and Alter are not significant, and were removed from the model. But the factor K (place of articulation) remains (kept).
The formula used is:
- formula = vot ~ K + (K | Vpn), data = stimm,
The Simplified Model:
stimm.lmer <- lmer(vot ~ K + (K | Vpn), data = stimm)
stimm.step <- step(stimm.lmer)
stimm.step
The Final Model:
lme4::lmer(formula = vot ~ K + (K | Vpn), data = stimm)
Part B: The test statistic (post hoc tests)
for the simplified model for the above data:
| K:Alter |
1.5466 |
1.5466 |
1 |
6 |
0.3511 |
1 |
0.5751 |
| Alter |
3.3045 |
3.3045 |
1 |
6 |
0.7501 |
2 |
0.4197 |
| K |
180.8614 |
180.8614 |
1 |
7 |
41.0563 |
kept |
0.0004 |
# as you can see here:
stimm2.lmer = lmer(vot ~ K + (K | Vpn), data = stimm)
anova(stimm2.lmer)
Which means that: VOT is significant for a main effect of place of articulation (F [1, 7] = 41.1, p <0.001) but is not influenced by age. Futhermore there is no interaction between these factors.
Post-hoc Tests
Post hoc test are needed if there is an interaction recorded between the fixed factors. These are carried out in a very similar manner to ANOVAs
Here we have another dataset
head(int)
10 speakers (Vpn) produced 6 different words (Wort) with either an / i / or / a / vowel (factor V). The intensity was (dependent variable db) was measured per vowel. The speakers were either male (m) or female (f) in a factor G.
When constructing the model it was found that V and G
#Therefore :
int.lmer = lmer(db ~ V * G + (V+G|Wort) + (V|Vpn), data = int)
int.step = step(int.lmer)
int.step
Based on the above table the final model will be:
# Final model:
# lme4::lmer(formula = db ~ V + G + (V | Vpn) + (1 | Wort) + V:G,
There is a significant interaction:
# There is a significant interaction:
# Fixed effects:
# Sum Sq Mean Sq NumDF DenDF F.value elim.num Pr(>F)
# V 126.6597 126.6597 1 8 29.8298 kept 0.0006
# G 47.3133 47.3133 1 8 11.1428 kept 0.0103
# V:G 31.9690 31.9690 1 8 7.5291 kept 0.0253
Therefore, we must apply a post-hoc test.
#
(int.ph=ph.step(int.step, "bonferroni"))
#
Post-hoc Bonferroni corrected t-tests showed significant differences between / i / and / a / in men (p <0.01) but not in women; and significant differences between men and women in / a / (p <0.05) but not in / i /.
LS0tCnRpdGxlOiBBIExpbmVhciBNaXhlZCBNb2RlbCBUdXRvcmlhbCwgYmFzZWQgb24gYSBtYXRlcmlhbHMgYnkgSm9uYXRoYW4gSGFycmluZ3RvbiwKICBJUFMsIE11bmljaAphdXRob3I6ICdQcmVzZW50ZWQgYnk6IEh5d2VsIFN0b2FrZXMgYW5kIEpvc2h1YSBDbG90aGllcicKZGF0ZTogIjI2dGggQXVndXN0IDIwMTYiCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKIyBTZXR1cCAKClRoZSBjb2RlIGZvciB0aGlzIHR1dG9yaWFsIGNhbiBiZSBmb3VuZCBhdDogPGh0dHA6Ly9ycHVicy5jb20vaHl3ZWwvbG1lbV93b3Jrc2hvcD4KCkJlZm9yZSBzdGFydGluZyB0aGUgdHV0b3JpYWwgbG9hZCB0aGUgcmVxdWlyZWQgcGFja2FnZXMgYW5kIHRoZSBkYXRhIHNldHMgd2Ugd2lsbCB1c2UgZm9yIHRoaXMgc2VtaW5hcjogIAoKV2Ugd2lsbCBiZSB1c2luZyBgUiB2ZXJzaW9uIDMuMy4wYCBhbmQgUnN0dWRpbyBgdmVyc2lvbiAwLjk5LjkwMmAuIEFueSByZWNlbnQgdmVyc2lvbnMgb2YgdGhlc2UgYXBwbGljYXRpb25zIHdpbGwgZG8gYW5kIHRoZSBzeW50YXggc2hvdWxkIHJ1biBvbiBhbGwgcGxhdGZvcm1zLgoKYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyMjIyBMb2FkIHRoZSBsaWJyYXJpZXMgIyMjCiMgaW5zdGFsbC5wYWNrYWdlcyhjKCJsbWVyVGVzdCIsImxhdHRpY2UiLCJleiIpKQojIFVzZSB0aGUgYWJvdmUgY29tbWFuZCBpZiB0aGVzZSBwYWNrYWdlcyBhcmUgbm90IGluc3RhbGxlZC4KbGlicmFyeShsbWVyVGVzdCkKbGlicmFyeShsYXR0aWNlKQpsaWJyYXJ5KGV6KQpgYGAKYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSxjYWNoZT1UUlVFfQojIyMjIExvYWQgdGhlIGZ1bmN0aW9ucyBhbmQgdGhlIGRhdGFzZXRzICMjIwpwZmFkdSA9ICJodHRwOi8vd3d3LnBob25ldGlrLnVuaS1tdWVuY2hlbi5kZS9+am1oL2xlaHJlL1JkZiIKc291cmNlKGZpbGUucGF0aChwZmFkdSwgInBob2MudHh0IikpCnNvdXJjZShmaWxlLnBhdGgocGZhZHUsICJwaC5zdGVwLlIiKSkKCiMjIyMgRGF0YXNldHMgIyMjIwpzdGltbSA9IHJlYWQudGFibGUoZmlsZS5wYXRoKHBmYWR1LCAic3RpbW0uZGYudHh0IikpCnN0aW1tMiA9IHJlYWQudGFibGUoZmlsZS5wYXRoKHBmYWR1LCAic3RpbW0yLmRmLnR4dCIpKQpzb2EgPSByZWFkLnRhYmxlKGZpbGUucGF0aChwZmFkdSwgInNvYS50eHQiKSkKaW50ID0gcmVhZC50YWJsZShmaWxlLnBhdGgocGZhZHUsICJkYndvcnQuZGYudHh0IikpCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCmBgYAoqKioKIyBJbnRyb2R1Y3Rpb24gCgpUaGlzIHNlbWluYXIgbWFpbmx5IGNvbmNlcm5zIG1vZGVsIGJ1aWxkaW5nIGZvciBMaW5lYXIgTWl4ZWQgRWZmZWN0cyBtb2RlbHMgKExNRU0pIGFuZCBob3cgdG8gY29uc3RydWN0IGEgZGF0YXNldCB0aGF0IGlzIHN1aXRhYmxlIGZvciB1c2UgaW4gbW9kZXJuIHN0YXRpc2lzdGljYWwgYW5hbHlzZXMuIFRoZXNlIG1hdGVyaWFscyBoYXZlIGJlZW4gdHJhbnNsYXRlZCBmcm9tIHRoZSBHZXJtYW4gYnkgSHl3ZWwgKGFuZCBHb29nbGUgVHJhbnNsYXRlKS4gTW9zdCBvZiB0aGUgdmFyaWFibGUgbmFtZXMgcmVtYWluIGluIEdlcm1hbiBhbmQgdGhlIGV4cHJlc3Npb24gaW4gc29tZSBvZiB0aGUgaGFyZGVyIHRvIHRyYW5zbGF0ZSBzZWN0aW9ucyBtYXkgYmUgYSBsaXR0bGUgY2x1bXN5LiBUaGUgb3JpZ2luYWwgY2FuIGJlIGZvdW5kIGhlcmU6IAo8aHR0cDovL3d3dy5waG9uZXRpay51bmktbXVlbmNoZW4uZGUvfmptaC9sZWhyZS9zZW0vc3MxNi9zdGF0aXN0aWsuaHRtPjoKR2VtaXNjaHRlIE1vZGVsbGUgKG1peGVkIG1vZGVscykgW21tYmVmZWhsZS5SXShodHRwOi8vd3d3LnBob25ldGlrLnVuaS1tdWVuY2hlbi5kZS9+am1oL2xlaHJlL3NlbS9zczE2L21tYmVmZWhsZS5SKS4gU3VnZ2VzdGlvbnMgb2YgY2xlYXJlciwgbW9yZSBzZW1hbnRpY2FsbHkgY29oZXJlbnQgdHJhbnNsYXRpb25zIGFyZSB3ZWxjb21lZC4KCiMgR2VuZXJhbCBMaW5lYXIgTWl4ZWQgTW9kZWxzIFteMV0KSW4gYSBtaXhlZCBtb2RlbCAoTE1NKSBhIGRlcGVuZGVudCB2YXJpYWJsZSAoY29udGludW91cyBvciBjYXRlZ29yaWNhbCkgaXMgZXhhbWluZWQgdG8gc2VlIGlmIGl0IGlzIGluZmx1ZW5jZWQgYnkgb25lIG9yIG1vcmUgaW5kZXBlbmRlbnQgZmFjdG9ycy4gTWl4ZWQgbW9kZWxzIChMTU0pIGhhdmUgYSBicm9hZCBhcHBsaWNhdGlvbi4gVGhleSBwYXJ0aWFsbHkgcmVwbGFjZSB0aGUgYW5hbHlzaXMgb2YgdmFyaWFuY2U7IFRoZXkgZW5oYW5jZSBsaW5lYXIgYW5kIGxvZ2lzdGljIHJlZ3Jlc3Npb24gYW5hbHlzZXMuCgpbXjFdOiBOb3RlOiBhcyBtZW50aW9uZWQgYWJvdmUgYWxsIHZhcmlhYmxlcyBpbiB0aGlzIHR1dG9yaWFsIGFyZSBpbiBHZXJtYW4KCioqKioqKioqKgoKIyBTb21lIGJlbmVmaXRzIChBLUQpICB0aGF0IGNvbWUgZnJvbSBhIExNTSB2cy4gYW5hbHlzaXMgb2YgdmFyaWFuY2UgKEFOT1ZBKS4KCiMjIEEuIEFuIExNTSBpcyBzaW1pbGFyIHRvIGFuIEFOT1ZBIApBTk9WQXMgY2FuIGJlIHVzZWQgYSBtaXhlZCBkZXNpZ24gYW5kIGhhdmUgcmFuZG9tIGVmZmVjdHMgaW5jbHVkZWQuICAKCgpCdXQgdW5saWtlIGFuIEFOT1ZBIGEgTE1NIGRvZXMgbm90IGhhdmUgdG8gYmUgYXZlcmFnZWQgb3ZlciByZXBldGl0aW9ucy4KKERvbid0IHdvcnJ5IGFib3V0IHRoZSBlekFOT1ZBIHN5bnRheCBpbiB0aGlzIGV4YW1wbGUsIHRoaXMgaXMganVzdCB0byBzaG93IHRoYXQgaXQgaXMgcG9zc2libGUgdG8gcnVuIG9uZSBvbiB0aGVzZSBkYXRhKS4gIAoKRm9yIGV4YW1wbGU6CmBgYHtyLGNhY2hlPVRSVUV9CmhlYWQoc3RpbW0pCmBgYApOb3RlOiBUaGUgdmFyaWFibGVzIGluIGBzdGltbWAgYXJlOiAgCgotIFZvaWNlIE9uc2V0IFRpbWUgKGB2b3RgKQotIFNwZWFrZXIgKGBWcG5gKSAqdmVyc2NoaWVkZW5lIFNwcmVjaGVyKiBvciAiZGlmZmVyZW50IHNwZWFrZXJzIj8KLSBQbGFjZSBvZiBBcnRpY3VsYXRpb24gKGBLYCkgYSBmYWN0b3Igd2l0aCAyIGxldmVscyAiYmEiIGFuZCAicGEiLgotIEFnZSAoYEFsdGVyYCkgd2l0aCAyIGxldmVscyBhZHVsdHMgKGBFcndgKSBhbmQgY2hpbGRyZW4gKGBLaW5kYCkKClRvIHdoYXQgZXh0ZW50IGlzIHRoZSBWT1QgKHZvdCkgaW5mbHVlbmNlZCBieSBwbGFjZSBvZiBhcnRpY3VsYXRpb24gYW5kIGFnZT8KCjEuIEZpcnN0IHN0ZXA6CgpgYGB7cixjYWNoZT1UUlVFfQoKc3RpbW0ubSA9IGFnZ3JlZ2F0ZSh2b3QgfiBLICogVnBuICogQWx0ZXIsIG1lYW4sIGRhdGEgPSBzdGltbSkKZXpBTk9WQShzdGltbS5tLCAuKHZvdCksIC4oVnBuKSwgLihLKSkKYGBgCgpTbyB0aGUgQU5PVkEgc2hvd3MgdGhhdCBwbGFjZSBvZiBhcnRpY3VsYXRpb24gKEspIGRvZXMgaGF2ZSBhIHNpZ25pZmljYW50IGVmZmVjdCBvbiBWT1QgKHAgPCAwLjA1KS4KCiMjIEIuIEEgQmFsYW5jZWQtZGVzaWduIGlzIG5vdCByZXF1aXJlZCBpbiBhIExNTS4KCipCYWxhbmNlZCBkZXNpZ246KiBJbiBhIGJhbGFuY2VkIGRlc2lnbiwgdGhlIG51bWJlciBvZiBzdWJqZWN0cyBtdXN0IGJlIGVxdWFsIHRvIGVhY2ggc3RhZ2Ugb2YgdGhlIGJldHdlZW4tZmFjdG9yLi4gVGhhdCBpcyBjbGVhcmx5IG5vdCBnaXZlbiBmb3IgdGhlIGJldHdlZW4tYWdlIGZhY3Rvci4KCkZvciB0aGVzZSBkYXRhOgpgYGB7cixjYWNoZT1UUlVFfQp3aXRoKHN0aW1tLCB0YWJsZShWcG4sIEFsdGVyKSkKYGBgCkluIHRoZSB0YWJsZSBhYm92ZSB3ZSBjYW4gc2VlIHRoYXQgdGhlcmUgYXJlIDMgYWR1bHRzIGFuZCA1IGNoaWxkcmVuLiBUaGVyZWZvcmUgd2UgY2Fubm90IHVzZSBhbiBBTk9WQSBhcyB0aGVzZSBkYXRhIGFyZSBub3QgYmFsYW5jZWQgYW5kIHRoZSBjZGUgYmVsb3cgd2lsbCBwcm9kdWNlIGFuIGVycm9yOgoKYGBge3J9CmV6QU5PVkEoc3RpbW0ubSwgLih2b3QpLCAuKFZwbiksIC4oSyksIGJldHdlZW4gPSAuKEFsdGVyKSkKYGBgClRoaXMgZm9ybSBvZiBkYXRhIGlzIG5vIHByb2JsZW0gZm9yIGFuIExNTS4KCiMjIEMuIE1pc3NpbmcgVmFsdWVzIEFyZSBBbGxvd2VkCkluIGFuIEFOT1ZBIGVhY2ggc3RhZ2Ugb2YgdGhlIHdpdGhpbi1ncm91cC1mYWN0b3IgbXVzdCBiZSBvY2N1cGllZC4gVGhpcyBpcyBub3QgdHJ1ZSBmb3IgYSBMaW5lYXIgTWl4ZWQgTW9kZWwuCgpgYGB7cn0Kd2l0aChzdGltbTIsIHRhYmxlKFZwbiwgSykpCmBgYApXZSBzZWUgYm90aCBzcGVha2VyIChgVnBuYCkgQSBhbmQgc3BlYWtlciBEIGhhdmUgbWlzc2luZyB2YWx1ZXMgKCJiYSIgZm9yIEEgYW5kICJwYSIgZm9yIEQpLiBUaGVyZWZvcmUgYW4gQU5PVkEgY2FuIG5vdCBiZSBwZXJmb3JtZWQuIAoKYGBge3IsY2FjaGU9VFJVRSwgZXZhbD1GQUxTRSwgY2FjaGU9VFJVRX0Kc3RpbW0yLm0gPSBhZ2dyZWdhdGUodm90IH4gSyAqIFZwbiAqIEFsdGVyLCBtZWFuLCBkYXRhID0gc3RpbW0yKQplekFOT1ZBKHN0aW1tMi5tLCAuKHZvdCksIC4oVnBuKSwgLihLKSwgYmV0d2VlbiA9IC4oQWx0ZXIpKQpgYGAKSGVyZSB0b28gYSBMTU0gd2lsbCBoYXZlIG5vIHByb2JsZW1zLgoKIyMgRC4gRXhjbHVkZSB0aGUgdmFyaWFiaWxpdHkgd2l0aGluIGEgZmFjdG9yLgoKSW4gYW4gQU5PVkEsIHRoZSB2YXJpYWJpbGl0eSBvZiBmYWN0b3JzIHRoYXQgYXJlIHBhcnQgb2YgYSBncmVhdGVyIHBvcHVsYXRpb24gKGZvciBleGFtcGxlLCBzdWJqZWN0cy4gT1Igd29yZCkgYXJlIGV4Y2x1ZGVkLiBUaGVyZSBpcyBubyBzdWNoIHJlc3RyaWN0aW9uIGluIGEgTE1NIChzZWUgMiBiZWxvdykuCgojIyBFLiBBIGRpc2FkdmFudGFnZSBvZiBtaXhlZCBtb2RlbHM6CgpBIExNTSBpcyBub3Qgc3RhYmxlIGF0IGEgc21hbGwgbnVtYmVyIG9mIG9ic2VydmF0aW9uczogbGVzcyB0aGFuIGFib3V0IDYwIHRoZSBMTU0gb2Z0ZW4gZG9lcyBub3QgY29udmVyZ2UgKGFsdGhvdWdoIHRoaXMgYWxzbyBkZXBlbmRzIG9uIHRoZSBudW1iZXIgb2YgZmFjdG9ycykuCgojIFdoYXQgYXJlIEZpeGVkIEZhY3RvcnMgYW5kIFJhbmRvbSBGYWN0b3JzPwpUaGlzIGNvbmNlcHQgaXMgY2VudHJhbCB0byB0aGUgbW9kZWwgZGVzaWduIGFuZCB0aGlzIGlzIHdoYXQgbWFrZXMgdGhpcyBhICdtaXhlZCcgbW9kZWwuIAoKKiBGaXhlZCBGYWN0b3JzOiBPbmUgb3IgbW9yZSBmYWN0b3JzIHRoYXQgYXJlIHRvIGJlIHRlc3RlZAoqIFJhbmRvbSBGYWN0b3JzOiBPbmUgb3IgbW9yZSBmYWN0b3JzLCBmb3Igd2hpY2ggdGhlIHZhcmlhYmlsaXR5IGlzIHRvIGJlIGV4Y2x1ZGVkIG9yIHJlZHVjZWQuCgpJbiBwaG9uZXRpY3MgYXJlIG9mdGVuIHN1YmplY3QgYW5kIC8gb3IgSXRlbSAobWF0ZXJpYWxzIHN1Y2ggYXMgV29yZHMpIFJhbmRvbSBGYWN0b3JzLgoKIyMjIEFuIEV4YW1wbGU6CgoyMCBzcGVha2VyIHByb2R1Y2VkIHNldmVyYWwgV29yZHMsIGluIHBocmFzZSBtZWRpYWwgb3IgcGhyYXNlIGZpbmFsIHBvc2l0aW9uIGFuZCB2b3dlbCBkdXJhdGlvbiB3YXMgbWVhc3VyZWQuIFRvIHdoYXQgZXh0ZW50IHRoZSB2b3dlbCBkdXJhdGlvbiBhZmZlY3RlZCBieSB0aGUgcGhyYXNlIHBvc2l0aW9uPwoKRGF0YXNldDogYHNvYWAKCiogRGVwZW5kZW50IHZhcmlhYmxlOiB0aW1lCiogRml4ZWQgRmFjdG9yOiBQaHJhc2UgaXRlbSAobWVkaWFsICB2cy4gZmluYWwpLgoqIFJhbmRvbSBGYWN0b3JzOiBzdWJqZWN0cywgd29yZCAod2Ugd2FudCB0byByZW1vdmUgdGhlIHZhcmlhYmlsaXR5IHRoYXQgYXJpc2VzIGZyb20gdGhpcyB2YXJpYWJsZSkuCgojIyMgV2hhdCBpcyBjYWxjdWxhdGVkIGluIGFuIExNTT8KQSBMTU0gaXMgc2ltaWxhciB0byB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gaW4gc29tZSByZXNwZWN0cy4KV2Ugd29uJ3QgZ28gdGhyb3VnaCB0aGlzIGluIG1vcmUgZGV0YWlsIGJ1dCBhIHZlcnkgZ29vZCBpbnRyb2R1Y3Rpb24gYnkgQm9kbyBXaW50ZXIgY2FuIGJlIGZvdW5kIGhlcmUgPGh0dHA6Ly9hcnhpdi5vcmcvcGRmLzEzMDguNTQ5OS5wZGY+CgoKIyMgTW9yZSBhYm91dCByYW5kb20gZmFjdG9ycyBhbmQgdGhlIGNob2ljZSBiZXR3ZWVuIChGRiB8IFJGKSBhbmQgKDEgfCBSRikKClRoZXJlIGFyZSB0d28gd2F5cyB0byBkZWNsYXJlIGEgcmFuZG9tIGZhY3RvcjogKEZGIHwgUkYpIG9yICgxIHwgUkYpLCB3aGVyZSBGRiBpcyBhIGZpeGVkIGZhY3RvciBhbmQgUkYgaXMgYSByYW5kb20gZmFjdG9yCgpgYGB7cn0KaGVhZChzb2EpCmBgYAoKVGhyZWUgc3BlYWtlcnMgcHJvZHVjZWQgIkJhZCIsICJQZmFkIiwgIlN0YXJ0IiBlaXRoZXIgaW4gcGhyYXNlcyBtZWRpYWwgb3IgZmluYWwgcG9zaXRpb24uIFRvIHdoYXQgZXh0ZW50IGRvZXMgRjEgYWZmZWN0cyB0aGUgdm93ZWwgb2YgdGhlIHBocmFzZSBpdGVtPwoKKiBEZXBlbmRlbnQgdmFyaWFibGU6IEYxCiogRml4ZWQgRmFjdG9yIChGRik6ICBQb3MgKG1lZGlhbC9maW5hbCkKKiBSYW5kb20gRmFjdG9ycyAoUkYpOiBWcG4gKFNwZWFrZXIpLCBXIChXb3JkKQoKVGhlcmVmb3JlIHRoZXJlIGFyZSA0IG9wdGlvbnMgd2hlbiBjb25zdHJ1Y3RpbmcgYSBtb2RlbDoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgb3B0aW9uIDEuCnNvYS5sbWVyLjEgPSBsbWVyKEYxIH4gUG9zICsgKFBvc3wgVnBuKSArIChQb3MgfCBXKSwgZGF0YSA9IHNvYSkKIyBvcHRpb24gMi4Kc29hLmxtZXIuMiA9IGxtZXIoRjEgfiBQb3MgKyAoMXwgVnBuKSArIChQb3MgfCBXKSwgZGF0YSA9IHNvYSkKIyBvcHRpb24gMy4Kc29hLmxtZXIuMyA9IGxtZXIoRjEgfiBQb3MgKyAoUG9zfCBWcG4pICsgKDEgfCBXKSwgZGF0YSA9IHNvYSkKIyBvcHRpb24gNC4Kc29hLmxtZXIuNCA9IGxtZXIoRjEgfiBQb3MgKyAoMXwgVnBuKSArICgxIHwgVyksIGRhdGEgPSBzb2EpCmBgYAoKQmFzaWNhbGx5IHdlIHdpbGwgdXNlIChGRnxSRikgIChhcyBzaG93biBpbiBvcHRpb24gMS4pLCB1bmxlc3MgdGhlIEZGIGlzIGNsZWFybHkgJ2JldHdlZW4nIGluIHJlbGF0aW9uIHRvIHRoZSBSRi4KRm9yIHRoZSBleGFtcGxlIGFib3ZlLCBGRiAoUG9zKSBpcyAnd2l0aGluJyB3aXRoIHJlc3BlY3QgdG8gdGhlIFJGLCBzcGVha2VyIChWcG4pOgoKYGBge3J9CiMgRkYgKFBvcykgaXMgJ3dpdGhpbicgd2l0aCByZXNwZWN0IHRvIHRoZSBSRiwgc3BlYWtlciAoVnBuKToKd2l0aChzb2EsIHRhYmxlKFZwbiwgUG9zKSkKYGBgCkZGIGlzIGFsc28gJ3dpdGhpbicgd2l0aCByZWdhcmQgdG8gd29yZCAoV29ydCkKYGBge3J9CiMgRkYgaXMgYWxzbyAnd2l0aGluJyB3aXRoIHJlZ2FyZCB0byB3b3JkIChXb3J0KQp3aXRoKHNvYSwgdGFibGUoVywgUG9zKSkKYGBgCgojIFRoZSBpbXBvcnRhbmNlIG9mIChGRiB8IFJGKQoKV2hlbiBjb25zdHJ1Y3RpbmcgdGhlIG1vZGVscyBpZiB3ZSBpbmNsdWRlIChGRiB8IFJGKQoKMS4gQSBzbG9wZSBSRi5tIGFuZCBJbnRlcmNlcHQgUkYuayB3aWxsIGJlIGNhbGN1bGF0ZWQKMi4gKEltcG9ydGFudCk6IHRoZSB2YXJpYWJpbGl0eSBvZiB0aGUgUkYgaXMgZXhjbHVkZWQsIGFzc3VtaW5nIHRoYXQgUkYgYW5kIEZGIGNvdWxkIGludGVyYWN0LgoKaS5lLiB3aXRoIChQb3MgfCBWcG4pLCBleGFtaW5lcyB0aGUgZXh0ZW50IFBvc2l0aW9uIGFuZCBTcGVha2VyIChWcG4pIGludGVyYWN0IHdpdGggZWFjaCBvdGhlcjoKCmBgYHtyLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD00fQpid3Bsb3QoRjEgfiBQb3N8VnBuLCBkYXRhID0gc29hKQpgYGAKCkFsc28gd2l0aCAoUG9zIHwgVyksIGV4YW1pbmVzIHRoZSBleHRlbnQgUG9zIGFuZCBXICh3b3JkKSBpbnRlcmFjdC4KCmBgYHtyLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD00fQpid3Bsb3QoRjEgfiBQb3N8VywgZGF0YSA9IHNvYSkKYGBgCgojIFRoZSBpbXBvcnRhbmNlIG9mICgxIHwgUkYpCgogMS4gQW4gSW50ZXJjZXB0IFJGLmsgKGJ1dCBubyBzbG9wZSkgaXMgY2FsY3VsYXRlZC4KIDIuIChJbXBvcnRhbnQpOiB0aGUgdmFyaWFiaWxpdHkgb2YgdGhlIFJGIGlzIGV4Y2x1ZGVkIHdpdGhvdXQgdGhlIGludGVyYWN0aW9uIGJldHdlZW4gUkYgYW5kIEZGLgoKKiAgKDEgfCBSRikgdG8gYmUgdXNlZCB3aGVuIHRoZSBGRiAnYmV0d2VlbicgaXMgcmVsYXRlZCB0byBSRi4gZS5nLgpgYGB7cn0KaGVhZChzdGltbSkKYGBgCgpUbyB3aGF0IGV4dGVudCBpcyB2b3QgYWZmZWN0ZWQgYnkgYWdlPwoqIEZpeGVkIGZhY3RvcjogYWdlIChBbHRlcikKKiBSYW5kb20gZmFjdG9yOiBTcGVha2VyIChWcG4pCiogQWdlIGlzIGJldHdlZW4gdGhlIHJlZmVyZW5jZSB0byBWcG4gKHNwZWFrZXIpOgoKYGBge3J9CndpdGgoc3RpbW0sIHRhYmxlKFZwbiwgQWx0ZXIpKQpgYGAKQWdlIGFuZCBzcGVha2VyIGNhbiBub3QgaW50ZXJhY3QgYmVjYXVzZSBlYWNoIHNwZWFrZXIgaXMgZWl0aGVyIGEgY2hpbGQgb3IgYWR1bHQ6CgpgYGB7ciwgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9NX0KYndwbG90KHZvdCB+IEFsdGVyfFZwbiwgZGF0YSA9IHN0aW1tKQpgYGAKClRoZXJlZm9yZSAoQWx0ZXIgfCBWcG4pIHdvdWxkIGJlIG1lYW5pbmdsZXNzIC0gc2luY2UgdGhpcyByZXF1aXJlcyB0aGF0IGFnZSAoQWx0ZXIpIGFuZCBWcG4gKHNwZWFrZXIpIGNhbiBpbnRlcmFjdCwgd2hpY2ggaXMgbm90IHBvc3NpYmxlIHdoZW4gRkYgaXMgJ2JldHdlZW4nIHdpdGggcmVnYXJkIHRvIHRoZSBSRi4KClRoZXJlZm9yZToKYGBge3J9CnN0aW1tMyA9IGxtZXIodm90IH4gQWx0ZXIgKyAoMXxWcG4pLCBkYXRhID0gc3RpbW0pCmBgYAphbmQgbm90IChBZ2UgfCBWcG4pIGlzIHVzZWQgaW4gdGhlIGZpbmFsIG1vZGVsLgoKKipHZW5lcmFsbHk6IGFsd2F5cyB1c2UgKEZGIHwgUkYpIGV4Y2VwdCB3aGVuIHRoZSBGRiBpcyAnYmV0d2VlbicgaW4gcmVsYXRpb24gdG8gUkYuKioKCioqKgoKIyBUaGUgVGVzdCBTdGF0aXN0aWNzCgpUaGlzIGlzIHByb2JhYmx5IHRoZSBtb3N0IGltcG9ydGFudCBwYXJ0IG9mIHRoZSB0dXRvcmlhbCBzbmQgdGhlIHBhcnQgdGhhdCBuZWVkcyB0aGUgbW9zdCBleHBsYW5hdGlvbi4KCiogVGhlIGBzdGVwICgpYCBmdW5jdGlvbgoqIChBKSBzaW1wbGlmeWluZyB0aGUgbW9kZWwgaWYgcG9zc2libGUKKiAoQikgZXhhbWluZSB3aGV0aGVyIHRoZSBmYWN0b3JzIGFyZSBzaWduaWZpY2FudCBpbiB0aGUgc2ltcGxpZmllZCBtb2RlbC4KClRoZSBkYXRhZnJhbWUgYHN0aW1tYCBtZWFzdXJlcyB0aGUgVk9UIGZvciBhIG51bWJlciBvZiBzeWxsYWJsZXMgKC8gYmEsIHBhIC8pIHByb2R1Y2VkIGJ5IGJvdGggY2hpbGRyZW4gYW5kIGFkdWx0cyAoZW5jb2RlZCBpbiBhbiBhZ2UgdmFyaWFibGUgKEFsdGVyKSkuIFRvIHdoYXQgZXh0ZW50IGlzIFZPVCBhZmZlY3RlZCBieSB0aGUgcGxhY2Ugb2YgYXJ0aWN1bGF0aW9uIChLIGZhY3RvcikgYW5kIC8gb3IgYWdlIG9mIHNwZWFrZXIgKHdoZXRoZXIgdGhlIHNwZWFrZXIgaXMgYSBjaGlsZCBvciBhbiBhZHVsdDogQWx0ZXIgZmFjdG9yKSA/CgojIyBQYXJ0IEE6IFNpbXBsaWZ5aW5nIHRoZSBtb2RlbCAKIyMjIEZpcnN0IFBsb3QgdGhlIEludGVyYWN0aW9ucwpgYGB7ciwgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9M30KYndwbG90KHZvdCB+IEsgfCBBbHRlciwgZGF0YSA9IHN0aW1tKQpgYGAKCmBgYHtyfQojIExpbmVhciBNaXhlZCBNb2RlbApzdGltbS5sbWVyID0gbG1lcih2b3QgfiBLICogQWx0ZXIgKyAoS3xWcG4pLCBkYXRhID0gc3RpbW0pCnN0aW1tLnN0ZXAgPSBzdGVwKHN0aW1tLmxtZXIpCnN0aW1tLnN0ZXAKIyBwaC5zdGVwKHN0aW1tLnN0ZXApCiMgUGFydCAoYSk6IE1vZGVsIHNpbXBsaWZpY2F0aW9uCiMgKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysKIyBEbyB3ZSByZWFsbHkgbmVlZCBLICsgQWx0ZXIgKyBLOiBBbHRlciArIChLIHwgVnBuKT8KYGBgCgpEbyB3ZSByZWFsbHkgbmVlZCBgSyArIEFsdGVyICsgSzogQWx0ZXIgKyAoSyB8IFZwbilgPyAgCk5vdGU6IHRoaXMgaXMgZXF1aXZhbGVudCBub3RhdGlvbiB0byBgSyAqIEFsdGVyICsgKEsgfCBWcG4pYC4gCgpJZiB3ZSBsb29rIGF0IHRoZSBzaWduaWZpY2FuY2Ugb2YgYm90aCB0aGUgZml4ZWQgZWZmZWN0cyBhbmQgdGhlIHJhbmRvbSBlZmZlY3RzIHdlIHNlZSB0aGF0IHNvbWUgb2YgdGhlIGludGVyYWN0aW9ucyBhcmUgbm90IGNvbnRyaWJ1dGluZyB0byB0aGUgbW9kZWwuCgoqUmFuZG9tIGVmZmVjdHM6KgoKIGludGVyYWN0aW9uIHwgQ2hpLnNxIHwgQ2hpLkRGIHwgZWxpbS5udW0gfCBwLnZhbHVlICAKIC0tLS0tLS0tLS0tIHwtLS0tLSAgIHwgLS0tLS0tIHwgLS0tLS0tLS0gfCAtLS0tLS0tICAKIEs6VnBuICAgICAgIHwgIDUuMDQgIHwgICAyICAgIHwga2VwdCAgICAgfCAwLjA4MDMgICAKIAoKKkZpeGVkIGVmZmVjdHM6KgoKIHggfCBTdW0gU3EgfCBNZWFuIFNxIHwgTnVtREYgfCBEZW5ERiB8Ri52YWx1ZSB8ZWxpbS5udW0gfFByKD5GKQotLS18IC0tLS0tIHwgLS0tLS0tLSB8IC0tLS0tIHwtLS0tLS0gfCAtLS0tLS0gfCAtLS0tLS0tLQpLOkFsdGVyICB8ICAxLjU0NjYgfCAgMS41NDY2IHwgICAgMSAgIHwgIDYgIHwgMC4zNTExICB8ICAgICAgMSB8ICAwLjU3NTEKQWx0ZXIgICAgfCAzLjMwNDUgfCAgMy4zMDQ1ICAgfCAgMSAgICB8IDYgICB8IDAuNzUwMSAgfCAgICAgIDIgfCAwLjQxOTcKSyAgICAgICB8IDE4MC44NjE0IHwgMTgwLjg2MTQgIHwgICAxICB8ICAgNyB8IDQxLjA1NjMgfCAgIGtlcHQgfCAwLjAwMDQKCjEuIFJhbmRvbSBlZmZlY3RzIGlzIGFsbW9zdCBzaWduaWZpY2FudCAocCA9IDAuMDgpLiBIZW5jZSBgKEsgfCBWcG4pYCBpcyByZXRhaW5lZC4gVGhlcmVmb3JlIGBrZXB0YCBpLmUuIGAoSyB8IFZwbilgIHdhcyBub3QsIHNvIGAoMSB8IFZwbilgIHJlcGxhY2VkLgoKMi4gSW4gdGhlIGZpeGVkLWVmZmVjdHMuIGBLOkFsdGVyYCBhbmQgYEFsdGVyYCBhcmUgbm90IHNpZ25pZmljYW50LCBhbmQgd2VyZSByZW1vdmVkIGZyb20gdGhlIG1vZGVsLiBCdXQgdGhlIGZhY3RvciBgS2AgKHBsYWNlIG9mIGFydGljdWxhdGlvbikgcmVtYWlucyBgKGtlcHRgKS4KClRoZSBmb3JtdWxhIHVzZWQgaXM6CgoqIGZvcm11bGEgPSB2b3QgfiBLICsgKEsgfCBWcG4pLCBkYXRhID0gc3RpbW0sCgojIyMgVGhlIFNpbXBsaWZpZWQgTW9kZWw6CmBgYHtyfQpzdGltbS5sbWVyIDwtIGxtZXIodm90IH4gIEsgICsgKEsgfCBWcG4pLCBkYXRhID0gc3RpbW0pCnN0aW1tLnN0ZXAgPC0gc3RlcChzdGltbS5sbWVyKQpzdGltbS5zdGVwCmBgYAoKIyMjIFRoZSBGaW5hbCBNb2RlbDoKCmBgYHtyfQpsbWU0OjpsbWVyKGZvcm11bGEgPSB2b3QgfiBLICsgKEsgfCBWcG4pLCBkYXRhID0gc3RpbW0pCmBgYAoKIyMgUGFydCBCOiBUaGUgdGVzdCBzdGF0aXN0aWMgKHBvc3QgaG9jIHRlc3RzKQoKZm9yIHRoZSBzaW1wbGlmaWVkIG1vZGVsIGZvciB0aGUgYWJvdmUgZGF0YTogIAoKWCAgICAgICAgIHwgIFN1bSBTcSB8IE1lYW4gU3EgfE51bURGIHxEZW5ERiB8Ri52YWx1ZXwgZWxpbS5udW0gfFByKD5GKQotLS18IC0tLS0tIHwgLS0tLS0tLSB8IC0tLS0tIHwtLS0tLS0gfCAtLS0tLS0gfCAtLS0tLS0tLSB8IC0tLS0KKipLOkFsdGVyKiogIHwgMS41NDY2ICB8IDEuNTQ2NiB8ICAgIDEgIHwgICA2IHwgMC4zNTExIHwgICAgICAgMSB8MC41NzUxCioqQWx0ZXIqKiAgIHwgMy4zMDQ1ICB8IDMuMzA0NSAgfCAgIDEgIHwgICA2IHwgMC43NTAxICB8ICAgICAgMiB8MC40MTk3CioqSyoqICAgICB8IDE4MC44NjE0IHwxODAuODYxNCB8ICAgIDEgIHwgICA3IHw0MS4wNTYzICAgfCAga2VwdCB8MC4wMDA0CgpgYGB7cn0KIyBhcyB5b3UgY2FuIHNlZSBoZXJlOgpzdGltbTIubG1lciA9IGxtZXIodm90IH4gIEsgKyAoSyB8IFZwbiksIGRhdGEgPSBzdGltbSkKYW5vdmEoc3RpbW0yLmxtZXIpCmBgYAoKV2hpY2ggbWVhbnMgdGhhdDogVk9UIGlzIHNpZ25pZmljYW50IGZvciBhIG1haW4gZWZmZWN0IG9mIHBsYWNlIG9mIGFydGljdWxhdGlvbiAoRiBbMSwgN10gPSA0MS4xLCBwIDwwLjAwMSkgYnV0IGlzIG5vdCBpbmZsdWVuY2VkIGJ5IGFnZS4gRnV0aGVybW9yZSB0aGVyZSBpcyBubyBpbnRlcmFjdGlvbiBiZXR3ZWVuIHRoZXNlIGZhY3RvcnMuCgojIFBvc3QtaG9jIFRlc3RzCgpQb3N0IGhvYyB0ZXN0IGFyZSBuZWVkZWQgaWYgdGhlcmUgaXMgYW4gaW50ZXJhY3Rpb24gcmVjb3JkZWQgYmV0d2VlbiB0aGUgZml4ZWQgZmFjdG9ycy4gVGhlc2UgYXJlIGNhcnJpZWQgb3V0IGluIGEgdmVyeSBzaW1pbGFyIG1hbm5lciB0byBBTk9WQXMKCkhlcmUgd2UgaGF2ZSBhbm90aGVyIGRhdGFzZXQgCmBgYHtyfQpoZWFkKGludCkKYGBgCjEwIHNwZWFrZXJzIChWcG4pIHByb2R1Y2VkIDYgZGlmZmVyZW50IHdvcmRzIChXb3J0KSB3aXRoIGVpdGhlciBhbiAvIGkgLyBvciAvIGEgLyB2b3dlbCAoZmFjdG9yIGBWYCkuIFRoZSBpbnRlbnNpdHkgd2FzIChkZXBlbmRlbnQgdmFyaWFibGUgYGRiYCkgd2FzIG1lYXN1cmVkIHBlciB2b3dlbC4gVGhlIHNwZWFrZXJzIHdlcmUgZWl0aGVyIG1hbGUgKG0pIG9yIGZlbWFsZSAoZikgaW4gYSBmYWN0b3IgYEdgLgoKV2hlbiBjb25zdHJ1Y3RpbmcgdGhlIG1vZGVsIGl0IHdhcyBmb3VuZCB0aGF0IGBWYCBhbmQgYEdgIApgYGB7cn0KI1RoZXJlZm9yZSA6CmludC5sbWVyID0gbG1lcihkYiB+IFYgKiBHICsgKFYrR3xXb3J0KSArIChWfFZwbiksIGRhdGEgPSBpbnQpCmludC5zdGVwID0gc3RlcChpbnQubG1lcikKaW50LnN0ZXAKYGBgCkJhc2VkIG9uIHRoZSBhYm92ZSB0YWJsZSB0aGUgZmluYWwgbW9kZWwgd2lsbCBiZToKYGBge3J9CiMgRmluYWwgbW9kZWw6CiMgIGxtZTQ6OmxtZXIoZm9ybXVsYSA9IGRiIH4gViArIEcgKyAoViB8IFZwbikgKyAoMSB8IFdvcnQpICsgVjpHLApgYGAKVGhlcmUgaXMgYSBzaWduaWZpY2FudCBpbnRlcmFjdGlvbjoKYGBge3J9CiMgVGhlcmUgaXMgYSBzaWduaWZpY2FudCBpbnRlcmFjdGlvbjoKIyBGaXhlZCBlZmZlY3RzOgojICBTdW0gU3EgIE1lYW4gU3EgTnVtREYgRGVuREYgRi52YWx1ZSBlbGltLm51bSBQcig+RikKIyBWICAgMTI2LjY1OTcgMTI2LjY1OTcgICAgIDEgICAgIDggMjkuODI5OCAgICAga2VwdCAwLjAwMDYKIyBHICAgIDQ3LjMxMzMgIDQ3LjMxMzMgICAgIDEgICAgIDggMTEuMTQyOCAgICAga2VwdCAwLjAxMDMKIyBWOkcgIDMxLjk2OTAgIDMxLjk2OTAgICAgIDEgICAgIDggIDcuNTI5MSAgICAga2VwdCAwLjAyNTMKYGBgClRoZXJlZm9yZSwgd2UgbXVzdCBhcHBseSBhIHBvc3QtaG9jIHRlc3QuCmBgYHtyfQojCihpbnQucGg9cGguc3RlcChpbnQuc3RlcCwgImJvbmZlcnJvbmkiKSkKIwpgYGAKClBvc3QtaG9jIEJvbmZlcnJvbmkgY29ycmVjdGVkIHQtdGVzdHMgc2hvd2VkIHNpZ25pZmljYW50IGRpZmZlcmVuY2VzIGJldHdlZW4gLyBpIC8gYW5kIC8gYSAvIGluIG1lbiAocCA8MC4wMSkgYnV0IG5vdCBpbiB3b21lbjsKYW5kIHNpZ25pZmljYW50IGRpZmZlcmVuY2VzIGJldHdlZW4gbWVuIGFuZCB3b21lbiBpbiAvIGEgLyAocCA8MC4wNSkgYnV0IG5vdCBpbiAvIGkgLy4KCgoK