When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).
The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.
Data preprocessing
From payments_raw to payments: Change attributes to correct variable type and drop unnecessary columns.
# prepare payments data: change attribute type and drop unnecessary columns
payments = payments_raw %>% mutate(Date=as.Date(Date, format='%d/%m/%Y'))
payments = payments %>% mutate_at(vars(Form_of_Payment_or_Transfer_of_Value,Nature_of_Payment_or_Transfer_of_Value,
City_of_Travel,State_of_Travel,Country_of_Travel,
Ownership_Indicator,Third_Party_Recipient,Charity,Third_Party_Covered,
Related_Product_Indicator,Product_Type_1,Product_Type_2,Product_Type_3,
Product_Category_1,Product_Category_2,Product_Category_3),as.factor)
payments = payments %>% select(-City_of_Travel, -State_of_Travel, -Country_of_Travel,
-Charity, -Third_Party_Covered, -Contextual_Information,
-Product_Code_1, -Product_Code_2, -Product_Code_3,
-Product_Name_1, -Product_Name_2, -Product_Name_3)
From payments to payments_train: Filter out all transactions related to physicians from training set.
# prepare payments train: payments associated with training set physicians
payments_train = payments %>% merge(physicians_raw %>% filter(set == 'train') %>% select(id), by.x='Physician_ID', by.y='id')
Refine payments_train: Column Ownership_Indicator no longer refers to the payment but the physician.It tells whether the physician has an ownership interest, i.e. if she has at least one payment labeled as ownership interest. Whether the actual payment was labeled as ownership interest can be seen in the (new) column Ownership_Indicator_Payment.
physicians_id_fraud = payments_train %>% filter(Ownership_Indicator == "Yes") %>% select(Physician_ID) %>% distinct()
payments_train = payments_train %>% mutate(Physician_Ownership_Interest = case_when(Physician_ID %in% physicians_id_fraud$Physician_ID ~ "Yes",TRUE ~ "No"))
payments_train = payments_train %>% rename(Ownership_Indicator_Payment = Ownership_Indicator)
payments_train = payments_train %>% rename(Ownership_Indicator = Physician_Ownership_Interest)
payments_train = payments_train %>% relocate(Ownership_Indicator, .after = Physician_ID)
payments_train = payments_train %>% relocate(Ownership_Indicator_Payment, .after = Record_ID)
payments_train = payments_train %>% filter(Ownership_Indicator_Payment == "No")
Presenting payments_train:
[1] "Physician_ID" "Ownership_Indicator"
[3] "Record_ID" "Ownership_Indicator_Payment"
[5] "Company_ID" "Total_Amount_of_Payment_USDollars"
[7] "Date" "Number_of_Payments"
[9] "Form_of_Payment_or_Transfer_of_Value" "Nature_of_Payment_or_Transfer_of_Value"
[11] "Third_Party_Recipient" "Related_Product_Indicator"
[13] "Product_Type_1" "Product_Type_2"
[15] "Product_Type_3" "Product_Category_1"
[17] "Product_Category_2" "Product_Category_3"
Exploratory analysis
Procedure:
1. Group payments_train by Ownership_Indicator and compare different attributes.
2. Create additional feature from payments, if the attribute helps to distinguish between ownership interest.
Amount of Transactions per Ownership_Indicator
As we can see, 116,983 payments belong to physicians, which have an ownership interest (through other payments not in the data set).

Total amounts of payments
Total amount of payments is way higher for ownership interest. Create additional feature avg_total_amount_of_payments with high values signaling ownership interest.

Number of payment rates per payment
Average number of payments is slightly higher for ownership interest. Not sure if the difference is enough for an additional feature.

Nature of Payment
Not sure if binary features are justified here. We need to exclude attributes which do not help us to distinguish.

We need to exclude further attributes, which do not help to distinguish.

‘Education’ is associated with no ownership interest. ‘Current or prospective…’ is (obviously) associated with ownership interest. Not sure about the rest.

Third Party Recipient
Third party recipients - entities or individuals - are associated with ownership interest. Create binary feature third_party which signals ownership interest.

Product Type (1)
‘Biological’ and ‘Device or Medical Supply’ are associated with ownership interest. Not sure if enough evidence to construct a binary feature.

Physician Specialties
Pull out Primary_Specialty, factor in three different columns and join with physicians:
primary_specialties = physicians %>% select(id, Primary_Specialty)
primary_specialties = primary_specialties %>% separate(Primary_Specialty, into = c("First_Specialty","Second_Specialty","Third_Specialty"), sep = "\\|")
physicians = physicians %>% merge(primary_specialties)
physicians = physicians %>% relocate(First_Specialty,Second_Specialty,Third_Specialty,.before=Primary_Specialty)
We go from this:
To this:
Examine specialties

Examine absolute amount of first specialties

Lets examine share of second specialties based on first specialty

Lets look at first specialites apart from Allopathic & Osteopathic.

Merge specialties per physician with payments
payments_train_specialty = payments_train %>% inner_join(physicians %>% select(id,First_Specialty,Second_Specialty,Third_Specialty),by=c("Physician_ID" = "id"))
payments_train_specialty = payments_train_specialty %>% relocate(First_Specialty,Second_Specialty,Third_Specialty,.after=Physician_ID)
Examine relationship of ownership interest and first specialty.

Remove Allopathic & Osteopathic Physicians to get a closer view

Lets look at this numerically:
No Yes
Allopathic & Osteopathic Physicians 95.484 97.295
Chiropractic Providers 0.002 0.187
Dental Providers 1.246 1.307
Eye and Vision Services Providers 2.160 0.652
Podiatric Medicine & Surgery Service Providers 1.108 0.558
Lets have a final look into second specialties - nothing interesting here.

