library(ggplot2)
# library(RColorBrewer)
library(tidyverse)
library(ggpubr)
library(jcolors)

# load data 
# in excel pre generate a file with only including well information / R error when duplicated row names 

setwd("//atlas.uni.lux/users/isabel.rosety/GBA/MEA/RstudioAnalysis/")
The working directory was changed to //atlas.uni.lux/users/isabel.rosety/GBA/MEA/RstudioAnalysis inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
# NOTE: before and during loading, re-check the index of the rows. it is variable in some files (not fix index between extracted files)


#Day 15
d15_1<- read.csv("//atlas.uni.lux/users/isabel.rosety/GBA/MEA/20210729_MEA_E40/E40D15/Neural_Statistics_Compiler_20210803_E40d15.csv",header = T, skip = 157, sep=";",     nrows = 26)
n <- d15_1$Measurement
d15_1t <- as.data.frame(t(d15_1[,-1])) 
colnames( d15_1t) <- n  
m <- rownames(d15_1t) 
d15_1t %>% mutate(wells = rownames(d15_1t)) %>% mutate(timePoint = "day15") %>% mutate(day = 15) %>% mutate(experiment = "exp40") ->  d15_1t
m -> rownames(d15_1t)

d15_2<- read.csv("//atlas.uni.lux/users/isabel.rosety/GBA/MEA/20210806_MEA_E41/E41D15/20210806_E41_D15(000).csv",header = T, skip = 157, sep=";",     nrows = 26)
n <- d15_2$Measurement
d15_2t <- as.data.frame(t(d15_2[,-1])) 
colnames( d15_2t) <- n  
m <- rownames(d15_2t) 
d15_2t %>% mutate(wells = rownames(d15_2t)) %>% mutate(timePoint = "day15") %>% mutate(day = 15) %>% mutate(experiment = "exp41") ->  d15_2t
m -> rownames(d15_2t)

d15_3<- read.csv("//atlas.uni.lux/users/isabel.rosety/GBA/MEA/20210812_MEA_E42_E43/E42D10/20210812_E42D10(000).csv",header = T, skip = 157, sep=";",     nrows = 26)
n <- d15_3$Measurement
d15_3t <- as.data.frame(t(d15_3[,-1])) 
colnames( d15_3t) <- n  
m <- rownames(d15_3t) 
d15_3t %>% mutate(wells = rownames(d15_3t)) %>% mutate(timePoint = "day15") %>% mutate(day = 15) %>% mutate(experiment = "exp42") ->  d15_3t
m -> rownames(d15_3t)

d15_4<- read.csv("//atlas.uni.lux/users/isabel.rosety/GBA/MEA/20210812_MEA_E42_E43/E43D15/20210820_E43_D15(001).csv",header = T, skip = 157, sep=";",     nrows = 26)
n <- d15_4$Measurement
d15_4t <- as.data.frame(t(d15_4[,-1])) 
colnames( d15_4t) <- n  
m <- rownames(d15_4t) 
d15_3t %>% mutate(wells = rownames(d15_4t)) %>% mutate(timePoint = "day15") %>% mutate(day = 15) %>% mutate(experiment = "exp43") ->  d15_4t
m -> rownames(d15_4t)  
 
 

Merge data. obtain sumary data file (over threshold and without threshold)


# cahnge folder for results 
setwd("//atlas.uni.lux/users/isabel.rosety/GBA/MEA/RstudioAnalysis/")
The working directory was changed to //atlas.uni.lux/users/isabel.rosety/GBA/MEA/RstudioAnalysis inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
 ## merge all files form different time points 
dataWell <- rbind(d15_1t,d15_2t,d15_3t,d15_4t)

#dataWell <- rbind(d15_1t,d15_2t,d15_3t,d15_4t)
#dataWell[dataWell==0] <- NA
head(dataWell)

# add conditions to wells 


colnames(dataWell)<-c( 'NumberOfSpikes', 'MeanFiringRate', 'ISI', 'NumberOfBurst','BurstDuration_avrg', 'BurstDuration_std', 'NumberSpikesPerBurst_avg', 'NumberSpikesPerBurst_std', 'meanISI_burst_avg','meanISI_burst_std','medianISI_burst_avg','medianISI_burst_std','InterBurstInterval_avg', 'InterBurstInterval_std', 'BurstFrequency', 'IBI_CoefficientOfVariation', 'Normalized_Duration_IQR','BurstPercentage', 'condition','CellLine','wells', 'timepoint', 'day','experiment')

# create colum for removal of outliers (per condition & timepoint)
dataWell %>% mutate(out = paste(dataWell$condition, dataWell$timepoint)) ->  dataWell #here we create a new column called out, with condition and timepoint, because outliers will be calculated based on condition and timepoint

# convert into numeric all data of interest fro plotting (needed because data was as character) 
dataWell$NumberOfSpikes <- as.numeric(as.character(dataWell$NumberOfSpikes))
dataWell$MeanFiringRate <- as.numeric(as.character(dataWell$MeanFiringRate))
dataWell$ISI <- as.numeric(as.character(dataWell$ISI))
dataWell$NumberOfBurst <- as.numeric(as.character(dataWell$NumberOfBurst))
dataWell$BurstDuration_avrg <- as.numeric(as.character(dataWell$BurstDuration_avrg))
dataWell$BurstDuration_std <- as.numeric(as.character(dataWell$BurstDuration_std))
dataWell$NumberSpikesPerBurst_avg <- as.numeric(as.character(dataWell$NumberSpikesPerBurst_avg))
dataWell$NumberSpikesPerBurst_std <- as.numeric(as.character(dataWell$NumberSpikesPerBurst_std))
dataWell$meanISI_burst_avg <- as.numeric(as.character(dataWell$meanISI_burst_avg))
dataWell$meanISI_burst_std <- as.numeric(as.character(dataWell$meanISI_burst_std))
dataWell$medianISI_burst_std <- as.numeric(as.character(dataWell$medianISI_burst_std))
dataWell$medianISI_burst_avg <- as.numeric(as.character(dataWell$medianISI_burst_avg))
dataWell$InterBurstInterval_avg <- as.numeric(as.character(dataWell$InterBurstInterval_avg))
dataWell$InterBurstInterval_std <- as.numeric(as.character(dataWell$InterBurstInterval_std))
dataWell$BurstFrequency <- as.numeric(as.character(dataWell$BurstFrequency))
dataWell$IBI_CoefficientOfVariation <- as.numeric(as.character(dataWell$IBI_CoefficientOfVariation))
dataWell$Normalized_Duration_IQR <- as.numeric(as.character(dataWell$Normalized_Duration_IQR))
dataWell$BurstPercentage <- as.numeric(as.character(dataWell$BurstPercentage))
dataWell$day <- as.numeric(as.character(dataWell$day))


# obtain data only with bursting information for extracting into CSV and plotting in graphad (needed to take into consideration SD already calculated by software)
dataWell_burst <- dataWell[dataWell$NumberOfBurst != 0, ] #remove wells with no information for bursting 

dataWell_5spike <- dataWell[dataWell$NumberOfSpikes >= 5, ] #this is to select the measurements with more than 5 spikes per minute
dataWell_5spike_burst <- dataWell_burst[dataWell_burst$NumberOfSpikes >= 5, ]
dataWell_5spike<-filter(dataWell_5spike, condition %in% c("Control","PD_N370S"))

feature_names <- colnames(dataWell_5spike[,c(1:4,15:18)])  
#saving info: 

  #save summary file in CSV
  write.csv(dataWell,file="dataWell_allInfo.csv")
  write.csv(dataWell_5spike,file="dataWell_5spike.csv")
  # save information reagrding average and std
  write.csv(dataWell_burst,file="dataWell_burst_allInfo.csv") 
  write.csv(dataWell_5spike_burst,file="dataWell_5spike_burst.csv")

