0. INTRODUCTION
With the rapid increase in digitization over the past 20 years, the field of text mining has quickly moved to the forefront of data science as a method for better understanding patterns and trends in society. Text mining is often the first step in developing intricate methods for more advanced processes such as social network analysis or natural language processing. For models to learn, they must experience everything (or as much as possible) that is and is not what they are attempting to predict or mimic. Traditionally, this has meant incredibly large volumes of text have been required to feed these learning demands.
A challenge to this process is how human information tendencies are changing. While we continue to create more and more data, it’s presenting in smaller and smaller chunks. Short-form text is quickly becoming the primary means by which people consume daily, practical, and time-sensitive information. From news, to social media, to research abstracts, texts of 200 words or fewer are gaining popularity for carrying the the critical messages in our daily lives. As these shorter formats become increasingly important for analyzing and understanding social trends, it’s important for data science tools to adapt as the information environment continuously evolves (Jipeng et al., 2019).
The context for this project is provided by data from the website Towards Data Science (TDS), which I’ve used often to augment my studies on learning analytics. TDS is a Medium web publication where authors share concepts, ideas, and coding tips through self-published articles. This data experiment will explore what short-form text modeling can reveal about the changes in research focus over the last few years years as charted through TDS articles published from 2018-2021.
This research project aims to answer the following questions:
Can short texts provide enough context to adequately inform topic modeling?
Can short text metadata be used to augment the accuracy of these topic models?
Can short texts provide enough fidelity to measure topic prevalence over time?
Data scientists, students, or any professionals researching this field could benefit by understanding how research/publication trends are linked to shifts in technology. Understanding these shifts could influence how quickly people adopt or advance new technologies in the data science field. Additionally, this information could be useful to understand technologies ripe for study in higher education leading to how students select focus areas, majors, or certificate programs.
2. WRANGLE
For this case study, the data will be parsed into tokens for initial exploration to identify potential trends and context over time. Three topic modeling techniques will be employed, each with their own specific data format requirements. To meet these, the data will be tidied and stemmed and the stop words will be removed to better focus on topic-specific terminology.
2.1 Import Data
The raw data was downloaded into a comma separated values (.csv) file that will be imported into RStudio:
tds_raw <- read_csv("data/towards_data_science.csv")
With the data frame created, the data set will be further manipulated into a format fit for exploratory analysis. Wrangling will consist of the following steps:
Combine title and tagline columns to create a single ‘text’ variable.
Mutate the date column into separate ‘year’ and ‘month’ variables to enable time-based exploration.
Create a corpus through the tidytext package to enable additional text mining tools.
Stem the tidytext corpus and remove stop words.
Tokenize tidytext corpus into unigrams, bigrams, and trigrams to enable term frequency analysis.
Create a document term matrix (DTM) to explore the LDA modeling technique.
2.2 Create Single Text Variable
The current data frame contains the target text in two separate variables, title and tagline. As the pair of variables represent a single document (article in this case), combining the text into a single character variable will enable the models to focus on just one column, simplifying the work of the models.
tds_raw$text <- paste(tds_raw$title,tds_raw$tagline,sep=" ")
# Rename ID column
colnames(tds_raw)[1] <- "doc_id"
2.3 Create Year and Month Variables
To explore topic trending over time, the date variable is being separated into year and month variables.
tds_dates <- tds_raw %>%
mutate(date = mdy(date)) %>%
mutate_at(vars(date), funs(year, month))
2.4 Create tidytext Corpus
This process decomposes the long text string from the text variable into single terms, while maintaining their tie to the source document (doc_id) and its metadata (date, year, month).
tds_tidy <- tds_dates %>%
unnest_tokens(output = word, input = text) %>%
anti_join(stop_words, by = "word")
# Remove numbers
tds_tidy <- tds_tidy[-grep("\\b\\d+\\b", tds_tidy$word),]
tidy_top_tokens <- tds_tidy %>%
count(word, sort = TRUE) %>%
top_n(10)
## Selecting by n
tidy_top_tokens
## # A tibble: 10 × 2
## word n
## <chr> <int>
## 1 data 13884
## 2 learning 7084
## 3 python 6457
## 4 machine 4155
## 5 science 3930
## 6 model 2498
## 7 ai 2151
## 8 analysis 2096
## 9 guide 2096
## 10 deep 1986
The above code created a tidy version of the corpus at the single word (unigram) level while also removing stop words and numbers.
2.5 Stemming
Stemming reduces the feature size of a corpus by transforming terms to their base stem. Stemming reduces the chances of redundancy in terms and phrases as the various topic modeling techniques are explored.
tds_tidy <- tds_tidy %>%
mutate(word = wordStem(word))
2.6 Cast a Document Term Matrix
The LDA model requires the text be presented in the form of a tidy DTM, where each term occupies a single cell according to a unique and controlling variable. In this case, the title will act as that unique identifier.
tidy_tds_DTM <- tds_tidy %>%
count(title, word) %>%
cast_dtm(title, word, n)
tidy_tds_DTM
## <<DocumentTermMatrix (documents: 30128, terms: 14604)>>
## Non-/sparse entries: 294029/439695283
## Sparsity : 100%
## Maximal term length: 34
## Weighting : term frequency (tf)
2.7 Tokenization
Lastly, wrangling will end with the tokenization of the original data to enable further term frequency analysis at the bigram (word pair) and trigram (3-word set) levels. For these iterations, stop word removal and stemming has been incorporated:
tds_bigrams <- tds_dates %>%
unnest_tokens(output = bigram, input = text, token = "ngrams", n = 2)
tds_bigrams <- tds_bigrams %>%
separate(bigram, into = c("word1", "word2"), sep = " ") %>%
filter(!word1 %in% stop_words$word) %>%
filter(!word2 %in% stop_words$word) %>%
mutate(word1 = wordStem(word1)) %>%
mutate(word2 = wordStem(word2)) %>%
unite(bigram, c(word1, word2), sep = " ")
bigram_top_tokens <- tds_bigrams %>%
count(bigram, sort = TRUE) %>%
top_n(10)
bigram_top_tokens
## # A tibble: 10 × 2
## bigram n
## <chr> <int>
## 1 machin learn 3905
## 2 data scienc 3727
## 3 data scientist 1654
## 4 deep learn 1419
## 5 neural network 1366
## 6 learn model 765
## 7 time seri 678
## 8 data analysi 583
## 9 covid 19 560
## 10 reinforc learn 458
tds_trigrams <- tds_dates %>%
unnest_tokens(output = trigram, input = text, token = "ngrams", n = 3)
tds_trigrams <- tds_trigrams %>%
separate(trigram, into = c("word1", "word2", "word3"), sep = " ") %>%
filter(!word1 %in% stop_words$word) %>%
filter(!word2 %in% stop_words$word) %>%
filter(!word3 %in% stop_words$word) %>%
mutate(word1 = wordStem(word1)) %>%
mutate(word2 = wordStem(word2)) %>%
mutate(word3 = wordStem(word3)) %>%
unite(trigram, c(word1, word2, word3), sep = " ")
trigram_top_tokens <- tds_trigrams %>%
count(trigram, sort = TRUE) %>%
top_n(10)
trigram_top_tokens
## # A tibble: 10 × 2
## trigram n
## <chr> <int>
## 1 machin learn model 602
## 2 data scienc project 341
## 3 natur languag process 289
## 4 convolut neural network 233
## 5 exploratori data analysi 230
## 6 machin learn algorithm 201
## 7 deep learn model 138
## 8 time seri forecast 134
## 9 machin learn project 133
## 10 learn data scienc 132
3. EXPLORATORY ANALYSIS
3.1 Published Article Counts
tds_dates %>%
ggplot(aes(x = date, color = factor(month))) +
geom_bar(show.legend = FALSE) +
labs(y = "Date",
x = "Article Counts",
title = "Towards Data Science Articles",
subtitle = "Published from 2018 - 2021")

This visual depicts the 30k article spread over the past four years. TDS had a great year in 2020 with over 70 articles published monthly in the mid-year period. Any chance that was due to scientists being cooped up during the heart of the COVID-19 pandemic? Writing is a great way to pass the time! The next couple of sections will explore word frequencies to attempt to identify patterns within the most use words or word combinations.
3.2 Word Counts by Year
tds_tidy %>%
group_by(year) %>%
count(word, sort = TRUE) %>%
top_n(10) %>%
ungroup %>%
mutate(word = reorder_within(word, n, year)) %>%
ggplot(aes(x = word, y = n, fill = word)) +
geom_col(show.legend = FALSE) +
facet_wrap(~ year, scales = "free_y") +
coord_flip() +
scale_x_reordered() +
scale_y_continuous(expand = c(0,0)) +
labs(y = "Count",
x = "Unique words",
title = "Most frequent words found in TDS article titles & taglines",
subtitle = "Stop words removed from the list")

When diagraming the top ten unigrams by year, many terms are repeated. Since a data science blogging site is the focus for this project, it’s no surprise that words such as data, science, machine, learning, etc. would appear at the top. The chart below indicates how this changed when the graph was expanded to include the top 20 terms:
tds_tidy %>%
group_by(year) %>%
count(word, sort = TRUE) %>%
top_n(20) %>%
ungroup %>%
mutate(word = reorder_within(word, n, year)) %>%
ggplot(aes(x = word, y = n, fill = word)) +
geom_col(show.legend = FALSE) +
facet_wrap(~ year, scales = "free_y") +
coord_flip() +
scale_x_reordered() +
scale_y_continuous(expand = c(0,0)) +
labs(y = "Count",
x = "Unique words",
title = "Most frequent words found in TDS article titles & taglines",
subtitle = "Stop words removed from the list")

This second attempt does begin to reveal some unique terms by year. Another way to achieve this is by adding words common to all years to the list of stop words. In the end, I decided not to focus too much energy on individual terms as in this community, many of the key topics are described by multiple terms, such as ‘machine learning,’ as opposed to treating those words as separate and distinct entities. To that end, I repeated the above visuals, but for multi-word groupings.
3.3 Bigram Counts
tds_bigrams %>%
group_by(year) %>%
count(bigram, sort = TRUE) %>%
top_n(20) %>%
ungroup %>%
mutate(bigram = reorder_within(bigram, n, year)) %>%
ggplot(aes(x = bigram, y = n, fill = bigram)) +
geom_col(show.legend = FALSE) +
facet_wrap(~ year, scales = "free_y") +
coord_flip() +
scale_x_reordered() +
scale_y_continuous(expand = c(0,0)) +
labs(y = "Count",
x = "Unique Bigrams",
title = "Most frequent bigrams found in article titles & taglines",
subtitle = "Stop words removed from the list")

Expanding to 2-word phrases reveals unique topics in each year. Some interesting examples are random forest (2018), object detect (2019), covid 19 (2020), and python code (2021).
3.4 Trigram Counts
tds_trigrams %>%
group_by(year) %>%
count(trigram, sort = TRUE) %>%
top_n(10) %>%
ungroup %>%
mutate(trigram = reorder_within(trigram, n, year)) %>%
ggplot(aes(x = trigram, y = n, fill = trigram)) +
geom_col(show.legend = FALSE) +
facet_wrap(~ year, scales = "free_y") +
coord_flip() +
scale_x_reordered() +
scale_y_continuous(expand = c(0,0)) +
labs(y = "Count",
x = "Unique Trigrams",
title = "Most frequent trigrams found in article titles & taglines",
subtitle = "Stop words removed from the list")

Trigrams repeat quite a bit in the top ten, though you start seeing more complete ideas emerge about specific activities. Examples include data science job and time seri data.
3.5 Exploratory Analysis Insights
Word frequency analysis reveals that unique words and phrases do emerge from year to year. This implies that research topics are changing throughout the corpus of TDS articles between 2018 and 2021. Some other interesting features of this data set include:
single words do little to distinguish between topics and/or years
trigram phrases become repetitive beyond the top ten, so bigram phrases looks to be the optimal focus for short text topic modeling
4. MODEL
To address the potential for topic generation in short-form text, I will compare various qualitative model results to determine the differences, if any, in how they identify unique topics using shorter data observations. This analysis will examine three models:
Latent Dirichlet Allocation (LDA). LDA works under the premise that every document contains a mixture of topics, and every topic is composed of a mixture of words.
Structural Topic Model (STM). STM employs metadata to improve the assignment of words to topics in a corpus and that can be used to examine relationships between covariates and documents.
Biterm Topic Model (BTM). BTM was designed specifically for a short-format corpus and identifies topics by explicitly modeling word-word co-occurrences in a specified window of text.
4.1 Determining K
Each model is predicated on having a relatively optimum approximation for K, or the number of potential topics to be identified. If K is too small, the corpus is divided into a few very generic topics. If K is too large, the collection is broken into too many topics in which they either overlap or are hardly indecipherable. Before the models can be applied, a common K value should be determined so the results can be more consistently compared. A challenge to this process is that each model maintains distinct methods for determining K based on how the algorithm treats the text. As LDA and STM were designed for larger bodies of text, it will be interesting to see how the model treats the short-form data in this experiment. If a consistent K-value cannot be determined explicitly, then the alternative will be to conduct a trial-and-error approach to find a value that can be applied to all three models. As our main goal is to compare results between them, the alternative K solution will meet the intent of the project. How an optimal K should be selected depends on various factors that are unique to each type of topic model.
FindTopicsNumber() Function in the LDA Model
For the LDA model, four metrics were extracted, then plotted to visualize the maximum or minimum K value of each metric:
k_metrics <- FindTopicsNumber(
tidy_tds_DTM,
topics = seq(5, 50, by = 5),
metrics = c("Griffiths2004", "CaoJuan2009", "Arun2010", "Deveaud2014"),
method = "Gibbs",
control = list(seed = 77),
mc.cores = NA,
return_models = FALSE,
verbose = FALSE,
libpath = NULL
)
FindTopicsNumber_plot(k_metrics)

The key is to identify the visible bend, or inflection point, on each of the lines. This is where they transition from quickly increasing/decreasing to a more flat trajectory. In each of the lines, that inflection point is between 20 and 30 topics. The one metric that has a single and easily recognizable value is the CaoJuan2009 line, which yields a max value of 20 topics.
searchK() Function in STM
The stm package has a useful function called searchK which requires a specific range of values for K and outputs multiple goodness-of-fit measures:
findingk <- searchK(docs,
vocab,
K = c(5:30),
data = meta,
verbose=FALSE)
plot(findingk)

Based on the feedback from the LDA model, the range was tested between 5 and 30 topics. Similar to the LDA results, the curves hit their inflection point between 25 and 30 topics. (Note: this trial required ~ 8 hours to run on a fairly high end Macbook Pro. Your mileage may vary.)
Determining K for BTM
Unlike the previous two models, there is no function built into the BTM package in R for estimating K. As this is a comparative study, using similar K values for each model should provide consistent data for this purpose. Therefore, the BTM model was executed assuming an optimum K value between 20 and 30 topics. The models will use these values while also adding a lower value of 10 to verify what happens if a K value is selected that is too low.
4.2 Latent Dirichlet Allocation (LDA) Model
LDA is a mathematical method for estimating the mixture of words that is associated with each topic, while also determining the mixture of topics that describes each document (Silge & Robinson, 2017).
tds_lda <- LDA(tidy_tds_DTM,
k = 20,
control = list(seed = 588))
terms(tds_lda, 5)
## Topic 1 Topic 2 Topic 3 Topic 4 Topic 5 Topic 6 Topic 7
## [1,] "learn" "python" "data" "learn" "data" "data" "learn"
## [2,] "model" "function" "scienc" "data" "scienc" "python" "data"
## [3,] "data" "step" "sql" "tip" "python" "build" "featur"
## [4,] "understand" "learn" "top" "python" "learn" "neural" "python"
## [5,] "ai" "analysi" "databas" "model" "step" "network" "machin"
## Topic 8 Topic 9 Topic 10 Topic 11 Topic 12 Topic 13 Topic 14
## [1,] "data" "learn" "model" "code" "data" "learn" "model"
## [2,] "python" "deep" "data" "python" "scientist" "machin" "learn"
## [3,] "scienc" "data" "network" "perform" "learn" "model" "machin"
## [4,] "model" "intellig" "detect" "run" "it’" "ai" "data"
## [5,] "build" "artifici" "python" "chang" "machin" "data" "ai"
## Topic 15 Topic 16 Topic 17 Topic 18 Topic 19 Topic 20
## [1,] "introduct" "data" "data" "panda" "data" "build"
## [2,] "learn" "scienc" "learn" "guid" "scienc" "time"
## [3,] "machin" "time" "model" "simpl" "project" "imag"
## [4,] "data" "build" "network" "python" "creat" "analysi"
## [5,] "model" "seri" "machin" "data" "start" "detect"
For K = 20, the topics looks to be relatively similar across the board, though their are a couple that stand out as unique. In this format, however, recognizing those topics is not easy. The faceted plot below provides a much more informative visual:
top_terms_lda <- tds_lda %>%
group_by(topic) %>%
slice_max(beta, n = 5, with_ties = FALSE) %>%
ungroup() %>%
arrange(topic, -beta)
top_terms_lda %>%
mutate(term = reorder_within(term, beta, topic)) %>%
group_by(topic, term) %>%
arrange(desc(beta)) %>%
ungroup() %>%
ggplot(aes(beta, term, fill = as.factor(topic))) +
geom_col(show.legend = FALSE) +
scale_y_reordered() +
labs(title = "Top 5 terms in each LDA topic",
x = expression(beta), y = NULL) +
facet_wrap(~ topic, ncol = 4, scales = "free")

