1. Introduction

We are using dataset from MakeMyTrip which offers flight tickets for travel, rail and bus tickets, cab service and hotel booking. We are specifically targeting the hotel booking data scrappped from the website for analysis. This was made available to us from Kaggle

We did an analysis which reflects the best rated hotels in cities across various states in India. Also, narrowed down our analysis to look at which hotels are best in terms of customer experience (Food/Hospitality/Facilities/Value_for_Money). We have further explored the data further by making use of Geospatial Analysis.

We built a regression model to check how the hotel_star_rating is impacted by the customer service parameters like “Hospitality”, “Cleanliness”, “Value_For_Money”, “Food”.

2. Packages Required

As for now, we are using the following libraries:

dplyr - Data Manipulation

tidyr - For Manipulation, Cleaning & Processing data

ggplot2 - To plot the graphs

leaflet - For Geo-Spatial analysis

library(dplyr) ## To manipulate data
library(tidyr) ## To clean/tidy data
library(ggplot2)  ## To plot the graphs
library(leaflet)  ## For Geo- Spatial analysis

3. Data Preparation

Original Datasource: Kaggle

This is a pre-crawled dataset, taken as subset of a bigger dataset (more than 615,000 hotels) that was created by extracting data from MakeMyTrip.com, a travel portal in India. The complete dataset is available on DataStock.

We have 52 variables in the original dataset which we reduced to 15 variables.

Data Cleaning:

  1. There were numerous missing values in our dataset which was the major challenge. Initially, we replaced all missing values with “Unknown” except the ones where we need to perform quantitative analysis (Eg: Location Rating, Food, etc.)

  2. We have worked on “traveller_rating” which was in the following format “Location:5.0/5 | Hospitality:5.0/5 | Facilities:5.0/5 | Cleanliness:5.0/5 | Value for Money:5.0/5 | Food:5.0/5” and was not useful. Hence, we segregated this column into the respective attributes on which they were rated.

First, we read our csv file into R.

D1 = read.csv("https://www.dropbox.com/s/pdvfz4lp4df0kil/makemytrip_com-travel_sample.csv?dl=1",stringsAsFactors = FALSE)

Initial review of the data resulted in the removal of few insignificant columns (Ex: country “India” as it is uniform throughout the dataset)

D2 <- D1[, -c(3,4, 5, 6, 8,9,10, 13, 16, 18, 20, 21,22, 25, 26,28, 29,30,31,  33:52)]
head(D2)
NA

We have cleaned and standardized all our columns by filling in “Unknown” or “0” at places where there were no values.

## Replacing all the whitespaces with NA/Unknown or 0's as applicable
D3  <-  D2  %>%
  mutate(
        area = sub("^$", "Unknown", area),
        city = sub("^$", "Unknown", city),
        property_name = sub("^$", "Unknown", property_name),
        room_types = sub("^$", "Unknown", room_types),
        hotel_star_rating = sub("star$", "", hotel_star_rating),
        mmt_location_rating = sub("^Location:", "", mmt_location_rating),
        mmt_tripadvisor_count   =   sub("^$",   "0",    mmt_tripadvisor_count),
        mmt_review_count = sub("^$", "0", mmt_review_count),
        mmt_location_rating = sub("..", "", mmt_location_rating)
        )

We have segregated our “traveller_rating” column into “Location, Hospitality, Facilities, Cleanliness, Value_for_Money, Food”. Thus we have spread out our data into different columns for all the above mentioned attributes for the analysis based on specific customer satisfaction factors.

## Separate the values and spread into columns using functions of "tidyr" 
D4 <- separate(
  D3, traveller_rating,
  into = c("Location", "d1", "Hospitality", "d2",
           "Facilities", "d3", "Cleanliness", "d4",
           "d5", "d6", "Value_for_Money", "d7",
           "Food"),
  sep = " | "
)
## Removing the unnecessary columns
D5 <- D4[, -c(14,16,18,20,21,22, 24)]
## Editing the newly generated columns
D5 <- D5 %>%
  mutate(
        Location = sub("^Location:", "",Location),
        Hospitality = sub("^Hospitality:", "",Hospitality),
        Facilities = sub("^Facilities:", "",Facilities),
        Cleanliness = sub("^Cleanliness:", "",Cleanliness),
        Value_for_Money = sub("^Money:", "",Value_for_Money),
        Food = sub("^Food:", "", Food)
  )
## Removing"/5" from each of the values of the newly generated columns
D6 <- D5 %>%
  mutate(
       Location = sub("/5$", "", Location),
       Hospitality = sub("/5$", "", Hospitality),
       Facilities = sub("/5$", "", Facilities),
       Cleanliness = sub("/5$", "", Cleanliness),
       Value_for_Money = sub("/5$", "", Value_for_Money),
       Food = sub("/5$", "", Food)
  )

For final analysis we carried out data conversion for required columns into numeric using the as.numeric function so that we can further run our analysis seamlessly.

## Conversion using the as.numeric function
D7 <- D6
D7$mmt_review_count = as.numeric(D7$mmt_review_count)
D7$hotel_star_rating =  as.numeric(D7$hotel_star_rating)
D7$mmt_location_rating = as.numeric(D7$mmt_location_rating)
D7$mmt_review_score = as.numeric(D7$mmt_review_score)
D7$mmt_tripadvisor_count = as.numeric(D7$mmt_tripadvisor_count)
D7$Location = as.numeric(D7$Location)
D7$Facilities = as.numeric(D7$Facilities)
D7$Cleanliness = as.numeric(D7$Cleanliness)
D7$Value_for_Money = as.numeric(D7$Value_for_Money)
D7$Food = as.numeric(D7$Food)
D7$Hospitality = as.numeric(D7$Hospitality)

Since we had a lot of missing values we did not go ahead with omitting those rows or observations. The best way to resolve this is to ahead with the mean imputation method where we replace all the missing values with the average rating of the entire column so that our analysis is not impacted.

##Mean Imputation Function 
D7$mmt_location_rating <- ifelse(is.na(D7$mmt_location_rating),
                           mean(D7$mmt_location_rating, na.rm=TRUE), 
                           D7$mmt_location_rating)
D7$mmt_review_score <- ifelse(is.na(D7$mmt_review_score), 
                       mean(D7$mmt_review_score, na.rm=TRUE), 
                       D7$mmt_review_score)
D7$Location <- ifelse(is.na(D7$Location), 
               mean(D7$Location, na.rm=TRUE), 
               D7$Location)
D7$Facilities <- ifelse(is.na(D7$Facilities), 
                mean(D7$Facilities, na.rm=TRUE), 
                D7$Facilities)
D7$Cleanliness <- ifelse(is.na(D7$Cleanliness), 
                  mean(D7$Cleanliness, na.rm=TRUE), 
                  D7$Cleanliness)
D7$Value_for_Money <- ifelse(is.na(D7$Value_for_Money), 
                    mean(D7$Value_for_Money, na.rm=TRUE),
                    D7$Value_for_Money)
D7$Food <- ifelse(is.na(D7$Food), mean(D7$Food, na.rm=TRUE),
                  D7$Food)
D7$Hospitality <- ifelse(is.na(D7$Hospitality), 
                  mean(D7$Hospitality, na.rm=TRUE), 
                  D7$Hospitality)

Here we have the summary information for our cleaned dataset