Remove outliers based on 25th & 75th percentiles (for only the specific feature under study_ introducetion NAs) thes rest of the information for the full well remains

# NOTE!! if zeros are kept, may infomation will be extracted based on a simple value due to the ceros being the mean !! 
# transfrom 0 into NAs 

#in this one it wont remove the entire electrode, it will only remove the value of the outlier feature

dataWell_noZero <- replace(dataWell_5spike, dataWell_5spike == 0, NA)
#dataWell_noZero <- replace(dataWell, dataWell == 0, NA)

 groupOut <- unique(dataWell_noZero$out) # groupout 
datatest_NA <- data.frame()


for (i in 1:length(groupOut))
{
  datatest <- dataWell_noZero %>% 
  filter(out %in% groupOut[i]) # go for each line and extract rows for that line 
  for (j in 1:18) # for (j in 2:length(exprs))
  {
    out <- boxplot.stats(datatest[,j])$out
    for (k in 1:length(out))
    {
    datatest[,j][datatest[,j] == out[k]] <- NA #it changes the outlier value for NA
    }
    rm(out)
  }
  datatest_NA <- rbind(datatest_NA,datatest)
  rm(datatest)
} 


#saving info: 
# files without outliers were generated from file No zeros - reason: too many zeros cause exclusion of real values as outliers 
# _well > meaningn all well information was removed for all variable when at least one of the variables presented one outlier -> the well is not consider any longer 

# Remove an entire row if it has >22 NAs (for example >50% of your features)
count_na <- function(x)sum(is.na(x))
data_noOut_value <- datatest_NA %>%
  dplyr::mutate(count_na = apply(., 1, count_na))
data_noOut_value <-datatest_NA[!(data_noOut_value$count_na >= 19),] # remove wells containing no info 

dataWell_burst_NoOut_value <- subset(data_noOut_value, !is.na(NumberOfBurst)) #remove wells with no information for bursting

  write.csv(dataWell_burst_NoOut_value,file="dataWell_burst_NoOut_value.csv") # infor for bursting (graphad) _ only the specific outlier was removed. well information for other variables remain  
  write.csv(data_noOut_value,file="data_noOut_value.csv") # infor - no outliers for specific variable -well info remains 
  


#dataWell2=dataWell[!grepl("exp40",dataWell$experiment),]
feature_names <- colnames(data_noOut_value[,c(1:4,15:18)])  
head(data_noOut_value)
NA

Shapiro Test to check normality # plotting without SD already calculated (“NumberOfSpikes” “MeanFiringRate” “ISI” “NumberOfBurst” “NumberBurstingElectrodes”)

#if p-value >0.05, normality can be assumed
TestShapiro<- select(data_noOut_value, c(1:18))
#shapiro.test(data_noOut_value$MeanFiringRate)
apply(TestShapiro,2,shapiro.test)

Plots


names <- c("Number Of Spikes", "Mean Firing Rate (spikes/s)" , "Interspike Interval [ISI]"  ,"Number Of Bursts" , "Burst Frequency (bursts/s)","IBI Coefficient Of Variation", "Normalized Duration IQR"  ,  "Burst Percentage")

for (i in 1:length(feature_names)) {
data_noOut_value  %>% #change dataset selected - change saving option depending on the dataset 
  pivot_longer(cols=feature_names, names_to = "feature", values_to = "value") %>%
  filter(feature %in% feature_names[i]) %>%
  #ggplot( aes(x = factor(condition, level = c( 'Control','PD_N370S', 'Control_GDNF','PD_N370S_GDNF')), y=value) ) +
  #geom_boxplot(aes(fill=fct_relevel(condition, 'Control','PD_N370S', 'Control_GDNF','PD_N370S_GDNF')),width=0.5,outlier.shape=NA) +
  ggplot( aes(x = factor(condition, level = c( 'Control','PD_N370S')), y=value) ) +
  geom_boxplot(aes(fill=fct_relevel(condition, 'Control','PD_N370S')),width=0.8,outlier.shape=NA) +
    geom_point(aes(color=CellLine),size=3,show.legend = T,alpha = 0.5)+
   
    scale_color_jcolors("pal7")+
    theme(legend.key=element_blank()) +
  #scale_fill_manual(values= c("#008B8B","#B22222","#2171b5","#000066"),name = "condition", guide = "none") +
    scale_fill_manual(values= c("white", "black"),name = "condition", guide = "none") +  
  #stat_compare_means(comparisons=list(c("WT", "3xSNCA")), method = "t.test", label="p.signif", label.x = 1.5)+
  
  # Mann-Whitney
   geom_signif(comparisons = list(c('Control','PD_N370S')), test='wilcox.test',
              vjust=0.6, size=0.5, textsize=9, map_signif_level=c("***"=0.001, "**"=0.01, "*"=0.05,  " "=2) ) +
  facet_grid(~fct_relevel(timepoint, "day10", "day15","day30", "day60","day100"), scales="free")  +
    

  labs(x     ="",
       y     = paste(names[i]),
       fill  = "Condition",
       title = paste(names[i])) +
    
  theme_bw() +
  theme(
    axis.line = element_line(colour = 'black', size = 0.5) ,
    axis.title.x = element_blank(),
    axis.text.x = element_text(size=21, color="black"),
    axis.title.y = element_text(size = 21),
    axis.text.y = element_text(size=15, color="black"),
    axis.ticks.y = element_line(),
    axis.ticks.length=unit(.25, "cm"),
    #change legend text font size)
    #legend.key.size = unit(0.7, "cm"),
    #legend.key.width = unit(0.6,"cm"),
    legend.key=element_blank(),
    panel.grid.major = element_blank(), 
    panel.grid.minor = element_blank(),
    panel.border = element_blank(),
    plot.title = element_text(size = 20, hjust=0.5, vjust= 1, face = "bold"),
    plot.subtitle = element_blank(),#element_text(size = 2, hjust=0.5)
    strip.text = element_text(size=12, vjust=0.5),
    strip.background = element_rect(fill="lightgray"),
   # panel.border = element_rect(fill = NA, color = "black"),
    panel.spacing.y = unit(0.8, "lines"),
    strip.switch.pad.wrap=unit(20, "lines"),
    legend.position="right",
    legend.text = element_text(size=17),
    legend.title = element_text(size=19)
  )  -> p
  t<- cowplot::ggdraw(cowplot::add_sub(p, "Wilcox-test, ***p=0.001, **p=0.01, *p=0.05",hjust=-0.5, size=15))
  print(t)

#ggsave(paste0(Sys.Date(),"_", feature_names[i], "_value_electrode_DIV15.pdf"), plot=t,height=4) # change saving option depending on the dataset 

}

Per cell line