The recipe
??recipes
library(recipes)
We need to specify one outcome and n predictors. That means we need to have one data table in tidy format, i.e. the physicians. Each row represents one physician (ID) and each column represents one feature (which we engineer from payments and companies).
Lets make a mock data frame i_physicians:
Fraud = c(0,0,1,0,0)
Physician_ID = c(1,2,3,4,5)
License_State = c("NY","CA","TX","NY","CA")
Primary_Specialty = c("Allopathic","Radiology","Osteopathic","Physician","Cardiology")
Total_amount_payments = c(9945,2210,34250,12050,1256)
Total_number_of_payments = c(5,12,45,22,8)
i_physicians = data.frame(Fraud,Physician_ID,License_State,Primary_Specialty,Total_amount_payments,Total_number_of_payments)
i_physicians
Lets specify the outcome and the predictors:
rec_obj = recipe(Fraud ~ ., data = i_physicians) %>% update_role(Physician_ID, new_role = "ID")
rec_obj
Data Recipe
Inputs:
Convert categorical predictors into numeric dummy variables:
ind_obj = rec_obj %>% step_dummy(all_predictors(), -all_numeric())
ind_obj
Data Recipe
Inputs:
Operations:
Dummy variables from all_predictors(), -all_numeric()
As all variables are now numeric, we can center and scale them:
stand_obj = ind_obj %>% step_center(all_predictors()) %>% step_scale(all_predictors())
stand_obj
Data Recipe
Inputs:
Operations:
Dummy variables from all_predictors(), -all_numeric()
Centering for all_predictors()
Scaling for all_predictors()
Estimate means and sd from training set:
trained_rec = prep(stand_obj, training = i_physicians)
trained_rec
Data Recipe
Inputs:
Training data contained 5 data points and no missing data.
Operations:
Dummy variables from License_State, Primary_Specialty [trained]
Centering for Total_amount_payments, ... [trained]
Scaling for Total_amount_payments, ... [trained]
Now, the preprocessing can be applied to the actual training (and test) set:
i_train = bake(trained_rec, new_data = i_physicians)
#j_test = bake(trained_rec, new_data = j_physicians)
i_train
LS0tDQp0aXRsZTogIkFuYWx5dGljcyBDdXAiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBkZl9wcmludDogcGFnZWQNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gDQoNCldoZW4geW91IHNhdmUgdGhlIG5vdGVib29rLCBhbiBIVE1MIGZpbGUgY29udGFpbmluZyB0aGUgY29kZSBhbmQgb3V0cHV0IHdpbGwgYmUgc2F2ZWQgYWxvbmdzaWRlIGl0IChjbGljayB0aGUgKlByZXZpZXcqIGJ1dHRvbiBvciBwcmVzcyAqQ3RybCtTaGlmdCtLKiB0byBwcmV2aWV3IHRoZSBIVE1MIGZpbGUpLg0KDQpUaGUgcHJldmlldyBzaG93cyB5b3UgYSByZW5kZXJlZCBIVE1MIGNvcHkgb2YgdGhlIGNvbnRlbnRzIG9mIHRoZSBlZGl0b3IuIENvbnNlcXVlbnRseSwgdW5saWtlICpLbml0KiwgKlByZXZpZXcqIGRvZXMgbm90IHJ1biBhbnkgUiBjb2RlIGNodW5rcy4gSW5zdGVhZCwgdGhlIG91dHB1dCBvZiB0aGUgY2h1bmsgd2hlbiBpdCB3YXMgbGFzdCBydW4gaW4gdGhlIGVkaXRvciBpcyBkaXNwbGF5ZWQuDQoNCioqKg0KDQojIyMgU2V0dXANCmBgYHtyLCBtZXNzYWdlPUZ9DQojIGltcG9ydCBwYWNrYWdlcw0Kc2V0d2QoIn4vUi9UVU0gQnVzaW5lc3MgQW5hbHl0aWNzLzA5IEFuYWx5dGljcyBDdXAiKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHJlYWRyKQ0KbGlicmFyeShkYXRhLnRhYmxlKQ0KbGlicmFyeSh0aWJibGUpDQpvcHRpb25zKGRwbHlyLndpZHRoID0gSW5mKQ0KdGhlbWVfc2V0KHRoZW1lX21pbmltYWwoKSkNCmBgYA0KDQojIyMgRGF0YSBpbXBvcnQNCmBgYHtyfQ0KIyBpbXBvcnQgcmF3IGRhdGENCnBoeXNpY2lhbnNfcmF3ID0gYXNfdGliYmxlKHJlYWQuY3N2KCdkYXRhL3BoeXNpY2lhbnMuY3N2JykpICAjIDYwMDAgeCAxNw0KcGF5bWVudHNfcmF3ID0gYXNfdGliYmxlKHJlYWQuY3N2KCdkYXRhL3BheW1lbnRzLmNzdicpKSAgICAgICMgMTQwMjI1MCB4IDI5DQpjb21wYW5pZXNfcmF3ID0gYXNfdGliYmxlKHJlYWQuY3N2KCdkYXRhL2NvbXBhbmllcy5jc3YnKSkgICAgIyAyNDMxIHggNA0KYGBgDQoNCiMjIyBEYXRhIHByZXByb2Nlc3NpbmcNCkZyb20gYHBheW1lbnRzX3Jhd2AgdG8gYHBheW1lbnRzYDogQ2hhbmdlIGF0dHJpYnV0ZXMgdG8gY29ycmVjdCB2YXJpYWJsZSB0eXBlIGFuZCBkcm9wIHVubmVjZXNzYXJ5IGNvbHVtbnMuDQpgYGB7cn0NCiMgcHJlcGFyZSBwYXltZW50cyBkYXRhOiBjaGFuZ2UgYXR0cmlidXRlIHR5cGUgYW5kIGRyb3AgdW5uZWNlc3NhcnkgY29sdW1ucw0KcGF5bWVudHMgPSBwYXltZW50c19yYXcgJT4lIG11dGF0ZShEYXRlPWFzLkRhdGUoRGF0ZSwgZm9ybWF0PSclZC8lbS8lWScpKQ0KcGF5bWVudHMgPSBwYXltZW50cyAlPiUgbXV0YXRlX2F0KHZhcnMoRm9ybV9vZl9QYXltZW50X29yX1RyYW5zZmVyX29mX1ZhbHVlLE5hdHVyZV9vZl9QYXltZW50X29yX1RyYW5zZmVyX29mX1ZhbHVlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ2l0eV9vZl9UcmF2ZWwsU3RhdGVfb2ZfVHJhdmVsLENvdW50cnlfb2ZfVHJhdmVsLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgT3duZXJzaGlwX0luZGljYXRvcixUaGlyZF9QYXJ0eV9SZWNpcGllbnQsQ2hhcml0eSxUaGlyZF9QYXJ0eV9Db3ZlcmVkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUmVsYXRlZF9Qcm9kdWN0X0luZGljYXRvcixQcm9kdWN0X1R5cGVfMSxQcm9kdWN0X1R5cGVfMixQcm9kdWN0X1R5cGVfMywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFByb2R1Y3RfQ2F0ZWdvcnlfMSxQcm9kdWN0X0NhdGVnb3J5XzIsUHJvZHVjdF9DYXRlZ29yeV8zKSxhcy5mYWN0b3IpDQpwYXltZW50cyA9IHBheW1lbnRzICU+JSBzZWxlY3QoLUNpdHlfb2ZfVHJhdmVsLCAtU3RhdGVfb2ZfVHJhdmVsLCAtQ291bnRyeV9vZl9UcmF2ZWwsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLUNoYXJpdHksIC1UaGlyZF9QYXJ0eV9Db3ZlcmVkLCAtQ29udGV4dHVhbF9JbmZvcm1hdGlvbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtUHJvZHVjdF9Db2RlXzEsIC1Qcm9kdWN0X0NvZGVfMiwgLVByb2R1Y3RfQ29kZV8zLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC1Qcm9kdWN0X05hbWVfMSwgLVByb2R1Y3RfTmFtZV8yLCAtUHJvZHVjdF9OYW1lXzMpDQpgYGANCg0KRnJvbSBgcGF5bWVudHNgIHRvIGBwYXltZW50c190cmFpbmA6IEZpbHRlciBvdXQgYWxsIHRyYW5zYWN0aW9ucyByZWxhdGVkIHRvIHBoeXNpY2lhbnMgZnJvbSB0cmFpbmluZyBzZXQuDQpgYGB7cn0NCiMgcHJlcGFyZSBwYXltZW50cyB0cmFpbjogcGF5bWVudHMgYXNzb2NpYXRlZCB3aXRoIHRyYWluaW5nIHNldCBwaHlzaWNpYW5zDQpwYXltZW50c190cmFpbiA9IHBheW1lbnRzICU+JSBtZXJnZShwaHlzaWNpYW5zX3JhdyAlPiUgZmlsdGVyKHNldCA9PSAndHJhaW4nKSAlPiUgc2VsZWN0KGlkKSwgYnkueD0nUGh5c2ljaWFuX0lEJywgYnkueT0naWQnKQ0KYGBgDQoNClJlZmluZSBgcGF5bWVudHNfdHJhaW5gOiBDb2x1bW4gYE93bmVyc2hpcF9JbmRpY2F0b3JgIG5vIGxvbmdlciByZWZlcnMgdG8gdGhlIHBheW1lbnQgYnV0IHRoZSBwaHlzaWNpYW4uSXQgdGVsbHMgd2hldGhlciB0aGUgcGh5c2ljaWFuIGhhcyBhbiBvd25lcnNoaXAgaW50ZXJlc3QsIGkuZS4gaWYgc2hlIGhhcyBhdCBsZWFzdCBvbmUgcGF5bWVudCBsYWJlbGVkIGFzIG93bmVyc2hpcCBpbnRlcmVzdC4gV2hldGhlciB0aGUgYWN0dWFsIHBheW1lbnQgd2FzIGxhYmVsZWQgYXMgb3duZXJzaGlwIGludGVyZXN0IGNhbiBiZSBzZWVuIGluIHRoZSAobmV3KSBjb2x1bW4gYE93bmVyc2hpcF9JbmRpY2F0b3JfUGF5bWVudGAuDQpgYGB7ciwgd2FybmluZz1GfQ0KcGh5c2ljaWFuc19pZF9mcmF1ZCA9IHBheW1lbnRzX3RyYWluICU+JSBmaWx0ZXIoT3duZXJzaGlwX0luZGljYXRvciA9PSAiWWVzIikgJT4lIHNlbGVjdChQaHlzaWNpYW5fSUQpICU+JSBkaXN0aW5jdCgpDQpwYXltZW50c190cmFpbiA9IHBheW1lbnRzX3RyYWluICU+JSBtdXRhdGUoUGh5c2ljaWFuX093bmVyc2hpcF9JbnRlcmVzdCA9IGNhc2Vfd2hlbihQaHlzaWNpYW5fSUQgJWluJSBwaHlzaWNpYW5zX2lkX2ZyYXVkJFBoeXNpY2lhbl9JRCB+ICJZZXMiLFRSVUUgfiAiTm8iKSkNCnBheW1lbnRzX3RyYWluID0gcGF5bWVudHNfdHJhaW4gJT4lIHJlbmFtZShPd25lcnNoaXBfSW5kaWNhdG9yX1BheW1lbnQgPSBPd25lcnNoaXBfSW5kaWNhdG9yKQ0KcGF5bWVudHNfdHJhaW4gPSBwYXltZW50c190cmFpbiAlPiUgcmVuYW1lKE93bmVyc2hpcF9JbmRpY2F0b3IgPSBQaHlzaWNpYW5fT3duZXJzaGlwX0ludGVyZXN0KQ0KcGF5bWVudHNfdHJhaW4gPSBwYXltZW50c190cmFpbiAlPiUgcmVsb2NhdGUoT3duZXJzaGlwX0luZGljYXRvciwgLmFmdGVyID0gUGh5c2ljaWFuX0lEKQ0KcGF5bWVudHNfdHJhaW4gPSBwYXltZW50c190cmFpbiAlPiUgcmVsb2NhdGUoT3duZXJzaGlwX0luZGljYXRvcl9QYXltZW50LCAuYWZ0ZXIgPSBSZWNvcmRfSUQpDQpwYXltZW50c190cmFpbiA9IHBheW1lbnRzX3RyYWluICU+JSBmaWx0ZXIoT3duZXJzaGlwX0luZGljYXRvcl9QYXltZW50ID09ICJObyIpDQpgYGANClwNClByZXNlbnRpbmcgYHBheW1lbnRzX3RyYWluYDoNCmBgYHtyLCBlY2hvPUZ9DQpwYXltZW50c190cmFpbiAlPiUgbmFtZXMoKQ0KYGBgDQoqKioNCg0KIyMjIEV4cGxvcmF0b3J5IGFuYWx5c2lzDQpQcm9jZWR1cmU6XA0KMS4gR3JvdXAgYHBheW1lbnRzX3RyYWluYCBieSBgT3duZXJzaGlwX0luZGljYXRvcmAgYW5kIGNvbXBhcmUgZGlmZmVyZW50IGF0dHJpYnV0ZXMuXA0KMi4gQ3JlYXRlIGFkZGl0aW9uYWwgZmVhdHVyZSBmcm9tIGBwYXltZW50c2AsIGlmIHRoZSBhdHRyaWJ1dGUgaGVscHMgdG8gZGlzdGluZ3Vpc2ggYmV0d2VlbiBvd25lcnNoaXAgaW50ZXJlc3QuXA0KXA0KDQojIyMjIEFtb3VudCBvZiBUcmFuc2FjdGlvbnMgcGVyIE93bmVyc2hpcF9JbmRpY2F0b3INCkFzIHdlIGNhbiBzZWUsIDExNiw5ODMgcGF5bWVudHMgYmVsb25nIHRvIHBoeXNpY2lhbnMsIHdoaWNoIGhhdmUgYW4gb3duZXJzaGlwIGludGVyZXN0ICh0aHJvdWdoIG90aGVyIHBheW1lbnRzIG5vdCBpbiB0aGUgZGF0YSBzZXQpLg0KYGBge3IsIGVjaG89RiwgbWVzc2FnZT1GfQ0KcGF5bWVudHNfdHJhaW4gJT4lIGNvdW50KE93bmVyc2hpcF9JbmRpY2F0b3IpICU+JSBnZ3Bsb3QoYWVzKHg9T3duZXJzaGlwX0luZGljYXRvciwgeT1uKSkgKw0KICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIGZpbGw9J3R1cnF1b2lzZScpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1uKSwgdmp1c3Q9LTAuNSkgKw0KICBsYWJzKHRpdGxlID0gIk51bWJlciBvZiBwYXltZW50cyBwZXIgcGh5c2ljaWFuIG93bmVyc2hpcCBpbnRlcmVzdCIsIHk9Ik51bWJlciBvZiBwYXltZW50cyIpDQpgYGANClwNCg0KIyMjIyBUb3RhbCBhbW91bnRzIG9mIHBheW1lbnRzDQpUb3RhbCBhbW91bnQgb2YgcGF5bWVudHMgaXMgd2F5IGhpZ2hlciBmb3Igb3duZXJzaGlwIGludGVyZXN0LiBDcmVhdGUgYWRkaXRpb25hbCBmZWF0dXJlIGBhdmdfdG90YWxfYW1vdW50X29mX3BheW1lbnRzYCB3aXRoIGhpZ2ggdmFsdWVzIHNpZ25hbGluZyBvd25lcnNoaXAgaW50ZXJlc3QuDQpgYGB7ciwgZWNobz1GLCBtZXNzYWdlPUZ9DQpwYXltZW50c190cmFpbiAlPiUgZ3JvdXBfYnkoT3duZXJzaGlwX0luZGljYXRvcikgJT4lIHN1bW1hcmlzZShhdmdfVG90YWxfQW1vdW50X29mX1BheW1lbnRfVVNEb2xsYXJzID0gbWVhbihUb3RhbF9BbW91bnRfb2ZfUGF5bWVudF9VU0RvbGxhcnMpKSAlPiUgZ2dwbG90KGFlcyh4PU93bmVyc2hpcF9JbmRpY2F0b3IsIHk9YXZnX1RvdGFsX0Ftb3VudF9vZl9QYXltZW50X1VTRG9sbGFycykpICsNCiAgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknLCBmaWxsPSd0dXJxdW9pc2UnKSArDQogIGdlb21fdGV4dChhZXMobGFiZWw9cm91bmQoYXZnX1RvdGFsX0Ftb3VudF9vZl9QYXltZW50X1VTRG9sbGFycywgZGlnaXRzPTMpKSwgdmp1c3Q9LTAuNSkgKw0KICBsYWJzKHRpdGxlID0gJ0F2ZXJhZ2UgdG90YWwgYW1vdW50IG9mIHBheW1lbnRzIHBlciBvd25lcnNoaXAgaW50ZXJlc3QnLCB5ID0gJ0F2ZXJhZ2UgVG90YWwgYW1vdW50IG9mIFBheW1lbnQnKQ0KYGBgDQpcDQoNCiMjIyMgTnVtYmVyIG9mIHBheW1lbnQgcmF0ZXMgcGVyIHBheW1lbnQNCkF2ZXJhZ2UgbnVtYmVyIG9mIHBheW1lbnRzIGlzIHNsaWdodGx5IGhpZ2hlciBmb3Igb3duZXJzaGlwIGludGVyZXN0LiBOb3Qgc3VyZSBpZiB0aGUgZGlmZmVyZW5jZSBpcyBlbm91Z2ggZm9yIGFuIGFkZGl0aW9uYWwgZmVhdHVyZS4NCmBgYHtyLCBlY2hvPUYsIG1lc3NhZ2U9Rn0NCnBheW1lbnRzX3RyYWluICU+JSBncm91cF9ieShPd25lcnNoaXBfSW5kaWNhdG9yKSAlPiUgc3VtbWFyaXNlKGF2Z19OdW1iZXJfb2ZfUGF5bWVudHMgPSBtZWFuKE51bWJlcl9vZl9QYXltZW50cykpICU+JSBnZ3Bsb3QoYWVzKHk9YXZnX051bWJlcl9vZl9QYXltZW50cywgeD1Pd25lcnNoaXBfSW5kaWNhdG9yKSkgKyANCiAgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknLCBmaWxsPSd0dXJxdW9pc2UnKSArDQogIGdlb21fdGV4dChhZXMobGFiZWw9cm91bmQoYXZnX051bWJlcl9vZl9QYXltZW50cywgZGlnaXRzID0gMykpLCB2anVzdD0tMC41KSArDQogIGxhYnModGl0bGUgPSAnQXZlcmFnZSBudW1iZXIgb2YgcGF5bWVudHMgKHBlciBwYXltZW50KSBwZXIgb3duZXJzaGlwIGludGVyZXN0JywgeSA9ICdBdmVyYWdlIG51bWJlciBvZiBwYXltZW50cycpDQpgYGANClwNCg0KIyMjIyBGb3JtIG9mIFBheW1lbnQNCkV4Y2VwdCBmb3IgQ2FzaCBhbmQgaW4ta2luZCBpdGVtcyBhbmQgc2VydmljZXMsIGFsbCBvdGhlciBmb3JtcyBhcmUgMTAwJSBhc3NvY2lhdGVkIHdpdGggb3duZXJzaGlwIGludGVyZXN0LlwNCkNyZWF0ZSBiaW5hcnkgdmFyaWFibGUgdGhhdCBzaWduYWxzLCBpZiBgRm9ybV9vZl9QYXltZW50X29yX1RyYW5zZmVyX29mX1ZhbHVlYCBkb2VzIG5vdCBjb250YWluICdjYXNoJyBvciAnaXRlbXMnLg0KYGBge3IsIGVjaG89RiwgbWVzc2FnZT1GfQ0KcGF5bWVudHNfdHJhaW4gJT4lIGNvdW50KE93bmVyc2hpcF9JbmRpY2F0b3IsIEZvcm1fb2ZfUGF5bWVudF9vcl9UcmFuc2Zlcl9vZl9WYWx1ZSkgJT4lICBnZ3Bsb3QoYWVzKHg9T3duZXJzaGlwX0luZGljYXRvcix5PW4sZmlsbD1Gb3JtX29mX1BheW1lbnRfb3JfVHJhbnNmZXJfb2ZfVmFsdWUpKSArIA0KICBnZW9tX2NvbChwb3NpdGlvbiA9ICdmaWxsJykgKyANCiAgbGFicyh5PSdSZWxhdGl2ZSBmcmVxdWVuY3knLCB0aXRsZSA9ICdGcmVxdWVuY3kgb2YgRm9ybSBvZiBwYXltZW50IHBlciBvd25lcnNoaXAgaW50ZXJlc3QnLCBmaWxsID0gJ0Zvcm1zIG9mIHBheW1lbnQnKQ0KDQojIHBheW1lbnRzX3RyYWluICU+JSBjb3VudChPd25lcnNoaXBfSW5kaWNhdG9yLCBGb3JtX29mX1BheW1lbnRfb3JfVHJhbnNmZXJfb2ZfVmFsdWUpICU+JSBhcnJhbmdlKGRlc2MobikpDQpgYGANClwNCg0KIyMjIyBOYXR1cmUgb2YgUGF5bWVudA0KTm90IHN1cmUgaWYgYmluYXJ5IGZlYXR1cmVzIGFyZSBqdXN0aWZpZWQgaGVyZS4gV2UgbmVlZCB0byBleGNsdWRlIGF0dHJpYnV0ZXMgd2hpY2ggZG8gbm90IGhlbHAgdXMgdG8gZGlzdGluZ3Vpc2guDQpgYGB7ciwgZWNobz1GLCBtZXNzYWdlPUZ9DQpwYXltZW50c190cmFpbiAlPiUgY291bnQoT3duZXJzaGlwX0luZGljYXRvcixOYXR1cmVfb2ZfUGF5bWVudF9vcl9UcmFuc2Zlcl9vZl9WYWx1ZSkgJT4lIGdncGxvdChhZXMoeD1Pd25lcnNoaXBfSW5kaWNhdG9yLHk9biwgZmlsbD1zdHJfd3JhcChOYXR1cmVfb2ZfUGF5bWVudF9vcl9UcmFuc2Zlcl9vZl9WYWx1ZSw1MCkpKSArIA0KICBnZW9tX2NvbChwb3NpdGlvbj0nZmlsbCcpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJyaWdodCIsIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA3KSkgKw0KICBsYWJzKGZpbGw9JycsIHRpdGxlPSdGcmVxdWVuY3kgb2YgTmF0dXJlIG9mIHBheW1lbnQgcGVyIG93bmVyc2hpcCBpbnRlcmVzdCAjMScseT0nUmVsYXRpdmUgZnJlcXVlbmN5JykNCmBgYA0KXA0KV2UgbmVlZCB0byBleGNsdWRlIGZ1cnRoZXIgYXR0cmlidXRlcywgd2hpY2ggZG8gbm90IGhlbHAgdG8gZGlzdGluZ3Vpc2guDQpgYGB7ciwgZWNobz1GLCBtZXNzYWdlPUZ9DQpwYXltZW50c190cmFpbiAlPiUgZmlsdGVyKCFncmVwbCgiRm9vZHxMb2RnaW5nfHZlbnVlIiwgTmF0dXJlX29mX1BheW1lbnRfb3JfVHJhbnNmZXJfb2ZfVmFsdWUpKSAlPiUgY291bnQoT3duZXJzaGlwX0luZGljYXRvcixOYXR1cmVfb2ZfUGF5bWVudF9vcl9UcmFuc2Zlcl9vZl9WYWx1ZSkgJT4lIGdncGxvdChhZXMoeD1Pd25lcnNoaXBfSW5kaWNhdG9yLHk9bixmaWxsPXN0cl93cmFwKE5hdHVyZV9vZl9QYXltZW50X29yX1RyYW5zZmVyX29mX1ZhbHVlLDUwKSkpICsgZ2VvbV9jb2wocG9zaXRpb249ImZpbGwiKSArDQp0aGVtZShsZWdlbmQucG9zaXRpb249InJpZ2h0IiwgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKSArDQpsYWJzKGZpbGw9JycsIHRpdGxlPSdGcmVxdWVuY3kgb2YgTmF0dXJlIG9mIHBheW1lbnQgcGVyIG93bmVyc2hpcCBpbnRlcmVzdCAjMicseT0nUmVsYXRpdmUgZnJlcXVlbmN5JykNCmBgYA0KXA0KJ0VkdWNhdGlvbicgaXMgYXNzb2NpYXRlZCB3aXRoIG5vIG93bmVyc2hpcCBpbnRlcmVzdC4gJ0N1cnJlbnQgb3IgcHJvc3BlY3RpdmUuLi4nIGlzIChvYnZpb3VzbHkpIGFzc29jaWF0ZWQgd2l0aCBvd25lcnNoaXAgaW50ZXJlc3QuIE5vdCBzdXJlIGFib3V0IHRoZSByZXN0Lg0KYGBge3IsIGVjaG89RiwgbWVzc2FnZT1GfQ0KcGF5bWVudHNfdHJhaW4gJT4lIGZpbHRlcighZ3JlcGwoIkZvb2R8TG9kZ2luZ3x2ZW51ZXxDb25zdWx0aW5nfEdpZnR8bm9uLWFjY3JlZGl0ZWQiLCBOYXR1cmVfb2ZfUGF5bWVudF9vcl9UcmFuc2Zlcl9vZl9WYWx1ZSkpICU+JSBjb3VudChPd25lcnNoaXBfSW5kaWNhdG9yLE5hdHVyZV9vZl9QYXltZW50X29yX1RyYW5zZmVyX29mX1ZhbHVlKSAlPiUgZ2dwbG90KGFlcyh4PU93bmVyc2hpcF9JbmRpY2F0b3IseT1uLGZpbGw9c3RyX3dyYXAoTmF0dXJlX29mX1BheW1lbnRfb3JfVHJhbnNmZXJfb2ZfVmFsdWUsNTApKSkgKyBnZW9tX2NvbChwb3NpdGlvbj0iZmlsbCIpICsNCnRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiLCBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCkpICsNCmxhYnMoZmlsbD0nJywgdGl0bGU9J0ZyZXF1ZW5jeSBvZiBOYXR1cmUgb2YgcGF5bWVudCBwZXIgb3duZXJzaGlwIGludGVyZXN0ICMzJyx5PSdSZWxhdGl2ZSBmcmVxdWVuY3knKQ0KYGBgDQpcDQoNCiMjIyMgVGhpcmQgUGFydHkgUmVjaXBpZW50DQpUaGlyZCBwYXJ0eSByZWNpcGllbnRzIC0gZW50aXRpZXMgb3IgaW5kaXZpZHVhbHMgLSBhcmUgYXNzb2NpYXRlZCB3aXRoIG93bmVyc2hpcCBpbnRlcmVzdC4gQ3JlYXRlIGJpbmFyeSBmZWF0dXJlIGB0aGlyZF9wYXJ0eWAgd2hpY2ggc2lnbmFscyBvd25lcnNoaXAgaW50ZXJlc3QuDQpgYGB7ciwgZWNobz1GLCBtZXNzYWdlPUZ9DQp0YWIgPSBwcm9wLnRhYmxlKHRhYmxlKHBheW1lbnRzX3RyYWluJE93bmVyc2hpcF9JbmRpY2F0b3IsIHBheW1lbnRzX3RyYWluJFRoaXJkX1BhcnR5X1JlY2lwaWVudCksMSkgJT4lIGFzLmRhdGEuZnJhbWUoKQ0KdGFiICU+JSBnZ3Bsb3QoYWVzKHg9VmFyMix5PUZyZXEsZmlsbD1WYXIxKSkgKw0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICdkb2RnZScsIHN0YXQgPSAnaWRlbnRpdHknKSArDQogIGdlb21fdGV4dChwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC45KSwgYWVzKGxhYmVsPXJvdW5kKEZyZXEsIGRpZ2l0cyA9IDMpLCB2anVzdD0tMC41KSkgKw0KICBsYWJzKHRpdGxlID0gJ1JlbGF0aXZlIGZyZXF1ZW5jeSBvZiB0aGlyZCBwYXJ0eSByZWNpcGllbnQgcGVyIG93bmVyc2hpcCBpbnRlcmVzdCcsIHg9J1RoaXJkIHBhcnR5IHJlY2lwaWVudCcsIHk9J1JlbGF0aXZlIGZyZXFlbmN5JywgZmlsbD0nT3duZXJzaGlwIGludGVyZXN0JykNCmBgYA0KXA0KDQojIyMjIFJlbGF0ZWQgcHJvZHVjdCBpbmRpY2F0b3IgKDEpDQonTm8nIG9yICdOb25lJyBwcm9kdWN0IGluZGljYXRvciBpcyBhc3NvY2lhdGVkIHdpdGggb3duZXJzaGlwIGludGVyZXN0LiBOb3Qgc3VyZSBpZiBlbm91Z2ggZXZpZGVuY2UgdG8gY29uc3RydWN0IGEgYmluYXJ5IGZlYXR1cmUuDQpgYGB7ciwgZWNobz1GLCBtZXNzYWdlPUZ9DQpwb3NpdGlvbnMgPSBjKCdOb25lJywnTm8nLCdOb24tQ292ZXJlZCcsIkNvbWJpbmF0aW9uIiwiQ292ZXJlZCIsIlllcyIpDQp0YWIgPSBwcm9wLnRhYmxlKHRhYmxlKHBheW1lbnRzX3RyYWluJE93bmVyc2hpcF9JbmRpY2F0b3IsIHBheW1lbnRzX3RyYWluJFJlbGF0ZWRfUHJvZHVjdF9JbmRpY2F0b3IpLDEpICU+JSBhcy5kYXRhLmZyYW1lKCkNCnRhYiAlPiUgZ2dwbG90KGFlcyh4PVZhcjIseT1GcmVxLGZpbGw9VmFyMSkpICsNCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAnZG9kZ2UnLCBzdGF0ID0gJ2lkZW50aXR5JykgKw0KICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IHBvc2l0aW9ucykgKw0KICBsYWJzKHRpdGxlID0gIkZyZXF1ZW5jeSBvZiBSZWxhdGVkIHByb2R1Y3QgaW5kaWNhdG9yIHBlciBvd25lcnNoaXAgaW50ZXJlc3QiLCB4PSJQcm9kdWN0IGluZGljYXRvciIsIHk9IlJlbGF0aXZlIGZyZXF1ZW5jeSIsIGZpbGw9Ik93bmVyc2hpcCBpbnRlcmVzdCIpICsNCiAgZ2VvbV90ZXh0KHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjkpLCBhZXMobGFiZWw9cm91bmQoRnJlcSwgZGlnaXRzID0gMyksIHZqdXN0PS0wLjUpKQ0KICANCmBgYA0KXA0KDQojIyMjIFByb2R1Y3QgVHlwZSAoMSkNCidCaW9sb2dpY2FsJyBhbmQgJ0RldmljZSBvciBNZWRpY2FsIFN1cHBseScgYXJlIGFzc29jaWF0ZWQgd2l0aCBvd25lcnNoaXAgaW50ZXJlc3QuIE5vdCBzdXJlIGlmIGVub3VnaCBldmlkZW5jZSB0byBjb25zdHJ1Y3QgYSBiaW5hcnkgZmVhdHVyZS4NCmBgYHtyLCBlY2hvPUYsIG1lc3NhZ2U9Rn0NCnBvc2l0aW9ucyA9IGMoIkRydWciLCJEcnVnIG9yIEJpb2xvZ2ljYWwiLCJCaW9sb2dpY2FsIiwiRGV2aWNlIiwiRGV2aWNlIG9yIE1lZGljYWwgU3VwcGx5IiwiTWVkaWNhbCBTdXBwbHkiKQ0KdGFiID0gcHJvcC50YWJsZSh0YWJsZShwYXltZW50c190cmFpbiRPd25lcnNoaXBfSW5kaWNhdG9yLHBheW1lbnRzX3RyYWluJFByb2R1Y3RfVHlwZV8xKSwxKSAlPiUgYXMuZGF0YS5mcmFtZSgpDQp0YWIgJT4lIGdncGxvdChhZXMoeD1zdHJfd3JhcChWYXIyLDE1KSx5PUZyZXEsZmlsbD1WYXIxKSkgKw0KICBnZW9tX2Jhcihwb3NpdGlvbj0nZG9kZ2UnLCBzdGF0ID0gImlkZW50aXR5IikgKw0KICBnZW9tX3RleHQocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuOSksIGFlcyhsYWJlbD1yb3VuZChGcmVxLCBkaWdpdHMgPSAzKSwgdmp1c3Q9LTAuNSkpICsNCiAgbGFicyh0aXRsZSA9ICJQcm9kdWN0IHR5cGUgZnJlcXVlbmN5IHBlciBvd25lcnNoaXAgaW50ZXJlc3QiLCB4PSJQcm9kdWN0IHR5cGUiLHk9IlJlbGF0aXZlIGZyZXF1ZW5jeSIsZmlsbD0iT3duZXJzaGlwIGludGVyZXN0IikNCmBgYA0KKioqDQpcDQoNCiMjIyMgUGh5c2ljaWFuIFNwZWNpYWx0aWVzDQpgYGB7cixlY2hvPUZ9DQpwaHlzaWNhbnNfb3duX2xpc3QgPC0gcGF5bWVudHNfcmF3ICU+JSBmaWx0ZXIoT3duZXJzaGlwX0luZGljYXRvciA9PSAiWWVzIikgJT4lIHB1bGwoUGh5c2ljaWFuX0lEKSAlPiUgdW5pcXVlKCkNCnBoeXNpY2lhbnMgPC0gcGh5c2ljaWFucyAlPiUgbXV0YXRlKEZyYXVkX2xhYmVsID0gY2FzZV93aGVuKChpZCAlaW4lIHBoeXNpY2Fuc19vd25fbGlzdCkgfiAxLCBUUlVFIH4gMCkpDQpgYGANCg0KUHVsbCBvdXQgUHJpbWFyeV9TcGVjaWFsdHksIGZhY3RvciBpbiB0aHJlZSBkaWZmZXJlbnQgY29sdW1ucyBhbmQgam9pbiB3aXRoIGBwaHlzaWNpYW5zYDoNCmBgYHtyLCB3YXJuaW5nPUZ9DQpwcmltYXJ5X3NwZWNpYWx0aWVzID0gcGh5c2ljaWFucyAlPiUgc2VsZWN0KGlkLCBQcmltYXJ5X1NwZWNpYWx0eSkNCnByaW1hcnlfc3BlY2lhbHRpZXMgPSBwcmltYXJ5X3NwZWNpYWx0aWVzICU+JSBzZXBhcmF0ZShQcmltYXJ5X1NwZWNpYWx0eSwgaW50byA9IGMoIkZpcnN0X1NwZWNpYWx0eSIsIlNlY29uZF9TcGVjaWFsdHkiLCJUaGlyZF9TcGVjaWFsdHkiKSwgc2VwID0gIlxcfCIpDQoNCnBoeXNpY2lhbnMgPSBwaHlzaWNpYW5zICU+JSBtZXJnZShwcmltYXJ5X3NwZWNpYWx0aWVzKQ0KcGh5c2ljaWFucyA9IHBoeXNpY2lhbnMgJT4lIHJlbG9jYXRlKEZpcnN0X1NwZWNpYWx0eSxTZWNvbmRfU3BlY2lhbHR5LFRoaXJkX1NwZWNpYWx0eSwuYmVmb3JlPVByaW1hcnlfU3BlY2lhbHR5KQ0KYGBgDQoNCldlIGdvIGZyb20gdGhpczoNCmBgYHtyLGVjaG89Rn0NCnBoeXNpY2lhbnMgJT4lIHNlbGVjdChQcmltYXJ5X1NwZWNpYWx0eSkgJT4lIGhlYWQoMykNCmBgYA0KDQpUbyB0aGlzOg0KYGBge3IsIGVjaG89Rix3YXJuaW5nPUZ9DQpwaHlzaWNpYW5zICU+JSBzZXBhcmF0ZShQcmltYXJ5X1NwZWNpYWx0eSwgaW50byA9IGMoIkZpcnN0X1NwZWNpYWx0eSIsIlNlY29uZF9TcGVjaWFsdHkiLCJUaGlyZF9TcGVjaWFsdHkiKSwgc2VwID0gIlxcfCIpICU+JSBzZWxlY3QoRmlyc3RfU3BlY2lhbHR5LFNlY29uZF9TcGVjaWFsdHksVGhpcmRfU3BlY2lhbHR5KSAlPiUgaGVhZCgzKQ0KYGBgDQpcDQoNCkV4YW1pbmUgc3BlY2lhbHRpZXMNCmBgYHtyLCBlY2hvPUZ9DQpzcGVjaWFsdGllcyA9IGMoIkZpcnN0IHNwZWNpYWx0eSIsIlNlY29uZCBzcGVjaWFsdHkiLCAiVGhpcmQgc3BlY2lhbHR5IikNCm4gPSBjKHRhYmxlKHBoeXNpY2lhbnMkRmlyc3RfU3BlY2lhbHR5LHVzZU5BID0gImFsd2F5cyIpICU+JSBsZW5ndGgoKSx0YWJsZShwaHlzaWNpYW5zJFNlY29uZF9TcGVjaWFsdHksIHVzZU5BID0gImFsd2F5cyIpICU+JSBsZW5ndGgoKSx0YWJsZShwaHlzaWNpYW5zJFRoaXJkX1NwZWNpYWx0eSx1c2VOQSA9ICJhbHdheXMiKSAlPiUgbGVuZ3RoKCkpDQpucl9zcGVjaWFsdGllcyA9IGRhdGEuZnJhbWUoc3BlY2lhbHRpZXMsbikNCm5yX3NwZWNpYWx0aWVzICU+JSBnZ3Bsb3QoYWVzKHg9c3BlY2lhbHRpZXMseT1uKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbD0idHVycXVvaXNlIikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsPW4pLHZqdXN0PS0wLjUpICsNCiAgbGFicyh0aXRsZSA9ICJOdW1iZXIgb2YgZGlmZmVyZW50IHNwZWNpYWx0aWVzIHBlciBjYXRlb2dvcnkiKQ0KYGBgDQpcDQoNCkV4YW1pbmUgYWJzb2x1dGUgYW1vdW50IG9mIGZpcnN0IHNwZWNpYWx0aWVzDQpgYGB7cixlY2hvPUZ9DQp0YWJsZShwaHlzaWNpYW5zJEZpcnN0X1NwZWNpYWx0eSx1c2VOQSA9ICJhbHdheXMiKSAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSBnZ3Bsb3QoYWVzKHg9c3RyX3dyYXAoVmFyMSwxNSkseT1GcmVxKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbD0idHVycXVvaXNlIikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsPUZyZXEpLHZqdXN0PS0wLjMpICsNCiAgbGFicyh0aXRsZSA9ICJOdW1iZXIgb2YgZmlyc3Qgc3BlY2lhbHRpZXMiLHg9IkZpcnN0IHNwZWNpYWx0aWVzIix5PSJuIikNCmBgYA0KXA0KDQpMZXRzIGV4YW1pbmUgc2hhcmUgb2Ygc2Vjb25kIHNwZWNpYWx0aWVzIGJhc2VkIG9uIGZpcnN0IHNwZWNpYWx0eQ0KYGBge3IsZWNobz1GfQ0KcGh5c2ljaWFucyAlPiUgY291bnQoRmlyc3RfU3BlY2lhbHR5LFNlY29uZF9TcGVjaWFsdHkpICU+JSBhcnJhbmdlKGRlc2MobikpICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIGdncGxvdChhZXMoeD1zdHJfd3JhcChGaXJzdF9TcGVjaWFsdHksOCkseT1uLGZpbGw9c3RyX3dyYXAoU2Vjb25kX1NwZWNpYWx0eSwxNSkpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiLCBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNikpICsNCiAgbGFicyh0aXRsZSA9ICJTZWNvbmQgc3BlY2lhbGl0aWVzIGJhc2VkIG9uIGZpcnN0IHNwZWNpYWxpdGllcyAjMSIseD0iRmlyc3Qgc3BlY2lhbHRpZXMiLGZpbGw9IiIpICsNCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5jb2w9MykpDQpgYGANClwNCg0KTGV0cyBsb29rIGF0IGZpcnN0IHNwZWNpYWxpdGVzIGFwYXJ0IGZyb20gQWxsb3BhdGhpYyAmIE9zdGVvcGF0aGljLg0KYGBge3IsZWNobz1GfQ0KcGh5c2ljaWFucyAlPiUgZmlsdGVyKCFGaXJzdF9TcGVjaWFsdHkgPT0gIkFsbG9wYXRoaWMgJiBPc3Rlb3BhdGhpYyBQaHlzaWNpYW5zIikgJT4lIGNvdW50KEZpcnN0X1NwZWNpYWx0eSxTZWNvbmRfU3BlY2lhbHR5KSAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSBnZ3Bsb3QoYWVzKHg9c3RyX3dyYXAoRmlyc3RfU3BlY2lhbHR5LDIwKSx5PW4sZmlsbD1TZWNvbmRfU3BlY2lhbHR5KSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsPW4pLHZqdXN0PS0wLjMpICsNCiAgbGFicyh0aXRsZSA9ICJTZWNvbmQgc3BlY2lhbHRpZXMgYmFzZWQgb24gZmlyc3Qgc3BlY2lhbHRpZXMgIzIiLHg9IkZpcnN0IHNwZWNpYWx0aWVzIG90aGVyIHRoYW4gQWxsb3BhdGhpYyAmIE9zdGVvcGF0aGljIixmaWxsPSJTZWNvbmQgc3BlY2lhbHR5IikNCmBgYA0KXA0KDQpNZXJnZSBzcGVjaWFsdGllcyBwZXIgcGh5c2ljaWFuIHdpdGggcGF5bWVudHMNCmBgYHtyfQ0KcGF5bWVudHNfdHJhaW5fc3BlY2lhbHR5ID0gcGF5bWVudHNfdHJhaW4gJT4lIGlubmVyX2pvaW4ocGh5c2ljaWFucyAlPiUgc2VsZWN0KGlkLEZpcnN0X1NwZWNpYWx0eSxTZWNvbmRfU3BlY2lhbHR5LFRoaXJkX1NwZWNpYWx0eSksYnk9YygiUGh5c2ljaWFuX0lEIiA9ICJpZCIpKQ0KDQpwYXltZW50c190cmFpbl9zcGVjaWFsdHkgPSBwYXltZW50c190cmFpbl9zcGVjaWFsdHkgJT4lIHJlbG9jYXRlKEZpcnN0X1NwZWNpYWx0eSxTZWNvbmRfU3BlY2lhbHR5LFRoaXJkX1NwZWNpYWx0eSwuYWZ0ZXI9UGh5c2ljaWFuX0lEKQ0KYGBgDQpcDQoNCkV4YW1pbmUgcmVsYXRpb25zaGlwIG9mIG93bmVyc2hpcCBpbnRlcmVzdCBhbmQgZmlyc3Qgc3BlY2lhbHR5Lg0KYGBge3IsZWNobz1GfQ0KcGF5bWVudHNfdHJhaW5fc3BlY2lhbHR5ICU+JSBjb3VudChPd25lcnNoaXBfSW5kaWNhdG9yLEZpcnN0X1NwZWNpYWx0eSkgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgZ2dwbG90KGFlcyh4PU93bmVyc2hpcF9JbmRpY2F0b3IseT1uLGZpbGw9Rmlyc3RfU3BlY2lhbHR5KSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iixwb3NpdGlvbiA9ICJmaWxsIikgKw0KICBsYWJzKHRpdGxlID0gIkZpcnN0IHNwZWNpYWx0eSByYXRpbyByZWxhdGl2ZSB0byBvd25lcnNoaXAgaW50ZXJlc3QgIzEiLHk9InJlbGF0aXZlIGZyZXF1ZW5jeSIpDQpgYGANClwNCg0KUmVtb3ZlIEFsbG9wYXRoaWMgJiBPc3Rlb3BhdGhpYyBQaHlzaWNpYW5zIHRvIGdldCBhIGNsb3NlciB2aWV3DQpgYGB7cixlY2hvPUZ9DQpwYXltZW50c190cmFpbl9zcGVjaWFsdHkgJT4lIGZpbHRlcighZ3JlcGwoIkFsbG9wYXRoaWMiLEZpcnN0X1NwZWNpYWx0eSkpICU+JSBjb3VudChPd25lcnNoaXBfSW5kaWNhdG9yLEZpcnN0X1NwZWNpYWx0eSkgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgZ2dwbG90KGFlcyh4PU93bmVyc2hpcF9JbmRpY2F0b3IseT1uLGZpbGw9Rmlyc3RfU3BlY2lhbHR5KSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iixwb3NpdGlvbiA9ICJmaWxsIikgKw0KICAgIGxhYnModGl0bGUgPSAiRmlyc3Qgc3BlY2lhbHR5IHJhdGlvIHJlbGF0aXZlIHRvIG93bmVyc2hpcCBpbnRlcmVzdCAjMiIseT0icmVsYXRpdmUgZnJlcXVlbmN5IikNCmBgYA0KXA0KDQpMZXRzIGxvb2sgYXQgdGhpcyBudW1lcmljYWxseToNCmBgYHtyLGVjaG89Rn0NCnByb3AudGFibGUodGFibGUocGF5bWVudHNfdHJhaW5fc3BlY2lhbHR5JEZpcnN0X1NwZWNpYWx0eSxwYXltZW50c190cmFpbl9zcGVjaWFsdHkkT3duZXJzaGlwX0luZGljYXRvciksMikgJT4lIGAqYCgxMDApICU+JSByb3VuZCgzKQ0KYGBgDQpcDQoNCkxldHMgaGF2ZSBhIGZpbmFsIGxvb2sgaW50byBzZWNvbmQgc3BlY2lhbHRpZXMgLSBub3RoaW5nIGludGVyZXN0aW5nIGhlcmUuDQpgYGB7cixlY2hvPUYsd2FybmluZz1GLG1lc3NhZ2U9Rn0NCnBoeXNpY2lhbnMgJT4lIGNvdW50KFNlY29uZF9TcGVjaWFsdHkpICU+JSBhcnJhbmdlKGRlc2MobikpICU+JSB0b3BfbigxMCkgJT4lIGdncGxvdChhZXMoeD1zdHJfd3JhcChTZWNvbmRfU3BlY2lhbHR5LDgpLHk9bikpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGw9InR1cnF1b2lzZSIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1uKSx2anVzdD0tMC4zKSArDQogIGxhYnModGl0bGUgPSAiVG9wIDEwIHNlY29uZCBzcGVjaWFsdGllcyIseD0iU2Vjb25kIHNwZWNpYWx0aWVzIikNCmBgYA0KDQoqKioNCiMjIyMgVGhlIHJlY2lwZQ0KYGBge3IsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KPz9yZWNpcGVzDQpsaWJyYXJ5KHJlY2lwZXMpDQpgYGANCg0KV2UgbmVlZCB0byBzcGVjaWZ5IG9uZSBvdXRjb21lIGFuZCBuIHByZWRpY3RvcnMuIFRoYXQgbWVhbnMgd2UgbmVlZCB0byBoYXZlIG9uZSBkYXRhIHRhYmxlIGluIHRpZHkgZm9ybWF0LCBpLmUuIHRoZSBgcGh5c2ljaWFuc2AuIEVhY2ggcm93IHJlcHJlc2VudHMgb25lIHBoeXNpY2lhbiAoSUQpIGFuZCBlYWNoIGNvbHVtbiByZXByZXNlbnRzIG9uZSBmZWF0dXJlICh3aGljaCB3ZSBlbmdpbmVlciBmcm9tIGBwYXltZW50c2AgYW5kIGBjb21wYW5pZXNgKS5cDQoNCkxldHMgbWFrZSBhIG1vY2sgZGF0YSBmcmFtZSBgaV9waHlzaWNpYW5zYDoNCmBgYHtyfQ0KRnJhdWQgPSBjKDAsMCwxLDAsMCkNClBoeXNpY2lhbl9JRCA9IGMoMSwyLDMsNCw1KQ0KTGljZW5zZV9TdGF0ZSA9IGMoIk5ZIiwiQ0EiLCJUWCIsIk5ZIiwiQ0EiKQ0KUHJpbWFyeV9TcGVjaWFsdHkgPSBjKCJBbGxvcGF0aGljIiwiUmFkaW9sb2d5IiwiT3N0ZW9wYXRoaWMiLCJQaHlzaWNpYW4iLCJDYXJkaW9sb2d5IikNClRvdGFsX2Ftb3VudF9wYXltZW50cyA9IGMoOTk0NSwyMjEwLDM0MjUwLDEyMDUwLDEyNTYpDQpUb3RhbF9udW1iZXJfb2ZfcGF5bWVudHMgPSBjKDUsMTIsNDUsMjIsOCkNCmlfcGh5c2ljaWFucyA9IGRhdGEuZnJhbWUoRnJhdWQsUGh5c2ljaWFuX0lELExpY2Vuc2VfU3RhdGUsUHJpbWFyeV9TcGVjaWFsdHksVG90YWxfYW1vdW50X3BheW1lbnRzLFRvdGFsX251bWJlcl9vZl9wYXltZW50cykNCmlfcGh5c2ljaWFucw0KYGBgDQoNCkxldHMgc3BlY2lmeSB0aGUgb3V0Y29tZSBhbmQgdGhlIHByZWRpY3RvcnM6DQpgYGB7cn0NCnJlY19vYmogPSByZWNpcGUoRnJhdWQgfiAuLCBkYXRhID0gaV9waHlzaWNpYW5zKSAlPiUgdXBkYXRlX3JvbGUoUGh5c2ljaWFuX0lELCBuZXdfcm9sZSA9ICJJRCIpDQpyZWNfb2JqDQpgYGANCg0KQ29udmVydCBjYXRlZ29yaWNhbCBwcmVkaWN0b3JzIGludG8gbnVtZXJpYyBkdW1teSB2YXJpYWJsZXM6DQpgYGB7cn0NCmluZF9vYmogPSByZWNfb2JqICU+JSBzdGVwX2R1bW15KGFsbF9wcmVkaWN0b3JzKCksIC1hbGxfbnVtZXJpYygpKQ0KaW5kX29iag0KYGBgDQoNCkFzIGFsbCB2YXJpYWJsZXMgYXJlIG5vdyBudW1lcmljLCB3ZSBjYW4gY2VudGVyIGFuZCBzY2FsZSB0aGVtOg0KYGBge3J9DQpzdGFuZF9vYmogPSBpbmRfb2JqICU+JSBzdGVwX2NlbnRlcihhbGxfcHJlZGljdG9ycygpKSAlPiUgc3RlcF9zY2FsZShhbGxfcHJlZGljdG9ycygpKQ0Kc3RhbmRfb2JqDQpgYGANCg0KRXN0aW1hdGUgbWVhbnMgYW5kIHNkIGZyb20gdHJhaW5pbmcgc2V0Og0KYGBge3J9DQp0cmFpbmVkX3JlYyA9IHByZXAoc3RhbmRfb2JqLCB0cmFpbmluZyA9IGlfcGh5c2ljaWFucykNCnRyYWluZWRfcmVjDQpgYGANCg0KTm93LCB0aGUgcHJlcHJvY2Vzc2luZyBjYW4gYmUgYXBwbGllZCB0byB0aGUgYWN0dWFsIHRyYWluaW5nIChhbmQgdGVzdCkgc2V0Og0KYGBge3J9DQppX3RyYWluID0gYmFrZSh0cmFpbmVkX3JlYywgbmV3X2RhdGEgPSBpX3BoeXNpY2lhbnMpDQojal90ZXN0ID0gYmFrZSh0cmFpbmVkX3JlYywgbmV3X2RhdGEgPSBqX3BoeXNpY2lhbnMpDQppX3RyYWluDQpgYGANCg0KDQoNCg0KDQoNCg0K