Project B - Study 2

Quynh Nguyen

2022-12-17

1 Setup and Data Ingest

1.1 Initial Setup and Package Loads

1.2 Loading the Raw Data into R

For this study, I would like to use some datasets that I obtained from the National Health and Nutrition Examinaition Survey (NHANES) by National Center for Health Statistics by using nhanesA package. I would like to use the following datasets:

  • Blood Pressure - Oscillometric Measurement (P_BPXO)
  • Smoking - Cigarette Use (P_SMQ)
  • Weight History (P_WHQ)
  • Income (P_INQ)
  • Sleep Disorder (P_SLQ)
  • Diabetes (P_DIQ)
  • Blood Pressure & Cholesterol (P_BPQ)
library(nhanesA)

#Blood pressure measurement
bp_raw <- nhanes('P_BPXO') |> tibble()
saveRDS(bp_raw, "BPX_J.Rds")
bp_raw <- readRDS("BPX_J.Rds")

# Smoking 
smoke_raw <- nhanes('P_SMQ') |> tibble()
saveRDS(smoke_raw, "P_SMQ.Rds")
smoke_raw <- readRDS("P_SMQ.Rds")

# Weight History
weight_raw <- nhanes('P_WHQ') |> tibble()
saveRDS(weight_raw, "P_WHQ.Rds")
weight_raw <- readRDS("P_WHQ.Rds")

# Income
income_raw <- nhanes('P_INQ') |> tibble()
saveRDS(income_raw, "P_INQ.Rds")
income_raw <- readRDS("P_INQ.Rds")

# Sleep Disorder
sleep_raw <- nhanes('P_SLQ') |> tibble()
saveRDS(sleep_raw, "P_SLQ.Rds")
sleep_raw <- readRDS("P_SLQ.Rds")

# Diabetes
diabetes_raw <- nhanes('P_DIQ') |> tibble()
saveRDS(diabetes_raw, "P_DIQ.Rds")
diabetes_raw <- readRDS("P_DIQ.Rds")

# Cholesterol level 
chol_raw <- nhanes('P_BPQ') |> tibble()
saveRDS(chol_raw, "P_BPQ.Rds")
chol_raw <- readRDS("P_BPQ.Rds")

1.3 Content of the Raw Tibbles

  • For the first tible, bp_raw, it contains 12 variables with 11656 rows, including the unique identifier, i.e SEQN as respondent sequence, which is unique for each patient for each participant.
dim(bp_raw)
[1] 11656    12
  • For smoke_raw, it contains 16 variables with 11137 rows, including the unique identifier, i.e SEQN as respondent sequence, which is unique for each patient for each participant.
dim(smoke_raw)
[1] 11137    16
  • For weight_raw, it contains 35 vairables with 10195 rows, including the unique identifier, i.e SEQN as respondent sequence, which is unique for each patient for each participant.
dim(weight_raw)
[1] 10195    35
  • For income_raw, it contains 3 variables with 15560 rows,including the unique identifier, i.e SEQN as respondent sequence, which is unique for each patient for each participant.
dim(income_raw)
[1] 15560     3
  • For sleep_raw, it contains 11 variables with 10195 rows, including the unique identifier, i.e SEQN as respondent sequence, which is unique for each patient for each participant.
dim(sleep_raw)
[1] 10195    11
  • For diabetes_raw, it contains 28 variables with 14986 rows, including the unique identifier, i.e SEQN as respondent sequence, which is unique for each patient for each participant.
dim(diabetes_raw)
[1] 14986    28
  • For chol_raw, it contains 11 variables with 10195 rows, including the unique identifier, i.e SEQN as respondent sequence, which is unique for each patient for each participant.
dim(chol_raw)
[1] 10195    11

1.4 Merging Steps

I would like to joining columns of these tables togehter by using inner join on SEQN. It should contains 110 columns after the merging step.

df_raw <- inner_join(bp_raw, smoke_raw, by = "SEQN")
df_raw <- inner_join(df_raw, weight_raw, by = "SEQN")
df_raw <- inner_join(df_raw, sleep_raw, by = "SEQN")
df_raw <- inner_join(df_raw, diabetes_raw, by = "SEQN")
df_raw <- inner_join(df_raw, income_raw, by = "SEQN")
df_raw <- inner_join(df_raw, chol_raw, by = "SEQN")
dim(df_raw)
[1] 9445  110

1.5 Selecting Variables:

I would like to use these variables for my study:

  • SEQN as the respondent sequence, which is unique for each patient.
  • BPXOSY1, BPXOSY2, BPXOSY3 determine the 1st, 2nd, and third systolic value of oscillometric reading, respectively, orignially from the Blood Pressure - Oscillometric Measurement (P_BPXO) tibble (bp_raw).
  • BPXODI1, BPXODI2, BPXODI3 determine the 1st, 2nd, and third systolic value of oscillometric reading, respectively, originally from the Blood Pressure - Oscillometric Measurement (P_BPXO) or (bp_raw) tibble.
  • SMQ020 from Smoking - Cigarette Use (P_SMQ) or smoke_raw tibble as if the participant smoke about 100 cigarettes in their lives.
  • WHD010 from Weight History (P_WHQ) or weight_raw tibble as the self-report height of the participants.
  • WHD0120 from Weight History (P_WHQ) or weight_raw tibble as the self-report weight of the participants.
  • INDFMMPC from Income (P_INQ) or income_raw tibble as family monthly poverty level category
  • DIQ010 from Diabetes (P_DIQ) or diabetes_raw tibble as if the person was told by doctor that they have diabetes.
  • SLD012 from Sleep Disorder (P_SLQ) or sleep_raw tibble as hours of sleep during weekdays of the participants
  • SLD013 from Sleep Disorder (P_SLQ) or sleep_raw tibble as hours of sleep during weekends of the participants
  • BPQ080 from Blood Pressure & Cholesterol (P_BPQ) or chol_raw tibble as being told to have high cholesterol level
df <- df_raw[, c("SEQN", "BPXOSY1", "BPXOSY2", "BPXOSY3", "BPXODI1", "BPXODI2", "BPXODI3", "SMQ020", 'WHD020', 'WHD010', 'INDFMMPC', 'DIQ010', 'SLD012', 'SLD013', 'BPQ080')]

2 Cleaning the Data

2.1 Pre-processing Data

I would like to check the missing values from my current tibble.

miss_var_summary(df) |> kable()
variable n_miss pct_miss
BPXOSY3 1021 10.8099524
BPXODI3 1021 10.8099524
BPXOSY2 999 10.5770249
BPXODI2 999 10.5770249
BPXOSY1 988 10.4605611
BPXODI1 988 10.4605611
INDFMMPC 755 7.9936474
SMQ020 480 5.0820540
SLD013 85 0.8999471
SLD012 81 0.8575966
WHD010 52 0.5505558
SEQN 0 0.0000000
WHD020 0 0.0000000
DIQ010 0 0.0000000
BPQ080 0 0.0000000

The missing percentage for each variable is quite small. Therefore, I would like to pre-process my data by filtering out those missing value, don'know value, and refused value. These values are specified in each variable where it originally come from.

This following snippet would to filter out those missing values.

df <- df[complete.cases(df),]

Then, I would like to filter out the value 7777 and 9999 from WHD010 and WHD020. These values are stated for the refused (7777) and don’t know (9999) value.

df <- df[df$WHD010 < 7777,]
df <- df[df$WHD020 < 7777,]

I would like to filter out values 7 and 9 from DIQ0101. These values are stated for the refused (7) and don’t know (9) value.

df <- df[df$DIQ010 < 7,]

I would like to filter out values 7 and 9 from SMQ020. These values are stated for the refused (7) and don’t know (9) value.

df <-df[df$SMQ020 < 7,]

I would like to filter out values 7 and 9 from INDFMMPC. These values are stated for the refused (7) and don’t know (9) value.

df <- df[df$INDFMMPC < 7,]

Finally, I would like to filter out values 7 and 9 from BPQ080. These values are stated for the refused (7) and don’t know (9) value.

df <- df[df$BPQ080 < 7,]

After all, this is my current tibble with 6837 rows and 15 columns.

dim(df)
[1] 6837   15

2.2 Checking Quantitative Variables

df %>% select(BPXOSY1, BPXOSY2, BPXOSY3, BPXODI1, BPXODI2, BPXODI3, WHD010, WHD020, SLD012, SLD013) %>% mosaic::inspect() 
Registered S3 method overwritten by 'mosaic':
  method                           from   
  fortify.SpatialPolygonsDataFrame ggplot2

quantitative variables:  
      name   class min    Q1 median    Q3 max       mean        sd    n missing
1  BPXOSY1 numeric  66 110.0  121.0 135.0 220 124.049144 19.518912 6837       0
2  BPXOSY2 numeric  54 110.0  121.0 134.0 222 123.870265 19.438437 6837       0
3  BPXOSY3 numeric  60 110.0  121.0 134.0 220 123.798157 19.260963 6837       0
4  BPXODI1 numeric  36  67.0   74.0  82.0 141  74.751645 11.755247 6837       0
5  BPXODI2 numeric  31  66.0   73.0  81.0 146  74.220711 11.769410 6837       0
6  BPXODI3 numeric  36  66.0   73.0  81.0 145  73.931110 11.833857 6837       0
7   WHD010 numeric  49  63.0   66.0  70.0  82  66.467018  4.160892 6837       0
8   WHD020 numeric  80 150.0  175.0 210.0 457 183.057335 48.703957 6837       0
9   SLD012 numeric   2   6.5    7.5   8.5  14   7.571449  1.649106 6837       0
10  SLD013 numeric   2   7.0    8.0   9.0  14   8.256180  1.786721 6837       0
glimpse(df)
Rows: 6,837
Columns: 15
$ SEQN     <dbl> 109266, 109271, 109273, 109274, 109282, 109290, 109292, 10929…
$ BPXOSY1  <dbl> 99, 102, 116, 138, 141, 126, 143, 126, 158, 105, 94, 155, 103…
$ BPXOSY2  <dbl> 99, 108, 110, 132, 137, 116, 138, 130, 161, 105, 91, 165, 104…
$ BPXOSY3  <dbl> 99, 111, 115, 132, 140, 122, 133, 130, 158, 102, 92, 167, 96,…
$ BPXODI1  <dbl> 56, 65, 68, 70, 77, 62, 96, 83, 96, 72, 51, 95, 65, 77, 71, 6…
$ BPXODI2  <dbl> 55, 68, 66, 69, 71, 60, 98, 84, 92, 69, 51, 92, 66, 73, 67, 6…
$ BPXODI3  <dbl> 52, 68, 68, 71, 70, 59, 97, 84, 92, 68, 49, 95, 63, 75, 67, 6…
$ SMQ020   <dbl> 2, 1, 1, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2…
$ WHD020   <dbl> 210, 222, 165, 219, 185, 155, 185, 230, 137, 120, 205, 130, 1…
$ WHD010   <dbl> 64, 72, 72, 75, 70, 63, 66, 70, 63, 60, 66, 59, 62, 69, 66, 6…
$ INDFMMPC <dbl> 3, 1, 1, 1, 3, 3, 2, 1, 3, 3, 2, 3, 1, 3, 3, 3, 3, 1, 1, 3, 1…
$ DIQ010   <dbl> 2, 2, 2, 1, 2, 1, 1, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2…
$ SLD012   <dbl> 7.5, 10.0, 6.5, 9.5, 7.0, 4.0, 4.5, 7.5, 7.0, 8.0, 6.0, 4.0, …
$ SLD013   <dbl> 8.0, 13.0, 8.0, 9.5, 8.0, 4.0, 4.5, 6.5, 7.0, 8.0, 7.0, 14.0,…
$ BPQ080   <dbl> 1, 1, 2, 1, 1, 2, 1, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2…

2.3 Checking our Quantitative Variables

In this study, I have 10 quantitative variables.I want to check the range for each of them to ensure there are no abnormal value.

df |>
  select(BPXOSY1, BPXOSY2, BPXOSY3, BPXODI1, BPXODI2, BPXODI3, WHD010, WHD020, SLD012, SLD013) |>
  mosaic::inspect()

quantitative variables:  
      name   class min    Q1 median    Q3 max       mean        sd    n missing
1  BPXOSY1 numeric  66 110.0  121.0 135.0 220 124.049144 19.518912 6837       0
2  BPXOSY2 numeric  54 110.0  121.0 134.0 222 123.870265 19.438437 6837       0
3  BPXOSY3 numeric  60 110.0  121.0 134.0 220 123.798157 19.260963 6837       0
4  BPXODI1 numeric  36  67.0   74.0  82.0 141  74.751645 11.755247 6837       0
5  BPXODI2 numeric  31  66.0   73.0  81.0 146  74.220711 11.769410 6837       0
6  BPXODI3 numeric  36  66.0   73.0  81.0 145  73.931110 11.833857 6837       0
7   WHD010 numeric  49  63.0   66.0  70.0  82  66.467018  4.160892 6837       0
8   WHD020 numeric  80 150.0  175.0 210.0 457 183.057335 48.703957 6837       0
9   SLD012 numeric   2   6.5    7.5   8.5  14   7.571449  1.649106 6837       0
10  SLD013 numeric   2   7.0    8.0   9.0  14   8.256180  1.786721 6837       0

We can see that the range for each variable regarding to each meaning are quite reasonable. Therefore, I would like to proceed into next step.

###Processing Height and Weight Value to Calculate the Body Mass Index (BMI)

In this study,instead of using weight and height, I would like to use them for calculating BMI. The BMI formula for inches and pounds is available at http://www.bmi-calculator.net/bmi-formula.php. Normally, the normal range for BMI would be reasonable from 15 to 50. However, since NHANES’s purpose was to collect data prevalence of chronic conditions in the population in the U.S. Due to this, the range can be go over or under the normal reasonable range. Therefore, getting values of BMI above 50 or slightly under 15 as below would be considered as acceptable. However, in order to confirm it, I would like to check the range value for the height and weight.

df['BMI'] <-df$WHD020*703/(df$WHD010)**2
describe(~ BMI, data = df)
BMI 

 1  Variables      6837  Observations
--------------------------------------------------------------------------------
BMI 
       n  missing distinct     Info     Mean      Gmd      .05      .10 
    6837        0     2056        1    29.05    7.521    20.17    21.45 
     .25      .50      .75      .90      .95 
   24.21    27.81    32.48    38.17    42.28 

lowest : 14.63059 14.74719 14.87748 14.99636 15.10352
highest: 70.47227 72.25278 72.62397 74.87574 76.87125
--------------------------------------------------------------------------------

As we can see in here, the range of weight are quite big as there are people go up to 457 lbs. The tallest person also have the height of 82 inches (6’ 8’’). These are quite rare in normal, but if we consider as sampling people in the population, there would be some exceptional cases. Therefore, I would leave these values to be intact.

df|>
  select(WHD020, WHD010) |>
  describe()
select(df, WHD020, WHD010) 

 2  Variables      6837  Observations
--------------------------------------------------------------------------------
WHD020 
       n  missing distinct     Info     Mean      Gmd      .05      .10 
    6837        0      265        1    183.1       53    118.0    128.0 
     .25      .50      .75      .90      .95 
   150.0    175.0    210.0    248.0    271.2 

lowest :  80  82  86  88  89, highest: 415 416 434 450 457
--------------------------------------------------------------------------------
WHD010 
       n  missing distinct     Info     Mean      Gmd      .05      .10 
    6837        0       31    0.995    66.47     4.72       60       61 
     .25      .50      .75      .90      .95 
      63       66       70       72       73 

lowest : 49 50 53 54 55, highest: 77 78 79 81 82
--------------------------------------------------------------------------------

2.3.1 Average the Systolic and Diastolic Values:

Instead of getting 3 values for each systolic and diastolic reading, I would like to average them out.

df['systolic'] <- as.numeric(format(round(rowMeans(df[,c("BPXOSY3","BPXOSY2","BPXOSY1")]), 1), nsmall = 1))
df['diastolic'] <- as.numeric(format(round(rowMeans(df[,c("BPXODI1","BPXODI2","BPXODI3")]), 1), nsmall = 1))

Then, I would like to see their range. The normal people should have systolic/diastolic as 90/60. However, for some people who have abnormal blood pressure (i.e hypotension, high blood pressure, hypertension), the range can be out of scope. As there are cases that people with hypertension crisis can have systolic to be over 200/diastolic over 120 or hypotension with systolic to be under 90/diastolic to be under 60 (but over 40), the range describes below are reasonable to be keep intact.

df |>
  select(systolic, diastolic) |>
  describe()
select(df, systolic, diastolic) 

 2  Variables      6837  Observations
--------------------------------------------------------------------------------
systolic 
       n  missing distinct     Info     Mean      Gmd      .05      .10 
    6837        0      344        1    123.9    20.91     98.3    102.7 
     .25      .50      .75      .90      .95 
   110.0    121.0    134.3    149.7    160.3 

lowest :  78.7  79.7  80.0  80.3  82.7, highest: 210.3 213.3 214.0 217.0 218.7
--------------------------------------------------------------------------------
diastolic 
       n  missing distinct     Info     Mean      Gmd      .05      .10 
    6837        0      229        1     74.3    12.78     57.3     60.3 
     .25      .50      .75      .90      .95 
    66.3     73.3     81.3     89.0     94.0 

lowest :  41.3  41.7  42.0  43.0  43.7, highest: 126.0 128.0 135.0 137.7 143.7
--------------------------------------------------------------------------------

2.3.2 Average Hours of Sleep

Instead of having hours of sleep during the weekdays and weekends separately, I would like to keep one value by average these two hours.

df['SleepHours'] <- as.numeric(format(round(rowMeans(df[,c('SLD012', 'SLD013')]), 1), nsmall = 1))

Then, I would like to check the range of this values. In the original study, the hours of sleep going from 2 to 14. Then, the average range, if normal, should be within this scope.

df |>
  select(SleepHours) |>
  describe()
select(df, SleepHours) 

 1  Variables      6837  Observations
--------------------------------------------------------------------------------
SleepHours 
       n  missing distinct     Info     Mean      Gmd      .05      .10 
    6837        0       47    0.994    7.914    1.653      5.5      6.0 
     .25      .50      .75      .90      .95 
     7.0      8.0      9.0      9.8     10.5 

lowest :  2.0  2.5  2.8  3.0  3.2, highest: 12.8 13.0 13.5 13.8 14.0
--------------------------------------------------------------------------------

2.4 Checking Binary Variables

My binary vairables including SMQ020 and BPQ080 ### For SMQ020 Variables

df |> select(SMQ020) |> glimpse()
Rows: 6,837
Columns: 1
$ SMQ020 <dbl> 2, 1, 1, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, …

From the original tibble, 1 denotes Yes, and 2 denotes No. Therefore, I would like to change it into Yes/No value and factor it.

df <- df %>% mutate(smoking = fct_recode(factor(SMQ020), "Yes" = "1", "No" = "2"))
df  |> select(smoking) |> summary()
 smoking   
 Yes:2783  
 No :4054  

There are no values that out of range.

2.4.1 For BPQ080 Variables

df |> select(BPQ080) |> glimpse()
Rows: 6,837
Columns: 1
$ BPQ080 <dbl> 1, 1, 2, 1, 1, 2, 1, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, …

From the original tibble, 1 denotes Yes, and 2 denotes No. Therefore, I would like to change it into Yes/No value and factor it.

df <- df %>% mutate(highchol = fct_recode(factor(BPQ080), "Yes" = "1", "No" = "2"))
df  |> select(highchol) |> summary()
 highchol  
 Yes:2409  
 No :4428  

There are no values that out of range.

2.5 Checking Multi-Category Variables

I have two multi-category variables: INDFMMPC, and DIQ010. I will first check them if they have any surprising values and they will rename and factor it to mirror what we need in our analyses.

2.6 The INDFMMPC vairable

First, I would take a look at this variable.

df |> tabyl(INDFMMPC) |> kable()
INDFMMPC n percent
1 2141 0.3131490
2 1051 0.1537224
3 3645 0.5331286

