Rmarkdown notebook for examining DWI QC.

We’re starting by using the “eddy_outlier_n_stdev_map” from the EDDY documentation: “The numbers denote how many standard deviations off the mean difference between observation and prediction is.” By “the prediction”, I’m assuming they’re talking about the diffusion model. This is similar (in principle to the residuals of the tensor model). In short, people who are “noisy” tend to have more consistently higher SD values across all volumes rather than a few.

In examining these data, I found that collapsing the data into volumes/directions by taking the standard deviation across slices worked well. This essentially asks what is the mean distance of each slice from every other slice? As can be seen below, people with noisier brains are more similar to each other (smaller distance from each other - see distance matrix).

Data are given per person in a matrix where each column represents a slice, and each row represents a scan. Therefore for the POND data, we have a 69 (scans) x 140 (slices). The following code will need to be re-written to be flexibly applied to all datasets (i.e. scan and volume numbers are hard-coded at the moment).

Dependencies:

library(tidyverse)  # data manipulation
library(cluster)    # clustering algorithms
library(factoextra) # clustering algorithms & visualization
library(reshape2)
library(ggplot2)
library(gplots) 
library(readbulk)
library(ggthemr)
library(ggpubr)
library(doBy)
library(Rmisc)
library(dendextend)
library(dendextendRcpp)
library(kableExtra)
ggthemr("chalk")

#function to prepare SD files for analysis
QC_DWI_Prep <- function(x){
  input <- read_bulk(x, skip = 1, header =FALSE, sep =' ')
  input$V141 <- NULL
  data <- melt(input, id.vars = "File")
  #this function (dcast) combines the data by calculating an SD per volume across slices.  
  #Thus, we have a measure of how variable the variability is per volume.  
  b <- dcast(data, variable ~ File, fun.aggregate = sd, na.rm = TRUE)
  b$variable <- NULL
  d <- t(b)
  ds <- scale(d)
  my_list <- list(d, ds)
return(my_list)
}

First read in the data using “readbulk”:

First just look at the distance matrix

#distance matrix visualization
distance <- get_dist(ds)
fviz_dist(distance, gradient = list(low = "#00AFBB", mid = "white", high = "#FC4E07"))

Now try Ward’s method for clustering

# Ward Hierarchical Clustering
d <- dist(d, method = "euclidean") # distance matrix
fit <- hclust(d, method="ward") 
groups <- cutree(fit, k=5) # cut tree into 5 clusters
# draw dendogram with red borders around the 5 clusters 
# using piping to get the dend
dend <- fit %>% as.dendrogram

# plot + color the dend's branches before, based on 3 clusters:


par(mar = c(2,2,2,15))

dend %>% color_branches(k=5) %>% plot(horiz=TRUE)

# add horiz rect
#dend %>% rect.dendrogram(k=5,horiz=TRUE)

# add horiz (well, vertical) line:
text(50, 10, "bad \n participants", col = "#C84D6F")
text(50, 27, "good \n participants", col = "#9C4DCC")
text(50, 17, "?")

Now try a K-means solution

#Now to try a K-means solution 

k2 <- kmeans(ds, centers = 2, nstart = 25)
k3 <- kmeans(ds, centers = 3, nstart = 25)
k4 <- kmeans(ds, centers = 4, nstart = 25)
k5 <- kmeans(ds, centers = 5, nstart = 25)
k10 <- kmeans(ds, centers = 10, nstart = 25)

# plots to compare
p1 <- fviz_cluster(k2, geom = "point", data = ds) + ggtitle("k = 2")
p2 <- fviz_cluster(k3, geom = "point",  data = ds) + ggtitle("k = 3")
p3 <- fviz_cluster(k4, geom = "point",  data = ds) + ggtitle("k = 4")
p4 <- fviz_cluster(k5, geom = "point",  data = ds) + ggtitle("k = 5")
#p10 <- fviz_cluster(k10, geom = "point",  data = d) + ggtitle("k = 10")

library(gridExtra)
grid.arrange(p1, p2, p3, p4, nrow = 2)

fviz_nbclust(ds, kmeans, method = "silhouette")

fviz_nbclust(ds, kmeans, method = "wss")

gap_stat <- clusGap(ds, FUN = kmeans, nstart = 25,
                    K.max = 10, B = 50)
fviz_gap_stat(gap_stat)