## Preview of the structure of our cleaned dataset
str(D7)
'data.frame':   20063 obs. of  18 variables:
 $ area                 : chr  "Hardasji Ki Magri" "Near Nai Gaon" "Near Bagore Ki Haveli" "Dabok" ...
 $ city                 : chr  "Udaipur" "Udaipur" "Udaipur" "Udaipur" ...
 $ hotel_star_rating    : num  1 3 2 1 2 1 3 3 4 2 ...
 $ latitude             : chr  "24.581788" "24.544342" "24.579942" "24.622607" ...
 $ longitude            : num  73.7 73.6 73.7 73.9 73.7 ...
 $ mmt_location_rating  : num  4.35 4.35 4.35 4.35 4.35 ...
 $ mmt_review_count     : num  0 0 0 0 0 0 0 0 0 0 ...
 $ mmt_review_score     : num  4.50 4.50 1.05e+14 1.05e+14 3.70 ...
 $ mmt_tripadvisor_count: num  6 34 16 3 139 79 92 176 344 12 ...
 $ property_name        : chr  "Zion Home Stay" "Araliayas Resorts" "Shri Udai Palace" "SNP House Airport Hotel And Restaurant" ...
 $ property_type        : chr  "Hotel" "Hotel" "Hotel" "Hotel" ...
 $ room_types           : chr  "Unknown" "Unknown" "Unknown" "`standard" ...
 $ Location             : num  4.14 4.14 4.14 4.14 4.14 ...
 $ Hospitality          : num  3.87 3.87 3.87 3.87 3.87 ...
 $ Facilities           : num  3.73 3.73 3.73 3.73 3.73 ...
 $ Cleanliness          : num  3.8 3.8 3.8 3.8 3.8 ...
 $ Value_for_Money      : num  3.9 3.9 3.9 3.9 3.9 ...
 $ Food                 : num  3.08 3.08 3.08 3.08 3.08 ...
## Preview of the summary of our dataset
summary(D7)
     area               city           hotel_star_rating   latitude        
 Length:20063       Length:20063       Min.   : 0.000    Length:20063      
 Class :character   Class :character   1st Qu.: 1.000    Class :character  
 Mode  :character   Mode  :character   Median : 1.000    Mode  :character  
                                       Mean   : 1.844                      
                                       3rd Qu.: 3.000                      
                                       Max.   :77.173                      
                                       NA's   :39                          
   longitude      mmt_location_rating mmt_review_count  mmt_review_score   
 Min.   :  0.00   Min.   :0.000       Min.   :   0.00   Min.   :0.000e+00  
 1st Qu.: 72.56   1st Qu.:4.354       1st Qu.:   0.00   1st Qu.:4.000e+00  
 Median : 75.85   Median :4.354       Median :   0.00   Median :1.053e+14  
 Mean   : 59.34   Mean   :4.354       Mean   :  10.15   Mean   :1.053e+14  
 3rd Qu.: 77.62   3rd Qu.:4.354       3rd Qu.:   3.00   3rd Qu.:1.053e+14  
 Max.   :130.00   Max.   :9.000       Max.   :1335.00   Max.   :2.016e+17  
 NA's   :42                           NA's   :9                            
 mmt_tripadvisor_count property_name      property_type       room_types       
 Min.   :0.000e+00     Length:20063       Length:20063       Length:20063      
 1st Qu.:0.000e+00     Class :character   Class :character   Class :character  
 Median :2.000e+00     Mode  :character   Mode  :character   Mode  :character  
 Mean   :1.005e+13                                                             
 3rd Qu.:3.000e+01                                                             
 Max.   :2.015e+17                                                             
 NA's   :8                                                                     
    Location      Hospitality      Facilities     Cleanliness    Value_for_Money
 Min.   :0.000   Min.   :0.000   Min.   :1.000   Min.   :0.000   Min.   :0.000  
 1st Qu.:4.138   1st Qu.:3.869   1st Qu.:3.728   1st Qu.:3.805   1st Qu.:3.903  
 Median :4.138   Median :3.869   Median :3.728   Median :3.805   Median :3.903  
 Mean   :4.138   Mean   :3.869   Mean   :3.728   Mean   :3.805   Mean   :3.903  
 3rd Qu.:4.138   3rd Qu.:3.869   3rd Qu.:3.728   3rd Qu.:3.805   3rd Qu.:3.903  
 Max.   :5.000   Max.   :5.000   Max.   :5.000   Max.   :5.000   Max.   :5.000  
                                                                                
      Food      
 Min.   :0.000  
 1st Qu.:3.079  
 Median :3.079  
 Mean   :3.079  
 3rd Qu.:3.079  
 Max.   :5.000  
                
## Preview of the first 6 rows
head(D7)
## Preview of the last 6 rows
tail(D7)

4. Data Exploration:

Here we have filtered our data to find the top 5 cities having most number of hotels in our dataset.

#Created a dataframe to store top 5 cities based on the frequency
top5 <- as.data.frame(table(D8$city))
top5 %>% arrange(desc(Freq))%>%head(n=5)

Here, we have incorporated geospatial analysis which assisted us in visualizing the geopoints which attract more customers. Initially, we filtered on hotels having star rating >=3 and later went on to find which out of these had the most number of reviews

D8 <- D7
#Converted latitude from character variable to numeric
D8$latitude <- as.numeric(D8$latitude)
#Created the dataframe to filter out the hotels with rating more than or equal to 3
D9 = D8 %>%
  filter(hotel_star_rating >= 3 ) %>% 
  filter(!is.na(latitude)) %>%
  filter(!is.na(longitude))
#Created the dataframe to filter out the hotels with rating more than or equal to 3 and review count greater than 100
D10 = D8 %>%
filter(hotel_star_rating >= 3 & mmt_review_count>100) %>% 
  filter(!is.na(latitude)) %>%
  filter(!is.na(longitude))
#Calculated the median of latitude and longitude for geo-spatial analysis
longitude_center = median(D9$longitude)
latitude_center = median(D9$latitude)
#
devtools::install_github("rstudio/leaflet")
#Created the colorpallete for different ratings
colorpal <- colorFactor(topo.colors(6,alpha = 1), 
                       D9$hotel_star_rating)
#Geo-spatial analysis only for hotel_star_rating
leaflet(D9) %>% addTiles() %>%
  addCircles(lng = ~longitude, lat = ~latitude, 
             color = ~colorpal(hotel_star_rating))  %>%
  setView(lng=longitude_center, lat=latitude_center,zoom = 5) %>%
  addLegend("bottomright", pal = colorpal, values = ~hotel_star_rating,
            title = "MMT Hotels start rating across India",
            opacity = 1)

#Geo-spatial analysis only for hotel_star_rating and review counts
leaflet(D10) %>% addTiles() %>%
  addCircles(lng = ~longitude, lat = ~latitude, 
             color = ~colorpal(hotel_star_rating))  %>%
  setView(lng=longitude_center, lat=latitude_center,zoom = 5) %>%
  
  addLegend("bottomright", pal = colorpal, values = ~hotel_star_rating,
            title = "MMT Hotels start rating with atleast 100 reviews on MMT",
            opacity = 1)

5. Analysis/Findings:

Here we summarised the best 10 Cities having the maximum number of hotels

# Created the dataframe to find the cities with the maximum hotel frequency
D8 %>% group_by(city) %>%
  summarise(count = n()) %>%
  arrange(desc(count)) %>%
  head(n = 10) %>%
  ggplot(aes(x = city,y = count,fill=count)) +
  geom_bar(stat = "identity") + coord_flip()

Here we have plotted the top 10 cities with hotels based on the average ratings of Cleanliness,Food,Value For Money respectively

#Plotted the top cities with hotels with best Cleaniliness ratings
D8 %>% 
  group_by(city) %>%
  summarise(avg=mean(Cleanliness)) %>%
  arrange(desc(avg)) %>%
  select(city,avg) %>%
  head(n=10) %>%
  ggplot(aes(x= city,y= avg)) +
  geom_bar(stat = "identity",fill ="#FF3399", colour="black") + coord_flip()

#Plotted the top cities with hotels with best Food ratings
D8 %>%
  group_by(city) %>%
  summarise(avg=mean(Food)) %>%
  arrange(desc(avg)) %>%
  select(city,avg) %>%
  head(n=10) %>%
  ggplot(aes(x=city,y=avg)) +
  geom_bar(stat = "identity",fill="#33CCFF", colour="black") + coord_flip()

