Most of these libraries have been used someone or another in the code. Some were left out however.

#Necessary Libraries
library(lubridate)
library(ggpubr)
library(xts)
library(topicmodels) 
library(plotly)
library(tm)
library(sentimentr)
library(dplyr)
library(syuzhet)
library(rvest)
library(httr)
library(magrittr)
library(stringr)
library(tm)
library(tibble)
library(rvest)
library(tidytext)
library(ggplot2)
library(wordcloud)
library(tidyr)
library(usmap)
library(maps)
library(ggmap)
library(googleAuthR)
library(sp)
library(maps)
library(maptools)
library(revgeo)
library(choroplethrMaps)
library(mapproj)
library(SnowballC)
library(RColorBrewer)

The following code snippet reads the number of reviews on the website for the uber partner drivers site

number = read_html(paste("https://www.indeed.com/cmp/Uber-Partner-Drivers/reviews?start=0")) %>%
  html_nodes("b") %>%
  html_text()
number = gsub(pattern = ",", replacement = "", number) #removing the comma so it can be transformed into a number
number = as.numeric(as.character(number))

Scraping the tite of the review, the text, the date, and the number of stars given and putting it into a data frame

page = seq(from = 0, to = number, by = 20) #Deriving the number of pages to be considered
uber_drivers = NULL #Creating an empty data frame
for (start in (page)) { # For loop to read in the file and append it to a "running total dataframe" called articles}
 na = c(NA, NA,NA,NA, NA,NA,NA, NA,NA,NA, NA,NA,NA, NA,NA,NA, NA,NA,NA, NA)
   title = read_html(paste("https://www.indeed.com/cmp/Uber-Partner-Drivers/reviews?start=", start, sep = '')) %>%
    html_nodes("div[itemprop='review']:nth-of-type(n+2) .cmp-review-title span:nth-of-type(1)") %>%
    html_text()
#print(title)
   date = read_html(paste("https://www.indeed.com/cmp/Uber-Partner-Drivers/reviews?start=", start, sep = '')) %>%
    html_nodes("div[itemprop='review']:nth-of-type(n+2) span.cmp-review-date-created") %>%
    html_text()
 #print(date)
 review = read_html(paste("https://www.indeed.com/cmp/Uber-Partner-Drivers/reviews?start=", start, sep = '')) %>%
   html_nodes("div[itemprop='review']:nth-of-type(n+2) span[itemprop='reviewBody']") %>%
   html_text()
 #print(review)
 
 stars = read_html(paste("https://www.indeed.com/cmp/Uber-Partner-Drivers/reviews?start=", start, sep = '')) %>%
   html_nodes("div[itemprop='review']:nth-of-type(n+2) div.cmp-ratingNumber") %>%
   html_text()
    stars = gsub(pattern = "", replacement = "", stars)
    stars = as.numeric(stars)
 #print(stars)
    
  city = read_html(paste("https://www.indeed.com/cmp/Uber-Partner-Drivers/reviews?start=", start, sep = '')) %>%
   html_nodes("div[itemprop='review']:nth-of-type(n+2) span.cmp-reviewer-job-location") %>%
   html_text()
  
#The web scraper about 5 times will break if it only finds 19 elements on the page instead of 20.  The following set of if statements allows for loop to run even if this occurr by inserting a string of NA's when Web scraper doesn't return the correct number of values
  if(length(title == 20))  {
  title = title
} else {
  title = na
} 
 
if(length(date == 20))  {
  date = date
} else {
  date = na
} 
 
if(length(review == 20))  {
  review = review
} else {
  review = na
}  
 
 if(length(stars == 20))  {
  stars = stars
} else {
  stars = na
} 
    
if(length(city == 20))  {
  city = city
} else {
  city = na
} 
temp = data.frame(title, review, date, stars, city)
uber_drivers = rbind(uber_drivers, temp)
}

Sentiment analysis for uber drivers, grouping by month, and plotting over time

uber_drivers$sentiment = as.numeric(get_sentiment(as.character(uber_drivers$review))) #Getting sentiment
uber_drivers2 = uber_drivers %>%
  group_by(date) %>%
  summarise(mean(sentiment))
names(uber_drivers2) = c("date", "sentiment")
#uber_drivers2 %>% ggplot(aes(x=date, y=sentiment)) + geom_col(color="red") + xlab("Sequence") +
    #ylab("Sentiment") + ggtitle("Driver Sentiment") +
    #theme(plot.title = element_text(hjust = 0.5)) + geom_smooth(method = "auto") + geom_vline(xintercept = as.numeric(uber_drivers$date[3267]))
uber_drivers_monthly = uber_drivers2 %>% #Aggregating data into month bins
    group_by(month=floor_date(date, "month")) %>%
    summarise(sentiment = mean(sentiment))
uber_drivers_monthly %>% ggplot(aes(x=month, y=sentiment)) + geom_col(color="black") + xlab("Sequence") +
    ylab("Sentiment") + ggtitle("Driver Sentiment") +
    theme(plot.title = element_text(hjust = 0.4)) + geom_smooth(method = "auto") + geom_vline(xintercept = as.numeric(uber_drivers$date[3267])) + ggtitle("Sentiment over Time for Uber Drivers") 

The following code snippet reads the number of reviews on the website for the uber page

#The following code snippet reads the number of reviews on the website for a given company
number_employees = read_html(paste("https://www.indeed.com/cmp/Uber/reviews?start=0")) %>%
  html_nodes("b") %>%
  html_text()
number_employees = gsub(pattern = ",", replacement = "", number_employees) #removing the comma so it can be transformed into a number
number_employees = as.numeric(as.character(number_employees))
page2 = seq(from = 0, to = (number_employees), by = 20)
uber_employees  = NULL #Creating an empty data frame
for (start in (page2)) { # For loop to read in the file and append it to a "running total dataframe" called articles}
 na = c(NA, NA,NA,NA, NA,NA,NA, NA,NA,NA, NA,NA,NA, NA,NA,NA, NA,NA,NA, NA)
  
  title = read_html(paste("https://www.indeed.com/cmp/Uber/reviews?start=", start, sep = '')) %>%
    html_nodes("div[itemprop='review']:nth-of-type(n+2) .cmp-review-title span:nth-of-type(1)") %>%
    html_text()
  #print(title)
   date = read_html(paste("https://www.indeed.com/cmp/Uber/reviews?start=", start, sep = '')) %>%
    html_nodes("div[itemprop='review']:nth-of-type(n+2) span.cmp-review-date-created") %>%
    html_text()
 #print(date)
 review = read_html(paste("https://www.indeed.com/cmp/Uber/reviews?start=", start, sep = '')) %>%
   html_nodes("div[itemprop='review']:nth-of-type(n+2) span[itemprop='reviewBody']") %>%
   html_text()
 #print(review)
 
 stars = read_html(paste("https://www.indeed.com/cmp/Uber/reviews?start=", start, sep = '')) %>%
   html_nodes("div[itemprop='review']:nth-of-type(n+2) div.cmp-ratingNumber") %>%
   html_text()
    stars = gsub(pattern = "", replacement = "", stars)
    stars = as.numeric(stars)
 #print(stars)
    
    position = read_html(paste("https://www.indeed.com/cmp/Uber/reviews?start=", start, sep = '')) %>%
   html_nodes("div[itemprop='review']:nth-of-type(n+2) span.cmp-reviewer") %>%
   html_text()
    position = gsub(pattern = "", replacement = "", position)
  #print(position)  
  if(length(title == 20))  {
  title = title
} else {
  title = na
} 
 
if(length(date == 20))  {
  date = date
} else {
  date = na
} 
 
if(length(review == 20))  {
  review = review
} else {
  review = na
}  
 
if(length(stars == 20))  {
  stars = stars
} else {
  stars = na
} 
if(length(position == 20))  {
  position = position
} else {
  position = na
} 
 
temp = data.frame(title, review, date, stars, position)
uber_employees = rbind(uber_employees, temp)
}

Converting the dates to be read in date format

uber_employees = na.omit(uber_employees)
uber_employees$date= str_replace_all(uber_employees$date, ",", "")
uber_employees$date = as.Date(uber_employees$date, format = "%B %d %Y") #Converting it to date format

Plotting the sentiment for uber drivers over time

Sentiment analysis for uber employees and then grouping by month and plotting over time

uber_employees$sentiment =as.numeric(get_sentiment(as.character(uber_employees$review))) #Getting sentiment
uber_employees2 = uber_employees %>%
  #mutate(month = format(date, "%m"), year = format(date, "%Y")) %>%
  group_by(date) %>%
  summarise(mean(sentiment))
##uber_employees2$date = as.Date(uber_employees2$date, format = "%M %Y")
names(uber_employees2) = c("date", "sentiment")
uber_employees_monthly = uber_employees2 %>% #Aggregating data into month bins
    group_by(month=floor_date(date, "month")) %>%
    summarise(sentiment = mean(sentiment))
uber_employees_monthly %>% ggplot(aes(x=month, y=sentiment)) + geom_col(color="white") + xlab("Date") +
    ylab("Sentiment") + ggtitle("EmployeeSentiment") +
    theme(plot.title = element_text(hjust = 0.5)) + geom_smooth(method = "auto")  + geom_vline(xintercept = as.numeric(uber_drivers$date[3267])) + ggtitle("Sentiment over Time for Uber Employees")

Uber Drivers versus Employees Sentiment

# Area plot
uber_drivers_monthly$type = "driver" #Adding the fact that the review comes from a driver
uber_employees_monthly$type = "employee" #Adding the fact that the review comes from an employee
all_uber = rbind(uber_drivers_monthly, uber_employees_monthly)
ggplot(all_uber, aes(x = month, y = sentiment)) + 
  geom_area(aes(color = type, fill = type), 
            alpha = 0.3, position = position_dodge(0.8)) +
  scale_color_manual(values = c("#00AFBB", "#E7B800")) +
  scale_fill_manual(values = c("#00AFBB", "#E7B800")) + 
  geom_vline(xintercept = as.numeric(uber_drivers$date[3267])) +xlab("Date") + ylab("Sentiment") + ggtitle("Sentiment over Time by Worker Type") + scale_x_date(limits = as.Date(c("2014-01-01","2019-05-05")))

The following code snippet divides drives into a before and after the new ceo and then only takes the bad reviews and performs an LDA classification. The following six snippets weren’t really included in the project

uber_drivers_before = subset(uber_drivers, date>=as.Date("2010-11-25") & date<=as.Date("2017-08-27")) #Before the New CEO
#uber_drivers_before = uber_drivers_before %>%
  #filter(stars <=2)
uber_drivers_after = subset(uber_drivers, date>=as.Date("2017-08-28") & date<=as.Date("2020-01-01")) #After the new ceo
#uber_drivers_after = uber_drivers_after %>%
  #filter(stars <=2)
#Crating a corpus of the reviews and cleaning it up
reviews_before = VCorpus(VectorSource(uber_drivers_before$review))
reviews_after = VCorpus(VectorSource(uber_drivers_after$review))
reviews_before = reviews_before %>%
  tm_map(removeWords, stopwords("english")) %>% #Remove stopwords
  tm_map(stripWhitespace) %>% #Remove Whitespace
  tm_map(content_transformer(tolower)) %>% #Convert to lowercase
  tm_map(removePunctuation) %>% #Remove punctuation
  tm_map(removeNumbers) %>% #Remove numbers
  tm_map(content_transformer(stemDocument) ,lazy=TRUE) %>% #Stemming words
  tm_map(content_transformer(removeWords), c("and", "said", "but", "the") ,lazy=TRUE) #Taking out further unnecessecary words
reviews_after = reviews_after %>%
  tm_map(removeWords, stopwords("english")) %>% #Remove stopwords
  tm_map(stripWhitespace) %>% #Remove Whitespace
  tm_map(content_transformer(tolower)) %>% #Convert to lowercase
  tm_map(removePunctuation) %>% #Remove punctuation
  tm_map(removeNumbers) %>% #Remove numbers
  tm_map(content_transformer(stemDocument) ,lazy=TRUE) %>% #Stemming words
  tm_map(content_transformer(removeWords), c("and", "said", "but", "the") ,lazy=TRUE) #Taking out further unnecessecary words
dtm_before = DocumentTermMatrix(reviews_before)
dtm_after = DocumentTermMatrix(reviews_after)
#Removing sparse terms
dtms_before = removeSparseTerms(dtm_before, .99) 
dtms_after = removeSparseTerms(dtm_after, .99) 
#Building an LDA Model
dtm_matrix_lda_before = as.matrix(dtms_before)
dtm_matrix_lda_after = as.matrix(dtms_after)

terms_before = rowSums(dtm_matrix_lda_before) != 0 #Finding values that never appear
dtm_matrix_lda_before = dtm_matrix_lda_before[terms_before,] #Clearing out values that never appear
terms_after = rowSums(dtm_matrix_lda_after) != 0 #Finding values that never appear
dtm_matrix_lda_after = dtm_matrix_lda_after[terms_after,] #Clearing out values that never appear


lda_uber_before <-LDA(dtm_matrix_lda_before, 12, method="Gibbs", control = list(seed = 1234))
terms(lda_uber_before,10)

lda_uber_after <-LDA(dtm_matrix_lda_after, 12, method="Gibbs", control = list(seed = 1234))
terms(lda_uber_after,10)

The following three code snippets divide uber employees into two groups and perform an LDA on each

#Dividing Uber employees into two subgroups
uber_employees2 = uber_employees %>%
  filter(position != "Driver"&  position != "DRIVER" & position != "Uber Driver")
uber_employees_before = subset(uber_employees, date>=as.Date("2010-11-25") & date<=as.Date("2017-08-27")) #Before the New CEO
uber_employees_after = subset(uber_employees, date>=as.Date("2017-08-28") & date<=as.Date("2020-01-01")) #After the new ceo
#Creating a corpus of the employee reviews and cleaning it up
employee_reviews_before = VCorpus(VectorSource(uber_drivers_before$review))
employee_reviews_after = VCorpus(VectorSource(uber_drivers_after$review))
employee_reviews_before = employee_reviews_before %>%
  tm_map(removeWords, stopwords("english")) %>% #Remove stopwords
  tm_map(stripWhitespace) %>% #Remove Whitespace
  tm_map(content_transformer(tolower)) %>% #Convert to lowercase
  tm_map(removePunctuation) %>% #Remove punctuation
  tm_map(removeNumbers) %>% #Remove numbers
  tm_map(content_transformer(stemDocument) ,lazy=TRUE) %>% #Stemming words
  tm_map(content_transformer(removeWords), c("and", "said", "but", "the") ,lazy=TRUE) #Taking out further unnecessecary words
employee_reviews_after = employee_reviews_after %>%
  tm_map(removeWords, stopwords("english")) %>% #Remove stopwords
  tm_map(stripWhitespace) %>% #Remove Whitespace
  tm_map(content_transformer(tolower)) %>% #Convert to lowercase
  tm_map(removePunctuation) %>% #Remove punctuation
  tm_map(removeNumbers) %>% #Remove numbers
  tm_map(content_transformer(stemDocument) ,lazy=TRUE) %>% #Stemming words
  tm_map(content_transformer(removeWords), c("and", "said", "but", "the") ,lazy=TRUE) #Taking out further unnecessecary words
employee_dtm_before = DocumentTermMatrix(employee_reviews_before)
employee_dtm_after = DocumentTermMatrix(employee_reviews_after)
#Removing sparse terms
employee_dtms_before = removeSparseTerms(employee_dtm_before, .99) 
employee_dtms_after = removeSparseTerms(employee_dtm_after, .99) 
#Building an LDA Model for employees
employee_dtm_matrix_before = as.matrix(employee_dtms_before)
employee_dtm_matrix_after = as.matrix(employee_dtms_after)

employee_terms_before = rowSums(employee_dtm_matrix_before) != 0 #Finding values that never appear
employee_dtm_matrix_before = employee_dtm_matrix_before[employee_terms_before,] #Clearing out values that never appear

employee_terms_after = rowSums(employee_dtm_matrix_after) != 0 #Finding values that never appear
employee_dtm_matrix_after = employee_dtm_matrix_after[employee_terms_after,] #Clearing out values that never appear


lda_uber_employee_before <-LDA(employee_dtm_matrix_before, 8, method="Gibbs", control = list(seed = 1234))
terms(lda_uber_employee_before,8)

lda_uber_employee_after <-LDA(employee_dtm_matrix_after, 8, method="Gibbs", control = list(seed = 1234))
terms(lda_uber_employee_after,8)

Looking at the cities with the best reviews

uber_driver_city = uber_drivers %>%
  group_by(city) %>%
  summarise(mean(sentiment))
names(uber_driver_city) = c("city", "sentiment")
uber_driver_city =uber_driver_city %>%
    arrange(desc(sentiment))
uber_driver_city$city2 = gsub(pattern = ",", replacement = "", uber_driver_city$city) #Taking out the comma to make it readable to google

The following code prepares the datafame for state analysis by geocoding and providing lat long. This solves a lot of trouble with the messy data

The following code prepares the datafame for state analysis

uber_driver_city2 = uber_driver_city #Duplicating the dataframe as it takes a long time for google to geocode
latlong2state <- function(pointsDF) { #Convets all the coordinates back to state codes
    # Prepare SpatialPolygons object with one SpatialPolygon
    # per state (plus DC, minus HI & AK)
    states <- map('state', fill=TRUE, col="transparent", plot=FALSE)
    IDs <- sapply(strsplit(states$names, ":"), function(x) x[1])
    states_sp <- map2SpatialPolygons(states, IDs=IDs,
                     proj4string=CRS("+proj=longlat +datum=wgs84"))
    # Convert pointsDF to a SpatialPoints object 
    pointsSP <- SpatialPoints(pointsDF, 
                    proj4string=CRS("+proj=longlat +datum=wgs84"))
    # Use 'over' to get _indices_ of the Polygons object containing each point 
    indices <- over(pointsSP, states_sp)
    # Return the state names of the Polygons object containing each point
    stateNames <- sapply(states_sp@polygons, function(x) x@ID)
    stateNames[indices]
}
uber_driver_city3 = uber_driver_city2
uber_driver_city3 = na.omit(uber_driver_city3) #Removing NA values
uber_driver_city3$state = latlong2state(uber_driver_city3[4:5]) #Adding a column of state codes
uber_driver_city3 = na.omit(uber_driver_city3)#Removing NA values
states = read.csv("statelatlong.csv") #A data fame with states and state codes
states = states[, c(-2,-3)]
states$City = tolower(states$City) #converting state names to lowercase
uber_driver_city4 = left_join(uber_driver_city3, states, by = c("state" ="City")) #AAssociating each state with a code to be read into plotly

Which Cities are the best for Uber Drivers

Old Version of best cities code

uber_driver_city20 = uber_driver_city[1:20,]
uber_driver_city20 =uber_driver_city20 %>%
    arrange(desc(sentiment))

uber_driver_city20$city <- factor(uber_driver_city20$city, levels = uber_driver_city20$city[order(uber_driver_city20$sentiment)])


best_cities = ggplot(uber_driver_city20, aes(x=city, y=sentiment)) + 
  geom_bar(stat="identity", width=.5, fill="black") + 
  labs(title="Average Sentiment by City") + 
  theme(axis.text.x = element_text(angle=65, vjust=0.6))
best_cities

Code for building the interactive map

Exporting the widget to be embedded in the website

Text Analysis First Cleaning the Text

uber_driver_text = uber_drivers
#Crating a corpus of the reviews and cleaning it up
reviews = VCorpus(VectorSource(uber_driver_text$review))
reviews = reviews %>%
  tm_map(removeWords, stopwords("english")) %>% #Remove stopwords
  tm_map(stripWhitespace) %>% #Remove Whitespace
  tm_map(content_transformer(tolower)) %>% #Convert to lowercase
  tm_map(removePunctuation) %>% #Remove punctuation
  tm_map(removeNumbers) #Remove numbers
