Multi-Level Models

  • Hierarchical designs: Students nested in classrooms [Cluster] with student-level predictors
    • We will examine the effect of adding level 1 random slopes first today
  • Multilevel designs: Students nested in classrooms with student-level and classroom-level predictors

Does clustering matter?

  • You want to measure how students respond to a new type of active learning method (computer based) in math class
  • You measure students math scores (DV) and the proportion of time (IV) they spend using the computer
    • You expect that the more time they spend doing the active learning method, the higher their math test scores will be
  • You have access to different classrooms, but only 20 students per class (too small to publish)
    • So you decide to treat all students like they are from the same classroom

Simulation

  • Set the same slope per class, but set a different intercept per class. Create 4 linear regressions and merge them.
  • Note: You would not “know” that the slopes were the same and the intercepts differed.This is not how we would normally simulate this, but I have use this format to make match the simulations I have been showing you all semester.
#Set seed so your answers are all the same
set.seed(9)
# Sample Per class room people
n1 <- 20; n2 <- 20; n3 <- 20; n4 <- 20
N<-n1+n2+n3+n4 # Total N
# Uniform distrobution of proportion of time per classroom
X1 <- runif(n1,  0, .35)
X2 <- runif(n2, .3, .55)
X3 <- runif(n3, .5, .75)
X4 <- runif(n4, .7,1.0)
# noise per classroom
e1 <- rnorm(n1, 0, sd=2.5)
e2 <- rnorm(n2, 0, sd=2.5)
e3 <- rnorm(n3, 0, sd=2.5)
e4 <- rnorm(n4, 0, sd=2.5)
# Intercepts per classroom
B0.1 <- 80
B0.2 <- 70
B0.3 <- 60
B0.4 <- 50
# Same slope per classroom
B1=10
# Our equation to  create Y for each classroom
Y1 = B1*scale(X1,scale=F)  + B0.1 + e1
Y2 = B1*scale(X2,scale=F)  + B0.2 + e2
Y3 = B1*scale(X3,scale=F)  + B0.3 + e3
Y4 = B1*scale(X4,scale=F)  + B0.4 + e4
# Merge classrooms into 1 data.frame
Math.Data<-data.frame(Math=c(Y1,Y2,Y3,Y4),ActiveTime=c(X1,X2,X3,X4),
                      Classroom=c(rep("C1",n1),rep("C2",n2),rep("C3",n3),rep("C4",n4)),
                      StudentID=as.factor(1:N))

Step 1: Plot our results

  • We will ignore classroom and simply plot the results
    • Note: *ggplot2 lets us plot the results with a regression line automatically added
      • ggplot2 works by adding “layers” to our data
library(ggplot2)
theme_set(theme_bw())
ClassRoom.Plot.1 <-ggplot(data = Math.Data, 
                          aes(x = ActiveTime, y=Math))+ #scaffold
  geom_point()+ # add layer of scatterplot
  geom_smooth(method = "lm", se = TRUE)+ # add regression line
  xlab("Proportion of Time Engaged in Active Learning")+
  ylab("Math Score") # add labels
ClassRoom.Plot.1 #call plot

Step 2: Run Regression

  • We will run the regression across all classrooms
library(stargazer)
Class.All<-lm(Math~ActiveTime, data = Math.Data)
stargazer(Class.All,type="latex",
          intercept.bottom = FALSE, single.row=TRUE, 
          star.cutoffs=c(.05,.01,.001), notes.append = FALSE,
          header=FALSE)
  • Results show that a high intercept = 84.52 and a strong negative slope = -38.23 and a very high \(R^2\) = 0.8
  • You expected a positive slope, but the graph shows a negative one (and so clearly at that!)
  • With your massive \(R^2\) and counterintuitively strong negative slope you rush to publish in Psych Science and report, “Active learning is a flop! Dont waste your class time!”
    • Impact: Every math teacher in America changes their teaching, and the next generation of math students score worse than the last generation of student. You ruined America’s future. Good job!
    • What went wrong? Your \(R^2\) and slope were so huge you could not be possible wrong?!

What is Random?

  • Each kid add noise (is a random effect)
  • But are all the kids from all the classroom adding independent noise?
    • No! Students are not independent of each of other within the classroom
      • Error could be systematic (e.g., class clowns, class moral, room temp, time of day for the class) or unsystematic error, but that error that is unrelated to your main question
  • Each classroom is, therefore a cluster where the kids scores are inter-related based their shared experience
    • The kids are nested in the classroom!
      • Another way to think about it: The kids are repeated measurements of their classroom

Re-examine by Cluster

  • Hierarchical designs: Students nested in classrooms [Clustered] with student-level predictors Level 1
ClassRoom.Plot.2 <-ggplot(data = Math.Data, 
                          aes(x = ActiveTime, y=Math))+
  geom_point(aes(colour = Classroom, shape=Classroom))+ # we add color by cluster
  geom_smooth(method = "lm", se = TRUE)+
  xlab("Proportion of Time Engaged in Active Learning")+
  ylab("Math Score")+ # add labels
  theme(legend.position = "top")
ClassRoom.Plot.2

  • Clearly there are difference between classrooms (Note: real data is never this well clustered)
  • We can rerun the regression within each cluster to see the correct slopes.
ClassRoom.Plot <-ggplot(data = Math.Data, 
                        aes(x = ActiveTime, y=Math))+ 
  geom_point(aes(colour = Classroom, shape=Classroom))+
  geom_smooth(method = "lm", se = TRUE, aes(group = Classroom))+# we add group level
  xlab("Proportion of Time Engaged in Active Learning")+ylab("Math Score")+ # add labels
  theme(legend.position = "top")
ClassRoom.Plot

  • The slopes look positive now!
  • We need a regression method that capture the differences in the clusters and correctly estimate our slopes.

Hierarchical Linear Modeling

  • We will focus today only 2 level model [students nested in classroom]

Levels

  • Level 1 = Smallest level (often subjects/students)
  • Level 2 = The group/cluster the students belong too (classrooms)
  • You can have higher levels as well, such as Level 3 = Classrooms nested in schools

HLM with only Level 1 predictors

  • Controlling for classroom differences, but making predictions about students

Equations (two level model)

  • Quick review:

\[ Y_i = \beta_1X_1 +\beta_0 + \epsilon\]

Level 1

  • Within each Group/Cluster (Students = \(i\) within Classroom 1 = \(j\), and we would replace \(j\) with \(g\) for Classroom 2 and so forth for each classroom!) \[y_{ij} = B_{1j}X_{ij} + B_{0j} + r_{ij} \]

  • Where \(B_{0j}\) = intercept in group j

  • Where \(B_{ij}\) = slope of students in group j

  • Where \(r_{ij}\) = residuals of each i (student) within group j

Level 2

  • Each Group/Cluster (Students = \(i\) within Classroom 1 = \(j\), Classroom 2 = \(g\), and so forth for each classroom)

Level 2 Intercept

\[B_{0j} = \gamma_{00}+u_{0j} \]

  • Where \(\gamma_{00}\) = intercept of the classroom
  • Where \(u_{0j}\) = random deviation of the classroom intercept from fixed population intercept
  • We assume students will vary randomly around the population intercept of classroom

Level 2 Slope

\[B_{1j} = \gamma_{10}+u_{1j} \]

  • Where \(\gamma_{10}\) = slope of the classroom
  • Where \(u_{1j}\) = random deviation of the classroom slope from fixed population slope
  • We assume students will vary randomly around the population intercept of classroom
  • Note: We would repeat this for each level of classroom we have (g, etc)

Mixed Equation

  • We put level 1 and level 2 together \[y_{ij} = (\gamma_{10}+u_{1j})X_{ij} + (\gamma_{00}+u_{0j})+ r_{ij} \]

Variance Components

  • We now have multiple sources of error in our equation
  • \(\sigma^2\) = variance of \(r_{ij}\), AKA variance due to error at level 1
  • \(\tau_{00}\) = variance of \(u_{0j}\), AKA variance of random intercepts at level 2
  • \(\tau_{11}\) = variance of \(u_{1j}\), AKA variance of random slopes at level 2
  • \(\tau_{01}\) = co-variance of \(u_{0j}\) and \(u_{1j}\), AKA random slopes and random intercept can correlate

ICC

  • The degree of clustering is measured via the inter-class correlation ICC

  • The proportion of total variance that is between the groups \[ ICC = \frac{\tau}{\tau + \sigma^2}\]

  • \(\tau\) = variance in a variable due to differences between a group

  • \(\sigma^2\) = total variance across groups

  • We would fit this on the null model (no level 1 predictors)

    • OLS regression assumes, ICC = 0: each classroom is unrelated to the others
    • If this value is large, it means that variance can be attributed to level 2

HLM analysis [Random Intecepts only Model]

  • Linear regressions only has fixed-effects [continuous] or fixed-factors [groups]: variables of interest (often your manipulation)
    • The only random factor assumed is your subject’s response
  • What if you have other effect or factors that might add random variation?
    • Random-effects/factors: Levels randomly sampled from a much larger population: Something you want to generalize across: Subjects, Classrooms, Words, Pictures, etc

LME4 package

  • LME4 was created as a modern approach to mixed models.
  • LME4 was designed specifically to be flexible in how we can control the random effect [Crossed & Nested] and how they correlated with other.
  • What makes mixed useful (dealing with crossed and nested data) also makes it hard to get pvalues [which we will explore next week]

lmer function

  • It is similar the lm function, but we add additional random effects
  • lmer(DV ~ IV +(1|RandomFactor), data = X, REML= FALSE)
    • (1|RandomFactor), means let the intercept of the random factor vary a function of the group (cluster).
    • In our case, (1|Classroom), means that we let each classroom have its own intercept
    • REML = FALSE, means use a Maximum Likelihood estimation (not Restricted ML) [and of course not OLS]
    • Note: Kids were coded from 1 to total number of kids. We implicitly nested kids in that classrooms. Kid 1 is in classroom 1. Kids 21 is in classroom 2, etc.

Analysis

  • Lets run our analysis first without centering the IVs.

Null Model

  • No fixed factors (intercept only) with random intercept per classroom
library(lme4) 
Model.Null<-lmer(Math ~1+(1|Classroom),  
                   data=Math.Data, REML=FALSE)