#Fuzzy K-Means clustering (2 groups) This approximates what Hajer is doing I think (trying to make a binary decision, but providing us with probabities).  I'd look at any person who gets a score of less than .9 for either category.
fannyd <- fanny(ds, k=2, metric = "euclidean", memb.exp = 1.2)
round(fannyd$membership, 2)
FALSE                                 [,1] [,2]
FALSE sub-0880043_outlier_n_stdev_map 1.00 0.00
FALSE sub-0880107_outlier_n_stdev_map 0.81 0.19
FALSE sub-0880138_outlier_n_stdev_map 1.00 0.00
FALSE sub-0880397_outlier_n_stdev_map 0.52 0.48
FALSE sub-0880418_outlier_n_stdev_map 1.00 0.00
FALSE sub-0880464_outlier_n_stdev_map 1.00 0.00
FALSE sub-0880473_outlier_n_stdev_map 0.24 0.76
FALSE sub-0880533_outlier_n_stdev_map 0.98 0.02
FALSE sub-0880558_outlier_n_stdev_map 0.99 0.01
FALSE sub-0880601_outlier_n_stdev_map 0.99 0.01
FALSE sub-0880624_outlier_n_stdev_map 1.00 0.00
FALSE sub-0880703_outlier_n_stdev_map 0.36 0.64
FALSE sub-1050015_outlier_n_stdev_map 0.32 0.68
FALSE sub-1050019_outlier_n_stdev_map 0.09 0.91
FALSE sub-1050032_outlier_n_stdev_map 0.26 0.74
FALSE sub-1050054_outlier_n_stdev_map 0.15 0.85
FALSE sub-1050065_outlier_n_stdev_map 1.00 0.00
FALSE sub-1050090_outlier_n_stdev_map 1.00 0.00
FALSE sub-1050105_outlier_n_stdev_map 0.98 0.02
FALSE sub-1050131_outlier_n_stdev_map 0.21 0.79
FALSE sub-1050179_outlier_n_stdev_map 0.04 0.96
FALSE sub-1050235_outlier_n_stdev_map 0.99 0.01
FALSE sub-1050250_outlier_n_stdev_map 0.20 0.80
FALSE sub-1050253_outlier_n_stdev_map 0.03 0.97
FALSE sub-1050349_outlier_n_stdev_map 0.79 0.21
FALSE sub-1050353_outlier_n_stdev_map 0.02 0.98
FALSE sub-1050378_outlier_n_stdev_map 0.02 0.98
FALSE sub-1050402_outlier_n_stdev_map 0.99 0.01
FALSE sub-1050429_outlier_n_stdev_map 0.08 0.92
FALSE sub-1050431_outlier_n_stdev_map 1.00 0.00
#Clustering cluster sets with Jaccard index - how similar are the cluster solutions?
source("http://faculty.ucr.edu/~tgirke/Documents/R_BioCond/My_R_Scripts/clusterIndex.R") 
clVlist <- lapply(2:12, function(x) clara(ds, k=x)$clustering); names(clVlist) <- paste("k", "=", 2:12)
e <- sapply(names(clVlist), function(x) sapply(names(clVlist), function(ds) cindex(clV1=clVlist[[ds]], clV2=clVlist[[x]], method="jaccard")[[3]]))
hv <- hclust(as.dist(1-e))
plot(as.dendrogram(hv), edgePar=list(col=3, lwd=4), horiz=T, main="Similarities of 10 Clara Clustering Results for k: 2-12") 

So, it looks like 2-3 clusters are optimal

Let’s plot the 3-groups from the hclust (Ward’s solution) - we can try other versions later

#should likely re-write this to take the clusters from the other solutions rather than hard-coding it to be subdirectories (use the %in% function from dplyr)

group3 <-"/Users/John/Dropbox/qc_solution/group3"
group2 <- "/Users/John/Dropbox/qc_solution/group2"
group1 <-"/Users/John/Dropbox/qc_solution/group1"

QC_DWI_Plot <- function(x){
input <- read_bulk(x, skip = 1, header =FALSE, sep =' ')
input$V141 <- NULL
rows <- as.data.frame(rep(c(0:68), times = length(unique(input$File))))
colnames(rows) <- "rows"
data <- cbind(input, rows)
group_L <- melt(data, id.vars = c("File","rows"))
group_L$variable <- as.numeric(substring(group_L$variable, 2))
return(group_L)
}


group1_L <- QC_DWI_Plot(group1)
group2_L <- QC_DWI_Plot(group2)
group3_L <- QC_DWI_Plot(group3)
all_L <- QC_DWI_Plot("/Users/John/Dropbox/qc_solution/qc_stdev_files")

group1_plot <- ggplot(group1_L, aes(rows, variable)) +
geom_raster(aes(fill = value))+facet_grid(.~File)+ 
  scale_y_continuous(breaks=seq(0,140,10))+
  labs(x = "volume|directions", y = "slice")
group2_plot <- ggplot(group2_L, aes(rows, variable)) +
geom_raster(aes(fill = value))+facet_grid(.~File)+ 
  scale_y_continuous(breaks=seq(0,140,10))+
  labs(x = "volume|directions", y = "slice")
group3_plot <- ggplot(group3_L, aes(rows, variable)) +
geom_raster(aes(fill = value))+facet_grid(.~File)+ 
  scale_y_continuous(breaks=seq(0,140,10))+
  labs(x = "volume|directions", y = "slice")


ggarrange(group1_plot, group2_plot, group3_plot, nrow=3, ncol =1, labels = c("Group1", "Group2","Group3"), common.legend = TRUE, widths = c(1, 0.64, 0.5))

ggplot() + 
  geom_histogram(data = group1_L, aes(x = value), color = "green", fill = "green") +
  geom_histogram(data = group2_L, aes(x = value), color = "blue", fill = "blue") + 
  geom_histogram(data = group3_L, aes(x = value), color = "red", fill = "red")+
  xlim(-3,3)

Trainin a classifier?

library(caret)
library(MASS)

pond_all_training <- read_csv("/Users/John/Dropbox/qc_solution/pond_all_training.csv")

TestRows     <- subset(pond_all_training, Training =="No")
TrainData    <- subset(pond_all_training, Training =="Yes")
TrainClasses <- as.data.frame(TrainData[,142])
TrainData    <- TrainData[,2:141]
TestData     <- TestRows[,2:141]
TestSubs     <- TestRows[,1]
#TestClasses  <- iris[TestRows,5]

TrainClasses <- factor(TrainClasses$Group, labels = c("group_1", "group_2","group_3"))

nnetFit <- train(x=TrainData, y=TrainClasses,
                 method = "nnet",
                 preProcess = "range", 
                 tuneLength = 2,
                 trace = FALSE,
                 maxit = 100)