dtm_all = DocumentTermMatrix(reviews)
#Removing sparse terms
dtms_all = removeSparseTerms(dtm_all, .99) 

Logistic Regression for Words Assoiciated Positively

#Feature Selection Positive
dtm_matrix_selection = as.matrix(dtms_all)
uber_driver_text$positive = uber_driver_text$stars == 4 | uber_driver_text$stars == 5 #Creating a row of true or false value to see if the post was related to popular sentiment or not
corr_positive = cor(uber_driver_text$positive, dtm_matrix_selection)
top20_positive = order(corr_positive, decreasing=T)[1:20]
top20_positive_words = colnames(corr_positive)[top20_positive]
top20_positive_words
 [1] "great"        "meet"         "love"         "enjoy"        "schedule"     "people"       "boss"        
 [8] "work"         "flexible"     "easy"         "fun"          "different"    "able"         "part"        
[15] "enjoyable"    "hardest"      "job"          "destinations" "flexibility"  "new"         
#Building a logistic regression to see which words lead to most positive reviews
logistic_positive = as.data.frame(cbind(positive = uber_driver_text$positive, dtm_matrix_selection[,top20_positive_words]))
pedictive_positive = glm(positive~., data=logistic_positive, family=binomial)
Warning in deparse(x, width.cutoff = 500L, backtick = !is.symbol(x) && is.language(x)) :
  closing unused connection 3 (https://www.indeed.com/cmp/Uber-Partner-Drivers/reviews?start=60)
#summary(pedictive_positive)

Feature Selection for words associated with negative Reviews

#Feature Selection Negative
uber_driver_text$negative = uber_driver_text$stars == 1 | uber_driver_text$stars == 2 #Creating a row of true or false value to see if the post was related to negative sentiment
corr_negative = cor(uber_driver_text$negative, dtm_matrix_selection)
top20_negative = order(corr_negative, decreasing=T)[1:20]
top20_negative_words = colnames(corr_negative)[top20_negative]
top20_negative_words
 [1] "drivers" "they"    "pay"     "will"    "uber"    "even"    "gas"     "minimum" "less"    "wage"    "car"    
[12] "miles"   "low"     "driver"  "hour"    "per"     "waste"   "care"    "rating"  "after"  
#Building a logistic regression to see which words lead to most negative reviews
logistic_negative = as.data.frame(cbind(negative = uber_driver_text$negative, dtm_matrix_selection[,top20_negative_words]))
pedictive_negative = glm(negative~., data=logistic_negative, family=binomial)
#summary(pedictive_negative)
#Words most significantly associated with leaving bad cultural review
coef_negative = coef(pedictive_negative)[-1]
negative.terms = coef_negative[coef_negative>0]
top.negative = sort(negative.terms,decreasing=T)[1:20]
top.negative.frame = as.data.frame(top.negative)
top.negative.frame = rownames_to_column(top.negative.frame)
names(top.negative.frame) = c("word", "magnitude")
wordcloud(words = top.negative.frame$word, freq = top.negative.frame$magnitude,
          max.words=200, random.order=FALSE, rot.per=.2, 
          colors=brewer.pal(8, "Dark2"), scale=c(6,.5))

Building an LDA model for Uber Reviews

#Building an LDA Model
dtm_matrix_lda = as.matrix(dtms_all)
terms = rowSums(dtm_matrix_lda) != 0 #Finding values that never appear
dtm_matrix_lda = dtm_matrix_lda[terms,] #Clearing out values that never appear
ldaOut <-LDA(dtm_matrix_lda, 10, method="Gibbs", control = list(seed = 1234))
terms(ldaOut,10)
      Topic 1       Topic 2   Topic 3      Topic 4  Topic 5 Topic 6    Topic 7     Topic 8   Topic 9   Topic 10
 [1,] "uber"        "car"     "job"        "time"   "make"  "work"     "people"    "driving" "uber"    "get"   
 [2,] "working"     "pay"     "the"        "job"    "money" "can"      "new"       "uber"    "drivers" "drive" 
 [3,] "company"     "gas"     "day"        "great"  "good"  "want"     "meet"      "like"    "will"    "you"   
 [4,] "customer"    "vehicle" "part"       "way"    "lot"   "hours"    "great"     "really"  "driver"  "paid"  
 [5,] "service"     "miles"   "customers"  "good"   "can"   "schedule" "different" "drive"   "they"    "pick"  
 [6,] "also"        "wear"    "hardest"    "extra"  "hours" "you"      "fun"       "just"    "many"    "app"   
 [7,] "the"         "tear"    "passengers" "income" "much"  "able"     "enjoy"     "never"   "riders"  "ride"  
 [8,] "well"        "worth"   "best"       "its"    "days"  "flexible" "love"      "one"     "even"    "take"  
 [9,] "help"        "less"    "enjoyable"  "full"   "long"  "boss"     "city"      "made"    "rating"  "times" 
[10,] "flexibility" "per"     "learned"    "this"   "time"  "place"    "meeting"   "area"    "give"    "just"  
#LDA Model for words associate with positive Revies, that is for reviews with star ratings of 4 or 5
dtm_matrix_lda_positive = as.matrix(dtms_all)
dtm_matrix_lda_positive = dtm_matrix_lda_positive[which(uber_driver_text$positive==TRUE),] #Taking only rows associated with positive sentiment
terms2 = rowSums(dtm_matrix_lda_positive) != 0
dtm_matrix_lda_positive = dtm_matrix_lda_positive[terms2,]
lda_positive <-LDA(dtm_matrix_lda_positive, 9, method="Gibbs", control = list(seed = 1234))
terms(lda_positive,10)
      Topic 1    Topic 2  Topic 3   Topic 4       Topic 5   Topic 6    Topic 7     Topic 8     Topic 9 
 [1,] "work"     "time"   "car"     "great"       "uber"    "good"     "people"    "job"       "get"   
 [2,] "can"      "make"   "pay"     "work"        "driving" "uber"     "new"       "the"       "drive" 
 [3,] "hours"    "money"  "will"    "schedule"    "working" "company"  "meet"      "part"      "just"  
 [4,] "want"     "job"    "vehicle" "love"        "like"    "customer" "different" "day"       "pick"  
 [5,] "you"      "way"    "put"     "able"        "drivers" "also"     "city"      "customers" "take"  
 [6,] "boss"     "great"  "gas"     "flexible"    "really"  "service"  "meeting"   "hardest"   "know"  
 [7,] "like"     "good"   "better"  "flexibility" "however" "driver"   "enjoy"     "around"    "app"   
 [8,] "much"     "extra"  "make"    "place"       "made"    "one"      "fun"       "enjoyable" "riders"
 [9,] "long"     "income" "much"    "working"     "enjoyed" "time"     "lot"       "learned"   "paid"  
[10,] "whenever" "easy"   "week"    "opportunity" "now"     "well"     "many"      "getting"   "ride"  
#LDA Model for words associate with negative Revies, that is for reviews with star ratings of 1 or 2
dtm_matrix_lda_negative = as.matrix(dtms_all)
uber_driver_text$negative = uber_driver_text$stars <= 2 #Creating a row of bad reviews
dtm_matrix_lda_negative = dtm_matrix_lda_negative[which(uber_driver_text$negative==TRUE),] #Taking only the rows with bad reviews
terms3 = rowSums(dtm_matrix_lda_negative) != 0
dtm_matrix_lda_negative = dtm_matrix_lda_negative[terms3,]
lda_negative <-LDA(dtm_matrix_lda_negative,9, method="Gibbs", control = list(seed = 1234))
terms(lda_negative,10)
      Topic 1     Topic 2     Topic 3     Topic 4   Topic 5      Topic 6     Topic 7   Topic 8 Topic 9 
 [1,] "car"       "get"       "uber"      "hours"   "job"        "drivers"   "uber"    "make"  "will"  
 [2,] "pay"       "drive"     "driver"    "driving" "people"     "they"      "working" "money" "get"   
 [3,] "gas"       "customers" "the"       "less"    "time"       "company"   "just"    "work"  "riders"
 [4,] "vehicle"   "time"      "pay"       "per"     "part"       "many"      "never"   "can"   "rating"
 [5,] "miles"     "customer"  "even"      "making"  "the"        "care"      "one"     "you"   "can"   
 [6,] "wear"      "paid"      "way"       "hour"    "income"     "take"      "good"    "much"  "like"  
 [7,] "tear"      "app"       "rate"      "week"    "long"       "business"  "time"    "good"  "bad"   
 [8,] "enough"    "times"     "passenger" "minimum" "well"       "recommend" "account" "want"  "always"
 [9,] "put"       "also"      "trip"      "wage"    "management" "anyone"    "now"     "lot"   "give"  
[10,] "insurance" "pick"      "city"      "days"    "full"       "one"       "really"  "this"  "rides" 
#Looking at the words associated with positive reviews using beta analysis
positive_words <- tidy(lda_positive, matrix = "beta")
postive_top_terms <- positive_words %>%
  group_by(topic) %>%
  top_n(10, beta) %>%
  ungroup() %>%
  arrange(topic, -beta)
postive_top_terms %>%
  mutate(term = reorder(term, beta)) %>%
  ggplot(aes(term, beta, fill = factor(topic))) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~ topic, scales = "free") +
  coord_flip()

#Looking at the words associated with negative reviews using beta analysis
negative_words <- tidy(lda_negative, matrix = "beta")
negative_top_terms <- negative_words %>%
  group_by(topic) %>%
  top_n(10, beta) %>%
  ungroup() %>%
  arrange(topic, -beta)
negative_top_terms %>%
  mutate(term = reorder(term, beta)) %>%
  ggplot(aes(term, beta, fill = factor(topic))) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~ topic, scales = "free") +
  coord_flip()

#The following code snippet reads the number of reviews on the website for the lyft page
number_lyft = read_html(paste("https://www.indeed.com/cmp/Lyft/reviews?fjobtitle=Driver&start=0")) %>%
  html_nodes("b") %>%
  html_text()
number_lyft = gsub(pattern = ",", replacement = "", number_lyft) #removing the comma so it can be transformed into a number
number_lyft = as.numeric(as.character(number_lyft))
#Scraping the tite of the review, the text, the date, and the number of stars given and putting it into a data frame
page = seq(from = 0, to = number_lyft, by = 20) #Deriving the number of pages to be considered
lyft_drivers = NULL #Creating an empty data frame
for (start in (page)) { # For loop to read in the file and append it to a "running total dataframe" called articles}
 na = c(NA, NA,NA,NA, NA,NA,NA, NA,NA,NA, NA,NA,NA, NA,NA,NA, NA,NA,NA, NA)
   title = read_html(paste("https://www.indeed.com/cmp/Lyft/reviews?fjobtitle=Driver&start=", start, sep = '')) %>%
    html_nodes("div[itemprop='review']:nth-of-type(n+2) .cmp-review-title span:nth-of-type(1)") %>%
    html_text()
#print(title)
   date = read_html(paste("https://www.indeed.com/cmp/Lyft/reviews?fjobtitle=Driver&start=", start, sep = '')) %>%
    html_nodes("div[itemprop='review']:nth-of-type(n+2) span.cmp-review-date-created") %>%
    html_text()
 #print(date)
 review = read_html(paste("https://www.indeed.com/cmp/Lyft/reviews?fjobtitle=Driver&start=", start, sep = '')) %>%
   html_nodes("div[itemprop='review']:nth-of-type(n+2) span[itemprop='reviewBody']") %>%
   html_text()
 #print(review)
 
 stars = read_html(paste("https://www.indeed.com/cmp/Lyft/reviews?fjobtitle=Driver&start=", start, sep = '')) %>%
   html_nodes("div[itemprop='review']:nth-of-type(n+2) div.cmp-ratingNumber") %>%
   html_text()
    stars = gsub(pattern = "", replacement = "", stars)
    stars = as.numeric(stars)
 #print(stars)
    
     city = read_html(paste("https://www.indeed.com/cmp/Lyft/reviews?fjobtitle=Driver&start=", start, sep = '')) %>%
   html_nodes("div[itemprop='review']:nth-of-type(n+2) span.cmp-reviewer-job-location") %>%
   html_text()
    
    
#The web scraper about 5 times will break if it only finds 19 elements on the page instead of 20.  The following set of if statements allows for loop to run even if this occurr by inserting a string of NA's when Web scraper doesn't return the correct number of values
  if(length(title == 20))  {
  title = title
} else {
  title = na
} 
 
if(length(date == 20))  {
  date = date
} else {
  date = na
} 
 
if(length(review == 20))  {
  review = review
} else {
  review = na
}  
 
 if(length(stars == 20))  {
  stars = stars
} else {
  stars = na
} 
     
if(length(city == 20))  {
  city = city
} else {
  title = na
} 
 
temp = data.frame(title, review, date, stars, city)
lyft_drivers = rbind(lyft_drivers, temp)
}
lyft_drivers = na.omit(lyft_drivers)
lyft_drivers$position = "driver"
lyft_drivers$date = str_replace_all(lyft_drivers$date, ",", "")
lyft_drivers$date = as.Date(lyft_drivers$date, format = "%B %d %Y") #Converting it to date format
lyft_drivers$sentiment = as.numeric(get_sentiment(as.character(lyft_drivers$review))) #Getting sentiment
lyft_drivers2 = lyft_drivers %>%
  group_by(date) %>%
  summarise(mean(sentiment))
names(lyft_drivers2) = c("date", "sentiment")
lyft_drivers_monthly = lyft_drivers2 %>% #Aggregating data into month bins
    group_by(month=floor_date(date, "month")) %>%
    summarise(sentiment = mean(sentiment))
lyft_drivers_monthly %>% ggplot(aes(x=month, y=sentiment)) + geom_col(color="pink") + xlab("Sequence") +
    ylab("Sentiment") + ggtitle("Lyft Driver Sentiment") +
    theme(plot.title = element_text(hjust = 0.5)) + geom_smooth(method = "auto") +xlab("Date")

# Area plot
uber_drivers_monthly$type=NULL
uber_drivers_monthly$company = "uber"
lyft_drivers_monthly$company = "lyft"
lyft_v_uber = rbind(uber_drivers_monthly, lyft_drivers_monthly)
ggplot(lyft_v_uber, aes(x = month, y = sentiment)) + 
  geom_area(aes(color = company, fill = company), 
            alpha = 0.3, position = position_dodge(0.8)) + xlab("Date") + ylab("Sentiment") + ggtitle("Lyft versus Uber", subtitle = "Driver Sentiment over Time")+ scale_x_date(limits = as.Date(c("2014-05-01","2019-05-05")))

Lyft Best Cities Old

lyft_driver_city = lyft_drivers %>%
  group_by(city) %>%
  summarise(mean(sentiment))

names(lyft_driver_city) = c("city", "sentiment")
lyft_driver_city =lyft_driver_city %>%
    arrange(desc(sentiment))


lyft_driver_city$city2 = gsub(pattern = ",", replacement = "", lyft_driver_city$city) #Taking out the comma to make it readable to google
lyft_driver_city <- lyft_driver_city %>% 
  mutate_geocode(city2) #Adding a lat/lon column for each city
lyft_driver_city2 = lyft_driver_city #Duplicating the dataframe as it takes a long time for google to geocode
latlong2state <- function(pointsDF) { #Convets all the coordinates back to state codes
    # Prepare SpatialPolygons object with one SpatialPolygon
    # per state (plus DC, minus HI & AK)
    states <- map('state', fill=TRUE, col="transparent", plot=FALSE)
    IDs <- sapply(strsplit(states$names, ":"), function(x) x[1])
    states_sp <- map2SpatialPolygons(states, IDs=IDs,
                     proj4string=CRS("+proj=longlat +datum=wgs84"))
    # Convert pointsDF to a SpatialPoints object 
    pointsSP <- SpatialPoints(pointsDF, 
                    proj4string=CRS("+proj=longlat +datum=wgs84"))
    # Use 'over' to get _indices_ of the Polygons object containing each point 
    indices <- over(pointsSP, states_sp)
    # Return the state names of the Polygons object containing each point
    stateNames <- sapply(states_sp@polygons, function(x) x@ID)
    stateNames[indices]
}
lyft_driver_city3 = lyft_driver_city2
lyft_driver_city3 = na.omit(lyft_driver_city3) #Removing NA values
lyft_driver_city3$state = latlong2state(lyft_driver_city3[4:5]) #Adding a column of state codes
lyft_driver_city3 = na.omit(lyft_driver_city3)#Removing NA values
states = read.csv("statelatlong.csv") #A data fame with states and state codes
states = states[, c(-2,-3)]
states$City = tolower(states$City) #converting state names to lowercase
lyft_driver_city4 = left_join(lyft_driver_city3, states, by = c("state" ="City")) #AAssociating each state with a code to be read into plotly

Lyft Driver Best Cities

lyft_driver_city5 = lyft_driver_city4 %>% #Creating a data frame grrouping by state and finding the mean sentiment for drivers
  group_by(State) %>%
  summarise(mean(sentiment)) 
names(lyft_driver_city5) = c("State", "Sentiment")
# give state boundaries a white border
l <- list(color = toRGB("white"), width = 2)
# specify some map projection/options
lyft_map <- list(
  scope = 'usa',
  projection = list(type = 'albers usa'),
  showlakes = TRUE,
  lakecolor = toRGB('lightblue'))
lyft_state_map <- plot_geo(lyft_driver_city5, locationmode = 'USA-states') %>%
  add_trace(
    z = ~Sentiment, locations = ~State,
    color = ~Sentiment, colors = 'Reds'
  ) %>%
  colorbar(title = "Sentiment Level") %>%
  layout(
    title = 'Historical lyft Driver Sentiment by State<br>(Hover for breakdown)',
    geo = map
  )
lyft_state_map

#chart_link_lyft = api_create(lyft_state_map, filename="lyft_choropleth")
#chart_link_lyft #Exporting the Lyft state map to be embedded

Performing a two sample t-test to see if sentiment amongst uber drivers was better than that amongsth lyft drivers

t.test(uber_drivers$sentiment, lyft_drivers$sentiment, paired = FALSE, alternative = "two.sided")
argument is not numeric or logical: returning NAError in var(x) : 'x' is NULL

Perfirming a t-test between uber drivers before and after the CEO switch

t.test(uber_drivers_before$sentiment, uber_drivers_after$sentiment, paired = FALSE, alternative = "two.sided")

    Welch Two Sample t-test

data:  uber_drivers_before$sentiment and uber_drivers_after$sentiment
t = 3.1033, df = 4805.1, p-value = 0.001925
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 0.05645862 0.25015230
sample estimates:
mean of x mean of y 
 1.995271  1.841965 

Perfirming a t-test between uber **Employees before and after the CEO switch

t.test(uber_employees_before$sentiment, uber_employees_after$sentiment, paired = FALSE, alternative = "two.sided")

    Welch Two Sample t-test

data:  uber_employees_before$sentiment and uber_employees_after$sentiment
t = 2.1398, df = 199.53, p-value = 0.03358
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 0.04288492 1.05027848
sample estimates:
mean of x mean of y 
 2.862381  2.315799 
t.test(uber_drivers$sentiment, uber_employees$sentiment, paired = FALSE, alternative = "two.sided")
argument is not numeric or logical: returning NAError in var(x) : 'x' is NULL

Looking to see if there is a correlation betwee

minimum_wage = read.csv("Minimum Wage Data.csv")
minimum_wage$State = tolower(minimum_wage$State)
minimum_wage_avg = minimum_wage %>%
  filter(Year == 2012 | Year == 2013 |Year == 2014 |Year == 2015 |Year == 2016 |Year == 2017 |Year == 2018 |Year == 2019) %>%
  group_by(State) %>%
  summarise(mean(High.Value))
