This is my project for prediction Credit Risk Scoring

Dataset from kaggle Credit Risk Dataset

Library

library(knitr)
library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(data.table)
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
data.table 1.16.2 using 4 threads (see ?getDTthreads).  Latest news: r-datatable.com

Attaching package: ‘data.table’

The following objects are masked from ‘package:dplyr’:

    between, first, last
library(corrplot)
Warning: package ‘corrplot’ was built under R version 4.4.3corrplot 0.95 loaded
library(ggplot2)
Learn more about the underlying theory at https://ggplot2-book.org/
library(reshape2)
Warning: package ‘reshape2’ was built under R version 4.4.3
Attaching package: ‘reshape2’

The following objects are masked from ‘package:data.table’:

    dcast, melt
library(e1071)

Preparation Data

Loading Dataset

Detailed data description of Credit Risk dataset:

  • person_age <- Age
  • person_income <- Annual Income
  • person_home_ownership <- Home ownership
  • person_emp_length <- Employment length (in years)
  • loan_intent <- Loan intent
  • loan_grade <- Loan grade
  • loan_amnt <- Loan amount
  • loan_int_rate <- Interest rate
  • loan_status <- Loan status (0 is non default 1 is default)
  • loan_percent_income <- Percent income
  • cb_person_default_on_file <- Historical default
  • cb_preson_cred_hist_length <- Credit history length
df <- read.csv("Dataset/credit_risk_dataset.csv")
df %>% head(10) %>% data.table()

Dataset Structure

str(df)
'data.frame':   32581 obs. of  12 variables:
 $ person_age                : int  22 21 25 23 24 21 26 24 24 21 ...
 $ person_income             : int  59000 9600 9600 65500 54400 9900 77100 78956 83000 10000 ...
 $ person_home_ownership     : chr  "RENT" "OWN" "MORTGAGE" "RENT" ...
 $ person_emp_length         : num  123 5 1 4 8 2 8 5 8 6 ...
 $ loan_intent               : chr  "PERSONAL" "EDUCATION" "MEDICAL" "MEDICAL" ...
 $ loan_grade                : chr  "D" "B" "C" "C" ...
 $ loan_amnt                 : int  35000 1000 5500 35000 35000 2500 35000 35000 35000 1600 ...
 $ loan_int_rate             : num  16 11.1 12.9 15.2 14.3 ...
 $ loan_status               : int  1 0 1 1 1 1 1 1 1 1 ...
 $ loan_percent_income       : num  0.59 0.1 0.57 0.53 0.55 0.25 0.45 0.44 0.42 0.16 ...
 $ cb_person_default_on_file : chr  "Y" "N" "N" "N" ...
 $ cb_person_cred_hist_length: int  3 2 3 2 4 2 3 4 2 3 ...

Character Columns

# Home ownership
df %>% 
  count(person_home_ownership, sort = TRUE)
set_levels_person_home_ownership <- unique(df$person_home_ownership)

# Home ownership
df %>% 
  count(loan_intent, sort = TRUE)
set_levels_loan_intent <- unique(df$loan_intent)

# Loan Grade
df %>% 
  count(loan_grade, sort = TRUE)
set_levels_loan_grade <- sort(unique(df$loan_grade))

# Historical default
df %>% 
  count(cb_person_default_on_file , sort = TRUE)
set_levels_cb_person_default_on_file <- c("N", "Y")

Set Factor Dataset character Type

df <- df %>% 
  mutate(
    person_emp_length = ifelse(is.na(person_emp_length), 0, person_emp_length),
    loan_int_rate = ifelse(is.na(loan_int_rate), 0, person_emp_length),
    person_home_ownership = factor(person_home_ownership, levels = set_levels_person_home_ownership),
    person_home_ownership_int = as.integer(person_home_ownership),
    loan_intent = factor(loan_intent, levels = set_levels_loan_intent),
    loan_intent_int = as.integer(loan_intent),
    loan_grade = factor(loan_grade, levels = set_levels_loan_grade),
    loan_grade_int = as.integer(loan_grade),
    cb_person_default_on_file = factor(cb_person_default_on_file, levels = set_levels_cb_person_default_on_file),
    cb_person_default_on_file_int = as.integer(cb_person_default_on_file)-1
  )

df %>% head()

Correlation Heatmap

# Compute the correlation matrix
cor_matrix <- cor(df %>% select_if(is.numeric))

# Convert the matrix to a format suitable for ggplot
cor_df <- melt(cor_matrix)

