• Grupa D: Alicja Kuban, MichaƂ Gross
  • 2024-01-23


ZATWIERDZENIE KREDYTU MIESZKANIOWEGO


Projekt zespoƂowy z przedmiotu Analiza Danych



Wprowadzenie

Kim jesteƛmy?

Jesteƛmy firma, zajmującą się udzielaniem kredytów mieszkaniowych.DziaƂamy na obszarach miejskich, póƂmiejskich oraz wiejskich. Nasz klient najpierw ubiega się o kredyt mieszkaniowy, a następnie weryfikujemy, czy kwalifikuje się do jego otrzymania.


Nasze zadanie

Chcemy zautomatyzować proces przyznawania kredytów (w czasie rzeczywistym) w oparciu o dane klienta. W pierwszym etapie starania się o kredyt, klient wypeƂnia wniosek online, jeƛli speƂni wstępne wymagania, umawiamy się z nim na spotkanie, w celu weryfikacji danych i rozmowy o kredycie. Zadaniem naszego dziaƂu (Automatyzacji Procesów i UsprawnieƄ) jest zbudowanie modelu, który w oparciu o dane ankietowe, zbada czy nasz klient ma zdolnoƛci kredytową.


Dane jakimi dysponujemy

Posiadamy następujące dane na temat naszych klientów:

  • pƂeć,

  • stan cywilny,

  • wyksztaƂcenie,

  • liczbę osĂłb na utrzymaniu,

  • dochĂłd,

  • kwotę poĆŒyczki,

  • historię kredytową

  • czy kredyt zostaƂ udzielony (zmienna objaƛniana)


Cel

Zbadanie jakie warunki muszą być speƂnione, aby kredyt zostaƂ udzielony


Zakres analizy

  1. Okreƛlenie problemu

  2. Postawienie hipotez

  3. Przygotowanie systemu i zaƂadowanie danych

  4. Zrozumienie danych

  5. EDA

    • Przeprowadzanie analizy jednowymiarowej,

    • Przeprowadzenie analizy dwuwymiarowej


1. Okreƛlenie problemu

W oparciu o zebrane dane, chcemy usprawnić proces udzielania kredytĂłw, ktĂłry obecnie jest nieefektywny i pochƂania większoƛć czasu pracy naszych konsultantĂłw, ze względu na indywidualne rozpatrywanie sprawy kaĆŒdego potencjalnego klienta z osobna. Dzięki wprowadzeniu Modeleu Trustworthy AI w skoringu kredytowym, usprawnimy proces weryfikacji, W ciągu 3 minut potencalny klient, będzie wiedziaƂ czy ma zdolnoƛć kredytową, aby zaciągnąć kredyt w naszym banku.

Dzięki tym dziaƂaniom uporamy się z problemami firmy.

  • SkrĂłcimy czas potrzebny na wstępne stwierdzenie czy dany klient speƂnia wszelkie niezbędne wymagania, aby dostać kredyt.

  • Nasze decyzje będą poparte o wczeƛniejsze decyzje, tym samym będzie istniaƂo mniejsze ryzyko bƂędnej oceny kielnta.

  • Staniemy się bardziej efektywni, rzetelni i konkurencyjni.

  • Usprawnimy system przepƂywu informacji i dziaƂanie firmy.

  • Osiągniemy lepsze wyniki oraz będziemy mogli zredukować liczbę etatĂłw.


2. Postawienie hipotezy

GƂównym czynnikiem decydującym o tym, czy kredyt zostanie przyznany jest wysokoƛć dochodów aplikanta.


3. Przygotowanie systemu i zaƂadowanie danych

Przygotowanie systemu do pracy z danymi

ZaƂadownanie bibliotek, z których będziemy korzystać w dalej częƛci projektu. Dostosowanie globalnych ustawieƄ.

knitr::opts_chunk$set(echo = TRUE)
options(scipen = 999, digits=6) 
if(!require('DMwR')) install.packages("https://cran.r-project.org/src/contrib/Archive/DMwR/DMwR_0.4.1.tar.gz", repos=NULL, type="source", dependencies=TRUE)
library(tidyverse)
library(ggplot2)
library(rmdformats)
library(validate)
library(validatetools)
library(dcmodify)
library(errorlocate)
library(deductive)
library(VIM)
library(simputation)
library(lumberjack)
library(ISLR) 
library(dlookr)
library(xts)
library(quantmod)
library(ROCR)
library(DMwR)
library(Information)
library(scorecard)
library(naniar)
library(visdat)
library(shapeR)
library(arsenal)
library(e1071)
library(haven)
library(papeR)
library(dplyr)
library(tidyverse)
library(ggplot2)
library(kableExtra)
library(classInt)
library(pastecs)
library(desctable)
library(frequency)
library(corrplot)
library(ggpubr)
library(RColorBrewer)
library(wesanderson)
library(cowplot)
library(hrbrthemes)
library(scales)
library(formattable)
library('MASS')
library(rcompanion)
library(lsr)
library(vcd)
library(DescTools)
library(waffle)
library(cleanrmd)
library(thematic)
library(shiny)
library(showtext)
library(ragg)
library(distill)
library(rmdformats)
library(prettydoc)
library(hrbrthemes)
library(tint)
library(gapminder)
library(yaml)
library(rpart)
library(tidyverse)
library(pander)
library(modelr)
library(broom) 
library(caret)
library(ggplot2)
library(ROCR)
library(psych)
library(qwraps2)
library(ggpubr)
library(rstatix)
library(kableExtra)
library(datarium)
library("correlation")
library(polycor)
library(corrgram)
library(knitr)
library(flextable)







library(ggstatsplot)
library(dplyr)

ZaƂadowanie danych, na podstawie których zostanie przeprowadzona analiza. Są to następujące pliki: Hipoteczny1.csv, Hipoteczny2.csv, które zawierają dane klientów, ubiegających się o kredyt.

dane1 <- read.csv("Hipoteczny1.csv")
dane2 <- read.csv("Hipoteczny2.csv")


Organizacja pracy z danymi (wstępne czyszczenie i przegląd danych)

W celu usprawnienia pracy, dane będą przechowywane w formie tabeli “tabble”.

dane1 <- as.tibble(dane1)
dane2 <- as.tibble(dane2)

Dane1 z pliku Hipoteczny1.csv, w przeciwieƄstwie do danych2 z pliku Hipoteczny2.csv nie zawierają informacji, czy kredyt zostaƂ przyznany (Loan_Status). W związku z tym dane2 wykorzystamy do przeprowadzenia analizy, sprawdzimy co decyduje o zdolnoƛci kredytowej klientĂłw. Zaƛ dane1 potraktujemy jako wnioski naszych klientĂłw czekających na odpowiedĆș i w oparciu o przeprowadzoną analizę stwierdziemy, ktĂłrzy z nich dostaną pozytywną odpowiedĆș na wniosek kredytowy.

dane

dane1

formattable(head(dane1,7), align ="c")
Loan_ID Gender Married Dependents Education Self_Employed ApplicantIncome CoapplicantIncome LoanAmount Loan_Amount_Term Credit_History Property_Area
LP001015 Male Yes 0 Graduate No 5720 0 110 360 1 Urban
LP001022 Male Yes 1 Graduate No 3076 1500 126 360 1 Urban
LP001031 Male Yes 2 Graduate No 5000 1800 208 360 1 Urban
LP001035 Male Yes 2 Graduate No 2340 2546 100 360 NA Urban
LP001051 Male No 0 Not Graduate No 3276 0 78 360 1 Urban
LP001054 Male Yes 0 Not Graduate Yes 2165 3422 152 360 1 Urban
LP001055 Female No 1 Not Graduate No 2226 0 59 360 1 Semiurban

dane2


formattable(head(dane2,7), align ="c")
Loan_ID Gender Married Dependents Education Self_Employed ApplicantIncome CoapplicantIncome LoanAmount Loan_Amount_Term Credit_History Property_Area Loan_Status
LP001002 Male No 0 Graduate No 5849 0 NA 360 1 Urban Y
LP001003 Male Yes 1 Graduate No 4583 1508 128 360 1 Rural N
LP001005 Male Yes 0 Graduate Yes 3000 0 66 360 1 Urban Y
LP001006 Male Yes 0 Not Graduate No 2583 2358 120 360 1 Urban Y
LP001008 Male No 0 Graduate No 6000 0 141 360 1 Urban Y
LP001011 Male Yes 2 Graduate Yes 5417 4196 267 360 1 Urban Y
LP001013 Male Yes 0 Not Graduate No 2333 1516 95 360 1 Urban Y

Na samym początku porządkujemy dane rosnąco wedƂug “Loan_ID”

#dane2
dane2$Loan_ID <- str_replace(dane2$Loan_ID,"LP","LP ")
  
dane2 <- separate(dane2, col = "Loan_ID", c("Loan","ID"),sep = " ")

dane2$ID <- as.numeric(dane2$ID)
dane2 <- dane2 %>% 
  arrange(desc("Loan_ID"))

dane2 <- unite(dane2,"Loan_ID", "Loan", "ID", sep=" ")
dane2$Loan_ID <- str_replace(dane2$Loan_ID,"LP ","LP")



#dane1
dane1$Loan_ID <- str_replace(dane1$Loan_ID,"LP","LP ")
  
dane1 <- separate(dane1, col = "Loan_ID", c("Loan","ID"),sep = " ")

dane1$ID <- as.numeric(dane1$ID)
dane1 <- dane1 %>% 
  arrange(desc("Loan_ID"))


dane1 <- unite(dane1,"Loan_ID", "Loan", "ID", sep=" ")
dane1$Loan_ID <- str_replace(dane1$Loan_ID,"LP ","LP")

Następnie zamieniamy puste miejsca na NA

#dane2
dane2[dane2 == ''] <- NA

#dane1
dane1[dane1 == ''] <- NA

Kolejno zmieniamy klasę danych, aby Ƃatwiej się z nimi pracowaƂo.

#dane1
dane1$ApplicantIncome <- as.numeric(dane1$ApplicantIncome)
dane1$CoapplicantIncome <- as.numeric(dane1$CoapplicantIncome)
dane1$Dependents <- as.factor(dane1$Dependents)
dane1$Property_Area <- as.factor(dane1$Property_Area)
dane1$Gender <- as.factor(dane1$Gender)

#dane2
dane2$ApplicantIncome <- as.numeric(dane2$ApplicantIncome)
dane2$CoapplicantIncome <- as.numeric(dane2$CoapplicantIncome)
dane2$Dependents <- as.factor(dane2$Dependents)
dane2$Property_Area <- as.factor(dane2$Property_Area)
dane2$Gender <- as.factor(dane2$Gender)
dane2$Loan_Status <- as.factor(dane2$Loan_Status)

Widzimy, ĆŒe w niektĂłrych nazach zmiennych brakuje ” “, w celu lepszego wyglądu danych i efektywniejsze pracy, poprawiamy nazwy tych zmiennych

#dane1
dane1 <- dane1 %>%
  rename(Applicant_Income=ApplicantIncome) 

dane1 <- dane1 %>%
  rename(Coapplicant_Income=CoapplicantIncome) 

dane1 <- dane1 %>%
  rename(Loan_Amount=LoanAmount) 

dane1 <- dane1 %>%
  rename(Loan_ID=Loan_ID) 

#dane2
dane2 <- dane2 %>%
  rename(Applicant_Income=ApplicantIncome) 

dane2 <- dane2 %>%
  rename(Coapplicant_Income=CoapplicantIncome) 

dane2 <- dane2 %>%
  rename(Loan_Amount=LoanAmount) 

dane2 <- dane2 %>%
  rename(Loan_ID=Loan_ID) 

Dane zostaƂy zorganizowane.


Walidacja danych

NaleĆŒy sprawdzić poprawnoƛć danych2, do tego celu przeprowadzimy walidację danych. Nasze dane powinny speƂniać następujące warunki:

  • Jeƛli wnioskodawca jest w związku maĆ‚ĆŒeƄskim, to wĂłwczas tylko wtedy wspóƂwnioskodaca moĆŒe wykazywać dochody. (“Coapplicant Income” >=0, gdy “Married”= TRUE)

  • Status maĆ‚ĆŒeƄstwa moĆŒe przyjmować jedynie wartoƛci “tak” lub “nie”

  • PƂeć powinna przyjmować wartoƛć “kobieta” lub “mÄ™ĆŒczyzna”

  • Liczba osĂłb na utrzymaniu przyjmuje wartoƛci “0”, “1”, “2”, “3+”

  • Edukacja wartoƛć “po studiach”, “nie ukuoƄczyƂ studiĂłw”.

  • PrzychĂłd aplikanta musi być większy od 0, poniewaĆŒ w przeciwnym razie nie powinien on ubiegać się o kredy (nie będzie miaƂ jak go spƂacić).

  • PrzychĂłd wspóƂwnioskodacy moĆŒe przymować wartoƛć 0 w przypadku, gdy wspóƂwnioskodawca nie wykazuje ĆŒadnych dochodĂłw lub aplikant jest sam. Większe w przypadku gdy wspóƂwnioskodaca wykazuje jakieƛ dochody.

  • Kwota kredytu oraz termin muszą być większe od 0, ĆŒeby zaistniaƂo zdarzenie.

  • Historia kredytowa powinna przyjmować wartoƛć “1” jeƛli aplikant braƂ juĆŒ wczeƛniej kredyt, a “0” jeƛli jeszcze nie.

  • Aplikant moĆŒe pochodzić z miasta, ze wsi lub z terenĂłw podmiejskich.

rules <- validator(if(Married=="Yes") Coapplicant_Income >= 0, 
                   Married %in% c("Yes", "No"),
                   Gender %in% c("Female", "Male"), 
                   Dependents %in% c("0","1","2","3+"),
                   Education %in% c("Graduate","Not Graduate"),
                   Self_Employed %in% c("Yes","No"), 
                   Applicant_Income > 0,
                   Coapplicant_Income >= 0,
                   Loan_Amount>0, 
                   Loan_Amount_Term>0, 
                   Credit_History %in% c("1", "0"), 
                   Property_Area %in% c("Urban", "Semiurban", "Rural"))

#dane2
summary(confront(dane2, rules)) %>%
  formattable(align ="c")
name items passes fails nNA error warning expression
V01 614 614 0 0 FALSE FALSE Married != “Yes” | (Coapplicant_Income - 0 >= -0.00000001)
V02 614 611 0 3 FALSE FALSE Married %vin% c(“Yes”, “No”)
V03 614 601 0 13 FALSE FALSE Gender %vin% c(“Female”, “Male”)
V04 614 599 0 15 FALSE FALSE Dependents %vin% c(“0”, “1”, “2”, “3+”)
V05 614 614 0 0 FALSE FALSE Education %vin% c(“Graduate”, “Not Graduate”)
V06 614 582 0 32 FALSE FALSE Self_Employed %vin% c(“Yes”, “No”)
V07 614 614 0 0 FALSE FALSE Applicant_Income > 0
V08 614 614 0 0 FALSE FALSE Coapplicant_Income - 0 >= -0.00000001
V09 614 592 0 22 FALSE FALSE Loan_Amount > 0
V10 614 600 0 14 FALSE FALSE Loan_Amount_Term > 0
V11 614 564 0 50 FALSE FALSE Credit_History %vin% c(“1”, “0”)
V12 614 614 0 0 FALSE FALSE Property_Area %vin% c(“Urban”, “Semiurban”, “Rural”)

Analiza walidacji: Nasze dane nie zawierają bƂędów logicznych.


Identyfikacja rozkƂadu danych, eliminacja danych odstających

W tym podrozdziale przyjrzymy się zmiennym ekstremalnym, odstającym i wyeliminujemy obserwacje wpƂywowe, ĆŒeby nie zakƂócaƂy pĂłĆșniejszej analizy. Identyfikacja będzie miaƂa zastosowanie w przypadku zmiennych iloƛciowych: Applicant_Income, Coapplicant_Income, Loan_Amount


Dane odstające

Przyglądamy się rozkƂadowi danych, pod względem obserwacji odstających i widzimy, ĆŒe dane mają one duĆŒy rozstęp. Dla wszystkich zmiennych istnieją obserwacje ekstremalne, ktĂłre znieksztaƂcają rozkƂad prawostronnie.

box1.1 <- dane2 %>%
  ggplot(aes(x = Applicant_Income)) +
  geom_boxplot(fill='#A3b5c0', color="gray20", notch = TRUE, alpha=0.7) +
  ggtitle("Aplicant Income")+
  xlab("") +
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

box1.2 <- dane2 %>%
  filter(Coapplicant_Income>0)%>% #częƛć wspóƂwnioskodacĂłw nie wykazuje ĆŒadnych dochodĂłw, co zaburzaƂoby rozkƂad zmiennych
  ggplot(aes(x = Coapplicant_Income)) +
  geom_boxplot(fill='#A3b5c0', color="gray20", notch = TRUE, alpha=0.7) +
  ggtitle("Coapplicant Income")+
  xlab("") +
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

box1.3 <- dane2 %>%
  ggplot(aes(x = Loan_Amount)) +
  geom_boxplot(fill='#A3b5c0', color="gray20", notch = TRUE, alpha=0.7) +
  ggtitle("Loan Amount")+
  xlab("") +
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

plot_grid(box1.1,box1.2,box1.3, nrow = 3)


Dlatego najpierw usuwamy obserwacje, które są ekstremalne.

  • Dla zmiennej Applicant Income powyĆŒej 20100

  • Dla zmiennej Coapplicant Income powyĆŒej 10000

  • Dla zmiennnej Loan Amount powyĆŒej 370

dane2 <-  dane2 %>%
         filter(Applicant_Income<20100)

dane2 <-  dane2 %>%
         filter(Coapplicant_Income<10000)

dane2 <-  dane2 %>%
         filter(Loan_Amount<370)

Następnie pozostaƂe obserwacje wyeliminujemy metodą kwartyli. W tym celu stworzymy funkcję, króra usunie obserwacje, które przekraczają dwukrotnoƛć rozstępu w odniesieniu do dolnej i górnej granicy.

detect_outlier <- function(x) {
  na.omit(x)
  lower_bound <- quantile(x, 0.25)
  upper_bound <- quantile(x, 0.75)
  na.rm = TRUE
  I = upper_bound - lower_bound
  which(x < lower_bound-I*2 | x > upper_bound+I*2)
}

Zastosowanie funkcji.

#dane2
d2.1 <- detect_outlier(dane2$Applicant_Income)
d2.2 <- detect_outlier(dane2$Coapplicant_Income)
d2.3 <- detect_outlier(na.omit(dane2$Loan_Amount))
d2 <- c(d2.1, d2.2, d2.3)
d2

dane2 <- dane2[-c(33,  52,  64,  99, 107, 127, 133, 135, 175, 183, 237, 241, 254, 302, 344, 393, 432, 440, 443, 447, 456, 471, 492, 493, 528, 549,  11,  36, 114, 124, 166, 236, 409,  20,  33,  52,  64, 124, 241, 243, 302, 326, 354, 476, 495, 547),]

RozkƂad po usunięciu obserwacji zakƂócających, zostaƂ przedstawiony poniĆŒej. Dzięki wyeliminowaniu danych odstających, uporaliƛmy się z problemem obserwacji znieksztaƂcających rozkƂad. Ponadto dzięki temu, pĂłĆșniejsza analiza będzie bardziej rzetelna.

box3.1 <- dane2 %>%
  ggplot(aes(x = Applicant_Income)) +
  geom_boxplot(fill='#A3b5c0', color="gray20", notch = TRUE, alpha=0.7) +
  ggtitle("Aplicant Income")+
  xlab("") +
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

box3.2 <- dane2 %>%
  filter(Coapplicant_Income>0)%>% 
  ggplot(aes(x = Coapplicant_Income)) +
  geom_boxplot(fill='#A3b5c0', color="gray20", notch = TRUE, alpha=0.7) +
  ggtitle("Coapplicant Income")+
  xlab("") +
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

box3.3 <- dane2 %>%
  ggplot(aes(x = Loan_Amount)) +
  geom_boxplot(fill='#A3b5c0', color="gray20", notch = TRUE, alpha=0.7) +
  ggtitle("Loan Amount")+
  xlab("") +
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

plot_grid(box3.1,box3.2,box3.3, nrow = 3)


Braki w danych

Mamy 109 brakujących obserwacji. Braki występują w 6/13 zmiennych.

  • Najwięcej brakujących obserwacji zawiera zmienna Credit_History. Następnie Self_Employed.

  • Reszta obserwacji, w ktĂłrych są braki: Gender, Dependents, Loan_Amount_Term

  • Jest ich za duĆŒo, aby usunąć, dlatego naleĆŒy uzupeƂnić je w jak najbardziej rzetelny sposĂłb, zbliĆŒony do rzeczywistoƛci i usunąć tylko te obserwacji, w ktĂłrych występuje więcej niĆŒ jedno NA


Wizualizacja

gg_miss_var(dane2) +theme_minimal()

Tabela przedstawiająca braki

`Iloƛć obserwacji` <- c(sum(is.na(dane2$Married)),sum(is.na(dane2$Gender)), sum(is.na(dane2$Dependents)), sum(is.na(dane2$Loan_Amount_Term)), sum(is.na(dane2$Self_Employed)), sum(is.na(dane2$Credit_History)))
`Miejsce wystąpienia` <- c("Married","Gender","Dependents","Loan_Amount_Term","Self_Employed", "Credit_History")
braki1 <-data.frame(`Miejsce wystąpienia`,`Iloƛć obserwacji`)
braki1 %>% formattable(align="c")
Miejsce.wystąpienia Iloƛć.obserwacji
Married 2
Gender 9
Dependents 13
Loan_Amount_Term 14
Self_Employed 28
Credit_History 43

SzczegóƂowe przedstawienie braków

dane2[!complete.cases(dane2),] %>% 
  head(20) %>%
  formattable(align="c")
Loan_ID Gender Married Dependents Education Self_Employed Applicant_Income Coapplicant_Income Loan_Amount Loan_Amount_Term Credit_History Property_Area Loan_Status
LP1027 Male Yes 2 Graduate NA 2500 1840 109 360 1 Urban Y
LP1034 Male No 1 Not Graduate No 3596 0 100 240 NA Urban Y
LP1041 Male Yes 0 Graduate NA 2600 3500 115 NA 1 Urban Y
LP1050 NA Yes 2 Not Graduate No 3365 1917 112 360 0 Rural N
LP1052 Male Yes 1 Graduate NA 3717 2925 151 360 NA Semiurban N
LP1087 Female No 2 Graduate NA 3750 2083 120 360 1 Semiurban Y
LP1091 Male Yes 1 Graduate NA 4166 3369 201 360 NA Urban N
LP1109 Male Yes 0 Graduate No 1828 1330 100 NA 0 Urban N
LP1123 Male Yes 0 Graduate No 2400 0 75 360 NA Urban Y
LP1136 Male Yes 0 Not Graduate Yes 4695 0 96 NA 1 Urban Y
LP1137 Female No 0 Graduate No 3410 0 88 NA 1 Urban Y
LP1250 Male Yes 3+ Not Graduate No 4755 0 95 NA 0 Semiurban N
LP1264 Male Yes 3+ Not Graduate Yes 3333 2166 130 360 NA Semiurban Y
LP1273 Male Yes 0 Graduate No 6000 2250 265 360 NA Semiurban N
LP1280 Male Yes 2 Not Graduate No 3333 2000 99 360 NA Semiurban Y
LP1357 Male NA NA Graduate No 3816 754 160 360 1 Urban Y
LP1370 Male No 0 Not Graduate NA 7333 0 120 360 1 Rural N
LP1387 Female Yes 0 Graduate NA 2929 2333 139 360 1 Semiurban Y
LP1391 Male Yes 0 Not Graduate No 3572 4114 152 NA 0 Rural N
LP1398 Male No 0 Graduate NA 5050 0 118 360 1 Semiurban Y

Imputacja brakujących danych

Zmienna “Gender”

Daną “Gender” zostawimy z brakami, poniewaĆŒ w pĂłĆșniejszej analizje, ta zmienna nie będzie miaƂa istotnego wpƂywu na wynik analizy.


Zmienna “Married”

Podczas przeprowadzania walidacji, ustaliliƛmy, ĆŒe tylko dla osobĂłw, w związkach maĆ‚ĆŒeƄskich, moĆŒe być wykazany przychĂłd wspóƂwnioskodawcy. Zatem naleĆŒy sprawdzić, czy dla brakujących obserwacji, wartoƛć “Coapplicant_Income” >0, jeƛli tak osoba jest w związku maĆ‚ĆŒeƄskim. Jeƛli “Coapplicant_Income”=0, nie wiemy, czy dana osoba jest w związku maĆ‚ĆŒeƄskim, pozostawimy te pola puste.

which(is.na(dane2$Married))
dane2[is.na(dane2$Married),] %>% formattable(align="c")

Jest jeden brak, ktĂłry moĆŒemy uzupeƂnić, Drugą obserwację zdecydowaliƛmy się usunąć, poniewaĆŒ, te obserwacje mają więcej niĆŒ jedno “NA”

dane2[is.na(dane2$Married)& dane2$Coapplicant_Income > 0,"Married"] <- "Yes"
dane2[is.na(dane2$Married),] %>% formattable(align="c")
dane2 <- dane2[-195,]
which(is.na(dane2$Married))


Usuwanie brakĂłw, gdzie występuje więcej niĆŒ jedno NA

Usuwamy, te obserwacje, dla ktĂłrych mamy więcej niĆŒ brak w jednej zmiennej

#Credit History
which(is.na(dane2$Credit_History) & (is.na(dane2$Dependents) | is.na(dane2$Gender) | is.na(dane2$Education) | is.na(dane2$Self_Employed)| is.na(dane2$Coapplicant_Income)|is.na(dane2$Loan_Amount)|is.na(dane2$Applicant_Income)|is.na(dane2$Loan_Amount_Term)))

dane2 <- dane2[-c(21,  27, 202, 355, 397),]

#Dependends
which(is.na(dane2$Dependents) & (is.na(dane2$Gender) | is.na(dane2$Education) | is.na(dane2$Self_Employed)| is.na(dane2$Coapplicant_Income)|is.na(dane2$Loan_Amount)|is.na(dane2$Applicant_Income)|is.na(dane2$Loan_Amount_Term)))
dane2 <- dane2[-c(285),]


Podgląd brakujących obserwacji po dokonaniu czyszczenia danych

Dzięki czyszczeniu pozbyliƛmy się braków w zmiennej “Married” oraz zredukowaliƛmy licznę poozostaƂych braków

Wykres z obserwacjami brakującymi

gg_miss_var(dane2) +theme_minimal()

Tabela z podsumowaniem brakĂłw

`Iloƛć obserwacji` <- c(sum(is.na(dane2$Married)),sum(is.na(dane2$Gender)), sum(is.na(dane2$Dependents)), sum(is.na(dane2$Loan_Amount_Term)), sum(is.na(dane2$Self_Employed)), sum(is.na(dane2$Credit_History)))
`Miejsce wystąpienia` <- c("Married","Gender","Dependents","Loan_Amount_Term","Self_Employed", "Credit_History")
braki1 <-data.frame(`Miejsce wystąpienia`,`Iloƛć obserwacji`)
braki1 %>% formattable(align="c")
Miejsce.wystąpienia Iloƛć.obserwacji
Married 0
Gender 8
Dependents 11
Loan_Amount_Term 13
Self_Employed 24
Credit_History 38



4. Zrozumienie danych

Zmienne jakoƛciowe

Loan Status

Loan Status jest naszą zmienną objaƛnianą. Widzimy, ĆŒe 66% wnioskĂłw zostaƂo rozpatrzonych pozytwynie.

dane2 %>%
  dplyr::select(Loan_Status) %>%
  drop_na() %>%
  group_by(Loan_Status)%>%
  count() %>%
  mutate(`udziaƂ procentowy`=percent(n/(155+393))) %>%
  ggplot(aes(x = "", y = n, fill = Loan_Status))+
  geom_col(colour = "gray20", alpha=0.7, width = .3) +
  scale_fill_manual(values = c("#A3b5c0", "#e5e0d8", "#b4bdba","thistle3"), labels=c("Yes","No")) + 
  ggtitle("Loan Status")+
  xlab("") +
  labs(fill="Loan Status")+
  coord_flip() +
  geom_text(aes(label=`udziaƂ procentowy`), position=position_stack(vjust = 0.49), vjust=1, size=4, color="gray20")+
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

Gender

Klientami naszego banku są najczęƛciej mÄ™ĆŒczyĆșni. Stanowią oni ponad 80% wszystkich klientĂłw

dane2 %>%
  dplyr::select(Gender) %>%
  drop_na() %>%
  group_by(Gender)%>%
  count() %>%
  mutate(`udziaƂ procentowy`=percent(n/(99+411))) %>%
  ggplot(aes(x = "", y = n, fill = Gender))+
  geom_col(colour = "gray20", alpha=0.7, width = .3) +
  scale_fill_manual(values = c("#A3b5c0", "#e5e0d8", "gray20")) + 
  ggtitle("Gender")+
  xlab("") +
  coord_flip() +
  geom_text(aes(label=`udziaƂ procentowy`), position=position_stack(vjust = 0.49), vjust=1, size=4, color="gray20")+
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

Married

Najczęƛciej osoby, ktĂłre ubiegają się o kredyt są w związkach maĆ‚ĆŒeƄskich. Są to osoby ustabilizowane, Stanowią oni ponad 64% wszystkich klientĂłw

dane2 %>%
  dplyr::select(Married) %>%
  drop_na() %>%
  group_by(Married)%>%
  count() %>%
  mutate(`udziaƂ procentowy`=percent(n/(183+335))) %>%
  ggplot(aes(x = "", y = n, fill = Married))+
  geom_col(colour = "gray20", alpha=0.7, width = .3) +
  scale_fill_manual(values = c("#A3b5c0", "#e5e0d8", "gray20")) + 
  ggtitle("Married")+
  xlab("") +
  coord_flip() +
  geom_text(aes(label=`udziaƂ procentowy`), position=position_stack(vjust = 0.49), vjust=1, size=4, color="gray20")+
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

Dependents

Prawie 60% klientów nie miaƂa nikogo na utrzymaniu. 15.58% wnioskujących miaƂo tylko jedną osobę, 17.36% dwie osoby. Najmniej liczną grupę stanowili klienci, którzy miali 3. lub więcej osób, które musieli utrzymywać.

dane2 %>%
  dplyr::select(Dependents) %>%
  drop_na() %>%
  group_by(Dependents)%>%
  count() %>%
  mutate(`udziaƂ procentowy`=percent(n/(301+79+88+39))) %>%
  ggplot(aes(x = "", y = n, fill = Dependents))+
  geom_col(colour = "gray20", alpha=0.7, width = .3) +
  scale_fill_manual(values = c("#A3b5c0", "#e5e0d8", "#b4bdba","mistyrose3")) + 
  ggtitle("Dependents")+
  xlab("") +
  coord_flip() +
  geom_text(aes(label=`udziaƂ procentowy`), position=position_stack(vjust = 0.49), vjust=1, size=4, color="gray20")+
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

