In this example, we will use Generalized Estimating Equations to do some longitudinal modeling of data from the ECLS-K 2011. Specifically, we will model changes in a student’s standardized math score as a continuous outcome and self rated health as a binomial outcome, from fall kindergarten to spring, 1st grade.

Introduction to GEE’s

Up until now, we have used (G)LMM’s to analyze data that were “clustered”

  • Persons within neighborhoods
  • Survey data in general - stratified sampling

The next topic will introduce a modeling strategy that allows us to consider clustered data, but in a different fashion

GLMMS’s

GLMM’s are commonly referred to as conditional models, because the model coefficients “\(\beta\)’s” are condition on the random effects in the model.

Likewise, the mean if conditional on the random effects. This is another way of saying that the mean for a given covariate pattern is conditional on the group that the particular person is in.

\(\mu_{ij}^c = E(Y_{ij} | u_j) = X_{ij}\beta + u_j\)

GLMMS’s and GEE’s

In contrast, Generalzed Estimating Equations are referred to as marginal models because they only estimate the overall mean.

\(\mu_{ij} = X_{ij}\beta\)

Lee and Nelder, 2004 provide a very good description of how these two methods compare to one another

Generalized Estimating Equations

  • Typically first attributed to Liang and Zeger, 1986
  • GEE’s are regression models
  • Interested in modeling the mean response, while treating correlation within person/cluster as a nuisance
  • NOT based on maximum likelihood
  • Does not need a fully specified joint distribution, only the marginal distribution (mean) of the outcome
  • Models can be for any distribution for the outcome

GEE’s

  • For longitudinal data, we assume we have \(y_{ij}\) as our outcome on person i at time j. This could just as easily be persons within other types of clusters, like counties or sampling units.
  • Also have \(X_{ij}\), the matrix of predictors
  • Specify the link function between \(y_{ij}\) and \(X_{ij}\) as in a GLM, via a link function
  • Focus is on the linear predictor of the link function - the mean
  • NOT INTERESTED in variance components ONLY regression coefficients

GEE’s

  • Covariance structure
    • We also may wish to model how observations are related to one another via some type of correlation structure between waves
    • This directly implies that observations are NOT INDEPENDENT, and that’s fine
    • Observations between clusters are independent
    • Errors are correlated
    • No assumption of common variance (homoskedsasticity)

GEE’s - Model form

A basic form of the model would be:

\(Y_{ij} = \beta_0 + \sum_k X_{ijk} \beta_k + CORR + error\)

Ordinary models will tend to over estimate the standard errors for the \(\beta\)’s for time varying predictors in a model with repeated observations, because these models do not account for the correlation within clusters  observations over time.

Likewise, the standard errors of time invariant predictors will be under estimated

GEE’s - Model estimation

Given the mean function for the model and a specified correlation function, the model parameters may be estimated by finding the solution for:

\[U(\beta) = \sum_i ^n \frac{\delta \mu_{ij}}{ \delta \beta_k} V_i^{-1} (Y_{ij} - \mu(\beta))\]

Which gives estimates of the \(\beta\)’s for the linear mean function.

GEE’s - Model estimation

  • First, a naive linear regression analysis is carried out, assuming the observations within subjects are independent.
  • Then, residuals are calculated from the naive model (observed-predicted) and a working correlation matrix is estimated from these residuals.
  • Then the regression coefficients are refit, correcting for the correlation. (Iterative process)
  • The within-subject correlation structure is treated as a nuisance variable (i.e. as a covariate)

GEE’s - Correlation Structure

For three time points per person, the ordinary regression model correlation in residuals within clusters/persons over time can be thought of as the matrix:

\[\begin{bmatrix} \sigma^2 & 0 & 0 \\ 0 & \sigma^2 &0 \\ 0 & 0 & \sigma^2 \end{bmatrix}\]

which assumed the variances are constant and the residuals are independent over time

GEE’s - Correlation Structure

But in a GEE, the model include the actual correlation between measurements over time:

\[\begin{bmatrix} \sigma_1 ^2 & a & c \\ a & \sigma_2 ^2 &b \\ b & c & \sigma_3 ^2 \end{bmatrix}\]

Which allows the variances over time to be different, as well as correlations between times to be present.

GEE’s - Correlation Structure

  • Several types of correlation/covariance are commonly used in GEE’s
  • When we fit a GEE, we have to assume a certain type of correlation for the repeated measures. These are typically:
    • Independence - same as OLS
    • Exchangeable/compound symmetry (simplest)
    • Autoregressive
    • Unstructured (most complicated)

GEE’s - Correlation Structure - Independent

\[\begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 &0 \\ 0 & 0 & 1 \end{bmatrix}\]

GEE’s - Correlation Structure - Exchangeable

\[\begin{bmatrix} 1 & \rho & \rho \\ \rho & 1 &\rho \\ \rho &\rho & 1 \end{bmatrix}\]

GEE’s - Correlation Structure - AR(1)

\[\begin{bmatrix} 1 & \rho & \rho^2 \\ \rho & 1 &\rho\\ \rho^2 & \rho & 1 \end{bmatrix}\]

GEE’s - Correlation Structure - Unstructured

\[\begin{bmatrix} 1 & \rho_1 & \rho_2 \\ \rho_1 & 1 &\rho_3 \\ \rho_2 & \rho_3& 1 \end{bmatrix}\]

library (car)
## Loading required package: carData
library(geepack)
library(MuMIn)  #may need to install
## Warning: package 'MuMIn' was built under R version 4.0.5
## 
## Attaching package: 'MuMIn'
## The following object is masked from 'package:geepack':
## 
##     QIC
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following object is masked from 'package:car':
## 
##     recode
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library("rio")
#install_formats()

Data and recodes

First we load our data

eclskk5 <- import(choose.files())
names(eclskk5)<-tolower(names(eclskk5))
#get out only the variables I'm going to use for this example



#subset the data

eclsk.sub<-eclskk5%>%
  select(childid, x_chsex_r, x1locale, x_raceth_r, x2povty, x12par1ed_i, p1curmar, x1htotal, x1mscalk5, x2mscalk5, x3mscalk5, x4mscalk5, x5mscalk5, p1hscale, p2hscale, p4hscale, x2fsstat2, x4fsstat2, x4fsstat2, x12sesl, x4sesl_i, p2parct1, p2parct2, s1_id, p2safepl, x2krceth, p1o2near, x_distpov, w1c0, w1p0, w2p0, w1c0str, w1p0str, w4c4p_40, w4c4p_4str,w4c4p_4psu, w1c0psu, w1p0psu, x1height, x2height, x4height, x4height, x5height, x1kage_r, x2kage_r, x3age, x4age, x5age)

#rm(eclsk11); gc()

Time constant variables

First, I do some recoding of variables. First, we code time invariant variables, meaning their values do not change at each wave.

#Non time varying variables
#First we recode some Child characteristics
#Child's sex: recode as male =1
eclsk.sub$male<-Recode(eclsk.sub$x_chsex_r, recodes="1=1; 2=0; -9=NA")

#Recode race with white, non Hispanic as reference using dummy vars
eclsk.sub$hisp<-Recode (eclsk.sub$x_raceth_r, recodes="3:4=1;-9=NA; else=0")
eclsk.sub$black<-Recode (eclsk.sub$x_raceth_r, recodes="2=1;-9=NA; else=0")
eclsk.sub$asian<-Recode (eclsk.sub$x_raceth_r, recodes="5=1;-9=NA; else=0")
eclsk.sub$nahn<-Recode (eclsk.sub$x_raceth_r, recodes="6:7=1;-9=NA; else=0")
eclsk.sub$other<-Recode (eclsk.sub$x_raceth_r, recodes="8=1;-9=NA; else=0")


#Then we recode some parent/mother characteristics
#Mother's education, recode as 2 dummys with HS = reference
eclsk.sub$lths<-Recode(eclsk.sub$x12par1ed_i, recodes = "0:2=1; 3:8=0; else = NA")
eclsk.sub$gths<-Recode(eclsk.sub$x12par1ed_i, recodes = "1:3=0; 4:8=1; else =NA") 

#marital status, recode as 2 dummys, ref= married
eclsk.sub$single<-Recode(eclsk.sub$p1curmar, recodes="4=1; -7:-9=NA; else=0")
eclsk.sub$notmar<-Recode(eclsk.sub$p1curmar, recodes="2:3=1; -7:-9=NA; else=0")


#Then we do some household level variables

#Urban school location = 1
eclsk.sub$urban<-Recode(eclsk.sub$x1locale, recodes = "1:3=1; 4=0; -1:-9=NA")

#poverty level in poverty = 1
eclsk.sub$pov<-Recode(eclsk.sub$x2povty , recodes ="1:2=1; 3=0; -9=NA")

#Household size
eclsk.sub$hhsize<-eclsk.sub$x1htotal

#school % minority student body
eclsk.sub$minorsch<-ifelse(eclsk.sub$x2krceth <0, NA, eclsk.sub$x2krceth/10)

#Unsafe neighborhood
eclsk.sub$unsafe<-Recode(eclsk.sub$p2safepl , recodes = "1:2='unsafe'; 3='safe'; else=NA",as.factor = T)

#school district poverty
eclsk.sub$dist_pov<-ifelse(eclsk.sub$x_distpov==-9, NA, scale(eclsk.sub$x_distpov))

Time varying variables

I have to make the repeated measures of each of my longitudinal variables. These are referred to as time varying variables, meaning their values change at each wave.

#Longitudinal variables
#recode our outcomes, the  first is the child's math standardized test score  in Kindergarten
eclsk.sub$math_1<-ifelse(eclsk.sub$x1mscalk5<0, NA, eclsk.sub$x1mscalk5)
eclsk.sub$math_2<-ifelse(eclsk.sub$x2mscalk5<0, NA, eclsk.sub$x2mscalk5)
#eclsk.sub$math3<-ifelse(eclsk.sub$x3mscalk1<0, NA, eclsk.sub$x3mscalk1)
eclsk.sub$math_4<-ifelse(eclsk.sub$x4mscalk5<0, NA, eclsk.sub$x4mscalk5)

#Second outcome is child's height for age, continuous outcome
eclsk.sub$height_1<-ifelse(eclsk.sub$x1height<=-7, NA, eclsk.sub$x1height)
eclsk.sub$height_2<-ifelse(eclsk.sub$x2height<=-7, NA, eclsk.sub$x2height)
#eclsk.sub$height3<-ifelse(eclsk.sub$x3height<=-7, NA, eclsk.sub$x3height)
eclsk.sub$height_4<-ifelse(eclsk.sub$x4height<=-7, NA, eclsk.sub$x4height)

#Age at each wave
eclsk.sub$ageyrs_1<-ifelse(eclsk.sub$x1kage_r<0, NA, eclsk.sub$x1kage_r/12)
eclsk.sub$ageyrs_2<-ifelse(eclsk.sub$x2kage_r<0, NA, eclsk.sub$x2kage_r/12)
#eclsk.sub$age_yrs3<-ifelse(eclsk.sub$x3age<0, NA, eclsk.sub$x3age/12)
eclsk.sub$ageyrs_4<-ifelse(eclsk.sub$x4age<0, NA, eclsk.sub$x4age/12)

eclsk.sub<- eclsk.sub[is.na(eclsk.sub$ageyrs_1)==F, ]

#Height for age z score standardized by sex and age
eclsk.sub$heightz_1<-ave(eclsk.sub$height_1, as.factor(paste(round(eclsk.sub$ageyrs_1, 1.5), eclsk.sub$male)), FUN=scale)
eclsk.sub$heightz_2<-ave(eclsk.sub$height_2, as.factor(paste(round(eclsk.sub$ageyrs_2, 1.5), eclsk.sub$male)), FUN=scale)
#eclsk.sub$height_z3<-ave(eclsk.sub$height3, as.factor(paste(round(eclsk.sub$age_yrs3, 1.5), eclsk.sub$male)), FUN=scale)
eclsk.sub$heightz_4<-ave(eclsk.sub$height_4, as.factor(paste(round(eclsk.sub$ageyrs_4, 1.5), eclsk.sub$male)), FUN=scale)


#Household food insecurity, dichotomous outcome
#This outcome is only present at two waves

