1. Setup Environment

require(ggplot2)
require(summarytools)
require(cowplot)
require(caret)
require(corrplot)
package ‘corrplot’ was built under R version 4.0.2
require(RColorBrewer)
require(vembedr)
require(Rmisc)
require(varian)
package ‘rstan’ was built under R version 4.0.2package ‘StanHeaders’ was built under R version 4.0.2Failed with error:  ‘package ‘rstan’ could not be loaded’
require(patchwork)
require(plotly)
require(Metrics)
require(dplyr)
package ‘dplyr’ was built under R version 4.0.2
require(ggpubr)
require(mosaic)
require(openxlsx)
require(visreg)
require(factoextra)
require(rstatix)
require(gridExtra)
require(colorspace)
require(grid)
require(data.table)
package ‘data.table’ was built under R version 4.0.2
require(psych)
package ‘psych’ was built under R version 4.0.2
require(sna)
source("~/Documents/xia_gps/code/plotting_functions.R")
project_path = "~/Documents/xia_gps/"
data_path = file.path(project_path,"beiwe_output_043020")
gps_df_path = file.path(data_path,"Processed_Data/Group/feature_matrix.txt")

2.Prepare GPS data

gps_df = read.table(gps_df_path,header = T, dec = ",")[,c(1,2,97:111)]
gps_df[,3:17] = apply(gps_df[,3:17],2,as.numeric)
# loop through each subj to remove 1st and last days of gps data
    gps_df_clean = data.frame() #initiate a df
    for (subj in unique(gps_df$IID)){ #loop through each subj
      gps_df_subj <- subset(gps_df, IID == subj) #get gps_df per subject
      gps_df_subj <- gps_df_subj[2:(dim(gps_df_subj)[1]-1),] #remove the 1st and last days
      gps_df_clean <- rbind(gps_df_clean,gps_df_subj) #combine all subjs
    }

    # exclude days with too much excessiveness
    sensitivity_cutoff = 1440 # this controls the cutoff threshold
    gps_df_clean2 = subset(gps_df_clean, MinsMissing < sensitivity_cutoff)
set.seed(510)
make_subj_seq = function(gps_df_clean2,part_times) {
  subj_seq = list()
  for (subj in unique(gps_df_clean2$IID)){
    subj_data = subset(gps_df_clean2, IID==subj)
    if (dim(subj_data)[1] >5) {
    subj_seq[[subj]] <-createDataPartition(subj_data$IID,times = part_times, p =0.5)
    }
  }
  return(subj_seq)
}
part_times = 1000
subj_seq = make_subj_seq(gps_df_clean2, part_times)
# an example of subj 1, and first half
example_data = gps_df_clean2[subj_seq$`16xv6ko1`$Resample0001,3:17]
gps_cor = rquery.cormat(example_data, type = "full")


gps_clean2_feature = make_feature_matrix(gps_df_clean2, subj_seq, 3:17 )
subject_seq = names(gps_clean2_feature$subj_mat_1)

3. Prepare accelerometer data

subj_list = unique(gps_df$IID)
There were 50 or more warnings (use warnings() to see the first 50)
subj_acc_data_clean = as.data.frame(rbindlist(lapply(subj_list, function(subj) { subj_data =  readRDS(file.path(data_path,"Processed_Data/Group/accelerometer",subj,"accelerometer_ft.rds")); subj_data[2:(dim(subj_data)[1]-1),c(2:8,11)]}))) #also removes 1st and last day
cannot open compressed file '/Users/hxia/Documents/xia_gps//beiwe_output_043020/Processed_Data/Group/accelerometer/14w5qlo8/accelerometer_ft.rds', probable reason 'No such file or directory'Error in gzfile(file, "rb") : cannot open the connection
example_acc_data = subj_acc_data_clean[subj_seq$`16xv6ko1`$Resample0001,1:7]
Warning messages:
1: In readChar(file, size, TRUE) : truncating string with embedded nuls
2: In readChar(file, size, TRUE) : truncating string with embedded nuls
acc_cor = rquery.cormat(example_acc_data, type = "full")

subj_acc_data_clean$IID = subj_acc_data_clean$subject
set.seed(510)
acc_subj_seq = make_subj_seq(subj_acc_data_clean, part_times)
acc_clean_feature = make_feature_matrix(subj_acc_data_clean, acc_subj_seq,  c(1:7) )

4. Merge GPS and accelerometer data

gps_acc_combined_feat = list()
for (subj in subj_list){
  for (time in 1:part_times){
    gps_feat1 = gps_clean2_feature$subj_mat_1[[subj]][[time]]$cor
    acc_feat1 = acc_clean_feature$subj_mat_1[[subj]][[time]]$cor
    
    gps_feat2 = gps_clean2_feature$subj_mat_2[[subj]][[time]]$cor
    acc_feat2 = acc_clean_feature$subj_mat_2[[subj]][[time]]$cor

    gps_acc_combined_feat$subj_mat_1[[subj]][[time]] = c(gps_feat1, acc_feat1)
    gps_acc_combined_feat$subj_mat_2[[subj]][[time]] = c(gps_feat2, acc_feat2)
  }
}

5. GPS and accelerometer based Individual Identification

match_combined_gps_accfeat = calc_match_vector(gps_acc_combined_feat$subj_mat_1, gps_acc_combined_feat$subj_mat_2, "cor")
acc_time_cb_accgps  = calc_acc_time(match_combined_gps_accfeat, "max")
acc_subj_cb_accgps  = calc_acc_subj(match_combined_gps_accfeat, "max")
p = hist_chx(acc_time_cb_accgps, bins = 14, title = paste("Individual Identificaiton Accuracy \n (GPS+ Accel)"), xaxis = "Prediction Accuracy", yaxis = "Count")
ggplotly(p)
perm_time = 10
gps_df_perm = gps_df_clean2
acc_df_perm = subj_acc_data_clean

perm_acc_gps_time = list()
perm_acc_gps_subj = list()

for (i in 1:perm_time) {
  perm_part_times = 1
  print(paste("processing ...", i,"..."))
  gps_df_perm$IID = sample(gps_df_perm$IID)
  acc_df_perm$IID = sample(acc_df_perm$IID)
  
  perm_subj_seq = make_subj_seq(gps_df_perm,part_times = perm_part_times)
  perm_acc_subj_seq = make_subj_seq(acc_df_perm,part_times = perm_part_times)
  
  perm_gps_cor = make_feature_matrix(gps_df_perm,perm_subj_seq,3:17)
  perm_acc_cor = make_feature_matrix(acc_df_perm,perm_acc_subj_seq,1:7)
  
  subj_mat_1 = list()
  subj_mat_2 = list()
  
    for (subj in subj_list){
        subj_mat_1[[subj]]$Resample1 = as.numeric(c(perm_gps_cor$subj_mat_1[[subj]][[1]]$cor, perm_acc_cor$subj_mat_1[[subj]][[1]]$cor))
        subj_mat_2[[subj]]$Resample1 = as.numeric(c(perm_gps_cor$subj_mat_2[[subj]][[1]]$cor, perm_acc_cor$subj_mat_2[[subj]][[1]]$cor))
    }
  perm_gps_acc_mats = list(subj_mat_1 = subj_mat_1, subj_mat_2 = subj_mat_2)
  
  perm_match = calc_match_vector(perm_gps_acc_mats$subj_mat_1,perm_gps_acc_mats$subj_mat_2, "cor")
  perm_acc_gps_time[[i]] = calc_acc_time(perm_match, "max")
  perm_acc_gps_subj[[i]] = calc_acc_subj(perm_match, "max")
}


perm_acc_gps_time = unlist(perm_acc_gps_time)
perm_acc_gps_subj = unlist(perm_acc_gps_subj)
df = data.frame(val = perm_acc_gps_time)
Warning messages:
1: In readChar(file, size, TRUE) : truncating string with embedded nuls
2: In readChar(file, size, TRUE) : truncating string with embedded nuls
3: In readChar(file, size, TRUE) : truncating string with embedded nuls
4: In readChar(file, size, TRUE) : truncating string with embedded nuls
5: In readChar(file, size, TRUE) : truncating string with embedded nuls
6: In readChar(file, size, TRUE) : truncating string with embedded nuls
p = ggplot(df, aes(x=val)) + 
    geom_histogram(bins = 7, fill = "#9ECAE1", size = 2) + 
    theme_cowplot() +
    labs(title=paste("GPS+Accel: \n",part_times,"Permutations"),
         x  = "Individual Identificaiton Accuracy", y = "Count") + 
  #geom_density(alpha=.2) +
    theme(plot.title = element_text(hjust = 0.5))
ggplotly(p)
perm_subj = sapply(subject_seq, function(subj) sum(perm_acc_gps_subj[which(names(perm_acc_gps_subj) == subj)])/1000)
Warning messages:
1: In readChar(file, size, TRUE) : truncating string with embedded nuls
2: In readChar(file, size, TRUE) : truncating string with embedded nuls
3: In readChar(file, size, TRUE) : truncating string with embedded nuls
4: In readChar(file, size, TRUE) : truncating string with embedded nuls
5: In readChar(file, size, TRUE) : truncating string with embedded nuls
6: In readChar(file, size, TRUE) : truncating string with embedded nuls
subj_df <- data.frame(x=names(subj_seq))
subj_df$y = acc_subj_cb_accgps
subj_df$y_perm = perm_subj
subj_df = subj_df[order(subj_df$y),]
p_subj_cor_perm = ggplot(subj_df, aes(x = reorder(x, y), y = value)) + 
  geom_point(aes(y = y), color = "#F69274", size = 2) + 
  geom_point(aes(y = y_perm), color = "#9ECAE1", size =2 ) +
  theme_cowplot() + 
  labs(title = paste("GPS+Accel \n Subject Level Accuracy Across",part_times,"Data Partitions"), 
       x = "Subjects", y = "Individual Identification Accuracy") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 8), plot.title = element_text(hjust = 0.5))
ggplotly(p_subj_cor_perm)

6. GPS only based Individual Identification

gps_match_cor = calc_match_cor(gps_clean2_feature$subj_mat_1,gps_clean2_feature$subj_mat_2)
gps_acc_time = calc_acc_time(gps_match_cor, "max")
gps_acc_subj = calc_acc_subj(gps_match_cor)

gps_df_perm = gps_df_clean2
perm_time = 1000
perm_acc_time = list()
perm_acc_subj = list()
for (i in 1:perm_time) {
  perm_part_times = 1
  print(paste("processing ...", i,"..."))
  gps_df_perm$IID = sample(gps_df_perm$IID)
  perm_subj_seq = make_subj_seq(gps_df_perm, part_times = perm_part_times)
  perm_gps = make_feature_matrix(gps_df_perm,perm_subj_seq,3:17)
  perm_mat_1 = perm_gps$subj_mat_1
  perm_mat_2 = perm_gps$subj_mat_2
  perm_match_cor = calc_match_cor(perm_mat_1,perm_mat_2)
  perm_acc_time[[i]] = calc_acc_time(perm_match_cor)
  perm_acc_subj[[i]] = calc_acc_subj(perm_match_cor)
}
p_time_cor = hist_chx(acc_time, bins = 17, title = paste("GPS: \n Individual identification accuracy"), xaxis = "Individual identification accuracy", yaxis = "Count")
ggplotly(p_time_cor)
#perm_acc_time_all = unlist(perm_acc_time)
Warning messages:
1: In readChar(file, size, TRUE) : truncating string with embedded nuls
2: In readChar(file, size, TRUE) : truncating string with embedded nuls
q_time_cor = hist_chx(perm_acc_time_all, bins = 7, title = paste("GPS accuracy across",length(perm_acc_time_all),"Permutations"), xaxis = "Individual Identificaiton Accuracy", yaxis = "Count")
ggplotly(q_time_cor)
perm_acc_subj = unlist(perm_acc_subj)
perm_subj = sapply(subject_seq, function(subj) sum(perm_acc_subj[which(names(perm_acc_subj) == subj)])/1000)

subj_df <- data.frame(x=names(subj_seq))
subj_df$y = gps_acc_subj
subj_df$y_perm = perm_subj
subj_df = subj_df[order(subj_df$y),]
p_subj_cor_perm = ggplot(subj_df, aes(x = reorder(x, y), y = value)) + 
  geom_point(aes(y = y, col = "subject data")) + 
  geom_point(aes(y = y_perm, col = "permutation")) +
  theme_cowplot() + 
  labs(title = paste("GPS \n Subject Level Accuracy Across",perm_time,"permutations"), 
       x = "Subjects", y = "Individual identification accuracy") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 8), plot.title = element_text(hjust = 0.5))
ggplotly(p_subj_cor_perm)

7. Accelerometer only based Individual Identification

acc_match_cor = calc_match_cor(acc_clean_feature$subj_mat_1,acc_clean_feature$subj_mat_2)
acc_acc_time = calc_acc_time(acc_match_cor, "max")
acc_acc_subj = calc_acc_subj(acc_match_cor)
acc_df_perm = subj_acc_data_clean
perm_time = 1000
perm_acc_acc_time = list()
perm_acc_acc_subj = list()
for (i in 1:perm_time) {
  perm_part_times = 1
  print(paste("processing ...", i,"..."))
  acc_df_perm$IID = sample(acc_df_perm$IID)
  perm_subj_seq = make_subj_seq(acc_df_perm, part_times = perm_part_times)
  perm_acc = make_feature_matrix(acc_df_perm,perm_subj_seq,1:7)
  perm_mat_1 = perm_acc$subj_mat_1
  perm_mat_2 = perm_acc$subj_mat_2
  perm_match_cor = calc_match_cor(perm_mat_1,perm_mat_2)
  perm_acc_acc_time[[i]] = calc_acc_time(perm_match_cor)
  perm_acc_acc_subj[[i]] = calc_acc_subj(perm_match_cor)
}
df = data.frame(val = acc_acc_time)
p = ggplot(df, aes(x=val)) + 
    geom_histogram(bins = 15, fill = "#F69274", size = 2) + 
    theme_cowplot() +
    labs(title=paste("Accelerometer Features: \n Average Accuracy Across",part_times,"Data Partitions"),
         x  = "Mobility Footprint Individual Identification Accuracy", y = "Count") + 
  #geom_density(alpha=.2) +
    theme(plot.title = element_text(hjust = 0.5))