I’ve highlighted three topics (11, 18, and 20) whose top five terms were outside the range of the majority of the other topics.
Topic 11 references running or changing Python code.
Topic 18 mentions a specific library in Python, called Pandas.
Topic 20 speaks to building models for image analysis/detection.
While three topics presented themselves as unique, that is a relatively small number within 20 topics. To explore how the results change based on the chosen number of topics, I ran the same model using K=10 and K=30.
For K=10, I had difficulty distinguishing between any of the topics. Very generic with repeated terms of data, machin, learn, model, and scienc, which are what one would expect to see on a website that publihed data science articles.

Using K=30 yields results similar to the image from K=20. While topic 3 is unique compared to the previous run, topics 18 and 20 look almost identical to their counterparts from the earlier trial.

4.3 Structural Topic Model (STM)
The stm package in R requires the documents, meta data, and “vocab”—or total list of words described in the documents— to be stored in separate objects (see code below). The first line of code eliminates both extremely common terms and extremely rare terms, as is common practice in topic modeling, since such terms make word-topic assignment much more difficult (Bail, 2019).
temp <- textProcessor(tds_raw$text,
metadata = tds_raw,
lowercase=TRUE,
removestopwords=TRUE,
removenumbers=TRUE,
removepunctuation=TRUE,
wordLengths=c(3,Inf),
stem=TRUE,
onlycharacter= FALSE,
striphtml=TRUE,
customstopwords=NULL)
docs <- temp$documents
meta <- temp$meta
vocab <- temp$vocab
tds_stm <- stm(documents=docs,
data=meta,
vocab=vocab,
prevalence =~ date,
K=20,
max.em.its=25,
verbose = FALSE,
gamma.prior='L1')

This first iteration of the STM model (K=20) shows a much more diverse range of terms than LDA. For this data set, the only additional information available for distinguishing topics is the publication date of each article. This linear format, however, is still difficult to capture how each topic differs from it’s neighbors. A more visual and useful method is enabled by the toLDAvis function.
toLDAvis(mod = tds_stm, docs = docs)

This tools explores how each topic relates to the others spatially. In a perfect environment, the model would have identified 20 topics separate and distinct from each other. The diagram would’ve shown 20 circles with no overlap. While it’s doubtful a model could predict that level of precision, this diagram shows that with K=20, there is relatively good topic separation. There are just four areas where topics overlap, meaning they share a larger proportion of similar terms.
Topic 2 is highlighted as it is large and maintains good spacing from its nearest neighbors. The size indicates the number of terms associated with this topic. In this case, the key term is ‘data,’ which is not surprising, but its companion terms are what separates it from its neighbors. The lone bubble indicates that this topic is distinct in its term composition. Combining the top 10 terms reveals the topic describes articles written about the skills required to become a data scientist. For comparison across topic values, this model was repeated for K=10 and K=30:

These views add to the inference that K=20 is pretty close to optimum. For K=10, eight of the topics overlap with a neighbor. Likewise for K=30, there are several groupings of overlapping topics.
4.4 Biterm Topic Model (BTM)
The Biterm Topic Model (BTM) is a word co-occurrence based topic model that learns topics by modeling word-word co-occurrences patterns (e.g., biterms) (Wijffels, 2021).
A biterm consists of two words co-occurring in the same context, for example, in the same short text window. This window is described by the parameters skipgram and width.
Skipgram defines the number of words to include in the biterm search space, while width defines how many words on average exist in a single document. For this project, a skipgram value of 5 was used and the width was kept at the default value of 15.
BTM models the biterm occurrences across a complete corpus (unlike LDA models which model the word occurrences in a single document).
Based on the first two iterations, the BTM trial focused on training a model to identify 20 topics:
# Tag parts of speech
anno <- udpipe(tds_raw, "english", trace = 10)
biterms <- as.data.table(anno)
biterms <- biterms[, cooccurrence(x = lemma,
relevant = upos %in% c("NOUN",
"ADJ",
"PROPN"),
skipgram = 5),
by = list(doc_id)]
# Build BTM
set.seed(588)
traindata <- subset(anno, upos %in% c("NOUN", "ADJ", "PROPN"))
traindata <- traindata[, c("doc_id", "lemma")]
model <- BTM(traindata, k = 20,
beta = 0.01,
iter = 500,
biterms = biterms,
trace = 100)
# Plot Model Results (do not run when knitting)
#library(ggraph)
#plot(model,
# top_n = 10,
# title = "BTM model",
# subtitle = "K = 20, 500 Training Iterations",
# labels = c("0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
# "10", "11", "12", "13", "14", "15", "16", "17",
# "18", "19"))

5. COMMUNICATE
5.1 Topics Discoverability in Short-form Text
LDA. The LDA model did little to distinguish between topics beyond a generic summary of what one could expect to read in any article on data science. These results qualitatively confirm the inability of the model to consistently recognize unique topics in short-form text. Treating an article’s title and tagline as a complete “document” gives the model too little data to inform latent topic discovery.
STM. The STM model distinguished between topics much more effectively than the LDA model. These results add additional reinforcement that the optimum number of terms is around 20 and adding metadata increases the likelihood of distinguishing between unique topics.
BTM.
|
Topic 0
|
Topic 1
|
Topic 2
|
Topic 3
|
Topic 4
|
Topic 5
|
Topic 6
|
Topic 7
|
Topic 8
|
Topic 9
|
Topic 10
|
Topic 11
|
Topic 12
|
Topic 13
|
Topic 14
|
Topic 15
|
Topic 16
|
Topic 17
|
Topic 18
|
Topic 19
|
|
language
|
part
|
Analysis
|
detection
|
data
|
python
|
data
|
data
|
time
|
python
|
feature
|
data
|
Neural
|
python
|
Regression
|
data
|
data
|
machine
|
data
|
ai
|
|
text
|
game
|
data
|
image
|
ai
|
Pandas
|
python
|
model
|
Series
|
code
|
data
|
science
|
Network
|
data
|
model
|
Covid
|
Google
|
learning
|
python
|
data
|
|
model
|
python
|
sentiment
|
object
|
article
|
data
|
guide
|
time
|
python
|
Jupyter
|
model
|
scientist
|
Networks
|
visualization
|
python
|
Analysis
|
python
|
deep
|
machine
|
Artificial
|
|
NLP
|
Carlo
|
python
|
model
|
science
|
function
|
step
|
machine
|
Analysis
|
line
|
selection
|
Science
|
deep
|
chart
|
Linear
|
case
|
AWS
|
learn
|
science
|
Intelligence
|
|
Natural
|
Monte
|
customer
|
vision
|
machine
|
Sql
|
Analysis
|
analysis
|
part
|
notebook
|
machine
|
project
|
network
|
plot
|
machine
|
price
|
cloud
|
model
|
testing
|
machine
|
|
word
|
Reinforcement
|
analysis
|
python
|
week
|
use
|
tutorial
|
dataset
|
series
|
data
|
python
|
interview
|
image
|
map
|
decision
|
python
|
machine
|
end
|
probability
|
be
|
|
processing
|
simulation
|
Twitter
|
recognition
|
ML
|
database
|
Pandas
|
real
|
model
|
Julia
|
reduction
|
machine
|
neural
|
Plotly
|
regression
|
analysis
|
model
|
Reinforcement
|
common
|
science
|
|
python
|
Markov
|
recommendation
|
deep
|
knowledge
|
power
|
beginner
|
python
|
data
|
programming
|
value
|
Scientists
|
Convolutional
|
create
|
algorithm
|
using
|
web
|
part
|
learning
|
intelligence
|
|
image
|
player
|
review
|
face
|
paper
|
sql
|
part
|
series
|
price
|
new
|
different
|
skill
|
PyTorch
|
Matplotlib
|
random
|
science
|
step
|
models
|
concept
|
human
|
|
classification
|
League
|
topic
|
computer
|
research
|
file
|
introduction
|
simple
|
Forecasting
|
best
|
missing
|
job
|
part
|
using
|
Gradient
|
use
|
Azure
|
Classification
|
introduction
|
learning
|
This list provides the top ten terms in each of the 20 topics as described by the BTM model. These topic phrases were, by far, the most unique of the three models. In addition, the visual provided strength of ties between terms depicting which relationships were most influential in each topic. From a qualitative perspective, BTM discovered the most unique latent topics of the three models and looks to be an optimum choice for short texts.
5.2 Topic Proportion Over Time
While there are ways to visualize LDA topics over time, in this corpus that model did little to distinguish between relevant topics. Therefore, it was eliminated from topic proportion comparison over time. The BTM package in R is not designed to compare topics by metadata as those covariates are removed in building the final model. While I believe it’s possible to do, there needs to be a way to re-join the final BTM model data with the metadata. That left STM as the only model that was built to understand the metadata’s impact on the topic results. The STM exercise used date as a covariate leading to the plot below for each topic and how its prevalence changed over four years.