eclsk.sub$foodinsec_1<-Recode(eclsk.sub$x2fsstat2, recodes="2:3=1; 1=0; else=NA")
eclsk.sub$foodinsec_2<-Recode(eclsk.sub$x2fsstat2, recodes="2:3=1; 1=0; else=NA")
eclsk.sub$foodinsec_4<-Recode(eclsk.sub$x4fsstat2, recodes="2:3=1; 1=0; else=NA")


#Child health assessment Excellent to poor , ordinal outcome
eclsk.sub$chhealth_1<-ifelse(eclsk.sub$p1hscale<0, NA, eclsk.sub$p1hscale)
eclsk.sub$chhealth_2<-ifelse(eclsk.sub$p2hscale<0, NA, eclsk.sub$p2hscale)
eclsk.sub$chhealth_4<-ifelse(eclsk.sub$p4hscale<0, NA, eclsk.sub$p4hscale)

#SES
eclsk.sub$hhses_1<-ifelse(eclsk.sub$x12sesl==-9, NA, scale(eclsk.sub$x12sesl))
eclsk.sub$hhses_2<-ifelse(eclsk.sub$x12sesl==-9, NA, scale(eclsk.sub$x12sesl))

eclsk.sub$hhses_4<-ifelse(eclsk.sub$x4sesl_i==-9, NA, scale(eclsk.sub$x4sesl_i))

Reshaping data into longitudinal format

To analyze data longitudinally, we must reshape the data from its current “wide” format, where each repeated measure is a column, into the “long” format, where there is a single column for a particular variable, and we account for the repeated measurements of each person. In this case, I’m going to use three waves of data, so each child can contribute up to three lines to the data.

The reshape() function will do this for us, but below I use a tidy method, using a combination of the data.table and dplyr packages. I first make a long data set of the height, age, math, child health and household SES measures, then I left join it to the time invariant variables i’ll use in my models below.

library(tidyr)
e.long.comp<-eclsk.sub%>%
  rename(wt = w4c4p_40,strata= w4c4p_4str, psu = w4c4p_4psu)%>%
  select(childid,male, hisp, black, asian, nahn, other,wt, strata, psu, #time constant
         height_1, height_2, height_4, #t-varying variables
         ageyrs_1, ageyrs_2, ageyrs_4,
         chhealth_1, chhealth_2, chhealth_4,
         foodinsec_1, foodinsec_2, foodinsec_4,
         hhses_1, hhses_2, hhses_4,
         math_1,math_2, math_4)%>%
  pivot_longer(cols = c(-childid, -male, -hisp, -black, -asian,-nahn, -other, -wt, -strata, -psu), #time constant variables go here
               names_to  = c(".value", "wave"), #make wave variable and put t-v vars into columns
               names_sep = "_")%>% #all t-v variables have _ between name and time, like age_1, age_2
  filter(complete.cases(.))%>%
  arrange(childid, wave)


head(e.long.comp)

useing data.table

library(data.table)
library(magrittr)
out<-melt(setDT(eclsk.sub), id = "childid",
          measure.vars = list(ht=c("height_z1","height_z2","height_z4"),
                              age=c("age_yrs1", "age_yrs2", "age_yrs4"), 
                              math=c("math1", "math2", "math4"),
                              hhses=c("hhses1", "hhses2", "hhses4"),
                              health=c("chhealth1", "chhealth2", "chhealth4")))%>%
  setorder(childid)
  

head(out, n=20)

#merge back to other data
e.long<-eclsk.sub%>%
  select(childid, hisp, black,asian, nahn, other,male, unsafe, s1_id, pov, hhsize, urban, w4c4p_40, w4c4p_4str, w4c4p_4psu)%>%
  left_join(., out, "childid")


e.long$wave<-e.long$variable
head(e.long)


e.long.comp<-e.long%>%
  filter(complete.cases(.), w4c4p_40>0)

Visualization of longitudinal data

library(ggplot2)

first10<-unique(e.long.comp$childid)[1:10]

sub<-e.long.comp%>%
  filter(childid%in%first10)

ggplot(sub, aes(x=ageyrs, y=math))+
  geom_point()+
  geom_smooth(method='lm',formula=y~x)+
  facet_wrap(~childid,nrow = 3)+
  ggtitle(label = "Change in Math score across age", 
          subtitle = "First 10 children in ECLS-K 2011")
## Warning in qt((1 - level)/2, df): NaNs produced

## Warning in qt((1 - level)/2, df): NaNs produced
## Warning in max(ids, na.rm = TRUE): no non-missing arguments to max; returning -
## Inf

## Warning in max(ids, na.rm = TRUE): no non-missing arguments to max; returning -
## Inf

Modeling

Longitudinal Models using GEE’s

The GEE is used here

#basic linear model
fit.1<-glm(scale(math)~scale(ageyrs)+male+black+hisp+asian+nahn+other+hhses, data=e.long.comp, weights=wt/mean(wt))
summary(fit.1)
## Warning in summary.glm(fit.1): observations with zero weight not used for
## calculating dispersion
#Get residuals and put them in a data frame
e.long.comp$resid<- residuals(fit.1)

e.res<-e.long.comp%>%
  select(childid, wave,resid)%>%
  pivot_wider(id_cols=c(childid),
              names_from = wave,
               values_from=resid )

head(e.res)

Here is our actual correlation matrix in the residuals between waves:

cor(e.res[,-1], use="pairwise.complete")
##           1         2         4
## 1 1.0000000 0.8149430 0.7218168
## 2 0.8149430 1.0000000 0.7992329
## 4 0.7218168 0.7992329 1.0000000

This is certainly not independence, and looks more like an AR(1), because the correlation decreases as the difference between wave number increases.

Now we fit the GEE: ### Model with independent correlation Meaning ZERO correlation between waves

fit.1<-geeglm(scale(math)~scale(ageyrs)+male+black+hisp+asian+nahn+other+hhses,
              id=childid ,
              wave = wave,
              corstr ="independence",
              data=e.long.comp,
              weights=wt/mean(wt))
summary(fit.1)
## Warning in summary.glm(object): observations with zero weight not used for
## calculating dispersion
## 
## Call:
## geeglm(formula = scale(math) ~ scale(ageyrs) + male + black + 
##     hisp + asian + nahn + other + hhses, data = e.long.comp, 
##     weights = wt/mean(wt), id = childid, waves = wave, corstr = "independence")
## 
##  Coefficients:
##                Estimate   Std.err      Wald Pr(>|W|)    
## (Intercept)   -0.001055  0.012256     0.007    0.931    
## scale(ageyrs)  0.701513  0.005727 15006.485  < 2e-16 ***
## male           0.001107  0.014345     0.006    0.938    
## black         -0.231107  0.023467    96.986  < 2e-16 ***
## hisp          -0.116031  0.019612    35.003 3.29e-09 ***
## asian          0.196819  0.033163    35.223 2.94e-09 ***
## nahn          -0.099816  0.071867     1.929    0.165    
## other          0.009371  0.036362     0.066    0.797    
## hhses          0.278024  0.009075   938.672  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation structure = independence 
## Estimated Scale Parameters:
## 
##             Estimate  Std.err
## (Intercept)   0.4263 0.007448
## Number of clusters:   10179  Maximum cluster size: 3

Model with Exchangeable correlation

Meaning correlation between waves, but the correlation is the same for each pair waves

fit.2<-geeglm(scale(math)~scale(ageyrs)+male+black+hisp+asian+nahn+other+hhses ,
              id = childid,
              wave = wave,
              corstr ="exchangeable",
              data=e.long.comp, 
              weights=wt/mean(wt))
  
summary(fit.2)
## Warning in summary.glm(object): observations with zero weight not used for
## calculating dispersion
## 
## Call:
## geeglm(formula = scale(math) ~ scale(ageyrs) + male + black + 
##     hisp + asian + nahn + other + hhses, data = e.long.comp, 
##     weights = wt/mean(wt), id = childid, waves = wave, corstr = "exchangeable")
## 
##  Coefficients:
##                Estimate   Std.err     Wald Pr(>|W|)    
## (Intercept)   -0.000195  0.012921     0.00    0.988    
## scale(ageyrs)  0.847408  0.003044 77484.22  < 2e-16 ***
## male          -0.009904  0.015199     0.42    0.515    
## black         -0.243471  0.024648    97.57  < 2e-16 ***
## hisp          -0.128358  0.020268    40.11  2.4e-10 ***
## asian          0.229998  0.034954    43.30  4.7e-11 ***
## nahn          -0.140067  0.077768     3.24    0.072 .  
## other          0.005155  0.038890     0.02    0.895    
## hhses          0.234856  0.009501   611.02  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation structure = exchangeable 
## Estimated Scale Parameters:
## 
##             Estimate Std.err
## (Intercept)     0.45 0.00842
##   Link = identity 
## 
## Estimated Correlation Parameters:
##       Estimate Std.err
## alpha    0.814   0.021
## Number of clusters:   10179  Maximum cluster size: 3

The second model shows the exchangeable correlation to be 0.814, which is not very different from our measured correlations from above

1 2 4
1 1.000 0.815 0.722
2 0.815 1.000 0.799
4 0.722 0.799 1.000

Now we examine the AR1 correlation types:

fit.3<-geeglm(scale(math)~scale(ageyrs)+male+black+hisp+asian+nahn+other+hhses ,
              id = childid,
              wave = wave,
              corstr ="ar(1)",
              data=e.long.comp, 
              weights=wt/mean(wt))
## Warning in if (corstrv == -1) stop("invalid corstr."): the condition has length
## > 1 and only the first element will be used
## Warning in if (corstrv == 5) stop("need zcor matrix for userdefined corstr.")
## else zcor <- genZcor(clusz, : the condition has length > 1 and only the first
## element will be used
## Warning in if (corstrv == 1) return(matrix(0, 0, 0)): the condition has length >
## 1 and only the first element will be used
## Warning in if (corstrv == 6) alpha <- 1 else alpha <- rep(0, q): the condition
## has length > 1 and only the first element will be used
summary(fit.3)
## Warning in summary.glm(object): observations with zero weight not used for
## calculating dispersion
## 
## Call:
## geeglm(formula = scale(math) ~ scale(ageyrs) + male + black + 
##     hisp + asian + nahn + other + hhses, data = e.long.comp, 
##     weights = wt/mean(wt), id = childid, waves = wave, corstr = "ar(1)")
## 
##  Coefficients:
##                Estimate   Std.err     Wald Pr(>|W|)    
## (Intercept)   -0.000195  0.012921     0.00    0.988    
## scale(ageyrs)  0.847408  0.003044 77484.22  < 2e-16 ***
## male          -0.009904  0.015199     0.42    0.515    
## black         -0.243471  0.024648    97.57  < 2e-16 ***
## hisp          -0.128358  0.020268    40.11  2.4e-10 ***
## asian          0.229998  0.034954    43.30  4.7e-11 ***
## nahn          -0.140067  0.077768     3.24    0.072 .  
## other          0.005155  0.038890     0.02    0.895    
## hhses          0.234856  0.009501   611.02  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation structure = exchangeable ar1 unstructured userdefined fixed 
## Estimated Scale Parameters:
## 
##             Estimate Std.err
## (Intercept)     0.45 0.00842
## Warning in if (pmatch(x$corstr, "independence", 0) == 0) {: the condition has
## length > 1 and only the first element will be used
##   Link = identity 
## 
## Estimated Correlation Parameters:
##       Estimate Std.err
## alpha    0.814   0.021
## Number of clusters:   10179  Maximum cluster size: 3

The implied correlation in the AR(1) model is : 0.814