for (i in 1:length(feature_names)) {
dataWell_5spike %>% #change dataset selected - change saving option depending on the dataset 
  pivot_longer(cols=feature_names, names_to = "feature", values_to = "value") %>%
  filter(feature %in% feature_names[i]) %>%
  #ggplot( aes(x = factor(condition, level = c( 'Control','PD_N370S', 'Control_GDNF','PD_N370S_GDNF')), y=value) ) +
  #geom_boxplot(aes(fill=fct_relevel(condition, 'Control','PD_N370S', 'Control_GDNF','PD_N370S_GDNF')),width=0.5,outlier.shape=NA) +
  ggplot( aes(x = factor(CellLine, level = c( 'WT 56','WT 39', 'WT 68', 'MUT 309', 'MUT KTI6', 'MUT SGO1')), y=value) ) +
  geom_boxplot(aes(fill=CellLine),width=0.8,outlier.shape=NA) +
 # scale_fill_manual(values= c("#008B8B","#B22222","#2171b5","#000066"),name = "condition") +
    geom_point(aes(color=experiment),size=2)+
    #scale_color_viridis(option = "D", discrete=TRUE)+
    geom_point(shape = 1,size = 2,colour = "black")+
   # t-test
  #stat_compare_means(comparisons=list(c("WT", "3xSNCA")), method = "t.test", label="p.signif", label.x = 1.5)+
  
  # Mann-Whitney
  #ggpubr::stat_compare_means(comparisons=list(c("Control", "PD_N370S")), method="wilcox.test", p.adjust.method="BH",label="p.signif", label.x = 1.5)+
  
    
  labs(x     ="",
       y     = paste(names[i]),
       fill  = "CellLine",
       title = paste(names[i])) +
  theme_bw() +
    
  theme(
    axis.line = element_line(colour = 'black', size = 0.1) ,
    axis.title.x = element_blank(),
    axis.text.x = element_text(angle=45,size = 7,hjust=1), #angle=45),
    axis.title.y = element_text(size = 18),
    axis.text.y = element_text(size=15, color="black"),
    axis.ticks.y = element_line(),
    legend.position="right",
    legend.text = element_text(size = 11, face = "bold") ,
    legend.title = element_blank(),
    legend.key.size = unit(0.7, "cm"),
    legend.key.width = unit(0.6,"cm"),
    plot.title = element_text(size = 15, hjust=0.5, vjust= 1, face = "bold"),
    plot.subtitle = element_blank(),#element_text(size = 2, hjust=0.5)
    strip.text = element_text(size=12, vjust=0.5),
    strip.background = element_rect(fill="lightgray"),
    panel.border = element_rect(fill = NA, color = "black"),
    panel.spacing.y = unit(0.8, "lines"),
    strip.switch.pad.wrap=unit(20, "lines")
  )  -> p
  #t<- cowplot::ggdraw(cowplot::add_sub(p, "Wilcox-test, ***p=0.001, **p=0.01, *p=0.05",hjust=-0.5, size=9))
  print(p)

#ggsave(paste0(Sys.Date(),"_", feature_names[i], "_value_electrode_NoOutliers.pdf"), plot=t) # change saving option depending on the dataset 

}

One feature


dataWellOneF <-filter(dataWellNoNA,timepoint=="day15")
dataWellOneF%>%
  ggplot(aes(x=day, y=MeanFiringRate, group=condition, color=condition)) +
  geom_violin(method='loess', formula ='y ~ x', size = 1)+
  scale_colour_manual(name="Condition", values= c("Control"= "#008B8B", "PD_N370S"="#B22222")) +
  # facet_wrap(~timepoint, scales="free", labeller = labeller(groupwrap = label_wrap_gen(10))) +
  # facet_grid(~fct_relevel(timepoint, "day15","day30",  "day70", "day90"), scales="free") +
  labs(x="Time of differentiation [days]",
        y     = "Mean Firing Rate",
       fill  = "Condition",
       title = "Mean Firing Rate") +
   theme_bw() -> p
  t<- cowplot::ggdraw(cowplot::add_sub(p, "Locally estimated scatterplot smoothing: y ~ Time",hjust=-0.2, size=9))
  print(t)
  ggsave(paste0(Sys.Date(),"MeanFiringRate_timeline_data_noOut_value.pdf"), plot=t)
