1 Setup

library(skimr)
library(knitr)
library(rbounds)
library(tableone)
library(magrittr); library(janitor) 
library(broom); library(survival); library(lme4)
library(cobalt); library(Matching); library(MatchIt)
library(naniar)
library(simputation)
library(mice)
library(Epi)
library(twang)
library(survey)
library(knitr)
library(rbounds)
library(patchwork)
library(tidyverse)

decim <- function(x, k) format(round(x, k), nsmall=k)

options(max.print="250")
opts_knit$set(width=75)

theme_set(theme_bw())

2 Data Load

sample <- read.csv("sample2.csv") 

3 Cleaning

3.1 Managing Binary Variables

meps_clean <- sample %>% 
  mutate(dupersid=as.character(dupersid)) %>% 
  rename(RA=max_ra, 
         pneumo=max_pneu,
         age=agelast) %>% 
  mutate(RA_f = fct_recode(factor(RA),
"Yes" = "1", "No" = "0")) %>% 
  mutate(pneumo_f = fct_recode(factor(pneumo),
"Yes" = "1", "No" = "0")) %>% 
  select(-ra, -pneu, -icd10cdx, -condn, -rxnum, -pid, -duid)

3.2 cleaning demographics: region, race, sex, education, insurance, income, marital status

meps_clean <- meps_clean %>% 
  mutate(region = fct_recode(factor(region),
"Northeast" = "1", "Midwest" = "2", "South" = "3", "West" = "4",
NULL = "-1")) %>% 
  mutate(panel = as.factor(panel)) %>% 
  mutate(region = fct_infreq(region)) %>% 
  mutate(race=fct_recode(factor(racethx),
         "Hispanic" = "1", "White" = "2", "Black" = "3", "Other" = "4", "Other" = "5")) %>% 
  mutate(race = fct_infreq(race)) %>% 
  mutate(sex = fct_recode(factor(sex),
"M" = "1", "F" = "2")) %>% 

mutate(education = 
           fct_recode(factor(hideg),
"no_degree" = "1", "GED" = "2", "HS" = "3", "BA"= "4", "graduate" = "5", "graduate"= "6", "Other"="7",  NULL = "-15", NULL =  "-8", NULL = "-7", NULL = "8")) %>% 
  
   mutate(public_ins = fct_recode(factor(inscov),
"No" = "1", "Yes" = "2", "No"= "3")) %>% 
  
    mutate(public_ins = fct_relevel(public_ins, "Yes")) %>% 
  
  mutate(income = fct_recode(factor(povcat),
"Poor" = "1", "Near_poor" = "2", "Low" = "3", "Middle"= "4", "High"="5")) %>% 
  
  mutate(marital = fct_recode(factor(marry42x),
 NULL = "-9", NULL= "-8", NULL = "-7", NULL = "-1", "married"= "1","married" = "7", "widowed"= "2", "widowed"= "8", "divorced" = "3", "divorced" = "9", "separated" = "10", "separated" = "4", "never_married" = "5", )) %>% 
  
  mutate(marital = fct_infreq(marital)) %>% 
  
      mutate(employment = fct_recode(factor(empst31),
"No" = "4", "Yes"= "3", "Yes"= "2", "Yes"="1",  NULL = "-15", NULL =  "-7", NULL = "-8", NULL = "-1", NULL= "-9")) 

3.3 cleaning medical: primary care

meps_clean <- meps_clean %>% 
  
  mutate(prim_care = fct_recode(factor(haveus42),
"Yes" = "1", "No" = "2", NULL = "-8", NULL = "-7",
NULL = "-1", NULL = "-9")) %>% 
    mutate(flu_vac = fct_recode(factor(flu_vac),
"Yes" = "1", "No" = "2", NULL = "-15", NULL = "-1", NULL = "-9")) %>% 
  mutate(asthma = fct_recode(factor(asthdx),
"Yes" = "1", "No" = "2", NULL = "-8", NULL = "-7", NULL = "-1", NULL = "-9")) %>%
  mutate(asthma = fct_relevel(asthma, "Yes")) %>% 
  mutate(diabetes = fct_recode(factor(diabetes),
"Yes" = "1", "No" = "2", NULL = "-8", NULL = "-7", NULL = "-1", NULL = "-15", NULL = "-9")) %>% 
  mutate(diabetes = fct_relevel(diabetes, "Yes")) %>%
  mutate(cancer = fct_recode(factor(cancerdx),
"Yes" = "1", "No" = "2", NULL = "-8", NULL = "-7", NULL = "-1", NULL = "-15", NULL = "-9")) %>% 
  mutate(cancer = fct_relevel(cancer, "Yes")) %>%
  mutate(bronchitis = fct_recode(factor(chbron31),
"Yes" = "1", "No" = "2", NULL = "-8", NULL = "-7", NULL = "-1", NULL = "-15", NULL = "-9")) %>% 
  mutate(bronchitis = fct_relevel(bronchitis, "Yes")) %>%
  mutate(emphysema = fct_recode(factor(emphdx),
"Yes" = "1", "No" = "2", NULL = "-8", NULL = "-7 ", NULL = "-1", NULL = "-15", NULL = "-9")) %>% 
  mutate(emphysema = fct_relevel(emphysema, "Yes")) %>%
  mutate(stroke = fct_recode(factor(strkdx),
"Yes" = "1", "No" = "2", NULL = "-8", NULL = "-7 ", NULL = "-1", NULL = "-15", NULL = "-9")) %>% 
  mutate(stroke = fct_relevel(stroke, "Yes"))

3.4 cleaning health status

meps_clean <- meps_clean %>% 
mutate(smoke=factor(smoke)) %>% 
mutate(perc_health = fct_recode(factor(rthlth31),
"Poor" = "5", "Fair" = "4", "Good"= "3", "VG_Exc"= "2", "VG_Exc"="1", NULL = "-1", NULL =  "-7", NULL = "-8")) %>% 
  
  mutate(mental = fct_recode(factor(mnhlth31),
"Poor" = "5", "Fair" = "4", "Good"= "3", "VG"= "2", "Excellent"="1",  NULL = "-1", NULL =  "-7", NULL = "-8"))  %>% 
  
  
  mutate(IADL = fct_recode(factor(iadlhp31),
 "Yes" = "1", "No"= "2", NULL = "-8", NULL =  "-7", NULL = "-1"))  %>% 
  
    mutate(ADL = fct_recode(factor(adlhlp31),
 "Yes" = "1", "No"= "2", NULL = "-8", NULL =  "-7", NULL = "-1"))  %>% 
  
  mutate(difficult_lift10 = fct_recode(factor(lftdif31),
 "no" = "1", "some"= "2", "a_lot" = "3", "unable"= "4", NULL = "-8", NULL =  "-7", NULL = "-1"))  %>% 
  
  
mutate(phys_lim = fct_recode(factor(wlklim31),
 NULL = "-8", NULL= "-7", NULL = "-1", "yes"= "1","no" = "2"))  %>% 
  
  mutate(cog_dif = fct_recode(factor(dfcog42),
 NULL = "-8", NULL= "-7", NULL = "-1", "yes"= "1","no" = "2")) 

3.5 cleaning just added variables

meps_clean <- meps_clean %>% 
  
  mutate(highbp = fct_recode(factor(hibpdx),
"Yes" = "1", "No" = "2", NULL = "-8", NULL = "-7", NULL = "-1", NULL = "-9")) %>%
  mutate(highbp = fct_relevel(highbp, "Yes")) %>% 
  
  mutate(chd = fct_recode(factor(chddx),
"Yes" = "1", "No" = "2", NULL = "-8", NULL = "-7", NULL = "-1", NULL = "-9")) %>%
  mutate(chd = fct_relevel(chd, "Yes")) %>% 
  
    mutate(angina = fct_recode(factor(angidx),
"Yes" = "1", "No" = "2", NULL = "-8", NULL = "-7", NULL = "-1", NULL = "-9")) %>%
  mutate(angina = fct_relevel(angina, "Yes")) %>%
  
        mutate(MI = fct_recode(factor(midx),
"Yes" = "1", "No" = "2", NULL = "-8", NULL = "-7", NULL = "-1", NULL = "-9")) %>%
  mutate(MI = fct_relevel(MI, "Yes")) %>% 
  
         mutate(other_heart = fct_recode(factor(ohrtdx),
"Yes" = "1", "No" = "2", NULL = "-8", NULL = "-7", NULL = "-1", NULL = "-9")) %>%
  mutate(other_heart = fct_relevel(other_heart, "Yes")) %>% 
  
  mutate(high_chol = fct_recode(factor(choldx),
"Yes" = "1", "No" = "2", NULL = "-8", NULL = "-7", NULL = "-1", NULL = "-9")) %>%
  mutate(high_chol = fct_relevel(high_chol, "Yes")) %>% 
  
  mutate(arthritis = fct_recode(factor(arthdx),
"Yes" = "1", "No" = "2", NULL = "-8", NULL = "-7", NULL = "-1", NULL = "-9")) %>%
  mutate(arthritis = fct_relevel(arthritis, "Yes")) 

3.6 selecting only variables I need

meps_clean <- meps_clean %>% 
  select(dupersid, RA, RA_f, pneumo, pneumo_f, totexp, panel, age, sex, race, marital, region, income, education,  public_ins, prim_care, flu_vac, asthma, diabetes, cancer, bronchitis, emphysema, stroke,  perc_health, mental, employment,  IADL, ADL, smoke, phys_lim, cog_dif, highbp, chd, angina, MI,  other_heart, high_chol, arthritis)

4 Code Book

4.1 Experimental design I used

The original MEPS data was collected prospectively over 2 years. For my study, there was no regard to time when the exposure,RA and the outcome, pneumonia were collected (i.e. which came before the other). The covariate information is from the beginning of the study period, so at the same time or before the information about medical conditions were obtained (3 in person interviews per year).

4.2 Where to find covariate information

Information was obtained from

https://meps.ahrq.gov/data_stats/download_data/pufs/h209/h209doc.pdf

paste(colnames(meps_clean), collapse = " | ")
[1] "dupersid | RA | RA_f | pneumo | pneumo_f | totexp | panel | age | sex | race | marital | region | income | education | public_ins | prim_care | flu_vac | asthma | diabetes | cancer | bronchitis | emphysema | stroke | perc_health | mental | employment | IADL | ADL | smoke | phys_lim | cog_dif | highbp | chd | angina | MI | other_heart | high_chol | arthritis"
Variable Type Description
dupersid character subject identifier
panel factor (3 levels) corresponds to the cohort subjects belong (21, 22, 23)
RA numeric whether or not subject has rheumatoid arthritis (1=yes, 0=no). This will be the exposure variable.
RA_f binary whether or not subject has rheumatoid arthritis (yes, no). This will be the exposure variable.
pneumo numeric whether or not subject has pneumonia (1=yes, 0=no). This will be the outcome variable.
pneumo_f binary whether or not subject has pneumonia (yes, no). This will be the outcome variable.
totexp numeric total medical expenditure for the year
age integer age in years
sex F/M F = Female, M = Male
race factor (4 levels) White, Hispanic, Black, Other
marital factor (5 levels) marital status (married, never_married, divorced, widowed, separated)
region factor (4 levels) Census Region (South, West, Midwest, Northeast)
income factor (5 levels) Family income as a % of poverty line: Poor, Near_poor, Low, Middle, High
education factor (6 levels) highest degree when first entered MEPS (no_degree, GED, HS, BA, graduate, Other)
public_ins binary Whether or not has public insurance (Yes, No)
prim_care binary Does a person have a usual care provider (Yes, No)
flu_vac binary Flu vaccination in past 12 months (Yes, No)
asthma binary Asthma diagnosis (Yes, No)
diabetes binary Diabetes diagnosis (Yes, No)
cancer binary Cancer diagnosis (Yes, No)
bronchitis binary Bronchitis diagnosis (Yes, No)
emphysema binary emphysema diagnosis (Yes, No)
stroke binary history of stroke (Yes, No)
perc_health factor (4 levels) Perceived health status (VG_Exc = collapsed very good/excellent, good, fair, poor)
mental factor (5 levels) Perceived mental health status (excellent, very good, good, fair, poor)
employment binary currently employed (Yes, No)
IADL binary require help with Instrumental Activities of Daily Living (Yes, NO)
ADL binary require help with Activities of Daily Living (Yes, NO)
smoke binary Currently (Yes, No)
phys_lim binary difficulty in performing certain specific physical actions such as walking, climbing stairs, grasping objects, reaching overhead, lifting, bending or stooping, or standing for long periods of time
cog_dif binary difficulty concentrating, remembering or making decisions (Yes,No)
highbp binary have you ever been told you have high blood pressure? (Yes, No)
chd binary have you ever been diagnosed with coronary heart disease? (Yes, No)
angina binary had ever been diagnosed as having angina, or angina pectoris (Yes, No)
MI binary had ever been diagnosed as having a heart attack, or myocardial infarction (Yes, No)
other_heart binary had ever been diagnosed with any other kind of heart disease or condition (Yes, No)
high_chol binary had ever been diagnosed as having high cholesterol (Yes, No)
arthritis binary had ever been diagnosed with arthritis (Yes, No) *this was removed b/c only 2 people in RA group had ‘no’

5 Table 1

vars <- c('age' , 'sex' , 'race' , 'marital' , 'region' , 'income' , 'education' , 'public_ins' , 'prim_care' , 'flu_vac' , 'asthma' , 'diabetes' , 'cancer' , 'bronchitis' , 'emphysema' , 'stroke' , 'perc_health' , 'mental' , 'employment' , 'IADL' , 'ADL' , 'smoke' , 'phys_lim' , 'cog_dif' , 'highbp' ,'chd' , 'angina' , 'MI' , 'other_heart' , 'high_chol')



factorvars <- c('sex' , 'race' , 'marital' , 'region' , 'income' , 'education' , 'public_ins' , 'prim_care' , 'flu_vac' , 'asthma' , 'diabetes' , 'cancer' , 'bronchitis' , 'emphysema' , 'stroke' , 'perc_health' , 'mental' , 'employment' , 'IADL' , 'ADL' , 'smoke' , 'phys_lim' , 'cog_dif' , 'highbp' ,'chd' , 'angina' , 'MI' , 'other_heart' , 'high_chol')

trt <- c("RA_f")

table01 <- CreateTableOne(data = meps_clean, 
                       vars = vars, 
                       factorVars= factorvars,
                       strata = trt)
print(table01, verbose=TRUE) 
                      Stratified by RA_f
                       No            Yes           p      test
  n                     1986           516                    
  age (mean (SD))      52.34 (17.13) 61.50 (13.19) <0.001     
  sex = F (%)           1118 (56.3)    383 (74.2)  <0.001     
  race (%)                                         <0.001     
     White              1150 (57.9)    291 (56.4)             
     Hispanic            366 (18.4)     94 (18.2)             
     Black               290 (14.6)    108 (20.9)             
     Other               180 ( 9.1)     23 ( 4.5)             
  marital (%)                                      <0.001     
     married            1061 (53.7)    256 (49.6)             
     never_married       428 (21.7)     51 ( 9.9)             
     divorced            267 (13.5)     97 (18.8)             
     widowed             158 ( 8.0)     82 (15.9)             
     separated            61 ( 3.1)     30 ( 5.8)             
  region (%)                                        0.006     
     South               763 (38.7)    241 (46.8)             
     West                485 (24.6)    108 (21.0)             
     Midwest             405 (20.6)    102 (19.8)             
     Northeast           317 (16.1)     64 (12.4)             
  income (%)                                       <0.001     
     Poor                320 (16.1)    130 (25.2)             
     Near_poor            80 ( 4.0)     32 ( 6.2)             
     Low                 309 (15.6)     83 (16.1)             
     Middle              542 (27.3)    127 (24.6)             
     High                735 (37.0)    144 (27.9)             
  education (%)                                    <0.001     
     no_degree           243 (12.3)    116 (22.7)             
     GED                  82 ( 4.2)     34 ( 6.7)             
     HS                  816 (41.4)    226 (44.2)             
     BA                  397 (20.1)     55 (10.8)             
     graduate            235 (11.9)     42 ( 8.2)             
     Other               200 (10.1)     38 ( 7.4)             
  public_ins = No (%)   1407 (70.8)    263 (51.0)  <0.001     
  prim_care = No (%)     374 (19.4)     24 ( 4.7)  <0.001     
  flu_vac = No (%)       456 (48.3)    100 (35.5)  <0.001     
  asthma = No (%)       1702 (85.9)    409 (79.3)  <0.001     
  diabetes = No (%)     1653 (83.4)    403 (78.1)   0.006     
  cancer = No (%)       1745 (88.1)    419 (81.2)  <0.001     
  bronchitis = No (%)   1902 (97.1)    473 (92.0)  <0.001     
  emphysema = No (%)    1926 (97.2)    460 (89.1)  <0.001     
  stroke = No (%)       1870 (94.4)    458 (88.8)  <0.001     
  perc_health (%)                                  <0.001     
     VG_Exc             1003 (51.2)     88 (17.1)             
     Good                575 (29.4)    187 (36.4)             
     Fair                290 (14.8)    157 (30.5)             
     Poor                 90 ( 4.6)     82 (16.0)             
  mental (%)                                       <0.001     
     Excellent           636 (32.5)    109 (21.2)             
     VG                  570 (29.1)    109 (21.2)             
     Good                547 (27.9)    173 (33.7)             
     Fair                161 ( 8.2)    100 (19.5)             
     Poor                 44 ( 2.2)     23 ( 4.5)             
  employment = No (%)    822 (42.0)    360 (70.2)  <0.001     
  IADL = No (%)         1858 (94.8)    426 (82.9)  <0.001     
  ADL = No (%)          1902 (97.0)    457 (88.9)  <0.001     
  smoke = yes (%)        285 (16.4)    113 (24.3)  <0.001     
  phys_lim = no (%)     1587 (81.1)    221 (43.0)  <0.001     
  cog_dif = no (%)      1802 (91.8)    425 (82.5)  <0.001     
  highbp = No (%)       1115 (56.3)    191 (37.0)  <0.001     
  chd = No (%)          1838 (92.9)    448 (87.0)  <0.001     
  angina = No (%)       1921 (97.0)    485 (94.0)   0.002     
  MI = No (%)           1876 (94.7)    465 (90.1)  <0.001     
 [ reached getOption("max.print") -- omitted 2 rows ]

6 missingness and imputation

  • Half of the sample is missing flu_vac , so I will probably drop this as a covariate of interest.

  • Other variables we will be imputing are smoke, prim_car, phys_lim, employment, bronchitis, cog_dif, mental, per_health, IADL, ADL, region, education, marital, cancer, chd, angina, asthma, diabetes, emphysema, stroke, highbp, MI, other_heart, and high_chol

miss_var_summary(meps_clean) 
# A tibble: 38 x 3
   variable    n_miss pct_miss
   <chr>        <int>    <dbl>
 1 flu_vac       1276    51.0 
 2 smoke          300    12.0 
 3 prim_care       70     2.80
 4 employment      31     1.24
 5 bronchitis      30     1.20
 6 perc_health     30     1.20
 7 mental          30     1.20
 8 phys_lim        30     1.20
 9 IADL            28     1.12
10 ADL             27     1.08
# … with 28 more rows

Here i am taking out flu vaccine

meps_no_flu <- meps_clean %>% select(-flu_vac)

I will let the mice package, do all of the imputation for me, but instead of multiple imputation I will just do one. And then I will pull out that one simply imputed data set.

set.seed(432432)
meps_mice <- mice(meps_no_flu, m = 1, printFlag = FALSE)
Warning: Number of logged events: 3

I will now store the 1st imputed data set inmeps1718

meps1718 <- complete(meps_mice, 1) %>% tibble()

dim(meps1718)
[1] 2502   37

And I do not have any more missing!

n_miss(meps1718)
[1] 0

7 Unadjusted Analysis

Ignoring covariates, I will estimate the effect of RA vs. no RA on the outcome, developing pneumonia.

Below is a 2 x 2 table which will give us the RR, OR, and risk difference.

Epi::twoby2(table(meps1718$RA_f, meps1718$pneumo_f)) %>% kable(dig=3)
2 by 2 table analysis: 
------------------------------------------------------ 
Outcome   : No 
Comparing : No vs. Yes 

      No Yes    P(No) 95% conf. interval
No  1962  24   0.9879    0.9820   0.9919
Yes  487  29   0.9438    0.9203   0.9607

                                   95% conf. interval
             Relative Risk: 1.0467    1.0244   1.0696
         Sample Odds Ratio: 4.8681    2.8090   8.4366
Conditional MLE Odds Ratio: 4.8639    2.7070   8.8141
    Probability difference: 0.0441    0.0263   0.0678

             Exact P-value: 0.0000 
        Asymptotic P-value: 0.0000 
------------------------------------------------------
No Yes P(No) 95% conf. interval
No 1962 24 0.988 0.982 0.992
Yes 487 29 0.944 0.920 0.961
95% conf. interval
Relative Risk: 1.047 1.024 1.070
Sample Odds Ratio: 4.868 2.809 8.437
Conditional MLE Odds Ratio: 4.864 2.707 8.814
Probability difference: 0.044 0.026 0.068
x
0
0

Below is another method to obtain the OR, through logistic regression.

unadjust_binary_outcome <- glm(pneumo ~ RA, data = meps1718, family = binomial())

unadjust_binary_outcome_tidy <- tidy(unadjust_binary_outcome, conf.int = TRUE, conf.level = 0.95, exponentiate = TRUE) %>%
    filter(term == "RA")

unadjust_binary_outcome_tidy %>% select(-p.value, -statistic) %>% kable(dig=3) 
term estimate std.error conf.low conf.high
RA 4.868 0.281 2.813 8.502

The odds of having pneumonia in RA individuals was 4.87 (95%CI: 2.81, 8.5) times higher than the odds that a non-RA control had pneumonia.

8 Fitting the propensity score model

I will now fit the propensity score, which predicts RA status based on these 30 available covariates:

panel, age , sex , race, marital, region , income , education , public_ins, prim_care , asthma , diabetes , cancer , bronchitis , emphysema , stroke , perc_health , mental , employment , IADL , ADL , smoke , phys_lim , cog_dif , highbp , chd , angina , MI , other_heart , high_chol

paste(colnames(meps1718), collapse = ", ")
[1] "dupersid, RA, RA_f, pneumo, pneumo_f, totexp, panel, age, sex, race, marital, region, income, education, public_ins, prim_care, asthma, diabetes, cancer, bronchitis, emphysema, stroke, perc_health, mental, employment, IADL, ADL, smoke, phys_lim, cog_dif, highbp, chd, angina, MI, other_heart, high_chol, arthritis"

We’ll use the f.build tool from the cobalt package here.

meps1718 <- meps1718 %>%
    mutate(treat = as.logical(RA_f == "Yes"))

covs_1 <- meps1718 %>%
    select(panel, age, sex, race, marital, region, income, education, public_ins, prim_care, asthma, diabetes, cancer, bronchitis, emphysema, stroke, perc_health, mental, employment, IADL, ADL, smoke, phys_lim, cog_dif, highbp, chd, angina, MI, other_heart, high_chol)

prop_model <- glm(f.build("treat", covs_1), data = meps1718,
                  family = binomial)

tidy(prop_model, conf.int = TRUE) %>%
    select(term, estimate, std.error, conf.low, conf.high, p.value) %>%
    knitr::kable(digits = 3)
term estimate std.error conf.low conf.high p.value
(Intercept) -3.054 0.711 -4.454 -1.664 0.000
panel22 0.003 0.140 -0.270 0.278 0.983
panel23 -0.144 0.162 -0.463 0.173 0.374
age 0.024 0.005 0.013 0.034 0.000
sexF 0.844 0.132 0.589 1.105 0.000
raceHispanic 0.097 0.176 -0.250 0.440 0.579
raceBlack 0.309 0.169 -0.024 0.639 0.067
raceOther -0.564 0.269 -1.112 -0.054 0.036
maritalnever_married -0.653 0.205 -1.063 -0.257 0.001
maritaldivorced -0.203 0.172 -0.544 0.131 0.238
maritalwidowed -0.388 0.194 -0.772 -0.010 0.046
maritalseparated 0.114 0.287 -0.459 0.670 0.692
regionWest -0.083 0.158 -0.394 0.225 0.598
regionMidwest -0.165 0.158 -0.476 0.142 0.295
regionNortheast -0.201 0.181 -0.561 0.150 0.267
incomeNear_poor 0.088 0.272 -0.453 0.617 0.746
incomeLow -0.155 0.196 -0.541 0.228 0.428
incomeMiddle 0.065 0.190 -0.307 0.439 0.732
incomeHigh 0.283 0.208 -0.124 0.693 0.174
educationGED 0.142 0.279 -0.411 0.684 0.609
educationHS -0.189 0.170 -0.521 0.145 0.265
educationBA -0.606 0.230 -1.060 -0.159 0.008
educationgraduate -0.335 0.254 -0.837 0.158 0.187
educationOther -0.563 0.254 -1.069 -0.071 0.027
public_insNo -0.076 0.143 -0.356 0.205 0.594
prim_careNo -1.058 0.235 -1.539 -0.614 0.000
asthmaNo -0.062 0.165 -0.382 0.264 0.705
diabetesNo 0.456 0.152 0.161 0.756 0.003
cancerNo -0.140 0.160 -0.451 0.176 0.382
bronchitisNo -0.078 0.252 -0.566 0.422 0.758
emphysemaNo -0.385 0.246 -0.869 0.098 0.118
strokeNo 0.199 0.206 -0.200 0.608 0.333
perc_healthGood 1.105 0.164 0.787 1.429 0.000
perc_healthFair 1.273 0.193 0.897 1.652 0.000
perc_healthPoor 1.672 0.259 1.166 2.182 0.000
mentalVG -0.193 0.168 -0.523 0.135 0.249
mentalGood -0.301 0.170 -0.634 0.031 0.075
mentalFair 0.034 0.223 -0.405 0.471 0.878
mentalPoor -0.398 0.351 -1.097 0.283 0.257
employmentNo 0.220 0.153 -0.080 0.519 0.151
IADLNo 0.019 0.234 -0.440 0.480 0.936
ADLNo -0.173 0.275 -0.711 0.368 0.530
smokeyes 0.438 0.153 0.136 0.737 0.004
phys_limno -0.919 0.140 -1.193 -0.645 0.000
cog_difno 0.186 0.188 -0.180 0.559 0.322
highbpNo -0.086 0.132 -0.345 0.173 0.516
chdNo 0.023 0.224 -0.414 0.467 0.919
anginaNo -0.046 0.293 -0.614 0.536 0.876
MINo -0.034 0.238 -0.496 0.438 0.887
other_heartNo 0.159 0.159 -0.151 0.474 0.318
high_cholNo 0.046 0.127 -0.203 0.296 0.719
glance(prop_model)
# A tibble: 1 x 8
  null.deviance df.null logLik   AIC   BIC deviance df.residual  nobs
          <dbl>   <int>  <dbl> <dbl> <dbl>    <dbl>       <int> <int>
1         2547.    2501  -989. 2079. 2376.    1977.        2451  2502

8.0.1 Storing the Propensity Scores

meps1718 <- meps1718 %>%
    mutate(ps = prop_model$fitted,
           linps = prop_model$linear.predictors)

ggplot(meps1718, aes(x = RA_f, y = linps)) +
    geom_violin() +
    geom_boxplot(width = 0.3)

The density plot below shows us that we have a substantial number of RA patients who do not have overlapping PS scores with non-RA patients.

ggplot(meps1718, aes(x = linps, fill = RA_f)) +
    geom_density(alpha = 0.3)

8.0.2 checking range of PS

The range of PS for the non-RA group is 0.0018 to 0.8409. The range for the RA group is 0.02 to 0.85.

Having a minimum this low is worrisome.

meps1718%$% 
  mosaic::favstats(ps ~ RA_f) %>% kable(dig=4)
Registered S3 method overwritten by 'mosaic':
  method                           from   
  fortify.SpatialPolygonsDataFrame ggplot2
RA_f min Q1 median Q3 max mean sd n missing
No 0.0018 0.0429 0.0958 0.2204 0.8409 0.1597 0.1645 1986 0
Yes 0.0221 0.2174 0.3862 0.5411 0.8521 0.3853 0.2004 516 0

Hmisc::describe shows us that there are many observations with a PS score of 0.002

Hmisc::describe(~ ps, data = meps1718)
ps 

 1  Variables      2502  Observations
--------------------------------------------------------------------------------
ps 
       n  missing distinct     Info     Mean      Gmd      .05      .10 
    2502        0     2501        1   0.2062   0.2078  0.01289  0.02254 
     .25      .50      .75      .90      .95 
 0.05296  0.13242  0.31044  0.51550  0.62385 

lowest : 0.001779336 0.001957033 0.002080588 0.002114288 0.002691743
highest: 0.832155102 0.839330830 0.840857492 0.851275383 0.852068436
--------------------------------------------------------------------------------

9 match_1 1:1 Greedy matching on the linear PS

The first type of match I will conduct is greedy 1:1 matching, without replacement. As we had only 1986 controls, we will not match all of the 516 RA patients.

  • I am using the Matching package

  • I am defining our treat (treatment) as occurring when RA_f is yes.

match_1 <- Match(Tr = meps1718$treat, X = meps1718$linps, 
                 M = 1, replace = FALSE, ties = FALSE,
                 estimand = "ATT")

summary(match_1)

Estimate...  0 
SE.........  0 
T-stat.....  NaN 
p.val......  NA 

Original number of observations..............  2502 
Original number of treated obs...............  516 
Matched number of observations...............  516 
Matched number of observations  (unweighted).  516 

9.1 Obtaining the Matched Sample for 1:1 Greedy Match

I am creating a new data frame, match1_matches, for upcoming analyses. This will contain only the matched subjects.

match1_matches <- factor(rep(match_1$index.treated, 2))
meps1718_matched1 <- cbind(match1_matches, 
                         meps1718[c(match_1$index.control, 
                                  match_1$index.treated),])

Some sanity checks:

meps1718_matched1 %>% count(RA_f)
  RA_f   n
1   No 516
2  Yes 516
meps1718_matched1 %>% head()
  match1_matches   dupersid RA RA_f pneumo pneumo_f totexp panel age sex  race
1           1987   94379104  0   No      0       No  21156    22  85   F Other
2           1988   91142101  0   No      0       No   2400    22  71   F White
3           1989 2293255101  0   No      0       No  19626    22  70   F White
4           1990   90530101  0   No      0       No   1151    22  65   F White
5           1991   95795102  0   No      0       No    409    22  33   F White
6           1992 2295001101  0   No      0       No   6238    22  66   M White
   marital  region    income education public_ins prim_care asthma diabetes