##Plotted the top cities with hotels with best Value_for_Money ratings
D8 %>%
  group_by(city) %>%
  summarise(avg=mean(Value_for_Money)) %>%
  arrange(desc(avg)) %>% 
  select(city,avg) %>% head(n=10) %>%
  ggplot(aes(x=city,y=avg)) + 
  geom_bar(stat = "identity",fill="#6600CC", colour="black") + coord_flip()

We performed a multiple regression analysis to understand which of the 5 factors are best correlated or significantly impact the values of hotel star rating.

We found out that the hotel star rating is significantly dependent on the “Hospitality”, “Facilities”, “Cleanliness”, “Value for Money” factors. “Food” and “Location” donot contribute much to the variation in the hotel_star_rating.

## Building our Regression Model 
mmtmodel <- lm(hotel_star_rating ~ Location +
            Hospitality + Facilities + Cleanliness +
            Value_for_Money + Food + mmt_tripadvisor_count,
            data = D8)
summary(mmtmodel)

Call:
lm(formula = hotel_star_rating ~ Location + Hospitality + Facilities + 
    Cleanliness + Value_for_Money + Food + mmt_tripadvisor_count, 
    data = D8)

Residuals:
     Min       1Q   Median       3Q      Max 
-10.3375  -0.6717  -0.6717   0.8441   3.5918 

Coefficients:
                        Estimate Std. Error t value Pr(>|t|)    
(Intercept)            8.347e-01  6.770e-02  12.329  < 2e-16 ***
Location              -2.560e-03  1.701e-02  -0.151   0.8803    
Hospitality            1.098e-01  2.411e-02   4.555 5.27e-06 ***
Facilities             2.024e-01  2.709e-02   7.471 8.26e-14 ***
Cleanliness            8.817e-02  1.994e-02   4.423 9.81e-06 ***
Value_for_Money       -1.950e-01  3.519e-02  -5.540 3.06e-08 ***
Food                   3.043e-02  1.216e-02   2.502   0.0124 *  
mmt_tripadvisor_count  3.050e-03  4.626e-05  65.926  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.9723 on 20008 degrees of freedom
  (47 observations deleted due to missingness)
Multiple R-squared:  0.2069,    Adjusted R-squared:  0.2066 
F-statistic: 745.5 on 7 and 20008 DF,  p-value: < 2.2e-16

6. Summary:

The problem statement of our analysis was centered around understanding how the “Traveller_Rating” which was segregated into “Cleanliness”, “Value_For_Money”, “Food” & “Facilities” varies with the “Hotel Star Rating”. Hence, we looked at the top 10 hotels in the cities based on these 4 factors.

We also ran a regression analysis to understand which of these factors impacts our hotel_star_rating the most. Our results show that the hotel star rating is significantly dependent on the “Hospitality”, “Facilities”, “Cleanliness”, “Value for Money” factors. We looked at the p-value for the regression model we built to arrive at this analysis. “Food” and “Location” donot contribute much to the variation in the hotel_star_rating.

We found out the top 10 cities based on the frequency of hotels in a particular city which gave us the insight of cities attracting maximum travellers.These cities were not present when we drill down to find the top cities on customer satisfaction parameter though they have the customer base but they are lacking behind on the customer satisfaction scale

7. Limitations:

We had 52 columns which had irrelevant information on which analysis was not possible. Hence our first step was removing these and standardizing our dataset so that we could run our analysis seamlessly.

Our dataset had a lot of missing values which were replaced using the Mean Imputation Method inorder to ensure our results are not impacted due to the missing data.

Inorder to build upon this analysis in future we would suggest usage of Shiny package to enable interactive binding between input and output variables.

