The below cells are for reading in and formatting the data in a single matrix. After the break is where the exciting stuff happens.


setwd("~/Documents/RobertsLab/Clusters")

list.files(path = ".")
library(data.table)
data.table 1.10.2
  The fastest way to learn (by data.table authors): https://www.datacamp.com/courses/data-analysis-the-data-table-way
  Documentation: ?data.table, example(data.table) and browseVignettes("data.table")
  Release notes, videos and slides: http://r-datatable.com

EPI_103_percmeth_bedgraph <- read_delim("~/Documents/RobertsLab/Clusters/EPI-103-percmeth.bedgraph.txt", 
    "\t", escape_double = FALSE, col_names = FALSE, 
    col_types = cols(X4 = col_double()), 
    trim_ws = TRUE)

EPI_103_percmeth_bedgraph <- as.data.table(EPI_103_percmeth_bedgraph)

head(EPI_103_percmeth_bedgraph)

print(EPI_103_percmeth_bedgraph[7,])

EPI_103_DMRs <- EPI_103_percmeth_bedgraph[EPI_103_percmeth_bedgraph$X1 %in% DiffMethRegions$X1,]


EPI_103_DMRs$Loc <- paste0(EPI_103_DMRs$X1, "-", EPI_103_DMRs$X2)


head(EPI_103_DMRs)

EPI_103_DMRs <- EPI_103_DMRs[EPI_103_DMRs$Loc %in% DiffMethRegions$Loc,]

head(EPI_103_DMRs)

EPI_DMRs <- as.data.table(cbind(EPI_103_DMRs$Loc, EPI_103_DMRs$X4))



colnames(EPI_DMRs)[1] <- "DMRLoc"
colnames(EPI_DMRs)[2] <- "EPI_103"
head(EPI_DMRs)

EPI_DMRs$EPI_103 <- as.numeric(EPI_DMRs$EPI_103)

for(i in 2:length(sample.files))   {
  
  temp <- read_delim(paste0("~/Documents/RobertsLab/Clusters/", sample.files[i]), 
    "\t", escape_double = FALSE, col_names = FALSE, 
    col_types = cols(X4 = col_double()), 
    trim_ws = TRUE)

  temp <- as.data.table(temp)
  
  temp_DMRs <- temp[temp$X1 %in% DiffMethRegions$X1,]


  temp_DMRs$Loc <- paste0(temp_DMRs$X1, "-", temp_DMRs$X2)


  temp_DMRs <- temp_DMRs[temp_DMRs$Loc %in% DiffMethRegions$Loc,]

  EPI_DMRs$temp <- temp_DMRs$X4
  
  colnames(EPI_DMRs)[ncol(EPI_DMRs)] <- substr(sample.files[i], 1, 7)
  
  
}

head(EPI_DMRs)

First I make a new data frame that’s re-ordered such that the 4 samples by treatment are together, as opposed to ordered by sample number.

DMRs <- as.data.frame(cbind(EPI_DMRs$DMRLoc, EPI_DMRs[,c(6,7,10,11, 2, 3, 8, 9, 4, 5, 12, 13)]))

Everything after this is looking at different ways to identify the proper number of clusters (K) for a K-means clustering algorithm.

First I want to try a silhouette method using a K-medoid method. This clusters data points by distance from a “medoid”, which is a data point who’s average dissimilarity from all the other data points in the cluster is minimized. This differs from the typical K-means clustering algoritm because it uses a data point as a center, as opposed to a centroid in K-means, which is just a point in space which may not have a data point associated with it.

K-medoid is generally considered to be more robust against noise and outliers, so I felt this would be a good route to try.

library(cluster)
library(fpc)
pamk.best <- pamk(as.matrix(DMRs[1:267,2:13]))
cat("number of clusters estimated by optimum average silhouette width:", pamk.best$nc, "\n")
number of clusters estimated by optimum average silhouette width: 2 
plot(pam(as.matrix(DMRs[1:267,2:13]), pamk.best$nc), main = "Silhouette plot for K-medoid clustering")

The first plot shows general grouping for two “clusters” of data, but lets delve further in to it, and look at our data graphed with two means. I’m a little confused by the second graph. What should show up are grey bars with distances between points in groups, but instead it’s… nothing. I’m not sure if thats an artifact of the data, or if it’s indicitive of a lack of true grouping.

