Step 1: Get data

[1] "/Users/anitaowens/R_Programming/HI Bootcamp"
df <- read.csv("~/R_Programming/HI Bootcamp/Telco-Customer-Churn.csv")

Step 2: Explore Data

Spot problems

str(df)
'data.frame':   7043 obs. of  21 variables:
 $ customerID      : chr  "7590-VHVEG" "5575-GNVDE" "3668-QPYBK" "7795-CFOCW" ...
 $ gender          : chr  "Female" "Male" "Male" "Male" ...
 $ SeniorCitizen   : int  0 0 0 0 0 0 0 0 0 0 ...
 $ Partner         : chr  "Yes" "No" "No" "No" ...
 $ Dependents      : chr  "No" "No" "No" "No" ...
 $ tenure          : int  1 34 2 45 2 8 22 10 28 62 ...
 $ PhoneService    : chr  "No" "Yes" "Yes" "No" ...
 $ MultipleLines   : chr  "No phone service" "No" "No" "No phone service" ...
 $ InternetService : chr  "DSL" "DSL" "DSL" "DSL" ...
 $ OnlineSecurity  : chr  "No" "Yes" "Yes" "Yes" ...
 $ OnlineBackup    : chr  "Yes" "No" "Yes" "No" ...
 $ DeviceProtection: chr  "No" "Yes" "No" "Yes" ...
 $ TechSupport     : chr  "No" "No" "No" "Yes" ...
 $ StreamingTV     : chr  "No" "No" "No" "No" ...
 $ StreamingMovies : chr  "No" "No" "No" "No" ...
 $ Contract        : chr  "Month-to-month" "One year" "Month-to-month" "One year" ...
 $ PaperlessBilling: chr  "Yes" "No" "Yes" "No" ...
 $ PaymentMethod   : chr  "Electronic check" "Mailed check" "Mailed check" "Bank transfer (automatic)" ...
 $ MonthlyCharges  : num  29.9 57 53.9 42.3 70.7 ...
 $ TotalCharges    : num  29.9 1889.5 108.2 1840.8 151.7 ...
 $ Churn           : chr  "No" "No" "Yes" "No" ...
#7043 obs and 21 vars
#Explore data interactively with Explore package
#explore(df) #uncomment to run

#we have several different measures in the dataset
#churn rate baseline
(churn.base <- df %>% 
  group_by(Churn) %>% 
  count(Churn) %>% 
  mutate(perc = n/nrow(df) * 100) %>% 
  rename(customers = n))



#ordered bar plots - need to arrange column in desc order b4 plotting
ggbarplot(churn.base, x = "customers", y = "perc",               
         fill = "Churn", 
         color = "white",
         palette = "jco",
          sort.val = "desc",          # Sort the value in dscending order
          sort.by.groups = FALSE,     # Don't sort inside each group
          x.text.angle = 0,          # Rotate vertically x axis texts
          legend = "right",
          xlab = " Churn",
          ylab = " Percentage",
          label = paste(round(churn.base$perc,0),"%", sep = ""),
          label.pos = "out",
          title = "Churn rate baseline",
          ggtheme = theme_minimal()
         )


table(df$Churn)

  No  Yes 
5174 1869 
prop.table(table(df$Churn))

       No       Yes 
0.7346301 0.2653699 
#Our churn rate is 27%

#gender on x axis
box1 <- ggplot(data = df, aes(x = gender, y = MonthlyCharges, fill = gender))+geom_boxplot() ##+ stat_summary(fun=mean, geom="point", shape=20, size=8, color="red", fill="red")

box2 <- ggplot(data = df, aes(x = gender, y = TotalCharges, fill = gender))+geom_boxplot()

box3 <- ggplot(data = df, aes(x = gender, y = tenure, fill = gender))+geom_boxplot()


#phone service on x axis
box4 <- ggplot(data = df, aes(x = PhoneService, y = MonthlyCharges, fill = PhoneService))+geom_boxplot()

box5 <- ggplot(data = df, aes(x = PhoneService, y = TotalCharges, fill = PhoneService))+geom_boxplot()

box6 <- ggplot(data = df, aes(x = PhoneService, y = tenure, fill = PhoneService))+geom_boxplot()

#contract on x axis
box7 <- ggplot(data = df, aes(x = Contract, y = MonthlyCharges, fill = Contract))+geom_boxplot() + stat_summary(fun=mean, geom="point", shape=20, size=8, color="red", fill="red") + coord_flip()
#Similar avg monthly charges across all the different term plans,
#with the largest variability amongst 1-year contract holders

box8 <- ggplot(data = df, aes(x = Contract, y = TotalCharges, fill = Contract))+geom_boxplot() # some missing values
#higher than avg total charges for those on 2-year contracts

box9 <- ggplot(data = df, aes(x = Contract, y = tenure, fill = Contract))+geom_boxplot()
#The longer the contract the longer the average customer tenure with -year contract giving the highest avg tenure. Makes sense!

sjPlot::plot_grid(box1, box2, box3, box4, labels="AUTO")
Error in sjPlot::plot_grid(box1, box2, box3, box4, labels = "AUTO") : 
  unused arguments (box4, labels = "AUTO")

bi1 <- ggplot(data = df, aes(x = factor(Churn), y = tenure, fill = Churn)) +geom_boxplot()
#High churn for those with lower tenures

bi2 <- ggplot(data = df, aes(x = factor(Churn), y = MonthlyCharges, fill = Churn))+geom_boxplot()
#Higher churn for those with higher than avg monthly charges

bi3 <- ggplot(data = df, aes(x = factor(Churn), y = TotalCharges, fill = Churn))+geom_boxplot()
#Lower churn for those with lower than avg total charges

plot_grid(bi1, bi2, bi3, labels = "AUTO")

bibar1 <- ggplot(data = df, aes(x=gender, fill = factor(Churn))) + geom_bar(position = "fill") + scale_fill_manual(values = c("#1b9e77", "#d95f02")) + theme(
    plot.background = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    panel.border = element_blank(),
    plot.title = element_text(hjust = 0.5),
    text=element_text(size=14,  family="Helvetica")) + labs(x = " ", title = "Churn by gender")
#no difference by gender


bibar2 <- ggplot(data = df, aes(x=Contract, fill = factor(Churn)))+ geom_bar(position = "fill") + scale_fill_manual(values = c("#1b9e77", "#d95f02")) + theme(
    plot.background = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    panel.border = element_blank(),
    plot.title = element_text(hjust = 0.5),
    text=element_text(size=14,  family="Helvetica")) + labs(x = " ", title = "Contract type") + coord_flip()
#We have significantly higher churn than for those on month-to-month contracts


bibar3 <- ggplot(data = df, aes(x=factor(SeniorCitizen), fill = factor(Churn)))+ geom_bar(position = "fill") + scale_fill_manual(values = c("#1b9e77", "#d95f02")) + theme(
    plot.background = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    panel.border = element_blank(),
    plot.title = element_text(hjust = 0.5),
    text=element_text(size=14,  family="Helvetica")) + labs(x = " ", title = "Is a senior citizen")
#Higher churn for Senior Citizens


bibar4  <- ggplot(data = df, aes(x=PaperlessBilling, fill = factor(Churn))) + geom_bar(position = "fill") + scale_fill_manual(values = c("#1b9e77", "#d95f02")) + theme(
    plot.background = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    panel.border = element_blank(),
    plot.title = element_text(hjust = 0.5),
    text=element_text(size=14,  family="Helvetica")) + labs(x = " ", title = "Has paperless billing")
#higher churn rate for those on paperless billing

bibar5 <- ggplot(data = df, aes(x=PaymentMethod, fill = factor(Churn))) + geom_bar(position = "fill") + scale_fill_manual(values = c("#1b9e77", "#d95f02")) + theme(
    plot.background = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    panel.border = element_blank(),
    plot.title = element_text(hjust = 0.5),
    text=element_text(size=12,  family="Helvetica")) + labs(x = " ", title = "Payment Method") + coord_flip() 
#higher churn rate for those who pay by electronic
#check - lowest churn rates among those with
#automatic billing.

bibar6 <- ggplot(data = df, aes(x=PhoneService, fill = factor(Churn))) + geom_bar(position = "fill") + labs(title = "By phone service")
#no difference by phone service


bibar7 <- ggplot(data = df, aes(x=Partner, fill = factor(Churn))) + geom_bar(position = "fill") + scale_fill_manual(values = c("#1b9e77", "#d95f02")) + theme(
    plot.background = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    panel.border = element_blank(),
    plot.title = element_text(hjust = 0.5),
    text=element_text(size=12,  family="Helvetica")) + labs(x = " ", title = "Has a partner") 
#slightly higher churn rate for singles


bibar8 <-ggplot(data = df, aes(x=Dependents, fill = factor(Churn))) + geom_bar(position = "fill") + scale_fill_manual(values = c("#1b9e77", "#d95f02")) + theme(
    plot.background = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    panel.border = element_blank(),
    plot.title = element_text(hjust = 0.5),
    text=element_text(size=12,  family="Helvetica")) + labs(x = " ", title = "Has dependents") + coord_flip() 
#higher churn rate for those with no dependents

bibar9 <- ggplot(data = df, aes(x=MultipleLines, fill = factor(Churn))) + geom_bar(position = "fill") + labs(title = "Has multiple phone lines")
##very little difference if have multiple lines


bibar10 <- ggplot(data = df, aes(x=InternetService, fill = factor(Churn))) + geom_bar(position = "fill") + scale_fill_manual(values = c("#1b9e77", "#d95f02")) + theme(
    plot.background = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    panel.border = element_blank(),
    plot.title = element_text(hjust = 0.5),
    text=element_text(size=12,  family="Helvetica")) + labs(x = " ", title = "Internet service") + coord_flip() 
#Much higher churn if have fiber optic internet service


bibar11 <- ggplot(data = df, aes(x=OnlineSecurity, fill = factor(Churn))) + geom_bar(position = "fill") + coord_flip() + labs(title = "Online security")
#higher churn if have no online security

bibar12 <- ggplot(data = df, aes(x=OnlineBackup, fill = factor(Churn))) + geom_bar(position = "fill") + coord_flip() + labs(title = "Online backup")
#higher churn for those who have no online backup

bibar13 <- ggplot(data = df, aes(x=DeviceProtection, fill = factor(Churn))) + geom_bar(position = "fill") + coord_flip() + labs(title = "Device protection")
#higher churn for those who have no device protection

bibar14 <- ggplot(data = df, aes(x=TechSupport, fill = factor(Churn))) + geom_bar(position = "fill") + coord_flip() + labs(title = "Tech support")
#higher churn for those with no tech support

bibar15 <- ggplot(data = df, aes(x=StreamingTV, fill = factor(Churn))) + geom_bar(position = "fill") + labs(title = "Streaming TV")
#no difference among TV streamers - but very low churners if you don't have internet service

bibar16 <- ggplot(data = df, aes(x=StreamingMovies, fill = factor(Churn))) + geom_bar(position = "fill") + labs(title = "Streaming movies")
#no difference among movie streamers - but very low churners if you don't have internet service


plot_grid(bibar1, bibar2, bibar3, bibar4,
          bibar5, bibar6,
          labels = "AUTO")

plot_grid(bibar7, bibar8,
          bibar9, bibar10,
          labels = "AUTO")

plot_grid(bibar11, bibar12, bibar13,
          labels = "AUTO")

plot_grid(bibar14,
          bibar15, bibar16,
          labels = "AUTO")

sp1 <- ggplot(data = df, aes(x=MonthlyCharges, y=tenure, color=factor(Churn))) + geom_point(alpha = 0.5) + geom_smooth(method=lm) + labs(title = " ")
#lots of vertical lines - no horizontal patterns
#suggests that monthly charges has some association with churn as the churned customers are heavily represented on the higher end of the monthly charges
#there is most likely a higher likelihood of churn once you reach a certain point on the monthly fee


#believe that total charges can also be a proxy for tenure-hence the triangle looking scatterplot
sp2 <- ggplot(data = df, aes(x=TotalCharges, y=tenure, color=factor(Churn))) + geom_point(alpha = 0.5) +geom_smooth(method=lm) + labs(title = " ")

#histogram of tenure
hist(df$tenure)

#log transformation
sp3 <- ggplot(data = df, aes(x=MonthlyCharges, y=tenure, color=factor(Churn))) + geom_point(alpha = 0.5) + geom_smooth(method=lm) + scale_x_log10() + scale_y_log10() + labs(title = "Log transformation")

sp4 <- ggplot(data = df, aes(x=TotalCharges, y=tenure, color=factor(Churn))) + geom_point(alpha = 0.5) +geom_smooth(method=lm) + scale_x_log10() + labs(title = "Log transformation")

#awesome looking charts
plot_grid(sp1, sp2, sp3, sp4, labels="AUTO")

#Tenure and total charges are possibly collinear or bordering on collinearity!

#Internet Service
CrossTable(df$InternetService, df$Churn, digits=2, prop.c = TRUE,
  prop.r = TRUE, prop.t = FALSE, chisq = FALSE, format = "SAS", expected = FALSE)
#

table(df$InternetService, df$Churn)

round(addmargins(prop.table(table(df$InternetService, df$Churn))),3)


#Statistical test
chisq.test(df$Churn, df$InternetService)
#Contract
CrossTable(df$Contract, df$Churn, digits=2, prop.c = TRUE,
  prop.r = TRUE, prop.t = FALSE, chisq = FALSE, format = "SAS", expected = FALSE)
#

table(df$Contract, df$Churn)

round(addmargins(prop.table(table(df$Contract, df$Churn))),3)

#statistical test
chisq.test(df$Churn, df$Contract)

#Let's take age and put it into buckets
#let's get min and max Monthly Charge 
min(df$MonthlyCharges)
[1] 18.25
max(df$MonthlyCharges)
[1] 118.75
mean(df$MonthlyCharges)
[1] 64.79821
median(df$MonthlyCharges)
[1] 70.35
#quartiles
ggplot(df, aes(y=MonthlyCharges)) + geom_boxplot()


#quantiles - takes a vector of proportions
quantile(df$MonthlyCharges, probs = c(0, 0.2, 0.4, 0.6, 0.8,1))
    0%    20%    40%    60%    80%   100% 
 18.25  25.05  58.92  79.15  94.30 118.75 
#create monthly fee bands
df$monthly_fee_bin <- cut(df$MonthlyCharges, 
                   breaks= 5, 
                   labels=c("bin1", "bin2", "bin3", "bin4", "bin5"), 
                   right=FALSE)

table(df$monthly_fee_bin) #check results

bin1 bin2 bin3 bin4 bin5 
1791 1002 1366 1821 1052 
#check averages across the different fee bands
(fees <- df %>%  
  group_by(monthly_fee_bin) %>% 
  summarize(avg_monthly_fee = format(round(mean(MonthlyCharges),2)),
            median_monthly_fee = format(round(median(MonthlyCharges),2))))
`summarise()` ungrouping output (override with `.groups` argument)
#visualize fee bands
ggplot(data = df, aes(x=monthly_fee_bin, fill = factor(Churn))) + geom_bar(position = "fill")

##Step 3: Manage data

#any missing data?

#Using VIM package
missing <- aggr(df, prop = TRUE, bars = TRUE) # - some missing values


summary(missing)

 Missings per variable: 

 Missings in combinations of variables: 
## Show cases with missing values
(missing.df <- df[!complete.cases(df),]) #11 rows
#Some total charges missing

#We can remove missing rows - removing them won't affect our results that much - this is the code to remove below
df <- df[complete.cases(df),] 


#check results
sum(is.na(df$TotalCharges)) #all gone
[1] 0
#visualize entire dataframe again
aggr(df, prop = TRUE, bars = TRUE)

#any duplicate rows -
anyDuplicated(df)
[1] 0
#any duplicate columns -
names(df)[duplicated(names(df))]
character(0)
df[names(df)[!duplicated(names(df))]]
NA

There were no duplicates to worry about

Step 4: Statistical analysis & understanding relationships

Now we want to dig deeper We have some ideas of what to look for thanks to our EDA

#pairs.panels
df %>% 
  select_if(is.numeric) %>% 
  scale() %>% 
    pairs.panels()


#Tenure and TotalCharges has a very high correlation coefficient!
#Also shows us that SeniorCitizen is coded as a numerical variable (0 or 1)
#Monthly charges and TotalCharges has a high correlation coefficient also.

# i take these results with a grain of salt.
df %>% 
  select_if(is.numeric) %>% 
  cor() %>% 
    corrplot(type = "upper", insig = "blank", addCoef.col = "black", diag=FALSE)

#monthly charges is highly-correlated with total charges which makes sense. at 0.65.

#total charges and tenure has a high correlation.83
#which is bordering on collinearity
str(df$Churn)
 chr [1:7032] "No" "No" "Yes" "No" "Yes" "Yes" "No" "No" ...
(churnRate <- round(prop.table(table(df$Churn)),5))

     No     Yes 
0.73422 0.26578 
(churn_rate_overall <- churnRate[[2]])
[1] 0.26578
# recode churn variable into a numeric variable
df <- df %>% 
      mutate(Churn = ifelse(Churn == "Yes", 1, 0)) 

#check results
head(df$Churn)
[1] 0 0 1 0 1 1
`summarise()` ungrouping output (override with `.groups` argument)
[1] 1869
[1] 7032
[1] 1 0 0


#data frame grouped by fee
monthly.fee.df <- df %>% 
  group_by(monthly_fee_bin) %>% 
  summarize(total_count = n(),
    total_churns = sum(Churn)) %>% 
  mutate(churn_rate = total_churns/total_count)
Error: Must group by variables found in `.data`.
* Column `monthly_fee_bin` is not found.
Run `rlang::last_error()` to see where the error occurred.


#data frame grouped by fee
internet.df <- df %>% 
  group_by(InternetService) %>% 
  summarize(total_count = n(),
    total_churns = sum(Churn)) %>% 
  mutate(churn_rate = total_churns/total_count)

head(internet.df) #check results

#check churn columns
sum(internet.df$total_churns)
sum(internet.df$total_count)

#add highlight flag column
internet.df <- internet.df %>% 
mutate(highlight_flag =
    ifelse(churn_rate > churn_rate_overall, 1, 0))

#check results
head(internet.df$highlight_flag)

#plot response rate by fee difference
ggplot(data=internet.df, aes(x=reorder(InternetService, churn_rate), y=churn_rate,
  fill = factor(highlight_flag))) +
  geom_bar(stat="identity") +
  geom_hline(yintercept=churn_rate_overall, linetype="dashed", color = "black") +
  theme(axis.text.x = element_text(angle = 90)) +
  coord_flip() +
    scale_fill_manual(values = c('#595959', '#e41a1c')) +
  labs(x = ' ', y = 'Churn Rate', title = str_c("Churn rate by internet service")) +
  theme(legend.position = "none")

#data frame grouped by payment method
pymt.method.df <- df %>% 
  group_by(PaymentMethod) %>% 
  summarize(total_count = n(),
    total_churns = sum(Churn)) %>% 
  mutate(churn_rate = total_churns/total_count)

head(pymt.method.df) #check results

#check churn columns
sum(pymt.method.df$total_churns)
sum(pymt.method.df$total_count)

#add highlight flag column
pymt.method.df <- pymt.method.df %>% 
mutate(highlight_flag =
    ifelse(churn_rate > churn_rate_overall, 1, 0))

#check results
head(pymt.method.df$highlight_flag)

