EFA
- A multivariate method in which to determine the relationships and
patterns between variables.
- EFA examines a pattern of correlations/covariances between the items
and tries to determine if they are influenced by the same or a different
underlying factor.
- EFA requires the data to be multivariate normal and have
linear relationships
- If you violate these assumptions, you can move to Multidimensional
scaling (MDS), which is more flexible
- EFA is different from Confirmatory Factor Analysis (CFA) in which
you are trying to confirm a specific set of attempts to confirm how
things should be related to one another using an SEM model
- EFA usages: (see Yong & Pearce, 2013)
- Develop Scales: such as Big 5 Personality
- Item’s Analysis: which test items go together (to test specific
constructs)
- Dimensionality reduction: Which items/questions/features are most
important (strength of which things bind together) and create “factor
scores” representing underlying constructs for use in other
analyses.
Collect items measured in the same units
- You might need to reverse score the items or recode/transform them
Visualize the patterns in the data
Helpful to look at the patterns before you go further
Take data (BFI228) of the “study on personality and relationship
satisfaction (Luo, 2005). The participants were 228 undergraduate
students at a large public university in the US. The data were
participants’ self-ratings on the 44 items of the Big Five Inventory
(John, Donahue, & Kentle, 1991). These items are Likert variables:
disagree strongly (1), disagree a little (2), neither agree nor disagree
(3), agree a little (4), and agree strongly (5)”. Taken from the
EFAutilities package.
library(psych)
library(corrplot)
library(EFAutilities)
data(BFI228)
DataSet<-as.data.frame(BFI228)
corrplot(cor(DataSet, method='spearman'), order = "hclust",
hclust.method = "ward.D2", tl.col='black', tl.cex=.75)
Do I have enough data to proceed?
- Kaiser Meyer Olkin (KMO) Measure of Sampling Adequacy (Howard,
2016)
- You could remove anything below .6 and if the overall is below .6
you cannot do a EFA
0.00 to 0.50 |
Unacceptable |
No |
0.50 to 0.60 |
Miserable |
No |
0.60 to 0.70 |
Mediocre |
Yes |
0.70 to 0.80 |
Middling |
Yes |
0.80 to 0.90 |
Meritorious |
Yes |
0.90 to 1.00 |
Marvelous |
Yes |
KMO(DataSet)
## Kaiser-Meyer-Olkin factor adequacy
## Call: KMO(r = DataSet)
## Overall MSA = 0.83
## MSA for each item =
## talkative researvedR fullenergy enthusiastic quietR
## 0.81 0.81 0.81 0.85 0.86
## assertive shyR outgoing findfaultR helpful
## 0.91 0.78 0.84 0.87 0.83
## quarrelsR forgiving trusting coldR considerate
## 0.84 0.82 0.81 0.83 0.83
## rudeR cooperative thorough carelessR reliable
## 0.84 0.82 0.85 0.88 0.81
## disorganizedR lazyR persevere efficient plans
## 0.78 0.81 0.78 0.85 0.84
## distractedR blue relaxedR tense worries
## 0.85 0.90 0.83 0.87 0.87
## emostableR moody calmR nervous ideas
## 0.84 0.83 0.86 0.87 0.87
## curious ingenious imagination inventive artistic
## 0.84 0.85 0.77 0.80 0.72
## routineR reflect nonartisticR sophisticated
## 0.56 0.84 0.75 0.67
Remove an item
Remove routineR as it is below .6
DataSet.2 <- subset(DataSet, select = -c(routineR))
Get correlation/covariances matrix
- The type of correlation is important:
- If you have ordinal or nominal data you might want to switch from
Pearson to Spearman, or poly- or tetra-choric correlations (for better
fitting in IRT analysis)
- Most psychologists just ignore this and use Pearson as that is all
SPSS can do (but we will use Spearman as it’s easy to compute)
- Note: I have created the correlation matrix for the later factor
analysis by hand. The psych will do it for you but read the function
carefully to see what it defaults to doing
CM<-cor(DataSet.2, method = "spearman", use="complete.obs")
Select the number of factors
- You might have a specific number in mind, or it can be approximated
from the data.
- The Kaiser criterion: a number of factors equal to the number of the
eigenvalues of the correlation matrix that is greater
than one.
- The “Scree test” is a lot of the eigenvalues of the correlation
matrix in descending order
Selecting from the scree plot can be accomplished in a few different
ways:
- Visually: When does the plot level off
- Parallel Analysis: (basically what random correlations would give
you for the same number of subjects and items)
- Optimal Coordinate: extrapolation of the preceding eigenvalue by a
regression line between the eigenvalue coordinates and the last
eigenvalue coordinates
- Acceleration Factor: When the slope change most abruptly
(elbow)
- Note: Don’t use the default of “1” heuristic that many people
just apply.
library(nFactors)
ev <- eigen(CM) # get eigenvalues
ap <- parallel(subject=nrow(DataSet.2),var=ncol(DataSet.2),
rep=100,cent=.05)
nS <- nScree(x=ev$values, aparallel=ap$eigen$qevpea)
plotnScree(nS)
- Results suggest 6, but we will force a 5-factor solution to match
the Big 5
Rotate your factors to a final solution
- Think of each factor a dimension and the problem is there is a
nearly infinite set of dimensions that could explain your data
- We need a way to help come up with a solution that is more
interpretable (and reliable)
- By rotating your factors, you attempt to find a factor solution that
is equal to that obtained in the initial extraction, but which has the
most straightforward interpretation
- The simplest solution has 5 features (summarized in Abdi, 2003)
- each row contains at least one zero
- for each column, there are at least as many zeros as there are
columns
- for any pair of factors, there are some variables with zero loadings
on one factor and large loadings on the other factor
- for any pair of factors, there is a sizable proportion of zero
loadings
- for any pair of factors, there is only a small number of large
loadings
- There are many different types of rotation, but they all try to get
the strongest effect on the small subset of items.
There are two families of rotations: - Orthogonal rotations:
uncorrelated factors (e.g., varimax, quartimax,
equimax) - Oblique rotations: produce correlated factors (e.g.,
promax)
- Varimax is the most popular orthogonal rotation: “simple solution
means that each factor has a small number of large loadings and a large
number of zero (or small) loadings” (Abdi, 2003)
- Quartimax tries to find less factors and equimax balanced between
varimax and quartimax
Orthogonal Rotation
- Loadings: We tend to consider a loading at between .3 or .4 (see
Howard, 2016 for review)
- Howard, 2016 recommends the .40–.30–.20 rule:
- Primary factor above 0.40
- Alternative factors below 0.30
- Demonstrate a difference of 0.20 between their primary and
alternative factor loadings
mle.VM <- fa(CM,5, fm="mle",rotate="varimax")
print(mle.VM$loadings, cutoff=0.2)
##
## Loadings:
## ML4 ML2 ML1 ML3 ML5
## talkative 0.707
## researvedR 0.216 -0.546
## fullenergy -0.227 0.387 0.536
## enthusiastic 0.296 0.417 0.605
## quietR -0.781
## assertive 0.319 0.526
## shyR 0.218 -0.655
## outgoing 0.244 0.722
## findfaultR 0.290 -0.420
## helpful 0.499
## quarrelsR 0.278 -0.619
## forgiving 0.567
## trusting 0.570
## coldR -0.687
## considerate 0.280 0.626
## rudeR 0.348 -0.572
## cooperative 0.567 0.218 0.281
## thorough 0.753
## carelessR 0.327 -0.384
## reliable 0.269 0.577
## disorganizedR -0.583
## lazyR 0.211 -0.212 -0.468
## persevere 0.266 0.530
## efficient 0.634
## plans 0.641
## distractedR 0.522 -0.310
## blue 0.527 -0.267 -0.200 -0.287
## relaxedR -0.646
## tense 0.696
## worries 0.747
## emostableR -0.597 0.203 0.214
## moody 0.688
## calmR -0.530 0.226
## nervous 0.634 -0.275
## ideas 0.688
## curious 0.444 0.235
## ingenious 0.577
## imagination 0.689
## inventive 0.770
## artistic 0.603
## reflect 0.649 0.210
## nonartisticR -0.391
## sophisticated 0.557
##
## ML4 ML2 ML1 ML3 ML5
## SS loadings 4.322 4.007 3.862 3.813 3.321
## Proportion Var 0.101 0.093 0.090 0.089 0.077
## Cumulative Var 0.101 0.194 0.284 0.372 0.449
fa.diagram(mle.VM)
Useful to examine the two factors that account for the most variance
visually
- We will need to extract the factor loading
VM.load = mle.VM$loadings[,1:2]
plot(VM.load, type="n")
text(VM.load,labels=colnames(DataSet.2),cex=.75) # add variable names
Oblique Rotation
- When we examine the promax, meaning now will allow for (small)
correlation between factors. Oblique rotations were suggested by
Thurstone
- Promax performs a varimax rotation, and then it allows the factors
to correlate through raising the factor loadings to a specified power
(often 4) and useful for large datasets (see Howard, 2016)
- Direct Oblimin, is another popular type, is harder to use as it
requires you set a delta
mle.PM <- fa(CM,5, fm="mle",rotate="Promax")
print(mle.PM$loadings, cutoff=0.2)
##
## Loadings:
## ML4 ML2 ML1 ML3 ML5
## talkative 0.750
## researvedR -0.548
## fullenergy 0.315 0.494
## enthusiastic 0.368 0.575
## quietR -0.817
## assertive 0.267 0.491
## shyR -0.672
## outgoing 0.740
## findfaultR 0.245 -0.425
## helpful 0.507
## quarrelsR 0.202 -0.650
## forgiving 0.611
## trusting 0.590
## coldR -0.734
## considerate 0.215 0.661
## rudeR 0.292 -0.589
## cooperative 0.555
## thorough 0.813
## carelessR 0.270 0.248 -0.369
## reliable 0.601
## disorganizedR -0.635
## lazyR -0.455
## persevere 0.223 0.578
## efficient 0.667
## plans 0.697
## distractedR 0.505 -0.263
## blue 0.466
## relaxedR -0.669
## tense 0.737
## worries 0.792
## emostableR -0.617
## moody 0.733
## calmR -0.514
## nervous 0.688 0.218
## ideas 0.688
## curious 0.411
## ingenious 0.572 0.216
## imagination 0.698
## inventive 0.793
## artistic 0.619
## reflect 0.651
## nonartisticR -0.398
## sophisticated 0.583 -0.230
##
## ML4 ML2 ML1 ML3 ML5
## SS loadings 4.299 3.914 3.812 3.720 3.389
## Proportion Var 0.100 0.091 0.089 0.087 0.079
## Cumulative Var 0.100 0.191 0.280 0.366 0.445
fa.diagram(mle.PM)
Useful to examine the two factors that account for the most variance
visually
- We will need to extract the factor loading
PM.load = mle.PM$loadings[,1:2]
plot(PM.load, type="n")
text(PM.load,labels=colnames(DataSet.2),cex=.75) # add variable names
Interpret the Loadings
- This loading can be interpreted as like a part correlations (the
variables are all controlled for)
- You have to name the construct based on the pattern of positive and
negative loadings
- You could reverse score the loadings to make it easier
- Let’s name our factors from the varimax and promax and see what our
5 personality constructs might be
References
Abdi, H. (2003). Factor rotations in factor analyses.
Encyclopedia for Research Methods for the Social Sciences.
Sage: Thousand Oaks, CA, 792-795.
Howard, M. C. (2016). A Review of Exploratory Factor Analysis
Decisions and Overview of Current Practices: What We Are Doing and How
Can We Improve?. International Journal of Human-Computer
Interaction, 32(1), 51-62.
Yong, A. G., & Pearce, S. (2013). A beginner’s guide to factor
analysis: Focusing on exploratory factor analysis. Tutorials in
Quantitative Methods for Psychology, 9(2), 79-94.
LS0tDQp0aXRsZTogJ0V4cGxvcmF0b3J5IEZhY3RvciBBbmFseXNpcycNCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBmb250c2l6ZTogOHB0DQogICAgaGlnaGxpZ2h0OiB0ZXh0bWF0ZQ0KICAgIG51bWJlcl9zZWN0aW9uczogbm8NCiAgICB0aGVtZTogZmxhdGx5DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IG5vDQoNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmtuaXRyOjpvcHRzX2NodW5rJHNldChtZXNzYWdlID0gRkFMU0UpDQprbml0cjo6b3B0c19jaHVuayRzZXQod2FybmluZyA9ICBGQUxTRSkNCmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcud2lkdGg9NS43NSkNCmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcuaGVpZ2h0PTUuNzUpDQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLmFsaWduPSdjZW50ZXInKSANCmBgYA0KDQoNCiMgRUZBDQotIEEgbXVsdGl2YXJpYXRlIG1ldGhvZCBpbiB3aGljaCB0byBkZXRlcm1pbmUgdGhlIHJlbGF0aW9uc2hpcHMgYW5kIHBhdHRlcm5zIGJldHdlZW4gdmFyaWFibGVzLiANCiAgICAtIEVGQSBleGFtaW5lcyBhIHBhdHRlcm4gb2YgY29ycmVsYXRpb25zL2NvdmFyaWFuY2VzIGJldHdlZW4gdGhlIGl0ZW1zIGFuZCB0cmllcyB0byBkZXRlcm1pbmUgaWYgdGhleSBhcmUgaW5mbHVlbmNlZCBieSB0aGUgc2FtZSBvciBhIGRpZmZlcmVudCB1bmRlcmx5aW5nIGZhY3Rvci4NCiAgICAtIEVGQSByZXF1aXJlcyB0aGUgZGF0YSB0byBiZSBtdWx0aXZhcmlhdGUgbm9ybWFsIGFuZCBoYXZlICpsaW5lYXIqIHJlbGF0aW9uc2hpcHMNCiAgICAgICAgLSBJZiB5b3UgdmlvbGF0ZSB0aGVzZSBhc3N1bXB0aW9ucywgeW91IGNhbiBtb3ZlIHRvIE11bHRpZGltZW5zaW9uYWwgc2NhbGluZyAoTURTKSwgd2hpY2ggaXMgbW9yZSBmbGV4aWJsZSANCiAgICAtIEVGQSBpcyBkaWZmZXJlbnQgZnJvbSBDb25maXJtYXRvcnkgRmFjdG9yIEFuYWx5c2lzIChDRkEpIGluIHdoaWNoIHlvdSBhcmUgdHJ5aW5nIHRvIGNvbmZpcm0gYSBzcGVjaWZpYyBzZXQgb2YgYXR0ZW1wdHMgdG8gY29uZmlybSBob3cgdGhpbmdzIHNob3VsZCBiZSByZWxhdGVkIHRvIG9uZSBhbm90aGVyIHVzaW5nIGFuIFNFTSBtb2RlbA0KLSBFRkEgdXNhZ2VzOiAoc2VlIFlvbmcgJiBQZWFyY2UsIDIwMTMpDQogICAgLSAxLiBEZXZlbG9wIFNjYWxlczogc3VjaCBhcyBCaWcgNSBQZXJzb25hbGl0eSANCiAgICAtIDIuIEl0ZW0ncyBBbmFseXNpczogd2hpY2ggdGVzdCBpdGVtcyBnbyB0b2dldGhlciAodG8gdGVzdCBzcGVjaWZpYyBjb25zdHJ1Y3RzKQ0KICAgIC0gMy4gRGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uOiBXaGljaCBpdGVtcy9xdWVzdGlvbnMvZmVhdHVyZXMgYXJlIG1vc3QgaW1wb3J0YW50IChzdHJlbmd0aCBvZiB3aGljaCB0aGluZ3MgYmluZCB0b2dldGhlcikgYW5kIGNyZWF0ZSAiZmFjdG9yIHNjb3JlcyIgcmVwcmVzZW50aW5nIHVuZGVybHlpbmcgY29uc3RydWN0cyBmb3IgdXNlIGluIG90aGVyIGFuYWx5c2VzLg0KDQojIyBDb2xsZWN0IGl0ZW1zIG1lYXN1cmVkIGluIHRoZSBzYW1lIHVuaXRzDQotIFlvdSBtaWdodCBuZWVkIHRvIHJldmVyc2Ugc2NvcmUgdGhlIGl0ZW1zIG9yIHJlY29kZS90cmFuc2Zvcm0gdGhlbQ0KICAgIC0gVXN1YWxseSBMaWtlcnQgc2NhbGVzDQoNCiMjIyBWaXN1YWxpemUgdGhlIHBhdHRlcm5zIGluIHRoZSBkYXRhIA0KLSBIZWxwZnVsIHRvIGxvb2sgYXQgdGhlIHBhdHRlcm5zIGJlZm9yZSB5b3UgZ28gZnVydGhlcg0KDQotIFRha2UgZGF0YSAoQkZJMjI4KSBvZiB0aGUgInN0dWR5IG9uIHBlcnNvbmFsaXR5IGFuZCByZWxhdGlvbnNoaXAgc2F0aXNmYWN0aW9uIChMdW8sIDIwMDUpLiBUaGUNCnBhcnRpY2lwYW50cyB3ZXJlIDIyOCB1bmRlcmdyYWR1YXRlIHN0dWRlbnRzIGF0IGEgbGFyZ2UgcHVibGljIHVuaXZlcnNpdHkgaW4gdGhlIFVTLiBUaGUgZGF0YSB3ZXJlDQpwYXJ0aWNpcGFudHMnIHNlbGYtcmF0aW5ncyBvbiB0aGUgNDQgaXRlbXMgb2YgdGhlIEJpZyBGaXZlIEludmVudG9yeSAoSm9obiwgRG9uYWh1ZSwgJiBLZW50bGUsIDE5OTEpLg0KVGhlc2UgaXRlbXMgYXJlIExpa2VydCB2YXJpYWJsZXM6IGRpc2FncmVlIHN0cm9uZ2x5ICgxKSwgZGlzYWdyZWUgYSBsaXR0bGUgKDIpLCBuZWl0aGVyIGFncmVlIG5vciBkaXNhZ3JlZQ0KKDMpLCBhZ3JlZSBhIGxpdHRsZSAoNCksIGFuZCBhZ3JlZSBzdHJvbmdseSAoNSkiLiBUYWtlbiBmcm9tIHRoZSBFRkF1dGlsaXRpZXMgcGFja2FnZS4NCg0KYGBge3IsZmlnLndpZHRoPTcuNzUsIGZpZy5oZWlnaHQ9Ny43NSB9DQpsaWJyYXJ5KHBzeWNoKQ0KbGlicmFyeShjb3JycGxvdCkNCmxpYnJhcnkoRUZBdXRpbGl0aWVzKQ0KZGF0YShCRkkyMjgpDQpEYXRhU2V0PC1hcy5kYXRhLmZyYW1lKEJGSTIyOCkNCmNvcnJwbG90KGNvcihEYXRhU2V0LCBtZXRob2Q9J3NwZWFybWFuJyksIG9yZGVyID0gImhjbHVzdCIsIA0KICAgICAgICAgaGNsdXN0Lm1ldGhvZCA9ICJ3YXJkLkQyIiwgdGwuY29sPSdibGFjaycsIHRsLmNleD0uNzUpIA0KYGBgDQoNCiMjIERvIEkgaGF2ZSBlbm91Z2ggZGF0YSB0byBwcm9jZWVkPyANCi0gS2Fpc2VyIE1leWVyIE9sa2luIChLTU8pIE1lYXN1cmUgb2YgU2FtcGxpbmcgQWRlcXVhY3kgKEhvd2FyZCwgMjAxNikNCi0gWW91IGNvdWxkIHJlbW92ZSBhbnl0aGluZyBiZWxvdyAuNiBhbmQgaWYgdGhlIG92ZXJhbGwgaXMgYmVsb3cgLjYgeW91IGNhbm5vdCBkbyBhIEVGQQ0KDQpLTU8gICAgICAgICAgfCAgSW50ZXJwcmV0YXRpb24gfCBVc2U/DQotLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS18LS0tLS0NCjAuMDAgdG8gMC41MCB8ICBVbmFjY2VwdGFibGUgIHwgIE5vDQowLjUwIHRvIDAuNjAgfCAgTWlzZXJhYmxlICAgICB8IE5vDQowLjYwIHRvIDAuNzAgfCAgTWVkaW9jcmUgICAgICB8IFllcw0KMC43MCB0byAwLjgwIHwgIE1pZGRsaW5nICAgICAgfCBZZXMNCjAuODAgdG8gMC45MCB8ICBNZXJpdG9yaW91cyAgIHwgWWVzDQowLjkwIHRvIDEuMDAgfCAgTWFydmVsb3VzICAgICB8IFllcw0KDQpgYGB7cn0NCktNTyhEYXRhU2V0KQ0KYGBgDQoNCiMjIFJlbW92ZSBhbiBpdGVtDQpSZW1vdmUgcm91dGluZVIgYXMgaXQgaXMgYmVsb3cgLjYNCg0KYGBge3J9DQpEYXRhU2V0LjIgPC0gc3Vic2V0KERhdGFTZXQsIHNlbGVjdCA9IC1jKHJvdXRpbmVSKSkNCmBgYA0KDQoNCiMjIEdldCBjb3JyZWxhdGlvbi9jb3ZhcmlhbmNlcyBtYXRyaXgNCi0gVGhlIHR5cGUgb2YgY29ycmVsYXRpb24gaXMgaW1wb3J0YW50OiANCi0gSWYgeW91IGhhdmUgb3JkaW5hbCBvciBub21pbmFsIGRhdGEgeW91IG1pZ2h0IHdhbnQgdG8gc3dpdGNoIGZyb20gUGVhcnNvbiB0byBTcGVhcm1hbiwgb3IgcG9seS0gb3IgdGV0cmEtY2hvcmljIGNvcnJlbGF0aW9ucyAoZm9yIGJldHRlciBmaXR0aW5nIGluIElSVCBhbmFseXNpcykNCi0gTW9zdCBwc3ljaG9sb2dpc3RzIGp1c3QgaWdub3JlIHRoaXMgYW5kIHVzZSBQZWFyc29uIGFzIHRoYXQgaXMgYWxsIFNQU1MgY2FuIGRvIChidXQgd2Ugd2lsbCB1c2UgU3BlYXJtYW4gYXMgaXQncyBlYXN5IHRvIGNvbXB1dGUpDQogICAgLSBOb3RlOiBJIGhhdmUgY3JlYXRlZCB0aGUgY29ycmVsYXRpb24gbWF0cml4IGZvciB0aGUgbGF0ZXIgZmFjdG9yIGFuYWx5c2lzIGJ5IGhhbmQuIFRoZSBwc3ljaCB3aWxsIGRvIGl0IGZvciB5b3UgYnV0IHJlYWQgdGhlIGZ1bmN0aW9uIGNhcmVmdWxseSB0byBzZWUgd2hhdCBpdCBkZWZhdWx0cyB0byBkb2luZyAgDQoNCmBgYHtyfQ0KQ008LWNvcihEYXRhU2V0LjIsIG1ldGhvZCA9ICJzcGVhcm1hbiIsIHVzZT0iY29tcGxldGUub2JzIikNCmBgYA0KDQojIyBTZWxlY3QgdGhlIG51bWJlciBvZiBmYWN0b3JzIA0KLSBZb3UgbWlnaHQgaGF2ZSBhIHNwZWNpZmljIG51bWJlciBpbiBtaW5kLCBvciBpdCBjYW4gYmUgYXBwcm94aW1hdGVkIGZyb20gdGhlIGRhdGEuIA0KDQpBKSBUaGUgS2Fpc2VyIGNyaXRlcmlvbjogYSBudW1iZXIgb2YgZmFjdG9ycyBlcXVhbCB0byB0aGUgbnVtYmVyIG9mIHRoZSAqKmVpZ2VudmFsdWVzKiogb2YgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeCB0aGF0IGlzIGdyZWF0ZXIgdGhhbiBvbmUuDQpCKSBUaGUgIlNjcmVlIHRlc3QiIGlzIGEgbG90IG9mIHRoZSBlaWdlbnZhbHVlcyBvZiB0aGUgY29ycmVsYXRpb24gbWF0cml4IGluIGRlc2NlbmRpbmcgb3JkZXINCg0KLSBXaGF0IGFyZSAqKmVpZ2VudmFsdWVzKio/IGh0dHA6Ly9zZXRvc2EuaW8vZXYvZWlnZW52ZWN0b3JzLWFuZC1laWdlbnZhbHVlcy8NCg0KU2VsZWN0aW5nIGZyb20gdGhlIHNjcmVlIHBsb3QgY2FuIGJlIGFjY29tcGxpc2hlZCBpbiBhIGZldyBkaWZmZXJlbnQgd2F5czoNCg0KLSBWaXN1YWxseTogV2hlbiBkb2VzIHRoZSBwbG90IGxldmVsIG9mZiANCi0gUGFyYWxsZWwgQW5hbHlzaXM6IChiYXNpY2FsbHkgd2hhdCByYW5kb20gY29ycmVsYXRpb25zIHdvdWxkIGdpdmUgeW91IGZvciB0aGUgc2FtZSBudW1iZXIgb2Ygc3ViamVjdHMgYW5kIGl0ZW1zKQ0KLSBPcHRpbWFsIENvb3JkaW5hdGU6IGV4dHJhcG9sYXRpb24gb2YgdGhlIHByZWNlZGluZyBlaWdlbnZhbHVlIGJ5IGEgcmVncmVzc2lvbiBsaW5lIGJldHdlZW4gdGhlIGVpZ2VudmFsdWUgY29vcmRpbmF0ZXMgYW5kIHRoZSBsYXN0IGVpZ2VudmFsdWUgY29vcmRpbmF0ZXMNCi0gQWNjZWxlcmF0aW9uIEZhY3RvcjogV2hlbiB0aGUgc2xvcGUgY2hhbmdlIG1vc3QgYWJydXB0bHkgKGVsYm93KQ0KLSBOb3RlOiAqRG9uJ3QgdXNlIHRoZSBkZWZhdWx0IG9mICIxIiBoZXVyaXN0aWMgdGhhdCBtYW55IHBlb3BsZSBqdXN0IGFwcGx5Ki4NCg0KYGBge3J9DQpsaWJyYXJ5KG5GYWN0b3JzKQ0KDQpldiA8LSBlaWdlbihDTSkgIyBnZXQgZWlnZW52YWx1ZXMNCmFwIDwtIHBhcmFsbGVsKHN1YmplY3Q9bnJvdyhEYXRhU2V0LjIpLHZhcj1uY29sKERhdGFTZXQuMiksDQogIHJlcD0xMDAsY2VudD0uMDUpDQpuUyA8LSBuU2NyZWUoeD1ldiR2YWx1ZXMsIGFwYXJhbGxlbD1hcCRlaWdlbiRxZXZwZWEpDQpwbG90blNjcmVlKG5TKQ0KYGBgDQoNCi0gUmVzdWx0cyBzdWdnZXN0IDYsIGJ1dCB3ZSB3aWxsIGZvcmNlIGEgNS1mYWN0b3Igc29sdXRpb24gdG8gbWF0Y2ggdGhlIEJpZyA1DQoNCiMjIyBFeHRyYWN0IHlvdXIgaW5pdGlhbCBzb2x1dGlvbg0KLSBFeHRyYWN0aW9uIG1ldGhvZHM6ICBwcmluY2lwYWwgY29tcG9uZW50cyBhbmFseXNpcyAoUENBKSwgbWF4aW11bSBsaWtlbGlob29kIChNTCksIGFuZCBwcmluY2lwYWwgYXhpcyBmYWN0b3JpbmcgKFBBRikuIA0KICAgIC0gUENBIGlzICoqbm90KiogYSBmYWN0b3IgYW5hbHlzaXMgbWV0aG9kLCBidXQgaXQgaXMgb2Z0ZW4gdXNlZCBhcyBpdCB0cmllcyB0byBwdXQgbW9zdCBvZiB0aGUgdmFyaWFuY2UgaW50byB0aGUgZmlyc3QgZmFjdG9yDQogICAgLSBNTCBpcyBvZnRlbiB1c2VkLCBidXQgaXQncyBzZW5zaXRpdmUgdG8gbm9ybWFsaXR5IHZpb2xhdGlvbnMNCiAgICAgICAgLSBNTCBhbGxvd3MgY29tcGFyaXNvbiBiZXR3ZWVuIG1vZGVscyAoSG93YXJkLCAyMDE2KSAgDQogICAgICAgIC0gTm90ZTogaWYgTUwgZml0dGluZyBmYWlscyB5b3UgY2FuIGRvIE9MUyBmaXR0ZWQgYW5hbHlzaXMgKGNhbGxpbmcgYG1pbnJlc2AsIHdoaWNoIHdpbGwgZ2l2ZSBhIHNpbWlsYXIgcmVzcG9uc2UgdG8gTUwgYW5kIGlzIHRoZSBkZWZhdWx0IGluIHRoZSBwc3ljaCBwYWNrYWdlKSANCiAgICAtIFBBRiBpcyBsZXNzIHNlbnNpdGl2ZSB0byBub3JtYWxpdHkgdmlvbGF0aW9uIGJ1dCBsZXNzIGdlbmVyYWxpemFibGUNCiAgICAgICAgLSBDYW4gYmUgbW9yZSBhY2N1cmF0ZSB1bmRlciBhc3N1bXB0aW9uIHZpb2xhdGlvbnMsIGJ1dCBsZXNzIGZsZXhpYmxlIHRoYW4gTUwgKEhvd2FyZCwgMjAxNikgIA0KICAgIC0gVGhlcmUgYXJlICoqbWFueSoqIG90aGVyIHR5cGVzLCBzdWNoIGFzIEJheWVzaWFuIG1ldGhvZHMgYW5kIG1ldGhvZHMgZm9yIHNwZWNpZmljIHR5cGVzIG9mIGRhdGEgKGJlc3QgdG8gcmVzZWFyY2ggZWFjaCBmb3IgeW91ciBzcGVjaWZpYyBpc3N1ZSBhdCBoYW5kKS4gVG9kYXkgd2Ugd2lsbCBkbyBNTCBmaXR0aW5nOg0KDQpgYGB7cn0NCm1sZS5JbnRpYWwgPC0gZmEoQ00sNSwgZm09Im1sZSIscm90YXRlPSJub25lIikNCnByaW50KG1sZS5JbnRpYWwkbG9hZGluZ3MsIGN1dG9mZj0wLjIpDQpmYS5kaWFncmFtKG1sZS5JbnRpYWwpDQpgYGANCg0KIyMgUm90YXRlIHlvdXIgZmFjdG9ycyB0byBhIGZpbmFsIHNvbHV0aW9uDQotIFRoaW5rIG9mIGVhY2ggZmFjdG9yIGEgZGltZW5zaW9uIGFuZCB0aGUgcHJvYmxlbSBpcyB0aGVyZSBpcyBhIG5lYXJseSBpbmZpbml0ZSBzZXQgb2YgZGltZW5zaW9ucyB0aGF0IGNvdWxkIGV4cGxhaW4geW91ciBkYXRhDQotIFdlIG5lZWQgYSB3YXkgdG8gaGVscCBjb21lIHVwIHdpdGggYSBzb2x1dGlvbiB0aGF0IGlzIG1vcmUgaW50ZXJwcmV0YWJsZSAoYW5kIHJlbGlhYmxlKQ0KLSBCeSByb3RhdGluZyB5b3VyIGZhY3RvcnMsIHlvdSBhdHRlbXB0IHRvIGZpbmQgYSBmYWN0b3Igc29sdXRpb24gdGhhdCBpcyBlcXVhbCB0byB0aGF0IG9idGFpbmVkIGluIHRoZSBpbml0aWFsIGV4dHJhY3Rpb24sIGJ1dCB3aGljaCBoYXMgdGhlIG1vc3Qgc3RyYWlnaHRmb3J3YXJkIGludGVycHJldGF0aW9uDQotIFRoZSBzaW1wbGVzdCBzb2x1dGlvbiBoYXMgNSBmZWF0dXJlcyAoc3VtbWFyaXplZCBpbiBBYmRpLCAyMDAzKQ0KICAgIC0gMS4gZWFjaCByb3cgY29udGFpbnMgYXQgbGVhc3Qgb25lIHplcm8NCiAgICAtIDIuIGZvciBlYWNoIGNvbHVtbiwgdGhlcmUgYXJlIGF0IGxlYXN0IGFzIG1hbnkgemVyb3MgYXMgdGhlcmUgYXJlIGNvbHVtbnMNCiAgICAtIDMuIGZvciBhbnkgcGFpciBvZiBmYWN0b3JzLCB0aGVyZSBhcmUgc29tZSB2YXJpYWJsZXMgd2l0aCB6ZXJvIGxvYWRpbmdzIG9uIG9uZSBmYWN0b3IgYW5kIGxhcmdlIGxvYWRpbmdzIG9uIHRoZSBvdGhlciBmYWN0b3INCiAgICAtIDQuIGZvciBhbnkgcGFpciBvZiBmYWN0b3JzLCB0aGVyZSBpcyBhIHNpemFibGUgcHJvcG9ydGlvbiBvZiB6ZXJvIGxvYWRpbmdzDQogICAgLSA1LiBmb3IgYW55IHBhaXIgb2YgZmFjdG9ycywgdGhlcmUgaXMgb25seSBhIHNtYWxsIG51bWJlciBvZiBsYXJnZSBsb2FkaW5ncw0KDQotIFRoZXJlIGFyZSBtYW55IGRpZmZlcmVudCB0eXBlcyBvZiByb3RhdGlvbiwgYnV0IHRoZXkgYWxsIHRyeSB0byBnZXQgdGhlIHN0cm9uZ2VzdCBlZmZlY3Qgb24gdGhlIHNtYWxsIHN1YnNldCBvZiBpdGVtcy4gDQoNClRoZXJlIGFyZSB0d28gZmFtaWxpZXMgb2Ygcm90YXRpb25zOiANCi0gT3J0aG9nb25hbCByb3RhdGlvbnM6IHVuY29ycmVsYXRlZCBmYWN0b3JzIChlLmcuLCAqKnZhcmltYXgqKiwgcXVhcnRpbWF4LCBlcXVpbWF4KQ0KLSBPYmxpcXVlIHJvdGF0aW9uczogcHJvZHVjZSBjb3JyZWxhdGVkIGZhY3RvcnMgKGUuZy4sICoqcHJvbWF4KiopDQoNCi0gVmFyaW1heCBpcyB0aGUgbW9zdCBwb3B1bGFyIG9ydGhvZ29uYWwgcm90YXRpb246ICJzaW1wbGUgc29sdXRpb24gbWVhbnMgdGhhdCBlYWNoIGZhY3RvciBoYXMgYSBzbWFsbCBudW1iZXIgb2YgbGFyZ2UgbG9hZGluZ3MgYW5kIGEgbGFyZ2UgbnVtYmVyIG9mIHplcm8gKG9yIHNtYWxsKSBsb2FkaW5ncyIgKEFiZGksIDIwMDMpDQogICAgLSBRdWFydGltYXggdHJpZXMgdG8gZmluZCBsZXNzIGZhY3RvcnMgYW5kIGVxdWltYXggYmFsYW5jZWQgYmV0d2VlbiB2YXJpbWF4IGFuZCBxdWFydGltYXgNCg0KIyMjIE9ydGhvZ29uYWwgUm90YXRpb24NCi0gTG9hZGluZ3M6IFdlIHRlbmQgdG8gY29uc2lkZXIgYSBsb2FkaW5nIGF0IGJldHdlZW4gLjMgb3IgLjQgKHNlZSBIb3dhcmQsIDIwMTYgZm9yIHJldmlldykNCiAgICAtIEhvd2FyZCwgMjAxNiByZWNvbW1lbmRzIHRoZSAuNDDigJMuMzDigJMuMjAgcnVsZTogDQogICAgICAgIC0gUHJpbWFyeSBmYWN0b3IgYWJvdmUgMC40MA0KICAgICAgICAtIEFsdGVybmF0aXZlIGZhY3RvcnMgYmVsb3cgMC4zMA0KICAgICAgICAtIERlbW9uc3RyYXRlIGEgZGlmZmVyZW5jZSBvZiAwLjIwIGJldHdlZW4gdGhlaXIgcHJpbWFyeSBhbmQgYWx0ZXJuYXRpdmUgZmFjdG9yIGxvYWRpbmdzDQoNCmBgYHtyfQ0KbWxlLlZNIDwtIGZhKENNLDUsIGZtPSJtbGUiLHJvdGF0ZT0idmFyaW1heCIpDQpwcmludChtbGUuVk0kbG9hZGluZ3MsIGN1dG9mZj0wLjIpDQpmYS5kaWFncmFtKG1sZS5WTSkNCmBgYA0KDQoNCiMjIyMgVXNlZnVsIHRvIGV4YW1pbmUgdGhlIHR3byBmYWN0b3JzIHRoYXQgYWNjb3VudCBmb3IgdGhlIG1vc3QgdmFyaWFuY2UgdmlzdWFsbHkNCi0gV2Ugd2lsbCBuZWVkIHRvIGV4dHJhY3QgdGhlIGZhY3RvciBsb2FkaW5nDQoNCmBgYHtyfQ0KVk0ubG9hZCA9IG1sZS5WTSRsb2FkaW5nc1ssMToyXQ0KcGxvdChWTS5sb2FkLCB0eXBlPSJuIikNCnRleHQoVk0ubG9hZCxsYWJlbHM9Y29sbmFtZXMoRGF0YVNldC4yKSxjZXg9Ljc1KSAjIGFkZCB2YXJpYWJsZSBuYW1lcw0KYGBgDQoNCiMjIyBPYmxpcXVlIFJvdGF0aW9uDQotIFdoZW4gd2UgZXhhbWluZSB0aGUgcHJvbWF4LCBtZWFuaW5nIG5vdyB3aWxsIGFsbG93IGZvciAoc21hbGwpIGNvcnJlbGF0aW9uIGJldHdlZW4gZmFjdG9ycy4gT2JsaXF1ZSByb3RhdGlvbnMgd2VyZSBzdWdnZXN0ZWQgYnkgVGh1cnN0b25lDQogICAgLSBQcm9tYXggcGVyZm9ybXMgYSB2YXJpbWF4IHJvdGF0aW9uLCBhbmQgdGhlbiBpdCBhbGxvd3MgdGhlIGZhY3RvcnMgdG8gY29ycmVsYXRlIHRocm91Z2ggcmFpc2luZyB0aGUgZmFjdG9yIGxvYWRpbmdzIHRvIGEgc3BlY2lmaWVkIHBvd2VyIChvZnRlbiA0KSBhbmQgdXNlZnVsIGZvciBsYXJnZSBkYXRhc2V0cyAoc2VlIEhvd2FyZCwgMjAxNikNCiAgICAtIERpcmVjdCBPYmxpbWluLCBpcyBhbm90aGVyIHBvcHVsYXIgdHlwZSwgaXMgaGFyZGVyIHRvIHVzZSBhcyBpdCByZXF1aXJlcyB5b3Ugc2V0IGEgZGVsdGENCg0KYGBge3J9DQptbGUuUE0gPC0gZmEoQ00sNSwgZm09Im1sZSIscm90YXRlPSJQcm9tYXgiKQ0KcHJpbnQobWxlLlBNJGxvYWRpbmdzLCBjdXRvZmY9MC4yKQ0KZmEuZGlhZ3JhbShtbGUuUE0pDQpgYGANCg0KIyMjIyBVc2VmdWwgdG8gZXhhbWluZSB0aGUgdHdvIGZhY3RvcnMgdGhhdCBhY2NvdW50IGZvciB0aGUgbW9zdCB2YXJpYW5jZSB2aXN1YWxseQ0KLSBXZSB3aWxsIG5lZWQgdG8gZXh0cmFjdCB0aGUgZmFjdG9yIGxvYWRpbmcNCg0KYGBge3J9DQpQTS5sb2FkID0gbWxlLlBNJGxvYWRpbmdzWywxOjJdDQpwbG90KFBNLmxvYWQsIHR5cGU9Im4iKQ0KdGV4dChQTS5sb2FkLGxhYmVscz1jb2xuYW1lcyhEYXRhU2V0LjIpLGNleD0uNzUpICMgYWRkIHZhcmlhYmxlIG5hbWVzDQpgYGANCg0KIyMgSW50ZXJwcmV0IHRoZSBMb2FkaW5ncw0KLSBUaGlzIGxvYWRpbmcgY2FuIGJlIGludGVycHJldGVkIGFzIGxpa2UgYSBwYXJ0IGNvcnJlbGF0aW9ucyAodGhlIHZhcmlhYmxlcyBhcmUgYWxsIGNvbnRyb2xsZWQgZm9yKQ0KLSBZb3UgaGF2ZSB0byBuYW1lIHRoZSBjb25zdHJ1Y3QgYmFzZWQgb24gdGhlIHBhdHRlcm4gb2YgcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIGxvYWRpbmdzDQotIFlvdSBjb3VsZCByZXZlcnNlIHNjb3JlIHRoZSBsb2FkaW5ncyB0byBtYWtlIGl0IGVhc2llcg0KLSBMZXQncyBuYW1lIG91ciBmYWN0b3JzIGZyb20gdGhlIHZhcmltYXggYW5kIHByb21heCBhbmQgc2VlIHdoYXQgb3VyIDUgcGVyc29uYWxpdHkgY29uc3RydWN0cyBtaWdodCBiZQ0KDQojIyAgRXh0cmFjdCBmYWN0b3Igc2NvcmVzDQotIFlvdSBjYW4gY29uc3RydWN0IGZhY3RvciBzY29yZXMgZm9yIGVhY2ggc3ViamVjdCBhbmQgY29uZHVjdCBhZGRpdGlvbmFsIGFuYWx5c2lzIChpbiBzdGFuZGFyZGl6ZWQgdW5pdHMgW3otc2NvcmVzXSkNCi0gRm9yIGV4YW1wbGUsIHlvdSBib2lsZWQgZG93biB0aGVzZSBpdGVtcyB0byA1IGZhY3RvcnMsIHlvdSBjYW4gcnVuIDUgcmVncmVzc2lvbiBvbiB0aGVzZSBjb25zdHJ1Y3RzICh0aGV5IGNvdWxkIGJlIHByZWRpY3RvcnMgb3IgcHJlZGljdGVkKQ0KLSBUaGVyZSBhcmUgbWFueSB3YXlzIHRvIGV4dHJhY3Qgc2NvcmVzIHBlciBwZXJzb24gYmFzZWQgb24gdGhlIGZhY3RvciBhbmFseXNpcyAoaG93IG11Y2ggZWFjaCBwZXJzb24gZml0cyB3aXRoIHRoZSBjb25zdHJ1Y3QpLCBtb3N0IGNvbW1vbiBhcmU6IA0KICAgIC0gUmVncmVzc2lvbiAoYWthIFRodXJzdG9uZSkuIERvZXMgbm90IGNvcnJlY3QgZm9yIGJpYXMgaW4gdGhlIGVzdGltYXRpb24gb2YgdGhlIHNjb3JlcyBwZXIgcGVyc29uDQogICAgLSBCYXJ0bGV0dCdzOiB3aGljaCBpcyBhbiBvbGRlciBtZXRob2QgZm9yIGNvcnJlY3RpbmcgZm9yIGJpYXMgaW4gdGhlIGVzdGltYXRpb24gb2YgdGhlIHNjb3JlcyBwZXIgcGVyc29uDQogICAgLSB0ZW4gQmVyZ2UnczogY29ycmVjdHMgZm9yIGJpYXMgYW5kIGtlZXBzIHRoZSBjb3JyZWxhdGlvbiBtYXRyaXggZm9yIG9ibGlxdWUgcm90YXRpb25zDQoNCiMjIyBSZWdyZXNzaW9uDQotIFVzZXMgdGhlIGVzdGltYXRlZCBwYXJhbWV0ZXJzIGZyb20gYSBmYWN0b3IgYW5hbHlzaXMgdG8gZGVmaW5lIGxpbmVhciBjb21iaW5hdGlvbnMgb2Ygb2JzZXJ2ZWQgdmFyaWFibGVzIHRoYXQgZ2VuZXJhdGUgZmFjdG9yIHNjb3Jlcy4gVGhlIHNjb3JlcyBtYXkgYmUgY29ycmVsYXRlZCBldmVuIHdoZW4gZmFjdG9ycyB3ZXJlIHNldCB0byBiZSBvcnRob2dvbmFsDQoNCmBgYHtyfQ0KbWxlLlZNLlNjb3JlLlIgPC0gZmFjdG9yLnNjb3JlcyhEYXRhU2V0LjIsbWxlLlZNLCBtZXRob2QgPSAiVGh1cnN0b25lIikNCiNDb3INCnJvdW5kKG1sZS5WTS5TY29yZS5SJHIuc2NvcmVzLDMpDQojIE1lYW4NCnJvdW5kKGFwcGx5KG1sZS5WTS5TY29yZS5SJHNjb3JlcywyLCBtZWFuKSwzKQ0KIyBTRA0Kcm91bmQoYXBwbHkobWxlLlZNLlNjb3JlLlIkc2NvcmVzLDIsIHNkKSwzKQ0KYGBgDQoNCiMjIyB0ZW4gQmVyZ2Uncw0KLSBDb3JyZWN0cyBmb3IgYmlhcyBpbiB0aGUgc2NvcmVzIGFuZCBrZWVwIHRoZSB2YXJpYWJsZXMgdW5jb3JyZWxhdGVkIHdoZW4gZmFjdG9ycyBhcmUgb3J0aG9nb25hbA0KDQpgYGB7cn0NCm1sZS5WTS5TY29yZS5UQiA8LSBmYWN0b3Iuc2NvcmVzKERhdGFTZXQuMixtbGUuVk0sIG1ldGhvZCA9ICJ0ZW5CZXJnZSIpDQojQ29yDQpyb3VuZChtbGUuVk0uU2NvcmUuVEIkci5zY29yZXMsMykNCiMgTWVhbg0Kcm91bmQoYXBwbHkobWxlLlZNLlNjb3JlLlRCJHNjb3JlcywyLCBtZWFuKSwzKQ0KIyBTRA0Kcm91bmQoYXBwbHkobWxlLlZNLlNjb3JlLlRCJHNjb3JlcywyLCBzZCksMykNCmBgYA0KDQotIEFsc28gd29ya3Mgd2l0aCBvdXIgb2JsaXF1ZSByb3RhdGlvbg0KDQpgYGB7cn0NCm1sZS5QTS5TY29yZS5UQiA8LSBmYWN0b3Iuc2NvcmVzKERhdGFTZXQuMixtbGUuUE0sIG1ldGhvZCA9ICJ0ZW5CZXJnZSIpDQojQ29yDQpyb3VuZChtbGUuUE0uU2NvcmUuVEIkci5zY29yZXMsMykNCiMgTWVhbg0Kcm91bmQoYXBwbHkobWxlLlBNLlNjb3JlLlRCJHNjb3JlcywyLCBtZWFuKSwzKQ0KIyBTRA0Kcm91bmQoYXBwbHkobWxlLlBNLlNjb3JlLlRCJHNjb3JlcywyLCBzZCksMykNCmBgYA0KDQojIFJlZmVyZW5jZXMNCg0KQWJkaSwgSC4gKDIwMDMpLiBGYWN0b3Igcm90YXRpb25zIGluIGZhY3RvciBhbmFseXNlcy4gKkVuY3ljbG9wZWRpYSBmb3IgUmVzZWFyY2ggTWV0aG9kcyBmb3IgdGhlIFNvY2lhbCBTY2llbmNlcyouIFNhZ2U6IFRob3VzYW5kIE9ha3MsIENBLCA3OTItNzk1Lg0KDQpIb3dhcmQsIE0uIEMuICgyMDE2KS4gQSBSZXZpZXcgb2YgRXhwbG9yYXRvcnkgRmFjdG9yIEFuYWx5c2lzIERlY2lzaW9ucyBhbmQgT3ZlcnZpZXcgb2YgQ3VycmVudCBQcmFjdGljZXM6IFdoYXQgV2UgQXJlIERvaW5nIGFuZCBIb3cgQ2FuIFdlIEltcHJvdmU/LiAqSW50ZXJuYXRpb25hbCBKb3VybmFsIG9mIEh1bWFuLUNvbXB1dGVyIEludGVyYWN0aW9uKiwgMzIoMSksIDUxLTYyLg0KDQpZb25nLCBBLiBHLiwgJiBQZWFyY2UsIFMuICgyMDEzKS4gQSBiZWdpbm5lcidzIGd1aWRlIHRvIGZhY3RvciBhbmFseXNpczogRm9jdXNpbmcgb24gZXhwbG9yYXRvcnkgZmFjdG9yIGFuYWx5c2lzLiAqVHV0b3JpYWxzIGluIFF1YW50aXRhdGl2ZSBNZXRob2RzIGZvciBQc3ljaG9sb2d5KiwgOSgyKSwgNzktOTQuDQoNCjxzY3JpcHQ+DQogIChmdW5jdGlvbihpLHMsbyxnLHIsYSxtKXtpWydHb29nbGVBbmFseXRpY3NPYmplY3QnXT1yO2lbcl09aVtyXXx8ZnVuY3Rpb24oKXsNCiAgKGlbcl0ucT1pW3JdLnF8fFtdKS5wdXNoKGFyZ3VtZW50cyl9LGlbcl0ubD0xKm5ldyBEYXRlKCk7YT1zLmNyZWF0ZUVsZW1lbnQobyksDQogIG09cy5nZXRFbGVtZW50c0J5VGFnTmFtZShvKVswXTthLmFzeW5jPTE7YS5zcmM9ZzttLnBhcmVudE5vZGUuaW5zZXJ0QmVmb3JlKGEsbSkNCiAgfSkod2luZG93LGRvY3VtZW50LCdzY3JpcHQnLCdodHRwczovL3d3dy5nb29nbGUtYW5hbHl0aWNzLmNvbS9hbmFseXRpY3MuanMnLCdnYScpOw0KDQogIGdhKCdjcmVhdGUnLCAnVUEtOTA0MTUxNjAtMScsICdhdXRvJyk7DQogIGdhKCdzZW5kJywgJ3BhZ2V2aWV3Jyk7DQoNCjwvc2NyaXB0Pg0K