** Introduction Web Scraping ** Web scraping is a technique for converting the data present in unstructured format (HTML tags) over the web to the structured format which can easily be accessed and used. Most of the data available over the web is not readily available. It is present in an unstructured format (HTML format) and is not downloadable. Therefore, it requires knowledge and expertise to use this data.

We can locate useful data based on their CSS selectors, especially when the webpage uses semantic tag attributes. We can use [selectorgadget] (http://selectorgadget.com/) to find out which css selector matches the “review”. SelectGadget can be added an extension in Google chrome. It is shown as a magnifying glass.

pacman::p_load(tidyverse,tidytext,viridis,rvest,tm,wordcloud,SnowballC,tidyquant,ggridges,scales,highcharter,topicmodels)

We can specify the css selector in html_nodes() and extract the text with html_text(). We scrab over 1800 reviews of Fiat Chrysler Automobiles from glassdoor. There are about 155 webpages which contain these reviews.

n=155
#The reviews has 155 pages,thus n=155
FCA_urls <- paste0("https://www.glassdoor.com/Reviews/FCA-Fiat-Chrysler-Automobiles-Reviews-E149_P",seq(2, n), ".htm")
FCA_urls<-c("https://www.glassdoor.com/Reviews/FCA-Fiat-Chrysler-Automobiles-Reviews-E149.htm",FCA_urls)
FCA_html <- FCA_urls %>%
    map_chr(~ read_html(.) %>% html_node(".hreview")%>%html_text())
FCA_html[[1]]
[1] "Featured Review Helpful (1)\"love the company has taught me a lot of new information in my contained growth as a mechanic\"StarStarStarStarStarCurrent Employee - Service Technician in Dayton, OHCurrent Employee - Service Technician in Dayton, OHI have been working at FCA Fiat Chrysler Automobiles full-time (More than 3 years)Prospros are that the company is great, pays for your training that will help in advancement. I get to work on the new cars and technology in the automotive fieldConsno cons as of yet, I love the work I do.and the folk I work with. plan on staying with the company through retirementAdvice to Managementkeep up the good workShare on FacebookShare on TwitterShare on WhatsAppShare via EmailCopy LinkLink Copied!Flag as InappropriateFlag as InappropriateHelpful (1) FCA Fiat Chrysler Automobiles Response seconds ago Edit •  Delete FCA Fiat Chrysler Automobiles 2017-09-30 21:14 PDT"

Data Preparation

We can remove all unwanted characters at this stage

#Data-Preprocessing: removing '\n'
FCA_html<-gsub("\n","",FCA_html)
#remove all round brackets
FCA_html<-FCA_html%>%str_replace_all("\\(|\\)", "")
#remove all \\
FCA_html<-FCA_html%>%str_replace_all("\\\\", "")
#remove all non words and non numbers
#FCA_html<-FCA_html%>%str_replace_all("[^A-Za-z0-9]", "")
#remove all • 
FCA_html<-FCA_html%>%str_replace_all("\\•  ", "")
#remove all & 
FCA_html<-FCA_html%>%str_replace_all("\\ & ", "")
#remove all  non printable words
FCA_html<-FCA_html%>%str_replace_all("[^[:print:]]", "")
#remove all \
FCA_html<-FCA_html%>%str_replace_all(pattern = "\"", replacement = "")
#FCAindeed2<-FCAindeed2%>%stringi::stri_unescape_unicode()
# remove digits
#FCA_html%>%str_replace_all(pattern = "[[:digit:]]+", replacement = "")
#tm::removeNumbers(FCA_html)
#### pattern for dates
pattern ="\\(?\\d{4}\\)?[.-]? *\\d{2}[.-]? *[.-]?\\d{2}"
date=FCA_html%>%str_extract_all(pattern)
#FCA_html[[1]]%>%str_subset(pattern = "([0-9]{1,2})[- .]([a-zA-Z]+)[- .]([0-9]{4})")
#FCA_html[[1]]
#unlist(Date)
Date=as.Date(unlist(date))
#FCA_html_2=data_frame(Date=as.Date(unlist(date)),FCA_html)
get_sentiments(lexicon = "nrc")%>%
    count(sentiment, sort = TRUE)

Convert the text data to dataframe.

GlassdoorPages <- data_frame(date=as.Date(unlist(date)),page = seq(1, n),
                      text = c(FCA_html))%>%arrange(desc(date))
GlassdoorPages%>%head(5)
GlassdoorPages%>%tail(5)

Now we have the letters, and can convert this to a tidy text format.

tidy_FCA <- GlassdoorPages %>%
    unnest_tokens(word, text) %>%
    add_count(date) %>%
    dplyr::rename(date_total = n)
#remove stop words
data("stop_words")
tidy_FCA <- tidy_FCA %>%
  anti_join(stop_words)

Remove some other user defined stop words.

stop_user=c("linklink","whatsappshar","auburn","twittershar","edit","delet","via","edit","delet","via","starstarstarstarstarwork","pdt","hill","ago",
            "facebookshar")
stop_user2=data_frame(word=stop_user)
tidy_FCA <- tidy_FCA %>%
  anti_join(stop_user2)
tidy_FCA%>%head()

Next, let’s implement the sentiment analysis.

FCA_sentiment <- tidy_FCA %>%
    inner_join(get_sentiments("nrc"))
FCA_sentiment%>%head()

Now we have all we need to see the relative changes in these sentiments over the years.

theme_set(theme_bw())
#Alternatively
#FCA_sentiment%>%group_by(page, page_total, sentiment)%>%count()
FCA_sentiment %>%
    count(date, date_total, sentiment) %>%
    filter(sentiment %in% c("positive", "negative", 
                            "joy", "trust","fear","sadness"))%>%
    mutate(sentiment = as.factor(sentiment)) %>%
    #ggplot(aes(page, n / page_total, fill = sentiment)) +
     ggplot(aes(date, n / sum(n), fill = sentiment)) +
    geom_area(position = "identity", alpha = 0.5) +
    labs(y = "Relative frequency", x = "Year",
         title = "Sentiment analysis of FCA Glassdoor Reviews",
         subtitle = "Using the nrc lexicon")+theme_bw()+
 scale_fill_manual(values=viridis_pal(option = "D")(6))+
   scale_y_continuous(labels = scales::percent)

FCA_sentiment %>%
    count(date, date_total, sentiment) %>%
    filter(sentiment %in% c("positive", "negative", 
                            "joy", "trust","fear","sadness"))%>%
    mutate(sentiment = as.factor(sentiment)) %>%
    #ggplot(aes(page, n / page_total, fill = sentiment)) +
     ggplot(aes(x=date,y= n / sum(n), fill = sentiment,height=n / sum(n),group=sentiment)) +
   geom_ridgeline_gradient() +
    labs(y = "Relative frequency", x = "Year",
         title = "Sentiment analysis of FCA Glassdoor Reviews",
         subtitle = "Using the nrc lexicon")+theme_bw()+
 scale_fill_viridis(discrete = TRUE, direction = -1) +
   scale_y_continuous(labels = scales::percent)

#expand x and y limits  
#expand_limits(x=c(0,160), y=c(0, 0.0005))

At the beginning 2008 to the end of 2013,the positive sentiments outweigh the negative sentiments. The level of trust in FCA was also higher compared to sad sentiments expressed the reviewers. From 2014, the percentage of positive/trust sentiments does not overwhemly dorminate the negative/sadness sentiments. In general, the positive sentiments marked a decline from the 2014 onwards.

FCA_sentiment %>%
    count(date, date_total, sentiment) %>%
  #  filter(sentiment %in% c("positive", "negative",  "joy", "trust","fear","sadness"))%>%
  mutate(sentiment = forcats::fct_lump(sentiment, 6))%>%
    #mutate(sentiment = as.factor(sentiment)) %>%
    ggplot(aes(date, n / date_total, fill = sentiment)) +
     #ggplot(aes(page, n / sum(n), fill = sentiment)) +
    geom_area(position = "identity", alpha = 0.5) +
    labs(y = "Relative frequency", x = "Year",
         title = "Sentiment analysis of FCA Glassdoor Reviews",
         subtitle = "Using the nrc lexicon")+theme_bw()+
 scale_fill_manual(values=viridis_pal(option = "A")(7))+
   scale_y_continuous(labels = scales::percent)

tidy_FCA %>%
  inner_join(get_sentiments("afinn")) %>%
  group_by(date) %>%
  summarize(average_sentiment = mean(score), words = n()) %>%
  #filter(words >= 10) %>%
  ggplot(aes(date, average_sentiment)) +
  geom_line() +
  geom_hline(color = "red", lty = 2, yintercept = 0) +
labs(y = "Average AFINN sentiment score", x = "Year",
         title = "Sentiment analysis of FCA Glassdoor Reviews",
         subtitle = "Using the affin lexicon")+
  geom_smooth(method = "loess")

There average sentiment was positive at the first half of 2008. From the middle of 2008 to last quarter of 2009, there was a downward trend in the average sentiments expressed by reviewers. This can be attributed to the Global financial crisis which started around that time.The highest positive reviews occurred in october 2015 and April 2016 whereas the lowest negative sentiments were expressed in August 2016.

ldat=tidy_FCA %>%
  inner_join(get_sentiments("afinn")) %>%
  group_by(date) %>%
  summarize(average_sentiment = mean(score), words = n())
highchart() %>% 
    hc_title(text = "Sentiment analysis of FCA Glassdoor Reviews")%>%
  hc_add_series_times_values(ldat$date, ldat$average_sentiment, 
                             name = "Year",color="#440154FF")%>%
   hc_yAxis(title = list(text = "Average AFINN sentiment score"),labels = list(format = "{value}"), max = 4,min=-4,plotLines = list(
             list(label = list(text = ""),
                  color = "#35B779FF",
                  width = 2,
                  value = 0)))

highchart(type = "stock") %>% 
  hc_title(text = "Sentiment analysis of FCA Glassdoor Reviews") %>% 
  hc_subtitle(text = "") %>% 
  hc_tooltip(valueDecimals = 2) %>% 
  hc_add_series_times_values(ldat$date, ldat$average_sentiment,
                             name = "",color="#440154FF")%>% 
  hc_add_theme(hc_theme_gridlight())%>%
  hc_yAxis(title = list(text = "Average AFINN sentiment score"),labels = list(format = "{value}"), max = 4,min=-4,plotLines = list(
             list(label = list(text = ""),
                  color = "red",
                  width = 2,
                  value = 0)))
tidy_FCA %>%
  inner_join(get_sentiments("afinn")) %>%
  group_by(date) %>%
  summarize(average_sentiment = mean(score), words = n()) %>%
 # filter(words >= 5) %>%
  ggplot(aes(date, average_sentiment)) +
  
   geom_line( )+
    theme_minimal()+
    geom_ridgeline_gradient(aes(y=0,height=average_sentiment,fill=average_sentiment),min_height=-3.5)+
    scale_fill_viridis(option="C",limit=c(-3.5,4))+
  
  #geom_line() +
  geom_hline(color = "red", lty = 2, yintercept = 0) +
labs(y = "Average AFINN sentiment score", x = "Page",
         title = "Sentiment analysis of FCA Glassdoor Reviews",
         subtitle = "Using the affin lexicon")

FCA_sentiment %>%
    count(sentiment, word) %>%
    filter(sentiment %in% c("positive", "negative", 
                            "joy", "trust","fear","sadness")) %>%
    group_by(sentiment) %>%
    top_n(10) %>%
    ungroup %>%
    mutate(word = reorder(word, n)) %>%
   mutate(sentiment = as.factor(sentiment))  %>%
    ggplot(aes(word, n, fill = sentiment)) +
    geom_bar(alpha = 0.8, show.legend = FALSE,stat = "identity") +
    coord_flip() +
    scale_y_continuous(expand = c(0,0)) +
    facet_wrap(~sentiment, scales = "free") +
   labs(y = "Total number of occurrences", x = "",
         title = "Sentiment analysis of FCA Glassdoor Reviews",
         subtitle = "Using the nrc lexicon")+theme_bw()+
scale_fill_manual(values=viridis_pal(option = "D")(6))

 # # change text into italics
 #        theme(strip.text = element_text(face = "italic")) +
 #  # strip horizontal  axis labels
 #        theme(axis.title.x=element_blank()) +
 #        theme(axis.ticks.x=element_blank()) +
 #        theme(axis.text.x=element_blank())
   

Plot without viridis package

FCA_sentiment %>%
    count(date, date_total, sentiment) %>%
    filter(sentiment %in% c("positive", "negative", 
                            "joy", "trust","fear","sadness"))%>%
     mutate(sentiment = factor(sentiment, levels = c("negative",
                                                    "positive",
                                                    "joy", "trust","fear","sadness"))) %>%
    ggplot(aes(date, n / date_total, fill = sentiment)) +
    geom_area(position = "identity", alpha = 0.5) +
    labs(y = "Relative frequency", x = NULL,
         title = "Sentiment analysis of FCA Glassdoor Reviews",
         subtitle = "Using the nrc")+theme_bw()

Using bing Lexicon

FCA_sentiment <- tidy_FCA %>%
    inner_join(get_sentiments("bing"))
FCA_sentiment %>%
    count(date, date_total, sentiment)%>%
    mutate(sentiment = as.factor(sentiment))%>%
    ggplot(aes(date, n / date_total, fill = sentiment)) +
    geom_area(position = "identity", alpha = 0.5) +
    labs(y = "Relative frequency", x = "Page",
         title = "Sentiment analysis of FCA Glassdoor Reviews",
         subtitle = "Using the nrc")+theme_bw()+
# scale_fill_manual(values=viridis_pal(option = "plasma")(2))+
   scale_y_continuous(labels = scales::percent)

The negative and positive sentiments distribution is similar with the negative sentiments having a higher peak. The negative reviews is evenly distributed as like the positive reviews. Neither is clearly superior over the other.

 GlassdoorPages %>%
    unnest_tokens(word, text)%>%
  inner_join(get_sentiments("bing")) %>%
  count(word, sentiment, sort = TRUE)%>%
  spread(sentiment,n,fill=0)%>%
  mutate(sentiment = positive -negative)%>%
  ggplot(aes(x = sentiment)) +
geom_density(color = palette_light()[1], fill = palette_light()[1], alpha = 0.8) +
theme_tq()+xlim(c(-5,5))

den=GlassdoorPages %>%
    unnest_tokens(word, text)%>%
  inner_join(get_sentiments("bing")) %>%
  count(word, sentiment, sort = TRUE)%>%
  spread(sentiment,n,fill=0)%>%
  mutate(sentiment = positive -negative)
#hchart(den,density(den$sentiment), type = "area", color = "#B71C1C", name = "Density")
hchart(density(den$sentiment), type = "area", color =viridis_pal()(1), name = "Sentiment")%>%
  hc_xAxis(min = -5, max =5)%>%
  hc_yAxis(title = list(text = "density"),labels = list(format = "{value}"))

The most common positive and negative words are visualized below.

FCA_sentiment %>%
    count(sentiment, word) %>%
    group_by(sentiment) %>%
    top_n(15) %>%
    ungroup %>%
    mutate(word = reorder(word, n)) %>%
   mutate(sentiment = as.factor(sentiment))  %>%
    ggplot(aes(word, n, fill = sentiment)) +
    geom_col(alpha = 0.8, show.legend = FALSE) +
    coord_flip() +
    scale_y_continuous(expand = c(0,0)) +
    facet_wrap(~sentiment,scales="free") +
   labs(y = "Total number of occurrences", x = "",
         title = "Sentiment analysis of FCA Glassdoor Reviews",
         subtitle = "Using the bing lexicon")+
#scale_fill_manual(values=viridis_pal(option = "D")(8))+
 scale_fill_viridis(end = 0.75, discrete=TRUE, direction = -1) +
        scale_x_discrete(expand=c(0.02,0)) +
        theme(strip.text=element_text(hjust=0)) +
  # change text into italics
        theme(strip.text = element_text(face = "italic")) +
  # strip horizontal  axis labels
        theme(axis.title.x=element_blank()) +
        theme(axis.ticks.x=element_blank()) +
        theme(axis.text.x=element_blank())+
  theme_minimal(base_size = 13)

The count most common positive and negative sentiment is displayed graphicaly below.

bing_word_counts <-tidy_FCA %>%
  inner_join(get_sentiments("bing")) %>%
  count(word, sentiment, sort = TRUE) %>%
  ungroup()
bing_word_counts%>%spread(sentiment,n,fill = 0)%>%top_n(10)
bing_word_counts%>%spread(sentiment,n,fill = 0)%>%top_n(-10)%>%head(10)
bing_word_counts %>%
  filter(n > 3) %>%
  mutate(n = if_else(sentiment == "negative", -n, n)) %>%
  mutate(word = reorder(word, n)) %>%
  ggplot(aes(word, n, fill = sentiment)) +
  geom_bar(stat = "identity") +
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
  labs(y="Contribution to sentiment",title="bing sentiments")+
#scale_fill_manual(values=viridis_pal(option = "D")(2))
 scale_fill_viridis(end = 0.85, discrete=TRUE, direction = 1)

Alternative way of scrapping from several web pages

# n=4
# 
# FCA=list()
# 
# for (i in 2:n){
#   
# FCA[[1]]=read_html("https://www.glassdoor.com/Reviews/FCA-Fiat-Chrysler-Automobiles-Reviews-E149.htm")%>% html_nodes(".hreview")%>%html_text(trim = TRUE)
#   
# FCA[[i]]=read_html(paste("https://www.glassdoor.com/Reviews/FCA-Fiat-Chrysler-Automobiles-Reviews-E149_P",i,".htm",sep = ""))%>% html_nodes(".hreview")%>%html_text(trim = TRUE)
#   
# }
#FCA_html2<-FCA_html%>%str_replace_all("[[:xdigit:]]", "")
corpus = Corpus(VectorSource(FCA_html))
corpus = tm_map(corpus, tolower)
corpus<- tm_map(corpus, stripWhitespace)
corpus = tm_map(corpus, removeNumbers)
corpus = tm_map(corpus, removeWords, stopwords("english"))
corpus = tm_map(corpus, removeWords,stop_user )
tdm <- TermDocumentMatrix(corpus,
                          control = list(removePunctuation = TRUE, 
                                      stopwords =  TRUE, 
                                      removeNumbers = TRUE, tolower = TRUE,
                                      PlainTextDocument=TRUE,
                                      stripWhitespace=TRUE, stemming = TRUE))
inspect(tdm)
<<TermDocumentMatrix (terms: 2165, documents: 155)>>
Non-/sparse entries: 9569/326006
Sparsity           : 97%
Maximal term length: 38
Weighting          : term frequency (tf)
Sample             :
           Docs
Terms       113 115 116 118 145 27 28 33 43 84
  automobil   2   2   2   2   3  3  3  2  2  3
  chrysler    5   7   3   2   3  3  3  2  2  3
  copi        1   1   1   1   1  1  1  1  1  1
  employe     3   3   2   2   5  4  9  4  6  2
  fca         2   2   2   2   3  3  6  4  3  3
  fiat        3   2   2   2   5  3  3  2  2  3
  life        1   1   3   1   1  2  3  1  1  1
  respons     1   1   1   1   2  1  1  1  1  1
  second      1   1   1   1   1  2  1  1  1  1
  work        4   1   2   1   8  8  7  0  3  3
tidy(tdm)
tdm = as.matrix(tdm)
frequencies = DocumentTermMatrix(corpus)
frequencies
<<DocumentTermMatrix (documents: 155, terms: 2653)>>
Non-/sparse entries: 9847/401368
Sparsity           : 98%
Maximal term length: 38
Weighting          : term frequency (tf)
findFreqTerms(frequencies, lowfreq=100)
 [1] "automobiles"                     "chrysler"                       
 [3] "copied"                          "delete"                         
 [5] "emailcopy"                       "employee"                       
 [7] "facebookshare"                   "fca"                            
 [9] "fiat"                            "flag"                           
[11] "inappropriateflag"               "inappropriatehelpful"           
[13] "response"                        "seconds"                        
[15] "time"                            "twittershare"                   
[17] "whatsappshare"                   "work"                           
[19] "anonymous"                       "balanceculturevaluescareer"     
[21] "life"                            "opportunitiescompbenefitssenior"

Remove sparse terms

sparse = removeSparseTerms(frequencies, 0.995)
sparse
<<DocumentTermMatrix (documents: 155, terms: 2653)>>
Non-/sparse entries: 9847/401368
Sparsity           : 98%
Maximal term length: 38
Weighting          : term frequency (tf)

What about associations between words? Let’s have a look at what other words had a high association with “love”.

findAssocs(frequencies, c("love","poor","flexible","horrible"), c(0.6,0.6,0.6,0.6))
$love
                             dayton                            featured 
                               0.82                                0.82 
                        fieldconsno mechanicstarstarstarstarstarcurrent 
                               0.82                                0.82 
                                ohi                    retirementadvice 
                               0.82                                0.82 
                            staying                              taught 
                               0.82                                0.82 
                      yearsprospros 
                               0.82 

$poor
                         ctc                     continue                     advances 
                        0.75                         0.75                         0.71 
                     allowed                     cheapest                       choice 
                        0.71                         0.71                         0.71 
                        clue                     creation                      currect 
                        0.71                         0.71                         0.71 
                     decides                    developed                      drained 
                        0.71                         0.71                         0.71 
                      energy                       entire                       ethics 
                        0.71                         0.71                         0.71 
                    executed                       exited                    fantastic 
                        0.71                         0.71                         0.71 
                     feature                        final                     flounder 
                        0.71                         0.71                         0.71 
                     focused                        goals                        gouge 
                        0.71                         0.71                         0.71 
                        hair                      hammers                  highlighted 
                        0.71                         0.71                         0.71 
                        hurt                  incorporate                     interest 
                        0.71                         0.71                         0.71 
                       items                     location                       merger 
                        0.71                         0.71                         0.71 
                     minimal                      mirrors                  outstanding 
                        0.71                         0.71                         0.71 
                 participate                     partners                        price 
                        0.71                         0.71                         0.71 
                questionable                       reduce                    requested 
                        0.71                         0.71                         0.71 
                        roof                        shown                      sighted 
                        0.71                         0.71                         0.71 
                        sink                         site                      slogans 
                        0.71                         0.71                         0.71 
                       smoke                    stimulate swimstarstarstarstarstarwork 
                        0.71                         0.71                         0.71 
                      talked                          tco                    viability 
                        0.71                         0.71                         0.71 
                        view                     warranty                   washington 
                        0.71                         0.71                         0.71 
                       words                       recent                        since 
                        0.71                         0.63                         0.63 
                        seen 
                        0.60 

$flexible
numeric(0)

$horrible
                            bump                     consvacation 
                            0.70                             0.70 
                       designers designerstarstarstarstarstarwork 
                            0.70                             0.70 
                            earn                       impossible 
                            0.70                             0.70 
                         members                      possibility 
                            0.70                             0.70 
                 yearsproshealth                           affect 
                            0.70                             0.70 
                         affects                        anecdotal 
                            0.70                             0.70 
                      assertions                       astounding 
                            0.70                             0.70 
                           badge                             blow 
                            0.70                             0.70 
                         brought                          century 
                            0.70                             0.70 
                 ceoprosalthough                          channel 
                            0.70                             0.70 
                           chasm                    circumstances 
                            0.70                             0.70 
                          coduit                         colonels 
                            0.70                             0.70 
                       comradery                          concept 
                            0.70                             0.70 
                       countless                         disaster 
                            0.70                             0.70 
                       eliminate                         evidence 
                            0.70                             0.70 
                     exceptional                       exhaustive 
                            0.70                             0.70 
                           exist                            fatal 
                            0.70                             0.70 
                            fine                            giant 
                            0.70                             0.70 
                           grant                         granting 
                            0.70                             0.70 
                            guns                           habits 
                            0.70                             0.70 
                       intellect                           majors 
                            0.70                             0.70 
                  managementwhen                            maybe 
                            0.70                             0.70 
                       mechanism                           minded 
                            0.70                             0.70 
                           night                      nonetheless 
                            0.70                             0.70 
                         nowhere                      observation 
                            0.70                             0.70 
                         physics                       preventing 
                            0.70                             0.70 
                      procedures                            proof 
                            0.70                             0.70 
                          recipe                           regard 
                            0.70                             0.70 
                           roots                       separating 
                            0.70                             0.70 
                           sharp                         shooting 
                            0.70                             0.70 
                           soley                          special 
                            0.70                             0.70 
                      statements                          telling 
                            0.70                             0.70 
                         testing                             tops 
                            0.70                             0.70 
                        trenches                           trumps 
                            0.70                             0.70 
                            ugly                    unfortunately 
                            0.70                             0.70 
                             war                            weeds 
                            0.70                             0.70 
                           whole                          winning 
                            0.70                             0.70 
                           wiped                        wonderful 
                            0.70                             0.70 
                         however                              now 
                            0.63                             0.61 

High Frequency Words

The most commonly words used in the reviews is plotted below.

Word Cloud with Bing Lexicon

The most common positive and negative words are graphically depicted below.

c("edit","delet","via","starstarstarstarstarwork","pdt","hill","facebookshar")
[1] "edit"                     "delet"                    "via"                     
[4] "starstarstarstarstarwork" "pdt"                      "hill"                    
[7] "facebookshar"            
 GlassdoorPages %>%
    unnest_tokens(word, text)%>%
  inner_join(get_sentiments("bing")) %>%
  count(word, sentiment, sort = TRUE) %>%
 reshape2::acast(word ~ sentiment, value.var = "n", fill = 0) %>%
  comparison.cloud(colors = viridis_pal(option = "D")(2),
                   max.words = 100)

Word Cloud with nrc Lexicon

GlassdoorPages %>%
    unnest_tokens(word, text)%>%
  inner_join(get_sentiments("nrc")) %>%
  count(word, sentiment, sort = TRUE)%>%
  spread( word,n,fill = 0)%>%head(5)

Among some of the words commonly used by reviewers to express positive,negative,joy or sadness is displayed in the word cloud below.

GlassdoorPages %>%
    unnest_tokens(word, text)%>%
  inner_join(get_sentiments("nrc")) %>%
  count(word, sentiment, sort = TRUE)%>%
filter(sentiment %in% c("negative","positive","joy","sadness"))%>%
reshape2::acast(word ~ sentiment, value.var = "n", fill = 0)%>% 
comparison.cloud(colors = viridis_pal(option = "D")(4),
                  max.words = 200)

Network Graph Visualization

library(igraph)
library(ggraph)
tidy_descr_ngrams=GlassdoorPages %>%
unnest_tokens(word, text, token = "ngrams", n = 2) %>%
separate(word, c("word1", "word2"), sep = " ") %>%
filter(!word1 %in% stop_words$word) %>%
filter(!word2 %in% stop_words$word)%>%
filter(!word1 %in% stop_user) %>%
filter(!word2 %in% stop_user)%>%
 mutate(word1 = removeNumbers(word1))%>%
mutate(word2 = removeNumbers(word2))
bigram_counts=tidy_descr_ngrams %>%
count(word1, word2, sort = TRUE)
bigram_graph =bigram_counts %>%
filter(n > 10) %>%
graph_from_data_frame()
set.seed(1)
a=grid::arrow(type = "closed", length = unit(.15, "inches"))
ggraph(bigram_graph, layout = "fr") +
geom_edge_link(aes(edge_alpha = n), show.legend = FALSE,
arrow = a, end_cap = circle(.07, 'inches')) +
geom_node_point(color = palette_light()[1], size = 5, alpha = 0.8) +
geom_node_text(aes(label = name), vjust = 1, hjust = 0.5) +
theme_void()

What are the most commonly used words reviews?

The most common word is employee which suggest majority of the reviewers were either employees or ex-employees.The rest are related to management and the work environment.

data(stop_words)
tidy_descr<-GlassdoorPages %>%
unnest_tokens(word, text) %>% 
mutate(word=removeNumbers(word))%>% 
mutate(word_stem = wordStem(word)) %>%
anti_join(stop_words, by = "word") %>%
filter(!word_stem %in% stop_words$word) %>%
filter(!word_stem %in% stop_user) 
tidy_descr %>%
count(word_stem, sort = TRUE) %>%
filter(n > 30) %>%
ggplot(aes(x = reorder(word_stem, n), y = n)) +
geom_col(color = palette_light()[1], fill = palette_light()[1], alpha = 0.8) +
coord_flip() +
theme_tq() +
labs(x = "",
y = "count of most common words",titlt="Count of most common words")

tidy_descr %>%
count(word_stem) %>%
mutate(word_stem = removeNumbers(word_stem)) %>%
with(wordcloud(word_stem, n, max.words = 100, colors = palette_light()))

bigram_counts %>%
 mutate(word1 = removeNumbers(word1))%>%
mutate(word2 = removeNumbers(word2))%>% 
filter(n > 20) %>%
ggplot(aes(x = reorder(word1,-n), y = reorder(word2,-n), fill = n)) +
geom_tile(alpha = 0.8, color = "white") +
scale_fill_gradientn(colours = c(palette_light()[[1]], palette_light()[[2]])) +
coord_flip() +
theme_tq() +
theme(legend.position = "right") +
theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1)) +
labs(x = "first word in pair",
y = "second word in pair")

