Objective
Identify actionable treatment variation within patients with low back pain.
Data Preparation
For this analysis, data was pulled from the following tables in the data warehouse.
- V_ETG_MED_CLAIMS
- V_MEDICAL_SERVICE
- V_STD_TOS_CATEGORY
- V_CLAIM_PROCEDURE
- V_ICD10_DIAG_GRP
- V_INCURRED_DATE
Patients were members of BCBS from 2016 who had episodes of low back pain without complications. Ages were between 18-65. ETG outliers were excluded. Members with missing episode numbers were dropped. There were 30,000 episodes included in this analysis out of an original 36,300.
#read in the SAS file
df = read_sas("C:/Users/scasti01/Desktop/Cost and Use/R/Claims/episodes.sas7bdat") %>%
#convert data types to proper format
mutate(MEM_NUM = as.factor(MEM_NUM),
EPI_TYPE = as.factor(EPI_TYPE),
etg = as.character(ETG_FULL),
ETG_OUTLIER = as.factor(ETG_OUTLIER),
COMPLICATION = as.factor(COMPLICATION),
COMORBIDITY = as.factor(COMORBIDITY),
SEVERITY = as.factor(SEVERITY),
TOS_CAT1_DESC = as.factor(TOS_CAT1_DESC),
TOS_CAT2_DESC = as.factor(TOS_CAT2_DESC),
TOS_CAT3_DESC = as.factor(TOS_CAT3_DESC)
) %>%
#exclude members with complications
#eclude episodes with episodes number 0 (missing values)
filter(COMPLICATION == 0, EPISODE_NUM != 0, !is.na(EPISODE_NUM) ) %>%
#find the time difference between each appointment and the start of th episode
mutate(lag = difftime( INCURRED_DT_DAY, EPI_START_DT, units = "days"),
surgery_indicator = ifelse(test = (str_detect(CPT_PROC_DESC, "SURG"))|| any(TOS_CAT2_DESC == "SURGICAL"),
yes = T,
no = F)) %>%
#count the number of episodes for each patient
group_by(MEM_NUM) %>%
mutate( nth_episode = length(unique(EPISODE_NUM))) %>%
ungroup() %>%
#create clinical T/F variables for clinical categories
group_by(EPISODE_NUM) %>%
summarise(
#does the episode have surgery? these are the ETGs that have surgery at some point in the episode
surgery = as.factor(any(ETG_FULL %in% c(712208001, 712208011, 712208111, 712208101))),
#When does the surgery occur?
surgery_lag = ifelse( test = any(surgery_indicator),
yes = min(difftime( INCURRED_DT_DAY, EPI_START_DT, units = "days")),
no = 1000),
#How many physical therapy claims are on record?
PT_count = sum(ifelse( test = (TOS_CAT2_DESC == "PHYSICAL THERAPY"),
yes = T,
no = F)),
#did the patient go to more than 7 PT appointments?
PT = as.factor(any(TOS_CAT2_DESC == "PHYSICAL THERAPY" & PT_count >= 7)),
#Was there an MRI at any point in the episode?
MRI_any = as.factor(any(TOS_CAT3_DESC == "MRI" )),
#Was there an MRI in the first 6 weeks of the episode, and before surgery if there was a surgical outcome?
MRI_6_weeks = as.factor(any(TOS_CAT3_DESC == "MRI" & (lag <= 6*7) & (lag <= surgery_lag))),
#was there an MRI in the first 8 weeks and before surgery?
MRI_8_weeks = as.factor(any(TOS_CAT3_DESC == "MRI" & (lag <= 8*7) & (lag <= surgery_lag))),
#Did the patient have a spinal manipulation (chiropractic treatment) before surgery?
spinal_manipulation = as.factor(any(TOS_CAT3_DESC == "SPINAL MANIPULATION" & (lag <= surgery_lag))),
#did the patient have a diagnostic injection prior to surgery?
diag_injection = as.factor(any(TOS_CAT3_DESC == "DIAGNOSTIC INJECTION" & (lag <= surgery_lag))),
#where there xrays taken before surgery? In the first 6, 8 weeks, and prior to surgery?
xray_any = as.factor(any(str_detect( CPT_PROC_DESC, "X-RAY") & (lag <= surgery_lag))),
xray_6_weeks = as.factor(any(str_detect( CPT_PROC_DESC, "X-RAY") & (lag <= 6*7) & (lag <= surgery_lag))),
xray_8_weeks = as.factor(any(str_detect( CPT_PROC_DESC, "X-RAY") & (lag <= 8*7) & (lag <= surgery_lag))),
#what was the ETG severity?
severity1 = as.factor(any(SEVERITY == 1)),
severity2 = as.factor(any(SEVERITY == 2)),
severity3 = as.factor(any(SEVERITY == 3)),
#how old was the member?
member_age = as.numeric(member_age[1]),
nth_episode = as.factor(nth_episode[1]),
MEM_NUM = MEM_NUM[1],
comorbidity = as.factor(COMORBIDITY[1])
)#end summarise
surgery date test
Do the tos_cat dates agree with the surgery dates?
testdata =
read_sas("C:/Users/scasti01/Desktop/Cost and Use/R/Claims/episodes.sas7bdat") %>%
#convert data types to proper format
mutate(MEM_NUM = as.factor(MEM_NUM),
EPI_TYPE = as.factor(EPI_TYPE),
etg = as.character(ETG_FULL),
ETG_OUTLIER = as.factor(ETG_OUTLIER),
COMPLICATION = as.factor(COMPLICATION),
COMORBIDITY = as.factor(COMORBIDITY),
SEVERITY = as.factor(SEVERITY),
TOS_CAT1_DESC = as.factor(TOS_CAT1_DESC),
TOS_CAT2_DESC = as.factor(TOS_CAT2_DESC),
TOS_CAT3_DESC = as.factor(TOS_CAT3_DESC)
) %>%
#exclude members with complications
#eclude episodes with episodes number 0 (missing values)
filter(COMPLICATION == 0, EPISODE_NUM != 0, !is.na(EPISODE_NUM) ) %>%
#find the time difference between each appointment and the start of th episode
mutate(lag = difftime( INCURRED_DT_DAY, EPI_START_DT, units = "weeks"))
surgery_test = testdata %>%
#count the number of episodes for each patient
group_by(MEM_NUM) %>%
mutate( nth_episode = length(unique(EPISODE_NUM))) %>%
ungroup() %>%
group_by(EPISODE_NUM) %>%
summarise(
surgery_etg = as.factor(any(ETG_FULL %in% c(712208001, 712208011, 712208111, 712208101))),
surgery_toscat = as.factor(any((str_detect(CPT_PROC_DESC, "SURG"))|| any(TOS_CAT2_DESC == "SURGICAL"))),
hit = ifelse(surgery_etg == surgery_toscat,
yes = T, no = F))
confusionMatrix(surgery_test$surgery_etg, surgery_test$surgery_toscat)
testdata %>%
filter(ETG_FULL %in% c(712208001, 712208011, 712208111, 712208101)) %>%
select(HCPCS_PROC_DESC) %>%
unique()
Validation Set Split
A validation set was created to prevent overfitting the model to the data. A randomly-sampled 20% validation, 80% non-validation split was used. This 80% non-validation set was further split during a 10-fold cross validation which was repeated 3 times.
fraction_Validation <- 0.20
fraction_NonValidation <- 0.80
# Compute sample sizes.
sample_Size_Validation <- floor(fraction_Validation * nrow(df))
sample_Size_NonValidation <- floor(fraction_NonValidation * nrow(df))
# Create the randomly-sampled indices for the dataframe. Use setdiff() to
# avoid overlapping subsets of indices.
indices_Validation <- sort(sample(seq_len(nrow(df)), size = sample_Size_Validation))
indices_Not_Validation <- setdiff(seq_len(nrow(df)), indices_Validation)
# Finally, output the three dataframes for training, validation and test.
dfValidation <- df[indices_Validation, ]
dfNotValidation <- df[indices_Not_Validation,]
Adjusting for Class Inbalance
The surgical rate is only ~6%, which leads to a class inbalance in surgical and non-surgical outcomes. To account for this, the minority population can be oversampled and the majority (surgical) population undersampled. A technique known as Sythetic Minority Oversampling was used from the library SMOTE. To test performance, two versions were used: the first with both over-sampling of the surgical class and undersampling of the non-surgical class, and a second with only over-sampling of the surgical class. For brevity this report only contains the first version, which performed better based on sensitivity and accuracy ratings.
pre_processed_data = dplyr::as_data_frame(dfNotValidation) %>%
dplyr::select(-MRI_6_weeks,
-xray_6_weeks,
-MRI_any,
-xray_any,
- MEM_NUM,
- EPISODE_NUM,
- surgery_lag) %>%
as.data.frame()
Prior to the oversampling there is about a 95%-5% split between those episodes with surgery and without.
t1 = Sys.time()
rf.data.smote = SMOTE(surgery ~.,
data <- pre_processed_data,
perc.over = 1500,
perc.under = 100,
k = 9)
t2 = Sys.time()
run_time = t1 - t2
run_time
Time difference of -1.45 secs
dim(rf.data.smote)
[1] 23436 13
summary(rf.data.smote$surgery)
TRUE FALSE
12096 11340
After the bootstrap there are roughly an equal number of cases with and without surgery.
Fitting a Model
Because of the number of binary predictors and need to compensate for overfitting, a random forest was used with an automated tuning function from the library caret.
t1 = Sys.time()
# Create model with default paramters
control <- trainControl(method = "repeatedcv", number=10, repeats=3)
seed <- 7
metric <- "Kappa"
set.seed(seed)
mtry <- sqrt(ncol(rf.data.smote))
tunegrid <- expand.grid(.mtry=mtry)
rf_smote <- train(surgery ~.,
data= rf.data.smote,
method="rf",
metric=metric,
tuneGrid=tunegrid,
trControl=control)
t2 = Sys.time()
rf_runtime = t2 - t1
rf_runtime
Time difference of 5.044917 mins
Testing the Model
Testing was conducted both against the bootstrapped data and the un-inflated validation set, which contained the raw observations. The cutoff value for the random forest was optimized based on the sensitivity and accuracy of the model.
Test against bootstrapped data
predictor = rf_smote$finalModel$votes[,1]
response = rf.data.smote$surgery
roc(response, predictor, percent=TRUE,
# arguments for auc
plot=TRUE,
auc.polygon=TRUE,
max.auc.polygon=TRUE,
grid=TRUE,
print.auc=TRUE,
show.thres=TRUE)
Call:
roc.default(response = response, predictor = predictor, percent = TRUE, plot = TRUE, auc.polygon = TRUE, max.auc.polygon = TRUE, grid = TRUE, print.auc = TRUE, show.thres = TRUE)
Data: predictor in 12096 controls (response TRUE) > 11340 cases (response FALSE).
Area under the curve: 94.19%

