Executive Summary

The purpose of this report is to demonstrate the first steps of building a predictive text model for the Coursera Data Science Certificate program. The steps this report will cover are obtaining the data, cleaning it, and finally an exploratory analysis.

Getting and Loading Data

First step in the project is to get the data for the project, and import it. Also required, is a list of words that would be described as profanity. I used a list of banned words by Google. The list can be found here.

Once I downloaded the datasets, I unzipped them, and loaded into my R session.

library(tidytext); library(dplyr); library(caret); library(quanteda);library(stringr)
library(tidyr);library(stringi);library(igraph);library(ggraph);library(widyr)
setwd("D:/Personal/Coursea/10.  Capstone Project")
con <- "https://d396qusza40orc.cloudfront.net/dsscapstone/dataset/Coursera-SwiftKey.zip"
#Create Connections to load data
con1 <- file("en_US.blogs.txt", "r")
con2 <- file("en_US.news.txt", "r")
con3 <- file("en_US.twitter.txt", "r")
con4 <- file("profane_list.txt","r")
blogdata <- readLines(con1)
newsdata <- readLines(con2)
twitterdata <- readLines(con3)
profanedata <- readLines(con4)
profanedata <- as.data.frame(profanedata)

Corpora Summaries

Before I get to cleaning the data so I can perform some modelling, let’s take a look at some basic summary statistics of the 3 corpora used in the project.

Blog data has 38,601,176 words and 899,288 lines.

News data has 2,755,797 words and 77,259 lines.

Twitter data has 31,130,580 words and 2,360,148 lines.

Cleaning the Data

Before cleaning the data, I decided that since there were so many lines and words between the 3 corpora, I would combine them then take a random sample of 5% of the lines, to be used further. Here is the code:

#Combine the 3 datasets into 1
textdata <- Reduce(function(...) merge(..., all=TRUE), list(blogdata, newsdata, twitterdata))
textdata <- textdata %>%
    gather(-corpus, key = "test", value = "text") %>%
    select(-test)

textdata <- filter(textdata,!is.na(text))

#Randomly sample 5% of rows (166835 of 3336695 rows), then write the data to a csv
set.seed(123987)
sample_data <- textdata %>%
    sample_n(166835)

#remove non ascii characters
sample_data$text <- gsub("[^\x20-\x7E]", "", sample_data$text)

write.csv(sample_data,"sample_textdata.csv")

#Load sample data
sample_data <- read.csv("sample_textdata.csv")

Now that we have a smaller sample of the text, I used the tidytext R package to tokenize the dataset. A token is a meaninful unit of text, most often a word that can then be used for further analysis. Tokenization is then splitting the text into a one token per document per row format. The unnest_tokens function of the tidytext package accomplishes the tokenization task for me, as well, it strips punctuation, converts all words to lower case, and retains the line number from which the word came from. Here is a sample few lines from this process:

head(toktextdata)

Exploratory Data Analysis

Basic Exploration

Now that we have tidied up the data, let’s explore it. First, I want to explore the word frequency. We then could measure the importance of the word by it’s frequency, which is called term frequency(tf). A common problem of this is that common English words like “the”, “of”, “and” would then be classified as the most important. There are various approaches to dealing with this, but we will use a technique called inverse document frequency (idf). Using inverse document frequency reduces the importance (weight) of the most frequent words, and increases the weight of words that appear less frequently. Finally, if you multiply the term frequency and inverse document frequency together, you get a statistic (called tf-idf) that is intended to measure how important a word is to a document.

To start, lets count the occurence of each word in the corpus, and the total number of words. Below are the first few results.

head(toktextdata_words)

As expected, “the” is the most common word, with 146,809 occurences out of 3,550,923 possible words. We can then calculate the term frequency (tf) by dividing n/total. After calculating the term frequency, we calculate the inverse document frequeny, then the “term frequence - inverse document frequency statistic” and plot the results.

plot_tf

For the three different corpora, the results were not unexpected. For example, with the twitter data, I was expecting to see things like abbreviations as the imporant words, based on their frequency. For the news corpora, I was expecting to see a bit more nouns (people, places) as news would be reporting on something. For the blogs data, I was expecting a bit more random information since anyone can write a blog, and that’s what it looks like happened.

N-Grams

Moving on, we will take a look at relationships between words, starting with n-grams. N-grams are a tokenization of adjacent words. In this analysis, we will look at bi-grams (two adjacent words) and tri-grams (three adjacent words). Taking a first look at our bigrams, yields the below:

bigrams

The most frequent bigrams include the most frequent words, which is not very interesting. A method of finding the more interesting pairs of words is to remove stop words. Stop words are common words that do not really add any information about the text. So, next we can remove the stop words.

bigrams

The bigrams are far more interesting than the common ones. Now, let’s look at the tri-grams. Following the same process as above and removing the stop words, we get the following:

trigrams

