Telco_data Project Two

By Vincent Mwenda

setting a working directory

 setwd("~/R training")

importing libraries

library(rio)
library(tidyverse)
library(readxl)
library(dplyr)
library(psych)

importing data

df=read.csv("Telcom-data.csv")
head(df)

checking for missing values

library(tidyverse)
missing_value <- colSums(is.na(df))
data.frame(missing_value)

doublechecking for missing variables

colSums(is.na(df))
      customerID           gender    SeniorCitizen          Partner       Dependents           tenure     PhoneService 
               0                0                0                0                0                0                0 
   MultipleLines  InternetService   OnlineSecurity     OnlineBackup DeviceProtection      TechSupport      StreamingTV 
               0                0                0                0                0                0                0 
 StreamingMovies         Contract PaperlessBilling    PaymentMethod   MonthlyCharges     TotalCharges            Churn 
               0                0                0                0                0               11                0 

##handling missing variables

df$TotalCharges[is.na(df$TotalCharges)] <- median(df$TotalCharges, na.rm = TRUE)
head(df)
colSums(is.na(df))
      customerID           gender    SeniorCitizen          Partner       Dependents           tenure     PhoneService 
               0                0                0                0                0                0                0 
   MultipleLines  InternetService   OnlineSecurity     OnlineBackup DeviceProtection      TechSupport      StreamingTV 
               0                0                0                0                0                0                0 
 StreamingMovies         Contract PaperlessBilling    PaymentMethod   MonthlyCharges     TotalCharges            Churn 
               0                0                0                0                0                0                0 

converting categorical variables to numeric using encoding

df[]<-lapply(df,function(x){
  if (is.character(x)) 
as.numeric(factor(x)) else x
})
head(df)

checking for outliers

find_outliers_iqr <- function(x) {
  Q1 <- quantile(x, 0.25, na.rm = TRUE)
  Q3 <- quantile(x, 0.75, na.rm = TRUE)
  IQR_value <- Q3 - Q1
  lower_bound <- Q1 - 1.5 * IQR_value
  upper_bound <- Q3 + 1.5 * IQR_value
  return(which(x < lower_bound | x > upper_bound))
}
numeric_columns <- sapply(df, is.numeric)
outlier_indices_list <- lapply(df[, numeric_columns], find_outliers_iqr)

# Print summary of outliers
for (col in names(outlier_indices_list)) {
  cat("Variable:", col, " - Outliers found:", length(outlier_indices_list[[col]]), "\n")
}
Variable: customerID  - Outliers found: 0 
Variable: gender  - Outliers found: 0 
Variable: SeniorCitizen  - Outliers found: 1142 
Variable: Partner  - Outliers found: 0 
Variable: Dependents  - Outliers found: 0 
Variable: tenure  - Outliers found: 0 
Variable: PhoneService  - Outliers found: 682 
Variable: MultipleLines  - Outliers found: 0 
Variable: InternetService  - Outliers found: 0 
Variable: OnlineSecurity  - Outliers found: 0 
Variable: OnlineBackup  - Outliers found: 0 
Variable: DeviceProtection  - Outliers found: 0 
Variable: TechSupport  - Outliers found: 0 
Variable: StreamingTV  - Outliers found: 0 
Variable: StreamingMovies  - Outliers found: 0 
Variable: Contract  - Outliers found: 0 
Variable: PaperlessBilling  - Outliers found: 0 
Variable: PaymentMethod  - Outliers found: 0 
Variable: MonthlyCharges  - Outliers found: 0 
Variable: TotalCharges  - Outliers found: 0 
Variable: Churn  - Outliers found: 0 

clean the outliers

Q1 <- quantile(df$SeniorCitizen, 0.25)
Q3 <- quantile(df$SeniorCitizen, 0.75)
IQR <- Q3 - Q1
lower_bound <- Q1 - 1.5 * IQR
upper_bound <- Q1 +1.5 * IQR
df_clean <- df[df$SeniorCitizen >= lower_bound & df$SeniorCitizen <= upper_bound, ]
df_clean

checking if data has been cleaned

Q1 <- quantile(df_clean$SeniorCitizen, 0.25, na.rm = TRUE)
Q3 <- quantile(df_clean$SeniorCitizen, 0.75, na.rm = TRUE)
IQR <- Q3 - Q1
lower <- Q1 - 1.5 * IQR
upper <- Q3 + 1.5 * IQR

sum(df_clean$SeniorCitizen < lower | df_clean$SeniorCitizen > upper, na.rm = TRUE)
[1] 0

Deliverable:

• Cleaned dataset stored as a new CSV file.

write.csv(df_clean,"df_clean.csv",row.names = FALSE)

CHECKING IF Cleaned dataset stored as a new CSV file.

getwd()
[1] "C:/Users/PC/Documents/R training"

summary statistics

summary(df)
   customerID       gender      SeniorCitizen       Partner        Dependents      tenure       PhoneService  
 Min.   :   1   Min.   :1.000   Min.   :0.0000   Min.   :1.000   Min.   :1.0   Min.   : 0.00   Min.   :1.000  
 1st Qu.:1762   1st Qu.:1.000   1st Qu.:0.0000   1st Qu.:1.000   1st Qu.:1.0   1st Qu.: 9.00   1st Qu.:2.000  
 Median :3522   Median :2.000   Median :0.0000   Median :1.000   Median :1.0   Median :29.00   Median :2.000  
 Mean   :3522   Mean   :1.505   Mean   :0.1621   Mean   :1.483   Mean   :1.3   Mean   :32.37   Mean   :1.903  
 3rd Qu.:5282   3rd Qu.:2.000   3rd Qu.:0.0000   3rd Qu.:2.000   3rd Qu.:2.0   3rd Qu.:55.00   3rd Qu.:2.000  
 Max.   :7043   Max.   :2.000   Max.   :1.0000   Max.   :2.000   Max.   :2.0   Max.   :72.00   Max.   :2.000  
 MultipleLines   InternetService OnlineSecurity  OnlineBackup   DeviceProtection  TechSupport     StreamingTV   
 Min.   :1.000   Min.   :1.000   Min.   :1.00   Min.   :1.000   Min.   :1.000    Min.   :1.000   Min.   :1.000  
 1st Qu.:1.000   1st Qu.:1.000   1st Qu.:1.00   1st Qu.:1.000   1st Qu.:1.000    1st Qu.:1.000   1st Qu.:1.000  
 Median :2.000   Median :2.000   Median :2.00   Median :2.000   Median :2.000    Median :2.000   Median :2.000  
 Mean   :1.941   Mean   :1.873   Mean   :1.79   Mean   :1.906   Mean   :1.904    Mean   :1.797   Mean   :1.985  
 3rd Qu.:3.000   3rd Qu.:2.000   3rd Qu.:3.00   3rd Qu.:3.000   3rd Qu.:3.000    3rd Qu.:3.000   3rd Qu.:3.000  
 Max.   :3.000   Max.   :3.000   Max.   :3.00   Max.   :3.000   Max.   :3.000    Max.   :3.000   Max.   :3.000  
 StreamingMovies    Contract    PaperlessBilling PaymentMethod   MonthlyCharges    TotalCharges        Churn      
 Min.   :1.000   Min.   :1.00   Min.   :1.000    Min.   :1.000   Min.   : 18.25   Min.   :  18.8   Min.   :1.000  
 1st Qu.:1.000   1st Qu.:1.00   1st Qu.:1.000    1st Qu.:2.000   1st Qu.: 35.50   1st Qu.: 402.2   1st Qu.:1.000  
 Median :2.000   Median :1.00   Median :2.000    Median :3.000   Median : 70.35   Median :1397.5   Median :1.000  
 Mean   :1.992   Mean   :1.69   Mean   :1.592    Mean   :2.574   Mean   : 64.76   Mean   :2281.9   Mean   :1.265  
 3rd Qu.:3.000   3rd Qu.:2.00   3rd Qu.:2.000    3rd Qu.:3.000   3rd Qu.: 89.85   3rd Qu.:3786.6   3rd Qu.:2.000  
 Max.   :3.000   Max.   :3.00   Max.   :2.000    Max.   :4.000   Max.   :118.75   Max.   :8684.8   Max.   :2.000  

Explanation

3. Exploratory Data Analysis (EDA)

• Visualize customer distribution across different Contract types.

• Compare MonthlyCharges and TotalCharges for different customer groups.

• Check correlations between numerical variables.

• Use boxplots and histograms to understand spending behavior.

1.correlation matrix

numeric_df<-df[sapply(df,is.numeric)]
cor_matrix<-cor(numeric_df,use="complete.obs")
print(cor_matrix)
                    customerID        gender SeniorCitizen       Partner   Dependents       tenure PhoneService
customerID        1.000000e+00  6.287560e-03  -0.002074491 -0.0267293673 -0.012822980  0.008035245 -0.006483114
gender            6.287560e-03  1.000000e+00  -0.001873712 -0.0018080797  0.010516612  0.005106223 -0.006487672
SeniorCitizen    -2.074491e-03 -1.873712e-03   1.000000000  0.0164786576 -0.211185088  0.016566878  0.008576401
Partner          -2.672937e-02 -1.808080e-03   0.016478658  1.0000000000  0.452676283  0.379697461  0.017705663
Dependents       -1.282298e-02  1.051661e-02  -0.211185088  0.4526762829  1.000000000  0.159712331 -0.001761679
tenure            8.035245e-03  5.106223e-03   0.016566878  0.3796974612  0.159712331  1.000000000  0.008448207
PhoneService     -6.483114e-03 -6.487672e-03   0.008576401  0.0177056632 -0.001761679  0.008448207  1.000000000
MultipleLines     4.315642e-03 -6.738743e-03   0.146184870  0.1424104914 -0.024990642  0.343031854 -0.020537831
InternetService  -1.240661e-02 -8.633825e-04  -0.032310150  0.0008913469  0.044590441 -0.030359034  0.387436022
OnlineSecurity    1.329223e-02 -1.501689e-02  -0.128221307  0.1508282814  0.152166383  0.325467840 -0.015198344
OnlineBackup     -3.333703e-03 -1.205675e-02  -0.013632137  0.1531301336  0.091014768  0.370876123  0.024104791
DeviceProtection -6.918004e-03  5.490359e-04  -0.021397649  0.1663303505  0.080537163  0.371105436  0.003727316
TechSupport       1.139834e-03 -6.824559e-03  -0.151267697  0.1267333219  0.133523531  0.322941583 -0.019157686
StreamingTV      -7.776878e-03 -6.421366e-03   0.030776349  0.1373414370  0.046884886  0.289373218  0.055353254
StreamingMovies  -1.674614e-02 -8.743154e-03   0.047266359  0.1295738935  0.021321016  0.296866273  0.043869619
Contract          1.502778e-02  1.257477e-04  -0.142554080  0.2948062244  0.243187473  0.671606549  0.002247374
PaperlessBilling -1.945042e-03 -1.175369e-02   0.156529559 -0.0148766223 -0.111377229  0.006152482  0.016504806
PaymentMethod     1.160439e-02  1.735226e-02  -0.038550813 -0.1547975288 -0.040291899 -0.370436118 -0.004184054
MonthlyCharges   -3.916417e-03 -1.456932e-02   0.220173339  0.0968479363 -0.113890230  0.247899856  0.247397963
TotalCharges     -1.903837e-05 -1.921911e-06   0.102651590  0.3183638954  0.063593305  0.825464086  0.113012809
Churn            -1.744721e-02 -8.612095e-03   0.150889328 -0.1504475450 -0.164221402 -0.352228670  0.011941980
                 MultipleLines InternetService OnlineSecurity OnlineBackup DeviceProtection  TechSupport  StreamingTV