minimum_wage_avg  = left_join(minimum_wage_avg, states, by = c("State" ="City"))
minimum_wage_avg = na.omit(minimum_wage_avg)
collective = left_join(uber_driver_city5, minimum_wage_avg, by = c("State" ="State.y"))
names(collective) = c("Code", "Sentiment", "State", "MinWage")
sentiment_wage_correlation = cor(collective$Sentiment, collective$MinWage) #Looking at the correlation between sentiment and minimum wage.  A negative correlation is expected, because the higher the minimum wage, the more discontent the driver would be by riding with uber
sentiment_wage_correlation #Looking at the correlation of state minimum wage and sentiment
[1] -0.159709

Creating a Scatterplot

ggscatter(collective, x = "MinWage", y = "Sentiment", 
          add = "reg.line", conf.int = TRUE, 
          cor.coef = TRUE, cor.method = "pearson",
          xlab = "Minimum Wage", ylab = "Sentiment") 

The following block of code looked to see if variables by in census data were able to predict sentiment by state. Unfortunately, I couldn’t build the model because the census metrics weren’t as relevant as I thought

census = read.csv("acs2017_county_data.csv")

census_state = census %>% #Collapsing by state and computing the average of several potentially important metrics by state
  group_by(State) %>%
  summarise(median(Income), mean(IncomePerCap), mean(Poverty), mean(Professional), mean(Service), mean(Office), mean(Office), mean(Construction), mean(Production), mean(Drive), mean(Carpool), mean(Transit), mean(Walk), mean(WorkAtHome), mean(MeanCommute), mean(Employed), mean(Unemployment))

census_state$State = tolower(census_state$State) #Making state names lowercase
census_state  = left_join(census_state, states, by = c("State" ="City")) #Joining with state code
census_state = na.omit(census_state) #Removing NA values
names(census_state)[18] = c("State_Code")

census_uber_state = left_join(uber_driver_city5, census_state, by = c("State" ="State_Code")) #Merging uber driver sentiment and other census metrics