tidy_descr_ngrams=GlassdoorPages %>%
unnest_tokens(word, text, token = "ngrams", n = 2)%>%
separate(word, c("word1", "word2"), sep = " ")%>%
mutate(word1=removeNumbers(word1))%>% 
mutate(word1 = wordStem(word1))%>%
mutate(word2=removeNumbers(word2))%>% 
mutate(word2 = wordStem(word2))
tidy_descr_ngrams
 tidy_FCA %>%
    inner_join(get_sentiments("bing"))%>%
  group_by(sentiment)%>%count()%>%
ggplot(aes(x = reorder(sentiment, n), y = n,fill=palette_light()[1])) +
geom_col(  alpha = 0.8,width = 0.5) +
coord_flip() +
theme_tq()+
labs(y="sentiments ",title="bing lexicon sentiment count" ,x="frequency")+
  theme(legend.position="none")+
#scale_fill_viridis(end = 0.85, discrete=TRUE, direction = 1,option = "D") 
#scale_fill_manual(values=viridis_pal(option = "A")(2))
scale_fill_tq()

tidy_FCA %>%
    inner_join(get_sentiments("nrc"))%>%
  group_by(sentiment)%>%count()%>%
ggplot(aes(x = reorder(sentiment, n), y = n,fill=palette_light()[1])) +
geom_col(  alpha = 0.8) +
coord_flip() +
theme_tq()+
labs(y="sentiments ",title="nrc lexicon sentiment count" ,x="frequency")+
  theme(legend.position="none")+