LS0tDQp0aXRsZTogIioqTWFrZU15VHJpcCBIb3RlbCBBbmFseXNpcyoqIg0KQXV0aG9yOiAiUGFua2h1cmkgU2FjaGFuIEFuZCBNcmluYWwgQ2hhdWRoYXJ5Ig0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCmBgYHtyLCBlY2hvPUZBTFNFfQ0KIyBEZWZpbmUgdmFyaWFibGUgY29udGFpbmluZyB1cmwNCnVybCA8LSAiaHR0cDovL215ZWNhLmluL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE3LzAyL01ha2VteXRyaXAuanBnIg0KYGBgDQohW10oYHIgdXJsYCkNCg0KIyMgMS4gSW50cm9kdWN0aW9uDQoNCldlIGFyZSB1c2luZyBkYXRhc2V0IGZyb20gW01ha2VNeVRyaXBdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL01ha2VNeVRyaXApIHdoaWNoIG9mZmVycyBmbGlnaHQgdGlja2V0cyBmb3IgdHJhdmVsLCByYWlsIGFuZCBidXMgdGlja2V0cywgY2FiIHNlcnZpY2UgYW5kIGhvdGVsIGJvb2tpbmcuIFdlIGFyZSBzcGVjaWZpY2FsbHkgdGFyZ2V0aW5nIHRoZSBob3RlbCBib29raW5nIGRhdGEgc2NyYXBwcGVkIGZyb20gdGhlIHdlYnNpdGUgZm9yIGFuYWx5c2lzLg0KVGhpcyB3YXMgbWFkZSBhdmFpbGFibGUgdG8gdXMgZnJvbSBbS2FnZ2xlXShodHRwczovL3d3dy5rYWdnbGUuY29tL1Byb21wdENsb3VkSFEvaG90ZWxzLW9uLW1ha2VteXRyaXApDQoNCg0KV2UgZGlkIGFuIGFuYWx5c2lzIHdoaWNoIHJlZmxlY3RzIHRoZSBiZXN0IHJhdGVkIGhvdGVscyBpbiBjaXRpZXMgYWNyb3NzIHZhcmlvdXMgc3RhdGVzIGluIEluZGlhLiBBbHNvLCBuYXJyb3dlZCBkb3duIG91ciBhbmFseXNpcyB0byBsb29rIGF0IHdoaWNoIGhvdGVscyBhcmUgYmVzdCBpbiB0ZXJtcyBvZiBjdXN0b21lciBleHBlcmllbmNlIChGb29kL0hvc3BpdGFsaXR5L0ZhY2lsaXRpZXMvVmFsdWVfZm9yX01vbmV5KS4gV2UgaGF2ZSBmdXJ0aGVyIGV4cGxvcmVkIHRoZSBkYXRhIGZ1cnRoZXIgYnkgbWFraW5nIHVzZSBvZiBHZW9zcGF0aWFsIEFuYWx5c2lzLg0KDQpXZSBidWlsdCBhIHJlZ3Jlc3Npb24gbW9kZWwgdG8gY2hlY2sgaG93IHRoZSBob3RlbF9zdGFyX3JhdGluZyBpcyBpbXBhY3RlZCBieSB0aGUgY3VzdG9tZXIgc2VydmljZSBwYXJhbWV0ZXJzIGxpa2UgIkhvc3BpdGFsaXR5IiwgIkNsZWFubGluZXNzIiwgIlZhbHVlX0Zvcl9Nb25leSIsICJGb29kIi4NCg0KDQoNCiMjIDIuIFBhY2thZ2VzIFJlcXVpcmVkDQoNCkFzIGZvciBub3csIHdlIGFyZSB1c2luZyB0aGUgZm9sbG93aW5nIGxpYnJhcmllczoNCg0KKipkcGx5cioqIC0gRGF0YSBNYW5pcHVsYXRpb24NCg0KKip0aWR5cioqIC0gRm9yIE1hbmlwdWxhdGlvbiwgQ2xlYW5pbmcgJiBQcm9jZXNzaW5nIGRhdGENCg0KKipnZ3Bsb3QyKiogLSBUbyBwbG90IHRoZSBncmFwaHMNCg0KKipsZWFmbGV0KiogLSBGb3IgR2VvLVNwYXRpYWwgYW5hbHlzaXMNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KGRwbHlyKSAjIyBUbyBtYW5pcHVsYXRlIGRhdGENCmxpYnJhcnkodGlkeXIpICMjIFRvIGNsZWFuL3RpZHkgZGF0YQ0KbGlicmFyeShnZ3Bsb3QyKSAgIyMgVG8gcGxvdCB0aGUgZ3JhcGhzDQpsaWJyYXJ5KGxlYWZsZXQpICAjIyBGb3IgR2VvLSBTcGF0aWFsIGFuYWx5c2lzDQpgYGANCg0KDQojIyAzLiBEYXRhIFByZXBhcmF0aW9uDQpPcmlnaW5hbCBEYXRhc291cmNlOiBbS2FnZ2xlXShodHRwczovL3d3dy5rYWdnbGUuY29tL1Byb21wdENsb3VkSFEvaG90ZWxzLW9uLW1ha2VteXRyaXApDQoNClRoaXMgaXMgYSBwcmUtY3Jhd2xlZCBkYXRhc2V0LCB0YWtlbiBhcyBzdWJzZXQgb2YgYSBiaWdnZXIgZGF0YXNldCAobW9yZSB0aGFuIDYxNSwwMDAgaG90ZWxzKSB0aGF0IHdhcyBjcmVhdGVkIGJ5IGV4dHJhY3RpbmcgZGF0YSBmcm9tIE1ha2VNeVRyaXAuY29tLCBhIHRyYXZlbCBwb3J0YWwgaW4gSW5kaWEuIFRoZSBjb21wbGV0ZSBkYXRhc2V0IGlzIGF2YWlsYWJsZSBvbiBbRGF0YVN0b2NrXShodHRwczovL3d3dy5wcm9tcHRjbG91ZC5jb20vZGF0YXN0b2NrLWFjY2Vzcy1yZWFkeS10by11c2UtZGF0YXNldHMvP3V0bV9zb3VyY2U9bW0ta2FnZ2xlJnV0bV9tZWRpdW09cmVmZXJyYWwpLg0KDQpXZSBoYXZlIDUyIHZhcmlhYmxlcyBpbiB0aGUgb3JpZ2luYWwgZGF0YXNldCB3aGljaCB3ZSByZWR1Y2VkIHRvIDE1IHZhcmlhYmxlcy4NCg0KDQoqKkRhdGEgQ2xlYW5pbmcqKjogDQoNCjEuIFRoZXJlIHdlcmUgbnVtZXJvdXMgbWlzc2luZyB2YWx1ZXMgaW4gb3VyIGRhdGFzZXQgd2hpY2ggd2FzIHRoZSBtYWpvciBjaGFsbGVuZ2UuIEluaXRpYWxseSwgd2UgcmVwbGFjZWQgYWxsIG1pc3NpbmcgdmFsdWVzIHdpdGggIlVua25vd24iIGV4Y2VwdCB0aGUgb25lcyB3aGVyZSB3ZSBuZWVkIHRvIHBlcmZvcm0gcXVhbnRpdGF0aXZlIGFuYWx5c2lzIChFZzogTG9jYXRpb24gUmF0aW5nLCBGb29kLCBldGMuKQ0KDQoyLiBXZSBoYXZlIHdvcmtlZCBvbiAidHJhdmVsbGVyX3JhdGluZyIgd2hpY2ggd2FzIGluIHRoZSBmb2xsb3dpbmcgZm9ybWF0ICJMb2NhdGlvbjo1LjAvNSB8IEhvc3BpdGFsaXR5OjUuMC81IHwgRmFjaWxpdGllczo1LjAvNSB8IENsZWFubGluZXNzOjUuMC81IHwgVmFsdWUgZm9yIE1vbmV5OjUuMC81IHwgRm9vZDo1LjAvNSIgYW5kIHdhcyBub3QgdXNlZnVsLiBIZW5jZSwgd2Ugc2VncmVnYXRlZCB0aGlzIGNvbHVtbiBpbnRvIHRoZSByZXNwZWN0aXZlIGF0dHJpYnV0ZXMgb24gd2hpY2ggdGhleSB3ZXJlIHJhdGVkLg0KDQpGaXJzdCwgd2UgcmVhZCBvdXIgY3N2IGZpbGUgaW50byBSLg0KDQpgYGB7cn0NCkQxID0gcmVhZC5jc3YoImh0dHBzOi8vd3d3LmRyb3Bib3guY29tL3MvcGR2Zno0bHA0ZGYwa2lsL21ha2VteXRyaXBfY29tLXRyYXZlbF9zYW1wbGUuY3N2P2RsPTEiLHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCg0KYGBgDQoNCkluaXRpYWwgcmV2aWV3IG9mIHRoZSBkYXRhIHJlc3VsdGVkIGluIHRoZSByZW1vdmFsIG9mIGZldyBpbnNpZ25pZmljYW50IGNvbHVtbnMgKEV4OiBjb3VudHJ5ICJJbmRpYSIgYXMgaXQgaXMgdW5pZm9ybSB0aHJvdWdob3V0IHRoZSBkYXRhc2V0KQ0KDQpgYGB7cix3YXJuaW5nPUZBTFNFfQ0KDQpEMiA8LSBEMVssIC1jKDMsNCwgNSwgNiwgOCw5LDEwLCAxMywgMTYsIDE4LCAyMCwgMjEsMjIsIDI1LCAyNiwyOCwgMjksMzAsMzEsICAzMzo1MildDQpoZWFkKEQyKQ0KICAgICAgICAgICAgICANCmBgYA0KDQoNCldlIGhhdmUgY2xlYW5lZCBhbmQgc3RhbmRhcmRpemVkIGFsbCBvdXIgY29sdW1ucyBieSBmaWxsaW5nIGluICJVbmtub3duIiBvciAiMCIgYXQgcGxhY2VzIHdoZXJlIHRoZXJlIHdlcmUgbm8gdmFsdWVzLg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQoNCiMjIFJlcGxhY2luZyBhbGwgdGhlIHdoaXRlc3BhY2VzIHdpdGggTkEvVW5rbm93biBvciAwJ3MgYXMgYXBwbGljYWJsZQ0KRDMJPC0JRDIJJT4lDQogIG11dGF0ZSgNCiAgICAgICAgYXJlYSA9IHN1YigiXiQiLCAiVW5rbm93biIsIGFyZWEpLA0KICAgICAgICBjaXR5ID0gc3ViKCJeJCIsICJVbmtub3duIiwgY2l0eSksDQogICAgICAgIHByb3BlcnR5X25hbWUgPSBzdWIoIl4kIiwgIlVua25vd24iLCBwcm9wZXJ0eV9uYW1lKSwNCiAgICAgICAgcm9vbV90eXBlcyA9IHN1YigiXiQiLCAiVW5rbm93biIsIHJvb21fdHlwZXMpLA0KICAgICAgICBob3RlbF9zdGFyX3JhdGluZyA9IHN1Yigic3RhciQiLCAiIiwgaG90ZWxfc3Rhcl9yYXRpbmcpLA0KICAgICAgICBtbXRfbG9jYXRpb25fcmF0aW5nID0gc3ViKCJeTG9jYXRpb246IiwgIiIsIG1tdF9sb2NhdGlvbl9yYXRpbmcpLA0KICAgICAgICBtbXRfdHJpcGFkdmlzb3JfY291bnQJPQlzdWIoIl4kIiwJIjAiLAltbXRfdHJpcGFkdmlzb3JfY291bnQpLA0KICAgICAgICBtbXRfcmV2aWV3X2NvdW50ID0gc3ViKCJeJCIsICIwIiwgbW10X3Jldmlld19jb3VudCksDQogICAgICAgIG1tdF9sb2NhdGlvbl9yYXRpbmcgPSBzdWIoIi4uIiwgIiIsIG1tdF9sb2NhdGlvbl9yYXRpbmcpDQogICAgICAgICkNCg0KDQpgYGANCg0KDQpXZSBoYXZlIHNlZ3JlZ2F0ZWQgb3VyICJ0cmF2ZWxsZXJfcmF0aW5nIiBjb2x1bW4gaW50byAiTG9jYXRpb24sIEhvc3BpdGFsaXR5LCBGYWNpbGl0aWVzLCBDbGVhbmxpbmVzcywgVmFsdWVfZm9yX01vbmV5LCBGb29kIi4gVGh1cyB3ZSBoYXZlIHNwcmVhZCBvdXQgb3VyIGRhdGEgaW50byBkaWZmZXJlbnQgY29sdW1ucyBmb3IgYWxsIHRoZSBhYm92ZSBtZW50aW9uZWQgYXR0cmlidXRlcyBmb3IgdGhlIGFuYWx5c2lzIGJhc2VkIG9uIHNwZWNpZmljIGN1c3RvbWVyIHNhdGlzZmFjdGlvbiBmYWN0b3JzLg0KDQpgYGB7cix3YXJuaW5nPUZBTFNFfQ0KIyMgU2VwYXJhdGUgdGhlIHZhbHVlcyBhbmQgc3ByZWFkIGludG8gY29sdW1ucyB1c2luZyBmdW5jdGlvbnMgb2YgInRpZHlyIiANCkQ0IDwtIHNlcGFyYXRlKA0KICBEMywgdHJhdmVsbGVyX3JhdGluZywNCiAgaW50byA9IGMoIkxvY2F0aW9uIiwgImQxIiwgIkhvc3BpdGFsaXR5IiwgImQyIiwNCiAgICAgICAgICAgIkZhY2lsaXRpZXMiLCAiZDMiLCAiQ2xlYW5saW5lc3MiLCAiZDQiLA0KICAgICAgICAgICAiZDUiLCAiZDYiLCAiVmFsdWVfZm9yX01vbmV5IiwgImQ3IiwNCiAgICAgICAgICAgIkZvb2QiKSwNCiAgc2VwID0gIiB8ICINCikNCg0KIyMgUmVtb3ZpbmcgdGhlIHVubmVjZXNzYXJ5IGNvbHVtbnMNCkQ1IDwtIEQ0WywgLWMoMTQsMTYsMTgsMjAsMjEsMjIsIDI0KV0NCg0KIyMgRWRpdGluZyB0aGUgbmV3bHkgZ2VuZXJhdGVkIGNvbHVtbnMNCkQ1IDwtIEQ1ICU+JQ0KICBtdXRhdGUoDQogICAgICAgIExvY2F0aW9uID0gc3ViKCJeTG9jYXRpb246IiwgIiIsTG9jYXRpb24pLA0KICAgICAgICBIb3NwaXRhbGl0eSA9IHN1YigiXkhvc3BpdGFsaXR5OiIsICIiLEhvc3BpdGFsaXR5KSwNCiAgICAgICAgRmFjaWxpdGllcyA9IHN1YigiXkZhY2lsaXRpZXM6IiwgIiIsRmFjaWxpdGllcyksDQogICAgICAgIENsZWFubGluZXNzID0gc3ViKCJeQ2xlYW5saW5lc3M6IiwgIiIsQ2xlYW5saW5lc3MpLA0KICAgICAgICBWYWx1ZV9mb3JfTW9uZXkgPSBzdWIoIl5Nb25leToiLCAiIixWYWx1ZV9mb3JfTW9uZXkpLA0KICAgICAgICBGb29kID0gc3ViKCJeRm9vZDoiLCAiIiwgRm9vZCkNCiAgKQ0KDQojIyBSZW1vdmluZyIvNSIgZnJvbSBlYWNoIG9mIHRoZSB2YWx1ZXMgb2YgdGhlIG5ld2x5IGdlbmVyYXRlZCBjb2x1bW5zDQpENiA8LSBENSAlPiUNCiAgbXV0YXRlKA0KICAgICAgIExvY2F0aW9uID0gc3ViKCIvNSQiLCAiIiwgTG9jYXRpb24pLA0KICAgICAgIEhvc3BpdGFsaXR5ID0gc3ViKCIvNSQiLCAiIiwgSG9zcGl0YWxpdHkpLA0KICAgICAgIEZhY2lsaXRpZXMgPSBzdWIoIi81JCIsICIiLCBGYWNpbGl0aWVzKSwNCiAgICAgICBDbGVhbmxpbmVzcyA9IHN1YigiLzUkIiwgIiIsIENsZWFubGluZXNzKSwNCiAgICAgICBWYWx1ZV9mb3JfTW9uZXkgPSBzdWIoIi81JCIsICIiLCBWYWx1ZV9mb3JfTW9uZXkpLA0KICAgICAgIEZvb2QgPSBzdWIoIi81JCIsICIiLCBGb29kKQ0KICApDQoNCg0KYGBgDQoNCkZvciBmaW5hbCBhbmFseXNpcyB3ZSBjYXJyaWVkIG91dCBkYXRhIGNvbnZlcnNpb24gZm9yIHJlcXVpcmVkIGNvbHVtbnMgaW50byBudW1lcmljIHVzaW5nIHRoZSBhcy5udW1lcmljIGZ1bmN0aW9uIHNvIHRoYXQgd2UgY2FuIGZ1cnRoZXIgcnVuIG91ciBhbmFseXNpcyBzZWFtbGVzc2x5Lg0KDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KDQojIyBDb252ZXJzaW9uIHVzaW5nIHRoZSBhcy5udW1lcmljIGZ1bmN0aW9uDQpENyA8LSBENg0KRDckbW10X3Jldmlld19jb3VudCA9IGFzLm51bWVyaWMoRDckbW10X3Jldmlld19jb3VudCkNCkQ3JGhvdGVsX3N0YXJfcmF0aW5nID0gIGFzLm51bWVyaWMoRDckaG90ZWxfc3Rhcl9yYXRpbmcpDQpENyRtbXRfbG9jYXRpb25fcmF0aW5nID0gYXMubnVtZXJpYyhENyRtbXRfbG9jYXRpb25fcmF0aW5nKQ0KRDckbW10X3Jldmlld19zY29yZSA9IGFzLm51bWVyaWMoRDckbW10X3Jldmlld19zY29yZSkNCkQ3JG1tdF90cmlwYWR2aXNvcl9jb3VudCA9IGFzLm51bWVyaWMoRDckbW10X3RyaXBhZHZpc29yX2NvdW50KQ0KRDckTG9jYXRpb24gPSBhcy5udW1lcmljKEQ3JExvY2F0aW9uKQ0KRDckRmFjaWxpdGllcyA9IGFzLm51bWVyaWMoRDckRmFjaWxpdGllcykNCkQ3JENsZWFubGluZXNzID0gYXMubnVtZXJpYyhENyRDbGVhbmxpbmVzcykNCkQ3JFZhbHVlX2Zvcl9Nb25leSA9IGFzLm51bWVyaWMoRDckVmFsdWVfZm9yX01vbmV5KQ0KRDckRm9vZCA9IGFzLm51bWVyaWMoRDckRm9vZCkNCkQ3JEhvc3BpdGFsaXR5ID0gYXMubnVtZXJpYyhENyRIb3NwaXRhbGl0eSkNCg0KYGBgDQoNClNpbmNlIHdlIGhhZCBhIGxvdCBvZiBtaXNzaW5nIHZhbHVlcyB3ZSBkaWQgbm90IGdvIGFoZWFkIHdpdGggb21pdHRpbmcgdGhvc2Ugcm93cyBvciBvYnNlcnZhdGlvbnMuIFRoZSBiZXN0IHdheSB0byByZXNvbHZlIHRoaXMgaXMgdG8gYWhlYWQgd2l0aCB0aGUgbWVhbiBpbXB1dGF0aW9uIG1ldGhvZCB3aGVyZSB3ZSByZXBsYWNlIGFsbCB0aGUgbWlzc2luZyB2YWx1ZXMgd2l0aCB0aGUgYXZlcmFnZSByYXRpbmcgb2YgdGhlIGVudGlyZSBjb2x1bW4gc28gdGhhdCBvdXIgYW5hbHlzaXMgaXMgbm90IGltcGFjdGVkLg0KDQpgYGB7cn0NCiMjTWVhbiBJbXB1dGF0aW9uIEZ1bmN0aW9uIA0KDQoNCkQ3JG1tdF9sb2NhdGlvbl9yYXRpbmcgPC0gaWZlbHNlKGlzLm5hKEQ3JG1tdF9sb2NhdGlvbl9yYXRpbmcpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVhbihENyRtbXRfbG9jYXRpb25fcmF0aW5nLCBuYS5ybT1UUlVFKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBENyRtbXRfbG9jYXRpb25fcmF0aW5nKQ0KDQpENyRtbXRfcmV2aWV3X3Njb3JlIDwtIGlmZWxzZShpcy5uYShENyRtbXRfcmV2aWV3X3Njb3JlKSwgDQogICAgICAgICAgICAgICAgICAgICAgIG1lYW4oRDckbW10X3Jldmlld19zY29yZSwgbmEucm09VFJVRSksIA0KICAgICAgICAgICAgICAgICAgICAgICBENyRtbXRfcmV2aWV3X3Njb3JlKQ0KDQpENyRMb2NhdGlvbiA8LSBpZmVsc2UoaXMubmEoRDckTG9jYXRpb24pLCANCiAgICAgICAgICAgICAgIG1lYW4oRDckTG9jYXRpb24sIG5hLnJtPVRSVUUpLCANCiAgICAgICAgICAgICAgIEQ3JExvY2F0aW9uKQ0KDQpENyRGYWNpbGl0aWVzIDwtIGlmZWxzZShpcy5uYShENyRGYWNpbGl0aWVzKSwgDQogICAgICAgICAgICAgICAgbWVhbihENyRGYWNpbGl0aWVzLCBuYS5ybT1UUlVFKSwgDQogICAgICAgICAgICAgICAgRDckRmFjaWxpdGllcykNCg0KRDckQ2xlYW5saW5lc3MgPC0gaWZlbHNlKGlzLm5hKEQ3JENsZWFubGluZXNzKSwgDQogICAgICAgICAgICAgICAgICBtZWFuKEQ3JENsZWFubGluZXNzLCBuYS5ybT1UUlVFKSwgDQogICAgICAgICAgICAgICAgICBENyRDbGVhbmxpbmVzcykNCg0KRDckVmFsdWVfZm9yX01vbmV5IDwtIGlmZWxzZShpcy5uYShENyRWYWx1ZV9mb3JfTW9uZXkpLCANCiAgICAgICAgICAgICAgICAgICAgbWVhbihENyRWYWx1ZV9mb3JfTW9uZXksIG5hLnJtPVRSVUUpLA0KICAgICAgICAgICAgICAgICAgICBENyRWYWx1ZV9mb3JfTW9uZXkpDQoNCkQ3JEZvb2QgPC0gaWZlbHNlKGlzLm5hKEQ3JEZvb2QpLCBtZWFuKEQ3JEZvb2QsIG5hLnJtPVRSVUUpLA0KICAgICAgICAgICAgICAgICAgRDckRm9vZCkNCg0KRDckSG9zcGl0YWxpdHkgPC0gaWZlbHNlKGlzLm5hKEQ3JEhvc3BpdGFsaXR5KSwgDQogICAgICAgICAgICAgICAgICBtZWFuKEQ3JEhvc3BpdGFsaXR5LCBuYS5ybT1UUlVFKSwgDQogICAgICAgICAgICAgICAgICBENyRIb3NwaXRhbGl0eSkNCg0KYGBgDQoNCg0KSGVyZSB3ZSBoYXZlIHRoZSBzdW1tYXJ5IGluZm9ybWF0aW9uIGZvciBvdXIgY2xlYW5lZCBkYXRhc2V0DQpgYGB7cn0NCiMjIFByZXZpZXcgb2YgdGhlIHN0cnVjdHVyZSBvZiBvdXIgY2xlYW5lZCBkYXRhc2V0DQoNCnN0cihENykNCg0KIyMgUHJldmlldyBvZiB0aGUgc3VtbWFyeSBvZiBvdXIgZGF0YXNldA0Kc3VtbWFyeShENykNCg0KIyMgUHJldmlldyBvZiB0aGUgZmlyc3QgNiByb3dzDQpoZWFkKEQ3KQ0KDQojIyBQcmV2aWV3IG9mIHRoZSBsYXN0IDYgcm93cw0KdGFpbChENykNCg0KYGBgDQoNCg0KIyMgNC4gRGF0YSBFeHBsb3JhdGlvbjoNCg0KSGVyZSB3ZSBoYXZlIGZpbHRlcmVkIG91ciBkYXRhIHRvIGZpbmQgdGhlIHRvcCA1IGNpdGllcyBoYXZpbmcgbW9zdCBudW1iZXIgb2YgaG90ZWxzIGluIG91ciBkYXRhc2V0Lg0KDQpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCiNDcmVhdGVkIGEgZGF0YWZyYW1lIHRvIHN0b3JlIHRvcCA1IGNpdGllcyBiYXNlZCBvbiB0aGUgZnJlcXVlbmN5DQp0b3A1IDwtIGFzLmRhdGEuZnJhbWUodGFibGUoRDgkY2l0eSkpDQp0b3A1ICU+JSBhcnJhbmdlKGRlc2MoRnJlcSkpJT4laGVhZChuPTUpDQpgYGANCg0KDQpIZXJlLCB3ZSBoYXZlIGluY29ycG9yYXRlZCBnZW9zcGF0aWFsIGFuYWx5c2lzIHdoaWNoIGFzc2lzdGVkIHVzIGluIHZpc3VhbGl6aW5nIHRoZSBnZW9wb2ludHMgd2hpY2ggYXR0cmFjdCBtb3JlIGN1c3RvbWVycy4gSW5pdGlhbGx5LCB3ZSBmaWx0ZXJlZCBvbiBob3RlbHMgaGF2aW5nIHN0YXIgcmF0aW5nID49MyBhbmQgbGF0ZXIgd2VudCBvbiB0byBmaW5kIHdoaWNoIG91dCBvZiB0aGVzZSBoYWQgdGhlIG1vc3QgbnVtYmVyIG9mIHJldmlld3MNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQoNCkQ4IDwtIEQ3DQoNCg0KI0NvbnZlcnRlZCBsYXRpdHVkZSBmcm9tIGNoYXJhY3RlciB2YXJpYWJsZSB0byBudW1lcmljDQpEOCRsYXRpdHVkZSA8LSBhcy5udW1lcmljKEQ4JGxhdGl0dWRlKQ0KDQojQ3JlYXRlZCB0aGUgZGF0YWZyYW1lIHRvIGZpbHRlciBvdXQgdGhlIGhvdGVscyB3aXRoIHJhdGluZyBtb3JlIHRoYW4gb3IgZXF1YWwgdG8gMw0KRDkgPSBEOCAlPiUNCiAgZmlsdGVyKGhvdGVsX3N0YXJfcmF0aW5nID49IDMgKSAlPiUgDQogIGZpbHRlcighaXMubmEobGF0aXR1ZGUpKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShsb25naXR1ZGUpKQ0KDQojQ3JlYXRlZCB0aGUgZGF0YWZyYW1lIHRvIGZpbHRlciBvdXQgdGhlIGhvdGVscyB3aXRoIHJhdGluZyBtb3JlIHRoYW4gb3IgZXF1YWwgdG8gMyBhbmQgcmV2aWV3IGNvdW50IGdyZWF0ZXIgdGhhbiAxMDANCkQxMCA9IEQ4ICU+JQ0KZmlsdGVyKGhvdGVsX3N0YXJfcmF0aW5nID49IDMgJiBtbXRfcmV2aWV3X2NvdW50PjEwMCkgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKGxhdGl0dWRlKSkgJT4lDQogIGZpbHRlcighaXMubmEobG9uZ2l0dWRlKSkNCg0KI0NhbGN1bGF0ZWQgdGhlIG1lZGlhbiBvZiBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlIGZvciBnZW8tc3BhdGlhbCBhbmFseXNpcw0KbG9uZ2l0dWRlX2NlbnRlciA9IG1lZGlhbihEOSRsb25naXR1ZGUpDQpsYXRpdHVkZV9jZW50ZXIgPSBtZWRpYW4oRDkkbGF0aXR1ZGUpDQoNCiMNCmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigicnN0dWRpby9sZWFmbGV0IikNCg0KI0NyZWF0ZWQgdGhlIGNvbG9ycGFsbGV0ZSBmb3IgZGlmZmVyZW50IHJhdGluZ3MNCmNvbG9ycGFsIDwtIGNvbG9yRmFjdG9yKHRvcG8uY29sb3JzKDYsYWxwaGEgPSAxKSwgDQogICAgICAgICAgICAgICAgICAgICAgIEQ5JGhvdGVsX3N0YXJfcmF0aW5nKQ0KDQojR2VvLXNwYXRpYWwgYW5hbHlzaXMgb25seSBmb3IgaG90ZWxfc3Rhcl9yYXRpbmcNCmxlYWZsZXQoRDkpICU+JSBhZGRUaWxlcygpICU+JQ0KICBhZGRDaXJjbGVzKGxuZyA9IH5sb25naXR1ZGUsIGxhdCA9IH5sYXRpdHVkZSwgDQogICAgICAgICAgICAgY29sb3IgPSB+Y29sb3JwYWwoaG90ZWxfc3Rhcl9yYXRpbmcpKSAgJT4lDQogIHNldFZpZXcobG5nPWxvbmdpdHVkZV9jZW50ZXIsIGxhdD1sYXRpdHVkZV9jZW50ZXIsem9vbSA9IDUpICU+JQ0KICBhZGRMZWdlbmQoImJvdHRvbXJpZ2h0IiwgcGFsID0gY29sb3JwYWwsIHZhbHVlcyA9IH5ob3RlbF9zdGFyX3JhdGluZywNCiAgICAgICAgICAgIHRpdGxlID0gIk1NVCBIb3RlbHMgc3RhcnQgcmF0aW5nIGFjcm9zcyBJbmRpYSIsDQogICAgICAgICAgICBvcGFjaXR5ID0gMSkNCg0KDQoNCiNHZW8tc3BhdGlhbCBhbmFseXNpcyBvbmx5IGZvciBob3RlbF9zdGFyX3JhdGluZyBhbmQgcmV2aWV3IGNvdW50cw0KbGVhZmxldChEMTApICU+JSBhZGRUaWxlcygpICU+JQ0KICBhZGRDaXJjbGVzKGxuZyA9IH5sb25naXR1ZGUsIGxhdCA9IH5sYXRpdHVkZSwgDQogICAgICAgICAgICAgY29sb3IgPSB+Y29sb3JwYWwoaG90ZWxfc3Rhcl9yYXRpbmcpKSAgJT4lDQoNCiAgc2V0Vmlldyhsbmc9bG9uZ2l0dWRlX2NlbnRlciwgbGF0PWxhdGl0dWRlX2NlbnRlcix6b29tID0gNSkgJT4lDQogIA0KICBhZGRMZWdlbmQoImJvdHRvbXJpZ2h0IiwgcGFsID0gY29sb3JwYWwsIHZhbHVlcyA9IH5ob3RlbF9zdGFyX3JhdGluZywNCiAgICAgICAgICAgIHRpdGxlID0gIk1NVCBIb3RlbHMgc3RhcnQgcmF0aW5nIHdpdGggYXRsZWFzdCAxMDAgcmV2aWV3cyBvbiBNTVQiLA0KICAgICAgICAgICAgb3BhY2l0eSA9IDEpDQoNCmBgYA0KDQoNCg0KIyMgNS4gQW5hbHlzaXMvRmluZGluZ3M6DQoNCkhlcmUgd2Ugc3VtbWFyaXNlZCB0aGUgYmVzdCAxMCBDaXRpZXMgaGF2aW5nIHRoZSBtYXhpbXVtIG51bWJlciBvZiBob3RlbHMNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQoNCiMgQ3JlYXRlZCB0aGUgZGF0YWZyYW1lIHRvIGZpbmQgdGhlIGNpdGllcyB3aXRoIHRoZSBtYXhpbXVtIGhvdGVsIGZyZXF1ZW5jeQ0KRDggJT4lIGdyb3VwX2J5KGNpdHkpICU+JQ0KICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQ0KICBhcnJhbmdlKGRlc2MoY291bnQpKSAlPiUNCiAgaGVhZChuID0gMTApICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBjaXR5LHkgPSBjb3VudCxmaWxsPWNvdW50KSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKyBjb29yZF9mbGlwKCkNCg0KYGBgDQoNCkhlcmUgd2UgaGF2ZSBwbG90dGVkIHRoZSB0b3AgMTAgY2l0aWVzIHdpdGggaG90ZWxzIGJhc2VkIG9uIHRoZSBhdmVyYWdlIHJhdGluZ3Mgb2YgQ2xlYW5saW5lc3MsRm9vZCxWYWx1ZSBGb3IgTW9uZXkgcmVzcGVjdGl2ZWx5DQoNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQojUGxvdHRlZCB0aGUgdG9wIGNpdGllcyB3aXRoIGhvdGVscyB3aXRoIGJlc3QgQ2xlYW5pbGluZXNzIHJhdGluZ3MNCkQ4ICU+JSANCiAgZ3JvdXBfYnkoY2l0eSkgJT4lDQogIHN1bW1hcmlzZShhdmc9bWVhbihDbGVhbmxpbmVzcykpICU+JQ0KICBhcnJhbmdlKGRlc2MoYXZnKSkgJT4lDQogIHNlbGVjdChjaXR5LGF2ZykgJT4lDQogIGhlYWQobj0xMCkgJT4lDQogIGdncGxvdChhZXMoeD0gY2l0eSx5PSBhdmcpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLGZpbGwgPSIjRkYzMzk5IiwgY29sb3VyPSJibGFjayIpICsgY29vcmRfZmxpcCgpDQoNCiNQbG90dGVkIHRoZSB0b3AgY2l0aWVzIHdpdGggaG90ZWxzIHdpdGggYmVzdCBGb29kIHJhdGluZ3MNCkQ4ICU+JQ0KICBncm91cF9ieShjaXR5KSAlPiUNCiAgc3VtbWFyaXNlKGF2Zz1tZWFuKEZvb2QpKSAlPiUNCiAgYXJyYW5nZShkZXNjKGF2ZykpICU+JQ0KICBzZWxlY3QoY2l0eSxhdmcpICU+JQ0KICBoZWFkKG49MTApICU+JQ0KICBnZ3Bsb3QoYWVzKHg9Y2l0eSx5PWF2ZykpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsZmlsbD0iIzMzQ0NGRiIsIGNvbG91cj0iYmxhY2siKSArIGNvb3JkX2ZsaXAoKQ0KDQoNCiMjUGxvdHRlZCB0aGUgdG9wIGNpdGllcyB3aXRoIGhvdGVscyB3aXRoIGJlc3QgVmFsdWVfZm9yX01vbmV5IHJhdGluZ3MNCkQ4ICU+JQ0KICBncm91cF9ieShjaXR5KSAlPiUNCiAgc3VtbWFyaXNlKGF2Zz1tZWFuKFZhbHVlX2Zvcl9Nb25leSkpICU+JQ0KICBhcnJhbmdlKGRlc2MoYXZnKSkgJT4lIA0KICBzZWxlY3QoY2l0eSxhdmcpICU+JSBoZWFkKG49MTApICU+JQ0KICBnZ3Bsb3QoYWVzKHg9Y2l0eSx5PWF2ZykpICsgDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLGZpbGw9IiM2NjAwQ0MiLCBjb2xvdXI9ImJsYWNrIikgKyBjb29yZF9mbGlwKCkNCg0KYGBgDQoNCiANCldlIHBlcmZvcm1lZCBhIG11bHRpcGxlIHJlZ3Jlc3Npb24gYW5hbHlzaXMgdG8gdW5kZXJzdGFuZCB3aGljaCBvZiB0aGUgNSBmYWN0b3JzIGFyZSBiZXN0IGNvcnJlbGF0ZWQgb3Igc2lnbmlmaWNhbnRseSBpbXBhY3QgdGhlIHZhbHVlcyBvZiBob3RlbCBzdGFyIHJhdGluZy4gDQoNCldlIGZvdW5kIG91dCB0aGF0IHRoZSBob3RlbCBzdGFyIHJhdGluZyBpcyBzaWduaWZpY2FudGx5IGRlcGVuZGVudCBvbiB0aGUgIkhvc3BpdGFsaXR5IiwgIkZhY2lsaXRpZXMiLCAiQ2xlYW5saW5lc3MiLCAiVmFsdWUgZm9yIE1vbmV5IiBmYWN0b3JzLiAiRm9vZCIgYW5kICJMb2NhdGlvbiIgZG9ub3QgY29udHJpYnV0ZSBtdWNoIHRvIHRoZSB2YXJpYXRpb24gaW4gdGhlIGhvdGVsX3N0YXJfcmF0aW5nLg0KDQpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCg0KIyMgQnVpbGRpbmcgb3VyIFJlZ3Jlc3Npb24gTW9kZWwgDQptbXRtb2RlbCA8LSBsbShob3RlbF9zdGFyX3JhdGluZyB+IExvY2F0aW9uICsNCiAgICAgICAgICAgIEhvc3BpdGFsaXR5ICsgRmFjaWxpdGllcyArIENsZWFubGluZXNzICsNCiAgICAgICAgICAgIFZhbHVlX2Zvcl9Nb25leSArIEZvb2QgKyBtbXRfdHJpcGFkdmlzb3JfY291bnQsDQogICAgICAgICAgICBkYXRhID0gRDgpDQpzdW1tYXJ5KG1tdG1vZGVsKQ0KDQpgYGANCg0KDQoNCiMjIDYuIFN1bW1hcnk6DQoNClRoZSBwcm9ibGVtIHN0YXRlbWVudCBvZiBvdXIgYW5hbHlzaXMgd2FzIGNlbnRlcmVkIGFyb3VuZCB1bmRlcnN0YW5kaW5nIGhvdyB0aGUgIlRyYXZlbGxlcl9SYXRpbmciIHdoaWNoIHdhcyBzZWdyZWdhdGVkIGludG8gIkNsZWFubGluZXNzIiwgIlZhbHVlX0Zvcl9Nb25leSIsICJGb29kIiAmICJGYWNpbGl0aWVzIiB2YXJpZXMgd2l0aCB0aGUgIkhvdGVsIFN0YXIgUmF0aW5nIi4gSGVuY2UsIHdlIGxvb2tlZCBhdCB0aGUgdG9wIDEwIGhvdGVscyBpbiB0aGUgY2l0aWVzIGJhc2VkIG9uIHRoZXNlIDQgZmFjdG9ycy4NCg0KV2UgYWxzbyByYW4gYSByZWdyZXNzaW9uIGFuYWx5c2lzIHRvIHVuZGVyc3RhbmQgd2hpY2ggb2YgdGhlc2UgZmFjdG9ycyBpbXBhY3RzIG91ciBob3RlbF9zdGFyX3JhdGluZyB0aGUgbW9zdC4gT3VyIHJlc3VsdHMgc2hvdyB0aGF0IHRoZSBob3RlbCBzdGFyIHJhdGluZyBpcyBzaWduaWZpY2FudGx5IGRlcGVuZGVudCBvbiB0aGUgIkhvc3BpdGFsaXR5IiwgIkZhY2lsaXRpZXMiLCAiQ2xlYW5saW5lc3MiLCAiVmFsdWUgZm9yIE1vbmV5IiBmYWN0b3JzLiBXZSBsb29rZWQgYXQgdGhlIHAtdmFsdWUgZm9yIHRoZSByZWdyZXNzaW9uIG1vZGVsIHdlIGJ1aWx0IHRvIGFycml2ZSBhdCB0aGlzIGFuYWx5c2lzLg0KIkZvb2QiIGFuZCAiTG9jYXRpb24iIGRvbm90IGNvbnRyaWJ1dGUgbXVjaCB0byB0aGUgdmFyaWF0aW9uIGluIHRoZSBob3RlbF9zdGFyX3JhdGluZy4NCg0KV2UgZm91bmQgb3V0IHRoZSB0b3AgMTAgY2l0aWVzIGJhc2VkIG9uIHRoZSBmcmVxdWVuY3kgb2YgaG90ZWxzIGluIGEgcGFydGljdWxhciBjaXR5IHdoaWNoIGdhdmUgdXMgdGhlIGluc2lnaHQgb2YgY2l0aWVzIGF0dHJhY3RpbmcgbWF4aW11bSB0cmF2ZWxsZXJzLlRoZXNlIGNpdGllcyB3ZXJlIG5vdCBwcmVzZW50IHdoZW4gd2UgZHJpbGwgZG93biB0byBmaW5kIHRoZSB0b3AgY2l0aWVzIG9uIGN1c3RvbWVyIHNhdGlzZmFjdGlvbiBwYXJhbWV0ZXIgdGhvdWdoIHRoZXkgaGF2ZSB0aGUgY3VzdG9tZXIgYmFzZSBidXQgdGhleSBhcmUgbGFja2luZyBiZWhpbmQgb24gdGhlIGN1c3RvbWVyIHNhdGlzZmFjdGlvbiBzY2FsZQ0KDQoNCg0KDQoNCiMjIDcuIExpbWl0YXRpb25zOg0KDQpXZSBoYWQgNTIgY29sdW1ucyB3aGljaCBoYWQgaXJyZWxldmFudCBpbmZvcm1hdGlvbiBvbiB3aGljaCBhbmFseXNpcyB3YXMgbm90IHBvc3NpYmxlLiBIZW5jZSBvdXIgZmlyc3Qgc3RlcCB3YXMgcmVtb3ZpbmcgdGhlc2UgYW5kIHN0YW5kYXJkaXppbmcgb3VyIGRhdGFzZXQgc28gdGhhdCB3ZSBjb3VsZCBydW4gb3VyIGFuYWx5c2lzIHNlYW1sZXNzbHkuDQoNCk91ciBkYXRhc2V0IGhhZCBhIGxvdCBvZiBtaXNzaW5nIHZhbHVlcyB3aGljaCB3ZXJlIHJlcGxhY2VkIHVzaW5nIHRoZSBNZWFuIEltcHV0YXRpb24gTWV0aG9kIGlub3JkZXIgdG8gZW5zdXJlIG91ciByZXN1bHRzIGFyZSBub3QgaW1wYWN0ZWQgZHVlIHRvIHRoZSBtaXNzaW5nIGRhdGEuDQoNCklub3JkZXIgdG8gYnVpbGQgdXBvbiB0aGlzIGFuYWx5c2lzIGluIGZ1dHVyZSB3ZSB3b3VsZCBzdWdnZXN0IHVzYWdlIG9mIFNoaW55IHBhY2thZ2UgdG8gZW5hYmxlIGludGVyYWN0aXZlIGJpbmRpbmcgYmV0d2VlbiBpbnB1dCBhbmQgb3V0cHV0IHZhcmlhYmxlcy4NCg0KDQoNCg0KDQoNCg0KDQoNCg==