Dataset

The dataset used in this report is about customer churn from a telecommunication company. Customer churn relates to whether a customer leaves or remains with a company. It contains one month of customer data from the teleco company. The raw data contains 7043 rows (customers) and 21 columns (features). It contains one character type variable, 7 binary variables, 3 integer variables and 10 categorical variables.

  1. customer_id - Character
  2. gender - Binary
  3. SeniorCitizen - Binary
  4. Partner - Binary
  5. Dependents - Binary
  6. Tenure - Integer
  7. PhoneService - Binary
  8. MultipleLines - Categorical
  9. InternetService - Categorical
  10. OnlineSecurity - Categorical
  11. OnlineBackup - Categorical
  12. DeviceProtection - Categorical
  13. TechSupport - Categorical
  14. StreamingTV - Categorical
  15. StreamingMovies - Categorical
  16. Contract - Categorical
  17. PaperlessBilling - Binary
  18. PaymentMethod - Categorical
  19. MonthlyCharges - Integer
  20. TotalCharges - Integer
  21. Churn - Binary

Content Each row represents a customer, each column contains customer’s attributes described on the column Metadata.

The data set includes information about:

Customers who left or stayed with the company within the timeframe of one month – the column is called Churn

The timeline for the data is from one month within the company.

It contains information about services that each customer has signed up for – phone, multiple lines, internet, online security, online backup, device protection, tech support, and streaming TV and movies

It contains information about customer account information – how long they’ve been a customer, contract, payment method, paperless billing, monthly charges, and total charges

It contains information about customer demographics – gender, age range, and if they have partners and dependents

Link: https://www.kaggle.com/blastchar/telco-customer-churn

#Code chunk for setup- Version R Studio:  "1.2.5001"

#install.packages ("sqldf")
#install.packages ("ggplot2")
#install.packages ("dplyr")
#install.packages('gridExtra')
#install.packages('plotly')
#install.packages('ggpubr')







library('reshape2') 
library('sqldf')
library('ggplot2')
library('gridExtra')
library('dplyr')
library('plotly')
library('ggpubr')


#Read in File
teleco<-read.csv("C://Users//james//Documents//Teleco.csv", header = TRUE)
View(teleco)

glimpse(teleco)
Rows: 7,043
Columns: 21
$ customerID       <chr> "7590-VHVEG", "5575-GNVDE", "3668-QPYBK", "77...
$ gender           <chr> "Female", "Male", "Male", "Male", "Female", "...
$ SeniorCitizen    <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
$ Partner          <chr> "Yes", "No", "No", "No", "No", "No", "No", "N...
$ Dependents       <chr> "No", "No", "No", "No", "No", "No", "Yes", "N...
$ tenure           <int> 1, 34, 2, 45, 2, 8, 22, 10, 28, 62, 13, 16, 5...
$ PhoneService     <chr> "No", "Yes", "Yes", "No", "Yes", "Yes", "Yes"...
$ MultipleLines    <chr> "No phone service", "No", "No", "No phone ser...
$ InternetService  <chr> "DSL", "DSL", "DSL", "DSL", "Fiber optic", "F...
$ OnlineSecurity   <chr> "No", "Yes", "Yes", "Yes", "No", "No", "No", ...
$ OnlineBackup     <chr> "Yes", "No", "Yes", "No", "No", "No", "Yes", ...
$ DeviceProtection <chr> "No", "Yes", "No", "Yes", "No", "Yes", "No", ...
$ TechSupport      <chr> "No", "No", "No", "Yes", "No", "No", "No", "N...
$ StreamingTV      <chr> "No", "No", "No", "No", "No", "Yes", "Yes", "...
$ StreamingMovies  <chr> "No", "No", "No", "No", "No", "Yes", "No", "N...
$ Contract         <chr> "Month-to-month", "One year", "Month-to-month...
$ PaperlessBilling <chr> "Yes", "No", "Yes", "No", "Yes", "Yes", "Yes"...
$ PaymentMethod    <chr> "Electronic check", "Mailed check", "Mailed c...
$ MonthlyCharges   <dbl> 29.85, 56.95, 53.85, 42.30, 70.70, 99.65, 89....
$ TotalCharges     <dbl> 29.85, 1889.50, 108.15, 1840.75, 151.65, 820....
$ Churn            <chr> "No", "No", "Yes", "No", "Yes", "Yes", "No", ...
#Structure of DF#
str(teleco)
'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" ...
ncol(teleco)
[1] 21
nrow(teleco)
[1] 7043
length(studentresult)
[1] 8
dim(studentresult)
[1] 45  8
#Summary of data#
summary(teleco)
  customerID           gender          SeniorCitizen      Partner         
 Length:7043        Length:7043        Min.   :0.0000   Length:7043       
 Class :character   Class :character   1st Qu.:0.0000   Class :character  
 Mode  :character   Mode  :character   Median :0.0000   Mode  :character  
                                       Mean   :0.1621                     
                                       3rd Qu.:0.0000                     
                                       Max.   :1.0000                     
                                                                          
  Dependents            tenure      PhoneService       MultipleLines     
 Length:7043        Min.   : 0.00   Length:7043        Length:7043       
 Class :character   1st Qu.: 9.00   Class :character   Class :character  
 Mode  :character   Median :29.00   Mode  :character   Mode  :character  
                    Mean   :32.37                                        
                    3rd Qu.:55.00                                        
                    Max.   :72.00                                        
                                                                         
 InternetService    OnlineSecurity     OnlineBackup      
 Length:7043        Length:7043        Length:7043       
 Class :character   Class :character   Class :character  
 Mode  :character   Mode  :character   Mode  :character  
                                                         
                                                         
                                                         
                                                         
 DeviceProtection   TechSupport        StreamingTV       
 Length:7043        Length:7043        Length:7043       
 Class :character   Class :character   Class :character  
 Mode  :character   Mode  :character   Mode  :character  
                                                         
                                                         
                                                         
                                                         
 StreamingMovies      Contract         PaperlessBilling  
 Length:7043        Length:7043        Length:7043       
 Class :character   Class :character   Class :character  
 Mode  :character   Mode  :character   Mode  :character  
                                                         
                                                         
                                                         
                                                         
 PaymentMethod      MonthlyCharges    TotalCharges       Churn          
 Length:7043        Min.   : 18.25   Min.   :  18.8   Length:7043       
 Class :character   1st Qu.: 35.50   1st Qu.: 401.4   Class :character  
 Mode  :character   Median : 70.35   Median :1397.5   Mode  :character  
                    Mean   : 64.76   Mean   :2283.3                     
                    3rd Qu.: 89.85   3rd Qu.:3794.7                     
                    Max.   :118.75   Max.   :8684.8                     
                                     NA's   :11                         
#Look at missing data for all columns in df
teleco %>%
  select(everything()) %>%  
  summarise_all(funs(sum(is.na(.))))

#Remove Missing Values
teleco <-na.omit(teleco)

1. Introduction

As technology is advancing telecommunication services are in competition with eachother to retain customers (Amin et al., 2017). It is estimated that annual customer churn within telecommunication companies ranges from 20% to 40% (Ahn et al., 2006). It is important for telecommunication companies to retain a customer base in order to keep their profit level. It has also been stated many times how it is more expensive to acquire a new customer than it is to retain an existing customer (Ahn et al., 2006). Therefore it is clear that telecommunication companies should be focused on retaining customers rather than acquiring new customers. They should be focused on reducing brand switching within their existing customer base. As technology advances and new services become available to customers it is important for telecommunication companies to monitor the ttrends in the industry. They must be able to maintain industry standard of services expected by customers ar a fair price point. This report aims to investigate some of the causes of customer churn within the telecommunication industry.

2. User Story

Primary Groups or Individuals: The primary individuals I will be communicating to will be senior management in a Teleco company.

What does audience care about?: The audience will want to understand what is causing customers to leave their company. Teleco companys are quite competitive today with eachother and will want to retain as many customers as possible. They will want to know if there is anything they can improve with their business model to retain more customers. Is there something that customers are not satisfied with? In order to better understand customer churn they need to understand their customer behaviour and the triggers that are causing customers to leave.

What action does audience need to take?: Once the insights have been provided to senior management they will need to assess whether they need to improve or refine their business model for customers to make it more appealing and to avoid customers leaving. They will need to conudct an assessment to ensure the services they offer are up to date with industry standards and current trends in the market. They may need to improve their streaming service for example as it may not be up to industry standard or perhaps their pricing model is being undermined by a competitor.

Benefits: The benefits of providing the insights to senior management is that they will be able to identify key reasons why customers are leaving and not being retained. This will allow senior management to act and make changes necessary in order to avoid losing customers and retain as much as possible. Having a stable and large customer base gives a company a good image among the public. This in turn may also increase referrals from existing customers and will also ensure that the companys profit level remains stable.

Risks: The risks of not acting on the insights provided would be that they may lose a large amount of customers. Their services may also fall far behind current industry standards and they will not have any insights into what is required to retain their customer base and keep their customer base satisfied with their service. If the company continues to lose customers they will in turn lose profit. They also risk getting a bad reputation among the public due to word of mouth from customers that are leaving.

Overall, the aim of this report is to provide key insights into what is causing a lack of customer retention in a teleco company.

3. Data Exploration and wrangling

In the below section I have conducted some data exploration and created some simple plots of variables of interest. I have created bar graphs looking at churn, gender, contract type, phone service, internet service, movie streaming and tv streaming.I have also wrangled the data and created some data frames which I used to look at certain possible trends and some that I will use later in my visualisations. For example I have looked at the count of how many customers did or did not churn grouped by the contract type which will be used in visualisation 1.

#Data Exploration and wrangling 

#Look at Churn Count
Churn_count <- sqldf('Select count(churn) as count, churn from teleco group by churn')
View(Churn_count)

ggplot(data=Churn_count, aes(x=Churn, y=count, fill = Churn)) + geom_bar(stat="identity")



#Look at gender distribution
Gender <- sqldf('Select count(gender) as count, gender from teleco group by gender')
View(Gender)

ggplot(data=Gender, aes(x=gender, y=count, fill = gender)) + geom_bar(stat="identity")



#Look at Contract Type
Contract_count <- sqldf('Select count(Contract) as count, Contract from teleco group by Contract')
View(Contract_count)

ggplot(data=Contract_count, aes(x=Contract, y=count, fill = Contract)) + geom_bar(stat="identity")



#Look at Phone Service
Phone_service <- sqldf('Select count(PhoneService) as count, PhoneService from teleco group by PhoneService')
View(Phone_service)

ggplot(data=Phone_service, aes(x=PhoneService, y=count, fill = PhoneService)) + geom_bar(stat="identity")


#Look at Internet Service
Internet_service <- sqldf('Select count(InternetService) as count, InternetService from teleco group by InternetService')
View(Internet_service)

ggplot(data=Internet_service, aes(x=InternetService, y=count, fill = InternetService)) + geom_bar(stat="identity")



#Look at Movie Service
Movie_service <- sqldf('Select count(StreamingMovies) as count, StreamingMovies from teleco group by StreamingMovies')
View(Movie_service)

ggplot(data=Movie_service, aes(x=StreamingMovies, y=count, fill = StreamingMovies)) + geom_bar(stat="identity")



#Look at TV Service
TV_service <- sqldf('Select count(StreamingTV) as count, StreamingTV from teleco group by StreamingTV')
View(TV_service)

ggplot(data=TV_service, aes(x=StreamingTV, y=count, fill = StreamingTV)) + geom_bar(stat="identity")




#Look at Churn for Contract
Churn_contract <- sqldf('Select count(churn) as count, churn, contract from teleco  group by contract, churn order by churn, count desc')
View(Churn_contract)

df <- subset(Churn_contract, Churn_contract$Churn == 'Yes')
View(df)

#Look at Monthly Charges
Charges <- sqldf('Select count(churn) as count, MonthlyCharges, churn from teleco group by MonthlyCharges,churn order by churn, count desc')
View(Charges)


#Look at Payment Method
Payment <- sqldf('Select count(churn) as count, churn, PaymentMethod from teleco  group by PaymentMethod, churn order by churn, count desc')
View(Payment)

#Look at Internet Service
Internet <- sqldf('Select count(churn) as count, churn, InternetService from teleco group by InternetService, churn order by churn, count desc')
View(Internet)

#Look at Tenure
Tenure <- sqldf('Select count(churn) as count, Tenure, churn from teleco group by Tenure,churn order by churn, count desc')
View(Charges)