customerID         0.004315642   -0.0124066064    0.013292235 -0.003333703    -0.0069180043  0.001139834 -0.007776878
gender            -0.006738743   -0.0008633825   -0.015016887 -0.012056754     0.0005490359 -0.006824559 -0.006421366
SeniorCitizen      0.146184870   -0.0323101502   -0.128221307 -0.013632137    -0.0213976486 -0.151267697  0.030776349
Partner            0.142410491    0.0008913469    0.150828281  0.153130134     0.1663303505  0.126733322  0.137341437
Dependents        -0.024990642    0.0445904407    0.152166383  0.091014768     0.0805371627  0.133523531  0.046884886
tenure             0.343031854   -0.0303590343    0.325467840  0.370876123     0.3711054358  0.322941583  0.289373218
PhoneService      -0.020537831    0.3874360220   -0.015198344  0.024104791     0.0037273158 -0.019157686  0.055353254
MultipleLines      1.000000000   -0.1092157984    0.007140508  0.117327302     0.1223180267  0.011466091  0.175059025
InternetService   -0.109215798    1.0000000000   -0.028416181  0.036137937     0.0449442685 -0.026046946  0.107416772
OnlineSecurity     0.007140508   -0.0284161810    1.000000000  0.185125586     0.1759853288  0.285028465  0.044669272
OnlineBackup       0.117327302    0.0361379374    0.185125586  1.000000000     0.1877568644  0.195748148  0.147185665
DeviceProtection   0.122318027    0.0449442685    0.175985329  0.187756864     1.0000000000  0.240593121  0.276651547
TechSupport        0.011466091   -0.0260469460    0.285028465  0.195748148     0.2405931212  1.000000000  0.161305024
StreamingTV        0.175059025    0.1074167724    0.044669272  0.147185665     0.2766515471  0.161305024  1.000000000
StreamingMovies    0.180957159    0.0983496062    0.055953610  0.136721965     0.2887993891  0.161315802  0.434772353
Contract           0.110842245    0.0997207826    0.374415538  0.280980144     0.3502770893  0.425366672  0.227115572
PaperlessBilling   0.165145894   -0.1386247786   -0.157641192 -0.013370380    -0.0382340313 -0.113599641  0.096641567
PaymentMethod     -0.176793410    0.0861398581   -0.096725947 -0.124847381    -0.1357499660 -0.104669714 -0.104233699
MonthlyCharges     0.433576010   -0.3232601923   -0.053878094  0.119777245     0.1636518655 -0.008681602  0.336706239
TotalCharges       0.452849367   -0.1755884904    0.253935041  0.375062556     0.3885615563  0.276342769  0.392046136
Churn              0.038036886   -0.0472913877   -0.289308996 -0.195525014    -0.1781338611 -0.282491608 -0.036580993
                 StreamingMovies      Contract PaperlessBilling PaymentMethod MonthlyCharges  TotalCharges
customerID          -0.016746145  0.0150277787     -0.001945042   0.011604392   -0.003916417 -1.903837e-05
gender              -0.008743154  0.0001257477     -0.011753691   0.017352264   -0.014569324 -1.921911e-06
SeniorCitizen        0.047266359 -0.1425540803      0.156529559  -0.038550813    0.220173339  1.026516e-01
Partner              0.129573893  0.2948062244     -0.014876622  -0.154797529    0.096847936  3.183639e-01
Dependents           0.021321016  0.2431874726     -0.111377229  -0.040291899   -0.113890230  6.359330e-02
tenure               0.296866273  0.6716065492      0.006152482  -0.370436118    0.247899856  8.254641e-01
PhoneService         0.043869619  0.0022473744      0.016504806  -0.004184054    0.247397963  1.130128e-01
MultipleLines        0.180957159  0.1108422447      0.165145894  -0.176793410    0.433576010  4.528494e-01
InternetService      0.098349606  0.0997207826     -0.138624779   0.086139858   -0.323260192 -1.755885e-01
OnlineSecurity       0.055953610  0.3744155384     -0.157641192  -0.096725947   -0.053878094  2.539350e-01
OnlineBackup         0.136721965  0.2809801444     -0.013370380  -0.124847381    0.119777245  3.750626e-01
DeviceProtection     0.288799389  0.3502770893     -0.038234031  -0.135749966    0.163651866  3.885616e-01
TechSupport          0.161315802  0.4253666716     -0.113599641  -0.104669714   -0.008681602  2.763428e-01
StreamingTV          0.434772353  0.2271155725      0.096641567  -0.104233699    0.336706239  3.920461e-01
StreamingMovies      1.000000000  0.2312255160      0.083700449  -0.111240576    0.335459482  3.980449e-01
Contract             0.231225516  1.0000000000     -0.176733078  -0.227543497   -0.074194667  4.485643e-01
PaperlessBilling     0.083700449 -0.1767330778      1.000000000  -0.062903951    0.352149968  1.580546e-01
PaymentMethod       -0.111240576 -0.2275434968     -0.062903951   1.000000000   -0.193407294 -3.305106e-01
MonthlyCharges       0.335459482 -0.0741946672      0.352149968  -0.193407294    1.000000000  6.508643e-01
TotalCharges         0.398044852  0.4485642564      0.158054596  -0.330510561    0.650864350  1.000000e+00
Churn               -0.038491629 -0.3967126292      0.191825332   0.107062006    0.193356422 -1.990368e-01
                        Churn
customerID       -0.017447211
gender           -0.008612095
SeniorCitizen     0.150889328
Partner          -0.150447545
Dependents       -0.164221402
tenure           -0.352228670
PhoneService      0.011941980
MultipleLines     0.038036886
InternetService  -0.047291388
OnlineSecurity   -0.289308996
OnlineBackup     -0.195525014
DeviceProtection -0.178133861
TechSupport      -0.282491608
StreamingTV      -0.036580993
StreamingMovies  -0.038491629
Contract         -0.396712629
PaperlessBilling  0.191825332
PaymentMethod     0.107062006
MonthlyCharges    0.193356422
TotalCharges     -0.199036832
Churn             1.000000000

visualizing correlation

library(corrplot)
corrplot(cor_matrix,method="color")

• Visualize customer distribution across different Contract types.

inspecting contract column

table(df$Contract)

   1    2    3 
3875 1473 1695 

##tabled df

table(df$Contract)

   1    2    3 
3875 1473 1695 

Bar chart of customer distribution across different Contract types

barplot(
  table(df$Contract),
  main="customer distribution by contract type",
  xlab="Contract type",
  ylab="Number of Customers",
    ,col="blue")

• Compare MonthlyCharges and TotalCharges for different customer groups.

1.contract type

library(dplyr)
library(ggplot2)
### assigning column names
colnames(df)<-c("customerId","gender","SeniorCitizen","Partner","Dependents","tenure",
                 "PhoneService","MultipleLines","InternetService","OnlineSecurity","OnlineBackup",
                 "DeviceProtection","TechSupport","StreamingTv","StreamingMovies",
                 "Contract","PaperlessBilling","PaymentMethod","MonthlyCharges",
                 "TotalCharges","Churn")
## ensuring Monthly Charges And Total Charges are numeric
df$MonthlyCharges<-as.numeric(as.character(df$MonthlyCharges))
df$TotalCharges<-as.numeric(gsub("[^0-9.]","",as.character(df$TotalCharges)))
## Group by Contract Type
contract_summary <- df %>%
  group_by(Contract) %>%
  summarise(
    AvgMonthly=mean(MonthlyCharges,na.rm=TRUE),
    AvgTotal=mean(TotalCharges,na.rm=TRUE),
  )
print(contract_summary)

conclusion longer contracts have least monthly charges but higher average totaldue to higher lifetime value

Group by Churn

churn_summary <- df %>%
  group_by(Churn) %>%
  summarise(
    AvgMonthly=mean(MonthlyCharges,na.rm=TRUE),
    AvgTotal=mean(TotalCharges,na.rm=TRUE),
    count=n()
  )
print(churn_summary)

churned customers pay more monthly charges but less total charges(mostlikely they stay for a shorter time)

Group by Internet Service

internet_summary <- df %>%
  group_by(InternetService) %>%
  summarise(
    AvgMonthly=mean(MonthlyCharges,na.rm=TRUE),
    AvgTotal=mean(TotalCharges,na.rm=TRUE),
    count=n()
  )
print(internet_summary)

conclusion Fiber optic pay the most monthly and also the most in total charges ## group by PaymentMethod

payment_summary <- df %>%
  group_by(PaymentMethod) %>%
  summarise(
    AvgMonthly=mean(MonthlyCharges,na.rm=TRUE),
    AvgTotal=mean(TotalCharges,na.rm=TRUE),
    count=n()
  )
print(payment_summary)

group by Gender

gender_summary <- df %>%
  group_by(gender) %>%
  summarise(
    AvgMonthly=mean(MonthlyCharges,na.rm=TRUE),
    AvgTotal=mean(TotalCharges,na.rm=TRUE),
    count=n()
  )
print(gender_summary)

conclusion no significant difference but males tend to pay slightly more

##Visualizing monthly charges and total charges across different Customer Groups