#plot response rate by fee difference
ggplot(data=pymt.method.df, aes(x=reorder(PaymentMethod, churn_rate), y=churn_rate,
  fill = factor(highlight_flag))) +
  geom_bar(stat="identity") +
  geom_hline(yintercept=churn_rate_overall, linetype="dashed", color = "black") +
  theme(axis.text.x = element_text(angle = 90)) +
  coord_flip() +
    scale_fill_manual(values = c('#595959', '#e41a1c')) +
  labs(x = ' ', y = 'Churn Rate', title = str_c("Churn rate by payment method")) +
  theme(legend.position = "none")

We have uncovered some insights so far:

Now we have to decide whether we should do some data modeling.

Step 5A: Data pre-proessing

#let's use a fresh dataset
df_raw <- read.csv("~/R_Programming/HI Bootcamp/Telco-Customer-Churn.csv")
str(df_dummy$Churn) #check churn variable
 Factor w/ 2 levels "0","1": 1 1 2 1 2 2 1 1 2 1 ...
table(df_dummy$Churn)

   0    1 
5163 1869 

Part 5B: Modeling - Logistic Regression


# Check total number of rows in our dataset before splitting
nrow(df_dummy)
[1] 7032
#Set seed for reproducibility
set.seed(123) 

#Split the dataset into training and test sets at 70:30 ratio using caTools package
split <- sample.split(df_dummy$Churn, SplitRatio = 0.7)
train_data <- subset(df_dummy, split == TRUE)
test_data <- subset(df_dummy, split == FALSE)

#Check if distribution of partition data is correct for train and test set
prop.table(table(train_data$Churn))

        0         1 
0.7342544 0.2657456 
prop.table(table(test_data$Churn)) 

        0         1 
0.7341232 0.2658768 

set.seed(1)  # set seed for reproducibility

#Use glm function from glmnet package to create model
model_01_glm <- glm(formula = Churn ~ ., 
                             data = train_data, family = "binomial")
 
#use to debug if glm function not working   
# train_data %>%
#   select_if(is.factor) %>%
#   lapply(table)
                         
#Print the model output
summary(model_01_glm)

Call:
glm(formula = Churn ~ ., family = "binomial", data = train_data)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.9177  -0.6761  -0.2719   0.7253   3.5090  

Coefficients:
                                       Estimate
(Intercept)                           1.445e+00
gender1                               1.162e-02
SeniorCitizen1                        3.252e-01
Partner1                              2.019e-02
Dependents1                          -1.550e-01
tenure                               -6.148e-02
PhoneService1                         5.856e-01
MultipleLines1                        4.504e-01
InternetServiceFiber optic            2.019e+00
InternetServiceNo                    -2.319e+00
OnlineSecurity1                      -2.303e-01
OnlineBackup1                         1.104e-01
DeviceProtection1                     2.161e-01
TechSupport1                         -6.384e-02
StreamingTV1                          7.138e-01
StreamingMovies1                      6.613e-01
ContractOne year                     -6.419e-01
ContractTwo year                     -1.543e+00
PaperlessBilling1                     3.710e-01
PaymentMethodCredit card (automatic) -4.064e-02
PaymentMethodElectronic check         2.866e-01
PaymentMethodMailed check            -2.638e-02
MonthlyCharges                       -5.401e-02
TotalCharges                          3.415e-04
                                     Std. Error z value
(Intercept)                           9.764e-01   1.480
gender1                               7.790e-02   0.149
SeniorCitizen1                        1.010e-01   3.219
Partner1                              9.429e-02   0.214
Dependents1                           1.095e-01  -1.415
tenure                                7.597e-03  -8.093
PhoneService1                         7.788e-01   0.752
MultipleLines1                        2.125e-01   2.120
InternetServiceFiber optic            9.538e-01   2.117
InternetServiceNo                     9.678e-01  -2.396
OnlineSecurity1                       2.138e-01  -1.077
OnlineBackup1                         2.093e-01   0.528
DeviceProtection1                     2.123e-01   1.018
TechSupport1                          2.138e-01  -0.299
StreamingTV1                          3.904e-01   1.828
StreamingMovies1                      3.917e-01   1.688
ContractOne year                      1.283e-01  -5.001
ContractTwo year                      2.252e-01  -6.851
PaperlessBilling1                     8.956e-02   4.142
PaymentMethodCredit card (automatic)  1.371e-01  -0.296
PaymentMethodElectronic check         1.132e-01   2.532
PaymentMethodMailed check             1.367e-01  -0.193
MonthlyCharges                        3.800e-02  -1.421
TotalCharges                          8.628e-05   3.958
                                     Pr(>|z|)    
(Intercept)                           0.13899    
gender1                               0.88144    
SeniorCitizen1                        0.00129 ** 
Partner1                              0.83041    
Dependents1                           0.15702    
tenure                               5.81e-16 ***
PhoneService1                         0.45206    
MultipleLines1                        0.03401 *  
InternetServiceFiber optic            0.03424 *  
InternetServiceNo                     0.01659 *  
OnlineSecurity1                       0.28136    
OnlineBackup1                         0.59782    
DeviceProtection1                     0.30871    
TechSupport1                          0.76528    
StreamingTV1                          0.06751 .  
StreamingMovies1                      0.09132 .  
ContractOne year                     5.70e-07 ***
ContractTwo year                     7.33e-12 ***
PaperlessBilling1                    3.44e-05 ***
PaymentMethodCredit card (automatic)  0.76687    
PaymentMethodElectronic check         0.01135 *  
PaymentMethodMailed check             0.84698    
MonthlyCharges                        0.15526    
TotalCharges                         7.55e-05 ***
---
Signif. codes:  
0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 5699.5  on 4921  degrees of freedom
Residual deviance: 4041.2  on 4898  degrees of freedom
AIC: 4089.2

Number of Fisher Scoring iterations: 6

The Odds Ratio and Probability of each x variable is calculated based on the formulae, Odds Ratio = exp(Co-efficient estimate) Probability = Odds Ratio / (1 + Odds Ratio)


##Individual coefficients significance and interpretation

#library(coef.lmList)
summary(model_01_glm)

Call:
glm(formula = Churn ~ ., family = "binomial", data = train_data)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.9177  -0.6761  -0.2719   0.7253   3.5090  

Coefficients:
                                       Estimate Std. Error z value Pr(>|z|)    
(Intercept)                           1.445e+00  9.764e-01   1.480  0.13899    
gender1                               1.162e-02  7.790e-02   0.149  0.88144    
SeniorCitizen1                        3.252e-01  1.010e-01   3.219  0.00129 ** 
Partner1                              2.019e-02  9.429e-02   0.214  0.83041    
Dependents1                          -1.550e-01  1.095e-01  -1.415  0.15702    
tenure                               -6.148e-02  7.597e-03  -8.093 5.81e-16 ***
PhoneService1                         5.856e-01  7.788e-01   0.752  0.45206    
MultipleLines1                        4.504e-01  2.125e-01   2.120  0.03401 *  
InternetServiceFiber optic            2.019e+00  9.538e-01   2.117  0.03424 *  
InternetServiceNo                    -2.319e+00  9.678e-01  -2.396  0.01659 *  
OnlineSecurity1                      -2.303e-01  2.138e-01  -1.077  0.28136    
OnlineBackup1                         1.104e-01  2.093e-01   0.528  0.59782    
DeviceProtection1                     2.161e-01  2.123e-01   1.018  0.30871    
TechSupport1                         -6.384e-02  2.138e-01  -0.299  0.76528    
StreamingTV1                          7.138e-01  3.904e-01   1.828  0.06751 .  
StreamingMovies1                      6.613e-01  3.917e-01   1.688  0.09132 .  
ContractOne year                     -6.419e-01  1.283e-01  -5.001 5.70e-07 ***
ContractTwo year                     -1.543e+00  2.252e-01  -6.851 7.33e-12 ***
PaperlessBilling1                     3.710e-01  8.956e-02   4.142 3.44e-05 ***
PaymentMethodCredit card (automatic) -4.064e-02  1.371e-01  -0.296  0.76687    
PaymentMethodElectronic check         2.866e-01  1.132e-01   2.532  0.01135 *  
PaymentMethodMailed check            -2.638e-02  1.367e-01  -0.193  0.84698    
MonthlyCharges                       -5.401e-02  3.800e-02  -1.421  0.15526    
TotalCharges                          3.415e-04  8.628e-05   3.958 7.55e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 5699.5  on 4921  degrees of freedom
Residual deviance: 4041.2  on 4898  degrees of freedom
AIC: 4089.2

Number of Fisher Scoring iterations: 6
#prints confidence intervals
exp(confint(model_01_glm))
Waiting for profiling to be done...
                                          2.5 %     97.5 %
(Intercept)                          0.62585298 28.7897010
gender1                              0.86839042  1.1785628
SeniorCitizen1                       1.13557648  1.6876014
Partner1                             0.84831532  1.2277633
Dependents1                          0.69048169  1.0607877
tenure                               0.92620175  0.9542076
PhoneService1                        0.39040235  8.2748304
MultipleLines1                       1.03485063  2.3807466
InternetServiceFiber optic           1.16401499 49.0061508
InternetServiceNo                    0.01474087  0.6555454
OnlineSecurity1                      0.52200493  1.2072804
OnlineBackup1                        0.74097961  1.6836167
DeviceProtection1                    0.81873857  1.8824724
TechSupport1                         0.61666623  1.4262243
StreamingTV1                         0.95055719  4.3940173
StreamingMovies1                     0.89964339  4.1788776
ContractOne year                     0.40818333  0.6752918
ContractTwo year                     0.13509802  0.3274961
PaperlessBilling1                    1.21627232  1.7279757
PaymentMethodCredit card (automatic) 0.73366100  1.2559173
PaymentMethodElectronic check        1.06753609  1.6640737
PaymentMethodMailed check            0.74534346  1.2739225
MonthlyCharges                       0.87935181  1.0206554
TotalCharges                         1.00017446  1.0005129
#plot coefficients on odds ratio using sjPlot package
plot_model(model_01_glm, vline.color = "red",
  sort.est = TRUE, show.values = TRUE)

#Calculate Variance Inflation factor using car package
vif(model_01_glm)
                       GVIF Df GVIF^(1/(2*Df))
gender             1.006295  1        1.003142
SeniorCitizen      1.134530  1        1.065143
Partner            1.398308  1        1.182501
Dependents         1.304214  1        1.142022
tenure            16.009663  1        4.001208
PhoneService      34.990361  1        5.915265
MultipleLines      7.328239  1        2.707072
InternetService  368.911620  2        4.382587
OnlineSecurity     4.940076  1        2.222628
OnlineBackup       6.321778  1        2.514315
DeviceProtection   6.503891  1        2.550273
TechSupport        5.181150  1        2.276214
StreamingTV       24.424063  1        4.942071
StreamingMovies   24.781447  1        4.978097
Contract           1.581055  2        1.121339
PaperlessBilling   1.127862  1        1.062009
PaymentMethod      1.405279  3        1.058345
MonthlyCharges   683.317139  1       26.140335
TotalCharges      20.614481  1        4.540317
# Feature (x) variables with a VIF value above 5 indicate high degree of
# multi-collinearity.

MonthlyCharges has a very high VIF at 600+. We have some other variables e.g. InternetServiceFiber.optic, InternetServiceNo, and TotalCharges, PhoneServiceYes, with high VIFs. Normally, we would want to remove variables with high VIF as it can really mess with our model.

pred <- predict(model_01_glm, test_data, type = "response") #predict using test data

#check results
head(pred)
         2          5          7          8          9 
0.04019090 0.69621075 0.50018862 0.28517147 0.59281756 
        12 
0.02259621 
predicted <- round(pred) #>0.5 will convert to 1

#contingency table
contingency_tab <- table(test_data$Churn, predicted)
contingency_tab
   predicted
       0    1
  0 1373  176
  1  238  323
# Confusion Matrix using the caret package
caret::confusionMatrix(contingency_tab)
Confusion Matrix and Statistics

   predicted
       0    1
  0 1373  176
  1  238  323
                                          
               Accuracy : 0.8038          
                 95% CI : (0.7862, 0.8205)
    No Information Rate : 0.7635          
    P-Value [Acc > NIR] : 5.01e-06        
                                          
                  Kappa : 0.479           
                                          
 Mcnemar's Test P-Value : 0.002718        
                                          
            Sensitivity : 0.8523          
            Specificity : 0.6473          
         Pos Pred Value : 0.8864          
         Neg Pred Value : 0.5758          
             Prevalence : 0.7635          
         Detection Rate : 0.6507          
   Detection Prevalence : 0.7341          
      Balanced Accuracy : 0.7498          
                                          
       'Positive' Class : 0               
                                          
#Plot ROC Curve & Calculate AUC area
library(ROCR)

#ROC Curves are useful for comparing classifiers

#Check data structures first or else the ROC curve won't plot
typeof(predicted)
[1] "double"
typeof(test_data$Churn)
[1] "integer"
pr <- prediction(pred, test_data$Churn)
prf <- performance(pr, measure = "tpr", x.measure = "fpr")

plot(prf)

#The ideal ROC curve hugs the top left corner, indicating a high
#true positive rate and a low false positive rate.
#True positive rate on y-axis
#False positive rate on the x-axis

#The larger the AUC, the better the classifier
#The AUC line is insufficient to identify a best model
#It's used in combination with qualitative examination
#of the ROC curve
auc <- performance(pr, measure = "auc")
auc
A performance instance
  'Area under the ROC curve'
# AUC is 0.861989
as.numeric(performance(pr, measure = "auc")@y.values)
[1] 0.8396119
#Double density plot for explaining and picking thresholds of predicted churn probabilities. Perhaps better alternative than AUC or ROC curve
ggplot(data = test_data) + geom_density(aes(x=pred, color = Churn,linetype = Churn))

AUC Interpretation A: Outstanding = 0.9 to 1.0 B: Excellent/Good = 0.8 to 0.9 C: Acceptable/Fair = 0.7 to 0.8 D: Poor = 0.6 to 0.7 E: No Discrimination = 0.5 to 0.6

set.seed(1)  # for reproducibility
model_02_glm <- glm(formula = Churn ~ . -MonthlyCharges,
                             data = train_data, family = "binomial")

summary(model_02_glm)

Call:
glm(formula = Churn ~ . - MonthlyCharges, family = "binomial", 
    data = train_data)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.9155  -0.6758  -0.2734   0.7196   3.5126  

Coefficients:
                                       Estimate Std. Error z value Pr(>|z|)    
(Intercept)                           8.802e-02  2.059e-01   0.428  0.66898    
gender1                               1.479e-02  7.784e-02   0.190  0.84931    
SeniorCitizen1                        3.247e-01  1.010e-01   3.216  0.00130 ** 
Partner1                              2.214e-02  9.423e-02   0.235  0.81426    
Dependents1                          -1.545e-01  1.094e-01  -1.412  0.15802    
tenure                               -6.092e-02  7.567e-03  -8.051 8.21e-16 ***
PhoneService1                        -4.981e-01  1.589e-01  -3.134  0.00173 ** 
MultipleLines1                        1.811e-01  9.593e-02   1.888  0.05906 .  
InternetServiceFiber optic            6.748e-01  1.172e-01   5.757 8.57e-09 ***
InternetServiceNo                    -9.634e-01  1.660e-01  -5.804 6.48e-09 ***
OnlineSecurity1                      -4.980e-01  1.017e-01  -4.894 9.87e-07 ***
OnlineBackup1                        -1.566e-01  9.240e-02  -1.694  0.09020 .  
DeviceProtection1                    -5.383e-02  9.496e-02  -0.567  0.57078    
TechSupport1                         -3.309e-01  1.024e-01  -3.232  0.00123 ** 
StreamingTV1                          1.771e-01  9.836e-02   1.800  0.07183 .  
StreamingMovies1                      1.226e-01  9.808e-02   1.250  0.21136    
ContractOne year                     -6.426e-01  1.283e-01  -5.008 5.50e-07 ***
ContractTwo year                     -1.542e+00  2.252e-01  -6.848 7.49e-12 ***
PaperlessBilling1                     3.729e-01  8.953e-02   4.164 3.12e-05 ***
PaymentMethodCredit card (automatic) -4.020e-02  1.370e-01  -0.293  0.76923    
PaymentMethodElectronic check         2.880e-01  1.132e-01   2.544  0.01096 *  
PaymentMethodMailed check            -2.494e-02  1.367e-01  -0.182  0.85523    
TotalCharges                          3.343e-04  8.593e-05   3.891 1.00e-04 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 5699.5  on 4921  degrees of freedom
Residual deviance: 4043.2  on 4899  degrees of freedom
AIC: 4089.2

Number of Fisher Scoring iterations: 6
vif(model_02_glm)
                      GVIF Df GVIF^(1/(2*Df))
gender            1.005342  1        1.002668
SeniorCitizen     1.135116  1        1.065418
Partner           1.397455  1        1.182140
Dependents        1.304336  1        1.142075
tenure           15.872054  1        3.983975
PhoneService      1.460078  1        1.208337
MultipleLines     1.494095  1        1.222332
InternetService   2.649715  2        1.275851
OnlineSecurity    1.119919  1        1.058262
OnlineBackup      1.232516  1        1.110187
DeviceProtection  1.301363  1        1.140773
TechSupport       1.188972  1        1.090400
StreamingTV       1.550164  1        1.245056
StreamingMovies   1.554139  1        1.246651
Contract          1.581378  2        1.121396
PaperlessBilling  1.127640  1        1.061904
PaymentMethod     1.405135  3        1.058327
TotalCharges     20.435807  1        4.520598
#plot coefficients on odds ratio
plot_model(model_02_glm, vline.color = "red",
  sort.est = TRUE, show.values = TRUE)

InternetService - Fiber Optic cable still increases likelihood of churn followed by paperless billing and being a senior citizen.

pred <- predict(model_02_glm, test_data, type = "response") #predict using test data

#check results
head(pred)
         2          5          7          8          9         12 
0.04440227 0.70457399 0.48898415 0.28325323 0.59169889 0.02143049 
predicted <- round(pred) #>0.5 will convert predicted values to 1

#contingency table - manually done with table function
contingency_tab <- table(test_data$Churn, predicted)
contingency_tab
   predicted
       0    1
  0 1372  177
  1  237  324
# Confusion Matrix using the caret package
caret::confusionMatrix(contingency_tab)
Confusion Matrix and Statistics

   predicted
       0    1
  0 1372  177
  1  237  324
                                          
               Accuracy : 0.8038          
                 95% CI : (0.7862, 0.8205)
    No Information Rate : 0.7626          
    P-Value [Acc > NIR] : 3.136e-06       
                                          
                  Kappa : 0.4796          
                                          
 Mcnemar's Test P-Value : 0.003735        
                                          
            Sensitivity : 0.8527          
            Specificity : 0.6467          
         Pos Pred Value : 0.8857          
         Neg Pred Value : 0.5775          
             Prevalence : 0.7626          
         Detection Rate : 0.6502          
   Detection Prevalence : 0.7341          
      Balanced Accuracy : 0.7497          
                                          
       'Positive' Class : 0               
                                          
#Plot ROC Curve & Calculate AUC area
library(ROCR)


#check data structures first
typeof(predicted)
[1] "double"
typeof(test_data$Churn)
[1] "integer"
pr <- prediction(pred, test_data$Churn)
prf <- performance(pr, measure = "tpr", x.measure = "fpr")

plot(prf)


#The ideal ROC curve hugs the top left corner, indicating a high
#true positive rate and a low false positive rate.
#True positive rate on y-axis
#False positive rate on the x-axis