#scale_fill_viridis(end = 0.85, discrete=TRUE, direction = 1,option = "D") 
#scale_fill_manual(values=viridis_pal(option = "A")(2))
scale_fill_tq()

Topic Modelling

Topic modeling is a method for unsupervised classification of documents, by modeling each document as a mixture of topics and each topic as a mixture of words. Latent Dirichlet allocation(LDA) is a particularly popular method for fitting a topic model.

We can investigate what are the top five topics in the reviews by topic modelling.

dtm_words_count<-tidy_descr %>%
mutate(word_stem = removeNumbers(word_stem)) %>%
count(date, word_stem, sort = TRUE) %>%
ungroup() %>%
filter(word_stem != "") %>%
# Casting a data frame to a DocumentTermMatrix, TermDocumentMatrix, or dfm  
cast_dtm(date, word_stem, n)
# set a seed so that the output of the model is predictable
dtm_lda<-LDA(dtm_words_count, k = 5, control = list(seed = 1234))
topics_beta<-tidy(dtm_lda, matrix = "beta")
p1<-topics_beta %>%
filter(grepl("[a‐z]+", term)) %>% # extract alphabets a-z
group_by(topic) %>%
top_n(10, beta) %>%
ungroup() %>%
arrange(topic, -beta) %>%
mutate(term = reorder(term, beta)) %>%
ggplot(aes(term, beta, color = factor(topic), fill = factor(topic))) +
geom_col(show.legend = FALSE, alpha = 0.8) +
scale_color_manual(values = palette_light()) +
scale_fill_manual(values = palette_light()) +
facet_wrap(~ topic, ncol = 5) +
coord_flip() +
theme_tq() +
labs(x = "",
y = "beta (~ occurrence in topics 1‐5)",
title = "The top 10 most characteristic words describe topic categories.")
user_topic<-tidy(dtm_lda, matrix = "gamma") %>%
arrange(desc(gamma)) %>%
group_by(document) %>%
top_n(1, gamma)
p2<-user_topic %>%
group_by(topic) %>%
top_n(10, gamma) %>%
ggplot(aes(x = reorder(document, -gamma), y = gamma, color = factor(topic))) +
facet_wrap(~ topic, scales = "free", ncol = 5) +
geom_point(show.legend = FALSE, size = 4, alpha = 0.8) +
scale_color_manual(values = palette_light()) +
scale_fill_manual(values = palette_light()) +
theme_tq() +
coord_flip() +
labs(x = "",
y = "gamma\n(~ affiliation with topics 1‐5)")
library(grid)
library(gridExtra)
#grid.arrange(p1, p2, ncol = 1, heights = c(0.7, 0.3))
p1

p2

The dataframe can be cast into a document-term matrix (one-term-per-document-per-row) with the cast_dtm function in tidytext.The LDA function accepts input of this format.

date_dtm<-GlassdoorPages %>%
mutate(year=year(date))%>%  
unnest_tokens(word, text) %>%
anti_join(stop_words)%>%
anti_join(stop_user2)  %>%
filter(!word %in% stop_user2) %>%
mutate(word=removeNumbers(word))%>% 
mutate(word = wordStem(word))%>%
filter(word != "") %>%
count(year, word, sort = TRUE) %>%
ungroup()%>%
cast_dtm(year, word, n)
 

We can now use topicmodels package to create a five topic LDA model.

date_lda <- LDA(date_dtm, k = 5, control = list(seed = 1234))
date_lda_td <- tidy(date_lda)
date_lda_td%>%head()

The β represent the probability that each term on a row belongs the topic on the row.

top_terms <- date_lda_td %>%
  group_by(topic) %>%
  top_n(10, beta) %>%
  ungroup() %>%
  filter(grepl("[a‐z]+", term)) %>% # extract alphabets a-z
  arrange(topic, -beta)
top_terms%>%head()
top_terms %>%mutate(topic=as.factor(topic))%>%
  mutate(term = reorder(term, beta)) %>%
  ggplot(aes(term, beta, fill = topic)) +
  geom_bar(stat = "identity",show.legend = FALSE, alpha = 0.8) +
  facet_wrap(~ topic, scales = "free") +
  theme(axis.text.x = element_text(size = 15, angle = 90, hjust = 1))+
  coord_flip()+
  scale_fill_viridis(end = 0.75, discrete=TRUE, direction = -1,option = "D")+
  labs(x = "",
y = "beta (occurrence in topics 1-5)",
title = "The top 10 most characteristic words describe topic categories.")

We treat each year beginning from 2008 as a separate document. Setting matrix = “gamma” returns a tidied version with one-document-per-topic-per-row. Now that we have these document classifications, we can see how well our unsupervised learning did at distinguishing the five topics. First we re-separate the document name into title and chapter:

date_lda_gamma <- tidy(date_lda, matrix = "gamma")%>%mutate(topic=factor(topic))
date_lda_gamma
ggplot(date_lda_gamma, aes(gamma, fill = topic)) +
  geom_histogram(bins = 30,binwidth=0.25) +
  facet_wrap(~ document, nrow = 2)+
  scale_fill_viridis(end = 0.75, discrete=TRUE, direction = -1)+theme_tq()