library(tidyr)
library(dplyr)
library(ggplot2)
### assigning column names
colnames(df)<-c("customerId","gender","SeniorCitizen","Partner","Dependents","tenure",
                 "PhoneService","MultipleLines","InternetService","OnlineSecurity","OnlineBackup",
                 "DeviceProtection","TechSupport","StreamingTv","StreamingMovies",
                 "Contract","PaperlessBilling","PaymentMethod","MonthlyCharges",
                 "TotalCharges","Churn")
## ensuring Monthly Charges And Total Charges are numeric
df$MonthlyCharges<-as.numeric(as.character(df$MonthlyCharges))
df$TotalCharges<-as.numeric(gsub("[^0-9.]","",as.character(df$TotalCharges)))
## Grouped Summary
contract_summary <- df %>%
  group_by(Contract) %>%
  summarise(
    AvgMonthly=mean(MonthlyCharges,na.rm=TRUE),
    AvgTotal=mean(TotalCharges,na.rm=TRUE),
  )
## reshaping data for plotting

contract_plot_data<-contract_summary%>%pivot_longer(cols=c(AvgMonthly,AvgTotal),
                                                      names_to="ChargeType",
                                                      values_to="Average")
## plotting Comparison of Monthly Charges and Total Charges by contract
ggplot(contract_plot_data,aes(x=Contract,y=Average,fill=ChargeType))+
geom_bar(stat="identity",position = "dodge")+
  labs(title = "Average Monthly Charges vs Total Charges by Contract Type",
       x="Contract Type",
       y="Average Charge($)",
       fill="ChargeType")+
  theme_minimal()

Conclusion month to month customers pay more per month but stay shorter two year contracts pay the least but have higher total revenue

by churn status

library(tidyr)
library(dplyr)
library(ggplot2)
## Grouped Summary
churn_summary<-df%>%group_by(Churn)%>%summarise(
  AvgMonthly=mean(MonthlyCharges,na.rm=TRUE),
  AvgTotal=mean(TotalCharges,na.rm=TRUE),
)
## reshaping data for plotting

churn_summary%>%pivot_longer(cols=c(AvgMonthly,AvgTotal),
                                                      names_to="ChargeType",
                                                      values_to="Average")%>%
ggplot(aes(x=Churn,y=Average,fill=ChargeType))+
geom_bar(stat="identity",position = "dodge")+
  labs(title = "Average Monthly Charges vs Total Charges by Churn status",
       x="Churn",
       y="Average Charge($)",
       fill="ChargeType")+
  theme_minimal()

Conclusion A significant number of c

by internet service

library(tidyr)
library(dplyr)
library(ggplot2)
## Grouped Summary
internet_summary<-df%>%group_by(InternetService)%>%summarise(
  AvgMonthly=mean(MonthlyCharges,na.rm=TRUE),
  AvgTotal=mean(TotalCharges,na.rm=TRUE),
)
## reshaping data for plotting

internet_summary%>%pivot_longer(cols=c(AvgMonthly,AvgTotal),
                                                      names_to="ChargeType",
                                                      values_to="Average")%>%
ggplot(aes(x=InternetService,y=Average,fill=ChargeType))+
geom_bar(stat="identity",position = "dodge")+
  labs(title = "Average Monthly Charges vs Total Charges by InternetService",
       x="InternetService",
       y="Average Charge($)",
       fill="ChargeType")+
  theme_minimal()

##Scatterplot of monthly vs total charges

ggplot(df,aes(x=MonthlyCharges,y=TotalCharges,color=Churn))+
  geom_point(alpha=0.6)+
  labs(title = "Monthly Charges vs Total Charges(coloredby Churn)",
       x="MonthlyCharge($))",y="TotalCharges($)")+
  theme_minimal()

• Check correlations between numerical variables.

# converting numeric columns to correct type
df$tenure<-as.numeric(as.character(df$tenure))
df$MonthlyCharges<-as.numeric(as.character(df$MonthlyCharges))
df$TotalCharges<-as.numeric(as.character(df$TotalCharges))
## create a data frame with only numerical variables
numeric_df<-df%>%
  select(tenure,MonthlyCharges,TotalCharges)
#compute correlation matrix
cor_matrix<-cor(numeric_df,use="complete.obs")
print(cor_matrix)
                  tenure MonthlyCharges TotalCharges
tenure         1.0000000      0.2478999    0.8254641
MonthlyCharges 0.2478999      1.0000000    0.6508643
TotalCharges   0.8254641      0.6508643    1.0000000

Explanation Tenure vs total charges have a strong positive correlation(cor=0.8258805),meaning the longer the customer stays,the higher their charges. Tenure vs monthly charges have a week positive relationship(cor=0.2468618),meaning customers who stay longer maybe slightly on a cheaper plan. **Monthly charges vs Total Charges have an average positive correlation of 0.65,meaning the more the monthly charges,the higher the total charges

##pearson Rank
library(corrplot)
df<-df[,c("tenure","MonthlyCharges","TotalCharges")]
pearson_matrix<-cor(df,method="pearson")
pearson_matrix
                  tenure MonthlyCharges TotalCharges
tenure         1.0000000      0.2478999    0.8254641
MonthlyCharges 0.2478999      1.0000000    0.6508643
TotalCharges   0.8254641      0.6508643    1.0000000

##spearman rank correlation

library(corrplot)
df<-df[,c("tenure","MonthlyCharges","TotalCharges")]
spearman_matrix<-cor(df,method="spearman")
spearman_matrix
                  tenure MonthlyCharges TotalCharges
tenure         1.0000000      0.2764168    0.8869979
MonthlyCharges 0.2764168      1.0000000    0.6374752
TotalCharges   0.8869979      0.6374752    1.0000000

• Use boxplots and histograms to understand spending behavior

1.Boxplot of monthly charges

library(ggplot2)
ggplot(df,aes(x="",y=MonthlyCharges))+
  geom_boxplot(fill="blue",width=0.5)+
  labs(title="Boxplot of Monthly Charges",
       y="MonthlyCharges")+
  theme_minimal()

explanation **most customers pay around 70$ monthly charges,but some have higher monthly charges,maybe they are using premium services

Boxplot of TotalCharges

library(ggplot2)
ggplot(df,aes(x="",y=TotalCharges))+
  geom_boxplot(fill="green",width=0.5)+
  labs(title="Boxplot of Total Charges",
       y="TotalCharges")+
  theme_minimal()

Explanation ** Wide spread indicating varying tenure and monthly spending,likely long term customers or those with high monthly charges

Boxplot of Monthly Charges by Churn

library(ggplot2)
df<-read.csv("Telcom-data.csv")
### assigning column names
colnames(df)<-c("customerId","gender","SeniorCitizen","Partner","Dependents","tenure",
                 "PhoneService","MultipleLines","InternetService","OnlineSecurity","OnlineBackup",
                 "DeviceProtection","TechSupport","StreamingTv","StreamingMovies",
                 "Contract","PaperlessBilling","PaymentMethod","MonthlyCharges",
                 "TotalCharges","Churn")
## ensuring Monthly Charges And Total Charges are numeric
df$MonthlyCharges<-as.numeric(as.character(df$MonthlyCharges))
## plotting Boxplot of Montly charges
ggplot(df,aes(x=Churn,y=MonthlyCharges,fill=Churn))+
  geom_boxplot()+
  labs(title="Boxplot of monthly Charges by churn",
       y="MonthlyCharges")+
  theme_minimal()

#Total charges by churn
ggplot(df,aes(x=Churn,y=TotalCharges,fill=Churn))+
  geom_boxplot()+
  labs(title="Boxplot of Total Charges by churn",
       y="TotalCharges")+
  theme_minimal()

Explanation churned customers paid more monthly charges than those not churned churned customers have much lower total spending,indicating short tenures.

Histogram of Monthly Charges

library(ggplot2)
ggplot(df,aes(x=MonthlyCharges))+
  geom_histogram(bins=30,fill="red",color="black")+
  labs(title="Histogram of Monthly Charges Distribution",
       x="MonthlyCharges",
       y="frequency")+
  theme_minimal()

#Explanation Monthly charges cloud at certain price points,suggesting tiered pricing or bundled packages,

Histogram of Total Charges

library(ggplot2)
ggplot(df,aes(x=TotalCharges))+
  geom_histogram(bins=30,fill="red",color="black")+
  labs(title="Histogram of Total Charges Distribution",
       x="TotalCharges",
       y="frequency")+
  theme_minimal()

#Explanation most customers have low to moderate total charges,likely due to short tenure,few customers indicate high total spending,indicating long-term loyalty.

HYPOTHESIS TESTING

Hypothesis 1: Do customers with long-term contracts have higher total spending? • Null Hypothesis (H₀): No difference in TotalCharges among contract types. • Alternative Hypothesis (H₁): Customers with longer contracts have significantly higher TotalCharges. #Descriptive statistics by Contract Type

summary_by_contract<-df%>%
  group_by(Contract)%>%
  summarise(
    count=n(),
    meanTotalCharge=mean(TotalCharges,na.rm=TRUE),
    sdTotalCharge=sd(TotalCharges,na.rm=TRUE)
  )
print(summary_by_contract)
NA

Visualizing Total Charges by Contract Type

library(ggplot2)
ggplot(df,aes(x=TotalCharges))+
  geom_boxplot(fill="red")+
  labs(title="Total Charges by Contract",
       x="Contract Type",
       y="Total Charges")+
  theme_minimal()

Performing Anova

# fitting a linear model
anova_model<-aov(TotalCharges ~ Contract,data=df)
summary(anova_model)
              Df    Sum Sq   Mean Sq F value Pr(>F)    
Contract       2 7.590e+09 3.795e+09   934.7 <2e-16 ***
Residuals   7029 2.854e+10 4.060e+06                   
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
11 observations deleted due to missingness

#Conclusion p-value<0.05,reject null hypothesis, and conclude that there is statistically significant difference in Total Charges between Contract Types. #Recommendation customers with longer contracts spend significantly more compared to those with shorter contracts,hence encourage the customers to move to longer contracts

Post Hoc test:Tuskys HSD

TukeyTest<-TukeyHSD(anova_model)
plot(TukeyTest,las=1)

print(TukeyTest)
  Tukey multiple comparisons of means
    95% family-wise confidence level

Fit: aov(formula = TotalCharges ~ Contract, data = df)

$Contract
                             diff       lwr       upr p adj