#Look at Streaming TV
Tv <- sqldf('Select count(churn) as count, StreamingTv, churn from teleco group by StreamingTv,churn order by churn, count desc')
View(Tv)

#Look at Streaming Movies
Movies <- sqldf('Select count(churn) as count, StreamingMovies, churn from teleco group by StreamingMovies,churn order by churn,count desc')
View(Movies)

#Look at gender distribution
Gender <- sqldf('Select count(gender) as count, gender from teleco group by gender')
View(Gender)

Gender_churn <- sqldf('Select count(churn) as count, gender, churn from teleco group by gender, churn')
View(Gender_churn)

3. Visualisations

3.1 Visualisation 1: Relationship between contract type and customer churn

From the data exploration their appears to be a relationship between contract type and customer retention. Customers who are on a monthly contract are leaving more than customers on a one year or two year contract. Therefore this is an indicator to me that there is perhaps a relationship between the cost of monthly fees and customer retention. This is something that will be investigated with a visualization. The first visualisation that will be presented to senior management will be a histogram plot showing that most customers who have left have been on a monthly contract rather than a one year or two year contract. In my previous iterations which can be viewed further on in this report I experimented with the position of the bar graphs. I decided to use a bar graph as I found that it was the best method to portray this data. The bar graph effectively shows the large difference between the contract types. I had originally created a static plot but my final iteration I decided to make the plot interactive. This will allow senior management to interact with this visualisation and filter the data however they wish to view it.

#Contract Plot
View(Churn_contract)

Churn_contract2 <- melt(Churn_contract)
Using Churn, Contract as id variables
View (Churn_contract2)


#Contract Plot Final Iteration (Static)
Contract_Plot <- ggplot(data=Churn_contract, aes(x=Contract, y=count, fill = Churn)) + geom_bar(stat="identity", position= 'dodge') + ylab('Churn Count') + ggtitle('Relationship Between Customer Churn and Contract Type')


#Contract Plot Final Iteration (Interactive)
ggplotly(Contract_Plot) %>%
  layout(
    title = 'Relationship Between Churn and Contract Type'
  )

NA
NA

3.2 Visualisation 2: Relationship between Monthly Charges and Customer Churn

Senior management can see from the visualisation 1 that most customers leaving are on a monthly contract. The next visualisation that will be presented to senior management will be a boxplot showing that customers who are leaving are also paying higher monthly fees. This could be an indication to senior management that they need to assess their pricing plan.I had experimented with different plot types such as line graph or scatter plot which can be viewed in my previous iterations further on in the report. I used a boxplot as I found that it was the best plot to convey the message and effectively shows the difference in monthly fees between customers who are leaving and customers who are staying with the company. The box plot is easy to read and understand. However, senior management will now want to understand why these customers are paying higher monthly charges. They will question why customers are paying such higher monthly charges compared to other customers. This will be explored in the next visualisation.



#Monthly Charges Final Iteration
ggplot(teleco, aes(y= MonthlyCharges, x = "Churn", fill = Churn)) + geom_boxplot()+  xlab(" ") +ggtitle('Relationship Between Monthly Charges and Customer Churn ')

NA
NA

3.3 Visualisation 3: Comparison of Monthly charges for Services Offered

From the previous visualisation presented to senior management we can identify that customers who are leaving are paying high monthly fees. Senior management will wasn’t to question why these customers are paying high monthly fees so the aim of the next visualisation will be to look at how much difference there is in the cost of services they provide. It looks to investigate whether there is a big difference in the monthly fees for the services offered. The services that are looked at an compared in this visualisation are TV streaming, Movie streaming an internet service. I decided to continue to use box plot diagrams as it is effective in communicating whether there is a high difference between monthly fees of certain services. I also decided to arrange the plots created together in order to compare them. From the visualisation senior management can see that there is a wide gap in monthly fees for the high end services they provide. For example

#compare Internet Type and monthly charges

Internet_Charges <- sqldf('Select count(churn) as count, MonthlyCharges, churn, InternetService from teleco group by MonthlyCharges,churn order by churn, count desc')
View(Internet_Charges)


Internet_plot <-ggplot(Internet_Charges, aes(y= MonthlyCharges, x= " ", fill = InternetService)) + geom_boxplot()+  xlab(" Churn") + scale_fill_ordinal() + theme_minimal()

#compare Streaming TV and monthly charges

TV_Charges <- sqldf('Select count(churn) as count, MonthlyCharges, churn, StreamingTV from teleco group by MonthlyCharges,churn order by churn, count desc')
View(TV_Charges)



Tv_plot <- ggplot(TV_Charges, aes(y= MonthlyCharges, x= " ", fill = StreamingTV)) + geom_boxplot()+  xlab(" Churn") + scale_fill_ordinal() + theme_minimal()


#compare Streaming Movies and monthly charges

Movie_Charges <- sqldf('Select count(churn) as count, MonthlyCharges, churn, StreamingMovies from teleco group by MonthlyCharges,churn order by churn, count desc')
View(Movie_Charges)



Movie_plot <- ggplot(Movie_Charges, aes(y= MonthlyCharges, x= " ", fill = StreamingMovies)) + geom_boxplot()+  xlab(" Churn") + scale_fill_ordinal() + theme_minimal()


#Final Iteration

charges_comparison <- ggarrange( Tv_plot, Movie_plot,Internet_plot, ncol = 2, nrow = 2)

annotate_figure(charges_comparison,top = text_grob("Comparing Service Monthly Charges", color = "black", face = "bold", size = 14))

NA
NA
NA
NA

3.4 Visualisation 4: Relationship Between Churn and Tenure

The final visualisation presented to senior management will be to look at how tenure is related to customer churn. There tends to be a trend that the longer customers are retained the less likely they are to stop using a companies service. This can be seen in the line graph below. I have chosen an animated line graph to represent the data. Each line represents the churn type (yes and no). The line graph was chosen as there are two numeric variables being represented ( tenure and count of customers). From my iterations which can be seen below I found the line graph the best method to represent this data. From the graph it can be identified that the highest number of customers that churned were only with the company for 10 months. As customers tenure increased with the company their likelihood of churning decreased. This should prompt senior management to put more focus on the customers who have most recently joined within the past few months and to focus on retaining these customers.



p <- teleco %>%
  group_by(tenure, Churn) %>%
  summarise(count = n(), .groups = 'drop') %>%
  ggplot(aes(tenure, count, color = Churn)) +
  geom_line(size = 2) + ggtitle('Relationship Between Tenure And Customer Churn') + theme(plot.title = element_text(hjust = 0.5))

p + geom_point() + transition_reveal(tenure)

Frame 1 (1%)
Frame 2 (2%)
Frame 3 (3%)
Frame 4 (4%)
Frame 5 (5%)
Frame 6 (6%)
Frame 7 (7%)
Frame 8 (8%)
Frame 9 (9%)
Frame 10 (10%)
Frame 11 (11%)
Frame 12 (12%)
Frame 13 (13%)
Frame 14 (14%)
Frame 15 (15%)
Frame 16 (16%)
Frame 17 (17%)
Frame 18 (18%)
Frame 19 (19%)
Frame 20 (20%)
Frame 21 (21%)
Frame 22 (22%)
Frame 23 (23%)
Frame 24 (24%)
Frame 25 (25%)
Frame 26 (26%)
Frame 27 (27%)
Frame 28 (28%)
Frame 29 (29%)
Frame 30 (30%)
Frame 31 (31%)
Frame 32 (32%)
Frame 33 (33%)
Frame 34 (34%)
Frame 35 (35%)
Frame 36 (36%)
Frame 37 (37%)
Frame 38 (38%)
Frame 39 (39%)
Frame 40 (40%)
Frame 41 (41%)
Frame 42 (42%)
Frame 43 (43%)
Frame 44 (44%)
Frame 45 (45%)
Frame 46 (46%)
Frame 47 (47%)
Frame 48 (48%)
Frame 49 (49%)
Frame 50 (50%)
Frame 51 (51%)
Frame 52 (52%)
Frame 53 (53%)
Frame 54 (54%)
Frame 55 (55%)
Frame 56 (56%)
Frame 57 (57%)
Frame 58 (58%)
Frame 59 (59%)
Frame 60 (60%)
Frame 61 (61%)
Frame 62 (62%)
Frame 63 (63%)
Frame 64 (64%)
Frame 65 (65%)
Frame 66 (66%)
Frame 67 (67%)
Frame 68 (68%)
Frame 69 (69%)
Frame 70 (70%)
Frame 71 (71%)
Frame 72 (72%)
Frame 73 (73%)
Frame 74 (74%)
Frame 75 (75%)
Frame 76 (76%)
Frame 77 (77%)
Frame 78 (78%)
Frame 79 (79%)
Frame 80 (80%)
Frame 81 (81%)
Frame 82 (82%)
Frame 83 (83%)
Frame 84 (84%)
Frame 85 (85%)
Frame 86 (86%)
Frame 87 (87%)
Frame 88 (88%)
Frame 89 (89%)
Frame 90 (90%)
Frame 91 (91%)
Frame 92 (92%)
Frame 93 (93%)
Frame 94 (94%)
Frame 95 (95%)
Frame 96 (96%)
Frame 97 (97%)
Frame 98 (98%)
Frame 99 (99%)
Frame 100 (100%)
Finalizing encoding... done!

4.Previous Iterations

4.1 Visualisation 1 Iterations: Relationship between contract type and customer churn

In this section I will show previous iterations of the plots created for this report. Firstly below shows the previous iterations for visualisation 1. I experimented with flipping the position of the bar graphs and also stacking the bar graphs. However I decided to group the bar graphs in the end as I found that when they were stacked it made it more difficult to compare the sizes for each churn type. When I grouoed the bar plots it remedied this. I also had created a static plot that I felt looked well but in order to add a bit more interactivity I decided to make the final plot an interactive plot.


#Contract Plot Iteration 1
ggplot(data=Churn_contract, aes(x=count, y=Contract, fill = Churn)) + geom_histogram(stat="identity") 
Ignoring unknown parameters: binwidth, bins, pad

#Contract Plot Iteration 2
ggplot(data=Churn_contract, aes(x=count, y=Contract, fill = Churn)) + geom_histogram(stat="identity", position = 'dodge') 
Ignoring unknown parameters: binwidth, bins, pad

#Contract Plot Iteration 3 (Static)
ggplot(data=Churn_contract, aes(x=Contract, y=count, fill = Churn)) + geom_bar(stat="identity", position= 'dodge') + ylab('Churn Count') + ggtitle('Relationship Between Customer Churn and Contract Type')

NA
NA

4.2 Visualisation 2 Iterations: Relationship between Monthly Charges and Customer Churn

Previous Iterations for Visualisation 2 can be seen below. I experimented with different plot types such as line graph or a scatter plot and frequency polygon graph. However, I felt these plots did not represent the data in a clean manner and the box plot was much better at conveying the message to stakeholders. I also chose the box plot as I wanted to have a diverse range of plots in the report. The box plot allows senior management to quickly identify mean values and the difference in monthly charges being paid by customers who are leaving the company.

#Monthly Charges Plot Iteration 1
View(Charges) 

ggplot(Charges,aes(x = MonthlyCharges, y=count, color = Churn))+ geom_line(size=2) + ylab('Churn Count') +ggtitle('Relationship Between Monthly Charges and Customer Churn')


#Monthly Charges Iteration 2
ggplot(Charges,aes(x = MonthlyCharges, y=count, color = Churn))+ geom_point() + ylab('Churn Count') +ggtitle('Relationship Between Monthly Charges and Customer Churn')


#Monthly Charges Iteration 3
ggplot(Charges,aes(x = MonthlyCharges, color = Churn))+ geom_freqpoly(size=2) + ylab('Churn Count') +ggtitle('Relationship Between Monthly Charges and Customer Churn')

NA
NA

4.3 Visualisation 3 Iterations: Comparison of Monthly charges for Services Offered

Previous Iterations for Visualisation 3 can be seen below. I had to experiment with the number of row and columns. As can be seen below in my previous iterations the plots were overlapping eachother and did not present in a tidy manner. I also experimented with including a title for each of the plots but these titles also overlapped eachother. I decided to remove the titles and include just one title for the overall plot which made it look neater and I felt that the legend for each plot conveyed what the monthly charges were for in each plot.


#Iteration 1
grid.arrange(Internet_plot, Tv_plot, Movie_plot, nrow=2, widths = c(2, 1, 1),layout_matrix = rbind(c(1, 2, NA),c(3, 3, 4)))


#Iteration 2
grid.arrange(Internet_plot, Tv_plot, Movie_plot, nrow=2, widths = c(2, 1, 1))

