These packages are needed for this assignment:

pkg <- c("ggplot2", "RColorBrewer")
new.pkg <- pkg[!(pkg %in% installed.packages())]
if (length(new.pkg)) {
  install.packages(new.pkg)  
}

Let us first make sure we are working in a directory we feel comfortable with. So I will start by changing my working directory to the one listed below

Let us now read the file and start exploring the dataset memproc.

mydata1<- read.csv("data/memproc.csv", header=T)
summary(mydata1)
     host                proc              mem             state          
 Length:247         Min.   :-3.1517   Min.   :-3.5939   Length:247        
 Class :character   1st Qu.:-1.2056   1st Qu.:-1.4202   Class :character  
 Mode  :character   Median :-0.4484   Median :-0.6212   Mode  :character  
                    Mean   :-0.4287   Mean   :-0.5181                     
                    3rd Qu.: 0.3689   3rd Qu.: 0.2413                     
                    Max.   : 3.1428   Max.   : 3.2184                     
mydata1

In order to explore this dataset more in detail, let us create a plot to compare the processor and memory usage, and differentiate it based on the malware state.

library(ggplot2)
gg <- ggplot(mydata1, aes(proc, mem, color=state))
gg <- gg + scale_color_brewer(palette="Set2")
gg <- gg + geom_point(size=3) + theme_bw()
print(gg)

I suggest you to create now a new graph but this time including the title in the chart. The title would be “Memory vs Processor Usage as function of the Malaware state”. # Added a title by using + ggtitle(“Memory vs Processor Usage as function of the Malaware state”)) to the print: # print(gg + ggtitle(“Memory vs Processor Usage as function of the Malaware state”))

library(ggplot2)
gg <- ggplot(mydata1, aes(proc, mem, color=state))
gg <- gg + scale_color_brewer(palette="Set2")
gg <- gg + geom_point(size=3) + theme_bw()
print(gg + ggtitle("Memory vs Processor Usage as function of the Malaware state"))

Going to take 1/3 of the data for test data and the train the algorithm

on the remaining 2/3.

set.seed(1492)
# count how many in the overall sample
n <- nrow(mydata1)
# set the test.size to be 1/3rd
test.size <- as.integer(n/3)
# randomly sample the rows for test set
testset <- sample(n, test.size)
# now split the data into test and train
test <- mydata1[testset, ]
train <- mydata1[-testset, ]

Splitting the data into test and train

# pull out proc and mem columns for infected then normal
# then use colMeans() to means of the columns
inf <- colMeans(train[train$state=="Infected", c("proc", "mem")])
nrm <- colMeans(train[train$state=="Normal", c("proc", "mem")])

Display the results of the on the two columns and return vector with

two elements.

print(inf)
     proc       mem 
0.9354513 1.0868010 
print(nrm)
      proc        mem 
-0.7907962 -0.9352974 

Predicting malware predict.malware take in a single named vector called

data, extract out the proc and mem values, then calculate how far those are from the means

tat was generated during the training. Calculate the distance by using the

Pythagorean theorem a^2 + b^2 = c^2. Once the difference between the trained proc mean

and test proc mean and the trained mem mean and the test mem mean is calculated we just’

then just compare them.

predict.malware <- function(data) {
  # get 'proc' and 'mem' as numeric values
  proc <- as.numeric(data[['proc']])
  mem <- as.numeric(data[['mem']])
  # set up infected comparison
  inf.a <- inf['proc'] - proc
  inf.b <- inf['mem'] - mem
  # pythagorean distance c = sqrt(a^2 + b^2)
  inf.dist <- sqrt(inf.a^2 + inf.b^2)
  # repeat for normal systems
  nrm.a <- nrm['proc'] - proc
  nrm.b <- nrm['mem'] - mem
  nrm.dist <- sqrt(nrm.a^2 + nrm.b^2)
  # assign a label of the closest (smallest)
  ifelse(inf.dist<nrm.dist,"Infected", "Normal")
}
# could test with these if you uncomment them
# predict.malware(inf['proc'], inf['mem'])
# expect "Infected" 
# predict.malware(nrm['proc'], nrm['mem'])
# expect "Normal"

requires the values from test test <- memproc[testset, ] and predict.malware value

calculated earlier.

prediction <- apply(test, 1, predict.malware)

Now have a set of predictions and the ability to compare

them to the real values to determine how well it did, look at

the proportion of correctly predicted results on the test data.

