Doing the New York Times Crossword is the closest thing I have to an evening ritual. Most of the time, I’ll tackle it before the day actually arrives - they launch at 10pm the night before (except Sunday’s, which launches at 6pm on Saturday).
Other people have already done pretty cool explorations of crossword text data. They’ve looked at at comparisons to the Oxford English Dictionary and the Google Books corpora respectively. My favorite piece so far: last year, the NYTimes themselves published an interactive piece exploring the changing meanings of clues over the years.
Meanwhile, my goal here (aside from indulging my inner crossword geek) is to try out a few new packages: website scraping with rvest
and wrangling text data with tidytext
.
Some Questions
What are the most common answers?
Starting off with something easy. What words pop up most frequently?
crossword %>%
count(word, sort = TRUE) %>%
head(5)
It looks like ERA is our winner, with 514 appearances since 1994, with AREA, ERE, ONE and ELI filling out the top 5. It’s unsurprising that these are all short, vowel-heavy words. They’re likely used as short fillers between the longer, more inflexible feature words.
What about their frequency of use over time? Have some of these common words become more or less frequent? Plotting each word’s number of appearances by year:
crossword %>%
filter(word %in% c("ERA", "AREA", "ERE", "ONE", "ELI")) %>%
group_by(year) %>%
count(year, word, sort = TRUE) %>%
#####
ggplot(aes(x = year, y = n)) +
geom_point() +
geom_smooth(method = 'lm',
fill = NA) +
facet_wrap(~word) +
scale_x_continuous(breaks = seq(1996, 2016, 4)) +
labs(y = "Appearances per Year",
x = "Year",
title = "Appearance Frequency of Top 5 NYTimes Crossword Answers",
subtitle = "Using puzzles from the Shortz Era (1994-2017)",
caption = "Data source: XWordInfo.com") +
theme_custom()
ggsave(here("images", "cw_top5_freq.png"), width = 8, height = 6)
Short of a slight downward trend, nothing really convincing yet. What about the nature of the words themselves?
Are words getting longer? Shorter?
The primary source of difficulty in puzzles, in my opinion, stems from giving you clues with any number of plausible answers. Unfortunately, as I was unable to scrape the clue text, we’ll have to make do with a different proxy for puzzle difficulty: average answer length.
Why is this a useful proxy? Again, from purely anecdotal experience, the short answers are giveaways. They’re there to provide much needed letter fragments for other longer answers that are much harder to guess from scratch. The more short answers there are, the more information you can easily lock down. Think of Wheel of Fortune - it’s far easier to complete a phrase once you have most of the letters filled in than right at the start.
Calculating the average length of all the crossword answers in each year, then plotting them:
crossword %>%
mutate(cwdate = as.Date(cwdate)) %>%
group_by(cwdate) %>%
summarise(avg = mean(nchar(word))) %>%
#####
ggplot(aes(x = cwdate, y = avg)) +
geom_point(alpha = 0.1,
na.rm = TRUE) +
geom_smooth(method = 'lm',
color = "red",
size = 1.2,
fill = NA,
na.rm = TRUE) +
scale_y_continuous(breaks = seq(4, 8, 0.5)) +
labs(x = "Date of Publication",
y = "Average Number of Letters in Puzzle",
title = "Average Length of NYTimes Crossword Answers",
subtitle = "Using puzzles from the Shortz Era (1994-2017). Each point represents one crossword puzzle.",
caption = "Data source: XWordInfo.com") +
theme_custom()
ggsave(here("images", "cw_avglength.png"), width = 8, height = 6)
A weakly positive relationship, but this doesn’t tell us much. Monday puzzles are designed to be far easier than Saturday puzzles, so it’s likely that variation in word length between days will be far greater than within them. Plotting the average word length by day of the week, then year:
crossword %>%
filter(cwday != "NA") %>%
mutate(cwday = factor(cwday, levels = c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"))) %>%
mutate(cwdate = as.Date(cwdate)) %>%
group_by(cwdate, cwday) %>%
summarise(avg = mean(nchar(word))) %>%
#####
ggplot(aes(x = cwdate, y = avg)) +
geom_point(aes(color = cwday),
show.legend = FALSE,
alpha = 0.3) +
scale_color_viridis(discrete = TRUE, option = "viridis") +
geom_smooth(color = "black",
size = 1.1,
method = 'lm',
fill = NA) +
facet_wrap(~cwday, nrow = 1) +
scale_x_date(date_labels = "%Y",
date_breaks = "4 years",
date_minor_breaks = "4 years") +
scale_y_continuous(breaks = seq(4, 8, 0.5)) +
labs(x = "Date",
y = "Average Letter Count",
title = "Average Length of NYTimes Crossword Answers by Day",
subtitle = "Using puzzles from the Shortz Era (1994-2017). Each point represents one crossword puzzle.",
caption = "Data source: XWordInfo.com") +
theme_custom() +
theme(axis.text.x = element_text(angle = 60,
hjust = 1,
margin = margin(0, 0, 10, 0)))
ggsave(here("images", "cw_avglength_byday.png"), width = 8, height = 6)
Now we’re getting somewhere. A few observations:
- You can see a puzzle’s intended complexity reflected in the average word length for each day.
- Friday and Saturday words seem to be growing longer on average much faster than that of other days’.
- Sunday words, while described as comparable to Wednesdays or Thursdays in terms of difficulty, are probably a little longer on average to account for the larger grid.
Now, what does this actually look like in practice? I took a look at XWordInfo.com for the two puzzles with the shortest and longest average answer length respectively:
Interesting note: both puzzles have roughly the same number of letters on the grid - the puzzle on the left has 45 “blocks” (black unused spaces) while the puzzle on the right has 38. That led me to look me at the letter density of a puzzle, calculated by the number of lettes on a grid / total grid space.
How does letter density vary by day?
Thankfully, one of the variables that I scraped was the block count for each puzzle. Again, blocks are the fully-black unused spaces on a puzzle grid. If grid sizes are staying the same but average letter count per answer is increasing, it follows that the letter density of each puzzle is increasing. It also seems like a good opportunity to find a standardized measure across puzzles of different grid sizes (looking at you, Sunday). But what does the data actually show?
crossword %>%
select(-c(X, dir, word)) %>%
unique() %>%
mutate(density = ((rowcount * colcount) - blockcount) / (rowcount * colcount)) %>%
filter(cwday != "NA") %>%
mutate(cwday = factor(cwday, levels = c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"))) %>%
mutate(cwdate = as.Date(cwdate)) %>%
select(cwdate, cwday, density) %>%
#####
ggplot(aes(x = cwdate, y = density)) +
geom_point(aes(color = cwday),
show.legend = FALSE,
alpha = 0.3) +
scale_color_viridis(discrete = TRUE, option = "viridis") +
geom_smooth(color = "black",
size = 1,
method = 'lm',
fill = NA) +
facet_wrap(~cwday, nrow = 1) +
scale_x_date(date_labels = "%Y",
date_breaks = "4 years",
date_minor_breaks = "4 years") +
labs(x = "Day of the Week",
y = "Letter Density",
title = "Letter Density (Letters per Grid Space) by Day of the Week",
subtitle = "Using puzzles from the Shortz Era (1994-2017). Each point represents a one crossword puzzle.",
caption = "Data source: XWordInfo.com") +
theme_custom() +
theme(axis.text.x = element_text(angle = 60,
hjust = 1,
margin = margin(0, 0, 10, 0)))
ggsave(here("images", "cw_density_byday.png"), width = 8, height = 6)
As expected. The only minor surprise here is that the range of letter densities seems to be a little narrower on Sundays than the rest of the week - I’m interpreting that as a regression to the mean.
Again, a visual illustration of the puzzles with the lowest and highest letter densities from XWordInfo.com:
Note that the puzzle on the left has a pretty cool maze theme to it. Wish I could attempt it from scratch now!
What words have emerged recently?
When different words or phrases enter the lexicon, it’s only a matter of time before they’re referenced in popular media. I wanted to find the words that only became popular (in terms of the crossword) in recent years.
To do this, I’m leveraging the concept of term frequency-inverse document frequency (td-idf). From Julia Silge’s also-amazing resource, Text Mining with R:
The statistic tf-idf is intended to measure how important a word is to a document in a collection (or corpus) of documents, for example, to one novel in a collection of novels or to one website in a collection of websites.
If we treat each year as separate “documents”, we should be able to figure out what words are most important to each year. This is easily done using tidytext::bind_tf_idf
:
crossword %>%
count(year, word, sort = TRUE) %>%
ungroup() %>%
bind_tf_idf(word, year, n) %>%
arrange(desc(tf_idf))
Some curious results already, but we’ll have to dig deeper to get anything particularly interesting.
Plotting the words most important to the last 5 years:
crossword %>%
count(year, word, sort = TRUE) %>%
ungroup() %>%
bind_tf_idf(word, year, n) %>%
arrange(desc(tf_idf)) %>%
mutate(word = factor(word, levels = rev(unique(word)))) %>%
filter(year >= 2013) %>%
group_by(year) %>%
top_n(5, tf_idf) %>%
ungroup() %>%
#####
ggplot(aes(x = word, y = tf_idf, fill = year)) +
geom_col(show.legend = FALSE) +
facet_wrap(~year,
ncol = 2,
scales = "free") +
coord_flip() +
scale_fill_viridis_c() +
labs(title = "Most Year-Unique Crossword Answers, 2013-2017",
subtitle = "As determined by tf-idf scores for single-year corpora.",
x = NULL,
y = "TF-IDF score",
caption = "Data source: XWordInfo.com") +
theme_custom() +
theme(axis.text.y = element_text(hjust = 1),
strip.text = element_text(margin = margin(0, 0, 5, 0)))
ggsave(here("images", "cw_tf_idf.png"), width = 8, height = 6)
There you have it: 2017 is the year of the #BAE. In fact, it’s been used as an answer this year four whole times so far, and not once before. But what does identifying a word as “important” to a particular document actually look like in terms of appearance frequency? Plotting how often each of 2017’s important words appeared by year:
crossword %>%
filter(word %in% c("BAE", "LGBT", "IDRISELBA", "ABBACY", "ETSY", "NSFW")) %>%
mutate(word = factor(word, levels = c("BAE", "LGBT", "IDRISELBA", "ABBACY", "ETSY", "NSFW"))) %>%
count(year, word, sort = TRUE) %>%
ggplot(aes(x = year, y = n, fill = word)) +
geom_col(show.legend = FALSE) +
facet_wrap(~word, ncol = 2) +
scale_fill_viridis_d() +
scale_x_continuous(breaks = seq(2007, 2017, 2)) +
labs(x = NULL,
y = "Number of Appearances",
title = "Appearance Frequency for 2017's \"Important\" Answers",
subtitle = "As determined by tf-idf scores for single-year corpora.",
caption = "Data source: XWordInfo.com") +
theme_custom()
ggsave(here("images", "cw_tf_idf_2017.png"), width = 8, height = 6)
If unique to a year’s crossword answer corpus, it looks like a given word only needs to appear as few as 3-4 times within that year - unsurprising when you consider that most other words are used a handful of times across all time at best. Other observations:
- ETSY actually peaked in 2016 (so far), and you’ll notice that it appears as #3 on 2016’s important words as well.
- SIRI also ranks high twice in the last five years. It was lauched with the iPhone 4S in late 2011, so it makes sense that it’d take until 2013/2014 at the earliest for the word to become popular enough to use as a crossword clue.
- A manual look at the clues for IDRISELBA cited his roles in The Wire (2002-2004) once and Mandela: Long Walk to Freedom (2013) twice. Interestingly enough, no mention of the four films he’s been in this year ( Thor: Ragnarok , The Mountain Between Us, The Dark Tower and Molly’s Game ).
- What’s the deal with CCCCC and UUUUU in 2013? A count by date shows that both answers actually appeared three distinct times, all in the same puzzle. I looked the puzzle up out of curiosity and was faced with this monster, courtesy of Jeff Chen:
What a beaut. (Also note that TTTTT, unlike the other two letters, only appeared twice and did not rank as important to 2013’s corpus.)
How lexically diverse are crossword puzzles?
The last thing I want to look at is lexical diversity. How rich and varied are the answers used in the puzzles? The most common way to measure this is the Type-Token Ratio - the ratio of unique words to total words in a corpus. The idea is this: if there are fewer repeated words, then TTR increases and vice versa. There’s a great general explainer on TTR here.
As it’s highly unlikely that answers repeat within a single puzzle, I’ve aggregated all the answers for each year’s worth of crossword puzzles. Calculating the TTR by year and plotting it:
crossword %>%
filter(year != "2017") %>%
plyr::ddply(~year, summarise, distinct_words = n_distinct(word), total_words = length(word)) %>%
mutate(TTR = distinct_words / total_words) %>%
ggplot(aes(x = year, y = TTR)) +
geom_point() +
geom_smooth(color = "grey",
linetype = "dashed",
size = 0.5,
method = "lm",
se = FALSE) +
labs(x = "Year",
y = "Type-Token Ratio",
title = "Lexical Diversity of NYTimes Crosswords by Year",
subtitle = "Using puzzles from the Shortz Era (1994-2017). Lexical diversity is measured by Type Token Ratio \n(TTR), where TTR = (number of unique words) / (total number of words)",
caption = "Data source: XWordInfo.com") +
theme_custom()
ggsave(here("images", "cw_ttr.png"), width = 8, height = 6)
That’s a stricter upward trend than I imagined. This tells us a few things:
- An TTR in the range of 0.53-0.58 tells us that there are roughly half as many unique answers as total answers used within each year.
- The TTR has grown by about 0.05 between 1994 and 2016 (I omitted 2017 due to the incomplete year). A puzzle in 2016 features about 5% more unique answers than a puzzle in 1994 would have.
As before, the natural next question: how does lexical diversity vary by day of the week?
crossword %>%
filter(year != "2017") %>%
filter(cwday != "NA") %>%
mutate(cwday = factor(cwday, levels = c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"))) %>%
plyr::ddply(plyr::.(year, cwday), summarise, distinct_words = n_distinct(word), total_words = length(word)) %>%
mutate(TTR = distinct_words / total_words) %>%
ggplot(aes(x = year, y = TTR)) +
geom_point(aes(color = cwday),
show.legend = FALSE) +
scale_color_viridis(discrete = TRUE, option = "viridis") +
geom_smooth(color = "grey",
linetype = "dashed",
size = 0.5,
method = 'lm',
se = FALSE) +
facet_wrap(~cwday, nrow = 1) +
scale_x_continuous(breaks = seq(1996, 2016, 4)) +
labs(x = "Year",
y = "Type-Token Ratio",
title = "Lexical Diversity of NYTimes Crosswords by Day of the Week",
subtitle = "Using puzzles from the Shortz Era (1994-2017). Lexical diversity is measured by Type Token Ratio \n(TTR), where TTR = (number of unique words) / (total number of words)",
caption = "Data source: XWordInfo.com") +
theme_custom() +
theme(axis.text.x = element_text(angle = 60, hjust = 1))
ggsave(here("images", "cw_ttr_byday.png"), width = 8, height = 6)
- Variation in TTR between days is way greater than between years. Saturdays have almost 15% more unique answers per total answer count than Mondays.
- Always interesting to note where Sunday falls on the spectrum - in this case, much closer in lexical diversity to Mondays/Tuesdays than the middle of the week.
- Note how the TTRs have jumped to the 0.80-0.95 range when disaggregating by day, compared to 0.5-0.6 when plotting by year. That’s super interesting. One possible interpretation is that repeated words tend to be repeated across days rather than within them. But that’s an exploration for another time.
LS0tCnRpdGxlOiAiMjQgWWVhcnMgb2YgTllUIENyb3Nzd29yZCBBbnN3ZXJzIgphdXRob3I6ICJKb25hdGhhbiBUYW4iCmRhdGU6ICI5LzIvMjAxNyIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMwogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIGZpZy53aWR0aCA9IDgsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuaGVpZ2h0ID0gNiwKICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICdjZW50ZXInKQoKIyBTZXR1cApsaWJyYXJ5KHRpZHl2ZXJzZSkKIyBsaWJyYXJ5KHBseXIpCmxpYnJhcnkoaGVyZSkKbGlicmFyeSh0aWR5dGV4dCkKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KHZpcmlkaXMpCgpvcHRpb25zKHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCiMgU2V0IGN1c3RvbSB0aGVtZQp0aGVtZV9jdXN0b20gPC0gZnVuY3Rpb24oYmFzZV9zaXplID0gMTIsIGJhc2VfZmFtaWx5ID0gIiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgYmFzZV9saW5lX3NpemUgPSBiYXNlX3NpemUvMjIsCiAgICAgICAgICAgICAgICAgICAgICAgICBiYXNlX3JlY3Rfc2l6ZSA9IGJhc2Vfc2l6ZS8yMikgewogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gYmFzZV9zaXplLCBiYXNlX2ZhbWlseSA9IGJhc2VfZmFtaWx5LAogICAgICAgICAgICAgYmFzZV9saW5lX3NpemUgPSBiYXNlX2xpbmVfc2l6ZSwKICAgICAgICAgICAgIGJhc2VfcmVjdF9zaXplID0gYmFzZV9yZWN0X3NpemUpICUrcmVwbGFjZSUKICAgIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKGNvbG9yID0gIiNFRUVFRUUiKSwKICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gIkFzYXAgU2VtaUJvbGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luID0gbWFyZ2luKDUsIDAsIDUsIDApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICIjMEYwRjBGIiksCiAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJBc2FwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDEyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcmdpbiA9IG1hcmdpbigwLCAwLCAxMCwgMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIiMzRjNGM0YiKSwKICAgICAgICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiQXNhcCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZSA9ICJpdGFsaWMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSA4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJnaW4gPSBtYXJnaW4oNSwgMCwgMCwgMCkpLAogICAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gIkFzYXAgU2VtaUJvbGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gInBsYWluIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJnaW4gPSBtYXJnaW4oMTAsIDEwLCAxMCwgMTApKSwKICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJBc2FwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2UgPSAicGxhaW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luID0gbWFyZ2luKDAsIDAsIDUsIDApKSwKICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJBc2FwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2UgPSAicGxhaW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luID0gbWFyZ2luKDAsIDAsIDAsIDUpKSwgICAgICAgICAgCiAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gIkFzYXAgU2VtaUJvbGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICIjM0YzRjNGIiksCiAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiQXNhcCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICIjM0YzRjNGIiksCiAgICAgICAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJBc2FwIE1lZGl1bSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiIzNGM0YzRiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcmdpbiA9IG1hcmdpbigxMCwgMTAsIDEwLCAxMCkpKQp9CmBgYAoKRG9pbmcgdGhlIE5ldyBZb3JrIFRpbWVzIENyb3Nzd29yZCBpcyB0aGUgY2xvc2VzdCB0aGluZyBJIGhhdmUgdG8gYW4gZXZlbmluZyByaXR1YWwuIE1vc3Qgb2YgdGhlIHRpbWUsIEknbGwgdGFja2xlIGl0IGJlZm9yZSB0aGUgZGF5IGFjdHVhbGx5IGFycml2ZXMgLSB0aGV5IGxhdW5jaCBhdCAxMHBtIHRoZSBuaWdodCBiZWZvcmUgKGV4Y2VwdCBTdW5kYXkncywgd2hpY2ggbGF1bmNoZXMgYXQgNnBtIG9uIFNhdHVyZGF5KS4KCk90aGVyIHBlb3BsZSBoYXZlIGFscmVhZHkgZG9uZSBwcmV0dHkgY29vbCBleHBsb3JhdGlvbnMgb2YgY3Jvc3N3b3JkIHRleHQgZGF0YS4gVGhleSd2ZSBsb29rZWQgYXQgYXQgY29tcGFyaXNvbnMgdG8gdGhlIFtPeGZvcmQgRW5nbGlzaCBEaWN0aW9uYXJ5XShodHRwOi8vYmxvZy5ueWNkYXRhc2NpZW5jZS5jb20vc3R1ZGVudC13b3Jrcy93ZWItc2NyYXBpbmcvbnl0LWNyb3Nzd29yZC1wdXp6bGUtYXBwcm94aW1hdGVseS1jb29sLW9lZC8pIGFuZCB0aGUgW0dvb2dsZSBCb29rc10oaHR0cHM6Ly9ub2FodmVsdG1hbi5jb20vY3Jvc3N3b3JkL2Fib3V0Lmh0bWwpIGNvcnBvcmEgcmVzcGVjdGl2ZWx5LiBNeSBmYXZvcml0ZSBwaWVjZSBzbyBmYXI6IGxhc3QgeWVhciwgdGhlIE5ZVGltZXMgdGhlbXNlbHZlcyBwdWJsaXNoZWQgYW4gaW50ZXJhY3RpdmUgcGllY2UgZXhwbG9yaW5nIFt0aGUgY2hhbmdpbmcgbWVhbmluZ3Mgb2YgY2x1ZXMgb3ZlciB0aGUgeWVhcnNdKGh0dHBzOi8vd3d3Lm55dGltZXMuY29tL2ludGVyYWN0aXZlLzIwMTYvMDIvMDcvb3Bpbmlvbi93aGF0LTc0LXllYXJzLW9mLXRpbWVzLWNyb3Nzd29yZHMtc2F5LWFib3V0LXRoZS13b3Jkcy13ZS11c2UuaHRtbD9tY3Viej0zKS4KCk1lYW53aGlsZSwgbXkgZ29hbCBoZXJlIChhc2lkZSBmcm9tIGluZHVsZ2luZyBteSBpbm5lciBjcm9zc3dvcmQgZ2VlaykgaXMgdG8gdHJ5IG91dCBhIGZldyBuZXcgcGFja2FnZXM6IHdlYnNpdGUgc2NyYXBpbmcgd2l0aCBgcnZlc3RgIGFuZCB3cmFuZ2xpbmcgdGV4dCBkYXRhIHdpdGggYHRpZHl0ZXh0YC4KCiMgR2V0dGluZyB0aGUgRGF0YQoKKkVESVQ6IEFwcmlsIDEzLCAyMDE4KgoKKkFmdGVyIHJlY2VudCByZXF1ZXN0cyBmb3IgbWUgdG8gcmVsZWFzZSB0aGUgb3JpZ2luYWwgZGF0YXNldCwgSSBjb250YWN0ZWQgdGhlIHBlb3BsZSBydW5uaW5nIFhXb3JkIEluZm8uIFRoZXkndmUgc2luY2UgaW5mb3JtZWQgbWUgdGhhdCB3aGlsZSBYV29yZCBJbmZvIGhhcyBhbiBhZ3JlZW1lbnQgd2l0aCBOWVRpbWVzLCB0aGUgdW5kZXJseWluZyBkYXRhIGlzIG5vdCBpbiB0aGUgcHVibGljIGRvbWFpbi4gSSdtIGNvbXBseWluZyBieSAoMSkgcmVtb3ZpbmcgdGhlIHNjcmFwZXIgY29kZSwgYW5kICgyKSBjb250aW51aW5nIG5vdCB0byBkaXN0cmlidXRlIHRoZSB1bmRlcmx5aW5nIGRhdGFzZXQuKgoKKk15IHBlcnNvbmFsIHVuZGVyc3RhbmRpbmcgb2YgdGhlIGxlZ2FsIGFuZCBldGhpY2FsIGlzc3VlcyBhcm91bmQgd2ViIHNjcmFwaW5nIGlzIGdyb3dpbmcuIEluIHRoaXMgY2FzZSwgbGVzc29uIGxlYXJuZWQ6IGFzayB3ZWJzaXRlIG93bmVycyBiZWZvcmUgeW91IHNjcmFwZSB0aGVpciBkYXRhISoKCkkgZGlkbid0IHNjcmFwZSBOWVRpbWVzLmNvbSBpdHNlbGYuIFdoeT8gQmVjYXVzZSBjcm9zc3dvcmRzIHRlbmQgdG8gYXJyaXZlIGJsYW5rLCBhbmQgSSB3YW50ZWQgYW5zd2Vycy4gSW5zdGVhZCwgSSB1c2VkIHRoZSBgcnZlc3RgIHBhY2thZ2UgYW5kIFtTZWxlY3RvciBHYWRnZXRdKGh0dHA6Ly9zZWxlY3RvcmdhZGdldC5jb20vKSB0byBnYXRoZXIgaGlzdG9yaWNhbCBwdXp6bGUgZGF0YSBmcm9tIHRoZSBhbWF6aW5nIHJlc291cmNlIHRoYXQgaXMgW1hXb3JkIEluZm9dKGh0dHBzOi8vd3d3Lnh3b3JkaW5mby5jb20vKS4gfn5JZiB5b3Ugc2NyYXBlIHRoZWlyIHdlYnNpdGUsIEkgc3Ryb25nbHkgc3VnZ2VzdCBhIGRvbmF0aW9uIHRvIGtlZXAgdGhlbSBnb2luZyAtIEkgZGlkLiBUaGVyZSdzIGxvdHMgb2Ygd29uZGVyZnVsIGRhdGEgdGhlcmUgdGhhdCBJJ3ZlIGJhcmVseSBhdHRlbXB0ZWQgdG8gc2lmdCB0aHJvdWdoLn5+IAoKQWx0aG91Z2ggdGhlIE5ZVGltZXMgY3Jvc3N3b3JkIGhhcyBiZWVuIGFyb3VuZCBzaW5jZSBmYXIgZWFybGllciB0aGFuIDE5OTQsIEkgY2hvc2UgdG8gb25seSBsb29rIGF0IHB1enpsZXMgZnJvbSB0aGUgV2lsbCBTaG9ydHogZXJhICgxOTk0IC0gcHJlc2VudCkuIEdpdmVuIHRoZSBhYm92ZSwgSSd2ZSBhbHNvIGNob3NlbiBub3QgdG8gaG9zdCB0aGUgZGF0YSBoZXJlIGFuZCBoYXZlIHJlbW92ZWQgdGhlIHNjcmFwZXIgY29kZSBmcm9tIHRoZSByZXBvc2l0b3J5LgoKYGBge3IsIG1lc3NhZ2UgPSBGLCB3YXJuaW5nID0gRn0KIyBJbXBvcnQgZGF0YQpjcm9zc3dvcmQgPC0gcmVhZC5jc3YoaGVyZSgnZGF0YScsICdwcm9jZXNzZWRfZGF0YScsICdjbGVhbl9jcm9zc3dvcmRzLmNzdicpLAogICAgICAgICAgICAgICAgICAgICAgZmlsZUVuY29kaW5nID0gImxhdGluMSIpICU+JQogIGFzLnRibCgpCmBgYAoKIyBTb21lIFF1ZXN0aW9ucwoKIyMjIFdoYXQgYXJlIHRoZSBtb3N0IGNvbW1vbiBhbnN3ZXJzPwoKU3RhcnRpbmcgb2ZmIHdpdGggc29tZXRoaW5nIGVhc3kuIFdoYXQgd29yZHMgcG9wIHVwIG1vc3QgZnJlcXVlbnRseT8KCmBgYHtyLCBtZXNzYWdlID0gRiwgd2FybmluZyA9IEZ9CmNyb3Nzd29yZCAlPiUKICBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkgJT4lCiAgaGVhZCg1KQpgYGAKCkl0IGxvb2tzIGxpa2UgRVJBIGlzIG91ciB3aW5uZXIsIHdpdGggNTE0IGFwcGVhcmFuY2VzIHNpbmNlIDE5OTQsIHdpdGggQVJFQSwgRVJFLCBPTkUgYW5kIEVMSSBmaWxsaW5nIG91dCB0aGUgdG9wIDUuIEl0J3MgdW5zdXJwcmlzaW5nIHRoYXQgdGhlc2UgYXJlIGFsbCBzaG9ydCwgdm93ZWwtaGVhdnkgd29yZHMuIFRoZXkncmUgbGlrZWx5IHVzZWQgYXMgc2hvcnQgZmlsbGVycyBiZXR3ZWVuIHRoZSBsb25nZXIsIG1vcmUgaW5mbGV4aWJsZSBmZWF0dXJlIHdvcmRzLgoKV2hhdCBhYm91dCB0aGVpciBmcmVxdWVuY3kgb2YgdXNlIG92ZXIgdGltZT8gSGF2ZSBzb21lIG9mIHRoZXNlIGNvbW1vbiB3b3JkcyBiZWNvbWUgbW9yZSBvciBsZXNzIGZyZXF1ZW50PyBQbG90dGluZyBlYWNoIHdvcmQncyBudW1iZXIgb2YgYXBwZWFyYW5jZXMgYnkgeWVhcjoKCmBgYHtyLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNn0KY3Jvc3N3b3JkICU+JQogIGZpbHRlcih3b3JkICVpbiUgYygiRVJBIiwgIkFSRUEiLCAiRVJFIiwgIk9ORSIsICJFTEkiKSkgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgY291bnQoeWVhciwgd29yZCwgc29ydCA9IFRSVUUpICU+JQogICMjIyMjIAogIGdncGxvdChhZXMoeCA9IHllYXIsIHkgPSBuKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xtJywKICAgICAgICAgICAgICBmaWxsID0gTkEpICsKICBmYWNldF93cmFwKH53b3JkKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgxOTk2LCAyMDE2LCA0KSkgKwogIGxhYnMoeSA9ICJBcHBlYXJhbmNlcyBwZXIgWWVhciIsCiAgICAgICB4ID0gIlllYXIiLAogICAgICAgdGl0bGUgPSAiQXBwZWFyYW5jZSBGcmVxdWVuY3kgb2YgVG9wIDUgTllUaW1lcyBDcm9zc3dvcmQgQW5zd2VycyIsIAogICAgICAgc3VidGl0bGUgPSAiVXNpbmcgcHV6emxlcyBmcm9tIHRoZSBTaG9ydHogRXJhICgxOTk0LTIwMTcpIiwKICAgICAgIGNhcHRpb24gPSAiRGF0YSBzb3VyY2U6IFhXb3JkSW5mby5jb20iKSArCiAgdGhlbWVfY3VzdG9tKCkKCmdnc2F2ZShoZXJlKCJpbWFnZXMiLCAiY3dfdG9wNV9mcmVxLnBuZyIpLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYpCmBgYAoKU2hvcnQgb2YgYSBzbGlnaHQgZG93bndhcmQgdHJlbmQsIG5vdGhpbmcgcmVhbGx5IGNvbnZpbmNpbmcgeWV0LiBXaGF0IGFib3V0IHRoZSBuYXR1cmUgb2YgdGhlIHdvcmRzIHRoZW1zZWx2ZXM/CgojIyMgQXJlIHdvcmRzIGdldHRpbmcgbG9uZ2VyPyBTaG9ydGVyPwoKVGhlIHByaW1hcnkgc291cmNlIG9mIGRpZmZpY3VsdHkgaW4gcHV6emxlcywgaW4gbXkgb3Bpbmlvbiwgc3RlbXMgZnJvbSBnaXZpbmcgeW91IGNsdWVzIHdpdGggYW55IG51bWJlciBvZiBwbGF1c2libGUgYW5zd2Vycy4gVW5mb3J0dW5hdGVseSwgYXMgSSB3YXMgdW5hYmxlIHRvIHNjcmFwZSB0aGUgY2x1ZSB0ZXh0LCB3ZSdsbCBoYXZlIHRvIG1ha2UgZG8gd2l0aCBhIGRpZmZlcmVudCBwcm94eSBmb3IgcHV6emxlIGRpZmZpY3VsdHk6IF9hdmVyYWdlIGFuc3dlciBsZW5ndGhfLiAKCldoeSBpcyB0aGlzIGEgdXNlZnVsIHByb3h5PyBBZ2FpbiwgZnJvbSBwdXJlbHkgYW5lY2RvdGFsIGV4cGVyaWVuY2UsIHRoZSBzaG9ydCBhbnN3ZXJzIGFyZSBnaXZlYXdheXMuIFRoZXkncmUgdGhlcmUgdG8gcHJvdmlkZSBtdWNoIG5lZWRlZCBsZXR0ZXIgZnJhZ21lbnRzIGZvciBvdGhlciBsb25nZXIgYW5zd2VycyB0aGF0IGFyZSBtdWNoIGhhcmRlciB0byBndWVzcyBmcm9tIHNjcmF0Y2guIFRoZSBtb3JlIHNob3J0IGFuc3dlcnMgdGhlcmUgYXJlLCB0aGUgbW9yZSBpbmZvcm1hdGlvbiB5b3UgY2FuIGVhc2lseSBsb2NrIGRvd24uIFRoaW5rIG9mIFsqV2hlZWwgb2YgRm9ydHVuZSpdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1doZWVsX29mX0ZvcnR1bmVfKFUuUy5fZ2FtZV9zaG93KSkgLSBpdCdzIGZhciBlYXNpZXIgdG8gY29tcGxldGUgYSBwaHJhc2Ugb25jZSB5b3UgaGF2ZSBtb3N0IG9mIHRoZSBsZXR0ZXJzIGZpbGxlZCBpbiB0aGFuIHJpZ2h0IGF0IHRoZSBzdGFydC4KCkNhbGN1bGF0aW5nIHRoZSBhdmVyYWdlIGxlbmd0aCBvZiBhbGwgdGhlIGNyb3Nzd29yZCBhbnN3ZXJzIGluIGVhY2ggeWVhciwgdGhlbiBwbG90dGluZyB0aGVtOgoKYGBge3IsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2fQpjcm9zc3dvcmQgJT4lCiAgbXV0YXRlKGN3ZGF0ZSA9IGFzLkRhdGUoY3dkYXRlKSkgJT4lCiAgZ3JvdXBfYnkoY3dkYXRlKSAlPiUKICBzdW1tYXJpc2UoYXZnID0gbWVhbihuY2hhcih3b3JkKSkpICU+JQogICMjIyMjIAogIGdncGxvdChhZXMoeCA9IGN3ZGF0ZSwgeSA9IGF2ZykpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4xLAogICAgICAgICAgICAgbmEucm0gPSBUUlVFKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xtJywKICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLAogICAgICAgICAgICAgIHNpemUgPSAxLjIsCiAgICAgICAgICAgICAgZmlsbCA9IE5BLAogICAgICAgICAgICAgIG5hLnJtID0gVFJVRSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoNCwgOCwgMC41KSkgKwogIGxhYnMoeCA9ICJEYXRlIG9mIFB1YmxpY2F0aW9uIiwgCiAgICAgICB5ID0gIkF2ZXJhZ2UgTnVtYmVyIG9mIExldHRlcnMgaW4gUHV6emxlIiwKICAgICAgIHRpdGxlID0gIkF2ZXJhZ2UgTGVuZ3RoIG9mIE5ZVGltZXMgQ3Jvc3N3b3JkIEFuc3dlcnMiLAogICAgICAgc3VidGl0bGUgPSAiVXNpbmcgcHV6emxlcyBmcm9tIHRoZSBTaG9ydHogRXJhICgxOTk0LTIwMTcpLiBFYWNoIHBvaW50IHJlcHJlc2VudHMgb25lIGNyb3Nzd29yZCBwdXp6bGUuIiwKICAgICAgIGNhcHRpb24gPSAiRGF0YSBzb3VyY2U6IFhXb3JkSW5mby5jb20iKSArCiAgdGhlbWVfY3VzdG9tKCkKCmdnc2F2ZShoZXJlKCJpbWFnZXMiLCAiY3dfYXZnbGVuZ3RoLnBuZyIpLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYpCmBgYAoKCkEgd2Vha2x5IHBvc2l0aXZlIHJlbGF0aW9uc2hpcCwgYnV0IHRoaXMgZG9lc24ndCB0ZWxsIHVzIG11Y2guIE1vbmRheSBwdXp6bGVzIGFyZSBkZXNpZ25lZCB0byBiZSBmYXIgZWFzaWVyIHRoYW4gU2F0dXJkYXkgcHV6emxlcywgc28gaXQncyBsaWtlbHkgdGhhdCB2YXJpYXRpb24gaW4gd29yZCBsZW5ndGggYmV0d2VlbiBkYXlzIHdpbGwgYmUgZmFyIGdyZWF0ZXIgdGhhbiB3aXRoaW4gdGhlbS4gUGxvdHRpbmcgdGhlIGF2ZXJhZ2Ugd29yZCBsZW5ndGggYnkgZGF5IG9mIHRoZSB3ZWVrLCB0aGVuIHllYXI6CgpgYGB7ciwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDZ9CmNyb3Nzd29yZCAlPiUKICBmaWx0ZXIoY3dkYXkgIT0gIk5BIikgJT4lCiAgbXV0YXRlKGN3ZGF5ID0gZmFjdG9yKGN3ZGF5LCBsZXZlbHMgPSBjKCJNb25kYXkiLCAiVHVlc2RheSIsICJXZWRuZXNkYXkiLCAiVGh1cnNkYXkiLCAiRnJpZGF5IiwgIlNhdHVyZGF5IiwgIlN1bmRheSIpKSkgJT4lCiAgbXV0YXRlKGN3ZGF0ZSA9IGFzLkRhdGUoY3dkYXRlKSkgJT4lCiAgZ3JvdXBfYnkoY3dkYXRlLCBjd2RheSkgJT4lCiAgc3VtbWFyaXNlKGF2ZyA9IG1lYW4obmNoYXIod29yZCkpKSAlPiUKICAjIyMjIyAKICBnZ3Bsb3QoYWVzKHggPSBjd2RhdGUsIHkgPSBhdmcpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBjd2RheSksCiAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEZBTFNFLAogICAgICAgICAgICAgYWxwaGEgPSAwLjMpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzKGRpc2NyZXRlID0gVFJVRSwgb3B0aW9uID0gInZpcmlkaXMiKSArCiAgZ2VvbV9zbW9vdGgoY29sb3IgPSAiYmxhY2siLAogICAgICAgICAgICAgIHNpemUgPSAxLjEsCiAgICAgICAgICAgICAgbWV0aG9kID0gJ2xtJywgCiAgICAgICAgICAgICAgZmlsbCA9IE5BKSArCiAgZmFjZXRfd3JhcCh+Y3dkYXksIG5yb3cgPSAxKSArCiAgc2NhbGVfeF9kYXRlKGRhdGVfbGFiZWxzID0gIiVZIiwKICAgICAgICAgICAgICAgZGF0ZV9icmVha3MgPSAiNCB5ZWFycyIsCiAgICAgICAgICAgICAgIGRhdGVfbWlub3JfYnJlYWtzID0gIjQgeWVhcnMiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSg0LCA4LCAwLjUpKSArCiAgbGFicyh4ID0gIkRhdGUiLCAKICAgICAgIHkgPSAiQXZlcmFnZSBMZXR0ZXIgQ291bnQiLAogICAgICAgdGl0bGUgPSAiQXZlcmFnZSBMZW5ndGggb2YgTllUaW1lcyBDcm9zc3dvcmQgQW5zd2VycyBieSBEYXkiLAogICAgICAgc3VidGl0bGUgPSAiVXNpbmcgcHV6emxlcyBmcm9tIHRoZSBTaG9ydHogRXJhICgxOTk0LTIwMTcpLiBFYWNoIHBvaW50IHJlcHJlc2VudHMgb25lIGNyb3Nzd29yZCBwdXp6bGUuIiwKICAgICAgIGNhcHRpb24gPSAiRGF0YSBzb3VyY2U6IFhXb3JkSW5mby5jb20iKSArCiAgdGhlbWVfY3VzdG9tKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3QgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcmdpbiA9IG1hcmdpbigwLCAwLCAxMCwgMCkpKQoKZ2dzYXZlKGhlcmUoImltYWdlcyIsICJjd19hdmdsZW5ndGhfYnlkYXkucG5nIiksIHdpZHRoID0gOCwgaGVpZ2h0ID0gNikKYGBgCgoKTm93IHdlJ3JlIGdldHRpbmcgc29tZXdoZXJlLiBBIGZldyBvYnNlcnZhdGlvbnM6CgotICAgWW91IGNhbiBzZWUgYSBwdXp6bGUncyBpbnRlbmRlZCBjb21wbGV4aXR5IHJlZmxlY3RlZCBpbiB0aGUgYXZlcmFnZSB3b3JkIGxlbmd0aCBmb3IgZWFjaCBkYXkuCi0gICBGcmlkYXkgYW5kIFNhdHVyZGF5IHdvcmRzIHNlZW0gdG8gYmUgZ3Jvd2luZyBsb25nZXIgb24gYXZlcmFnZSBtdWNoIGZhc3RlciB0aGFuIHRoYXQgb2Ygb3RoZXIgZGF5cycuCi0gICBTdW5kYXkgd29yZHMsIHdoaWxlIGRlc2NyaWJlZCBhcyBjb21wYXJhYmxlIHRvIFdlZG5lc2RheXMgb3IgVGh1cnNkYXlzIGluIHRlcm1zIG9mIGRpZmZpY3VsdHksIGFyZSBwcm9iYWJseSBhIGxpdHRsZSBsb25nZXIgb24gYXZlcmFnZSB0byBhY2NvdW50IGZvciB0aGUgbGFyZ2VyIGdyaWQuCgpOb3csIHdoYXQgZG9lcyB0aGlzIGFjdHVhbGx5IGxvb2sgbGlrZSBpbiBwcmFjdGljZT8gSSB0b29rIGEgbG9vayBhdCBYV29yZEluZm8uY29tIGZvciB0aGUgdHdvIHB1enpsZXMgd2l0aCB0aGUgW3Nob3J0ZXN0XShodHRwczovL3d3dy54d29yZGluZm8uY29tL0Nyb3Nzd29yZD9kYXRlPTEyLzIzLzIwMDgpIGFuZCBbbG9uZ2VzdF0oaHR0cHM6Ly93d3cueHdvcmRpbmZvLmNvbS9Dcm9zc3dvcmQ/ZGF0ZT0xLzIxLzIwMDUpIGF2ZXJhZ2UgYW5zd2VyIGxlbmd0aCByZXNwZWN0aXZlbHk6Cgo8aW1nIHNyYz0iaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2p0YW53ay9ueXRjcm9zc3dvcmQvbWFzdGVyL2ltYWdlcy9zaG9ydF9wdXp6bGUuUE5HP3Jhdz10cnVlIiB3aWR0aD0iNDAwIj4gPGltZyBzcmM9Imh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9qdGFud2svbnl0Y3Jvc3N3b3JkL21hc3Rlci9pbWFnZXMvbG9uZ19wdXp6bGUuUE5HP3Jhdz10cnVlIiB3aWR0aD0iNDAwIj4KCkludGVyZXN0aW5nIG5vdGU6IGJvdGggcHV6emxlcyBoYXZlIHJvdWdobHkgdGhlIHNhbWUgbnVtYmVyIG9mIGxldHRlcnMgb24gdGhlIGdyaWQgLSB0aGUgcHV6emxlIG9uIHRoZSBsZWZ0IGhhcyA0NSAiYmxvY2tzIiAoYmxhY2sgdW51c2VkIHNwYWNlcykgd2hpbGUgdGhlIHB1enpsZSBvbiB0aGUgcmlnaHQgaGFzIDM4LiBUaGF0IGxlZCBtZSB0byBsb29rIG1lIGF0IHRoZSAqbGV0dGVyIGRlbnNpdHkqIG9mIGEgcHV6emxlLCBjYWxjdWxhdGVkIGJ5IHRoZSBudW1iZXIgb2YgbGV0dGVzIG9uIGEgZ3JpZCAvIHRvdGFsIGdyaWQgc3BhY2UuCgojIyMgSG93IGRvZXMgbGV0dGVyIGRlbnNpdHkgdmFyeSBieSBkYXk/IAoKVGhhbmtmdWxseSwgb25lIG9mIHRoZSB2YXJpYWJsZXMgdGhhdCBJIHNjcmFwZWQgd2FzIHRoZSBibG9jayBjb3VudCBmb3IgZWFjaCBwdXp6bGUuIEFnYWluLCBibG9ja3MgYXJlIHRoZSBmdWxseS1ibGFjayB1bnVzZWQgc3BhY2VzIG9uIGEgcHV6emxlIGdyaWQuIElmIGdyaWQgc2l6ZXMgYXJlIHN0YXlpbmcgdGhlIHNhbWUgYnV0IGF2ZXJhZ2UgbGV0dGVyIGNvdW50IHBlciBhbnN3ZXIgaXMgaW5jcmVhc2luZywgaXQgZm9sbG93cyB0aGF0IHRoZSBsZXR0ZXIgZGVuc2l0eSBvZiBlYWNoIHB1enpsZSBpcyBpbmNyZWFzaW5nLiBJdCBhbHNvIHNlZW1zIGxpa2UgYSBnb29kIG9wcG9ydHVuaXR5IHRvIGZpbmQgYSBzdGFuZGFyZGl6ZWQgbWVhc3VyZSBhY3Jvc3MgcHV6emxlcyBvZiBkaWZmZXJlbnQgZ3JpZCBzaXplcyAobG9va2luZyBhdCB5b3UsIFN1bmRheSkuIEJ1dCB3aGF0IGRvZXMgdGhlIGRhdGEgYWN0dWFsbHkgc2hvdz8KCmBgYHtyfQpjcm9zc3dvcmQgJT4lCiAgc2VsZWN0KC1jKFgsIGRpciwgd29yZCkpICU+JQogIHVuaXF1ZSgpICU+JQogIG11dGF0ZShkZW5zaXR5ID0gKChyb3djb3VudCAqIGNvbGNvdW50KSAtIGJsb2NrY291bnQpIC8gKHJvd2NvdW50ICogY29sY291bnQpKSAlPiUKICBmaWx0ZXIoY3dkYXkgIT0gIk5BIikgJT4lCiAgbXV0YXRlKGN3ZGF5ID0gZmFjdG9yKGN3ZGF5LCBsZXZlbHMgPSBjKCJNb25kYXkiLCAiVHVlc2RheSIsICJXZWRuZXNkYXkiLCAiVGh1cnNkYXkiLCAiRnJpZGF5IiwgIlNhdHVyZGF5IiwgIlN1bmRheSIpKSkgJT4lCiAgbXV0YXRlKGN3ZGF0ZSA9IGFzLkRhdGUoY3dkYXRlKSkgJT4lCiAgc2VsZWN0KGN3ZGF0ZSwgY3dkYXksIGRlbnNpdHkpICU+JQogICMjIyMjIAogIGdncGxvdChhZXMoeCA9IGN3ZGF0ZSwgeSA9IGRlbnNpdHkpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBjd2RheSksCiAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEZBTFNFLAogICAgICAgICAgICAgYWxwaGEgPSAwLjMpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzKGRpc2NyZXRlID0gVFJVRSwgb3B0aW9uID0gInZpcmlkaXMiKSArCiAgZ2VvbV9zbW9vdGgoY29sb3IgPSAiYmxhY2siLAogICAgICAgICAgICAgIHNpemUgPSAxLAogICAgICAgICAgICAgIG1ldGhvZCA9ICdsbScsIAogICAgICAgICAgICAgIGZpbGwgPSBOQSkgKwogIGZhY2V0X3dyYXAofmN3ZGF5LCBucm93ID0gMSkgKwogIHNjYWxlX3hfZGF0ZShkYXRlX2xhYmVscyA9ICIlWSIsCiAgICAgICAgICAgICAgIGRhdGVfYnJlYWtzID0gIjQgeWVhcnMiLAogICAgICAgICAgICAgICBkYXRlX21pbm9yX2JyZWFrcyA9ICI0IHllYXJzIikgKwogIGxhYnMoeCA9ICJEYXkgb2YgdGhlIFdlZWsiLAogICAgICAgeSA9ICJMZXR0ZXIgRGVuc2l0eSIsCiAgICAgICB0aXRsZSA9ICJMZXR0ZXIgRGVuc2l0eSAoTGV0dGVycyBwZXIgR3JpZCBTcGFjZSkgYnkgRGF5IG9mIHRoZSBXZWVrIiwKICAgICAgIHN1YnRpdGxlID0gIlVzaW5nIHB1enpsZXMgZnJvbSB0aGUgU2hvcnR6IEVyYSAoMTk5NC0yMDE3KS4gRWFjaCBwb2ludCByZXByZXNlbnRzIGEgb25lIGNyb3Nzd29yZCBwdXp6bGUuIiwKICAgICAgIGNhcHRpb24gPSAiRGF0YSBzb3VyY2U6IFhXb3JkSW5mby5jb20iKSArCiAgdGhlbWVfY3VzdG9tKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3QgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcmdpbiA9IG1hcmdpbigwLCAwLCAxMCwgMCkpKQoKZ2dzYXZlKGhlcmUoImltYWdlcyIsICJjd19kZW5zaXR5X2J5ZGF5LnBuZyIpLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYpCmBgYAoKCkFzIGV4cGVjdGVkLiBUaGUgb25seSBtaW5vciBzdXJwcmlzZSBoZXJlIGlzIHRoYXQgdGhlIHJhbmdlIG9mIGxldHRlciBkZW5zaXRpZXMgc2VlbXMgdG8gYmUgYSBsaXR0bGUgbmFycm93ZXIgb24gU3VuZGF5cyB0aGFuIHRoZSByZXN0IG9mIHRoZSB3ZWVrIC0gSSdtIGludGVycHJldGluZyB0aGF0IGFzIGEgcmVncmVzc2lvbiB0byB0aGUgbWVhbi4KCkFnYWluLCBhIHZpc3VhbCBpbGx1c3RyYXRpb24gb2YgdGhlIHB1enpsZXMgd2l0aCB0aGUgW2xvd2VzdF0oaHR0cHM6Ly93d3cueHdvcmRpbmZvLmNvbS9Dcm9zc3dvcmQ/ZGF0ZT01LzI5LzIwMTEpIGFuZCBbaGlnaGVzdF0oaHR0cHM6Ly93d3cueHdvcmRpbmZvLmNvbS9Dcm9zc3dvcmQ/ZGF0ZT03LzI3LzIwMTIpIGxldHRlciBkZW5zaXRpZXMgZnJvbSBYV29yZEluZm8uY29tOiAKCjxpbWcgc3JjPSJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vanRhbndrL255dGNyb3Nzd29yZC9tYXN0ZXIvaW1hZ2VzL2xlYXN0X2RlbnNlLlBORz9yYXc9dHJ1ZSIgd2lkdGg9IjQwMCI+IDxpbWcgc3JjPSJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vanRhbndrL255dGNyb3Nzd29yZC9tYXN0ZXIvaW1hZ2VzL21vc3RfZGVuc2UuUE5HP3Jhdz10cnVlIiB3aWR0aD0iNDAwIj4KCk5vdGUgdGhhdCB0aGUgcHV6emxlIG9uIHRoZSBsZWZ0IGhhcyBhIHByZXR0eSBjb29sIG1hemUgdGhlbWUgdG8gaXQuIFdpc2ggSSBjb3VsZCBhdHRlbXB0IGl0IGZyb20gc2NyYXRjaCBub3chCgojIyMgV2hhdCB3b3JkcyBoYXZlIGVtZXJnZWQgcmVjZW50bHk/CgpXaGVuIGRpZmZlcmVudCB3b3JkcyBvciBwaHJhc2VzIGVudGVyIHRoZSBsZXhpY29uLCBpdCdzIG9ubHkgYSBtYXR0ZXIgb2YgdGltZSBiZWZvcmUgdGhleSdyZSByZWZlcmVuY2VkIGluIHBvcHVsYXIgbWVkaWEuIEkgd2FudGVkIHRvIGZpbmQgdGhlIHdvcmRzIHRoYXQgb25seSBiZWNhbWUgcG9wdWxhciAoaW4gdGVybXMgb2YgdGhlIGNyb3Nzd29yZCkgaW4gcmVjZW50IHllYXJzLgoKVG8gZG8gdGhpcywgSSdtIGxldmVyYWdpbmcgdGhlIGNvbmNlcHQgb2YgKnRlcm0gZnJlcXVlbmN5LWludmVyc2UgZG9jdW1lbnQgZnJlcXVlbmN5KiAodGQtaWRmKS4gRnJvbSBKdWxpYSBTaWxnZSdzIGFsc28tYW1hemluZyByZXNvdXJjZSwgW1RleHQgTWluaW5nIHdpdGggUl0oaHR0cDovL3RpZHl0ZXh0bWluaW5nLmNvbS90ZmlkZi5odG1sKToKCj4gVGhlIHN0YXRpc3RpYyAqKnRmLWlkZioqIGlzIGludGVuZGVkIHRvIG1lYXN1cmUgaG93IGltcG9ydGFudCBhIHdvcmQgaXMgdG8gYSBkb2N1bWVudCBpbiBhIGNvbGxlY3Rpb24gKG9yIGNvcnB1cykgb2YgZG9jdW1lbnRzLCBmb3IgZXhhbXBsZSwgdG8gb25lIG5vdmVsIGluIGEgY29sbGVjdGlvbiBvZiBub3ZlbHMgb3IgdG8gb25lIHdlYnNpdGUgaW4gYSBjb2xsZWN0aW9uIG9mIHdlYnNpdGVzLgoKSWYgd2UgdHJlYXQgZWFjaCB5ZWFyIGFzIHNlcGFyYXRlICJkb2N1bWVudHMiLCB3ZSBzaG91bGQgYmUgYWJsZSB0byBmaWd1cmUgb3V0IHdoYXQgd29yZHMgYXJlIG1vc3QgaW1wb3J0YW50IHRvIGVhY2ggeWVhci4gVGhpcyBpcyBlYXNpbHkgZG9uZSB1c2luZyBgdGlkeXRleHQ6OmJpbmRfdGZfaWRmYDoKCmBgYHtyfQpjcm9zc3dvcmQgJT4lCiAgY291bnQoeWVhciwgd29yZCwgc29ydCA9IFRSVUUpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBiaW5kX3RmX2lkZih3b3JkLCB5ZWFyLCBuKSAlPiUKICBhcnJhbmdlKGRlc2ModGZfaWRmKSkKYGBgCgpTb21lIGN1cmlvdXMgcmVzdWx0cyBhbHJlYWR5LCBidXQgd2UnbGwgaGF2ZSB0byBkaWcgZGVlcGVyIHRvIGdldCBhbnl0aGluZyBwYXJ0aWN1bGFybHkgaW50ZXJlc3RpbmcuCgpQbG90dGluZyB0aGUgd29yZHMgbW9zdCBpbXBvcnRhbnQgdG8gdGhlIGxhc3QgNSB5ZWFyczoKCmBgYHtyfQpjcm9zc3dvcmQgJT4lCiAgY291bnQoeWVhciwgd29yZCwgc29ydCA9IFRSVUUpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBiaW5kX3RmX2lkZih3b3JkLCB5ZWFyLCBuKSAlPiUKICBhcnJhbmdlKGRlc2ModGZfaWRmKSkgJT4lCiAgbXV0YXRlKHdvcmQgPSBmYWN0b3Iod29yZCwgbGV2ZWxzID0gcmV2KHVuaXF1ZSh3b3JkKSkpKSAlPiUKICBmaWx0ZXIoeWVhciA+PSAyMDEzKSAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICB0b3Bfbig1LCB0Zl9pZGYpICU+JQogIHVuZ3JvdXAoKSAlPiUKICAjIyMjIwogIGdncGxvdChhZXMoeCA9IHdvcmQsIHkgPSB0Zl9pZGYsIGZpbGwgPSB5ZWFyKSkgKwogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsKICBmYWNldF93cmFwKH55ZWFyLCAKICAgICAgICAgICAgIG5jb2wgPSAyLCAKICAgICAgICAgICAgIHNjYWxlcyA9ICJmcmVlIikgKwogIGNvb3JkX2ZsaXAoKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKSArCiAgbGFicyh0aXRsZSA9ICJNb3N0IFllYXItVW5pcXVlIENyb3Nzd29yZCBBbnN3ZXJzLCAyMDEzLTIwMTciLAogICAgICAgc3VidGl0bGUgPSAiQXMgZGV0ZXJtaW5lZCBieSB0Zi1pZGYgc2NvcmVzIGZvciBzaW5nbGUteWVhciBjb3Jwb3JhLiIsCiAgICAgICB4ID0gTlVMTCwKICAgICAgIHkgPSAiVEYtSURGIHNjb3JlIiwKICAgICAgIGNhcHRpb24gPSAiRGF0YSBzb3VyY2U6IFhXb3JkSW5mby5jb20iKSArCiAgdGhlbWVfY3VzdG9tKCkgKwogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMSksCiAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4oMCwgMCwgNSwgMCkpKQoKZ2dzYXZlKGhlcmUoImltYWdlcyIsICJjd190Zl9pZGYucG5nIiksIHdpZHRoID0gOCwgaGVpZ2h0ID0gNikKYGBgCgpUaGVyZSB5b3UgaGF2ZSBpdDogMjAxNyBpcyB0aGUgeWVhciBvZiB0aGUgXCNCQUUuIEluIGZhY3QsIGl0J3MgYmVlbiB1c2VkIGFzIGFuIGFuc3dlciB0aGlzIHllYXIgZm91ciB3aG9sZSB0aW1lcyBzbyBmYXIsIGFuZCBub3Qgb25jZSBiZWZvcmUuIEJ1dCB3aGF0IGRvZXMgaWRlbnRpZnlpbmcgYSB3b3JkIGFzICJpbXBvcnRhbnQiIHRvIGEgcGFydGljdWxhciBkb2N1bWVudCBfYWN0dWFsbHlfIGxvb2sgbGlrZSBpbiB0ZXJtcyBvZiBhcHBlYXJhbmNlIGZyZXF1ZW5jeT8gUGxvdHRpbmcgaG93IG9mdGVuIGVhY2ggb2YgMjAxNydzIGltcG9ydGFudCB3b3JkcyBhcHBlYXJlZCBieSB5ZWFyOgoKYGBge3J9CmNyb3Nzd29yZCAlPiUKICBmaWx0ZXIod29yZCAlaW4lIGMoIkJBRSIsICJMR0JUIiwgIklEUklTRUxCQSIsICJBQkJBQ1kiLCAiRVRTWSIsICJOU0ZXIikpICU+JQogIG11dGF0ZSh3b3JkID0gZmFjdG9yKHdvcmQsIGxldmVscyA9IGMoIkJBRSIsICJMR0JUIiwgIklEUklTRUxCQSIsICJBQkJBQ1kiLCAiRVRTWSIsICJOU0ZXIikpKSAlPiUKICBjb3VudCh5ZWFyLCB3b3JkLCBzb3J0ID0gVFJVRSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IG4sIGZpbGwgPSB3b3JkKSkgKwogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsKICBmYWNldF93cmFwKH53b3JkLCBuY29sID0gMikgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKCkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMjAwNywgMjAxNywgMikpICsKICBsYWJzKHggPSBOVUxMLCAKICAgICAgIHkgPSAiTnVtYmVyIG9mIEFwcGVhcmFuY2VzIiwKICAgICAgIHRpdGxlID0gIkFwcGVhcmFuY2UgRnJlcXVlbmN5IGZvciAyMDE3J3MgXCJJbXBvcnRhbnRcIiBBbnN3ZXJzIiwKICAgICAgIHN1YnRpdGxlID0gIkFzIGRldGVybWluZWQgYnkgdGYtaWRmIHNjb3JlcyBmb3Igc2luZ2xlLXllYXIgY29ycG9yYS4iLAogICAgICAgY2FwdGlvbiA9ICJEYXRhIHNvdXJjZTogWFdvcmRJbmZvLmNvbSIpICsKICB0aGVtZV9jdXN0b20oKQoKZ2dzYXZlKGhlcmUoImltYWdlcyIsICJjd190Zl9pZGZfMjAxNy5wbmciKSwgd2lkdGggPSA4LCBoZWlnaHQgPSA2KQpgYGAKCgpJZiB1bmlxdWUgdG8gYSB5ZWFyJ3MgY3Jvc3N3b3JkIGFuc3dlciBjb3JwdXMsIGl0IGxvb2tzIGxpa2UgYSBnaXZlbiB3b3JkIG9ubHkgbmVlZHMgdG8gYXBwZWFyIGFzIGZldyBhcyAzLTQgdGltZXMgd2l0aGluIHRoYXQgeWVhciAtIHVuc3VycHJpc2luZyB3aGVuIHlvdSBjb25zaWRlciB0aGF0IG1vc3Qgb3RoZXIgd29yZHMgYXJlIHVzZWQgYSBoYW5kZnVsIG9mIHRpbWVzIGFjcm9zcyBhbGwgdGltZSBhdCBiZXN0LiBPdGhlciBvYnNlcnZhdGlvbnM6CgotICAgRVRTWSBhY3R1YWxseSBwZWFrZWQgaW4gMjAxNiAoc28gZmFyKSwgYW5kIHlvdSdsbCBub3RpY2UgdGhhdCBpdCBhcHBlYXJzIGFzIFwjMyBvbiAyMDE2J3MgaW1wb3J0YW50IHdvcmRzIGFzIHdlbGwuIAotICAgU0lSSSBhbHNvIHJhbmtzIGhpZ2ggdHdpY2UgaW4gdGhlIGxhc3QgZml2ZSB5ZWFycy4gSXQgd2FzIGxhdWNoZWQgd2l0aCB0aGUgaVBob25lIDRTIGluIGxhdGUgMjAxMSwgc28gaXQgbWFrZXMgc2Vuc2UgdGhhdCBpdCdkIHRha2UgdW50aWwgMjAxMy8yMDE0IGF0IHRoZSBlYXJsaWVzdCBmb3IgdGhlIHdvcmQgdG8gYmVjb21lIHBvcHVsYXIgZW5vdWdoIHRvIHVzZSBhcyBhIGNyb3Nzd29yZCBjbHVlLgotICAgQSBtYW51YWwgbG9vayBhdCB0aGUgY2x1ZXMgZm9yIElEUklTRUxCQSBjaXRlZCBoaXMgcm9sZXMgaW4gKlRoZSBXaXJlKiAoMjAwMi0yMDA0KSBvbmNlIGFuZCAqTWFuZGVsYTogTG9uZyBXYWxrIHRvIEZyZWVkb20qICgyMDEzKSB0d2ljZS4gSW50ZXJlc3RpbmdseSBlbm91Z2gsIG5vIG1lbnRpb24gb2YgdGhlIGZvdXIgZmlsbXMgaGUncyBiZWVuIGluIHRoaXMgeWVhciAoICpUaG9yOiBSYWduYXJvayogLCAqVGhlIE1vdW50YWluIEJldHdlZW4gVXMqLCAqVGhlIERhcmsgVG93ZXIqIGFuZCAqTW9sbHkncyBHYW1lKiApLgotICAgV2hhdCdzIHRoZSBkZWFsIHdpdGggQ0NDQ0MgYW5kIFVVVVVVIGluIDIwMTM/IEEgY291bnQgYnkgZGF0ZSBzaG93cyB0aGF0IGJvdGggYW5zd2VycyBhY3R1YWxseSBhcHBlYXJlZCB0aHJlZSBkaXN0aW5jdCB0aW1lcywgX2FsbCBpbiB0aGUgc2FtZSBwdXp6bGVfLiBJIGxvb2tlZCB0aGUgcHV6emxlIHVwIG91dCBvZiBjdXJpb3NpdHkgYW5kIHdhcyBmYWNlZCB3aXRoIHRoaXMgbW9uc3RlciwgY291cnRlc3kgb2YgSmVmZiBDaGVuOgoKIVtdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9qdGFud2svbnl0Y3Jvc3N3b3JkL21hc3Rlci9pbWFnZXMvY2NjY2MuUE5HP3Jhdz10cnVlKQoKV2hhdCBhIGJlYXV0LiAoQWxzbyBub3RlIHRoYXQgVFRUVFQsIHVubGlrZSB0aGUgb3RoZXIgdHdvIGxldHRlcnMsIG9ubHkgYXBwZWFyZWQgdHdpY2UgYW5kIGRpZCBub3QgcmFuayBhcyBpbXBvcnRhbnQgdG8gMjAxMydzIGNvcnB1cy4pCgojIyMgSG93IGxleGljYWxseSBkaXZlcnNlIGFyZSBjcm9zc3dvcmQgcHV6emxlcz8KClRoZSBsYXN0IHRoaW5nIEkgd2FudCB0byBsb29rIGF0IGlzIGxleGljYWwgZGl2ZXJzaXR5LiBIb3cgcmljaCBhbmQgdmFyaWVkIGFyZSB0aGUgYW5zd2VycyB1c2VkIGluIHRoZSBwdXp6bGVzPyBUaGUgbW9zdCBjb21tb24gd2F5IHRvIG1lYXN1cmUgdGhpcyBpcyB0aGUgKlR5cGUtVG9rZW4gUmF0aW8qIC0gdGhlIHJhdGlvIG9mIHVuaXF1ZSB3b3JkcyB0byB0b3RhbCB3b3JkcyBpbiBhIGNvcnB1cy4gVGhlIGlkZWEgaXMgdGhpczogaWYgdGhlcmUgYXJlIGZld2VyIHJlcGVhdGVkIHdvcmRzLCB0aGVuIFRUUiBpbmNyZWFzZXMgYW5kIHZpY2UgdmVyc2EuIFRoZXJlJ3MgYSBncmVhdCBnZW5lcmFsIGV4cGxhaW5lciBvbiBbVFRSXShodHRwczovL3d3dy5zbHRpbmZvLmNvbS90eXBlLXRva2VuLXJhdGlvLykgaGVyZS4KCkFzIGl0J3MgaGlnaGx5IHVubGlrZWx5IHRoYXQgYW5zd2VycyByZXBlYXQgd2l0aGluIGEgc2luZ2xlIHB1enpsZSwgSSd2ZSBhZ2dyZWdhdGVkIGFsbCB0aGUgYW5zd2VycyBmb3IgZWFjaCB5ZWFyJ3Mgd29ydGggb2YgY3Jvc3N3b3JkIHB1enpsZXMuIENhbGN1bGF0aW5nIHRoZSBUVFIgYnkgeWVhciBhbmQgcGxvdHRpbmcgaXQ6CgpgYGB7cn0KY3Jvc3N3b3JkICU+JQogIGZpbHRlcih5ZWFyICE9ICIyMDE3IikgJT4lCiAgcGx5cjo6ZGRwbHkofnllYXIsIHN1bW1hcmlzZSwgZGlzdGluY3Rfd29yZHMgPSBuX2Rpc3RpbmN0KHdvcmQpLCB0b3RhbF93b3JkcyA9IGxlbmd0aCh3b3JkKSkgJT4lCiAgbXV0YXRlKFRUUiA9IGRpc3RpbmN0X3dvcmRzIC8gdG90YWxfd29yZHMpICU+JQogIGdncGxvdChhZXMoeCA9IHllYXIsIHkgPSBUVFIpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChjb2xvciA9ICJncmV5IiwgCiAgICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwKICAgICAgICAgICAgICBzaXplID0gMC41LAogICAgICAgICAgICAgIG1ldGhvZCA9ICJsbSIsIAogICAgICAgICAgICAgIHNlID0gRkFMU0UpICsKICBsYWJzKHggPSAiWWVhciIsCiAgICAgICB5ID0gIlR5cGUtVG9rZW4gUmF0aW8iLAogICAgICAgdGl0bGUgPSAiTGV4aWNhbCBEaXZlcnNpdHkgb2YgTllUaW1lcyBDcm9zc3dvcmRzIGJ5IFllYXIiLAogICAgICAgc3VidGl0bGUgPSAiVXNpbmcgcHV6emxlcyBmcm9tIHRoZSBTaG9ydHogRXJhICgxOTk0LTIwMTcpLiBMZXhpY2FsIGRpdmVyc2l0eSBpcyBtZWFzdXJlZCBieSBUeXBlIFRva2VuIFJhdGlvIFxuKFRUUiksIHdoZXJlIFRUUiA9IChudW1iZXIgb2YgdW5pcXVlIHdvcmRzKSAvICh0b3RhbCBudW1iZXIgb2Ygd29yZHMpIiwKICAgICAgIGNhcHRpb24gPSAiRGF0YSBzb3VyY2U6IFhXb3JkSW5mby5jb20iKSArCiAgdGhlbWVfY3VzdG9tKCkKCmdnc2F2ZShoZXJlKCJpbWFnZXMiLCAiY3dfdHRyLnBuZyIpLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYpCmBgYAoKVGhhdCdzIGEgc3RyaWN0ZXIgdXB3YXJkIHRyZW5kIHRoYW4gSSBpbWFnaW5lZC4gVGhpcyB0ZWxscyB1cyBhIGZldyB0aGluZ3M6CgotICAgQW4gVFRSIGluIHRoZSByYW5nZSBvZiAwLjUzLTAuNTggdGVsbHMgdXMgdGhhdCB0aGVyZSBhcmUgcm91Z2hseSBoYWxmIGFzIG1hbnkgdW5pcXVlIGFuc3dlcnMgYXMgdG90YWwgYW5zd2VycyB1c2VkIHdpdGhpbiBlYWNoIHllYXIuCi0gICBUaGUgVFRSIGhhcyBncm93biBieSBhYm91dCAwLjA1IGJldHdlZW4gMTk5NCBhbmQgMjAxNiAoSSBvbWl0dGVkIDIwMTcgZHVlIHRvIHRoZSBpbmNvbXBsZXRlIHllYXIpLiBBIHB1enpsZSBpbiAyMDE2IGZlYXR1cmVzIGFib3V0IDUlIG1vcmUgdW5pcXVlIGFuc3dlcnMgdGhhbiBhIHB1enpsZSBpbiAxOTk0IHdvdWxkIGhhdmUuCgpBcyBiZWZvcmUsIHRoZSBuYXR1cmFsIG5leHQgcXVlc3Rpb246IGhvdyBkb2VzIGxleGljYWwgZGl2ZXJzaXR5IHZhcnkgYnkgZGF5IG9mIHRoZSB3ZWVrPwoKYGBge3J9CmNyb3Nzd29yZCAlPiUKICBmaWx0ZXIoeWVhciAhPSAiMjAxNyIpICU+JQogIGZpbHRlcihjd2RheSAhPSAiTkEiKSAlPiUKICBtdXRhdGUoY3dkYXkgPSBmYWN0b3IoY3dkYXksIGxldmVscyA9IGMoIk1vbmRheSIsICJUdWVzZGF5IiwgIldlZG5lc2RheSIsICJUaHVyc2RheSIsICJGcmlkYXkiLCAiU2F0dXJkYXkiLCAiU3VuZGF5IikpKSAlPiUKICBwbHlyOjpkZHBseShwbHlyOjouKHllYXIsIGN3ZGF5KSwgc3VtbWFyaXNlLCBkaXN0aW5jdF93b3JkcyA9IG5fZGlzdGluY3Qod29yZCksIHRvdGFsX3dvcmRzID0gbGVuZ3RoKHdvcmQpKSAlPiUKICBtdXRhdGUoVFRSID0gZGlzdGluY3Rfd29yZHMgLyB0b3RhbF93b3JkcykgJT4lCiAgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IFRUUikpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGN3ZGF5KSwKICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzKGRpc2NyZXRlID0gVFJVRSwgb3B0aW9uID0gInZpcmlkaXMiKSArCiAgZ2VvbV9zbW9vdGgoY29sb3IgPSAiZ3JleSIsCiAgICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwKICAgICAgICAgICAgICBzaXplID0gMC41LAogICAgICAgICAgICAgIG1ldGhvZCA9ICdsbScsIAogICAgICAgICAgICAgIHNlID0gRkFMU0UpICsKICBmYWNldF93cmFwKH5jd2RheSwgbnJvdyA9IDEpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDE5OTYsIDIwMTYsIDQpKSArCiAgbGFicyh4ID0gIlllYXIiLAogICAgICAgeSA9ICJUeXBlLVRva2VuIFJhdGlvIiwKICAgICAgIHRpdGxlID0gIkxleGljYWwgRGl2ZXJzaXR5IG9mIE5ZVGltZXMgQ3Jvc3N3b3JkcyBieSBEYXkgb2YgdGhlIFdlZWsiLAogICAgICAgc3VidGl0bGUgPSAiVXNpbmcgcHV6emxlcyBmcm9tIHRoZSBTaG9ydHogRXJhICgxOTk0LTIwMTcpLiBMZXhpY2FsIGRpdmVyc2l0eSBpcyBtZWFzdXJlZCBieSBUeXBlIFRva2VuIFJhdGlvIFxuKFRUUiksIHdoZXJlIFRUUiA9IChudW1iZXIgb2YgdW5pcXVlIHdvcmRzKSAvICh0b3RhbCBudW1iZXIgb2Ygd29yZHMpIiwKICAgICAgIGNhcHRpb24gPSAiRGF0YSBzb3VyY2U6IFhXb3JkSW5mby5jb20iKSArCiAgdGhlbWVfY3VzdG9tKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNjAsIGhqdXN0ID0gMSkpCgpnZ3NhdmUoaGVyZSgiaW1hZ2VzIiwgImN3X3R0cl9ieWRheS5wbmciKSwgd2lkdGggPSA4LCBoZWlnaHQgPSA2KQpgYGAKCi0gICBWYXJpYXRpb24gaW4gVFRSIGJldHdlZW4gZGF5cyBpcyAqd2F5KiBncmVhdGVyIHRoYW4gYmV0d2VlbiB5ZWFycy4gU2F0dXJkYXlzIGhhdmUgYWxtb3N0IDE1JSBtb3JlIHVuaXF1ZSBhbnN3ZXJzIHBlciB0b3RhbCBhbnN3ZXIgY291bnQgdGhhbiBNb25kYXlzLgotICAgQWx3YXlzIGludGVyZXN0aW5nIHRvIG5vdGUgd2hlcmUgU3VuZGF5IGZhbGxzIG9uIHRoZSBzcGVjdHJ1bSAtIGluIHRoaXMgY2FzZSwgbXVjaCBjbG9zZXIgaW4gbGV4aWNhbCBkaXZlcnNpdHkgdG8gTW9uZGF5cy9UdWVzZGF5cyB0aGFuIHRoZSBtaWRkbGUgb2YgdGhlIHdlZWsuCi0gICBOb3RlIGhvdyB0aGUgVFRScyBoYXZlIGp1bXBlZCB0byB0aGUgMC44MC0wLjk1IHJhbmdlIHdoZW4gZGlzYWdncmVnYXRpbmcgYnkgZGF5LCBjb21wYXJlZCB0byAwLjUtMC42IHdoZW4gcGxvdHRpbmcgYnkgeWVhci4gVGhhdCdzICoqc3VwZXIqKiBpbnRlcmVzdGluZy4gT25lIHBvc3NpYmxlIGludGVycHJldGF0aW9uIGlzIHRoYXQgcmVwZWF0ZWQgd29yZHMgdGVuZCB0byBiZSByZXBlYXRlZCAqYWNyb3NzKiBkYXlzIHJhdGhlciB0aGFuIHdpdGhpbiB0aGVtLiBCdXQgdGhhdCdzIGFuIGV4cGxvcmF0aW9uIGZvciBhbm90aGVyIHRpbWUuCgojIEZ1cnRoZXIgU3RlcHMKClRoZXJlIHdlcmUgbG90cyBvZiBpZGVhcyB0aGF0IEkgcGxheWVkIGFyb3VuZCB3aXRoIHRoYXQgd2VyZSBlaXRoZXIgbGVzcyBjb21wZWxsaW5nLCBkaWZmaWN1bHQgdG8gZXhlY3V0ZSBvciBvdXRzaWRlIHRoZSBzY29wZSBvZiB3aGF0IEkgd2FudGVkIHRvIGRvIGhlcmUgdG9kYXkuIEkgd2VsY29tZSB5b3UgdG8gdGFrZSBhIHN0YWIgYXQgdGhlbS4gSGVyZSBhcmUgYSBmZXc6CgotICAgV2hhdCBmaXJzdCBuYW1lcyBhcHBlYXIgbW9zdCBvZnRlbj8gRG8gbWFsZSBhbmQgZmVtYWxlIG5hbWVzIGFwcGVhciB3aXRoIHRoZSBzYW1lIGZyZXF1ZW5jeT8KLSAgIEFzIGFib3ZlLCBidXQgd2l0aCBjaXRpZXMgYW5kIGNvbnRpbmVudGFsIHJlcHJlc2VudGF0aW9uLgotICAgV2hhdCBsYW5ndWFnZXMgYXJlIHJlcHJlc2VudGVkIHRoZSBtb3N0PyBNYW55IGxvYW53b3JkcyBvciBzdHJhaWdodC11cCBmb3JlaWduIGxhbmd1YWdlIHdvcmRzIGV4aXN0IGluIHRoZSBjcm9zc3dvcmQgYnV0IGFyZSB2ZXJ5IGRpZmZpY3VsdCB0byBkZXRlY3QgY29tcHV0YXRpb25hbGx5IG91dCBvZiB0aGUgY29udGV4dCBvZiBhIHNlbnRlbmNlLgotICAgV2hvIGFyZSB0aGUgbW9zdCBwcm9saWZpYyBjcm9zc3dvcmQgc3VibWl0dGVycywgYW5kIGRvIHRoZXkgaGF2ZSBkaXN0aW5jdCBsZXhpY2FsIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhlbT8KLSAgIEFueSBhbmFseXNpcyBpbnZvbHZpbmcgdGhlIHRleHQgb2YgdGhlIGNyb3Nzd29yZCAqY2x1ZXMqIGFuZCBub3QganVzdCB0aGUgYW5zd2Vycy4KLSAgIEFueSBhbmFseXNpcyBpbnZvbHZpbmcgZGF0YSBzdXJyb3VuZGluZyB1c2VyIGJlaGF2aW9ycyBwb3NzaWJsZSBvbiBOWVRpbWVzIChlLmcuIHNvbHZlIHRpbWVzLCBjaGVja2luZyBhbnN3ZXJzLCBtb2JpbGUgdnMuIGJyb3dzZXIgYWN0aXZpdHkpCg==