Report summary
This is the first milestone report for the capstone project in the course of the finalization of JHU Data Science specialization by Coursera. As explained by instructions, the purpose of this report is “just to demonstrate that we’ve gotten used to working with the textual data and that we are on track to create our prediction algorithm.”. Hence, here you will find an initial exploratory data analysis of the data set provided by Coursera and SwiftKey, as well as ideas how the modelling task could be tackled.
We’ll be dealing with the English database which is actually a subset of a corpus called HC Corpora. The integral version of the data set which was used in this analysis can be found HERE. It contains data in four languages : English, German, Russian and Finish. There are three corpora per language which contain data generated by twitter, blogs and news feeds.
The code which was used to generate this report can be found in my GitHub repo.
Setting up the working environment and loading the data
# Loading needed packages
library(tm)
library(stringr)
library(qdap)
library(RColorBrewer)
library(SnowballC)
library(wordcloud)
library(RWeka)
library(dplyr)
library(ggplot2)
library(gridExtra)
library(graphics)
Loading the data, i.e. corpora that will be used in the analysis
con1 <- file("data/en_US.blogs.txt", "r")
blogs <- readLines(con1, encoding = "UTF-8", skipNul = TRUE)
close(con1)
con2 <- file("data/en_US.news.txt", "r")
news <- readLines(con2, encoding = "UTF-8", skipNul = TRUE)
close(con2)
con3 <- file("data/en_US.twitter.txt", "r")
twitter <- readLines(con3, encoding = "UTF-8", skipNul = TRUE)
close(con3)
Summary statistics
The table below provides the basic summary for our corpora. As you can see data sets are a bit bulky so we will sample and aggregate 10000 lines out of each corpus for further analysis. This is actually an approach recommended by good people from JHU.
Blogs |
200.42 |
899288 |
37334131 |
News |
196.28 |
77259 |
2643969 |
Twitter |
159.36 |
2360148 |
30373583 |
Data preprocessing - cleaning the corpus
Before the corpus analysis certain preprocessing steps are usually performed. These include the following:
- Text normalization:
- text lowercasing
- removal of numbers
- removal of punctuation signs
- removal of URLs
- white space striping,
- profanity filtering
- Replacement of special characters such as emoticons, special utf-8 characters and control characters
All these operation are conducted by the help of the tm package
, which is probably the best known and the most used package for text mining in R.
Initial corpus exploration - frequent terms
First step that has to be performed in order for us to be able to explore frequency distribution of certain words, i.e. terms in our corpus is to build a term-document matrix. This matrix contains terms as rows and documents where these terms occur as columns.
# Let's build a term-document matrix out of our clean corpus
sample_tdm <- TermDocumentMatrix(clean_sample)
sample_tdm
<<TermDocumentMatrix (terms: 40350, documents: 30000)>>
Non-/sparse entries: 431951/1210068049
Sparsity : 100%
Maximal term length: 64
Weighting : term frequency (tf)
# An easy way to start analyzing the information contained in TDM is to change it into a simple matrix
sample_m <- as.matrix(sample_tdm)
#Let's check how our matrix looks like
dim(sample_m)
[1] 40350 30000
sample_m[10000:10007, 2000:2010]
Docs
Terms 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010
dictatori 0 0 0 0 0 0 0 0 0 0 0
dictatorship 0 0 0 0 0 0 0 0 0 0 0
dictatorshiplit 0 0 0 0 0 0 0 0 0 0 0
dictionari 0 0 0 0 0 0 0 0 0 0 0
dictionaryush 0 0 0 0 0 0 0 0 0 0 0
didact 0 0 0 0 0 0 0 0 0 0 0
diddi 0 0 0 0 0 0 0 0 0 0 0
diddley 0 0 0 0 0 0 0 0 0 0 0
Let’s check what are the 20 most common terms in our cleaned sample corpora:
# Calculate the rowSums: term_frequency
term_frequency <- rowSums(sample_m)
# Sort term_frequency in descending order
term_frequency <- sort(term_frequency, decreasing = TRUE)
View the top 20 most common words:
term_frequency[1:20]
said will one like get just time year can make new day love work good
2935 2819 2658 2354 2310 2271 2189 2018 1976 1743 1633 1632 1464 1460 1382
say peopl now know want
1366 1350 1348 1337 1335
Plot a bar chart of the 20 most common words:
barplot(term_frequency[20:1], col = "steelblue", las = 2)

Word cloud
A word cloud is a very popular way to visualize frequency of terms (actually they are quite often overused). In a word cloud, size is usually scaled to frequency and in some cases the colors may indicate another measurement.
Let’s check what are the 50 most frequently occurring single words, i.e. unigrams in our clean corpus:
# Create word_freqs
word_freqs <- data.frame(term = names(term_frequency),
num = term_frequency)
# Print the wordcloud with the specified colors
wordcloud(word_freqs$term,
word_freqs$num,
max.words = 50,
colors = c("grey60", "darkorange", "steelblue")
)

