Clifton_Baldwin

Data Science Journal of Clif Baldwin

Analyzing Tweets about the Philadelphia Flyers Part 2

8 April ’18

Twitter Analysis of Philly Flyers

This post is a continuation of my previous post on Twitter analysis of the Philadelphia Flyers during most of March 2018. Again, it will be presented from a R Notebook.

Here is part 2 for the Philly Flyers R Notebook Analysis: <!DOCTYPE html>

Flyers Tweets - The Tweets

Part 2 of My Analysis of the Philadelphia Flyers Twitter Activity

The previous post examined the Twitter users that tweet about the Philly Flyers. In this post, we will look at the tweets themselves.

Through my position at Stockton University, I have heard that the Philadelphia Flyers are looking for ways to increase ticket sales for games. I determined several data research questions related to how word can be spread for the Philadelphia Flyers.

Specifically, I asked five questions that I want to answer (or attempt to answer) with my study. The first three questions were addressed in the previous post. Then I determined what Twitter data I wanted to analyze in an attempt to address my questions. Those data are tweets with the hashtags #Flyers, #FlyersNation, and #LETSGOFLYERS. Of course I could have added more hashtags, such as #PhillyFlyers, but the chosen hashtags resulted in sufficient data for an initial analysis. Thirdly, I determined what I wanted to measure in that data, and I divided my data questions into two groups. The first group is concerned mostly with the Twitter users and was the main subject of the previous post. This post will focus on the text of the tweets. Lastly I will present the results from the analysis in an attempt to learn something about using Twitter to attract fans who may then attend the games.

For this post, I asked the following questions: 4. How do events (wins vs losses, opponents) impact the amount of tweets? 5. What are the common characteristics of highly retweeted tweets?

I believe answering these questions will provide insight into when and what to tweet about the Flyers. If I were looking for ways to promote the Flyers, I might try to use what I learn to improve the use of social media.

As this is a R Notebook, all the code is in R, version 3.4.4 (2018-03-15) to be exact.

In March 2018, I scraped Twitter several times in order to gather all tweets that had the hashtags #Flyers, #FlyersNation, or #LETSGOFLYERS. The dates of the collections were March 11, March 20, and March 26, 2018. See the previous post for the code I used to scrape Twitter.

First, several R libraries are needed. Note, I tried to use only high quality libraries, such as those developed by the RStudio group.

library(rtweet) # for users_data()
library(tidyverse) # Instead of just ggplot2 and dplyr
library(tidytext)  # For Twitter text manipulation
library(lubridate)  # for date manipulation
library(reshape2)  # for mutate()
library(scales) # for date_breaks() in the ggplot - scale_x_date()
library(stringr) # for string manipulations
library("RColorBrewer") # Because I want to print with Flyers colors!

Read the three datasets into memory and combine into one master dataset. Then clean the datasets. For more information on the data preparation, see the previous post.

# Load the RData files that were saved after scraping Twitter
load(file="rtweets20180311.RData")
tw11 <- rstats_tweets
users11 <- users_data(rstats_tweets)
load(file="rtweets20180320.RData")
tw20 <- rstats_tweets
users20 <- users_data(rstats_tweets)
load(file="rtweets20180326.RData")
# Combine the two datasets
tw <- bind_rows(tw11, tw20, rstats_tweets)
users <- users_data(rstats_tweets)
users <- bind_rows(users11, users20, users)
rm(tw11, users11, tw20, users20, rstats_tweets)
# Remove duplicates, due to overlapping dates in the individual datasets.
tw <- unique(tw)
users <- unique(users)
### Clean up the data
# Remove users that do not (or should not) contribute value to this study.
users <- users[!(users$user_id %in% c("19618527", "471268712", "154699499", "426029765", "19276719", "493658381", "938072969552826368", "321035743")),]
# Only analyze "local" tweeters - location identified as PA, NJ, or DE
select <- grepl("Phil", users$location, ignore.case = TRUE) | grepl("PA", users$location, ignore.case = FALSE) | grepl("NJ", users$location, ignore.case = FALSE) | grepl("DE", users$location, ignore.case = FALSE)
users <- users[select,]
rm(select)
# Verified accounts include professional radio, TV, and news stations (e.g. NBC), and some names (a spot check identifies the selected as broadcastsers and reporters)
users <- users[!users$verified,] # Save only nonverified accounts
# Now select only the tweets that belong to these user_ids
tw <- tw[tw$user_id %in% users$user_id,]
# Save only the tweets that are in English (at least for now)
tw <- tw[tw$lang=="en",]

Prepare a working dataset that groups the tweets by the hour. In other words, how many tweets are there each hour over the time period.

twperhr <- tw %>%
  group_by(Group.1=format(tw$created_at, "%Y-%m-%d %H")) %>%
  summarise(x=n()) 

The data extends over the time period from 2018-03-04 20:56:42 to 2018-03-26 13:00:21. During that time, the Flyers played ten games, but we may want to consider the game previous to the time period as well as what was expected at the end of the time period.

  • Flyers lost to the Panthers (1-4) on March 4 3:00pm
  • Flyers lost to the Penguins (2-5) on March 7 8:00pm
  • Flyers lost to the Bruins (2-3) on March 8 7:00pm
  • Flyers beat the Jets (2-1) on March 10 1:00pm
  • Flyers lost to the Golden Knights (2-3) on March 12 7:00pm
  • Flyers lost to the Blue Jackets (3-5) on March 15 7:00pm
  • Flyers beat the Hurricanes (4-2) on March 17 7:00pm
  • Flyers beat the Capitals (6-3) on March 18 5:00pm
  • Flyers lost to the Red Wings (4-5) on March 20 7:30pm
  • Flyers beat the Rangers (4-3) on March 22 7:00pm
  • Flyers lost to the Penguins (4-5) on March 25 12:30pm
  • Nothing scheduled for March 26
  • Next scheduled game against Dallas on March 27 8:30pm

Load this information into data vectors.

schedule = c("2018-03-07 20", "2018-03-08 19", "2018-03-10 13", "2018-03-12 19", "2018-03-15 19", "2018-03-17 19", "2018-03-18 17", "2018-03-20 19", "2018-03-22 19", "2018-03-25 12")
result <- c("Loss", "Loss", "Win", "Loss", "Loss", "Win", "Win", "Loss", "Win", "Loss")
opponent <- c(" \nPenguins", "Bruins", "Jets", "Golden\nKnights", "Blue\nJackets", "Hurricanes", " \nCapitals", "Red\nWings", "Rangers", "Penguins")
d1 <- format(as.Date(min(tw$created_at),format="%Y-%m-%d"), "%m-%d")
d2 <- format(as.Date(max(tw$created_at),format="%Y-%m-%d"), "%m-%d")

Printing the data as a table

date1 <- data.frame(Date = substr(schedule, 1, 10), Opponent = trimws(sub("\n", " ", opponent)), Result = result)
date1

Using the information gathered, we can create a graph of the time period.

4. How do events (wins vs losses, opponents) impact the amount of tweets?

# Determine the vector locations for game times
matches <- grep(paste(schedule,collapse="|"), twperhr$Group.1)
# Determine the average time from a game official start time that the tweets peak
avPeak <- paste("Average time from game start when tweets peak,", 
  mean(sapply(matches, function(i) 
    which(twperhr$x[(i-12):(i+12)]==max(twperhr$x[(i-12):(i+12)]))-13 )), 
  "hours", sep=" ")
  
# Create a graph of tweets over time and indicate when games occured
ggplot(data=twperhr, aes(x=seq_len(nrow(twperhr)), y=x)) + geom_line() +
    scale_x_continuous(breaks = grep(" 00", twperhr$Group.1), 
      labels = substr(twperhr$Group.1[grepl(" 00", twperhr$Group.1)], 6, 10) ) +
    theme(axis.text.x = element_text(color="darkorange", angle=45), 
          panel.background = element_rect(fill = "white", colour = "orange"),
          panel.grid.minor = element_blank()) +
    annotate("text", x = matches, y = twperhr[matches+6,]$x, 
             label = paste(opponent, "\n(", substr(result,1,1),")",sep=""), 
             colour = gsub("Loss", "red", gsub("Win", "darkblue", result))) +
    labs(title="Tweets Mentioning the Flyers", subtitle=paste(d1,"to",d2,sep=" "), 
         caption= avPeak, x = "Date", y = "Tweets")