Then calculate that by taking the number of correct predictions

(where the real test$state and the predicted prediction match) and then

dividing that by the total number of predictions.

This simple algorithgm predicted 90% of the values correctly.

sum(test$state==prediction)/nrow(test)
[1] 0.902439
# Figure 9-2 #########################################################
slope <- -1*(1/((inf['mem']-nrm['mem'])/(inf['proc']-nrm['proc'])))
intercept <- mean(c(inf['mem'], nrm['mem'])) - (slope*mean(c(inf['proc'], nrm['proc'])))
result <- cbind(test, predict=prediction)
result$Accurate <- ifelse(result$state==result$predict, "Yes", "No")
result$Accurate <- factor(result$Accurate, levels=c("Yes", "No"), ordered=T)

This classifier creates a line halfway between the two means and perpendicular to an intersecting line. Anything above the line is predicted as infected; anything below is predicted to be normal.

The misclassified values are clearly marked. There are normal system above the line that are mislabeled as well as the infected systems below the line.

gg <- ggplot(result, aes(proc, mem, color=state, size=Accurate, shape=Accurate))
gg <- gg + scale_shape_manual(values=c(16, 8))
gg <- gg + scale_size_manual(values=c(3, 6))
gg <- gg + scale_color_brewer(palette="Set2")
gg <- gg + geom_point() + theme_bw()
gg <- gg + geom_abline(intercept = intercept, slope = slope, color="gray80")
print(gg)

Here we going to plot linear regression on non-linear data, this happens because

part of the linear regression is a reference to the linear coefficients estimated

not the data.

set.seed(1)
x <- runif(200, min=-10, max=10)
y <- 1.377*(x^3) + 0.92*(x^2) + .3*x + rnorm(200, sd=250) + 1572
x <- x + 10
smooth <- ggplot(data.frame(x,y), aes(x, y)) + geom_point() + 
  geom_smooth(method = "lm", formula = y ~ poly(x, 3), size = 1, se=F) + 
  theme_bw()
print(smooth)

memproc <- read.csv("data/memproc.csv", header=T)
memproc$infected <- ifelse(memproc$state=="Infected", 1, 0)
set.seed(1492)
n <- nrow(memproc)
test.size <- as.integer(n/3)
testset <- sample(n, test.size)
test <- memproc[testset, ]
train <- memproc[-testset, ]

The plots are showing linear regression on infection test data

Output on the x-axis is an estimated probability of a host being infected

based on the input variables. This plotted against the known values in the test

data on the y-axis(real life).

glm() function is one of serveral approaches to logistic regression. This function

can handle most situations.

glm.out = glm(infected ~ proc + mem, data=test, family=binomial(logit))
summary(glm.out)

Call:
glm(formula = infected ~ proc + mem, family = binomial(logit), 
    data = test)

Deviance Residuals: 
     Min        1Q    Median        3Q       Max  
-1.51110  -0.17718  -0.08015  -0.01132   2.28073  

Coefficients:
            Estimate Std. Error z value Pr(>|z|)   
(Intercept)  -2.1905     0.6943  -3.155  0.00160 **
proc          2.1378     0.7192   2.972  0.00295 **
mem           1.6530     0.5682   2.909  0.00362 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 68.275  on 81  degrees of freedom
Residual deviance: 25.348  on 79  degrees of freedom
AIC: 31.348

Number of Fisher Scoring iterations: 8
modelog <- predict.glm(glm.out, test, type="response")
gg <- ggplot(data.frame(x=modelog, y=ifelse(test$infected>0.5, "Yes", "No")), aes(x, y)) +
  geom_point(size=3, fill="steelblue", color="black", shape=4) + 
  ylab("Known Infected Host") +
  xlab("Estimated Probability of Infected Host") + theme_bw()
print(gg)

Creating a diagram with same data with multiple differenct k-values

kmeans() function will perform k-means clustering.

The first diagram is k-means with 3 clusters.

The second diagram is k-means with 4 clusters.

The third diagram is k-means with 5 clusters.

The fourth diagram is k-means with 6 clusters.