set.seed(123)
kmeans2 <- kmeans(DMRs[,2:13], centers = 2, nstart = 100)
plot(0, type="n", xlim=c(0,13), ylim=range(0, 1.01))
for(i in 1:nrow(DMRs))  {
points(1:12, DMRs[i,2:13], type="b", col="gray", lwd=0.5)
}
abline(v=c(4.5, 8.5), lty=2, col="blue") #add vertical lines between treatment groups
mtext(side=3, at=2, "Ambient", cex=0.5) #add text to each treatment group
mtext(side=3, at=6.5, "Low", cex=0.5) #add text to each treatment group
mtext(side=3, at=11, "Super Low", cex=0.5) #add text to each treatment group
points(1:12, kmeans2$centers[1,], col = "red", type = "b", lwd = 3)
points(1:12, kmeans2$centers[2,], col = "red", type = "b", lwd = 3)

Thats a little hard to view raw, so lets scale and center it and re-run the k-medoid test.

That… looks nearly the same. Which I guess makes sense, as K-medoid is supposed to be robust against scale and outlier issues. Again, I’m not sure whats going on with the second graph, but will try and figure it out.

scaled.kmeans2 <- kmeans(scaled.data, centers = 2, nstart = 100)
plot(0, type="n", xlim=c(0,13), ylim=range(-3, 3))
for(i in 1:nrow(scaled.data))  {
points(1:12, scaled.data[i,1:12], type="b", col="gray", lwd=0.5)
}
abline(v=c(4.5, 8.5), lty=2, col="blue") #add vertical lines between treatment groups
mtext(side=3, at=2, "Ambient", cex=0.5) #add text to each treatment group
mtext(side=3, at=6.5, "Low", cex=0.5) #add text to each treatment group
mtext(side=3, at=11, "Super Low", cex=0.5) #add text to each treatment group
points(1:12, scaled.kmeans2$centers[1,], col = "red", type = "b", lwd = 3)
points(1:12, scaled.kmeans2$centers[2,], col = "red", type = "b", lwd = 3)

A second way to calculate the number of clusters is via a gap statistic. I’m first going to try it using a K-means algorithm, and see how it fares. I feed it the scaled data, because the gap statistic behaves better with centered data, because scale has a large impact on the effectiveness of clustering.

Well. That’s not good. What the graph shows is that K never converges on some acceptable number (shown by the gap statistic never flattening out as K increases). Lets try it with a K-medoid approach, just for fun.

gapstat <- clusGap(scaled.data, FUN = pam, K.max = 100, B = 500)
Clustering k = 1,2,..., K.max (= 100): .. done
Bootstrapping, b = 1,2,..., B (= 500)  [one "." per sample]:
.................................................. 50 
.................................................. 100 
.................................................. 150 
.................................................. 200 
.................................................. 250 
.................................................. 300 
.................................................. 350 
.................................................. 400 
.................................................. 450 
.................................................. 500 

Two and a half hours later… lets see the results!

plot(gapstat)

Welp, yeah. Again, no convergence on the gap statistic for any value of K between 0 and 100. Which means our data likely doesn’t cluster in any meaningful way.

Given that the gap statistics don’t converge for any meaningful number of groups, I’m going to say that there isn’t likely any clusters of effects?

