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.

File Name File Size [MB] Number of Lines Number of Words
Blogs 200.42 899288 37334131
News 196.28 77259 2643969
Twitter 159.36 2360148 30373583

Forming the sample corpus

As already told we’ll be using an aggregated sample of the initial data set for further analysis.

sample_blogs <- blogs[sample(1:length(blogs),10000)]
sample_news <- news[sample(1:length(news),10000)]
sample_twitter <- twitter[sample(1:length(twitter),10000)]
text_sample <- c(sample_blogs,sample_news,sample_twitter)

Let’s check the summary statistics for our sample corpus:

File Name File Size [MB] Number of Lines Number of Words
Sample 4.86 30000 890090

Data preprocessing - cleaning the corpus

Before the corpus analysis certain preprocessing steps are usually performed. These include the following:

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

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