ggplotly(p)
df = data.frame(val = perm_acc_acc_time)
p = ggplot(df, aes(x=val)) + 
    geom_histogram(bins = 5, fill = "#9ECAE1", size = 2) + 
    theme_cowplot() +
    labs(title=paste("Accelerometer Features: \n Average Accuracy Across",perm_time,"Permutations"),
         x  = "Mobility Footprint Individual Identification Accuracy", y = "Count") + 
  #geom_density(alpha=.2) +
    theme(plot.title = element_text(hjust = 0.5))
ggplotly(p)
perm_acc_acc_subj = unlist(perm_acc_acc_subj)
There were 40 warnings (use warnings() to see them)
perm_subj = sapply(subject_seq, function(subj) sum(perm_acc_acc_subj[which(names(perm_acc_acc_subj) == subj)])/1000)

subj_df <- data.frame(x=names(subj_seq))
subj_df$y = acc_acc_subj
subj_df$y_perm = perm_subj
subj_df = subj_df[order(subj_df$y),]
p_subj_cor_perm = ggplot(subj_df, aes(x = reorder(x, y), y = value)) + 
  geom_point(aes(y = y, col = "subject data")) + 
  geom_point(aes(y = y_perm, col = "permutation")) +
  theme_cowplot() + 
  labs(title = paste("Accelerometer \n Subject Level Accuracy Across",perm_time,"permutations"), 
       x = "Subjects", y = "Individual identification accuracy") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 8), plot.title = element_text(hjust = 0.5))
ggplotly(p_subj_cor_perm)
df_accgps_time = data.frame(GPS = gps_acc_time, Accelerometer = acc_acc_time, `GPS and Acceleromter` = acc_time_cb_accgps)
df_accgps_time_long = gather(df_accgps_time, feature, accuracy, 1:3, factor_key=TRUE)
mu = ddply(df_accgps_time_long, "feature", summarise, grp.mean=mean(accuracy))
p_accgps_feature = ggplot(df_accgps_time_long, aes(x=accuracy,color = feature, fill = feature)) + 
    geom_density(size = 1, alpha = 0) + 
    #geom_histogram(aes(y=..density..), alpha=0.2, 
    #            position="identity") + 
    geom_vline(data=mu, aes(xintercept=grp.mean, color=feature),
             linetype="dashed") +
    labs(y = "Density", x = "Individual Identitication Accuracy") + 
    theme_cowplot() +
    theme(legend.position="top")
p_accgps_feature

8. Data quantity vs. quality associations

gps_days = gps_df_clean2 %>% group_by(IID) %>% dplyr::tally(name = "gps_days")
Warning messages:
1: In readChar(file, size, TRUE) : truncating string with embedded nuls
2: In readChar(file, size, TRUE) : truncating string with embedded nuls
3: In readChar(file, size, TRUE) : truncating string with embedded nuls
acc_days = subj_acc_data_clean %>% group_by(IID) %>% dplyr::tally(name = "acc_days")
data_quant_plots = list()
data_quant_plots$gps = conf_scatter_plot(gps_days, gps_acc_subj, "gps_days")
data_quant_plots$acc = conf_scatter_plot(acc_days, acc_acc_subj, "acc_days")
Reduce(`+`,data_quant_plots)

gps_quality = gps_df_clean2 %>% group_by(IID) %>% dplyr::summarize(MinsMissing = mean(MinsMissing,na.rm=T))
#acc_quality = sapply(subj_list,get_data_mean)
#acc_quality$IID = names(acc_data_quality)
#acc_quality$datapoints = as.numeric(value(unlist(acc_data_quality)[1:41]))

data_qual_plots = list()
data_qual_plots$gps = conf_scatter_plot(gps_quality, gps_acc_subj, "MinsMissing")
data_qual_plots$acc = conf_scatter_plot(acc_quality, acc_acc_subj, "datapoints")

Reduce(`+`,data_qual_plots)

9. Developmental effects and sex differences

acc_demo_df = inner_join(acc_demo_df,gps_days, by = c("beiweID" = "IID"))
Error in is.data.frame(y) : object 'gps_days' not found
agesex_gps_fit = gam(gps ~ s(admin_age) + admin_sex + gps_days, data = acc_demo_df, method = "REML")

p_gps = list()
p_gps$sex = visreg(agesex_gps_fit, "admin_sex", gg = T, line=list(col="#3576b5",size = 0.5, alpha = 0.25), 
       points=list(size=2, pch=19), fill= list(fill=c("#b3d3f2"), alpha = 0.25)) + 
  xlab("Sex") + ylab("Individual Footprint Distinctiveness \n (GPS)") +  theme_cowplot()

p_gps$age = visreg(agesex_gps_fit, "admin_age", gg = T, line=list(col="#3576b5",size = 0.5, alpha = 0.25), 
       points=list(size=2, pch=19), fill= list(fill=c("#b3d3f2"), alpha = 1)) + 
  xlab("Age") + ylab("Individual Footprint Distinctiveness \n (GPS)") +  theme_cowplot()

Reduce(`+`,p_gps)

agesex_acc_fit = gam(acc ~ s(admin_age) + admin_sex + acc_days , data = acc_demo_df, method = "REML")

p_acc = list()

p_acc$age = visreg(agesex_acc_fit, "admin_age", gg = T, line=list(col="#3576b5",size = 0.5, alpha = 0.25), 
       points=list(size=2, pch=19), fill= list(fill=c("#b3d3f2"), alpha = 0.25)) + 
  xlab("Age") + ylab("Individual Footprint Distinctiveness \n (Accel)") +  theme_cowplot()

p_acc$sex = visreg(agesex_acc_fit, "admin_sex", gg = T, line=list(col="#3576b5",size = 0.5, alpha = 0.25), 
       points=list(size=2, pch=19), fill= list(fill=c("#b3d3f2"), alpha = 1)) + 
  xlab("Sex") + ylab("Individual Footprint Distinctiveness \n (Accel)") +  theme_cowplot()

Reduce(`+`,p_acc)

10. Mood and sleep instability

survey_ft = readRDS(file.path(data_path,"Results/Group/survey_ft.rds"))

q_list = unique(substr(names(survey_ft$`26k56oo9`),1,29))

q_list_long = names(survey_ft$`26k56oo9`)

mood_since_qs = 20:26

rmssd_mood_since = sapply(subj_list, function(subj) rmssd(rbindlist(lapply(lapply(q_list[mood_since_qs], get_question_ans),function(q) q[[subj]]))$ans_num))
mean_mood_since = sapply(subj_list, function(subj) mean(rbindlist(lapply(lapply(q_list[mood_since_qs], get_question_ans),function(q) q[[subj]]))$ans_num, na.rm = T))
mood_df = acc_demo_df
mood_df$rmssd = rmssd_mood_since
mood_df$mean = mean_mood_since

mood_accgps_fit = gam(accgps ~ rmssd + gps_days + acc_days + s(admin_age) + admin_sex + mean, data = mood_df,  method = "REML")


sleep_duration = "About how many hours did you "
rmssd_sleep_dur = get_question_variance(sleep_duration)
mean_sleep_dur = get_question_mean(sleep_duration)
sleep_df = acc_demo_df
sleep_df$rmssd = rmssd_sleep_dur
sleep_df$mean = mean_sleep_dur

sleep_accgps_fit = gam(accgps ~ rmssd + gps_days + acc_days + s(admin_age) + admin_sex + mean, data = sleep_df,  method = "REML")
p_mood_sleep = list()

p_mood_sleep$mood = visreg(mood_accgps_fit, "rmssd", gg = T, line=list(col="#3576b5",size = 0.5, alpha = 0.25), 
       points=list(size=2, pch=19), fill= list(fill=c("#b3d3f2"), alpha = 1), ylim = c(0,1)) + 
  xlab("Mood Instability") + ylab("Individual Footprint Distinctiveness") +  theme_cowplot()

p_mood_sleep$sleep = visreg(sleep_accgps_fit, "rmssd", gg = T, line=list(col="#3576b5",size = 0.5, alpha = 0.25), 
       points=list(size=2, pch=19), fill= list(fill=c("#b3d3f2"), alpha = 1), ylim = c(0,1)) +
  xlab("Sleep Instability") + ylab("Individual Footprint Distinctiveness") +  theme_cowplot()

Reduce(`+`,p_mood_sleep)

11. Within Network Functional Connectivity

project_path = "~/Documents/xia_gps/"
data_path = file.path(project_path,"data/flywheel_data/network_txt")
subj_net_files = list()
atlases = c("schaefer200x7","schaefer400x7")
for (atlas in atlases) {
  subjects = list.files(file.path(data_path))
  for (subj in subjects){
    subj_files = list.files(file.path(data_path,subj))
    net_pattern = paste0("*task-rest*multi*",atlas,"*")
    file_path = file.path(data_path, 
                            subj,subj_files[grep(glob2rx(net_pattern),subj_files)])
    if (length(file_path)>0) {subj_net_files[[atlas]][[subj]] = read.table(file_path)
  }
  }
}

motion_df = c()
for (subj in subjects){
  subj_quality_files = list.files(file.path(data_path,"../quality_csv",subj))
  net_pattern = paste0("*task-rest*multi*")
  subj_quality_file = file.path(data_path, "../quality_csv",subj,subj_quality_files[grep(glob2rx(net_pattern),subj_quality_files)])
  motion_df[subj] = read.csv(subj_quality_file)$relMeanRMSMotion
}
atlas_path = "/Users/hxia/Documents/GitHub/xcpEngine/atlas"
fc_com = lapply(atlases, function(atlas) get_fc_com(atlas, subj_net_files))
names(fc_com) = atlases

fc_com_mean = lapply(atlases, function(atlas) as.data.frame(sapply(fc_com[[atlas]], function(net_to_net) sapply(net_to_net, function(subj) mean(subj$V1, na.rm=T)))))
names(fc_com_mean) = atlases
fpt_fc = list()
for (atlas in atlases){
  fc_subjs = as.numeric(rownames(fc_com_mean[[atlas]]))
  fc_com_mean[[atlas]]$BBLID = fc_subjs
  fc_com_mean[[atlas]]$motion = value(motion_df)
  fpt_fc[[atlas]] = merge(fc_com_mean[[atlas]], acc_demo_df, by = "BBLID")
}
som_som_fit = gam(som_som  ~ s(admin_age) + admin_sex + gps_days + acc_days  + motion + accgps, data = fpt_fc[[atlas]], method = "REML")
Warning messages:
1: In readChar(file, size, TRUE) : truncating string with embedded nuls
2: In readChar(file, size, TRUE) : truncating string with embedded nuls
3: In readChar(file, size, TRUE) : truncating string with embedded nuls
4: In readChar(file, size, TRUE) : truncating string with embedded nuls
5: In readChar(file, size, TRUE) : truncating string with embedded nuls
6: In readChar(file, size, TRUE) : truncating string with embedded nuls
7: In readChar(file, size, TRUE) : truncating string with embedded nuls
8: In readChar(file, size, TRUE) : truncating string with embedded nuls
9: In readChar(file, size, TRUE) : truncating string with embedded nuls
visreg(som_som_fit, 'accgps', gg = T) + theme_cowplot() + xlab("Individual Footprint Distinctiveness") + ylab("Som-Som")

12. FC predict footprint

fc_edge = lapply(atlases, function(atlas) get_fc_edge(atlas, subj_net_files))
names(fc_edge) = atlases

fc_edge_df = sapply(atlases, function(atlas) as.data.frame(t(sapply(fc_edge[[atlas]], function(subj) subj))))
names(fc_edge_df) = atlases

for (atlas in atlases){
  fc_edge_df[[atlas]]$BBLID = fc_subjs
  fc_edge_df[[atlas]]$motion = value(motion_df)
  fpt_fc_edge[[atlas]] = merge(fc_edge_df[[atlas]], acc_demo_df, by = "BBLID")
}
atlas = "schaefer200x7"
non_edge_var = c(tail(names(fpt_fc_edge[[atlas]]),9),"BBLID")
edge_vars = names(fpt_fc_edge[[atlas]])[-which(names(fpt_fc_edge[[atlas]]) %in% non_edge_var)]
edge_data = fpt_fc_edge[[atlas]]

lambdas = rev(seq(0.01,0.10,0.01))
lasso_tune_l = list()
lasso_out = data.frame()
for (i in 1:dim(edge_data)[1]){
  df_train = edge_data[-i,]
  df_test = edge_data[i,]
  
  lasso_tune = lasso_edge(df_train,edge_vars,lambdas)
  lasso_tune_l[[i]] = lasso_tune
  best_lambda = lambdas[which.max(lasso_tune)]
  
  print(lasso_tune)
  print(paste(i,"-----", "best lambda is", best_lambda))
  
  df_train_fit = gam(accgps  ~ s(admin_age) + admin_sex + gps_days + acc_days  + motion , data = df_train, method = "REML")
  df_train$accgps_res = df_train_fit$residuals
  
  x_train = as.matrix(df_train[,edge_vars])
  y_train = df_train$accgps_res

  x_test = as.matrix(df_test[,edge_vars])
  y_test = df_test$accgps - predict(df_train_fit,newdata = df_test)
  
  lasso_reg = glmnet(x_train, y_train, lambda = best_lambda, alpha = 1, standardize = TRUE)
  
  predictions_test <- predict(lasso_reg, s = best_lambda, newx = x_test)
  
  lasso_out[i,"predict"] = as.numeric(predictions_test)
  lasso_out[i,"real"] = y_test
}
ggplot(data = lasso_out,aes(x=predict,y = real)) + 
    geom_point() + theme_cowplot() + geom_smooth(method = lm, colour = "black")  +
    xlab("Predicted Footprint Distinctiveness ") + ylab("Actual Footprint Distinctiveness ")