census_uber_state = census_uber_state[,-3] #Removing the actual state names
getwd()
write.csv(census_uber_state, file = "uber.csv",row.names=TRUE)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKTW9zdCBvZiB0aGVzZSBsaWJyYXJpZXMgaGF2ZSBiZWVuIHVzZWQgc29tZW9uZSBvciBhbm90aGVyIGluIHRoZSBjb2RlLiAgU29tZSB3ZXJlIGxlZnQgb3V0IGhvd2V2ZXIuCmBgYHtyfQojTmVjZXNzYXJ5IExpYnJhcmllcwpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkoeHRzKQpsaWJyYXJ5KHRvcGljbW9kZWxzKSAKbGlicmFyeShwbG90bHkpCmxpYnJhcnkodG0pCmxpYnJhcnkoc2VudGltZW50cikKbGlicmFyeShkcGx5cikKbGlicmFyeShzeXV6aGV0KQpsaWJyYXJ5KHJ2ZXN0KQpsaWJyYXJ5KGh0dHIpCmxpYnJhcnkobWFncml0dHIpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeSh0bSkKbGlicmFyeSh0aWJibGUpCmxpYnJhcnkocnZlc3QpCmxpYnJhcnkodGlkeXRleHQpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeSh3b3JkY2xvdWQpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkodXNtYXApCmxpYnJhcnkobWFwcykKbGlicmFyeShnZ21hcCkKbGlicmFyeShnb29nbGVBdXRoUikKbGlicmFyeShzcCkKbGlicmFyeShtYXBzKQpsaWJyYXJ5KG1hcHRvb2xzKQpsaWJyYXJ5KHJldmdlbykKbGlicmFyeShjaG9yb3BsZXRock1hcHMpCmxpYnJhcnkobWFwcHJvaikKbGlicmFyeShTbm93YmFsbEMpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpgYGAKClRoZSBmb2xsb3dpbmcgY29kZSBzbmlwcGV0IHJlYWRzIHRoZSBudW1iZXIgb2YgcmV2aWV3cyBvbiB0aGUgd2Vic2l0ZSBmb3IgdGhlIHViZXIgcGFydG5lciBkcml2ZXJzIHNpdGUKYGBge3J9Cm51bWJlciA9IHJlYWRfaHRtbChwYXN0ZSgiaHR0cHM6Ly93d3cuaW5kZWVkLmNvbS9jbXAvVWJlci1QYXJ0bmVyLURyaXZlcnMvcmV2aWV3cz9zdGFydD0wIikpICU+JQogIGh0bWxfbm9kZXMoImIiKSAlPiUKICBodG1sX3RleHQoKQpudW1iZXIgPSBnc3ViKHBhdHRlcm4gPSAiLCIsIHJlcGxhY2VtZW50ID0gIiIsIG51bWJlcikgI3JlbW92aW5nIHRoZSBjb21tYSBzbyBpdCBjYW4gYmUgdHJhbnNmb3JtZWQgaW50byBhIG51bWJlcgpudW1iZXIgPSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihudW1iZXIpKQpgYGAKClNjcmFwaW5nIHRoZSB0aXRlIG9mIHRoZSByZXZpZXcsIHRoZSB0ZXh0LCB0aGUgZGF0ZSwgYW5kIHRoZSBudW1iZXIgb2Ygc3RhcnMgZ2l2ZW4gYW5kIHB1dHRpbmcgaXQgaW50byBhIGRhdGEgZnJhbWUKYGBge3J9CnBhZ2UgPSBzZXEoZnJvbSA9IDAsIHRvID0gbnVtYmVyLCBieSA9IDIwKSAjRGVyaXZpbmcgdGhlIG51bWJlciBvZiBwYWdlcyB0byBiZSBjb25zaWRlcmVkCgp1YmVyX2RyaXZlcnMgPSBOVUxMICNDcmVhdGluZyBhbiBlbXB0eSBkYXRhIGZyYW1lCgpmb3IgKHN0YXJ0IGluIChwYWdlKSkgeyAjIEZvciBsb29wIHRvIHJlYWQgaW4gdGhlIGZpbGUgYW5kIGFwcGVuZCBpdCB0byBhICJydW5uaW5nIHRvdGFsIGRhdGFmcmFtZSIgY2FsbGVkIGFydGljbGVzfQogbmEgPSBjKE5BLCBOQSxOQSxOQSwgTkEsTkEsTkEsIE5BLE5BLE5BLCBOQSxOQSxOQSwgTkEsTkEsTkEsIE5BLE5BLE5BLCBOQSkKICAgdGl0bGUgPSByZWFkX2h0bWwocGFzdGUoImh0dHBzOi8vd3d3LmluZGVlZC5jb20vY21wL1ViZXItUGFydG5lci1Ecml2ZXJzL3Jldmlld3M/c3RhcnQ9Iiwgc3RhcnQsIHNlcCA9ICcnKSkgJT4lCiAgICBodG1sX25vZGVzKCJkaXZbaXRlbXByb3A9J3JldmlldyddOm50aC1vZi10eXBlKG4rMikgLmNtcC1yZXZpZXctdGl0bGUgc3BhbjpudGgtb2YtdHlwZSgxKSIpICU+JQogICAgaHRtbF90ZXh0KCkKI3ByaW50KHRpdGxlKQoKICAgZGF0ZSA9IHJlYWRfaHRtbChwYXN0ZSgiaHR0cHM6Ly93d3cuaW5kZWVkLmNvbS9jbXAvVWJlci1QYXJ0bmVyLURyaXZlcnMvcmV2aWV3cz9zdGFydD0iLCBzdGFydCwgc2VwID0gJycpKSAlPiUKICAgIGh0bWxfbm9kZXMoImRpdltpdGVtcHJvcD0ncmV2aWV3J106bnRoLW9mLXR5cGUobisyKSBzcGFuLmNtcC1yZXZpZXctZGF0ZS1jcmVhdGVkIikgJT4lCiAgICBodG1sX3RleHQoKQogI3ByaW50KGRhdGUpCgogcmV2aWV3ID0gcmVhZF9odG1sKHBhc3RlKCJodHRwczovL3d3dy5pbmRlZWQuY29tL2NtcC9VYmVyLVBhcnRuZXItRHJpdmVycy9yZXZpZXdzP3N0YXJ0PSIsIHN0YXJ0LCBzZXAgPSAnJykpICU+JQogICBodG1sX25vZGVzKCJkaXZbaXRlbXByb3A9J3JldmlldyddOm50aC1vZi10eXBlKG4rMikgc3BhbltpdGVtcHJvcD0ncmV2aWV3Qm9keSddIikgJT4lCiAgIGh0bWxfdGV4dCgpCiAjcHJpbnQocmV2aWV3KQogCiBzdGFycyA9IHJlYWRfaHRtbChwYXN0ZSgiaHR0cHM6Ly93d3cuaW5kZWVkLmNvbS9jbXAvVWJlci1QYXJ0bmVyLURyaXZlcnMvcmV2aWV3cz9zdGFydD0iLCBzdGFydCwgc2VwID0gJycpKSAlPiUKICAgaHRtbF9ub2RlcygiZGl2W2l0ZW1wcm9wPSdyZXZpZXcnXTpudGgtb2YtdHlwZShuKzIpIGRpdi5jbXAtcmF0aW5nTnVtYmVyIikgJT4lCiAgIGh0bWxfdGV4dCgpCiAgICBzdGFycyA9IGdzdWIocGF0dGVybiA9ICIiLCByZXBsYWNlbWVudCA9ICIiLCBzdGFycykKICAgIHN0YXJzID0gYXMubnVtZXJpYyhzdGFycykKICNwcmludChzdGFycykKICAgIAogIGNpdHkgPSByZWFkX2h0bWwocGFzdGUoImh0dHBzOi8vd3d3LmluZGVlZC5jb20vY21wL1ViZXItUGFydG5lci1Ecml2ZXJzL3Jldmlld3M/c3RhcnQ9Iiwgc3RhcnQsIHNlcCA9ICcnKSkgJT4lCiAgIGh0bWxfbm9kZXMoImRpdltpdGVtcHJvcD0ncmV2aWV3J106bnRoLW9mLXR5cGUobisyKSBzcGFuLmNtcC1yZXZpZXdlci1qb2ItbG9jYXRpb24iKSAlPiUKICAgaHRtbF90ZXh0KCkKICAKCiNUaGUgd2ViIHNjcmFwZXIgYWJvdXQgNSB0aW1lcyB3aWxsIGJyZWFrIGlmIGl0IG9ubHkgZmluZHMgMTkgZWxlbWVudHMgb24gdGhlIHBhZ2UgaW5zdGVhZCBvZiAyMC4gIFRoZSBmb2xsb3dpbmcgc2V0IG9mIGlmIHN0YXRlbWVudHMgYWxsb3dzIGZvciBsb29wIHRvIHJ1biBldmVuIGlmIHRoaXMgb2NjdXJyIGJ5IGluc2VydGluZyBhIHN0cmluZyBvZiBOQSdzIHdoZW4gV2ViIHNjcmFwZXIgZG9lc24ndCByZXR1cm4gdGhlIGNvcnJlY3QgbnVtYmVyIG9mIHZhbHVlcwogIGlmKGxlbmd0aCh0aXRsZSA9PSAyMCkpICB7CiAgdGl0bGUgPSB0aXRsZQp9IGVsc2UgewogIHRpdGxlID0gbmEKfSAKIAppZihsZW5ndGgoZGF0ZSA9PSAyMCkpICB7CiAgZGF0ZSA9IGRhdGUKfSBlbHNlIHsKICBkYXRlID0gbmEKfSAKIAppZihsZW5ndGgocmV2aWV3ID09IDIwKSkgIHsKICByZXZpZXcgPSByZXZpZXcKfSBlbHNlIHsKICByZXZpZXcgPSBuYQp9ICAKIAogaWYobGVuZ3RoKHN0YXJzID09IDIwKSkgIHsKICBzdGFycyA9IHN0YXJzCn0gZWxzZSB7CiAgc3RhcnMgPSBuYQp9IAogICAgCmlmKGxlbmd0aChjaXR5ID09IDIwKSkgIHsKICBjaXR5ID0gY2l0eQp9IGVsc2UgewogIGNpdHkgPSBuYQp9IAoKdGVtcCA9IGRhdGEuZnJhbWUodGl0bGUsIHJldmlldywgZGF0ZSwgc3RhcnMsIGNpdHkpCnViZXJfZHJpdmVycyA9IHJiaW5kKHViZXJfZHJpdmVycywgdGVtcCkKfQoKdWJlcl9kcml2ZXJzID0gbmEub21pdCh1YmVyX2RyaXZlcnMpCnViZXJfZHJpdmVycyRwb3NpdGlvbiA9ICJkcml2ZXIiCgp1YmVyX2RyaXZlcnMkZGF0ZSA9IHN0cl9yZXBsYWNlX2FsbCh1YmVyX2RyaXZlcnMkZGF0ZSwgIiwiLCAiIikKdWJlcl9kcml2ZXJzJGRhdGUgPSBhcy5EYXRlKHViZXJfZHJpdmVycyRkYXRlLCBmb3JtYXQgPSAiJUIgJWQgJVkiKSAjQ29udmVydGluZyBpdCB0byBkYXRlIGZvcm1hdApgYGAKClNlbnRpbWVudCBhbmFseXNpcyBmb3IgdWJlciBkcml2ZXJzLCBncm91cGluZyBieSBtb250aCwgYW5kIHBsb3R0aW5nIG92ZXIgdGltZQpgYGB7cn0KdWJlcl9kcml2ZXJzJHNlbnRpbWVudCA9IGFzLm51bWVyaWMoZ2V0X3NlbnRpbWVudChhcy5jaGFyYWN0ZXIodWJlcl9kcml2ZXJzJHJldmlldykpKSAjR2V0dGluZyBzZW50aW1lbnQKCnViZXJfZHJpdmVyczIgPSB1YmVyX2RyaXZlcnMgJT4lCiAgZ3JvdXBfYnkoZGF0ZSkgJT4lCiAgc3VtbWFyaXNlKG1lYW4oc2VudGltZW50KSkKCm5hbWVzKHViZXJfZHJpdmVyczIpID0gYygiZGF0ZSIsICJzZW50aW1lbnQiKQoKI3ViZXJfZHJpdmVyczIgJT4lIGdncGxvdChhZXMoeD1kYXRlLCB5PXNlbnRpbWVudCkpICsgZ2VvbV9jb2woY29sb3I9InJlZCIpICsgeGxhYigiU2VxdWVuY2UiKSArCiAgICAjeWxhYigiU2VudGltZW50IikgKyBnZ3RpdGxlKCJEcml2ZXIgU2VudGltZW50IikgKwogICAgI3RoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJhdXRvIikgKyBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBhcy5udW1lcmljKHViZXJfZHJpdmVycyRkYXRlWzMyNjddKSkKCnViZXJfZHJpdmVyc19tb250aGx5ID0gdWJlcl9kcml2ZXJzMiAlPiUgI0FnZ3JlZ2F0aW5nIGRhdGEgaW50byBtb250aCBiaW5zCiAgICBncm91cF9ieShtb250aD1mbG9vcl9kYXRlKGRhdGUsICJtb250aCIpKSAlPiUKICAgIHN1bW1hcmlzZShzZW50aW1lbnQgPSBtZWFuKHNlbnRpbWVudCkpCgp1YmVyX2RyaXZlcnNfbW9udGhseSAlPiUgZ2dwbG90KGFlcyh4PW1vbnRoLCB5PXNlbnRpbWVudCkpICsgZ2VvbV9jb2woY29sb3I9ImJsYWNrIikgKyB4bGFiKCJTZXF1ZW5jZSIpICsKICAgIHlsYWIoIlNlbnRpbWVudCIpICsgZ2d0aXRsZSgiRHJpdmVyIFNlbnRpbWVudCIpICsKICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjQpKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJhdXRvIikgKyBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBhcy5udW1lcmljKHViZXJfZHJpdmVycyRkYXRlWzMyNjddKSkgKyBnZ3RpdGxlKCJTZW50aW1lbnQgb3ZlciBUaW1lIGZvciBVYmVyIERyaXZlcnMiKSAKYGBgCgoKVGhlIGZvbGxvd2luZyBjb2RlIHNuaXBwZXQgcmVhZHMgdGhlIG51bWJlciBvZiByZXZpZXdzIG9uIHRoZSB3ZWJzaXRlIGZvciB0aGUgdWJlciBwYWdlCmBgYHtyfQpudW1iZXJfZW1wbG95ZWVzID0gcmVhZF9odG1sKHBhc3RlKCJodHRwczovL3d3dy5pbmRlZWQuY29tL2NtcC9VYmVyL3Jldmlld3M/c3RhcnQ9MCIpKSAlPiUKICBodG1sX25vZGVzKCJiIikgJT4lCiAgaHRtbF90ZXh0KCkKbnVtYmVyX2VtcGxveWVlcyA9IGdzdWIocGF0dGVybiA9ICIsIiwgcmVwbGFjZW1lbnQgPSAiIiwgbnVtYmVyX2VtcGxveWVlcykgI3JlbW92aW5nIHRoZSBjb21tYSBzbyBpdCBjYW4gYmUgdHJhbnNmb3JtZWQgaW50byBhIG51bWJlcgpudW1iZXJfZW1wbG95ZWVzID0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIobnVtYmVyX2VtcGxveWVlcykpCgpwYWdlMiA9IHNlcShmcm9tID0gMCwgdG8gPSAobnVtYmVyX2VtcGxveWVlcyksIGJ5ID0gMjApCgp1YmVyX2VtcGxveWVlcyAgPSBOVUxMICNDcmVhdGluZyBhbiBlbXB0eSBkYXRhIGZyYW1lCgpmb3IgKHN0YXJ0IGluIChwYWdlMikpIHsgIyBGb3IgbG9vcCB0byByZWFkIGluIHRoZSBmaWxlIGFuZCBhcHBlbmQgaXQgdG8gYSAicnVubmluZyB0b3RhbCBkYXRhZnJhbWUiIGNhbGxlZCBhcnRpY2xlc30KIG5hID0gYyhOQSwgTkEsTkEsTkEsIE5BLE5BLE5BLCBOQSxOQSxOQSwgTkEsTkEsTkEsIE5BLE5BLE5BLCBOQSxOQSxOQSwgTkEpCiAgCiAgdGl0bGUgPSByZWFkX2h0bWwocGFzdGUoImh0dHBzOi8vd3d3LmluZGVlZC5jb20vY21wL1ViZXIvcmV2aWV3cz9zdGFydD0iLCBzdGFydCwgc2VwID0gJycpKSAlPiUKICAgIGh0bWxfbm9kZXMoImRpdltpdGVtcHJvcD0ncmV2aWV3J106bnRoLW9mLXR5cGUobisyKSAuY21wLXJldmlldy10aXRsZSBzcGFuOm50aC1vZi10eXBlKDEpIikgJT4lCiAgICBodG1sX3RleHQoKQogICNwcmludCh0aXRsZSkKCiAgIGRhdGUgPSByZWFkX2h0bWwocGFzdGUoImh0dHBzOi8vd3d3LmluZGVlZC5jb20vY21wL1ViZXIvcmV2aWV3cz9zdGFydD0iLCBzdGFydCwgc2VwID0gJycpKSAlPiUKICAgIGh0bWxfbm9kZXMoImRpdltpdGVtcHJvcD0ncmV2aWV3J106bnRoLW9mLXR5cGUobisyKSBzcGFuLmNtcC1yZXZpZXctZGF0ZS1jcmVhdGVkIikgJT4lCiAgICBodG1sX3RleHQoKQogI3ByaW50KGRhdGUpCgogcmV2aWV3ID0gcmVhZF9odG1sKHBhc3RlKCJodHRwczovL3d3dy5pbmRlZWQuY29tL2NtcC9VYmVyL3Jldmlld3M/c3RhcnQ9Iiwgc3RhcnQsIHNlcCA9ICcnKSkgJT4lCiAgIGh0bWxfbm9kZXMoImRpdltpdGVtcHJvcD0ncmV2aWV3J106bnRoLW9mLXR5cGUobisyKSBzcGFuW2l0ZW1wcm9wPSdyZXZpZXdCb2R5J10iKSAlPiUKICAgaHRtbF90ZXh0KCkKICNwcmludChyZXZpZXcpCiAKIHN0YXJzID0gcmVhZF9odG1sKHBhc3RlKCJodHRwczovL3d3dy5pbmRlZWQuY29tL2NtcC9VYmVyL3Jldmlld3M/c3RhcnQ9Iiwgc3RhcnQsIHNlcCA9ICcnKSkgJT4lCiAgIGh0bWxfbm9kZXMoImRpdltpdGVtcHJvcD0ncmV2aWV3J106bnRoLW9mLXR5cGUobisyKSBkaXYuY21wLXJhdGluZ051bWJlciIpICU+JQogICBodG1sX3RleHQoKQogICAgc3RhcnMgPSBnc3ViKHBhdHRlcm4gPSAiIiwgcmVwbGFjZW1lbnQgPSAiIiwgc3RhcnMpCiAgICBzdGFycyA9IGFzLm51bWVyaWMoc3RhcnMpCiAjcHJpbnQoc3RhcnMpCiAgICAKICAgIHBvc2l0aW9uID0gcmVhZF9odG1sKHBhc3RlKCJodHRwczovL3d3dy5pbmRlZWQuY29tL2NtcC9VYmVyL3Jldmlld3M/c3RhcnQ9Iiwgc3RhcnQsIHNlcCA9ICcnKSkgJT4lCiAgIGh0bWxfbm9kZXMoImRpdltpdGVtcHJvcD0ncmV2aWV3J106bnRoLW9mLXR5cGUobisyKSBzcGFuLmNtcC1yZXZpZXdlciIpICU+JQogICBodG1sX3RleHQoKQogICAgcG9zaXRpb24gPSBnc3ViKHBhdHRlcm4gPSAiIiwgcmVwbGFjZW1lbnQgPSAiIiwgcG9zaXRpb24pCiAgI3ByaW50KHBvc2l0aW9uKSAgCgogIGlmKGxlbmd0aCh0aXRsZSA9PSAyMCkpICB7CiAgdGl0bGUgPSB0aXRsZQp9IGVsc2UgewogIHRpdGxlID0gbmEKfSAKIAppZihsZW5ndGgoZGF0ZSA9PSAyMCkpICB7CiAgZGF0ZSA9IGRhdGUKfSBlbHNlIHsKICBkYXRlID0gbmEKfSAKIAppZihsZW5ndGgocmV2aWV3ID09IDIwKSkgIHsKICByZXZpZXcgPSByZXZpZXcKfSBlbHNlIHsKICByZXZpZXcgPSBuYQp9ICAKIAppZihsZW5ndGgoc3RhcnMgPT0gMjApKSAgewogIHN0YXJzID0gc3RhcnMKfSBlbHNlIHsKICBzdGFycyA9IG5hCn0gCmlmKGxlbmd0aChwb3NpdGlvbiA9PSAyMCkpICB7CiAgcG9zaXRpb24gPSBwb3NpdGlvbgp9IGVsc2UgewogIHBvc2l0aW9uID0gbmEKfSAKIAp0ZW1wID0gZGF0YS5mcmFtZSh0aXRsZSwgcmV2aWV3LCBkYXRlLCBzdGFycywgcG9zaXRpb24pCnViZXJfZW1wbG95ZWVzID0gcmJpbmQodWJlcl9lbXBsb3llZXMsIHRlbXApCn0KCmBgYAoKCkNvbnZlcnRpbmcgdGhlIGRhdGVzIHRvIGJlIHJlYWQgaW4gZGF0ZSBmb3JtYXQKYGBge3J9CnViZXJfZW1wbG95ZWVzID0gbmEub21pdCh1YmVyX2VtcGxveWVlcykKCnViZXJfZW1wbG95ZWVzJGRhdGU9IHN0cl9yZXBsYWNlX2FsbCh1YmVyX2VtcGxveWVlcyRkYXRlLCAiLCIsICIiKQp1YmVyX2VtcGxveWVlcyRkYXRlID0gYXMuRGF0ZSh1YmVyX2VtcGxveWVlcyRkYXRlLCBmb3JtYXQgPSAiJUIgJWQgJVkiKSAjQ29udmVydGluZyBpdCB0byBkYXRlIGZvcm1hdApgYGAKUGxvdHRpbmcgdGhlIHNlbnRpbWVudCBmb3IgdWJlciBkcml2ZXJzIG92ZXIgdGltZQoKClNlbnRpbWVudCBhbmFseXNpcyBmb3IgdWJlciBlbXBsb3llZXMgYW5kIHRoZW4gZ3JvdXBpbmcgYnkgbW9udGggYW5kIHBsb3R0aW5nIG92ZXIgdGltZQpgYGB7cn0KdWJlcl9lbXBsb3llZXMkc2VudGltZW50ID1hcy5udW1lcmljKGdldF9zZW50aW1lbnQoYXMuY2hhcmFjdGVyKHViZXJfZW1wbG95ZWVzJHJldmlldykpKSAjR2V0dGluZyBzZW50aW1lbnQKCnViZXJfZW1wbG95ZWVzMiA9IHViZXJfZW1wbG95ZWVzICU+JQogICNtdXRhdGUobW9udGggPSBmb3JtYXQoZGF0ZSwgIiVtIiksIHllYXIgPSBmb3JtYXQoZGF0ZSwgIiVZIikpICU+JQogIGdyb3VwX2J5KGRhdGUpICU+JQogIHN1bW1hcmlzZShtZWFuKHNlbnRpbWVudCkpCiMjdWJlcl9lbXBsb3llZXMyJGRhdGUgPSBhcy5EYXRlKHViZXJfZW1wbG95ZWVzMiRkYXRlLCBmb3JtYXQgPSAiJU0gJVkiKQoKbmFtZXModWJlcl9lbXBsb3llZXMyKSA9IGMoImRhdGUiLCAic2VudGltZW50IikKCgp1YmVyX2VtcGxveWVlc19tb250aGx5ID0gdWJlcl9lbXBsb3llZXMyICU+JSAjQWdncmVnYXRpbmcgZGF0YSBpbnRvIG1vbnRoIGJpbnMKICAgIGdyb3VwX2J5KG1vbnRoPWZsb29yX2RhdGUoZGF0ZSwgIm1vbnRoIikpICU+JQogICAgc3VtbWFyaXNlKHNlbnRpbWVudCA9IG1lYW4oc2VudGltZW50KSkKCnViZXJfZW1wbG95ZWVzX21vbnRobHkgJT4lIGdncGxvdChhZXMoeD1tb250aCwgeT1zZW50aW1lbnQpKSArIGdlb21fY29sKGNvbG9yPSJ3aGl0ZSIpICsgeGxhYigiRGF0ZSIpICsKICAgIHlsYWIoIlNlbnRpbWVudCIpICsgZ2d0aXRsZSgiRW1wbG95ZWVTZW50aW1lbnQiKSArCiAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKyBnZW9tX3Ntb290aChtZXRob2QgPSAiYXV0byIpICArIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGFzLm51bWVyaWModWJlcl9kcml2ZXJzJGRhdGVbMzI2N10pKSArIGdndGl0bGUoIlNlbnRpbWVudCBvdmVyIFRpbWUgZm9yIFViZXIgRW1wbG95ZWVzIikKYGBgCgpVYmVyIERyaXZlcnMgdmVyc3VzIEVtcGxveWVlcyBTZW50aW1lbnQKYGBge3J9CiMgQXJlYSBwbG90CnViZXJfZHJpdmVyc19tb250aGx5JHR5cGUgPSAiZHJpdmVyIiAjQWRkaW5nIHRoZSBmYWN0IHRoYXQgdGhlIHJldmlldyBjb21lcyBmcm9tIGEgZHJpdmVyCnViZXJfZW1wbG95ZWVzX21vbnRobHkkdHlwZSA9ICJlbXBsb3llZSIgI0FkZGluZyB0aGUgZmFjdCB0aGF0IHRoZSByZXZpZXcgY29tZXMgZnJvbSBhbiBlbXBsb3llZQphbGxfdWJlciA9IHJiaW5kKHViZXJfZHJpdmVyc19tb250aGx5LCB1YmVyX2VtcGxveWVlc19tb250aGx5KQoKZ2dwbG90KGFsbF91YmVyLCBhZXMoeCA9IG1vbnRoLCB5ID0gc2VudGltZW50KSkgKyAKICBnZW9tX2FyZWEoYWVzKGNvbG9yID0gdHlwZSwgZmlsbCA9IHR5cGUpLCAKICAgICAgICAgICAgYWxwaGEgPSAwLjMsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMC44KSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCIjMDBBRkJCIiwgIiNFN0I4MDAiKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiMwMEFGQkIiLCAiI0U3QjgwMCIpKSArIAogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGFzLm51bWVyaWModWJlcl9kcml2ZXJzJGRhdGVbMzI2N10pKSAreGxhYigiRGF0ZSIpICsgeWxhYigiU2VudGltZW50IikgKyBnZ3RpdGxlKCJTZW50aW1lbnQgb3ZlciBUaW1lIGJ5IFdvcmtlciBUeXBlIikgKyBzY2FsZV94X2RhdGUobGltaXRzID0gYXMuRGF0ZShjKCIyMDE0LTAxLTAxIiwiMjAxOS0wNS0wNSIpKSkKYGBgCgoKVGhlIGZvbGxvd2luZyBjb2RlIHNuaXBwZXQgZGl2aWRlcyBkcml2ZXMgaW50byBhIGJlZm9yZSBhbmQgYWZ0ZXIgdGhlIG5ldyBjZW8gYW5kIHRoZW4gb25seSB0YWtlcyB0aGUgYmFkIHJldmlld3MgYW5kIHBlcmZvcm1zIGFuIExEQSBjbGFzc2lmaWNhdGlvbi4gVGhlIGZvbGxvd2luZyBzaXggc25pcHBldHMgd2VyZW4ndCByZWFsbHkgaW5jbHVkZWQgaW4gdGhlIHByb2plY3QKYGBge3J9CnViZXJfZHJpdmVyc19iZWZvcmUgPSBzdWJzZXQodWJlcl9kcml2ZXJzLCBkYXRlPj1hcy5EYXRlKCIyMDEwLTExLTI1IikgJiBkYXRlPD1hcy5EYXRlKCIyMDE3LTA4LTI3IikpICNCZWZvcmUgdGhlIE5ldyBDRU8KI3ViZXJfZHJpdmVyc19iZWZvcmUgPSB1YmVyX2RyaXZlcnNfYmVmb3JlICU+JQogICNmaWx0ZXIoc3RhcnMgPD0yKQp1YmVyX2RyaXZlcnNfYWZ0ZXIgPSBzdWJzZXQodWJlcl9kcml2ZXJzLCBkYXRlPj1hcy5EYXRlKCIyMDE3LTA4LTI4IikgJiBkYXRlPD1hcy5EYXRlKCIyMDIwLTAxLTAxIikpICNBZnRlciB0aGUgbmV3IGNlbwojdWJlcl9kcml2ZXJzX2FmdGVyID0gdWJlcl9kcml2ZXJzX2FmdGVyICU+JQogICNmaWx0ZXIoc3RhcnMgPD0yKQpgYGAKCmBgYHtyfQojQ3JhdGluZyBhIGNvcnB1cyBvZiB0aGUgcmV2aWV3cyBhbmQgY2xlYW5pbmcgaXQgdXAKcmV2aWV3c19iZWZvcmUgPSBWQ29ycHVzKFZlY3RvclNvdXJjZSh1YmVyX2RyaXZlcnNfYmVmb3JlJHJldmlldykpCnJldmlld3NfYWZ0ZXIgPSBWQ29ycHVzKFZlY3RvclNvdXJjZSh1YmVyX2RyaXZlcnNfYWZ0ZXIkcmV2aWV3KSkKCnJldmlld3NfYmVmb3JlID0gcmV2aWV3c19iZWZvcmUgJT4lCiAgdG1fbWFwKHJlbW92ZVdvcmRzLCBzdG9wd29yZHMoImVuZ2xpc2giKSkgJT4lICNSZW1vdmUgc3RvcHdvcmRzCiAgdG1fbWFwKHN0cmlwV2hpdGVzcGFjZSkgJT4lICNSZW1vdmUgV2hpdGVzcGFjZQogIHRtX21hcChjb250ZW50X3RyYW5zZm9ybWVyKHRvbG93ZXIpKSAlPiUgI0NvbnZlcnQgdG8gbG93ZXJjYXNlCiAgdG1fbWFwKHJlbW92ZVB1bmN0dWF0aW9uKSAlPiUgI1JlbW92ZSBwdW5jdHVhdGlvbgogIHRtX21hcChyZW1vdmVOdW1iZXJzKSAlPiUgI1JlbW92ZSBudW1iZXJzCiAgdG1fbWFwKGNvbnRlbnRfdHJhbnNmb3JtZXIoc3RlbURvY3VtZW50KSAsbGF6eT1UUlVFKSAlPiUgI1N0ZW1taW5nIHdvcmRzCiAgdG1fbWFwKGNvbnRlbnRfdHJhbnNmb3JtZXIocmVtb3ZlV29yZHMpLCBjKCJhbmQiLCAic2FpZCIsICJidXQiLCAidGhlIikgLGxhenk9VFJVRSkgI1Rha2luZyBvdXQgZnVydGhlciB1bm5lY2Vzc2VjYXJ5IHdvcmRzCgpyZXZpZXdzX2FmdGVyID0gcmV2aWV3c19hZnRlciAlPiUKICB0bV9tYXAocmVtb3ZlV29yZHMsIHN0b3B3b3JkcygiZW5nbGlzaCIpKSAlPiUgI1JlbW92ZSBzdG9wd29yZHMKICB0bV9tYXAoc3RyaXBXaGl0ZXNwYWNlKSAlPiUgI1JlbW92ZSBXaGl0ZXNwYWNlCiAgdG1fbWFwKGNvbnRlbnRfdHJhbnNmb3JtZXIodG9sb3dlcikpICU+JSAjQ29udmVydCB0byBsb3dlcmNhc2UKICB0bV9tYXAocmVtb3ZlUHVuY3R1YXRpb24pICU+JSAjUmVtb3ZlIHB1bmN0dWF0aW9uCiAgdG1fbWFwKHJlbW92ZU51bWJlcnMpICU+JSAjUmVtb3ZlIG51bWJlcnMKICB0bV9tYXAoY29udGVudF90cmFuc2Zvcm1lcihzdGVtRG9jdW1lbnQpICxsYXp5PVRSVUUpICU+JSAjU3RlbW1pbmcgd29yZHMKICB0bV9tYXAoY29udGVudF90cmFuc2Zvcm1lcihyZW1vdmVXb3JkcyksIGMoImFuZCIsICJzYWlkIiwgImJ1dCIsICJ0aGUiKSAsbGF6eT1UUlVFKSAjVGFraW5nIG91dCBmdXJ0aGVyIHVubmVjZXNzZWNhcnkgd29yZHMKCmR0bV9iZWZvcmUgPSBEb2N1bWVudFRlcm1NYXRyaXgocmV2aWV3c19iZWZvcmUpCmR0bV9hZnRlciA9IERvY3VtZW50VGVybU1hdHJpeChyZXZpZXdzX2FmdGVyKQojUmVtb3Zpbmcgc3BhcnNlIHRlcm1zCmR0bXNfYmVmb3JlID0gcmVtb3ZlU3BhcnNlVGVybXMoZHRtX2JlZm9yZSwgLjk5KSAKZHRtc19hZnRlciA9IHJlbW92ZVNwYXJzZVRlcm1zKGR0bV9hZnRlciwgLjk5KSAKYGBgCgpgYGB7cn0KI0J1aWxkaW5nIGFuIExEQSBNb2RlbApkdG1fbWF0cml4X2xkYV9iZWZvcmUgPSBhcy5tYXRyaXgoZHRtc19iZWZvcmUpCmR0bV9tYXRyaXhfbGRhX2FmdGVyID0gYXMubWF0cml4KGR0bXNfYWZ0ZXIpCgp0ZXJtc19iZWZvcmUgPSByb3dTdW1zKGR0bV9tYXRyaXhfbGRhX2JlZm9yZSkgIT0gMCAjRmluZGluZyB2YWx1ZXMgdGhhdCBuZXZlciBhcHBlYXIKZHRtX21hdHJpeF9sZGFfYmVmb3JlID0gZHRtX21hdHJpeF9sZGFfYmVmb3JlW3Rlcm1zX2JlZm9yZSxdICNDbGVhcmluZyBvdXQgdmFsdWVzIHRoYXQgbmV2ZXIgYXBwZWFyCnRlcm1zX2FmdGVyID0gcm93U3VtcyhkdG1fbWF0cml4X2xkYV9hZnRlcikgIT0gMCAjRmluZGluZyB2YWx1ZXMgdGhhdCBuZXZlciBhcHBlYXIKZHRtX21hdHJpeF9sZGFfYWZ0ZXIgPSBkdG1fbWF0cml4X2xkYV9hZnRlclt0ZXJtc19hZnRlcixdICNDbGVhcmluZyBvdXQgdmFsdWVzIHRoYXQgbmV2ZXIgYXBwZWFyCgoKbGRhX3ViZXJfYmVmb3JlIDwtTERBKGR0bV9tYXRyaXhfbGRhX2JlZm9yZSwgMTIsIG1ldGhvZD0iR2liYnMiLCBjb250cm9sID0gbGlzdChzZWVkID0gMTIzNCkpCnRlcm1zKGxkYV91YmVyX2JlZm9yZSwxMCkKCmxkYV91YmVyX2FmdGVyIDwtTERBKGR0bV9tYXRyaXhfbGRhX2FmdGVyLCAxMiwgbWV0aG9kPSJHaWJicyIsIGNvbnRyb2wgPSBsaXN0KHNlZWQgPSAxMjM0KSkKdGVybXMobGRhX3ViZXJfYWZ0ZXIsMTApCmBgYAoKVGhlIGZvbGxvd2luZyB0aHJlZSBjb2RlIHNuaXBwZXRzIGRpdmlkZSB1YmVyIGVtcGxveWVlcyBpbnRvIHR3byBncm91cHMgYW5kIHBlcmZvcm0gYW4gTERBIG9uIGVhY2gKYGBge3J9CiNEaXZpZGluZyBVYmVyIGVtcGxveWVlcyBpbnRvIHR3byBzdWJncm91cHMKdWJlcl9lbXBsb3llZXMyID0gdWJlcl9lbXBsb3llZXMgJT4lCiAgZmlsdGVyKHBvc2l0aW9uICE9ICJEcml2ZXIiJiAgcG9zaXRpb24gIT0gIkRSSVZFUiIgJiBwb3NpdGlvbiAhPSAiVWJlciBEcml2ZXIiKQp1YmVyX2VtcGxveWVlc19iZWZvcmUgPSBzdWJzZXQodWJlcl9lbXBsb3llZXMsIGRhdGU+PWFzLkRhdGUoIjIwMTAtMTEtMjUiKSAmIGRhdGU8PWFzLkRhdGUoIjIwMTctMDgtMjciKSkgI0JlZm9yZSB0aGUgTmV3IENFTwp1YmVyX2VtcGxveWVlc19hZnRlciA9IHN1YnNldCh1YmVyX2VtcGxveWVlcywgZGF0ZT49YXMuRGF0ZSgiMjAxNy0wOC0yOCIpICYgZGF0ZTw9YXMuRGF0ZSgiMjAyMC0wMS0wMSIpKSAjQWZ0ZXIgdGhlIG5ldyBjZW8KYGBgCgpgYGB7cn0KI0NyZWF0aW5nIGEgY29ycHVzIG9mIHRoZSBlbXBsb3llZSByZXZpZXdzIGFuZCBjbGVhbmluZyBpdCB1cAplbXBsb3llZV9yZXZpZXdzX2JlZm9yZSA9IFZDb3JwdXMoVmVjdG9yU291cmNlKHViZXJfZHJpdmVyc19iZWZvcmUkcmV2aWV3KSkKZW1wbG95ZWVfcmV2aWV3c19hZnRlciA9IFZDb3JwdXMoVmVjdG9yU291cmNlKHViZXJfZHJpdmVyc19hZnRlciRyZXZpZXcpKQoKZW1wbG95ZWVfcmV2aWV3c19iZWZvcmUgPSBlbXBsb3llZV9yZXZpZXdzX2JlZm9yZSAlPiUKICB0bV9tYXAocmVtb3ZlV29yZHMsIHN0b3B3b3JkcygiZW5nbGlzaCIpKSAlPiUgI1JlbW92ZSBzdG9wd29yZHMKICB0bV9tYXAoc3RyaXBXaGl0ZXNwYWNlKSAlPiUgI1JlbW92ZSBXaGl0ZXNwYWNlCiAgdG1fbWFwKGNvbnRlbnRfdHJhbnNmb3JtZXIodG9sb3dlcikpICU+JSAjQ29udmVydCB0byBsb3dlcmNhc2UKICB0bV9tYXAocmVtb3ZlUHVuY3R1YXRpb24pICU+JSAjUmVtb3ZlIHB1bmN0dWF0aW9uCiAgdG1fbWFwKHJlbW92ZU51bWJlcnMpICU+JSAjUmVtb3ZlIG51bWJlcnMKICB0bV9tYXAoY29udGVudF90cmFuc2Zvcm1lcihzdGVtRG9jdW1lbnQpICxsYXp5PVRSVUUpICU+JSAjU3RlbW1pbmcgd29yZHMKICB0bV9tYXAoY29udGVudF90cmFuc2Zvcm1lcihyZW1vdmVXb3JkcyksIGMoImFuZCIsICJzYWlkIiwgImJ1dCIsICJ0aGUiKSAsbGF6eT1UUlVFKSAjVGFraW5nIG91dCBmdXJ0aGVyIHVubmVjZXNzZWNhcnkgd29yZHMKCmVtcGxveWVlX3Jldmlld3NfYWZ0ZXIgPSBlbXBsb3llZV9yZXZpZXdzX2FmdGVyICU+JQogIHRtX21hcChyZW1vdmVXb3Jkcywgc3RvcHdvcmRzKCJlbmdsaXNoIikpICU+JSAjUmVtb3ZlIHN0b3B3b3JkcwogIHRtX21hcChzdHJpcFdoaXRlc3BhY2UpICU+JSAjUmVtb3ZlIFdoaXRlc3BhY2UKICB0bV9tYXAoY29udGVudF90cmFuc2Zvcm1lcih0b2xvd2VyKSkgJT4lICNDb252ZXJ0IHRvIGxvd2VyY2FzZQogIHRtX21hcChyZW1vdmVQdW5jdHVhdGlvbikgJT4lICNSZW1vdmUgcHVuY3R1YXRpb24KICB0bV9tYXAocmVtb3ZlTnVtYmVycykgJT4lICNSZW1vdmUgbnVtYmVycwogIHRtX21hcChjb250ZW50X3RyYW5zZm9ybWVyKHN0ZW1Eb2N1bWVudCkgLGxhenk9VFJVRSkgJT4lICNTdGVtbWluZyB3b3JkcwogIHRtX21hcChjb250ZW50X3RyYW5zZm9ybWVyKHJlbW92ZVdvcmRzKSwgYygiYW5kIiwgInNhaWQiLCAiYnV0IiwgInRoZSIpICxsYXp5PVRSVUUpICNUYWtpbmcgb3V0IGZ1cnRoZXIgdW5uZWNlc3NlY2FyeSB3b3JkcwoKZW1wbG95ZWVfZHRtX2JlZm9yZSA9IERvY3VtZW50VGVybU1hdHJpeChlbXBsb3llZV9yZXZpZXdzX2JlZm9yZSkKZW1wbG95ZWVfZHRtX2FmdGVyID0gRG9jdW1lbnRUZXJtTWF0cml4KGVtcGxveWVlX3Jldmlld3NfYWZ0ZXIpCiNSZW1vdmluZyBzcGFyc2UgdGVybXMKZW1wbG95ZWVfZHRtc19iZWZvcmUgPSByZW1vdmVTcGFyc2VUZXJtcyhlbXBsb3llZV9kdG1fYmVmb3JlLCAuOTkpIAplbXBsb3llZV9kdG1zX2FmdGVyID0gcmVtb3ZlU3BhcnNlVGVybXMoZW1wbG95ZWVfZHRtX2FmdGVyLCAuOTkpIApgYGAKCmBgYHtyfQojQnVpbGRpbmcgYW4gTERBIE1vZGVsIGZvciBlbXBsb3llZXMKZW1wbG95ZWVfZHRtX21hdHJpeF9iZWZvcmUgPSBhcy5tYXRyaXgoZW1wbG95ZWVfZHRtc19iZWZvcmUpCmVtcGxveWVlX2R0bV9tYXRyaXhfYWZ0ZXIgPSBhcy5tYXRyaXgoZW1wbG95ZWVfZHRtc19hZnRlcikKCmVtcGxveWVlX3Rlcm1zX2JlZm9yZSA9IHJvd1N1bXMoZW1wbG95ZWVfZHRtX21hdHJpeF9iZWZvcmUpICE9IDAgI0ZpbmRpbmcgdmFsdWVzIHRoYXQgbmV2ZXIgYXBwZWFyCmVtcGxveWVlX2R0bV9tYXRyaXhfYmVmb3JlID0gZW1wbG95ZWVfZHRtX21hdHJpeF9iZWZvcmVbZW1wbG95ZWVfdGVybXNfYmVmb3JlLF0gI0NsZWFyaW5nIG91dCB2YWx1ZXMgdGhhdCBuZXZlciBhcHBlYXIKCmVtcGxveWVlX3Rlcm1zX2FmdGVyID0gcm93U3VtcyhlbXBsb3llZV9kdG1fbWF0cml4X2FmdGVyKSAhPSAwICNGaW5kaW5nIHZhbHVlcyB0aGF0IG5ldmVyIGFwcGVhcgplbXBsb3llZV9kdG1fbWF0cml4X2FmdGVyID0gZW1wbG95ZWVfZHRtX21hdHJpeF9hZnRlcltlbXBsb3llZV90ZXJtc19hZnRlcixdICNDbGVhcmluZyBvdXQgdmFsdWVzIHRoYXQgbmV2ZXIgYXBwZWFyCgoKbGRhX3ViZXJfZW1wbG95ZWVfYmVmb3JlIDwtTERBKGVtcGxveWVlX2R0bV9tYXRyaXhfYmVmb3JlLCA4LCBtZXRob2Q9IkdpYmJzIiwgY29udHJvbCA9IGxpc3Qoc2VlZCA9IDEyMzQpKQp0ZXJtcyhsZGFfdWJlcl9lbXBsb3llZV9iZWZvcmUsOCkKCmxkYV91YmVyX2VtcGxveWVlX2FmdGVyIDwtTERBKGVtcGxveWVlX2R0bV9tYXRyaXhfYWZ0ZXIsIDgsIG1ldGhvZD0iR2liYnMiLCBjb250cm9sID0gbGlzdChzZWVkID0gMTIzNCkpCnRlcm1zKGxkYV91YmVyX2VtcGxveWVlX2FmdGVyLDgpCmBgYAoKTG9va2luZyBhdCB0aGUgY2l0aWVzIHdpdGggdGhlIGJlc3QgcmV2aWV3cwpgYGB7cn0KdWJlcl9kcml2ZXJfY2l0eSA9IHViZXJfZHJpdmVycyAlPiUKICBncm91cF9ieShjaXR5KSAlPiUKICBzdW1tYXJpc2UobWVhbihzZW50aW1lbnQpKQoKbmFtZXModWJlcl9kcml2ZXJfY2l0eSkgPSBjKCJjaXR5IiwgInNlbnRpbWVudCIpCnViZXJfZHJpdmVyX2NpdHkgPXViZXJfZHJpdmVyX2NpdHkgJT4lCiAgICBhcnJhbmdlKGRlc2Moc2VudGltZW50KSkKCnViZXJfZHJpdmVyX2NpdHkkY2l0eTIgPSBnc3ViKHBhdHRlcm4gPSAiLCIsIHJlcGxhY2VtZW50ID0gIiIsIHViZXJfZHJpdmVyX2NpdHkkY2l0eSkgI1Rha2luZyBvdXQgdGhlIGNvbW1hIHRvIG1ha2UgaXQgcmVhZGFibGUgdG8gZ29vZ2xlCmBgYAoKClRoZSBmb2xsb3dpbmcgY29kZSBwcmVwYXJlcyB0aGUgZGF0YWZhbWUgZm9yIHN0YXRlIGFuYWx5c2lzIGJ5IGdlb2NvZGluZyBhbmQgcHJvdmlkaW5nIGxhdCBsb25nLiAgVGhpcyBzb2x2ZXMgYSBsb3Qgb2YgdHJvdWJsZSB3aXRoIHRoZSBtZXNzeSBkYXRhCmBgYHtyfQp1YmVyX2RyaXZlcl9jaXR5IDwtIHViZXJfZHJpdmVyX2NpdHkgJT4lIAogIG11dGF0ZV9nZW9jb2RlKGNpdHkyKSAjQWRkaW5nIGEgbGF0L2xvbiBjb2x1bW4gZm9yIGVhY2ggY2l0eQpgYGAKClRoZSBmb2xsb3dpbmcgY29kZSBwcmVwYXJlcyB0aGUgZGF0YWZhbWUgZm9yIHN0YXRlIGFuYWx5c2lzCmBgYHtyfQp1YmVyX2RyaXZlcl9jaXR5MiA9IHViZXJfZHJpdmVyX2NpdHkgI0R1cGxpY2F0aW5nIHRoZSBkYXRhZnJhbWUgYXMgaXQgdGFrZXMgYSBsb25nIHRpbWUgZm9yIGdvb2dsZSB0byBnZW9jb2RlCgpsYXRsb25nMnN0YXRlIDwtIGZ1bmN0aW9uKHBvaW50c0RGKSB7ICNDb252ZXRzIGFsbCB0aGUgY29vcmRpbmF0ZXMgYmFjayB0byBzdGF0ZSBjb2RlcwogICAgIyBQcmVwYXJlIFNwYXRpYWxQb2x5Z29ucyBvYmplY3Qgd2l0aCBvbmUgU3BhdGlhbFBvbHlnb24KICAgICMgcGVyIHN0YXRlIChwbHVzIERDLCBtaW51cyBISSAmIEFLKQogICAgc3RhdGVzIDwtIG1hcCgnc3RhdGUnLCBmaWxsPVRSVUUsIGNvbD0idHJhbnNwYXJlbnQiLCBwbG90PUZBTFNFKQogICAgSURzIDwtIHNhcHBseShzdHJzcGxpdChzdGF0ZXMkbmFtZXMsICI6IiksIGZ1bmN0aW9uKHgpIHhbMV0pCiAgICBzdGF0ZXNfc3AgPC0gbWFwMlNwYXRpYWxQb2x5Z29ucyhzdGF0ZXMsIElEcz1JRHMsCiAgICAgICAgICAgICAgICAgICAgIHByb2o0c3RyaW5nPUNSUygiK3Byb2o9bG9uZ2xhdCArZGF0dW09d2dzODQiKSkKCiAgICAjIENvbnZlcnQgcG9pbnRzREYgdG8gYSBTcGF0aWFsUG9pbnRzIG9iamVjdCAKICAgIHBvaW50c1NQIDwtIFNwYXRpYWxQb2ludHMocG9pbnRzREYsIAogICAgICAgICAgICAgICAgICAgIHByb2o0c3RyaW5nPUNSUygiK3Byb2o9bG9uZ2xhdCArZGF0dW09d2dzODQiKSkKCiAgICAjIFVzZSAnb3ZlcicgdG8gZ2V0IF9pbmRpY2VzXyBvZiB0aGUgUG9seWdvbnMgb2JqZWN0IGNvbnRhaW5pbmcgZWFjaCBwb2ludCAKICAgIGluZGljZXMgPC0gb3Zlcihwb2ludHNTUCwgc3RhdGVzX3NwKQoKICAgICMgUmV0dXJuIHRoZSBzdGF0ZSBuYW1lcyBvZiB0aGUgUG9seWdvbnMgb2JqZWN0IGNvbnRhaW5pbmcgZWFjaCBwb2ludAogICAgc3RhdGVOYW1lcyA8LSBzYXBwbHkoc3RhdGVzX3NwQHBvbHlnb25zLCBmdW5jdGlvbih4KSB4QElEKQogICAgc3RhdGVOYW1lc1tpbmRpY2VzXQp9Cgp1YmVyX2RyaXZlcl9jaXR5MyA9IHViZXJfZHJpdmVyX2NpdHkyCnViZXJfZHJpdmVyX2NpdHkzID0gbmEub21pdCh1YmVyX2RyaXZlcl9jaXR5MykgI1JlbW92aW5nIE5BIHZhbHVlcwp1YmVyX2RyaXZlcl9jaXR5MyRzdGF0ZSA9IGxhdGxvbmcyc3RhdGUodWJlcl9kcml2ZXJfY2l0eTNbNDo1XSkgI0FkZGluZyBhIGNvbHVtbiBvZiBzdGF0ZSBjb2Rlcwp1YmVyX2RyaXZlcl9jaXR5MyA9IG5hLm9taXQodWJlcl9kcml2ZXJfY2l0eTMpI1JlbW92aW5nIE5BIHZhbHVlcwoKc3RhdGVzID0gcmVhZC5jc3YoInN0YXRlbGF0bG9uZy5jc3YiKSAjQSBkYXRhIGZhbWUgd2l0aCBzdGF0ZXMgYW5kIHN0YXRlIGNvZGVzCnN0YXRlcyA9IHN0YXRlc1ssIGMoLTIsLTMpXQpzdGF0ZXMkQ2l0eSA9IHRvbG93ZXIoc3RhdGVzJENpdHkpICNjb252ZXJ0aW5nIHN0YXRlIG5hbWVzIHRvIGxvd2VyY2FzZQp1YmVyX2RyaXZlcl9jaXR5NCA9IGxlZnRfam9pbih1YmVyX2RyaXZlcl9jaXR5Mywgc3RhdGVzLCBieSA9IGMoInN0YXRlIiA9IkNpdHkiKSkgI0FBc3NvY2lhdGluZyBlYWNoIHN0YXRlIHdpdGggYSBjb2RlIHRvIGJlIHJlYWQgaW50byBwbG90bHkKYGBgCgpXaGljaCBDaXRpZXMgYXJlIHRoZSBiZXN0IGZvciBVYmVyIERyaXZlcnMKYGBge3J9CnViZXJfZHJpdmVyX2NpdHkxMCA9IHViZXJfZHJpdmVyX2NpdHkyCgp1YmVyX2RyaXZlcl9jaXR5MTAkY29vcmRpbmF0ZXMgPSBwYXN0ZSh1YmVyX2RyaXZlcl9jaXR5MTAkbGF0LCB1YmVyX2RyaXZlcl9jaXR5MTAkbG9uLCBzZXAgPSAiLCIpICNQYXN0aW5nIHRoZSBjb29yZGluYXRlcyB0b2dldGhlcgoKdWJlcl9kcml2ZXJfY2l0eTEwJGNpdHluYW1lID0gZ2VvY29kZSh1YmVyX2RyaXZlcl9jaXR5MTAkY29vcmRpbmF0ZXMsIG91dHB1dCA9ICJtb3JlIikKCnViZXJfZHJpdmVyX2NpdHkxMCA9IG5hLm9taXQodWJlcl9kcml2ZXJfY2l0eTEwKQoKdWJlcl9kcml2ZXJfY2l0eTEwPXViZXJfZHJpdmVyX2NpdHkxMFstYygxOCwxNTYsMjIxLDI4NywyODksNDA1LDUwNyw3MzIsNzkzLDk5NSwxMDE2LDEwODMpLF0KI1RoZSBmb2xsb3dpbmcgY29kZSBwdWxscyBvdXQgdGhlIGNpdHkgbmFtZSBvbmx5IGJlY2F1c2UgdGhlIGRhdGEgd2FzIHZlcnkgbWVzc3kKcGF0PSIoLC5cXHcrLCl8KCwuXFx3Ky5cXHcrLCkiCnViZXJfZHJpdmVyX2NpdHkxMCRyZWFsY2l0eW5hbWUgPSBnc3ViKCIoLFxccyl8LCIsIiIscmVnbWF0Y2hlcyhtPC1zdHJzcGxpdCh1YmVyX2RyaXZlcl9jaXR5MTAkY2l0eW5hbWVbWzVdXSwiXFx8IikscmVnZXhwcihwYXQsbSkpKQoKdWJlcl9kcml2ZXJfY2l0eTExID0gdWJlcl9kcml2ZXJfY2l0eTEwICU+JQogIGdyb3VwX2J5KHJlYWxjaXR5bmFtZSkgJT4lCiAgc3VtbWFyaXNlKG1lYW4oc2VudGltZW50KSkKCm5hbWVzKHViZXJfZHJpdmVyX2NpdHkxMSkgPSBjKCJjaXR5IiwgInNlbnRpbWVudCIpICAKdWJlcl9kcml2ZXJfY2l0eTExID0gdWJlcl9kcml2ZXJfY2l0eTExICU+JQogIGFycmFuZ2UoZGVzYyhzZW50aW1lbnQpKQoKdWJlcl9kcml2ZXJfY2l0eTExJGNpdHkgPC0gZmFjdG9yKHViZXJfZHJpdmVyX2NpdHkxMSRjaXR5LCBsZXZlbHMgPSB1YmVyX2RyaXZlcl9jaXR5MTEkY2l0eVtvcmRlcih1YmVyX2RyaXZlcl9jaXR5MTEkc2VudGltZW50KV0pCgpiZXN0X2NpdGllczIgPSBnZ3Bsb3QodWJlcl9kcml2ZXJfY2l0eTExWzE6MjAsXSwgYWVzKHg9Y2l0eSwgeT1zZW50aW1lbnQpKSArIAogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5Iiwgd2lkdGg9LjUsIGZpbGw9ImJsYWNrIikgKyAKICBsYWJzKHRpdGxlPSJBdmVyYWdlIFNlbnRpbWVudCBieSBDaXR5IikgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT02NSwgdmp1c3Q9MC42KSkKYmVzdF9jaXRpZXMyCmBgYApPbGQgVmVyc2lvbiBvZiBiZXN0IGNpdGllcyBjb2RlCmBgYHtyfQp1YmVyX2RyaXZlcl9jaXR5MjAgPSB1YmVyX2RyaXZlcl9jaXR5WzE6MjAsXQp1YmVyX2RyaXZlcl9jaXR5MjAgPXViZXJfZHJpdmVyX2NpdHkyMCAlPiUKICAgIGFycmFuZ2UoZGVzYyhzZW50aW1lbnQpKQoKdWJlcl9kcml2ZXJfY2l0eTIwJGNpdHkgPC0gZmFjdG9yKHViZXJfZHJpdmVyX2NpdHkyMCRjaXR5LCBsZXZlbHMgPSB1YmVyX2RyaXZlcl9jaXR5MjAkY2l0eVtvcmRlcih1YmVyX2RyaXZlcl9jaXR5MjAkc2VudGltZW50KV0pCgoKYmVzdF9jaXRpZXMgPSBnZ3Bsb3QodWJlcl9kcml2ZXJfY2l0eTIwLCBhZXMoeD1jaXR5LCB5PXNlbnRpbWVudCkpICsgCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCB3aWR0aD0uNSwgZmlsbD0iYmxhY2siKSArIAogIGxhYnModGl0bGU9IkF2ZXJhZ2UgU2VudGltZW50IGJ5IENpdHkiKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTY1LCB2anVzdD0wLjYpKQpiZXN0X2NpdGllcwpgYGAKCgpDb2RlIGZvciBidWlsZGluZyB0aGUgaW50ZXJhY3RpdmUgbWFwCmBgYHtyfQp1YmVyX2RyaXZlcl9jaXR5NSA9IHViZXJfZHJpdmVyX2NpdHk0ICU+JSAjQ3JlYXRpbmcgYSBkYXRhIGZyYW1lIGdycm91cGluZyBieSBzdGF0ZSBhbmQgZmluZGluZyB0aGUgbWVhbiBzZW50aW1lbnQgZm9yIGRyaXZlcnMKICBncm91cF9ieShTdGF0ZSkgJT4lCiAgc3VtbWFyaXNlKG1lYW4oc2VudGltZW50KSkgCm5hbWVzKHViZXJfZHJpdmVyX2NpdHk1KSA9IGMoIlN0YXRlIiwgIlNlbnRpbWVudCIpCgojIGdpdmUgc3RhdGUgYm91bmRhcmllcyBhIHdoaXRlIGJvcmRlcgpsIDwtIGxpc3QoY29sb3IgPSB0b1JHQigid2hpdGUiKSwgd2lkdGggPSAyKQojIHNwZWNpZnkgc29tZSBtYXAgcHJvamVjdGlvbi9vcHRpb25zCm1hcCA8LSBsaXN0KAogIHNjb3BlID0gJ3VzYScsCiAgcHJvamVjdGlvbiA9IGxpc3QodHlwZSA9ICdhbGJlcnMgdXNhJyksCiAgc2hvd2xha2VzID0gVFJVRSwKICBsYWtlY29sb3IgPSB0b1JHQignbGlnaHRibHVlJykpCgp1YmVyX3N0YXRlX21hcCA8LSBwbG90X2dlbyh1YmVyX2RyaXZlcl9jaXR5NSwgbG9jYXRpb25tb2RlID0gJ1VTQS1zdGF0ZXMnKSAlPiUKICBhZGRfdHJhY2UoCiAgICB6ID0gflNlbnRpbWVudCwgbG9jYXRpb25zID0gflN0YXRlLAogICAgY29sb3IgPSB+U2VudGltZW50LCBjb2xvcnMgPSAnT3JhbmdlcycKICApICU+JQogIGNvbG9yYmFyKHRpdGxlID0gIlNlbnRpbWVudCBMZXZlbCIpICU+JQogIGxheW91dCgKICAgIHRpdGxlID0gJ0hpc3RvcmljYWwgVWJlciBEcml2ZXIgU2VudGltZW50IGJ5IFN0YXRlPGJyPihIb3ZlciBmb3IgYnJlYWtkb3duKScsCiAgICBnZW8gPSBtYXAKICApCnViZXJfc3RhdGVfbWFwCmBgYAoKCgoKCgoKRXhwb3J0aW5nIHRoZSB3aWRnZXQgdG8gYmUgZW1iZWRkZWQgaW4gdGhlIHdlYnNpdGUKYGBge3J9ClN5cy5zZXRlbnYoInBsb3RseV91c2VybmFtZSI9Im1pa2ViZXJnIikKU3lzLnNldGVudigicGxvdGx5X2FwaV9rZXkiPSJXQmh4anVoWmVGTUQxWTlNOXFibCIpCmNoYXJ0X2xpbmsgPSBhcGlfY3JlYXRlKHViZXJfc3RhdGVfbWFwLCBmaWxlbmFtZT0iY2hvcm9wbGV0aCIpCiNjaGFydF9saW5rCmBgYAoKVGV4dCBBbmFseXNpcwpGaXJzdCBDbGVhbmluZyB0aGUgVGV4dApgYGB7cn0KdWJlcl9kcml2ZXJfdGV4dCA9IHViZXJfZHJpdmVycwojQ3JhdGluZyBhIGNvcnB1cyBvZiB0aGUgcmV2aWV3cyBhbmQgY2xlYW5pbmcgaXQgdXAKcmV2aWV3cyA9IFZDb3JwdXMoVmVjdG9yU291cmNlKHViZXJfZHJpdmVyX3RleHQkcmV2aWV3KSkKcmV2aWV3cyA9IHJldmlld3MgJT4lCiAgdG1fbWFwKHJlbW92ZVdvcmRzLCBzdG9wd29yZHMoImVuZ2xpc2giKSkgJT4lICNSZW1vdmUgc3RvcHdvcmRzCiAgdG1fbWFwKHN0cmlwV2hpdGVzcGFjZSkgJT4lICNSZW1vdmUgV2hpdGVzcGFjZQogIHRtX21hcChjb250ZW50X3RyYW5zZm9ybWVyKHRvbG93ZXIpKSAlPiUgI0NvbnZlcnQgdG8gbG93ZXJjYXNlCiAgdG1fbWFwKHJlbW92ZVB1bmN0dWF0aW9uKSAlPiUgI1JlbW92ZSBwdW5jdHVhdGlvbgogIHRtX21hcChyZW1vdmVOdW1iZXJzKSAjUmVtb3ZlIG51bWJlcnMKCgpkdG1fYWxsID0gRG9jdW1lbnRUZXJtTWF0cml4KHJldmlld3MpCgojUmVtb3Zpbmcgc3BhcnNlIHRlcm1zCmR0bXNfYWxsID0gcmVtb3ZlU3BhcnNlVGVybXMoZHRtX2FsbCwgLjk5KSAKYGBgCgpMb2dpc3RpYyBSZWdyZXNzaW9uIGZvciBXb3JkcyBBc3NvaWNpYXRlZCBQb3NpdGl2ZWx5CmBgYHtyfQojRmVhdHVyZSBTZWxlY3Rpb24gUG9zaXRpdmUKZHRtX21hdHJpeF9zZWxlY3Rpb24gPSBhcy5tYXRyaXgoZHRtc19hbGwpCgp1YmVyX2RyaXZlcl90ZXh0JHBvc2l0aXZlID0gdWJlcl9kcml2ZXJfdGV4dCRzdGFycyA9PSA0IHwgdWJlcl9kcml2ZXJfdGV4dCRzdGFycyA9PSA1ICNDcmVhdGluZyBhIHJvdyBvZiB0cnVlIG9yIGZhbHNlIHZhbHVlIHRvIHNlZSBpZiB0aGUgcG9zdCB3YXMgcmVsYXRlZCB0byBwb3B1bGFyIHNlbnRpbWVudCBvciBub3QKY29ycl9wb3NpdGl2ZSA9IGNvcih1YmVyX2RyaXZlcl90ZXh0JHBvc2l0aXZlLCBkdG1fbWF0cml4X3NlbGVjdGlvbikKCnRvcDIwX3Bvc2l0aXZlID0gb3JkZXIoY29ycl9wb3NpdGl2ZSwgZGVjcmVhc2luZz1UKVsxOjIwXQp0b3AyMF9wb3NpdGl2ZV93b3JkcyA9IGNvbG5hbWVzKGNvcnJfcG9zaXRpdmUpW3RvcDIwX3Bvc2l0aXZlXQp0b3AyMF9wb3NpdGl2ZV93b3JkcwpgYGAKCmBgYHtyfQojQnVpbGRpbmcgYSBsb2dpc3RpYyByZWdyZXNzaW9uIHRvIHNlZSB3aGljaCB3b3JkcyBsZWFkIHRvIG1vc3QgcG9zaXRpdmUgcmV2aWV3cwpsb2dpc3RpY19wb3NpdGl2ZSA9IGFzLmRhdGEuZnJhbWUoY2JpbmQocG9zaXRpdmUgPSB1YmVyX2RyaXZlcl90ZXh0JHBvc2l0aXZlLCBkdG1fbWF0cml4X3NlbGVjdGlvblssdG9wMjBfcG9zaXRpdmVfd29yZHNdKSkKcGVkaWN0aXZlX3Bvc2l0aXZlID0gZ2xtKHBvc2l0aXZlfi4sIGRhdGE9bG9naXN0aWNfcG9zaXRpdmUsIGZhbWlseT1iaW5vbWlhbCkKI3N1bW1hcnkocGVkaWN0aXZlX3Bvc2l0aXZlKQpgYGAKCmBgYHtyfQojV29yZHMgbW9zdCBzaWduaWZpY2FudGx5IGFzc29jaWF0ZWQgd2l0aCBsZWF2aW5nIGdvb2QgcmV2aWV3CmNvZWZfcG9zaXRpdmUgPSBjb2VmKHBlZGljdGl2ZV9wb3NpdGl2ZSlbLTFdICNwdWxsaW5nIG91dCB0aGUgY29lZmZpY2llbnRzCnBvc2l0aXZlLnRlcm1zID0gY29lZl9wb3NpdGl2ZVtjb2VmX3Bvc2l0aXZlPjBdICNXaGljaCBjb2VmZmljaWVudHMgYXJlIHBvc2l0aXZlCnRvcC5wb3NpdGl2ZSA9IHNvcnQocG9zaXRpdmUudGVybXMsZGVjcmVhc2luZz1UKVsxOjE5XSAjU29ydGluZyBjb2VmZmljaWVudHMgYnkgbWFnbml0dWRlCgoKdG9wLnBvc2l0aXZlLmZyYW1lID0gYXMuZGF0YS5mcmFtZSh0b3AucG9zaXRpdmUpCnRvcC5wb3NpdGl2ZS5mcmFtZSA9IHJvd25hbWVzX3RvX2NvbHVtbih0b3AucG9zaXRpdmUuZnJhbWUpCm5hbWVzKHRvcC5wb3NpdGl2ZS5mcmFtZSkgPSBjKCJ3b3JkIiwgIm1hZ25pdHVkZSIpCgp3b3JkY2xvdWQod29yZHMgPSB0b3AucG9zaXRpdmUuZnJhbWUkd29yZCwgZnJlcSA9IHRvcC5wb3NpdGl2ZS5mcmFtZSRtYWduaXR1ZGUsCiAgICAgICAgICBtYXgud29yZHM9MjAwLCByYW5kb20ub3JkZXI9RkFMU0UsIHJvdC5wZXI9LjIsIAogICAgICAgICAgY29sb3JzPWJyZXdlci5wYWwoOCwgIkRhcmsyIiksIHNjYWxlPWMoNCwuNSkpCgpgYGAKCkZlYXR1cmUgU2VsZWN0aW9uIGZvciB3b3JkcyBhc3NvY2lhdGVkIHdpdGggbmVnYXRpdmUgUmV2aWV3cwpgYGB7cn0KI0ZlYXR1cmUgU2VsZWN0aW9uIE5lZ2F0aXZlCnViZXJfZHJpdmVyX3RleHQkbmVnYXRpdmUgPSB1YmVyX2RyaXZlcl90ZXh0JHN0YXJzID09IDEgfCB1YmVyX2RyaXZlcl90ZXh0JHN0YXJzID09IDIgI0NyZWF0aW5nIGEgcm93IG9mIHRydWUgb3IgZmFsc2UgdmFsdWUgdG8gc2VlIGlmIHRoZSBwb3N0IHdhcyByZWxhdGVkIHRvIG5lZ2F0aXZlIHNlbnRpbWVudApjb3JyX25lZ2F0aXZlID0gY29yKHViZXJfZHJpdmVyX3RleHQkbmVnYXRpdmUsIGR0bV9tYXRyaXhfc2VsZWN0aW9uKQoKdG9wMjBfbmVnYXRpdmUgPSBvcmRlcihjb3JyX25lZ2F0aXZlLCBkZWNyZWFzaW5nPVQpWzE6MjBdCnRvcDIwX25lZ2F0aXZlX3dvcmRzID0gY29sbmFtZXMoY29ycl9uZWdhdGl2ZSlbdG9wMjBfbmVnYXRpdmVdCnRvcDIwX25lZ2F0aXZlX3dvcmRzCmBgYApgYGB7cn0KI0J1aWxkaW5nIGEgbG9naXN0aWMgcmVncmVzc2lvbiB0byBzZWUgd2hpY2ggd29yZHMgbGVhZCB0byBtb3N0IG5lZ2F0aXZlIHJldmlld3MKbG9naXN0aWNfbmVnYXRpdmUgPSBhcy5kYXRhLmZyYW1lKGNiaW5kKG5lZ2F0aXZlID0gdWJlcl9kcml2ZXJfdGV4dCRuZWdhdGl2ZSwgZHRtX21hdHJpeF9zZWxlY3Rpb25bLHRvcDIwX25lZ2F0aXZlX3dvcmRzXSkpCnBlZGljdGl2ZV9uZWdhdGl2ZSA9IGdsbShuZWdhdGl2ZX4uLCBkYXRhPWxvZ2lzdGljX25lZ2F0aXZlLCBmYW1pbHk9Ymlub21pYWwpCiNzdW1tYXJ5KHBlZGljdGl2ZV9uZWdhdGl2ZSkKYGBgCgpgYGB7cn0KI1dvcmRzIG1vc3Qgc2lnbmlmaWNhbnRseSBhc3NvY2lhdGVkIHdpdGggbGVhdmluZyBiYWQgY3VsdHVyYWwgcmV2aWV3CmNvZWZfbmVnYXRpdmUgPSBjb2VmKHBlZGljdGl2ZV9uZWdhdGl2ZSlbLTFdCm5lZ2F0aXZlLnRlcm1zID0gY29lZl9uZWdhdGl2ZVtjb2VmX25lZ2F0aXZlPjBdCnRvcC5uZWdhdGl2ZSA9IHNvcnQobmVnYXRpdmUudGVybXMsZGVjcmVhc2luZz1UKVsxOjIwXQoKdG9wLm5lZ2F0aXZlLmZyYW1lID0gYXMuZGF0YS5mcmFtZSh0b3AubmVnYXRpdmUpCnRvcC5uZWdhdGl2ZS5mcmFtZSA9IHJvd25hbWVzX3RvX2NvbHVtbih0b3AubmVnYXRpdmUuZnJhbWUpCm5hbWVzKHRvcC5uZWdhdGl2ZS5mcmFtZSkgPSBjKCJ3b3JkIiwgIm1hZ25pdHVkZSIpCndvcmRjbG91ZCh3b3JkcyA9IHRvcC5uZWdhdGl2ZS5mcmFtZSR3b3JkLCBmcmVxID0gdG9wLm5lZ2F0aXZlLmZyYW1lJG1hZ25pdHVkZSwKICAgICAgICAgIG1heC53b3Jkcz0yMDAsIHJhbmRvbS5vcmRlcj1GQUxTRSwgcm90LnBlcj0uMiwgCiAgICAgICAgICBjb2xvcnM9YnJld2VyLnBhbCg4LCAiRGFyazIiKSwgc2NhbGU9Yyg2LC41KSkKCmBgYApCdWlsZGluZyBhbiBMREEgbW9kZWwgZm9yIFViZXIgUmV2aWV3cwpgYGB7cn0KI0J1aWxkaW5nIGFuIExEQSBNb2RlbApkdG1fbWF0cml4X2xkYSA9IGFzLm1hdHJpeChkdG1zX2FsbCkKCnRlcm1zID0gcm93U3VtcyhkdG1fbWF0cml4X2xkYSkgIT0gMCAjRmluZGluZyB2YWx1ZXMgdGhhdCBuZXZlciBhcHBlYXIKZHRtX21hdHJpeF9sZGEgPSBkdG1fbWF0cml4X2xkYVt0ZXJtcyxdICNDbGVhcmluZyBvdXQgdmFsdWVzIHRoYXQgbmV2ZXIgYXBwZWFyCgpsZGFPdXQgPC1MREEoZHRtX21hdHJpeF9sZGEsIDEwLCBtZXRob2Q9IkdpYmJzIiwgY29udHJvbCA9IGxpc3Qoc2VlZCA9IDEyMzQpKQp0ZXJtcyhsZGFPdXQsMTApCmBgYAoKYGBge3J9CiNMREEgTW9kZWwgZm9yIHdvcmRzIGFzc29jaWF0ZSB3aXRoIHBvc2l0aXZlIFJldmllcywgdGhhdCBpcyBmb3IgcmV2aWV3cyB3aXRoIHN0YXIgcmF0aW5ncyBvZiA0IG9yIDUKZHRtX21hdHJpeF9sZGFfcG9zaXRpdmUgPSBhcy5tYXRyaXgoZHRtc19hbGwpCgpkdG1fbWF0cml4X2xkYV9wb3NpdGl2ZSA9IGR0bV9tYXRyaXhfbGRhX3Bvc2l0aXZlW3doaWNoKHViZXJfZHJpdmVyX3RleHQkcG9zaXRpdmU9PVRSVUUpLF0gI1Rha2luZyBvbmx5IHJvd3MgYXNzb2NpYXRlZCB3aXRoIHBvc2l0aXZlIHNlbnRpbWVudAp0ZXJtczIgPSByb3dTdW1zKGR0bV9tYXRyaXhfbGRhX3Bvc2l0aXZlKSAhPSAwCmR0bV9tYXRyaXhfbGRhX3Bvc2l0aXZlID0gZHRtX21hdHJpeF9sZGFfcG9zaXRpdmVbdGVybXMyLF0KCmxkYV9wb3NpdGl2ZSA8LUxEQShkdG1fbWF0cml4X2xkYV9wb3NpdGl2ZSwgOSwgbWV0aG9kPSJHaWJicyIsIGNvbnRyb2wgPSBsaXN0KHNlZWQgPSAxMjM0KSkKdGVybXMobGRhX3Bvc2l0aXZlLDEwKQpgYGAKCmBgYHtyfQojTERBIE1vZGVsIGZvciB3b3JkcyBhc3NvY2lhdGUgd2l0aCBuZWdhdGl2ZSBSZXZpZXMsIHRoYXQgaXMgZm9yIHJldmlld3Mgd2l0aCBzdGFyIHJhdGluZ3Mgb2YgMSBvciAyCmR0bV9tYXRyaXhfbGRhX25lZ2F0aXZlID0gYXMubWF0cml4KGR0bXNfYWxsKQoKdWJlcl9kcml2ZXJfdGV4dCRuZWdhdGl2ZSA9IHViZXJfZHJpdmVyX3RleHQkc3RhcnMgPD0gMiAjQ3JlYXRpbmcgYSByb3cgb2YgYmFkIHJldmlld3MKZHRtX21hdHJpeF9sZGFfbmVnYXRpdmUgPSBkdG1fbWF0cml4X2xkYV9uZWdhdGl2ZVt3aGljaCh1YmVyX2RyaXZlcl90ZXh0JG5lZ2F0aXZlPT1UUlVFKSxdICNUYWtpbmcgb25seSB0aGUgcm93cyB3aXRoIGJhZCByZXZpZXdzCnRlcm1zMyA9IHJvd1N1bXMoZHRtX21hdHJpeF9sZGFfbmVnYXRpdmUpICE9IDAKZHRtX21hdHJpeF9sZGFfbmVnYXRpdmUgPSBkdG1fbWF0cml4X2xkYV9uZWdhdGl2ZVt0ZXJtczMsXQoKbGRhX25lZ2F0aXZlIDwtTERBKGR0bV9tYXRyaXhfbGRhX25lZ2F0aXZlLDksIG1ldGhvZD0iR2liYnMiLCBjb250cm9sID0gbGlzdChzZWVkID0gMTIzNCkpCnRlcm1zKGxkYV9uZWdhdGl2ZSwxMCkKYGBgCgpgYGB7cn0KI0xvb2tpbmcgYXQgdGhlIHdvcmRzIGFzc29jaWF0ZWQgd2l0aCBwb3NpdGl2ZSByZXZpZXdzIHVzaW5nIGJldGEgYW5hbHlzaXMKcG9zaXRpdmVfd29yZHMgPC0gdGlkeShsZGFfcG9zaXRpdmUsIG1hdHJpeCA9ICJiZXRhIikKcG9zdGl2ZV90b3BfdGVybXMgPC0gcG9zaXRpdmVfd29yZHMgJT4lCiAgZ3JvdXBfYnkodG9waWMpICU+JQogIHRvcF9uKDEwLCBiZXRhKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgYXJyYW5nZSh0b3BpYywgLWJldGEpCgpwb3N0aXZlX3RvcF90ZXJtcyAlPiUKICBtdXRhdGUodGVybSA9IHJlb3JkZXIodGVybSwgYmV0YSkpICU+JQogIGdncGxvdChhZXModGVybSwgYmV0YSwgZmlsbCA9IGZhY3Rvcih0b3BpYykpKSArCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGZhY2V0X3dyYXAofiB0b3BpYywgc2NhbGVzID0gImZyZWUiKSArCiAgY29vcmRfZmxpcCgpCmBgYApgYGB7cn0KI0xvb2tpbmcgYXQgdGhlIHdvcmRzIGFzc29jaWF0ZWQgd2l0aCBuZWdhdGl2ZSByZXZpZXdzIHVzaW5nIGJldGEgYW5hbHlzaXMKbmVnYXRpdmVfd29yZHMgPC0gdGlkeShsZGFfbmVnYXRpdmUsIG1hdHJpeCA9ICJiZXRhIikKbmVnYXRpdmVfdG9wX3Rlcm1zIDwtIG5lZ2F0aXZlX3dvcmRzICU+JQogIGdyb3VwX2J5KHRvcGljKSAlPiUKICB0b3BfbigxMCwgYmV0YSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGFycmFuZ2UodG9waWMsIC1iZXRhKQoKbmVnYXRpdmVfdG9wX3Rlcm1zICU+JQogIG11dGF0ZSh0ZXJtID0gcmVvcmRlcih0ZXJtLCBiZXRhKSkgJT4lCiAgZ2dwbG90KGFlcyh0ZXJtLCBiZXRhLCBmaWxsID0gZmFjdG9yKHRvcGljKSkpICsKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZmFjZXRfd3JhcCh+IHRvcGljLCBzY2FsZXMgPSAiZnJlZSIpICsKICBjb29yZF9mbGlwKCkKYGBgCgpgYGB7cn0KI1RoZSBmb2xsb3dpbmcgY29kZSBzbmlwcGV0IHJlYWRzIHRoZSBudW1iZXIgb2YgcmV2aWV3cyBvbiB0aGUgd2Vic2l0ZSBmb3IgdGhlIGx5ZnQgcGFnZQpudW1iZXJfbHlmdCA9IHJlYWRfaHRtbChwYXN0ZSgiaHR0cHM6Ly93d3cuaW5kZWVkLmNvbS9jbXAvTHlmdC9yZXZpZXdzP2Zqb2J0aXRsZT1Ecml2ZXImc3RhcnQ9MCIpKSAlPiUKICBodG1sX25vZGVzKCJiIikgJT4lCiAgaHRtbF90ZXh0KCkKbnVtYmVyX2x5ZnQgPSBnc3ViKHBhdHRlcm4gPSAiLCIsIHJlcGxhY2VtZW50ID0gIiIsIG51bWJlcl9seWZ0KSAjcmVtb3ZpbmcgdGhlIGNvbW1hIHNvIGl0IGNhbiBiZSB0cmFuc2Zvcm1lZCBpbnRvIGEgbnVtYmVyCm51bWJlcl9seWZ0ID0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIobnVtYmVyX2x5ZnQpKQpgYGAKCmBgYHtyfQojU2NyYXBpbmcgdGhlIHRpdGUgb2YgdGhlIHJldmlldywgdGhlIHRleHQsIHRoZSBkYXRlLCBhbmQgdGhlIG51bWJlciBvZiBzdGFycyBnaXZlbiBhbmQgcHV0dGluZyBpdCBpbnRvIGEgZGF0YSBmcmFtZQpwYWdlID0gc2VxKGZyb20gPSAwLCB0byA9IG51bWJlcl9seWZ0LCBieSA9IDIwKSAjRGVyaXZpbmcgdGhlIG51bWJlciBvZiBwYWdlcyB0byBiZSBjb25zaWRlcmVkCgpseWZ0X2RyaXZlcnMgPSBOVUxMICNDcmVhdGluZyBhbiBlbXB0eSBkYXRhIGZyYW1lCgpmb3IgKHN0YXJ0IGluIChwYWdlKSkgeyAjIEZvciBsb29wIHRvIHJlYWQgaW4gdGhlIGZpbGUgYW5kIGFwcGVuZCBpdCB0byBhICJydW5uaW5nIHRvdGFsIGRhdGFmcmFtZSIgY2FsbGVkIGFydGljbGVzfQogbmEgPSBjKE5BLCBOQSxOQSxOQSwgTkEsTkEsTkEsIE5BLE5BLE5BLCBOQSxOQSxOQSwgTkEsTkEsTkEsIE5BLE5BLE5BLCBOQSkKICAgdGl0bGUgPSByZWFkX2h0bWwocGFzdGUoImh0dHBzOi8vd3d3LmluZGVlZC5jb20vY21wL0x5ZnQvcmV2aWV3cz9mam9idGl0bGU9RHJpdmVyJnN0YXJ0PSIsIHN0YXJ0LCBzZXAgPSAnJykpICU+JQogICAgaHRtbF9ub2RlcygiZGl2W2l0ZW1wcm9wPSdyZXZpZXcnXTpudGgtb2YtdHlwZShuKzIpIC5jbXAtcmV2aWV3LXRpdGxlIHNwYW46bnRoLW9mLXR5cGUoMSkiKSAlPiUKICAgIGh0bWxfdGV4dCgpCiNwcmludCh0aXRsZSkKCiAgIGRhdGUgPSByZWFkX2h0bWwocGFzdGUoImh0dHBzOi8vd3d3LmluZGVlZC5jb20vY21wL0x5ZnQvcmV2aWV3cz9mam9idGl0bGU9RHJpdmVyJnN0YXJ0PSIsIHN0YXJ0LCBzZXAgPSAnJykpICU+JQogICAgaHRtbF9ub2RlcygiZGl2W2l0ZW1wcm9wPSdyZXZpZXcnXTpudGgtb2YtdHlwZShuKzIpIHNwYW4uY21wLXJldmlldy1kYXRlLWNyZWF0ZWQiKSAlPiUKICAgIGh0bWxfdGV4dCgpCiAjcHJpbnQoZGF0ZSkKCiByZXZpZXcgPSByZWFkX2h0bWwocGFzdGUoImh0dHBzOi8vd3d3LmluZGVlZC5jb20vY21wL0x5ZnQvcmV2aWV3cz9mam9idGl0bGU9RHJpdmVyJnN0YXJ0PSIsIHN0YXJ0LCBzZXAgPSAnJykpICU+JQogICBodG1sX25vZGVzKCJkaXZbaXRlbXByb3A9J3JldmlldyddOm50aC1vZi10eXBlKG4rMikgc3BhbltpdGVtcHJvcD0ncmV2aWV3Qm9keSddIikgJT4lCiAgIGh0bWxfdGV4dCgpCiAjcHJpbnQocmV2aWV3KQogCiBzdGFycyA9IHJlYWRfaHRtbChwYXN0ZSgiaHR0cHM6Ly93d3cuaW5kZWVkLmNvbS9jbXAvTHlmdC9yZXZpZXdzP2Zqb2J0aXRsZT1Ecml2ZXImc3RhcnQ9Iiwgc3RhcnQsIHNlcCA9ICcnKSkgJT4lCiAgIGh0bWxfbm9kZXMoImRpdltpdGVtcHJvcD0ncmV2aWV3J106bnRoLW9mLXR5cGUobisyKSBkaXYuY21wLXJhdGluZ051bWJlciIpICU+JQogICBodG1sX3RleHQoKQogICAgc3RhcnMgPSBnc3ViKHBhdHRlcm4gPSAiIiwgcmVwbGFjZW1lbnQgPSAiIiwgc3RhcnMpCiAgICBzdGFycyA9IGFzLm51bWVyaWMoc3RhcnMpCiAjcHJpbnQoc3RhcnMpCiAgICAKICAgICBjaXR5ID0gcmVhZF9odG1sKHBhc3RlKCJodHRwczovL3d3dy5pbmRlZWQuY29tL2NtcC9MeWZ0L3Jldmlld3M/ZmpvYnRpdGxlPURyaXZlciZzdGFydD0iLCBzdGFydCwgc2VwID0gJycpKSAlPiUKICAgaHRtbF9ub2RlcygiZGl2W2l0ZW1wcm9wPSdyZXZpZXcnXTpudGgtb2YtdHlwZShuKzIpIHNwYW4uY21wLXJldmlld2VyLWpvYi1sb2NhdGlvbiIpICU+JQogICBodG1sX3RleHQoKQoKICAgIAogICAgCiNUaGUgd2ViIHNjcmFwZXIgYWJvdXQgNSB0aW1lcyB3aWxsIGJyZWFrIGlmIGl0IG9ubHkgZmluZHMgMTkgZWxlbWVudHMgb24gdGhlIHBhZ2UgaW5zdGVhZCBvZiAyMC4gIFRoZSBmb2xsb3dpbmcgc2V0IG9mIGlmIHN0YXRlbWVudHMgYWxsb3dzIGZvciBsb29wIHRvIHJ1biBldmVuIGlmIHRoaXMgb2NjdXJyIGJ5IGluc2VydGluZyBhIHN0cmluZyBvZiBOQSdzIHdoZW4gV2ViIHNjcmFwZXIgZG9lc24ndCByZXR1cm4gdGhlIGNvcnJlY3QgbnVtYmVyIG9mIHZhbHVlcwogIGlmKGxlbmd0aCh0aXRsZSA9PSAyMCkpICB7CiAgdGl0bGUgPSB0aXRsZQp9IGVsc2UgewogIHRpdGxlID0gbmEKfSAKIAppZihsZW5ndGgoZGF0ZSA9PSAyMCkpICB7CiAgZGF0ZSA9IGRhdGUKfSBlbHNlIHsKICBkYXRlID0gbmEKfSAKIAppZihsZW5ndGgocmV2aWV3ID09IDIwKSkgIHsKICByZXZpZXcgPSByZXZpZXcKfSBlbHNlIHsKICByZXZpZXcgPSBuYQp9ICAKIAogaWYobGVuZ3RoKHN0YXJzID09IDIwKSkgIHsKICBzdGFycyA9IHN0YXJzCn0gZWxzZSB7CiAgc3RhcnMgPSBuYQp9IAogICAgIAppZihsZW5ndGgoY2l0eSA9PSAyMCkpICB7CiAgY2l0eSA9IGNpdHkKfSBlbHNlIHsKICB0aXRsZSA9IG5hCn0gCiAKdGVtcCA9IGRhdGEuZnJhbWUodGl0bGUsIHJldmlldywgZGF0ZSwgc3RhcnMsIGNpdHkpCmx5ZnRfZHJpdmVycyA9IHJiaW5kKGx5ZnRfZHJpdmVycywgdGVtcCkKfQpseWZ0X2RyaXZlcnMgPSBuYS5vbWl0KGx5ZnRfZHJpdmVycykKbHlmdF9kcml2ZXJzJHBvc2l0aW9uID0gImRyaXZlciIKCmx5ZnRfZHJpdmVycyRkYXRlID0gc3RyX3JlcGxhY2VfYWxsKGx5ZnRfZHJpdmVycyRkYXRlLCAiLCIsICIiKQpseWZ0X2RyaXZlcnMkZGF0ZSA9IGFzLkRhdGUobHlmdF9kcml2ZXJzJGRhdGUsIGZvcm1hdCA9ICIlQiAlZCAlWSIpICNDb252ZXJ0aW5nIGl0IHRvIGRhdGUgZm9ybWF0CmBgYAoKYGBge3J9Cmx5ZnRfZHJpdmVycyRzZW50aW1lbnQgPSBhcy5udW1lcmljKGdldF9zZW50aW1lbnQoYXMuY2hhcmFjdGVyKGx5ZnRfZHJpdmVycyRyZXZpZXcpKSkgI0dldHRpbmcgc2VudGltZW50CgpseWZ0X2RyaXZlcnMyID0gbHlmdF9kcml2ZXJzICU+JQogIGdyb3VwX2J5KGRhdGUpICU+JQogIHN1bW1hcmlzZShtZWFuKHNlbnRpbWVudCkpCgpuYW1lcyhseWZ0X2RyaXZlcnMyKSA9IGMoImRhdGUiLCAic2VudGltZW50IikKCmx5ZnRfZHJpdmVyc19tb250aGx5ID0gbHlmdF9kcml2ZXJzMiAlPiUgI0FnZ3JlZ2F0aW5nIGRhdGEgaW50byBtb250aCBiaW5zCiAgICBncm91cF9ieShtb250aD1mbG9vcl9kYXRlKGRhdGUsICJtb250aCIpKSAlPiUKICAgIHN1bW1hcmlzZShzZW50aW1lbnQgPSBtZWFuKHNlbnRpbWVudCkpCgpseWZ0X2RyaXZlcnNfbW9udGhseSAlPiUgZ2dwbG90KGFlcyh4PW1vbnRoLCB5PXNlbnRpbWVudCkpICsgZ2VvbV9jb2woY29sb3I9InBpbmsiKSArIHhsYWIoIlNlcXVlbmNlIikgKwogICAgeWxhYigiU2VudGltZW50IikgKyBnZ3RpdGxlKCJMeWZ0IERyaXZlciBTZW50aW1lbnQiKSArCiAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKyBnZW9tX3Ntb290aChtZXRob2QgPSAiYXV0byIpICt4bGFiKCJEYXRlIikKYGBgCgpgYGB7cn0KIyBBcmVhIHBsb3QKdWJlcl9kcml2ZXJzX21vbnRobHkkdHlwZT1OVUxMCnViZXJfZHJpdmVyc19tb250aGx5JGNvbXBhbnkgPSAidWJlciIKbHlmdF9kcml2ZXJzX21vbnRobHkkY29tcGFueSA9ICJseWZ0IgoKbHlmdF92X3ViZXIgPSByYmluZCh1YmVyX2RyaXZlcnNfbW9udGhseSwgbHlmdF9kcml2ZXJzX21vbnRobHkpCgpnZ3Bsb3QobHlmdF92X3ViZXIsIGFlcyh4ID0gbW9udGgsIHkgPSBzZW50aW1lbnQpKSArIAogIGdlb21fYXJlYShhZXMoY29sb3IgPSBjb21wYW55LCBmaWxsID0gY29tcGFueSksIAogICAgICAgICAgICBhbHBoYSA9IDAuMywgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjgpKSArIHhsYWIoIkRhdGUiKSArIHlsYWIoIlNlbnRpbWVudCIpICsgZ2d0aXRsZSgiTHlmdCB2ZXJzdXMgVWJlciIsIHN1YnRpdGxlID0gIkRyaXZlciBTZW50aW1lbnQgb3ZlciBUaW1lIikrIHNjYWxlX3hfZGF0ZShsaW1pdHMgPSBhcy5EYXRlKGMoIjIwMTQtMDUtMDEiLCIyMDE5LTA1LTA1IikpKQpgYGAKCgpMeWZ0IEJlc3QgQ2l0aWVzIE9sZApgYGB7cn0KbHlmdF9kcml2ZXJfY2l0eSA9IGx5ZnRfZHJpdmVycyAlPiUKICBncm91cF9ieShjaXR5KSAlPiUKICBzdW1tYXJpc2UobWVhbihzZW50aW1lbnQpKQoKbmFtZXMobHlmdF9kcml2ZXJfY2l0eSkgPSBjKCJjaXR5IiwgInNlbnRpbWVudCIpCmx5ZnRfZHJpdmVyX2NpdHkgPWx5ZnRfZHJpdmVyX2NpdHkgJT4lCiAgICBhcnJhbmdlKGRlc2Moc2VudGltZW50KSkKCgpseWZ0X2RyaXZlcl9jaXR5JGNpdHkyID0gZ3N1YihwYXR0ZXJuID0gIiwiLCByZXBsYWNlbWVudCA9ICIiLCBseWZ0X2RyaXZlcl9jaXR5JGNpdHkpICNUYWtpbmcgb3V0IHRoZSBjb21tYSB0byBtYWtlIGl0IHJlYWRhYmxlIHRvIGdvb2dsZQpgYGAKCmBgYHtyfQpseWZ0X2RyaXZlcl9jaXR5IDwtIGx5ZnRfZHJpdmVyX2NpdHkgJT4lIAogIG11dGF0ZV9nZW9jb2RlKGNpdHkyKSAjQWRkaW5nIGEgbGF0L2xvbiBjb2x1bW4gZm9yIGVhY2ggY2l0eQpgYGAKCmBgYHtyfQpseWZ0X2RyaXZlcl9jaXR5MiA9IGx5ZnRfZHJpdmVyX2NpdHkgI0R1cGxpY2F0aW5nIHRoZSBkYXRhZnJhbWUgYXMgaXQgdGFrZXMgYSBsb25nIHRpbWUgZm9yIGdvb2dsZSB0byBnZW9jb2RlCgpsYXRsb25nMnN0YXRlIDwtIGZ1bmN0aW9uKHBvaW50c0RGKSB7ICNDb252ZXRzIGFsbCB0aGUgY29vcmRpbmF0ZXMgYmFjayB0byBzdGF0ZSBjb2RlcwogICAgIyBQcmVwYXJlIFNwYXRpYWxQb2x5Z29ucyBvYmplY3Qgd2l0aCBvbmUgU3BhdGlhbFBvbHlnb24KICAgICMgcGVyIHN0YXRlIChwbHVzIERDLCBtaW51cyBISSAmIEFLKQogICAgc3RhdGVzIDwtIG1hcCgnc3RhdGUnLCBmaWxsPVRSVUUsIGNvbD0idHJhbnNwYXJlbnQiLCBwbG90PUZBTFNFKQogICAgSURzIDwtIHNhcHBseShzdHJzcGxpdChzdGF0ZXMkbmFtZXMsICI6IiksIGZ1bmN0aW9uKHgpIHhbMV0pCiAgICBzdGF0ZXNfc3AgPC0gbWFwMlNwYXRpYWxQb2x5Z29ucyhzdGF0ZXMsIElEcz1JRHMsCiAgICAgICAgICAgICAgICAgICAgIHByb2o0c3RyaW5nPUNSUygiK3Byb2o9bG9uZ2xhdCArZGF0dW09d2dzODQiKSkKCiAgICAjIENvbnZlcnQgcG9pbnRzREYgdG8gYSBTcGF0aWFsUG9pbnRzIG9iamVjdCAKICAgIHBvaW50c1NQIDwtIFNwYXRpYWxQb2ludHMocG9pbnRzREYsIAogICAgICAgICAgICAgICAgICAgIHByb2o0c3RyaW5nPUNSUygiK3Byb2o9bG9uZ2xhdCArZGF0dW09d2dzODQiKSkKCiAgICAjIFVzZSAnb3ZlcicgdG8gZ2V0IF9pbmRpY2VzXyBvZiB0aGUgUG9seWdvbnMgb2JqZWN0IGNvbnRhaW5pbmcgZWFjaCBwb2ludCAKICAgIGluZGljZXMgPC0gb3Zlcihwb2ludHNTUCwgc3RhdGVzX3NwKQoKICAgICMgUmV0dXJuIHRoZSBzdGF0ZSBuYW1lcyBvZiB0aGUgUG9seWdvbnMgb2JqZWN0IGNvbnRhaW5pbmcgZWFjaCBwb2ludAogICAgc3RhdGVOYW1lcyA8LSBzYXBwbHkoc3RhdGVzX3NwQHBvbHlnb25zLCBmdW5jdGlvbih4KSB4QElEKQogICAgc3RhdGVOYW1lc1tpbmRpY2VzXQp9CgpseWZ0X2RyaXZlcl9jaXR5MyA9IGx5ZnRfZHJpdmVyX2NpdHkyCmx5ZnRfZHJpdmVyX2NpdHkzID0gbmEub21pdChseWZ0X2RyaXZlcl9jaXR5MykgI1JlbW92aW5nIE5BIHZhbHVlcwpseWZ0X2RyaXZlcl9jaXR5MyRzdGF0ZSA9IGxhdGxvbmcyc3RhdGUobHlmdF9kcml2ZXJfY2l0eTNbNDo1XSkgI0FkZGluZyBhIGNvbHVtbiBvZiBzdGF0ZSBjb2RlcwpseWZ0X2RyaXZlcl9jaXR5MyA9IG5hLm9taXQobHlmdF9kcml2ZXJfY2l0eTMpI1JlbW92aW5nIE5BIHZhbHVlcwoKc3RhdGVzID0gcmVhZC5jc3YoInN0YXRlbGF0bG9uZy5jc3YiKSAjQSBkYXRhIGZhbWUgd2l0aCBzdGF0ZXMgYW5kIHN0YXRlIGNvZGVzCnN0YXRlcyA9IHN0YXRlc1ssIGMoLTIsLTMpXQpzdGF0ZXMkQ2l0eSA9IHRvbG93ZXIoc3RhdGVzJENpdHkpICNjb252ZXJ0aW5nIHN0YXRlIG5hbWVzIHRvIGxvd2VyY2FzZQpseWZ0X2RyaXZlcl9jaXR5NCA9IGxlZnRfam9pbihseWZ0X2RyaXZlcl9jaXR5Mywgc3RhdGVzLCBieSA9IGMoInN0YXRlIiA9IkNpdHkiKSkgI0FBc3NvY2lhdGluZyBlYWNoIHN0YXRlIHdpdGggYSBjb2RlIHRvIGJlIHJlYWQgaW50byBwbG90bHkKYGBgCgpMeWZ0IERyaXZlciBCZXN0IENpdGllcwpgYGB7cn0KbHlmdF9kcml2ZXJfY2l0eTEwID0gbHlmdF9kcml2ZXJfY2l0eTIKCmx5ZnRfZHJpdmVyX2NpdHkxMCRjb29yZGluYXRlcyA9IHBhc3RlKGx5ZnRfZHJpdmVyX2NpdHkxMCRsYXQsIGx5ZnRfZHJpdmVyX2NpdHkxMCRsb24sIHNlcCA9ICIsIikgI1Bhc3RpbmcgdGhlIGNvb3JkaW5hdGVzIHRvZ2V0aGVyCgpseWZ0X2RyaXZlcl9jaXR5MTAkY2l0eW5hbWUgPSBnZW9jb2RlKGx5ZnRfZHJpdmVyX2NpdHkxMCRjb29yZGluYXRlcywgb3V0cHV0ID0gIm1vcmUiKQoKbHlmdF9kcml2ZXJfY2l0eTEwID0gbmEub21pdChseWZ0X2RyaXZlcl9jaXR5MTApCmx5ZnRfZHJpdmVyX2NpdHkxMAoKbHlmdF9kcml2ZXJfY2l0eTEwPWx5ZnRfZHJpdmVyX2NpdHkxMFstYygyMCwgMTQ0KSxdCiNUaGUgZm9sbG93aW5nIGNvZGUgcHVsbHMgb3V0IHRoZSBjaXR5IG5hbWUgb25seSBiZWNhdXNlIHRoZSBkYXRhIHdhcyB2ZXJ5IG1lc3N5CnBhdD0iKCwuXFx3KywpfCgsLlxcdysuXFx3KywpIgpseWZ0X2RyaXZlcl9jaXR5MTAkcmVhbGNpdHluYW1lID0gZ3N1YigiKCxcXHMpfCwiLCIiLHJlZ21hdGNoZXMobTwtc3Ryc3BsaXQobHlmdF9kcml2ZXJfY2l0eTEwJGNpdHluYW1lW1s1XV0sIlxcfCIpLHJlZ2V4cHIocGF0LG0pKSkKCgpseWZ0X2RyaXZlcl9jaXR5MTAkcmVhbGNpdHluYW1lID0gZ3N1YigiKCxcXHMpfCwiLCIiLHJlZ21hdGNoZXMobTwtc3Ryc3BsaXQobHlmdF9kcml2ZXJfY2l0eTEwJGNpdHluYW1lW1s1XV0sIlxcfCIpLHJlZ2V4cHIocGF0LG0pKSkKCmx5ZnRfZHJpdmVyX2NpdHkxMSA9IGx5ZnRfZHJpdmVyX2NpdHkxMCAlPiUKICBncm91cF9ieShyZWFsY2l0eW5hbWUpICU+JQogIHN1bW1hcmlzZShtZWFuKHNlbnRpbWVudCkpCgpuYW1lcyhseWZ0X2RyaXZlcl9jaXR5MTEpID0gYygiY2l0eSIsICJzZW50aW1lbnQiKSAgCmx5ZnRfZHJpdmVyX2NpdHkxMSA9IGx5ZnRfZHJpdmVyX2NpdHkxMSAlPiUKICBhcnJhbmdlKGRlc2Moc2VudGltZW50KSkKCmx5ZnRfZHJpdmVyX2NpdHkxMSRjaXR5IDwtIGZhY3RvcihseWZ0X2RyaXZlcl9jaXR5MTEkY2l0eSwgbGV2ZWxzID0gbHlmdF9kcml2ZXJfY2l0eTExJGNpdHlbb3JkZXIobHlmdF9kcml2ZXJfY2l0eTExJHNlbnRpbWVudCldKQoKbHlmdF9iZXN0X2NpdGllczIgPSBnZ3Bsb3QobHlmdF9kcml2ZXJfY2l0eTExWzE6MjAsXSwgYWVzKHg9Y2l0eSwgeT1zZW50aW1lbnQpKSArIAogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5Iiwgd2lkdGg9LjUsIGZpbGw9ImhvdHBpbmsiKSArIAogIGxhYnModGl0bGU9Ikx5ZnQgQXZlcmFnZSBTZW50aW1lbnQgYnkgQ2l0eSIpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NjUsIHZqdXN0PTAuNikpCmx5ZnRfYmVzdF9jaXRpZXMyCmBgYAoKCgoKYGBge3J9Cmx5ZnRfZHJpdmVyX2NpdHk1ID0gbHlmdF9kcml2ZXJfY2l0eTQgJT4lICNDcmVhdGluZyBhIGRhdGEgZnJhbWUgZ3Jyb3VwaW5nIGJ5IHN0YXRlIGFuZCBmaW5kaW5nIHRoZSBtZWFuIHNlbnRpbWVudCBmb3IgZHJpdmVycwogIGdyb3VwX2J5KFN0YXRlKSAlPiUKICBzdW1tYXJpc2UobWVhbihzZW50aW1lbnQpKSAKbmFtZXMobHlmdF9kcml2ZXJfY2l0eTUpID0gYygiU3RhdGUiLCAiU2VudGltZW50IikKCiMgZ2l2ZSBzdGF0ZSBib3VuZGFyaWVzIGEgd2hpdGUgYm9yZGVyCmwgPC0gbGlzdChjb2xvciA9IHRvUkdCKCJ3aGl0ZSIpLCB3aWR0aCA9IDIpCiMgc3BlY2lmeSBzb21lIG1hcCBwcm9qZWN0aW9uL29wdGlvbnMKbHlmdF9tYXAgPC0gbGlzdCgKICBzY29wZSA9ICd1c2EnLAogIHByb2plY3Rpb24gPSBsaXN0KHR5cGUgPSAnYWxiZXJzIHVzYScpLAogIHNob3dsYWtlcyA9IFRSVUUsCiAgbGFrZWNvbG9yID0gdG9SR0IoJ2xpZ2h0Ymx1ZScpKQoKbHlmdF9zdGF0ZV9tYXAgPC0gcGxvdF9nZW8obHlmdF9kcml2ZXJfY2l0eTUsIGxvY2F0aW9ubW9kZSA9ICdVU0Etc3RhdGVzJykgJT4lCiAgYWRkX3RyYWNlKAogICAgeiA9IH5TZW50aW1lbnQsIGxvY2F0aW9ucyA9IH5TdGF0ZSwKICAgIGNvbG9yID0gflNlbnRpbWVudCwgY29sb3JzID0gJ1JlZHMnCiAgKSAlPiUKICBjb2xvcmJhcih0aXRsZSA9ICJTZW50aW1lbnQgTGV2ZWwiKSAlPiUKICBsYXlvdXQoCiAgICB0aXRsZSA9ICdIaXN0b3JpY2FsIGx5ZnQgRHJpdmVyIFNlbnRpbWVudCBieSBTdGF0ZTxicj4oSG92ZXIgZm9yIGJyZWFrZG93biknLAogICAgZ2VvID0gbWFwCiAgKQpseWZ0X3N0YXRlX21hcAoKI2NoYXJ0X2xpbmtfbHlmdCA9IGFwaV9jcmVhdGUobHlmdF9zdGF0ZV9tYXAsIGZpbGVuYW1lPSJseWZ0X2Nob3JvcGxldGgiKQojY2hhcnRfbGlua19seWZ0ICNFeHBvcnRpbmcgdGhlIEx5ZnQgc3RhdGUgbWFwIHRvIGJlIGVtYmVkZGVkCmBgYApQZXJmb3JtaW5nIGEgdHdvIHNhbXBsZSB0LXRlc3QgdG8gc2VlIGlmIHNlbnRpbWVudCBhbW9uZ3N0IHViZXIgZHJpdmVycyB3YXMgYmV0dGVyIHRoYW4gdGhhdCBhbW9uZ3N0aCBseWZ0IGRyaXZlcnMKCmBgYHtyfQp0LnRlc3QodWJlcl9kcml2ZXJzJHNlbnRpbWVudCwgbHlmdF9kcml2ZXJzJHNlbnRpbWVudCwgcGFpcmVkID0gRkFMU0UsIGFsdGVybmF0aXZlID0gInR3by5zaWRlZCIpCmBgYAoKCgpQZXJmaXJtaW5nIGEgdC10ZXN0IGJldHdlZW4gdWJlciBkcml2ZXJzIGJlZm9yZSBhbmQgYWZ0ZXIgdGhlIENFTyBzd2l0Y2gKYGBge3J9CnQudGVzdCh1YmVyX2RyaXZlcnNfYmVmb3JlJHNlbnRpbWVudCwgdWJlcl9kcml2ZXJzX2FmdGVyJHNlbnRpbWVudCwgcGFpcmVkID0gRkFMU0UsIGFsdGVybmF0aXZlID0gInR3by5zaWRlZCIpCmBgYAoKUGVyZmlybWluZyBhIHQtdGVzdCBiZXR3ZWVuIHViZXIgKipFbXBsb3llZXMgYmVmb3JlIGFuZCBhZnRlciB0aGUgQ0VPIHN3aXRjaApgYGB7cn0KdC50ZXN0KHViZXJfZW1wbG95ZWVzX2JlZm9yZSRzZW50aW1lbnQsIHViZXJfZW1wbG95ZWVzX2FmdGVyJHNlbnRpbWVudCwgcGFpcmVkID0gRkFMU0UsIGFsdGVybmF0aXZlID0gInR3by5zaWRlZCIpCmBgYAoKYGBge3J9CnQudGVzdCh1YmVyX2RyaXZlcnMkc2VudGltZW50LCB1YmVyX2VtcGxveWVlcyRzZW50aW1lbnQsIHBhaXJlZCA9IEZBTFNFLCBhbHRlcm5hdGl2ZSA9ICJ0d28uc2lkZWQiKQpgYGAKTG9va2luZyB0byBzZWUgaWYgdGhlcmUgaXMgYSBjb3JyZWxhdGlvbiBiZXR3ZWUKYGBge3J9Cm1pbmltdW1fd2FnZSA9IHJlYWQuY3N2KCJNaW5pbXVtIFdhZ2UgRGF0YS5jc3YiKQptaW5pbXVtX3dhZ2UkU3RhdGUgPSB0b2xvd2VyKG1pbmltdW1fd2FnZSRTdGF0ZSkKbWluaW11bV93YWdlX2F2ZyA9IG1pbmltdW1fd2FnZSAlPiUKICBmaWx0ZXIoWWVhciA9PSAyMDEyIHwgWWVhciA9PSAyMDEzIHxZZWFyID09IDIwMTQgfFllYXIgPT0gMjAxNSB8WWVhciA9PSAyMDE2IHxZZWFyID09IDIwMTcgfFllYXIgPT0gMjAxOCB8WWVhciA9PSAyMDE5KSAlPiUKICBncm91cF9ieShTdGF0ZSkgJT4lCiAgc3VtbWFyaXNlKG1lYW4oSGlnaC5WYWx1ZSkpCgptaW5pbXVtX3dhZ2VfYXZnICA9IGxlZnRfam9pbihtaW5pbXVtX3dhZ2VfYXZnLCBzdGF0ZXMsIGJ5ID0gYygiU3RhdGUiID0iQ2l0eSIpKQptaW5pbXVtX3dhZ2VfYXZnID0gbmEub21pdChtaW5pbXVtX3dhZ2VfYXZnKQoKY29sbGVjdGl2ZSA9IGxlZnRfam9pbih1YmVyX2RyaXZlcl9jaXR5NSwgbWluaW11bV93YWdlX2F2ZywgYnkgPSBjKCJTdGF0ZSIgPSJTdGF0ZS55IikpCgoKbmFtZXMoY29sbGVjdGl2ZSkgPSBjKCJDb2RlIiwgIlNlbnRpbWVudCIsICJTdGF0ZSIsICJNaW5XYWdlIikKc2VudGltZW50X3dhZ2VfY29ycmVsYXRpb24gPSBjb3IoY29sbGVjdGl2ZSRTZW50aW1lbnQsIGNvbGxlY3RpdmUkTWluV2FnZSkgI0xvb2tpbmcgYXQgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gc2VudGltZW50IGFuZCBtaW5pbXVtIHdhZ2UuICBBIG5lZ2F0aXZlIGNvcnJlbGF0aW9uIGlzIGV4cGVjdGVkLCBiZWNhdXNlIHRoZSBoaWdoZXIgdGhlIG1pbmltdW0gd2FnZSwgdGhlIG1vcmUgZGlzY29udGVudCB0aGUgZHJpdmVyIHdvdWxkIGJlIGJ5IHJpZGluZyB3aXRoIHViZXIKCnNlbnRpbWVudF93YWdlX2NvcnJlbGF0aW9uICNMb29raW5nIGF0IHRoZSBjb3JyZWxhdGlvbiBvZiBzdGF0ZSBtaW5pbXVtIHdhZ2UgYW5kIHNlbnRpbWVudApgYGAKCkNyZWF0aW5nIGEgU2NhdHRlcnBsb3QKYGBge3J9Cmdnc2NhdHRlcihjb2xsZWN0aXZlLCB4ID0gIk1pbldhZ2UiLCB5ID0gIlNlbnRpbWVudCIsIAogICAgICAgICAgYWRkID0gInJlZy5saW5lIiwgY29uZi5pbnQgPSBUUlVFLCAKICAgICAgICAgIGNvci5jb2VmID0gVFJVRSwgY29yLm1ldGhvZCA9ICJwZWFyc29uIiwKICAgICAgICAgIHhsYWIgPSAiTWluaW11bSBXYWdlIiwgeWxhYiA9ICJTZW50aW1lbnQiKSAKYGBgCgoKVGhlIGZvbGxvd2luZyBibG9jayBvZiBjb2RlIGxvb2tlZCB0byBzZWUgaWYgdmFyaWFibGVzIGJ5IGluIGNlbnN1cyBkYXRhIHdlcmUgYWJsZSB0byBwcmVkaWN0IHNlbnRpbWVudCBieSBzdGF0ZS4gIFVuZm9ydHVuYXRlbHksIEkgY291bGRuJ3QgYnVpbGQgdGhlIG1vZGVsIGJlY2F1c2UgdGhlIGNlbnN1cyBtZXRyaWNzIHdlcmVuJ3QgYXMgcmVsZXZhbnQgYXMgSSB0aG91Z2h0IAoKCgoKYGBge3J9CmNlbnN1cyA9IHJlYWQuY3N2KCJhY3MyMDE3X2NvdW50eV9kYXRhLmNzdiIpCgpjZW5zdXNfc3RhdGUgPSBjZW5zdXMgJT4lICNDb2xsYXBzaW5nIGJ5IHN0YXRlIGFuZCBjb21wdXRpbmcgdGhlIGF2ZXJhZ2Ugb2Ygc2V2ZXJhbCBwb3RlbnRpYWxseSBpbXBvcnRhbnQgbWV0cmljcyBieSBzdGF0ZQogIGdyb3VwX2J5KFN0YXRlKSAlPiUKICBzdW1tYXJpc2UobWVkaWFuKEluY29tZSksIG1lYW4oSW5jb21lUGVyQ2FwKSwgbWVhbihQb3ZlcnR5KSwgbWVhbihQcm9mZXNzaW9uYWwpLCBtZWFuKFNlcnZpY2UpLCBtZWFuKE9mZmljZSksIG1lYW4oT2ZmaWNlKSwgbWVhbihDb25zdHJ1Y3Rpb24pLCBtZWFuKFByb2R1Y3Rpb24pLCBtZWFuKERyaXZlKSwgbWVhbihDYXJwb29sKSwgbWVhbihUcmFuc2l0KSwgbWVhbihXYWxrKSwgbWVhbihXb3JrQXRIb21lKSwgbWVhbihNZWFuQ29tbXV0ZSksIG1lYW4oRW1wbG95ZWQpLCBtZWFuKFVuZW1wbG95bWVudCkpCgpjZW5zdXNfc3RhdGUkU3RhdGUgPSB0b2xvd2VyKGNlbnN1c19zdGF0ZSRTdGF0ZSkgI01ha2luZyBzdGF0ZSBuYW1lcyBsb3dlcmNhc2UKY2Vuc3VzX3N0YXRlICA9IGxlZnRfam9pbihjZW5zdXNfc3RhdGUsIHN0YXRlcywgYnkgPSBjKCJTdGF0ZSIgPSJDaXR5IikpICNKb2luaW5nIHdpdGggc3RhdGUgY29kZQpjZW5zdXNfc3RhdGUgPSBuYS5vbWl0KGNlbnN1c19zdGF0ZSkgI1JlbW92aW5nIE5BIHZhbHVlcwpuYW1lcyhjZW5zdXNfc3RhdGUpWzE4XSA9IGMoIlN0YXRlX0NvZGUiKQoKY2Vuc3VzX3ViZXJfc3RhdGUgPSBsZWZ0X2pvaW4odWJlcl9kcml2ZXJfY2l0eTUsIGNlbnN1c19zdGF0ZSwgYnkgPSBjKCJTdGF0ZSIgPSJTdGF0ZV9Db2RlIikpICNNZXJnaW5nIHViZXIgZHJpdmVyIHNlbnRpbWVudCBhbmQgb3RoZXIgY2Vuc3VzIG1ldHJpY3MKCmNlbnN1c191YmVyX3N0YXRlID0gY2Vuc3VzX3ViZXJfc3RhdGVbLC0zXSAjUmVtb3ZpbmcgdGhlIGFjdHVhbCBzdGF0ZSBuYW1lcwpnZXR3ZCgpCndyaXRlLmNzdihjZW5zdXNfdWJlcl9zdGF0ZSwgZmlsZSA9ICJ1YmVyLmNzdiIscm93Lm5hbWVzPVRSVUUpCmBgYAoKCmBgYHtyfQphbGxfc3RhdGVzID0gTlVMTAogIGlubmVyX2pvaW4odWJlcl9kcml2ZXJfY2l0eTUsIGx5ZnRfZHJpdmVyX2NpdHk1LCBieSA9ICJTdGF0ZSIpCnNlbnRpbWVudF9jb3JyZWxhdGlvbiA9IE5VTEwKICBjb3IoYWxsX3N0YXRlcyRTZW50aW1lbnQueCwgYWxsX3N0YXRlcyRTZW50aW1lbnQueSkKc2VudGltZW50X2NvcnJlbGF0aW9uIApgYGAKCgoK