Introduction

One-year probability of default (PD) can be seen as a snapshot of lifetime PD. Two main reasons suggest treating one-year and lifetime separately. Firstly, banks have been developing one-year PD models over the last two decades for Basel II regulatory requirements. Secondly, a building-block-structure split in one-year and lifetime PD facilitates the learning process.

IFRS 9 and CECL accounting requirements lead banks to develop models by focusing on: point-intime (PIT), unbiased and forward-looking lifetime estimates. These requirements puzzle banks relying on the Basel II regulatory capital framework (BIS, 2006). Indeed, a one-year through-the-cycle (TTC) perspective informs Basel II capital requirement framework. Therefore new methodologies and information technology (IT) infrastructures are compulsory to align with new accounting requirements.

This chapter provides the foundation for PD model development and validation. A lifetime perspective is at the very heart of FASB (2016) accounting principle. On the other hand, IASB (2014) relies on one-year or lifetime expected losses, based on a significant increase in credit risk criteria. One may focus on one-year PDs as a snapshot of the lifetime PD term structure. Then, taking into account banks’ appetite to rely on a framework already put in place for Basel II and stress testing purposes, a split between one-year and lifetime PDs is considered beneficial for the entire learning journey.

In recent years, growing attention has been devoted to big data. Machine learning (ML) algorithms play a key role in this field. Classification and regression trees (CARTs), bagging, random forest, and boosting are studied both to challenge existing models, and explore new PD modelling solutions:

Examples and case studies are explored throughout the chapter by means of R software

# Load packages for modelling: 

rm(list = ls())
library(lubridate)
library(tidyverse)
library(caret)
library(scorecard)
library(corrplot)
library(broom)
library(pROC) 


#=================================
#   Stage 1: Default Definition
#=================================

# Import data and create default_flag by definition described in section 2.2.1: 
oneypd <- read.table("C:\\Users\\Zbook\\Desktop\\ifrs9_Bellini\\chap2\\oneyear.txt", sep = ",", header = TRUE) %>% 
  mutate(origination_date = ymd(origination_date), maturity_date = ymd(maturity_date), recent_arrears_date = ymd(recent_arrears_date)) %>% 
  mutate(default_flag = case_when(arrears_event == 1 | term_expiry_event == 1 | bankrupt_event == 1 ~ 1, TRUE ~ 0)) %>% 
  mutate(arrears_event = NULL, term_expiry_event = NULL, bankrupt_event = NULL, X = NULL, id = NULL)

# Some predictors for modelling: 

# my_vars <- c("bureau_score", "cc_util", "num_ccj", "max_arrears_12m", "max_arrears_bal_6m", 
#              "emp_length", "months_since_recent_cc_delinq", "annual_income", "default_flag")


my_vars <- c("cc_util", "num_ccj", "max_arrears_12m", "max_arrears_bal_6m", 
             "emp_length", "months_since_recent_cc_delinq", "annual_income", "default_flag")

# Select predictors selected: 
oneypd <- oneypd %>% select(my_vars)

# Split data: 
set.seed(2122)
id <- createDataPartition(y = oneypd$default_flag, p = 0.7, list = FALSE)
train <- oneypd[id, ]
test <- oneypd[-id, ]

#==================================
#  Stage 2: Univariate Analysis
#==================================

# WOE binning: 


bins <- woebin(train, y = "default_flag",  positive = "default_flag|1")
## [INFO] creating woe binning ...
## [INFO] converting into woe values ...

term estimate std.error statistic p.value
(Intercept) -2.920 0.053 -55.209 0.000
cc_util_woe -0.969 0.038 -25.447 0.000
max_arrears_12m_woe -0.772 0.075 -10.272 0.000
max_arrears_bal_6m_woe -0.277 0.082 -3.393 0.001
emp_length_woe -0.332 0.098 -3.395 0.001
months_since_recent_cc_delinq_woe -0.206 0.082 -2.505 0.012
annual_income_woe -0.879 0.059 -14.925 0.000
## [INFO] converting into woe values ...
Table 1: Scorecad Points by Group for Test Data (Stepwise Logistic)
default_flag min max median mean n
Default 295 572 406 405 416
NonDefault 295 586 539 520 7355

#========================================
#  Compare with Random Forest Approach
#========================================

# Precessing Data for modelling: 

df_train_ml <- train %>% 
  mutate_all(function(x) {replace_na(x, median(x, na.rm = TRUE))}) %>% 
  mutate(default_flag = case_when(default_flag == 1 ~ "Default", TRUE ~ "NonDefault") %>% as.factor())

df_test_ml <- test %>% mutate_all(function(x) {replace_na(x, median(x, na.rm = TRUE))})

# Conditions for training and evaluating RF model: 
number <- 5
repeats <- 5
n <- number*repeats

set.seed(1)
train.control <- trainControl(method = "repeatedcv", 
                              number = number,
                              repeats = repeats, 
                              classProbs = TRUE,
                              allowParallel = TRUE, 
                              summaryFunction = multiClassSummary)