Tri-grams are a little less interesting because most of the information in the top 10 are either numbers or a special occassion.

As a next step, let’s build a network map of our bigrams so we can visualize all the relationships between words.

markovchain

The visualization above is an example of a Markov Chain in text analysis. A Markov Chain in our context, is a word that depends solely on the previous word. What it shows is the links between two words. In this case, only bigrams that appear greater than 50 times are visualized, otherwise the visualization would be unreadable. The other thing to note is there are several clusters of words. For example, “minutes” is in the middle, and is surrounded by numbers. This symbolizes that combinations of phrases, which as “10 minutes” or “30 minutes” were very popular in the corpora.

Next Steps

Next steps of this project are to start building the first predictive model, so, stay tuned!

LS0tDQp0aXRsZTogIkRhdGEgU2NpZW5jZSBDYXBzdG9uZSBQcm9qZWN0IC0gTWlsZXN0b25lIDEiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCmF1dGhvcjogIENocmlzIFNlbGlnDQotLS0NCg0KIyNFeGVjdXRpdmUgU3VtbWFyeSANClRoZSBwdXJwb3NlIG9mIHRoaXMgcmVwb3J0IGlzIHRvIGRlbW9uc3RyYXRlIHRoZSBmaXJzdCBzdGVwcyBvZiBidWlsZGluZyBhIHByZWRpY3RpdmUgdGV4dCBtb2RlbCBmb3IgdGhlIENvdXJzZXJhIERhdGEgU2NpZW5jZSBDZXJ0aWZpY2F0ZSBwcm9ncmFtLiAgVGhlIHN0ZXBzIHRoaXMgcmVwb3J0IHdpbGwgY292ZXIgYXJlIG9idGFpbmluZyB0aGUgZGF0YSwgY2xlYW5pbmcgaXQsIGFuZCBmaW5hbGx5IGFuIGV4cGxvcmF0b3J5IGFuYWx5c2lzLiAgDQoNCiMjR2V0dGluZyBhbmQgTG9hZGluZyBEYXRhDQpGaXJzdCBzdGVwIGluIHRoZSBwcm9qZWN0IGlzIHRvIGdldCB0aGUgZGF0YSBmb3IgdGhlIHByb2plY3QsIGFuZCBpbXBvcnQgaXQuICBBbHNvIHJlcXVpcmVkLCBpcyBhIGxpc3Qgb2Ygd29yZHMgdGhhdCB3b3VsZCBiZSBkZXNjcmliZWQgYXMgcHJvZmFuaXR5LiAgSSB1c2VkIGEgbGlzdCBvZiBiYW5uZWQgd29yZHMgYnkgR29vZ2xlLiAgVGhlIGxpc3QgY2FuIGJlIGZvdW5kIFtoZXJlXShodHRwczovL3d3dy5mcmVld2ViaGVhZGVycy5jb20vZnVsbC1saXN0LW9mLWJhZC13b3Jkcy1iYW5uZWQtYnktZ29vZ2xlLykuDQoNCk9uY2UgSSBkb3dubG9hZGVkIHRoZSBkYXRhc2V0cywgSSB1bnppcHBlZCB0aGVtLCBhbmQgbG9hZGVkIGludG8gbXkgUiBzZXNzaW9uLg0KDQpgYGB7ciwgY2FjaGU9VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkodGlkeXRleHQpOyBsaWJyYXJ5KGRwbHlyKTsgbGlicmFyeShjYXJldCk7IGxpYnJhcnkocXVhbnRlZGEpO2xpYnJhcnkoc3RyaW5ncikNCmxpYnJhcnkodGlkeXIpO2xpYnJhcnkoc3RyaW5naSk7bGlicmFyeShpZ3JhcGgpO2xpYnJhcnkoZ2dyYXBoKTtsaWJyYXJ5KHdpZHlyKQ0KDQpzZXR3ZCgiRDovUGVyc29uYWwvQ291cnNlYS8xMC4gIENhcHN0b25lIFByb2plY3QiKQ0KDQpjb24gPC0gImh0dHBzOi8vZDM5NnF1c3phNDBvcmMuY2xvdWRmcm9udC5uZXQvZHNzY2Fwc3RvbmUvZGF0YXNldC9Db3Vyc2VyYS1Td2lmdEtleS56aXAiDQoNCiNDcmVhdGUgQ29ubmVjdGlvbnMgdG8gbG9hZCBkYXRhDQpjb24xIDwtIGZpbGUoImVuX1VTLmJsb2dzLnR4dCIsICJyIikNCmNvbjIgPC0gZmlsZSgiZW5fVVMubmV3cy50eHQiLCAiciIpDQpjb24zIDwtIGZpbGUoImVuX1VTLnR3aXR0ZXIudHh0IiwgInIiKQ0KY29uNCA8LSBmaWxlKCJwcm9mYW5lX2xpc3QudHh0IiwiciIpDQoNCmJsb2dkYXRhIDwtIHJlYWRMaW5lcyhjb24xKQ0KbmV3c2RhdGEgPC0gcmVhZExpbmVzKGNvbjIpDQp0d2l0dGVyZGF0YSA8LSByZWFkTGluZXMoY29uMykNCnByb2ZhbmVkYXRhIDwtIHJlYWRMaW5lcyhjb240KQ0KDQpwcm9mYW5lZGF0YSA8LSBhcy5kYXRhLmZyYW1lKHByb2ZhbmVkYXRhKQ0KYGBgDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0Kc2V0d2QoIkQ6L1BlcnNvbmFsL0NvdXJzZWEvMTAuICBDYXBzdG9uZSBQcm9qZWN0IikNCnNhbXBsZV9kYXRhIDwtIHJlYWQuY3N2KCJzYW1wbGVfdGV4dGRhdGEuY3N2IikNCmBgYA0KIyNDb3Jwb3JhIFN1bW1hcmllcw0KQmVmb3JlIEkgZ2V0IHRvIGNsZWFuaW5nIHRoZSBkYXRhIHNvIEkgY2FuIHBlcmZvcm0gc29tZSBtb2RlbGxpbmcsIGxldCdzIHRha2UgYSBsb29rIGF0IHNvbWUgYmFzaWMgc3VtbWFyeSBzdGF0aXN0aWNzIG9mIHRoZSAzIGNvcnBvcmEgdXNlZCBpbiB0aGUgcHJvamVjdC4NCmBgYHtyLCBpbmNsdWRlPUZBTFNFLCBjYWNoZT1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KYmxvZ3dvcmRjb3VudCA8LSBwcmV0dHlOdW0oc3VtKHN0cl9jb3VudChibG9nZGF0YSwnXFx3KycpLG5hLnJtID0gVFJVRSksIGJpZy5tYXJrID0gIiwiKQ0KbmV3c3dvcmRjb3VudCA8LSBwcmV0dHlOdW0oc3VtKHN0cl9jb3VudChuZXdzZGF0YSwnXFx3KycpLG5hLnJtID0gVFJVRSksIGJpZy5tYXJrID0gIiwiKQ0KdHdpdHRlcndvcmRjb3VudCA8LSBwcmV0dHlOdW0oc3VtKHN0cl9jb3VudCh0d2l0dGVyZGF0YSwnXFx3KycpLG5hLnJtID0gVFJVRSksIGJpZy5tYXJrID0gIiwiKQ0KYmxvZ2xpbmVjb3VudCA8LSBwcmV0dHlOdW0obGVuZ3RoKGJsb2dkYXRhKSwgYmlnLm1hcmsgPSAiLCIpDQpuZXdzbGluZWNvdW50IDwtIHByZXR0eU51bShsZW5ndGgobmV3c2RhdGEpLCBiaWcubWFyayA9ICIsIikNCnR3aXR0ZXJsaW5lY291bnQgPC0gcHJldHR5TnVtKGxlbmd0aCh0d2l0dGVyZGF0YSksIGJpZy5tYXJrID0gIiwiKQ0KYGBgDQoNCkJsb2cgZGF0YSBoYXMgYHIgYmxvZ3dvcmRjb3VudGAgd29yZHMgYW5kIGByIGJsb2dsaW5lY291bnRgIGxpbmVzLg0KDQpOZXdzIGRhdGEgaGFzIGByIG5ld3N3b3JkY291bnRgIHdvcmRzIGFuZCBgciBuZXdzbGluZWNvdW50YCBsaW5lcy4NCg0KVHdpdHRlciBkYXRhIGhhcyBgciB0d2l0dGVyd29yZGNvdW50YCB3b3JkcyBhbmQgYHIgdHdpdHRlcmxpbmVjb3VudGAgbGluZXMuDQoNCg0KIyNDbGVhbmluZyB0aGUgRGF0YQ0KDQpCZWZvcmUgY2xlYW5pbmcgdGhlIGRhdGEsIEkgZGVjaWRlZCB0aGF0IHNpbmNlIHRoZXJlIHdlcmUgc28gbWFueSBsaW5lcyBhbmQgd29yZHMgYmV0d2VlbiB0aGUgMyBjb3Jwb3JhLCBJIHdvdWxkIGNvbWJpbmUgdGhlbSB0aGVuIHRha2UgYSByYW5kb20gc2FtcGxlIG9mIDUlIG9mIHRoZSBsaW5lcywgdG8gYmUgdXNlZCBmdXJ0aGVyLiAgSGVyZSBpcyB0aGUgY29kZToNCg0KYGBge3IsIGNhY2hlPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGV2YWw9RkFMU0V9DQojQ29tYmluZSB0aGUgMyBkYXRhc2V0cyBpbnRvIDENCnRleHRkYXRhIDwtIFJlZHVjZShmdW5jdGlvbiguLi4pIG1lcmdlKC4uLiwgYWxsPVRSVUUpLCBsaXN0KGJsb2dkYXRhLCBuZXdzZGF0YSwgdHdpdHRlcmRhdGEpKQ0KdGV4dGRhdGEgPC0gdGV4dGRhdGEgJT4lDQogICAgZ2F0aGVyKC1jb3JwdXMsIGtleSA9ICJ0ZXN0IiwgdmFsdWUgPSAidGV4dCIpICU+JQ0KICAgIHNlbGVjdCgtdGVzdCkNCg0KdGV4dGRhdGEgPC0gZmlsdGVyKHRleHRkYXRhLCFpcy5uYSh0ZXh0KSkNCg0KI1JhbmRvbWx5IHNhbXBsZSA1JSBvZiByb3dzICgxNjY4MzUgb2YgMzMzNjY5NSByb3dzKSwgdGhlbiB3cml0ZSB0aGUgZGF0YSB0byBhIGNzdg0Kc2V0LnNlZWQoMTIzOTg3KQ0Kc2FtcGxlX2RhdGEgPC0gdGV4dGRhdGEgJT4lDQogICAgc2FtcGxlX24oMTY2ODM1KQ0KDQojcmVtb3ZlIG5vbiBhc2NpaSBjaGFyYWN0ZXJzDQpzYW1wbGVfZGF0YSR0ZXh0IDwtIGdzdWIoIlteXHgyMC1ceDdFXSIsICIiLCBzYW1wbGVfZGF0YSR0ZXh0KQ0KDQp3cml0ZS5jc3Yoc2FtcGxlX2RhdGEsInNhbXBsZV90ZXh0ZGF0YS5jc3YiKQ0KDQojTG9hZCBzYW1wbGUgZGF0YQ0Kc2FtcGxlX2RhdGEgPC0gcmVhZC5jc3YoInNhbXBsZV90ZXh0ZGF0YS5jc3YiKQ0KYGBgDQoNCk5vdyB0aGF0IHdlIGhhdmUgYSBzbWFsbGVyIHNhbXBsZSBvZiB0aGUgdGV4dCwgSSB1c2VkIHRoZSB0aWR5dGV4dCBSIHBhY2thZ2UgdG8gdG9rZW5pemUgdGhlIGRhdGFzZXQuICBBIHRva2VuIGlzIGEgbWVhbmluZnVsIHVuaXQgb2YgdGV4dCwgbW9zdCBvZnRlbiBhIHdvcmQgdGhhdCBjYW4gdGhlbiBiZSB1c2VkIGZvciBmdXJ0aGVyIGFuYWx5c2lzLiAgVG9rZW5pemF0aW9uIGlzIHRoZW4gc3BsaXR0aW5nIHRoZSB0ZXh0IGludG8gYSBvbmUgdG9rZW4gcGVyIGRvY3VtZW50IHBlciByb3cgZm9ybWF0LiAgVGhlIHVubmVzdF90b2tlbnMgZnVuY3Rpb24gb2YgdGhlIHRpZHl0ZXh0IHBhY2thZ2UgYWNjb21wbGlzaGVzIHRoZSB0b2tlbml6YXRpb24gdGFzayBmb3IgbWUsIGFzIHdlbGwsIGl0IHN0cmlwcyBwdW5jdHVhdGlvbiwgY29udmVydHMgYWxsIHdvcmRzIHRvIGxvd2VyIGNhc2UsIGFuZCByZXRhaW5zIHRoZSBsaW5lIG51bWJlciBmcm9tIHdoaWNoIHRoZSB3b3JkIGNhbWUgZnJvbS4gIEhlcmUgaXMgYSBzYW1wbGUgZmV3IGxpbmVzIGZyb20gdGhpcyBwcm9jZXNzOiANCg0KYGBge3IsIGNhY2hlPVRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgaW5jbHVkZSA9IEZBTFNFfQ0KbmFtZXMoc2FtcGxlX2RhdGEpIDwtIGMoInJvdyIsImNvcnB1cyIsInRleHQiKQ0Kc2FtcGxlX2RhdGEkdGV4dCA8LSBhcy5jaGFyYWN0ZXIoc2FtcGxlX2RhdGEkdGV4dCkNCg0KdG9rdGV4dGRhdGEgPC0gc2FtcGxlX2RhdGEgJT4lDQogICAgdW5uZXN0X3Rva2Vucyh3b3JkLCB0ZXh0KQ0KYGBgDQpgYGB7cn0NCmhlYWQodG9rdGV4dGRhdGEpDQpgYGANCiMjRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcw0KDQoNCiMjIyBCYXNpYyBFeHBsb3JhdGlvbg0KTm93IHRoYXQgd2UgaGF2ZSB0aWRpZWQgdXAgdGhlIGRhdGEsIGxldCdzIGV4cGxvcmUgaXQuICBGaXJzdCwgSSB3YW50IHRvIGV4cGxvcmUgdGhlIHdvcmQgZnJlcXVlbmN5LiAgV2UgdGhlbiBjb3VsZCBtZWFzdXJlIHRoZSBpbXBvcnRhbmNlIG9mIHRoZSB3b3JkIGJ5IGl0J3MgZnJlcXVlbmN5LCB3aGljaCBpcyBjYWxsZWQgdGVybSBmcmVxdWVuY3kodGYpLiAgQSBjb21tb24gcHJvYmxlbSBvZiB0aGlzIGlzIHRoYXQgY29tbW9uIEVuZ2xpc2ggd29yZHMgIGxpa2UgInRoZSIsICJvZiIsICJhbmQiIHdvdWxkIHRoZW4gYmUgY2xhc3NpZmllZCBhcyB0aGUgbW9zdCBpbXBvcnRhbnQuICBUaGVyZSBhcmUgdmFyaW91cyBhcHByb2FjaGVzIHRvIGRlYWxpbmcgd2l0aCB0aGlzLCBidXQgd2Ugd2lsbCB1c2UgYSB0ZWNobmlxdWUgY2FsbGVkIGludmVyc2UgZG9jdW1lbnQgZnJlcXVlbmN5IChpZGYpLiAgVXNpbmcgaW52ZXJzZSBkb2N1bWVudCBmcmVxdWVuY3kgcmVkdWNlcyB0aGUgaW1wb3J0YW5jZSAod2VpZ2h0KSBvZiB0aGUgbW9zdCBmcmVxdWVudCB3b3JkcywgYW5kIGluY3JlYXNlcyB0aGUgd2VpZ2h0IG9mIHdvcmRzIHRoYXQgYXBwZWFyIGxlc3MgZnJlcXVlbnRseS4gIEZpbmFsbHksIGlmIHlvdSBtdWx0aXBseSB0aGUgdGVybSBmcmVxdWVuY3kgYW5kIGludmVyc2UgZG9jdW1lbnQgZnJlcXVlbmN5IHRvZ2V0aGVyLCB5b3UgZ2V0IGEgc3RhdGlzdGljIChjYWxsZWQgdGYtaWRmKSB0aGF0IGlzIGludGVuZGVkIHRvIG1lYXN1cmUgaG93IGltcG9ydGFudCBhIHdvcmQgaXMgdG8gYSBkb2N1bWVudC4gDQoNCg0KVG8gc3RhcnQsIGxldHMgY291bnQgdGhlIG9jY3VyZW5jZSBvZiBlYWNoIHdvcmQgaW4gdGhlIGNvcnB1cywgYW5kIHRoZSB0b3RhbCBudW1iZXIgb2Ygd29yZHMuICBCZWxvdyBhcmUgdGhlIGZpcnN0IGZldyByZXN1bHRzLg0KDQpgYGB7ciwgY2FjaGU9VFJVRSwgbWVzc2FnZT1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCiNDb3VudCB3b3JkcyBpbiBkb2N1bWVudA0KdG9rdGV4dGRhdGFfd29yZHMgPC0gdG9rdGV4dGRhdGEgJT4lDQogICAgY291bnQoY29ycHVzLCB3b3JkLHNvcnQgPSBUUlVFKSAlPiUNCiAgICB1bmdyb3VwKCkNCg0KI0NvdW50IHRvdGFsIHdvcmRzLCB0byBiZSB1c2VkIGxhdGVyDQp0b2t0ZXh0ZGF0YV90b3RhbF93b3JkcyA8LSB0b2t0ZXh0ZGF0YV93b3JkcyAlPiUNCiAgICBncm91cF9ieShjb3JwdXMpICU+JQ0KICAgIHN1bW1hcml6ZSgNCiAgICAgICAgdG90YWwgPSBzdW0obikpDQoNCnRva3RleHRkYXRhX3dvcmRzIDwtIGxlZnRfam9pbih0b2t0ZXh0ZGF0YV93b3JkcywgdG9rdGV4dGRhdGFfdG90YWxfd29yZHMpDQpgYGANCmBgYHtyIG1lc3NhZ2U9RkFMU0V9DQpoZWFkKHRva3RleHRkYXRhX3dvcmRzKQ0KYGBgDQoNCkFzIGV4cGVjdGVkLCAidGhlIiBpcyB0aGUgbW9zdCBjb21tb24gd29yZCwgd2l0aCAxNDYsODA5IG9jY3VyZW5jZXMgb3V0IG9mIDMsNTUwLDkyMyBwb3NzaWJsZSB3b3Jkcy4gIFdlIGNhbiB0aGVuIGNhbGN1bGF0ZSB0aGUgdGVybSBmcmVxdWVuY3kgKHRmKSBieSBkaXZpZGluZyBuL3RvdGFsLiAgQWZ0ZXIgY2FsY3VsYXRpbmcgdGhlIHRlcm0gZnJlcXVlbmN5LCB3ZSBjYWxjdWxhdGUgdGhlIGludmVyc2UgZG9jdW1lbnQgZnJlcXVlbnksIHRoZW4gdGhlICJ0ZXJtIGZyZXF1ZW5jZSAtIGludmVyc2UgZG9jdW1lbnQgZnJlcXVlbmN5IHN0YXRpc3RpYyIgYW5kIHBsb3QgdGhlIHJlc3VsdHMuDQoNCmBgYHtyLCBpbmNsdWRlPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBjYWNoZT1UUlVFfQ0KI0NhbGN1bGF0ZSB0aGUgdGVybSBmcmVxdWVuY3ksIGludmVyc2UgZG9jdW1lbnQgZnJlcXVlbmN5LCBhbmQgdGYtaWRmDQp0ZXJtZnJlcSA8LSB0b2t0ZXh0ZGF0YV93b3JkcyAlPiUNCiAgICBiaW5kX3RmX2lkZih3b3JkLCBjb3JwdXMsIG4pICU+JQ0KICAgIGFycmFuZ2UoZGVzYyh0Zl9pZGYpKQ0KDQojUGxvdCB0Zi1pZGYNCnBsb3RfdGYgPC0gdGVybWZyZXEgJT4lDQogICAgYXJyYW5nZShkZXNjKHRmX2lkZikpICU+JQ0KICAgIG11dGF0ZSh3b3JkID0gZmFjdG9yKHdvcmQsIGxldmVscyA9IHJldih1bmlxdWUod29yZCkpKSkNCg0KcGxvdF90ZiA8LSBwbG90X3RmICU+JQ0KICAgIGdyb3VwX2J5KGNvcnB1cyklPiUNCiAgICB0b3BfbigxMCkgJT4lDQogICAgdW5ncm91cCAlPiUNCiAgICBnZ3Bsb3QoYWVzKHdvcmQsIHRmX2lkZiwgZmlsbCA9IGNvcnB1cykpICsNCiAgICBnZW9tX2NvbCgpICsNCiAgICBsYWJzKHggPSBOVUxMLCB5ID0gInRmLWlkZiIpICsNCiAgICBjb29yZF9mbGlwKCkrDQogICAgZmFjZXRfd3JhcCh+Y29ycHVzLCBuY29sID0gMiwgc2NhbGVzID0gImZyZWUiKSsNCiAgICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KYGBge3J9DQpwbG90X3RmDQpgYGANCg0KRm9yIHRoZSB0aHJlZSBkaWZmZXJlbnQgY29ycG9yYSwgdGhlIHJlc3VsdHMgd2VyZSBub3QgdW5leHBlY3RlZC4gIEZvciBleGFtcGxlLCB3aXRoIHRoZSB0d2l0dGVyIGRhdGEsIEkgd2FzIGV4cGVjdGluZyB0byBzZWUgdGhpbmdzIGxpa2UgYWJicmV2aWF0aW9ucyBhcyB0aGUgaW1wb3JhbnQgd29yZHMsIGJhc2VkIG9uIHRoZWlyIGZyZXF1ZW5jeS4gIEZvciB0aGUgbmV3cyBjb3Jwb3JhLCBJIHdhcyBleHBlY3RpbmcgdG8gc2VlIGEgYml0IG1vcmUgbm91bnMgKHBlb3BsZSwgcGxhY2VzKSBhcyBuZXdzIHdvdWxkIGJlIHJlcG9ydGluZyBvbiBzb21ldGhpbmcuICBGb3IgdGhlIGJsb2dzIGRhdGEsIEkgd2FzIGV4cGVjdGluZyBhIGJpdCBtb3JlIHJhbmRvbSBpbmZvcm1hdGlvbiBzaW5jZSBhbnlvbmUgY2FuIHdyaXRlIGEgYmxvZywgYW5kIHRoYXQncyB3aGF0IGl0IGxvb2tzIGxpa2UgaGFwcGVuZWQuICANCg0KIyMjTi1HcmFtcw0KDQpNb3Zpbmcgb24sIHdlIHdpbGwgdGFrZSBhIGxvb2sgYXQgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHdvcmRzLCBzdGFydGluZyB3aXRoIG4tZ3JhbXMuICBOLWdyYW1zIGFyZSBhIHRva2VuaXphdGlvbiBvZiBhZGphY2VudCB3b3Jkcy4gIEluIHRoaXMgYW5hbHlzaXMsIHdlIHdpbGwgbG9vayBhdCBiaS1ncmFtcyAodHdvIGFkamFjZW50IHdvcmRzKSBhbmQgdHJpLWdyYW1zICh0aHJlZSBhZGphY2VudCB3b3JkcykuICBUYWtpbmcgYSBmaXJzdCBsb29rIGF0IG91ciBiaWdyYW1zLCB5aWVsZHMgdGhlIGJlbG93OiANCg0KYGBge3IgY2FjaGU9VFJVRSwgaW5jbHVkZT1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRX0NCmJpZ3JhbXMgPC0gc2FtcGxlX2RhdGEgJT4lDQogICAgdW5uZXN0X3Rva2VucyhiaWdyYW0sIHRleHQsIHRva2VuID0gIm5ncmFtcyIsIG4gPSAyKSANCmBgYA0KYGBge3IgY2FjaGUgPSBUUlVFLCBpbmNsdWRlID0gRkFMU0V9DQpiaWdyYW1zICU+JQ0KICAgIGNvdW50KGJpZ3JhbSxzb3J0ID0gVFJVRSkNCmBgYA0KYGBge3J9DQpiaWdyYW1zDQpgYGANCg0KVGhlIG1vc3QgZnJlcXVlbnQgYmlncmFtcyBpbmNsdWRlIHRoZSBtb3N0IGZyZXF1ZW50IHdvcmRzLCB3aGljaCBpcyBub3QgdmVyeSBpbnRlcmVzdGluZy4gIEEgbWV0aG9kIG9mIGZpbmRpbmcgdGhlIG1vcmUgaW50ZXJlc3RpbmcgcGFpcnMgb2Ygd29yZHMgaXMgdG8gcmVtb3ZlIHN0b3Agd29yZHMuICBTdG9wIHdvcmRzIGFyZSBjb21tb24gd29yZHMgdGhhdCBkbyBub3QgcmVhbGx5IGFkZCBhbnkgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHRleHQuIFNvLCBuZXh0IHdlIGNhbiByZW1vdmUgdGhlIHN0b3Agd29yZHMuDQoNCmBgYHtyIGNhY2hlPVRSVUUsIGluY2x1ZGU9RkFMU0V9DQojUmVtb3ZlIHN0b3Agd29yZHMgc28gd2UgY2FuIHNlZSBtb3JlIGludGVyZXN0aW5nIHBhaXJzIG9mIHdvcmRzDQpkYXRhKCJzdG9wX3dvcmRzIikNCmJpZ3JhbXNfc2VwYXJhdGVkIDwtIGJpZ3JhbXMgJT4lDQogICAgc2VwYXJhdGUoYmlncmFtLCBjKCJ3b3JkMSIsICJ3b3JkMiIpLCBzZXAgPSAiICIpDQoNCmJpZ3JhbXNfZmlsdGVyZWQgPC0gYmlncmFtc19zZXBhcmF0ZWQgJT4lDQogICAgZmlsdGVyKCF3b3JkMSAlaW4lIHN0b3Bfd29yZHMkd29yZCkgJT4lDQogICAgZmlsdGVyKCF3b3JkMiAlaW4lIHN0b3Bfd29yZHMkd29yZCkNCg0KIyBuZXcgYmlncmFtIGNvdW50czoNCmJpZ3JhbXMgPC0gYmlncmFtc19maWx0ZXJlZCAlPiUgDQogICAgY291bnQod29yZDEsIHdvcmQyLCBzb3J0ID0gVFJVRSkNCmBgYA0KYGBge3J9DQpiaWdyYW1zDQpgYGANClRoZSBiaWdyYW1zIGFyZSBmYXIgbW9yZSBpbnRlcmVzdGluZyB0aGFuIHRoZSBjb21tb24gb25lcy4gIE5vdywgbGV0J3MgbG9vayBhdCB0aGUgdHJpLWdyYW1zLiAgRm9sbG93aW5nIHRoZSBzYW1lIHByb2Nlc3MgYXMgYWJvdmUgYW5kIHJlbW92aW5nIHRoZSBzdG9wIHdvcmRzLCB3ZSBnZXQgdGhlIGZvbGxvd2luZzoNCg0KYGBge3IgY2FjaGU9VFJVRSwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCiNUcmktZ3JhbXMNCnRyaWdyYW1zIDwtIHNhbXBsZV9kYXRhICU+JQ0KICAgIHVubmVzdF90b2tlbnModHJpZ3JhbSwgdGV4dCwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDMpIA0KDQp0cmlncmFtcyAlPiUNCiAgICBjb3VudCh0cmlncmFtLHNvcnQgPSBUUlVFKQ0KDQojUmVtb3ZlIHN0b3Agd29yZHMgc28gd2UgY2FuIHNlZSBtb3JlIGludGVyZXN0aW5nIHBhaXJzIG9mIHdvcmRzDQp0cmlncmFtc19zZXBhcmF0ZWQgPC0gdHJpZ3JhbXMgJT4lDQogICAgc2VwYXJhdGUodHJpZ3JhbSwgYygid29yZDEiLCAid29yZDIiLCJ3b3JkMyIpLCBzZXAgPSAiICIpDQoNCnRyaWdyYW1zX2ZpbHRlcmVkIDwtIHRyaWdyYW1zX3NlcGFyYXRlZCAlPiUNCiAgICBmaWx0ZXIoIXdvcmQxICVpbiUgc3RvcF93b3JkcyR3b3JkKSAlPiUNCiAgICBmaWx0ZXIoIXdvcmQyICVpbiUgc3RvcF93b3JkcyR3b3JkKSAlPiUNCiAgICBmaWx0ZXIoIXdvcmQzICVpbiUgc3RvcF93b3JkcyR3b3JkKQ0KDQojIG5ldyB0cmlncmFtIGNvdW50czoNCnRyaWdyYW1zIDwtIHRyaWdyYW1zX2ZpbHRlcmVkICU+JSANCiAgICBjb3VudCh3b3JkMSwgd29yZDIsd29yZDMsIHNvcnQgPSBUUlVFKQ0KYGBgDQpgYGB7cn0NCnRyaWdyYW1zDQpgYGANCg0KVHJpLWdyYW1zIGFyZSBhIGxpdHRsZSBsZXNzIGludGVyZXN0aW5nIGJlY2F1c2UgbW9zdCBvZiB0aGUgaW5mb3JtYXRpb24gaW4gdGhlIHRvcCAxMCBhcmUgZWl0aGVyIG51bWJlcnMgb3IgYSBzcGVjaWFsIG9jY2Fzc2lvbi4NCg0KQXMgYSBuZXh0IHN0ZXAsIGxldCdzIGJ1aWxkIGEgbmV0d29yayBtYXAgb2Ygb3VyIGJpZ3JhbXMgc28gd2UgY2FuIHZpc3VhbGl6ZSBhbGwgdGhlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB3b3Jkcy4NCmBgYHtyIGNhY2hlPVRSVUUsIGVjaG89IEZBTFNFfQ0KYmlncmFtX2dyYXBoIDwtIGJpZ3JhbXMgJT4lDQogICAgZmlsdGVyKG4gPiA1MCkgJT4lDQogICAgZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKCkNCg0KbWFya292Y2hhaW4gPC0gZ2dyYXBoKGJpZ3JhbV9ncmFwaCwgbGF5b3V0ID0gImZyIikgKw0KICAgIGdlb21fZWRnZV9saW5rKCkgKw0KICAgIGdlb21fbm9kZV9wb2ludCgpICsNCiAgICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgdmp1c3QgPSAxLCBoanVzdCA9IDEpDQpgYGANCmBgYHtyfQ0KbWFya292Y2hhaW4NCmBgYGANCg0KVGhlIHZpc3VhbGl6YXRpb24gYWJvdmUgaXMgYW4gZXhhbXBsZSBvZiBhIE1hcmtvdiBDaGFpbiBpbiB0ZXh0IGFuYWx5c2lzLiAgQSBNYXJrb3YgQ2hhaW4gaW4gb3VyIGNvbnRleHQsIGlzIGEgd29yZCB0aGF0IGRlcGVuZHMgc29sZWx5IG9uIHRoZSBwcmV2aW91cyB3b3JkLiAgV2hhdCBpdCBzaG93cyBpcyB0aGUgbGlua3MgYmV0d2VlbiB0d28gd29yZHMuIEluIHRoaXMgY2FzZSwgb25seSBiaWdyYW1zIHRoYXQgYXBwZWFyIGdyZWF0ZXIgdGhhbiA1MCB0aW1lcyBhcmUgdmlzdWFsaXplZCwgb3RoZXJ3aXNlIHRoZSB2aXN1YWxpemF0aW9uIHdvdWxkIGJlIHVucmVhZGFibGUuICBUaGUgb3RoZXIgdGhpbmcgdG8gbm90ZSBpcyB0aGVyZSBhcmUgc2V2ZXJhbCBjbHVzdGVycyBvZiB3b3Jkcy4gIEZvciBleGFtcGxlLCAibWludXRlcyIgaXMgaW4gdGhlIG1pZGRsZSwgYW5kIGlzIHN1cnJvdW5kZWQgYnkgbnVtYmVycy4gIFRoaXMgc3ltYm9saXplcyB0aGF0IGNvbWJpbmF0aW9ucyBvZiBwaHJhc2VzLCB3aGljaCBhcyAiMTAgbWludXRlcyIgb3IgIjMwIG1pbnV0ZXMiIHdlcmUgdmVyeSBwb3B1bGFyIGluIHRoZSBjb3Jwb3JhLiAgDQoNCg0KIyMgTmV4dCBTdGVwcw0KTmV4dCBzdGVwcyBvZiB0aGlzIHByb2plY3QgYXJlIHRvIHN0YXJ0IGJ1aWxkaW5nIHRoZSBmaXJzdCBwcmVkaWN0aXZlIG1vZGVsLCBzbywgc3RheSB0dW5lZCENCg0K