The Area Under the Curve (AUC) of 0.942 indicates that the model fits the bootstrapped data well. The interpretation is the probability that a randomly selected subject who ends up with surgery has a test result indicating greater suspicion than that of a randomly chosen subject who does not end up having surgery. (Hanley & McNeil, 1982)
dev.new()
pred = prediction( predictor, response)
perf = performance(pred, measure="tpr",x.measure="cutoff")
plot(perf,
main = "True Positive Rate vs Cutoff",
type = "s")

The true positive rate (sensitivity) is highest when the cutoff is set to close to zero. Because the goal is to correctly identify those patients who have surgery, the cutoff was set close to zero in order to optimize the sensitivity of the model, even at the cost of reduced accuracy. This does not effect the variable importance ranking, however.
dev.new()
pred = prediction( predictor, response)
perf = performance(pred, measure="fpr",x.measure="cutoff")
plot(perf,
main = "False Positive Rate vs Cutoff",
type = "s")

Test Against Validation Set
rf_smote$finalModel$forest$cutoff = c(0.0001, 0.9999)
prediction = predict( object = rf_smote,
newdata = dfValidation)
confusionMatrix(prediction, dfValidation$surgery)
Confusion Matrix and Statistics
Reference
Prediction TRUE FALSE
TRUE 131 2521
FALSE 60 3239
Accuracy : 0.5663
95% CI : (0.5536, 0.5789)
No Information Rate : 0.9679
P-Value [Acc > NIR] : 1
Kappa : 0.0343
Mcnemar's Test P-Value : <2e-16
Sensitivity : 0.68586
Specificity : 0.56233
Pos Pred Value : 0.04940
Neg Pred Value : 0.98181
Prevalence : 0.03210
Detection Rate : 0.02201
Detection Prevalence : 0.44564
Balanced Accuracy : 0.62410
'Positive' Class : TRUE
Interpretation
Variable Importance

