1. PREPARE
To help us better understand the context, questions, and data sources we’ll be using in Unit 3, this section will focus on the following topics:
- Context. As context for our analysis this week, we’ll review several related papers by my colleagues relevant to our analysis of MOOC-Ed discussion forums.
- Questions. We’ll also examine what insight topic modeling can provide to a question that we asked participants answer in their professional learning teams (PLTs).
- Project Setup. This should be very familiar by now, but we’ll set up a new R project and install and load the required packages for the topic modeling walkthrough.
1a. Context
Participating in a MOOC and Professional Learning Team: How a Blended Approach to Professional Development Makes a Difference

Full text: https://www.learntechlib.org/p/195234/
Abstract
Massive Open Online Courses for Educators (MOOC-Eds) provide opportunities for using research-based learning and teaching practices, along with new technological tools and facilitation approaches for delivering quality online professional development. The Teaching Statistics Through Data Investigations MOOC-Ed was built for preparing teachers in pedagogy for teaching statistics, and it has been offered to participants from around the world. During 2016-2017, professional learning teams (PLTs) were formed from a subset of MOOC-Ed participants. These teams met several times to share and discuss their learning and experiences. This study focused on examining the ways that a blended approach to professional development may result in similar or different patterns of engagement to those who only participate in a large-scale online course. Results show the benefits of a blended learning environment for retention, engagement with course materials, and connectedness within the online community of learners in an online professional development on teaching statistics. The findings suggest the use of self-forming autonomous PLTs for supporting a deeper and more comprehensive experience with self-directed online professional developments such as MOOCs. Other online professional development courses, such as MOOCs, may benefit from purposely suggesting and advertising, and perhaps facilitating, the formation of small face-to-face or virtual PLTs who commit to engage in learning together.
Data Source & Analysis
All peer interaction, including peer discussion, take place within discussion forums of MOOC-Eds, which are hosted using the Moodle Learning Management System. To build the dataset you’ll be using for this walkthrough, the research team wrote a query for Moodle’s MySQL database, which records participants’ user-logs of activity in the online forums. This sql query combines separate database tables containing postings and comments including participant IDs, timestamps, discussion text and other attributes or “metadata.”
For further description of the forums and data retrieval process, see also the following papers:
Kellogg, S., & Edelmann, A. (2015). Massively Open Online Course for Educators (MOOC‐Ed) network dataset. British journal of educational technology, 46(5), 977-983.
Ezen-Can, A., Boyer, K. E., Kellogg, S., & Booth, S. (2015, March). Unsupervised modeling for understanding MOOC discussion forums: a learning analytics approach. In Proceedings of the fifth international conference on learning analytics and knowledge (pp. 146-150).
Kellogg, S., Booth, S., & Oliver, K. (2014). A social network perspective on peer supported learning in MOOCs for educators. International Review of Research in Open and Distributed Learning, 15(5), 263-289.
Summary of Key Findings
The following highlight some key findings related to the discussion forums in the papers cited above:
- MOOCs designed specifically for K-12 teachers can provide positive self-directed learning experiences and rich engagement in discussion forums that help form online communities for educators.
- Analysis of discussion forum data in TSDI provided a very clear picture of how enthusiastic many PLT members and leaders were to talk to others in the online community. They posed their questions and shared ideas with others about teaching statistics throughout the units, even though they were also meeting synchronously several times with their colleagues in small group PLTs.
- Findings on knowledge construction demonstrated that over half of the discussions in both courses moved beyond sharing information and statements of agreement and entered a process of dissonance, negotiation and co-construction of knowledge, but seldom moved beyond this phase in which new knowledge was tested or applied. These findings echo similar research on difficulties in promoting knowledge construction in online settings.
- Topic modeling provides more interpretable and cohesive models for discussion forums than other popular unsupervised modeling techniques such as k-means and k-medoids clustering algorithms.
1b. Guiding Questions
For the paper, Participating in a MOOC and Professional Learning Team: How a Blended Approach to Professional Development Makes a Difference, the researchers were interested in unpacking how participants who enrolled in the Teaching Statistics through Data Investigations MOOC-Ed might benefit from also being in a smaller group of professionals committed to engaging in the same professional development. The specific research question for this paper was:
What are the similarities and differences between how PLT members and Non-PLT online participants engage and meet course goals in a MOOC-Ed designed for educators in secondary and collegiate settings?
Dr. Hollylynne Lee and the TSDI team also developed a facilitation guide designed specifically for PLT teams to help groups synthesize the ideas in the course and make plans for how to implement new strategies in their classroom in order to impact students’ learning of statistics. One question PLT members were asked to address was:
What ideas or issues emerged in the discussion forums this past week?
For this walkthrough, we will further examine that question through the use of topic modeling.
And just to reiterate yet again from Unit 1, one overarching question we’ll explore throughout this course, and that Silge and Robinson (2018) identify as a central question to text mining and natural language processing, is:
How do we to quantify what a document or collection of documents is about?
1c. Set Up
As highlighted in Chapter 6 of Data Science in Education Using R (DSIEUR), one of the first steps of every workflow should be to set up a “Project” within RStudio. This will be your “home” for any files and code used or created in Unit 2.
You are welcome to continue using the same project created for Unit 1, or create an entirely new project for Unit 2. However, after you’ve created your project open up a new R script, and load the following packages that we’ll be needing for this walkthrough:
library(tidyverse)
library(tidytext)
library(SnowballC)
library(topicmodels)
library(stm)
library(ldatuning)
library(knitr)
library(LDAvis)
At the end of this week, I encourage you share with me your R script as evidence that you have complete the walkthrough. Although I highly recommend that that you manually type the code shared throughout this walkthrough, for large blocks of text it may be easier to copy and paste.
2. WRANGLE
As noted previously, data wrangling involves some combination of cleaning, reshaping, transforming, and merging data (Wickham & Grolemund, 2017). This week we’ll revisit tidying and tokenizing text using the tidytext package, but are also introduced to the the stm package. This package makes use of tm text mining package to preprocess text (e.g., removing punctuation, stop words, etc.) and will also be our first introduction to word stemming.
- Import Data. We’ll be working with .csv files this week and the
read_csv() function but will introduce a new argument for changing column types.
- Cast a DTM. We revisit the
tidytext package to “tidy” and tokenize our forum data and introduce the cast_dtm() function to create the document term matrix (dtm) need for topic modeling.
- To Stem or not to STEM? We conclude our data wrangling by also introducing the
textProcessor() function for preprocessing and discuss the pros and cons of word stemming.
2a. Import Forum Data
To get started, we need to import, or “read”, our data into R. The function used to import your data will depend on the file format of the data you are trying to import. First, however, you’ll need to do the following:
- Download the
ts_forum_data.csv file we’ll be using for this Unit from our NCSU Moodle course site.
- Create a folder in the directory on your computer where you stored your R Project and name it “data”.
- Add the file to your data folder.
- Check your Files tab in RStudio to verify that your file is indeed in your data folder.
Now let’s read our data into our Environment using the read_csv() function and assign it to a variable name so we can work with it like any other object in R.
ts_forum_data <- read_csv("data/ts_forum_data.csv",
col_types = cols(course_id = col_character(),
forum_id = col_character(),
discussion_id = col_character(),
post_id = col_character()
)
)
By default, many of the columns like course_id and forum_id are read in as numeric data. For our purposes, we plan to treat them as unique identifiers or names for out courses, forums, discussions, and posts. The read_csv() function has a handy col_types = argument changing the column types from numeric to characters.
2b. Cast a Document Term Matrix
In this section we’ll revisit some familiar tidytext functions used in Units 1 & 2 for tidying and tokenizing text and introduce some new functions from the stm package for processing text and transforming our data frames into new data structures required for topic modeling.
Functions Used
tidytext functions
unnest_tokens() splits a column into tokens
anti_join() returns all rows from x without a match in y and used to remove stop words from out data.
cast_dtm() takes a tidied data frame take and “casts” it into a document-term matrix (dtm)
dplyr functions
count() lets you quickly count the unique values of one or more variables
group_by() takes a data frame and one or more variables to group by
summarise() creates a summary of data using arguments like sum and mean
stm functions
textProcessor() takes in a vector or column of raw texts and performs text processing like removing punctuation and word stemming.
prepDocuments() performs several corpus manipulations including removing words and renumbering word indices
Tidying Text
Prior to topic modeling, we have a few remaining steps to tidy our text that hopefully should feel familiar by this point. If you recall from Chapter 1 of Text Mining With R, these preprocessing steps include:
- Transforming our text into “tokens”
- Removing unnecessary characters, punctuation, and whitespace
- Converting all text to lowercase
- Removing stop words such as “the”, “of”, and “to”
Let’s tokenize our forum text and by using the familiar unnest_tokens() and remove stop words per usual:
forums_tidy <- ts_forum_data %>%
unnest_tokens(output = word, input = post_content) %>%
anti_join(stop_words, by = "word")
forums_tidy
## # A tibble: 192,159 x 14
## course_id course_name forum_id forum_name discussion_id discussion_name
## <chr> <chr> <chr> <chr> <chr> <chr>
## 1 9 Teaching Statis~ 126 Investigat~ 6822 Not much compa~
## 2 9 Teaching Statis~ 126 Investigat~ 6822 Not much compa~
## 3 9 Teaching Statis~ 126 Investigat~ 6822 Not much compa~
## 4 9 Teaching Statis~ 126 Investigat~ 6822 Not much compa~
## 5 9 Teaching Statis~ 126 Investigat~ 6822 Not much compa~
## 6 9 Teaching Statis~ 126 Investigat~ 6822 Not much compa~
## 7 9 Teaching Statis~ 126 Investigat~ 6822 Not much compa~
## 8 9 Teaching Statis~ 126 Investigat~ 6822 Not much compa~
## 9 9 Teaching Statis~ 126 Investigat~ 6822 Not much compa~
## 10 9 Teaching Statis~ 126 Investigat~ 6822 Not much compa~
## # ... with 192,149 more rows, and 8 more variables: discussion_creator <dbl>,
## # discussion_poster <dbl>, discussion_reference <chr>, parent_id <dbl>,
## # post_date <chr>, post_id <chr>, post_title <chr>, word <chr>
Now let’s do a quick word count to see some of the most common words used throughout the forums. This should get a sense of what we’re working with and later we’ll need these word counts for creating our document term matrix for topic modeling:
forums_tidy %>%
count(word, sort = TRUE)
## # A tibble: 13,620 x 2
## word n
## <chr> <int>
## 1 students 6841
## 2 data 4365
## 3 statistics 3103
## 4 school 1488
## 5 questions 1470
## 6 class 1426
## 7 font 1311
## 8 span 1267
## 9 time 1253
## 10 style 1150
## # ... with 13,610 more rows
Terms like “students,” “data,” and “class” are about what we would have expected from a course teaching statistics. The term “agree” and “time” however, are not so intuitive and worth a quick look as well.
✅ Comprehension Check
Use the filter() and grepl() functions introduced in Unit 1. Section 3b to filter for rows in our ts_forum_data data frame that contain the terms “agree” and “time” and another term or terms of your choosing. Select a random sample of 10 posts using the sample_n() function for your terms and answer the following questions:
- What, if anything, do these posts have in common?
- These posts are all replies to a separate discussion. Depending on the words used you may find similarities for the original question.
- What topics or themes might be apparent, or do you anticipate emerging, from our topic modeling?
- Lessons that the participants worked together on, or shared from past experience. Also, statistical models that are useful for explaning hard to understand concepts.
Your output should look something like this:
## # A tibble: 10 x 1
## post_content
## <chr>
## 1 "I couldn't agree more about how fabulously brilliant i think this lesson is~
## 2 "Great point! I agree that it was interesting the students only wanted to do~
## 3 "Hi Stephanie I agree with you it is a shame that too much of our focus ne~
## 4 "I thought the same thing when watching the video! I thought it was a good ~
## 5 "I also agree. The most beneficially part of this course for me was the exp~
## 6 "Sherri I agree with how this is helping you design your lesson plans. I ~
## 7 "I agree with this comment. Sometimes time constraints limit our ability to~
## 8 "Kelly- I love that you acknowledge the importance of still doing some thin~
## 9 "Doug I completely agree with you about how beneficial this online course ~
## 10 "I agree. I also think its a let less \\high stakes\\\" than a test. This wi~
Creating a Document Term Matrix
As highlighted in Chapter 5 of Text Mining with R, the topicmodels package and the Latent Dirichlet allocation (LDA) algorithm and LDA() function it uses expects document-term matrix as the data input.
Before we create a our document-term matrix, however, we have an important decision to make:
What do we consider to be a “document” in a MOOC-Ed discussion forum?
For example, we could consider each individual discussion post, or post_id in our data frame, as a document. It might also make sense to combine texts from all posts within each discussion, or disccussion_id, and consider that as a document since these posts are often interconnected an build off one another.
For now, however, let treat each individual post as a unique “document.” noted above, to create our document term matrix, we’ll need to first count() how many times each word occurs in each document, or post_id in our case, and create a matrix that contains one row per post as our original data frame did, but now contains a column for each word in the entire corpus and a value of n for how many times that word occurs in each post.
To create this document term matrix from our post counts, we’ll use the cast_dtm() function like so and assign it to the variable forums_dtm:
forums_dtm <- forums_tidy %>%
count(post_id, word) %>%
cast_dtm(post_id, word, n)
✅ Comprehension Check
Take a look at our forums_dtm object in the console and answer the following question:
- What “class” of object is
forums_dtm?
- It is a simple triplet matrix
- How many unique documents and terms are included our matrix?
- There are 57766 documents and 13620 terms in our Matrix
- Why might there be fewer documents/posts than were in our original data frame?
- Some documents are removed because they appear in the text 0 times.
- What exactly is meant by “sparsity”?
- Sparsity means that A term-document matrix whose terms are removed because they have at least a sparse percentage calculated by the term-frequency weighting.
class(forums_dtm)
## [1] "DocumentTermMatrix" "simple_triplet_matrix"
forums_dtm
## <<DocumentTermMatrix (documents: 5766, terms: 13620)>>
## Non-/sparse entries: 142641/78390279
## Sparsity : 100%
## Maximal term length: NA
## Weighting : term frequency (tf)
2c. To Stem or not to Stem?
Next we’ll need to prepare our original data set for structural topic modeling using the textProcessor() function. The stm package has a number of features that extend the functionality of the topicmodels package, including an argument for “stemming” words, which Schofield and Mimno (2016) describe as follows:
Stemming is a popular way to reduce the size of a vocabulary in natural language tasks by conflating words with related meanings. Specifically, stemming aims to convert words with the same “stem” or root (e.g “creative” and “creator”) to a single word type (“create”). Though originally developed in the context of information retrieval (IR) systems, stemmers are now commonly used as a preprocessing step in unsupervised machine learning tasks.
The rationale behind stemming is that it can dramatically reduce the number of words or terms to be modeled, which in theory should help simplify and improve the performance of your model. We’ll explore this assumption a little later in this section.
Processing and Stemming for STM
Like unnest_tokens(), the textProcessor() function includes several useful arguments for processing text like converting text to lowercase and removing punctuation and numbers. I’ve included several of these in the script below along with their defaults used if you do not explicitly specify in your function. Most of these are pretty intuitive and you can learn more by viewing the ?textProcessor documentation.
Let’s go ahead and process our discussion forum post_content in preparation for structural topic modeling:
temp <- textProcessor(ts_forum_data$post_content,
metadata = ts_forum_data,
lowercase=TRUE,
removestopwords=TRUE,
removenumbers=TRUE,
removepunctuation=TRUE,
wordLengths=c(3,Inf),
stem=TRUE,
onlycharacter= FALSE,
striphtml=TRUE,
customstopwords=NULL)
## Building corpus...
## Converting to Lower Case...
## Removing punctuation...
## Removing stopwords...
## Removing numbers...
## Stemming...
## Creating Output...
Note that the first argument the textProcessor function expects is the column in our data frame that contains the text to be processed, the second argument metadata = expects the data frame that contains the text of interest and uses the column names to label the metadata such as course ids and forum names. This meatdata can be used to improve the assignment of words to topics in a corpus and examine the relationship between topics and various covariates of interest.
Unlike the unnest_tokens() function, the output is not a nice tidy data frame. Topic modeling using the stm package requires a very unique set of inputs that are specific to the package. The following code will pull elements from the temp list that was created that will be required for the stm() function we’ll use in Section 4:
meta <- temp$meta
vocab <- temp$vocab
docs <- temp$documents
Stemming Tidy Text
Notice that the textProcessor stem argument we used above is set to TRUE by default. We haven’t introduced word stemming at this point because there is some debate about the actual value of this process. While words like “students” and “student” might make sense to collapse into their base word and actually make analyses and visualizations more concise and easier to interpret. Hvitfeldt and Silge (2021) note, however, that words like the following have dramatic differences in meaning, semantics, and use and could result in poor models or misinterpreted results:
- meaning and mean
- likely, like, liking
- university and universe
The first word pair is particularly relevant to discussion posts from our Teaching Statistics course data. In addition, collapsing words like “teachers” and “teaching” could dramatically alter the results from a topic model.
Schofield and Mimno (2016) specifically,
Despite their frequent use in topic modeling, we find that stemmers produce no meaningful improvement in likelihood and coherence and in fact can degrade topic stability.
For now, we will leave as is the forums_dtm we created earlier with words unstemmed, but what if we wanted to stem words in a “tidy” way?
Since the unnest_tokens() function does not (intentionally I believe) include a stemming function, one approach would be to use the wordStem() function from the snowballC package to either replace our words column with a word stems or create a new variable called stem with our stemmed words. Let’s do the latter and take a look at the original words and the stem that was produced:
stemmed_forums <- ts_forum_data %>%
unnest_tokens(output = word, input = post_content) %>%
anti_join(stop_words, by = "word") %>%
mutate(stem = wordStem(word))
stemmed_forums
## # A tibble: 192,159 x 15
## course_id course_name forum_id forum_name discussion_id discussion_name
## <chr> <chr> <chr> <chr> <chr> <chr>
## 1 9 Teaching Statis~ 126 Investigat~ 6822 Not much compa~
## 2 9 Teaching Statis~ 126 Investigat~ 6822 Not much compa~
## 3 9 Teaching Statis~ 126 Investigat~ 6822 Not much compa~
## 4 9 Teaching Statis~ 126 Investigat~ 6822 Not much compa~
## 5 9 Teaching Statis~ 126 Investigat~ 6822 Not much compa~
## 6 9 Teaching Statis~ 126 Investigat~ 6822 Not much compa~
## 7 9 Teaching Statis~ 126 Investigat~ 6822 Not much compa~
## 8 9 Teaching Statis~ 126 Investigat~ 6822 Not much compa~
## 9 9 Teaching Statis~ 126 Investigat~ 6822 Not much compa~
## 10 9 Teaching Statis~ 126 Investigat~ 6822 Not much compa~
## # ... with 192,149 more rows, and 9 more variables: discussion_creator <dbl>,
## # discussion_poster <dbl>, discussion_reference <chr>, parent_id <dbl>,
## # post_date <chr>, post_id <chr>, post_title <chr>, word <chr>, stem <chr>
You can see that words like “activity” and “activities” that occur frequently in our discussions have been reduced to the word stem “activ”. If you are interested in learning other approaches for word stemming in R, as well as reading a more in depth description of the stemming process, I highly recommend the Chapter 4 Stemming from Hvitfeldt and Silge (2021) book, Supervised Machine Learning for Text Analysis in R.
✅ Comprehension Check
Complete the following code using what we learned in the section on Creating a Document Term Matrix and answer the following questions:
- How many fewer terms are in our stemmed document term matrix?
- The stemmed corpus has 10,001 observations compared to the 192159 observations.
- Did stemming words significantly reduce the sparsity of the network?
- Yes the forums_dtm has 78390279 sparse documents and the stemmed_dtm has 57529581 sparse items. This is a 27% decrease in sparsity of the network.
Hint: Make sure your code includes stem counts rather than word counts.
stemmed_dtm <- ts_forum_data %>%
unnest_tokens(output = word, input = post_content) %>%
anti_join(stop_words, by = "word") %>%
mutate(stem = wordStem(word)) %>%
count(stem, sort = TRUE)
stemmed_dtm
## <<DocumentTermMatrix (documents: 5766, terms: 10001)>>
## Non-/sparse entries: 136185/57529581
## Sparsity : 100%
## Maximal term length: NA
## Weighting : term frequency (tf)
## <<DocumentTermMatrix (documents: 5766, terms: 13620)>>
## Non-/sparse entries: 142641/78390279
## Sparsity : 100%
## Maximal term length: NA
## Weighting : term frequency (tf)
## # A tibble: 10,001 x 2
## stem n
## <chr> <int>
## 1 student 7354
## 2 data 4365
## 3 statist 4161
## 4 question 2470
## 5 teach 1858
## 6 class 1738
## 7 school 1606
## 8 time 1457
## 9 learn 1372
## 10 font 1311
## # ... with 9,991 more rows
3. MODEL
This unit provides our first opportunity for modeling a text as data. In very simple terms, modeling involves developing a mathematical summary of a dataset. These summaries can help us further explore trends and patterns in our data.
In their book, Learning Analytics Goes to School, Krumm and Means (2018) describe two general types of modeling approaches used in the Data-Intensive Research workflow: unsupervised and supervised machine learning. In distinguishing between the two, they note:
Unsupervised learning algorithms can be used to understand the structure of one’s dataset. Supervised models, on the other hand, help to quantify relationships between features and a known outcome. Known outcomes are also commonly referred to as labels or dependent variables.
In Section 3 we focus on Topic Modeling, an unsupervised learning approach to automatically identify topics in a collection of documents. In fact, we’ll explore two different approaches to topic modeling, as well as strategies for identifying the “right” number of topics:
- Fitting a Topic Modeling with LDA. In this section we learn to use the
topicmodels package and associated LDA() function for unsupervised classification of our forum discussions to find natural groupings of words, or topics.
- Fitting a Structural Topic Model. We then explore the use of the
stm package and stm() function to fit our model and uses metadata about documents to improve the assignment of words to “topics” in a corpus.
- Choosing K. Finally, we wrap up Section 3 by learning about diagnostic properties like exclusivity, semantic coherence, and heldout likelihood for helping to select an appropriate number of topics.
3a. Fitting a Topic Modeling with LDA
Before running our first topic model using the LDA() function, let’s quick recap from our readings some basic principles behind Latent Dirichlet allocation and why LDA is of preferred over other automatic classification or clustering approaches.
Unlike simple forms of cluster analysis such as k-means clustering, LDA is a “mixture” model, which in our context means that:
- Every document contains a mixture of topics. Unlike algorithms like k-means, LDA treats each document as a mixture of topics, which allows documents to “overlap” each other in terms of content, rather than being separated into discrete groups. So in practice, this means that a discussion forum post could have an estimated topic proportion of 70% for Topic 1 (e.g. be mostly about a Topic 1), but also be partly about Topic 2.
- Every topic contains a mixture of words. For example, if we specified in our LDA model just 2 topics for our discussion posts, we might find that one topic seems to be about pedagogy while another is about learning. The most common words in the pedagogy topic might be “teacher”, “strategies”, and “instruction”, while the learning topic may be made up of words like “understanding” and “students”. However, words can be shared between topics and words like “statistics” or “assessment” might appear in both equally.
Similar to k-means other other simple clustering approaches, however, LDA does require us to specify a value of k for the number of topics in our corpus. Selecting k is no trivial matter and can greatly impact your results.
Since we don’t have a have strong rationale about the number of topics that might exist in discussion forums, let’s use the n_distinct() function from the dplyr package to find the number of unique forum names in our course data and run with that:
n_distinct(ts_forum_data$forum_name)
## [1] 21
Since it looks like there are 20 distinct discussion forums, we’ll use that as our value for the k = argument of the LDA(). Be patient while this runs, since the default setting of is to perform a large number of iterations.
n_distinct(ts_forum_data$forum_name)
## [1] 21
forums_lda <- LDA(forums_dtm,
k = 20,
control = list(seed = 588)
)
forums_lda
## A LDA_VEM topic model with 20 topics.
Note that we used the control = argument to pass a random number (588) to seed the assignment of topics to each word in our corpus. Since LDA is a stochastic algorithm that could have different results depending on where the algorithm starts, specified a seed for reproducibility and so we’re all seeing the same results every time we specify the same number of topics.
And tying back to our work in Unit 1, Bail (2020) notes that topic assignments for each word are updated in an iterative fashion and that LDA employs the Term Frequency-Inverse Document Frequency (TF-IDF) metric to assign probabilities.
3b. Fitting a Structural Topic Model
Bail notes that LDA, while perhaps the most common approach to topic modeling, is just one of many different types, including Dynamic Topic Models, Correlated Topic Models, Hierarchical Topic Models, and more recently, Structural Topic Modeling (STM). He argues that one reason STM has rising in popularity and use is that it employs meta data about documents to improve the assignment of words to topics in a corpus and that can be used to examine relationships between covariates and documents.
Also, since Julia Silge has indicated that STM is, “my current favorite implementation of topic modeling in R” and has built supports in the tidytext package for building structural topic models, this package definitely is worth discussing in this walkthrough. I also highly recommend her own walkthrough of the stm package: The game is afoot! Topic modeling of Sherlock Holmes stories as well as her follow up post, Training, evaluating, and interpreting topic models.
The stm Package
As we’ve seen above, STM produced an unusual temp textProcessor output that is unique to the stm package. And as you’ve probably already guessed, the stm() function for fitting a structural topic model does not take a fairly standard document term matrix like the LDA() function.
Before we fit our model, we’ll have to extract the elements from the temp object created after we processed our text. Specifically, the stm() function expects the following arguments:
documents = the document term matrix to be modeled in the native stm format
data = an optional data frame containing meta data for the prevalence and/or content covariates to include in the model
vocab = a character vector specifying the words in the corpus in the order of the vocab indices in documents.
Let’s go ahead and extract these elements:
docs <- temp$documents
meta <- temp$meta
vocab <- temp$vocab
And now use these elements to fit the model using the same number of topics for K that we specified for our LDA topic model. Let’s also take advantage of the fact that we can include the course_id and forum_id covariates in the prevealence = argument to help improve, in theory, our model fit:
forums_stm <- stm(documents=docs,
data=meta,
vocab=vocab,
prevalence =~ course_id + forum_id,
K=20,
max.em.its=25,
verbose = FALSE)
forums_stm
## A topic model with 20 topics, 5781 documents and a 7820 word dictionary.
As noted earlier, the stm package has a number of handy features. One of these is the plot.STM() function for viewing the most probable words assigned to each topic.
By default, it only shows the first 3 terms so let’s change that to 5 to help with interpretation:
plot.STM(forums_stm, n = 5)

Note that you can also just use plot() as well:
plot(forums_stm, n = 5)

✅ Comprehension Check
Fit a model for both LDA and STM using different values for K and answer the following questions:
#stm model with 10 topics
forums_stm2 <- stm(documents=docs,
data=meta,
vocab=vocab,
prevalence =~ course_id + forum_id,
K=10,
max.em.its=25,
verbose = FALSE)
forums_stm2
## A topic model with 10 topics, 5781 documents and a 7820 word dictionary.
n_distinct(ts_forum_data$forum_name)
## [1] 21
forums_lda2 <- LDA(forums_dtm,
k = 5,
control = list(seed = 588)
)
forums_lda2
## A LDA_VEM topic model with 5 topics.
- What topics appear to be similar to those using 20 topics for K?
- Knowing that you don’t have as much context as the researchers of this study do, how might you interpret one of these latent topics or themes using the key terms assigned?
- What topic emerged that seem dramatically different and how might you interpret this topic?
3c. Finding K
As alluded to earlier, selecting the number of topics for your model is a non-trivial decision and can dramatically impact your results. Bail (2018) notes that
The results of topic models should not be over-interpreted unless the researcher has strong theoretical apriori about the number of topics in a given corpus, or if the researcher has carefully validated the results of a topic model using both the quantitative and qualitative techniques described above.
There are several approaches to estimating a value for K and we’ll take a quick look at one from the ldatuning package and one from our stm package.
The FindTopicsNumber Function
The ldatuning package has functions for both calculating and plotting different metrics that can be used to estimate the most preferable number of topics for LDA model. It also conveniently takes the standard document term matrix object that we created from out tidy text and has the added benefit of running fairly quickly, especially compared to the function for finding K from the stm package.
Let’s use the defaults specified in the ?FindTopicNumber documentation and modify slightly get metrics for a sequence of topics from 10-75 counting by 5 and plot the output we saved using the FindTopicsNumber_plot() function:
k_metrics <- FindTopicsNumber(
forums_dtm,
topics = seq(10, 75, by = 5),
metrics = "Griffiths2004",
method = "Gibbs",
control = list(),
mc.cores = NA,
return_models = FALSE,
verbose = FALSE,
libpath = NULL
)
FindTopicsNumber_plot(k_metrics)
Note that the FindTopicNumbers() function contains three additional metrics for calculating metrics that can be used to estimate the most preferable number of topics for LDA model. We used the Griffiths2004 metrics included in the default example and I’ve also found this to produce the most interpretable results as show in the figure below:

As a general rule of thumb and overly simplistic heuristic, we’re looking for an inflection point in our plot which indicates an optimal number of topics to select for a value of K.
The findingK() Function
Finally, Bail (2018) notes that thestm package has a useful function called searchK which allows us to specify a range of values for k and outputs multiple goodness-of-fit measures that are “very useful in identifying a range of values for k that provide the best fit for the data.”
The syntax of this function is very similar to the stm() function we used above, except that we specify a range for k as one of the arguments. In the code below, we search all values of k between 10 and 30.
#I am not expecting you run this code as it will take too long
#findingk <- searchK(docs,
#vocab,
#K = c(5:15),
#data = meta,
#verbose=FALSE)
#plot(findingk)
Note that Running searchK() function on this corpus took all night on a pretty powerful MacBook Pro and crashed once as well, so I do not expect you to run this for the walkthrough. I ran a couple iterations and landed on between 5 and 15 with an optimal number of topics somewhere around 14:

Given the somewhat conflicting results, also somewhat selfishly and for the same of simplicity for this walkthrough, I’m just going to stick with the rather arbitrary selection of 20 topics for the remainder of this Unit.
The LDAvis Explorer
One final tool that I want to introduce from the stm package is the toLDAvis() function which provides a great visualizations for exploring topic and word distributions using LDAvis topic browser:
toLDAvis(mod = forums_stm, docs = docs)
## Loading required namespace: servr
As you can see from the browser screen shot below, our current stm model of 20 topics is resulting in a lot of overlap among topics and suggest that 20 may not be an optimal number of topics, as other approaches for finding k also suggests:

4. EXPLORE
Silge and Robinson (2018) note that fitting at topic model is the “easy part.” The hard part is making sense of the model results and that the rest of the analysis involves exploring and interpreting the model using a variety of approaches which we’ll walkthrough in in this section.
Bail (2018) cautions, however, that:
…post-hoc interpretation of topic models is rather dangerous… and can quickly come to resemble the process of “reading tea leaves,” or finding meaning in patterns that are in fact quite arbitrary or even random.
4a. Exploring Beta Values
Hidden within this forums_lda topic model object we created are per-topic-per-word probabilities, called β (“beta”). It is the probability of a term (word) belonging to a topic.
Let’s take a look at the 5 most likely terms assigned to each topic, i.e. those with the largest β values using the terms() function from the topicmodels package:
terms(forums_lda, 5)
## Topic 1 Topic 2 Topic 3 Topic 4 Topic 5 Topic 6
## [1,] "dice" "resources" "students" "statistics" "age" "stats"
## [2,] "trials" "statistics" "kids" "math" "tv" "students"
## [3,] "fair" "teaching" "understand" "mathematics" "coasters" "statistics"
## [4,] "sasi" "unit" "standard" "students" "roller" "class"
## [5,] "level" "mooc" "test" "common" "steel" "ap"
## Topic 7 Topic 8 Topic 9 Topic 10 Topic 11 Topic 12
## [1,] "students" "gapminder" "students" "li" "td" "students"
## [2,] "school" "students" "software" "strong" "difference" "time"
## [3,] "statistics" "td" "technology" "href" "class" "technology"
## [4,] "middle" "love" "statistical" "https" "1" "school"
## [5,] "level" "videos" "calculator" "target" "5" "data"
## Topic 13 Topic 14 Topic 15 Topic 16 Topic 17 Topic 18
## [1,] "questions" "students" "data" "font" "task" "students"
## [2,] "students" "data" "students" "normal" "students" "sample"
## [3,] "answer" "real" "questions" "text" "data" "results"
## [4,] "question" "agree" "set" "0px" "tasks" "size"
## [5,] "answers" "activity" "collecting" "51" "mind" "data"
## Topic 19 Topic 20
## [1,] "div" "span"
## [2,] "http" "style"
## [3,] "href" "height"
## [4,] "https" "font"
## [5,] "target" "0"
Even though we’ve somewhat arbitrarily selected the number of topics for our corpus, some these topics or themes are fairly intuitive to interpret. For example:
Topic 11 (technology, students, software, program, excel) seems to be about students use of technology including software programs like excel;
Topic 9 (questions, kids, love, gapminder, sharing) seems to be about the gapminder activity from the MOOC-Ed and kids enjoyment of it; and
Topic 18 (data, students, collect, real, sets) seems to be about student collection and use of real world data sets.
Not surprisingly, the tidytext package has a handy function conveniently name tidy() to convert our lda model to a tidy data frame containing these beta values for each term:
# convert to a tidy data frame with Beta values
tidy_lda <- tidy(forums_lda)
tidy_lda
## # A tibble: 272,400 x 3
## topic term beta
## <int> <chr> <dbl>
## 1 1 2015 1.01e-234
## 2 2 2015 7.71e- 4
## 3 3 2015 2.50e- 4
## 4 4 2015 3.97e- 4
## 5 5 2015 3.01e- 4
## 6 6 2015 3.39e- 17
## 7 7 2015 2.25e- 52
## 8 8 2015 2.10e- 39
## 9 9 2015 1.31e- 31
## 10 10 2015 7.11e- 5
## # ... with 272,390 more rows
Obviously, it’s not very easy to interpret what the topics are about from a data frame like this so let’s borrow code again from Chapter 8.4.3 Interpreting the topic model in Text Mining with R to examine the top 5 terms for each topic and then look at this information visually:
# visually interpret top terms for each topic
top_terms <- tidy_lda %>%
group_by(topic) %>%
slice_max(beta, n = 5, with_ties = FALSE) %>%
ungroup() %>%
arrange(topic, -beta)
top_terms %>%
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")

4b. Exploring Gamma Values
Now that we have a sense of the most common words associated with each topic, let’s take a look at the topic prevalence in our MOOC-Ed discussion forum corpus, including the words that contribute to each topic we examined above.
Also, hidden within our forums_lda topic model object we created are per-document-per-topic probabilities, called γ (“gamma”). This provides the probabilities that each document is generated from each topic, that gamma matrix. We can combine our beta and gamma values to understand the topic prevalence in our corpus, and which words contribute to each topic.
To do this, we’re going to borrow some code from the Silge (2018) post, Training, evaluating, and interpreting topic models.
First, let’s create two tidy data frames for our beta and gamma values
td_beta <- tidy(forums_lda)
td_gamma <- tidy(forums_lda, matrix = "gamma")
td_beta
## # A tibble: 272,400 x 3
## topic term beta
## <int> <chr> <dbl>
## 1 1 2015 1.01e-234
## 2 2 2015 7.71e- 4
## 3 3 2015 2.50e- 4
## 4 4 2015 3.97e- 4
## 5 5 2015 3.01e- 4
## 6 6 2015 3.39e- 17
## 7 7 2015 2.25e- 52
## 8 8 2015 2.10e- 39
## 9 9 2015 1.31e- 31
## 10 10 2015 7.11e- 5
## # ... with 272,390 more rows
td_gamma
## # A tibble: 115,320 x 3
## document topic gamma
## <chr> <int> <dbl>
## 1 11295 1 0.00188
## 2 12711 1 0.000237
## 3 12725 1 0.0256
## 4 12733 1 0.00219
## 5 12743 1 0.00746
## 6 12744 1 0.00374
## 7 12756 1 0.0256
## 8 12757 1 0.00276
## 9 12775 1 0.00276
## 10 12816 1 0.00276
## # ... with 115,310 more rows
Next, we’ll adopt Julia’s code wholesale to create a filtered data frame of our top_terms, join this to a new data frame for gamma-terms and create a nice clean table using the kabel() function knitr package:
top_terms <- td_beta %>%
arrange(beta) %>%
group_by(topic) %>%
top_n(7, beta) %>%
arrange(-beta) %>%
select(topic, term) %>%
summarise(terms = list(term)) %>%
mutate(terms = map(terms, paste, collapse = ", ")) %>%
unnest()
## Warning: `cols` is now required when using unnest().
## Please use `cols = c(terms)`
gamma_terms <- td_gamma %>%
group_by(topic) %>%
summarise(gamma = mean(gamma)) %>%
arrange(desc(gamma)) %>%
left_join(top_terms, by = "topic") %>%
mutate(topic = paste0("Topic ", topic),
topic = reorder(topic, gamma))
gamma_terms %>%
select(topic, gamma, terms) %>%
kable(digits = 3,
col.names = c("Topic", "Expected topic proportion", "Top 7 terms"))
| Topic 7 |
0.098 |
students, school, statistics, middle, level, questions, understanding |
| Topic 6 |
0.087 |
stats, students, statistics, class, ap, teach, school |
| Topic 2 |
0.085 |
resources, statistics, teaching, unit, mooc, ideas, learning |
| Topic 14 |
0.083 |
students, data, real, agree, activity, time, analyze |
| Topic 13 |
0.070 |
questions, students, answer, question, answers, correct, results |
| Topic 15 |
0.064 |
data, students, questions, set, collecting, collection, question |
| Topic 8 |
0.061 |
gapminder, students, td, love, videos, video, top |
| Topic 4 |
0.057 |
statistics, math, mathematics, students, common, teaching, teach |
| Topic 17 |
0.055 |
task, students, data, tasks, mind, activity, statistical |
| Topic 18 |
0.050 |
students, sample, results, size, data, simulation, statistical |
| Topic 9 |
0.048 |
students, software, technology, statistical, calculator, excel, learn |
| Topic 12 |
0.046 |
students, time, technology, school, data, student, video |
| Topic 3 |
0.043 |
students, kids, understand, standard, test, box, deviation |
| Topic 19 |
0.028 |
div, http, href, https, target, _blank, class |
| Topic 11 |
0.027 |
td, difference, class, 1, 5, 2, align |
| Topic 10 |
0.026 |
li, strong, href, https, target, _blank, statistics |
| Topic 1 |
0.019 |
dice, trials, fair, sasi, level, framework, sophistication |
| Topic 5 |
0.019 |
age, tv, coasters, roller, steel, coaster, hours |
| Topic 16 |
0.018 |
font, normal, text, 0px, 51, 255, style |
| Topic 20 |
0.013 |
span, style, height, font, 0, line, margin |
And let’s also compare this to the most prevalent topics and terms from our forums_stm model that we created using the plot() function:
plot(forums_stm, n = 7)

4c. Reading the Tea Leaves
Recognizing that topic modeling is best used as a “tool for reading” and provides only an incomplete answer to our overarching, “How do we quantify what a corpus is about?”, the results do suggest some potential topics that have emerges as well as some areas worth following up on.
Specifically, looking at some of the common clusters of words for the more prevalent topics suggest that some key topics or “latent themes” (renamed in bold) might include:
- Teaching Statistics: Unsurprising, given the course title, the topics most prevalent in both the
forums_stm and forums_lda models contains the terms “teach”, “students”, “statistics”. This could be an “overarching theme” but more likely may simply be just the residue of the course title though being sprinkled throughout the forums and deserves some follow up. Topics 8 from the LDA model may overlap with this topic as well.
- Course Utility: The second most prevalent Topics (13 and 2) in the
lda and stm models respectively, seem to potentially be about the usefulness of course “resources” like lessons, tools, videos, and activities. I’m wagering this might be a forum dedicated to course feedback. Topic 15 from the STM model also suggest this may be a broader theme.
- Using Real-World Data: Topics 18 & 12 from the LDA model particularly intrigue me and I’m wagering this is pretty positive sentiment among participants about the value and benefit of having students collect and analyze real data sets (e.g. Census data in Topic 1) and work on projects relevant to their real life. Will definitely follow up on this one.
- Technology Use: Several topics (6 & 11 from LDA and 8 & 19 from STM) appear to be about student use of technology and software like calculators and Excel for teaching statistics and using simulations. Topic 16 from LDA also suggest the use of the Common Online Data Analysis Platform (CODAP).
- Student Struggle & Engagement: Topic 15 from LDA and Topic 16 from STM also intrigue me and appear to be two opposite sides of perhaps the same coin. The former includes “struggle” and “reading” which suggests perhaps a barrier to teaching statistics while Topic 16 contains top stems like “engage”, “activ”, and “think” and may suggest participants anticipate activities may engage students.
To serve as a check on my tea leaf reading, I’m going to follow Bail’s recommendation to examine some of these topics qualitatively. The stm package has another useful function though exceptionally fussy function called findThoughts which extracts passages from documents within the corpus associate with topics that you specify.
The first line of code may not be necessary for your independent analysis, but because the textProcessor() function removed several documents during processing, the findthoughts() function can’t properly index the processed docs. This line of code found on stackoverflow removes documents from original ts_forum_data source that were removed during processing so there is a one-to-one correspondence with forums_stm and so you can use the function to find posts associated with a given topic.
Let’s slightly reduce our original data set to match our STM model, pass both to the findThoughts() function, and set our arguments to return n =10 posts from topics = 2 (i.e. Topic 2) that have at least 50% or thresh = 0.5 as a minimum threshold for the estimated topic proportion.
ts_forum_data_reduced <-ts_forum_data$post_content[-temp$docs.removed]
findThoughts(forums_stm,
texts = ts_forum_data_reduced,
topics = 2,
n = 10,
thresh = 0.5)
##
## Topic 2:
## Ronald- We are glad that you have identied resources that you want to return to. If you do bookmark the urls they are available on the web after the course is closed.
## The materials presented in this course have been great resources. I can't wait to use them int he future.
## Thank you for providing these links and resources. The resources you provided in this MOOC have been very beneficial. I look forward to utilizing them in future lessons.
## I would download all pdfs and bookmark sites you might want in the future.
## The Annenberg Learner - is a teaching PD website that not only has \Against All Odds\" but whole lessons PD programs you can also access free with additional instructional footage - teaching teachers. It's a great resource. There are also a lot of lesson plans. Another great resource is \"The Teaching Channel\" They have lesson plans along with video. "
## I wonder the same thing Donna. I am trying to save in my hard drive the materials that had been provided us something I could pull out in the near future for use or reference.
## I as well found my weaknesses and now have resources to improve.
## I'll add my voice to the chorus of gratitude. I've already used a couple of the activities presented here in my fall quarter course and I will definitely bookmark as many of the resources as possible to try to use in the future. Thank you!
## Thanks for the info. I will definitely download many of the resources I liked for future use. Having a bank of resources for statistics is crucial.
## I agree. The resources made available are excellent for future use. I printed out several that were in print form to use and refresh my memory.
Duplicate posts aside, this Course Utility topic returns posts there were expected based on my interpretation of the key terms for Topic 2. It looks like I may have read those tea leaves correctly.
Now let’s take a look at Topic 16 that we thought might be related to student engagement:
findThoughts(forums_stm,
texts = ts_forum_data_reduced,
topics = 16,
n = 10,
thresh = 0.5)
##
## Topic 16:
## For a final project two years ago I had a couple of students do a study on water. They used 4 types of water: tap Aquafina Poland Springs and Fiji. Conclusion: Fiji water tasted the best. It was interesting to note that Fiji water also works best for \snap freezing\" experiments. If you're not familiar with snap freezing try a you-tube search...pretty cool stuff! "
## If you have not yet explored the Animated Movie dataset in the Dive Into Data I highly recommend it. I have two graphs below that I created. I'd love to hear what worthwhile statistics tasks you could imagine getting students engaged in where making sense of one or both of these graphical displays of the data would come into play. <b>Graph 1: two boxplots of movie ratings for a sample of Pixar and DreamWorks movies. The green color scale shows the overall budget for producing the movie.</b> <img src=\@@PLUGINFILE@@/MovieratinngsCOmpareBoxplots.JPG\" alt=\"two box plots\" width=\"961\" height=\"530\" style=\"vertical-align:text-bottom; margin: 0 .5em;\" class=\"img-responsive\"> What statistical question might we pose to students to investigate? <b>Graph 2: Scatterplot of Profit vs. Production budget. Legend Color indicates whether the movie was produced by Pixar or Dreamworks.</b> <img src=\"@@PLUGINFILE@@/BudgetimpactonProfitMovies.JPG\" alt=\"scatterplot of profit vs. budget\" width=\"831\" height=\"570\" style=\"vertical-align:text-bottom; margin: 0 .5em;\" class=\"img-responsive\"> What statistical question might we pose to students to investigate? "
## If you have not yet explored the Animated Movie dataset in the <b>Dive Into Data</b> I highly recommend it. I have two graphs below that I created. I'd love to hear what worthwhile statistics tasks you could imagine getting students engaged in where making sense of one or both of these graphical displays of the data would come into play. <b>Graph 1: two boxplots of movie ratings for a sample of Pixar and DreamWorks movies. The green color scale shows the overall budget for producing the movie.</b> <img src=\https://place.fi.ncsu.edu/pluginfile.php?file=/36093/mod_forum/post/36848/MovieratinngsCOmpareBoxplots.JPG\" alt=\"two box plots\" width=\"961\" height=\"530\"> What statistical question might we pose to students to investigate? <b>Graph 2: Scatterplot of Profit vs. Production budget. Legend Color indicates whether the movie was produced by Pixar or Dreamworks.</b> <img src=\"https://place.fi.ncsu.edu/pluginfile.php?file=/36093/mod_forum/post/36848/BudgetimpactonProfitMovies.JPG\" alt=\"scatterplot of profit vs. budget\" width=\"831\" height=\"570\"> What statistical question might we pose to students to investigate? "
## The television activity is more basic for sixth grade to comprehend. In my county this past week was Digital Citizenship week. The first task was to talk about what social media they use. I would like to say here that I was shocked at some of the media sites my sixth graders use on a regular basis but that was another discussion. We created a dot plot of the social media used and how many used it. The next day the students were asked to think about how long they are on social media on a daily basis. We graphed the data using a histogram. My sixth graders discovered that they spend too much time on social media and not on their studies. Since data collect is not until a later unit this was a great way to introduce the types of graphs and measures of center in a way that absolutely applies to them. The airplane data was great for teaching students how to interact with large numbers. I would love to do a follow up investigation on flight cancellations since this topic impacts many travelers all to often. I have done the Old Faithful investigation that I found in the Connected Math Program Series with my eighth graders but this activity was not as interesting to them.
## I analyzed data for air quality using excel and found the activity valuable but very time consuming. Being my students would only have access to excel I used that program to download data into. The first problem my students will have is to find the data they want to analyze. For instance there are four locations to look at before considering the affect location may have on measurements. Are these representative of a larger area or affected by geographical features? After students download the data this would require their becoming familiar with the data and a discussion asking many questions. This would be good for developing “habits of mind” but may be overwhelming if care is not taken to allow them time to discern the possibilities. Second the amount of data requires a lot of work when using Excel. Most students have only used small bits of data to make graphs if at all. Now they have to move large bits of information which they should be able to graph with little direction. However this brings up new problems as their initial graphs were I made very busy and would not make sense to many. For instance a dot plot of all the data from say one location for a graph of ozone on primary axis and nitrogen on secondary access by location shows a comparison of these two variables by location. The graph is very busy and I don not think my students would think of breaking down the location data by finding the means for each month and plotting those to simplify and communicate any possible relationships. Making such a graph will show a trend (using linear regression) that most will think as a means to an end. They would see a slight increase over the period measured and incorrectly make connections to global warming and would tend to end their motivation for further investigation there. My challenge question to myself is “how to keep students engaged with the bigger ideas behind the data and not get overwhelmed?” What I anticipate is much confusion initially and keeping students motivated in higher thinking will be my greatest challenge. Second finding another data analysis program besides Excel and the necessary technology would be equally problematic. I would consider TUVA before Census at School and the tools supplied would be a better choice if the we have access to the technology. Lastly time for implementation of such lessons may be greater than I am allowed; I need to find some way to integrate many standards into each lesson that follow existing course objectives.
## I wonder if introducing more than two variables in a graph would be engaging with Khan Academy Pixar in a Box.
## HI. Great idea with the balloons. With my year 9 class I always pose the quesiton 'Who writes bigger Adults or Year 9?' We then discuss what I mean by bigger and we end up by defining bigger by length as it's easy to measure. We use a sentence that has all the letters of the alphabet (The quick brown fox jumps over the lazy dog) and measure the length from the first pen stroke to the furthest pen stoke. We then collect the data in a tally chart (continuous data) draw a histogram and calculate the average(s) Homework is then set to get two adults to do the same thing. Next lesson we do the same analysis and compare the results. You normally get an interesting set of responses and some pretty obvious bias if all the students bring 2 bits of data in. Great to get the students thinking and applying some statistical skills.
## What are some of the ways you have already used in your classroom to make statistics real? In collaboration with the another teacher at my previous school we found the USGS Data on Stream Flow (cubic feet per second) compared with the size of a watershed. We shared this data with our student. They created a scatterplot and calculated the line of regression. We then took our students outside. Using a cross-section we calculated the cubic feet per second of our stream. From there we could predict how large our watershed was. We then looked at the topographic map and looked at how big our watershed was.
## You could have students analyze each of the contacts. They could identify how many contacts they have how many of those contacts they use daily and how many of those contact they haven't used in the past six months. With these questions you could make them think about which factors might influence the variability between their classmates data among other things.
## Help me make use of this dreamworks vs pixar graph. What does the placement of the dots relative to the other dots have to do with anything?
It looks like my tea reading was a partially correct for Topic 16, though the results seem to be about a specific “Pepsi challenge” activity to conduct with students.
Finally, let’s look at posts from Topic 3 which we though might be an overarching theme about teaching statistics:
ts_forum_data_reduced <-ts_forum_data$post_content[-temp$docs.removed]
findThoughts(forums_stm,
texts = ts_forum_data_reduced,
topics = 3,
n = 10,
thresh = 0.5)
##
## Topic 3:
## In my college faculty are given a syllabus (internal course designers) which we can modify. And I do modify the content. The standard syllabus for third year college students is less than HS AP Stats. Some students will get another dose of Statistics in their fourth year which is good. I have seen the quality and degradation of content over the 14 years. It is too bad as stats is far more than a math course.
## I teach statistics in HS. Most of the students take it because it sounds easier than trig or calculus. One comment I get frequently every year is how they enjoy the relevance of the course. \It's the first math class where I didn't have to ask why do we need to know this?\" "
## I feel I have grown in my knowledge of statistics because of this course. I do not teach a course in stats but do have to teach a part to my math 3 students and always felt like I could be doing a better job. I feel much more confident that this year will be much better.
## In the\ Extend you Learning Area\" there were two sections that made me laugh and want to dance One of these was on Harry Potter. It was amazingly creative. <div id=\"page-content\" class=\"row-fluid\"><section id=\"region-main-course\" class=\"col-md-10\"><div class=\"region-main-inner\"><div role=\"main\"><div class=\"list_content\"><section class=\"course-section course_resource\"><div class=\"section-content\"><div class=\"resource_item\"><div class=\"bookmark_container\" id=\"bookmark_container_318\" style=\"float: left; margin: 5px 0; padding-right: 10px;\"><div class=\"resource_title\"><img src=\"https://s3.amazonaws.com/fi-assets/icons/video.png\" alt=\"https://s3.amazonaws.com/fi-assets/icons/video.png\" class=\"iconsmall\" style=\"width: 30px; height: 30px;\"><a target=\"_blank\" class=\"resourcelink\" data-objectid=\"370\" href=\"https://www.youtube.com/playlist?list=PLG6iFkLydgaqBCtH6jjMgrUQnernI1Yqh\">Student Projects in Statistics There was one on Harry Potter which was amazingly creative. </a> </div><a class=\"bookmarklink\" id=\"bookmarklink_318\" data-objectid=\"318\" data-bookmark=\"\" data-action=\"bookmark\" style=\"cursor: pointer;\"></a></div></div><div class=\"resource_item\"><div class=\"resource_body\"><div class=\"resource_title\"><img src=\"https://s3.amazonaws.com/fi-assets/icons/video.png\" alt=\"https://s3.amazonaws.com/fi-assets/icons/video.png\" class=\"iconsmall\" style=\"width: 30px; height: 30px;\"><a target=\"_blank\" class=\"resourcelink\" data-objectid=\"371\" href=\"https://www.youtube.com/playlist?list=PLG6iFkLydgaoelxWwAozvKOVWFcw3P0bi\">Student-created parodies about statistics The one to Soul Sister got me moving/</a> I think most people find Statistics dry and boring. Although it is hard to make learning entertaining all the time now and then would certainly be great. If nothing else it will may help keep students awake. <a target=\"_blank\" class=\"resourcelink\" data-objectid=\"371\" href=\"https://www.youtube.com/playlist?list=PLG6iFkLydgaoelxWwAozvKOVWFcw3P0bi\"></a></div><div class=\"a2a_kit a2a_default_style\"> <a aria-label=\"Share\" class=\"a2a_dd\" href=\"https://www.addtoany.com/share#url=https%3A%2F%2Fwww.youtube.com%2Fplaylist%3Flist%3DPLG6iFkLydgaoelxWwAozvKOVWFcw3P0bi&title=Student-created%20parodies%20about%20statistics&description=\"></a><span class=\"a2a_divider\"></span><a aria-label=\"Facebook\" rel=\"nofollow\" href=\"https://place.fi.ncsu.edu/\" target=\"_blank\" class=\"a2a_button_facebook\"><span class=\"a2a_img a2a_i_facebook\"></span></a><a aria-label=\"Twitter\" rel=\"nofollow\" href=\"https://place.fi.ncsu.edu/\" target=\"_blank\" class=\"a2a_button_twitter\"><span class=\"a2a_img a2a_i_twitter\"></span></a><a aria-label=\"Google+\" rel=\"nofollow\" href=\"https://place.fi.ncsu.edu/\" target=\"_blank\" class=\"a2a_button_google_plus\"><span class=\"a2a_img a2a_i_google_plus\"></span></a></div></div><div class=\"addtoany\" style=\"margin: 5px 0;\"> </div></div></div></section></div> </div> </div> </section> </div> "
## I prepare math teachers and couldn't agree more. Our pre-service teachers only take one statistics course and it isn't a methods course. I am going to try to incorporate some of the statistics methods into my technology course for pre-service math teachers.
## All these posts are relevant to my everyday teaching and the way I assess students and the material I use in class. I am often questioned about whether statistics is really mathematics and whether my class should count in the math department offerings. Of course it does because that is how statistics is labeled but teachers disagree. Clarity about how important this is and how it is and isn't like other mathematics classes is important. I am originally trained as a sociologist and find that even when teaching a \pure\" mathematics class I look for motivation or context to engage the students...after many years of teaching math I would not dare call myself a mathematician but when I get to delve into statistics I feel I integrate all my loves. "
## As a person who prepares math teachers I feel that we don't spend enough time with the pre-service teachers on Statistics. They only take one class and I am not convinced that class is giving them the tools needed to be a good Stats teacher. It is reassuring (although sad) that our university is not the only that doesn't provide enough guidance in statistics to our pre-service teachers.
## I totally agree. I teach both Alg 2 and AP Statistics. We don't teach anything beyond the basics of mean median and mode and most years stats is the first thing to get cut when there is a time crunch at the end of the year. We have to start from the very beginning in AP. I think one of the major road blocks is that teachers themselves don't know enough about statistics to know where to start when deciding what and how to teach statistics. We weren't required to take much as far as statistics is concerned and so the importance of the subject was never emphasized to us.
## I agree with you Janet. Our introductory course does teach some calculations but focuses on the concepts and interpretations. I have had students who have taken AP stats and through conversations I have learned that they enjoyed this class and learned a lot. I do believe that an AP stats course is different than an introductory stats course.
## I am very happy to have found this course. I am willing to admit that I am one of the educators ill-prepared to teach statistics. I have little to no experience and I have found it hard and expensive to get the training I need. I suffer from a lack of experience for two main reasons: my K-12 teachers had very little statistical knowledge and I was given the choice to take Statistics or not in college. I made the wrong choice. I just wanted to post this for all the others out there who may be feeling inadequate!
Looking at just the 10 posts returned, perhaps a better name for this topic would be Course Reflections on Teaching Statistics.
Unit Takeaway
In addition to some useful R packages and functions for the actual process of topic modeling, hopefully there are two main lessons I’m hoping you take away from this walkthrough:
- Topic modeling requires a lot of decisions. Beyond deciding on a value for K, there are a number of key decisions that you have to make that can dramatically affect your results. For example, to stem or not to stem? What qualifies as a document? What flavor or topic modeling is best suited to your data and research questions? How many iterations to run?
- Topic modeling is as much art as (data) science. As Bail (2018) noted, the term “topic” is somewhat ambitious, and topic models do not produce highly nuanced classification of texts. Once you’ve fit your model, interpreting your model requires some mental gymnastics and ideally some knowledge of the context from which the data came to help with interpretation of your topics. Moreover, the quantitative approaches for making the decisions highlighted above are imperfect and a good deal of human judgment required.
✅ Comprehension Check
Using the STM model you fit from the Section 3 [Comprehension Check] with a different value for K, use the approaches demonstrated in Section 4 to explore and interpret your topics and terms and revisit the following question:
# Ran LDA model with 5 topics
# find top 5 terms in the lda model
terms(forums_lda2, 5)
## Topic 1 Topic 2 Topic 3 Topic 4 Topic 5
## [1,] "font" "li" "students" "students" "data"
## [2,] "span" "href" "sample" "statistics" "students"
## [3,] "style" "strong" "data" "school" "task"
## [4,] "text" "https" "test" "teach" "questions"
## [5,] "normal" "target" "size" "teaching" "question"
# convert to a tidy data frame with Beta values
tidy_lda2 <- tidy(forums_lda2)
# visually interpret top terms for each topic
top_terms2 <- tidy_lda2 %>%
group_by(topic) %>%
slice_max(beta, n = 5, with_ties = FALSE) %>%
ungroup() %>%
arrange(topic, -beta)
td_beta2 <- tidy(forums_lda2)
td_beta2 %>%
group_by(topic) %>%
top_n(10, beta) %>%
ungroup() %>%
mutate(topic = paste0("Topic ", topic),
term = reorder_within(term, beta, topic)) %>%
ggplot(aes(term, beta, fill = as.factor(topic))) +
geom_col(alpha = 0.8, show.legend = FALSE) +
facet_wrap(~ topic, scales = "free_y") +
coord_flip() +
scale_x_reordered() +
labs(x = NULL, y = expression(beta),
title = "Highest word probabilities for each topic",
subtitle = "Different words are associated with different topics")