Education

WyraĆșną większoƛć wƛrĂłd aplikantĂłw stanowiƂy osoby, ktĂłre ukoƄczyƂy studia, stanowili oni 76% wszystkich klientĂłw.

dane2 %>%
  dplyr::select(Education) %>%
  drop_na() %>%
  group_by(Education)%>%
  count() %>%
  mutate(`udziaƂ procentowy`=percent(n/(394+124))) %>%
  ggplot(aes(x = "", y = n, fill = Education))+
  geom_col(colour = "gray20", alpha=0.7, width = .3) +
  scale_fill_manual(values = c("#A3b5c0", "#e5e0d8", "#b4bdba","mistyrose3")) + 
  ggtitle("Education")+
  xlab("") +
  coord_flip() +
  geom_text(aes(label=`udziaƂ procentowy`), position=position_stack(vjust = 0.49), vjust=1, size=4, color="gray20")+
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

Self Employed

Większoƛć osób wnioskujących o kredyt w naszym banku, nie prowadzi wƂasnego biznesu. Osoby samozatrudnione stanowią zaledwie 11,94% wszystkich klientów.

dane2$Credit_History <- as.factor(dane2$Credit_History)

dane2 %>%
  dplyr::select(Self_Employed) %>%
  drop_na() %>%
  group_by(Self_Employed)%>%
  count() %>%
  mutate(`udziaƂ procentowy`=percent(n/(435+59))) %>%
  ggplot(aes(x = "", y = n, fill = Self_Employed))+
  geom_col(colour = "gray20", alpha=0.7, width = .3) +
  scale_fill_manual(values = c("#A3b5c0", "#e5e0d8", "#b4bdba","mistyrose3")) + 
  ggtitle("Self Employed")+
  xlab("") +
  labs(fill="Self Employed")+
  coord_flip() +
  geom_text(aes(label=`udziaƂ procentowy`), position=position_stack(vjust = 0.49), vjust=1, size=4, color="gray20")+
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

Loan Amount Term

Najczęƛciej wybieranym okresem, na ktĂłry klienci chcą zaciągnąć kredyt jest okres 360, ktĂłry stanowi ponad 85% wszystkich okresĂłw na jakie zawierana jest poĆŒyczka. Drugą najczęƛciej wybieraną opcją jest 180 dni, ponad 7% klientĂłw, wybiera tę opcję.

Tabela

dane2 %>%
  dplyr::select(Loan_Amount_Term) %>%
  drop_na() %>%
  group_by(Loan_Amount_Term)%>%
  count() %>%
  mutate(`UdziaƂ procentowy`= percent(n/505))%>% 
  formattable()
Loan_Amount_Term n UdziaƂ procentowy
12 1 0.20%
36 2 0.40%
60 2 0.40%
84 4 0.79%
120 3 0.59%
180 36 7.13%
240 3 0.59%
300 10 1.98%
360 431 85.35%
480 13 2.57%

Prezentacja rozkƂadu

wafel <- dane2 %>%
  dplyr::select(Loan_Amount_Term) %>%
  drop_na() %>%
  group_by(Loan_Amount_Term)%>%
  count() 
wafel <- c(`term 12-120`= 12, `term 80`=    36, `term 240`=3, `term 300`=   10, `term 360`= 431, `term 480`= 13)
  waffle(wafel, rows=15,colors = c("#eee2d4", "mistyrose3", "lightsteelblue2","#959595","#ced0ce","pink3"))

Credit History

Ponad 84% klientów ma historię kredytową.

dane2 %>%
  dplyr::select(Credit_History) %>%
  drop_na() %>%
  group_by(Credit_History)%>%
  count() %>%
  mutate(`udziaƂ procentowy`=percent(n/(404+76))) %>%
  ggplot(aes(x = "", y = n, fill = Credit_History))+
  geom_col(colour = "gray20", alpha=0.7, width = .3) +
  scale_fill_manual(values = c("#A3b5c0", "#e5e0d8", "#b4bdba","thistle3"), labels=c("No","Yes")) + 
  ggtitle("Credit History")+
  xlab("") +
  labs(fill = "Credit History")+
  coord_flip() +
  geom_text(aes(label=`udziaƂ procentowy`), position=position_stack(vjust = 0.49), vjust=1, size=4, color="gray20")+
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

Property Area

Proporcję pomiędzy miejscem zamieszkania aplikantĂłw są w miarę zbliĆŒonę. Największy udziaƂ stanowią klienci z obszarĂłw podmiejskich (41,46%). Następnie 33,75% stanowią wnioskodawcy z obszarĂłw miejskich, i zaledwie 1% mniejszy od nich udziaƂ mają klienci z obszarĂłw wiejskich.

dane2 %>%
  dplyr::select(Property_Area) %>%
  drop_na() %>%
  group_by(Property_Area)%>%
  count() %>%
  mutate(`udziaƂ procentowy`=percent(n/(404+76))) %>%
  ggplot(aes(x = "", y = n, fill = Property_Area))+
  geom_col(colour = "gray20", alpha=0.7, width = .3) +
  scale_fill_manual(values = c("#A3b5c0", "#e5e0d8","thistle3")) + 
  ggtitle("Property Area")+
  xlab("") +
  coord_flip() +
  labs(fill="Property Area")+
  geom_text(aes(label=`udziaƂ procentowy`), position=position_stack(vjust = 0.49), vjust=1, size=4, color="gray20")+
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

Zmienne iloƛciowe

Applicant Income

dane2 %>%
  dplyr::select(Applicant_Income) %>%
  ggplot(aes(x = Applicant_Income)) +
  geom_density(fill="#A3b5c0", alpha=0.4) +
  geom_vline(aes(xintercept = mean(Applicant_Income, na.rm = T)), 
               colour = "pink3", linetype ="longdash", size = .7)+
  geom_vline(aes(xintercept = median(Applicant_Income, na.rm = T)), 
               colour = "royalblue3", linetype ="longdash", size = .7)+
  geom_vline(aes(xintercept = quantile(Applicant_Income, probs = 0.25, na.rm = T)), 
               colour = "darkseagreen4", linetype ="longdash", size = .7)+
  geom_vline(aes(xintercept = quantile(Applicant_Income, probs = 0.75, na.rm = T)), 
               colour = "gold3", linetype ="longdash", size = .7)+
  ggtitle("Applicant Income")+
  xlab("") +
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

Oznaczenia na wykresie: ƛrednia- rĂłĆŒowy, mediana- niebieski, Q1-zielony, Q3-ĆŒĂłĆ‚ty

statystyka <- c("Min", "Max", "Q1","Mediana","Q3" ,"ƚrednia", "Odch. std.", "Skoƛnoƛć","Kurtoza")
wartosc <- c(min(dane2$Applicant_Income),max(dane2$Applicant_Income), quantile(dane2$Applicant_Income, 0.25), round(median(dane2$Applicant_Income,0.5),2), quantile(dane2$Applicant_Income, 0.75), round(mean(dane2$Applicant_Income),2), round(sd(dane2$Applicant_Income),2), round(skew(dane2$Applicant_Income),2), round(kurtosi(dane2$Applicant_Income),2))

raport <- data.frame(statystyka,wartosc) 
raport %>% formattable()
statystyka wartosc
Min 150.00
Max 10513.00
Q1 2765.00
Mediana 3597.50
Q3 4929.00
Úrednia 4103.83
Odch. std. 1928.70
Skoƛnoƛć 1.18
Kurtoza 1.24

Obserwacja:

  • Úrednio aplikanci zarabiają 4104.

  • Aplikant, ktĂłry zarabiaƂ najmniej, wykazywaƂ dochĂłd w wysokoƛci 150,

  • NajwyĆŒszy dochĂłd klienta wyniĂłsƂ 10513

  • 25% aplikantĂłw miaƂa mniejszy dochĂłd niĆŒ 2764

  • 75% klientĂłw miaƂa dochĂłd wyĆŒszy niĆŒ 4931

  • 50% klientĂłw miaƂa niĆŒszy niĆŒ dochĂłd niĆŒ 3597 (tym samym druga poƂowa aplikantĂłw miaƂa dochĂłd wyĆŒszy niĆŒ 3597)

  • Úrednio o 1928.7 dochody klientĂłw odchylają się od ƛredniego dochodu. To oznacza, ĆŒe bardzo rozbieĆŒne są dochody naszych aplikantĂłw.




Coapplicant Income

dane2 %>%
  dplyr::select(Coapplicant_Income) %>%
    filter(Coapplicant_Income>0) %>%
  ggplot(aes(x = Coapplicant_Income)) +
  geom_density(fill="#A3b5c0", alpha=0.4) +
  geom_vline(aes(xintercept = mean(Coapplicant_Income, na.rm = T)), 
               colour = "pink3", linetype ="longdash", size = .7)+
  geom_vline(aes(xintercept = median(Coapplicant_Income, na.rm = T)), 
               colour = "royalblue3", linetype ="longdash", size = .7)+
  geom_vline(aes(xintercept = quantile(Coapplicant_Income, probs = 0.25, na.rm = T)), 
               colour = "darkseagreen4", linetype ="longdash", size = .7)+
    geom_vline(aes(xintercept = quantile(Coapplicant_Income, probs = 0.75, na.rm = T)), 
               colour = "gold3", linetype ="longdash", size = .7)+
  ggtitle("Coapplicant Income")+
  xlab("") +
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

Oznaczenia na wykresie: ƛrednia- rĂłĆŒowy, mediana- niebieski, Q1-zielony, Q3-ĆŒĂłĆ‚ty

statystyka <- c("Min", "Max", "Q1","Mediana","Q3" ,"ƚrednia", "Odch. std.", "Skoƛnoƛć","Kurtoza")
wartosci <- dane2 %>%
  transmute(Coapplicant_Income) %>%
  filter(Coapplicant_Income>0)

wartosci <- c(min(wartosci$Coapplicant_Income),max(wartosci$Coapplicant_Income), quantile(wartosci$Coapplicant_Income, 0.25), round(median(wartosci$Coapplicant_Income,0.5),2), quantile(wartosci$Coapplicant_Income, 0.75), round(mean(wartosci$Coapplicant_Income),2), round(sd(wartosci$Coapplicant_Income),2), round(skew(wartosci$Coapplicant_Income),2), round(kurtosi(wartosci$Coapplicant_Income),2))

raport <- data.frame(statystyka,wartosci) 
raport %>% formattable()
statystyka wartosci
Min 16.12
Max 6666.00
Q1 1625.00
Mediana 2079.00
Q3 2857.00
Úrednia 2333.43
Odch. std. 1164.50
Skoƛnoƛć 1.09
Kurtoza 1.23

Obserwacja: Pod uwagę są brani jedynie wspóƂwnioskodawcy, którzy wykazują przychody.

  • Úrednio aplikanci zarabiają 4104.

  • DochĂłd wspóƂwnioskodawcy, o najmniejszej wartoƛci byƂ rĂłwny 16.12,

  • NajwyĆŒszy dochĂłd wspóƂwnioskodawcy wyniĂłsƂ 6666,

  • 25% wspóƂwnioskodawcĂłw miaƂa mniejszy dochĂłd niĆŒ 1625

  • 75% wspóƂwnioskodawcĂłw miaƂa dochĂłd wyĆŒszy niĆŒ 2857

  • 50% wspóƂwnioskodawcĂłw miaƂa niĆŒszy niĆŒ dochĂłd niĆŒ 2079 (tym samym druga poƂowa miaƂa dochĂłd wyĆŒszy niĆŒ 3597)

  • Úrednio o 1164.5 dochody wspóƂwnioskodawcĂłw odchylaƂ się od ich ƛredniego dochodu.




Loan Amount

dane2 %>%
  dplyr::select(Loan_Amount) %>%
  ggplot(aes(x = Loan_Amount)) +
  geom_density(fill="#A3b5c0", alpha=0.4) +
  geom_vline(aes(xintercept = mean(Loan_Amount, na.rm = T)), 
               colour = "pink3", linetype ="longdash", size = .7)+
  geom_vline(aes(xintercept = median(Loan_Amount, na.rm = T)), 
               colour = "royalblue3", linetype ="longdash", size = .7)+
  geom_vline(aes(xintercept = quantile(Loan_Amount, probs = 0.25, na.rm = T)), 
               colour = "darkseagreen4", linetype ="longdash", size = .7)+
    geom_vline(aes(xintercept = quantile(Loan_Amount, probs = 0.75, na.rm = T)), 
               colour = "gold3", linetype ="longdash", size = .7)+
  ggtitle("Loan Amount")+
  xlab("") +
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

Oznaczenia na wykresie: ƛrednia- rĂłĆŒowy, mediana- niebieski, Q1-zielony, Q3-ĆŒĂłĆ‚ty

statystyka <- c("Min", "Max", "Q1","Mediana","Q3" ,"ƚrednia", "Odch. std.", "Skoƛnoƛć","Kurtoza")
wartosc <- c(min(dane2$Loan_Amount),max(dane2$Loan_Amount), quantile(dane2$Loan_Amount, 0.25), round(median(dane2$Loan_Amount,0.5),2), quantile(dane2$Loan_Amount, 0.75), round(mean(dane2$Loan_Amount),2), round(sd(dane2$Loan_Amount),2), round(skew(dane2$Loan_Amount),2), round(kurtosi(dane2$Loan_Amount),2))

raport <- data.frame(statystyka,wartosc) 
raport %>% formattable()
statystyka wartosc
Min 9.00
Max 280.00
Q1 99.00
Mediana 122.00
Q3 155.00
Úrednia 127.63
Odch. std. 47.40
Skoƛnoƛć 0.59
Kurtoza 0.66

Obserwacja:

  • Úrednio aplikanci wnioskują o kredyt w wysokoƛci 127.63,

  • NajniĆŒszy wniosek o kredyt byƂ rĂłwny 9,

  • NajwyĆŒszy dochĂłd klienta wyniĂłsƂ 280,

  • 25% wnioskĂłw byƂa na kwotę niĆŒszą niĆŒ 99,

  • 75% wnioskĂłw byƂa na kwotę wyĆŒszą niĆŒ 155,

  • 50% wnioskĂłw byƂa na kwotę niĆŒszą niĆŒ 122 (tym samym druga poƂowa na kwotę wyĆŒszy niĆŒ 122),

  • Úrednio o 47.4 kwoty na wniioskach poĆŒyczkowych odchylają się od ƛredniej kwoty, na ktĂłrą zaciągane są zobowiązania.




Korelacja

Korelacja to miara kierunku i siƂy związku między dwiema zmiennymi. Jej wartoƛć zawiera się w przedziale od -1 do 1. Korelacja bliska zeru sugeruje brak istotnej zaleĆŒnoƛci liniowej między zmiennymi - ale nie oznacza, ĆŒe nie ma między nimi innych związkĂłw. Gdy wartoƛć korelacji jest bliska 1, to obydwie zmienne rosną lub maleją razem, a gdy jest bliska -1, to wzrostom wartoƛci jednej ze zmiennych towarzyszą spadki wartoƛci drugiej.


Gdy badamy więcej niz dwie zmienne, moĆŒemy przedstawić ich wspóƂczynniki korelacji w postaci jednej macierzy. W naszym przypadku uĆŒyliƛmy funkcji hetcor, aby zbadać wspóƂczynniki korelacji liniowej Pearsona między zmiennymi numerycznymi (“Loan Amount”, “Applicant Income”, “Coapplicant Income”, i “Loan Amount Term”), wspóƂczynniki korelacji polichorycznej między zmiennymi czynnikowymi (“Loan Status”, “Gender”, “Credit History”, “Property Area” i “Dependents”) oraz wspóƂczynniki korelacji wieloseryjnej między zmiennymi czynnikowymi a numerycznymi.



Korelacje między decyzją o przyznaniu kredytu a innymi zmiennymi

  • Z macierzy korelacji dowiadujemy się, ĆŒe zmienna najsilniej skorelowaną z decyzją o przyznaniu kredytu jest posiadanie historii kredytowej - wspóƂczynnik korelacji wynosi w tym przypadku aĆŒ 0,87.

  • SƂabszą, ale wciÄ…ĆŒ istotną korelację pozytywną wykazują pƂeć męska, dochĂłd wspóƂmaĆ‚ĆŒonka i iloƛc posiadanych dzieci - tu wspóƂczynniki wynoszą odpowiednio 0,16, 0,09 oraz 0,06.

  • W przypadku poƂoĆŒenia nieruchomoƛci moĆŒna zwrĂłcic uwagę na delikatną korelację pozytywną decyzji o przyznaniu kredytu z terenami miejskimi raczej niĆŒ wiejskimi - wspóƂczynnik korelacji polichorycznej wyniĂłsƂ dla tej pary zmiennych 0,04.

  • Co ciekawe, wspóƂczynniki korelacji pomiędzy decyzją o przyznaniu kredytu a dochodem klienta, okresem kredytowania i wysokoƛcią poĆŒyczki są bliskie zeru, co sugeruje brak bezpoƛredniego związku między zmiennymi.

Korelacje między pozostaƂymi zmiennymi

Interesujące są rĂłwnieĆŒ zaleĆŒnoƛci między zmiennymi wpƂywającymi na zdolnoƛć kredytową:

  • Występuje wyraĆșna, pozytywna korelacja między wysokoƛcia poĆŒyczki o ktĂłra wnioskuje klient a jego dochodem - wspóƂczynnik wynosi aĆŒ 0,53. moĆŒe to wyjaƛnić brak wyraĆșnej korelacji między tymi zmiennymi a decyzją banku - klienci o niĆŒszym dochodzie wydają się rezygnowac z ubiegania się o wyĆŒszą poĆŒyczkę juĆŒ na etapie skƂadania wniosku, więc decyzja banku pozornie nie jest skorelowana z ich dochodem i wysokoƛcia poĆŒyczki o jaką wnioskują - choć moĆŒna przypuszczać, ĆŒe taka korelacja by wystąpiƂa gdyby nie tego typu “samoograniczanie się” klientĂłw.

  • Doƛć znacząca jest tez korelacja między męską pƂcią klientĂłw a iloƛcią dzieci i dochodem wspóƂmaĆ‚ĆŒonka (wspóƂczynniki na poziomie odpowiednio 0,4 i 0,32) - sugeruje to, ĆŒe w przypadku maĆ‚ĆŒeƄstw korzystających z kredytu hipotecznego maĆ‚ĆŒonkiem, ktĂłry bierze kredyt jest częƛciej mÄ…ĆŒ niĆŒ ĆŒona.

  • MÄ™ĆŒczyĆșni wydaja się teĆŒ skƂonni do zaciągania większych kredytĂłw oraz akceptowania krĂłtszego okresu kredytowania - wspóƂczynniki korelacji między pƂcią męską a wysokoƛcia poĆŒyczki i okresem jej spƂaty wynoszą odpowiednio 0,16 i -0,15.

  • Mozna zaobserwowac ujemna korelacje na poziomie -0,26 pomiędzy dochodem klienta a dochodem jego wspóƂmaĆ‚ĆŒonka. MoĆŒna wiec przypuszczać, ĆŒe wƛrĂłd maĆ‚ĆŒeƄstw występujących z wnioskiem o kredyt hipoteczny występują rĂłĆŒnice w dochodzie obydwu maĆ‚ĆŒonkĂłw.

  • DochĂłd wspóƂmaĆ‚ĆŒonka jest dodatnio skorelowany z wysokoƛcia poĆŒyczki o jaka klient wnioskuje. WspóƂczynnik tej korelacji wynosi jednak 0,26, jest więc okoƂo dwukrotnie niĆŒszy niĆŒ w przypadku dochodu samego klienta.

  • Klienci starający sie o kredyt hipoteczny na nieruchomoƛc poƂozona na terenie wiejskim charakteryzują się niĆŒszym dochodem - zarĂłwno wƂasnym jak i wspóƂmaĆ‚ĆŒonka wspóƂczynniki wynoszą odpowiednio -0,05 i -0,07) - oraz wnioskują o niĆŒsze kredyty z krĂłtszym terminem zapadalnoƛci (wspóƂczynniki korelacji na poziomie -0,1 i -0,08). Sugeruje to, ĆŒe wƛrĂłd klientĂłw występują nierĂłwnoƛci dochodowe i majątkowe uzaleĆŒnione od miejsca zamieszkania.

  • Wraz z liczbą posiadanych dzieci roƛnie dochĂłd klienta, ale spada dochĂłd jego wspóƂmaĆ‚ĆŒonka - wspóƂczynniki korelacji wynoszą tu odpowiednio 0,14 i -0,05. MoĆŒe to sugerować, ĆŒe niekiedy w tej sytuacji jedno z maĆ‚ĆŒonkĂłw w większym stopniu skupia sie na wychowaniu dzieci i jego dochĂłd spada.

  • Co więcej, liczba posiadanych dzieci jest dodatnio skorelowane z wysokoƛcią kredytu o jaki wnioskuje klient i negatywnie skorelowana z terminem kredytowania - wspóƂczynniki korelacji wynoszą odpowiednio 0,15 oraz -0,1. MoĆŒe to sugerować, ĆŒe klienci mogący sobie pozwolić na wyĆŒsze kredyty i krĂłtsze okresy kredytowania mogą rĂłwnieĆŒ utrzymać więcej dzieci.

dane_do_macierzy <- data.frame(dane2$Loan_Status, dane2$Loan_Amount, dane2$Applicant_Income, dane2$Coapplicant_Income, dane2$Loan_Amount_Term, dane2$Dependents,  dane2$Gender, dane2$Credit_History, dane2$Property_Area)
as.tibble(dane_do_macierzy)
etykiety <- c("Loan Status", "Loan Amount", "Applicant Income", "Coapplicant Income", "Loan Amount Term", "Dependents", "Gender", "Credit History", "Property Area")
macierz_korelacji <- polycor::hetcor(dane_do_macierzy)
print(macierz_korelacji)
wspolczynniki_korelacji <- macierz_korelacji$correlations
korelogram <- corrgram(wspolczynniki_korelacji, 
                       order = FALSE, 
                       labels = etykiety,
                       
                       diag.panel = panel.histogram, 
                       upper.panel=panel.pie, 
                       col.regions = colorRampPalette(c("#A3b5c0","#eee2d4", "yellow4", "gray92","#959595","blue4","pink3"))
                       )

5. EDA, czyli Eksploracyjna Analiza Danych

EDA to zestaw interaktywnych i wizualnych technik analizy zbioru danych bez bardziej precyzyjnego wskazania celu eksploracji.

EDA pozwala na:

  • zgƂębienie zbioru danych,

  • sprawdzenie zaleĆŒnoƛci między atrybutami,

  • opracowanie wstępnych wnioskĂłw dotyczących prawidƂowoƛci w zbiorze przykƂadĂłw.


Analiza jednowymiarowa

Celem tej częƛci projektu jest prezentacja w postaci wykresĂłw zaleĆŒnoƛci między decyzją kredytową a poszczegĂłlnymi zmiennymi jakoƛciowymi i iloƛciowymi. Sprawdzimy jak poszczegĂłlne zmienne objaƛniające wpƂywaja na zmienna objaƛnianą.

Zmienne jakoƛciowe

Sprawdzimy jak dane cechy wpƂywają na podejmowanie decyzji kredytowej.

Gender

Zmienna “Gender” nie ma znaczącego wpƂywu na ksztaƂtowanie się zdolnoƛci kredytowej. ZauwaĆŒalna jest nieznaczna rĂłĆŒnica pomiędzy liczbą udzielanych kredytĂłw na korzyƛć mÄ™ĆŒczyzn.

dane2 %>%
  dplyr::select(Gender,Loan_Status) %>%
  filter(Gender==c("Female", "Male")) %>%
  group_by(Gender) %>%
  count(Loan_Status) %>%
  ggplot(aes(x = Gender, y = n, fill = Loan_Status)) +
  geom_col(position = "fill",colour = "gray20", alpha=0.7, width = .7) +
  scale_fill_manual(values=c("#A3b5c0","#e5e0d8"), labels=c("No", "Yes"))+
  labs(fill="Loan Status")+
  ggtitle("Gender")+
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

Dependents

Co ciekawe, najczęƛciej odmawiane są kredyty osobom, które mają tylko 1 osobę na utzymaniu. Dla braku osób na utrzymanej, 2 osób oraz 3 i więcej zdolnoƛć kredytowa jest podobna.

dane2 %>%
 dplyr:: select(Dependents,Loan_Status) %>%
  filter(Dependents==c("0", "1", "2","3+")) %>%
  group_by(Dependents) %>%
  count(Loan_Status) %>%
  ggplot(aes(x = Dependents, y = n, fill = Loan_Status)) +
  geom_col(position = "fill",colour = "gray20", alpha=0.7, width = .7) +
  scale_fill_manual(values=c("#A3b5c0","#e5e0d8"), labels=c("No", "Yes"))+
  labs(fill="Loan Status")+
  ggtitle("Dependents")+
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

Education

Osobom, ktĂłre ukoƄczyƂy szkoƂę częƛciej są udzielane poĆŒyczki, niĆŒ osobom, ktĂłre jej nie ukoƄczyƂy. MoĆŒe to wynikać z faktu, ĆŒe zazwyczaj osoby po szkole mają lepiej pƂatną pracę.

dane2 %>%
  dplyr::select(Education,Loan_Status) %>%
  filter(Education==c("Graduate", "Not Graduate")) %>%
  group_by(Education) %>%
  count(Loan_Status) %>%
  ggplot(aes(x = Education, y = n, fill = Loan_Status)) +
  geom_col(position = "fill",colour = "gray20", alpha=0.7, width = .7) +
  scale_fill_manual(values=c("#A3b5c0","#e5e0d8"), labels=c("No", "Yes"))+
  labs(fill="Loan Status")+
  ggtitle("Education")+
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

Self Employed

WskaĆșnik dla obu grup ksztaƂtuje się na podobnym poziomie.

dane2 %>%
  dplyr::select(Self_Employed,Loan_Status) %>%
  filter(Self_Employed==c("Yes", "No")) %>%
  group_by(Self_Employed) %>%
  count(Loan_Status) %>%
  ggplot(aes(x = Self_Employed, y = n, fill = Loan_Status)) +
  geom_col(position = "fill",colour = "gray20", alpha=0.7, width = .7) +
  scale_fill_manual(values=c("#A3b5c0","#e5e0d8"), labels=c("No", "Yes"))+
  labs(fill="Loan Status")+
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

Credit Hisory

Widoczna jest znacząca rĂłĆŒniaca. Tylko niecaƂe 10% osĂłb bez zdolnoƛci kredytowej, dostaje pozytywną odpowiedĆș w sprawie kredytu. Jeƛli ktoƛ ma historię kredytową jego szansa na pozytywne rozpatrzenie winosku wynosi ok 80%.

dane2 %>%
  dplyr::select(Credit_History,Loan_Status) %>%
  filter(Credit_History==c("1", "0")) %>%
  group_by(Credit_History) %>%
  count(Loan_Status) %>%
  ggplot(aes(x = Credit_History, y = n, fill = Loan_Status)) +
  geom_col(position = "fill",colour = "gray20", alpha=0.7, width = .7) +
  scale_fill_manual(values=c("#A3b5c0","#e5e0d8"), labels=c("No", "Yes"))+
  labs(fill="Loan Status")+
  ggtitle("Credit History")+
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

Proparty Area

RĂłĆŒnice są niewielkie. Najwięcekszy odsetek osĂłb, ktĂłrym zostaje udzielony kredyt, pochodzą z obszarĂłw póƂmiejskich, zaƛ najmniejszy osoby z obszarĂłw wiejskich.

dane2 %>%
  dplyr::select(Property_Area,Loan_Status) %>%
  group_by(Property_Area) %>%
  count(Loan_Status) %>%
  ggplot(aes(x = Property_Area, y = n, fill = Loan_Status)) +
  geom_col(position = "fill",colour = "gray20", alpha=0.7, width = .7) +
  scale_fill_manual(values=c("#A3b5c0","#e5e0d8"), labels=c("No", "Yes"))+
  labs(fill="Loan Status")+
  ggtitle("Property Area")+
  theme_minimal()+
  theme(plot.title = element_text(hjust = 0.5))

Zmienna Iloƛciowe

Aplicant Income

Wysokoƛć dochodu nie wpƂywa w istotnym stopniu na decyzję o tym czy kredyt zostanie przyznany. Dochód osoby aplikującej ksztaƂtuje się podobnie zarówno dla decyzji pozytywnych jak i negatywnych.

ggplot(dane2, aes(x=Applicant_Income, fill=Loan_Status)) +
    geom_histogram( color="gray20",alpha=.5, position = 'identity') +
  scale_fill_manual(values=c("#A3b5c0","#e5e0d8"), labels=c("No", "Yes"))+
  labs(fill="Loan Status")+
    theme_minimal()+
    theme(text=element_text(size = 12), plot.title = element_text(hjust = 0.5))

Coaplicant Income

Sytuacja prezentuje się w ten sam sposób dla zmiennej Coapplicant Income.

ggplot(dane2, aes(x=Coapplicant_Income, fill=Loan_Status)) +
    geom_histogram( color="gray20", alpha=0.5, position = 'identity') +
  scale_fill_manual(values=c("#A3b5c0","#e5e0d8"), labels=c("No", "Yes"))+
  labs(fill="Loan Status")+
    theme_minimal()+
    theme(text=element_text(size = 12), plot.title = element_text(hjust = 0.5))

Loan Amount Term

Najwięcej kredytów jest branych na okres roku. Czas nie wpƂywa w stopniu istotnym na decyzję o udzieleniu kredytu.

ggplot(dane2, aes(x=Loan_Amount_Term, fill=Loan_Status)) +
    geom_histogram( color="gray20", alpha=0.5, position = 'identity') +
  scale_fill_manual(values=c("#A3b5c0","#e5e0d8"), labels=c("No", "Yes"))+
  labs(fill="Loan Status")+
    theme_minimal()+
    theme(text=element_text(size = 12), plot.title = element_text(hjust = 0.5))

Loan_Amount

Wysokoƛć poĆŒyczki nie jest czynnikiem decydującym o tym czy kredyt zostanie przyznany. Proporcje rozkƂadają się w podobny sposĂłb.

ggplot(dane2, aes(x=Loan_Amount, fill=Loan_Status)) +
    geom_histogram( color="gray20", alpha=0.5, position = 'identity') +
  scale_fill_manual(values=c("#A3b5c0","#e5e0d8"), labels=c("No", "Yes"))+
  labs(fill="Loan Status")+
  xlab("Loan Status")+
    theme_minimal()+
    theme(text=element_text(size = 12), plot.title = element_text(hjust = 0.5))

Analiza wielowymiarowa

Widzimy, ĆŒe osoby bez historii kredytowej praktycznie nie mają szansy aby dostaną kredytu w naszym banku (wyk1). Jeƛli, klient ma historię kredytową (wyk2) i kwota na jaką chce zaciągnąć kredyt nie przekracza 0,04 ze stosunku Loan_Amount/(Applicant_Income+Coapplicant_Income) to ma duĆŒe szansę na pozytyną odpowiedĆș. Jeƛli stosunek wykracza ponad 0.04, jest zdyskwalifikowany z moĆŒliwoƛci zaciągnięcia kredytu, nawet jeƛli posiada historię kredytową.

