This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

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.


Setup

# import packages
setwd("~/R/TUM Business Analytics/09 Analytics Cup")
library(tidyverse)
library(readr)
library(data.table)
library(tibble)
options(dplyr.width = Inf)
theme_set(theme_minimal())

Data import

# import raw data
physicians_raw = as_tibble(read.csv('data/physicians.csv'))  # 6000 x 17
payments_raw = as_tibble(read.csv('data/payments.csv'))      # 1402250 x 29
companies_raw = as_tibble(read.csv('data/companies.csv'))    # 2431 x 4

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.


Form of Payment

Except for Cash and in-kind items and services, all other forms are 100% associated with ownership interest.
Create binary variable that signals, if Form_of_Payment_or_Transfer_of_Value does not contain ‘cash’ or ‘items’.


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