Since GEE’s aren’t fit via maximum likelihood, they aren’t comparable in terms of AIC or likelihood ratio tests. However, Pan, 2001 describe an information criterion using a Quasi-likelihood formulation. This can be used to compare models with alternative correlation structures, with the lowest QIC representing the best fitting model. Another criterion is the Correlation Information Criterion (Hin and Wang, 2008)[https://onlinelibrary.wiley.com/doi/abs/10.1002/sim.3489], which is proposed to be better for choosing among models with the same mean function, but different correlation structures, which is what we’re doing here.

library(MESS) #need to install
## Warning: package 'MESS' was built under R version 4.0.5
## 
## Attaching package: 'MESS'
## The following object is masked from 'package:MuMIn':
## 
##     QIC
## The following object is masked from 'package:geepack':
## 
##     QIC
QIC(fit.1)
##       QIC      QICu Quasi Lik       CIC    params      QICC 
##     11541     11499     -5740        30         9     11541
QIC(fit.2)
##       QIC      QICu Quasi Lik       CIC    params      QICC 
##   12029.0   12016.2   -5999.1      15.4       9.0   12029.0
QIC(fit.3)
##       QIC      QICu Quasi Lik       CIC    params      QICC 
##   12029.0   12016.2   -5999.1      15.4       9.0   12029.0

So, it looks like the AR(1) correlation structure is slightly better than the exchangeable structure, using the CIC but there is not much difference between models using this criteria.

Binary response longitudinal model

Here we use the GEE for a binomial outcome.

Here are what the data look like:

binomial_smooth <- function(...) {
  geom_smooth(method = "glm", method.args = list(family = "binomial"), ...)
}

e.long.comp$poorhealth<-Recode(e.long.comp$chhealth, recodes="2:3=1; else=0")

ggplot(e.long.comp, aes(x=ageyrs, y=poorhealth))+
  geom_point()+
  binomial_smooth()+
  ggtitle(label = "Change in Math score across age",
          subtitle = "First 10 children in ECLS-K 2011 - All children")
## `geom_smooth()` using formula 'y ~ x'

ids<-unique(e.long.comp$childid)[1:10]

e.long.comp%>%
  filter(childid %in% ids)%>%
  ggplot( aes(x=ageyrs, y=poorhealth))+
  geom_point()+ binomial_smooth()+
  facet_wrap(~childid,nrow=3)+
  ggtitle(label = "Change in Math score across age",
          subtitle = "First 10 children in ECLS-K 2011 - Invidivual Children")
## `geom_smooth()` using formula 'y ~ x'
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

btest<-glm(I(chhealth>2)~scale(ageyrs)+male+black+hisp+asian+nahn+other+hhses+factor(wave) , family=binomial, data=e.long.comp, weights=wt/mean(wt))
## Warning in eval(family$initialize): non-integer #successes in a binomial glm!
e.long.comp$residb<- residuals(btest)

e.res3<-e.long.comp%>%
  select(childid, wave,residb)%>%
  pivot_wider(id_cols=c(childid),
              names_from = wave,
               values_from=residb )

head(e.res3)
cor(e.res3[, -1], use = "pairwise")
##       1     2     4
## 1 1.000 0.397 0.297
## 2 0.397 1.000 0.333
## 4 0.297 0.333 1.000

These look like a constant correlation, or AR(1) perhaps because the correlation decreases between waves 1 and 4, but is pretty similar between 1 and 2.

Logistic GEE with independent correlation

fitb.1<-geeglm(poorhealth~scale(ageyrs)+male+black+hisp+asian+nahn+other+hhses,
               waves = wave,
               id=childid ,
               corstr ="independence",
               family=binomial,
               data=e.long.comp,
               weights=wt/mean(wt))
## Warning in eval(family$initialize): non-integer #successes in a binomial glm!
summary(fitb.1)
## 
## Call:
## geeglm(formula = poorhealth ~ scale(ageyrs) + male + black + 
##     hisp + asian + nahn + other + hhses, family = binomial, data = e.long.comp, 
##     weights = wt/mean(wt), id = childid, waves = wave, corstr = "independence")
## 
##  Coefficients:
##               Estimate Std.err   Wald Pr(>|W|)    
## (Intercept)    -0.6742  0.0347 378.20  < 2e-16 ***
## scale(ageyrs)   0.0110  0.0150   0.54  0.46441    
## male            0.1687  0.0389  18.81  1.4e-05 ***
## black           0.2414  0.0658  13.48  0.00024 ***
## hisp            0.2694  0.0508  28.11  1.1e-07 ***
## asian           0.5571  0.0770  52.29  4.8e-13 ***
## nahn            0.4698  0.1775   7.01  0.00812 ** 
## other           0.0697  0.0974   0.51  0.47424    
## hhses          -0.3198  0.0235 185.65  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation structure = independence 
## Estimated Scale Parameters:
## 
##             Estimate Std.err
## (Intercept)        1 0.00618
## Number of clusters:   10179  Maximum cluster size: 3

Logistic GEE with exchangeable correlations

fitb.2<-geeglm(poorhealth~scale(ageyrs)+male+black+hisp+asian+nahn+other+hhses,
               waves = wave,
               id=childid ,
               corstr ="exch",
               family=binomial,
               data=e.long.comp,
               weights=wt/mean(wt))
## Warning in eval(family$initialize): non-integer #successes in a binomial glm!
summary(fitb.2)
## 
## Call:
## geeglm(formula = poorhealth ~ scale(ageyrs) + male + black + 
##     hisp + asian + nahn + other + hhses, family = binomial, data = e.long.comp, 
##     weights = wt/mean(wt), id = childid, waves = wave, corstr = "exch")
## 
##  Coefficients:
##               Estimate  Std.err   Wald Pr(>|W|)    
## (Intercept)   -0.67362  0.03436 384.34  < 2e-16 ***
## scale(ageyrs)  0.00178  0.01374   0.02  0.89716    
## male           0.17266  0.03853  20.08  7.4e-06 ***
## black          0.23871  0.06511  13.44  0.00025 ***
## hisp           0.27063  0.05007  29.22  6.5e-08 ***
## asian          0.54350  0.07542  51.93  5.7e-13 ***
## nahn           0.48061  0.17362   7.66  0.00564 ** 
## other          0.05751  0.09592   0.36  0.54878    
## hhses         -0.29754  0.02308 166.14  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation structure = exchangeable 
## Estimated Scale Parameters:
## 
##             Estimate Std.err
## (Intercept)    0.997 0.00586
##   Link = identity 
## 
## Estimated Correlation Parameters:
##       Estimate Std.err
## alpha     0.35  0.0147
## Number of clusters:   10179  Maximum cluster size: 3

Logistic GEE with AR(1) correlation

fitb.3<-geeglm(poorhealth~scale(ageyrs)+male+black+hisp+asian+nahn+other+hhses,
               waves = wave,
               id=childid ,
               corstr ="ar(1)",
               family=binomial,
               data=e.long.comp, weights=wt/mean(wt))
## Warning in eval(family$initialize): non-integer #successes in a binomial glm!
## Warning in if (corstrv == -1) stop("invalid corstr."): the condition has length
## > 1 and only the first element will be used
## Warning in if (corstrv == 5) stop("need zcor matrix for userdefined corstr.")
## else zcor <- genZcor(clusz, : the condition has length > 1 and only the first
## element will be used
## Warning in if (corstrv == 1) return(matrix(0, 0, 0)): the condition has length >
## 1 and only the first element will be used
## Warning in if (corstrv == 6) alpha <- 1 else alpha <- rep(0, q): the condition
## has length > 1 and only the first element will be used
summary(fitb.3)
## 
## Call:
## geeglm(formula = poorhealth ~ scale(ageyrs) + male + black + 
##     hisp + asian + nahn + other + hhses, family = binomial, data = e.long.comp, 
##     weights = wt/mean(wt), id = childid, waves = wave, corstr = "ar(1)")
## 
##  Coefficients:
##               Estimate  Std.err   Wald Pr(>|W|)    
## (Intercept)   -0.67362  0.03436 384.34  < 2e-16 ***
## scale(ageyrs)  0.00178  0.01374   0.02  0.89716    
## male           0.17266  0.03853  20.08  7.4e-06 ***
## black          0.23871  0.06511  13.44  0.00025 ***
## hisp           0.27063  0.05007  29.22  6.5e-08 ***
## asian          0.54350  0.07542  51.93  5.7e-13 ***
## nahn           0.48061  0.17362   7.66  0.00564 ** 
## other          0.05751  0.09592   0.36  0.54878    
## hhses         -0.29754  0.02308 166.14  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation structure = exchangeable ar1 unstructured userdefined fixed 
## Estimated Scale Parameters:
## 
##             Estimate Std.err
## (Intercept)    0.997 0.00586
## Warning in if (pmatch(x$corstr, "independence", 0) == 0) {: the condition has
## length > 1 and only the first element will be used
##   Link = identity 
## 
## Estimated Correlation Parameters:
##       Estimate Std.err
## alpha     0.35  0.0147
## Number of clusters:   10179  Maximum cluster size: 3

Compare the three models:

QIC(fitb.1)
##       QIC      QICu Quasi Lik       CIC    params      QICC 
##   35075.1   35052.1  -17517.0      20.5       9.0   35075.1
QIC(fitb.2)
##       QIC      QICu Quasi Lik       CIC    params      QICC 
##   35064.3   35055.9  -17518.9      13.2       9.0   35064.3
QIC(fitb.3)
##       QIC      QICu Quasi Lik       CIC    params      QICC 
##   35064.3   35055.9  -17518.9      13.2       9.0   35064.3

In the binomial case, it looks like the exchangeable correlation structure and the AR(1) model are very similar.

LS0tDQp0aXRsZTogJ0RFTSA3MjgzOiBMb25naXR1ZGluYWwgTW9kZWxzIGZvciBDaGFuZ2UgdXNpbmcgR2VuZXJhbGl6ZWQgRXN0aW1hdGluZyBFcXVhdGlvbnMnDQphdXRob3I6ICJDb3JleSBTcGFya3MsIFBoRCINCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCLCAlWScpYCINCm91dHB1dDoNCiAgIGh0bWxfZG9jdW1lbnQ6DQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogICAgZmlnX2hlaWdodDogNw0KICAgIGZpZ193aWR0aDogNw0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQotLS0NCg0KSW4gdGhpcyBleGFtcGxlLCB3ZSB3aWxsIHVzZSBHZW5lcmFsaXplZCBFc3RpbWF0aW5nIEVxdWF0aW9ucyB0byBkbyBzb21lIGxvbmdpdHVkaW5hbCBtb2RlbGluZyBvZiBkYXRhIGZyb20gdGhlIFtFQ0xTLUsgMjAxMV0oaHR0cHM6Ly9uY2VzLmVkLmdvdi9lY2xzL2RhdGFwcm9kdWN0cy5hc3ApLiBTcGVjaWZpY2FsbHksIHdlIHdpbGwgbW9kZWwgY2hhbmdlcyBpbiBhIHN0dWRlbnQncyBzdGFuZGFyZGl6ZWQgbWF0aCBzY29yZSBhcyBhIGNvbnRpbnVvdXMgb3V0Y29tZSBhbmQgc2VsZiByYXRlZCBoZWFsdGggYXMgYSBiaW5vbWlhbCBvdXRjb21lLCBmcm9tIGZhbGwga2luZGVyZ2FydGVuIHRvIHNwcmluZywgMXN0IGdyYWRlLiANCg0KSW50cm9kdWN0aW9uIHRvIEdFRSdzDQo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQpVcCB1bnRpbCBub3csIHdlIGhhdmUgdXNlZCAoRylMTU0ncyB0byBhbmFseXplIGRhdGEgdGhhdCB3ZXJlICJjbHVzdGVyZWQiDQoNCi0gUGVyc29ucyB3aXRoaW4gbmVpZ2hib3Job29kcw0KLSBTdXJ2ZXkgZGF0YSBpbiBnZW5lcmFsIC0gc3RyYXRpZmllZCBzYW1wbGluZw0KDQpUaGUgbmV4dCB0b3BpYyB3aWxsIGludHJvZHVjZSBhIG1vZGVsaW5nIHN0cmF0ZWd5IHRoYXQgYWxsb3dzIHVzIHRvIGNvbnNpZGVyIGNsdXN0ZXJlZCBkYXRhLCBidXQgaW4gYSBkaWZmZXJlbnQgZmFzaGlvbg0KDQpHTE1NUydzDQo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQpHTE1NJ3MgYXJlIGNvbW1vbmx5IHJlZmVycmVkIHRvIGFzICpjb25kaXRpb25hbCogbW9kZWxzLCBiZWNhdXNlIHRoZSBtb2RlbCBjb2VmZmljaWVudHMgIiRcYmV0YSQncyIgYXJlIGNvbmRpdGlvbiBvbiB0aGUgcmFuZG9tIGVmZmVjdHMgaW4gdGhlIG1vZGVsLiANCg0KTGlrZXdpc2UsIHRoZSBtZWFuIGlmIGNvbmRpdGlvbmFsIG9uIHRoZSByYW5kb20gZWZmZWN0cy4gVGhpcyBpcyBhbm90aGVyIHdheSBvZiBzYXlpbmcgdGhhdCB0aGUgbWVhbiBmb3IgYSBnaXZlbiBjb3ZhcmlhdGUgcGF0dGVybiBpcyBjb25kaXRpb25hbCBvbiB0aGUgZ3JvdXAgdGhhdCB0aGUgcGFydGljdWxhciBwZXJzb24gaXMgaW4uIA0KDQokXG11X3tpan1eYyA9IEUoWV97aWp9IHwgdV9qKSA9IFhfe2lqfVxiZXRhICsgdV9qJA0KDQpHTE1NUydzIGFuZCBHRUUncw0KPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KSW4gY29udHJhc3QsICoqR2VuZXJhbHplZCBFc3RpbWF0aW5nIEVxdWF0aW9ucyoqIGFyZSByZWZlcnJlZCB0byBhcyAqbWFyZ2luYWwqIG1vZGVscyBiZWNhdXNlIHRoZXkgb25seSBlc3RpbWF0ZSB0aGUgb3ZlcmFsbCBtZWFuLg0KDQokXG11X3tpan0gPSBYX3tpan1cYmV0YSQNCg0KW0xlZSBhbmQgTmVsZGVyLCAyMDA0XShodHRwOi8vd3d3LnBlb3BsZS52Y3UuZWR1L35kYmFuZHlvcC9CSU9TNjI1L0xlZU5lbGRlcjIwMDQucGRmKSBwcm92aWRlIGEgdmVyeSBnb29kIGRlc2NyaXB0aW9uIG9mIGhvdyB0aGVzZSB0d28gbWV0aG9kcyBjb21wYXJlIHRvIG9uZSBhbm90aGVyDQoNCkdlbmVyYWxpemVkIEVzdGltYXRpbmcgRXF1YXRpb25zDQo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQotIFR5cGljYWxseSBmaXJzdCBhdHRyaWJ1dGVkIHRvIFtMaWFuZyBhbmQgWmVnZXIsIDE5ODZdKGh0dHA6Ly9iaW9tZXQub3hmb3Jkam91cm5hbHMub3JnL2NvbnRlbnQvNzMvMS8xMy5zaG9ydCkNCi0gR0VFJ3MgYXJlIHJlZ3Jlc3Npb24gbW9kZWxzDQotIEludGVyZXN0ZWQgaW4gbW9kZWxpbmcgdGhlICptZWFuIHJlc3BvbnNlKiwgd2hpbGUgdHJlYXRpbmcgY29ycmVsYXRpb24gd2l0aGluIHBlcnNvbi9jbHVzdGVyIGFzIGEgKm51aXNhbmNlKg0KLSAqKk5PVCoqIGJhc2VkIG9uIG1heGltdW0gbGlrZWxpaG9vZA0KLSBEb2VzIG5vdCBuZWVkIGEgZnVsbHkgc3BlY2lmaWVkIGpvaW50IGRpc3RyaWJ1dGlvbiwgb25seSB0aGUgbWFyZ2luYWwgZGlzdHJpYnV0aW9uICgqbWVhbiopIG9mIHRoZSBvdXRjb21lDQotIE1vZGVscyBjYW4gYmUgZm9yIGFueSBkaXN0cmlidXRpb24gZm9yIHRoZSBvdXRjb21lDQoNCkdFRSdzDQo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQotIEZvciBsb25naXR1ZGluYWwgZGF0YSwgd2UgYXNzdW1lIHdlIGhhdmUgJHlfe2lqfSQgYXMgb3VyIG91dGNvbWUgb24gcGVyc29uICppKiBhdCB0aW1lICpqKi4gVGhpcyBjb3VsZCBqdXN0IGFzIGVhc2lseSBiZSBwZXJzb25zIHdpdGhpbiBvdGhlciB0eXBlcyBvZiBjbHVzdGVycywgbGlrZSBjb3VudGllcyBvciBzYW1wbGluZyB1bml0cy4NCi0gQWxzbyBoYXZlICRYX3tpan0kLCB0aGUgbWF0cml4IG9mIHByZWRpY3RvcnMNCi0gU3BlY2lmeSB0aGUgbGluayBmdW5jdGlvbiBiZXR3ZWVuICR5X3tpan0kIGFuZCAkWF97aWp9JCBhcyBpbiBhIEdMTSwgdmlhIGEgKmxpbmsgZnVuY3Rpb24qDQotIEZvY3VzIGlzIG9uIHRoZSAqbGluZWFyIHByZWRpY3Rvciogb2YgdGhlIGxpbmsgZnVuY3Rpb24gLSB0aGUgbWVhbg0KLSAqKk5PVCBJTlRFUkVTVEVEKiogaW4gdmFyaWFuY2UgY29tcG9uZW50cyAqKk9OTFkqKiByZWdyZXNzaW9uIGNvZWZmaWNpZW50cw0KDQpHRUUncw0KPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KLSBDb3ZhcmlhbmNlIHN0cnVjdHVyZQ0KICAtIFdlIGFsc28gbWF5IHdpc2ggdG8gbW9kZWwgKmhvdyogb2JzZXJ2YXRpb25zIGFyZSByZWxhdGVkIHRvIG9uZSBhbm90aGVyIHZpYSBzb21lIHR5cGUgb2YgKmNvcnJlbGF0aW9uIHN0cnVjdHVyZSogYmV0d2VlbiB3YXZlcw0KICAtIFRoaXMgZGlyZWN0bHkgaW1wbGllcyB0aGF0IG9ic2VydmF0aW9ucyBhcmUgKk5PVCBJTkRFUEVOREVOVCosIGFuZCB0aGF0J3MgZmluZQ0KICAtIE9ic2VydmF0aW9ucyBiZXR3ZWVuIGNsdXN0ZXJzICoqYXJlKiogaW5kZXBlbmRlbnQNCiAgLSBFcnJvcnMgYXJlIGNvcnJlbGF0ZWQNCiAgLSBObyBhc3N1bXB0aW9uIG9mIGNvbW1vbiB2YXJpYW5jZSAoaG9tb3NrZWRzYXN0aWNpdHkpDQoNCg0KR0VFJ3MgLSBNb2RlbCBmb3JtDQo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQpBIGJhc2ljIGZvcm0gb2YgdGhlIG1vZGVsIHdvdWxkIGJlOg0KDQokWV97aWp9ID0gXGJldGFfMCArIFxzdW1fayBYX3tpamt9IFxiZXRhX2sgKyBDT1JSICsgZXJyb3IkDQoNCk9yZGluYXJ5IG1vZGVscyB3aWxsIHRlbmQgdG8gb3ZlciBlc3RpbWF0ZSB0aGUgc3RhbmRhcmQgZXJyb3JzIGZvciB0aGUgJFxiZXRhJCdzICBmb3IgdGltZSB2YXJ5aW5nIHByZWRpY3RvcnMgaW4gYSBtb2RlbCB3aXRoIHJlcGVhdGVkIG9ic2VydmF0aW9ucywgYmVjYXVzZSB0aGVzZSBtb2RlbHMgZG8gbm90IGFjY291bnQgZm9yIHRoZSBjb3JyZWxhdGlvbiB3aXRoaW4gY2x1c3RlcnMgXCBvYnNlcnZhdGlvbnMgb3ZlciB0aW1lLiANCg0KTGlrZXdpc2UsIHRoZSBzdGFuZGFyZCBlcnJvcnMgb2YgdGltZSBpbnZhcmlhbnQgcHJlZGljdG9ycyB3aWxsIGJlIHVuZGVyIGVzdGltYXRlZA0KDQoNCkdFRSdzIC0gTW9kZWwgZXN0aW1hdGlvbg0KPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KR2l2ZW4gdGhlIG1lYW4gZnVuY3Rpb24gZm9yIHRoZSBtb2RlbCBhbmQgYSBzcGVjaWZpZWQgY29ycmVsYXRpb24gZnVuY3Rpb24sIHRoZSBtb2RlbCBwYXJhbWV0ZXJzIG1heSBiZSBlc3RpbWF0ZWQgYnkgZmluZGluZyB0aGUgc29sdXRpb24gZm9yOg0KDQokJFUoXGJldGEpID0gXHN1bV9pIF5uICAgXGZyYWN7XGRlbHRhIFxtdV97aWp9fXsgXGRlbHRhIFxiZXRhX2t9IFZfaV57LTF9IChZX3tpan0gLSBcbXUoXGJldGEpKSQkDQoNCg0KV2hpY2ggZ2l2ZXMgZXN0aW1hdGVzIG9mIHRoZSAkXGJldGEkJ3MgZm9yIHRoZSBsaW5lYXIgbWVhbiBmdW5jdGlvbi4gDQoNCkdFRSdzIC0gTW9kZWwgZXN0aW1hdGlvbg0KPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KLSBGaXJzdCwgYSBuYWl2ZSBsaW5lYXIgcmVncmVzc2lvbiBhbmFseXNpcyBpcyBjYXJyaWVkIG91dCwgYXNzdW1pbmcgdGhlIG9ic2VydmF0aW9ucyB3aXRoaW4gc3ViamVjdHMgYXJlIGluZGVwZW5kZW50Lg0KLSBUaGVuLCByZXNpZHVhbHMgYXJlIGNhbGN1bGF0ZWQgZnJvbSB0aGUgbmFpdmUgbW9kZWwgKG9ic2VydmVkLXByZWRpY3RlZCkgYW5kIGEgd29ya2luZyBjb3JyZWxhdGlvbiBtYXRyaXggaXMgZXN0aW1hdGVkIGZyb20gdGhlc2UgcmVzaWR1YWxzLg0KLSBUaGVuIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBhcmUgcmVmaXQsIGNvcnJlY3RpbmcgZm9yIHRoZSBjb3JyZWxhdGlvbi4gKEl0ZXJhdGl2ZSBwcm9jZXNzKQ0KLSBUaGUgd2l0aGluLXN1YmplY3QgY29ycmVsYXRpb24gc3RydWN0dXJlIGlzIHRyZWF0ZWQgYXMgYSBudWlzYW5jZSB2YXJpYWJsZSAoaS5lLiBhcyBhIGNvdmFyaWF0ZSkgIA0KDQpHRUUncyAtIENvcnJlbGF0aW9uIFN0cnVjdHVyZQ0KPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KRm9yIHRocmVlIHRpbWUgcG9pbnRzIHBlciBwZXJzb24sIHRoZSBvcmRpbmFyeSByZWdyZXNzaW9uIG1vZGVsIGNvcnJlbGF0aW9uIGluIHJlc2lkdWFscyB3aXRoaW4gY2x1c3RlcnMvcGVyc29ucyBvdmVyIHRpbWUgY2FuIGJlIHRob3VnaHQgb2YgYXMgdGhlIG1hdHJpeDoNCg0KJCRcYmVnaW57Ym1hdHJpeH0NClxzaWdtYV4yICYgMCAmIDAgXFwgDQowICYgXHNpZ21hXjIgJjAgXFwgDQogMCAmIDAgJiBcc2lnbWFeMg0KXGVuZHtibWF0cml4fSQkDQoNCndoaWNoIGFzc3VtZWQgdGhlIHZhcmlhbmNlcyBhcmUgY29uc3RhbnQgYW5kIHRoZSByZXNpZHVhbHMgYXJlIGluZGVwZW5kZW50IG92ZXIgdGltZQ0KDQpHRUUncyAtIENvcnJlbGF0aW9uIFN0cnVjdHVyZQ0KPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KQnV0IGluIGEgR0VFLCB0aGUgbW9kZWwgaW5jbHVkZSB0aGUgYWN0dWFsIGNvcnJlbGF0aW9uIGJldHdlZW4gbWVhc3VyZW1lbnRzIG92ZXIgdGltZTogDQoNCiQkXGJlZ2lue2JtYXRyaXh9DQpcc2lnbWFfMSBeMiAmIGEgJiBjIFxcIA0KYSAmIFxzaWdtYV8yIF4yICZiIFxcIA0KIGIgJiBjICYgXHNpZ21hXzMgXjINClxlbmR7Ym1hdHJpeH0kJA0KDQpXaGljaCBhbGxvd3MgdGhlIHZhcmlhbmNlcyBvdmVyIHRpbWUgdG8gYmUgZGlmZmVyZW50LCBhcyB3ZWxsIGFzIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIHRpbWVzIHRvIGJlIHByZXNlbnQuDQoNCg0KR0VFJ3MgLSBDb3JyZWxhdGlvbiBTdHJ1Y3R1cmUNCj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCi0gU2V2ZXJhbCB0eXBlcyBvZiBjb3JyZWxhdGlvbi9jb3ZhcmlhbmNlIGFyZSBjb21tb25seSB1c2VkIGluIEdFRSdzDQotIFdoZW4gd2UgZml0IGEgR0VFLCB3ZSBoYXZlIHRvIGFzc3VtZSBhIGNlcnRhaW4gdHlwZSBvZiBjb3JyZWxhdGlvbiBmb3IgdGhlIHJlcGVhdGVkIG1lYXN1cmVzLiBUaGVzZSBhcmUgdHlwaWNhbGx5Og0KICAtIEluZGVwZW5kZW5jZSAtIHNhbWUgYXMgT0xTDQogIC0gRXhjaGFuZ2VhYmxlL2NvbXBvdW5kIHN5bW1ldHJ5IChzaW1wbGVzdCkNCiAgLSBBdXRvcmVncmVzc2l2ZQ0KICAtIFVuc3RydWN0dXJlZCAobW9zdCBjb21wbGljYXRlZCkNCiAgDQpHRUUncyAtIENvcnJlbGF0aW9uIFN0cnVjdHVyZSAtIEluZGVwZW5kZW50DQo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQokJFxiZWdpbntibWF0cml4fQ0KMSAmIDAgJiAwIFxcIA0KMCAmIDEgJjAgXFwgDQogMCAmIDAgJiAxDQpcZW5ke2JtYXRyaXh9JCQNCg0KDQpHRUUncyAtIENvcnJlbGF0aW9uIFN0cnVjdHVyZSAtIEV4Y2hhbmdlYWJsZQ0KPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KJCRcYmVnaW57Ym1hdHJpeH0NCjEgJiBccmhvICYgXHJobyBcXCANClxyaG8gJiAxICZccmhvIFxcIA0KIFxyaG8gJlxyaG8gJiAxDQpcZW5ke2JtYXRyaXh9JCQNCg0KDQpHRUUncyAtIENvcnJlbGF0aW9uIFN0cnVjdHVyZSAtIEFSKDEpDQo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQokJFxiZWdpbntibWF0cml4fQ0KMSAmIFxyaG8gJiBccmhvXjIgXFwgDQpccmhvICYgMSAmXHJob1xcIA0KIFxyaG9eMiAmIFxyaG8gJiAxDQpcZW5ke2JtYXRyaXh9JCQNCg0KR0VFJ3MgLSBDb3JyZWxhdGlvbiBTdHJ1Y3R1cmUgLSBVbnN0cnVjdHVyZWQNCj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCiQkXGJlZ2lue2JtYXRyaXh9DQoxICYgXHJob18xICYgXHJob18yIFxcIA0KXHJob18xICYgMSAmXHJob18zIFxcIA0KIFxyaG9fMiAmIFxyaG9fMyYgMQ0KXGVuZHtibWF0cml4fSQkDQoNCmBgYHtyfQ0KbGlicmFyeSAoY2FyKQ0KbGlicmFyeShnZWVwYWNrKQ0KbGlicmFyeShNdU1JbikgICNtYXkgbmVlZCB0byBpbnN0YWxsDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSgicmlvIikNCiNpbnN0YWxsX2Zvcm1hdHMoKQ0KYGBgDQoNCg0KIyMgRGF0YSBhbmQgcmVjb2Rlcw0KRmlyc3Qgd2UgbG9hZCBvdXIgZGF0YQ0KYGBge3J9DQplY2xza2s1IDwtIGltcG9ydChjaG9vc2UuZmlsZXMoKSkNCm5hbWVzKGVjbHNrazUpPC10b2xvd2VyKG5hbWVzKGVjbHNrazUpKQ0KI2dldCBvdXQgb25seSB0aGUgdmFyaWFibGVzIEknbSBnb2luZyB0byB1c2UgZm9yIHRoaXMgZXhhbXBsZQ0KDQoNCg0KI3N1YnNldCB0aGUgZGF0YQ0KDQplY2xzay5zdWI8LWVjbHNrazUlPiUNCiAgc2VsZWN0KGNoaWxkaWQsIHhfY2hzZXhfciwgeDFsb2NhbGUsIHhfcmFjZXRoX3IsIHgycG92dHksIHgxMnBhcjFlZF9pLCBwMWN1cm1hciwgeDFodG90YWwsIHgxbXNjYWxrNSwgeDJtc2NhbGs1LCB4M21zY2FsazUsIHg0bXNjYWxrNSwgeDVtc2NhbGs1LCBwMWhzY2FsZSwgcDJoc2NhbGUsIHA0aHNjYWxlLCB4MmZzc3RhdDIsIHg0ZnNzdGF0MiwgeDRmc3N0YXQyLCB4MTJzZXNsLCB4NHNlc2xfaSwgcDJwYXJjdDEsIHAycGFyY3QyLCBzMV9pZCwgcDJzYWZlcGwsIHgya3JjZXRoLCBwMW8ybmVhciwgeF9kaXN0cG92LCB3MWMwLCB3MXAwLCB3MnAwLCB3MWMwc3RyLCB3MXAwc3RyLCB3NGM0cF80MCwgdzRjNHBfNHN0cix3NGM0cF80cHN1LCB3MWMwcHN1LCB3MXAwcHN1LCB4MWhlaWdodCwgeDJoZWlnaHQsIHg0aGVpZ2h0LCB4NGhlaWdodCwgeDVoZWlnaHQsIHgxa2FnZV9yLCB4MmthZ2VfciwgeDNhZ2UsIHg0YWdlLCB4NWFnZSkNCg0KI3JtKGVjbHNrMTEpOyBnYygpDQoNCmBgYA0KDQoNCiANCiMjIFRpbWUgY29uc3RhbnQgdmFyaWFibGVzDQoNCkZpcnN0LCBJIGRvIHNvbWUgcmVjb2Rpbmcgb2YgdmFyaWFibGVzLiBGaXJzdCwgd2UgY29kZSB0aW1lICoqaW52YXJpYW50KiogdmFyaWFibGVzLCBtZWFuaW5nIHRoZWlyIHZhbHVlcyBkbyBub3QgY2hhbmdlIGF0IGVhY2ggd2F2ZS4gDQoNCmBgYHtyfQ0KI05vbiB0aW1lIHZhcnlpbmcgdmFyaWFibGVzDQojRmlyc3Qgd2UgcmVjb2RlIHNvbWUgQ2hpbGQgY2hhcmFjdGVyaXN0aWNzDQojQ2hpbGQncyBzZXg6IHJlY29kZSBhcyBtYWxlID0xDQplY2xzay5zdWIkbWFsZTwtUmVjb2RlKGVjbHNrLnN1YiR4X2Noc2V4X3IsIHJlY29kZXM9IjE9MTsgMj0wOyAtOT1OQSIpDQoNCiNSZWNvZGUgcmFjZSB3aXRoIHdoaXRlLCBub24gSGlzcGFuaWMgYXMgcmVmZXJlbmNlIHVzaW5nIGR1bW15IHZhcnMNCmVjbHNrLnN1YiRoaXNwPC1SZWNvZGUgKGVjbHNrLnN1YiR4X3JhY2V0aF9yLCByZWNvZGVzPSIzOjQ9MTstOT1OQTsgZWxzZT0wIikNCmVjbHNrLnN1YiRibGFjazwtUmVjb2RlIChlY2xzay5zdWIkeF9yYWNldGhfciwgcmVjb2Rlcz0iMj0xOy05PU5BOyBlbHNlPTAiKQ0KZWNsc2suc3ViJGFzaWFuPC1SZWNvZGUgKGVjbHNrLnN1YiR4X3JhY2V0aF9yLCByZWNvZGVzPSI1PTE7LTk9TkE7IGVsc2U9MCIpDQplY2xzay5zdWIkbmFobjwtUmVjb2RlIChlY2xzay5zdWIkeF9yYWNldGhfciwgcmVjb2Rlcz0iNjo3PTE7LTk9TkE7IGVsc2U9MCIpDQplY2xzay5zdWIkb3RoZXI8LVJlY29kZSAoZWNsc2suc3ViJHhfcmFjZXRoX3IsIHJlY29kZXM9Ijg9MTstOT1OQTsgZWxzZT0wIikNCg0KDQojVGhlbiB3ZSByZWNvZGUgc29tZSBwYXJlbnQvbW90aGVyIGNoYXJhY3RlcmlzdGljcw0KI01vdGhlcidzIGVkdWNhdGlvbiwgcmVjb2RlIGFzIDIgZHVtbXlzIHdpdGggSFMgPSByZWZlcmVuY2UNCmVjbHNrLnN1YiRsdGhzPC1SZWNvZGUoZWNsc2suc3ViJHgxMnBhcjFlZF9pLCByZWNvZGVzID0gIjA6Mj0xOyAzOjg9MDsgZWxzZSA9IE5BIikNCmVjbHNrLnN1YiRndGhzPC1SZWNvZGUoZWNsc2suc3ViJHgxMnBhcjFlZF9pLCByZWNvZGVzID0gIjE6Mz0wOyA0Ojg9MTsgZWxzZSA9TkEiKSANCg0KI21hcml0YWwgc3RhdHVzLCByZWNvZGUgYXMgMiBkdW1teXMsIHJlZj0gbWFycmllZA0KZWNsc2suc3ViJHNpbmdsZTwtUmVjb2RlKGVjbHNrLnN1YiRwMWN1cm1hciwgcmVjb2Rlcz0iND0xOyAtNzotOT1OQTsgZWxzZT0wIikNCmVjbHNrLnN1YiRub3RtYXI8LVJlY29kZShlY2xzay5zdWIkcDFjdXJtYXIsIHJlY29kZXM9IjI6Mz0xOyAtNzotOT1OQTsgZWxzZT0wIikNCg0KDQojVGhlbiB3ZSBkbyBzb21lIGhvdXNlaG9sZCBsZXZlbCB2YXJpYWJsZXMNCg0KI1VyYmFuIHNjaG9vbCBsb2NhdGlvbiA9IDENCmVjbHNrLnN1YiR1cmJhbjwtUmVjb2RlKGVjbHNrLnN1YiR4MWxvY2FsZSwgcmVjb2RlcyA9ICIxOjM9MTsgND0wOyAtMTotOT1OQSIpDQoNCiNwb3ZlcnR5IGxldmVsIGluIHBvdmVydHkgPSAxDQplY2xzay5zdWIkcG92PC1SZWNvZGUoZWNsc2suc3ViJHgycG92dHkgLCByZWNvZGVzID0iMToyPTE7IDM9MDsgLTk9TkEiKQ0KDQojSG91c2Vob2xkIHNpemUNCmVjbHNrLnN1YiRoaHNpemU8LWVjbHNrLnN1YiR4MWh0b3RhbA0KDQojc2Nob29sICUgbWlub3JpdHkgc3R1ZGVudCBib2R5DQplY2xzay5zdWIkbWlub3JzY2g8LWlmZWxzZShlY2xzay5zdWIkeDJrcmNldGggPDAsIE5BLCBlY2xzay5zdWIkeDJrcmNldGgvMTApDQoNCiNVbnNhZmUgbmVpZ2hib3Job29kDQplY2xzay5zdWIkdW5zYWZlPC1SZWNvZGUoZWNsc2suc3ViJHAyc2FmZXBsICwgcmVjb2RlcyA9ICIxOjI9J3Vuc2FmZSc7IDM9J3NhZmUnOyBlbHNlPU5BIixhcy5mYWN0b3IgPSBUKQ0KDQojc2Nob29sIGRpc3RyaWN0IHBvdmVydHkNCmVjbHNrLnN1YiRkaXN0X3BvdjwtaWZlbHNlKGVjbHNrLnN1YiR4X2Rpc3Rwb3Y9PS05LCBOQSwgc2NhbGUoZWNsc2suc3ViJHhfZGlzdHBvdikpDQpgYGANCg0KIyMgVGltZSB2YXJ5aW5nIHZhcmlhYmxlcw0KSSBoYXZlIHRvIG1ha2UgdGhlIHJlcGVhdGVkIG1lYXN1cmVzIG9mIGVhY2ggb2YgbXkgbG9uZ2l0dWRpbmFsIHZhcmlhYmxlcy4gVGhlc2UgYXJlIHJlZmVycmVkIHRvIGFzICoqdGltZSB2YXJ5aW5nKiogdmFyaWFibGVzLCBtZWFuaW5nIHRoZWlyIHZhbHVlcyBjaGFuZ2UgYXQgZWFjaCB3YXZlLg0KDQpgYGB7cn0NCiNMb25naXR1ZGluYWwgdmFyaWFibGVzDQojcmVjb2RlIG91ciBvdXRjb21lcywgdGhlICBmaXJzdCBpcyB0aGUgY2hpbGQncyBtYXRoIHN0YW5kYXJkaXplZCB0ZXN0IHNjb3JlICBpbiBLaW5kZXJnYXJ0ZW4NCmVjbHNrLnN1YiRtYXRoXzE8LWlmZWxzZShlY2xzay5zdWIkeDFtc2NhbGs1PDAsIE5BLCBlY2xzay5zdWIkeDFtc2NhbGs1KQ0KZWNsc2suc3ViJG1hdGhfMjwtaWZlbHNlKGVjbHNrLnN1YiR4Mm1zY2FsazU8MCwgTkEsIGVjbHNrLnN1YiR4Mm1zY2FsazUpDQojZWNsc2suc3ViJG1hdGgzPC1pZmVsc2UoZWNsc2suc3ViJHgzbXNjYWxrMTwwLCBOQSwgZWNsc2suc3ViJHgzbXNjYWxrMSkNCmVjbHNrLnN1YiRtYXRoXzQ8LWlmZWxzZShlY2xzay5zdWIkeDRtc2NhbGs1PDAsIE5BLCBlY2xzay5zdWIkeDRtc2NhbGs1KQ0KDQojU2Vjb25kIG91dGNvbWUgaXMgY2hpbGQncyBoZWlnaHQgZm9yIGFnZSwgY29udGludW91cyBvdXRjb21lDQplY2xzay5zdWIkaGVpZ2h0XzE8LWlmZWxzZShlY2xzay5zdWIkeDFoZWlnaHQ8PS03LCBOQSwgZWNsc2suc3ViJHgxaGVpZ2h0KQ0KZWNsc2suc3ViJGhlaWdodF8yPC1pZmVsc2UoZWNsc2suc3ViJHgyaGVpZ2h0PD0tNywgTkEsIGVjbHNrLnN1YiR4MmhlaWdodCkNCiNlY2xzay5zdWIkaGVpZ2h0MzwtaWZlbHNlKGVjbHNrLnN1YiR4M2hlaWdodDw9LTcsIE5BLCBlY2xzay5zdWIkeDNoZWlnaHQpDQplY2xzay5zdWIkaGVpZ2h0XzQ8LWlmZWxzZShlY2xzay5zdWIkeDRoZWlnaHQ8PS03LCBOQSwgZWNsc2suc3ViJHg0aGVpZ2h0KQ0KDQojQWdlIGF0IGVhY2ggd2F2ZQ0KZWNsc2suc3ViJGFnZXlyc18xPC1pZmVsc2UoZWNsc2suc3ViJHgxa2FnZV9yPDAsIE5BLCBlY2xzay5zdWIkeDFrYWdlX3IvMTIpDQplY2xzay5zdWIkYWdleXJzXzI8LWlmZWxzZShlY2xzay5zdWIkeDJrYWdlX3I8MCwgTkEsIGVjbHNrLnN1YiR4MmthZ2Vfci8xMikNCiNlY2xzay5zdWIkYWdlX3lyczM8LWlmZWxzZShlY2xzay5zdWIkeDNhZ2U8MCwgTkEsIGVjbHNrLnN1YiR4M2FnZS8xMikNCmVjbHNrLnN1YiRhZ2V5cnNfNDwtaWZlbHNlKGVjbHNrLnN1YiR4NGFnZTwwLCBOQSwgZWNsc2suc3ViJHg0YWdlLzEyKQ0KDQplY2xzay5zdWI8LSBlY2xzay5zdWJbaXMubmEoZWNsc2suc3ViJGFnZXlyc18xKT09RiwgXQ0KDQojSGVpZ2h0IGZvciBhZ2UgeiBzY29yZSBzdGFuZGFyZGl6ZWQgYnkgc2V4IGFuZCBhZ2UNCmVjbHNrLnN1YiRoZWlnaHR6XzE8LWF2ZShlY2xzay5zdWIkaGVpZ2h0XzEsIGFzLmZhY3RvcihwYXN0ZShyb3VuZChlY2xzay5zdWIkYWdleXJzXzEsIDEuNSksIGVjbHNrLnN1YiRtYWxlKSksIEZVTj1zY2FsZSkNCmVjbHNrLnN1YiRoZWlnaHR6XzI8LWF2ZShlY2xzay5zdWIkaGVpZ2h0XzIsIGFzLmZhY3RvcihwYXN0ZShyb3VuZChlY2xzay5zdWIkYWdleXJzXzIsIDEuNSksIGVjbHNrLnN1YiRtYWxlKSksIEZVTj1zY2FsZSkNCiNlY2xzay5zdWIkaGVpZ2h0X3ozPC1hdmUoZWNsc2suc3ViJGhlaWdodDMsIGFzLmZhY3RvcihwYXN0ZShyb3VuZChlY2xzay5zdWIkYWdlX3lyczMsIDEuNSksIGVjbHNrLnN1YiRtYWxlKSksIEZVTj1zY2FsZSkNCmVjbHNrLnN1YiRoZWlnaHR6XzQ8LWF2ZShlY2xzay5zdWIkaGVpZ2h0XzQsIGFzLmZhY3RvcihwYXN0ZShyb3VuZChlY2xzay5zdWIkYWdleXJzXzQsIDEuNSksIGVjbHNrLnN1YiRtYWxlKSksIEZVTj1zY2FsZSkNCg0KDQojSG91c2Vob2xkIGZvb2QgaW5zZWN1cml0eSwgZGljaG90b21vdXMgb3V0Y29tZQ0KI1RoaXMgb3V0Y29tZSBpcyBvbmx5IHByZXNlbnQgYXQgdHdvIHdhdmVzDQoNCmVjbHNrLnN1YiRmb29kaW5zZWNfMTwtUmVjb2RlKGVjbHNrLnN1YiR4MmZzc3RhdDIsIHJlY29kZXM9IjI6Mz0xOyAxPTA7IGVsc2U9TkEiKQ0KZWNsc2suc3ViJGZvb2RpbnNlY18yPC1SZWNvZGUoZWNsc2suc3ViJHgyZnNzdGF0MiwgcmVjb2Rlcz0iMjozPTE7IDE9MDsgZWxzZT1OQSIpDQplY2xzay5zdWIkZm9vZGluc2VjXzQ8LVJlY29kZShlY2xzay5zdWIkeDRmc3N0YXQyLCByZWNvZGVzPSIyOjM9MTsgMT0wOyBlbHNlPU5BIikNCg0KDQojQ2hpbGQgaGVhbHRoIGFzc2Vzc21lbnQgRXhjZWxsZW50IHRvIHBvb3IgLCBvcmRpbmFsIG91dGNvbWUNCmVjbHNrLnN1YiRjaGhlYWx0aF8xPC1pZmVsc2UoZWNsc2suc3ViJHAxaHNjYWxlPDAsIE5BLCBlY2xzay5zdWIkcDFoc2NhbGUpDQplY2xzay5zdWIkY2hoZWFsdGhfMjwtaWZlbHNlKGVjbHNrLnN1YiRwMmhzY2FsZTwwLCBOQSwgZWNsc2suc3ViJHAyaHNjYWxlKQ0KZWNsc2suc3ViJGNoaGVhbHRoXzQ8LWlmZWxzZShlY2xzay5zdWIkcDRoc2NhbGU8MCwgTkEsIGVjbHNrLnN1YiRwNGhzY2FsZSkNCg0KI1NFUw0KZWNsc2suc3ViJGhoc2VzXzE8LWlmZWxzZShlY2xzay5zdWIkeDEyc2VzbD09LTksIE5BLCBzY2FsZShlY2xzay5zdWIkeDEyc2VzbCkpDQplY2xzay5zdWIkaGhzZXNfMjwtaWZlbHNlKGVjbHNrLnN1YiR4MTJzZXNsPT0tOSwgTkEsIHNjYWxlKGVjbHNrLnN1YiR4MTJzZXNsKSkNCg0KZWNsc2suc3ViJGhoc2VzXzQ8LWlmZWxzZShlY2xzay5zdWIkeDRzZXNsX2k9PS05LCBOQSwgc2NhbGUoZWNsc2suc3ViJHg0c2VzbF9pKSkNCmBgYA0KDQojIyBSZXNoYXBpbmcgZGF0YSBpbnRvIGxvbmdpdHVkaW5hbCBmb3JtYXQNClRvIGFuYWx5emUgZGF0YSBsb25naXR1ZGluYWxseSwgd2UgbXVzdCByZXNoYXBlIHRoZSBkYXRhIGZyb20gaXRzIGN1cnJlbnQgIndpZGUiIGZvcm1hdCwgd2hlcmUgZWFjaCByZXBlYXRlZCBtZWFzdXJlIGlzIGEgY29sdW1uLCBpbnRvIHRoZSAibG9uZyIgZm9ybWF0LCB3aGVyZSB0aGVyZSBpcyBhIHNpbmdsZSBjb2x1bW4gZm9yIGEgcGFydGljdWxhciB2YXJpYWJsZSwgYW5kIHdlIGFjY291bnQgZm9yIHRoZSByZXBlYXRlZCBtZWFzdXJlbWVudHMgb2YgZWFjaCBwZXJzb24uIEluIHRoaXMgY2FzZSwgSSdtIGdvaW5nIHRvIHVzZSB0aHJlZSB3YXZlcyBvZiBkYXRhLCBzbyBlYWNoIGNoaWxkIGNhbiBjb250cmlidXRlIHVwIHRvIHRocmVlIGxpbmVzIHRvIHRoZSBkYXRhLg0KDQpUaGUgYHJlc2hhcGUoKWAgZnVuY3Rpb24gd2lsbCBkbyB0aGlzIGZvciB1cywgYnV0IGJlbG93IEkgdXNlIGEgdGlkeSBtZXRob2QsIHVzaW5nIGEgY29tYmluYXRpb24gb2YgdGhlIGBkYXRhLnRhYmxlYCBhbmQgYGRwbHlyYCBwYWNrYWdlcy4gSSBmaXJzdCBtYWtlIGEgbG9uZyBkYXRhIHNldCBvZiB0aGUgaGVpZ2h0LCBhZ2UsIG1hdGgsIGNoaWxkIGhlYWx0aCBhbmQgaG91c2Vob2xkIFNFUyBtZWFzdXJlcywgdGhlbiBJIGxlZnQgam9pbiBpdCB0byB0aGUgdGltZSBpbnZhcmlhbnQgdmFyaWFibGVzIGknbGwgdXNlIGluIG15IG1vZGVscyBiZWxvdy4gDQpgYGB7cn0NCmxpYnJhcnkodGlkeXIpDQplLmxvbmcuY29tcDwtZWNsc2suc3ViJT4lDQogIHJlbmFtZSh3dCA9IHc0YzRwXzQwLHN0cmF0YT0gdzRjNHBfNHN0ciwgcHN1ID0gdzRjNHBfNHBzdSklPiUNCiAgc2VsZWN0KGNoaWxkaWQsbWFsZSwgaGlzcCwgYmxhY2ssIGFzaWFuLCBuYWhuLCBvdGhlcix3dCwgc3RyYXRhLCBwc3UsICN0aW1lIGNvbnN0YW50DQogICAgICAgICBoZWlnaHRfMSwgaGVpZ2h0XzIsIGhlaWdodF80LCAjdC12YXJ5aW5nIHZhcmlhYmxlcw0KICAgICAgICAgYWdleXJzXzEsIGFnZXlyc18yLCBhZ2V5cnNfNCwNCiAgICAgICAgIGNoaGVhbHRoXzEsIGNoaGVhbHRoXzIsIGNoaGVhbHRoXzQsDQogICAgICAgICBmb29kaW5zZWNfMSwgZm9vZGluc2VjXzIsIGZvb2RpbnNlY180LA0KICAgICAgICAgaGhzZXNfMSwgaGhzZXNfMiwgaGhzZXNfNCwNCiAgICAgICAgIG1hdGhfMSxtYXRoXzIsIG1hdGhfNCklPiUNCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKC1jaGlsZGlkLCAtbWFsZSwgLWhpc3AsIC1ibGFjaywgLWFzaWFuLC1uYWhuLCAtb3RoZXIsIC13dCwgLXN0cmF0YSwgLXBzdSksICN0aW1lIGNvbnN0YW50IHZhcmlhYmxlcyBnbyBoZXJlDQogICAgICAgICAgICAgICBuYW1lc190byAgPSBjKCIudmFsdWUiLCAid2F2ZSIpLCAjbWFrZSB3YXZlIHZhcmlhYmxlIGFuZCBwdXQgdC12IHZhcnMgaW50byBjb2x1bW5zDQogICAgICAgICAgICAgICBuYW1lc19zZXAgPSAiXyIpJT4lICNhbGwgdC12IHZhcmlhYmxlcyBoYXZlIF8gYmV0d2VlbiBuYW1lIGFuZCB0aW1lLCBsaWtlIGFnZV8xLCBhZ2VfMg0KICBmaWx0ZXIoY29tcGxldGUuY2FzZXMoLikpJT4lDQogIGFycmFuZ2UoY2hpbGRpZCwgd2F2ZSkNCg0KDQpoZWFkKGUubG9uZy5jb21wKQ0KYGBgDQojIyMgdXNlaW5nIGRhdGEudGFibGUNCmBgYHtyLCBldmFsPUZBTFNFfQ0KDQpsaWJyYXJ5KGRhdGEudGFibGUpDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0Kb3V0PC1tZWx0KHNldERUKGVjbHNrLnN1YiksIGlkID0gImNoaWxkaWQiLA0KICAgICAgICAgIG1lYXN1cmUudmFycyA9IGxpc3QoaHQ9YygiaGVpZ2h0X3oxIiwiaGVpZ2h0X3oyIiwiaGVpZ2h0X3o0IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZ2U9YygiYWdlX3lyczEiLCAiYWdlX3lyczIiLCAiYWdlX3lyczQiKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXRoPWMoIm1hdGgxIiwgIm1hdGgyIiwgIm1hdGg0IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoaHNlcz1jKCJoaHNlczEiLCAiaGhzZXMyIiwgImhoc2VzNCIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhbHRoPWMoImNoaGVhbHRoMSIsICJjaGhlYWx0aDIiLCAiY2hoZWFsdGg0IikpKSU+JQ0KICBzZXRvcmRlcihjaGlsZGlkKQ0KICANCg0KaGVhZChvdXQsIG49MjApDQoNCiNtZXJnZSBiYWNrIHRvIG90aGVyIGRhdGENCmUubG9uZzwtZWNsc2suc3ViJT4lDQogIHNlbGVjdChjaGlsZGlkLCBoaXNwLCBibGFjayxhc2lhbiwgbmFobiwgb3RoZXIsbWFsZSwgdW5zYWZlLCBzMV9pZCwgcG92LCBoaHNpemUsIHVyYmFuLCB3NGM0cF80MCwgdzRjNHBfNHN0ciwgdzRjNHBfNHBzdSklPiUNCiAgbGVmdF9qb2luKC4sIG91dCwgImNoaWxkaWQiKQ0KDQoNCmUubG9uZyR3YXZlPC1lLmxvbmckdmFyaWFibGUNCmhlYWQoZS5sb25nKQ0KDQoNCmUubG9uZy5jb21wPC1lLmxvbmclPiUNCiAgZmlsdGVyKGNvbXBsZXRlLmNhc2VzKC4pLCB3NGM0cF80MD4wKQ0KDQoNCmBgYA0KDQoNCiMjIyBWaXN1YWxpemF0aW9uIG9mIGxvbmdpdHVkaW5hbCBkYXRhDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCg0KZmlyc3QxMDwtdW5pcXVlKGUubG9uZy5jb21wJGNoaWxkaWQpWzE6MTBdDQoNCnN1YjwtZS5sb25nLmNvbXAlPiUNCiAgZmlsdGVyKGNoaWxkaWQlaW4lZmlyc3QxMCkNCg0KZ2dwbG90KHN1YiwgYWVzKHg9YWdleXJzLCB5PW1hdGgpKSsNCiAgZ2VvbV9wb2ludCgpKw0KICBnZW9tX3Ntb290aChtZXRob2Q9J2xtJyxmb3JtdWxhPXl+eCkrDQogIGZhY2V0X3dyYXAofmNoaWxkaWQsbnJvdyA9IDMpKw0KICBnZ3RpdGxlKGxhYmVsID0gIkNoYW5nZSBpbiBNYXRoIHNjb3JlIGFjcm9zcyBhZ2UiLCANCiAgICAgICAgICBzdWJ0aXRsZSA9ICJGaXJzdCAxMCBjaGlsZHJlbiBpbiBFQ0xTLUsgMjAxMSIpDQoNCmBgYA0KDQojIyBNb2RlbGluZw0KDQojIyMgTG9uZ2l0dWRpbmFsIE1vZGVscyB1c2luZyBHRUUncw0KDQpUaGUgR0VFIGlzIHVzZWQgaGVyZSAgDQpgYGB7ciwgIHJlc3VsdHM9J2hpZGUnfQ0KDQojYmFzaWMgbGluZWFyIG1vZGVsDQpmaXQuMTwtZ2xtKHNjYWxlKG1hdGgpfnNjYWxlKGFnZXlycykrbWFsZStibGFjaytoaXNwK2FzaWFuK25haG4rb3RoZXIraGhzZXMsIGRhdGE9ZS5sb25nLmNvbXAsIHdlaWdodHM9d3QvbWVhbih3dCkpDQpzdW1tYXJ5KGZpdC4xKQ0KDQojR2V0IHJlc2lkdWFscyBhbmQgcHV0IHRoZW0gaW4gYSBkYXRhIGZyYW1lDQplLmxvbmcuY29tcCRyZXNpZDwtIHJlc2lkdWFscyhmaXQuMSkNCg0KZS5yZXM8LWUubG9uZy5jb21wJT4lDQogIHNlbGVjdChjaGlsZGlkLCB3YXZlLHJlc2lkKSU+JQ0KICBwaXZvdF93aWRlcihpZF9jb2xzPWMoY2hpbGRpZCksDQogICAgICAgICAgICAgIG5hbWVzX2Zyb20gPSB3YXZlLA0KICAgICAgICAgICAgICAgdmFsdWVzX2Zyb209cmVzaWQgKQ0KDQpoZWFkKGUucmVzKQ0KYGBgDQoNCkhlcmUgaXMgb3VyIGFjdHVhbCBjb3JyZWxhdGlvbiBtYXRyaXggaW4gdGhlIHJlc2lkdWFscyBiZXR3ZWVuIHdhdmVzOiANCmBgYHtyfQ0KY29yKGUucmVzWywtMV0sIHVzZT0icGFpcndpc2UuY29tcGxldGUiKQ0KYGBgDQoNClRoaXMgaXMgY2VydGFpbmx5IG5vdCBpbmRlcGVuZGVuY2UsIGFuZCBsb29rcyBtb3JlIGxpa2UgYW4gQVIoMSksIGJlY2F1c2UgdGhlIGNvcnJlbGF0aW9uIGRlY3JlYXNlcyBhcyB0aGUgKmRpZmZlcmVuY2UqIGJldHdlZW4gd2F2ZSBudW1iZXIgaW5jcmVhc2VzLg0KDQpOb3cgd2UgZml0IHRoZSBHRUU6DQojIyMgTW9kZWwgd2l0aCBpbmRlcGVuZGVudCBjb3JyZWxhdGlvbg0KTWVhbmluZyBaRVJPIGNvcnJlbGF0aW9uIGJldHdlZW4gd2F2ZXMNCmBgYHtyfQ0KDQpmaXQuMTwtZ2VlZ2xtKHNjYWxlKG1hdGgpfnNjYWxlKGFnZXlycykrbWFsZStibGFjaytoaXNwK2FzaWFuK25haG4rb3RoZXIraGhzZXMsDQogICAgICAgICAgICAgIGlkPWNoaWxkaWQgLA0KICAgICAgICAgICAgICB3YXZlID0gd2F2ZSwNCiAgICAgICAgICAgICAgY29yc3RyID0iaW5kZXBlbmRlbmNlIiwNCiAgICAgICAgICAgICAgZGF0YT1lLmxvbmcuY29tcCwNCiAgICAgICAgICAgICAgd2VpZ2h0cz13dC9tZWFuKHd0KSkNCnN1bW1hcnkoZml0LjEpDQpgYGANCg0KDQojIyMgTW9kZWwgd2l0aCBFeGNoYW5nZWFibGUgY29ycmVsYXRpb24NCk1lYW5pbmcgY29ycmVsYXRpb24gYmV0d2VlbiB3YXZlcywgYnV0IHRoZSBjb3JyZWxhdGlvbiBpcyB0aGUgc2FtZSBmb3IgZWFjaCBwYWlyIHdhdmVzIA0KYGBge3J9DQoNCmZpdC4yPC1nZWVnbG0oc2NhbGUobWF0aCl+c2NhbGUoYWdleXJzKSttYWxlK2JsYWNrK2hpc3ArYXNpYW4rbmFobitvdGhlcitoaHNlcyAsDQogICAgICAgICAgICAgIGlkID0gY2hpbGRpZCwNCiAgICAgICAgICAgICAgd2F2ZSA9IHdhdmUsDQogICAgICAgICAgICAgIGNvcnN0ciA9ImV4Y2hhbmdlYWJsZSIsDQogICAgICAgICAgICAgIGRhdGE9ZS5sb25nLmNvbXAsIA0KICAgICAgICAgICAgICB3ZWlnaHRzPXd0L21lYW4od3QpKQ0KICANCnN1bW1hcnkoZml0LjIpDQoNCmBgYA0KDQpUaGUgc2Vjb25kIG1vZGVsIHNob3dzIHRoZSBleGNoYW5nZWFibGUgY29ycmVsYXRpb24gdG8gYmUgYHIgZml0LjIkZ2Vlc2UkYWxwaGFgLCB3aGljaCBpcyBub3QgdmVyeSBkaWZmZXJlbnQgZnJvbSBvdXIgbWVhc3VyZWQgY29ycmVsYXRpb25zIGZyb20gYWJvdmUNCg0KYHIga25pdHI6OmthYmxlKGNvcihlLnJlc1ssLTFdLCB1c2U9InBhaXJ3aXNlLmNvbXBsZXRlIikpYA0KDQpOb3cgd2UgZXhhbWluZSB0aGUgQVIxIGNvcnJlbGF0aW9uIHR5cGVzOg0KYGBge3J9DQpmaXQuMzwtZ2VlZ2xtKHNjYWxlKG1hdGgpfnNjYWxlKGFnZXlycykrbWFsZStibGFjaytoaXNwK2FzaWFuK25haG4rb3RoZXIraGhzZXMgLA0KICAgICAgICAgICAgICBpZCA9IGNoaWxkaWQsDQogICAgICAgICAgICAgIHdhdmUgPSB3YXZlLA0KICAgICAgICAgICAgICBjb3JzdHIgPSJhcigxKSIsDQogICAgICAgICAgICAgIGRhdGE9ZS5sb25nLmNvbXAsIA0KICAgICAgICAgICAgICB3ZWlnaHRzPXd0L21lYW4od3QpKQ0KICANCnN1bW1hcnkoZml0LjMpDQoNCmBgYA0KDQpUaGUgaW1wbGllZCBjb3JyZWxhdGlvbiBpbiB0aGUgQVIoMSkgbW9kZWwgaXMgOiBgciBmaXQuMyRnZWVzZSRhbHBoYWANCg0KU2luY2UgR0VFJ3MgYXJlbid0IGZpdCB2aWEgbWF4aW11bSBsaWtlbGlob29kLCB0aGV5IGFyZW4ndCBjb21wYXJhYmxlIGluIHRlcm1zIG9mIEFJQyBvciBsaWtlbGlob29kIHJhdGlvIHRlc3RzLiBIb3dldmVyLCBbUGFuLCAyMDAxXShodHRwOi8vb25saW5lbGlicmFyeS53aWxleS5jb20vZG9pLzEwLjExMTEvai4wMDA2LTM0MVguMjAwMS4wMDEyMC54L2Fic3RyYWN0KSBkZXNjcmliZSBhbiBpbmZvcm1hdGlvbiBjcml0ZXJpb24gdXNpbmcgYSBRdWFzaS1saWtlbGlob29kIGZvcm11bGF0aW9uLiBUaGlzIGNhbiBiZSB1c2VkIHRvIGNvbXBhcmUgbW9kZWxzIHdpdGggYWx0ZXJuYXRpdmUgY29ycmVsYXRpb24gc3RydWN0dXJlcywgd2l0aCB0aGUgbG93ZXN0ICoqUUlDKiogcmVwcmVzZW50aW5nIHRoZSBiZXN0IGZpdHRpbmcgbW9kZWwuIEFub3RoZXIgY3JpdGVyaW9uIGlzIHRoZSAqKkNvcnJlbGF0aW9uIEluZm9ybWF0aW9uIENyaXRlcmlvbioqIChIaW4gYW5kIFdhbmcsIDIwMDgpW2h0dHBzOi8vb25saW5lbGlicmFyeS53aWxleS5jb20vZG9pL2Ficy8xMC4xMDAyL3NpbS4zNDg5XSwgd2hpY2ggaXMgcHJvcG9zZWQgdG8gYmUgYmV0dGVyIGZvciBjaG9vc2luZyBhbW9uZyBtb2RlbHMgd2l0aCB0aGUgc2FtZSBtZWFuIGZ1bmN0aW9uLCBidXQgZGlmZmVyZW50IGNvcnJlbGF0aW9uIHN0cnVjdHVyZXMsIHdoaWNoIGlzIHdoYXQgd2UncmUgZG9pbmcgaGVyZS4NCg0KYGBge3J9DQpsaWJyYXJ5KE1FU1MpICNuZWVkIHRvIGluc3RhbGwNClFJQyhmaXQuMSkNClFJQyhmaXQuMikNClFJQyhmaXQuMykNCmBgYA0KU28sIGl0IGxvb2tzIGxpa2UgdGhlIEFSKDEpIGNvcnJlbGF0aW9uIHN0cnVjdHVyZSBpcyBzbGlnaHRseSAgYmV0dGVyIHRoYW4gdGhlIGV4Y2hhbmdlYWJsZSBzdHJ1Y3R1cmUsIHVzaW5nIHRoZSBDSUMgYnV0IHRoZXJlIGlzIG5vdCBtdWNoIGRpZmZlcmVuY2UgYmV0d2VlbiBtb2RlbHMgdXNpbmcgdGhpcyBjcml0ZXJpYS4gDQoNCg0KIyMjIEJpbmFyeSByZXNwb25zZSBsb25naXR1ZGluYWwgbW9kZWwNCg0KSGVyZSB3ZSB1c2UgdGhlIEdFRSBmb3IgYSBiaW5vbWlhbCBvdXRjb21lLiANCg0KSGVyZSBhcmUgd2hhdCB0aGUgZGF0YSBsb29rIGxpa2U6DQpgYGB7cn0NCg0KYmlub21pYWxfc21vb3RoIDwtIGZ1bmN0aW9uKC4uLikgew0KICBnZW9tX3Ntb290aChtZXRob2QgPSAiZ2xtIiwgbWV0aG9kLmFyZ3MgPSBsaXN0KGZhbWlseSA9ICJiaW5vbWlhbCIpLCAuLi4pDQp9DQoNCmUubG9uZy5jb21wJHBvb3JoZWFsdGg8LVJlY29kZShlLmxvbmcuY29tcCRjaGhlYWx0aCwgcmVjb2Rlcz0iMjozPTE7IGVsc2U9MCIpDQoNCmdncGxvdChlLmxvbmcuY29tcCwgYWVzKHg9YWdleXJzLCB5PXBvb3JoZWFsdGgpKSsNCiAgZ2VvbV9wb2ludCgpKw0KICBiaW5vbWlhbF9zbW9vdGgoKSsNCiAgZ2d0aXRsZShsYWJlbCA9ICJDaGFuZ2UgaW4gTWF0aCBzY29yZSBhY3Jvc3MgYWdlIiwNCiAgICAgICAgICBzdWJ0aXRsZSA9ICJGaXJzdCAxMCBjaGlsZHJlbiBpbiBFQ0xTLUsgMjAxMSAtIEFsbCBjaGlsZHJlbiIpDQoNCmlkczwtdW5pcXVlKGUubG9uZy5jb21wJGNoaWxkaWQpWzE6MTBdDQoNCmUubG9uZy5jb21wJT4lDQogIGZpbHRlcihjaGlsZGlkICVpbiUgaWRzKSU+JQ0KICBnZ3Bsb3QoIGFlcyh4PWFnZXlycywgeT1wb29yaGVhbHRoKSkrDQogIGdlb21fcG9pbnQoKSsgYmlub21pYWxfc21vb3RoKCkrDQogIGZhY2V0X3dyYXAofmNoaWxkaWQsbnJvdz0zKSsNCiAgZ2d0aXRsZShsYWJlbCA9ICJDaGFuZ2UgaW4gTWF0aCBzY29yZSBhY3Jvc3MgYWdlIiwNCiAgICAgICAgICBzdWJ0aXRsZSA9ICJGaXJzdCAxMCBjaGlsZHJlbiBpbiBFQ0xTLUsgMjAxMSAtIEludmlkaXZ1YWwgQ2hpbGRyZW4iKQ0KDQpgYGANCg0KDQpgYGB7cn0NCmJ0ZXN0PC1nbG0oSShjaGhlYWx0aD4yKX5zY2FsZShhZ2V5cnMpK21hbGUrYmxhY2sraGlzcCthc2lhbituYWhuK290aGVyK2hoc2VzK2ZhY3Rvcih3YXZlKSAsIGZhbWlseT1iaW5vbWlhbCwgZGF0YT1lLmxvbmcuY29tcCwgd2VpZ2h0cz13dC9tZWFuKHd0KSkNCg0KZS5sb25nLmNvbXAkcmVzaWRiPC0gcmVzaWR1YWxzKGJ0ZXN0KQ0KDQplLnJlczM8LWUubG9uZy5jb21wJT4lDQogIHNlbGVjdChjaGlsZGlkLCB3YXZlLHJlc2lkYiklPiUNCiAgcGl2b3Rfd2lkZXIoaWRfY29scz1jKGNoaWxkaWQpLA0KICAgICAgICAgICAgICBuYW1lc19mcm9tID0gd2F2ZSwNCiAgICAgICAgICAgICAgIHZhbHVlc19mcm9tPXJlc2lkYiApDQoNCmhlYWQoZS5yZXMzKQ0KY29yKGUucmVzM1ssIC0xXSwgdXNlID0gInBhaXJ3aXNlIikNCmBgYA0KVGhlc2UgbG9vayBsaWtlIGEgY29uc3RhbnQgY29ycmVsYXRpb24sIG9yIEFSKDEpIHBlcmhhcHMgYmVjYXVzZSB0aGUgY29ycmVsYXRpb24gZGVjcmVhc2VzIGJldHdlZW4gd2F2ZXMgMSBhbmQgNCwgYnV0IGlzIHByZXR0eSBzaW1pbGFyIGJldHdlZW4gMSBhbmQgMi4NCg0KIyMjIExvZ2lzdGljIEdFRSB3aXRoIGluZGVwZW5kZW50IGNvcnJlbGF0aW9uDQpgYGB7cn0NCg0KZml0Yi4xPC1nZWVnbG0ocG9vcmhlYWx0aH5zY2FsZShhZ2V5cnMpK21hbGUrYmxhY2sraGlzcCthc2lhbituYWhuK290aGVyK2hoc2VzLA0KICAgICAgICAgICAgICAgd2F2ZXMgPSB3YXZlLA0KICAgICAgICAgICAgICAgaWQ9Y2hpbGRpZCAsDQogICAgICAgICAgICAgICBjb3JzdHIgPSJpbmRlcGVuZGVuY2UiLA0KICAgICAgICAgICAgICAgZmFtaWx5PWJpbm9taWFsLA0KICAgICAgICAgICAgICAgZGF0YT1lLmxvbmcuY29tcCwNCiAgICAgICAgICAgICAgIHdlaWdodHM9d3QvbWVhbih3dCkpDQpzdW1tYXJ5KGZpdGIuMSkNCg0KDQpgYGANCg0KIyMjIExvZ2lzdGljIEdFRSB3aXRoIGV4Y2hhbmdlYWJsZSBjb3JyZWxhdGlvbnMNCmBgYHtyfQ0KDQpmaXRiLjI8LWdlZWdsbShwb29yaGVhbHRofnNjYWxlKGFnZXlycykrbWFsZStibGFjaytoaXNwK2FzaWFuK25haG4rb3RoZXIraGhzZXMsDQogICAgICAgICAgICAgICB3YXZlcyA9IHdhdmUsDQogICAgICAgICAgICAgICBpZD1jaGlsZGlkICwNCiAgICAgICAgICAgICAgIGNvcnN0ciA9ImV4Y2giLA0KICAgICAgICAgICAgICAgZmFtaWx5PWJpbm9taWFsLA0KICAgICAgICAgICAgICAgZGF0YT1lLmxvbmcuY29tcCwNCiAgICAgICAgICAgICAgIHdlaWdodHM9d3QvbWVhbih3dCkpDQpzdW1tYXJ5KGZpdGIuMikNCg0KDQpgYGANCg0KIyMjIExvZ2lzdGljIEdFRSB3aXRoIEFSKDEpIGNvcnJlbGF0aW9uDQpgYGB7cn0NCmZpdGIuMzwtZ2VlZ2xtKHBvb3JoZWFsdGh+c2NhbGUoYWdleXJzKSttYWxlK2JsYWNrK2hpc3ArYXNpYW4rbmFobitvdGhlcitoaHNlcywNCiAgICAgICAgICAgICAgIHdhdmVzID0gd2F2ZSwNCiAgICAgICAgICAgICAgIGlkPWNoaWxkaWQgLA0KICAgICAgICAgICAgICAgY29yc3RyID0iYXIoMSkiLA0KICAgICAgICAgICAgICAgZmFtaWx5PWJpbm9taWFsLA0KICAgICAgICAgICAgICAgZGF0YT1lLmxvbmcuY29tcCwgd2VpZ2h0cz13dC9tZWFuKHd0KSkNCnN1bW1hcnkoZml0Yi4zKQ0KYGBgDQoNCkNvbXBhcmUgdGhlIHRocmVlIG1vZGVsczoNCmBgYHtyfQ0KUUlDKGZpdGIuMSkNClFJQyhmaXRiLjIpDQpRSUMoZml0Yi4zKQ0KYGBgDQoNCkluIHRoZSBiaW5vbWlhbCBjYXNlLCBpdCBsb29rcyBsaWtlIHRoZSBleGNoYW5nZWFibGUgY29ycmVsYXRpb24gc3RydWN0dXJlIGFuZCB0aGUgQVIoMSkgbW9kZWwgYXJlIHZlcnkgc2ltaWxhci4NCg0K