#The larger the AUC, the better the classifier
#The AUC line is insufficient to identify a best model
#It's used in combination with qualitative examination
#of the ROC curve
auc <- performance(pr, measure = "auc")
auc
A performance instance
  'Area under the ROC curve'
as.numeric(performance(pr, measure = "auc")@y.values)
[1] 0.8397523

Comparing logistic regression models 1 and 2 -Model 1 has better accuracy than Model 2 (just slightly better), but we do care about specificity and sensitivity.

Sensitivity is the proportion of our variable of interest(churn) correctly identified.

Specificity is the proportion of our variable of interest(churn)

Read: https://www.theanalysisfactor.com/sensitivity-and-specificity/

Part 5B: Modeling - Random Forest

# Train a Random Forest using randomForest package
set.seed(1)  # for reproducibility
model_01_rf <- randomForest(formula = Churn ~ ., data = train_data, importance = TRUE, na.action=na.exclude)
                             
# Print the model output                
print(model_01_rf)

Model 500 trees: -No. of variables tried at each split: 5 -OOB estimate of error rate: 20%

# prints variable importance
summary(model_01_rf)


varImpPlot(model_01_rf, main="Variable Importance")
# Train a Random Forest
set.seed(1)  # for reproducibility
model_02_rf <- randomForest(formula = Churn ~ . -tenure,
                             data = train_data, importance = TRUE)
                             
# Print the model output
print(model_02_rf)

varImpPlot(model_02_rf, main="Variable Importance without tenure")

Type of random forest: classification Number of trees: 500 No. of variables tried at each split: 4

OOB estimate of error rate: 21.51% Confusion matrix: 0 1 class.error 0 3621 499 0.1211165 1 829 677 0.5504648

our oob error rate increased to 21.51% from 21.3% - let’s tune the 1st model - model_01_rf

# Grab OOB error matrix & take a look
err <- model_01_rf$err.rate
head(err)

# Look at final OOB error rate (last row in err matrix)
oob_err <- err[nrow(err), "OOB"]
print(oob_err)

# Plot the model trained
plot(model_01_rf)

# Add a legend since it doesn't have one by default
legend(x = "right", 
       legend = colnames(err),
       fill = 1:ncol(err))
# Generate predicted classes using the model object
class_prediction <- predict(object = model_01_rf,   # model object 
                            newdata = test_data,  # test dataset
                            type = "class") # return classification labels
                            
# Calculate the confusion matrix for the test set
cm <- caret::confusionMatrix(data = class_prediction,#predicted classes
                      reference = test_data$Churn)  # actual classes
print(cm)

# Compare test set accuracy to OOB accuracy
paste0("Test Accuracy: ", cm$overall[1])
paste0("OOB Accuracy: ", 1 - oob_err)

Compare test set accuracy to OOB accuracy

[1] “Test Accuracy: 0.809388335704125”

[1] “OOB Accuracy: 0.796836118023462”


# Generate predictions on the test set
pred <-predict(object = model_01_rf,
            newdata = test_data,
            type = "prob") 

# Uncomment to take a look at the pred format - `pred` object is a matrix
#head(pred)
                
# Compute the AUC (`actual` must be a binary 1/0 numeric vector)
round(auc(actual = ifelse(test_data$Churn == "1", 1, 0),
    predicted = pred[,"1"]) ,2)

AUC is good to excellent

CAUTION: Random forest models are computationally heavy to run on your computer especially the parameter tuning as the model needs to iterate through all possible values in the hyper grid. This could take many hours to run and if your dataset is large, could crash your computer!

Optimal model

Mtry = 4 nodesize = 7 sampsize = 3445.4

# Train a Random Forest
set.seed(1)  # for reproducibility
model_03_rf_final <- randomForest(formula = Churn ~ ., 
                                  mtry = 4,
                                  nodesize = 7,
                                  sampsize = 3445.4,
                             data = train_data, importance = TRUE, keep.forest=TRUE)
                             
# Print the model output
print(model_03_rf_final)

#variable importance
importance(model_03_rf_final)

#variable importance plot
varImpPlot(model_03_rf_final, main="Variable importance on the tuned model")
# Generate predictions on the test set
pred <-predict(object = model_03_rf_final,
            newdata = test_data,
            type = "prob") 

round(auc(actual = ifelse(test_data$Churn == "1", 1, 0),
    predicted = pred[,"1"]) ,2)
require(pROC)
rf.roc<-roc(train_data$Churn,model_03_rf_final$votes[,2])
plot(rf.roc)
auc(rf.roc)

# Train the model (to predict 'Churn') using rpart package
tree_mod_01 <- rpart(formula = Churn ~., 
                      data = train_data,
                      method = "class")

# Look at the model output                  
print(tree_mod_01)


# Display the results using rpart.plot package
rpart.plot(x = tree_mod_01, yesno = 2, type = 0, extra = 0)

rpart.plot(tree_mod_01,
yesno = 2,
extra = 104, # show fitted class, probs, percentages
box.palette = "GnBu", # color scheme
branch.lty = 3, # 1= solid, 3 = dotted branch lines
shadow.col = "gray", # shadows under the node boxes
nn = TRUE, 
main = "Classification tree on our churn data")
# Train a gini-based model
tree_mod_02<- rpart(formula = Churn ~., 
                      data = train_data_tree,
                       method = "class",
                       parms = list(split = "gini"))

# Display the results
rpart.plot(x = tree_mod_02, yesno = 2, type = 0, extra = 0)

# Train an information-based model
tree_mod_03<- rpart(formula = Churn ~., 
                      data = train_data_tree, 
                       method = "class",
                       parms = list(split = "information"))

# Display the results
rpart.plot(x = tree_mod_03, yesno = 2, type = 5, extra = 6)

# Generate predictions on the test set using the gini model
pred1 <- predict(object = tree_mod_02, 
             newdata = test_data_tree,
             type = "class")    

# Generate predictions on the test set using the information model
pred2 <- predict(object = tree_mod_03, 
             newdata = test_data_tree,
             type = "class")

# Compare classification error - using ModelMetrics library
ce(actual = test_data$Churn, 
   predicted = pred1)
ce(actual = test_data$Churn, 
   predicted = pred2)  

Part 6: Summary of Insights and hypotheses generated and testing them

What insights have we discovered and can confidently report to our stakeholders?


df %>% 
  filter(Churn == "Yes" & InternetService == "Fiber optic") %>% 
  summarize(lost_revenue = sum(TotalCharges))
NA

Now, you have a good starting point to present your findings & insights to stakeholders, but this is only the beginning. Currently, you only have a strong hypotheses so now you need to validate and test what you have uncovered. You may need to do more research or data to do this as you may need to uncover the root causes.

