#Load Packages
library(tidyverse)
library(tm) # Will use to create corpus and modify text therein.
library(SnowballC) # Will use for "stemming." 
library(rpart) # Will use to construct a CART model.
library(rpart.plot) # Will use to plot CART tree.
library(gridExtra)

1a) Let us first explore the dataset. What is the number of reviews associated with each rating? What is the average length for each of the five ratings? Please comment briefly. [10 pts] [Hint: The number of characters can be obtained with the nchar function.]

reviews = read.csv("/Users/kayhanbabakan/OneDrive/MIT/Analytics Edge/HWK5/airbnb-small.csv", stringsAsFactors = FALSE)
aggregate(id~review_scores_rating,reviews,length)
review_text = Corpus(VectorSource(reviews$comments)) 
review_charcount = data.frame(sapply(review_text, nchar))
mean(review_charcount[,"sapply.review_text..nchar."])
[1] 297.6779

Comments
The number of reviews associated with each data set can be found as per the table above. There seem to be alot of reviews in the 5 star range and fairly few as you decrease in rating. The average character count of eaching rating is 297.6779295


1b) Create a corpus based on the comments column of the dataset. Clean the corpus by converting the text to lowercase, removing all stop words, removing the word “airbnb”, and stemming the document. What is the text of the first three documents in the corpus? [5 pts]

corpus = Corpus(VectorSource(reviews$comments))
corpus = tm_map(corpus,tolower)
corpus = tm_map(corpus, removeWords, stopwords("english"))
corpus = tm_map(corpus, removeWords, c("airbnb"))
corpus = tm_map(corpus, stemDocument)
corpus <- tm_map(corpus, removePunctuation)
suppressWarnings(strwrap(corpus[[1]]))
[1] "good stay issu direct instruct differ actual properti"
suppressWarnings(strwrap(corpus[[2]]))
[1] "home quiet neighborhood flushing room stay newli renovated enjoy stay access manhattan via one bus 7 train recommend host"
[2] "home"                                                                                                                     
suppressWarnings(strwrap(corpus[[3]]))
[1] "ernest welcom us generous beauti home plenti inform apart area enjoy wonder stay comfort avail surroundings can recommend"
[2] "ernest host apart great manhattan get away"                                                                               

1c) Calculate the word frequencies in the corpus. What are the words that appear in 800 reviews or more? Then, remove the words that appear in less than 1% of the reviews. How many words do you still have after sparsifying the corpus? [5 pts]

frequencies = DocumentTermMatrix(corpus)
findFreqTerms(frequencies, lowfreq=800)
 [1] "stay"      "host"      "recommend" "room"      "apart"     "great"     "clean"     "locat"     "place"     "subway"    "nice"     
[12] "realli"   
sparse = removeSparseTerms(frequencies, 0.99)
document_terms = as.data.frame(as.matrix(sparse))
ncol(document_terms)
[1] 413

1d) Convert your corpus to a dataframe containing a row for each review, a column for each word in the sparsified corpus, a column for the review length, and a column for the dependent variable (i.e., whether the review is positive or negative). Next, split the dataframe into a training set comprising all reviews up to December 31, 2017, and a test set comprising all from January 1, 2018 onward. What is the proportion of positive reviews in the training set and in the test set? Comment briefly.

document_terms$char_count = sapply(review_text, nchar)
document_terms$review_scores_rating = reviews$review_scores_rating >= 4
document_terms$date = reviews$date
split1 = (document_terms$date <= "2017-12-31")
split2 = (document_terms$date >= "2018-01-01")
train = document_terms[split1,]
test = document_terms[split2,]
train$date = as.Date(train$date)
test$date = as.Date(test$date)

train_tot=nrow(train)
train_tru = nrow(subset(train,review_scores_rating==TRUE))
test_tot=nrow(test)
test_tru = nrow(subset(test,review_scores_rating==TRUE))

train_tru/train_tot
[1] 0.9269134
test_tru/test_tot
[1] 0.9583756

Comments
The proportion of Trues/Total is rougly the same however we did not stratify the data set and we do not have the same proprotion of true/total in both datasets. The percentages are so similar due to the low volume of negative reviews in general.


1e) Construct a manually (without performing cross- validation). Attach the images of three trees obtained with three values of cp. Discuss and interpret the variables selected by the models. [15 pts] Note: If you wish, you can look for comments containing a particular word with the following command: reviews[grepl(“wordtosearch”, reviews$comments), “comments”]

tree.cp01 = rpart(review_scores_rating ~. -char_count -date, data=train,method = "class", control = rpart.control(cp=.01))
prp(tree.cp01)


tree.cp001 = rpart(review_scores_rating ~. -char_count -date, data=train,method = "class", control = rpart.control(cp=.001))
prp(tree.cp001)


tree.cp005 = rpart(review_scores_rating ~. -char_count -date, data=train, method = "class", control = rpart.control(cp=.005))
prp(tree.cp005)

Comments
following the path of the simple model, we can see if the word bathroom was not used at all the rating is percieved as positive. If the bathroom was mentioned once or more, and the word expect was used then the rating was poor as this could also mean (unexpected), if the word expected was not used and the word door was used we look to the word recommend, if the word recommend was not used the rating was poor if it was the rating was positive.


1f) Propose a simple baseline model. Report the accuracy, the true positive rate and the false positive rate of each your three CART models and of your baseline model on the test set. Comment briefly on your results—including the magnitude of your true positive and false positive rates. [10 pts]

#baselineprediction
baseline = matrix(0,2,2)
baseline[1,2]=223
baseline[2,2]=2955
colnames(baseline)=c("False","True")
rownames(baseline)=c("False","True")
baseline
      False True
False     0  223
True      0 2955
#r-part prediction
pred = predict(tree.cp01, newdata=test, type="class")
confusionmatrix.cp01 = table(test$review_scores_rating,pred)
confusionmatrix.cp01
       pred
        FALSE TRUE
  FALSE     3   38
  TRUE      8  936
pred = predict(tree.cp001, newdata=test, type="class")
confusionmatrix.cp001 = table(test$review_scores_rating,pred)
confusionmatrix.cp001
       pred
        FALSE TRUE
  FALSE     8   33
  TRUE     14  930
pred = predict(tree.cp005, newdata=test, type="class")
confusionmatrix.cp005 = table(test$review_scores_rating,pred)
confusionmatrix.cp005
       pred
        FALSE TRUE
  FALSE     5   36
  TRUE     12  932
LS0tCnRpdGxlOiAiSG9tZXdvcmsgNSBUZXh0IEFuYWx5dGljcyIKYXV0aG9yOiAiS2F5aGFuIEJhYmFrYW4gPC9icj4gQW5hbHl0aWNzIEVkZ2UgMTUuMDcxIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKICB3b3JkX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQpgYGB7cn0KI0xvYWQgUGFja2FnZXMKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkodG0pICMgV2lsbCB1c2UgdG8gY3JlYXRlIGNvcnB1cyBhbmQgbW9kaWZ5IHRleHQgdGhlcmVpbi4KbGlicmFyeShTbm93YmFsbEMpICMgV2lsbCB1c2UgZm9yICJzdGVtbWluZy4iIApsaWJyYXJ5KHJwYXJ0KSAjIFdpbGwgdXNlIHRvIGNvbnN0cnVjdCBhIENBUlQgbW9kZWwuCmxpYnJhcnkocnBhcnQucGxvdCkgIyBXaWxsIHVzZSB0byBwbG90IENBUlQgdHJlZS4KbGlicmFyeShncmlkRXh0cmEpCmBgYAo8Yj4KMWEpIExldCB1cyBmaXJzdCBleHBsb3JlIHRoZSBkYXRhc2V0LiBXaGF0IGlzIHRoZSBudW1iZXIgb2YgcmV2aWV3cyBhc3NvY2lhdGVkIHdpdGggZWFjaCByYXRpbmc/IFdoYXQgaXMgdGhlIGF2ZXJhZ2UgbGVuZ3RoIGZvciBlYWNoIG9mIHRoZSBmaXZlIHJhdGluZ3M/IFBsZWFzZSBjb21tZW50IGJyaWVmbHkuIFsxMCBwdHNdCltIaW50OiBUaGUgbnVtYmVyIG9mIGNoYXJhY3RlcnMgY2FuIGJlIG9idGFpbmVkIHdpdGggdGhlIG5jaGFyIGZ1bmN0aW9uLl08L2I+CgpgYGB7cn0KcmV2aWV3cyA9IHJlYWQuY3N2KCIvVXNlcnMva2F5aGFuYmFiYWthbi9PbmVEcml2ZS9NSVQvQW5hbHl0aWNzIEVkZ2UvSFdLNS9haXJibmItc21hbGwuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQphZ2dyZWdhdGUoaWR+cmV2aWV3X3Njb3Jlc19yYXRpbmcscmV2aWV3cyxsZW5ndGgpCnJldmlld190ZXh0ID0gQ29ycHVzKFZlY3RvclNvdXJjZShyZXZpZXdzJGNvbW1lbnRzKSkgCnJldmlld19jaGFyY291bnQgPSBkYXRhLmZyYW1lKHNhcHBseShyZXZpZXdfdGV4dCwgbmNoYXIpKQptZWFuKHJldmlld19jaGFyY291bnRbLCJzYXBwbHkucmV2aWV3X3RleHQuLm5jaGFyLiJdKQpgYGAKPHNtYWxsPjxiPiBDb21tZW50cyA8L2I+PC9icj4KVGhlIG51bWJlciBvZiByZXZpZXdzIGFzc29jaWF0ZWQgd2l0aCBlYWNoIGRhdGEgc2V0IGNhbiBiZSBmb3VuZCBhcyBwZXIgdGhlIHRhYmxlIGFib3ZlLiBUaGVyZSBzZWVtIHRvIGJlIGFsb3Qgb2YgcmV2aWV3cyBpbiB0aGUgNSBzdGFyIHJhbmdlIGFuZCBmYWlybHkgZmV3IGFzIHlvdSBkZWNyZWFzZSBpbiByYXRpbmcuIFRoZSBhdmVyYWdlIGNoYXJhY3RlciBjb3VudCBvZiBlYWNoaW5nIHJhdGluZyBpcyBgciBtZWFuKHJldmlld19jaGFyY291bnRbLCJzYXBwbHkucmV2aWV3X3RleHQuLm5jaGFyLiJdKWAKPC9zbWFsbD48L2JyPjwvYnI+CjxiPjFiKSBDcmVhdGUgYSBjb3JwdXMgYmFzZWQgb24gdGhlIGNvbW1lbnRzIGNvbHVtbiBvZiB0aGUgZGF0YXNldC4gQ2xlYW4gdGhlIGNvcnB1cyBieSBjb252ZXJ0aW5nIHRoZSB0ZXh0IHRvIGxvd2VyY2FzZSwgcmVtb3ZpbmcgYWxsIHN0b3Agd29yZHMsIHJlbW92aW5nIHRoZSB3b3JkIOKAnGFpcmJuYuKAnSwgYW5kIHN0ZW1taW5nIHRoZSBkb2N1bWVudC4gV2hhdCBpcyB0aGUgdGV4dCBvZiB0aGUgZmlyc3QgdGhyZWUgZG9jdW1lbnRzIGluIHRoZSBjb3JwdXM/IFs1IHB0c108L2I+CgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpjb3JwdXMgPSBDb3JwdXMoVmVjdG9yU291cmNlKHJldmlld3MkY29tbWVudHMpKQpjb3JwdXMgPSB0bV9tYXAoY29ycHVzLHRvbG93ZXIpCmNvcnB1cyA9IHRtX21hcChjb3JwdXMsIHJlbW92ZVdvcmRzLCBzdG9wd29yZHMoImVuZ2xpc2giKSkKY29ycHVzID0gdG1fbWFwKGNvcnB1cywgcmVtb3ZlV29yZHMsIGMoImFpcmJuYiIpKQpjb3JwdXMgPSB0bV9tYXAoY29ycHVzLCBzdGVtRG9jdW1lbnQpCmNvcnB1cyA8LSB0bV9tYXAoY29ycHVzLCByZW1vdmVQdW5jdHVhdGlvbikKc3VwcHJlc3NXYXJuaW5ncyhzdHJ3cmFwKGNvcnB1c1tbMV1dKSkKc3VwcHJlc3NXYXJuaW5ncyhzdHJ3cmFwKGNvcnB1c1tbMl1dKSkKc3VwcHJlc3NXYXJuaW5ncyhzdHJ3cmFwKGNvcnB1c1tbM11dKSkKYGBgCjxiPgoxYykgQ2FsY3VsYXRlIHRoZSB3b3JkIGZyZXF1ZW5jaWVzIGluIHRoZSBjb3JwdXMuIFdoYXQgYXJlIHRoZSB3b3JkcyB0aGF0IGFwcGVhciBpbiA4MDAgcmV2aWV3cyBvciBtb3JlPyBUaGVuLCByZW1vdmUgdGhlIHdvcmRzIHRoYXQgYXBwZWFyIGluIGxlc3MgdGhhbiAxJSBvZiB0aGUgcmV2aWV3cy4gSG93IG1hbnkgd29yZHMgZG8geW91IHN0aWxsIGhhdmUgYWZ0ZXIgc3BhcnNpZnlpbmcgdGhlIGNvcnB1cz8gWzUgcHRzXTwvYj4KYGBge3J9CmZyZXF1ZW5jaWVzID0gRG9jdW1lbnRUZXJtTWF0cml4KGNvcnB1cykKZmluZEZyZXFUZXJtcyhmcmVxdWVuY2llcywgbG93ZnJlcT04MDApCnNwYXJzZSA9IHJlbW92ZVNwYXJzZVRlcm1zKGZyZXF1ZW5jaWVzLCAwLjk5KQpkb2N1bWVudF90ZXJtcyA9IGFzLmRhdGEuZnJhbWUoYXMubWF0cml4KHNwYXJzZSkpCm5jb2woZG9jdW1lbnRfdGVybXMpCmBgYAoKPGI+MWQpIENvbnZlcnQgeW91ciBjb3JwdXMgdG8gYSBkYXRhZnJhbWUgY29udGFpbmluZyBhIHJvdyBmb3IgZWFjaCByZXZpZXcsIGEgY29sdW1uIGZvciBlYWNoIHdvcmQgaW4gdGhlIHNwYXJzaWZpZWQgY29ycHVzLCBhIGNvbHVtbiBmb3IgdGhlIHJldmlldyBsZW5ndGgsIGFuZCBhIGNvbHVtbiBmb3IgdGhlIGRlcGVuZGVudCB2YXJpYWJsZSAoaS5lLiwgd2hldGhlciB0aGUgcmV2aWV3IGlzIHBvc2l0aXZlIG9yIG5lZ2F0aXZlKS4gTmV4dCwgc3BsaXQgdGhlIGRhdGFmcmFtZSBpbnRvIGEgdHJhaW5pbmcgc2V0IGNvbXByaXNpbmcgYWxsIHJldmlld3MgdXAgdG8gRGVjZW1iZXIgMzEsIDIwMTcsIGFuZCBhIHRlc3Qgc2V0IGNvbXByaXNpbmcgYWxsIGZyb20gSmFudWFyeSAxLCAyMDE4IG9ud2FyZC4gV2hhdCBpcyB0aGUgcHJvcG9ydGlvbiBvZiBwb3NpdGl2ZSByZXZpZXdzIGluIHRoZSB0cmFpbmluZyBzZXQgYW5kIGluIHRoZSB0ZXN0IHNldD8gQ29tbWVudCBicmllZmx5LiA8L2JyPiA8L2I+CgpgYGB7cn0KZG9jdW1lbnRfdGVybXMkY2hhcl9jb3VudCA9IHNhcHBseShyZXZpZXdfdGV4dCwgbmNoYXIpCmRvY3VtZW50X3Rlcm1zJHJldmlld19zY29yZXNfcmF0aW5nID0gcmV2aWV3cyRyZXZpZXdfc2NvcmVzX3JhdGluZyA+PSA0CmRvY3VtZW50X3Rlcm1zJGRhdGUgPSByZXZpZXdzJGRhdGUKc3BsaXQxID0gKGRvY3VtZW50X3Rlcm1zJGRhdGUgPD0gIjIwMTctMTItMzEiKQpzcGxpdDIgPSAoZG9jdW1lbnRfdGVybXMkZGF0ZSA+PSAiMjAxOC0wMS0wMSIpCnRyYWluID0gZG9jdW1lbnRfdGVybXNbc3BsaXQxLF0KdGVzdCA9IGRvY3VtZW50X3Rlcm1zW3NwbGl0MixdCnRyYWluJGRhdGUgPSBhcy5EYXRlKHRyYWluJGRhdGUpCnRlc3QkZGF0ZSA9IGFzLkRhdGUodGVzdCRkYXRlKQoKdHJhaW5fdG90PW5yb3codHJhaW4pCnRyYWluX3RydSA9IG5yb3coc3Vic2V0KHRyYWluLHJldmlld19zY29yZXNfcmF0aW5nPT1UUlVFKSkKdGVzdF90b3Q9bnJvdyh0ZXN0KQp0ZXN0X3RydSA9IG5yb3coc3Vic2V0KHRlc3QscmV2aWV3X3Njb3Jlc19yYXRpbmc9PVRSVUUpKQoKdHJhaW5fdHJ1L3RyYWluX3RvdAp0ZXN0X3RydS90ZXN0X3RvdAoKYGBgCjxzbWFsbD4KPGI+IENvbW1lbnRzIDwvYj48L2JyPgpUaGUgcHJvcG9ydGlvbiBvZiBUcnVlcy9Ub3RhbCBpcyByb3VnbHkgdGhlIHNhbWUgaG93ZXZlciB3ZSBkaWQgbm90IHN0cmF0aWZ5IHRoZSBkYXRhIHNldCBhbmQgd2UgZG8gbm90IGhhdmUgdGhlIHNhbWUgcHJvcHJvdGlvbiBvZiB0cnVlL3RvdGFsIGluIGJvdGggZGF0YXNldHMuIFRoZSBwZXJjZW50YWdlcyBhcmUgc28gc2ltaWxhciBkdWUgdG8gdGhlIGxvdyB2b2x1bWUgb2YgbmVnYXRpdmUgcmV2aWV3cyBpbiBnZW5lcmFsLjwvc21hbGw+PC9icj48L2JyPgoKPGI+MWUpIENvbnN0cnVjdCBhIG1hbnVhbGx5ICh3aXRob3V0IHBlcmZvcm1pbmcgY3Jvc3MtIHZhbGlkYXRpb24pLiBBdHRhY2ggdGhlIGltYWdlcyBvZiB0aHJlZSB0cmVlcyBvYnRhaW5lZCB3aXRoIHRocmVlIHZhbHVlcyBvZiBjcC4gRGlzY3VzcyBhbmQgaW50ZXJwcmV0IHRoZSB2YXJpYWJsZXMgc2VsZWN0ZWQgYnkgdGhlIG1vZGVscy4gWzE1IHB0c10KTm90ZTogSWYgeW91IHdpc2gsIHlvdSBjYW4gbG9vayBmb3IgY29tbWVudHMgY29udGFpbmluZyBhIHBhcnRpY3VsYXIgd29yZCB3aXRoIHRoZSBmb2xsb3dpbmcgY29tbWFuZDogcmV2aWV3c1tncmVwbCgid29yZHRvc2VhcmNoIiwgcmV2aWV3cyRjb21tZW50cyksICJjb21tZW50cyJdPC9iPjwvYnI+CgpgYGB7cn0KdHJlZS5jcDAxID0gcnBhcnQocmV2aWV3X3Njb3Jlc19yYXRpbmcgfi4gLWNoYXJfY291bnQgLWRhdGUsIGRhdGE9dHJhaW4sbWV0aG9kID0gImNsYXNzIiwgY29udHJvbCA9IHJwYXJ0LmNvbnRyb2woY3A9LjAxKSkKcHJwKHRyZWUuY3AwMSkKCnRyZWUuY3AwMDEgPSBycGFydChyZXZpZXdfc2NvcmVzX3JhdGluZyB+LiAtY2hhcl9jb3VudCAtZGF0ZSwgZGF0YT10cmFpbixtZXRob2QgPSAiY2xhc3MiLCBjb250cm9sID0gcnBhcnQuY29udHJvbChjcD0uMDAxKSkKcHJwKHRyZWUuY3AwMDEpCgp0cmVlLmNwMDA1ID0gcnBhcnQocmV2aWV3X3Njb3Jlc19yYXRpbmcgfi4gLWNoYXJfY291bnQgLWRhdGUsIGRhdGE9dHJhaW4sIG1ldGhvZCA9ICJjbGFzcyIsIGNvbnRyb2wgPSBycGFydC5jb250cm9sKGNwPS4wMDUpKQpwcnAodHJlZS5jcDAwNSkKYGBgCjxzbWFsbD4KPGI+IENvbW1lbnRzIDwvYj48L2JyPgpmb2xsb3dpbmcgdGhlIHBhdGggb2YgdGhlIHNpbXBsZSBtb2RlbCwgd2UgY2FuIHNlZSBpZiB0aGUgd29yZCBiYXRocm9vbSB3YXMgbm90IHVzZWQgYXQgYWxsIHRoZSByYXRpbmcgaXMgcGVyY2lldmVkIGFzIHBvc2l0aXZlLiAKSWYgdGhlIGJhdGhyb29tIHdhcyBtZW50aW9uZWQgb25jZSBvciBtb3JlLCBhbmQgdGhlIHdvcmQgZXhwZWN0IHdhcyB1c2VkIHRoZW4gdGhlIHJhdGluZyB3YXMgcG9vciBhcyB0aGlzIGNvdWxkIGFsc28gbWVhbiAodW5leHBlY3RlZCksIGlmIHRoZSB3b3JkIGV4cGVjdGVkIHdhcyBub3QgdXNlZCBhbmQgdGhlIHdvcmQgZG9vciB3YXMgdXNlZCB3ZSBsb29rIHRvIHRoZSB3b3JkIHJlY29tbWVuZCwgaWYgdGhlIHdvcmQgcmVjb21tZW5kIHdhcyBub3QgdXNlZCB0aGUgcmF0aW5nIHdhcyBwb29yIGlmIGl0IHdhcyB0aGUgcmF0aW5nIHdhcyBwb3NpdGl2ZS48L3NtYWxsPjwvYnI+PC9icj4KCjxiPjFmKSBQcm9wb3NlIGEgc2ltcGxlIGJhc2VsaW5lIG1vZGVsLiBSZXBvcnQgdGhlIGFjY3VyYWN5LCB0aGUgdHJ1ZSBwb3NpdGl2ZSByYXRlIGFuZCB0aGUgZmFsc2UgcG9zaXRpdmUgcmF0ZSBvZiBlYWNoIHlvdXIgdGhyZWUgQ0FSVCBtb2RlbHMgYW5kIG9mIHlvdXIgYmFzZWxpbmUgbW9kZWwgb24gdGhlIHRlc3Qgc2V0LiBDb21tZW50IGJyaWVmbHkgb24geW91ciByZXN1bHRz4oCUaW5jbHVkaW5nIHRoZSBtYWduaXR1ZGUgb2YgeW91ciB0cnVlIHBvc2l0aXZlIGFuZCBmYWxzZSBwb3NpdGl2ZSByYXRlcy4gWzEwIHB0c108L2I+PC9icj4KCmBgYHtyfQojYmFzZWxpbmVwcmVkaWN0aW9uCmJhc2VsaW5lID0gbWF0cml4KDAsMiwyKQpiYXNlbGluZVsxLDJdPTIyMwpiYXNlbGluZVsyLDJdPTI5NTUKY29sbmFtZXMoYmFzZWxpbmUpPWMoIkZhbHNlIiwiVHJ1ZSIpCnJvd25hbWVzKGJhc2VsaW5lKT1jKCJGYWxzZSIsIlRydWUiKQpiYXNlbGluZQoKI3ItcGFydCBwcmVkaWN0aW9uCnByZWQgPSBwcmVkaWN0KHRyZWUuY3AwMSwgbmV3ZGF0YT10ZXN0LCB0eXBlPSJjbGFzcyIpCmNvbmZ1c2lvbm1hdHJpeC5jcDAxID0gdGFibGUodGVzdCRyZXZpZXdfc2NvcmVzX3JhdGluZyxwcmVkKQpjb25mdXNpb25tYXRyaXguY3AwMQoKcHJlZCA9IHByZWRpY3QodHJlZS5jcDAwMSwgbmV3ZGF0YT10ZXN0LCB0eXBlPSJjbGFzcyIpCmNvbmZ1c2lvbm1hdHJpeC5jcDAwMSA9IHRhYmxlKHRlc3QkcmV2aWV3X3Njb3Jlc19yYXRpbmcscHJlZCkKY29uZnVzaW9ubWF0cml4LmNwMDAxCgpwcmVkID0gcHJlZGljdCh0cmVlLmNwMDA1LCBuZXdkYXRhPXRlc3QsIHR5cGU9ImNsYXNzIikKY29uZnVzaW9ubWF0cml4LmNwMDA1ID0gdGFibGUodGVzdCRyZXZpZXdfc2NvcmVzX3JhdGluZyxwcmVkKQpjb25mdXNpb25tYXRyaXguY3AwMDUKYGBgCg==