# Clean up variables that are no longer needed
rm(matches, avPeak) 

Over the specified time, it does not appear that a win or a loss regularly impacts the number of tweets. The fact that a game is played has a huge impact, but not the outcome. And since the number of tweets peak after the conclusion of the game, the peaks are not the result of anticipation.

While the previous graph was by the hour, perhaps there is something to be gained by looking at the number of tweets per day.

ggplot(data = tw, aes(x = day(created_at))) +
  geom_bar(aes(fill = ..count..)) +
  theme(legend.position = "none") +
#  xlab("March") + ylab("Number of tweets") + 
  labs(title="Tweets Mentioning the Flyers", subtitle=paste(d1,"to",d2,sep=" "), 
         x = "March", y = "Number of Tweets") +
  scale_fill_gradient(low = "orange", high = "orangered2")

There may be something else going on to impact the number of tweets. Perhaps looking at the data by day of the week as well as when games are played would have some pattern?

5. What are the common characteristics of retweeted tweets?

Let us look at the tweets themselves. To do so, we need to clean them up. By that I mean remove references to screen names, hashtags, spaces, numbers, punctuations, and urls.

clean_tweet <- gsub('\\n', '', tw$text) %>% 
  str_replace_all("http\\S+\\s*","") %>%
  str_replace("RT @[a-z,A-Z,0-9]*: ","") %>%
  str_replace_all("#[a-z,A-Z]*","") %>%
  str_replace_all("@[a-z,A-Z]*","") %>%
  str_replace_all("[0-9]","") %>%
  str_replace_all(" "," ")

First we will look at the words used in tweets, and then we will consider the tweets as a whole.

tweets <- data_frame(text=clean_tweet) %>% unnest_tokens(word, text)
data(stop_words)
tweets <- tweets %>% anti_join(stop_words)
tweets %>% count(word, sort = TRUE) 

Graph the words that occur at least 200 times.

tweets %>%
  count(word, sort = TRUE) %>%
  filter(n > 200) %>%
  mutate(word = reorder(word, n)) %>%
  ggplot(aes(word, n)) +
  geom_col(aes(fill = n)) +
  scale_fill_distiller(palette="Oranges") +
  theme(legend.position = "none") +
  xlab("Popular Words") + ylab("Number Occurences") +  
  labs(title="Most Popular Words of Tweets") +
  coord_flip()

Using a sentiment score, analyze each tweet as a whole. To accomplish this, i am determining the sentiment of the words in the tweet, with positive sentiments getting a positive score and negative sentiments getting a negative score. Then I am summing the score for each tweet. Tweets of the form “that is not good” will get a sentiment score of 0, since “not” is negative and “good” is positive, but at least that should not sway the analysis. If “not good” got a score of positive due to the “good”, that would be bad. The sentiment scores will be determined by hour so we can see how the games impact the sentiments of the tweets.

#Determine the sentiments of the tweets
sentiment <- tibble(index = 1:nrow(tw),
                    created = tw$created_at,
                    text = clean_tweet) %>%
  unnest_tokens(word, text) %>%
  inner_join(get_sentiments("nrc")) %>%
  filter(sentiment %in% c("positive", "negative")) %>%
  group_by(index, created) %>%
  count(index, sentiment) %>%
  spread(sentiment, n, fill = 0) %>%
  group_by(Time = round_date(created, unit="hours")) %>%
  mutate(score = positive - negative) %>%
  summarise(score = sum(score))
sentiment$sentiment <- factor(ifelse(sentiment$score > 0, "Negative", "Positive"), labels=c("Positive", "Negative"))

Now we can graph the tweets.

date2 <- format(sentiment$Time, "%Y-%m-%d %H")
for (i in seq_len(10)) { date2 <- sub(schedule[i], opponent[i], date2, ignore.case = TRUE) }
date2[grep("2018-03-", date2, ignore.case = TRUE)] <- ""
ggplot(sentiment, aes(x=as.Date(Time), y=score)) +
  geom_line(size = 1.5, alpha = 0.7, aes(colour = sentiment)) +
  labs(title="Sentiments of Tweets", subtitle=paste(d1,"to",d2,sep=" "), 
       x = "March", y = "Sentiment Score") +
  geom_text(y=rep(c(50, 75, 100), len = nrow(sentiment)), label=date2) +
  theme(legend.position="none")

rm(date2,i)  

The graph may be a little misleading since it appears the Bruins game causes a large spike in tweets. When I manually inspect the data, it appears that the large spike occurs after the conclusion of the March 7 Penguins game and just prior to the start of the March 8 Bruins game.

Moving on, let us now look at the characteristics of retweets. Since retweets, by definition, have multiple occurences, we want just one tweet to represent each retweet set.

clean_tweet <- tw[tw$status_id %in% unique(tw$retweet_status_id),]$text

Clean the text again.

# For removing retweets, references to screen names, hashtags, spaces, numbers, punctuations, urls.
clean_tweet <- gsub('\\n', '', clean_tweet) %>% 
  str_replace_all("http\\S+\\s*","") %>%
  str_replace("RT @[a-z,A-Z,0-9]*: ","") %>%
  str_replace_all("#[a-z,A-Z]*","") %>%
  str_replace_all("@[a-z,A-Z]*","") %>%
  str_replace_all("[0-9]","") %>%
  str_replace_all(" "," ")

Determine the sentiment of each tweet and graph. The top line graphs the positive sentiment, with higher numbers indicating a higher positive sentiment. The lower line graphs the negative sentiment tweets, with lower numbers indicating an increase in negative sentiments.

sentiment <- tibble(index = 1:length(clean_tweet),
  created = tw[tw$status_id %in% unique(tw$retweet_status_id),]$created_at,
              text = clean_tweet) %>%
  unnest_tokens(word, text) %>%
  inner_join(get_sentiments("nrc")) %>%
  filter(sentiment %in% c("positive", "negative")) %>%
  group_by(index, created) %>%
  count(index, sentiment) %>%
  spread(sentiment, n, fill = 0) %>%
  group_by(Time = round_date(created, unit="hours")) %>%
  mutate(score = positive - negative) %>%
  summarise(score = sum(score))
  
sentiment$sentiment <- factor(ifelse(sentiment$score > 0, "Negative", "Positive"), labels=c("Positive", "Negative"))
ggplot(sentiment, aes(x=as.Date(Time), y=score)) +
  geom_line(size = 1.5, alpha = 0.7, aes(colour = sentiment)) +
  labs(title="Sentiments of Tweets", subtitle=paste(d1,"to",d2,sep=" "), 
       x = "March", y = "Sentiment Score") +
  theme(legend.position="none")

date1

Looking at the graph, it appears the loss to the Golden Knights on March 12 was followed by a large spike in positive sentiment with a reduced trend of negative sentiment. Did the loss of the one game cause a high level of encouragement for the upcoming Blue Jackets game? However, after losing to the Blue Jackets, there is a jump in negative sentiment.

I admit that these results do little to address the 5th question, and a more thorough analysis is needed. This further analysis would require more of the same, just in more detail. Maybe looking at what other events occured during the time? Definitley expanding the list of words since the top results are somewhat expected (e.g. game, flyers, win, goal). Another interesting result might be if the sentiment causes more or less retweets? That would be useful information if we wanted to spread the word better.

There is much more I could analyze from this data. For one thing, I should expand on my conclusions from analyzing the data. Since this study was “for fun,” I may or may not return to write a more thorough conclusion, but the reader is free to look at the analysis and form their own conclusions. I guess we will see if I continue with this study or get distracted by another “for fun” project in the next post.