summary(Model.Null)
## Linear mixed model fit by maximum likelihood  ['lmerMod']
## Formula: Math ~ 1 + (1 | Classroom)
##    Data: Math.Data
## 
##      AIC      BIC   logLik deviance df.resid 
##    408.6    415.7   -201.3    402.6       77 
## 
## Scaled residuals: 
##      Min       1Q   Median       3Q      Max 
## -2.34755 -0.72263  0.01011  0.73079  2.21975 
## 
## Random effects:
##  Groups    Name        Variance Std.Dev.
##  Classroom (Intercept) 126.884  11.264  
##  Residual                6.668   2.582  
## Number of obs: 80, groups:  Classroom, 4
## 
## Fixed effects:
##             Estimate Std. Error t value
## (Intercept)    64.70       5.64   11.47
  • Rule of thumb: t-values > |2| are significant
  • Next we examine the intercepts for each classroom (random effect)
ranef(Model.Null)
## $Classroom
##    (Intercept)
## C1   14.959676
## C2    5.307215
## C3   -5.190059
## C4  -15.076832
## 
## with conditional variances for "Classroom"
  • This is the difference from the grand mean
  • Look at ICC: The var and sd of these values are what was seen in the table above
    • \(ICC = \frac{\tau}{\tau + \sigma^2}\), where \(\tau\) = 126.884 & \(\sigma^2\) = 6.668
    • use the function in r below from the sjstats package
sjstats::icc(Model.Null)
## # Intraclass Correlation Coefficient
## 
##     Adjusted ICC: 0.950
##   Unadjusted ICC: 0.950
  • The ICC large and clearly > 0, meaning we were correct to think of this as HLM problem

Test Model

Model.1<-lmer(Math ~ActiveTime+(1|Classroom),  
                   data=Math.Data, REML=FALSE)
summary(Model.1)
## Linear mixed model fit by maximum likelihood  ['lmerMod']
## Formula: Math ~ ActiveTime + (1 | Classroom)
##    Data: Math.Data
## 
##      AIC      BIC   logLik deviance df.resid 
##    399.3    408.8   -195.6    391.3       76 
## 
## Scaled residuals: 
##      Min       1Q   Median       3Q      Max 
## -2.13111 -0.77650 -0.04481  0.61218  2.50097 
## 
## Random effects:
##  Groups    Name        Variance Std.Dev.
##  Classroom (Intercept) 200.00   14.142  
##  Residual                5.61    2.368  
## Number of obs: 80, groups:  Classroom, 4
## 
## Fixed effects:
##             Estimate Std. Error t value
## (Intercept)   58.862      7.261   8.107
## ActiveTime    11.269      3.140   3.589
## 
## Correlation of Fixed Effects:
##            (Intr)
## ActiveTime -0.224
  • Lets sum it up.
    • Our intercept was 58.86, which was significant
    • Our slope was 11.27, which was significant (and positive)
    • Our random effects changed (now we added fixed effects)

What happened to \(R^2\)?

  • To understand the \(R^2\) you need to first see the problem with predicting out random effects models

Plot the final model and \(R^2\) problems

  • I find the effects package most accurate
library(effects)
Results.Model.1<-Effect(c("ActiveTime"),Model.1,
     xlevels=list(ActiveTime=seq(0,1,.2)))
     
Results.Model.1<-as.data.frame(Results.Model.1)

Final.Fixed.Plot.1 <-ggplot(data = Results.Model.1, 
                            aes(x = ActiveTime, y =fit))+
  geom_line(size=2)+
  coord_cartesian(xlim = c(0, 1),ylim = c(0, 100))+ 
  geom_ribbon(aes(ymin=fit-se, ymax=fit+se),alpha=.2)+
  xlab("Proportion of Time Engaged in Active Learning")+
  ylab("Math Score")+ 
  theme(legend.position = "none")
Final.Fixed.Plot.1

  • Someone will ask you, “I want to see the real data”, meaning they want to see how the line fits the data.
  • We plot the fixed effect on top of the raw data
#Fixed only, removed random effects
Math.Data$Model.1.Fitted<-predict(Model.1, re.form=NA)
     
ClassRoom.Plot.Bad <-ggplot(data = Math.Data)+ 
  coord_cartesian(xlim = c(0, 1),ylim = c(0, 100))+ 
  geom_point( aes(x = ActiveTime, y=Math, 
                                   colour = Classroom, shape=Classroom))+
  geom_line(aes(x = ActiveTime, y=Model.1.Fitted))+
  xlab("Proportion of Time Engaged in Active Learning")+
  ylab("Math Score")+ 
  theme(legend.position = "top")
ClassRoom.Plot.Bad

  • Next, they will say, “Wow your model sucks and your \(R^2\) must be terrible. You suck as a modeler!”
  • To test the \(R^2\) of the fixed effect of the final model over the raw data (meaning we IGNORE the random effects), all we have to do is run a linear regression of the fitted data fixed data to the raw data
  • Nakagawa and Schielzeth (2013): function from https://github.com/jslefche/rsquared.glmm/blob/master/rsquaredglmm.R
    • \(R^2_{Marginal}\) = (fixed variance / Total variance)

\[ R^2_{Marginal} = \frac{\sigma^2_{fixed}} {\sigma^2_{fixed}+\sigma^2_{random}+\sigma^2_{residual}} \]

library(performance)
r2_nakagawa(Model.1)
## # R2 for Mixed Models
## 
##   Conditional R2: 0.974
##      Marginal R2: 0.044
  • The Marginal \(R^2\) is terrible.

  • Often you cannot simply add a scatterplot. However, there are ways around this (but you end up plotting every classroom)

  • There is a fast and dirty method (but it will not work well when you have lots of control variables)

# Fixed + Random effects
Math.Data$Model.1.Fitted.Random<-predict(Model.1) 
     
ClassRoom.Plot.Better <-ggplot(data = Math.Data)+ 
  coord_cartesian(xlim = c(0, 1),ylim = c(0, 100))+ 
  geom_point(aes(x = ActiveTime, y=Math, 
                                   colour = Classroom, shape=Classroom))+
  geom_line(aes(x = ActiveTime, y=Model.1.Fitted.Random, 
                                  colour = Classroom))+
  xlab("Proportion of Time Engaged in Active Learning")+
  ylab("Math Score")+
  theme(legend.position = "top")
ClassRoom.Plot.Better

  • This is what the model basically fit (controlling for the random effect)!

  • a more accurate way to graph it is show the data in its clusters

ClassRoom.Plot.Best <-ggplot(data = Math.Data, group=Classroom)+ 
  facet_wrap(~Classroom)+
  coord_cartesian(xlim = c(0, 1),ylim = c(25, 100))+ 
  geom_point(aes(x = ActiveTime, y=Math, 
                                   colour = Classroom, shape=Classroom))+
  geom_line(aes(x = ActiveTime, y=Model.1.Fitted.Random))+
  xlab("Proportion of Time Engaged in Active Learning")+
  ylab("Math Score")+
  theme(legend.position = "top")
ClassRoom.Plot.Best

  • The conditional \(R^2\) will be crazy high now (fixed variance + random variance/ Total variance)

\[ R^2_{conditional} = \frac{\sigma^2_{fixed}+\sigma^2_{random}} {\sigma^2_{fixed}+\sigma^2_{random}+\sigma^2_{residual}} \]

r2_nakagawa(Model.1)
## # R2 for Mixed Models
## 
##   Conditional R2: 0.974
##      Marginal R2: 0.044

\(R^2\)

  • Both \(R^2\) values (fixed or fixed + random) don’t really have any meaning in the traditional sense
    • I showed you before with ICC how much of the variance was explained by the random effect
  • Now I am just making it look like I fit all the data and I am the best modeler in the world.
    • In reality it is hard to really how good our fit is, so instead, we ask the question is the model with our predicts a better “fit” than our null or lower order model.

Deviance testing (-2 Log-Likelihood)

  • We will cover the theory of this more next week
  • For now just look at the pvalue (is model 1 better than the null model)
anova(Model.Null,Model.1)
## Data: Math.Data
## Models:
## Model.Null: Math ~ 1 + (1 | Classroom)
## Model.1: Math ~ ActiveTime + (1 | Classroom)
##            npar    AIC    BIC  logLik deviance  Chisq Df Pr(>Chisq)    
## Model.Null    3 408.59 415.74 -201.30   402.59                         
## Model.1       4 399.27 408.80 -195.64   391.27 11.321  1  0.0007662 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Centering

  • I have not yet shown you how to center the IV correctly
  • There are two approaches: Grand mean vs Group centering
    • When to center at the grand mean or the group?
      • This depends on your question and the data

Grand mean

  • You may want to ask a grand mean question (how all people are impacted by the predictor regardless of group), but the data may not allow that question to be asked (if the predictor is confounded with group)
    • For example, in our example its dangerous to make predictions as ActiveTime range is confounded with classroom!
      • Remember be careful about making predictions about data you do not have
Math.Data$ActiveTime.C<-scale(Math.Data$ActiveTime, center=TRUE, scale=FALSE)
Model.1.GM<-lmer(Math ~ActiveTime.C+(1|Classroom),  
                   data=Math.Data, REML=FALSE)
summary(Model.1.GM)
## Linear mixed model fit by maximum likelihood  ['lmerMod']
## Formula: Math ~ ActiveTime.C + (1 | Classroom)
##    Data: Math.Data
## 
##      AIC      BIC   logLik deviance df.resid 
##    399.3    408.8   -195.6    391.3       76 
## 
## Scaled residuals: 
##      Min       1Q   Median       3Q      Max 
## -2.13111 -0.77650 -0.04481  0.61218  2.50097 
## 
## Random effects:
##  Groups    Name        Variance Std.Dev.
##  Classroom (Intercept) 200.00   14.142  
##  Residual                5.61    2.368  
## Number of obs: 80, groups:  Classroom, 4
## 
## Fixed effects:
##              Estimate Std. Error t value
## (Intercept)    64.704      7.076   9.144
## ActiveTime.C   11.269      3.140   3.589
## 
## Correlation of Fixed Effects:
##             (Intr)
## ActiveTim.C 0.000
  • You will notice the slope is the same as before, but intercept changed
  • The intercept is now the mean of ActiveTime = 0.5183501
  • Why? See below:
Math.Data$Model.1.Fitted.Random.GM<-predict(Model.1.GM) #plots fixed Random effects
     
ClassRoom.Plot.Better <-ggplot()+ 
  coord_cartesian(xlim = c(-.5,.5),ylim = c(0, 100))+ 
  geom_point(data = Math.Data, aes(x = ActiveTime.C, y=Math, 
                                   colour = Classroom, shape=Classroom))+
  geom_line(data = Math.Data, aes(x = ActiveTime.C, y=Model.1.Fitted.Random.GM, 
                                  colour = Classroom))+
  xlab("Proportion of Time Engaged in Active Learning\n(Grand Mean Centered)")+
  ylab("Math Score")+
  theme(legend.position = "top")
ClassRoom.Plot.Better