NA
NA

4.4 Visualisation 4 Iterations: Relationship between Tenure and Customer Churn

Previous Iterations for Visualisation 4 can be seen below. I first experimented with different pot types such as histogram and scatterplots. I found that the frequency polygon plot seemed to look the most clean and convey the message most effectively. However, when I attempted to animate this plot it did not work very effectively. I experimented with the transition functions such as transition_state etc but it did not animate correctly. Therefore instead of using a frequency polygon plot I switched to a line graph which was used in my final visualisation and could be animated correctly.


#Tenure Plot Iteration 1
ggplot(data=Tenure, aes(x=tenure, y=count, fill = Churn)) + geom_histogram(stat="identity") 
Ignoring unknown parameters: binwidth, bins, pad

#Tenure Plot Iteration 2
ggplot(Tenure,aes(x = tenure, y=count, color = Churn))+ geom_point() + ylab('Churn Count') +ggtitle('Relationship Between Monthly Charges and Customer Churn')


#Tenure Plot Iteration 3
Tenure_Iteration4 <- ggplot(teleco,aes(x = tenure, color = Churn))+ geom_freqpoly(size=2)+theme_minimal()

Tenure_Iteration4

Tenure_Iteration4 + transition_reveal(tenure)
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Frame 1 (1%)
Frame 2 (2%)
Frame 3 (3%)
Frame 4 (4%)
Frame 5 (5%)
Frame 6 (6%)
Frame 7 (7%)
Frame 8 (8%)
Frame 9 (9%)
Frame 10 (10%)
Frame 11 (11%)
Frame 12 (12%)
Frame 13 (13%)
Frame 14 (14%)
Frame 15 (15%)
Frame 16 (16%)
Frame 17 (17%)
Frame 18 (18%)
Frame 19 (19%)
Frame 20 (20%)
Frame 21 (21%)
Frame 22 (22%)
Frame 23 (23%)
Frame 24 (24%)
Frame 25 (25%)
Frame 26 (26%)
Frame 27 (27%)
Frame 28 (28%)
Frame 29 (29%)
Frame 30 (30%)
Frame 31 (31%)
Frame 32 (32%)
Frame 33 (33%)
Frame 34 (34%)
Frame 35 (35%)
Frame 36 (36%)
Frame 37 (37%)
Frame 38 (38%)
Frame 39 (39%)
Frame 40 (40%)
Frame 41 (41%)
Frame 42 (42%)
Frame 43 (43%)
Frame 44 (44%)
Frame 45 (45%)
Frame 46 (46%)
Frame 47 (47%)
Frame 48 (48%)
Frame 49 (49%)
Frame 50 (50%)
Frame 51 (51%)
Frame 52 (52%)
Frame 53 (53%)
Frame 54 (54%)
Frame 55 (55%)
Frame 56 (56%)
Frame 57 (57%)
Frame 58 (58%)
Frame 59 (59%)
Frame 60 (60%)
Frame 61 (61%)
Frame 62 (62%)
Frame 63 (63%)
Frame 64 (64%)
Frame 65 (65%)
Frame 66 (66%)
Frame 67 (67%)
Frame 68 (68%)
Frame 69 (69%)
Frame 70 (70%)
Frame 71 (71%)
Frame 72 (72%)
Frame 73 (73%)
Frame 74 (74%)
Frame 75 (75%)
Frame 76 (76%)
Frame 77 (77%)
Frame 78 (78%)
Frame 79 (79%)
Frame 80 (80%)
Frame 81 (81%)
Frame 82 (82%)
Frame 83 (83%)
Frame 84 (84%)
Frame 85 (85%)
Frame 86 (86%)
Frame 87 (87%)
Frame 88 (88%)
Frame 89 (89%)
Frame 90 (90%)
Frame 91 (91%)
Frame 92 (92%)
Frame 93 (93%)
Frame 94 (94%)
Frame 95 (95%)
Frame 96 (96%)
Frame 97 (97%)
Frame 98 (98%)
Frame 99 (99%)
Frame 100 (100%)
Finalizing encoding... done!

References

  1. Ahn, J. H., Han, S. P., & Lee, Y. S. (2006). Customer churn analysis: Churn determinants and mediation effects of partial defection in the Korean mobile telecommunications service industry. Telecommunications policy, 30(10-11), 552-568.

  2. Amin, A., Anwar, S., Adnan, A., Nawaz, M., Alawfi, K., Hussain, A., & Huang, K. (2017). Customer churn prediction in the telecommunication sector using a rough set approach. Neurocomputing, 237, 242-254.