One year-Month-to-month 1665.4285 1520.8120 1810.0450     0
Two year-Month-to-month 2359.6794 2221.8461 2497.5126     0
Two year-One year        694.2509  525.7369  862.7648     0

Hypothesis 2: Is there a significant difference in spending between male and female customers?

• Null Hypothesis (H₀): No difference in MonthlyCharges between male and female customers. • Alternative Hypothesis (H₁): Males and females have different spending behaviors. • Test to use: Independent t-test.

t_test_result <- t.test(MonthlyCharges~gender, data=df)

# 7. Print the result
print(t_test_result)

    Welch Two Sample t-test

data:  MonthlyCharges by gender
t = 1.2227, df = 7038.9, p-value = 0.2215
alternative hypothesis: true difference in means between group Female and group Male is not equal to 0
95 percent confidence interval:
 -0.5289426  2.2824640
sample estimates:
mean in group Female   mean in group Male 
            65.20424             64.32748 

conclusion

p_value(0.2215)>0.05,hence do not reject the null hypothesis, and conclude that there is no difference between male and female spending.

#5. Regression Analysis Building a Linear Regression Model to Predict MonthlyCharges • Define MonthlyCharges as the dependent variable. • Use Tenure, Contract, and Payment Method as independent variables.

model<-lm(MonthlyCharges~tenure+Contract+PaymentMethod,data=df)
summary(model)

Call:
lm(formula = MonthlyCharges ~ tenure + Contract + PaymentMethod, 
    data = df)

Residuals:
    Min      1Q  Median      3Q     Max 
-72.154 -20.211   3.787  19.016  76.913 

Coefficients:
                                       Estimate Std. Error t value Pr(>|t|)    
(Intercept)                           54.445739   0.894149  60.891   <2e-16 ***
tenure                                 0.551490   0.018054  30.547   <2e-16 ***
ContractOne year                     -11.885419   0.912707 -13.022   <2e-16 ***
ContractTwo year                     -22.775408   1.047304 -21.747   <2e-16 ***
PaymentMethodCredit card (automatic)   0.006174   0.935446   0.007    0.995    
PaymentMethodElectronic check         11.288247   0.896915  12.586   <2e-16 ***
PaymentMethodMailed check            -14.685846   0.973991 -15.078   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 25.89 on 7036 degrees of freedom
Multiple R-squared:  0.2605,    Adjusted R-squared:  0.2599 
F-statistic: 413.1 on 6 and 7036 DF,  p-value: < 2.2e-16

model diagnostics

library(car) 
G2;H2;Warningh: package ‘car’ was built under R version 4.4.3g
G3;Loading required package: carData
gG2;H2;Warningh: package ‘carData’ was built under R version 4.4.3g
G3;
Attaching package: ‘car’

gG3;The following object is masked from ‘package:psych’:

    logit

gG3;The following object is masked from ‘package:dplyr’:

    recode

gG3;The following object is masked from ‘package:purrr’:

    some

g
vif_values=vif(model)
print(vif_values)
                  GVIF Df GVIF^(1/(2*Df))
tenure        2.065973  1        1.437349
Contract      2.023452  2        1.192678
PaymentMethod 1.308307  3        1.045807

conclusion

no multicollinearity

model performance metrics

# Load performance package
library(performance)

# Get model performance metrics
model_performance(model)
# R²: Measures model goodness-of-fit.

outliers and Influential Points Detection

# Cook’s Distance Plot
plot(cooks.distance(model), 
     type = "h", 
     main = "Cook’s Distance", 
     ylab = "Cook’s Distance")

##conclusion no need for further investigations

#Residual diagnostics

# Histogram of residuals
ggplot(data = data.frame(residuals = resid(model)), aes(x = residuals)) +
  geom_histogram(color = "black", fill = "blue", bins = 10) +
  labs(title = "Histogram of Residuals", x = "Residuals", y = "Count")

conclusion