LS0tCnRpdGxlOiAiRmx5ZXJzIFR3ZWV0cyAtIFRoZSBUd2VldHMiCmF1dGhvcjogIkRyLiBDbGlmdG9uIEJhbGR3aW4iCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgUGFydCAyIG9mIE15IEFuYWx5c2lzIG9mIHRoZSBQaGlsYWRlbHBoaWEgRmx5ZXJzIFR3aXR0ZXIgQWN0aXZpdHkKClRoZSBwcmV2aW91cyBwb3N0IGV4YW1pbmVkIHRoZSBUd2l0dGVyIHVzZXJzIHRoYXQgdHdlZXQgYWJvdXQgdGhlIFBoaWxseSBGbHllcnMuIEluIHRoaXMgcG9zdCwgd2Ugd2lsbCBsb29rIGF0IHRoZSB0d2VldHMgdGhlbXNlbHZlcy4KClRocm91Z2ggbXkgcG9zaXRpb24gYXQgU3RvY2t0b24gVW5pdmVyc2l0eSwgSSBoYXZlIGhlYXJkIHRoYXQgdGhlIFBoaWxhZGVscGhpYSBGbHllcnMgYXJlIGxvb2tpbmcgZm9yIHdheXMgdG8gaW5jcmVhc2UgdGlja2V0IHNhbGVzIGZvciBnYW1lcy4gSSBkZXRlcm1pbmVkIHNldmVyYWwgZGF0YSByZXNlYXJjaCBxdWVzdGlvbnMgcmVsYXRlZCB0byBob3cgd29yZCBjYW4gYmUgc3ByZWFkIGZvciB0aGUgUGhpbGFkZWxwaGlhIEZseWVycy4KClNwZWNpZmljYWxseSwgSSBhc2tlZCBmaXZlIHF1ZXN0aW9ucyB0aGF0IEkgd2FudCB0byBhbnN3ZXIgKG9yIGF0dGVtcHQgdG8gYW5zd2VyKSB3aXRoIG15IHN0dWR5LiBUaGUgZmlyc3QgdGhyZWUgcXVlc3Rpb25zIHdlcmUgYWRkcmVzc2VkIGluIHRoZSBwcmV2aW91cyBwb3N0LiBUaGVuIEkgZGV0ZXJtaW5lZCB3aGF0IFR3aXR0ZXIgZGF0YSBJIHdhbnRlZCB0byBhbmFseXplIGluIGFuIGF0dGVtcHQgdG8gYWRkcmVzcyBteSBxdWVzdGlvbnMuIFRob3NlIGRhdGEgYXJlIHR3ZWV0cyB3aXRoIHRoZSBoYXNodGFncyAjRmx5ZXJzLCAjRmx5ZXJzTmF0aW9uLCBhbmQgI0xFVFNHT0ZMWUVSUy4gT2YgY291cnNlIEkgY291bGQgaGF2ZSBhZGRlZCBtb3JlIGhhc2h0YWdzLCBzdWNoIGFzICNQaGlsbHlGbHllcnMsIGJ1dCB0aGUgY2hvc2VuIGhhc2h0YWdzIHJlc3VsdGVkIGluIHN1ZmZpY2llbnQgZGF0YSBmb3IgYW4gaW5pdGlhbCBhbmFseXNpcy4gVGhpcmRseSwgSSBkZXRlcm1pbmVkIHdoYXQgSSB3YW50ZWQgdG8gbWVhc3VyZSBpbiB0aGF0IGRhdGEsIGFuZCBJIGRpdmlkZWQgbXkgZGF0YSBxdWVzdGlvbnMgaW50byB0d28gZ3JvdXBzLiBUaGUgZmlyc3QgZ3JvdXAgaXMgY29uY2VybmVkIG1vc3RseSB3aXRoIHRoZSBUd2l0dGVyIHVzZXJzIGFuZCB3YXMgdGhlIG1haW4gc3ViamVjdCBvZiB0aGUgcHJldmlvdXMgcG9zdC4gVGhpcyBwb3N0IHdpbGwgZm9jdXMgb24gdGhlIHRleHQgb2YgdGhlIHR3ZWV0cy4gTGFzdGx5IEkgd2lsbCBwcmVzZW50IHRoZSByZXN1bHRzIGZyb20gdGhlIGFuYWx5c2lzIGluIGFuIGF0dGVtcHQgdG8gbGVhcm4gc29tZXRoaW5nIGFib3V0IHVzaW5nIFR3aXR0ZXIgdG8gYXR0cmFjdCBmYW5zIHdobyBtYXkgdGhlbiBhdHRlbmQgdGhlIGdhbWVzLgoKRm9yIHRoaXMgcG9zdCwgSSBhc2tlZCB0aGUgZm9sbG93aW5nIHF1ZXN0aW9uczoKNC4gSG93IGRvIGV2ZW50cyAod2lucyB2cyBsb3NzZXMsIG9wcG9uZW50cykgaW1wYWN0IHRoZSBhbW91bnQgb2YgdHdlZXRzPwo1LiBXaGF0IGFyZSB0aGUgY29tbW9uIGNoYXJhY3RlcmlzdGljcyBvZiBoaWdobHkgcmV0d2VldGVkIHR3ZWV0cz8KCkkgYmVsaWV2ZSBhbnN3ZXJpbmcgdGhlc2UgcXVlc3Rpb25zIHdpbGwgcHJvdmlkZSBpbnNpZ2h0IGludG8gd2hlbiBhbmQgd2hhdCB0byB0d2VldCBhYm91dCB0aGUgRmx5ZXJzLiBJZiBJIHdlcmUgbG9va2luZyBmb3Igd2F5cyB0byBwcm9tb3RlIHRoZSBGbHllcnMsIEkgbWlnaHQgdHJ5IHRvIHVzZSB3aGF0IEkgbGVhcm4gdG8gaW1wcm92ZSB0aGUgdXNlIG9mIHNvY2lhbCBtZWRpYS4KCkFzIHRoaXMgaXMgYSBSIE5vdGVib29rLCBhbGwgdGhlIGNvZGUgaXMgaW4gUiwgdmVyc2lvbiAzLjQuNCAoMjAxOC0wMy0xNSkgdG8gYmUgZXhhY3QuIAoKSW4gTWFyY2ggMjAxOCwgSSBzY3JhcGVkIFR3aXR0ZXIgc2V2ZXJhbCB0aW1lcyBpbiBvcmRlciB0byBnYXRoZXIgYWxsIHR3ZWV0cyB0aGF0IGhhZCB0aGUgaGFzaHRhZ3MgI0ZseWVycywgI0ZseWVyc05hdGlvbiwgb3IgI0xFVFNHT0ZMWUVSUy4gVGhlIGRhdGVzIG9mIHRoZSBjb2xsZWN0aW9ucyB3ZXJlIE1hcmNoIDExLCBNYXJjaCAyMCwgYW5kIE1hcmNoIDI2LCAyMDE4LiBTZWUgdGhlIHByZXZpb3VzIHBvc3QgZm9yIHRoZSBjb2RlIEkgdXNlZCB0byBzY3JhcGUgVHdpdHRlci4KCkZpcnN0LCBzZXZlcmFsIFIgbGlicmFyaWVzIGFyZSBuZWVkZWQuIE5vdGUsIEkgdHJpZWQgdG8gdXNlIG9ubHkgaGlnaCBxdWFsaXR5IGxpYnJhcmllcywgc3VjaCBhcyB0aG9zZSBkZXZlbG9wZWQgYnkgdGhlIFJTdHVkaW8gZ3JvdXAuCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHJ0d2VldCkgIyBmb3IgdXNlcnNfZGF0YSgpCmxpYnJhcnkodGlkeXZlcnNlKSAjIEluc3RlYWQgb2YganVzdCBnZ3Bsb3QyIGFuZCBkcGx5cgpsaWJyYXJ5KHRpZHl0ZXh0KSAgIyBGb3IgVHdpdHRlciB0ZXh0IG1hbmlwdWxhdGlvbgpsaWJyYXJ5KGx1YnJpZGF0ZSkgICMgZm9yIGRhdGUgbWFuaXB1bGF0aW9uCmxpYnJhcnkocmVzaGFwZTIpICAjIGZvciBtdXRhdGUoKQpsaWJyYXJ5KHNjYWxlcykgIyBmb3IgZGF0ZV9icmVha3MoKSBpbiB0aGUgZ2dwbG90IC0gc2NhbGVfeF9kYXRlKCkKbGlicmFyeShzdHJpbmdyKSAjIGZvciBzdHJpbmcgbWFuaXB1bGF0aW9ucwpsaWJyYXJ5KCJSQ29sb3JCcmV3ZXIiKSAjIEJlY2F1c2UgSSB3YW50IHRvIHByaW50IHdpdGggRmx5ZXJzIGNvbG9ycyEKYGBgCgoKUmVhZCB0aGUgdGhyZWUgZGF0YXNldHMgaW50byBtZW1vcnkgYW5kIGNvbWJpbmUgaW50byBvbmUgbWFzdGVyIGRhdGFzZXQuIFRoZW4gY2xlYW4gdGhlIGRhdGFzZXRzLiBGb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiB0aGUgZGF0YSBwcmVwYXJhdGlvbiwgc2VlIHRoZSBwcmV2aW91cyBwb3N0LgpgYGB7cn0KIyBMb2FkIHRoZSBSRGF0YSBmaWxlcyB0aGF0IHdlcmUgc2F2ZWQgYWZ0ZXIgc2NyYXBpbmcgVHdpdHRlcgpsb2FkKGZpbGU9InJ0d2VldHMyMDE4MDMxMS5SRGF0YSIpCnR3MTEgPC0gcnN0YXRzX3R3ZWV0cwp1c2VyczExIDwtIHVzZXJzX2RhdGEocnN0YXRzX3R3ZWV0cykKbG9hZChmaWxlPSJydHdlZXRzMjAxODAzMjAuUkRhdGEiKQp0dzIwIDwtIHJzdGF0c190d2VldHMKdXNlcnMyMCA8LSB1c2Vyc19kYXRhKHJzdGF0c190d2VldHMpCmxvYWQoZmlsZT0icnR3ZWV0czIwMTgwMzI2LlJEYXRhIikKCiMgQ29tYmluZSB0aGUgdHdvIGRhdGFzZXRzCnR3IDwtIGJpbmRfcm93cyh0dzExLCB0dzIwLCByc3RhdHNfdHdlZXRzKQp1c2VycyA8LSB1c2Vyc19kYXRhKHJzdGF0c190d2VldHMpCnVzZXJzIDwtIGJpbmRfcm93cyh1c2VyczExLCB1c2VyczIwLCB1c2VycykKCnJtKHR3MTEsIHVzZXJzMTEsIHR3MjAsIHVzZXJzMjAsIHJzdGF0c190d2VldHMpCgojIFJlbW92ZSBkdXBsaWNhdGVzLCBkdWUgdG8gb3ZlcmxhcHBpbmcgZGF0ZXMgaW4gdGhlIGluZGl2aWR1YWwgZGF0YXNldHMuCnR3IDwtIHVuaXF1ZSh0dykKdXNlcnMgPC0gdW5pcXVlKHVzZXJzKQojIyMgQ2xlYW4gdXAgdGhlIGRhdGEKIyBSZW1vdmUgdXNlcnMgdGhhdCBkbyBub3QgKG9yIHNob3VsZCBub3QpIGNvbnRyaWJ1dGUgdmFsdWUgdG8gdGhpcyBzdHVkeS4KdXNlcnMgPC0gdXNlcnNbISh1c2VycyR1c2VyX2lkICVpbiUgYygiMTk2MTg1MjciLCAiNDcxMjY4NzEyIiwgIjE1NDY5OTQ5OSIsICI0MjYwMjk3NjUiLCAiMTkyNzY3MTkiLCAiNDkzNjU4MzgxIiwgIjkzODA3Mjk2OTU1MjgyNjM2OCIsICIzMjEwMzU3NDMiKSksXQoKIyBPbmx5IGFuYWx5emUgImxvY2FsIiB0d2VldGVycyAtIGxvY2F0aW9uIGlkZW50aWZpZWQgYXMgUEEsIE5KLCBvciBERQpzZWxlY3QgPC0gZ3JlcGwoIlBoaWwiLCB1c2VycyRsb2NhdGlvbiwgaWdub3JlLmNhc2UgPSBUUlVFKSB8IGdyZXBsKCJQQSIsIHVzZXJzJGxvY2F0aW9uLCBpZ25vcmUuY2FzZSA9IEZBTFNFKSB8IGdyZXBsKCJOSiIsIHVzZXJzJGxvY2F0aW9uLCBpZ25vcmUuY2FzZSA9IEZBTFNFKSB8IGdyZXBsKCJERSIsIHVzZXJzJGxvY2F0aW9uLCBpZ25vcmUuY2FzZSA9IEZBTFNFKQoKdXNlcnMgPC0gdXNlcnNbc2VsZWN0LF0Kcm0oc2VsZWN0KQoKIyBWZXJpZmllZCBhY2NvdW50cyBpbmNsdWRlIHByb2Zlc3Npb25hbCByYWRpbywgVFYsIGFuZCBuZXdzIHN0YXRpb25zIChlLmcuIE5CQyksIGFuZCBzb21lIG5hbWVzIChhIHNwb3QgY2hlY2sgaWRlbnRpZmllcyB0aGUgc2VsZWN0ZWQgYXMgYnJvYWRjYXN0c2VycyBhbmQgcmVwb3J0ZXJzKQp1c2VycyA8LSB1c2Vyc1shdXNlcnMkdmVyaWZpZWQsXSAjIFNhdmUgb25seSBub252ZXJpZmllZCBhY2NvdW50cwoKIyBOb3cgc2VsZWN0IG9ubHkgdGhlIHR3ZWV0cyB0aGF0IGJlbG9uZyB0byB0aGVzZSB1c2VyX2lkcwp0dyA8LSB0d1t0dyR1c2VyX2lkICVpbiUgdXNlcnMkdXNlcl9pZCxdCgojIFNhdmUgb25seSB0aGUgdHdlZXRzIHRoYXQgYXJlIGluIEVuZ2xpc2ggKGF0IGxlYXN0IGZvciBub3cpCnR3IDwtIHR3W3R3JGxhbmc9PSJlbiIsXQoKYGBgCgpQcmVwYXJlIGEgd29ya2luZyBkYXRhc2V0IHRoYXQgZ3JvdXBzIHRoZSB0d2VldHMgYnkgdGhlIGhvdXIuIEluIG90aGVyIHdvcmRzLCBob3cgbWFueSB0d2VldHMgYXJlIHRoZXJlIGVhY2ggaG91ciBvdmVyIHRoZSB0aW1lIHBlcmlvZC4KYGBge3J9CnR3cGVyaHIgPC0gdHcgJT4lCiAgZ3JvdXBfYnkoR3JvdXAuMT1mb3JtYXQodHckY3JlYXRlZF9hdCwgIiVZLSVtLSVkICVIIikpICU+JQogIHN1bW1hcmlzZSh4PW4oKSkgCmBgYAoKVGhlIGRhdGEgZXh0ZW5kcyBvdmVyIHRoZSB0aW1lIHBlcmlvZCBmcm9tIGByIG1pbih0dyRjcmVhdGVkX2F0KWAgdG8gYHIgbWF4KHR3JGNyZWF0ZWRfYXQpYC4gRHVyaW5nIHRoYXQgdGltZSwgdGhlIEZseWVycyBwbGF5ZWQgdGVuIGdhbWVzLCBidXQgd2UgbWF5IHdhbnQgdG8gY29uc2lkZXIgdGhlIGdhbWUgcHJldmlvdXMgdG8gdGhlIHRpbWUgcGVyaW9kIGFzIHdlbGwgYXMgd2hhdCB3YXMgZXhwZWN0ZWQgYXQgdGhlIGVuZCBvZiB0aGUgdGltZSBwZXJpb2QuCgotIEZseWVycyBsb3N0IHRvIHRoZSBQYW50aGVycyAoMS00KSBvbiBNYXJjaCA0IDM6MDBwbQotIEZseWVycyBsb3N0IHRvIHRoZSBQZW5ndWlucyAoMi01KSBvbiBNYXJjaCA3IDg6MDBwbQotIEZseWVycyBsb3N0IHRvIHRoZSBCcnVpbnMgKDItMykgb24gTWFyY2ggOCA3OjAwcG0KLSBGbHllcnMgYmVhdCB0aGUgSmV0cyAoMi0xKSBvbiBNYXJjaCAxMCAxOjAwcG0KLSBGbHllcnMgbG9zdCB0byB0aGUgR29sZGVuIEtuaWdodHMgKDItMykgb24gTWFyY2ggMTIgNzowMHBtCi0gRmx5ZXJzIGxvc3QgdG8gdGhlIEJsdWUgSmFja2V0cyAoMy01KSBvbiBNYXJjaCAxNSA3OjAwcG0KLSBGbHllcnMgYmVhdCB0aGUgSHVycmljYW5lcyAoNC0yKSBvbiBNYXJjaCAxNyA3OjAwcG0KLSBGbHllcnMgYmVhdCB0aGUgQ2FwaXRhbHMgKDYtMykgb24gTWFyY2ggMTggNTowMHBtCi0gRmx5ZXJzIGxvc3QgdG8gdGhlIFJlZCBXaW5ncyAoNC01KSBvbiBNYXJjaCAyMCA3OjMwcG0KLSBGbHllcnMgYmVhdCB0aGUgUmFuZ2VycyAoNC0zKSBvbiBNYXJjaCAyMiA3OjAwcG0KLSBGbHllcnMgbG9zdCB0byB0aGUgUGVuZ3VpbnMgKDQtNSkgb24gTWFyY2ggMjUgMTI6MzBwbQotIE5vdGhpbmcgc2NoZWR1bGVkIGZvciBNYXJjaCAyNiAKLSBOZXh0IHNjaGVkdWxlZCBnYW1lIGFnYWluc3QgRGFsbGFzIG9uIE1hcmNoIDI3IDg6MzBwbQoKTG9hZCB0aGlzIGluZm9ybWF0aW9uIGludG8gZGF0YSB2ZWN0b3JzLgpgYGB7cn0Kc2NoZWR1bGUgPSBjKCIyMDE4LTAzLTA3IDIwIiwgIjIwMTgtMDMtMDggMTkiLCAiMjAxOC0wMy0xMCAxMyIsICIyMDE4LTAzLTEyIDE5IiwgIjIwMTgtMDMtMTUgMTkiLCAiMjAxOC0wMy0xNyAxOSIsICIyMDE4LTAzLTE4IDE3IiwgIjIwMTgtMDMtMjAgMTkiLCAiMjAxOC0wMy0yMiAxOSIsICIyMDE4LTAzLTI1IDEyIikKCnJlc3VsdCA8LSBjKCJMb3NzIiwgIkxvc3MiLCAiV2luIiwgIkxvc3MiLCAiTG9zcyIsICJXaW4iLCAiV2luIiwgIkxvc3MiLCAiV2luIiwgIkxvc3MiKQoKb3Bwb25lbnQgPC0gYygiIFxuUGVuZ3VpbnMiLCAiQnJ1aW5zIiwgIkpldHMiLCAiR29sZGVuXG5LbmlnaHRzIiwgIkJsdWVcbkphY2tldHMiLCAiSHVycmljYW5lcyIsICIgXG5DYXBpdGFscyIsICJSZWRcbldpbmdzIiwgIlJhbmdlcnMiLCAiUGVuZ3VpbnMiKQoKZDEgPC0gZm9ybWF0KGFzLkRhdGUobWluKHR3JGNyZWF0ZWRfYXQpLGZvcm1hdD0iJVktJW0tJWQiKSwgIiVtLSVkIikKZDIgPC0gZm9ybWF0KGFzLkRhdGUobWF4KHR3JGNyZWF0ZWRfYXQpLGZvcm1hdD0iJVktJW0tJWQiKSwgIiVtLSVkIikKYGBgCgpQcmludGluZyB0aGUgZGF0YSBhcyBhIHRhYmxlCmBgYHtyfQpkYXRlMSA8LSBkYXRhLmZyYW1lKERhdGUgPSBzdWJzdHIoc2NoZWR1bGUsIDEsIDEwKSwgT3Bwb25lbnQgPSB0cmltd3Moc3ViKCJcbiIsICIgIiwgb3Bwb25lbnQpKSwgUmVzdWx0ID0gcmVzdWx0KQpkYXRlMQpgYGAKClVzaW5nIHRoZSBpbmZvcm1hdGlvbiBnYXRoZXJlZCwgd2UgY2FuIGNyZWF0ZSBhIGdyYXBoIG9mIHRoZSB0aW1lIHBlcmlvZC4KCiMjIDQuIEhvdyBkbyBldmVudHMgKHdpbnMgdnMgbG9zc2VzLCBvcHBvbmVudHMpIGltcGFjdCB0aGUgYW1vdW50IG9mIHR3ZWV0cz8KCmBgYHtyfQojIERldGVybWluZSB0aGUgdmVjdG9yIGxvY2F0aW9ucyBmb3IgZ2FtZSB0aW1lcwptYXRjaGVzIDwtIGdyZXAocGFzdGUoc2NoZWR1bGUsY29sbGFwc2U9InwiKSwgdHdwZXJociRHcm91cC4xKQojIERldGVybWluZSB0aGUgYXZlcmFnZSB0aW1lIGZyb20gYSBnYW1lIG9mZmljaWFsIHN0YXJ0IHRpbWUgdGhhdCB0aGUgdHdlZXRzIHBlYWsKYXZQZWFrIDwtIHBhc3RlKCJBdmVyYWdlIHRpbWUgZnJvbSBnYW1lIHN0YXJ0IHdoZW4gdHdlZXRzIHBlYWssIiwgCiAgbWVhbihzYXBwbHkobWF0Y2hlcywgZnVuY3Rpb24oaSkgCiAgICB3aGljaCh0d3BlcmhyJHhbKGktMTIpOihpKzEyKV09PW1heCh0d3BlcmhyJHhbKGktMTIpOihpKzEyKV0pKS0xMyApKSwgCiAgImhvdXJzIiwgc2VwPSIgIikKICAKIyBDcmVhdGUgYSBncmFwaCBvZiB0d2VldHMgb3ZlciB0aW1lIGFuZCBpbmRpY2F0ZSB3aGVuIGdhbWVzIG9jY3VyZWQKZ2dwbG90KGRhdGE9dHdwZXJociwgYWVzKHg9c2VxX2xlbihucm93KHR3cGVyaHIpKSwgeT14KSkgKyBnZW9tX2xpbmUoKSArCiAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gZ3JlcCgiIDAwIiwgdHdwZXJociRHcm91cC4xKSwgCiAgICAgIGxhYmVscyA9IHN1YnN0cih0d3BlcmhyJEdyb3VwLjFbZ3JlcGwoIiAwMCIsIHR3cGVyaHIkR3JvdXAuMSldLCA2LCAxMCkgKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChjb2xvcj0iZGFya29yYW5nZSIsIGFuZ2xlPTQ1KSwgCiAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiLCBjb2xvdXIgPSAib3JhbmdlIiksCiAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSArCiAgICBhbm5vdGF0ZSgidGV4dCIsIHggPSBtYXRjaGVzLCB5ID0gdHdwZXJoclttYXRjaGVzKzYsXSR4LCAKICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUob3Bwb25lbnQsICJcbigiLCBzdWJzdHIocmVzdWx0LDEsMSksIikiLHNlcD0iIiksIAogICAgICAgICAgICAgY29sb3VyID0gZ3N1YigiTG9zcyIsICJyZWQiLCBnc3ViKCJXaW4iLCAiZGFya2JsdWUiLCByZXN1bHQpKSkgKwogICAgbGFicyh0aXRsZT0iVHdlZXRzIE1lbnRpb25pbmcgdGhlIEZseWVycyIsIHN1YnRpdGxlPXBhc3RlKGQxLCJ0byIsZDIsc2VwPSIgIiksIAogICAgICAgICBjYXB0aW9uPSBhdlBlYWssIHggPSAiRGF0ZSIsIHkgPSAiVHdlZXRzIikKCiMgQ2xlYW4gdXAgdmFyaWFibGVzIHRoYXQgYXJlIG5vIGxvbmdlciBuZWVkZWQKcm0obWF0Y2hlcywgYXZQZWFrKSAKYGBgCgpPdmVyIHRoZSBzcGVjaWZpZWQgdGltZSwgaXQgZG9lcyBub3QgYXBwZWFyIHRoYXQgYSB3aW4gb3IgYSBsb3NzIHJlZ3VsYXJseSBpbXBhY3RzIHRoZSBudW1iZXIgb2YgdHdlZXRzLiBUaGUgZmFjdCB0aGF0IGEgZ2FtZSBpcyBwbGF5ZWQgaGFzIGEgaHVnZSBpbXBhY3QsIGJ1dCBub3QgdGhlIG91dGNvbWUuIEFuZCBzaW5jZSB0aGUgbnVtYmVyIG9mIHR3ZWV0cyBwZWFrIGFmdGVyIHRoZSBjb25jbHVzaW9uIG9mIHRoZSBnYW1lLCB0aGUgcGVha3MgYXJlIG5vdCB0aGUgcmVzdWx0IG9mIGFudGljaXBhdGlvbi4gCgpXaGlsZSB0aGUgcHJldmlvdXMgZ3JhcGggd2FzIGJ5IHRoZSBob3VyLCBwZXJoYXBzIHRoZXJlIGlzIHNvbWV0aGluZyB0byBiZSBnYWluZWQgYnkgbG9va2luZyBhdCB0aGUgbnVtYmVyIG9mIHR3ZWV0cyBwZXIgZGF5LgoKYGBge3J9CmdncGxvdChkYXRhID0gdHcsIGFlcyh4ID0gZGF5KGNyZWF0ZWRfYXQpKSkgKwogIGdlb21fYmFyKGFlcyhmaWxsID0gLi5jb3VudC4uKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwojICB4bGFiKCJNYXJjaCIpICsgeWxhYigiTnVtYmVyIG9mIHR3ZWV0cyIpICsgCiAgbGFicyh0aXRsZT0iVHdlZXRzIE1lbnRpb25pbmcgdGhlIEZseWVycyIsIHN1YnRpdGxlPXBhc3RlKGQxLCJ0byIsZDIsc2VwPSIgIiksIAogICAgICAgICB4ID0gIk1hcmNoIiwgeSA9ICJOdW1iZXIgb2YgVHdlZXRzIikgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIm9yYW5nZSIsIGhpZ2ggPSAib3JhbmdlcmVkMiIpCmBgYAoKVGhlcmUgbWF5IGJlIHNvbWV0aGluZyBlbHNlIGdvaW5nIG9uIHRvIGltcGFjdCB0aGUgbnVtYmVyIG9mIHR3ZWV0cy4gUGVyaGFwcyBsb29raW5nIGF0IHRoZSBkYXRhIGJ5IGRheSBvZiB0aGUgd2VlayBhcyB3ZWxsIGFzIHdoZW4gZ2FtZXMgYXJlIHBsYXllZCB3b3VsZCBoYXZlIHNvbWUgcGF0dGVybj8gCgojIyA1LiBXaGF0IGFyZSB0aGUgY29tbW9uIGNoYXJhY3RlcmlzdGljcyBvZiByZXR3ZWV0ZWQgdHdlZXRzPwoKTGV0IHVzIGxvb2sgYXQgdGhlIHR3ZWV0cyB0aGVtc2VsdmVzLiBUbyBkbyBzbywgd2UgbmVlZCB0byBjbGVhbiB0aGVtIHVwLiBCeSB0aGF0IEkgbWVhbiByZW1vdmUgcmVmZXJlbmNlcyB0byBzY3JlZW4gbmFtZXMsIGhhc2h0YWdzLCBzcGFjZXMsIG51bWJlcnMsIHB1bmN0dWF0aW9ucywgYW5kIHVybHMuCgpgYGB7cn0KY2xlYW5fdHdlZXQgPC0gZ3N1YignXFxuJywgJycsIHR3JHRleHQpICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoImh0dHBcXFMrXFxzKiIsIiIpICU+JQogIHN0cl9yZXBsYWNlKCJSVCBAW2EteixBLVosMC05XSo6ICIsIiIpICU+JQogIHN0cl9yZXBsYWNlX2FsbCgiI1thLXosQS1aXSoiLCIiKSAlPiUKICBzdHJfcmVwbGFjZV9hbGwoIkBbYS16LEEtWl0qIiwiIikgJT4lCiAgc3RyX3JlcGxhY2VfYWxsKCJbMC05XSIsIiIpICU+JQogIHN0cl9yZXBsYWNlX2FsbCgiICIsIiAiKQoKYGBgCgpGaXJzdCB3ZSB3aWxsIGxvb2sgYXQgdGhlIHdvcmRzIHVzZWQgaW4gdHdlZXRzLCBhbmQgdGhlbiB3ZSB3aWxsIGNvbnNpZGVyIHRoZSB0d2VldHMgYXMgYSB3aG9sZS4KYGBge3IsIG1lc3NhZ2U9RkFMU0V9CnR3ZWV0cyA8LSBkYXRhX2ZyYW1lKHRleHQ9Y2xlYW5fdHdlZXQpICU+JSB1bm5lc3RfdG9rZW5zKHdvcmQsIHRleHQpCgpkYXRhKHN0b3Bfd29yZHMpCnR3ZWV0cyA8LSB0d2VldHMgJT4lIGFudGlfam9pbihzdG9wX3dvcmRzKQoKdHdlZXRzICU+JSBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkgCmBgYAoKR3JhcGggdGhlIHdvcmRzIHRoYXQgb2NjdXIgYXQgbGVhc3QgMjAwIHRpbWVzLgpgYGB7cn0KdHdlZXRzICU+JQogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKSAlPiUKICBmaWx0ZXIobiA+IDIwMCkgJT4lCiAgbXV0YXRlKHdvcmQgPSByZW9yZGVyKHdvcmQsIG4pKSAlPiUKICBnZ3Bsb3QoYWVzKHdvcmQsIG4pKSArCiAgZ2VvbV9jb2woYWVzKGZpbGwgPSBuKSkgKwogIHNjYWxlX2ZpbGxfZGlzdGlsbGVyKHBhbGV0dGU9Ik9yYW5nZXMiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgeGxhYigiUG9wdWxhciBXb3JkcyIpICsgeWxhYigiTnVtYmVyIE9jY3VyZW5jZXMiKSArICAKICBsYWJzKHRpdGxlPSJNb3N0IFBvcHVsYXIgV29yZHMgb2YgVHdlZXRzIikgKwogIGNvb3JkX2ZsaXAoKQpgYGAKClVzaW5nIGEgc2VudGltZW50IHNjb3JlLCBhbmFseXplIGVhY2ggdHdlZXQgYXMgYSB3aG9sZS4gVG8gYWNjb21wbGlzaCB0aGlzLCBpIGFtIGRldGVybWluaW5nIHRoZSBzZW50aW1lbnQgb2YgdGhlIHdvcmRzIGluIHRoZSB0d2VldCwgd2l0aCBwb3NpdGl2ZSBzZW50aW1lbnRzIGdldHRpbmcgYSBwb3NpdGl2ZSBzY29yZSBhbmQgbmVnYXRpdmUgc2VudGltZW50cyBnZXR0aW5nIGEgbmVnYXRpdmUgc2NvcmUuIFRoZW4gSSBhbSBzdW1taW5nIHRoZSBzY29yZSBmb3IgZWFjaCB0d2VldC4gVHdlZXRzIG9mIHRoZSBmb3JtICJ0aGF0IGlzIG5vdCBnb29kIiB3aWxsIGdldCBhIHNlbnRpbWVudCBzY29yZSBvZiAwLCBzaW5jZSAibm90IiBpcyBuZWdhdGl2ZSBhbmQgImdvb2QiIGlzIHBvc2l0aXZlLCBidXQgYXQgbGVhc3QgdGhhdCBzaG91bGQgbm90IHN3YXkgdGhlIGFuYWx5c2lzLiBJZiAibm90IGdvb2QiIGdvdCBhIHNjb3JlIG9mIHBvc2l0aXZlIGR1ZSB0byB0aGUgImdvb2QiLCB0aGF0IHdvdWxkIGJlIGJhZC4gVGhlIHNlbnRpbWVudCBzY29yZXMgd2lsbCBiZSBkZXRlcm1pbmVkIGJ5IGhvdXIgc28gd2UgY2FuIHNlZSBob3cgdGhlIGdhbWVzIGltcGFjdCB0aGUgc2VudGltZW50cyBvZiB0aGUgdHdlZXRzLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CiNEZXRlcm1pbmUgdGhlIHNlbnRpbWVudHMgb2YgdGhlIHR3ZWV0cwpzZW50aW1lbnQgPC0gdGliYmxlKGluZGV4ID0gMTpucm93KHR3KSwKICAgICAgICAgICAgICAgICAgICBjcmVhdGVkID0gdHckY3JlYXRlZF9hdCwKICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gY2xlYW5fdHdlZXQpICU+JQogIHVubmVzdF90b2tlbnMod29yZCwgdGV4dCkgJT4lCiAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygibnJjIikpICU+JQogIGZpbHRlcihzZW50aW1lbnQgJWluJSBjKCJwb3NpdGl2ZSIsICJuZWdhdGl2ZSIpKSAlPiUKICBncm91cF9ieShpbmRleCwgY3JlYXRlZCkgJT4lCiAgY291bnQoaW5kZXgsIHNlbnRpbWVudCkgJT4lCiAgc3ByZWFkKHNlbnRpbWVudCwgbiwgZmlsbCA9IDApICU+JQogIGdyb3VwX2J5KFRpbWUgPSByb3VuZF9kYXRlKGNyZWF0ZWQsIHVuaXQ9ImhvdXJzIikpICU+JQogIG11dGF0ZShzY29yZSA9IHBvc2l0aXZlIC0gbmVnYXRpdmUpICU+JQogIHN1bW1hcmlzZShzY29yZSA9IHN1bShzY29yZSkpCgpzZW50aW1lbnQkc2VudGltZW50IDwtIGZhY3RvcihpZmVsc2Uoc2VudGltZW50JHNjb3JlID4gMCwgIk5lZ2F0aXZlIiwgIlBvc2l0aXZlIiksIGxhYmVscz1jKCJQb3NpdGl2ZSIsICJOZWdhdGl2ZSIpKQoKYGBgCgpOb3cgd2UgY2FuIGdyYXBoIHRoZSB0d2VldHMuCmBgYHtyfQpkYXRlMiA8LSBmb3JtYXQoc2VudGltZW50JFRpbWUsICIlWS0lbS0lZCAlSCIpCmZvciAoaSBpbiBzZXFfbGVuKDEwKSkgeyBkYXRlMiA8LSBzdWIoc2NoZWR1bGVbaV0sIG9wcG9uZW50W2ldLCBkYXRlMiwgaWdub3JlLmNhc2UgPSBUUlVFKSB9CmRhdGUyW2dyZXAoIjIwMTgtMDMtIiwgZGF0ZTIsIGlnbm9yZS5jYXNlID0gVFJVRSldIDwtICIiCgpnZ3Bsb3Qoc2VudGltZW50LCBhZXMoeD1hcy5EYXRlKFRpbWUpLCB5PXNjb3JlKSkgKwogIGdlb21fbGluZShzaXplID0gMS41LCBhbHBoYSA9IDAuNywgYWVzKGNvbG91ciA9IHNlbnRpbWVudCkpICsKICBsYWJzKHRpdGxlPSJTZW50aW1lbnRzIG9mIFR3ZWV0cyIsIHN1YnRpdGxlPXBhc3RlKGQxLCJ0byIsZDIsc2VwPSIgIiksIAogICAgICAgeCA9ICJNYXJjaCIsIHkgPSAiU2VudGltZW50IFNjb3JlIikgKwogIGdlb21fdGV4dCh5PXJlcChjKDUwLCA3NSwgMTAwKSwgbGVuID0gbnJvdyhzZW50aW1lbnQpKSwgbGFiZWw9ZGF0ZTIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQoKcm0oZGF0ZTIsaSkgIAoKYGBgCgpUaGUgZ3JhcGggbWF5IGJlIGEgbGl0dGxlIG1pc2xlYWRpbmcgc2luY2UgaXQgYXBwZWFycyB0aGUgQnJ1aW5zIGdhbWUgY2F1c2VzIGEgbGFyZ2Ugc3Bpa2UgaW4gdHdlZXRzLiBXaGVuIEkgbWFudWFsbHkgaW5zcGVjdCB0aGUgZGF0YSwgaXQgYXBwZWFycyB0aGF0IHRoZSBsYXJnZSBzcGlrZSBvY2N1cnMgYWZ0ZXIgdGhlIGNvbmNsdXNpb24gb2YgdGhlIE1hcmNoIDcgUGVuZ3VpbnMgZ2FtZSBhbmQganVzdCBwcmlvciB0byB0aGUgc3RhcnQgb2YgdGhlIE1hcmNoIDggQnJ1aW5zIGdhbWUuIAoKTW92aW5nIG9uLCBsZXQgdXMgbm93IGxvb2sgYXQgdGhlIGNoYXJhY3RlcmlzdGljcyBvZiByZXR3ZWV0cy4gU2luY2UgcmV0d2VldHMsIGJ5IGRlZmluaXRpb24sIGhhdmUgbXVsdGlwbGUgb2NjdXJlbmNlcywgd2Ugd2FudCBqdXN0IG9uZSB0d2VldCB0byByZXByZXNlbnQgZWFjaCByZXR3ZWV0IHNldC4KCmBgYHtyfQpjbGVhbl90d2VldCA8LSB0d1t0dyRzdGF0dXNfaWQgJWluJSB1bmlxdWUodHckcmV0d2VldF9zdGF0dXNfaWQpLF0kdGV4dApgYGAKCkNsZWFuIHRoZSB0ZXh0IGFnYWluLgpgYGB7cn0KIyBGb3IgcmVtb3ZpbmcgcmV0d2VldHMsIHJlZmVyZW5jZXMgdG8gc2NyZWVuIG5hbWVzLCBoYXNodGFncywgc3BhY2VzLCBudW1iZXJzLCBwdW5jdHVhdGlvbnMsIHVybHMuCmNsZWFuX3R3ZWV0IDwtIGdzdWIoJ1xcbicsICcnLCBjbGVhbl90d2VldCkgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiaHR0cFxcUytcXHMqIiwiIikgJT4lCiAgc3RyX3JlcGxhY2UoIlJUIEBbYS16LEEtWiwwLTldKjogIiwiIikgJT4lCiAgc3RyX3JlcGxhY2VfYWxsKCIjW2EteixBLVpdKiIsIiIpICU+JQogIHN0cl9yZXBsYWNlX2FsbCgiQFthLXosQS1aXSoiLCIiKSAlPiUKICBzdHJfcmVwbGFjZV9hbGwoIlswLTldIiwiIikgJT4lCiAgc3RyX3JlcGxhY2VfYWxsKCIgIiwiICIpCgpgYGAKCkRldGVybWluZSB0aGUgc2VudGltZW50IG9mIGVhY2ggdHdlZXQgYW5kIGdyYXBoLiBUaGUgdG9wIGxpbmUgZ3JhcGhzIHRoZSBwb3NpdGl2ZSBzZW50aW1lbnQsIHdpdGggaGlnaGVyIG51bWJlcnMgaW5kaWNhdGluZyBhIGhpZ2hlciBwb3NpdGl2ZSBzZW50aW1lbnQuIFRoZSBsb3dlciBsaW5lIGdyYXBocyB0aGUgbmVnYXRpdmUgc2VudGltZW50IHR3ZWV0cywgd2l0aCBsb3dlciBudW1iZXJzIGluZGljYXRpbmcgYW4gaW5jcmVhc2UgaW4gbmVnYXRpdmUgc2VudGltZW50cy4gCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQpzZW50aW1lbnQgPC0gdGliYmxlKGluZGV4ID0gMTpsZW5ndGgoY2xlYW5fdHdlZXQpLAogIGNyZWF0ZWQgPSB0d1t0dyRzdGF0dXNfaWQgJWluJSB1bmlxdWUodHckcmV0d2VldF9zdGF0dXNfaWQpLF0kY3JlYXRlZF9hdCwKICAgICAgICAgICAgICB0ZXh0ID0gY2xlYW5fdHdlZXQpICU+JQogIHVubmVzdF90b2tlbnMod29yZCwgdGV4dCkgJT4lCiAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygibnJjIikpICU+JQogIGZpbHRlcihzZW50aW1lbnQgJWluJSBjKCJwb3NpdGl2ZSIsICJuZWdhdGl2ZSIpKSAlPiUKICBncm91cF9ieShpbmRleCwgY3JlYXRlZCkgJT4lCiAgY291bnQoaW5kZXgsIHNlbnRpbWVudCkgJT4lCiAgc3ByZWFkKHNlbnRpbWVudCwgbiwgZmlsbCA9IDApICU+JQogIGdyb3VwX2J5KFRpbWUgPSByb3VuZF9kYXRlKGNyZWF0ZWQsIHVuaXQ9ImhvdXJzIikpICU+JQogIG11dGF0ZShzY29yZSA9IHBvc2l0aXZlIC0gbmVnYXRpdmUpICU+JQogIHN1bW1hcmlzZShzY29yZSA9IHN1bShzY29yZSkpCiAgCnNlbnRpbWVudCRzZW50aW1lbnQgPC0gZmFjdG9yKGlmZWxzZShzZW50aW1lbnQkc2NvcmUgPiAwLCAiTmVnYXRpdmUiLCAiUG9zaXRpdmUiKSwgbGFiZWxzPWMoIlBvc2l0aXZlIiwgIk5lZ2F0aXZlIikpCgpnZ3Bsb3Qoc2VudGltZW50LCBhZXMoeD1hcy5EYXRlKFRpbWUpLCB5PXNjb3JlKSkgKwogIGdlb21fbGluZShzaXplID0gMS41LCBhbHBoYSA9IDAuNywgYWVzKGNvbG91ciA9IHNlbnRpbWVudCkpICsKICBsYWJzKHRpdGxlPSJTZW50aW1lbnRzIG9mIFR3ZWV0cyIsIHN1YnRpdGxlPXBhc3RlKGQxLCJ0byIsZDIsc2VwPSIgIiksIAogICAgICAgeCA9ICJNYXJjaCIsIHkgPSAiU2VudGltZW50IFNjb3JlIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpCgpgYGAKCmBgYHtyfQpkYXRlMQpgYGAKCkxvb2tpbmcgYXQgdGhlIGdyYXBoLCBpdCBhcHBlYXJzIHRoZSBsb3NzIHRvIHRoZSBHb2xkZW4gS25pZ2h0cyBvbiBNYXJjaCAxMiB3YXMgZm9sbG93ZWQgYnkgYSBsYXJnZSBzcGlrZSBpbiBwb3NpdGl2ZSBzZW50aW1lbnQgd2l0aCBhIHJlZHVjZWQgdHJlbmQgb2YgbmVnYXRpdmUgc2VudGltZW50LiBEaWQgdGhlIGxvc3Mgb2YgdGhlIG9uZSBnYW1lIGNhdXNlIGEgaGlnaCBsZXZlbCBvZiBlbmNvdXJhZ2VtZW50IGZvciB0aGUgdXBjb21pbmcgQmx1ZSBKYWNrZXRzIGdhbWU/IEhvd2V2ZXIsIGFmdGVyIGxvc2luZyB0byB0aGUgQmx1ZSBKYWNrZXRzLCB0aGVyZSBpcyBhIGp1bXAgaW4gbmVnYXRpdmUgc2VudGltZW50LgoKSSBhZG1pdCB0aGF0IHRoZXNlIHJlc3VsdHMgZG8gbGl0dGxlIHRvIGFkZHJlc3MgdGhlIDV0aCBxdWVzdGlvbiwgYW5kIGEgbW9yZSB0aG9yb3VnaCBhbmFseXNpcyBpcyBuZWVkZWQuIFRoaXMgZnVydGhlciBhbmFseXNpcyB3b3VsZCByZXF1aXJlIG1vcmUgb2YgdGhlIHNhbWUsIGp1c3QgaW4gbW9yZSBkZXRhaWwuIE1heWJlIGxvb2tpbmcgYXQgd2hhdCBvdGhlciBldmVudHMgb2NjdXJlZCBkdXJpbmcgdGhlIHRpbWU/IERlZmluaXRsZXkgZXhwYW5kaW5nIHRoZSBsaXN0IG9mIHdvcmRzIHNpbmNlIHRoZSB0b3AgcmVzdWx0cyBhcmUgc29tZXdoYXQgZXhwZWN0ZWQgKGUuZy4gZ2FtZSwgZmx5ZXJzLCB3aW4sIGdvYWwpLiBBbm90aGVyIGludGVyZXN0aW5nIHJlc3VsdCBtaWdodCBiZSBpZiB0aGUgc2VudGltZW50IGNhdXNlcyBtb3JlIG9yIGxlc3MgcmV0d2VldHM/IFRoYXQgd291bGQgYmUgdXNlZnVsIGluZm9ybWF0aW9uIGlmIHdlIHdhbnRlZCB0byBzcHJlYWQgdGhlIHdvcmQgYmV0dGVyLiAKClRoZXJlIGlzIG11Y2ggbW9yZSBJIGNvdWxkIGFuYWx5emUgZnJvbSB0aGlzIGRhdGEuIEZvciBvbmUgdGhpbmcsIEkgc2hvdWxkIGV4cGFuZCBvbiBteSBjb25jbHVzaW9ucyBmcm9tIGFuYWx5emluZyB0aGUgZGF0YS4gU2luY2UgdGhpcyBzdHVkeSB3YXMgImZvciBmdW4sIiBJIG1heSBvciBtYXkgbm90IHJldHVybiB0byB3cml0ZSBhIG1vcmUgdGhvcm91Z2ggY29uY2x1c2lvbiwgYnV0IHRoZSByZWFkZXIgaXMgZnJlZSB0byBsb29rIGF0IHRoZSBhbmFseXNpcyBhbmQgZm9ybSB0aGVpciBvd24gY29uY2x1c2lvbnMuIEkgZ3Vlc3Mgd2Ugd2lsbCBzZWUgaWYgSSBjb250aW51ZSB3aXRoIHRoaXMgc3R1ZHkgb3IgZ2V0IGRpc3RyYWN0ZWQgYnkgYW5vdGhlciAiZm9yIGZ1biIgcHJvamVjdCBpbiB0aGUgbmV4dCBwb3N0Lg==