td_gamma2 <- tidy(forums_lda2, matrix = "gamma")
beta_terms2 <- td_beta2 %>%
arrange(beta) %>%
group_by(topic) %>%
top_n(7, beta) %>%
arrange(-beta) %>%
select(topic, term) %>%
summarise(terms = list(term)) %>%
mutate(terms = map(terms, paste, collapse = ", ")) %>%
unnest()
## Warning: `cols` is now required when using unnest().
## Please use `cols = c(terms)`
gamma_terms2 <- td_gamma2 %>%
group_by(topic) %>%
summarise(gamma = mean(gamma)) %>%
arrange(desc(gamma)) %>%
left_join(top_terms, by = "topic") %>%
mutate(topic = paste0("Topic ", topic),
topic = reorder(topic, gamma))
gamma_terms2 %>%
select(topic, gamma, terms) %>%
kable(digits = 3,
col.names = c("Topic", "Expected topic proportion", "Top 7 terms"))
| Topic 4 |
0.436 |
statistics, math, mathematics, students, common, teaching, teach |
| Topic 5 |
0.293 |
age, tv, coasters, roller, steel, coaster, hours |
| Topic 3 |
0.160 |
students, kids, understand, standard, test, box, deviation |
| Topic 2 |
0.079 |
resources, statistics, teaching, unit, mooc, ideas, learning |
| Topic 1 |
0.032 |
dice, trials, fair, sasi, level, framework, sophistication |
- Now that you have a little more context, how might you revise your initial interpretation of some of the latent topics or latent themes from your model?
- I revised my topics to 10 and 5 in two different models. After revising for 10 I inspected with LDAvis. There was still overlap so I reduced it to 5 topics. Although there was overlap in the STM of 10 topics going down to only 5 topics in the second LDA model was to hard to interpret. Ten topics, are easier to make sense of and can quantify the topic amount chosen. My tea leaf interpretation for the LDA 5 Topics:
- Topic 4 - Teaching Students statistics using the common core
- Topic 5 - A popular data set is about Steel Roller Coasters
- Topic 3 - Students can understand Standard Deviation by testing in a Box Plot.
- Topic 2 - Educators sharing resources and ideas in the learning MOOCs
- Topic 1 - Items for learning and teaching statistics
LS0tDQp0aXRsZTogIlVuaXQgMyBXYWxrdGhyb3VnaDogVG9waWMgTW9kZWxpbmcgaW4gTU9PQy1FZHMiDQpvdXRwdXQ6IA0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19kZXB0aDogMw0KICAgIHRvY19mbG9hdDogeWVzDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogVFJVRQ0KZWRpdG9yX29wdGlvbnM6IA0KICBtYXJrZG93bjogDQogICAgd3JhcDogNzINCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYA0KDQojIyAwLiBJTlRST0RVQ1RJT04NCg0KVGhlIFVuaXQgMyB3YWxrdGhyb3VnaCBleHRlbmRzIHByZXZpb3VzIHJlc2VhcmNoIGFuZCBldmFsdWF0aW9uIHdvcmsgYXQNCnRoZSBGcmlkYXkgSW5zdGl0dXRlIGZvciBFZHVjYXRpb25hbCBJbm5vdmF0aW9uIGF0IE5vcnRoIENhcm9saW5hIFN0YXRlDQpVbml2ZXJzaXR5LiBJbiBhZGRpdGlvbiB0byBtYW55IG90aGVyIGFyZWFzIG9mIGlucXVpcnksIHRoaXMgd29yayB3YXMNCmFpbWVkIGF0IHVuZGVyc3RhbmRpbmcgYW5kIGltcHJvdmluZyBwZWVyIGludGVyYWN0aW9uIGFuZCBkaXNjdXNzaW9uIGluDQp0aGUgRnJpZGF5IEluc3RpdHV0ZSdzIE1hc3NpdmVseSBPcGVuIE9ubGluZSBDb3Vyc2VzIGZvciBFZHVjYXRvcnMNCihNT09DLUVkKSBhbmQgT25saW5lIFByb2Zlc3Npb25hbCBMZWFybmluZyBwcm9ncmFtcy4gVG8gbGVhcm4gbW9yZSBhYm91dA0KdGhlc2UgY291cnNlcyBhbmQgcHJvZ3JhbXMsIHZpc2l0OiA8aHR0cHM6Ly9wbGFjZS5maS5uY3N1LmVkdT4NCg0KIyMjIFdhbGt0aHJvdWdoIEZvY3VzDQoNCk91ciBmb2N1cyB0aGlzIHdlZWsgd2lsbCBiZSBvbiBpZGVudGlmeWluZyAidG9waWNzIiBieSBleGFtaW5pbmcgaG93DQp3b3JkcyBjb2hlcmUgaW50byBkaWZmZXJlbnQgbGF0ZW50LCBvciBoaWRkZW4sIHRoZW1lcyBiYXNlZCBvbiBwYXR0ZXJucw0Kb2YgY28tb2NjdXJyZW5jZSBvZiB3b3JkcyB3aXRoaW4gZG9jdW1lbnRzLiBXaXRoIGEgYml0IG9mDQp0b25ndWUtaW4tY2hlZWsswqBbTWVla3MgYW5kIFdlaW5nYXJ0DQooMjAxMildKGh0dHA6Ly9qb3VybmFsb2ZkaWdpdGFsaHVtYW5pdGllcy5vcmcvMi0xL2RoLWNvbnRyaWJ1dGlvbi10by10b3BpYy1tb2RlbGluZy8pwqBkZXNjcmliZQ0KdG9waWMgbW9kZWxpbmcgYXM6wqANCg0KPiAqLi4uZm9jdXNlZCBvbiBjb3Jwb3JhIGFuZCBub3QgaW5kaXZpZHVhbCB0ZXh0cywgdHJlYXRpbmcgdGhlIHdvcmtzDQo+IHRoZW1zZWx2ZXMgYXMgdW5jZXJlbW9uaW91cyAnYnVja2V0cyBvZiB3b3JkcywnIGFuZCBwcm92aWRpbmcNCj4gc2VkdWN0aXZlIGJ1dCBvYnNjdXJlIHJlc3VsdHMgaW4gdGhlIGZvcm1zIG9mIGVhc2lseSBpbnRlcnByZXRlZCAoYW5kDQo+IG1hbmlwdWxhdGVkKSAndG9waWNzJy4uLi4gVG8gYWNoaWV2ZSBpdHMgcmVzdWx0cywgaXQgbGV2ZXJhZ2VzIG9jY3VsdA0KPiBzdGF0aXN0aWNhbCBtZXRob2RzIGxpa2UgJ2RpcmljaGxldCBwcmlvcnMnIGFuZCAnYmF5ZXNpYW4gbW9kZWxzLicqDQoNClRoYXQgYmVpbmcgc2FpZCwNCltXZWluZ2FydF0oaHR0cDovL2pvdXJuYWxvZmRpZ2l0YWxodW1hbml0aWVzLm9yZy8yLTEvZGgtY29udHJpYnV0aW9uLXRvLXRvcGljLW1vZGVsaW5nLykNCmFsc28gbm90ZWQgdGhhdCAiYSB0b3BpYyBtb2RlbCBpcyBhICJjbGV2ZXIgYW5kIGV4Y2VwdGlvbmFsbHkgdmVyc2F0aWxlDQpsaXR0bGUgYWxnb3JpdGhtIHRoYXQgY2FuIGJlIGN1c3RvbWl6ZWQgdG8gYWxsIHNvcnRzIG9mIGFwcGxpY2F0aW9ucyINCmFuZCBbQmFpbA0KKDIwMjApXShodHRwczovL3NpY3NzLmlvLzIwMjAvbWF0ZXJpYWxzL2RheTMtdGV4dC1hbmFseXNpcy90b3BpYy1tb2RlbGluZy9ybWFya2Rvd24vVG9waWNfTW9kZWxpbmcuaHRtbCNydW5uaW5nLXlvdXItZmlyc3QtdG9waWMtbW9kZWwpDQphZGQgdGhhdCB0b3BpYyBtb2RlbGluZyBjYW4gYmUgImEgcG93ZXJmdWwgdG9vbCBmb3IgaWRlbnRpZnlpbmcgZ2VuZXJhbA0KdHJlbmRzIGluIGEgY29ycHVzIHRoYXQgY2FuIHRoZW4gYmUgYW5hbHl6ZWQgaW4gYSBtb3JlIGdyYW51bGFyIG1hbm5lcg0KdXNpbmcgb3RoZXIgdGVjaG5pcXVlcy4iDQoNCldpdGggcmVzcGVjdCB0byB0aGUgYWN0dWFsIFIgd29ya2Zsb3cgb2YgYXBwbHlpbmcgdG9waWMgbW9kZWxzIHRvDQpkb2N1bWVudHMgYW5kIHRleHQgb2YgaW50ZXJlc3RzLCBTaWxnZSAmIFJvYmluc29uIGFuZCBhIG5ldyBib3R0b20gcm93DQp0aGVpciBmbG93Y2hhcnQgY29uc2lzdGluZyBuZXcgZGF0YSBzdHJ1Y3R1cmVzIChpLmUuLCBhIGNvcnB1cyBvYmplY3QNCmFuZCBkb2N1bWVudC10ZXJtIG1hdHJpeCkgYW5kIGFuZCB0aGUgTERBIG1vZGVsOiDCoA0KDQpbIVtGaWd1cmXCoHNvdXJjZTrCoFNpbGdlLMKgSi4swqAmwqBSb2JpbnNvbizCoEQuwqAoMjAxNykuwqBUZXh0wqBtaW5pbmfCoHdpdGjCoFI6wqBBwqB0aWR5wqBhcHByb2FjaC7CoE8nUmVpbGx5wqBNZWRpYSzCoEluYy4NClJldHJpZXZlZCBmcm9tOg0KaHR0cHM6Ly93d3cudGlkeXRleHRtaW5pbmcuY29tL3RvcGljbW9kZWxpbmcuaHRtbF0oaW1nL3RtX2Zsb3cucG5nICJBIGZsb3djaGFydCBvZiBhIHRleHQgYW5hbHlzaXMgdGhhdCBpbmNvcnBvcmF0ZXMgdG9waWMgbW9kZWxpbmcuIFRoZSB0b3BpY21vZGVscyBwYWNrYWdlIHRha2VzIGEgRG9jdW1lbnQtVGVybSBNYXRyaXggYXMgaW5wdXQgYW5kIHByb2R1Y2VzIGEgbW9kZWwgdGhhdCBjYW4gYmUgdGlkZWQgYnkgdGlkeXRleHQsIHN1Y2ggdGhhdCBpdCBjYW4gYmUgbWFuaXB1bGF0ZWQgYW5kIHZpc3VhbGl6ZWQgd2l0aCBkcGx5ciBhbmQgZ2dwbG90Mi4iKXt3aWR0aD0iOTAlIn1dKGh0dHBzOi8vd3d3LnRpZHl0ZXh0bWluaW5nLmNvbS90b3BpY21vZGVsaW5nLmh0bWwpDQoNClRoaXMgd2VlayB3aWxsIGJlIGFsc28gYmUgb3VyIGZpcnN0IGludHJvZHVjdGlvbiB0byB0aGUgIk1vZGVsIiBwcm9jZXNzDQpvZiB0aGUgZGF0YS1pbnRlbnNpdmUgd29ya2Zsb3cgZGVzY3JpYmVkIGluIG91ciBjb3Vyc2UgdGV4dCwgWypMZWFybmluZw0KQW5hbHl0aWNzIEdvZXMgdG8NClNjaG9vbCpdKGh0dHBzOi8vY2F0YWxvZy5saWIubmNzdS5lZHUvY2F0YWxvZy9OQ1NVNDg2MjEzNCkqLiogQXMgbm90ZWQNCmJ5IEtydW1tIGFuZCBNZWFucyAoMjAxOCksIHRoaXMgd29ya2Zsb3cgaXMgbm90IGFsd2F5cyBhIGxpbmVhciBwcm9jZXNzDQphbmQgdGhlcmUgaXMgb2Z0ZW4gYSBncmVhdCBkZWFsIG9mIGl0ZXJhdGlvbiB0aGF0IG9jY3VycyB3aXRoaW4gYW5kDQpiZXR3ZWVuIHdyYW5nbGluZywgZXhwbG9yaW5nLCBtb2RlbGluZy4gQXMgaWxsdXN0cmF0ZWQgYnkgb3VyIHdvcmtmbG93DQpiZWxvdywgdGhpcyB3ZWVrIHdlIHdpbGwgcHJpbWFyaWx5IGV4cGxvcmUgb3VyIGRhdGEgYWZ0ZXIgdGhlIG1vZGVsaW5nDQpwcm9jZXNzIGluIG9yZGVyIHRvIGdhaW4gc29tZSBhZGRpdGlvbmFsIGluc2lnaHQgaW50byB0aGUgdG9waWNzDQpnZW5lcmF0ZWQgYnkgb3VyIG1vZGVsLiBTcGVjaWZpY2FsbHksIHRoaXMgd2VlayBjb3ZlcnMgdGhlIGZvbGxvd2luZw0KY29uY2VwdHMgYW5kIHNraWxsczoNCg0KMS4gICoqUHJlcGFyZSoqOiBQcmlvciB0byBhbmFseXNpcywgd2UnbGwgdGFrZSBhIHF1aWNrIGxvb2sgYXQgc29tZSBvZg0KICAgIHRoZSByZWxhdGVkIE1PT0MtRWQgcmVzZWFyY2ggYW5kIGV2YWx1YXRpb24gd29yayB0byBnYWluIHNvbWUNCiAgICBjb250ZXh0IGZvciBvdXIgYW5hbHlzaXMuIFRoaXMgc2hvdWxkIGFpZCBpbiB0aGUgaW50ZXJwcmV0YXRpb24gb2YNCiAgICBvdXIgcmVzdWx0cyBhbmQgaGVscCBndWlkZSBzb21lIGRlY2lzaW9ucyBhcyB3ZSB0aWR5LCBtb2RlbCwgYW5kDQogICAgdmlzdWFsaXplIG91ciBkYXRhLg0KMi4gICoqV3JhbmdsZSoqOiBJbiBzZWN0aW9uIDIgd2UgYWdhaW4gcmV2aXNpdCB0aWR5aW5nIGFuZCB0b2tlbml6aW5nDQogICAgdGV4dCB1c2luZyB0aGUgYHRpZHl0ZXh0YCBwYWNrYWdlIGJ1dCBhcmUgYWxzbyBpbnRyb2R1Y2VkIHRvIHRoZSB0aGUNCiAgICBgc3RtYCBwYWNrYWdlLiBUaGlzIHBhY2thZ2UgbWFrZXMgdXNlIG9mIGB0bWAgdGV4dCBtaW5pbmcgcGFja2FnZSB0bw0KICAgIHByZXByb2Nlc3MgdGV4dCBhbmQgd2lsbCBhbHNvIGJlIG91ciBmaXJzdCBpbnRyb2R1Y3Rpb24gdG8gd29yZA0KICAgIHN0ZW1taW5nLg0KMy4gICoqTW9kZWwqKjogV2UgdGFrZSBhIGxvb2sgYXQgdHdvIGRpZmZlcmVudCBhcHByb2FjaGVzIHRvIHRvcGljDQogICAgbW9kZWxpbmc6IExhdGVudCBEaXJpY2hsZXQgQWxsb2NhdGlvbiAoTERBKSBhbmQgU3RydWN0dXJhbCBUb3BpYw0KICAgIE1vZGVsaW5nIChTVE0pLCB3aGljaCBpcyB2ZXJ5IHNpbWlsYXIgdG8gTERBIGJ1dCBjYW4gdXNlIG1ldGFkYXRhDQogICAgYWJvdXQgZG9jdW1lbnRzIHRvIGltcHJvdmUgdGhlIGFzc2lnbm1lbnQgb2Ygd29yZHMgdG8gInRvcGljcyIgaW4gYQ0KICAgIGNvcnB1cyBhbmQgZXhhbWluZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gdG9waWNzIGFuZCBjb3ZhcmlhdGVzLsKgDQo0LiAgKipFeHBsb3JlKio6IFRvIGZ1cnRoZXIgZXhwbG9yZSB0aGUgcmVzdWx0cyBvZiBvdXIgdG9waWMgbW9kZWwsIHdlDQogICAgdXNlIHNldmVyYWwgaGFuZHkgZnVuY3Rpb25zIGZyb20gdGhlIGB0b3BpY21vZGVsc2AgYW5kIGBzdG1gDQogICAgcGFja2FnZXMsIGluY2x1ZGluZyB0aGUgYGZpbmRUaG91Z2h0c2AgZnVuY3Rpb24gZm9yIHZpZXdpbmcNCiAgICBkb2N1bWVudHMgYXNzaWduZWQgdG8gYSBnaXZlbiB0b3BpYyBhbmQgdGhlIGB0b0xEQXZpc2AgZnVuY3Rpb24gZm9yDQogICAgZXhwbG9yaW5nIHRvcGljIGFuZCB3b3JkIGRpc3RyaWJ1dGlvbnMuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAxLiBQUkVQQVJFDQoNClRvIGhlbHAgdXMgYmV0dGVyIHVuZGVyc3RhbmQgdGhlIGNvbnRleHQsIHF1ZXN0aW9ucywgYW5kIGRhdGEgc291cmNlcw0Kd2UnbGwgYmUgdXNpbmcgaW4gVW5pdCAzLCB0aGlzIHNlY3Rpb24gd2lsbCBmb2N1cyBvbiB0aGUgZm9sbG93aW5nDQp0b3BpY3M6DQoNCmEuICAqKkNvbnRleHQqKi4gQXMgY29udGV4dCBmb3Igb3VyIGFuYWx5c2lzIHRoaXMgd2Vlaywgd2UnbGwgcmV2aWV3DQogICAgc2V2ZXJhbCByZWxhdGVkIHBhcGVycyBieSBteSBjb2xsZWFndWVzIHJlbGV2YW50IHRvIG91ciBhbmFseXNpcyBvZg0KICAgIE1PT0MtRWQgZGlzY3Vzc2lvbiBmb3J1bXMuDQpiLiAgKipRdWVzdGlvbnMuKiogV2UnbGwgYWxzbyBleGFtaW5lIHdoYXQgaW5zaWdodCB0b3BpYyBtb2RlbGluZyBjYW4NCiAgICBwcm92aWRlIHRvIGEgcXVlc3Rpb24gdGhhdCB3ZSBhc2tlZCBwYXJ0aWNpcGFudHMgYW5zd2VyIGluIHRoZWlyDQogICAgcHJvZmVzc2lvbmFsIGxlYXJuaW5nIHRlYW1zIChQTFRzKS4NCmMuICAqKlByb2plY3QgU2V0dXAuKiogVGhpcyBzaG91bGQgYmUgdmVyeSBmYW1pbGlhciBieSBub3csIGJ1dCB3ZSdsbA0KICAgIHNldCB1cCBhIG5ldyBSIHByb2plY3QgYW5kIGluc3RhbGwgYW5kIGxvYWQgdGhlIHJlcXVpcmVkIHBhY2thZ2VzDQogICAgZm9yIHRoZSB0b3BpYyBtb2RlbGluZyB3YWxrdGhyb3VnaC4NCg0KIyMjIDFhLiBDb250ZXh0DQoNCiMjIyMgUGFydGljaXBhdGluZyBpbiBhIE1PT0MgYW5kIFByb2Zlc3Npb25hbCBMZWFybmluZyBUZWFtOiBIb3cgYSBCbGVuZGVkIEFwcHJvYWNoIHRvIFByb2Zlc3Npb25hbCBEZXZlbG9wbWVudCBNYWtlcyBhIERpZmZlcmVuY2UNCg0KWyFbVGVhY2hpbmcgU3RhdGlzdGljcyBUaHJvdWdoIERhdGEgSW52ZXN0aWdhdGlvbnMNCk1PT0MtRWRdKGltZy90c2RpLnBuZyAiT3VyIHdvcmxkIGlzIHJpY2ggd2l0aCBkYXRhIHNvdXJjZXMsIGFuZCB0ZWNobm9sb2d5IG1ha2VzIGRhdGEgbW9yZSBhY2Nlc3NpYmxlIHRoYW4gZXZlciBiZWZvcmUhIFRvIGhlbHAgZW5zdXJlIHN0dWRlbnRzIGFyZSBmdXR1cmUgcmVhZHkgdG8gdXNlIGRhdGEgZm9yIG1ha2luZyBpbmZvcm1lZCBkZWNpc2lvbnMsIG1hbnkgY291bnRyaWVzIGFyb3VuZCB0aGUgd29ybGQgaGF2ZSBpbmNyZWFzZWQgdGhlIGVtcGhhc2lzIG9uIHN0YXRpc3RpY3MgYW5kIGRhdGEgYW5hbHlzaXMgaW4gc2Nob29sIGN1cnJpY3VsdW3igJNmcm9tIGVsZW1lbnRhcnkvcHJpbWFyeSBncmFkZXMgdGhyb3VnaCBjb2xsZWdlLiBUaGlzIGNvdXJzZSBhbGxvd3MgeW91IHRvIGxlYXJuLCBhbG9uZyB3aXRoIGNvbGxlYWd1ZXMgZnJvbSBvdGhlciBzY2hvb2xzLCBhbiBpbnZlc3RpZ2F0aW9uIGN5Y2xlIHRvIHRlYWNoIHN0YXRpc3RpY3MgYW5kIHRvIGhlbHAgc3R1ZGVudHMgZXhwbG9yZSBkYXRhIHRvIG1ha2UgZXZpZGVuY2UtYmFzZWQgY2xhaW1zLiBUbyBsZWFybiBtb3JlIGFib3V0IGVuZ2FnaW5nIGxlYXJuZXJzIGluIG1ha2luZyBpbmZlcmVuY2VzIGFuZCBjbGFpbXMgc3VwcG9ydGVkIGJ5IGRhdGEgYW5kIGhvdyB0byBlbXBoYXNpemUgaW5mZXJlbnRpYWwgcmVhc29uaW5nIGluIHRlYWNoaW5nIHN0YXRpc3RpY3MgdGhyb3VnaCBwb3NpbmcgZGlmZmVyZW50IHR5cGVzIG9mIGludmVzdGlnYXRpdmUgcXVlc3Rpb25zLCBlbnJvbGwgaW4gb3VyIFRlYWNoaW5nIFN0YXRpc3RpY3MgdGhyb3VnaCBJbmZlcmVudGlhbCBSZWFzb25pbmcgTU9PQy1FZC4iKXt3aWR0aD0iNTAlIn1dKGh0dHBzOi8vcGxhY2UuZmkubmNzdS5lZHUvbG9jYWwvY2F0YWxvZy9jb3Vyc2UucGhwP2lkPTQmcmVmPTEpDQoNCkZ1bGwgdGV4dDogPGh0dHBzOi8vd3d3LmxlYXJudGVjaGxpYi5vcmcvcC8xOTUyMzQvPg0KDQoqKkFic3RyYWN0KioNCg0KTWFzc2l2ZSBPcGVuIE9ubGluZSBDb3Vyc2VzIGZvciBFZHVjYXRvcnMgKE1PT0MtRWRzKSBwcm92aWRlDQpvcHBvcnR1bml0aWVzIGZvciB1c2luZyByZXNlYXJjaC1iYXNlZCBsZWFybmluZyBhbmQgdGVhY2hpbmcgcHJhY3RpY2VzLA0KYWxvbmcgd2l0aCBuZXcgdGVjaG5vbG9naWNhbCB0b29scyBhbmQgZmFjaWxpdGF0aW9uIGFwcHJvYWNoZXMgZm9yDQpkZWxpdmVyaW5nIHF1YWxpdHkgb25saW5lIHByb2Zlc3Npb25hbCBkZXZlbG9wbWVudC4gVGhlIFRlYWNoaW5nDQpTdGF0aXN0aWNzIFRocm91Z2ggRGF0YSBJbnZlc3RpZ2F0aW9ucyBNT09DLUVkIHdhcyBidWlsdCBmb3IgcHJlcGFyaW5nDQp0ZWFjaGVycyBpbiBwZWRhZ29neSBmb3IgdGVhY2hpbmcgc3RhdGlzdGljcywgYW5kIGl0IGhhcyBiZWVuIG9mZmVyZWQgdG8NCnBhcnRpY2lwYW50cyBmcm9tIGFyb3VuZCB0aGUgd29ybGQuIER1cmluZyAyMDE2LTIwMTcsIHByb2Zlc3Npb25hbA0KbGVhcm5pbmcgdGVhbXMgKFBMVHMpIHdlcmUgZm9ybWVkIGZyb20gYSBzdWJzZXQgb2YgTU9PQy1FZCBwYXJ0aWNpcGFudHMuDQpUaGVzZSB0ZWFtcyBtZXQgc2V2ZXJhbCB0aW1lcyB0byBzaGFyZSBhbmQgZGlzY3VzcyB0aGVpciBsZWFybmluZyBhbmQNCmV4cGVyaWVuY2VzLiBUaGlzIHN0dWR5IGZvY3VzZWQgb24gZXhhbWluaW5nIHRoZSB3YXlzIHRoYXQgYSBibGVuZGVkDQphcHByb2FjaCB0byBwcm9mZXNzaW9uYWwgZGV2ZWxvcG1lbnQgbWF5IHJlc3VsdCBpbiBzaW1pbGFyIG9yIGRpZmZlcmVudA0KcGF0dGVybnMgb2YgZW5nYWdlbWVudCB0byB0aG9zZSB3aG8gb25seSBwYXJ0aWNpcGF0ZSBpbiBhIGxhcmdlLXNjYWxlDQpvbmxpbmUgY291cnNlLiBSZXN1bHRzIHNob3cgdGhlIGJlbmVmaXRzIG9mIGEgYmxlbmRlZCBsZWFybmluZw0KZW52aXJvbm1lbnQgZm9yIHJldGVudGlvbiwgZW5nYWdlbWVudCB3aXRoIGNvdXJzZSBtYXRlcmlhbHMsIGFuZA0KY29ubmVjdGVkbmVzcyB3aXRoaW4gdGhlIG9ubGluZSBjb21tdW5pdHkgb2YgbGVhcm5lcnMgaW4gYW4gb25saW5lDQpwcm9mZXNzaW9uYWwgZGV2ZWxvcG1lbnQgb24gdGVhY2hpbmcgc3RhdGlzdGljcy4gVGhlIGZpbmRpbmdzIHN1Z2dlc3QNCnRoZSB1c2Ugb2Ygc2VsZi1mb3JtaW5nIGF1dG9ub21vdXMgUExUcyBmb3Igc3VwcG9ydGluZyBhIGRlZXBlciBhbmQgbW9yZQ0KY29tcHJlaGVuc2l2ZSBleHBlcmllbmNlIHdpdGggc2VsZi1kaXJlY3RlZCBvbmxpbmUgcHJvZmVzc2lvbmFsDQpkZXZlbG9wbWVudHMgc3VjaCBhcyBNT09Dcy4gT3RoZXIgb25saW5lIHByb2Zlc3Npb25hbCBkZXZlbG9wbWVudA0KY291cnNlcywgc3VjaCBhcyBNT09DcywgbWF5IGJlbmVmaXQgZnJvbSBwdXJwb3NlbHkgc3VnZ2VzdGluZyBhbmQNCmFkdmVydGlzaW5nLCBhbmQgcGVyaGFwcyBmYWNpbGl0YXRpbmcsIHRoZSBmb3JtYXRpb24gb2Ygc21hbGwNCmZhY2UtdG8tZmFjZSBvciB2aXJ0dWFsIFBMVHMgd2hvIGNvbW1pdCB0byBlbmdhZ2UgaW4gbGVhcm5pbmcgdG9nZXRoZXIuDQoNCioqRGF0YSBTb3VyY2UgJiBBbmFseXNpcyoqDQoNCkFsbCBwZWVyIGludGVyYWN0aW9uLCBpbmNsdWRpbmcgcGVlciBkaXNjdXNzaW9uLCB0YWtlIHBsYWNlIHdpdGhpbg0KZGlzY3Vzc2lvbiBmb3J1bXMgb2YgTU9PQy1FZHMsIHdoaWNoIGFyZSBob3N0ZWQgdXNpbmcgdGhlIE1vb2RsZQ0KTGVhcm5pbmcgTWFuYWdlbWVudCBTeXN0ZW0uIFRvIGJ1aWxkIHRoZSBkYXRhc2V0IHlvdSdsbCBiZSB1c2luZyBmb3INCnRoaXMgd2Fsa3Rocm91Z2gsIHRoZSByZXNlYXJjaCB0ZWFtIHdyb3RlIGEgcXVlcnkgZm9yIE1vb2RsZSdzIE15U1FMDQpkYXRhYmFzZSwgd2hpY2ggcmVjb3JkcyBwYXJ0aWNpcGFudHMnIHVzZXItbG9ncyBvZiBhY3Rpdml0eSBpbiB0aGUNCm9ubGluZSBmb3J1bXMuIFRoaXMgc3FsIHF1ZXJ5IGNvbWJpbmVzIHNlcGFyYXRlIGRhdGFiYXNlIHRhYmxlcw0KY29udGFpbmluZyBwb3N0aW5ncyBhbmQgY29tbWVudHMgaW5jbHVkaW5nIHBhcnRpY2lwYW50IElEcywgdGltZXN0YW1wcywNCmRpc2N1c3Npb24gdGV4dCBhbmQgb3RoZXIgYXR0cmlidXRlcyBvciAibWV0YWRhdGEuIg0KDQpGb3IgZnVydGhlciBkZXNjcmlwdGlvbiBvZiB0aGUgZm9ydW1zIGFuZCBkYXRhIHJldHJpZXZhbCBwcm9jZXNzLCBzZWUNCmFsc28gdGhlIGZvbGxvd2luZyBwYXBlcnM6DQoNCi0gICBLZWxsb2dnLCBTLiwgJiBFZGVsbWFubiwgQS4gKDIwMTUpLiBbTWFzc2l2ZWx5IE9wZW4gT25saW5lIENvdXJzZQ0KICAgIGZvciBFZHVjYXRvcnMgKE1PT0PigJBFZCkgbmV0d29yaw0KICAgIGRhdGFzZXRdKGh0dHBzOi8vYmVyYS1qb3VybmFscy5vbmxpbmVsaWJyYXJ5LndpbGV5LmNvbS9kb2kvcGRmZGlyZWN0LzEwLjExMTEvYmpldC4xMjMxMikuwqAqQnJpdGlzaA0KICAgIGpvdXJuYWwgb2YgZWR1Y2F0aW9uYWwgdGVjaG5vbG9neSoswqAqNDYqKDUpLCA5NzctOTgzLg0KDQotICAgRXplbi1DYW4sIEEuLCBCb3llciwgSy4gRS4sIEtlbGxvZ2csIFMuLCAmIEJvb3RoLCBTLiAoMjAxNSwgTWFyY2gpLg0KICAgIFtVbnN1cGVydmlzZWQgbW9kZWxpbmcgZm9yIHVuZGVyc3RhbmRpbmcgTU9PQyBkaXNjdXNzaW9uIGZvcnVtczogYQ0KICAgIGxlYXJuaW5nIGFuYWx5dGljcw0KICAgIGFwcHJvYWNoXShodHRwczovL2RsLmFjbS5vcmcvZG9pL3BkZi8xMC4xMTQ1LzI3MjM1NzYuMjcyMzU4OSkuDQogICAgSW7CoCpQcm9jZWVkaW5ncyBvZiB0aGUgZmlmdGggaW50ZXJuYXRpb25hbCBjb25mZXJlbmNlIG9uIGxlYXJuaW5nDQogICAgYW5hbHl0aWNzIGFuZCBrbm93bGVkZ2UqwqAocHAuIDE0Ni0xNTApLg0KDQotICAgS2VsbG9nZywgUy4sIEJvb3RoLCBTLiwgJiBPbGl2ZXIsIEsuICgyMDE0KS4gW0Egc29jaWFsIG5ldHdvcmsNCiAgICBwZXJzcGVjdGl2ZSBvbiBwZWVyIHN1cHBvcnRlZCBsZWFybmluZyBpbiBNT09DcyBmb3INCiAgICBlZHVjYXRvcnMuXShodHRwczovL3d3dy5lcnVkaXQub3JnL2VuL2pvdXJuYWxzL2lycm9kbC8xOTAwLXYxLW4xLWlycm9kbDA0OTQ1LzEwNjU1NDVhci5wZGYpwqAqSW50ZXJuYXRpb25hbA0KICAgIFJldmlldyBvZiBSZXNlYXJjaCBpbiBPcGVuIGFuZCBEaXN0cmlidXRlZCBMZWFybmluZyoswqAqMTUqKDUpLA0KICAgIDI2My0yODkuDQoNCioqU3VtbWFyeSBvZiBLZXkgRmluZGluZ3MqKg0KDQpUaGUgZm9sbG93aW5nIGhpZ2hsaWdodCBzb21lIGtleSBmaW5kaW5ncyByZWxhdGVkIHRvIHRoZSBkaXNjdXNzaW9uDQpmb3J1bXMgaW4gdGhlIHBhcGVycyBjaXRlZCBhYm92ZToNCg0KMS4gIE1PT0NzIGRlc2lnbmVkIHNwZWNpZmljYWxseSBmb3IgSy0xMiB0ZWFjaGVycyBjYW4gcHJvdmlkZSBwb3NpdGl2ZQ0KICAgIHNlbGYtZGlyZWN0ZWQgbGVhcm5pbmcgZXhwZXJpZW5jZXMgYW5kIHJpY2ggZW5nYWdlbWVudCBpbiBkaXNjdXNzaW9uDQogICAgZm9ydW1zIHRoYXQgaGVscCBmb3JtIG9ubGluZSBjb21tdW5pdGllcyBmb3IgZWR1Y2F0b3JzLg0KMi4gIEFuYWx5c2lzIG9mIGRpc2N1c3Npb24gZm9ydW0gZGF0YSBpbiBUU0RJIHByb3ZpZGVkIGEgdmVyeSBjbGVhcg0KICAgIHBpY3R1cmUgb2YgaG93IGVudGh1c2lhc3RpYyBtYW55IFBMVCBtZW1iZXJzIGFuZCBsZWFkZXJzIHdlcmUgdG8NCiAgICB0YWxrIHRvIG90aGVycyBpbiB0aGUgb25saW5lIGNvbW11bml0eS4gVGhleSBwb3NlZCB0aGVpciBxdWVzdGlvbnMNCiAgICBhbmQgc2hhcmVkIGlkZWFzIHdpdGggb3RoZXJzIGFib3V0IHRlYWNoaW5nIHN0YXRpc3RpY3MgdGhyb3VnaG91dA0KICAgIHRoZSB1bml0cywgZXZlbiB0aG91Z2ggdGhleSB3ZXJlIGFsc28gbWVldGluZyBzeW5jaHJvbm91c2x5IHNldmVyYWwNCiAgICB0aW1lcyB3aXRoIHRoZWlyIGNvbGxlYWd1ZXMgaW4gc21hbGwgZ3JvdXAgUExUcy4NCjMuICBGaW5kaW5ncyBvbiBrbm93bGVkZ2UgY29uc3RydWN0aW9uIGRlbW9uc3RyYXRlZCB0aGF0IG92ZXIgaGFsZiBvZg0KICAgIHRoZSBkaXNjdXNzaW9ucyBpbiBib3RoIGNvdXJzZXMgbW92ZWQgYmV5b25kIHNoYXJpbmcgaW5mb3JtYXRpb24gYW5kDQogICAgc3RhdGVtZW50cyBvZiBhZ3JlZW1lbnQgYW5kIGVudGVyZWQgYSBwcm9jZXNzIG9mIGRpc3NvbmFuY2UsDQogICAgbmVnb3RpYXRpb24gYW5kIGNvLWNvbnN0cnVjdGlvbiBvZiBrbm93bGVkZ2UsIGJ1dCBzZWxkb20gbW92ZWQNCiAgICBiZXlvbmQgdGhpcyBwaGFzZSBpbiB3aGljaCBuZXcga25vd2xlZGdlIHdhcyB0ZXN0ZWQgb3IgYXBwbGllZC4NCiAgICBUaGVzZSBmaW5kaW5ncyBlY2hvIHNpbWlsYXIgcmVzZWFyY2ggb24gZGlmZmljdWx0aWVzIGluIHByb21vdGluZw0KICAgIGtub3dsZWRnZSBjb25zdHJ1Y3Rpb24gaW4gb25saW5lIHNldHRpbmdzLg0KNC4gIFRvcGljIG1vZGVsaW5nIHByb3ZpZGVzIG1vcmUgaW50ZXJwcmV0YWJsZSBhbmQgY29oZXNpdmUgbW9kZWxzIGZvcg0KICAgIGRpc2N1c3Npb24gZm9ydW1zIHRoYW4gb3RoZXIgcG9wdWxhciB1bnN1cGVydmlzZWQgbW9kZWxpbmcNCiAgICB0ZWNobmlxdWVzIHN1Y2ggYXMgay1tZWFucyBhbmQgay1tZWRvaWRzIGNsdXN0ZXJpbmcgYWxnb3JpdGhtcy4NCg0KIyMjIDFiLiBHdWlkaW5nIFF1ZXN0aW9ucw0KDQpGb3IgdGhlIHBhcGVyLCBbKlBhcnRpY2lwYXRpbmcgaW4gYSBNT09DIGFuZCBQcm9mZXNzaW9uYWwgTGVhcm5pbmcgVGVhbToNCkhvdyBhIEJsZW5kZWQgQXBwcm9hY2ggdG8gUHJvZmVzc2lvbmFsIERldmVsb3BtZW50IE1ha2VzIGENCkRpZmZlcmVuY2UqXShodHRwczovL3d3dy5sZWFybnRlY2hsaWIub3JnL3AvMTk1MjM0LyksIHRoZSByZXNlYXJjaGVycw0Kd2VyZSBpbnRlcmVzdGVkIGluIHVucGFja2luZyBob3cgcGFydGljaXBhbnRzIHdobyBlbnJvbGxlZCBpbiB0aGUNClRlYWNoaW5nIFN0YXRpc3RpY3MgdGhyb3VnaCBEYXRhIEludmVzdGlnYXRpb25zIE1PT0MtRWQgbWlnaHQgYmVuZWZpdA0KZnJvbSBhbHNvIGJlaW5nIGluIGEgc21hbGxlciBncm91cCBvZiBwcm9mZXNzaW9uYWxzIGNvbW1pdHRlZCB0bw0KZW5nYWdpbmcgaW4gdGhlIHNhbWUgcHJvZmVzc2lvbmFsIGRldmVsb3BtZW50LiBUaGUgc3BlY2lmaWMgcmVzZWFyY2gNCnF1ZXN0aW9uIGZvciB0aGlzIHBhcGVyIHdhczoNCg0KPiBXaGF0IGFyZSB0aGUgc2ltaWxhcml0aWVzIGFuZCBkaWZmZXJlbmNlcyBiZXR3ZWVuIGhvdyBQTFQgbWVtYmVycyBhbmQNCj4gTm9uLVBMVCBvbmxpbmUgcGFydGljaXBhbnRzIGVuZ2FnZSBhbmQgbWVldCBjb3Vyc2UgZ29hbHMgaW4gYSBNT09DLUVkDQo+IGRlc2lnbmVkIGZvciBlZHVjYXRvcnMgaW4gc2Vjb25kYXJ5IGFuZCBjb2xsZWdpYXRlIHNldHRpbmdzPw0KDQpEci4gSG9sbHlseW5uZSBMZWUgYW5kIHRoZSBUU0RJIHRlYW0gYWxzbyBkZXZlbG9wZWQgYSBmYWNpbGl0YXRpb24gZ3VpZGUNCmRlc2lnbmVkIHNwZWNpZmljYWxseSBmb3IgUExUIHRlYW1zIHRvIGhlbHAgZ3JvdXBzIHN5bnRoZXNpemUgdGhlIGlkZWFzDQppbiB0aGUgY291cnNlIGFuZCBtYWtlIHBsYW5zIGZvciBob3cgdG8gaW1wbGVtZW50IG5ldyBzdHJhdGVnaWVzIGluDQp0aGVpciBjbGFzc3Jvb20gaW4gb3JkZXIgdG8gaW1wYWN0IHN0dWRlbnRzJyBsZWFybmluZyBvZiBzdGF0aXN0aWNzLiBPbmUNCnF1ZXN0aW9uIFBMVCBtZW1iZXJzIHdlcmUgYXNrZWQgdG8gYWRkcmVzcyB3YXM6DQoNCj4gV2hhdCBpZGVhcyBvciBpc3N1ZXMgZW1lcmdlZCBpbiB0aGUgZGlzY3Vzc2lvbiBmb3J1bXMgdGhpcyBwYXN0IHdlZWs/DQoNCkZvciB0aGlzIHdhbGt0aHJvdWdoLCB3ZSB3aWxsIGZ1cnRoZXIgZXhhbWluZSB0aGF0IHF1ZXN0aW9uIHRocm91Z2ggdGhlDQp1c2Ugb2YgdG9waWMgbW9kZWxpbmcuDQoNCkFuZCBqdXN0IHRvIHJlaXRlcmF0ZSB5ZXQgYWdhaW4gZnJvbSBVbml0IDEsIG9uZSBvdmVyYXJjaGluZyBxdWVzdGlvbg0Kd2UnbGwgZXhwbG9yZSB0aHJvdWdob3V0IHRoaXMgY291cnNlLCBhbmQgdGhhdCBTaWxnZSBhbmQgUm9iaW5zb24gKDIwMTgpDQppZGVudGlmeSBhcyBhIGNlbnRyYWwgcXVlc3Rpb24gdG8gdGV4dCBtaW5pbmcgYW5kIG5hdHVyYWwgbGFuZ3VhZ2UNCnByb2Nlc3NpbmcsIGlzOg0KDQo+IEhvdyBkbyB3ZSB0byAqKnF1YW50aWZ5Kiogd2hhdCBhIGRvY3VtZW50IG9yIGNvbGxlY3Rpb24gb2YgZG9jdW1lbnRzDQo+IGlzIGFib3V0Pw0KDQojIyMgMWMuIFNldCBVcA0KDQpBcyBoaWdobGlnaHRlZCBpbiBbQ2hhcHRlciA2IG9mIERhdGEgU2NpZW5jZSBpbiBFZHVjYXRpb24gVXNpbmcNClJdKGh0dHBzOi8vZGF0YXNjaWVuY2VpbmVkdWNhdGlvbi5jb20vYzA2Lmh0bWwpIChEU0lFVVIpLCBvbmUgb2YgdGhlDQpmaXJzdCBzdGVwcyBvZiBldmVyeSB3b3JrZmxvdyBzaG91bGQgYmUgdG8gc2V0IHVwIGEgIlByb2plY3QiIHdpdGhpbg0KUlN0dWRpby4gVGhpcyB3aWxsIGJlIHlvdXIgImhvbWUiIGZvciBhbnkgZmlsZXMgYW5kIGNvZGUgdXNlZCBvciBjcmVhdGVkDQppbiBVbml0IDIuDQoNCllvdSBhcmUgd2VsY29tZSB0byBjb250aW51ZSB1c2luZyB0aGUgc2FtZSBwcm9qZWN0IGNyZWF0ZWQgZm9yIFVuaXQgMSwNCm9yIGNyZWF0ZSBhbiBlbnRpcmVseSBuZXcgcHJvamVjdCBmb3IgVW5pdCAyLiBIb3dldmVyLCBhZnRlciB5b3UndmUNCmNyZWF0ZWQgeW91ciBwcm9qZWN0IG9wZW4gdXAgYSBuZXcgUiBzY3JpcHQsIGFuZCBsb2FkIHRoZSBmb2xsb3dpbmcNCnBhY2thZ2VzIHRoYXQgd2UnbGwgYmUgbmVlZGluZyBmb3IgdGhpcyB3YWxrdGhyb3VnaDoNCg0KYGBge3IgbG9hZC1wYWNrYWdlcywgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeSh0aWR5dGV4dCkNCmxpYnJhcnkoU25vd2JhbGxDKQ0KbGlicmFyeSh0b3BpY21vZGVscykNCmxpYnJhcnkoc3RtKQ0KbGlicmFyeShsZGF0dW5pbmcpDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShMREF2aXMpDQpgYGANCg0KQXQgdGhlIGVuZCBvZiB0aGlzIHdlZWssIEkgZW5jb3VyYWdlIHlvdSBzaGFyZSB3aXRoIG1lIHlvdXIgUiBzY3JpcHQgYXMNCmV2aWRlbmNlIHRoYXQgeW91IGhhdmUgY29tcGxldGUgdGhlIHdhbGt0aHJvdWdoLiBBbHRob3VnaCBJIGhpZ2hseQ0KcmVjb21tZW5kIHRoYXQgdGhhdCB5b3UgbWFudWFsbHkgdHlwZSB0aGUgY29kZSBzaGFyZWQgdGhyb3VnaG91dCB0aGlzDQp3YWxrdGhyb3VnaCwgZm9yIGxhcmdlIGJsb2NrcyBvZiB0ZXh0IGl0IG1heSBiZSBlYXNpZXIgdG8gY29weSBhbmQNCnBhc3RlLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMi4gV1JBTkdMRQ0KDQpBcyBub3RlZCBwcmV2aW91c2x5LCBkYXRhIHdyYW5nbGluZyBpbnZvbHZlcyBzb21lIGNvbWJpbmF0aW9uIG9mDQpjbGVhbmluZywgcmVzaGFwaW5nLCB0cmFuc2Zvcm1pbmcsIGFuZCBtZXJnaW5nIGRhdGEgKFdpY2toYW0gJg0KR3JvbGVtdW5kLCAyMDE3KS4gVGhpcyB3ZWVrIHdlJ2xsIHJldmlzaXQgdGlkeWluZyBhbmQgdG9rZW5pemluZyB0ZXh0DQp1c2luZyB0aGUgYHRpZHl0ZXh0YCBwYWNrYWdlLCBidXQgYXJlIGFsc28gaW50cm9kdWNlZCB0byB0aGUgdGhlIGBzdG1gDQpwYWNrYWdlLiBUaGlzIHBhY2thZ2UgbWFrZXMgdXNlIG9mIGB0bWAgdGV4dCBtaW5pbmcgcGFja2FnZSB0bw0KcHJlcHJvY2VzcyB0ZXh0IChlLmcuLCByZW1vdmluZyBwdW5jdHVhdGlvbiwgc3RvcCB3b3JkcywgZXRjLikgYW5kIHdpbGwNCmFsc28gYmUgb3VyIGZpcnN0IGludHJvZHVjdGlvbiB0byB3b3JkIHN0ZW1taW5nLg0KDQphLiAgKipJbXBvcnQgRGF0YSoqLiBXZSdsbCBiZSB3b3JraW5nIHdpdGggLmNzdiBmaWxlcyB0aGlzIHdlZWsgYW5kIHRoZQ0KICAgIGByZWFkX2NzdigpYCBmdW5jdGlvbiBidXQgd2lsbCBpbnRyb2R1Y2UgYSBuZXcgYXJndW1lbnQgZm9yIGNoYW5naW5nDQogICAgY29sdW1uIHR5cGVzLg0KYi4gICoqQ2FzdCBhIERUTSoqLiBXZSByZXZpc2l0IHRoZSBgdGlkeXRleHRgIHBhY2thZ2UgdG8gInRpZHkiIGFuZA0KICAgIHRva2VuaXplIG91ciBmb3J1bSBkYXRhIGFuZCBpbnRyb2R1Y2UgdGhlIGBjYXN0X2R0bSgpYCBmdW5jdGlvbiB0bw0KICAgIGNyZWF0ZSB0aGUgZG9jdW1lbnQgdGVybSBtYXRyaXggKGR0bSkgbmVlZCBmb3IgdG9waWMgbW9kZWxpbmcuDQpjLiAgKipUbyBTdGVtIG9yIG5vdCB0byBTVEVNPyoqIFdlIGNvbmNsdWRlIG91ciBkYXRhIHdyYW5nbGluZyBieSBhbHNvDQogICAgaW50cm9kdWNpbmcgdGhlIGB0ZXh0UHJvY2Vzc29yKClgIGZ1bmN0aW9uIGZvciBwcmVwcm9jZXNzaW5nIGFuZA0KICAgIGRpc2N1c3MgdGhlIHByb3MgYW5kIGNvbnMgb2Ygd29yZCBzdGVtbWluZy4NCg0KIyMjIDJhLiBJbXBvcnQgRm9ydW0gRGF0YQ0KDQpUbyBnZXQgc3RhcnRlZCwgd2UgbmVlZCB0byBpbXBvcnQsIG9yICJyZWFkIiwgb3VyIGRhdGEgaW50byBSLiBUaGUNCmZ1bmN0aW9uIHVzZWQgdG8gaW1wb3J0IHlvdXIgZGF0YSB3aWxsIGRlcGVuZCBvbiB0aGUgZmlsZSBmb3JtYXQgb2YgdGhlDQpkYXRhIHlvdSBhcmUgdHJ5aW5nIHRvIGltcG9ydC4gRmlyc3QsIGhvd2V2ZXIsIHlvdSdsbCBuZWVkIHRvIGRvIHRoZQ0KZm9sbG93aW5nOg0KDQoxLiAgRG93bmxvYWQgdGhlIGB0c19mb3J1bV9kYXRhLmNzdmAgZmlsZSB3ZSdsbCBiZSB1c2luZyBmb3IgdGhpcyBVbml0DQogICAgZnJvbSBvdXIgTkNTVSBNb29kbGUgY291cnNlIHNpdGUuDQoyLiAgQ3JlYXRlIGEgZm9sZGVyIGluIHRoZSBkaXJlY3Rvcnkgb24geW91ciBjb21wdXRlciB3aGVyZSB5b3Ugc3RvcmVkDQogICAgeW91ciBSIFByb2plY3QgYW5kIG5hbWUgaXQgImRhdGEiLg0KMy4gIEFkZCB0aGUgZmlsZSB0byB5b3VyIGRhdGEgZm9sZGVyLg0KNC4gIENoZWNrIHlvdXIgRmlsZXMgdGFiIGluIFJTdHVkaW8gdG8gdmVyaWZ5IHRoYXQgeW91ciBmaWxlIGlzIGluZGVlZA0KICAgIGluIHlvdXIgZGF0YSBmb2xkZXIuDQoNCk5vdyBsZXQncyByZWFkIG91ciBkYXRhIGludG8gb3VyIEVudmlyb25tZW50IHVzaW5nIHRoZSBgcmVhZF9jc3YoKWANCmZ1bmN0aW9uIGFuZCBhc3NpZ24gaXQgdG8gYSB2YXJpYWJsZSBuYW1lIHNvIHdlIGNhbiB3b3JrIHdpdGggaXQgbGlrZQ0KYW55IG90aGVyIG9iamVjdCBpbiBSLg0KDQpgYGB7ciByZWFkLWNzdn0NCnRzX2ZvcnVtX2RhdGEgPC0gcmVhZF9jc3YoImRhdGEvdHNfZm9ydW1fZGF0YS5jc3YiLCANCiAgICAgY29sX3R5cGVzID0gY29scyhjb3Vyc2VfaWQgPSBjb2xfY2hhcmFjdGVyKCksDQogICAgICAgICAgICAgICAgICAgZm9ydW1faWQgPSBjb2xfY2hhcmFjdGVyKCksIA0KICAgICAgICAgICAgICAgICAgIGRpc2N1c3Npb25faWQgPSBjb2xfY2hhcmFjdGVyKCksIA0KICAgICAgICAgICAgICAgICAgIHBvc3RfaWQgPSBjb2xfY2hhcmFjdGVyKCkNCiAgICAgICAgICAgICAgICAgICApDQogICAgKQ0KYGBgDQoNCkJ5IGRlZmF1bHQsIG1hbnkgb2YgdGhlIGNvbHVtbnMgbGlrZSBgY291cnNlX2lkYCBhbmQgYGZvcnVtX2lkYCBhcmUgcmVhZA0KaW4gYXMgbnVtZXJpYyBkYXRhLiBGb3Igb3VyIHB1cnBvc2VzLCB3ZSBwbGFuIHRvIHRyZWF0IHRoZW0gYXMgdW5pcXVlDQppZGVudGlmaWVycyBvciBuYW1lcyBmb3Igb3V0IGNvdXJzZXMsIGZvcnVtcywgZGlzY3Vzc2lvbnMsIGFuZCBwb3N0cy4NClRoZSBgcmVhZF9jc3YoKWAgZnVuY3Rpb24gaGFzIGEgaGFuZHkgYGNvbF90eXBlcyA9YCBhcmd1bWVudCBjaGFuZ2luZw0KdGhlIGNvbHVtbiB0eXBlcyBmcm9tIG51bWVyaWMgdG8gY2hhcmFjdGVycy4NCg0KIyMjIDJiLiBDYXN0IGEgRG9jdW1lbnQgVGVybSBNYXRyaXgNCg0KSW4gdGhpcyBzZWN0aW9uIHdlJ2xsIHJldmlzaXQgc29tZSBmYW1pbGlhciBgdGlkeXRleHRgIGZ1bmN0aW9ucyB1c2VkIGluDQpVbml0cyAxICYgMiBmb3IgdGlkeWluZyBhbmQgdG9rZW5pemluZyB0ZXh0IGFuZCBpbnRyb2R1Y2Ugc29tZSBuZXcNCmZ1bmN0aW9ucyBmcm9tIHRoZSBgc3RtYCBwYWNrYWdlIGZvciBwcm9jZXNzaW5nIHRleHQgYW5kIHRyYW5zZm9ybWluZw0Kb3VyIGRhdGEgZnJhbWVzIGludG8gbmV3IGRhdGEgc3RydWN0dXJlcyByZXF1aXJlZCBmb3IgdG9waWMgbW9kZWxpbmcuDQoNCiMjIyMgRnVuY3Rpb25zIFVzZWQNCg0KKipgdGlkeXRleHRgIGZ1bmN0aW9ucyoqDQoNCi0gICBgdW5uZXN0X3Rva2VucygpYCBzcGxpdHMgYSBjb2x1bW4gaW50byB0b2tlbnMNCi0gICBgYW50aV9qb2luKClgIHJldHVybnMgYWxsIHJvd3MgZnJvbSB4IHdpdGhvdXQgYSBtYXRjaCBpbiB5IGFuZCB1c2VkDQogICAgdG8gcmVtb3ZlIGBzdG9wIHdvcmRzYCBmcm9tIG91dCBkYXRhLg0KLSAgIGBjYXN0X2R0bSgpYCB0YWtlcyBhIHRpZGllZCBkYXRhIGZyYW1lIHRha2UgYW5kICJjYXN0cyIgaXQgaW50byBhDQogICAgZG9jdW1lbnQtdGVybSBtYXRyaXggKGR0bSkNCg0KKipgZHBseXJgKiogKipmdW5jdGlvbnMqKg0KDQotICAgYGNvdW50KClgIGxldHMgeW91IHF1aWNrbHkgY291bnQgdGhlIHVuaXF1ZSB2YWx1ZXMgb2Ygb25lIG9yIG1vcmUNCiAgICB2YXJpYWJsZXMNCi0gICBgZ3JvdXBfYnkoKWAgdGFrZXMgYSBkYXRhIGZyYW1lIGFuZCBvbmUgb3IgbW9yZSB2YXJpYWJsZXMgdG8gZ3JvdXANCiAgICBieQ0KLSAgIGBzdW1tYXJpc2UoKWAgY3JlYXRlcyBhIHN1bW1hcnkgb2YgZGF0YSB1c2luZyBhcmd1bWVudHMgbGlrZSBzdW0gYW5kDQogICAgbWVhbg0KDQoqKmBzdG1gIGZ1bmN0aW9ucyoqDQoNCi0gICBgdGV4dFByb2Nlc3NvcigpYCB0YWtlcyBpbiBhIHZlY3RvciBvciBjb2x1bW4gb2YgcmF3IHRleHRzIGFuZA0KICAgIHBlcmZvcm1zIHRleHQgcHJvY2Vzc2luZyBsaWtlIHJlbW92aW5nIHB1bmN0dWF0aW9uIGFuZCB3b3JkDQogICAgc3RlbW1pbmcuDQotICAgYHByZXBEb2N1bWVudHMoKWAgcGVyZm9ybXMgc2V2ZXJhbCBjb3JwdXMgbWFuaXB1bGF0aW9ucyBpbmNsdWRpbmcNCiAgICByZW1vdmluZyB3b3JkcyBhbmQgcmVudW1iZXJpbmcgd29yZCBpbmRpY2VzDQoNCiMjIyMgVGlkeWluZyBUZXh0DQoNClByaW9yIHRvIHRvcGljIG1vZGVsaW5nLCB3ZSBoYXZlIGEgZmV3IHJlbWFpbmluZyBzdGVwcyB0byB0aWR5IG91ciB0ZXh0DQp0aGF0IGhvcGVmdWxseSBzaG91bGQgZmVlbCBmYW1pbGlhciBieSB0aGlzIHBvaW50LiBJZiB5b3UgcmVjYWxsIGZyb20NCltDaGFwdGVyIDEgb2YgVGV4dCBNaW5pbmcgV2l0aA0KUl0oaHR0cHM6Ly93d3cudGlkeXRleHRtaW5pbmcuY29tL3RpZHl0ZXh0Lmh0bWwpLCB0aGVzZSBwcmVwcm9jZXNzaW5nDQpzdGVwcyBpbmNsdWRlOg0KDQoxLiAgVHJhbnNmb3JtaW5nIG91ciB0ZXh0IGludG8gInRva2VucyINCjIuICBSZW1vdmluZyB1bm5lY2Vzc2FyeSBjaGFyYWN0ZXJzLCBwdW5jdHVhdGlvbiwgYW5kIHdoaXRlc3BhY2UNCjMuICBDb252ZXJ0aW5nIGFsbCB0ZXh0IHRvIGxvd2VyY2FzZQ0KNC4gIFJlbW92aW5nIHN0b3Agd29yZHMgc3VjaCBhcyAidGhlIiwgIm9mIiwgYW5kICJ0byINCg0KTGV0J3MgdG9rZW5pemUgb3VyIGZvcnVtIHRleHQgYW5kIGJ5IHVzaW5nIHRoZSBmYW1pbGlhcg0KYHVubmVzdF90b2tlbnMoKWAgYW5kIHJlbW92ZSBzdG9wIHdvcmRzIHBlciB1c3VhbDoNCg0KYGBge3IgdG9rZW5pemUtZm9ydW1zfQ0KZm9ydW1zX3RpZHkgPC0gdHNfZm9ydW1fZGF0YSAlPiUNCiAgdW5uZXN0X3Rva2VucyhvdXRwdXQgPSB3b3JkLCBpbnB1dCA9IHBvc3RfY29udGVudCkgJT4lDQogIGFudGlfam9pbihzdG9wX3dvcmRzLCBieSA9ICJ3b3JkIikNCg0KZm9ydW1zX3RpZHkNCmBgYA0KDQpOb3cgbGV0J3MgZG8gYSBxdWljayB3b3JkIGNvdW50IHRvIHNlZSBzb21lIG9mIHRoZSBtb3N0IGNvbW1vbiB3b3Jkcw0KdXNlZCB0aHJvdWdob3V0IHRoZSBmb3J1bXMuIFRoaXMgc2hvdWxkIGdldCBhIHNlbnNlIG9mIHdoYXQgd2UncmUNCndvcmtpbmcgd2l0aCBhbmQgbGF0ZXIgd2UnbGwgbmVlZCB0aGVzZSB3b3JkIGNvdW50cyBmb3IgY3JlYXRpbmcgb3VyDQpkb2N1bWVudCB0ZXJtIG1hdHJpeCBmb3IgdG9waWMgbW9kZWxpbmc6DQoNCmBgYHtyIGNvdW50LXdvcmRzfQ0KZm9ydW1zX3RpZHkgJT4lDQogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKQ0KYGBgDQoNClRlcm1zIGxpa2UgInN0dWRlbnRzLCIgImRhdGEsIiBhbmQgImNsYXNzIiBhcmUgYWJvdXQgd2hhdCB3ZSB3b3VsZCBoYXZlDQpleHBlY3RlZCBmcm9tIGEgY291cnNlIHRlYWNoaW5nIHN0YXRpc3RpY3MuIFRoZSB0ZXJtICJhZ3JlZSIgYW5kICJ0aW1lIg0KaG93ZXZlciwgYXJlIG5vdCBzbyBpbnR1aXRpdmUgYW5kIHdvcnRoIGEgcXVpY2sgbG9vayBhcyB3ZWxsLg0KDQojIyMjIyDinIUgQ29tcHJlaGVuc2lvbiBDaGVjaw0KDQpVc2UgdGhlIGBmaWx0ZXIoKWAgYW5kIGBncmVwbCgpYCBmdW5jdGlvbnMgaW50cm9kdWNlZCBpbiBbVW5pdCAxLg0KU2VjdGlvbg0KM2JdKGh0dHA6Ly9zaGl5YW4yMDMwLmNvbS91bml0LTEtd2Fsa3Rocm91Z2guaHRtbCMzYl9Xb3JkX1NlYXJjaCkgdG8NCmZpbHRlciBmb3Igcm93cyBpbiBvdXIgYHRzX2ZvcnVtX2RhdGFgIGRhdGEgZnJhbWUgdGhhdCBjb250YWluIHRoZSB0ZXJtcw0KImFncmVlIiBhbmQgInRpbWUiIGFuZCBhbm90aGVyIHRlcm0gb3IgdGVybXMgb2YgeW91ciBjaG9vc2luZy4gU2VsZWN0IGENCnJhbmRvbSBzYW1wbGUgb2YgMTAgcG9zdHMgdXNpbmcgdGhlIGBzYW1wbGVfbigpYCBmdW5jdGlvbiBmb3IgeW91ciB0ZXJtcw0KYW5kIGFuc3dlciB0aGUgZm9sbG93aW5nIHF1ZXN0aW9uczoNCg0KMS4gIFdoYXQsIGlmIGFueXRoaW5nLCBkbyB0aGVzZSBwb3N0cyBoYXZlIGluIGNvbW1vbj8NCi0gICBUaGVzZSBwb3N0cyBhcmUgYWxsIHJlcGxpZXMgdG8gYSBzZXBhcmF0ZSBkaXNjdXNzaW9uLiBEZXBlbmRpbmcgb24gdGhlIHdvcmRzIHVzZWQgeW91IG1heSBmaW5kIHNpbWlsYXJpdGllcyBmb3IgdGhlIG9yaWdpbmFsIHF1ZXN0aW9uLiANCg0KMi4gIFdoYXQgdG9waWNzIG9yIHRoZW1lcyBtaWdodCBiZSBhcHBhcmVudCwgb3IgZG8geW91IGFudGljaXBhdGUNCiAgICBlbWVyZ2luZywgZnJvbSBvdXIgdG9waWMgbW9kZWxpbmc/DQotICAgTGVzc29ucyB0aGF0IHRoZSBwYXJ0aWNpcGFudHMgd29ya2VkIHRvZ2V0aGVyIG9uLCBvciBzaGFyZWQgZnJvbSBwYXN0IGV4cGVyaWVuY2UuIEFsc28sIHN0YXRpc3RpY2FsIG1vZGVscyB0aGF0IGFyZSB1c2VmdWwgZm9yIGV4cGxhbmluZyBoYXJkIHRvIHVuZGVyc3RhbmQgY29uY2VwdHMuDQoNCllvdXIgb3V0cHV0IHNob3VsZCBsb29rIHNvbWV0aGluZyBsaWtlIHRoaXM6DQoNCmBgYHtyIGZpbmQtcXVvdGVzLCBlY2hvPUZBTFNFfQ0KZm9ydW1fcXVvdGVzIDwtIHRzX2ZvcnVtX2RhdGEgJT4lDQogIHNlbGVjdChwb3N0X2NvbnRlbnQpICU+JSANCiAgZmlsdGVyKGdyZXBsKCd0aW1lJywgcG9zdF9jb250ZW50KSklPiUNCiAgZmlsdGVyKGdyZXBsKCdhZ3JlZScsIHBvc3RfY29udGVudCkpICU+JQ0KICBmaWx0ZXIoZ3JlcGwoJ2xlc3NvbicsIHBvc3RfY29udGVudCkpDQoNCnNhbXBsZV9uKGZvcnVtX3F1b3RlcywxMCkNCmBgYA0KDQojIyMjIENyZWF0aW5nIGEgRG9jdW1lbnQgVGVybSBNYXRyaXgNCg0KQXMgaGlnaGxpZ2h0ZWQgaW4gW0NoYXB0ZXIgNSBvZiBUZXh0IE1pbmluZyB3aXRoDQpSXShodHRwczovL3d3dy50aWR5dGV4dG1pbmluZy5jb20vZHRtLmh0bWwjY2FzdC1kdG0pLCB0aGUgYHRvcGljbW9kZWxzYA0KcGFja2FnZSBhbmQgdGhlIFtMYXRlbnQgRGlyaWNobGV0IGFsbG9jYXRpb24NCihMREEpXShodHRwczovL3d3dy50aWR5dGV4dG1pbmluZy5jb20vdG9waWNtb2RlbGluZy5odG1sI2xhdGVudC1kaXJpY2hsZXQtYWxsb2NhdGlvbikNCmFsZ29yaXRobSBhbmQgYExEQSgpYCBmdW5jdGlvbiBpdCB1c2VzIGV4cGVjdHMgZG9jdW1lbnQtdGVybSBtYXRyaXggYXMNCnRoZSBkYXRhIGlucHV0Lg0KDQpCZWZvcmUgd2UgY3JlYXRlIGEgb3VyIGRvY3VtZW50LXRlcm0gbWF0cml4LCBob3dldmVyLCB3ZSBoYXZlIGFuDQppbXBvcnRhbnQgZGVjaXNpb24gdG8gbWFrZToNCg0KPiAqKldoYXQgZG8gd2UgY29uc2lkZXIgdG8gYmUgYSAiZG9jdW1lbnQiIGluIGEgTU9PQy1FZCBkaXNjdXNzaW9uDQo+IGZvcnVtPyoqDQoNCg0KRm9yIGV4YW1wbGUsIHdlIGNvdWxkIGNvbnNpZGVyIGVhY2ggaW5kaXZpZHVhbCBkaXNjdXNzaW9uIHBvc3QsIG9yDQpgcG9zdF9pZGAgaW4gb3VyIGRhdGEgZnJhbWUsIGFzIGEgZG9jdW1lbnQuIEl0IG1pZ2h0IGFsc28gbWFrZSBzZW5zZSB0bw0KY29tYmluZSB0ZXh0cyBmcm9tIGFsbCBwb3N0cyB3aXRoaW4gZWFjaCBkaXNjdXNzaW9uLCBvcg0KYGRpc2NjdXNzaW9uX2lkYCwgYW5kIGNvbnNpZGVyIHRoYXQgYXMgYSBkb2N1bWVudCBzaW5jZSB0aGVzZSBwb3N0cyBhcmUNCm9mdGVuIGludGVyY29ubmVjdGVkIGFuIGJ1aWxkIG9mZiBvbmUgYW5vdGhlci4NCg0KRm9yIG5vdywgaG93ZXZlciwgbGV0IHRyZWF0IGVhY2ggaW5kaXZpZHVhbCBwb3N0IGFzIGEgdW5pcXVlICJkb2N1bWVudC4iDQpub3RlZCBhYm92ZSwgdG8gY3JlYXRlIG91ciBkb2N1bWVudCB0ZXJtIG1hdHJpeCwgd2UnbGwgbmVlZCB0byBmaXJzdA0KYGNvdW50KClgIGhvdyBtYW55IHRpbWVzIGVhY2ggYHdvcmRgIG9jY3VycyBpbiBlYWNoIGRvY3VtZW50LCBvcg0KYHBvc3RfaWRgIGluIG91ciBjYXNlLCBhbmQgY3JlYXRlIGEgbWF0cml4IHRoYXQgY29udGFpbnMgb25lIHJvdyBwZXINCnBvc3QgYXMgb3VyIG9yaWdpbmFsIGRhdGEgZnJhbWUgZGlkLCBidXQgbm93IGNvbnRhaW5zIGEgY29sdW1uIGZvciBlYWNoDQpgd29yZGAgaW4gdGhlIGVudGlyZSBjb3JwdXMgYW5kIGEgdmFsdWUgb2YgYG5gIGZvciBob3cgbWFueSB0aW1lcyB0aGF0DQp3b3JkIG9jY3VycyBpbiBlYWNoIHBvc3QuDQoNClRvIGNyZWF0ZSB0aGlzIGRvY3VtZW50IHRlcm0gbWF0cml4IGZyb20gb3VyIHBvc3QgY291bnRzLCB3ZSdsbCB1c2UgdGhlDQpgY2FzdF9kdG0oKWAgZnVuY3Rpb24gbGlrZSBzbyBhbmQgYXNzaWduIGl0IHRvIHRoZSB2YXJpYWJsZQ0KYGZvcnVtc19kdG1gOg0KDQpgYGB7ciBjYXN0LWR0bX0NCmZvcnVtc19kdG0gPC0gZm9ydW1zX3RpZHkgJT4lDQogIGNvdW50KHBvc3RfaWQsIHdvcmQpICU+JQ0KICBjYXN0X2R0bShwb3N0X2lkLCB3b3JkLCBuKQ0KYGBgDQoNCiMjIyMjIOKchSBDb21wcmVoZW5zaW9uIENoZWNrDQoNClRha2UgYSBsb29rIGF0IG91ciBgZm9ydW1zX2R0bWAgb2JqZWN0IGluIHRoZSBjb25zb2xlIGFuZCBhbnN3ZXIgdGhlDQpmb2xsb3dpbmcgcXVlc3Rpb246DQoNCjEuICBXaGF0ICJjbGFzcyIgb2Ygb2JqZWN0IGlzIGBmb3J1bXNfZHRtYD8NCi0gICAgSXQgaXMgYSBzaW1wbGUgdHJpcGxldCBtYXRyaXgNCjIuICBIb3cgbWFueSB1bmlxdWUgZG9jdW1lbnRzIGFuZCB0ZXJtcyBhcmUgaW5jbHVkZWQgb3VyIG1hdHJpeD8NCi0gICAgVGhlcmUgYXJlIDU3NzY2IGRvY3VtZW50cyBhbmQgMTM2MjAgdGVybXMgaW4gb3VyIE1hdHJpeA0KMy4gIFdoeSBtaWdodCB0aGVyZSBiZSBmZXdlciBkb2N1bWVudHMvcG9zdHMgdGhhbiB3ZXJlIGluIG91ciBvcmlnaW5hbA0KICAgIGRhdGEgZnJhbWU/IA0KLSAgIFNvbWUgZG9jdW1lbnRzIGFyZSByZW1vdmVkIGJlY2F1c2UgdGhleSBhcHBlYXIgaW4gdGhlIHRleHQgMCB0aW1lcy4NCjQuICBXaGF0IGV4YWN0bHkgaXMgbWVhbnQgYnkgInNwYXJzaXR5Ij8NCi0gICBTcGFyc2l0eSBtZWFucyB0aGF0IEEgdGVybS1kb2N1bWVudCBtYXRyaXggd2hvc2UgdGVybXMgYXJlIHJlbW92ZWQgYmVjYXVzZSB0aGV5IGhhdmUgYXQgbGVhc3QgYSBzcGFyc2UgcGVyY2VudGFnZSBjYWxjdWxhdGVkIGJ5IHRoZSB0ZXJtLWZyZXF1ZW5jeSB3ZWlnaHRpbmcuDQoNCmBgYHtyIGNsYXNzLWR0bX0NCmNsYXNzKGZvcnVtc19kdG0pDQoNCmZvcnVtc19kdG0NCmBgYA0KDQojIyMgMmMuIFRvIFN0ZW0gb3Igbm90IHRvIFN0ZW0/DQoNCk5leHQgd2UnbGwgbmVlZCB0byBwcmVwYXJlIG91ciBvcmlnaW5hbCBkYXRhIHNldCBmb3Igc3RydWN0dXJhbCB0b3BpYw0KbW9kZWxpbmcgdXNpbmcgdGhlIGB0ZXh0UHJvY2Vzc29yKClgIGZ1bmN0aW9uLiBUaGUgYHN0bWAgcGFja2FnZSBoYXMgYQ0KbnVtYmVyIG9mIGZlYXR1cmVzIHRoYXQgZXh0ZW5kIHRoZSBmdW5jdGlvbmFsaXR5IG9mIHRoZSBgdG9waWNtb2RlbHNgDQpwYWNrYWdlLCBpbmNsdWRpbmcgYW4gYXJndW1lbnQgZm9yICJzdGVtbWluZyIgd29yZHMsIHdoaWNoIFtTY2hvZmllbGQNCmFuZCBNaW1ubw0KKDIwMTYpXShodHRwczovL2RpcmVjdC5taXQuZWR1L3RhY2wvYXJ0aWNsZS9kb2kvMTAuMTE2Mi90YWNsX2FfMDAwOTkvNDMzNzAvQ29tcGFyaW5nLUFwcGxlcy10by1BcHBsZS1UaGUtRWZmZWN0cy1vZi1TdGVtbWVycykNCmRlc2NyaWJlIGFzIGZvbGxvd3M6DQoNCj4gU3RlbW1pbmcgaXMgYSBwb3B1bGFyIHdheSB0byByZWR1Y2UgdGhlIHNpemUgb2YgYSB2b2NhYnVsYXJ5IGluDQo+IG5hdHVyYWwgbGFuZ3VhZ2UgdGFza3MgYnkgY29uZmxhdGluZyB3b3JkcyB3aXRoIHJlbGF0ZWQgbWVhbmluZ3MuDQo+IFNwZWNpZmljYWxseSwgc3RlbW1pbmcgYWltcyB0byBjb252ZXJ0IHdvcmRzIHdpdGggdGhlIHNhbWUgInN0ZW0iIG9yDQo+IHJvb3QgKGUuZyAiY3JlYXRpdmUiIGFuZCAiY3JlYXRvciIpIHRvIGEgc2luZ2xlIHdvcmQgdHlwZSAoImNyZWF0ZSIpLg0KPiBUaG91Z2ggb3JpZ2luYWxseSBkZXZlbG9wZWQgaW4gdGhlIGNvbnRleHQgb2YgaW5mb3JtYXRpb24gcmV0cmlldmFsDQo+IChJUikgc3lzdGVtcywgc3RlbW1lcnMgYXJlIG5vdyBjb21tb25seSB1c2VkIGFzIGEgcHJlcHJvY2Vzc2luZyBzdGVwDQo+IGluIHVuc3VwZXJ2aXNlZCBtYWNoaW5lIGxlYXJuaW5nIHRhc2tzLg0KDQpUaGUgcmF0aW9uYWxlIGJlaGluZCBzdGVtbWluZyBpcyB0aGF0IGl0IGNhbiBkcmFtYXRpY2FsbHkgcmVkdWNlIHRoZQ0KbnVtYmVyIG9mIHdvcmRzIG9yIHRlcm1zIHRvIGJlIG1vZGVsZWQsIHdoaWNoIGluIHRoZW9yeSBzaG91bGQgaGVscA0Kc2ltcGxpZnkgYW5kIGltcHJvdmUgdGhlIHBlcmZvcm1hbmNlIG9mIHlvdXIgbW9kZWwuIFdlJ2xsIGV4cGxvcmUgdGhpcw0KYXNzdW1wdGlvbiBhIGxpdHRsZSBsYXRlciBpbiB0aGlzIHNlY3Rpb24uDQoNCiMjIyMgUHJvY2Vzc2luZyBhbmQgU3RlbW1pbmcgZm9yIFNUTQ0KDQpMaWtlIGB1bm5lc3RfdG9rZW5zKClgLCB0aGUgYHRleHRQcm9jZXNzb3IoKWAgZnVuY3Rpb24gaW5jbHVkZXMgc2V2ZXJhbA0KdXNlZnVsIGFyZ3VtZW50cyBmb3IgcHJvY2Vzc2luZyB0ZXh0IGxpa2UgY29udmVydGluZyB0ZXh0IHRvIGxvd2VyY2FzZQ0KYW5kIHJlbW92aW5nIHB1bmN0dWF0aW9uIGFuZCBudW1iZXJzLiBJJ3ZlIGluY2x1ZGVkIHNldmVyYWwgb2YgdGhlc2UgaW4NCnRoZSBzY3JpcHQgYmVsb3cgYWxvbmcgd2l0aCB0aGVpciBkZWZhdWx0cyB1c2VkIGlmIHlvdSBkbyBub3QgZXhwbGljaXRseQ0Kc3BlY2lmeSBpbiB5b3VyIGZ1bmN0aW9uLiBNb3N0IG9mIHRoZXNlIGFyZSBwcmV0dHkgaW50dWl0aXZlIGFuZCB5b3UgY2FuDQpsZWFybiBtb3JlIGJ5IHZpZXdpbmcgdGhlIGA/dGV4dFByb2Nlc3NvcmAgZG9jdW1lbnRhdGlvbi4NCg0KTGV0J3MgZ28gYWhlYWQgYW5kIHByb2Nlc3Mgb3VyIGRpc2N1c3Npb24gZm9ydW0gYHBvc3RfY29udGVudGAgaW4NCnByZXBhcmF0aW9uIGZvciBzdHJ1Y3R1cmFsIHRvcGljIG1vZGVsaW5nOg0KDQpgYGB7ciB0ZXh0UHJvY2Vzc29yfQ0KdGVtcCA8LSB0ZXh0UHJvY2Vzc29yKHRzX2ZvcnVtX2RhdGEkcG9zdF9jb250ZW50LCANCiAgICAgICAgICAgICAgICAgICAgbWV0YWRhdGEgPSB0c19mb3J1bV9kYXRhLCAgDQogICAgICAgICAgICAgICAgICAgIGxvd2VyY2FzZT1UUlVFLCANCiAgICAgICAgICAgICAgICAgICAgcmVtb3Zlc3RvcHdvcmRzPVRSVUUsIA0KICAgICAgICAgICAgICAgICAgICByZW1vdmVudW1iZXJzPVRSVUUsICANCiAgICAgICAgICAgICAgICAgICAgcmVtb3ZlcHVuY3R1YXRpb249VFJVRSwgDQogICAgICAgICAgICAgICAgICAgIHdvcmRMZW5ndGhzPWMoMyxJbmYpLA0KICAgICAgICAgICAgICAgICAgICBzdGVtPVRSVUUsDQogICAgICAgICAgICAgICAgICAgIG9ubHljaGFyYWN0ZXI9IEZBTFNFLCANCiAgICAgICAgICAgICAgICAgICAgc3RyaXBodG1sPVRSVUUsIA0KICAgICAgICAgICAgICAgICAgICBjdXN0b21zdG9wd29yZHM9TlVMTCkNCmBgYA0KDQpOb3RlIHRoYXQgdGhlIGZpcnN0IGFyZ3VtZW50IHRoZSBgdGV4dFByb2Nlc3NvcmAgZnVuY3Rpb24gZXhwZWN0cyBpcyB0aGUNCmNvbHVtbiBpbiBvdXIgZGF0YSBmcmFtZSB0aGF0IGNvbnRhaW5zIHRoZSB0ZXh0IHRvIGJlIHByb2Nlc3NlZCwgdGhlDQpzZWNvbmQgYXJndW1lbnQgYG1ldGFkYXRhID1gIGV4cGVjdHMgdGhlIGRhdGEgZnJhbWUgdGhhdCBjb250YWlucyB0aGUNCnRleHQgb2YgaW50ZXJlc3QgYW5kIHVzZXMgdGhlIGNvbHVtbiBuYW1lcyB0byBsYWJlbCB0aGUgbWV0YWRhdGEgc3VjaCBhcw0KY291cnNlIGlkcyBhbmQgZm9ydW0gbmFtZXMuIFRoaXMgbWVhdGRhdGEgY2FuIGJlIHVzZWQgdG8gaW1wcm92ZSB0aGUNCmFzc2lnbm1lbnQgb2Ygd29yZHMgdG8gdG9waWNzIGluIGEgY29ycHVzIGFuZCBleGFtaW5lIHRoZSByZWxhdGlvbnNoaXANCmJldHdlZW4gdG9waWNzIGFuZCB2YXJpb3VzIGNvdmFyaWF0ZXMgb2YgaW50ZXJlc3QuDQoNClVubGlrZSB0aGUgYHVubmVzdF90b2tlbnMoKWAgZnVuY3Rpb24sIHRoZSBvdXRwdXQgaXMgbm90IGEgbmljZSB0aWR5DQpkYXRhIGZyYW1lLiBUb3BpYyBtb2RlbGluZyB1c2luZyB0aGUgYHN0bWAgcGFja2FnZSByZXF1aXJlcyBhIHZlcnkNCnVuaXF1ZSBzZXQgb2YgaW5wdXRzIHRoYXQgYXJlIHNwZWNpZmljIHRvIHRoZSBwYWNrYWdlLiBUaGUgZm9sbG93aW5nDQpjb2RlIHdpbGwgcHVsbCBlbGVtZW50cyBmcm9tIHRoZSBgdGVtcGAgbGlzdCB0aGF0IHdhcyBjcmVhdGVkIHRoYXQgd2lsbA0KYmUgcmVxdWlyZWQgZm9yIHRoZSBgc3RtKClgIGZ1bmN0aW9uIHdlJ2xsIHVzZSBpbiBTZWN0aW9uIDQ6DQoNCmBgYHtyIHN0bS1pbnB1dHN9DQptZXRhIDwtIHRlbXAkbWV0YQ0Kdm9jYWIgPC0gdGVtcCR2b2NhYg0KZG9jcyA8LSB0ZW1wJGRvY3VtZW50cw0KDQpgYGANCg0KIyMjIyBTdGVtbWluZyBUaWR5IFRleHQNCg0KTm90aWNlIHRoYXQgdGhlIGB0ZXh0UHJvY2Vzc29yYCBzdGVtIGFyZ3VtZW50IHdlIHVzZWQgYWJvdmUgaXMgc2V0IHRvDQpgVFJVRWAgYnkgZGVmYXVsdC4gV2UgaGF2ZW4ndCBpbnRyb2R1Y2VkIHdvcmQgc3RlbW1pbmcgYXQgdGhpcyBwb2ludA0KYmVjYXVzZSB0aGVyZSBpcyBzb21lIGRlYmF0ZSBhYm91dCB0aGUgYWN0dWFsIHZhbHVlIG9mIHRoaXMgcHJvY2Vzcy4NCldoaWxlIHdvcmRzIGxpa2UgInN0dWRlbnRzIiBhbmQgInN0dWRlbnQiIG1pZ2h0IG1ha2Ugc2Vuc2UgdG8gY29sbGFwc2UNCmludG8gdGhlaXIgYmFzZSB3b3JkIGFuZCBhY3R1YWxseSBtYWtlIGFuYWx5c2VzIGFuZCB2aXN1YWxpemF0aW9ucyBtb3JlDQpjb25jaXNlIGFuZCBlYXNpZXIgdG8gaW50ZXJwcmV0LiBbSHZpdGZlbGR0IGFuZCBTaWxnZQ0KKDIwMjEpXShodHRwczovL3NtbHRhci5jb20vc3RlbW1pbmcuaHRtbCkgbm90ZSwgaG93ZXZlciwgdGhhdCB3b3JkcyBsaWtlDQp0aGUgZm9sbG93aW5nIGhhdmUgZHJhbWF0aWMgZGlmZmVyZW5jZXMgaW4gbWVhbmluZywgc2VtYW50aWNzLCBhbmQgdXNlDQphbmQgY291bGQgcmVzdWx0IGluIHBvb3IgbW9kZWxzIG9yIG1pc2ludGVycHJldGVkIHJlc3VsdHM6DQoNCi0gICBtZWFuaW5nIGFuZCBtZWFuDQotICAgbGlrZWx5LCBsaWtlLCBsaWtpbmcNCi0gICB1bml2ZXJzaXR5IGFuZCB1bml2ZXJzZQ0KDQpUaGUgZmlyc3Qgd29yZCBwYWlyIGlzIHBhcnRpY3VsYXJseSByZWxldmFudCB0byBkaXNjdXNzaW9uIHBvc3RzIGZyb20NCm91ciBUZWFjaGluZyBTdGF0aXN0aWNzIGNvdXJzZSBkYXRhLiBJbiBhZGRpdGlvbiwgY29sbGFwc2luZyB3b3JkcyBsaWtlDQoidGVhY2hlcnMiIGFuZCAidGVhY2hpbmciIGNvdWxkIGRyYW1hdGljYWxseSBhbHRlciB0aGUgcmVzdWx0cyBmcm9tIGENCnRvcGljIG1vZGVsLg0KDQpbU2Nob2ZpZWxkIGFuZCBNaW1ubw0KKDIwMTYpXShodHRwczovL2RpcmVjdC5taXQuZWR1L3RhY2wvYXJ0aWNsZS9kb2kvMTAuMTE2Mi90YWNsX2FfMDAwOTkvNDMzNzAvQ29tcGFyaW5nLUFwcGxlcy10by1BcHBsZS1UaGUtRWZmZWN0cy1vZi1TdGVtbWVycykNCnNwZWNpZmljYWxseSwNCg0KPiAqRGVzcGl0ZSB0aGVpciBmcmVxdWVudCB1c2UgaW4gdG9waWMgbW9kZWxpbmcsIHdlIGZpbmQgdGhhdCBzdGVtbWVycw0KPiBwcm9kdWNlIG5vIG1lYW5pbmdmdWwgaW1wcm92ZW1lbnQgaW4gbGlrZWxpaG9vZCBhbmQgY29oZXJlbmNlIGFuZCBpbg0KPiBmYWN0IGNhbiBkZWdyYWRlIHRvcGljIHN0YWJpbGl0eS4qDQoNCkZvciBub3csIHdlIHdpbGwgbGVhdmUgYXMgaXMgdGhlIGBmb3J1bXNfZHRtYCB3ZSBjcmVhdGVkIGVhcmxpZXIgd2l0aA0Kd29yZHMgdW5zdGVtbWVkLCBidXQgd2hhdCBpZiB3ZSB3YW50ZWQgdG8gc3RlbSB3b3JkcyBpbiBhICJ0aWR5IiB3YXk/DQoNClNpbmNlIHRoZSBgdW5uZXN0X3Rva2VucygpYCBmdW5jdGlvbiBkb2VzIG5vdCAoaW50ZW50aW9uYWxseSBJIGJlbGlldmUpDQppbmNsdWRlIGEgc3RlbW1pbmcgZnVuY3Rpb24sIG9uZSBhcHByb2FjaCB3b3VsZCBiZSB0byB1c2UgdGhlDQpgd29yZFN0ZW0oKWAgZnVuY3Rpb24gZnJvbSB0aGUgYHNub3diYWxsQ2AgcGFja2FnZSB0byBlaXRoZXIgcmVwbGFjZSBvdXINCmB3b3Jkc2AgY29sdW1uIHdpdGggYSB3b3JkIHN0ZW1zIG9yIGNyZWF0ZSBhIG5ldyB2YXJpYWJsZSBjYWxsZWQgYHN0ZW1gDQp3aXRoIG91ciBzdGVtbWVkIHdvcmRzLiBMZXQncyBkbyB0aGUgbGF0dGVyIGFuZCB0YWtlIGEgbG9vayBhdCB0aGUNCm9yaWdpbmFsIHdvcmRzIGFuZCB0aGUgc3RlbSB0aGF0IHdhcyBwcm9kdWNlZDoNCg0KYGBge3Igd29yZFN0ZW19DQpzdGVtbWVkX2ZvcnVtcyA8LSB0c19mb3J1bV9kYXRhICU+JQ0KICB1bm5lc3RfdG9rZW5zKG91dHB1dCA9IHdvcmQsIGlucHV0ID0gcG9zdF9jb250ZW50KSAlPiUNCiAgYW50aV9qb2luKHN0b3Bfd29yZHMsIGJ5ID0gIndvcmQiKSAlPiUNCiAgbXV0YXRlKHN0ZW0gPSB3b3JkU3RlbSh3b3JkKSkNCg0Kc3RlbW1lZF9mb3J1bXMNCmBgYA0KDQpZb3UgY2FuIHNlZSB0aGF0IHdvcmRzIGxpa2UgImFjdGl2aXR5IiBhbmQgImFjdGl2aXRpZXMiIHRoYXQgb2NjdXINCmZyZXF1ZW50bHkgaW4gb3VyIGRpc2N1c3Npb25zIGhhdmUgYmVlbiByZWR1Y2VkIHRvIHRoZSB3b3JkIHN0ZW0NCiJhY3RpdiIuIElmIHlvdSBhcmUgaW50ZXJlc3RlZCBpbiBsZWFybmluZyBvdGhlciBhcHByb2FjaGVzIGZvciB3b3JkDQpzdGVtbWluZyBpbiBSLCBhcyB3ZWxsIGFzIHJlYWRpbmcgYSBtb3JlIGluIGRlcHRoIGRlc2NyaXB0aW9uIG9mIHRoZQ0Kc3RlbW1pbmcgcHJvY2VzcywgSSBoaWdobHkgcmVjb21tZW5kIHRoZSBbQ2hhcHRlciA0DQpTdGVtbWluZ10oaHR0cHM6Ly9zbWx0YXIuY29tL3N0ZW1taW5nLmh0bWwpIGZyb20gSHZpdGZlbGR0IGFuZCBTaWxnZQ0KKDIwMjEpIGJvb2ssICpTdXBlcnZpc2VkIE1hY2hpbmUgTGVhcm5pbmcgZm9yIFRleHQgQW5hbHlzaXMgaW4gUiouDQoNCiMjIyMjIOKchSBDb21wcmVoZW5zaW9uIENoZWNrDQoNCkNvbXBsZXRlIHRoZSBmb2xsb3dpbmcgY29kZSB1c2luZyB3aGF0IHdlIGxlYXJuZWQgaW4gdGhlIHNlY3Rpb24gb24NCltDcmVhdGluZyBhIERvY3VtZW50IFRlcm0gTWF0cml4XSBhbmQgYW5zd2VyIHRoZSBmb2xsb3dpbmcgcXVlc3Rpb25zOg0KDQoxLiAgSG93IG1hbnkgZmV3ZXIgdGVybXMgYXJlIGluIG91ciBzdGVtbWVkIGRvY3VtZW50IHRlcm0gbWF0cml4PyANCi0gVGhlIHN0ZW1tZWQgY29ycHVzIGhhcyAxMCwwMDEgb2JzZXJ2YXRpb25zIGNvbXBhcmVkIHRvIHRoZSAxOTIxNTkgb2JzZXJ2YXRpb25zLg0KMi4gIERpZCBzdGVtbWluZyB3b3JkcyBzaWduaWZpY2FudGx5IHJlZHVjZSB0aGUgc3BhcnNpdHkgb2YgdGhlIG5ldHdvcms/IA0KLSAgICBZZXMgdGhlIGZvcnVtc19kdG0gaGFzIDc4MzkwMjc5IHNwYXJzZSBkb2N1bWVudHMgYW5kIHRoZSBzdGVtbWVkX2R0bSBoYXMgNTc1Mjk1ODEgc3BhcnNlIGl0ZW1zLiBUaGlzIGlzIGEgMjclIGRlY3JlYXNlIGluIHNwYXJzaXR5IG9mIHRoZSBuZXR3b3JrLg0KDQoqKkhpbnQ6KiogTWFrZSBzdXJlIHlvdXIgY29kZSBpbmNsdWRlcyBzdGVtIGNvdW50cyByYXRoZXIgdGhhbiB3b3JkDQpjb3VudHMuDQoNCmBgYHtyIHN0ZW0tcHJhY3RpY2UsIGV2YWw9RkFMU0V9DQpzdGVtbWVkX2R0bSA8LSB0c19mb3J1bV9kYXRhICU+JQ0KICB1bm5lc3RfdG9rZW5zKG91dHB1dCA9IHdvcmQsIGlucHV0ID0gcG9zdF9jb250ZW50KSAlPiUNCiAgYW50aV9qb2luKHN0b3Bfd29yZHMsIGJ5ID0gIndvcmQiKSAlPiUNCiAgbXV0YXRlKHN0ZW0gPSB3b3JkU3RlbSh3b3JkKSkgJT4lDQogIGNvdW50KHN0ZW0sIHNvcnQgPSBUUlVFKQ0KICANCnN0ZW1tZWRfZHRtDQpgYGANCg0KYGBge3Igc3RlbS1jb3VudHMsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpzdGVtbWVkX2R0bSA8LSB0c19mb3J1bV9kYXRhICU+JQ0KICB1bm5lc3RfdG9rZW5zKG91dHB1dCA9IHdvcmQsIGlucHV0ID0gcG9zdF9jb250ZW50KSAlPiUNCiAgYW50aV9qb2luKHN0b3Bfd29yZHMsIGJ5ID0gIndvcmQiKSAlPiUNCiAgbXV0YXRlKHN0ZW0gPSB3b3JkU3RlbSh3b3JkKSkgJT4lDQogIGNvdW50KHBvc3RfaWQsIHN0ZW0sIHNvcnQgPSBUUlVFKSAlPiUNCiAgY2FzdF9kdG0ocG9zdF9pZCwgc3RlbSwgbikNCg0Kc3RlbW1lZF9kdG0NCmZvcnVtc19kdG0NCg0Kc3RlbV9jb3VudHMgPC0gdHNfZm9ydW1fZGF0YSAlPiUNCiAgdW5uZXN0X3Rva2VucyhvdXRwdXQgPSB3b3JkLCBpbnB1dCA9IHBvc3RfY29udGVudCkgJT4lDQogIGFudGlfam9pbihzdG9wX3dvcmRzLCBieSA9ICJ3b3JkIikgJT4lDQogIG11dGF0ZShzdGVtID0gd29yZFN0ZW0od29yZCkpICU+JQ0KICBjb3VudChzdGVtLCBzb3J0ID0gVFJVRSkNCg0Kc3RlbV9jb3VudHMNCmBgYA0KDQojIyAzLiBNT0RFTA0KDQpUaGlzIHVuaXQgcHJvdmlkZXMgb3VyIGZpcnN0IG9wcG9ydHVuaXR5IGZvciBtb2RlbGluZyBhIHRleHQgYXMgZGF0YS4gSW4NCnZlcnkgc2ltcGxlIHRlcm1zLCBtb2RlbGluZyBpbnZvbHZlcyBkZXZlbG9waW5nIGEgbWF0aGVtYXRpY2FsIHN1bW1hcnkNCm9mIGEgZGF0YXNldC4gVGhlc2Ugc3VtbWFyaWVzIGNhbiBoZWxwIHVzIGZ1cnRoZXIgZXhwbG9yZSB0cmVuZHMgYW5kDQpwYXR0ZXJucyBpbiBvdXIgZGF0YS4NCg0KSW4gdGhlaXIgYm9vaywgKkxlYXJuaW5nIEFuYWx5dGljcyBHb2VzIHRvIFNjaG9vbCwqIEtydW1tIGFuZCBNZWFucw0KKDIwMTgpIGRlc2NyaWJlIHR3byBnZW5lcmFsIHR5cGVzIG9mIG1vZGVsaW5nIGFwcHJvYWNoZXMgdXNlZCBpbiB0aGUNCkRhdGEtSW50ZW5zaXZlIFJlc2VhcmNoIHdvcmtmbG93OiB1bnN1cGVydmlzZWQgYW5kIHN1cGVydmlzZWQgbWFjaGluZQ0KbGVhcm5pbmcuIEluIGRpc3Rpbmd1aXNoaW5nIGJldHdlZW4gdGhlIHR3bywgdGhleSBub3RlOg0KDQo+ICpVbnN1cGVydmlzZWQgbGVhcm5pbmcgYWxnb3JpdGhtcyBjYW4gYmUgdXNlZCB0byB1bmRlcnN0YW5kIHRoZQ0KPiBzdHJ1Y3R1cmUgb2Ygb25lJ3MgZGF0YXNldC4gU3VwZXJ2aXNlZCBtb2RlbHMsIG9uIHRoZSBvdGhlciBoYW5kLCBoZWxwDQo+IHRvIHF1YW50aWZ5IHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBmZWF0dXJlcyBhbmQgYSBrbm93biBvdXRjb21lLiBLbm93bg0KPiBvdXRjb21lcyBhcmUgYWxzbyBjb21tb25seSByZWZlcnJlZCB0byBhcyBsYWJlbHMgb3IgZGVwZW5kZW50DQo+IHZhcmlhYmxlcy4qDQoNCkluIFNlY3Rpb24gMyB3ZSBmb2N1cyBvbiBUb3BpYyBNb2RlbGluZywgYW4gdW5zdXBlcnZpc2VkIGxlYXJuaW5nDQphcHByb2FjaCB0byBhdXRvbWF0aWNhbGx5IGlkZW50aWZ5IHRvcGljcyBpbiBhIGNvbGxlY3Rpb24gb2YgZG9jdW1lbnRzLg0KSW4gZmFjdCwgd2UnbGwgZXhwbG9yZSB0d28gZGlmZmVyZW50IGFwcHJvYWNoZXMgdG8gdG9waWMgbW9kZWxpbmcsIGFzDQp3ZWxsIGFzIHN0cmF0ZWdpZXMgZm9yIGlkZW50aWZ5aW5nIHRoZSAicmlnaHQiIG51bWJlciBvZiB0b3BpY3M6DQoNCmEuICAqKkZpdHRpbmcgYSBUb3BpYyBNb2RlbGluZyB3aXRoIExEQSoqLiBJbiB0aGlzIHNlY3Rpb24gd2UgbGVhcm4gdG8NCiAgICB1c2UgdGhlIGB0b3BpY21vZGVsc2AgcGFja2FnZSBhbmQgYXNzb2NpYXRlZCBgTERBKClgIGZ1bmN0aW9uIGZvcg0KICAgIHVuc3VwZXJ2aXNlZCBjbGFzc2lmaWNhdGlvbiBvZiBvdXIgZm9ydW0gZGlzY3Vzc2lvbnMgdG8gZmluZCBuYXR1cmFsDQogICAgZ3JvdXBpbmdzIG9mIHdvcmRzLCBvciB0b3BpY3MuDQpiLiAgKipGaXR0aW5nIGEgU3RydWN0dXJhbCBUb3BpYyBNb2RlbCoqLiBXZSB0aGVuIGV4cGxvcmUgdGhlIHVzZSBvZiB0aGUNCiAgICBgc3RtYCBwYWNrYWdlIGFuZCBgc3RtKClgIGZ1bmN0aW9uIHRvIGZpdCBvdXIgbW9kZWwgYW5kIHVzZXMNCiAgICBtZXRhZGF0YSBhYm91dCBkb2N1bWVudHMgdG8gaW1wcm92ZSB0aGUgYXNzaWdubWVudCBvZiB3b3JkcyB0bw0KICAgICJ0b3BpY3MiIGluIGEgY29ycHVzLg0KYy4gICoqQ2hvb3NpbmcgSy4qKiBGaW5hbGx5LCB3ZSB3cmFwIHVwIFNlY3Rpb24gMyBieSBsZWFybmluZyBhYm91dA0KICAgIGRpYWdub3N0aWMgcHJvcGVydGllcyBsaWtlIGV4Y2x1c2l2aXR5LCBzZW1hbnRpYyBjb2hlcmVuY2UsIGFuZA0KICAgIGhlbGRvdXQgbGlrZWxpaG9vZCBmb3IgaGVscGluZyB0byBzZWxlY3QgYW4gYXBwcm9wcmlhdGUgbnVtYmVyIG9mDQogICAgdG9waWNzLg0KDQojIyMgM2EuIEZpdHRpbmcgYSBUb3BpYyBNb2RlbGluZyB3aXRoIExEQQ0KDQpCZWZvcmUgcnVubmluZyBvdXIgZmlyc3QgdG9waWMgbW9kZWwgdXNpbmcgdGhlIGBMREEoKWAgZnVuY3Rpb24sIGxldCdzDQpxdWljayByZWNhcCBmcm9tIG91ciByZWFkaW5ncyBzb21lIGJhc2ljIHByaW5jaXBsZXMgYmVoaW5kIExhdGVudA0KRGlyaWNobGV0IGFsbG9jYXRpb24gYW5kIHdoeSBMREEgaXMgb2YgcHJlZmVycmVkIG92ZXIgb3RoZXIgYXV0b21hdGljDQpjbGFzc2lmaWNhdGlvbiBvciBjbHVzdGVyaW5nIGFwcHJvYWNoZXMuDQoNClVubGlrZSBzaW1wbGUgZm9ybXMgb2YgY2x1c3RlciBhbmFseXNpcyBzdWNoIGFzIGstbWVhbnMgY2x1c3RlcmluZywgTERBDQppcyBhICoqIm1peHR1cmUiIG1vZGVsKiosIHdoaWNoIGluIG91ciBjb250ZXh0IG1lYW5zIHRoYXQ6DQoNCjEuICAqKkV2ZXJ5IFtkb2N1bWVudF17LnVsfSBjb250YWlucyBhIG1peHR1cmUgb2YgdG9waWNzLioqIFVubGlrZQ0KICAgIGFsZ29yaXRobXMgbGlrZSBrLW1lYW5zLCBMREEgdHJlYXRzIGVhY2ggZG9jdW1lbnQgYXMgYSBtaXh0dXJlIG9mDQogICAgdG9waWNzLCB3aGljaCBhbGxvd3MgZG9jdW1lbnRzIHRvICJvdmVybGFwIiBlYWNoIG90aGVyIGluIHRlcm1zIG9mDQogICAgY29udGVudCwgcmF0aGVyIHRoYW4gYmVpbmcgc2VwYXJhdGVkIGludG8gZGlzY3JldGUgZ3JvdXBzLiBTbyBpbg0KICAgIHByYWN0aWNlLCB0aGlzIG1lYW5zIHRoYXQgYSBkaXNjdXNzaW9uIGZvcnVtIHBvc3QgY291bGQgaGF2ZSBhbg0KICAgIGVzdGltYXRlZCB0b3BpYyBwcm9wb3J0aW9uIG9mIDcwJSBmb3IgVG9waWMgMSAoZS5nLiBiZSBtb3N0bHkgYWJvdXQNCiAgICBhIFRvcGljIDEpLCBidXQgYWxzbyBiZSBwYXJ0bHkgYWJvdXQgVG9waWMgMi4NCjIuICAqKkV2ZXJ5IFt0b3BpY117LnVsfSBjb250YWlucyBhIG1peHR1cmUgb2Ygd29yZHMuKirCoEZvciBleGFtcGxlLCBpZg0KICAgIHdlIHNwZWNpZmllZCBpbiBvdXIgTERBIG1vZGVsIGp1c3QgMiB0b3BpY3MgZm9yIG91ciBkaXNjdXNzaW9uDQogICAgcG9zdHMsIHdlIG1pZ2h0IGZpbmQgdGhhdCBvbmUgdG9waWMgc2VlbXMgdG8gYmUgYWJvdXQgcGVkYWdvZ3kgd2hpbGUNCiAgICBhbm90aGVyIGlzIGFib3V0IGxlYXJuaW5nLiBUaGUgbW9zdCBjb21tb24gd29yZHMgaW4gdGhlIHBlZGFnb2d5DQogICAgdG9waWMgbWlnaHQgYmUgInRlYWNoZXIiLCAic3RyYXRlZ2llcyIsIGFuZCAiaW5zdHJ1Y3Rpb24iLCB3aGlsZSB0aGUNCiAgICBsZWFybmluZyB0b3BpYyBtYXkgYmUgbWFkZSB1cCBvZiB3b3JkcyBsaWtlICJ1bmRlcnN0YW5kaW5nIiBhbmQNCiAgICAic3R1ZGVudHMiLiBIb3dldmVyLCB3b3JkcyBjYW4gYmUgc2hhcmVkIGJldHdlZW4gdG9waWNzIGFuZCB3b3Jkcw0KICAgIGxpa2UgInN0YXRpc3RpY3MiIG9yICJhc3Nlc3NtZW50IiBtaWdodCBhcHBlYXIgaW4gYm90aCBlcXVhbGx5Lg0KDQpTaW1pbGFyIHRvIGstbWVhbnMgb3RoZXIgb3RoZXIgc2ltcGxlIGNsdXN0ZXJpbmcgYXBwcm9hY2hlcywgaG93ZXZlciwNCkxEQSBkb2VzIHJlcXVpcmUgdXMgdG8gc3BlY2lmeSBhIHZhbHVlIG9mICprKiBmb3IgdGhlIG51bWJlciBvZiB0b3BpY3MNCmluIG91ciBjb3JwdXMuIFNlbGVjdGluZyAqayogaXMgbm8gdHJpdmlhbCBtYXR0ZXIgYW5kIGNhbiBncmVhdGx5IGltcGFjdA0KeW91ciByZXN1bHRzLg0KDQpTaW5jZSB3ZSBkb24ndCBoYXZlIGEgaGF2ZSBzdHJvbmcgcmF0aW9uYWxlIGFib3V0IHRoZSBudW1iZXIgb2YgdG9waWNzDQp0aGF0IG1pZ2h0IGV4aXN0IGluIGRpc2N1c3Npb24gZm9ydW1zLCBsZXQncyB1c2UgdGhlIGBuX2Rpc3RpbmN0KClgDQpmdW5jdGlvbiBmcm9tIHRoZSBgZHBseXJgIHBhY2thZ2UgdG8gZmluZCB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBmb3J1bQ0KbmFtZXMgaW4gb3VyIGNvdXJzZSBkYXRhIGFuZCBydW4gd2l0aCB0aGF0Og0KDQpgYGB7ciBuLWRpc3RpbmN0fQ0Kbl9kaXN0aW5jdCh0c19mb3J1bV9kYXRhJGZvcnVtX25hbWUpDQpgYGANCg0KU2luY2UgaXQgbG9va3MgbGlrZSB0aGVyZSBhcmUgMjAgZGlzdGluY3QgZGlzY3Vzc2lvbiBmb3J1bXMsIHdlJ2xsIHVzZQ0KdGhhdCBhcyBvdXIgdmFsdWUgZm9yIHRoZSBgayA9YCBhcmd1bWVudCBvZiB0aGUgYExEQSgpYC4gQmUgcGF0aWVudA0Kd2hpbGUgdGhpcyBydW5zLCBzaW5jZSB0aGUgZGVmYXVsdCBzZXR0aW5nIG9mIGlzIHRvIHBlcmZvcm0gYSBsYXJnZQ0KbnVtYmVyIG9mIGl0ZXJhdGlvbnMuDQoNCmBgYHtyIExEQX0NCm5fZGlzdGluY3QodHNfZm9ydW1fZGF0YSRmb3J1bV9uYW1lKQ0KDQpmb3J1bXNfbGRhIDwtIExEQShmb3J1bXNfZHRtLCANCiAgICAgICAgICAgICAgICAgIGsgPSAyMCwgDQogICAgICAgICAgICAgICAgICBjb250cm9sID0gbGlzdChzZWVkID0gNTg4KQ0KICAgICAgICAgICAgICAgICAgKQ0KDQpmb3J1bXNfbGRhDQpgYGANCg0KTm90ZSB0aGF0IHdlIHVzZWQgdGhlIGBjb250cm9sID1gIGFyZ3VtZW50IHRvIHBhc3MgYSByYW5kb20gbnVtYmVyDQooYDU4OGApIHRvIHNlZWQgdGhlIGFzc2lnbm1lbnQgb2YgdG9waWNzIHRvIGVhY2ggd29yZCBpbiBvdXIgY29ycHVzLg0KU2luY2UgTERBIGlzIGEgW3N0b2NoYXN0aWMNCmFsZ29yaXRobV0oaHR0cHM6Ly9tYWNoaW5lbGVhcm5pbmdtYXN0ZXJ5LmNvbS9zdG9jaGFzdGljLWluLW1hY2hpbmUtbGVhcm5pbmcvKQ0KdGhhdCBjb3VsZCBoYXZlIGRpZmZlcmVudCByZXN1bHRzIGRlcGVuZGluZyBvbiB3aGVyZSB0aGUgYWxnb3JpdGhtDQpzdGFydHMsIHNwZWNpZmllZCBhIGBzZWVkYCBmb3IgcmVwcm9kdWNpYmlsaXR5IGFuZCBzbyB3ZSdyZSBhbGwgc2VlaW5nDQp0aGUgc2FtZSByZXN1bHRzIGV2ZXJ5IHRpbWUgd2Ugc3BlY2lmeSB0aGUgc2FtZSBudW1iZXIgb2YgdG9waWNzLg0KDQpBbmQgdHlpbmcgYmFjayB0byBvdXIgd29yayBpbiBVbml0IDEsIEJhaWwgKDIwMjApIG5vdGVzIHRoYXQgdG9waWMNCmFzc2lnbm1lbnRzIGZvciBlYWNoIHdvcmQgYXJlIHVwZGF0ZWQgaW4gYW4gaXRlcmF0aXZlIGZhc2hpb24gYW5kIHRoYXQNCkxEQSBlbXBsb3lzIHRoZSBUZXJtIEZyZXF1ZW5jeS1JbnZlcnNlIERvY3VtZW50IEZyZXF1ZW5jeSAoVEYtSURGKQ0KbWV0cmljIHRvIGFzc2lnbiBwcm9iYWJpbGl0aWVzLg0KDQojIyMgM2IuIEZpdHRpbmcgYSBTdHJ1Y3R1cmFsIFRvcGljIE1vZGVsDQoNCkJhaWwgbm90ZXMgdGhhdCBMREEsIHdoaWxlIHBlcmhhcHMgdGhlIG1vc3QgY29tbW9uIGFwcHJvYWNoIHRvIHRvcGljDQptb2RlbGluZywgaXMganVzdCBvbmUgb2YgbWFueSBkaWZmZXJlbnQgdHlwZXMsIGluY2x1ZGluZyBEeW5hbWljIFRvcGljDQpNb2RlbHMsIENvcnJlbGF0ZWQgVG9waWMgTW9kZWxzLCBIaWVyYXJjaGljYWwgVG9waWMgTW9kZWxzLCBhbmQgbW9yZQ0KcmVjZW50bHksIFN0cnVjdHVyYWwgVG9waWMgTW9kZWxpbmcgKFNUTSkuIEhlIGFyZ3VlcyB0aGF0IG9uZSByZWFzb24gU1RNDQpoYXMgcmlzaW5nIGluIHBvcHVsYXJpdHkgYW5kIHVzZSBpcyB0aGF0IGl0IGVtcGxveXMgbWV0YSBkYXRhIGFib3V0DQpkb2N1bWVudHMgdG8gaW1wcm92ZSB0aGUgYXNzaWdubWVudCBvZiB3b3JkcyB0byB0b3BpY3MgaW4gYSBjb3JwdXMgYW5kDQp0aGF0IGNhbiBiZSB1c2VkIHRvIGV4YW1pbmUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIGNvdmFyaWF0ZXMgYW5kDQpkb2N1bWVudHMuwqANCg0KQWxzbywgc2luY2UgSnVsaWEgU2lsZ2UgaGFzIGluZGljYXRlZCB0aGF0IFNUTSBpcywgIm15IGN1cnJlbnQgZmF2b3JpdGUNCmltcGxlbWVudGF0aW9uIG9mIHRvcGljIG1vZGVsaW5nIGluIFIiIGFuZCBoYXMgYnVpbHQgc3VwcG9ydHMgaW4gdGhlDQpgdGlkeXRleHRgIHBhY2thZ2UgZm9yIGJ1aWxkaW5nIHN0cnVjdHVyYWwgdG9waWMgbW9kZWxzLCB0aGlzIHBhY2thZ2UNCmRlZmluaXRlbHkgaXMgd29ydGggZGlzY3Vzc2luZyBpbiB0aGlzIHdhbGt0aHJvdWdoLiBJIGFsc28gaGlnaGx5DQpyZWNvbW1lbmQgaGVyIG93biB3YWxrdGhyb3VnaCBvZiB0aGUgYHN0bWAgcGFja2FnZTogW1RoZSBnYW1lIGlzIGFmb290IQ0KVG9waWMgbW9kZWxpbmcgb2YgU2hlcmxvY2sgSG9sbWVzDQpzdG9yaWVzXShodHRwczovL2p1bGlhc2lsZ2UuY29tL2Jsb2cvc2hlcmxvY2staG9sbWVzLXN0bS8pIGFzIHdlbGwgYXMNCmhlciBmb2xsb3cgdXAgcG9zdCwgW1RyYWluaW5nLCBldmFsdWF0aW5nLCBhbmQgaW50ZXJwcmV0aW5nIHRvcGljDQptb2RlbHNdKGh0dHBzOi8vanVsaWFzaWxnZS5jb20vYmxvZy9ldmFsdWF0aW5nLXN0bS8pLg0KDQojIyMjIFRoZSBgc3RtYCBQYWNrYWdlDQoNCkFzIHdlJ3ZlIHNlZW4gYWJvdmUsIFNUTSBwcm9kdWNlZCBhbiB1bnVzdWFsIGB0ZW1wYCB0ZXh0UHJvY2Vzc29yIG91dHB1dA0KdGhhdCBpcyB1bmlxdWUgdG8gdGhlIGBzdG1gIHBhY2thZ2UuIEFuZCBhcyB5b3UndmUgcHJvYmFibHkgYWxyZWFkeQ0KZ3Vlc3NlZCwgdGhlIGBzdG0oKWAgZnVuY3Rpb24gZm9yIGZpdHRpbmcgYSBzdHJ1Y3R1cmFsIHRvcGljIG1vZGVsIGRvZXMNCm5vdCB0YWtlIGEgZmFpcmx5IHN0YW5kYXJkIGRvY3VtZW50IHRlcm0gbWF0cml4IGxpa2UgdGhlIGBMREEoKWANCmZ1bmN0aW9uLg0KDQpCZWZvcmUgd2UgZml0IG91ciBtb2RlbCwgd2UnbGwgaGF2ZSB0byBleHRyYWN0IHRoZSBlbGVtZW50cyBmcm9tIHRoZQ0KYHRlbXBgIG9iamVjdCBjcmVhdGVkIGFmdGVyIHdlIHByb2Nlc3NlZCBvdXIgdGV4dC4gU3BlY2lmaWNhbGx5LCB0aGUNCmBzdG0oKWAgZnVuY3Rpb24gZXhwZWN0cyB0aGUgZm9sbG93aW5nIGFyZ3VtZW50czoNCg0KLSAgIGBkb2N1bWVudHMgPWAgdGhlIGRvY3VtZW50IHRlcm0gbWF0cml4IHRvIGJlIG1vZGVsZWQgaW4gdGhlIG5hdGl2ZQ0KICAgIHN0bSBmb3JtYXQNCi0gICBgZGF0YSA9YCBhbiBvcHRpb25hbCBkYXRhIGZyYW1lIGNvbnRhaW5pbmcgbWV0YSBkYXRhIGZvciB0aGUNCiAgICBwcmV2YWxlbmNlIGFuZC9vciBjb250ZW50IGNvdmFyaWF0ZXMgdG8gaW5jbHVkZSBpbiB0aGUgbW9kZWwNCi0gICBgdm9jYWIgPWAgYSBjaGFyYWN0ZXIgdmVjdG9yIHNwZWNpZnlpbmcgdGhlIHdvcmRzIGluIHRoZSBjb3JwdXMgaW4NCiAgICB0aGUgb3JkZXIgb2YgdGhlIHZvY2FiIGluZGljZXMgaW4gZG9jdW1lbnRzLg0KDQpMZXQncyBnbyBhaGVhZCBhbmQgZXh0cmFjdCB0aGVzZSBlbGVtZW50czoNCg0KYGBge3Igc3RtLWRvY3N9DQpkb2NzIDwtIHRlbXAkZG9jdW1lbnRzIA0KbWV0YSA8LSB0ZW1wJG1ldGEgDQp2b2NhYiA8LSB0ZW1wJHZvY2FiIA0KYGBgDQoNCkFuZCBub3cgdXNlIHRoZXNlIGVsZW1lbnRzIHRvIGZpdCB0aGUgbW9kZWwgdXNpbmcgdGhlIHNhbWUgbnVtYmVyIG9mDQp0b3BpY3MgZm9yICpLKiB0aGF0IHdlIHNwZWNpZmllZCBmb3Igb3VyIExEQSB0b3BpYyBtb2RlbC4gTGV0J3MgYWxzbw0KdGFrZSBhZHZhbnRhZ2Ugb2YgdGhlIGZhY3QgdGhhdCB3ZSBjYW4gaW5jbHVkZSB0aGUgYGNvdXJzZV9pZGAgYW5kDQpgZm9ydW1faWRgIGNvdmFyaWF0ZXMgaW4gdGhlIGBwcmV2ZWFsZW5jZSA9YCBhcmd1bWVudCB0byBoZWxwIGltcHJvdmUsDQppbiB0aGVvcnksIG91ciBtb2RlbCBmaXQ6DQoNCmBgYHtyIHN0bSwgbWVzc2FnZT1GQUxTRX0NCmZvcnVtc19zdG0gPC0gc3RtKGRvY3VtZW50cz1kb2NzLCANCiAgICAgICAgIGRhdGE9bWV0YSwNCiAgICAgICAgIHZvY2FiPXZvY2FiLCANCiAgICAgICAgIHByZXZhbGVuY2UgPX4gY291cnNlX2lkICsgZm9ydW1faWQsDQogICAgICAgICBLPTIwLA0KICAgICAgICAgbWF4LmVtLml0cz0yNSwNCiAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkNCg0KZm9ydW1zX3N0bQ0KYGBgDQoNCkFzIG5vdGVkIGVhcmxpZXIsIHRoZSBgc3RtYCBwYWNrYWdlIGhhcyBhIG51bWJlciBvZiBoYW5keSBmZWF0dXJlcy4gT25lDQpvZiB0aGVzZSBpcyB0aGUgYHBsb3QuU1RNKClgIGZ1bmN0aW9uIGZvciB2aWV3aW5nIHRoZSBtb3N0IHByb2JhYmxlDQp3b3JkcyBhc3NpZ25lZCB0byBlYWNoIHRvcGljLg0KDQpCeSBkZWZhdWx0LCBpdCBvbmx5IHNob3dzIHRoZSBmaXJzdCAzIHRlcm1zIHNvIGxldCdzIGNoYW5nZSB0aGF0IHRvIDUgdG8NCmhlbHAgd2l0aCBpbnRlcnByZXRhdGlvbjoNCg0KYGBge3IgcGxvdC1zdG19DQpwbG90LlNUTShmb3J1bXNfc3RtLCBuID0gNSkNCmBgYA0KDQpOb3RlIHRoYXQgeW91IGNhbiBhbHNvIGp1c3QgdXNlIGBwbG90KClgIGFzIHdlbGw6DQoNCmBgYHtyIHBsb3R9DQpwbG90KGZvcnVtc19zdG0sIG4gPSA1KQ0KYGBgDQoNCiMjIyMjIOKchSBDb21wcmVoZW5zaW9uIENoZWNrDQoNCkZpdCBhIG1vZGVsIGZvciBib3RoIExEQSBhbmQgU1RNIHVzaW5nIGRpZmZlcmVudCB2YWx1ZXMgZm9yIEsgYW5kIGFuc3dlcg0KdGhlIGZvbGxvd2luZyBxdWVzdGlvbnM6DQoNCmBgYHtyIHN0bTJ9DQojc3RtIG1vZGVsIHdpdGggMTAgdG9waWNzDQpmb3J1bXNfc3RtMiA8LSBzdG0oZG9jdW1lbnRzPWRvY3MsIA0KICAgICAgICAgZGF0YT1tZXRhLA0KICAgICAgICAgdm9jYWI9dm9jYWIsIA0KICAgICAgICAgcHJldmFsZW5jZSA9fiBjb3Vyc2VfaWQgKyBmb3J1bV9pZCwNCiAgICAgICAgIEs9MTAsDQogICAgICAgICBtYXguZW0uaXRzPTI1LA0KICAgICAgICAgdmVyYm9zZSA9IEZBTFNFKQ0KDQpmb3J1bXNfc3RtMg0KYGBgDQoNCmBgYHtyfQ0Kbl9kaXN0aW5jdCh0c19mb3J1bV9kYXRhJGZvcnVtX25hbWUpDQoNCmZvcnVtc19sZGEyIDwtIExEQShmb3J1bXNfZHRtLCANCiAgICAgICAgICAgICAgICAgIGsgPSA1LCANCiAgICAgICAgICAgICAgICAgIGNvbnRyb2wgPSBsaXN0KHNlZWQgPSA1ODgpDQogICAgICAgICAgICAgICAgICApDQoNCmZvcnVtc19sZGEyDQpgYGANCg0KDQoxLiAgV2hhdCB0b3BpY3MgYXBwZWFyIHRvIGJlIHNpbWlsYXIgdG8gdGhvc2UgdXNpbmcgMjAgdG9waWNzIGZvciBLPw0KMi4gIEtub3dpbmcgdGhhdCB5b3UgZG9uJ3QgaGF2ZSBhcyBtdWNoIGNvbnRleHQgYXMgdGhlIHJlc2VhcmNoZXJzIG9mIHRoaXMgc3R1ZHkgZG8sIGhvdyBtaWdodCB5b3UNCiAgICBpbnRlcnByZXQgb25lIG9mIHRoZXNlIGxhdGVudCB0b3BpY3Mgb3IgdGhlbWVzIHVzaW5nIHRoZSBrZXkgdGVybXMNCiAgICBhc3NpZ25lZD8NCjMuICBXaGF0IHRvcGljIGVtZXJnZWQgdGhhdCBzZWVtIGRyYW1hdGljYWxseSBkaWZmZXJlbnQgYW5kIGhvdyBtaWdodA0KICAgIHlvdSBpbnRlcnByZXQgdGhpcyB0b3BpYz8NCg0KIyMjIDNjLiBGaW5kaW5nICpLKg0KDQpBcyBhbGx1ZGVkIHRvIGVhcmxpZXIsIHNlbGVjdGluZyB0aGUgbnVtYmVyIG9mIHRvcGljcyBmb3IgeW91ciBtb2RlbCBpcw0KYSBub24tdHJpdmlhbCBkZWNpc2lvbiBhbmQgY2FuIGRyYW1hdGljYWxseSBpbXBhY3QgeW91ciByZXN1bHRzLiBCYWlsDQooMjAxOCkgbm90ZXMgdGhhdA0KDQo+ICpUaGUgcmVzdWx0cyBvZiB0b3BpYyBtb2RlbHMgc2hvdWxkIG5vdCBiZSBvdmVyLWludGVycHJldGVkIHVubGVzcyB0aGUNCj4gcmVzZWFyY2hlciBoYXMgc3Ryb25nIHRoZW9yZXRpY2FsIGFwcmlvcmkgYWJvdXQgdGhlIG51bWJlciBvZiB0b3BpY3MNCj4gaW4gYSBnaXZlbiBjb3JwdXMsIG9yIGlmIHRoZSByZXNlYXJjaGVyIGhhcyBjYXJlZnVsbHkgdmFsaWRhdGVkIHRoZQ0KPiByZXN1bHRzIG9mIGEgdG9waWMgbW9kZWwgdXNpbmcgYm90aCB0aGUgcXVhbnRpdGF0aXZlIGFuZCBxdWFsaXRhdGl2ZQ0KPiB0ZWNobmlxdWVzIGRlc2NyaWJlZCBhYm92ZS4qDQoNClRoZXJlIGFyZSBzZXZlcmFsIGFwcHJvYWNoZXMgdG8gZXN0aW1hdGluZyBhIHZhbHVlIGZvciBLIGFuZCB3ZSdsbCB0YWtlDQphIHF1aWNrIGxvb2sgYXQgb25lIGZyb20gdGhlIGBsZGF0dW5pbmdgIHBhY2thZ2UgYW5kIG9uZSBmcm9tIG91ciBgc3RtYA0KcGFja2FnZS4NCg0KIyMjIyBUaGUgRmluZFRvcGljc051bWJlciBGdW5jdGlvbg0KDQpUaGUgYGxkYXR1bmluZ2AgcGFja2FnZSBoYXMgZnVuY3Rpb25zIGZvciBib3RoIGNhbGN1bGF0aW5nIGFuZCBwbG90dGluZw0KZGlmZmVyZW50IG1ldHJpY3MgdGhhdCBjYW4gYmUgdXNlZCB0byBlc3RpbWF0ZSB0aGUgbW9zdCBwcmVmZXJhYmxlDQpudW1iZXIgb2YgdG9waWNzIGZvciBMREEgbW9kZWwuIEl0IGFsc28gY29udmVuaWVudGx5IHRha2VzIHRoZSBzdGFuZGFyZA0KZG9jdW1lbnQgdGVybSBtYXRyaXggb2JqZWN0IHRoYXQgd2UgY3JlYXRlZCBmcm9tIG91dCB0aWR5IHRleHQgYW5kIGhhcw0KdGhlIGFkZGVkIGJlbmVmaXQgb2YgcnVubmluZyBmYWlybHkgcXVpY2tseSwgZXNwZWNpYWxseSBjb21wYXJlZCB0byB0aGUNCmZ1bmN0aW9uIGZvciBmaW5kaW5nIEsgZnJvbSB0aGUgYHN0bWAgcGFja2FnZS4NCg0KTGV0J3MgdXNlIHRoZSBkZWZhdWx0cyBzcGVjaWZpZWQgaW4gdGhlIGA/RmluZFRvcGljTnVtYmVyYCBkb2N1bWVudGF0aW9uDQphbmQgbW9kaWZ5IHNsaWdodGx5IGdldCBtZXRyaWNzIGZvciBhIHNlcXVlbmNlIG9mIHRvcGljcyBmcm9tIDEwLTc1DQpjb3VudGluZyBieSA1IGFuZCBwbG90IHRoZSBvdXRwdXQgd2Ugc2F2ZWQgdXNpbmcgdGhlDQpgRmluZFRvcGljc051bWJlcl9wbG90KClgIGZ1bmN0aW9uOg0KDQpgYGB7ciBmaW5kLXRvcGljLCBldmFsPUZBTFNFfQ0Ka19tZXRyaWNzIDwtIEZpbmRUb3BpY3NOdW1iZXIoDQogIGZvcnVtc19kdG0sDQogIHRvcGljcyA9IHNlcSgxMCwgNzUsIGJ5ID0gNSksDQogIG1ldHJpY3MgPSAiR3JpZmZpdGhzMjAwNCIsDQogIG1ldGhvZCA9ICJHaWJicyIsDQogIGNvbnRyb2wgPSBsaXN0KCksDQogIG1jLmNvcmVzID0gTkEsDQogIHJldHVybl9tb2RlbHMgPSBGQUxTRSwNCiAgdmVyYm9zZSA9IEZBTFNFLA0KICBsaWJwYXRoID0gTlVMTA0KKQ0KDQpGaW5kVG9waWNzTnVtYmVyX3Bsb3Qoa19tZXRyaWNzKQ0KYGBgDQoNCk5vdGUgdGhhdCB0aGUgYEZpbmRUb3BpY051bWJlcnMoKWAgZnVuY3Rpb24gY29udGFpbnMgdGhyZWUgYWRkaXRpb25hbA0KbWV0cmljcyBmb3IgY2FsY3VsYXRpbmcgbWV0cmljcyB0aGF0IGNhbiBiZSB1c2VkIHRvIGVzdGltYXRlIHRoZSBtb3N0DQpwcmVmZXJhYmxlIG51bWJlciBvZiB0b3BpY3MgZm9yIExEQSBtb2RlbC4gV2UgdXNlZCB0aGUgR3JpZmZpdGhzMjAwNA0KbWV0cmljcyBpbmNsdWRlZCBpbiB0aGUgZGVmYXVsdCBleGFtcGxlIGFuZCBJJ3ZlIGFsc28gZm91bmQgdGhpcyB0bw0KcHJvZHVjZSB0aGUgbW9zdCBpbnRlcnByZXRhYmxlIHJlc3VsdHMgYXMgc2hvdyBpbiB0aGUgZmlndXJlIGJlbG93Og0KDQohW10oaW1nL2tfbWV0cmljcy5wbmcpe3dpZHRoPSI5MCUifQ0KDQpBcyBhIGdlbmVyYWwgcnVsZSBvZiB0aHVtYiBhbmQgb3Zlcmx5IHNpbXBsaXN0aWMgaGV1cmlzdGljLCB3ZSdyZQ0KbG9va2luZyBmb3IgYW4gaW5mbGVjdGlvbiBwb2ludCBpbiBvdXIgcGxvdCB3aGljaCBpbmRpY2F0ZXMgYW4gb3B0aW1hbA0KbnVtYmVyIG9mIHRvcGljcyB0byBzZWxlY3QgZm9yIGEgdmFsdWUgb2YgSy4NCg0KIyMjIyBUaGUgZmluZGluZ0soKSBGdW5jdGlvbg0KDQpGaW5hbGx5LCBCYWlsICgyMDE4KSBub3RlcyB0aGF0IHRoZWBzdG1gIHBhY2thZ2UgaGFzIGEgdXNlZnVsIGZ1bmN0aW9uDQpjYWxsZWQgYHNlYXJjaEtgIHdoaWNoIGFsbG93cyB1cyB0byBzcGVjaWZ5IGEgcmFuZ2Ugb2YgdmFsdWVzIGZvciBga2ANCmFuZCBvdXRwdXRzIG11bHRpcGxlIGdvb2RuZXNzLW9mLWZpdCBtZWFzdXJlcyB0aGF0IGFyZSAidmVyeSB1c2VmdWwgaW4NCmlkZW50aWZ5aW5nIGEgcmFuZ2Ugb2YgdmFsdWVzIGZvciBga2AgdGhhdCBwcm92aWRlIHRoZSBiZXN0IGZpdCBmb3IgdGhlDQpkYXRhLiINCg0KVGhlIHN5bnRheCBvZiB0aGlzIGZ1bmN0aW9uIGlzIHZlcnkgc2ltaWxhciB0byB0aGUgYHN0bSgpYCBmdW5jdGlvbiB3ZQ0KdXNlZCBhYm92ZSwgZXhjZXB0IHRoYXQgd2Ugc3BlY2lmeSBhIHJhbmdlIGZvciBga2AgYXMgb25lIG9mIHRoZQ0KYXJndW1lbnRzLiBJbiB0aGUgY29kZSBiZWxvdywgd2Ugc2VhcmNoIGFsbCB2YWx1ZXMgb2bCoGBrYMKgYmV0d2VlbiAxMCBhbmQNCjMwLg0KDQpgYGB7ciBzZWFyY2staywgZXZhbD1GQUxTRX0NCiNJIGFtIG5vdCBleHBlY3RpbmcgeW91IHJ1biB0aGlzIGNvZGUgYXMgaXQgd2lsbCB0YWtlIHRvbyBsb25nDQojZmluZGluZ2sgPC0gc2VhcmNoSyhkb2NzLCANCiAgICAgICAgICAgICAgICAgICAgI3ZvY2FiLCANCiAgICAgICAgICAgICAgICAgICAgI0sgPSBjKDU6MTUpLA0KICAgICAgICAgICAgICAgICAgICAjZGF0YSA9IG1ldGEsIA0KICAgICAgICAgICAgICAgICAgICAjdmVyYm9zZT1GQUxTRSkNCg0KI3Bsb3QoZmluZGluZ2spDQpgYGANCg0KTm90ZSB0aGF0IFJ1bm5pbmcgYHNlYXJjaEsoKWAgZnVuY3Rpb24gb24gdGhpcyBjb3JwdXMgdG9vayBhbGwgbmlnaHQgb24NCmEgcHJldHR5IHBvd2VyZnVsIE1hY0Jvb2sgUHJvIGFuZCBjcmFzaGVkIG9uY2UgYXMgd2VsbCwgc28gSSBkbyBub3QNCmV4cGVjdCB5b3UgdG8gcnVuIHRoaXMgZm9yIHRoZSB3YWxrdGhyb3VnaC4gSSByYW4gYSBjb3VwbGUgaXRlcmF0aW9ucw0KYW5kIGxhbmRlZCBvbiBiZXR3ZWVuIDUgYW5kIDE1IHdpdGggYW4gb3B0aW1hbCBudW1iZXIgb2YgdG9waWNzDQpzb21ld2hlcmUgYXJvdW5kIDE0Og0KDQohW10oaW1nL3NlYXJjaGtfcmVzdWx0cy5wbmcpe3dpZHRoPSI5MCUifQ0KDQpHaXZlbiB0aGUgc29tZXdoYXQgY29uZmxpY3RpbmcgcmVzdWx0cywgYWxzbyBzb21ld2hhdCBzZWxmaXNobHkgYW5kIGZvcg0KdGhlIHNhbWUgb2Ygc2ltcGxpY2l0eSBmb3IgdGhpcyB3YWxrdGhyb3VnaCwgSSdtIGp1c3QgZ29pbmcgdG8gc3RpY2sNCndpdGggdGhlIHJhdGhlciBhcmJpdHJhcnkgc2VsZWN0aW9uIG9mIDIwIHRvcGljcyBmb3IgdGhlIHJlbWFpbmRlciBvZg0KdGhpcyBVbml0Lg0KDQojIyMjIFRoZSBMREF2aXMgRXhwbG9yZXINCg0KT25lIGZpbmFsIHRvb2wgdGhhdCBJIHdhbnQgdG8gaW50cm9kdWNlIGZyb20gdGhlIGBzdG1gIHBhY2thZ2UgaXMgdGhlDQpgdG9MREF2aXMoKWAgZnVuY3Rpb24gd2hpY2ggcHJvdmlkZXMgYSBncmVhdCB2aXN1YWxpemF0aW9ucyBmb3INCmV4cGxvcmluZyB0b3BpYyBhbmQgd29yZCBkaXN0cmlidXRpb25zIHVzaW5nIGBMREF2aXNgIHRvcGljIGJyb3dzZXI6DQoNCmBgYHtyIExEQXZpc30NCnRvTERBdmlzKG1vZCA9IGZvcnVtc19zdG0sIGRvY3MgPSBkb2NzKQ0KYGBgDQoNCkFzIHlvdSBjYW4gc2VlIGZyb20gdGhlIGJyb3dzZXIgc2NyZWVuIHNob3QgYmVsb3csIG91ciBjdXJyZW50IHN0bSBtb2RlbA0Kb2YgMjAgdG9waWNzIGlzIHJlc3VsdGluZyBpbiBhIGxvdCBvZiBvdmVybGFwIGFtb25nIHRvcGljcyBhbmQgc3VnZ2VzdA0KdGhhdCAyMCBtYXkgbm90IGJlIGFuIG9wdGltYWwgbnVtYmVyIG9mIHRvcGljcywgYXMgb3RoZXIgYXBwcm9hY2hlcyBmb3INCmZpbmRpbmcgayBhbHNvIHN1Z2dlc3RzOg0KDQohW10oaW1nL2xkYXZpcy5wbmcpDQoNCiMjIDQuIEVYUExPUkUNCg0KU2lsZ2UgYW5kIFJvYmluc29uICgyMDE4KSBub3RlIHRoYXQgZml0dGluZyBhdCB0b3BpYyBtb2RlbCBpcyB0aGUgImVhc3kNCnBhcnQuIiBUaGUgaGFyZCBwYXJ0IGlzIG1ha2luZyBzZW5zZSBvZiB0aGUgbW9kZWwgcmVzdWx0cyBhbmQgdGhhdCB0aGUNCnJlc3Qgb2YgdGhlIGFuYWx5c2lzIGludm9sdmVzIGV4cGxvcmluZyBhbmQgaW50ZXJwcmV0aW5nIHRoZSBtb2RlbCB1c2luZw0KYSB2YXJpZXR5IG9mIGFwcHJvYWNoZXMgd2hpY2ggd2UnbGwgd2Fsa3Rocm91Z2ggaW4gaW4gdGhpcyBzZWN0aW9uLg0KDQpCYWlsICgyMDE4KSBjYXV0aW9ucywgaG93ZXZlciwgdGhhdDoNCg0KPiAqLi4ucG9zdC1ob2MgaW50ZXJwcmV0YXRpb24gb2YgdG9waWMgbW9kZWxzIGlzIHJhdGhlciBkYW5nZXJvdXMuLi4gYW5kDQo+IGNhbiBxdWlja2x5IGNvbWUgdG8gcmVzZW1ibGUgdGhlIHByb2Nlc3Mgb2YgInJlYWRpbmcgdGVhIGxlYXZlcywiIG9yDQo+IGZpbmRpbmcgbWVhbmluZyBpbiBwYXR0ZXJucyB0aGF0IGFyZSBpbiBmYWN0IHF1aXRlIGFyYml0cmFyeSBvciBldmVuDQo+IHJhbmRvbS4qDQoNCiMjIyA0YS4gRXhwbG9yaW5nIEJldGEgVmFsdWVzDQoNCkhpZGRlbiB3aXRoaW4gdGhpcyBgZm9ydW1zX2xkYWAgdG9waWMgbW9kZWwgb2JqZWN0IHdlIGNyZWF0ZWQgYXJlDQpwZXItdG9waWMtcGVyLXdvcmQgcHJvYmFiaWxpdGllcywgY2FsbGVkIM6yICgiYmV0YSIpLiBJdCBpcyB0aGUNCnByb2JhYmlsaXR5IG9mIGEgdGVybSAod29yZCkgYmVsb25naW5nIHRvIGEgdG9waWMuwqANCg0KTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIDUgbW9zdCBsaWtlbHkgdGVybXMgYXNzaWduZWQgdG8gZWFjaCB0b3BpYywNCmkuZS4gdGhvc2Ugd2l0aCB0aGUgbGFyZ2VzdCDOsiB2YWx1ZXMgdXNpbmcgdGhlIGB0ZXJtcygpYCBmdW5jdGlvbiBmcm9tDQp0aGUgYHRvcGljbW9kZWxzYCBwYWNrYWdlOg0KDQpgYGB7ciB0ZXJtc30NCnRlcm1zKGZvcnVtc19sZGEsIDUpDQpgYGANCg0KRXZlbiB0aG91Z2ggd2UndmUgc29tZXdoYXQgYXJiaXRyYXJpbHkgc2VsZWN0ZWQgdGhlIG51bWJlciBvZiB0b3BpY3MgZm9yDQpvdXIgY29ycHVzLCBzb21lIHRoZXNlIHRvcGljcyBvciB0aGVtZXMgYXJlIGZhaXJseSBpbnR1aXRpdmUgdG8NCmludGVycHJldC4gRm9yIGV4YW1wbGU6DQoNCi0gICBUb3BpYyAxMSAodGVjaG5vbG9neSwgc3R1ZGVudHMsIHNvZnR3YXJlLCBwcm9ncmFtLCBleGNlbCkgc2VlbXMgdG8NCiAgICBiZSBhYm91dCBzdHVkZW50cyB1c2Ugb2YgdGVjaG5vbG9neSBpbmNsdWRpbmcgc29mdHdhcmUgcHJvZ3JhbXMgbGlrZQ0KICAgIGV4Y2VsOw0KDQotICAgVG9waWMgOSAocXVlc3Rpb25zLCBraWRzLCBsb3ZlLCBnYXBtaW5kZXIsIHNoYXJpbmcpIHNlZW1zIHRvIGJlDQogICAgYWJvdXQgdGhlIGdhcG1pbmRlciBhY3Rpdml0eSBmcm9tIHRoZSBNT09DLUVkIGFuZCBraWRzIGVuam95bWVudCBvZg0KICAgIGl0OyBhbmQNCg0KLSAgIFRvcGljIDE4IChkYXRhLCBzdHVkZW50cywgY29sbGVjdCwgcmVhbCwgc2V0cykgc2VlbXMgdG8gYmUgYWJvdXQNCiAgICBzdHVkZW50IGNvbGxlY3Rpb24gYW5kIHVzZSBvZiByZWFsIHdvcmxkIGRhdGEgc2V0cy4NCg0KTm90IHN1cnByaXNpbmdseSwgdGhlIGB0aWR5dGV4dGAgcGFja2FnZSBoYXMgYSBoYW5keSBmdW5jdGlvbg0KY29udmVuaWVudGx5IG5hbWUgYHRpZHkoKWAgdG8gY29udmVydCBvdXIgbGRhIG1vZGVsIHRvIGEgdGlkeSBkYXRhIGZyYW1lDQpjb250YWluaW5nIHRoZXNlIGJldGEgdmFsdWVzIGZvciBlYWNoIHRlcm06DQoNCmBgYHtyIHRpZHlfbGRhfQ0KDQojIGNvbnZlcnQgdG8gYSB0aWR5IGRhdGEgZnJhbWUgd2l0aCBCZXRhIHZhbHVlcw0KdGlkeV9sZGEgPC0gdGlkeShmb3J1bXNfbGRhKQ0KDQp0aWR5X2xkYQ0KYGBgDQoNCk9idmlvdXNseSwgaXQncyBub3QgdmVyeSBlYXN5IHRvIGludGVycHJldCB3aGF0IHRoZSB0b3BpY3MgYXJlIGFib3V0DQpmcm9tIGEgZGF0YSBmcmFtZSBsaWtlIHRoaXMgc28gbGV0J3MgYm9ycm93IGNvZGUgYWdhaW4gZnJvbSBbQ2hhcHRlcg0KOC40LjMgSW50ZXJwcmV0aW5nIHRoZSB0b3BpYw0KbW9kZWxdKGh0dHBzOi8vd3d3LnRpZHl0ZXh0bWluaW5nLmNvbS9uYXNhLmh0bWw/cT1iZXRhI2ludGVycHJldGluZy10aGUtdG9waWMtbW9kZWwpDQppbiBUZXh0IE1pbmluZyB3aXRoIFIgdG8gZXhhbWluZSB0aGUgdG9wIDUgdGVybXMgZm9yIGVhY2ggdG9waWMgYW5kIHRoZW4NCmxvb2sgYXQgdGhpcyBpbmZvcm1hdGlvbiB2aXN1YWxseToNCg0KYGBge3IgdG9wX3Rlcm1zfQ0KDQojIHZpc3VhbGx5IGludGVycHJldCB0b3AgdGVybXMgZm9yIGVhY2ggdG9waWMNCnRvcF90ZXJtcyA8LSB0aWR5X2xkYSAlPiUNCiAgZ3JvdXBfYnkodG9waWMpICU+JQ0KICBzbGljZV9tYXgoYmV0YSwgbiA9IDUsIHdpdGhfdGllcyA9IEZBTFNFKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBhcnJhbmdlKHRvcGljLCAtYmV0YSkNCg0KdG9wX3Rlcm1zICU+JQ0KICBtdXRhdGUodGVybSA9IHJlb3JkZXJfd2l0aGluKHRlcm0sIGJldGEsIHRvcGljKSkgJT4lDQogIGdyb3VwX2J5KHRvcGljLCB0ZXJtKSAlPiUgICAgDQogIGFycmFuZ2UoZGVzYyhiZXRhKSkgJT4lICANCiAgdW5ncm91cCgpICU+JQ0KICBnZ3Bsb3QoYWVzKGJldGEsIHRlcm0sIGZpbGwgPSBhcy5mYWN0b3IodG9waWMpKSkgKw0KICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogIHNjYWxlX3lfcmVvcmRlcmVkKCkgKw0KICBsYWJzKHRpdGxlID0gIlRvcCA1IHRlcm1zIGluIGVhY2ggTERBIHRvcGljIiwNCiAgICAgICB4ID0gZXhwcmVzc2lvbihiZXRhKSwgeSA9IE5VTEwpICsNCiAgZmFjZXRfd3JhcCh+IHRvcGljLCBuY29sID0gNCwgc2NhbGVzID0gImZyZWUiKQ0KYGBgDQoNCiMjIyA0Yi4gRXhwbG9yaW5nIEdhbW1hIFZhbHVlcw0KDQpOb3cgdGhhdCB3ZSBoYXZlIGEgc2Vuc2Ugb2YgdGhlIG1vc3QgY29tbW9uIHdvcmRzIGFzc29jaWF0ZWQgd2l0aCBlYWNoDQp0b3BpYywgbGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIHRvcGljIHByZXZhbGVuY2UgaW4gb3VyIE1PT0MtRWQNCmRpc2N1c3Npb24gZm9ydW0gY29ycHVzLCBpbmNsdWRpbmcgdGhlIHdvcmRzIHRoYXQgY29udHJpYnV0ZSB0byBlYWNoDQp0b3BpYyB3ZSBleGFtaW5lZCBhYm92ZS4NCg0KQWxzbywgaGlkZGVuIHdpdGhpbiBvdXIgYGZvcnVtc19sZGFgIHRvcGljIG1vZGVsIG9iamVjdCB3ZSBjcmVhdGVkIGFyZQ0KcGVyLWRvY3VtZW50LXBlci10b3BpYyBwcm9iYWJpbGl0aWVzLCBjYWxsZWQgzrMgKCJnYW1tYSIpLiBUaGlzIHByb3ZpZGVzDQp0aGUgcHJvYmFiaWxpdGllcyB0aGF0IGVhY2ggZG9jdW1lbnQgaXMgZ2VuZXJhdGVkIGZyb20gZWFjaCB0b3BpYywgdGhhdA0KZ2FtbWEgbWF0cml4LiBXZSBjYW4gY29tYmluZSBvdXIgYmV0YSBhbmQgZ2FtbWEgdmFsdWVzIHRvIHVuZGVyc3RhbmQgdGhlDQp0b3BpYyBwcmV2YWxlbmNlIGluIG91ciBjb3JwdXMsIGFuZCB3aGljaCB3b3JkcyBjb250cmlidXRlIHRvIGVhY2gNCnRvcGljLg0KDQpUbyBkbyB0aGlzLCB3ZSdyZSBnb2luZyB0byBib3Jyb3cgc29tZSBjb2RlIGZyb20gdGhlIFNpbGdlICgyMDE4KSBwb3N0LA0KW1RyYWluaW5nLCBldmFsdWF0aW5nLCBhbmQgaW50ZXJwcmV0aW5nIHRvcGljDQptb2RlbHNdKGh0dHBzOi8vanVsaWFzaWxnZS5jb20vYmxvZy9ldmFsdWF0aW5nLXN0bS8pLg0KDQpGaXJzdCwgbGV0J3MgY3JlYXRlIHR3byB0aWR5IGRhdGEgZnJhbWVzIGZvciBvdXIgYmV0YSBhbmQgZ2FtbWEgdmFsdWVzDQoNCmBgYHtyIGJldGFfZ2FtbWF9DQp0ZF9iZXRhIDwtIHRpZHkoZm9ydW1zX2xkYSkNCg0KdGRfZ2FtbWEgPC0gdGlkeShmb3J1bXNfbGRhLCBtYXRyaXggPSAiZ2FtbWEiKQ0KDQp0ZF9iZXRhDQp0ZF9nYW1tYQ0KDQpgYGANCg0KTmV4dCwgd2UnbGwgYWRvcHQgSnVsaWEncyBjb2RlIHdob2xlc2FsZSB0byBjcmVhdGUgYSBmaWx0ZXJlZCBkYXRhIGZyYW1lDQpvZiBvdXIgYHRvcF90ZXJtc2AsIGpvaW4gdGhpcyB0byBhIG5ldyBkYXRhIGZyYW1lIGZvciBgZ2FtbWEtdGVybXNgIGFuZA0KY3JlYXRlIGEgbmljZSBjbGVhbiB0YWJsZSB1c2luZyB0aGUgYGthYmVsKClgIGZ1bmN0aW9uIGBrbml0cmAgcGFja2FnZToNCg0KYGBge3IgcHJldmFsZW5jZV90YWJsZX0NCnRvcF90ZXJtcyA8LSB0ZF9iZXRhICU+JQ0KICBhcnJhbmdlKGJldGEpICU+JQ0KICBncm91cF9ieSh0b3BpYykgJT4lDQogIHRvcF9uKDcsIGJldGEpICU+JQ0KICBhcnJhbmdlKC1iZXRhKSAlPiUNCiAgc2VsZWN0KHRvcGljLCB0ZXJtKSAlPiUNCiAgc3VtbWFyaXNlKHRlcm1zID0gbGlzdCh0ZXJtKSkgJT4lDQogIG11dGF0ZSh0ZXJtcyA9IG1hcCh0ZXJtcywgcGFzdGUsIGNvbGxhcHNlID0gIiwgIikpICU+JSANCiAgdW5uZXN0KCkNCg0KZ2FtbWFfdGVybXMgPC0gdGRfZ2FtbWEgJT4lDQogIGdyb3VwX2J5KHRvcGljKSAlPiUNCiAgc3VtbWFyaXNlKGdhbW1hID0gbWVhbihnYW1tYSkpICU+JQ0KICBhcnJhbmdlKGRlc2MoZ2FtbWEpKSAlPiUNCiAgbGVmdF9qb2luKHRvcF90ZXJtcywgYnkgPSAidG9waWMiKSAlPiUNCiAgbXV0YXRlKHRvcGljID0gcGFzdGUwKCJUb3BpYyAiLCB0b3BpYyksDQogICAgICAgICB0b3BpYyA9IHJlb3JkZXIodG9waWMsIGdhbW1hKSkNCg0KZ2FtbWFfdGVybXMgJT4lDQogIHNlbGVjdCh0b3BpYywgZ2FtbWEsIHRlcm1zKSAlPiUNCiAga2FibGUoZGlnaXRzID0gMywgDQogICAgICAgIGNvbC5uYW1lcyA9IGMoIlRvcGljIiwgIkV4cGVjdGVkIHRvcGljIHByb3BvcnRpb24iLCAiVG9wIDcgdGVybXMiKSkNCmBgYA0KDQpBbmQgbGV0J3MgYWxzbyBjb21wYXJlIHRoaXMgdG8gdGhlIG1vc3QgcHJldmFsZW50IHRvcGljcyBhbmQgdGVybXMgZnJvbQ0Kb3VyIGBmb3J1bXNfc3RtYCBtb2RlbCB0aGF0IHdlIGNyZWF0ZWQgdXNpbmcgdGhlIGBwbG90KClgIGZ1bmN0aW9uOg0KDQpgYGB7ciBwbG90X3N0bX0NCnBsb3QoZm9ydW1zX3N0bSwgbiA9IDcpDQpgYGANCg0KIyMjIDRjLiBSZWFkaW5nIHRoZSBUZWEgTGVhdmVzDQoNClJlY29nbml6aW5nIHRoYXQgdG9waWMgbW9kZWxpbmcgaXMgYmVzdCB1c2VkIGFzIGEgInRvb2wgZm9yIHJlYWRpbmciIGFuZA0KcHJvdmlkZXMgb25seSBhbiBpbmNvbXBsZXRlIGFuc3dlciB0byBvdXIgb3ZlcmFyY2hpbmcsICoqIkhvdyBkbyB3ZQ0KcXVhbnRpZnkgd2hhdCBhIGNvcnB1cyBpcyBhYm91dD8iKiosIHRoZSByZXN1bHRzIGRvIHN1Z2dlc3Qgc29tZQ0KcG90ZW50aWFsIHRvcGljcyB0aGF0IGhhdmUgZW1lcmdlcyBhcyB3ZWxsIGFzIHNvbWUgYXJlYXMgd29ydGggZm9sbG93aW5nDQp1cCBvbi4NCg0KU3BlY2lmaWNhbGx5LCBsb29raW5nIGF0IHNvbWUgb2YgdGhlIGNvbW1vbiBjbHVzdGVycyBvZiB3b3JkcyBmb3IgdGhlDQptb3JlIHByZXZhbGVudCB0b3BpY3Mgc3VnZ2VzdCB0aGF0IHNvbWUga2V5IHRvcGljcyBvciAibGF0ZW50IHRoZW1lcyINCihyZW5hbWVkIGluIGJvbGQpIG1pZ2h0IGluY2x1ZGU6DQoNCi0gICAqKlRlYWNoaW5nIFN0YXRpc3RpY3M6KiogVW5zdXJwcmlzaW5nLCBnaXZlbiB0aGUgY291cnNlIHRpdGxlLCB0aGUNCiAgICB0b3BpY3MgbW9zdCBwcmV2YWxlbnQgaW4gYm90aCB0aGUgYGZvcnVtc19zdG1gIGFuZCBgZm9ydW1zX2xkYWANCiAgICBtb2RlbHMgY29udGFpbnMgdGhlIHRlcm1zICJ0ZWFjaCIsICJzdHVkZW50cyIsICJzdGF0aXN0aWNzIi4gVGhpcw0KICAgIGNvdWxkIGJlIGFuICJvdmVyYXJjaGluZyB0aGVtZSIgYnV0IG1vcmUgbGlrZWx5IG1heSBzaW1wbHkgYmUganVzdA0KICAgIHRoZSByZXNpZHVlIG9mIHRoZSBjb3Vyc2UgdGl0bGUgdGhvdWdoIGJlaW5nIHNwcmlua2xlZCB0aHJvdWdob3V0DQogICAgdGhlIGZvcnVtcyBhbmQgZGVzZXJ2ZXMgc29tZSBmb2xsb3cgdXAuIFRvcGljcyA4IGZyb20gdGhlIExEQSBtb2RlbA0KICAgIG1heSBvdmVybGFwIHdpdGggdGhpcyB0b3BpYyBhcyB3ZWxsLg0KLSAgICoqQ291cnNlIFV0aWxpdHk6KiogVGhlIHNlY29uZCBtb3N0IHByZXZhbGVudCBUb3BpY3MgKDEzIGFuZCAyKSBpbg0KICAgIHRoZSBgbGRhYCBhbmQgYHN0bWAgbW9kZWxzIHJlc3BlY3RpdmVseSwgc2VlbSB0byBwb3RlbnRpYWxseSBiZQ0KICAgIGFib3V0IHRoZSB1c2VmdWxuZXNzIG9mIGNvdXJzZSAicmVzb3VyY2VzIiBsaWtlIGxlc3NvbnMsIHRvb2xzLA0KICAgIHZpZGVvcywgYW5kIGFjdGl2aXRpZXMuIEknbSB3YWdlcmluZyB0aGlzIG1pZ2h0IGJlIGEgZm9ydW0gZGVkaWNhdGVkDQogICAgdG8gY291cnNlIGZlZWRiYWNrLiBUb3BpYyAxNSBmcm9tIHRoZSBTVE0gbW9kZWwgYWxzbyBzdWdnZXN0IHRoaXMNCiAgICBtYXkgYmUgYSBicm9hZGVyIHRoZW1lLg0KLSAgICoqVXNpbmcgUmVhbC1Xb3JsZCBEYXRhOioqIFRvcGljcyAxOCAmIDEyIGZyb20gdGhlIExEQSBtb2RlbA0KICAgIHBhcnRpY3VsYXJseSBpbnRyaWd1ZSBtZSBhbmQgSSdtIHdhZ2VyaW5nIHRoaXMgaXMgcHJldHR5IHBvc2l0aXZlDQogICAgc2VudGltZW50IGFtb25nIHBhcnRpY2lwYW50cyBhYm91dCB0aGUgdmFsdWUgYW5kIGJlbmVmaXQgb2YgaGF2aW5nDQogICAgc3R1ZGVudHMgY29sbGVjdCBhbmQgYW5hbHl6ZSByZWFsIGRhdGEgc2V0cyAoZS5nLiBDZW5zdXMgZGF0YSBpbg0KICAgIFRvcGljIDEpIGFuZCB3b3JrIG9uIHByb2plY3RzIHJlbGV2YW50IHRvIHRoZWlyIHJlYWwgbGlmZS4gV2lsbA0KICAgIGRlZmluaXRlbHkgZm9sbG93IHVwIG9uIHRoaXMgb25lLg0KLSAgICoqVGVjaG5vbG9neSBVc2U6KiogU2V2ZXJhbCB0b3BpY3MgKDYgJiAxMSBmcm9tIExEQSBhbmQgOCAmIDE5IGZyb20NCiAgICBTVE0pIGFwcGVhciB0byBiZSBhYm91dCBzdHVkZW50IHVzZSBvZiB0ZWNobm9sb2d5IGFuZCBzb2Z0d2FyZSBsaWtlDQogICAgY2FsY3VsYXRvcnMgYW5kIEV4Y2VsIGZvciB0ZWFjaGluZyBzdGF0aXN0aWNzIGFuZCB1c2luZyBzaW11bGF0aW9ucy4NCiAgICBUb3BpYyAxNiBmcm9tIExEQSBhbHNvIHN1Z2dlc3QgdGhlIHVzZSBvZiB0aGUgQ29tbW9uIE9ubGluZSBEYXRhDQogICAgQW5hbHlzaXMgUGxhdGZvcm0NCiAgICAoW0NPREFQXShDb21tb24lMjBPbmxpbmUlMjBEYXRhJTIwQW5hbHlzaXMlMjBQbGF0Zm9ybSUyMChDT0RBUCklMjBPcGVuLXNvdXJjZSUyMHNvZnR3YXJlJTIwZm9yJTIwZHluYW1pYyUyMGRhdGElMjBleHBsb3JhdGlvbikpLg0KLSAgICoqU3R1ZGVudCBTdHJ1Z2dsZSAmIEVuZ2FnZW1lbnQ6KiogVG9waWMgMTUgZnJvbSBMREEgYW5kIFRvcGljIDE2DQogICAgZnJvbSBTVE0gYWxzbyBpbnRyaWd1ZSBtZSBhbmQgYXBwZWFyIHRvIGJlIHR3byBvcHBvc2l0ZSBzaWRlcyBvZg0KICAgIHBlcmhhcHMgdGhlIHNhbWUgY29pbi4gVGhlIGZvcm1lciBpbmNsdWRlcyAic3RydWdnbGUiIGFuZCAicmVhZGluZyINCiAgICB3aGljaCBzdWdnZXN0cyBwZXJoYXBzIGEgYmFycmllciB0byB0ZWFjaGluZyBzdGF0aXN0aWNzIHdoaWxlIFRvcGljDQogICAgMTYgY29udGFpbnMgdG9wIHN0ZW1zIGxpa2UgImVuZ2FnZSIsICJhY3RpdiIsIGFuZCAidGhpbmsiIGFuZCBtYXkNCiAgICBzdWdnZXN0IHBhcnRpY2lwYW50cyBhbnRpY2lwYXRlIGFjdGl2aXRpZXMgbWF5IGVuZ2FnZSBzdHVkZW50cy4NCg0KVG8gc2VydmUgYXMgYSBjaGVjayBvbiBteSB0ZWEgbGVhZiByZWFkaW5nLCBJJ20gZ29pbmcgdG8gZm9sbG93IEJhaWwncw0KcmVjb21tZW5kYXRpb24gdG8gZXhhbWluZSBzb21lIG9mIHRoZXNlIHRvcGljcyBxdWFsaXRhdGl2ZWx5LiBUaGUgYHN0bWANCnBhY2thZ2UgaGFzIGFub3RoZXIgdXNlZnVsIGZ1bmN0aW9uIHRob3VnaCBleGNlcHRpb25hbGx5IGZ1c3N5IGZ1bmN0aW9uDQpjYWxsZWQgYGZpbmRUaG91Z2h0c2Agd2hpY2ggZXh0cmFjdHMgcGFzc2FnZXMgZnJvbSBkb2N1bWVudHMgd2l0aGluIHRoZQ0KY29ycHVzIGFzc29jaWF0ZSB3aXRoIHRvcGljcyB0aGF0IHlvdSBzcGVjaWZ5Lg0KDQpUaGUgZmlyc3QgbGluZSBvZiBjb2RlIG1heSBub3QgYmUgbmVjZXNzYXJ5IGZvciB5b3VyIGluZGVwZW5kZW50DQphbmFseXNpcywgYnV0IGJlY2F1c2UgdGhlIGB0ZXh0UHJvY2Vzc29yKClgIGZ1bmN0aW9uIHJlbW92ZWQgc2V2ZXJhbA0KZG9jdW1lbnRzIGR1cmluZyBwcm9jZXNzaW5nLCB0aGUgYGZpbmR0aG91Z2h0cygpYCBmdW5jdGlvbiBjYW4ndA0KcHJvcGVybHkgaW5kZXggdGhlIHByb2Nlc3NlZCBkb2NzLiBUaGlzIFtsaW5lIG9mIGNvZGUgZm91bmQgb24NCnN0YWNrb3ZlcmZsb3ddKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzQzNDkyNjY3L3Itc3RtLW51bWJlci1vZi1wcm92aWRlZC10ZXh0cy1hbmQtbnVtYmVyLW9mLWRvY3VtZW50cy1tb2RlbGVkLWRvLW5vdC1tYXRjaCkNCnJlbW92ZXMgZG9jdW1lbnRzIGZyb20gb3JpZ2luYWwgYHRzX2ZvcnVtX2RhdGFgIHNvdXJjZSB0aGF0IHdlcmUgcmVtb3ZlZA0KZHVyaW5nIHByb2Nlc3Npbmcgc28gdGhlcmUgaXMgYSBvbmUtdG8tb25lIGNvcnJlc3BvbmRlbmNlIHdpdGgNCmBmb3J1bXNfc3RtYCBhbmQgc28geW91IGNhbiB1c2UgdGhlIGZ1bmN0aW9uIHRvIGZpbmQgcG9zdHMgYXNzb2NpYXRlZA0Kd2l0aCBhIGdpdmVuIHRvcGljLg0KDQpMZXQncyBzbGlnaHRseSByZWR1Y2Ugb3VyIG9yaWdpbmFsIGRhdGEgc2V0IHRvIG1hdGNoIG91ciBTVE0gbW9kZWwsIHBhc3MNCmJvdGggdG8gdGhlIGBmaW5kVGhvdWdodHMoKWAgZnVuY3Rpb24sIGFuZCBzZXQgb3VyIGFyZ3VtZW50cyB0byByZXR1cm4NCmBuID0xMGAgcG9zdHMgZnJvbSBgdG9waWNzID0gMmAgKGkuZS4gVG9waWMgMikgdGhhdCBoYXZlIGF0IGxlYXN0IDUwJSBvcg0KYHRocmVzaCA9IDAuNWAgYXMgYSBtaW5pbXVtIHRocmVzaG9sZCBmb3IgdGhlIGVzdGltYXRlZCB0b3BpYw0KcHJvcG9ydGlvbi4NCg0KYGBge3IgZmluZFRob3VnaHRzXzJ9DQoNCnRzX2ZvcnVtX2RhdGFfcmVkdWNlZCA8LXRzX2ZvcnVtX2RhdGEkcG9zdF9jb250ZW50Wy10ZW1wJGRvY3MucmVtb3ZlZF0NCg0KZmluZFRob3VnaHRzKGZvcnVtc19zdG0sDQogICAgICAgICAgICAgdGV4dHMgPSB0c19mb3J1bV9kYXRhX3JlZHVjZWQsDQogICAgICAgICAgICAgdG9waWNzID0gMiwgDQogICAgICAgICAgICAgbiA9IDEwLA0KICAgICAgICAgICAgIHRocmVzaCA9IDAuNSkNCmBgYA0KDQpEdXBsaWNhdGUgcG9zdHMgYXNpZGUsIHRoaXMgKipDb3Vyc2UgVXRpbGl0eSoqIHRvcGljIHJldHVybnMgcG9zdHMgdGhlcmUNCndlcmUgZXhwZWN0ZWQgYmFzZWQgb24gbXkgaW50ZXJwcmV0YXRpb24gb2YgdGhlIGtleSB0ZXJtcyBmb3IgVG9waWMgMi4NCkl0IGxvb2tzIGxpa2UgSSBtYXkgaGF2ZSByZWFkIHRob3NlIHRlYSBsZWF2ZXMgY29ycmVjdGx5Lg0KDQpOb3cgbGV0J3MgdGFrZSBhIGxvb2sgYXQgVG9waWMgMTYgdGhhdCB3ZSB0aG91Z2h0IG1pZ2h0IGJlIHJlbGF0ZWQgdG8NCnN0dWRlbnQgZW5nYWdlbWVudDoNCg0KYGBge3IgZmluZFRob3VnaHRzXzE2fQ0KDQpmaW5kVGhvdWdodHMoZm9ydW1zX3N0bSwNCiAgICAgICAgICAgICB0ZXh0cyA9IHRzX2ZvcnVtX2RhdGFfcmVkdWNlZCwNCiAgICAgICAgICAgICB0b3BpY3MgPSAxNiwgDQogICAgICAgICAgICAgbiA9IDEwLA0KICAgICAgICAgICAgIHRocmVzaCA9IDAuNSkNCmBgYA0KDQpJdCBsb29rcyBsaWtlIG15IHRlYSByZWFkaW5nIHdhcyBhIHBhcnRpYWxseSBjb3JyZWN0IGZvciBUb3BpYyAxNiwNCnRob3VnaCB0aGUgcmVzdWx0cyBzZWVtIHRvIGJlIGFib3V0IGEgc3BlY2lmaWMgIlBlcHNpIGNoYWxsZW5nZSINCmFjdGl2aXR5IHRvIGNvbmR1Y3Qgd2l0aCBzdHVkZW50cy4NCg0KRmluYWxseSwgbGV0J3MgbG9vayBhdCBwb3N0cyBmcm9tIFRvcGljIDMgd2hpY2ggd2UgdGhvdWdoIG1pZ2h0IGJlIGFuDQpvdmVyYXJjaGluZyB0aGVtZSBhYm91dCB0ZWFjaGluZyBzdGF0aXN0aWNzOg0KDQpgYGB7ciBmaW5kVGhvdWdodHNfM30NCg0KdHNfZm9ydW1fZGF0YV9yZWR1Y2VkIDwtdHNfZm9ydW1fZGF0YSRwb3N0X2NvbnRlbnRbLXRlbXAkZG9jcy5yZW1vdmVkXQ0KDQpmaW5kVGhvdWdodHMoZm9ydW1zX3N0bSwNCiAgICAgICAgICAgICB0ZXh0cyA9IHRzX2ZvcnVtX2RhdGFfcmVkdWNlZCwNCiAgICAgICAgICAgICB0b3BpY3MgPSAzLCANCiAgICAgICAgICAgICBuID0gMTAsDQogICAgICAgICAgICAgdGhyZXNoID0gMC41KQ0KYGBgDQoNCkxvb2tpbmcgYXQganVzdCB0aGUgMTAgcG9zdHMgcmV0dXJuZWQsIHBlcmhhcHMgYSBiZXR0ZXIgbmFtZSBmb3IgdGhpcw0KdG9waWMgd291bGQgYmUgKipDb3Vyc2UgUmVmbGVjdGlvbnMgb24gVGVhY2hpbmcgU3RhdGlzdGljcyoqLg0KDQojIyMjIFVuaXQgVGFrZWF3YXkNCg0KSW4gYWRkaXRpb24gdG8gc29tZSB1c2VmdWwgUiBwYWNrYWdlcyBhbmQgZnVuY3Rpb25zIGZvciB0aGUgYWN0dWFsDQpwcm9jZXNzIG9mIHRvcGljIG1vZGVsaW5nLCBob3BlZnVsbHkgdGhlcmUgYXJlIHR3byBtYWluIGxlc3NvbnMgSSdtDQpob3BpbmcgeW91IHRha2UgYXdheSBmcm9tIHRoaXMgd2Fsa3Rocm91Z2g6DQoNCjEuICAqKlRvcGljIG1vZGVsaW5nIHJlcXVpcmVzIGEgbG90IG9mIGRlY2lzaW9ucy4qKiBCZXlvbmQgZGVjaWRpbmcgb24gYQ0KICAgIHZhbHVlIGZvciBLLCB0aGVyZSBhcmUgYSBudW1iZXIgb2Yga2V5IGRlY2lzaW9ucyB0aGF0IHlvdSBoYXZlIHRvDQogICAgbWFrZSB0aGF0IGNhbiBkcmFtYXRpY2FsbHkgYWZmZWN0IHlvdXIgcmVzdWx0cy4gRm9yIGV4YW1wbGUsIHRvIHN0ZW0NCiAgICBvciBub3QgdG8gc3RlbT8gV2hhdCBxdWFsaWZpZXMgYXMgYSBkb2N1bWVudD8gV2hhdCBmbGF2b3Igb3IgdG9waWMNCiAgICBtb2RlbGluZyBpcyBiZXN0IHN1aXRlZCB0byB5b3VyIGRhdGEgYW5kIHJlc2VhcmNoIHF1ZXN0aW9ucz8gSG93DQogICAgbWFueSBpdGVyYXRpb25zIHRvIHJ1bj8NCjIuICAqKlRvcGljIG1vZGVsaW5nIGlzIGFzIG11Y2ggYXJ0IGFzIChkYXRhKSBzY2llbmNlLioqIEFzIEJhaWwgKDIwMTgpDQogICAgbm90ZWQsIHRoZSB0ZXJtICJ0b3BpYyIgaXMgc29tZXdoYXQgYW1iaXRpb3VzLCBhbmQgdG9waWMgbW9kZWxzIGRvDQogICAgbm90IHByb2R1Y2UgaGlnaGx5IG51YW5jZWQgY2xhc3NpZmljYXRpb24gb2YgdGV4dHMuIE9uY2UgeW91J3ZlIGZpdA0KICAgIHlvdXIgbW9kZWwsIGludGVycHJldGluZyB5b3VyIG1vZGVsIHJlcXVpcmVzIHNvbWUgbWVudGFsIGd5bW5hc3RpY3MNCiAgICBhbmQgaWRlYWxseSBzb21lIGtub3dsZWRnZSBvZiB0aGUgY29udGV4dCBmcm9tIHdoaWNoIHRoZSBkYXRhIGNhbWUNCiAgICB0byBoZWxwIHdpdGggaW50ZXJwcmV0YXRpb24gb2YgeW91ciB0b3BpY3MuIE1vcmVvdmVyLCB0aGUNCiAgICBxdWFudGl0YXRpdmUgYXBwcm9hY2hlcyBmb3IgbWFraW5nIHRoZSBkZWNpc2lvbnMgaGlnaGxpZ2h0ZWQgYWJvdmUNCiAgICBhcmUgaW1wZXJmZWN0IGFuZCBhIGdvb2QgZGVhbCBvZiBodW1hbiBqdWRnbWVudCByZXF1aXJlZC4NCg0KIyMjIyMg4pyFIENvbXByZWhlbnNpb24gQ2hlY2sNCg0KVXNpbmcgdGhlIFNUTSBtb2RlbCB5b3UgZml0IGZyb20gdGhlIFNlY3Rpb24gMyBbQ29tcHJlaGVuc2lvbiBDaGVja10NCndpdGggYSBkaWZmZXJlbnQgdmFsdWUgZm9yIEssIHVzZSB0aGUgYXBwcm9hY2hlcyBkZW1vbnN0cmF0ZWQgaW4gU2VjdGlvbg0KNCB0byBleHBsb3JlIGFuZCBpbnRlcnByZXQgeW91ciB0b3BpY3MgYW5kIHRlcm1zIGFuZCByZXZpc2l0IHRoZQ0KZm9sbG93aW5nIHF1ZXN0aW9uOg0KDQpgYGB7cn0NCg0KIyBSYW4gTERBIG1vZGVsIHdpdGggNSB0b3BpY3MNCiMgZmluZCB0b3AgNSB0ZXJtcyBpbiB0aGUgbGRhIG1vZGVsIA0KdGVybXMoZm9ydW1zX2xkYTIsIDUpDQojIGNvbnZlcnQgdG8gYSB0aWR5IGRhdGEgZnJhbWUgd2l0aCBCZXRhIHZhbHVlcw0KdGlkeV9sZGEyIDwtIHRpZHkoZm9ydW1zX2xkYTIpDQoNCg0KIyB2aXN1YWxseSBpbnRlcnByZXQgdG9wIHRlcm1zIGZvciBlYWNoIHRvcGljDQp0b3BfdGVybXMyIDwtIHRpZHlfbGRhMiAlPiUNCiAgZ3JvdXBfYnkodG9waWMpICU+JQ0KICBzbGljZV9tYXgoYmV0YSwgbiA9IDUsIHdpdGhfdGllcyA9IEZBTFNFKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBhcnJhbmdlKHRvcGljLCAtYmV0YSkNCmBgYA0KDQpgYGB7cn0NCnRkX2JldGEyIDwtIHRpZHkoZm9ydW1zX2xkYTIpDQp0ZF9iZXRhMiAlPiUNCiAgICBncm91cF9ieSh0b3BpYykgJT4lDQogICAgdG9wX24oMTAsIGJldGEpICU+JQ0KICAgIHVuZ3JvdXAoKSAlPiUNCiAgICBtdXRhdGUodG9waWMgPSBwYXN0ZTAoIlRvcGljICIsIHRvcGljKSwNCiAgICAgICAgICAgdGVybSA9IHJlb3JkZXJfd2l0aGluKHRlcm0sIGJldGEsIHRvcGljKSkgJT4lDQogICAgZ2dwbG90KGFlcyh0ZXJtLCBiZXRhLCBmaWxsID0gYXMuZmFjdG9yKHRvcGljKSkpICsNCiAgICBnZW9tX2NvbChhbHBoYSA9IDAuOCwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICAgIGZhY2V0X3dyYXAofiB0b3BpYywgc2NhbGVzID0gImZyZWVfeSIpICsNCiAgICBjb29yZF9mbGlwKCkgKw0KICAgIHNjYWxlX3hfcmVvcmRlcmVkKCkgKw0KICAgIGxhYnMoeCA9IE5VTEwsIHkgPSBleHByZXNzaW9uKGJldGEpLA0KICAgICAgICAgdGl0bGUgPSAiSGlnaGVzdCB3b3JkIHByb2JhYmlsaXRpZXMgZm9yIGVhY2ggdG9waWMiLA0KICAgICAgICAgc3VidGl0bGUgPSAiRGlmZmVyZW50IHdvcmRzIGFyZSBhc3NvY2lhdGVkIHdpdGggZGlmZmVyZW50IHRvcGljcyIpDQoNCnRkX2dhbW1hMiA8LSB0aWR5KGZvcnVtc19sZGEyLCBtYXRyaXggPSAiZ2FtbWEiKQ0KDQpiZXRhX3Rlcm1zMiA8LSB0ZF9iZXRhMiAlPiUNCiAgYXJyYW5nZShiZXRhKSAlPiUNCiAgZ3JvdXBfYnkodG9waWMpICU+JQ0KICB0b3Bfbig3LCBiZXRhKSAlPiUNCiAgYXJyYW5nZSgtYmV0YSkgJT4lDQogIHNlbGVjdCh0b3BpYywgdGVybSkgJT4lDQogIHN1bW1hcmlzZSh0ZXJtcyA9IGxpc3QodGVybSkpICU+JQ0KICBtdXRhdGUodGVybXMgPSBtYXAodGVybXMsIHBhc3RlLCBjb2xsYXBzZSA9ICIsICIpKSAlPiUgDQogIHVubmVzdCgpDQoNCmdhbW1hX3Rlcm1zMiA8LSB0ZF9nYW1tYTIgJT4lDQogIGdyb3VwX2J5KHRvcGljKSAlPiUNCiAgc3VtbWFyaXNlKGdhbW1hID0gbWVhbihnYW1tYSkpICU+JQ0KICBhcnJhbmdlKGRlc2MoZ2FtbWEpKSAlPiUNCiAgbGVmdF9qb2luKHRvcF90ZXJtcywgYnkgPSAidG9waWMiKSAlPiUNCiAgbXV0YXRlKHRvcGljID0gcGFzdGUwKCJUb3BpYyAiLCB0b3BpYyksDQogICAgICAgICB0b3BpYyA9IHJlb3JkZXIodG9waWMsIGdhbW1hKSkNCg0KZ2FtbWFfdGVybXMyICU+JQ0KICBzZWxlY3QodG9waWMsIGdhbW1hLCB0ZXJtcykgJT4lDQogIGthYmxlKGRpZ2l0cyA9IDMsIA0KICAgICAgICBjb2wubmFtZXMgPSBjKCJUb3BpYyIsICJFeHBlY3RlZCB0b3BpYyBwcm9wb3J0aW9uIiwgIlRvcCA3IHRlcm1zIikpDQoNCg0KYGBgDQoNCg0KMS4gIE5vdyB0aGF0IHlvdSBoYXZlIGEgbGl0dGxlIG1vcmUgY29udGV4dCwgaG93IG1pZ2h0IHlvdSByZXZpc2UgeW91cg0KICAgIGluaXRpYWwgaW50ZXJwcmV0YXRpb24gb2Ygc29tZSBvZiB0aGUgbGF0ZW50IHRvcGljcyBvciBsYXRlbnQgdGhlbWVzDQogICAgZnJvbSB5b3VyIG1vZGVsPw0KICAgIA0KICAtIEkgcmV2aXNlZCBteSB0b3BpY3MgdG8gMTAgYW5kIDUgaW4gdHdvIGRpZmZlcmVudCBtb2RlbHMuIEFmdGVyIHJldmlzaW5nIGZvciAxMCBJIGluc3BlY3RlZCB3aXRoIExEQXZpcy4gVGhlcmUgd2FzIHN0aWxsIG92ZXJsYXAgc28gSSByZWR1Y2VkIGl0IHRvIDUgdG9waWNzLiBBbHRob3VnaCB0aGVyZSB3YXMgb3ZlcmxhcCBpbiB0aGUgU1RNIG9mIDEwIHRvcGljcyBnb2luZyBkb3duIHRvIG9ubHkgNSB0b3BpY3MgaW4gdGhlIHNlY29uZCBMREEgbW9kZWwgd2FzIHRvIGhhcmQgdG8gaW50ZXJwcmV0LiBUZW4gdG9waWNzLCBhcmUgZWFzaWVyIHRvIG1ha2Ugc2Vuc2Ugb2YgYW5kIGNhbiBxdWFudGlmeSB0aGUgdG9waWMgYW1vdW50IGNob3Nlbi4NCiAgTXkgdGVhIGxlYWYgaW50ZXJwcmV0YXRpb24gZm9yIHRoZSBMREEgNSBUb3BpY3M6DQogIC0gICBUb3BpYyA0IC0gVGVhY2hpbmcgU3R1ZGVudHMgc3RhdGlzdGljcyB1c2luZyB0aGUgY29tbW9uIGNvcmUNCiAgLSAgIFRvcGljIDUgLSBBIHBvcHVsYXIgZGF0YSBzZXQgaXMgYWJvdXQgU3RlZWwgUm9sbGVyIENvYXN0ZXJzIA0KICAtICAgVG9waWMgMyAtIFN0dWRlbnRzIGNhbiB1bmRlcnN0YW5kIFN0YW5kYXJkIERldmlhdGlvbiBieSB0ZXN0aW5nIGluIGEgQm94IFBsb3QuDQogIC0gICBUb3BpYyAyIC0gRWR1Y2F0b3JzIHNoYXJpbmcgcmVzb3VyY2VzIGFuZCBpZGVhcyBpbiB0aGUgbGVhcm5pbmcgTU9PQ3MNCiAgLSAgIFRvcGljIDEgLSBJdGVtcyBmb3IgbGVhcm5pbmcgYW5kIHRlYWNoaW5nIHN0YXRpc3RpY3MNCg==