N-Gram tokenization
Now we will shift our focus to tokens containing two and three words. This can help extract useful phrases which can lead to some additional insights or provide improved predictive attributes for construction of a machine learning algorithm.

Insights
- Stemming needs to be adjusted so we don’t have trigrams like “happi mother day” or “presid barack obama”
- Lowcasing induces a loss of information re the presence of personal names, city names, state names and alike
- Changing N-gram order, i.e. from bigram to trigram, yields to drastic decrease of observed counts
- Corpora is huge, I need more research into how to efficiently deal with it, especially in terms of building a model trained on the complete data set.
Next steps
The future work on the capstone project will be directed towards the development of proper strategy that will be used for modelling, i.e. choosing and constructing an adequate set of features, choosing and implementing the optimal prediction algorithm which will enable building fast and user friendly app.
LS0tDQp0aXRsZTogJ0RhdGEgU2NpZW5jZSBTcGVjaWFsaXphdGlvbiBNaWxlc3RvbmUgUmVwb3J0OiBJbml0aWFsIEVEQScgDQphdXRob3I6ICJJZ29yIEh1dCINCmRhdGU6ICJGZWJydWFyeSAxOSwgMjAxNyINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICBoaWdobGlnaHQ6IHRleHRtYXRlDQogICAgdGhlbWU6IGNlcnVsZWFuDQogICAgdG9jOiB5ZXMNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KLS0tDQoNCiMjIFJlcG9ydCBzdW1tYXJ5DQoNClRoaXMgaXMgdGhlIGZpcnN0IG1pbGVzdG9uZSByZXBvcnQgZm9yIHRoZSBjYXBzdG9uZSBwcm9qZWN0IGluIHRoZSBjb3Vyc2Ugb2YgdGhlIGZpbmFsaXphdGlvbiBvZiBKSFUgRGF0YSBTY2llbmNlIHNwZWNpYWxpemF0aW9uIGJ5IENvdXJzZXJhLiBBcyBleHBsYWluZWQgYnkgaW5zdHJ1Y3Rpb25zLCB0aGUgcHVycG9zZSBvZiB0aGlzIHJlcG9ydCBpcyAianVzdCB0byBkZW1vbnN0cmF0ZSB0aGF0IHdlJ3ZlIGdvdHRlbiB1c2VkIHRvIHdvcmtpbmcgd2l0aCB0aGUgdGV4dHVhbCBkYXRhIGFuZCB0aGF0IHdlIGFyZSBvbiB0cmFjayB0byBjcmVhdGUgb3VyIHByZWRpY3Rpb24gYWxnb3JpdGhtLiIuIEhlbmNlLCBoZXJlIHlvdSB3aWxsIGZpbmQgYW4gaW5pdGlhbCBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzIG9mIHRoZSBkYXRhIHNldCBwcm92aWRlZCBieSBDb3Vyc2VyYSBhbmQgU3dpZnRLZXksIGFzIHdlbGwgYXMgaWRlYXMgaG93IHRoZSBtb2RlbGxpbmcgdGFzayBjb3VsZCBiZSB0YWNrbGVkLg0KDQpXZSdsbCBiZSBkZWFsaW5nIHdpdGggdGhlIEVuZ2xpc2ggZGF0YWJhc2Ugd2hpY2ggaXMgYWN0dWFsbHkgYSBzdWJzZXQgb2YgYSBjb3JwdXMgY2FsbGVkIFtIQyBDb3Jwb3JhXShodHRwOi8vZGF0YS5kYW5ldHNvZnQuY29tL2NvcnBvcmEuaGVsaW9ob3N0Lm9yZykuIFRoZSBpbnRlZ3JhbCB2ZXJzaW9uIG9mIHRoZSBkYXRhIHNldCB3aGljaCB3YXMgdXNlZCBpbiB0aGlzIGFuYWx5c2lzIGNhbiBiZSBmb3VuZCBbSEVSRV0oaHR0cHM6Ly9kMzk2cXVzemE0MG9yYy5jbG91ZGZyb250Lm5ldC9kc3NjYXBzdG9uZS9kYXRhc2V0L0NvdXJzZXJhLVN3aWZ0S2V5LnppcCkuIEl0IGNvbnRhaW5zIGRhdGEgaW4gZm91ciBsYW5ndWFnZXMgOiBFbmdsaXNoLCBHZXJtYW4sIFJ1c3NpYW4gYW5kIEZpbmlzaC4gVGhlcmUgYXJlIHRocmVlIGNvcnBvcmEgcGVyIGxhbmd1YWdlIHdoaWNoIGNvbnRhaW4gZGF0YSBnZW5lcmF0ZWQgYnkgdHdpdHRlciwgYmxvZ3MgYW5kIG5ld3MgZmVlZHMuDQoNClRoZSBjb2RlIHdoaWNoIHdhcyB1c2VkIHRvIGdlbmVyYXRlIHRoaXMgcmVwb3J0IGNhbiBiZSBmb3VuZCBpbiBteSBbR2l0SHViIHJlcG9dKGh0dHBzOi8vZ2l0aHViLmNvbS9JZ29ySHV0L0pIVV9EYXRhX1NjaWVuY2VfQ2Fwc3RvbmUpLg0KDQojIyBTZXR0aW5nIHVwIHRoZSB3b3JraW5nIGVudmlyb25tZW50IGFuZCBsb2FkaW5nIHRoZSBkYXRhDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBMb2FkaW5nIG5lZWRlZCBwYWNrYWdlcyANCmxpYnJhcnkodG0pICAgICAgICANCmxpYnJhcnkoc3RyaW5ncikgICAgIA0KbGlicmFyeShxZGFwKQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQpsaWJyYXJ5KFNub3diYWxsQykNCmxpYnJhcnkod29yZGNsb3VkKQ0KbGlicmFyeShSV2VrYSkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGdyaWRFeHRyYSkNCmxpYnJhcnkoZ3JhcGhpY3MpDQpgYGANCg0KTG9hZGluZyB0aGUgZGF0YSwgaS5lLiBjb3Jwb3JhIHRoYXQgd2lsbCBiZSB1c2VkIGluIHRoZSBhbmFseXNpcw0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCiAgY29uMSA8LSBmaWxlKCJkYXRhL2VuX1VTLmJsb2dzLnR4dCIsICJyIikgDQogIGJsb2dzIDwtIHJlYWRMaW5lcyhjb24xLCBlbmNvZGluZyA9ICJVVEYtOCIsIHNraXBOdWwgPSBUUlVFKQ0KICBjbG9zZShjb24xKQ0KDQogIGNvbjIgPC0gZmlsZSgiZGF0YS9lbl9VUy5uZXdzLnR4dCIsICJyIikgDQogIG5ld3MgPC0gcmVhZExpbmVzKGNvbjIsIGVuY29kaW5nID0gIlVURi04Iiwgc2tpcE51bCA9IFRSVUUpDQogIGNsb3NlKGNvbjIpDQoNCiAgY29uMyA8LSBmaWxlKCJkYXRhL2VuX1VTLnR3aXR0ZXIudHh0IiwgInIiKSANCiAgdHdpdHRlciA8LSByZWFkTGluZXMoY29uMywgZW5jb2RpbmcgPSAiVVRGLTgiLCBza2lwTnVsID0gVFJVRSkNCiAgY2xvc2UoY29uMykNCg0KYGBgDQoNCiMjIFN1bW1hcnkgc3RhdGlzdGljcw0KDQpgYGB7ciwgaW5jbHVkZT1GQUxTRX0NCiMjIENoZWNraW5nIHRoZSBzaXplIGFuZCBsZW5ndGggb2YgdGhlIGZpbGVzIGFuZCBjYWxjdWxhdGUgdGhlIHdvcmQgY291bnQNCmJsb2dzRmlsZSA8LSBmaWxlLmluZm8oImRhdGEvZW5fVVMuYmxvZ3MudHh0Iikkc2l6ZSAvIDEwMjReMiANCm5ld3NGaWxlIDwtIGZpbGUuaW5mbygiZGF0YS9lbl9VUy5uZXdzLnR4dCIpJHNpemUgLyAxMDI0XjIgDQp0d2l0dGVyRmlsZSA8LSBmaWxlLmluZm8oImRhdGEvZW5fVVMudHdpdHRlci50eHQiKSRzaXplIC8gMTAyNF4yIA0KDQoNCmJsb2dzTGVuZ3RoIDwtIGxlbmd0aChibG9ncykNCm5ld3NMZW5ndGggPC0gbGVuZ3RoKG5ld3MpDQp0d2l0dGVyTGVuZ3RoIDwtIGxlbmd0aCh0d2l0dGVyKQ0KDQoNCmJsb2dzV29yZHMgPC0gc3VtKHNhcHBseShncmVnZXhwcigiXFxTKyIsIGJsb2dzKSwgbGVuZ3RoKSkNCm5ld3NXb3JkcyA8LSBzdW0oc2FwcGx5KGdyZWdleHByKCJcXFMrIiwgbmV3cyksIGxlbmd0aCkpDQp0d2l0dGVyV29yZHMgPC0gc3VtKHNhcHBseShncmVnZXhwcigiXFxTKyIsIHR3aXR0ZXIpLCBsZW5ndGgpKQ0KDQpmaWxlX3N1bW1hcnkgPC0gZGF0YS5mcmFtZSgNCiAgICAgICAgZmlsZU5hbWUgPSBjKCJCbG9ncyIsIk5ld3MiLCJUd2l0dGVyIiksDQogICAgICAgIGZpbGVTaXplID0gYyhyb3VuZChibG9nc0ZpbGUsIGRpZ2l0cyA9IDIpLCANCiAgICAgICAgICAgICAgICAgICAgIHJvdW5kKG5ld3NGaWxlLGRpZ2l0cyA9IDIpLCANCiAgICAgICAgICAgICAgICAgICAgIHJvdW5kKHR3aXR0ZXJGaWxlLCBkaWdpdHMgPSAyKSksDQogICAgICAgICAgICAgICAgICAgICBsaW5lQ291bnQgPSBjKGJsb2dzTGVuZ3RoLCBuZXdzTGVuZ3RoLCB0d2l0dGVyTGVuZ3RoKSwNCiAgICAgICAgICAgICAgICAgICAgIHdvcmRDb3VudCA9IGMoYmxvZ3NXb3JkcywgbmV3c1dvcmRzLCB0d2l0dGVyV29yZHMpICAgICAgICAgICAgICAgICAgDQopDQoNCmNvbG5hbWVzKGZpbGVfc3VtbWFyeSkgPC0gYygiRmlsZSBOYW1lIiwgIkZpbGUgU2l6ZSBbTUJdIiwgIk51bWJlciBvZiBMaW5lcyIsICJOdW1iZXIgb2YgV29yZHMiKQ0KDQpzYXZlUkRTKGZpbGVfc3VtbWFyeSwgZmlsZSA9ICJzdW1tYXJ5LlJkYSIpDQoNCnN1bW1hcnlfREYgPC0gcmVhZFJEUygic3VtbWFyeS5SZGEiKQ0KDQpgYGANClRoZSB0YWJsZSBiZWxvdyBwcm92aWRlcyB0aGUgYmFzaWMgc3VtbWFyeSBmb3Igb3VyIGNvcnBvcmEuIEFzIHlvdSBjYW4gc2VlIGRhdGEgc2V0cyBhcmUgYSBiaXQgYnVsa3kgc28gd2Ugd2lsbCBzYW1wbGUgYW5kIGFnZ3JlZ2F0ZSAxMDAwMCBsaW5lcyBvdXQgb2YgZWFjaCBjb3JwdXMgZm9yIGZ1cnRoZXIgYW5hbHlzaXMuIFRoaXMgaXMgYWN0dWFsbHkgYW4gYXBwcm9hY2ggcmVjb21tZW5kZWQgYnkgZ29vZCBwZW9wbGUgZnJvbSBKSFUuDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0Ka25pdHI6OmthYmxlKGhlYWQoc3VtbWFyeV9ERiwgMTApKQ0KYGBgDQoNCiMjIEZvcm1pbmcgdGhlIHNhbXBsZSBjb3JwdXMNCg0KQXMgYWxyZWFkeSB0b2xkIHdlJ2xsIGJlIHVzaW5nIGFuIGFnZ3JlZ2F0ZWQgc2FtcGxlIG9mIHRoZSBpbml0aWFsIGRhdGEgc2V0IGZvciBmdXJ0aGVyIGFuYWx5c2lzLg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpzYW1wbGVfYmxvZ3MgPC0gYmxvZ3Nbc2FtcGxlKDE6bGVuZ3RoKGJsb2dzKSwxMDAwMCldDQpzYW1wbGVfbmV3cyA8LSBuZXdzW3NhbXBsZSgxOmxlbmd0aChuZXdzKSwxMDAwMCldDQpzYW1wbGVfdHdpdHRlciA8LSB0d2l0dGVyW3NhbXBsZSgxOmxlbmd0aCh0d2l0dGVyKSwxMDAwMCldDQp0ZXh0X3NhbXBsZSA8LSBjKHNhbXBsZV9ibG9ncyxzYW1wbGVfbmV3cyxzYW1wbGVfdHdpdHRlcikNCmBgYA0KDQpgYGB7ciwgaW5jbHVkZT1GQUxTRX0NCiMjIFNhdmUgc2FtcGxlDQp3cml0ZUxpbmVzKHRleHRfc2FtcGxlLCAidGV4dF9zYW1wbGUudHh0IikNCmNvbiA8LSBmaWxlKCJ0ZXh0X3NhbXBsZS50eHQiKQ0Kc2FtcGxlIDwtIHJlYWRMaW5lcyhjb24pDQpjbG9zZShjb24pDQpgYGANCg0KTGV0J3MgY2hlY2sgdGhlIHN1bW1hcnkgc3RhdGlzdGljcyBmb3Igb3VyIHNhbXBsZSBjb3JwdXM6DQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0Kc2FtcGxlRmlsZSA8LSBmaWxlLmluZm8oInRleHRfc2FtcGxlLnR4dCIpJHNpemUgLyAxMDI0XjINCnNhbXBsZUxlbmd0aCA8LSBsZW5ndGgoc2FtcGxlKQ0Kc2FtcGxlV29yZHMgPC0gc3VtKHNhcHBseShncmVnZXhwcigiXFxTKyIsIHNhbXBsZSksIGxlbmd0aCkpDQoNCmZpbGVfc3VtbWFyeV9zYW1wbGUgPC0gZGF0YS5mcmFtZSgNCiAgICAgICAgZmlsZU5hbWUgPSAiU2FtcGxlIiwNCiAgICAgICAgZmlsZVNpemUgPSByb3VuZChzYW1wbGVGaWxlLCBkaWdpdHMgPSAyKSwgDQogICAgICAgICAgICAgICAgICAgICBsaW5lQ291bnQgPSBzYW1wbGVMZW5ndGgsDQogICAgICAgICAgICAgICAgICAgICB3b3JkQ291bnQgPSBzYW1wbGVXb3JkcyAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgKQ0KDQpjb2xuYW1lcyhmaWxlX3N1bW1hcnlfc2FtcGxlKSA8LSBjKCJGaWxlIE5hbWUiLCAiRmlsZSBTaXplIFtNQl0iLCAiTnVtYmVyIG9mIExpbmVzIiwgIk51bWJlciBvZiBXb3JkcyIpDQoNCnNhdmVSRFMoZmlsZV9zdW1tYXJ5X3NhbXBsZSwgZmlsZSA9ICJzdW1tYXJ5X3NhbXBsZS5SZGEiKQ0KDQpzdW1tYXJ5X0RGX3NhbXBsZSA8LSByZWFkUkRTKCJzdW1tYXJ5X3NhbXBsZS5SZGEiKQ0KDQprbml0cjo6a2FibGUoaGVhZChzdW1tYXJ5X0RGX3NhbXBsZSkpDQpgYGANCg0KIyMgRGF0YSBwcmVwcm9jZXNzaW5nIC0gY2xlYW5pbmcgdGhlIGNvcnB1cw0KDQpCZWZvcmUgdGhlIGNvcnB1cyBhbmFseXNpcyBjZXJ0YWluIHByZXByb2Nlc3Npbmcgc3RlcHMgYXJlIHVzdWFsbHkgcGVyZm9ybWVkLiBUaGVzZSBpbmNsdWRlIHRoZSBmb2xsb3dpbmc6DQoNCi0gVGV4dCBub3JtYWxpemF0aW9uOg0KICAgIC0gdGV4dCBsb3dlcmNhc2luZw0KICAgIC0gcmVtb3ZhbCBvZiBudW1iZXJzDQogICAgLSByZW1vdmFsIG9mIHB1bmN0dWF0aW9uIHNpZ25zDQogICAgLSByZW1vdmFsIG9mIFVSTHMNCiAgICAtIHdoaXRlIHNwYWNlIHN0cmlwaW5nLA0KICAgIC0gcHJvZmFuaXR5IGZpbHRlcmluZw0KICAgDQotIFJlcGxhY2VtZW50IG9mIHNwZWNpYWwgY2hhcmFjdGVycyBzdWNoIGFzIGVtb3RpY29ucywgc3BlY2lhbCB1dGYtOCBjaGFyYWN0ZXJzIGFuZCBjb250cm9sIGNoYXJhY3RlcnMNCg0KQWxsIHRoZXNlIG9wZXJhdGlvbiBhcmUgY29uZHVjdGVkIGJ5IHRoZSBoZWxwIG9mIHRoZSBbYHRtIHBhY2thZ2VgXShodHRwOi8vdG0uci1mb3JnZS5yLXByb2plY3Qub3JnL2luZGV4Lmh0bWwpLCB3aGljaCBpcyBwcm9iYWJseSB0aGUgYmVzdCBrbm93biBhbmQgdGhlIG1vc3QgdXNlZCBwYWNrYWdlIGZvciB0ZXh0IG1pbmluZyBpbiBSLiANCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQpiYWRfd29yZHMgPC0gcmVhZC50YWJsZSgiYmFkX3dvcmRzLnR4dCIsIGhlYWRlciA9IEZBTFNFLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpJFYxDQoNCiMjIEJ1aWxkIHRoZSBjb3JwdXMsIGFuZCBzcGVjaWZ5IHRoZSBzb3VyY2UgdG8gYmUgY2hhcmFjdGVyIHZlY3RvcnMgDQpjbGVhbl9zYW1wbGUgPC0gQ29ycHVzKFZlY3RvclNvdXJjZShzYW1wbGUpKQ0KDQojIyBDb252ZXJ0IHRvIGxvd2VyIGNhc2UNCmNsZWFuX3NhbXBsZSA8LSB0bV9tYXAoY2xlYW5fc2FtcGxlLCBjb250ZW50X3RyYW5zZm9ybWVyKHRvbG93ZXIpLCBsYXp5ID0gVFJVRSkNCg0KIyMgUmVtb3ZlIHB1bmN0aW9uLCBudW1iZXJzLCBVUkxzLCBzdG9wLCBwcm9mYW5pdHkgYW5kIHBlcmZvcm0gc3RlbW1pbmcNCmNsZWFuX3NhbXBsZSA8LSB0bV9tYXAoY2xlYW5fc2FtcGxlLCBjb250ZW50X3RyYW5zZm9ybWVyKHJlbW92ZVB1bmN0dWF0aW9uKSkNCmNsZWFuX3NhbXBsZSA8LSB0bV9tYXAoY2xlYW5fc2FtcGxlLCBjb250ZW50X3RyYW5zZm9ybWVyKHJlbW92ZU51bWJlcnMpKQ0KcmVtb3ZlVVJMIDwtIGZ1bmN0aW9uKHgpIGdzdWIoImh0dHBbWzphbG51bTpdXSoiLCAiIiwgeCkgDQpjbGVhbl9zYW1wbGUgPC0gdG1fbWFwKGNsZWFuX3NhbXBsZSwgY29udGVudF90cmFuc2Zvcm1lcihyZW1vdmVVUkwpKQ0KY2xlYW5fc2FtcGxlIDwtIHRtX21hcChjbGVhbl9zYW1wbGUsIHN0cmlwV2hpdGVzcGFjZSkNCmNsZWFuX3NhbXBsZSA8LSB0bV9tYXAoY2xlYW5fc2FtcGxlLCByZW1vdmVXb3Jkcywgc3RvcHdvcmRzKCJlbmdsaXNoIikpDQpjbGVhbl9zYW1wbGUgPC0gdG1fbWFwKGNsZWFuX3NhbXBsZSwgcmVtb3ZlV29yZHMsIGJhZF93b3JkcykNCmNsZWFuX3NhbXBsZSA8LSB0bV9tYXAoY2xlYW5fc2FtcGxlLCBzdGVtRG9jdW1lbnQpDQpjbGVhbl9zYW1wbGUgPC0gdG1fbWFwKGNsZWFuX3NhbXBsZSwgc3RyaXBXaGl0ZXNwYWNlKQ0KDQojIyBTYXZpbmcgdGhlIGZpbmFsIGNvcnB1cw0Kc2F2ZVJEUyhjbGVhbl9zYW1wbGUsIGZpbGUgPSAiY2xlYW5fY29ycHVzLlJEUyIpDQoNCmBgYA0KDQojIyBJbml0aWFsIGNvcnB1cyBleHBsb3JhdGlvbiAtIGZyZXF1ZW50IHRlcm1zDQoNCkZpcnN0IHN0ZXAgdGhhdCBoYXMgdG8gYmUgcGVyZm9ybWVkIGluIG9yZGVyIGZvciB1cyB0byBiZSBhYmxlIHRvIGV4cGxvcmUgZnJlcXVlbmN5IGRpc3RyaWJ1dGlvbiBvZiBjZXJ0YWluIHdvcmRzLCBpLmUuIHRlcm1zIGluIG91ciBjb3JwdXMgaXMgdG8gYnVpbGQgYSB0ZXJtLWRvY3VtZW50IG1hdHJpeC4gVGhpcyBtYXRyaXggY29udGFpbnMgdGVybXMgYXMgcm93cyBhbmQgZG9jdW1lbnRzIHdoZXJlIHRoZXNlIHRlcm1zIG9jY3VyIGFzIGNvbHVtbnMuDQoNCmBgYHtyfQ0KIyBMZXQncyBidWlsZCBhIHRlcm0tZG9jdW1lbnQgbWF0cml4IG91dCBvZiBvdXIgY2xlYW4gY29ycHVzDQpzYW1wbGVfdGRtIDwtIFRlcm1Eb2N1bWVudE1hdHJpeChjbGVhbl9zYW1wbGUpDQpzYW1wbGVfdGRtDQoNCiMgQW4gZWFzeSB3YXkgdG8gc3RhcnQgYW5hbHl6aW5nIHRoZSBpbmZvcm1hdGlvbiBjb250YWluZWQgaW4gVERNIGlzIHRvIGNoYW5nZSBpdCBpbnRvIGEgc2ltcGxlIG1hdHJpeCANCnNhbXBsZV9tIDwtIGFzLm1hdHJpeChzYW1wbGVfdGRtKQ0KDQojTGV0J3MgY2hlY2sgaG93IG91ciBtYXRyaXggbG9va3MgbGlrZQ0KZGltKHNhbXBsZV9tKQ0Kc2FtcGxlX21bMTAwMDA6MTAwMDcsIDIwMDA6MjAxMF0NCmBgYA0KDQoNCkxldCdzIGNoZWNrIHdoYXQgYXJlIHRoZSAyMCBtb3N0IGNvbW1vbiB0ZXJtcyBpbiBvdXIgY2xlYW5lZCBzYW1wbGUgY29ycG9yYToNCg0KYGBge3J9DQojIENhbGN1bGF0ZSB0aGUgcm93U3VtczogdGVybV9mcmVxdWVuY3kNCnRlcm1fZnJlcXVlbmN5IDwtIHJvd1N1bXMoc2FtcGxlX20pDQoNCiMgU29ydCB0ZXJtX2ZyZXF1ZW5jeSBpbiBkZXNjZW5kaW5nIG9yZGVyDQp0ZXJtX2ZyZXF1ZW5jeSA8LSBzb3J0KHRlcm1fZnJlcXVlbmN5LCBkZWNyZWFzaW5nID0gVFJVRSkNCmBgYA0KVmlldyB0aGUgdG9wIDIwIG1vc3QgY29tbW9uIHdvcmRzOg0KYGBge3J9DQp0ZXJtX2ZyZXF1ZW5jeVsxOjIwXQ0KYGBgDQpQbG90IGEgYmFyIGNoYXJ0IG9mIHRoZSAyMCBtb3N0IGNvbW1vbiB3b3JkczoNCmBgYHtyfQ0KYmFycGxvdCh0ZXJtX2ZyZXF1ZW5jeVsyMDoxXSwgY29sID0gInN0ZWVsYmx1ZSIsIGxhcyA9IDIpDQpgYGANCg0KIyMjIFdvcmQgY2xvdWQNCkEgd29yZCBjbG91ZCBpcyBhIHZlcnkgcG9wdWxhciB3YXkgdG8gdmlzdWFsaXplIGZyZXF1ZW5jeSBvZiB0ZXJtcyAoYWN0dWFsbHkgdGhleSBhcmUgcXVpdGUgb2Z0ZW4gb3ZlcnVzZWQpLiBJbiBhIHdvcmQgY2xvdWQsIHNpemUgaXMgdXN1YWxseSBzY2FsZWQgdG8gZnJlcXVlbmN5IGFuZCBpbiBzb21lIGNhc2VzIHRoZSBjb2xvcnMgbWF5IGluZGljYXRlIGFub3RoZXIgbWVhc3VyZW1lbnQuDQoNCkxldCdzIGNoZWNrIHdoYXQgYXJlIHRoZSA1MCBtb3N0IGZyZXF1ZW50bHkgb2NjdXJyaW5nIHNpbmdsZSB3b3JkcywgaS5lLiB1bmlncmFtcyBpbiBvdXIgY2xlYW4gY29ycHVzOg0KYGBge3J9DQojIENyZWF0ZSB3b3JkX2ZyZXFzDQp3b3JkX2ZyZXFzIDwtIGRhdGEuZnJhbWUodGVybSA9IG5hbWVzKHRlcm1fZnJlcXVlbmN5KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtID0gdGVybV9mcmVxdWVuY3kpDQoNCiMgUHJpbnQgdGhlIHdvcmRjbG91ZCB3aXRoIHRoZSBzcGVjaWZpZWQgY29sb3JzDQp3b3JkY2xvdWQod29yZF9mcmVxcyR0ZXJtLA0KICAgICAgICAgIHdvcmRfZnJlcXMkbnVtLA0KICAgICAgICAgIG1heC53b3JkcyA9IDUwLA0KICAgICAgICAgIGNvbG9ycyA9IGMoImdyZXk2MCIsICJkYXJrb3JhbmdlIiwgInN0ZWVsYmx1ZSIpDQogICAgICAgICAgKQ0KYGBgDQoNCiMjIE4tR3JhbSB0b2tlbml6YXRpb24NCk5vdyB3ZSB3aWxsIHNoaWZ0IG91ciBmb2N1cyB0byB0b2tlbnMgY29udGFpbmluZyB0d28gYW5kIHRocmVlIHdvcmRzLiBUaGlzIGNhbiBoZWxwIGV4dHJhY3QgdXNlZnVsIHBocmFzZXMgd2hpY2ggY2FuIGxlYWQgdG8gc29tZSBhZGRpdGlvbmFsIGluc2lnaHRzIG9yIHByb3ZpZGUgaW1wcm92ZWQgcHJlZGljdGl2ZSBhdHRyaWJ1dGVzIGZvciBjb25zdHJ1Y3Rpb24gb2YgYSBtYWNoaW5lIGxlYXJuaW5nIGFsZ29yaXRobS4gDQoNCmBgYHtyLCBlY2hvPUZBTFNFLCBmaWcuaGVpZ2h0PTV9DQoNCmJpZ3JhbVRva2VuaXplciA8LSBmdW5jdGlvbih4KSBOR3JhbVRva2VuaXplcih4LCBXZWthX2NvbnRyb2wobWluID0gMiwgbWF4ID0gMikpDQpiaWdyYW1zIDwtIHVubGlzdChsYXBwbHkoY2xlYW5fc2FtcGxlLCBiaWdyYW1Ub2tlbml6ZXIpKQ0KbmJCaWdyYW1zIDwtIGxlbmd0aCh0YWJsZShiaWdyYW1zKSkNCnRyaWdyYW1Ub2tlbml6ZXIgPC0gZnVuY3Rpb24oeCkgTkdyYW1Ub2tlbml6ZXIoeCwgV2VrYV9jb250cm9sKG1pbiA9IDMsIG1heCA9IDMpKQ0KdHJpZ3JhbXMgPC0gdW5saXN0KGxhcHBseShjbGVhbl9zYW1wbGUsIHRyaWdyYW1Ub2tlbml6ZXIpKQ0KbmJUcmlncmFtcyA8LSBsZW5ndGgodGFibGUodHJpZ3JhbXMpKQ0KYGBgDQpgYGB7ciwgZWNobz1GQUxTRX0NCg0KZGZCaWdyYW1GcmVxIDwtIHRibF9kZihkYXRhLmZyYW1lKHRhYmxlKGJpZ3JhbXMpKSkgJT4lIGFycmFuZ2UoRnJlcSkgJT4lDQogICAgICAgICAgICAgICAgbXV0YXRlKGJpZ3JhbXMgPSBmYWN0b3IoYmlncmFtcyxsZXZlbHMgPSBhcy5jaGFyYWN0ZXIoYmlncmFtcykpKQ0KDQpkZlRyaWdyYW1GcmVxIDwtIHRibF9kZihkYXRhLmZyYW1lKHRhYmxlKHRyaWdyYW1zKSkpICU+JSBhcnJhbmdlKEZyZXEpICU+JQ0KICAgICAgICAgICAgICAgICBtdXRhdGUodHJpZ3JhbXMgPSBmYWN0b3IodHJpZ3JhbXMsbGV2ZWxzID0gYXMuY2hhcmFjdGVyKHRyaWdyYW1zKSkpDQoNCmdiaWdyYW0gPC0gZ2dwbG90KHRhaWwoZGZCaWdyYW1GcmVxLDEwKSxhZXMoeCA9IGJpZ3JhbXMseSA9IEZyZXEsIGZpbGwgPSBGcmVxKSkgKyANCiAgICAgICAgICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJzdGVlbGJsdWUiLCBzaXplID0gMTQpLCBheGlzLnRleHQueCAgPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDEyKSkgKyBnZ3RpdGxlKCJNb3N0IGZyZXF1ZW50IGJpZ3JhbXMiKSArIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gImdyZXk2MCIsIGhpZ2ggPSAic3RlZWxibHVlIikNCg0KZ3RyaWdyYW0gPC0gZ2dwbG90KHRhaWwoZGZUcmlncmFtRnJlcSwxMCksYWVzKHggPSB0cmlncmFtcyx5ID0gRnJlcSwgZmlsbCA9IEZyZXEpKSArIA0KICAgICAgICAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKyB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoY29sb3VyID0gInN0ZWVsYmx1ZSIsIHNpemUgPSAxNCksIGF4aXMudGV4dC54ICA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBmYWNlID0gImJvbGQiLCB2anVzdCA9IDAuNSwgc2l6ZSA9IDEyKSkgKyBnZ3RpdGxlKCJNb3N0IGZyZXF1ZW50IHRyaWdyYW1zIikgKyBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJncmV5NjAiLCBoaWdoID0gInN0ZWVsYmx1ZSIpDQoNCmdyaWQuYXJyYW5nZShnYmlncmFtLGd0cmlncmFtLG5jb2wgPSAyKQ0KYGBgDQoNCiMjIEluc2lnaHRzDQotIFN0ZW1taW5nIG5lZWRzIHRvIGJlIGFkanVzdGVkIHNvIHdlIGRvbid0IGhhdmUgdHJpZ3JhbXMgbGlrZSAiaGFwcGkgbW90aGVyIGRheSIgb3IgInByZXNpZCBiYXJhY2sgb2JhbWEiDQotIExvd2Nhc2luZyBpbmR1Y2VzIGEgbG9zcyBvZiBpbmZvcm1hdGlvbiByZSB0aGUgcHJlc2VuY2Ugb2YgcGVyc29uYWwgbmFtZXMsIGNpdHkgbmFtZXMsIHN0YXRlIG5hbWVzIGFuZCBhbGlrZQ0KLSBDaGFuZ2luZyBOLWdyYW0gb3JkZXIsIGkuZS4gZnJvbSBiaWdyYW0gdG8gdHJpZ3JhbSwgeWllbGRzIHRvIGRyYXN0aWMgZGVjcmVhc2Ugb2Ygb2JzZXJ2ZWQgY291bnRzIA0KLSBDb3Jwb3JhIGlzIGh1Z2UsIEkgbmVlZCBtb3JlIHJlc2VhcmNoIGludG8gaG93IHRvIGVmZmljaWVudGx5IGRlYWwgd2l0aCBpdCwgZXNwZWNpYWxseSBpbiB0ZXJtcyBvZiBidWlsZGluZyBhIG1vZGVsIHRyYWluZWQgb24gdGhlIGNvbXBsZXRlIGRhdGEgc2V0Lg0KDQojIyBOZXh0IHN0ZXBzDQpUaGUgZnV0dXJlIHdvcmsgb24gdGhlIGNhcHN0b25lIHByb2plY3Qgd2lsbCBiZSBkaXJlY3RlZCB0b3dhcmRzIHRoZSBkZXZlbG9wbWVudCBvZiBwcm9wZXIgc3RyYXRlZ3kgdGhhdCB3aWxsIGJlIHVzZWQgZm9yIG1vZGVsbGluZywgaS5lLiBjaG9vc2luZyBhbmQgY29uc3RydWN0aW5nIGFuIGFkZXF1YXRlIHNldCBvZiBmZWF0dXJlcywgY2hvb3NpbmcgYW5kIGltcGxlbWVudGluZyB0aGUgb3B0aW1hbCBwcmVkaWN0aW9uIGFsZ29yaXRobSB3aGljaCB3aWxsIGVuYWJsZSBidWlsZGluZyBmYXN0IGFuZCB1c2VyIGZyaWVuZGx5IGFwcC4NCg0K