# Plot the heatmap with correlation values and rotated axis labels
ggplot(cor_df, aes(Var1, Var2, fill = value)) +
  geom_tile() +
  geom_text(aes(label = round(value, 2)), color = "black", size = 4) +  # Show correlation numbers
  scale_fill_gradient2(low = "blue", mid = "white", high = "red", midpoint = 0) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1)) +  # Rotate x-axis labels
  labs(title = "Heatmap of Correlation Matrix", fill = "Correlation")

NA
NA

Exclude Variable With Stong Correlation with Other

2 variable are indicate have strong correlation - cb_person_cred_hist_length (strong with person_age) - loan_int_rate (strong with person_employe_length)

#List Exclude Variable
list_exclude <- c("cb_person_cred_hist_length", "loan_int_rate")

# Compute the correlation matrix
cor_matrix <- cor(df %>% select_if(is.numeric) %>% select(-list_exclude))
Warning: Using an external vector in selections was deprecated in tidyselect 1.1.0.
Please use `all_of()` or `any_of()` instead.
# Was:
data %>% select(list_exclude)

# Now:
data %>% select(all_of(list_exclude))

See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
# Convert the matrix to a format suitable for ggplot
cor_df <- melt(cor_matrix)

# Plot the heatmap with correlation values and rotated axis labels
ggplot(cor_df, aes(Var1, Var2, fill = value)) +
  geom_tile() +
  geom_text(aes(label = round(value, 2)), color = "black", size = 4) +  # Show correlation numbers
  scale_fill_gradient2(low = "blue", mid = "white", high = "red", midpoint = 0) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1)) +  # Rotate x-axis labels
  labs(title = "Heatmap of Correlation Matrix", fill = "Correlation")

Variable Final

df_clean <- df %>% 
  select_if(is.numeric) %>% 
  select(-list_exclude)

str(df_clean)
'data.frame':   32581 obs. of  10 variables:
 $ person_age                   : int  22 21 25 23 24 21 26 24 24 21 ...
 $ person_income                : int  59000 9600 9600 65500 54400 9900 77100 78956 83000 10000 ...
 $ person_emp_length            : num  123 5 1 4 8 2 8 5 8 6 ...
 $ loan_amnt                    : int  35000 1000 5500 35000 35000 2500 35000 35000 35000 1600 ...
 $ loan_status                  : int  1 0 1 1 1 1 1 1 1 1 ...
 $ loan_percent_income          : num  0.59 0.1 0.57 0.53 0.55 0.25 0.45 0.44 0.42 0.16 ...
 $ person_home_ownership_int    : int  1 2 3 1 1 2 1 1 1 2 ...
 $ loan_intent_int              : int  1 2 3 3 3 4 2 3 1 4 ...
 $ loan_grade_int               : int  4 2 3 3 3 1 2 2 1 4 ...
 $ cb_person_default_on_file_int: num  1 0 0 0 1 0 0 0 0 0 ...

Split Data Train and Test

library(caTools)
Warning: package ‘caTools’ was built under R version 4.4.3
set.seed(123)

split <- sample.split(df_clean$loan_status, SplitRatio = 0.7)
df_train <- subset(df_clean, split == TRUE)
df_test <- subset(df_clean, split == FALSE)

df_train %>% 
  head()

df_test %>% 
  head()

> Model Naive Bayes

# Train the Naive Bayes model
model <- naiveBayes(loan_status ~ ., data = df_train)

# Make predictions
predictions <- predict(model, df_test)

# View results
table(df_test$loan_status, predictions)
   predictions
       0    1
  0 6610 1032
  1  830 1302
# Accuracy
accuracy <- sum(predictions == df_test$loan_status) / nrow(df_test)
print(paste("Accuracy:", round(accuracy * 100, 2), "%"))
[1] "Accuracy: 80.95 %"

> Comparation Model

# Evaluate accuracy
results <- sapply(models, function(model) {
  predictions <- predict(model, df_train)
  mean(predictions == df_train$loan_status) * 100 # Accuracy percentage
})
# Evaluate accuracy
results <- sapply(models, function(model) {
  predictions <- predict(model, df_train)
  mean(predictions == df_train$loan_status) * 100 # Accuracy percentage
})
print(results)
        naive_bayes logistic_regression       decision_tree       random_forest 
           81.71176             0.00000            11.38685             0.00000 
                svm 
            0.00000 

Best of model : Naive Bayes