Group centering

  • Group centering asks how does the within-group variation of the predictor predicts the outcome
    • This is often a very interesting question and may not always differ in story from the grand mean
  • However, technically its meaning has changed!
    • So, you must think about what it means for your data to have centered within group
### Rescale IV
library(dplyr)
Math.Data<-Math.Data %>% 
  group_by(Classroom) %>% 
  mutate(ActiveTime.Class.Centered = ActiveTime - mean(ActiveTime))
  • In this case our model will change a bit
Model.1.GC<-lmer(Math ~ActiveTime.Class.Centered+(1|Classroom),  
                   data=Math.Data, REML=FALSE)
summary(Model.1.GC)
## Linear mixed model fit by maximum likelihood  ['lmerMod']
## Formula: Math ~ ActiveTime.Class.Centered + (1 | Classroom)
##    Data: Math.Data
## 
##      AIC      BIC   logLik deviance df.resid 
##    397.4    406.9   -194.7    389.4       76 
## 
## Scaled residuals: 
##      Min       1Q   Median       3Q      Max 
## -2.16057 -0.78325 -0.04672  0.62451  2.50992 
## 
## Random effects:
##  Groups    Name        Variance Std.Dev.
##  Classroom (Intercept) 126.937  11.267  
##  Residual                5.606   2.368  
## Number of obs: 80, groups:  Classroom, 4
## 
## Fixed effects:
##                           Estimate Std. Error t value
## (Intercept)                 64.704      5.640  11.473
## ActiveTime.Class.Centered   11.990      3.159   3.795
## 
## Correlation of Fixed Effects:
##             (Intr)
## ActvTm.Cl.C 0.000
  • The slope and intercept changed. Why? See below:
Math.Data$Model.1.Fitted.Random.GC<-predict(Model.1.GC) #plots fixed Random effects
     
ClassRoom.Plot.Better <-ggplot()+ 
  coord_cartesian(xlim = c(-.3,.3),ylim = c(0, 100))+ 
  geom_point(data = Math.Data, aes(x = ActiveTime.Class.Centered, y=Math, 
                                   colour = Classroom, shape=Classroom))+
  geom_line(data = Math.Data, aes(x = ActiveTime.Class.Centered, y=Model.1.Fitted.Random.GC, 
                                  colour = Classroom))+
  xlab("Proportion of Time Engaged in Active Learning\n(Centered per Classroom)")+
  ylab("Math Score")+
  theme(legend.position = "top")
ClassRoom.Plot.Better

  • Make sure to pay attention to the x-axis!
