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

# Lets check our working directory
getwd()
[1] "C:/Users/antho/OneDrive/Documents/School/4.DataSecurity&Governance"

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

mydata1<- read.csv("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                     

We can see we were succesfull in uploading the memproc.csv data into a data fram we named “mydata1” which has 4 fields. Lets get more info on the dataset by using the head function and inspecting the colomns and top 6 rows.

head(mydata1)

We can see that There are two fields (“host” & “state”) that look like characters and two fields (“proc” & “mem”) that look like numeric data under the dbl data type

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.

# you will see that we use the ggplot function to create graph comparing the proc(x) and mem(y) fields and set the details/legend of the "state" field by color. 
library(ggplot2)
Warning: package ‘ggplot2’ was built under R version 4.2.1
gg <- ggplot(mydata1, aes(proc, mem, color=state))
gg <- gg + scale_color_brewer(palette="Set2") # we can change the color here
gg <- gg + geom_point(size=3) + theme_bw() # we can change the size of the dots here
print(gg)

I sugest 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”.

# We print the original plot (gg), adding the string title we want by inserting it using the ggplot2 function ggtitle()
print(gg + ggtitle("Memory vs Processor Usage as function of the Malware state"))

We print the original plot (gg), adding the string title we want by inserting it using the ggplot2 function ggtitle()

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, ]
# 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")])

We create two variables storing the mean value for all the “infected” and “normal” states from the train data that we split earlier.

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

By using the colMens function we are able to see the mean of the “infected” and “normal” states for the Processor(proc) and memory (mem) fields in the train data.

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"

We create a function called predict.malware to do exactly that, by finding the shortest distance from all point in the train data. Very interesting algorithms we use above to find the pythagorean distance forbetween the processor and memory values for the infected and normal states.

prediction <- apply(test, 1, predict.malware)
sum(test$state==prediction)/nrow(test)
[1] 0.902439

Above is our predictions accuracy which is considerably good at 90%

Lets make a slope and intercept so we can add an diagnal line on the chart so we can better visualize the divide of states.

# 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'])))

Lets create a result variable that will display weather or predictions were accurate or not

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)

Now recreate the previous graph but add the detail/legend of accuracy by shape. We also use the geom_abline() function to create the diagnal line from the slope and intercept we identified and created above.

# notice here we set the detail/legend at the end of this next line where we create the chart. 
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)

we can see that we have less false negatives than we do false positives which means we are predicting more infected situations accurately than we are non infected meaning those with a threat are being identified. Only issue is there are six times more people that are being told they are infected when they are not.

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)

Here we create a linear regression model based of random data x, y values we created with a distribution like that of sigmoid function. Very interesting notice how we use the geom_smooth() function to define and deploy the linear regression model in nearly one line. This is useful for predicting quantitative values.

memproc <- read.csv("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, ]

Since this data is being used more so to classify a qualitative field (“state”) which has only values it we would benifit us to convert these two string values into binary digits so that they are easier to use as a target value for logistic regression.

You will see below how we target only the infected state for our chart when creating this logistic regression model. Logistic regrssion are achieved using the “generalized linear models” glm() function which i read can also do poisson regression and survival analysis.

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)

Notice this chart is perfect for showing the probability of a classification, kind of like a confusion matrix this is identify the probability of each of a binary classifications two values. Notice the host that have a higher probability of infection have more grouping on the yes line of the y axis. Also Notice that the majority of records fall near the 0 probability and on the “no” known infected line.

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]])

Here we create a random random scatter plot to demonstrate k-means clustering. The K stands for the number of clusters you want to create. Notice that we made 4 different charts with a cluster of 3 in the first and ascending to 6 clusters in the last. The clustering is at random and needs to be specified by you which is why it hierarchical clustering can help in eliminating the guessing as to how many clusters suits you, maybe for dimension reduction.