set.seed(1) # repeatable
x <- c(rnorm(200), rnorm(400)+2, rnorm(400)-2)
y <- c(rnorm(200), rnorm(200)+2, rnorm(200)-2, rnorm(200)+2, rnorm(200)-2)
randata <- data.frame(x=x, y=y)
out <- list()
for(i in c(3,4,5,6)) {
  km <- kmeans(randata, i)
  centers <- data.frame(x=km$centers[ ,1], y=km$centers[ ,2], cluster=1)
  randata$cluster <- factor(km$cluster)
  gg <- ggplot(randata, aes(x, y, color=cluster)) + geom_point(size=2)
  gg <- gg + geom_point(data=centers, aes(x, y), shape=8, color="black", size=4)
  gg <- gg + scale_x_continuous(expand=c(0,0.1))
  gg <- gg + scale_y_continuous(expand=c(0,0.1))
  gg <- gg + ggtitle(paste("k-means with", i, "clusters"))
  gg <- gg + theme(panel.grid = element_blank(),
                   panel.background = element_rect(colour = "black", fill=NA),
                   axis.text = element_blank(),
                   axis.title = element_blank(),
                   legend.position = "none",
                   axis.ticks = element_blank())
  out[[i-2]] <- gg
}
print(out[[1]])

print(out[[2]])

print(out[[3]])

print(out[[4]])