LS0tDQp0aXRsZTogJ0hpZXJhcmNoaWNhbC1MaW5lYXIgTW9kZWxzJw0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIGZvbnRzaXplOiA4cHQNCiAgICBoaWdobGlnaHQ6IHRleHRtYXRlDQogICAgbnVtYmVyX3NlY3Rpb25zOiBubw0KICAgIHRoZW1lOiBmbGF0bHkNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogbm8NCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmtuaXRyOjpvcHRzX2NodW5rJHNldChtZXNzYWdlID0gRkFMU0UpDQprbml0cjo6b3B0c19jaHVuayRzZXQod2FybmluZyA9ICBGQUxTRSkNCmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcud2lkdGg9My4yNSkNCmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcuaGVpZ2h0PTMuMCkNCmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcuYWxpZ249J2NlbnRlcicpIA0KYGBgDQoNCg0KYGBge3IsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojc2V0d2QoJ0M6L1dlYnNpdGUvV2Vic2l0ZScpDQpgYGANCg0KIyBNdWx0aS1MZXZlbCBNb2RlbHMNCi0gSGllcmFyY2hpY2FsIGRlc2lnbnM6IFN0dWRlbnRzIG5lc3RlZCBpbiBjbGFzc3Jvb21zIFsqKkNsdXN0ZXIqKl0gd2l0aCBzdHVkZW50LWxldmVsIHByZWRpY3RvcnMgDQogICAgLSBXZSB3aWxsIGV4YW1pbmUgdGhlIGVmZmVjdCBvZiBhZGRpbmcgbGV2ZWwgMSByYW5kb20gc2xvcGVzIGZpcnN0IHRvZGF5DQotIE11bHRpbGV2ZWwgZGVzaWduczogU3R1ZGVudHMgbmVzdGVkIGluIGNsYXNzcm9vbXMgd2l0aCAqc3R1ZGVudC1sZXZlbCogYW5kICpjbGFzc3Jvb20tbGV2ZWwqIHByZWRpY3RvcnMgDQogICAgLSBFeGFtaW5lIHJhbmRvbSBzbG9wZXMgb2YgbGV2ZWwgMiB2YXJpYWJsZXMgKHNlZSBodHRwOi8vd3d3LmFsZXhhbmRlcmRlbW9zLm9yZy9NaXhlZDMuaHRtbDsgV2Ugd2lsbCBub3QgZG8gdGhpcyB0b2RheSkNCg0KIyBEb2VzIGNsdXN0ZXJpbmcgbWF0dGVyPw0KLSBZb3Ugd2FudCB0byBtZWFzdXJlIGhvdyBzdHVkZW50cyByZXNwb25kIHRvIGEgbmV3IHR5cGUgb2YgYWN0aXZlIGxlYXJuaW5nIG1ldGhvZCAoY29tcHV0ZXIgYmFzZWQpIGluIG1hdGggY2xhc3MNCi0gWW91IG1lYXN1cmUgc3R1ZGVudHMgKiptYXRoIHNjb3JlcyoqIChEVikgYW5kIHRoZSAqKnByb3BvcnRpb24gb2YgdGltZSoqIChJVikgdGhleSBzcGVuZCB1c2luZyB0aGUgY29tcHV0ZXINCiAgICAtIFlvdSBleHBlY3QgdGhhdCB0aGUgbW9yZSB0aW1lIHRoZXkgc3BlbmQgZG9pbmcgdGhlIGFjdGl2ZSBsZWFybmluZyBtZXRob2QsIHRoZSBoaWdoZXIgdGhlaXIgbWF0aCB0ZXN0IHNjb3JlcyB3aWxsIGJlDQotIFlvdSBoYXZlIGFjY2VzcyB0byBkaWZmZXJlbnQgY2xhc3Nyb29tcywgYnV0IG9ubHkgKioyMCoqIHN0dWRlbnRzIHBlciBjbGFzcyAodG9vIHNtYWxsIHRvIHB1Ymxpc2gpDQogICAgLSBTbyB5b3UgZGVjaWRlIHRvIHRyZWF0IGFsbCBzdHVkZW50cyBsaWtlIHRoZXkgYXJlIGZyb20gdGhlIHNhbWUgY2xhc3Nyb29tDQoNCiMjIFNpbXVsYXRpb24gDQotIFNldCB0aGUgc2FtZSBzbG9wZSBwZXIgY2xhc3MsIGJ1dCBzZXQgYSBkaWZmZXJlbnQgaW50ZXJjZXB0IHBlciBjbGFzcy4gQ3JlYXRlIDQgbGluZWFyIHJlZ3Jlc3Npb25zIGFuZCBtZXJnZSB0aGVtLiANCi0gKk5vdGUqOiBZb3Ugd291bGQgbm90ICJrbm93IiB0aGF0IHRoZSBzbG9wZXMgd2VyZSB0aGUgc2FtZSBhbmQgdGhlIGludGVyY2VwdHMgZGlmZmVyZWQuKlRoaXMgaXMgbm90IGhvdyB3ZSB3b3VsZCBub3JtYWxseSBzaW11bGF0ZSB0aGlzLCBidXQgSSBoYXZlIHVzZSB0aGlzIGZvcm1hdCB0byBtYWtlIG1hdGNoIHRoZSBzaW11bGF0aW9ucyBJIGhhdmUgYmVlbiBzaG93aW5nIHlvdSBhbGwgc2VtZXN0ZXIuKiANCg0KYGBge3J9DQojU2V0IHNlZWQgc28geW91ciBhbnN3ZXJzIGFyZSBhbGwgdGhlIHNhbWUNCnNldC5zZWVkKDkpDQojIFNhbXBsZSBQZXIgY2xhc3Mgcm9vbSBwZW9wbGUNCm4xIDwtIDIwOyBuMiA8LSAyMDsgbjMgPC0gMjA7IG40IDwtIDIwDQpOPC1uMStuMituMytuNCAjIFRvdGFsIE4NCiMgVW5pZm9ybSBkaXN0cm9idXRpb24gb2YgcHJvcG9ydGlvbiBvZiB0aW1lIHBlciBjbGFzc3Jvb20NClgxIDwtIHJ1bmlmKG4xLCAgMCwgLjM1KQ0KWDIgPC0gcnVuaWYobjIsIC4zLCAuNTUpDQpYMyA8LSBydW5pZihuMywgLjUsIC43NSkNClg0IDwtIHJ1bmlmKG40LCAuNywxLjApDQojIG5vaXNlIHBlciBjbGFzc3Jvb20NCmUxIDwtIHJub3JtKG4xLCAwLCBzZD0yLjUpDQplMiA8LSBybm9ybShuMiwgMCwgc2Q9Mi41KQ0KZTMgPC0gcm5vcm0objMsIDAsIHNkPTIuNSkNCmU0IDwtIHJub3JtKG40LCAwLCBzZD0yLjUpDQojIEludGVyY2VwdHMgcGVyIGNsYXNzcm9vbQ0KQjAuMSA8LSA4MA0KQjAuMiA8LSA3MA0KQjAuMyA8LSA2MA0KQjAuNCA8LSA1MA0KIyBTYW1lIHNsb3BlIHBlciBjbGFzc3Jvb20NCkIxPTEwDQojIE91ciBlcXVhdGlvbiB0byAgY3JlYXRlIFkgZm9yIGVhY2ggY2xhc3Nyb29tDQpZMSA9IEIxKnNjYWxlKFgxLHNjYWxlPUYpICArIEIwLjEgKyBlMQ0KWTIgPSBCMSpzY2FsZShYMixzY2FsZT1GKSAgKyBCMC4yICsgZTINClkzID0gQjEqc2NhbGUoWDMsc2NhbGU9RikgICsgQjAuMyArIGUzDQpZNCA9IEIxKnNjYWxlKFg0LHNjYWxlPUYpICArIEIwLjQgKyBlNA0KIyBNZXJnZSBjbGFzc3Jvb21zIGludG8gMSBkYXRhLmZyYW1lDQpNYXRoLkRhdGE8LWRhdGEuZnJhbWUoTWF0aD1jKFkxLFkyLFkzLFk0KSxBY3RpdmVUaW1lPWMoWDEsWDIsWDMsWDQpLA0KICAgICAgICAgICAgICAgICAgICAgIENsYXNzcm9vbT1jKHJlcCgiQzEiLG4xKSxyZXAoIkMyIixuMikscmVwKCJDMyIsbjMpLHJlcCgiQzQiLG40KSksDQogICAgICAgICAgICAgICAgICAgICAgU3R1ZGVudElEPWFzLmZhY3RvcigxOk4pKQ0KYGBgDQoNCiMjIyBTdGVwIDE6IFBsb3Qgb3VyIHJlc3VsdHMNCi0gV2Ugd2lsbCBpZ25vcmUgY2xhc3Nyb29tIGFuZCBzaW1wbHkgcGxvdCB0aGUgcmVzdWx0cw0KICAgIC0gKk5vdGUqOiAqKipnZ3Bsb3QyKiogbGV0cyB1cyBwbG90IHRoZSByZXN1bHRzIHdpdGggYSByZWdyZXNzaW9uIGxpbmUgYXV0b21hdGljYWxseSBhZGRlZA0KICAgICAgICAgLSAqKmdncGxvdDIqKiB3b3JrcyBieSBhZGRpbmcgImxheWVycyIgdG8gb3VyIGRhdGENCiAgICANCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KdGhlbWVfc2V0KHRoZW1lX2J3KCkpDQpDbGFzc1Jvb20uUGxvdC4xIDwtZ2dwbG90KGRhdGEgPSBNYXRoLkRhdGEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IEFjdGl2ZVRpbWUsIHk9TWF0aCkpKyAjc2NhZmZvbGQNCiAgZ2VvbV9wb2ludCgpKyAjIGFkZCBsYXllciBvZiBzY2F0dGVycGxvdA0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IFRSVUUpKyAjIGFkZCByZWdyZXNzaW9uIGxpbmUNCiAgeGxhYigiUHJvcG9ydGlvbiBvZiBUaW1lIEVuZ2FnZWQgaW4gQWN0aXZlIExlYXJuaW5nIikrDQogIHlsYWIoIk1hdGggU2NvcmUiKSAjIGFkZCBsYWJlbHMNCkNsYXNzUm9vbS5QbG90LjEgI2NhbGwgcGxvdA0KYGBgDQoNCg0KIyMjICBTdGVwIDI6IFJ1biBSZWdyZXNzaW9uDQotIFdlIHdpbGwgcnVuIHRoZSByZWdyZXNzaW9uICoqYWNyb3NzKiogYWxsIGNsYXNzcm9vbXMNCg0KYGBge3IsIHJlc3VsdHM9ImFzaXMifQ0KbGlicmFyeShzdGFyZ2F6ZXIpDQpDbGFzcy5BbGw8LWxtKE1hdGh+QWN0aXZlVGltZSwgZGF0YSA9IE1hdGguRGF0YSkNCnN0YXJnYXplcihDbGFzcy5BbGwsdHlwZT0ibGF0ZXgiLA0KICAgICAgICAgIGludGVyY2VwdC5ib3R0b20gPSBGQUxTRSwgc2luZ2xlLnJvdz1UUlVFLCANCiAgICAgICAgICBzdGFyLmN1dG9mZnM9YyguMDUsLjAxLC4wMDEpLCBub3Rlcy5hcHBlbmQgPSBGQUxTRSwNCiAgICAgICAgICBoZWFkZXI9RkFMU0UpDQpgYGANCg0KLSBSZXN1bHRzIHNob3cgdGhhdCBhIGhpZ2ggaW50ZXJjZXB0ID0gYHIgcm91bmQoQ2xhc3MuQWxsJGNvZWZbMV0sMilgIGFuZCBhIHN0cm9uZyBuZWdhdGl2ZSBzbG9wZSA9IGByIHJvdW5kKENsYXNzLkFsbCRjb2VmWzJdLDIpYCBhbmQgYSB2ZXJ5IGhpZ2ggJFJeMiQgPSBgciByb3VuZChzdW1tYXJ5KENsYXNzLkFsbCkkci5zcXVhcmVkLDIpYA0KLSBZb3UgZXhwZWN0ZWQgYSBwb3NpdGl2ZSBzbG9wZSwgYnV0IHRoZSBncmFwaCBzaG93cyBhIG5lZ2F0aXZlIG9uZSAoYW5kIHNvIGNsZWFybHkgYXQgdGhhdCEpDQotIFdpdGggeW91ciBtYXNzaXZlICRSXjIkIGFuZCBjb3VudGVyaW50dWl0aXZlbHkgc3Ryb25nIG5lZ2F0aXZlIHNsb3BlIHlvdSBydXNoIHRvIHB1Ymxpc2ggaW4gKipQc3ljaCBTY2llbmNlKiogYW5kIHJlcG9ydCwgIkFjdGl2ZSBsZWFybmluZyBpcyBhIGZsb3AhIERvbnQgd2FzdGUgeW91ciBjbGFzcyB0aW1lISINCiAgICAtICpJbXBhY3QqOiBFdmVyeSBtYXRoIHRlYWNoZXIgaW4gQW1lcmljYSBjaGFuZ2VzIHRoZWlyIHRlYWNoaW5nLCBhbmQgdGhlIG5leHQgZ2VuZXJhdGlvbiBvZiBtYXRoIHN0dWRlbnRzIHNjb3JlICp3b3JzZSogdGhhbiB0aGUgbGFzdCBnZW5lcmF0aW9uIG9mIHN0dWRlbnQuIFlvdSBydWluZWQgQW1lcmljYSdzIGZ1dHVyZS4gR29vZCBqb2IhIA0KICAgIC0gKipXaGF0IHdlbnQgd3Jvbmc/IFlvdXIgJFJeMiQgYW5kIHNsb3BlIHdlcmUgc28gaHVnZSB5b3UgY291bGQgbm90IGJlIHBvc3NpYmxlIHdyb25nPyEqKiANCg0KIyMgV2hhdCBpcyBSYW5kb20/DQotIEVhY2gga2lkIGFkZCBub2lzZSAoaXMgYSByYW5kb20gZWZmZWN0KQ0KLSBCdXQgYXJlIGFsbCB0aGUga2lkcyBmcm9tIGFsbCB0aGUgY2xhc3Nyb29tIGFkZGluZyBpbmRlcGVuZGVudCBub2lzZT8gDQogICAgLSAqKk5vISoqIFN0dWRlbnRzIGFyZSBub3QgaW5kZXBlbmRlbnQgb2YgZWFjaCBvZiBvdGhlciB3aXRoaW4gdGhlIGNsYXNzcm9vbSANCiAgICAgICAgLSBFcnJvciBjb3VsZCBiZSBzeXN0ZW1hdGljIChlLmcuLCBjbGFzcyBjbG93bnMsIGNsYXNzIG1vcmFsLCByb29tIHRlbXAsIHRpbWUgb2YgZGF5IGZvciB0aGUgY2xhc3MpIG9yIHVuc3lzdGVtYXRpYyBlcnJvciwgYnV0IHRoYXQgZXJyb3IgdGhhdCBpcyAqdW5yZWxhdGVkKiB0byB5b3VyIG1haW4gcXVlc3Rpb24NCi0gRWFjaCBjbGFzc3Jvb20gaXMsIHRoZXJlZm9yZSBhICoqY2x1c3RlcioqIHdoZXJlIHRoZSBraWRzIHNjb3JlcyBhcmUgaW50ZXItcmVsYXRlZCBiYXNlZCB0aGVpciBzaGFyZWQgZXhwZXJpZW5jZQ0KICAgIC0gVGhlIGtpZHMgYXJlICoqbmVzdGVkKiogaW4gdGhlIGNsYXNzcm9vbSENCiAgICAgICAgLSBBbm90aGVyIHdheSB0byB0aGluayBhYm91dCBpdDogVGhlIGtpZHMgYXJlIHJlcGVhdGVkIG1lYXN1cmVtZW50cyBvZiB0aGVpciBjbGFzc3Jvb20NCiAgICAgICAgDQojIyBSZS1leGFtaW5lIGJ5IENsdXN0ZXINCi0gSGllcmFyY2hpY2FsIGRlc2lnbnM6IFN0dWRlbnRzIG5lc3RlZCBpbiBjbGFzc3Jvb21zIFsqKkNsdXN0ZXJlZCoqXSB3aXRoICpzdHVkZW50LWxldmVsIHByZWRpY3RvcnMqIFtMZXZlbCAxXQ0KDQpgYGB7cn0NCkNsYXNzUm9vbS5QbG90LjIgPC1nZ3Bsb3QoZGF0YSA9IE1hdGguRGF0YSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gQWN0aXZlVGltZSwgeT1NYXRoKSkrDQogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IENsYXNzcm9vbSwgc2hhcGU9Q2xhc3Nyb29tKSkrICMgd2UgYWRkIGNvbG9yIGJ5IGNsdXN0ZXINCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBUUlVFKSsNCiAgeGxhYigiUHJvcG9ydGlvbiBvZiBUaW1lIEVuZ2FnZWQgaW4gQWN0aXZlIExlYXJuaW5nIikrDQogIHlsYWIoIk1hdGggU2NvcmUiKSsgIyBhZGQgbGFiZWxzDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQ0KQ2xhc3NSb29tLlBsb3QuMg0KYGBgDQoNCi0gQ2xlYXJseSB0aGVyZSBhcmUgZGlmZmVyZW5jZSBiZXR3ZWVuIGNsYXNzcm9vbXMgKE5vdGU6IHJlYWwgZGF0YSBpcyBuZXZlciB0aGlzIHdlbGwgY2x1c3RlcmVkKQ0KLSBXZSBjYW4gcmVydW4gdGhlIHJlZ3Jlc3Npb24gd2l0aGluIGVhY2ggY2x1c3RlciB0byBzZWUgdGhlIGNvcnJlY3Qgc2xvcGVzLiANCg0KYGBge3J9DQpDbGFzc1Jvb20uUGxvdCA8LWdncGxvdChkYXRhID0gTWF0aC5EYXRhLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gQWN0aXZlVGltZSwgeT1NYXRoKSkrIA0KICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBDbGFzc3Jvb20sIHNoYXBlPUNsYXNzcm9vbSkpKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IFRSVUUsIGFlcyhncm91cCA9IENsYXNzcm9vbSkpKyMgd2UgYWRkIGdyb3VwIGxldmVsDQogIHhsYWIoIlByb3BvcnRpb24gb2YgVGltZSBFbmdhZ2VkIGluIEFjdGl2ZSBMZWFybmluZyIpK3lsYWIoIk1hdGggU2NvcmUiKSsgIyBhZGQgbGFiZWxzDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQ0KQ2xhc3NSb29tLlBsb3QNCmBgYA0KDQotIFRoZSBzbG9wZXMgbG9vayBwb3NpdGl2ZSBub3chIA0KLSBXZSBuZWVkIGEgcmVncmVzc2lvbiBtZXRob2QgdGhhdCBjYXB0dXJlIHRoZSBkaWZmZXJlbmNlcyBpbiB0aGUgY2x1c3RlcnMgYW5kIGNvcnJlY3RseSBlc3RpbWF0ZSBvdXIgc2xvcGVzLg0KDQojIEhpZXJhcmNoaWNhbCBMaW5lYXIgTW9kZWxpbmcNCi0gV2Ugd2lsbCBmb2N1cyB0b2RheSBvbmx5IDIgbGV2ZWwgbW9kZWwgW3N0dWRlbnRzIG5lc3RlZCBpbiBjbGFzc3Jvb21dDQoNCiMjIExldmVscw0KLSBMZXZlbCAxID0gU21hbGxlc3QgbGV2ZWwgKG9mdGVuIHN1YmplY3RzL3N0dWRlbnRzKQ0KLSBMZXZlbCAyID0gVGhlIGdyb3VwL2NsdXN0ZXIgdGhlIHN0dWRlbnRzIGJlbG9uZyB0b28gKGNsYXNzcm9vbXMpDQotIFlvdSBjYW4gaGF2ZSBoaWdoZXIgbGV2ZWxzIGFzIHdlbGwsIHN1Y2ggYXMgTGV2ZWwgMyA9IENsYXNzcm9vbXMgbmVzdGVkIGluIHNjaG9vbHMNCg0KIyMjIEhMTSB3aXRoIG9ubHkgKipMZXZlbCAxKiogcHJlZGljdG9ycyANCi0gQ29udHJvbGxpbmcgZm9yIGNsYXNzcm9vbSBkaWZmZXJlbmNlcywgYnV0IG1ha2luZyBwcmVkaWN0aW9ucyBhYm91dCBzdHVkZW50cw0KDQojIyBFcXVhdGlvbnMgKHR3byBsZXZlbCBtb2RlbCkNCi0gUXVpY2sgcmV2aWV3Og0KDQokJCBZX2kgPSBcYmV0YV8xWF8xICtcYmV0YV8wICsgXGVwc2lsb24kJA0KDQojIyMgTGV2ZWwgMQ0KLSBXaXRoaW4gZWFjaCBHcm91cC9DbHVzdGVyIChTdHVkZW50cyA9ICRpJCB3aXRoaW4gQ2xhc3Nyb29tIDEgPSAkaiQsIGFuZCB3ZSB3b3VsZCByZXBsYWNlICRqJCB3aXRoICRnJCBmb3IgQ2xhc3Nyb29tIDIgYW5kIHNvIGZvcnRoIGZvciBlYWNoIGNsYXNzcm9vbSEpDQokJHlfe2lqfSA9IEJfezFqfVhfe2lqfSArIEJfezBqfSArIHJfe2lqfSAkJA0KDQotIFdoZXJlICRCX3swan0kID0gaW50ZXJjZXB0IGluIGdyb3VwIGoNCi0gV2hlcmUgJEJfe2lqfSQgPSBzbG9wZSBvZiBzdHVkZW50cyBpbiBncm91cCBqDQotIFdoZXJlICRyX3tpan0kID0gcmVzaWR1YWxzIG9mIGVhY2ggaSAoc3R1ZGVudCkgd2l0aGluIGdyb3VwIGoNCg0KIyMjIExldmVsIDINCi0gRWFjaCBHcm91cC9DbHVzdGVyIChTdHVkZW50cyA9ICRpJCB3aXRoaW4gQ2xhc3Nyb29tIDEgPSAkaiQsIENsYXNzcm9vbSAyID0gJGckLCBhbmQgc28gZm9ydGggZm9yIGVhY2ggY2xhc3Nyb29tKQ0KDQojIyMjIExldmVsIDIgSW50ZXJjZXB0DQoNCiQkQl97MGp9ID0gXGdhbW1hX3swMH0rdV97MGp9ICQkDQoNCi0gV2hlcmUgJFxnYW1tYV97MDB9JCA9IGludGVyY2VwdCBvZiB0aGUgY2xhc3Nyb29tDQotIFdoZXJlICR1X3swan0kID0gcmFuZG9tIGRldmlhdGlvbiBvZiB0aGUgY2xhc3Nyb29tIGludGVyY2VwdCBmcm9tIGZpeGVkIHBvcHVsYXRpb24gaW50ZXJjZXB0DQotIFdlIGFzc3VtZSBzdHVkZW50cyB3aWxsIHZhcnkgcmFuZG9tbHkgYXJvdW5kIHRoZSBwb3B1bGF0aW9uIGludGVyY2VwdCBvZiBjbGFzc3Jvb20NCiANCiMjIyMgTGV2ZWwgMiBTbG9wZQ0KDQokJEJfezFqfSA9IFxnYW1tYV97MTB9K3VfezFqfSAkJA0KDQotIFdoZXJlICRcZ2FtbWFfezEwfSQgPSBzbG9wZSBvZiB0aGUgY2xhc3Nyb29tDQotIFdoZXJlICR1X3sxan0kID0gcmFuZG9tIGRldmlhdGlvbiBvZiB0aGUgY2xhc3Nyb29tIHNsb3BlIGZyb20gZml4ZWQgcG9wdWxhdGlvbiBzbG9wZQ0KLSBXZSBhc3N1bWUgc3R1ZGVudHMgd2lsbCB2YXJ5IHJhbmRvbWx5IGFyb3VuZCB0aGUgcG9wdWxhdGlvbiBpbnRlcmNlcHQgb2YgY2xhc3Nyb29tDQotIE5vdGU6IFdlIHdvdWxkIHJlcGVhdCB0aGlzIGZvciBlYWNoIGxldmVsIG9mIGNsYXNzcm9vbSB3ZSBoYXZlIChnLCBldGMpDQoNCiMjIyBNaXhlZCBFcXVhdGlvbg0KLSBXZSBwdXQgbGV2ZWwgMSBhbmQgbGV2ZWwgMiB0b2dldGhlcg0KJCR5X3tpan0gPSAoXGdhbW1hX3sxMH0rdV97MWp9KVhfe2lqfSArICAoXGdhbW1hX3swMH0rdV97MGp9KSsgcl97aWp9ICQkDQoNCiMjIyMgVmFyaWFuY2UgQ29tcG9uZW50cw0KLSBXZSBub3cgaGF2ZSBtdWx0aXBsZSBzb3VyY2VzIG9mIGVycm9yIGluIG91ciBlcXVhdGlvbg0KLSAkXHNpZ21hXjIkID0gdmFyaWFuY2Ugb2YgJHJfe2lqfSQsIEFLQSB2YXJpYW5jZSBkdWUgdG8gZXJyb3IgYXQgbGV2ZWwgMQ0KLSAkXHRhdV97MDB9JCA9IHZhcmlhbmNlIG9mICR1X3swan0kLCBBS0EgdmFyaWFuY2Ugb2YgcmFuZG9tIGludGVyY2VwdHMgYXQgbGV2ZWwgMg0KLSAkXHRhdV97MTF9JCA9IHZhcmlhbmNlIG9mICR1X3sxan0kLCBBS0EgdmFyaWFuY2Ugb2YgcmFuZG9tIHNsb3BlcyBhdCBsZXZlbCAyDQotICRcdGF1X3swMX0kID0gY28tdmFyaWFuY2Ugb2YgJHVfezBqfSQgYW5kICR1X3sxan0kLCBBS0EgcmFuZG9tIHNsb3BlcyBhbmQgcmFuZG9tIGludGVyY2VwdCBjYW4gY29ycmVsYXRlDQoNCiMjIElDQw0KLSBUaGUgZGVncmVlIG9mIGNsdXN0ZXJpbmcgaXMgbWVhc3VyZWQgdmlhIHRoZSBpbnRlci1jbGFzcyBjb3JyZWxhdGlvbiAqKklDQyoqDQotIFRoZSBwcm9wb3J0aW9uIG9mIHRvdGFsIHZhcmlhbmNlIHRoYXQgaXMgYmV0d2VlbiB0aGUgZ3JvdXBzDQokJCBJQ0MgPSBcZnJhY3tcdGF1fXtcdGF1ICsgXHNpZ21hXjJ9JCQNCg0KLSAkXHRhdSQgPSB2YXJpYW5jZSBpbiBhIHZhcmlhYmxlIGR1ZSB0byBkaWZmZXJlbmNlcyBiZXR3ZWVuIGEgZ3JvdXANCi0gJFxzaWdtYV4yJCA9IHRvdGFsIHZhcmlhbmNlIGFjcm9zcyBncm91cHMNCi0gV2Ugd291bGQgZml0IHRoaXMgb24gdGhlIG51bGwgbW9kZWwgKG5vIGxldmVsIDEgcHJlZGljdG9ycykNCiAgICAtIE9MUyByZWdyZXNzaW9uIGFzc3VtZXMsIElDQyA9IDA6ICBlYWNoIGNsYXNzcm9vbSBpcyB1bnJlbGF0ZWQgdG8gdGhlIG90aGVycw0KICAgIC0gSWYgdGhpcyB2YWx1ZSBpcyBsYXJnZSwgaXQgbWVhbnMgdGhhdCB2YXJpYW5jZSBjYW4gYmUgYXR0cmlidXRlZCB0byBsZXZlbCAyDQoNCiMgSExNIGFuYWx5c2lzIFtSYW5kb20gSW50ZWNlcHRzIG9ubHkgTW9kZWxdDQotIExpbmVhciByZWdyZXNzaW9ucyBvbmx5IGhhcyBmaXhlZC1lZmZlY3RzIFtjb250aW51b3VzXSBvciBmaXhlZC1mYWN0b3JzIFtncm91cHNdOiB2YXJpYWJsZXMgb2YgaW50ZXJlc3QgKG9mdGVuIHlvdXIgbWFuaXB1bGF0aW9uKQ0KICAgIC0gVGhlIG9ubHkgcmFuZG9tIGZhY3RvciBhc3N1bWVkIGlzIHlvdXIgc3ViamVjdCdzIHJlc3BvbnNlDQotIFdoYXQgaWYgeW91IGhhdmUgb3RoZXIgZWZmZWN0IG9yIGZhY3RvcnMgdGhhdCBtaWdodCBhZGQgcmFuZG9tIHZhcmlhdGlvbj8gDQogICAgLSBSYW5kb20tZWZmZWN0cy9mYWN0b3JzOiBMZXZlbHMgcmFuZG9tbHkgc2FtcGxlZCBmcm9tIGEgbXVjaCBsYXJnZXIgcG9wdWxhdGlvbjogU29tZXRoaW5nIHlvdSB3YW50IHRvIGdlbmVyYWxpemUgYWNyb3NzOiBTdWJqZWN0cywgQ2xhc3Nyb29tcywgV29yZHMsIFBpY3R1cmVzLCBldGMNCg0KIyMgTE1FNCBwYWNrYWdlDQotIExNRTQgd2FzIGNyZWF0ZWQgYXMgYSBtb2Rlcm4gYXBwcm9hY2ggdG8gbWl4ZWQgbW9kZWxzLiANCi0gTE1FNCB3YXMgZGVzaWduZWQgc3BlY2lmaWNhbGx5IHRvIGJlIGZsZXhpYmxlIGluIGhvdyB3ZSBjYW4gY29udHJvbCB0aGUgcmFuZG9tIGVmZmVjdCBbQ3Jvc3NlZCAmIE5lc3RlZF0gYW5kIGhvdyB0aGV5IGNvcnJlbGF0ZWQgd2l0aCBvdGhlci4gDQotIFdoYXQgbWFrZXMgbWl4ZWQgdXNlZnVsIChkZWFsaW5nIHdpdGggY3Jvc3NlZCBhbmQgbmVzdGVkIGRhdGEpIGFsc28gbWFrZXMgaXQgaGFyZCB0byBnZXQgcHZhbHVlcyBbd2hpY2ggd2Ugd2lsbCBleHBsb3JlIG5leHQgd2Vla10NCg0KIyMjIGxtZXIgZnVuY3Rpb24NCi0gSXQgaXMgc2ltaWxhciB0aGUgbG0gZnVuY3Rpb24sIGJ1dCB3ZSBhZGQgYWRkaXRpb25hbCByYW5kb20gZWZmZWN0cw0KLSBsbWVyKERWIH4gSVYgKygxfFJhbmRvbUZhY3RvciksIGRhdGEgPSBYLCBSRU1MPSBGQUxTRSkNCiAgICAtICgxfFJhbmRvbUZhY3RvciksIG1lYW5zIGxldCB0aGUgaW50ZXJjZXB0IG9mIHRoZSByYW5kb20gZmFjdG9yIHZhcnkgYSBmdW5jdGlvbiBvZiB0aGUgZ3JvdXAgKGNsdXN0ZXIpLiANCiAgICAtIEluIG91ciBjYXNlLCAoMXxDbGFzc3Jvb20pLCBtZWFucyB0aGF0IHdlIGxldCBlYWNoIGNsYXNzcm9vbSBoYXZlIGl0cyBvd24gaW50ZXJjZXB0DQogICAgLSBSRU1MID0gRkFMU0UsIG1lYW5zIHVzZSBhIE1heGltdW0gTGlrZWxpaG9vZCBlc3RpbWF0aW9uIChub3QgUmVzdHJpY3RlZCBNTCkgW2FuZCBvZiBjb3Vyc2Ugbm90IE9MU10gDQogICAgLSAqTm90ZSo6IEtpZHMgd2VyZSBjb2RlZCBmcm9tIDEgdG8gdG90YWwgbnVtYmVyIG9mIGtpZHMuIFdlICoqaW1wbGljaXRseSoqIG5lc3RlZCBraWRzIGluIHRoYXQgY2xhc3Nyb29tcy4gS2lkIDEgaXMgaW4gY2xhc3Nyb29tIDEuIEtpZHMgMjEgaXMgaW4gY2xhc3Nyb29tIDIsIGV0Yy4gDQoNCiMjIEFuYWx5c2lzDQotIExldHMgcnVuIG91ciBhbmFseXNpcyBmaXJzdCB3aXRob3V0IGNlbnRlcmluZyB0aGUgSVZzLg0KDQojIyMgTnVsbCBNb2RlbA0KLSBObyBmaXhlZCBmYWN0b3JzIChpbnRlcmNlcHQgb25seSkgd2l0aCByYW5kb20gaW50ZXJjZXB0IHBlciBjbGFzc3Jvb20NCg0KYGBge3J9DQpsaWJyYXJ5KGxtZTQpIA0KTW9kZWwuTnVsbDwtbG1lcihNYXRoIH4xKygxfENsYXNzcm9vbSksICANCiAgICAgICAgICAgICAgICAgICBkYXRhPU1hdGguRGF0YSwgUkVNTD1GQUxTRSkNCnN1bW1hcnkoTW9kZWwuTnVsbCkNCmBgYA0KDQotICpSdWxlIG9mIHRodW1iKjogdC12YWx1ZXMgPiB8MnwgYXJlIHNpZ25pZmljYW50IA0KLSBOZXh0IHdlIGV4YW1pbmUgdGhlIGludGVyY2VwdHMgZm9yIGVhY2ggY2xhc3Nyb29tIChyYW5kb20gZWZmZWN0KQ0KDQpgYGB7cn0NCnJhbmVmKE1vZGVsLk51bGwpDQpgYGANCg0KLSBUaGlzIGlzIHRoZSBkaWZmZXJlbmNlIGZyb20gdGhlIGdyYW5kIG1lYW4NCi0gTG9vayBhdCBJQ0M6IFRoZSB2YXIgYW5kIHNkIG9mIHRoZXNlIHZhbHVlcyBhcmUgd2hhdCB3YXMgc2VlbiBpbiB0aGUgdGFibGUgYWJvdmUNCiAgICAtICRJQ0MgPSBcZnJhY3tcdGF1fXtcdGF1ICsgXHNpZ21hXjJ9JCwgd2hlcmUgJFx0YXUkID0gYHIgcm91bmQoYXMubnVtZXJpYyhsYXBwbHkoc3VtbWFyeShNb2RlbC5OdWxsKSR2YXJjb3IsIGRpYWcpKSwzKWAgJiAkXHNpZ21hXjIkID0gYHIgcm91bmQoYXMubnVtZXJpYyhhdHRyKHN1bW1hcnkoTW9kZWwuTnVsbCkkdmFyY29yLCAic2MiKV4yKSwzKWANCiAgICAtIHVzZSB0aGUgZnVuY3Rpb24gaW4gciBiZWxvdyBmcm9tIHRoZSBgc2pzdGF0c2AgcGFja2FnZQ0KYGBge3J9DQpzanN0YXRzOjppY2MoTW9kZWwuTnVsbCkNCmBgYA0KDQotIFRoZSBJQ0MgbGFyZ2UgYW5kIGNsZWFybHkgPiAwLCBtZWFuaW5nIHdlIHdlcmUgY29ycmVjdCB0byB0aGluayBvZiB0aGlzIGFzIEhMTSBwcm9ibGVtDQoNCiMjIyBUZXN0IE1vZGVsDQpgYGB7cn0NCk1vZGVsLjE8LWxtZXIoTWF0aCB+QWN0aXZlVGltZSsoMXxDbGFzc3Jvb20pLCAgDQogICAgICAgICAgICAgICAgICAgZGF0YT1NYXRoLkRhdGEsIFJFTUw9RkFMU0UpDQpzdW1tYXJ5KE1vZGVsLjEpDQpgYGANCg0KLSBMZXRzIHN1bSBpdCB1cC4gDQogICAgLSBPdXIgaW50ZXJjZXB0IHdhcyBgciByb3VuZCh1bm5hbWUoZml4ZWYoTW9kZWwuMSlbMV0pLDIpYCwgd2hpY2ggd2FzIHNpZ25pZmljYW50DQogICAgLSBPdXIgc2xvcGUgd2FzIGByIHJvdW5kKHVubmFtZShmaXhlZihNb2RlbC4xKVsyXSksMilgLCB3aGljaCB3YXMgc2lnbmlmaWNhbnQgKGFuZCBwb3NpdGl2ZSkNCiAgICAtIE91ciByYW5kb20gZWZmZWN0cyBjaGFuZ2VkIChub3cgd2UgYWRkZWQgZml4ZWQgZWZmZWN0cykNCg0KIyMgV2hhdCBoYXBwZW5lZCB0byAkUl4yJD8gDQotIFRvIHVuZGVyc3RhbmQgdGhlICRSXjIkIHlvdSBuZWVkIHRvIGZpcnN0IHNlZSB0aGUgcHJvYmxlbSB3aXRoIHByZWRpY3Rpbmcgb3V0IHJhbmRvbSBlZmZlY3RzIG1vZGVscw0KDQojIyMgUGxvdCB0aGUgZmluYWwgbW9kZWwgYW5kICRSXjIkIHByb2JsZW1zDQotIEkgZmluZCB0aGUgZWZmZWN0cyBwYWNrYWdlIG1vc3QgYWNjdXJhdGUNCg0KYGBge3J9DQpsaWJyYXJ5KGVmZmVjdHMpDQpSZXN1bHRzLk1vZGVsLjE8LUVmZmVjdChjKCJBY3RpdmVUaW1lIiksTW9kZWwuMSwNCiAgICAgeGxldmVscz1saXN0KEFjdGl2ZVRpbWU9c2VxKDAsMSwuMikpKQ0KICAgICANClJlc3VsdHMuTW9kZWwuMTwtYXMuZGF0YS5mcmFtZShSZXN1bHRzLk1vZGVsLjEpDQoNCkZpbmFsLkZpeGVkLlBsb3QuMSA8LWdncGxvdChkYXRhID0gUmVzdWx0cy5Nb2RlbC4xLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IEFjdGl2ZVRpbWUsIHkgPWZpdCkpKw0KICBnZW9tX2xpbmUoc2l6ZT0yKSsNCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsIDEpLHlsaW0gPSBjKDAsIDEwMCkpKyANCiAgZ2VvbV9yaWJib24oYWVzKHltaW49Zml0LXNlLCB5bWF4PWZpdCtzZSksYWxwaGE9LjIpKw0KICB4bGFiKCJQcm9wb3J0aW9uIG9mIFRpbWUgRW5nYWdlZCBpbiBBY3RpdmUgTGVhcm5pbmciKSsNCiAgeWxhYigiTWF0aCBTY29yZSIpKyANCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KRmluYWwuRml4ZWQuUGxvdC4xDQpgYGANCg0KLSBTb21lb25lIHdpbGwgYXNrIHlvdSwgIkkgd2FudCB0byBzZWUgdGhlICpyZWFsKiBkYXRhIiwgbWVhbmluZyB0aGV5IHdhbnQgdG8gc2VlIGhvdyB0aGUgbGluZSBmaXRzIHRoZSBkYXRhLg0KLSBXZSBwbG90IHRoZSBmaXhlZCBlZmZlY3Qgb24gdG9wIG9mIHRoZSByYXcgZGF0YQ0KICAgIA0KYGBge3J9DQojRml4ZWQgb25seSwgcmVtb3ZlZCByYW5kb20gZWZmZWN0cw0KTWF0aC5EYXRhJE1vZGVsLjEuRml0dGVkPC1wcmVkaWN0KE1vZGVsLjEsIHJlLmZvcm09TkEpDQogICAgIA0KQ2xhc3NSb29tLlBsb3QuQmFkIDwtZ2dwbG90KGRhdGEgPSBNYXRoLkRhdGEpKyANCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsIDEpLHlsaW0gPSBjKDAsIDEwMCkpKyANCiAgZ2VvbV9wb2ludCggYWVzKHggPSBBY3RpdmVUaW1lLCB5PU1hdGgsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvdXIgPSBDbGFzc3Jvb20sIHNoYXBlPUNsYXNzcm9vbSkpKw0KICBnZW9tX2xpbmUoYWVzKHggPSBBY3RpdmVUaW1lLCB5PU1vZGVsLjEuRml0dGVkKSkrDQogIHhsYWIoIlByb3BvcnRpb24gb2YgVGltZSBFbmdhZ2VkIGluIEFjdGl2ZSBMZWFybmluZyIpKw0KICB5bGFiKCJNYXRoIFNjb3JlIikrIA0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikNCkNsYXNzUm9vbS5QbG90LkJhZA0KYGBgDQoNCi0gTmV4dCwgdGhleSB3aWxsIHNheSwgIldvdyB5b3VyIG1vZGVsIHN1Y2tzIGFuZCB5b3VyICRSXjIkIG11c3QgYmUgdGVycmlibGUuIFlvdSBzdWNrIGFzIGEgbW9kZWxlciEiDQotIFRvIHRlc3QgdGhlICRSXjIkIG9mIHRoZSBmaXhlZCBlZmZlY3Qgb2YgdGhlIGZpbmFsIG1vZGVsIG92ZXIgdGhlIHJhdyBkYXRhIChtZWFuaW5nIHdlIElHTk9SRSB0aGUgcmFuZG9tIGVmZmVjdHMpLCBhbGwgd2UgaGF2ZSB0byBkbyBpcyBydW4gYSBsaW5lYXIgcmVncmVzc2lvbiBvZiB0aGUgZml0dGVkIGRhdGEgZml4ZWQgZGF0YSB0byB0aGUgcmF3IGRhdGENCi0gTmFrYWdhd2EgYW5kIFNjaGllbHpldGggKDIwMTMpOiBmdW5jdGlvbiBmcm9tIGh0dHBzOi8vZ2l0aHViLmNvbS9qc2xlZmNoZS9yc3F1YXJlZC5nbG1tL2Jsb2IvbWFzdGVyL3JzcXVhcmVkZ2xtbS5SDQogICAgLSAkUl4yX3tNYXJnaW5hbH0kID0gKGZpeGVkIHZhcmlhbmNlIC8gVG90YWwgdmFyaWFuY2UpDQoNCiQkIA0KUl4yX3tNYXJnaW5hbH0gID0gXGZyYWN7XHNpZ21hXjJfe2ZpeGVkfX0NCntcc2lnbWFeMl97Zml4ZWR9K1xzaWdtYV4yX3tyYW5kb219K1xzaWdtYV4yX3tyZXNpZHVhbH19DQokJA0KDQpgYGB7cn0NCmxpYnJhcnkocGVyZm9ybWFuY2UpDQpyMl9uYWthZ2F3YShNb2RlbC4xKQ0KYGBgDQoNCi0gVGhlIE1hcmdpbmFsICRSXjIkIGlzIHRlcnJpYmxlLiANCg0KLSBPZnRlbiB5b3UgY2Fubm90IHNpbXBseSBhZGQgYSBzY2F0dGVycGxvdC4gSG93ZXZlciwgdGhlcmUgYXJlIHdheXMgYXJvdW5kIHRoaXMgKGJ1dCB5b3UgZW5kIHVwIHBsb3R0aW5nIGV2ZXJ5IGNsYXNzcm9vbSkNCi0gVGhlcmUgaXMgYSBmYXN0IGFuZCBkaXJ0eSBtZXRob2QgKGJ1dCBpdCB3aWxsIG5vdCB3b3JrIHdlbGwgd2hlbiB5b3UgaGF2ZSBsb3RzIG9mIGNvbnRyb2wgdmFyaWFibGVzKQ0KDQogICAgDQpgYGB7cn0NCiMgRml4ZWQgKyBSYW5kb20gZWZmZWN0cw0KTWF0aC5EYXRhJE1vZGVsLjEuRml0dGVkLlJhbmRvbTwtcHJlZGljdChNb2RlbC4xKSANCiAgICAgDQpDbGFzc1Jvb20uUGxvdC5CZXR0ZXIgPC1nZ3Bsb3QoZGF0YSA9IE1hdGguRGF0YSkrIA0KICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgMSkseWxpbSA9IGMoMCwgMTAwKSkrIA0KICBnZW9tX3BvaW50KGFlcyh4ID0gQWN0aXZlVGltZSwgeT1NYXRoLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3VyID0gQ2xhc3Nyb29tLCBzaGFwZT1DbGFzc3Jvb20pKSsNCiAgZ2VvbV9saW5lKGFlcyh4ID0gQWN0aXZlVGltZSwgeT1Nb2RlbC4xLkZpdHRlZC5SYW5kb20sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IENsYXNzcm9vbSkpKw0KICB4bGFiKCJQcm9wb3J0aW9uIG9mIFRpbWUgRW5nYWdlZCBpbiBBY3RpdmUgTGVhcm5pbmciKSsNCiAgeWxhYigiTWF0aCBTY29yZSIpKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikNCkNsYXNzUm9vbS5QbG90LkJldHRlcg0KYGBgDQoNCi0gVGhpcyBpcyB3aGF0IHRoZSBtb2RlbCBiYXNpY2FsbHkgZml0IChjb250cm9sbGluZyBmb3IgdGhlIHJhbmRvbSBlZmZlY3QpIQ0KDQotIGEgbW9yZSBhY2N1cmF0ZSB3YXkgdG8gZ3JhcGggaXQgaXMgc2hvdyB0aGUgZGF0YSBpbiBpdHMgY2x1c3RlcnMNCg0KYGBge3J9DQpDbGFzc1Jvb20uUGxvdC5CZXN0IDwtZ2dwbG90KGRhdGEgPSBNYXRoLkRhdGEsIGdyb3VwPUNsYXNzcm9vbSkrIA0KICBmYWNldF93cmFwKH5DbGFzc3Jvb20pKw0KICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgMSkseWxpbSA9IGMoMjUsIDEwMCkpKyANCiAgZ2VvbV9wb2ludChhZXMoeCA9IEFjdGl2ZVRpbWUsIHk9TWF0aCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IENsYXNzcm9vbSwgc2hhcGU9Q2xhc3Nyb29tKSkrDQogIGdlb21fbGluZShhZXMoeCA9IEFjdGl2ZVRpbWUsIHk9TW9kZWwuMS5GaXR0ZWQuUmFuZG9tKSkrDQogIHhsYWIoIlByb3BvcnRpb24gb2YgVGltZSBFbmdhZ2VkIGluIEFjdGl2ZSBMZWFybmluZyIpKw0KICB5bGFiKCJNYXRoIFNjb3JlIikrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQ0KQ2xhc3NSb29tLlBsb3QuQmVzdA0KYGBgDQoNCg0KLSBUaGUgY29uZGl0aW9uYWwgJFJeMiQgd2lsbCBiZSBjcmF6eSBoaWdoIG5vdyAoZml4ZWQgdmFyaWFuY2UgKyByYW5kb20gdmFyaWFuY2UvIFRvdGFsIHZhcmlhbmNlKQ0KDQokJCANCiBSXjJfe2NvbmRpdGlvbmFsfSA9IFxmcmFje1xzaWdtYV4yX3tmaXhlZH0rXHNpZ21hXjJfe3JhbmRvbX19DQp7XHNpZ21hXjJfe2ZpeGVkfStcc2lnbWFeMl97cmFuZG9tfStcc2lnbWFeMl97cmVzaWR1YWx9fQ0KJCQNCg0KYGBge3J9DQpyMl9uYWthZ2F3YShNb2RlbC4xKQ0KYGBgDQoNCiMjIyAkUl4yJA0KLSBCb3RoICRSXjIkIHZhbHVlcyAoZml4ZWQgb3IgZml4ZWQgKyByYW5kb20pIGRvbid0IHJlYWxseSBoYXZlIGFueSBtZWFuaW5nIGluIHRoZSB0cmFkaXRpb25hbCBzZW5zZQ0KICAgIC0gSSBzaG93ZWQgeW91IGJlZm9yZSB3aXRoIElDQyBob3cgbXVjaCBvZiB0aGUgdmFyaWFuY2Ugd2FzIGV4cGxhaW5lZCBieSB0aGUgcmFuZG9tIGVmZmVjdA0KLSBOb3cgSSBhbSBqdXN0IG1ha2luZyBpdCBsb29rIGxpa2UgSSBmaXQgYWxsIHRoZSBkYXRhIGFuZCBJIGFtIHRoZSBiZXN0IG1vZGVsZXIgaW4gdGhlIHdvcmxkLiANCiAgICAtIEluIHJlYWxpdHkgaXQgaXMgaGFyZCB0byByZWFsbHkgaG93IGdvb2Qgb3VyIGZpdCBpcywgc28gaW5zdGVhZCwgd2UgYXNrIHRoZSBxdWVzdGlvbiBpcyB0aGUgbW9kZWwgd2l0aCBvdXIgcHJlZGljdHMgYSBiZXR0ZXIgImZpdCIgdGhhbiBvdXIgbnVsbCBvciBsb3dlciBvcmRlciBtb2RlbC4gDQogICAgDQojIyBEZXZpYW5jZSB0ZXN0aW5nICgtMiBMb2ctTGlrZWxpaG9vZCkNCi0gV2Ugd2lsbCBjb3ZlciB0aGUgdGhlb3J5IG9mIHRoaXMgbW9yZSBuZXh0IHdlZWsgDQotIEZvciBub3cganVzdCBsb29rIGF0IHRoZSBwdmFsdWUgKGlzIG1vZGVsIDEgYmV0dGVyIHRoYW4gdGhlIG51bGwgbW9kZWwpDQoNCmBgYHtyfQ0KYW5vdmEoTW9kZWwuTnVsbCxNb2RlbC4xKQ0KYGBgIA0KDQojIyBDZW50ZXJpbmcNCi0gSSBoYXZlIG5vdCB5ZXQgc2hvd24geW91IGhvdyB0byBjZW50ZXIgdGhlIElWIGNvcnJlY3RseQ0KLSBUaGVyZSBhcmUgdHdvIGFwcHJvYWNoZXM6ICoqR3JhbmQgbWVhbioqIHZzICoqR3JvdXAgY2VudGVyaW5nKioNCiAgICAtIFdoZW4gdG8gY2VudGVyIGF0IHRoZSBncmFuZCBtZWFuIG9yIHRoZSBncm91cD8NCiAgICAgICAgIC0gVGhpcyBkZXBlbmRzIG9uIHlvdXIgcXVlc3Rpb24gYW5kIHRoZSBkYXRhDQoNCiMjIyBHcmFuZCBtZWFuDQotIFlvdSBtYXkgd2FudCB0byBhc2sgYSBncmFuZCBtZWFuIHF1ZXN0aW9uIChob3cgYWxsIHBlb3BsZSBhcmUgaW1wYWN0ZWQgYnkgdGhlIHByZWRpY3RvciByZWdhcmRsZXNzIG9mIGdyb3VwKSwgYnV0IHRoZSBkYXRhIG1heSBub3QgYWxsb3cgdGhhdCBxdWVzdGlvbiB0byBiZSBhc2tlZCAoaWYgdGhlIHByZWRpY3RvciBpcyBjb25mb3VuZGVkIHdpdGggZ3JvdXApDQogICAgLSBGb3IgZXhhbXBsZSwgaW4gb3VyIGV4YW1wbGUgaXRzIGRhbmdlcm91cyB0byBtYWtlIHByZWRpY3Rpb25zIGFzIEFjdGl2ZVRpbWUgcmFuZ2UgaXMgY29uZm91bmRlZCB3aXRoIGNsYXNzcm9vbSEgDQogICAgICAgIC0gKlJlbWVtYmVyIGJlIGNhcmVmdWwgYWJvdXQgbWFraW5nIHByZWRpY3Rpb25zIGFib3V0IGRhdGEgeW91IGRvIG5vdCBoYXZlKg0KDQpgYGB7cn0NCk1hdGguRGF0YSRBY3RpdmVUaW1lLkM8LXNjYWxlKE1hdGguRGF0YSRBY3RpdmVUaW1lLCBjZW50ZXI9VFJVRSwgc2NhbGU9RkFMU0UpDQpNb2RlbC4xLkdNPC1sbWVyKE1hdGggfkFjdGl2ZVRpbWUuQysoMXxDbGFzc3Jvb20pLCAgDQogICAgICAgICAgICAgICAgICAgZGF0YT1NYXRoLkRhdGEsIFJFTUw9RkFMU0UpDQpzdW1tYXJ5KE1vZGVsLjEuR00pDQpgYGANCg0KLSBZb3Ugd2lsbCBub3RpY2UgdGhlIHNsb3BlIGlzIHRoZSBzYW1lIGFzIGJlZm9yZSwgYnV0IGludGVyY2VwdCBjaGFuZ2VkDQotIFRoZSBpbnRlcmNlcHQgaXMgbm93IHRoZSBtZWFuIG9mIEFjdGl2ZVRpbWUgPSAgYHIgbWVhbihNYXRoLkRhdGEkQWN0aXZlVGltZSlgDQotIFdoeT8gU2VlIGJlbG93Og0KDQpgYGB7cn0NCk1hdGguRGF0YSRNb2RlbC4xLkZpdHRlZC5SYW5kb20uR008LXByZWRpY3QoTW9kZWwuMS5HTSkgI3Bsb3RzIGZpeGVkIFJhbmRvbSBlZmZlY3RzDQogICAgIA0KQ2xhc3NSb29tLlBsb3QuQmV0dGVyIDwtZ2dwbG90KCkrIA0KICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoLS41LC41KSx5bGltID0gYygwLCAxMDApKSsgDQogIGdlb21fcG9pbnQoZGF0YSA9IE1hdGguRGF0YSwgYWVzKHggPSBBY3RpdmVUaW1lLkMsIHk9TWF0aCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IENsYXNzcm9vbSwgc2hhcGU9Q2xhc3Nyb29tKSkrDQogIGdlb21fbGluZShkYXRhID0gTWF0aC5EYXRhLCBhZXMoeCA9IEFjdGl2ZVRpbWUuQywgeT1Nb2RlbC4xLkZpdHRlZC5SYW5kb20uR00sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IENsYXNzcm9vbSkpKw0KICB4bGFiKCJQcm9wb3J0aW9uIG9mIFRpbWUgRW5nYWdlZCBpbiBBY3RpdmUgTGVhcm5pbmdcbihHcmFuZCBNZWFuIENlbnRlcmVkKSIpKw0KICB5bGFiKCJNYXRoIFNjb3JlIikrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQ0KQ2xhc3NSb29tLlBsb3QuQmV0dGVyDQpgYGANCg0KDQojIyMgR3JvdXAgY2VudGVyaW5nDQotIEdyb3VwIGNlbnRlcmluZyBhc2tzIGhvdyBkb2VzIHRoZSB3aXRoaW4tZ3JvdXAgdmFyaWF0aW9uIG9mIHRoZSBwcmVkaWN0b3IgcHJlZGljdHMgdGhlIG91dGNvbWUNCiAgICAtIFRoaXMgaXMgb2Z0ZW4gYSB2ZXJ5IGludGVyZXN0aW5nIHF1ZXN0aW9uIGFuZCBtYXkgbm90IGFsd2F5cyBkaWZmZXIgaW4gc3RvcnkgZnJvbSB0aGUgZ3JhbmQgbWVhbg0KLSBIb3dldmVyLCB0ZWNobmljYWxseSBpdHMgbWVhbmluZyBoYXMgY2hhbmdlZCEgDQogICAgLSBTbywgeW91IG11c3QgdGhpbmsgYWJvdXQgd2hhdCBpdCBtZWFucyBmb3IgeW91ciBkYXRhIHRvIGhhdmUgY2VudGVyZWQgd2l0aGluIGdyb3VwDQoNCmBgYHtyfQ0KIyMjIFJlc2NhbGUgSVYNCmxpYnJhcnkoZHBseXIpDQpNYXRoLkRhdGE8LU1hdGguRGF0YSAlPiUgDQogIGdyb3VwX2J5KENsYXNzcm9vbSkgJT4lIA0KICBtdXRhdGUoQWN0aXZlVGltZS5DbGFzcy5DZW50ZXJlZCA9IEFjdGl2ZVRpbWUgLSBtZWFuKEFjdGl2ZVRpbWUpKQ0KYGBgDQogDQogLSBJbiB0aGlzIGNhc2Ugb3VyIG1vZGVsIHdpbGwgY2hhbmdlIGEgYml0DQogDQpgYGB7cn0NCk1vZGVsLjEuR0M8LWxtZXIoTWF0aCB+QWN0aXZlVGltZS5DbGFzcy5DZW50ZXJlZCsoMXxDbGFzc3Jvb20pLCAgDQogICAgICAgICAgICAgICAgICAgZGF0YT1NYXRoLkRhdGEsIFJFTUw9RkFMU0UpDQpzdW1tYXJ5KE1vZGVsLjEuR0MpDQpgYGANCg0KLSBUaGUgc2xvcGUgYW5kIGludGVyY2VwdCBjaGFuZ2VkLiBXaHk/IFNlZSBiZWxvdzoNCg0KYGBge3J9DQpNYXRoLkRhdGEkTW9kZWwuMS5GaXR0ZWQuUmFuZG9tLkdDPC1wcmVkaWN0KE1vZGVsLjEuR0MpICNwbG90cyBmaXhlZCBSYW5kb20gZWZmZWN0cw0KICAgICANCkNsYXNzUm9vbS5QbG90LkJldHRlciA8LWdncGxvdCgpKyANCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKC0uMywuMykseWxpbSA9IGMoMCwgMTAwKSkrIA0KICBnZW9tX3BvaW50KGRhdGEgPSBNYXRoLkRhdGEsIGFlcyh4ID0gQWN0aXZlVGltZS5DbGFzcy5DZW50ZXJlZCwgeT1NYXRoLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3VyID0gQ2xhc3Nyb29tLCBzaGFwZT1DbGFzc3Jvb20pKSsNCiAgZ2VvbV9saW5lKGRhdGEgPSBNYXRoLkRhdGEsIGFlcyh4ID0gQWN0aXZlVGltZS5DbGFzcy5DZW50ZXJlZCwgeT1Nb2RlbC4xLkZpdHRlZC5SYW5kb20uR0MsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IENsYXNzcm9vbSkpKw0KICB4bGFiKCJQcm9wb3J0aW9uIG9mIFRpbWUgRW5nYWdlZCBpbiBBY3RpdmUgTGVhcm5pbmdcbihDZW50ZXJlZCBwZXIgQ2xhc3Nyb29tKSIpKw0KICB5bGFiKCJNYXRoIFNjb3JlIikrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQ0KQ2xhc3NSb29tLlBsb3QuQmV0dGVyDQpgYGANCg0KLSBNYWtlIHN1cmUgdG8gcGF5IGF0dGVudGlvbiB0byB0aGUgeC1heGlzISAgDQogIA0KICANCiAgDQogIA0KDQoNCjxzY3JpcHQ+DQogIChmdW5jdGlvbihpLHMsbyxnLHIsYSxtKXtpWydHb29nbGVBbmFseXRpY3NPYmplY3QnXT1yO2lbcl09aVtyXXx8ZnVuY3Rpb24oKXsNCiAgKGlbcl0ucT1pW3JdLnF8fFtdKS5wdXNoKGFyZ3VtZW50cyl9LGlbcl0ubD0xKm5ldyBEYXRlKCk7YT1zLmNyZWF0ZUVsZW1lbnQobyksDQogIG09cy5nZXRFbGVtZW50c0J5VGFnTmFtZShvKVswXTthLmFzeW5jPTE7YS5zcmM9ZzttLnBhcmVudE5vZGUuaW5zZXJ0QmVmb3JlKGEsbSkNCiAgfSkod2luZG93LGRvY3VtZW50LCdzY3JpcHQnLCdodHRwczovL3d3dy5nb29nbGUtYW5hbHl0aWNzLmNvbS9hbmFseXRpY3MuanMnLCdnYScpOw0KDQogIGdhKCdjcmVhdGUnLCAnVUEtOTA0MTUxNjAtMScsICdhdXRvJyk7DQogIGdhKCdzZW5kJywgJ3BhZ2V2aWV3Jyk7DQoNCjwvc2NyaXB0Pg==