predicted <-as.data.frame(predict(nnetFit, TestData))
test_preds <- cbind(TestSubs, predicted)
kable(test_preds)
File predict(nnetFit, TestData)
sub-0880002 group_1
sub-0880019 group_2
sub-0880020 group_2
sub-0880023 group_1
sub-0880044 group_1
sub-0880046 group_1
sub-0880049 group_1
sub-0880050 group_1
sub-0880061 group_1
sub-0880062 group_1
sub-0880079 group_1
sub-0880081 group_2
sub-0880082 group_1
sub-0880085 group_3
sub-0880096 group_1
sub-0880099 group_1
sub-0880100 group_1
sub-0880102 group_1
sub-0880112 group_1
sub-0880114 group_2
sub-0880136 group_1
sub-0880137 group_1
sub-0880139 group_1
sub-0880140 group_2
sub-0880146 group_3
sub-0880152 group_1
sub-0880155 group_1
sub-0880157 group_1
sub-0880167 group_1
sub-0880168 group_1
sub-0880175 group_1
sub-0880182 group_1
sub-0880195 group_1
sub-0880205 group_2
sub-0880208 group_2
sub-0880215 group_1
sub-0880221 group_1
sub-0880229 group_1
sub-0880247 group_2
sub-0880250 group_1
sub-0880252 group_1
sub-0880253 group_1
sub-0880254 group_1
sub-0880255 group_3
sub-0880258 group_1
sub-0880265 group_1
sub-0880276 group_1
sub-0880278 group_1
sub-0880279 group_1
sub-0880284 group_1
sub-0880291 group_2
sub-0880293 group_1
sub-0880303 group_1
sub-0880304 group_1
sub-0880305 group_1
sub-0880308 group_2
sub-0880323 group_3
sub-0880325 group_1
sub-0880328 group_2
sub-0880338 group_1
sub-0880349 group_1
sub-0880351 group_1
sub-0880353 group_1
sub-0880369 group_1
sub-0880370 group_1
sub-0880372 group_1
sub-0880381 group_1
sub-0880386 group_1
sub-0880396 group_1
sub-0880405 group_1
sub-0880409 group_1
sub-0880415 group_1
sub-0880419 group_1
sub-0880428 group_1
sub-0880432 group_2
sub-0880436 group_1
sub-0880447 group_1
sub-0880454 group_1
sub-0880461 group_1
sub-0880462 group_1
sub-0880470 group_1
sub-0880476 group_1
sub-0880481 group_1
sub-0880494 group_1
sub-0880514 group_2
sub-0880515 group_1
sub-0880536 group_1
sub-0880539 group_1
sub-0880552 group_1
sub-0880555 group_1
sub-0880582 group_1
sub-0880584 group_1
sub-0880590 group_1
sub-0880620 group_1
sub-0880642 group_1
sub-0880644 group_1
sub-0880650 group_1
sub-0880663 group_1
sub-0880664 group_1
sub-0880665 group_1
sub-0880669 group_1
sub-0880686 group_1
sub-0880687 group_3
sub-0880688 group_1
sub-0880689 group_1
sub-0880692 group_1
sub-0880693 group_1
sub-0880696 group_2
sub-0880720 group_1
sub-0880738 group_1
sub-0880742 group_1
sub-0880745 group_1
sub-0880759 group_2
sub-0880763 group_1
sub-0880770 group_3
sub-0880785 group_1
sub-0880794 group_3
sub-0880825 group_1
sub-0880856 group_2
sub-0880861 group_2
sub-0880862 group_3
sub-0880863 group_1
sub-0880864 group_1
sub-0880865 group_1
sub-0880869 group_1
sub-0880870 group_1
sub-0880871 group_1
sub-0880872 group_2
sub-0880873 group_3
sub-0880874 group_1
sub-0880881 group_1
sub-0880882 group_1
sub-0880895 group_2
sub-0880896 group_1
sub-0880898 group_1
sub-0880912 group_1
sub-0880913 group_1
sub-0880915 group_2
sub-0880927 group_1
sub-0880928 group_1
sub-0880929 group_1
sub-0880930 group_1
sub-0880931 group_2
sub-0880935 group_1
sub-0880938 group_1
sub-0880941 group_1
sub-0880960 group_2
sub-0880964 group_1
sub-0885004 group_1
sub-0885005 group_3
sub-1050004 group_1
sub-1050007 group_1
sub-1050026 group_1
sub-1050027 group_1
sub-1050029 group_2
sub-1050038 group_2
sub-1050039 group_1
sub-1050040 group_1
sub-1050051 group_2
sub-1050053 group_1
sub-1050055 group_1
sub-1050056 group_1
sub-1050064 group_1
sub-1050070 group_1
sub-1050077 group_2
sub-1050089 group_1
sub-1050092 group_1
sub-1050093 group_1
sub-1050097 group_1
sub-1050098 group_1
sub-1050100 group_1
sub-1050101 group_1
sub-1050102 group_1
sub-1050108 group_1
sub-1050111 group_1
sub-1050112 group_1
sub-1050125 group_3
sub-1050128 group_1
sub-1050132 group_1
sub-1050134 group_1
sub-1050135 group_1
sub-1050136 group_1
sub-1050137 group_1
sub-1050142 group_2
sub-1050145 group_1
sub-1050150 group_1
sub-1050158 group_1
sub-1050165 group_1
sub-1050167 group_2
sub-1050172 group_1
sub-1050173 group_1
sub-1050174 group_1
sub-1050178 group_1
sub-1050180 group_1
sub-1050182 group_1
sub-1050184 group_3
sub-1050188 group_3
sub-1050189 group_1
sub-1050192 group_1
sub-1050194 group_1
sub-1050195 group_1
sub-1050198 group_1
sub-1050207 group_3
sub-1050211 group_1
sub-1050212 group_3
sub-1050213 group_1
sub-1050215 group_2
sub-1050218 group_1
sub-1050219 group_1
sub-1050221 group_1
sub-1050222 group_1
sub-1050224 group_1
sub-1050225 group_1
sub-1050229 group_1
sub-1050230 group_1
sub-1050232 group_2
sub-1050234 group_1
sub-1050239 group_1
sub-1050241 group_1
sub-1050242 group_1
sub-1050247 group_1
sub-1050248 group_2
sub-1050249 group_1
sub-1050254 group_1
sub-1050257 group_1
sub-1050258 group_1
sub-1050261 group_1
sub-1050265 group_1
sub-1050268 group_1
sub-1050273 group_3
sub-1050277 group_1
sub-1050280 group_1
sub-1050281 group_1
sub-1050284 group_1
sub-1050286 group_1
sub-1050287 group_1
sub-1050288 group_1
sub-1050289 group_1
sub-1050292 group_1
sub-1050296 group_2
sub-1050298 group_2
sub-1050307 group_1
sub-1050308 group_1
sub-1050320 group_1
sub-1050321 group_3
sub-1050322 group_3
sub-1050325 group_2
sub-1050331 group_2
sub-1050334 group_1
sub-1050335 group_1
sub-1050336 group_1
sub-1050337 group_1
sub-1050340 group_1
sub-1050341 group_1
sub-1050342 group_1
sub-1050344 group_2
sub-1050345 group_1
sub-1050363 group_1
sub-1050368 group_1
sub-1050369 group_1
sub-1050371 group_2
sub-1050373 group_1
sub-1050375 group_3
sub-1050383 group_1
sub-1050385 group_1
sub-1050389 group_1
sub-1050393 group_2
sub-1050395 group_1
sub-1050398 group_1
sub-1050399 group_1
sub-1050401 group_1
sub-1050403 group_2
sub-1050404 group_3
sub-1050414 group_1
sub-1050420 group_1
sub-1050421 group_1
sub-1050422 group_1
sub-1050427 group_1
sub-1050428 group_1
sub-1050432 group_1
sub-1050438 group_1
sub-1050440 group_1
sub-1050445 group_3
sub-1050446 group_2
sub-1050447 group_1
sub-1050448 group_3
sub-1050451 group_1
sub-1050452 group_1
sub-1050457 group_1
sub-1050460 group_1
sub-1050461 group_1
sub-1050462 group_1
sub-1050467 group_3
sub-1050468 group_3
sub-1050486 group_1
sub-1050487 group_1
sub-1050488 group_1
sub-1050495 group_1
sub-1050496 group_2
sub-1050497 group_3
sub-1050500 group_1
sub-1050502 group_1
sub-1050505 group_1
sub-1050506 group_1
sub-1050509 group_1
sub-1050510 group_1
sub-1050512 group_3
sub-1050513 group_1
sub-1050516 group_1
sub-1050517 group_1
sub-1050518 group_3
sub-1050523 group_1
sub-1050525 group_3
sub-1050526 group_1
sub-1050528 group_2
sub-1050531 group_1
sub-1050532 group_1
sub-1050533 group_1
sub-1050543 group_1
sub-1050544 group_1
sub-1050545 group_3
sub-1050546 group_1
sub-1050547 group_3
sub-1050548 group_3
sub-1050551 group_1
sub-1050552 group_2
sub-1050556 group_1
sub-1050557 group_1
sub-1050561 group_1
sub-1050563 group_3
sub-1050570 group_2
sub-1050577 group_2
sub-1050579 group_3
sub-1050580 group_3
sub-1050582 group_1
sub-1050586 group_3
sub-1050591 group_3
sub-1050593 group_1
sub-1050596 group_1
sub-1050598 group_1
sub-1050600 group_1
sub-1050604 group_2
sub-1050607 group_2
sub-1050608 group_3
sub-1050613 group_1
sub-1050614 group_2
sub-1050615 group_1
sub-1050619 group_1
sub-1050621 group_1
sub-1050627 group_2
sub-1050628 group_1
sub-1050630 group_1
sub-1050633 group_1
sub-1050636 group_1
sub-1050639 group_3
sub-1050643 group_1
sub-1050644 group_1
sub-1050646 group_1
sub-1050647 group_1
sub-1050648 group_1
sub-1050649 group_1
sub-1050650 group_1
sub-1050655 group_2
sub-1050658 group_1
sub-1050659 group_1
sub-1050660 group_2
sub-1050662 group_1
sub-1050665 group_1
sub-1050666 group_3
sub-1050672 group_3
sub-1050680 group_1
sub-1050681 group_1
sub-1050687 group_1
sub-1050689 group_1
sub-1050698 group_1
sub-1050700 group_3
sub-1050701 group_3
sub-1050704 group_2
sub-1130131 group_3
sub-1130160 group_1
sub-1130202 group_1
sub-1130211 group_1
sub-1130217 group_1
sub-1130227 group_1
sub-1140078 group_3
sub-3940103 group_1
sub-3940114 group_1
sub-3940137 group_1
sub-3940147 group_1
sub-3940184 group_1
sub-3940185 group_1
sub-3940186 group_1
sub-3940189 group_1
sub-3940193 group_1
sub-3940195 group_1
sub-3940196 group_1
sub-3940203 group_1
table(TrainClasses, predict(nnetFit)) 
FALSE             
FALSE TrainClasses group_1 group_2 group_3
FALSE      group_1      14       0       0
FALSE      group_2       0       9       0
FALSE      group_3       0       0       7
#table(TestClasses,  predict(nnetFit,TestData))  