# Train RF model: 
set.seed(1)
my_rf <- train(default_flag ~., 
               data = df_train_ml, 
               method = "ranger", 
               metric = "Accuracy", 
               trControl = train.control)


# PD from RF model: 

pd_rf <- predict(my_rf, df_test_ml, type = "prob") %>% pull(Default)


# Assign scores: 
scores_rf <- scaled_score(pd_rf)
scores_rf <- case_when(scores_rf == Inf ~ max(scores_rf[!is.infinite(scores_rf)]), TRUE ~ scores_rf)


df_scored_rf <- test %>%
  select(default_flag) %>% 
  mutate(SCORE = scores_rf) %>% 
  mutate(default_flag = case_when(default_flag == 1 ~ "Default", TRUE ~ "NonDefault")) 

df_scored_rf %>% 
  group_by(default_flag) %>% 
  summarise_each(funs(min, max, median, mean, n()), SCORE) %>% 
  mutate_if(is.numeric, function(x) {round(x, 0)}) %>% 
  knitr::kable(caption = "Table 2: Scorecad Points by Group for Test Data (RF Approach)")
Table 2: Scorecad Points by Group for Test Data (RF Approach)
default_flag min max median mean n
Default 282 566 396 401 416
NonDefault 305 773 541 557 7355

LS0tDQp0aXRsZTogIklGUlMgOSBhbmQgQ0VDTCBDcmVkaXQgUmlzayBNb2RlbGxpbmcgYW5kIFZhbGlkYXRpb24gKENoYXB0ZXIgMikiIA0Kc3VidGl0bGU6ICJSIGZvciBQbGVhc3VyZSINCmF1dGhvcjogIk5ndXllbiBDaGkgRHVuZyINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgICMgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgaGlnaGxpZ2h0OiBweWdtZW50cw0KICAgICMgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0aGVtZTogImZsYXRseSINCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCi0tLQ0KDQpgYGB7ciBzZXR1cCxpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSkNCmBgYA0KDQoNCiFbXShDOlxcVXNlcnNcXFpib29rXFxEZXNrdG9wXFxpZnJzOV9CZWxsaW5pXFxjaGFwMl9wMS5KUEcpDQoNCiMgSW50cm9kdWN0aW9uDQoNCk9uZS15ZWFyIHByb2JhYmlsaXR5IG9mIGRlZmF1bHQgKFBEKSBjYW4gYmUgc2VlbiBhcyBhIHNuYXBzaG90IG9mIGxpZmV0aW1lIFBELiBUd28gbWFpbiByZWFzb25zIHN1Z2dlc3QgdHJlYXRpbmcgb25lLXllYXIgYW5kIGxpZmV0aW1lIHNlcGFyYXRlbHkuIEZpcnN0bHksIGJhbmtzIGhhdmUgYmVlbiBkZXZlbG9waW5nIG9uZS15ZWFyIFBEIG1vZGVscyBvdmVyIHRoZSBsYXN0IHR3byBkZWNhZGVzIGZvciBCYXNlbCBJSSByZWd1bGF0b3J5IHJlcXVpcmVtZW50cy4gU2Vjb25kbHksIGEgYnVpbGRpbmctYmxvY2stc3RydWN0dXJlIHNwbGl0IGluIG9uZS15ZWFyIGFuZCBsaWZldGltZSBQRCBmYWNpbGl0YXRlcyB0aGUgbGVhcm5pbmcgcHJvY2Vzcy4NCg0KSUZSUyA5IGFuZCBDRUNMIGFjY291bnRpbmcgcmVxdWlyZW1lbnRzIGxlYWQgYmFua3MgdG8gZGV2ZWxvcCBtb2RlbHMgYnkgZm9jdXNpbmcgb246IHBvaW50LWludGltZSAoUElUKSwgdW5iaWFzZWQgYW5kIGZvcndhcmQtbG9va2luZyBsaWZldGltZSBlc3RpbWF0ZXMuIFRoZXNlIHJlcXVpcmVtZW50cyBwdXp6bGUgYmFua3MgcmVseWluZyBvbiB0aGUgQmFzZWwgSUkgcmVndWxhdG9yeSBjYXBpdGFsIGZyYW1ld29yayAoQklTLCAyMDA2KS4gSW5kZWVkLCBhIG9uZS15ZWFyIHRocm91Z2gtdGhlLWN5Y2xlIChUVEMpIHBlcnNwZWN0aXZlIGluZm9ybXMgQmFzZWwgSUkgY2FwaXRhbCByZXF1aXJlbWVudCBmcmFtZXdvcmsuIFRoZXJlZm9yZSBuZXcgbWV0aG9kb2xvZ2llcyBhbmQgaW5mb3JtYXRpb24gdGVjaG5vbG9neSAoSVQpIGluZnJhc3RydWN0dXJlcyBhcmUgY29tcHVsc29yeSB0byBhbGlnbiB3aXRoIG5ldyBhY2NvdW50aW5nIHJlcXVpcmVtZW50cy4NCg0KVGhpcyBjaGFwdGVyIHByb3ZpZGVzIHRoZSBmb3VuZGF0aW9uIGZvciBQRCBtb2RlbCBkZXZlbG9wbWVudCBhbmQgdmFsaWRhdGlvbi4gQSBsaWZldGltZSBwZXJzcGVjdGl2ZSBpcyBhdCB0aGUgdmVyeSBoZWFydCBvZiBGQVNCICgyMDE2KSBhY2NvdW50aW5nIHByaW5jaXBsZS4gT24gdGhlIG90aGVyIGhhbmQsIElBU0IgKDIwMTQpIHJlbGllcyBvbiBvbmUteWVhciBvciBsaWZldGltZSBleHBlY3RlZCBsb3NzZXMsIGJhc2VkIG9uIGEgc2lnbmlmaWNhbnQgaW5jcmVhc2UgaW4gY3JlZGl0IHJpc2sgY3JpdGVyaWEuIE9uZSBtYXkgZm9jdXMgb24gb25lLXllYXIgUERzIGFzIGEgc25hcHNob3Qgb2YgdGhlIGxpZmV0aW1lIFBEIHRlcm0gc3RydWN0dXJlLiBUaGVuLCB0YWtpbmcgaW50byBhY2NvdW50IGJhbmtz4oCZIGFwcGV0aXRlIHRvIHJlbHkgb24gYSBmcmFtZXdvcmsgYWxyZWFkeSBwdXQgaW4gcGxhY2UgZm9yIEJhc2VsIElJIGFuZCBzdHJlc3MgdGVzdGluZyBwdXJwb3NlcywgYSBzcGxpdCBiZXR3ZWVuIG9uZS15ZWFyIGFuZCBsaWZldGltZSBQRHMgaXMgY29uc2lkZXJlZCBiZW5lZmljaWFsIGZvciB0aGUgZW50aXJlIGxlYXJuaW5nIGpvdXJuZXkuDQoNCkluIHJlY2VudCB5ZWFycywgZ3Jvd2luZyBhdHRlbnRpb24gaGFzIGJlZW4gZGV2b3RlZCB0byBiaWcgZGF0YS4gTWFjaGluZSBsZWFybmluZyAoTUwpIGFsZ29yaXRobXMgcGxheSBhIGtleSByb2xlIGluIHRoaXMgZmllbGQuIENsYXNzaWZpY2F0aW9uIGFuZCByZWdyZXNzaW9uIHRyZWVzIChDQVJUcyksIGJhZ2dpbmcsIHJhbmRvbSBmb3Jlc3QsIGFuZCBib29zdGluZyBhcmUgc3R1ZGllZCBib3RoIHRvIGNoYWxsZW5nZSBleGlzdGluZyBtb2RlbHMsIGFuZCBleHBsb3JlIG5ldyBQRCBtb2RlbGxpbmcgc29sdXRpb25zOiANCg0KIVtdKEM6XFxVc2Vyc1xcWmJvb2tcXERlc2t0b3BcXGlmcnM5X0JlbGxpbmlcXGNoYXAyX3AyLnBuZykNCkV4YW1wbGVzIGFuZCBjYXNlIHN0dWRpZXMgYXJlIGV4cGxvcmVkIHRocm91Z2hvdXQgdGhlIGNoYXB0ZXIgYnkgbWVhbnMgb2YgUiBzb2Z0d2FyZQ0KDQoNCmBgYHtyfQ0KDQojIExvYWQgcGFja2FnZXMgZm9yIG1vZGVsbGluZzogDQoNCnJtKGxpc3QgPSBscygpKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KHNjb3JlY2FyZCkNCmxpYnJhcnkoY29ycnBsb3QpDQpsaWJyYXJ5KGJyb29tKQ0KbGlicmFyeShwUk9DKSANCg0KDQojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojICAgU3RhZ2UgMTogRGVmYXVsdCBEZWZpbml0aW9uDQojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCiMgSW1wb3J0IGRhdGEgYW5kIGNyZWF0ZSBkZWZhdWx0X2ZsYWcgYnkgZGVmaW5pdGlvbiBkZXNjcmliZWQgaW4gc2VjdGlvbiAyLjIuMTogDQpvbmV5cGQgPC0gcmVhZC50YWJsZSgiQzpcXFVzZXJzXFxaYm9va1xcRGVza3RvcFxcaWZyczlfQmVsbGluaVxcY2hhcDJcXG9uZXllYXIudHh0Iiwgc2VwID0gIiwiLCBoZWFkZXIgPSBUUlVFKSAlPiUgDQogIG11dGF0ZShvcmlnaW5hdGlvbl9kYXRlID0geW1kKG9yaWdpbmF0aW9uX2RhdGUpLCBtYXR1cml0eV9kYXRlID0geW1kKG1hdHVyaXR5X2RhdGUpLCByZWNlbnRfYXJyZWFyc19kYXRlID0geW1kKHJlY2VudF9hcnJlYXJzX2RhdGUpKSAlPiUgDQogIG11dGF0ZShkZWZhdWx0X2ZsYWcgPSBjYXNlX3doZW4oYXJyZWFyc19ldmVudCA9PSAxIHwgdGVybV9leHBpcnlfZXZlbnQgPT0gMSB8IGJhbmtydXB0X2V2ZW50ID09IDEgfiAxLCBUUlVFIH4gMCkpICU+JSANCiAgbXV0YXRlKGFycmVhcnNfZXZlbnQgPSBOVUxMLCB0ZXJtX2V4cGlyeV9ldmVudCA9IE5VTEwsIGJhbmtydXB0X2V2ZW50ID0gTlVMTCwgWCA9IE5VTEwsIGlkID0gTlVMTCkNCg0KIyBTb21lIHByZWRpY3RvcnMgZm9yIG1vZGVsbGluZzogDQoNCiMgbXlfdmFycyA8LSBjKCJidXJlYXVfc2NvcmUiLCAiY2NfdXRpbCIsICJudW1fY2NqIiwgIm1heF9hcnJlYXJzXzEybSIsICJtYXhfYXJyZWFyc19iYWxfNm0iLCANCiMgICAgICAgICAgICAgICJlbXBfbGVuZ3RoIiwgIm1vbnRoc19zaW5jZV9yZWNlbnRfY2NfZGVsaW5xIiwgImFubnVhbF9pbmNvbWUiLCAiZGVmYXVsdF9mbGFnIikNCg0KDQpteV92YXJzIDwtIGMoImNjX3V0aWwiLCAibnVtX2NjaiIsICJtYXhfYXJyZWFyc18xMm0iLCAibWF4X2FycmVhcnNfYmFsXzZtIiwgDQogICAgICAgICAgICAgImVtcF9sZW5ndGgiLCAibW9udGhzX3NpbmNlX3JlY2VudF9jY19kZWxpbnEiLCAiYW5udWFsX2luY29tZSIsICJkZWZhdWx0X2ZsYWciKQ0KDQojIFNlbGVjdCBwcmVkaWN0b3JzIHNlbGVjdGVkOiANCm9uZXlwZCA8LSBvbmV5cGQgJT4lIHNlbGVjdChteV92YXJzKQ0KDQojIFNwbGl0IGRhdGE6IA0Kc2V0LnNlZWQoMjEyMikNCmlkIDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oeSA9IG9uZXlwZCRkZWZhdWx0X2ZsYWcsIHAgPSAwLjcsIGxpc3QgPSBGQUxTRSkNCnRyYWluIDwtIG9uZXlwZFtpZCwgXQ0KdGVzdCA8LSBvbmV5cGRbLWlkLCBdDQoNCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojICBTdGFnZSAyOiBVbml2YXJpYXRlIEFuYWx5c2lzDQojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQojIFdPRSBiaW5uaW5nOiANCg0KDQpiaW5zIDwtIHdvZWJpbih0cmFpbiwgeSA9ICJkZWZhdWx0X2ZsYWciLCAgcG9zaXRpdmUgPSAiZGVmYXVsdF9mbGFnfDEiKQ0KDQpkby5jYWxsKCJyYmluZCIsIGJpbnMpICU+JSANCiAgZmlsdGVyKCFkdXBsaWNhdGVkKHZhcmlhYmxlKSkgJT4lIA0KICBmaWx0ZXIodG90YWxfaXYgPj0gMC4xKSAlPiUgDQogIHB1bGwodmFyaWFibGUpIC0+IHZhcl9JVl8wMQ0KDQojIFdPRSB0cmFuc2Zvcm1hdGlvbjogDQoNCnRyYWluX3dvZSA8LSB3b2ViaW5fcGx5KHRyYWluLCBiaW5zID0gYmlucykgJT4lIA0KICBhcy5kYXRhLmZyYW1lKCkgJT4lIA0KICBzZWxlY3QoZGVmYXVsdF9mbGFnLCBwYXN0ZSh2YXJfSVZfMDEsICJ3b2UiLCBzZXAgPSAiXyIpKQ0KDQpkZWZhdWx0X2ZsYWcgPC0gdHJhaW5fd29lJGRlZmF1bHRfZmxhZyANCg0KIyBDb3JyZWxhdGlvbnM6IA0KDQpteV9jb3JyIDwtIGNvcih0cmFpbl93b2UgJT4lIHNlbGVjdChjb250YWlucygid29lIikpKQ0KDQpteV9jb3JyICU+JQ0KICBjb3JycGxvdCguLCBtZXRob2QgPSAibnVtYmVyIiwgdGwuY2V4ID0gMC43KQ0KDQoNCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojICBTdGFnZSAzOiBMb2dpc3RpYyBTdGVwd2lzZSBSZWdyZXNzaW9uDQojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQp0cmFpbl93b2UgPC0gdHJhaW5fd29lICU+JSANCiAgc2VsZWN0KC1kZWZhdWx0X2ZsYWcpICU+JSANCiAgbXV0YXRlX2FsbChmdW5jdGlvbih4KSB7LXh9KSAlPiUgDQogIG11dGF0ZShkZWZhdWx0X2ZsYWcgPSBkZWZhdWx0X2ZsYWcpDQoNCmxvZ2l0X3N0ZXB3aXNlIDwtIGdsbShkZWZhdWx0X2ZsYWcgfi4sIGZhbWlseSA9ICJiaW5vbWlhbCIsIGRhdGEgPSB0cmFpbl93b2UpICU+JSANCiAgc3RlcCh0cmFjZSA9IDApDQoNCg0KdGlkeShsb2dpdF9zdGVwd2lzZSkgJT4lIA0KICBtdXRhdGVfaWYoaXMubnVtZXJpYywgZnVuY3Rpb24oeCkge3JvdW5kKHgsIDMpfSkgJT4lIA0KICBrbml0cjo6a2FibGUoKQ0KDQojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojICAgICBTdGFnZSA0OiBTY29yZSBmb3IgdGVzdCBkYXRhDQojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCnRlc3Rfd29lIDwtIHdvZWJpbl9wbHkodGVzdCwgYmlucyA9IGJpbnMpICU+JSANCiAgYXMuZGF0YS5mcmFtZSgpICU+JSANCiAgc2VsZWN0KHBhc3RlKHZhcl9JVl8wMSwgIndvZSIsIHNlcCA9ICJfIikpICU+JSANCiAgbXV0YXRlX2FsbChmdW5jdGlvbih4KSB7LXh9KQ0KDQpwZF90ZXN0IDwtIHByZWRpY3QobG9naXRfc3RlcHdpc2UsIHRlc3Rfd29lLCB0eXBlID0gInJlc3BvbnNlIikgJT4lIGFzLnZlY3RvcigpDQoNCg0KIyBGdW5jdGlvbiBmb3Igc2NvcmluZyBiYXNlZCBvbiBQRCBwcmVkaWN0ZWQ6IA0KDQpzY2FsZWRfc2NvcmUgPC0gZnVuY3Rpb24ocGRfc2VsZWN0ZWQpIHsNCiAgDQogIG9kZHMgPC0gNzINCiAgbXlfb2Zmc2V0IDwtIDUwMA0KICBwZG8gPC0gMjANCiAgYiA8LSBwZG8gLyBsb2coMikNCiAgYSA8LSBteV9vZmZzZXQgLSBiKmxvZyhvZGRzKQ0KICANCiAgc2NvcmVzIDwtIGEgKyBiKmxvZygoMSAtIHBkX3NlbGVjdGVkKSAvIHBkX3NlbGVjdGVkKQ0KICByZXR1cm4ocm91bmQoc2NvcmVzLCAwKSkNCiAgDQp9DQoNCiMgQXNzaWduIHNjb3JlY2FyZCBwb2ludDogDQoNCnNjb3JlcyA8LSBzY2FsZWRfc2NvcmUocGRfdGVzdCkNCg0KDQojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAgU3RhZ2UgNTogRXZhbHVhdGUgRGlzY3JpbWluYXRvcnkgUG93ZXINCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCmRmX3Njb3JlZF90ZXN0IDwtIHRlc3QgJT4lDQogIHNlbGVjdChkZWZhdWx0X2ZsYWcpICU+JSANCiAgbXV0YXRlKFNDT1JFID0gc2NvcmVzKSAlPiUgDQogIG11dGF0ZShkZWZhdWx0X2ZsYWcgPSBjYXNlX3doZW4oZGVmYXVsdF9mbGFnID09IDEgfiAiRGVmYXVsdCIsIFRSVUUgfiAiTm9uRGVmYXVsdCIpKSANCg0KZGZfc2NvcmVkX3Rlc3QgJT4lIA0KICBncm91cF9ieShkZWZhdWx0X2ZsYWcpICU+JSANCiAgc3VtbWFyaXNlX2VhY2goZnVucyhtaW4sIG1heCwgbWVkaWFuLCBtZWFuLCBuKCkpLCBTQ09SRSkgJT4lIA0KICBtdXRhdGVfaWYoaXMubnVtZXJpYywgZnVuY3Rpb24oeCkge3JvdW5kKHgsIDApfSkgJT4lIA0KICBrbml0cjo6a2FibGUoY2FwdGlvbiA9ICJUYWJsZSAxOiBTY29yZWNhZCBQb2ludHMgYnkgR3JvdXAgZm9yIFRlc3QgRGF0YSAoU3RlcHdpc2UgTG9naXN0aWMpIikNCg0KDQpkZl9zY29yZWRfdGVzdCAlPiUgDQogIGdyb3VwX2J5KGRlZmF1bHRfZmxhZykgJT4lIA0KICBzdW1tYXJpc2UodGIgPSBtZWFuKFNDT1JFKSkgJT4lIA0KICB1bmdyb3VwKCkgLT4gbWVhbl9zY29yZV90ZXN0DQoNCnRoZW1lX3NldCh0aGVtZV9taW5pbWFsKCkpDQoNCmcxIDwtIGRmX3Njb3JlZF90ZXN0ICU+JSANCiAgZ2dwbG90KGFlcyhTQ09SRSwgY29sb3IgPSBkZWZhdWx0X2ZsYWcsIGZpbGwgPSBkZWZhdWx0X2ZsYWcpKSArIA0KICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjMpICsgDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuX3Njb3JlX3Rlc3QkdGJbMV0pLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiKSArIA0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbl9zY29yZV90ZXN0JHRiWzJdKSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiYmx1ZSIpICsgDQogIGdlb21fdGV4dChhZXMoeCA9IDM5MCwgeSA9IDAuMDA4LCBsYWJlbCA9IG1lYW5fc2NvcmVfdGVzdCR0YlsxXSAlPiUgcm91bmQoMCkpLCBjb2xvciA9ICJyZWQiLCBzaXplID0gNCkgKyANCiAgZ2VvbV90ZXh0KGFlcyh4ID0gNTQ1LCB5ID0gMC4wMDgsIGxhYmVsID0gbWVhbl9zY29yZV90ZXN0JHRiWzJdICU+JSByb3VuZCgwKSksIGNvbG9yID0gImJsdWUiLCBzaXplID0gNCkgKyANCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIA0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMiwgMC44KSkgKyANCiAgdGhlbWUocGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSkgKw0KICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkgKyANCiAgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoMS4zLCAxLjMsIDEuMywgMS4zKSwgImNtIikpICsgDQogIGxhYnMoeCA9ICJTY29yZWNhcmQgUG9pbnQiLCB5ID0gTlVMTCwgDQogICAgICAgdGl0bGUgPSAiRmlndXJlIDE6IFNjb3JlY2FyZCBQb2ludCBEaXN0cmlidXRpb24gYnkgR3JvdXAgZm9yIFRlc3QgRGF0YSwgU3RlcHdpc2UgTG9naXN0aWMgTW9kZWwiLCANCiAgICAgICBzdWJ0aXRsZSA9ICJUaGUgc2NvcmVjYXJkIHBvaW50IGlzIGEgbnVtZXJpYyBleHByZXNzaW9uIG1lYXN1cmluZyBjcmVkaXR3b3J0aGluZXNzLiBDb21tZXJjaWFsIEJhbmtzXG51c3VhbGx5IHV0aWxpemUgaXQgYXMgYSBtZXRob2QgdG8gc3VwcG9ydCB0aGUgZGVjaXNpb24tbWFraW5nIGFib3V0IGNyZWRpdCBhcHBsaWNhdGlvbnMuIikNCg0KDQpnMQ0KDQojIEZ1bmN0aW9uIGNhbGN1bGF0ZXMgQVVDOiANCg0KYXVjX2Zvcl90ZXN0IDwtIGZ1bmN0aW9uKHBkKSB7DQogIGFjdHVhbCA8LSB0ZXN0JGRlZmF1bHRfZmxhZw0KICByZXR1cm4ocm9jKGFjdHVhbCwgcGQpKQ0KfQ0KDQojIFVzZSB0aGlzIGZ1bmN0aW9uOiANCm15X2F1Y19sb2dpdCA8LSBhdWNfZm9yX3Rlc3QocGRfdGVzdCkNCg0KIyBGdW5jdGlvbiBmb3IgcHJlc2VudGluZyBBVUMvUk9DIGN1cnZlOiANCg0KbXlfUk9DX2N1cnZlIDwtIGZ1bmN0aW9uKGF1Y19vYmplY3QpIHsNCiAgDQogIHNlbl9zcGVjX2RmIDwtIGRhdGFfZnJhbWUoVFBSID0gYXVjX29iamVjdCRzZW5zaXRpdml0aWVzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBGUFIgPSAxIC0gYXVjX29iamVjdCRzcGVjaWZpY2l0aWVzKQ0KICANCiAgc2VuX3NwZWNfZGYgJT4lIA0KICAgIGdncGxvdChhZXMoeCA9IEZQUiwgeW1pbiA9IDAsIHltYXggPSBUUFIpKSsNCiAgICBnZW9tX3BvbHlnb24oYWVzKHkgPSBUUFIpLCBmaWxsID0gInJlZCIsIGFscGhhID0gMC4zKSsNCiAgICBnZW9tX3BhdGgoYWVzKHkgPSBUUFIpLCBjb2wgPSAiZmlyZWJyaWNrIiwgc2l6ZSA9IDEuMikgKw0KICAgIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMSwgY29sb3IgPSAiZ3JheTM3Iiwgc2l6ZSA9IDEsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArIA0KICAgIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQpICsgDQogICAgdGhlbWVfYncoKSArDQogICAgY29vcmRfZXF1YWwoKSAlPiUgDQogICAgcmV0dXJuKCkNCn0NCg0KDQojIFJPQyBjdXJ2ZSBmb3IgU3RlcHdpc2UgTG9naXN0aWM6IA0KDQpteV9hdWNfbG9naXQgJT4lIA0KICBteV9ST0NfY3VydmUoKSArIA0KICBsYWJzKHggPSAiRlBSICgxIC0gU3BlY2lmaWNpdHkpIiwgDQogICAgICAgeSA9ICJUUFIgKFNlbnNpdGl2aXR5KSIsIA0KICAgICAgIHRpdGxlID0gIkZpZ3VyZSAyOiBNb2RlbCBQZXJmb3JtYW5jZSBCYXNlZCBvbiBUZXN0IERhdGEiLCANCiAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMCgiQVVDIFZhbHVlIGZvciBTdGVwd2lzZSBMb2dpc3RpYzogIiwgbXlfYXVjX2xvZ2l0JGF1YyAlPiUgcm91bmQoMykpKSAtPiBnMiANCg0KZzINCg0KDQojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAgQ29tcGFyZSB3aXRoIFJhbmRvbSBGb3Jlc3QgQXBwcm9hY2gNCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCiMgUHJlY2Vzc2luZyBEYXRhIGZvciBtb2RlbGxpbmc6IA0KDQpkZl90cmFpbl9tbCA8LSB0cmFpbiAlPiUgDQogIG11dGF0ZV9hbGwoZnVuY3Rpb24oeCkge3JlcGxhY2VfbmEoeCwgbWVkaWFuKHgsIG5hLnJtID0gVFJVRSkpfSkgJT4lIA0KICBtdXRhdGUoZGVmYXVsdF9mbGFnID0gY2FzZV93aGVuKGRlZmF1bHRfZmxhZyA9PSAxIH4gIkRlZmF1bHQiLCBUUlVFIH4gIk5vbkRlZmF1bHQiKSAlPiUgYXMuZmFjdG9yKCkpDQoNCmRmX3Rlc3RfbWwgPC0gdGVzdCAlPiUgbXV0YXRlX2FsbChmdW5jdGlvbih4KSB7cmVwbGFjZV9uYSh4LCBtZWRpYW4oeCwgbmEucm0gPSBUUlVFKSl9KQ0KDQojIENvbmRpdGlvbnMgZm9yIHRyYWluaW5nIGFuZCBldmFsdWF0aW5nIFJGIG1vZGVsOiANCm51bWJlciA8LSA1DQpyZXBlYXRzIDwtIDUNCm4gPC0gbnVtYmVyKnJlcGVhdHMNCg0Kc2V0LnNlZWQoMSkNCnRyYWluLmNvbnRyb2wgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJyZXBlYXRlZGN2IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudW1iZXIgPSBudW1iZXIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBlYXRzID0gcmVwZWF0cywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGFzc1Byb2JzID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsbG93UGFyYWxsZWwgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcnlGdW5jdGlvbiA9IG11bHRpQ2xhc3NTdW1tYXJ5KQ0KDQoNCiMgVHJhaW4gUkYgbW9kZWw6IA0Kc2V0LnNlZWQoMSkNCm15X3JmIDwtIHRyYWluKGRlZmF1bHRfZmxhZyB+LiwgDQogICAgICAgICAgICAgICBkYXRhID0gZGZfdHJhaW5fbWwsIA0KICAgICAgICAgICAgICAgbWV0aG9kID0gInJhbmdlciIsIA0KICAgICAgICAgICAgICAgbWV0cmljID0gIkFjY3VyYWN5IiwgDQogICAgICAgICAgICAgICB0ckNvbnRyb2wgPSB0cmFpbi5jb250cm9sKQ0KDQoNCiMgUEQgZnJvbSBSRiBtb2RlbDogDQoNCnBkX3JmIDwtIHByZWRpY3QobXlfcmYsIGRmX3Rlc3RfbWwsIHR5cGUgPSAicHJvYiIpICU+JSBwdWxsKERlZmF1bHQpDQoNCg0KIyBBc3NpZ24gc2NvcmVzOiANCnNjb3Jlc19yZiA8LSBzY2FsZWRfc2NvcmUocGRfcmYpDQpzY29yZXNfcmYgPC0gY2FzZV93aGVuKHNjb3Jlc19yZiA9PSBJbmYgfiBtYXgoc2NvcmVzX3JmWyFpcy5pbmZpbml0ZShzY29yZXNfcmYpXSksIFRSVUUgfiBzY29yZXNfcmYpDQoNCg0KZGZfc2NvcmVkX3JmIDwtIHRlc3QgJT4lDQogIHNlbGVjdChkZWZhdWx0X2ZsYWcpICU+JSANCiAgbXV0YXRlKFNDT1JFID0gc2NvcmVzX3JmKSAlPiUgDQogIG11dGF0ZShkZWZhdWx0X2ZsYWcgPSBjYXNlX3doZW4oZGVmYXVsdF9mbGFnID09IDEgfiAiRGVmYXVsdCIsIFRSVUUgfiAiTm9uRGVmYXVsdCIpKSANCg0KZGZfc2NvcmVkX3JmICU+JSANCiAgZ3JvdXBfYnkoZGVmYXVsdF9mbGFnKSAlPiUgDQogIHN1bW1hcmlzZV9lYWNoKGZ1bnMobWluLCBtYXgsIG1lZGlhbiwgbWVhbiwgbigpKSwgU0NPUkUpICU+JSANCiAgbXV0YXRlX2lmKGlzLm51bWVyaWMsIGZ1bmN0aW9uKHgpIHtyb3VuZCh4LCAwKX0pICU+JSANCiAga25pdHI6OmthYmxlKGNhcHRpb24gPSAiVGFibGUgMjogU2NvcmVjYWQgUG9pbnRzIGJ5IEdyb3VwIGZvciBUZXN0IERhdGEgKFJGIEFwcHJvYWNoKSIpDQoNCg0KZGZfc2NvcmVkX3JmICU+JSANCiAgZ3JvdXBfYnkoZGVmYXVsdF9mbGFnKSAlPiUgDQogIHN1bW1hcmlzZSh0YiA9IG1lYW4oU0NPUkUpKSAlPiUgDQogIHVuZ3JvdXAoKSAtPiBtZWFuX3Njb3JlX3Rlc3QNCg0KDQoNCmRmX3Njb3JlZF9yZiAlPiUgDQogIGdncGxvdChhZXMoU0NPUkUsIGNvbG9yID0gZGVmYXVsdF9mbGFnLCBmaWxsID0gZGVmYXVsdF9mbGFnKSkgKyANCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC4zKSArIA0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbl9zY29yZV90ZXN0JHRiWzFdKSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIikgKyANCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW5fc2NvcmVfdGVzdCR0YlsyXSksIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImJsdWUiKSArIA0KICBnZW9tX3RleHQoYWVzKHggPSA0MTUsIHkgPSAwLjAwOCwgbGFiZWwgPSBtZWFuX3Njb3JlX3Rlc3QkdGJbMV0gJT4lIHJvdW5kKDApKSwgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDQpICsgDQogIGdlb21fdGV4dChhZXMoeCA9IDUzNSwgeSA9IDAuMDA4LCBsYWJlbCA9IG1lYW5fc2NvcmVfdGVzdCR0YlsyXSAlPiUgcm91bmQoMCkpLCBjb2xvciA9ICJibHVlIiwgc2l6ZSA9IDQpICsgDQogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyANCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjcsIDAuOCkpICsgDQogIHRoZW1lKHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCkpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCkpICsgDQogIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDEuMywgMS4zLCAxLjMsIDEuMyksICJjbSIpKSArIA0KICBsYWJzKHggPSAiU2NvcmVjYXJkIFBvaW50IiwgeSA9IE5VTEwsIA0KICAgICAgIHRpdGxlID0gIkZpZ3VyZSAzOiBTY29yZWNhcmQgUG9pbnQgRGlzdHJpYnV0aW9uIGJ5IEdyb3VwIGZvciBUZXN0IERhdGEsIFJhbmRvbSBGb3Jlc3QgTW9kZWwiLCANCiAgICAgICBzdWJ0aXRsZSA9ICJUaGUgc2NvcmVjYXJkIHBvaW50IGlzIGEgbnVtZXJpYyBleHByZXNzaW9uIG1lYXN1cmluZyBjcmVkaXR3b3J0aGluZXNzLiBDb21tZXJjaWFsIEJhbmtzXG51c3VhbGx5IHV0aWxpemUgaXQgYXMgYSBtZXRob2QgdG8gc3VwcG9ydCB0aGUgZGVjaXNpb24tbWFraW5nIGFib3V0IGNyZWRpdCBhcHBsaWNhdGlvbnMuIikNCg0KDQoNCnBkX3JmICU+JSANCiAgYXVjX2Zvcl90ZXN0KCkgLT4gbXlfYXVjX3JmDQoNCg0KbXlfYXVjX3JmICU+JSANCiAgbXlfUk9DX2N1cnZlKCkgKyANCiAgbGFicyh4ID0gIkZQUiAoMSAtIFNwZWNpZmljaXR5KSIsIA0KICAgICAgIHkgPSAiVFBSIChTZW5zaXRpdml0eSkiLCANCiAgICAgICB0aXRsZSA9ICJGaWd1cmUgNDogUk9DIEN1cnZlIEJhc2VkIG9uIFRlc3QgRGF0YSIsIA0KICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKCJBVUMgVmFsdWUgZm9yIFJhbmRvbSBGb3Jlc3Q6ICIsIG15X2F1Y19yZiRhdWMgJT4lIHJvdW5kKDMpKSkNCg0KDQpgYGANCg0K