LS0tDQp0aXRsZTogIk1hY2hpbmVfTGVhcm5pbmciDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KVGhlc2UgcGFja2FnZXMgYXJlIG5lZWRlZCBmb3IgdGhpcyBhc3NpZ25tZW50Og0KDQpgYGB7cn0NCnBrZyA8LSBjKCJnZ3Bsb3QyIiwgIlJDb2xvckJyZXdlciIpDQpuZXcucGtnIDwtIHBrZ1shKHBrZyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpKV0NCmlmIChsZW5ndGgobmV3LnBrZykpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcyhuZXcucGtnKSAgDQp9DQpgYGANCg0KDQpMZXQgdXMgZmlyc3QgbWFrZSBzdXJlIHdlIGFyZSB3b3JraW5nIGluIGEgZGlyZWN0b3J5IHdlIGZlZWwgY29tZm9ydGFibGUgd2l0aC4gU28gSSB3aWxsIHN0YXJ0IGJ5IGNoYW5naW5nIG15IHdvcmtpbmcgZGlyZWN0b3J5IHRvIHRoZSBvbmUgbGlzdGVkIGJlbG93DQoNCmBgYHtyfQ0KIyBMZXRzIGNoZWNrIG91ciB3b3JraW5nIGRpcmVjdG9yeQ0KZ2V0d2QoKQ0KDQpgYGANCg0KTGV0IHVzIG5vdyByZWFkIHRoZSBmaWxlIGFuZCBzdGFydCBleHBsb3JpbmcgdGhlIGRhdGFzZXQgKm1lbXByb2MqLg0KDQpgYGB7cn0NCm15ZGF0YTE8LSByZWFkLmNzdigibWVtcHJvYy5jc3YiLCBoZWFkZXI9VCkNCnN1bW1hcnkobXlkYXRhMSkNCmBgYA0KDQpXZSBjYW4gc2VlIHdlIHdlcmUgc3VjY2VzZnVsbCBpbiB1cGxvYWRpbmcgdGhlIG1lbXByb2MuY3N2IGRhdGEgaW50byBhIGRhdGEgZnJhbSB3ZSBuYW1lZCAibXlkYXRhMSIgd2hpY2ggaGFzIDQgZmllbGRzLg0KTGV0cyBnZXQgbW9yZSBpbmZvIG9uIHRoZSBkYXRhc2V0IGJ5IHVzaW5nIHRoZSBoZWFkIGZ1bmN0aW9uIGFuZCBpbnNwZWN0aW5nIHRoZSBjb2xvbW5zIGFuZCB0b3AgNiByb3dzLiANCg0KYGBge3J9DQpoZWFkKG15ZGF0YTEpDQpgYGANCg0KV2UgY2FuIHNlZSB0aGF0IFRoZXJlIGFyZSB0d28gZmllbGRzICgiaG9zdCIgJiAic3RhdGUiKSB0aGF0IGxvb2sgbGlrZSBjaGFyYWN0ZXJzIGFuZCB0d28gZmllbGRzICgicHJvYyIgJiAibWVtIikgdGhhdCBsb29rIGxpa2UgbnVtZXJpYyBkYXRhIHVuZGVyIHRoZSBkYmwgZGF0YSB0eXBlDQoNCkluIG9yZGVyIHRvIGV4cGxvcmUgdGhpcyBkYXRhc2V0IG1vcmUgaW4gZGV0YWlsLCBsZXQgdXMgY3JlYXRlIGEgcGxvdCB0byBjb21wYXJlIHRoZSBwcm9jZXNzb3IgYW5kIG1lbW9yeSB1c2FnZSwgYW5kIGRpZmZlcmVudGlhdGUgaXQgYmFzZWQgb24gdGhlIG1hbHdhcmUgc3RhdGUuDQoNCmBgYHtyfQ0KIyB5b3Ugd2lsbCBzZWUgdGhhdCB3ZSB1c2UgdGhlIGdncGxvdCBmdW5jdGlvbiB0byBjcmVhdGUgZ3JhcGggY29tcGFyaW5nIHRoZSBwcm9jKHgpIGFuZCBtZW0oeSkgZmllbGRzIGFuZCBzZXQgdGhlIGRldGFpbHMvbGVnZW5kIG9mIHRoZSAic3RhdGUiIGZpZWxkIGJ5IGNvbG9yLiANCmxpYnJhcnkoZ2dwbG90MikNCmdnIDwtIGdncGxvdChteWRhdGExLCBhZXMocHJvYywgbWVtLCBjb2xvcj1zdGF0ZSkpDQpnZyA8LSBnZyArIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJTZXQyIikgIyB3ZSBjYW4gY2hhbmdlIHRoZSBjb2xvciBoZXJlDQpnZyA8LSBnZyArIGdlb21fcG9pbnQoc2l6ZT0zKSArIHRoZW1lX2J3KCkgIyB3ZSBjYW4gY2hhbmdlIHRoZSBzaXplIG9mIHRoZSBkb3RzIGhlcmUNCnByaW50KGdnKQ0KYGBgDQoNCkkgc3VnZXN0IHlvdSB0byBjcmVhdGUgbm93IGEgbmV3IGdyYXBoIGJ1dCB0aGlzIHRpbWUgaW5jbHVkaW5nIHRoZSB0aXRsZSBpbiB0aGUgY2hhcnQuIFRoZSB0aXRsZSB3b3VsZCBiZSAiTWVtb3J5IHZzIFByb2Nlc3NvciBVc2FnZSBhcyBmdW5jdGlvbiBvZiB0aGUgTWFsYXdhcmUgc3RhdGUiLg0KDQoNCmBgYHtyfQ0KcHJpbnQoZ2cgKyBnZ3RpdGxlKCJNZW1vcnkgdnMgUHJvY2Vzc29yIFVzYWdlIGFzIGZ1bmN0aW9uIG9mIHRoZSBNYWx3YXJlIHN0YXRlIikpDQpgYGANCldlIHByaW50IHRoZSBvcmlnaW5hbCBwbG90IChnZyksIGFkZGluZyB0aGUgc3RyaW5nIHRpdGxlIHdlIHdhbnQgYnkgaW5zZXJ0aW5nIGl0IHVzaW5nIHRoZSBnZ3Bsb3QyIGZ1bmN0aW9uIGdndGl0bGUoKQ0KDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTQ5MikNCiMgY291bnQgaG93IG1hbnkgaW4gdGhlIG92ZXJhbGwgc2FtcGxlDQpuIDwtIG5yb3cobXlkYXRhMSkNCiMgc2V0IHRoZSB0ZXN0LnNpemUgdG8gYmUgMS8zcmQNCnRlc3Quc2l6ZSA8LSBhcy5pbnRlZ2VyKG4vMykNCiMgcmFuZG9tbHkgc2FtcGxlIHRoZSByb3dzIGZvciB0ZXN0IHNldA0KdGVzdHNldCA8LSBzYW1wbGUobiwgdGVzdC5zaXplKQ0KIyBub3cgc3BsaXQgdGhlIGRhdGEgaW50byB0ZXN0IGFuZCB0cmFpbg0KdGVzdCA8LSBteWRhdGExW3Rlc3RzZXQsIF0NCnRyYWluIDwtIG15ZGF0YTFbLXRlc3RzZXQsIF0NCmBgYA0KDQoNCg0KYGBge3J9DQojIHB1bGwgb3V0IHByb2MgYW5kIG1lbSBjb2x1bW5zIGZvciBpbmZlY3RlZCB0aGVuIG5vcm1hbA0KIyB0aGVuIHVzZSBjb2xNZWFucygpIHRvIG1lYW5zIG9mIHRoZSBjb2x1bW5zDQppbmYgPC0gY29sTWVhbnModHJhaW5bdHJhaW4kc3RhdGU9PSJJbmZlY3RlZCIsIGMoInByb2MiLCAibWVtIildKQ0KbnJtIDwtIGNvbE1lYW5zKHRyYWluW3RyYWluJHN0YXRlPT0iTm9ybWFsIiwgYygicHJvYyIsICJtZW0iKV0pDQpgYGANCg0KDQpXZSBjcmVhdGUgdHdvIHZhcmlhYmxlcyBzdG9yaW5nIHRoZSBtZWFuIHZhbHVlIGZvciBhbGwgdGhlICJpbmZlY3RlZCIgYW5kICJub3JtYWwiIHN0YXRlcyBmcm9tIHRoZSB0cmFpbiBkYXRhIHRoYXQgd2Ugc3BsaXQgZWFybGllci4gDQoNCmBgYHtyfQ0KcHJpbnQoaW5mKQ0KcHJpbnQobnJtKQ0KYGBgDQpCeSB1c2luZyB0aGUgY29sTWVucyBmdW5jdGlvbiB3ZSBhcmUgYWJsZSB0byBzZWUgdGhlIG1lYW4gb2YgdGhlICJpbmZlY3RlZCIgYW5kICJub3JtYWwiIHN0YXRlcyBmb3IgdGhlIFByb2Nlc3Nvcihwcm9jKSBhbmQgbWVtb3J5IChtZW0pIGZpZWxkcyBpbiB0aGUgdHJhaW4gZGF0YS4gDQoNCg0KYGBge3J9DQpwcmVkaWN0Lm1hbHdhcmUgPC0gZnVuY3Rpb24oZGF0YSkgew0KICAjIGdldCAncHJvYycgYW5kICdtZW0nIGFzIG51bWVyaWMgdmFsdWVzDQogIHByb2MgPC0gYXMubnVtZXJpYyhkYXRhW1sncHJvYyddXSkNCiAgbWVtIDwtIGFzLm51bWVyaWMoZGF0YVtbJ21lbSddXSkNCiAgIyBzZXQgdXAgaW5mZWN0ZWQgY29tcGFyaXNvbg0KICBpbmYuYSA8LSBpbmZbJ3Byb2MnXSAtIHByb2MNCiAgaW5mLmIgPC0gaW5mWydtZW0nXSAtIG1lbQ0KICAjIHB5dGhhZ29yZWFuIGRpc3RhbmNlIGMgPSBzcXJ0KGFeMiArIGJeMikNCiAgaW5mLmRpc3QgPC0gc3FydChpbmYuYV4yICsgaW5mLmJeMikNCiAgIyByZXBlYXQgZm9yIG5vcm1hbCBzeXN0ZW1zDQogIG5ybS5hIDwtIG5ybVsncHJvYyddIC0gcHJvYw0KICBucm0uYiA8LSBucm1bJ21lbSddIC0gbWVtDQogIG5ybS5kaXN0IDwtIHNxcnQobnJtLmFeMiArIG5ybS5iXjIpDQogICMgYXNzaWduIGEgbGFiZWwgb2YgdGhlIGNsb3Nlc3QgKHNtYWxsZXN0KQ0KICBpZmVsc2UoaW5mLmRpc3Q8bnJtLmRpc3QsIkluZmVjdGVkIiwgIk5vcm1hbCIpDQp9DQojIGNvdWxkIHRlc3Qgd2l0aCB0aGVzZSBpZiB5b3UgdW5jb21tZW50IHRoZW0NCiMgcHJlZGljdC5tYWx3YXJlKGluZlsncHJvYyddLCBpbmZbJ21lbSddKQ0KIyBleHBlY3QgIkluZmVjdGVkIiANCiMgcHJlZGljdC5tYWx3YXJlKG5ybVsncHJvYyddLCBucm1bJ21lbSddKQ0KIyBleHBlY3QgIk5vcm1hbCINCmBgYA0KDQpXZSBjcmVhdGUgYSBmdW5jdGlvbiBjYWxsZWQgcHJlZGljdC5tYWx3YXJlIHRvIGRvIGV4YWN0bHkgdGhhdCwgYnkgZmluZGluZyB0aGUgc2hvcnRlc3QgZGlzdGFuY2UgZnJvbSBhbGwgcG9pbnQgaW4gdGhlIHRyYWluIGRhdGEuIFZlcnkgaW50ZXJlc3RpbmcgYWxnb3JpdGhtcyB3ZSB1c2UgYWJvdmUgdG8gZmluZCB0aGUgcHl0aGFnb3JlYW4gZGlzdGFuY2UgZm9yYmV0d2VlbiB0aGUgcHJvY2Vzc29yIGFuZCBtZW1vcnkgdmFsdWVzIGZvciB0aGUgaW5mZWN0ZWQgYW5kIG5vcm1hbCBzdGF0ZXMuIA0KDQpgYGB7cn0NCnByZWRpY3Rpb24gPC0gYXBwbHkodGVzdCwgMSwgcHJlZGljdC5tYWx3YXJlKQ0KYGBgDQoNCmBgYHtyfQ0Kc3VtKHRlc3Qkc3RhdGU9PXByZWRpY3Rpb24pL25yb3codGVzdCkNCmBgYA0KQWJvdmUgaXMgb3VyIHByZWRpY3Rpb25zIGFjY3VyYWN5IHdoaWNoIGlzIGNvbnNpZGVyYWJseSBnb29kIGF0IDkwJQ0KDQpMZXRzIG1ha2UgYSBzbG9wZSBhbmQgaW50ZXJjZXB0IHNvIHdlIGNhbiBhZGQgYW4gZGlhZ25hbCBsaW5lIG9uIHRoZSBjaGFydCBzbyB3ZSBjYW4gYmV0dGVyIHZpc3VhbGl6ZSB0aGUgZGl2aWRlIG9mIHN0YXRlcy4gDQoNCmBgYHtyfQ0KIyBGaWd1cmUgOS0yICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0Kc2xvcGUgPC0gLTEqKDEvKChpbmZbJ21lbSddLW5ybVsnbWVtJ10pLyhpbmZbJ3Byb2MnXS1ucm1bJ3Byb2MnXSkpKQ0KaW50ZXJjZXB0IDwtIG1lYW4oYyhpbmZbJ21lbSddLCBucm1bJ21lbSddKSkgLSAoc2xvcGUqbWVhbihjKGluZlsncHJvYyddLCBucm1bJ3Byb2MnXSkpKQ0KYGBgDQoNCkxldHMgY3JlYXRlIGEgcmVzdWx0IHZhcmlhYmxlIHRoYXQgd2lsbCBkaXNwbGF5IHdlYXRoZXIgb3IgcHJlZGljdGlvbnMgd2VyZSBhY2N1cmF0ZSBvciBub3QNCg0KYGBge3J9DQpyZXN1bHQgPC0gY2JpbmQodGVzdCwgcHJlZGljdD1wcmVkaWN0aW9uKQ0KcmVzdWx0JEFjY3VyYXRlIDwtIGlmZWxzZShyZXN1bHQkc3RhdGU9PXJlc3VsdCRwcmVkaWN0LCAiWWVzIiwgIk5vIikNCnJlc3VsdCRBY2N1cmF0ZSA8LSBmYWN0b3IocmVzdWx0JEFjY3VyYXRlLCBsZXZlbHM9YygiWWVzIiwgIk5vIiksIG9yZGVyZWQ9VCkNCmBgYA0KDQpOb3cgcmVjcmVhdGUgdGhlIHByZXZpb3VzIGdyYXBoIGJ1dCBhZGQgdGhlIGRldGFpbC9sZWdlbmQgb2YgYWNjdXJhY3kgYnkgc2hhcGUuIFdlIGFsc28gdXNlIHRoZSBnZW9tX2FibGluZSgpIGZ1bmN0aW9uIHRvIGNyZWF0ZSB0aGUgZGlhZ25hbCBsaW5lIGZyb20gdGhlIHNsb3BlIGFuZCBpbnRlcmNlcHQgd2UgaWRlbnRpZmllZCBhbmQgY3JlYXRlZCBhYm92ZS4gDQoNCg0KYGBge3J9DQojIG5vdGljZSBoZXJlIHdlIHNldCB0aGUgZGV0YWlsL2xlZ2VuZCBhdCB0aGUgZW5kIG9mIHRoaXMgbmV4dCBsaW5lIHdoZXJlIHdlIGNyZWF0ZSB0aGUgY2hhcnQuIA0KZ2cgPC0gZ2dwbG90KHJlc3VsdCwgYWVzKHByb2MsIG1lbSwgY29sb3I9c3RhdGUsIHNpemU9QWNjdXJhdGUsIHNoYXBlPUFjY3VyYXRlKSkgDQpnZyA8LSBnZyArIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXM9YygxNiwgOCkpDQpnZyA8LSBnZyArIHNjYWxlX3NpemVfbWFudWFsKHZhbHVlcz1jKDMsIDYpKQ0KZ2cgPC0gZ2cgKyBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iU2V0MiIpDQpnZyA8LSBnZyArIGdlb21fcG9pbnQoKSArIHRoZW1lX2J3KCkNCmdnIDwtIGdnICsgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gaW50ZXJjZXB0LCBzbG9wZSA9IHNsb3BlLCBjb2xvcj0iZ3JheTgwIikNCnByaW50KGdnKQ0KYGBgDQoNCndlIGNhbiBzZWUgdGhhdCB3ZSBoYXZlIGxlc3MgZmFsc2UgbmVnYXRpdmVzIHRoYW4gd2UgZG8gZmFsc2UgcG9zaXRpdmVzIHdoaWNoIG1lYW5zIHdlIGFyZSBwcmVkaWN0aW5nIG1vcmUgaW5mZWN0ZWQgc2l0dWF0aW9ucyBhY2N1cmF0ZWx5IHRoYW4gd2UgYXJlIG5vbiBpbmZlY3RlZCBtZWFuaW5nIHRob3NlIHdpdGggYSB0aHJlYXQgYXJlIGJlaW5nIGlkZW50aWZpZWQuIE9ubHkgaXNzdWUgaXMgdGhlcmUgYXJlIHNpeCB0aW1lcyBtb3JlIHBlb3BsZSB0aGF0IGFyZSBiZWluZyB0b2xkIHRoZXkgYXJlIGluZmVjdGVkIHdoZW4gdGhleSBhcmUgbm90LiANCg0KDQpgYGB7cn0NCnNldC5zZWVkKDEpDQp4IDwtIHJ1bmlmKDIwMCwgbWluPS0xMCwgbWF4PTEwKQ0KeSA8LSAxLjM3NyooeF4zKSArIDAuOTIqKHheMikgKyAuMyp4ICsgcm5vcm0oMjAwLCBzZD0yNTApICsgMTU3Mg0KeCA8LSB4ICsgMTANCnNtb290aCA8LSBnZ3Bsb3QoZGF0YS5mcmFtZSh4LHkpLCBhZXMoeCwgeSkpICsgZ2VvbV9wb2ludCgpICsgDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5IH4gcG9seSh4LCAzKSwgc2l6ZSA9IDEsIHNlPUYpICsgDQogIHRoZW1lX2J3KCkNCnByaW50KHNtb290aCkNCmBgYA0KSGVyZSB3ZSBjcmVhdGUgYSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBiYXNlZCBvZiByYW5kb20gZGF0YSB4LCB5IHZhbHVlcyB3ZSBjcmVhdGVkIHdpdGggYSBkaXN0cmlidXRpb24gbGlrZSB0aGF0IG9mIHNpZ21vaWQgZnVuY3Rpb24uIA0KVmVyeSBpbnRlcmVzdGluZyBub3RpY2UgaG93IHdlIHVzZSB0aGUgZ2VvbV9zbW9vdGgoKSBmdW5jdGlvbiB0byBkZWZpbmUgYW5kIGRlcGxveSB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgaW4gbmVhcmx5IG9uZSBsaW5lLiANClRoaXMgaXMgdXNlZnVsIGZvciBwcmVkaWN0aW5nIHF1YW50aXRhdGl2ZSB2YWx1ZXMuICANCg0KYGBge3J9DQptZW1wcm9jIDwtIHJlYWQuY3N2KCJtZW1wcm9jLmNzdiIsIGhlYWRlcj1UKQ0KbWVtcHJvYyRpbmZlY3RlZCA8LSBpZmVsc2UobWVtcHJvYyRzdGF0ZT09IkluZmVjdGVkIiwgMSwgMCkNCnNldC5zZWVkKDE0OTIpDQpuIDwtIG5yb3cobWVtcHJvYykNCnRlc3Quc2l6ZSA8LSBhcy5pbnRlZ2VyKG4vMykNCnRlc3RzZXQgPC0gc2FtcGxlKG4sIHRlc3Quc2l6ZSkNCnRlc3QgPC0gbWVtcHJvY1t0ZXN0c2V0LCBdDQp0cmFpbiA8LSBtZW1wcm9jWy10ZXN0c2V0LCBdDQpgYGANCg0KU2luY2UgdGhpcyBkYXRhIGlzIGJlaW5nIHVzZWQgbW9yZSBzbyB0byBjbGFzc2lmeSBhIHF1YWxpdGF0aXZlIGZpZWxkICgic3RhdGUiKSB3aGljaCBoYXMgb25seSB2YWx1ZXMgaXQgd2Ugd291bGQgYmVuaWZpdCB1cyB0byBjb252ZXJ0IHRoZXNlIHR3byBzdHJpbmcgdmFsdWVzIGludG8gYmluYXJ5IGRpZ2l0cyBzbyB0aGF0IHRoZXkgYXJlIGVhc2llciB0byB1c2UgYXMgYSB0YXJnZXQgdmFsdWUgIGZvciBsb2dpc3RpYyByZWdyZXNzaW9uLiANCg0KWW91IHdpbGwgc2VlIGJlbG93IGhvdyB3ZSB0YXJnZXQgb25seSB0aGUgaW5mZWN0ZWQgc3RhdGUgZm9yIG91ciBjaGFydCB3aGVuIGNyZWF0aW5nIHRoaXMgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbC4NCkxvZ2lzdGljIHJlZ3Jzc2lvbiBhcmUgYWNoaWV2ZWQgdXNpbmcgdGhlICJnZW5lcmFsaXplZCBsaW5lYXIgbW9kZWxzIiBnbG0oKSBmdW5jdGlvbiB3aGljaCBpIHJlYWQgY2FuIGFsc28gZG8gcG9pc3NvbiByZWdyZXNzaW9uIGFuZCBzdXJ2aXZhbCBhbmFseXNpcy4gDQoNCmBgYHtyfQ0KZ2xtLm91dCA9IGdsbShpbmZlY3RlZCB+IHByb2MgKyBtZW0sIGRhdGE9dGVzdCwgZmFtaWx5PWJpbm9taWFsKGxvZ2l0KSkNCnN1bW1hcnkoZ2xtLm91dCkNCm1vZGVsb2cgPC0gcHJlZGljdC5nbG0oZ2xtLm91dCwgdGVzdCwgdHlwZT0icmVzcG9uc2UiKQ0KZ2cgPC0gZ2dwbG90KGRhdGEuZnJhbWUoeD1tb2RlbG9nLCB5PWlmZWxzZSh0ZXN0JGluZmVjdGVkPjAuNSwgIlllcyIsICJObyIpKSwgYWVzKHgsIHkpKSArDQogIGdlb21fcG9pbnQoc2l6ZT0zLCBmaWxsPSJzdGVlbGJsdWUiLCBjb2xvcj0iYmxhY2siLCBzaGFwZT00KSArIA0KICB5bGFiKCJLbm93biBJbmZlY3RlZCBIb3N0IikgKw0KICB4bGFiKCJFc3RpbWF0ZWQgUHJvYmFiaWxpdHkgb2YgSW5mZWN0ZWQgSG9zdCIpICsgdGhlbWVfYncoKQ0KcHJpbnQoZ2cpDQpgYGANCg0KTm90aWNlIHRoaXMgY2hhcnQgaXMgcGVyZmVjdCBmb3Igc2hvd2luZyB0aGUgcHJvYmFiaWxpdHkgb2YgYSBjbGFzc2lmaWNhdGlvbiwga2luZCBvZiBsaWtlIGEgY29uZnVzaW9uIG1hdHJpeCB0aGlzIGlzIGlkZW50aWZ5IHRoZSBwcm9iYWJpbGl0eSBvZiBlYWNoIG9mIGEgYmluYXJ5IGNsYXNzaWZpY2F0aW9ucyB0d28gdmFsdWVzLiANCk5vdGljZSB0aGUgaG9zdCB0aGF0IGhhdmUgYSBoaWdoZXIgcHJvYmFiaWxpdHkgb2YgaW5mZWN0aW9uIGhhdmUgbW9yZSBncm91cGluZyBvbiB0aGUgeWVzIGxpbmUgb2YgdGhlIHkgYXhpcy4gDQpBbHNvIE5vdGljZSB0aGF0IHRoZSBtYWpvcml0eSBvZiByZWNvcmRzIGZhbGwgbmVhciB0aGUgMCBwcm9iYWJpbGl0eSBhbmQgb24gdGhlICJubyIga25vd24gaW5mZWN0ZWQgbGluZS4gDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMSkgIyByZXBlYXRhYmxlDQp4IDwtIGMocm5vcm0oMjAwKSwgcm5vcm0oNDAwKSsyLCBybm9ybSg0MDApLTIpDQp5IDwtIGMocm5vcm0oMjAwKSwgcm5vcm0oMjAwKSsyLCBybm9ybSgyMDApLTIsIHJub3JtKDIwMCkrMiwgcm5vcm0oMjAwKS0yKQ0KcmFuZGF0YSA8LSBkYXRhLmZyYW1lKHg9eCwgeT15KQ0Kb3V0IDwtIGxpc3QoKQ0KZm9yKGkgaW4gYygzLDQsNSw2KSkgew0KICBrbSA8LSBrbWVhbnMocmFuZGF0YSwgaSkNCiAgY2VudGVycyA8LSBkYXRhLmZyYW1lKHg9a20kY2VudGVyc1sgLDFdLCB5PWttJGNlbnRlcnNbICwyXSwgY2x1c3Rlcj0xKQ0KICByYW5kYXRhJGNsdXN0ZXIgPC0gZmFjdG9yKGttJGNsdXN0ZXIpDQogIGdnIDwtIGdncGxvdChyYW5kYXRhLCBhZXMoeCwgeSwgY29sb3I9Y2x1c3RlcikpICsgZ2VvbV9wb2ludChzaXplPTIpDQogIGdnIDwtIGdnICsgZ2VvbV9wb2ludChkYXRhPWNlbnRlcnMsIGFlcyh4LCB5KSwgc2hhcGU9OCwgY29sb3I9ImJsYWNrIiwgc2l6ZT00KQ0KICBnZyA8LSBnZyArIHNjYWxlX3hfY29udGludW91cyhleHBhbmQ9YygwLDAuMSkpDQogIGdnIDwtIGdnICsgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZD1jKDAsMC4xKSkNCiAgZ2cgPC0gZ2cgKyBnZ3RpdGxlKHBhc3RlKCJrLW1lYW5zIHdpdGgiLCBpLCAiY2x1c3RlcnMiKSkNCiAgZ2cgPC0gZ2cgKyB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgZmlsbD1OQSksDQogICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgICAgICAgICAgICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCkpDQogIG91dFtbaS0yXV0gPC0gZ2cNCn0NCnByaW50KG91dFtbMV1dKQ0KcHJpbnQob3V0W1syXV0pDQpwcmludChvdXRbWzNdXSkNCnByaW50KG91dFtbNF1dKQ0KYGBgDQoNCkhlcmUgd2UgY3JlYXRlIGEgcmFuZG9tIHJhbmRvbSBzY2F0dGVyIHBsb3QgdG8gZGVtb25zdHJhdGUgay1tZWFucyBjbHVzdGVyaW5nLiBUaGUgSyBzdGFuZHMgZm9yIHRoZSBudW1iZXIgb2YgY2x1c3RlcnMgeW91IHdhbnQgdG8gY3JlYXRlLiBOb3RpY2UgdGhhdCB3ZSBtYWRlIDQgZGlmZmVyZW50IGNoYXJ0cyB3aXRoIGEgY2x1c3RlciBvZiAzIGluIHRoZSBmaXJzdCBhbmQgYXNjZW5kaW5nIHRvIDYgY2x1c3RlcnMgaW4gdGhlIGxhc3QuIFRoZSBjbHVzdGVyaW5nIGlzIGF0IHJhbmRvbSBhbmQgbmVlZHMgdG8gYmUgc3BlY2lmaWVkIGJ5IHlvdSB3aGljaCBpcyB3aHkgaXQgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgY2FuIGhlbHAgaW4gZWxpbWluYXRpbmcgdGhlIGd1ZXNzaW5nIGFzIHRvIGhvdyBtYW55IGNsdXN0ZXJzIHN1aXRzIHlvdSwgbWF5YmUgZm9yIGRpbWVuc2lvbiByZWR1Y3Rpb24uDQo=