Some examples with large shifts over time include:
Deep Learning (1st topic, 2nd row) - significant decrease in popularity
How-to Guides on Statistics (4th topic, 2nd row) - decreased as more articles were becoming popular on visualizations and coding vice basic stats
Data Science Project Research (4th topic, 1st row) - gained in popularity as did topic on Python (5th topic in both 1st and 2nd rows)
5.3 Summary Findings
This project was designed to answer three questions about using topic modeling to identify latent themes in a body of short-form text. Specifically:
- Can short texts provide enough context to adequately inform topic modeling?
Both the LDA and STM trials struggled to differentiate between unique topics. Even with metadata added, the STM model improved little over its LDA counterpart. The BTM model, however, discovered numerous unique topics missed by the other two models. The results showed both separate topics and the relationships between the terms within those topics. Therefore, BTM is a sound choice to inform short text topic modeling.
- Can short text metadata be used to augment the accuracy of these topic models?
This is still somewhat unknown. The STM model used metadata but showed little improvement over LDA, which only used document-term frequencies. As the metadata is stripped from the BTM model, it remains to be seen if those two elements can be re-joined for comparison.
- Can short texts provide enough fidelity to measure topic prevalence over time?
The STM model data was plotted over time and showed how topic prevalence changed. So while it is possible to do this with short-form text data, this experiment was unable to do this with the BTM model.
This project was able to demonstrate qualitatively that short texts can be used to discover latent topics. This could save enormous amounts of time and processing resources as shown by how quickly most of the models ran against a corpus of over 30K articles.
5.4 Limitations
Qualitative vs Quantitative. This study compared qualitative metrics on differences between terms, topics, and proportions over time. Quantitative metrics were not explored as it would have significantly increased the scope of the project. Metrics such as model efficiency, precision, and topic coherence are measures that could provide additional comparative insights for these models.
K determination for BTM. While a mathematical method exists to determine K values for the BTM model, they were too advanced for this project and are not built into the R package in a way that makes them simple to extract. As a result, the LDA and STM K values were compared to derive the optimum K.
Additional short text models. There are other models (such as stLDA-C) to derive latent topics from short texts, but they don’t have specific packages that work in the R environment.
Metadata. The only additional variable captured to distinguish between articles was publication date. Other variables available are author, length of article, number of comments, and number of likes. These variables could be used in future experiments to add more nuanced context to each article.
LS0tCnRpdGxlOiAnU2hvcnQgVGV4dCBUb3BpYyBNb2RlbGluZzogQXJ0aWNsZSBUaXRsZXMgJiBUYWdsaW5lcycKc3VidGl0bGU6ICdGaW5hbCBQcm9qZWN0IGZvciBFQ0kgNTg4LCBUZXh0IE1pbmluZyBpbiBFZHVjYXRpb24nCmF1dGhvcjogIkphbWVzIEhhcmRhd2F5IgpkYXRlOiAiTWF5IDEsIDIwMjIiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFCmVkaXRvcl9vcHRpb25zOiAKICBtYXJrZG93bjogCiAgICB3cmFwOiA3MgotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKIyMgMC4gSU5UUk9EVUNUSU9OCgpXaXRoIHRoZSByYXBpZCBpbmNyZWFzZSBpbiBkaWdpdGl6YXRpb24gb3ZlciB0aGUgcGFzdCAyMCB5ZWFycywgdGhlCmZpZWxkIG9mIHRleHQgbWluaW5nIGhhcyBxdWlja2x5IG1vdmVkIHRvIHRoZSBmb3JlZnJvbnQgb2YgZGF0YSBzY2llbmNlCmFzIGEgbWV0aG9kIGZvciBiZXR0ZXIgdW5kZXJzdGFuZGluZyBwYXR0ZXJucyBhbmQgdHJlbmRzIGluIHNvY2lldHkuClRleHQgbWluaW5nIGlzIG9mdGVuIHRoZSBmaXJzdCBzdGVwIGluIGRldmVsb3BpbmcgaW50cmljYXRlIG1ldGhvZHMgZm9yCm1vcmUgYWR2YW5jZWQgcHJvY2Vzc2VzIHN1Y2ggYXMgc29jaWFsIG5ldHdvcmsgYW5hbHlzaXMgb3IgbmF0dXJhbApsYW5ndWFnZSBwcm9jZXNzaW5nLiBGb3IgbW9kZWxzIHRvIGxlYXJuLCB0aGV5IG11c3QgZXhwZXJpZW5jZQpldmVyeXRoaW5nIChvciBhcyBtdWNoIGFzIHBvc3NpYmxlKSB0aGF0IGlzIGFuZCBpcyBub3Qgd2hhdCB0aGV5IGFyZQphdHRlbXB0aW5nIHRvIHByZWRpY3Qgb3IgbWltaWMuIFRyYWRpdGlvbmFsbHksIHRoaXMgaGFzIG1lYW50IGluY3JlZGlibHkKbGFyZ2Ugdm9sdW1lcyBvZiB0ZXh0IGhhdmUgYmVlbiByZXF1aXJlZCB0byBmZWVkIHRoZXNlIGxlYXJuaW5nIGRlbWFuZHMuCgpBIGNoYWxsZW5nZSB0byB0aGlzIHByb2Nlc3MgaXMgaG93IGh1bWFuIGluZm9ybWF0aW9uIHRlbmRlbmNpZXMgYXJlCmNoYW5naW5nLiBXaGlsZSB3ZSBjb250aW51ZSB0byBjcmVhdGUgbW9yZSBhbmQgbW9yZSBkYXRhLCBpdCdzCnByZXNlbnRpbmcgaW4gc21hbGxlciBhbmQgc21hbGxlciBjaHVua3MuIFNob3J0LWZvcm0gdGV4dCBpcyBxdWlja2x5CmJlY29taW5nIHRoZSBwcmltYXJ5IG1lYW5zIGJ5IHdoaWNoIHBlb3BsZSBjb25zdW1lIGRhaWx5LCBwcmFjdGljYWwsIGFuZAp0aW1lLXNlbnNpdGl2ZSBpbmZvcm1hdGlvbi4gRnJvbSBuZXdzLCB0byBzb2NpYWwgbWVkaWEsIHRvIHJlc2VhcmNoCmFic3RyYWN0cywgdGV4dHMgb2YgMjAwIHdvcmRzIG9yIGZld2VyIGFyZSBnYWluaW5nIHBvcHVsYXJpdHkgZm9yCmNhcnJ5aW5nIHRoZSB0aGUgY3JpdGljYWwgbWVzc2FnZXMgaW4gb3VyIGRhaWx5IGxpdmVzLiBBcyB0aGVzZSBzaG9ydGVyCmZvcm1hdHMgYmVjb21lIGluY3JlYXNpbmdseSBpbXBvcnRhbnQgZm9yIGFuYWx5emluZyBhbmQgdW5kZXJzdGFuZGluZwpzb2NpYWwgdHJlbmRzLCBpdCdzIGltcG9ydGFudCBmb3IgZGF0YSBzY2llbmNlIHRvb2xzIHRvIGFkYXB0IGFzIHRoZQppbmZvcm1hdGlvbiBlbnZpcm9ubWVudCBjb250aW51b3VzbHkgZXZvbHZlcyAoW0ppcGVuZyBldCBhbC4sCjIwMTldKGh0dHBzOi8vZG9pLm9yZy8xMC40ODU1MC9hclhpdi4xOTA0LjA3Njk1KSkuCgpUaGUgY29udGV4dCBmb3IgdGhpcyBwcm9qZWN0IGlzIHByb3ZpZGVkIGJ5IGRhdGEgZnJvbSB0aGUgd2Vic2l0ZQpbVG93YXJkcyBEYXRhIFNjaWVuY2VdKGh0dHBzOi8vdG93YXJkc2RhdGFzY2llbmNlLmNvbS8pIChURFMpLCB3aGljaApJJ3ZlIHVzZWQgb2Z0ZW4gdG8gYXVnbWVudCBteSBzdHVkaWVzIG9uIGxlYXJuaW5nIGFuYWx5dGljcy4gVERTIGlzIGEKW01lZGl1bV0oaHR0cHM6Ly9tZWRpdW0uY29tLykgd2ViIHB1YmxpY2F0aW9uIHdoZXJlIGF1dGhvcnMgc2hhcmUKY29uY2VwdHMsIGlkZWFzLCBhbmQgY29kaW5nIHRpcHMgdGhyb3VnaCBzZWxmLXB1Ymxpc2hlZCBhcnRpY2xlcy4gVGhpcwpkYXRhIGV4cGVyaW1lbnQgd2lsbCBleHBsb3JlIHdoYXQgc2hvcnQtZm9ybSB0ZXh0IG1vZGVsaW5nIGNhbiByZXZlYWwKYWJvdXQgdGhlIGNoYW5nZXMgaW4gcmVzZWFyY2ggZm9jdXMgb3ZlciB0aGUgbGFzdCBmZXcgeWVhcnMgeWVhcnMgYXMKY2hhcnRlZCB0aHJvdWdoIFREUyBhcnRpY2xlcyBwdWJsaXNoZWQgZnJvbSAyMDE4LTIwMjEuCgpUaGlzIHJlc2VhcmNoIHByb2plY3QgYWltcyB0byBhbnN3ZXIgdGhlIGZvbGxvd2luZyBxdWVzdGlvbnM6CgotICAgKioqQ2FuIHNob3J0IHRleHRzIHByb3ZpZGUgZW5vdWdoIGNvbnRleHQgdG8gYWRlcXVhdGVseSBpbmZvcm0gdG9waWMKICAgIG1vZGVsaW5nPyoqKgoKLSAgICoqKkNhbiBzaG9ydCB0ZXh0IG1ldGFkYXRhIGJlIHVzZWQgdG8gYXVnbWVudCB0aGUgYWNjdXJhY3kgb2YgdGhlc2UKICAgIHRvcGljIG1vZGVscz8qKioKCi0gICAqKipDYW4gc2hvcnQgdGV4dHMgcHJvdmlkZSBlbm91Z2ggZmlkZWxpdHkgdG8gbWVhc3VyZSB0b3BpYwogICAgcHJldmFsZW5jZSBvdmVyIHRpbWU/KioqCgpEYXRhIHNjaWVudGlzdHMsIHN0dWRlbnRzLCBvciBhbnkgcHJvZmVzc2lvbmFscyByZXNlYXJjaGluZyB0aGlzIGZpZWxkCmNvdWxkIGJlbmVmaXQgYnkgdW5kZXJzdGFuZGluZyBob3cgcmVzZWFyY2gvcHVibGljYXRpb24gdHJlbmRzIGFyZQpsaW5rZWQgdG8gc2hpZnRzIGluIHRlY2hub2xvZ3kuIFVuZGVyc3RhbmRpbmcgdGhlc2Ugc2hpZnRzIGNvdWxkCmluZmx1ZW5jZSBob3cgcXVpY2tseSBwZW9wbGUgYWRvcHQgb3IgYWR2YW5jZSBuZXcgdGVjaG5vbG9naWVzIGluIHRoZQpkYXRhIHNjaWVuY2UgZmllbGQuIEFkZGl0aW9uYWxseSwgdGhpcyBpbmZvcm1hdGlvbiBjb3VsZCBiZSB1c2VmdWwgdG8KdW5kZXJzdGFuZCB0ZWNobm9sb2dpZXMgcmlwZSBmb3Igc3R1ZHkgaW4gaGlnaGVyIGVkdWNhdGlvbiBsZWFkaW5nIHRvCmhvdyBzdHVkZW50cyBzZWxlY3QgZm9jdXMgYXJlYXMsIG1ham9ycywgb3IgY2VydGlmaWNhdGUgcHJvZ3JhbXMuCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIDEuIFBSRVBBUkUKCiMjIyAxLjEgRGF0YSBTb3VyY2UKCk15IHJhdyBkYXRhIHdhcyBpbml0aWFsbHkgZGV2ZWxvcGVkIGJ5IFtKb2hhbm5lcwpIw7Z0dGVyXShodHRwczovL3d3dy5rYWdnbGUuY29tL2pvaG9ldHRlcikgZWFybGllciB0aGlzIHllYXIgYW5kIHBvc3RlZAp0bwpbS2FnZ2xlXShodHRwczovL3d3dy5rYWdnbGUuY29tL2RhdGFzZXRzL2pvaG9ldHRlci90b3dhcmRzLWRhdGEtc2NpZW5jZSkuClRoZSBkYXRhIHNldCBjb250YWlucyB0aGUgdGl0bGVzIGFuZCB0YWdsaW5lcyBmb3Igb3ZlciAzMCwwMDAgVERTCmFydGljbGVzLiBXaGlsZSB0aGUgdGl0bGVzIGFyZSBzZWxmIGV4cGxhbmF0b3J5LCB0YWdsaW5lcyBhcmUgdGhvc2UKc3VidGl0bGVzIHRoYXQgdGVuZCB0byBnaXZlIGEgYnJpZWYgZ2xpbXBzZSBpbnRvIHRoZSBjb3JlIHB1cnBvc2Ugb2YgdGhlCmFydGljbGUuIFRoZSByYXcgZGF0YSBpcyBvcmdhbml6ZWQgaW50byBmaXZlIHZhcmlhYmxlczoKCjEuICAqKmRvY19pZCoqOiB1bmlxdWUgbnVtZXJpY2FsIGlkZW50aWZpZXIgZm9yIGVhY2ggYXJ0aWNsZQoKMi4gICoqdGl0bGUqKjogYXJ0aWNsZSB0aXRsZQoKMy4gICoqdGFnbGluZSoqOiBhcnRpY2xlIHN1YnRpdGxlCgo0LiAgKip1cmwqKjogYXJ0aWNsZSB3ZWIgYWRkcmVzcwoKNS4gICoqZGF0ZSoqOiBwdWJsaWNhdGlvbiBkYXRlCgojIyMgMS4yIFIgUGFja2FnZSBTZXQgVXAKClRoZSBmb2xsb3dpbmcgcGFja2FnZXMgd2VyZSBpbnN0YWxsZWQgYW5kL29yIGxvYWRlZCB0byBwcmVwYXJlIGZvciB0aGlzCnByb2plY3Q6CgpgYGB7ciBsb2FkLXBhY2thZ2VzLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeSh0aWR5dGV4dCkKbGlicmFyeShTbm93YmFsbEMpCmxpYnJhcnkodG9waWNtb2RlbHMpCmxpYnJhcnkoc3RtKQpsaWJyYXJ5KGxkYXR1bmluZykKbGlicmFyeShrbml0cikKbGlicmFyeSh0bSkKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShCVE0pCmxpYnJhcnkodGV4dHBsb3QpCmxpYnJhcnkoY29uY2F2ZW1hbikKbGlicmFyeSh1ZHBpcGUpCmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShzdG9wd29yZHMpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyAyLiBXUkFOR0xFCgpGb3IgdGhpcyBjYXNlIHN0dWR5LCB0aGUgZGF0YSB3aWxsIGJlIHBhcnNlZCBpbnRvIHRva2VucyBmb3IgaW5pdGlhbApleHBsb3JhdGlvbiB0byBpZGVudGlmeSBwb3RlbnRpYWwgdHJlbmRzIGFuZCBjb250ZXh0IG92ZXIgdGltZS4gVGhyZWUKdG9waWMgbW9kZWxpbmcgdGVjaG5pcXVlcyB3aWxsIGJlIGVtcGxveWVkLCBlYWNoIHdpdGggdGhlaXIgb3duIHNwZWNpZmljCmRhdGEgZm9ybWF0IHJlcXVpcmVtZW50cy4gVG8gbWVldCB0aGVzZSwgdGhlIGRhdGEgd2lsbCBiZSB0aWRpZWQgYW5kCnN0ZW1tZWQgYW5kIHRoZSBzdG9wIHdvcmRzIHdpbGwgYmUgcmVtb3ZlZCB0byBiZXR0ZXIgZm9jdXMgb24KdG9waWMtc3BlY2lmaWMgdGVybWlub2xvZ3kuCgojIyMgMi4xIEltcG9ydCBEYXRhCgpUaGUgcmF3IGRhdGEgd2FzIGRvd25sb2FkZWQgaW50byBhIGNvbW1hIHNlcGFyYXRlZCB2YWx1ZXMgKC5jc3YpIGZpbGUKdGhhdCB3aWxsIGJlIGltcG9ydGVkIGludG8gUlN0dWRpbzoKCmBgYHtyIHRpdGxlcyBpbXBvcnQsIG1lc3NhZ2U9RkFMU0V9CnRkc19yYXcgPC0gcmVhZF9jc3YoImRhdGEvdG93YXJkc19kYXRhX3NjaWVuY2UuY3N2IikKYGBgCgpXaXRoIHRoZSBkYXRhIGZyYW1lIGNyZWF0ZWQsIHRoZSBkYXRhIHNldCB3aWxsIGJlIGZ1cnRoZXIgbWFuaXB1bGF0ZWQKaW50byBhIGZvcm1hdCBmaXQgZm9yIGV4cGxvcmF0b3J5IGFuYWx5c2lzLiBXcmFuZ2xpbmcgd2lsbCBjb25zaXN0IG9mCnRoZSBmb2xsb3dpbmcgc3RlcHM6CgoxLiAgQ29tYmluZSB0aXRsZSBhbmQgdGFnbGluZSBjb2x1bW5zIHRvIGNyZWF0ZSBhIHNpbmdsZSAqKid0ZXh0JyoqCiAgICB2YXJpYWJsZS4KCjIuICBNdXRhdGUgdGhlIGRhdGUgY29sdW1uIGludG8gc2VwYXJhdGUgKioneWVhcicqKiBhbmQgKionbW9udGgnKioKICAgIHZhcmlhYmxlcyB0byBlbmFibGUgdGltZS1iYXNlZCBleHBsb3JhdGlvbi4KCjMuICBDcmVhdGUgYSBjb3JwdXMgdGhyb3VnaCB0aGUgKipgdGlkeXRleHRgKiogcGFja2FnZSB0byBlbmFibGUKICAgIGFkZGl0aW9uYWwgdGV4dCBtaW5pbmcgdG9vbHMuCgo0LiAgU3RlbSB0aGUgKipgdGlkeXRleHRgKiogY29ycHVzIGFuZCByZW1vdmUgc3RvcCB3b3Jkcy4KCjUuICBUb2tlbml6ZSAqKmB0aWR5dGV4dGAqKiBjb3JwdXMgaW50byB1bmlncmFtcywgYmlncmFtcywgYW5kIHRyaWdyYW1zCiAgICB0byBlbmFibGUgdGVybSBmcmVxdWVuY3kgYW5hbHlzaXMuCgo2LiAgQ3JlYXRlIGEgZG9jdW1lbnQgdGVybSBtYXRyaXggKERUTSkgdG8gZXhwbG9yZSB0aGUgTERBIG1vZGVsaW5nCiAgICB0ZWNobmlxdWUuCgojIyMgMi4yIENyZWF0ZSBTaW5nbGUgVGV4dCBWYXJpYWJsZQoKVGhlIGN1cnJlbnQgZGF0YSBmcmFtZSBjb250YWlucyB0aGUgdGFyZ2V0IHRleHQgaW4gdHdvIHNlcGFyYXRlCnZhcmlhYmxlcywgKip0aXRsZSoqIGFuZCAqKnRhZ2xpbmUqKi4gQXMgdGhlIHBhaXIgb2YgdmFyaWFibGVzIHJlcHJlc2VudAphIHNpbmdsZSBkb2N1bWVudCAoYXJ0aWNsZSBpbiB0aGlzIGNhc2UpLCBjb21iaW5pbmcgdGhlIHRleHQgaW50byBhCnNpbmdsZSBjaGFyYWN0ZXIgdmFyaWFibGUgd2lsbCBlbmFibGUgdGhlIG1vZGVscyB0byBmb2N1cyBvbiBqdXN0IG9uZQpjb2x1bW4sIHNpbXBsaWZ5aW5nIHRoZSB3b3JrIG9mIHRoZSBtb2RlbHMuCgpgYGB7ciBjb21iaW5lIHRleHR9CnRkc19yYXckdGV4dCA8LSBwYXN0ZSh0ZHNfcmF3JHRpdGxlLHRkc19yYXckdGFnbGluZSxzZXA9IiAiKQoKIyBSZW5hbWUgSUQgY29sdW1uCmNvbG5hbWVzKHRkc19yYXcpWzFdICA8LSAiZG9jX2lkIgpgYGAKCiMjIyAyLjMgQ3JlYXRlIFllYXIgYW5kIE1vbnRoIFZhcmlhYmxlcwoKVG8gZXhwbG9yZSB0b3BpYyB0cmVuZGluZyBvdmVyIHRpbWUsIHRoZSAqKmRhdGUqKiB2YXJpYWJsZSBpcyBiZWluZwpzZXBhcmF0ZWQgaW50byAqKnllYXIqKiBhbmQgKiptb250aCoqIHZhcmlhYmxlcy4KCmBgYHtyIG11dGF0ZSBkYXRlLCB3YXJuaW5nPUZBTFNFfQp0ZHNfZGF0ZXMgPC0gdGRzX3JhdyAlPiUgCiAgbXV0YXRlKGRhdGUgPSBtZHkoZGF0ZSkpICU+JQogIG11dGF0ZV9hdCh2YXJzKGRhdGUpLCBmdW5zKHllYXIsIG1vbnRoKSkKYGBgCgojIyMgMi40IENyZWF0ZSAqKmB0aWR5dGV4dGAqKiBDb3JwdXMKClRoaXMgcHJvY2VzcyBkZWNvbXBvc2VzIHRoZSBsb25nIHRleHQgc3RyaW5nIGZyb20gdGhlICoqdGV4dCoqIHZhcmlhYmxlCmludG8gc2luZ2xlIHRlcm1zLCB3aGlsZSBtYWludGFpbmluZyB0aGVpciB0aWUgdG8gdGhlIHNvdXJjZSBkb2N1bWVudAooKipkb2NfaWQqKikgYW5kIGl0cyBtZXRhZGF0YSAoKipkYXRlKiosICoqeWVhciwqKiAqKm1vbnRoKiopLgoKYGBge3IgdGlkeXRleHQgY29ycHVzfQp0ZHNfdGlkeSA8LSB0ZHNfZGF0ZXMgJT4lCiAgdW5uZXN0X3Rva2VucyhvdXRwdXQgPSB3b3JkLCBpbnB1dCA9IHRleHQpICU+JQogIGFudGlfam9pbihzdG9wX3dvcmRzLCBieSA9ICJ3b3JkIikKCiMgUmVtb3ZlIG51bWJlcnMKdGRzX3RpZHkgPC0gdGRzX3RpZHlbLWdyZXAoIlxcYlxcZCtcXGIiLCB0ZHNfdGlkeSR3b3JkKSxdCgp0aWR5X3RvcF90b2tlbnMgPC0gdGRzX3RpZHkgJT4lIAogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKSAlPiUgCiAgdG9wX24oMTApCgp0aWR5X3RvcF90b2tlbnMKYGBgCgpUaGUgYWJvdmUgY29kZSBjcmVhdGVkIGEgdGlkeSB2ZXJzaW9uIG9mIHRoZSBjb3JwdXMgYXQgdGhlIHNpbmdsZSB3b3JkCih1bmlncmFtKSBsZXZlbCB3aGlsZSBhbHNvIHJlbW92aW5nIHN0b3Agd29yZHMgYW5kIG51bWJlcnMuCgojIyMgMi41IFN0ZW1taW5nCgpTdGVtbWluZyByZWR1Y2VzIHRoZSBmZWF0dXJlIHNpemUgb2YgYSBjb3JwdXMgYnkgdHJhbnNmb3JtaW5nIHRlcm1zIHRvCnRoZWlyIGJhc2Ugc3RlbS4gU3RlbW1pbmcgcmVkdWNlcyB0aGUgY2hhbmNlcyBvZiByZWR1bmRhbmN5IGluIHRlcm1zIGFuZApwaHJhc2VzIGFzIHRoZSB2YXJpb3VzIHRvcGljIG1vZGVsaW5nIHRlY2huaXF1ZXMgYXJlIGV4cGxvcmVkLgoKYGBge3Igc3RlbSBlYWNoIGNvcnB1cywgd2FybmluZz1GQUxTRX0KdGRzX3RpZHkgPC0gdGRzX3RpZHkgJT4lIAogIG11dGF0ZSh3b3JkID0gd29yZFN0ZW0od29yZCkpCmBgYAoKIyMjIDIuNiBDYXN0IGEgRG9jdW1lbnQgVGVybSBNYXRyaXgKClRoZSBMREEgbW9kZWwgcmVxdWlyZXMgdGhlIHRleHQgYmUgcHJlc2VudGVkIGluIHRoZSBmb3JtIG9mIGEgdGlkeSBEVE0sCndoZXJlIGVhY2ggdGVybSBvY2N1cGllcyBhIHNpbmdsZSBjZWxsIGFjY29yZGluZyB0byBhIHVuaXF1ZSBhbmQKY29udHJvbGxpbmcgdmFyaWFibGUuIEluIHRoaXMgY2FzZSwgdGhlIHRpdGxlIHdpbGwgYWN0IGFzIHRoYXQgdW5pcXVlCmlkZW50aWZpZXIuCgpgYGB7ciB0aWR5dGV4dCBEVE19CnRpZHlfdGRzX0RUTSA8LSB0ZHNfdGlkeSAlPiUKICBjb3VudCh0aXRsZSwgd29yZCkgJT4lCiAgY2FzdF9kdG0odGl0bGUsIHdvcmQsIG4pCgp0aWR5X3Rkc19EVE0KYGBgCgojIyMgMi43IFRva2VuaXphdGlvbgoKTGFzdGx5LCB3cmFuZ2xpbmcgd2lsbCBlbmQgd2l0aCB0aGUgdG9rZW5pemF0aW9uIG9mIHRoZSBvcmlnaW5hbCBkYXRhIHRvCmVuYWJsZSBmdXJ0aGVyIHRlcm0gZnJlcXVlbmN5IGFuYWx5c2lzIGF0IHRoZSAqYmlncmFtKiAod29yZCBwYWlyKSBhbmQKKnRyaWdyYW0qICgzLXdvcmQgc2V0KSBsZXZlbHMuIEZvciB0aGVzZSBpdGVyYXRpb25zLCBzdG9wIHdvcmQgcmVtb3ZhbAphbmQgc3RlbW1pbmcgaGFzIGJlZW4gaW5jb3Jwb3JhdGVkOgoKYGBge3IgdG9rZW5pemUgYmlncmFtcywgbWVzc2FnZT1GQUxTRX0KdGRzX2JpZ3JhbXMgPC0gdGRzX2RhdGVzICU+JSAgIAogIHVubmVzdF90b2tlbnMob3V0cHV0ID0gYmlncmFtLCBpbnB1dCA9IHRleHQsIHRva2VuID0gIm5ncmFtcyIsIG4gPSAyKQoKdGRzX2JpZ3JhbXMgPC0gdGRzX2JpZ3JhbXMgJT4lIAogIHNlcGFyYXRlKGJpZ3JhbSwgaW50byA9IGMoIndvcmQxIiwgIndvcmQyIiksIHNlcCA9ICIgIikgJT4lCiAgZmlsdGVyKCF3b3JkMSAlaW4lIHN0b3Bfd29yZHMkd29yZCkgJT4lCiAgZmlsdGVyKCF3b3JkMiAlaW4lIHN0b3Bfd29yZHMkd29yZCkgJT4lCiAgbXV0YXRlKHdvcmQxID0gd29yZFN0ZW0od29yZDEpKSAlPiUgCiAgbXV0YXRlKHdvcmQyID0gd29yZFN0ZW0od29yZDIpKSAlPiUgCiAgdW5pdGUoYmlncmFtLCBjKHdvcmQxLCB3b3JkMiksIHNlcCA9ICIgIikKCmJpZ3JhbV90b3BfdG9rZW5zIDwtIHRkc19iaWdyYW1zICU+JSAKICBjb3VudChiaWdyYW0sIHNvcnQgPSBUUlVFKSAlPiUgCiAgdG9wX24oMTApCgpiaWdyYW1fdG9wX3Rva2VucwpgYGAKCmBgYHtyIHRva2VuaXplIHRyaWdyYW1zLCBtZXNzYWdlPUZBTFNFfQp0ZHNfdHJpZ3JhbXMgPC0gdGRzX2RhdGVzICU+JSAgIAogIHVubmVzdF90b2tlbnMob3V0cHV0ID0gdHJpZ3JhbSwgaW5wdXQgPSB0ZXh0LCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMykKCnRkc190cmlncmFtcyA8LSB0ZHNfdHJpZ3JhbXMgJT4lIAogIHNlcGFyYXRlKHRyaWdyYW0sIGludG8gPSBjKCJ3b3JkMSIsICJ3b3JkMiIsICJ3b3JkMyIpLCBzZXAgPSAiICIpICU+JQogIGZpbHRlcighd29yZDEgJWluJSBzdG9wX3dvcmRzJHdvcmQpICU+JQogIGZpbHRlcighd29yZDIgJWluJSBzdG9wX3dvcmRzJHdvcmQpICU+JSAKICBmaWx0ZXIoIXdvcmQzICVpbiUgc3RvcF93b3JkcyR3b3JkKSAlPiUKICBtdXRhdGUod29yZDEgPSB3b3JkU3RlbSh3b3JkMSkpICU+JSAKICBtdXRhdGUod29yZDIgPSB3b3JkU3RlbSh3b3JkMikpICU+JSAKICBtdXRhdGUod29yZDMgPSB3b3JkU3RlbSh3b3JkMykpICU+JSAKICB1bml0ZSh0cmlncmFtLCBjKHdvcmQxLCB3b3JkMiwgd29yZDMpLCBzZXAgPSAiICIpCgp0cmlncmFtX3RvcF90b2tlbnMgPC0gdGRzX3RyaWdyYW1zICU+JSAKICBjb3VudCh0cmlncmFtLCBzb3J0ID0gVFJVRSkgJT4lIAogIHRvcF9uKDEwKQoKdHJpZ3JhbV90b3BfdG9rZW5zCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyAzLiBFWFBMT1JBVE9SWSBBTkFMWVNJUwoKIyMjIDMuMSBQdWJsaXNoZWQgQXJ0aWNsZSBDb3VudHMKCmBgYHtyIEFydGljbGUgY291bnRzfQp0ZHNfZGF0ZXMgJT4lIAogIGdncGxvdChhZXMoeCA9IGRhdGUsIGNvbG9yID0gZmFjdG9yKG1vbnRoKSkpICsKICBnZW9tX2JhcihzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgbGFicyh5ID0gIkRhdGUiLAogICAgIHggPSAiQXJ0aWNsZSBDb3VudHMiLAogICAgIHRpdGxlID0gIlRvd2FyZHMgRGF0YSBTY2llbmNlIEFydGljbGVzIiwKICAgICBzdWJ0aXRsZSA9ICJQdWJsaXNoZWQgZnJvbSAyMDE4IC0gMjAyMSIpCmBgYAoKVGhpcyB2aXN1YWwgZGVwaWN0cyB0aGUgMzBrIGFydGljbGUgc3ByZWFkIG92ZXIgdGhlIHBhc3QgZm91ciB5ZWFycy4gVERTCmhhZCBhIGdyZWF0IHllYXIgaW4gMjAyMCB3aXRoIG92ZXIgNzAgYXJ0aWNsZXMgcHVibGlzaGVkIG1vbnRobHkgaW4gdGhlCm1pZC15ZWFyIHBlcmlvZC4gQW55IGNoYW5jZSB0aGF0IHdhcyBkdWUgdG8gc2NpZW50aXN0cyBiZWluZyBjb29wZWQgdXAKZHVyaW5nIHRoZSBoZWFydCBvZiB0aGUgQ09WSUQtMTkgcGFuZGVtaWM/IFdyaXRpbmcgaXMgYSBncmVhdCB3YXkgdG8KcGFzcyB0aGUgdGltZSEgVGhlIG5leHQgY291cGxlIG9mIHNlY3Rpb25zIHdpbGwgZXhwbG9yZSB3b3JkIGZyZXF1ZW5jaWVzCnRvIGF0dGVtcHQgdG8gaWRlbnRpZnkgcGF0dGVybnMgd2l0aGluIHRoZSBtb3N0IHVzZSB3b3JkcyBvciB3b3JkCmNvbWJpbmF0aW9ucy4KCiMjIyAzLjIgV29yZCBDb3VudHMgYnkgWWVhcgoKYGBge3IgVW5pZ3JhbSBjb3VudHMsIG1lc3NhZ2U9RkFMU0V9CnRkc190aWR5ICU+JQogIGdyb3VwX2J5KHllYXIpICU+JQogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKSAlPiUKICB0b3BfbigxMCkgJT4lCiAgdW5ncm91cCAlPiUKICBtdXRhdGUod29yZCA9IHJlb3JkZXJfd2l0aGluKHdvcmQsIG4sIHllYXIpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB3b3JkLCB5ID0gbiwgZmlsbCA9IHdvcmQpKSArCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGZhY2V0X3dyYXAofiB5ZWFyLCBzY2FsZXMgPSAiZnJlZV95IikgKwogIGNvb3JkX2ZsaXAoKSArCiAgc2NhbGVfeF9yZW9yZGVyZWQoKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwwKSkgKwogIGxhYnMoeSA9ICJDb3VudCIsCiAgICAgICB4ID0gIlVuaXF1ZSB3b3JkcyIsCiAgICAgICB0aXRsZSA9ICJNb3N0IGZyZXF1ZW50IHdvcmRzIGZvdW5kIGluIFREUyBhcnRpY2xlIHRpdGxlcyAmIHRhZ2xpbmVzIiwKICAgICAgIHN1YnRpdGxlID0gIlN0b3Agd29yZHMgcmVtb3ZlZCBmcm9tIHRoZSBsaXN0IikKYGBgCgpXaGVuIGRpYWdyYW1pbmcgdGhlIHRvcCB0ZW4gdW5pZ3JhbXMgYnkgeWVhciwgbWFueSB0ZXJtcyBhcmUgcmVwZWF0ZWQuClNpbmNlIGEgZGF0YSBzY2llbmNlIGJsb2dnaW5nIHNpdGUgaXMgdGhlIGZvY3VzIGZvciB0aGlzIHByb2plY3QsIGl0J3MKbm8gc3VycHJpc2UgdGhhdCB3b3JkcyBzdWNoIGFzIGRhdGEsIHNjaWVuY2UsIG1hY2hpbmUsIGxlYXJuaW5nLCBldGMuCndvdWxkIGFwcGVhciBhdCB0aGUgdG9wLiBUaGUgY2hhcnQgYmVsb3cgaW5kaWNhdGVzIGhvdyB0aGlzIGNoYW5nZWQgd2hlbgp0aGUgZ3JhcGggd2FzIGV4cGFuZGVkIHRvIGluY2x1ZGUgdGhlIHRvcCAyMCB0ZXJtczoKCmBgYHtyIFVuaWdyYW0gdG9wIDIwLCBtZXNzYWdlPUZBTFNFfQp0ZHNfdGlkeSAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkgJT4lCiAgdG9wX24oMjApICU+JQogIHVuZ3JvdXAgJT4lCiAgbXV0YXRlKHdvcmQgPSByZW9yZGVyX3dpdGhpbih3b3JkLCBuLCB5ZWFyKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gd29yZCwgeSA9IG4sIGZpbGwgPSB3b3JkKSkgKwogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsKICBmYWNldF93cmFwKH4geWVhciwgc2NhbGVzID0gImZyZWVfeSIpICsKICBjb29yZF9mbGlwKCkgKwogIHNjYWxlX3hfcmVvcmRlcmVkKCkgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsMCkpICsKICBsYWJzKHkgPSAiQ291bnQiLAogICAgICAgeCA9ICJVbmlxdWUgd29yZHMiLAogICAgICAgdGl0bGUgPSAiTW9zdCBmcmVxdWVudCB3b3JkcyBmb3VuZCBpbiBURFMgYXJ0aWNsZSB0aXRsZXMgJiB0YWdsaW5lcyIsCiAgICAgICBzdWJ0aXRsZSA9ICJTdG9wIHdvcmRzIHJlbW92ZWQgZnJvbSB0aGUgbGlzdCIpCmBgYAoKVGhpcyBzZWNvbmQgYXR0ZW1wdCBkb2VzIGJlZ2luIHRvIHJldmVhbCBzb21lIHVuaXF1ZSB0ZXJtcyBieSB5ZWFyLgpBbm90aGVyIHdheSB0byBhY2hpZXZlIHRoaXMgaXMgYnkgYWRkaW5nIHdvcmRzIGNvbW1vbiB0byBhbGwgeWVhcnMgdG8KdGhlIGxpc3Qgb2Ygc3RvcCB3b3Jkcy4gSW4gdGhlIGVuZCwgSSBkZWNpZGVkIG5vdCB0byBmb2N1cyB0b28gbXVjaAplbmVyZ3kgb24gaW5kaXZpZHVhbCB0ZXJtcyBhcyBpbiB0aGlzIGNvbW11bml0eSwgbWFueSBvZiB0aGUga2V5IHRvcGljcwphcmUgZGVzY3JpYmVkIGJ5IG11bHRpcGxlIHRlcm1zLCBzdWNoIGFzICdtYWNoaW5lIGxlYXJuaW5nLCcgYXMgb3Bwb3NlZAp0byB0cmVhdGluZyB0aG9zZSB3b3JkcyBhcyBzZXBhcmF0ZSBhbmQgZGlzdGluY3QgZW50aXRpZXMuIFRvIHRoYXQgZW5kLApJIHJlcGVhdGVkIHRoZSBhYm92ZSB2aXN1YWxzLCBidXQgZm9yIG11bHRpLXdvcmQgZ3JvdXBpbmdzLgoKIyMjIDMuMyBCaWdyYW0gQ291bnRzCgpgYGB7ciBCaWdyYW0gY291bnRzLCBtZXNzYWdlPUZBTFNFfQp0ZHNfYmlncmFtcyAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICBjb3VudChiaWdyYW0sIHNvcnQgPSBUUlVFKSAlPiUKICB0b3BfbigyMCkgJT4lCiAgdW5ncm91cCAlPiUKICBtdXRhdGUoYmlncmFtID0gcmVvcmRlcl93aXRoaW4oYmlncmFtLCBuLCB5ZWFyKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gYmlncmFtLCB5ID0gbiwgZmlsbCA9IGJpZ3JhbSkpICsKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZmFjZXRfd3JhcCh+IHllYXIsIHNjYWxlcyA9ICJmcmVlX3kiKSArCiAgY29vcmRfZmxpcCgpICsKICBzY2FsZV94X3Jlb3JkZXJlZCgpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLDApKSArCiAgbGFicyh5ID0gIkNvdW50IiwKICAgICAgIHggPSAiVW5pcXVlIEJpZ3JhbXMiLAogICAgICAgdGl0bGUgPSAiTW9zdCBmcmVxdWVudCBiaWdyYW1zIGZvdW5kIGluIGFydGljbGUgdGl0bGVzICYgdGFnbGluZXMiLAogICAgICAgc3VidGl0bGUgPSAiU3RvcCB3b3JkcyByZW1vdmVkIGZyb20gdGhlIGxpc3QiKQpgYGAKCkV4cGFuZGluZyB0byAyLXdvcmQgcGhyYXNlcyByZXZlYWxzIHVuaXF1ZSB0b3BpY3MgaW4gZWFjaCB5ZWFyLiBTb21lCmludGVyZXN0aW5nIGV4YW1wbGVzIGFyZSAqKnJhbmRvbSBmb3Jlc3QqKiAoMjAxOCksICoqb2JqZWN0IGRldGVjdCoqCigyMDE5KSwgKipjb3ZpZCAxOSoqICgyMDIwKSwgYW5kICoqcHl0aG9uIGNvZGUqKiAoMjAyMSkuCgojIyMgMy40IFRyaWdyYW0gQ291bnRzCgpgYGB7ciBUcmlncmFtIGNvdW50cywgbWVzc2FnZT1GQUxTRX0KdGRzX3RyaWdyYW1zICU+JQogIGdyb3VwX2J5KHllYXIpICU+JQogIGNvdW50KHRyaWdyYW0sIHNvcnQgPSBUUlVFKSAlPiUKICB0b3BfbigxMCkgJT4lCiAgdW5ncm91cCAlPiUKICBtdXRhdGUodHJpZ3JhbSA9IHJlb3JkZXJfd2l0aGluKHRyaWdyYW0sIG4sIHllYXIpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB0cmlncmFtLCB5ID0gbiwgZmlsbCA9IHRyaWdyYW0pKSArCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGZhY2V0X3dyYXAofiB5ZWFyLCBzY2FsZXMgPSAiZnJlZV95IikgKwogIGNvb3JkX2ZsaXAoKSArCiAgc2NhbGVfeF9yZW9yZGVyZWQoKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwwKSkgKwogIGxhYnMoeSA9ICJDb3VudCIsCiAgICAgICB4ID0gIlVuaXF1ZSBUcmlncmFtcyIsCiAgICAgICB0aXRsZSA9ICJNb3N0IGZyZXF1ZW50IHRyaWdyYW1zIGZvdW5kIGluIGFydGljbGUgdGl0bGVzICYgdGFnbGluZXMiLAogICAgICAgc3VidGl0bGUgPSAiU3RvcCB3b3JkcyByZW1vdmVkIGZyb20gdGhlIGxpc3QiKQpgYGAKClRyaWdyYW1zIHJlcGVhdCBxdWl0ZSBhIGJpdCBpbiB0aGUgdG9wIHRlbiwgdGhvdWdoIHlvdSBzdGFydCBzZWVpbmcgbW9yZQpjb21wbGV0ZSBpZGVhcyBlbWVyZ2UgYWJvdXQgc3BlY2lmaWMgYWN0aXZpdGllcy4gRXhhbXBsZXMgaW5jbHVkZSAqKmRhdGEKc2NpZW5jZSBqb2IqKiBhbmQgKip0aW1lIHNlcmkgZGF0YSoqLgoKIyMjIDMuNSBFeHBsb3JhdG9yeSBBbmFseXNpcyBJbnNpZ2h0cwoKV29yZCBmcmVxdWVuY3kgYW5hbHlzaXMgcmV2ZWFscyB0aGF0IHVuaXF1ZSB3b3JkcyBhbmQgcGhyYXNlcyBkbyBlbWVyZ2UKZnJvbSB5ZWFyIHRvIHllYXIuIFRoaXMgaW1wbGllcyB0aGF0IHJlc2VhcmNoIHRvcGljcyBhcmUgY2hhbmdpbmcKdGhyb3VnaG91dCB0aGUgY29ycHVzIG9mIFREUyBhcnRpY2xlcyBiZXR3ZWVuIDIwMTggYW5kIDIwMjEuIFNvbWUgb3RoZXIKaW50ZXJlc3RpbmcgZmVhdHVyZXMgb2YgdGhpcyBkYXRhIHNldCBpbmNsdWRlOgoKLSAgIHNpbmdsZSB3b3JkcyBkbyBsaXR0bGUgdG8gZGlzdGluZ3Vpc2ggYmV0d2VlbiB0b3BpY3MgYW5kL29yIHllYXJzCgotICAgdHJpZ3JhbSBwaHJhc2VzIGJlY29tZSByZXBldGl0aXZlIGJleW9uZCB0aGUgdG9wIHRlbiwgc28gYmlncmFtCiAgICBwaHJhc2VzIGxvb2tzIHRvIGJlIHRoZSBvcHRpbWFsIGZvY3VzIGZvciBzaG9ydCB0ZXh0IHRvcGljIG1vZGVsaW5nCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIDQuIE1PREVMCgpUbyBhZGRyZXNzIHRoZSBwb3RlbnRpYWwgZm9yIHRvcGljIGdlbmVyYXRpb24gaW4gc2hvcnQtZm9ybSB0ZXh0LCBJIHdpbGwKY29tcGFyZSB2YXJpb3VzIHF1YWxpdGF0aXZlIG1vZGVsIHJlc3VsdHMgdG8gZGV0ZXJtaW5lIHRoZSBkaWZmZXJlbmNlcywKaWYgYW55LCBpbiBob3cgdGhleSBpZGVudGlmeSB1bmlxdWUgdG9waWNzIHVzaW5nIHNob3J0ZXIgZGF0YQpvYnNlcnZhdGlvbnMuIFRoaXMgYW5hbHlzaXMgd2lsbCBleGFtaW5lIHRocmVlIG1vZGVsczoKCi0gICAqKkxhdGVudCBEaXJpY2hsZXQgQWxsb2NhdGlvbioqIChMREEpLiBMREEgd29ya3MgdW5kZXIgdGhlIHByZW1pc2UKICAgIHRoYXQgZXZlcnkgZG9jdW1lbnQgY29udGFpbnMgYSBtaXh0dXJlIG9mIHRvcGljcywgYW5kIGV2ZXJ5IHRvcGljIGlzCiAgICBjb21wb3NlZCBvZiBhIG1peHR1cmUgb2Ygd29yZHMuCgotICAgKipTdHJ1Y3R1cmFsIFRvcGljIE1vZGVsKiogKFNUTSkuIFNUTSBlbXBsb3lzIG1ldGFkYXRhIHRvIGltcHJvdmUKICAgIHRoZSBhc3NpZ25tZW50IG9mIHdvcmRzIHRvIHRvcGljcyBpbiBhIGNvcnB1cyBhbmQgdGhhdCBjYW4gYmUgdXNlZAogICAgdG8gZXhhbWluZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gY292YXJpYXRlcyBhbmQgZG9jdW1lbnRzLgoKLSAgICoqQml0ZXJtIFRvcGljIE1vZGVsKiogKEJUTSkuIEJUTSB3YXMgZGVzaWduZWQgc3BlY2lmaWNhbGx5IGZvciBhCiAgICBzaG9ydC1mb3JtYXQgY29ycHVzIGFuZCBpZGVudGlmaWVzIHRvcGljcyBieSBleHBsaWNpdGx5IG1vZGVsaW5nCiAgICB3b3JkLXdvcmQgY28tb2NjdXJyZW5jZXMgaW4gYSBzcGVjaWZpZWQgd2luZG93IG9mIHRleHQuCgojIyMgNC4xIERldGVybWluaW5nIEsKCkVhY2ggbW9kZWwgaXMgcHJlZGljYXRlZCBvbiBoYXZpbmcgYSByZWxhdGl2ZWx5IG9wdGltdW0gYXBwcm94aW1hdGlvbgpmb3IgKksqLCBvciB0aGUgbnVtYmVyIG9mIHBvdGVudGlhbCB0b3BpY3MgdG8gYmUgaWRlbnRpZmllZC4gSWYgKksqIGlzCnRvbyBzbWFsbCwgdGhlIGNvcnB1cyBpcyBkaXZpZGVkIGludG8gYSBmZXcgdmVyeSBnZW5lcmljIHRvcGljcy4gSWYgKksqCmlzIHRvbyBsYXJnZSwgdGhlIGNvbGxlY3Rpb24gaXMgYnJva2VuIGludG8gdG9vIG1hbnkgdG9waWNzIGluIHdoaWNoCnRoZXkgZWl0aGVyIG92ZXJsYXAgb3IgYXJlIGhhcmRseSBpbmRlY2lwaGVyYWJsZS4gQmVmb3JlIHRoZSBtb2RlbHMgY2FuCmJlIGFwcGxpZWQsIGEgY29tbW9uIEsgdmFsdWUgc2hvdWxkIGJlIGRldGVybWluZWQgc28gdGhlIHJlc3VsdHMgY2FuIGJlCm1vcmUgY29uc2lzdGVudGx5IGNvbXBhcmVkLiBBIGNoYWxsZW5nZSB0byB0aGlzIHByb2Nlc3MgaXMgdGhhdCBlYWNoCm1vZGVsIG1haW50YWlucyBkaXN0aW5jdCBtZXRob2RzIGZvciBkZXRlcm1pbmluZyBLIGJhc2VkIG9uIGhvdyB0aGUKYWxnb3JpdGhtIHRyZWF0cyB0aGUgdGV4dC4gQXMgTERBIGFuZCBTVE0gd2VyZSBkZXNpZ25lZCBmb3IgbGFyZ2VyCmJvZGllcyBvZiB0ZXh0LCBpdCB3aWxsIGJlIGludGVyZXN0aW5nIHRvIHNlZSBob3cgdGhlIG1vZGVsIHRyZWF0cyB0aGUKc2hvcnQtZm9ybSBkYXRhIGluIHRoaXMgZXhwZXJpbWVudC4gSWYgYSBjb25zaXN0ZW50IEstdmFsdWUgY2Fubm90IGJlCmRldGVybWluZWQgZXhwbGljaXRseSwgdGhlbiB0aGUgYWx0ZXJuYXRpdmUgd2lsbCBiZSB0byBjb25kdWN0IGEKdHJpYWwtYW5kLWVycm9yIGFwcHJvYWNoIHRvIGZpbmQgYSB2YWx1ZSB0aGF0IGNhbiBiZSBhcHBsaWVkIHRvIGFsbAp0aHJlZSBtb2RlbHMuIEFzIG91ciBtYWluIGdvYWwgaXMgdG8gY29tcGFyZSByZXN1bHRzIGJldHdlZW4gdGhlbSwgdGhlCmFsdGVybmF0aXZlIEsgc29sdXRpb24gd2lsbCBtZWV0IHRoZSBpbnRlbnQgb2YgdGhlIHByb2plY3QuIEhvdyBhbgpvcHRpbWFsICpLKiBzaG91bGQgYmUgc2VsZWN0ZWQgZGVwZW5kcyBvbiB2YXJpb3VzIGZhY3RvcnMgdGhhdCBhcmUKdW5pcXVlIHRvIGVhY2ggdHlwZSBvZiB0b3BpYyBtb2RlbC4KCiMjIyMgRmluZFRvcGljc051bWJlcigpIEZ1bmN0aW9uIGluIHRoZSBMREEgTW9kZWwKCkZvciB0aGUgTERBIG1vZGVsLCBmb3VyIG1ldHJpY3Mgd2VyZSBleHRyYWN0ZWQsIHRoZW4gcGxvdHRlZCB0bwp2aXN1YWxpemUgdGhlIG1heGltdW0gb3IgbWluaW11bSAqSyogdmFsdWUgb2YgZWFjaCBtZXRyaWM6CgpgYGB7ciBMREEgTWV0aG9kLCBldmFsPUZBTFNFfQprX21ldHJpY3MgPC0gRmluZFRvcGljc051bWJlcigKICB0aWR5X3Rkc19EVE0sCiAgdG9waWNzID0gc2VxKDUsIDUwLCBieSA9IDUpLAogIG1ldHJpY3MgPSBjKCJHcmlmZml0aHMyMDA0IiwgIkNhb0p1YW4yMDA5IiwgIkFydW4yMDEwIiwgIkRldmVhdWQyMDE0IiksCiAgbWV0aG9kID0gIkdpYmJzIiwKICBjb250cm9sID0gbGlzdChzZWVkID0gNzcpLAogIG1jLmNvcmVzID0gTkEsCiAgcmV0dXJuX21vZGVscyA9IEZBTFNFLAogIHZlcmJvc2UgPSBGQUxTRSwKICBsaWJwYXRoID0gTlVMTAopCkZpbmRUb3BpY3NOdW1iZXJfcGxvdChrX21ldHJpY3MpCmBgYAoKIVtdKGltYWdlcy9iZGJhYzY3ZC04YTI4LTQ4NjQtOGYzZC0yMmU5NDlhNTY0N2UucG5nKQoKVGhlIGtleSBpcyB0byBpZGVudGlmeSB0aGUgdmlzaWJsZSBiZW5kLCBvciBpbmZsZWN0aW9uIHBvaW50LCBvbiBlYWNoIG9mCnRoZSBsaW5lcy4gVGhpcyBpcyB3aGVyZSB0aGV5IHRyYW5zaXRpb24gZnJvbSBxdWlja2x5CmluY3JlYXNpbmcvZGVjcmVhc2luZyB0byBhIG1vcmUgZmxhdCB0cmFqZWN0b3J5LiBJbiBlYWNoIG9mIHRoZSBsaW5lcywKdGhhdCBpbmZsZWN0aW9uIHBvaW50IGlzIGJldHdlZW4gKioqMjAqKiogYW5kICoqKjMwKioqIHRvcGljcy4gVGhlIG9uZQptZXRyaWMgdGhhdCBoYXMgYSBzaW5nbGUgYW5kIGVhc2lseSByZWNvZ25pemFibGUgdmFsdWUgaXMgdGhlCioqKkNhb0p1YW4yMDA5KioqIGxpbmUsIHdoaWNoIHlpZWxkcyBhIG1heCB2YWx1ZSBvZiAqKioyMCoqKiB0b3BpY3MuCgojIyMjIHNlYXJjaEsoKSBGdW5jdGlvbiBpbiBTVE0KClRoZSAqKmBzdG1gKiogcGFja2FnZSBoYXMgYSB1c2VmdWwgZnVuY3Rpb24gY2FsbGVkICpzZWFyY2hLKiB3aGljaApyZXF1aXJlcyBhIHNwZWNpZmljIHJhbmdlIG9mIHZhbHVlcyBmb3IgKksqIGFuZCBvdXRwdXRzIG11bHRpcGxlCmdvb2RuZXNzLW9mLWZpdCBtZWFzdXJlczoKCmBgYHtyIFNUTSBNZXRob2QsIGV2YWw9RkFMU0V9CmZpbmRpbmdrIDwtIHNlYXJjaEsoZG9jcywKICAgICAgICAgICAgICAgICAgICB2b2NhYiwKICAgICAgICAgICAgICAgICAgICBLID0gYyg1OjMwKSwKICAgICAgICAgICAgICAgICAgICBkYXRhID0gbWV0YSwKICAgICAgICAgICAgICAgICAgICB2ZXJib3NlPUZBTFNFKQoKcGxvdChmaW5kaW5naykKYGBgCgohW10oaW1hZ2VzL1NUTV9LcGxvdC5qcGVnKQoKQmFzZWQgb24gdGhlIGZlZWRiYWNrIGZyb20gdGhlIExEQSBtb2RlbCwgdGhlIHJhbmdlIHdhcyB0ZXN0ZWQgYmV0d2VlbiA1CmFuZCAzMCB0b3BpY3MuIFNpbWlsYXIgdG8gdGhlIExEQSByZXN1bHRzLCB0aGUgY3VydmVzIGhpdCB0aGVpcgppbmZsZWN0aW9uIHBvaW50IGJldHdlZW4gKioqMjUqKiogYW5kICoqKjMwKioqIHRvcGljcy4gKihOb3RlOiB0aGlzCnRyaWFsIHJlcXVpcmVkIFx+IDggaG91cnMgdG8gcnVuIG9uIGEgZmFpcmx5IGhpZ2ggZW5kIE1hY2Jvb2sgUHJvLiBZb3VyCm1pbGVhZ2UgbWF5IHZhcnkuKSoKCiMjIyMgRGV0ZXJtaW5pbmcgSyBmb3IgQlRNCgpVbmxpa2UgdGhlIHByZXZpb3VzIHR3byBtb2RlbHMsIHRoZXJlIGlzIG5vIGZ1bmN0aW9uIGJ1aWx0IGludG8gdGhlCioqYEJUTWAqKiBwYWNrYWdlIGluIFIgZm9yIGVzdGltYXRpbmcgKksqLiBBcyB0aGlzIGlzIGEgY29tcGFyYXRpdmUKc3R1ZHksIHVzaW5nIHNpbWlsYXIgSyB2YWx1ZXMgZm9yIGVhY2ggbW9kZWwgc2hvdWxkIHByb3ZpZGUgY29uc2lzdGVudApkYXRhIGZvciB0aGlzIHB1cnBvc2UuIFRoZXJlZm9yZSwgdGhlIEJUTSBtb2RlbCB3YXMgZXhlY3V0ZWQgYXNzdW1pbmcgYW4Kb3B0aW11bSAqSyogdmFsdWUgYmV0d2VlbiAqKioyMCoqKiBhbmQgKioqMzAqKiogdG9waWNzLiBUaGUgbW9kZWxzIHdpbGwKdXNlIHRoZXNlIHZhbHVlcyB3aGlsZSBhbHNvIGFkZGluZyBhIGxvd2VyIHZhbHVlIG9mIDEwIHRvIHZlcmlmeSB3aGF0CmhhcHBlbnMgaWYgYSAqSyogdmFsdWUgaXMgc2VsZWN0ZWQgdGhhdCBpcyB0b28gbG93LgoKIyMjIDQuMiAqKkxhdGVudCBEaXJpY2hsZXQgQWxsb2NhdGlvbiAoKipMREEpIE1vZGVsCgpMREEgaXMgYSBtYXRoZW1hdGljYWwgbWV0aG9kIGZvciBlc3RpbWF0aW5nIHRoZSBtaXh0dXJlIG9mIHdvcmRzIHRoYXQgaXMKYXNzb2NpYXRlZCB3aXRoIGVhY2ggdG9waWMsIHdoaWxlIGFsc28gZGV0ZXJtaW5pbmcgdGhlIG1peHR1cmUgb2YgdG9waWNzCnRoYXQgZGVzY3JpYmVzIGVhY2ggZG9jdW1lbnQgKFtTaWxnZSAmIFJvYmluc29uLAoyMDE3XShodHRwczovL3d3dy50aWR5dGV4dG1pbmluZy5jb20vdG9waWNtb2RlbGluZy5odG1sKSkuCgpgYGB7ciBMREEgTW9kZWx9CnRkc19sZGEgPC0gTERBKHRpZHlfdGRzX0RUTSwgCiAgICAgICAgICAgICAgICAgIGsgPSAyMCwgCiAgICAgICAgICAgICAgICAgIGNvbnRyb2wgPSBsaXN0KHNlZWQgPSA1ODgpKQoKdGVybXModGRzX2xkYSwgNSkKYGBgCgpGb3IgKioqSyA9IDIwKioqLCB0aGUgdG9waWNzIGxvb2tzIHRvIGJlIHJlbGF0aXZlbHkgc2ltaWxhciBhY3Jvc3MgdGhlCmJvYXJkLCB0aG91Z2ggdGhlaXIgYXJlIGEgY291cGxlIHRoYXQgc3RhbmQgb3V0IGFzIHVuaXF1ZS4gSW4gdGhpcwpmb3JtYXQsIGhvd2V2ZXIsIHJlY29nbml6aW5nIHRob3NlIHRvcGljcyBpcyBub3QgZWFzeS4gVGhlIGZhY2V0ZWQgcGxvdApiZWxvdyBwcm92aWRlcyBhIG11Y2ggbW9yZSBpbmZvcm1hdGl2ZSB2aXN1YWw6CgpgYGB7ciBMREEgRmFjZXQgUGxvdCwgZXZhbD1GQUxTRX0KdG9wX3Rlcm1zX2xkYSA8LSB0ZHNfbGRhICU+JQogIGdyb3VwX2J5KHRvcGljKSAlPiUKICBzbGljZV9tYXgoYmV0YSwgbiA9IDUsIHdpdGhfdGllcyA9IEZBTFNFKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgYXJyYW5nZSh0b3BpYywgLWJldGEpCnRvcF90ZXJtc19sZGEgJT4lCiAgbXV0YXRlKHRlcm0gPSByZW9yZGVyX3dpdGhpbih0ZXJtLCBiZXRhLCB0b3BpYykpICU+JQogIGdyb3VwX2J5KHRvcGljLCB0ZXJtKSAlPiUgICAgCiAgYXJyYW5nZShkZXNjKGJldGEpKSAlPiUgIAogIHVuZ3JvdXAoKSAlPiUKICBnZ3Bsb3QoYWVzKGJldGEsIHRlcm0sIGZpbGwgPSBhcy5mYWN0b3IodG9waWMpKSkgKwogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV95X3Jlb3JkZXJlZCgpICsKICBsYWJzKHRpdGxlID0gIlRvcCA1IHRlcm1zIGluIGVhY2ggTERBIHRvcGljIiwKICAgICAgIHggPSBleHByZXNzaW9uKGJldGEpLCB5ID0gTlVMTCkgKwogIGZhY2V0X3dyYXAofiB0b3BpYywgbmNvbCA9IDQsIHNjYWxlcyA9ICJmcmVlIikKYGBgCgohW10oaW1hZ2VzL0xEQV8yMC5wbmcpCgpJJ3ZlIGhpZ2hsaWdodGVkIHRocmVlIHRvcGljcyAoMTEsIDE4LCBhbmQgMjApIHdob3NlIHRvcCBmaXZlIHRlcm1zIHdlcmUKb3V0c2lkZSB0aGUgcmFuZ2Ugb2YgdGhlIG1ham9yaXR5IG9mIHRoZSBvdGhlciB0b3BpY3MuCgotICAgVG9waWMgMTEgcmVmZXJlbmNlcyBydW5uaW5nIG9yIGNoYW5naW5nIFB5dGhvbiBjb2RlLgoKLSAgIFRvcGljIDE4IG1lbnRpb25zIGEgc3BlY2lmaWMgbGlicmFyeSBpbiBQeXRob24sIGNhbGxlZCBQYW5kYXMuCgotICAgVG9waWMgMjAgc3BlYWtzIHRvIGJ1aWxkaW5nIG1vZGVscyBmb3IgaW1hZ2UgYW5hbHlzaXMvZGV0ZWN0aW9uLgoKV2hpbGUgdGhyZWUgdG9waWNzIHByZXNlbnRlZCB0aGVtc2VsdmVzIGFzIHVuaXF1ZSwgdGhhdCBpcyBhIHJlbGF0aXZlbHkKc21hbGwgbnVtYmVyIHdpdGhpbiAyMCB0b3BpY3MuIFRvIGV4cGxvcmUgaG93IHRoZSByZXN1bHRzIGNoYW5nZSBiYXNlZApvbiB0aGUgY2hvc2VuIG51bWJlciBvZiB0b3BpY3MsIEkgcmFuIHRoZSBzYW1lIG1vZGVsIHVzaW5nICoqKks9MTAqKioKYW5kICoqKks9MzAqKiouCgpGb3IgKksqPTEwLCBJIGhhZCBkaWZmaWN1bHR5IGRpc3Rpbmd1aXNoaW5nIGJldHdlZW4gYW55IG9mIHRoZSB0b3BpY3MuClZlcnkgZ2VuZXJpYyB3aXRoIHJlcGVhdGVkIHRlcm1zIG9mICoqKmRhdGEsIG1hY2hpbiwgbGVhcm4sIG1vZGVsLCoqKgphbmQgKioqc2NpZW5jKioqLCB3aGljaCBhcmUgd2hhdCBvbmUgd291bGQgZXhwZWN0IHRvIHNlZSBvbiBhIHdlYnNpdGUKdGhhdCBwdWJsaWhlZCBkYXRhIHNjaWVuY2UgYXJ0aWNsZXMuCgohW10oaW1hZ2VzL0xEQV8xMC5wbmcpCgpVc2luZyAqKipLPTMwKioqIHlpZWxkcyByZXN1bHRzIHNpbWlsYXIgdG8gdGhlIGltYWdlIGZyb20gKksqPTIwLiBXaGlsZQp0b3BpYyAqKjMqKiBpcyB1bmlxdWUgY29tcGFyZWQgdG8gdGhlIHByZXZpb3VzIHJ1biwgdG9waWNzICoqMTgqKiBhbmQKKioyMCoqIGxvb2sgYWxtb3N0IGlkZW50aWNhbCB0byB0aGVpciBjb3VudGVycGFydHMgZnJvbSB0aGUgZWFybGllcgp0cmlhbC4KCiFbXShpbWFnZXMvTERBXzMwLnBuZykKCiMjIyMgUGVyZm9ybWFuY2UgU3VtbWFyeQoKV2l0aCBmZXcgZXhjZXB0aW9ucywgdGhlIExEQSBtb2RlbCBkaWQgbGl0dGxlIHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4KdG9waWNzIGJleW9uZCBhIGdlbmVyaWMgc3VtbWFyeSBvZiB3aGF0IG9uZSBjb3VsZCBleHBlY3QgdG8gcmVhZCBpbiBhbnkKYXJ0aWNsZSBvbiBkYXRhIHNjaWVuY2UuIFRoZXNlIHJlc3VsdHMgcXVhbGl0YXRpdmVseSBjb25maXJtIHRoZQppbmFiaWxpdHkgb2YgdGhlIG1vZGVsIHRvIGNvbnNpc3RlbnRseSByZWNvZ25pemUgdW5pcXVlIHRvcGljcyBpbgpzaG9ydC1mb3JtIHRleHQuIFRyZWF0aW5nIGFuIGFydGljbGUncyB0aXRsZSBhbmQgdGFnbGluZSBhcyBhIGNvbXBsZXRlCiJkb2N1bWVudCIgZ2l2ZXMgdGhlIG1vZGVsIHRvbyBsaXR0bGUgZGF0YSB0byBpbmZvcm0gbGF0ZW50IHRvcGljCmRpc2NvdmVyeS4gVGhlIG5leHQgbW9kZWwgc2hvdWxkIHByb3ZpZGUgc29tZXdoYXQgZGlmZmVyZW50IHJlc3VsdHMgYXMKdGhlIGFuYWx5c2lzIGluY2x1ZGVzIGFydGljbGUgbWV0YWRhdGEgdG8gYXNzaXN0IGluIGRldGVybWluaW5nCnBvdGVudGlhbCB0b3BpY3MuCgojIyMgNC4zIFN0cnVjdHVyYWwgVG9waWMgTW9kZWwgKFNUTSkKClRoZSBgc3RtYCBwYWNrYWdlIGluIFIgcmVxdWlyZXMgdGhlIGRvY3VtZW50cywgbWV0YSBkYXRhLCBhbmQKInZvY2FiIi0tLW9yIHRvdGFsIGxpc3Qgb2Ygd29yZHMgZGVzY3JpYmVkIGluIHRoZSBkb2N1bWVudHMtLS0gdG8gYmUKc3RvcmVkIGluIHNlcGFyYXRlIG9iamVjdHMgKHNlZSBjb2RlIGJlbG93KS4gVGhlIGZpcnN0IGxpbmUgb2YgY29kZQplbGltaW5hdGVzIGJvdGggZXh0cmVtZWx5IGNvbW1vbiB0ZXJtcyBhbmQgZXh0cmVtZWx5IHJhcmUgdGVybXMsIGFzIGlzCmNvbW1vbiBwcmFjdGljZSBpbiB0b3BpYyBtb2RlbGluZywgc2luY2Ugc3VjaCB0ZXJtcyBtYWtlIHdvcmQtdG9waWMKYXNzaWdubWVudCBtdWNoIG1vcmUgZGlmZmljdWx0IChbQmFpbCwKMjAxOV0oaHR0cHM6Ly9zaWNzcy5pby8yMDE5L21hdGVyaWFscy9kYXkzLXRleHQtYW5hbHlzaXMvdG9waWMtbW9kZWxpbmcvcm1hcmtkb3duL1RvcGljX01vZGVsaW5nLmh0bWwpKS4KCmBgYHtyIFNUTSBNb2RlbCwgbWVzc2FnZT1GQUxTRSwgcmVzdWx0cz0naGlkZSd9CnRlbXAgPC0gdGV4dFByb2Nlc3Nvcih0ZHNfcmF3JHRleHQsIAogICAgICAgICAgICAgICAgICAgICAgbWV0YWRhdGEgPSB0ZHNfcmF3LCAgCiAgICAgICAgICAgICAgICAgICAgICBsb3dlcmNhc2U9VFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICByZW1vdmVzdG9wd29yZHM9VFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICByZW1vdmVudW1iZXJzPVRSVUUsICAKICAgICAgICAgICAgICAgICAgICAgIHJlbW92ZXB1bmN0dWF0aW9uPVRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgd29yZExlbmd0aHM9YygzLEluZiksCiAgICAgICAgICAgICAgICAgICAgICBzdGVtPVRSVUUsCiAgICAgICAgICAgICAgICAgICAgICBvbmx5Y2hhcmFjdGVyPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICBzdHJpcGh0bWw9VFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICBjdXN0b21zdG9wd29yZHM9TlVMTCkKCmRvY3MgPC0gdGVtcCRkb2N1bWVudHMgCm1ldGEgPC0gdGVtcCRtZXRhIAp2b2NhYiA8LSB0ZW1wJHZvY2FiIAoKdGRzX3N0bSA8LSBzdG0oZG9jdW1lbnRzPWRvY3MsIAogICAgICAgICAgICAgICBkYXRhPW1ldGEsCiAgICAgICAgICAgICAgIHZvY2FiPXZvY2FiLCAKICAgICAgICAgICAgICAgcHJldmFsZW5jZSA9fiBkYXRlLAogICAgICAgICAgICAgICBLPTIwLAogICAgICAgICAgICAgICBtYXguZW0uaXRzPTI1LAogICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UsCiAgICAgICAgICAgICAgIGdhbW1hLnByaW9yPSdMMScpCmBgYAoKIVtdKGltYWdlcy9TVE1fMjAucG5nKQoKVGhpcyBmaXJzdCBpdGVyYXRpb24gb2YgdGhlIFNUTSBtb2RlbCAoKksqPTIwKSBzaG93cyBhIG11Y2ggbW9yZSBkaXZlcnNlCnJhbmdlIG9mIHRlcm1zIHRoYW4gTERBLiBGb3IgdGhpcyBkYXRhIHNldCwgdGhlIG9ubHkgYWRkaXRpb25hbAppbmZvcm1hdGlvbiBhdmFpbGFibGUgZm9yIGRpc3Rpbmd1aXNoaW5nIHRvcGljcyBpcyB0aGUgcHVibGljYXRpb24gZGF0ZQpvZiBlYWNoIGFydGljbGUuIFRoaXMgbGluZWFyIGZvcm1hdCwgaG93ZXZlciwgaXMgc3RpbGwgZGlmZmljdWx0IHRvCmNhcHR1cmUgaG93IGVhY2ggdG9waWMgZGlmZmVycyBmcm9tIGl0J3MgbmVpZ2hib3JzLiBBIG1vcmUgdmlzdWFsIGFuZAp1c2VmdWwgbWV0aG9kIGlzIGVuYWJsZWQgYnkgdGhlICoqYHRvTERBdmlzYCoqIGZ1bmN0aW9uLgoKYGBge3IgTERBdmlzIEV4cGxvcmVyLCBtZXNzYWdlPUZBTFNFfQp0b0xEQXZpcyhtb2QgPSB0ZHNfc3RtLCBkb2NzID0gZG9jcykKYGBgCgohW10oaW1hZ2VzL1NUTV92aXMyMC0wMS5wbmcpCgpUaGlzIHRvb2xzIGV4cGxvcmVzIGhvdyBlYWNoIHRvcGljIHJlbGF0ZXMgdG8gdGhlIG90aGVycyBzcGF0aWFsbHkuIEluIGEKcGVyZmVjdCBlbnZpcm9ubWVudCwgdGhlIG1vZGVsIHdvdWxkIGhhdmUgaWRlbnRpZmllZCAyMCB0b3BpY3Mgc2VwYXJhdGUKYW5kIGRpc3RpbmN0IGZyb20gZWFjaCBvdGhlci4gVGhlIGRpYWdyYW0gd291bGQndmUgc2hvd24gMjAgY2lyY2xlcyB3aXRoCm5vIG92ZXJsYXAuIFdoaWxlIGl0J3MgZG91YnRmdWwgYSBtb2RlbCBjb3VsZCBwcmVkaWN0IHRoYXQgbGV2ZWwgb2YKcHJlY2lzaW9uLCB0aGlzIGRpYWdyYW0gc2hvd3MgdGhhdCB3aXRoICoqKks9MjAqKiosIHRoZXJlIGlzIHJlbGF0aXZlbHkKZ29vZCB0b3BpYyBzZXBhcmF0aW9uLiBUaGVyZSBhcmUganVzdCBmb3VyIGFyZWFzIHdoZXJlIHRvcGljcyBvdmVybGFwLAptZWFuaW5nIHRoZXkgc2hhcmUgYSBsYXJnZXIgcHJvcG9ydGlvbiBvZiBzaW1pbGFyIHRlcm1zLgoKKipUb3BpYyAyKiogaXMgaGlnaGxpZ2h0ZWQgYXMgaXQgaXMgbGFyZ2UgYW5kIG1haW50YWlucyBnb29kIHNwYWNpbmcKZnJvbSBpdHMgbmVhcmVzdCBuZWlnaGJvcnMuIFRoZSBzaXplIGluZGljYXRlcyB0aGUgbnVtYmVyIG9mIHRlcm1zCmFzc29jaWF0ZWQgd2l0aCB0aGlzIHRvcGljLiBJbiB0aGlzIGNhc2UsIHRoZSBrZXkgdGVybSBpcyAnZGF0YSwnIHdoaWNoCmlzIG5vdCBzdXJwcmlzaW5nLCBidXQgaXRzIGNvbXBhbmlvbiB0ZXJtcyBhcmUgd2hhdCBzZXBhcmF0ZXMgaXQgZnJvbQppdHMgbmVpZ2hib3JzLiBUaGUgbG9uZSBidWJibGUgaW5kaWNhdGVzIHRoYXQgdGhpcyB0b3BpYyBpcyBkaXN0aW5jdCBpbgppdHMgdGVybSBjb21wb3NpdGlvbi4gQ29tYmluaW5nIHRoZSB0b3AgMTAgdGVybXMgcmV2ZWFscyB0aGUgdG9waWMKZGVzY3JpYmVzIGFydGljbGVzIHdyaXR0ZW4gYWJvdXQgdGhlIHNraWxscyByZXF1aXJlZCB0byBiZWNvbWUgYSBkYXRhCnNjaWVudGlzdC4gRm9yIGNvbXBhcmlzb24gYWNyb3NzIHRvcGljIHZhbHVlcywgdGhpcyBtb2RlbCB3YXMgcmVwZWF0ZWQKZm9yICoqKks9MTAqKiogYW5kICoqKks9MzAqKio6CgohW10oaW1hZ2VzL1BpY3R1cmUxLnBuZykKClRoZXNlIHZpZXdzIGFkZCB0byB0aGUgaW5mZXJlbmNlIHRoYXQgSz0yMCBpcyBwcmV0dHkgY2xvc2UgdG8gb3B0aW11bS4KRm9yIEs9MTAsIGVpZ2h0IG9mIHRoZSB0b3BpY3Mgb3ZlcmxhcCB3aXRoIGEgbmVpZ2hib3IuIExpa2V3aXNlIGZvcgpLPTMwLCB0aGVyZSBhcmUgc2V2ZXJhbCBncm91cGluZ3Mgb2Ygb3ZlcmxhcHBpbmcgdG9waWNzLgoKIyMjIyBQZXJmb3JtYW5jZSBTdW1tYXJ5CgpRdWFsaXRhdGl2ZWx5LCB0aGUgU1RNIG1vZGVsIGRpc3Rpbmd1aXNoZWQgYmV0d2VlbiB0b3BpY3MgbXVjaCBtb3JlCmVmZmVjdGl2ZWx5IHRoYW4gdGhlIExEQSBtb2RlbC4gVGhlIHNlcGFyYXRpb24gb2YgdG9waWMgdGVybXMgYW5kIG1lZGl1bQp0byBsYXJnZS1zaXplZCBidWJibGVzIHJlaW5mb3JjZSB0aGF0IHRoZSBvcHRpbXVtIG51bWJlciBvZiB0ZXJtcyBpcwphcm91bmQgMjAgYW5kIGFkZGluZyBtZXRhZGF0YSBpbmNyZWFzZXMgdGhlIGxpa2VsaWhvb2Qgb2YgZGlzdGluZ3Vpc2hpbmcKYmV0d2VlbiB1bmlxdWUgdG9waWNzLgoKIyMjIDQuNCBCaXRlcm0gVG9waWMgTW9kZWwgKEJUTSkKClRoZSBCaXRlcm0gVG9waWMgTW9kZWwgKEJUTSkgaXMgYSB3b3JkIGNvLW9jY3VycmVuY2UgYmFzZWQgdG9waWMgbW9kZWwKdGhhdCBsZWFybnMgdG9waWNzIGJ5IG1vZGVsaW5nIHdvcmQtd29yZCBjby1vY2N1cnJlbmNlcyBwYXR0ZXJucyAoZS5nLiwKYml0ZXJtcykgKFtXaWpmZmVscywKMjAyMV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL0JUTS9CVE0ucGRmKSkuCgotICAgQSBiaXRlcm0gY29uc2lzdHMgb2YgdHdvIHdvcmRzIGNvLW9jY3VycmluZyBpbiB0aGUgc2FtZSBjb250ZXh0LCBmb3IKICAgIGV4YW1wbGUsIGluIHRoZSBzYW1lIHNob3J0IHRleHQgd2luZG93LiBUaGlzIHdpbmRvdyBpcyBkZXNjcmliZWQgYnkKICAgIHRoZSBwYXJhbWV0ZXJzICpza2lwZ3JhbSogYW5kICp3aWR0aCouCgotICAgU2tpcGdyYW0gZGVmaW5lcyB0aGUgbnVtYmVyIG9mIHdvcmRzIHRvIGluY2x1ZGUgaW4gdGhlIGJpdGVybSBzZWFyY2gKICAgIHNwYWNlLCB3aGlsZSB3aWR0aCBkZWZpbmVzIGhvdyBtYW55IHdvcmRzIG9uIGF2ZXJhZ2UgZXhpc3QgaW4gYQogICAgc2luZ2xlIGRvY3VtZW50LiBGb3IgdGhpcyBwcm9qZWN0LCBhIHNraXBncmFtIHZhbHVlIG9mIDUgd2FzIHVzZWQKICAgIGFuZCB0aGUgd2lkdGggd2FzIGtlcHQgYXQgdGhlIGRlZmF1bHQgdmFsdWUgb2YgMTUuCgotICAgQlRNIG1vZGVscyB0aGUgYml0ZXJtIG9jY3VycmVuY2VzIGFjcm9zcyBhIGNvbXBsZXRlIGNvcnB1cyAodW5saWtlCiAgICBMREEgbW9kZWxzIHdoaWNoIG1vZGVsIHRoZSB3b3JkIG9jY3VycmVuY2VzIGluIGEgc2luZ2xlIGRvY3VtZW50KS4KCkJhc2VkIG9uIHRoZSBmaXJzdCB0d28gaXRlcmF0aW9ucywgdGhlIEJUTSB0cmlhbCBmb2N1c2VkIG9uIHRyYWluaW5nIGEKbW9kZWwgdG8gaWRlbnRpZnkgMjAgdG9waWNzOgoKYGBge3IgQlRNIE1vZGVsLCByZXN1bHRzPSdoaWRlJ30KIyBUYWcgcGFydHMgb2Ygc3BlZWNoCmFubm8gICAgPC0gdWRwaXBlKHRkc19yYXcsICJlbmdsaXNoIiwgdHJhY2UgPSAxMCkKYml0ZXJtcyA8LSBhcy5kYXRhLnRhYmxlKGFubm8pCmJpdGVybXMgPC0gYml0ZXJtc1ssIGNvb2NjdXJyZW5jZSh4ID0gbGVtbWEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWxldmFudCA9IHVwb3MgJWluJSBjKCJOT1VOIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkFESiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQUk9QTiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2tpcGdyYW0gPSA1KSwKICAgICAgICAgICAgICAgICAgIGJ5ID0gbGlzdChkb2NfaWQpXQoKIyBCdWlsZCBCVE0Kc2V0LnNlZWQoNTg4KQp0cmFpbmRhdGEgPC0gc3Vic2V0KGFubm8sIHVwb3MgJWluJSBjKCJOT1VOIiwgIkFESiIsICJQUk9QTiIpKQp0cmFpbmRhdGEgPC0gdHJhaW5kYXRhWywgYygiZG9jX2lkIiwgImxlbW1hIildCm1vZGVsIDwtIEJUTSh0cmFpbmRhdGEsIGsgPSAyMCwgCiAgICAgICAgICAgICBiZXRhID0gMC4wMSwgCiAgICAgICAgICAgICBpdGVyID0gNTAwLAogICAgICAgICAgICAgYml0ZXJtcyA9IGJpdGVybXMsIAogICAgICAgICAgICAgdHJhY2UgPSAxMDApCgojIFBsb3QgTW9kZWwgUmVzdWx0cyAoZG8gbm90IHJ1biB3aGVuIGtuaXR0aW5nKQojbGlicmFyeShnZ3JhcGgpCiNwbG90KG1vZGVsLAojICAgICB0b3BfbiA9IDEwLAojICAgICB0aXRsZSA9ICJCVE0gbW9kZWwiLAojICAgICBzdWJ0aXRsZSA9ICJLID0gMjAsIDUwMCBUcmFpbmluZyBJdGVyYXRpb25zIiwKIyAgICAgbGFiZWxzID0gYygiMCIsICIxIiwgIjIiLCAiMyIsICI0IiwgIjUiLCAiNiIsICI3IiwgIjgiLCAiOSIsCiMgICAgICAgICAgICAgICAgIjEwIiwgIjExIiwgIjEyIiwgIjEzIiwgICIxNCIsICIxNSIsICIxNiIsICIxNyIsIAojICAgICAgICAgICAgICAgICIxOCIsICIxOSIpKQpgYGAKCiFbXShpbWFnZXMvQlRNXzIwX2xhYmVscy5wbmcpCgojIyMjIFBlcmZvcm1hbmNlIFN1bW1hcnkKClRoZSB2aXN1YWwgcmVzdWx0cyBvZiB0aGUgQlRNIHdpdGggSyBzZXQgdG8gMjAgdG9waWNzIGRlcGljdHMgYSBmZXcKdW5pcXVlIGZlYXR1cmVzOgoKMS4gIE5vbmUgb2YgdGhlIGdyb3VwZWQgdG9waWNzIHJlcGVhdC4gVGhleSBhcmUgYWxsIGZhaXJseSB1bmlxdWUgaW4KICAgIHRlcm1pbm9sb2d5LgoyLiAgV29yZCBzaXplIGlzIGEgcHJvZHVjdCBvZiBob3cgaW1wb3J0YW50IChvciBjb21tb24pIHRoYXQgd29yZCBpcyBpbgogICAgdGhhdCBwYXJ0aWN1bGFyIHRvcGljLiBXb3JkcyBpbiBzbWFsbGVyIGZvbnQgaW5kaWNhdGUgdGhlaXIKICAgIHByb2JhYmlsaXR5ICgqKip0aGV0YSoqKikgb2YgYXBwZWFyaW5nIGluIHRoYXQgdG9waWMgd2FzIGxlc3MuCjMuICBUaGUgbGluZSB3ZWlnaHRzIHdpdGhpbiB0aGUgdG9waWNzIGFyZSBpbmRpY2F0aXZlIG9mIHRpZSBzdHJlbmd0aAogICAgYmV0d2VlbiB3b3Jkcy4gVGhpY2tlciBsaW5lcyBpbmRpY2F0ZSBzdHJvbmdlciByZWxhdGlvbnNoaXBzIGJldHdlZW4KICAgIHRlcm1zLgoKV2hpbGUgdGhpcyB2aXN1YWxpemF0aW9uIHJlc2VtYmxlcyB3b3JkIGNsb3VkcywgdGhlIGFsZ29yaXRobSBiZWhpbmQgdGhlCmdyYXBoaWMgaXMgbXVjaCBtb3JlIGNvbXBsZXggdGhhbiBzaW1wbGUgdGVybSBmcmVxdWVuY2llcyBhbmQgaXMgYQpwcm9kdWN0IG9mIHRoZSBjby1vY2N1cnJlbmNlIG9mIHRlcm1zLgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyA1LiBDT01NVU5JQ0FURQoKIyMjIDUuMSBUb3BpY3MgRGlzY292ZXJhYmlsaXR5IGluIFNob3J0LWZvcm0gVGV4dAoKKipMREEuKiogVGhlIExEQSBtb2RlbCBkaWQgbGl0dGxlIHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4gdG9waWNzIGJleW9uZCBhCmdlbmVyaWMgc3VtbWFyeSBvZiB3aGF0IG9uZSBjb3VsZCBleHBlY3QgdG8gcmVhZCBpbiBhbnkgYXJ0aWNsZSBvbiBkYXRhCnNjaWVuY2UuIFRoZXNlIHJlc3VsdHMgcXVhbGl0YXRpdmVseSBjb25maXJtIHRoZSBpbmFiaWxpdHkgb2YgdGhlIG1vZGVsCnRvIGNvbnNpc3RlbnRseSByZWNvZ25pemUgdW5pcXVlIHRvcGljcyBpbiBzaG9ydC1mb3JtIHRleHQuIFRyZWF0aW5nIGFuCmFydGljbGUncyB0aXRsZSBhbmQgdGFnbGluZSBhcyBhIGNvbXBsZXRlICJkb2N1bWVudCIgZ2l2ZXMgdGhlIG1vZGVsIHRvbwpsaXR0bGUgZGF0YSB0byBpbmZvcm0gbGF0ZW50IHRvcGljIGRpc2NvdmVyeS4KCioqU1RNLioqIFRoZSBTVE0gbW9kZWwgZGlzdGluZ3Vpc2hlZCBiZXR3ZWVuIHRvcGljcyBtdWNoIG1vcmUKZWZmZWN0aXZlbHkgdGhhbiB0aGUgTERBIG1vZGVsLiBUaGVzZSByZXN1bHRzIGFkZCBhZGRpdGlvbmFsCnJlaW5mb3JjZW1lbnQgdGhhdCB0aGUgb3B0aW11bSBudW1iZXIgb2YgdGVybXMgaXMgYXJvdW5kIDIwIGFuZCBhZGRpbmcKbWV0YWRhdGEgaW5jcmVhc2VzIHRoZSBsaWtlbGlob29kIG9mIGRpc3Rpbmd1aXNoaW5nIGJldHdlZW4gdW5pcXVlCnRvcGljcy4KCioqQlRNLioqCgpgYGB7ciBMaXN0IEJUTSBUZXJtcyBieSBUb3BpYywgZWNobz1GQUxTRX0KdG9waWN0ZXJtcyA8LSB0ZXJtcyhtb2RlbCwgdG9wX24gPSAxMCkKc3RtX3Rlcm1zIDwtIGFzLmRhdGEuZnJhbWUodG9waWN0ZXJtcykKCiMgVmlldyBCVE0gVG9waWMgVGFibGUKYnRtX3RvcGljcyA8LSBzdG1fdGVybXMgJT4lIAogIHNlbGVjdCgtc3RhcnRzX3dpdGgoJ3AnKSkgJT4lIAogIHJlbmFtZSgiVG9waWMgMCIgPSAidG9rZW4iLAogICAgICAgICAiVG9waWMgMSIgPSAidG9rZW4uMSIsCiAgICAgICAgICJUb3BpYyAyIiA9ICJ0b2tlbi4yIiwKICAgICAgICAgIlRvcGljIDMiID0gInRva2VuLjMiLAogICAgICAgICAiVG9waWMgNCIgPSAidG9rZW4uNCIsCiAgICAgICAgICJUb3BpYyA1IiA9ICJ0b2tlbi41IiwKICAgICAgICAgIlRvcGljIDYiID0gInRva2VuLjYiLAogICAgICAgICAiVG9waWMgNyIgPSAidG9rZW4uNyIsCiAgICAgICAgICJUb3BpYyA4IiA9ICJ0b2tlbi44IiwKICAgICAgICAgIlRvcGljIDkiID0gInRva2VuLjkiLAogICAgICAgICAiVG9waWMgMTAiID0gInRva2VuLjEwIiwKICAgICAgICAgIlRvcGljIDExIiA9ICJ0b2tlbi4xMSIsCiAgICAgICAgICJUb3BpYyAxMiIgPSAidG9rZW4uMTIiLAogICAgICAgICAiVG9waWMgMTMiID0gInRva2VuLjEzIiwKICAgICAgICAgIlRvcGljIDE0IiA9ICJ0b2tlbi4xNCIsCiAgICAgICAgICJUb3BpYyAxNSIgPSAidG9rZW4uMTUiLAogICAgICAgICAiVG9waWMgMTYiID0gInRva2VuLjE2IiwKICAgICAgICAgIlRvcGljIDE3IiA9ICJ0b2tlbi4xNyIsCiAgICAgICAgICJUb3BpYyAxOCIgPSAidG9rZW4uMTgiLAogICAgICAgICAiVG9waWMgMTkiID0gInRva2VuLjE5IiwpICAgCiAgCiMgVmlldyBCVE0gVG9waWMgVGFibGUKYnRtX3RvcGljcyAlPiUgCiAga2JsKCkgJT4lIAogIGthYmxlX3N0eWxpbmcoKSAlPiUgCiAgc2Nyb2xsX2JveCh3aWR0aCA9ICI4MDBweCIsIGhlaWdodCA9ICI1MDBweCIpCmBgYAoKVGhpcyBsaXN0IHByb3ZpZGVzIHRoZSB0b3AgdGVuIHRlcm1zIGluIGVhY2ggb2YgdGhlIDIwIHRvcGljcyBhcwpkZXNjcmliZWQgYnkgdGhlIEJUTSBtb2RlbC4gVGhlc2UgdG9waWMgcGhyYXNlcyB3ZXJlLCBieSBmYXIsIHRoZSBtb3N0CnVuaXF1ZSBvZiB0aGUgdGhyZWUgbW9kZWxzLiBJbiBhZGRpdGlvbiwgdGhlIHZpc3VhbCBwcm92aWRlZCBzdHJlbmd0aCBvZgp0aWVzIGJldHdlZW4gdGVybXMgZGVwaWN0aW5nIHdoaWNoIHJlbGF0aW9uc2hpcHMgd2VyZSBtb3N0IGluZmx1ZW50aWFsCmluIGVhY2ggdG9waWMuIEZyb20gYSBxdWFsaXRhdGl2ZSBwZXJzcGVjdGl2ZSwgQlRNIGRpc2NvdmVyZWQgdGhlIG1vc3QKdW5pcXVlIGxhdGVudCB0b3BpY3Mgb2YgdGhlIHRocmVlIG1vZGVscyBhbmQgbG9va3MgdG8gYmUgYW4gb3B0aW11bQpjaG9pY2UgZm9yIHNob3J0IHRleHRzLgoKIyMjIDUuMiBUb3BpYyBQcm9wb3J0aW9uIE92ZXIgVGltZQoKV2hpbGUgdGhlcmUgYXJlIHdheXMgdG8gdmlzdWFsaXplIExEQSB0b3BpY3Mgb3ZlciB0aW1lLCBpbiB0aGlzIGNvcnB1cwp0aGF0IG1vZGVsIGRpZCBsaXR0bGUgdG8gZGlzdGluZ3Vpc2ggYmV0d2VlbiByZWxldmFudCB0b3BpY3MuIFRoZXJlZm9yZSwKaXQgd2FzIGVsaW1pbmF0ZWQgZnJvbSB0b3BpYyBwcm9wb3J0aW9uIGNvbXBhcmlzb24gb3ZlciB0aW1lLiBUaGUgQlRNCnBhY2thZ2UgaW4gUiBpcyBub3QgZGVzaWduZWQgdG8gY29tcGFyZSB0b3BpY3MgYnkgbWV0YWRhdGEgYXMgdGhvc2UKY292YXJpYXRlcyBhcmUgcmVtb3ZlZCBpbiBidWlsZGluZyB0aGUgZmluYWwgbW9kZWwuIFdoaWxlIEkgYmVsaWV2ZSBpdCdzCnBvc3NpYmxlIHRvIGRvLCB0aGVyZSBuZWVkcyB0byBiZSBhIHdheSB0byByZS1qb2luIHRoZSBmaW5hbCBCVE0gbW9kZWwKZGF0YSB3aXRoIHRoZSBtZXRhZGF0YS4gVGhhdCBsZWZ0IFNUTSBhcyB0aGUgb25seSBtb2RlbCB0aGF0IHdhcyBidWlsdAp0byB1bmRlcnN0YW5kIHRoZSBtZXRhZGF0YSdzIGltcGFjdCBvbiB0aGUgdG9waWMgcmVzdWx0cy4gVGhlIFNUTQpleGVyY2lzZSB1c2VkICpkYXRlKiBhcyBhIGNvdmFyaWF0ZSBsZWFkaW5nIHRvIHRoZSBwbG90IGJlbG93IGZvciBlYWNoCnRvcGljIGFuZCBob3cgaXRzIHByZXZhbGVuY2UgY2hhbmdlZCBvdmVyIGZvdXIgeWVhcnMuCgohW10oaW1hZ2VzL1NUTV8yMF90aW1lLnBuZykKClNvbWUgZXhhbXBsZXMgd2l0aCBsYXJnZSBzaGlmdHMgb3ZlciB0aW1lIGluY2x1ZGU6CgotICAgKipEZWVwIExlYXJuaW5nKiogKDFzdCB0b3BpYywgMm5kIHJvdykgLSBzaWduaWZpY2FudCBkZWNyZWFzZSBpbgogICAgcG9wdWxhcml0eQoKLSAgICoqSG93LXRvIEd1aWRlcyBvbiBTdGF0aXN0aWNzKiogKDR0aCB0b3BpYywgMm5kIHJvdykgLSBkZWNyZWFzZWQgYXMKICAgIG1vcmUgYXJ0aWNsZXMgd2VyZSBiZWNvbWluZyBwb3B1bGFyIG9uIHZpc3VhbGl6YXRpb25zIGFuZCBjb2RpbmcKICAgIHZpY2UgYmFzaWMgc3RhdHMKCi0gICAqKkRhdGEgU2NpZW5jZSBQcm9qZWN0IFJlc2VhcmNoKiogKDR0aCB0b3BpYywgMXN0IHJvdykgLSBnYWluZWQgaW4KICAgIHBvcHVsYXJpdHkgYXMgZGlkIHRvcGljIG9uIFB5dGhvbiAoNXRoIHRvcGljIGluIGJvdGggMXN0IGFuZCAybmQKICAgIHJvd3MpCgojIyMgNS4zIFN1bW1hcnkgRmluZGluZ3MKClRoaXMgcHJvamVjdCB3YXMgZGVzaWduZWQgdG8gYW5zd2VyIHRocmVlIHF1ZXN0aW9ucyBhYm91dCB1c2luZyB0b3BpYwptb2RlbGluZyB0byBpZGVudGlmeSBsYXRlbnQgdGhlbWVzIGluIGEgYm9keSBvZiBzaG9ydC1mb3JtIHRleHQuClNwZWNpZmljYWxseToKCjEuICAqKipDYW4gc2hvcnQgdGV4dHMgcHJvdmlkZSBlbm91Z2ggY29udGV4dCB0byBhZGVxdWF0ZWx5IGluZm9ybSB0b3BpYwogICAgbW9kZWxpbmc/KioqCgpCb3RoIHRoZSBMREEgYW5kIFNUTSB0cmlhbHMgc3RydWdnbGVkIHRvIGRpZmZlcmVudGlhdGUgYmV0d2VlbiB1bmlxdWUKdG9waWNzLiBFdmVuIHdpdGggbWV0YWRhdGEgYWRkZWQsIHRoZSBTVE0gbW9kZWwgaW1wcm92ZWQgbGl0dGxlIG92ZXIgaXRzCkxEQSBjb3VudGVycGFydC4gVGhlIEJUTSBtb2RlbCwgaG93ZXZlciwgZGlzY292ZXJlZCBudW1lcm91cyB1bmlxdWUKdG9waWNzIG1pc3NlZCBieSB0aGUgb3RoZXIgdHdvIG1vZGVscy4gVGhlIHJlc3VsdHMgc2hvd2VkIGJvdGggc2VwYXJhdGUKdG9waWNzIGFuZCB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHRoZSB0ZXJtcyB3aXRoaW4gdGhvc2UgdG9waWNzLgpUaGVyZWZvcmUsIEJUTSBpcyBhIHNvdW5kIGNob2ljZSB0byBpbmZvcm0gc2hvcnQgdGV4dCB0b3BpYyBtb2RlbGluZy4KCjIuICAqKipDYW4gc2hvcnQgdGV4dCBtZXRhZGF0YSBiZSB1c2VkIHRvIGF1Z21lbnQgdGhlIGFjY3VyYWN5IG9mIHRoZXNlCiAgICB0b3BpYyBtb2RlbHM/KioqCgpUaGlzIGlzIHN0aWxsIHNvbWV3aGF0IHVua25vd24uIFRoZSBTVE0gbW9kZWwgdXNlZCBtZXRhZGF0YSBidXQgc2hvd2VkCmxpdHRsZSBpbXByb3ZlbWVudCBvdmVyIExEQSwgd2hpY2ggb25seSB1c2VkIGRvY3VtZW50LXRlcm0gZnJlcXVlbmNpZXMuCkFzIHRoZSBtZXRhZGF0YSBpcyBzdHJpcHBlZCBmcm9tIHRoZSBCVE0gbW9kZWwsIGl0IHJlbWFpbnMgdG8gYmUgc2VlbiBpZgp0aG9zZSB0d28gZWxlbWVudHMgY2FuIGJlIHJlLWpvaW5lZCBmb3IgY29tcGFyaXNvbi4KCjMuICAqKipDYW4gc2hvcnQgdGV4dHMgcHJvdmlkZSBlbm91Z2ggZmlkZWxpdHkgdG8gbWVhc3VyZSB0b3BpYwogICAgcHJldmFsZW5jZSBvdmVyIHRpbWU/KioqCgpUaGUgU1RNIG1vZGVsIGRhdGEgd2FzIHBsb3R0ZWQgb3ZlciB0aW1lIGFuZCBzaG93ZWQgaG93IHRvcGljIHByZXZhbGVuY2UKY2hhbmdlZC4gU28gd2hpbGUgaXQgaXMgcG9zc2libGUgdG8gZG8gdGhpcyB3aXRoIHNob3J0LWZvcm0gdGV4dCBkYXRhLAp0aGlzIGV4cGVyaW1lbnQgd2FzIHVuYWJsZSB0byBkbyB0aGlzIHdpdGggdGhlIEJUTSBtb2RlbC4KClRoaXMgcHJvamVjdCB3YXMgYWJsZSB0byBkZW1vbnN0cmF0ZSBxdWFsaXRhdGl2ZWx5IHRoYXQgc2hvcnQgdGV4dHMgY2FuCmJlIHVzZWQgdG8gZGlzY292ZXIgbGF0ZW50IHRvcGljcy4gVGhpcyBjb3VsZCBzYXZlIGVub3Jtb3VzIGFtb3VudHMgb2YKdGltZSBhbmQgcHJvY2Vzc2luZyByZXNvdXJjZXMgYXMgc2hvd24gYnkgaG93IHF1aWNrbHkgbW9zdCBvZiB0aGUgbW9kZWxzCnJhbiBhZ2FpbnN0IGEgY29ycHVzIG9mIG92ZXIgMzBLIGFydGljbGVzLgoKIyMjIDUuNCBMaW1pdGF0aW9ucwoKKipRdWFsaXRhdGl2ZSB2cyBRdWFudGl0YXRpdmUuKiogVGhpcyBzdHVkeSBjb21wYXJlZCBxdWFsaXRhdGl2ZSBtZXRyaWNzCm9uIGRpZmZlcmVuY2VzIGJldHdlZW4gdGVybXMsIHRvcGljcywgYW5kIHByb3BvcnRpb25zIG92ZXIgdGltZS4KUXVhbnRpdGF0aXZlIG1ldHJpY3Mgd2VyZSBub3QgZXhwbG9yZWQgYXMgaXQgd291bGQgaGF2ZSBzaWduaWZpY2FudGx5CmluY3JlYXNlZCB0aGUgc2NvcGUgb2YgdGhlIHByb2plY3QuIE1ldHJpY3Mgc3VjaCBhcyBtb2RlbCBlZmZpY2llbmN5LApwcmVjaXNpb24sIGFuZCB0b3BpYyBjb2hlcmVuY2UgYXJlIG1lYXN1cmVzIHRoYXQgY291bGQgcHJvdmlkZQphZGRpdGlvbmFsIGNvbXBhcmF0aXZlIGluc2lnaHRzIGZvciB0aGVzZSBtb2RlbHMuCgoqKksgZGV0ZXJtaW5hdGlvbiBmb3IgQlRNLioqIFdoaWxlIGEgbWF0aGVtYXRpY2FsIG1ldGhvZCBleGlzdHMgdG8KZGV0ZXJtaW5lIEsgdmFsdWVzIGZvciB0aGUgQlRNIG1vZGVsLCB0aGV5IHdlcmUgdG9vIGFkdmFuY2VkIGZvciB0aGlzCnByb2plY3QgYW5kIGFyZSBub3QgYnVpbHQgaW50byB0aGUgUiBwYWNrYWdlIGluIGEgd2F5IHRoYXQgbWFrZXMgdGhlbQpzaW1wbGUgdG8gZXh0cmFjdC4gQXMgYSByZXN1bHQsIHRoZSBMREEgYW5kIFNUTSBLIHZhbHVlcyB3ZXJlIGNvbXBhcmVkCnRvIGRlcml2ZSB0aGUgb3B0aW11bSBLLgoKKipBZGRpdGlvbmFsIHNob3J0IHRleHQgbW9kZWxzLioqIFRoZXJlIGFyZSBvdGhlciBtb2RlbHMgKHN1Y2ggYXMKc3RMREEtQykgdG8gZGVyaXZlIGxhdGVudCB0b3BpY3MgZnJvbSBzaG9ydCB0ZXh0cywgYnV0IHRoZXkgZG9uJ3QgaGF2ZQpzcGVjaWZpYyBwYWNrYWdlcyB0aGF0IHdvcmsgaW4gdGhlIFIgZW52aXJvbm1lbnQuCgoqKk1ldGFkYXRhLioqIFRoZSBvbmx5IGFkZGl0aW9uYWwgdmFyaWFibGUgY2FwdHVyZWQgdG8gZGlzdGluZ3Vpc2gKYmV0d2VlbiBhcnRpY2xlcyB3YXMgcHVibGljYXRpb24gZGF0ZS4gT3RoZXIgdmFyaWFibGVzIGF2YWlsYWJsZSBhcmUKYXV0aG9yLCBsZW5ndGggb2YgYXJ0aWNsZSwgbnVtYmVyIG9mIGNvbW1lbnRzLCBhbmQgbnVtYmVyIG9mIGxpa2VzLgpUaGVzZSB2YXJpYWJsZXMgY291bGQgYmUgdXNlZCBpbiBmdXR1cmUgZXhwZXJpbWVudHMgdG8gYWRkIG1vcmUgbnVhbmNlZApjb250ZXh0IHRvIGVhY2ggYXJ0aWNsZS4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgNi4gUkVGRVJFTkNFUwoKMS4gIEJhaWwsIEMuICgyMDE5KS4gKlRvcGljIE1vZGVsaW5nKi4gVGV4dCBhcyBEYXRhIENvdXJzZS4gUmV0cmlldmVkCiAgICBBcHJpbCAyMiwgMjAyMiwgZnJvbQogICAgPGh0dHBzOi8vc2ljc3MuaW8vMjAxOS9tYXRlcmlhbHMvZGF5My10ZXh0LWFuYWx5c2lzL3RvcGljLW1vZGVsaW5nL3JtYXJrZG93bi9Ub3BpY19Nb2RlbGluZy5odG1sPgoyLiAgV2lqZmZlbHMsIEouICgyMDIxKS4gKkJUTTogQml0ZXJtIHRvcGljIG1vZGVscyBmb3Igc2hvcnQgdGV4dCAtCiAgICBjcmFuLnItcHJvamVjdC5vcmcqLiBDUkFOIC0gUGFja2FnZSBCVE0uIFJldHJpZXZlZCBBcHJpbCAyNywgMjAyMiwKICAgIGZyb20gPGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9CVE0vQlRNLnBkZj4KMy4gIEppcGVuZywgUS4sIFpoZW55dSwgUS4sIFl1biwgTC4sIFl1bmhhbywgWS4sICYgWGluZG9uZywgVy4gKDIwMTkpLgogICAgKlNob3J0IHRleHQgdG9waWMgbW9kZWxpbmcgdGVjaG5pcXVlcywgYXBwbGljYXRpb25zLCBhbmQKICAgIHBlcmZvcm1hbmNlOiBBIHN1cnZleSouIGFyWGl2Lm9yZy4gUmV0cmlldmVkIEFwcmlsIDIxLCAyMDIyLCBmcm9tCiAgICA8aHR0cHM6Ly9kb2kub3JnLzEwLjQ4NTUwL2FyWGl2LjE5MDQuMDc2OTU+CjQuICBTaWxnZSwgSi4sICYgUm9iaW5zb24sIEQuICgyMDE3KS4gKlRvcGljIE1vZGVsaW5nOiBUZXh0IG1pbmluZyB3aXRoCiAgICBSKi4gNiBUb3BpYyBtb2RlbGluZyBcfCBUZXh0IE1pbmluZyB3aXRoIFIuIFJldHJpZXZlZCBBcHJpbCAzMCwKICAgIDIwMjIsIGZyb20gPGh0dHBzOi8vd3d3LnRpZHl0ZXh0bWluaW5nLmNvbS90b3BpY21vZGVsaW5nLmh0bWw+CjUuICBZYW4sIFguLCBHdW8sIEouLCBMYW4sIFkuLCAmIENoZW5nLCBYLiAoMjAxMykuICpBIGJpdGVybSB0b3BpYyBtb2RlbAogICAgZm9yIHNob3J0IHRleHRzKi4gWGlhb2h1aSBZYW4ncyBIb21lcGFnZS4gUmV0cmlldmVkIEFwcmlsIDI3LCAyMDIyLAogICAgZnJvbSA8aHR0cDovL3hpYW9odWl5YW4uZ2l0aHViLmlvL3BhcGVyL0JUTS1XV1cxMy5wZGY+Cg==