dane2 %>%
  drop_na(Credit_History) %>%
  ggplot( aes(x=Loan_Amount/(Applicant_Income+Coapplicant_Income), fill=Loan_Status)) +
    geom_histogram(color="gray20", alpha=0.5, position = 'identity') +
  scale_fill_manual(values=c("#A3b5c0","#e5e0d8"), labels=c("No", "Yes"))+
  labs(fill="Loan Status")+
    facet_grid(~Credit_History)+
    theme_minimal()+
    theme(text=element_text(size = 12), plot.title = element_text(hjust = 0.5))

Weryfikacja zdolnoƛci kredytowej klientów oczekujących

Sprawdzamy powiązanie pomiędzy zmienną objaƛnianą, a zmiennymi objaƛniającymi. Tak naprawdę istotna statystycznie jest jedynie zmienna Credit_History. Model logitowy w tym przypadku byƂby nieefetywny. NaleĆŒy więc znaleĆșć inne rozwiązanie, w oparciu o ktĂłre będą podejmowane decyzje kredytowe.

summary(glm(Loan_Status ~ Gender + Applicant_Income + Married + Dependents + Education + Self_Employed + Coapplicant_Income + Loan_Amount + Loan_Amount_Term + Credit_History + Property_Area , data = dane2, family = "binomial")) 
## 
## Call:
## glm(formula = Loan_Status ~ Gender + Applicant_Income + Married + 
##     Dependents + Education + Self_Employed + Coapplicant_Income + 
##     Loan_Amount + Loan_Amount_Term + Credit_History + Property_Area, 
##     family = "binomial", data = dane2)
## 
## Coefficients:
##                          Estimate Std. Error z value           Pr(>|z|)    
## (Intercept)            -3.5586101  1.0396236   -3.42            0.00062 ***
## GenderMale              0.6389214  0.3555258    1.80            0.07232 .  
## Applicant_Income        0.0000830  0.0000964    0.86            0.38928    
## MarriedYes              0.3037559  0.3282168    0.93            0.35472    
## Dependents1            -0.2707238  0.3802473   -0.71            0.47648    
## Dependents2             0.1662815  0.4096429    0.41            0.68480    
## Dependents3+            0.4637033  0.5539328    0.84            0.40253    
## EducationNot Graduate  -0.4626491  0.3203789   -1.44            0.14872    
## Self_EmployedYes       -0.4417265  0.4054680   -1.09            0.27597    
## Coapplicant_Income      0.0001620  0.0001238    1.31            0.19089    
## Loan_Amount            -0.0049203  0.0038161   -1.29            0.19728    
## Loan_Amount_Term        0.0005754  0.0020558    0.28            0.77957    
## Credit_History1         3.9666548  0.5058218    7.84 0.0000000000000044 ***
## Property_AreaSemiurban  0.9594574  0.3341797    2.87            0.00409 ** 
## Property_AreaUrban      0.0666799  0.3306927    0.20            0.84020    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 518.38  on 424  degrees of freedom
## Residual deviance: 368.35  on 410  degrees of freedom
##   (93 obserwacje zostaƂy skasowane z uwagi na braki w nich zawarte)
## AIC: 398.3
## 
## Number of Fisher Scoring iterations: 5

Jako dziaƂ usprawnieƄ procesĂłw decyzyjnych, w oparciu o przeprowadzoną analizę danych postanowiliƛmy, ĆŒe kredyty będą przyznawane jeƛli klient zdąbędzie okreƛloną iloƛć punktĂłw. MoĆŒemy jednoznacznie stwierdzić, ĆŒe dotychczasowe decyzje o przyznawaniu kredytĂłw byƂy randomowe, co negatywnie wpƂywaƂo na reputację i ekektywnoƛć dziaƂania naszego banku. Pozbędziemy się przypadkowowych decyzji. Dzięki opracowaniu modelu, wykluczymy przypadkowe decyzje kredytowe. Klientami naszego banku, będą klienci, ktĂłrzy będą w stanie regulować zobowiązanie.

prĂłba <- dane2 

Uzasadnienie punktacji:

  • Z wczeƛniejszej analizy wywnioskowaliƛmy, ĆŒe w oparciu o Credit_History, w gƂównej mierze wczeƛniej zostaƂy podejmowane decyzje o udzieleniu kredytu. Podejmując przyszƂe decyzje kredytowe historia kredytowa rĂłwnieĆŒ będzie znaczącym czynnikiem.

  • Najczęƛciej osoby z obszarĂłw podmiejskich otrzymywaƂy kredyty, rĂłwnieĆŒ tę zmienną postanowiliƛmy uwzględnić

  • Osoby w związku maĆ‚ĆŒeƄskim otrzymają dodatkowe 0.35 pkt. Są to osoby ustabilizowane.

  • Jako najwaĆŒniejszy skƂadnik w ocenie punktowej potraktujemy stosunek poĆŒyczki do caƂkowitych dochodĂłw.

  • Punkty za przychĂłd aplikanta, będą przyznawane zgodnie z wczeƛniej poznym rozkƂadem, tzn. 0 pkt jeƛli dochody są mniejsze niĆŒ Q1, 0.8 jeƛli dochody są mniejsze niĆŒ mediana, 1.6 jeƛli są większe niĆŒ mediana i mniejsze niĆŒ Q3, i 1.6 pkt jeƛli są większe niĆŒ Q3

  • Liczba osĂłb na utrzymaniu jest obciÄ…ĆŒenieniem dla osoby spƂacającej kredyt, dlatego jeƛli nie ma nikogo na utrzymaniu otrzyma 1 pkt, jeƛli zaƛ ma 3+ osĂłb otrzyma 0 pkt.

  • Punkty za przychĂłd wspóƂwnioskodawcy, będą przyznawane na podstawie wczeƛniejszego pozanego rozkƂadu tzn. 0 pkt jeƛli dochody coapplicanta są mniejsze niĆŒ Q1, 068 jeƛli dochody są mniejsze niĆŒ mediana, 0.8 jeƛli są większe niĆŒ mediana i mniejsze niĆŒ Q3, i 1.6 pkt jeƛli są większe niĆŒ Q3.

Maksymalna liczba punktĂłw do zdobycia to 11.05.

prĂłba <- prĂłba %>%
  dplyr::  mutate(pktCH = case_when(
      Credit_History =="1" ~ 3.5,
      Credit_History != "1" ~ 0 ))


prĂłba <- prĂłba %>%
  dplyr::  mutate(pktSemi = case_when(
      Property_Area =="Semiurban" ~ 0.3,
      Property_Area != "Semiurban" ~ 0 ))

prĂłba <- prĂłba %>%
  dplyr::  mutate(pktGen = case_when(
      Gender =="Male" ~ 0.2,
      Gender != "Male" ~ 0 ))

prĂłba <- prĂłba %>%
  dplyr::  mutate(pktMar = case_when(
      Married =="Yes" ~ 0.35,
      Married != "Yes" ~ 0 ))

prĂłba <- prĂłba %>%
  mutate("SKZ"=Loan_Amount/(Applicant_Income+Coapplicant_Income)) %>%
  mutate(pktSKZ= case_when( SKZ > 0.00 & SKZ <0.03 ~ 3,
                            SKZ > 0.03 & SKZ <0.035 ~ 2.5,
                            SKZ > 0.35 ~ -8 ))

prĂłba <- prĂłba %>%
  dplyr:: mutate(pktINC= case_when( Applicant_Income >=  0   & Applicant_Income < 2764 ~ 0,
                                    Applicant_Income >= 2764 & Applicant_Income < 4104 ~ 0.8,
                                    Applicant_Income >= 4104 & Applicant_Income < 4931 ~ 1.6,
                                    Applicant_Income >= 4931 ~ 2))

prĂłba <- prĂłba %>%
  mutate(pktDEP= case_when( Dependents =="0" ~ 0.7,
                            Dependents =="1" ~ 0.4,
                            Dependents =="2" ~ 0.2,
                            Dependents =="3+" ~ 0))


prĂłba <- prĂłba %>%
  mutate(pktCOAPP= case_when( Coapplicant_Income == 0 ~ 0,
                              Coapplicant_Income > 0    & Coapplicant_Income < 1625 ~ 0.2,
                              Coapplicant_Income >= 1625 & Coapplicant_Income < 2079 ~ 0.4,
                              Coapplicant_Income >= 2079 & Coapplicant_Income < 2857 ~ 0.6,
                              Coapplicant_Income >= 2857 ~ 1))


prĂłba <- prĂłba %>%
 mutate(wynik = pktCH + pktSemi + pktGen + pktSKZ+ pktMar+ pktINC+ pktDEP+ pktCOAPP)

prĂłba  %>%
  dplyr:: select(wynik, Loan_Status) %>%
  group_by(Loan_Status) %>%
  drop_na() %>%
  dplyr::summarise(mean(wynik), min(wynik), max(wynik))  %>%
  formattable(align ="c")
Loan_Status mean(wynik) min(wynik) max(wynik)
N 7.15833 3.45 11.05
Y 8.78349 4.80 11.05
prĂłba <- prĂłba %>%
  transmute(Loan_ID, Gender, Applicant_Income , Married , Dependents , Education , Self_Employed , Coapplicant_Income , Loan_Amount , Loan_Amount_Term , Credit_History , Property_Area, Loan_Status, wynik)

prĂłba  %>%
  dplyr:: select(wynik, Loan_Status) %>%
  group_by(Loan_Status) %>%
  drop_na() %>%
  ggplot(aes(wynik, colour=Loan_Status, group=Loan_Status, fill=Loan_Status)) +
    geom_density(color="gray20", alpha=0.5, position = 'identity') +
    scale_fill_manual(values = c("#A3b5c0", "#e5e0d8")) +
    theme_minimal()+
    theme(text=element_text(size = 12), plot.title = element_text(hjust = 0.5))

Wyniki są bardzo ciekawe, poniewaĆŒ jedna częƛć decyzji o negatywnych, zostaƂa sƂusznie odrzucona (od 0 do 7 pkt), ale pĂłĆșniej widzimy, ĆŒe z jakiegoƛ powodu spora częƛć osĂłb, ktĂłra zdobyƂa znaczącą iloƛć punktĂłw, miaƂa niesƂusznie odrzucone wnioski (lub z nieznanych nam powodĂłw, wykraczających poza zakres dostępnych danych- np. te osoby mogƂy być karane, co na starcie skreƛla je z moĆŒliwoƛci otrzymania kredytu, jednak nie zostaƂy nam dostarczone tego typu informacje, przez co obniĆŒona jest efektywnoƛć naszej analizy). Jeƛli nasz dziaƂ uzyska informacje na ten temat, zaktualizuje obecnie przyjęty model podejmowania decyzji. Co do wnioskĂłw rozpatrzonych pozytywnie, nie ma nieƛcisƂoƛci. Widzimy, ĆŒe w tej grupie są tylko aplikanci, ktĂłrzy zdobyli powyĆŒej 7 pkt, i tym samym dostawali kredyt.



Decyzje o przyznaniu kredytu klientów oczekujących

ETAP 1

Weryfikacja, czy dane zostaƂy uzupeƂnione poprawnie, czy podstawowe zaƂoĆŒnienia są speƂnione. DwĂłch klientĂłw nie wykazuje ĆŒadnych dochodĂłw, co teĆŒ na wstępnie dyskwalifikuje ich z dalszej weryfikacji zdolnoƛci kredytowej

summary(confront(dane1, rules)) %>%
  formattable(align ="c")
name items passes fails nNA error warning expression
V01 367 367 0 0 FALSE FALSE Married != “Yes” | (Coapplicant_Income - 0 >= -0.00000001)
V02 367 367 0 0 FALSE FALSE Married %vin% c(“Yes”, “No”)
V03 367 356 0 11 FALSE FALSE Gender %vin% c(“Female”, “Male”)
V04 367 357 0 10 FALSE FALSE Dependents %vin% c(“0”, “1”, “2”, “3+”)
V05 367 367 0 0 FALSE FALSE Education %vin% c(“Graduate”, “Not Graduate”)
V06 367 344 0 23 FALSE FALSE Self_Employed %vin% c(“Yes”, “No”)
V07 367 365 2 0 FALSE FALSE Applicant_Income > 0
V08 367 367 0 0 FALSE FALSE Coapplicant_Income - 0 >= -0.00000001
V09 367 362 0 5 FALSE FALSE Loan_Amount > 0
V10 367 361 0 6 FALSE FALSE Loan_Amount_Term > 0
V11 367 338 0 29 FALSE FALSE Credit_History %vin% c(“1”, “0”)
V12 367 367 0 0 FALSE FALSE Property_Area %vin% c(“Urban”, “Semiurban”, “Rural”)

ETAP 2

Zastosowanie punktacji do weryfikacji czy klient ma zdolnoƛć kredytową

dane1 <- dane1 %>%
  dplyr::  mutate(pktCH = case_when(
      Credit_History =="1" ~ 3.5,
      Credit_History != "1" ~ 0 ))

dane1 <- dane1 %>%
  dplyr::  mutate(pktSemi = case_when(
      Property_Area =="Semiurban" ~ 0.3,
      Property_Area != "Semiurban" ~ 0 ))

dane1 <- dane1 %>%
  dplyr::  mutate(pktGen = case_when(
      Gender =="Male" ~ 0.2,
      Gender != "Male" ~ 0 ))

dane1 <- dane1 %>%
  dplyr::  mutate(pktMar = case_when(
      Married =="Yes" ~ 0.35,
      Married != "Yes" ~ 0 ))


dane1 <- dane1 %>%
  mutate("SKZ"=Loan_Amount/(Applicant_Income+Coapplicant_Income)) %>%
  mutate(pktSKZ= case_when( SKZ > 0.00 & SKZ <0.03 ~ 3,
                            SKZ > 0.03 & SKZ <0.035 ~ 2.5,
                            SKZ > 0.35 ~ -8 ))

dane1 <- dane1 %>%
  dplyr:: mutate(pktINC= case_when( Applicant_Income >=  0   & Applicant_Income < 2764 ~ 0,
                                    Applicant_Income >= 2764 & Applicant_Income < 4104 ~ 0.8,
                                    Applicant_Income >= 4104 & Applicant_Income < 4931 ~ 1.6,
                                    Applicant_Income >= 4931 ~ 2))

dane1 <- dane1 %>%
  mutate(pktDEP= case_when( Dependents =="0" ~ 0.7,
                            Dependents =="1" ~ 0.4,
                            Dependents =="2" ~ 0.2,
                            Dependents =="3+" ~ 0))

dane1 <- dane1 %>%
  mutate(pktCOAPP= case_when( Coapplicant_Income == 0 ~ 0,
                              Coapplicant_Income > 0    & Coapplicant_Income < 1625 ~ 0.2,
                              Coapplicant_Income >= 1625 & Coapplicant_Income < 2079 ~ 0.4,
                              Coapplicant_Income >= 2079 & Coapplicant_Income < 2857 ~ 0.6,
                              Coapplicant_Income >= 2857 ~ 1))


dane1 <- dane1 %>%
 mutate(wynik = pktCH + pktSemi + pktGen + pktSKZ+ pktMar+ pktINC+ pktDEP+ pktCOAPP)


dane1 <- dane1 %>%
  mutate(Loan_Status=case_when(wynik >=7.51 ~ "Y",
                               wynik < 7.51 ~ "N"))

dane1  %>%
  dplyr:: select(wynik, Loan_Status) %>%
  group_by(Loan_Status) %>%
  drop_na() %>%
  dplyr::summarise(mean(wynik), min(wynik), max(wynik)) %>%
  formattable(align ="c") 
Loan_Status mean(wynik) min(wynik) max(wynik)
N 5.83525 3.45 7.50
Y 9.03392 7.55 10.75
dane1 <- dane1 %>%
  transmute(Loan_ID, Gender, Applicant_Income , Married , Dependents , Education , Self_Employed , Coapplicant_Income , Loan_Amount , Loan_Amount_Term , Credit_History , Property_Area, Loan_Status, wynik)

dane1  %>%
  dplyr:: select(Loan_Status) %>%
  group_by(Loan_Status) %>%
  drop_na() %>%
  count(Loan_Status) %>%
  formattable(align ="c") 
Loan_Status n
N 61
Y 227

W oparciu o opracowaną punktację 61 wniosków zostaƂo odrzuconych, 227 zaakceptowanych. Dzięki opracowanej punktacji, decyzje o udzielaniu kredytów będą miaƂy uzasadnienie, skrócimy czas potrzebny do podjęcia decyzji oraz uporamy się z problemem przypadkowoƛci.


Wiskowanie statystyczne

PYTANIA BADAWCZE:
- Czy wysokoƛć kredytu zaleĆŒy od caƂkowitego dochodu (aplikanta i wspóƂaplikanta)? Sprawdzanie za pomocą ANOVA – testowanie istotnoƛci wspóƂczynnikĂłw regresji liniowej

mod <- stats::aov(Loan_Amount~ Applicant_Income + Coapplicant_Income, dane2)
ggcoefstats(mod)

Bardzo niski wspóƂczynnik pvalue ƛwiadczy o istotnoƛci parametrów. Im mniejsze wartoƛci Kryterium Informacyjnego Akaike’a (AIC) i Kryterium Informacyjnego Bayesa (BIC), tym „lepszy” jest model. Poziome proste pokazują przedziaƂy 95% obserwacji odpowiednio dla zmiennej Coapplicant Income i Applicant Income. Ponadto pokazana jest statystyka testu F i Chi dla wybranych ziennych.

Wnioski koƄcowe

W procesie analizy EDA okazaƂo się, ĆŒe wysokoƛć dochodĂłw praktycznie nie decyduje o zdolnoƛci kredytowej klientĂłw, toteĆŒ nasza hitpoteza zostaƂa odrzucona. W oparciu o przeprowadzoną analizę oraz uzasadnione przesƂanki opracowaliƛmy model, ktĂłry przyznaje punkty w oparciu o okreƛlone kryteria i na tej podstawie zostaje podjęta decyzja o udzieleniu kredytu. (Nasz model wymaga jednak dopracowania, szczegĂłlnie w konkekƛcie osĂłb, ktĂłre otrzymaƂy wysoką liczbę punktĂłw, a z nieznanych nam powodĂłw ich wnioski zostaƂy odrzucone. Po uwazględnieniu takich dodatkowych danych, wykraczających poza zakres dostępnych obecnie danych, model naleĆŒaƂoby uaktualnić, ĆŒeby staƂ się bardziej efektywny).