LS0tCnRpdGxlOiAiQ2x1c3RlcmluZyBmb3IgZml4ZWQgRGF5IDEwIGRhdGEiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KClRoZSBiZWxvdyBjZWxscyBhcmUgZm9yIHJlYWRpbmcgaW4gYW5kIGZvcm1hdHRpbmcgdGhlIGRhdGEgaW4gYSBzaW5nbGUgbWF0cml4LiBBZnRlciB0aGUgYnJlYWsgaXMgd2hlcmUgdGhlIGV4Y2l0aW5nIHN0dWZmIGhhcHBlbnMuCgpgYGB7cn0KCnNldHdkKCJ+L0RvY3VtZW50cy9Sb2JlcnRzTGFiL0NsdXN0ZXJzIikKCmxpc3QuZmlsZXMocGF0aCA9ICIuIikKCmBgYAoKYGBge3J9CgpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkocmVhZHIpCkRpZmZNZXRoUmVnaW9ucyA8LSByZWFkX2RlbGltKCJ+L0RvY3VtZW50cy9Sb2JlcnRzTGFiL0NsdXN0ZXJzL0RheTEwLTEweENvdi1EaWZmTWV0aFJlZ2lvbnMuYmVkIiwgCiAgICAiXHQiLCBlc2NhcGVfZG91YmxlID0gRkFMU0UsIGNvbF9uYW1lcyA9IEZBTFNFLCAKICAgIHRyaW1fd3MgPSBUUlVFKQoKRGlmZk1ldGhSZWdpb25zJExvYyA8LSBwYXN0ZTAoRGlmZk1ldGhSZWdpb25zJFgxLCAiLSIsIERpZmZNZXRoUmVnaW9ucyRYMikKCmhlYWQoRGlmZk1ldGhSZWdpb25zKQoKc2FtcGxlLmZpbGVzIDwtIGxpc3QuZmlsZXMocGF0aCA9ICIuIiwgcGF0dGVybiA9ICIqYmVkZ3JhcGgudHh0IikKCnByaW50KHNhbXBsZS5maWxlcykKCmBgYAoKCmBgYHtyfQoKRVBJXzEwM19wZXJjbWV0aF9iZWRncmFwaCA8LSByZWFkX2RlbGltKCJ+L0RvY3VtZW50cy9Sb2JlcnRzTGFiL0NsdXN0ZXJzL0VQSS0xMDMtcGVyY21ldGguYmVkZ3JhcGgudHh0IiwgCiAgICAiXHQiLCBlc2NhcGVfZG91YmxlID0gRkFMU0UsIGNvbF9uYW1lcyA9IEZBTFNFLCAKICAgIGNvbF90eXBlcyA9IGNvbHMoWDQgPSBjb2xfZG91YmxlKCkpLCAKICAgIHRyaW1fd3MgPSBUUlVFKQoKRVBJXzEwM19wZXJjbWV0aF9iZWRncmFwaCA8LSBhcy5kYXRhLnRhYmxlKEVQSV8xMDNfcGVyY21ldGhfYmVkZ3JhcGgpCgpoZWFkKEVQSV8xMDNfcGVyY21ldGhfYmVkZ3JhcGgpCgpwcmludChFUElfMTAzX3BlcmNtZXRoX2JlZGdyYXBoWzcsXSkKCmBgYAoKYGBge3J9CgpFUElfMTAzX0RNUnMgPC0gRVBJXzEwM19wZXJjbWV0aF9iZWRncmFwaFtFUElfMTAzX3BlcmNtZXRoX2JlZGdyYXBoJFgxICVpbiUgRGlmZk1ldGhSZWdpb25zJFgxLF0KCgpFUElfMTAzX0RNUnMkTG9jIDwtIHBhc3RlMChFUElfMTAzX0RNUnMkWDEsICItIiwgRVBJXzEwM19ETVJzJFgyKQoKCmhlYWQoRVBJXzEwM19ETVJzKQoKRVBJXzEwM19ETVJzIDwtIEVQSV8xMDNfRE1Sc1tFUElfMTAzX0RNUnMkTG9jICVpbiUgRGlmZk1ldGhSZWdpb25zJExvYyxdCgpoZWFkKEVQSV8xMDNfRE1ScykKCkVQSV9ETVJzIDwtIGFzLmRhdGEudGFibGUoY2JpbmQoRVBJXzEwM19ETVJzJExvYywgRVBJXzEwM19ETVJzJFg0KSkKCgoKY29sbmFtZXMoRVBJX0RNUnMpWzFdIDwtICJETVJMb2MiCmNvbG5hbWVzKEVQSV9ETVJzKVsyXSA8LSAiRVBJXzEwMyIKaGVhZChFUElfRE1ScykKCkVQSV9ETVJzJEVQSV8xMDMgPC0gYXMubnVtZXJpYyhFUElfRE1ScyRFUElfMTAzKQpgYGAKCgpgYGB7cn0KCmZvcihpIGluIDI6bGVuZ3RoKHNhbXBsZS5maWxlcykpICAgewogIAogIHRlbXAgPC0gcmVhZF9kZWxpbShwYXN0ZTAoIn4vRG9jdW1lbnRzL1JvYmVydHNMYWIvQ2x1c3RlcnMvIiwgc2FtcGxlLmZpbGVzW2ldKSwgCiAgICAiXHQiLCBlc2NhcGVfZG91YmxlID0gRkFMU0UsIGNvbF9uYW1lcyA9IEZBTFNFLCAKICAgIGNvbF90eXBlcyA9IGNvbHMoWDQgPSBjb2xfZG91YmxlKCkpLCAKICAgIHRyaW1fd3MgPSBUUlVFKQoKICB0ZW1wIDwtIGFzLmRhdGEudGFibGUodGVtcCkKICAKICB0ZW1wX0RNUnMgPC0gdGVtcFt0ZW1wJFgxICVpbiUgRGlmZk1ldGhSZWdpb25zJFgxLF0KCgogIHRlbXBfRE1ScyRMb2MgPC0gcGFzdGUwKHRlbXBfRE1ScyRYMSwgIi0iLCB0ZW1wX0RNUnMkWDIpCgoKICB0ZW1wX0RNUnMgPC0gdGVtcF9ETVJzW3RlbXBfRE1ScyRMb2MgJWluJSBEaWZmTWV0aFJlZ2lvbnMkTG9jLF0KCiAgRVBJX0RNUnMkdGVtcCA8LSB0ZW1wX0RNUnMkWDQKICAKICBjb2xuYW1lcyhFUElfRE1ScylbbmNvbChFUElfRE1ScyldIDwtIHN1YnN0cihzYW1wbGUuZmlsZXNbaV0sIDEsIDcpCiAgCiAgCn0KCmhlYWQoRVBJX0RNUnMpCgpgYGAKCi0tLQoKRmlyc3QgSSBtYWtlIGEgbmV3IGRhdGEgZnJhbWUgdGhhdCdzIHJlLW9yZGVyZWQgc3VjaCB0aGF0IHRoZSA0IHNhbXBsZXMgYnkgdHJlYXRtZW50IGFyZSB0b2dldGhlciwgYXMgb3Bwb3NlZCB0byBvcmRlcmVkIGJ5IHNhbXBsZSBudW1iZXIuCmBgYHtyfQoKRE1ScyA8LSBhcy5kYXRhLmZyYW1lKGNiaW5kKEVQSV9ETVJzJERNUkxvYywgRVBJX0RNUnNbLGMoNiw3LDEwLDExLCAyLCAzLCA4LCA5LCA0LCA1LCAxMiwgMTMpXSkpCmBgYAoKRXZlcnl0aGluZyBhZnRlciB0aGlzIGlzIGxvb2tpbmcgYXQgZGlmZmVyZW50IHdheXMgdG8gaWRlbnRpZnkgdGhlIHByb3BlciBudW1iZXIgb2YgY2x1c3RlcnMgKEspIGZvciBhIEstbWVhbnMgY2x1c3RlcmluZyBhbGdvcml0aG0uCgpGaXJzdCBJIHdhbnQgdG8gdHJ5IGEgc2lsaG91ZXR0ZSBtZXRob2QgdXNpbmcgYSBLLW1lZG9pZCBtZXRob2QuIFRoaXMgY2x1c3RlcnMgZGF0YSBwb2ludHMgYnkgZGlzdGFuY2UgZnJvbSBhICJtZWRvaWQiLCB3aGljaCBpcyBhIGRhdGEgcG9pbnQgd2hvJ3MgYXZlcmFnZSBkaXNzaW1pbGFyaXR5IGZyb20gYWxsIHRoZSBvdGhlciBkYXRhIHBvaW50cyBpbiB0aGUgY2x1c3RlciBpcyBtaW5pbWl6ZWQuIFRoaXMgZGlmZmVycyBmcm9tIHRoZSB0eXBpY2FsIEstbWVhbnMgY2x1c3RlcmluZyBhbGdvcml0bSBiZWNhdXNlIGl0IHVzZXMgYSBkYXRhIHBvaW50IGFzIGEgY2VudGVyLCBhcyBvcHBvc2VkIHRvIGEgY2VudHJvaWQgaW4gSy1tZWFucywgd2hpY2ggaXMganVzdCBhIHBvaW50IGluIHNwYWNlIHdoaWNoIG1heSBub3QgaGF2ZSBhIGRhdGEgcG9pbnQgYXNzb2NpYXRlZCB3aXRoIGl0LgoKSy1tZWRvaWQgaXMgZ2VuZXJhbGx5IGNvbnNpZGVyZWQgdG8gYmUgbW9yZSByb2J1c3QgYWdhaW5zdCBub2lzZSBhbmQgb3V0bGllcnMsIHNvIEkgZmVsdCB0aGlzIHdvdWxkIGJlIGEgZ29vZCByb3V0ZSB0byB0cnkuCgpgYGB7cn0KbGlicmFyeShjbHVzdGVyKQpsaWJyYXJ5KGZwYykKcGFtay5iZXN0IDwtIHBhbWsoYXMubWF0cml4KERNUnNbMToyNjcsMjoxM10pKQoKY2F0KCJudW1iZXIgb2YgY2x1c3RlcnMgZXN0aW1hdGVkIGJ5IG9wdGltdW0gYXZlcmFnZSBzaWxob3VldHRlIHdpZHRoOiIsIHBhbWsuYmVzdCRuYywgIlxuIikKCnBsb3QocGFtKGFzLm1hdHJpeChETVJzWzE6MjY3LDI6MTNdKSwgcGFtay5iZXN0JG5jKSwgbWFpbiA9ICJTaWxob3VldHRlIHBsb3QgZm9yIEstbWVkb2lkIGNsdXN0ZXJpbmciKQoKYGBgCgpUaGUgZmlyc3QgcGxvdCBzaG93cyBnZW5lcmFsIGdyb3VwaW5nIGZvciB0d28gImNsdXN0ZXJzIiBvZiBkYXRhLCBidXQgbGV0cyBkZWx2ZSBmdXJ0aGVyIGluIHRvIGl0LCBhbmQgbG9vayBhdCBvdXIgZGF0YSBncmFwaGVkIHdpdGggdHdvIG1lYW5zLiBJJ20gYSBsaXR0bGUgY29uZnVzZWQgYnkgdGhlIHNlY29uZCBncmFwaC4gV2hhdCBzaG91bGQgc2hvdyB1cCBhcmUgZ3JleSBiYXJzIHdpdGggZGlzdGFuY2VzIGJldHdlZW4gcG9pbnRzIGluIGdyb3VwcywgYnV0IGluc3RlYWQgaXQncy4uLiBub3RoaW5nLiBJJ20gbm90IHN1cmUgaWYgdGhhdHMgYW4gYXJ0aWZhY3Qgb2YgdGhlIGRhdGEsIG9yIGlmIGl0J3MgaW5kaWNpdGl2ZSBvZiBhIGxhY2sgb2YgdHJ1ZSBncm91cGluZy4KCmBgYHtyfQoKc2V0LnNlZWQoMTIzKQoKa21lYW5zMiA8LSBrbWVhbnMoRE1Sc1ssMjoxM10sIGNlbnRlcnMgPSAyLCBuc3RhcnQgPSAxMDApCgpwbG90KDAsIHR5cGU9Im4iLCB4bGltPWMoMCwxMyksIHlsaW09cmFuZ2UoMCwgMS4wMSkpCgpmb3IoaSBpbiAxOm5yb3coRE1ScykpICB7CnBvaW50cygxOjEyLCBETVJzW2ksMjoxM10sIHR5cGU9ImIiLCBjb2w9ImdyYXkiLCBsd2Q9MC41KQoKfQphYmxpbmUodj1jKDQuNSwgOC41KSwgbHR5PTIsIGNvbD0iYmx1ZSIpICNhZGQgdmVydGljYWwgbGluZXMgYmV0d2VlbiB0cmVhdG1lbnQgZ3JvdXBzCm10ZXh0KHNpZGU9MywgYXQ9MiwgIkFtYmllbnQiLCBjZXg9MC41KSAjYWRkIHRleHQgdG8gZWFjaCB0cmVhdG1lbnQgZ3JvdXAKbXRleHQoc2lkZT0zLCBhdD02LjUsICJMb3ciLCBjZXg9MC41KSAjYWRkIHRleHQgdG8gZWFjaCB0cmVhdG1lbnQgZ3JvdXAKbXRleHQoc2lkZT0zLCBhdD0xMSwgIlN1cGVyIExvdyIsIGNleD0wLjUpICNhZGQgdGV4dCB0byBlYWNoIHRyZWF0bWVudCBncm91cAoKCnBvaW50cygxOjEyLCBrbWVhbnMyJGNlbnRlcnNbMSxdLCBjb2wgPSAicmVkIiwgdHlwZSA9ICJiIiwgbHdkID0gMykKcG9pbnRzKDE6MTIsIGttZWFuczIkY2VudGVyc1syLF0sIGNvbCA9ICJyZWQiLCB0eXBlID0gImIiLCBsd2QgPSAzKQpgYGAKClRoYXRzIGEgbGl0dGxlIGhhcmQgdG8gdmlldyByYXcsIHNvIGxldHMgc2NhbGUgYW5kIGNlbnRlciBpdCBhbmQgcmUtcnVuIHRoZSBrLW1lZG9pZCB0ZXN0LgoKYGBge3J9CgpzY2FsZWQuZGF0YSA8LSBzY2FsZShhcy5tYXRyaXgoRE1Sc1sxOjI2NywyOjEzXSksIGNlbnRlciA9IFRSVUUsIHNjYWxlID0gVFJVRSkKCnNjYWxlZC5wYW1rIDwtIHBhbWsoc2NhbGVkLmRhdGEpCgpjYXQoIm51bWJlciBvZiBjbHVzdGVycyBlc3RpbWF0ZWQgYnkgb3B0aW11bSBhdmVyYWdlIHNpbGhvdWV0dGUgd2lkdGg6Iiwgc2NhbGVkLnBhbWskbmMsICJcbiIpCgpwbG90KHBhbShzY2FsZWQuZGF0YSwgc2NhbGVkLnBhbWskbmMpLCBtYWluID0gIlNpbGhvdWV0dGUgcGxvdCBmb3IgSy1tZWRvaWQgY2x1c3RlcmluZyIpCgpgYGAKClRoYXQuLi4gbG9va3MgbmVhcmx5IHRoZSBzYW1lLiBXaGljaCBJIGd1ZXNzIG1ha2VzIHNlbnNlLCBhcyBLLW1lZG9pZCBpcyBzdXBwb3NlZCB0byBiZSByb2J1c3QgYWdhaW5zdCBzY2FsZSBhbmQgb3V0bGllciBpc3N1ZXMuIEFnYWluLCBJJ20gbm90IHN1cmUgd2hhdHMgZ29pbmcgb24gd2l0aCB0aGUgc2Vjb25kIGdyYXBoLCBidXQgd2lsbCB0cnkgYW5kIGZpZ3VyZSBpdCBvdXQuCgpgYGB7cn0KCnNjYWxlZC5rbWVhbnMyIDwtIGttZWFucyhzY2FsZWQuZGF0YSwgY2VudGVycyA9IDIsIG5zdGFydCA9IDEwMCkKCnBsb3QoMCwgdHlwZT0ibiIsIHhsaW09YygwLDEzKSwgeWxpbT1yYW5nZSgtMywgMykpCgpmb3IoaSBpbiAxOm5yb3coc2NhbGVkLmRhdGEpKSAgewpwb2ludHMoMToxMiwgc2NhbGVkLmRhdGFbaSwxOjEyXSwgdHlwZT0iYiIsIGNvbD0iZ3JheSIsIGx3ZD0wLjUpCgp9CmFibGluZSh2PWMoNC41LCA4LjUpLCBsdHk9MiwgY29sPSJibHVlIikgI2FkZCB2ZXJ0aWNhbCBsaW5lcyBiZXR3ZWVuIHRyZWF0bWVudCBncm91cHMKbXRleHQoc2lkZT0zLCBhdD0yLCAiQW1iaWVudCIsIGNleD0wLjUpICNhZGQgdGV4dCB0byBlYWNoIHRyZWF0bWVudCBncm91cAptdGV4dChzaWRlPTMsIGF0PTYuNSwgIkxvdyIsIGNleD0wLjUpICNhZGQgdGV4dCB0byBlYWNoIHRyZWF0bWVudCBncm91cAptdGV4dChzaWRlPTMsIGF0PTExLCAiU3VwZXIgTG93IiwgY2V4PTAuNSkgI2FkZCB0ZXh0IHRvIGVhY2ggdHJlYXRtZW50IGdyb3VwCgoKcG9pbnRzKDE6MTIsIHNjYWxlZC5rbWVhbnMyJGNlbnRlcnNbMSxdLCBjb2wgPSAicmVkIiwgdHlwZSA9ICJiIiwgbHdkID0gMykKcG9pbnRzKDE6MTIsIHNjYWxlZC5rbWVhbnMyJGNlbnRlcnNbMixdLCBjb2wgPSAicmVkIiwgdHlwZSA9ICJiIiwgbHdkID0gMykKCmBgYAoKQSBzZWNvbmQgd2F5IHRvIGNhbGN1bGF0ZSB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzIGlzIHZpYSBhIGdhcCBzdGF0aXN0aWMuIEknbSBmaXJzdCBnb2luZyB0byB0cnkgaXQgdXNpbmcgYSBLLW1lYW5zIGFsZ29yaXRobSwgYW5kIHNlZSBob3cgaXQgZmFyZXMuIEkgZmVlZCBpdCB0aGUgc2NhbGVkIGRhdGEsIGJlY2F1c2UgdGhlIGdhcCBzdGF0aXN0aWMgYmVoYXZlcyBiZXR0ZXIgd2l0aCBjZW50ZXJlZCBkYXRhLCBiZWNhdXNlIHNjYWxlIGhhcyBhIGxhcmdlIGltcGFjdCBvbiB0aGUgZWZmZWN0aXZlbmVzcyBvZiBjbHVzdGVyaW5nLgoKYGBge3J9CgpnYXBzdGF0IDwtIGNsdXNHYXAoc2NhbGVkLmRhdGEsIEZVTiA9IGttZWFucywgbnN0YXJ0ID0gMjUsIEsubWF4ID0gMTAwLCBCID0gNTAwKQoKcGxvdChnYXBzdGF0KQoKYGBgCgpXZWxsLiBUaGF0J3Mgbm90IGdvb2QuIFdoYXQgdGhlIGdyYXBoIHNob3dzIGlzIHRoYXQgSyBuZXZlciBjb252ZXJnZXMgb24gc29tZSBhY2NlcHRhYmxlIG51bWJlciAoc2hvd24gYnkgdGhlIGdhcCBzdGF0aXN0aWMgbmV2ZXIgZmxhdHRlbmluZyBvdXQgYXMgSyBpbmNyZWFzZXMpLiBMZXRzIHRyeSBpdCB3aXRoIGEgSy1tZWRvaWQgYXBwcm9hY2gsIGp1c3QgZm9yIGZ1bi4KCgpgYGB7cn0KCmdhcHN0YXQgPC0gY2x1c0dhcChzY2FsZWQuZGF0YSwgRlVOID0gcGFtLCBLLm1heCA9IDEwMCwgQiA9IDUwMCkKCgoKYGBgCgpUd28gYW5kIGEgaGFsZiBob3VycyBsYXRlci4uLiBsZXRzIHNlZSB0aGUgcmVzdWx0cyEKCmBgYHtyfQoKcGxvdChnYXBzdGF0KQoKYGBgCgpXZWxwLCB5ZWFoLiBBZ2Fpbiwgbm8gY29udmVyZ2VuY2Ugb24gdGhlIGdhcCBzdGF0aXN0aWMgZm9yIGFueSB2YWx1ZSBvZiBLIGJldHdlZW4gMCBhbmQgMTAwLiBXaGljaCBtZWFucyBvdXIgZGF0YSBsaWtlbHkgZG9lc24ndCBjbHVzdGVyIGluIGFueSBtZWFuaW5nZnVsIHdheS4KCkdpdmVuIHRoYXQgdGhlIGdhcCBzdGF0aXN0aWNzIGRvbid0IGNvbnZlcmdlIGZvciBhbnkgbWVhbmluZ2Z1bCBudW1iZXIgb2YgZ3JvdXBzLCBJJ20gZ29pbmcgdG8gc2F5IHRoYXQgdGhlcmUgaXNuJ3QgbGlrZWx5IGFueSBjbHVzdGVycyBvZiBlZmZlY3RzPyAKCg==