LS0tDQp0aXRsZTogIkRhdGEgVmlzdWFsaXNhdGlvbiBDQTIiDQphdXRob3I6ICdEMTkxMjU2NTA6IEphbWllIEJheHRlciBUVTA2MCAtIFBhcnQgVGltZScNCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQojIERhdGFzZXQNClRoZSBkYXRhc2V0IHVzZWQgaW4gdGhpcyByZXBvcnQgaXMgYWJvdXQgY3VzdG9tZXIgY2h1cm4gZnJvbSBhIHRlbGVjb21tdW5pY2F0aW9uIGNvbXBhbnkuIEN1c3RvbWVyIGNodXJuIHJlbGF0ZXMgdG8gd2hldGhlciBhIGN1c3RvbWVyIGxlYXZlcyBvciByZW1haW5zIHdpdGggYSBjb21wYW55LiBJdCBjb250YWlucyBvbmUgbW9udGggb2YgY3VzdG9tZXIgZGF0YSBmcm9tIHRoZSB0ZWxlY28gY29tcGFueS4gVGhlIHJhdyBkYXRhIGNvbnRhaW5zIDcwNDMgcm93cyAoY3VzdG9tZXJzKSBhbmQgMjEgY29sdW1ucyAoZmVhdHVyZXMpLiBJdCBjb250YWlucyBvbmUgY2hhcmFjdGVyIHR5cGUgdmFyaWFibGUsIDcgYmluYXJ5IHZhcmlhYmxlcywgMyBpbnRlZ2VyIHZhcmlhYmxlcyBhbmQgMTAgY2F0ZWdvcmljYWwgdmFyaWFibGVzLg0KDQoNCjEuIGN1c3RvbWVyX2lkIC0gQ2hhcmFjdGVyDQoyLiBnZW5kZXIgLSBCaW5hcnkNCjMuIFNlbmlvckNpdGl6ZW4gIC0gQmluYXJ5DQo0LiBQYXJ0bmVyIC0gQmluYXJ5DQo1LiBEZXBlbmRlbnRzIC0gQmluYXJ5DQo2LiBUZW51cmUgLSBJbnRlZ2VyDQo3LiBQaG9uZVNlcnZpY2UgLSBCaW5hcnkNCjguIE11bHRpcGxlTGluZXMgLSBDYXRlZ29yaWNhbA0KOS4gSW50ZXJuZXRTZXJ2aWNlIC0gQ2F0ZWdvcmljYWwNCjEwLiBPbmxpbmVTZWN1cml0eSAtIENhdGVnb3JpY2FsDQoxMS4gT25saW5lQmFja3VwIC0gQ2F0ZWdvcmljYWwNCjEyLiBEZXZpY2VQcm90ZWN0aW9uIC0gQ2F0ZWdvcmljYWwNCjEzLiBUZWNoU3VwcG9ydCAtIENhdGVnb3JpY2FsDQoxNC4gU3RyZWFtaW5nVFYgLSBDYXRlZ29yaWNhbA0KMTUuIFN0cmVhbWluZ01vdmllcyAtIENhdGVnb3JpY2FsDQoxNi4gQ29udHJhY3QgLSBDYXRlZ29yaWNhbA0KMTcuIFBhcGVybGVzc0JpbGxpbmcgLSBCaW5hcnkNCjE4LiBQYXltZW50TWV0aG9kIC0gQ2F0ZWdvcmljYWwNCjE5LiBNb250aGx5Q2hhcmdlcyAtIEludGVnZXINCjIwLiBUb3RhbENoYXJnZXMgLSBJbnRlZ2VyDQoyMS4gQ2h1cm4gLSBCaW5hcnkNCg0KQ29udGVudA0KRWFjaCByb3cgcmVwcmVzZW50cyBhIGN1c3RvbWVyLCBlYWNoIGNvbHVtbiBjb250YWlucyBjdXN0b21lcuKAmXMgYXR0cmlidXRlcyBkZXNjcmliZWQgb24gdGhlIGNvbHVtbiBNZXRhZGF0YS4NCg0KVGhlIGRhdGEgc2V0IGluY2x1ZGVzIGluZm9ybWF0aW9uIGFib3V0Og0KDQpDdXN0b21lcnMgd2hvIGxlZnQgb3Igc3RheWVkIHdpdGggdGhlIGNvbXBhbnkgd2l0aGluIHRoZSB0aW1lZnJhbWUgb2Ygb25lIG1vbnRoIOKAkyB0aGUgY29sdW1uIGlzIGNhbGxlZCBDaHVybg0KDQpUaGUgdGltZWxpbmUgZm9yIHRoZSBkYXRhIGlzIGZyb20gb25lIG1vbnRoIHdpdGhpbiB0aGUgY29tcGFueS4NCg0KSXQgY29udGFpbnMgaW5mb3JtYXRpb24gYWJvdXQgc2VydmljZXMgdGhhdCBlYWNoIGN1c3RvbWVyIGhhcyBzaWduZWQgdXAgZm9yIOKAkyBwaG9uZSwgbXVsdGlwbGUgbGluZXMsIGludGVybmV0LCBvbmxpbmUgc2VjdXJpdHksIG9ubGluZSBiYWNrdXAsIGRldmljZSBwcm90ZWN0aW9uLCB0ZWNoIHN1cHBvcnQsIGFuZCBzdHJlYW1pbmcgVFYgYW5kIG1vdmllcw0KDQpJdCBjb250YWlucyBpbmZvcm1hdGlvbiBhYm91dCBjdXN0b21lciBhY2NvdW50IGluZm9ybWF0aW9uIOKAkyBob3cgbG9uZyB0aGV54oCZdmUgYmVlbiBhIGN1c3RvbWVyLCBjb250cmFjdCwgcGF5bWVudCBtZXRob2QsIHBhcGVybGVzcyBiaWxsaW5nLCBtb250aGx5IGNoYXJnZXMsIGFuZCB0b3RhbCBjaGFyZ2VzDQoNCkl0IGNvbnRhaW5zIGluZm9ybWF0aW9uIGFib3V0IGN1c3RvbWVyIGRlbW9ncmFwaGljcyDigJMgZ2VuZGVyLCBhZ2UgcmFuZ2UsIGFuZCBpZiB0aGV5IGhhdmUgcGFydG5lcnMgYW5kIGRlcGVuZGVudHMNCg0KTGluazogaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9ibGFzdGNoYXIvdGVsY28tY3VzdG9tZXItY2h1cm4NCg0KDQoNCmBgYHtyfQ0KI0NvZGUgY2h1bmsgZm9yIHNldHVwLSBWZXJzaW9uIFIgU3R1ZGlvOiAgIjEuMi41MDAxIg0KDQojaW5zdGFsbC5wYWNrYWdlcyAoInNxbGRmIikNCiNpbnN0YWxsLnBhY2thZ2VzICgiZ2dwbG90MiIpDQojaW5zdGFsbC5wYWNrYWdlcyAoImRwbHlyIikNCiNpbnN0YWxsLnBhY2thZ2VzKCdncmlkRXh0cmEnKQ0KI2luc3RhbGwucGFja2FnZXMoJ3Bsb3RseScpDQojaW5zdGFsbC5wYWNrYWdlcygnZ2dwdWJyJykNCg0KDQpsaWJyYXJ5KCdyZXNoYXBlMicpIA0KbGlicmFyeSgnc3FsZGYnKQ0KbGlicmFyeSgnZ2dwbG90MicpDQpsaWJyYXJ5KCdncmlkRXh0cmEnKQ0KbGlicmFyeSgnZHBseXInKQ0KbGlicmFyeSgncGxvdGx5JykNCmxpYnJhcnkoJ2dncHVicicpDQpsaWJyYXJ5KCdnZ2FuaW1hdGUnKQ0KDQoNCiNSZWFkIGluIEZpbGUNCnRlbGVjbzwtcmVhZC5jc3YoIkM6Ly9Vc2Vycy8vamFtZXMvL0RvY3VtZW50cy8vVGVsZWNvLmNzdiIsIGhlYWRlciA9IFRSVUUpDQpWaWV3KHRlbGVjbykNCg0KZ2xpbXBzZSh0ZWxlY28pDQoNCiNTdHJ1Y3R1cmUgb2YgREYjDQpzdHIodGVsZWNvKQ0KbmNvbCh0ZWxlY28pDQpucm93KHRlbGVjbykNCmxlbmd0aCh0ZWxlY28pDQoNCmRpbSh0ZWxlY28pDQoNCiNTdW1tYXJ5IG9mIGRhdGEjDQpzdW1tYXJ5KHRlbGVjbykNCg0KI0xvb2sgYXQgbWlzc2luZyBkYXRhIGZvciBhbGwgY29sdW1ucyBpbiBkZg0KdGVsZWNvICU+JQ0KICBzZWxlY3QoZXZlcnl0aGluZygpKSAlPiUgIA0KICBzdW1tYXJpc2VfYWxsKGZ1bnMoc3VtKGlzLm5hKC4pKSkpDQoNCiNSZW1vdmUgTWlzc2luZyBWYWx1ZXMNCnRlbGVjbyA8LW5hLm9taXQodGVsZWNvKQ0KDQpgYGANCiMgMS4gSW50cm9kdWN0aW9uDQpBcyB0ZWNobm9sb2d5IGlzIGFkdmFuY2luZyB0ZWxlY29tbXVuaWNhdGlvbiBzZXJ2aWNlcyBhcmUgaW4gY29tcGV0aXRpb24gd2l0aCBlYWNob3RoZXIgdG8gcmV0YWluIGN1c3RvbWVycyAoQW1pbiBldCBhbC4sIDIwMTcpLiBJdCBpcyBlc3RpbWF0ZWQgdGhhdCBhbm51YWwgY3VzdG9tZXIgY2h1cm4gd2l0aGluIHRlbGVjb21tdW5pY2F0aW9uIGNvbXBhbmllcyByYW5nZXMgZnJvbSAyMCUgdG8gNDAlIChBaG4gZXQgYWwuLCAyMDA2KS4gSXQgaXMgaW1wb3J0YW50IGZvciB0ZWxlY29tbXVuaWNhdGlvbiBjb21wYW5pZXMgdG8gcmV0YWluIGEgY3VzdG9tZXIgYmFzZSBpbiBvcmRlciB0byBrZWVwIHRoZWlyIHByb2ZpdCBsZXZlbC4gSXQgaGFzIGFsc28gYmVlbiBzdGF0ZWQgbWFueSB0aW1lcyBob3cgaXQgaXMgbW9yZSBleHBlbnNpdmUgdG8gYWNxdWlyZSBhIG5ldyBjdXN0b21lciB0aGFuIGl0IGlzIHRvIHJldGFpbiBhbiBleGlzdGluZyBjdXN0b21lciAoQWhuIGV0IGFsLiwgMjAwNikuIFRoZXJlZm9yZSBpdCBpcyBjbGVhciB0aGF0IHRlbGVjb21tdW5pY2F0aW9uIGNvbXBhbmllcyBzaG91bGQgYmUgZm9jdXNlZCBvbiByZXRhaW5pbmcgY3VzdG9tZXJzIHJhdGhlciB0aGFuIGFjcXVpcmluZyBuZXcgY3VzdG9tZXJzLiBUaGV5IHNob3VsZCBiZSBmb2N1c2VkIG9uIHJlZHVjaW5nIGJyYW5kIHN3aXRjaGluZyB3aXRoaW4gdGhlaXIgZXhpc3RpbmcgY3VzdG9tZXIgYmFzZS4gQXMgdGVjaG5vbG9neSBhZHZhbmNlcyBhbmQgbmV3IHNlcnZpY2VzIGJlY29tZSBhdmFpbGFibGUgdG8gY3VzdG9tZXJzIGl0IGlzIGltcG9ydGFudCBmb3IgdGVsZWNvbW11bmljYXRpb24gY29tcGFuaWVzIHRvIG1vbml0b3IgdGhlIHR0cmVuZHMgaW4gdGhlIGluZHVzdHJ5LiBUaGV5IG11c3QgYmUgYWJsZSB0byBtYWludGFpbiBpbmR1c3RyeSBzdGFuZGFyZCBvZiBzZXJ2aWNlcyBleHBlY3RlZCBieSBjdXN0b21lcnMgYXIgYSBmYWlyIHByaWNlIHBvaW50LiBUaGlzIHJlcG9ydCBhaW1zIHRvIGludmVzdGlnYXRlIHNvbWUgb2YgdGhlIGNhdXNlcyBvZiBjdXN0b21lciBjaHVybiB3aXRoaW4gdGhlIHRlbGVjb21tdW5pY2F0aW9uIGluZHVzdHJ5Lg0KDQojIDIuIFVzZXIgU3RvcnkNCg0KKipQcmltYXJ5IEdyb3VwcyBvciBJbmRpdmlkdWFsczoqKiBUaGUgcHJpbWFyeSBpbmRpdmlkdWFscyBJIHdpbGwgYmUgY29tbXVuaWNhdGluZyB0byB3aWxsIGJlIHNlbmlvciBtYW5hZ2VtZW50IGluIGEgVGVsZWNvIGNvbXBhbnkuIA0KDQoqKldoYXQgZG9lcyBhdWRpZW5jZSBjYXJlIGFib3V0PzoqKiBUaGUgYXVkaWVuY2Ugd2lsbCB3YW50IHRvIHVuZGVyc3RhbmQgd2hhdCBpcyBjYXVzaW5nIGN1c3RvbWVycyB0byBsZWF2ZSB0aGVpciBjb21wYW55LiBUZWxlY28gY29tcGFueXMgYXJlIHF1aXRlIGNvbXBldGl0aXZlIHRvZGF5IHdpdGggZWFjaG90aGVyIGFuZCB3aWxsIHdhbnQgdG8gcmV0YWluIGFzIG1hbnkgY3VzdG9tZXJzIGFzIHBvc3NpYmxlLiBUaGV5IHdpbGwgd2FudCB0byBrbm93IGlmIHRoZXJlIGlzIGFueXRoaW5nIHRoZXkgY2FuIGltcHJvdmUgd2l0aCB0aGVpciBidXNpbmVzcyBtb2RlbCB0byByZXRhaW4gbW9yZSBjdXN0b21lcnMuIElzIHRoZXJlIHNvbWV0aGluZyB0aGF0IGN1c3RvbWVycyBhcmUgbm90IHNhdGlzZmllZCB3aXRoPyBJbiBvcmRlciB0byBiZXR0ZXIgdW5kZXJzdGFuZCBjdXN0b21lciBjaHVybiB0aGV5IG5lZWQgdG8gdW5kZXJzdGFuZCB0aGVpciBjdXN0b21lciBiZWhhdmlvdXIgYW5kIHRoZSB0cmlnZ2VycyB0aGF0IGFyZSBjYXVzaW5nIGN1c3RvbWVycyB0byBsZWF2ZS4NCg0KKipXaGF0IGFjdGlvbiBkb2VzIGF1ZGllbmNlIG5lZWQgdG8gdGFrZT86KiogT25jZSB0aGUgaW5zaWdodHMgaGF2ZSBiZWVuIHByb3ZpZGVkIHRvIHNlbmlvciBtYW5hZ2VtZW50IHRoZXkgd2lsbCBuZWVkIHRvIGFzc2VzcyB3aGV0aGVyIHRoZXkgbmVlZCB0byBpbXByb3ZlIG9yIHJlZmluZSB0aGVpciBidXNpbmVzcyBtb2RlbCBmb3IgY3VzdG9tZXJzIHRvIG1ha2UgaXQgbW9yZSBhcHBlYWxpbmcgYW5kIHRvIGF2b2lkIGN1c3RvbWVycyBsZWF2aW5nLiBUaGV5IHdpbGwgbmVlZCB0byBjb251ZGN0IGFuIGFzc2Vzc21lbnQgdG8gZW5zdXJlIHRoZSBzZXJ2aWNlcyB0aGV5IG9mZmVyIGFyZSB1cCB0byBkYXRlIHdpdGggaW5kdXN0cnkgc3RhbmRhcmRzIGFuZCBjdXJyZW50IHRyZW5kcyBpbiB0aGUgbWFya2V0LiBUaGV5IG1heSBuZWVkIHRvIGltcHJvdmUgdGhlaXIgc3RyZWFtaW5nIHNlcnZpY2UgZm9yIGV4YW1wbGUgYXMgaXQgbWF5IG5vdCBiZSB1cCB0byBpbmR1c3RyeSBzdGFuZGFyZCBvciBwZXJoYXBzIHRoZWlyIHByaWNpbmcgbW9kZWwgaXMgYmVpbmcgdW5kZXJtaW5lZCBieSBhIGNvbXBldGl0b3IuIA0KDQoqKkJlbmVmaXRzOioqIFRoZSBiZW5lZml0cyBvZiBwcm92aWRpbmcgdGhlIGluc2lnaHRzIHRvIHNlbmlvciBtYW5hZ2VtZW50IGlzIHRoYXQgdGhleSB3aWxsIGJlIGFibGUgdG8gaWRlbnRpZnkga2V5IHJlYXNvbnMgd2h5IGN1c3RvbWVycyBhcmUgbGVhdmluZyBhbmQgbm90IGJlaW5nIHJldGFpbmVkLiBUaGlzIHdpbGwgYWxsb3cgc2VuaW9yIG1hbmFnZW1lbnQgdG8gYWN0IGFuZCBtYWtlIGNoYW5nZXMgbmVjZXNzYXJ5IGluIG9yZGVyIHRvIGF2b2lkIGxvc2luZyBjdXN0b21lcnMgYW5kIHJldGFpbiBhcyBtdWNoIGFzIHBvc3NpYmxlLiBIYXZpbmcgYSBzdGFibGUgYW5kIGxhcmdlIGN1c3RvbWVyIGJhc2UgZ2l2ZXMgYSBjb21wYW55IGEgZ29vZCBpbWFnZSBhbW9uZyB0aGUgcHVibGljLiBUaGlzIGluIHR1cm4gbWF5IGFsc28gaW5jcmVhc2UgcmVmZXJyYWxzIGZyb20gZXhpc3RpbmcgY3VzdG9tZXJzIGFuZCB3aWxsIGFsc28gZW5zdXJlIHRoYXQgdGhlIGNvbXBhbnlzIHByb2ZpdCBsZXZlbCByZW1haW5zIHN0YWJsZS4NCg0KDQoqKlJpc2tzOioqIFRoZSByaXNrcyBvZiBub3QgYWN0aW5nIG9uIHRoZSBpbnNpZ2h0cyBwcm92aWRlZCB3b3VsZCBiZSB0aGF0IHRoZXkgbWF5IGxvc2UgYSBsYXJnZSBhbW91bnQgb2YgY3VzdG9tZXJzLiBUaGVpciBzZXJ2aWNlcyBtYXkgYWxzbyBmYWxsIGZhciBiZWhpbmQgY3VycmVudCBpbmR1c3RyeSBzdGFuZGFyZHMgYW5kIHRoZXkgd2lsbCBub3QgaGF2ZSBhbnkgaW5zaWdodHMgaW50byB3aGF0IGlzIHJlcXVpcmVkIHRvIHJldGFpbiB0aGVpciBjdXN0b21lciBiYXNlIGFuZCBrZWVwIHRoZWlyIGN1c3RvbWVyIGJhc2Ugc2F0aXNmaWVkIHdpdGggdGhlaXIgc2VydmljZS4gSWYgdGhlIGNvbXBhbnkgY29udGludWVzIHRvIGxvc2UgY3VzdG9tZXJzIHRoZXkgd2lsbCBpbiB0dXJuIGxvc2UgcHJvZml0LiBUaGV5IGFsc28gcmlzayBnZXR0aW5nIGEgYmFkIHJlcHV0YXRpb24gYW1vbmcgdGhlIHB1YmxpYyBkdWUgdG8gd29yZCBvZiBtb3V0aCBmcm9tIGN1c3RvbWVycyB0aGF0IGFyZSBsZWF2aW5nLg0KDQpPdmVyYWxsLCB0aGUgYWltIG9mIHRoaXMgcmVwb3J0IGlzIHRvIHByb3ZpZGUga2V5IGluc2lnaHRzIGludG8gd2hhdCBpcyBjYXVzaW5nIGEgbGFjayBvZiBjdXN0b21lciByZXRlbnRpb24gaW4gYSB0ZWxlY28gY29tcGFueS4NCg0KIyAzLiBEYXRhIEV4cGxvcmF0aW9uIGFuZCB3cmFuZ2xpbmcNCkluIHRoZSBiZWxvdyBzZWN0aW9uIEkgaGF2ZSBjb25kdWN0ZWQgc29tZSBkYXRhIGV4cGxvcmF0aW9uIGFuZCBjcmVhdGVkIHNvbWUgc2ltcGxlIHBsb3RzIG9mIHZhcmlhYmxlcyBvZiBpbnRlcmVzdC4gSSBoYXZlIGNyZWF0ZWQgYmFyIGdyYXBocyBsb29raW5nIGF0IGNodXJuLCBnZW5kZXIsIGNvbnRyYWN0IHR5cGUsIHBob25lIHNlcnZpY2UsIGludGVybmV0IHNlcnZpY2UsIG1vdmllIHN0cmVhbWluZyBhbmQgdHYgc3RyZWFtaW5nLkkgIGhhdmUgYWxzbyB3cmFuZ2xlZCB0aGUgZGF0YSBhbmQgY3JlYXRlZCBzb21lIGRhdGEgZnJhbWVzIHdoaWNoIEkgdXNlZCB0byBsb29rIGF0IGNlcnRhaW4gcG9zc2libGUgdHJlbmRzIGFuZCBzb21lIHRoYXQgSSB3aWxsIHVzZSBsYXRlciBpbiBteSB2aXN1YWxpc2F0aW9ucy4gRm9yIGV4YW1wbGUgSSBoYXZlIGxvb2tlZCBhdCB0aGUgY291bnQgb2YgaG93IG1hbnkgY3VzdG9tZXJzIGRpZCBvciBkaWQgbm90IGNodXJuIGdyb3VwZWQgYnkgdGhlIGNvbnRyYWN0IHR5cGUgd2hpY2ggd2lsbCBiZSB1c2VkIGluIHZpc3VhbGlzYXRpb24gMS4gDQoNCmBgYHtyIGRhdGFkZXNjMX0NCiNEYXRhIEV4cGxvcmF0aW9uIGFuZCB3cmFuZ2xpbmcgDQoNCiNMb29rIGF0IENodXJuIENvdW50DQpDaHVybl9jb3VudCA8LSBzcWxkZignU2VsZWN0IGNvdW50KGNodXJuKSBhcyBjb3VudCwgY2h1cm4gZnJvbSB0ZWxlY28gZ3JvdXAgYnkgY2h1cm4nKQ0KVmlldyhDaHVybl9jb3VudCkNCg0KZ2dwbG90KGRhdGE9Q2h1cm5fY291bnQsIGFlcyh4PUNodXJuLCB5PWNvdW50LCBmaWxsID0gQ2h1cm4pKSArIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikNCg0KDQojTG9vayBhdCBnZW5kZXIgZGlzdHJpYnV0aW9uDQpHZW5kZXIgPC0gc3FsZGYoJ1NlbGVjdCBjb3VudChnZW5kZXIpIGFzIGNvdW50LCBnZW5kZXIgZnJvbSB0ZWxlY28gZ3JvdXAgYnkgZ2VuZGVyJykNClZpZXcoR2VuZGVyKQ0KDQpnZ3Bsb3QoZGF0YT1HZW5kZXIsIGFlcyh4PWdlbmRlciwgeT1jb3VudCwgZmlsbCA9IGdlbmRlcikpICsgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKQ0KDQoNCiNMb29rIGF0IENvbnRyYWN0IFR5cGUNCkNvbnRyYWN0X2NvdW50IDwtIHNxbGRmKCdTZWxlY3QgY291bnQoQ29udHJhY3QpIGFzIGNvdW50LCBDb250cmFjdCBmcm9tIHRlbGVjbyBncm91cCBieSBDb250cmFjdCcpDQpWaWV3KENvbnRyYWN0X2NvdW50KQ0KDQpnZ3Bsb3QoZGF0YT1Db250cmFjdF9jb3VudCwgYWVzKHg9Q29udHJhY3QsIHk9Y291bnQsIGZpbGwgPSBDb250cmFjdCkpICsgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKQ0KDQoNCiNMb29rIGF0IFBob25lIFNlcnZpY2UNClBob25lX3NlcnZpY2UgPC0gc3FsZGYoJ1NlbGVjdCBjb3VudChQaG9uZVNlcnZpY2UpIGFzIGNvdW50LCBQaG9uZVNlcnZpY2UgZnJvbSB0ZWxlY28gZ3JvdXAgYnkgUGhvbmVTZXJ2aWNlJykNClZpZXcoUGhvbmVfc2VydmljZSkNCg0KZ2dwbG90KGRhdGE9UGhvbmVfc2VydmljZSwgYWVzKHg9UGhvbmVTZXJ2aWNlLCB5PWNvdW50LCBmaWxsID0gUGhvbmVTZXJ2aWNlKSkgKyBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpDQoNCiNMb29rIGF0IEludGVybmV0IFNlcnZpY2UNCkludGVybmV0X3NlcnZpY2UgPC0gc3FsZGYoJ1NlbGVjdCBjb3VudChJbnRlcm5ldFNlcnZpY2UpIGFzIGNvdW50LCBJbnRlcm5ldFNlcnZpY2UgZnJvbSB0ZWxlY28gZ3JvdXAgYnkgSW50ZXJuZXRTZXJ2aWNlJykNClZpZXcoSW50ZXJuZXRfc2VydmljZSkNCg0KZ2dwbG90KGRhdGE9SW50ZXJuZXRfc2VydmljZSwgYWVzKHg9SW50ZXJuZXRTZXJ2aWNlLCB5PWNvdW50LCBmaWxsID0gSW50ZXJuZXRTZXJ2aWNlKSkgKyBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpDQoNCg0KI0xvb2sgYXQgTW92aWUgU2VydmljZQ0KTW92aWVfc2VydmljZSA8LSBzcWxkZignU2VsZWN0IGNvdW50KFN0cmVhbWluZ01vdmllcykgYXMgY291bnQsIFN0cmVhbWluZ01vdmllcyBmcm9tIHRlbGVjbyBncm91cCBieSBTdHJlYW1pbmdNb3ZpZXMnKQ0KVmlldyhNb3ZpZV9zZXJ2aWNlKQ0KDQpnZ3Bsb3QoZGF0YT1Nb3ZpZV9zZXJ2aWNlLCBhZXMoeD1TdHJlYW1pbmdNb3ZpZXMsIHk9Y291bnQsIGZpbGwgPSBTdHJlYW1pbmdNb3ZpZXMpKSArIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikNCg0KDQojTG9vayBhdCBUViBTZXJ2aWNlDQpUVl9zZXJ2aWNlIDwtIHNxbGRmKCdTZWxlY3QgY291bnQoU3RyZWFtaW5nVFYpIGFzIGNvdW50LCBTdHJlYW1pbmdUViBmcm9tIHRlbGVjbyBncm91cCBieSBTdHJlYW1pbmdUVicpDQpWaWV3KFRWX3NlcnZpY2UpDQoNCmdncGxvdChkYXRhPVRWX3NlcnZpY2UsIGFlcyh4PVN0cmVhbWluZ1RWLCB5PWNvdW50LCBmaWxsID0gU3RyZWFtaW5nVFYpKSArIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikNCg0KDQoNCiNMb29rIGF0IENodXJuIGZvciBDb250cmFjdA0KQ2h1cm5fY29udHJhY3QgPC0gc3FsZGYoJ1NlbGVjdCBjb3VudChjaHVybikgYXMgY291bnQsIGNodXJuLCBjb250cmFjdCBmcm9tIHRlbGVjbyAgZ3JvdXAgYnkgY29udHJhY3QsIGNodXJuIG9yZGVyIGJ5IGNodXJuLCBjb3VudCBkZXNjJykNClZpZXcoQ2h1cm5fY29udHJhY3QpDQoNCmRmIDwtIHN1YnNldChDaHVybl9jb250cmFjdCwgQ2h1cm5fY29udHJhY3QkQ2h1cm4gPT0gJ1llcycpDQpWaWV3KGRmKQ0KDQojTG9vayBhdCBNb250aGx5IENoYXJnZXMNCkNoYXJnZXMgPC0gc3FsZGYoJ1NlbGVjdCBjb3VudChjaHVybikgYXMgY291bnQsIE1vbnRobHlDaGFyZ2VzLCBjaHVybiBmcm9tIHRlbGVjbyBncm91cCBieSBNb250aGx5Q2hhcmdlcyxjaHVybiBvcmRlciBieSBjaHVybiwgY291bnQgZGVzYycpDQpWaWV3KENoYXJnZXMpDQoNCg0KI0xvb2sgYXQgUGF5bWVudCBNZXRob2QNClBheW1lbnQgPC0gc3FsZGYoJ1NlbGVjdCBjb3VudChjaHVybikgYXMgY291bnQsIGNodXJuLCBQYXltZW50TWV0aG9kIGZyb20gdGVsZWNvICBncm91cCBieSBQYXltZW50TWV0aG9kLCBjaHVybiBvcmRlciBieSBjaHVybiwgY291bnQgZGVzYycpDQpWaWV3KFBheW1lbnQpDQoNCiNMb29rIGF0IEludGVybmV0IFNlcnZpY2UNCkludGVybmV0IDwtIHNxbGRmKCdTZWxlY3QgY291bnQoY2h1cm4pIGFzIGNvdW50LCBjaHVybiwgSW50ZXJuZXRTZXJ2aWNlIGZyb20gdGVsZWNvIGdyb3VwIGJ5IEludGVybmV0U2VydmljZSwgY2h1cm4gb3JkZXIgYnkgY2h1cm4sIGNvdW50IGRlc2MnKQ0KVmlldyhJbnRlcm5ldCkNCg0KI0xvb2sgYXQgVGVudXJlDQpUZW51cmUgPC0gc3FsZGYoJ1NlbGVjdCBjb3VudChjaHVybikgYXMgY291bnQsIFRlbnVyZSwgY2h1cm4gZnJvbSB0ZWxlY28gZ3JvdXAgYnkgVGVudXJlLGNodXJuIG9yZGVyIGJ5IGNodXJuLCBjb3VudCBkZXNjJykNClZpZXcoQ2hhcmdlcykNCg0KI0xvb2sgYXQgU3RyZWFtaW5nIFRWDQpUdiA8LSBzcWxkZignU2VsZWN0IGNvdW50KGNodXJuKSBhcyBjb3VudCwgU3RyZWFtaW5nVHYsIGNodXJuIGZyb20gdGVsZWNvIGdyb3VwIGJ5IFN0cmVhbWluZ1R2LGNodXJuIG9yZGVyIGJ5IGNodXJuLCBjb3VudCBkZXNjJykNClZpZXcoVHYpDQoNCiNMb29rIGF0IFN0cmVhbWluZyBNb3ZpZXMNCk1vdmllcyA8LSBzcWxkZignU2VsZWN0IGNvdW50KGNodXJuKSBhcyBjb3VudCwgU3RyZWFtaW5nTW92aWVzLCBjaHVybiBmcm9tIHRlbGVjbyBncm91cCBieSBTdHJlYW1pbmdNb3ZpZXMsY2h1cm4gb3JkZXIgYnkgY2h1cm4sY291bnQgZGVzYycpDQpWaWV3KE1vdmllcykNCg0KI0xvb2sgYXQgZ2VuZGVyIGRpc3RyaWJ1dGlvbg0KR2VuZGVyIDwtIHNxbGRmKCdTZWxlY3QgY291bnQoZ2VuZGVyKSBhcyBjb3VudCwgZ2VuZGVyIGZyb20gdGVsZWNvIGdyb3VwIGJ5IGdlbmRlcicpDQpWaWV3KEdlbmRlcikNCg0KR2VuZGVyX2NodXJuIDwtIHNxbGRmKCdTZWxlY3QgY291bnQoY2h1cm4pIGFzIGNvdW50LCBnZW5kZXIsIGNodXJuIGZyb20gdGVsZWNvIGdyb3VwIGJ5IGdlbmRlciwgY2h1cm4nKQ0KVmlldyhHZW5kZXJfY2h1cm4pDQoNCmBgYA0KDQojIDMuIFZpc3VhbGlzYXRpb25zDQoNCiMjIDMuMSBWaXN1YWxpc2F0aW9uIDE6IFJlbGF0aW9uc2hpcCBiZXR3ZWVuIGNvbnRyYWN0IHR5cGUgYW5kIGN1c3RvbWVyIGNodXJuDQpGcm9tIHRoZSBkYXRhIGV4cGxvcmF0aW9uIHRoZWlyIGFwcGVhcnMgdG8gYmUgYSByZWxhdGlvbnNoaXAgYmV0d2VlbiBjb250cmFjdCB0eXBlIGFuZCBjdXN0b21lciByZXRlbnRpb24uIEN1c3RvbWVycyB3aG8gYXJlIG9uIGEgbW9udGhseSBjb250cmFjdCBhcmUgbGVhdmluZyBtb3JlIHRoYW4gY3VzdG9tZXJzIG9uIGEgb25lIHllYXIgb3IgdHdvIHllYXIgY29udHJhY3QuICBUaGVyZWZvcmUgdGhpcyBpcyBhbiBpbmRpY2F0b3IgdG8gbWUgdGhhdCB0aGVyZSBpcyBwZXJoYXBzIGEgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIGNvc3Qgb2YgbW9udGhseSBmZWVzIGFuZCBjdXN0b21lciByZXRlbnRpb24uIFRoaXMgaXMgc29tZXRoaW5nIHRoYXQgd2lsbCBiZSBpbnZlc3RpZ2F0ZWQgd2l0aCBhIHZpc3VhbGl6YXRpb24uIFRoZSBmaXJzdCB2aXN1YWxpc2F0aW9uIHRoYXQgd2lsbCBiZSBwcmVzZW50ZWQgdG8gc2VuaW9yIG1hbmFnZW1lbnQgd2lsbCBiZSBhIGhpc3RvZ3JhbSBwbG90IHNob3dpbmcgdGhhdCBtb3N0IGN1c3RvbWVycyB3aG8gaGF2ZSBsZWZ0IGhhdmUgYmVlbiBvbiBhIG1vbnRobHkgY29udHJhY3QgcmF0aGVyIHRoYW4gYSBvbmUgeWVhciBvciB0d28geWVhciBjb250cmFjdC4gSW4gbXkgcHJldmlvdXMgaXRlcmF0aW9ucyB3aGljaCBjYW4gYmUgdmlld2VkIGZ1cnRoZXIgb24gaW4gdGhpcyByZXBvcnQgSSBleHBlcmltZW50ZWQgd2l0aCB0aGUgcG9zaXRpb24gb2YgdGhlIGJhciBncmFwaHMuIEkgZGVjaWRlZCB0byB1c2UgYSBiYXIgZ3JhcGggYXMgSSBmb3VuZCB0aGF0IGl0IHdhcyB0aGUgYmVzdCBtZXRob2QgdG8gcG9ydHJheSB0aGlzIGRhdGEuIFRoZSBiYXIgZ3JhcGggZWZmZWN0aXZlbHkgc2hvd3MgdGhlIGxhcmdlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgY29udHJhY3QgdHlwZXMuIEkgaGFkIG9yaWdpbmFsbHkgY3JlYXRlZCBhIHN0YXRpYyBwbG90IGJ1dCBteSBmaW5hbCBpdGVyYXRpb24gSSBkZWNpZGVkIHRvIG1ha2UgdGhlIHBsb3QgaW50ZXJhY3RpdmUuIFRoaXMgd2lsbCBhbGxvdyBzZW5pb3IgbWFuYWdlbWVudCB0byBpbnRlcmFjdCB3aXRoIHRoaXMgdmlzdWFsaXNhdGlvbiBhbmQgZmlsdGVyIHRoZSBkYXRhIGhvd2V2ZXIgdGhleSB3aXNoIHRvIHZpZXcgaXQuDQpgYGB7ciBkYXRhZGVzYzJ9DQojQ29udHJhY3QgUGxvdA0KVmlldyhDaHVybl9jb250cmFjdCkNCg0KQ2h1cm5fY29udHJhY3QyIDwtIG1lbHQoQ2h1cm5fY29udHJhY3QpDQoNClZpZXcgKENodXJuX2NvbnRyYWN0MikNCg0KDQojQ29udHJhY3QgUGxvdCBGaW5hbCBJdGVyYXRpb24gKFN0YXRpYykNCkNvbnRyYWN0X1Bsb3QgPC0gZ2dwbG90KGRhdGE9Q2h1cm5fY29udHJhY3QsIGFlcyh4PUNvbnRyYWN0LCB5PWNvdW50LCBmaWxsID0gQ2h1cm4pKSArIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ICdkb2RnZScpICsgeWxhYignQ2h1cm4gQ291bnQnKSArIGdndGl0bGUoJ1JlbGF0aW9uc2hpcCBCZXR3ZWVuIEN1c3RvbWVyIENodXJuIGFuZCBDb250cmFjdCBUeXBlJykNCg0KDQojQ29udHJhY3QgUGxvdCBGaW5hbCBJdGVyYXRpb24gKEludGVyYWN0aXZlKQ0KZ2dwbG90bHkoQ29udHJhY3RfUGxvdCkgJT4lDQogIGxheW91dCgNCiAgICB0aXRsZSA9ICdSZWxhdGlvbnNoaXAgQmV0d2VlbiBDaHVybiBhbmQgQ29udHJhY3QgVHlwZScNCiAgKQ0KDQoNCmBgYA0KDQoNCiMjIDMuMiBWaXN1YWxpc2F0aW9uIDI6IFJlbGF0aW9uc2hpcCBiZXR3ZWVuIE1vbnRobHkgQ2hhcmdlcyBhbmQgQ3VzdG9tZXIgQ2h1cm4NClNlbmlvciBtYW5hZ2VtZW50IGNhbiBzZWUgZnJvbSB0aGUgdmlzdWFsaXNhdGlvbiAxIHRoYXQgbW9zdCBjdXN0b21lcnMgbGVhdmluZyBhcmUgb24gYSBtb250aGx5IGNvbnRyYWN0LiBUaGUgbmV4dCB2aXN1YWxpc2F0aW9uIHRoYXQgd2lsbCBiZSBwcmVzZW50ZWQgdG8gc2VuaW9yIG1hbmFnZW1lbnQgd2lsbCBiZSBhIGJveHBsb3Qgc2hvd2luZyB0aGF0IGN1c3RvbWVycyB3aG8gYXJlIGxlYXZpbmcgYXJlIGFsc28gcGF5aW5nIGhpZ2hlciBtb250aGx5IGZlZXMuIFRoaXMgY291bGQgYmUgYW4gaW5kaWNhdGlvbiB0byBzZW5pb3IgbWFuYWdlbWVudCB0aGF0IHRoZXkgbmVlZCB0byBhc3Nlc3MgdGhlaXIgcHJpY2luZyBwbGFuLkkgaGFkIGV4cGVyaW1lbnRlZCB3aXRoIGRpZmZlcmVudCBwbG90IHR5cGVzIHN1Y2ggYXMgbGluZSBncmFwaCBvciBzY2F0dGVyIHBsb3Qgd2hpY2ggY2FuIGJlIHZpZXdlZCBpbiBteSBwcmV2aW91cyBpdGVyYXRpb25zIGZ1cnRoZXIgb24gaW4gdGhlIHJlcG9ydC4gSSB1c2VkIGEgYm94cGxvdCBhcyBJIGZvdW5kIHRoYXQgaXQgd2FzIHRoZSBiZXN0IHBsb3QgdG8gY29udmV5IHRoZSBtZXNzYWdlIGFuZCBlZmZlY3RpdmVseSBzaG93cyB0aGUgZGlmZmVyZW5jZSBpbiBtb250aGx5IGZlZXMgYmV0d2VlbiBjdXN0b21lcnMgd2hvIGFyZSBsZWF2aW5nIGFuZCBjdXN0b21lcnMgd2hvIGFyZSBzdGF5aW5nIHdpdGggdGhlIGNvbXBhbnkuIFRoZSBib3ggcGxvdCBpcyBlYXN5IHRvIHJlYWQgYW5kIHVuZGVyc3RhbmQuIEhvd2V2ZXIsIHNlbmlvciBtYW5hZ2VtZW50IHdpbGwgbm93IHdhbnQgdG8gdW5kZXJzdGFuZCB3aHkgdGhlc2UgY3VzdG9tZXJzIGFyZSBwYXlpbmcgaGlnaGVyIG1vbnRobHkgY2hhcmdlcy4gVGhleSB3aWxsIHF1ZXN0aW9uIHdoeSBjdXN0b21lcnMgYXJlIHBheWluZyBzdWNoIGhpZ2hlciBtb250aGx5IGNoYXJnZXMgY29tcGFyZWQgdG8gb3RoZXIgY3VzdG9tZXJzLiBUaGlzIHdpbGwgYmUgZXhwbG9yZWQgaW4gdGhlIG5leHQgdmlzdWFsaXNhdGlvbi4NCmBgYHtyIGRhdGFkZXNjM30NCg0KDQojTW9udGhseSBDaGFyZ2VzIEZpbmFsIEl0ZXJhdGlvbg0KZ2dwbG90KHRlbGVjbywgYWVzKHk9IE1vbnRobHlDaGFyZ2VzLCB4ID0gIkNodXJuIiwgZmlsbCA9IENodXJuKSkgKyBnZW9tX2JveHBsb3QoKSsgIHhsYWIoIiAiKSArZ2d0aXRsZSgnUmVsYXRpb25zaGlwIEJldHdlZW4gTW9udGhseSBDaGFyZ2VzIGFuZCBDdXN0b21lciBDaHVybiAnKQ0KDQoNCmBgYA0KDQojIyAzLjMgVmlzdWFsaXNhdGlvbiAzOiBDb21wYXJpc29uIG9mIE1vbnRobHkgY2hhcmdlcyBmb3IgU2VydmljZXMgT2ZmZXJlZA0KRnJvbSB0aGUgcHJldmlvdXMgdmlzdWFsaXNhdGlvbiBwcmVzZW50ZWQgdG8gc2VuaW9yIG1hbmFnZW1lbnQgd2UgY2FuIGlkZW50aWZ5IHRoYXQgY3VzdG9tZXJzIHdobyBhcmUgbGVhdmluZyBhcmUgcGF5aW5nIGhpZ2ggbW9udGhseSBmZWVzLiBTZW5pb3IgbWFuYWdlbWVudCB3aWxsIHdhc24ndCB0byBxdWVzdGlvbiB3aHkgdGhlc2UgY3VzdG9tZXJzIGFyZSBwYXlpbmcgaGlnaCBtb250aGx5IGZlZXMgc28gdGhlIGFpbSBvZiB0aGUgbmV4dCB2aXN1YWxpc2F0aW9uIHdpbGwgYmUgdG8gbG9vayBhdCBob3cgbXVjaCAgZGlmZmVyZW5jZSB0aGVyZSBpcyBpbiB0aGUgY29zdCBvZiBzZXJ2aWNlcyB0aGV5IHByb3ZpZGUuIEl0IGxvb2tzIHRvIGludmVzdGlnYXRlIHdoZXRoZXIgdGhlcmUgaXMgYSBiaWcgZGlmZmVyZW5jZSBpbiB0aGUgbW9udGhseSBmZWVzIGZvciB0aGUgc2VydmljZXMgb2ZmZXJlZC4gVGhlIHNlcnZpY2VzIHRoYXQgYXJlIGxvb2tlZCBhdCBhbiBjb21wYXJlZCBpbiB0aGlzIHZpc3VhbGlzYXRpb24gYXJlIFRWIHN0cmVhbWluZywgTW92aWUgc3RyZWFtaW5nIGFuIGludGVybmV0IHNlcnZpY2UuIEkgZGVjaWRlZCB0byBjb250aW51ZSB0byB1c2UgYm94IHBsb3QgZGlhZ3JhbXMgYXMgaXQgaXMgZWZmZWN0aXZlIGluIGNvbW11bmljYXRpbmcgd2hldGhlciB0aGVyZSBpcyBhIGhpZ2ggZGlmZmVyZW5jZSBiZXR3ZWVuIG1vbnRobHkgZmVlcyBvZiBjZXJ0YWluIHNlcnZpY2VzLiBJIGFsc28gZGVjaWRlZCB0byBhcnJhbmdlIHRoZSBwbG90cyBjcmVhdGVkIHRvZ2V0aGVyIGluIG9yZGVyIHRvIGNvbXBhcmUgdGhlbS4gRnJvbSB0aGUgdmlzdWFsaXNhdGlvbiBzZW5pb3IgbWFuYWdlbWVudCBjYW4gc2VlIHRoYXQgdGhlcmUgaXMgYSB3aWRlIGdhcCBpbiBtb250aGx5IGZlZXMgZm9yIHRoZSBoaWdoIGVuZCBzZXJ2aWNlcyB0aGV5IHByb3ZpZGUuIEZvciBleGFtcGxlIA0KDQpgYGB7cn0NCiNjb21wYXJlIEludGVybmV0IFR5cGUgYW5kIG1vbnRobHkgY2hhcmdlcw0KDQpJbnRlcm5ldF9DaGFyZ2VzIDwtIHNxbGRmKCdTZWxlY3QgY291bnQoY2h1cm4pIGFzIGNvdW50LCBNb250aGx5Q2hhcmdlcywgY2h1cm4sIEludGVybmV0U2VydmljZSBmcm9tIHRlbGVjbyBncm91cCBieSBNb250aGx5Q2hhcmdlcyxjaHVybiBvcmRlciBieSBjaHVybiwgY291bnQgZGVzYycpDQpWaWV3KEludGVybmV0X0NoYXJnZXMpDQoNCg0KSW50ZXJuZXRfcGxvdCA8LWdncGxvdChJbnRlcm5ldF9DaGFyZ2VzLCBhZXMoeT0gTW9udGhseUNoYXJnZXMsIHg9ICIgIiwgZmlsbCA9IEludGVybmV0U2VydmljZSkpICsgZ2VvbV9ib3hwbG90KCkrICB4bGFiKCIgQ2h1cm4iKSArIHNjYWxlX2ZpbGxfb3JkaW5hbCgpICsgdGhlbWVfbWluaW1hbCgpDQoNCiNjb21wYXJlIFN0cmVhbWluZyBUViBhbmQgbW9udGhseSBjaGFyZ2VzDQoNClRWX0NoYXJnZXMgPC0gc3FsZGYoJ1NlbGVjdCBjb3VudChjaHVybikgYXMgY291bnQsIE1vbnRobHlDaGFyZ2VzLCBjaHVybiwgU3RyZWFtaW5nVFYgZnJvbSB0ZWxlY28gZ3JvdXAgYnkgTW9udGhseUNoYXJnZXMsY2h1cm4gb3JkZXIgYnkgY2h1cm4sIGNvdW50IGRlc2MnKQ0KVmlldyhUVl9DaGFyZ2VzKQ0KDQoNCg0KVHZfcGxvdCA8LSBnZ3Bsb3QoVFZfQ2hhcmdlcywgYWVzKHk9IE1vbnRobHlDaGFyZ2VzLCB4PSAiICIsIGZpbGwgPSBTdHJlYW1pbmdUVikpICsgZ2VvbV9ib3hwbG90KCkrICB4bGFiKCIgQ2h1cm4iKSArIHNjYWxlX2ZpbGxfb3JkaW5hbCgpICsgdGhlbWVfbWluaW1hbCgpDQoNCg0KI2NvbXBhcmUgU3RyZWFtaW5nIE1vdmllcyBhbmQgbW9udGhseSBjaGFyZ2VzDQoNCk1vdmllX0NoYXJnZXMgPC0gc3FsZGYoJ1NlbGVjdCBjb3VudChjaHVybikgYXMgY291bnQsIE1vbnRobHlDaGFyZ2VzLCBjaHVybiwgU3RyZWFtaW5nTW92aWVzIGZyb20gdGVsZWNvIGdyb3VwIGJ5IE1vbnRobHlDaGFyZ2VzLGNodXJuIG9yZGVyIGJ5IGNodXJuLCBjb3VudCBkZXNjJykNClZpZXcoTW92aWVfQ2hhcmdlcykNCg0KDQoNCk1vdmllX3Bsb3QgPC0gZ2dwbG90KE1vdmllX0NoYXJnZXMsIGFlcyh5PSBNb250aGx5Q2hhcmdlcywgeD0gIiAiLCBmaWxsID0gU3RyZWFtaW5nTW92aWVzKSkgKyBnZW9tX2JveHBsb3QoKSsgIHhsYWIoIiBDaHVybiIpICsgc2NhbGVfZmlsbF9vcmRpbmFsKCkgKyB0aGVtZV9taW5pbWFsKCkNCg0KDQojRmluYWwgSXRlcmF0aW9uDQoNCmNoYXJnZXNfY29tcGFyaXNvbiA8LSBnZ2FycmFuZ2UoIFR2X3Bsb3QsIE1vdmllX3Bsb3QsSW50ZXJuZXRfcGxvdCwgbmNvbCA9IDIsIG5yb3cgPSAyKQ0KDQphbm5vdGF0ZV9maWd1cmUoY2hhcmdlc19jb21wYXJpc29uLHRvcCA9IHRleHRfZ3JvYigiQ29tcGFyaW5nIFNlcnZpY2UgTW9udGhseSBDaGFyZ2VzIiwgY29sb3IgPSAiYmxhY2siLCBmYWNlID0gImJvbGQiLCBzaXplID0gMTQpKQ0KDQoNCg0KDQpgYGANCg0KDQojIyAzLjQgVmlzdWFsaXNhdGlvbiA0OiBSZWxhdGlvbnNoaXAgQmV0d2VlbiBDaHVybiBhbmQgVGVudXJlDQpUaGUgZmluYWwgdmlzdWFsaXNhdGlvbiBwcmVzZW50ZWQgdG8gc2VuaW9yIG1hbmFnZW1lbnQgd2lsbCBiZSB0byBsb29rIGF0IGhvdyB0ZW51cmUgaXMgcmVsYXRlZCB0byBjdXN0b21lciBjaHVybi4gVGhlcmUgdGVuZHMgdG8gYmUgYSB0cmVuZCB0aGF0IHRoZSBsb25nZXIgY3VzdG9tZXJzIGFyZSByZXRhaW5lZCB0aGUgbGVzcyBsaWtlbHkgdGhleSBhcmUgdG8gc3RvcCB1c2luZyBhIGNvbXBhbmllcyBzZXJ2aWNlLiBUaGlzIGNhbiBiZSBzZWVuIGluIHRoZSBsaW5lIGdyYXBoIGJlbG93LiBJIGhhdmUgY2hvc2VuIGFuIGFuaW1hdGVkIGxpbmUgZ3JhcGggdG8gcmVwcmVzZW50IHRoZSBkYXRhLiBFYWNoIGxpbmUgcmVwcmVzZW50cyB0aGUgY2h1cm4gdHlwZSAoeWVzIGFuZCBubykuIFRoZSBsaW5lIGdyYXBoIHdhcyBjaG9zZW4gYXMgdGhlcmUgYXJlIHR3byBudW1lcmljIHZhcmlhYmxlcyBiZWluZyByZXByZXNlbnRlZCAoIHRlbnVyZSBhbmQgY291bnQgb2YgY3VzdG9tZXJzKS4gRnJvbSBteSBpdGVyYXRpb25zIHdoaWNoIGNhbiBiZSBzZWVuIGJlbG93IEkgZm91bmQgdGhlIGxpbmUgZ3JhcGggdGhlIGJlc3QgbWV0aG9kIHRvIHJlcHJlc2VudCB0aGlzIGRhdGEuIEZyb20gdGhlIGdyYXBoIGl0IGNhbiBiZSBpZGVudGlmaWVkIHRoYXQgdGhlIGhpZ2hlc3QgbnVtYmVyIG9mIGN1c3RvbWVycyB0aGF0IGNodXJuZWQgd2VyZSBvbmx5IHdpdGggdGhlIGNvbXBhbnkgZm9yIDEwIG1vbnRocy4gQXMgY3VzdG9tZXJzIHRlbnVyZSBpbmNyZWFzZWQgd2l0aCB0aGUgY29tcGFueSB0aGVpciBsaWtlbGlob29kIG9mIGNodXJuaW5nIGRlY3JlYXNlZC4gVGhpcyBzaG91bGQgcHJvbXB0IHNlbmlvciBtYW5hZ2VtZW50IHRvIHB1dCBtb3JlIGZvY3VzIG9uIHRoZSBjdXN0b21lcnMgd2hvIGhhdmUgbW9zdCByZWNlbnRseSBqb2luZWQgd2l0aGluIHRoZSBwYXN0IGZldyBtb250aHMgYW5kIHRvIGZvY3VzIG9uIHJldGFpbmluZyB0aGVzZSBjdXN0b21lcnMuDQpgYGB7cn0NCg0KDQpwIDwtIHRlbGVjbyAlPiUNCiAgZ3JvdXBfYnkodGVudXJlLCBDaHVybikgJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSwgLmdyb3VwcyA9ICdkcm9wJykgJT4lDQogIGdncGxvdChhZXModGVudXJlLCBjb3VudCwgY29sb3IgPSBDaHVybikpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAyKSArIGdndGl0bGUoJ1JlbGF0aW9uc2hpcCBCZXR3ZWVuIFRlbnVyZSBBbmQgQ3VzdG9tZXIgQ2h1cm4nKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KDQpwICsgZ2VvbV9wb2ludCgpICsgdHJhbnNpdGlvbl9yZXZlYWwodGVudXJlKQ0KDQpgYGANCg0KIyA0LlByZXZpb3VzIEl0ZXJhdGlvbnMNCiMjIDQuMSBWaXN1YWxpc2F0aW9uIDEgSXRlcmF0aW9uczogUmVsYXRpb25zaGlwIGJldHdlZW4gY29udHJhY3QgdHlwZSBhbmQgY3VzdG9tZXIgY2h1cm4NCkluIHRoaXMgc2VjdGlvbiBJIHdpbGwgc2hvdyBwcmV2aW91cyBpdGVyYXRpb25zIG9mIHRoZSBwbG90cyBjcmVhdGVkIGZvciB0aGlzIHJlcG9ydC4gRmlyc3RseSBiZWxvdyBzaG93cyB0aGUgcHJldmlvdXMgaXRlcmF0aW9ucyBmb3IgdmlzdWFsaXNhdGlvbiAxLiBJIGV4cGVyaW1lbnRlZCB3aXRoIGZsaXBwaW5nIHRoZSBwb3NpdGlvbiBvZiB0aGUgYmFyIGdyYXBocyBhbmQgYWxzbyBzdGFja2luZyB0aGUgYmFyIGdyYXBocy4gSG93ZXZlciBJIGRlY2lkZWQgdG8gZ3JvdXAgdGhlIGJhciBncmFwaHMgaW4gdGhlIGVuZCBhcyBJIGZvdW5kIHRoYXQgd2hlbiB0aGV5IHdlcmUgc3RhY2tlZCBpdCBtYWRlIGl0IG1vcmUgZGlmZmljdWx0IHRvIGNvbXBhcmUgdGhlIHNpemVzIGZvciBlYWNoIGNodXJuIHR5cGUuIFdoZW4gSSBncm91b2VkIHRoZSBiYXIgcGxvdHMgaXQgcmVtZWRpZWQgdGhpcy4gSSBhbHNvIGhhZCBjcmVhdGVkIGEgc3RhdGljIHBsb3QgdGhhdCBJIGZlbHQgbG9va2VkIHdlbGwgYnV0IGluIG9yZGVyIHRvIGFkZCBhIGJpdCBtb3JlIGludGVyYWN0aXZpdHkgSSBkZWNpZGVkIHRvIG1ha2UgdGhlIGZpbmFsIHBsb3QgYW4gaW50ZXJhY3RpdmUgcGxvdC4NCmBgYHtyfQ0KDQojQ29udHJhY3QgUGxvdCBJdGVyYXRpb24gMQ0KZ2dwbG90KGRhdGE9Q2h1cm5fY29udHJhY3QsIGFlcyh4PWNvdW50LCB5PUNvbnRyYWN0LCBmaWxsID0gQ2h1cm4pKSArIGdlb21faGlzdG9ncmFtKHN0YXQ9ImlkZW50aXR5IikgDQoNCiNDb250cmFjdCBQbG90IEl0ZXJhdGlvbiAyDQpnZ3Bsb3QoZGF0YT1DaHVybl9jb250cmFjdCwgYWVzKHg9Y291bnQsIHk9Q29udHJhY3QsIGZpbGwgPSBDaHVybikpICsgZ2VvbV9oaXN0b2dyYW0oc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICdkb2RnZScpIA0KDQojQ29udHJhY3QgUGxvdCBJdGVyYXRpb24gMyAoU3RhdGljKQ0KZ2dwbG90KGRhdGE9Q2h1cm5fY29udHJhY3QsIGFlcyh4PUNvbnRyYWN0LCB5PWNvdW50LCBmaWxsID0gQ2h1cm4pKSArIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ICdkb2RnZScpICsgeWxhYignQ2h1cm4gQ291bnQnKSArIGdndGl0bGUoJ1JlbGF0aW9uc2hpcCBCZXR3ZWVuIEN1c3RvbWVyIENodXJuIGFuZCBDb250cmFjdCBUeXBlJykNCg0KDQpgYGANCg0KIyMgNC4yIFZpc3VhbGlzYXRpb24gMiBJdGVyYXRpb25zOiBSZWxhdGlvbnNoaXAgYmV0d2VlbiBNb250aGx5IENoYXJnZXMgYW5kIEN1c3RvbWVyIENodXJuDQpQcmV2aW91cyBJdGVyYXRpb25zIGZvciBWaXN1YWxpc2F0aW9uIDIgY2FuIGJlIHNlZW4gYmVsb3cuIEkgZXhwZXJpbWVudGVkIHdpdGggZGlmZmVyZW50IHBsb3QgdHlwZXMgc3VjaCBhcyBsaW5lIGdyYXBoIG9yIGEgc2NhdHRlciBwbG90IGFuZCBmcmVxdWVuY3kgcG9seWdvbiBncmFwaC4gSG93ZXZlciwgSSBmZWx0IHRoZXNlIHBsb3RzIGRpZCBub3QgcmVwcmVzZW50IHRoZSBkYXRhIGluIGEgY2xlYW4gbWFubmVyIGFuZCB0aGUgYm94IHBsb3Qgd2FzIG11Y2ggYmV0dGVyIGF0IGNvbnZleWluZyB0aGUgbWVzc2FnZSB0byBzdGFrZWhvbGRlcnMuIEkgYWxzbyBjaG9zZSB0aGUgYm94IHBsb3QgYXMgSSB3YW50ZWQgdG8gaGF2ZSBhIGRpdmVyc2UgcmFuZ2Ugb2YgcGxvdHMgaW4gdGhlIHJlcG9ydC4gVGhlIGJveCBwbG90IGFsbG93cyBzZW5pb3IgbWFuYWdlbWVudCB0byBxdWlja2x5IGlkZW50aWZ5IG1lYW4gdmFsdWVzIGFuZCB0aGUgZGlmZmVyZW5jZSBpbiBtb250aGx5IGNoYXJnZXMgYmVpbmcgcGFpZCBieSBjdXN0b21lcnMgd2hvIGFyZSBsZWF2aW5nIHRoZSBjb21wYW55Lg0KDQoNCg0KYGBge3J9DQojTW9udGhseSBDaGFyZ2VzIFBsb3QgSXRlcmF0aW9uIDENClZpZXcoQ2hhcmdlcykgDQoNCmdncGxvdChDaGFyZ2VzLGFlcyh4ID0gTW9udGhseUNoYXJnZXMsIHk9Y291bnQsIGNvbG9yID0gQ2h1cm4pKSsgZ2VvbV9saW5lKHNpemU9MikgKyB5bGFiKCdDaHVybiBDb3VudCcpICtnZ3RpdGxlKCdSZWxhdGlvbnNoaXAgQmV0d2VlbiBNb250aGx5IENoYXJnZXMgYW5kIEN1c3RvbWVyIENodXJuJykNCg0KI01vbnRobHkgQ2hhcmdlcyBJdGVyYXRpb24gMg0KZ2dwbG90KENoYXJnZXMsYWVzKHggPSBNb250aGx5Q2hhcmdlcywgeT1jb3VudCwgY29sb3IgPSBDaHVybikpKyBnZW9tX3BvaW50KCkgKyB5bGFiKCdDaHVybiBDb3VudCcpICtnZ3RpdGxlKCdSZWxhdGlvbnNoaXAgQmV0d2VlbiBNb250aGx5IENoYXJnZXMgYW5kIEN1c3RvbWVyIENodXJuJykNCg0KI01vbnRobHkgQ2hhcmdlcyBJdGVyYXRpb24gMw0KZ2dwbG90KENoYXJnZXMsYWVzKHggPSBNb250aGx5Q2hhcmdlcywgY29sb3IgPSBDaHVybikpKyBnZW9tX2ZyZXFwb2x5KHNpemU9MikgKyB5bGFiKCdDaHVybiBDb3VudCcpICtnZ3RpdGxlKCdSZWxhdGlvbnNoaXAgQmV0d2VlbiBNb250aGx5IENoYXJnZXMgYW5kIEN1c3RvbWVyIENodXJuJykNCg0KDQpgYGANCg0KIyMgNC4zIFZpc3VhbGlzYXRpb24gMyBJdGVyYXRpb25zOiBDb21wYXJpc29uIG9mIE1vbnRobHkgY2hhcmdlcyBmb3IgU2VydmljZXMgT2ZmZXJlZA0KUHJldmlvdXMgSXRlcmF0aW9ucyBmb3IgVmlzdWFsaXNhdGlvbiAzIGNhbiBiZSBzZWVuIGJlbG93LiBJIGhhZCB0byBleHBlcmltZW50IHdpdGggdGhlIG51bWJlciBvZiByb3cgYW5kIGNvbHVtbnMuIEFzIGNhbiBiZSBzZWVuIGJlbG93IGluIG15IHByZXZpb3VzIGl0ZXJhdGlvbnMgdGhlIHBsb3RzIHdlcmUgb3ZlcmxhcHBpbmcgZWFjaG90aGVyIGFuZCBkaWQgbm90IHByZXNlbnQgaW4gYSB0aWR5IG1hbm5lci4gSSBhbHNvIGV4cGVyaW1lbnRlZCB3aXRoIGluY2x1ZGluZyBhIHRpdGxlIGZvciBlYWNoIG9mIHRoZSBwbG90cyBidXQgdGhlc2UgdGl0bGVzIGFsc28gb3ZlcmxhcHBlZCBlYWNob3RoZXIuIEkgZGVjaWRlZCB0byByZW1vdmUgdGhlIHRpdGxlcyBhbmQgaW5jbHVkZSBqdXN0IG9uZSB0aXRsZSBmb3IgdGhlIG92ZXJhbGwgcGxvdCB3aGljaCBtYWRlIGl0IGxvb2sgbmVhdGVyIGFuZCBJIGZlbHQgdGhhdCB0aGUgbGVnZW5kIGZvciBlYWNoIHBsb3QgY29udmV5ZWQgd2hhdCB0aGUgbW9udGhseSBjaGFyZ2VzIHdlcmUgZm9yIGluIGVhY2ggcGxvdC4gDQoNCg0KYGBge3J9DQoNCiNJdGVyYXRpb24gMQ0KZ3JpZC5hcnJhbmdlKEludGVybmV0X3Bsb3QsIFR2X3Bsb3QsIE1vdmllX3Bsb3QsIG5yb3c9Miwgd2lkdGhzID0gYygyLCAxLCAxKSxsYXlvdXRfbWF0cml4ID0gcmJpbmQoYygxLCAyLCBOQSksYygzLCAzLCA0KSkpDQoNCiNJdGVyYXRpb24gMg0KZ3JpZC5hcnJhbmdlKEludGVybmV0X3Bsb3QsIFR2X3Bsb3QsIE1vdmllX3Bsb3QsIG5yb3c9Miwgd2lkdGhzID0gYygyLCAxLCAxKSkNCg0KDQpgYGANCg0KIyMgNC40IFZpc3VhbGlzYXRpb24gNCBJdGVyYXRpb25zOiBSZWxhdGlvbnNoaXAgYmV0d2VlbiBUZW51cmUgYW5kIEN1c3RvbWVyIENodXJuDQpQcmV2aW91cyBJdGVyYXRpb25zIGZvciBWaXN1YWxpc2F0aW9uIDQgY2FuIGJlIHNlZW4gYmVsb3cuIEkgZmlyc3QgZXhwZXJpbWVudGVkIHdpdGggZGlmZmVyZW50IHBvdCB0eXBlcyBzdWNoIGFzIGhpc3RvZ3JhbSBhbmQgc2NhdHRlcnBsb3RzLiBJIGZvdW5kIHRoYXQgdGhlIGZyZXF1ZW5jeSBwb2x5Z29uIHBsb3Qgc2VlbWVkIHRvIGxvb2sgdGhlIG1vc3QgY2xlYW4gYW5kIGNvbnZleSB0aGUgbWVzc2FnZSBtb3N0IGVmZmVjdGl2ZWx5LiBIb3dldmVyLCB3aGVuIEkgYXR0ZW1wdGVkIHRvIGFuaW1hdGUgdGhpcyBwbG90IGl0IGRpZCBub3Qgd29yayB2ZXJ5IGVmZmVjdGl2ZWx5LiBJIGV4cGVyaW1lbnRlZCB3aXRoIHRoZSB0cmFuc2l0aW9uIGZ1bmN0aW9ucyBzdWNoIGFzIHRyYW5zaXRpb25fc3RhdGUgZXRjIGJ1dCBpdCBkaWQgbm90IGFuaW1hdGUgY29ycmVjdGx5LiBUaGVyZWZvcmUgaW5zdGVhZCBvZiB1c2luZyBhIGZyZXF1ZW5jeSBwb2x5Z29uIHBsb3QgSSBzd2l0Y2hlZCB0byBhIGxpbmUgZ3JhcGggd2hpY2ggd2FzIHVzZWQgaW4gbXkgZmluYWwgdmlzdWFsaXNhdGlvbiBhbmQgY291bGQgYmUgYW5pbWF0ZWQgY29ycmVjdGx5Lg0KDQpgYGB7cn0NCg0KI1RlbnVyZSBQbG90IEl0ZXJhdGlvbiAxDQpnZ3Bsb3QoZGF0YT1UZW51cmUsIGFlcyh4PXRlbnVyZSwgeT1jb3VudCwgZmlsbCA9IENodXJuKSkgKyBnZW9tX2hpc3RvZ3JhbShzdGF0PSJpZGVudGl0eSIpIA0KDQoNCiNUZW51cmUgUGxvdCBJdGVyYXRpb24gMg0KZ2dwbG90KFRlbnVyZSxhZXMoeCA9IHRlbnVyZSwgeT1jb3VudCwgY29sb3IgPSBDaHVybikpKyBnZW9tX3BvaW50KCkgKyB5bGFiKCdDaHVybiBDb3VudCcpICtnZ3RpdGxlKCdSZWxhdGlvbnNoaXAgQmV0d2VlbiBNb250aGx5IENoYXJnZXMgYW5kIEN1c3RvbWVyIENodXJuJykNCg0KI1RlbnVyZSBQbG90IEl0ZXJhdGlvbiAzDQpUZW51cmVfSXRlcmF0aW9uNCA8LSBnZ3Bsb3QodGVsZWNvLGFlcyh4ID0gdGVudXJlLCBjb2xvciA9IENodXJuKSkrIGdlb21fZnJlcXBvbHkoc2l6ZT0yKSt0aGVtZV9taW5pbWFsKCkNCg0KVGVudXJlX0l0ZXJhdGlvbjQNCg0KVGVudXJlX0l0ZXJhdGlvbjQgKyB0cmFuc2l0aW9uX3JldmVhbCh0ZW51cmUpDQoNCmBgYA0KDQoNCiMgUmVmZXJlbmNlcw0KDQoxKSBBaG4sIEouIEguLCBIYW4sIFMuIFAuLCAmIExlZSwgWS4gUy4gKDIwMDYpLiBDdXN0b21lciBjaHVybiBhbmFseXNpczogQ2h1cm4gZGV0ZXJtaW5hbnRzIGFuZCBtZWRpYXRpb24gZWZmZWN0cyBvZiBwYXJ0aWFsIGRlZmVjdGlvbiBpbiB0aGUgS29yZWFuIG1vYmlsZSB0ZWxlY29tbXVuaWNhdGlvbnMgc2VydmljZSBpbmR1c3RyeS4gVGVsZWNvbW11bmljYXRpb25zIHBvbGljeSwgMzAoMTAtMTEpLCA1NTItNTY4Lg0KDQoyKSBBbWluLCBBLiwgQW53YXIsIFMuLCBBZG5hbiwgQS4sIE5hd2F6LCBNLiwgQWxhd2ZpLCBLLiwgSHVzc2FpbiwgQS4sICYgSHVhbmcsIEsuICgyMDE3KS4gQ3VzdG9tZXIgY2h1cm4gcHJlZGljdGlvbiBpbiB0aGUgdGVsZWNvbW11bmljYXRpb24gc2VjdG9yIHVzaW5nIGEgcm91Z2ggc2V0IGFwcHJvYWNoLiBOZXVyb2NvbXB1dGluZywgMjM3LCAyNDItMjU0Lg==