1  widowed   South Near_poor no_degree        Yes       Yes     No       No
2  married   South      High        HS        Yes       Yes     No       No
3 divorced Midwest Near_poor        HS        Yes       Yes     No       No
4 divorced Midwest      High        HS         No       Yes     No       No
5  married   South      High        BA         No       Yes     No       No
6  married   South    Middle        HS        Yes       Yes     No      Yes
  cancer bronchitis emphysema stroke perc_health    mental employment IADL ADL
1     No         No        No     No        Good      Good         No  Yes Yes
2     No         No        No     No        Fair      Fair        Yes   No  No
3    Yes        Yes       Yes     No      VG_Exc        VG         No  Yes  No
4     No         No        No     No        Good        VG        Yes   No  No
5     No         No        No     No      VG_Exc Excellent        Yes   No  No
6    Yes         No        No     No        Poor      Good         No   No  No
  smoke phys_lim cog_dif highbp chd angina MI other_heart high_chol arthritis
1    no      yes      no    Yes  No     No No          No        No        No
2    no       no      no     No  No     No No          No        No        No
3    no      yes      no     No  No     No No          No        No       Yes
4    no       no      no    Yes  No     No No          No        No        No
5    no       no      no     No  No     No No         Yes        No       Yes
6    no      yes      no    Yes  No     No No         Yes       Yes       Yes
  treat         ps       linps
1 FALSE 0.57371174  0.29701130
2 FALSE 0.51210454  0.04842762
3 FALSE 0.42143501 -0.31688530
4 FALSE 0.29988929 -0.84782511
5 FALSE 0.05658893 -2.81368871
6 FALSE 0.40948396 -0.36609908

Looks good. I built a new matched sample data frame with only the matched subjects

9.2 Checking Covariate Balance for our 1:1 Greedy Match

9.2.1 Using bal.tab to obtain a balance table

paste(colnames(meps1718), collapse = ", ")
[1] "dupersid, RA, RA_f, pneumo, pneumo_f, totexp, panel, age, sex, race, marital, region, income, education, public_ins, prim_care, asthma, diabetes, cancer, bronchitis, emphysema, stroke, perc_health, mental, employment, IADL, ADL, smoke, phys_lim, cog_dif, highbp, chd, angina, MI, other_heart, high_chol, arthritis, treat, ps, linps"
covs_1plus <- meps1718 %>%
    select(panel, age, sex, race, marital, region, income, education, public_ins, prim_care, asthma, diabetes, cancer, bronchitis, emphysema, stroke, perc_health, mental, employment, IADL, ADL, smoke, phys_lim, cog_dif, highbp, chd, angina, MI, other_heart, high_chol, ps, linps)

bal1 <- bal.tab(match_1,
                treat = meps1718$RA,
                covs = covs_1plus, quick = FALSE,
                data = meps1718, stats = c("m", "v"),
                un = TRUE, disp.v.ratio = TRUE)
bal1
Balance Measures
                         Type Diff.Un V.Ratio.Un Diff.Adj V.Ratio.Adj
panel_21               Binary -0.0044          .  -0.0097           .
panel_22               Binary  0.0070          .   0.0136           .
panel_23               Binary -0.0026          .  -0.0039           .
age                   Contin.  0.6945     0.5928  -0.0993      0.8479
sex_F                  Binary  0.1793          .   0.0252           .
race_White             Binary -0.0151          .   0.0058           .
race_Hispanic          Binary -0.0021          .  -0.0058           .
race_Black             Binary  0.0633          .  -0.0019           .
race_Other             Binary -0.0461          .   0.0019           .
marital_married        Binary -0.0406          .  -0.0155           .
marital_never_married  Binary -0.1182          .   0.0213           .
marital_divorced       Binary  0.0535          .   0.0097           .
marital_widowed        Binary  0.0778          .  -0.0213           .
marital_separated      Binary  0.0274          .   0.0058           .
region_South           Binary  0.0798          .   0.0194           .
region_West            Binary -0.0359          .  -0.0136           .
region_Midwest         Binary -0.0073          .   0.0097           .
region_Northeast       Binary -0.0366          .  -0.0155           .
income_Poor            Binary  0.0908          .   0.0213           .
income_Near_poor       Binary  0.0217          .  -0.0116           .
income_Low             Binary  0.0053          .   0.0116           .
income_Middle          Binary -0.0268          .   0.0019           .
income_High            Binary -0.0910          .  -0.0233           .
education_no_degree    Binary  0.1034          .  -0.0019           .
education_GED          Binary  0.0265          .   0.0136           .
education_HS           Binary  0.0290          .   0.0039           .
education_BA           Binary -0.0953          .   0.0019           .
education_graduate     Binary -0.0360          .  -0.0174           .
education_Other        Binary -0.0276          .   0.0000           .
public_ins_No          Binary -0.1988          .  -0.0155           .
prim_care_No           Binary -0.1459          .   0.0058           .
asthma_No              Binary -0.0664          .  -0.0388           .
diabetes_No            Binary -0.0528          .  -0.0039           .
cancer_No              Binary -0.0686          .   0.0136           .
bronchitis_No          Binary -0.0503          .  -0.0039           .
emphysema_No           Binary -0.0808          .  -0.0252           .
stroke_No              Binary -0.0560          .   0.0097           .
perc_health_VG_Exc     Binary -0.3410          .  -0.0155           .
perc_health_Good       Binary  0.0668          .  -0.0271           .
perc_health_Fair       Binary  0.1597          .   0.0116           .
perc_health_Poor       Binary  0.1145          .   0.0310           .
mental_Excellent       Binary -0.1120          .   0.0078           .
mental_VG              Binary -0.0789          .  -0.0155           .
mental_Good            Binary  0.0563          .  -0.0213           .
mental_Fair            Binary  0.1112          .   0.0329           .
mental_Poor            Binary  0.0233          .  -0.0039           .
employment_No          Binary  0.2792          .  -0.0136           .
IADL_No                Binary -0.1215          .  -0.0271           .
ADL_No                 Binary -0.0817          .  -0.0233           .
smoke_yes              Binary  0.0780          .   0.0291           .
 [ reached 'max' / getOption("max.print") -- omitted 10 rows ]

Sample sizes
          Control Treated
All          1986     516
Matched       516     516
Unmatched    1470       0

9.2.2 Checking Rubin’s Rules 1 and 2

We’ll build a little table of the Rubin’s Rules (1 and 2) before and after our match_1 is applied.

covs_for_rubin <- meps1718 %>%
    select(linps)

rubin_m1 <- bal.tab(match_1,
                treat = meps1718$treat,
                covs = covs_for_rubin,
                data = meps1718, stats = c("m", "v"),
                un = TRUE, disp.v.ratio = TRUE)[1]

rubin_report_m1 <- tibble(
    status = c("Rule1", "Rule2"),
    Unmatched = c(rubin_m1$Balance$Diff.Un,
                  rubin_m1$Balance$V.Ratio.Un),
    Matched = c(rubin_m1$Balance$Diff.Adj,
               rubin_m1$Balance$V.Ratio.Adj))

rubin_report_m1 %>% knitr::kable(digits = 2)
status Unmatched Matched
Rule1 1.55 0.10
Rule2 0.57 1.16

Explanation of Rubin’s Rules

Rule 1:

  • Evaluates the standardized differences of the linear PS expressed as proportions

    • We can multiply the results by 100 to describe them as percentages
  • Ideally, these results should be as close to 0 as possible, and definitely < 0.5 in absolute value (or 50%)

Rule 2

  • Evaluates the variance ratio of the linear propensity scores.

  • Ideally, these results should be within 0.8 to 1.25, but defiantly within 0.5 to 2

My Results

  • Rule 1: before matching we have a bias of 155.1308952%, and this is reduced to 9.5955115% after 1:1 greedy matching.

  • Rule 2: before matching we have a variance ratio of 56.6665221%, and this becomes 115.6520243% after 1:1 greedy matching.

  • We can see a considerable improvement, but I think we can do better

9.2.3 Using bal.plot from cobalt

Age was my only quantitative variable. And we will see in all of the analyses that this was one of the most difficult to balance between groups. Here we can see the distributional balance for age before and after matching.

bal.plot(match_1,
         treat = meps1718$RA,
         covs = covs_1plus,
         var.name = "age", 
         which = "both",
         sample.names = 
             c("Unmatched Sample", "Matched Sample"))

We can graphically compare the distribution of PS in both groups with mirrored histograms, before and after matching

bal.plot(match_1,
         treat = meps1718$RA,
         covs = covs_1plus,
         var.name = "ps", 
         which = "both",
         sample.names = 
             c("Unmatched Sample", "Matched Sample"),
         type = "histogram", mirror = TRUE)

After matching, the mirrored histogram looks the same! So cool!!

I am also evaluating some of the categorical variables that I am interested in based on previous some previous substantial imbalances

bal.plot(match_1,
         treat = meps1718$RA,
         covs = covs_1plus,
         var.name = "phys_lim", 
         which = "both",
         sample.names = 
             c("Unmatched Sample", "Matched Sample"))

bal.plot(match_1,
         treat = meps1718$RA,
         covs = covs_1plus,
         var.name = "perc_health", 
         which = "both",
         sample.names = 
             c("Unmatched Sample", "Matched Sample"))

It makes sense that perceived health and physical limitations would be so imbalanced before matching, given that people with RA have significantly reduced mobility. So that’s really awesome that we were able to correct this because these both are greatly associated with poor health outcomes.

9.2.4 Using love.plot to look at Standardized Differences

The love plots below show the standardized differences before and matching for each covariate and the linear PS.

Although only one is necessary as they both describe the same thing, I like that I have the option of two different ways of visualizing the same message. The first love plot provides the cut offs for -10% to 10%, which the second one shows the absolute difference.

love.plot(bal1, 
          threshold = .1, size = 3,
          var.order = "unadjusted",
          stats = "mean.diffs",
          stars = "raw",
          sample.names = c("Unmatched", "Matched"),
          title = "Love Plot for 1:1 Greedy Match") +
    labs(caption = "* indicates raw mean differences (for binary variables)")

love.plot(bal1, 
          threshold = .1, size = 3,
          var.order = "unadjusted",
          stats = "mean.diffs",
          stars = "raw",
          abs = TRUE,
          sample.names = c("Unmatched", "Matched"),
          title = "Absolute Differences for 1:1 Greedy Match") +
    labs(caption = "* indicates raw mean differences (for binary variables)")

The balance after matching is incredible. All of the covariates are within our target range. But as we saw before with Rubin’s Rule #1, that we just barely made it within the desired range for linear PS. I think we can do better. So I will try matching with a caliper next.

9.2.5 Using love.plot to look at Variance Ratios