Logistic Regression of Top Predictors
A logistic regression allows for inferrence on whether or not each predictor increases or decreases the probability of having surgery. A variable with a positive coefficient indicates that this variable increases the likelyhood of the patient having surgery.
The sensitivity of the logistic model is only 42%. The sign of the coefficient is all that matters in this case.
control <- trainControl(method = "repeatedcv", number=10, repeats=3)
logit = train( surgery ~ .,
data = rf.data.smote,
method = "glm",
family = "binomial",
trControl = control)
prediction = predict( object = logit,
newdata = dfValidation)
confusionMatrix(prediction, dfValidation$surgery)
Confusion Matrix and Statistics
Reference
Prediction TRUE FALSE
TRUE 81 1296
FALSE 110 4464
Accuracy : 0.7637
95% CI : (0.7527, 0.7745)
No Information Rate : 0.9679
P-Value [Acc > NIR] : 1
Kappa : 0.0497
Mcnemar's Test P-Value : <2e-16
Sensitivity : 0.42408
Specificity : 0.77500
Pos Pred Value : 0.05882
Neg Pred Value : 0.97595
Prevalence : 0.03210
Detection Rate : 0.01361
Detection Prevalence : 0.23139
Balanced Accuracy : 0.59954
'Positive' Class : TRUE
Coefficients
exp(coef(logit$finalModel))
(Intercept) surgery_lag PT_count PTTRUE MRI_6_weeksTRUE spinal_manipulationTRUE diag_injectionTRUE xray_6_weeksFALSE
0.01892649 1.00467641 0.94601127 0.43084952 0.24220599 1.72237896 0.21415402 7.79866040
severity1TRUE severity2FALSE severity3TRUE member_age nth_episode2 comorbidity1
2.77189418 2.70571302 0.36142152 1.01904203 0.44337266 0.82503731
LS0tDQp0aXRsZTogIklkZW50aWZ5aW5nIEFjdGlvbmFibGUgVHJlYXRtZW50IFZhcmlhdGlvbiBpbiBFcGlzb2RlcyBvZiBMb3cgQmFjayBQYWluIg0KYXV0aG9yOiAiU2FtdWVsIENhc3RpbGxvIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICB0b2M6IHRydWUNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KLS0tDQoNCmBgYHtyIGVjaG8gPSBULCBpbmNsdWRlID0gRiwgZXZhbCAgPSBUfQ0KI21vZGVsIGZpdHRpbmcgcGFja2FnZXMNCnJlcXVpcmUocmFuZG9tRm9yZXN0KQ0KbGlicmFyeShyZlV0aWxpdGllcykNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KERNd1IpDQpsaWJyYXJ5KHBST0MpDQpsaWJyYXJ5KFJPQ1IpDQojdmlzdWFsaXphaXRvbiBwYWNrYWdlcw0KbGlicmFyeShnb29nbGVWaXMpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGdyaWRFeHRyYSkNCmxpYnJhcnkoZ2dyYXBoKQ0KbGlicmFyeShpZ3JhcGgpDQpsaWJyYXJ5KGtuaXRyKQ0KI2RhdGEgbWFuaXB1bGF0aW9uIHBhY2thZ2VzDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoaGF2ZW4pDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCiNzZXR3ZCgiQzovVXNlcnMvc2Nhc3RpMDEvRGVza3RvcC9Db3N0IGFuZCBVc2UvRGF0YS9DbGFpbXMiKQ0KYGBgDQoNCiNPYmplY3RpdmUNCg0KSWRlbnRpZnkgYWN0aW9uYWJsZSB0cmVhdG1lbnQgdmFyaWF0aW9uIHdpdGhpbiBwYXRpZW50cyB3aXRoIGxvdyBiYWNrIHBhaW4uDQoNCioqKg0KDQojRGF0YSBQcmVwYXJhdGlvbg0KDQpGb3IgdGhpcyBhbmFseXNpcywgZGF0YSB3YXMgcHVsbGVkIGZyb20gdGhlIGZvbGxvd2luZyB0YWJsZXMgaW4gdGhlIGRhdGEgd2FyZWhvdXNlLiAgDQoNCiogVl9FVEdfTUVEX0NMQUlNUw0KKiBWX01FRElDQUxfU0VSVklDRQ0KKiBWX1NURF9UT1NfQ0FURUdPUlkNCiogVl9DTEFJTV9QUk9DRURVUkUNCiogVl9JQ0QxMF9ESUFHX0dSUA0KKiBWX0lOQ1VSUkVEX0RBVEUNCg0KUGF0aWVudHMgd2VyZSBtZW1iZXJzIG9mIEJDQlMgZnJvbSAyMDE2IHdobyBoYWQgZXBpc29kZXMgb2YgbG93IGJhY2sgcGFpbiB3aXRob3V0IGNvbXBsaWNhdGlvbnMuICBBZ2VzIHdlcmUgYmV0d2VlbiAxOC02NS4gIEVURyBvdXRsaWVycyB3ZXJlIGV4Y2x1ZGVkLiAgTWVtYmVycyB3aXRoIG1pc3NpbmcgZXBpc29kZSBudW1iZXJzIHdlcmUgZHJvcHBlZC4gIFRoZXJlIHdlcmUgMzAsMDAwIGVwaXNvZGVzIGluY2x1ZGVkIGluIHRoaXMgYW5hbHlzaXMgb3V0IG9mIGFuIG9yaWdpbmFsIDM2LDMwMC4gIA0KDQpgYGB7ciBnbG9iYWxfb3B0aW9ucywgbWVzc2FnZT0gRiwgd2FybmluZz0gRiwgZXZhbCA9IFQsIGVjaG8gPSBUfQ0KI3JlYWQgaW4gdGhlIFNBUyBmaWxlIA0KZGYgPSByZWFkX3NhcygiQzovVXNlcnMvc2Nhc3RpMDEvRGVza3RvcC9Db3N0IGFuZCBVc2UvUi9DbGFpbXMvZXBpc29kZXMuc2FzN2JkYXQiKSAlPiUNCiAgI2NvbnZlcnQgZGF0YSB0eXBlcyB0byBwcm9wZXIgZm9ybWF0DQogIG11dGF0ZShNRU1fTlVNID0gYXMuZmFjdG9yKE1FTV9OVU0pLA0KICAgICAgICAgRVBJX1RZUEUgPSBhcy5mYWN0b3IoRVBJX1RZUEUpLA0KICAgICAgICAgZXRnID0gYXMuY2hhcmFjdGVyKEVUR19GVUxMKSwNCiAgICAgICAgIEVUR19PVVRMSUVSID0gYXMuZmFjdG9yKEVUR19PVVRMSUVSKSwNCiAgICAgICAgIENPTVBMSUNBVElPTiA9IGFzLmZhY3RvcihDT01QTElDQVRJT04pLA0KICAgICAgICAgQ09NT1JCSURJVFkgPSBhcy5mYWN0b3IoQ09NT1JCSURJVFkpLA0KICAgICAgICAgU0VWRVJJVFkgPSBhcy5mYWN0b3IoU0VWRVJJVFkpLA0KICAgICAgICAgVE9TX0NBVDFfREVTQyA9IGFzLmZhY3RvcihUT1NfQ0FUMV9ERVNDKSwNCiAgICAgICAgIFRPU19DQVQyX0RFU0MgPSBhcy5mYWN0b3IoVE9TX0NBVDJfREVTQyksDQogICAgICAgICBUT1NfQ0FUM19ERVNDID0gYXMuZmFjdG9yKFRPU19DQVQzX0RFU0MpDQogICAgICAgICApICU+JQ0KICAjZXhjbHVkZSBtZW1iZXJzIHdpdGggY29tcGxpY2F0aW9ucw0KICAjZWNsdWRlIGVwaXNvZGVzIHdpdGggZXBpc29kZXMgbnVtYmVyIDAgKG1pc3NpbmcgdmFsdWVzKQ0KICBmaWx0ZXIoQ09NUExJQ0FUSU9OID09IDAsIEVQSVNPREVfTlVNICE9IDAsICFpcy5uYShFUElTT0RFX05VTSkgKSAlPiUNCiAgI2ZpbmQgdGhlIHRpbWUgZGlmZmVyZW5jZSBiZXR3ZWVuIGVhY2ggYXBwb2ludG1lbnQgYW5kIHRoZSBzdGFydCBvZiB0aCBlcGlzb2RlDQogIG11dGF0ZShsYWcgPSBkaWZmdGltZSggSU5DVVJSRURfRFRfREFZLCBFUElfU1RBUlRfRFQsIHVuaXRzID0gImRheXMiKSwNCiAgICAgICAgIHN1cmdlcnlfaW5kaWNhdG9yID0gIGlmZWxzZSh0ZXN0ID0gKHN0cl9kZXRlY3QoQ1BUX1BST0NfREVTQywgIlNVUkciKSl8fCBhbnkoVE9TX0NBVDJfREVTQyA9PSAiU1VSR0lDQUwiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllcyA9IFQsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm8gPSBGKSkgJT4lDQogICNjb3VudCB0aGUgbnVtYmVyIG9mIGVwaXNvZGVzIGZvciBlYWNoIHBhdGllbnQNCiAgZ3JvdXBfYnkoTUVNX05VTSkgJT4lDQogIG11dGF0ZSggbnRoX2VwaXNvZGUgPSBsZW5ndGgodW5pcXVlKEVQSVNPREVfTlVNKSkpICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogICNjcmVhdGUgY2xpbmljYWwgVC9GIHZhcmlhYmxlcyBmb3IgY2xpbmljYWwgY2F0ZWdvcmllcw0KICBncm91cF9ieShFUElTT0RFX05VTSkgJT4lDQogIHN1bW1hcmlzZSggDQogICAgICAgICAgICAjZG9lcyB0aGUgZXBpc29kZSBoYXZlIHN1cmdlcnk/ICB0aGVzZSBhcmUgdGhlIEVUR3MgdGhhdCBoYXZlIHN1cmdlcnkgYXQgc29tZSBwb2ludCBpbiB0aGUgZXBpc29kZSAgICANCiAgICAgICAgICAgICBzdXJnZXJ5ID0gYXMuZmFjdG9yKGFueShFVEdfRlVMTCAgJWluJSBjKDcxMjIwODAwMSwgNzEyMjA4MDExLCA3MTIyMDgxMTEsIDcxMjIwODEwMSkpKSwNCiAgICAgICAgICAgICAjV2hlbiBkb2VzIHRoZSBzdXJnZXJ5IG9jY3VyPw0KICAgICAgICAgICAgIHN1cmdlcnlfbGFnID0gaWZlbHNlKCB0ZXN0ID0gYW55KHN1cmdlcnlfaW5kaWNhdG9yKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVzID0gbWluKGRpZmZ0aW1lKCBJTkNVUlJFRF9EVF9EQVksIEVQSV9TVEFSVF9EVCwgdW5pdHMgPSAiZGF5cyIpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm8gPSAxMDAwKSwNCiAgICAgICAgICAgICAjSG93IG1hbnkgcGh5c2ljYWwgdGhlcmFweSBjbGFpbXMgYXJlIG9uIHJlY29yZD8NCiAgICAgICAgICAgICBQVF9jb3VudCA9IHN1bShpZmVsc2UoIHRlc3QgPSAoVE9TX0NBVDJfREVTQyA9PSAiUEhZU0lDQUwgVEhFUkFQWSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVzID0gVCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBubyA9IEYpKSwNCiAgICAgICAgICAgICAjZGlkIHRoZSBwYXRpZW50IGdvIHRvIG1vcmUgdGhhbiA3IFBUIGFwcG9pbnRtZW50cz8NCiAgICAgICAgICAgICBQVCA9IGFzLmZhY3RvcihhbnkoVE9TX0NBVDJfREVTQyA9PSAiUEhZU0lDQUwgVEhFUkFQWSIgJiBQVF9jb3VudCA+PSA3KSksDQogICAgICAgICAgICAgI1dhcyB0aGVyZSBhbiBNUkkgYXQgYW55IHBvaW50IGluIHRoZSBlcGlzb2RlIGJlZm9yZSBzdXJnZXJ5PyAgQXQgNiB3ZWVrcywgOCB3ZWVrcz8NCiAgICAgICAgICAgICBNUklfYW55ID0gYXMuZmFjdG9yKGFueShUT1NfQ0FUM19ERVNDID09ICJNUkkiICkpLA0KICAgICAgICAgICAgIE1SSV82X3dlZWtzID0gYXMuZmFjdG9yKGFueShUT1NfQ0FUM19ERVNDID09ICJNUkkiICYgKGxhZyA8PSA2KjcpICYgKGxhZyA8PSBzdXJnZXJ5X2xhZykpKSwNCiAgICAgICAgICAgICBNUklfOF93ZWVrcyA9IGFzLmZhY3RvcihhbnkoVE9TX0NBVDNfREVTQyA9PSAiTVJJIiAmIChsYWcgPD0gOCo3KSAmIChsYWcgPD0gc3VyZ2VyeV9sYWcpKSksDQogICAgICAgICAgICAgI0RpZCB0aGUgcGF0aWVudCBoYXZlIGEgc3BpbmFsIG1hbmlwdWxhdGlvbiAoY2hpcm9wcmFjdGljIHRyZWF0bWVudCkgYmVmb3JlIHN1cmdlcnk/DQogICAgICAgICAgICAgc3BpbmFsX21hbmlwdWxhdGlvbiA9IGFzLmZhY3RvcihhbnkoVE9TX0NBVDNfREVTQyA9PSAiU1BJTkFMIE1BTklQVUxBVElPTiIgJiAobGFnIDw9IHN1cmdlcnlfbGFnKSkpLA0KICAgICAgICAgICAgICNkaWQgdGhlIHBhdGllbnQgaGF2ZSBhIGRpYWdub3N0aWMgaW5qZWN0aW9uIHByaW9yIHRvIHN1cmdlcnk/DQogICAgICAgICAgICAgZGlhZ19pbmplY3Rpb24gPSBhcy5mYWN0b3IoYW55KFRPU19DQVQzX0RFU0MgPT0gIkRJQUdOT1NUSUMgSU5KRUNUSU9OIiAmIChsYWcgPD0gc3VyZ2VyeV9sYWcpKSksDQogICAgICAgICAgICAgI3doZXJlIHRoZXJlIHhyYXlzIHRha2VuIGJlZm9yZSBzdXJnZXJ5PyAgSW4gdGhlIGZpcnN0IDYsIDggd2Vla3MsIGFuZCBwcmlvciB0byBzdXJnZXJ5Pw0KICAgICAgICAgICAgIHhyYXlfYW55ID0gYXMuZmFjdG9yKGFueShzdHJfZGV0ZWN0KCBDUFRfUFJPQ19ERVNDLCAiWC1SQVkiKSAmIChsYWcgPD0gc3VyZ2VyeV9sYWcpKSksDQogICAgICAgICAgICAgeHJheV82X3dlZWtzID0gYXMuZmFjdG9yKGFueShzdHJfZGV0ZWN0KCBDUFRfUFJPQ19ERVNDLCAiWC1SQVkiKSAmIChsYWcgPD0gNio3KSAmIChsYWcgPD0gc3VyZ2VyeV9sYWcpKSksDQogICAgICAgICAgICAgeHJheV84X3dlZWtzID0gYXMuZmFjdG9yKGFueShzdHJfZGV0ZWN0KCBDUFRfUFJPQ19ERVNDLCAiWC1SQVkiKSAmIChsYWcgPD0gOCo3KSAmIChsYWcgPD0gc3VyZ2VyeV9sYWcpKSksDQogICAgICAgICAgICAgI3doYXQgd2FzIHRoZSBFVEcgc2V2ZXJpdHk/DQogICAgICAgICAgICAgc2V2ZXJpdHkxID0gYXMuZmFjdG9yKGFueShTRVZFUklUWSA9PSAxKSksDQogICAgICAgICAgICAgc2V2ZXJpdHkyID0gYXMuZmFjdG9yKGFueShTRVZFUklUWSA9PSAyKSksDQogICAgICAgICAgICAgc2V2ZXJpdHkzID0gYXMuZmFjdG9yKGFueShTRVZFUklUWSA9PSAzKSksDQogICAgICAgICAgICAgI2hvdyBvbGQgd2FzIHRoZSBtZW1iZXI/DQogICAgICAgICAgICAgbWVtYmVyX2FnZSA9IGFzLm51bWVyaWMobWVtYmVyX2FnZVsxXSksDQogICAgICAgICAgICAgI0hvdyBtYW55IGVwaXNvZGVzIGRpZCB0aGlzIG1lbWJlciBoYXZlIGluIHRoZSBjdXJyZW50IHllYXI/ICBBbGwgbWVtYmVycyBpbiAyMDE2IGhhdmUgYXQgbW9zdCAyIGVwaXNvZGVzDQogICAgICAgICAgICAgbnRoX2VwaXNvZGUgPSBhcy5mYWN0b3IobnRoX2VwaXNvZGVbMV0pLA0KICAgICAgICAgICAgIE1FTV9OVU0gPSBNRU1fTlVNWzFdLA0KICAgICAgICAgICAgICNEaWQgdGhlIG1lbWJlciBoYXZlIGEgY29tb3JiaWRpdHk/DQogICAgICAgICAgICAgY29tb3JiaWRpdHkgPSBhcy5mYWN0b3IoQ09NT1JCSURJVFlbMV0pDQogICAgICAgICAgICAgDQogICkjZW5kIHN1bW1hcmlzZQ0KDQpgYGANCg0KI3N1cmdlcnkgZGF0ZSB0ZXN0DQoNCkRvIHRoZSB0b3NfY2F0IGRhdGVzIGFncmVlIHdpdGggdGhlIHN1cmdlcnkgZGF0ZXM/DQpgYGB7cn0NCnRlc3RkYXRhID0gDQpyZWFkX3NhcygiQzovVXNlcnMvc2Nhc3RpMDEvRGVza3RvcC9Db3N0IGFuZCBVc2UvUi9DbGFpbXMvZXBpc29kZXMuc2FzN2JkYXQiKSAlPiUNCiAgI2NvbnZlcnQgZGF0YSB0eXBlcyB0byBwcm9wZXIgZm9ybWF0DQogIG11dGF0ZShNRU1fTlVNID0gYXMuZmFjdG9yKE1FTV9OVU0pLA0KICAgICAgICAgRVBJX1RZUEUgPSBhcy5mYWN0b3IoRVBJX1RZUEUpLA0KICAgICAgICAgZXRnID0gYXMuY2hhcmFjdGVyKEVUR19GVUxMKSwNCiAgICAgICAgIEVUR19PVVRMSUVSID0gYXMuZmFjdG9yKEVUR19PVVRMSUVSKSwNCiAgICAgICAgIENPTVBMSUNBVElPTiA9IGFzLmZhY3RvcihDT01QTElDQVRJT04pLA0KICAgICAgICAgQ09NT1JCSURJVFkgPSBhcy5mYWN0b3IoQ09NT1JCSURJVFkpLA0KICAgICAgICAgU0VWRVJJVFkgPSBhcy5mYWN0b3IoU0VWRVJJVFkpLA0KICAgICAgICAgVE9TX0NBVDFfREVTQyA9IGFzLmZhY3RvcihUT1NfQ0FUMV9ERVNDKSwNCiAgICAgICAgIFRPU19DQVQyX0RFU0MgPSBhcy5mYWN0b3IoVE9TX0NBVDJfREVTQyksDQogICAgICAgICBUT1NfQ0FUM19ERVNDID0gYXMuZmFjdG9yKFRPU19DQVQzX0RFU0MpDQogICAgICAgICApICU+JQ0KICAjZXhjbHVkZSBtZW1iZXJzIHdpdGggY29tcGxpY2F0aW9ucw0KICAjZWNsdWRlIGVwaXNvZGVzIHdpdGggZXBpc29kZXMgbnVtYmVyIDAgKG1pc3NpbmcgdmFsdWVzKQ0KICBmaWx0ZXIoQ09NUExJQ0FUSU9OID09IDAsIEVQSVNPREVfTlVNICE9IDAsICFpcy5uYShFUElTT0RFX05VTSkgKSAlPiUNCiAgI2ZpbmQgdGhlIHRpbWUgZGlmZmVyZW5jZSBiZXR3ZWVuIGVhY2ggYXBwb2ludG1lbnQgYW5kIHRoZSBzdGFydCBvZiB0aCBlcGlzb2RlDQogIG11dGF0ZShsYWcgPSBkaWZmdGltZSggSU5DVVJSRURfRFRfREFZLCBFUElfU1RBUlRfRFQsIHVuaXRzID0gIndlZWtzIikpDQoNCg0KDQpzdXJnZXJ5X3Rlc3QgPSB0ZXN0ZGF0YSAlPiUNCiAgI2NvdW50IHRoZSBudW1iZXIgb2YgZXBpc29kZXMgZm9yIGVhY2ggcGF0aWVudA0KICBncm91cF9ieShNRU1fTlVNKSAlPiUNCiAgbXV0YXRlKCBudGhfZXBpc29kZSA9IGxlbmd0aCh1bmlxdWUoRVBJU09ERV9OVU0pKSkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgZ3JvdXBfYnkoRVBJU09ERV9OVU0pICU+JQ0KICBzdW1tYXJpc2UoIA0KICAgIHN1cmdlcnlfZXRnID0gYXMuZmFjdG9yKGFueShFVEdfRlVMTCAgJWluJSBjKDcxMjIwODAwMSwgNzEyMjA4MDExLCA3MTIyMDgxMTEsIDcxMjIwODEwMSkpKSwNCiAgICBzdXJnZXJ5X3Rvc2NhdCA9IGFzLmZhY3RvcihhbnkoKHN0cl9kZXRlY3QoQ1BUX1BST0NfREVTQywgIlNVUkciKSl8fCBhbnkoVE9TX0NBVDJfREVTQyA9PSAiU1VSR0lDQUwiKSkpLA0KICAgIGhpdCA9IGlmZWxzZShzdXJnZXJ5X2V0ZyA9PSBzdXJnZXJ5X3Rvc2NhdCwgDQogICAgICAgICAgICAgICAgIHllcyA9IFQsIG5vID0gRikpDQoNCmNvbmZ1c2lvbk1hdHJpeChzdXJnZXJ5X3Rlc3Qkc3VyZ2VyeV9ldGcsIHN1cmdlcnlfdGVzdCRzdXJnZXJ5X3Rvc2NhdCkNCg0KdGVzdGRhdGEgJT4lIA0KICBmaWx0ZXIoRVRHX0ZVTEwgJWluJSBjKDcxMjIwODAwMSwgNzEyMjA4MDExLCA3MTIyMDgxMTEsIDcxMjIwODEwMSkpICU+JQ0KICBzZWxlY3QoSENQQ1NfUFJPQ19ERVNDKSAlPiUNCiAgdW5pcXVlKCkNCg0KDQpgYGANCg0KDQojI1ZhbGlkYXRpb24gU2V0IFNwbGl0DQoNCkEgdmFsaWRhdGlvbiBzZXQgd2FzIGNyZWF0ZWQgdG8gcHJldmVudCBvdmVyZml0dGluZyB0aGUgbW9kZWwgdG8gdGhlIGRhdGEuICBBIHJhbmRvbWx5LXNhbXBsZWQgMjAlIHZhbGlkYXRpb24sIDgwJSBub24tdmFsaWRhdGlvbiBzcGxpdCB3YXMgdXNlZC4gIFRoaXMgODAlIG5vbi12YWxpZGF0aW9uIHNldCB3YXMgZnVydGhlciBzcGxpdCBkdXJpbmcgYSAxMC1mb2xkIGNyb3NzIHZhbGlkYXRpb24gd2hpY2ggd2FzIHJlcGVhdGVkIDMgdGltZXMuDQoNCmBgYHtyfQ0KZnJhY3Rpb25fVmFsaWRhdGlvbiA8LSAwLjIwDQpmcmFjdGlvbl9Ob25WYWxpZGF0aW9uIDwtIDAuODANCg0KIyBDb21wdXRlIHNhbXBsZSBzaXplcy4NCnNhbXBsZV9TaXplX1ZhbGlkYXRpb24gICA8LSBmbG9vcihmcmFjdGlvbl9WYWxpZGF0aW9uICAgKiBucm93KGRmKSkNCnNhbXBsZV9TaXplX05vblZhbGlkYXRpb24gPC0gZmxvb3IoZnJhY3Rpb25fTm9uVmFsaWRhdGlvbiAqIG5yb3coZGYpKQ0KDQojIENyZWF0ZSB0aGUgcmFuZG9tbHktc2FtcGxlZCBpbmRpY2VzIGZvciB0aGUgZGF0YWZyYW1lLiBVc2Ugc2V0ZGlmZigpIHRvDQojIGF2b2lkIG92ZXJsYXBwaW5nIHN1YnNldHMgb2YgaW5kaWNlcy4NCmluZGljZXNfVmFsaWRhdGlvbiAgICA8LSBzb3J0KHNhbXBsZShzZXFfbGVuKG5yb3coZGYpKSwgc2l6ZSA9IHNhbXBsZV9TaXplX1ZhbGlkYXRpb24pKQ0KaW5kaWNlc19Ob3RfVmFsaWRhdGlvbiA8LSBzZXRkaWZmKHNlcV9sZW4obnJvdyhkZikpLCBpbmRpY2VzX1ZhbGlkYXRpb24pDQoNCiMgRmluYWxseSwgb3V0cHV0IHRoZSB0aHJlZSBkYXRhZnJhbWVzIGZvciB0cmFpbmluZywgdmFsaWRhdGlvbiBhbmQgdGVzdC4NCmRmVmFsaWRhdGlvbiA8LSBkZltpbmRpY2VzX1ZhbGlkYXRpb24sIF0NCmRmTm90VmFsaWRhdGlvbiA8LSBkZltpbmRpY2VzX05vdF9WYWxpZGF0aW9uLF0NCmBgYA0KDQojI0FkanVzdGluZyBmb3IgQ2xhc3MgSW5iYWxhbmNlDQoNClRoZSBzdXJnaWNhbCByYXRlIGlzIG9ubHkgfjYlLCB3aGljaCBsZWFkcyB0byBhIGNsYXNzIGluYmFsYW5jZSBpbiBzdXJnaWNhbCBhbmQgbm9uLXN1cmdpY2FsIG91dGNvbWVzLiAgVG8gYWNjb3VudCBmb3IgdGhpcywgdGhlIG1pbm9yaXR5IHBvcHVsYXRpb24gY2FuIGJlIG92ZXJzYW1wbGVkIGFuZCB0aGUgbWFqb3JpdHkgKHN1cmdpY2FsKSBwb3B1bGF0aW9uIHVuZGVyc2FtcGxlZC4gIEEgdGVjaG5pcXVlIGtub3duIGFzIFN5dGhldGljIE1pbm9yaXR5IE92ZXJzYW1wbGluZyB3YXMgdXNlZCBmcm9tIHRoZSBsaWJyYXJ5IGBTTU9URWAuICBUbyB0ZXN0IHBlcmZvcm1hbmNlLCB0d28gdmVyc2lvbnMgd2VyZSB1c2VkOiB0aGUgZmlyc3Qgd2l0aCBib3RoIG92ZXItc2FtcGxpbmcgb2YgdGhlIHN1cmdpY2FsIGNsYXNzIGFuZCB1bmRlcnNhbXBsaW5nIG9mIHRoZSBub24tc3VyZ2ljYWwgY2xhc3MsIGFuZCBhIHNlY29uZCB3aXRoIG9ubHkgb3Zlci1zYW1wbGluZyBvZiB0aGUgc3VyZ2ljYWwgY2xhc3MuICBGb3IgYnJldml0eSB0aGlzIHJlcG9ydCBvbmx5IGNvbnRhaW5zIHRoZSBmaXJzdCB2ZXJzaW9uLCB3aGljaCBwZXJmb3JtZWQgYmV0dGVyIGJhc2VkIG9uIHNlbnNpdGl2aXR5IGFuZCBhY2N1cmFjeSByYXRpbmdzLg0KDQpgYGB7cn0NCnByZV9wcm9jZXNzZWRfZGF0YSA9IGRwbHlyOjphc19kYXRhX2ZyYW1lKGRmTm90VmFsaWRhdGlvbikgJT4lIA0KICBkcGx5cjo6c2VsZWN0KC1NUklfNl93ZWVrcywgDQogICAgICAgICAgICAgICAgLXhyYXlfNl93ZWVrcywgDQogICAgICAgICAgICAgICAgLU1SSV9hbnksIA0KICAgICAgICAgICAgICAgIC14cmF5X2FueSwgDQogICAgICAgICAgICAgICAgLSBNRU1fTlVNLA0KICAgICAgICAgICAgICAgIC0gRVBJU09ERV9OVU0sDQogICAgICAgICAgICAgICAgLSBzdXJnZXJ5X2xhZykgJT4lDQogIGFzLmRhdGEuZnJhbWUoKQ0KDQpgYGANCg0KUHJpb3IgdG8gdGhlIG92ZXJzYW1wbGluZyB0aGVyZSBpcyBhYm91dCBhIDk1JS01JSBzcGxpdCBiZXR3ZWVuIHRob3NlIGVwaXNvZGVzIHdpdGggc3VyZ2VyeSBhbmQgd2l0aG91dC4NCg0KYGBge3IgfQ0KDQp0MSA9IFN5cy50aW1lKCkNCnJmLmRhdGEuc21vdGUgPSBTTU9URShzdXJnZXJ5IH4uLCANCiAgICAgICAgICAgICAgICAgICAgICBkYXRhIDwtIHByZV9wcm9jZXNzZWRfZGF0YSwgDQogICAgICAgICAgICAgICAgICAgICAgcGVyYy5vdmVyID0gMTUwMCwNCiAgICAgICAgICAgICAgICAgICAgICBwZXJjLnVuZGVyID0gMTAwLA0KICAgICAgICAgICAgICAgICAgICAgIGsgPSA5KQ0KDQp0MiA9IFN5cy50aW1lKCkNCnJ1bl90aW1lID0gdDEgLSB0Mg0KcnVuX3RpbWUNCmRpbShyZi5kYXRhLnNtb3RlKQ0Kc3VtbWFyeShyZi5kYXRhLnNtb3RlJHN1cmdlcnkpDQpgYGANCg0KQWZ0ZXIgdGhlIGJvb3RzdHJhcCB0aGVyZSBhcmUgcm91Z2hseSBhbiBlcXVhbCBudW1iZXIgb2YgY2FzZXMgd2l0aCBhbmQgd2l0aG91dCBzdXJnZXJ5Lg0KDQojRml0dGluZyBhIE1vZGVsDQoNCkJlY2F1c2Ugb2YgdGhlIG51bWJlciBvZiBiaW5hcnkgcHJlZGljdG9ycyBhbmQgbmVlZCB0byBjb21wZW5zYXRlIGZvciBvdmVyZml0dGluZywgYSByYW5kb20gZm9yZXN0IHdhcyB1c2VkIHdpdGggYW4gYXV0b21hdGVkIHR1bmluZyBmdW5jdGlvbiBmcm9tIHRoZSBsaWJyYXJ5IGBjYXJldGAuDQoNCmBgYHtyIH0NCnQxID0gU3lzLnRpbWUoKQ0KIyBDcmVhdGUgbW9kZWwgd2l0aCBkZWZhdWx0IHBhcmFtdGVycw0KY29udHJvbCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kID0gInJlcGVhdGVkY3YiLCBudW1iZXI9MTAsIHJlcGVhdHM9MykNCnNlZWQgPC0gNw0KbWV0cmljIDwtICJLYXBwYSINCnNldC5zZWVkKHNlZWQpDQptdHJ5IDwtIHNxcnQobmNvbChyZi5kYXRhLnNtb3RlKSkNCnR1bmVncmlkIDwtIGV4cGFuZC5ncmlkKC5tdHJ5PW10cnkpDQoNCnJmX3Ntb3RlIDwtIHRyYWluKHN1cmdlcnkgfi4sIA0KICAgICAgICAgICAgICAgICAgICBkYXRhPSByZi5kYXRhLnNtb3RlLCANCiAgICAgICAgICAgICAgICAgICAgbWV0aG9kPSJyZiIsIA0KICAgICAgICAgICAgICAgICAgICBtZXRyaWM9bWV0cmljLCANCiAgICAgICAgICAgICAgICAgICAgdHVuZUdyaWQ9dHVuZWdyaWQsIA0KICAgICAgICAgICAgICAgICAgICB0ckNvbnRyb2w9Y29udHJvbCkNCg0KdDIgPSBTeXMudGltZSgpDQpyZl9ydW50aW1lID0gdDIgLSB0MQ0KcmZfcnVudGltZQ0KYGBgDQoNCg0KIyNUZXN0aW5nIHRoZSBNb2RlbA0KDQpUZXN0aW5nIHdhcyBjb25kdWN0ZWQgYm90aCBhZ2FpbnN0IHRoZSBib290c3RyYXBwZWQgZGF0YSBhbmQgdGhlIHVuLWluZmxhdGVkIHZhbGlkYXRpb24gc2V0LCB3aGljaCBjb250YWluZWQgdGhlIHJhdyBvYnNlcnZhdGlvbnMuICBUaGUgY3V0b2ZmIHZhbHVlIGZvciB0aGUgcmFuZG9tIGZvcmVzdCB3YXMgb3B0aW1pemVkIGJhc2VkIG9uIHRoZSBzZW5zaXRpdml0eSBhbmQgYWNjdXJhY3kgb2YgdGhlIG1vZGVsLg0KDQojI1Rlc3QgYWdhaW5zdCBib290c3RyYXBwZWQgZGF0YQ0KDQpgYGB7ciBmaWcud2lkdGg9IDQsIGZpZy5oZWlnaHQ9IDR9DQoNCnByZWRpY3RvciA9IHJmX3Ntb3RlJGZpbmFsTW9kZWwkdm90ZXNbLDFdDQpyZXNwb25zZSA9IHJmLmRhdGEuc21vdGUkc3VyZ2VyeSANCg0Kcm9jKHJlc3BvbnNlLCBwcmVkaWN0b3IsIHBlcmNlbnQ9VFJVRSwNCiAgICAgICAgICAgICMgYXJndW1lbnRzIGZvciBhdWMNCiAgICAgICAgICAgIHBsb3Q9VFJVRSwgDQogICAgICAgICAgICBhdWMucG9seWdvbj1UUlVFLCANCiAgICAgICAgICAgIG1heC5hdWMucG9seWdvbj1UUlVFLCANCiAgICAgICAgICAgIGdyaWQ9VFJVRSwNCiAgICAgICAgICAgIHByaW50LmF1Yz1UUlVFLCANCiAgICAgICAgICAgIHNob3cudGhyZXM9VFJVRSkNCmBgYA0KDQpUaGUgQXJlYSBVbmRlciB0aGUgQ3VydmUgKEFVQykgb2YgMC45NDIgaW5kaWNhdGVzIHRoYXQgdGhlIG1vZGVsIGZpdHMgdGhlIGJvb3RzdHJhcHBlZCBkYXRhIHdlbGwuICBUaGUgaW50ZXJwcmV0YXRpb24gaXMgdGhlIHByb2JhYmlsaXR5IHRoYXQgYSByYW5kb21seSBzZWxlY3RlZCBzdWJqZWN0IHdobyBlbmRzIHVwIHdpdGggc3VyZ2VyeSBoYXMgYSB0ZXN0IHJlc3VsdCBpbmRpY2F0aW5nIGdyZWF0ZXIgc3VzcGljaW9uIHRoYW4gdGhhdCBvZiBhIHJhbmRvbWx5IGNob3NlbiBzdWJqZWN0IHdobyBkb2VzIG5vdCBlbmQgdXAgaGF2aW5nIHN1cmdlcnkuIChIYW5sZXkgJiBNY05laWwsIDE5ODIpDQoNCmBgYHtyIGZpZy53aWR0aD0gNCwgZmlnLmhlaWdodD0gNH0NCmRldi5uZXcoKQ0KcHJlZCA9IHByZWRpY3Rpb24oIHByZWRpY3RvciwgcmVzcG9uc2UpDQpwZXJmID0gcGVyZm9ybWFuY2UocHJlZCwgbWVhc3VyZT0idHByIix4Lm1lYXN1cmU9ImN1dG9mZiIpDQpwbG90KHBlcmYsIA0KICAgICBtYWluID0gIlRydWUgUG9zaXRpdmUgUmF0ZSB2cyBDdXRvZmYiLA0KICAgICB0eXBlID0gInMiKQ0KYGBgDQpUaGUgdHJ1ZSBwb3NpdGl2ZSByYXRlIChzZW5zaXRpdml0eSkgaXMgaGlnaGVzdCB3aGVuIHRoZSBjdXRvZmYgaXMgc2V0IHRvIGNsb3NlIHRvIHplcm8uICBCZWNhdXNlIHRoZSBnb2FsIGlzIHRvIGNvcnJlY3RseSBpZGVudGlmeSB0aG9zZSBwYXRpZW50cyB3aG8gaGF2ZSBzdXJnZXJ5LCB0aGUgY3V0b2ZmIHdhcyBzZXQgY2xvc2UgdG8gemVybyBpbiBvcmRlciB0byBvcHRpbWl6ZSB0aGUgc2Vuc2l0aXZpdHkgb2YgdGhlIG1vZGVsLCBldmVuIGF0IHRoZSBjb3N0IG9mIHJlZHVjZWQgYWNjdXJhY3kuICBUaGlzIGRvZXMgbm90IGVmZmVjdCB0aGUgdmFyaWFibGUgaW1wb3J0YW5jZSByYW5raW5nLCBob3dldmVyLiAgDQoNCmBgYHtyIH0NCmRldi5uZXcoKQ0KcHJlZCA9IHByZWRpY3Rpb24oIHByZWRpY3RvciwgcmVzcG9uc2UpDQpwZXJmID0gcGVyZm9ybWFuY2UocHJlZCwgbWVhc3VyZT0iZnByIix4Lm1lYXN1cmU9ImN1dG9mZiIpDQpwbG90KHBlcmYsIA0KICAgICBtYWluID0gIkZhbHNlIFBvc2l0aXZlIFJhdGUgdnMgQ3V0b2ZmIiwNCiAgICAgdHlwZSA9ICJzIikNCmBgYA0KDQojI1Rlc3QgQWdhaW5zdCBWYWxpZGF0aW9uIFNldA0KDQpgYGB7ciB9DQpyZl9zbW90ZSRmaW5hbE1vZGVsJGZvcmVzdCRjdXRvZmYgPSBjKDAuMDAwMSwgMC45OTk5KQ0KcHJlZGljdGlvbiA9IHByZWRpY3QoIG9iamVjdCA9IHJmX3Ntb3RlLA0KICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBkZlZhbGlkYXRpb24pDQoNCmNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9uLCBkZlZhbGlkYXRpb24kc3VyZ2VyeSkNCmBgYA0KDQojSW50ZXJwcmV0YXRpb24NCg0KIyNWYXJpYWJsZSBJbXBvcnRhbmNlDQpgYGB7ciB9DQpuYW1lcyA9IGMoIk51bWJlciBvZiBQVCBWaXNpdHMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQW55IFBUIFkvTiIsICJNUkkgaW4gdGhlIGZpcnN0IDggd2Vla3MiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hpcm9wcmFjdGljIFNwaW5hbCBNYW5pcHVsYXRpb24iLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRGlhZ25vc3RpYyBJbmplY3Rpb24iLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiWFJheSBpbiB0aGUgZmlyc3QgOCB3ZWVrcyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTZXZlcml0eSAxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU2V2ZXJpdHkgMiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNldmVyaXR5IDMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNZW1iZXIgQWdlIiwgIk51bWJlciBvZiBFcGlzb2RlIiwgIkNvbW9yYmlkaXR5IFkvTiIpDQoNCmltcG9ydGFuY2UgPSB2YXJJbXAocmZfc21vdGUkZmluYWxNb2RlbCkkT3ZlcmFsbA0KDQpwb3NpdGlvbnMgPSBkYXRhX2ZyYW1lKG5hbWVzLCBpbXBvcnRhbmNlKSAlPiUNCiAgYXJyYW5nZShpbXBvcnRhbmNlKSAlPiUNCiAgc2VsZWN0KG5hbWVzKSANCg0KDQpwb3NpdGlvbnMgPC0gcG9zaXRpb25zJG5hbWVzDQoNCmdnZGF0YSA9IGRhdGFfZnJhbWUobmFtZXMsIGltcG9ydGFuY2UpDQoNCmdncGxvdChnZ2RhdGEsIGFlcyh4ID0gbmFtZXMsIHkgPSBpbXBvcnRhbmNlKSkgKyANCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3IgPSAiZ3JleSIpICsgIA0KICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IHBvc2l0aW9ucykgKyANCiAgY29vcmRfZmxpcCgpICsgeWxhYigiUmVsYXRpdmUgSW1wb3JhbmNlIikgKyANCiAgeGxhYigiVmFyaWFibGUiKSArIA0KICB0aGVtZV9saWdodCgpICsgDQogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xNikpDQpgYGANCg0KDQojI0xvZ2lzdGljIFJlZ3Jlc3Npb24gb2YgVG9wIFByZWRpY3RvcnMNCg0KQSBsb2dpc3RpYyByZWdyZXNzaW9uIGFsbG93cyBmb3IgaW5mZXJyZW5jZSBvbiB3aGV0aGVyIG9yIG5vdCBlYWNoIHByZWRpY3RvciBpbmNyZWFzZXMgb3IgZGVjcmVhc2VzIHRoZSBwcm9iYWJpbGl0eSBvZiBoYXZpbmcgc3VyZ2VyeS4gIEEgdmFyaWFibGUgd2l0aCBhIHBvc2l0aXZlIGNvZWZmaWNpZW50IGluZGljYXRlcyB0aGF0IHRoaXMgdmFyaWFibGUgaW5jcmVhc2VzIHRoZSBsaWtlbHlob29kIG9mIHRoZSBwYXRpZW50IGhhdmluZyBzdXJnZXJ5Lg0KDQpUaGUgc2Vuc2l0aXZpdHkgb2YgdGhlIGxvZ2lzdGljIG1vZGVsIGlzIG9ubHkgNDIlLiAgVGhlIHNpZ24gb2YgdGhlIGNvZWZmaWNpZW50IGlzIGFsbCB0aGF0IG1hdHRlcnMgaW4gdGhpcyBjYXNlLg0KYGBge3IgfQ0KY29udHJvbCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kID0gInJlcGVhdGVkY3YiLCBudW1iZXI9MTAsIHJlcGVhdHM9MykNCg0KbG9naXQgPSB0cmFpbiggc3VyZ2VyeSB+IC4sDQogICAgICAgICAgICAgICBkYXRhID0gcmYuZGF0YS5zbW90ZSwgDQogICAgICAgICAgICAgICBtZXRob2QgPSAiZ2xtIiwNCiAgICAgICAgICAgICAgIGZhbWlseSA9ICJiaW5vbWlhbCIsIA0KICAgICAgICAgICAgICAgdHJDb250cm9sID0gY29udHJvbCkNCg0KDQpwcmVkaWN0aW9uID0gcHJlZGljdCggb2JqZWN0ID0gbG9naXQsDQogICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IGRmVmFsaWRhdGlvbikNCg0KY29uZnVzaW9uTWF0cml4KHByZWRpY3Rpb24sIGRmVmFsaWRhdGlvbiRzdXJnZXJ5KQ0KYGBgDQojI0NvZWZmaWNpZW50cw0KYGBge3J9DQpleHAoY29lZihsb2dpdCRmaW5hbE1vZGVsKSkNCmBgYA0KDQoNCg0K