LS0tDQp0aXRsZTogPGNlbnRlcj4gPGZvbnQgY29sb3I9IiMwODA4MDgiPiBaQVRXSUVSRFpFTklFIEtSRURZVFUgTUlFU1pLQU5JT1dFR08gPC9mcm9udD4gPC9jZW50ZXI+DQpzdWJ0aXRsZTogPGNlbnRlcj4gUHJvamVrdCB6ZXNwb8WCb3d5IHogcHJ6ZWRtaW90dSBBbmFsaXphIERhbnljaCA8L2NlbnRlcj4NCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCmF1dGhvcjogIkdydXBhIEQ6ICBBbGljamEgS3ViYW4sICBNaWNoYcWCIEdyb3NzIg0Kb3V0cHV0OiANCiAgIHJtZGZvcm1hdHM6OnJvYm9ib29rOg0KICAgIHByaW1hcnk6ICIjRkZEQkRBIg0KICAgIHNlY29uZGFyeTogIiNGRkRCREEiDQogICAgaGlnaGxpZ2h0OiBrYXRlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIGdlb21ldHJ5OiBtYXJnaW49MC4yaW4NCmVkaXRvcl9vcHRpb25zOiANCiAgbWFya2Rvd246IA0KICAgIHdyYXA6IDcyDQotLS0NCg0KPGJyPiA8YnI+DQoNCiMgV3Byb3dhZHplbmllDQoNCiMjIyAqKktpbSBqZXN0ZcWbbXk/KioNCg0KPHAgc3R5bGU9InRleHQtYWxpZ246IGp1c3RpZnkiPg0KDQpKZXN0ZcWbbXkgZmlybWEsIHpham11asSFY8SFIHNpxJkgdWR6aWVsYW5pZW0ga3JlZHl0w7N3IG1pZXN6a2FuaW93eWNoLkR6aWHFgmFteSBuYSBvYnN6YXJhY2ggbWllanNraWNoLCBww7PFgm1pZWpza2ljaCBvcmF6IHdpZWpza2ljaC4gTmFzeiBrbGllbnQgbmFqcGllcncgdWJpZWdhIHNpxJkgbyBrcmVkeXQgbWllc3prYW5pb3d5LCBhIG5hc3TEmXBuaWUgd2VyeWZpa3VqZW15LCBjenkga3dhbGlmaWt1amUgc2nEmSBkbyBqZWdvIG90cnp5bWFuaWEuDQoNCjwvcD4NCg0KPGJyPg0KDQojIyMgKipOYXN6ZSB6YWRhbmllKioNCg0KPHAgc3R5bGU9InRleHQtYWxpZ246IGp1c3RpZnkiPg0KQ2hjZW15IHphdXRvbWF0eXpvd2HEhyBwcm9jZXMgcHJ6eXpuYXdhbmlhIGtyZWR5dMOzdyAodyBjemFzaWUgcnplY3p5d2lzdHltKSB3IG9wYXJjaXUgbyBkYW5lIGtsaWVudGEuIFcgcGllcndzenltIGV0YXBpZSBzdGFyYW5pYSBzacSZIG8ga3JlZHl0LCBrbGllbnQgd3lwZcWCbmlhIHduaW9zZWsgb25saW5lLCBqZcWbbGkgc3BlxYJuaSB3c3TEmXBuZSB3eW1hZ2FuaWEsIHVtYXdpYW15IHNpxJkgeiBuaW0gbmEgc3BvdGthbmllLCB3IGNlbHUgd2VyeWZpa2FjamkgZGFueWNoIGkgcm96bW93eSBvIGtyZWR5Y2llLiBaYWRhbmllbSBuYXN6ZWdvIGR6aWHFgnUgKEF1dG9tYXR5emFjamkgUHJvY2Vzw7N3IGkgVXNwcmF3bmllxYQpIGplc3QgemJ1ZG93YW5pZSBtb2RlbHUsIGt0w7NyeSB3IG9wYXJjaXUgbyBkYW5lIGFua2lldG93ZSwgemJhZGEgY3p5IG5hc3oga2xpZW50IG1hIHpkb2xub8WbY2kga3JlZHl0b3fEhS4gPC9wPg0KDQo8YnI+DQoNCiMjIyAqKkRhbmUgamFraW1pIGR5c3BvbnVqZW15KioNCg0KUG9zaWFkYW15IG5hc3TEmXB1asSFY2UgZGFuZSBuYSB0ZW1hdCBuYXN6eWNoIGtsaWVudMOzdzoNCg0KLSAgIHDFgmXEhywNCg0KLSAgIHN0YW4gY3l3aWxueSwNCg0KLSAgIHd5a3N6dGHFgmNlbmllLA0KDQotICAgbGljemLEmSBvc8OzYiBuYSB1dHJ6eW1hbml1LA0KDQotICAgZG9jaMOzZCwNCg0KLSAgIGt3b3TEmSBwb8W8eWN6a2ksDQoNCi0gICBoaXN0b3JpxJkga3JlZHl0b3fEhQ0KDQotICAgY3p5IGtyZWR5dCB6b3N0YcWCIHVkemllbG9ueSAoem1pZW5uYSBvYmphxZtuaWFuYSkNCg0KPGJyPg0KDQojIyMgKipDZWwqKg0KDQoqKlpiYWRhbmllIGpha2llIHdhcnVua2kgbXVzesSFIGJ5xIcgc3BlxYJuaW9uZSwgYWJ5IGtyZWR5dCB6b3N0YcWCIHVkemllbG9ueSoqDQoNCjxicj4NCg0KIyBaYWtyZXMgYW5hbGl6eQ0KDQoxLiAgT2tyZcWbbGVuaWUgcHJvYmxlbXUNCg0KMi4gIFBvc3Rhd2llbmllIGhpcG90ZXoNCg0KMy4gIFByenlnb3Rvd2FuaWUgc3lzdGVtdSBpIHphxYJhZG93YW5pZSBkYW55Y2gNCg0KNC4gIFpyb3p1bWllbmllIGRhbnljaA0KDQo1LiAgRURBDQoNCiAgICAtICAgUHJ6ZXByb3dhZHphbmllIGFuYWxpenkgamVkbm93eW1pYXJvd2VqLA0KDQogICAgLSAgIFByemVwcm93YWR6ZW5pZSBhbmFsaXp5IGR3dXd5bWlhcm93ZWoNCg0KPGJyPg0KDQojIDEuIE9rcmXFm2xlbmllIHByb2JsZW11DQoNCjxwIHN0eWxlPSJ0ZXh0LWFsaWduOiBqdXN0aWZ5Ij4NClcgb3BhcmNpdSBvIHplYnJhbmUgZGFuZSwgY2hjZW15IHVzcHJhd25pxIcgcHJvY2VzIHVkemllbGFuaWEga3JlZHl0w7N3LCBrdMOzcnkgb2JlY25pZSBqZXN0IG5pZWVmZWt0eXdueSBpIHBvY2jFgmFuaWEgd2nEmWtzem/Fm8SHIGN6YXN1IHByYWN5IG5hc3p5Y2gga29uc3VsdGFudMOzdywgemUgd3pnbMSZZHUgbmEgaW5keXdpZHVhbG5lIHJvenBhdHJ5d2FuaWUgc3ByYXd5IGthxbxkZWdvIHBvdGVuY2phbG5lZ28ga2xpZW50YSB6IG9zb2JuYS4gRHppxJlraSB3cHJvd2FkemVuaXUgTW9kZWxldSBUcnVzdHdvcnRoeSBBSSB3IHNrb3Jpbmd1IGtyZWR5dG93eW0sIHVzcHJhd25pbXkgcHJvY2VzIHdlcnlmaWthY2ppLCBXDQpjacSFZ3UgMyBtaW51dCBwb3RlbmNhbG55IGtsaWVudCwgYsSZZHppZSB3aWVkemlhxYIgY3p5IG1hIHpkb2xub8WbxIcga3JlZHl0b3fEhSwgYWJ5IHphY2nEhWduxIXEhyBrcmVkeXQgdyBuYXN6eW0gYmFua3UuDQo8L3A+DQoNCkR6acSZa2kgdHltIGR6aWHFgmFuaW9tIHVwb3JhbXkgc2nEmSB6IHByb2JsZW1hbWkgZmlybXkuDQoNCi0gICBTa3LDs2NpbXkgY3phcyBwb3RyemVibnkgbmEgd3N0xJlwbmUgc3R3aWVyZHplbmllIGN6eSBkYW55IGtsaWVudCBzcGXFgm5pYSB3c3plbGtpZSBuaWV6YsSZZG5lIHd5bWFnYW5pYSwgYWJ5IGRvc3RhxIcga3JlZHl0Lg0KDQotICAgTmFzemUgZGVjeXpqZSBixJlkxIUgcG9wYXJ0ZSBvIHdjemXFm25pZWpzemUgZGVjeXpqZSwgdHltIHNhbXltIGLEmWR6aWUgaXN0bmlhxYJvIG1uaWVqc3plIHJ5enlrbyBixYLEmWRuZWogb2Nlbnkga2llbG50YS4NCg0KLSAgIFN0YW5pZW15IHNpxJkgYmFyZHppZWogZWZla3R5d25pLCByemV0ZWxuaSBpIGtvbmt1cmVuY3lqbmkuDQoNCi0gICBVc3ByYXduaW15IHN5c3RlbSBwcnplcMWCeXd1IGluZm9ybWFjamkgaSBkemlhxYJhbmllIGZpcm15Lg0KDQotICAgT3NpxIVnbmllbXkgbGVwc3plIHd5bmlraSBvcmF6IGLEmWR6aWVteSBtb2dsaSB6cmVkdWtvd2HEhyBsaWN6YsSZIGV0YXTDs3cuDQoNCjxicj4NCg0KIyAyLiBQb3N0YXdpZW5pZSBoaXBvdGV6eQ0KDQoqKkfFgsOzd255bSBjenlubmlraWVtIGRlY3lkdWrEhWN5bSBvIHR5bSwgY3p5IGtyZWR5dCB6b3N0YW5pZSBwcnp5em5hbnkgamVzdCB3eXNva2/Fm8SHIGRvY2hvZMOzdyBhcGxpa2FudGEuKioNCg0KPGJyPg0KDQojIDMuIFByenlnb3Rvd2FuaWUgc3lzdGVtdSBpIHphxYJhZG93YW5pZSBkYW55Y2gNCg0KIyMgUHJ6eWdvdG93YW5pZSBzeXN0ZW11IGRvIHByYWN5IHogZGFueW1pDQoNClphxYJhZG93bmFuaWUgYmlibGlvdGVrLCB6IGt0w7NyeWNoIGLEmWR6aWVteSBrb3J6eXN0YcSHIHcgZGFsZWogY3rEmcWbY2kgcHJvamVrdHUuIERvc3Rvc293YW5pZSBnbG9iYWxueWNoIHVzdGF3aWXFhC4NCg0KYGBge3Igc2V0dXAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpvcHRpb25zKHNjaXBlbiA9IDk5OSwgZGlnaXRzPTYpIA0KaWYoIXJlcXVpcmUoJ0RNd1InKSkgaW5zdGFsbC5wYWNrYWdlcygiaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvc3JjL2NvbnRyaWIvQXJjaGl2ZS9ETXdSL0RNd1JfMC40LjEudGFyLmd6IiwgcmVwb3M9TlVMTCwgdHlwZT0ic291cmNlIiwgZGVwZW5kZW5jaWVzPVRSVUUpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocm1kZm9ybWF0cykNCmxpYnJhcnkodmFsaWRhdGUpDQpsaWJyYXJ5KHZhbGlkYXRldG9vbHMpDQpsaWJyYXJ5KGRjbW9kaWZ5KQ0KbGlicmFyeShlcnJvcmxvY2F0ZSkNCmxpYnJhcnkoZGVkdWN0aXZlKQ0KbGlicmFyeShWSU0pDQpsaWJyYXJ5KHNpbXB1dGF0aW9uKQ0KbGlicmFyeShsdW1iZXJqYWNrKQ0KbGlicmFyeShJU0xSKSANCmxpYnJhcnkoZGxvb2tyKQ0KbGlicmFyeSh4dHMpDQpsaWJyYXJ5KHF1YW50bW9kKQ0KbGlicmFyeShST0NSKQ0KbGlicmFyeShETXdSKQ0KbGlicmFyeShJbmZvcm1hdGlvbikNCmxpYnJhcnkoc2NvcmVjYXJkKQ0KbGlicmFyeShuYW5pYXIpDQpsaWJyYXJ5KHZpc2RhdCkNCmxpYnJhcnkoc2hhcGVSKQ0KbGlicmFyeShhcnNlbmFsKQ0KbGlicmFyeShlMTA3MSkNCmxpYnJhcnkoaGF2ZW4pDQpsaWJyYXJ5KHBhcGVSKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KbGlicmFyeShjbGFzc0ludCkNCmxpYnJhcnkocGFzdGVjcykNCmxpYnJhcnkoZGVzY3RhYmxlKQ0KbGlicmFyeShmcmVxdWVuY3kpDQpsaWJyYXJ5KGNvcnJwbG90KQ0KbGlicmFyeShnZ3B1YnIpDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmxpYnJhcnkod2VzYW5kZXJzb24pDQpsaWJyYXJ5KGNvd3Bsb3QpDQpsaWJyYXJ5KGhyYnJ0aGVtZXMpDQpsaWJyYXJ5KHNjYWxlcykNCmxpYnJhcnkoZm9ybWF0dGFibGUpDQpsaWJyYXJ5KCdNQVNTJykNCmxpYnJhcnkocmNvbXBhbmlvbikNCmxpYnJhcnkobHNyKQ0KbGlicmFyeSh2Y2QpDQpsaWJyYXJ5KERlc2NUb29scykNCmxpYnJhcnkod2FmZmxlKQ0KbGlicmFyeShjbGVhbnJtZCkNCmxpYnJhcnkodGhlbWF0aWMpDQpsaWJyYXJ5KHNoaW55KQ0KbGlicmFyeShzaG93dGV4dCkNCmxpYnJhcnkocmFnZykNCmxpYnJhcnkoZGlzdGlsbCkNCmxpYnJhcnkocm1kZm9ybWF0cykNCmxpYnJhcnkocHJldHR5ZG9jKQ0KbGlicmFyeShocmJydGhlbWVzKQ0KbGlicmFyeSh0aW50KQ0KbGlicmFyeShnYXBtaW5kZXIpDQpsaWJyYXJ5KHlhbWwpDQpsaWJyYXJ5KHJwYXJ0KQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHBhbmRlcikNCmxpYnJhcnkobW9kZWxyKQ0KbGlicmFyeShicm9vbSkgDQpsaWJyYXJ5KGNhcmV0KQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShST0NSKQ0KbGlicmFyeShwc3ljaCkNCmxpYnJhcnkocXdyYXBzMikNCmxpYnJhcnkoZ2dwdWJyKQ0KbGlicmFyeShyc3RhdGl4KQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KbGlicmFyeShkYXRhcml1bSkNCmxpYnJhcnkoImNvcnJlbGF0aW9uIikNCmxpYnJhcnkocG9seWNvcikNCmxpYnJhcnkoY29ycmdyYW0pDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShmbGV4dGFibGUpDQoNCg0KDQoNCg0KDQoNCmxpYnJhcnkoZ2dzdGF0c3Bsb3QpDQpsaWJyYXJ5KGRwbHlyKQ0KYGBgDQoNClphxYJhZG93YW5pZSBkYW55Y2gsIG5hIHBvZHN0YXdpZSBrdMOzcnljaCB6b3N0YW5pZSBwcnplcHJvd2Fkem9uYSBhbmFsaXphLiBTxIUgdG8gbmFzdMSZcHVqxIVjZSBwbGlraTogSGlwb3RlY3pueTEuY3N2LCBIaXBvdGVjem55Mi5jc3YsIGt0w7NyZSB6YXdpZXJhasSFIGRhbmUga2xpZW50w7N3LCB1YmllZ2FqxIVjeWNoIHNpxJkgbyBrcmVkeXQuDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZGFuZTEgPC0gcmVhZC5jc3YoIkhpcG90ZWN6bnkxLmNzdiIpDQpkYW5lMiA8LSByZWFkLmNzdigiSGlwb3RlY3pueTIuY3N2IikNCmBgYA0KDQo8YnI+DQoNCiMjIE9yZ2FuaXphY2phIHByYWN5IHogZGFueW1pICh3c3TEmXBuZSBjenlzemN6ZW5pZSBpIHByemVnbMSFZCBkYW55Y2gpDQoNClcgY2VsdSB1c3ByYXduaWVuaWEgcHJhY3ksIGRhbmUgYsSZZMSFIHByemVjaG93eXdhbmUgdyBmb3JtaWUgdGFiZWxpDQoidGFiYmxlIi4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpkYW5lMSA8LSBhcy50aWJibGUoZGFuZTEpDQpkYW5lMiA8LSBhcy50aWJibGUoZGFuZTIpDQpgYGANCg0KPHAgc3R5bGU9InRleHQtYWxpZ246IGp1c3RpZnkiPiAgRGFuZTEgeiBwbGlrdSBIaXBvdGVjem55MS5jc3YsIHcgcHJ6ZWNpd2llxYRzdHdpZSBkbyBkYW55Y2gyIHogcGxpa3UgSGlwb3RlY3pueTIuY3N2IG5pZSB6YXdpZXJhasSFIGluZm9ybWFjamksIGN6eSBrcmVkeXQgem9zdGHFgiBwcnp5em5hbnkgKExvYW5fU3RhdHVzKS4gVyB6d2nEhXprdSB6IHR5bSBkYW5lMiB3eWtvcnp5c3RhbXkgZG8gcHJ6ZXByb3dhZHplbmlhIGFuYWxpenksIHNwcmF3ZHppbXkgY28gZGVjeWR1amUgbyB6ZG9sbm/Fm2NpIGtyZWR5dG93ZWoga2xpZW50w7N3LiBaYcWbIGRhbmUxIHBvdHJha3R1amVteSBqYWtvIHduaW9za2kgbmFzenljaCBrbGllbnTDs3cgY3pla2FqxIVjeWNoIG5hIG9kcG93aWVkxbogaSB3IG9wYXJjaXUgbyBwcnplcHJvd2Fkem9uxIUgYW5hbGl6xJkgc3R3aWVyZHppZW15LCBrdMOzcnp5IHogbmljaCBkb3N0YW7EhSBwb3p5dHl3bsSFIG9kcG93aWVkxbogbmEgd25pb3NlayBrcmVkeXRvd3kuIDwvcD4NCg0KIyMgZGFuZSB7LnRhYnNldH0NCg0KIyMjIGRhbmUxDQoNCmBgYHtyLCByZXN1bHRzPSJhc2lzIixmaWcuYWxpZ249ImNlbnRlciIsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTZ9DQpmb3JtYXR0YWJsZShoZWFkKGRhbmUxLDcpLCBhbGlnbiA9ImMiKQ0KYGBgDQoNCiMjIyBkYW5lMg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY29sbGFwc2UgPSBUUlVFfQ0KDQpmb3JtYXR0YWJsZShoZWFkKGRhbmUyLDcpLCBhbGlnbiA9ImMiKQ0KYGBgDQoNCk5hIHNhbXltIHBvY3rEhXRrdSBwb3J6xIVka3VqZW15IGRhbmUgcm9zbsSFY28gd2VkxYJ1ZyAiTG9hbl9JRCINCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojZGFuZTINCmRhbmUyJExvYW5fSUQgPC0gc3RyX3JlcGxhY2UoZGFuZTIkTG9hbl9JRCwiTFAiLCJMUCAiKQ0KICANCmRhbmUyIDwtIHNlcGFyYXRlKGRhbmUyLCBjb2wgPSAiTG9hbl9JRCIsIGMoIkxvYW4iLCJJRCIpLHNlcCA9ICIgIikNCg0KZGFuZTIkSUQgPC0gYXMubnVtZXJpYyhkYW5lMiRJRCkNCmRhbmUyIDwtIGRhbmUyICU+JSANCiAgYXJyYW5nZShkZXNjKCJMb2FuX0lEIikpDQoNCmRhbmUyIDwtIHVuaXRlKGRhbmUyLCJMb2FuX0lEIiwgIkxvYW4iLCAiSUQiLCBzZXA9IiAiKQ0KZGFuZTIkTG9hbl9JRCA8LSBzdHJfcmVwbGFjZShkYW5lMiRMb2FuX0lELCJMUCAiLCJMUCIpDQoNCg0KDQojZGFuZTENCmRhbmUxJExvYW5fSUQgPC0gc3RyX3JlcGxhY2UoZGFuZTEkTG9hbl9JRCwiTFAiLCJMUCAiKQ0KICANCmRhbmUxIDwtIHNlcGFyYXRlKGRhbmUxLCBjb2wgPSAiTG9hbl9JRCIsIGMoIkxvYW4iLCJJRCIpLHNlcCA9ICIgIikNCg0KZGFuZTEkSUQgPC0gYXMubnVtZXJpYyhkYW5lMSRJRCkNCmRhbmUxIDwtIGRhbmUxICU+JSANCiAgYXJyYW5nZShkZXNjKCJMb2FuX0lEIikpDQoNCg0KZGFuZTEgPC0gdW5pdGUoZGFuZTEsIkxvYW5fSUQiLCAiTG9hbiIsICJJRCIsIHNlcD0iICIpDQpkYW5lMSRMb2FuX0lEIDwtIHN0cl9yZXBsYWNlKGRhbmUxJExvYW5fSUQsIkxQICIsIkxQIikNCg0KYGBgDQoNCk5hc3TEmXBuaWUgemFtaWVuaWFteSBwdXN0ZSBtaWVqc2NhIG5hIE5BDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KI2RhbmUyDQpkYW5lMltkYW5lMiA9PSAnJ10gPC0gTkENCg0KI2RhbmUxDQpkYW5lMVtkYW5lMSA9PSAnJ10gPC0gTkENCg0KYGBgDQoNCktvbGVqbm8gem1pZW5pYW15IGtsYXPEmSBkYW55Y2gsIGFieSDFgmF0d2llaiBzacSZIHogbmltaSBwcmFjb3dhxYJvLg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNkYW5lMQ0KZGFuZTEkQXBwbGljYW50SW5jb21lIDwtIGFzLm51bWVyaWMoZGFuZTEkQXBwbGljYW50SW5jb21lKQ0KZGFuZTEkQ29hcHBsaWNhbnRJbmNvbWUgPC0gYXMubnVtZXJpYyhkYW5lMSRDb2FwcGxpY2FudEluY29tZSkNCmRhbmUxJERlcGVuZGVudHMgPC0gYXMuZmFjdG9yKGRhbmUxJERlcGVuZGVudHMpDQpkYW5lMSRQcm9wZXJ0eV9BcmVhIDwtIGFzLmZhY3RvcihkYW5lMSRQcm9wZXJ0eV9BcmVhKQ0KZGFuZTEkR2VuZGVyIDwtIGFzLmZhY3RvcihkYW5lMSRHZW5kZXIpDQoNCiNkYW5lMg0KZGFuZTIkQXBwbGljYW50SW5jb21lIDwtIGFzLm51bWVyaWMoZGFuZTIkQXBwbGljYW50SW5jb21lKQ0KZGFuZTIkQ29hcHBsaWNhbnRJbmNvbWUgPC0gYXMubnVtZXJpYyhkYW5lMiRDb2FwcGxpY2FudEluY29tZSkNCmRhbmUyJERlcGVuZGVudHMgPC0gYXMuZmFjdG9yKGRhbmUyJERlcGVuZGVudHMpDQpkYW5lMiRQcm9wZXJ0eV9BcmVhIDwtIGFzLmZhY3RvcihkYW5lMiRQcm9wZXJ0eV9BcmVhKQ0KZGFuZTIkR2VuZGVyIDwtIGFzLmZhY3RvcihkYW5lMiRHZW5kZXIpDQpkYW5lMiRMb2FuX1N0YXR1cyA8LSBhcy5mYWN0b3IoZGFuZTIkTG9hbl9TdGF0dXMpDQoNCmBgYA0KDQpXaWR6aW15LCDFvGUgdyBuaWVrdMOzcnljaCBuYXphY2ggem1pZW5ueWNoIGJyYWt1amUgIiAiLCB3IGNlbHUgbGVwc3plZ28gd3lnbMSFZHUgZGFueWNoIGkgZWZla3R5d25pZWpzemUgcHJhY3ksIHBvcHJhd2lhbXkgbmF6d3kgdHljaCB6bWllbm55Y2gNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojZGFuZTENCmRhbmUxIDwtIGRhbmUxICU+JQ0KICByZW5hbWUoQXBwbGljYW50X0luY29tZT1BcHBsaWNhbnRJbmNvbWUpIA0KDQpkYW5lMSA8LSBkYW5lMSAlPiUNCiAgcmVuYW1lKENvYXBwbGljYW50X0luY29tZT1Db2FwcGxpY2FudEluY29tZSkgDQoNCmRhbmUxIDwtIGRhbmUxICU+JQ0KICByZW5hbWUoTG9hbl9BbW91bnQ9TG9hbkFtb3VudCkgDQoNCmRhbmUxIDwtIGRhbmUxICU+JQ0KICByZW5hbWUoTG9hbl9JRD1Mb2FuX0lEKSANCg0KI2RhbmUyDQpkYW5lMiA8LSBkYW5lMiAlPiUNCiAgcmVuYW1lKEFwcGxpY2FudF9JbmNvbWU9QXBwbGljYW50SW5jb21lKSANCg0KZGFuZTIgPC0gZGFuZTIgJT4lDQogIHJlbmFtZShDb2FwcGxpY2FudF9JbmNvbWU9Q29hcHBsaWNhbnRJbmNvbWUpIA0KDQpkYW5lMiA8LSBkYW5lMiAlPiUNCiAgcmVuYW1lKExvYW5fQW1vdW50PUxvYW5BbW91bnQpIA0KDQpkYW5lMiA8LSBkYW5lMiAlPiUNCiAgcmVuYW1lKExvYW5fSUQ9TG9hbl9JRCkgDQoNCmBgYA0KDQpEYW5lIHpvc3RhxYJ5IHpvcmdhbml6b3dhbmUuDQoNCjxicj4NCg0KIyMgV2FsaWRhY2phIGRhbnljaA0KDQpOYWxlxbx5IHNwcmF3ZHppxIcgcG9wcmF3bm/Fm8SHIGRhbnljaDIsIGRvIHRlZ28gY2VsdSBwcnplcHJvd2FkemlteSB3YWxpZGFjasSZIGRhbnljaC4gTmFzemUgZGFuZSBwb3dpbm55IHNwZcWCbmlhxIcgbmFzdMSZcHVqxIVjZSB3YXJ1bmtpOg0KDQotICAgSmXFm2xpIHduaW9za29kYXdjYSBqZXN0IHcgendpxIV6a3UgbWHFgsW8ZcWEc2tpbSwgdG8gd8Ozd2N6YXMgdHlsa28gd3RlZHkgd3Nww7PFgnduaW9za29kYWNhIG1vxbxlIHd5a2F6eXdhxIcgZG9jaG9keS4gKCJDb2FwcGxpY2FudCBJbmNvbWUiID49MCwgZ2R5ICJNYXJyaWVkIj0gVFJVRSkNCg0KLSAgIFN0YXR1cyBtYcWCxbxlxYRzdHdhIG1vxbxlIHByenlqbW93YcSHIGplZHluaWUgd2FydG/Fm2NpICJ0YWsiIGx1YiAibmllIg0KDQotICAgUMWCZcSHIHBvd2lubmEgcHJ6eWptb3dhxIcgd2FydG/Fm8SHICJrb2JpZXRhIiBsdWIgIm3EmcW8Y3p5em5hIg0KDQotICAgTGljemJhIG9zw7NiIG5hIHV0cnp5bWFuaXUgcHJ6eWptdWplIHdhcnRvxZtjaSAiMCIsICIxIiwgIjIiLCAiMysiDQoNCi0gICBFZHVrYWNqYSB3YXJ0b8WbxIcgInBvIHN0dWRpYWNoIiwgIm5pZSB1a3VvxYRjennFgiBzdHVkacOzdyIuDQoNCi0gICBQcnp5Y2jDs2QgYXBsaWthbnRhIG11c2kgYnnEhyB3acSZa3N6eSBvZCAwLCBwb25pZXdhxbwgdyBwcnplY2l3bnltIHJhemllIG5pZSBwb3dpbmllbiBvbiB1YmllZ2HEhyBzacSZIG8ga3JlZHkgKG5pZSBixJlkemllIG1pYcWCIGphayBnbyBzcMWCYWNpxIcpLg0KDQotICAgUHJ6eWNow7NkIHdzcMOzxYJ3bmlvc2tvZGFjeSBtb8W8ZSBwcnp5bW93YcSHIHdhcnRvxZvEhyAwIHcgcHJ6eXBhZGt1LCBnZHkgd3Nww7PFgnduaW9za29kYXdjYSBuaWUgd3lrYXp1amUgxbxhZG55Y2ggZG9jaG9kw7N3IGx1YiBhcGxpa2FudCBqZXN0IHNhbS4gV2nEmWtzemUgdyBwcnp5cGFka3UgZ2R5IHdzcMOzxYJ3bmlvc2tvZGFjYSB3eWthenVqZSBqYWtpZcWbIGRvY2hvZHkuDQoNCi0gICBLd290YSBrcmVkeXR1IG9yYXogdGVybWluIG11c3rEhSBiecSHIHdpxJlrc3plIG9kIDAsIMW8ZWJ5IHphaXN0bmlhxYJvIHpkYXJ6ZW5pZS4NCg0KLSAgIEhpc3RvcmlhIGtyZWR5dG93YSBwb3dpbm5hIHByenlqbW93YcSHIHdhcnRvxZvEhyAiMSIgamXFm2xpIGFwbGlrYW50IGJyYcWCIGp1xbwgd2N6ZcWbbmllaiBrcmVkeXQsIGEgIjAiIGplxZtsaSBqZXN6Y3plIG5pZS4NCg0KLSAgIEFwbGlrYW50IG1vxbxlIHBvY2hvZHppxIcgeiBtaWFzdGEsIHplIHdzaSBsdWIgeiB0ZXJlbsOzdyBwb2RtaWVqc2tpY2guDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KcnVsZXMgPC0gdmFsaWRhdG9yKGlmKE1hcnJpZWQ9PSJZZXMiKSBDb2FwcGxpY2FudF9JbmNvbWUgPj0gMCwgDQogICAgICAgICAgICAgICAgICAgTWFycmllZCAlaW4lIGMoIlllcyIsICJObyIpLA0KICAgICAgICAgICAgICAgICAgIEdlbmRlciAlaW4lIGMoIkZlbWFsZSIsICJNYWxlIiksIA0KICAgICAgICAgICAgICAgICAgIERlcGVuZGVudHMgJWluJSBjKCIwIiwiMSIsIjIiLCIzKyIpLA0KICAgICAgICAgICAgICAgICAgIEVkdWNhdGlvbiAlaW4lIGMoIkdyYWR1YXRlIiwiTm90IEdyYWR1YXRlIiksDQogICAgICAgICAgICAgICAgICAgU2VsZl9FbXBsb3llZCAlaW4lIGMoIlllcyIsIk5vIiksIA0KICAgICAgICAgICAgICAgICAgIEFwcGxpY2FudF9JbmNvbWUgPiAwLA0KICAgICAgICAgICAgICAgICAgIENvYXBwbGljYW50X0luY29tZSA+PSAwLA0KICAgICAgICAgICAgICAgICAgIExvYW5fQW1vdW50PjAsIA0KICAgICAgICAgICAgICAgICAgIExvYW5fQW1vdW50X1Rlcm0+MCwgDQogICAgICAgICAgICAgICAgICAgQ3JlZGl0X0hpc3RvcnkgJWluJSBjKCIxIiwgIjAiKSwgDQogICAgICAgICAgICAgICAgICAgUHJvcGVydHlfQXJlYSAlaW4lIGMoIlVyYmFuIiwgIlNlbWl1cmJhbiIsICJSdXJhbCIpKQ0KDQojZGFuZTINCnN1bW1hcnkoY29uZnJvbnQoZGFuZTIsIHJ1bGVzKSkgJT4lDQogIGZvcm1hdHRhYmxlKGFsaWduID0iYyIpDQpgYGANCg0KKipBbmFsaXphIHdhbGlkYWNqaToqKiBOYXN6ZSBkYW5lIG5pZSB6YXdpZXJhasSFIGLFgsSZZMOzdyBsb2dpY3pueWNoLg0KDQo8YnI+DQoNCiMjIElkZW50eWZpa2FjamEgcm96a8WCYWR1IGRhbnljaCwgZWxpbWluYWNqYSBkYW55Y2ggb2RzdGFqxIVjeWNoDQoNCjxwIHN0eWxlPSJ0ZXh0LWFsaWduOiBqdXN0aWZ5Ij4gVyB0eW0gcG9kcm96ZHppYWxlIHByenlqcnp5bXkgc2nEmSB6bWllbm55bSBla3N0cmVtYWxueW0sIG9kc3RhasSFY3ltIGkgd3llbGltaW51amVteSBvYnNlcndhY2plIHdwxYJ5d293ZSwgxbxlYnkgbmllIHpha8WCw7NjYcWCeSBww7PFum5pZWpzemVqIGFuYWxpenkuIElkZW50eWZpa2FjamEgYsSZZHppZSBtaWHFgmEgemFzdG9zb3dhbmllIHcgcHJ6eXBhZGt1IHptaWVubnljaCBpbG/Fm2Npb3d5Y2g6IEFwcGxpY2FudF9JbmNvbWUsIENvYXBwbGljYW50X0luY29tZSwgTG9hbl9BbW91bnQgPC9wPg0KDQo8YnI+DQoNCiMjIyBEYW5lIG9kc3RhasSFY2UNCg0KPHAgc3R5bGU9InRleHQtYWxpZ246IGp1c3RpZnkiPiBQcnp5Z2zEhWRhbXkgc2nEmSByb3prxYJhZG93aSBkYW55Y2gsIHBvZCB3emdsxJlkZW0gb2JzZXJ3YWNqaSBvZHN0YWrEhWN5Y2ggaSB3aWR6aW15LCDFvGUgZGFuZSBtYWrEhSBvbmUgZHXFvHkgcm96c3TEmXAuIERsYSB3c3p5c3RraWNoIHptaWVubnljaCBpc3RuaWVqxIUgb2JzZXJ3YWNqZSBla3N0cmVtYWxuZSwga3TDs3JlIHpuaWVrc3p0YcWCY2FqxIUgcm96a8WCYWQgcHJhd29zdHJvbm5pZS4gPC9wPg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCg0KYm94MS4xIDwtIGRhbmUyICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBBcHBsaWNhbnRfSW5jb21lKSkgKw0KICBnZW9tX2JveHBsb3QoZmlsbD0nI0EzYjVjMCcsIGNvbG9yPSJncmF5MjAiLCBub3RjaCA9IFRSVUUsIGFscGhhPTAuNykgKw0KICBnZ3RpdGxlKCJBcGxpY2FudCBJbmNvbWUiKSsNCiAgeGxhYigiIikgKw0KICB0aGVtZV9taW5pbWFsKCkrDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KDQpib3gxLjIgPC0gZGFuZTIgJT4lDQogIGZpbHRlcihDb2FwcGxpY2FudF9JbmNvbWU+MCklPiUgI2N6xJnFm8SHIHdzcMOzxYJ3bmlvc2tvZGFjw7N3IG5pZSB3eWthenVqZSDFvGFkbnljaCBkb2Nob2TDs3csIGNvIHphYnVyemHFgm9ieSByb3prxYJhZCB6bWllbm55Y2gNCiAgZ2dwbG90KGFlcyh4ID0gQ29hcHBsaWNhbnRfSW5jb21lKSkgKw0KICBnZW9tX2JveHBsb3QoZmlsbD0nI0EzYjVjMCcsIGNvbG9yPSJncmF5MjAiLCBub3RjaCA9IFRSVUUsIGFscGhhPTAuNykgKw0KICBnZ3RpdGxlKCJDb2FwcGxpY2FudCBJbmNvbWUiKSsNCiAgeGxhYigiIikgKw0KICB0aGVtZV9taW5pbWFsKCkrDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KDQpib3gxLjMgPC0gZGFuZTIgJT4lDQogIGdncGxvdChhZXMoeCA9IExvYW5fQW1vdW50KSkgKw0KICBnZW9tX2JveHBsb3QoZmlsbD0nI0EzYjVjMCcsIGNvbG9yPSJncmF5MjAiLCBub3RjaCA9IFRSVUUsIGFscGhhPTAuNykgKw0KICBnZ3RpdGxlKCJMb2FuIEFtb3VudCIpKw0KICB4bGFiKCIiKSArDQogIHRoZW1lX21pbmltYWwoKSsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQoNCnBsb3RfZ3JpZChib3gxLjEsYm94MS4yLGJveDEuMywgbnJvdyA9IDMpDQoNCmBgYA0KDQo8YnI+DQoNCkRsYXRlZ28gbmFqcGllcncgdXN1d2FteSBvYnNlcndhY2plLCBrdMOzcmUgc8SFIGVrc3RyZW1hbG5lLg0KDQotICAgRGxhIHptaWVubmVqIEFwcGxpY2FudCBJbmNvbWUgcG93ecW8ZWogMjAxMDANCg0KLSAgIERsYSB6bWllbm5laiBDb2FwcGxpY2FudCBJbmNvbWUgcG93ecW8ZWogMTAwMDANCg0KLSAgIERsYSB6bWllbm5uZWogTG9hbiBBbW91bnQgcG93ecW8ZWogMzcwDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZGFuZTIgPC0gIGRhbmUyICU+JQ0KICAgICAgICAgZmlsdGVyKEFwcGxpY2FudF9JbmNvbWU8MjAxMDApDQoNCmRhbmUyIDwtICBkYW5lMiAlPiUNCiAgICAgICAgIGZpbHRlcihDb2FwcGxpY2FudF9JbmNvbWU8MTAwMDApDQoNCmRhbmUyIDwtICBkYW5lMiAlPiUNCiAgICAgICAgIGZpbHRlcihMb2FuX0Ftb3VudDwzNzApDQpgYGANCg0KPHAgc3R5bGU9InRleHQtYWxpZ246IGp1c3RpZnkiPiBOYXN0xJlwbmllIHBvem9zdGHFgmUgb2JzZXJ3YWNqZSB3eWVsaW1pbnVqZW15IG1ldG9kxIUga3dhcnR5bGkuIFcgdHltIGNlbHUgc3R3b3J6eW15IGZ1bmtjasSZLCBrcsOzcmEgdXN1bmllIG9ic2Vyd2FjamUsIGt0w7NyZSBwcnpla3JhY3phasSFIGR3dWtyb3Rub8WbxIcgcm96c3TEmXB1IHcgb2RuaWVzaWVuaXUgZG8gZG9sbmVqIGkgZ8Ozcm5laiBncmFuaWN5LiA8L3A+DQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZGV0ZWN0X291dGxpZXIgPC0gZnVuY3Rpb24oeCkgew0KICBuYS5vbWl0KHgpDQogIGxvd2VyX2JvdW5kIDwtIHF1YW50aWxlKHgsIDAuMjUpDQogIHVwcGVyX2JvdW5kIDwtIHF1YW50aWxlKHgsIDAuNzUpDQogIG5hLnJtID0gVFJVRQ0KICBJID0gdXBwZXJfYm91bmQgLSBsb3dlcl9ib3VuZA0KICB3aGljaCh4IDwgbG93ZXJfYm91bmQtSSoyIHwgeCA+IHVwcGVyX2JvdW5kK0kqMikNCn0NCg0KYGBgDQoNClphc3Rvc293YW5pZSBmdW5rY2ppLg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9DQojZGFuZTINCmQyLjEgPC0gZGV0ZWN0X291dGxpZXIoZGFuZTIkQXBwbGljYW50X0luY29tZSkNCmQyLjIgPC0gZGV0ZWN0X291dGxpZXIoZGFuZTIkQ29hcHBsaWNhbnRfSW5jb21lKQ0KZDIuMyA8LSBkZXRlY3Rfb3V0bGllcihuYS5vbWl0KGRhbmUyJExvYW5fQW1vdW50KSkNCmQyIDwtIGMoZDIuMSwgZDIuMiwgZDIuMykNCmQyDQoNCmRhbmUyIDwtIGRhbmUyWy1jKDMzLCAgNTIsICA2NCwgIDk5LCAxMDcsIDEyNywgMTMzLCAxMzUsIDE3NSwgMTgzLCAyMzcsIDI0MSwgMjU0LCAzMDIsIDM0NCwgMzkzLCA0MzIsIDQ0MCwgNDQzLCA0NDcsIDQ1NiwgNDcxLCA0OTIsIDQ5MywgNTI4LCA1NDksICAxMSwgIDM2LCAxMTQsIDEyNCwgMTY2LCAyMzYsIDQwOSwgIDIwLCAgMzMsICA1MiwgIDY0LCAxMjQsIDI0MSwgMjQzLCAzMDIsIDMyNiwgMzU0LCA0NzYsIDQ5NSwgNTQ3KSxdDQoNCmBgYA0KDQo8cCBzdHlsZT0idGV4dC1hbGlnbjoganVzdGlmeSI+IFJvemvFgmFkIHBvIHVzdW5pxJljaXUgb2JzZXJ3YWNqaSB6YWvFgsOzY2FqxIVjeWNoLCB6b3N0YcWCIHByemVkc3Rhd2lvbnkgcG9uacW8ZWouIER6acSZa2kgd3llbGltaW5vd2FuaXUgZGFueWNoIG9kc3RhasSFY3ljaCwgdXBvcmFsacWbbXkgc2nEmSB6IHByb2JsZW1lbSBvYnNlcndhY2ppIHpuaWVrc3p0YcWCY2FqxIVjeWNoIHJvemvFgmFkLiBQb25hZHRvIGR6acSZa2kgdGVtdSwgcMOzxbpuaWVqc3phIGFuYWxpemEgYsSZZHppZSBiYXJkemllaiByemV0ZWxuYS4gPC9wPg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmJveDMuMSA8LSBkYW5lMiAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gQXBwbGljYW50X0luY29tZSkpICsNCiAgZ2VvbV9ib3hwbG90KGZpbGw9JyNBM2I1YzAnLCBjb2xvcj0iZ3JheTIwIiwgbm90Y2ggPSBUUlVFLCBhbHBoYT0wLjcpICsNCiAgZ2d0aXRsZSgiQXBsaWNhbnQgSW5jb21lIikrDQogIHhsYWIoIiIpICsNCiAgdGhlbWVfbWluaW1hbCgpKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCg0KYm94My4yIDwtIGRhbmUyICU+JQ0KICBmaWx0ZXIoQ29hcHBsaWNhbnRfSW5jb21lPjApJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBDb2FwcGxpY2FudF9JbmNvbWUpKSArDQogIGdlb21fYm94cGxvdChmaWxsPScjQTNiNWMwJywgY29sb3I9ImdyYXkyMCIsIG5vdGNoID0gVFJVRSwgYWxwaGE9MC43KSArDQogIGdndGl0bGUoIkNvYXBwbGljYW50IEluY29tZSIpKw0KICB4bGFiKCIiKSArDQogIHRoZW1lX21pbmltYWwoKSsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQoNCmJveDMuMyA8LSBkYW5lMiAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gTG9hbl9BbW91bnQpKSArDQogIGdlb21fYm94cGxvdChmaWxsPScjQTNiNWMwJywgY29sb3I9ImdyYXkyMCIsIG5vdGNoID0gVFJVRSwgYWxwaGE9MC43KSArDQogIGdndGl0bGUoIkxvYW4gQW1vdW50IikrDQogIHhsYWIoIiIpICsNCiAgdGhlbWVfbWluaW1hbCgpKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCg0KcGxvdF9ncmlkKGJveDMuMSxib3gzLjIsYm94My4zLCBucm93ID0gMykNCg0KYGBgDQoNCjxicj4NCg0KIyMgQnJha2kgdyBkYW55Y2ggey50YWJzZXR9DQoNCk1hbXkgMTA5IGJyYWt1asSFY3ljaCBvYnNlcndhY2ppLiBCcmFraSB3eXN0xJlwdWrEhSB3IDYvMTMgem1pZW5ueWNoLg0KDQotICAgTmFqd2nEmWNlaiBicmFrdWrEhWN5Y2ggb2JzZXJ3YWNqaSB6YXdpZXJhIHptaWVubmEgQ3JlZGl0X0hpc3RvcnkuIE5hc3TEmXBuaWUgU2VsZl9FbXBsb3llZC4NCg0KLSAgIFJlc3p0YSBvYnNlcndhY2ppLCB3IGt0w7NyeWNoIHPEhSBicmFraTogR2VuZGVyLCBEZXBlbmRlbnRzLCBMb2FuX0Ftb3VudF9UZXJtDQoNCi0gICBKZXN0IGljaCB6YSBkdcW8bywgYWJ5IHVzdW7EhcSHLCBkbGF0ZWdvIG5hbGXFvHkgdXp1cGXFgm5pxIcgamUgdyBqYWsgbmFqYmFyZHppZWogcnpldGVsbnkgc3Bvc8OzYiwgemJsacW8b255IGRvIHJ6ZWN6eXdpc3RvxZtjaSBpIHVzdW7EhcSHIHR5bGtvIHRlIG9ic2Vyd2FjamksIHcga3TDs3J5Y2ggd3lzdMSZcHVqZSB3acSZY2VqIG5pxbwgamVkbm8gTkENCg0KPGJyPg0KDQojIyMgV2l6dWFsaXphY2phDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZ2dfbWlzc192YXIoZGFuZTIpICt0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQojIyMgVGFiZWxhIHByemVkc3Rhd2lhasSFY2EgYnJha2kNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpgSWxvxZvEhyBvYnNlcndhY2ppYCA8LSBjKHN1bShpcy5uYShkYW5lMiRNYXJyaWVkKSksc3VtKGlzLm5hKGRhbmUyJEdlbmRlcikpLCBzdW0oaXMubmEoZGFuZTIkRGVwZW5kZW50cykpLCBzdW0oaXMubmEoZGFuZTIkTG9hbl9BbW91bnRfVGVybSkpLCBzdW0oaXMubmEoZGFuZTIkU2VsZl9FbXBsb3llZCkpLCBzdW0oaXMubmEoZGFuZTIkQ3JlZGl0X0hpc3RvcnkpKSkNCmBNaWVqc2NlIHd5c3TEhXBpZW5pYWAgPC0gYygiTWFycmllZCIsIkdlbmRlciIsIkRlcGVuZGVudHMiLCJMb2FuX0Ftb3VudF9UZXJtIiwiU2VsZl9FbXBsb3llZCIsICJDcmVkaXRfSGlzdG9yeSIpDQpicmFraTEgPC1kYXRhLmZyYW1lKGBNaWVqc2NlIHd5c3TEhXBpZW5pYWAsYElsb8WbxIcgb2JzZXJ3YWNqaWApDQpicmFraTEgJT4lIGZvcm1hdHRhYmxlKGFsaWduPSJjIikNCmBgYA0KDQojIyMgU3pjemVnw7PFgm93ZSBwcnplZHN0YXdpZW5pZSBicmFrw7N3DQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZGFuZTJbIWNvbXBsZXRlLmNhc2VzKGRhbmUyKSxdICU+JSANCiAgaGVhZCgyMCkgJT4lDQogIGZvcm1hdHRhYmxlKGFsaWduPSJjIikNCg0KYGBgDQoNCiMjICB7LnVubnVtYmVyZWR9DQoNCiMjIyBJbXB1dGFjamEgYnJha3VqxIVjeWNoIGRhbnljaA0KDQoqKlptaWVubmEgIkdlbmRlciIqKg0KDQpEYW7EhSAiR2VuZGVyIiB6b3N0YXdpbXkgeiBicmFrYW1pLCBwb25pZXdhxbwgdyBww7PFum5pZWpzemVqIGFuYWxpemplLCB0YSB6bWllbm5hIG5pZSBixJlkemllIG1pYcWCYSBpc3RvdG5lZ28gd3DFgnl3dSBuYSB3eW5payBhbmFsaXp5Lg0KDQo8YnI+DQoNCioqWm1pZW5uYSAiTWFycmllZCIqKg0KDQo8cCBzdHlsZT0idGV4dC1hbGlnbjoganVzdGlmeSI+IFBvZGN6YXMgcHJ6ZXByb3dhZHphbmlhIHdhbGlkYWNqaSwgdXN0YWxpbGnFm215LCDFvGUgdHlsa28gZGxhIG9zb2LDs3csIHcgendpxIV6a2FjaCBtYcWCxbxlxYRza2ljaCwgbW/FvGUgYnnEhyB3eWthemFueSBwcnp5Y2jDs2Qgd3Nww7PFgnduaW9za29kYXdjeS4gWmF0ZW0gbmFsZcW8eSBzcHJhd2R6acSHLCBjenkgZGxhIGJyYWt1asSFY3ljaCBvYnNlcndhY2ppLCB3YXJ0b8WbxIcgIkNvYXBwbGljYW50X0luY29tZSIgPjAsIGplxZtsaSB0YWsgb3NvYmEgamVzdCB3IHp3acSFemt1IG1hxYLFvGXFhHNraW0uIEplxZtsaSAiQ29hcHBsaWNhbnRfSW5jb21lIj0wLCBuaWUgd2llbXksIGN6eSBkYW5hIG9zb2JhIGplc3QgdyB6d2nEhXprdSBtYcWCxbxlxYRza2ltLCBwb3pvc3Rhd2lteSB0ZSBwb2xhIHB1c3RlLiA8L3A+DQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCndoaWNoKGlzLm5hKGRhbmUyJE1hcnJpZWQpKQ0KZGFuZTJbaXMubmEoZGFuZTIkTWFycmllZCksXSAlPiUgZm9ybWF0dGFibGUoYWxpZ249ImMiKQ0KYGBgDQoNCkplc3QgamVkZW4gYnJhaywga3TDs3J5IG1vxbxlbXkgdXp1cGXFgm5pxIcsIERydWfEhSBvYnNlcndhY2rEmSB6ZGVjeWRvd2FsacWbbXkgc2nEmSB1c3VuxIXEhywgcG9uaWV3YcW8LCB0ZSBvYnNlcndhY2plIG1hasSFIHdpxJljZWogbmnFvCBqZWRubyAiTkEiDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCmRhbmUyW2lzLm5hKGRhbmUyJE1hcnJpZWQpJiBkYW5lMiRDb2FwcGxpY2FudF9JbmNvbWUgPiAwLCJNYXJyaWVkIl0gPC0gIlllcyINCmRhbmUyW2lzLm5hKGRhbmUyJE1hcnJpZWQpLF0gJT4lIGZvcm1hdHRhYmxlKGFsaWduPSJjIikNCmRhbmUyIDwtIGRhbmUyWy0xOTUsXQ0Kd2hpY2goaXMubmEoZGFuZTIkTWFycmllZCkpDQpgYGANCg0KPGJyPg0KDQoqKlVzdXdhbmllIGJyYWvDs3csIGdkemllIHd5c3TEmXB1amUgd2nEmWNlaiBuacW8IGplZG5vIE5BKioNCg0KVXN1d2FteSwgdGUgb2JzZXJ3YWNqZSwgZGxhIGt0w7NyeWNoIG1hbXkgd2nEmWNlaiBuacW8IGJyYWsgdyBqZWRuZWogem1pZW5uZWoNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KI0NyZWRpdCBIaXN0b3J5DQp3aGljaChpcy5uYShkYW5lMiRDcmVkaXRfSGlzdG9yeSkgJiAoaXMubmEoZGFuZTIkRGVwZW5kZW50cykgfCBpcy5uYShkYW5lMiRHZW5kZXIpIHwgaXMubmEoZGFuZTIkRWR1Y2F0aW9uKSB8IGlzLm5hKGRhbmUyJFNlbGZfRW1wbG95ZWQpfCBpcy5uYShkYW5lMiRDb2FwcGxpY2FudF9JbmNvbWUpfGlzLm5hKGRhbmUyJExvYW5fQW1vdW50KXxpcy5uYShkYW5lMiRBcHBsaWNhbnRfSW5jb21lKXxpcy5uYShkYW5lMiRMb2FuX0Ftb3VudF9UZXJtKSkpDQoNCmRhbmUyIDwtIGRhbmUyWy1jKDIxLCAgMjcsIDIwMiwgMzU1LCAzOTcpLF0NCg0KI0RlcGVuZGVuZHMNCndoaWNoKGlzLm5hKGRhbmUyJERlcGVuZGVudHMpICYgKGlzLm5hKGRhbmUyJEdlbmRlcikgfCBpcy5uYShkYW5lMiRFZHVjYXRpb24pIHwgaXMubmEoZGFuZTIkU2VsZl9FbXBsb3llZCl8IGlzLm5hKGRhbmUyJENvYXBwbGljYW50X0luY29tZSl8aXMubmEoZGFuZTIkTG9hbl9BbW91bnQpfGlzLm5hKGRhbmUyJEFwcGxpY2FudF9JbmNvbWUpfGlzLm5hKGRhbmUyJExvYW5fQW1vdW50X1Rlcm0pKSkNCmRhbmUyIDwtIGRhbmUyWy1jKDI4NSksXQ0KDQpgYGANCg0KPGJyPg0KDQojIyBQb2RnbMSFZCBicmFrdWrEhWN5Y2ggb2JzZXJ3YWNqaSBwbyBkb2tvbmFuaXUgY3p5c3pjemVuaWEgZGFueWNoIHsudGFic2V0fQ0KDQpEemnEmWtpIGN6eXN6Y3plbml1IHBvemJ5bGnFm215IHNpxJkgYnJha8OzdyB3IHptaWVubmVqICJNYXJyaWVkIiBvcmF6IHpyZWR1a293YWxpxZtteSBsaWN6bsSZIHBvb3pvc3RhxYJ5Y2ggYnJha8Ozdw0KDQojIyMgV3lrcmVzIHogb2JzZXJ3YWNqYW1pIGJyYWt1asSFY3ltaQ0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmdnX21pc3NfdmFyKGRhbmUyKSArdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KIyMjIFRhYmVsYSB6IHBvZHN1bW93YW5pZW0gYnJha8Ozdw0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmBJbG/Fm8SHIG9ic2Vyd2FjamlgIDwtIGMoc3VtKGlzLm5hKGRhbmUyJE1hcnJpZWQpKSxzdW0oaXMubmEoZGFuZTIkR2VuZGVyKSksIHN1bShpcy5uYShkYW5lMiREZXBlbmRlbnRzKSksIHN1bShpcy5uYShkYW5lMiRMb2FuX0Ftb3VudF9UZXJtKSksIHN1bShpcy5uYShkYW5lMiRTZWxmX0VtcGxveWVkKSksIHN1bShpcy5uYShkYW5lMiRDcmVkaXRfSGlzdG9yeSkpKQ0KYE1pZWpzY2Ugd3lzdMSFcGllbmlhYCA8LSBjKCJNYXJyaWVkIiwiR2VuZGVyIiwiRGVwZW5kZW50cyIsIkxvYW5fQW1vdW50X1Rlcm0iLCJTZWxmX0VtcGxveWVkIiwgIkNyZWRpdF9IaXN0b3J5IikNCmJyYWtpMSA8LWRhdGEuZnJhbWUoYE1pZWpzY2Ugd3lzdMSFcGllbmlhYCxgSWxvxZvEhyBvYnNlcndhY2ppYCkNCmJyYWtpMSAlPiUgZm9ybWF0dGFibGUoYWxpZ249ImMiKQ0KYGBgDQoNCjxicj4gPGJyPg0KDQojIDQuIFpyb3p1bWllbmllIGRhbnljaA0KDQojIyBabWllbm5lIGpha2/Fm2Npb3dlIHsudGFic2V0IC50YWJzZXQtZmFkZX0NCg0KIyMjIExvYW4gU3RhdHVzDQoNCkxvYW4gU3RhdHVzIGplc3QgbmFzesSFIHptaWVubsSFIG9iamHFm25pYW7EhS4gV2lkemlteSwgxbxlIDY2JSB3bmlvc2vDs3cgem9zdGHFgm8gcm96cGF0cnpvbnljaCBwb3p5dHd5bmllLg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmRhbmUyICU+JQ0KICBkcGx5cjo6c2VsZWN0KExvYW5fU3RhdHVzKSAlPiUNCiAgZHJvcF9uYSgpICU+JQ0KICBncm91cF9ieShMb2FuX1N0YXR1cyklPiUNCiAgY291bnQoKSAlPiUNCiAgbXV0YXRlKGB1ZHppYcWCIHByb2NlbnRvd3lgPXBlcmNlbnQobi8oMTU1KzM5MykpKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gIiIsIHkgPSBuLCBmaWxsID0gTG9hbl9TdGF0dXMpKSsNCiAgZ2VvbV9jb2woY29sb3VyID0gImdyYXkyMCIsIGFscGhhPTAuNywgd2lkdGggPSAuMykgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjQTNiNWMwIiwgIiNlNWUwZDgiLCAiI2I0YmRiYSIsInRoaXN0bGUzIiksIGxhYmVscz1jKCJZZXMiLCJObyIpKSArIA0KICBnZ3RpdGxlKCJMb2FuIFN0YXR1cyIpKw0KICB4bGFiKCIiKSArDQogIGxhYnMoZmlsbD0iTG9hbiBTdGF0dXMiKSsNCiAgY29vcmRfZmxpcCgpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1gdWR6aWHFgiBwcm9jZW50b3d5YCksIHBvc2l0aW9uPXBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC40OSksIHZqdXN0PTEsIHNpemU9NCwgY29sb3I9ImdyYXkyMCIpKw0KICB0aGVtZV9taW5pbWFsKCkrDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KYGBgDQoNCiMjIyBHZW5kZXINCg0KS2xpZW50YW1pIG5hc3plZ28gYmFua3Ugc8SFIG5hamN6xJnFm2NpZWogbcSZxbxjennFum5pLiBTdGFub3dpxIUgb25pIHBvbmFkIDgwJSB3c3p5c3RraWNoIGtsaWVudMOzdw0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCg0KZGFuZTIgJT4lDQogIGRwbHlyOjpzZWxlY3QoR2VuZGVyKSAlPiUNCiAgZHJvcF9uYSgpICU+JQ0KICBncm91cF9ieShHZW5kZXIpJT4lDQogIGNvdW50KCkgJT4lDQogIG11dGF0ZShgdWR6aWHFgiBwcm9jZW50b3d5YD1wZXJjZW50KG4vKDk5KzQxMSkpKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gIiIsIHkgPSBuLCBmaWxsID0gR2VuZGVyKSkrDQogIGdlb21fY29sKGNvbG91ciA9ICJncmF5MjAiLCBhbHBoYT0wLjcsIHdpZHRoID0gLjMpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI0EzYjVjMCIsICIjZTVlMGQ4IiwgImdyYXkyMCIpKSArIA0KICBnZ3RpdGxlKCJHZW5kZXIiKSsNCiAgeGxhYigiIikgKw0KICBjb29yZF9mbGlwKCkgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsPWB1ZHppYcWCIHByb2NlbnRvd3lgKSwgcG9zaXRpb249cG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjQ5KSwgdmp1c3Q9MSwgc2l6ZT00LCBjb2xvcj0iZ3JheTIwIikrDQogIHRoZW1lX21pbmltYWwoKSsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQoNCg0KYGBgDQoNCiMjIyBNYXJyaWVkDQoNCk5hamN6xJnFm2NpZWogb3NvYnksIGt0w7NyZSB1YmllZ2FqxIUgc2nEmSBvIGtyZWR5dCBzxIUgdyB6d2nEhXprYWNoIG1hxYLFvGXFhHNraWNoLiBTxIUgdG8gb3NvYnkgdXN0YWJpbGl6b3dhbmUsIFN0YW5vd2nEhSBvbmkgcG9uYWQgNjQlIHdzenlzdGtpY2gga2xpZW50w7N3DQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZGFuZTIgJT4lDQogIGRwbHlyOjpzZWxlY3QoTWFycmllZCkgJT4lDQogIGRyb3BfbmEoKSAlPiUNCiAgZ3JvdXBfYnkoTWFycmllZCklPiUNCiAgY291bnQoKSAlPiUNCiAgbXV0YXRlKGB1ZHppYcWCIHByb2NlbnRvd3lgPXBlcmNlbnQobi8oMTgzKzMzNSkpKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gIiIsIHkgPSBuLCBmaWxsID0gTWFycmllZCkpKw0KICBnZW9tX2NvbChjb2xvdXIgPSAiZ3JheTIwIiwgYWxwaGE9MC43LCB3aWR0aCA9IC4zKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNBM2I1YzAiLCAiI2U1ZTBkOCIsICJncmF5MjAiKSkgKyANCiAgZ2d0aXRsZSgiTWFycmllZCIpKw0KICB4bGFiKCIiKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIGdlb21fdGV4dChhZXMobGFiZWw9YHVkemlhxYIgcHJvY2VudG93eWApLCBwb3NpdGlvbj1wb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNDkpLCB2anVzdD0xLCBzaXplPTQsIGNvbG9yPSJncmF5MjAiKSsNCiAgdGhlbWVfbWluaW1hbCgpKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCiAgDQpgYGANCg0KIyMjIERlcGVuZGVudHMNCg0KUHJhd2llIDYwJSBrbGllbnTDs3cgbmllIG1pYcWCYSBuaWtvZ28gbmEgdXRyenltYW5pdS4gMTUuNTglIHduaW9za3VqxIVjeWNoIG1pYcWCbyB0eWxrbyBqZWRuxIUgb3NvYsSZLCAxNy4zNiUgZHdpZSBvc29ieS4gTmFqbW5pZWogbGljem7EhSBncnVwxJkgc3Rhbm93aWxpIGtsaWVuY2ksIGt0w7NyenkgbWlhbGkgMy4gbHViIHdpxJljZWogb3PDs2IsIGt0w7NyZSBtdXNpZWxpIHV0cnp5bXl3YcSHLg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmRhbmUyICU+JQ0KICBkcGx5cjo6c2VsZWN0KERlcGVuZGVudHMpICU+JQ0KICBkcm9wX25hKCkgJT4lDQogIGdyb3VwX2J5KERlcGVuZGVudHMpJT4lDQogIGNvdW50KCkgJT4lDQogIG11dGF0ZShgdWR6aWHFgiBwcm9jZW50b3d5YD1wZXJjZW50KG4vKDMwMSs3OSs4OCszOSkpKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gIiIsIHkgPSBuLCBmaWxsID0gRGVwZW5kZW50cykpKw0KICBnZW9tX2NvbChjb2xvdXIgPSAiZ3JheTIwIiwgYWxwaGE9MC43LCB3aWR0aCA9IC4zKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNBM2I1YzAiLCAiI2U1ZTBkOCIsICIjYjRiZGJhIiwibWlzdHlyb3NlMyIpKSArIA0KICBnZ3RpdGxlKCJEZXBlbmRlbnRzIikrDQogIHhsYWIoIiIpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1gdWR6aWHFgiBwcm9jZW50b3d5YCksIHBvc2l0aW9uPXBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC40OSksIHZqdXN0PTEsIHNpemU9NCwgY29sb3I9ImdyYXkyMCIpKw0KICB0aGVtZV9taW5pbWFsKCkrDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KYGBgDQoNCiMjIyBFZHVjYXRpb24NCg0KV3lyYcW6bsSFIHdpxJlrc3pvxZvEhyB3xZtyw7NkIGFwbGlrYW50w7N3IHN0YW5vd2nFgnkgb3NvYnksIGt0w7NyZSB1a2/FhGN6ecWCeSBzdHVkaWEsIHN0YW5vd2lsaSBvbmkgNzYlIHdzenlzdGtpY2gga2xpZW50w7N3Lg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmRhbmUyICU+JQ0KICBkcGx5cjo6c2VsZWN0KEVkdWNhdGlvbikgJT4lDQogIGRyb3BfbmEoKSAlPiUNCiAgZ3JvdXBfYnkoRWR1Y2F0aW9uKSU+JQ0KICBjb3VudCgpICU+JQ0KICBtdXRhdGUoYHVkemlhxYIgcHJvY2VudG93eWA9cGVyY2VudChuLygzOTQrMTI0KSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSAiIiwgeSA9IG4sIGZpbGwgPSBFZHVjYXRpb24pKSsNCiAgZ2VvbV9jb2woY29sb3VyID0gImdyYXkyMCIsIGFscGhhPTAuNywgd2lkdGggPSAuMykgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjQTNiNWMwIiwgIiNlNWUwZDgiLCAiI2I0YmRiYSIsIm1pc3R5cm9zZTMiKSkgKyANCiAgZ2d0aXRsZSgiRWR1Y2F0aW9uIikrDQogIHhsYWIoIiIpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1gdWR6aWHFgiBwcm9jZW50b3d5YCksIHBvc2l0aW9uPXBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC40OSksIHZqdXN0PTEsIHNpemU9NCwgY29sb3I9ImdyYXkyMCIpKw0KICB0aGVtZV9taW5pbWFsKCkrDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KYGBgDQoNCiMjIyBTZWxmIEVtcGxveWVkDQoNCldpxJlrc3pvxZvEhyBvc8OzYiB3bmlvc2t1asSFY3ljaCBvIGtyZWR5dCB3IG5hc3p5bSBiYW5rdSwgbmllIHByb3dhZHppIHfFgmFzbmVnbyBiaXpuZXN1LiBPc29ieSBzYW1vemF0cnVkbmlvbmUgc3Rhbm93acSFIHphbGVkd2llIDExLDk0JSB3c3p5c3RraWNoIGtsaWVudMOzdy4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpkYW5lMiRDcmVkaXRfSGlzdG9yeSA8LSBhcy5mYWN0b3IoZGFuZTIkQ3JlZGl0X0hpc3RvcnkpDQoNCmRhbmUyICU+JQ0KICBkcGx5cjo6c2VsZWN0KFNlbGZfRW1wbG95ZWQpICU+JQ0KICBkcm9wX25hKCkgJT4lDQogIGdyb3VwX2J5KFNlbGZfRW1wbG95ZWQpJT4lDQogIGNvdW50KCkgJT4lDQogIG11dGF0ZShgdWR6aWHFgiBwcm9jZW50b3d5YD1wZXJjZW50KG4vKDQzNSs1OSkpKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gIiIsIHkgPSBuLCBmaWxsID0gU2VsZl9FbXBsb3llZCkpKw0KICBnZW9tX2NvbChjb2xvdXIgPSAiZ3JheTIwIiwgYWxwaGE9MC43LCB3aWR0aCA9IC4zKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNBM2I1YzAiLCAiI2U1ZTBkOCIsICIjYjRiZGJhIiwibWlzdHlyb3NlMyIpKSArIA0KICBnZ3RpdGxlKCJTZWxmIEVtcGxveWVkIikrDQogIHhsYWIoIiIpICsNCiAgbGFicyhmaWxsPSJTZWxmIEVtcGxveWVkIikrDQogIGNvb3JkX2ZsaXAoKSArDQogIGdlb21fdGV4dChhZXMobGFiZWw9YHVkemlhxYIgcHJvY2VudG93eWApLCBwb3NpdGlvbj1wb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNDkpLCB2anVzdD0xLCBzaXplPTQsIGNvbG9yPSJncmF5MjAiKSsNCiAgdGhlbWVfbWluaW1hbCgpKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCmBgYA0KDQojIyMgTG9hbiBBbW91bnQgVGVybSB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30NCg0KTmFqY3rEmcWbY2llaiB3eWJpZXJhbnltIG9rcmVzZW0sIG5hIGt0w7NyeSBrbGllbmNpIGNoY8SFIHphY2nEhWduxIXEhyBrcmVkeXQgamVzdCBva3JlcyAzNjAsIGt0w7NyeSBzdGFub3dpIHBvbmFkIDg1JSB3c3p5c3RraWNoIG9rcmVzw7N3IG5hIGpha2llIHphd2llcmFuYSBqZXN0IHBvxbx5Y3prYS4gRHJ1Z8SFIG5hamN6xJnFm2NpZWogd3liaWVyYW7EhSBvcGNqxIUgamVzdCAxODAgZG5pLCBwb25hZCA3JSBrbGllbnTDs3csIHd5YmllcmEgdMSZIG9wY2rEmS4NCg0KIyMjIyBUYWJlbGENCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpkYW5lMiAlPiUNCiAgZHBseXI6OnNlbGVjdChMb2FuX0Ftb3VudF9UZXJtKSAlPiUNCiAgZHJvcF9uYSgpICU+JQ0KICBncm91cF9ieShMb2FuX0Ftb3VudF9UZXJtKSU+JQ0KICBjb3VudCgpICU+JQ0KICBtdXRhdGUoYFVkemlhxYIgcHJvY2VudG93eWA9IHBlcmNlbnQobi81MDUpKSU+JSANCiAgZm9ybWF0dGFibGUoKQ0KYGBgDQoNCiMjIyMgUHJlemVudGFjamEgcm96a8WCYWR1DQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kd2FmZWwgPC0gZGFuZTIgJT4lDQogIGRwbHlyOjpzZWxlY3QoTG9hbl9BbW91bnRfVGVybSkgJT4lDQogIGRyb3BfbmEoKSAlPiUNCiAgZ3JvdXBfYnkoTG9hbl9BbW91bnRfVGVybSklPiUNCiAgY291bnQoKSANCndhZmVsIDwtIGMoYHRlcm0gMTItMTIwYD0gMTIsIGB0ZXJtIDgwYD0JMzYsIGB0ZXJtIDI0MGA9MywgYHRlcm0gMzAwYD0JMTAsIGB0ZXJtIDM2MGA9CTQzMSwgYHRlcm0gNDgwYD0gMTMpDQogIHdhZmZsZSh3YWZlbCwgcm93cz0xNSxjb2xvcnMgPSBjKCIjZWVlMmQ0IiwgIm1pc3R5cm9zZTMiLCAibGlnaHRzdGVlbGJsdWUyIiwiIzk1OTU5NSIsIiNjZWQwY2UiLCJwaW5rMyIpKQ0KICANCmBgYA0KDQojIyMgQ3JlZGl0IEhpc3RvcnkNCg0KUG9uYWQgODQlIGtsaWVudMOzdyBtYSBoaXN0b3JpxJkga3JlZHl0b3fEhS4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCmRhbmUyICU+JQ0KICBkcGx5cjo6c2VsZWN0KENyZWRpdF9IaXN0b3J5KSAlPiUNCiAgZHJvcF9uYSgpICU+JQ0KICBncm91cF9ieShDcmVkaXRfSGlzdG9yeSklPiUNCiAgY291bnQoKSAlPiUNCiAgbXV0YXRlKGB1ZHppYcWCIHByb2NlbnRvd3lgPXBlcmNlbnQobi8oNDA0Kzc2KSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSAiIiwgeSA9IG4sIGZpbGwgPSBDcmVkaXRfSGlzdG9yeSkpKw0KICBnZW9tX2NvbChjb2xvdXIgPSAiZ3JheTIwIiwgYWxwaGE9MC43LCB3aWR0aCA9IC4zKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNBM2I1YzAiLCAiI2U1ZTBkOCIsICIjYjRiZGJhIiwidGhpc3RsZTMiKSwgbGFiZWxzPWMoIk5vIiwiWWVzIikpICsgDQogIGdndGl0bGUoIkNyZWRpdCBIaXN0b3J5IikrDQogIHhsYWIoIiIpICsNCiAgbGFicyhmaWxsID0gIkNyZWRpdCBIaXN0b3J5IikrDQogIGNvb3JkX2ZsaXAoKSArDQogIGdlb21fdGV4dChhZXMobGFiZWw9YHVkemlhxYIgcHJvY2VudG93eWApLCBwb3NpdGlvbj1wb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNDkpLCB2anVzdD0xLCBzaXplPTQsIGNvbG9yPSJncmF5MjAiKSsNCiAgdGhlbWVfbWluaW1hbCgpKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCmBgYA0KDQojIyMgUHJvcGVydHkgQXJlYQ0KDQpQcm9wb3JjasSZIHBvbWnEmWR6eSBtaWVqc2NlbSB6YW1pZXN6a2FuaWEgYXBsaWthbnTDs3cgc8SFIHcgbWlhcsSZIHpibGnFvG9uxJkuIE5handpxJlrc3p5IHVkemlhxYIgc3Rhbm93acSFIGtsaWVuY2kgeiBvYnN6YXLDs3cgcG9kbWllanNraWNoICg0MSw0NiUpLiBOYXN0xJlwbmllIDMzLDc1JSBzdGFub3dpxIUgd25pb3Nrb2Rhd2N5IHogb2JzemFyw7N3IG1pZWpza2ljaCwgaSB6YWxlZHdpZSAxJSBtbmllanN6eSBvZCBuaWNoIHVkemlhxYIgbWFqxIUga2xpZW5jaSB6IG9ic3phcsOzdyB3aWVqc2tpY2guDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KDQpkYW5lMiAlPiUNCiAgZHBseXI6OnNlbGVjdChQcm9wZXJ0eV9BcmVhKSAlPiUNCiAgZHJvcF9uYSgpICU+JQ0KICBncm91cF9ieShQcm9wZXJ0eV9BcmVhKSU+JQ0KICBjb3VudCgpICU+JQ0KICBtdXRhdGUoYHVkemlhxYIgcHJvY2VudG93eWA9cGVyY2VudChuLyg0MDQrNzYpKSkgJT4lDQogIGdncGxvdChhZXMoeCA9ICIiLCB5ID0gbiwgZmlsbCA9IFByb3BlcnR5X0FyZWEpKSsNCiAgZ2VvbV9jb2woY29sb3VyID0gImdyYXkyMCIsIGFscGhhPTAuNywgd2lkdGggPSAuMykgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjQTNiNWMwIiwgIiNlNWUwZDgiLCJ0aGlzdGxlMyIpKSArIA0KICBnZ3RpdGxlKCJQcm9wZXJ0eSBBcmVhIikrDQogIHhsYWIoIiIpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgbGFicyhmaWxsPSJQcm9wZXJ0eSBBcmVhIikrDQogIGdlb21fdGV4dChhZXMobGFiZWw9YHVkemlhxYIgcHJvY2VudG93eWApLCBwb3NpdGlvbj1wb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNDkpLCB2anVzdD0xLCBzaXplPTQsIGNvbG9yPSJncmF5MjAiKSsNCiAgdGhlbWVfbWluaW1hbCgpKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCmBgYA0KDQojIyBabWllbm5lIGlsb8WbY2lvd2UNCg0KIyMjIEFwcGxpY2FudCBJbmNvbWUNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpkYW5lMiAlPiUNCiAgZHBseXI6OnNlbGVjdChBcHBsaWNhbnRfSW5jb21lKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gQXBwbGljYW50X0luY29tZSkpICsNCiAgZ2VvbV9kZW5zaXR5KGZpbGw9IiNBM2I1YzAiLCBhbHBoYT0wLjQpICsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oQXBwbGljYW50X0luY29tZSwgbmEucm0gPSBUKSksIA0KICAgICAgICAgICAgICAgY29sb3VyID0gInBpbmszIiwgbGluZXR5cGUgPSJsb25nZGFzaCIsIHNpemUgPSAuNykrDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWRpYW4oQXBwbGljYW50X0luY29tZSwgbmEucm0gPSBUKSksIA0KICAgICAgICAgICAgICAgY29sb3VyID0gInJveWFsYmx1ZTMiLCBsaW5ldHlwZSA9ImxvbmdkYXNoIiwgc2l6ZSA9IC43KSsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IHF1YW50aWxlKEFwcGxpY2FudF9JbmNvbWUsIHByb2JzID0gMC4yNSwgbmEucm0gPSBUKSksIA0KICAgICAgICAgICAgICAgY29sb3VyID0gImRhcmtzZWFncmVlbjQiLCBsaW5ldHlwZSA9ImxvbmdkYXNoIiwgc2l6ZSA9IC43KSsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IHF1YW50aWxlKEFwcGxpY2FudF9JbmNvbWUsIHByb2JzID0gMC43NSwgbmEucm0gPSBUKSksIA0KICAgICAgICAgICAgICAgY29sb3VyID0gImdvbGQzIiwgbGluZXR5cGUgPSJsb25nZGFzaCIsIHNpemUgPSAuNykrDQogIGdndGl0bGUoIkFwcGxpY2FudCBJbmNvbWUiKSsNCiAgeGxhYigiIikgKw0KICB0aGVtZV9taW5pbWFsKCkrDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KYGBgDQoNCioqT3puYWN6ZW5pYSBuYSB3eWtyZXNpZToqKiDFm3JlZG5pYS0gcsOzxbxvd3ksIG1lZGlhbmEtIG5pZWJpZXNraSwgUTEtemllbG9ueSwgUTMtxbzDs8WCdHkNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCnN0YXR5c3R5a2EgPC0gYygiTWluIiwgIk1heCIsICJRMSIsIk1lZGlhbmEiLCJRMyIgLCLFmnJlZG5pYSIsICJPZGNoLiBzdGQuIiwgIlNrb8Wbbm/Fm8SHIiwiS3VydG96YSIpDQp3YXJ0b3NjIDwtIGMobWluKGRhbmUyJEFwcGxpY2FudF9JbmNvbWUpLG1heChkYW5lMiRBcHBsaWNhbnRfSW5jb21lKSwgcXVhbnRpbGUoZGFuZTIkQXBwbGljYW50X0luY29tZSwgMC4yNSksIHJvdW5kKG1lZGlhbihkYW5lMiRBcHBsaWNhbnRfSW5jb21lLDAuNSksMiksIHF1YW50aWxlKGRhbmUyJEFwcGxpY2FudF9JbmNvbWUsIDAuNzUpLCByb3VuZChtZWFuKGRhbmUyJEFwcGxpY2FudF9JbmNvbWUpLDIpLCByb3VuZChzZChkYW5lMiRBcHBsaWNhbnRfSW5jb21lKSwyKSwgcm91bmQoc2tldyhkYW5lMiRBcHBsaWNhbnRfSW5jb21lKSwyKSwgcm91bmQoa3VydG9zaShkYW5lMiRBcHBsaWNhbnRfSW5jb21lKSwyKSkNCg0KcmFwb3J0IDwtIGRhdGEuZnJhbWUoc3RhdHlzdHlrYSx3YXJ0b3NjKSANCnJhcG9ydCAlPiUgZm9ybWF0dGFibGUoKQ0KDQpgYGANCg0KKipPYnNlcndhY2phOioqDQoNCi0gICDFmnJlZG5pbyBhcGxpa2FuY2kgemFyYWJpYWrEhSA0MTA0Lg0KDQotICAgQXBsaWthbnQsIGt0w7NyeSB6YXJhYmlhxYIgbmFqbW5pZWosIHd5a2F6eXdhxYIgZG9jaMOzZCB3IHd5c29rb8WbY2kgMTUwLA0KDQotICAgTmFqd3nFvHN6eSBkb2Now7NkIGtsaWVudGEgd3luacOzc8WCIDEwNTEzDQoNCi0gICAyNSUgYXBsaWthbnTDs3cgbWlhxYJhIG1uaWVqc3p5IGRvY2jDs2QgbmnFvCAyNzY0DQoNCi0gICA3NSUga2xpZW50w7N3IG1pYcWCYSBkb2Now7NkIHd5xbxzenkgbmnFvCA0OTMxDQoNCi0gICA1MCUga2xpZW50w7N3IG1pYcWCYSBuacW8c3p5IG5pxbwgZG9jaMOzZCBuacW8IDM1OTcgKHR5bSBzYW15bSBkcnVnYSBwb8WCb3dhIGFwbGlrYW50w7N3IG1pYcWCYSBkb2Now7NkIHd5xbxzenkgbmnFvCAzNTk3KQ0KDQotICAgxZpyZWRuaW8gbyAxOTI4LjcgZG9jaG9keSBrbGllbnTDs3cgb2RjaHlsYWrEhSBzacSZIG9kIMWbcmVkbmllZ28gZG9jaG9kdS4gVG8gb3puYWN6YSwgxbxlIGJhcmR6byByb3piaWXFvG5lIHPEhSBkb2Nob2R5IG5hc3p5Y2ggYXBsaWthbnTDs3cuDQoNCjxicj4gPGJyPiA8YnI+DQoNCiMjIyBDb2FwcGxpY2FudCBJbmNvbWUNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpkYW5lMiAlPiUNCiAgZHBseXI6OnNlbGVjdChDb2FwcGxpY2FudF9JbmNvbWUpICU+JQ0KICAgIGZpbHRlcihDb2FwcGxpY2FudF9JbmNvbWU+MCkgJT4lDQogIGdncGxvdChhZXMoeCA9IENvYXBwbGljYW50X0luY29tZSkpICsNCiAgZ2VvbV9kZW5zaXR5KGZpbGw9IiNBM2I1YzAiLCBhbHBoYT0wLjQpICsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oQ29hcHBsaWNhbnRfSW5jb21lLCBuYS5ybSA9IFQpKSwgDQogICAgICAgICAgICAgICBjb2xvdXIgPSAicGluazMiLCBsaW5ldHlwZSA9ImxvbmdkYXNoIiwgc2l6ZSA9IC43KSsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lZGlhbihDb2FwcGxpY2FudF9JbmNvbWUsIG5hLnJtID0gVCkpLCANCiAgICAgICAgICAgICAgIGNvbG91ciA9ICJyb3lhbGJsdWUzIiwgbGluZXR5cGUgPSJsb25nZGFzaCIsIHNpemUgPSAuNykrDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBxdWFudGlsZShDb2FwcGxpY2FudF9JbmNvbWUsIHByb2JzID0gMC4yNSwgbmEucm0gPSBUKSksIA0KICAgICAgICAgICAgICAgY29sb3VyID0gImRhcmtzZWFncmVlbjQiLCBsaW5ldHlwZSA9ImxvbmdkYXNoIiwgc2l6ZSA9IC43KSsNCiAgICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoQ29hcHBsaWNhbnRfSW5jb21lLCBwcm9icyA9IDAuNzUsIG5hLnJtID0gVCkpLCANCiAgICAgICAgICAgICAgIGNvbG91ciA9ICJnb2xkMyIsIGxpbmV0eXBlID0ibG9uZ2Rhc2giLCBzaXplID0gLjcpKw0KICBnZ3RpdGxlKCJDb2FwcGxpY2FudCBJbmNvbWUiKSsNCiAgeGxhYigiIikgKw0KICB0aGVtZV9taW5pbWFsKCkrDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KYGBgDQoNCioqT3puYWN6ZW5pYSBuYSB3eWtyZXNpZToqKiDFm3JlZG5pYS0gcsOzxbxvd3ksIG1lZGlhbmEtIG5pZWJpZXNraSwgUTEtemllbG9ueSwgUTMtxbzDs8WCdHkNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCnN0YXR5c3R5a2EgPC0gYygiTWluIiwgIk1heCIsICJRMSIsIk1lZGlhbmEiLCJRMyIgLCLFmnJlZG5pYSIsICJPZGNoLiBzdGQuIiwgIlNrb8Wbbm/Fm8SHIiwiS3VydG96YSIpDQp3YXJ0b3NjaSA8LSBkYW5lMiAlPiUNCiAgdHJhbnNtdXRlKENvYXBwbGljYW50X0luY29tZSkgJT4lDQogIGZpbHRlcihDb2FwcGxpY2FudF9JbmNvbWU+MCkNCg0Kd2FydG9zY2kgPC0gYyhtaW4od2FydG9zY2kkQ29hcHBsaWNhbnRfSW5jb21lKSxtYXgod2FydG9zY2kkQ29hcHBsaWNhbnRfSW5jb21lKSwgcXVhbnRpbGUod2FydG9zY2kkQ29hcHBsaWNhbnRfSW5jb21lLCAwLjI1KSwgcm91bmQobWVkaWFuKHdhcnRvc2NpJENvYXBwbGljYW50X0luY29tZSwwLjUpLDIpLCBxdWFudGlsZSh3YXJ0b3NjaSRDb2FwcGxpY2FudF9JbmNvbWUsIDAuNzUpLCByb3VuZChtZWFuKHdhcnRvc2NpJENvYXBwbGljYW50X0luY29tZSksMiksIHJvdW5kKHNkKHdhcnRvc2NpJENvYXBwbGljYW50X0luY29tZSksMiksIHJvdW5kKHNrZXcod2FydG9zY2kkQ29hcHBsaWNhbnRfSW5jb21lKSwyKSwgcm91bmQoa3VydG9zaSh3YXJ0b3NjaSRDb2FwcGxpY2FudF9JbmNvbWUpLDIpKQ0KDQpyYXBvcnQgPC0gZGF0YS5mcmFtZShzdGF0eXN0eWthLHdhcnRvc2NpKSANCnJhcG9ydCAlPiUgZm9ybWF0dGFibGUoKQ0KDQpgYGANCg0KKipPYnNlcndhY2phOioqIFBvZCB1d2FnxJkgc8SFIGJyYW5pIGplZHluaWUgd3Nww7PFgnduaW9za29kYXdjeSwga3TDs3J6eSB3eWthenVqxIUgcHJ6eWNob2R5LiANCg0KLSAgIMWacmVkbmlvIGFwbGlrYW5jaSB6YXJhYmlhasSFIDQxMDQuDQoNCi0gICBEb2Now7NkIHdzcMOzxYJ3bmlvc2tvZGF3Y3ksIG8gbmFqbW5pZWpzemVqIHdhcnRvxZtjaSBiecWCIHLDs3dueSAxNi4xMiwNCg0KLSAgIE5hand5xbxzenkgZG9jaMOzZCB3c3DDs8WCd25pb3Nrb2Rhd2N5IHd5bmnDs3PFgiA2NjY2LA0KDQotICAgMjUlIHdzcMOzxYJ3bmlvc2tvZGF3Y8OzdyBtaWHFgmEgbW5pZWpzenkgZG9jaMOzZCBuacW8IDE2MjUNCg0KLSAgIDc1JSB3c3DDs8WCd25pb3Nrb2Rhd2PDs3cgbWlhxYJhIGRvY2jDs2Qgd3nFvHN6eSBuacW8IDI4NTcNCg0KLSAgIDUwJSB3c3DDs8WCd25pb3Nrb2Rhd2PDs3cgbWlhxYJhIG5pxbxzenkgbmnFvCBkb2Now7NkIG5pxbwgMjA3OSAodHltIHNhbXltICAgICBkcnVnYSBwb8WCb3dhIG1pYcWCYSBkb2Now7NkIHd5xbxzenkgbmnFvCAzNTk3KQ0KDQotICAgxZpyZWRuaW8gbyAxMTY0LjUgZG9jaG9keSB3c3DDs8WCd25pb3Nrb2Rhd2PDs3cgb2RjaHlsYcWCIHNpxJkgb2QgaWNoIMWbcmVkbmllZ28gZG9jaG9kdS4NCg0KPGJyPiA8YnI+IDxicj4NCg0KIyMjIExvYW4gQW1vdW50DQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZGFuZTIgJT4lDQogIGRwbHlyOjpzZWxlY3QoTG9hbl9BbW91bnQpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBMb2FuX0Ftb3VudCkpICsNCiAgZ2VvbV9kZW5zaXR5KGZpbGw9IiNBM2I1YzAiLCBhbHBoYT0wLjQpICsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oTG9hbl9BbW91bnQsIG5hLnJtID0gVCkpLCANCiAgICAgICAgICAgICAgIGNvbG91ciA9ICJwaW5rMyIsIGxpbmV0eXBlID0ibG9uZ2Rhc2giLCBzaXplID0gLjcpKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVkaWFuKExvYW5fQW1vdW50LCBuYS5ybSA9IFQpKSwgDQogICAgICAgICAgICAgICBjb2xvdXIgPSAicm95YWxibHVlMyIsIGxpbmV0eXBlID0ibG9uZ2Rhc2giLCBzaXplID0gLjcpKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoTG9hbl9BbW91bnQsIHByb2JzID0gMC4yNSwgbmEucm0gPSBUKSksIA0KICAgICAgICAgICAgICAgY29sb3VyID0gImRhcmtzZWFncmVlbjQiLCBsaW5ldHlwZSA9ImxvbmdkYXNoIiwgc2l6ZSA9IC43KSsNCiAgICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoTG9hbl9BbW91bnQsIHByb2JzID0gMC43NSwgbmEucm0gPSBUKSksIA0KICAgICAgICAgICAgICAgY29sb3VyID0gImdvbGQzIiwgbGluZXR5cGUgPSJsb25nZGFzaCIsIHNpemUgPSAuNykrDQogIGdndGl0bGUoIkxvYW4gQW1vdW50IikrDQogIHhsYWIoIiIpICsNCiAgdGhlbWVfbWluaW1hbCgpKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCmBgYA0KDQoqKk96bmFjemVuaWEgbmEgd3lrcmVzaWU6KiogxZtyZWRuaWEtIHLDs8W8b3d5LCBtZWRpYW5hLSBuaWViaWVza2ksIFExLXppZWxvbnksIFEzLcW8w7PFgnR5DQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kc3RhdHlzdHlrYSA8LSBjKCJNaW4iLCAiTWF4IiwgIlExIiwiTWVkaWFuYSIsIlEzIiAsIsWacmVkbmlhIiwgIk9kY2guIHN0ZC4iLCAiU2tvxZtub8WbxIciLCJLdXJ0b3phIikNCndhcnRvc2MgPC0gYyhtaW4oZGFuZTIkTG9hbl9BbW91bnQpLG1heChkYW5lMiRMb2FuX0Ftb3VudCksIHF1YW50aWxlKGRhbmUyJExvYW5fQW1vdW50LCAwLjI1KSwgcm91bmQobWVkaWFuKGRhbmUyJExvYW5fQW1vdW50LDAuNSksMiksIHF1YW50aWxlKGRhbmUyJExvYW5fQW1vdW50LCAwLjc1KSwgcm91bmQobWVhbihkYW5lMiRMb2FuX0Ftb3VudCksMiksIHJvdW5kKHNkKGRhbmUyJExvYW5fQW1vdW50KSwyKSwgcm91bmQoc2tldyhkYW5lMiRMb2FuX0Ftb3VudCksMiksIHJvdW5kKGt1cnRvc2koZGFuZTIkTG9hbl9BbW91bnQpLDIpKQ0KDQpyYXBvcnQgPC0gZGF0YS5mcmFtZShzdGF0eXN0eWthLHdhcnRvc2MpIA0KcmFwb3J0ICU+JSBmb3JtYXR0YWJsZSgpDQpgYGANCg0KKipPYnNlcndhY2phOioqDQoNCi0gICDFmnJlZG5pbyBhcGxpa2FuY2kgd25pb3NrdWrEhSBvIGtyZWR5dCB3IHd5c29rb8WbY2kgMTI3LjYzLA0KDQotICAgTmFqbmnFvHN6eSB3bmlvc2VrIG8ga3JlZHl0IGJ5xYIgcsOzd255IDksDQoNCi0gICBOYWp3ecW8c3p5IGRvY2jDs2Qga2xpZW50YSB3eW5pw7NzxYIgMjgwLA0KDQotICAgMjUlIHduaW9za8OzdyBiecWCYSBuYSBrd290xJkgbmnFvHN6xIUgbmnFvCA5OSwNCg0KLSAgIDc1JSB3bmlvc2vDs3cgYnnFgmEgbmEga3dvdMSZIHd5xbxzesSFIG5pxbwgMTU1LA0KDQotICAgNTAlIHduaW9za8OzdyBiecWCYSBuYSBrd290xJkgbmnFvHN6xIUgbmnFvCAxMjIgKHR5bSBzYW15bSBkcnVnYSBwb8WCb3dhIG5hIGt3b3TEmSB3ecW8c3p5IG5pxbwgMTIyKSwNCg0KLSAgIMWacmVkbmlvIG8gNDcuNCBrd290eSBuYSB3bmlpb3NrYWNoIHBvxbx5Y3prb3d5Y2ggb2RjaHlsYWrEhSBzacSZIG9kIMWbcmVkbmllaiBrd290eSwgbmEga3TDs3LEhSB6YWNpxIVnYW5lIHPEhSB6b2Jvd2nEhXphbmlhLg0KDQo8YnI+IDxicj4gPGJyPg0KDQojIyBLb3JlbGFjamENCg0KPHAgc3R5bGU9InRleHQtYWxpZ246IGp1c3RpZnkiPiBLb3JlbGFjamEgdG8gbWlhcmEga2llcnVua3UgaSBzacWCeSB6d2nEhXprdSBtacSZZHp5IGR3aWVtYSB6bWllbm55bWkuIEplaiB3YXJ0b8WbxIcgemF3aWVyYSBzacSZIHcgcHJ6ZWR6aWFsZSBvZCAtMSBkbyAxLiBLb3JlbGFjamEgYmxpc2thIHplcnUgc3VnZXJ1amUgYnJhayBpc3RvdG5laiB6YWxlxbxub8WbY2kgbGluaW93ZWogbWnEmWR6eSB6bWllbm55bWkgLSBhbGUgbmllIG96bmFjemEsIMW8ZSBuaWUgbWEgbWnEmWR6eSBuaW1pIGlubnljaCB6d2nEhXprw7N3LiBHZHkgd2FydG/Fm8SHIGtvcmVsYWNqaSBqZXN0IGJsaXNrYSAxLCB0byBvYnlkd2llIHptaWVubmUgcm9zbsSFIGx1YiBtYWxlasSFIHJhemVtLCBhIGdkeSBqZXN0IGJsaXNrYSAtMSwgdG8gd3pyb3N0b20gd2FydG/Fm2NpIGplZG5laiB6ZSB6bWllbm55Y2ggdG93YXJ6eXN6xIUgc3BhZGtpIHdhcnRvxZtjaSBkcnVnaWVqLiA8L3A+DQo8YnI+DQo8cCBzdHlsZT0idGV4dC1hbGlnbjoganVzdGlmeSI+IEdkeSBiYWRhbXkgd2nEmWNlaiBuaXogZHdpZSB6bWllbm5lLCBtb8W8ZW15IHByemVkc3Rhd2nEhyBpY2ggd3Nww7PFgmN6eW5uaWtpIGtvcmVsYWNqaSB3IHBvc3RhY2kgamVkbmVqIG1hY2llcnp5LiBXIG5hc3p5bSBwcnp5cGFka3UgdcW8eWxpxZtteSBmdW5rY2ppIGhldGNvciwgYWJ5IHpiYWRhxIcgd3Nww7PFgmN6eW5uaWtpIGtvcmVsYWNqaSBsaW5pb3dlaiBQZWFyc29uYSBtacSZZHp5IHptaWVubnltaSBudW1lcnljem55bWkgKCJMb2FuIEFtb3VudCIsICJBcHBsaWNhbnQgSW5jb21lIiwgIkNvYXBwbGljYW50IEluY29tZSIsIGkgIkxvYW4gQW1vdW50IFRlcm0iKSwgd3Nww7PFgmN6eW5uaWtpIGtvcmVsYWNqaSBwb2xpY2hvcnljem5laiBtacSZZHp5IHptaWVubnltaSBjenlubmlrb3d5bWkgKCJMb2FuIFN0YXR1cyIsICJHZW5kZXIiLCAiQ3JlZGl0IEhpc3RvcnkiLCAiUHJvcGVydHkgQXJlYSIgaSAiRGVwZW5kZW50cyIpIG9yYXogd3Nww7PFgmN6eW5uaWtpIGtvcmVsYWNqaSB3aWVsb3NlcnlqbmVqIG1pxJlkenkgem1pZW5ueW1pIGN6eW5uaWtvd3ltaSBhIG51bWVyeWN6bnltaS4gPC9wPg0KPGJyPiA8YnI+DQoNCiMjIyBLb3JlbGFjamUgbWnEmWR6eSBkZWN5emrEhSBvIHByenl6bmFuaXUga3JlZHl0dSBhIGlubnltaSB6bWllbm55bWkNCg0KLSAgIDxwIHN0eWxlPSJ0ZXh0LWFsaWduOiBqdXN0aWZ5Ij4gWiBtYWNpZXJ6eSBrb3JlbGFjamkgZG93aWFkdWplbXkgc2nEmSwgxbxlIHptaWVubmEgbmFqc2lsbmllaiBza29yZWxvd2FuxIUgeiBkZWN5emrEhSBvIHByenl6bmFuaXUga3JlZHl0dSBqZXN0IHBvc2lhZGFuaWUgaGlzdG9yaWkgICBrcmVkeXRvd2VqIC0gd3Nww7PFgmN6eW5uaWsga29yZWxhY2ppIHd5bm9zaSB3IHR5bSBwcnp5cGFka3UgYcW8IDAsODcuPC9wPg0KDQotICAgPHAgc3R5bGU9InRleHQtYWxpZ246IGp1c3RpZnkiPiBTxYJhYnN6xIUsIGFsZSB3Y2nEhcW8IGlzdG90bsSFIGtvcmVsYWNqxJkgcG96eXR5d27EhSB3eWthenVqxIUgcMWCZcSHIG3EmXNrYSwgZG9jaMOzZCB3c3DDs8WCbWHFgsW8b25rYSBpIGlsb8WbYyBwb3NpYWRhbnljaCBkemllY2kgLSB0dSB3c3DDs8WCY3p5bm5pa2kgd3lub3N6xIUgb2Rwb3dpZWRuaW8gMCwxNiwgMCwwOSBvcmF6IDAsMDYuIDwvcD4NCg0KLSAgIDxwIHN0eWxlPSJ0ZXh0LWFsaWduOiBqdXN0aWZ5Ij4gVyBwcnp5cGFka3UgcG/Fgm/FvGVuaWEgbmllcnVjaG9tb8WbY2kgbW/FvG5hIHp3csOzY2ljIHV3YWfEmSBuYSBkZWxpa2F0bsSFIGtvcmVsYWNqxJkgcG96eXR5d27EhSBkZWN5emppIG8gcHJ6eXpuYW5pdSBrcmVkeXR1IHogdGVyZW5hbWkgbWllanNraW1pIHJhY3plaiBuacW8IHdpZWpza2ltaSAtIHdzcMOzxYJjenlubmlrIGtvcmVsYWNqaSBwb2xpY2hvcnljem5laiB3eW5pw7NzxYIgZGxhIHRlaiBwYXJ5IHptaWVubnljaCAwLDA0LiA8L3A+DQoNCi0gICA8cCBzdHlsZT0idGV4dC1hbGlnbjoganVzdGlmeSI+IENvIGNpZWthd2UsIHdzcMOzxYJjenlubmlraSBrb3JlbGFjamkgcG9tacSZZHp5IGRlY3l6asSFIG8gcHJ6eXpuYW5pdSBrcmVkeXR1IGEgZG9jaG9kZW0ga2xpZW50YSwgb2tyZXNlbSBrcmVkeXRvd2FuaWEgaSB3eXNva2/Fm2NpxIUgcG/FvHljemtpIHPEhSBibGlza2llIHplcnUsIGNvIHN1Z2VydWplIGJyYWsgYmV6cG/Fm3JlZG5pZWdvIHp3acSFemt1IG1pxJlkenkgem1pZW5ueW1pLiA8L3A+DQoNCiMjIyBLb3JlbGFjamUgbWnEmWR6eSBwb3pvc3RhxYJ5bWkgem1pZW5ueW1pDQoNCkludGVyZXN1asSFY2Ugc8SFIHLDs3duaWXFvCB6YWxlxbxub8WbY2kgbWnEmWR6eSB6bWllbm55bWkgd3DFgnl3YWrEhWN5bWkgbmEgemRvbG5vxZvEhyBrcmVkeXRvd8SFOg0KDQotICAgPHAgc3R5bGU9InRleHQtYWxpZ246IGp1c3RpZnkiPiBXeXN0xJlwdWplIHd5cmHFum5hLCBwb3p5dHl3bmEga29yZWxhY2phIG1pxJlkenkgd3lzb2tvxZtjaWEgcG/FvHljemtpIG8ga3TDs3JhIHduaW9za3VqZSBrbGllbnQgYSBqZWdvIGRvY2hvZGVtIC0gd3Nww7PFgmN6eW5uaWsgd3lub3NpIGHFvCAwLDUzLiBtb8W8ZSB0byB3eWphxZtuacSHIGJyYWsgd3lyYcW6bmVqIGtvcmVsYWNqaSBtacSZZHp5IHR5bWkgem1pZW5ueW1pIGEgZGVjeXpqxIUgYmFua3UgLSBrbGllbmNpIG8gbmnFvHN6eW0gZG9jaG9kemllIHd5ZGFqxIUgc2nEmSByZXp5Z25vd2FjIHogdWJpZWdhbmlhIHNpxJkgbyB3ecW8c3rEhSBwb8W8eWN6a8SZIGp1xbwgbmEgZXRhcGllIHNrxYJhZGFuaWEgd25pb3NrdSwgd2nEmWMgZGVjeXpqYSBiYW5rdSBwb3pvcm5pZSBuaWUgamVzdCBza29yZWxvd2FuYSB6IGljaCBkb2Nob2RlbSBpIHd5c29rb8WbY2lhIHBvxbx5Y3praSBvIGpha8SFIHduaW9za3VqxIUgLSBjaG/EhyBtb8W8bmEgcHJ6eXB1c3pjemHEhywgxbxlIHRha2Ega29yZWxhY2phIGJ5IHd5c3TEhXBpxYJhIGdkeWJ5IG5pZSB0ZWdvIHR5cHUgInNhbW9vZ3JhbmljemFuaWUgc2nEmSIga2xpZW50w7N3LiA8L3A+DQoNCi0gICA8cCBzdHlsZT0idGV4dC1hbGlnbjoganVzdGlmeSI+IERvxZvEhyB6bmFjesSFY2EgamVzdCB0ZXoga29yZWxhY2phIG1pxJlkenkgbcSZc2vEhSBwxYJjacSFIGtsaWVudMOzdyBhIGlsb8WbY2nEhSBkemllY2kgaSBkb2Nob2RlbSB3c3DDs8WCbWHFgsW8b25rYSAod3Nww7PFgmN6eW5uaWtpIG5hIHBvemlvbWllIG9kcG93aWVkbmlvIDAsNCBpIDAsMzIpIC0gc3VnZXJ1amUgdG8sIMW8ZSB3IHByenlwYWRrdSBtYcWCxbxlxYRzdHcga29yenlzdGFqxIVjeWNoIHoga3JlZHl0dSBoaXBvdGVjem5lZ28gbWHFgsW8b25raWVtLCBrdMOzcnkgYmllcnplIGtyZWR5dCBqZXN0IGN6xJnFm2NpZWogbcSFxbwgbmnFvCDFvG9uYS4gPC9wPg0KDQotICAgPHAgc3R5bGU9InRleHQtYWxpZ246IGp1c3RpZnkiPiBNxJnFvGN6ecW6bmkgd3lkYWphIHNpxJkgdGXFvCBza8WCb25uaSBkbyB6YWNpxIVnYW5pYSB3acSZa3N6eWNoIGtyZWR5dMOzdyBvcmF6IGFrY2VwdG93YW5pYSBrcsOzdHN6ZWdvIG9rcmVzdSBrcmVkeXRvd2FuaWEgLSB3c3DDs8WCY3p5bm5pa2kga29yZWxhY2ppIG1pxJlkenkgcMWCY2nEhSBtxJlza8SFIGEgd3lzb2tvxZtjaWEgcG/FvHljemtpIGkgb2tyZXNlbSBqZWogc3DFgmF0eSB3eW5vc3rEhSBvZHBvd2llZG5pbyAwLDE2IGkgLTAsMTUuIDwvcD4NCg0KLSAgIDxwIHN0eWxlPSJ0ZXh0LWFsaWduOiBqdXN0aWZ5Ij4gTW96bmEgemFvYnNlcndvd2FjIHVqZW1uYSBrb3JlbGFjamUgbmEgcG96aW9taWUgLTAsMjYgcG9tacSZZHp5IGRvY2hvZGVtIGtsaWVudGEgYSBkb2Nob2RlbSBqZWdvIHdzcMOzxYJtYcWCxbxvbmthLiBNb8W8bmEgd2llYyBwcnp5cHVzemN6YcSHLCDFvGUgd8WbcsOzZCBtYcWCxbxlxYRzdHcgd3lzdMSZcHVqxIVjeWNoIHogd25pb3NraWVtIG8ga3JlZHl0IGhpcG90ZWN6bnkgd3lzdMSZcHVqxIUgcsOzxbxuaWNlIHcgZG9jaG9kemllIG9ieWR3dSBtYcWCxbxvbmvDs3cuIDwvcD4NCg0KLSAgIDxwIHN0eWxlPSJ0ZXh0LWFsaWduOiBqdXN0aWZ5Ij4gRG9jaMOzZCB3c3DDs8WCbWHFgsW8b25rYSBqZXN0IGRvZGF0bmlvIHNrb3JlbG93YW55IHogd3lzb2tvxZtjaWEgcG/FvHljemtpIG8gamFrYSBrbGllbnQgd25pb3NrdWplLiBXc3DDs8WCY3p5bm5payB0ZWoga29yZWxhY2ppIHd5bm9zaSBqZWRuYWsgMCwyNiwgamVzdCB3acSZYyBva2/Fgm8gZHd1a3JvdG5pZSBuacW8c3p5IG5pxbwgdyBwcnp5cGFka3UgZG9jaG9kdSBzYW1lZ28ga2xpZW50YS4gPC9wPg0KDQotICAgPHAgc3R5bGU9InRleHQtYWxpZ246IGp1c3RpZnkiPiBLbGllbmNpIHN0YXJhasSFY3kgc2llIG8ga3JlZHl0IGhpcG90ZWN6bnkgbmEgbmllcnVjaG9tb8WbYyBwb8WCb3pvbmEgbmEgdGVyZW5pZSB3aWVqc2tpbSBjaGFyYWt0ZXJ5enVqxIUgc2nEmSBuacW8c3p5bSBkb2Nob2RlbSAtIHphcsOzd25vIHfFgmFzbnltIGphayBpIHdzcMOzxYJtYcWCxbxvbmthIHdzcMOzxYJjenlubmlraSB3eW5vc3rEhSBvZHBvd2llZG5pbyAtMCwwNSBpIC0wLDA3KSAtIG9yYXogd25pb3NrdWrEhSBvIG5pxbxzemUga3JlZHl0eSB6IGtyw7N0c3p5bSB0ZXJtaW5lbSB6YXBhZGFsbm/Fm2NpICh3c3DDs8WCY3p5bm5pa2kga29yZWxhY2ppIG5hIHBvemlvbWllIC0wLDEgaSAtMCwwOCkuIFN1Z2VydWplIHRvLCDFvGUgd8WbcsOzZCBrbGllbnTDs3cgd3lzdMSZcHVqxIUgbmllcsOzd25vxZtjaSBkb2Nob2Rvd2UgaSBtYWrEhXRrb3dlIHV6YWxlxbxuaW9uZSBvZCBtaWVqc2NhIHphbWllc3prYW5pYS48L3A+DQogICAgDQotICAgPHAgc3R5bGU9InRleHQtYWxpZ246IGp1c3RpZnkiPiBXcmF6IHogbGljemLEhSBwb3NpYWRhbnljaCBkemllY2kgcm/Fm25pZSBkb2Now7NkIGtsaWVudGEsIGFsZSBzcGFkYSBkb2Now7NkIGplZ28gd3Nww7PFgm1hxYLFvG9ua2EgLSB3c3DDs8WCY3p5bm5pa2kga29yZWxhY2ppIHd5bm9zesSFIHR1IG9kcG93aWVkbmlvIDAsMTQgaSAtMCwwNS4gTW/FvGUgdG8gc3VnZXJvd2HEhywgxbxlIG5pZWtpZWR5IHcgdGVqIHN5dHVhY2ppIGplZG5vIHogbWHFgsW8b25rw7N3IHcgd2nEmWtzenltIHN0b3BuaXUgc2t1cGlhIHNpZSBuYSB3eWNob3dhbml1IGR6aWVjaSBpIGplZ28gZG9jaMOzZCBzcGFkYS4gPC9wPg0KDQotICAgPHAgc3R5bGU9InRleHQtYWxpZ246IGp1c3RpZnkiPiBDbyB3acSZY2VqLCBsaWN6YmEgcG9zaWFkYW55Y2ggZHppZWNpIGplc3QgZG9kYXRuaW8gc2tvcmVsb3dhbmUgeiB3eXNva2/Fm2NpxIUga3JlZHl0dSBvIGpha2kgd25pb3NrdWplIGtsaWVudCBpIG5lZ2F0eXduaWUgc2tvcmVsb3dhbmEgeiB0ZXJtaW5lbSBrcmVkeXRvd2FuaWEgLSB3c3DDs8WCY3p5bm5pa2kga29yZWxhY2ppIHd5bm9zesSFIG9kcG93aWVkbmlvIDAsMTUgb3JheiAtMCwxLiBNb8W8ZSB0byBzdWdlcm93YcSHLCDFvGUga2xpZW5jaSBtb2fEhWN5IHNvYmllIHBvendvbGnEhyBuYSB3ecW8c3plIGtyZWR5dHkgaSBrcsOzdHN6ZSBva3Jlc3kga3JlZHl0b3dhbmlhIG1vZ8SFIHLDs3duaWXFvCB1dHJ6eW1hxIcgd2nEmWNlaiBkemllY2kuIDwvcD4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KZGFuZV9kb19tYWNpZXJ6eSA8LSBkYXRhLmZyYW1lKGRhbmUyJExvYW5fU3RhdHVzLCBkYW5lMiRMb2FuX0Ftb3VudCwgZGFuZTIkQXBwbGljYW50X0luY29tZSwgZGFuZTIkQ29hcHBsaWNhbnRfSW5jb21lLCBkYW5lMiRMb2FuX0Ftb3VudF9UZXJtLCBkYW5lMiREZXBlbmRlbnRzLCAgZGFuZTIkR2VuZGVyLCBkYW5lMiRDcmVkaXRfSGlzdG9yeSwgZGFuZTIkUHJvcGVydHlfQXJlYSkNCmFzLnRpYmJsZShkYW5lX2RvX21hY2llcnp5KQ0KZXR5a2lldHkgPC0gYygiTG9hbiBTdGF0dXMiLCAiTG9hbiBBbW91bnQiLCAiQXBwbGljYW50IEluY29tZSIsICJDb2FwcGxpY2FudCBJbmNvbWUiLCAiTG9hbiBBbW91bnQgVGVybSIsICJEZXBlbmRlbnRzIiwgIkdlbmRlciIsICJDcmVkaXQgSGlzdG9yeSIsICJQcm9wZXJ0eSBBcmVhIikNCm1hY2llcnpfa29yZWxhY2ppIDwtIHBvbHljb3I6OmhldGNvcihkYW5lX2RvX21hY2llcnp5KQ0KcHJpbnQobWFjaWVyel9rb3JlbGFjamkpDQp3c3BvbGN6eW5uaWtpX2tvcmVsYWNqaSA8LSBtYWNpZXJ6X2tvcmVsYWNqaSRjb3JyZWxhdGlvbnMNCmtvcmVsb2dyYW0gPC0gY29ycmdyYW0od3Nwb2xjenlubmlraV9rb3JlbGFjamksIA0KICAgICAgICAgICAgICAgICAgICAgICBvcmRlciA9IEZBTFNFLCANCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gZXR5a2lldHksDQogICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICBkaWFnLnBhbmVsID0gcGFuZWwuaGlzdG9ncmFtLCANCiAgICAgICAgICAgICAgICAgICAgICAgdXBwZXIucGFuZWw9cGFuZWwucGllLCANCiAgICAgICAgICAgICAgICAgICAgICAgY29sLnJlZ2lvbnMgPSBjb2xvclJhbXBQYWxldHRlKGMoIiNBM2I1YzAiLCIjZWVlMmQ0IiwgInllbGxvdzQiLCAiZ3JheTkyIiwiIzk1OTU5NSIsImJsdWU0IiwicGluazMiKSkNCiAgICAgICAgICAgICAgICAgICAgICAgKQ0KDQpgYGANCg0KIyA1LiBFREEsIGN6eWxpIEVrc3Bsb3JhY3lqbmEgQW5hbGl6YSBEYW55Y2gNCg0KRURBIHRvIHplc3RhdyBpbnRlcmFrdHl3bnljaCBpIHdpenVhbG55Y2ggdGVjaG5payBhbmFsaXp5IHpiaW9ydSBkYW55Y2ggYmV6IGJhcmR6aWVqIHByZWN5enlqbmVnbyB3c2themFuaWEgY2VsdSBla3NwbG9yYWNqaS4NCg0KRURBIHBvendhbGEgbmE6DQoNCi0gICB6Z8WCxJliaWVuaWUgemJpb3J1IGRhbnljaCwNCg0KLSAgIHNwcmF3ZHplbmllIHphbGXFvG5vxZtjaSBtacSZZHp5IGF0cnlidXRhbWksDQoNCi0gICBvcHJhY293YW5pZSB3c3TEmXBueWNoIHduaW9za8OzdyBkb3R5Y3rEhWN5Y2ggcHJhd2lkxYJvd2/Fm2NpIHcgemJpb3J6ZSBwcnp5a8WCYWTDs3cuDQoNCjxicj4NCg0KIyMgQW5hbGl6YSBqZWRub3d5bWlhcm93YQ0KDQo8cCBzdHlsZT0idGV4dC1hbGlnbjoganVzdGlmeSI+IENlbGVtIHRlaiBjesSZxZtjaSBwcm9qZWt0dSBqZXN0IHByZXplbnRhY2phIHcgcG9zdGFjaSB3eWtyZXPDs3cgemFsZcW8bm/Fm2NpIG1pxJlkenkgZGVjeXpqxIUga3JlZHl0b3fEhSBhIHBvc3pjemVnw7NsbnltaSB6bWllbm55bWkgamFrb8WbY2lvd3ltaSBpIGlsb8WbY2lvd3ltaS4gU3ByYXdkemlteSBqYWsgcG9zemN6ZWfDs2xuZSB6bWllbm5lIG9iamHFm25pYWrEhWNlIHdwxYJ5d2FqYSBuYSB6bWllbm5hIG9iamHFm25pYW7EhS4gPC9wPg0KDQojIyMgWm1pZW5uZSBqYWtvxZtjaW93ZSB7LnRhYnNldH0NCg0KU3ByYXdkemlteSBqYWsgZGFuZSBjZWNoeSB3cMWCeXdhasSFIG5hIHBvZGVqbW93YW5pZSBkZWN5emppIGtyZWR5dG93ZWouDQoNCiMjIyMgR2VuZGVyDQoNClptaWVubmEgIkdlbmRlciIgbmllIG1hIHpuYWN6xIVjZWdvIHdwxYJ5d3UgbmEga3N6dGHFgnRvd2FuaWUgc2nEmSB6ZG9sbm/Fm2NpIGtyZWR5dG93ZWouIFphdXdhxbxhbG5hIGplc3Qgbmllem5hY3puYSByw7PFvG5pY2EgcG9tacSZZHp5IGxpY3pixIUgdWR6aWVsYW55Y2gga3JlZHl0w7N3IG5hIGtvcnp5xZvEhyBtxJnFvGN6eXpuLg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmRhbmUyICU+JQ0KICBkcGx5cjo6c2VsZWN0KEdlbmRlcixMb2FuX1N0YXR1cykgJT4lDQogIGZpbHRlcihHZW5kZXI9PWMoIkZlbWFsZSIsICJNYWxlIikpICU+JQ0KICBncm91cF9ieShHZW5kZXIpICU+JQ0KICBjb3VudChMb2FuX1N0YXR1cykgJT4lDQogIGdncGxvdChhZXMoeCA9IEdlbmRlciwgeSA9IG4sIGZpbGwgPSBMb2FuX1N0YXR1cykpICsNCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAiZmlsbCIsY29sb3VyID0gImdyYXkyMCIsIGFscGhhPTAuNywgd2lkdGggPSAuNykgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiI0EzYjVjMCIsIiNlNWUwZDgiKSwgbGFiZWxzPWMoIk5vIiwgIlllcyIpKSsNCiAgbGFicyhmaWxsPSJMb2FuIFN0YXR1cyIpKw0KICBnZ3RpdGxlKCJHZW5kZXIiKSsNCiAgdGhlbWVfbWluaW1hbCgpKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCmBgYA0KDQojIyMjIERlcGVuZGVudHMNCg0KQ28gY2lla2F3ZSwgbmFqY3rEmcWbY2llaiBvZG1hd2lhbmUgc8SFIGtyZWR5dHkgb3NvYm9tLCBrdMOzcmUgbWFqxIUgdHlsa28gMSBvc29ixJkgbmEgdXR6eW1hbml1LiBEbGEgYnJha3Ugb3PDs2IgbmEgdXRyenltYW5laiwgMiBvc8OzYiBvcmF6IDMgaSB3acSZY2VqIHpkb2xub8WbxIcga3JlZHl0b3dhIGplc3QgcG9kb2JuYS4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpkYW5lMiAlPiUNCiBkcGx5cjo6IHNlbGVjdChEZXBlbmRlbnRzLExvYW5fU3RhdHVzKSAlPiUNCiAgZmlsdGVyKERlcGVuZGVudHM9PWMoIjAiLCAiMSIsICIyIiwiMysiKSkgJT4lDQogIGdyb3VwX2J5KERlcGVuZGVudHMpICU+JQ0KICBjb3VudChMb2FuX1N0YXR1cykgJT4lDQogIGdncGxvdChhZXMoeCA9IERlcGVuZGVudHMsIHkgPSBuLCBmaWxsID0gTG9hbl9TdGF0dXMpKSArDQogIGdlb21fY29sKHBvc2l0aW9uID0gImZpbGwiLGNvbG91ciA9ICJncmF5MjAiLCBhbHBoYT0wLjcsIHdpZHRoID0gLjcpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiNBM2I1YzAiLCIjZTVlMGQ4IiksIGxhYmVscz1jKCJObyIsICJZZXMiKSkrDQogIGxhYnMoZmlsbD0iTG9hbiBTdGF0dXMiKSsNCiAgZ2d0aXRsZSgiRGVwZW5kZW50cyIpKw0KICB0aGVtZV9taW5pbWFsKCkrDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KYGBgDQoNCiMjIyMgRWR1Y2F0aW9uDQoNCk9zb2JvbSwga3TDs3JlIHVrb8WEY3p5xYJ5IHN6a2/FgsSZIGN6xJnFm2NpZWogc8SFIHVkemllbGFuZSBwb8W8eWN6a2ksIG5pxbwgb3NvYm9tLCBrdMOzcmUgamVqIG5pZSB1a2/FhGN6ecWCeS4gTW/FvGUgdG8gd3luaWthxIcgeiBmYWt0dSwgxbxlIHphend5Y3phaiBvc29ieSBwbyBzemtvbGUgbWFqxIUgbGVwaWVqIHDFgmF0bsSFIHByYWPEmS4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpkYW5lMiAlPiUNCiAgZHBseXI6OnNlbGVjdChFZHVjYXRpb24sTG9hbl9TdGF0dXMpICU+JQ0KICBmaWx0ZXIoRWR1Y2F0aW9uPT1jKCJHcmFkdWF0ZSIsICJOb3QgR3JhZHVhdGUiKSkgJT4lDQogIGdyb3VwX2J5KEVkdWNhdGlvbikgJT4lDQogIGNvdW50KExvYW5fU3RhdHVzKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gRWR1Y2F0aW9uLCB5ID0gbiwgZmlsbCA9IExvYW5fU3RhdHVzKSkgKw0KICBnZW9tX2NvbChwb3NpdGlvbiA9ICJmaWxsIixjb2xvdXIgPSAiZ3JheTIwIiwgYWxwaGE9MC43LCB3aWR0aCA9IC43KSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjQTNiNWMwIiwiI2U1ZTBkOCIpLCBsYWJlbHM9YygiTm8iLCAiWWVzIikpKw0KICBsYWJzKGZpbGw9IkxvYW4gU3RhdHVzIikrDQogIGdndGl0bGUoIkVkdWNhdGlvbiIpKw0KICB0aGVtZV9taW5pbWFsKCkrDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KYGBgDQoNCiMjIyMgU2VsZiBFbXBsb3llZA0KDQpXc2thxbpuaWsgZGxhIG9idSBncnVwIGtzenRhxYJ0dWplIHNpxJkgbmEgcG9kb2JueW0gcG96aW9taWUuDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZGFuZTIgJT4lDQogIGRwbHlyOjpzZWxlY3QoU2VsZl9FbXBsb3llZCxMb2FuX1N0YXR1cykgJT4lDQogIGZpbHRlcihTZWxmX0VtcGxveWVkPT1jKCJZZXMiLCAiTm8iKSkgJT4lDQogIGdyb3VwX2J5KFNlbGZfRW1wbG95ZWQpICU+JQ0KICBjb3VudChMb2FuX1N0YXR1cykgJT4lDQogIGdncGxvdChhZXMoeCA9IFNlbGZfRW1wbG95ZWQsIHkgPSBuLCBmaWxsID0gTG9hbl9TdGF0dXMpKSArDQogIGdlb21fY29sKHBvc2l0aW9uID0gImZpbGwiLGNvbG91ciA9ICJncmF5MjAiLCBhbHBoYT0wLjcsIHdpZHRoID0gLjcpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiNBM2I1YzAiLCIjZTVlMGQ4IiksIGxhYmVscz1jKCJObyIsICJZZXMiKSkrDQogIGxhYnMoZmlsbD0iTG9hbiBTdGF0dXMiKSsNCiAgdGhlbWVfbWluaW1hbCgpKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCmBgYA0KDQojIyMjIENyZWRpdCBIaXNvcnkNCg0KV2lkb2N6bmEgamVzdCB6bmFjesSFY2EgcsOzxbxuaWFjYS4gVHlsa28gbmllY2HFgmUgMTAlIG9zw7NiIGJleiB6ZG9sbm/Fm2NpIGtyZWR5dG93ZWosIGRvc3RhamUgcG96eXR5d27EhSBvZHBvd2llZMW6IHcgc3ByYXdpZSBrcmVkeXR1LiBKZcWbbGkga3RvxZsgbWEgaGlzdG9yacSZIGtyZWR5dG93xIUgamVnbyBzemFuc2EgbmEgcG96eXR5d25lIHJvenBhdHJ6ZW5pZSB3aW5vc2t1IHd5bm9zaSBvayA4MCUuDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZGFuZTIgJT4lDQogIGRwbHlyOjpzZWxlY3QoQ3JlZGl0X0hpc3RvcnksTG9hbl9TdGF0dXMpICU+JQ0KICBmaWx0ZXIoQ3JlZGl0X0hpc3Rvcnk9PWMoIjEiLCAiMCIpKSAlPiUNCiAgZ3JvdXBfYnkoQ3JlZGl0X0hpc3RvcnkpICU+JQ0KICBjb3VudChMb2FuX1N0YXR1cykgJT4lDQogIGdncGxvdChhZXMoeCA9IENyZWRpdF9IaXN0b3J5LCB5ID0gbiwgZmlsbCA9IExvYW5fU3RhdHVzKSkgKw0KICBnZW9tX2NvbChwb3NpdGlvbiA9ICJmaWxsIixjb2xvdXIgPSAiZ3JheTIwIiwgYWxwaGE9MC43LCB3aWR0aCA9IC43KSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjQTNiNWMwIiwiI2U1ZTBkOCIpLCBsYWJlbHM9YygiTm8iLCAiWWVzIikpKw0KICBsYWJzKGZpbGw9IkxvYW4gU3RhdHVzIikrDQogIGdndGl0bGUoIkNyZWRpdCBIaXN0b3J5IikrDQogIHRoZW1lX21pbmltYWwoKSsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQpgYGANCg0KIyMjIyBQcm9wYXJ0eSBBcmVhDQoNClLDs8W8bmljZSBzxIUgbmlld2llbGtpZS4gTmFqd2nEmWNla3N6eSBvZHNldGVrIG9zw7NiLCBrdMOzcnltIHpvc3RhamUgdWR6aWVsb255IGtyZWR5dCwgcG9jaG9kesSFIHogb2JzemFyw7N3IHDDs8WCbWllanNraWNoLCB6YcWbIG5ham1uaWVqc3p5IG9zb2J5IHogb2JzemFyw7N3IHdpZWpza2ljaC4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpkYW5lMiAlPiUNCiAgZHBseXI6OnNlbGVjdChQcm9wZXJ0eV9BcmVhLExvYW5fU3RhdHVzKSAlPiUNCiAgZ3JvdXBfYnkoUHJvcGVydHlfQXJlYSkgJT4lDQogIGNvdW50KExvYW5fU3RhdHVzKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gUHJvcGVydHlfQXJlYSwgeSA9IG4sIGZpbGwgPSBMb2FuX1N0YXR1cykpICsNCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAiZmlsbCIsY29sb3VyID0gImdyYXkyMCIsIGFscGhhPTAuNywgd2lkdGggPSAuNykgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiI0EzYjVjMCIsIiNlNWUwZDgiKSwgbGFiZWxzPWMoIk5vIiwgIlllcyIpKSsNCiAgbGFicyhmaWxsPSJMb2FuIFN0YXR1cyIpKw0KICBnZ3RpdGxlKCJQcm9wZXJ0eSBBcmVhIikrDQogIHRoZW1lX21pbmltYWwoKSsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQoNCmBgYA0KDQojIyMgWm1pZW5uYSBJbG/Fm2Npb3dlIHsudGFic2V0fQ0KDQojIyMjIEFwbGljYW50IEluY29tZQ0KPHAgc3R5bGU9InRleHQtYWxpZ246IGp1c3RpZnkiPiBXeXNva2/Fm8SHIGRvY2hvZHUgbmllIHdwxYJ5d2EgdyBpc3RvdG55bSBzdG9wbml1IG5hIGRlY3l6asSZIG8gdHltIGN6eSBrcmVkeXQgem9zdGFuaWUgcHJ6eXpuYW55LiBEb2Now7NkIG9zb2J5IGFwbGlrdWrEhWNlaiBrc3p0YcWCdHVqZSBzacSZIHBvZG9ibmllIHphcsOzd25vIGRsYSBkZWN5emppIHBvenl0eXdueWNoIGphayBpIG5lZ2F0eXdueWNoLjwvcD4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpnZ3Bsb3QoZGFuZTIsIGFlcyh4PUFwcGxpY2FudF9JbmNvbWUsIGZpbGw9TG9hbl9TdGF0dXMpKSArDQogICAgZ2VvbV9oaXN0b2dyYW0oIGNvbG9yPSJncmF5MjAiLGFscGhhPS41LCBwb3NpdGlvbiA9ICdpZGVudGl0eScpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiNBM2I1YzAiLCIjZTVlMGQ4IiksIGxhYmVscz1jKCJObyIsICJZZXMiKSkrDQogIGxhYnMoZmlsbD0iTG9hbiBTdGF0dXMiKSsNCiAgICB0aGVtZV9taW5pbWFsKCkrDQogICAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQoNCmBgYA0KDQojIyMjIENvYXBsaWNhbnQgSW5jb21lDQoNClN5dHVhY2phIHByZXplbnR1amUgc2nEmSB3IHRlbiBzYW0gc3Bvc8OzYiBkbGEgem1pZW5uZWogQ29hcHBsaWNhbnQgSW5jb21lLg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmdncGxvdChkYW5lMiwgYWVzKHg9Q29hcHBsaWNhbnRfSW5jb21lLCBmaWxsPUxvYW5fU3RhdHVzKSkgKw0KICAgIGdlb21faGlzdG9ncmFtKCBjb2xvcj0iZ3JheTIwIiwgYWxwaGE9MC41LCBwb3NpdGlvbiA9ICdpZGVudGl0eScpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiNBM2I1YzAiLCIjZTVlMGQ4IiksIGxhYmVscz1jKCJObyIsICJZZXMiKSkrDQogIGxhYnMoZmlsbD0iTG9hbiBTdGF0dXMiKSsNCiAgICB0aGVtZV9taW5pbWFsKCkrDQogICAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQpgYGANCg0KYGBge3J9DQoNCmBgYA0KDQojIyMjIExvYW4gQW1vdW50IFRlcm0NCg0KTmFqd2nEmWNlaiBrcmVkeXTDs3cgamVzdCBicmFueWNoIG5hIG9rcmVzIHJva3UuIEN6YXMgbmllIHdwxYJ5d2EgdyBzdG9wbml1IGlzdG90bnltIG5hIGRlY3l6asSZIG8gdWR6aWVsZW5pdSBrcmVkeXR1Lg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmdncGxvdChkYW5lMiwgYWVzKHg9TG9hbl9BbW91bnRfVGVybSwgZmlsbD1Mb2FuX1N0YXR1cykpICsNCiAgICBnZW9tX2hpc3RvZ3JhbSggY29sb3I9ImdyYXkyMCIsIGFscGhhPTAuNSwgcG9zaXRpb24gPSAnaWRlbnRpdHknKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjQTNiNWMwIiwiI2U1ZTBkOCIpLCBsYWJlbHM9YygiTm8iLCAiWWVzIikpKw0KICBsYWJzKGZpbGw9IkxvYW4gU3RhdHVzIikrDQogICAgdGhlbWVfbWluaW1hbCgpKw0KICAgIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KHNpemUgPSAxMiksIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KYGBgDQoNCiMjIyMgTG9hbl9BbW91bnQNCg0KV3lzb2tvxZvEhyBwb8W8eWN6a2kgbmllIGplc3QgY3p5bm5pa2llbSBkZWN5ZHVqxIVjeW0gbyB0eW0gY3p5IGtyZWR5dCB6b3N0YW5pZSBwcnp5em5hbnkuIFByb3BvcmNqZSByb3prxYJhZGFqxIUgc2nEmSB3IHBvZG9ibnkgc3Bvc8OzYi4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpnZ3Bsb3QoZGFuZTIsIGFlcyh4PUxvYW5fQW1vdW50LCBmaWxsPUxvYW5fU3RhdHVzKSkgKw0KICAgIGdlb21faGlzdG9ncmFtKCBjb2xvcj0iZ3JheTIwIiwgYWxwaGE9MC41LCBwb3NpdGlvbiA9ICdpZGVudGl0eScpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiNBM2I1YzAiLCIjZTVlMGQ4IiksIGxhYmVscz1jKCJObyIsICJZZXMiKSkrDQogIGxhYnMoZmlsbD0iTG9hbiBTdGF0dXMiKSsNCiAgeGxhYigiTG9hbiBTdGF0dXMiKSsNCiAgICB0aGVtZV9taW5pbWFsKCkrDQogICAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQogIA0KYGBgDQoNCiMjIEFuYWxpemEgd2llbG93eW1pYXJvd2ENCg0KPHAgc3R5bGU9InRleHQtYWxpZ246IGp1c3RpZnkiPiBXaWR6aW15LCDFvGUgb3NvYnkgYmV6IGhpc3RvcmlpIGtyZWR5dG93ZWogcHJha3R5Y3puaWUgbmllIG1hasSFIHN6YW5zeSBhYnkgZG9zdGFuxIUga3JlZHl0dSB3IG5hc3p5bSBiYW5rdSAod3lrMSkuIEplxZtsaSwga2xpZW50IG1hIGhpc3RvcmnEmSBrcmVkeXRvd8SFICh3eWsyKSBpIGt3b3RhIG5hIGpha8SFIGNoY2UgemFjacSFZ27EhcSHIGtyZWR5dCBuaWUgcHJ6ZWtyYWN6YSAwLDA0IHplIHN0b3N1bmt1IExvYW5fQW1vdW50LyhBcHBsaWNhbnRfSW5jb21lK0NvYXBwbGljYW50X0luY29tZSkgdG8gbWEgZHXFvGUgc3phbnPEmSBuYSBwb3p5dHluxIUgb2Rwb3dpZWTFui4gSmXFm2xpIHN0b3N1bmVrIHd5a3JhY3phIHBvbmFkIDAuMDQsIGplc3QgemR5c2t3YWxpZmlrb3dhbnkgeiBtb8W8bGl3b8WbY2kgemFjacSFZ25pxJljaWEga3JlZHl0dSwgbmF3ZXQgamXFm2xpIHBvc2lhZGEgaGlzdG9yacSZIGtyZWR5dG93xIUuIDwvcD4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpkYW5lMiAlPiUNCiAgZHJvcF9uYShDcmVkaXRfSGlzdG9yeSkgJT4lDQogIGdncGxvdCggYWVzKHg9TG9hbl9BbW91bnQvKEFwcGxpY2FudF9JbmNvbWUrQ29hcHBsaWNhbnRfSW5jb21lKSwgZmlsbD1Mb2FuX1N0YXR1cykpICsNCiAgICBnZW9tX2hpc3RvZ3JhbShjb2xvcj0iZ3JheTIwIiwgYWxwaGE9MC41LCBwb3NpdGlvbiA9ICdpZGVudGl0eScpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiNBM2I1YzAiLCIjZTVlMGQ4IiksIGxhYmVscz1jKCJObyIsICJZZXMiKSkrDQogIGxhYnMoZmlsbD0iTG9hbiBTdGF0dXMiKSsNCiAgICBmYWNldF9ncmlkKH5DcmVkaXRfSGlzdG9yeSkrDQogICAgdGhlbWVfbWluaW1hbCgpKw0KICAgIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KHNpemUgPSAxMiksIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KYGBgDQoNCiMgV2VyeWZpa2FjamEgemRvbG5vxZtjaSBrcmVkeXRvd2VqIGtsaWVudMOzdyBvY3pla3VqxIVjeWNoDQo8cCBzdHlsZT0idGV4dC1hbGlnbjoganVzdGlmeSI+IFNwcmF3ZHphbXkgcG93acSFemFuaWUgcG9tacSZZHp5IHptaWVubsSFIG9iamHFm25pYW7EhSwgYSB6bWllbm55bWkgb2JqYcWbbmlhasSFY3ltaS4NClRhayBuYXByYXdkxJkgaXN0b3RuYSBzdGF0eXN0eWN6bmllIGplc3QgamVkeW5pZSB6bWllbm5hIENyZWRpdF9IaXN0b3J5LiBNb2RlbCBsb2dpdG93eSB3IHR5bSBwcnp5cGFka3UgYnnFgmJ5IG5pZWVmZXR5d255LiBOYWxlxbx5IHdpxJljIHpuYWxlxbrEhyBpbm5lIHJvendpxIV6YW5pZSwgdyBvcGFyY2l1IG8ga3TDs3JlIGLEmWTEhSBwb2Rlam1vd2FuZSBkZWN5emplIGtyZWR5dG93ZS4gPC9wPg0KDQpgYGB7cn0NCnN1bW1hcnkoZ2xtKExvYW5fU3RhdHVzIH4gR2VuZGVyICsgQXBwbGljYW50X0luY29tZSArIE1hcnJpZWQgKyBEZXBlbmRlbnRzICsgRWR1Y2F0aW9uICsgU2VsZl9FbXBsb3llZCArIENvYXBwbGljYW50X0luY29tZSArIExvYW5fQW1vdW50ICsgTG9hbl9BbW91bnRfVGVybSArIENyZWRpdF9IaXN0b3J5ICsgUHJvcGVydHlfQXJlYSAsIGRhdGEgPSBkYW5lMiwgZmFtaWx5ID0gImJpbm9taWFsIikpIA0KDQoNCmBgYA0KDQo8cCBzdHlsZT0idGV4dC1hbGlnbjoganVzdGlmeSI+IEpha28gZHppYcWCIHVzcHJhd25pZcWEIHByb2Nlc8OzdyBkZWN5enlqbnljaCwgdyBvcGFyY2l1IG8gcHJ6ZXByb3dhZHpvbsSFIGFuYWxpesSZIGRhbnljaCBwb3N0YW5vd2lsacWbbXksIMW8ZSBrcmVkeXR5IGLEmWTEhSBwcnp5em5hd2FuZSBqZcWbbGkga2xpZW50IHpkxIVixJlkemllIG9rcmXFm2xvbsSFIGlsb8WbxIcgcHVua3TDs3cuIE1vxbxlbXkgamVkbm96bmFjem5pZSBzdHdpZXJkemnEhywgxbxlIGRvdHljaGN6YXNvd2UgZGVjeXpqZSBvIHByenl6bmF3YW5pdSBrcmVkeXTDs3cgYnnFgnkgcmFuZG9tb3dlLCBjbyBuZWdhdHl3bmllIHdwxYJ5d2HFgm8gbmEgcmVwdXRhY2rEmSBpIGVrZWt0eXdub8WbxIcgZHppYcWCYW5pYSBuYXN6ZWdvIGJhbmt1LiBQb3pixJlkemllbXkgc2nEmSBwcnp5cGFka293b3d5Y2ggZGVjeXpqaS4gRHppxJlraSBvcHJhY293YW5pdSBtb2RlbHUsIHd5a2x1Y3p5bXkgcHJ6eXBhZGtvd2UgZGVjeXpqZSBrcmVkeXRvd2UuIEtsaWVudGFtaSBuYXN6ZWdvIGJhbmt1LCBixJlkxIUga2xpZW5jaSwga3TDs3J6eSBixJlkxIUgdyBzdGFuaWUgcmVndWxvd2HEhyB6b2Jvd2nEhXphbmllLiA8L3A+DQoNCmBgYHtyfQ0KcHLDs2JhIDwtIGRhbmUyIA0KDQpgYGANCg0KKipVemFzYWRuaWVuaWUgcHVua3RhY2ppOioqDQoNCi0gICA8cCBzdHlsZT0idGV4dC1hbGlnbjoganVzdGlmeSI+ICBaIHdjemXFm25pZWpzemVqIGFuYWxpenkgd3l3bmlvc2tvd2FsacWbbXksIMW8ZSB3IG9wYXJjaXUgbyBDcmVkaXRfSGlzdG9yeSwgdyBnxYLDs3duZWogbWllcnplIHdjemXFm25pZWogem9zdGHFgnkgcG9kZWptb3dhbmUgZGVjeXpqZSBvIHVkemllbGVuaXUga3JlZHl0dS4gUG9kZWptdWrEhWMgcHJ6eXN6xYJlIGRlY3l6amUga3JlZHl0b3dlIGhpc3RvcmlhIGtyZWR5dG93YSByw7N3bmllxbwgYsSZZHppZSB6bmFjesSFY3ltIGN6eW5uaWtpZW0uPC9wPg0KDQotICAgPHAgc3R5bGU9InRleHQtYWxpZ246IGp1c3RpZnkiPiAgTmFqY3rEmcWbY2llaiBvc29ieSB6IG9ic3phcsOzdyBwb2RtaWVqc2tpY2ggb3RyenlteXdhxYJ5IGtyZWR5dHksIHLDs3duaWXFvCB0xJkgem1pZW5uxIUgcG9zdGFub3dpbGnFm215IHV3emdsxJlkbmnEhyA8L3A+DQoNCi0gICA8cCBzdHlsZT0idGV4dC1hbGlnbjoganVzdGlmeSI+ICBPc29ieSB3IHp3acSFemt1IG1hxYLFvGXFhHNraW0gb3RyenltYWrEhSBkb2RhdGtvd2UgMC4zNSBwa3QuIFPEhSB0byBvc29ieSB1c3RhYmlsaXpvd2FuZS48L3A+DQoNCi0gICA8cCBzdHlsZT0idGV4dC1hbGlnbjoganVzdGlmeSI+ICBKYWtvIG5handhxbxuaWVqc3p5IHNrxYJhZG5payB3IG9jZW5pZSBwdW5rdG93ZWogcG90cmFrdHVqZW15IHN0b3N1bmVrIHBvxbx5Y3praSBkbyBjYcWCa293aXR5Y2ggZG9jaG9kw7N3LjwvcD4NCg0KLSAgIDxwIHN0eWxlPSJ0ZXh0LWFsaWduOiBqdXN0aWZ5Ij4gIFB1bmt0eSB6YSBwcnp5Y2jDs2QgYXBsaWthbnRhLCBixJlkxIUgcHJ6eXpuYXdhbmUgemdvZG5pZSB6IHdjemXFm25pZWogcG96bnltIHJvemvFgmFkZW0sIHR6bi4gMCBwa3QgamXFm2xpIGRvY2hvZHkgc8SFIG1uaWVqc3plIG5pxbwgUTEsIDAuOCBqZcWbbGkgZG9jaG9keSBzxIUgbW5pZWpzemUgbmnFvCBtZWRpYW5hLCAxLjYgamXFm2xpIHPEhSB3acSZa3N6ZSBuacW8IG1lZGlhbmEgaSBtbmllanN6ZSBuacW8IFEzLCBpIDEuNiBwa3QgamXFm2xpIHPEhSB3acSZa3N6ZSBuacW8IFEzIDwvcD4NCg0KLSAgIDxwIHN0eWxlPSJ0ZXh0LWFsaWduOiBqdXN0aWZ5Ij4gIExpY3piYSBvc8OzYiBuYSB1dHJ6eW1hbml1IGplc3Qgb2JjacSFxbxlbmllbmllbSBkbGEgb3NvYnkgc3DFgmFjYWrEhWNlaiBrcmVkeXQsIGRsYXRlZ28gamXFm2xpIG5pZSBtYSBuaWtvZ28gbmEgdXRyenltYW5pdSBvdHJ6eW1hIDEgcGt0LCBqZcWbbGkgemHFmyBtYSAzKyBvc8OzYiBvdHJ6eW1hIDAgcGt0LjwvcD4NCg0KLSAgIDxwIHN0eWxlPSJ0ZXh0LWFsaWduOiBqdXN0aWZ5Ij4gIFB1bmt0eSB6YSBwcnp5Y2jDs2Qgd3Nww7PFgnduaW9za29kYXdjeSwgYsSZZMSFIHByenl6bmF3YW5lIG5hIHBvZHN0YXdpZSB3Y3plxZtuaWVqc3plZ28gcG96YW5lZ28gcm96a8WCYWR1IHR6bi4gMCBwa3QgamXFm2xpIGRvY2hvZHkgY29hcHBsaWNhbnRhIHPEhSBtbmllanN6ZSBuacW8IFExLCAwNjggamXFm2xpIGRvY2hvZHkgc8SFIG1uaWVqc3plIG5pxbwgbWVkaWFuYSwgMC44IGplxZtsaSBzxIUgd2nEmWtzemUgbmnFvCBtZWRpYW5hIGkgbW5pZWpzemUgbmnFvCBRMywgaSAxLjYgcGt0IGplxZtsaSBzxIUgd2nEmWtzemUgbmnFvCBRMy48L3A+DQoNCk1ha3N5bWFsbmEgbGljemJhIHB1bmt0w7N3IGRvIHpkb2J5Y2lhIHRvIDExLjA1Lg0KDQpgYGB7cn0NCnByw7NiYSA8LSBwcsOzYmEgJT4lDQogIGRwbHlyOjogIG11dGF0ZShwa3RDSCA9IGNhc2Vfd2hlbigNCiAgICAgIENyZWRpdF9IaXN0b3J5ID09IjEiIH4gMy41LA0KICAgICAgQ3JlZGl0X0hpc3RvcnkgIT0gIjEiIH4gMCApKQ0KDQoNCnByw7NiYSA8LSBwcsOzYmEgJT4lDQogIGRwbHlyOjogIG11dGF0ZShwa3RTZW1pID0gY2FzZV93aGVuKA0KICAgICAgUHJvcGVydHlfQXJlYSA9PSJTZW1pdXJiYW4iIH4gMC4zLA0KICAgICAgUHJvcGVydHlfQXJlYSAhPSAiU2VtaXVyYmFuIiB+IDAgKSkNCg0KcHLDs2JhIDwtIHByw7NiYSAlPiUNCiAgZHBseXI6OiAgbXV0YXRlKHBrdEdlbiA9IGNhc2Vfd2hlbigNCiAgICAgIEdlbmRlciA9PSJNYWxlIiB+IDAuMiwNCiAgICAgIEdlbmRlciAhPSAiTWFsZSIgfiAwICkpDQoNCnByw7NiYSA8LSBwcsOzYmEgJT4lDQogIGRwbHlyOjogIG11dGF0ZShwa3RNYXIgPSBjYXNlX3doZW4oDQogICAgICBNYXJyaWVkID09IlllcyIgfiAwLjM1LA0KICAgICAgTWFycmllZCAhPSAiWWVzIiB+IDAgKSkNCg0KcHLDs2JhIDwtIHByw7NiYSAlPiUNCiAgbXV0YXRlKCJTS1oiPUxvYW5fQW1vdW50LyhBcHBsaWNhbnRfSW5jb21lK0NvYXBwbGljYW50X0luY29tZSkpICU+JQ0KICBtdXRhdGUocGt0U0taPSBjYXNlX3doZW4oIFNLWiA+IDAuMDAgJiBTS1ogPDAuMDMgfiAzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNLWiA+IDAuMDMgJiBTS1ogPDAuMDM1IH4gMi41LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNLWiA+IDAuMzUgfiAtOCApKQ0KDQpwcsOzYmEgPC0gcHLDs2JhICU+JQ0KICBkcGx5cjo6IG11dGF0ZShwa3RJTkM9IGNhc2Vfd2hlbiggQXBwbGljYW50X0luY29tZSA+PSAgMCAgICYgQXBwbGljYW50X0luY29tZSA8IDI3NjQgfiAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQXBwbGljYW50X0luY29tZSA+PSAyNzY0ICYgQXBwbGljYW50X0luY29tZSA8IDQxMDQgfiAwLjgsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBcHBsaWNhbnRfSW5jb21lID49IDQxMDQgJiBBcHBsaWNhbnRfSW5jb21lIDwgNDkzMSB+IDEuNiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFwcGxpY2FudF9JbmNvbWUgPj0gNDkzMSB+IDIpKQ0KDQpwcsOzYmEgPC0gcHLDs2JhICU+JQ0KICBtdXRhdGUocGt0REVQPSBjYXNlX3doZW4oIERlcGVuZGVudHMgPT0iMCIgfiAwLjcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgRGVwZW5kZW50cyA9PSIxIiB+IDAuNCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBEZXBlbmRlbnRzID09IjIiIH4gMC4yLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIERlcGVuZGVudHMgPT0iMysiIH4gMCkpDQoNCg0KcHLDs2JhIDwtIHByw7NiYSAlPiUNCiAgbXV0YXRlKHBrdENPQVBQPSBjYXNlX3doZW4oIENvYXBwbGljYW50X0luY29tZSA9PSAwIH4gMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENvYXBwbGljYW50X0luY29tZSA+IDAgICAgJiBDb2FwcGxpY2FudF9JbmNvbWUgPCAxNjI1IH4gMC4yLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ29hcHBsaWNhbnRfSW5jb21lID49IDE2MjUgJiBDb2FwcGxpY2FudF9JbmNvbWUgPCAyMDc5IH4gMC40LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ29hcHBsaWNhbnRfSW5jb21lID49IDIwNzkgJiBDb2FwcGxpY2FudF9JbmNvbWUgPCAyODU3IH4gMC42LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ29hcHBsaWNhbnRfSW5jb21lID49IDI4NTcgfiAxKSkNCg0KDQpwcsOzYmEgPC0gcHLDs2JhICU+JQ0KIG11dGF0ZSh3eW5payA9IHBrdENIICsgcGt0U2VtaSArIHBrdEdlbiArIHBrdFNLWisgcGt0TWFyKyBwa3RJTkMrIHBrdERFUCsgcGt0Q09BUFApDQoNCnByw7NiYSAgJT4lDQogIGRwbHlyOjogc2VsZWN0KHd5bmlrLCBMb2FuX1N0YXR1cykgJT4lDQogIGdyb3VwX2J5KExvYW5fU3RhdHVzKSAlPiUNCiAgZHJvcF9uYSgpICU+JQ0KICBkcGx5cjo6c3VtbWFyaXNlKG1lYW4od3luaWspLCBtaW4od3luaWspLCBtYXgod3luaWspKSAgJT4lDQogIGZvcm1hdHRhYmxlKGFsaWduID0iYyIpDQoNCg0KcHLDs2JhIDwtIHByw7NiYSAlPiUNCiAgdHJhbnNtdXRlKExvYW5fSUQsIEdlbmRlciwgQXBwbGljYW50X0luY29tZSAsIE1hcnJpZWQgLCBEZXBlbmRlbnRzICwgRWR1Y2F0aW9uICwgU2VsZl9FbXBsb3llZCAsIENvYXBwbGljYW50X0luY29tZSAsIExvYW5fQW1vdW50ICwgTG9hbl9BbW91bnRfVGVybSAsIENyZWRpdF9IaXN0b3J5ICwgUHJvcGVydHlfQXJlYSwgTG9hbl9TdGF0dXMsIHd5bmlrKQ0KDQpwcsOzYmEgICU+JQ0KICBkcGx5cjo6IHNlbGVjdCh3eW5paywgTG9hbl9TdGF0dXMpICU+JQ0KICBncm91cF9ieShMb2FuX1N0YXR1cykgJT4lDQogIGRyb3BfbmEoKSAlPiUNCiAgZ2dwbG90KGFlcyh3eW5paywgY29sb3VyPUxvYW5fU3RhdHVzLCBncm91cD1Mb2FuX1N0YXR1cywgZmlsbD1Mb2FuX1N0YXR1cykpICsNCiAgICBnZW9tX2RlbnNpdHkoY29sb3I9ImdyYXkyMCIsIGFscGhhPTAuNSwgcG9zaXRpb24gPSAnaWRlbnRpdHknKSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI0EzYjVjMCIsICIjZTVlMGQ4IikpICsNCiAgICB0aGVtZV9taW5pbWFsKCkrDQogICAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQoNCmBgYA0KDQo8cCBzdHlsZT0idGV4dC1hbGlnbjoganVzdGlmeSI+ICBXeW5pa2kgc8SFIGJhcmR6byBjaWVrYXdlLCBwb25pZXdhxbwgamVkbmEgY3rEmcWbxIcgZGVjeXpqaSBvIG5lZ2F0eXdueWNoLCB6b3N0YcWCYSBzxYJ1c3puaWUgb2RyenVjb25hIChvZCAwIGRvIDcgcGt0KSwgYWxlIHDDs8W6bmllaiB3aWR6aW15LCDFvGUgeiBqYWtpZWdvxZsgcG93b2R1IHNwb3JhIGN6xJnFm8SHIG9zw7NiLCBrdMOzcmEgemRvYnnFgmEgem5hY3rEhWPEhSBpbG/Fm8SHIHB1bmt0w7N3LCBtaWHFgmEgbmllc8WCdXN6bmllIG9kcnp1Y29uZSB3bmlvc2tpIChsdWIgeiBuaWV6bmFueWNoIG5hbSBwb3dvZMOzdywgd3lrcmFjemFqxIVjeWNoIHBvemEgemFrcmVzIGRvc3TEmXBueWNoIGRhbnljaC0gbnAuIHRlIG9zb2J5IG1vZ8WCeSBiecSHIGthcmFuZSwgY28gbmEgc3RhcmNpZSBza3JlxZtsYSBqZSB6IG1vxbxsaXdvxZtjaSBvdHJ6eW1hbmlhIGtyZWR5dHUsIGplZG5hayBuaWUgem9zdGHFgnkgbmFtIGRvc3RhcmN6b25lIHRlZ28gdHlwdSBpbmZvcm1hY2plLCBwcnpleiBjbyBvYm5pxbxvbmEgamVzdCBlZmVrdHl3bm/Fm8SHIG5hc3plaiBhbmFsaXp5KS4gSmXFm2xpIG5hc3ogZHppYcWCIHV6eXNrYSBpbmZvcm1hY2plIG5hIHRlbiB0ZW1hdCwgemFrdHVhbGl6dWplIG9iZWNuaWUgcHJ6eWrEmXR5IG1vZGVsIHBvZGVqbW93YW5pYSBkZWN5emppLiBDbyBkbyB3bmlvc2vDs3cgcm96cGF0cnpvbnljaCBwb3p5dHl3bmllLCBuaWUgbWEgbmllxZtjaXPFgm/Fm2NpLiBXaWR6aW15LCDFvGUgdyB0ZWogZ3J1cGllIHPEhSB0eWxrbyBhcGxpa2FuY2ksIGt0w7NyenkgemRvYnlsaSBwb3d5xbxlaiA3IHBrdCwgaSB0eW0gc2FteW0gZG9zdGF3YWxpIGtyZWR5dC4gPC9wPg0KDQo8YnI+IDxicj4NCg0KIyMgRGVjeXpqZSBvIHByenl6bmFuaXUga3JlZHl0dSBrbGllbnTDs3cgb2N6ZWt1asSFY3ljaA0KDQoqKkVUQVAgMSoqDQoNCldlcnlmaWthY2phLCBjenkgZGFuZSB6b3N0YcWCeSB1enVwZcWCbmlvbmUgcG9wcmF3bmllLCBjenkgcG9kc3Rhd293ZSB6YcWCb8W8bmllbmlhIHPEhSBzcGXFgm5pb25lLiBEd8OzY2gga2xpZW50w7N3IG5pZSB3eWthenVqZSDFvGFkbnljaCBkb2Nob2TDs3csIGNvIHRlxbwgbmEgd3N0xJlwbmllIGR5c2t3YWxpZmlrdWplIGljaCB6IGRhbHN6ZWogd2VyeWZpa2FjamkgemRvbG5vxZtjaQ0Ka3JlZHl0b3dlag0KDQpgYGB7cn0NCnN1bW1hcnkoY29uZnJvbnQoZGFuZTEsIHJ1bGVzKSkgJT4lDQogIGZvcm1hdHRhYmxlKGFsaWduID0iYyIpDQoNCmBgYA0KDQoqKkVUQVAgMioqDQoNClphc3Rvc293YW5pZSBwdW5rdGFjamkgZG8gd2VyeWZpa2FjamkgY3p5IGtsaWVudCBtYSB6ZG9sbm/Fm8SHIGtyZWR5dG93xIUNCg0KYGBge3J9DQoNCmRhbmUxIDwtIGRhbmUxICU+JQ0KICBkcGx5cjo6ICBtdXRhdGUocGt0Q0ggPSBjYXNlX3doZW4oDQogICAgICBDcmVkaXRfSGlzdG9yeSA9PSIxIiB+IDMuNSwNCiAgICAgIENyZWRpdF9IaXN0b3J5ICE9ICIxIiB+IDAgKSkNCg0KZGFuZTEgPC0gZGFuZTEgJT4lDQogIGRwbHlyOjogIG11dGF0ZShwa3RTZW1pID0gY2FzZV93aGVuKA0KICAgICAgUHJvcGVydHlfQXJlYSA9PSJTZW1pdXJiYW4iIH4gMC4zLA0KICAgICAgUHJvcGVydHlfQXJlYSAhPSAiU2VtaXVyYmFuIiB+IDAgKSkNCg0KZGFuZTEgPC0gZGFuZTEgJT4lDQogIGRwbHlyOjogIG11dGF0ZShwa3RHZW4gPSBjYXNlX3doZW4oDQogICAgICBHZW5kZXIgPT0iTWFsZSIgfiAwLjIsDQogICAgICBHZW5kZXIgIT0gIk1hbGUiIH4gMCApKQ0KDQpkYW5lMSA8LSBkYW5lMSAlPiUNCiAgZHBseXI6OiAgbXV0YXRlKHBrdE1hciA9IGNhc2Vfd2hlbigNCiAgICAgIE1hcnJpZWQgPT0iWWVzIiB+IDAuMzUsDQogICAgICBNYXJyaWVkICE9ICJZZXMiIH4gMCApKQ0KDQoNCmRhbmUxIDwtIGRhbmUxICU+JQ0KICBtdXRhdGUoIlNLWiI9TG9hbl9BbW91bnQvKEFwcGxpY2FudF9JbmNvbWUrQ29hcHBsaWNhbnRfSW5jb21lKSkgJT4lDQogIG11dGF0ZShwa3RTS1o9IGNhc2Vfd2hlbiggU0taID4gMC4wMCAmIFNLWiA8MC4wMyB+IDMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgU0taID4gMC4wMyAmIFNLWiA8MC4wMzUgfiAyLjUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgU0taID4gMC4zNSB+IC04ICkpDQoNCmRhbmUxIDwtIGRhbmUxICU+JQ0KICBkcGx5cjo6IG11dGF0ZShwa3RJTkM9IGNhc2Vfd2hlbiggQXBwbGljYW50X0luY29tZSA+PSAgMCAgICYgQXBwbGljYW50X0luY29tZSA8IDI3NjQgfiAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQXBwbGljYW50X0luY29tZSA+PSAyNzY0ICYgQXBwbGljYW50X0luY29tZSA8IDQxMDQgfiAwLjgsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBcHBsaWNhbnRfSW5jb21lID49IDQxMDQgJiBBcHBsaWNhbnRfSW5jb21lIDwgNDkzMSB+IDEuNiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFwcGxpY2FudF9JbmNvbWUgPj0gNDkzMSB+IDIpKQ0KDQpkYW5lMSA8LSBkYW5lMSAlPiUNCiAgbXV0YXRlKHBrdERFUD0gY2FzZV93aGVuKCBEZXBlbmRlbnRzID09IjAiIH4gMC43LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIERlcGVuZGVudHMgPT0iMSIgfiAwLjQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgRGVwZW5kZW50cyA9PSIyIiB+IDAuMiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBEZXBlbmRlbnRzID09IjMrIiB+IDApKQ0KDQpkYW5lMSA8LSBkYW5lMSAlPiUNCiAgbXV0YXRlKHBrdENPQVBQPSBjYXNlX3doZW4oIENvYXBwbGljYW50X0luY29tZSA9PSAwIH4gMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENvYXBwbGljYW50X0luY29tZSA+IDAgICAgJiBDb2FwcGxpY2FudF9JbmNvbWUgPCAxNjI1IH4gMC4yLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ29hcHBsaWNhbnRfSW5jb21lID49IDE2MjUgJiBDb2FwcGxpY2FudF9JbmNvbWUgPCAyMDc5IH4gMC40LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ29hcHBsaWNhbnRfSW5jb21lID49IDIwNzkgJiBDb2FwcGxpY2FudF9JbmNvbWUgPCAyODU3IH4gMC42LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ29hcHBsaWNhbnRfSW5jb21lID49IDI4NTcgfiAxKSkNCg0KDQpkYW5lMSA8LSBkYW5lMSAlPiUNCiBtdXRhdGUod3luaWsgPSBwa3RDSCArIHBrdFNlbWkgKyBwa3RHZW4gKyBwa3RTS1orIHBrdE1hcisgcGt0SU5DKyBwa3RERVArIHBrdENPQVBQKQ0KDQoNCmRhbmUxIDwtIGRhbmUxICU+JQ0KICBtdXRhdGUoTG9hbl9TdGF0dXM9Y2FzZV93aGVuKHd5bmlrID49Ny41MSB+ICJZIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3eW5payA8IDcuNTEgfiAiTiIpKQ0KDQpkYW5lMSAgJT4lDQogIGRwbHlyOjogc2VsZWN0KHd5bmlrLCBMb2FuX1N0YXR1cykgJT4lDQogIGdyb3VwX2J5KExvYW5fU3RhdHVzKSAlPiUNCiAgZHJvcF9uYSgpICU+JQ0KICBkcGx5cjo6c3VtbWFyaXNlKG1lYW4od3luaWspLCBtaW4od3luaWspLCBtYXgod3luaWspKSAlPiUNCiAgZm9ybWF0dGFibGUoYWxpZ24gPSJjIikgDQoNCg0KZGFuZTEgPC0gZGFuZTEgJT4lDQogIHRyYW5zbXV0ZShMb2FuX0lELCBHZW5kZXIsIEFwcGxpY2FudF9JbmNvbWUgLCBNYXJyaWVkICwgRGVwZW5kZW50cyAsIEVkdWNhdGlvbiAsIFNlbGZfRW1wbG95ZWQgLCBDb2FwcGxpY2FudF9JbmNvbWUgLCBMb2FuX0Ftb3VudCAsIExvYW5fQW1vdW50X1Rlcm0gLCBDcmVkaXRfSGlzdG9yeSAsIFByb3BlcnR5X0FyZWEsIExvYW5fU3RhdHVzLCB3eW5paykNCg0KZGFuZTEgICU+JQ0KICBkcGx5cjo6IHNlbGVjdChMb2FuX1N0YXR1cykgJT4lDQogIGdyb3VwX2J5KExvYW5fU3RhdHVzKSAlPiUNCiAgZHJvcF9uYSgpICU+JQ0KICBjb3VudChMb2FuX1N0YXR1cykgJT4lDQogIGZvcm1hdHRhYmxlKGFsaWduID0iYyIpIA0KDQoNCmBgYA0KDQo8cCBzdHlsZT0idGV4dC1hbGlnbjoganVzdGlmeSI+IFcgb3BhcmNpdSBvIG9wcmFjb3dhbsSFIHB1bmt0YWNqxJkgNjEgd25pb3Nrw7N3IHpvc3RhxYJvIG9kcnp1Y29ueWNoLCAyMjcgemFha2NlcHRvd2FueWNoLiBEemnEmWtpIG9wcmFjb3dhbmVqIHB1bmt0YWNqaSwgZGVjeXpqZSBvIHVkemllbGFuaXUga3JlZHl0w7N3IGLEmWTEhSBtaWHFgnkgdXphc2FkbmllbmllLCBza3LDs2NpbXkgY3phcyBwb3RyemVibnkgZG8gcG9kasSZY2lhIGRlY3l6amkgb3JheiB1cG9yYW15IHNpxJkgeiBwcm9ibGVtZW0gcHJ6eXBhZGtvd2/Fm2NpLiA8L3A+DQoNCjxicj4NCg0KIyBXaXNrb3dhbmllIHN0YXR5c3R5Y3puZQ0KDQoqKlBZVEFOSUEgQkFEQVdDWkU6KioNCjxicj4NCi0gICBDenkgd3lzb2tvxZvEhyBrcmVkeXR1IHphbGXFvHkgb2QgY2HFgmtvd2l0ZWdvIGRvY2hvZHUgKGFwbGlrYW50YSBpIHdzcMOzxYJhcGxpa2FudGEpPyBTcHJhd2R6YW5pZSB6YSBwb21vY8SFIEFOT1ZBIOKAkyB0ZXN0b3dhbmllIGlzdG90bm/Fm2NpIHdzcMOzxYJjenlubmlrw7N3IHJlZ3Jlc2ppIGxpbmlvd2VqDQoNCmBgYHtyfQ0KbW9kIDwtIHN0YXRzOjphb3YoTG9hbl9BbW91bnR+IEFwcGxpY2FudF9JbmNvbWUgKyBDb2FwcGxpY2FudF9JbmNvbWUsIGRhbmUyKQ0KZ2djb2Vmc3RhdHMobW9kKQ0KDQpgYGANCjxwIHN0eWxlPSJ0ZXh0LWFsaWduOiBqdXN0aWZ5Ij4NCkJhcmR6byBuaXNraSB3c3DDs8WCY3p5bm5payBwdmFsdWUgxZt3aWFkY3p5IG8gaXN0b3Rub8WbY2kgcGFyYW1ldHLDs3cuIEltIG1uaWVqc3plIHdhcnRvxZtjaSBLcnl0ZXJpdW0gSW5mb3JtYWN5am5lZ28gQWthaWtlJ2EgKEFJQykgaSBLcnl0ZXJpdW0gSW5mb3JtYWN5am5lZ28gQmF5ZXNhIChCSUMpLCB0eW0g4oCebGVwc3p54oCdIGplc3QgbW9kZWwuIFBvemlvbWUgcHJvc3RlIHBva2F6dWrEhSBwcnplZHppYcWCeSA5NSUgb2JzZXJ3YWNqaSBvZHBvd2llZG5pbyBkbGEgem1pZW5uZWogQ29hcHBsaWNhbnQgSW5jb21lIGkgQXBwbGljYW50IEluY29tZS4gUG9uYWR0byBwb2themFuYSBqZXN0IHN0YXR5c3R5a2EgdGVzdHUgRiBpIENoaSBkbGEgd3licmFueWNoIHppZW5ueWNoLiA8L3A+DQoNCiMjIFduaW9za2kga2/FhGNvd2UNCg0KPHAgc3R5bGU9InRleHQtYWxpZ246IGp1c3RpZnkiPiBXIHByb2Nlc2llIGFuYWxpenkgRURBIG9rYXphxYJvIHNpxJksIMW8ZSB3eXNva2/Fm8SHIGRvY2hvZMOzdyBwcmFrdHljem5pZSBuaWUgZGVjeWR1amUgbyB6ZG9sbm/Fm2NpIGtyZWR5dG93ZWoga2xpZW50w7N3LCB0b3RlxbwgbmFzemEgaGl0cG90ZXphIHpvc3RhxYJhIG9kcnp1Y29uYS4gVyBvcGFyY2l1IG8gcHJ6ZXByb3dhZHpvbsSFIGFuYWxpesSZIG9yYXogdXphc2FkbmlvbmUgcHJ6ZXPFgmFua2kgb3ByYWNvd2FsacWbbXkgbW9kZWwsIGt0w7NyeSBwcnp5em5hamUgcHVua3R5IHcgb3BhcmNpdSBvIG9rcmXFm2xvbmUga3J5dGVyaWEgaSBuYSB0ZWogcG9kc3Rhd2llIHpvc3RhamUgcG9kasSZdGEgZGVjeXpqYSBvIHVkemllbGVuaXUga3JlZHl0dS4gKE5hc3ogbW9kZWwgd3ltYWdhIGplZG5hayBkb3ByYWNvd2FuaWEsIHN6Y3plZ8OzbG5pZSB3IGtvbmtla8WbY2llIG9zw7NiLCBrdMOzcmUgb3RyenltYcWCeSB3eXNva8SFIGxpY3pixJkgcHVua3TDs3csIGEgeiBuaWV6bmFueWNoIG5hbSBwb3dvZMOzdyBpY2ggd25pb3NraSB6b3N0YcWCeSBvZHJ6dWNvbmUuIFBvIHV3YXpnbMSZZG5pZW5pdSB0YWtpY2ggZG9kYXRrb3d5Y2ggZGFueWNoLCB3eWtyYWN6YWrEhWN5Y2ggcG96YSB6YWtyZXMgZG9zdMSZcG55Y2ggb2JlY25pZSBkYW55Y2gsIG1vZGVsIG5hbGXFvGHFgm9ieSB1YWt0dWFsbmnEhywgxbxlYnkgc3RhxYIgc2nEmSBiYXJkemllaiBlZmVrdHl3bnkpLiA8L3A+DQo=