lasso_perm = list()
for (j in 1:1000){
  lambdas = rev(seq(0.01,0.10,0.01))
  lasso_tune_l = list()
  lasso_out = data.frame()
  for (i in 1:dim(edge_data)){
    df_train = edge_data[-i,]
    df_test = edge_data[i,]
    
    lasso_tune = lasso_edge_perm(df_train,edge_vars,lambdas)
    lasso_tune_l[[i]] = lasso_tune
    best_lambda = lambdas[which.max(lasso_tune)]
    
    print(lasso_tune)
    print(paste(i,"-----", "best lambda is", best_lambda))
    
    df_train_fit = gam(accgps  ~ s(age) + sex + gps_days + acc_days  + motion , data = df_train, method = "REML")
    df_train$accgps_res = df_train_fit$residuals
    
    x_train = as.matrix(df_train[,edge_vars])
    y_train = df_train$accgps_res
    y_train = sample(y_train)
    
    x_test = as.matrix(df_test[,edge_vars])
    y_test = df_test$accgps - predict(df_train_fit,newdata = df_test)
    
    lasso_reg = glmnet(x_train, y_train, lambda = best_lambda, alpha = 1, standardize = TRUE)
    
    predictions_test <- predict(lasso_reg, s = best_lambda, newx = x_test)
    
    lasso_out[i,"predict"] = as.numeric(predictions_test)
    lasso_out[i,"real"] = y_test
  }
  lasso_perm[[j]] = cor(lasso_out$predict,lasso_out$real)
}
lasso_pval = paste("permuted p =", length(which(lasso_perm > cor(lasso_out$predict,lasso_out$real)))/1000)
There were 11 warnings (use warnings() to see them)
ggplot(data.frame(perm=lasso_perm), aes(x=perm)) + 
  geom_histogram(fill = "light blue") + theme_cowplot() +
  geom_vline(xintercept= cor(lasso_out$predict,lasso_out$real), color = "red") +
  xlab("Correlation between actual and predicted") + geom_text(x=-0.2, y=80, label=lasso_pval)

lasso_beta = get_final_lass(edge_data,edge_vars)$beta
Warning messages:
1: In readChar(file, size, TRUE) : truncating string with embedded nuls
2: In readChar(file, size, TRUE) : truncating string with embedded nuls
3: In readChar(file, size, TRUE) : truncating string with embedded nuls
lasso_beta_df = summary(lasso_beta)
lasso_beta_df$name = edge_vars[summary(lasso_beta)$i]
print(lasso_beta_df)
19900 x 1 sparse Matrix of class "dgCMatrix", with 6 entries 
      i j           x          name