LS0tDQp0aXRsZTogIk1hY2hpbmVfTGVhcm5pbmciDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KVGhlc2UgcGFja2FnZXMgYXJlIG5lZWRlZCBmb3IgdGhpcyBhc3NpZ25tZW50Og0KDQpgYGB7cn0NCnBrZyA8LSBjKCJnZ3Bsb3QyIiwgIlJDb2xvckJyZXdlciIpDQpuZXcucGtnIDwtIHBrZ1shKHBrZyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpKV0NCmlmIChsZW5ndGgobmV3LnBrZykpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcyhuZXcucGtnKSAgDQp9DQpgYGANCg0KDQpMZXQgdXMgZmlyc3QgbWFrZSBzdXJlIHdlIGFyZSB3b3JraW5nIGluIGEgZGlyZWN0b3J5IHdlIGZlZWwgY29tZm9ydGFibGUgd2l0aC4gU28gSSB3aWxsIHN0YXJ0IGJ5IGNoYW5naW5nIG15IHdvcmtpbmcgZGlyZWN0b3J5IHRvIHRoZSBvbmUgbGlzdGVkIGJlbG93DQoNCg0KTGV0IHVzIG5vdyByZWFkIHRoZSBmaWxlIGFuZCBzdGFydCBleHBsb3JpbmcgdGhlIGRhdGFzZXQgKm1lbXByb2MqLg0KDQpgYGB7cn0NCm15ZGF0YTE8LSByZWFkLmNzdigiZGF0YS9tZW1wcm9jLmNzdiIsIGhlYWRlcj1UKQ0Kc3VtbWFyeShteWRhdGExKQ0KYGBgDQpgYGB7cn0NCm15ZGF0YTENCmBgYA0KDQpJbiBvcmRlciB0byBleHBsb3JlIHRoaXMgZGF0YXNldCBtb3JlIGluIGRldGFpbCwgbGV0IHVzIGNyZWF0ZSBhIHBsb3QgdG8gY29tcGFyZSB0aGUgcHJvY2Vzc29yIGFuZCBtZW1vcnkgdXNhZ2UsIGFuZCBkaWZmZXJlbnRpYXRlIGl0IGJhc2VkIG9uIHRoZSBtYWx3YXJlIHN0YXRlLg0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmdnIDwtIGdncGxvdChteWRhdGExLCBhZXMocHJvYywgbWVtLCBjb2xvcj1zdGF0ZSkpDQpnZyA8LSBnZyArIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJTZXQyIikNCmdnIDwtIGdnICsgZ2VvbV9wb2ludChzaXplPTMpICsgdGhlbWVfYncoKQ0KcHJpbnQoZ2cpDQpgYGANCg0KDQpJIHN1Z2dlc3QgeW91IHRvIGNyZWF0ZSBub3cgYSBuZXcgZ3JhcGggYnV0IHRoaXMgdGltZSBpbmNsdWRpbmcgdGhlIHRpdGxlIGluIHRoZSBjaGFydC4gVGhlIHRpdGxlIHdvdWxkIGJlICJNZW1vcnkgdnMgUHJvY2Vzc29yIFVzYWdlIGFzIGZ1bmN0aW9uIG9mIHRoZSBNYWxhd2FyZSBzdGF0ZSIuDQojIEFkZGVkIGEgdGl0bGUgYnkgdXNpbmcgKyBnZ3RpdGxlKCJNZW1vcnkgdnMgUHJvY2Vzc29yIFVzYWdlIGFzIGZ1bmN0aW9uIG9mIHRoZSBNYWxhd2FyZSBzdGF0ZSIpKSB0byB0aGUgcHJpbnQ6DQojIHByaW50KGdnICsgZ2d0aXRsZSgiTWVtb3J5IHZzIFByb2Nlc3NvciBVc2FnZSBhcyBmdW5jdGlvbiBvZiB0aGUgTWFsYXdhcmUgc3RhdGUiKSkNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KZ2cgPC0gZ2dwbG90KG15ZGF0YTEsIGFlcyhwcm9jLCBtZW0sIGNvbG9yPXN0YXRlKSkNCmdnIDwtIGdnICsgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGU9IlNldDIiKQ0KZ2cgPC0gZ2cgKyBnZW9tX3BvaW50KHNpemU9MykgKyB0aGVtZV9idygpDQpwcmludChnZyArIGdndGl0bGUoIk1lbW9yeSB2cyBQcm9jZXNzb3IgVXNhZ2UgYXMgZnVuY3Rpb24gb2YgdGhlIE1hbGF3YXJlIHN0YXRlIikpDQpgYGANCg0KIyBHb2luZyB0byB0YWtlIDEvMyBvZiB0aGUgZGF0YSBmb3IgdGVzdCBkYXRhIGFuZCB0aGUgdHJhaW4gdGhlIGFsZ29yaXRobQ0KIyBvbiB0aGUgcmVtYWluaW5nIDIvMy4NCmBgYHtyfQ0Kc2V0LnNlZWQoMTQ5MikNCiMgY291bnQgaG93IG1hbnkgaW4gdGhlIG92ZXJhbGwgc2FtcGxlDQpuIDwtIG5yb3cobXlkYXRhMSkNCiMgc2V0IHRoZSB0ZXN0LnNpemUgdG8gYmUgMS8zcmQNCnRlc3Quc2l6ZSA8LSBhcy5pbnRlZ2VyKG4vMykNCiMgcmFuZG9tbHkgc2FtcGxlIHRoZSByb3dzIGZvciB0ZXN0IHNldA0KdGVzdHNldCA8LSBzYW1wbGUobiwgdGVzdC5zaXplKQ0KIyBub3cgc3BsaXQgdGhlIGRhdGEgaW50byB0ZXN0IGFuZCB0cmFpbg0KdGVzdCA8LSBteWRhdGExW3Rlc3RzZXQsIF0NCnRyYWluIDwtIG15ZGF0YTFbLXRlc3RzZXQsIF0NCmBgYA0KDQojIFNwbGl0dGluZyB0aGUgZGF0YSBpbnRvIHRlc3QgYW5kIHRyYWluDQoNCmBgYHtyfQ0KIyBwdWxsIG91dCBwcm9jIGFuZCBtZW0gY29sdW1ucyBmb3IgaW5mZWN0ZWQgdGhlbiBub3JtYWwNCiMgdGhlbiB1c2UgY29sTWVhbnMoKSB0byBtZWFucyBvZiB0aGUgY29sdW1ucw0KaW5mIDwtIGNvbE1lYW5zKHRyYWluW3RyYWluJHN0YXRlPT0iSW5mZWN0ZWQiLCBjKCJwcm9jIiwgIm1lbSIpXSkNCm5ybSA8LSBjb2xNZWFucyh0cmFpblt0cmFpbiRzdGF0ZT09Ik5vcm1hbCIsIGMoInByb2MiLCAibWVtIildKQ0KYGBgDQoNCg0KIyBEaXNwbGF5IHRoZSByZXN1bHRzIG9mIHRoZSBvbiB0aGUgdHdvIGNvbHVtbnMgYW5kIHJldHVybiB2ZWN0b3Igd2l0aA0KIyB0d28gZWxlbWVudHMuDQoNCmBgYHtyfQ0KcHJpbnQoaW5mKQ0KcHJpbnQobnJtKQ0KYGBgDQoNCiMgUHJlZGljdGluZyBtYWx3YXJlIHByZWRpY3QubWFsd2FyZSB0YWtlIGluIGEgc2luZ2xlIG5hbWVkIHZlY3RvciBjYWxsZWQNCiMgZGF0YSwgZXh0cmFjdCBvdXQgdGhlIHByb2MgYW5kIG1lbSB2YWx1ZXMsIHRoZW4gY2FsY3VsYXRlIGhvdyBmYXIgdGhvc2UgYXJlIGZyb20gdGhlIG1lYW5zDQojIHRhdCB3YXMgZ2VuZXJhdGVkIGR1cmluZyB0aGUgdHJhaW5pbmcuIENhbGN1bGF0ZSB0aGUgZGlzdGFuY2UgYnkgdXNpbmcgdGhlIA0KIyBQeXRoYWdvcmVhbiB0aGVvcmVtIGFeMiArIGJeMiA9IGNeMi4gT25jZSB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSB0cmFpbmVkIHByb2MgbWVhbiANCiMgYW5kIHRlc3QgcHJvYyBtZWFuIGFuZCB0aGUgdHJhaW5lZCBtZW0gbWVhbiBhbmQgdGhlIHRlc3QgbWVtIG1lYW4gaXMgY2FsY3VsYXRlZCB3ZSBqdXN0Jw0KIyB0aGVuIGp1c3QgY29tcGFyZSB0aGVtLg0KDQoNCmBgYHtyfQ0KcHJlZGljdC5tYWx3YXJlIDwtIGZ1bmN0aW9uKGRhdGEpIHsNCiAgIyBnZXQgJ3Byb2MnIGFuZCAnbWVtJyBhcyBudW1lcmljIHZhbHVlcw0KICBwcm9jIDwtIGFzLm51bWVyaWMoZGF0YVtbJ3Byb2MnXV0pDQogIG1lbSA8LSBhcy5udW1lcmljKGRhdGFbWydtZW0nXV0pDQogICMgc2V0IHVwIGluZmVjdGVkIGNvbXBhcmlzb24NCiAgaW5mLmEgPC0gaW5mWydwcm9jJ10gLSBwcm9jDQogIGluZi5iIDwtIGluZlsnbWVtJ10gLSBtZW0NCiAgIyBweXRoYWdvcmVhbiBkaXN0YW5jZSBjID0gc3FydChhXjIgKyBiXjIpDQogIGluZi5kaXN0IDwtIHNxcnQoaW5mLmFeMiArIGluZi5iXjIpDQogICMgcmVwZWF0IGZvciBub3JtYWwgc3lzdGVtcw0KICBucm0uYSA8LSBucm1bJ3Byb2MnXSAtIHByb2MNCiAgbnJtLmIgPC0gbnJtWydtZW0nXSAtIG1lbQ0KICBucm0uZGlzdCA8LSBzcXJ0KG5ybS5hXjIgKyBucm0uYl4yKQ0KICAjIGFzc2lnbiBhIGxhYmVsIG9mIHRoZSBjbG9zZXN0IChzbWFsbGVzdCkNCiAgaWZlbHNlKGluZi5kaXN0PG5ybS5kaXN0LCJJbmZlY3RlZCIsICJOb3JtYWwiKQ0KfQ0KIyBjb3VsZCB0ZXN0IHdpdGggdGhlc2UgaWYgeW91IHVuY29tbWVudCB0aGVtDQojIHByZWRpY3QubWFsd2FyZShpbmZbJ3Byb2MnXSwgaW5mWydtZW0nXSkNCiMgZXhwZWN0ICJJbmZlY3RlZCIgDQojIHByZWRpY3QubWFsd2FyZShucm1bJ3Byb2MnXSwgbnJtWydtZW0nXSkNCiMgZXhwZWN0ICJOb3JtYWwiDQpgYGANCg0KIyByZXF1aXJlcyB0aGUgdmFsdWVzIGZyb20gdGVzdCB0ZXN0IDwtIG1lbXByb2NbdGVzdHNldCwgXSBhbmQgcHJlZGljdC5tYWx3YXJlIHZhbHVlDQojIGNhbGN1bGF0ZWQgZWFybGllci4NCmBgYHtyfQ0KcHJlZGljdGlvbiA8LSBhcHBseSh0ZXN0LCAxLCBwcmVkaWN0Lm1hbHdhcmUpDQpgYGANCg0KIyBOb3cgaGF2ZSBhIHNldCBvZiBwcmVkaWN0aW9ucyBhbmQgdGhlIGFiaWxpdHkgdG8gY29tcGFyZQ0KIyB0aGVtIHRvIHRoZSByZWFsIHZhbHVlcyB0byBkZXRlcm1pbmUgaG93IHdlbGwgaXQgZGlkLCBsb29rIGF0DQojIHRoZSBwcm9wb3J0aW9uIG9mIGNvcnJlY3RseSBwcmVkaWN0ZWQgcmVzdWx0cyBvbiB0aGUgdGVzdCBkYXRhLiANCiMgVGhlbiBjYWxjdWxhdGUgdGhhdCBieSB0YWtpbmcgdGhlIG51bWJlciBvZiBjb3JyZWN0IHByZWRpY3Rpb25zIA0KIyAod2hlcmUgdGhlIHJlYWwgdGVzdCRzdGF0ZSBhbmQgdGhlIHByZWRpY3RlZCBwcmVkaWN0aW9uIG1hdGNoKSBhbmQgdGhlbg0KIyBkaXZpZGluZyB0aGF0IGJ5IHRoZSB0b3RhbCBudW1iZXIgb2YgcHJlZGljdGlvbnMuDQoNCiMgVGhpcyBzaW1wbGUgYWxnb3JpdGhnbSBwcmVkaWN0ZWQgOTAlIG9mIHRoZSB2YWx1ZXMgY29ycmVjdGx5Lg0KDQpgYGB7cn0NCnN1bSh0ZXN0JHN0YXRlPT1wcmVkaWN0aW9uKS9ucm93KHRlc3QpDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBGaWd1cmUgOS0yICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0Kc2xvcGUgPC0gLTEqKDEvKChpbmZbJ21lbSddLW5ybVsnbWVtJ10pLyhpbmZbJ3Byb2MnXS1ucm1bJ3Byb2MnXSkpKQ0KaW50ZXJjZXB0IDwtIG1lYW4oYyhpbmZbJ21lbSddLCBucm1bJ21lbSddKSkgLSAoc2xvcGUqbWVhbihjKGluZlsncHJvYyddLCBucm1bJ3Byb2MnXSkpKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCnJlc3VsdCA8LSBjYmluZCh0ZXN0LCBwcmVkaWN0PXByZWRpY3Rpb24pDQpyZXN1bHQkQWNjdXJhdGUgPC0gaWZlbHNlKHJlc3VsdCRzdGF0ZT09cmVzdWx0JHByZWRpY3QsICJZZXMiLCAiTm8iKQ0KcmVzdWx0JEFjY3VyYXRlIDwtIGZhY3RvcihyZXN1bHQkQWNjdXJhdGUsIGxldmVscz1jKCJZZXMiLCAiTm8iKSwgb3JkZXJlZD1UKQ0KYGBgDQoNClRoaXMgY2xhc3NpZmllciBjcmVhdGVzIGEgbGluZSBoYWxmd2F5IGJldHdlZW4gdGhlIHR3byBtZWFucyBhbmQgcGVycGVuZGljdWxhciB0byBhbiBpbnRlcnNlY3RpbmcgbGluZS4NCkFueXRoaW5nIGFib3ZlIHRoZSBsaW5lIGlzIHByZWRpY3RlZCBhcyBpbmZlY3RlZDsgYW55dGhpbmcgYmVsb3cgaXMgcHJlZGljdGVkIHRvIGJlIG5vcm1hbC4gDQoNClRoZSBtaXNjbGFzc2lmaWVkIHZhbHVlcyBhcmUgY2xlYXJseSBtYXJrZWQuIFRoZXJlIGFyZSBub3JtYWwgc3lzdGVtIGFib3ZlIHRoZQ0KbGluZSB0aGF0IGFyZSBtaXNsYWJlbGVkIGFzIHdlbGwgYXMgdGhlIGluZmVjdGVkIHN5c3RlbXMgYmVsb3cgdGhlIGxpbmUuDQoNCmBgYHtyfQ0KZ2cgPC0gZ2dwbG90KHJlc3VsdCwgYWVzKHByb2MsIG1lbSwgY29sb3I9c3RhdGUsIHNpemU9QWNjdXJhdGUsIHNoYXBlPUFjY3VyYXRlKSkNCmdnIDwtIGdnICsgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1jKDE2LCA4KSkNCmdnIDwtIGdnICsgc2NhbGVfc2l6ZV9tYW51YWwodmFsdWVzPWMoMywgNikpDQpnZyA8LSBnZyArIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJTZXQyIikNCmdnIDwtIGdnICsgZ2VvbV9wb2ludCgpICsgdGhlbWVfYncoKQ0KZ2cgPC0gZ2cgKyBnZW9tX2FibGluZShpbnRlcmNlcHQgPSBpbnRlcmNlcHQsIHNsb3BlID0gc2xvcGUsIGNvbG9yPSJncmF5ODAiKQ0KcHJpbnQoZ2cpDQpgYGANCg0KIyBIZXJlIHdlIGdvaW5nIHRvIHBsb3QgbGluZWFyIHJlZ3Jlc3Npb24gb24gbm9uLWxpbmVhciBkYXRhLCB0aGlzIGhhcHBlbnMgYmVjYXVzZQ0KIyBwYXJ0IG9mIHRoZSBsaW5lYXIgcmVncmVzc2lvbiBpcyBhIHJlZmVyZW5jZSB0byB0aGUgbGluZWFyIGNvZWZmaWNpZW50cyBlc3RpbWF0ZWQNCiMgbm90IHRoZSBkYXRhLg0KDQpgYGB7cn0NCnNldC5zZWVkKDEpDQp4IDwtIHJ1bmlmKDIwMCwgbWluPS0xMCwgbWF4PTEwKQ0KeSA8LSAxLjM3NyooeF4zKSArIDAuOTIqKHheMikgKyAuMyp4ICsgcm5vcm0oMjAwLCBzZD0yNTApICsgMTU3Mg0KeCA8LSB4ICsgMTANCnNtb290aCA8LSBnZ3Bsb3QoZGF0YS5mcmFtZSh4LHkpLCBhZXMoeCwgeSkpICsgZ2VvbV9wb2ludCgpICsgDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5IH4gcG9seSh4LCAzKSwgc2l6ZSA9IDEsIHNlPUYpICsgDQogIHRoZW1lX2J3KCkNCnByaW50KHNtb290aCkNCmBgYA0KDQoNCg0KYGBge3J9DQptZW1wcm9jIDwtIHJlYWQuY3N2KCJkYXRhL21lbXByb2MuY3N2IiwgaGVhZGVyPVQpDQptZW1wcm9jJGluZmVjdGVkIDwtIGlmZWxzZShtZW1wcm9jJHN0YXRlPT0iSW5mZWN0ZWQiLCAxLCAwKQ0Kc2V0LnNlZWQoMTQ5MikNCm4gPC0gbnJvdyhtZW1wcm9jKQ0KdGVzdC5zaXplIDwtIGFzLmludGVnZXIobi8zKQ0KdGVzdHNldCA8LSBzYW1wbGUobiwgdGVzdC5zaXplKQ0KdGVzdCA8LSBtZW1wcm9jW3Rlc3RzZXQsIF0NCnRyYWluIDwtIG1lbXByb2NbLXRlc3RzZXQsIF0NCmBgYA0KDQoNCiMgVGhlIHBsb3RzIGFyZSBzaG93aW5nIGxpbmVhciByZWdyZXNzaW9uIG9uIGluZmVjdGlvbiB0ZXN0IGRhdGENCiMgT3V0cHV0IG9uIHRoZSB4LWF4aXMgaXMgYW4gZXN0aW1hdGVkIHByb2JhYmlsaXR5IG9mIGEgaG9zdCBiZWluZyBpbmZlY3RlZA0KIyBiYXNlZCBvbiB0aGUgaW5wdXQgdmFyaWFibGVzLiBUaGlzIHBsb3R0ZWQgYWdhaW5zdCB0aGUga25vd24gdmFsdWVzIGluIHRoZSB0ZXN0DQojIGRhdGEgb24gdGhlIHktYXhpcyhyZWFsIGxpZmUpLiANCiMgZ2xtKCkgZnVuY3Rpb24gaXMgb25lIG9mIHNlcnZlcmFsIGFwcHJvYWNoZXMgdG8gbG9naXN0aWMgcmVncmVzc2lvbi4gVGhpcyBmdW5jdGlvbg0KIyBjYW4gaGFuZGxlIG1vc3Qgc2l0dWF0aW9ucy4NCmBgYHtyfQ0KZ2xtLm91dCA9IGdsbShpbmZlY3RlZCB+IHByb2MgKyBtZW0sIGRhdGE9dGVzdCwgZmFtaWx5PWJpbm9taWFsKGxvZ2l0KSkNCnN1bW1hcnkoZ2xtLm91dCkNCm1vZGVsb2cgPC0gcHJlZGljdC5nbG0oZ2xtLm91dCwgdGVzdCwgdHlwZT0icmVzcG9uc2UiKQ0KZ2cgPC0gZ2dwbG90KGRhdGEuZnJhbWUoeD1tb2RlbG9nLCB5PWlmZWxzZSh0ZXN0JGluZmVjdGVkPjAuNSwgIlllcyIsICJObyIpKSwgYWVzKHgsIHkpKSArDQogIGdlb21fcG9pbnQoc2l6ZT0zLCBmaWxsPSJzdGVlbGJsdWUiLCBjb2xvcj0iYmxhY2siLCBzaGFwZT00KSArIA0KICB5bGFiKCJLbm93biBJbmZlY3RlZCBIb3N0IikgKw0KICB4bGFiKCJFc3RpbWF0ZWQgUHJvYmFiaWxpdHkgb2YgSW5mZWN0ZWQgSG9zdCIpICsgdGhlbWVfYncoKQ0KcHJpbnQoZ2cpDQpgYGANCg0KIyBDcmVhdGluZyBhIGRpYWdyYW0gd2l0aCBzYW1lIGRhdGEgd2l0aCBtdWx0aXBsZSBkaWZmZXJlbmN0IGstdmFsdWVzDQojIGttZWFucygpIGZ1bmN0aW9uIHdpbGwgcGVyZm9ybSBrLW1lYW5zIGNsdXN0ZXJpbmcuDQojIFRoZSBmaXJzdCBkaWFncmFtIGlzIGstbWVhbnMgd2l0aCAzIGNsdXN0ZXJzLg0KIyBUaGUgc2Vjb25kIGRpYWdyYW0gaXMgay1tZWFucyB3aXRoIDQgY2x1c3RlcnMuDQojIFRoZSB0aGlyZCBkaWFncmFtIGlzIGstbWVhbnMgd2l0aCA1IGNsdXN0ZXJzLg0KIyBUaGUgZm91cnRoIGRpYWdyYW0gaXMgay1tZWFucyB3aXRoIDYgY2x1c3RlcnMuDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMSkgIyByZXBlYXRhYmxlDQp4IDwtIGMocm5vcm0oMjAwKSwgcm5vcm0oNDAwKSsyLCBybm9ybSg0MDApLTIpDQp5IDwtIGMocm5vcm0oMjAwKSwgcm5vcm0oMjAwKSsyLCBybm9ybSgyMDApLTIsIHJub3JtKDIwMCkrMiwgcm5vcm0oMjAwKS0yKQ0KcmFuZGF0YSA8LSBkYXRhLmZyYW1lKHg9eCwgeT15KQ0Kb3V0IDwtIGxpc3QoKQ0KZm9yKGkgaW4gYygzLDQsNSw2KSkgew0KICBrbSA8LSBrbWVhbnMocmFuZGF0YSwgaSkNCiAgY2VudGVycyA8LSBkYXRhLmZyYW1lKHg9a20kY2VudGVyc1sgLDFdLCB5PWttJGNlbnRlcnNbICwyXSwgY2x1c3Rlcj0xKQ0KICByYW5kYXRhJGNsdXN0ZXIgPC0gZmFjdG9yKGttJGNsdXN0ZXIpDQogIGdnIDwtIGdncGxvdChyYW5kYXRhLCBhZXMoeCwgeSwgY29sb3I9Y2x1c3RlcikpICsgZ2VvbV9wb2ludChzaXplPTIpDQogIGdnIDwtIGdnICsgZ2VvbV9wb2ludChkYXRhPWNlbnRlcnMsIGFlcyh4LCB5KSwgc2hhcGU9OCwgY29sb3I9ImJsYWNrIiwgc2l6ZT00KQ0KICBnZyA8LSBnZyArIHNjYWxlX3hfY29udGludW91cyhleHBhbmQ9YygwLDAuMSkpDQogIGdnIDwtIGdnICsgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZD1jKDAsMC4xKSkNCiAgZ2cgPC0gZ2cgKyBnZ3RpdGxlKHBhc3RlKCJrLW1lYW5zIHdpdGgiLCBpLCAiY2x1c3RlcnMiKSkNCiAgZ2cgPC0gZ2cgKyB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgZmlsbD1OQSksDQogICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgICAgICAgICAgICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCkpDQogIG91dFtbaS0yXV0gPC0gZ2cNCn0NCnByaW50KG91dFtbMV1dKQ0KcHJpbnQob3V0W1syXV0pDQpwcmludChvdXRbWzNdXSkNCnByaW50KG91dFtbNF1dKQ0KYGBgDQoNCg0K