LS0tCnRpdGxlOiAiVGVsY28gQ3VzdG9tZXIgQ2h1cm4iCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KIyMgU3RlcCAxOiBHZXQgZGF0YQoKYGBge3IgTG9hZCBsaWJyYXJpZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CiNDb3JlICYgc3RhdHMgcGFja2FnZXMKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocHN5Y2gpCmxpYnJhcnkoZ21vZGVscykKbGlicmFyeShWSU0pCmxpYnJhcnkoZ2djb3JycGxvdCkKbGlicmFyeShjb3JycGxvdCkKCiNEYXRhIHZpc3VhbGl6YXRpb24gcGFja2FnZXMKbGlicmFyeShleHBsb3JlKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShnZ3RoZW1lcykKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkoc2pQbG90KQoKI01vZGVsaW5nIHBhY2thZ2VzCmxpYnJhcnkoY2FUb29scykKbGlicmFyeShjYXIpCmxpYnJhcnkoZ2xtbmV0KQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KFJPQ1IpCmxpYnJhcnkoUFJST0MpCmxpYnJhcnkocmFuZG9tRm9yZXN0KQpsaWJyYXJ5KHBST0MpCmxpYnJhcnkocnBhcnQpCmxpYnJhcnkocnBhcnQucGxvdCkKbGlicmFyeShNb2RlbE1ldHJpY3MpCmBgYAoKCgpgYGB7ciBDaGVjayB3ZCwgZWNobyA9IEZBTFNFfQoKc2V0d2QoIi9Vc2Vycy9hbml0YW93ZW5zL1JfUHJvZ3JhbW1pbmcvSEkgQm9vdGNhbXAiKQpnZXR3ZCgpCmBgYAoKYGBge3IgTG9hZCBkYXRhc2V0fQpkZiA8LSByZWFkLmNzdigifi9SX1Byb2dyYW1taW5nL0hJIEJvb3RjYW1wL1RlbGNvLUN1c3RvbWVyLUNodXJuLmNzdiIpCmBgYAoKCiMjIFN0ZXAgMjogRXhwbG9yZSBEYXRhClNwb3QgcHJvYmxlbXMKCmBgYHtyIENoZWNrIHN0cnVjdHVyZX0Kc3RyKGRmKQojNzA0MyBvYnMgYW5kIDIxIHZhcnMKYGBgCgoKCgpgYGB7ciBFeHBsb3JlIERhdGEgSW50ZXJhdGl2ZWx5fQojRXhwbG9yZSBkYXRhIGludGVyYWN0aXZlbHkgd2l0aCBFeHBsb3JlIHBhY2thZ2UKI2V4cGxvcmUoZGYpICN1bmNvbW1lbnQgdG8gcnVuCgojd2UgaGF2ZSBzZXZlcmFsIGRpZmZlcmVudCBtZWFzdXJlcyBpbiB0aGUgZGF0YXNldApgYGAKCgoKYGBge3IgRXN0YWJsaXNoIGNodXJuIHJhdGUgYmFzZWxpbmUgLSB0aGF0IGlzIHdoYXQgd2UgYXJlIGludGVyZXN0ZWQgaW59CiNjaHVybiByYXRlIGJhc2VsaW5lCihjaHVybi5iYXNlIDwtIGRmICU+JSAKICBncm91cF9ieShDaHVybikgJT4lIAogIGNvdW50KENodXJuKSAlPiUgCiAgbXV0YXRlKHBlcmMgPSBuL25yb3coZGYpICogMTAwKSAlPiUgCiAgcmVuYW1lKGN1c3RvbWVycyA9IG4pKQoKCgojb3JkZXJlZCBiYXIgcGxvdHMgLSBuZWVkIHRvIGFycmFuZ2UgY29sdW1uIGluIGRlc2Mgb3JkZXIgYjQgcGxvdHRpbmcKZ2diYXJwbG90KGNodXJuLmJhc2UsIHggPSAiY3VzdG9tZXJzIiwgeSA9ICJwZXJjIiwgICAgICAgICAgICAgICAKICAgICAgICAgZmlsbCA9ICJDaHVybiIsIAogICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsCiAgICAgICAgIHBhbGV0dGUgPSAiamNvIiwKICAgICAgICAgIHNvcnQudmFsID0gImRlc2MiLCAgICAgICAgICAjIFNvcnQgdGhlIHZhbHVlIGluIGRzY2VuZGluZyBvcmRlcgogICAgICAgICAgc29ydC5ieS5ncm91cHMgPSBGQUxTRSwgICAgICMgRG9uJ3Qgc29ydCBpbnNpZGUgZWFjaCBncm91cAogICAgICAgICAgeC50ZXh0LmFuZ2xlID0gMCwgICAgICAgICAgIyBSb3RhdGUgdmVydGljYWxseSB4IGF4aXMgdGV4dHMKICAgICAgICAgIGxlZ2VuZCA9ICJyaWdodCIsCiAgICAgICAgICB4bGFiID0gIiBDaHVybiIsCiAgICAgICAgICB5bGFiID0gIiBQZXJjZW50YWdlIiwKICAgICAgICAgIGxhYmVsID0gcGFzdGUocm91bmQoY2h1cm4uYmFzZSRwZXJjLDApLCIlIiwgc2VwID0gIiIpLAogICAgICAgICAgbGFiZWwucG9zID0gIm91dCIsCiAgICAgICAgICB0aXRsZSA9ICJDaHVybiByYXRlIGJhc2VsaW5lIiwKICAgICAgICAgIGdndGhlbWUgPSB0aGVtZV9taW5pbWFsKCkKICAgICAgICAgKQoKdGFibGUoZGYkQ2h1cm4pCnByb3AudGFibGUodGFibGUoZGYkQ2h1cm4pKQoKI091ciBjaHVybiByYXRlIGlzIDI3JQpgYGAKCgpgYGB7ciBVbml2YXJpYXRlIEFuYWx5c2lzOiBCYXJwbG90cyBmb3IgaW1wb3J0YW50IGNhdGVnb3JpY2FsIHZhcmlhYmxlcyAoeCksIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CgpsaWJyYXJ5KGNvd3Bsb3QpCgojIERvbid0IG1hcCBhIHZhcmlhYmxlIHRvIHkKYmFyMSA8LSBnZ3Bsb3QoZGYsIGFlcyh4PWZhY3RvcihnZW5kZXIpKSkrCiAgZ2VvbV9iYXIoc3RhdD0iY291bnQiLCB3aWR0aD0wLjcsIGZpbGw9IiNhNmNlZTMiKSsKICB0aGVtZV9taW5pbWFsKCkKCmJhcjIgPC0gZ2dwbG90KGRmLCBhZXMoeD1mYWN0b3IoU2VuaW9yQ2l0aXplbikpKSsKICBnZW9tX2JhcihzdGF0PSJjb3VudCIsIHdpZHRoPTAuNywgZmlsbD0iIzFmNzhiNCIpKwogIHRoZW1lX21pbmltYWwoKQoKYmFyMyA8LSBnZ3Bsb3QoZGYsIGFlcyh4PWZhY3RvcihQYXJ0bmVyKSkpKwogIGdlb21fYmFyKHN0YXQ9ImNvdW50Iiwgd2lkdGg9MC43LCBmaWxsPSIjYjJkZjhhIikrCiAgdGhlbWVfbWluaW1hbCgpCgpiYXI0IDwtIGdncGxvdChkZiwgYWVzKHg9ZmFjdG9yKERlcGVuZGVudHMpKSkrCiAgZ2VvbV9iYXIoc3RhdD0iY291bnQiLCB3aWR0aD0wLjcsIGZpbGw9IiMzM2EwMmMiKSsKICB0aGVtZV9taW5pbWFsKCkKCmJhcjUgPC0gZ2dwbG90KGRmLCBhZXMoeD1mYWN0b3IoUGhvbmVTZXJ2aWNlKSkpKwogIGdlb21fYmFyKHN0YXQ9ImNvdW50Iiwgd2lkdGg9MC43LCBmaWxsPSIjZmI5YTk5IikrCiAgdGhlbWVfbWluaW1hbCgpCgpiYXI2IDwtIGdncGxvdChkZiwgYWVzKHg9ZmFjdG9yKE11bHRpcGxlTGluZXMpKSkrCiAgZ2VvbV9iYXIoc3RhdD0iY291bnQiLCB3aWR0aD0wLjcsIGZpbGw9IiNlMzFhMWMiKSsKICB0aGVtZV9taW5pbWFsKCkKCmJhcjcgPC0gZ2dwbG90KGRmLCBhZXMoeD1mYWN0b3IoSW50ZXJuZXRTZXJ2aWNlKSkpKwogIGdlb21fYmFyKHN0YXQ9ImNvdW50Iiwgd2lkdGg9MC43LCBmaWxsPSIjZmRiZjZmIikrCiAgdGhlbWVfbWluaW1hbCgpCgpiYXI4IDwtIGdncGxvdChkZiwgYWVzKHg9ZmFjdG9yKE9ubGluZVNlY3VyaXR5KSkpKwogIGdlb21fYmFyKHN0YXQ9ImNvdW50Iiwgd2lkdGg9MC43LCBmaWxsPSIjZmY3ZjAwIikrCiAgdGhlbWVfbWluaW1hbCgpCgpiYXI5IDwtIGdncGxvdChkZiwgYWVzKHg9ZmFjdG9yKE9ubGluZUJhY2t1cCkpKSsKICBnZW9tX2JhcihzdGF0PSJjb3VudCIsIHdpZHRoPTAuNywgZmlsbD0iI2NhYjJkNiIpKwogIHRoZW1lX21pbmltYWwoKQoKYmFyMTAgPC0gZ2dwbG90KGRmLCBhZXMoeD1mYWN0b3IoRGV2aWNlUHJvdGVjdGlvbikpKSsKICBnZW9tX2JhcihzdGF0PSJjb3VudCIsIHdpZHRoPTAuNywgZmlsbD0iIzZhM2Q5YSIpKwogIHRoZW1lX21pbmltYWwoKQoKYmFyMTEgPC0gZ2dwbG90KGRmLCBhZXMoeD1mYWN0b3IoVGVjaFN1cHBvcnQpKSkrCiAgZ2VvbV9iYXIoc3RhdD0iY291bnQiLCB3aWR0aD0wLjcsIGZpbGw9IiNmZmZmOTkiKSsKICB0aGVtZV9taW5pbWFsKCkKCmJhcjEyIDwtIGdncGxvdChkZiwgYWVzKHg9ZmFjdG9yKFN0cmVhbWluZ1RWKSkpKwogIGdlb21fYmFyKHN0YXQ9ImNvdW50Iiwgd2lkdGg9MC43LCBmaWxsPSIjYjE1OTI4IikrCiAgdGhlbWVfbWluaW1hbCgpCgojVGhlcmUgaXMgYSBsb25nIGRlbGF5IGluIHBsb3R0aW5nCnBsb3RfZ3JpZChiYXIxLCBiYXIyLCBiYXIzLCBiYXI0LCBiYXI1LCBiYXI2LCBiYXI3LCBiYXI4LCBiYXI5LCBiYXIxMCwgYmFyMTEsIGJhcjEyLCBsYWJlbHM9IkFVVE8iKQoKYmFyMTMgPC0gZ2dwbG90KGRmLCBhZXMoeD1mYWN0b3IoU3RyZWFtaW5nTW92aWVzKSkpKwogIGdlb21fYmFyKHN0YXQ9ImNvdW50Iiwgd2lkdGg9MC43LCBmaWxsPSIjOGRkM2M3IikrCiAgdGhlbWVfbWluaW1hbCgpCgpiYXIxNCA8LSBnZ3Bsb3QoZGYsIGFlcyh4PWZhY3RvcihDb250cmFjdCkpKSsKICBnZW9tX2JhcihzdGF0PSJjb3VudCIsIHdpZHRoPTAuNywgZmlsbD0iI2ZmZmZiMyIpKwogIHRoZW1lX21pbmltYWwoKQoKYmFyMTUgPC0gZ2dwbG90KGRmLCBhZXMoeD1mYWN0b3IoUGFwZXJsZXNzQmlsbGluZykpKSsKICBnZW9tX2JhcihzdGF0PSJjb3VudCIsIHdpZHRoPTAuNywgZmlsbD0iI2JlYmFkYSIpKwogIHRoZW1lX21pbmltYWwoKQogIApiYXIxNiA8LSBnZ3Bsb3QoZGYsIGFlcyh4PWZhY3RvcihQYXltZW50TWV0aG9kKSkpKwogIGdlb21fYmFyKHN0YXQ9ImNvdW50Iiwgd2lkdGg9MC43LCBmaWxsPSIjZmI4MDcyIikrCiAgdGhlbWVfbWluaW1hbCgpICsgY29vcmRfZmxpcCgpCgpwbG90X2dyaWQoYmFyMTMsIGJhcjE0LCBiYXIxNSwgYmFyMTYsIGxhYmVscz0iQVVUTyIpCmBgYAoKCgpgYGB7ciBVbml2YXJpYXRlIEFuYWx5c2lzOiAgSGlzdG9ncmFtcyBmb3IgbnVtZXJpYyB2YXJpYWJsZXMsIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CgpoaXN0MSA8LSBnZ3Bsb3QoZGF0YSA9IGRmLCBhZXMoeCA9IHRlbnVyZSkpKwogICAgICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIiNlNDFhMWMiLCBiaW53aWR0aCA9IDUsIGNvbG91ciA9ICJibGFjayIpICsKICAgICAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lZGlhbih0ZW51cmUpKSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKyB0aGVtZV90dWZ0ZSgpICsgeWxhYigiQ291bnRzIikKI2JpLW1vZGFsIGRpc3RyaWJ1dGlvbgoKaGlzdDIgPC0gZ2dwbG90KGRhdGEgPSBkZiwgYWVzKHggPSBNb250aGx5Q2hhcmdlcykpKwogICAgICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIiMzNzdlYjgiLCBiaW53aWR0aCA9IDUsIGNvbG91ciA9ICJibGFjayIpICsKICAgICAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lZGlhbih0ZW51cmUpKSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKyB0aGVtZV90dWZ0ZSgpICsgeWxhYigiQ291bnRzIikKI2JpLW1vZGFsCgpoaXN0MyA8LSBnZ3Bsb3QoZGF0YSA9IGRmLCBhZXMoeCA9IFRvdGFsQ2hhcmdlcykpKwogICAgICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIiNlNDFhMWMiLCBiaW53aWR0aCA9IDUsIGNvbG91ciA9ICJibGFjayIpICsKICAgICAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lZGlhbih0ZW51cmUpKSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiI2U0MWExYyIpICsgbGFicyh0aXRsZSA9ICJUb3RhbCBjaGFyZ2VzIikgKyB0aGVtZV90dWZ0ZSgpICsgeWxhYigiQ291bnRzIikKI3JpZ2h0LXNrZXcKCmhpc3Quc2NhbGVkNCA8LSBnZ3Bsb3QoZGF0YSA9IGRmLCBhZXMoeCA9IFRvdGFsQ2hhcmdlcykpKwogICAgICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIiMzNzdlYjgiLCBiaW53aWR0aCA9IDUsIGNvbG91ciA9ICJibGFjayIpICsKICAgICAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lZGlhbih0ZW51cmUpKSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKyBzY2FsZV94X2xvZzEwKCkgKyBsYWJzKHRpdGxlID0gIlRvdGFsIGNoYXJnZXMgb24gbG9nIikgKyB0aGVtZV90dWZ0ZSgpICsgeWxhYigiQ291bnRzIikKI3N0aWxsIGEgYml0IG9mIHNrZXcKCgpwbG90X2dyaWQoaGlzdDEsIGhpc3QyLCBoaXN0MywgaGlzdC5zY2FsZWQ0LCBsYWJlbHM9IkFVVE8iKQpgYGAKCgoKCmBgYHtyIEJpdmFyaWF0ZSBBbmFseXNpczogQm94cGxvdHMgZm9yIGltcG9ydGFudCAoeCkgY2F0ZWdvcmljYWwgdmFyaWFibGVzICYgKHkpIG51bWVyaWMgdmFyaWFibGVzLCBlY2hvPVRSVUV9CgojZ2VuZGVyIG9uIHggYXhpcwpib3gxIDwtIGdncGxvdChkYXRhID0gZGYsIGFlcyh4ID0gZ2VuZGVyLCB5ID0gTW9udGhseUNoYXJnZXMsIGZpbGwgPSBnZW5kZXIpKStnZW9tX2JveHBsb3QoKSArIHN0YXRfc3VtbWFyeShmdW49bWVhbiwgZ2VvbT0icG9pbnQiLCBzaGFwZT0yMCwgc2l6ZT04LCBjb2xvcj0icmVkIiwgZmlsbD0icmVkIikKCmJveDIgPC0gZ2dwbG90KGRhdGEgPSBkZiwgYWVzKHggPSBnZW5kZXIsIHkgPSBUb3RhbENoYXJnZXMsIGZpbGwgPSBnZW5kZXIpKStnZW9tX2JveHBsb3QoKQoKYm94MyA8LSBnZ3Bsb3QoZGF0YSA9IGRmLCBhZXMoeCA9IGdlbmRlciwgeSA9IHRlbnVyZSwgZmlsbCA9IGdlbmRlcikpK2dlb21fYm94cGxvdCgpCgoKI3Bob25lIHNlcnZpY2Ugb24geCBheGlzCmJveDQgPC0gZ2dwbG90KGRhdGEgPSBkZiwgYWVzKHggPSBQaG9uZVNlcnZpY2UsIHkgPSBNb250aGx5Q2hhcmdlcywgZmlsbCA9IFBob25lU2VydmljZSkpK2dlb21fYm94cGxvdCgpCgpib3g1IDwtIGdncGxvdChkYXRhID0gZGYsIGFlcyh4ID0gUGhvbmVTZXJ2aWNlLCB5ID0gVG90YWxDaGFyZ2VzLCBmaWxsID0gUGhvbmVTZXJ2aWNlKSkrZ2VvbV9ib3hwbG90KCkKCmJveDYgPC0gZ2dwbG90KGRhdGEgPSBkZiwgYWVzKHggPSBQaG9uZVNlcnZpY2UsIHkgPSB0ZW51cmUsIGZpbGwgPSBQaG9uZVNlcnZpY2UpKStnZW9tX2JveHBsb3QoKQoKI2NvbnRyYWN0IG9uIHggYXhpcwpib3g3IDwtIGdncGxvdChkYXRhID0gZGYsIGFlcyh4ID0gQ29udHJhY3QsIHkgPSBNb250aGx5Q2hhcmdlcywgZmlsbCA9IENvbnRyYWN0KSkrZ2VvbV9ib3hwbG90KCkgKyBzdGF0X3N1bW1hcnkoZnVuPW1lYW4sIGdlb209InBvaW50Iiwgc2hhcGU9MjAsIHNpemU9OCwgY29sb3I9InJlZCIsIGZpbGw9InJlZCIpICsgY29vcmRfZmxpcCgpCiNTaW1pbGFyIGF2ZyBtb250aGx5IGNoYXJnZXMgYWNyb3NzIGFsbCB0aGUgZGlmZmVyZW50IHRlcm0gcGxhbnMsCiN3aXRoIHRoZSBsYXJnZXN0IHZhcmlhYmlsaXR5IGFtb25nc3QgMS15ZWFyIGNvbnRyYWN0IGhvbGRlcnMKCmJveDggPC0gZ2dwbG90KGRhdGEgPSBkZiwgYWVzKHggPSBDb250cmFjdCwgeSA9IFRvdGFsQ2hhcmdlcywgZmlsbCA9IENvbnRyYWN0KSkrZ2VvbV9ib3hwbG90KCkgIyBzb21lIG1pc3NpbmcgdmFsdWVzCiNoaWdoZXIgdGhhbiBhdmcgdG90YWwgY2hhcmdlcyBmb3IgdGhvc2Ugb24gMi15ZWFyIGNvbnRyYWN0cwoKYm94OSA8LSBnZ3Bsb3QoZGF0YSA9IGRmLCBhZXMoeCA9IENvbnRyYWN0LCB5ID0gdGVudXJlLCBmaWxsID0gQ29udHJhY3QpKStnZW9tX2JveHBsb3QoKQojVGhlIGxvbmdlciB0aGUgY29udHJhY3QgdGhlIGxvbmdlciB0aGUgYXZlcmFnZSBjdXN0b21lciB0ZW51cmUgd2l0aCAteWVhciBjb250cmFjdCBnaXZpbmcgdGhlIGhpZ2hlc3QgYXZnIHRlbnVyZS4gTWFrZXMgc2Vuc2UhCgpzalBsb3Q6OnBsb3RfZ3JpZChib3gxLCBib3gyLCBib3gzLCBib3g0LCBsYWJlbHM9IkFVVE8iKQoKcGxvdF9ncmlkKGJveDUsIGJveDYsIGJveDcsIGJveDgsIGJveDksIGxhYmVscyA9ICJBVVRPIikKYGBgCgoKYGBge3IgQml2YXJpYXRlIGFuYWx5c2lzOiBCb3hwbG90cyBmb3IgQ0hVUk4gKGFzIHggdmFyaWFibGUpIHZzIGltcG9ydGFudCBudW1lcmljICh5IHZhcmlhYmxlcyksIGVjaG89VFJVRX0KCmJpMSA8LSBnZ3Bsb3QoZGF0YSA9IGRmLCBhZXMoeCA9IGZhY3RvcihDaHVybiksIHkgPSB0ZW51cmUsIGZpbGwgPSBDaHVybikpICtnZW9tX2JveHBsb3QoKQojSGlnaCBjaHVybiBmb3IgdGhvc2Ugd2l0aCBsb3dlciB0ZW51cmVzCgpiaTIgPC0gZ2dwbG90KGRhdGEgPSBkZiwgYWVzKHggPSBmYWN0b3IoQ2h1cm4pLCB5ID0gTW9udGhseUNoYXJnZXMsIGZpbGwgPSBDaHVybikpK2dlb21fYm94cGxvdCgpCiNIaWdoZXIgY2h1cm4gZm9yIHRob3NlIHdpdGggaGlnaGVyIHRoYW4gYXZnIG1vbnRobHkgY2hhcmdlcwoKYmkzIDwtIGdncGxvdChkYXRhID0gZGYsIGFlcyh4ID0gZmFjdG9yKENodXJuKSwgeSA9IFRvdGFsQ2hhcmdlcywgZmlsbCA9IENodXJuKSkrZ2VvbV9ib3hwbG90KCkKI0xvd2VyIGNodXJuIGZvciB0aG9zZSB3aXRoIGxvd2VyIHRoYW4gYXZnIHRvdGFsIGNoYXJnZXMKCnBsb3RfZ3JpZChiaTEsIGJpMiwgYmkzLCBsYWJlbHMgPSAiQVVUTyIpCgpgYGAKCgoKYGBge3IgQml2YXJpYXRlIEFuYWx5c2lzOiBTdGFja2VkIGJhciBjaGFydHMgZm9yIGltcG9ydGFudCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgKHgpIGJ5IENIVVJOICh5KSBmYWN0b3IsIGVjaG89VFJVRX0KCmJpYmFyMSA8LSBnZ3Bsb3QoZGF0YSA9IGRmLCBhZXMoeD1nZW5kZXIsIGZpbGwgPSBmYWN0b3IoQ2h1cm4pKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjMWI5ZTc3IiwgIiNkOTVmMDIiKSkgKyB0aGVtZSgKICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICB0ZXh0PWVsZW1lbnRfdGV4dChzaXplPTE0LCAgZmFtaWx5PSJIZWx2ZXRpY2EiKSkgKyBsYWJzKHggPSAiICIsIHRpdGxlID0gIkNodXJuIGJ5IGdlbmRlciIpCiNubyBkaWZmZXJlbmNlIGJ5IGdlbmRlcgoKCmJpYmFyMiA8LSBnZ3Bsb3QoZGF0YSA9IGRmLCBhZXMoeD1Db250cmFjdCwgZmlsbCA9IGZhY3RvcihDaHVybikpKSsgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiIzFiOWU3NyIsICIjZDk1ZjAyIikpICsgdGhlbWUoCiAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgdGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xNCwgIGZhbWlseT0iSGVsdmV0aWNhIikpICsgbGFicyh4ID0gIiAiLCB0aXRsZSA9ICJDb250cmFjdCB0eXBlIikgKyBjb29yZF9mbGlwKCkKI1dlIGhhdmUgc2lnbmlmaWNhbnRseSBoaWdoZXIgY2h1cm4gdGhhbiBmb3IgdGhvc2Ugb24gbW9udGgtdG8tbW9udGggY29udHJhY3RzCgoKYmliYXIzIDwtIGdncGxvdChkYXRhID0gZGYsIGFlcyh4PWZhY3RvcihTZW5pb3JDaXRpemVuKSwgZmlsbCA9IGZhY3RvcihDaHVybikpKSsgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiIzFiOWU3NyIsICIjZDk1ZjAyIikpICsgdGhlbWUoCiAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgdGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xNCwgIGZhbWlseT0iSGVsdmV0aWNhIikpICsgbGFicyh4ID0gIiAiLCB0aXRsZSA9ICJJcyBhIHNlbmlvciBjaXRpemVuIikKI0hpZ2hlciBjaHVybiBmb3IgU2VuaW9yIENpdGl6ZW5zCgoKYmliYXI0ICA8LSBnZ3Bsb3QoZGF0YSA9IGRmLCBhZXMoeD1QYXBlcmxlc3NCaWxsaW5nLCBmaWxsID0gZmFjdG9yKENodXJuKSkpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiIzFiOWU3NyIsICIjZDk1ZjAyIikpICsgdGhlbWUoCiAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgdGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xNCwgIGZhbWlseT0iSGVsdmV0aWNhIikpICsgbGFicyh4ID0gIiAiLCB0aXRsZSA9ICJIYXMgcGFwZXJsZXNzIGJpbGxpbmciKQojaGlnaGVyIGNodXJuIHJhdGUgZm9yIHRob3NlIG9uIHBhcGVybGVzcyBiaWxsaW5nCgpiaWJhcjUgPC0gZ2dwbG90KGRhdGEgPSBkZiwgYWVzKHg9UGF5bWVudE1ldGhvZCwgZmlsbCA9IGZhY3RvcihDaHVybikpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiMxYjllNzciLCAiI2Q5NWYwMiIpKSArIHRoZW1lKAogICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgIHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIsICBmYW1pbHk9IkhlbHZldGljYSIpKSArIGxhYnMoeCA9ICIgIiwgdGl0bGUgPSAiUGF5bWVudCBNZXRob2QiKSArIGNvb3JkX2ZsaXAoKSAKI2hpZ2hlciBjaHVybiByYXRlIGZvciB0aG9zZSB3aG8gcGF5IGJ5IGVsZWN0cm9uaWMKI2NoZWNrIC0gbG93ZXN0IGNodXJuIHJhdGVzIGFtb25nIHRob3NlIHdpdGgKI2F1dG9tYXRpYyBiaWxsaW5nLgoKYmliYXI2IDwtIGdncGxvdChkYXRhID0gZGYsIGFlcyh4PVBob25lU2VydmljZSwgZmlsbCA9IGZhY3RvcihDaHVybikpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKSArIGxhYnModGl0bGUgPSAiQnkgcGhvbmUgc2VydmljZSIpCiNubyBkaWZmZXJlbmNlIGJ5IHBob25lIHNlcnZpY2UKCgpiaWJhcjcgPC0gZ2dwbG90KGRhdGEgPSBkZiwgYWVzKHg9UGFydG5lciwgZmlsbCA9IGZhY3RvcihDaHVybikpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiMxYjllNzciLCAiI2Q5NWYwMiIpKSArIHRoZW1lKAogICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgIHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIsICBmYW1pbHk9IkhlbHZldGljYSIpKSArIGxhYnMoeCA9ICIgIiwgdGl0bGUgPSAiSGFzIGEgcGFydG5lciIpIAojc2xpZ2h0bHkgaGlnaGVyIGNodXJuIHJhdGUgZm9yIHNpbmdsZXMKCgpiaWJhcjggPC1nZ3Bsb3QoZGF0YSA9IGRmLCBhZXMoeD1EZXBlbmRlbnRzLCBmaWxsID0gZmFjdG9yKENodXJuKSkpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiIzFiOWU3NyIsICIjZDk1ZjAyIikpICsgdGhlbWUoCiAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgdGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiwgIGZhbWlseT0iSGVsdmV0aWNhIikpICsgbGFicyh4ID0gIiAiLCB0aXRsZSA9ICJIYXMgZGVwZW5kZW50cyIpICsgY29vcmRfZmxpcCgpIAojaGlnaGVyIGNodXJuIHJhdGUgZm9yIHRob3NlIHdpdGggbm8gZGVwZW5kZW50cwoKYmliYXI5IDwtIGdncGxvdChkYXRhID0gZGYsIGFlcyh4PU11bHRpcGxlTGluZXMsIGZpbGwgPSBmYWN0b3IoQ2h1cm4pKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikgKyBsYWJzKHRpdGxlID0gIkhhcyBtdWx0aXBsZSBwaG9uZSBsaW5lcyIpCiMjdmVyeSBsaXR0bGUgZGlmZmVyZW5jZSBpZiBoYXZlIG11bHRpcGxlIGxpbmVzCgoKYmliYXIxMCA8LSBnZ3Bsb3QoZGF0YSA9IGRmLCBhZXMoeD1JbnRlcm5ldFNlcnZpY2UsIGZpbGwgPSBmYWN0b3IoQ2h1cm4pKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjMWI5ZTc3IiwgIiNkOTVmMDIiKSkgKyB0aGVtZSgKICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICB0ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyLCAgZmFtaWx5PSJIZWx2ZXRpY2EiKSkgKyBsYWJzKHggPSAiICIsIHRpdGxlID0gIkludGVybmV0IHNlcnZpY2UiKSArIGNvb3JkX2ZsaXAoKSAKI011Y2ggaGlnaGVyIGNodXJuIGlmIGhhdmUgZmliZXIgb3B0aWMgaW50ZXJuZXQgc2VydmljZQoKCmJpYmFyMTEgPC0gZ2dwbG90KGRhdGEgPSBkZiwgYWVzKHg9T25saW5lU2VjdXJpdHksIGZpbGwgPSBmYWN0b3IoQ2h1cm4pKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikgKyBjb29yZF9mbGlwKCkgKyBsYWJzKHRpdGxlID0gIk9ubGluZSBzZWN1cml0eSIpCiNoaWdoZXIgY2h1cm4gaWYgaGF2ZSBubyBvbmxpbmUgc2VjdXJpdHkKCmJpYmFyMTIgPC0gZ2dwbG90KGRhdGEgPSBkZiwgYWVzKHg9T25saW5lQmFja3VwLCBmaWxsID0gZmFjdG9yKENodXJuKSkpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpICsgY29vcmRfZmxpcCgpICsgbGFicyh0aXRsZSA9ICJPbmxpbmUgYmFja3VwIikKI2hpZ2hlciBjaHVybiBmb3IgdGhvc2Ugd2hvIGhhdmUgbm8gb25saW5lIGJhY2t1cAoKYmliYXIxMyA8LSBnZ3Bsb3QoZGF0YSA9IGRmLCBhZXMoeD1EZXZpY2VQcm90ZWN0aW9uLCBmaWxsID0gZmFjdG9yKENodXJuKSkpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpICsgY29vcmRfZmxpcCgpICsgbGFicyh0aXRsZSA9ICJEZXZpY2UgcHJvdGVjdGlvbiIpCiNoaWdoZXIgY2h1cm4gZm9yIHRob3NlIHdobyBoYXZlIG5vIGRldmljZSBwcm90ZWN0aW9uCgpiaWJhcjE0IDwtIGdncGxvdChkYXRhID0gZGYsIGFlcyh4PVRlY2hTdXBwb3J0LCBmaWxsID0gZmFjdG9yKENodXJuKSkpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpICsgY29vcmRfZmxpcCgpICsgbGFicyh0aXRsZSA9ICJUZWNoIHN1cHBvcnQiKQojaGlnaGVyIGNodXJuIGZvciB0aG9zZSB3aXRoIG5vIHRlY2ggc3VwcG9ydAoKYmliYXIxNSA8LSBnZ3Bsb3QoZGF0YSA9IGRmLCBhZXMoeD1TdHJlYW1pbmdUViwgZmlsbCA9IGZhY3RvcihDaHVybikpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKSArIGxhYnModGl0bGUgPSAiU3RyZWFtaW5nIFRWIikKI25vIGRpZmZlcmVuY2UgYW1vbmcgVFYgc3RyZWFtZXJzIC0gYnV0IHZlcnkgbG93IGNodXJuZXJzIGlmIHlvdSBkb24ndCBoYXZlIGludGVybmV0IHNlcnZpY2UKCmJpYmFyMTYgPC0gZ2dwbG90KGRhdGEgPSBkZiwgYWVzKHg9U3RyZWFtaW5nTW92aWVzLCBmaWxsID0gZmFjdG9yKENodXJuKSkpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpICsgbGFicyh0aXRsZSA9ICJTdHJlYW1pbmcgbW92aWVzIikKI25vIGRpZmZlcmVuY2UgYW1vbmcgbW92aWUgc3RyZWFtZXJzIC0gYnV0IHZlcnkgbG93IGNodXJuZXJzIGlmIHlvdSBkb24ndCBoYXZlIGludGVybmV0IHNlcnZpY2UKCgpwbG90X2dyaWQoYmliYXIxLCBiaWJhcjIsIGJpYmFyMywgYmliYXI0LAogICAgICAgICAgYmliYXI1LCBiaWJhcjYsCiAgICAgICAgICBsYWJlbHMgPSAiQVVUTyIpCgpwbG90X2dyaWQoYmliYXI3LCBiaWJhcjgsCiAgICAgICAgICBiaWJhcjksIGJpYmFyMTAsCiAgICAgICAgICBsYWJlbHMgPSAiQVVUTyIpCgpwbG90X2dyaWQoYmliYXIxMSwgYmliYXIxMiwgYmliYXIxMywKICAgICAgICAgIGxhYmVscyA9ICJBVVRPIikKCnBsb3RfZ3JpZChiaWJhcjE0LAogICAgICAgICAgYmliYXIxNSwgYmliYXIxNiwKICAgICAgICAgIGxhYmVscyA9ICJBVVRPIikKYGBgCgoKYGBge3IgTXVsdGl2YXJpYXRlIEFuYWx5c2lzIC0gU2NhdHRlcnBsb3RzIGFuZCBsb2cgdHJhbnNmb3JtYXRpb259CgpzcDEgPC0gZ2dwbG90KGRhdGEgPSBkZiwgYWVzKHg9TW9udGhseUNoYXJnZXMsIHk9dGVudXJlLCBjb2xvcj1mYWN0b3IoQ2h1cm4pKSkgKyBnZW9tX3BvaW50KGFscGhhID0gMC41KSArIGdlb21fc21vb3RoKG1ldGhvZD1sbSkgKyBsYWJzKHRpdGxlID0gIiAiKQojbG90cyBvZiB2ZXJ0aWNhbCBsaW5lcyAtIG5vIGhvcml6b250YWwgcGF0dGVybnMKI3N1Z2dlc3RzIHRoYXQgbW9udGhseSBjaGFyZ2VzIGhhcyBzb21lIGFzc29jaWF0aW9uIHdpdGggY2h1cm4gYXMgdGhlIGNodXJuZWQgY3VzdG9tZXJzIGFyZSBoZWF2aWx5IHJlcHJlc2VudGVkIG9uIHRoZSBoaWdoZXIgZW5kIG9mIHRoZSBtb250aGx5IGNoYXJnZXMKI3RoZXJlIGlzIG1vc3QgbGlrZWx5IGEgaGlnaGVyIGxpa2VsaWhvb2Qgb2YgY2h1cm4gb25jZSB5b3UgcmVhY2ggYSBjZXJ0YWluIHBvaW50IG9uIHRoZSBtb250aGx5IGZlZQoKCiNiZWxpZXZlIHRoYXQgdG90YWwgY2hhcmdlcyBjYW4gYWxzbyBiZSBhIHByb3h5IGZvciB0ZW51cmUtaGVuY2UgdGhlIHRyaWFuZ2xlIGxvb2tpbmcgc2NhdHRlcnBsb3QKc3AyIDwtIGdncGxvdChkYXRhID0gZGYsIGFlcyh4PVRvdGFsQ2hhcmdlcywgeT10ZW51cmUsIGNvbG9yPWZhY3RvcihDaHVybikpKSArIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpICtnZW9tX3Ntb290aChtZXRob2Q9bG0pICsgbGFicyh0aXRsZSA9ICIgIikKCiNoaXN0b2dyYW0gb2YgdGVudXJlCmhpc3QoZGYkdGVudXJlKQoKI2xvZyB0cmFuc2Zvcm1hdGlvbgpzcDMgPC0gZ2dwbG90KGRhdGEgPSBkZiwgYWVzKHg9TW9udGhseUNoYXJnZXMsIHk9dGVudXJlLCBjb2xvcj1mYWN0b3IoQ2h1cm4pKSkgKyBnZW9tX3BvaW50KGFscGhhID0gMC41KSArIGdlb21fc21vb3RoKG1ldGhvZD1sbSkgKyBzY2FsZV94X2xvZzEwKCkgKyBzY2FsZV95X2xvZzEwKCkgKyBsYWJzKHRpdGxlID0gIkxvZyB0cmFuc2Zvcm1hdGlvbiIpCgpzcDQgPC0gZ2dwbG90KGRhdGEgPSBkZiwgYWVzKHg9VG90YWxDaGFyZ2VzLCB5PXRlbnVyZSwgY29sb3I9ZmFjdG9yKENodXJuKSkpICsgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgK2dlb21fc21vb3RoKG1ldGhvZD1sbSkgKyBzY2FsZV94X2xvZzEwKCkgKyBsYWJzKHRpdGxlID0gIkxvZyB0cmFuc2Zvcm1hdGlvbiIpCgojYXdlc29tZSBsb29raW5nIGNoYXJ0cwpwbG90X2dyaWQoc3AxLCBzcDIsIHNwMywgc3A0LCBsYWJlbHM9IkFVVE8iKQoKI1RlbnVyZSBhbmQgdG90YWwgY2hhcmdlcyBhcmUgcG9zc2libHkgY29sbGluZWFyIG9yIGJvcmRlcmluZyBvbiBjb2xsaW5lYXJpdHkhCmBgYAoKCgoKCmBgYHtyIDItV2F5IENyb3NzIFRhYnVsYXRpb24gVGFibGVzIHRvIGNvbXBhcmUgY2F0ZWdvcmljYWwgdmFyaWFibGVzIC0gSW50ZXJuZXQgU2VydmljZX0KCiNJbnRlcm5ldCBTZXJ2aWNlCkNyb3NzVGFibGUoZGYkSW50ZXJuZXRTZXJ2aWNlLCBkZiRDaHVybiwgZGlnaXRzPTIsIHByb3AuYyA9IFRSVUUsCiAgcHJvcC5yID0gVFJVRSwgcHJvcC50ID0gRkFMU0UsIGNoaXNxID0gRkFMU0UsIGZvcm1hdCA9ICJTQVMiLCBleHBlY3RlZCA9IEZBTFNFKQojCgp0YWJsZShkZiRJbnRlcm5ldFNlcnZpY2UsIGRmJENodXJuKQoKcm91bmQoYWRkbWFyZ2lucyhwcm9wLnRhYmxlKHRhYmxlKGRmJEludGVybmV0U2VydmljZSwgZGYkQ2h1cm4pKSksMykKCgojU3RhdGlzdGljYWwgdGVzdApjaGlzcS50ZXN0KGRmJENodXJuLCBkZiRJbnRlcm5ldFNlcnZpY2UpCgoKYGBgCgoKYGBge3IgMi1XYXkgQ3Jvc3MgVGFidWxhdGlvbiBUYWJsZXMgdG8gY29tcGFyZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgLSBDb250cmFjdH0KI0NvbnRyYWN0CkNyb3NzVGFibGUoZGYkQ29udHJhY3QsIGRmJENodXJuLCBkaWdpdHM9MiwgcHJvcC5jID0gVFJVRSwKICBwcm9wLnIgPSBUUlVFLCBwcm9wLnQgPSBGQUxTRSwgY2hpc3EgPSBGQUxTRSwgZm9ybWF0ID0gIlNBUyIsIGV4cGVjdGVkID0gRkFMU0UpCiMKCnRhYmxlKGRmJENvbnRyYWN0LCBkZiRDaHVybikKCnJvdW5kKGFkZG1hcmdpbnMocHJvcC50YWJsZSh0YWJsZShkZiRDb250cmFjdCwgZGYkQ2h1cm4pKSksMykKCiNzdGF0aXN0aWNhbCB0ZXN0CmNoaXNxLnRlc3QoZGYkQ2h1cm4sIGRmJENvbnRyYWN0KQpgYGAKCmBgYHtyIENyZWF0ZSBiaW5zIGZvciBtb250aGx5IGNoYXJnZXMgdG8gZXhwbG9yZSBkYXRhLCBlY2hvPVRSVUV9CgojTGV0J3MgdGFrZSBhZ2UgYW5kIHB1dCBpdCBpbnRvIGJ1Y2tldHMKI2xldCdzIGdldCBtaW4gYW5kIG1heCBNb250aGx5IENoYXJnZSAKbWluKGRmJE1vbnRobHlDaGFyZ2VzKQptYXgoZGYkTW9udGhseUNoYXJnZXMpCm1lYW4oZGYkTW9udGhseUNoYXJnZXMpCm1lZGlhbihkZiRNb250aGx5Q2hhcmdlcykKCiNxdWFydGlsZXMKZ2dwbG90KGRmLCBhZXMoeT1Nb250aGx5Q2hhcmdlcykpICsgZ2VvbV9ib3hwbG90KCkKCiNxdWFudGlsZXMgLSB0YWtlcyBhIHZlY3RvciBvZiBwcm9wb3J0aW9ucwpxdWFudGlsZShkZiRNb250aGx5Q2hhcmdlcywgcHJvYnMgPSBjKDAsIDAuMiwgMC40LCAwLjYsIDAuOCwxKSkKCiNjcmVhdGUgbW9udGhseSBmZWUgYmFuZHMKZGYkbW9udGhseV9mZWVfYmluIDwtIGN1dChkZiRNb250aGx5Q2hhcmdlcywgCiAgICAgICAgICAgICAgICAgICBicmVha3M9IDUsIAogICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoImJpbjEiLCAiYmluMiIsICJiaW4zIiwgImJpbjQiLCAiYmluNSIpLCAKICAgICAgICAgICAgICAgICAgIHJpZ2h0PUZBTFNFKQoKdGFibGUoZGYkbW9udGhseV9mZWVfYmluKSAjY2hlY2sgcmVzdWx0cwoKCiNjaGVjayBhdmVyYWdlcyBhY3Jvc3MgdGhlIGRpZmZlcmVudCBmZWUgYmFuZHMKKGZlZXMgPC0gZGYgJT4lICAKICBncm91cF9ieShtb250aGx5X2ZlZV9iaW4pICU+JSAKICBzdW1tYXJpemUoYXZnX21vbnRobHlfZmVlID0gZm9ybWF0KHJvdW5kKG1lYW4oTW9udGhseUNoYXJnZXMpLDIpKSwKICAgICAgICAgICAgbWVkaWFuX21vbnRobHlfZmVlID0gZm9ybWF0KHJvdW5kKG1lZGlhbihNb250aGx5Q2hhcmdlcyksMikpKSkKCgojdmlzdWFsaXplIGZlZSBiYW5kcwpnZ3Bsb3QoZGF0YSA9IGRmLCBhZXMoeD1tb250aGx5X2ZlZV9iaW4sIGZpbGwgPSBmYWN0b3IoQ2h1cm4pKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikKYGBgCgojI1N0ZXAgMzogTWFuYWdlIGRhdGEKCgpgYGB7ciBDaGVjayBmb3IgTWlzc2luZyBEYXRhIC0gUmVtb3ZlIE1pc3NpbmcgRGF0YX0KI2FueSBtaXNzaW5nIGRhdGE/CgojVXNpbmcgVklNIHBhY2thZ2UKbWlzc2luZyA8LSBhZ2dyKGRmLCBwcm9wID0gVFJVRSwgYmFycyA9IFRSVUUpICMgLSBzb21lIG1pc3NpbmcgdmFsdWVzCgpzdW1tYXJ5KG1pc3NpbmcpCgojIyBTaG93IGNhc2VzIHdpdGggbWlzc2luZyB2YWx1ZXMKKG1pc3NpbmcuZGYgPC0gZGZbIWNvbXBsZXRlLmNhc2VzKGRmKSxdKSAjMTEgcm93cwojU29tZSB0b3RhbCBjaGFyZ2VzIG1pc3NpbmcKCiNXZSBjYW4gcmVtb3ZlIG1pc3Npbmcgcm93cyAtIHJlbW92aW5nIHRoZW0gd29uJ3QgYWZmZWN0IG91ciByZXN1bHRzIHRoYXQgbXVjaCAtIHRoaXMgaXMgdGhlIGNvZGUgdG8gcmVtb3ZlIGJlbG93CmRmIDwtIGRmW2NvbXBsZXRlLmNhc2VzKGRmKSxdIAoKCiNjaGVjayByZXN1bHRzCnN1bShpcy5uYShkZiRUb3RhbENoYXJnZXMpKSAjYWxsIGdvbmUKCiN2aXN1YWxpemUgZW50aXJlIGRhdGFmcmFtZSBhZ2FpbgphZ2dyKGRmLCBwcm9wID0gVFJVRSwgYmFycyA9IFRSVUUpCmBgYAoKCmBgYHtyIENoZWNrIGZvciBkdXBsaWNhdGUgZGF0YX0KI2FueSBkdXBsaWNhdGUgcm93cyAtCmFueUR1cGxpY2F0ZWQoZGYpCgojYW55IGR1cGxpY2F0ZSBjb2x1bW5zIC0KbmFtZXMoZGYpW2R1cGxpY2F0ZWQobmFtZXMoZGYpKV0KCmRmW25hbWVzKGRmKVshZHVwbGljYXRlZChuYW1lcyhkZikpXV0KCmBgYAoKVGhlcmUgd2VyZSBubyBkdXBsaWNhdGVzIHRvIHdvcnJ5IGFib3V0CgoKCiMjIFN0ZXAgNDogU3RhdGlzdGljYWwgYW5hbHlzaXMgJiB1bmRlcnN0YW5kaW5nIHJlbGF0aW9uc2hpcHMKCk5vdyB3ZSB3YW50IHRvIGRpZyBkZWVwZXIKV2UgaGF2ZSBzb21lIGlkZWFzIG9mIHdoYXQgdG8gbG9vayBmb3IgdGhhbmtzIHRvIG91ciBFREEKCgpgYGB7ciBQYWlycyBmdW5jdGlvbn0KI3BhaXJzLnBhbmVscwpkZiAlPiUgCiAgc2VsZWN0X2lmKGlzLm51bWVyaWMpICU+JSAKICBzY2FsZSgpICU+JSAKICAgIHBhaXJzLnBhbmVscygpCgojVGVudXJlIGFuZCBUb3RhbENoYXJnZXMgaGFzIGEgdmVyeSBoaWdoIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IQojQWxzbyBzaG93cyB1cyB0aGF0IFNlbmlvckNpdGl6ZW4gaXMgY29kZWQgYXMgYSBudW1lcmljYWwgdmFyaWFibGUgKDAgb3IgMSkKI01vbnRobHkgY2hhcmdlcyBhbmQgVG90YWxDaGFyZ2VzIGhhcyBhIGhpZ2ggY29ycmVsYXRpb24gY29lZmZpY2llbnQgYWxzby4KYGBgCgoKCmBgYHtyIENvcnJlbGF0aW9uIG1hdHJpeCBmb3IgbnVtZXJpYyB2YXJpYWJsZXN9CgojIGkgdGFrZSB0aGVzZSByZXN1bHRzIHdpdGggYSBncmFpbiBvZiBzYWx0LgpkZiAlPiUgCiAgc2VsZWN0X2lmKGlzLm51bWVyaWMpICU+JSAKICBjb3IoKSAlPiUgCiAgICBjb3JycGxvdCh0eXBlID0gInVwcGVyIiwgaW5zaWcgPSAiYmxhbmsiLCBhZGRDb2VmLmNvbCA9ICJibGFjayIsIGRpYWc9RkFMU0UpCiNtb250aGx5IGNoYXJnZXMgaXMgaGlnaGx5LWNvcnJlbGF0ZWQgd2l0aCB0b3RhbCBjaGFyZ2VzIHdoaWNoIG1ha2VzIHNlbnNlLiBhdCAwLjY1LgoKI3RvdGFsIGNoYXJnZXMgYW5kIHRlbnVyZSBoYXMgYSBoaWdoIGNvcnJlbGF0aW9uLjgzCiN3aGljaCBpcyBib3JkZXJpbmcgb24gY29sbGluZWFyaXR5CmBgYAoKCmBgYHtyIFJlY29kZSBjaHVybiBjaGFyYWN0ZXIgdmFyaWFibGUgaW50byBhIG51bWVyaWMgdmFyaWFibGV9CnN0cihkZiRDaHVybikKCihjaHVyblJhdGUgPC0gcm91bmQocHJvcC50YWJsZSh0YWJsZShkZiRDaHVybikpLDUpKQooY2h1cm5fcmF0ZV9vdmVyYWxsIDwtIGNodXJuUmF0ZVtbMl1dKQoKIyByZWNvZGUgY2h1cm4gdmFyaWFibGUgaW50byBhIG51bWVyaWMgdmFyaWFibGUKZGYgPC0gZGYgJT4lIAogICAgICBtdXRhdGUoQ2h1cm4gPSBpZmVsc2UoQ2h1cm4gPT0gIlllcyIsIDEsIDApKSAKCiNjaGVjayByZXN1bHRzCmhlYWQoZGYkQ2h1cm4pCmBgYAoKCmBgYHtyIENodXJuIHJhdGUgYnkgY29udHJhY3QgdHlwZSwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRX0KI2RhdGEgZnJhbWUgZ3JvdXBlZCBieSBjb250cmFjdApjb250cmFjdC5kZiA8LSBkZiAlPiUgCiAgZ3JvdXBfYnkoQ29udHJhY3QpICU+JSAKICBzdW1tYXJpemUodG90YWxfY291bnQgPSBuKCksCiAgICB0b3RhbF9jaHVybnMgPSBzdW0oQ2h1cm4pKSAlPiUgCiAgbXV0YXRlKGNodXJuX3JhdGUgPSB0b3RhbF9jaHVybnMvdG90YWxfY291bnQpCgpjb250cmFjdC5kZiAjY2hlY2sgcmVzdWx0cwoKI2NoZWNrIGNodXJuIGNvbHVtbnMKc3VtKGNvbnRyYWN0LmRmJHRvdGFsX2NodXJucykKc3VtKGNvbnRyYWN0LmRmJHRvdGFsX2NvdW50KQoKI2FkZCBoaWdobGlnaHQgZmxhZyBjb2x1bW4KY29udHJhY3QuZGYgPC0gY29udHJhY3QuZGYgJT4lIAptdXRhdGUoaGlnaGxpZ2h0X2ZsYWcgPQogICAgaWZlbHNlKGNodXJuX3JhdGUgPiBjaHVybl9yYXRlX292ZXJhbGwsIDEsIDApKQoKI2NoZWNrIHJlc3VsdHMKaGVhZChjb250cmFjdC5kZiRoaWdobGlnaHRfZmxhZykKCiNwbG90IHJlc3BvbnNlIHJhdGUgYnkgY2F0IHZhcgpnZ3Bsb3QoZGF0YT1jb250cmFjdC5kZiwgYWVzKHg9cmVvcmRlcihDb250cmFjdCwgY2h1cm5fcmF0ZSksIHk9Y2h1cm5fcmF0ZSwKICBmaWxsID0gZmFjdG9yKGhpZ2hsaWdodF9mbGFnKSkpICsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9Y2h1cm5fcmF0ZV9vdmVyYWxsLCBsaW5ldHlwZT0iZGFzaGVkIiwgY29sb3IgPSAiYmxhY2siKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpICsKICBjb29yZF9mbGlwKCkgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygnIzU5NTk1OScsICcjZTQxYTFjJykpICsKICBsYWJzKHggPSAnICcsIHkgPSAnQ2h1cm4gUmF0ZScsIHRpdGxlID0gc3RyX2MoIkNodXJuIHJhdGUgYnkgY29udHJhY3QiKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgoKCmBgYHtyIENodXJuIHJhdGUgYnkgbW9udGhseSBmZWUgbGV2ZWx9CgojZGF0YSBmcmFtZSBncm91cGVkIGJ5IGZlZQptb250aGx5LmZlZS5kZiA8LSBkZiAlPiUgCiAgZ3JvdXBfYnkobW9udGhseV9mZWVfYmluKSAlPiUgCiAgc3VtbWFyaXplKHRvdGFsX2NvdW50ID0gbigpLAogICAgdG90YWxfY2h1cm5zID0gc3VtKENodXJuKSkgJT4lIAogIG11dGF0ZShjaHVybl9yYXRlID0gdG90YWxfY2h1cm5zL3RvdGFsX2NvdW50KQoKaGVhZChtb250aGx5LmZlZS5kZikgI2NoZWNrIHJlc3VsdHMKCiNjaGVjayBjaHVybiBjb2x1bW5zCnN1bShtb250aGx5LmZlZS5kZiR0b3RhbF9jaHVybnMpCnN1bShtb250aGx5LmZlZS5kZiR0b3RhbF9jb3VudCkKCiNhZGQgaGlnaGxpZ2h0IGZsYWcgY29sdW1uCm1vbnRobHkuZmVlLmRmIDwtIG1vbnRobHkuZmVlLmRmICU+JSAKbXV0YXRlKGhpZ2hsaWdodF9mbGFnID0KICAgIGlmZWxzZShjaHVybl9yYXRlID4gY2h1cm5fcmF0ZV9vdmVyYWxsLCAxLCAwKSkKCiNjaGVjayByZXN1bHRzCmhlYWQobW9udGhseS5mZWUuZGYkaGlnaGxpZ2h0X2ZsYWcpCgojcGxvdCByZXNwb25zZSByYXRlIGJ5IGZlZSBkaWZmZXJlbmNlCnBsb3QuZmVlLmRpZmYgPC0gZ2dwbG90KGRhdGE9bW9udGhseS5mZWUuZGYsIGFlcyh4PXJlb3JkZXIobW9udGhseV9mZWVfYmluLCBjaHVybl9yYXRlKSwgeT1jaHVybl9yYXRlLAogIGZpbGwgPSBmYWN0b3IoaGlnaGxpZ2h0X2ZsYWcpKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD1jaHVybl9yYXRlX292ZXJhbGwsIGxpbmV0eXBlPSJkYXNoZWQiLCBjb2xvciA9ICJibGFjayIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkgKwogIGNvb3JkX2ZsaXAoKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCcjNTk1OTU5JywgJyNlNDFhMWMnKSkgKwogIGxhYnMoeCA9ICcgJywgeSA9ICdDaHVybiBSYXRlJywgdGl0bGUgPSBzdHJfYygiQ2h1cm4gcmF0ZSBieSBtb250aGx5IGZlZSIpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKcGxvdC5mZWUuZGlmZgoKYGBgCgoKCmBgYHtyIENodXJuIHJhdGUgYnkgSW50ZXJuZXRTZXJ2aWNlfQoKCiNkYXRhIGZyYW1lIGdyb3VwZWQgYnkgZmVlCmludGVybmV0LmRmIDwtIGRmICU+JSAKICBncm91cF9ieShJbnRlcm5ldFNlcnZpY2UpICU+JSAKICBzdW1tYXJpemUodG90YWxfY291bnQgPSBuKCksCiAgICB0b3RhbF9jaHVybnMgPSBzdW0oQ2h1cm4pKSAlPiUgCiAgbXV0YXRlKGNodXJuX3JhdGUgPSB0b3RhbF9jaHVybnMvdG90YWxfY291bnQpCgpoZWFkKGludGVybmV0LmRmKSAjY2hlY2sgcmVzdWx0cwoKI2NoZWNrIGNodXJuIGNvbHVtbnMKc3VtKGludGVybmV0LmRmJHRvdGFsX2NodXJucykKc3VtKGludGVybmV0LmRmJHRvdGFsX2NvdW50KQoKI2FkZCBoaWdobGlnaHQgZmxhZyBjb2x1bW4KaW50ZXJuZXQuZGYgPC0gaW50ZXJuZXQuZGYgJT4lIAptdXRhdGUoaGlnaGxpZ2h0X2ZsYWcgPQogICAgaWZlbHNlKGNodXJuX3JhdGUgPiBjaHVybl9yYXRlX292ZXJhbGwsIDEsIDApKQoKI2NoZWNrIHJlc3VsdHMKaGVhZChpbnRlcm5ldC5kZiRoaWdobGlnaHRfZmxhZykKCiNwbG90IHJlc3BvbnNlIHJhdGUgYnkgZmVlIGRpZmZlcmVuY2UKZ2dwbG90KGRhdGE9aW50ZXJuZXQuZGYsIGFlcyh4PXJlb3JkZXIoSW50ZXJuZXRTZXJ2aWNlLCBjaHVybl9yYXRlKSwgeT1jaHVybl9yYXRlLAogIGZpbGwgPSBmYWN0b3IoaGlnaGxpZ2h0X2ZsYWcpKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD1jaHVybl9yYXRlX292ZXJhbGwsIGxpbmV0eXBlPSJkYXNoZWQiLCBjb2xvciA9ICJibGFjayIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkgKwogIGNvb3JkX2ZsaXAoKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCcjNTk1OTU5JywgJyNlNDFhMWMnKSkgKwogIGxhYnMoeCA9ICcgJywgeSA9ICdDaHVybiBSYXRlJywgdGl0bGUgPSBzdHJfYygiQ2h1cm4gcmF0ZSBieSBpbnRlcm5ldCBzZXJ2aWNlIikpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKCgpgYGB7ciBDaHVybiByYXRlIGJ5IFBheW1lbnQgTWV0aG9kfQoKI2RhdGEgZnJhbWUgZ3JvdXBlZCBieSBwYXltZW50IG1ldGhvZApweW10Lm1ldGhvZC5kZiA8LSBkZiAlPiUgCiAgZ3JvdXBfYnkoUGF5bWVudE1ldGhvZCkgJT4lIAogIHN1bW1hcml6ZSh0b3RhbF9jb3VudCA9IG4oKSwKICAgIHRvdGFsX2NodXJucyA9IHN1bShDaHVybikpICU+JSAKICBtdXRhdGUoY2h1cm5fcmF0ZSA9IHRvdGFsX2NodXJucy90b3RhbF9jb3VudCkKCmhlYWQocHltdC5tZXRob2QuZGYpICNjaGVjayByZXN1bHRzCgojY2hlY2sgY2h1cm4gY29sdW1ucwpzdW0ocHltdC5tZXRob2QuZGYkdG90YWxfY2h1cm5zKQpzdW0ocHltdC5tZXRob2QuZGYkdG90YWxfY291bnQpCgojYWRkIGhpZ2hsaWdodCBmbGFnIGNvbHVtbgpweW10Lm1ldGhvZC5kZiA8LSBweW10Lm1ldGhvZC5kZiAlPiUgCm11dGF0ZShoaWdobGlnaHRfZmxhZyA9CiAgICBpZmVsc2UoY2h1cm5fcmF0ZSA+IGNodXJuX3JhdGVfb3ZlcmFsbCwgMSwgMCkpCgojY2hlY2sgcmVzdWx0cwpoZWFkKHB5bXQubWV0aG9kLmRmJGhpZ2hsaWdodF9mbGFnKQoKI3Bsb3QgcmVzcG9uc2UgcmF0ZSBieSBmZWUgZGlmZmVyZW5jZQpnZ3Bsb3QoZGF0YT1weW10Lm1ldGhvZC5kZiwgYWVzKHg9cmVvcmRlcihQYXltZW50TWV0aG9kLCBjaHVybl9yYXRlKSwgeT1jaHVybl9yYXRlLAogIGZpbGwgPSBmYWN0b3IoaGlnaGxpZ2h0X2ZsYWcpKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD1jaHVybl9yYXRlX292ZXJhbGwsIGxpbmV0eXBlPSJkYXNoZWQiLCBjb2xvciA9ICJibGFjayIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkgKwogIGNvb3JkX2ZsaXAoKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCcjNTk1OTU5JywgJyNlNDFhMWMnKSkgKwogIGxhYnMoeCA9ICcgJywgeSA9ICdDaHVybiBSYXRlJywgdGl0bGUgPSBzdHJfYygiQ2h1cm4gcmF0ZSBieSBwYXltZW50IG1ldGhvZCIpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKYGBgCgpXZSBoYXZlIHVuY292ZXJlZCBzb21lIGluc2lnaHRzIHNvIGZhcjoKCgpOb3cgd2UgaGF2ZSB0byBkZWNpZGUgd2hldGhlciB3ZSBzaG91bGQgZG8gc29tZSBkYXRhIG1vZGVsaW5nLgoKIyMgU3RlcCA1QTogRGF0YSBwcmUtcHJvZXNzaW5nCgoKCmBgYHtyIEdldCBkYXRhIHJlYWR5IGZvciBtb2RlbGluZ30KI2xldCdzIHVzZSBhIGZyZXNoIGRhdGFzZXQKZGZfcmF3IDwtIHJlYWQuY3N2KCJ+L1JfUHJvZ3JhbW1pbmcvSEkgQm9vdGNhbXAvVGVsY28tQ3VzdG9tZXItQ2h1cm4uY3N2IikKCiNzdHIoZGZfcmF3KSAjY2hlY2sgcmVzdWx0cwoKI1JlbW92ZSByb3dzIHdpdGggbWlzc2luZyBvYnNlcnZhdGlvbnMKZGZfcmF3IDwtIGRmX3Jhd1tjb21wbGV0ZS5jYXNlcyhkZl9yYXcpLF0gCgojZ2V0IHJpZCBvZiBjdXN0b21lcklEIGNvbHVtbiAtIHdlIGRvbid0IG5lZWQgaXQgZm9yIG1vZGVsaW5nCiNsZXQncyBqdXN0IGtlZXAgdGhlIHZhcmlhYmxlcyB3ZSBuZWVkCmRmX3ByZW1vZGVsIDwtIGRmX3JhdyAlPiUgCiAgc2VsZWN0KGdlbmRlciwgU2VuaW9yQ2l0aXplbiwgUGFydG5lciwgRGVwZW5kZW50cywgdGVudXJlLCBQaG9uZVNlcnZpY2UsIE11bHRpcGxlTGluZXMsIEludGVybmV0U2VydmljZSwgT25saW5lU2VjdXJpdHksIE9ubGluZUJhY2t1cCwgRGV2aWNlUHJvdGVjdGlvbiwgVGVjaFN1cHBvcnQsIFN0cmVhbWluZ1RWLCBTdHJlYW1pbmdNb3ZpZXMsQ29udHJhY3QsUGFwZXJsZXNzQmlsbGluZywgUGF5bWVudE1ldGhvZCwgTW9udGhseUNoYXJnZXMsIFRvdGFsQ2hhcmdlcywgQ2h1cm4pCgoKI21lcmdlIHVubmVjZXNzYXJ5IGxldmVscyAobm8gaW50ZXJuZXQpIGFuZCByZWNvZGUgdmFyaWFibGVzIHRvIGR1bW15IHZhcmlhYmxlCmRmX2R1bW15IDwtIGRmX3ByZW1vZGVsICU+JSAKICAgIG11dGF0ZShnZW5kZXIgPSBpZmVsc2UoZ2VuZGVyID09ICJGZW1hbGUiLCAxLCAwKSwKICAgICAgICAgICBQYXJ0bmVyID0gaWZlbHNlKFBhcnRuZXIgPT0gIlllcyIsIDEsIDApLAogICAgICAgICAgIERlcGVuZGVudHMgPSBpZmVsc2UoRGVwZW5kZW50cyA9PSAiWWVzIiwgMSwgMCksCiAgICAgICAgICAgUGhvbmVTZXJ2aWNlID0gaWZlbHNlKFBob25lU2VydmljZSA9PSAiWWVzIiwgMSwgMCksCiAgICAgICAgICAgTXVsdGlwbGVMaW5lcyA9IGlmZWxzZShNdWx0aXBsZUxpbmVzID09ICJZZXMiLCAxLCAwKSwKICAgICAgICAgIE9ubGluZVNlY3VyaXR5ID0gaWZlbHNlKE9ubGluZVNlY3VyaXR5ICA9PSAiWWVzIiwgMSwgMCksCiAgICAgICAgICBPbmxpbmVCYWNrdXAgPSBpZmVsc2UoT25saW5lQmFja3VwICA9PSAiWWVzIiwgMSwgMCksCiAgICAgICAgICBEZXZpY2VQcm90ZWN0aW9uID0gaWZlbHNlKERldmljZVByb3RlY3Rpb24gID09ICJZZXMiLCAxLCAwKSwKICAgICAgICAgIFRlY2hTdXBwb3J0ID0gaWZlbHNlKFRlY2hTdXBwb3J0ICA9PSAiWWVzIiwgMSwgMCksCiAgICAgICAgICBTdHJlYW1pbmdUViA9IGlmZWxzZShTdHJlYW1pbmdUViAgPT0gIlllcyIsIDEsIDApLAogICAgICAgICAgU3RyZWFtaW5nTW92aWVzID0gaWZlbHNlKFN0cmVhbWluZ01vdmllcyAgPT0gIlllcyIsIDEsIDApLAogICAgICAgICAgUGFwZXJsZXNzQmlsbGluZyA9IGlmZWxzZShQYXBlcmxlc3NCaWxsaW5nICA9PSAiWWVzIiwgMSwgMCksCiAgICAgICAgICBDaHVybiA9IGlmZWxzZShDaHVybiA9PSAiWWVzIiwgMSwgMCkpCgojY2hlY2sgcmVzdWx0cwpzdHIoZGZfZHVtbXkpCgojcmVjb2RlIG51bWVyaWMgdmFyaWFibGVzIGludG8gZmFjdG9yIHZhcmlhYmxlcwpkZl9kdW1teSA8LSBkZl9kdW1teSAlPiUgCiAgbXV0YXRlX2F0KGMoImdlbmRlciIsICJTZW5pb3JDaXRpemVuIiwgIlBhcnRuZXIiLCAiRGVwZW5kZW50cyIsICJQaG9uZVNlcnZpY2UiLCAiTXVsdGlwbGVMaW5lcyIsICJJbnRlcm5ldFNlcnZpY2UiLCAiT25saW5lU2VjdXJpdHkiLCAiT25saW5lQmFja3VwIiwgIkRldmljZVByb3RlY3Rpb24iLCAiVGVjaFN1cHBvcnQiLCAiU3RyZWFtaW5nVFYiLCAiU3RyZWFtaW5nTW92aWVzIiwiQ29udHJhY3QiLCJQYXBlcmxlc3NCaWxsaW5nIiwgIlBheW1lbnRNZXRob2QiLCAiQ2h1cm4iKSwgLmZ1bnMgPSBmYWN0b3IpCgojY2hlY2sgcmVzdWx0cwpzdHIoZGZfZHVtbXkpCgpgYGAKCgpgYGB7ciBDaGVjayBjaHVybiB2YXJpYWJsZSBpZiBuZWVkZWR9CnN0cihkZl9kdW1teSRDaHVybikgI2NoZWNrIGNodXJuIHZhcmlhYmxlCgp0YWJsZShkZl9kdW1teSRDaHVybikKYGBgCgoKCgoKIyMgUGFydCA1QjogTW9kZWxpbmcgLSBMb2dpc3RpYyBSZWdyZXNzaW9uCgoKCmBgYHtyIFRyYWluIGFuZCB0ZXN0IGRhdGEgc3BsaXR9CgojIENoZWNrIHRvdGFsIG51bWJlciBvZiByb3dzIGluIG91ciBkYXRhc2V0IGJlZm9yZSBzcGxpdHRpbmcKbnJvdyhkZl9kdW1teSkKCiNTZXQgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5CnNldC5zZWVkKDEyMykgCgojU3BsaXQgdGhlIGRhdGFzZXQgaW50byB0cmFpbmluZyBhbmQgdGVzdCBzZXRzIGF0IDcwOjMwIHJhdGlvIHVzaW5nIGNhVG9vbHMgcGFja2FnZQpzcGxpdCA8LSBzYW1wbGUuc3BsaXQoZGZfZHVtbXkkQ2h1cm4sIFNwbGl0UmF0aW8gPSAwLjcpCnRyYWluX2RhdGEgPC0gc3Vic2V0KGRmX2R1bW15LCBzcGxpdCA9PSBUUlVFKQp0ZXN0X2RhdGEgPC0gc3Vic2V0KGRmX2R1bW15LCBzcGxpdCA9PSBGQUxTRSkKCiNDaGVjayBpZiBkaXN0cmlidXRpb24gb2YgcGFydGl0aW9uIGRhdGEgaXMgY29ycmVjdCBmb3IgdHJhaW4gYW5kIHRlc3Qgc2V0CnByb3AudGFibGUodGFibGUodHJhaW5fZGF0YSRDaHVybikpCnByb3AudGFibGUodGFibGUodGVzdF9kYXRhJENodXJuKSkgCgpgYGAKCgpgYGB7ciBMb2dpc3RpYyBSZWdyZXNzaW9uIE1vZGVsIDEgLSBCYXNlbGluZSBNb2RlbH0KCnNldC5zZWVkKDEpICAjIHNldCBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkKCiNVc2UgZ2xtIGZ1bmN0aW9uIGZyb20gZ2xtbmV0IHBhY2thZ2UgdG8gY3JlYXRlIG1vZGVsCm1vZGVsXzAxX2dsbSA8LSBnbG0oZm9ybXVsYSA9IENodXJuIH4gLiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluX2RhdGEsIGZhbWlseSA9ICJiaW5vbWlhbCIpCiAKI3VzZSB0byBkZWJ1ZyBpZiBnbG0gZnVuY3Rpb24gbm90IHdvcmtpbmcgICAKIyB0cmFpbl9kYXRhICU+JQojICAgc2VsZWN0X2lmKGlzLmZhY3RvcikgJT4lCiMgICBsYXBwbHkodGFibGUpCiAgICAgICAgICAgICAgICAgICAgICAgICAKI1ByaW50IHRoZSBtb2RlbCBvdXRwdXQKc3VtbWFyeShtb2RlbF8wMV9nbG0pCmBgYAoKCgpUaGUgT2RkcyBSYXRpbyBhbmQgUHJvYmFiaWxpdHkgb2YgZWFjaCB4IHZhcmlhYmxlIGlzIGNhbGN1bGF0ZWQgYmFzZWQgb24gdGhlIGZvcm11bGFlLCBPZGRzIFJhdGlvID0gZXhwKENvLWVmZmljaWVudCBlc3RpbWF0ZSkgUHJvYmFiaWxpdHkgPSBPZGRzIFJhdGlvIC8gKDEgKyBPZGRzIFJhdGlvKQoKYGBge3IgQ29uZmlkZW5jZSBJbnRlcnZhbHMgYW5kIENvZWZmaWNpZW50cyBJbnRlcnByZXRhdGlvbn0KCiMjSW5kaXZpZHVhbCBjb2VmZmljaWVudHMgc2lnbmlmaWNhbmNlIGFuZCBpbnRlcnByZXRhdGlvbgoKI2xpYnJhcnkoY29lZi5sbUxpc3QpCnN1bW1hcnkobW9kZWxfMDFfZ2xtKQoKI3ByaW50cyBjb25maWRlbmNlIGludGVydmFscwpleHAoY29uZmludChtb2RlbF8wMV9nbG0pKQoKCiNwbG90IGNvZWZmaWNpZW50cyBvbiBvZGRzIHJhdGlvIHVzaW5nIHNqUGxvdCBwYWNrYWdlCnBsb3RfbW9kZWwobW9kZWxfMDFfZ2xtLCB2bGluZS5jb2xvciA9ICJyZWQiLAogIHNvcnQuZXN0ID0gVFJVRSwgc2hvdy52YWx1ZXMgPSBUUlVFKQoKYGBgCgoKYGBge3IgQ2hlY2sgZm9yIGNvbGxpbmVhcml0eSAtIFZhcmlhbmNlIEluZmxhdGlvbiBGYWN0b3J9CiNDYWxjdWxhdGUgVmFyaWFuY2UgSW5mbGF0aW9uIGZhY3RvciB1c2luZyBjYXIgcGFja2FnZQp2aWYobW9kZWxfMDFfZ2xtKQoKIyBGZWF0dXJlICh4KSB2YXJpYWJsZXMgd2l0aCBhIFZJRiB2YWx1ZSBhYm92ZSA1IGluZGljYXRlIGhpZ2ggZGVncmVlIG9mCiMgbXVsdGktY29sbGluZWFyaXR5LgpgYGAKCk1vbnRobHlDaGFyZ2VzICBoYXMgYSB2ZXJ5IGhpZ2ggVklGIGF0IDYwMCsuIFdlIGhhdmUgc29tZSBvdGhlciB2YXJpYWJsZXMgZS5nLiBJbnRlcm5ldFNlcnZpY2VGaWJlci5vcHRpYywgSW50ZXJuZXRTZXJ2aWNlTm8sIGFuZCBUb3RhbENoYXJnZXMsIFBob25lU2VydmljZVllcywgd2l0aCBoaWdoIFZJRnMuIE5vcm1hbGx5LCB3ZSB3b3VsZCB3YW50IHRvIHJlbW92ZSB2YXJpYWJsZXMgd2l0aCBoaWdoIFZJRiBhcyBpdCBjYW4gcmVhbGx5IG1lc3Mgd2l0aCBvdXIgbW9kZWwuCgoKYGBge3IgTG9naXN0aWMgUmVncmVzc2lvbiBBY2N1cmFjeSBhbmQgTW9kZWwgRXZhbHVhdGlvbiB1c2luZyBNb2RlbCAxfQpwcmVkIDwtIHByZWRpY3QobW9kZWxfMDFfZ2xtLCB0ZXN0X2RhdGEsIHR5cGUgPSAicmVzcG9uc2UiKSAjcHJlZGljdCB1c2luZyB0ZXN0IGRhdGEKCiNjaGVjayByZXN1bHRzCmhlYWQocHJlZCkKCnByZWRpY3RlZCA8LSByb3VuZChwcmVkKSAjPjAuNSB3aWxsIGNvbnZlcnQgdG8gMQoKI2NvbnRpbmdlbmN5IHRhYmxlCmNvbnRpbmdlbmN5X3RhYiA8LSB0YWJsZSh0ZXN0X2RhdGEkQ2h1cm4sIHByZWRpY3RlZCkKY29udGluZ2VuY3lfdGFiCgojIENvbmZ1c2lvbiBNYXRyaXggdXNpbmcgdGhlIGNhcmV0IHBhY2thZ2UKY2FyZXQ6OmNvbmZ1c2lvbk1hdHJpeChjb250aW5nZW5jeV90YWIpCgojUGxvdCBST0MgQ3VydmUgJiBDYWxjdWxhdGUgQVVDIGFyZWEKbGlicmFyeShST0NSKQoKI1JPQyBDdXJ2ZXMgYXJlIHVzZWZ1bCBmb3IgY29tcGFyaW5nIGNsYXNzaWZpZXJzCgojQ2hlY2sgZGF0YSBzdHJ1Y3R1cmVzIGZpcnN0IG9yIGVsc2UgdGhlIFJPQyBjdXJ2ZSB3b24ndCBwbG90CnR5cGVvZihwcmVkaWN0ZWQpCnR5cGVvZih0ZXN0X2RhdGEkQ2h1cm4pCgoKcHIgPC0gcHJlZGljdGlvbihwcmVkLCB0ZXN0X2RhdGEkQ2h1cm4pCnByZiA8LSBwZXJmb3JtYW5jZShwciwgbWVhc3VyZSA9ICJ0cHIiLCB4Lm1lYXN1cmUgPSAiZnByIikKCnBsb3QocHJmKQoKI1RoZSBpZGVhbCBST0MgY3VydmUgaHVncyB0aGUgdG9wIGxlZnQgY29ybmVyLCBpbmRpY2F0aW5nIGEgaGlnaAojdHJ1ZSBwb3NpdGl2ZSByYXRlIGFuZCBhIGxvdyBmYWxzZSBwb3NpdGl2ZSByYXRlLgojVHJ1ZSBwb3NpdGl2ZSByYXRlIG9uIHktYXhpcwojRmFsc2UgcG9zaXRpdmUgcmF0ZSBvbiB0aGUgeC1heGlzCgojVGhlIGxhcmdlciB0aGUgQVVDLCB0aGUgYmV0dGVyIHRoZSBjbGFzc2lmaWVyCiNUaGUgQVVDIGxpbmUgaXMgaW5zdWZmaWNpZW50IHRvIGlkZW50aWZ5IGEgYmVzdCBtb2RlbAojSXQncyB1c2VkIGluIGNvbWJpbmF0aW9uIHdpdGggcXVhbGl0YXRpdmUgZXhhbWluYXRpb24KI29mIHRoZSBST0MgY3VydmUKYXVjIDwtIHBlcmZvcm1hbmNlKHByLCBtZWFzdXJlID0gImF1YyIpCmF1YwoKIyBBVUMgaXMgMC44NjE5ODkKYXMubnVtZXJpYyhwZXJmb3JtYW5jZShwciwgbWVhc3VyZSA9ICJhdWMiKUB5LnZhbHVlcykKCiNEb3VibGUgZGVuc2l0eSBwbG90IGZvciBleHBsYWluaW5nIGFuZCBwaWNraW5nIHRocmVzaG9sZHMgb2YgcHJlZGljdGVkIGNodXJuIHByb2JhYmlsaXRpZXMuIFBlcmhhcHMgYmV0dGVyIGFsdGVybmF0aXZlIHRoYW4gQVVDIG9yIFJPQyBjdXJ2ZQpnZ3Bsb3QoZGF0YSA9IHRlc3RfZGF0YSkgKyBnZW9tX2RlbnNpdHkoYWVzKHg9cHJlZCwgY29sb3IgPSBDaHVybixsaW5ldHlwZSA9IENodXJuKSkKYGBgCkFVQyBJbnRlcnByZXRhdGlvbgpBOiBPdXRzdGFuZGluZyA9IDAuOSB0byAxLjAKQjogRXhjZWxsZW50L0dvb2QgPSAwLjggdG8gMC45CkM6IEFjY2VwdGFibGUvRmFpciA9IDAuNyB0byAwLjgKRDogUG9vciA9IDAuNiB0byAwLjcKRTogTm8gRGlzY3JpbWluYXRpb24gPSAwLjUgdG8gMC42CgoKYGBge3IgTG9nIFJlZ3Jlc3Npb24gTW9kZWwgMiB3aXRob3V0IGhpZ2ggVklGIHZhcmlhYmxlfQpzZXQuc2VlZCgxKSAgIyBmb3IgcmVwcm9kdWNpYmlsaXR5Cm1vZGVsXzAyX2dsbSA8LSBnbG0oZm9ybXVsYSA9IENodXJuIH4gLiAtTW9udGhseUNoYXJnZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluX2RhdGEsIGZhbWlseSA9ICJiaW5vbWlhbCIpCgpzdW1tYXJ5KG1vZGVsXzAyX2dsbSkKCnZpZihtb2RlbF8wMl9nbG0pCgojcGxvdCBjb2VmZmljaWVudHMgb24gb2RkcyByYXRpbwpwbG90X21vZGVsKG1vZGVsXzAyX2dsbSwgdmxpbmUuY29sb3IgPSAicmVkIiwKICBzb3J0LmVzdCA9IFRSVUUsIHNob3cudmFsdWVzID0gVFJVRSkKYGBgCkludGVybmV0U2VydmljZSAtIEZpYmVyIE9wdGljIGNhYmxlIHN0aWxsIGluY3JlYXNlcyBsaWtlbGlob29kIG9mIGNodXJuIGZvbGxvd2VkIGJ5IHBhcGVybGVzcyBiaWxsaW5nIGFuZCBiZWluZyBhIHNlbmlvciBjaXRpemVuLgoKCmBgYHtyIExvZ2lzdGljIFJlZ3Jlc3Npb24gQWNjdXJhY3kgYW5kIE1vZGVsIEV2YWx1YXRpb24gdXNpbmcgTW9kZWwgMn0KcHJlZCA8LSBwcmVkaWN0KG1vZGVsXzAyX2dsbSwgdGVzdF9kYXRhLCB0eXBlID0gInJlc3BvbnNlIikgI3ByZWRpY3QgdXNpbmcgdGVzdCBkYXRhCgojY2hlY2sgcmVzdWx0cwpoZWFkKHByZWQpCgpwcmVkaWN0ZWQgPC0gcm91bmQocHJlZCkgIz4wLjUgd2lsbCBjb252ZXJ0IHByZWRpY3RlZCB2YWx1ZXMgdG8gMQoKI2NvbnRpbmdlbmN5IHRhYmxlIC0gbWFudWFsbHkgZG9uZSB3aXRoIHRhYmxlIGZ1bmN0aW9uCmNvbnRpbmdlbmN5X3RhYiA8LSB0YWJsZSh0ZXN0X2RhdGEkQ2h1cm4sIHByZWRpY3RlZCkKY29udGluZ2VuY3lfdGFiCgojIENvbmZ1c2lvbiBNYXRyaXggdXNpbmcgdGhlIGNhcmV0IHBhY2thZ2UKY2FyZXQ6OmNvbmZ1c2lvbk1hdHJpeChjb250aW5nZW5jeV90YWIpCgojUGxvdCBST0MgQ3VydmUgJiBDYWxjdWxhdGUgQVVDIGFyZWEKbGlicmFyeShST0NSKQoKCiNjaGVjayBkYXRhIHN0cnVjdHVyZXMgZmlyc3QKdHlwZW9mKHByZWRpY3RlZCkKdHlwZW9mKHRlc3RfZGF0YSRDaHVybikKCgpwciA8LSBwcmVkaWN0aW9uKHByZWQsIHRlc3RfZGF0YSRDaHVybikKcHJmIDwtIHBlcmZvcm1hbmNlKHByLCBtZWFzdXJlID0gInRwciIsIHgubWVhc3VyZSA9ICJmcHIiKQoKcGxvdChwcmYpCgojVGhlIGlkZWFsIFJPQyBjdXJ2ZSBodWdzIHRoZSB0b3AgbGVmdCBjb3JuZXIsIGluZGljYXRpbmcgYSBoaWdoCiN0cnVlIHBvc2l0aXZlIHJhdGUgYW5kIGEgbG93IGZhbHNlIHBvc2l0aXZlIHJhdGUuCiNUcnVlIHBvc2l0aXZlIHJhdGUgb24geS1heGlzCiNGYWxzZSBwb3NpdGl2ZSByYXRlIG9uIHRoZSB4LWF4aXMKCiNUaGUgbGFyZ2VyIHRoZSBBVUMsIHRoZSBiZXR0ZXIgdGhlIGNsYXNzaWZpZXIKI1RoZSBBVUMgbGluZSBpcyBpbnN1ZmZpY2llbnQgdG8gaWRlbnRpZnkgYSBiZXN0IG1vZGVsCiNJdCdzIHVzZWQgaW4gY29tYmluYXRpb24gd2l0aCBxdWFsaXRhdGl2ZSBleGFtaW5hdGlvbgojb2YgdGhlIFJPQyBjdXJ2ZQphdWMgPC0gcGVyZm9ybWFuY2UocHIsIG1lYXN1cmUgPSAiYXVjIikKYXVjCgphcy5udW1lcmljKHBlcmZvcm1hbmNlKHByLCBtZWFzdXJlID0gImF1YyIpQHkudmFsdWVzKQpgYGAKCgpDb21wYXJpbmcgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbHMgMSBhbmQgMgotTW9kZWwgMSBoYXMgYmV0dGVyIGFjY3VyYWN5IHRoYW4gTW9kZWwgMiAoanVzdCBzbGlnaHRseSBiZXR0ZXIpLCBidXQgd2UgZG8gY2FyZSBhYm91dCBzcGVjaWZpY2l0eSBhbmQgc2Vuc2l0aXZpdHkuCgpTZW5zaXRpdml0eSBpcyB0aGUgcHJvcG9ydGlvbiBvZiBvdXIgdmFyaWFibGUgb2YgaW50ZXJlc3QoY2h1cm4pIGNvcnJlY3RseSBpZGVudGlmaWVkLgoKU3BlY2lmaWNpdHkgaXMgdGhlIHByb3BvcnRpb24gb2Ygb3VyIHZhcmlhYmxlIG9mIGludGVyZXN0KGNodXJuKQoKUmVhZDogaHR0cHM6Ly93d3cudGhlYW5hbHlzaXNmYWN0b3IuY29tL3NlbnNpdGl2aXR5LWFuZC1zcGVjaWZpY2l0eS8KCgojIyBQYXJ0IDVCOiBNb2RlbGluZyAtIFJhbmRvbSBGb3Jlc3QKCgoKCmBgYHtyIFRyYWluIGEgUmFuZG9tIEZvcmVzdCBtb2RlbCBSRiBNb2RlbCAxfQojIFRyYWluIGEgUmFuZG9tIEZvcmVzdCB1c2luZyByYW5kb21Gb3Jlc3QgcGFja2FnZQpzZXQuc2VlZCgxKSAgIyBmb3IgcmVwcm9kdWNpYmlsaXR5Cm1vZGVsXzAxX3JmIDwtIHJhbmRvbUZvcmVzdChmb3JtdWxhID0gQ2h1cm4gfiAuLCBkYXRhID0gdHJhaW5fZGF0YSwgaW1wb3J0YW5jZSA9IFRSVUUsIG5hLmFjdGlvbj1uYS5leGNsdWRlKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIAojIFByaW50IHRoZSBtb2RlbCBvdXRwdXQgICAgICAgICAgICAgICAgCnByaW50KG1vZGVsXzAxX3JmKQpgYGAKTW9kZWwgNTAwIHRyZWVzOgotTm8uIG9mIHZhcmlhYmxlcyB0cmllZCBhdCBlYWNoIHNwbGl0OiA1Ci1PT0IgZXN0aW1hdGUgb2YgIGVycm9yIHJhdGU6IDIwJQoKYGBge3IgVmFyaWFibGUgSW1wb3J0YW5jZX0KIyBwcmludHMgdmFyaWFibGUgaW1wb3J0YW5jZQpzdW1tYXJ5KG1vZGVsXzAxX3JmKQoKCnZhckltcFBsb3QobW9kZWxfMDFfcmYsIG1haW49IlZhcmlhYmxlIEltcG9ydGFuY2UiKQpgYGAKCmBgYHtyIFJGIE1vZGVsIDIgLSBXaXRob3V0IFRlbnVyZX0KIyBUcmFpbiBhIFJhbmRvbSBGb3Jlc3QKc2V0LnNlZWQoMSkgICMgZm9yIHJlcHJvZHVjaWJpbGl0eQptb2RlbF8wMl9yZiA8LSByYW5kb21Gb3Jlc3QoZm9ybXVsYSA9IENodXJuIH4gLiAtdGVudXJlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSB0cmFpbl9kYXRhLCBpbXBvcnRhbmNlID0gVFJVRSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKIyBQcmludCB0aGUgbW9kZWwgb3V0cHV0CnByaW50KG1vZGVsXzAyX3JmKQoKdmFySW1wUGxvdChtb2RlbF8wMl9yZiwgbWFpbj0iVmFyaWFibGUgSW1wb3J0YW5jZSB3aXRob3V0IHRlbnVyZSIpCmBgYAoKVHlwZSBvZiByYW5kb20gZm9yZXN0OiBjbGFzc2lmaWNhdGlvbgogICAgICAgICAgICAgICAgICAgICBOdW1iZXIgb2YgdHJlZXM6IDUwMApOby4gb2YgdmFyaWFibGVzIHRyaWVkIGF0IGVhY2ggc3BsaXQ6IDQKCk9PQiBlc3RpbWF0ZSBvZiAgZXJyb3IgcmF0ZTogMjEuNTElCkNvbmZ1c2lvbiBtYXRyaXg6CiAgICAgMCAgIDEgY2xhc3MuZXJyb3IKMCAzNjIxIDQ5OSAgIDAuMTIxMTE2NQoxICA4MjkgNjc3ICAgMC41NTA0NjQ4CgpvdXIgb29iIGVycm9yIHJhdGUgaW5jcmVhc2VkIHRvIDIxLjUxJSBmcm9tIDIxLjMlIC0KbGV0J3MgdHVuZSB0aGUgMXN0IG1vZGVsIC0gbW9kZWxfMDFfcmYKCmBgYHtyIEV2YWx1YXRlIG91dC1vZi1iYWcgZXJyb3J9CiMgR3JhYiBPT0IgZXJyb3IgbWF0cml4ICYgdGFrZSBhIGxvb2sKZXJyIDwtIG1vZGVsXzAxX3JmJGVyci5yYXRlCmhlYWQoZXJyKQoKIyBMb29rIGF0IGZpbmFsIE9PQiBlcnJvciByYXRlIChsYXN0IHJvdyBpbiBlcnIgbWF0cml4KQpvb2JfZXJyIDwtIGVycltucm93KGVyciksICJPT0IiXQpwcmludChvb2JfZXJyKQoKIyBQbG90IHRoZSBtb2RlbCB0cmFpbmVkCnBsb3QobW9kZWxfMDFfcmYpCgojIEFkZCBhIGxlZ2VuZCBzaW5jZSBpdCBkb2Vzbid0IGhhdmUgb25lIGJ5IGRlZmF1bHQKbGVnZW5kKHggPSAicmlnaHQiLCAKICAgICAgIGxlZ2VuZCA9IGNvbG5hbWVzKGVyciksCiAgICAgICBmaWxsID0gMTpuY29sKGVycikpCmBgYAoKCgoKYGBge3IgRXZhbHVhdGUgbW9kZWwgcGVyZm9ybWFuY2Ugb24gYSB0ZXN0IHNldCB9CiMgR2VuZXJhdGUgcHJlZGljdGVkIGNsYXNzZXMgdXNpbmcgdGhlIG1vZGVsIG9iamVjdApjbGFzc19wcmVkaWN0aW9uIDwtIHByZWRpY3Qob2JqZWN0ID0gbW9kZWxfMDFfcmYsICAgIyBtb2RlbCBvYmplY3QgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gdGVzdF9kYXRhLCAgIyB0ZXN0IGRhdGFzZXQKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAiY2xhc3MiKSAjIHJldHVybiBjbGFzc2lmaWNhdGlvbiBsYWJlbHMKICAgICAgICAgICAgICAgICAgICAgICAgICAgIAojIENhbGN1bGF0ZSB0aGUgY29uZnVzaW9uIG1hdHJpeCBmb3IgdGhlIHRlc3Qgc2V0CmNtIDwtIGNhcmV0Ojpjb25mdXNpb25NYXRyaXgoZGF0YSA9IGNsYXNzX3ByZWRpY3Rpb24sI3ByZWRpY3RlZCBjbGFzc2VzCiAgICAgICAgICAgICAgICAgICAgICByZWZlcmVuY2UgPSB0ZXN0X2RhdGEkQ2h1cm4pICAjIGFjdHVhbCBjbGFzc2VzCnByaW50KGNtKQoKIyBDb21wYXJlIHRlc3Qgc2V0IGFjY3VyYWN5IHRvIE9PQiBhY2N1cmFjeQpwYXN0ZTAoIlRlc3QgQWNjdXJhY3k6ICIsIGNtJG92ZXJhbGxbMV0pCnBhc3RlMCgiT09CIEFjY3VyYWN5OiAiLCAxIC0gb29iX2VycikKYGBgCgpDb21wYXJlIHRlc3Qgc2V0IGFjY3VyYWN5IHRvIE9PQiBhY2N1cmFjeQoKWzFdICJUZXN0IEFjY3VyYWN5OiAwLjgwOTM4ODMzNTcwNDEyNSIKClsxXSAiT09CIEFjY3VyYWN5OiAwLjc5NjgzNjExODAyMzQ2MiIKCgoKYGBge3IgRXZhbHVhdGUgdGVzdCBzZXQgQVVDIC0gQXJlYSB1bmRlciB0aGUgY3VydmUgZm9yIGZpcnN0IFJGIG1vZGVsfQoKIyBHZW5lcmF0ZSBwcmVkaWN0aW9ucyBvbiB0aGUgdGVzdCBzZXQKcHJlZCA8LXByZWRpY3Qob2JqZWN0ID0gbW9kZWxfMDFfcmYsCiAgICAgICAgICAgIG5ld2RhdGEgPSB0ZXN0X2RhdGEsCiAgICAgICAgICAgIHR5cGUgPSAicHJvYiIpIAoKIyBVbmNvbW1lbnQgdG8gdGFrZSBhIGxvb2sgYXQgdGhlIHByZWQgZm9ybWF0IC0gYHByZWRgIG9iamVjdCBpcyBhIG1hdHJpeAojaGVhZChwcmVkKQogICAgICAgICAgICAgICAgCiMgQ29tcHV0ZSB0aGUgQVVDIChgYWN0dWFsYCBtdXN0IGJlIGEgYmluYXJ5IDEvMCBudW1lcmljIHZlY3RvcikKcm91bmQoYXVjKGFjdHVhbCA9IGlmZWxzZSh0ZXN0X2RhdGEkQ2h1cm4gPT0gIjEiLCAxLCAwKSwKICAgIHByZWRpY3RlZCA9IHByZWRbLCIxIl0pICwyKQpgYGAKCkFVQyBpcyBnb29kIHRvIGV4Y2VsbGVudAoKCgoKQ0FVVElPTjogUmFuZG9tIGZvcmVzdCBtb2RlbHMgYXJlIGNvbXB1dGF0aW9uYWxseSBoZWF2eSB0byBydW4gb24geW91ciBjb21wdXRlciBlc3BlY2lhbGx5IHRoZSBwYXJhbWV0ZXIgdHVuaW5nIGFzIHRoZSBtb2RlbCBuZWVkcyB0byBpdGVyYXRlIHRocm91Z2ggYWxsIHBvc3NpYmxlIHZhbHVlcyBpbiB0aGUgaHlwZXIgZ3JpZC4gVGhpcyBjb3VsZCB0YWtlIG1hbnkgaG91cnMgdG8gcnVuIGFuZCBpZiB5b3VyIGRhdGFzZXQgaXMgbGFyZ2UsIGNvdWxkIGNyYXNoIHlvdXIgY29tcHV0ZXIhCgpgYGB7ciBUdW5pbmcgUmFuZG9tIEZvcmVzdCAtIGh5cGVyIGdyaWQgLSBtdHJ5IG5vZGVzaXplIHNhbXBzaXplLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQoKIyBFc3RhYmxpc2ggYSBsaXN0IG9mIHBvc3NpYmxlIHZhbHVlcyBmb3IgbXRyeSwgbm9kZXNpemUgYW5kIHNhbXBzaXplCm10cnkgPC0gc2VxKDQsIG5jb2wodHJhaW5fZGF0YSkgKiAwLjgsIDIpCm5vZGVzaXplIDwtIHNlcSgzLCA4LCAyKQpzYW1wc2l6ZSA8LSBucm93KHRyYWluX2RhdGEpICogYygwLjcsIDAuOCkKCiMgQ3JlYXRlIGEgZGF0YSBmcmFtZSBjb250YWluaW5nIGFsbCBjb21iaW5hdGlvbnMgCmh5cGVyX2dyaWQgPC0gZXhwYW5kLmdyaWQobXRyeSA9IG10cnksIG5vZGVzaXplID0gbm9kZXNpemUsIHNhbXBzaXplID0gc2FtcHNpemUpCgojIENyZWF0ZSBhbiBlbXB0eSB2ZWN0b3IgdG8gc3RvcmUgT09CIGVycm9yIHZhbHVlcwpvb2JfZXJyIDwtIGMoKQoKIyBXcml0ZSBhIGxvb3Agb3ZlciB0aGUgcm93cyBvZiBoeXBlcl9ncmlkIHRvIHRyYWluIHRoZSBncmlkIG9mIG1vZGVscwpmb3IgKGkgaW4gMTpucm93KGh5cGVyX2dyaWQpKSB7CgojIFRyYWluIGEgUmFuZG9tIEZvcmVzdCBtb2RlbAogICAgbW9kZWwgPC0gcmFuZG9tRm9yZXN0KGZvcm11bGEgPSBDaHVybiB+IC4sIAogICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSB0cmFpbl9kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgIG10cnkgPSBoeXBlcl9ncmlkJG10cnlbaV0sIyBtb3N0IGltcG9ydGFudCB0dW5pbmcgcGFyYW0KICAgICAgICAgICAgICAgICAgICAgICAgICBub2Rlc2l6ZSA9IGh5cGVyX2dyaWQkbm9kZXNpemVbaV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcHNpemUgPSBoeXBlcl9ncmlkJHNhbXBzaXplW2ldKQogICAgICAgICAgICAgICAgICAgICAgICAgIAojIFN0b3JlIE9PQiBlcnJvciBmb3IgdGhlIG1vZGVsICAgICAgICAgICAgICAgICAgICAgIAogICAgb29iX2VycltpXSA8LSBtb2RlbCRlcnIucmF0ZVtucm93KG1vZGVsJGVyci5yYXRlKSwgIk9PQiJdCn0KCiMgSWRlbnRpZnkgb3B0aW1hbCBzZXQgb2YgaHlwZXJwYXJtZXRlcnMgYmFzZWQgb24gT09CIGVycm9yCm9wdF9pIDwtIHdoaWNoLm1pbihvb2JfZXJyKQpwcmludChoeXBlcl9ncmlkW29wdF9pLF0pCgpgYGAKT3B0aW1hbCBtb2RlbAoKTXRyeSA9IDQKbm9kZXNpemUgPSA3CnNhbXBzaXplID0gMzQ0NS40CQoKCgpgYGB7ciBSRiBNb2RlbCAzIC0gVGhlIFR1bmVkIE1vZGVsfQojIFRyYWluIGEgUmFuZG9tIEZvcmVzdApzZXQuc2VlZCgxKSAgIyBmb3IgcmVwcm9kdWNpYmlsaXR5Cm1vZGVsXzAzX3JmX2ZpbmFsIDwtIHJhbmRvbUZvcmVzdChmb3JtdWxhID0gQ2h1cm4gfiAuLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG10cnkgPSA0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9kZXNpemUgPSA3LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcHNpemUgPSAzNDQ1LjQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluX2RhdGEsIGltcG9ydGFuY2UgPSBUUlVFLCBrZWVwLmZvcmVzdD1UUlVFKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIAojIFByaW50IHRoZSBtb2RlbCBvdXRwdXQKcHJpbnQobW9kZWxfMDNfcmZfZmluYWwpCgojdmFyaWFibGUgaW1wb3J0YW5jZQppbXBvcnRhbmNlKG1vZGVsXzAzX3JmX2ZpbmFsKQoKI3ZhcmlhYmxlIGltcG9ydGFuY2UgcGxvdAp2YXJJbXBQbG90KG1vZGVsXzAzX3JmX2ZpbmFsLCBtYWluPSJWYXJpYWJsZSBpbXBvcnRhbmNlIG9uIHRoZSB0dW5lZCBtb2RlbCIpCmBgYAoKYGBge3IgUGFydGlhbCBkZXBlbmRlbmNlIHBsb3RzIGZvciB0aGUgbW9zdCBpbXBvcnRhbnQgdmFyaWFibGVzLCBldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQojIExldOKAmXMgY2hlY2sgcGFydGlhbCBkZXBlbmRlbmNlIHBsb3RzIGZvciBhIGZldyB2YXJpYWJsZXM6CgpvcCA8LSBwYXIobWZyb3c9YygyLCAyKSkKCnBhcnRpYWxQbG90KG1vZGVsXzAzX3JmX2ZpbmFsLCB0cmFpbl9kYXRhLCB0ZW51cmUsIDEpCgpwYXJ0aWFsUGxvdChtb2RlbF8wM19yZl9maW5hbCwgdHJhaW5fZGF0YSwgVG90YWxDaGFyZ2VzLCAxKQoKcGFydGlhbFBsb3QobW9kZWxfMDNfcmZfZmluYWwsIHRyYWluX2RhdGEsIENvbnRyYWN0LCAxKQoKcGFydGlhbFBsb3QobW9kZWxfMDNfcmZfZmluYWwsIHRyYWluX2RhdGEsIE1vbnRobHlDaGFyZ2VzLCAxKQoKcGFyKG9wKQpgYGAKCgoKYGBge3IgRXZhbHVhdGUgdGVzdCBzZXQgQVVDIC0gQXJlYSB1bmRlciB0aGUgY3VydmUgZm9yIGJlc3QgdHVuZWQgbW9kZWwgLSBSRjN9CiMgR2VuZXJhdGUgcHJlZGljdGlvbnMgb24gdGhlIHRlc3Qgc2V0CnByZWQgPC1wcmVkaWN0KG9iamVjdCA9IG1vZGVsXzAzX3JmX2ZpbmFsLAogICAgICAgICAgICBuZXdkYXRhID0gdGVzdF9kYXRhLAogICAgICAgICAgICB0eXBlID0gInByb2IiKSAKCnJvdW5kKGF1YyhhY3R1YWwgPSBpZmVsc2UodGVzdF9kYXRhJENodXJuID09ICIxIiwgMSwgMCksCiAgICBwcmVkaWN0ZWQgPSBwcmVkWywiMSJdKSAsMikKYGBgCgoKYGBge3IgUk9DIEN1cnZlIGZvciBSRiBNb2RlbCAtIEJlc3QgTW9kZWx9CnJlcXVpcmUocFJPQykKcmYucm9jPC1yb2ModHJhaW5fZGF0YSRDaHVybixtb2RlbF8wM19yZl9maW5hbCR2b3Rlc1ssMl0pCnBsb3QocmYucm9jKQphdWMocmYucm9jKQpgYGAKCgoKCmBgYHtyIERlY2lzaW9uIFRyZWUgTW9kZWx9CgojIFRyYWluIHRoZSBtb2RlbCAodG8gcHJlZGljdCAnQ2h1cm4nKSB1c2luZyBycGFydCBwYWNrYWdlCnRyZWVfbW9kXzAxIDwtIHJwYXJ0KGZvcm11bGEgPSBDaHVybiB+LiwgCiAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW5fZGF0YSwKICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJjbGFzcyIpCgojIExvb2sgYXQgdGhlIG1vZGVsIG91dHB1dCAgICAgICAgICAgICAgICAgIApwcmludCh0cmVlX21vZF8wMSkKCgojIERpc3BsYXkgdGhlIHJlc3VsdHMgdXNpbmcgcnBhcnQucGxvdCBwYWNrYWdlCnJwYXJ0LnBsb3QoeCA9IHRyZWVfbW9kXzAxLCB5ZXNubyA9IDIsIHR5cGUgPSAwLCBleHRyYSA9IDApCgpycGFydC5wbG90KHRyZWVfbW9kXzAxLAp5ZXNubyA9IDIsCmV4dHJhID0gMTA0LCAjIHNob3cgZml0dGVkIGNsYXNzLCBwcm9icywgcGVyY2VudGFnZXMKYm94LnBhbGV0dGUgPSAiR25CdSIsICMgY29sb3Igc2NoZW1lCmJyYW5jaC5sdHkgPSAzLCAjIDE9IHNvbGlkLCAzID0gZG90dGVkIGJyYW5jaCBsaW5lcwpzaGFkb3cuY29sID0gImdyYXkiLCAjIHNoYWRvd3MgdW5kZXIgdGhlIG5vZGUgYm94ZXMKbm4gPSBUUlVFLCAKbWFpbiA9ICJDbGFzc2lmaWNhdGlvbiB0cmVlIG9uIG91ciBjaHVybiBkYXRhIikKYGBgCgoKCmBgYHtyIENvbXBhcmUgdHJlZSBtb2RlbHMgd2l0aCBhIGRpZmZlcmVudCBzcGxpdHRpbmcgY3JpdGVyaW9ufQojIFRyYWluIGEgZ2luaS1iYXNlZCBtb2RlbAp0cmVlX21vZF8wMjwtIHJwYXJ0KGZvcm11bGEgPSBDaHVybiB+LiwgCiAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW5fZGF0YV90cmVlLAogICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJjbGFzcyIsCiAgICAgICAgICAgICAgICAgICAgICAgcGFybXMgPSBsaXN0KHNwbGl0ID0gImdpbmkiKSkKCiMgRGlzcGxheSB0aGUgcmVzdWx0cwpycGFydC5wbG90KHggPSB0cmVlX21vZF8wMiwgeWVzbm8gPSAyLCB0eXBlID0gMCwgZXh0cmEgPSAwKQoKIyBUcmFpbiBhbiBpbmZvcm1hdGlvbi1iYXNlZCBtb2RlbAp0cmVlX21vZF8wMzwtIHJwYXJ0KGZvcm11bGEgPSBDaHVybiB+LiwgCiAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW5fZGF0YV90cmVlLCAKICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiY2xhc3MiLAogICAgICAgICAgICAgICAgICAgICAgIHBhcm1zID0gbGlzdChzcGxpdCA9ICJpbmZvcm1hdGlvbiIpKQoKIyBEaXNwbGF5IHRoZSByZXN1bHRzCnJwYXJ0LnBsb3QoeCA9IHRyZWVfbW9kXzAzLCB5ZXNubyA9IDIsIHR5cGUgPSA1LCBleHRyYSA9IDYpCgojIEdlbmVyYXRlIHByZWRpY3Rpb25zIG9uIHRoZSB0ZXN0IHNldCB1c2luZyB0aGUgZ2luaSBtb2RlbApwcmVkMSA8LSBwcmVkaWN0KG9iamVjdCA9IHRyZWVfbW9kXzAyLCAKICAgICAgICAgICAgIG5ld2RhdGEgPSB0ZXN0X2RhdGFfdHJlZSwKICAgICAgICAgICAgIHR5cGUgPSAiY2xhc3MiKSAgICAKCiMgR2VuZXJhdGUgcHJlZGljdGlvbnMgb24gdGhlIHRlc3Qgc2V0IHVzaW5nIHRoZSBpbmZvcm1hdGlvbiBtb2RlbApwcmVkMiA8LSBwcmVkaWN0KG9iamVjdCA9IHRyZWVfbW9kXzAzLCAKICAgICAgICAgICAgIG5ld2RhdGEgPSB0ZXN0X2RhdGFfdHJlZSwKICAgICAgICAgICAgIHR5cGUgPSAiY2xhc3MiKQoKIyBDb21wYXJlIGNsYXNzaWZpY2F0aW9uIGVycm9yIC0gdXNpbmcgTW9kZWxNZXRyaWNzIGxpYnJhcnkKY2UoYWN0dWFsID0gdGVzdF9kYXRhJENodXJuLCAKICAgcHJlZGljdGVkID0gcHJlZDEpCmNlKGFjdHVhbCA9IHRlc3RfZGF0YSRDaHVybiwgCiAgIHByZWRpY3RlZCA9IHByZWQyKSAgCgpgYGAKCiMjIFBhcnQgNjogU3VtbWFyeSBvZiBJbnNpZ2h0cyBhbmQgaHlwb3RoZXNlcyBnZW5lcmF0ZWQgYW5kIHRlc3RpbmcgdGhlbQoKCldoYXQgaW5zaWdodHMgaGF2ZSB3ZSBkaXNjb3ZlcmVkIGFuZCBjYW4gY29uZmlkZW50bHkgcmVwb3J0IHRvIG91ciBzdGFrZWhvbGRlcnM/CgoKCgpgYGB7ciBXaGF0IGlzIHRoZSBlY29ub21pYyBpbXBhY3Qgb2Ygc3VjaCBhIGhpZ2ggY2h1cm4gcmF0ZSBmb3IgZmliZXIgb3B0aWMgcHJvZHVjdD99CgpkZiAlPiUgCiAgZmlsdGVyKENodXJuID09ICJZZXMiICYgSW50ZXJuZXRTZXJ2aWNlID09ICJGaWJlciBvcHRpYyIpICU+JSAKICBzdW1tYXJpemUobG9zdF9yZXZlbnVlID0gc3VtKFRvdGFsQ2hhcmdlcykpCgpgYGAKCgoKCgpOb3csIHlvdSBoYXZlIGEgZ29vZCBzdGFydGluZyBwb2ludCB0byBwcmVzZW50IHlvdXIgZmluZGluZ3MgJiBpbnNpZ2h0cyB0byBzdGFrZWhvbGRlcnMsIGJ1dCB0aGlzIGlzIG9ubHkgdGhlIGJlZ2lubmluZy4gQ3VycmVudGx5LCB5b3Ugb25seSBoYXZlIGEgc3Ryb25nIGh5cG90aGVzZXMgc28gbm93IHlvdSBuZWVkIHRvIHZhbGlkYXRlIGFuZCB0ZXN0IHdoYXQgeW91IGhhdmUgdW5jb3ZlcmVkLiBZb3UgbWF5IG5lZWQgdG8gZG8gbW9yZSByZXNlYXJjaCBvciBkYXRhIHRvIGRvIHRoaXMgYXMgeW91IG1heSBuZWVkIHRvIHVuY292ZXIgdGhlIHJvb3QgY2F1c2VzLg==