1  2746 1 -0.03663705   fro66_som15
2  3285 1  0.13595765   lim56_som18
3  3439 1  0.10944845   som29_som19
4  7285 1 -0.08554632  dor146_dor41
5 10880 1  0.07220949   def91_fro66
6 16826 1  0.07710780 som129_som122
LS0tCnRpdGxlOiAiTW9iaWxlIEZvb3RwcmludGluZyIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OgogICAgICB0b2NfY29sbGFwc2VkOiB5ZXMKLS0tCiMjIyAxLiBTZXR1cCBFbnZpcm9ubWVudCAKCmBgYHtyIGxvYWQgcGFja2FnZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9VFJVRX0KcmVxdWlyZShnZ3Bsb3QyKQpyZXF1aXJlKHN1bW1hcnl0b29scykKcmVxdWlyZShjb3dwbG90KQpyZXF1aXJlKGNhcmV0KQpyZXF1aXJlKGNvcnJwbG90KQpyZXF1aXJlKFJDb2xvckJyZXdlcikKcmVxdWlyZSh2ZW1iZWRyKQpyZXF1aXJlKFJtaXNjKQpyZXF1aXJlKHZhcmlhbikKcmVxdWlyZShwYXRjaHdvcmspCnJlcXVpcmUocGxvdGx5KQpyZXF1aXJlKE1ldHJpY3MpCnJlcXVpcmUoZHBseXIpCnJlcXVpcmUoZ2dwdWJyKQpyZXF1aXJlKG1vc2FpYykKcmVxdWlyZShvcGVueGxzeCkKcmVxdWlyZSh2aXNyZWcpCnJlcXVpcmUoZmFjdG9leHRyYSkKcmVxdWlyZShyc3RhdGl4KQpyZXF1aXJlKGdyaWRFeHRyYSkKcmVxdWlyZShjb2xvcnNwYWNlKQpyZXF1aXJlKGdyaWQpCnJlcXVpcmUoZGF0YS50YWJsZSkKcmVxdWlyZShwc3ljaCkKcmVxdWlyZShzbmEpCnNvdXJjZSgifi9Eb2N1bWVudHMveGlhX2dwcy9jb2RlL3Bsb3R0aW5nX2Z1bmN0aW9ucy5SIikKYGBgCgpgYGB7ciBkZWZpbmUgcGF0aHN9CnByb2plY3RfcGF0aCA9ICJ+L0RvY3VtZW50cy94aWFfZ3BzLyIKZGF0YV9wYXRoID0gZmlsZS5wYXRoKHByb2plY3RfcGF0aCwiYmVpd2Vfb3V0cHV0XzA0MzAyMCIpCmdwc19kZl9wYXRoID0gZmlsZS5wYXRoKGRhdGFfcGF0aCwiUHJvY2Vzc2VkX0RhdGEvR3JvdXAvZmVhdHVyZV9tYXRyaXgudHh0IikKYGBgCgoKIyMjIDIuUHJlcGFyZSBHUFMgZGF0YSAKYGBge3IgcmVhZF9ncHN9Cmdwc19kZiA9IHJlYWQudGFibGUoZ3BzX2RmX3BhdGgsaGVhZGVyID0gVCwgZGVjID0gIiwiKVssYygxLDIsOTc6MTExKV0KZ3BzX2RmWywzOjE3XSA9IGFwcGx5KGdwc19kZlssMzoxN10sMixhcy5udW1lcmljKQpgYGAKCmBgYHtyIGV4Y2x1ZGUgR1BTIGRhdGEsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD00fQojIGxvb3AgdGhyb3VnaCBlYWNoIHN1YmogdG8gcmVtb3ZlIDFzdCBhbmQgbGFzdCBkYXlzIG9mIGdwcyBkYXRhCglncHNfZGZfY2xlYW4gPSBkYXRhLmZyYW1lKCkgI2luaXRpYXRlIGEgZGYKCWZvciAoc3ViaiBpbiB1bmlxdWUoZ3BzX2RmJElJRCkpeyAjbG9vcCB0aHJvdWdoIGVhY2ggc3ViagoJICBncHNfZGZfc3ViaiA8LSBzdWJzZXQoZ3BzX2RmLCBJSUQgPT0gc3ViaikgI2dldCBncHNfZGYgcGVyIHN1YmplY3QKCSAgZ3BzX2RmX3N1YmogPC0gZ3BzX2RmX3N1YmpbMjooZGltKGdwc19kZl9zdWJqKVsxXS0xKSxdICNyZW1vdmUgdGhlIDFzdCBhbmQgbGFzdCBkYXlzCgkgIGdwc19kZl9jbGVhbiA8LSByYmluZChncHNfZGZfY2xlYW4sZ3BzX2RmX3N1YmopICNjb21iaW5lIGFsbCBzdWJqcwoJfQoKCSMgZXhjbHVkZSBkYXlzIHdpdGggdG9vIG11Y2ggZXhjZXNzaXZlbmVzcwoJc2Vuc2l0aXZpdHlfY3V0b2ZmID0gMTQ0MCAjIHRoaXMgY29udHJvbHMgdGhlIGN1dG9mZiB0aHJlc2hvbGQKCWdwc19kZl9jbGVhbjIgPSBzdWJzZXQoZ3BzX2RmX2NsZWFuLCBNaW5zTWlzc2luZyA8IHNlbnNpdGl2aXR5X2N1dG9mZikKYGBgCiAgIApgYGB7ciBQYXJ0aXRpb24gR1BTIGRhdGF9CnNldC5zZWVkKDUxMCkKcGFydF90aW1lcyA9IDEwMDAKc3Vial9zZXEgPSBtYWtlX3N1Ympfc2VxKGdwc19kZl9jbGVhbjIsIHBhcnRfdGltZXMpCmBgYAoKYGBge3IgZXhhbXBsZV9jb3JfZmlnLCBmaWcud2lkdGg9MywgZmlnLndpZHRoPTMsIGZpZy5hbGlnbj0iY2VudGVyIn0KIyBhbiBleGFtcGxlIG9mIHN1YmogMSwgYW5kIGZpcnN0IGhhbGYKZXhhbXBsZV9kYXRhID0gZ3BzX2RmX2NsZWFuMltzdWJqX3NlcSRgMTZ4djZrbzFgJFJlc2FtcGxlMDAwMSwzOjE3XQpncHNfY29yID0gcnF1ZXJ5LmNvcm1hdChleGFtcGxlX2RhdGEsIHR5cGUgPSAiZnVsbCIpCmBgYAoKYGBge3IgY3JlYXRlIGZlYXR1cmUgbWF0cml4IGZvciBldmVyeW9uZSwgd2FybmluZz1GQUxTRX0KCmdwc19jbGVhbjJfZmVhdHVyZSA9IG1ha2VfZmVhdHVyZV9tYXRyaXgoZ3BzX2RmX2NsZWFuMiwgc3Vial9zZXEsIDM6MTcgKQpzdWJqZWN0X3NlcSA9IG5hbWVzKGdwc19jbGVhbjJfZmVhdHVyZSRzdWJqX21hdF8xKQpgYGAKCmBgYHtyIHZpc3VhbGl6YXRpb24gb2YgZmVhdHVyZSBjb3JyZWxhdGlvbnMgYWNyb3NzIHN1YmplY3RzfQpncHNfd2lkZV9tYXRyaXggPSBkYXRhLmZyYW1lKCkKc3Vial9kYXlzID0gZ3BzX2RmX2NsZWFuMiAlPiUgZ3JvdXBfYnkoSUlEKSAlPiUgZHBseXI6OnRhbGx5KG5hbWUgPSAiRGF5cyBDb2xsZWN0ZWQiKQpmb3IgKHN1YmogaW4gYXJyYW5nZShzdWJqX2RheXMsYERheXMgQ29sbGVjdGVkYCkkSUlEKXsKICAjaWYgKChzdWJqICVpbiUgc3Vial9kYXlzJElJRFt3aGljaChzdWJqX2RheXMkYERheXMgQ29sbGVjdGVkYDw9MTApXSkgPT0gVCkgewogICAgZm9yIChwYXJ0IGluIDE6NSl7CiAgICAgIGhhbGZfMSA9IGdwc19jbGVhbjJfZmVhdHVyZSRzdWJqX21hdF8xW1tzdWJqXV1bW3BhcnRdXSRjb3IKICAgICAgaGFsZl8yID0gZ3BzX2NsZWFuMl9mZWF0dXJlJHN1YmpfbWF0XzJbW3N1YmpdXVtbcGFydF1dJGNvcgogICAgICBoYWxmXzFfaGFsZl8yID0gYyhoYWxmXzEsaGFsZl8yKQogICAgICBncHNfd2lkZV9tYXRyaXggPSByYmluZChncHNfd2lkZV9tYXRyaXgsaGFsZl8xKQogICAgICBncHNfd2lkZV9tYXRyaXggPSByYmluZChncHNfd2lkZV9tYXRyaXgsaGFsZl8yKQogICAgICB9CiAgICAjfQp9CgpncHNfd2lkZV9tYXRyaXggPSB0KGdwc193aWRlX21hdHJpeCkKZ3BzX2NvcnBsb3QgPSBycXVlcnkuY29ybWF0KGdwc193aWRlX21hdHJpeCwgdHlwZSA9ICJmdWxsIixncmFwaD1GQUxTRSkKZ3BzX2NvcnBsb3Qkc3ViaiA9IGFycmFuZ2Uoc3Vial9kYXlzLGBEYXlzIENvbGxlY3RlZGApJElJRAoKZ2V0UGFsZXR0ZSA9IGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbCg5LCAiUmRZbEJ1IikpCmxldmVscGxvdChncHNfY29ycGxvdCRyLHNjYWxlcz1saXN0KGRyYXc9RkFMU0UpLGNvbC5yZWdpb25zID0gcmV2KGdldFBhbGV0dGUoNTApKSwgcmVnaW9uID1ULCB5bGFiLnJpZ2h0ID0gIlBlYXJzb24gY29ycmVsYXRpb24iLCBtYWluPWxpc3QobGFiZWw9J0dQUyBGZWF0dXJlIFNpbWlsYXJpdHknKSx4bGFiPSIiLHlsYWI9IiIpCmBgYAoKIyMjIDMuIFByZXBhcmUgYWNjZWxlcm9tZXRlciBkYXRhCmBgYHtyIG9yZ2FuaXppbmcgYWNjZWxlcm9tZXRlciBkYXRhfQpzdWJqX2xpc3QgPSB1bmlxdWUoZ3BzX2RmJElJRCkKc3Vial9hY2NfZGF0YV9jbGVhbiA9IGFzLmRhdGEuZnJhbWUocmJpbmRsaXN0KGxhcHBseShzdWJqX2xpc3QsIGZ1bmN0aW9uKHN1YmopIHsgc3Vial9kYXRhID0gIHJlYWRSRFMoZmlsZS5wYXRoKGRhdGFfcGF0aCwiUHJvY2Vzc2VkX0RhdGEvR3JvdXAvYWNjZWxlcm9tZXRlciIsc3ViaiwiYWNjZWxlcm9tZXRlcl9mdC5yZHMiKSk7IHN1YmpfZGF0YVsyOihkaW0oc3Vial9kYXRhKVsxXS0xKSxjKDI6OCwxMSldfSkpKSAjYWxzbyByZW1vdmVzIDFzdCBhbmQgbGFzdCBkYXkKYGBgCgpgYGB7ciBleGFtcGxlIG9mIHRoZSBjb3Ygb2YgYWNjZWxlcm9tZXRlcixmaWcud2lkdGg9MywgZmlnLndpZHRoPTMsZmlnLmFsaWduPSJjZW50ZXIifQpleGFtcGxlX2FjY19kYXRhID0gc3Vial9hY2NfZGF0YV9jbGVhbltzdWJqX3NlcSRgMTZ4djZrbzFgJFJlc2FtcGxlMDAwMSwxOjddCmFjY19jb3IgPSBycXVlcnkuY29ybWF0KGV4YW1wbGVfYWNjX2RhdGEsIHR5cGUgPSAiZnVsbCIpCmBgYAoKYGBge3IgY2FsYyBhY2MgZmVhdHVyZXN9CnN1YmpfYWNjX2RhdGFfY2xlYW4kSUlEID0gc3Vial9hY2NfZGF0YV9jbGVhbiRzdWJqZWN0CnNldC5zZWVkKDUxMCkKYWNjX3N1Ympfc2VxID0gbWFrZV9zdWJqX3NlcShzdWJqX2FjY19kYXRhX2NsZWFuLCBwYXJ0X3RpbWVzKQphY2NfY2xlYW5fZmVhdHVyZSA9IG1ha2VfZmVhdHVyZV9tYXRyaXgoc3Vial9hY2NfZGF0YV9jbGVhbiwgYWNjX3N1Ympfc2VxLCAgYygxOjcpICkKCmBgYAoKYGBge3IgcGxvdCBhY2Mgc3ViamVjdCBzaW1pbGFyaXR5IG1hdHJpeH0KYWNjX3dpZGVfbWF0cml4ID0gZGF0YS5mcmFtZSgpCnN1YmpfYWNjX2RheXMgPSBzdWJqX2FjY19kYXRhX2NsZWFuICU+JSBncm91cF9ieShJSUQpICU+JSBkcGx5cjo6dGFsbHkobmFtZSA9ICJhY2NfZGF5cyIpCmZvciAoc3ViaiBpbiBhcnJhbmdlKHN1YmpfYWNjX2RheXMsYGFjY19kYXlzYCkkSUlEKXsKICAgIGZvciAocGFydCBpbiAxOjUpewogICAgICBoYWxmXzEgPSBhY2NfY2xlYW5fZmVhdHVyZSRzdWJqX21hdF8xW1tzdWJqXV1bW3BhcnRdXSRjb3IKICAgICAgaGFsZl8yID0gYWNjX2NsZWFuX2ZlYXR1cmUkc3Vial9tYXRfMltbc3Vial1dW1twYXJ0XV0kY29yCiAgICAgIGhhbGZfMV9oYWxmXzIgPSBjKGhhbGZfMSxoYWxmXzIpCiAgICAgIGFjY193aWRlX21hdHJpeCA9IHJiaW5kKGFjY193aWRlX21hdHJpeCxoYWxmXzEpCiAgICAgIGFjY193aWRlX21hdHJpeCA9IHJiaW5kKGFjY193aWRlX21hdHJpeCxoYWxmXzIpCiAgICAgIH0KfQoKYWNjX3dpZGVfbWF0cml4ID0gdChhY2Nfd2lkZV9tYXRyaXgpCmFjY19jb3JwbG90ID0gcnF1ZXJ5LmNvcm1hdChhY2Nfd2lkZV9tYXRyaXgsIHR5cGUgPSAiZnVsbCIsZ3JhcGg9RkFMU0UpCmFjY19jb3JwbG90JHN1YmogPSBhcnJhbmdlKHN1YmpfYWNjX2RheXMsYGFjY19kYXlzYCkkSUlECgoKbGV2ZWxwbG90KGFjY19jb3JwbG90JHIsc2NhbGVzPWxpc3QoZHJhdz1GQUxTRSksY29sLnJlZ2lvbnMgPSByZXYoZ2V0UGFsZXR0ZSg1MCkpLCByZWdpb24gPVQsIHlsYWIucmlnaHQgPSAiUGVhcnNvbiBjb3JyZWxhdGlvbiIsIG1haW49bGlzdChsYWJlbD0nQWNjIEZlYXR1cmUgU2ltaWxhcml0eScpLHhsYWI9IiIseWxhYj0iIikKYGBgCgojIyMgNC4gTWVyZ2UgR1BTIGFuZCBhY2NlbGVyb21ldGVyIGRhdGEKYGBge3IgY29tYmluZSBncHMgKyBhY2N9Cmdwc19hY2NfY29tYmluZWRfZmVhdCA9IGxpc3QoKQpmb3IgKHN1YmogaW4gc3Vial9saXN0KXsKICBmb3IgKHRpbWUgaW4gMTpwYXJ0X3RpbWVzKXsKICAgIGdwc19mZWF0MSA9IGdwc19jbGVhbjJfZmVhdHVyZSRzdWJqX21hdF8xW1tzdWJqXV1bW3RpbWVdXSRjb3IKICAgIGFjY19mZWF0MSA9IGFjY19jbGVhbl9mZWF0dXJlJHN1YmpfbWF0XzFbW3N1YmpdXVtbdGltZV1dJGNvcgogICAgCiAgICBncHNfZmVhdDIgPSBncHNfY2xlYW4yX2ZlYXR1cmUkc3Vial9tYXRfMltbc3Vial1dW1t0aW1lXV0kY29yCiAgICBhY2NfZmVhdDIgPSBhY2NfY2xlYW5fZmVhdHVyZSRzdWJqX21hdF8yW1tzdWJqXV1bW3RpbWVdXSRjb3IKCiAgICBncHNfYWNjX2NvbWJpbmVkX2ZlYXQkc3Vial9tYXRfMVtbc3Vial1dW1t0aW1lXV0gPSBjKGdwc19mZWF0MSwgYWNjX2ZlYXQxKQogICAgZ3BzX2FjY19jb21iaW5lZF9mZWF0JHN1YmpfbWF0XzJbW3N1YmpdXVtbdGltZV1dID0gYyhncHNfZmVhdDIsIGFjY19mZWF0MikKICB9Cn0KYGBgCgoKIyMjIDUuIEdQUyBhbmQgYWNjZWxlcm9tZXRlciBiYXNlZCBJbmRpdmlkdWFsIElkZW50aWZpY2F0aW9uCmBgYHtyIG1hdGNoIHdpdGggY29tYmluZWQgZmVhdHVyZXN9Cm1hdGNoX2NvbWJpbmVkX2dwc19hY2NmZWF0ID0gY2FsY19tYXRjaF92ZWN0b3IoZ3BzX2FjY19jb21iaW5lZF9mZWF0JHN1YmpfbWF0XzEsIGdwc19hY2NfY29tYmluZWRfZmVhdCRzdWJqX21hdF8yLCAiY29yIikKYWNjX3RpbWVfY2JfYWNjZ3BzICA9IGNhbGNfYWNjX3RpbWUobWF0Y2hfY29tYmluZWRfZ3BzX2FjY2ZlYXQsICJtYXgiKQphY2Nfc3Vial9jYl9hY2NncHMgID0gY2FsY19hY2Nfc3ViaihtYXRjaF9jb21iaW5lZF9ncHNfYWNjZmVhdCwgIm1heCIpCmBgYAoKYGBge3IgLCBmaWcuYWxpZ249ImNlbnRlciIsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTV9CnAgPSBoaXN0X2NoeChhY2NfdGltZV9jYl9hY2NncHMsIGJpbnMgPSAxNCwgdGl0bGUgPSBwYXN0ZSgiSW5kaXZpZHVhbCBJZGVudGlmaWNhaXRvbiBBY2N1cmFjeSBcbiAoR1BTKyBBY2NlbCkiKSwgeGF4aXMgPSAiUHJlZGljdGlvbiBBY2N1cmFjeSIsIHlheGlzID0gIkNvdW50IikKZ2dwbG90bHkocCkKYGBgCgoKCmBgYHtyIGFjYytncHMgcGVybX0KcGVybV90aW1lID0gMTAKZ3BzX2RmX3Blcm0gPSBncHNfZGZfY2xlYW4yCmFjY19kZl9wZXJtID0gc3Vial9hY2NfZGF0YV9jbGVhbgoKcGVybV9hY2NfZ3BzX3RpbWUgPSBsaXN0KCkKcGVybV9hY2NfZ3BzX3N1YmogPSBsaXN0KCkKCmZvciAoaSBpbiAxOnBlcm1fdGltZSkgewogIHBlcm1fcGFydF90aW1lcyA9IDEKICBwcmludChwYXN0ZSgicHJvY2Vzc2luZyAuLi4iLCBpLCIuLi4iKSkKICBncHNfZGZfcGVybSRJSUQgPSBzYW1wbGUoZ3BzX2RmX3Blcm0kSUlEKQogIGFjY19kZl9wZXJtJElJRCA9IHNhbXBsZShhY2NfZGZfcGVybSRJSUQpCiAgCiAgcGVybV9zdWJqX3NlcSA9IG1ha2Vfc3Vial9zZXEoZ3BzX2RmX3Blcm0scGFydF90aW1lcyA9IHBlcm1fcGFydF90aW1lcykKICBwZXJtX2FjY19zdWJqX3NlcSA9IG1ha2Vfc3Vial9zZXEoYWNjX2RmX3Blcm0scGFydF90aW1lcyA9IHBlcm1fcGFydF90aW1lcykKICAKICBwZXJtX2dwc19jb3IgPSBtYWtlX2ZlYXR1cmVfbWF0cml4KGdwc19kZl9wZXJtLHBlcm1fc3Vial9zZXEsMzoxNykKICBwZXJtX2FjY19jb3IgPSBtYWtlX2ZlYXR1cmVfbWF0cml4KGFjY19kZl9wZXJtLHBlcm1fYWNjX3N1Ympfc2VxLDE6NykKICAKICBzdWJqX21hdF8xID0gbGlzdCgpCiAgc3Vial9tYXRfMiA9IGxpc3QoKQogIAogICAgZm9yIChzdWJqIGluIHN1YmpfbGlzdCl7CiAgICAgICAgc3Vial9tYXRfMVtbc3Vial1dJFJlc2FtcGxlMSA9IGFzLm51bWVyaWMoYyhwZXJtX2dwc19jb3Ikc3Vial9tYXRfMVtbc3Vial1dW1sxXV0kY29yLCBwZXJtX2FjY19jb3Ikc3Vial9tYXRfMVtbc3Vial1dW1sxXV0kY29yKSkKICAgICAgICBzdWJqX21hdF8yW1tzdWJqXV0kUmVzYW1wbGUxID0gYXMubnVtZXJpYyhjKHBlcm1fZ3BzX2NvciRzdWJqX21hdF8yW1tzdWJqXV1bWzFdXSRjb3IsIHBlcm1fYWNjX2NvciRzdWJqX21hdF8yW1tzdWJqXV1bWzFdXSRjb3IpKQogICAgfQogIHBlcm1fZ3BzX2FjY19tYXRzID0gbGlzdChzdWJqX21hdF8xID0gc3Vial9tYXRfMSwgc3Vial9tYXRfMiA9IHN1YmpfbWF0XzIpCiAgCiAgcGVybV9tYXRjaCA9IGNhbGNfbWF0Y2hfdmVjdG9yKHBlcm1fZ3BzX2FjY19tYXRzJHN1YmpfbWF0XzEscGVybV9ncHNfYWNjX21hdHMkc3Vial9tYXRfMiwgImNvciIpCiAgcGVybV9hY2NfZ3BzX3RpbWVbW2ldXSA9IGNhbGNfYWNjX3RpbWUocGVybV9tYXRjaCwgIm1heCIpCiAgcGVybV9hY2NfZ3BzX3N1YmpbW2ldXSA9IGNhbGNfYWNjX3N1YmoocGVybV9tYXRjaCwgIm1heCIpCn0KCgpwZXJtX2FjY19ncHNfdGltZSA9IHVubGlzdChwZXJtX2FjY19ncHNfdGltZSkKcGVybV9hY2NfZ3BzX3N1YmogPSB1bmxpc3QocGVybV9hY2NfZ3BzX3N1YmopCgpgYGAKCmBgYHtyIGdwc19hY2NlbCBwZXJtdXRhdGlvbn0KZGYgPSBkYXRhLmZyYW1lKHZhbCA9IHBlcm1fYWNjX2dwc190aW1lKQpwID0gZ2dwbG90KGRmLCBhZXMoeD12YWwpKSArIAogICAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDcsIGZpbGwgPSAiIzlFQ0FFMSIsIHNpemUgPSAyKSArIAogICAgdGhlbWVfY293cGxvdCgpICsKICAgIGxhYnModGl0bGU9cGFzdGUoIkdQUytBY2NlbDogXG4iLHBhcnRfdGltZXMsIlBlcm11dGF0aW9ucyIpLAogICAgICAgICB4ICA9ICJJbmRpdmlkdWFsIElkZW50aWZpY2FpdG9uIEFjY3VyYWN5IiwgeSA9ICJDb3VudCIpICsgCiAgI2dlb21fZGVuc2l0eShhbHBoYT0uMikgKwogICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCmdncGxvdGx5KHApCmBgYApgYGB7ciBwbG90IHN1YmogYW5hbHlzaXMgd2l0aCBwZXJtLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD00fQpwZXJtX3N1YmogPSBzYXBwbHkoc3ViamVjdF9zZXEsIGZ1bmN0aW9uKHN1YmopIHN1bShwZXJtX2FjY19ncHNfc3Vialt3aGljaChuYW1lcyhwZXJtX2FjY19ncHNfc3ViaikgPT0gc3ViaildKS8xMDAwKQoKc3Vial9kZiA8LSBkYXRhLmZyYW1lKHg9bmFtZXMoc3Vial9zZXEpKQpzdWJqX2RmJHkgPSBhY2Nfc3Vial9jYl9hY2NncHMKc3Vial9kZiR5X3Blcm0gPSBwZXJtX3N1YmoKc3Vial9kZiA9IHN1YmpfZGZbb3JkZXIoc3Vial9kZiR5KSxdCnBfc3Vial9jb3JfcGVybSA9IGdncGxvdChzdWJqX2RmLCBhZXMoeCA9IHJlb3JkZXIoeCwgeSksIHkgPSB2YWx1ZSkpICsgCiAgZ2VvbV9wb2ludChhZXMoeSA9IHkpLCBjb2xvciA9ICIjRjY5Mjc0Iiwgc2l6ZSA9IDIpICsgCiAgZ2VvbV9wb2ludChhZXMoeSA9IHlfcGVybSksIGNvbG9yID0gIiM5RUNBRTEiLCBzaXplID0yICkgKwogIHRoZW1lX2Nvd3Bsb3QoKSArIAogIGxhYnModGl0bGUgPSBwYXN0ZSgiR1BTK0FjY2VsIFxuIFN1YmplY3QgTGV2ZWwgQWNjdXJhY3kgQWNyb3NzIixwYXJ0X3RpbWVzLCJEYXRhIFBhcnRpdGlvbnMiKSwgCiAgICAgICB4ID0gIlN1YmplY3RzIiwgeSA9ICJJbmRpdmlkdWFsIElkZW50aWZpY2F0aW9uIEFjY3VyYWN5IikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgc2l6ZSA9IDgpLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKZ2dwbG90bHkocF9zdWJqX2Nvcl9wZXJtKQpgYGAKCiMjIyA2LiBHUFMgb25seSBiYXNlZCBJbmRpdmlkdWFsIElkZW50aWZpY2F0aW9uCmBgYHtyIGdwcyBJRH0KZ3BzX21hdGNoX2NvciA9IGNhbGNfbWF0Y2hfY29yKGdwc19jbGVhbjJfZmVhdHVyZSRzdWJqX21hdF8xLGdwc19jbGVhbjJfZmVhdHVyZSRzdWJqX21hdF8yKQpncHNfYWNjX3RpbWUgPSBjYWxjX2FjY190aW1lKGdwc19tYXRjaF9jb3IsICJtYXgiKQpncHNfYWNjX3N1YmogPSBjYWxjX2FjY19zdWJqKGdwc19tYXRjaF9jb3IpCgpncHNfZGZfcGVybSA9IGdwc19kZl9jbGVhbjIKcGVybV90aW1lID0gMTAwMApwZXJtX2FjY190aW1lID0gbGlzdCgpCnBlcm1fYWNjX3N1YmogPSBsaXN0KCkKZm9yIChpIGluIDE6cGVybV90aW1lKSB7CiAgcGVybV9wYXJ0X3RpbWVzID0gMQogIHByaW50KHBhc3RlKCJwcm9jZXNzaW5nIC4uLiIsIGksIi4uLiIpKQogIGdwc19kZl9wZXJtJElJRCA9IHNhbXBsZShncHNfZGZfcGVybSRJSUQpCiAgcGVybV9zdWJqX3NlcSA9IG1ha2Vfc3Vial9zZXEoZ3BzX2RmX3Blcm0sIHBhcnRfdGltZXMgPSBwZXJtX3BhcnRfdGltZXMpCiAgcGVybV9ncHMgPSBtYWtlX2ZlYXR1cmVfbWF0cml4KGdwc19kZl9wZXJtLHBlcm1fc3Vial9zZXEsMzoxNykKICBwZXJtX21hdF8xID0gcGVybV9ncHMkc3Vial9tYXRfMQogIHBlcm1fbWF0XzIgPSBwZXJtX2dwcyRzdWJqX21hdF8yCiAgcGVybV9tYXRjaF9jb3IgPSBjYWxjX21hdGNoX2NvcihwZXJtX21hdF8xLHBlcm1fbWF0XzIpCiAgcGVybV9hY2NfdGltZVtbaV1dID0gY2FsY19hY2NfdGltZShwZXJtX21hdGNoX2NvcikKICBwZXJtX2FjY19zdWJqW1tpXV0gPSBjYWxjX2FjY19zdWJqKHBlcm1fbWF0Y2hfY29yKQp9CmBgYAoKYGBge3IgZ3BzIHBsb3RzIDF9CnBfdGltZV9jb3IgPSBoaXN0X2NoeChhY2NfdGltZSwgYmlucyA9IDE3LCB0aXRsZSA9IHBhc3RlKCJHUFM6IFxuIEluZGl2aWR1YWwgaWRlbnRpZmljYXRpb24gYWNjdXJhY3kiKSwgeGF4aXMgPSAiSW5kaXZpZHVhbCBpZGVudGlmaWNhdGlvbiBhY2N1cmFjeSIsIHlheGlzID0gIkNvdW50IikKZ2dwbG90bHkocF90aW1lX2NvcikKYGBgCgpgYGB7ciBncHMgcGxvdHMgMn0KI3Blcm1fYWNjX3RpbWVfYWxsID0gdW5saXN0KHBlcm1fYWNjX3RpbWUpCnFfdGltZV9jb3IgPSBoaXN0X2NoeChwZXJtX2FjY190aW1lX2FsbCwgYmlucyA9IDcsIHRpdGxlID0gcGFzdGUoIkdQUyBhY2N1cmFjeSBhY3Jvc3MiLGxlbmd0aChwZXJtX2FjY190aW1lX2FsbCksIlBlcm11dGF0aW9ucyIpLCB4YXhpcyA9ICJJbmRpdmlkdWFsIElkZW50aWZpY2FpdG9uIEFjY3VyYWN5IiwgeWF4aXMgPSAiQ291bnQiKQpnZ3Bsb3RseShxX3RpbWVfY29yKQpgYGAKCmBgYHtyIGdwcyBwbG90cyAzfQpwZXJtX2FjY19zdWJqID0gdW5saXN0KHBlcm1fYWNjX3N1YmopCnBlcm1fc3ViaiA9IHNhcHBseShzdWJqZWN0X3NlcSwgZnVuY3Rpb24oc3Viaikgc3VtKHBlcm1fYWNjX3N1Ympbd2hpY2gobmFtZXMocGVybV9hY2Nfc3ViaikgPT0gc3ViaildKS8xMDAwKQoKc3Vial9kZiA8LSBkYXRhLmZyYW1lKHg9bmFtZXMoc3Vial9zZXEpKQpzdWJqX2RmJHkgPSBncHNfYWNjX3N1YmoKc3Vial9kZiR5X3Blcm0gPSBwZXJtX3N1YmoKc3Vial9kZiA9IHN1YmpfZGZbb3JkZXIoc3Vial9kZiR5KSxdCnBfc3Vial9jb3JfcGVybSA9IGdncGxvdChzdWJqX2RmLCBhZXMoeCA9IHJlb3JkZXIoeCwgeSksIHkgPSB2YWx1ZSkpICsgCiAgZ2VvbV9wb2ludChhZXMoeSA9IHksIGNvbCA9ICJzdWJqZWN0IGRhdGEiKSkgKyAKICBnZW9tX3BvaW50KGFlcyh5ID0geV9wZXJtLCBjb2wgPSAicGVybXV0YXRpb24iKSkgKwogIHRoZW1lX2Nvd3Bsb3QoKSArIAogIGxhYnModGl0bGUgPSBwYXN0ZSgiR1BTIFxuIFN1YmplY3QgTGV2ZWwgQWNjdXJhY3kgQWNyb3NzIixwZXJtX3RpbWUsInBlcm11dGF0aW9ucyIpLCAKICAgICAgIHggPSAiU3ViamVjdHMiLCB5ID0gIkluZGl2aWR1YWwgaWRlbnRpZmljYXRpb24gYWNjdXJhY3kiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCBzaXplID0gOCksIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpnZ3Bsb3RseShwX3N1YmpfY29yX3Blcm0pCmBgYAoKIyMjIDcuIEFjY2VsZXJvbWV0ZXIgb25seSBiYXNlZCBJbmRpdmlkdWFsIElkZW50aWZpY2F0aW9uIApgYGB7ciBtYXRjaCBvbiBhY2MgZmVhdHVyZXN9CmFjY19tYXRjaF9jb3IgPSBjYWxjX21hdGNoX2NvcihhY2NfY2xlYW5fZmVhdHVyZSRzdWJqX21hdF8xLGFjY19jbGVhbl9mZWF0dXJlJHN1YmpfbWF0XzIpCmFjY19hY2NfdGltZSA9IGNhbGNfYWNjX3RpbWUoYWNjX21hdGNoX2NvciwgIm1heCIpCmFjY19hY2Nfc3ViaiA9IGNhbGNfYWNjX3N1YmooYWNjX21hdGNoX2NvcikKYGBgCgpgYGB7ciBhY2MgcGVybX0KYWNjX2RmX3Blcm0gPSBzdWJqX2FjY19kYXRhX2NsZWFuCnBlcm1fdGltZSA9IDEwMDAKcGVybV9hY2NfYWNjX3RpbWUgPSBsaXN0KCkKcGVybV9hY2NfYWNjX3N1YmogPSBsaXN0KCkKZm9yIChpIGluIDE6cGVybV90aW1lKSB7CiAgcGVybV9wYXJ0X3RpbWVzID0gMQogIHByaW50KHBhc3RlKCJwcm9jZXNzaW5nIC4uLiIsIGksIi4uLiIpKQogIGFjY19kZl9wZXJtJElJRCA9IHNhbXBsZShhY2NfZGZfcGVybSRJSUQpCiAgcGVybV9zdWJqX3NlcSA9IG1ha2Vfc3Vial9zZXEoYWNjX2RmX3Blcm0sIHBhcnRfdGltZXMgPSBwZXJtX3BhcnRfdGltZXMpCiAgcGVybV9hY2MgPSBtYWtlX2ZlYXR1cmVfbWF0cml4KGFjY19kZl9wZXJtLHBlcm1fc3Vial9zZXEsMTo3KQogIHBlcm1fbWF0XzEgPSBwZXJtX2FjYyRzdWJqX21hdF8xCiAgcGVybV9tYXRfMiA9IHBlcm1fYWNjJHN1YmpfbWF0XzIKICBwZXJtX21hdGNoX2NvciA9IGNhbGNfbWF0Y2hfY29yKHBlcm1fbWF0XzEscGVybV9tYXRfMikKICBwZXJtX2FjY19hY2NfdGltZVtbaV1dID0gY2FsY19hY2NfdGltZShwZXJtX21hdGNoX2NvcikKICBwZXJtX2FjY19hY2Nfc3VialtbaV1dID0gY2FsY19hY2Nfc3ViaihwZXJtX21hdGNoX2NvcikKfQpgYGAKCmBgYHtyIGFjYyBwbG90cyAxfQpkZiA9IGRhdGEuZnJhbWUodmFsID0gYWNjX2FjY190aW1lKQpwID0gZ2dwbG90KGRmLCBhZXMoeD12YWwpKSArIAogICAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDE1LCBmaWxsID0gIiNGNjkyNzQiLCBzaXplID0gMikgKyAKICAgIHRoZW1lX2Nvd3Bsb3QoKSArCiAgICBsYWJzKHRpdGxlPXBhc3RlKCJBY2NlbGVyb21ldGVyIEZlYXR1cmVzOiBcbiBBdmVyYWdlIEFjY3VyYWN5IEFjcm9zcyIscGFydF90aW1lcywiRGF0YSBQYXJ0aXRpb25zIiksCiAgICAgICAgIHggID0gIk1vYmlsaXR5IEZvb3RwcmludCBJbmRpdmlkdWFsIElkZW50aWZpY2F0aW9uIEFjY3VyYWN5IiwgeSA9ICJDb3VudCIpICsgCiAgI2dlb21fZGVuc2l0eShhbHBoYT0uMikgKwogICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCmdncGxvdGx5KHApCmBgYApgYGB7ciBhY2MgcGxvdHMgMn0KZGYgPSBkYXRhLmZyYW1lKHZhbCA9IHBlcm1fYWNjX2FjY190aW1lKQpwID0gZ2dwbG90KGRmLCBhZXMoeD12YWwpKSArIAogICAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDUsIGZpbGwgPSAiIzlFQ0FFMSIsIHNpemUgPSAyKSArIAogICAgdGhlbWVfY293cGxvdCgpICsKICAgIGxhYnModGl0bGU9cGFzdGUoIkFjY2VsZXJvbWV0ZXIgRmVhdHVyZXM6IFxuIEF2ZXJhZ2UgQWNjdXJhY3kgQWNyb3NzIixwZXJtX3RpbWUsIlBlcm11dGF0aW9ucyIpLAogICAgICAgICB4ICA9ICJNb2JpbGl0eSBGb290cHJpbnQgSW5kaXZpZHVhbCBJZGVudGlmaWNhdGlvbiBBY2N1cmFjeSIsIHkgPSAiQ291bnQiKSArIAogICNnZW9tX2RlbnNpdHkoYWxwaGE9LjIpICsKICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpnZ3Bsb3RseShwKQpgYGAKYGBge3IgYWNjIHBsb3RzIDN9CnBlcm1fYWNjX2FjY19zdWJqID0gdW5saXN0KHBlcm1fYWNjX2FjY19zdWJqKQpwZXJtX3N1YmogPSBzYXBwbHkoc3ViamVjdF9zZXEsIGZ1bmN0aW9uKHN1YmopIHN1bShwZXJtX2FjY19hY2Nfc3Vialt3aGljaChuYW1lcyhwZXJtX2FjY19hY2Nfc3ViaikgPT0gc3ViaildKS8xMDAwKQoKc3Vial9kZiA8LSBkYXRhLmZyYW1lKHg9bmFtZXMoc3Vial9zZXEpKQpzdWJqX2RmJHkgPSBhY2NfYWNjX3N1YmoKc3Vial9kZiR5X3Blcm0gPSBwZXJtX3N1YmoKc3Vial9kZiA9IHN1YmpfZGZbb3JkZXIoc3Vial9kZiR5KSxdCnBfc3Vial9jb3JfcGVybSA9IGdncGxvdChzdWJqX2RmLCBhZXMoeCA9IHJlb3JkZXIoeCwgeSksIHkgPSB2YWx1ZSkpICsgCiAgZ2VvbV9wb2ludChhZXMoeSA9IHksIGNvbCA9ICJzdWJqZWN0IGRhdGEiKSkgKyAKICBnZW9tX3BvaW50KGFlcyh5ID0geV9wZXJtLCBjb2wgPSAicGVybXV0YXRpb24iKSkgKwogIHRoZW1lX2Nvd3Bsb3QoKSArIAogIGxhYnModGl0bGUgPSBwYXN0ZSgiQWNjZWxlcm9tZXRlciBcbiBTdWJqZWN0IExldmVsIEFjY3VyYWN5IEFjcm9zcyIscGVybV90aW1lLCJwZXJtdXRhdGlvbnMiKSwgCiAgICAgICB4ID0gIlN1YmplY3RzIiwgeSA9ICJJbmRpdmlkdWFsIGlkZW50aWZpY2F0aW9uIGFjY3VyYWN5IikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgc2l6ZSA9IDgpLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKZ2dwbG90bHkocF9zdWJqX2Nvcl9wZXJtKQpgYGAKCmBgYHtyIGdzcCB2IGFjYyB2cyBncHMrYWNjfQpkZl9hY2NncHNfdGltZSA9IGRhdGEuZnJhbWUoR1BTID0gZ3BzX2FjY190aW1lLCBBY2NlbGVyb21ldGVyID0gYWNjX2FjY190aW1lLCBgR1BTIGFuZCBBY2NlbGVyb210ZXJgID0gYWNjX3RpbWVfY2JfYWNjZ3BzKQpkZl9hY2NncHNfdGltZV9sb25nID0gZ2F0aGVyKGRmX2FjY2dwc190aW1lLCBmZWF0dXJlLCBhY2N1cmFjeSwgMTozLCBmYWN0b3Jfa2V5PVRSVUUpCm11ID0gZGRwbHkoZGZfYWNjZ3BzX3RpbWVfbG9uZywgImZlYXR1cmUiLCBzdW1tYXJpc2UsIGdycC5tZWFuPW1lYW4oYWNjdXJhY3kpKQpwX2FjY2dwc19mZWF0dXJlID0gZ2dwbG90KGRmX2FjY2dwc190aW1lX2xvbmcsIGFlcyh4PWFjY3VyYWN5LGNvbG9yID0gZmVhdHVyZSwgZmlsbCA9IGZlYXR1cmUpKSArIAogICAgZ2VvbV9kZW5zaXR5KHNpemUgPSAxLCBhbHBoYSA9IDApICsgCiAgICAjZ2VvbV9oaXN0b2dyYW0oYWVzKHk9Li5kZW5zaXR5Li4pLCBhbHBoYT0wLjIsIAogICAgIyAgICAgICAgICAgIHBvc2l0aW9uPSJpZGVudGl0eSIpICsgCiAgICBnZW9tX3ZsaW5lKGRhdGE9bXUsIGFlcyh4aW50ZXJjZXB0PWdycC5tZWFuLCBjb2xvcj1mZWF0dXJlKSwKICAgICAgICAgICAgIGxpbmV0eXBlPSJkYXNoZWQiKSArCiAgICBsYWJzKHkgPSAiRGVuc2l0eSIsIHggPSAiSW5kaXZpZHVhbCBJZGVudGl0aWNhdGlvbiBBY2N1cmFjeSIpICsgCiAgICB0aGVtZV9jb3dwbG90KCkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJ0b3AiKQpwX2FjY2dwc19mZWF0dXJlCmBgYAoKCgojIyMgOC4gRGF0YSBxdWFudGl0eSB2cy4gcXVhbGl0eSBhc3NvY2lhdGlvbnMKYGBge3IgZGF0YSBxdWFudGl0eX0KZ3BzX2RheXMgPSBncHNfZGZfY2xlYW4yICU+JSBncm91cF9ieShJSUQpICU+JSBkcGx5cjo6dGFsbHkobmFtZSA9ICJncHNfZGF5cyIpCmFjY19kYXlzID0gc3Vial9hY2NfZGF0YV9jbGVhbiAlPiUgZ3JvdXBfYnkoSUlEKSAlPiUgZHBseXI6OnRhbGx5KG5hbWUgPSAiYWNjX2RheXMiKQpkYXRhX3F1YW50X3Bsb3RzID0gbGlzdCgpCmRhdGFfcXVhbnRfcGxvdHMkZ3BzID0gY29uZl9zY2F0dGVyX3Bsb3QoZ3BzX2RheXMsIGdwc19hY2Nfc3ViaiwgImdwc19kYXlzIikKZGF0YV9xdWFudF9wbG90cyRhY2MgPSBjb25mX3NjYXR0ZXJfcGxvdChhY2NfZGF5cywgYWNjX2FjY19zdWJqLCAiYWNjX2RheXMiKQpSZWR1Y2UoYCtgLGRhdGFfcXVhbnRfcGxvdHMpCmBgYAoKCmBgYHtyIGRhdGEgcXVhbGl0eX0KZ3BzX3F1YWxpdHkgPSBncHNfZGZfY2xlYW4yICU+JSBncm91cF9ieShJSUQpICU+JSBkcGx5cjo6c3VtbWFyaXplKE1pbnNNaXNzaW5nID0gbWVhbihNaW5zTWlzc2luZyxuYS5ybT1UKSkKYWNjX3F1YWxpdHkgPSBzYXBwbHkoc3Vial9saXN0LGdldF9kYXRhX21lYW4pCmFjY19xdWFsaXR5JElJRCA9IG5hbWVzKGFjY19kYXRhX3F1YWxpdHkpCmFjY19xdWFsaXR5JGRhdGFwb2ludHMgPSBhcy5udW1lcmljKHZhbHVlKHVubGlzdChhY2NfZGF0YV9xdWFsaXR5KVsxOjQxXSkpCgpkYXRhX3F1YWxfcGxvdHMgPSBsaXN0KCkKZGF0YV9xdWFsX3Bsb3RzJGdwcyA9IGNvbmZfc2NhdHRlcl9wbG90KGdwc19xdWFsaXR5LCBncHNfYWNjX3N1YmosICJNaW5zTWlzc2luZyIpCmRhdGFfcXVhbF9wbG90cyRhY2MgPSBjb25mX3NjYXR0ZXJfcGxvdChhY2NfcXVhbGl0eSwgYWNjX2FjY19zdWJqLCAiZGF0YXBvaW50cyIpCgpSZWR1Y2UoYCtgLGRhdGFfcXVhbF9wbG90cykKYGBgCgojIyMgOS4gRGV2ZWxvcG1lbnRhbCBlZmZlY3RzIGFuZCBzZXggZGlmZmVyZW5jZXMKYGBge3IgY3VyYXRlIGRlbW9ncmFwaGljIGRhdGF9CmlkcyA9IHJlYWQueGxzeChmaWxlLnBhdGgocHJvamVjdF9wYXRoLCJkYXRhL2NsaW5pY2FsX2RhdGEvc3ViamVjdHRyYWNrZXJfNC54bHN4IikpWzE6NDEsYygxLDMpXQpkZW1vX3RlbSA9IHJlYWQuY3N2KGZpbGUucGF0aChwcm9qZWN0X3BhdGgsImRhdGEvY2xpbmljYWxfZGF0YS9zZWxmX3JlcG9ydF9pdGVtd2lzZS5jc3YiKSwgY29sQ2xhc3NlcyA9IGMoIk5VTEwiLE5BLCJOVUxMIiwiTlVMTCIsTkEsTkEscmVwKCJOVUxMIiwzNjMpKSkKCmRlbW9fYmVpd2UgPSBpbm5lcl9qb2luKGlkcyxkZW1vX3RlbSwgYnkgPSBjKCJCQkxJRCIgPSAiYmJsaWQiKSkKYWNjX3N1YmpfZGYgPSBkYXRhLmZyYW1lKGJlaXdlSUQgPSBuYW1lcyhhY2Nfc3ViaiksIGdwcyA9IGdwc19hY2Nfc3ViaiwgYWNjID0gYWNjX2FjY19zdWJqLCBhY2NncHMgPSBhY2Nfc3Vial9jYl9hY2NncHMpCmFjY19kZW1vX2RmID0gaW5uZXJfam9pbihhY2Nfc3Vial9kZixkZW1vX2JlaXdlLCBieSA9ICJiZWl3ZUlEIikKYWNjX2RlbW9fZGYgPSBpbm5lcl9qb2luKGFjY19kZW1vX2RmLGdwc19kYXlzLCBieSA9IGMoImJlaXdlSUQiID0gIklJRCIpKQphY2NfZGVtb19kZiA9IGlubmVyX2pvaW4oYWNjX2RlbW9fZGYsYWNjX2RheXMsIGJ5ID0gYygiYmVpd2VJRCIgPSAiSUlEIikpCmFjY19kZW1vX2RmJGFkbWluX3NleCA9IGFzLmZhY3RvcihhY2NfZGVtb19kZiRhZG1pbl9zZXgpCmBgYAoKYGBge3IgZ3BzIGFnZSBhbmQgc2V4fQphZ2VzZXhfZ3BzX2ZpdCA9IGdhbShncHMgfiBzKGFkbWluX2FnZSkgKyBhZG1pbl9zZXggKyBncHNfZGF5cywgZGF0YSA9IGFjY19kZW1vX2RmLCBtZXRob2QgPSAiUkVNTCIpCgpwX2dwcyA9IGxpc3QoKQoKcF9ncHMkYWdlID0gdmlzcmVnKGFnZXNleF9ncHNfZml0LCAiYWRtaW5fYWdlIiwgZ2cgPSBULCBsaW5lPWxpc3QoY29sPSIjMzU3NmI1IixzaXplID0gMC41LCBhbHBoYSA9IDAuMjUpLCAKICAgICAgIHBvaW50cz1saXN0KHNpemU9MiwgcGNoPTE5KSwgZmlsbD0gbGlzdChmaWxsPWMoIiNiM2QzZjIiKSwgYWxwaGEgPSAxKSkgKyAKICB4bGFiKCJBZ2UiKSArIHlsYWIoIkluZGl2aWR1YWwgRm9vdHByaW50IERpc3RpbmN0aXZlbmVzcyBcbiAoR1BTKSIpICsgIHRoZW1lX2Nvd3Bsb3QoKQoKcF9ncHMkc2V4ID0gdmlzcmVnKGFnZXNleF9ncHNfZml0LCAiYWRtaW5fc2V4IiwgZ2cgPSBULCBsaW5lPWxpc3QoY29sPSIjMzU3NmI1IixzaXplID0gMC41LCBhbHBoYSA9IDAuMjUpLCAKICAgICAgIHBvaW50cz1saXN0KHNpemU9MiwgcGNoPTE5KSwgZmlsbD0gbGlzdChmaWxsPWMoIiNiM2QzZjIiKSwgYWxwaGEgPSAwLjI1KSkgKyAKICB4bGFiKCJTZXgiKSArIHlsYWIoIkluZGl2aWR1YWwgRm9vdHByaW50IERpc3RpbmN0aXZlbmVzcyBcbiAoR1BTKSIpICsgIHRoZW1lX2Nvd3Bsb3QoKQoKUmVkdWNlKGArYCxwX2dwcykKYGBgCgpgYGB7ciBhY2MgYWdlIGFuZCBzZXh9CmFnZXNleF9hY2NfZml0ID0gZ2FtKGFjYyB+IHMoYWRtaW5fYWdlKSArIGFkbWluX3NleCArIGFjY19kYXlzICwgZGF0YSA9IGFjY19kZW1vX2RmLCBtZXRob2QgPSAiUkVNTCIpCgpwX2FjYyA9IGxpc3QoKQoKcF9hY2MkYWdlID0gdmlzcmVnKGFnZXNleF9hY2NfZml0LCAiYWRtaW5fYWdlIiwgZ2cgPSBULCBsaW5lPWxpc3QoY29sPSIjMzU3NmI1IixzaXplID0gMC41LCBhbHBoYSA9IDAuMjUpLCAKICAgICAgIHBvaW50cz1saXN0KHNpemU9MiwgcGNoPTE5KSwgZmlsbD0gbGlzdChmaWxsPWMoIiNiM2QzZjIiKSwgYWxwaGEgPSAwLjI1KSkgKyAKICB4bGFiKCJBZ2UiKSArIHlsYWIoIkluZGl2aWR1YWwgRm9vdHByaW50IERpc3RpbmN0aXZlbmVzcyBcbiAoQWNjZWwpIikgKyAgdGhlbWVfY293cGxvdCgpCgpwX2FjYyRzZXggPSB2aXNyZWcoYWdlc2V4X2FjY19maXQsICJhZG1pbl9zZXgiLCBnZyA9IFQsIGxpbmU9bGlzdChjb2w9IiMzNTc2YjUiLHNpemUgPSAwLjUsIGFscGhhID0gMC4yNSksIAogICAgICAgcG9pbnRzPWxpc3Qoc2l6ZT0yLCBwY2g9MTkpLCBmaWxsPSBsaXN0KGZpbGw9YygiI2IzZDNmMiIpLCBhbHBoYSA9IDEpKSArIAogIHhsYWIoIlNleCIpICsgeWxhYigiSW5kaXZpZHVhbCBGb290cHJpbnQgRGlzdGluY3RpdmVuZXNzIFxuIChBY2NlbCkiKSArICB0aGVtZV9jb3dwbG90KCkKClJlZHVjZShgK2AscF9hY2MpCmBgYAoKCiMjIyAxMC4gTW9vZCBhbmQgc2xlZXAgaW5zdGFiaWxpdHkKYGBge3IgfQpzdXJ2ZXlfZnQgPSByZWFkUkRTKGZpbGUucGF0aChkYXRhX3BhdGgsIlJlc3VsdHMvR3JvdXAvc3VydmV5X2Z0LnJkcyIpKQoKcV9saXN0ID0gdW5pcXVlKHN1YnN0cihuYW1lcyhzdXJ2ZXlfZnQkYDI2azU2b285YCksMSwyOSkpCgpxX2xpc3RfbG9uZyA9IG5hbWVzKHN1cnZleV9mdCRgMjZrNTZvbzlgKQoKbW9vZF9zaW5jZV9xcyA9IDIwOjI2CgpybXNzZF9tb29kX3NpbmNlID0gc2FwcGx5KHN1YmpfbGlzdCwgZnVuY3Rpb24oc3Viaikgcm1zc2QocmJpbmRsaXN0KGxhcHBseShsYXBwbHkocV9saXN0W21vb2Rfc2luY2VfcXNdLCBnZXRfcXVlc3Rpb25fYW5zKSxmdW5jdGlvbihxKSBxW1tzdWJqXV0pKSRhbnNfbnVtKSkKbWVhbl9tb29kX3NpbmNlID0gc2FwcGx5KHN1YmpfbGlzdCwgZnVuY3Rpb24oc3ViaikgbWVhbihyYmluZGxpc3QobGFwcGx5KGxhcHBseShxX2xpc3RbbW9vZF9zaW5jZV9xc10sIGdldF9xdWVzdGlvbl9hbnMpLGZ1bmN0aW9uKHEpIHFbW3N1YmpdXSkpJGFuc19udW0sIG5hLnJtID0gVCkpCm1vb2RfZGYgPSBhY2NfZGVtb19kZgptb29kX2RmJHJtc3NkID0gcm1zc2RfbW9vZF9zaW5jZQptb29kX2RmJG1lYW4gPSBtZWFuX21vb2Rfc2luY2UKCm1vb2RfYWNjZ3BzX2ZpdCA9IGdhbShhY2NncHMgfiBybXNzZCArIGdwc19kYXlzICsgYWNjX2RheXMgKyBzKGFkbWluX2FnZSkgKyBhZG1pbl9zZXggKyBtZWFuLCBkYXRhID0gbW9vZF9kZiwgIG1ldGhvZCA9ICJSRU1MIikKCgpzbGVlcF9kdXJhdGlvbiA9ICJBYm91dCBob3cgbWFueSBob3VycyBkaWQgeW91ICIKcm1zc2Rfc2xlZXBfZHVyID0gZ2V0X3F1ZXN0aW9uX3ZhcmlhbmNlKHNsZWVwX2R1cmF0aW9uKQptZWFuX3NsZWVwX2R1ciA9IGdldF9xdWVzdGlvbl9tZWFuKHNsZWVwX2R1cmF0aW9uKQpzbGVlcF9kZiA9IGFjY19kZW1vX2RmCnNsZWVwX2RmJHJtc3NkID0gcm1zc2Rfc2xlZXBfZHVyCnNsZWVwX2RmJG1lYW4gPSBtZWFuX3NsZWVwX2R1cgoKc2xlZXBfYWNjZ3BzX2ZpdCA9IGdhbShhY2NncHMgfiBybXNzZCArIGdwc19kYXlzICsgYWNjX2RheXMgKyBzKGFkbWluX2FnZSkgKyBhZG1pbl9zZXggKyBtZWFuLCBkYXRhID0gc2xlZXBfZGYsICBtZXRob2QgPSAiUkVNTCIpCmBgYAoKYGBge3IgcGxvdHN9CnBfbW9vZF9zbGVlcCA9IGxpc3QoKQoKcF9tb29kX3NsZWVwJG1vb2QgPSB2aXNyZWcobW9vZF9hY2NncHNfZml0LCAicm1zc2QiLCBnZyA9IFQsIGxpbmU9bGlzdChjb2w9IiMzNTc2YjUiLHNpemUgPSAwLjUsIGFscGhhID0gMC4yNSksIAogICAgICAgcG9pbnRzPWxpc3Qoc2l6ZT0yLCBwY2g9MTkpLCBmaWxsPSBsaXN0KGZpbGw9YygiI2IzZDNmMiIpLCBhbHBoYSA9IDEpLCB5bGltID0gYygwLDEpKSArIAogIHhsYWIoIk1vb2QgSW5zdGFiaWxpdHkiKSArIHlsYWIoIkluZGl2aWR1YWwgRm9vdHByaW50IERpc3RpbmN0aXZlbmVzcyIpICsgIHRoZW1lX2Nvd3Bsb3QoKQoKcF9tb29kX3NsZWVwJHNsZWVwID0gdmlzcmVnKHNsZWVwX2FjY2dwc19maXQsICJybXNzZCIsIGdnID0gVCwgbGluZT1saXN0KGNvbD0iIzM1NzZiNSIsc2l6ZSA9IDAuNSwgYWxwaGEgPSAwLjI1KSwgCiAgICAgICBwb2ludHM9bGlzdChzaXplPTIsIHBjaD0xOSksIGZpbGw9IGxpc3QoZmlsbD1jKCIjYjNkM2YyIiksIGFscGhhID0gMSksIHlsaW0gPSBjKDAsMSkpICsKICB4bGFiKCJTbGVlcCBJbnN0YWJpbGl0eSIpICsgeWxhYigiSW5kaXZpZHVhbCBGb290cHJpbnQgRGlzdGluY3RpdmVuZXNzIikgKyAgdGhlbWVfY293cGxvdCgpCgpSZWR1Y2UoYCtgLHBfbW9vZF9zbGVlcCkKCmBgYAoKCiMjIyAxMS4gV2l0aGluIE5ldHdvcmsgRnVuY3Rpb25hbCBDb25uZWN0aXZpdHkKYGBge3IgZGVmaW5lIEZDIHBhdGhzfQpwcm9qZWN0X3BhdGggPSAifi9Eb2N1bWVudHMveGlhX2dwcy8iCmRhdGFfcGF0aCA9IGZpbGUucGF0aChwcm9qZWN0X3BhdGgsImRhdGEvZmx5d2hlZWxfZGF0YS9uZXR3b3JrX3R4dCIpCmBgYAoKYGBge3IgY29tcGlsZSBmYyBkYXRhfQpzdWJqX25ldF9maWxlcyA9IGxpc3QoKQphdGxhc2VzID0gYygic2NoYWVmZXIyMDB4NyIsInNjaGFlZmVyNDAweDciKQpmb3IgKGF0bGFzIGluIGF0bGFzZXMpIHsKICBzdWJqZWN0cyA9IGxpc3QuZmlsZXMoZmlsZS5wYXRoKGRhdGFfcGF0aCkpCiAgZm9yIChzdWJqIGluIHN1YmplY3RzKXsKICAgIHN1YmpfZmlsZXMgPSBsaXN0LmZpbGVzKGZpbGUucGF0aChkYXRhX3BhdGgsc3ViaikpCiAgICBuZXRfcGF0dGVybiA9IHBhc3RlMCgiKnRhc2stcmVzdCptdWx0aSoiLGF0bGFzLCIqIikKICAgIGZpbGVfcGF0aCA9IGZpbGUucGF0aChkYXRhX3BhdGgsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc3ViaixzdWJqX2ZpbGVzW2dyZXAoZ2xvYjJyeChuZXRfcGF0dGVybiksc3Vial9maWxlcyldKQogICAgaWYgKGxlbmd0aChmaWxlX3BhdGgpPjApIHtzdWJqX25ldF9maWxlc1tbYXRsYXNdXVtbc3Vial1dID0gcmVhZC50YWJsZShmaWxlX3BhdGgpCiAgfQogIH0KfQoKbW90aW9uX2RmID0gYygpCmZvciAoc3ViaiBpbiBzdWJqZWN0cyl7CiAgc3Vial9xdWFsaXR5X2ZpbGVzID0gbGlzdC5maWxlcyhmaWxlLnBhdGgoZGF0YV9wYXRoLCIuLi9xdWFsaXR5X2NzdiIsc3ViaikpCiAgbmV0X3BhdHRlcm4gPSBwYXN0ZTAoIip0YXNrLXJlc3QqbXVsdGkqIikKICBzdWJqX3F1YWxpdHlfZmlsZSA9IGZpbGUucGF0aChkYXRhX3BhdGgsICIuLi9xdWFsaXR5X2NzdiIsc3ViaixzdWJqX3F1YWxpdHlfZmlsZXNbZ3JlcChnbG9iMnJ4KG5ldF9wYXR0ZXJuKSxzdWJqX3F1YWxpdHlfZmlsZXMpXSkKICBtb3Rpb25fZGZbc3Vial0gPSByZWFkLmNzdihzdWJqX3F1YWxpdHlfZmlsZSkkcmVsTWVhblJNU01vdGlvbgp9CmBgYAoKCgpgYGB7ciBjb21wdXRlIGZjIGNvbW11bml0eX0KYXRsYXNfcGF0aCA9ICIvVXNlcnMvaHhpYS9Eb2N1bWVudHMvR2l0SHViL3hjcEVuZ2luZS9hdGxhcyIKZmNfY29tID0gbGFwcGx5KGF0bGFzZXMsIGZ1bmN0aW9uKGF0bGFzKSBnZXRfZmNfY29tKGF0bGFzLCBzdWJqX25ldF9maWxlcykpCm5hbWVzKGZjX2NvbSkgPSBhdGxhc2VzCgpmY19jb21fbWVhbiA9IGxhcHBseShhdGxhc2VzLCBmdW5jdGlvbihhdGxhcykgYXMuZGF0YS5mcmFtZShzYXBwbHkoZmNfY29tW1thdGxhc11dLCBmdW5jdGlvbihuZXRfdG9fbmV0KSBzYXBwbHkobmV0X3RvX25ldCwgZnVuY3Rpb24oc3ViaikgbWVhbihzdWJqJFYxLCBuYS5ybT1UKSkpKSkKbmFtZXMoZmNfY29tX21lYW4pID0gYXRsYXNlcwpgYGAKCgpgYGB7ciBtZXJnZSB3aXRoIGRlbW9ncmFwaGljc30KZnB0X2ZjID0gbGlzdCgpCmZvciAoYXRsYXMgaW4gYXRsYXNlcyl7CiAgZmNfc3VianMgPSBhcy5udW1lcmljKHJvd25hbWVzKGZjX2NvbV9tZWFuW1thdGxhc11dKSkKICBmY19jb21fbWVhbltbYXRsYXNdXSRCQkxJRCA9IGZjX3N1YmpzCiAgZmNfY29tX21lYW5bW2F0bGFzXV0kbW90aW9uID0gdmFsdWUobW90aW9uX2RmKQogIGZwdF9mY1tbYXRsYXNdXSA9IG1lcmdlKGZjX2NvbV9tZWFuW1thdGxhc11dLCBhY2NfZGVtb19kZiwgYnkgPSAiQkJMSUQiKQp9CgpgYGAKCgpgYGB7ciBuZXR3b3JrIGNvbm5lY3Rpdml0eSBhc3NvcyBmb290cHJpbnR9CmF0bGFzID0gInNjaGFlZmVyMjAweDciCmZjX25ldHMgPSBjKCJ2aXNfdmlzIiwic29tX3NvbSIsImRvcl9kb3IiLCJzYWxfc2FsIiwibGltX2xpbSIsImZyb19mcm8iKQpmcHRfZmNfcCA9IGRhdGEuZnJhbWUobmV0ID0gcmVwKE5BLCBsZW5ndGgoZmNfbmV0cykpLCBwX3ZhbCA9IHJlcChOQSwgbGVuZ3RoKGZjX25ldHMpKSkKZnB0X2ZjX2ZpdCA9IGxpc3QoKQpmb3IgKG5ldCBpbiBmY19uZXRzKXsKICBuZXRfaSA9IHdoaWNoKGZjX25ldHMgPT0gbmV0KQogIGZpdCA9IGdhbShnZXQobmV0KSAgfiBzKGFkbWluX2FnZSkgKyBhZG1pbl9zZXggKyBncHNfZGF5cyArIGFjY19kYXlzICArIG1vdGlvbiArIGFjY2dwcywgZGF0YSA9IGZwdF9mY1tbYXRsYXNdXSwgbWV0aG9kID0gIlJFTUwiKQogIGZwdF9mY19wJG5ldFtuZXRfaV0gPSBuZXQKICBmcHRfZmNfcCRwX3ZhbFtuZXRfaV0gPSBzdW1tYXJ5KGZpdClbWzRdXVs2XQogIGZwdF9mY19maXRbW25ldF1dID0gZml0Cn0KCmZwdF9mY19wJHBfdmFsX2FkanVzdCA9IHAuYWRqdXN0KGZwdF9mY19wJHBfdmFsLCJmZHIiKQpwcmludChmcHRfZmNfcCkKYGBgCgoKYGBge3Igc29tX3NvbSBwbG90c30Kc29tX3NvbV9maXQgPSBnYW0oc29tX3NvbSAgfiBzKGFkbWluX2FnZSkgKyBhZG1pbl9zZXggKyBncHNfZGF5cyArIGFjY19kYXlzICArIG1vdGlvbiArIGFjY2dwcywgZGF0YSA9IGZwdF9mY1tbYXRsYXNdXSwgbWV0aG9kID0gIlJFTUwiKQp2aXNyZWcoc29tX3NvbV9maXQsICdhY2NncHMnLCBnZyA9IFQpICsgdGhlbWVfY293cGxvdCgpICsgeGxhYigiSW5kaXZpZHVhbCBGb290cHJpbnQgRGlzdGluY3RpdmVuZXNzIikgKyB5bGFiKCJTb20tU29tIikKYGBgCgojIyMgMTIuIEZDIHByZWRpY3QgZm9vdHByaW50CmBgYHtyIGdldCBmYyBlZGdlIGRhdGF9CmZjX2VkZ2UgPSBsYXBwbHkoYXRsYXNlcywgZnVuY3Rpb24oYXRsYXMpIGdldF9mY19lZGdlKGF0bGFzLCBzdWJqX25ldF9maWxlcykpCm5hbWVzKGZjX2VkZ2UpID0gYXRsYXNlcwoKZmNfZWRnZV9kZiA9IHNhcHBseShhdGxhc2VzLCBmdW5jdGlvbihhdGxhcykgYXMuZGF0YS5mcmFtZSh0KHNhcHBseShmY19lZGdlW1thdGxhc11dLCBmdW5jdGlvbihzdWJqKSBzdWJqKSkpKQpuYW1lcyhmY19lZGdlX2RmKSA9IGF0bGFzZXMKCmZvciAoYXRsYXMgaW4gYXRsYXNlcyl7CiAgZmNfZWRnZV9kZltbYXRsYXNdXSRCQkxJRCA9IGZjX3N1YmpzCiAgZmNfZWRnZV9kZltbYXRsYXNdXSRtb3Rpb24gPSB2YWx1ZShtb3Rpb25fZGYpCiAgZnB0X2ZjX2VkZ2VbW2F0bGFzXV0gPSBtZXJnZShmY19lZGdlX2RmW1thdGxhc11dLCBhY2NfZGVtb19kZiwgYnkgPSAiQkJMSUQiKQp9CmBgYAoKYGBge3J9CmF0bGFzID0gInNjaGFlZmVyMjAweDciCm5vbl9lZGdlX3ZhciA9IGModGFpbChuYW1lcyhmcHRfZmNfZWRnZVtbYXRsYXNdXSksOSksIkJCTElEIikKZWRnZV92YXJzID0gbmFtZXMoZnB0X2ZjX2VkZ2VbW2F0bGFzXV0pWy13aGljaChuYW1lcyhmcHRfZmNfZWRnZVtbYXRsYXNdXSkgJWluJSBub25fZWRnZV92YXIpXQplZGdlX2RhdGEgPSBmcHRfZmNfZWRnZVtbYXRsYXNdXQoKbGFtYmRhcyA9IHJldihzZXEoMC4wMSwwLjEwLDAuMDEpKQpsYXNzb190dW5lX2wgPSBsaXN0KCkKbGFzc29fb3V0ID0gZGF0YS5mcmFtZSgpCmZvciAoaSBpbiAxOmRpbShlZGdlX2RhdGEpWzFdKXsKICBkZl90cmFpbiA9IGVkZ2VfZGF0YVstaSxdCiAgZGZfdGVzdCA9IGVkZ2VfZGF0YVtpLF0KICAKICBsYXNzb190dW5lID0gbGFzc29fZWRnZShkZl90cmFpbixlZGdlX3ZhcnMsbGFtYmRhcykKICBsYXNzb190dW5lX2xbW2ldXSA9IGxhc3NvX3R1bmUKICBiZXN0X2xhbWJkYSA9IGxhbWJkYXNbd2hpY2gubWF4KGxhc3NvX3R1bmUpXQogIAogIHByaW50KGxhc3NvX3R1bmUpCiAgcHJpbnQocGFzdGUoaSwiLS0tLS0iLCAiYmVzdCBsYW1iZGEgaXMiLCBiZXN0X2xhbWJkYSkpCiAgCiAgZGZfdHJhaW5fZml0ID0gZ2FtKGFjY2dwcyAgfiBzKGFkbWluX2FnZSkgKyBhZG1pbl9zZXggKyBncHNfZGF5cyArIGFjY19kYXlzICArIG1vdGlvbiAsIGRhdGEgPSBkZl90cmFpbiwgbWV0aG9kID0gIlJFTUwiKQogIGRmX3RyYWluJGFjY2dwc19yZXMgPSBkZl90cmFpbl9maXQkcmVzaWR1YWxzCiAgCiAgeF90cmFpbiA9IGFzLm1hdHJpeChkZl90cmFpblssZWRnZV92YXJzXSkKICB5X3RyYWluID0gZGZfdHJhaW4kYWNjZ3BzX3JlcwoKICB4X3Rlc3QgPSBhcy5tYXRyaXgoZGZfdGVzdFssZWRnZV92YXJzXSkKICB5X3Rlc3QgPSBkZl90ZXN0JGFjY2dwcyAtIHByZWRpY3QoZGZfdHJhaW5fZml0LG5ld2RhdGEgPSBkZl90ZXN0KQogIAogIGxhc3NvX3JlZyA9IGdsbW5ldCh4X3RyYWluLCB5X3RyYWluLCBsYW1iZGEgPSBiZXN0X2xhbWJkYSwgYWxwaGEgPSAxLCBzdGFuZGFyZGl6ZSA9IFRSVUUpCiAgCiAgcHJlZGljdGlvbnNfdGVzdCA8LSBwcmVkaWN0KGxhc3NvX3JlZywgcyA9IGJlc3RfbGFtYmRhLCBuZXd4ID0geF90ZXN0KQogIAogIGxhc3NvX291dFtpLCJwcmVkaWN0Il0gPSBhcy5udW1lcmljKHByZWRpY3Rpb25zX3Rlc3QpCiAgbGFzc29fb3V0W2ksInJlYWwiXSA9IHlfdGVzdAp9CmBgYAoKCmBgYHtyIHByZWRpY3Qgc2NhdHRlciBwbG90fQpnZ3Bsb3QoZGF0YSA9IGxhc3NvX291dCxhZXMoeD1wcmVkaWN0LHkgPSByZWFsKSkgKyAKICAgIGdlb21fcG9pbnQoKSArIHRoZW1lX2Nvd3Bsb3QoKSArIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtLCBjb2xvdXIgPSAiYmxhY2siKSAgKwogICAgeGxhYigiUHJlZGljdGVkIEZvb3RwcmludCBEaXN0aW5jdGl2ZW5lc3MgIikgKyB5bGFiKCJBY3R1YWwgRm9vdHByaW50IERpc3RpbmN0aXZlbmVzcyAiKQpgYGAKCmBgYHtyIHByZWRpY3QgcGVybXV0YXRpb259Cmxhc3NvX3Blcm0gPSBsaXN0KCkKZm9yIChqIGluIDE6MTAwMCl7CiAgbGFtYmRhcyA9IHJldihzZXEoMC4wMSwwLjEwLDAuMDEpKQogIGxhc3NvX3R1bmVfbCA9IGxpc3QoKQogIGxhc3NvX291dCA9IGRhdGEuZnJhbWUoKQogIGZvciAoaSBpbiAxOmRpbShlZGdlX2RhdGEpKXsKICAgIGRmX3RyYWluID0gZWRnZV9kYXRhWy1pLF0KICAgIGRmX3Rlc3QgPSBlZGdlX2RhdGFbaSxdCiAgICAKICAgIGxhc3NvX3R1bmUgPSBsYXNzb19lZGdlX3Blcm0oZGZfdHJhaW4sZWRnZV92YXJzLGxhbWJkYXMpCiAgICBsYXNzb190dW5lX2xbW2ldXSA9IGxhc3NvX3R1bmUKICAgIGJlc3RfbGFtYmRhID0gbGFtYmRhc1t3aGljaC5tYXgobGFzc29fdHVuZSldCiAgICAKICAgIHByaW50KGxhc3NvX3R1bmUpCiAgICBwcmludChwYXN0ZShpLCItLS0tLSIsICJiZXN0IGxhbWJkYSBpcyIsIGJlc3RfbGFtYmRhKSkKICAgIAogICAgZGZfdHJhaW5fZml0ID0gZ2FtKGFjY2dwcyAgfiBzKGFnZSkgKyBzZXggKyBncHNfZGF5cyArIGFjY19kYXlzICArIG1vdGlvbiAsIGRhdGEgPSBkZl90cmFpbiwgbWV0aG9kID0gIlJFTUwiKQogICAgZGZfdHJhaW4kYWNjZ3BzX3JlcyA9IGRmX3RyYWluX2ZpdCRyZXNpZHVhbHMKICAgIAogICAgeF90cmFpbiA9IGFzLm1hdHJpeChkZl90cmFpblssZWRnZV92YXJzXSkKICAgIHlfdHJhaW4gPSBkZl90cmFpbiRhY2NncHNfcmVzCiAgICB5X3RyYWluID0gc2FtcGxlKHlfdHJhaW4pCiAgICAKICAgIHhfdGVzdCA9IGFzLm1hdHJpeChkZl90ZXN0WyxlZGdlX3ZhcnNdKQogICAgeV90ZXN0ID0gZGZfdGVzdCRhY2NncHMgLSBwcmVkaWN0KGRmX3RyYWluX2ZpdCxuZXdkYXRhID0gZGZfdGVzdCkKICAgIAogICAgbGFzc29fcmVnID0gZ2xtbmV0KHhfdHJhaW4sIHlfdHJhaW4sIGxhbWJkYSA9IGJlc3RfbGFtYmRhLCBhbHBoYSA9IDEsIHN0YW5kYXJkaXplID0gVFJVRSkKICAgIAogICAgcHJlZGljdGlvbnNfdGVzdCA8LSBwcmVkaWN0KGxhc3NvX3JlZywgcyA9IGJlc3RfbGFtYmRhLCBuZXd4ID0geF90ZXN0KQogICAgCiAgICBsYXNzb19vdXRbaSwicHJlZGljdCJdID0gYXMubnVtZXJpYyhwcmVkaWN0aW9uc190ZXN0KQogICAgbGFzc29fb3V0W2ksInJlYWwiXSA9IHlfdGVzdAogIH0KICBsYXNzb19wZXJtW1tqXV0gPSBjb3IobGFzc29fb3V0JHByZWRpY3QsbGFzc29fb3V0JHJlYWwpCn0KCmBgYAoKYGBge3IgcGVybSBoaXN0IHBsb3R9Cmxhc3NvX3B2YWwgPSBwYXN0ZSgicGVybXV0ZWQgcCA9IiwgbGVuZ3RoKHdoaWNoKGxhc3NvX3Blcm0gPiBjb3IobGFzc29fb3V0JHByZWRpY3QsbGFzc29fb3V0JHJlYWwpKSkvMTAwMCkKZ2dwbG90KGRhdGEuZnJhbWUocGVybT1sYXNzb19wZXJtKSwgYWVzKHg9cGVybSkpICsgCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJsaWdodCBibHVlIikgKyB0aGVtZV9jb3dwbG90KCkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD0gY29yKGxhc3NvX291dCRwcmVkaWN0LGxhc3NvX291dCRyZWFsKSwgY29sb3IgPSAicmVkIikgKwogIHhsYWIoIkNvcnJlbGF0aW9uIGJldHdlZW4gYWN0dWFsIGFuZCBwcmVkaWN0ZWQiKSArIGdlb21fdGV4dCh4PS0wLjIsIHk9ODAsIGxhYmVsPWxhc3NvX3B2YWwpCmBgYAoKYGBge3IgZWRnZSByZXN1bHRzfQpsYXNzb19iZXRhID0gZ2V0X2ZpbmFsX2xhc3MoZWRnZV9kYXRhLGVkZ2VfdmFycykkYmV0YQpsYXNzb19iZXRhX2RmID0gc3VtbWFyeShsYXNzb19iZXRhKQpsYXNzb19iZXRhX2RmJG5hbWUgPSBlZGdlX3ZhcnNbc3VtbWFyeShsYXNzb19iZXRhKSRpXQpwcmludChsYXNzb19iZXRhX2RmKQpgYGAKCg==