LS0tDQp0aXRsZTogIkNyZWRpdCBSaXNrIFNjb3JpbmciDQphdXRob3I6ICJBZGkgQXJ0YSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNClRoaXMgaXMgbXkgcHJvamVjdCBmb3IgcHJlZGljdGlvbiBDcmVkaXQgUmlzayBTY29yaW5nIA0KDQpEYXRhc2V0IGZyb20gW2thZ2dsZSBDcmVkaXQgUmlzayBEYXRhc2V0XShodHRwczovL3d3dy5rYWdnbGUuY29tL2RhdGFzZXRzL2xhb3RzZS9jcmVkaXQtcmlzay1kYXRhc2V0L2RhdGEpDQoNCg0KIyMgTGlicmFyeQ0KYGBge3IgaW5jbHVkZSA9IFRSVUUsIG1hc3NhZ2UgPSBGQUxTRX0NCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShkYXRhLnRhYmxlKQ0KbGlicmFyeShjb3JycGxvdCkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocmVzaGFwZTIpDQpsaWJyYXJ5KGUxMDcxKQ0KYGBgDQojIFByZXBhcmF0aW9uIERhdGENCiMjIExvYWRpbmcgRGF0YXNldA0KDQoqKkRldGFpbGVkIGRhdGEgZGVzY3JpcHRpb24gb2YgQ3JlZGl0IFJpc2sgZGF0YXNldDoqKiANCg0KLSAqKnBlcnNvbl9hZ2UqKiA8LQlBZ2UNCi0gKipwZXJzb25faW5jb21lKioJPC0gQW5udWFsIEluY29tZQ0KLSAqKnBlcnNvbl9ob21lX293bmVyc2hpcCoqIDwtCUhvbWUgb3duZXJzaGlwDQotICoqcGVyc29uX2VtcF9sZW5ndGgqKiA8LQlFbXBsb3ltZW50IGxlbmd0aCAoaW4geWVhcnMpDQotICoqbG9hbl9pbnRlbnQqKiA8LQlMb2FuIGludGVudA0KLSAqKmxvYW5fZ3JhZGUqKiA8LQlMb2FuIGdyYWRlDQotICoqbG9hbl9hbW50KiogPC0JTG9hbiBhbW91bnQNCi0gKipsb2FuX2ludF9yYXRlKiogPC0JSW50ZXJlc3QgcmF0ZQ0KLSAqKmxvYW5fc3RhdHVzKiogPC0JTG9hbiBzdGF0dXMgKDAgaXMgbm9uIGRlZmF1bHQgMSBpcyBkZWZhdWx0KQ0KLSAqKmxvYW5fcGVyY2VudF9pbmNvbWUqKiA8LQlQZXJjZW50IGluY29tZQ0KLSAqKmNiX3BlcnNvbl9kZWZhdWx0X29uX2ZpbGUqKiA8LQlIaXN0b3JpY2FsIGRlZmF1bHQNCi0gKipjYl9wcmVzb25fY3JlZF9oaXN0X2xlbmd0aCoqIDwtCUNyZWRpdCBoaXN0b3J5IGxlbmd0aA0KDQpgYGB7ciBpbmNsdWRlID0gVFJVRSwgZWNobyA9IFRSVUV9DQpkZiA8LSByZWFkLmNzdigiRGF0YXNldC9jcmVkaXRfcmlza19kYXRhc2V0LmNzdiIpDQpkZiAlPiUgaGVhZCgxMCkgJT4lIGRhdGEudGFibGUoKQ0KYGBgDQojIyBEYXRhc2V0IFN0cnVjdHVyZQ0KYGBge3IgaW5jbHVkZT1UUlVFfQ0Kc3RyKGRmKQ0KYGBgDQojIyBDaGFyYWN0ZXIgQ29sdW1ucw0KYGBge3IsIGluY2x1ZGUgPSBUUlVFfQ0KIyBIb21lIG93bmVyc2hpcA0KZGYgJT4lIA0KICBjb3VudChwZXJzb25faG9tZV9vd25lcnNoaXAsIHNvcnQgPSBUUlVFKQ0Kc2V0X2xldmVsc19wZXJzb25faG9tZV9vd25lcnNoaXAgPC0gdW5pcXVlKGRmJHBlcnNvbl9ob21lX293bmVyc2hpcCkNCg0KIyBIb21lIG93bmVyc2hpcA0KZGYgJT4lIA0KICBjb3VudChsb2FuX2ludGVudCwgc29ydCA9IFRSVUUpDQpzZXRfbGV2ZWxzX2xvYW5faW50ZW50IDwtIHVuaXF1ZShkZiRsb2FuX2ludGVudCkNCg0KIyBMb2FuIEdyYWRlDQpkZiAlPiUgDQogIGNvdW50KGxvYW5fZ3JhZGUsIHNvcnQgPSBUUlVFKQ0Kc2V0X2xldmVsc19sb2FuX2dyYWRlIDwtIHNvcnQodW5pcXVlKGRmJGxvYW5fZ3JhZGUpKQ0KDQojIEhpc3RvcmljYWwgZGVmYXVsdA0KZGYgJT4lIA0KICBjb3VudChjYl9wZXJzb25fZGVmYXVsdF9vbl9maWxlICwgc29ydCA9IFRSVUUpDQpzZXRfbGV2ZWxzX2NiX3BlcnNvbl9kZWZhdWx0X29uX2ZpbGUgPC0gYygiTiIsICJZIikNCmBgYA0KDQojIyBTZXQgRmFjdG9yIERhdGFzZXQgY2hhcmFjdGVyIFR5cGUNCmBgYHtyfQ0KZGYgPC0gZGYgJT4lIA0KICBtdXRhdGUoDQogICAgcGVyc29uX2VtcF9sZW5ndGggPSBpZmVsc2UoaXMubmEocGVyc29uX2VtcF9sZW5ndGgpLCAwLCBwZXJzb25fZW1wX2xlbmd0aCksDQogICAgbG9hbl9pbnRfcmF0ZSA9IGlmZWxzZShpcy5uYShsb2FuX2ludF9yYXRlKSwgMCwgcGVyc29uX2VtcF9sZW5ndGgpLA0KICAgIHBlcnNvbl9ob21lX293bmVyc2hpcCA9IGZhY3RvcihwZXJzb25faG9tZV9vd25lcnNoaXAsIGxldmVscyA9IHNldF9sZXZlbHNfcGVyc29uX2hvbWVfb3duZXJzaGlwKSwNCiAgICBwZXJzb25faG9tZV9vd25lcnNoaXBfaW50ID0gYXMuaW50ZWdlcihwZXJzb25faG9tZV9vd25lcnNoaXApLA0KICAgIGxvYW5faW50ZW50ID0gZmFjdG9yKGxvYW5faW50ZW50LCBsZXZlbHMgPSBzZXRfbGV2ZWxzX2xvYW5faW50ZW50KSwNCiAgICBsb2FuX2ludGVudF9pbnQgPSBhcy5pbnRlZ2VyKGxvYW5faW50ZW50KSwNCiAgICBsb2FuX2dyYWRlID0gZmFjdG9yKGxvYW5fZ3JhZGUsIGxldmVscyA9IHNldF9sZXZlbHNfbG9hbl9ncmFkZSksDQogICAgbG9hbl9ncmFkZV9pbnQgPSBhcy5pbnRlZ2VyKGxvYW5fZ3JhZGUpLA0KICAgIGNiX3BlcnNvbl9kZWZhdWx0X29uX2ZpbGUgPSBmYWN0b3IoY2JfcGVyc29uX2RlZmF1bHRfb25fZmlsZSwgbGV2ZWxzID0gc2V0X2xldmVsc19jYl9wZXJzb25fZGVmYXVsdF9vbl9maWxlKSwNCiAgICBjYl9wZXJzb25fZGVmYXVsdF9vbl9maWxlX2ludCA9IGFzLmludGVnZXIoY2JfcGVyc29uX2RlZmF1bHRfb25fZmlsZSktMQ0KICApDQoNCmRmICU+JSBoZWFkKCkNCmBgYA0KDQojIyBDb3JyZWxhdGlvbiBIZWF0bWFwDQpgYGB7ciBmaWcuY2FwPSAiQ29ycmVsYXRpb24gSGVhdCBNYXAgQWxsIFZhcmlhYmxlIn0NCiMgQ29tcHV0ZSB0aGUgY29ycmVsYXRpb24gbWF0cml4DQpjb3JfbWF0cml4IDwtIGNvcihkZiAlPiUgc2VsZWN0X2lmKGlzLm51bWVyaWMpKQ0KDQojIENvbnZlcnQgdGhlIG1hdHJpeCB0byBhIGZvcm1hdCBzdWl0YWJsZSBmb3IgZ2dwbG90DQpjb3JfZGYgPC0gbWVsdChjb3JfbWF0cml4KQ0KDQojIFBsb3QgdGhlIGhlYXRtYXAgd2l0aCBjb3JyZWxhdGlvbiB2YWx1ZXMgYW5kIHJvdGF0ZWQgYXhpcyBsYWJlbHMNCmdncGxvdChjb3JfZGYsIGFlcyhWYXIxLCBWYXIyLCBmaWxsID0gdmFsdWUpKSArDQogIGdlb21fdGlsZSgpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHJvdW5kKHZhbHVlLCAyKSksIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDQpICsgICMgU2hvdyBjb3JyZWxhdGlvbiBudW1iZXJzDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGxvdyA9ICJibHVlIiwgbWlkID0gIndoaXRlIiwgaGlnaCA9ICJyZWQiLCBtaWRwb2ludCA9IDApICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gMSkpICsgICMgUm90YXRlIHgtYXhpcyBsYWJlbHMNCiAgbGFicyh0aXRsZSA9ICJIZWF0bWFwIG9mIENvcnJlbGF0aW9uIE1hdHJpeCIsIGZpbGwgPSAiQ29ycmVsYXRpb24iKQ0KDQoNCmBgYA0KIyMgRXhjbHVkZSBWYXJpYWJsZSBXaXRoIFN0b25nIENvcnJlbGF0aW9uIHdpdGggT3RoZXINCg0KMiB2YXJpYWJsZSBhcmUgaW5kaWNhdGUgaGF2ZSBzdHJvbmcgY29ycmVsYXRpb24NCi0gY2JfcGVyc29uX2NyZWRfaGlzdF9sZW5ndGggKHN0cm9uZyB3aXRoIHBlcnNvbl9hZ2UpDQotIGxvYW5faW50X3JhdGUgKHN0cm9uZyB3aXRoIHBlcnNvbl9lbXBsb3llX2xlbmd0aCkNCmBgYHtyIGZpZy5jYXA9ICJDb3JyZWxhdGlvbiBIZWF0IE1hcCBBbGwgVmFyaWFibGUgKEV4Y2x1ZGVTdHJvbmcgQ29ycikifQ0KI0xpc3QgRXhjbHVkZSBWYXJpYWJsZQ0KbGlzdF9leGNsdWRlIDwtIGMoImNiX3BlcnNvbl9jcmVkX2hpc3RfbGVuZ3RoIiwgImxvYW5faW50X3JhdGUiKQ0KDQojIENvbXB1dGUgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeA0KY29yX21hdHJpeCA8LSBjb3IoZGYgJT4lIHNlbGVjdF9pZihpcy5udW1lcmljKSAlPiUgc2VsZWN0KC1saXN0X2V4Y2x1ZGUpKQ0KDQojIENvbnZlcnQgdGhlIG1hdHJpeCB0byBhIGZvcm1hdCBzdWl0YWJsZSBmb3IgZ2dwbG90DQpjb3JfZGYgPC0gbWVsdChjb3JfbWF0cml4KQ0KDQojIFBsb3QgdGhlIGhlYXRtYXAgd2l0aCBjb3JyZWxhdGlvbiB2YWx1ZXMgYW5kIHJvdGF0ZWQgYXhpcyBsYWJlbHMNCmdncGxvdChjb3JfZGYsIGFlcyhWYXIxLCBWYXIyLCBmaWxsID0gdmFsdWUpKSArDQogIGdlb21fdGlsZSgpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHJvdW5kKHZhbHVlLCAyKSksIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDQpICsgICMgU2hvdyBjb3JyZWxhdGlvbiBudW1iZXJzDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGxvdyA9ICJibHVlIiwgbWlkID0gIndoaXRlIiwgaGlnaCA9ICJyZWQiLCBtaWRwb2ludCA9IDApICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gMSkpICsgICMgUm90YXRlIHgtYXhpcyBsYWJlbHMNCiAgbGFicyh0aXRsZSA9ICJIZWF0bWFwIG9mIENvcnJlbGF0aW9uIE1hdHJpeCIsIGZpbGwgPSAiQ29ycmVsYXRpb24iKQ0KDQpgYGANCg0KIyBWYXJpYWJsZSBGaW5hbA0KYGBge3J9DQpkZl9jbGVhbiA8LSBkZiAlPiUgDQogIHNlbGVjdF9pZihpcy5udW1lcmljKSAlPiUgDQogIHNlbGVjdCgtbGlzdF9leGNsdWRlKQ0KDQpzdHIoZGZfY2xlYW4pDQpgYGANCiMgU3BsaXQgRGF0YSBUcmFpbiBhbmQgVGVzdA0KYGBge3J9DQpsaWJyYXJ5KGNhVG9vbHMpDQpzZXQuc2VlZCgxMjMpDQoNCnNwbGl0IDwtIHNhbXBsZS5zcGxpdChkZl9jbGVhbiRsb2FuX3N0YXR1cywgU3BsaXRSYXRpbyA9IDAuNykNCmRmX3RyYWluIDwtIHN1YnNldChkZl9jbGVhbiwgc3BsaXQgPT0gVFJVRSkNCmRmX3Rlc3QgPC0gc3Vic2V0KGRmX2NsZWFuLCBzcGxpdCA9PSBGQUxTRSkNCg0KZGZfdHJhaW4gJT4lIA0KICBoZWFkKCkNCg0KZGZfdGVzdCAlPiUgDQogIGhlYWQoKQ0KYGBgDQojID4gTW9kZWwgTmFpdmUgQmF5ZXMgDQpgYGB7cn0NCiMgVHJhaW4gdGhlIE5haXZlIEJheWVzIG1vZGVsDQptb2RlbCA8LSBuYWl2ZUJheWVzKGxvYW5fc3RhdHVzIH4gLiwgZGF0YSA9IGRmX3RyYWluKQ0KDQojIE1ha2UgcHJlZGljdGlvbnMNCnByZWRpY3Rpb25zIDwtIHByZWRpY3QobW9kZWwsIGRmX3Rlc3QpDQoNCiMgVmlldyByZXN1bHRzDQp0YWJsZShkZl90ZXN0JGxvYW5fc3RhdHVzLCBwcmVkaWN0aW9ucykNCg0KIyBBY2N1cmFjeQ0KYWNjdXJhY3kgPC0gc3VtKHByZWRpY3Rpb25zID09IGRmX3Rlc3QkbG9hbl9zdGF0dXMpIC8gbnJvdyhkZl90ZXN0KQ0KcHJpbnQocGFzdGUoIkFjY3VyYWN5OiIsIHJvdW5kKGFjY3VyYWN5ICogMTAwLCAyKSwgIiUiKSkNCg0KYGBgDQojID4gQ29tcGFyYXRpb24gTW9kZWwNCmBgYHtyfQ0KIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCmxpYnJhcnkoZTEwNzEpICAgICAgICMgTmFpdmUgQmF5ZXMNCmxpYnJhcnkocnBhcnQpICAgICAgICMgRGVjaXNpb24gVHJlZQ0KbGlicmFyeShyYW5kb21Gb3Jlc3QpICMgUmFuZG9tIEZvcmVzdA0KbGlicmFyeShrZXJubGFiKSAgICAgIyBTVk0NCg0KDQojIFRyYWluIG1vZGVscyBtYW51YWxseQ0KbW9kZWxzIDwtIGxpc3QoDQogIG5haXZlX2JheWVzID0gbmFpdmVCYXllcyhsb2FuX3N0YXR1cyB+IC4sIGRhdGEgPSBkZl90cmFpbiksDQogIGxvZ2lzdGljX3JlZ3Jlc3Npb24gPSBnbG0obG9hbl9zdGF0dXMgfiAuLCBkYXRhID0gZGZfdHJhaW4sIGZhbWlseSA9ICJiaW5vbWlhbCIpLA0KICBkZWNpc2lvbl90cmVlID0gcnBhcnQobG9hbl9zdGF0dXMgfiAuLCBkYXRhID0gZGZfdHJhaW4pLA0KICByYW5kb21fZm9yZXN0ID0gcmFuZG9tRm9yZXN0KGxvYW5fc3RhdHVzIH4gLiwgZGF0YSA9IGRmX3RyYWluKSwNCiAgc3ZtID0ga3N2bShsb2FuX3N0YXR1cyB+IC4sIGRhdGEgPSBkZl90cmFpbikNCikNCg0KIyBFdmFsdWF0ZSBhY2N1cmFjeQ0KcmVzdWx0cyA8LSBzYXBwbHkobW9kZWxzLCBmdW5jdGlvbihtb2RlbCkgew0KICBwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KG1vZGVsLCBkZl90ZXN0KQ0KICBtZWFuKHByZWRpY3Rpb25zID09IGRmX3Rlc3QkbG9hbl9zdGF0dXMpICogMTAwICMgQWNjdXJhY3kgcGVyY2VudGFnZQ0KfSkNCg0KcHJpbnQocmVzdWx0cykNCg0KYGBgDQoNCg0KQmVzdCBvZiBtb2RlbCA6IE5haXZlIEJheWVz