LS0tCnRpdGxlOiAiU2VudGltZW50IEFuYWx5c2lzIG9mIEZDQSBFbXBsb3llZSBSZXZpZXdzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKYXV0aG9yOiBOYW5hIEJvYXRlbmcKZGZfcHJpbnQ6IHBhZ2VkClRpbWU6ICdgciBTeXMudGltZSgpYCcKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJUIgJWQsICVZJylgIgotLS0KCgoKCmBgYHtyIHNldHVwLGluY2x1ZGU9RkFMU0V9Cgprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICBvdXQud2lkdGggPSIxMDAlIiwKICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICdkZWZhdWx0JywgCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgZmlnLmNhcCA9IkZpZy4gMzAiLCAKICAgICAgICAgICAgICAgICAgICAgIG91dC53aWR0aD0iMTAwJSIpCgpgYGAKCgoqKiBJbnRyb2R1Y3Rpb24gIFdlYiBTY3JhcGluZyAqKgpXZWIgc2NyYXBpbmcgaXMgYSB0ZWNobmlxdWUgZm9yIGNvbnZlcnRpbmcgdGhlIGRhdGEgcHJlc2VudCBpbiB1bnN0cnVjdHVyZWQgZm9ybWF0IChIVE1MIHRhZ3MpIG92ZXIgdGhlIHdlYiB0byB0aGUgc3RydWN0dXJlZCBmb3JtYXQgd2hpY2ggY2FuIGVhc2lseSBiZSBhY2Nlc3NlZCBhbmQgdXNlZC4KTW9zdCBvZiB0aGUgZGF0YSBhdmFpbGFibGUgb3ZlciB0aGUgd2ViIGlzIG5vdCByZWFkaWx5IGF2YWlsYWJsZS4gSXQgaXMgcHJlc2VudCBpbiBhbiB1bnN0cnVjdHVyZWQgZm9ybWF0IChIVE1MIGZvcm1hdCkgYW5kIGlzIG5vdCBkb3dubG9hZGFibGUuIFRoZXJlZm9yZSwgaXQgcmVxdWlyZXMga25vd2xlZGdlIGFuZCBleHBlcnRpc2UgdG8gdXNlIHRoaXMgZGF0YS4KCldlIGNhbiBsb2NhdGUgdXNlZnVsIGRhdGEgYmFzZWQgb24gdGhlaXIgQ1NTIHNlbGVjdG9ycywgZXNwZWNpYWxseSB3aGVuIHRoZSB3ZWJwYWdlIHVzZXMgc2VtYW50aWMgdGFnIGF0dHJpYnV0ZXMuIFdlIGNhbiB1c2UgW3NlbGVjdG9yZ2FkZ2V0XSAoaHR0cDovL3NlbGVjdG9yZ2FkZ2V0LmNvbS8pIHRvIGZpbmQgb3V0IHdoaWNoIGNzcyBzZWxlY3RvciBtYXRjaGVzIHRoZSAicmV2aWV3Ii4gU2VsZWN0R2FkZ2V0IGNhbiBiZSBhZGRlZCBhbiBleHRlbnNpb24gaW4gIEdvb2dsZSBjaHJvbWUuIEl0IGlzIHNob3duIGFzIGEgbWFnbmlmeWluZyBnbGFzcy4KCmBgYHtyfQpwYWNtYW46OnBfbG9hZCh0aWR5dmVyc2UsdGlkeXRleHQsdmlyaWRpcyxydmVzdCx0bSx3b3JkY2xvdWQsU25vd2JhbGxDLHRpZHlxdWFudCxnZ3JpZGdlcyxzY2FsZXMsaGlnaGNoYXJ0ZXIsdG9waWNtb2RlbHMpCmBgYAoKCgpXZSBjYW4gc3BlY2lmeSB0aGUgY3NzIHNlbGVjdG9yIGluIGh0bWxfbm9kZXMoKSBhbmQgZXh0cmFjdCB0aGUgdGV4dCB3aXRoIGh0bWxfdGV4dCgpLiBXZSBzY3JhYiBvdmVyIDE4MDAgcmV2aWV3cyBvZiBGaWF0IENocnlzbGVyIEF1dG9tb2JpbGVzIGZyb20gZ2xhc3Nkb29yLiBUaGVyZSBhcmUgYWJvdXQgMTU1IHdlYnBhZ2VzIHdoaWNoIGNvbnRhaW4gdGhlc2UgcmV2aWV3cy4KCgoKCgoKYGBge3J9Cm49MTU1CgojVGhlIHJldmlld3MgaGFzIDE1NSBwYWdlcyx0aHVzIG49MTU1CgoKCkZDQV91cmxzIDwtIHBhc3RlMCgiaHR0cHM6Ly93d3cuZ2xhc3Nkb29yLmNvbS9SZXZpZXdzL0ZDQS1GaWF0LUNocnlzbGVyLUF1dG9tb2JpbGVzLVJldmlld3MtRTE0OV9QIixzZXEoMiwgbiksICIuaHRtIikKRkNBX3VybHM8LWMoImh0dHBzOi8vd3d3LmdsYXNzZG9vci5jb20vUmV2aWV3cy9GQ0EtRmlhdC1DaHJ5c2xlci1BdXRvbW9iaWxlcy1SZXZpZXdzLUUxNDkuaHRtIixGQ0FfdXJscykKCgpGQ0FfaHRtbCA8LSBGQ0FfdXJscyAlPiUKICAgIG1hcF9jaHIofiByZWFkX2h0bWwoLikgJT4lIGh0bWxfbm9kZSgiLmhyZXZpZXciKSU+JWh0bWxfdGV4dCgpKQoKRkNBX2h0bWxbWzFdXQoKYGBgCgojIyMgRGF0YSBQcmVwYXJhdGlvbgpXZSBjYW4gcmVtb3ZlIGFsbCB1bndhbnRlZCBjaGFyYWN0ZXJzIGF0IHRoaXMgc3RhZ2UKCmBgYHtyfQojRGF0YS1QcmVwcm9jZXNzaW5nOiByZW1vdmluZyAnXG4nCkZDQV9odG1sPC1nc3ViKCJcbiIsIiIsRkNBX2h0bWwpCgoKI3JlbW92ZSBhbGwgcm91bmQgYnJhY2tldHMKRkNBX2h0bWw8LUZDQV9odG1sJT4lc3RyX3JlcGxhY2VfYWxsKCJcXCh8XFwpIiwgIiIpCgojcmVtb3ZlIGFsbCBcXApGQ0FfaHRtbDwtRkNBX2h0bWwlPiVzdHJfcmVwbGFjZV9hbGwoIlxcXFwiLCAiIikKCgojcmVtb3ZlIGFsbCBub24gd29yZHMgYW5kIG5vbiBudW1iZXJzCgojRkNBX2h0bWw8LUZDQV9odG1sJT4lc3RyX3JlcGxhY2VfYWxsKCJbXkEtWmEtejAtOV0iLCAiIikKCiNyZW1vdmUgYWxsIOKAoiAKRkNBX2h0bWw8LUZDQV9odG1sJT4lc3RyX3JlcGxhY2VfYWxsKCJcXOKAoiAgIiwgIiIpCgojcmVtb3ZlIGFsbCAmIApGQ0FfaHRtbDwtRkNBX2h0bWwlPiVzdHJfcmVwbGFjZV9hbGwoIlxcICYgIiwgIiIpCgojcmVtb3ZlIGFsbCAgbm9uIHByaW50YWJsZSB3b3JkcwpGQ0FfaHRtbDwtRkNBX2h0bWwlPiVzdHJfcmVwbGFjZV9hbGwoIlteWzpwcmludDpdXSIsICIiKQoKI3JlbW92ZSBhbGwgXApGQ0FfaHRtbDwtRkNBX2h0bWwlPiVzdHJfcmVwbGFjZV9hbGwocGF0dGVybiA9ICJcIiIsIHJlcGxhY2VtZW50ID0gIiIpCgojRkNBaW5kZWVkMjwtRkNBaW5kZWVkMiU+JXN0cmluZ2k6OnN0cmlfdW5lc2NhcGVfdW5pY29kZSgpCgoKIyByZW1vdmUgZGlnaXRzCiNGQ0FfaHRtbCU+JXN0cl9yZXBsYWNlX2FsbChwYXR0ZXJuID0gIltbOmRpZ2l0Ol1dKyIsIHJlcGxhY2VtZW50ID0gIiIpCiN0bTo6cmVtb3ZlTnVtYmVycyhGQ0FfaHRtbCkKCiMjIyMgcGF0dGVybiBmb3IgZGF0ZXMKcGF0dGVybiA9IlxcKD9cXGR7NH1cXCk/Wy4tXT8gKlxcZHsyfVsuLV0/ICpbLi1dP1xcZHsyfSIKCmRhdGU9RkNBX2h0bWwlPiVzdHJfZXh0cmFjdF9hbGwocGF0dGVybikKCiNGQ0FfaHRtbFtbMV1dJT4lc3RyX3N1YnNldChwYXR0ZXJuID0gIihbMC05XXsxLDJ9KVstIC5dKFthLXpBLVpdKylbLSAuXShbMC05XXs0fSkiKQoKI0ZDQV9odG1sW1sxXV0KCiN1bmxpc3QoRGF0ZSkKCkRhdGU9YXMuRGF0ZSh1bmxpc3QoZGF0ZSkpCgojRkNBX2h0bWxfMj1kYXRhX2ZyYW1lKERhdGU9YXMuRGF0ZSh1bmxpc3QoZGF0ZSkpLEZDQV9odG1sKQpgYGAKCgoKYGBge3J9CmdldF9zZW50aW1lbnRzKGxleGljb24gPSAibnJjIiklPiUKICAgIGNvdW50KHNlbnRpbWVudCwgc29ydCA9IFRSVUUpCmBgYAoKCkNvbnZlcnQgdGhlICB0ZXh0IGRhdGEgdG8gZGF0YWZyYW1lLgoKYGBge3J9CkdsYXNzZG9vclBhZ2VzIDwtIGRhdGFfZnJhbWUoZGF0ZT1hcy5EYXRlKHVubGlzdChkYXRlKSkscGFnZSA9IHNlcSgxLCBuKSwKICAgICAgICAgICAgICAgICAgICAgIHRleHQgPSBjKEZDQV9odG1sKSklPiVhcnJhbmdlKGRlc2MoZGF0ZSkpCgpHbGFzc2Rvb3JQYWdlcyU+JWhlYWQoNSkKR2xhc3Nkb29yUGFnZXMlPiV0YWlsKDUpCgoKYGBgCgoKCk5vdyB3ZSBoYXZlIHRoZSBsZXR0ZXJzLCBhbmQgY2FuIGNvbnZlcnQgdGhpcyB0byBhIHRpZHkgdGV4dCBmb3JtYXQuCgpgYGB7cn0KCgp0aWR5X0ZDQSA8LSBHbGFzc2Rvb3JQYWdlcyAlPiUKICAgIHVubmVzdF90b2tlbnMod29yZCwgdGV4dCkgJT4lCiAgICBhZGRfY291bnQoZGF0ZSkgJT4lCiAgICBkcGx5cjo6cmVuYW1lKGRhdGVfdG90YWwgPSBuKQoKCgojcmVtb3ZlIHN0b3Agd29yZHMKCmRhdGEoInN0b3Bfd29yZHMiKQp0aWR5X0ZDQSA8LSB0aWR5X0ZDQSAlPiUKICBhbnRpX2pvaW4oc3RvcF93b3JkcykKCgoKCmBgYAoKUmVtb3ZlIHNvbWUgb3RoZXIgdXNlciBkZWZpbmVkIHN0b3Agd29yZHMuCmBgYHtyfQoKCnN0b3BfdXNlcj1jKCJsaW5rbGluayIsIndoYXRzYXBwc2hhciIsImF1YnVybiIsInR3aXR0ZXJzaGFyIiwiZWRpdCIsImRlbGV0IiwidmlhIiwiZWRpdCIsImRlbGV0IiwidmlhIiwic3RhcnN0YXJzdGFyc3RhcnN0YXJ3b3JrIiwicGR0IiwiaGlsbCIsImFnbyIsCiAgICAgICAgICAgICJmYWNlYm9va3NoYXIiKQoKc3RvcF91c2VyMj1kYXRhX2ZyYW1lKHdvcmQ9c3RvcF91c2VyKQoKdGlkeV9GQ0EgPC0gdGlkeV9GQ0EgJT4lCiAgYW50aV9qb2luKHN0b3BfdXNlcjIpCgoKdGlkeV9GQ0ElPiVoZWFkKCkKCmBgYAoKCgoKCk5leHQsIGxldOKAmXMgaW1wbGVtZW50IHRoZSBzZW50aW1lbnQgYW5hbHlzaXMuCgoKYGBge3J9CkZDQV9zZW50aW1lbnQgPC0gdGlkeV9GQ0EgJT4lCiAgICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJucmMiKSkKCkZDQV9zZW50aW1lbnQlPiVoZWFkKCkKYGBgCgpOb3cgd2UgaGF2ZSBhbGwgd2UgbmVlZCB0byBzZWUgdGhlIHJlbGF0aXZlIGNoYW5nZXMgaW4gdGhlc2Ugc2VudGltZW50cyBvdmVyIHRoZSB5ZWFycy4KCmBgYHtyfQoKdGhlbWVfc2V0KHRoZW1lX2J3KCkpCgojQWx0ZXJuYXRpdmVseQojRkNBX3NlbnRpbWVudCU+JWdyb3VwX2J5KHBhZ2UsIHBhZ2VfdG90YWwsIHNlbnRpbWVudCklPiVjb3VudCgpCgpGQ0Ffc2VudGltZW50ICU+JQogICAgY291bnQoZGF0ZSwgZGF0ZV90b3RhbCwgc2VudGltZW50KSAlPiUKICAgIGZpbHRlcihzZW50aW1lbnQgJWluJSBjKCJwb3NpdGl2ZSIsICJuZWdhdGl2ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgImpveSIsICJ0cnVzdCIsImZlYXIiLCJzYWRuZXNzIikpJT4lCiAgICBtdXRhdGUoc2VudGltZW50ID0gYXMuZmFjdG9yKHNlbnRpbWVudCkpICU+JQogICAgI2dncGxvdChhZXMocGFnZSwgbiAvIHBhZ2VfdG90YWwsIGZpbGwgPSBzZW50aW1lbnQpKSArCiAgICAgZ2dwbG90KGFlcyhkYXRlLCBuIC8gc3VtKG4pLCBmaWxsID0gc2VudGltZW50KSkgKwogICAgZ2VvbV9hcmVhKHBvc2l0aW9uID0gImlkZW50aXR5IiwgYWxwaGEgPSAwLjUpICsKICAgIGxhYnMoeSA9ICJSZWxhdGl2ZSBmcmVxdWVuY3kiLCB4ID0gIlllYXIiLAogICAgICAgICB0aXRsZSA9ICJTZW50aW1lbnQgYW5hbHlzaXMgb2YgRkNBIEdsYXNzZG9vciBSZXZpZXdzIiwKICAgICAgICAgc3VidGl0bGUgPSAiVXNpbmcgdGhlIG5yYyBsZXhpY29uIikrdGhlbWVfYncoKSsKIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz12aXJpZGlzX3BhbChvcHRpb24gPSAiRCIpKDYpKSsKICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkKCgoKYGBgCgoKCgoKCmBgYHtyfQoKRkNBX3NlbnRpbWVudCAlPiUKICAgIGNvdW50KGRhdGUsIGRhdGVfdG90YWwsIHNlbnRpbWVudCkgJT4lCiAgICBmaWx0ZXIoc2VudGltZW50ICVpbiUgYygicG9zaXRpdmUiLCAibmVnYXRpdmUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJqb3kiLCAidHJ1c3QiLCJmZWFyIiwic2FkbmVzcyIpKSU+JQogICAgbXV0YXRlKHNlbnRpbWVudCA9IGFzLmZhY3RvcihzZW50aW1lbnQpKSAlPiUKICAgICNnZ3Bsb3QoYWVzKHBhZ2UsIG4gLyBwYWdlX3RvdGFsLCBmaWxsID0gc2VudGltZW50KSkgKwogICAgIGdncGxvdChhZXMoeD1kYXRlLHk9IG4gLyBzdW0obiksIGZpbGwgPSBzZW50aW1lbnQsaGVpZ2h0PW4gLyBzdW0obiksZ3JvdXA9c2VudGltZW50KSkgKwogICBnZW9tX3JpZGdlbGluZV9ncmFkaWVudCgpICsKICAgIGxhYnMoeSA9ICJSZWxhdGl2ZSBmcmVxdWVuY3kiLCB4ID0gIlllYXIiLAogICAgICAgICB0aXRsZSA9ICJTZW50aW1lbnQgYW5hbHlzaXMgb2YgRkNBIEdsYXNzZG9vciBSZXZpZXdzIiwKICAgICAgICAgc3VidGl0bGUgPSAiVXNpbmcgdGhlIG5yYyBsZXhpY29uIikrdGhlbWVfYncoKSsKIHNjYWxlX2ZpbGxfdmlyaWRpcyhkaXNjcmV0ZSA9IFRSVUUsIGRpcmVjdGlvbiA9IC0xKSArCiAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQpCiNleHBhbmQgeCBhbmQgeSBsaW1pdHMgIAojZXhwYW5kX2xpbWl0cyh4PWMoMCwxNjApLCB5PWMoMCwgMC4wMDA1KSkKCmBgYApBdCB0aGUgYmVnaW5uaW5nIDIwMDggdG8gIHRoZSBlbmQgb2YgMjAxMyx0aGUgcG9zaXRpdmUgc2VudGltZW50cyBvdXR3ZWlnaCB0aGUgbmVnYXRpdmUgc2VudGltZW50cy4gVGhlIGxldmVsIG9mIHRydXN0IGluIEZDQSB3YXMgYWxzbyBoaWdoZXIgY29tcGFyZWQgdG8gc2FkIHNlbnRpbWVudHMgZXhwcmVzc2VkIHRoZSByZXZpZXdlcnMuIEZyb20gMjAxNCwgdGhlIHBlcmNlbnRhZ2Ugb2YgcG9zaXRpdmUvdHJ1c3Qgc2VudGltZW50cyBkb2VzIG5vdCBvdmVyd2hlbWx5IGRvcm1pbmF0ZSB0aGUgbmVnYXRpdmUvc2FkbmVzcyBzZW50aW1lbnRzLiBJbiBnZW5lcmFsLCB0aGUgcG9zaXRpdmUgc2VudGltZW50cyBtYXJrZWQgYSBkZWNsaW5lIGZyb20gdGhlIDIwMTQgb253YXJkcy4gCgoKYGBge3J9CkZDQV9zZW50aW1lbnQgJT4lCiAgICBjb3VudChkYXRlLCBkYXRlX3RvdGFsLCBzZW50aW1lbnQpICU+JQogICMgIGZpbHRlcihzZW50aW1lbnQgJWluJSBjKCJwb3NpdGl2ZSIsICJuZWdhdGl2ZSIsICAiam95IiwgInRydXN0IiwiZmVhciIsInNhZG5lc3MiKSklPiUKICBtdXRhdGUoc2VudGltZW50ID0gZm9yY2F0czo6ZmN0X2x1bXAoc2VudGltZW50LCA2KSklPiUKICAgICNtdXRhdGUoc2VudGltZW50ID0gYXMuZmFjdG9yKHNlbnRpbWVudCkpICU+JQogICAgZ2dwbG90KGFlcyhkYXRlLCBuIC8gZGF0ZV90b3RhbCwgZmlsbCA9IHNlbnRpbWVudCkpICsKICAgICAjZ2dwbG90KGFlcyhwYWdlLCBuIC8gc3VtKG4pLCBmaWxsID0gc2VudGltZW50KSkgKwogICAgZ2VvbV9hcmVhKHBvc2l0aW9uID0gImlkZW50aXR5IiwgYWxwaGEgPSAwLjUpICsKICAgIGxhYnMoeSA9ICJSZWxhdGl2ZSBmcmVxdWVuY3kiLCB4ID0gIlllYXIiLAogICAgICAgICB0aXRsZSA9ICJTZW50aW1lbnQgYW5hbHlzaXMgb2YgRkNBIEdsYXNzZG9vciBSZXZpZXdzIiwKICAgICAgICAgc3VidGl0bGUgPSAiVXNpbmcgdGhlIG5yYyBsZXhpY29uIikrdGhlbWVfYncoKSsKIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz12aXJpZGlzX3BhbChvcHRpb24gPSAiQSIpKDcpKSsKICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkKCgpgYGAKCgoKCmBgYHtyfQp0aWR5X0ZDQSAlPiUKICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJhZmlubiIpKSAlPiUKICBncm91cF9ieShkYXRlKSAlPiUKICBzdW1tYXJpemUoYXZlcmFnZV9zZW50aW1lbnQgPSBtZWFuKHNjb3JlKSwgd29yZHMgPSBuKCkpICU+JQogICNmaWx0ZXIod29yZHMgPj0gMTApICU+JQogIGdncGxvdChhZXMoZGF0ZSwgYXZlcmFnZV9zZW50aW1lbnQpKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21faGxpbmUoY29sb3IgPSAicmVkIiwgbHR5ID0gMiwgeWludGVyY2VwdCA9IDApICsKbGFicyh5ID0gIkF2ZXJhZ2UgQUZJTk4gc2VudGltZW50IHNjb3JlIiwgeCA9ICJZZWFyIiwKICAgICAgICAgdGl0bGUgPSAiU2VudGltZW50IGFuYWx5c2lzIG9mIEZDQSBHbGFzc2Rvb3IgUmV2aWV3cyIsCiAgICAgICAgIHN1YnRpdGxlID0gIlVzaW5nIHRoZSBhZmZpbiBsZXhpY29uIikrCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIikKCmBgYApUaGVyZSBhdmVyYWdlIHNlbnRpbWVudCB3YXMgcG9zaXRpdmUgYXQgdGhlIGZpcnN0IGhhbGYgb2YgMjAwOC4gRnJvbSB0aGUgbWlkZGxlIG9mIDIwMDggdG8gbGFzdCBxdWFydGVyIG9mIDIwMDksIHRoZXJlIHdhcyBhIGRvd253YXJkIHRyZW5kIGluIHRoZSBhdmVyYWdlIHNlbnRpbWVudHMgZXhwcmVzc2VkIGJ5IHJldmlld2Vycy4gVGhpcyBjYW4gYmUgYXR0cmlidXRlZCB0byB0aGUgR2xvYmFsIGZpbmFuY2lhbCBjcmlzaXMgd2hpY2ggc3RhcnRlZCBhcm91bmQgdGhhdCB0aW1lLlRoZSBoaWdoZXN0IHBvc2l0aXZlIHJldmlld3Mgb2NjdXJyZWQgaW4gb2N0b2JlciAgMjAxNSBhbmQgQXByaWwgIDIwMTYgd2hlcmVhcyB0aGUgbG93ZXN0IG5lZ2F0aXZlIHNlbnRpbWVudHMgd2VyZSBleHByZXNzZWQgaW4gQXVndXN0IDIwMTYuCgpgYGB7cn0KbGRhdD10aWR5X0ZDQSAlPiUKICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJhZmlubiIpKSAlPiUKICBncm91cF9ieShkYXRlKSAlPiUKICBzdW1tYXJpemUoYXZlcmFnZV9zZW50aW1lbnQgPSBtZWFuKHNjb3JlKSwgd29yZHMgPSBuKCkpCgpoaWdoY2hhcnQoKSAlPiUgCiAgICBoY190aXRsZSh0ZXh0ID0gIlNlbnRpbWVudCBhbmFseXNpcyBvZiBGQ0EgR2xhc3Nkb29yIFJldmlld3MiKSU+JQogIGhjX2FkZF9zZXJpZXNfdGltZXNfdmFsdWVzKGxkYXQkZGF0ZSwgbGRhdCRhdmVyYWdlX3NlbnRpbWVudCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJZZWFyIixjb2xvcj0iIzQ0MDE1NEZGIiklPiUKICAgaGNfeUF4aXModGl0bGUgPSBsaXN0KHRleHQgPSAiQXZlcmFnZSBBRklOTiBzZW50aW1lbnQgc2NvcmUiKSxsYWJlbHMgPSBsaXN0KGZvcm1hdCA9ICJ7dmFsdWV9IiksIG1heCA9IDQsbWluPS00LHBsb3RMaW5lcyA9IGxpc3QoCiAgICAgICAgICAgICBsaXN0KGxhYmVsID0gbGlzdCh0ZXh0ID0gIiIpLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICIjMzVCNzc5RkYiLAogICAgICAgICAgICAgICAgICB3aWR0aCA9IDIsCiAgICAgICAgICAgICAgICAgIHZhbHVlID0gMCkpKQoKCgoKCgoKaGlnaGNoYXJ0KHR5cGUgPSAic3RvY2siKSAlPiUgCiAgaGNfdGl0bGUodGV4dCA9ICJTZW50aW1lbnQgYW5hbHlzaXMgb2YgRkNBIEdsYXNzZG9vciBSZXZpZXdzIikgJT4lIAogIGhjX3N1YnRpdGxlKHRleHQgPSAiIikgJT4lIAogIGhjX3Rvb2x0aXAodmFsdWVEZWNpbWFscyA9IDIpICU+JSAKICBoY19hZGRfc2VyaWVzX3RpbWVzX3ZhbHVlcyhsZGF0JGRhdGUsIGxkYXQkYXZlcmFnZV9zZW50aW1lbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICIiLGNvbG9yPSIjNDQwMTU0RkYiKSU+JSAKICBoY19hZGRfdGhlbWUoaGNfdGhlbWVfZ3JpZGxpZ2h0KCkpJT4lCiAgaGNfeUF4aXModGl0bGUgPSBsaXN0KHRleHQgPSAiQXZlcmFnZSBBRklOTiBzZW50aW1lbnQgc2NvcmUiKSxsYWJlbHMgPSBsaXN0KGZvcm1hdCA9ICJ7dmFsdWV9IiksIG1heCA9IDQsbWluPS00LHBsb3RMaW5lcyA9IGxpc3QoCiAgICAgICAgICAgICBsaXN0KGxhYmVsID0gbGlzdCh0ZXh0ID0gIiIpLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLAogICAgICAgICAgICAgICAgICB3aWR0aCA9IDIsCiAgICAgICAgICAgICAgICAgIHZhbHVlID0gMCkpKQpgYGAKCgoKCgoKYGBge3J9CnRpZHlfRkNBICU+JQogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImFmaW5uIikpICU+JQogIGdyb3VwX2J5KGRhdGUpICU+JQogIHN1bW1hcml6ZShhdmVyYWdlX3NlbnRpbWVudCA9IG1lYW4oc2NvcmUpLCB3b3JkcyA9IG4oKSkgJT4lCiAjIGZpbHRlcih3b3JkcyA+PSA1KSAlPiUKICBnZ3Bsb3QoYWVzKGRhdGUsIGF2ZXJhZ2Vfc2VudGltZW50KSkgKwogIAogICBnZW9tX2xpbmUoICkrCiAgICB0aGVtZV9taW5pbWFsKCkrCiAgICBnZW9tX3JpZGdlbGluZV9ncmFkaWVudChhZXMoeT0wLGhlaWdodD1hdmVyYWdlX3NlbnRpbWVudCxmaWxsPWF2ZXJhZ2Vfc2VudGltZW50KSxtaW5faGVpZ2h0PS0zLjUpKwogICAgc2NhbGVfZmlsbF92aXJpZGlzKG9wdGlvbj0iQyIsbGltaXQ9YygtMy41LDQpKSsKICAKICAjZ2VvbV9saW5lKCkgKwogIGdlb21faGxpbmUoY29sb3IgPSAicmVkIiwgbHR5ID0gMiwgeWludGVyY2VwdCA9IDApICsKbGFicyh5ID0gIkF2ZXJhZ2UgQUZJTk4gc2VudGltZW50IHNjb3JlIiwgeCA9ICJQYWdlIiwKICAgICAgICAgdGl0bGUgPSAiU2VudGltZW50IGFuYWx5c2lzIG9mIEZDQSBHbGFzc2Rvb3IgUmV2aWV3cyIsCiAgICAgICAgIHN1YnRpdGxlID0gIlVzaW5nIHRoZSBhZmZpbiBsZXhpY29uIikKYGBgCgoKCgoKYGBge3J9CkZDQV9zZW50aW1lbnQgJT4lCiAgICBjb3VudChzZW50aW1lbnQsIHdvcmQpICU+JQogICAgZmlsdGVyKHNlbnRpbWVudCAlaW4lIGMoInBvc2l0aXZlIiwgIm5lZ2F0aXZlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiam95IiwgInRydXN0IiwiZmVhciIsInNhZG5lc3MiKSkgJT4lCiAgICBncm91cF9ieShzZW50aW1lbnQpICU+JQogICAgdG9wX24oMTApICU+JQogICAgdW5ncm91cCAlPiUKICAgIG11dGF0ZSh3b3JkID0gcmVvcmRlcih3b3JkLCBuKSkgJT4lCiAgIG11dGF0ZShzZW50aW1lbnQgPSBhcy5mYWN0b3Ioc2VudGltZW50KSkgICU+JQogICAgZ2dwbG90KGFlcyh3b3JkLCBuLCBmaWxsID0gc2VudGltZW50KSkgKwogICAgZ2VvbV9iYXIoYWxwaGEgPSAwLjgsIHNob3cubGVnZW5kID0gRkFMU0Usc3RhdCA9ICJpZGVudGl0eSIpICsKICAgIGNvb3JkX2ZsaXAoKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLDApKSArCiAgICBmYWNldF93cmFwKH5zZW50aW1lbnQsIHNjYWxlcyA9ICJmcmVlIikgKwogICBsYWJzKHkgPSAiVG90YWwgbnVtYmVyIG9mIG9jY3VycmVuY2VzIiwgeCA9ICIiLAogICAgICAgICB0aXRsZSA9ICJTZW50aW1lbnQgYW5hbHlzaXMgb2YgRkNBIEdsYXNzZG9vciBSZXZpZXdzIiwKICAgICAgICAgc3VidGl0bGUgPSAiVXNpbmcgdGhlIG5yYyBsZXhpY29uIikrdGhlbWVfYncoKSsKc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPXZpcmlkaXNfcGFsKG9wdGlvbiA9ICJEIikoNikpCgogIyAjIGNoYW5nZSB0ZXh0IGludG8gaXRhbGljcwogIyAgICAgICAgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChmYWNlID0gIml0YWxpYyIpKSArCiAjICAjIHN0cmlwIGhvcml6b250YWwgIGF4aXMgbGFiZWxzCiAjICAgICAgICB0aGVtZShheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpKSArCiAjICAgICAgICB0aGVtZShheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpKSArCiAjICAgICAgICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCkpCiAgIApgYGAKCgoKIyMjIFBsb3Qgd2l0aG91dCB2aXJpZGlzIHBhY2thZ2UKCmBgYHtyfQoKCgoKRkNBX3NlbnRpbWVudCAlPiUKICAgIGNvdW50KGRhdGUsIGRhdGVfdG90YWwsIHNlbnRpbWVudCkgJT4lCiAgICBmaWx0ZXIoc2VudGltZW50ICVpbiUgYygicG9zaXRpdmUiLCAibmVnYXRpdmUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJqb3kiLCAidHJ1c3QiLCJmZWFyIiwic2FkbmVzcyIpKSU+JQogICAgIG11dGF0ZShzZW50aW1lbnQgPSBmYWN0b3Ioc2VudGltZW50LCBsZXZlbHMgPSBjKCJuZWdhdGl2ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicG9zaXRpdmUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImpveSIsICJ0cnVzdCIsImZlYXIiLCJzYWRuZXNzIikpKSAlPiUKICAgIGdncGxvdChhZXMoZGF0ZSwgbiAvIGRhdGVfdG90YWwsIGZpbGwgPSBzZW50aW1lbnQpKSArCiAgICBnZW9tX2FyZWEocG9zaXRpb24gPSAiaWRlbnRpdHkiLCBhbHBoYSA9IDAuNSkgKwogICAgbGFicyh5ID0gIlJlbGF0aXZlIGZyZXF1ZW5jeSIsIHggPSBOVUxMLAogICAgICAgICB0aXRsZSA9ICJTZW50aW1lbnQgYW5hbHlzaXMgb2YgRkNBIEdsYXNzZG9vciBSZXZpZXdzIiwKICAgICAgICAgc3VidGl0bGUgPSAiVXNpbmcgdGhlIG5yYyIpK3RoZW1lX2J3KCkKCmBgYAoKIyMjIyBVc2luZyBiaW5nIExleGljb24KCmBgYHtyfQpGQ0Ffc2VudGltZW50IDwtIHRpZHlfRkNBICU+JQogICAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygiYmluZyIpKQoKCkZDQV9zZW50aW1lbnQgJT4lCiAgICBjb3VudChkYXRlLCBkYXRlX3RvdGFsLCBzZW50aW1lbnQpJT4lCiAgICBtdXRhdGUoc2VudGltZW50ID0gYXMuZmFjdG9yKHNlbnRpbWVudCkpJT4lCiAgICBnZ3Bsb3QoYWVzKGRhdGUsIG4gLyBkYXRlX3RvdGFsLCBmaWxsID0gc2VudGltZW50KSkgKwogICAgZ2VvbV9hcmVhKHBvc2l0aW9uID0gImlkZW50aXR5IiwgYWxwaGEgPSAwLjUpICsKICAgIGxhYnMoeSA9ICJSZWxhdGl2ZSBmcmVxdWVuY3kiLCB4ID0gIlBhZ2UiLAogICAgICAgICB0aXRsZSA9ICJTZW50aW1lbnQgYW5hbHlzaXMgb2YgRkNBIEdsYXNzZG9vciBSZXZpZXdzIiwKICAgICAgICAgc3VidGl0bGUgPSAiVXNpbmcgdGhlIG5yYyIpK3RoZW1lX2J3KCkrCiMgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPXZpcmlkaXNfcGFsKG9wdGlvbiA9ICJwbGFzbWEiKSgyKSkrCiAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQpCmBgYAoKCgoKVGhlIG5lZ2F0aXZlIGFuZCBwb3NpdGl2ZSBzZW50aW1lbnRzIGRpc3RyaWJ1dGlvbiBpcyBzaW1pbGFyIHdpdGggdGhlIG5lZ2F0aXZlIHNlbnRpbWVudHMgaGF2aW5nIGEgaGlnaGVyIHBlYWsuIFRoZSBuZWdhdGl2ZSByZXZpZXdzIGlzIGV2ZW5seSBkaXN0cmlidXRlZCBhcyBsaWtlIHRoZSBwb3NpdGl2ZSByZXZpZXdzLgpOZWl0aGVyICBpcyBjbGVhcmx5ICBzdXBlcmlvciBvdmVyIHRoZSBvdGhlci4KCmBgYHtyfQogR2xhc3Nkb29yUGFnZXMgJT4lCiAgICB1bm5lc3RfdG9rZW5zKHdvcmQsIHRleHQpJT4lCiAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygiYmluZyIpKSAlPiUKICBjb3VudCh3b3JkLCBzZW50aW1lbnQsIHNvcnQgPSBUUlVFKSU+JQogIHNwcmVhZChzZW50aW1lbnQsbixmaWxsPTApJT4lCiAgbXV0YXRlKHNlbnRpbWVudCA9IHBvc2l0aXZlIC1uZWdhdGl2ZSklPiUKICBnZ3Bsb3QoYWVzKHggPSBzZW50aW1lbnQpKSArCmdlb21fZGVuc2l0eShjb2xvciA9IHBhbGV0dGVfbGlnaHQoKVsxXSwgZmlsbCA9IHBhbGV0dGVfbGlnaHQoKVsxXSwgYWxwaGEgPSAwLjgpICsKdGhlbWVfdHEoKSt4bGltKGMoLTUsNSkpCmBgYAoKCgpgYGB7cn0KZGVuPUdsYXNzZG9vclBhZ2VzICU+JQogICAgdW5uZXN0X3Rva2Vucyh3b3JkLCB0ZXh0KSU+JQogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImJpbmciKSkgJT4lCiAgY291bnQod29yZCwgc2VudGltZW50LCBzb3J0ID0gVFJVRSklPiUKICBzcHJlYWQoc2VudGltZW50LG4sZmlsbD0wKSU+JQogIG11dGF0ZShzZW50aW1lbnQgPSBwb3NpdGl2ZSAtbmVnYXRpdmUpCiNoY2hhcnQoZGVuLGRlbnNpdHkoZGVuJHNlbnRpbWVudCksIHR5cGUgPSAiYXJlYSIsIGNvbG9yID0gIiNCNzFDMUMiLCBuYW1lID0gIkRlbnNpdHkiKQoKaGNoYXJ0KGRlbnNpdHkoZGVuJHNlbnRpbWVudCksIHR5cGUgPSAiYXJlYSIsIGNvbG9yID12aXJpZGlzX3BhbCgpKDEpLCBuYW1lID0gIlNlbnRpbWVudCIpJT4lCiAgaGNfeEF4aXMobWluID0gLTUsIG1heCA9NSklPiUKICBoY195QXhpcyh0aXRsZSA9IGxpc3QodGV4dCA9ICJkZW5zaXR5IiksbGFiZWxzID0gbGlzdChmb3JtYXQgPSAie3ZhbHVlfSIpKQpgYGAKCgoKVGhlIG1vc3QgY29tbW9uIHBvc2l0aXZlIGFuZCBuZWdhdGl2ZSB3b3JkcyBhcmUgdmlzdWFsaXplZCBiZWxvdy4KCmBgYHtyfQpGQ0Ffc2VudGltZW50ICU+JQogICAgY291bnQoc2VudGltZW50LCB3b3JkKSAlPiUKICAgIGdyb3VwX2J5KHNlbnRpbWVudCkgJT4lCiAgICB0b3BfbigxNSkgJT4lCiAgICB1bmdyb3VwICU+JQogICAgbXV0YXRlKHdvcmQgPSByZW9yZGVyKHdvcmQsIG4pKSAlPiUKICAgbXV0YXRlKHNlbnRpbWVudCA9IGFzLmZhY3RvcihzZW50aW1lbnQpKSAgJT4lCiAgICBnZ3Bsb3QoYWVzKHdvcmQsIG4sIGZpbGwgPSBzZW50aW1lbnQpKSArCiAgICBnZW9tX2NvbChhbHBoYSA9IDAuOCwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogICAgY29vcmRfZmxpcCgpICsKICAgIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsMCkpICsKICAgIGZhY2V0X3dyYXAofnNlbnRpbWVudCxzY2FsZXM9ImZyZWUiKSArCiAgIGxhYnMoeSA9ICJUb3RhbCBudW1iZXIgb2Ygb2NjdXJyZW5jZXMiLCB4ID0gIiIsCiAgICAgICAgIHRpdGxlID0gIlNlbnRpbWVudCBhbmFseXNpcyBvZiBGQ0EgR2xhc3Nkb29yIFJldmlld3MiLAogICAgICAgICBzdWJ0aXRsZSA9ICJVc2luZyB0aGUgYmluZyBsZXhpY29uIikrCiNzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9dmlyaWRpc19wYWwob3B0aW9uID0gIkQiKSg4KSkrCiBzY2FsZV9maWxsX3ZpcmlkaXMoZW5kID0gMC43NSwgZGlzY3JldGU9VFJVRSwgZGlyZWN0aW9uID0gLTEpICsKICAgICAgICBzY2FsZV94X2Rpc2NyZXRlKGV4cGFuZD1jKDAuMDIsMCkpICsKICAgICAgICB0aGVtZShzdHJpcC50ZXh0PWVsZW1lbnRfdGV4dChoanVzdD0wKSkgKwogICMgY2hhbmdlIHRleHQgaW50byBpdGFsaWNzCiAgICAgICAgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChmYWNlID0gIml0YWxpYyIpKSArCiAgIyBzdHJpcCBob3Jpem9udGFsICBheGlzIGxhYmVscwogICAgICAgIHRoZW1lKGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCkpICsKICAgICAgICB0aGVtZShheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpKSArCiAgICAgICAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpKSsKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDEzKQpgYGAKCgoKCgoKVGhlIGNvdW50IG1vc3QgY29tbW9uIHBvc2l0aXZlIGFuZCBuZWdhdGl2ZSBzZW50aW1lbnQgIGlzIGRpc3BsYXllZCBncmFwaGljYWx5IGJlbG93LgoKYGBge3J9CmJpbmdfd29yZF9jb3VudHMgPC10aWR5X0ZDQSAlPiUKICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJiaW5nIikpICU+JQogIGNvdW50KHdvcmQsIHNlbnRpbWVudCwgc29ydCA9IFRSVUUpICU+JQogIHVuZ3JvdXAoKQoKYmluZ193b3JkX2NvdW50cyU+JXNwcmVhZChzZW50aW1lbnQsbixmaWxsID0gMCklPiV0b3BfbigxMCkKYmluZ193b3JkX2NvdW50cyU+JXNwcmVhZChzZW50aW1lbnQsbixmaWxsID0gMCklPiV0b3BfbigtMTApJT4laGVhZCgxMCkKCmJpbmdfd29yZF9jb3VudHMgJT4lCiAgZmlsdGVyKG4gPiAzKSAlPiUKICBtdXRhdGUobiA9IGlmX2Vsc2Uoc2VudGltZW50ID09ICJuZWdhdGl2ZSIsIC1uLCBuKSkgJT4lCiAgbXV0YXRlKHdvcmQgPSByZW9yZGVyKHdvcmQsIG4pKSAlPiUKICBnZ3Bsb3QoYWVzKHdvcmQsIG4sIGZpbGwgPSBzZW50aW1lbnQpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArCiAgbGFicyh5PSJDb250cmlidXRpb24gdG8gc2VudGltZW50Iix0aXRsZT0iYmluZyBzZW50aW1lbnRzIikrCiNzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9dmlyaWRpc19wYWwob3B0aW9uID0gIkQiKSgyKSkKIHNjYWxlX2ZpbGxfdmlyaWRpcyhlbmQgPSAwLjg1LCBkaXNjcmV0ZT1UUlVFLCBkaXJlY3Rpb24gPSAxKQoKYGBgCgoKIyMjIyBBbHRlcm5hdGl2ZSB3YXkgb2Ygc2NyYXBwaW5nIGZyb20gc2V2ZXJhbCB3ZWIgcGFnZXMKCmBgYHtyfQoKIyBuPTQKIyAKIyBGQ0E9bGlzdCgpCiMgCiMgZm9yIChpIGluIDI6bil7CiMgICAKIyBGQ0FbWzFdXT1yZWFkX2h0bWwoImh0dHBzOi8vd3d3LmdsYXNzZG9vci5jb20vUmV2aWV3cy9GQ0EtRmlhdC1DaHJ5c2xlci1BdXRvbW9iaWxlcy1SZXZpZXdzLUUxNDkuaHRtIiklPiUgaHRtbF9ub2RlcygiLmhyZXZpZXciKSU+JWh0bWxfdGV4dCh0cmltID0gVFJVRSkKIyAgIAojIEZDQVtbaV1dPXJlYWRfaHRtbChwYXN0ZSgiaHR0cHM6Ly93d3cuZ2xhc3Nkb29yLmNvbS9SZXZpZXdzL0ZDQS1GaWF0LUNocnlzbGVyLUF1dG9tb2JpbGVzLVJldmlld3MtRTE0OV9QIixpLCIuaHRtIixzZXAgPSAiIikpJT4lIGh0bWxfbm9kZXMoIi5ocmV2aWV3IiklPiVodG1sX3RleHQodHJpbSA9IFRSVUUpCiMgICAKIyB9CgoKCgoKCmBgYAoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKYGBge3J9CiNGQ0FfaHRtbDI8LUZDQV9odG1sJT4lc3RyX3JlcGxhY2VfYWxsKCJbWzp4ZGlnaXQ6XV0iLCAiIikKCmNvcnB1cyA9IENvcnB1cyhWZWN0b3JTb3VyY2UoRkNBX2h0bWwpKQoKY29ycHVzID0gdG1fbWFwKGNvcnB1cywgdG9sb3dlcikKY29ycHVzPC0gdG1fbWFwKGNvcnB1cywgc3RyaXBXaGl0ZXNwYWNlKQpjb3JwdXMgPSB0bV9tYXAoY29ycHVzLCByZW1vdmVOdW1iZXJzKQpjb3JwdXMgPSB0bV9tYXAoY29ycHVzLCByZW1vdmVXb3Jkcywgc3RvcHdvcmRzKCJlbmdsaXNoIikpCgpjb3JwdXMgPSB0bV9tYXAoY29ycHVzLCByZW1vdmVXb3JkcyxzdG9wX3VzZXIgKQoKCnRkbSA8LSBUZXJtRG9jdW1lbnRNYXRyaXgoY29ycHVzLAogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyb2wgPSBsaXN0KHJlbW92ZVB1bmN0dWF0aW9uID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RvcHdvcmRzID0gIFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbW92ZU51bWJlcnMgPSBUUlVFLCB0b2xvd2VyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQbGFpblRleHREb2N1bWVudD1UUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmlwV2hpdGVzcGFjZT1UUlVFLCBzdGVtbWluZyA9IFRSVUUpKQoKCgppbnNwZWN0KHRkbSkKCgp0aWR5KHRkbSkKCnRkbSA9IGFzLm1hdHJpeCh0ZG0pCgoKCmBgYAoKCgoKYGBge3J9CmZyZXF1ZW5jaWVzID0gRG9jdW1lbnRUZXJtTWF0cml4KGNvcnB1cykKZnJlcXVlbmNpZXMKYGBgCgoKYGBge3J9CgpmaW5kRnJlcVRlcm1zKGZyZXF1ZW5jaWVzLCBsb3dmcmVxPTEwMCkKCmBgYAoKUmVtb3ZlIHNwYXJzZSB0ZXJtcwpgYGB7cn0Kc3BhcnNlID0gcmVtb3ZlU3BhcnNlVGVybXMoZnJlcXVlbmNpZXMsIDAuOTk1KQpzcGFyc2UKYGBgCgpXaGF0IGFib3V0IGFzc29jaWF0aW9ucyBiZXR3ZWVuIHdvcmRzPyBMZXTigJlzIGhhdmUgYSBsb29rIGF0IHdoYXQgb3RoZXIgd29yZHMgaGFkIGEgaGlnaCBhc3NvY2lhdGlvbiB3aXRoIOKAnGxvdmXigJ0uCmBgYHtyfQoKZmluZEFzc29jcyhmcmVxdWVuY2llcywgYygibG92ZSIsInBvb3IiLCJmbGV4aWJsZSIsImhvcnJpYmxlIiksIGMoMC42LDAuNiwwLjYsMC42KSkKCmBgYAoKCgoKCgoKCgoKIyMjIEhpZ2ggRnJlcXVlbmN5IFdvcmRzClRoZSBtb3N0IGNvbW1vbmx5IHdvcmRzIHVzZWQgaW4gdGhlIHJldmlld3MgaXMgcGxvdHRlZCBiZWxvdy4KCiMjIyBXb3JkIENsb3VkIHdpdGggQmluZyBMZXhpY29uClRoZSBtb3N0IGNvbW1vbiBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgd29yZHMgYXJlIGdyYXBoaWNhbGx5IGRlcGljdGVkIGJlbG93LgoKYGBge3J9CgpjKCJlZGl0IiwiZGVsZXQiLCJ2aWEiLCJzdGFyc3RhcnN0YXJzdGFyc3RhcndvcmsiLCJwZHQiLCJoaWxsIiwiZmFjZWJvb2tzaGFyIikKCiBHbGFzc2Rvb3JQYWdlcyAlPiUKICAgIHVubmVzdF90b2tlbnMod29yZCwgdGV4dCklPiUKICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJiaW5nIikpICU+JQogIGNvdW50KHdvcmQsIHNlbnRpbWVudCwgc29ydCA9IFRSVUUpICU+JQogcmVzaGFwZTI6OmFjYXN0KHdvcmQgfiBzZW50aW1lbnQsIHZhbHVlLnZhciA9ICJuIiwgZmlsbCA9IDApICU+JQogIGNvbXBhcmlzb24uY2xvdWQoY29sb3JzID0gdmlyaWRpc19wYWwob3B0aW9uID0gIkQiKSgyKSwKICAgICAgICAgICAgICAgICAgIG1heC53b3JkcyA9IDEwMCkKYGBgCgoKCgoKIyMjIFdvcmQgQ2xvdWQgd2l0aCBucmMgTGV4aWNvbgoKCmBgYHtyfQpHbGFzc2Rvb3JQYWdlcyAlPiUKICAgIHVubmVzdF90b2tlbnMod29yZCwgdGV4dCklPiUKICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJucmMiKSkgJT4lCiAgY291bnQod29yZCwgc2VudGltZW50LCBzb3J0ID0gVFJVRSklPiUKICBzcHJlYWQoIHdvcmQsbixmaWxsID0gMCklPiVoZWFkKDUpCmBgYAoKCkFtb25nIHNvbWUgb2YgdGhlIHdvcmRzIGNvbW1vbmx5IHVzZWQgYnkgcmV2aWV3ZXJzIHRvIGV4cHJlc3MgcG9zaXRpdmUsbmVnYXRpdmUsam95IG9yIHNhZG5lc3MgaXMgZGlzcGxheWVkIGluIHRoZSB3b3JkIGNsb3VkIGJlbG93LgoKYGBge3J9CgpHbGFzc2Rvb3JQYWdlcyAlPiUKICAgIHVubmVzdF90b2tlbnMod29yZCwgdGV4dCklPiUKICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJucmMiKSkgJT4lCiAgY291bnQod29yZCwgc2VudGltZW50LCBzb3J0ID0gVFJVRSklPiUKZmlsdGVyKHNlbnRpbWVudCAlaW4lIGMoIm5lZ2F0aXZlIiwicG9zaXRpdmUiLCJqb3kiLCJzYWRuZXNzIikpJT4lCnJlc2hhcGUyOjphY2FzdCh3b3JkIH4gc2VudGltZW50LCB2YWx1ZS52YXIgPSAibiIsIGZpbGwgPSAwKSU+JSAKY29tcGFyaXNvbi5jbG91ZChjb2xvcnMgPSB2aXJpZGlzX3BhbChvcHRpb24gPSAiRCIpKDQpLAogICAgICAgICAgICAgICAgICBtYXgud29yZHMgPSAyMDApCgoKCmBgYAoKIyMjIyBOZXR3b3JrIEdyYXBoIFZpc3VhbGl6YXRpb24KCmBgYHtyfQpsaWJyYXJ5KGlncmFwaCkKbGlicmFyeShnZ3JhcGgpCgoKCnRpZHlfZGVzY3JfbmdyYW1zPUdsYXNzZG9vclBhZ2VzICU+JQp1bm5lc3RfdG9rZW5zKHdvcmQsIHRleHQsIHRva2VuID0gIm5ncmFtcyIsIG4gPSAyKSAlPiUKc2VwYXJhdGUod29yZCwgYygid29yZDEiLCAid29yZDIiKSwgc2VwID0gIiAiKSAlPiUKZmlsdGVyKCF3b3JkMSAlaW4lIHN0b3Bfd29yZHMkd29yZCkgJT4lCmZpbHRlcighd29yZDIgJWluJSBzdG9wX3dvcmRzJHdvcmQpJT4lCmZpbHRlcighd29yZDEgJWluJSBzdG9wX3VzZXIpICU+JQpmaWx0ZXIoIXdvcmQyICVpbiUgc3RvcF91c2VyKSU+JQogbXV0YXRlKHdvcmQxID0gcmVtb3ZlTnVtYmVycyh3b3JkMSkpJT4lCm11dGF0ZSh3b3JkMiA9IHJlbW92ZU51bWJlcnMod29yZDIpKQoKCmJpZ3JhbV9jb3VudHM9dGlkeV9kZXNjcl9uZ3JhbXMgJT4lCmNvdW50KHdvcmQxLCB3b3JkMiwgc29ydCA9IFRSVUUpCgoKYmlncmFtX2dyYXBoID1iaWdyYW1fY291bnRzICU+JQpmaWx0ZXIobiA+IDEwKSAlPiUKZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKCkKc2V0LnNlZWQoMSkKYT1ncmlkOjphcnJvdyh0eXBlID0gImNsb3NlZCIsIGxlbmd0aCA9IHVuaXQoLjE1LCAiaW5jaGVzIikpCmdncmFwaChiaWdyYW1fZ3JhcGgsIGxheW91dCA9ICJmciIpICsKZ2VvbV9lZGdlX2xpbmsoYWVzKGVkZ2VfYWxwaGEgPSBuKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSwKYXJyb3cgPSBhLCBlbmRfY2FwID0gY2lyY2xlKC4wNywgJ2luY2hlcycpKSArCmdlb21fbm9kZV9wb2ludChjb2xvciA9IHBhbGV0dGVfbGlnaHQoKVsxXSwgc2l6ZSA9IDUsIGFscGhhID0gMC44KSArCmdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUpLCB2anVzdCA9IDEsIGhqdXN0ID0gMC41KSArCnRoZW1lX3ZvaWQoKQpgYGAKCgojIyMjIFdoYXQgYXJlIHRoZSBtb3N0IGNvbW1vbmx5IHVzZWQgd29yZHMgcmV2aWV3cz8KVGhlIG1vc3QgY29tbW9uIHdvcmQgaXMgZW1wbG95ZWUgd2hpY2ggc3VnZ2VzdCBtYWpvcml0eSBvZiB0aGUgcmV2aWV3ZXJzIHdlcmUgZWl0aGVyIGVtcGxveWVlcyBvciBleC1lbXBsb3llZXMuVGhlIHJlc3QgYXJlIHJlbGF0ZWQgdG8gbWFuYWdlbWVudCBhbmQgdGhlIHdvcmsgZW52aXJvbm1lbnQuCgoKCmBgYHtyfQpkYXRhKHN0b3Bfd29yZHMpCnRpZHlfZGVzY3I8LUdsYXNzZG9vclBhZ2VzICU+JQp1bm5lc3RfdG9rZW5zKHdvcmQsIHRleHQpICU+JSAKbXV0YXRlKHdvcmQ9cmVtb3ZlTnVtYmVycyh3b3JkKSklPiUgCm11dGF0ZSh3b3JkX3N0ZW0gPSB3b3JkU3RlbSh3b3JkKSkgJT4lCmFudGlfam9pbihzdG9wX3dvcmRzLCBieSA9ICJ3b3JkIikgJT4lCmZpbHRlcighd29yZF9zdGVtICVpbiUgc3RvcF93b3JkcyR3b3JkKSAlPiUKZmlsdGVyKCF3b3JkX3N0ZW0gJWluJSBzdG9wX3VzZXIpIAoKCnRpZHlfZGVzY3IgJT4lCmNvdW50KHdvcmRfc3RlbSwgc29ydCA9IFRSVUUpICU+JQpmaWx0ZXIobiA+IDMwKSAlPiUKZ2dwbG90KGFlcyh4ID0gcmVvcmRlcih3b3JkX3N0ZW0sIG4pLCB5ID0gbikpICsKZ2VvbV9jb2woY29sb3IgPSBwYWxldHRlX2xpZ2h0KClbMV0sIGZpbGwgPSBwYWxldHRlX2xpZ2h0KClbMV0sIGFscGhhID0gMC44KSArCmNvb3JkX2ZsaXAoKSArCnRoZW1lX3RxKCkgKwpsYWJzKHggPSAiIiwKeSA9ICJjb3VudCBvZiBtb3N0IGNvbW1vbiB3b3JkcyIsdGl0bHQ9IkNvdW50IG9mIG1vc3QgY29tbW9uIHdvcmRzIikKYGBgCgoKYGBge3J9Cgp0aWR5X2Rlc2NyICU+JQpjb3VudCh3b3JkX3N0ZW0pICU+JQptdXRhdGUod29yZF9zdGVtID0gcmVtb3ZlTnVtYmVycyh3b3JkX3N0ZW0pKSAlPiUKd2l0aCh3b3JkY2xvdWQod29yZF9zdGVtLCBuLCBtYXgud29yZHMgPSAxMDAsIGNvbG9ycyA9IHBhbGV0dGVfbGlnaHQoKSkpCgpgYGAKCgoKYGBge3J9CmJpZ3JhbV9jb3VudHMgJT4lCiBtdXRhdGUod29yZDEgPSByZW1vdmVOdW1iZXJzKHdvcmQxKSklPiUKbXV0YXRlKHdvcmQyID0gcmVtb3ZlTnVtYmVycyh3b3JkMikpJT4lIApmaWx0ZXIobiA+IDIwKSAlPiUKZ2dwbG90KGFlcyh4ID0gcmVvcmRlcih3b3JkMSwtbiksIHkgPSByZW9yZGVyKHdvcmQyLC1uKSwgZmlsbCA9IG4pKSArCmdlb21fdGlsZShhbHBoYSA9IDAuOCwgY29sb3IgPSAid2hpdGUiKSArCnNjYWxlX2ZpbGxfZ3JhZGllbnRuKGNvbG91cnMgPSBjKHBhbGV0dGVfbGlnaHQoKVtbMV1dLCBwYWxldHRlX2xpZ2h0KClbWzJdXSkpICsKY29vcmRfZmxpcCgpICsKdGhlbWVfdHEoKSArCnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICsKdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgdmp1c3QgPSAxLCBoanVzdCA9IDEpKSArCmxhYnMoeCA9ICJmaXJzdCB3b3JkIGluIHBhaXIiLAp5ID0gInNlY29uZCB3b3JkIGluIHBhaXIiKQpgYGAKCgpgYGB7cn0KdGlkeV9kZXNjcl9uZ3JhbXM9R2xhc3Nkb29yUGFnZXMgJT4lCnVubmVzdF90b2tlbnMod29yZCwgdGV4dCwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDIpJT4lCnNlcGFyYXRlKHdvcmQsIGMoIndvcmQxIiwgIndvcmQyIiksIHNlcCA9ICIgIiklPiUKbXV0YXRlKHdvcmQxPXJlbW92ZU51bWJlcnMod29yZDEpKSU+JSAKbXV0YXRlKHdvcmQxID0gd29yZFN0ZW0od29yZDEpKSU+JQptdXRhdGUod29yZDI9cmVtb3ZlTnVtYmVycyh3b3JkMikpJT4lIAptdXRhdGUod29yZDIgPSB3b3JkU3RlbSh3b3JkMikpCgoKCnRpZHlfZGVzY3JfbmdyYW1zCgpgYGAKCgoKCmBgYHtyfQogdGlkeV9GQ0EgJT4lCiAgICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJiaW5nIikpJT4lCiAgZ3JvdXBfYnkoc2VudGltZW50KSU+JWNvdW50KCklPiUKZ2dwbG90KGFlcyh4ID0gcmVvcmRlcihzZW50aW1lbnQsIG4pLCB5ID0gbixmaWxsPXBhbGV0dGVfbGlnaHQoKVsxXSkpICsKZ2VvbV9jb2woICBhbHBoYSA9IDAuOCx3aWR0aCA9IDAuNSkgKwpjb29yZF9mbGlwKCkgKwp0aGVtZV90cSgpKwpsYWJzKHk9InNlbnRpbWVudHMgIix0aXRsZT0iYmluZyBsZXhpY29uIHNlbnRpbWVudCBjb3VudCIgLHg9ImZyZXF1ZW5jeSIpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpKwojc2NhbGVfZmlsbF92aXJpZGlzKGVuZCA9IDAuODUsIGRpc2NyZXRlPVRSVUUsIGRpcmVjdGlvbiA9IDEsb3B0aW9uID0gIkQiKSAKI3NjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz12aXJpZGlzX3BhbChvcHRpb24gPSAiQSIpKDIpKQpzY2FsZV9maWxsX3RxKCkKCgp0aWR5X0ZDQSAlPiUKICAgIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoIm5yYyIpKSU+JQogIGdyb3VwX2J5KHNlbnRpbWVudCklPiVjb3VudCgpJT4lCmdncGxvdChhZXMoeCA9IHJlb3JkZXIoc2VudGltZW50LCBuKSwgeSA9IG4sZmlsbD1wYWxldHRlX2xpZ2h0KClbMV0pKSArCmdlb21fY29sKCAgYWxwaGEgPSAwLjgpICsKY29vcmRfZmxpcCgpICsKdGhlbWVfdHEoKSsKbGFicyh5PSJzZW50aW1lbnRzICIsdGl0bGU9Im5yYyBsZXhpY29uIHNlbnRpbWVudCBjb3VudCIgLHg9ImZyZXF1ZW5jeSIpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpKwojc2NhbGVfZmlsbF92aXJpZGlzKGVuZCA9IDAuODUsIGRpc2NyZXRlPVRSVUUsIGRpcmVjdGlvbiA9IDEsb3B0aW9uID0gIkQiKSAKI3NjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz12aXJpZGlzX3BhbChvcHRpb24gPSAiQSIpKDIpKQpzY2FsZV9maWxsX3RxKCkKCmBgYAoKCgojIyMjIFRvcGljIE1vZGVsbGluZwogVG9waWMgbW9kZWxpbmcgaXMgYSBtZXRob2QgZm9yIHVuc3VwZXJ2aXNlZCBjbGFzc2lmaWNhdGlvbiBvZiBkb2N1bWVudHMsIGJ5IG1vZGVsaW5nIGVhY2ggZG9jdW1lbnQgYXMgYSBtaXh0dXJlIG9mIHRvcGljcyBhbmQgZWFjaCB0b3BpYyBhcyBhIG1peHR1cmUgb2Ygd29yZHMuIExhdGVudCBEaXJpY2hsZXQgYWxsb2NhdGlvbihMREEpIGlzIGEgcGFydGljdWxhcmx5IHBvcHVsYXIgbWV0aG9kIGZvciBmaXR0aW5nIGEgdG9waWMgbW9kZWwuCgpXZSBjYW4gaW52ZXN0aWdhdGUgd2hhdCBhcmUgdGhlIHRvcCBmaXZlIHRvcGljcyBpbiB0aGUgcmV2aWV3cyBieSB0b3BpYyBtb2RlbGxpbmcuCgpgYGB7cn0KCmR0bV93b3Jkc19jb3VudDwtdGlkeV9kZXNjciAlPiUKbXV0YXRlKHdvcmRfc3RlbSA9IHJlbW92ZU51bWJlcnMod29yZF9zdGVtKSkgJT4lCmNvdW50KGRhdGUsIHdvcmRfc3RlbSwgc29ydCA9IFRSVUUpICU+JQp1bmdyb3VwKCkgJT4lCmZpbHRlcih3b3JkX3N0ZW0gIT0gIiIpICU+JQojIENhc3RpbmcgYSBkYXRhIGZyYW1lIHRvIGEgRG9jdW1lbnRUZXJtTWF0cml4LCBUZXJtRG9jdW1lbnRNYXRyaXgsIG9yIGRmbSAgCmNhc3RfZHRtKGRhdGUsIHdvcmRfc3RlbSwgbikKCiMgc2V0IGEgc2VlZCBzbyB0aGF0IHRoZSBvdXRwdXQgb2YgdGhlIG1vZGVsIGlzIHByZWRpY3RhYmxlCmR0bV9sZGE8LUxEQShkdG1fd29yZHNfY291bnQsIGsgPSA1LCBjb250cm9sID0gbGlzdChzZWVkID0gMTIzNCkpCgp0b3BpY3NfYmV0YTwtdGlkeShkdG1fbGRhLCBtYXRyaXggPSAiYmV0YSIpCgpgYGAKCgoKYGBge3J9CnAxPC10b3BpY3NfYmV0YSAlPiUKZmlsdGVyKGdyZXBsKCJbYeKAkHpdKyIsIHRlcm0pKSAlPiUgIyBleHRyYWN0IGFscGhhYmV0cyBhLXoKZ3JvdXBfYnkodG9waWMpICU+JQp0b3BfbigxMCwgYmV0YSkgJT4lCnVuZ3JvdXAoKSAlPiUKYXJyYW5nZSh0b3BpYywgLWJldGEpICU+JQptdXRhdGUodGVybSA9IHJlb3JkZXIodGVybSwgYmV0YSkpICU+JQpnZ3Bsb3QoYWVzKHRlcm0sIGJldGEsIGNvbG9yID0gZmFjdG9yKHRvcGljKSwgZmlsbCA9IGZhY3Rvcih0b3BpYykpKSArCmdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UsIGFscGhhID0gMC44KSArCnNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlX2xpZ2h0KCkpICsKc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZV9saWdodCgpKSArCmZhY2V0X3dyYXAofiB0b3BpYywgbmNvbCA9IDUpICsKY29vcmRfZmxpcCgpICsKdGhlbWVfdHEoKSArCmxhYnMoeCA9ICIiLAp5ID0gImJldGEgKH4gb2NjdXJyZW5jZSBpbiB0b3BpY3MgMeKAkDUpIiwKdGl0bGUgPSAiVGhlIHRvcCAxMCBtb3N0IGNoYXJhY3RlcmlzdGljIHdvcmRzIGRlc2NyaWJlIHRvcGljIGNhdGVnb3JpZXMuIikKYGBgCgoKCgpgYGB7cn0KdXNlcl90b3BpYzwtdGlkeShkdG1fbGRhLCBtYXRyaXggPSAiZ2FtbWEiKSAlPiUKYXJyYW5nZShkZXNjKGdhbW1hKSkgJT4lCmdyb3VwX2J5KGRvY3VtZW50KSAlPiUKdG9wX24oMSwgZ2FtbWEpCmBgYAoKCgpgYGB7cn0KcDI8LXVzZXJfdG9waWMgJT4lCmdyb3VwX2J5KHRvcGljKSAlPiUKdG9wX24oMTAsIGdhbW1hKSAlPiUKZ2dwbG90KGFlcyh4ID0gcmVvcmRlcihkb2N1bWVudCwgLWdhbW1hKSwgeSA9IGdhbW1hLCBjb2xvciA9IGZhY3Rvcih0b3BpYykpKSArCmZhY2V0X3dyYXAofiB0b3BpYywgc2NhbGVzID0gImZyZWUiLCBuY29sID0gNSkgKwpnZW9tX3BvaW50KHNob3cubGVnZW5kID0gRkFMU0UsIHNpemUgPSA0LCBhbHBoYSA9IDAuOCkgKwpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZV9saWdodCgpKSArCnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGVfbGlnaHQoKSkgKwp0aGVtZV90cSgpICsKY29vcmRfZmxpcCgpICsKbGFicyh4ID0gIiIsCnkgPSAiZ2FtbWFcbih+IGFmZmlsaWF0aW9uIHdpdGggdG9waWNzIDHigJA1KSIpCmBgYAoKCmBgYHtyfQpsaWJyYXJ5KGdyaWQpCmxpYnJhcnkoZ3JpZEV4dHJhKQojZ3JpZC5hcnJhbmdlKHAxLCBwMiwgbmNvbCA9IDEsIGhlaWdodHMgPSBjKDAuNywgMC4zKSkKcDEKYGBgCgpgYGB7cn0KcDIKYGBgCgoKVGhlIGRhdGFmcmFtZSBjYW4gYmUgY2FzdCBpbnRvIGEgZG9jdW1lbnQtdGVybSBtYXRyaXggKG9uZS10ZXJtLXBlci1kb2N1bWVudC1wZXItcm93KSB3aXRoIHRoZSBjYXN0X2R0bSBmdW5jdGlvbiBpbiB0aWR5dGV4dC5UaGUgTERBIGZ1bmN0aW9uIGFjY2VwdHMgaW5wdXQgb2YgdGhpcyBmb3JtYXQuCmBgYHtyfQoKZGF0ZV9kdG08LUdsYXNzZG9vclBhZ2VzICU+JQptdXRhdGUoeWVhcj15ZWFyKGRhdGUpKSU+JSAgCnVubmVzdF90b2tlbnMod29yZCwgdGV4dCkgJT4lCmFudGlfam9pbihzdG9wX3dvcmRzKSU+JQphbnRpX2pvaW4oc3RvcF91c2VyMikgICU+JQpmaWx0ZXIoIXdvcmQgJWluJSBzdG9wX3VzZXIyKSAlPiUKbXV0YXRlKHdvcmQ9cmVtb3ZlTnVtYmVycyh3b3JkKSklPiUgCm11dGF0ZSh3b3JkID0gd29yZFN0ZW0od29yZCkpJT4lCmZpbHRlcih3b3JkICE9ICIiKSAlPiUKY291bnQoeWVhciwgd29yZCwgc29ydCA9IFRSVUUpICU+JQp1bmdyb3VwKCklPiUKY2FzdF9kdG0oeWVhciwgd29yZCwgbikKCgogCmBgYAoKCldlIGNhbiBub3cgdXNlIHRvcGljbW9kZWxzIHBhY2thZ2UgdG8gY3JlYXRlIGEgZml2ZSB0b3BpYyBMREEgbW9kZWwuCgpgYGB7cn0KZGF0ZV9sZGEgPC0gTERBKGRhdGVfZHRtLCBrID0gNSwgY29udHJvbCA9IGxpc3Qoc2VlZCA9IDEyMzQpKQoKYGBgCgoKYGBge3J9CmRhdGVfbGRhX3RkIDwtIHRpZHkoZGF0ZV9sZGEpCmRhdGVfbGRhX3RkJT4laGVhZCgpCmBgYAoKVGhlIM6yIHJlcHJlc2VudCB0aGUgcHJvYmFiaWxpdHkgdGhhdCBlYWNoIHRlcm0gb24gYSByb3cgYmVsb25ncyB0aGUgdG9waWMgb24gdGhlIHJvdy4KCgoKYGBge3J9CnRvcF90ZXJtcyA8LSBkYXRlX2xkYV90ZCAlPiUKICBncm91cF9ieSh0b3BpYykgJT4lCiAgdG9wX24oMTAsIGJldGEpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBmaWx0ZXIoZ3JlcGwoIlth4oCQel0rIiwgdGVybSkpICU+JSAjIGV4dHJhY3QgYWxwaGFiZXRzIGEtegogIGFycmFuZ2UodG9waWMsIC1iZXRhKQoKdG9wX3Rlcm1zJT4laGVhZCgpCmBgYAoKCgoKYGBge3J9Cgp0b3BfdGVybXMgJT4lbXV0YXRlKHRvcGljPWFzLmZhY3Rvcih0b3BpYykpJT4lCiAgbXV0YXRlKHRlcm0gPSByZW9yZGVyKHRlcm0sIGJldGEpKSAlPiUKICBnZ3Bsb3QoYWVzKHRlcm0sIGJldGEsIGZpbGwgPSB0b3BpYykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IixzaG93LmxlZ2VuZCA9IEZBTFNFLCBhbHBoYSA9IDAuOCkgKwogIGZhY2V0X3dyYXAofiB0b3BpYywgc2NhbGVzID0gImZyZWUiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1LCBhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSsKICBjb29yZF9mbGlwKCkrCiAgc2NhbGVfZmlsbF92aXJpZGlzKGVuZCA9IDAuNzUsIGRpc2NyZXRlPVRSVUUsIGRpcmVjdGlvbiA9IC0xLG9wdGlvbiA9ICJEIikrCiAgbGFicyh4ID0gIiIsCnkgPSAiYmV0YSAob2NjdXJyZW5jZSBpbiB0b3BpY3MgMS01KSIsCnRpdGxlID0gIlRoZSB0b3AgMTAgbW9zdCBjaGFyYWN0ZXJpc3RpYyB3b3JkcyBkZXNjcmliZSB0b3BpYyBjYXRlZ29yaWVzLiIpCmBgYAoKV2UgdHJlYXQgZWFjaCB5ZWFyIGJlZ2lubmluZyBmcm9tICAyMDA4ICBhcyBhIHNlcGFyYXRlIGRvY3VtZW50LiBTZXR0aW5nIG1hdHJpeCA9ICJnYW1tYSIgcmV0dXJucyBhIHRpZGllZCB2ZXJzaW9uIHdpdGggb25lLWRvY3VtZW50LXBlci10b3BpYy1wZXItcm93LiBOb3cgdGhhdCB3ZSBoYXZlIHRoZXNlIGRvY3VtZW50IGNsYXNzaWZpY2F0aW9ucywgd2UgY2FuIHNlZSBob3cgd2VsbCBvdXIgdW5zdXBlcnZpc2VkIGxlYXJuaW5nIGRpZCBhdCBkaXN0aW5ndWlzaGluZyB0aGUgZml2ZSB0b3BpY3MuIEZpcnN0IHdlIHJlLXNlcGFyYXRlIHRoZSBkb2N1bWVudCBuYW1lIGludG8gdGl0bGUgYW5kIGNoYXB0ZXI6CgpgYGB7cn0KZGF0ZV9sZGFfZ2FtbWEgPC0gdGlkeShkYXRlX2xkYSwgbWF0cml4ID0gImdhbW1hIiklPiVtdXRhdGUodG9waWM9ZmFjdG9yKHRvcGljKSkKZGF0ZV9sZGFfZ2FtbWEKCgpgYGAKCmBgYHtyfQoKZ2dwbG90KGRhdGVfbGRhX2dhbW1hLCBhZXMoZ2FtbWEsIGZpbGwgPSB0b3BpYykpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzAsYmlud2lkdGg9MC4yNSkgKwogIGZhY2V0X3dyYXAofiBkb2N1bWVudCwgbnJvdyA9IDIpKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhlbmQgPSAwLjc1LCBkaXNjcmV0ZT1UUlVFLCBkaXJlY3Rpb24gPSAtMSkrdGhlbWVfdHEoKQoKYGBgCgo=