For this variable, I would like to create a new factor called eco_statuswhich is a factor which has specially level names: 1 as Lower,2asMiddle, and3forUpper`.

df <- df %>% mutate(eco_status = fct_recode(factor(INDFMMPC), "Lower" = "1", "Middle" = "2", "Upper" = "3"))

After that, I would check if I factor them correctly.

df |> count(INDFMMPC, eco_status) |> kable()
INDFMMPC eco_status n
1 Lower 2141
2 Middle 1051
3 Upper 3645

2.7 The DIQ010 Vairable

First, I would take a look at this variable.

df |> tabyl(DIQ010) |> kable()
DIQ010 n percent
1 986 0.1442153
2 5664 0.8284335
3 187 0.0273512

For this variable, I would like to create a new factor called diabeteswhich is a factor which has specially level names: 1 as Yes, 2 as No, and 3 for Borderline.

df <- df %>% mutate(diabetes = fct_recode(factor(DIQ010), "Yes" = "1", "No" = "2", "Borderline" = "3"))

After that, I would check if I factor them correctly.

df |> count(DIQ010, diabetes) |> kable()
DIQ010 diabetes n
1 Yes 986
2 No 5664
3 Borderline 187

2.8 Creating Analytic Tibles

So our analytic tibble, which I will call as df should contains 7 values after processing.

df <- df %>% select(SEQN,systolic, diastolic, BMI, SleepHours, smoking, eco_status, diabetes, highchol)

2.9 List of Missing Values

As I already filter those don’t know/missing values above, our analytic tibble should not contain any of these values

miss_var_summary(df) |> kable()
variable n_miss pct_miss
SEQN 0 0
systolic 0 0
diastolic 0 0
BMI 0 0
SleepHours 0 0
smoking 0 0
eco_status 0 0
diabetes 0 0
highchol 0 0

3 Codebook and Data Description

3.1 Codebook

The 9 variables of my analytic tibble would be described below. The Type column indicates the number of levels in each categorical (factor) variable. As for the Type information, I’m using Quant to indicate quantitative variables, and Cat-x indicates a categorical variable (factor) with x levels.

Variable Type Description/Levels
SEQN id respondent sequence, unique for each participants.
systolic Quant outcome variable, Average of systolic readings
diastolic Quant Average of diastolic readings
BMI Quant key predictor Body Mass Index
SleepHours Quant Average sleeping hours a week
smoking Cat - 2 Yes, No: Did you smoke 100 cigarettes in life?
eco_status Cat - 3 Lower Class, Middle Class, Upper Class:
diabetes Cat - 3 Yes, No, Borderline: Have doctor told you that you have diabetes?
highchol Cat - 2 Yes, No: Have doctor told you that you have high cholesterol?

3.2 Analytic Tible

df
# A tibble: 6,837 × 9
     SEQN systolic diastolic   BMI SleepHours smoking eco_status diabe…¹ highc…²
    <dbl>    <dbl>     <dbl> <dbl>      <dbl> <fct>   <fct>      <fct>   <fct>  
 1 109266      99       54.3  36.0        7.8 No      Upper      No      Yes    
 2 109271     107       67    30.1       11.5 Yes     Lower      No      Yes    
 3 109273     114.      67.3  22.4        7.2 Yes     Lower      No      No     
 4 109274     134       70    27.4        9.5 No      Lower      Yes     Yes    
 5 109282     139.      72.7  26.5        7.5 Yes     Upper      No      Yes    
 6 109290     121.      60.3  27.5        4   No      Upper      Yes     No     
 7 109292     138       97    29.9        4.5 No      Middle     Yes     Yes    
 8 109293     129.      83.7  33.0        7   No      Lower      No      No     
 9 109295     159       93.3  24.3        7   No      Upper      No      No     
10 109297     104       69.7  23.4        8   No      Upper      No      No     
# … with 6,827 more rows, and abbreviated variable names ¹​diabetes, ²​highchol
is_tibble(df)
[1] TRUE

3.3 Data Summary

Hmisc::describe(df |> select(-SEQN))
select(df, -SEQN) 

 8  Variables      6837  Observations
--------------------------------------------------------------------------------
systolic 
       n  missing distinct     Info     Mean      Gmd      .05      .10 
    6837        0      344        1    123.9    20.91     98.3    102.7 
     .25      .50      .75      .90      .95 
   110.0    121.0    134.3    149.7    160.3 

lowest :  78.7  79.7  80.0  80.3  82.7, highest: 210.3 213.3 214.0 217.0 218.7
--------------------------------------------------------------------------------
diastolic 
       n  missing distinct     Info     Mean      Gmd      .05      .10 
    6837        0      229        1     74.3    12.78     57.3     60.3 
     .25      .50      .75      .90      .95 
    66.3     73.3     81.3     89.0     94.0 

lowest :  41.3  41.7  42.0  43.0  43.7, highest: 126.0 128.0 135.0 137.7 143.7
--------------------------------------------------------------------------------
BMI 
       n  missing distinct     Info     Mean      Gmd      .05      .10 
    6837        0     2056        1    29.05    7.521    20.17    21.45 
     .25      .50      .75      .90      .95 
   24.21    27.81    32.48    38.17    42.28 

lowest : 14.63059 14.74719 14.87748 14.99636 15.10352
highest: 70.47227 72.25278 72.62397 74.87574 76.87125
--------------------------------------------------------------------------------
SleepHours 
       n  missing distinct     Info     Mean      Gmd      .05      .10 
    6837        0       47    0.994    7.914    1.653      5.5      6.0 
     .25      .50      .75      .90      .95 
     7.0      8.0      9.0      9.8     10.5 

lowest :  2.0  2.5  2.8  3.0  3.2, highest: 12.8 13.0 13.5 13.8 14.0
--------------------------------------------------------------------------------
smoking 
       n  missing distinct 
    6837        0        2 
                      
Value        Yes    No
Frequency   2783  4054
Proportion 0.407 0.593
--------------------------------------------------------------------------------
eco_status 
       n  missing distinct 
    6837        0        3 
                               
Value       Lower Middle  Upper
Frequency    2141   1051   3645
Proportion  0.313  0.154  0.533
--------------------------------------------------------------------------------
diabetes 
       n  missing distinct 
    6837        0        3 
                                           
Value             Yes         No Borderline
Frequency         986       5664        187
Proportion      0.144      0.828      0.027
--------------------------------------------------------------------------------
highchol 
       n  missing distinct 
    6837        0        2 
                      
Value        Yes    No
Frequency   2409  4428
Proportion 0.352 0.648
--------------------------------------------------------------------------------

Since I already filter out all missing values and other noisy ones, it should return no missing values for each variable.

4 My Research Question

From Center of Disease Control and Prevention (CDC), nearly half of adults in the U.S (47% or 116 million people) have hypertension, which is defined as systolic blood pressure greater than 130 mmHg or diastolic blood pressure greater than 80 mmHg. Having hypertension puts you at risk for heart disease and stroke, which are top 10 of leading causes of death in the U.S.

There are various of factors that can lead to high blood pressure, such as age, race, family history, physical activities, weight, smoking, stress, certain chronic condition, sleep disorder. Therefore, in this study, I would like to choose serveral risk factors in order to investigate about blood pressure, which includes: weight, smoke (cigarettes), income, sleep disorder, and diabetes from the dataset from National Center of Health Statistics during 2017 to 2020 before the pandemic.

In addition, in reality, to address one’s blood pressure, the heatlhcare professionals would use both systolic and diastolic readings. However, since I can only use one outcome, I would like to use systolic over diastolic as outcome for my study. Both of these values are equally important, however, there are some study showed that people who have higher risk of heart attack and stroke would likely to have higher systolic readings. in addition, for people who are still in the elevated state of blood pressure, they have higher systolic readings not diastolic reading comparing to normal person. Due to this, having systolic would be more useful and more noticeable in our study.

This leads to the research of my study:

How effectively can we predict the high blood pressure via systolic reading using BMI, and is the quality of prediction meaningfully when I adjust for other predictors (sleep hours, diabetes, income, smoking, and cholesterol) based on my data?

5 Paritioning the Data

Here, I will obtain a training sample with a randomly selected 70% of the data, and have the remaining 30% in a test sample, properly labeled, and using set.seed so that the results can be replicated later.

I will call the training sample df_training and the test sample df_test.

  • The slice_sample function will sample the specified proportion of the data.
  • The anti_join function returns all rows in the first data frame (here specified as df_analytic) that are not in the second data frame (here specified as df_training) as assessed by the row-specific identification code (here SEQN)).
set.seed(42)
df_analytic <- df 
df_training <- df_analytic |> slice_sample(prop = .70)
df_test <- anti_join(df_analytic, df_training, by = "SEQN")
dim(df_analytic)
[1] 6837    9
dim(df_training)
[1] 4785    9
dim(df_test)
[1] 2052    9

Since 4785 + 2052 = 6837, we should be fine. # Transforming the Outcome

5.1 Visualizing the Outcome Distribution

In order to address the distribution of my outcomes (systolic)

p1 <-  ggplot(df_training, aes(sample = systolic)) +
  geom_qq() + # plot the points
  geom_qq_line(col = "blue") + # plot the Y = X line
  theme(aspect.ratio = 1) + # make the plot square
  labs(title = "Normal Q-Q plot: Untransformed Systolic")
p2 <- ggplot(df_training, aes(x=systolic)) +
      geom_histogram(aes(y=stat(density)), bins = 20, fill ="royalblue", col ="white") +
      stat_function(fun = dnorm, args = list(mean = mean(df_training$systolic), sd = sd(df_training$systolic)), col ="red", lwd = 1.5) +
      labs(title="Density Function: Untransformed Systolic")
p3 <- ggplot(df_training, aes(x = systolic, y = "")) +
      geom_boxplot(fill = "royalblue",
      outlier.color = "royalblue") +
      labs(title = "Boxplot: Untransformed Systolic", y = "")
p1 + (p2 / p3 + plot_layout(heights = c(4,1)))

We can see that the distribution of our outcome is clearly right-skewed. Therefore, it is better to do some transformation for the outcome. In order to do this, I would like to use boxCox

5.2 boxCox function to assess need for transformation of our outcome

In order to use the boxCox function, I need to ensure that my outcome, systolic, including strictly positive values. We can see that from below, the minimum value for our outcome is 78.7, so we are good to go to next step.

mosaic::favstats(~ systolic, data = df_training) |> kable()
min Q1 median Q3 max mean sd n missing
79.7 110 120.7 133.3 218.7 123.3937 18.64711 4785 0
mod_0 <- lm(systolic ~ BMI + SleepHours + smoking + diabetes + eco_status + highchol, data = df_training)
boxCox(mod_0)

powerTransform(mod_0)
Estimated transformation parameter 
        Y1 
-0.9637329 

We can see that from the boxCox function and the estimated transformation parameter, it suggested a value of nearly -1, which looks the inverse of the systolic. Therefore, I would like to re-investigate the distribution if I choose to do the transformation for my outcome.

p1 <-  ggplot(df_training, aes(sample = 1/systolic)) +
  geom_qq() + # plot the points
  geom_qq_line(col = "blue") + # plot the Y = X line
  theme(aspect.ratio = 1) + # make the plot square
  labs(title = "Normal Q-Q plot: Inverse Systolic")
p2 <- ggplot(df_training, aes(x=1/systolic)) +
      geom_histogram(aes(y=stat(density)), bins = 20, fill ="royalblue", col ="white") +
      stat_function(fun = dnorm, args = list(mean = mean(1/df_training$systolic), sd = sd(1/df_training$systolic)), col ="red", lwd = 1.5) +
      labs(title="Density Function: Inverse Systolic")
p3 <- ggplot(df_training, aes(x = 1/systolic, y = "")) +
      geom_boxplot(fill = "royalblue",
      outlier.color = "royalblue") +
      labs(title = "Boxplot: Inverse Systolic", y = "")
p1 + (p2 / p3 + plot_layout(heights = c(4,1)))

We could see that from those graphs, after the transformation, the outcome looks to be normally distributed. Therefore, in my study, I would like to do the transformation for the outcome.

5.3 Numerical Summary of the Outcome

This is the numerical summary of the outcome before the transformation

mosaic::favstats(~ systolic, data = df_training) |> kable()
min Q1 median Q3 max mean sd n missing
79.7 110 120.7 133.3 218.7 123.3937 18.64711 4785 0

And this is the numerical summary of the outcome after the transformation

mosaic::favstats(~ 1/systolic, data = df_training) |> kable()
min Q1 median Q3 max mean sd n missing
0.0045725 0.0075019 0.008285 0.0090909 0.0125471 0.008278 0.0011759 4785 0

5.4 Numerical Summaries of the Predictors

I would like to see some numerical summaries for my predictor variable in the training data

df_training |> select(-SEQN, -systolic, -diastolic) |> 
  mosaic::inspect()

categorical variables:  
        name  class levels    n missing
1    smoking factor      2 4785       0
2 eco_status factor      3 4785       0
3   diabetes factor      3 4785       0
4   highchol factor      2 4785       0
                                   distribution
1 No (59.5%), Yes (40.5%)                      
2 Upper (53.9%), Lower (31.2%) ...             
3 No (83.1%), Yes (14.3%) ...                  
4 No (64.5%), Yes (35.5%)                      

quantitative variables:  
        name   class      min       Q1   median       Q3      max      mean
1        BMI numeric 14.63059 24.12663 27.75849 32.60009 72.62397 29.039051
2 SleepHours numeric  2.00000  7.00000  8.00000  9.00000 14.00000  7.924305
        sd    n missing
1 7.051636 4785       0
2 1.511098 4785       0

5.5 Scatterplot Matrix

Then, here I would like to build scatterplot matrices to investigate the relationship between our outcome and the predictors. The first matrix should include all the quantitative variables, while the second one includes categorical variables.

This plot is the one between our outcome and its quantitative predictors. We can see that the correlation between our outcome ( 1/systolic) and the predictors are relatively low, as it is only -0.048 and 0.080. However, comparing to the values that I got during my presentation, this already improved. In addition to that, the distribution of my key predictor is also right-skewed.

df4_training <- mutate(df_training, systolic = 1/systolic)
temp <-  df4_training |> 
  select(systolic,  BMI, SleepHours) 

ggpairs(temp, title = "Scatterplot Matrix",
        lower = list(combo = wrap("facethist", bins = 20)))

From this scatterplot matrix, we can see that there are a lot of outliners for both end between our outcome and our categorical predictors. In addition, the distribution between groups in one variables also not equal to each other, i.e diabetes. However, it seems like the distribution between groups in one variables is normally distributed when comparing to the outcome

df4_training <- mutate(df_training, systolic = 1/systolic)
temp <-  df4_training |> 
  select(systolic, smoking, diabetes, eco_status, highchol) 

ggpairs(temp, title = "Scatterplot Matrix",
        lower = list(combo = wrap("facethist", bins = 20)))

Since the inverse of systolic gives the numerical value that are relatively small, we could see that for smoking variable, the difference between two groups are not noticeable. Howe

mosaic::favstats(systolic ~ smoking, data = df4_training) |> kable()
smoking min Q1 median Q3 max mean sd n missing
Yes 0.0046729 0.0073153 0.0081301 0.0089286 0.0125471 0.0081269 0.0011751 1939 0
No 0.0045725 0.0076161 0.0084034 0.0091743 0.0125000 0.0083809 0.0011655 2846 0

5.6 Collinearity Checking

None of the numeric candidate predictors show any substantial correlation with each other. The largest Pearson correlation (in absolute value) between predictors is (-0.054) for BMI and SleepHours, and that’s not strong. If we did see signs of meaningful collinearity, we might rethink our selected set of predictors.

6 The Big Model

The “kitchen sink” linear regression model is to describe the relationship between our outcome (1/systolic) and the main effects of each of our predictors.

6.1 Fitting/Summarizing the Kitchen Sink model

Our “kitchen sink” or “big” model predicts the square root of sbp2 using the predictors (square root of sbp1), age, bmi1, diabetes and tobacco.

Our kitchen sink or mod_1 predicts the inverse of outcome using the predictors: BMI,SleepHours, smoking, diabetes, eco_status, highchol.

mod_1 <- lm(1/systolic ~ BMI + SleepHours + smoking + diabetes + eco_status + highchol, data = df_training)
summary(mod_1)

Call:
lm(formula = 1/systolic ~ BMI + SleepHours + smoking + diabetes + 
    eco_status + highchol, data = df_training)

Residuals:
       Min         1Q     Median         3Q        Max 
-0.0040245 -0.0007491 -0.0000029  0.0007505  0.0049285 

Coefficients:
                     Estimate Std. Error t value Pr(>|t|)    
(Intercept)         7.347e-03  1.298e-04  56.613  < 2e-16 ***
BMI                -2.017e-06  2.378e-06  -0.848   0.3965    
SleepHours          4.940e-05  1.096e-05   4.506 6.77e-06 ***
smokingNo           1.911e-04  3.409e-05   5.607 2.17e-08 ***
diabetesNo          3.603e-04  4.939e-05   7.296 3.46e-13 ***
diabetesBorderline  4.387e-05  1.106e-04   0.397   0.6917    
eco_statusMiddle   -9.760e-05  5.186e-05  -1.882   0.0599 .  
eco_statusUpper    -8.097e-05  3.748e-05  -2.160   0.0308 *  
highcholNo          3.745e-04  3.597e-05  10.412  < 2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.001139 on 4776 degrees of freedom
Multiple R-squared:  0.06357,   Adjusted R-squared:  0.062 
F-statistic: 40.53 on 8 and 4776 DF,  p-value: < 2.2e-16

6.2 Effect Sizes: Coefficient Estimates

Specify the size and magnitude of all coefficients, providing estimated effect sizes with 90% confidence intervals. Since the value of my coefficients are very small, so I need it to be display in full-form in order to get some integter. From the analysis below, we could see that our key predictor, BMI, is not really significant as the p-value is larger than 0.1.

tidy(mod_1, conf.int = TRUE, conf.level = 0.90) |> 
  select(term, estimate, std.error, conf.low, conf.high, p.value) |> 
  kable()
term estimate std.error conf.low conf.high p.value
(Intercept) 0.0073475 0.0001298 0.0071340 0.0075610 0.0000000
BMI -0.0000020 0.0000024 -0.0000059 0.0000019 0.3964848
SleepHours 0.0000494 0.0000110 0.0000314 0.0000674 0.0000068
smokingNo 0.0001911 0.0000341 0.0001351 0.0002472 0.0000000
diabetesNo 0.0003603 0.0000494 0.0002791 0.0004416 0.0000000
diabetesBorderline 0.0000439 0.0001106 -0.0001381 0.0002258 0.6917092
eco_statusMiddle -0.0000976 0.0000519 -0.0001829 -0.0000123 0.0598946
eco_statusUpper -0.0000810 0.0000375 -0.0001426 -0.0000193 0.0308103
highcholNo 0.0003745 0.0000360 0.0003153 0.0004337 0.0000000

6.3 Describing the Equation

extract_eq(mod_1, use_coefs = TRUE, coef_digits = 8,
           terms_per_line = 3, wrap = TRUE, ital_vars = TRUE)

\[ \begin{aligned} \widehat{1/systolic} &= 0.0073475 - 2.02e-06(BMI) + 4.94e-05(SleepHours)\ + \\ &\quad 0.00019114(smoking_{No}) + 0.00036035(diabetes_{No}) + 4.387e-05(diabetes_{Borderline})\ - \\ &\quad 9.76e-05(eco\_status_{Middle}) - 8.097e-05(eco\_status_{Upper}) + 0.0003745(highchol_{No}) \end{aligned} \]

  • This model can be described as for every increasing in BMI, decrease in sleep hours, we anticipated the increase in the outcome (systolic), which is decrease in the inverse of systolic.
  • If you are not smoking/not diabetes or diabetes only at borderline/no high cholesterol, there would be a decrease in the systolic, which is increase in the inverse of systolic.
  • If you are in either upper or lower classes, there would be an increase in the outcome (systolic), which is decrease in the inverse of systolic.

7 The Smaller Model

7.1 Backwards Stepwise Elimination

Instead of using step function for backwards stepwise elimination, I would like to use a couple selected models that I want to investigate. This is because when I use step function, it will eventually remove my key predictor out of the model, which is not what I wanted.

step(mod_1)
Start:  AIC=-64853.86
1/systolic ~ BMI + SleepHours + smoking + diabetes + eco_status + 
    highchol

             Df  Sum of Sq       RSS    AIC
- BMI         1 9.3300e-07 0.0061954 -64855
<none>                     0.0061945 -64854
- eco_status  2 7.3720e-06 0.0062018 -64852
- SleepHours  1 2.6330e-05 0.0062208 -64836
- smoking     1 4.0777e-05 0.0062353 -64824
- diabetes    2 7.6476e-05 0.0062710 -64799
- highchol    1 1.4061e-04 0.0063351 -64748

Step:  AIC=-64855.14
1/systolic ~ SleepHours + smoking + diabetes + eco_status + highchol

             Df  Sum of Sq       RSS    AIC
<none>                     0.0061954 -64855
- eco_status  2 7.1900e-06 0.0062026 -64854
- SleepHours  1 2.6898e-05 0.0062223 -64836
- smoking     1 4.0834e-05 0.0062362 -64826
- diabetes    2 8.1007e-05 0.0062764 -64797
- highchol    1 1.4171e-04 0.0063371 -64749

Call:
lm(formula = 1/systolic ~ SleepHours + smoking + diabetes + eco_status + 
    highchol, data = df_training)

Coefficients:
       (Intercept)          SleepHours           smokingNo          diabetesNo  
         7.278e-03           4.987e-05           1.913e-04           3.668e-04  
diabetesBorderline    eco_statusMiddle     eco_statusUpper          highcholNo  
         4.669e-05          -9.713e-05          -7.950e-05           3.757e-04  

Therefore, besides my big model, I would like to investigate the other three models:

  • Model 2: 1/systolic ~ BMI
  • Model 3: 1/systolic ~ BMI, SleepHours
  • Model 4: 1/systolic ~ BMI, SleepHours, diabetes.

7.2 Fitting the “small” model

This is my first small model, which is investigate the relationship between the outcome and the key predictor.

mod_2 <- lm(1/systolic ~ BMI, data = df_training)
summary(mod_2)

Call:
lm(formula = 1/systolic ~ BMI, data = df_training)

Residuals:
       Min         1Q     Median         3Q        Max 
-0.0036536 -0.0007741  0.0000151  0.0008147  0.0045362 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  8.512e-03  7.197e-05 118.276  < 2e-16 ***
BMI         -8.064e-06  2.408e-06  -3.348 0.000819 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.001175 on 4783 degrees of freedom
Multiple R-squared:  0.002339,  Adjusted R-squared:  0.00213 
F-statistic: 11.21 on 1 and 4783 DF,  p-value: 0.0008189

This is my second small model, which is investigate the relationship between the outcome and the key predictor along with the average hours of sleep per week.

mod_3 <- lm(1/systolic ~ BMI + SleepHours, data = df_training)
summary(mod_3)

Call:
lm(formula = 1/systolic ~ BMI + SleepHours, data = df_training)

Residuals:
       Min         1Q     Median         3Q        Max 
-0.0036914 -0.0007896  0.0000078  0.0008064  0.0044072 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  8.016e-03  1.172e-04  68.388  < 2e-16 ***
BMI         -7.374e-06  2.405e-06  -3.066  0.00218 ** 
SleepHours   6.010e-05  1.122e-05   5.355 8.94e-08 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.001171 on 4782 degrees of freedom
Multiple R-squared:  0.008287,  Adjusted R-squared:  0.007872 
F-statistic: 19.98 on 2 and 4782 DF,  p-value: 2.288e-09

This is my second small model, which is investigate the relationship between the outcome and the key predictor along with the average hours of sleep per week and diabetes groups.

mod_4 <- lm(1/systolic ~ BMI + SleepHours + diabetes, data = df_training)
summary(mod_4)

Call:
lm(formula = 1/systolic ~ BMI + SleepHours + diabetes, data = df_training)

Residuals:
       Min         1Q     Median         3Q        Max 
-0.0037745 -0.0007651 -0.0000047  0.0007947  0.0046675 

Coefficients:
                     Estimate Std. Error t value Pr(>|t|)    
(Intercept)         7.477e-03  1.273e-04  58.749  < 2e-16 ***
BMI                -2.877e-06  2.412e-06  -1.193    0.233    
SleepHours          5.816e-05  1.109e-05   5.242 1.65e-07 ***
diabetesNo          5.072e-04  4.858e-05  10.441  < 2e-16 ***
diabetesBorderline  9.720e-05  1.123e-04   0.866    0.387    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.001157 on 4780 degrees of freedom
Multiple R-squared:  0.03236,   Adjusted R-squared:  0.03155 
F-statistic: 39.96 on 4 and 4780 DF,  p-value: < 2.2e-16

7.3 Effect Sizes: Coefficient Estimates

tidy(mod_2, conf.int = TRUE, conf.level = 0.90) |> 
  select(term, estimate, std.error, conf.low, conf.high, p.value) |> kable()
term estimate std.error conf.low conf.high p.value
(Intercept) 0.0085122 7.2e-05 0.0083938 0.0086306 0.0000000
BMI -0.0000081 2.4e-06 -0.0000120 -0.0000041 0.0008189
tidy(mod_3, conf.int = TRUE, conf.level = 0.90) |> 
  select(term, estimate, std.error, conf.low, conf.high, p.value) |> kable()
term estimate std.error conf.low conf.high p.value
(Intercept) 0.0080159 0.0001172 0.0078230 0.0082087 0.0000000
BMI -0.0000074 0.0000024 -0.0000113 -0.0000034 0.0021808
SleepHours 0.0000601 0.0000112 0.0000416 0.0000786 0.0000001
tidy(mod_4, conf.int = TRUE, conf.level = 0.90) |> 
  select(term, estimate, std.error, conf.low, conf.high, p.value) |> kable()
term estimate std.error conf.low conf.high p.value
(Intercept) 0.0074769 0.0001273 0.0072675 0.0076862 0.0000000
BMI -0.0000029 0.0000024 -0.0000068 0.0000011 0.2329745
SleepHours 0.0000582 0.0000111 0.0000399 0.0000764 0.0000002
diabetesNo 0.0005072 0.0000486 0.0004273 0.0005871 0.0000000
diabetesBorderline 0.0000972 0.0001123 -0.0000875 0.0002819 0.3866541

7.4 Small Model Regression Equation

extract_eq(mod_2, use_coefs = TRUE, coef_digits = 8,
           terms_per_line = 3, wrap = TRUE, ital_vars = TRUE)

\[ \begin{aligned} \widehat{1/systolic} &= 0.00851217 - 8.06e-06(BMI) \end{aligned} \]

  • This model can be described as for every increasing in BMI, we anticipated the increase in the outcome (systolic), which is decrease in the inverse of systolic.
extract_eq(mod_3, use_coefs = TRUE, coef_digits = 8,
           terms_per_line = 3, wrap = TRUE, ital_vars = TRUE)

\[ \begin{aligned} \widehat{1/systolic} &= 0.00801586 - 7.37e-06(BMI) + 6.01e-05(SleepHours) \end{aligned} \] - This model can be described as for every increasing in BMI, decrease in sleep hours, we anticipated the increase in the outcome (systolic), which is decrease in the inverse of systolic.

extract_eq(mod_4, use_coefs = TRUE, coef_digits = 8,
           terms_per_line = 3, wrap = TRUE, ital_vars = TRUE)

\[ \begin{aligned} \widehat{1/systolic} &= 0.00747686 - 2.88e-06(BMI) + 5.816e-05(SleepHours)\ + \\ &\quad 0.00050721(diabetes_{No}) + 9.72e-05(diabetes_{Borderline}) \end{aligned} \] - This model can be described as for every increasing in BMI, decrease in sleep hours, we anticipated the increase in the outcome (systolic), which is decrease in the inverse of systolic. - If you are not diabetes or diabetes only at borderline, there would be a decrease in the systolic, which is increase in the inverse of systolic.

8 In-Sample Comparison

8.1 Quality of Fit

In order to do the in-cample comparison, I would like to compare all of my small model to the big model in our training sample using adjusted R2, the residual standard error, AIC and BIC.

bind_rows(glance(mod_1), glance(mod_2), glance(mod_3), glance(mod_4)) %>%
mutate(model_name = c("Model 1", "Model 2", "Model 3", "Model 4"))%>%
select(model_name, r.squared, adj.r.squared, sigma, AIC, BIC, nobs) %>%
kable()
model_name r.squared adj.r.squared sigma AIC BIC nobs
Model 1 0.0635718 0.0620032 0.0011389 -51272.62 -51207.89 4785
Model 2 0.0023387 0.0021301 0.0011746 -50983.54 -50964.12 4785
Model 3 0.0082865 0.0078717 0.0011713 -51010.15 -50984.25 4785
Model 4 0.0323557 0.0315460 0.0011572 -51123.71 -51084.87 4785

When comparing the AIC, Model 1 is the best, which is following by Model 4, Model 3, Model 2, respectively. When comparing the BIC, Model 1 is the best, which is following by Model 4, Model 3, Model 2, respectively. When comparing the sigma value, Model 1 is the best, which is following by Model 4, Model 3, Model 2, respectively. When comparing the r-squared value, Model 1 is the best, which is following by Model 4, Model 3, Model 2, respectively. ## Assessing Assumptions Here I would like to run the residula plots for every model. ### Residual Plots for the Big Model

par(mfrow = c(2,2)); plot(mod_1); par(mfrow = c(1,1))

I see no serious problems with the assumptions of linearity, Normality and constant variance, nor do I see any highly influential points in our big model. ### Residual Plots for the Small Model Model 2

par(mfrow = c(2,2)); plot(mod_2); par(mfrow = c(1,1))

I see no serious problems with the assumptions of linearity, Normality and constant variance, nor do I see any highly influential points in our big model.

Model 3

par(mfrow = c(2,2)); plot(mod_3); par(mfrow = c(1,1))

I see no serious problems with the assumptions of linearity, Normality and constant variance, nor do I see any highly influential points in our big model.

Model 4

par(mfrow = c(2,2)); plot(mod_4); par(mfrow = c(1,1))

I see no serious problems with the assumptions of linearity, Normality and constant variance, nor do I see any highly influential points in our big model. However, it is weired that it forms two clusters in the graph. I believe this is influenced by the diabetes variables as this only happens after adding this values into the model 4 comparing to Model 1 and 2. The main reason may be because the groups in diabetes are not having equal size.

8.1.1 Does collinearity have a meaningful impact?

car::vif(mod_1)
               GVIF Df GVIF^(1/(2*Df))
BMI        1.037236  1        1.018448
SleepHours 1.012586  1        1.006273
smoking    1.033282  1        1.016505
diabetes   1.106591  2        1.025644
eco_status 1.024753  2        1.006132
highchol   1.092392  1        1.045175
car::vif(mod_4)
               GVIF Df GVIF^(1/(2*Df))
BMI        1.033453  1        1.016589
SleepHours 1.004043  1        1.002020
diabetes   1.031996  2        1.007905

The generalized variance inflation factors are under 5 for both model, which address that there are no potential impact of collinearity. ## Comparing the Models Based on the training sample, my conclusions so far is to support the biggest model or Model 1. It won every values to be the best model for the fit quality measures, and each model shows no serious problems with regression assumptions. # Model Validation Since I transformed my outcome earlier, then I need to back-transformed for model validation ## Calculating Prediction Errors

8.1.2 Big Model: Back-Transformation and Calculating Fits/Residuals

We’ll use the augment function from the broom package to help us here, and create fit_systolic to hold the fitted values on the original systolic scale after back-transformation (by doing the inverse of systolic) and then res_systolic to hold the residuals (prediction errors) we observe using the big model on the chosen nhannes data.

test_m1 <- augment(mod_1, newdata = df_test) %>% mutate(name = "mod_1", fit_systolic = 1 / .fitted, res_systolic = systolic - fit_systolic) %>% select(SEQN, name, systolic, fit_systolic,  res_systolic, everything())
head(test_m1,3)
# A tibble: 3 × 14
    SEQN name  systolic fit_syst…¹ res_s…² diast…³   BMI Sleep…⁴ smoking eco_s…⁵
   <dbl> <chr>    <dbl>      <dbl>   <dbl>   <dbl> <dbl>   <dbl> <fct>   <fct>  
1 109266 mod_1      99        123.  -24.0     54.3  36.0     7.8 No      Upper  
2 109282 mod_1     139.       126.   13.4     72.7  26.5     7.5 Yes     Upper  
3 109290 mod_1     121.       125.   -4.10    60.3  27.5     4   No      Upper  
# … with 4 more variables: diabetes <fct>, highchol <fct>, .fitted <dbl>,
#   .resid <dbl>, and abbreviated variable names ¹​fit_systolic, ²​res_systolic,
#   ³​diastolic, ⁴​SleepHours, ⁵​eco_status

8.1.3 Small Model: Back-Transformation and Calculating Fits/Residuals

We’ll do the same thing, but using the small model in the chosen nhannes data.

Model 2

test_m2 <- augment(mod_2, newdata = df_test) %>% mutate(name = "mod_2", fit_systolic = 1 / .fitted, res_systolic = systolic - fit_systolic) %>% select(SEQN, name, systolic, fit_systolic,  res_systolic, everything())
head(test_m2,3)
# A tibble: 3 × 14
    SEQN name  systolic fit_syst…¹ res_s…² diast…³   BMI Sleep…⁴ smoking eco_s…⁵
   <dbl> <chr>    <dbl>      <dbl>   <dbl>   <dbl> <dbl>   <dbl> <fct>   <fct>  
1 109266 mod_2      99        122. -22.6      54.3  36.0     7.8 No      Upper  
2 109282 mod_2     139.       121.  18.8      72.7  26.5     7.5 Yes     Upper  
3 109290 mod_2     121.       121.   0.684    60.3  27.5     4   No      Upper  
# … with 4 more variables: diabetes <fct>, highchol <fct>, .fitted <dbl>,
#   .resid <dbl>, and abbreviated variable names ¹​fit_systolic, ²​res_systolic,
#   ³​diastolic, ⁴​SleepHours, ⁵​eco_status

Model 3

test_m3 <- augment(mod_3, newdata = df_test) %>% mutate(name = "mod_3", fit_systolic = 1 / .fitted, res_systolic = systolic - fit_systolic) %>% select(SEQN, name, systolic, fit_systolic,  res_systolic, everything())
head(test_m3,3)
# A tibble: 3 × 14
    SEQN name  systolic fit_syst…¹ res_s…² diast…³   BMI Sleep…⁴ smoking eco_s…⁵
   <dbl> <chr>    <dbl>      <dbl>   <dbl>   <dbl> <dbl>   <dbl> <fct>   <fct>  
1 109266 mod_3      99        122.  -22.7     54.3  36.0     7.8 No      Upper  
2 109282 mod_3     139.       121.   18.4     72.7  26.5     7.5 Yes     Upper  
3 109290 mod_3     121.       124.   -2.86    60.3  27.5     4   No      Upper  
# … with 4 more variables: diabetes <fct>, highchol <fct>, .fitted <dbl>,
#   .resid <dbl>, and abbreviated variable names ¹​fit_systolic, ²​res_systolic,
#   ³​diastolic, ⁴​SleepHours, ⁵​eco_status

Model 4

test_m4 <- augment(mod_4, newdata = df_test) %>% mutate(name = "mod_4", fit_systolic = 1 / .fitted, res_systolic = systolic - fit_systolic) %>% select(SEQN, name, systolic, fit_systolic,  res_systolic, everything())
head(test_m4,3)
# A tibble: 3 × 14
    SEQN name  systolic fit_syst…¹ res_s…² diast…³   BMI Sleep…⁴ smoking eco_s…⁵
   <dbl> <chr>    <dbl>      <dbl>   <dbl>   <dbl> <dbl>   <dbl> <fct>   <fct>  
1 109266 mod_4      99        120.  -21.0     54.3  36.0     7.8 No      Upper  
2 109282 mod_4     139.       120.   19.5     72.7  26.5     7.5 Yes     Upper  
3 109290 mod_4     121.       131.   -9.75    60.3  27.5     4   No      Upper  
# … with 4 more variables: diabetes <fct>, highchol <fct>, .fitted <dbl>,
#   .resid <dbl>, and abbreviated variable names ¹​fit_systolic, ²​res_systolic,
#   ³​diastolic, ⁴​SleepHours, ⁵​eco_status

8.1.4 Combining the Results

the test_comp tibble including all the predictions and residuals from 4 models. It helps to visualize the predictions, summarizing the errors, identify the largest errors, and validated the r-squared values.

test_comp <- bind_rows(test_m1, test_m2, test_m3, test_m4) |> arrange(SEQN, name)

test_comp |> head()
# A tibble: 6 × 14
    SEQN name  systolic fit_syst…¹ res_s…² diast…³   BMI Sleep…⁴ smoking eco_s…⁵
   <dbl> <chr>    <dbl>      <dbl>   <dbl>   <dbl> <dbl>   <dbl> <fct>   <fct>  
1 109266 mod_1      99        123.   -24.0    54.3  36.0     7.8 No      Upper  
2 109266 mod_2      99        122.   -22.6    54.3  36.0     7.8 No      Upper  
3 109266 mod_3      99        122.   -22.7    54.3  36.0     7.8 No      Upper  
4 109266 mod_4      99        120.   -21.0    54.3  36.0     7.8 No      Upper  
5 109282 mod_1     139.       126.    13.4    72.7  26.5     7.5 Yes     Upper  
6 109282 mod_2     139.       121.    18.8    72.7  26.5     7.5 Yes     Upper  
# … with 4 more variables: diabetes <fct>, highchol <fct>, .fitted <dbl>,
#   .resid <dbl>, and abbreviated variable names ¹​fit_systolic, ²​res_systolic,
#   ³​diastolic, ⁴​SleepHours, ⁵​eco_status

8.2 Visualizing the Predictions

ggplot(test_comp, aes(x = fit_systolic, y = systolic)) +
  geom_point() +
  geom_abline(slope = 1, intercept = 0, lty = "dashed") + 
  geom_smooth(method = "loess", col = "blue", se = FALSE, formula = y ~ x) +
  facet_wrap( ~ name, labeller = "label_both") +
  labs(x = "Predicted systolic",
       y = "Observed systolic",
       title = "Observed vs. Predicted systolic",
       subtitle = "Comparing Big to 3 Small Modesl in Test Sample",
       caption = "Dashed line is where Observed = Predicted")

We can see that in four graphs, the Model 1 seems to be the most reasonable comparing to other 3. Other 3 models, all the points seem to be cluster at one place, except for Model 4 that it clusters into 2 different clusters. However, there are not much difference in the dashed line, which is predictable since the coefficients are really low.

8.3 Summarizing the Errors

Calculate the mean absolute prediction error (MAPE), the root mean squared prediction error (RMSPE) and the maximum absolute error across the predictions made by each model.

test_comp |>
  group_by(name) |>
  dplyr::summarise(n = n(),
            MAPE = mean(abs(res_systolic)), 
            RMSPE = sqrt(mean(res_systolic**2)),
            max_error = max(abs(res_systolic)))
# A tibble: 4 × 5
  name      n  MAPE RMSPE max_error
  <chr> <int> <dbl> <dbl>     <dbl>
1 mod_1  2052  14.9  19.9      91.8
2 mod_2  2052  15.4  20.4      94.8
3 mod_3  2052  15.4  20.3      92.7
4 mod_4  2052  15.3  20.2      94.8

Model 1 has the lowest MAPE, RMSPE, and max_error. Model 2 has the second-lowest for all these 3 values. Due to this, it also suggests that Model 1 is the best model so far.

8.3.1 Identify the largest errors

temp1 <- test_m1 %>%
filter(abs(res_systolic) == max(abs(res_systolic)))
temp2 <- test_m2 %>%
filter(abs(res_systolic) == max(abs(res_systolic)))
temp3 <- test_m3 %>%
filter(abs(res_systolic) == max(abs(res_systolic)))
temp4 <- test_m4 %>%
filter(abs(res_systolic) == max(abs(res_systolic)))
bind_rows(temp1, temp2, temp3, temp4) %>%
select(SEQN, name, systolic, fit_systolic, res_systolic)
# A tibble: 4 × 5
    SEQN name  systolic fit_systolic res_systolic
   <dbl> <chr>    <dbl>        <dbl>        <dbl>
1 112898 mod_1      217         125.         91.8
2 112898 mod_2      217         122.         94.8
3 112898 mod_3      217         124.         92.7
4 112898 mod_4      217         122.         94.8

This helps to identify the outliner for all 4 models. We can try to investigate the errors after removing this point.

test_comp %>% filter(SEQN != "112898") %>%
group_by(name) |>
  dplyr::summarise(n = n(),
            MAPE = mean(abs(res_systolic)), 
            RMSPE = sqrt(mean(res_systolic**2)),
            max_error = max(abs(res_systolic)))
# A tibble: 4 × 5
  name      n  MAPE RMSPE max_error
  <chr> <int> <dbl> <dbl>     <dbl>
1 mod_1  2051  14.9  19.8      82.3
2 mod_2  2051  15.4  20.3      80.8
3 mod_3  2051  15.3  20.2      81.1
4 mod_4  2051  15.2  20.1      82.0

After removing that outliner, Model 1 only has the lowest value for MAPE and RMSPE, with the biggest maximum absolute error. However, it still outweights other models.

8.3.2 Validated R-square values

cor(test_m1$systolic, test_m1$fit_systolic)**2
[1] 0.04779547
cor(test_m2$systolic, test_m2$fit_systolic)**2
[1] 0.000361166
cor(test_m3$systolic, test_m3$fit_systolic)**2
[1] 0.002475879
cor(test_m4$systolic, test_m4$fit_systolic)**2
[1] 0.01602802

From this analysis, we can see that Model 1 has the biggest correlations.

8.4 Comparing the Models

After the analysis, I would like to use Model 1, or the big model because it performs the best for fit quality as well as has the smallest errors comparing to other models.

9 Discussion

9.1 Chosen Model

After the analysis, I would like to use Model 1, or the big model because it performs the best for fit quality (lowest AIC, BIC,sigma but highest R-squared ) as well as has the smallest errors (mean absolute prediction error, , maximum absolute error ) comparing to other models.

9.2 Answering My Question

This helps to answer my questions that having higher BMI, less sleep could lead to higher systolic, which indicate the high blood pressure. In addition, not having diabetes or diabetes at the borderline could also have lower systolic reading. Not having high cholesterol or smokig also indicates lower systolic reading. People come from the upper and lower, there would be an increase in the systolic. Even though the coefficients are very small, but the result seems to be reasonable as those risk factors only increase the systolic reading when it increase in the harmful way. For the economic status, it can be explained that people from the lower class, they have less chance to get access to the healthcare and don’t take care much about their food. For the upper class, they have access to more food, and may overconsume it.

9.3 Next Steps

In this analysis, I only consider 4 models without doing in the step function. That’s being said, there could be other models that are better fit comparing to my big model. Further works must be done in order to investigate different combinations of predictors in order to predict the outcome.

In addition to that, I only use systolic reading for this analysis without consider diastolic reading. Further work should be worked on diastolic reading to see what factors affect this value. This is because both of these values are essential in order to indicate a person who is at risk for having abnormal blood pressure or not. Getting to know one value could lead to bias. ## Reflection

I already changed my approach when doing this study after the presentation after listening advice from Professor. However, it seems like my coefficients are still very low. I believe if I can redo it, I would like to investigate more about the datasets and make a better choice for my predictors.

9.4 Session Information

sessionInfo()
R version 4.2.1 (2022-06-23)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Big Sur ... 10.16

Matrix products: default
BLAS:   /Library/Frameworks/R.framework/Versions/4.2/Resources/lib/libRblas.0.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.2/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] mosaicData_0.20.3    ggformula_0.10.2     Matrix_1.5-3        
 [4] nhanesA_0.7.1        GGally_2.1.2         equatiomatic_0.3.1  
 [7] car_3.1-1            carData_3.0-5        forcats_0.5.2       
[10] stringr_1.4.1        dplyr_1.0.10         purrr_0.3.5         
[13] readr_2.1.3          tidyr_1.2.1          tibble_3.1.8        
[16] tidyverse_1.3.2      Hmisc_4.7-1          ggplot2_3.3.6       
[19] Formula_1.2-4        survival_3.3-1       lattice_0.20-45     
[22] Epi_2.47             readxl_1.4.1         patchwork_1.1.2.9000
[25] broom_1.0.1          naniar_0.6.1         janitor_2.1.0       
[28] rmdformats_1.0.4     knitr_1.40          

loaded via a namespace (and not attached):
  [1] googledrive_2.0.0   colorspace_2.0-3    deldir_1.0-6       
  [4] ggridges_0.5.4      ellipsis_0.3.2      visdat_0.5.3       
  [7] ggstance_0.3.5      snakecase_0.11.0    htmlTable_2.4.1    
 [10] base64enc_0.1-3     fs_1.5.2            rstudioapi_0.14    
 [13] farver_2.1.1        fansi_1.0.3         lubridate_1.8.0    
 [16] xml2_1.3.3          mosaic_1.8.4.2      splines_4.2.1      
 [19] cachem_1.0.6        polyclip_1.10-0     jsonlite_1.8.2     
 [22] cluster_2.1.3       dbplyr_2.2.1        png_0.1-7          
 [25] ggforce_0.4.1       shiny_1.7.2         compiler_4.2.1     
 [28] httr_1.4.4          backports_1.4.1     assertthat_0.2.1   
 [31] fastmap_1.1.0       gargle_1.2.1        cli_3.4.1          
 [34] tweenr_2.0.2        later_1.3.0         htmltools_0.5.3    
 [37] tools_4.2.1         gtable_0.3.1        glue_1.6.2         
 [40] Rcpp_1.0.9          cellranger_1.1.0    jquerylib_0.1.4    
 [43] vctrs_0.5.0         nlme_3.1-157        xfun_0.33          
 [46] rvest_1.0.3         mosaicCore_0.9.2.1  mime_0.12          
 [49] lifecycle_1.0.3     googlesheets4_1.0.1 MASS_7.3-57        
 [52] zoo_1.8-11          scales_1.2.1        hms_1.1.2          
 [55] promises_1.2.0.1    parallel_4.2.1      RColorBrewer_1.1-3 
 [58] yaml_2.3.5          gridExtra_2.3       sass_0.4.2         
 [61] labelled_2.10.0     rpart_4.1.16        reshape_0.8.9      
 [64] latticeExtra_0.6-30 stringi_1.7.8       highr_0.9          
 [67] checkmate_2.1.0     rlang_1.0.6         pkgconfig_2.0.3    
 [70] evaluate_0.16       labeling_0.4.2      htmlwidgets_1.5.4  
 [73] cmprsk_2.2-11       tidyselect_1.1.2    plyr_1.8.7         
 [76] magrittr_2.0.3      bookdown_0.29       R6_2.5.1           
 [79] generics_0.1.3      DBI_1.1.3           pillar_1.8.1       
 [82] haven_2.5.1         foreign_0.8-82      withr_2.5.0        
 [85] mgcv_1.8-40         abind_1.4-5         nnet_7.3-17        
 [88] etm_1.1.1           modelr_0.1.9        crayon_1.5.2       
 [91] interp_1.1-3        utf8_1.2.2          tzdb_0.3.0         
 [94] rmarkdown_2.16      jpeg_0.1-9          grid_4.2.1         
 [97] data.table_1.14.2   reprex_2.0.2        digest_0.6.30      
[100] xtable_1.8-4        httpuv_1.6.6        numDeriv_2016.8-1.1
[103] munsell_0.5.0       bslib_0.4.0        
LS0tCnRpdGxlOiAiUHJvamVjdCBCIC0gU3R1ZHkgMiIKYXV0aG9yOiAiUXV5bmggTmd1eWVuIgpwYXJhbXM6CmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIHJtZGZvcm1hdHM6OmRvd25jdXRlOgogICAgbnVtYmVyX3NlY3Rpb25zOiBUUlVFCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUKLS0tCiMgU2V0dXAgYW5kIERhdGEgSW5nZXN0CiMjIEluaXRpYWwgU2V0dXAgYW5kIFBhY2thZ2UgTG9hZHMKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkoa25pdHIpOyBsaWJyYXJ5KHJtZGZvcm1hdHMpCgpsaWJyYXJ5KGphbml0b3IpOyBsaWJyYXJ5KG5hbmlhcikKbGlicmFyeShicm9vbSk7IGxpYnJhcnkocGF0Y2h3b3JrKQoKbGlicmFyeShyZWFkeGwpCmxpYnJhcnkoRXBpKQpsaWJyYXJ5KEhtaXNjKQpsaWJyYXJ5KHRpZHl2ZXJzZSkgCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KGNhcikKbGlicmFyeShlcXVhdGlvbWF0aWMpCmxpYnJhcnkoR0dhbGx5KQoKIyMgTG9hZCBMb3ZlLWJvb3N0IApzb3VyY2UoIi9Vc2Vycy9xdXluaG5ndXllbi9Eb2N1bWVudHMvU3R1ZHkgTWF0ZXJpYWxzL2Jvb3N0LnIiKQpvcHRzX2NodW5rJHNldChjb21tZW50PU5BKQpvcHRzX2tuaXQkc2V0KHdpZHRoPTc1KQoKdGhlbWVfc2V0KHRoZW1lX2J3KCkpCmBgYAoKIyMgTG9hZGluZyB0aGUgUmF3IERhdGEgaW50byBSCkZvciB0aGlzIHN0dWR5LCBJIHdvdWxkIGxpa2UgdG8gdXNlIHNvbWUgZGF0YXNldHMgdGhhdCBJIG9idGFpbmVkIGZyb20gdGhlIE5hdGlvbmFsIEhlYWx0aCBhbmQgTnV0cml0aW9uIEV4YW1pbmFpdGlvbiBTdXJ2ZXkgKE5IQU5FUykgYnkgTmF0aW9uYWwgQ2VudGVyIGZvciBIZWFsdGggU3RhdGlzdGljcyBieSB1c2luZyBgbmhhbmVzQWAgcGFja2FnZS4gSSB3b3VsZCBsaWtlIHRvIHVzZSB0aGUgZm9sbG93aW5nIGRhdGFzZXRzOiAKCi0gQmxvb2QgUHJlc3N1cmUgLSBPc2NpbGxvbWV0cmljIE1lYXN1cmVtZW50IChQX0JQWE8pCi0gU21va2luZyAtIENpZ2FyZXR0ZSBVc2UgKFBfU01RKQotIFdlaWdodCBIaXN0b3J5IChQX1dIUSkKLSBJbmNvbWUgKFBfSU5RKQotIFNsZWVwIERpc29yZGVyIChQX1NMUSkKLSBEaWFiZXRlcyAoUF9ESVEpCi0gQmxvb2QgUHJlc3N1cmUgJiBDaG9sZXN0ZXJvbCAoUF9CUFEpCgpgYGB7cn0KbGlicmFyeShuaGFuZXNBKQoKI0Jsb29kIHByZXNzdXJlIG1lYXN1cmVtZW50CmJwX3JhdyA8LSBuaGFuZXMoJ1BfQlBYTycpIHw+IHRpYmJsZSgpCnNhdmVSRFMoYnBfcmF3LCAiQlBYX0ouUmRzIikKYnBfcmF3IDwtIHJlYWRSRFMoIkJQWF9KLlJkcyIpCgojIFNtb2tpbmcgCnNtb2tlX3JhdyA8LSBuaGFuZXMoJ1BfU01RJykgfD4gdGliYmxlKCkKc2F2ZVJEUyhzbW9rZV9yYXcsICJQX1NNUS5SZHMiKQpzbW9rZV9yYXcgPC0gcmVhZFJEUygiUF9TTVEuUmRzIikKCiMgV2VpZ2h0IEhpc3RvcnkKd2VpZ2h0X3JhdyA8LSBuaGFuZXMoJ1BfV0hRJykgfD4gdGliYmxlKCkKc2F2ZVJEUyh3ZWlnaHRfcmF3LCAiUF9XSFEuUmRzIikKd2VpZ2h0X3JhdyA8LSByZWFkUkRTKCJQX1dIUS5SZHMiKQoKIyBJbmNvbWUKaW5jb21lX3JhdyA8LSBuaGFuZXMoJ1BfSU5RJykgfD4gdGliYmxlKCkKc2F2ZVJEUyhpbmNvbWVfcmF3LCAiUF9JTlEuUmRzIikKaW5jb21lX3JhdyA8LSByZWFkUkRTKCJQX0lOUS5SZHMiKQoKIyBTbGVlcCBEaXNvcmRlcgpzbGVlcF9yYXcgPC0gbmhhbmVzKCdQX1NMUScpIHw+IHRpYmJsZSgpCnNhdmVSRFMoc2xlZXBfcmF3LCAiUF9TTFEuUmRzIikKc2xlZXBfcmF3IDwtIHJlYWRSRFMoIlBfU0xRLlJkcyIpCgojIERpYWJldGVzCmRpYWJldGVzX3JhdyA8LSBuaGFuZXMoJ1BfRElRJykgfD4gdGliYmxlKCkKc2F2ZVJEUyhkaWFiZXRlc19yYXcsICJQX0RJUS5SZHMiKQpkaWFiZXRlc19yYXcgPC0gcmVhZFJEUygiUF9ESVEuUmRzIikKCiMgQ2hvbGVzdGVyb2wgbGV2ZWwgCmNob2xfcmF3IDwtIG5oYW5lcygnUF9CUFEnKSB8PiB0aWJibGUoKQpzYXZlUkRTKGNob2xfcmF3LCAiUF9CUFEuUmRzIikKY2hvbF9yYXcgPC0gcmVhZFJEUygiUF9CUFEuUmRzIikKYGBgCiMjIENvbnRlbnQgb2YgdGhlIFJhdyBUaWJibGVzIAoKKiBGb3IgdGhlIGZpcnN0IHRpYmxlLCBgYnBfcmF3YCwgaXQgY29udGFpbnMgMTIgdmFyaWFibGVzIHdpdGggMTE2NTYgcm93cywgaW5jbHVkaW5nIHRoZSB1bmlxdWUgaWRlbnRpZmllciwgaS5lIGBTRVFOYCBhcyByZXNwb25kZW50IHNlcXVlbmNlLCB3aGljaCBpcyB1bmlxdWUgZm9yIGVhY2ggcGF0aWVudCBmb3IgZWFjaCBwYXJ0aWNpcGFudC4KYGBge3J9CmRpbShicF9yYXcpCmBgYAoKKiBGb3IgYHNtb2tlX3Jhd2AsIGl0IGNvbnRhaW5zIDE2IHZhcmlhYmxlcyB3aXRoIDExMTM3IHJvd3MsIGluY2x1ZGluZyB0aGUgdW5pcXVlIGlkZW50aWZpZXIsIGkuZSBgU0VRTmAgYXMgcmVzcG9uZGVudCBzZXF1ZW5jZSwgd2hpY2ggaXMgdW5pcXVlIGZvciBlYWNoIHBhdGllbnQgZm9yIGVhY2ggcGFydGljaXBhbnQuCmBgYHtyfQpkaW0oc21va2VfcmF3KQpgYGAKCiogRm9yIGB3ZWlnaHRfcmF3YCwgaXQgY29udGFpbnMgMzUgdmFpcmFibGVzIHdpdGggMTAxOTUgcm93cywgaW5jbHVkaW5nIHRoZSB1bmlxdWUgaWRlbnRpZmllciwgaS5lIGBTRVFOYCBhcyByZXNwb25kZW50IHNlcXVlbmNlLCB3aGljaCBpcyB1bmlxdWUgZm9yIGVhY2ggcGF0aWVudCBmb3IgZWFjaCBwYXJ0aWNpcGFudC4KYGBge3J9CmRpbSh3ZWlnaHRfcmF3KQpgYGAKCiogRm9yIGBpbmNvbWVfcmF3YCwgaXQgY29udGFpbnMgMyB2YXJpYWJsZXMgd2l0aCAxNTU2MCByb3dzLGluY2x1ZGluZyB0aGUgdW5pcXVlIGlkZW50aWZpZXIsIGkuZSBgU0VRTmAgYXMgcmVzcG9uZGVudCBzZXF1ZW5jZSwgd2hpY2ggaXMgdW5pcXVlIGZvciBlYWNoIHBhdGllbnQgZm9yIGVhY2ggcGFydGljaXBhbnQuCmBgYHtyfQpkaW0oaW5jb21lX3JhdykKYGBgCgoqIEZvciBgc2xlZXBfcmF3YCwgaXQgY29udGFpbnMgMTEgdmFyaWFibGVzIHdpdGggMTAxOTUgcm93cywgaW5jbHVkaW5nIHRoZSB1bmlxdWUgaWRlbnRpZmllciwgaS5lIGBTRVFOYCBhcyByZXNwb25kZW50IHNlcXVlbmNlLCB3aGljaCBpcyB1bmlxdWUgZm9yIGVhY2ggcGF0aWVudCBmb3IgZWFjaCBwYXJ0aWNpcGFudC4KYGBge3J9CmRpbShzbGVlcF9yYXcpCmBgYAoKKiBGb3IgYGRpYWJldGVzX3Jhd2AsIGl0IGNvbnRhaW5zIDI4IHZhcmlhYmxlcyB3aXRoIDE0OTg2IHJvd3MsIGluY2x1ZGluZyB0aGUgdW5pcXVlIGlkZW50aWZpZXIsIGkuZSBgU0VRTmAgYXMgcmVzcG9uZGVudCBzZXF1ZW5jZSwgd2hpY2ggaXMgdW5pcXVlIGZvciBlYWNoIHBhdGllbnQgZm9yIGVhY2ggcGFydGljaXBhbnQuCmBgYHtyfQpkaW0oZGlhYmV0ZXNfcmF3KQpgYGAKCiogRm9yIGBjaG9sX3Jhd2AsIGl0IGNvbnRhaW5zIDExIHZhcmlhYmxlcyB3aXRoIDEwMTk1IHJvd3MsIGluY2x1ZGluZyB0aGUgdW5pcXVlIGlkZW50aWZpZXIsIGkuZSBgU0VRTmAgYXMgcmVzcG9uZGVudCBzZXF1ZW5jZSwgd2hpY2ggaXMgdW5pcXVlIGZvciBlYWNoIHBhdGllbnQgZm9yIGVhY2ggcGFydGljaXBhbnQuCgpgYGB7cn0KZGltKGNob2xfcmF3KQpgYGAKIyMgTWVyZ2luZyBTdGVwcwpJIHdvdWxkIGxpa2UgdG8gam9pbmluZyBjb2x1bW5zIG9mIHRoZXNlIHRhYmxlcyB0b2dlaHRlciBieSB1c2luZyBpbm5lciBqb2luIG9uIGBTRVFOYC4gCkl0IHNob3VsZCBjb250YWlucyAxMTAgY29sdW1ucyBhZnRlciB0aGUgbWVyZ2luZyBzdGVwLgpgYGB7cn0KZGZfcmF3IDwtIGlubmVyX2pvaW4oYnBfcmF3LCBzbW9rZV9yYXcsIGJ5ID0gIlNFUU4iKQpkZl9yYXcgPC0gaW5uZXJfam9pbihkZl9yYXcsIHdlaWdodF9yYXcsIGJ5ID0gIlNFUU4iKQpkZl9yYXcgPC0gaW5uZXJfam9pbihkZl9yYXcsIHNsZWVwX3JhdywgYnkgPSAiU0VRTiIpCmRmX3JhdyA8LSBpbm5lcl9qb2luKGRmX3JhdywgZGlhYmV0ZXNfcmF3LCBieSA9ICJTRVFOIikKZGZfcmF3IDwtIGlubmVyX2pvaW4oZGZfcmF3LCBpbmNvbWVfcmF3LCBieSA9ICJTRVFOIikKZGZfcmF3IDwtIGlubmVyX2pvaW4oZGZfcmF3LCBjaG9sX3JhdywgYnkgPSAiU0VRTiIpCmRpbShkZl9yYXcpCmBgYAojIyBTZWxlY3RpbmcgVmFyaWFibGVzOgpJIHdvdWxkIGxpa2UgdG8gdXNlIHRoZXNlIHZhcmlhYmxlcyBmb3IgbXkgc3R1ZHk6CgotIGBTRVFOYCBhcyB0aGUgcmVzcG9uZGVudCBzZXF1ZW5jZSwgd2hpY2ggaXMgdW5pcXVlIGZvciBlYWNoIHBhdGllbnQuIAotIGBCUFhPU1kxYCwgYEJQWE9TWTJgLCBgQlBYT1NZM2AgZGV0ZXJtaW5lIHRoZSAxc3QsIDJuZCwgYW5kIHRoaXJkIHN5c3RvbGljIHZhbHVlIG9mIG9zY2lsbG9tZXRyaWMgcmVhZGluZywgcmVzcGVjdGl2ZWx5LCBvcmlnbmlhbGx5IGZyb20gdGhlIEJsb29kIFByZXNzdXJlIC0gIE9zY2lsbG9tZXRyaWMgTWVhc3VyZW1lbnQgKFBfQlBYTykgdGliYmxlIChgYnBfcmF3YCkuIAotIGBCUFhPREkxYCwgYEJQWE9ESTJgLCBgQlBYT0RJM2AgZGV0ZXJtaW5lIHRoZSAxc3QsIDJuZCwgYW5kIHRoaXJkIHN5c3RvbGljIHZhbHVlIG9mIG9zY2lsbG9tZXRyaWMgcmVhZGluZywgcmVzcGVjdGl2ZWx5LCBvcmlnaW5hbGx5IGZyb20gdGhlIEJsb29kIFByZXNzdXJlIC0gIE9zY2lsbG9tZXRyaWMgTWVhc3VyZW1lbnQgKFBfQlBYTykgb3IgKGBicF9yYXdgKSB0aWJibGUuICAKLSBgU01RMDIwYCBmcm9tIFNtb2tpbmcgLSBDaWdhcmV0dGUgVXNlIChQX1NNUSkgb3IgYHNtb2tlX3Jhd2AgdGliYmxlIGFzIGlmIHRoZSBwYXJ0aWNpcGFudCBzbW9rZSBhYm91dCAxMDAgY2lnYXJldHRlcyBpbiB0aGVpciBsaXZlcy4gCi0gYFdIRDAxMGAgZnJvbSBXZWlnaHQgSGlzdG9yeSAoUF9XSFEpIG9yIGB3ZWlnaHRfcmF3YCB0aWJibGUgIGFzIHRoZSBzZWxmLXJlcG9ydCBoZWlnaHQgb2YgdGhlIHBhcnRpY2lwYW50cy4gCi0gYFdIRDAxMjBgIGZyb20gV2VpZ2h0IEhpc3RvcnkgKFBfV0hRKSBvciBgd2VpZ2h0X3Jhd2AgdGliYmxlICBhcyB0aGUgc2VsZi1yZXBvcnQgd2VpZ2h0IG9mIHRoZSBwYXJ0aWNpcGFudHMuCi0gYElOREZNTVBDYCBmcm9tIEluY29tZSAoUF9JTlEpIG9yIGBpbmNvbWVfcmF3YCB0aWJibGUgYXMgZmFtaWx5IG1vbnRobHkgcG92ZXJ0eSBsZXZlbCBjYXRlZ29yeQotIGBESVEwMTBgIGZyb20gRGlhYmV0ZXMgKFBfRElRKSBvciBgZGlhYmV0ZXNfcmF3YCB0aWJibGUgYXMgaWYgdGhlIHBlcnNvbiB3YXMgdG9sZCBieSBkb2N0b3IgdGhhdCB0aGV5IGhhdmUgZGlhYmV0ZXMuCi0gYFNMRDAxMmAgZnJvbSBTbGVlcCBEaXNvcmRlciAoUF9TTFEpIG9yIGBzbGVlcF9yYXdgIHRpYmJsZSBhcyBob3VycyBvZiBzbGVlcCBkdXJpbmcgd2Vla2RheXMgb2YgdGhlIHBhcnRpY2lwYW50cwotIGBTTEQwMTNgIGZyb20gU2xlZXAgRGlzb3JkZXIgKFBfU0xRKSBvciBgc2xlZXBfcmF3YCB0aWJibGUgYXMgaG91cnMgb2Ygc2xlZXAgZHVyaW5nIHdlZWtlbmRzIG9mIHRoZSBwYXJ0aWNpcGFudHMKLSBgQlBRMDgwYCBmcm9tIEJsb29kIFByZXNzdXJlICYgQ2hvbGVzdGVyb2wgKFBfQlBRKSBvciBgY2hvbF9yYXdgIHRpYmJsZSBhcyBiZWluZyB0b2xkIHRvIGhhdmUgaGlnaCBjaG9sZXN0ZXJvbCBsZXZlbAoKYGBge3J9CmRmIDwtIGRmX3Jhd1ssIGMoIlNFUU4iLCAiQlBYT1NZMSIsICJCUFhPU1kyIiwgIkJQWE9TWTMiLCAiQlBYT0RJMSIsICJCUFhPREkyIiwgIkJQWE9ESTMiLCAiU01RMDIwIiwgJ1dIRDAyMCcsICdXSEQwMTAnLCAnSU5ERk1NUEMnLCAnRElRMDEwJywgJ1NMRDAxMicsICdTTEQwMTMnLCAnQlBRMDgwJyldCmBgYAoKIyBDbGVhbmluZyB0aGUgRGF0YSAKIyMgUHJlLXByb2Nlc3NpbmcgRGF0YQpJIHdvdWxkIGxpa2UgdG8gY2hlY2sgdGhlIG1pc3NpbmcgdmFsdWVzIGZyb20gbXkgY3VycmVudCB0aWJibGUuIAoKYGBge3J9Cm1pc3NfdmFyX3N1bW1hcnkoZGYpIHw+IGthYmxlKCkKYGBgClRoZSBtaXNzaW5nIHBlcmNlbnRhZ2UgZm9yIGVhY2ggdmFyaWFibGUgaXMgcXVpdGUgc21hbGwuIFRoZXJlZm9yZSwgSSB3b3VsZCBsaWtlIHRvIHByZS1wcm9jZXNzIG15IGRhdGEgYnkgZmlsdGVyaW5nIG91dCB0aG9zZSBgbWlzc2luZ2AgdmFsdWUsIGBkb24na25vd2AgdmFsdWUsIGFuZCBgcmVmdXNlZGAgdmFsdWUuIFRoZXNlIHZhbHVlcyBhcmUgc3BlY2lmaWVkIGluIGVhY2ggdmFyaWFibGUgd2hlcmUgaXQgb3JpZ2luYWxseSBjb21lIGZyb20uIAoKVGhpcyBmb2xsb3dpbmcgc25pcHBldCB3b3VsZCB0byBmaWx0ZXIgb3V0IHRob3NlIG1pc3NpbmcgdmFsdWVzLiAKYGBge3J9CmRmIDwtIGRmW2NvbXBsZXRlLmNhc2VzKGRmKSxdCmBgYCAKClRoZW4sIEkgd291bGQgbGlrZSB0byBmaWx0ZXIgb3V0IHRoZSB2YWx1ZSBgNzc3N2AgYW5kIGA5OTk5YCBmcm9tIGBXSEQwMTBgIGFuZCBgV0hEMDIwYC4gVGhlc2UgdmFsdWVzIGFyZSBzdGF0ZWQgZm9yIHRoZSByZWZ1c2VkICg3Nzc3KSBhbmQgZG9uJ3Qga25vdyAoOTk5OSkgdmFsdWUuIApgYGB7cn0KZGYgPC0gZGZbZGYkV0hEMDEwIDwgNzc3NyxdCmRmIDwtIGRmW2RmJFdIRDAyMCA8IDc3NzcsXQpgYGAKCkkgd291bGQgbGlrZSB0byBmaWx0ZXIgb3V0IHZhbHVlcyBgN2AgYW5kIGA5YCBmcm9tIGBESVEwMTAxYC4gVGhlc2UgdmFsdWVzIGFyZSBzdGF0ZWQgZm9yIHRoZSByZWZ1c2VkICg3KSBhbmQgZG9uJ3Qga25vdyAoOSkgdmFsdWUuIApgYGB7cn0KZGYgPC0gZGZbZGYkRElRMDEwIDwgNyxdCmBgYAoKSSB3b3VsZCBsaWtlIHRvIGZpbHRlciBvdXQgdmFsdWVzIGA3YCBhbmQgYDlgIGZyb20gYFNNUTAyMGAuIFRoZXNlIHZhbHVlcyBhcmUgc3RhdGVkIGZvciB0aGUgcmVmdXNlZCAoNykgYW5kIGRvbid0IGtub3cgKDkpIHZhbHVlLiAKYGBge3J9CmRmIDwtZGZbZGYkU01RMDIwIDwgNyxdCmBgYAoKSSB3b3VsZCBsaWtlIHRvIGZpbHRlciBvdXQgdmFsdWVzIGA3YCBhbmQgYDlgIGZyb20gYElOREZNTVBDYC4gVGhlc2UgdmFsdWVzIGFyZSBzdGF0ZWQgZm9yIHRoZSByZWZ1c2VkICg3KSBhbmQgZG9uJ3Qga25vdyAoOSkgdmFsdWUuIApgYGB7cn0KZGYgPC0gZGZbZGYkSU5ERk1NUEMgPCA3LF0KYGBgCkZpbmFsbHksIEkgd291bGQgbGlrZSB0byBmaWx0ZXIgb3V0IHZhbHVlcyBgN2AgYW5kIGA5YCBmcm9tIGBCUFEwODAgYC4gVGhlc2UgdmFsdWVzIGFyZSBzdGF0ZWQgZm9yIHRoZSByZWZ1c2VkICg3KSBhbmQgZG9uJ3Qga25vdyAoOSkgdmFsdWUuIApgYGB7cn0KZGYgPC0gZGZbZGYkQlBRMDgwIDwgNyxdCmBgYApBZnRlciBhbGwsIHRoaXMgaXMgbXkgY3VycmVudCB0aWJibGUgd2l0aCA2ODM3IHJvd3MgYW5kIDE1IGNvbHVtbnMuIApgYGB7cn0KZGltKGRmKQpgYGAKCiMjIENoZWNraW5nIFF1YW50aXRhdGl2ZSBWYXJpYWJsZXMKYGBge3J9CmRmICU+JSBzZWxlY3QoQlBYT1NZMSwgQlBYT1NZMiwgQlBYT1NZMywgQlBYT0RJMSwgQlBYT0RJMiwgQlBYT0RJMywgV0hEMDEwLCBXSEQwMjAsIFNMRDAxMiwgU0xEMDEzKSAlPiUgbW9zYWljOjppbnNwZWN0KCkgCmBgYAoKYGBge3J9CmdsaW1wc2UoZGYpCgpgYGAKCiMjIENoZWNraW5nIG91ciBRdWFudGl0YXRpdmUgVmFyaWFibGVzCgpJbiB0aGlzIHN0dWR5LCBJIGhhdmUgMTAgcXVhbnRpdGF0aXZlIHZhcmlhYmxlcy5JIHdhbnQgdG8gY2hlY2sgdGhlIHJhbmdlIGZvciBlYWNoIG9mIHRoZW0gdG8gZW5zdXJlIHRoZXJlIGFyZSBubyBhYm5vcm1hbCB2YWx1ZS4KCmBgYHtyfQpkZiB8PgogIHNlbGVjdChCUFhPU1kxLCBCUFhPU1kyLCBCUFhPU1kzLCBCUFhPREkxLCBCUFhPREkyLCBCUFhPREkzLCBXSEQwMTAsIFdIRDAyMCwgU0xEMDEyLCBTTEQwMTMpIHw+CiAgbW9zYWljOjppbnNwZWN0KCkKYGBgCgpXZSBjYW4gc2VlIHRoYXQgdGhlIHJhbmdlIGZvciBlYWNoIHZhcmlhYmxlIHJlZ2FyZGluZyB0byBlYWNoIG1lYW5pbmcgYXJlIHF1aXRlIHJlYXNvbmFibGUuIFRoZXJlZm9yZSwgSSB3b3VsZCBsaWtlIHRvIHByb2NlZWQgaW50byBuZXh0IHN0ZXAuIAoKIyMjUHJvY2Vzc2luZyBIZWlnaHQgYW5kIFdlaWdodCBWYWx1ZSB0byBDYWxjdWxhdGUgdGhlIEJvZHkgTWFzcyBJbmRleCAoQk1JKQoKSW4gdGhpcyBzdHVkeSxpbnN0ZWFkIG9mIHVzaW5nIHdlaWdodCBhbmQgaGVpZ2h0LCBJIHdvdWxkIGxpa2UgdG8gdXNlIHRoZW0gZm9yIGNhbGN1bGF0aW5nIEJNSS4gVGhlIEJNSSBmb3JtdWxhIGZvciBpbmNoZXMgYW5kIHBvdW5kcyBpcyBhdmFpbGFibGUgYXQgaHR0cDovL3d3dy5ibWktY2FsY3VsYXRvci5uZXQvYm1pLWZvcm11bGEucGhwLiAKTm9ybWFsbHksIHRoZSBub3JtYWwgcmFuZ2UgZm9yIEJNSSB3b3VsZCBiZSByZWFzb25hYmxlIGZyb20gMTUgdG8gNTAuIEhvd2V2ZXIsIHNpbmNlIE5IQU5FUydzIHB1cnBvc2Ugd2FzIHRvIGNvbGxlY3QgZGF0YSBwcmV2YWxlbmNlIG9mIGNocm9uaWMgY29uZGl0aW9ucyBpbiB0aGUgcG9wdWxhdGlvbiBpbiB0aGUgVS5TLiBEdWUgdG8gdGhpcywgdGhlIHJhbmdlIGNhbiBiZSBnbyBvdmVyIG9yIHVuZGVyIHRoZSBub3JtYWwgcmVhc29uYWJsZSByYW5nZS4gVGhlcmVmb3JlLCBnZXR0aW5nIHZhbHVlcyBvZiBCTUkgYWJvdmUgNTAgb3Igc2xpZ2h0bHkgdW5kZXIgMTUgYXMgYmVsb3cgd291bGQgYmUgY29uc2lkZXJlZCBhcyBhY2NlcHRhYmxlLiBIb3dldmVyLCBpbiBvcmRlciB0byBjb25maXJtIGl0LCBJIHdvdWxkIGxpa2UgdG8gY2hlY2sgdGhlIHJhbmdlIHZhbHVlIGZvciB0aGUgaGVpZ2h0IGFuZCB3ZWlnaHQuIAoKYGBge3J9CmRmWydCTUknXSA8LWRmJFdIRDAyMCo3MDMvKGRmJFdIRDAxMCkqKjIKZGVzY3JpYmUofiBCTUksIGRhdGEgPSBkZikKYGBgCgoKQXMgd2UgY2FuIHNlZSBpbiBoZXJlLCB0aGUgcmFuZ2Ugb2Ygd2VpZ2h0IGFyZSBxdWl0ZSBiaWcgYXMgdGhlcmUgYXJlIHBlb3BsZSBnbyB1cCB0byA0NTcgbGJzLiBUaGUgdGFsbGVzdCBwZXJzb24gYWxzbyBoYXZlIHRoZSBoZWlnaHQgb2YgODIgaW5jaGVzICg2JyA4JycpLiBUaGVzZSBhcmUgcXVpdGUgcmFyZSBpbiBub3JtYWwsIGJ1dCBpZiB3ZSBjb25zaWRlciBhcyBzYW1wbGluZyBwZW9wbGUgaW4gdGhlIHBvcHVsYXRpb24sIHRoZXJlIHdvdWxkIGJlIHNvbWUgZXhjZXB0aW9uYWwgY2FzZXMuIFRoZXJlZm9yZSwgSSB3b3VsZCBsZWF2ZSB0aGVzZSB2YWx1ZXMgdG8gYmUgaW50YWN0LgoKYGBge3J9CmRmfD4KICBzZWxlY3QoV0hEMDIwLCBXSEQwMTApIHw+CiAgZGVzY3JpYmUoKQpgYGAKCiMjIyBBdmVyYWdlIHRoZSBTeXN0b2xpYyBhbmQgRGlhc3RvbGljIFZhbHVlczoKSW5zdGVhZCBvZiBnZXR0aW5nIDMgdmFsdWVzIGZvciBlYWNoIHN5c3RvbGljIGFuZCBkaWFzdG9saWMgcmVhZGluZywgSSB3b3VsZCBsaWtlIHRvIGF2ZXJhZ2UgdGhlbSBvdXQuCmBgYHtyfQpkZlsnc3lzdG9saWMnXSA8LSBhcy5udW1lcmljKGZvcm1hdChyb3VuZChyb3dNZWFucyhkZlssYygiQlBYT1NZMyIsIkJQWE9TWTIiLCJCUFhPU1kxIildKSwgMSksIG5zbWFsbCA9IDEpKQpkZlsnZGlhc3RvbGljJ10gPC0gYXMubnVtZXJpYyhmb3JtYXQocm91bmQocm93TWVhbnMoZGZbLGMoIkJQWE9ESTEiLCJCUFhPREkyIiwiQlBYT0RJMyIpXSksIDEpLCBuc21hbGwgPSAxKSkKYGBgCgpUaGVuLCBJIHdvdWxkIGxpa2UgdG8gc2VlIHRoZWlyIHJhbmdlLiBUaGUgbm9ybWFsIHBlb3BsZSBzaG91bGQgaGF2ZSBzeXN0b2xpYy9kaWFzdG9saWMgYXMgOTAvNjAuIEhvd2V2ZXIsIGZvciBzb21lIHBlb3BsZSB3aG8gaGF2ZSBhYm5vcm1hbCBibG9vZCBwcmVzc3VyZSAoaS5lIGh5cG90ZW5zaW9uLCBoaWdoIGJsb29kIHByZXNzdXJlLCBoeXBlcnRlbnNpb24pLCB0aGUgcmFuZ2UgY2FuIGJlIG91dCBvZiBzY29wZS4gQXMgdGhlcmUgYXJlIGNhc2VzIHRoYXQgcGVvcGxlIHdpdGggaHlwZXJ0ZW5zaW9uIGNyaXNpcyBjYW4gaGF2ZSBzeXN0b2xpYyB0byBiZSBvdmVyIDIwMC9kaWFzdG9saWMgb3ZlciAxMjAgb3IgaHlwb3RlbnNpb24gd2l0aCBzeXN0b2xpYyB0byBiZSB1bmRlciA5MC9kaWFzdG9saWMgdG8gYmUgdW5kZXIgNjAgKGJ1dCBvdmVyIDQwKSwgdGhlIHJhbmdlIGRlc2NyaWJlcyBiZWxvdyBhcmUgcmVhc29uYWJsZSB0byBiZSBrZWVwIGludGFjdC4gCmBgYHtyfQpkZiB8PgogIHNlbGVjdChzeXN0b2xpYywgZGlhc3RvbGljKSB8PgogIGRlc2NyaWJlKCkKYGBgCgojIyMgQXZlcmFnZSBIb3VycyBvZiBTbGVlcApJbnN0ZWFkIG9mIGhhdmluZyBob3VycyBvZiBzbGVlcCBkdXJpbmcgdGhlIHdlZWtkYXlzIGFuZCB3ZWVrZW5kcyBzZXBhcmF0ZWx5LCBJIHdvdWxkIGxpa2UgdG8ga2VlcCBvbmUgdmFsdWUgYnkgYXZlcmFnZSB0aGVzZSB0d28gaG91cnMuIApgYGB7cn0KZGZbJ1NsZWVwSG91cnMnXSA8LSBhcy5udW1lcmljKGZvcm1hdChyb3VuZChyb3dNZWFucyhkZlssYygnU0xEMDEyJywgJ1NMRDAxMycpXSksIDEpLCBuc21hbGwgPSAxKSkKYGBgCgpUaGVuLCBJIHdvdWxkIGxpa2UgdG8gY2hlY2sgdGhlIHJhbmdlIG9mIHRoaXMgdmFsdWVzLiBJbiB0aGUgb3JpZ2luYWwgc3R1ZHksIHRoZSBob3VycyBvZiBzbGVlcCBnb2luZyBmcm9tIDIgdG8gMTQuIFRoZW4sIHRoZSBhdmVyYWdlIHJhbmdlLCBpZiBub3JtYWwsIHNob3VsZCBiZSB3aXRoaW4gdGhpcyBzY29wZS4gCmBgYHtyfQpkZiB8PgogIHNlbGVjdChTbGVlcEhvdXJzKSB8PgogIGRlc2NyaWJlKCkKYGBgCgojIyBDaGVja2luZyBCaW5hcnkgVmFyaWFibGVzCk15IGJpbmFyeSB2YWlyYWJsZXMgaW5jbHVkaW5nIGBTTVEwMjBgIGFuZCBgQlBRMDgwYAojIyMgRm9yIGBTTVEwMjBgIFZhcmlhYmxlcwpgYGB7cn0KZGYgfD4gc2VsZWN0KFNNUTAyMCkgfD4gZ2xpbXBzZSgpCmBgYApGcm9tIHRoZSBvcmlnaW5hbCB0aWJibGUsICAgYDFgIGRlbm90ZXMgWWVzLCBhbmQgYDJgIGRlbm90ZXMgTm8uIFRoZXJlZm9yZSwgSSB3b3VsZCBsaWtlIHRvIGNoYW5nZSBpdCBpbnRvIFllcy9ObyB2YWx1ZSBhbmQgZmFjdG9yIGl0LiAKCmBgYHtyfQpkZiA8LSBkZiAlPiUgbXV0YXRlKHNtb2tpbmcgPSBmY3RfcmVjb2RlKGZhY3RvcihTTVEwMjApLCAiWWVzIiA9ICIxIiwgIk5vIiA9ICIyIikpCmRmICB8PiBzZWxlY3Qoc21va2luZykgfD4gc3VtbWFyeSgpCmBgYAoKVGhlcmUgYXJlIG5vIHZhbHVlcyB0aGF0IG91dCBvZiByYW5nZS4gCgojIyMgRm9yIGBCUFEwODBgIFZhcmlhYmxlcwpgYGB7cn0KZGYgfD4gc2VsZWN0KEJQUTA4MCkgfD4gZ2xpbXBzZSgpCmBgYApGcm9tIHRoZSBvcmlnaW5hbCB0aWJibGUsICAgYDFgIGRlbm90ZXMgWWVzLCBhbmQgYDJgIGRlbm90ZXMgTm8uIFRoZXJlZm9yZSwgSSB3b3VsZCBsaWtlIHRvIGNoYW5nZSBpdCBpbnRvIFllcy9ObyB2YWx1ZSBhbmQgZmFjdG9yIGl0LiAKCmBgYHtyfQpkZiA8LSBkZiAlPiUgbXV0YXRlKGhpZ2hjaG9sID0gZmN0X3JlY29kZShmYWN0b3IoQlBRMDgwKSwgIlllcyIgPSAiMSIsICJObyIgPSAiMiIpKQpkZiAgfD4gc2VsZWN0KGhpZ2hjaG9sKSB8PiBzdW1tYXJ5KCkKYGBgClRoZXJlIGFyZSBubyB2YWx1ZXMgdGhhdCBvdXQgb2YgcmFuZ2UuIAoKIyMgQ2hlY2tpbmcgTXVsdGktQ2F0ZWdvcnkgVmFyaWFibGVzCkkgaGF2ZSB0d28gbXVsdGktY2F0ZWdvcnkgdmFyaWFibGVzOiBgSU5ERk1NUENgLCBhbmQgYERJUTAxMGAuIEkgd2lsbCBmaXJzdCBjaGVjayB0aGVtIGlmIHRoZXkgaGF2ZSBhbnkgc3VycHJpc2luZyB2YWx1ZXMgYW5kIHRoZXkgd2lsbCByZW5hbWUgYW5kIGZhY3RvciBpdCB0byBtaXJyb3Igd2hhdCB3ZSBuZWVkIGluIG91ciBhbmFseXNlcy4KCiMjIFRoZSBgSU5ERk1NUENgIHZhaXJhYmxlCkZpcnN0LCBJIHdvdWxkIHRha2UgYSBsb29rIGF0IHRoaXMgdmFyaWFibGUuCmBgYHtyfQpkZiB8PiB0YWJ5bChJTkRGTU1QQykgfD4ga2FibGUoKQpgYGAKCkZvciB0aGlzIHZhcmlhYmxlLCBJIHdvdWxkIGxpa2UgdG8gY3JlYXRlIGEgbmV3IGZhY3RvciBjYWxsZWQgYGVjb19zdGF0dXNgd2hpY2ggaXMgYSBmYWN0b3Igd2hpY2ggaGFzIHNwZWNpYWxseSBsZXZlbCBuYW1lczogYDFgIGFzIGBMb3dlciwgYDJgIGFzIGBNaWRkbGVgLCBhbmQgYDNgIGZvciBgVXBwZXJgLiAKYGBge3J9CmRmIDwtIGRmICU+JSBtdXRhdGUoZWNvX3N0YXR1cyA9IGZjdF9yZWNvZGUoZmFjdG9yKElOREZNTVBDKSwgIkxvd2VyIiA9ICIxIiwgIk1pZGRsZSIgPSAiMiIsICJVcHBlciIgPSAiMyIpKQpgYGAKCkFmdGVyIHRoYXQsIEkgd291bGQgY2hlY2sgaWYgSSBmYWN0b3IgdGhlbSBjb3JyZWN0bHkuCmBgYHtyfQpkZiB8PiBjb3VudChJTkRGTU1QQywgZWNvX3N0YXR1cykgfD4ga2FibGUoKQpgYGAKCiMjIFRoZSBgRElRMDEwYCBWYWlyYWJsZQpGaXJzdCwgSSB3b3VsZCB0YWtlIGEgbG9vayBhdCB0aGlzIHZhcmlhYmxlLgpgYGB7cn0KZGYgfD4gdGFieWwoRElRMDEwKSB8PiBrYWJsZSgpCmBgYAoKRm9yIHRoaXMgdmFyaWFibGUsIEkgd291bGQgbGlrZSB0byBjcmVhdGUgYSBuZXcgZmFjdG9yIGNhbGxlZCBgZGlhYmV0ZXNgd2hpY2ggaXMgYSBmYWN0b3Igd2hpY2ggaGFzIHNwZWNpYWxseSBsZXZlbCBuYW1lczogYDFgIGFzIGBZZXNgLCBgMmAgYXMgYE5vYCwgYW5kIGAzYCBmb3IgYEJvcmRlcmxpbmVgLiAKYGBge3J9CmRmIDwtIGRmICU+JSBtdXRhdGUoZGlhYmV0ZXMgPSBmY3RfcmVjb2RlKGZhY3RvcihESVEwMTApLCAiWWVzIiA9ICIxIiwgIk5vIiA9ICIyIiwgIkJvcmRlcmxpbmUiID0gIjMiKSkKYGBgCgpBZnRlciB0aGF0LCBJIHdvdWxkIGNoZWNrIGlmIEkgZmFjdG9yIHRoZW0gY29ycmVjdGx5LgpgYGB7cn0KZGYgfD4gY291bnQoRElRMDEwLCBkaWFiZXRlcykgfD4ga2FibGUoKQpgYGAKCiMjIENyZWF0aW5nIEFuYWx5dGljIFRpYmxlcyAKU28gb3VyIGFuYWx5dGljIHRpYmJsZSwgd2hpY2ggSSB3aWxsIGNhbGwgYXMgYGRmYCBzaG91bGQgY29udGFpbnMgNyB2YWx1ZXMgYWZ0ZXIgcHJvY2Vzc2luZy4gCgpgYGB7cn0KZGYgPC0gZGYgJT4lIHNlbGVjdChTRVFOLHN5c3RvbGljLCBkaWFzdG9saWMsIEJNSSwgU2xlZXBIb3Vycywgc21va2luZywgZWNvX3N0YXR1cywgZGlhYmV0ZXMsIGhpZ2hjaG9sKQpgYGAKCiMjIExpc3Qgb2YgTWlzc2luZyBWYWx1ZXMKQXMgSSBhbHJlYWR5IGZpbHRlciB0aG9zZSBkb24ndCBrbm93L21pc3NpbmcgdmFsdWVzIGFib3ZlLCBvdXIgYW5hbHl0aWMgdGliYmxlIHNob3VsZCBub3QgY29udGFpbiBhbnkgb2YgdGhlc2UgdmFsdWVzCmBgYHtyfQptaXNzX3Zhcl9zdW1tYXJ5KGRmKSB8PiBrYWJsZSgpCmBgYAojIENvZGVib29rIGFuZCBEYXRhIERlc2NyaXB0aW9uCgojIyBDb2RlYm9vawpUaGUgOSB2YXJpYWJsZXMgb2YgbXkgYW5hbHl0aWMgdGliYmxlIHdvdWxkIGJlIGRlc2NyaWJlZCBiZWxvdy4gVGhlIFR5cGUgY29sdW1uIGluZGljYXRlcyB0aGUgbnVtYmVyIG9mIGxldmVscyBpbiBlYWNoIGNhdGVnb3JpY2FsIChmYWN0b3IpIHZhcmlhYmxlLiBBcyBmb3IgdGhlIFR5cGUgaW5mb3JtYXRpb24sIEnigJltIHVzaW5nIFF1YW50IHRvIGluZGljYXRlIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMsIGFuZCBDYXQteCBpbmRpY2F0ZXMgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSAoZmFjdG9yKSB3aXRoIHggbGV2ZWxzLgoKVmFyaWFibGV8IFR5cGUgfCBEZXNjcmlwdGlvbi9MZXZlbHMKLS0tLS0tLS18LS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0KU0VRTiB8IGlkIHwgcmVzcG9uZGVudCBzZXF1ZW5jZSwgdW5pcXVlIGZvciBlYWNoIHBhcnRpY2lwYW50cy4gCnN5c3RvbGljIHwgUXVhbnQgfCAqKm91dGNvbWUqKiB2YXJpYWJsZSwgQXZlcmFnZSBvZiBzeXN0b2xpYyByZWFkaW5ncwpkaWFzdG9saWMgfCBRdWFudCB8IEF2ZXJhZ2Ugb2YgZGlhc3RvbGljIHJlYWRpbmdzCkJNSSB8IFF1YW50IHwgKiprZXkgcHJlZGljdG9yKiogQm9keSBNYXNzIEluZGV4ClNsZWVwSG91cnMgfCBRdWFudCB8IEF2ZXJhZ2Ugc2xlZXBpbmcgaG91cnMgYSB3ZWVrCnNtb2tpbmcgfCBDYXQgLSAyIHwgWWVzLCBObzogRGlkIHlvdSBzbW9rZSAxMDAgY2lnYXJldHRlcyBpbiBsaWZlPwplY29fc3RhdHVzIHwgQ2F0IC0gM3wgTG93ZXIgQ2xhc3MsIE1pZGRsZSBDbGFzcywgVXBwZXIgQ2xhc3M6IApkaWFiZXRlcyB8IENhdCAtIDN8IFllcywgTm8sIEJvcmRlcmxpbmU6IEhhdmUgZG9jdG9yIHRvbGQgeW91IHRoYXQgeW91IGhhdmUgZGlhYmV0ZXM/CmhpZ2hjaG9sIHwgQ2F0IC0gMiB8IFllcywgTm86IEhhdmUgZG9jdG9yIHRvbGQgeW91IHRoYXQgeW91IGhhdmUgaGlnaCBjaG9sZXN0ZXJvbD8KCgojIyBBbmFseXRpYyBUaWJsZQpgYGB7cn0KZGYKYGBgCgpgYGB7cn0KaXNfdGliYmxlKGRmKQpgYGAKCiMjIERhdGEgU3VtbWFyeSAKYGBge3J9CkhtaXNjOjpkZXNjcmliZShkZiB8PiBzZWxlY3QoLVNFUU4pKQpgYGAKClNpbmNlIEkgYWxyZWFkeSBmaWx0ZXIgb3V0IGFsbCBtaXNzaW5nIHZhbHVlcyBhbmQgb3RoZXIgbm9pc3kgb25lcywgaXQgc2hvdWxkIHJldHVybiBubyBtaXNzaW5nIHZhbHVlcyBmb3IgZWFjaCB2YXJpYWJsZS4gCgojIE15IFJlc2VhcmNoIFF1ZXN0aW9uCkZyb20gQ2VudGVyIG9mIERpc2Vhc2UgQ29udHJvbCBhbmQgUHJldmVudGlvbiAoQ0RDKSwgbmVhcmx5IGhhbGYgb2YgYWR1bHRzIGluIHRoZSBVLlMgKDQ3JSBvciAxMTYgbWlsbGlvbiBwZW9wbGUpIGhhdmUgaHlwZXJ0ZW5zaW9uLCB3aGljaCBpcyBkZWZpbmVkIGFzIHN5c3RvbGljIGJsb29kIHByZXNzdXJlIGdyZWF0ZXIgdGhhbiAxMzAgbW1IZyBvciBkaWFzdG9saWMgYmxvb2QgcHJlc3N1cmUgZ3JlYXRlciB0aGFuIDgwIG1tSGcuICBIYXZpbmcgaHlwZXJ0ZW5zaW9uIHB1dHMgeW91IGF0IHJpc2sgZm9yIGhlYXJ0IGRpc2Vhc2UgYW5kIHN0cm9rZSwgd2hpY2ggYXJlIHRvcCAxMCBvZiBsZWFkaW5nIGNhdXNlcyBvZiBkZWF0aCBpbiB0aGUgVS5TLgoKVGhlcmUgYXJlIHZhcmlvdXMgb2YgZmFjdG9ycyB0aGF0IGNhbiBsZWFkIHRvIGhpZ2ggYmxvb2QgcHJlc3N1cmUsIHN1Y2ggYXMgYWdlLCByYWNlLCBmYW1pbHkgaGlzdG9yeSwgcGh5c2ljYWwgYWN0aXZpdGllcywgd2VpZ2h0LCBzbW9raW5nLCBzdHJlc3MsIGNlcnRhaW4gY2hyb25pYyBjb25kaXRpb24sIHNsZWVwIGRpc29yZGVyLiBUaGVyZWZvcmUsIGluIHRoaXMgc3R1ZHksIEkgd291bGQgbGlrZSB0byBjaG9vc2Ugc2VydmVyYWwgcmlzayBmYWN0b3JzIGluIG9yZGVyIHRvIGludmVzdGlnYXRlIGFib3V0IGJsb29kIHByZXNzdXJlLCB3aGljaCBpbmNsdWRlczogd2VpZ2h0LCBzbW9rZSAoY2lnYXJldHRlcyksIGluY29tZSwgc2xlZXAgZGlzb3JkZXIsIGFuZCBkaWFiZXRlcyBmcm9tIHRoZSBkYXRhc2V0IGZyb20gTmF0aW9uYWwgQ2VudGVyIG9mIEhlYWx0aCBTdGF0aXN0aWNzIGR1cmluZyAyMDE3IHRvIDIwMjAgYmVmb3JlIHRoZSBwYW5kZW1pYy4gCgpJbiBhZGRpdGlvbiwgaW4gcmVhbGl0eSwgdG8gYWRkcmVzcyBvbmUncyBibG9vZCBwcmVzc3VyZSwgdGhlIGhlYXRsaGNhcmUgcHJvZmVzc2lvbmFscyB3b3VsZCB1c2UgYm90aCBzeXN0b2xpYyBhbmQgZGlhc3RvbGljIHJlYWRpbmdzLiBIb3dldmVyLCBzaW5jZSBJIGNhbiBvbmx5IHVzZSBvbmUgb3V0Y29tZSwgSSB3b3VsZCBsaWtlIHRvIHVzZSBzeXN0b2xpYyBvdmVyIGRpYXN0b2xpYyBhcyBvdXRjb21lIGZvciBteSBzdHVkeS4gQm90aCBvZiB0aGVzZSB2YWx1ZXMgYXJlIGVxdWFsbHkgaW1wb3J0YW50LCBob3dldmVyLCB0aGVyZSBhcmUgc29tZSBzdHVkeSBzaG93ZWQgdGhhdCBwZW9wbGUgd2hvIGhhdmUgaGlnaGVyIHJpc2sgb2YgaGVhcnQgYXR0YWNrIGFuZCBzdHJva2Ugd291bGQgbGlrZWx5IHRvIGhhdmUgaGlnaGVyIHN5c3RvbGljIHJlYWRpbmdzLiBpbiBhZGRpdGlvbiwgZm9yIHBlb3BsZSB3aG8gYXJlIHN0aWxsIGluIHRoZSBlbGV2YXRlZCBzdGF0ZSBvZiBibG9vZCBwcmVzc3VyZSwgdGhleSBoYXZlIGhpZ2hlciBzeXN0b2xpYyByZWFkaW5ncyBub3QgZGlhc3RvbGljIHJlYWRpbmcgY29tcGFyaW5nIHRvIG5vcm1hbCBwZXJzb24uIER1ZSB0byB0aGlzLCBoYXZpbmcgc3lzdG9saWMgd291bGQgYmUgbW9yZSB1c2VmdWwgYW5kIG1vcmUgbm90aWNlYWJsZSBpbiBvdXIgc3R1ZHkuIAoKVGhpcyBsZWFkcyB0byB0aGUgcmVzZWFyY2ggb2YgbXkgc3R1ZHk6CgoqKkhvdyBlZmZlY3RpdmVseSBjYW4gd2UgcHJlZGljdCB0aGUgaGlnaCBibG9vZCBwcmVzc3VyZSB2aWEgc3lzdG9saWMgcmVhZGluZyB1c2luZyBCTUksIGFuZCBpcyB0aGUgcXVhbGl0eSBvZiBwcmVkaWN0aW9uIG1lYW5pbmdmdWxseSB3aGVuIEkgYWRqdXN0IGZvciBvdGhlciBwcmVkaWN0b3JzIChzbGVlcCBob3VycywgZGlhYmV0ZXMsIGluY29tZSwgc21va2luZywgYW5kIGNob2xlc3Rlcm9sKSBiYXNlZCBvbiBteSBkYXRhPyoqCgojIFBhcml0aW9uaW5nIHRoZSBEYXRhIApIZXJlLCBJIHdpbGwgb2J0YWluIGEgdHJhaW5pbmcgc2FtcGxlIHdpdGggYSByYW5kb21seSBzZWxlY3RlZCA3MCUgb2YgdGhlIGRhdGEsIGFuZCBoYXZlIHRoZSByZW1haW5pbmcgMzAlIGluIGEgdGVzdCBzYW1wbGUsIHByb3Blcmx5IGxhYmVsZWQsIGFuZCB1c2luZyBgc2V0LnNlZWRgIHNvIHRoYXQgdGhlIHJlc3VsdHMgY2FuIGJlIHJlcGxpY2F0ZWQgbGF0ZXIuCgpJIHdpbGwgY2FsbCB0aGUgdHJhaW5pbmcgc2FtcGxlIGBkZl90cmFpbmluZ2AgYW5kIHRoZSB0ZXN0IHNhbXBsZSBgZGZfdGVzdGAuCgotIFRoZSBgc2xpY2Vfc2FtcGxlYCBmdW5jdGlvbiB3aWxsIHNhbXBsZSB0aGUgc3BlY2lmaWVkIHByb3BvcnRpb24gb2YgdGhlIGRhdGEuCi0gVGhlIGBhbnRpX2pvaW5gIGZ1bmN0aW9uIHJldHVybnMgYWxsIHJvd3MgaW4gdGhlIGZpcnN0IGRhdGEgZnJhbWUgKGhlcmUgc3BlY2lmaWVkIGFzIGBkZl9hbmFseXRpY2ApIHRoYXQgYXJlIG5vdCBpbiB0aGUgc2Vjb25kIGRhdGEgZnJhbWUgKGhlcmUgc3BlY2lmaWVkIGFzIGBkZl90cmFpbmluZ2ApIGFzIGFzc2Vzc2VkIGJ5IHRoZSByb3ctc3BlY2lmaWMgaWRlbnRpZmljYXRpb24gY29kZSAoaGVyZSBgU0VRTmApKS4KCmBgYHtyfQpzZXQuc2VlZCg0MikKZGZfYW5hbHl0aWMgPC0gZGYgCmRmX3RyYWluaW5nIDwtIGRmX2FuYWx5dGljIHw+IHNsaWNlX3NhbXBsZShwcm9wID0gLjcwKQpkZl90ZXN0IDwtIGFudGlfam9pbihkZl9hbmFseXRpYywgZGZfdHJhaW5pbmcsIGJ5ID0gIlNFUU4iKQpkaW0oZGZfYW5hbHl0aWMpCmRpbShkZl90cmFpbmluZykKZGltKGRmX3Rlc3QpCmBgYApTaW5jZSA0Nzg1ICsgMjA1MiA9IDY4MzcsIHdlIHNob3VsZCBiZSBmaW5lLiAKIyBUcmFuc2Zvcm1pbmcgdGhlIE91dGNvbWUKCiMjIFZpc3VhbGl6aW5nIHRoZSBPdXRjb21lIERpc3RyaWJ1dGlvbgpJbiBvcmRlciB0byBhZGRyZXNzIHRoZSBkaXN0cmlidXRpb24gb2YgbXkgb3V0Y29tZXMgKGBzeXN0b2xpY2ApCmBgYHtyfQpwMSA8LSAgZ2dwbG90KGRmX3RyYWluaW5nLCBhZXMoc2FtcGxlID0gc3lzdG9saWMpKSArCiAgZ2VvbV9xcSgpICsgIyBwbG90IHRoZSBwb2ludHMKICBnZW9tX3FxX2xpbmUoY29sID0gImJsdWUiKSArICMgcGxvdCB0aGUgWSA9IFggbGluZQogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsgIyBtYWtlIHRoZSBwbG90IHNxdWFyZQogIGxhYnModGl0bGUgPSAiTm9ybWFsIFEtUSBwbG90OiBVbnRyYW5zZm9ybWVkIFN5c3RvbGljIikKcDIgPC0gZ2dwbG90KGRmX3RyYWluaW5nLCBhZXMoeD1zeXN0b2xpYykpICsKICAgICAgZ2VvbV9oaXN0b2dyYW0oYWVzKHk9c3RhdChkZW5zaXR5KSksIGJpbnMgPSAyMCwgZmlsbCA9InJveWFsYmx1ZSIsIGNvbCA9IndoaXRlIikgKwogICAgICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGRub3JtLCBhcmdzID0gbGlzdChtZWFuID0gbWVhbihkZl90cmFpbmluZyRzeXN0b2xpYyksIHNkID0gc2QoZGZfdHJhaW5pbmckc3lzdG9saWMpKSwgY29sID0icmVkIiwgbHdkID0gMS41KSArCiAgICAgIGxhYnModGl0bGU9IkRlbnNpdHkgRnVuY3Rpb246IFVudHJhbnNmb3JtZWQgU3lzdG9saWMiKQpwMyA8LSBnZ3Bsb3QoZGZfdHJhaW5pbmcsIGFlcyh4ID0gc3lzdG9saWMsIHkgPSAiIikpICsKICAgICAgZ2VvbV9ib3hwbG90KGZpbGwgPSAicm95YWxibHVlIiwKICAgICAgb3V0bGllci5jb2xvciA9ICJyb3lhbGJsdWUiKSArCiAgICAgIGxhYnModGl0bGUgPSAiQm94cGxvdDogVW50cmFuc2Zvcm1lZCBTeXN0b2xpYyIsIHkgPSAiIikKcDEgKyAocDIgLyBwMyArIHBsb3RfbGF5b3V0KGhlaWdodHMgPSBjKDQsMSkpKQpgYGAKV2UgY2FuIHNlZSB0aGF0IHRoZSBkaXN0cmlidXRpb24gb2Ygb3VyIG91dGNvbWUgaXMgY2xlYXJseSByaWdodC1za2V3ZWQuIFRoZXJlZm9yZSwgaXQgaXMgYmV0dGVyIHRvIGRvIHNvbWUgdHJhbnNmb3JtYXRpb24gZm9yIHRoZSBvdXRjb21lLiBJbiBvcmRlciB0byBkbyB0aGlzLCBJIHdvdWxkIGxpa2UgdG8gdXNlIGBib3hDb3hgCgojIyBgYm94Q294YCBmdW5jdGlvbiB0byBhc3Nlc3MgbmVlZCBmb3IgdHJhbnNmb3JtYXRpb24gb2Ygb3VyIG91dGNvbWUKSW4gb3JkZXIgdG8gdXNlIHRoZSBib3hDb3ggZnVuY3Rpb24sIEkgbmVlZCB0byBlbnN1cmUgdGhhdCBteSBvdXRjb21lLCBgc3lzdG9saWNgLCBpbmNsdWRpbmcgc3RyaWN0bHkgcG9zaXRpdmUgdmFsdWVzLiBXZSBjYW4gc2VlIHRoYXQgZnJvbSBiZWxvdywgdGhlIG1pbmltdW0gdmFsdWUgZm9yIG91ciBvdXRjb21lIGlzIDc4LjcsIHNvIHdlIGFyZSBnb29kIHRvIGdvIHRvIG5leHQgc3RlcC4KCmBgYHtyfQptb3NhaWM6OmZhdnN0YXRzKH4gc3lzdG9saWMsIGRhdGEgPSBkZl90cmFpbmluZykgfD4ga2FibGUoKQpgYGAKCgpgYGB7cn0KbW9kXzAgPC0gbG0oc3lzdG9saWMgfiBCTUkgKyBTbGVlcEhvdXJzICsgc21va2luZyArIGRpYWJldGVzICsgZWNvX3N0YXR1cyArIGhpZ2hjaG9sLCBkYXRhID0gZGZfdHJhaW5pbmcpCmJveENveChtb2RfMCkKYGBgCgpgYGB7cn0KcG93ZXJUcmFuc2Zvcm0obW9kXzApCmBgYAoKV2UgY2FuIHNlZSB0aGF0IGZyb20gdGhlIGJveENveCBmdW5jdGlvbiBhbmQgdGhlIGVzdGltYXRlZCB0cmFuc2Zvcm1hdGlvbiBwYXJhbWV0ZXIsIGl0IHN1Z2dlc3RlZCBhIHZhbHVlIG9mIG5lYXJseSAtMSwgd2hpY2ggbG9va3MgdGhlIGludmVyc2Ugb2YgdGhlIGBzeXN0b2xpY2AuIFRoZXJlZm9yZSwgSSB3b3VsZCBsaWtlIHRvIHJlLWludmVzdGlnYXRlIHRoZSBkaXN0cmlidXRpb24gaWYgSSBjaG9vc2UgdG8gZG8gdGhlIHRyYW5zZm9ybWF0aW9uIGZvciBteSBvdXRjb21lLiAKYGBge3J9CnAxIDwtICBnZ3Bsb3QoZGZfdHJhaW5pbmcsIGFlcyhzYW1wbGUgPSAxL3N5c3RvbGljKSkgKwogIGdlb21fcXEoKSArICMgcGxvdCB0aGUgcG9pbnRzCiAgZ2VvbV9xcV9saW5lKGNvbCA9ICJibHVlIikgKyAjIHBsb3QgdGhlIFkgPSBYIGxpbmUKICB0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArICMgbWFrZSB0aGUgcGxvdCBzcXVhcmUKICBsYWJzKHRpdGxlID0gIk5vcm1hbCBRLVEgcGxvdDogSW52ZXJzZSBTeXN0b2xpYyIpCnAyIDwtIGdncGxvdChkZl90cmFpbmluZywgYWVzKHg9MS9zeXN0b2xpYykpICsKICAgICAgZ2VvbV9oaXN0b2dyYW0oYWVzKHk9c3RhdChkZW5zaXR5KSksIGJpbnMgPSAyMCwgZmlsbCA9InJveWFsYmx1ZSIsIGNvbCA9IndoaXRlIikgKwogICAgICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGRub3JtLCBhcmdzID0gbGlzdChtZWFuID0gbWVhbigxL2RmX3RyYWluaW5nJHN5c3RvbGljKSwgc2QgPSBzZCgxL2RmX3RyYWluaW5nJHN5c3RvbGljKSksIGNvbCA9InJlZCIsIGx3ZCA9IDEuNSkgKwogICAgICBsYWJzKHRpdGxlPSJEZW5zaXR5IEZ1bmN0aW9uOiBJbnZlcnNlIFN5c3RvbGljIikKcDMgPC0gZ2dwbG90KGRmX3RyYWluaW5nLCBhZXMoeCA9IDEvc3lzdG9saWMsIHkgPSAiIikpICsKICAgICAgZ2VvbV9ib3hwbG90KGZpbGwgPSAicm95YWxibHVlIiwKICAgICAgb3V0bGllci5jb2xvciA9ICJyb3lhbGJsdWUiKSArCiAgICAgIGxhYnModGl0bGUgPSAiQm94cGxvdDogSW52ZXJzZSBTeXN0b2xpYyIsIHkgPSAiIikKcDEgKyAocDIgLyBwMyArIHBsb3RfbGF5b3V0KGhlaWdodHMgPSBjKDQsMSkpKQpgYGAKV2UgY291bGQgc2VlIHRoYXQgZnJvbSB0aG9zZSBncmFwaHMsIGFmdGVyIHRoZSB0cmFuc2Zvcm1hdGlvbiwgdGhlIG91dGNvbWUgbG9va3MgdG8gYmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuIFRoZXJlZm9yZSwgaW4gbXkgc3R1ZHksIEkgd291bGQgbGlrZSB0byBkbyB0aGUgdHJhbnNmb3JtYXRpb24gZm9yIHRoZSBvdXRjb21lLiAKCiMjICBOdW1lcmljYWwgU3VtbWFyeSBvZiB0aGUgT3V0Y29tZQpUaGlzIGlzIHRoZSBudW1lcmljYWwgc3VtbWFyeSBvZiB0aGUgb3V0Y29tZSBiZWZvcmUgdGhlIHRyYW5zZm9ybWF0aW9uCmBgYHtyfQptb3NhaWM6OmZhdnN0YXRzKH4gc3lzdG9saWMsIGRhdGEgPSBkZl90cmFpbmluZykgfD4ga2FibGUoKQpgYGAKCkFuZCB0aGlzIGlzIHRoZSBudW1lcmljYWwgc3VtbWFyeSBvZiB0aGUgb3V0Y29tZSBhZnRlciB0aGUgdHJhbnNmb3JtYXRpb24KCmBgYHtyfQptb3NhaWM6OmZhdnN0YXRzKH4gMS9zeXN0b2xpYywgZGF0YSA9IGRmX3RyYWluaW5nKSB8PiBrYWJsZSgpCmBgYAojIyBOdW1lcmljYWwgU3VtbWFyaWVzIG9mIHRoZSBQcmVkaWN0b3JzCkkgd291bGQgbGlrZSB0byBzZWUgc29tZSBudW1lcmljYWwgc3VtbWFyaWVzIGZvciBteSBwcmVkaWN0b3IgdmFyaWFibGUgaW4gdGhlIHRyYWluaW5nIGRhdGEKCmBgYHtyfQpkZl90cmFpbmluZyB8PiBzZWxlY3QoLVNFUU4sIC1zeXN0b2xpYywgLWRpYXN0b2xpYykgfD4gCiAgbW9zYWljOjppbnNwZWN0KCkKYGBgCgojIyBTY2F0dGVycGxvdCBNYXRyaXgKVGhlbiwgaGVyZSBJIHdvdWxkIGxpa2UgdG8gYnVpbGQgc2NhdHRlcnBsb3QgbWF0cmljZXMgdG8gaW52ZXN0aWdhdGUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIG91ciBvdXRjb21lIGFuZCB0aGUgcHJlZGljdG9ycy4gVGhlIGZpcnN0IG1hdHJpeCBzaG91bGQgaW5jbHVkZSBhbGwgdGhlIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMsIHdoaWxlIHRoZSBzZWNvbmQgb25lIGluY2x1ZGVzIGNhdGVnb3JpY2FsIHZhcmlhYmxlcy4KClRoaXMgcGxvdCBpcyB0aGUgb25lIGJldHdlZW4gb3VyIG91dGNvbWUgYW5kIGl0cyBxdWFudGl0YXRpdmUgcHJlZGljdG9ycy4gV2UgY2FuIHNlZSB0aGF0IHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIG91ciBvdXRjb21lICggYDEvc3lzdG9saWNgKSBhbmQgdGhlIHByZWRpY3RvcnMgYXJlIHJlbGF0aXZlbHkgbG93LCBhcyBpdCBpcyBvbmx5IC0wLjA0OCBhbmQgMC4wODAuIEhvd2V2ZXIsIGNvbXBhcmluZyB0byB0aGUgdmFsdWVzIHRoYXQgSSBnb3QgZHVyaW5nIG15IHByZXNlbnRhdGlvbiwgdGhpcyBhbHJlYWR5IGltcHJvdmVkLiAKSW4gYWRkaXRpb24gdG8gdGhhdCwgdGhlIGRpc3RyaWJ1dGlvbiBvZiBteSBrZXkgcHJlZGljdG9yIGlzIGFsc28gcmlnaHQtc2tld2VkLiAKYGBge3IgbWVzc2FnZT1GQUxTRX0KZGY0X3RyYWluaW5nIDwtIG11dGF0ZShkZl90cmFpbmluZywgc3lzdG9saWMgPSAxL3N5c3RvbGljKQp0ZW1wIDwtICBkZjRfdHJhaW5pbmcgfD4gCiAgc2VsZWN0KHN5c3RvbGljLCAgQk1JLCBTbGVlcEhvdXJzKSAKCmdncGFpcnModGVtcCwgdGl0bGUgPSAiU2NhdHRlcnBsb3QgTWF0cml4IiwKICAgICAgICBsb3dlciA9IGxpc3QoY29tYm8gPSB3cmFwKCJmYWNldGhpc3QiLCBiaW5zID0gMjApKSkKYGBgCgpGcm9tIHRoaXMgc2NhdHRlcnBsb3QgbWF0cml4LCB3ZSBjYW4gc2VlIHRoYXQgdGhlcmUgYXJlIGEgbG90IG9mIG91dGxpbmVycyBmb3IgYm90aCBlbmQgYmV0d2VlbiBvdXIgb3V0Y29tZSBhbmQgb3VyIGNhdGVnb3JpY2FsIHByZWRpY3RvcnMuIEluIGFkZGl0aW9uLCB0aGUgZGlzdHJpYnV0aW9uIGJldHdlZW4gZ3JvdXBzIGluIG9uZSB2YXJpYWJsZXMgYWxzbyBub3QgZXF1YWwgdG8gZWFjaCBvdGhlciwgaS5lIGRpYWJldGVzLiBIb3dldmVyLCBpdCBzZWVtcyBsaWtlIHRoZSBkaXN0cmlidXRpb24gYmV0d2VlbiBncm91cHMgaW4gb25lIHZhcmlhYmxlcyBpcyBub3JtYWxseSBkaXN0cmlidXRlZCB3aGVuIGNvbXBhcmluZyB0byB0aGUgb3V0Y29tZSAKYGBge3IgbWVzc2FnZT1GQUxTRX0KZGY0X3RyYWluaW5nIDwtIG11dGF0ZShkZl90cmFpbmluZywgc3lzdG9saWMgPSAxL3N5c3RvbGljKQp0ZW1wIDwtICBkZjRfdHJhaW5pbmcgfD4gCiAgc2VsZWN0KHN5c3RvbGljLCBzbW9raW5nLCBkaWFiZXRlcywgZWNvX3N0YXR1cywgaGlnaGNob2wpIAoKZ2dwYWlycyh0ZW1wLCB0aXRsZSA9ICJTY2F0dGVycGxvdCBNYXRyaXgiLAogICAgICAgIGxvd2VyID0gbGlzdChjb21ibyA9IHdyYXAoImZhY2V0aGlzdCIsIGJpbnMgPSAyMCkpKQpgYGAKU2luY2UgdGhlIGludmVyc2Ugb2Ygc3lzdG9saWMgZ2l2ZXMgdGhlIG51bWVyaWNhbCB2YWx1ZSB0aGF0IGFyZSByZWxhdGl2ZWx5IHNtYWxsLCB3ZSBjb3VsZCBzZWUgdGhhdCBmb3Igc21va2luZyB2YXJpYWJsZSwgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0d28gZ3JvdXBzIGFyZSBub3Qgbm90aWNlYWJsZS4gSG93ZQpgYGB7cn0KbW9zYWljOjpmYXZzdGF0cyhzeXN0b2xpYyB+IHNtb2tpbmcsIGRhdGEgPSBkZjRfdHJhaW5pbmcpIHw+IGthYmxlKCkKYGBgCgojIyBDb2xsaW5lYXJpdHkgQ2hlY2tpbmcKTm9uZSBvZiB0aGUgbnVtZXJpYyBjYW5kaWRhdGUgcHJlZGljdG9ycyBzaG93IGFueSBzdWJzdGFudGlhbCBjb3JyZWxhdGlvbiB3aXRoIGVhY2ggb3RoZXIuIFRoZSBsYXJnZXN0IFBlYXJzb24gY29ycmVsYXRpb24gKGluIGFic29sdXRlIHZhbHVlKSBiZXR3ZWVuIHByZWRpY3RvcnMgaXMgKC0wLjA1NCkgZm9yIGBCTUlgIGFuZCBgU2xlZXBIb3Vyc2AsIGFuZCB0aGF04oCZcyBub3Qgc3Ryb25nLiBJZiB3ZSBkaWQgc2VlIHNpZ25zIG9mIG1lYW5pbmdmdWwgY29sbGluZWFyaXR5LCB3ZSBtaWdodCByZXRoaW5rIG91ciBzZWxlY3RlZCBzZXQgb2YgcHJlZGljdG9ycy4KCgojIFRoZSBCaWcgTW9kZWwgCgpUaGUgImtpdGNoZW4gc2luayIgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgaXMgdG8gZGVzY3JpYmUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIG91ciBvdXRjb21lIChgMS9zeXN0b2xpY2ApIGFuZCB0aGUgbWFpbiBlZmZlY3RzIG9mIGVhY2ggb2Ygb3VyIHByZWRpY3RvcnMuIAoKIyMgRml0dGluZy9TdW1tYXJpemluZyB0aGUgS2l0Y2hlbiBTaW5rIG1vZGVsCk91ciDigJxraXRjaGVuIHNpbmvigJ0gb3Ig4oCcYmln4oCdIG1vZGVsIHByZWRpY3RzIHRoZSBzcXVhcmUgcm9vdCBvZiBzYnAyIHVzaW5nIHRoZSBwcmVkaWN0b3JzIChzcXVhcmUgcm9vdCBvZiBzYnAxKSwgYWdlLCBibWkxLCBkaWFiZXRlcyBhbmQgdG9iYWNjby4KCk91ciBga2l0Y2hlbiBzaW5rYCBvciBgbW9kXzFgIHByZWRpY3RzIHRoZSBpbnZlcnNlIG9mIG91dGNvbWUgdXNpbmcgdGhlIHByZWRpY3RvcnM6IGBCTUlgLGAgU2xlZXBIb3Vyc2AsIGBzbW9raW5nYCwgYGRpYWJldGVzYCwgYGVjb19zdGF0dXNgLCBgaGlnaGNob2xgLgoKYGBge3J9Cm1vZF8xIDwtIGxtKDEvc3lzdG9saWMgfiBCTUkgKyBTbGVlcEhvdXJzICsgc21va2luZyArIGRpYWJldGVzICsgZWNvX3N0YXR1cyArIGhpZ2hjaG9sLCBkYXRhID0gZGZfdHJhaW5pbmcpCnN1bW1hcnkobW9kXzEpCmBgYAoKIyMgRWZmZWN0IFNpemVzOiBDb2VmZmljaWVudCBFc3RpbWF0ZXMKU3BlY2lmeSB0aGUgc2l6ZSBhbmQgbWFnbml0dWRlIG9mIGFsbCBjb2VmZmljaWVudHMsIHByb3ZpZGluZyBlc3RpbWF0ZWQgZWZmZWN0IHNpemVzIHdpdGggOTAlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzLgpTaW5jZSB0aGUgdmFsdWUgb2YgbXkgY29lZmZpY2llbnRzIGFyZSB2ZXJ5IHNtYWxsLCBzbyBJIG5lZWQgaXQgdG8gYmUgZGlzcGxheSBpbiBmdWxsLWZvcm0gaW4gb3JkZXIgdG8gZ2V0IHNvbWUgaW50ZWd0ZXIuIApGcm9tIHRoZSBhbmFseXNpcyBiZWxvdywgd2UgY291bGQgc2VlIHRoYXQgb3VyIGtleSBwcmVkaWN0b3IsIGBCTUlgLCBpcyBub3QgcmVhbGx5IHNpZ25pZmljYW50IGFzIHRoZSBwLXZhbHVlIGlzIGxhcmdlciB0aGFuIDAuMS4gCmBgYHtyfQp0aWR5KG1vZF8xLCBjb25mLmludCA9IFRSVUUsIGNvbmYubGV2ZWwgPSAwLjkwKSB8PiAKICBzZWxlY3QodGVybSwgZXN0aW1hdGUsIHN0ZC5lcnJvciwgY29uZi5sb3csIGNvbmYuaGlnaCwgcC52YWx1ZSkgfD4gCiAga2FibGUoKQpgYGAKIyMgRGVzY3JpYmluZyB0aGUgRXF1YXRpb24KYGBge3J9CmV4dHJhY3RfZXEobW9kXzEsIHVzZV9jb2VmcyA9IFRSVUUsIGNvZWZfZGlnaXRzID0gOCwKICAgICAgICAgICB0ZXJtc19wZXJfbGluZSA9IDMsIHdyYXAgPSBUUlVFLCBpdGFsX3ZhcnMgPSBUUlVFKQpgYGAKCi0gVGhpcyBtb2RlbCBjYW4gYmUgZGVzY3JpYmVkIGFzIGZvciBldmVyeSBpbmNyZWFzaW5nIGluIEJNSSwgZGVjcmVhc2UgaW4gc2xlZXAgaG91cnMsIHdlIGFudGljaXBhdGVkIHRoZSBpbmNyZWFzZSBpbiB0aGUgb3V0Y29tZSAoc3lzdG9saWMpLCB3aGljaCBpcyBkZWNyZWFzZSBpbiB0aGUgaW52ZXJzZSBvZiBzeXN0b2xpYy4gCi0gSWYgeW91IGFyZSBub3Qgc21va2luZy9ub3QgZGlhYmV0ZXMgb3IgZGlhYmV0ZXMgb25seSBhdCBib3JkZXJsaW5lL25vIGhpZ2ggY2hvbGVzdGVyb2wsIHRoZXJlIHdvdWxkIGJlIGEgZGVjcmVhc2UgaW4gdGhlIHN5c3RvbGljLCB3aGljaCBpcyBpbmNyZWFzZSBpbiB0aGUgaW52ZXJzZSBvZiBzeXN0b2xpYy4KLSBJZiB5b3UgYXJlIGluIGVpdGhlciB1cHBlciBvciBsb3dlciBjbGFzc2VzLCB0aGVyZSB3b3VsZCBiZSBhbiBpbmNyZWFzZSBpbiB0aGUgb3V0Y29tZSAoc3lzdG9saWMpLCB3aGljaCBpcyBkZWNyZWFzZSBpbiB0aGUgaW52ZXJzZSBvZiBzeXN0b2xpYy4gCgojIFRoZSBTbWFsbGVyIE1vZGVsCgojIyBCYWNrd2FyZHMgU3RlcHdpc2UgRWxpbWluYXRpb24KSW5zdGVhZCBvZiB1c2luZyBgc3RlcGAgZnVuY3Rpb24gZm9yIGJhY2t3YXJkcyBzdGVwd2lzZSBlbGltaW5hdGlvbiwgSSB3b3VsZCBsaWtlIHRvIHVzZSBhIGNvdXBsZSBzZWxlY3RlZCBtb2RlbHMgdGhhdCBJIHdhbnQgdG8gaW52ZXN0aWdhdGUuIFRoaXMgaXMgYmVjYXVzZSB3aGVuIEkgdXNlIGBzdGVwYCBmdW5jdGlvbiwgaXQgd2lsbCBldmVudHVhbGx5IHJlbW92ZSBteSBrZXkgcHJlZGljdG9yIG91dCBvZiB0aGUgbW9kZWwsIHdoaWNoIGlzIG5vdCB3aGF0IEkgd2FudGVkLiAKYGBge3J9CnN0ZXAobW9kXzEpCmBgYAoKVGhlcmVmb3JlLCBiZXNpZGVzIG15IGJpZyBtb2RlbCwgSSB3b3VsZCBsaWtlIHRvIGludmVzdGlnYXRlIHRoZSBvdGhlciB0aHJlZSBtb2RlbHM6CgotIGBNb2RlbCAyYDogMS9zeXN0b2xpYyB+IEJNSQotIGBNb2RlbCAzYDogMS9zeXN0b2xpYyB+IEJNSSwgU2xlZXBIb3VycyAKLSBgTW9kZWwgNGA6IDEvc3lzdG9saWMgfiBCTUksIFNsZWVwSG91cnMsIGRpYWJldGVzLiAKCiMjIEZpdHRpbmcgdGhlIOKAnHNtYWxs4oCdIG1vZGVsClRoaXMgaXMgbXkgZmlyc3Qgc21hbGwgbW9kZWwsIHdoaWNoIGlzIGludmVzdGlnYXRlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgb3V0Y29tZSBhbmQgdGhlIGtleSBwcmVkaWN0b3IuIApgYGB7cn0KbW9kXzIgPC0gbG0oMS9zeXN0b2xpYyB+IEJNSSwgZGF0YSA9IGRmX3RyYWluaW5nKQpzdW1tYXJ5KG1vZF8yKQpgYGAKClRoaXMgaXMgbXkgc2Vjb25kIHNtYWxsIG1vZGVsLCB3aGljaCBpcyBpbnZlc3RpZ2F0ZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIG91dGNvbWUgYW5kIHRoZSBrZXkgcHJlZGljdG9yIGFsb25nIHdpdGggdGhlIGF2ZXJhZ2UgaG91cnMgb2Ygc2xlZXAgcGVyIHdlZWsuCmBgYHtyfQptb2RfMyA8LSBsbSgxL3N5c3RvbGljIH4gQk1JICsgU2xlZXBIb3VycywgZGF0YSA9IGRmX3RyYWluaW5nKQpzdW1tYXJ5KG1vZF8zKQpgYGAKClRoaXMgaXMgbXkgc2Vjb25kIHNtYWxsIG1vZGVsLCB3aGljaCBpcyBpbnZlc3RpZ2F0ZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIG91dGNvbWUgYW5kIHRoZSBrZXkgcHJlZGljdG9yIGFsb25nIHdpdGggdGhlIGF2ZXJhZ2UgaG91cnMgb2Ygc2xlZXAgcGVyIHdlZWsgYW5kIGRpYWJldGVzIGdyb3Vwcy4gCmBgYHtyfQptb2RfNCA8LSBsbSgxL3N5c3RvbGljIH4gQk1JICsgU2xlZXBIb3VycyArIGRpYWJldGVzLCBkYXRhID0gZGZfdHJhaW5pbmcpCnN1bW1hcnkobW9kXzQpCmBgYAoKCiMjIEVmZmVjdCBTaXplczogQ29lZmZpY2llbnQgRXN0aW1hdGVzCmBgYHtyfQp0aWR5KG1vZF8yLCBjb25mLmludCA9IFRSVUUsIGNvbmYubGV2ZWwgPSAwLjkwKSB8PiAKICBzZWxlY3QodGVybSwgZXN0aW1hdGUsIHN0ZC5lcnJvciwgY29uZi5sb3csIGNvbmYuaGlnaCwgcC52YWx1ZSkgfD4ga2FibGUoKQpgYGAKCmBgYHtyfQp0aWR5KG1vZF8zLCBjb25mLmludCA9IFRSVUUsIGNvbmYubGV2ZWwgPSAwLjkwKSB8PiAKICBzZWxlY3QodGVybSwgZXN0aW1hdGUsIHN0ZC5lcnJvciwgY29uZi5sb3csIGNvbmYuaGlnaCwgcC52YWx1ZSkgfD4ga2FibGUoKQpgYGAKCmBgYHtyfQp0aWR5KG1vZF80LCBjb25mLmludCA9IFRSVUUsIGNvbmYubGV2ZWwgPSAwLjkwKSB8PiAKICBzZWxlY3QodGVybSwgZXN0aW1hdGUsIHN0ZC5lcnJvciwgY29uZi5sb3csIGNvbmYuaGlnaCwgcC52YWx1ZSkgfD4ga2FibGUoKQoKYGBgCgoKCiMjIFNtYWxsIE1vZGVsIFJlZ3Jlc3Npb24gRXF1YXRpb24KYGBge3J9CmV4dHJhY3RfZXEobW9kXzIsIHVzZV9jb2VmcyA9IFRSVUUsIGNvZWZfZGlnaXRzID0gOCwKICAgICAgICAgICB0ZXJtc19wZXJfbGluZSA9IDMsIHdyYXAgPSBUUlVFLCBpdGFsX3ZhcnMgPSBUUlVFKQpgYGAKCi0gVGhpcyBtb2RlbCBjYW4gYmUgZGVzY3JpYmVkIGFzIGZvciBldmVyeSBpbmNyZWFzaW5nIGluIEJNSSwgd2UgYW50aWNpcGF0ZWQgdGhlIGluY3JlYXNlIGluIHRoZSBvdXRjb21lIChzeXN0b2xpYyksIHdoaWNoIGlzIGRlY3JlYXNlIGluIHRoZSBpbnZlcnNlIG9mIHN5c3RvbGljLiAKCgpgYGB7cn0KZXh0cmFjdF9lcShtb2RfMywgdXNlX2NvZWZzID0gVFJVRSwgY29lZl9kaWdpdHMgPSA4LAogICAgICAgICAgIHRlcm1zX3Blcl9saW5lID0gMywgd3JhcCA9IFRSVUUsIGl0YWxfdmFycyA9IFRSVUUpCmBgYAotIFRoaXMgbW9kZWwgY2FuIGJlIGRlc2NyaWJlZCBhcyBmb3IgZXZlcnkgaW5jcmVhc2luZyBpbiBCTUksIGRlY3JlYXNlIGluIHNsZWVwIGhvdXJzLCB3ZSBhbnRpY2lwYXRlZCB0aGUgaW5jcmVhc2UgaW4gdGhlIG91dGNvbWUgKHN5c3RvbGljKSwgd2hpY2ggaXMgZGVjcmVhc2UgaW4gdGhlIGludmVyc2Ugb2Ygc3lzdG9saWMuIAoKCmBgYHtyfQpleHRyYWN0X2VxKG1vZF80LCB1c2VfY29lZnMgPSBUUlVFLCBjb2VmX2RpZ2l0cyA9IDgsCiAgICAgICAgICAgdGVybXNfcGVyX2xpbmUgPSAzLCB3cmFwID0gVFJVRSwgaXRhbF92YXJzID0gVFJVRSkKYGBgCi0gVGhpcyBtb2RlbCBjYW4gYmUgZGVzY3JpYmVkIGFzIGZvciBldmVyeSBpbmNyZWFzaW5nIGluIEJNSSwgZGVjcmVhc2UgaW4gc2xlZXAgaG91cnMsIHdlIGFudGljaXBhdGVkIHRoZSBpbmNyZWFzZSBpbiB0aGUgb3V0Y29tZSAoc3lzdG9saWMpLCB3aGljaCBpcyBkZWNyZWFzZSBpbiB0aGUgaW52ZXJzZSBvZiBzeXN0b2xpYy4gCi0gSWYgeW91IGFyZSBub3QgZGlhYmV0ZXMgb3IgZGlhYmV0ZXMgb25seSBhdCBib3JkZXJsaW5lLCB0aGVyZSB3b3VsZCBiZSBhIGRlY3JlYXNlIGluIHRoZSBzeXN0b2xpYywgd2hpY2ggaXMgaW5jcmVhc2UgaW4gdGhlIGludmVyc2Ugb2Ygc3lzdG9saWMuCgojIEluLVNhbXBsZSBDb21wYXJpc29uCiMjIFF1YWxpdHkgb2YgRml0CgpJbiBvcmRlciB0byBkbyB0aGUgaW4tY2FtcGxlIGNvbXBhcmlzb24sIEkgd291bGQgbGlrZSB0byBjb21wYXJlIGFsbCBvZiBteSBzbWFsbCBtb2RlbCB0byB0aGUgYmlnIG1vZGVsIGluIG91ciB0cmFpbmluZyBzYW1wbGUgdXNpbmcgYWRqdXN0ZWQgUjIsIHRoZSByZXNpZHVhbCBzdGFuZGFyZCBlcnJvciwgQUlDIGFuZCBCSUMuCmBgYHtyfQpiaW5kX3Jvd3MoZ2xhbmNlKG1vZF8xKSwgZ2xhbmNlKG1vZF8yKSwgZ2xhbmNlKG1vZF8zKSwgZ2xhbmNlKG1vZF80KSkgJT4lCm11dGF0ZShtb2RlbF9uYW1lID0gYygiTW9kZWwgMSIsICJNb2RlbCAyIiwgIk1vZGVsIDMiLCAiTW9kZWwgNCIpKSU+JQpzZWxlY3QobW9kZWxfbmFtZSwgci5zcXVhcmVkLCBhZGouci5zcXVhcmVkLCBzaWdtYSwgQUlDLCBCSUMsIG5vYnMpICU+JQprYWJsZSgpCmBgYApXaGVuIGNvbXBhcmluZyB0aGUgQUlDLCBNb2RlbCAxIGlzIHRoZSBiZXN0LCB3aGljaCBpcyBmb2xsb3dpbmcgYnkgTW9kZWwgNCwgTW9kZWwgMywgTW9kZWwgMiwgcmVzcGVjdGl2ZWx5LiAKV2hlbiBjb21wYXJpbmcgdGhlIEJJQywgTW9kZWwgMSBpcyB0aGUgYmVzdCwgd2hpY2ggaXMgZm9sbG93aW5nIGJ5IE1vZGVsIDQsIE1vZGVsIDMsIE1vZGVsIDIsIHJlc3BlY3RpdmVseS4KV2hlbiBjb21wYXJpbmcgdGhlIHNpZ21hIHZhbHVlLCBNb2RlbCAxIGlzIHRoZSBiZXN0LCB3aGljaCBpcyBmb2xsb3dpbmcgYnkgTW9kZWwgNCwgTW9kZWwgMywgTW9kZWwgMiwgcmVzcGVjdGl2ZWx5LgpXaGVuIGNvbXBhcmluZyB0aGUgci1zcXVhcmVkIHZhbHVlLCBNb2RlbCAxIGlzIHRoZSBiZXN0LCB3aGljaCBpcyBmb2xsb3dpbmcgYnkgTW9kZWwgNCwgTW9kZWwgMywgTW9kZWwgMiwgcmVzcGVjdGl2ZWx5LiAKIyMgQXNzZXNzaW5nIEFzc3VtcHRpb25zCkhlcmUgSSB3b3VsZCBsaWtlIHRvIHJ1biB0aGUgcmVzaWR1bGEgcGxvdHMgZm9yIGV2ZXJ5IG1vZGVsLiAKIyMjIFJlc2lkdWFsIFBsb3RzIGZvciB0aGUgQmlnIE1vZGVsCmBgYHtyfQpwYXIobWZyb3cgPSBjKDIsMikpOyBwbG90KG1vZF8xKTsgcGFyKG1mcm93ID0gYygxLDEpKQpgYGAKSSBzZWUgbm8gc2VyaW91cyBwcm9ibGVtcyB3aXRoIHRoZSBhc3N1bXB0aW9ucyBvZiBsaW5lYXJpdHksIE5vcm1hbGl0eSBhbmQgY29uc3RhbnQgdmFyaWFuY2UsIG5vciBkbyBJIHNlZSBhbnkgaGlnaGx5IGluZmx1ZW50aWFsIHBvaW50cyBpbiBvdXIgYmlnIG1vZGVsLgojIyMgUmVzaWR1YWwgUGxvdHMgZm9yIHRoZSBTbWFsbCBNb2RlbApgTW9kZWwgMmAKYGBge3J9CnBhcihtZnJvdyA9IGMoMiwyKSk7IHBsb3QobW9kXzIpOyBwYXIobWZyb3cgPSBjKDEsMSkpCmBgYApJIHNlZSBubyBzZXJpb3VzIHByb2JsZW1zIHdpdGggdGhlIGFzc3VtcHRpb25zIG9mIGxpbmVhcml0eSwgTm9ybWFsaXR5IGFuZCBjb25zdGFudCB2YXJpYW5jZSwgbm9yIGRvIEkgc2VlIGFueSBoaWdobHkgaW5mbHVlbnRpYWwgcG9pbnRzIGluIG91ciBiaWcgbW9kZWwuCgpgTW9kZWwgM2AKYGBge3J9CnBhcihtZnJvdyA9IGMoMiwyKSk7IHBsb3QobW9kXzMpOyBwYXIobWZyb3cgPSBjKDEsMSkpCmBgYApJIHNlZSBubyBzZXJpb3VzIHByb2JsZW1zIHdpdGggdGhlIGFzc3VtcHRpb25zIG9mIGxpbmVhcml0eSwgTm9ybWFsaXR5IGFuZCBjb25zdGFudCB2YXJpYW5jZSwgbm9yIGRvIEkgc2VlIGFueSBoaWdobHkgaW5mbHVlbnRpYWwgcG9pbnRzIGluIG91ciBiaWcgbW9kZWwuCgpgTW9kZWwgNGAKYGBge3J9CnBhcihtZnJvdyA9IGMoMiwyKSk7IHBsb3QobW9kXzQpOyBwYXIobWZyb3cgPSBjKDEsMSkpCmBgYApJIHNlZSBubyBzZXJpb3VzIHByb2JsZW1zIHdpdGggdGhlIGFzc3VtcHRpb25zIG9mIGxpbmVhcml0eSwgTm9ybWFsaXR5IGFuZCBjb25zdGFudCB2YXJpYW5jZSwgbm9yIGRvIEkgc2VlIGFueSBoaWdobHkgaW5mbHVlbnRpYWwgcG9pbnRzIGluIG91ciBiaWcgbW9kZWwuIEhvd2V2ZXIsIGl0IGlzIHdlaXJlZCB0aGF0IGl0IGZvcm1zIHR3byBjbHVzdGVycyBpbiB0aGUgZ3JhcGguIEkgYmVsaWV2ZSB0aGlzIGlzIGluZmx1ZW5jZWQgYnkgdGhlIGRpYWJldGVzIHZhcmlhYmxlcyBhcyB0aGlzIG9ubHkgaGFwcGVucyBhZnRlciBhZGRpbmcgdGhpcyB2YWx1ZXMgaW50byB0aGUgbW9kZWwgNCBjb21wYXJpbmcgdG8gTW9kZWwgMSBhbmQgMi4gVGhlIG1haW4gcmVhc29uIG1heSBiZSBiZWNhdXNlIHRoZSBncm91cHMgaW4gZGlhYmV0ZXMgYXJlIG5vdCBoYXZpbmcgZXF1YWwgc2l6ZS4KCiMjIyBEb2VzIGNvbGxpbmVhcml0eSBoYXZlIGEgbWVhbmluZ2Z1bCBpbXBhY3Q/CmBgYHtyfQpjYXI6OnZpZihtb2RfMSkKY2FyOjp2aWYobW9kXzQpCmBgYAoKVGhlIGdlbmVyYWxpemVkIHZhcmlhbmNlIGluZmxhdGlvbiBmYWN0b3JzIGFyZSB1bmRlciA1IGZvciBib3RoIG1vZGVsLCB3aGljaCBhZGRyZXNzIHRoYXQgdGhlcmUgYXJlIG5vIHBvdGVudGlhbCBpbXBhY3Qgb2YgY29sbGluZWFyaXR5LgojIyBDb21wYXJpbmcgdGhlIE1vZGVscwpCYXNlZCBvbiB0aGUgdHJhaW5pbmcgc2FtcGxlLCBteSBjb25jbHVzaW9ucyBzbyBmYXIgaXMgdG8gc3VwcG9ydCB0aGUgYmlnZ2VzdCAgbW9kZWwgb3IgTW9kZWwgMS4gSXQgd29uIGV2ZXJ5IHZhbHVlcyB0byBiZSB0aGUgYmVzdCBtb2RlbCBmb3IgdGhlIGZpdCBxdWFsaXR5IG1lYXN1cmVzLCBhbmQgZWFjaCBtb2RlbCBzaG93cyBubyBzZXJpb3VzIHByb2JsZW1zIHdpdGggcmVncmVzc2lvbiBhc3N1bXB0aW9ucy4KIyBNb2RlbCBWYWxpZGF0aW9uClNpbmNlIEkgdHJhbnNmb3JtZWQgbXkgb3V0Y29tZSBlYXJsaWVyLCB0aGVuIEkgbmVlZCB0byBiYWNrLXRyYW5zZm9ybWVkIGZvciBtb2RlbCB2YWxpZGF0aW9uCiMjIENhbGN1bGF0aW5nIFByZWRpY3Rpb24gRXJyb3JzCgojIyMgQmlnIE1vZGVsOiBCYWNrLVRyYW5zZm9ybWF0aW9uIGFuZCBDYWxjdWxhdGluZyBGaXRzL1Jlc2lkdWFscwpXZeKAmWxsIHVzZSB0aGUgYXVnbWVudCBmdW5jdGlvbiBmcm9tIHRoZSBicm9vbSBwYWNrYWdlIHRvIGhlbHAgdXMgaGVyZSwgYW5kIGNyZWF0ZSBmaXRfc3lzdG9saWMgdG8gaG9sZCB0aGUgZml0dGVkIHZhbHVlcyBvbiB0aGUgb3JpZ2luYWwgc3lzdG9saWMgc2NhbGUgYWZ0ZXIgYmFjay10cmFuc2Zvcm1hdGlvbiAoYnkgZG9pbmcgdGhlIGludmVyc2Ugb2Ygc3lzdG9saWMpIGFuZCB0aGVuIHJlc19zeXN0b2xpYyB0byBob2xkIHRoZSByZXNpZHVhbHMgKHByZWRpY3Rpb24gZXJyb3JzKSB3ZSBvYnNlcnZlIHVzaW5nIHRoZSBiaWcgbW9kZWwgb24gdGhlIGNob3NlbiBuaGFubmVzIGRhdGEuCgoKYGBge3J9CnRlc3RfbTEgPC0gYXVnbWVudChtb2RfMSwgbmV3ZGF0YSA9IGRmX3Rlc3QpICU+JSBtdXRhdGUobmFtZSA9ICJtb2RfMSIsIGZpdF9zeXN0b2xpYyA9IDEgLyAuZml0dGVkLCByZXNfc3lzdG9saWMgPSBzeXN0b2xpYyAtIGZpdF9zeXN0b2xpYykgJT4lIHNlbGVjdChTRVFOLCBuYW1lLCBzeXN0b2xpYywgZml0X3N5c3RvbGljLCAgcmVzX3N5c3RvbGljLCBldmVyeXRoaW5nKCkpCmhlYWQodGVzdF9tMSwzKQpgYGAKCiMjIyBTbWFsbCBNb2RlbDogQmFjay1UcmFuc2Zvcm1hdGlvbiBhbmQgQ2FsY3VsYXRpbmcgRml0cy9SZXNpZHVhbHMKV2XigJlsbCBkbyB0aGUgc2FtZSB0aGluZywgYnV0IHVzaW5nIHRoZSBzbWFsbCBtb2RlbCBpbiB0aGUgY2hvc2VuIG5oYW5uZXMgZGF0YS4KCmBNb2RlbCAyYApgYGB7cn0KdGVzdF9tMiA8LSBhdWdtZW50KG1vZF8yLCBuZXdkYXRhID0gZGZfdGVzdCkgJT4lIG11dGF0ZShuYW1lID0gIm1vZF8yIiwgZml0X3N5c3RvbGljID0gMSAvIC5maXR0ZWQsIHJlc19zeXN0b2xpYyA9IHN5c3RvbGljIC0gZml0X3N5c3RvbGljKSAlPiUgc2VsZWN0KFNFUU4sIG5hbWUsIHN5c3RvbGljLCBmaXRfc3lzdG9saWMsICByZXNfc3lzdG9saWMsIGV2ZXJ5dGhpbmcoKSkKaGVhZCh0ZXN0X20yLDMpCgpgYGAKCmBNb2RlbCAzYApgYGB7cn0KdGVzdF9tMyA8LSBhdWdtZW50KG1vZF8zLCBuZXdkYXRhID0gZGZfdGVzdCkgJT4lIG11dGF0ZShuYW1lID0gIm1vZF8zIiwgZml0X3N5c3RvbGljID0gMSAvIC5maXR0ZWQsIHJlc19zeXN0b2xpYyA9IHN5c3RvbGljIC0gZml0X3N5c3RvbGljKSAlPiUgc2VsZWN0KFNFUU4sIG5hbWUsIHN5c3RvbGljLCBmaXRfc3lzdG9saWMsICByZXNfc3lzdG9saWMsIGV2ZXJ5dGhpbmcoKSkKaGVhZCh0ZXN0X20zLDMpCmBgYAoKYE1vZGVsIDRgCmBgYHtyfQp0ZXN0X200IDwtIGF1Z21lbnQobW9kXzQsIG5ld2RhdGEgPSBkZl90ZXN0KSAlPiUgbXV0YXRlKG5hbWUgPSAibW9kXzQiLCBmaXRfc3lzdG9saWMgPSAxIC8gLmZpdHRlZCwgcmVzX3N5c3RvbGljID0gc3lzdG9saWMgLSBmaXRfc3lzdG9saWMpICU+JSBzZWxlY3QoU0VRTiwgbmFtZSwgc3lzdG9saWMsIGZpdF9zeXN0b2xpYywgIHJlc19zeXN0b2xpYywgZXZlcnl0aGluZygpKQpoZWFkKHRlc3RfbTQsMykKYGBgCgojIyMgQ29tYmluaW5nIHRoZSBSZXN1bHRzCnRoZSBgdGVzdF9jb21wYCB0aWJibGUgaW5jbHVkaW5nIGFsbCB0aGUgcHJlZGljdGlvbnMgYW5kIHJlc2lkdWFscyBmcm9tIDQgbW9kZWxzLiBJdCBoZWxwcyB0byB2aXN1YWxpemUgdGhlIHByZWRpY3Rpb25zLCBzdW1tYXJpemluZyB0aGUgZXJyb3JzLCBpZGVudGlmeSB0aGUgbGFyZ2VzdCBlcnJvcnMsIGFuZCB2YWxpZGF0ZWQgdGhlIHItc3F1YXJlZCB2YWx1ZXMuCmBgYHtyfQp0ZXN0X2NvbXAgPC0gYmluZF9yb3dzKHRlc3RfbTEsIHRlc3RfbTIsIHRlc3RfbTMsIHRlc3RfbTQpIHw+IGFycmFuZ2UoU0VRTiwgbmFtZSkKCnRlc3RfY29tcCB8PiBoZWFkKCkKYGBgCiMjIFZpc3VhbGl6aW5nIHRoZSBQcmVkaWN0aW9ucwpgYGB7cn0KZ2dwbG90KHRlc3RfY29tcCwgYWVzKHggPSBmaXRfc3lzdG9saWMsIHkgPSBzeXN0b2xpYykpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fYWJsaW5lKHNsb3BlID0gMSwgaW50ZXJjZXB0ID0gMCwgbHR5ID0gImRhc2hlZCIpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIiwgY29sID0gImJsdWUiLCBzZSA9IEZBTFNFLCBmb3JtdWxhID0geSB+IHgpICsKICBmYWNldF93cmFwKCB+IG5hbWUsIGxhYmVsbGVyID0gImxhYmVsX2JvdGgiKSArCiAgbGFicyh4ID0gIlByZWRpY3RlZCBzeXN0b2xpYyIsCiAgICAgICB5ID0gIk9ic2VydmVkIHN5c3RvbGljIiwKICAgICAgIHRpdGxlID0gIk9ic2VydmVkIHZzLiBQcmVkaWN0ZWQgc3lzdG9saWMiLAogICAgICAgc3VidGl0bGUgPSAiQ29tcGFyaW5nIEJpZyB0byAzIFNtYWxsIE1vZGVzbCBpbiBUZXN0IFNhbXBsZSIsCiAgICAgICBjYXB0aW9uID0gIkRhc2hlZCBsaW5lIGlzIHdoZXJlIE9ic2VydmVkID0gUHJlZGljdGVkIikKYGBgCldlIGNhbiBzZWUgdGhhdCBpbiBmb3VyIGdyYXBocywgdGhlIE1vZGVsIDEgc2VlbXMgdG8gYmUgdGhlIG1vc3QgcmVhc29uYWJsZSBjb21wYXJpbmcgdG8gb3RoZXIgMy4gT3RoZXIgMyBtb2RlbHMsIGFsbCB0aGUgcG9pbnRzIHNlZW0gdG8gYmUgY2x1c3RlciBhdCBvbmUgcGxhY2UsIGV4Y2VwdCBmb3IgTW9kZWwgNCB0aGF0IGl0IGNsdXN0ZXJzIGludG8gMiBkaWZmZXJlbnQgY2x1c3RlcnMuIEhvd2V2ZXIsIHRoZXJlIGFyZSBub3QgbXVjaCBkaWZmZXJlbmNlIGluIHRoZSBkYXNoZWQgbGluZSwgd2hpY2ggaXMgcHJlZGljdGFibGUgc2luY2UgdGhlIGNvZWZmaWNpZW50cyBhcmUgcmVhbGx5IGxvdy4gCgojIyBTdW1tYXJpemluZyB0aGUgRXJyb3JzCgpDYWxjdWxhdGUgdGhlIG1lYW4gYWJzb2x1dGUgcHJlZGljdGlvbiBlcnJvciAoTUFQRSksIHRoZSByb290IG1lYW4gc3F1YXJlZCBwcmVkaWN0aW9uIGVycm9yIChSTVNQRSkgYW5kIHRoZSBtYXhpbXVtIGFic29sdXRlIGVycm9yIGFjcm9zcyB0aGUgcHJlZGljdGlvbnMgbWFkZSBieSBlYWNoIG1vZGVsLgoKYGBge3J9CnRlc3RfY29tcCB8PgogIGdyb3VwX2J5KG5hbWUpIHw+CiAgZHBseXI6OnN1bW1hcmlzZShuID0gbigpLAogICAgICAgICAgICBNQVBFID0gbWVhbihhYnMocmVzX3N5c3RvbGljKSksIAogICAgICAgICAgICBSTVNQRSA9IHNxcnQobWVhbihyZXNfc3lzdG9saWMqKjIpKSwKICAgICAgICAgICAgbWF4X2Vycm9yID0gbWF4KGFicyhyZXNfc3lzdG9saWMpKSkKYGBgCk1vZGVsIDEgaGFzIHRoZSBsb3dlc3QgTUFQRSwgUk1TUEUsIGFuZCBtYXhfZXJyb3IuIE1vZGVsIDIgaGFzIHRoZSBzZWNvbmQtbG93ZXN0IGZvciBhbGwgdGhlc2UgMyB2YWx1ZXMuIER1ZSB0byB0aGlzLCBpdCBhbHNvIHN1Z2dlc3RzIHRoYXQgTW9kZWwgMSBpcyB0aGUgYmVzdCBtb2RlbCBzbyBmYXIuIAoKIyMjIElkZW50aWZ5IHRoZSBsYXJnZXN0IGVycm9ycwoKYGBge3J9CnRlbXAxIDwtIHRlc3RfbTEgJT4lCmZpbHRlcihhYnMocmVzX3N5c3RvbGljKSA9PSBtYXgoYWJzKHJlc19zeXN0b2xpYykpKQp0ZW1wMiA8LSB0ZXN0X20yICU+JQpmaWx0ZXIoYWJzKHJlc19zeXN0b2xpYykgPT0gbWF4KGFicyhyZXNfc3lzdG9saWMpKSkKdGVtcDMgPC0gdGVzdF9tMyAlPiUKZmlsdGVyKGFicyhyZXNfc3lzdG9saWMpID09IG1heChhYnMocmVzX3N5c3RvbGljKSkpCnRlbXA0IDwtIHRlc3RfbTQgJT4lCmZpbHRlcihhYnMocmVzX3N5c3RvbGljKSA9PSBtYXgoYWJzKHJlc19zeXN0b2xpYykpKQpiaW5kX3Jvd3ModGVtcDEsIHRlbXAyLCB0ZW1wMywgdGVtcDQpICU+JQpzZWxlY3QoU0VRTiwgbmFtZSwgc3lzdG9saWMsIGZpdF9zeXN0b2xpYywgcmVzX3N5c3RvbGljKQpgYGAKVGhpcyBoZWxwcyB0byBpZGVudGlmeSB0aGUgb3V0bGluZXIgZm9yIGFsbCA0IG1vZGVscy4gV2UgY2FuIHRyeSB0byBpbnZlc3RpZ2F0ZSB0aGUgZXJyb3JzIGFmdGVyIHJlbW92aW5nIHRoaXMgcG9pbnQuCmBgYHtyfQp0ZXN0X2NvbXAgJT4lIGZpbHRlcihTRVFOICE9ICIxMTI4OTgiKSAlPiUKZ3JvdXBfYnkobmFtZSkgfD4KICBkcGx5cjo6c3VtbWFyaXNlKG4gPSBuKCksCiAgICAgICAgICAgIE1BUEUgPSBtZWFuKGFicyhyZXNfc3lzdG9saWMpKSwgCiAgICAgICAgICAgIFJNU1BFID0gc3FydChtZWFuKHJlc19zeXN0b2xpYyoqMikpLAogICAgICAgICAgICBtYXhfZXJyb3IgPSBtYXgoYWJzKHJlc19zeXN0b2xpYykpKQoKYGBgCkFmdGVyIHJlbW92aW5nIHRoYXQgb3V0bGluZXIsIE1vZGVsIDEgb25seSBoYXMgdGhlIGxvd2VzdCB2YWx1ZSBmb3IgTUFQRSBhbmQgUk1TUEUsIHdpdGggdGhlIGJpZ2dlc3QgbWF4aW11bSBhYnNvbHV0ZSBlcnJvci4gSG93ZXZlciwgaXQgc3RpbGwgb3V0d2VpZ2h0cyBvdGhlciBtb2RlbHMuIAoKIyMjIFZhbGlkYXRlZCBSLXNxdWFyZSB2YWx1ZXMKYGBge3J9CmNvcih0ZXN0X20xJHN5c3RvbGljLCB0ZXN0X20xJGZpdF9zeXN0b2xpYykqKjIKYGBgCmBgYHtyfQpjb3IodGVzdF9tMiRzeXN0b2xpYywgdGVzdF9tMiRmaXRfc3lzdG9saWMpKioyCmBgYApgYGB7cn0KY29yKHRlc3RfbTMkc3lzdG9saWMsIHRlc3RfbTMkZml0X3N5c3RvbGljKSoqMgpgYGAKCmBgYHtyfQpjb3IodGVzdF9tNCRzeXN0b2xpYywgdGVzdF9tNCRmaXRfc3lzdG9saWMpKioyCmBgYAoKRnJvbSB0aGlzIGFuYWx5c2lzLCB3ZSBjYW4gc2VlIHRoYXQgTW9kZWwgMSBoYXMgdGhlIGJpZ2dlc3QgY29ycmVsYXRpb25zLiAKCiMjIENvbXBhcmluZyB0aGUgTW9kZWxzCkFmdGVyIHRoZSBhbmFseXNpcywgSSB3b3VsZCBsaWtlIHRvIHVzZSBNb2RlbCAxLCBvciB0aGUgYmlnIG1vZGVsIGJlY2F1c2UgaXQgcGVyZm9ybXMgdGhlIGJlc3QgZm9yIGZpdCBxdWFsaXR5IGFzIHdlbGwgYXMgaGFzIHRoZSBzbWFsbGVzdCBlcnJvcnMgY29tcGFyaW5nIHRvIG90aGVyIG1vZGVscy4KCiMgRGlzY3Vzc2lvbgojIyBDaG9zZW4gTW9kZWwKCkFmdGVyIHRoZSBhbmFseXNpcywgSSB3b3VsZCBsaWtlIHRvIHVzZSBNb2RlbCAxLCBvciB0aGUgYmlnIG1vZGVsIGJlY2F1c2UgaXQgcGVyZm9ybXMgdGhlIGJlc3QgZm9yIGZpdCBxdWFsaXR5IChsb3dlc3QgQUlDLCBCSUMsc2lnbWEgYnV0IGhpZ2hlc3QgUi1zcXVhcmVkICkgYXMgd2VsbCBhcyBoYXMgdGhlIHNtYWxsZXN0IGVycm9ycyAobWVhbiBhYnNvbHV0ZSBwcmVkaWN0aW9uIGVycm9yLCAsIG1heGltdW0gYWJzb2x1dGUgZXJyb3IgKSBjb21wYXJpbmcgdG8gb3RoZXIgbW9kZWxzLiAKCiMjIEFuc3dlcmluZyBNeSBRdWVzdGlvbgpUaGlzIGhlbHBzIHRvIGFuc3dlciBteSBxdWVzdGlvbnMgdGhhdCBoYXZpbmcgaGlnaGVyIEJNSSwgbGVzcyBzbGVlcCBjb3VsZCBsZWFkIHRvIGhpZ2hlciBzeXN0b2xpYywgd2hpY2ggaW5kaWNhdGUgdGhlIGhpZ2ggYmxvb2QgcHJlc3N1cmUuIEluIGFkZGl0aW9uLCBub3QgaGF2aW5nIGRpYWJldGVzIG9yIGRpYWJldGVzIGF0IHRoZSBib3JkZXJsaW5lIGNvdWxkIGFsc28gaGF2ZSBsb3dlciBzeXN0b2xpYyByZWFkaW5nLiBOb3QgaGF2aW5nIGhpZ2ggY2hvbGVzdGVyb2wgb3Igc21va2lnIGFsc28gaW5kaWNhdGVzIGxvd2VyIHN5c3RvbGljIHJlYWRpbmcuIFBlb3BsZSBjb21lIGZyb20gdGhlIHVwcGVyIGFuZCBsb3dlciwgdGhlcmUgd291bGQgYmUgYW4gaW5jcmVhc2UgaW4gdGhlIHN5c3RvbGljLiAKRXZlbiB0aG91Z2ggdGhlIGNvZWZmaWNpZW50cyBhcmUgdmVyeSBzbWFsbCwgYnV0IHRoZSByZXN1bHQgc2VlbXMgdG8gYmUgcmVhc29uYWJsZSBhcyB0aG9zZSByaXNrIGZhY3RvcnMgb25seSBpbmNyZWFzZSB0aGUgc3lzdG9saWMgcmVhZGluZyB3aGVuIGl0IGluY3JlYXNlIGluIHRoZSBoYXJtZnVsIHdheS4gCkZvciB0aGUgZWNvbm9taWMgc3RhdHVzLCBpdCBjYW4gYmUgZXhwbGFpbmVkIHRoYXQgcGVvcGxlIGZyb20gdGhlIGxvd2VyIGNsYXNzLCB0aGV5IGhhdmUgbGVzcyBjaGFuY2UgdG8gZ2V0IGFjY2VzcyB0byB0aGUgaGVhbHRoY2FyZSBhbmQgZG9uJ3QgdGFrZSBjYXJlIG11Y2ggYWJvdXQgdGhlaXIgZm9vZC4gRm9yIHRoZSB1cHBlciBjbGFzcywgdGhleSBoYXZlIGFjY2VzcyB0byBtb3JlIGZvb2QsIGFuZCBtYXkgb3ZlcmNvbnN1bWUgaXQuIAoKIyMgTmV4dCBTdGVwcwoKSW4gdGhpcyBhbmFseXNpcywgSSBvbmx5IGNvbnNpZGVyIDQgbW9kZWxzIHdpdGhvdXQgZG9pbmcgaW4gdGhlIHN0ZXAgZnVuY3Rpb24uIFRoYXQncyBiZWluZyBzYWlkLCB0aGVyZSBjb3VsZCBiZSBvdGhlciBtb2RlbHMgdGhhdCBhcmUgYmV0dGVyIGZpdCBjb21wYXJpbmcgdG8gbXkgYmlnIG1vZGVsLiBGdXJ0aGVyIHdvcmtzIG11c3QgYmUgZG9uZSBpbiBvcmRlciB0byBpbnZlc3RpZ2F0ZSBkaWZmZXJlbnQgY29tYmluYXRpb25zIG9mIHByZWRpY3RvcnMgaW4gb3JkZXIgdG8gcHJlZGljdCB0aGUgb3V0Y29tZS4gCgpJbiBhZGRpdGlvbiB0byB0aGF0LCBJIG9ubHkgdXNlIHN5c3RvbGljIHJlYWRpbmcgZm9yIHRoaXMgYW5hbHlzaXMgd2l0aG91dCBjb25zaWRlciBkaWFzdG9saWMgcmVhZGluZy4gRnVydGhlciB3b3JrIHNob3VsZCBiZSB3b3JrZWQgb24gZGlhc3RvbGljIHJlYWRpbmcgdG8gc2VlIHdoYXQgZmFjdG9ycyBhZmZlY3QgdGhpcyB2YWx1ZS4gVGhpcyBpcyBiZWNhdXNlIGJvdGggb2YgdGhlc2UgdmFsdWVzIGFyZSBlc3NlbnRpYWwgaW4gb3JkZXIgdG8gaW5kaWNhdGUgYSBwZXJzb24gd2hvIGlzIGF0IHJpc2sgZm9yIGhhdmluZyBhYm5vcm1hbCBibG9vZCBwcmVzc3VyZSBvciBub3QuIEdldHRpbmcgdG8ga25vdyBvbmUgdmFsdWUgY291bGQgbGVhZCB0byBiaWFzLiAKIyMgUmVmbGVjdGlvbgoKSSBhbHJlYWR5IGNoYW5nZWQgbXkgYXBwcm9hY2ggd2hlbiBkb2luZyB0aGlzIHN0dWR5IGFmdGVyIHRoZSBwcmVzZW50YXRpb24gYWZ0ZXIgbGlzdGVuaW5nIGFkdmljZSBmcm9tIFByb2Zlc3Nvci4gSG93ZXZlciwgaXQgc2VlbXMgbGlrZSBteSBjb2VmZmljaWVudHMgYXJlIHN0aWxsIHZlcnkgbG93LiBJIGJlbGlldmUgaWYgSSBjYW4gcmVkbyBpdCwgSSB3b3VsZCBsaWtlIHRvIGludmVzdGlnYXRlIG1vcmUgYWJvdXQgdGhlIGRhdGFzZXRzIGFuZCBtYWtlIGEgYmV0dGVyIGNob2ljZSBmb3IgbXkgcHJlZGljdG9ycy4gCgojIyBTZXNzaW9uIEluZm9ybWF0aW9uCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAoKCg==