作業五

download.file('https://github.com/ywchiu/rtibame/raw/master/Data/oneday2.csv', 'oneday.csv') 
oneday <- read.csv('oneday.csv', stringsAsFactors = FALSE)
head(oneday)
str(oneday)

library(jiebaR)
mixseg <- worker()
oneday.seg <- segment(oneday$content, mixseg)
tb <- table(oneday.seg)
tb <- tb[(nchar(names(tb)) >= 2) & (tb >= 10) & grepl('[\u4e00-\u9fa5]+', x = names(tb))]
tb

library(wordcloud2)
wordcloud2(tb, shape = "star")

iris classfication

data(iris)
#iris
library(rpart)
fit <- rpart( Species  ~ Sepal.Length +Sepal.Width + Petal.Length +Petal.Width,   data = iris )
fit
n= 150 

node), split, n, loss, yval, (yprob)
      * denotes terminal node

1) root 150 100 setosa (0.33333333 0.33333333 0.33333333)  
  2) Petal.Length< 2.45 50   0 setosa (1.00000000 0.00000000 0.00000000) *
  3) Petal.Length>=2.45 100  50 versicolor (0.00000000 0.50000000 0.50000000)  
    6) Petal.Width< 1.75 54   5 versicolor (0.00000000 0.90740741 0.09259259) *
    7) Petal.Width>=1.75 46   1 virginica (0.00000000 0.02173913 0.97826087) *
plot(fit, margin=0.1)
text(fit)

plot(iris$Petal.Length, iris$Petal.Width, col = iris$Species)
abline(v = 2.45, col="orange")
abline(h = 1.75, col="blue")

predict(fit, data.frame(Petal.Length = 2, Petal.Width= 3, Sepal.Length = 2, Sepal.Width = 2))
  setosa versicolor virginica
1      1          0         0
#predict(fit, iris)
predicted <- predict(fit, iris, type= 'class')
tb <- table(predicted,iris$Species)
# accuracy 
(50 + 49 + 45) /150
[1] 0.96
#install.packages('caret')
#install.packages('e1071')
library(caret)
cm <- confusionMatrix(tb)
cm
Confusion Matrix and Statistics

            
predicted    setosa versicolor virginica
  setosa         50          0         0
  versicolor      0         49         5
  virginica       0          1        45

Overall Statistics
                                         
               Accuracy : 0.96           
                 95% CI : (0.915, 0.9852)
    No Information Rate : 0.3333         
    P-Value [Acc > NIR] : < 2.2e-16      
                                         
                  Kappa : 0.94           
 Mcnemar's Test P-Value : NA             

Statistics by Class:

                     Class: setosa Class: versicolor
Sensitivity                 1.0000            0.9800
Specificity                 1.0000            0.9500
Pos Pred Value              1.0000            0.9074
Neg Pred Value              1.0000            0.9896
Prevalence                  0.3333            0.3333
Detection Rate              0.3333            0.3267
Detection Prevalence        0.3333            0.3600
Balanced Accuracy           1.0000            0.9650
                     Class: virginica
Sensitivity                    0.9000
Specificity                    0.9900
Pos Pred Value                 0.9783
Neg Pred Value                 0.9519
Prevalence                     0.3333
Detection Rate                 0.3000
Detection Prevalence           0.3067
Balanced Accuracy              0.9450
# data sampling
set.seed(123)
sample.int(42, 6)
[1] 13 33 17 35 36  2
sample.int(42, 6)
[1] 23 37 42 18 41 17
sample.int(42, 6)
[1] 29 24  5 36 10  2
a  <- c(1,2,3,4,5)
ix <- c(1,0,1,0,1)
a[ix == 1]
[1] 1 3 5
# split data into trainset and testset
nrow(iris)
[1] 150
set.seed(123)
idx <- sample.int(2, nrow(iris), replace = TRUE, prob=c(0.7,0.3))
trainset <- iris[idx == 1, ]
testset  <- iris[idx == 2, ]
# build model
dim(trainset)
[1] 106   5
dim(testset)
[1] 44  5
fit <- rpart(Species ~ ., data = trainset)
fit
n= 106 

node), split, n, loss, yval, (yprob)
      * denotes terminal node

1) root 106 70 versicolor (0.33018868 0.33962264 0.33018868)  
  2) Petal.Length< 2.45 35  0 setosa (1.00000000 0.00000000 0.00000000) *
  3) Petal.Length>=2.45 71 35 versicolor (0.00000000 0.50704225 0.49295775)  
    6) Petal.Length< 4.75 34  0 versicolor (0.00000000 1.00000000 0.00000000) *
    7) Petal.Length>=4.75 37  2 virginica (0.00000000 0.05405405 0.94594595) *
plot(fit, margin = 0.1)
text(fit)

# apply model on testset
predicted <- predict(fit, testset, type="class")
tb <- table(testset$Species, predicted)
cm <- confusionMatrix(tb)
cm
Confusion Matrix and Statistics

            predicted
             setosa versicolor virginica
  setosa         15          0         0
  versicolor      0         10         4
  virginica       0          1        14

Overall Statistics
                                          
               Accuracy : 0.8864          
                 95% CI : (0.7544, 0.9621)
    No Information Rate : 0.4091          
    P-Value [Acc > NIR] : 6.207e-11       
                                          
                  Kappa : 0.8291          
 Mcnemar's Test P-Value : NA              

Statistics by Class:

                     Class: setosa Class: versicolor
Sensitivity                 1.0000            0.9091
Specificity                 1.0000            0.8788
Pos Pred Value              1.0000            0.7143
Neg Pred Value              1.0000            0.9667
Prevalence                  0.3409            0.2500
Detection Rate              0.3409            0.2273
Detection Prevalence        0.3409            0.3182
Balanced Accuracy           1.0000            0.8939
                     Class: virginica
Sensitivity                    0.7778
Specificity                    0.9615
Pos Pred Value                 0.9333
Neg Pred Value                 0.8621
Prevalence                     0.4091
Detection Rate                 0.3182
Detection Prevalence           0.3409
Balanced Accuracy              0.8697
# apply model on trainset
predicted2 <- predict(fit, trainset, type="class")
tb2 <- table(trainset$Species, predicted2)
cm2 <- confusionMatrix(tb2)
cm2
Confusion Matrix and Statistics

            predicted2
             setosa versicolor virginica
  setosa         35          0         0
  versicolor      0         34         2
  virginica       0          0        35

Overall Statistics
                                          
               Accuracy : 0.9811          
                 95% CI : (0.9335, 0.9977)
    No Information Rate : 0.3491          
    P-Value [Acc > NIR] : < 2.2e-16       
                                          
                  Kappa : 0.9717          
 Mcnemar's Test P-Value : NA              

Statistics by Class:

                     Class: setosa Class: versicolor
Sensitivity                 1.0000            1.0000
Specificity                 1.0000            0.9722
Pos Pred Value              1.0000            0.9444
Neg Pred Value              1.0000            1.0000
Prevalence                  0.3302            0.3208
Detection Rate              0.3302            0.3208
Detection Prevalence        0.3302            0.3396
Balanced Accuracy           1.0000            0.9861
                     Class: virginica
Sensitivity                    0.9459
Specificity                    1.0000
Pos Pred Value                 1.0000
Neg Pred Value                 0.9718
Prevalence                     0.3491
Detection Rate                 0.3302
Detection Prevalence           0.3302
Balanced Accuracy              0.9730

Churn rate analysis

#install.packages('C50')
library(C50)
data(churn)
#churnTrain
churnTrain <- churnTrain[,!names(churnTrain)%in%c("state", "area_code", "account_length")]
set.seed(2)
idx <- sample.int(2,nrow(churnTrain), replace = TRUE, prob = c(0.7, 0.3))
trainset <- churnTrain[idx == 1, ]
testset  <- churnTrain[idx == 2, ]
churn.rp <- rpart(churn ~., data = trainset)
plot(churn.rp, margin=0.1)
text(churn.rp)

Verify Classfication Result

mgraph(L,graph="IMP",leg=names(trainset),col="gray",Grid=10)
Error: could not find function "mgraph"

Distance

x <- c(0, 0, 1, 1, 1, 1)
y <- c(1, 0, 1, 1, 0, 1)
# euclidean
sqrt(sum((x - y) ^ 2))
[1] 1.414214
dist(rbind(x,y), method = 'euclidean')
         x
y 1.414214
# manhattan
sum(abs(x - y))
[1] 2
dist(rbind(x,y), method = 'manhattan')
  x
y 2

Clustering

data(iris)
dist.iris <- dist(iris[,-5], method='euclidean')
hc <- hclust(dist.iris, method = "ward.D2")
plot(hc)

fit <- cutree(hc, k = 3)
fit
  [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [30] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2
 [59] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 2 2 2 2 2 2 2 2 2
 [88] 2 2 2 2 2 2 2 2 2 2 2 2 2 3 2 3 3 3 3 2 3 3 3 3 3 3 2 2 3
[117] 3 3 3 2 3 2 3 2 3 3 2 2 3 3 3 3 3 2 2 3 3 3 2 3 3 3 2 3 3
[146] 3 2 3 3 2
plot(hc, hang =-0.01, cex=0.7)
rect.hclust(hc, k =3, border="red")
fit <- cutree(hc, k = 3)
fit
  [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [30] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2
 [59] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 2 2 2 2 2 2 2 2 2
 [88] 2 2 2 2 2 2 2 2 2 2 2 2 2 3 2 3 3 3 3 2 3 3 3 3 3 3 2 2 3
[117] 3 3 3 2 3 2 3 2 3 3 2 2 3 3 3 3 3 2 2 3 3 3 2 3 3 3 2 3 3
[146] 3 2 3 3 2
par(mfrow=c(1,2))

plot(iris$Petal.Length, iris$Petal.Width, col =iris$Species, main = 'with species')
plot(iris$Petal.Length, iris$Petal.Width, col =fit, main= 'clustering result')

Article Clustering

a <- c(1, 2, 2, 1, 1, 1, 0)
b <- c(1, 2, 2, 1, 1, 2, 1)

sum(a * b ) /( sqrt(sum(a^ 2)) *  sqrt(sum(b^ 2)))
#install.packages('proxy')
1 - proxy::dist(rbind(a,b), method="cosine")

# Download and load data
download.file('https://raw.githubusercontent.com/ywchiu/rtibame/master/History/Class1/news_big5.RData', 'new.RData')
load('new.RData')

# Data Preprocessing
names(news)  <- c('title', 'content', 'articleid')
news$title   <- as.character(news$title)
news$content <- as.character(news$content)

library(jiebaR)
mixseg <- worker()
news.seg <- lapply(news$content, function(e)segment(e, mixseg))
source('https://raw.githubusercontent.com/ywchiu/rtibame/master/Lib/CNCorpus.R')

corpus=CNCorpus(news.seg)
control.list=list(wordLengths=c(2,Inf),tokenize=space_tokenizer)
doc  <- tm_map(corpus, removeNumbers)
dtm  <- DocumentTermMatrix(doc, control<-control.list)
dtm.remove <- removeSparseTerms(dtm, 0.99)
dtm.remove
#dtm$dimnames$Terms


dtm.dist <- proxy::dist(as.matrix(dtm.remove), method ="cosine")
dtm.mat  <- as.matrix(dtm.dist)
idx <- order(dtm.mat[127,])[1:10]
cbind(news$title[idx], dtm.mat[127,idx])



# article clustering
dtm.cluster <- hclust(dtm.dist, method = "ward.D2")
plot(dtm.cluster, hang=-1)
fit <- cutree(dtm.cluster, k = 6)

news$title[fit ==5]
LS0tDQp0aXRsZTogIkRlbW8yMDE2MTIxNyINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQojI+S9nOalreS6lA0KDQpgYGB7cn0NCmRvd25sb2FkLmZpbGUoJ2h0dHBzOi8vZ2l0aHViLmNvbS95d2NoaXUvcnRpYmFtZS9yYXcvbWFzdGVyL0RhdGEvb25lZGF5Mi5jc3YnLCAnb25lZGF5LmNzdicpIA0Kb25lZGF5IDwtIHJlYWQuY3N2KCdvbmVkYXkuY3N2Jywgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KaGVhZChvbmVkYXkpDQpzdHIob25lZGF5KQ0KDQpsaWJyYXJ5KGppZWJhUikNCm1peHNlZyA8LSB3b3JrZXIoKQ0Kb25lZGF5LnNlZyA8LSBzZWdtZW50KG9uZWRheSRjb250ZW50LCBtaXhzZWcpDQp0YiA8LSB0YWJsZShvbmVkYXkuc2VnKQ0KdGIgPC0gdGJbKG5jaGFyKG5hbWVzKHRiKSkgPj0gMikgJiAodGIgPj0gMTApICYgZ3JlcGwoJ1tcdTRlMDAtXHU5ZmE1XSsnLCB4ID0gbmFtZXModGIpKV0NCnRiDQoNCmxpYnJhcnkod29yZGNsb3VkMikNCndvcmRjbG91ZDIodGIsIHNoYXBlID0gInN0YXIiKQ0KDQpgYGANCg0KDQojIyBpcmlzIGNsYXNzZmljYXRpb24NCmBgYHtyfQ0KZGF0YShpcmlzKQ0KI2lyaXMNCg0KbGlicmFyeShycGFydCkNCmZpdCA8LSBycGFydCggU3BlY2llcyAgfiBTZXBhbC5MZW5ndGggK1NlcGFsLldpZHRoICsgUGV0YWwuTGVuZ3RoICtQZXRhbC5XaWR0aCwgICBkYXRhID0gaXJpcyApDQpmaXQNCnBsb3QoZml0LCBtYXJnaW49MC4xKQ0KdGV4dChmaXQpDQoNCnBsb3QoaXJpcyRQZXRhbC5MZW5ndGgsIGlyaXMkUGV0YWwuV2lkdGgsIGNvbCA9IGlyaXMkU3BlY2llcykNCmFibGluZSh2ID0gMi40NSwgY29sPSJvcmFuZ2UiKQ0KYWJsaW5lKGggPSAxLjc1LCBjb2w9ImJsdWUiKQ0KDQoNCnByZWRpY3QoZml0LCBkYXRhLmZyYW1lKFBldGFsLkxlbmd0aCA9IDIsIFBldGFsLldpZHRoPSAzLCBTZXBhbC5MZW5ndGggPSAyLCBTZXBhbC5XaWR0aCA9IDIpKQ0KDQojcHJlZGljdChmaXQsIGlyaXMpDQpwcmVkaWN0ZWQgPC0gcHJlZGljdChmaXQsIGlyaXMsIHR5cGU9ICdjbGFzcycpDQp0YiA8LSB0YWJsZShwcmVkaWN0ZWQsaXJpcyRTcGVjaWVzKQ0KDQojIGFjY3VyYWN5IA0KKDUwICsgNDkgKyA0NSkgLzE1MA0KDQojaW5zdGFsbC5wYWNrYWdlcygnY2FyZXQnKQ0KI2luc3RhbGwucGFja2FnZXMoJ2UxMDcxJykNCmxpYnJhcnkoY2FyZXQpDQpjbSA8LSBjb25mdXNpb25NYXRyaXgodGIpDQpjbQ0KDQojIGRhdGEgc2FtcGxpbmcNCnNldC5zZWVkKDEyMykNCnNhbXBsZS5pbnQoNDIsIDYpDQpzYW1wbGUuaW50KDQyLCA2KQ0Kc2FtcGxlLmludCg0MiwgNikNCg0KYSAgPC0gYygxLDIsMyw0LDUpDQppeCA8LSBjKDEsMCwxLDAsMSkNCmFbaXggPT0gMV0NCg0KIyBzcGxpdCBkYXRhIGludG8gdHJhaW5zZXQgYW5kIHRlc3RzZXQNCm5yb3coaXJpcykNCnNldC5zZWVkKDEyMykNCmlkeCA8LSBzYW1wbGUuaW50KDIsIG5yb3coaXJpcyksIHJlcGxhY2UgPSBUUlVFLCBwcm9iPWMoMC43LDAuMykpDQp0cmFpbnNldCA8LSBpcmlzW2lkeCA9PSAxLCBdDQp0ZXN0c2V0ICA8LSBpcmlzW2lkeCA9PSAyLCBdDQoNCiMgYnVpbGQgbW9kZWwNCmRpbSh0cmFpbnNldCkNCmRpbSh0ZXN0c2V0KQ0KDQpmaXQgPC0gcnBhcnQoU3BlY2llcyB+IC4sIGRhdGEgPSB0cmFpbnNldCkNCmZpdA0KcGxvdChmaXQsIG1hcmdpbiA9IDAuMSkNCnRleHQoZml0KQ0KDQojIGFwcGx5IG1vZGVsIG9uIHRlc3RzZXQNCnByZWRpY3RlZCA8LSBwcmVkaWN0KGZpdCwgdGVzdHNldCwgdHlwZT0iY2xhc3MiKQ0KdGIgPC0gdGFibGUodGVzdHNldCRTcGVjaWVzLCBwcmVkaWN0ZWQpDQpjbSA8LSBjb25mdXNpb25NYXRyaXgodGIpDQpjbQ0KDQojIGFwcGx5IG1vZGVsIG9uIHRyYWluc2V0DQpwcmVkaWN0ZWQyIDwtIHByZWRpY3QoZml0LCB0cmFpbnNldCwgdHlwZT0iY2xhc3MiKQ0KdGIyIDwtIHRhYmxlKHRyYWluc2V0JFNwZWNpZXMsIHByZWRpY3RlZDIpDQpjbTIgPC0gY29uZnVzaW9uTWF0cml4KHRiMikNCmNtMg0KYGBgDQoNCiMjIENodXJuIHJhdGUgYW5hbHlzaXMNCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoJ0M1MCcpDQpsaWJyYXJ5KEM1MCkNCmRhdGEoY2h1cm4pDQoNCiNjaHVyblRyYWluDQoNCmNodXJuVHJhaW4gPC0gY2h1cm5UcmFpblssIW5hbWVzKGNodXJuVHJhaW4pJWluJWMoInN0YXRlIiwgImFyZWFfY29kZSIsICJhY2NvdW50X2xlbmd0aCIpXQ0KDQpzZXQuc2VlZCgyKQ0KaWR4IDwtIHNhbXBsZS5pbnQoMixucm93KGNodXJuVHJhaW4pLCByZXBsYWNlID0gVFJVRSwgcHJvYiA9IGMoMC43LCAwLjMpKQ0KdHJhaW5zZXQgPC0gY2h1cm5UcmFpbltpZHggPT0gMSwgXQ0KdGVzdHNldCAgPC0gY2h1cm5UcmFpbltpZHggPT0gMiwgXQ0KDQpjaHVybi5ycCA8LSBycGFydChjaHVybiB+LiwgZGF0YSA9IHRyYWluc2V0KQ0KcGxvdChjaHVybi5ycCwgbWFyZ2luPTAuMSkNCnRleHQoY2h1cm4ucnApDQoNCg0KYGBgDQojIyBWZXJpZnkgQ2xhc3NmaWNhdGlvbiBSZXN1bHQNCmBgYHtyfQ0KcHJlZGljdGVkIDwtIHByZWRpY3QoY2h1cm4ucnAsIHRlc3RzZXQsIHR5cGUgPSJjbGFzcyIpDQp0YiA8LSB0YWJsZShwcmVkaWN0ZWQsIHRlc3RzZXQkY2h1cm4pDQpkaW0odGVzdHNldCkNCg0KKDg1OSArIDEwMCApLzEwMTgNCmNvbmZ1c2lvbk1hdHJpeCh0YikNCg0KdGFibGUoIHRlc3RzZXQkY2h1cm4pDQo4NzcvMTAxOA0KDQoNCiMgc2VwYXJhdGUgZGF0YSBpbnRvIDEwIGZvbGRzDQppZHggPC0gc2FtcGxlLmludCgxMCwgbnJvdyh0cmFpbnNldCksIHJlcGxhY2UgPVRSVUUpDQpmb3IoaSBpbiBzZXEoMSwxMCkpew0KICB0cmFpbl9kYXRhIDwtIHRyYWluc2V0W2lkeCAhPSBpLCBdDQogIHRlc3RfZGF0YSAgPC0gdHJhaW5zZXRbaWR4ID09IGksIF0NCiAgZml0IDwtIHJwYXJ0KGNodXJuIH4uLCB0cmFpbl9kYXRhKQ0KICBwcmVkaWN0ZWQgPC0gcHJlZGljdChmaXQsIHRlc3RfZGF0YSx0eXBlID0gImNsYXNzIikNCiAgdGIgPC0gdGFibGUocHJlZGljdGVkLCB0ZXN0X2RhdGEkY2h1cm4pDQogIGNtIDwtIGNvbmZ1c2lvbk1hdHJpeCh0YikNCiAgYWNjdXJhY3kgPC0gY20kb3ZlcmFsbFsxXQ0KICBwcmludChhY2N1cmFjeSkNCn0NCg0KIyB1c2UgY2FyZXQgdG8gYnVpbGQgbWFjaGluZSBsZWFybmluZyBtb2RlbA0KbGlicmFyeShjYXJldCkNCmNvbnRyb2wgPC0gdHJhaW5Db250cm9sKG1ldGhvZD0icmVwZWF0ZWRjdiIsIG51bWJlcj0xMCwgcmVwZWF0cz0zKQ0KbW9kZWwgPC0gdHJhaW4oY2h1cm5+LiwgZGF0YT10cmFpbnNldCwgbWV0aG9kPSJycGFydCIsIHByZVByb2Nlc3M9InNjYWxlIiwgdHJDb250cm9sPWNvbnRyb2wpDQptb2RlbA0KDQoNCiMgY3JlYXRlIGEgUk9DIGN1cnZlDQpmaXQgICAgICAgPC0gcnBhcnQoY2h1cm4gfi4sIHRyYWluc2V0KQ0KcHJlZGljdGVkIDwtIHByZWRpY3QoZml0LCB0ZXN0c2V0KQ0KDQpST0MgPC0gZGF0YS5mcmFtZSgpDQpmb3IgKGMgaW4gc2VxKDAsMSwwLjEpKSB7DQogIHByZWRpY3Rpb25zIDwtIGlmZWxzZShwcmVkaWN0ZWRbLDJdID49IGMsIDEsIDApDQogIHByZWRpY3Rpb25zIDwtIGFzLmZhY3RvcihwcmVkaWN0aW9ucykNCiAgbGV2ZWxzKHByZWRpY3Rpb25zKSA8LSBjKCd5ZXMnLCAnbm8nKQ0KICBwcmVkaWN0aW9ucyA8LSBhcy5mYWN0b3IocHJlZGljdGlvbnMpDQogIHRiICAgICAgICA8LSB0YWJsZShwcmVkaWN0aW9ucywgdGVzdHNldCRjaHVybikNCiAgY20gICAgICAgIDwtIGNvbmZ1c2lvbk1hdHJpeCh0YikNCiAgVFBSIDwtIGNtJGJ5Q2xhc3NbMV0NCiAgRlBSIDwtIDEtIGNtJGJ5Q2xhc3NbMl0NCiAgUk9DIDwtIHJiaW5kKFJPQywgZGF0YS5mcmFtZShUUFIsIEZQUikpDQp9DQoNCnBsb3QoVFBSIH4gRlBSLCBkYXRhPVJPQ1syOm5yb3coUk9DKSxdLCB0eXBlPSAnbCcsIGNvbD0icmVkIikNCg0KDQoNCiMgdXNlIFJPQ1INCiNpbnN0YWxsLnBhY2thZ2VzKCJST0NSIikNCmxpYnJhcnkoUk9DUikNCnByZWRpY3Rpb25zIDwtcHJlZGljdChjaHVybi5ycCwgdGVzdHNldCwgdHlwZT0icHJvYiIpDQpwcmVkLnRvLnJvYzwtcHJlZGljdGlvbnNbLCAxXQ0KcHJlZC5yb2NyPC1wcmVkaWN0aW9uKHByZWQudG8ucm9jLCBhcy5mYWN0b3IodGVzdHNldFssKGRpbSh0ZXN0c2V0KVtbMl1dKV0pKQ0KDQpwZXJmLnJvY3I8LXBlcmZvcm1hbmNlKHByZWQucm9jciwgbWVhc3VyZSA9ImF1YyIsIHgubWVhc3VyZT0iY3V0b2ZmIikNCnBlcmYudHByLnJvY3I8LXBlcmZvcm1hbmNlKHByZWQucm9jciwgInRwciIsImZwciIpDQoNCnBsb3QocGVyZi50cHIucm9jcixjb2xvcml6ZT1ULG1haW49cGFzdGUoIkFVQzoiLChwZXJmLnJvY3JAeS52YWx1ZXMpKSkNCg0KDQoNCiNpbnN0YWxsLnBhY2thZ2VzKCJybWluZXIiKQ0KI2luc3RhbGwucGFja2FnZXMoInhnYm9vc3QiKQ0KI2xpYnJhcnkocm1pbmVyKQ0KI21vZGVsIDwtIGZpdChjaHVybn4uLHRyYWluc2V0LG1vZGVsPSJycGFydCIpDQojVmFyaWFibGVJbXBvcnRhbmNlIDwtIEltcG9ydGFuY2UobW9kZWwsdHJhaW5zZXQsbWV0aG9kPSJzZW5zdiIpDQojTD1saXN0KHJ1bnM9MSxzZW49dChWYXJpYWJsZUltcG9ydGFuY2UkaW1wKSxzcmVzcG9uc2VzPVZhcmlhYmxlSW1wb3J0YW5jZSRzcmVzcG9uc2VzKQ0KI21ncmFwaChMLGdyYXBoPSJJTVAiLGxlZz1uYW1lcyh0cmFpbnNldCksY29sPSJncmF5IixHcmlkPTEwKQ0KYGBgDQojIyBEaXN0YW5jZQ0KYGBge3J9DQp4IDwtIGMoMCwgMCwgMSwgMSwgMSwgMSkNCnkgPC0gYygxLCAwLCAxLCAxLCAwLCAxKQ0KIyBldWNsaWRlYW4NCnNxcnQoc3VtKCh4IC0geSkgXiAyKSkNCmRpc3QocmJpbmQoeCx5KSwgbWV0aG9kID0gJ2V1Y2xpZGVhbicpDQoNCiMgbWFuaGF0dGFuDQpzdW0oYWJzKHggLSB5KSkNCmRpc3QocmJpbmQoeCx5KSwgbWV0aG9kID0gJ21hbmhhdHRhbicpDQoNCg0KYGBgDQoNCiNDbHVzdGVyaW5nDQoNCmBgYHtyfQ0KZGF0YShpcmlzKQ0KZGlzdC5pcmlzIDwtIGRpc3QoaXJpc1ssLTVdLCBtZXRob2Q9J2V1Y2xpZGVhbicpDQpoYyA8LSBoY2x1c3QoZGlzdC5pcmlzLCBtZXRob2QgPSAid2FyZC5EMiIpDQpwbG90KGhjKQ0KDQpmaXQgPC0gY3V0cmVlKGhjLCBrID0gMykNCmZpdA0KDQpwbG90KGhjLCBoYW5nID0tMC4wMSwgY2V4PTAuNykNCnJlY3QuaGNsdXN0KGhjLCBrID0zLCBib3JkZXI9InJlZCIpDQoNCg0KZml0IDwtIGN1dHJlZShoYywgayA9IDMpDQpmaXQNCg0KcGFyKG1mcm93PWMoMSwyKSkNCnBsb3QoaXJpcyRQZXRhbC5MZW5ndGgsIGlyaXMkUGV0YWwuV2lkdGgsIGNvbCA9aXJpcyRTcGVjaWVzLCBtYWluID0gJ3dpdGggc3BlY2llcycpDQpwbG90KGlyaXMkUGV0YWwuTGVuZ3RoLCBpcmlzJFBldGFsLldpZHRoLCBjb2wgPWZpdCwgbWFpbj0gJ2NsdXN0ZXJpbmcgcmVzdWx0JykNCg0KDQpgYGANCiMjIEFydGljbGUgQ2x1c3RlcmluZw0KYGBgDQphIDwtIGMoMSwgMiwgMiwgMSwgMSwgMSwgMCkNCmIgPC0gYygxLCAyLCAyLCAxLCAxLCAyLCAxKQ0KDQpzdW0oYSAqIGIgKSAvKCBzcXJ0KHN1bShhXiAyKSkgKiAgc3FydChzdW0oYl4gMikpKQ0KI2luc3RhbGwucGFja2FnZXMoJ3Byb3h5JykNCjEgLSBwcm94eTo6ZGlzdChyYmluZChhLGIpLCBtZXRob2Q9ImNvc2luZSIpDQoNCiMgRG93bmxvYWQgYW5kIGxvYWQgZGF0YQ0KZG93bmxvYWQuZmlsZSgnaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3l3Y2hpdS9ydGliYW1lL21hc3Rlci9IaXN0b3J5L0NsYXNzMS9uZXdzX2JpZzUuUkRhdGEnLCAnbmV3LlJEYXRhJykNCmxvYWQoJ25ldy5SRGF0YScpDQoNCiMgRGF0YSBQcmVwcm9jZXNzaW5nDQpuYW1lcyhuZXdzKSAgPC0gYygndGl0bGUnLCAnY29udGVudCcsICdhcnRpY2xlaWQnKQ0KbmV3cyR0aXRsZSAgIDwtIGFzLmNoYXJhY3RlcihuZXdzJHRpdGxlKQ0KbmV3cyRjb250ZW50IDwtIGFzLmNoYXJhY3RlcihuZXdzJGNvbnRlbnQpDQoNCmxpYnJhcnkoamllYmFSKQ0KbWl4c2VnIDwtIHdvcmtlcigpDQpuZXdzLnNlZyA8LSBsYXBwbHkobmV3cyRjb250ZW50LCBmdW5jdGlvbihlKXNlZ21lbnQoZSwgbWl4c2VnKSkNCnNvdXJjZSgnaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3l3Y2hpdS9ydGliYW1lL21hc3Rlci9MaWIvQ05Db3JwdXMuUicpDQoNCmNvcnB1cz1DTkNvcnB1cyhuZXdzLnNlZykNCmNvbnRyb2wubGlzdD1saXN0KHdvcmRMZW5ndGhzPWMoMixJbmYpLHRva2VuaXplPXNwYWNlX3Rva2VuaXplcikNCmRvYyAgPC0gdG1fbWFwKGNvcnB1cywgcmVtb3ZlTnVtYmVycykNCmR0bSAgPC0gRG9jdW1lbnRUZXJtTWF0cml4KGRvYywgY29udHJvbDwtY29udHJvbC5saXN0KQ0KZHRtLnJlbW92ZSA8LSByZW1vdmVTcGFyc2VUZXJtcyhkdG0sIDAuOTkpDQpkdG0ucmVtb3ZlDQojZHRtJGRpbW5hbWVzJFRlcm1zDQoNCg0KZHRtLmRpc3QgPC0gcHJveHk6OmRpc3QoYXMubWF0cml4KGR0bS5yZW1vdmUpLCBtZXRob2QgPSJjb3NpbmUiKQ0KZHRtLm1hdCAgPC0gYXMubWF0cml4KGR0bS5kaXN0KQ0KaWR4IDwtIG9yZGVyKGR0bS5tYXRbMTI3LF0pWzE6MTBdDQpjYmluZChuZXdzJHRpdGxlW2lkeF0sIGR0bS5tYXRbMTI3LGlkeF0pDQoNCg0KDQojIGFydGljbGUgY2x1c3RlcmluZw0KZHRtLmNsdXN0ZXIgPC0gaGNsdXN0KGR0bS5kaXN0LCBtZXRob2QgPSAid2FyZC5EMiIpDQpwbG90KGR0bS5jbHVzdGVyLCBoYW5nPS0xKQ0KZml0IDwtIGN1dHJlZShkdG0uY2x1c3RlciwgayA9IDYpDQoNCm5ld3MkdGl0bGVbZml0ID09NV0NCg0KYGBgDQoNCg0KDQo=