LS0tDQp0aXRsZTogIm5hbHlzaXMgb2YgTUVBIGRhdGEgYW5kIHBsb3R0aW5nIC0gdXNpbmcgb3V0cHV0IGZyb20gbWF0bGFiIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCiMgbGlicmFyeShSQ29sb3JCcmV3ZXIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoZ2dwdWJyKQ0KbGlicmFyeShqY29sb3JzKQ0KYGBgDQoNCg0KYGBge3J9DQoNCiMgbG9hZCBkYXRhIA0KIyBpbiBleGNlbCBwcmUgZ2VuZXJhdGUgYSBmaWxlIHdpdGggb25seSBpbmNsdWRpbmcgd2VsbCBpbmZvcm1hdGlvbiAvIFIgZXJyb3Igd2hlbiBkdXBsaWNhdGVkIHJvdyBuYW1lcyANCg0Kc2V0d2QoIi8vYXRsYXMudW5pLmx1eC91c2Vycy9pc2FiZWwucm9zZXR5L0dCQS9NRUEvUnN0dWRpb0FuYWx5c2lzLyIpDQoNCg0KIyBOT1RFOiBiZWZvcmUgYW5kIGR1cmluZyBsb2FkaW5nLCByZS1jaGVjayB0aGUgaW5kZXggb2YgdGhlIHJvd3MuIGl0IGlzIHZhcmlhYmxlIGluIHNvbWUgZmlsZXMgKG5vdCBmaXggaW5kZXggYmV0d2VlbiBleHRyYWN0ZWQgZmlsZXMpDQoNCg0KI0RheSAxNQ0KZDE1XzE8LSByZWFkLmNzdigiLy9hdGxhcy51bmkubHV4L3VzZXJzL2lzYWJlbC5yb3NldHkvR0JBL01FQS8yMDIxMDcyOV9NRUFfRTQwL0U0MEQxNS9OZXVyYWxfU3RhdGlzdGljc19Db21waWxlcl8yMDIxMDgwM19FNDBkMTUuY3N2IixoZWFkZXIgPSBULCBza2lwID0gMTU3LCBzZXA9IjsiLCAgICAgbnJvd3MgPSAyNikNCm4gPC0gZDE1XzEkTWVhc3VyZW1lbnQNCmQxNV8xdCA8LSBhcy5kYXRhLmZyYW1lKHQoZDE1XzFbLC0xXSkpIA0KY29sbmFtZXMoIGQxNV8xdCkgPC0gbiAgDQptIDwtIHJvd25hbWVzKGQxNV8xdCkgDQpkMTVfMXQgJT4lIG11dGF0ZSh3ZWxscyA9IHJvd25hbWVzKGQxNV8xdCkpICU+JSBtdXRhdGUodGltZVBvaW50ID0gImRheTE1IikgJT4lIG11dGF0ZShkYXkgPSAxNSkgJT4lIG11dGF0ZShleHBlcmltZW50ID0gImV4cDQwIikgLT4gIGQxNV8xdA0KbSAtPiByb3duYW1lcyhkMTVfMXQpDQoNCmQxNV8yPC0gcmVhZC5jc3YoIi8vYXRsYXMudW5pLmx1eC91c2Vycy9pc2FiZWwucm9zZXR5L0dCQS9NRUEvMjAyMTA4MDZfTUVBX0U0MS9FNDFEMTUvMjAyMTA4MDZfRTQxX0QxNSgwMDApLmNzdiIsaGVhZGVyID0gVCwgc2tpcCA9IDE1Nywgc2VwPSI7IiwgICAgIG5yb3dzID0gMjYpDQpuIDwtIGQxNV8yJE1lYXN1cmVtZW50DQpkMTVfMnQgPC0gYXMuZGF0YS5mcmFtZSh0KGQxNV8yWywtMV0pKSANCmNvbG5hbWVzKCBkMTVfMnQpIDwtIG4gIA0KbSA8LSByb3duYW1lcyhkMTVfMnQpIA0KZDE1XzJ0ICU+JSBtdXRhdGUod2VsbHMgPSByb3duYW1lcyhkMTVfMnQpKSAlPiUgbXV0YXRlKHRpbWVQb2ludCA9ICJkYXkxNSIpICU+JSBtdXRhdGUoZGF5ID0gMTUpICU+JSBtdXRhdGUoZXhwZXJpbWVudCA9ICJleHA0MSIpIC0+ICBkMTVfMnQNCm0gLT4gcm93bmFtZXMoZDE1XzJ0KQ0KDQpkMTVfMzwtIHJlYWQuY3N2KCIvL2F0bGFzLnVuaS5sdXgvdXNlcnMvaXNhYmVsLnJvc2V0eS9HQkEvTUVBLzIwMjEwODEyX01FQV9FNDJfRTQzL0U0MkQxMC8yMDIxMDgxMl9FNDJEMTAoMDAwKS5jc3YiLGhlYWRlciA9IFQsIHNraXAgPSAxNTcsIHNlcD0iOyIsICAgICBucm93cyA9IDI2KQ0KbiA8LSBkMTVfMyRNZWFzdXJlbWVudA0KZDE1XzN0IDwtIGFzLmRhdGEuZnJhbWUodChkMTVfM1ssLTFdKSkgDQpjb2xuYW1lcyggZDE1XzN0KSA8LSBuICANCm0gPC0gcm93bmFtZXMoZDE1XzN0KSANCmQxNV8zdCAlPiUgbXV0YXRlKHdlbGxzID0gcm93bmFtZXMoZDE1XzN0KSkgJT4lIG11dGF0ZSh0aW1lUG9pbnQgPSAiZGF5MTUiKSAlPiUgbXV0YXRlKGRheSA9IDE1KSAlPiUgbXV0YXRlKGV4cGVyaW1lbnQgPSAiZXhwNDIiKSAtPiAgZDE1XzN0DQptIC0+IHJvd25hbWVzKGQxNV8zdCkNCg0KZDE1XzQ8LSByZWFkLmNzdigiLy9hdGxhcy51bmkubHV4L3VzZXJzL2lzYWJlbC5yb3NldHkvR0JBL01FQS8yMDIxMDgxMl9NRUFfRTQyX0U0My9FNDNEMTUvMjAyMTA4MjBfRTQzX0QxNSgwMDEpLmNzdiIsaGVhZGVyID0gVCwgc2tpcCA9IDE1Nywgc2VwPSI7IiwgICAgIG5yb3dzID0gMjYpDQpuIDwtIGQxNV80JE1lYXN1cmVtZW50DQpkMTVfNHQgPC0gYXMuZGF0YS5mcmFtZSh0KGQxNV80WywtMV0pKSANCmNvbG5hbWVzKCBkMTVfNHQpIDwtIG4gIA0KbSA8LSByb3duYW1lcyhkMTVfNHQpIA0KZDE1XzN0ICU+JSBtdXRhdGUod2VsbHMgPSByb3duYW1lcyhkMTVfNHQpKSAlPiUgbXV0YXRlKHRpbWVQb2ludCA9ICJkYXkxNSIpICU+JSBtdXRhdGUoZGF5ID0gMTUpICU+JSBtdXRhdGUoZXhwZXJpbWVudCA9ICJleHA0MyIpIC0+ICBkMTVfNHQNCm0gLT4gcm93bmFtZXMoZDE1XzR0KSAgDQogDQogDQpgYGANCg0KTWVyZ2UgZGF0YS4gb2J0YWluIHN1bWFyeSBkYXRhIGZpbGUgKG92ZXIgdGhyZXNob2xkIGFuZCB3aXRob3V0IHRocmVzaG9sZCkgDQpgYGB7cn0NCg0KIyBjYWhuZ2UgZm9sZGVyIGZvciByZXN1bHRzIA0Kc2V0d2QoIi8vYXRsYXMudW5pLmx1eC91c2Vycy9pc2FiZWwucm9zZXR5L0dCQS9NRUEvUnN0dWRpb0FuYWx5c2lzLyIpDQoNCiAjIyBtZXJnZSBhbGwgZmlsZXMgZm9ybSBkaWZmZXJlbnQgdGltZSBwb2ludHMgDQpkYXRhV2VsbCA8LSByYmluZChkMTVfMXQsZDE1XzJ0LGQxNV8zdCxkMTVfNHQpDQoNCiNkYXRhV2VsbCA8LSByYmluZChkMTVfMXQsZDE1XzJ0LGQxNV8zdCxkMTVfNHQpDQojZGF0YVdlbGxbZGF0YVdlbGw9PTBdIDwtIE5BDQpoZWFkKGRhdGFXZWxsKQ0KDQojIGFkZCBjb25kaXRpb25zIHRvIHdlbGxzIA0KDQoNCmNvbG5hbWVzKGRhdGFXZWxsKTwtYyggJ051bWJlck9mU3Bpa2VzJywgJ01lYW5GaXJpbmdSYXRlJywgJ0lTSScsICdOdW1iZXJPZkJ1cnN0JywnQnVyc3REdXJhdGlvbl9hdnJnJywgJ0J1cnN0RHVyYXRpb25fc3RkJywgJ051bWJlclNwaWtlc1BlckJ1cnN0X2F2ZycsICdOdW1iZXJTcGlrZXNQZXJCdXJzdF9zdGQnLCAnbWVhbklTSV9idXJzdF9hdmcnLCdtZWFuSVNJX2J1cnN0X3N0ZCcsJ21lZGlhbklTSV9idXJzdF9hdmcnLCdtZWRpYW5JU0lfYnVyc3Rfc3RkJywnSW50ZXJCdXJzdEludGVydmFsX2F2ZycsICdJbnRlckJ1cnN0SW50ZXJ2YWxfc3RkJywgJ0J1cnN0RnJlcXVlbmN5JywgJ0lCSV9Db2VmZmljaWVudE9mVmFyaWF0aW9uJywgJ05vcm1hbGl6ZWRfRHVyYXRpb25fSVFSJywnQnVyc3RQZXJjZW50YWdlJywgJ2NvbmRpdGlvbicsJ0NlbGxMaW5lJywnd2VsbHMnLCAndGltZXBvaW50JywgJ2RheScsJ2V4cGVyaW1lbnQnKQ0KDQojIGNyZWF0ZSBjb2x1bSBmb3IgcmVtb3ZhbCBvZiBvdXRsaWVycyAocGVyIGNvbmRpdGlvbiAmIHRpbWVwb2ludCkNCmRhdGFXZWxsICU+JSBtdXRhdGUob3V0ID0gcGFzdGUoZGF0YVdlbGwkY29uZGl0aW9uLCBkYXRhV2VsbCR0aW1lcG9pbnQpKSAtPiAgZGF0YVdlbGwgI2hlcmUgd2UgY3JlYXRlIGEgbmV3IGNvbHVtbiBjYWxsZWQgb3V0LCB3aXRoIGNvbmRpdGlvbiBhbmQgdGltZXBvaW50LCBiZWNhdXNlIG91dGxpZXJzIHdpbGwgYmUgY2FsY3VsYXRlZCBiYXNlZCBvbiBjb25kaXRpb24gYW5kIHRpbWVwb2ludA0KDQojIGNvbnZlcnQgaW50byBudW1lcmljIGFsbCBkYXRhIG9mIGludGVyZXN0IGZybyBwbG90dGluZyAobmVlZGVkIGJlY2F1c2UgZGF0YSB3YXMgYXMgY2hhcmFjdGVyKSANCmRhdGFXZWxsJE51bWJlck9mU3Bpa2VzIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGRhdGFXZWxsJE51bWJlck9mU3Bpa2VzKSkNCmRhdGFXZWxsJE1lYW5GaXJpbmdSYXRlIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGRhdGFXZWxsJE1lYW5GaXJpbmdSYXRlKSkNCmRhdGFXZWxsJElTSSA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihkYXRhV2VsbCRJU0kpKQ0KZGF0YVdlbGwkTnVtYmVyT2ZCdXJzdCA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihkYXRhV2VsbCROdW1iZXJPZkJ1cnN0KSkNCmRhdGFXZWxsJEJ1cnN0RHVyYXRpb25fYXZyZyA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihkYXRhV2VsbCRCdXJzdER1cmF0aW9uX2F2cmcpKQ0KZGF0YVdlbGwkQnVyc3REdXJhdGlvbl9zdGQgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZGF0YVdlbGwkQnVyc3REdXJhdGlvbl9zdGQpKQ0KZGF0YVdlbGwkTnVtYmVyU3Bpa2VzUGVyQnVyc3RfYXZnIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGRhdGFXZWxsJE51bWJlclNwaWtlc1BlckJ1cnN0X2F2ZykpDQpkYXRhV2VsbCROdW1iZXJTcGlrZXNQZXJCdXJzdF9zdGQgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZGF0YVdlbGwkTnVtYmVyU3Bpa2VzUGVyQnVyc3Rfc3RkKSkNCmRhdGFXZWxsJG1lYW5JU0lfYnVyc3RfYXZnIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGRhdGFXZWxsJG1lYW5JU0lfYnVyc3RfYXZnKSkNCmRhdGFXZWxsJG1lYW5JU0lfYnVyc3Rfc3RkIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGRhdGFXZWxsJG1lYW5JU0lfYnVyc3Rfc3RkKSkNCmRhdGFXZWxsJG1lZGlhbklTSV9idXJzdF9zdGQgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZGF0YVdlbGwkbWVkaWFuSVNJX2J1cnN0X3N0ZCkpDQpkYXRhV2VsbCRtZWRpYW5JU0lfYnVyc3RfYXZnIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGRhdGFXZWxsJG1lZGlhbklTSV9idXJzdF9hdmcpKQ0KZGF0YVdlbGwkSW50ZXJCdXJzdEludGVydmFsX2F2ZyA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihkYXRhV2VsbCRJbnRlckJ1cnN0SW50ZXJ2YWxfYXZnKSkNCmRhdGFXZWxsJEludGVyQnVyc3RJbnRlcnZhbF9zdGQgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZGF0YVdlbGwkSW50ZXJCdXJzdEludGVydmFsX3N0ZCkpDQpkYXRhV2VsbCRCdXJzdEZyZXF1ZW5jeSA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihkYXRhV2VsbCRCdXJzdEZyZXF1ZW5jeSkpDQpkYXRhV2VsbCRJQklfQ29lZmZpY2llbnRPZlZhcmlhdGlvbiA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihkYXRhV2VsbCRJQklfQ29lZmZpY2llbnRPZlZhcmlhdGlvbikpDQpkYXRhV2VsbCROb3JtYWxpemVkX0R1cmF0aW9uX0lRUiA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihkYXRhV2VsbCROb3JtYWxpemVkX0R1cmF0aW9uX0lRUikpDQpkYXRhV2VsbCRCdXJzdFBlcmNlbnRhZ2UgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZGF0YVdlbGwkQnVyc3RQZXJjZW50YWdlKSkNCmRhdGFXZWxsJGRheSA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihkYXRhV2VsbCRkYXkpKQ0KDQoNCiMgb2J0YWluIGRhdGEgb25seSB3aXRoIGJ1cnN0aW5nIGluZm9ybWF0aW9uIGZvciBleHRyYWN0aW5nIGludG8gQ1NWIGFuZCBwbG90dGluZyBpbiBncmFwaGFkIChuZWVkZWQgdG8gdGFrZSBpbnRvIGNvbnNpZGVyYXRpb24gU0QgYWxyZWFkeSBjYWxjdWxhdGVkIGJ5IHNvZnR3YXJlKQ0KZGF0YVdlbGxfYnVyc3QgPC0gZGF0YVdlbGxbZGF0YVdlbGwkTnVtYmVyT2ZCdXJzdCAhPSAwLCBdICNyZW1vdmUgd2VsbHMgd2l0aCBubyBpbmZvcm1hdGlvbiBmb3IgYnVyc3RpbmcgDQoNCmRhdGFXZWxsXzVzcGlrZSA8LSBkYXRhV2VsbFtkYXRhV2VsbCROdW1iZXJPZlNwaWtlcyA+PSA1LCBdICN0aGlzIGlzIHRvIHNlbGVjdCB0aGUgbWVhc3VyZW1lbnRzIHdpdGggbW9yZSB0aGFuIDUgc3Bpa2VzIHBlciBtaW51dGUNCmRhdGFXZWxsXzVzcGlrZV9idXJzdCA8LSBkYXRhV2VsbF9idXJzdFtkYXRhV2VsbF9idXJzdCROdW1iZXJPZlNwaWtlcyA+PSA1LCBdDQpkYXRhV2VsbF81c3Bpa2U8LWZpbHRlcihkYXRhV2VsbF81c3Bpa2UsIGNvbmRpdGlvbiAlaW4lIGMoIkNvbnRyb2wiLCJQRF9OMzcwUyIpKQ0KDQpmZWF0dXJlX25hbWVzIDwtIGNvbG5hbWVzKGRhdGFXZWxsXzVzcGlrZVssYygxOjQsMTU6MTgpXSkgIA0KI3NhdmluZyBpbmZvOiANCg0KICAjc2F2ZSBzdW1tYXJ5IGZpbGUgaW4gQ1NWDQogIHdyaXRlLmNzdihkYXRhV2VsbCxmaWxlPSJkYXRhV2VsbF9hbGxJbmZvLmNzdiIpDQogIHdyaXRlLmNzdihkYXRhV2VsbF81c3Bpa2UsZmlsZT0iZGF0YVdlbGxfNXNwaWtlLmNzdiIpDQogICMgc2F2ZSBpbmZvcm1hdGlvbiByZWFncmRpbmcgYXZlcmFnZSBhbmQgc3RkDQogIHdyaXRlLmNzdihkYXRhV2VsbF9idXJzdCxmaWxlPSJkYXRhV2VsbF9idXJzdF9hbGxJbmZvLmNzdiIpIA0KICB3cml0ZS5jc3YoZGF0YVdlbGxfNXNwaWtlX2J1cnN0LGZpbGU9ImRhdGFXZWxsXzVzcGlrZV9idXJzdC5jc3YiKQ0KDQoNCmBgYA0KDQpSZW1vdmUgb3V0bGllcnMgYmFzZWQgb24gMjV0aCAmIDc1dGggcGVyY2VudGlsZXMgKGZvciBvbmx5IHRoZSBzcGVjaWZpYyBmZWF0dXJlIHVuZGVyIHN0dWR5XyBpbnRyb2R1Y2V0aW9uIE5BcykgdGhlcyByZXN0IG9mIHRoZSBpbmZvcm1hdGlvbiBmb3IgdGhlIGZ1bGwgd2VsbCByZW1haW5zIA0KYGBge3J9IA0KIyBOT1RFISEgaWYgemVyb3MgYXJlIGtlcHQsIG1heSBpbmZvbWF0aW9uIHdpbGwgYmUgZXh0cmFjdGVkIGJhc2VkIG9uIGEgc2ltcGxlIHZhbHVlIGR1ZSB0byB0aGUgY2Vyb3MgYmVpbmcgdGhlIG1lYW4gISEgDQojIHRyYW5zZnJvbSAwIGludG8gTkFzIA0KDQojaW4gdGhpcyBvbmUgaXQgd29udCByZW1vdmUgdGhlIGVudGlyZSBlbGVjdHJvZGUsIGl0IHdpbGwgb25seSByZW1vdmUgdGhlIHZhbHVlIG9mIHRoZSBvdXRsaWVyIGZlYXR1cmUNCg0KZGF0YVdlbGxfbm9aZXJvIDwtIHJlcGxhY2UoZGF0YVdlbGxfNXNwaWtlLCBkYXRhV2VsbF81c3Bpa2UgPT0gMCwgTkEpDQojZGF0YVdlbGxfbm9aZXJvIDwtIHJlcGxhY2UoZGF0YVdlbGwsIGRhdGFXZWxsID09IDAsIE5BKQ0KDQogZ3JvdXBPdXQgPC0gdW5pcXVlKGRhdGFXZWxsX25vWmVybyRvdXQpICMgZ3JvdXBvdXQgDQpkYXRhdGVzdF9OQSA8LSBkYXRhLmZyYW1lKCkNCg0KDQpmb3IgKGkgaW4gMTpsZW5ndGgoZ3JvdXBPdXQpKQ0Kew0KICBkYXRhdGVzdCA8LSBkYXRhV2VsbF9ub1plcm8gJT4lIA0KICBmaWx0ZXIob3V0ICVpbiUgZ3JvdXBPdXRbaV0pICMgZ28gZm9yIGVhY2ggbGluZSBhbmQgZXh0cmFjdCByb3dzIGZvciB0aGF0IGxpbmUgDQogIGZvciAoaiBpbiAxOjE4KSAjIGZvciAoaiBpbiAyOmxlbmd0aChleHBycykpDQogIHsNCiAgICBvdXQgPC0gYm94cGxvdC5zdGF0cyhkYXRhdGVzdFssal0pJG91dA0KICAgIGZvciAoayBpbiAxOmxlbmd0aChvdXQpKQ0KICAgIHsNCiAgICBkYXRhdGVzdFssal1bZGF0YXRlc3RbLGpdID09IG91dFtrXV0gPC0gTkEgI2l0IGNoYW5nZXMgdGhlIG91dGxpZXIgdmFsdWUgZm9yIE5BDQogICAgfQ0KICAgIHJtKG91dCkNCiAgfQ0KICBkYXRhdGVzdF9OQSA8LSByYmluZChkYXRhdGVzdF9OQSxkYXRhdGVzdCkNCiAgcm0oZGF0YXRlc3QpDQp9IA0KDQoNCiNzYXZpbmcgaW5mbzogDQojIGZpbGVzIHdpdGhvdXQgb3V0bGllcnMgd2VyZSBnZW5lcmF0ZWQgZnJvbSBmaWxlIE5vIHplcm9zIC0gcmVhc29uOiB0b28gbWFueSB6ZXJvcyBjYXVzZSBleGNsdXNpb24gb2YgcmVhbCB2YWx1ZXMgYXMgb3V0bGllcnMgDQojIF93ZWxsID4gbWVhbmluZ24gYWxsIHdlbGwgaW5mb3JtYXRpb24gd2FzIHJlbW92ZWQgZm9yIGFsbCB2YXJpYWJsZSB3aGVuIGF0IGxlYXN0IG9uZSBvZiB0aGUgdmFyaWFibGVzIHByZXNlbnRlZCBvbmUgb3V0bGllciAtPiB0aGUgd2VsbCBpcyBub3QgY29uc2lkZXIgYW55IGxvbmdlciANCg0KIyBSZW1vdmUgYW4gZW50aXJlIHJvdyBpZiBpdCBoYXMgPjIyIE5BcyAoZm9yIGV4YW1wbGUgPjUwJSBvZiB5b3VyIGZlYXR1cmVzKQ0KY291bnRfbmEgPC0gZnVuY3Rpb24oeClzdW0oaXMubmEoeCkpDQpkYXRhX25vT3V0X3ZhbHVlIDwtIGRhdGF0ZXN0X05BICU+JQ0KICBkcGx5cjo6bXV0YXRlKGNvdW50X25hID0gYXBwbHkoLiwgMSwgY291bnRfbmEpKQ0KZGF0YV9ub091dF92YWx1ZSA8LWRhdGF0ZXN0X05BWyEoZGF0YV9ub091dF92YWx1ZSRjb3VudF9uYSA+PSAxOSksXSAjIHJlbW92ZSB3ZWxscyBjb250YWluaW5nIG5vIGluZm8gDQoNCmRhdGFXZWxsX2J1cnN0X05vT3V0X3ZhbHVlIDwtIHN1YnNldChkYXRhX25vT3V0X3ZhbHVlLCAhaXMubmEoTnVtYmVyT2ZCdXJzdCkpICNyZW1vdmUgd2VsbHMgd2l0aCBubyBpbmZvcm1hdGlvbiBmb3IgYnVyc3RpbmcNCg0KICB3cml0ZS5jc3YoZGF0YVdlbGxfYnVyc3RfTm9PdXRfdmFsdWUsZmlsZT0iZGF0YVdlbGxfYnVyc3RfTm9PdXRfdmFsdWUuY3N2IikgIyBpbmZvIGZvciBidXJzdGluZyAoZ3JhcGhhZCkgXyBvbmx5IHRoZSBzcGVjaWZpYyBvdXRsaWVyIHdhcyByZW1vdmVkLiB3ZWxsIGluZm9ybWF0aW9uIGZvciBvdGhlciB2YXJpYWJsZXMgcmVtYWluICANCiAgd3JpdGUuY3N2KGRhdGFfbm9PdXRfdmFsdWUsZmlsZT0iZGF0YV9ub091dF92YWx1ZS5jc3YiKSAjIGluZm9yIC0gbm8gb3V0bGllcnMgZm9yIHNwZWNpZmljIHZhcmlhYmxlIC13ZWxsIGluZm8gcmVtYWlucyANCiAgDQoNCg0KI2RhdGFXZWxsMj1kYXRhV2VsbFshZ3JlcGwoImV4cDQwIixkYXRhV2VsbCRleHBlcmltZW50KSxdDQpmZWF0dXJlX25hbWVzIDwtIGNvbG5hbWVzKGRhdGFfbm9PdXRfdmFsdWVbLGMoMTo0LDE1OjE4KV0pICANCmhlYWQoZGF0YV9ub091dF92YWx1ZSkNCiAgDQpgYGANCg0KU2hhcGlybyBUZXN0IHRvIGNoZWNrIG5vcm1hbGl0eQ0KIyBwbG90dGluZyB3aXRob3V0IFNEIGFscmVhZHkgY2FsY3VsYXRlZCAoIk51bWJlck9mU3Bpa2VzIiAiTWVhbkZpcmluZ1JhdGUiICAiSVNJIiAgIk51bWJlck9mQnVyc3QiICAiTnVtYmVyQnVyc3RpbmdFbGVjdHJvZGVzIikNCmBgYHtyfQ0KI2lmIHAtdmFsdWUgPjAuMDUsIG5vcm1hbGl0eSBjYW4gYmUgYXNzdW1lZA0KVGVzdFNoYXBpcm88LSBzZWxlY3QoZGF0YV9ub091dF92YWx1ZSwgYygxOjE4KSkNCiNzaGFwaXJvLnRlc3QoZGF0YV9ub091dF92YWx1ZSRNZWFuRmlyaW5nUmF0ZSkNCmFwcGx5KFRlc3RTaGFwaXJvLDIsc2hhcGlyby50ZXN0KQ0KDQpgYGANCg0KDQpQbG90cw0KYGBge3Igd2FybmluZz1GQUxTRX0NCg0KbmFtZXMgPC0gYygiTnVtYmVyIE9mIFNwaWtlcyIsICJNZWFuIEZpcmluZyBSYXRlIChzcGlrZXMvcykiICwgIkludGVyc3Bpa2UgSW50ZXJ2YWwgW0lTSV0iICAsIk51bWJlciBPZiBCdXJzdHMiICwgIkJ1cnN0IEZyZXF1ZW5jeSAoYnVyc3RzL3MpIiwiSUJJIENvZWZmaWNpZW50IE9mIFZhcmlhdGlvbiIsICJOb3JtYWxpemVkIER1cmF0aW9uIElRUiIgICwgICJCdXJzdCBQZXJjZW50YWdlIikNCg0KZm9yIChpIGluIDE6bGVuZ3RoKGZlYXR1cmVfbmFtZXMpKSB7DQpkYXRhX25vT3V0X3ZhbHVlICAlPiUgI2NoYW5nZSBkYXRhc2V0IHNlbGVjdGVkIC0gY2hhbmdlIHNhdmluZyBvcHRpb24gZGVwZW5kaW5nIG9uIHRoZSBkYXRhc2V0IA0KICBwaXZvdF9sb25nZXIoY29scz1mZWF0dXJlX25hbWVzLCBuYW1lc190byA9ICJmZWF0dXJlIiwgdmFsdWVzX3RvID0gInZhbHVlIikgJT4lDQogIGZpbHRlcihmZWF0dXJlICVpbiUgZmVhdHVyZV9uYW1lc1tpXSkgJT4lDQogICNnZ3Bsb3QoIGFlcyh4ID0gZmFjdG9yKGNvbmRpdGlvbiwgbGV2ZWwgPSBjKCAnQ29udHJvbCcsJ1BEX04zNzBTJywgJ0NvbnRyb2xfR0RORicsJ1BEX04zNzBTX0dETkYnKSksIHk9dmFsdWUpICkgKw0KICAjZ2VvbV9ib3hwbG90KGFlcyhmaWxsPWZjdF9yZWxldmVsKGNvbmRpdGlvbiwgJ0NvbnRyb2wnLCdQRF9OMzcwUycsICdDb250cm9sX0dETkYnLCdQRF9OMzcwU19HRE5GJykpLHdpZHRoPTAuNSxvdXRsaWVyLnNoYXBlPU5BKSArDQogIGdncGxvdCggYWVzKHggPSBmYWN0b3IoY29uZGl0aW9uLCBsZXZlbCA9IGMoICdDb250cm9sJywnUERfTjM3MFMnKSksIHk9dmFsdWUpICkgKw0KICBnZW9tX2JveHBsb3QoYWVzKGZpbGw9ZmN0X3JlbGV2ZWwoY29uZGl0aW9uLCAnQ29udHJvbCcsJ1BEX04zNzBTJykpLHdpZHRoPTAuOCxvdXRsaWVyLnNoYXBlPU5BKSArDQogICAgZ2VvbV9wb2ludChhZXMoY29sb3I9Q2VsbExpbmUpLHNpemU9MyxzaG93LmxlZ2VuZCA9IFQsYWxwaGEgPSAwLjUpKw0KICAgDQogICAgc2NhbGVfY29sb3JfamNvbG9ycygicGFsNyIpKw0KICAgIHRoZW1lKGxlZ2VuZC5rZXk9ZWxlbWVudF9ibGFuaygpKSArDQogICNzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9IGMoIiMwMDhCOEIiLCIjQjIyMjIyIiwiIzIxNzFiNSIsIiMwMDAwNjYiKSxuYW1lID0gImNvbmRpdGlvbiIsIGd1aWRlID0gIm5vbmUiKSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPSBjKCJ3aGl0ZSIsICJibGFjayIpLG5hbWUgPSAiY29uZGl0aW9uIiwgZ3VpZGUgPSAibm9uZSIpICsgIA0KICAjc3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zPWxpc3QoYygiV1QiLCAiM3hTTkNBIikpLCBtZXRob2QgPSAidC50ZXN0IiwgbGFiZWw9InAuc2lnbmlmIiwgbGFiZWwueCA9IDEuNSkrDQogIA0KICAjIE1hbm4tV2hpdG5leQ0KICAgZ2VvbV9zaWduaWYoY29tcGFyaXNvbnMgPSBsaXN0KGMoJ0NvbnRyb2wnLCdQRF9OMzcwUycpKSwgdGVzdD0nd2lsY294LnRlc3QnLA0KICAgICAgICAgICAgICB2anVzdD0wLjYsIHNpemU9MC41LCB0ZXh0c2l6ZT05LCBtYXBfc2lnbmlmX2xldmVsPWMoIioqKiI9MC4wMDEsICIqKiI9MC4wMSwgIioiPTAuMDUsICAiICI9MikgKSArDQogIGZhY2V0X2dyaWQofmZjdF9yZWxldmVsKHRpbWVwb2ludCwgImRheTEwIiwgImRheTE1IiwiZGF5MzAiLCAiZGF5NjAiLCJkYXkxMDAiKSwgc2NhbGVzPSJmcmVlIikgICsNCiAgICANCg0KICBsYWJzKHggICAgID0iIiwNCiAgICAgICB5ICAgICA9IHBhc3RlKG5hbWVzW2ldKSwNCiAgICAgICBmaWxsICA9ICJDb25kaXRpb24iLA0KICAgICAgIHRpdGxlID0gcGFzdGUobmFtZXNbaV0pKSArDQogICAgDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZSgNCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2JsYWNrJywgc2l6ZSA9IDAuNSkgLA0KICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTIxLCBjb2xvcj0iYmxhY2siKSwNCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIxKSwNCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTE1LCBjb2xvcj0iYmxhY2siKSwNCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2xpbmUoKSwNCiAgICBheGlzLnRpY2tzLmxlbmd0aD11bml0KC4yNSwgImNtIiksDQogICAgI2NoYW5nZSBsZWdlbmQgdGV4dCBmb250IHNpemUpDQogICAgI2xlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC43LCAiY20iKSwNCiAgICAjbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoMC42LCJjbSIpLA0KICAgIGxlZ2VuZC5rZXk9ZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIA0KICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwLCBoanVzdD0wLjUsIHZqdXN0PSAxLCBmYWNlID0gImJvbGQiKSwNCiAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCNlbGVtZW50X3RleHQoc2l6ZSA9IDIsIGhqdXN0PTAuNSkNCiAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTIsIHZqdXN0PTAuNSksDQogICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsPSJsaWdodGdyYXkiKSwNCiAgICMgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAiYmxhY2siKSwNCiAgICBwYW5lbC5zcGFjaW5nLnkgPSB1bml0KDAuOCwgImxpbmVzIiksDQogICAgc3RyaXAuc3dpdGNoLnBhZC53cmFwPXVuaXQoMjAsICJsaW5lcyIpLA0KICAgIGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiLA0KICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTcpLA0KICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTE5KQ0KICApICAtPiBwDQogIHQ8LSBjb3dwbG90OjpnZ2RyYXcoY293cGxvdDo6YWRkX3N1YihwLCAiV2lsY294LXRlc3QsICoqKnA9MC4wMDEsICoqcD0wLjAxLCAqcD0wLjA1IixoanVzdD0tMC41LCBzaXplPTE1KSkNCiAgcHJpbnQodCkNCg0KI2dnc2F2ZShwYXN0ZTAoU3lzLkRhdGUoKSwiXyIsIGZlYXR1cmVfbmFtZXNbaV0sICJfdmFsdWVfZWxlY3Ryb2RlX0RJVjE1LnBkZiIpLCBwbG90PXQsaGVpZ2h0PTQpICMgY2hhbmdlIHNhdmluZyBvcHRpb24gZGVwZW5kaW5nIG9uIHRoZSBkYXRhc2V0IA0KDQp9DQoNCmBgYA0KUGVyIGNlbGwgbGluZQ0KYGBge3J9DQoNCmZvciAoaSBpbiAxOmxlbmd0aChmZWF0dXJlX25hbWVzKSkgew0KZGF0YVdlbGxfNXNwaWtlICU+JSAjY2hhbmdlIGRhdGFzZXQgc2VsZWN0ZWQgLSBjaGFuZ2Ugc2F2aW5nIG9wdGlvbiBkZXBlbmRpbmcgb24gdGhlIGRhdGFzZXQgDQogIHBpdm90X2xvbmdlcihjb2xzPWZlYXR1cmVfbmFtZXMsIG5hbWVzX3RvID0gImZlYXR1cmUiLCB2YWx1ZXNfdG8gPSAidmFsdWUiKSAlPiUNCiAgZmlsdGVyKGZlYXR1cmUgJWluJSBmZWF0dXJlX25hbWVzW2ldKSAlPiUNCiAgI2dncGxvdCggYWVzKHggPSBmYWN0b3IoY29uZGl0aW9uLCBsZXZlbCA9IGMoICdDb250cm9sJywnUERfTjM3MFMnLCAnQ29udHJvbF9HRE5GJywnUERfTjM3MFNfR0RORicpKSwgeT12YWx1ZSkgKSArDQogICNnZW9tX2JveHBsb3QoYWVzKGZpbGw9ZmN0X3JlbGV2ZWwoY29uZGl0aW9uLCAnQ29udHJvbCcsJ1BEX04zNzBTJywgJ0NvbnRyb2xfR0RORicsJ1BEX04zNzBTX0dETkYnKSksd2lkdGg9MC41LG91dGxpZXIuc2hhcGU9TkEpICsNCiAgZ2dwbG90KCBhZXMoeCA9IGZhY3RvcihDZWxsTGluZSwgbGV2ZWwgPSBjKCAnV1QgNTYnLCdXVCAzOScsICdXVCA2OCcsICdNVVQgMzA5JywgJ01VVCBLVEk2JywgJ01VVCBTR08xJykpLCB5PXZhbHVlKSApICsNCiAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPUNlbGxMaW5lKSx3aWR0aD0wLjgsb3V0bGllci5zaGFwZT1OQSkgKw0KICMgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPSBjKCIjMDA4QjhCIiwiI0IyMjIyMiIsIiMyMTcxYjUiLCIjMDAwMDY2IiksbmFtZSA9ICJjb25kaXRpb24iKSArDQogICAgZ2VvbV9wb2ludChhZXMoY29sb3I9ZXhwZXJpbWVudCksc2l6ZT0yKSsNCiAgICAjc2NhbGVfY29sb3JfdmlyaWRpcyhvcHRpb24gPSAiRCIsIGRpc2NyZXRlPVRSVUUpKw0KICAgIGdlb21fcG9pbnQoc2hhcGUgPSAxLHNpemUgPSAyLGNvbG91ciA9ICJibGFjayIpKw0KICAgIyB0LXRlc3QNCiAgI3N0YXRfY29tcGFyZV9tZWFucyhjb21wYXJpc29ucz1saXN0KGMoIldUIiwgIjN4U05DQSIpKSwgbWV0aG9kID0gInQudGVzdCIsIGxhYmVsPSJwLnNpZ25pZiIsIGxhYmVsLnggPSAxLjUpKw0KICANCiAgIyBNYW5uLVdoaXRuZXkNCiAgI2dncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zPWxpc3QoYygiQ29udHJvbCIsICJQRF9OMzcwUyIpKSwgbWV0aG9kPSJ3aWxjb3gudGVzdCIsIHAuYWRqdXN0Lm1ldGhvZD0iQkgiLGxhYmVsPSJwLnNpZ25pZiIsIGxhYmVsLnggPSAxLjUpKw0KICANCiAgICANCiAgbGFicyh4ICAgICA9IiIsDQogICAgICAgeSAgICAgPSBwYXN0ZShuYW1lc1tpXSksDQogICAgICAgZmlsbCAgPSAiQ2VsbExpbmUiLA0KICAgICAgIHRpdGxlID0gcGFzdGUobmFtZXNbaV0pKSArDQogIHRoZW1lX2J3KCkgKw0KICAgIA0KICB0aGVtZSgNCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2JsYWNrJywgc2l6ZSA9IDAuMSkgLA0KICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSxzaXplID0gNyxoanVzdD0xKSwgI2FuZ2xlPTQ1KSwNCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSwNCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTE1LCBjb2xvcj0iYmxhY2siKSwNCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2xpbmUoKSwNCiAgICBsZWdlbmQucG9zaXRpb249InJpZ2h0IiwNCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEsIGZhY2UgPSAiYm9sZCIpICwNCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjcsICJjbSIpLA0KICAgIGxlZ2VuZC5rZXkud2lkdGggPSB1bml0KDAuNiwiY20iKSwNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSwgaGp1c3Q9MC41LCB2anVzdD0gMSwgZmFjZSA9ICJib2xkIiksDQogICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwjZWxlbWVudF90ZXh0KHNpemUgPSAyLCBoanVzdD0wLjUpDQogICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTEyLCB2anVzdD0wLjUpLA0KICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD0ibGlnaHRncmF5IiksDQogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAiYmxhY2siKSwNCiAgICBwYW5lbC5zcGFjaW5nLnkgPSB1bml0KDAuOCwgImxpbmVzIiksDQogICAgc3RyaXAuc3dpdGNoLnBhZC53cmFwPXVuaXQoMjAsICJsaW5lcyIpDQogICkgIC0+IHANCiAgI3Q8LSBjb3dwbG90OjpnZ2RyYXcoY293cGxvdDo6YWRkX3N1YihwLCAiV2lsY294LXRlc3QsICoqKnA9MC4wMDEsICoqcD0wLjAxLCAqcD0wLjA1IixoanVzdD0tMC41LCBzaXplPTkpKQ0KICBwcmludChwKQ0KDQojZ2dzYXZlKHBhc3RlMChTeXMuRGF0ZSgpLCJfIiwgZmVhdHVyZV9uYW1lc1tpXSwgIl92YWx1ZV9lbGVjdHJvZGVfTm9PdXRsaWVycy5wZGYiKSwgcGxvdD10KSAjIGNoYW5nZSBzYXZpbmcgb3B0aW9uIGRlcGVuZGluZyBvbiB0aGUgZGF0YXNldCANCg0KfQ0KYGBgDQoNCk9uZSBmZWF0dXJlDQpgYGB7cn0NCg0KZGF0YVdlbGxPbmVGIDwtZmlsdGVyKGRhdGFXZWxsTm9OQSx0aW1lcG9pbnQ9PSJkYXkxNSIpDQpkYXRhV2VsbE9uZUYlPiUNCiAgZ2dwbG90KGFlcyh4PWRheSwgeT1NZWFuRmlyaW5nUmF0ZSwgZ3JvdXA9Y29uZGl0aW9uLCBjb2xvcj1jb25kaXRpb24pKSArDQogIGdlb21fdmlvbGluKG1ldGhvZD0nbG9lc3MnLCBmb3JtdWxhID0neSB+IHgnLCBzaXplID0gMSkrDQogIHNjYWxlX2NvbG91cl9tYW51YWwobmFtZT0iQ29uZGl0aW9uIiwgdmFsdWVzPSBjKCJDb250cm9sIj0gIiMwMDhCOEIiLCAiUERfTjM3MFMiPSIjQjIyMjIyIikpICsNCiAgIyBmYWNldF93cmFwKH50aW1lcG9pbnQsIHNjYWxlcz0iZnJlZSIsIGxhYmVsbGVyID0gbGFiZWxsZXIoZ3JvdXB3cmFwID0gbGFiZWxfd3JhcF9nZW4oMTApKSkgKw0KICAjIGZhY2V0X2dyaWQofmZjdF9yZWxldmVsKHRpbWVwb2ludCwgImRheTE1IiwiZGF5MzAiLCAgImRheTcwIiwgImRheTkwIiksIHNjYWxlcz0iZnJlZSIpICsNCiAgbGFicyh4PSJUaW1lIG9mIGRpZmZlcmVudGlhdGlvbiBbZGF5c10iLA0KICAgICAgICB5ICAgICA9ICJNZWFuIEZpcmluZyBSYXRlIiwNCiAgICAgICBmaWxsICA9ICJDb25kaXRpb24iLA0KICAgICAgIHRpdGxlID0gIk1lYW4gRmlyaW5nIFJhdGUiKSArDQogICB0aGVtZV9idygpIC0+IHANCiAgdDwtIGNvd3Bsb3Q6OmdnZHJhdyhjb3dwbG90OjphZGRfc3ViKHAsICJMb2NhbGx5IGVzdGltYXRlZCBzY2F0dGVycGxvdCBzbW9vdGhpbmc6IHkgfiBUaW1lIixoanVzdD0tMC4yLCBzaXplPTkpKQ0KICBwcmludCh0KQ0KICBnZ3NhdmUocGFzdGUwKFN5cy5EYXRlKCksIk1lYW5GaXJpbmdSYXRlX3RpbWVsaW5lX2RhdGFfbm9PdXRfdmFsdWUucGRmIiksIHBsb3Q9dCkNCmBgYA0K