Bell shaped curve,Residuals are normal

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIFRlbGNvX2RhdGEgUHJvamVjdCBUd28NCg0KIyBCeSBWaW5jZW50IE13ZW5kYQ0KDQojIyBzZXR0aW5nIGEgd29ya2luZyBkaXJlY3RvcnkNCmBgYHtyfQ0KIHNldHdkKCJ+L1IgdHJhaW5pbmciKQ0KYGBgDQoNCiMjIGltcG9ydGluZyBsaWJyYXJpZXMNCmBgYHtyfQ0KbGlicmFyeShyaW8pDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkocHN5Y2gpDQpgYGANCg0KIyMgaW1wb3J0aW5nIGRhdGENCmBgYHtyfQ0KZGY9cmVhZC5jc3YoIlRlbGNvbS1kYXRhLmNzdiIpDQpoZWFkKGRmKQ0KYGBgDQoNCiMjIyBjaGVja2luZyBmb3IgbWlzc2luZyB2YWx1ZXMNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQptaXNzaW5nX3ZhbHVlIDwtIGNvbFN1bXMoaXMubmEoZGYpKQ0KZGF0YS5mcmFtZShtaXNzaW5nX3ZhbHVlKQ0KYGBgDQoNCiMjIGRvdWJsZWNoZWNraW5nIGZvciBtaXNzaW5nIHZhcmlhYmxlcw0KYGBge3J9DQpjb2xTdW1zKGlzLm5hKGRmKSkNCmBgYA0KDQojI2hhbmRsaW5nIG1pc3NpbmcgdmFyaWFibGVzDQpgYGB7cn0NCmRmJFRvdGFsQ2hhcmdlc1tpcy5uYShkZiRUb3RhbENoYXJnZXMpXSA8LSBtZWRpYW4oZGYkVG90YWxDaGFyZ2VzLCBuYS5ybSA9IFRSVUUpDQpoZWFkKGRmKQ0KYGBgDQoNCmBgYHtyfQ0KY29sU3Vtcyhpcy5uYShkZikpDQpgYGANCg0KIyMgY29udmVydGluZyBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgdG8gbnVtZXJpYyB1c2luZyBlbmNvZGluZw0KYGBge3J9DQpkZltdPC1sYXBwbHkoZGYsZnVuY3Rpb24oeCl7DQogIGlmIChpcy5jaGFyYWN0ZXIoeCkpIA0KYXMubnVtZXJpYyhmYWN0b3IoeCkpIGVsc2UgeA0KfSkNCmhlYWQoZGYpDQpgYGANCg0KIyMgY2hlY2tpbmcgZm9yIG91dGxpZXJzDQpgYGB7cn0NCmZpbmRfb3V0bGllcnNfaXFyIDwtIGZ1bmN0aW9uKHgpIHsNCiAgUTEgPC0gcXVhbnRpbGUoeCwgMC4yNSwgbmEucm0gPSBUUlVFKQ0KICBRMyA8LSBxdWFudGlsZSh4LCAwLjc1LCBuYS5ybSA9IFRSVUUpDQogIElRUl92YWx1ZSA8LSBRMyAtIFExDQogIGxvd2VyX2JvdW5kIDwtIFExIC0gMS41ICogSVFSX3ZhbHVlDQogIHVwcGVyX2JvdW5kIDwtIFEzICsgMS41ICogSVFSX3ZhbHVlDQogIHJldHVybih3aGljaCh4IDwgbG93ZXJfYm91bmQgfCB4ID4gdXBwZXJfYm91bmQpKQ0KfQ0KbnVtZXJpY19jb2x1bW5zIDwtIHNhcHBseShkZiwgaXMubnVtZXJpYykNCm91dGxpZXJfaW5kaWNlc19saXN0IDwtIGxhcHBseShkZlssIG51bWVyaWNfY29sdW1uc10sIGZpbmRfb3V0bGllcnNfaXFyKQ0KDQojIFByaW50IHN1bW1hcnkgb2Ygb3V0bGllcnMNCmZvciAoY29sIGluIG5hbWVzKG91dGxpZXJfaW5kaWNlc19saXN0KSkgew0KICBjYXQoIlZhcmlhYmxlOiIsIGNvbCwgIiAtIE91dGxpZXJzIGZvdW5kOiIsIGxlbmd0aChvdXRsaWVyX2luZGljZXNfbGlzdFtbY29sXV0pLCAiXG4iKQ0KfQ0KDQpgYGANCg0KIyMgY2xlYW4gdGhlIG91dGxpZXJzDQpgYGB7cn0NClExIDwtIHF1YW50aWxlKGRmJFNlbmlvckNpdGl6ZW4sIDAuMjUpDQpRMyA8LSBxdWFudGlsZShkZiRTZW5pb3JDaXRpemVuLCAwLjc1KQ0KSVFSIDwtIFEzIC0gUTENCmxvd2VyX2JvdW5kIDwtIFExIC0gMS41ICogSVFSDQp1cHBlcl9ib3VuZCA8LSBRMSArMS41ICogSVFSDQpkZl9jbGVhbiA8LSBkZltkZiRTZW5pb3JDaXRpemVuID49IGxvd2VyX2JvdW5kICYgZGYkU2VuaW9yQ2l0aXplbiA8PSB1cHBlcl9ib3VuZCwgXQ0KZGZfY2xlYW4NCmBgYA0KDQojIyBjaGVja2luZyBpZiBkYXRhIGhhcyBiZWVuIGNsZWFuZWQNCmBgYHtyfQ0KUTEgPC0gcXVhbnRpbGUoZGZfY2xlYW4kU2VuaW9yQ2l0aXplbiwgMC4yNSwgbmEucm0gPSBUUlVFKQ0KUTMgPC0gcXVhbnRpbGUoZGZfY2xlYW4kU2VuaW9yQ2l0aXplbiwgMC43NSwgbmEucm0gPSBUUlVFKQ0KSVFSIDwtIFEzIC0gUTENCmxvd2VyIDwtIFExIC0gMS41ICogSVFSDQp1cHBlciA8LSBRMyArIDEuNSAqIElRUg0KDQpzdW0oZGZfY2xlYW4kU2VuaW9yQ2l0aXplbiA8IGxvd2VyIHwgZGZfY2xlYW4kU2VuaW9yQ2l0aXplbiA+IHVwcGVyLCBuYS5ybSA9IFRSVUUpDQpgYGANCg0KIyMgRGVsaXZlcmFibGU6DQrigKIJQ2xlYW5lZCBkYXRhc2V0IHN0b3JlZCBhcyBhIG5ldyBDU1YgZmlsZS4NCmBgYHtyfQ0Kd3JpdGUuY3N2KGRmX2NsZWFuLCJkZl9jbGVhbi5jc3YiLHJvdy5uYW1lcyA9IEZBTFNFKQ0KYGBgDQoNCiMjIENIRUNLSU5HIElGIENsZWFuZWQgZGF0YXNldCBzdG9yZWQgYXMgYSBuZXcgQ1NWIGZpbGUuDQpgYGB7cn0NCmdldHdkKCkNCmBgYA0KDQojIyBzdW1tYXJ5IHN0YXRpc3RpY3MNCmBgYHtyfQ0Kc3VtbWFyeShkZikNCmBgYA0KRXhwbGFuYXRpb24NCg0KDQojIyAzLiBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIChFREEpDQojIyDigKIJVmlzdWFsaXplIGN1c3RvbWVyIGRpc3RyaWJ1dGlvbiBhY3Jvc3MgZGlmZmVyZW50IENvbnRyYWN0IHR5cGVzLg0KIyMg4oCiCUNvbXBhcmUgTW9udGhseUNoYXJnZXMgYW5kIFRvdGFsQ2hhcmdlcyBmb3IgZGlmZmVyZW50IGN1c3RvbWVyIGdyb3Vwcy4NCiMjIOKAoglDaGVjayBjb3JyZWxhdGlvbnMgYmV0d2VlbiBudW1lcmljYWwgdmFyaWFibGVzLg0KIyMg4oCiCVVzZSBib3hwbG90cyBhbmQgaGlzdG9ncmFtcyB0byB1bmRlcnN0YW5kIHNwZW5kaW5nIGJlaGF2aW9yLg0KDQoxLmNvcnJlbGF0aW9uIG1hdHJpeA0KYGBge3J9DQpudW1lcmljX2RmPC1kZltzYXBwbHkoZGYsaXMubnVtZXJpYyldDQpjb3JfbWF0cml4PC1jb3IobnVtZXJpY19kZix1c2U9ImNvbXBsZXRlLm9icyIpDQpwcmludChjb3JfbWF0cml4KQ0KYGBgDQoNCiMjIyB2aXN1YWxpemluZyBjb3JyZWxhdGlvbg0KYGBge3J9DQpsaWJyYXJ5KGNvcnJwbG90KQ0KY29ycnBsb3QoY29yX21hdHJpeCxtZXRob2Q9ImNvbG9yIikNCmBgYA0KDQojIyDigKIJVmlzdWFsaXplIGN1c3RvbWVyIGRpc3RyaWJ1dGlvbiBhY3Jvc3MgZGlmZmVyZW50IENvbnRyYWN0IHR5cGVzLg0KDQppbnNwZWN0aW5nIGNvbnRyYWN0IGNvbHVtbg0KYGBge3J9DQp0YWJsZShkZiRDb250cmFjdCkNCmBgYA0KDQoNCg0KDQojI3RhYmxlZCBkZg0KYGBge3J9DQp0YWJsZShkZiRDb250cmFjdCkNCmBgYA0KDQojIyBCYXIgY2hhcnQgb2YgY3VzdG9tZXIgZGlzdHJpYnV0aW9uIGFjcm9zcyBkaWZmZXJlbnQgQ29udHJhY3QgdHlwZXMNCg0KYGBge3J9DQpiYXJwbG90KA0KICB0YWJsZShkZiRDb250cmFjdCksDQogIG1haW49ImN1c3RvbWVyIGRpc3RyaWJ1dGlvbiBieSBjb250cmFjdCB0eXBlIiwNCiAgeGxhYj0iQ29udHJhY3QgdHlwZSIsDQogIHlsYWI9Ik51bWJlciBvZiBDdXN0b21lcnMiLA0KICAgICxjb2w9ImJsdWUiKQ0KYGBgDQoNCiMjIOKAoglDb21wYXJlIE1vbnRobHlDaGFyZ2VzIGFuZCBUb3RhbENoYXJnZXMgZm9yIGRpZmZlcmVudCBjdXN0b21lciBncm91cHMuDQojIyAxLmNvbnRyYWN0IHR5cGUNCmBgYHtyfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dwbG90MikNCiMjIyBhc3NpZ25pbmcgY29sdW1uIG5hbWVzDQpjb2xuYW1lcyhkZik8LWMoImN1c3RvbWVySWQiLCJnZW5kZXIiLCJTZW5pb3JDaXRpemVuIiwiUGFydG5lciIsIkRlcGVuZGVudHMiLCJ0ZW51cmUiLA0KICAgICAgICAgICAgICAgICAiUGhvbmVTZXJ2aWNlIiwiTXVsdGlwbGVMaW5lcyIsIkludGVybmV0U2VydmljZSIsIk9ubGluZVNlY3VyaXR5IiwiT25saW5lQmFja3VwIiwNCiAgICAgICAgICAgICAgICAgIkRldmljZVByb3RlY3Rpb24iLCJUZWNoU3VwcG9ydCIsIlN0cmVhbWluZ1R2IiwiU3RyZWFtaW5nTW92aWVzIiwNCiAgICAgICAgICAgICAgICAgIkNvbnRyYWN0IiwiUGFwZXJsZXNzQmlsbGluZyIsIlBheW1lbnRNZXRob2QiLCJNb250aGx5Q2hhcmdlcyIsDQogICAgICAgICAgICAgICAgICJUb3RhbENoYXJnZXMiLCJDaHVybiIpDQojIyBlbnN1cmluZyBNb250aGx5IENoYXJnZXMgQW5kIFRvdGFsIENoYXJnZXMgYXJlIG51bWVyaWMNCmRmJE1vbnRobHlDaGFyZ2VzPC1hcy5udW1lcmljKGFzLmNoYXJhY3RlcihkZiRNb250aGx5Q2hhcmdlcykpDQpkZiRUb3RhbENoYXJnZXM8LWFzLm51bWVyaWMoZ3N1YigiW14wLTkuXSIsIiIsYXMuY2hhcmFjdGVyKGRmJFRvdGFsQ2hhcmdlcykpKQ0KIyMgR3JvdXAgYnkgQ29udHJhY3QgVHlwZQ0KY29udHJhY3Rfc3VtbWFyeSA8LSBkZiAlPiUNCiAgZ3JvdXBfYnkoQ29udHJhY3QpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgQXZnTW9udGhseT1tZWFuKE1vbnRobHlDaGFyZ2VzLG5hLnJtPVRSVUUpLA0KICAgIEF2Z1RvdGFsPW1lYW4oVG90YWxDaGFyZ2VzLG5hLnJtPVRSVUUpLA0KICApDQpwcmludChjb250cmFjdF9zdW1tYXJ5KQ0KYGBgDQpjb25jbHVzaW9uDQpsb25nZXIgY29udHJhY3RzIGhhdmUgbGVhc3QgbW9udGhseSBjaGFyZ2VzIGJ1dCBoaWdoZXIgYXZlcmFnZSB0b3RhbGR1ZSB0byBoaWdoZXIgbGlmZXRpbWUgdmFsdWUNCg0KIyMgR3JvdXAgYnkgQ2h1cm4NCmBgYHtyfQ0KY2h1cm5fc3VtbWFyeSA8LSBkZiAlPiUNCiAgZ3JvdXBfYnkoQ2h1cm4pICU+JQ0KICBzdW1tYXJpc2UoDQogICAgQXZnTW9udGhseT1tZWFuKE1vbnRobHlDaGFyZ2VzLG5hLnJtPVRSVUUpLA0KICAgIEF2Z1RvdGFsPW1lYW4oVG90YWxDaGFyZ2VzLG5hLnJtPVRSVUUpLA0KICAgIGNvdW50PW4oKQ0KICApDQpwcmludChjaHVybl9zdW1tYXJ5KQ0KYGBgDQoNCmNodXJuZWQgY3VzdG9tZXJzIHBheSBtb3JlIG1vbnRobHkgY2hhcmdlcyBidXQgbGVzcyB0b3RhbCBjaGFyZ2VzKG1vc3RsaWtlbHkgdGhleSBzdGF5IGZvciBhIHNob3J0ZXIgdGltZSkNCg0KIyMgR3JvdXAgYnkgSW50ZXJuZXQgU2VydmljZQ0KYGBge3J9DQppbnRlcm5ldF9zdW1tYXJ5IDwtIGRmICU+JQ0KICBncm91cF9ieShJbnRlcm5ldFNlcnZpY2UpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgQXZnTW9udGhseT1tZWFuKE1vbnRobHlDaGFyZ2VzLG5hLnJtPVRSVUUpLA0KICAgIEF2Z1RvdGFsPW1lYW4oVG90YWxDaGFyZ2VzLG5hLnJtPVRSVUUpLA0KICAgIGNvdW50PW4oKQ0KICApDQpwcmludChpbnRlcm5ldF9zdW1tYXJ5KQ0KYGBgDQpjb25jbHVzaW9uDQpGaWJlciBvcHRpYyBwYXkgdGhlIG1vc3QgbW9udGhseSBhbmQgYWxzbyB0aGUgbW9zdCBpbiB0b3RhbCBjaGFyZ2VzDQojIyBncm91cCBieSBQYXltZW50TWV0aG9kDQpgYGB7cn0NCnBheW1lbnRfc3VtbWFyeSA8LSBkZiAlPiUNCiAgZ3JvdXBfYnkoUGF5bWVudE1ldGhvZCkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBBdmdNb250aGx5PW1lYW4oTW9udGhseUNoYXJnZXMsbmEucm09VFJVRSksDQogICAgQXZnVG90YWw9bWVhbihUb3RhbENoYXJnZXMsbmEucm09VFJVRSksDQogICAgY291bnQ9bigpDQogICkNCnByaW50KHBheW1lbnRfc3VtbWFyeSkNCmBgYA0KDQojIyBncm91cCBieSBHZW5kZXINCmBgYHtyfQ0KZ2VuZGVyX3N1bW1hcnkgPC0gZGYgJT4lDQogIGdyb3VwX2J5KGdlbmRlcikgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBBdmdNb250aGx5PW1lYW4oTW9udGhseUNoYXJnZXMsbmEucm09VFJVRSksDQogICAgQXZnVG90YWw9bWVhbihUb3RhbENoYXJnZXMsbmEucm09VFJVRSksDQogICAgY291bnQ9bigpDQogICkNCnByaW50KGdlbmRlcl9zdW1tYXJ5KQ0KYGBgDQoNCmNvbmNsdXNpb24NCm5vIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgYnV0IG1hbGVzIHRlbmQgdG8gcGF5IHNsaWdodGx5IG1vcmUNCg0KIyNWaXN1YWxpemluZyBtb250aGx5IGNoYXJnZXMgYW5kIHRvdGFsIGNoYXJnZXMgYWNyb3NzIGRpZmZlcmVudCBDdXN0b21lciBHcm91cHMNCmBgYHtyfQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGdncGxvdDIpDQojIyMgYXNzaWduaW5nIGNvbHVtbiBuYW1lcw0KY29sbmFtZXMoZGYpPC1jKCJjdXN0b21lcklkIiwiZ2VuZGVyIiwiU2VuaW9yQ2l0aXplbiIsIlBhcnRuZXIiLCJEZXBlbmRlbnRzIiwidGVudXJlIiwNCiAgICAgICAgICAgICAgICAgIlBob25lU2VydmljZSIsIk11bHRpcGxlTGluZXMiLCJJbnRlcm5ldFNlcnZpY2UiLCJPbmxpbmVTZWN1cml0eSIsIk9ubGluZUJhY2t1cCIsDQogICAgICAgICAgICAgICAgICJEZXZpY2VQcm90ZWN0aW9uIiwiVGVjaFN1cHBvcnQiLCJTdHJlYW1pbmdUdiIsIlN0cmVhbWluZ01vdmllcyIsDQogICAgICAgICAgICAgICAgICJDb250cmFjdCIsIlBhcGVybGVzc0JpbGxpbmciLCJQYXltZW50TWV0aG9kIiwiTW9udGhseUNoYXJnZXMiLA0KICAgICAgICAgICAgICAgICAiVG90YWxDaGFyZ2VzIiwiQ2h1cm4iKQ0KIyMgZW5zdXJpbmcgTW9udGhseSBDaGFyZ2VzIEFuZCBUb3RhbCBDaGFyZ2VzIGFyZSBudW1lcmljDQpkZiRNb250aGx5Q2hhcmdlczwtYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZGYkTW9udGhseUNoYXJnZXMpKQ0KZGYkVG90YWxDaGFyZ2VzPC1hcy5udW1lcmljKGdzdWIoIlteMC05Ll0iLCIiLGFzLmNoYXJhY3RlcihkZiRUb3RhbENoYXJnZXMpKSkNCiMjIEdyb3VwZWQgU3VtbWFyeQ0KY29udHJhY3Rfc3VtbWFyeSA8LSBkZiAlPiUNCiAgZ3JvdXBfYnkoQ29udHJhY3QpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgQXZnTW9udGhseT1tZWFuKE1vbnRobHlDaGFyZ2VzLG5hLnJtPVRSVUUpLA0KICAgIEF2Z1RvdGFsPW1lYW4oVG90YWxDaGFyZ2VzLG5hLnJtPVRSVUUpLA0KICApDQojIyByZXNoYXBpbmcgZGF0YSBmb3IgcGxvdHRpbmcNCg0KY29udHJhY3RfcGxvdF9kYXRhPC1jb250cmFjdF9zdW1tYXJ5JT4lcGl2b3RfbG9uZ2VyKGNvbHM9YyhBdmdNb250aGx5LEF2Z1RvdGFsKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVzX3RvPSJDaGFyZ2VUeXBlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlc190bz0iQXZlcmFnZSIpDQojIyBwbG90dGluZyBDb21wYXJpc29uIG9mIE1vbnRobHkgQ2hhcmdlcyBhbmQgVG90YWwgQ2hhcmdlcyBieSBjb250cmFjdA0KZ2dwbG90KGNvbnRyYWN0X3Bsb3RfZGF0YSxhZXMoeD1Db250cmFjdCx5PUF2ZXJhZ2UsZmlsbD1DaGFyZ2VUeXBlKSkrDQpnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIscG9zaXRpb24gPSAiZG9kZ2UiKSsNCiAgbGFicyh0aXRsZSA9ICJBdmVyYWdlIE1vbnRobHkgQ2hhcmdlcyB2cyBUb3RhbCBDaGFyZ2VzIGJ5IENvbnRyYWN0IFR5cGUiLA0KICAgICAgIHg9IkNvbnRyYWN0IFR5cGUiLA0KICAgICAgIHk9IkF2ZXJhZ2UgQ2hhcmdlKCQpIiwNCiAgICAgICBmaWxsPSJDaGFyZ2VUeXBlIikrDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQpDb25jbHVzaW9uDQptb250aCB0byBtb250aCBjdXN0b21lcnMgcGF5IG1vcmUgcGVyIG1vbnRoIGJ1dCBzdGF5IHNob3J0ZXINCnR3byB5ZWFyIGNvbnRyYWN0cyBwYXkgdGhlIGxlYXN0IGJ1dCBoYXZlIGhpZ2hlciB0b3RhbCByZXZlbnVlDQoNCg0KIyMgYnkgY2h1cm4gc3RhdHVzDQpgYGB7cn0NCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KIyMgR3JvdXBlZCBTdW1tYXJ5DQpjaHVybl9zdW1tYXJ5PC1kZiU+JWdyb3VwX2J5KENodXJuKSU+JXN1bW1hcmlzZSgNCiAgQXZnTW9udGhseT1tZWFuKE1vbnRobHlDaGFyZ2VzLG5hLnJtPVRSVUUpLA0KICBBdmdUb3RhbD1tZWFuKFRvdGFsQ2hhcmdlcyxuYS5ybT1UUlVFKSwNCikNCiMjIHJlc2hhcGluZyBkYXRhIGZvciBwbG90dGluZw0KDQpjaHVybl9zdW1tYXJ5JT4lcGl2b3RfbG9uZ2VyKGNvbHM9YyhBdmdNb250aGx5LEF2Z1RvdGFsKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVzX3RvPSJDaGFyZ2VUeXBlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlc190bz0iQXZlcmFnZSIpJT4lDQpnZ3Bsb3QoYWVzKHg9Q2h1cm4seT1BdmVyYWdlLGZpbGw9Q2hhcmdlVHlwZSkpKw0KZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLHBvc2l0aW9uID0gImRvZGdlIikrDQogIGxhYnModGl0bGUgPSAiQXZlcmFnZSBNb250aGx5IENoYXJnZXMgdnMgVG90YWwgQ2hhcmdlcyBieSBDaHVybiBzdGF0dXMiLA0KICAgICAgIHg9IkNodXJuIiwNCiAgICAgICB5PSJBdmVyYWdlIENoYXJnZSgkKSIsDQogICAgICAgZmlsbD0iQ2hhcmdlVHlwZSIpKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KQ29uY2x1c2lvbg0KQSBzaWduaWZpY2FudCBudW1iZXIgb2YgYyANCg0KDQojIyBieSBpbnRlcm5ldCBzZXJ2aWNlDQpgYGB7cn0NCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KIyMgR3JvdXBlZCBTdW1tYXJ5DQppbnRlcm5ldF9zdW1tYXJ5PC1kZiU+JWdyb3VwX2J5KEludGVybmV0U2VydmljZSklPiVzdW1tYXJpc2UoDQogIEF2Z01vbnRobHk9bWVhbihNb250aGx5Q2hhcmdlcyxuYS5ybT1UUlVFKSwNCiAgQXZnVG90YWw9bWVhbihUb3RhbENoYXJnZXMsbmEucm09VFJVRSksDQopDQojIyByZXNoYXBpbmcgZGF0YSBmb3IgcGxvdHRpbmcNCg0KaW50ZXJuZXRfc3VtbWFyeSU+JXBpdm90X2xvbmdlcihjb2xzPWMoQXZnTW9udGhseSxBdmdUb3RhbCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lc190bz0iQ2hhcmdlVHlwZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXNfdG89IkF2ZXJhZ2UiKSU+JQ0KZ2dwbG90KGFlcyh4PUludGVybmV0U2VydmljZSx5PUF2ZXJhZ2UsZmlsbD1DaGFyZ2VUeXBlKSkrDQpnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIscG9zaXRpb24gPSAiZG9kZ2UiKSsNCiAgbGFicyh0aXRsZSA9ICJBdmVyYWdlIE1vbnRobHkgQ2hhcmdlcyB2cyBUb3RhbCBDaGFyZ2VzIGJ5IEludGVybmV0U2VydmljZSIsDQogICAgICAgeD0iSW50ZXJuZXRTZXJ2aWNlIiwNCiAgICAgICB5PSJBdmVyYWdlIENoYXJnZSgkKSIsDQogICAgICAgZmlsbD0iQ2hhcmdlVHlwZSIpKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQojI1NjYXR0ZXJwbG90IG9mIG1vbnRobHkgdnMgdG90YWwgY2hhcmdlcw0KYGBge3J9DQpnZ3Bsb3QoZGYsYWVzKHg9TW9udGhseUNoYXJnZXMseT1Ub3RhbENoYXJnZXMsY29sb3I9Q2h1cm4pKSsNCiAgZ2VvbV9wb2ludChhbHBoYT0wLjYpKw0KICBsYWJzKHRpdGxlID0gIk1vbnRobHkgQ2hhcmdlcyB2cyBUb3RhbCBDaGFyZ2VzKGNvbG9yZWRieSBDaHVybikiLA0KICAgICAgIHg9Ik1vbnRobHlDaGFyZ2UoJCkpIix5PSJUb3RhbENoYXJnZXMoJCkiKSsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KDQojIyDigKIJQ2hlY2sgY29ycmVsYXRpb25zIGJldHdlZW4gbnVtZXJpY2FsIHZhcmlhYmxlcy4NCmBgYHtyfQ0KIyBjb252ZXJ0aW5nIG51bWVyaWMgY29sdW1ucyB0byBjb3JyZWN0IHR5cGUNCmRmJHRlbnVyZTwtYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZGYkdGVudXJlKSkNCmRmJE1vbnRobHlDaGFyZ2VzPC1hcy5udW1lcmljKGFzLmNoYXJhY3RlcihkZiRNb250aGx5Q2hhcmdlcykpDQpkZiRUb3RhbENoYXJnZXM8LWFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGRmJFRvdGFsQ2hhcmdlcykpDQojIyBjcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggb25seSBudW1lcmljYWwgdmFyaWFibGVzDQpudW1lcmljX2RmPC1kZiU+JQ0KICBzZWxlY3QodGVudXJlLE1vbnRobHlDaGFyZ2VzLFRvdGFsQ2hhcmdlcykNCiNjb21wdXRlIGNvcnJlbGF0aW9uIG1hdHJpeA0KY29yX21hdHJpeDwtY29yKG51bWVyaWNfZGYsdXNlPSJjb21wbGV0ZS5vYnMiKQ0KcHJpbnQoY29yX21hdHJpeCkNCmBgYA0KRXhwbGFuYXRpb24NCioqVGVudXJlIHZzIHRvdGFsIGNoYXJnZXMgaGF2ZSBhIHN0cm9uZyBwb3NpdGl2ZSBjb3JyZWxhdGlvbihjb3I9MC44MjU4ODA1KSxtZWFuaW5nIHRoZSBsb25nZXIgdGhlIGN1c3RvbWVyIHN0YXlzLHRoZSBoaWdoZXIgdGhlaXIgY2hhcmdlcy4NCioqVGVudXJlIHZzIG1vbnRobHkgY2hhcmdlcyBoYXZlIGEgd2VlayBwb3NpdGl2ZSByZWxhdGlvbnNoaXAoY29yPTAuMjQ2ODYxOCksbWVhbmluZyBjdXN0b21lcnMgd2hvIHN0YXkgbG9uZ2VyIG1heWJlIHNsaWdodGx5IG9uIGEgY2hlYXBlciBwbGFuLg0KKipNb250aGx5IGNoYXJnZXMgdnMgVG90YWwgQ2hhcmdlcyBoYXZlIGFuIGF2ZXJhZ2UgcG9zaXRpdmUgY29ycmVsYXRpb24gb2YgMC42NSxtZWFuaW5nIHRoZSBtb3JlIHRoZSBtb250aGx5IGNoYXJnZXMsdGhlIGhpZ2hlciB0aGUgdG90YWwgY2hhcmdlcw0KDQpgYGB7cn0NCiMjcGVhcnNvbiBSYW5rDQpsaWJyYXJ5KGNvcnJwbG90KQ0KZGY8LWRmWyxjKCJ0ZW51cmUiLCJNb250aGx5Q2hhcmdlcyIsIlRvdGFsQ2hhcmdlcyIpXQ0KcGVhcnNvbl9tYXRyaXg8LWNvcihkZixtZXRob2Q9InBlYXJzb24iKQ0KcGVhcnNvbl9tYXRyaXgNCmBgYA0KDQoNCiMjc3BlYXJtYW4gcmFuayBjb3JyZWxhdGlvbg0KYGBge3J9DQpsaWJyYXJ5KGNvcnJwbG90KQ0KZGY8LWRmWyxjKCJ0ZW51cmUiLCJNb250aGx5Q2hhcmdlcyIsIlRvdGFsQ2hhcmdlcyIpXQ0Kc3BlYXJtYW5fbWF0cml4PC1jb3IoZGYsbWV0aG9kPSJzcGVhcm1hbiIpDQpzcGVhcm1hbl9tYXRyaXgNCmBgYA0KDQoNCiMjIOKAoglVc2UgYm94cGxvdHMgYW5kIGhpc3RvZ3JhbXMgdG8gdW5kZXJzdGFuZCBzcGVuZGluZyBiZWhhdmlvcg0KIyAxLkJveHBsb3Qgb2YgbW9udGhseSBjaGFyZ2VzDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmdncGxvdChkZixhZXMoeD0iIix5PU1vbnRobHlDaGFyZ2VzKSkrDQogIGdlb21fYm94cGxvdChmaWxsPSJibHVlIix3aWR0aD0wLjUpKw0KICBsYWJzKHRpdGxlPSJCb3hwbG90IG9mIE1vbnRobHkgQ2hhcmdlcyIsDQogICAgICAgeT0iTW9udGhseUNoYXJnZXMiKSsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KZXhwbGFuYXRpb24NCioqbW9zdCBjdXN0b21lcnMgcGF5IGFyb3VuZCA3MCQgbW9udGhseSBjaGFyZ2VzLGJ1dCBzb21lIGhhdmUgIGhpZ2hlciBtb250aGx5IGNoYXJnZXMsbWF5YmUgdGhleSBhcmUgdXNpbmcgcHJlbWl1bSBzZXJ2aWNlcw0KDQojIEJveHBsb3Qgb2YgVG90YWxDaGFyZ2VzDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmdncGxvdChkZixhZXMoeD0iIix5PVRvdGFsQ2hhcmdlcykpKw0KICBnZW9tX2JveHBsb3QoZmlsbD0iZ3JlZW4iLHdpZHRoPTAuNSkrDQogIGxhYnModGl0bGU9IkJveHBsb3Qgb2YgVG90YWwgQ2hhcmdlcyIsDQogICAgICAgeT0iVG90YWxDaGFyZ2VzIikrDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQpFeHBsYW5hdGlvbg0KKiogV2lkZSBzcHJlYWQgaW5kaWNhdGluZyB2YXJ5aW5nIHRlbnVyZSBhbmQgbW9udGhseSBzcGVuZGluZyxsaWtlbHkgbG9uZyB0ZXJtIGN1c3RvbWVycyBvciB0aG9zZSB3aXRoIGhpZ2ggbW9udGhseSBjaGFyZ2VzDQoNCiMjIEJveHBsb3Qgb2YgTW9udGhseSBDaGFyZ2VzIGJ5IENodXJuDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmRmPC1yZWFkLmNzdigiVGVsY29tLWRhdGEuY3N2IikNCiMjIyBhc3NpZ25pbmcgY29sdW1uIG5hbWVzDQpjb2xuYW1lcyhkZik8LWMoImN1c3RvbWVySWQiLCJnZW5kZXIiLCJTZW5pb3JDaXRpemVuIiwiUGFydG5lciIsIkRlcGVuZGVudHMiLCJ0ZW51cmUiLA0KICAgICAgICAgICAgICAgICAiUGhvbmVTZXJ2aWNlIiwiTXVsdGlwbGVMaW5lcyIsIkludGVybmV0U2VydmljZSIsIk9ubGluZVNlY3VyaXR5IiwiT25saW5lQmFja3VwIiwNCiAgICAgICAgICAgICAgICAgIkRldmljZVByb3RlY3Rpb24iLCJUZWNoU3VwcG9ydCIsIlN0cmVhbWluZ1R2IiwiU3RyZWFtaW5nTW92aWVzIiwNCiAgICAgICAgICAgICAgICAgIkNvbnRyYWN0IiwiUGFwZXJsZXNzQmlsbGluZyIsIlBheW1lbnRNZXRob2QiLCJNb250aGx5Q2hhcmdlcyIsDQogICAgICAgICAgICAgICAgICJUb3RhbENoYXJnZXMiLCJDaHVybiIpDQojIyBlbnN1cmluZyBNb250aGx5IENoYXJnZXMgQW5kIFRvdGFsIENoYXJnZXMgYXJlIG51bWVyaWMNCmRmJE1vbnRobHlDaGFyZ2VzPC1hcy5udW1lcmljKGFzLmNoYXJhY3RlcihkZiRNb250aGx5Q2hhcmdlcykpDQojIyBwbG90dGluZyBCb3hwbG90IG9mIE1vbnRseSBjaGFyZ2VzDQpnZ3Bsb3QoZGYsYWVzKHg9Q2h1cm4seT1Nb250aGx5Q2hhcmdlcyxmaWxsPUNodXJuKSkrDQogIGdlb21fYm94cGxvdCgpKw0KICBsYWJzKHRpdGxlPSJCb3hwbG90IG9mIG1vbnRobHkgQ2hhcmdlcyBieSBjaHVybiIsDQogICAgICAgeT0iTW9udGhseUNoYXJnZXMiKSsNCiAgdGhlbWVfbWluaW1hbCgpDQojVG90YWwgY2hhcmdlcyBieSBjaHVybg0KZ2dwbG90KGRmLGFlcyh4PUNodXJuLHk9VG90YWxDaGFyZ2VzLGZpbGw9Q2h1cm4pKSsNCiAgZ2VvbV9ib3hwbG90KCkrDQogIGxhYnModGl0bGU9IkJveHBsb3Qgb2YgVG90YWwgQ2hhcmdlcyBieSBjaHVybiIsDQogICAgICAgeT0iVG90YWxDaGFyZ2VzIikrDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQpFeHBsYW5hdGlvbg0KY2h1cm5lZCBjdXN0b21lcnMgcGFpZCBtb3JlIG1vbnRobHkgY2hhcmdlcyB0aGFuIHRob3NlIG5vdCBjaHVybmVkDQpjaHVybmVkIGN1c3RvbWVycyBoYXZlIG11Y2ggbG93ZXIgdG90YWwgc3BlbmRpbmcsaW5kaWNhdGluZyBzaG9ydCB0ZW51cmVzLg0KDQojIyBIaXN0b2dyYW0gb2YgTW9udGhseSBDaGFyZ2VzDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmdncGxvdChkZixhZXMoeD1Nb250aGx5Q2hhcmdlcykpKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zPTMwLGZpbGw9InJlZCIsY29sb3I9ImJsYWNrIikrDQogIGxhYnModGl0bGU9Ikhpc3RvZ3JhbSBvZiBNb250aGx5IENoYXJnZXMgRGlzdHJpYnV0aW9uIiwNCiAgICAgICB4PSJNb250aGx5Q2hhcmdlcyIsDQogICAgICAgeT0iZnJlcXVlbmN5IikrDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCiNFeHBsYW5hdGlvbg0KTW9udGhseSBjaGFyZ2VzIGNsb3VkIGF0IGNlcnRhaW4gcHJpY2UgcG9pbnRzLHN1Z2dlc3RpbmcgdGllcmVkIHByaWNpbmcgb3IgYnVuZGxlZCBwYWNrYWdlcywNCg0KIyBIaXN0b2dyYW0gb2YgVG90YWwgQ2hhcmdlcw0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpnZ3Bsb3QoZGYsYWVzKHg9VG90YWxDaGFyZ2VzKSkrDQogIGdlb21faGlzdG9ncmFtKGJpbnM9MzAsZmlsbD0icmVkIixjb2xvcj0iYmxhY2siKSsNCiAgbGFicyh0aXRsZT0iSGlzdG9ncmFtIG9mIFRvdGFsIENoYXJnZXMgRGlzdHJpYnV0aW9uIiwNCiAgICAgICB4PSJUb3RhbENoYXJnZXMiLA0KICAgICAgIHk9ImZyZXF1ZW5jeSIpKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KI0V4cGxhbmF0aW9uDQptb3N0IGN1c3RvbWVycyBoYXZlIGxvdyB0byBtb2RlcmF0ZSB0b3RhbCBjaGFyZ2VzLGxpa2VseSBkdWUgdG8gc2hvcnQgdGVudXJlLGZldyBjdXN0b21lcnMgaW5kaWNhdGUgaGlnaCB0b3RhbCBzcGVuZGluZyxpbmRpY2F0aW5nIGxvbmctdGVybSBsb3lhbHR5Lg0KDQojIyBIWVBPVEhFU0lTIFRFU1RJTkcNCkh5cG90aGVzaXMgMTogRG8gY3VzdG9tZXJzIHdpdGggbG9uZy10ZXJtIGNvbnRyYWN0cyBoYXZlIGhpZ2hlciB0b3RhbCBzcGVuZGluZz8NCuKAoglOdWxsIEh5cG90aGVzaXMgKEjigoApOiBObyBkaWZmZXJlbmNlIGluIFRvdGFsQ2hhcmdlcyBhbW9uZyBjb250cmFjdCB0eXBlcy4NCuKAoglBbHRlcm5hdGl2ZSBIeXBvdGhlc2lzIChI4oKBKTogQ3VzdG9tZXJzIHdpdGggbG9uZ2VyIGNvbnRyYWN0cyBoYXZlIHNpZ25pZmljYW50bHkgaGlnaGVyIFRvdGFsQ2hhcmdlcy4NCiNEZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIGJ5IENvbnRyYWN0IFR5cGUNCmBgYHtyfQ0Kc3VtbWFyeV9ieV9jb250cmFjdDwtZGYlPiUNCiAgZ3JvdXBfYnkoQ29udHJhY3QpJT4lDQogIHN1bW1hcmlzZSgNCiAgICBjb3VudD1uKCksDQogICAgbWVhblRvdGFsQ2hhcmdlPW1lYW4oVG90YWxDaGFyZ2VzLG5hLnJtPVRSVUUpLA0KICAgIHNkVG90YWxDaGFyZ2U9c2QoVG90YWxDaGFyZ2VzLG5hLnJtPVRSVUUpDQogICkNCnByaW50KHN1bW1hcnlfYnlfY29udHJhY3QpDQogIA0KYGBgDQoNCiMjIFZpc3VhbGl6aW5nIFRvdGFsIENoYXJnZXMgYnkgQ29udHJhY3QgVHlwZQ0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpnZ3Bsb3QoZGYsYWVzKHg9VG90YWxDaGFyZ2VzKSkrDQogIGdlb21fYm94cGxvdChmaWxsPSJyZWQiKSsNCiAgbGFicyh0aXRsZT0iVG90YWwgQ2hhcmdlcyBieSBDb250cmFjdCIsDQogICAgICAgeD0iQ29udHJhY3QgVHlwZSIsDQogICAgICAgeT0iVG90YWwgQ2hhcmdlcyIpKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQojIyBQZXJmb3JtaW5nIEFub3ZhDQpgYGB7cn0NCiMgZml0dGluZyBhIGxpbmVhciBtb2RlbA0KYW5vdmFfbW9kZWw8LWFvdihUb3RhbENoYXJnZXMgfiBDb250cmFjdCxkYXRhPWRmKQ0Kc3VtbWFyeShhbm92YV9tb2RlbCkNCmBgYA0KI0NvbmNsdXNpb24NCnAtdmFsdWU8MC4wNSxyZWplY3QgbnVsbCBoeXBvdGhlc2lzLCBhbmQgY29uY2x1ZGUgdGhhdCB0aGVyZSBpcyBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGRpZmZlcmVuY2UgaW4gVG90YWwgQ2hhcmdlcyBiZXR3ZWVuIENvbnRyYWN0IFR5cGVzLg0KI1JlY29tbWVuZGF0aW9uDQpjdXN0b21lcnMgd2l0aCBsb25nZXIgY29udHJhY3RzIHNwZW5kIHNpZ25pZmljYW50bHkgbW9yZSBjb21wYXJlZCB0byB0aG9zZSB3aXRoIHNob3J0ZXIgY29udHJhY3RzLGhlbmNlIGVuY291cmFnZSB0aGUgY3VzdG9tZXJzIHRvIG1vdmUgdG8gbG9uZ2VyIGNvbnRyYWN0cw0KDQojIyBQb3N0IEhvYyB0ZXN0OlR1c2t5cyBIU0QNCmBgYHtyfQ0KVHVrZXlUZXN0PC1UdWtleUhTRChhbm92YV9tb2RlbCkNCnBsb3QoVHVrZXlUZXN0LGxhcz0xKQ0KcHJpbnQoVHVrZXlUZXN0KQ0KYGBgDQoNCiMjIEh5cG90aGVzaXMgMjogSXMgdGhlcmUgYSBzaWduaWZpY2FudCBkaWZmZXJlbmNlIGluIHNwZW5kaW5nIGJldHdlZW4gbWFsZSBhbmQgZmVtYWxlIGN1c3RvbWVycz8NCuKAoglOdWxsIEh5cG90aGVzaXMgKEjigoApOiBObyBkaWZmZXJlbmNlIGluIE1vbnRobHlDaGFyZ2VzIGJldHdlZW4gbWFsZSBhbmQgZmVtYWxlIGN1c3RvbWVycy4NCuKAoglBbHRlcm5hdGl2ZSBIeXBvdGhlc2lzIChI4oKBKTogTWFsZXMgYW5kIGZlbWFsZXMgaGF2ZSBkaWZmZXJlbnQgc3BlbmRpbmcgYmVoYXZpb3JzLg0K4oCiCVRlc3QgdG8gdXNlOiBJbmRlcGVuZGVudCB0LXRlc3QuDQpgYGB7cn0NCnRfdGVzdF9yZXN1bHQgPC0gdC50ZXN0KE1vbnRobHlDaGFyZ2VzfmdlbmRlciwgZGF0YT1kZikNCg0KIyA3LiBQcmludCB0aGUgcmVzdWx0DQpwcmludCh0X3Rlc3RfcmVzdWx0KQ0KYGBgDQojIyBjb25jbHVzaW9uDQpwX3ZhbHVlKDAuMjIxNSk+MC4wNSxoZW5jZSBkbyBub3QgcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMsIGFuZCBjb25jbHVkZSB0aGF0IHRoZXJlIGlzIG5vIGRpZmZlcmVuY2UgYmV0d2VlbiBtYWxlIGFuZCBmZW1hbGUgc3BlbmRpbmcuIA0KDQojNS4gUmVncmVzc2lvbiBBbmFseXNpcw0KQnVpbGRpbmcgYSBMaW5lYXIgUmVncmVzc2lvbiBNb2RlbCB0byBQcmVkaWN0IE1vbnRobHlDaGFyZ2VzDQrigKIJRGVmaW5lIE1vbnRobHlDaGFyZ2VzIGFzIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUuDQrigKIJVXNlIFRlbnVyZSwgQ29udHJhY3QsIGFuZCBQYXltZW50IE1ldGhvZCBhcyBpbmRlcGVuZGVudCB2YXJpYWJsZXMuDQoNCmBgYHtyfQ0KbW9kZWw8LWxtKE1vbnRobHlDaGFyZ2VzfnRlbnVyZStDb250cmFjdCtQYXltZW50TWV0aG9kLGRhdGE9ZGYpDQpzdW1tYXJ5KG1vZGVsKQ0KYGBgDQoNCiMjIG1vZGVsIGRpYWdub3N0aWNzDQpgYGB7cn0NCmxpYnJhcnkoY2FyKSANCnZpZl92YWx1ZXM9dmlmKG1vZGVsKQ0KcHJpbnQodmlmX3ZhbHVlcykNCmBgYA0KIyMgY29uY2x1c2lvbg0Kbm8gbXVsdGljb2xsaW5lYXJpdHkNCg0KIyMgbW9kZWwgcGVyZm9ybWFuY2UgbWV0cmljcw0KYGBge3J9DQojIExvYWQgcGVyZm9ybWFuY2UgcGFja2FnZQ0KbGlicmFyeShwZXJmb3JtYW5jZSkNCg0KIyBHZXQgbW9kZWwgcGVyZm9ybWFuY2UgbWV0cmljcw0KbW9kZWxfcGVyZm9ybWFuY2UobW9kZWwpDQojIFLCsjogTWVhc3VyZXMgbW9kZWwgZ29vZG5lc3Mtb2YtZml0Lg0KYGBgDQojIyBvdXRsaWVycyBhbmQgSW5mbHVlbnRpYWwgUG9pbnRzIERldGVjdGlvbg0KYGBge3J9DQojIENvb2vigJlzIERpc3RhbmNlIFBsb3QNCnBsb3QoY29va3MuZGlzdGFuY2UobW9kZWwpLCANCiAgICAgdHlwZSA9ICJoIiwgDQogICAgIG1haW4gPSAiQ29va+KAmXMgRGlzdGFuY2UiLCANCiAgICAgeWxhYiA9ICJDb29r4oCZcyBEaXN0YW5jZSIpDQpgYGANCiMjY29uY2x1c2lvbg0Kbm8gbmVlZCBmb3IgZnVydGhlciBpbnZlc3RpZ2F0aW9ucw0KDQojUmVzaWR1YWwgZGlhZ25vc3RpY3MNCmBgYHtyfQ0KIyBIaXN0b2dyYW0gb2YgcmVzaWR1YWxzDQpnZ3Bsb3QoZGF0YSA9IGRhdGEuZnJhbWUocmVzaWR1YWxzID0gcmVzaWQobW9kZWwpKSwgYWVzKHggPSByZXNpZHVhbHMpKSArDQogIGdlb21faGlzdG9ncmFtKGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJibHVlIiwgYmlucyA9IDEwKSArDQogIGxhYnModGl0bGUgPSAiSGlzdG9ncmFtIG9mIFJlc2lkdWFscyIsIHggPSAiUmVzaWR1YWxzIiwgeSA9ICJDb3VudCIpDQoNCmBgYA0KIyMgY29uY2x1c2lvbg0KQmVsbCBzaGFwZWQgY3VydmUsUmVzaWR1YWxzIGFyZSBub3JtYWwNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0K