#visualization
LS0tCnRpdGxlOiAiRFdJIFFDIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0aGVtZTogdW5pdGVkCgotLS0KCiMjUm1hcmtkb3duIG5vdGVib29rIGZvciBleGFtaW5pbmcgRFdJIFFDLgoKV2UncmUgc3RhcnRpbmcgYnkgdXNpbmcgdGhlICJlZGR5X291dGxpZXJfbl9zdGRldl9tYXAiIGZyb20gdGhlIEVERFkgZG9jdW1lbnRhdGlvbjogIlRoZSBudW1iZXJzIGRlbm90ZSBob3cgbWFueSBzdGFuZGFyZCBkZXZpYXRpb25zIG9mZiB0aGUgbWVhbiBkaWZmZXJlbmNlIGJldHdlZW4gb2JzZXJ2YXRpb24gYW5kIHByZWRpY3Rpb24gaXMuIiAgQnkgInRoZSBwcmVkaWN0aW9uIiwgSSdtIGFzc3VtaW5nIHRoZXkncmUgdGFsa2luZyBhYm91dCB0aGUgZGlmZnVzaW9uIG1vZGVsLiAgVGhpcyBpcyBzaW1pbGFyIChpbiBwcmluY2lwbGUgdG8gdGhlIHJlc2lkdWFscyBvZiB0aGUgdGVuc29yIG1vZGVsKS4gIEluIHNob3J0LCBwZW9wbGUgd2hvIGFyZSAibm9pc3kiIHRlbmQgdG8gaGF2ZSBtb3JlIGNvbnNpc3RlbnRseSBoaWdoZXIgU0QgdmFsdWVzIGFjcm9zcyAqYWxsKiB2b2x1bWVzIHJhdGhlciB0aGFuIGEgZmV3LiAgCgpJbiBleGFtaW5pbmcgdGhlc2UgZGF0YSwgSSBmb3VuZCB0aGF0IGNvbGxhcHNpbmcgdGhlIGRhdGEgaW50byB2b2x1bWVzL2RpcmVjdGlvbnMgYnkgdGFraW5nIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gYWNyb3NzIHNsaWNlcyB3b3JrZWQgd2VsbC4gIFRoaXMgZXNzZW50aWFsbHkgYXNrcyB3aGF0IGlzIHRoZSBtZWFuIGRpc3RhbmNlIG9mIGVhY2ggc2xpY2UgZnJvbSBldmVyeSBvdGhlciBzbGljZT8gIEFzIGNhbiBiZSBzZWVuIGJlbG93LCBwZW9wbGUgd2l0aCBub2lzaWVyIGJyYWlucyBhcmUgKm1vcmUqIHNpbWlsYXIgdG8gZWFjaCBvdGhlciAoc21hbGxlciBkaXN0YW5jZSBmcm9tIGVhY2ggb3RoZXIgLSBzZWUgZGlzdGFuY2UgbWF0cml4KS4gIAoKRGF0YSBhcmUgZ2l2ZW4gcGVyIHBlcnNvbiBpbiBhIG1hdHJpeCB3aGVyZSBlYWNoIGNvbHVtbiByZXByZXNlbnRzIGEgc2xpY2UsIGFuZCBlYWNoIHJvdyByZXByZXNlbnRzIGEgc2Nhbi4gIFRoZXJlZm9yZSBmb3IgdGhlIFBPTkQgZGF0YSwgd2UgaGF2ZSBhIDY5IChzY2FucykgeCAxNDAgKHNsaWNlcykuICBUaGUgZm9sbG93aW5nIGNvZGUgd2lsbCBuZWVkIHRvIGJlIHJlLXdyaXR0ZW4gdG8gYmUgZmxleGlibHkgYXBwbGllZCB0byBhbGwgZGF0YXNldHMgKGkuZS4gc2NhbiBhbmQgdm9sdW1lIG51bWJlcnMgYXJlIGhhcmQtY29kZWQgYXQgdGhlIG1vbWVudCkuCgojRGVwZW5kZW5jaWVzOgpgYGB7ciwgbWVzc2FnZT1GQUxTRSxjb21tZW50PUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkgICMgZGF0YSBtYW5pcHVsYXRpb24KbGlicmFyeShjbHVzdGVyKSAgICAjIGNsdXN0ZXJpbmcgYWxnb3JpdGhtcwpsaWJyYXJ5KGZhY3RvZXh0cmEpICMgY2x1c3RlcmluZyBhbGdvcml0aG1zICYgdmlzdWFsaXphdGlvbgpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ3Bsb3RzKSAKbGlicmFyeShyZWFkYnVsaykKbGlicmFyeShnZ3RoZW1yKQpsaWJyYXJ5KGdncHVicikKbGlicmFyeShkb0J5KQpsaWJyYXJ5KFJtaXNjKQpsaWJyYXJ5KGRlbmRleHRlbmQpCmxpYnJhcnkoZGVuZGV4dGVuZFJjcHApCmxpYnJhcnkoa2FibGVFeHRyYSkKZ2d0aGVtcigiY2hhbGsiKQoKI2Z1bmN0aW9uIHRvIHByZXBhcmUgU0QgZmlsZXMgZm9yIGFuYWx5c2lzClFDX0RXSV9QcmVwIDwtIGZ1bmN0aW9uKHgpewogIGlucHV0IDwtIHJlYWRfYnVsayh4LCBza2lwID0gMSwgaGVhZGVyID1GQUxTRSwgc2VwID0nICcpCiAgaW5wdXQkVjE0MSA8LSBOVUxMCiAgZGF0YSA8LSBtZWx0KGlucHV0LCBpZC52YXJzID0gIkZpbGUiKQogICN0aGlzIGZ1bmN0aW9uIChkY2FzdCkgY29tYmluZXMgdGhlIGRhdGEgYnkgY2FsY3VsYXRpbmcgYW4gU0QgcGVyIHZvbHVtZSBhY3Jvc3Mgc2xpY2VzLiAgCiAgI1RodXMsIHdlIGhhdmUgYSBtZWFzdXJlIG9mIGhvdyB2YXJpYWJsZSB0aGUgdmFyaWFiaWxpdHkgaXMgcGVyIHZvbHVtZS4gIAogIGIgPC0gZGNhc3QoZGF0YSwgdmFyaWFibGUgfiBGaWxlLCBmdW4uYWdncmVnYXRlID0gc2QsIG5hLnJtID0gVFJVRSkKICBiJHZhcmlhYmxlIDwtIE5VTEwKICBkIDwtIHQoYikKICBkcyA8LSBzY2FsZShkKQogIG15X2xpc3QgPC0gbGlzdChkLCBkcykKcmV0dXJuKG15X2xpc3QpCn0KYGBgCgojRmlyc3QgcmVhZCBpbiB0aGUgZGF0YSB1c2luZyAicmVhZGJ1bGsiOgpgYGB7ciwgbWVzc2FnZT1GQUxTRSxjb21tZW50PUZBTFNFLCB3YXJuaW5nPUZBTFNFLGVjaG89RkFMU0V9CiNsb2NhdGlvbiBvZiB0aGUgCiNhIDwtICIvVXNlcnMvSm9obi9Ecm9wYm94L3FjX3NvbHV0aW9uL3FjX3N0ZGV2X2FsbF9maWxlcyIKYSA8LSAiL1VzZXJzL0pvaG4vRHJvcGJveC9xY19zb2x1dGlvbi9xY19zdGRldl9maWxlcyIKYWEgPC0gUUNfRFdJX1ByZXAoYSkKZCA8LSBhYVtbMV1dCmRzIDwtIGFhW1syXV0KYGBgCgojRmlyc3QganVzdCBsb29rIGF0IHRoZSBkaXN0YW5jZSBtYXRyaXgKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLGNvbW1lbnQ9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiNkaXN0YW5jZSBtYXRyaXggdmlzdWFsaXphdGlvbgpkaXN0YW5jZSA8LSBnZXRfZGlzdChkcykKZnZpel9kaXN0KGRpc3RhbmNlLCBncmFkaWVudCA9IGxpc3QobG93ID0gIiMwMEFGQkIiLCBtaWQgPSAid2hpdGUiLCBoaWdoID0gIiNGQzRFMDciKSkKYGBgCgojI05vdyB0cnkgV2FyZCdzIG1ldGhvZCBmb3IgY2x1c3RlcmluZwoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsY29tbWVudD1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBXYXJkIEhpZXJhcmNoaWNhbCBDbHVzdGVyaW5nCmQgPC0gZGlzdChkLCBtZXRob2QgPSAiZXVjbGlkZWFuIikgIyBkaXN0YW5jZSBtYXRyaXgKZml0IDwtIGhjbHVzdChkLCBtZXRob2Q9IndhcmQiKSAKZ3JvdXBzIDwtIGN1dHJlZShmaXQsIGs9NSkgIyBjdXQgdHJlZSBpbnRvIDUgY2x1c3RlcnMKIyBkcmF3IGRlbmRvZ3JhbSB3aXRoIHJlZCBib3JkZXJzIGFyb3VuZCB0aGUgNSBjbHVzdGVycyAKIyB1c2luZyBwaXBpbmcgdG8gZ2V0IHRoZSBkZW5kCmRlbmQgPC0gZml0ICU+JSBhcy5kZW5kcm9ncmFtCgojIHBsb3QgKyBjb2xvciB0aGUgZGVuZCdzIGJyYW5jaGVzIGJlZm9yZSwgYmFzZWQgb24gMyBjbHVzdGVyczoKCgpwYXIobWFyID0gYygyLDIsMiwxNSkpCgpkZW5kICU+JSBjb2xvcl9icmFuY2hlcyhrPTUpICU+JSBwbG90KGhvcml6PVRSVUUpCgojIGFkZCBob3JpeiByZWN0CiNkZW5kICU+JSByZWN0LmRlbmRyb2dyYW0oaz01LGhvcml6PVRSVUUpCgojIGFkZCBob3JpeiAod2VsbCwgdmVydGljYWwpIGxpbmU6CnRleHQoNTAsIDEwLCAiYmFkIFxuIHBhcnRpY2lwYW50cyIsIGNvbCA9ICIjQzg0RDZGIikKdGV4dCg1MCwgMjcsICJnb29kIFxuIHBhcnRpY2lwYW50cyIsIGNvbCA9ICIjOUM0RENDIikKdGV4dCg1MCwgMTcsICI/IikKCgpgYGAKCgojI05vdyB0cnkgYSBLLW1lYW5zIHNvbHV0aW9uCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSxjb21tZW50PUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKI05vdyB0byB0cnkgYSBLLW1lYW5zIHNvbHV0aW9uIAoKazIgPC0ga21lYW5zKGRzLCBjZW50ZXJzID0gMiwgbnN0YXJ0ID0gMjUpCmszIDwtIGttZWFucyhkcywgY2VudGVycyA9IDMsIG5zdGFydCA9IDI1KQprNCA8LSBrbWVhbnMoZHMsIGNlbnRlcnMgPSA0LCBuc3RhcnQgPSAyNSkKazUgPC0ga21lYW5zKGRzLCBjZW50ZXJzID0gNSwgbnN0YXJ0ID0gMjUpCmsxMCA8LSBrbWVhbnMoZHMsIGNlbnRlcnMgPSAxMCwgbnN0YXJ0ID0gMjUpCgojIHBsb3RzIHRvIGNvbXBhcmUKcDEgPC0gZnZpel9jbHVzdGVyKGsyLCBnZW9tID0gInBvaW50IiwgZGF0YSA9IGRzKSArIGdndGl0bGUoImsgPSAyIikKcDIgPC0gZnZpel9jbHVzdGVyKGszLCBnZW9tID0gInBvaW50IiwgIGRhdGEgPSBkcykgKyBnZ3RpdGxlKCJrID0gMyIpCnAzIDwtIGZ2aXpfY2x1c3RlcihrNCwgZ2VvbSA9ICJwb2ludCIsICBkYXRhID0gZHMpICsgZ2d0aXRsZSgiayA9IDQiKQpwNCA8LSBmdml6X2NsdXN0ZXIoazUsIGdlb20gPSAicG9pbnQiLCAgZGF0YSA9IGRzKSArIGdndGl0bGUoImsgPSA1IikKI3AxMCA8LSBmdml6X2NsdXN0ZXIoazEwLCBnZW9tID0gInBvaW50IiwgIGRhdGEgPSBkKSArIGdndGl0bGUoImsgPSAxMCIpCgpsaWJyYXJ5KGdyaWRFeHRyYSkKZ3JpZC5hcnJhbmdlKHAxLCBwMiwgcDMsIHA0LCBucm93ID0gMikKCmZ2aXpfbmJjbHVzdChkcywga21lYW5zLCBtZXRob2QgPSAic2lsaG91ZXR0ZSIpCmZ2aXpfbmJjbHVzdChkcywga21lYW5zLCBtZXRob2QgPSAid3NzIikKCmdhcF9zdGF0IDwtIGNsdXNHYXAoZHMsIEZVTiA9IGttZWFucywgbnN0YXJ0ID0gMjUsCiAgICAgICAgICAgICAgICAgICAgSy5tYXggPSAxMCwgQiA9IDUwKQpmdml6X2dhcF9zdGF0KGdhcF9zdGF0KQoKI0Z1enp5IEstTWVhbnMgY2x1c3RlcmluZyAoMiBncm91cHMpIFRoaXMgYXBwcm94aW1hdGVzIHdoYXQgSGFqZXIgaXMgZG9pbmcgSSB0aGluayAodHJ5aW5nIHRvIG1ha2UgYSBiaW5hcnkgZGVjaXNpb24sIGJ1dCBwcm92aWRpbmcgdXMgd2l0aCBwcm9iYWJpdGllcykuICBJJ2QgbG9vayBhdCBhbnkgcGVyc29uIHdobyBnZXRzIGEgc2NvcmUgb2YgbGVzcyB0aGFuIC45IGZvciBlaXRoZXIgY2F0ZWdvcnkuCmZhbm55ZCA8LSBmYW5ueShkcywgaz0yLCBtZXRyaWMgPSAiZXVjbGlkZWFuIiwgbWVtYi5leHAgPSAxLjIpCnJvdW5kKGZhbm55ZCRtZW1iZXJzaGlwLCAyKQoKI0NsdXN0ZXJpbmcgY2x1c3RlciBzZXRzIHdpdGggSmFjY2FyZCBpbmRleCAtIGhvdyBzaW1pbGFyIGFyZSB0aGUgY2x1c3RlciBzb2x1dGlvbnM/CnNvdXJjZSgiaHR0cDovL2ZhY3VsdHkudWNyLmVkdS9+dGdpcmtlL0RvY3VtZW50cy9SX0Jpb0NvbmQvTXlfUl9TY3JpcHRzL2NsdXN0ZXJJbmRleC5SIikgCmNsVmxpc3QgPC0gbGFwcGx5KDI6MTIsIGZ1bmN0aW9uKHgpIGNsYXJhKGRzLCBrPXgpJGNsdXN0ZXJpbmcpOyBuYW1lcyhjbFZsaXN0KSA8LSBwYXN0ZSgiayIsICI9IiwgMjoxMikKZSA8LSBzYXBwbHkobmFtZXMoY2xWbGlzdCksIGZ1bmN0aW9uKHgpIHNhcHBseShuYW1lcyhjbFZsaXN0KSwgZnVuY3Rpb24oZHMpIGNpbmRleChjbFYxPWNsVmxpc3RbW2RzXV0sIGNsVjI9Y2xWbGlzdFtbeF1dLCBtZXRob2Q9ImphY2NhcmQiKVtbM11dKSkKaHYgPC0gaGNsdXN0KGFzLmRpc3QoMS1lKSkKcGxvdChhcy5kZW5kcm9ncmFtKGh2KSwgZWRnZVBhcj1saXN0KGNvbD0zLCBsd2Q9NCksIGhvcml6PVQsIG1haW49IlNpbWlsYXJpdGllcyBvZiAxMCBDbGFyYSBDbHVzdGVyaW5nIFJlc3VsdHMgZm9yIGs6IDItMTIiKSAKCmBgYAoKKlNvLCBpdCBsb29rcyBsaWtlIDItMyBjbHVzdGVycyBhcmUgb3B0aW1hbCoKCiNMZXQncyBwbG90IHRoZSAzLWdyb3VwcyBmcm9tIHRoZSBoY2x1c3QgKFdhcmQncyBzb2x1dGlvbikgLSB3ZSBjYW4gdHJ5IG90aGVyIHZlcnNpb25zIGxhdGVyCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSxjb21tZW50PUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojc2hvdWxkIGxpa2VseSByZS13cml0ZSB0aGlzIHRvIHRha2UgdGhlIGNsdXN0ZXJzIGZyb20gdGhlIG90aGVyIHNvbHV0aW9ucyByYXRoZXIgdGhhbiBoYXJkLWNvZGluZyBpdCB0byBiZSBzdWJkaXJlY3RvcmllcyAodXNlIHRoZSAlaW4lIGZ1bmN0aW9uIGZyb20gZHBseXIpCgpncm91cDMgPC0iL1VzZXJzL0pvaG4vRHJvcGJveC9xY19zb2x1dGlvbi9ncm91cDMiCmdyb3VwMiA8LSAiL1VzZXJzL0pvaG4vRHJvcGJveC9xY19zb2x1dGlvbi9ncm91cDIiCmdyb3VwMSA8LSIvVXNlcnMvSm9obi9Ecm9wYm94L3FjX3NvbHV0aW9uL2dyb3VwMSIKClFDX0RXSV9QbG90IDwtIGZ1bmN0aW9uKHgpewppbnB1dCA8LSByZWFkX2J1bGsoeCwgc2tpcCA9IDEsIGhlYWRlciA9RkFMU0UsIHNlcCA9JyAnKQppbnB1dCRWMTQxIDwtIE5VTEwKcm93cyA8LSBhcy5kYXRhLmZyYW1lKHJlcChjKDA6NjgpLCB0aW1lcyA9IGxlbmd0aCh1bmlxdWUoaW5wdXQkRmlsZSkpKSkKY29sbmFtZXMocm93cykgPC0gInJvd3MiCmRhdGEgPC0gY2JpbmQoaW5wdXQsIHJvd3MpCmdyb3VwX0wgPC0gbWVsdChkYXRhLCBpZC52YXJzID0gYygiRmlsZSIsInJvd3MiKSkKZ3JvdXBfTCR2YXJpYWJsZSA8LSBhcy5udW1lcmljKHN1YnN0cmluZyhncm91cF9MJHZhcmlhYmxlLCAyKSkKcmV0dXJuKGdyb3VwX0wpCn0KCgpncm91cDFfTCA8LSBRQ19EV0lfUGxvdChncm91cDEpCmdyb3VwMl9MIDwtIFFDX0RXSV9QbG90KGdyb3VwMikKZ3JvdXAzX0wgPC0gUUNfRFdJX1Bsb3QoZ3JvdXAzKQphbGxfTCA8LSBRQ19EV0lfUGxvdCgiL1VzZXJzL0pvaG4vRHJvcGJveC9xY19zb2x1dGlvbi9xY19zdGRldl9maWxlcyIpCgpncm91cDFfcGxvdCA8LSBnZ3Bsb3QoZ3JvdXAxX0wsIGFlcyhyb3dzLCB2YXJpYWJsZSkpICsKZ2VvbV9yYXN0ZXIoYWVzKGZpbGwgPSB2YWx1ZSkpK2ZhY2V0X2dyaWQoLn5GaWxlKSsgCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwxNDAsMTApKSsKICBsYWJzKHggPSAidm9sdW1lfGRpcmVjdGlvbnMiLCB5ID0gInNsaWNlIikKZ3JvdXAyX3Bsb3QgPC0gZ2dwbG90KGdyb3VwMl9MLCBhZXMocm93cywgdmFyaWFibGUpKSArCmdlb21fcmFzdGVyKGFlcyhmaWxsID0gdmFsdWUpKStmYWNldF9ncmlkKC5+RmlsZSkrIAogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9c2VxKDAsMTQwLDEwKSkrCiAgbGFicyh4ID0gInZvbHVtZXxkaXJlY3Rpb25zIiwgeSA9ICJzbGljZSIpCmdyb3VwM19wbG90IDwtIGdncGxvdChncm91cDNfTCwgYWVzKHJvd3MsIHZhcmlhYmxlKSkgKwpnZW9tX3Jhc3RlcihhZXMoZmlsbCA9IHZhbHVlKSkrZmFjZXRfZ3JpZCgufkZpbGUpKyAKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDE0MCwxMCkpKwogIGxhYnMoeCA9ICJ2b2x1bWV8ZGlyZWN0aW9ucyIsIHkgPSAic2xpY2UiKQoKCmdnYXJyYW5nZShncm91cDFfcGxvdCwgZ3JvdXAyX3Bsb3QsIGdyb3VwM19wbG90LCBucm93PTMsIG5jb2wgPTEsIGxhYmVscyA9IGMoIkdyb3VwMSIsICJHcm91cDIiLCJHcm91cDMiKSwgY29tbW9uLmxlZ2VuZCA9IFRSVUUsIHdpZHRocyA9IGMoMSwgMC42NCwgMC41KSkKCgpnZ3Bsb3QoKSArIAogIGdlb21faGlzdG9ncmFtKGRhdGEgPSBncm91cDFfTCwgYWVzKHggPSB2YWx1ZSksIGNvbG9yID0gImdyZWVuIiwgZmlsbCA9ICJncmVlbiIpICsKICBnZW9tX2hpc3RvZ3JhbShkYXRhID0gZ3JvdXAyX0wsIGFlcyh4ID0gdmFsdWUpLCBjb2xvciA9ICJibHVlIiwgZmlsbCA9ICJibHVlIikgKyAKICBnZW9tX2hpc3RvZ3JhbShkYXRhID0gZ3JvdXAzX0wsIGFlcyh4ID0gdmFsdWUpLCBjb2xvciA9ICJyZWQiLCBmaWxsID0gInJlZCIpKwogIHhsaW0oLTMsMykKYGBgCgojI1RyYWluaW4gYSBjbGFzc2lmaWVyPwoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsY29tbWVudD1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShjYXJldCkKbGlicmFyeShNQVNTKQoKcG9uZF9hbGxfdHJhaW5pbmcgPC0gcmVhZF9jc3YoIi9Vc2Vycy9Kb2huL0Ryb3Bib3gvcWNfc29sdXRpb24vcG9uZF9hbGxfdHJhaW5pbmcuY3N2IikKClRlc3RSb3dzICAgICA8LSBzdWJzZXQocG9uZF9hbGxfdHJhaW5pbmcsIFRyYWluaW5nID09Ik5vIikKVHJhaW5EYXRhICAgIDwtIHN1YnNldChwb25kX2FsbF90cmFpbmluZywgVHJhaW5pbmcgPT0iWWVzIikKVHJhaW5DbGFzc2VzIDwtIGFzLmRhdGEuZnJhbWUoVHJhaW5EYXRhWywxNDJdKQpUcmFpbkRhdGEgICAgPC0gVHJhaW5EYXRhWywyOjE0MV0KVGVzdERhdGEgICAgIDwtIFRlc3RSb3dzWywyOjE0MV0KVGVzdFN1YnMgICAgIDwtIFRlc3RSb3dzWywxXQojVGVzdENsYXNzZXMgIDwtIGlyaXNbVGVzdFJvd3MsNV0KClRyYWluQ2xhc3NlcyA8LSBmYWN0b3IoVHJhaW5DbGFzc2VzJEdyb3VwLCBsYWJlbHMgPSBjKCJncm91cF8xIiwgImdyb3VwXzIiLCJncm91cF8zIikpCgpubmV0Rml0IDwtIHRyYWluKHg9VHJhaW5EYXRhLCB5PVRyYWluQ2xhc3NlcywKICAgICAgICAgICAgICAgICBtZXRob2QgPSAibm5ldCIsCiAgICAgICAgICAgICAgICAgcHJlUHJvY2VzcyA9ICJyYW5nZSIsIAogICAgICAgICAgICAgICAgIHR1bmVMZW5ndGggPSAyLAogICAgICAgICAgICAgICAgIHRyYWNlID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgbWF4aXQgPSAxMDApCgpwcmVkaWN0ZWQgPC1hcy5kYXRhLmZyYW1lKHByZWRpY3Qobm5ldEZpdCwgVGVzdERhdGEpKQp0ZXN0X3ByZWRzIDwtIGNiaW5kKFRlc3RTdWJzLCBwcmVkaWN0ZWQpCmthYmxlKHRlc3RfcHJlZHMpCgp0YWJsZShUcmFpbkNsYXNzZXMsIHByZWRpY3Qobm5ldEZpdCkpIAojdGFibGUoVGVzdENsYXNzZXMsICBwcmVkaWN0KG5uZXRGaXQsVGVzdERhdGEpKSAgCgojdmlzdWFsaXphdGlvbgpgYGAK