Here we can see the variance ratios (Rubin Rule #2) before and after matching for the linear PS. we can also see it for my only quantitative variable, age (categorical variables are dropped). We were not within the desired range of 4/5 to 5/4 before and after matching for age (that was a tough variable), but we are for linps (Rubin #2)

love.plot(bal1, 
          threshold = 4/5, size = 3,
          stats = "variance.ratios",
          sample.names = c("Unmatched", "Matched"),
          title = "Variance Ratios for our 1:1 Match") 

10 Adjusted Analysis 1 (after 1:1 Greedy Matching)

adj.m1.pneumo <- clogit(pneumo ~ RA + strata(match1_matches), data=meps1718_matched1)
adj.m1_tidy <- tidy(adj.m1.pneumo, exponentiate = TRUE,
                        conf.int = TRUE)

adj.m1_tidy %>% kable(digits=2)
term estimate std.error statistic p.value conf.low conf.high
RA 2.64 0.35 2.74 0.01 1.32 5.28

The odds of having pneumonia in RA individuals was 2.64 (95%CI: 1.32, 5.28) times higher than the odds that a non-RA control had pneumonia.

11 match_4 Caliper Matching (1:1 without replacement) with the Matching package

  • I will use the Match function to specify a caliper of 0.2 (from Matching package)

    • Here, subjects will only be matched if they fall within the maximum distance of 0.2 standard deviations of the logit of the propensity score.

    • If they do not fall within this range, those subjects will be dropped.

  • The matching design will be 1:1 without replacement

match_4 <- Match(Tr = meps1718$treat, X = meps1718$linps, 
                 M = 1, replace = FALSE, ties = FALSE,
                 caliper = 0.2, estimand = "ATT")

summary(match_4)

Estimate...  0 
SE.........  0 
T-stat.....  NaN 
p.val......  NA 

Original number of observations..............  2502 
Original number of treated obs...............  516 
Matched number of observations...............  478 
Matched number of observations  (unweighted).  478 

Caliper (SDs)........................................   0.2 
Number of obs dropped by 'exact' or 'caliper'  38 

Note that we have now dropped 38 of the RA=yes subjects, and reduced our sample to the 478 remaining RA=yes subjects, who are paired with 478 unique matched RA = no subjects in our matched sample.

11.1 Obtaining the Matched Sample

Here I am storing the matched sample

match4_matches <- factor(rep(match_4$index.treated, 2))
meps1718_matched4 <- cbind(match4_matches, 
                         meps1718[c(match_4$index.control, 
                                  match_4$index.treated),])

How many unique subjects are in our matched sample?

meps1718_matched4 %$% n_distinct(dupersid)
[1] 956

This match includes 478 pairs so 956 subjects, since we’ve done matching without replacement.

meps1718_matched4 %>% count(RA_f)
  RA_f   n
1   No 478
2  Yes 478

11.2 Checking Covariate Balance for our 1:1 Caliper Match

11.2.1 Using bal.tab to obtain a balance table

covs_4plus <- meps1718 %>%
    select(panel, age, sex, race, marital, region, income, education, public_ins, prim_care, asthma, diabetes, cancer, bronchitis, emphysema, stroke, perc_health, mental, employment, IADL, ADL, smoke, phys_lim, cog_dif, highbp, chd, angina, MI, other_heart, high_chol, ps, linps)

bal4 <- bal.tab(match_4,
                treat = meps1718$RA_f,
                covs = covs_4plus, quick = FALSE,
                data = meps1718, stats = c("m", "v"),
                un = TRUE, disp.v.ratio = TRUE)
bal4
Balance Measures
                         Type Diff.Un V.Ratio.Un Diff.Adj V.Ratio.Adj
panel_21               Binary -0.0044          .   0.0126           .
panel_22               Binary  0.0070          .  -0.0335           .
panel_23               Binary -0.0026          .   0.0209           .
age                   Contin.  0.6945     0.5928  -0.1337      0.8203
sex_F                  Binary  0.1793          .   0.0063           .
race_White             Binary -0.0151          .  -0.0439           .
race_Hispanic          Binary -0.0021          .   0.0084           .
race_Black             Binary  0.0633          .   0.0293           .
race_Other             Binary -0.0461          .   0.0063           .
marital_married        Binary -0.0406          .  -0.0251           .
marital_never_married  Binary -0.1182          .   0.0377           .
marital_divorced       Binary  0.0535          .   0.0000           .
marital_widowed        Binary  0.0778          .  -0.0167           .
marital_separated      Binary  0.0274          .   0.0042           .
region_South           Binary  0.0798          .   0.0209           .
region_West            Binary -0.0359          .  -0.0063           .
region_Midwest         Binary -0.0073          .  -0.0084           .
region_Northeast       Binary -0.0366          .  -0.0063           .
income_Poor            Binary  0.0908          .   0.0230           .
income_Near_poor       Binary  0.0217          .  -0.0042           .
income_Low             Binary  0.0053          .  -0.0146           .
income_Middle          Binary -0.0268          .   0.0167           .
income_High            Binary -0.0910          .  -0.0209           .
education_no_degree    Binary  0.1034          .   0.0021           .
education_GED          Binary  0.0265          .   0.0063           .
education_HS           Binary  0.0290          .  -0.0021           .
education_BA           Binary -0.0953          .  -0.0209           .
education_graduate     Binary -0.0360          .   0.0063           .
education_Other        Binary -0.0276          .   0.0084           .
public_ins_No          Binary -0.1988          .  -0.0105           .
prim_care_No           Binary -0.1459          .   0.0188           .
asthma_No              Binary -0.0664          .  -0.0230           .
diabetes_No            Binary -0.0528          .  -0.0063           .
cancer_No              Binary -0.0686          .   0.0251           .
bronchitis_No          Binary -0.0503          .   0.0000           .
emphysema_No           Binary -0.0808          .  -0.0230           .
stroke_No              Binary -0.0560          .  -0.0021           .
perc_health_VG_Exc     Binary -0.3410          .  -0.0146           .
perc_health_Good       Binary  0.0668          .  -0.0021           .
perc_health_Fair       Binary  0.1597          .   0.0105           .
perc_health_Poor       Binary  0.1145          .   0.0063           .
mental_Excellent       Binary -0.1120          .  -0.0167           .
mental_VG              Binary -0.0789          .   0.0146           .
mental_Good            Binary  0.0563          .  -0.0084           .
mental_Fair            Binary  0.1112          .   0.0146           .
mental_Poor            Binary  0.0233          .  -0.0042           .
employment_No          Binary  0.2792          .  -0.0293           .
IADL_No                Binary -0.1215          .  -0.0042           .
ADL_No                 Binary -0.0817          .  -0.0063           .
smoke_yes              Binary  0.0780          .   0.0418           .
 [ reached 'max' / getOption("max.print") -- omitted 10 rows ]

Sample sizes
            No Yes
All       1986 516
Matched    478 478
Unmatched 1508   0
Discarded    0  38

11.2.2 Checking Rubin’s Rules 1 and 2

Below is a table of Rubin’s Rules 1 & 2 before and after our 1:1 caliper match is applied

covs_for_rubin <- meps1718 %>%
    select(linps)

rubin_m4 <- bal.tab(match_4,
                treat = meps1718$treat,
                covs = covs_for_rubin, 
                data = meps1718, stats = c("m", "v"),
                un = TRUE, disp.v.ratio = TRUE)[1]

rubin_report_m4 <- tibble(
    status = c("Rule1", "Rule2"),
    Unmatched = c(rubin_m4$Balance$Diff.Un,
                  rubin_m4$Balance$V.Ratio.Un),
    Matched = c(rubin_m4$Balance$Diff.Adj,
               rubin_m4$Balance$V.Ratio.Adj))

rubin_report_m4 %>% knitr::kable(digits = 3)
status Unmatched Matched
Rule1 1.551 0.004
Rule2 0.567 1.010
  • Rubin Rule 1: We want the standardized differences of the propensity scores of the two groups to be as close to zero as possible.
    • Before matching we have a bias of 155.1308952%, and this is reduced to 0.4257898% after 1:1 caliper matching.
  • Rubin Rule 2: we want the variances of the ratio of the lin PS to be within 4/5 to 5/4, but our ultimate goal is 1.
    • Before matching we have a variance ratio of 0.5666652, and this becomes 1.0099473 after 1:1 caliper matching.

In summary, we can see that caliper matching produced an exceptionally strong match. Rubin’s Rule 1 is 0 and Rule 2 is essentially 1. However, this cost us 38 patients in the RA group who were the “hardest to match”, which is quite sad.

11.2.3 Using bal.plot from cobalt

We can graphically compare the distribution of PS in both groups with mirrored histograms, before and after matching

bal.plot(match_4,
         treat = meps1718$RA_f,
         covs = covs_4plus,
         var.name = "ps", 
         which = "both",
         sample.names = 
             c("Unmatched Sample", "match_4 Sample"),
         type = "histogram", mirror = TRUE)

After matching, the mirrored histogram looks the same! So cool!!

11.2.4 Using love.plot to look at Standardized Differences

The love plot below helps us to look at the balance of each of the covariates.

We can see that we got all of the covariates in the desired range of -10% to +10%, except for age. However its a whole lot better than it was before.

love.plot(bal4, 
          threshold = .1, size = 3,
          var.order = "unadjusted",
          stats = "mean.diffs",
          stars = "raw",
          sample.names = c("Unmatched", "Matched"),
          title = "Love Plot for 1:1 Caliper Match (478 unique pairs)") +
    labs(caption = "* indicates raw mean differences (for binary variables)")

11.2.5 Using love.plot to look at Variance Ratios

We only had one continuous variable, age. We can see that the variance ratio of age. We can see that its even closer to 1 after matching.

Most importantly, we can visually see that we have met Rubin’s Rule #2 because the variance of the linear PS=1

love.plot(bal4, 
          threshold = .5, size = 3,
          stats = "variance.ratios",
          sample.names = c("Unmatched", "Matched"),
          title = "Variance Ratios for our 1:1 Caliper Match") 

12 Adjusted Analysis 2 (After Caliper 1:1 Matching)

From the matched sample, match4_matches, I will perform a conditional logistic regression to estimate the exposure effect in terms of an odds ratio (by exponentiating the log odds ratio). NOTE: I am using the 0/1 version of the outcome (pneumo) and exposure (RA) indicator.

I will be using the function, clogit, from the survival package.

adj.m.pneumo <- clogit(pneumo ~ RA + strata(match4_matches), data=meps1718_matched4)
adj.m_tidy <- tidy(adj.m.pneumo, exponentiate = TRUE,
                        conf.int = TRUE)

adj.m_tidy %>% kable(digits=2)
term estimate std.error statistic p.value conf.low conf.high
RA 1.85 0.34 1.78 0.08 0.94 3.63

The odds of having pneumonia in RA individuals was 1.85 (95%CI: 0.94, 3.63) times higher than the odds that a non-RA control had pneumonia.

13 ATT Weighting: weighting by the inverse PS

I will be weighting by the inverse of the propensity score using the ATT approach.

meps1718$wts1 <- ifelse(meps1718$RA==1, 1, meps1718$ps/(1-meps1718$ps))

Here is a plot of the resulting ATT (average treatment effect on the treated) weights:

ggplot(meps1718, aes(x = ps, y = wts1, color = RA_f)) +
    geom_point() + 
    guides(color = FALSE) +
    facet_wrap(~ RA_f) +
    labs(x = "Estimated Propensity for RA",
         y = "ATT weights for the MEPS data",
         title = "ATT weighting structure")

The exposed (RA) patients (right) all get a weight of 1, while the control patients, the higher their PS score is, the higher their weight. When controls have a very low PS for having RA, they have low weights.

13.1 Assess Balance After Weighting

I will use the twang package to assess balance after weighting. The twang package will calculate the effects on the weights.

The balanced table below shows me the RA mean (treated mean ) and control mean before and after weighting and a standardized difference, which will be very useful.

meps1718_df <- base::data.frame(meps1718) 

covlist <- c('panel', 'age', 'sex', 'race', 'marital', 'region', 'income', 'education', 'public_ins', 'prim_care', 'asthma', 'diabetes', 'cancer', 'bronchitis', 'emphysema', 'stroke', 'perc_health', 'mental', 'employment', 'IADL', 'ADL', 'smoke', 'phys_lim', 'cog_dif', 'highbp', 'chd', 'angina', 'MI', 'other_heart', 'high_chol', 'ps', 'linps')


bal.wts1 <- dx.wts(x=meps1718_df$wts1, data=meps1718_df, vars=covlist, treat.var= 'RA', estimand="ATT")
 

bal.table(bal.wts1)
$unw
                       tx.mn  tx.sd  ct.mn  ct.sd std.eff.sz   stat     p    ks
panel:21               0.252  0.434  0.256  0.437     -0.010  0.041 0.960 0.004
panel:22               0.502  0.500  0.495  0.500      0.014     NA    NA 0.007
panel:23               0.246  0.431  0.249  0.432     -0.006     NA    NA 0.003
age                   61.504 13.191 52.343 17.132      0.695 13.162 0.000 0.281
sex:M                  0.258  0.437  0.437  0.496     -0.410 54.843 0.000 0.179
sex:F                  0.742  0.437  0.563  0.496      0.410     NA    NA 0.179
race:White             0.564  0.496  0.579  0.494     -0.030  7.061 0.000 0.015
race:Hispanic          0.182  0.386  0.184  0.388     -0.005     NA    NA 0.002
race:Black             0.209  0.407  0.146  0.353      0.156     NA    NA 0.063
race:Other             0.045  0.206  0.091  0.287     -0.223     NA    NA 0.046
marital:married        0.496  0.500  0.537  0.499     -0.081 18.261 0.000 0.041
marital:never_married  0.099  0.298  0.217  0.412     -0.396     NA    NA 0.118
marital:divorced       0.188  0.391  0.134  0.341      0.137     NA    NA 0.054
marital:widowed        0.159  0.366  0.081  0.273      0.213     NA    NA 0.078
marital:separated      0.058  0.234  0.031  0.173      0.117     NA    NA 0.027
region:South           0.469  0.499  0.389  0.488      0.160  4.110 0.006 0.080
region:West            0.209  0.407  0.245  0.430     -0.088     NA    NA 0.036
region:Midwest         0.198  0.398  0.205  0.404     -0.018     NA    NA 0.007
region:Northeast       0.124  0.330  0.161  0.367     -0.111     NA    NA 0.037
income:Poor            0.252  0.434  0.161  0.368      0.209  8.480 0.000 0.091
income:Near_poor       0.062  0.241  0.040  0.197      0.090     NA    NA 0.022
income:Low             0.161  0.367  0.156  0.362      0.014     NA    NA 0.005
income:Middle          0.246  0.431  0.273  0.445     -0.062     NA    NA 0.027
income:High            0.279  0.449  0.370  0.483     -0.203     NA    NA 0.091
education:no_degree    0.227  0.419  0.123  0.329      0.247 13.128 0.000 0.103
education:GED          0.068  0.251  0.041  0.199      0.106     NA    NA 0.027
education:HS           0.442  0.497  0.413  0.492      0.058     NA    NA 0.029
                      ks.pval
panel:21                0.960
panel:22                0.960
panel:23                0.960
age                     0.000
sex:M                   0.000
sex:F                   0.000
race:White              0.000
race:Hispanic           0.000
race:Black              0.000
race:Other              0.000
marital:married         0.000
marital:never_married   0.000
marital:divorced        0.000
marital:widowed         0.000
marital:separated       0.000
region:South            0.006
region:West             0.006
region:Midwest          0.006
region:Northeast        0.006
income:Poor             0.000
income:Near_poor        0.000
income:Low              0.000
income:Middle           0.000
income:High             0.000
education:no_degree     0.000
education:GED           0.000
education:HS            0.000
 [ reached 'max' / getOption("max.print") -- omitted 54 rows ]

[[2]]
                       tx.mn  tx.sd  ct.mn  ct.sd std.eff.sz   stat     p    ks
panel:21               0.252  0.434  0.247  0.431      0.011  0.020 0.980 0.005
panel:22               0.502  0.500  0.503  0.500     -0.002     NA    NA 0.001
panel:23               0.246  0.431  0.250  0.433     -0.009     NA    NA 0.004
age                   61.504 13.191 62.825 14.427     -0.100 -1.619 0.106 0.090
sex:M                  0.258  0.437  0.250  0.433      0.017  0.091 0.763 0.008
sex:F                  0.742  0.437  0.750  0.433     -0.017     NA    NA 0.008
race:White             0.564  0.496  0.575  0.494     -0.022  0.246 0.862 0.011
race:Hispanic          0.182  0.386  0.163  0.369      0.050     NA    NA 0.019
race:Black             0.209  0.407  0.218  0.413     -0.022     NA    NA 0.009
race:Other             0.045  0.206  0.044  0.205      0.003     NA    NA 0.001
marital:married        0.496  0.500  0.481  0.500      0.031  0.312 0.864 0.016
marital:never_married  0.099  0.298  0.088  0.284      0.035     NA    NA 0.010
marital:divorced       0.188  0.391  0.192  0.394     -0.010     NA    NA 0.004
marital:widowed        0.159  0.366  0.183  0.387     -0.066     NA    NA 0.024
marital:separated      0.058  0.234  0.056  0.230      0.009     NA    NA 0.002
region:South           0.469  0.499  0.482  0.500     -0.026  0.107 0.955 0.013
region:West            0.209  0.407  0.212  0.409     -0.006     NA    NA 0.003
region:Midwest         0.198  0.398  0.187  0.390      0.027     NA    NA 0.011
region:Northeast       0.124  0.330  0.119  0.324      0.015     NA    NA 0.005
income:Poor            0.252  0.434  0.250  0.433      0.004  0.193 0.940 0.002
income:Near_poor       0.062  0.241  0.072  0.259     -0.042     NA    NA 0.010
income:Low             0.161  0.367  0.159  0.366      0.004     NA    NA 0.002
income:Middle          0.246  0.431  0.257  0.437     -0.025     NA    NA 0.011
income:High            0.279  0.449  0.261  0.439      0.039     NA    NA 0.018
education:no_degree    0.227  0.419  0.228  0.419     -0.003  0.125 0.984 0.001
education:GED          0.068  0.251  0.077  0.266     -0.035     NA    NA 0.009
education:HS           0.442  0.497  0.449  0.497     -0.015     NA    NA 0.007
                      ks.pval
panel:21                0.980
panel:22                0.980
panel:23                0.980
age                     0.030
sex:M                   0.763
sex:F                   0.763
race:White              0.862
race:Hispanic           0.862
race:Black              0.862
race:Other              0.862
marital:married         0.864
marital:never_married   0.864
marital:divorced        0.864
marital:widowed         0.864
marital:separated       0.864
region:South            0.955
region:West             0.955
region:Midwest          0.955
region:Northeast        0.955
income:Poor             0.940
income:Near_poor        0.940
income:Low              0.940
income:Middle           0.940
income:High             0.940
education:no_degree     0.984
education:GED           0.984
education:HS            0.984
 [ reached 'max' / getOption("max.print") -- omitted 54 rows ]

The std.eff.sz shows the standardized difference, but as a proportion, rather than as a percentage. Therefore, I will multiply them by 100 & plot the results.

bal.before.wts1 <- bal.table(bal.wts1)[1]
bal.after.wts1 <- bal.table(bal.wts1)[2]

balance.att.weights <- base::data.frame(names = rownames(bal.before.wts1$unw), 
                              pre.weighting = 100*bal.before.wts1$unw$std.eff.sz, 
                              ATT.weighted = 100*bal.after.wts1[[1]]$std.eff.sz)
balance.att.weights <- gather(balance.att.weights, timing, szd, 2:3)

Below is a plot of standardized differences before and after ATT weighting

ggplot(balance.att.weights, aes(x = szd, y = reorder(names, szd), color = timing)) +
    geom_point(size = 3) + 
    geom_vline(xintercept = 0) +
    geom_vline(xintercept = c(-10,10), linetype = "dashed", col = "blue") +
    labs(x = "Standardized Difference", y = "", 
         title = "Standardized Difference before and after ATT Weighting",
         subtitle = "N=2502") 

This plot indicates that the weighting is doing a nice job. The red dots are all within the -10% to +10% range.

13.2 Rubin’s Rules after ATT weighting

13.2.1 Rubin’s Rule 1

balance.att.weights %>% filter(names == "linps")
  names        timing   szd
1 linps pre.weighting 155.1
2 linps  ATT.weighted -11.5

The standardized difference before weighting was 155.1%. After weighting, the standardized difference was -11.5%. This is not within the -10% to 10% range, so we would not pass Rule 1 with flying colors.

13.2.2 Rubin’s Rule 2

meps1718_df <- base::data.frame(meps1718) 

covlist_ps <- c('linps')


bal.wts2 <- dx.wts(x=meps1718_df$wts1, data=meps1718_df, vars=covlist_ps, treat.var= 'RA', estimand="ATT")
 

bal.table(bal.wts2)
$unw
       tx.mn tx.sd ct.mn ct.sd std.eff.sz   stat p    ks ks.pval
linps -0.592 1.037  -2.2 1.377      1.551 29.195 0 0.517       0

[[2]]
       tx.mn tx.sd  ct.mn ct.sd std.eff.sz   stat     p   ks ks.pval
linps -0.592 1.037 -0.473 1.186     -0.115 -1.631 0.103 0.11   0.004

treated sd: (1.037)^2=1.075369 control sd: (1.186)^2=1.406596

1.186*1.186
[1] 1.406596
((1.037)^2)/((1.186)^2)
[1] 0.7645187

The standard deviations of the linear PS after weighting of 1.037 in the RA group and 1.186 in the non-RA group. 1.037^2 / 1.186 ^2 = 0.7645, which is just outside our desired range of 4/5 to 5/4, however it is within the 1/2 to 2. Arguably, we can pass Rule 2.

14 Twang package for weighting

# Recall that twang does not play well with tibbles,

ps.meps1718 <- ps(RA ~ panel + age + sex + race + marital + region + income + education + public_ins + prim_care + asthma + diabetes + cancer + bronchitis + emphysema + stroke + perc_health + mental + employment + IADL + ADL + smoke + phys_lim + cog_dif + highbp + chd + angina + MI + other_heart + high_chol,
             data = meps1718_df,
             n.trees = 3000,
             interaction.depth = 2,
             stop.method = c("es.mean"),
             estimand = "ATT",
             verbose = FALSE)

14.0.1 Assessing Balance with cobalt

bal.tab(ps.meps1718, full.stop.method = "es.mean.att")
Call
 ps(formula = RA ~ panel + age + sex + race + marital + region + 
    income + education + public_ins + prim_care + asthma + diabetes + 
    cancer + bronchitis + emphysema + stroke + perc_health + 
    mental + employment + IADL + ADL + smoke + phys_lim + cog_dif + 
    highbp + chd + angina + MI + other_heart + high_chol, data = meps1718_df, 
    n.trees = 3000, interaction.depth = 2, verbose = FALSE, estimand = "ATT", 
    stop.method = c("es.mean"))

Balance Measures
                          Type Diff.Adj
prop.score            Distance   0.2787
panel_21                Binary  -0.0040
panel_22                Binary   0.0056
panel_23                Binary  -0.0015
age                    Contin.   0.0084
sex_F                   Binary   0.0110
race_White              Binary  -0.0178
race_Hispanic           Binary   0.0072
race_Black              Binary   0.0147
race_Other              Binary  -0.0041
marital_married         Binary   0.0156
marital_never_married   Binary  -0.0008
marital_divorced        Binary   0.0002
marital_widowed         Binary  -0.0188
marital_separated       Binary   0.0039
region_South            Binary   0.0153
region_West             Binary  -0.0151
region_Midwest          Binary   0.0003
region_Northeast        Binary  -0.0005
income_Poor             Binary   0.0046
income_Near_poor        Binary  -0.0060
income_Low              Binary  -0.0111
income_Middle           Binary  -0.0029
income_High             Binary   0.0155
education_no_degree     Binary   0.0135
education_GED           Binary   0.0011
education_HS            Binary  -0.0058
education_BA            Binary  -0.0069
education_graduate      Binary   0.0001
education_Other         Binary  -0.0020
public_ins_No           Binary  -0.0194
prim_care_No            Binary  -0.0055
asthma_No               Binary  -0.0011
diabetes_No             Binary   0.0010
cancer_No               Binary   0.0011
bronchitis_No           Binary  -0.0008
emphysema_No            Binary  -0.0023
stroke_No               Binary   0.0139
perc_health_VG_Exc      Binary  -0.0198
perc_health_Good        Binary   0.0080
perc_health_Fair        Binary   0.0037
perc_health_Poor        Binary   0.0081
mental_Excellent        Binary   0.0195
mental_VG               Binary  -0.0071
mental_Good             Binary  -0.0134
mental_Fair             Binary   0.0084
mental_Poor             Binary  -0.0074
employment_No           Binary   0.0173
IADL_No                 Binary   0.0009
ADL_No                  Binary  -0.0065
smoke_yes               Binary   0.0142
phys_lim_no             Binary  -0.0101
cog_dif_no              Binary   0.0176
highbp_No               Binary  -0.0091
chd_No                  Binary  -0.0073
angina_No               Binary  -0.0067
MI_No                   Binary  -0.0157
other_heart_No          Binary   0.0020
high_chol_No            Binary   0.0124

Effective sample sizes
           Control Treated
Unadjusted 1986.       516
Adjusted    640.52     516

14.1 Semi-Automated Love plot of Standardized Differences

We can see that the propensity score has improved, but not all the way down to where we would like it to be. (Rubin 1)

p <- love.plot(bal.tab(ps.meps1718), 
               threshold = .1, size = 3, 
               title = "Standardized Diffs and TWANG ATT weighting")
Warning: Standardized mean differences and raw mean differences are present in the same plot. 
Use the 'stars' argument to distinguish between them and appropriately label the x-axis.
p + theme_bw()

14.2 plot of variance ratios

We can see below that we have a variance ratio of the lin PS in the range we would like it to be.

p <- love.plot(bal.tab(ps.meps1718), stat = "v",
               threshold = 1.25, size = 3, 
               title = "Variance Ratios: TWANG ATT weighting")
p + theme_bw()

15 Adjusted Analysis 3 (After Weighting)

15.1 Weighting only

15.1.1 With ATT weights

I am using logistic regression with the weighed sample to get the adjusted odds of the outcome, pneumonia.

meps1718wt1.design <- svydesign(ids=~1, weights=~wts1, data=meps1718) # using ATT weights

adjpneumo.wt1 <- svyglm(pneumo ~ RA, design=meps1718wt1.design, family=quasibinomial())

wt_att_results <- tidy(adjpneumo.wt1, conf.int = TRUE, exponentiate = TRUE) %>% 
    filter(term == "RA") %>% select(-statistic, - p.value) 

wt_att_results %>% kable(digits=3)
term estimate std.error conf.low conf.high
RA 2.601 0.371 1.256 5.386

The odds of having pneumonia in RA individuals was 2.6 (95%CI: 1.26, 5.39) times higher than the odds that a non-RA control had pneumonia.

15.1.2 with TWANG ATT weights

meps1718wt3.design <- svydesign(ids=~1, 
                           weights=~get.weights(ps.meps1718, 
                                                stop.method = "es.mean"),
                           data=meps1718) # using twang ATT weights

adjout2.wt3 <- svyglm(pneumo ~ RA, design=meps1718wt3.design,
                      family=quasibinomial())

wt_twangatt_results2 <- tidy(adjout2.wt3, conf.int = TRUE, exponentiate = TRUE) %>% 
    filter(term == "RA") %>% select(-statistic, - p.value) 

wt_twangatt_results2 %>% kable(dig=2)
term estimate std.error conf.low conf.high
RA 3 0.34 1.54 5.87

The odds of having pneumonia in RA individuals was 3 (95%CI: 1.54, 5.87) times higher than the odds that a non-RA control had pneumonia.

15.2 Double Robust

15.2.1 Using ATT weights

I am using the double robust approach to estimate the odds of pneumonia in RA vs non-RA patients.

The logistic regression approach below uses the svydesign and svyglm functions from the survey package. It is double robust because in addition to RA, it also adds linps

dr.pneumo.wt1 <- svyglm(pneumo ~ RA + linps, design=meps1718wt1.design,
                      family=quasibinomial())
dr_att_pneumo <- tidy(dr.pneumo.wt1, exponentiate = TRUE, conf.int = TRUE) %>% 
    filter(term == "RA")

dr_att_pneumo  %>% kable(digits=2)
term estimate std.error statistic p.value conf.low conf.high
RA 2.88 0.37 2.86 0 1.39 5.93

The odds of having pneumonia in RA individuals was 2.88 (95%CI: 1.39, 5.93) times higher than the odds that a non-RA control had pneumonia.

15.2.2 Using twang ATT weights

dr.out2.wt3 <- svyglm(pneumo ~ RA + linps, design=meps1718wt3.design,
                      family=quasibinomial())
dr_twangatt_out2 <- tidy(dr.out2.wt3, exponentiate = TRUE, conf.int = TRUE) %>% 
    filter(term == "RA")

dr_twangatt_out2
# A tibble: 1 x 7
  term  estimate std.error statistic p.value conf.low conf.high
  <chr>    <dbl>     <dbl>     <dbl>   <dbl>    <dbl>     <dbl>
1 RA        2.97     0.346      3.15 0.00167     1.51      5.84

16 Comparison of Results

Below are the OR and the associated 95% CI for each of the PS analyses

fullpo_tte <- tibble(
    analysis = c("Unadjusted", "1:1 Greedy Matching", "1:1 Matching with Caliper",
                 "ATT Weighting by Inverse PS", "Twang ATT Weighting", "Doubly Robust"),
    estimate = c(4.868, 2.64, 2.18, 2.60,3.00, 2.88),
    conf.low = c(2.813, 1.32, 1.07, 1.26, 1.54, 1.39),
    conf.high = c(8.502, 5.28, 4.45, 5.39, 5.87, 5.93))

ggplot(fullpo_tte, aes(x = analysis, y = estimate)) +
    geom_errorbar(aes(ymax = conf.high, ymin = conf.low), width = 0.5) + 
    geom_label(aes(label = estimate), size = 5) +
    theme_bw() + 
  theme(axis.text.x = element_text(angle = 45,  hjust=1)) +
  labs(title = "Compariso of Odds Ratios and 95% Confidence Intervals (N=2502)",
       subtitle = "1:1 Matching with a Caliper Preferred (Best Balance)",
       x = "")

Of all of these methods, I prefer the 1:1 matching with a caliber because it produced the best covariate balance.

17 Sensitivity Analysis

17.1 Obtaining the Matched Sample

Although I already had the matched sample from the caliper match meps1718_matched4 above, I created another one meps1718_matched4_s because I noticed that the code in the example included an arrange statement at the bottom and I’m not sure if we need that.

meps1718_matched4_s <- 
      cbind(match4_matches, 
      meps1718[c(match_4$index.control, 
      match_4$index.treated),]) %>% 
  arrange(match4_matches)

17.2 The Matched Sample

Below is the matched sample from the 1:1 caliper match without replacement

head(meps1718_matched4_s)
  match4_matches dupersid RA RA_f pneumo pneumo_f totexp panel age sex  race
1           1987 94379104  0   No      0       No  21156    22  85   F Other
2           1987 10014101  1  Yes      0       No   2000    21  71   F White
3           1988 91142101  0   No      0       No   2400    22  71   F White
4           1988 10404101  1  Yes      0       No   5295    21  77   F Black
5           1989 17587101  0   No      0       No   5363    21  78   F White
   marital    region    income education public_ins prim_care asthma diabetes
1  widowed     South Near_poor no_degree        Yes       Yes     No       No
2 divorced Northeast      High  graduate         No       Yes     No       No
3  married     South      High        HS        Yes       Yes     No       No
4  married     South      High        BA         No       Yes     No       No
5  married   Midwest    Middle        HS         No        No     No       No
  cancer bronchitis emphysema stroke perc_health mental employment IADL ADL
1     No         No        No     No        Good   Good         No  Yes Yes
2     No         No        No     No        Fair   Fair        Yes   No  No
3     No         No        No     No        Fair   Fair        Yes   No  No
4    Yes         No        No     No        Good     VG         No   No  No
5    Yes         No        No     No        Fair   Good         No  Yes Yes
  smoke phys_lim cog_dif highbp chd angina MI other_heart high_chol arthritis
1    no      yes      no    Yes  No     No No          No        No        No
2    no      yes      no     No  No     No No          No       Yes       Yes
3    no       no      no     No  No     No No          No        No        No
4    no       no      no    Yes  No     No No          No        No       Yes
5    no      yes     yes    Yes  No    Yes No          No       Yes       Yes
  treat        ps       linps      wts1
1 FALSE 0.5737117  0.29701130 1.3458305
2  TRUE 0.5725266  0.29216693 1.0000000
3 FALSE 0.5121045  0.04842762 1.0496194
4  TRUE 0.5135560  0.05423738 1.0000000
5 FALSE 0.4230939 -0.31008533 0.7333844
 [ reached 'max' / getOption("max.print") -- omitted 1 rows ]

17.3 Building a 2x2 table from the Matched Sample

This 2 x 2 table that I am making from the matched sample will be plugged into the spreadsheet to get an exact value of gamma

tmp <- meps1718_matched4_s %>% 
  mutate(res = 10*RA + pneumo_f) %>%
    group_by(match4_matches) %>%
    summarize(out.treated = pneumo_f[2], 
              out.control = pneumo_f[1]) 
Warning: Problem with `mutate()` input `res`.
ℹ '+' not meaningful for factors
ℹ Input `res` is `10 * RA + pneumo_f`.
Warning in Ops.factor(10 * RA, pneumo_f): '+' not meaningful for factors
`summarise()` ungrouping output (override with `.groups` argument)
tmp %>% tabyl(out.control, out.treated) %>% adorn_title()
             out.treated    
 out.control          No Yes
          No         441  24
         Yes          13   0

Gamma_results_from_Excel

The gamma obtained was 5.426. This gamma indicates a highly insensitive study (extremely far from 1)

Ways of interpreting:

  • The result was not sensitive to an unmeasured binary covariate which led to a 5.43 fold increase in the odds of exposure to RA and was a perfect predictor of pneumonia.

  • To explain the association in this particular study, one would need a hidden bias of 5.43

  • To attribute the observed significant outcome to an unobserved covariate rather than to RA, that observed covariate has to increase the odds of being in the RA group by a factor of 5.43 and also predict pneumonia quite well.

17.4 Estimating \(\Gamma\) with binarysens

The code below didn’t work, which is why I had to do it on the spreadsheet. The error stated: “Error in if (y.tmp1 >= y.tmp2) { : missing value where TRUE/FALSE needed”

# binarysens(match_4, Gamma = 2.5, GammaInc = 0.25)

17.5 Making our \(\Gamma\) Estimate more precise: binarysens

# binarysens(m.obj, Gamma = 1.75, GammaInc = 0.05)$bounds %>%
#   tibble() %>% slice(11:17)

18 Discussion

In conclusion, the results from my all of my analyses indicate that the burden of pneumonia among a nationally representative sample of adults (N=2500), is greater among people treated for rheumatoid arthritis. The PS analyses used to balance the groups were 1:1 greedy matching, 1:1 matching with a caliper without replacement, weighting with the inverse propensity score, and twang weighting.

As seen in the plot below, the adjusted ORs range from 2.18 (95% CI 1.07, 4.45) (1:1 match with a caliper) to 3.00 (1.54 to 5.87) (Twang ATT weighting), which are a meaningful drop from the unadjusted OR of 4.87 (95% CI 2.81, 8.50). This great drop is due to the substantial selection bias in the unadjusted analysis, which can be seen by the lack of overlap in the linear PS (Rubin Rule 1= 1.551, Rubin Rule 2=0.567).

fullpo_tte <- tibble(
    analysis = c("Unadjusted", "1:1 Greedy Matching", "1:1 Matching with Caliper",
                 "ATT Weighting by Inverse PS", "Twang ATT Weighting", "Doubly Robust"),
    estimate = c(4.868, 2.64, 2.18, 2.60,3.00, 2.88),
    conf.low = c(2.813, 1.32, 1.07, 1.26, 1.54, 1.39),
    conf.high = c(8.502, 5.28, 4.45, 5.39, 5.87, 5.93))

ggplot(fullpo_tte, aes(x = analysis, y = estimate)) +
    geom_errorbar(aes(ymax = conf.high, ymin = conf.low), width = 0.5) + 
    geom_label(aes(label = estimate), size = 5) +
    theme_bw() + 
  theme(axis.text.x = element_text(angle = 45,  hjust=1)) +
  labs(title = "Compariso of Odds Ratios and 95% Confidence Intervals (N=2502)",
       subtitle = "1:1 Matching with a Caliper Preferred (Best Balance)",
       x = "")

Of all of the PS analyses, I preferred the 1:1 matching with a caliper, which also produced the lowest OR and tightest CI. This was the best method for balancing the groups because it almost perfectly eliminated the observed covariate imbalance between the groups, indicated by Rubin’s Rules 1 and 2 of 0.004 and 1.010, respectively. The improvement in overlap can also visually be seen in the figure below.

p1 <- ggplot(meps1718, aes(x = linps, fill = RA_f)) +
    geom_density(alpha = 0.3)

p2 <- ggplot(meps1718_matched4, aes(x = linps, fill = RA_f)) +
    geom_density(alpha = 0.3)

 p1 + p2

The sensitivity analysis using the matched sample from my preferred 1:1 caliper match produced a gamma of 5.43. This extraordinarily high gamma, indicating a highly insensitive study, should be interpreted with caution. Using the small control sample size (a random subsample of 1986 adults from the available 33,010), there were no matched pairs that experienced the outcome, pneumonia. If all of the controls were used from the original MEPS sample, this gamma (and my results) would be meaningfully different. Thus, my next step will be to include all of the controls and then only filter those with a PS below the minimum PS in the RA group. I will then use the new subset of controls to fit a new PS.

Although this analysis is not yet finished, the relationship observed is consistent with the results seen in a previous case control study, the high number of infections observed in RA drug clinical trials, the pathophysiology of the disease (the lungs are one of the most common sites for extra articular disease), and the mechanism of action of the drugs. These results may help contribute to the scientific community in terms of public health planning, such as ensuring that RA patients are up to date on pneumonia, COVID-19, and influenza immunizations.

This project was the most valuable piece of work that I have done this year. First, I learned how to use the MEPS database for research. This was by far was one of the most difficult parts of the project, as there were many files to navigate with a profound amount of information, intricacies of the study design that were necessary to understand the variables and which were appropriate, and certain data collection limitations that determined what analyses I could complete. The MEPS workshops were extremely helpful, and all of the instructors have given me fast and detailed responses to my multitude of emails. I have gained such an appreciation for this rich database and I cannot believe it is free to the public. I am incredibly grateful that I this class gave me the opportunity to use it.

Second, I learned about what should be included in a propensity score. I thought after reading Rosenbaum, Rubin’s essay, and taking notes in class that I understood and it was simple. However, my original covariate list was less than 15 covariates. As the project went on and I was learning more and more about the database and reading other papers/listening to OSIA presentations, I finally understood that our goal is to pick up as much signal as possible and make the groups as equal as possible. For example, at first, I was very meticulous about including which comorbidities were shown in the literature to potentially be related to my exposure RA, or my outcome, pneumonia. So, I wasn’t including comorbidities like hypertension or hyperlipidemia as these have not been shown to be associated with RA or be risk factors for pneumonia (although other cardiovascular comorbidities are such as CAD). However, after Dr. Love said that one of his papers had 90 something covariates, and I was getting a better feel for propensity scores, I realized it didn’t matter that factors like hypertension, hyperlipidemia, or being a widow weren’t related to the exposure or outcome, what matters is that these groups are as similar as we can possibly make them so we can remove some hidden bias. When I would go for runs in the morning, I would start thinking about all of the other covariates I could add (MEPS is the gift that keeps on giving with covariates) because there is going to be all sorts of hidden bias in a non-randomized trial, but we can help eliminate some of that if we balance people on any available baseline characteristics. I stress baseline, because as I got up to 40 something covariates, that was also a mistake. I was including covariates that were measured at the end of the interview period, such as expenditure, which is incorrect. Not only was this wrong, when I did this, my OR crossed 1 (but also that was back when I was including all RA patients regardless of rx filled). My huge lesson learned is that at the start of the project, select all of the baseline characteristics that are available. Then, keep them at their most granular form. Only collapse them if there are less than 20 people in a particular group for a certain level. Then, evaluate missingness. If there is more than like 25% missing, just drop it (I only had to do this for flu vaccine which had 50% missing). Lastly, at the very end of the project, I learned I need to add weights into the propensity scores. This is another next step.

Third, I learned how many different methods for weighting and matching there are. Before the project, I knew (from class) about 1:k matching with and without replacement and Twang and inverse PS weighting. I was familiar with the concept of caliper, but I didn’t really understand it after learning about it in class. This project allowed me to practice matching with a caliper and see its advantages (super strong balance) and disadvantages (dropping hard to match people from the exposed).

Fourth, I learned a lot about data cleaning in R and making work more reproducible. At first, I was cleaning in both R and SAS. Dr. Love explained to me that it was fine if I chose to do that, but that I need to be able to go back a few years from now and know exactly what I did. By having 4 different documents in different coding languages that were so disorganized, there was no way that I would be able to reproduce what I did. Now I am down to two documents and they are both in R and much better organized.

Lastly, I also learned a lot from other people’s presentations. I liked how Stephanie compared her results from all of the matches and used a plot with 95% CI to do so. She was kind enough to provide me with the code, which I was so happy to include in my project. Although this might seem like something really simple, I know I will be able to use that code in the future. Amr’s presentation using a matching method that matched up to 2 controls for each exposed was a great learning experience for me. I recently read a JAMA paper that used HCUP-NIS data for PS matching and I thought that it was really strong that they did up to two matches; like that their approach was superior to mine because they could have more controls. However, as you explained, it is misleading as they really do not have two matches for every treated. And if you want something similar, to just do weighting. Now that I have learned that weighting is actually better than that method, I will not try to emulate that approach. Finally, while I was presenting, I learned about what happens when the PS is really close to 0 or 1. I learned that you should go back and find out which covariates might be the culprit of this. But furthermore, I learned that I can solve that when I include more controls and remove subjects who do not have PS close to the lower end of the PS range of my exposed group.

To complete this project to my satisfaction I will:

  1. Include weights in the PS analysis. There were two different weights to choose from and I confirmed with two of the people from MEPS which survey weight I will be using. According to their instructions, I will add the weights together from both years and then divide by two
  2. Exclude people with other disease states that use the same medications for RA (eg methotrexate, humira). This will require me to go into the prescription drug file.
  3. After refining the eligibility, I would like to include the whole available (and eligible) sample of controls to make the population as large as possible (There are about 30K potential controls to include, but this number will decrease after I have refined the eligibility)
  4. As I was exploring which weight variable would be appropriate, I found another covariate I wanted to include: whether someone has received food stamps. This slightly scares me that I am still finding covariates. I thought I had done a very thorough job looking through covariates (I went back about 6 times and added more, and I really thought the last time I had exhausted the list), but clearly, I need to do that again.
  5. Someone had suggested that I looked at other diagnosis codes for my outcome. I agreed with that as J18 is not the only diagnosis code for pneumonia. This was the only one that they seemed to use though out of the list of pneumonia codes (pneumonia due to other causes) as they do not specify the bacterial organism involved (eg they do not say wither it was streptococcal pneumonia or mycoplasma pneumonia which have different codes). Not sure why I am listing this as something I am not satisfied with and could change, but I do need to bring that limitation up.
  6. I would like to try matchit and weightit.
LS0tCnRpdGxlOiAiQnVyZGVuIG9mIFBuZXVtb25pYSBpbiBSaGV1bWF0b2lkIEFydGhyaXRpcyBQYXRpZW50cyIKYXV0aG9yOiAiTGluZHNheSBQZXRyZW5jaGlrIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogCiAgICBodG1sX2RvY3VtZW50OgogICAgICAgIHRvYzogVFJVRQogICAgICAgIHRvY19mbG9hdDogVFJVRQogICAgICAgIG51bWJlcl9zZWN0aW9uczogVFJVRQogICAgICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUKLS0tCgojIFNldHVwCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGNvbW1lbnQgPSBOQSkKCmBgYAoKCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KbGlicmFyeShza2ltcikKbGlicmFyeShrbml0cikKbGlicmFyeShyYm91bmRzKQpsaWJyYXJ5KHRhYmxlb25lKQpsaWJyYXJ5KG1hZ3JpdHRyKTsgbGlicmFyeShqYW5pdG9yKSAKbGlicmFyeShicm9vbSk7IGxpYnJhcnkoc3Vydml2YWwpOyBsaWJyYXJ5KGxtZTQpCmxpYnJhcnkoY29iYWx0KTsgbGlicmFyeShNYXRjaGluZyk7IGxpYnJhcnkoTWF0Y2hJdCkKbGlicmFyeShuYW5pYXIpCmxpYnJhcnkoc2ltcHV0YXRpb24pCmxpYnJhcnkobWljZSkKbGlicmFyeShFcGkpCmxpYnJhcnkodHdhbmcpCmxpYnJhcnkoc3VydmV5KQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHJib3VuZHMpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCmRlY2ltIDwtIGZ1bmN0aW9uKHgsIGspIGZvcm1hdChyb3VuZCh4LCBrKSwgbnNtYWxsPWspCgpvcHRpb25zKG1heC5wcmludD0iMjUwIikKb3B0c19rbml0JHNldCh3aWR0aD03NSkKCnRoZW1lX3NldCh0aGVtZV9idygpKQpgYGAKCgoKIyBEYXRhIExvYWQKCmBgYHtyfQpzYW1wbGUgPC0gcmVhZC5jc3YoInNhbXBsZTIuY3N2IikgCmBgYAoKCiMgQ2xlYW5pbmcKCiMjIE1hbmFnaW5nIEJpbmFyeSBWYXJpYWJsZXMKCmBgYHtyIGR1cGVyc2lkfQptZXBzX2NsZWFuIDwtIHNhbXBsZSAlPiUgCiAgbXV0YXRlKGR1cGVyc2lkPWFzLmNoYXJhY3RlcihkdXBlcnNpZCkpICU+JSAKICByZW5hbWUoUkE9bWF4X3JhLCAKICAgICAgICAgcG5ldW1vPW1heF9wbmV1LAogICAgICAgICBhZ2U9YWdlbGFzdCkgJT4lIAogIG11dGF0ZShSQV9mID0gZmN0X3JlY29kZShmYWN0b3IoUkEpLAoiWWVzIiA9ICIxIiwgIk5vIiA9ICIwIikpICU+JSAKICBtdXRhdGUocG5ldW1vX2YgPSBmY3RfcmVjb2RlKGZhY3RvcihwbmV1bW8pLAoiWWVzIiA9ICIxIiwgIk5vIiA9ICIwIikpICU+JSAKICBzZWxlY3QoLXJhLCAtcG5ldSwgLWljZDEwY2R4LCAtY29uZG4sIC1yeG51bSwgLXBpZCwgLWR1aWQpCmBgYAoKCgojIyBjbGVhbmluZyBkZW1vZ3JhcGhpY3M6IHJlZ2lvbiwgcmFjZSwgc2V4LCBlZHVjYXRpb24sIGluc3VyYW5jZSwgaW5jb21lLCBtYXJpdGFsIHN0YXR1cwoKYGBge3J9Cm1lcHNfY2xlYW4gPC0gbWVwc19jbGVhbiAlPiUgCiAgbXV0YXRlKHJlZ2lvbiA9IGZjdF9yZWNvZGUoZmFjdG9yKHJlZ2lvbiksCiJOb3J0aGVhc3QiID0gIjEiLCAiTWlkd2VzdCIgPSAiMiIsICJTb3V0aCIgPSAiMyIsICJXZXN0IiA9ICI0IiwKTlVMTCA9ICItMSIpKSAlPiUgCiAgbXV0YXRlKHBhbmVsID0gYXMuZmFjdG9yKHBhbmVsKSkgJT4lIAogIG11dGF0ZShyZWdpb24gPSBmY3RfaW5mcmVxKHJlZ2lvbikpICU+JSAKICBtdXRhdGUocmFjZT1mY3RfcmVjb2RlKGZhY3RvcihyYWNldGh4KSwKICAgICAgICAgIkhpc3BhbmljIiA9ICIxIiwgIldoaXRlIiA9ICIyIiwgIkJsYWNrIiA9ICIzIiwgIk90aGVyIiA9ICI0IiwgIk90aGVyIiA9ICI1IikpICU+JSAKICBtdXRhdGUocmFjZSA9IGZjdF9pbmZyZXEocmFjZSkpICU+JSAKICBtdXRhdGUoc2V4ID0gZmN0X3JlY29kZShmYWN0b3Ioc2V4KSwKIk0iID0gIjEiLCAiRiIgPSAiMiIpKSAlPiUgCgptdXRhdGUoZWR1Y2F0aW9uID0gCiAgICAgICAgICAgZmN0X3JlY29kZShmYWN0b3IoaGlkZWcpLAoibm9fZGVncmVlIiA9ICIxIiwgIkdFRCIgPSAiMiIsICJIUyIgPSAiMyIsICJCQSI9ICI0IiwgImdyYWR1YXRlIiA9ICI1IiwgImdyYWR1YXRlIj0gIjYiLCAiT3RoZXIiPSI3IiwgIE5VTEwgPSAiLTE1IiwgTlVMTCA9ICAiLTgiLCBOVUxMID0gIi03IiwgTlVMTCA9ICI4IikpICU+JSAKICAKICAgbXV0YXRlKHB1YmxpY19pbnMgPSBmY3RfcmVjb2RlKGZhY3RvcihpbnNjb3YpLAoiTm8iID0gIjEiLCAiWWVzIiA9ICIyIiwgIk5vIj0gIjMiKSkgJT4lIAogIAogICAgbXV0YXRlKHB1YmxpY19pbnMgPSBmY3RfcmVsZXZlbChwdWJsaWNfaW5zLCAiWWVzIikpICU+JSAKICAKICBtdXRhdGUoaW5jb21lID0gZmN0X3JlY29kZShmYWN0b3IocG92Y2F0KSwKIlBvb3IiID0gIjEiLCAiTmVhcl9wb29yIiA9ICIyIiwgIkxvdyIgPSAiMyIsICJNaWRkbGUiPSAiNCIsICJIaWdoIj0iNSIpKSAlPiUgCiAgCiAgbXV0YXRlKG1hcml0YWwgPSBmY3RfcmVjb2RlKGZhY3RvcihtYXJyeTQyeCksCiBOVUxMID0gIi05IiwgTlVMTD0gIi04IiwgTlVMTCA9ICItNyIsIE5VTEwgPSAiLTEiLCAibWFycmllZCI9ICIxIiwibWFycmllZCIgPSAiNyIsICJ3aWRvd2VkIj0gIjIiLCAid2lkb3dlZCI9ICI4IiwgImRpdm9yY2VkIiA9ICIzIiwgImRpdm9yY2VkIiA9ICI5IiwgInNlcGFyYXRlZCIgPSAiMTAiLCAic2VwYXJhdGVkIiA9ICI0IiwgIm5ldmVyX21hcnJpZWQiID0gIjUiLCApKSAlPiUgCiAgCiAgbXV0YXRlKG1hcml0YWwgPSBmY3RfaW5mcmVxKG1hcml0YWwpKSAlPiUgCiAgCiAgICAgIG11dGF0ZShlbXBsb3ltZW50ID0gZmN0X3JlY29kZShmYWN0b3IoZW1wc3QzMSksCiJObyIgPSAiNCIsICJZZXMiPSAiMyIsICJZZXMiPSAiMiIsICJZZXMiPSIxIiwgIE5VTEwgPSAiLTE1IiwgTlVMTCA9ICAiLTciLCBOVUxMID0gIi04IiwgTlVMTCA9ICItMSIsIE5VTEw9ICItOSIpKSAKCmBgYAoKCiMjIGNsZWFuaW5nIG1lZGljYWw6IHByaW1hcnkgY2FyZQoKYGBge3J9Cm1lcHNfY2xlYW4gPC0gbWVwc19jbGVhbiAlPiUgCiAgCiAgbXV0YXRlKHByaW1fY2FyZSA9IGZjdF9yZWNvZGUoZmFjdG9yKGhhdmV1czQyKSwKIlllcyIgPSAiMSIsICJObyIgPSAiMiIsIE5VTEwgPSAiLTgiLCBOVUxMID0gIi03IiwKTlVMTCA9ICItMSIsIE5VTEwgPSAiLTkiKSkgJT4lIAogICAgbXV0YXRlKGZsdV92YWMgPSBmY3RfcmVjb2RlKGZhY3RvcihmbHVfdmFjKSwKIlllcyIgPSAiMSIsICJObyIgPSAiMiIsIE5VTEwgPSAiLTE1IiwgTlVMTCA9ICItMSIsIE5VTEwgPSAiLTkiKSkgJT4lIAogIG11dGF0ZShhc3RobWEgPSBmY3RfcmVjb2RlKGZhY3Rvcihhc3RoZHgpLAoiWWVzIiA9ICIxIiwgIk5vIiA9ICIyIiwgTlVMTCA9ICItOCIsIE5VTEwgPSAiLTciLCBOVUxMID0gIi0xIiwgTlVMTCA9ICItOSIpKSAlPiUKICBtdXRhdGUoYXN0aG1hID0gZmN0X3JlbGV2ZWwoYXN0aG1hLCAiWWVzIikpICU+JSAKICBtdXRhdGUoZGlhYmV0ZXMgPSBmY3RfcmVjb2RlKGZhY3RvcihkaWFiZXRlcyksCiJZZXMiID0gIjEiLCAiTm8iID0gIjIiLCBOVUxMID0gIi04IiwgTlVMTCA9ICItNyIsIE5VTEwgPSAiLTEiLCBOVUxMID0gIi0xNSIsIE5VTEwgPSAiLTkiKSkgJT4lIAogIG11dGF0ZShkaWFiZXRlcyA9IGZjdF9yZWxldmVsKGRpYWJldGVzLCAiWWVzIikpICU+JQogIG11dGF0ZShjYW5jZXIgPSBmY3RfcmVjb2RlKGZhY3RvcihjYW5jZXJkeCksCiJZZXMiID0gIjEiLCAiTm8iID0gIjIiLCBOVUxMID0gIi04IiwgTlVMTCA9ICItNyIsIE5VTEwgPSAiLTEiLCBOVUxMID0gIi0xNSIsIE5VTEwgPSAiLTkiKSkgJT4lIAogIG11dGF0ZShjYW5jZXIgPSBmY3RfcmVsZXZlbChjYW5jZXIsICJZZXMiKSkgJT4lCiAgbXV0YXRlKGJyb25jaGl0aXMgPSBmY3RfcmVjb2RlKGZhY3RvcihjaGJyb24zMSksCiJZZXMiID0gIjEiLCAiTm8iID0gIjIiLCBOVUxMID0gIi04IiwgTlVMTCA9ICItNyIsIE5VTEwgPSAiLTEiLCBOVUxMID0gIi0xNSIsIE5VTEwgPSAiLTkiKSkgJT4lIAogIG11dGF0ZShicm9uY2hpdGlzID0gZmN0X3JlbGV2ZWwoYnJvbmNoaXRpcywgIlllcyIpKSAlPiUKICBtdXRhdGUoZW1waHlzZW1hID0gZmN0X3JlY29kZShmYWN0b3IoZW1waGR4KSwKIlllcyIgPSAiMSIsICJObyIgPSAiMiIsIE5VTEwgPSAiLTgiLCBOVUxMID0gIi03ICIsIE5VTEwgPSAiLTEiLCBOVUxMID0gIi0xNSIsIE5VTEwgPSAiLTkiKSkgJT4lIAogIG11dGF0ZShlbXBoeXNlbWEgPSBmY3RfcmVsZXZlbChlbXBoeXNlbWEsICJZZXMiKSkgJT4lCiAgbXV0YXRlKHN0cm9rZSA9IGZjdF9yZWNvZGUoZmFjdG9yKHN0cmtkeCksCiJZZXMiID0gIjEiLCAiTm8iID0gIjIiLCBOVUxMID0gIi04IiwgTlVMTCA9ICItNyAiLCBOVUxMID0gIi0xIiwgTlVMTCA9ICItMTUiLCBOVUxMID0gIi05IikpICU+JSAKICBtdXRhdGUoc3Ryb2tlID0gZmN0X3JlbGV2ZWwoc3Ryb2tlLCAiWWVzIikpCiAgCmBgYAoKIyMgY2xlYW5pbmcgaGVhbHRoIHN0YXR1cwoKCmBgYHtyfQptZXBzX2NsZWFuIDwtIG1lcHNfY2xlYW4gJT4lIAptdXRhdGUoc21va2U9ZmFjdG9yKHNtb2tlKSkgJT4lIAptdXRhdGUocGVyY19oZWFsdGggPSBmY3RfcmVjb2RlKGZhY3RvcihydGhsdGgzMSksCiJQb29yIiA9ICI1IiwgIkZhaXIiID0gIjQiLCAiR29vZCI9ICIzIiwgIlZHX0V4YyI9ICIyIiwgIlZHX0V4YyI9IjEiLCBOVUxMID0gIi0xIiwgTlVMTCA9ICAiLTciLCBOVUxMID0gIi04IikpICU+JSAKICAKICBtdXRhdGUobWVudGFsID0gZmN0X3JlY29kZShmYWN0b3IobW5obHRoMzEpLAoiUG9vciIgPSAiNSIsICJGYWlyIiA9ICI0IiwgIkdvb2QiPSAiMyIsICJWRyI9ICIyIiwgIkV4Y2VsbGVudCI9IjEiLCAgTlVMTCA9ICItMSIsIE5VTEwgPSAgIi03IiwgTlVMTCA9ICItOCIpKSAgJT4lIAogIAogIAogIG11dGF0ZShJQURMID0gZmN0X3JlY29kZShmYWN0b3IoaWFkbGhwMzEpLAogIlllcyIgPSAiMSIsICJObyI9ICIyIiwgTlVMTCA9ICItOCIsIE5VTEwgPSAgIi03IiwgTlVMTCA9ICItMSIpKSAgJT4lIAogIAogICAgbXV0YXRlKEFETCA9IGZjdF9yZWNvZGUoZmFjdG9yKGFkbGhscDMxKSwKICJZZXMiID0gIjEiLCAiTm8iPSAiMiIsIE5VTEwgPSAiLTgiLCBOVUxMID0gICItNyIsIE5VTEwgPSAiLTEiKSkgICU+JSAKICAKICBtdXRhdGUoZGlmZmljdWx0X2xpZnQxMCA9IGZjdF9yZWNvZGUoZmFjdG9yKGxmdGRpZjMxKSwKICJubyIgPSAiMSIsICJzb21lIj0gIjIiLCAiYV9sb3QiID0gIjMiLCAidW5hYmxlIj0gIjQiLCBOVUxMID0gIi04IiwgTlVMTCA9ICAiLTciLCBOVUxMID0gIi0xIikpICAlPiUgCiAgCiAgCm11dGF0ZShwaHlzX2xpbSA9IGZjdF9yZWNvZGUoZmFjdG9yKHdsa2xpbTMxKSwKIE5VTEwgPSAiLTgiLCBOVUxMPSAiLTciLCBOVUxMID0gIi0xIiwgInllcyI9ICIxIiwibm8iID0gIjIiKSkgICU+JSAKICAKICBtdXRhdGUoY29nX2RpZiA9IGZjdF9yZWNvZGUoZmFjdG9yKGRmY29nNDIpLAogTlVMTCA9ICItOCIsIE5VTEw9ICItNyIsIE5VTEwgPSAiLTEiLCAieWVzIj0gIjEiLCJubyIgPSAiMiIpKSAKYGBgCgoKCgojIyBjbGVhbmluZyBqdXN0IGFkZGVkIHZhcmlhYmxlcyAKCgoKYGBge3J9Cm1lcHNfY2xlYW4gPC0gbWVwc19jbGVhbiAlPiUgCiAgCiAgbXV0YXRlKGhpZ2hicCA9IGZjdF9yZWNvZGUoZmFjdG9yKGhpYnBkeCksCiJZZXMiID0gIjEiLCAiTm8iID0gIjIiLCBOVUxMID0gIi04IiwgTlVMTCA9ICItNyIsIE5VTEwgPSAiLTEiLCBOVUxMID0gIi05IikpICU+JQogIG11dGF0ZShoaWdoYnAgPSBmY3RfcmVsZXZlbChoaWdoYnAsICJZZXMiKSkgJT4lIAogIAogIG11dGF0ZShjaGQgPSBmY3RfcmVjb2RlKGZhY3RvcihjaGRkeCksCiJZZXMiID0gIjEiLCAiTm8iID0gIjIiLCBOVUxMID0gIi04IiwgTlVMTCA9ICItNyIsIE5VTEwgPSAiLTEiLCBOVUxMID0gIi05IikpICU+JQogIG11dGF0ZShjaGQgPSBmY3RfcmVsZXZlbChjaGQsICJZZXMiKSkgJT4lIAogIAogICAgbXV0YXRlKGFuZ2luYSA9IGZjdF9yZWNvZGUoZmFjdG9yKGFuZ2lkeCksCiJZZXMiID0gIjEiLCAiTm8iID0gIjIiLCBOVUxMID0gIi04IiwgTlVMTCA9ICItNyIsIE5VTEwgPSAiLTEiLCBOVUxMID0gIi05IikpICU+JQogIG11dGF0ZShhbmdpbmEgPSBmY3RfcmVsZXZlbChhbmdpbmEsICJZZXMiKSkgJT4lCiAgCiAgICAgICAgbXV0YXRlKE1JID0gZmN0X3JlY29kZShmYWN0b3IobWlkeCksCiJZZXMiID0gIjEiLCAiTm8iID0gIjIiLCBOVUxMID0gIi04IiwgTlVMTCA9ICItNyIsIE5VTEwgPSAiLTEiLCBOVUxMID0gIi05IikpICU+JQogIG11dGF0ZShNSSA9IGZjdF9yZWxldmVsKE1JLCAiWWVzIikpICU+JSAKICAKICAgICAgICAgbXV0YXRlKG90aGVyX2hlYXJ0ID0gZmN0X3JlY29kZShmYWN0b3Iob2hydGR4KSwKIlllcyIgPSAiMSIsICJObyIgPSAiMiIsIE5VTEwgPSAiLTgiLCBOVUxMID0gIi03IiwgTlVMTCA9ICItMSIsIE5VTEwgPSAiLTkiKSkgJT4lCiAgbXV0YXRlKG90aGVyX2hlYXJ0ID0gZmN0X3JlbGV2ZWwob3RoZXJfaGVhcnQsICJZZXMiKSkgJT4lIAogIAogIG11dGF0ZShoaWdoX2Nob2wgPSBmY3RfcmVjb2RlKGZhY3RvcihjaG9sZHgpLAoiWWVzIiA9ICIxIiwgIk5vIiA9ICIyIiwgTlVMTCA9ICItOCIsIE5VTEwgPSAiLTciLCBOVUxMID0gIi0xIiwgTlVMTCA9ICItOSIpKSAlPiUKICBtdXRhdGUoaGlnaF9jaG9sID0gZmN0X3JlbGV2ZWwoaGlnaF9jaG9sLCAiWWVzIikpICU+JSAKICAKICBtdXRhdGUoYXJ0aHJpdGlzID0gZmN0X3JlY29kZShmYWN0b3IoYXJ0aGR4KSwKIlllcyIgPSAiMSIsICJObyIgPSAiMiIsIE5VTEwgPSAiLTgiLCBOVUxMID0gIi03IiwgTlVMTCA9ICItMSIsIE5VTEwgPSAiLTkiKSkgJT4lCiAgbXV0YXRlKGFydGhyaXRpcyA9IGZjdF9yZWxldmVsKGFydGhyaXRpcywgIlllcyIpKSAKICAKYGBgCgoKIyMgc2VsZWN0aW5nIG9ubHkgdmFyaWFibGVzIEkgbmVlZCAKCmBgYHtyfQptZXBzX2NsZWFuIDwtIG1lcHNfY2xlYW4gJT4lIAogIHNlbGVjdChkdXBlcnNpZCwgUkEsIFJBX2YsIHBuZXVtbywgcG5ldW1vX2YsIHRvdGV4cCwgcGFuZWwsIGFnZSwgc2V4LCByYWNlLCBtYXJpdGFsLCByZWdpb24sIGluY29tZSwgZWR1Y2F0aW9uLCAgcHVibGljX2lucywgcHJpbV9jYXJlLCBmbHVfdmFjLCBhc3RobWEsIGRpYWJldGVzLCBjYW5jZXIsIGJyb25jaGl0aXMsIGVtcGh5c2VtYSwgc3Ryb2tlLCAgcGVyY19oZWFsdGgsIG1lbnRhbCwgZW1wbG95bWVudCwgIElBREwsIEFETCwgc21va2UsIHBoeXNfbGltLCBjb2dfZGlmLCBoaWdoYnAsIGNoZCwgYW5naW5hLCBNSSwgIG90aGVyX2hlYXJ0LCBoaWdoX2Nob2wsIGFydGhyaXRpcykKYGBgCgoKIyBDb2RlIEJvb2sgCgojIyBFeHBlcmltZW50YWwgZGVzaWduIEkgdXNlZAoKVGhlIG9yaWdpbmFsIE1FUFMgZGF0YSB3YXMgY29sbGVjdGVkIHByb3NwZWN0aXZlbHkgb3ZlciAyIHllYXJzLiAKRm9yIG15IHN0dWR5LCB0aGVyZSB3YXMgbm8gcmVnYXJkIHRvIHRpbWUgd2hlbiB0aGUgZXhwb3N1cmUsYCBSQWAgYW5kIHRoZSBvdXRjb21lLCBgcG5ldW1vbmlhYCB3ZXJlIGNvbGxlY3RlZCAoaS5lLiB3aGljaCBjYW1lIGJlZm9yZSB0aGUgb3RoZXIpLiBUaGUgY292YXJpYXRlIGluZm9ybWF0aW9uIGlzIGZyb20gdGhlIGJlZ2lubmluZyBvZiB0aGUgc3R1ZHkgcGVyaW9kLCBzbyBhdCB0aGUgc2FtZSB0aW1lIG9yIGJlZm9yZSB0aGUgaW5mb3JtYXRpb24gYWJvdXQgbWVkaWNhbCBjb25kaXRpb25zIHdlcmUgb2J0YWluZWQgKDMgaW4gcGVyc29uIGludGVydmlld3MgcGVyIHllYXIpLiAKCiMjIFdoZXJlIHRvIGZpbmQgY292YXJpYXRlIGluZm9ybWF0aW9uCkluZm9ybWF0aW9uIHdhcyBvYnRhaW5lZCBmcm9tCgpodHRwczovL21lcHMuYWhycS5nb3YvZGF0YV9zdGF0cy9kb3dubG9hZF9kYXRhL3B1ZnMvaDIwOS9oMjA5ZG9jLnBkZgoKCmBgYHtyfQpwYXN0ZShjb2xuYW1lcyhtZXBzX2NsZWFuKSwgY29sbGFwc2UgPSAiIHwgIikKYGBgCgoKCgpWYXJpYWJsZSB8IFR5cGUgfCBEZXNjcmlwdGlvbgotLS0tLS0tLS0tLTogfCA6LS0tLS06IHwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmR1cGVyc2lkIHxjaGFyYWN0ZXIgfCBzdWJqZWN0IGlkZW50aWZpZXIgCnBhbmVsIHwgZmFjdG9yICgzIGxldmVscykgfCBjb3JyZXNwb25kcyB0byB0aGUgY29ob3J0IHN1YmplY3RzIGJlbG9uZyAoMjEsIDIyLCAyMykKUkEgfCBudW1lcmljIHwgd2hldGhlciBvciBub3Qgc3ViamVjdCBoYXMgcmhldW1hdG9pZCBhcnRocml0aXMgKDE9eWVzLCAwPW5vKS4gVGhpcyB3aWxsIGJlIHRoZSBleHBvc3VyZSB2YXJpYWJsZS4gClJBX2YgfCBiaW5hcnkgfCB3aGV0aGVyIG9yIG5vdCBzdWJqZWN0IGhhcyByaGV1bWF0b2lkIGFydGhyaXRpcyAoeWVzLCBubykuIFRoaXMgd2lsbCBiZSB0aGUgZXhwb3N1cmUgdmFyaWFibGUuIApwbmV1bW8gfCBudW1lcmljIHwgd2hldGhlciBvciBub3Qgc3ViamVjdCBoYXMgcG5ldW1vbmlhICgxPXllcywgMD1ubykuIFRoaXMgd2lsbCBiZSB0aGUgb3V0Y29tZSB2YXJpYWJsZS4KcG5ldW1vX2YgfCBiaW5hcnkgfCB3aGV0aGVyIG9yIG5vdCBzdWJqZWN0IGhhcyBwbmV1bW9uaWEgKHllcywgbm8pLiBUaGlzIHdpbGwgYmUgdGhlIG91dGNvbWUgdmFyaWFibGUuCnRvdGV4cCB8IG51bWVyaWMgfCB0b3RhbCBtZWRpY2FsIGV4cGVuZGl0dXJlIGZvciB0aGUgeWVhcgphZ2UgfCBpbnRlZ2VyIHwgYWdlIGluIHllYXJzCnNleCB8IEYvTSB8IEYgPSBGZW1hbGUsIE0gPSBNYWxlCnJhY2UgfCBmYWN0b3IgKDQgbGV2ZWxzKSB8IFdoaXRlLCBIaXNwYW5pYywgQmxhY2ssIE90aGVyCm1hcml0YWwgfCBmYWN0b3IgKDUgbGV2ZWxzKSB8IG1hcml0YWwgc3RhdHVzIChtYXJyaWVkLCBuZXZlcl9tYXJyaWVkLCBkaXZvcmNlZCwgd2lkb3dlZCwgc2VwYXJhdGVkKQpyZWdpb24gfCBmYWN0b3IgKDQgbGV2ZWxzKSB8IENlbnN1cyBSZWdpb24gKFNvdXRoLCBXZXN0LCBNaWR3ZXN0LCBOb3J0aGVhc3QpCmluY29tZSB8IGZhY3RvciAoNSBsZXZlbHMpIHwgRmFtaWx5IGluY29tZSBhcyBhICUgb2YgcG92ZXJ0eSBsaW5lOiBQb29yLCBOZWFyX3Bvb3IsIExvdywgTWlkZGxlLCBIaWdoCmVkdWNhdGlvbiB8IGZhY3RvciAoNiBsZXZlbHMpIHwgaGlnaGVzdCBkZWdyZWUgd2hlbiBmaXJzdCBlbnRlcmVkIE1FUFMgKG5vX2RlZ3JlZSwgR0VELCBIUywgQkEsIGdyYWR1YXRlLCBPdGhlcikKcHVibGljX2lucyB8IGJpbmFyeSB8IFdoZXRoZXIgb3Igbm90IGhhcyBwdWJsaWMgaW5zdXJhbmNlIChZZXMsIE5vKQpwcmltX2NhcmUgfCBiaW5hcnkgfCBEb2VzIGEgcGVyc29uIGhhdmUgYSB1c3VhbCBjYXJlIHByb3ZpZGVyIChZZXMsIE5vKQpmbHVfdmFjIHwgYmluYXJ5IHwgRmx1IHZhY2NpbmF0aW9uIGluIHBhc3QgMTIgbW9udGhzIChZZXMsIE5vKQphc3RobWEgfCAgYmluYXJ5IHwgQXN0aG1hIGRpYWdub3NpcyAoWWVzLCBObykKZGlhYmV0ZXMgfCBiaW5hcnkgfCBEaWFiZXRlcyBkaWFnbm9zaXMgKFllcywgTm8pCmNhbmNlciB8IGJpbmFyeSB8IENhbmNlciBkaWFnbm9zaXMgKFllcywgTm8pCmJyb25jaGl0aXMgfCBiaW5hcnkgfCBCcm9uY2hpdGlzIGRpYWdub3NpcyAoWWVzLCBObykKZW1waHlzZW1hIHwgIGJpbmFyeSB8IGVtcGh5c2VtYSBkaWFnbm9zaXMgKFllcywgTm8pCnN0cm9rZSB8ICBiaW5hcnkgfCBoaXN0b3J5IG9mIHN0cm9rZSAoWWVzLCBObykKcGVyY19oZWFsdGggfCBmYWN0b3IgKDQgbGV2ZWxzKSB8IFBlcmNlaXZlZCBoZWFsdGggc3RhdHVzIChWR19FeGMgPSBjb2xsYXBzZWQgdmVyeSBnb29kL2V4Y2VsbGVudCwgZ29vZCwgZmFpciwgcG9vcikKbWVudGFsIHwgZmFjdG9yICg1IGxldmVscykgfCBQZXJjZWl2ZWQgbWVudGFsIGhlYWx0aCBzdGF0dXMgKGV4Y2VsbGVudCwgdmVyeSBnb29kLCBnb29kLCBmYWlyLCBwb29yKQplbXBsb3ltZW50IHwgYmluYXJ5IHwgY3VycmVudGx5IGVtcGxveWVkIChZZXMsIE5vKQpJQURMIHwgIGJpbmFyeSB8IHJlcXVpcmUgaGVscCB3aXRoIEluc3RydW1lbnRhbCBBY3Rpdml0aWVzIG9mIERhaWx5IExpdmluZyAoWWVzLCBOTykKQURMIHwgYmluYXJ5IHwgcmVxdWlyZSBoZWxwIHdpdGggQWN0aXZpdGllcyBvZiBEYWlseSBMaXZpbmcgKFllcywgTk8pCnNtb2tlIHwgYmluYXJ5IHwgQ3VycmVudGx5IChZZXMsIE5vKQpwaHlzX2xpbSB8IGJpbmFyeSB8ICBkaWZmaWN1bHR5IGluIHBlcmZvcm1pbmcgY2VydGFpbiBzcGVjaWZpYyBwaHlzaWNhbCBhY3Rpb25zIHN1Y2ggYXMgd2Fsa2luZywgY2xpbWJpbmcgc3RhaXJzLCBncmFzcGluZyBvYmplY3RzLCByZWFjaGluZyBvdmVyaGVhZCwgbGlmdGluZywgYmVuZGluZyBvciBzdG9vcGluZywgb3Igc3RhbmRpbmcgZm9yIGxvbmcgcGVyaW9kcyBvZiB0aW1lCmNvZ19kaWZ8ICBiaW5hcnkgfCBkaWZmaWN1bHR5IGNvbmNlbnRyYXRpbmcsIHJlbWVtYmVyaW5nIG9yIG1ha2luZyBkZWNpc2lvbnMgKFllcyxObykKaGlnaGJwIHwgYmluYXJ5IHwgaGF2ZSB5b3UgZXZlciBiZWVuIHRvbGQgeW91IGhhdmUgaGlnaCBibG9vZCBwcmVzc3VyZT8gKFllcywgTm8pCmNoZCB8IGJpbmFyeSB8IGhhdmUgeW91IGV2ZXIgYmVlbiBkaWFnbm9zZWQgd2l0aCBjb3JvbmFyeSBoZWFydCBkaXNlYXNlPyAoWWVzLCBObykKYW5naW5hIHwgYmluYXJ5IHwgaGFkIGV2ZXIgYmVlbiBkaWFnbm9zZWQgYXMgaGF2aW5nIGFuZ2luYSwgb3IgYW5naW5hIHBlY3RvcmlzIChZZXMsIE5vKQpNSSB8IGJpbmFyeSB8IGhhZCBldmVyIGJlZW4gZGlhZ25vc2VkIGFzIGhhdmluZyBhIGhlYXJ0IGF0dGFjaywgb3IgbXlvY2FyZGlhbCBpbmZhcmN0aW9uIChZZXMsIE5vKQpvdGhlcl9oZWFydCB8IGJpbmFyeSB8IGhhZCBldmVyIGJlZW4gZGlhZ25vc2VkIHdpdGggYW55IG90aGVyIGtpbmQgb2YgaGVhcnQgZGlzZWFzZSBvciBjb25kaXRpb24gKFllcywgTm8pCmhpZ2hfY2hvbCB8IGJpbmFyeSB8IGhhZCBldmVyIGJlZW4gZGlhZ25vc2VkIGFzIGhhdmluZyBoaWdoIGNob2xlc3Rlcm9sIChZZXMsIE5vKQphcnRocml0aXMgfCBiaW5hcnkgfCAgaGFkIGV2ZXIgYmVlbiBkaWFnbm9zZWQgd2l0aCBhcnRocml0aXMgKFllcywgTm8pICp0aGlzIHdhcyByZW1vdmVkIGIvYyBvbmx5IDIgcGVvcGxlIGluIFJBIGdyb3VwIGhhZCAnbm8nCgojIFRhYmxlIDEKCmBgYHtyIHRhYmxlb25lfQp2YXJzIDwtIGMoJ2FnZScgLCAnc2V4JyAsICdyYWNlJyAsICdtYXJpdGFsJyAsICdyZWdpb24nICwgJ2luY29tZScgLCAnZWR1Y2F0aW9uJyAsICdwdWJsaWNfaW5zJyAsICdwcmltX2NhcmUnICwgJ2ZsdV92YWMnICwgJ2FzdGhtYScgLCAnZGlhYmV0ZXMnICwgJ2NhbmNlcicgLCAnYnJvbmNoaXRpcycgLCAnZW1waHlzZW1hJyAsICdzdHJva2UnICwgJ3BlcmNfaGVhbHRoJyAsICdtZW50YWwnICwgJ2VtcGxveW1lbnQnICwgJ0lBREwnICwgJ0FETCcgLCAnc21va2UnICwgJ3BoeXNfbGltJyAsICdjb2dfZGlmJyAsICdoaWdoYnAnICwnY2hkJyAsICdhbmdpbmEnICwgJ01JJyAsICdvdGhlcl9oZWFydCcgLCAnaGlnaF9jaG9sJykKCgoKZmFjdG9ydmFycyA8LSBjKCdzZXgnICwgJ3JhY2UnICwgJ21hcml0YWwnICwgJ3JlZ2lvbicgLCAnaW5jb21lJyAsICdlZHVjYXRpb24nICwgJ3B1YmxpY19pbnMnICwgJ3ByaW1fY2FyZScgLCAnZmx1X3ZhYycgLCAnYXN0aG1hJyAsICdkaWFiZXRlcycgLCAnY2FuY2VyJyAsICdicm9uY2hpdGlzJyAsICdlbXBoeXNlbWEnICwgJ3N0cm9rZScgLCAncGVyY19oZWFsdGgnICwgJ21lbnRhbCcgLCAnZW1wbG95bWVudCcgLCAnSUFETCcgLCAnQURMJyAsICdzbW9rZScgLCAncGh5c19saW0nICwgJ2NvZ19kaWYnICwgJ2hpZ2hicCcgLCdjaGQnICwgJ2FuZ2luYScgLCAnTUknICwgJ290aGVyX2hlYXJ0JyAsICdoaWdoX2Nob2wnKQoKdHJ0IDwtIGMoIlJBX2YiKQoKdGFibGUwMSA8LSBDcmVhdGVUYWJsZU9uZShkYXRhID0gbWVwc19jbGVhbiwgCiAgICAgICAgICAgICAgICAgICAgICAgdmFycyA9IHZhcnMsIAogICAgICAgICAgICAgICAgICAgICAgIGZhY3RvclZhcnM9IGZhY3RvcnZhcnMsCiAgICAgICAgICAgICAgICAgICAgICAgc3RyYXRhID0gdHJ0KQpwcmludCh0YWJsZTAxLCB2ZXJib3NlPVRSVUUpIAoKYGBgCgojIG1pc3NpbmduZXNzIGFuZCBpbXB1dGF0aW9uCgotIEhhbGYgb2YgdGhlIHNhbXBsZSBpcyBtaXNzaW5nIGBmbHVfdmFjYCAsIHNvIEkgd2lsbCBwcm9iYWJseSBkcm9wIHRoaXMgYXMgYSBjb3ZhcmlhdGUgb2YgaW50ZXJlc3QuIAoKLSBPdGhlciB2YXJpYWJsZXMgd2Ugd2lsbCBiZSBpbXB1dGluZyBhcmUgc21va2UsIHByaW1fY2FyLCBwaHlzX2xpbSwgZW1wbG95bWVudCwgYnJvbmNoaXRpcywgY29nX2RpZiwgbWVudGFsLCBwZXJfaGVhbHRoLCBJQURMLCBBREwsIHJlZ2lvbiwgZWR1Y2F0aW9uLCBtYXJpdGFsLCBjYW5jZXIsIGNoZCwgYW5naW5hLCBhc3RobWEsIGRpYWJldGVzLCBlbXBoeXNlbWEsIHN0cm9rZSwgaGlnaGJwLCBNSSwgb3RoZXJfaGVhcnQsIGFuZCBoaWdoX2Nob2wKCgpgYGB7ciBtaXNzX3N1bW1hcnkxfQptaXNzX3Zhcl9zdW1tYXJ5KG1lcHNfY2xlYW4pIApgYGAKSGVyZSBpIGFtIHRha2luZyBvdXQgZmx1IHZhY2NpbmUKCmBgYHtyIG5vX2ZsdX0KbWVwc19ub19mbHUgPC0gbWVwc19jbGVhbiAlPiUgc2VsZWN0KC1mbHVfdmFjKQpgYGAKCkkgd2lsbCBsZXQgdGhlICBgbWljZWAgcGFja2FnZSwgZG8gYWxsIG9mIHRoZSBpbXB1dGF0aW9uIGZvciBtZSwgYnV0IGluc3RlYWQgb2YgbXVsdGlwbGUgaW1wdXRhdGlvbiBJIHdpbGwganVzdCBkbyBvbmUuIEFuZCB0aGVuIEkgd2lsbCBwdWxsIG91dCB0aGF0IG9uZSBzaW1wbHkgaW1wdXRlZCBkYXRhIHNldC4gCgoKCmBgYHtyIG1pY2V9CnNldC5zZWVkKDQzMjQzMikKbWVwc19taWNlIDwtIG1pY2UobWVwc19ub19mbHUsIG0gPSAxLCBwcmludEZsYWcgPSBGQUxTRSkKYGBgCgpJIHdpbGwgbm93IHN0b3JlIHRoZSAxc3QgaW1wdXRlZCBkYXRhIHNldCBpbmAgbWVwczE3MThgCgpgYGB7ciBtZXBzMTcxOH0KbWVwczE3MTggPC0gY29tcGxldGUobWVwc19taWNlLCAxKSAlPiUgdGliYmxlKCkKCmRpbShtZXBzMTcxOCkKYGBgCgpBbmQgSSBkbyBub3QgaGF2ZSBhbnkgbW9yZSBtaXNzaW5nIQoKYGBge3Igbl9taXNzfQpuX21pc3MobWVwczE3MTgpCmBgYAoKIyBVbmFkanVzdGVkIEFuYWx5c2lzCgpJZ25vcmluZyBjb3ZhcmlhdGVzLCBJIHdpbGwgZXN0aW1hdGUgdGhlIGVmZmVjdCBvZiBSQSB2cy4gbm8gUkEgb24gdGhlIG91dGNvbWUsIGRldmVsb3BpbmcgcG5ldW1vbmlhLiAKCkJlbG93IGlzIGEgMiB4IDIgdGFibGUgd2hpY2ggd2lsbCBnaXZlIHVzIHRoZSBSUiwgT1IsIGFuZCByaXNrIGRpZmZlcmVuY2UuIApgYGB7cn0KRXBpOjp0d29ieTIodGFibGUobWVwczE3MTgkUkFfZiwgbWVwczE3MTgkcG5ldW1vX2YpKSAlPiUga2FibGUoZGlnPTMpCmBgYAoKCgpCZWxvdyBpcyBhbm90aGVyIG1ldGhvZCB0byBvYnRhaW4gdGhlIE9SLCB0aHJvdWdoIGxvZ2lzdGljIHJlZ3Jlc3Npb24uIAoKYGBge3J9CnVuYWRqdXN0X2JpbmFyeV9vdXRjb21lIDwtIGdsbShwbmV1bW8gfiBSQSwgZGF0YSA9IG1lcHMxNzE4LCBmYW1pbHkgPSBiaW5vbWlhbCgpKQoKdW5hZGp1c3RfYmluYXJ5X291dGNvbWVfdGlkeSA8LSB0aWR5KHVuYWRqdXN0X2JpbmFyeV9vdXRjb21lLCBjb25mLmludCA9IFRSVUUsIGNvbmYubGV2ZWwgPSAwLjk1LCBleHBvbmVudGlhdGUgPSBUUlVFKSAlPiUKICAgIGZpbHRlcih0ZXJtID09ICJSQSIpCgp1bmFkanVzdF9iaW5hcnlfb3V0Y29tZV90aWR5ICU+JSBzZWxlY3QoLXAudmFsdWUsIC1zdGF0aXN0aWMpICU+JSBrYWJsZShkaWc9MykgCmBgYAoKClRoZSAgb2RkcyBvZiBoYXZpbmcgcG5ldW1vbmlhIGluIFJBIGluZGl2aWR1YWxzIHdhcyAgYHIgcm91bmQodW5hZGp1c3RfYmluYXJ5X291dGNvbWVfdGlkeSRlc3RpbWF0ZSwyKWAgKDk1JUNJOiBgciByb3VuZCh1bmFkanVzdF9iaW5hcnlfb3V0Y29tZV90aWR5JGNvbmYubG93LDIpYCwgYHIgcm91bmQodW5hZGp1c3RfYmluYXJ5X291dGNvbWVfdGlkeSRjb25mLmhpZ2gsMilgKSB0aW1lcyBoaWdoZXIgdGhhbiB0aGUgb2RkcyB0aGF0IGEgIG5vbi1SQSBjb250cm9sIGhhZCBwbmV1bW9uaWEuCgoKIyBGaXR0aW5nIHRoZSBwcm9wZW5zaXR5IHNjb3JlIG1vZGVsCgpJIHdpbGwgbm93IGZpdCB0aGUgcHJvcGVuc2l0eSBzY29yZSwgd2hpY2ggcHJlZGljdHMgUkEgc3RhdHVzIGJhc2VkIG9uIHRoZXNlIDMwIGF2YWlsYWJsZSBjb3ZhcmlhdGVzOiAKCmBwYW5lbGAsIGBhZ2VgICwgYHNleGAgLCBgcmFjZWAsIGBtYXJpdGFsYCwgYHJlZ2lvbmAgLCBgaW5jb21lYCAsIGBlZHVjYXRpb25gICwgYHB1YmxpY19pbnMgYCwgYHByaW1fY2FyZWAgLCBgYXN0aG1hYCAsIGBkaWFiZXRlc2AgLCBgY2FuY2VyYCAsIGBicm9uY2hpdGlzYCAsIGBlbXBoeXNlbWFgICwgYHN0cm9rZWAgLCBgcGVyY19oZWFsdGhgICwgYG1lbnRhbGAgLCBgZW1wbG95bWVudGAgLCBgSUFETGAgLCBgQURMYCAsIGBzbW9rZWAgLCBgcGh5c19saW1gICwgYGNvZ19kaWZgICwgYGhpZ2hicGAgLCBgY2hkYCAsIGBhbmdpbmFgICwgYE1JYCAsIGBvdGhlcl9oZWFydGAgLCBgaGlnaF9jaG9sYCAKCmBgYHtyIHBhc3RlX2NvbW1hfQpwYXN0ZShjb2xuYW1lcyhtZXBzMTcxOCksIGNvbGxhcHNlID0gIiwgIikKYGBgCgpXZSdsbCB1c2UgdGhlIGBmLmJ1aWxkYCB0b29sIGZyb20gdGhlIGBjb2JhbHRgIHBhY2thZ2UgaGVyZS4KCmBgYHtyfQptZXBzMTcxOCA8LSBtZXBzMTcxOCAlPiUKICAgIG11dGF0ZSh0cmVhdCA9IGFzLmxvZ2ljYWwoUkFfZiA9PSAiWWVzIikpCgpjb3ZzXzEgPC0gbWVwczE3MTggJT4lCiAgICBzZWxlY3QocGFuZWwsIGFnZSwgc2V4LCByYWNlLCBtYXJpdGFsLCByZWdpb24sIGluY29tZSwgZWR1Y2F0aW9uLCBwdWJsaWNfaW5zLCBwcmltX2NhcmUsIGFzdGhtYSwgZGlhYmV0ZXMsIGNhbmNlciwgYnJvbmNoaXRpcywgZW1waHlzZW1hLCBzdHJva2UsIHBlcmNfaGVhbHRoLCBtZW50YWwsIGVtcGxveW1lbnQsIElBREwsIEFETCwgc21va2UsIHBoeXNfbGltLCBjb2dfZGlmLCBoaWdoYnAsIGNoZCwgYW5naW5hLCBNSSwgb3RoZXJfaGVhcnQsIGhpZ2hfY2hvbCkKCnByb3BfbW9kZWwgPC0gZ2xtKGYuYnVpbGQoInRyZWF0IiwgY292c18xKSwgZGF0YSA9IG1lcHMxNzE4LAogICAgICAgICAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbCkKCnRpZHkocHJvcF9tb2RlbCwgY29uZi5pbnQgPSBUUlVFKSAlPiUKICAgIHNlbGVjdCh0ZXJtLCBlc3RpbWF0ZSwgc3RkLmVycm9yLCBjb25mLmxvdywgY29uZi5oaWdoLCBwLnZhbHVlKSAlPiUKICAgIGtuaXRyOjprYWJsZShkaWdpdHMgPSAzKQpgYGAKCmBgYHtyIGdsYW5jZX0KZ2xhbmNlKHByb3BfbW9kZWwpCmBgYAoKCiMjIyBTdG9yaW5nIHRoZSBQcm9wZW5zaXR5IFNjb3JlcwoKYGBge3Igc3RvcmVfcHN9Cm1lcHMxNzE4IDwtIG1lcHMxNzE4ICU+JQogICAgbXV0YXRlKHBzID0gcHJvcF9tb2RlbCRmaXR0ZWQsCiAgICAgICAgICAgbGlucHMgPSBwcm9wX21vZGVsJGxpbmVhci5wcmVkaWN0b3JzKQoKZ2dwbG90KG1lcHMxNzE4LCBhZXMoeCA9IFJBX2YsIHkgPSBsaW5wcykpICsKICAgIGdlb21fdmlvbGluKCkgKwogICAgZ2VvbV9ib3hwbG90KHdpZHRoID0gMC4zKQpgYGAKClRoZSBkZW5zaXR5IHBsb3QgYmVsb3cgc2hvd3MgdXMgdGhhdCB3ZSBoYXZlIGEgc3Vic3RhbnRpYWwgbnVtYmVyIG9mIFJBIHBhdGllbnRzIHdobyBkbyBub3QgaGF2ZSBvdmVybGFwcGluZyBQUyBzY29yZXMgd2l0aCBub24tUkEgcGF0aWVudHMuIAoKCmBgYHtyIGRlbnNpdHlfcGxvdH0KZ2dwbG90KG1lcHMxNzE4LCBhZXMoeCA9IGxpbnBzLCBmaWxsID0gUkFfZikpICsKICAgIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuMykKYGBgCgoKIyMjIGNoZWNraW5nIHJhbmdlIG9mIFBTCgpUaGUgcmFuZ2Ugb2YgUFMgZm9yIHRoZSBub24tUkEgZ3JvdXAgaXMgMC4wMDE4IHRvIDAuODQwOS4gVGhlIHJhbmdlIGZvciB0aGUgUkEgZ3JvdXAgaXMgMC4wMiB0byAwLjg1LgoKSGF2aW5nIGEgbWluaW11bSB0aGlzIGxvdyBpcyB3b3JyaXNvbWUuICAKCgpgYGB7ciBmYXZzdGF0c30KbWVwczE3MTglJCUgCiAgbW9zYWljOjpmYXZzdGF0cyhwcyB+IFJBX2YpICU+JSBrYWJsZShkaWc9NCkKYGBgCgoKSG1pc2M6OmRlc2NyaWJlIHNob3dzIHVzIHRoYXQgdGhlcmUgYXJlIG1hbnkgb2JzZXJ2YXRpb25zIHdpdGggYSBQUyBzY29yZSBvZiAwLjAwMgoKCgpgYGB7ciBobWlzY30KSG1pc2M6OmRlc2NyaWJlKH4gcHMsIGRhdGEgPSBtZXBzMTcxOCkKYGBgCgojIGBtYXRjaF8xYCAxOjEgR3JlZWR5IG1hdGNoaW5nIG9uIHRoZSBsaW5lYXIgUFMKClRoZSBmaXJzdCB0eXBlIG9mIG1hdGNoIEkgd2lsbCBjb25kdWN0IGlzIGdyZWVkeSAxOjEgbWF0Y2hpbmcsIHdpdGhvdXQgcmVwbGFjZW1lbnQuIEFzIHdlIGhhZCBvbmx5IDE5ODYgIGNvbnRyb2xzLCB3ZSB3aWxsIG5vdCBtYXRjaCBhbGwgb2YgdGhlIDUxNiBSQSBwYXRpZW50cy4KCi0gSSBhbSB1c2luZyB0aGUgYE1hdGNoaW5nYCBwYWNrYWdlCgotIEkgYW0gZGVmaW5pbmcgb3VyIGB0cmVhdGAgKHRyZWF0bWVudCkgYXMgb2NjdXJyaW5nIHdoZW4gYFJBX2ZgIGlzIHllcy4gCgpgYGB7ciBvbmVfdG9fb25lfQptYXRjaF8xIDwtIE1hdGNoKFRyID0gbWVwczE3MTgkdHJlYXQsIFggPSBtZXBzMTcxOCRsaW5wcywgCiAgICAgICAgICAgICAgICAgTSA9IDEsIHJlcGxhY2UgPSBGQUxTRSwgdGllcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgIGVzdGltYW5kID0gIkFUVCIpCgpzdW1tYXJ5KG1hdGNoXzEpCmBgYAoKCiMjIE9idGFpbmluZyB0aGUgTWF0Y2hlZCBTYW1wbGUgZm9yIDE6MSBHcmVlZHkgTWF0Y2gKCkkgYW0gY3JlYXRpbmcgYSBuZXcgZGF0YSBmcmFtZSwgYG1hdGNoMV9tYXRjaGVzYCwgZm9yIHVwY29taW5nIGFuYWx5c2VzLiBUaGlzIHdpbGwgY29udGFpbiBvbmx5IHRoZSBtYXRjaGVkIHN1YmplY3RzLiAKCmBgYHtyfQptYXRjaDFfbWF0Y2hlcyA8LSBmYWN0b3IocmVwKG1hdGNoXzEkaW5kZXgudHJlYXRlZCwgMikpCm1lcHMxNzE4X21hdGNoZWQxIDwtIGNiaW5kKG1hdGNoMV9tYXRjaGVzLCAKICAgICAgICAgICAgICAgICAgICAgICAgIG1lcHMxNzE4W2MobWF0Y2hfMSRpbmRleC5jb250cm9sLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hdGNoXzEkaW5kZXgudHJlYXRlZCksXSkKYGBgCgoKClNvbWUgc2FuaXR5IGNoZWNrczoKCmBgYHtyIGNvdW50X3Nhbml0eX0KbWVwczE3MThfbWF0Y2hlZDEgJT4lIGNvdW50KFJBX2YpCmBgYAoKYGBge3IgaGVhZF9zYW5pdHl9Cm1lcHMxNzE4X21hdGNoZWQxICU+JSBoZWFkKCkKYGBgCgoKTG9va3MgZ29vZC4gIEkgYnVpbHQgYSBuZXcgbWF0Y2hlZCBzYW1wbGUgZGF0YSBmcmFtZSB3aXRoIG9ubHkgdGhlIG1hdGNoZWQgc3ViamVjdHMKCgojIyBDaGVja2luZyBDb3ZhcmlhdGUgQmFsYW5jZSBmb3Igb3VyIDE6MSBHcmVlZHkgTWF0Y2gKCiMjIyBVc2luZyBgYmFsLnRhYmAgdG8gb2J0YWluIGEgYmFsYW5jZSB0YWJsZQoKCmBgYHtyfQpwYXN0ZShjb2xuYW1lcyhtZXBzMTcxOCksIGNvbGxhcHNlID0gIiwgIikKYGBgCgpgYGB7cn0KY292c18xcGx1cyA8LSBtZXBzMTcxOCAlPiUKICAgIHNlbGVjdChwYW5lbCwgYWdlLCBzZXgsIHJhY2UsIG1hcml0YWwsIHJlZ2lvbiwgaW5jb21lLCBlZHVjYXRpb24sIHB1YmxpY19pbnMsIHByaW1fY2FyZSwgYXN0aG1hLCBkaWFiZXRlcywgY2FuY2VyLCBicm9uY2hpdGlzLCBlbXBoeXNlbWEsIHN0cm9rZSwgcGVyY19oZWFsdGgsIG1lbnRhbCwgZW1wbG95bWVudCwgSUFETCwgQURMLCBzbW9rZSwgcGh5c19saW0sIGNvZ19kaWYsIGhpZ2hicCwgY2hkLCBhbmdpbmEsIE1JLCBvdGhlcl9oZWFydCwgaGlnaF9jaG9sLCBwcywgbGlucHMpCgpiYWwxIDwtIGJhbC50YWIobWF0Y2hfMSwKICAgICAgICAgICAgICAgIHRyZWF0ID0gbWVwczE3MTgkUkEsCiAgICAgICAgICAgICAgICBjb3ZzID0gY292c18xcGx1cywgcXVpY2sgPSBGQUxTRSwKICAgICAgICAgICAgICAgIGRhdGEgPSBtZXBzMTcxOCwgc3RhdHMgPSBjKCJtIiwgInYiKSwKICAgICAgICAgICAgICAgIHVuID0gVFJVRSwgZGlzcC52LnJhdGlvID0gVFJVRSkKYmFsMQpgYGAKCiMjIyBDaGVja2luZyBSdWJpbidzIFJ1bGVzIDEgYW5kIDIKCldlJ2xsIGJ1aWxkIGEgbGl0dGxlIHRhYmxlIG9mIHRoZSBSdWJpbidzIFJ1bGVzICgxIGFuZCAyKSBiZWZvcmUgYW5kIGFmdGVyIG91ciBgbWF0Y2hfMWAgaXMgYXBwbGllZC4KCmBgYHtyfQpjb3ZzX2Zvcl9ydWJpbiA8LSBtZXBzMTcxOCAlPiUKICAgIHNlbGVjdChsaW5wcykKCnJ1YmluX20xIDwtIGJhbC50YWIobWF0Y2hfMSwKICAgICAgICAgICAgICAgIHRyZWF0ID0gbWVwczE3MTgkdHJlYXQsCiAgICAgICAgICAgICAgICBjb3ZzID0gY292c19mb3JfcnViaW4sCiAgICAgICAgICAgICAgICBkYXRhID0gbWVwczE3MTgsIHN0YXRzID0gYygibSIsICJ2IiksCiAgICAgICAgICAgICAgICB1biA9IFRSVUUsIGRpc3Audi5yYXRpbyA9IFRSVUUpWzFdCgpydWJpbl9yZXBvcnRfbTEgPC0gdGliYmxlKAogICAgc3RhdHVzID0gYygiUnVsZTEiLCAiUnVsZTIiKSwKICAgIFVubWF0Y2hlZCA9IGMocnViaW5fbTEkQmFsYW5jZSREaWZmLlVuLAogICAgICAgICAgICAgICAgICBydWJpbl9tMSRCYWxhbmNlJFYuUmF0aW8uVW4pLAogICAgTWF0Y2hlZCA9IGMocnViaW5fbTEkQmFsYW5jZSREaWZmLkFkaiwKICAgICAgICAgICAgICAgcnViaW5fbTEkQmFsYW5jZSRWLlJhdGlvLkFkaikpCgpydWJpbl9yZXBvcnRfbTEgJT4lIGtuaXRyOjprYWJsZShkaWdpdHMgPSAyKQpgYGAKCioqRXhwbGFuYXRpb24gb2YgUnViaW4ncyBSdWxlcyoqCgoqKlJ1bGUgMSoqOgoKLSBFdmFsdWF0ZXMgdGhlIHN0YW5kYXJkaXplZCBkaWZmZXJlbmNlcyBvZiB0aGUgbGluZWFyIFBTIGV4cHJlc3NlZCBhcyBwcm9wb3J0aW9ucwoKICAtIFdlIGNhbiBtdWx0aXBseSB0aGUgcmVzdWx0cyBieSAxMDAgdG8gZGVzY3JpYmUgdGhlbSBhcyBwZXJjZW50YWdlcwogIAotIElkZWFsbHksIHRoZXNlIHJlc3VsdHMgc2hvdWxkIGJlIGFzIGNsb3NlIHRvIDAgYXMgcG9zc2libGUsIGFuZCBkZWZpbml0ZWx5IDwgMC41IGluIGFic29sdXRlIHZhbHVlIChvciA1MCUpCgoKKipSdWxlIDIqKgoKLSBFdmFsdWF0ZXMgdGhlIHZhcmlhbmNlIHJhdGlvIG9mIHRoZSBsaW5lYXIgcHJvcGVuc2l0eSBzY29yZXMuCgotIElkZWFsbHksIHRoZXNlIHJlc3VsdHMgc2hvdWxkIGJlIHdpdGhpbiAwLjggdG8gMS4yNSwgYnV0IGRlZmlhbnRseSB3aXRoaW4gMC41IHRvIDIKCgoqKk15IFJlc3VsdHMqKgoKLSBSdWxlIDE6IGJlZm9yZSBtYXRjaGluZyB3ZSBoYXZlIGEgYmlhcyBvZiBgciAxMDAqcnViaW5fcmVwb3J0X20xWzEsMl1gJSwgYW5kIHRoaXMgaXMgcmVkdWNlZCB0byBgciAxMDAqcnViaW5fcmVwb3J0X20xWzEsM11gJSBhZnRlciAxOjEgZ3JlZWR5IG1hdGNoaW5nLgoKLSBSdWxlIDI6IGJlZm9yZSBtYXRjaGluZyB3ZSBoYXZlIGEgdmFyaWFuY2UgcmF0aW8gb2YgYHIgMTAwKnJ1YmluX3JlcG9ydF9tMVsyLDJdYCUsIGFuZCB0aGlzIGJlY29tZXMgYHIgMTAwKnJ1YmluX3JlcG9ydF9tMVsyLDNdYCUgYWZ0ZXIgMToxIGdyZWVkeSBtYXRjaGluZy4KCi0gV2UgY2FuIHNlZSBhIGNvbnNpZGVyYWJsZSBpbXByb3ZlbWVudCwgYnV0IEkgdGhpbmsgd2UgY2FuIGRvIGJldHRlcgoKCgojIyMgVXNpbmcgYGJhbC5wbG90YCBmcm9tIGBjb2JhbHRgCgpBZ2Ugd2FzIG15IG9ubHkgcXVhbnRpdGF0aXZlIHZhcmlhYmxlLiBBbmQgd2Ugd2lsbCBzZWUgaW4gYWxsIG9mIHRoZSBhbmFseXNlcyB0aGF0IHRoaXMgd2FzIG9uZSBvZiB0aGUgbW9zdCBkaWZmaWN1bHQgdG8gYmFsYW5jZSBiZXR3ZWVuIGdyb3Vwcy4gSGVyZSB3ZSBjYW4gc2VlIHRoZSBkaXN0cmlidXRpb25hbCBiYWxhbmNlIGZvciBhZ2UgYmVmb3JlIGFuZCBhZnRlciBtYXRjaGluZy4gCgoKCmBgYHtyIGJhbHBsb3RfbWF0Y2gxfQpiYWwucGxvdChtYXRjaF8xLAogICAgICAgICB0cmVhdCA9IG1lcHMxNzE4JFJBLAogICAgICAgICBjb3ZzID0gY292c18xcGx1cywKICAgICAgICAgdmFyLm5hbWUgPSAiYWdlIiwgCiAgICAgICAgIHdoaWNoID0gImJvdGgiLAogICAgICAgICBzYW1wbGUubmFtZXMgPSAKICAgICAgICAgICAgIGMoIlVubWF0Y2hlZCBTYW1wbGUiLCAiTWF0Y2hlZCBTYW1wbGUiKSkKYGBgCgpXZSBjYW4gZ3JhcGhpY2FsbHkgY29tcGFyZSB0aGUgZGlzdHJpYnV0aW9uIG9mIFBTIGluIGJvdGggZ3JvdXBzIHdpdGggbWlycm9yZWQgaGlzdG9ncmFtcywgYmVmb3JlIGFuZCBhZnRlciBtYXRjaGluZwoKYGBge3J9CmJhbC5wbG90KG1hdGNoXzEsCiAgICAgICAgIHRyZWF0ID0gbWVwczE3MTgkUkEsCiAgICAgICAgIGNvdnMgPSBjb3ZzXzFwbHVzLAogICAgICAgICB2YXIubmFtZSA9ICJwcyIsIAogICAgICAgICB3aGljaCA9ICJib3RoIiwKICAgICAgICAgc2FtcGxlLm5hbWVzID0gCiAgICAgICAgICAgICBjKCJVbm1hdGNoZWQgU2FtcGxlIiwgIk1hdGNoZWQgU2FtcGxlIiksCiAgICAgICAgIHR5cGUgPSAiaGlzdG9ncmFtIiwgbWlycm9yID0gVFJVRSkKYGBgCgpBZnRlciBtYXRjaGluZywgdGhlIG1pcnJvcmVkIGhpc3RvZ3JhbSBsb29rcyB0aGUgc2FtZSEgU28gY29vbCEhCgoKSSBhbSBhbHNvIGV2YWx1YXRpbmcgc29tZSBvZiB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHRoYXQgSSBhbSBpbnRlcmVzdGVkIGluIGJhc2VkIG9uIHByZXZpb3VzIHNvbWUgcHJldmlvdXMgc3Vic3RhbnRpYWwgaW1iYWxhbmNlcwoKCmBgYHtyIHBoeXNfbGltfQpiYWwucGxvdChtYXRjaF8xLAogICAgICAgICB0cmVhdCA9IG1lcHMxNzE4JFJBLAogICAgICAgICBjb3ZzID0gY292c18xcGx1cywKICAgICAgICAgdmFyLm5hbWUgPSAicGh5c19saW0iLCAKICAgICAgICAgd2hpY2ggPSAiYm90aCIsCiAgICAgICAgIHNhbXBsZS5uYW1lcyA9IAogICAgICAgICAgICAgYygiVW5tYXRjaGVkIFNhbXBsZSIsICJNYXRjaGVkIFNhbXBsZSIpKQpgYGAKCgoKYGBge3J9CmJhbC5wbG90KG1hdGNoXzEsCiAgICAgICAgIHRyZWF0ID0gbWVwczE3MTgkUkEsCiAgICAgICAgIGNvdnMgPSBjb3ZzXzFwbHVzLAogICAgICAgICB2YXIubmFtZSA9ICJwZXJjX2hlYWx0aCIsIAogICAgICAgICB3aGljaCA9ICJib3RoIiwKICAgICAgICAgc2FtcGxlLm5hbWVzID0gCiAgICAgICAgICAgICBjKCJVbm1hdGNoZWQgU2FtcGxlIiwgIk1hdGNoZWQgU2FtcGxlIikpCmBgYAoKSXQgbWFrZXMgc2Vuc2UgdGhhdCBwZXJjZWl2ZWQgaGVhbHRoIGFuZCBwaHlzaWNhbCBsaW1pdGF0aW9ucyB3b3VsZCBiZSBzbyBpbWJhbGFuY2VkIGJlZm9yZSBtYXRjaGluZywgZ2l2ZW4gdGhhdCBwZW9wbGUgd2l0aCBSQSBoYXZlIHNpZ25pZmljYW50bHkgcmVkdWNlZCBtb2JpbGl0eS4gU28gdGhhdCdzIHJlYWxseSBhd2Vzb21lIHRoYXQgd2Ugd2VyZSBhYmxlIHRvIGNvcnJlY3QgdGhpcyBiZWNhdXNlIHRoZXNlIGJvdGggYXJlIGdyZWF0bHkgYXNzb2NpYXRlZCB3aXRoIHBvb3IgaGVhbHRoIG91dGNvbWVzLiAKCgoKCiMjIyBVc2luZyBgbG92ZS5wbG90YCB0byBsb29rIGF0IFN0YW5kYXJkaXplZCBEaWZmZXJlbmNlcwoKVGhlIGxvdmUgcGxvdHMgYmVsb3cgc2hvdyB0aGUgc3RhbmRhcmRpemVkIGRpZmZlcmVuY2VzIGJlZm9yZSBhbmQgbWF0Y2hpbmcgZm9yIGVhY2ggY292YXJpYXRlIGFuZCB0aGUgbGluZWFyIFBTLiAKCkFsdGhvdWdoIG9ubHkgb25lIGlzIG5lY2Vzc2FyeSBhcyB0aGV5IGJvdGggZGVzY3JpYmUgdGhlIHNhbWUgdGhpbmcsIEkgbGlrZSB0aGF0IEkgaGF2ZSB0aGUgb3B0aW9uIG9mIHR3byBkaWZmZXJlbnQgd2F5cyBvZiB2aXN1YWxpemluZyB0aGUgc2FtZSBtZXNzYWdlLiBUaGUgZmlyc3QgbG92ZSBwbG90IHByb3ZpZGVzIHRoZSBjdXQgb2ZmcyBmb3IgLTEwJSB0byAxMCUsIHdoaWNoIHRoZSBzZWNvbmQgb25lIHNob3dzIHRoZSBhYnNvbHV0ZSBkaWZmZXJlbmNlLgoKYGBge3IgbG92ZV9ncmVlZHkxLCBmaWcuaGVpZ2h0ID0gMTB9CmxvdmUucGxvdChiYWwxLCAKICAgICAgICAgIHRocmVzaG9sZCA9IC4xLCBzaXplID0gMywKICAgICAgICAgIHZhci5vcmRlciA9ICJ1bmFkanVzdGVkIiwKICAgICAgICAgIHN0YXRzID0gIm1lYW4uZGlmZnMiLAogICAgICAgICAgc3RhcnMgPSAicmF3IiwKICAgICAgICAgIHNhbXBsZS5uYW1lcyA9IGMoIlVubWF0Y2hlZCIsICJNYXRjaGVkIiksCiAgICAgICAgICB0aXRsZSA9ICJMb3ZlIFBsb3QgZm9yIDE6MSBHcmVlZHkgTWF0Y2giKSArCiAgICBsYWJzKGNhcHRpb24gPSAiKiBpbmRpY2F0ZXMgcmF3IG1lYW4gZGlmZmVyZW5jZXMgKGZvciBiaW5hcnkgdmFyaWFibGVzKSIpCmBgYAoKYGBge3IgbG92ZV9ncmVlZHkyLCBmaWcuaGVpZ2h0ID0gMTB9CmxvdmUucGxvdChiYWwxLCAKICAgICAgICAgIHRocmVzaG9sZCA9IC4xLCBzaXplID0gMywKICAgICAgICAgIHZhci5vcmRlciA9ICJ1bmFkanVzdGVkIiwKICAgICAgICAgIHN0YXRzID0gIm1lYW4uZGlmZnMiLAogICAgICAgICAgc3RhcnMgPSAicmF3IiwKICAgICAgICAgIGFicyA9IFRSVUUsCiAgICAgICAgICBzYW1wbGUubmFtZXMgPSBjKCJVbm1hdGNoZWQiLCAiTWF0Y2hlZCIpLAogICAgICAgICAgdGl0bGUgPSAiQWJzb2x1dGUgRGlmZmVyZW5jZXMgZm9yIDE6MSBHcmVlZHkgTWF0Y2giKSArCiAgICBsYWJzKGNhcHRpb24gPSAiKiBpbmRpY2F0ZXMgcmF3IG1lYW4gZGlmZmVyZW5jZXMgKGZvciBiaW5hcnkgdmFyaWFibGVzKSIpCmBgYAoKVGhlIGJhbGFuY2UgYWZ0ZXIgbWF0Y2hpbmcgaXMgaW5jcmVkaWJsZS4gQWxsIG9mIHRoZSBjb3ZhcmlhdGVzIGFyZSB3aXRoaW4gb3VyIHRhcmdldCByYW5nZS4gQnV0IGFzIHdlIHNhdyBiZWZvcmUgd2l0aCBSdWJpbidzIFJ1bGUgIzEsIHRoYXQgd2UganVzdCBiYXJlbHkgbWFkZSBpdCB3aXRoaW4gdGhlIGRlc2lyZWQgcmFuZ2UgZm9yIGxpbmVhciBQUy4gSSB0aGluayB3ZSBjYW4gZG8gYmV0dGVyLiBTbyBJIHdpbGwgdHJ5IG1hdGNoaW5nIHdpdGggYSBjYWxpcGVyIG5leHQuIAoKIyMjIFVzaW5nIGBsb3ZlLnBsb3RgIHRvIGxvb2sgYXQgVmFyaWFuY2UgUmF0aW9zCgpIZXJlIHdlIGNhbiBzZWUgdGhlIHZhcmlhbmNlIHJhdGlvcyAoUnViaW4gUnVsZSAjMikgYmVmb3JlIGFuZCBhZnRlciBtYXRjaGluZyBmb3IgdGhlIGxpbmVhciBQUy4gd2UgY2FuIGFsc28gc2VlIGl0IGZvciBteSBvbmx5IHF1YW50aXRhdGl2ZSB2YXJpYWJsZSwgYWdlIChjYXRlZ29yaWNhbCB2YXJpYWJsZXMgYXJlIGRyb3BwZWQpLiBXZSB3ZXJlIG5vdCB3aXRoaW4gdGhlIGRlc2lyZWQgcmFuZ2Ugb2YgNC81IHRvIDUvNCBiZWZvcmUgYW5kIGFmdGVyIG1hdGNoaW5nIGZvciBhZ2UgKHRoYXQgd2FzIGEgdG91Z2ggdmFyaWFibGUpLCBidXQgd2UgYXJlIGZvciBsaW5wcyAoUnViaW4gIzIpCgoKCmBgYHtyfQpsb3ZlLnBsb3QoYmFsMSwgCiAgICAgICAgICB0aHJlc2hvbGQgPSA0LzUsIHNpemUgPSAzLAogICAgICAgICAgc3RhdHMgPSAidmFyaWFuY2UucmF0aW9zIiwKICAgICAgICAgIHNhbXBsZS5uYW1lcyA9IGMoIlVubWF0Y2hlZCIsICJNYXRjaGVkIiksCiAgICAgICAgICB0aXRsZSA9ICJWYXJpYW5jZSBSYXRpb3MgZm9yIG91ciAxOjEgTWF0Y2giKSAKYGBgCgoKIyBBZGp1c3RlZCBBbmFseXNpcyAxIChhZnRlciAxOjEgR3JlZWR5IE1hdGNoaW5nKQoKCgpgYGB7ciBhZGp1c3RlZF9ncmVlZHl9CmFkai5tMS5wbmV1bW8gPC0gY2xvZ2l0KHBuZXVtbyB+IFJBICsgc3RyYXRhKG1hdGNoMV9tYXRjaGVzKSwgZGF0YT1tZXBzMTcxOF9tYXRjaGVkMSkKYWRqLm0xX3RpZHkgPC0gdGlkeShhZGoubTEucG5ldW1vLCBleHBvbmVudGlhdGUgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICBjb25mLmludCA9IFRSVUUpCgphZGoubTFfdGlkeSAlPiUga2FibGUoZGlnaXRzPTIpCmBgYAoKVGhlICBvZGRzIG9mIGhhdmluZyBwbmV1bW9uaWEgaW4gUkEgaW5kaXZpZHVhbHMgd2FzICBgciByb3VuZChhZGoubTFfdGlkeSRlc3RpbWF0ZSwyKWAgKDk1JUNJOiBgciByb3VuZChhZGoubTFfdGlkeSRjb25mLmxvdywyKWAsIGByIHJvdW5kKGFkai5tMV90aWR5JGNvbmYuaGlnaCwyKWApIHRpbWVzIGhpZ2hlciB0aGFuIHRoZSBvZGRzIHRoYXQgYSBub24tUkEgY29udHJvbCBoYWQgcG5ldW1vbmlhLgoKCiMgYG1hdGNoXzRgIENhbGlwZXIgTWF0Y2hpbmcgKDE6MSB3aXRob3V0IHJlcGxhY2VtZW50KSB3aXRoIHRoZSBgTWF0Y2hpbmdgIHBhY2thZ2UKCi0gSSB3aWxsIHVzZSB0aGUgYE1hdGNoYCBmdW5jdGlvbiB0byBzcGVjaWZ5IGEgY2FsaXBlciBvZiAwLjIgKGZyb20gYE1hdGNoaW5nYCBwYWNrYWdlKQoKICAtIEhlcmUsIHN1YmplY3RzIHdpbGwgb25seSBiZSBtYXRjaGVkIGlmIHRoZXkgZmFsbCB3aXRoaW4gdGhlIG1heGltdW0gZGlzdGFuY2Ugb2YgMC4yIHN0YW5kYXJkIGRldmlhdGlvbnMgb2YgdGhlIGxvZ2l0IG9mIHRoZSBwcm9wZW5zaXR5IHNjb3JlLiAKCiAgLSBJZiB0aGV5IGRvIG5vdCBmYWxsIHdpdGhpbiB0aGlzIHJhbmdlLCB0aG9zZSBzdWJqZWN0cyB3aWxsIGJlIGRyb3BwZWQuIAoKLSBUaGUgbWF0Y2hpbmcgZGVzaWduIHdpbGwgYmUgMToxIHdpdGhvdXQgcmVwbGFjZW1lbnQgCgoKYGBge3J9Cm1hdGNoXzQgPC0gTWF0Y2goVHIgPSBtZXBzMTcxOCR0cmVhdCwgWCA9IG1lcHMxNzE4JGxpbnBzLCAKICAgICAgICAgICAgICAgICBNID0gMSwgcmVwbGFjZSA9IEZBTFNFLCB0aWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgY2FsaXBlciA9IDAuMiwgZXN0aW1hbmQgPSAiQVRUIikKCnN1bW1hcnkobWF0Y2hfNCkKYGBgCgpOb3RlIHRoYXQgd2UgaGF2ZSBub3cgZHJvcHBlZCAzOCBvZiB0aGUgUkE9eWVzIHN1YmplY3RzLCBhbmQgcmVkdWNlZCBvdXIgc2FtcGxlIHRvIHRoZSA0NzggcmVtYWluaW5nIFJBPXllcyBzdWJqZWN0cywgd2hvIGFyZSBwYWlyZWQgd2l0aCA0NzggdW5pcXVlIG1hdGNoZWQgUkEgPSBubyBzdWJqZWN0cyBpbiBvdXIgbWF0Y2hlZCBzYW1wbGUuCgojIyBPYnRhaW5pbmcgdGhlIE1hdGNoZWQgU2FtcGxlCgpIZXJlIEkgYW0gc3RvcmluZyB0aGUgbWF0Y2hlZCBzYW1wbGUKCmBgYHtyfQptYXRjaDRfbWF0Y2hlcyA8LSBmYWN0b3IocmVwKG1hdGNoXzQkaW5kZXgudHJlYXRlZCwgMikpCm1lcHMxNzE4X21hdGNoZWQ0IDwtIGNiaW5kKG1hdGNoNF9tYXRjaGVzLCAKICAgICAgICAgICAgICAgICAgICAgICAgIG1lcHMxNzE4W2MobWF0Y2hfNCRpbmRleC5jb250cm9sLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hdGNoXzQkaW5kZXgudHJlYXRlZCksXSkKYGBgCgpIb3cgbWFueSB1bmlxdWUgc3ViamVjdHMgYXJlIGluIG91ciBtYXRjaGVkIHNhbXBsZT8KCmBgYHtyfQptZXBzMTcxOF9tYXRjaGVkNCAlJCUgbl9kaXN0aW5jdChkdXBlcnNpZCkKYGBgCgpUaGlzIG1hdGNoIGluY2x1ZGVzIDQ3OCBwYWlycyBzbyA5NTYgc3ViamVjdHMsIHNpbmNlIHdlJ3ZlIGRvbmUgbWF0Y2hpbmcgd2l0aG91dCByZXBsYWNlbWVudC4KCmBgYHtyfQptZXBzMTcxOF9tYXRjaGVkNCAlPiUgY291bnQoUkFfZikKYGBgCgoKIyMgQ2hlY2tpbmcgQ292YXJpYXRlIEJhbGFuY2UgZm9yIG91ciAxOjEgQ2FsaXBlciBNYXRjaAoKIyMjIFVzaW5nIGBiYWwudGFiYCB0byBvYnRhaW4gYSBiYWxhbmNlIHRhYmxlCgpgYGB7cn0KY292c180cGx1cyA8LSBtZXBzMTcxOCAlPiUKICAgIHNlbGVjdChwYW5lbCwgYWdlLCBzZXgsIHJhY2UsIG1hcml0YWwsIHJlZ2lvbiwgaW5jb21lLCBlZHVjYXRpb24sIHB1YmxpY19pbnMsIHByaW1fY2FyZSwgYXN0aG1hLCBkaWFiZXRlcywgY2FuY2VyLCBicm9uY2hpdGlzLCBlbXBoeXNlbWEsIHN0cm9rZSwgcGVyY19oZWFsdGgsIG1lbnRhbCwgZW1wbG95bWVudCwgSUFETCwgQURMLCBzbW9rZSwgcGh5c19saW0sIGNvZ19kaWYsIGhpZ2hicCwgY2hkLCBhbmdpbmEsIE1JLCBvdGhlcl9oZWFydCwgaGlnaF9jaG9sLCBwcywgbGlucHMpCgpiYWw0IDwtIGJhbC50YWIobWF0Y2hfNCwKICAgICAgICAgICAgICAgIHRyZWF0ID0gbWVwczE3MTgkUkFfZiwKICAgICAgICAgICAgICAgIGNvdnMgPSBjb3ZzXzRwbHVzLCBxdWljayA9IEZBTFNFLAogICAgICAgICAgICAgICAgZGF0YSA9IG1lcHMxNzE4LCBzdGF0cyA9IGMoIm0iLCAidiIpLAogICAgICAgICAgICAgICAgdW4gPSBUUlVFLCBkaXNwLnYucmF0aW8gPSBUUlVFKQpiYWw0CmBgYAoKIyMjIENoZWNraW5nIFJ1YmluJ3MgUnVsZXMgMSBhbmQgMgoKQmVsb3cgaXMgYSB0YWJsZSBvZiBSdWJpbidzIFJ1bGVzIDEgJiAyIGJlZm9yZSBhbmQgYWZ0ZXIgb3VyIDE6MSBjYWxpcGVyIG1hdGNoIGlzIGFwcGxpZWQKCgpgYGB7cn0KY292c19mb3JfcnViaW4gPC0gbWVwczE3MTggJT4lCiAgICBzZWxlY3QobGlucHMpCgpydWJpbl9tNCA8LSBiYWwudGFiKG1hdGNoXzQsCiAgICAgICAgICAgICAgICB0cmVhdCA9IG1lcHMxNzE4JHRyZWF0LAogICAgICAgICAgICAgICAgY292cyA9IGNvdnNfZm9yX3J1YmluLCAKICAgICAgICAgICAgICAgIGRhdGEgPSBtZXBzMTcxOCwgc3RhdHMgPSBjKCJtIiwgInYiKSwKICAgICAgICAgICAgICAgIHVuID0gVFJVRSwgZGlzcC52LnJhdGlvID0gVFJVRSlbMV0KCnJ1YmluX3JlcG9ydF9tNCA8LSB0aWJibGUoCiAgICBzdGF0dXMgPSBjKCJSdWxlMSIsICJSdWxlMiIpLAogICAgVW5tYXRjaGVkID0gYyhydWJpbl9tNCRCYWxhbmNlJERpZmYuVW4sCiAgICAgICAgICAgICAgICAgIHJ1YmluX200JEJhbGFuY2UkVi5SYXRpby5VbiksCiAgICBNYXRjaGVkID0gYyhydWJpbl9tNCRCYWxhbmNlJERpZmYuQWRqLAogICAgICAgICAgICAgICBydWJpbl9tNCRCYWxhbmNlJFYuUmF0aW8uQWRqKSkKCnJ1YmluX3JlcG9ydF9tNCAlPiUga25pdHI6OmthYmxlKGRpZ2l0cyA9IDMpCgpgYGAKCi0gUnViaW4gUnVsZSAxOiBXZSB3YW50IHRoZSBzdGFuZGFyZGl6ZWQgZGlmZmVyZW5jZXMgb2YgdGhlIHByb3BlbnNpdHkgc2NvcmVzIG9mIHRoZSB0d28gZ3JvdXBzIHRvIGJlIGFzIGNsb3NlIHRvIHplcm8gYXMgcG9zc2libGUuIAogIC0gQmVmb3JlIG1hdGNoaW5nIHdlIGhhdmUgYSBiaWFzIG9mICBgciAxMDAqcnViaW5fcmVwb3J0X200WzEsMl1gJSwgYW5kIHRoaXMgaXMgcmVkdWNlZCB0byBgciAxMDAqcnViaW5fcmVwb3J0X200WzEsM11gJSBhZnRlciAxOjEgY2FsaXBlciBtYXRjaGluZy4KCi0gUnViaW4gUnVsZSAyOiAgd2Ugd2FudCB0aGUgdmFyaWFuY2VzIG9mIHRoZSByYXRpbyBvZiB0aGUgbGluIFBTIHRvIGJlIHdpdGhpbiA0LzUgdG8gNS80LCBidXQgb3VyIHVsdGltYXRlIGdvYWwgaXMgMS4gIAogICAgLSBCZWZvcmUgbWF0Y2hpbmcgd2UgaGF2ZSBhIHZhcmlhbmNlIHJhdGlvIG9mIGByIHJ1YmluX3JlcG9ydF9tNFsyLDJdYCwgYW5kIHRoaXMgYmVjb21lcyBgciBydWJpbl9yZXBvcnRfbTRbMiwzXWAgYWZ0ZXIgMToxIGNhbGlwZXIgbWF0Y2hpbmcuCgoKSW4gc3VtbWFyeSwgd2UgY2FuIHNlZSB0aGF0IGNhbGlwZXIgbWF0Y2hpbmcgcHJvZHVjZWQgYW4gZXhjZXB0aW9uYWxseSBzdHJvbmcgbWF0Y2guIFJ1YmluJ3MgUnVsZSAxIGlzIDAgYW5kIFJ1bGUgMiBpcyBlc3NlbnRpYWxseSAxLiBIb3dldmVyLCB0aGlzIGNvc3QgdXMgMzggcGF0aWVudHMgaW4gdGhlIFJBIGdyb3VwIHdobyB3ZXJlIHRoZSAiaGFyZGVzdCB0byBtYXRjaCIsIHdoaWNoIGlzIHF1aXRlIHNhZC4gCgoKIyMjIFVzaW5nIGBiYWwucGxvdGAgZnJvbSBgY29iYWx0YAoKV2UgY2FuIGdyYXBoaWNhbGx5IGNvbXBhcmUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBQUyBpbiBib3RoIGdyb3VwcyB3aXRoIG1pcnJvcmVkIGhpc3RvZ3JhbXMsIGJlZm9yZSBhbmQgYWZ0ZXIgbWF0Y2hpbmcKCgoKYGBge3J9CmJhbC5wbG90KG1hdGNoXzQsCiAgICAgICAgIHRyZWF0ID0gbWVwczE3MTgkUkFfZiwKICAgICAgICAgY292cyA9IGNvdnNfNHBsdXMsCiAgICAgICAgIHZhci5uYW1lID0gInBzIiwgCiAgICAgICAgIHdoaWNoID0gImJvdGgiLAogICAgICAgICBzYW1wbGUubmFtZXMgPSAKICAgICAgICAgICAgIGMoIlVubWF0Y2hlZCBTYW1wbGUiLCAibWF0Y2hfNCBTYW1wbGUiKSwKICAgICAgICAgdHlwZSA9ICJoaXN0b2dyYW0iLCBtaXJyb3IgPSBUUlVFKQpgYGAKCgpBZnRlciBtYXRjaGluZywgdGhlIG1pcnJvcmVkIGhpc3RvZ3JhbSBsb29rcyB0aGUgc2FtZSEgU28gY29vbCEhCgojIyMgVXNpbmcgYGxvdmUucGxvdGAgdG8gbG9vayBhdCBTdGFuZGFyZGl6ZWQgRGlmZmVyZW5jZXMKClRoZSBsb3ZlIHBsb3QgYmVsb3cgaGVscHMgdXMgdG8gbG9vayBhdCB0aGUgYmFsYW5jZSBvZiBlYWNoIG9mIHRoZSBjb3ZhcmlhdGVzLiAKCldlIGNhbiBzZWUgdGhhdCB3ZSBnb3QgYWxsIG9mIHRoZSBjb3ZhcmlhdGVzIGluIHRoZSBkZXNpcmVkIHJhbmdlIG9mIC0xMCUgdG8gKzEwJSwgZXhjZXB0IGZvciBgYWdlYC4gSG93ZXZlciBpdHMgYSB3aG9sZSBsb3QgYmV0dGVyIHRoYW4gaXQgd2FzIGJlZm9yZS4gCgoKCmBgYHtyIGZpZy5oZWlnaHQgPSAxMH0KbG92ZS5wbG90KGJhbDQsIAogICAgICAgICAgdGhyZXNob2xkID0gLjEsIHNpemUgPSAzLAogICAgICAgICAgdmFyLm9yZGVyID0gInVuYWRqdXN0ZWQiLAogICAgICAgICAgc3RhdHMgPSAibWVhbi5kaWZmcyIsCiAgICAgICAgICBzdGFycyA9ICJyYXciLAogICAgICAgICAgc2FtcGxlLm5hbWVzID0gYygiVW5tYXRjaGVkIiwgIk1hdGNoZWQiKSwKICAgICAgICAgIHRpdGxlID0gIkxvdmUgUGxvdCBmb3IgMToxIENhbGlwZXIgTWF0Y2ggKDQ3OCB1bmlxdWUgcGFpcnMpIikgKwogICAgbGFicyhjYXB0aW9uID0gIiogaW5kaWNhdGVzIHJhdyBtZWFuIGRpZmZlcmVuY2VzIChmb3IgYmluYXJ5IHZhcmlhYmxlcykiKQpgYGAKCiMjIyBVc2luZyBgbG92ZS5wbG90YCB0byBsb29rIGF0IFZhcmlhbmNlIFJhdGlvcwoKV2Ugb25seSBoYWQgb25lIGNvbnRpbnVvdXMgdmFyaWFibGUsIGFnZS4gV2UgY2FuIHNlZSB0aGF0IHRoZSB2YXJpYW5jZSByYXRpbyBvZiBhZ2UuIFdlIGNhbiBzZWUgdGhhdCBpdHMgZXZlbiBjbG9zZXIgdG8gMSBhZnRlciBtYXRjaGluZy4gCgpNb3N0IGltcG9ydGFudGx5LCB3ZSBjYW4gdmlzdWFsbHkgc2VlIHRoYXQgd2UgaGF2ZSBtZXQgUnViaW4ncyBSdWxlICMyIGJlY2F1c2UgdGhlIHZhcmlhbmNlIG9mIHRoZSBsaW5lYXIgUFM9MQoKYGBge3J9CmxvdmUucGxvdChiYWw0LCAKICAgICAgICAgIHRocmVzaG9sZCA9IC41LCBzaXplID0gMywKICAgICAgICAgIHN0YXRzID0gInZhcmlhbmNlLnJhdGlvcyIsCiAgICAgICAgICBzYW1wbGUubmFtZXMgPSBjKCJVbm1hdGNoZWQiLCAiTWF0Y2hlZCIpLAogICAgICAgICAgdGl0bGUgPSAiVmFyaWFuY2UgUmF0aW9zIGZvciBvdXIgMToxIENhbGlwZXIgTWF0Y2giKSAKYGBgCgojIEFkanVzdGVkIEFuYWx5c2lzIDIgKEFmdGVyIENhbGlwZXIgMToxIE1hdGNoaW5nKQoKRnJvbSB0aGUgbWF0Y2hlZCBzYW1wbGUsIGBtYXRjaDRfbWF0Y2hlc2AsIEkgd2lsbCBwZXJmb3JtIGEgY29uZGl0aW9uYWwgbG9naXN0aWMgcmVncmVzc2lvbiB0byBlc3RpbWF0ZSB0aGUgZXhwb3N1cmUgZWZmZWN0IGluIHRlcm1zIG9mIGFuIG9kZHMgcmF0aW8gKGJ5IGV4cG9uZW50aWF0aW5nIHRoZSBsb2cgb2RkcyByYXRpbykuIE5PVEU6IEkgYW0gdXNpbmcgdGhlIDAvMSB2ZXJzaW9uIG9mIHRoZSBvdXRjb21lIChgcG5ldW1vYCkgYW5kIGV4cG9zdXJlIChgUkFgKSBpbmRpY2F0b3IuIAoKSSB3aWxsIGJlIHVzaW5nIHRoZSBmdW5jdGlvbiwgYGNsb2dpdGAsIGZyb20gdGhlIGBzdXJ2aXZhbGAgcGFja2FnZS4gCgpgYGB7ciBjbG9naXR9CmFkai5tLnBuZXVtbyA8LSBjbG9naXQocG5ldW1vIH4gUkEgKyBzdHJhdGEobWF0Y2g0X21hdGNoZXMpLCBkYXRhPW1lcHMxNzE4X21hdGNoZWQ0KQphZGoubV90aWR5IDwtIHRpZHkoYWRqLm0ucG5ldW1vLCBleHBvbmVudGlhdGUgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICBjb25mLmludCA9IFRSVUUpCgphZGoubV90aWR5ICU+JSBrYWJsZShkaWdpdHM9MikKYGBgCgpUaGUgIG9kZHMgb2YgaGF2aW5nIHBuZXVtb25pYSBpbiBSQSBpbmRpdmlkdWFscyB3YXMgIGByIHJvdW5kKGFkai5tX3RpZHkkZXN0aW1hdGUsMilgICg5NSVDSTogYHIgcm91bmQoYWRqLm1fdGlkeSRjb25mLmxvdywyKWAsIGByIHJvdW5kKGFkai5tX3RpZHkkY29uZi5oaWdoLDIpYCkgdGltZXMgaGlnaGVyIHRoYW4gdGhlIG9kZHMgdGhhdCBhIG5vbi1SQSBjb250cm9sIGhhZCBwbmV1bW9uaWEuCgoKCgojIEFUVCBXZWlnaHRpbmc6IHdlaWdodGluZyBieSB0aGUgaW52ZXJzZSBQUwoKCkkgd2lsbCBiZSB3ZWlnaHRpbmcgYnkgdGhlIGludmVyc2Ugb2YgdGhlIHByb3BlbnNpdHkgc2NvcmUgdXNpbmcgdGhlIEFUVCBhcHByb2FjaC4gCgpgYGB7cn0KbWVwczE3MTgkd3RzMSA8LSBpZmVsc2UobWVwczE3MTgkUkE9PTEsIDEsIG1lcHMxNzE4JHBzLygxLW1lcHMxNzE4JHBzKSkKYGBgCgpIZXJlIGlzIGEgcGxvdCBvZiB0aGUgcmVzdWx0aW5nIEFUVCAoYXZlcmFnZSB0cmVhdG1lbnQgZWZmZWN0IG9uIHRoZSB0cmVhdGVkKSB3ZWlnaHRzOgoKYGBge3J9CmdncGxvdChtZXBzMTcxOCwgYWVzKHggPSBwcywgeSA9IHd0czEsIGNvbG9yID0gUkFfZikpICsKICAgIGdlb21fcG9pbnQoKSArIAogICAgZ3VpZGVzKGNvbG9yID0gRkFMU0UpICsKICAgIGZhY2V0X3dyYXAofiBSQV9mKSArCiAgICBsYWJzKHggPSAiRXN0aW1hdGVkIFByb3BlbnNpdHkgZm9yIFJBIiwKICAgICAgICAgeSA9ICJBVFQgd2VpZ2h0cyBmb3IgdGhlIE1FUFMgZGF0YSIsCiAgICAgICAgIHRpdGxlID0gIkFUVCB3ZWlnaHRpbmcgc3RydWN0dXJlIikKYGBgCgoKVGhlIGV4cG9zZWQgKFJBKSBwYXRpZW50cyAocmlnaHQpIGFsbCBnZXQgYSB3ZWlnaHQgb2YgMSwgd2hpbGUgdGhlIGNvbnRyb2wgcGF0aWVudHMsIHRoZSBoaWdoZXIgdGhlaXIgUFMgc2NvcmUgaXMsIHRoZSBoaWdoZXIgdGhlaXIgd2VpZ2h0LiBXaGVuIGNvbnRyb2xzIGhhdmUgYSB2ZXJ5IGxvdyBQUyBmb3IgaGF2aW5nIFJBLCB0aGV5IGhhdmUgbG93IHdlaWdodHMuIAoKIyMgQXNzZXNzIEJhbGFuY2UgQWZ0ZXIgV2VpZ2h0aW5nCgpJIHdpbGwgdXNlIHRoZSB0d2FuZyBwYWNrYWdlIHRvIGFzc2VzcyBiYWxhbmNlIGFmdGVyIHdlaWdodGluZy4KVGhlIHR3YW5nIHBhY2thZ2Ugd2lsbCBjYWxjdWxhdGUgdGhlIGVmZmVjdHMgb24gdGhlIHdlaWdodHMuCgpUaGUgYmFsYW5jZWQgdGFibGUgYmVsb3cgc2hvd3MgbWUgdGhlIFJBIG1lYW4gKHRyZWF0ZWQgbWVhbiApIGFuZCBjb250cm9sIG1lYW4gYmVmb3JlIGFuZCBhZnRlciB3ZWlnaHRpbmcgYW5kIGEgc3RhbmRhcmRpemVkIGRpZmZlcmVuY2UsIHdoaWNoIHdpbGwgYmUgdmVyeSB1c2VmdWwuIAoKYGBge3J9Cm1lcHMxNzE4X2RmIDwtIGJhc2U6OmRhdGEuZnJhbWUobWVwczE3MTgpIAoKY292bGlzdCA8LSBjKCdwYW5lbCcsICdhZ2UnLCAnc2V4JywgJ3JhY2UnLCAnbWFyaXRhbCcsICdyZWdpb24nLCAnaW5jb21lJywgJ2VkdWNhdGlvbicsICdwdWJsaWNfaW5zJywgJ3ByaW1fY2FyZScsICdhc3RobWEnLCAnZGlhYmV0ZXMnLCAnY2FuY2VyJywgJ2Jyb25jaGl0aXMnLCAnZW1waHlzZW1hJywgJ3N0cm9rZScsICdwZXJjX2hlYWx0aCcsICdtZW50YWwnLCAnZW1wbG95bWVudCcsICdJQURMJywgJ0FETCcsICdzbW9rZScsICdwaHlzX2xpbScsICdjb2dfZGlmJywgJ2hpZ2hicCcsICdjaGQnLCAnYW5naW5hJywgJ01JJywgJ290aGVyX2hlYXJ0JywgJ2hpZ2hfY2hvbCcsICdwcycsICdsaW5wcycpCgoKYmFsLnd0czEgPC0gZHgud3RzKHg9bWVwczE3MThfZGYkd3RzMSwgZGF0YT1tZXBzMTcxOF9kZiwgdmFycz1jb3ZsaXN0LCB0cmVhdC52YXI9ICdSQScsIGVzdGltYW5kPSJBVFQiKQogCgpiYWwudGFibGUoYmFsLnd0czEpCmBgYAoKCgpUaGUgYHN0ZC5lZmYuc3pgIHNob3dzIHRoZSBzdGFuZGFyZGl6ZWQgZGlmZmVyZW5jZSwgYnV0IGFzIGEgcHJvcG9ydGlvbiwgcmF0aGVyIHRoYW4gYXMgYSBwZXJjZW50YWdlLiBUaGVyZWZvcmUsIEkgd2lsbCBtdWx0aXBseSB0aGVtIGJ5IDEwMCAmIHBsb3QgdGhlIHJlc3VsdHMuIAoKCmBgYHtyIH0KYmFsLmJlZm9yZS53dHMxIDwtIGJhbC50YWJsZShiYWwud3RzMSlbMV0KYmFsLmFmdGVyLnd0czEgPC0gYmFsLnRhYmxlKGJhbC53dHMxKVsyXQoKYmFsYW5jZS5hdHQud2VpZ2h0cyA8LSBiYXNlOjpkYXRhLmZyYW1lKG5hbWVzID0gcm93bmFtZXMoYmFsLmJlZm9yZS53dHMxJHVudyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcmUud2VpZ2h0aW5nID0gMTAwKmJhbC5iZWZvcmUud3RzMSR1bnckc3RkLmVmZi5zeiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFUVC53ZWlnaHRlZCA9IDEwMCpiYWwuYWZ0ZXIud3RzMVtbMV1dJHN0ZC5lZmYuc3opCmJhbGFuY2UuYXR0LndlaWdodHMgPC0gZ2F0aGVyKGJhbGFuY2UuYXR0LndlaWdodHMsIHRpbWluZywgc3pkLCAyOjMpCmBgYAoKCgoKQmVsb3cgaXMgYSAgcGxvdCBvZiBzdGFuZGFyZGl6ZWQgZGlmZmVyZW5jZXMgYmVmb3JlIGFuZCBhZnRlciBBVFQgd2VpZ2h0aW5nCgoKYGBge3IgZmlnLmhlaWdodCA9IDEwfQpnZ3Bsb3QoYmFsYW5jZS5hdHQud2VpZ2h0cywgYWVzKHggPSBzemQsIHkgPSByZW9yZGVyKG5hbWVzLCBzemQpLCBjb2xvciA9IHRpbWluZykpICsKICAgIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsgCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwKSArCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKC0xMCwxMCksIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbCA9ICJibHVlIikgKwogICAgbGFicyh4ID0gIlN0YW5kYXJkaXplZCBEaWZmZXJlbmNlIiwgeSA9ICIiLCAKICAgICAgICAgdGl0bGUgPSAiU3RhbmRhcmRpemVkIERpZmZlcmVuY2UgYmVmb3JlIGFuZCBhZnRlciBBVFQgV2VpZ2h0aW5nIiwKICAgICAgICAgc3VidGl0bGUgPSAiTj0yNTAyIikgCmBgYAoKClRoaXMgcGxvdCBpbmRpY2F0ZXMgdGhhdCB0aGUgd2VpZ2h0aW5nIGlzIGRvaW5nIGEgbmljZSBqb2IuIFRoZSByZWQgZG90cyBhcmUgYWxsIHdpdGhpbiB0aGUgLTEwJSB0byArMTAlIHJhbmdlLiAKCiMjIFJ1YmluJ3MgUnVsZXMgYWZ0ZXIgQVRUIHdlaWdodGluZwoKCiMjIyBSdWJpbidzIFJ1bGUgMQoKCmBgYHtyIHJ1YmluXzF9CmJhbGFuY2UuYXR0LndlaWdodHMgJT4lIGZpbHRlcihuYW1lcyA9PSAibGlucHMiKQpgYGAKCgpUaGUgc3RhbmRhcmRpemVkIGRpZmZlcmVuY2UgYmVmb3JlIHdlaWdodGluZyB3YXMgMTU1LjElLiBBZnRlciB3ZWlnaHRpbmcsIHRoZSBzdGFuZGFyZGl6ZWQgZGlmZmVyZW5jZSB3YXMgLTExLjUlLiBUaGlzIGlzIG5vdCB3aXRoaW4gdGhlIC0xMCUgdG8gMTAlIHJhbmdlLCBzbyB3ZSB3b3VsZCBub3QgcGFzcyBSdWxlIDEgd2l0aCBmbHlpbmcgY29sb3JzLiAgCgoKIyMjIFJ1YmluJ3MgUnVsZSAyCgoKCmBgYHtyfQptZXBzMTcxOF9kZiA8LSBiYXNlOjpkYXRhLmZyYW1lKG1lcHMxNzE4KSAKCmNvdmxpc3RfcHMgPC0gYygnbGlucHMnKQoKCmJhbC53dHMyIDwtIGR4Lnd0cyh4PW1lcHMxNzE4X2RmJHd0czEsIGRhdGE9bWVwczE3MThfZGYsIHZhcnM9Y292bGlzdF9wcywgdHJlYXQudmFyPSAnUkEnLCBlc3RpbWFuZD0iQVRUIikKIAoKYmFsLnRhYmxlKGJhbC53dHMyKQpgYGAKCnRyZWF0ZWQgc2Q6ICgxLjAzNyleMj0xLjA3NTM2OQpjb250cm9sIHNkOiAoMS4xODYpXjI9MS40MDY1OTYKCmBgYHtyfQoxLjE4NioxLjE4NgpgYGAKCmBgYHtyfQooKDEuMDM3KV4yKS8oKDEuMTg2KV4yKQpgYGAKCgpUaGUgc3RhbmRhcmQgZGV2aWF0aW9ucyBvZiB0aGUgbGluZWFyIFBTIGFmdGVyIHdlaWdodGluZyBvZiAxLjAzNyBpbiB0aGUgUkEgZ3JvdXAgYW5kIDEuMTg2IGluIHRoZSBub24tUkEgZ3JvdXAuIDEuMDM3XjIgLyAxLjE4NiBeMiA9IDAuNzY0NSwgd2hpY2ggaXMganVzdCBvdXRzaWRlIG91ciBkZXNpcmVkIHJhbmdlIG9mIDQvNSB0byA1LzQsIGhvd2V2ZXIgaXQgaXMgd2l0aGluIHRoZSAgMS8yIHRvIDIuIEFyZ3VhYmx5LCB3ZSBjYW4gcGFzcyBSdWxlIDIuIAoKCiMgVHdhbmcgcGFja2FnZSBmb3Igd2VpZ2h0aW5nCgoKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0V9CiMgUmVjYWxsIHRoYXQgdHdhbmcgZG9lcyBub3QgcGxheSB3ZWxsIHdpdGggdGliYmxlcywKCnBzLm1lcHMxNzE4IDwtIHBzKFJBIH4gcGFuZWwgKyBhZ2UgKyBzZXggKyByYWNlICsgbWFyaXRhbCArIHJlZ2lvbiArIGluY29tZSArIGVkdWNhdGlvbiArIHB1YmxpY19pbnMgKyBwcmltX2NhcmUgKyBhc3RobWEgKyBkaWFiZXRlcyArIGNhbmNlciArIGJyb25jaGl0aXMgKyBlbXBoeXNlbWEgKyBzdHJva2UgKyBwZXJjX2hlYWx0aCArIG1lbnRhbCArIGVtcGxveW1lbnQgKyBJQURMICsgQURMICsgc21va2UgKyBwaHlzX2xpbSArIGNvZ19kaWYgKyBoaWdoYnAgKyBjaGQgKyBhbmdpbmEgKyBNSSArIG90aGVyX2hlYXJ0ICsgaGlnaF9jaG9sLAogICAgICAgICAgICAgZGF0YSA9IG1lcHMxNzE4X2RmLAogICAgICAgICAgICAgbi50cmVlcyA9IDMwMDAsCiAgICAgICAgICAgICBpbnRlcmFjdGlvbi5kZXB0aCA9IDIsCiAgICAgICAgICAgICBzdG9wLm1ldGhvZCA9IGMoImVzLm1lYW4iKSwKICAgICAgICAgICAgIGVzdGltYW5kID0gIkFUVCIsCiAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UpCmBgYAoKCgojIyMgQXNzZXNzaW5nIEJhbGFuY2Ugd2l0aCBgY29iYWx0YAoKYGBge3J9CmJhbC50YWIocHMubWVwczE3MTgsIGZ1bGwuc3RvcC5tZXRob2QgPSAiZXMubWVhbi5hdHQiKQpgYGAKCgoKCgojIyBTZW1pLUF1dG9tYXRlZCBMb3ZlIHBsb3Qgb2YgU3RhbmRhcmRpemVkIERpZmZlcmVuY2VzCgpXZSBjYW4gc2VlIHRoYXQgdGhlIHByb3BlbnNpdHkgc2NvcmUgaGFzIGltcHJvdmVkLCBidXQgbm90IGFsbCB0aGUgd2F5IGRvd24gdG8gd2hlcmUgd2Ugd291bGQgbGlrZSBpdCB0byBiZS4gIChSdWJpbiAxKQoKYGBge3IgZmlnLmhlaWdodCA9IDEwfQpwIDwtIGxvdmUucGxvdChiYWwudGFiKHBzLm1lcHMxNzE4KSwgCiAgICAgICAgICAgICAgIHRocmVzaG9sZCA9IC4xLCBzaXplID0gMywgCiAgICAgICAgICAgICAgIHRpdGxlID0gIlN0YW5kYXJkaXplZCBEaWZmcyBhbmQgVFdBTkcgQVRUIHdlaWdodGluZyIpCnAgKyB0aGVtZV9idygpCmBgYAoKCgojIyBwbG90IG9mIHZhcmlhbmNlIHJhdGlvcwoKV2UgY2FuIHNlZSBiZWxvdyB0aGF0IHdlIGhhdmUgYSB2YXJpYW5jZSByYXRpbyBvZiB0aGUgbGluIFBTIGluIHRoZSByYW5nZSB3ZSB3b3VsZCBsaWtlIGl0IHRvIGJlLiAKCmBgYHtyfQpwIDwtIGxvdmUucGxvdChiYWwudGFiKHBzLm1lcHMxNzE4KSwgc3RhdCA9ICJ2IiwKICAgICAgICAgICAgICAgdGhyZXNob2xkID0gMS4yNSwgc2l6ZSA9IDMsIAogICAgICAgICAgICAgICB0aXRsZSA9ICJWYXJpYW5jZSBSYXRpb3M6IFRXQU5HIEFUVCB3ZWlnaHRpbmciKQpwICsgdGhlbWVfYncoKQpgYGAKCgoKIyBBZGp1c3RlZCBBbmFseXNpcyAzICAoQWZ0ZXIgV2VpZ2h0aW5nKQoKIyMgV2VpZ2h0aW5nIG9ubHkgCgojIyMgV2l0aCBBVFQgd2VpZ2h0cwoKCkkgYW0gdXNpbmcgbG9naXN0aWMgcmVncmVzc2lvbiB3aXRoIHRoZSB3ZWlnaGVkIHNhbXBsZSB0byBnZXQgdGhlIGFkanVzdGVkIG9kZHMgb2YgdGhlIG91dGNvbWUsIHBuZXVtb25pYS4gCgpgYGB7cn0KbWVwczE3MTh3dDEuZGVzaWduIDwtIHN2eWRlc2lnbihpZHM9fjEsIHdlaWdodHM9fnd0czEsIGRhdGE9bWVwczE3MTgpICMgdXNpbmcgQVRUIHdlaWdodHMKCmFkanBuZXVtby53dDEgPC0gc3Z5Z2xtKHBuZXVtbyB+IFJBLCBkZXNpZ249bWVwczE3MTh3dDEuZGVzaWduLCBmYW1pbHk9cXVhc2liaW5vbWlhbCgpKQoKd3RfYXR0X3Jlc3VsdHMgPC0gdGlkeShhZGpwbmV1bW8ud3QxLCBjb25mLmludCA9IFRSVUUsIGV4cG9uZW50aWF0ZSA9IFRSVUUpICU+JSAKICAgIGZpbHRlcih0ZXJtID09ICJSQSIpICU+JSBzZWxlY3QoLXN0YXRpc3RpYywgLSBwLnZhbHVlKSAKCnd0X2F0dF9yZXN1bHRzICU+JSBrYWJsZShkaWdpdHM9MykKYGBgCgoKVGhlICBvZGRzIG9mIGhhdmluZyBwbmV1bW9uaWEgaW4gUkEgaW5kaXZpZHVhbHMgd2FzICBgciByb3VuZCh3dF9hdHRfcmVzdWx0cyRlc3RpbWF0ZSwyKWAgKDk1JUNJOiBgciByb3VuZCh3dF9hdHRfcmVzdWx0cyRjb25mLmxvdywyKWAsIGByIHJvdW5kKHd0X2F0dF9yZXN1bHRzJGNvbmYuaGlnaCwyKWApIHRpbWVzIGhpZ2hlciB0aGFuIHRoZSBvZGRzIHRoYXQgYSBub24tUkEgY29udHJvbCBoYWQgcG5ldW1vbmlhLgoKCgojIyMgd2l0aCBUV0FORyBBVFQgd2VpZ2h0cwoKYGBge3J9Cm1lcHMxNzE4d3QzLmRlc2lnbiA8LSBzdnlkZXNpZ24oaWRzPX4xLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgd2VpZ2h0cz1+Z2V0LndlaWdodHMocHMubWVwczE3MTgsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdG9wLm1ldGhvZCA9ICJlcy5tZWFuIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE9bWVwczE3MTgpICMgdXNpbmcgdHdhbmcgQVRUIHdlaWdodHMKCmFkam91dDIud3QzIDwtIHN2eWdsbShwbmV1bW8gfiBSQSwgZGVzaWduPW1lcHMxNzE4d3QzLmRlc2lnbiwKICAgICAgICAgICAgICAgICAgICAgIGZhbWlseT1xdWFzaWJpbm9taWFsKCkpCgp3dF90d2FuZ2F0dF9yZXN1bHRzMiA8LSB0aWR5KGFkam91dDIud3QzLCBjb25mLmludCA9IFRSVUUsIGV4cG9uZW50aWF0ZSA9IFRSVUUpICU+JSAKICAgIGZpbHRlcih0ZXJtID09ICJSQSIpICU+JSBzZWxlY3QoLXN0YXRpc3RpYywgLSBwLnZhbHVlKSAKCnd0X3R3YW5nYXR0X3Jlc3VsdHMyICU+JSBrYWJsZShkaWc9MikKYGBgCgoKVGhlICBvZGRzIG9mIGhhdmluZyBwbmV1bW9uaWEgaW4gUkEgaW5kaXZpZHVhbHMgd2FzICBgciByb3VuZCh3dF90d2FuZ2F0dF9yZXN1bHRzMiRlc3RpbWF0ZSwyKWAgKDk1JUNJOiBgciByb3VuZCh3dF90d2FuZ2F0dF9yZXN1bHRzMiRjb25mLmxvdywyKWAsIGByIHJvdW5kKHd0X3R3YW5nYXR0X3Jlc3VsdHMyJGNvbmYuaGlnaCwyKWApIHRpbWVzIGhpZ2hlciB0aGFuIHRoZSBvZGRzIHRoYXQgYSBub24tUkEgY29udHJvbCBoYWQgcG5ldW1vbmlhLgoKIyMgRG91YmxlIFJvYnVzdAoKIyMjIFVzaW5nIEFUVCB3ZWlnaHRzCgpJIGFtIHVzaW5nIHRoZSBkb3VibGUgcm9idXN0IGFwcHJvYWNoIHRvIGVzdGltYXRlIHRoZSBvZGRzIG9mIHBuZXVtb25pYSBpbiBSQSB2cyBub24tUkEgcGF0aWVudHMuIAoKVGhlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gYXBwcm9hY2ggYmVsb3cgdXNlcyB0aGUgYHN2eWRlc2lnbmAgYW5kIGBzdnlnbG1gIGZ1bmN0aW9ucyBmcm9tIHRoZSBgc3VydmV5YCBwYWNrYWdlLiBJdCBpcyBkb3VibGUgcm9idXN0IGJlY2F1c2UgaW4gYWRkaXRpb24gdG8gYFJBYCwgaXQgYWxzbyBhZGRzIGBsaW5wc2AKCgpgYGB7ciBEUl9hdHR9CmRyLnBuZXVtby53dDEgPC0gc3Z5Z2xtKHBuZXVtbyB+IFJBICsgbGlucHMsIGRlc2lnbj1tZXBzMTcxOHd0MS5kZXNpZ24sCiAgICAgICAgICAgICAgICAgICAgICBmYW1pbHk9cXVhc2liaW5vbWlhbCgpKQpkcl9hdHRfcG5ldW1vIDwtIHRpZHkoZHIucG5ldW1vLnd0MSwgZXhwb25lbnRpYXRlID0gVFJVRSwgY29uZi5pbnQgPSBUUlVFKSAlPiUgCiAgICBmaWx0ZXIodGVybSA9PSAiUkEiKQoKZHJfYXR0X3BuZXVtbyAgJT4lIGthYmxlKGRpZ2l0cz0yKQpgYGAKCgpUaGUgIG9kZHMgb2YgaGF2aW5nIHBuZXVtb25pYSBpbiBSQSBpbmRpdmlkdWFscyB3YXMgIGByIHJvdW5kKGRyX2F0dF9wbmV1bW8kZXN0aW1hdGUsMilgICg5NSVDSTogYHIgcm91bmQoZHJfYXR0X3BuZXVtbyRjb25mLmxvdywyKWAsIGByIHJvdW5kKGRyX2F0dF9wbmV1bW8kY29uZi5oaWdoLDIpYCkgdGltZXMgaGlnaGVyIHRoYW4gdGhlIG9kZHMgdGhhdCBhIG5vbi1SQSBjb250cm9sIGhhZCBwbmV1bW9uaWEuCgoKCiMjIyBVc2luZyBgdHdhbmdgIEFUVCB3ZWlnaHRzCgpgYGB7cn0KZHIub3V0Mi53dDMgPC0gc3Z5Z2xtKHBuZXVtbyB+IFJBICsgbGlucHMsIGRlc2lnbj1tZXBzMTcxOHd0My5kZXNpZ24sCiAgICAgICAgICAgICAgICAgICAgICBmYW1pbHk9cXVhc2liaW5vbWlhbCgpKQpkcl90d2FuZ2F0dF9vdXQyIDwtIHRpZHkoZHIub3V0Mi53dDMsIGV4cG9uZW50aWF0ZSA9IFRSVUUsIGNvbmYuaW50ID0gVFJVRSkgJT4lIAogICAgZmlsdGVyKHRlcm0gPT0gIlJBIikKCmRyX3R3YW5nYXR0X291dDIKYGBgCgoKIyBDb21wYXJpc29uIG9mIFJlc3VsdHMKCkJlbG93IGFyZSB0aGUgT1IgYW5kIHRoZSBhc3NvY2lhdGVkIDk1JSBDSSBmb3IgZWFjaCBvZiB0aGUgUFMgYW5hbHlzZXMKCmBgYHtyIE9SX3Bsb3R9CmZ1bGxwb190dGUgPC0gdGliYmxlKAogICAgYW5hbHlzaXMgPSBjKCJVbmFkanVzdGVkIiwgIjE6MSBHcmVlZHkgTWF0Y2hpbmciLCAiMToxIE1hdGNoaW5nIHdpdGggQ2FsaXBlciIsCiAgICAgICAgICAgICAgICAgIkFUVCBXZWlnaHRpbmcgYnkgSW52ZXJzZSBQUyIsICJUd2FuZyBBVFQgV2VpZ2h0aW5nIiwgIkRvdWJseSBSb2J1c3QiKSwKICAgIGVzdGltYXRlID0gYyg0Ljg2OCwgMi42NCwgMi4xOCwgMi42MCwzLjAwLCAyLjg4KSwKICAgIGNvbmYubG93ID0gYygyLjgxMywgMS4zMiwgMS4wNywgMS4yNiwgMS41NCwgMS4zOSksCiAgICBjb25mLmhpZ2ggPSBjKDguNTAyLCA1LjI4LCA0LjQ1LCA1LjM5LCA1Ljg3LCA1LjkzKSkKCmdncGxvdChmdWxscG9fdHRlLCBhZXMoeCA9IGFuYWx5c2lzLCB5ID0gZXN0aW1hdGUpKSArCiAgICBnZW9tX2Vycm9yYmFyKGFlcyh5bWF4ID0gY29uZi5oaWdoLCB5bWluID0gY29uZi5sb3cpLCB3aWR0aCA9IDAuNSkgKyAKICAgIGdlb21fbGFiZWwoYWVzKGxhYmVsID0gZXN0aW1hdGUpLCBzaXplID0gNSkgKwogICAgdGhlbWVfYncoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsICBoanVzdD0xKSkgKwogIGxhYnModGl0bGUgPSAiQ29tcGFyaXNvIG9mIE9kZHMgUmF0aW9zIGFuZCA5NSUgQ29uZmlkZW5jZSBJbnRlcnZhbHMgKE49MjUwMikiLAogICAgICAgc3VidGl0bGUgPSAiMToxIE1hdGNoaW5nIHdpdGggYSBDYWxpcGVyIFByZWZlcnJlZCAoQmVzdCBCYWxhbmNlKSIsCiAgICAgICB4ID0gIiIpCmBgYAoKCk9mIGFsbCBvZiB0aGVzZSBtZXRob2RzLCBJIHByZWZlciB0aGUgMToxIG1hdGNoaW5nIHdpdGggYSBjYWxpYmVyIGJlY2F1c2UgaXQgcHJvZHVjZWQgdGhlIGJlc3QgY292YXJpYXRlIGJhbGFuY2UuIAoKIyBTZW5zaXRpdml0eSBBbmFseXNpcyAKCiMjIE9idGFpbmluZyB0aGUgTWF0Y2hlZCBTYW1wbGUKCkFsdGhvdWdoIEkgYWxyZWFkeSBoYWQgdGhlIG1hdGNoZWQgc2FtcGxlIGZyb20gdGhlIGNhbGlwZXIgbWF0Y2ggYG1lcHMxNzE4X21hdGNoZWQ0YCBhYm92ZSwgSSBjcmVhdGVkIGFub3RoZXIgb25lIGBtZXBzMTcxOF9tYXRjaGVkNF9zYCBiZWNhdXNlIEkgbm90aWNlZCB0aGF0IHRoZSBjb2RlIGluIHRoZSBleGFtcGxlIGluY2x1ZGVkIGFuIGFycmFuZ2Ugc3RhdGVtZW50IGF0IHRoZSBib3R0b20gYW5kIEknbSBub3Qgc3VyZSBpZiB3ZSBuZWVkIHRoYXQuICAKCmBgYHtyfQptZXBzMTcxOF9tYXRjaGVkNF9zIDwtIAogICAgICBjYmluZChtYXRjaDRfbWF0Y2hlcywgCiAgICAgIG1lcHMxNzE4W2MobWF0Y2hfNCRpbmRleC5jb250cm9sLCAKICAgICAgbWF0Y2hfNCRpbmRleC50cmVhdGVkKSxdKSAlPiUgCiAgYXJyYW5nZShtYXRjaDRfbWF0Y2hlcykKYGBgCgojIyBUaGUgTWF0Y2hlZCBTYW1wbGUKCkJlbG93IGlzIHRoZSBtYXRjaGVkIHNhbXBsZSBmcm9tIHRoZSAxOjEgY2FsaXBlciBtYXRjaCB3aXRob3V0IHJlcGxhY2VtZW50CgpgYGB7cn0KaGVhZChtZXBzMTcxOF9tYXRjaGVkNF9zKQpgYGAKCiMjIEJ1aWxkaW5nIGEgMngyIHRhYmxlIGZyb20gdGhlIE1hdGNoZWQgU2FtcGxlCgpUaGlzIDIgeCAyIHRhYmxlIHRoYXQgSSBhbSBtYWtpbmcgZnJvbSB0aGUgbWF0Y2hlZCBzYW1wbGUgd2lsbCBiZSBwbHVnZ2VkIGludG8gdGhlIHNwcmVhZHNoZWV0IHRvIGdldCBhbiBleGFjdCB2YWx1ZSBvZiBnYW1tYQoKYGBge3J9CnRtcCA8LSBtZXBzMTcxOF9tYXRjaGVkNF9zICU+JSAKICBtdXRhdGUocmVzID0gMTAqUkEgKyBwbmV1bW9fZikgJT4lCiAgICBncm91cF9ieShtYXRjaDRfbWF0Y2hlcykgJT4lCiAgICBzdW1tYXJpemUob3V0LnRyZWF0ZWQgPSBwbmV1bW9fZlsyXSwgCiAgICAgICAgICAgICAgb3V0LmNvbnRyb2wgPSBwbmV1bW9fZlsxXSkgCmBgYAoKYGBge3J9CnRtcCAlPiUgdGFieWwob3V0LmNvbnRyb2wsIG91dC50cmVhdGVkKSAlPiUgYWRvcm5fdGl0bGUoKQpgYGAKCiFbR2FtbWFfcmVzdWx0c19mcm9tX0V4Y2VsXShGaWd1cmUxLnBuZykKClRoZSBnYW1tYSBvYnRhaW5lZCB3YXMgNS40MjYuIFRoaXMgZ2FtbWEgaW5kaWNhdGVzIGEgaGlnaGx5IGluc2Vuc2l0aXZlIHN0dWR5IChleHRyZW1lbHkgZmFyIGZyb20gMSkKCldheXMgb2YgaW50ZXJwcmV0aW5nOgoKLSBUaGUgcmVzdWx0IHdhcyBub3Qgc2Vuc2l0aXZlIHRvIGFuIHVubWVhc3VyZWQgYmluYXJ5IGNvdmFyaWF0ZSB3aGljaCBsZWQgdG8gYSA1LjQzIGZvbGQgaW5jcmVhc2UgaW4gdGhlIG9kZHMgb2YgZXhwb3N1cmUgdG8gUkEgYW5kIHdhcyBhIHBlcmZlY3QgcHJlZGljdG9yIG9mIHBuZXVtb25pYS4gCgotIFRvIGV4cGxhaW4gdGhlIGFzc29jaWF0aW9uIGluIHRoaXMgcGFydGljdWxhciBzdHVkeSwgb25lIHdvdWxkIG5lZWQgYSBoaWRkZW4gYmlhcyBvZiA1LjQzCgotIFRvIGF0dHJpYnV0ZSB0aGUgb2JzZXJ2ZWQgc2lnbmlmaWNhbnQgb3V0Y29tZSB0byBhbiB1bm9ic2VydmVkIGNvdmFyaWF0ZSByYXRoZXIgdGhhbiB0byBSQSwgdGhhdCBvYnNlcnZlZCBjb3ZhcmlhdGUgaGFzIHRvIGluY3JlYXNlIHRoZSBvZGRzIG9mIGJlaW5nIGluIHRoZSBSQSBncm91cCBieSBhIGZhY3RvciBvZiA1LjQzIGFuZCBhbHNvIHByZWRpY3QgcG5ldW1vbmlhIHF1aXRlIHdlbGwuIAoKIyMgRXN0aW1hdGluZyAkXEdhbW1hJCB3aXRoIGBiaW5hcnlzZW5zYAoKVGhlIGNvZGUgYmVsb3cgZGlkbid0IHdvcmssIHdoaWNoIGlzIHdoeSBJIGhhZCB0byBkbyBpdCBvbiB0aGUgc3ByZWFkc2hlZXQuIFRoZSBlcnJvciBzdGF0ZWQ6ICJFcnJvciBpbiBpZiAoeS50bXAxID49IHkudG1wMikgeyA6IG1pc3NpbmcgdmFsdWUgd2hlcmUgVFJVRS9GQUxTRSBuZWVkZWQiCgpgYGB7cn0KIyBiaW5hcnlzZW5zKG1hdGNoXzQsIEdhbW1hID0gMi41LCBHYW1tYUluYyA9IDAuMjUpCmBgYAoKIyMgTWFraW5nIG91ciAkXEdhbW1hJCBFc3RpbWF0ZSBtb3JlIHByZWNpc2U6IGBiaW5hcnlzZW5zYAoKYGBge3J9CiMgYmluYXJ5c2VucyhtLm9iaiwgR2FtbWEgPSAxLjc1LCBHYW1tYUluYyA9IDAuMDUpJGJvdW5kcyAlPiUKIyAgIHRpYmJsZSgpICU+JSBzbGljZSgxMToxNykKYGBgCgoKIyBEaXNjdXNzaW9uCgpJbiBjb25jbHVzaW9uLCB0aGUgcmVzdWx0cyBmcm9tIG15IGFsbCBvZiBteSBhbmFseXNlcyBpbmRpY2F0ZSB0aGF0IHRoZSBidXJkZW4gb2YgcG5ldW1vbmlhIGFtb25nIGEgbmF0aW9uYWxseSByZXByZXNlbnRhdGl2ZSBzYW1wbGUgb2YgYWR1bHRzIChOPTI1MDApLCBpcyBncmVhdGVyIGFtb25nIHBlb3BsZSB0cmVhdGVkIGZvciByaGV1bWF0b2lkIGFydGhyaXRpcy4gVGhlIFBTIGFuYWx5c2VzIHVzZWQgdG8gYmFsYW5jZSB0aGUgZ3JvdXBzIHdlcmUgMToxIGdyZWVkeSBtYXRjaGluZywgMToxIG1hdGNoaW5nIHdpdGggYSBjYWxpcGVyIHdpdGhvdXQgcmVwbGFjZW1lbnQsIHdlaWdodGluZyB3aXRoIHRoZSBpbnZlcnNlIHByb3BlbnNpdHkgc2NvcmUsIGFuZCB0d2FuZyB3ZWlnaHRpbmcuCgogQXMgc2VlbiBpbiB0aGUgcGxvdCBiZWxvdywgdGhlIGFkanVzdGVkIE9ScyByYW5nZSBmcm9tIDIuMTggKDk1JSBDSSAxLjA3LCA0LjQ1KSAoMToxIG1hdGNoIHdpdGggYSBjYWxpcGVyKSB0byAzLjAwICgxLjU0IHRvIDUuODcpIChUd2FuZyBBVFQgd2VpZ2h0aW5nKSwgd2hpY2ggYXJlIGEgbWVhbmluZ2Z1bCBkcm9wIGZyb20gdGhlIHVuYWRqdXN0ZWQgT1Igb2YgNC44NyAoOTUlIENJIDIuODEsIDguNTApLiBUaGlzIGdyZWF0IGRyb3AgaXMgZHVlIHRvIHRoZSBzdWJzdGFudGlhbCBzZWxlY3Rpb24gYmlhcyBpbiB0aGUgdW5hZGp1c3RlZCBhbmFseXNpcywgd2hpY2ggY2FuIGJlIHNlZW4gYnkgdGhlIGxhY2sgb2Ygb3ZlcmxhcCBpbiB0aGUgbGluZWFyIFBTIChSdWJpbiBSdWxlIDE9IDEuNTUxLCBSdWJpbiBSdWxlIDI9MC41NjcpLiAKCgpgYGB7ciBPUl9wbG90Mn0KZnVsbHBvX3R0ZSA8LSB0aWJibGUoCiAgICBhbmFseXNpcyA9IGMoIlVuYWRqdXN0ZWQiLCAiMToxIEdyZWVkeSBNYXRjaGluZyIsICIxOjEgTWF0Y2hpbmcgd2l0aCBDYWxpcGVyIiwKICAgICAgICAgICAgICAgICAiQVRUIFdlaWdodGluZyBieSBJbnZlcnNlIFBTIiwgIlR3YW5nIEFUVCBXZWlnaHRpbmciLCAiRG91Ymx5IFJvYnVzdCIpLAogICAgZXN0aW1hdGUgPSBjKDQuODY4LCAyLjY0LCAyLjE4LCAyLjYwLDMuMDAsIDIuODgpLAogICAgY29uZi5sb3cgPSBjKDIuODEzLCAxLjMyLCAxLjA3LCAxLjI2LCAxLjU0LCAxLjM5KSwKICAgIGNvbmYuaGlnaCA9IGMoOC41MDIsIDUuMjgsIDQuNDUsIDUuMzksIDUuODcsIDUuOTMpKQoKZ2dwbG90KGZ1bGxwb190dGUsIGFlcyh4ID0gYW5hbHlzaXMsIHkgPSBlc3RpbWF0ZSkpICsKICAgIGdlb21fZXJyb3JiYXIoYWVzKHltYXggPSBjb25mLmhpZ2gsIHltaW4gPSBjb25mLmxvdyksIHdpZHRoID0gMC41KSArIAogICAgZ2VvbV9sYWJlbChhZXMobGFiZWwgPSBlc3RpbWF0ZSksIHNpemUgPSA1KSArCiAgICB0aGVtZV9idygpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgIGhqdXN0PTEpKSArCiAgbGFicyh0aXRsZSA9ICJDb21wYXJpc28gb2YgT2RkcyBSYXRpb3MgYW5kIDk1JSBDb25maWRlbmNlIEludGVydmFscyAoTj0yNTAyKSIsCiAgICAgICBzdWJ0aXRsZSA9ICIxOjEgTWF0Y2hpbmcgd2l0aCBhIENhbGlwZXIgUHJlZmVycmVkIChCZXN0IEJhbGFuY2UpIiwKICAgICAgIHggPSAiIikKYGBgCgoKCk9mIGFsbCBvZiB0aGUgUFMgYW5hbHlzZXMsIEkgcHJlZmVycmVkIHRoZSAxOjEgbWF0Y2hpbmcgd2l0aCBhIGNhbGlwZXIsIHdoaWNoIGFsc28gcHJvZHVjZWQgdGhlIGxvd2VzdCBPUiBhbmQgdGlnaHRlc3QgQ0kuIFRoaXMgd2FzIHRoZSBiZXN0IG1ldGhvZCBmb3IgYmFsYW5jaW5nIHRoZSBncm91cHMgYmVjYXVzZSBpdCBhbG1vc3QgcGVyZmVjdGx5IGVsaW1pbmF0ZWQgdGhlIG9ic2VydmVkIGNvdmFyaWF0ZSBpbWJhbGFuY2UgYmV0d2VlbiB0aGUgZ3JvdXBzLCBpbmRpY2F0ZWQgYnkgUnViaW7igJlzIFJ1bGVzIDEgYW5kIDIgb2YgMC4wMDQgYW5kIDEuMDEwLCByZXNwZWN0aXZlbHkuICBUaGUgaW1wcm92ZW1lbnQgaW4gb3ZlcmxhcCBjYW4gYWxzbyB2aXN1YWxseSBiZSBzZWVuIGluIHRoZSBmaWd1cmUgYmVsb3cuICAKCgoKYGBge3IgZGVuc2l0eV9wbG90Mn0KcDEgPC0gZ2dwbG90KG1lcHMxNzE4LCBhZXMoeCA9IGxpbnBzLCBmaWxsID0gUkFfZikpICsKICAgIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuMykKCnAyIDwtIGdncGxvdChtZXBzMTcxOF9tYXRjaGVkNCwgYWVzKHggPSBsaW5wcywgZmlsbCA9IFJBX2YpKSArCiAgICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjMpCgogcDEgKyBwMgpgYGAKClRoZSBzZW5zaXRpdml0eSBhbmFseXNpcyB1c2luZyB0aGUgbWF0Y2hlZCBzYW1wbGUgZnJvbSBteSBwcmVmZXJyZWQgMToxIGNhbGlwZXIgbWF0Y2ggcHJvZHVjZWQgYSBnYW1tYSBvZiA1LjQzLiBUaGlzIGV4dHJhb3JkaW5hcmlseSBoaWdoIGdhbW1hLCBpbmRpY2F0aW5nIGEgaGlnaGx5IGluc2Vuc2l0aXZlIHN0dWR5LCBzaG91bGQgYmUgaW50ZXJwcmV0ZWQgd2l0aCBjYXV0aW9uLiBVc2luZyB0aGUgc21hbGwgY29udHJvbCBzYW1wbGUgc2l6ZSAoYSByYW5kb20gc3Vic2FtcGxlIG9mIDE5ODYgYWR1bHRzIGZyb20gdGhlIGF2YWlsYWJsZSAzMywwMTApLCB0aGVyZSB3ZXJlIG5vIG1hdGNoZWQgcGFpcnMgdGhhdCBleHBlcmllbmNlZCB0aGUgb3V0Y29tZSwgcG5ldW1vbmlhLiBJZiBhbGwgb2YgdGhlIGNvbnRyb2xzIHdlcmUgdXNlZCBmcm9tIHRoZSBvcmlnaW5hbCBNRVBTIHNhbXBsZSwgdGhpcyBnYW1tYSAoYW5kIG15IHJlc3VsdHMpIHdvdWxkIGJlIG1lYW5pbmdmdWxseSBkaWZmZXJlbnQuIFRodXMsIG15IG5leHQgc3RlcCB3aWxsIGJlIHRvIGluY2x1ZGUgYWxsIG9mIHRoZSBjb250cm9scyBhbmQgdGhlbiBvbmx5IGZpbHRlciB0aG9zZSB3aXRoIGEgUFMgYmVsb3cgdGhlIG1pbmltdW0gUFMgaW4gdGhlIFJBIGdyb3VwLiBJIHdpbGwgdGhlbiB1c2UgdGhlIG5ldyBzdWJzZXQgb2YgY29udHJvbHMgdG8gZml0IGEgbmV3IFBTLiAKCkFsdGhvdWdoIHRoaXMgYW5hbHlzaXMgaXMgbm90IHlldCBmaW5pc2hlZCwgdGhlIHJlbGF0aW9uc2hpcCBvYnNlcnZlZCBpcyBjb25zaXN0ZW50IHdpdGggdGhlIHJlc3VsdHMgc2VlbiBpbiBhIHByZXZpb3VzIGNhc2UgY29udHJvbCBzdHVkeSwgdGhlIGhpZ2ggbnVtYmVyIG9mIGluZmVjdGlvbnMgb2JzZXJ2ZWQgaW4gUkEgZHJ1ZyBjbGluaWNhbCB0cmlhbHMsIHRoZSBwYXRob3BoeXNpb2xvZ3kgb2YgdGhlIGRpc2Vhc2UgKHRoZSBsdW5ncyBhcmUgb25lIG9mIHRoZSBtb3N0IGNvbW1vbiBzaXRlcyBmb3IgZXh0cmEgYXJ0aWN1bGFyIGRpc2Vhc2UpLCBhbmQgdGhlIG1lY2hhbmlzbSBvZiBhY3Rpb24gb2YgdGhlIGRydWdzLiBUaGVzZSByZXN1bHRzIG1heSBoZWxwIGNvbnRyaWJ1dGUgdG8gdGhlIHNjaWVudGlmaWMgY29tbXVuaXR5IGluIHRlcm1zIG9mIHB1YmxpYyBoZWFsdGggcGxhbm5pbmcsIHN1Y2ggYXMgZW5zdXJpbmcgdGhhdCBSQSBwYXRpZW50cyBhcmUgdXAgdG8gZGF0ZSBvbiBwbmV1bW9uaWEsIENPVklELTE5LCBhbmQgaW5mbHVlbnphIGltbXVuaXphdGlvbnMuIAoKCiAgVGhpcyBwcm9qZWN0IHdhcyB0aGUgbW9zdCB2YWx1YWJsZSBwaWVjZSBvZiB3b3JrIHRoYXQgSSBoYXZlIGRvbmUgdGhpcyB5ZWFyLiBGaXJzdCwgSSBsZWFybmVkIGhvdyB0byB1c2UgdGhlIE1FUFMgZGF0YWJhc2UgZm9yIHJlc2VhcmNoLiBUaGlzIHdhcyBieSBmYXIgd2FzIG9uZSBvZiB0aGUgbW9zdCBkaWZmaWN1bHQgcGFydHMgb2YgdGhlIHByb2plY3QsIGFzIHRoZXJlIHdlcmUgbWFueSBmaWxlcyB0byBuYXZpZ2F0ZSB3aXRoIGEgcHJvZm91bmQgYW1vdW50IG9mIGluZm9ybWF0aW9uLCBpbnRyaWNhY2llcyBvZiB0aGUgc3R1ZHkgZGVzaWduIHRoYXQgd2VyZSBuZWNlc3NhcnkgdG8gdW5kZXJzdGFuZCB0aGUgdmFyaWFibGVzIGFuZCB3aGljaCB3ZXJlIGFwcHJvcHJpYXRlLCBhbmQgY2VydGFpbiBkYXRhIGNvbGxlY3Rpb24gbGltaXRhdGlvbnMgdGhhdCBkZXRlcm1pbmVkIHdoYXQgYW5hbHlzZXMgSSBjb3VsZCBjb21wbGV0ZS4gIFRoZSBNRVBTIHdvcmtzaG9wcyB3ZXJlIGV4dHJlbWVseSBoZWxwZnVsLCBhbmQgYWxsIG9mIHRoZSBpbnN0cnVjdG9ycyBoYXZlIGdpdmVuIG1lIGZhc3QgYW5kIGRldGFpbGVkIHJlc3BvbnNlcyB0byBteSBtdWx0aXR1ZGUgb2YgZW1haWxzLiAgSSBoYXZlIGdhaW5lZCBzdWNoIGFuIGFwcHJlY2lhdGlvbiBmb3IgdGhpcyByaWNoIGRhdGFiYXNlIGFuZCBJIGNhbm5vdCBiZWxpZXZlIGl0IGlzIGZyZWUgdG8gdGhlIHB1YmxpYy4gSSBhbSBpbmNyZWRpYmx5IGdyYXRlZnVsIHRoYXQgSSB0aGlzIGNsYXNzIGdhdmUgbWUgdGhlIG9wcG9ydHVuaXR5IHRvIHVzZSBpdC4gCgogIFNlY29uZCwgSSBsZWFybmVkIGFib3V0IHdoYXQgc2hvdWxkIGJlIGluY2x1ZGVkIGluIGEgcHJvcGVuc2l0eSBzY29yZS4gSSB0aG91Z2h0IGFmdGVyIHJlYWRpbmcgUm9zZW5iYXVtLCBSdWJpbuKAmXMgZXNzYXksIGFuZCB0YWtpbmcgbm90ZXMgaW4gY2xhc3MgdGhhdCBJIHVuZGVyc3Rvb2QgYW5kIGl0IHdhcyBzaW1wbGUuIEhvd2V2ZXIsIG15IG9yaWdpbmFsIGNvdmFyaWF0ZSBsaXN0IHdhcyBsZXNzIHRoYW4gMTUgY292YXJpYXRlcy4gQXMgdGhlIHByb2plY3Qgd2VudCBvbiBhbmQgSSB3YXMgbGVhcm5pbmcgbW9yZSBhbmQgbW9yZSBhYm91dCB0aGUgZGF0YWJhc2UgYW5kIHJlYWRpbmcgb3RoZXIgcGFwZXJzL2xpc3RlbmluZyB0byBPU0lBIHByZXNlbnRhdGlvbnMsIEkgZmluYWxseSB1bmRlcnN0b29kIHRoYXQgb3VyIGdvYWwgaXMgdG8gcGljayB1cCBhcyBtdWNoIHNpZ25hbCBhcyBwb3NzaWJsZSBhbmQgbWFrZSB0aGUgZ3JvdXBzIGFzIGVxdWFsIGFzIHBvc3NpYmxlLiBGb3IgZXhhbXBsZSwgYXQgZmlyc3QsIEkgd2FzIHZlcnkgbWV0aWN1bG91cyBhYm91dCBpbmNsdWRpbmcgd2hpY2ggY29tb3JiaWRpdGllcyB3ZXJlIHNob3duIGluIHRoZSBsaXRlcmF0dXJlIHRvIHBvdGVudGlhbGx5IGJlIHJlbGF0ZWQgdG8gbXkgZXhwb3N1cmUgUkEsIG9yIG15IG91dGNvbWUsIHBuZXVtb25pYS4gU28sIEkgd2FzbuKAmXQgaW5jbHVkaW5nIGNvbW9yYmlkaXRpZXMgbGlrZSBoeXBlcnRlbnNpb24gb3IgaHlwZXJsaXBpZGVtaWEgYXMgdGhlc2UgaGF2ZSBub3QgYmVlbiBzaG93biB0byBiZSBhc3NvY2lhdGVkIHdpdGggUkEgb3IgYmUgcmlzayBmYWN0b3JzIGZvciBwbmV1bW9uaWEgKGFsdGhvdWdoIG90aGVyIGNhcmRpb3Zhc2N1bGFyIGNvbW9yYmlkaXRpZXMgYXJlIHN1Y2ggYXMgQ0FEKS4gSG93ZXZlciwgYWZ0ZXIgRHIuIExvdmUgc2FpZCB0aGF0IG9uZSBvZiBoaXMgcGFwZXJzIGhhZCA5MCBzb21ldGhpbmcgY292YXJpYXRlcywgYW5kIEkgd2FzIGdldHRpbmcgYSBiZXR0ZXIgZmVlbCBmb3IgcHJvcGVuc2l0eSBzY29yZXMsIEkgcmVhbGl6ZWQgaXQgZGlkbuKAmXQgbWF0dGVyIHRoYXQgZmFjdG9ycyBsaWtlIGh5cGVydGVuc2lvbiwgaHlwZXJsaXBpZGVtaWEsIG9yIGJlaW5nIGEgd2lkb3cgd2VyZW7igJl0IHJlbGF0ZWQgdG8gdGhlIGV4cG9zdXJlIG9yIG91dGNvbWUsIHdoYXQgbWF0dGVycyBpcyB0aGF0IHRoZXNlIGdyb3VwcyBhcmUgYXMgc2ltaWxhciBhcyB3ZSBjYW4gcG9zc2libHkgbWFrZSB0aGVtIHNvIHdlIGNhbiByZW1vdmUgKnNvbWUqIGhpZGRlbiBiaWFzLiBXaGVuIEkgd291bGQgZ28gZm9yIHJ1bnMgaW4gdGhlIG1vcm5pbmcsIEkgd291bGQgc3RhcnQgdGhpbmtpbmcgYWJvdXQgYWxsIG9mIHRoZSBvdGhlciBjb3ZhcmlhdGVzIEkgY291bGQgYWRkIChNRVBTIGlzIHRoZSBnaWZ0IHRoYXQga2VlcHMgb24gZ2l2aW5nIHdpdGggY292YXJpYXRlcykgYmVjYXVzZSB0aGVyZSBpcyBnb2luZyB0byBiZSBhbGwgc29ydHMgb2YgaGlkZGVuIGJpYXMgaW4gYSBub24tcmFuZG9taXplZCB0cmlhbCwgYnV0IHdlIGNhbiBoZWxwIGVsaW1pbmF0ZSBzb21lIG9mIHRoYXQgaWYgd2UgYmFsYW5jZSBwZW9wbGUgb24gKmFueSogYXZhaWxhYmxlICoqYmFzZWxpbmUqKiBjaGFyYWN0ZXJpc3RpY3MuIEkgc3RyZXNzICoqYmFzZWxpbmUqKiwgYmVjYXVzZSBhcyBJIGdvdCB1cCB0byA0MCBzb21ldGhpbmcgY292YXJpYXRlcywgdGhhdCB3YXMgYWxzbyBhIG1pc3Rha2UuIEkgd2FzIGluY2x1ZGluZyBjb3ZhcmlhdGVzIHRoYXQgd2VyZSBtZWFzdXJlZCBhdCB0aGUgKmVuZCogb2YgdGhlIGludGVydmlldyBwZXJpb2QsIHN1Y2ggYXMgZXhwZW5kaXR1cmUsIHdoaWNoIGlzIGluY29ycmVjdC4gTm90IG9ubHkgd2FzIHRoaXMgd3JvbmcsIHdoZW4gSSBkaWQgdGhpcywgbXkgT1IgY3Jvc3NlZCAxIChidXQgYWxzbyB0aGF0IHdhcyBiYWNrIHdoZW4gSSB3YXMgaW5jbHVkaW5nIGFsbCBSQSBwYXRpZW50cyByZWdhcmRsZXNzIG9mIHJ4IGZpbGxlZCkuIE15IGh1Z2UgbGVzc29uIGxlYXJuZWQgaXMgdGhhdCBhdCB0aGUgc3RhcnQgb2YgdGhlIHByb2plY3QsIHNlbGVjdCAqKmFsbCoqIG9mIHRoZSAqKmJhc2VsaW5lKiogY2hhcmFjdGVyaXN0aWNzIHRoYXQgYXJlIGF2YWlsYWJsZS4gVGhlbiwga2VlcCB0aGVtIGF0IHRoZWlyIG1vc3QgZ3JhbnVsYXIgZm9ybS4gT25seSBjb2xsYXBzZSB0aGVtIGlmIHRoZXJlIGFyZSBsZXNzIHRoYW4gMjAgcGVvcGxlIGluIGEgcGFydGljdWxhciBncm91cCBmb3IgYSBjZXJ0YWluIGxldmVsLiBUaGVuLCBldmFsdWF0ZSBtaXNzaW5nbmVzcy4gSWYgdGhlcmUgaXMgbW9yZSB0aGFuIGxpa2UgMjUlIG1pc3NpbmcsIGp1c3QgZHJvcCBpdCAoSSBvbmx5IGhhZCB0byBkbyB0aGlzIGZvciBmbHUgdmFjY2luZSB3aGljaCBoYWQgNTAlIG1pc3NpbmcpLiBMYXN0bHksIGF0IHRoZSB2ZXJ5IGVuZCBvZiB0aGUgcHJvamVjdCwgSSBsZWFybmVkIEkgbmVlZCB0byBhZGQgd2VpZ2h0cyBpbnRvIHRoZSBwcm9wZW5zaXR5IHNjb3Jlcy4gVGhpcyBpcyBhbm90aGVyIG5leHQgc3RlcC4gCgoJVGhpcmQsIEkgbGVhcm5lZCBob3cgbWFueSBkaWZmZXJlbnQgbWV0aG9kcyBmb3Igd2VpZ2h0aW5nIGFuZCBtYXRjaGluZyB0aGVyZSBhcmUuIEJlZm9yZSB0aGUgcHJvamVjdCwgSSBrbmV3IChmcm9tIGNsYXNzKSBhYm91dCAxOmsgbWF0Y2hpbmcgd2l0aCBhbmQgd2l0aG91dCByZXBsYWNlbWVudCBhbmQgVHdhbmcgYW5kIGludmVyc2UgUFMgd2VpZ2h0aW5nLiBJIHdhcyBmYW1pbGlhciB3aXRoIHRoZSBjb25jZXB0IG9mIGNhbGlwZXIsIGJ1dCBJIGRpZG7igJl0IHJlYWxseSB1bmRlcnN0YW5kIGl0IGFmdGVyIGxlYXJuaW5nIGFib3V0IGl0IGluIGNsYXNzLiBUaGlzIHByb2plY3QgYWxsb3dlZCBtZSB0byBwcmFjdGljZSBtYXRjaGluZyB3aXRoIGEgY2FsaXBlciBhbmQgc2VlIGl0cyBhZHZhbnRhZ2VzIChzdXBlciBzdHJvbmcgYmFsYW5jZSkgYW5kIGRpc2FkdmFudGFnZXMgKGRyb3BwaW5nIGhhcmQgdG8gbWF0Y2ggcGVvcGxlIGZyb20gdGhlIGV4cG9zZWQpLgoJCglGb3VydGgsIEkgbGVhcm5lZCBhIGxvdCBhYm91dCBkYXRhIGNsZWFuaW5nIGluIFIgYW5kIG1ha2luZyB3b3JrIG1vcmUgcmVwcm9kdWNpYmxlLiBBdCBmaXJzdCwgSSB3YXMgY2xlYW5pbmcgaW4gYm90aCBSIGFuZCBTQVMuIERyLiBMb3ZlIGV4cGxhaW5lZCB0byBtZSB0aGF0IGl0IHdhcyBmaW5lIGlmIEkgY2hvc2UgdG8gZG8gdGhhdCwgYnV0IHRoYXQgSSBuZWVkIHRvIGJlIGFibGUgdG8gZ28gYmFjayBhIGZldyB5ZWFycyBmcm9tIG5vdyBhbmQga25vdyBleGFjdGx5IHdoYXQgSSBkaWQuIEJ5IGhhdmluZyA0IGRpZmZlcmVudCBkb2N1bWVudHMgaW4gZGlmZmVyZW50IGNvZGluZyBsYW5ndWFnZXMgdGhhdCB3ZXJlIHNvIGRpc29yZ2FuaXplZCwgdGhlcmUgd2FzIG5vIHdheSB0aGF0IEkgd291bGQgYmUgYWJsZSB0byByZXByb2R1Y2Ugd2hhdCBJIGRpZC4gTm93IEkgYW0gZG93biB0byB0d28gZG9jdW1lbnRzIGFuZCB0aGV5IGFyZSBib3RoIGluIFIgYW5kIG11Y2ggYmV0dGVyIG9yZ2FuaXplZC4gCgogIExhc3RseSwgSSBhbHNvIGxlYXJuZWQgYSBsb3QgZnJvbSBvdGhlciBwZW9wbGXigJlzIHByZXNlbnRhdGlvbnMuIEkgbGlrZWQgaG93IFN0ZXBoYW5pZSBjb21wYXJlZCBoZXIgcmVzdWx0cyBmcm9tIGFsbCBvZiB0aGUgbWF0Y2hlcyBhbmQgdXNlZCBhIHBsb3Qgd2l0aCA5NSUgQ0kgdG8gZG8gc28uIFNoZSB3YXMga2luZCBlbm91Z2ggdG8gcHJvdmlkZSBtZSB3aXRoIHRoZSBjb2RlLCB3aGljaCBJIHdhcyBzbyBoYXBweSB0byBpbmNsdWRlIGluIG15IHByb2plY3QuIEFsdGhvdWdoIHRoaXMgbWlnaHQgc2VlbSBsaWtlIHNvbWV0aGluZyByZWFsbHkgc2ltcGxlLCBJIGtub3cgSSB3aWxsIGJlIGFibGUgdG8gdXNlIHRoYXQgY29kZSBpbiB0aGUgZnV0dXJlLiBBbXLigJlzIHByZXNlbnRhdGlvbiB1c2luZyBhIG1hdGNoaW5nIG1ldGhvZCB0aGF0IG1hdGNoZWQgKnVwIHRvKiAyIGNvbnRyb2xzIGZvciBlYWNoIGV4cG9zZWQgd2FzIGEgZ3JlYXQgbGVhcm5pbmcgZXhwZXJpZW5jZSBmb3IgbWUuIEkgcmVjZW50bHkgcmVhZCBhIEpBTUEgcGFwZXIgdGhhdCB1c2VkIEhDVVAtTklTIGRhdGEgZm9yIFBTIG1hdGNoaW5nIGFuZCBJICp0aG91Z2h0KiB0aGF0IGl0IHdhcyByZWFsbHkgc3Ryb25nIHRoYXQgdGhleSBkaWQgKnVwIHRvKiB0d28gbWF0Y2hlczsgbGlrZSB0aGF0IHRoZWlyIGFwcHJvYWNoIHdhcyBzdXBlcmlvciB0byBtaW5lIGJlY2F1c2UgdGhleSBjb3VsZCBoYXZlIG1vcmUgY29udHJvbHMuIEhvd2V2ZXIsIGFzIHlvdSBleHBsYWluZWQsIGl0IGlzIG1pc2xlYWRpbmcgYXMgdGhleSByZWFsbHkgZG8gbm90IGhhdmUgdHdvIG1hdGNoZXMgZm9yIGV2ZXJ5IHRyZWF0ZWQuIEFuZCBpZiB5b3Ugd2FudCBzb21ldGhpbmcgc2ltaWxhciwgdG8ganVzdCBkbyB3ZWlnaHRpbmcuIE5vdyB0aGF0IEkgaGF2ZSBsZWFybmVkIHRoYXQgd2VpZ2h0aW5nIGlzIGFjdHVhbGx5IGJldHRlciB0aGFuIHRoYXQgbWV0aG9kLCBJIHdpbGwgbm90IHRyeSB0byBlbXVsYXRlIHRoYXQgYXBwcm9hY2guICBGaW5hbGx5LCB3aGlsZSBJIHdhcyBwcmVzZW50aW5nLCBJIGxlYXJuZWQgYWJvdXQgd2hhdCBoYXBwZW5zIHdoZW4gdGhlIFBTIGlzIHJlYWxseSBjbG9zZSB0byAwIG9yIDEuIEkgbGVhcm5lZCB0aGF0IHlvdSBzaG91bGQgZ28gYmFjayBhbmQgZmluZCBvdXQgd2hpY2ggY292YXJpYXRlcyBtaWdodCBiZSB0aGUgY3VscHJpdCBvZiB0aGlzLiBCdXQgZnVydGhlcm1vcmUsIEkgbGVhcm5lZCB0aGF0IEkgY2FuIHNvbHZlIHRoYXQgd2hlbiBJIGluY2x1ZGUgbW9yZSBjb250cm9scyBhbmQgcmVtb3ZlIHN1YmplY3RzIHdobyBkbyBub3QgaGF2ZSBQUyBjbG9zZSB0byB0aGUgbG93ZXIgZW5kIG9mIHRoZSBQUyByYW5nZSBvZiBteSBleHBvc2VkIGdyb3VwLiAgCgpUbyBjb21wbGV0ZSB0aGlzIHByb2plY3QgdG8gbXkgc2F0aXNmYWN0aW9uIEkgd2lsbDoKCjEuCUluY2x1ZGUgd2VpZ2h0cyBpbiB0aGUgUFMgYW5hbHlzaXMuIFRoZXJlIHdlcmUgdHdvIGRpZmZlcmVudCB3ZWlnaHRzIHRvIGNob29zZSBmcm9tIGFuZCBJIGNvbmZpcm1lZCB3aXRoIHR3byBvZiB0aGUgcGVvcGxlIGZyb20gTUVQUyB3aGljaCBzdXJ2ZXkgd2VpZ2h0IEkgd2lsbCBiZSB1c2luZy4gQWNjb3JkaW5nIHRvIHRoZWlyIGluc3RydWN0aW9ucywgSSB3aWxsIGFkZCB0aGUgd2VpZ2h0cyB0b2dldGhlciBmcm9tIGJvdGggeWVhcnMgYW5kIHRoZW4gZGl2aWRlIGJ5IHR3bwoyLglFeGNsdWRlIHBlb3BsZSB3aXRoIG90aGVyIGRpc2Vhc2Ugc3RhdGVzIHRoYXQgdXNlIHRoZSBzYW1lIG1lZGljYXRpb25zIGZvciBSQSAoZWcgbWV0aG90cmV4YXRlLCBodW1pcmEpLiBUaGlzIHdpbGwgcmVxdWlyZSBtZSB0byBnbyBpbnRvIHRoZSBwcmVzY3JpcHRpb24gZHJ1ZyBmaWxlLiAKMy4JQWZ0ZXIgcmVmaW5pbmcgdGhlIGVsaWdpYmlsaXR5LCBJIHdvdWxkIGxpa2UgdG8gaW5jbHVkZSB0aGUgd2hvbGUgYXZhaWxhYmxlIChhbmQgZWxpZ2libGUpIHNhbXBsZSBvZiBjb250cm9scyB0byBtYWtlIHRoZSBwb3B1bGF0aW9uIGFzIGxhcmdlIGFzIHBvc3NpYmxlIChUaGVyZSBhcmUgYWJvdXQgMzBLIHBvdGVudGlhbCBjb250cm9scyB0byBpbmNsdWRlLCBidXQgdGhpcyBudW1iZXIgd2lsbCBkZWNyZWFzZSBhZnRlciBJIGhhdmUgcmVmaW5lZCB0aGUgZWxpZ2liaWxpdHkpCjQuCUFzIEkgd2FzIGV4cGxvcmluZyB3aGljaCB3ZWlnaHQgdmFyaWFibGUgd291bGQgYmUgYXBwcm9wcmlhdGUsIEkgZm91bmQgYW5vdGhlciBjb3ZhcmlhdGUgSSB3YW50ZWQgdG8gaW5jbHVkZTogd2hldGhlciBzb21lb25lIGhhcyByZWNlaXZlZCBmb29kIHN0YW1wcy4gVGhpcyBzbGlnaHRseSBzY2FyZXMgbWUgdGhhdCBJIGFtIHN0aWxsIGZpbmRpbmcgY292YXJpYXRlcy4gSSB0aG91Z2h0IEkgaGFkIGRvbmUgYSB2ZXJ5IHRob3JvdWdoIGpvYiBsb29raW5nIHRocm91Z2ggY292YXJpYXRlcyAoSSB3ZW50IGJhY2sgYWJvdXQgNiB0aW1lcyBhbmQgYWRkZWQgbW9yZSwgYW5kIEkgcmVhbGx5IHRob3VnaHQgdGhlIGxhc3QgdGltZSBJIGhhZCBleGhhdXN0ZWQgdGhlIGxpc3QpLCBidXQgY2xlYXJseSwgSSBuZWVkIHRvIGRvIHRoYXQgYWdhaW4uICAKNS4JU29tZW9uZSBoYWQgc3VnZ2VzdGVkIHRoYXQgSSBsb29rZWQgYXQgb3RoZXIgZGlhZ25vc2lzIGNvZGVzIGZvciBteSBvdXRjb21lLiBJIGFncmVlZCB3aXRoIHRoYXQgYXMgSjE4IGlzIG5vdCB0aGUgb25seSBkaWFnbm9zaXMgY29kZSBmb3IgcG5ldW1vbmlhLiBUaGlzIHdhcyB0aGUgb25seSBvbmUgdGhhdCB0aGV5IHNlZW1lZCB0byB1c2UgdGhvdWdoIG91dCBvZiB0aGUgbGlzdCBvZiBwbmV1bW9uaWEgY29kZXMgKHBuZXVtb25pYSBkdWUgdG8gb3RoZXIgY2F1c2VzKSBhcyB0aGV5IGRvIG5vdCBzcGVjaWZ5IHRoZSBiYWN0ZXJpYWwgb3JnYW5pc20gaW52b2x2ZWQgKGVnIHRoZXkgZG8gbm90IHNheSB3aXRoZXIgaXQgd2FzIHN0cmVwdG9jb2NjYWwgcG5ldW1vbmlhIG9yIG15Y29wbGFzbWEgcG5ldW1vbmlhIHdoaWNoIGhhdmUgZGlmZmVyZW50IGNvZGVzKS4gTm90IHN1cmUgd2h5IEkgYW0gbGlzdGluZyB0aGlzIGFzIHNvbWV0aGluZyBJIGFtIG5vdCBzYXRpc2ZpZWQgd2l0aCBhbmQgY291bGQgY2hhbmdlLCBidXQgSSBkbyBuZWVkIHRvIGJyaW5nIHRoYXQgbGltaXRhdGlvbiB1cC4gCjYuCUkgd291bGQgbGlrZSB0byB0cnkgbWF0Y2hpdCBhbmQgd2VpZ2h0aXQuIAo=