The analyses presented here test subsequent memory effects during encoding of a multi-feature event (from the GitHub repository: https://github.com/memobc/paper-bindingfmri).

Mean single trial beta estimates at i) onset and ii) offset of each visual display (onset N and offset N are 6s apart) were extracted from each of 204 ROIs across the cortex and medial temporal lobe (see ~/data/bindingfmri_wholebrain_ROIs_info.csv). Onsets are used to test the relationship between brain activity and memory for the upcoming event, whereas offsets are used to test the relationship between brain activity and memory for the preceding event.

Each event includes 3 associations encoded in parallel – an object with an associated i) color, ii) sound, and iii) scene location.

Subsequent memory detail is calculated as the number of features (0-3) later correctly recalled. Feature-specific estimates of subsequent memory are also analyzed (correct, 1, vs. incorrect, 0). The relationship between activity (each ROI’s beta series) and subsequent memory is estimated using linear mixed effects (LME) models with the following pipeline:

  1. Data are cleaned by removing any trials with outlying betas (z > |4|), calculated within subject per ROI
  2. Each LME model is initially specified with a maximal random effects structure (including variable slopes for all fixed effects, as well as intercepts, by subject), but to avoid convergence problems and singular fits:
  1. Random effects correlations are constrained to zero for all models
  2. The random effect structure of models with > 2 fixed effects is simplified by iteratively removing slopes that do not improve the model fit
  1. The significance of the fixed effects (slope > or < 0?) is tested using t-tests with Satterthwaite’s approximation method

Behavioral Data

### Load in behavioral data for all 27 subjects:
behavData <- read.csv('../data/AllSubjects_bindingfMRI-behavior.csv', header = TRUE)
behav_subs <- unique(behavData$SubID)
cat('Number of subjects =',length(behav_subs))
Number of subjects = 27
# ** note. behavioral data is already sorted by subject and encoding trial onset ** #
# Create additional columns for subsequent memory measures of interest -- Sound, Color, & Scene features ----------------------------
behavData$Sound <- 0
behavData$Sound[behavData$SoundCorrect == 1 & behavData$SoundConfidence == 1] <- 1 # sound only sucessful if correct *and* high confidence
# define 'success' thresholds for color and scene trials -> 75% probability within von Mises for 27 subjects aggregate data
cCutOff = 42
sCutOff = 24
# feature accuracy
behavData$Color <- 0
behavData$Color[behavData$ColAbsError <=cCutOff] <- 1 #correct if error (how far from target) less than or equal to threshold
behavData$Scene <- 0
behavData$Scene[behavData$SceAbsError <=sCutOff] <- 1 #correct if error (how far from target) less than or equal to threshold
# overall memory detail (accounting for feature success - 0-3)
behavData$MemoryDetail <- behavData$Sound + behavData$Color + behavData$Scene
#### separate trial-unique conditions based on feature combinations recalled --------------------
## 1 feature
behavData$Color.Only   <- 0
behavData$Sound.Only <- 0
behavData$Scene.Only   <- 0
behavData$Color.Only[behavData$Color == 1 & behavData$Sound == 0 & behavData$Scene == 0]  <- 1
behavData$Sound.Only[behavData$Color == 0 & behavData$Sound == 1 & behavData$Scene == 0]  <- 1
behavData$Scene.Only[behavData$Color == 0 & behavData$Sound == 0 & behavData$Scene == 1]  <- 1
## 2 features
behavData$Color.Scene   <- 0
behavData$Sound.Color <- 0
behavData$Scene.Sound <- 0
behavData$Color.Scene[behavData$Color == 1 & behavData$Sound == 0 & behavData$Scene == 1] <- 1
behavData$Sound.Color[behavData$Color == 1 & behavData$Sound == 1 & behavData$Scene == 0] <- 1
behavData$Scene.Sound[behavData$Color == 0 & behavData$Sound == 1 & behavData$Scene == 1] <- 1
## 3 features
behavData$Color.Scene.Sound  <- 0
behavData$Color.Scene.Sound[behavData$Color == 1 & behavData$Sound == 1 & behavData$Scene == 1] <- 1
### run a check to make sure no trial has been allocated to more than one combination:
nPresent <- rowSums(behavData[,c("Color.Only","Sound.Only","Scene.Only","Color.Scene","Sound.Color","Scene.Sound","Color.Scene.Sound")])
if (max(nPresent) < 1) {
  cat('ERROR: trial(s) assigned to more than one combination\n')
}
# double check encoding data is sorted correctly (by subject, then encoding onset (by run and time))
behavData <- behavData[order(behavData$TotalEncodingTrial),]
nTotal <- nrow(behavData)
# print percentage correct for each feature
feature.accuracy <- behavData[,c("SubID","StudyTrial","Color","Sound","Scene")]
sub.behav <- feature.accuracy %>% gather(Feature,Accuracy,Color:Scene) %>%
                          group_by(SubID, Feature) %>%
                          summarise(M = mean(Accuracy))
kable(sub.behav %>% group_by(Feature) %>%
                          summarise(Memory = mean(M), SE = se(M)),format="pandoc",
                                    caption="Feature Accuracy (proportion of trials labeled as correct)")


Table: Feature Accuracy (proportion of trials labeled as correct)

Feature       Memory          SE
--------  ----------  ----------
Color      0.6604424   0.0240285
Scene      0.6606996   0.0331897
Sound      0.5626543   0.0338430
### print total number of trials that uniquely fall into each possible combination:
cat('\nNone recalled trials =', nrow(subset(behavData,MemoryDetail == 0)),'(',round(((nrow(subset(behavData,MemoryDetail == 0)))/nTotal)*100,2),'%)\n')

None recalled trials = 456 ( 12.03 %)
cat('Color only trials =', sum(behavData$Color.Only),'(',round(((sum(behavData$Color.Only))/nTotal)*100,2),'%)\n')
Color only trials = 384 ( 10.13 %)
cat('Scene only trials =', sum(behavData$Scene.Only),'(',round(((sum(behavData$Scene.Only))/nTotal)*100,2),'%)\n')
Scene only trials = 268 ( 7.07 %)
cat('Sound only trials =', sum(behavData$Sound.Only),'(',round(((sum(behavData$Sound.Only))/nTotal)*100,2),'%)\n')
Sound only trials = 162 ( 4.27 %)
cat('Color & Scene only trials =', sum(behavData$Color.Scene),'(',round(((sum(behavData$Color.Scene))/nTotal)*100,2),'%)\n')
Color & Scene only trials = 542 ( 14.29 %)
cat('Scene & Sound only trials =', sum(behavData$Scene.Sound),'(',round(((sum(behavData$Scene.Sound))/nTotal)*100,2),'%)\n')
Scene & Sound only trials = 397 ( 10.47 %)
cat('Sound & Color only trials =', sum(behavData$Sound.Color),'(',round(((sum(behavData$Sound.Color))/nTotal)*100,2),'%)\n')
Sound & Color only trials = 277 ( 7.3 %)
cat('Color & Scene & Sound trials =', sum(behavData$Color.Scene.Sound),'(',round(((sum(behavData$Color.Scene.Sound))/nTotal)*100,2),'%)\n')
Color & Scene & Sound trials = 1306 ( 34.44 %)

Check for behavioral (memory) trial dependencies

Check if encoding success at trial N is related to encoding success on trial N+1 within subjects

behavData$EncodingEvent <- rep(1:24, nrow(behavData)/24) #event number within run
# overall detail, then each feature separately
features = c('MemoryDetail','Color','Sound','Scene')
for (f in 1:length(features)) {
  my.col <- which(colnames(behavData) %in% features[f])
  correlations <- data.frame(array(0,c(length(behav_subs),2)))
  colnames(correlations) <- c('SubID','Z')
  for (s in 1:length(behav_subs)){
    subData <- subset(behavData, SubID == behav_subs[s])
    correlations$SubID[s] <- behav_subs[s]
    trialn  <- subset(subData, EncodingEvent < 24) #all but last event of run for N vector (1:23) correlated with ...
    trialn1 <- subset(subData, EncodingEvent > 1)  #all but first event of the run for N+1 vector (2:24)
    correlations$Z[s] <- fisherz(cor(trialn[,my.col],trialn1[,my.col]))
  }
  
  R = fisherz2r(mean(correlations$Z))
  SEmean = se(correlations$Z)
  cat('Mean correlation (r) between trial N and N+1 for',features[f],'within subject =',R,', SE =',SEmean,'\n')
}
Mean correlation (r) between trial N and N+1 for MemoryDetail within subject = 0.1005285 , SE = 0.02729053 
Mean correlation (r) between trial N and N+1 for Color within subject = 0.01079016 , SE = 0.01759081 
Mean correlation (r) between trial N and N+1 for Sound within subject = 0.1127844 , SE = 0.03943163 
Mean correlation (r) between trial N and N+1 for Scene within subject = 0.06142598 , SE = 0.02007916 

Get Onset Data

Onsets are used to test the relationship between activity and memory for the upcoming event

############################# single trial activity data ##############################
# get subjects' data
subjects <- list.files(path='../data/single-trial-betas/event-onset/',full.names=TRUE, recursive = TRUE, pattern = 'onset-wholebrain')
allData  = do.call(rbind, lapply(subjects, function(x) {read.csv(x, header = TRUE)}))
allData$SubID=as.factor(allData$SubID)
allData$ROI <- as.factor(allData$ROI)
allData <- allData[,-c(5)] # remove vox numbers - not needed here
rois = levels(allData$ROI)
cat('Number of subjects =', length(unique(allData$SubID)),'\n')
Number of subjects = 27 
cat('Removing influential onset betas, |z| >', outlier_value,'\n')
Removing influential onset betas, |z| > 4 
# ******************************************************** #
### add z score of betas within region and subject to remove outliers:
allData <- allData %>%
               group_by(SubID, ROI) %>%
               mutate(Z = zscore(MeanBeta))
# remove betas that are > XSD -- print this number
allData.clean <- allData
allData.clean$MeanBeta[abs(allData.clean$Z) > outlier_value] <- NA  #mark beta as NA if outlier
allData.clean <- allData.clean[,-c(6)] # remove Z values  
##########################################################
#spread data so each of the 204 ROIs is in its own column (to align with behavioral data)
myBetas = data.frame(spread(allData.clean, ROI, MeanBeta))
myBetas$TotalEncodingTrial <- 1:nTotal
# remove all trials where there is at least one NA (at least one of the ROIs is an outlier on that trial) - 
# to keep the trials analyzed the same across regions:
idxBetas <- complete.cases(myBetas)
myBetas.clean <- myBetas[idxBetas,]
cat('Total number of onsets removed =',sum(!idxBetas),'out of',nTotal,'\n\n')
Total number of onsets removed = 108 out of 3792 
##### merge cleaned betas with behavioral data, removing corresponding behavioral trials:
behavData.clean <- behavData[idxBetas,]
testData <- merge.data.frame(myBetas.clean,behavData.clean,by="TotalEncodingTrial")
# remove duplicate columns for subject, run, and study trial
testData <- testData[,-c(2,3,4)]
colnames(testData)[colnames(testData) == 'SubID.y'] <- 'SubID'
colnames(testData)[colnames(testData) == 'Run.y'] <- 'Run'
subjects <- unique(testData$SubID)
onset.data  <- testData  #store onset betas and behavioral data for upcoming trial
cat('Cleaned and merged onset betas and behavioral trials\n')
Cleaned and merged onset betas and behavioral trials

Get Offset Data

Offsets are used to test the relationship between activity and memory for the preceding event

############################# single trial activity data ##############################
# get subjects' data
subjects <- list.files(path='../data/single-trial-betas/event-offset/',full.names=TRUE, recursive = TRUE, pattern = 'offset-wholebrain')
allData  = do.call(rbind, lapply(subjects, function(x) {read.csv(x, header = TRUE)}))
allData$SubID=as.factor(allData$SubID)
allData$ROI <- as.factor(allData$ROI)
allData <- allData[,-c(5)] # remove vox numbers - not needed here
rois = levels(allData$ROI)
cat('Number of subjects =', length(unique(allData$SubID)),'\n')
Number of subjects = 27 
cat('Removing influential offset betas, |z| >', outlier_value,'\n')
Removing influential offset betas, |z| > 4 
# ******************************************************** #
### add z score of betas within region and subject to remove outliers:
allData <- allData %>%
               group_by(SubID, ROI) %>%
               mutate(Z = zscore(MeanBeta))
# remove betas that are > XSD -- print this number
allData.clean <- allData
allData.clean$MeanBeta[abs(allData.clean$Z) > outlier_value] <- NA  #mark beta as NA if outlier
allData.clean <- allData.clean[,-c(6)] # remove Z values  
##########################################################
#spread data so each of the 204 ROIs is in its own column (to align with behavioral data)
myBetas = data.frame(spread(allData.clean, ROI, MeanBeta))
myBetas$TotalEncodingTrial <- 1:nTotal
# remove all trials where there is at least one NA (at least one of the ROIs is an outlier on that trial) - 
# to keep the trials analyzed the same across regions:
idxBetas <- complete.cases(myBetas)
myBetas.clean <- myBetas[idxBetas,]
cat('Total number of offsets removed =',sum(!idxBetas),'out of',nTotal,'\n\n')
Total number of offsets removed = 113 out of 3792 
##### merge cleaned betas with behavioral data, removing corresponding behavioral trials:
behavData.clean <- behavData[idxBetas,]
testData <- merge.data.frame(myBetas.clean,behavData.clean,by="TotalEncodingTrial")
# remove duplicate columns for subject, run, and study trial
testData <- testData[,-c(2,3,4)]
colnames(testData)[colnames(testData) == 'SubID.y'] <- 'SubID'
colnames(testData)[colnames(testData) == 'Run.y'] <- 'Run'
subjects <- unique(testData$SubID)
offset.data  <- testData  #store offset betas and behavioral data for preceding trial
cat('Cleaned and merged offset betas and behavioral trials\n')
Cleaned and merged offset betas and behavioral trials

Correlate onset/offset betas

  1. Separated by 1s ITI: First remove any onsets for which offset N-1 is not available (1s apart) within subject and run, and vice versa. Correlation by roi and subject, then take the overall average within subject, and at the group level.
  2. Separated by 6s event: Correlation between same-trial onsets and offsets.
######### A) #########
## onset N to offset N-1
onset.data.cor <- onset.data
offset.data.cor <- offset.data
trials_keep <- c()
for (s in 1:length(subjects)) {
  for (r in unique(onset.data.cor$Run[onset.data.cor$SubID == subjects[s]])) {
    subData.onset <- subset(onset.data.cor, SubID == subjects[s] & Run == r)
    subData.offset <- subset(offset.data.cor, SubID == subjects[s] & Run == r)
    for (o in 1:nrow(subData.onset)) {
      if ((subData.onset$StudyTrial[o]-1) %in% subData.offset$StudyTrial) {  #if the trial before current onset is present in offset list, keep
        trials_keep <- c(trials_keep, subData.onset$TotalEncodingTrial[o])
      }
    } 
  }
}
onset.data.cor <- onset.data.cor[onset.data.cor$TotalEncodingTrial %in% trials_keep,]
trials_keep <- c()
for (s in 1:length(subjects)) {
  for (r in unique(offset.data.cor$Run[offset.data.cor$SubID == subjects[s]])) {
    subData.onset <- subset(onset.data.cor, SubID == subjects[s] & Run == r)
    subData.offset <- subset(offset.data.cor, SubID == subjects[s] & Run == r)
    for (o in 1:nrow(subData.offset)) {
      if ((subData.offset$StudyTrial[o]+1) %in% subData.onset$StudyTrial) { #if the trial after current offset is present in onset list, keep
        trials_keep <- c(trials_keep, subData.offset$TotalEncodingTrial[o])
      }
    } 
  }
}
offset.data.cor <- offset.data.cor[offset.data.cor$TotalEncodingTrial %in% trials_keep,]
# now run per ROI and subject
correlations <- data.frame(array(0,c((length(subjects)*length(rois)),3)))
colnames(correlations) <- c('SubID','ROI','Z')
row = 0
for (r in 1:length(rois)) {
 for (s in 1:length(subjects)){
   row = row +1
   sub.onset  <- onset.data.cor[onset.data.cor$SubID == subjects[s],colnames(onset.data.cor) == rois[r]]
   sub.offset <- offset.data.cor[offset.data.cor$SubID == subjects[s],colnames(offset.data.cor) == rois[r]]
   correlations$SubID[row] <- subjects[s]
   correlations$ROI[row]   <- rois[r]
   correlations$Z[row]     <- fisherz(cor(sub.onset,sub.offset))
 }  
}
  
sub.summary <- correlations %>% group_by(SubID) %>%
                   summarise(meanZ = mean(Z))
R = fisherz2r(mean(sub.summary$meanZ))
SEmean = se(sub.summary$meanZ)
cat('Mean correlation (r) between onset N and offset N-1 (separated by 1s ITI) within subject and ROI =',R,', SE =',SEmean,'\n')
Mean correlation (r) between onset N and offset N-1 (separated by 1s ITI) within subject and ROI = 0.8802484 , SE = 0.02030568 
######### B) #########
## onset N to offset N
shared.trials <- intersect(onset.data$TotalEncodingTrial,offset.data$TotalEncodingTrial)
onset.data.cor  <- onset.data[onset.data$TotalEncodingTrial %in% shared.trials,]
offset.data.cor <- offset.data[offset.data$TotalEncodingTrial %in% shared.trials,]
# now run per ROI and subject
correlations <- data.frame(array(0,c((length(subjects)*length(rois)),3)))
colnames(correlations) <- c('SubID','ROI','Z')
row = 0
for (r in 1:length(rois)) {
 for (s in 1:length(subjects)){
   row = row +1
   sub.onset  <- onset.data.cor[onset.data.cor$SubID == subjects[s],colnames(onset.data.cor) == rois[r]]
   sub.offset <- offset.data.cor[offset.data.cor$SubID == subjects[s],colnames(offset.data.cor) == rois[r]]
   correlations$SubID[row] <- subjects[s]
   correlations$ROI[row]   <- rois[r]
   correlations$Z[row]     <- fisherz(cor(sub.onset,sub.offset))
 }  
}
  
sub.summary <- correlations %>% group_by(SubID) %>%
                   summarise(meanZ = mean(Z))
R = fisherz2r(mean(sub.summary$meanZ))
SEmean = se(sub.summary$meanZ)
cat('Mean correlation (r) between onset N and offset N (separated by 6s trial) within subject and ROI =',R,', SE =',SEmean,'\n')
Mean correlation (r) between onset N and offset N (separated by 6s trial) within subject and ROI = 0.1379871 , SE = 0.01122709 

Whole brain analyses

Note - each chunk will take a few minutes to run.

Memory detail

Where across the brain does activity at linearly scale with memory quantity (0,1,2,3) in the upcoming trial (using onset) or the preceding trial (using offset)?

curFile = 'memorydetail_effects_onsetoffset.csv'
### set up data frame for mixed effect model results:
mixed.data <- data.frame(array(0,c(length(rois)*2,8)))
colnames(mixed.data) <- c('ROI','Phase','Feature','Beta','SE','T','Df','Pvalue')
row = 0
### loop over onset (~ upcoming event memory) and offset (~ preceding event memory)
for (phase in 1:2) {
  
  # gather data so behavioral variables are replicated over each ROI (now as a single column factor)
  if (phase == 1) {
    model.data <- gather(onset.data, ROI, Beta, rois)
    time = 'Onset'
  } else if (phase == 2) {
    model.data <- gather(offset.data, ROI, Beta, rois)
    time = 'Offset'
  }
  
  # ******** run linear mixed effects model within each ROI and store results **********
  for (r in 1:length(rois)) {
    
    thisROI <- as.character(rois[r])
    
    roiData <- subset(model.data, ROI == thisROI)
    roiData$MemoryDetail <- scale(roiData$MemoryDetail, scale = FALSE) # mean-center memory predictor
    
    # run lmer and get fixed effect stats
    full.model <- suppressMessages(lmer(Beta ~ MemoryDetail + 
                                          (1 + MemoryDetail||SubID), data = roiData))
    stats <- summary(full.model)$coefficients
    nIV <- nrow(stats) - 1 # number of fixed effects, discounting intercept
  
    ### add fixed effects and p values to data frame
    mixed.data$ROI[(row+1):(row+nIV)]      <- thisROI
    mixed.data$Phase[(row+1):(row+nIV)]    <- time
    mixed.data$Feature[(row+1):(row+nIV)]  <- row.names(stats)[2:(nIV+1)]
    mixed.data[(row+1):(row+nIV),c("Beta","SE","T","Df","Pvalue")] <- stats[2:(nIV+1),
                                                                           c("Estimate","Std. Error","t value","df","Pr(>|t|)")]
    row = row + nIV
  }
}
#### FDR-correct all p-values for multiple comparisons
mixed.data$Pfdr <- 1
p.values <- mixed.data$Pvalue
mixed.data$Pfdr <- p.adjust(p.values, method = "fdr")
## save out parameter estimates and p values as csv file for creating a .nii map of results
write.csv(mixed.data, file = curFile, row.names = FALSE)
cat('Results saved in',curFile)
Results saved in memorydetail_effects_onsetoffset.csv

Control analysis 1

This analysis is the same as above, but we are checking that the results look similar if we explicitly include memory detail on trial N-1 or N+1 as a covariate when testing the relationship between onset/offset N and memory detail on trial N.

curFile = 'memorydetail_effects_covariate.csv'
### set up data frame for mixed effect model results:
mixed.data <- data.frame(array(0,c(length(rois)*2,8)))
colnames(mixed.data) <- c('ROI','Phase','Feature','Beta','SE','T','Df','Pvalue')
row = 0
### loop over onset and offset
for (phase in 1:2) {
  
  trials.n <- c()
  trials.n1 <- c()
    
  # gather data so behavioral variables are replicated over each ROI (now as a single column factor)
  if (phase == 1) {
    model.data <- onset.data
    time = 'Onset'
    # now loop through and only retain trial N if trial N-1 (immediately adjacent to onset N) is present (within subject and run)
    for (n in 2:nrow(model.data)) {  #can't include first trial in N vector
      if (model.data$EncodingEvent[n-1] == model.data$EncodingEvent[n]-1) {
           trials.n  <- c(trials.n, n)
           trials.n1 <- c(trials.n1, n-1)
      }
    }
  } else if (phase == 2) {
    model.data <- offset.data
    time = 'Offset'
    # now loop through and only retain trial N if trial N+1 (immediately adjacent to offset N) is present (within subject and run)
    for (n in 1:(nrow(model.data)-1)) {  #can't include final trial N vector
      if (model.data$EncodingEvent[n+1] == model.data$EncodingEvent[n]+1) {
           trials.n  <- c(trials.n, n)
           trials.n1 <- c(trials.n1, n+1)
      }
    }
  }
  
  # format data for MemoryDetail and MemoryDetailN1 predictors
  new.data    <- model.data[trials.n,]
  new.data.n1 <- model.data[trials.n1,]
  new.data$MemoryDetailN1  <- new.data.n1$MemoryDetail
  model.data <- gather(new.data, ROI, Beta, rois)
  
  # ******** run linear mixed effects within each ROI and store results **********
  for (r in 1:length(rois)) {
    
    thisROI <- as.character(rois[r])
    
    roiData <- subset(model.data, ROI == thisROI)
    roiData$MemoryDetail   <- scale(roiData$MemoryDetail, scale = FALSE) # mean-center predictor
    roiData$MemoryDetailN1 <- scale(roiData$MemoryDetailN1, scale = FALSE) # mean-center predictor
   
    # run lmer and get fixed effect stats
    full.model <- suppressMessages(lmer(Beta ~ MemoryDetail + MemoryDetailN1 +
                                          (1 + MemoryDetail + MemoryDetailN1||SubID), data = roiData))
    stats <- summary(full.model)$coefficients
    nIV <- nrow(stats) - 2 # number of fixed effect predictors - but don't need to store covariate results or intercept
  
    ### add fixed effects and p values to data matrix
    mixed.data$ROI[(row+1):(row+nIV)]      <- thisROI
    mixed.data$Phase[(row+1):(row+nIV)]    <- time
    mixed.data$Feature[(row+1):(row+nIV)]  <- row.names(stats)[2:(nIV+1)]
    mixed.data[(row+1):(row+nIV),c("Beta","SE","T","Df","Pvalue")] <- stats[2:(nIV+1),
                                                                           c("Estimate","Std. Error","t value","df","Pr(>|t|)")]
    row = row + nIV
  }
}
#### FDR-correct all p-values for multiple comparisons
mixed.data$Pfdr <- 1
p.values <- mixed.data$Pvalue
mixed.data$Pfdr <- p.adjust(p.values, method = "fdr")
## save out parameter estimates and p values as csv file for creating a .nii map of results
write.csv(mixed.data, file = curFile, row.names = FALSE)
cat('Results saved in',curFile)
Results saved in memorydetail_effects_covariate.csv

Control analysis 2

Instead of modeling time points at the beginning and end of each event, this analysis models each event (from onset to offset) as a 6s epoch.

############################# single trial activity data ##############################
# get subjects' data
subjects <- list.files(path='../data/single-trial-betas/6s-event/',full.names=TRUE, recursive = TRUE, pattern = 'epoch-wholebrain')
allData  = do.call(rbind, lapply(subjects, function(x) {read.csv(x, header = TRUE)}))
allData$SubID=as.factor(allData$SubID)
allData$ROI <- as.factor(allData$ROI)
allData <- allData[,-c(5)] # remove vox numbers - not needed here
rois = levels(allData$ROI)
cat('Number of subjects =', length(unique(allData$SubID)),'\n')
Number of subjects = 27 
cat('Removing influential event betas, |z| >', outlier_value,'\n')
Removing influential event betas, |z| > 4 
# ******************************************************** #
### add z score of betas within region and subject to remove outliers:
allData <- allData %>%
               group_by(SubID, ROI) %>%
               mutate(Z = zscore(MeanBeta))
# remove betas that are > XSD -- print this number
allData.clean <- allData
allData.clean$MeanBeta[abs(allData.clean$Z) > outlier_value] <- NA  #mark beta as NA if outlier
allData.clean <- allData.clean[,-c(6)] # remove Z values  
##########################################################
#spread data so each of the 204 ROIs is in its own column (to align with behavioral data)
myBetas = data.frame(spread(allData.clean, ROI, MeanBeta))
myBetas$TotalEncodingTrial <- 1:nTotal
# remove all trials where there is at least one NA (at least one of the ROIs is an outlier on that trial) - 
# to keep the trials analyzed the same across regions:
idxBetas <- complete.cases(myBetas)
myBetas.clean <- myBetas[idxBetas,]
cat('Total number of events removed =',sum(!idxBetas),'out of',nTotal,'\n\n')
Total number of events removed = 59 out of 3792 
##### merge cleaned betas with behavioral data, removing corresponding behavioral trials:
behavData.clean <- behavData[idxBetas,]
testData <- merge.data.frame(myBetas.clean,behavData.clean,by="TotalEncodingTrial")
# remove duplicate columns for subject, run, and study trial
testData <- testData[,-c(2,3,4)]
colnames(testData)[colnames(testData) == 'SubID.y'] <- 'SubID'
colnames(testData)[colnames(testData) == 'Run.y'] <- 'Run'
subjects <- unique(testData$SubID)
epoch.data  <- testData  #store event betas and behavioral data for current trial
cat('Cleaned and merged event betas and behavioral trials\n')
Cleaned and merged event betas and behavioral trials
curFile = 'memorydetail_effects_eventepoch.csv'
### set up data frame for mixed effect model results:
mixed.data <- data.frame(array(0,c(length(rois),8)))
colnames(mixed.data) <- c('ROI','Phase','Feature','Beta','SE','T','Df','Pvalue')
row = 0
model.data <- gather(epoch.data, ROI, Beta, rois)
time = 'Epoch'
# ******** run linear mixed effects within each ROI and store results **********
for (r in 1:length(rois)) {
  
  thisROI <- as.character(rois[r])
  
  roiData <- subset(model.data, ROI == thisROI)
  roiData$MemoryDetail <- scale(roiData$MemoryDetail, scale = FALSE) # mean-center predictor
  
  # run lmer and get fixed effect stats
  full.model <- suppressMessages(lmer(Beta ~ MemoryDetail + 
                                        (1 + MemoryDetail||SubID), data = roiData))
  stats <- summary(full.model)$coefficients
  nIV <- nrow(stats) - 1 # number of fixed effects, discounting intercept
    
  ### add fixed effects and p values to data matrix
  mixed.data$ROI[(row+1):(row+nIV)]      <- thisROI
  mixed.data$Phase[(row+1):(row+nIV)]    <- time
  mixed.data$Feature[(row+1):(row+nIV)]  <- row.names(stats)[2:(nIV+1)]
  mixed.data[(row+1):(row+nIV),c("Beta","SE","T","Df","Pvalue")] <- stats[2:(nIV+1),
                                                                         c("Estimate","Std. Error","t value","df","Pr(>|t|)")]
  row = row + nIV
}
#### FDR-correct all p-values for multiple comparisons
mixed.data$Pfdr <- 1
p.values <- mixed.data$Pvalue
mixed.data$Pfdr <- p.adjust(p.values, method = "fdr")
## save out parameter estimates and p values as csv file for creating a .nii map of results
write.csv(mixed.data, file = curFile, row.names = FALSE)
cat('Results saved in',curFile)
Results saved in memorydetail_effects_eventepoch.csv

Control analysis 3

Here, we demonstrate the similarity of our onset- and offset-based analyses to a model where we disregard any distinction between correlated offset N and onset N+1 signals, modeling them together as a single 1s ITI. ITI activity is predicted by subsequent memory for the upcoming or preceding event.

############################# single trial activity data ##############################
# get subjects' data
subjects <- list.files(path='../data/single-trial-betas/1s-ITI/',full.names=TRUE, recursive = TRUE, pattern = 'ITI-wholebrain')
allData  = do.call(rbind, lapply(subjects, function(x) {read.csv(x, header = TRUE)}))
allData$SubID=as.factor(allData$SubID)
allData$ROI <- as.factor(allData$ROI)
allData <- allData[,-c(5)] # remove vox numbers - not needed here
rois = levels(allData$ROI)
cat('Number of subjects =', length(unique(allData$SubID)),'\n')
Number of subjects = 27 
cat('Removing influential ITI betas, |z| >', outlier_value,'\n')
Removing influential ITI betas, |z| > 4 
# ******************************************************** #
### add z score of betas within region and subject to remove outlier boundaries:
allData <- allData %>%
               group_by(SubID, ROI) %>%
               mutate(Z = zscore(MeanBeta))
# remove betas that are > XSD -- print this number
allData.clean <- allData
allData.clean$MeanBeta[abs(allData.clean$Z) > outlier_value] <- NA  #mark beta as NA if outlier
allData.clean <- allData.clean[,-c(6)] # remove Z values
##########################################################
#spread data so each of the 204 ROIs is in its own column
myBetas = data.frame(spread(allData.clean, ROI, MeanBeta))
myBetas$TotalEvent <- 1:nrow(myBetas)
forwardBehav <- behavData[behavData$EncodingEvent > 1,]  #if predicting forward from ITI, we can use all but behavioral trial 1 (first trial per run)
forwardBehav$TotalEvent <- 1:nrow(forwardBehav)
backwardBehav <- behavData[behavData$EncodingEvent < 24,] #if predicting backward from ITI, we can use all but behavioral trial 24 (final trial per run)
backwardBehav$TotalEvent <- 1:nrow(backwardBehav)
### CLEAN DATA and merge with behavior
# remove all trials where there is at least one NA (at least one of the ROIs is an outlier on that trial):
idxBetas <- complete.cases(myBetas)
myBetas.clean <- myBetas[idxBetas,]
forwardBehav.clean <- forwardBehav[idxBetas,]
backwardBehav.clean <- backwardBehav[idxBetas,]
cat('Total number of ITIs removed =',nrow(myBetas)-nrow(myBetas.clean),'out of',nrow(myBetas),'\n\n')
Total number of ITIs removed = 98 out of 3634 
forward.data <- merge.data.frame(myBetas.clean,forwardBehav.clean,by="TotalEvent")
backward.data <- merge.data.frame(myBetas.clean,backwardBehav.clean,by="TotalEvent")
# remove duplicate columns for subject, study trial, and run ----------
forward.data <- forward.data[,-c(2,3,4)]
colnames(forward.data)[colnames(forward.data) == 'SubID.y'] <- 'SubID'
colnames(forward.data)[colnames(forward.data) == 'Run.y'] <- 'Run'
backward.data <- backward.data[,-c(2,3,4)]
colnames(backward.data)[colnames(backward.data) == 'SubID.y'] <- 'SubID'
colnames(backward.data)[colnames(backward.data) == 'Run.y'] <- 'Run'
subjects <- unique(forward.data$SubID)
cat('Cleaned and merged ITI betas and behavioral trials\n')
Cleaned and merged ITI betas and behavioral trials
curFile = 'memorydetail_effects_ITI.csv'
### set up data frame for mixed effect model results:
mixed.data <- data.frame(array(0,c(length(rois)*2,8)))
colnames(mixed.data) <- c('ROI','Phase','Feature','Beta','SE','T','Df','Pvalue')
row = 0
### loop over onset and offset
for (phase in 1:2) {
  
  # gather data so behavioral variables are replicated over each ROI (now as a single column factor)
  if (phase == 1) {
    model.data <- gather(forward.data, ROI, Beta, rois)
    time = 'Upcoming'
  } else if (phase == 2) {
    model.data <- gather(backward.data, ROI, Beta, rois)
    time = 'Preceding'
  }
  
  # ******** run linear mixed effects within each ROI and store results **********
  for (r in 1:length(rois)) {
    
    thisROI <- as.character(rois[r])
    
    roiData <- subset(model.data, ROI == thisROI)
    roiData$MemoryDetail <- scale(roiData$MemoryDetail, scale = FALSE) # mean-center predictor
    
    # run lmer and get fixed effect stats
    full.model <- suppressMessages(lmer(Beta ~ MemoryDetail + 
                                          (1 + MemoryDetail||SubID), data = roiData))
    stats <- summary(full.model)$coefficients
    nIV <- nrow(stats) - 1 # number of fixed effects, discounting intercept
    
    ### add fixed effects and p values to data matrix
    mixed.data$ROI[(row+1):(row+nIV)]      <- thisROI
    mixed.data$Phase[(row+1):(row+nIV)]    <- time
    mixed.data$Feature[(row+1):(row+nIV)]  <- row.names(stats)[2:(nIV+1)]
    mixed.data[(row+1):(row+nIV),c("Beta","SE","T","Df","Pvalue")] <- stats[2:(nIV+1),
                                                                             c("Estimate","Std. Error","t value","df","Pr(>|t|)")]
    row = row + nIV
  }
}
#### FDR-correct p-values for multiple comparisons
mixed.data$Pfdr <- 1
p.values <- mixed.data$Pvalue
mixed.data$Pfdr <- p.adjust(p.values, method = "fdr")
## save out parameter estimates and p values as csv file for creating a .nii map of results
write.csv(mixed.data, file = curFile, row.names = FALSE)
cat('Results saved in',curFile)
Results saved in memorydetail_effects_ITI.csv

Unique feature encoding

After accounting for covariance with the other two features in the model, where is onset/offset activity uniquely predicted by successful color, scene, or sound encoding?

curFile = 'feature_effects_onsetoffset.csv'
### set up data frame for mixed effect model results:
mixed.data <- data.frame(array(0,c(length(rois)*6,8)))  # *6 (3 features x 2 times)
colnames(mixed.data) <- c('ROI','Phase','Feature','Beta','SE','T','Df','Pvalue')
row = 0
### loop over onset and offset
for (phase in 1:2) {
  
  # gather data so behavioral variables are replicated over each ROI (now as a single column factor)
  if (phase == 1) {
    model.data <- gather(onset.data, ROI, Beta, rois)
    time = 'Onset'
  } else if (phase == 2) {
    model.data <- gather(offset.data, ROI, Beta, rois)
    time = 'Offset'
  }
  
  # ******** run linear mixed effects within each ROI and store results for each feature fixed effect **********
  for (r in 1:length(rois)) {
    
    thisROI <- as.character(rois[r])
    roiData <- subset(model.data, ROI == thisROI)
    roiData$Sound  <- scale(roiData$Sound, scale = FALSE) # mean-center
    roiData$Color  <- scale(roiData$Color, scale = FALSE) # mean-center
    roiData$Scene  <- scale(roiData$Scene, scale = FALSE) # mean-center
    
    # run lmer, simplify random effect structure as > 2 fixed effects, and get fixed effect stats
    full.model <- suppressMessages(suppressWarnings(lmer(Beta ~ Sound + Color + Scene + 
                                                           (1 + Sound + Color + Scene||SubID), data = roiData)))
    full.model <- suppressMessages(suppressWarnings(get_model(step(full.model, 
                                                                   reduce.random = TRUE, reduce.fixed = FALSE))))
    stats <- summary(full.model)$coefficients
    nIV <- nrow(stats) - 1 # number of fixed effects, discounting intercept
    
    ### add fixed effects and p values to data matrix
    mixed.data$ROI[(row+1):(row+nIV)]      <- thisROI
    mixed.data$Phase[(row+1):(row+nIV)]    <- time
    mixed.data$Feature[(row+1):(row+nIV)]  <- row.names(stats)[2:(nIV+1)]
    mixed.data[(row+1):(row+nIV),c("Beta","SE","T","Df","Pvalue")] <- stats[2:(nIV+1),
                                                                           c("Estimate","Std. Error","t value","df","Pr(>|t|)")]
    row = row + nIV
  }
}
#### FDR-correct p-values for multiple comparisons
mixed.data$Pfdr <- 1
p.values <- mixed.data$Pvalue
mixed.data$Pfdr <- p.adjust(p.values, method = "fdr")
## save out parameter estimates and p values as csv file for creating a .nii map of results
write.csv(mixed.data, file = curFile, row.names = FALSE)
cat('Results saved in',curFile)
Results saved in feature_effects_onsetoffset.csv

Control analysis - event epoch

Like for overall memory detail, here we are predicting activity through the entire 6s trial (onset to offset) with subsequent memory for each feature.

curFile = 'feature_effects_eventepoch.csv'
### set up data frame for mixed effect model results:
mixed.data <- data.frame(array(0,c(length(rois)*3,8)))  # *3 features
colnames(mixed.data) <- c('ROI','Phase','Feature','Beta','SE','T','Df','Pvalue')
row = 0
model.data <- gather(epoch.data, ROI, Beta, rois)
time = 'Epoch'
# ******** run linear mixed effects within each ROI and store results for each feature fixed effect **********
for (r in 1:length(rois)) {
  
  thisROI <- as.character(rois[r])
  
  roiData <- subset(model.data, ROI == thisROI)
  roiData$Sound  <- scale(roiData$Sound, scale = FALSE) # mean-center
  roiData$Color  <- scale(roiData$Color, scale = FALSE) # mean-center
  roiData$Scene  <- scale(roiData$Scene, scale = FALSE) # mean-center
  
  # run lmer, simplify random effect structure as > 2 fixed effects, and get fixed effect stats
  full.model <- suppressMessages(suppressWarnings(lmer(Beta ~ Sound + Color + Scene + 
                                                         (1 + Sound + Color + Scene||SubID), data = roiData)))
  full.model <- suppressMessages(suppressWarnings(get_model(step(full.model, 
                                                                 reduce.random = TRUE, reduce.fixed = FALSE))))
  stats <- summary(full.model)$coefficients
  nIV <- nrow(stats) - 1 # number of fixed effects, discounting intercept
  
  ### add fixed effects and p values to data matrix
  mixed.data$ROI[(row+1):(row+nIV)]      <- thisROI
  mixed.data$Phase[(row+1):(row+nIV)]    <- time
  mixed.data$Feature[(row+1):(row+nIV)]  <- row.names(stats)[2:(nIV+1)]
  mixed.data[(row+1):(row+nIV),c("Beta","SE","T","Df","Pvalue")] <- stats[2:(nIV+1),
                                                                          c("Estimate","Std. Error","t value","df","Pr(>|t|)")]
  row = row + nIV
}
#### FDR-correct all p-values for multiple comparisons
mixed.data$Pfdr <- 1
p.values <- mixed.data$Pvalue
mixed.data$Pfdr <- p.adjust(p.values, method = "fdr")
## save out parameter estimates and p values as csv file for creating a .nii map of results
write.csv(mixed.data, file = curFile, row.names = FALSE)
cat('Results saved in',curFile)
Results saved in feature_effects_eventepoch.csv

ROI visualization

Define regions and data

Note, here we are predicting memory on trial N from onset N and offset N (in the same model) to test and contrast unique variance of fixed effects

############ define rois ##############
# 1. Significant offset memory detail response (activity relates to memory for preceding event)
HIPP    <- c('LH_HIPP')
# 3. Significant onset memory detail response (activity relates to memory for upcoming event)
IFG   <- c('LH_ContA_PFCl_2','LH_DefaultB_PFCv_4','LH_ContA_PFCl_1')
# 2. Significant feature onset effects (activity relates to memory for upcoming event)
VTC   <- c('RH_DorsAttnA_TempOcc_1','RH_LimbicA_TempPole_4','RH_VisCent_ExStr_2')  # Color
AUD   <- c('RH_SomMotB_Aud_1','LH_SomMotB_Aud_1','RH_SomMotB_Aud_2','LH_SomMotB_Aud_2')  # Sound
MTL   <- c('RH_DefaultC_PHC_1','LH_DefaultC_PHC_1','RH_DefaultC_Rsp_1','LH_DefaultC_Rsp_1')  # Scene
myRois <- c(HIPP,IFG,VTC,AUD,MTL)
newRois <- c('HIPP','IFG','VTC','AUD','MTL')
mycolors <- c("#7c67a2","#ff9000","#F64F59","#1D976C","#009FFF")
cat('Regions analyzed:\n\n',
    'HIPP:',HIPP,'\n',
    'IFG:',IFG,'\n',
    'VTC:',VTC,'\n',
    'AUD:',AUD,'\n',
    'MTL:',MTL,'\n')
Regions analyzed:

 HIPP: LH_HIPP 
 IFG: LH_ContA_PFCl_2 LH_DefaultB_PFCv_4 LH_ContA_PFCl_1 
 VTC: RH_DorsAttnA_TempOcc_1 RH_LimbicA_TempPole_4 RH_VisCent_ExStr_2 
 AUD: RH_SomMotB_Aud_1 LH_SomMotB_Aud_1 RH_SomMotB_Aud_2 LH_SomMotB_Aud_2 
 MTL: RH_DefaultC_PHC_1 LH_DefaultC_PHC_1 RH_DefaultC_Rsp_1 LH_DefaultC_Rsp_1 
### make sure each trial to-be-modeled has an associated onset and offset beta
matching_trials <- intersect(onset.data$TotalEncodingTrial,offset.data$TotalEncodingTrial)
onset.data.roi  <- onset.data[onset.data$TotalEncodingTrial %in% matching_trials,]
offset.data.roi <- offset.data[offset.data$TotalEncodingTrial %in% matching_trials,]
behav.roi <- onset.data.roi[,206:244] #grab behavioral data for all trials with valid onsets & offsets
########## average betas within these new ROIs ###########
##### onset data for rois
onset.data.roi <- gather(onset.data.roi, ROI, Beta, myRois)
nleft <- length(rois) - length(myRois) + 1
onset.data.roi <- onset.data.roi[,-c(1:nleft)] #remove other regions
### allocate 'new' ROI
onset.data.roi$NewROI[onset.data.roi$ROI %in% HIPP] <- 'HIPP'
onset.data.roi$NewROI[onset.data.roi$ROI %in% IFG] <- 'IFG'
onset.data.roi$NewROI[onset.data.roi$ROI %in% VTC] <- 'VTC'
onset.data.roi$NewROI[onset.data.roi$ROI %in% AUD] <- 'AUD'
onset.data.roi$NewROI[onset.data.roi$ROI %in% MTL] <- 'MTL'
### now summarise activity within these new ROIs per trial
model.data <- onset.data.roi
grouped.data <- model.data %>% group_by(SubID,StudyTrial,NewROI) %>%
                                 summarise(NewBeta = mean(Beta))
grouped.data <- spread(grouped.data, NewROI, NewBeta)
myROIData <- merge(grouped.data,behav.roi)
model.data <- gather(myROIData, ROI, Beta, AUD:VTC)
model.data.onset <- model.data
##### offset data for rois
offset.data.roi <- gather(offset.data.roi, ROI, Beta, myRois)
nleft <- length(rois) - length(myRois) + 1
offset.data.roi <- offset.data.roi[,-c(1:nleft)]
### allocate 'new' ROI
offset.data.roi$NewROI[offset.data.roi$ROI %in% HIPP] <- 'HIPP'
offset.data.roi$NewROI[offset.data.roi$ROI %in% IFG] <- 'IFG'
offset.data.roi$NewROI[offset.data.roi$ROI %in% VTC] <- 'VTC'
offset.data.roi$NewROI[offset.data.roi$ROI %in% AUD] <- 'AUD'
offset.data.roi$NewROI[offset.data.roi$ROI %in% MTL] <- 'MTL'
### now summarise within these new ROIs
model.data <- offset.data.roi
grouped.data <- model.data %>% group_by(SubID,StudyTrial,NewROI) %>%
                                 summarise(NewBeta = mean(Beta))
grouped.data <- spread(grouped.data, NewROI, NewBeta)
myROIData <- merge(grouped.data,behav.roi)
model.data <- gather(myROIData, ROI, Beta, AUD:VTC)
model.data.offset <- model.data
###### combined onset and offset data
model.data.onset$Phase <- 'Onset'
model.data.offset$Phase <- 'Offset'
model.data <- rbind(model.data.onset,model.data.offset)

Onset/Offset subsequent memory

Now we are predicting memory detail or feature memory on trial N with onset N and offset N regressors to get unique variance and to run offset - onset contrast per ROI

####################################
mixed.data <- data.frame(array(0,c(length(newRois)*2,7)))
colnames(mixed.data) <- c('ROI','Feature','Beta','SE','T','Df','Pvalue')
row = 0
for (r in 1:length(newRois)) {
  
  this.roi <- newRois[r]
  
  roiData <- subset(model.data, ROI == this.roi)
  roiData <- spread(roiData, Phase, Beta)  #make onset and offset activity different columns
  roiData$Onset  <- scale(roiData$Onset, scale = TRUE) # z-score for direct comparison of betas
  roiData$Offset <- scale(roiData$Offset, scale = TRUE) # z-score for direct comparison of betas
  
  if (this.roi == 'HIPP' | this.roi == 'IFG') {  # Memory Detail
       roiData$MemoryDetail <- scale(roiData$MemoryDetail, scale = TRUE) # z-score for direct visual comparison across features
       # run lmer, simplify random effect structure, and get fixed effect stats
       full.model <- suppressMessages(lmer(MemoryDetail ~ Onset + Offset + 
                                                     (1 + Onset + Offset||SubID), data = roiData))
       
  } else if (this.roi == 'VTC') {  # Color
       roiData$Color <- scale(roiData$Color, scale = TRUE) # z-score for direct visual comparison across features
       # run lmer, simplify random effect structure, and get fixed effect stats
       full.model <- suppressMessages(lmer(Color ~ Onset + Offset + 
                                              (1 + Onset + Offset||SubID), data = roiData))
  } else if (this.roi == 'AUD') {  # Sound
       roiData$Sound <- scale(roiData$Sound, scale = TRUE) # z-score for direct visual comparison across features
       # run lmer, simplify random effect structure, and get fixed effect stats
       full.model <- suppressMessages(lmer(Sound ~ Onset + Offset + 
                                              (1 + Onset + Offset||SubID), data = roiData))
  } else if (this.roi == 'MTL') {  # Scene
       roiData$Scene <- scale(roiData$Scene, scale = TRUE) # z-score for direct visual comparison across features
       # run lmer, simplify random effect structure, and get fixed effect stats
       full.model <- suppressMessages(lmer(Scene ~ Onset + Offset + 
                                              (1 + Onset + Offset||SubID), data = roiData))
  }
  stats <- summary(full.model)$coefficients
  nIV <- nrow(stats) - 1 # number of fixed effects, discounting intercept
    
  # one-sample t-test (first regressor is intercept) and effect size
  con <- contest(full.model, c(0,-1,1), joint = FALSE, ddf = "Satterthwaite")
  print(kable(con, 
              format="pandoc", caption=paste(this.roi,': offset - onset',sep="")))
  ### add fixed effects and p values to data matrix
  mixed.data$ROI[(row+1):(row+nIV)]      <- as.character(this.roi)
  mixed.data$Feature[(row+1):(row+nIV)]  <- row.names(stats)[2:(nIV+1)]
  mixed.data[(row+1):(row+nIV),c("Beta","SE","T","Df","Pvalue")] <- stats[2:(nIV+1),
                                                                         c("Estimate","Std. Error","t value","df","Pr(>|t|)")]
  row = row + nIV
}


Table: HIPP: offset - onset

  Estimate   Std. Error         df    t value       lower       upper    Pr(>|t|)
----------  -----------  ---------  ---------  ----------  ----------  ----------
 0.0778628    0.0314208   52.53739   2.478067   0.0148277   0.1408979   0.0164591


Table: IFG: offset - onset

  Estimate   Std. Error         df     t value        lower        upper    Pr(>|t|)
----------  -----------  ---------  ----------  -----------  -----------  ----------
 -0.069933    0.0289432   64.63027   -2.416221   -0.1277428   -0.0121233   0.0185171


Table: VTC: offset - onset

   Estimate   Std. Error         df     t value        lower       upper    Pr(>|t|)
-----------  -----------  ---------  ----------  -----------  ----------  ----------
 -0.0342065    0.0302195   63.75277   -1.131933   -0.0945814   0.0261684   0.2619021


Table: AUD: offset - onset

   Estimate   Std. Error         df     t value        lower        upper    Pr(>|t|)
-----------  -----------  ---------  ----------  -----------  -----------  ----------
 -0.1526207    0.0316429   49.82872   -4.823219   -0.2161827   -0.0890586   0.0000137


Table: MTL: offset - onset

   Estimate   Std. Error         df     t value        lower        upper   Pr(>|t|)
-----------  -----------  ---------  ----------  -----------  -----------  ---------
 -0.1021936    0.0298283   64.05106   -3.426061   -0.1617816   -0.0426056   0.001074
# order factors for plotting
mixed.data$ROI     <- factor(mixed.data$ROI, levels=newRois)
mixed.data$Feature <- factor(mixed.data$Feature, levels=c('Onset','Offset'))
### print full output:
print(kable(mixed.data,format="pandoc",caption = 'Memory onset and offset fixed effects for ROIs'))


Table: Memory onset and offset fixed effects for ROIs

ROI    Feature          Beta          SE            T         Df      Pvalue
-----  --------  -----------  ----------  -----------  ---------  ----------
HIPP   Onset      -0.0065245   0.0244801   -0.2665232   24.62175   0.7920557
HIPP   Offset      0.0713383   0.0183772    3.8818908   25.48367   0.0006532
IFG    Onset       0.0916657   0.0208859    4.3888752   28.30980   0.0001441
IFG    Offset      0.0217326   0.0184680    1.1767737   28.28564   0.2490942
VTC    Onset       0.0696376   0.0192857    3.6108400   26.60833   0.0012466
VTC    Offset      0.0354312   0.0211580    1.6746018   25.15619   0.1064018
AUD    Onset       0.1247330   0.0254266    4.9056194   26.17756   0.0000424
AUD    Offset     -0.0278876   0.0167154   -1.6683770   26.59410   0.1069743
MTL    Onset       0.1036571   0.0189948    5.4571405   28.65378   0.0000074
MTL    Offset      0.0014635   0.0211917    0.0690607   25.07645   0.9454891
###### plot
ggplot(mixed.data, aes(x=ROI, y=Beta, fill=Feature)) +
     geom_hline(yintercept=0,size=1) +
     geom_bar(stat = "identity", alpha = 0.75, size = 1, width = 0.75,
              color = "black", position = position_dodge(1)) +
     scale_fill_manual(values = c("#BFBFBF","#404040")) +
     ggtitle("Unique effects of pre- and post-event activity\non subsequent memory") +
     geom_errorbar(aes(ymin = Beta - SE, ymax = Beta + SE), width = 0.4, size = 1,
                   color = "black", position = position_dodge(1)) +
     theme(plot.title = element_text(hjust = 0.5, size=28, margin=margin(0,0,20,0)),
          axis.line.x = element_line(color = "black", size = 1), axis.line.y = element_line(color = "black", size = 1),
          axis.text = element_text(size = 22), axis.title.y = element_text(size = 26),
          axis.title.x = element_blank(), panel.background = element_blank(),
          text = element_text(family="Helvetica"), legend.title = element_blank(),
          plot.margin = margin(1, 1, 1, 1, "cm"))

#ggsave('Rois.png',plot=last_plot(),width=11,height=5,units="in",dpi=500) 

Modulation of onset/offset relationship by memory detail

Here, we are testing if the relationship between onset activity of IFG/VTC/AUD/PHC-RSC and HIPP offset activity is modulated by subsequent memory detail (strongest synchrony if more features recalled?)

####################################
mixed.data <- data.frame(array(0,c(4*3,7)))
colnames(mixed.data) <- c('ROI','Predictor','Beta','SE','T','Df','Pvalue')
row = 0
new.df <- model.data[model.data$ROI == 'HIPP' & model.data$Phase == 'Offset',c("SubID","Beta","MemoryDetail")]
colnames(new.df)[2] <- 'HIPP'
new.df$IFG <- model.data[model.data$ROI == 'IFG' & model.data$Phase == 'Onset',c("Beta")]
new.df$VTC <- model.data[model.data$ROI == 'VTC' & model.data$Phase == 'Onset',c("Beta")]
new.df$AUD <- model.data[model.data$ROI == 'AUD' & model.data$Phase == 'Onset',c("Beta")]
new.df$MTL <- model.data[model.data$ROI == 'MTL' & model.data$Phase == 'Onset',c("Beta")]
new.df[,2:7] <- scale(new.df[,2:7], scale = TRUE) # z score all variables
for (r in 1:(length(newRois)-1)) {
  
  if (r == 1) {  # IFG
       full.model <- suppressMessages(lmer(HIPP ~ IFG*MemoryDetail + 
                                             (1 + IFG*MemoryDetail||SubID), data = new.df))
  } else if (r == 2) {  # VTC
       full.model <- suppressMessages(lmer(HIPP ~ VTC*MemoryDetail + 
                                             (1 + VTC*MemoryDetail||SubID), data = new.df))
  } else if (r == 3) {  # AUD
       full.model <- suppressMessages(lmer(HIPP ~ AUD*MemoryDetail + 
                                             (1 + AUD*MemoryDetail||SubID), data = new.df))
  } else if (r == 4) {  # MTL
       full.model <- suppressMessages(lmer(HIPP ~ MTL*MemoryDetail + 
                                             (1 + MTL*MemoryDetail||SubID), data = new.df))
  }
  stats <- summary(full.model)$coefficients
  nIV <- nrow(stats) - 1 #removing intercept
  ### add fixed effects and p values to data matrix
  mixed.data$ROI[(row+1):(row+nIV)]        <- as.character(newRois[r+1])
  mixed.data$Predictor[(row+1):(row+nIV)]  <- row.names(stats)[2:(nIV+1)]
  mixed.data[(row+1):(row+nIV),c("Beta","SE","T","Df","Pvalue")] <- stats[2:(nIV+1),
                                                                          c("Estimate","Std. Error","t value","df","Pr(>|t|)")]
  row = row + nIV
}
### print full output:
print(kable(mixed.data,format="pandoc",caption = 'Hipp Offset ~ [ROI] Onset * Memory Detail'))


Table: Hipp Offset ~ [ROI] Onset * Memory Detail

ROI   Predictor                 Beta          SE            T           Df      Pvalue
----  -----------------  -----------  ----------  -----------  -----------  ----------
IFG   IFG                  0.1496640   0.0223252    6.7038143     22.37516   0.0000009
IFG   MemoryDetail         0.0679213   0.0231550    2.9333270     22.82346   0.0075071
IFG   IFG:MemoryDetail    -0.0080134   0.0178003   -0.4501845     24.11499   0.6565985
VTC   VTC                  0.0959211   0.0241919    3.9650043     21.03917   0.0007043
VTC   MemoryDetail         0.0728899   0.0235342    3.0971874     23.11032   0.0050628
VTC   VTC:MemoryDetail    -0.0062203   0.0178057   -0.3493454     15.99040   0.7313882
AUD   AUD                  0.1133499   0.0226320    5.0083941     23.36767   0.0000437
AUD   MemoryDetail         0.0724787   0.0233296    3.1067301     23.42577   0.0048983
AUD   AUD:MemoryDetail     0.0110593   0.0168390    0.6567632   1422.24848   0.5114394
MTL   MTL                  0.1210409   0.0258302    4.6860189     24.47365   0.0000882
MTL   MemoryDetail         0.0700037   0.0219351    3.1914049     21.66850   0.0042720
MTL   MTL:MemoryDetail     0.0045381   0.0191412    0.2370848     19.51902   0.8150615

Hippocampal feature combinations

Which specific feature combinations is hippocampus sensitive to? Here, we are predicting L_HIPP onset or offset activity with binary regressors capturing each possible combination of features recalled.

my.plots = list()
plot.max = c(0.6, 1)
c = 0
for (this.roi in c('HIPP')) {
####################################
mixed.data <- data.frame(array(0,c(7*2,7)))
colnames(mixed.data) <- c('Feature','Beta','SE','T','Df','Pvalue','Phase')
row=0 
### loop over onset and offset
for (phase in 1:2) {
  
  # gather data so behavioral variables are replicated over each ROI (now as a single column factor)
  if (phase == 1) {
    roiData <- subset(model.data.onset, ROI == this.roi)
    time = 'Onset'
  } else if (phase == 2) {
    roiData <- subset(model.data.offset, ROI == this.roi)
    time = 'Offset'
  }
  
  # NOTE. not mean-centering here as zero is meaningful -- excluding trials in other conditions. Implicit baseline = none recalled
  full.model <- suppressMessages(suppressWarnings(lmer(Beta ~ Color.Only + Sound.Only + Scene.Only +
                                                              Color.Scene + Sound.Color + Scene.Sound +
                                                              Color.Scene.Sound +
                                                         (1 + Color.Only + Sound.Only + Scene.Only +
                                                              Color.Scene + Sound.Color + Scene.Sound +
                                                              Color.Scene.Sound||SubID), 
                                                       data = roiData)))
  full.model <- suppressMessages(suppressWarnings(get_model(step(full.model, 
                                                                 reduce.random = TRUE, reduce.fixed = FALSE))))
  stats <- summary(full.model)$coefficients
  nIV <- nrow(stats) - 1 # number of fixed effects, discounting intercept
  
  # test if scene binding is > recalling scene alone (first position is intercept)
  con <- contest(full.model, c(0,0,0,-1,(1/3),0,(1/3),(1/3)), joint = FALSE, ddf = "Satterthwaite")
  print(kable(con,
              format="pandoc", caption=paste(this.roi,': Scene Binding vs. Scene Alone at ',time,sep="")))
  # test if scene binding is > recalling color and sound without scene (first position is intercept)
  con <- contest(full.model, c(0,0,0,0,(1/3),-1,(1/3),(1/3)), joint = FALSE, ddf = "Satterthwaite")
  print(kable(con,
              format="pandoc", caption=paste(this.roi,': Scene Binding vs. Color&Sound at ',time,sep="")))
  
  ### add fixed effects and p values to data matrix    
  mixed.data$Phase[(row+1):(row+nIV)]    <- time
  mixed.data$Feature[(row+1):(row+nIV)]  <- row.names(stats)[2:(nIV+1)]
  mixed.data[(row+1):(row+nIV),c("Beta","SE","T","Df","Pvalue")] <- stats[2:(nIV+1),
                                                                          c("Estimate","Std. Error","t value","df","Pr(>|t|)")]
  row = row + nIV
}
##### PLOT
order <- c("Color.Only","Scene.Only","Sound.Only","Sound.Color","Color.Scene","Scene.Sound","Color.Scene.Sound")
# colors for venn diagram space feature combinations
barcolors <- c("#f76c67","#67aaf7","#0bca81","#f6a84f","#9c67f7","#67f7f0","#7F7F7F")
mixed.data$Feature <- factor(mixed.data$Feature, levels=order)
mixed.data$Phase <- factor(mixed.data$Phase, levels=c('Onset','Offset'))
c = c+1
my.plots[[c]] <- ggplot(mixed.data, aes(x=Feature, y=Beta, fill=Feature)) +
  facet_grid(. ~ Phase) +
  geom_hline(yintercept=0,size=1) +
  geom_bar(stat = "identity", alpha = 0.80, size = 1, width = 0.75,
           color = "black", position = position_dodge(1)) +
  scale_fill_manual(values = barcolors) +
  scale_y_continuous(limits = c(-0.3,plot.max[c])) +
  geom_errorbar(aes(ymin = Beta - SE, ymax = Beta + SE), width = 0.4, size = 1,
                color = "black", position = position_dodge(1)) +
  ggtitle(paste(this.roi,'encoding activity vs. none recalled',sep=" ")) +
  theme(plot.title = element_text(hjust = 0.5, size=28, margin=margin(0,0,20,0)),
        axis.line.x = element_line(color = "black", size = 1), axis.line.y = element_line(color = "black", size = 1),
        axis.text.x = element_text(size = 16, angle = 45, hjust=1, vjust=1),
        axis.text.y = element_text(size = 22), axis.title.y = element_text(size = 26),
        axis.title.x = element_blank(), panel.background = element_blank(),
        legend.position="none", strip.text.x = element_text(size=28), text = element_text(family="Helvetica"),
        plot.margin = margin(1, 1, 1, 1, "cm"))
### print full output:
print(kable(mixed.data,format="pandoc",caption = paste('Remembered features (vs. none recalled) for',this.roi,sep = " ")))
}


Table: HIPP: Scene Binding vs. Scene Alone at Onset

  Estimate   Std. Error         df     t value        lower       upper    Pr(>|t|)
----------  -----------  ---------  ----------  -----------  ----------  ----------
 0.0490484    0.1119956   2594.081   0.4379491   -0.1705615   0.2686583   0.6614597


Table: HIPP: Scene Binding vs. Color&Sound at Onset

  Estimate   Std. Error         df     t value        lower       upper    Pr(>|t|)
----------  -----------  ---------  ----------  -----------  ----------  ----------
 0.0309742    0.1129433   2469.901   0.2742455   -0.1904991   0.2524474   0.7839189


Table: HIPP: Scene Binding vs. Scene Alone at Offset

 Estimate   Std. Error         df    t value        lower       upper    Pr(>|t|)
---------  -----------  ---------  ---------  -----------  ----------  ----------
 0.239022    0.1476316   22.43739   1.619043   -0.0668017   0.5448456   0.1194101


Table: HIPP: Scene Binding vs. Color&Sound at Offset

  Estimate   Std. Error         df    t value       lower       upper    Pr(>|t|)
----------  -----------  ---------  ---------  ----------  ----------  ----------
 0.2328095    0.1167075   1285.221   1.994812   0.0038514   0.4617677   0.0462743


Table: Remembered features (vs. none recalled) for HIPP

Feature                    Beta          SE            T           Df      Pvalue  Phase  
------------------  -----------  ----------  -----------  -----------  ----------  -------
Color.Only           -0.1415691   0.1165886   -1.2142619   3555.61457   0.2247284  Onset  
Sound.Only           -0.0489743   0.1539740   -0.3180684   3554.33722   0.7504517  Onset  
Scene.Only           -0.0811641   0.1291471   -0.6284619   3559.88947   0.5297417  Onset  
Color.Scene           0.0131775   0.1103558    0.1194093   3548.08313   0.9049579  Onset  
Sound.Color          -0.0630898   0.1304795   -0.4835231   3574.89334   0.6287540  Onset  
Scene.Sound          -0.0796042   0.1186943   -0.6706652   3540.50522   0.5024776  Onset  
Color.Scene.Sound    -0.0299203   0.1098568   -0.2723576     91.60632   0.7859600  Onset  
Color.Only            0.0875331   0.1178921    0.7424850   3537.85669   0.4578428  Offset 
Sound.Only            0.0388375   0.1556466    0.2495238   3529.99510   0.8029701  Offset 
Scene.Only            0.1604685   0.1598229    1.0040394     30.36143   0.3232950  Offset 
Color.Scene           0.3601762   0.1322209    2.7240494     51.89518   0.0087661  Offset 
Sound.Color           0.1666809   0.1319913    1.2628168   3562.50568   0.2067377  Offset 
Scene.Sound           0.4503095   0.1201740    3.7471461   3525.03630   0.0001817  Offset 
Color.Scene.Sound     0.3879857   0.1128184    3.4390288     90.31313   0.0008853  Offset 
ggarrange(plotlist = my.plots, ncol = c, nrow = 1)

Anterior vs. posterior

##### load in hippocampal voxel data for all subjects:
subjects <- list.files(path='../data/single-trial-betas/event-offset/',full.names=TRUE, recursive = TRUE, pattern = 'offset-LH_HIPP')
allData  = do.call(rbind, lapply(subjects, function(x) {read.csv(x, header = TRUE)}))
allData$SubID=as.factor(allData$SubID)
allData$Segment <- as.factor(allData$Segment)
### add z score of betas within region and subject to remove outliers:
allData <- allData %>%
               group_by(SubID, Segment) %>%
               mutate(Z = zscore(Beta))
# mark betas that are > XSD
allData$Beta[abs(allData$Z) > outlier_value] <- NA  #mark beta as NA if outlier
allData <- allData[,-c(6)] # remove Z values  
# merge with behavioral data
myBetas = spread(allData, Segment, Beta)
myBetas$TotalEncodingTrial <- 1:nTotal
segment.info <- merge(myBetas, behavData, by="TotalEncodingTrial")
segment.info <- segment.info[,-c(2,3,4)]
colnames(segment.info)[colnames(segment.info) == 'SubID.y'] <- 'SubID'
colnames(segment.info)[colnames(segment.info) == 'Run.y'] <- 'Run'
# remove all trials where there is at least one NA (at least one of the ROIs is an outlier on that trial):
idxBetas     <- complete.cases(myBetas)
segment.info <- segment.info[idxBetas,]
segment.info <- gather(segment.info, Segment, Beta, Anterior:Posterior)
  
####################################
mixed.data <- data.frame(array(0,c(7*2,7)))
colnames(mixed.data) <- c('Segment','Feature','Beta','SE','T','Df','Pvalue')
row=0 
for (v in c('Anterior','Posterior')) {
  
  this.data <- subset(segment.info, Segment == v)
  
  # NOTE. not mean-centering here as zero is meaningful -- excluding trials in other conditions. Implicit baseline = none recalled
  full.model <- suppressMessages(suppressWarnings(lmer(Beta ~ Color.Only + Sound.Only + Scene.Only +
                                                              Color.Scene + Sound.Color + Scene.Sound +
                                                              Color.Scene.Sound +
                                                         (1 + Color.Only + Sound.Only + Scene.Only +
                                                              Color.Scene + Sound.Color + Scene.Sound +
                                                              Color.Scene.Sound||SubID),
                                                       data = this.data)))
  full.model <- suppressMessages(suppressWarnings(get_model(step(full.model, 
                                                                 reduce.random = TRUE, reduce.fixed = FALSE))))
  stats <- summary(full.model)$coefficients
  nIV <- nrow(stats) - 1 # number of fixed effects, discounting intercept
  
  # binding: >=2 features vs. 1
  con <- contest(full.model, c(0,-(1/3),-(1/3),-(1/3),(1/4),(1/4),(1/4),(1/4)), joint = FALSE, ddf = "Satterthwaite")
  print(kable(con,
              format="pandoc", caption=paste(this.roi,': Binding (>1 feature vs. 1 feature): ',v,sep="")))
  # test if scene binding is > recalling color and sound without scene (first position is intercept)
  con <- contest(full.model, c(0,0,0,0,(1/3),-1,(1/3),(1/3)), joint = FALSE, ddf = "Satterthwaite")
  print(kable(con,
              format="pandoc", caption=paste(this.roi,': Scene Binding vs. Color&Sound: ',v,sep="")))
  
  ### add fixed effects and p values to data matrix    
  mixed.data$Segment[(row+1):(row+nIV)]  <- v
  mixed.data$Feature[(row+1):(row+nIV)]  <- row.names(stats)[2:(nIV+1)]
  mixed.data[(row+1):(row+nIV),c("Beta","SE","T","Df","Pvalue")] <- stats[2:(nIV+1),
                                                                            c("Estimate","Std. Error","t value","df","Pr(>|t|)")]
  row = row + nIV
}


Table: HIPP: Binding (>1 feature vs. 1 feature): Anterior

  Estimate   Std. Error         df    t value       lower     upper   Pr(>|t|)
----------  -----------  ---------  ---------  ----------  --------  ---------
 0.2518564    0.0977626   2264.288   2.576204   0.0601427   0.44357   0.010052


Table: HIPP: Scene Binding vs. Color&Sound: Anterior

  Estimate   Std. Error         df    t value       lower       upper    Pr(>|t|)
----------  -----------  ---------  ---------  ----------  ----------  ----------
 0.3674827    0.1431639   2476.641   2.566867   0.0867494   0.6482161   0.0103205


Table: HIPP: Binding (>1 feature vs. 1 feature): Posterior

  Estimate   Std. Error         df    t value       lower       upper    Pr(>|t|)
----------  -----------  ---------  ---------  ----------  ----------  ----------
 0.2296148     0.095161   798.2388   2.412909   0.0428194   0.4164102   0.0160502


Table: HIPP: Scene Binding vs. Color&Sound: Posterior

  Estimate   Std. Error         df     t value        lower       upper   Pr(>|t|)
----------  -----------  ---------  ----------  -----------  ----------  ---------
 0.0399494    0.1383117   1092.224   0.2888357   -0.2314374   0.3113361   0.772762
### print full output:
print(kable(mixed.data,format="pandoc",caption = paste('Remembered feature combinations (vs. none recalled) for Anterior vs. Posterior',this.roi,sep = " ")))


Table: Remembered feature combinations (vs. none recalled) for Anterior vs. Posterior HIPP

Segment     Feature                    Beta          SE            T           Df      Pvalue
----------  ------------------  -----------  ----------  -----------  -----------  ----------
Anterior    Color.Only            0.0252935   0.1481130    0.1707715   3751.46478   0.8644127
Anterior    Sound.Only           -0.0729386   0.1950732   -0.3739039   3750.31295   0.7084969
Anterior    Scene.Only            0.1793859   0.1645030    1.0904717   3754.80165   0.2755754
Anterior    Color.Scene           0.4046226   0.1392027    2.9067152   3754.48405   0.0036738
Anterior    Sound.Color           0.0201579   0.1650469    0.1221343   3772.33454   0.9027991
Anterior    Scene.Sound           0.3882230   0.1502669    2.5835574   3743.48907   0.0098163
Anterior    Color.Scene.Sound     0.3700763   0.1420303    2.6056152     87.75649   0.0107723
Posterior   Color.Only           -0.0072212   0.1408325   -0.0512752   3747.72789   0.9591090
Posterior   Sound.Only            0.0901479   0.1854740    0.4860405   3741.94425   0.6269669
Posterior   Scene.Only            0.1133564   0.1563476    0.7250281   3745.43220   0.4684801
Posterior   Color.Scene           0.1661307   0.1700307    0.9770630     39.05305   0.3345519
Posterior   Sound.Color           0.2650805   0.1564853    1.6939645   3761.39199   0.0903548
Posterior   Scene.Sound           0.4083049   0.1419232    2.8769414   3762.88419   0.0040381
Posterior   Color.Scene.Sound     0.3406540   0.1160245    2.9360522   3646.37646   0.0033450
##### PLOT
order <- c("Color.Only","Scene.Only","Sound.Only","Sound.Color","Color.Scene","Scene.Sound","Color.Scene.Sound")
# colors for venn diagram space feature combinations
barcolors <- c("#f76c67","#67aaf7","#0bca81","#f6a84f","#9c67f7","#67f7f0","#7F7F7F")
mixed.data$Feature <- factor(mixed.data$Feature, levels=order)
mixed.data$Segment <- as.factor(mixed.data$Segment)
##### PLOT
ggplot(mixed.data, aes(x=Feature, y=Beta, fill=Feature)) +
  facet_grid(. ~ Segment) +
  geom_hline(yintercept=0,size=1) +
  scale_fill_manual(values = barcolors) +
  scale_y_continuous(limits = c(-0.35,0.65)) +
  geom_bar(stat = "identity", alpha = 0.80, size = 1, width = 0.75,
           color = "black", position = position_dodge(1)) +
  geom_errorbar(aes(ymin = Beta - SE, ymax = Beta + SE), width = 0.4, size = 1,
                color = "black", position = position_dodge(1)) +
  ggtitle("Anterior/posterior hippocampal offset encoding activity") +
  theme(plot.title = element_text(hjust = 0.5, size=28, margin=margin(0,0,20,0)),
        axis.line.x = element_line(color = "black", size = 1), axis.line.y = element_line(color = "black", size = 1),
        axis.text.x = element_text(size = 16, angle = 45, hjust=1, vjust=1),
        axis.text.y = element_text(size = 22), axis.title.y = element_text(size = 26),
        axis.title.x = element_blank(), panel.background = element_blank(),
        legend.position="none", strip.text.x = element_text(size=28), text = element_text(family="Helvetica"),
        plot.margin = margin(1, 1, 1, 1, "cm"))

LS0tCnRpdGxlOiAiQmluZGluZyBmTVJJOiBBbmFseXNlcyBvZiBmZWF0dXJlIGVuY29kaW5nIGFuZCBpbnRlZ3JhdGlvbiIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGZvbnRzaXplOiA2cHQKICAgIHRoZW1lOiBjZXJ1bGVhbgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgdG9jOiB5ZXMKLS0tCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CmJvZHl7IC8qIE5vcm1hbCAgKi8KICAgICAgZm9udC1zaXplOiAxNHB4fQp0ZCB7ICAvKiBUYWJsZSAgKi8KICBmb250LXNpemU6IDEycHh9CmgxLnRpdGxlIHsKICBmb250LXNpemU6IDMwcHh9CmgxIHsgLyogSGVhZGVyIDEgKi8KICBmb250LXNpemU6IDI0cHh9CmgyIHsgLyogSGVhZGVyIDIgKi8KICAgIGZvbnQtc2l6ZTogMjBweH0KaDMgeyAvKiBIZWFkZXIgMyAqLwogICAgZm9udC1zaXplOiAxOHB4fQpjb2RlLnJ7IC8qIENvZGUgYmxvY2sgKi8KICAgIGZvbnQtc2l6ZTogMTJweH0KPC9zdHlsZT4KClRoZSBhbmFseXNlcyBwcmVzZW50ZWQgaGVyZSB0ZXN0IHN1YnNlcXVlbnQgbWVtb3J5IGVmZmVjdHMgZHVyaW5nIGVuY29kaW5nIG9mIGEgbXVsdGktZmVhdHVyZSBldmVudCAoZnJvbSB0aGUgR2l0SHViIHJlcG9zaXRvcnk6IGh0dHBzOi8vZ2l0aHViLmNvbS9tZW1vYmMvcGFwZXItYmluZGluZ2ZtcmkpLiAgICAKCk1lYW4gc2luZ2xlIHRyaWFsIGJldGEgZXN0aW1hdGVzIGF0IGkpIG9uc2V0IGFuZCBpaSkgb2Zmc2V0IG9mIGVhY2ggdmlzdWFsIGRpc3BsYXkgKG9uc2V0IE4gYW5kIG9mZnNldCBOIGFyZSA2cyBhcGFydCkgd2VyZSBleHRyYWN0ZWQgZnJvbSBlYWNoIG9mIDIwNCBST0lzIGFjcm9zcyB0aGUgY29ydGV4IGFuZCBtZWRpYWwgdGVtcG9yYWwgbG9iZSAoc2VlIH4vZGF0YS9iaW5kaW5nZm1yaV93aG9sZWJyYWluX1JPSXNfaW5mby5jc3YpLiAqT25zZXRzKiBhcmUgdXNlZCB0byB0ZXN0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBicmFpbiBhY3Rpdml0eSBhbmQgbWVtb3J5IGZvciB0aGUgKnVwY29taW5nIGV2ZW50Kiwgd2hlcmVhcyAqb2Zmc2V0cyogYXJlIHVzZWQgdG8gdGVzdCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYnJhaW4gYWN0aXZpdHkgYW5kIG1lbW9yeSBmb3IgdGhlICpwcmVjZWRpbmcgZXZlbnQqLgoKRWFjaCBldmVudCBpbmNsdWRlcyAzIGFzc29jaWF0aW9ucyBlbmNvZGVkIGluIHBhcmFsbGVsIC0tIGFuIG9iamVjdCB3aXRoIGFuIGFzc29jaWF0ZWQgaSkgY29sb3IsIGlpKSBzb3VuZCwgYW5kIGlpaSkgc2NlbmUgbG9jYXRpb24uICAKClN1YnNlcXVlbnQgbWVtb3J5IGRldGFpbCBpcyBjYWxjdWxhdGVkIGFzIHRoZSBudW1iZXIgb2YgZmVhdHVyZXMgKDAtMykgbGF0ZXIgY29ycmVjdGx5IHJlY2FsbGVkLiBGZWF0dXJlLXNwZWNpZmljIGVzdGltYXRlcyBvZiBzdWJzZXF1ZW50IG1lbW9yeSBhcmUgYWxzbyBhbmFseXplZCAoY29ycmVjdCwgMSwgdnMuIGluY29ycmVjdCwgMCkuIApUaGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYWN0aXZpdHkgKGVhY2ggUk9JJ3MgYmV0YSBzZXJpZXMpIGFuZCBzdWJzZXF1ZW50IG1lbW9yeSBpcyBlc3RpbWF0ZWQgdXNpbmcgbGluZWFyIG1peGVkIGVmZmVjdHMgKExNRSkgbW9kZWxzIHdpdGggdGhlIGZvbGxvd2luZyBwaXBlbGluZTogIAoKMSkgRGF0YSBhcmUgY2xlYW5lZCBieSByZW1vdmluZyBhbnkgdHJpYWxzIHdpdGggb3V0bHlpbmcgYmV0YXMgKHogPiB8NHwpLCBjYWxjdWxhdGVkIHdpdGhpbiBzdWJqZWN0IHBlciBST0kgIAoyKSBFYWNoIExNRSBtb2RlbCBpcyBpbml0aWFsbHkgc3BlY2lmaWVkIHdpdGggYSBtYXhpbWFsIHJhbmRvbSBlZmZlY3RzIHN0cnVjdHVyZSAoaW5jbHVkaW5nIHZhcmlhYmxlIHNsb3BlcyBmb3IgYWxsIGZpeGVkIGVmZmVjdHMsIGFzIHdlbGwgYXMgaW50ZXJjZXB0cywgYnkgc3ViamVjdCksIGJ1dCB0byBhdm9pZCBjb252ZXJnZW5jZSBwcm9ibGVtcyBhbmQgc2luZ3VsYXIgZml0czogIAphKSBSYW5kb20gZWZmZWN0cyBjb3JyZWxhdGlvbnMgYXJlIGNvbnN0cmFpbmVkIHRvIHplcm8gZm9yIGFsbCBtb2RlbHMgIApiKSBUaGUgcmFuZG9tIGVmZmVjdCBzdHJ1Y3R1cmUgb2YgbW9kZWxzIHdpdGggPiAyIGZpeGVkIGVmZmVjdHMgaXMgc2ltcGxpZmllZCBieSBpdGVyYXRpdmVseSByZW1vdmluZyBzbG9wZXMgdGhhdCBkbyBub3QgaW1wcm92ZSB0aGUgbW9kZWwgZml0IAozKSBUaGUgc2lnbmlmaWNhbmNlIG9mIHRoZSBmaXhlZCBlZmZlY3RzIChzbG9wZSA+IG9yIDwgMD8pIGlzIHRlc3RlZCB1c2luZyB0LXRlc3RzIHdpdGggU2F0dGVydGh3YWl0ZeKAmXMgYXBwcm94aW1hdGlvbiBtZXRob2QKCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQoKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdncHVicikKbGlicmFyeShnZ2ZvcmNlKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHBhbmRlcikKbGlicmFyeShsbWU0KQpsaWJyYXJ5KGxtZXJUZXN0KQpsaWJyYXJ5KE11TUluKQpsaWJyYXJ5KGV6KQpsaWJyYXJ5KGxzcikKbGlicmFyeShwc3ljaCkKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkocHJhY21hKQpsaWJyYXJ5KG1vc2FpYykKbGlicmFyeShsZXNzUikKCnNlIDwtIGZ1bmN0aW9uKHgpIHNxcnQodmFyKHgpL2xlbmd0aCh4KSkgICNmdW5jdGlvbiB0byBjYWxjdWxhdGUgU0UKY2kgPC0gZnVuY3Rpb24oeCkgKHNxcnQodmFyKHgpL2xlbmd0aCh4KSkpICogMS45NiAgI2Z1bmN0aW9uIHRvIGNhbGN1bGF0ZSA5NSUgQ0kKCm91dGxpZXJfdmFsdWUgPSA0ICNTRCBmb3IgcmVtb3ZpbmcgZXh0cmVtZSBkYXRhIChiZXRhcykKCmBgYAoKIyBCZWhhdmlvcmFsIERhdGEKYGBge3J9CgojIyMgTG9hZCBpbiBiZWhhdmlvcmFsIGRhdGEgZm9yIGFsbCAyNyBzdWJqZWN0czoKYmVoYXZEYXRhIDwtIHJlYWQuY3N2KCcuLi9kYXRhL0FsbFN1YmplY3RzX2JpbmRpbmdmTVJJLWJlaGF2aW9yLmNzdicsIGhlYWRlciA9IFRSVUUpCmJlaGF2X3N1YnMgPC0gdW5pcXVlKGJlaGF2RGF0YSRTdWJJRCkKCmNhdCgnTnVtYmVyIG9mIHN1YmplY3RzID0nLGxlbmd0aChiZWhhdl9zdWJzKSkKCiMgKiogbm90ZS4gYmVoYXZpb3JhbCBkYXRhIGlzIGFscmVhZHkgc29ydGVkIGJ5IHN1YmplY3QgYW5kIGVuY29kaW5nIHRyaWFsIG9uc2V0ICoqICMKCgojIENyZWF0ZSBhZGRpdGlvbmFsIGNvbHVtbnMgZm9yIHN1YnNlcXVlbnQgbWVtb3J5IG1lYXN1cmVzIG9mIGludGVyZXN0IC0tIFNvdW5kLCBDb2xvciwgJiBTY2VuZSBmZWF0dXJlcyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmJlaGF2RGF0YSRTb3VuZCA8LSAwCmJlaGF2RGF0YSRTb3VuZFtiZWhhdkRhdGEkU291bmRDb3JyZWN0ID09IDEgJiBiZWhhdkRhdGEkU291bmRDb25maWRlbmNlID09IDFdIDwtIDEgIyBzb3VuZCBvbmx5IHN1Y2Vzc2Z1bCBpZiBjb3JyZWN0ICphbmQqIGhpZ2ggY29uZmlkZW5jZQoKIyBkZWZpbmUgJ3N1Y2Nlc3MnIHRocmVzaG9sZHMgZm9yIGNvbG9yIGFuZCBzY2VuZSB0cmlhbHMgLT4gNzUlIHByb2JhYmlsaXR5IHdpdGhpbiB2b24gTWlzZXMgZm9yIDI3IHN1YmplY3RzIGFnZ3JlZ2F0ZSBkYXRhCmNDdXRPZmYgPSA0MgpzQ3V0T2ZmID0gMjQKCiMgZmVhdHVyZSBhY2N1cmFjeQpiZWhhdkRhdGEkQ29sb3IgPC0gMApiZWhhdkRhdGEkQ29sb3JbYmVoYXZEYXRhJENvbEFic0Vycm9yIDw9Y0N1dE9mZl0gPC0gMSAjY29ycmVjdCBpZiBlcnJvciAoaG93IGZhciBmcm9tIHRhcmdldCkgbGVzcyB0aGFuIG9yIGVxdWFsIHRvIHRocmVzaG9sZApiZWhhdkRhdGEkU2NlbmUgPC0gMApiZWhhdkRhdGEkU2NlbmVbYmVoYXZEYXRhJFNjZUFic0Vycm9yIDw9c0N1dE9mZl0gPC0gMSAjY29ycmVjdCBpZiBlcnJvciAoaG93IGZhciBmcm9tIHRhcmdldCkgbGVzcyB0aGFuIG9yIGVxdWFsIHRvIHRocmVzaG9sZAoKIyBvdmVyYWxsIG1lbW9yeSBkZXRhaWwgKGFjY291bnRpbmcgZm9yIGZlYXR1cmUgc3VjY2VzcyAtIDAtMykKYmVoYXZEYXRhJE1lbW9yeURldGFpbCA8LSBiZWhhdkRhdGEkU291bmQgKyBiZWhhdkRhdGEkQ29sb3IgKyBiZWhhdkRhdGEkU2NlbmUKCgojIyMjIHNlcGFyYXRlIHRyaWFsLXVuaXF1ZSBjb25kaXRpb25zIGJhc2VkIG9uIGZlYXR1cmUgY29tYmluYXRpb25zIHJlY2FsbGVkIC0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIDEgZmVhdHVyZQpiZWhhdkRhdGEkQ29sb3IuT25seSAgIDwtIDAKYmVoYXZEYXRhJFNvdW5kLk9ubHkgPC0gMApiZWhhdkRhdGEkU2NlbmUuT25seSAgIDwtIDAKYmVoYXZEYXRhJENvbG9yLk9ubHlbYmVoYXZEYXRhJENvbG9yID09IDEgJiBiZWhhdkRhdGEkU291bmQgPT0gMCAmIGJlaGF2RGF0YSRTY2VuZSA9PSAwXSAgPC0gMQpiZWhhdkRhdGEkU291bmQuT25seVtiZWhhdkRhdGEkQ29sb3IgPT0gMCAmIGJlaGF2RGF0YSRTb3VuZCA9PSAxICYgYmVoYXZEYXRhJFNjZW5lID09IDBdICA8LSAxCmJlaGF2RGF0YSRTY2VuZS5Pbmx5W2JlaGF2RGF0YSRDb2xvciA9PSAwICYgYmVoYXZEYXRhJFNvdW5kID09IDAgJiBiZWhhdkRhdGEkU2NlbmUgPT0gMV0gIDwtIDEKCiMjIDIgZmVhdHVyZXMKYmVoYXZEYXRhJENvbG9yLlNjZW5lICAgPC0gMApiZWhhdkRhdGEkU291bmQuQ29sb3IgPC0gMApiZWhhdkRhdGEkU2NlbmUuU291bmQgPC0gMApiZWhhdkRhdGEkQ29sb3IuU2NlbmVbYmVoYXZEYXRhJENvbG9yID09IDEgJiBiZWhhdkRhdGEkU291bmQgPT0gMCAmIGJlaGF2RGF0YSRTY2VuZSA9PSAxXSA8LSAxCmJlaGF2RGF0YSRTb3VuZC5Db2xvcltiZWhhdkRhdGEkQ29sb3IgPT0gMSAmIGJlaGF2RGF0YSRTb3VuZCA9PSAxICYgYmVoYXZEYXRhJFNjZW5lID09IDBdIDwtIDEKYmVoYXZEYXRhJFNjZW5lLlNvdW5kW2JlaGF2RGF0YSRDb2xvciA9PSAwICYgYmVoYXZEYXRhJFNvdW5kID09IDEgJiBiZWhhdkRhdGEkU2NlbmUgPT0gMV0gPC0gMQoKIyMgMyBmZWF0dXJlcwpiZWhhdkRhdGEkQ29sb3IuU2NlbmUuU291bmQgIDwtIDAKYmVoYXZEYXRhJENvbG9yLlNjZW5lLlNvdW5kW2JlaGF2RGF0YSRDb2xvciA9PSAxICYgYmVoYXZEYXRhJFNvdW5kID09IDEgJiBiZWhhdkRhdGEkU2NlbmUgPT0gMV0gPC0gMQoKCiMjIyBydW4gYSBjaGVjayB0byBtYWtlIHN1cmUgbm8gdHJpYWwgaGFzIGJlZW4gYWxsb2NhdGVkIHRvIG1vcmUgdGhhbiBvbmUgY29tYmluYXRpb246Cm5QcmVzZW50IDwtIHJvd1N1bXMoYmVoYXZEYXRhWyxjKCJDb2xvci5Pbmx5IiwiU291bmQuT25seSIsIlNjZW5lLk9ubHkiLCJDb2xvci5TY2VuZSIsIlNvdW5kLkNvbG9yIiwiU2NlbmUuU291bmQiLCJDb2xvci5TY2VuZS5Tb3VuZCIpXSkKaWYgKG1heChuUHJlc2VudCkgPCAxKSB7CiAgY2F0KCdFUlJPUjogdHJpYWwocykgYXNzaWduZWQgdG8gbW9yZSB0aGFuIG9uZSBjb21iaW5hdGlvblxuJykKfQoKCiMgZG91YmxlIGNoZWNrIGVuY29kaW5nIGRhdGEgaXMgc29ydGVkIGNvcnJlY3RseSAoYnkgc3ViamVjdCwgdGhlbiBlbmNvZGluZyBvbnNldCAoYnkgcnVuIGFuZCB0aW1lKSkKYmVoYXZEYXRhIDwtIGJlaGF2RGF0YVtvcmRlcihiZWhhdkRhdGEkVG90YWxFbmNvZGluZ1RyaWFsKSxdCm5Ub3RhbCA8LSBucm93KGJlaGF2RGF0YSkKCgojIHByaW50IHBlcmNlbnRhZ2UgY29ycmVjdCBmb3IgZWFjaCBmZWF0dXJlCmZlYXR1cmUuYWNjdXJhY3kgPC0gYmVoYXZEYXRhWyxjKCJTdWJJRCIsIlN0dWR5VHJpYWwiLCJDb2xvciIsIlNvdW5kIiwiU2NlbmUiKV0Kc3ViLmJlaGF2IDwtIGZlYXR1cmUuYWNjdXJhY3kgJT4lIGdhdGhlcihGZWF0dXJlLEFjY3VyYWN5LENvbG9yOlNjZW5lKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cF9ieShTdWJJRCwgRmVhdHVyZSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKE0gPSBtZWFuKEFjY3VyYWN5KSkKCmthYmxlKHN1Yi5iZWhhdiAlPiUgZ3JvdXBfYnkoRmVhdHVyZSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKE1lbW9yeSA9IG1lYW4oTSksIFNFID0gc2UoTSkpLGZvcm1hdD0icGFuZG9jIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FwdGlvbj0iRmVhdHVyZSBBY2N1cmFjeSAocHJvcG9ydGlvbiBvZiB0cmlhbHMgbGFiZWxlZCBhcyBjb3JyZWN0KSIpCgojIyMgcHJpbnQgdG90YWwgbnVtYmVyIG9mIHRyaWFscyB0aGF0IHVuaXF1ZWx5IGZhbGwgaW50byBlYWNoIHBvc3NpYmxlIGNvbWJpbmF0aW9uOgpjYXQoJ1xuTm9uZSByZWNhbGxlZCB0cmlhbHMgPScsIG5yb3coc3Vic2V0KGJlaGF2RGF0YSxNZW1vcnlEZXRhaWwgPT0gMCkpLCcoJyxyb3VuZCgoKG5yb3coc3Vic2V0KGJlaGF2RGF0YSxNZW1vcnlEZXRhaWwgPT0gMCkpKS9uVG90YWwpKjEwMCwyKSwnJSlcbicpCmNhdCgnQ29sb3Igb25seSB0cmlhbHMgPScsIHN1bShiZWhhdkRhdGEkQ29sb3IuT25seSksJygnLHJvdW5kKCgoc3VtKGJlaGF2RGF0YSRDb2xvci5Pbmx5KSkvblRvdGFsKSoxMDAsMiksJyUpXG4nKQpjYXQoJ1NjZW5lIG9ubHkgdHJpYWxzID0nLCBzdW0oYmVoYXZEYXRhJFNjZW5lLk9ubHkpLCcoJyxyb3VuZCgoKHN1bShiZWhhdkRhdGEkU2NlbmUuT25seSkpL25Ub3RhbCkqMTAwLDIpLCclKVxuJykKY2F0KCdTb3VuZCBvbmx5IHRyaWFscyA9Jywgc3VtKGJlaGF2RGF0YSRTb3VuZC5Pbmx5KSwnKCcscm91bmQoKChzdW0oYmVoYXZEYXRhJFNvdW5kLk9ubHkpKS9uVG90YWwpKjEwMCwyKSwnJSlcbicpCmNhdCgnQ29sb3IgJiBTY2VuZSBvbmx5IHRyaWFscyA9Jywgc3VtKGJlaGF2RGF0YSRDb2xvci5TY2VuZSksJygnLHJvdW5kKCgoc3VtKGJlaGF2RGF0YSRDb2xvci5TY2VuZSkpL25Ub3RhbCkqMTAwLDIpLCclKVxuJykKY2F0KCdTY2VuZSAmIFNvdW5kIG9ubHkgdHJpYWxzID0nLCBzdW0oYmVoYXZEYXRhJFNjZW5lLlNvdW5kKSwnKCcscm91bmQoKChzdW0oYmVoYXZEYXRhJFNjZW5lLlNvdW5kKSkvblRvdGFsKSoxMDAsMiksJyUpXG4nKQpjYXQoJ1NvdW5kICYgQ29sb3Igb25seSB0cmlhbHMgPScsIHN1bShiZWhhdkRhdGEkU291bmQuQ29sb3IpLCcoJyxyb3VuZCgoKHN1bShiZWhhdkRhdGEkU291bmQuQ29sb3IpKS9uVG90YWwpKjEwMCwyKSwnJSlcbicpCmNhdCgnQ29sb3IgJiBTY2VuZSAmIFNvdW5kIHRyaWFscyA9Jywgc3VtKGJlaGF2RGF0YSRDb2xvci5TY2VuZS5Tb3VuZCksJygnLHJvdW5kKCgoc3VtKGJlaGF2RGF0YSRDb2xvci5TY2VuZS5Tb3VuZCkpL25Ub3RhbCkqMTAwLDIpLCclKVxuJykKCmBgYAoKIyMgQ2hlY2sgZm9yIGJlaGF2aW9yYWwgKG1lbW9yeSkgdHJpYWwgZGVwZW5kZW5jaWVzCkNoZWNrIGlmIGVuY29kaW5nIHN1Y2Nlc3MgYXQgdHJpYWwgTiBpcyByZWxhdGVkIHRvIGVuY29kaW5nIHN1Y2Nlc3Mgb24gdHJpYWwgTisxIHdpdGhpbiBzdWJqZWN0cwpgYGB7cn0KCmJlaGF2RGF0YSRFbmNvZGluZ0V2ZW50IDwtIHJlcCgxOjI0LCBucm93KGJlaGF2RGF0YSkvMjQpICNldmVudCBudW1iZXIgd2l0aGluIHJ1bgoKIyBvdmVyYWxsIGRldGFpbCwgdGhlbiBlYWNoIGZlYXR1cmUgc2VwYXJhdGVseQpmZWF0dXJlcyA9IGMoJ01lbW9yeURldGFpbCcsJ0NvbG9yJywnU291bmQnLCdTY2VuZScpCgpmb3IgKGYgaW4gMTpsZW5ndGgoZmVhdHVyZXMpKSB7CiAgbXkuY29sIDwtIHdoaWNoKGNvbG5hbWVzKGJlaGF2RGF0YSkgJWluJSBmZWF0dXJlc1tmXSkKCiAgY29ycmVsYXRpb25zIDwtIGRhdGEuZnJhbWUoYXJyYXkoMCxjKGxlbmd0aChiZWhhdl9zdWJzKSwyKSkpCiAgY29sbmFtZXMoY29ycmVsYXRpb25zKSA8LSBjKCdTdWJJRCcsJ1onKQogIGZvciAocyBpbiAxOmxlbmd0aChiZWhhdl9zdWJzKSl7CiAgICBzdWJEYXRhIDwtIHN1YnNldChiZWhhdkRhdGEsIFN1YklEID09IGJlaGF2X3N1YnNbc10pCiAgICBjb3JyZWxhdGlvbnMkU3ViSURbc10gPC0gYmVoYXZfc3Vic1tzXQoKICAgIHRyaWFsbiAgPC0gc3Vic2V0KHN1YkRhdGEsIEVuY29kaW5nRXZlbnQgPCAyNCkgI2FsbCBidXQgbGFzdCBldmVudCBvZiBydW4gZm9yIE4gdmVjdG9yICgxOjIzKSBjb3JyZWxhdGVkIHdpdGggLi4uCiAgICB0cmlhbG4xIDwtIHN1YnNldChzdWJEYXRhLCBFbmNvZGluZ0V2ZW50ID4gMSkgICNhbGwgYnV0IGZpcnN0IGV2ZW50IG9mIHRoZSBydW4gZm9yIE4rMSB2ZWN0b3IgKDI6MjQpCiAgICBjb3JyZWxhdGlvbnMkWltzXSA8LSBmaXNoZXJ6KGNvcih0cmlhbG5bLG15LmNvbF0sdHJpYWxuMVssbXkuY29sXSkpCiAgfQogIAogIFIgPSBmaXNoZXJ6MnIobWVhbihjb3JyZWxhdGlvbnMkWikpCiAgU0VtZWFuID0gc2UoY29ycmVsYXRpb25zJFopCiAgY2F0KCdNZWFuIGNvcnJlbGF0aW9uIChyKSBiZXR3ZWVuIHRyaWFsIE4gYW5kIE4rMSBmb3InLGZlYXR1cmVzW2ZdLCd3aXRoaW4gc3ViamVjdCA9JyxSLCcsIFNFID0nLFNFbWVhbiwnXG4nKQp9CgpgYGAKCiMgR2V0IE9uc2V0IERhdGEKT25zZXRzIGFyZSB1c2VkIHRvIHRlc3QgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGFjdGl2aXR5IGFuZCBtZW1vcnkgZm9yIHRoZSB1cGNvbWluZyBldmVudCAgCmBgYHtyfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgc2luZ2xlIHRyaWFsIGFjdGl2aXR5IGRhdGEgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgZ2V0IHN1YmplY3RzJyBkYXRhCnN1YmplY3RzIDwtIGxpc3QuZmlsZXMocGF0aD0nLi4vZGF0YS9zaW5nbGUtdHJpYWwtYmV0YXMvZXZlbnQtb25zZXQvJyxmdWxsLm5hbWVzPVRSVUUsIHJlY3Vyc2l2ZSA9IFRSVUUsIHBhdHRlcm4gPSAnb25zZXQtd2hvbGVicmFpbicpCmFsbERhdGEgID0gZG8uY2FsbChyYmluZCwgbGFwcGx5KHN1YmplY3RzLCBmdW5jdGlvbih4KSB7cmVhZC5jc3YoeCwgaGVhZGVyID0gVFJVRSl9KSkKCmFsbERhdGEkU3ViSUQ9YXMuZmFjdG9yKGFsbERhdGEkU3ViSUQpCmFsbERhdGEkUk9JIDwtIGFzLmZhY3RvcihhbGxEYXRhJFJPSSkKYWxsRGF0YSA8LSBhbGxEYXRhWywtYyg1KV0gIyByZW1vdmUgdm94IG51bWJlcnMgLSBub3QgbmVlZGVkIGhlcmUKCnJvaXMgPSBsZXZlbHMoYWxsRGF0YSRST0kpCgoKY2F0KCdOdW1iZXIgb2Ygc3ViamVjdHMgPScsIGxlbmd0aCh1bmlxdWUoYWxsRGF0YSRTdWJJRCkpLCdcbicpCmNhdCgnUmVtb3ZpbmcgaW5mbHVlbnRpYWwgb25zZXQgYmV0YXMsIHx6fCA+Jywgb3V0bGllcl92YWx1ZSwnXG4nKQoKIyAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiAjCgojIyMgYWRkIHogc2NvcmUgb2YgYmV0YXMgd2l0aGluIHJlZ2lvbiBhbmQgc3ViamVjdCB0byByZW1vdmUgb3V0bGllcnM6CmFsbERhdGEgPC0gYWxsRGF0YSAlPiUKICAgICAgICAgICAgICAgZ3JvdXBfYnkoU3ViSUQsIFJPSSkgJT4lCiAgICAgICAgICAgICAgIG11dGF0ZShaID0genNjb3JlKE1lYW5CZXRhKSkKCiMgcmVtb3ZlIGJldGFzIHRoYXQgYXJlID4gWFNEIC0tIHByaW50IHRoaXMgbnVtYmVyCmFsbERhdGEuY2xlYW4gPC0gYWxsRGF0YQphbGxEYXRhLmNsZWFuJE1lYW5CZXRhW2FicyhhbGxEYXRhLmNsZWFuJFopID4gb3V0bGllcl92YWx1ZV0gPC0gTkEgICNtYXJrIGJldGEgYXMgTkEgaWYgb3V0bGllcgphbGxEYXRhLmNsZWFuIDwtIGFsbERhdGEuY2xlYW5bLC1jKDYpXSAjIHJlbW92ZSBaIHZhbHVlcyAgCgoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojc3ByZWFkIGRhdGEgc28gZWFjaCBvZiB0aGUgMjA0IFJPSXMgaXMgaW4gaXRzIG93biBjb2x1bW4gKHRvIGFsaWduIHdpdGggYmVoYXZpb3JhbCBkYXRhKQpteUJldGFzID0gZGF0YS5mcmFtZShzcHJlYWQoYWxsRGF0YS5jbGVhbiwgUk9JLCBNZWFuQmV0YSkpCm15QmV0YXMkVG90YWxFbmNvZGluZ1RyaWFsIDwtIDE6blRvdGFsCgoKIyByZW1vdmUgYWxsIHRyaWFscyB3aGVyZSB0aGVyZSBpcyBhdCBsZWFzdCBvbmUgTkEgKGF0IGxlYXN0IG9uZSBvZiB0aGUgUk9JcyBpcyBhbiBvdXRsaWVyIG9uIHRoYXQgdHJpYWwpIC0gCiMgdG8ga2VlcCB0aGUgdHJpYWxzIGFuYWx5emVkIHRoZSBzYW1lIGFjcm9zcyByZWdpb25zOgppZHhCZXRhcyA8LSBjb21wbGV0ZS5jYXNlcyhteUJldGFzKQpteUJldGFzLmNsZWFuIDwtIG15QmV0YXNbaWR4QmV0YXMsXQpjYXQoJ1RvdGFsIG51bWJlciBvZiBvbnNldHMgcmVtb3ZlZCA9JyxzdW0oIWlkeEJldGFzKSwnb3V0IG9mJyxuVG90YWwsJ1xuXG4nKQoKCiMjIyMjIG1lcmdlIGNsZWFuZWQgYmV0YXMgd2l0aCBiZWhhdmlvcmFsIGRhdGEsIHJlbW92aW5nIGNvcnJlc3BvbmRpbmcgYmVoYXZpb3JhbCB0cmlhbHM6CmJlaGF2RGF0YS5jbGVhbiA8LSBiZWhhdkRhdGFbaWR4QmV0YXMsXQp0ZXN0RGF0YSA8LSBtZXJnZS5kYXRhLmZyYW1lKG15QmV0YXMuY2xlYW4sYmVoYXZEYXRhLmNsZWFuLGJ5PSJUb3RhbEVuY29kaW5nVHJpYWwiKQoKCiMgcmVtb3ZlIGR1cGxpY2F0ZSBjb2x1bW5zIGZvciBzdWJqZWN0LCBydW4sIGFuZCBzdHVkeSB0cmlhbAp0ZXN0RGF0YSA8LSB0ZXN0RGF0YVssLWMoMiwzLDQpXQpjb2xuYW1lcyh0ZXN0RGF0YSlbY29sbmFtZXModGVzdERhdGEpID09ICdTdWJJRC55J10gPC0gJ1N1YklEJwpjb2xuYW1lcyh0ZXN0RGF0YSlbY29sbmFtZXModGVzdERhdGEpID09ICdSdW4ueSddIDwtICdSdW4nCnN1YmplY3RzIDwtIHVuaXF1ZSh0ZXN0RGF0YSRTdWJJRCkKCm9uc2V0LmRhdGEgIDwtIHRlc3REYXRhICAjc3RvcmUgb25zZXQgYmV0YXMgYW5kIGJlaGF2aW9yYWwgZGF0YSBmb3IgdXBjb21pbmcgdHJpYWwKCmNhdCgnQ2xlYW5lZCBhbmQgbWVyZ2VkIG9uc2V0IGJldGFzIGFuZCBiZWhhdmlvcmFsIHRyaWFsc1xuJykKCmBgYAoKIyBHZXQgT2Zmc2V0IERhdGEKT2Zmc2V0cyBhcmUgdXNlZCB0byB0ZXN0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBhY3Rpdml0eSBhbmQgbWVtb3J5IGZvciB0aGUgcHJlY2VkaW5nIGV2ZW50ICAKYGBge3J9CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyBzaW5nbGUgdHJpYWwgYWN0aXZpdHkgZGF0YSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBnZXQgc3ViamVjdHMnIGRhdGEKc3ViamVjdHMgPC0gbGlzdC5maWxlcyhwYXRoPScuLi9kYXRhL3NpbmdsZS10cmlhbC1iZXRhcy9ldmVudC1vZmZzZXQvJyxmdWxsLm5hbWVzPVRSVUUsIHJlY3Vyc2l2ZSA9IFRSVUUsIHBhdHRlcm4gPSAnb2Zmc2V0LXdob2xlYnJhaW4nKQphbGxEYXRhICA9IGRvLmNhbGwocmJpbmQsIGxhcHBseShzdWJqZWN0cywgZnVuY3Rpb24oeCkge3JlYWQuY3N2KHgsIGhlYWRlciA9IFRSVUUpfSkpCgphbGxEYXRhJFN1YklEPWFzLmZhY3RvcihhbGxEYXRhJFN1YklEKQphbGxEYXRhJFJPSSA8LSBhcy5mYWN0b3IoYWxsRGF0YSRST0kpCmFsbERhdGEgPC0gYWxsRGF0YVssLWMoNSldICMgcmVtb3ZlIHZveCBudW1iZXJzIC0gbm90IG5lZWRlZCBoZXJlCgpyb2lzID0gbGV2ZWxzKGFsbERhdGEkUk9JKQoKCmNhdCgnTnVtYmVyIG9mIHN1YmplY3RzID0nLCBsZW5ndGgodW5pcXVlKGFsbERhdGEkU3ViSUQpKSwnXG4nKQpjYXQoJ1JlbW92aW5nIGluZmx1ZW50aWFsIG9mZnNldCBiZXRhcywgfHp8ID4nLCBvdXRsaWVyX3ZhbHVlLCdcbicpCgojICoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqICMKCiMjIyBhZGQgeiBzY29yZSBvZiBiZXRhcyB3aXRoaW4gcmVnaW9uIGFuZCBzdWJqZWN0IHRvIHJlbW92ZSBvdXRsaWVyczoKYWxsRGF0YSA8LSBhbGxEYXRhICU+JQogICAgICAgICAgICAgICBncm91cF9ieShTdWJJRCwgUk9JKSAlPiUKICAgICAgICAgICAgICAgbXV0YXRlKFogPSB6c2NvcmUoTWVhbkJldGEpKQoKIyByZW1vdmUgYmV0YXMgdGhhdCBhcmUgPiBYU0QgLS0gcHJpbnQgdGhpcyBudW1iZXIKYWxsRGF0YS5jbGVhbiA8LSBhbGxEYXRhCmFsbERhdGEuY2xlYW4kTWVhbkJldGFbYWJzKGFsbERhdGEuY2xlYW4kWikgPiBvdXRsaWVyX3ZhbHVlXSA8LSBOQSAgI21hcmsgYmV0YSBhcyBOQSBpZiBvdXRsaWVyCmFsbERhdGEuY2xlYW4gPC0gYWxsRGF0YS5jbGVhblssLWMoNildICMgcmVtb3ZlIFogdmFsdWVzICAKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiNzcHJlYWQgZGF0YSBzbyBlYWNoIG9mIHRoZSAyMDQgUk9JcyBpcyBpbiBpdHMgb3duIGNvbHVtbiAodG8gYWxpZ24gd2l0aCBiZWhhdmlvcmFsIGRhdGEpCm15QmV0YXMgPSBkYXRhLmZyYW1lKHNwcmVhZChhbGxEYXRhLmNsZWFuLCBST0ksIE1lYW5CZXRhKSkKbXlCZXRhcyRUb3RhbEVuY29kaW5nVHJpYWwgPC0gMTpuVG90YWwKCgojIHJlbW92ZSBhbGwgdHJpYWxzIHdoZXJlIHRoZXJlIGlzIGF0IGxlYXN0IG9uZSBOQSAoYXQgbGVhc3Qgb25lIG9mIHRoZSBST0lzIGlzIGFuIG91dGxpZXIgb24gdGhhdCB0cmlhbCkgLSAKIyB0byBrZWVwIHRoZSB0cmlhbHMgYW5hbHl6ZWQgdGhlIHNhbWUgYWNyb3NzIHJlZ2lvbnM6CmlkeEJldGFzIDwtIGNvbXBsZXRlLmNhc2VzKG15QmV0YXMpCm15QmV0YXMuY2xlYW4gPC0gbXlCZXRhc1tpZHhCZXRhcyxdCmNhdCgnVG90YWwgbnVtYmVyIG9mIG9mZnNldHMgcmVtb3ZlZCA9JyxzdW0oIWlkeEJldGFzKSwnb3V0IG9mJyxuVG90YWwsJ1xuXG4nKQoKCiMjIyMjIG1lcmdlIGNsZWFuZWQgYmV0YXMgd2l0aCBiZWhhdmlvcmFsIGRhdGEsIHJlbW92aW5nIGNvcnJlc3BvbmRpbmcgYmVoYXZpb3JhbCB0cmlhbHM6CmJlaGF2RGF0YS5jbGVhbiA8LSBiZWhhdkRhdGFbaWR4QmV0YXMsXQp0ZXN0RGF0YSA8LSBtZXJnZS5kYXRhLmZyYW1lKG15QmV0YXMuY2xlYW4sYmVoYXZEYXRhLmNsZWFuLGJ5PSJUb3RhbEVuY29kaW5nVHJpYWwiKQoKCiMgcmVtb3ZlIGR1cGxpY2F0ZSBjb2x1bW5zIGZvciBzdWJqZWN0LCBydW4sIGFuZCBzdHVkeSB0cmlhbAp0ZXN0RGF0YSA8LSB0ZXN0RGF0YVssLWMoMiwzLDQpXQpjb2xuYW1lcyh0ZXN0RGF0YSlbY29sbmFtZXModGVzdERhdGEpID09ICdTdWJJRC55J10gPC0gJ1N1YklEJwpjb2xuYW1lcyh0ZXN0RGF0YSlbY29sbmFtZXModGVzdERhdGEpID09ICdSdW4ueSddIDwtICdSdW4nCnN1YmplY3RzIDwtIHVuaXF1ZSh0ZXN0RGF0YSRTdWJJRCkKCm9mZnNldC5kYXRhICA8LSB0ZXN0RGF0YSAgI3N0b3JlIG9mZnNldCBiZXRhcyBhbmQgYmVoYXZpb3JhbCBkYXRhIGZvciBwcmVjZWRpbmcgdHJpYWwKCmNhdCgnQ2xlYW5lZCBhbmQgbWVyZ2VkIG9mZnNldCBiZXRhcyBhbmQgYmVoYXZpb3JhbCB0cmlhbHNcbicpCgpgYGAKCiMgQ29ycmVsYXRlIG9uc2V0L29mZnNldCBiZXRhcwphKSBTZXBhcmF0ZWQgYnkgMXMgSVRJOiBGaXJzdCByZW1vdmUgYW55IG9uc2V0cyBmb3Igd2hpY2ggb2Zmc2V0IE4tMSBpcyBub3QgYXZhaWxhYmxlICgxcyBhcGFydCkgd2l0aGluIHN1YmplY3QgYW5kIHJ1biwgYW5kIHZpY2UgdmVyc2EuIENvcnJlbGF0aW9uIGJ5IHJvaSBhbmQgc3ViamVjdCwgdGhlbiB0YWtlIHRoZSBvdmVyYWxsIGF2ZXJhZ2Ugd2l0aGluIHN1YmplY3QsIGFuZCBhdCB0aGUgZ3JvdXAgbGV2ZWwuICAgIApiKSBTZXBhcmF0ZWQgYnkgNnMgZXZlbnQ6IENvcnJlbGF0aW9uIGJldHdlZW4gc2FtZS10cmlhbCBvbnNldHMgYW5kIG9mZnNldHMuICAKYGBge3J9CgojIyMjIyMjIyMgQSkgIyMjIyMjIyMjCiMjIG9uc2V0IE4gdG8gb2Zmc2V0IE4tMQoKb25zZXQuZGF0YS5jb3IgPC0gb25zZXQuZGF0YQpvZmZzZXQuZGF0YS5jb3IgPC0gb2Zmc2V0LmRhdGEKCnRyaWFsc19rZWVwIDwtIGMoKQpmb3IgKHMgaW4gMTpsZW5ndGgoc3ViamVjdHMpKSB7CiAgZm9yIChyIGluIHVuaXF1ZShvbnNldC5kYXRhLmNvciRSdW5bb25zZXQuZGF0YS5jb3IkU3ViSUQgPT0gc3ViamVjdHNbc11dKSkgewogICAgc3ViRGF0YS5vbnNldCA8LSBzdWJzZXQob25zZXQuZGF0YS5jb3IsIFN1YklEID09IHN1YmplY3RzW3NdICYgUnVuID09IHIpCiAgICBzdWJEYXRhLm9mZnNldCA8LSBzdWJzZXQob2Zmc2V0LmRhdGEuY29yLCBTdWJJRCA9PSBzdWJqZWN0c1tzXSAmIFJ1biA9PSByKQogICAgZm9yIChvIGluIDE6bnJvdyhzdWJEYXRhLm9uc2V0KSkgewogICAgICBpZiAoKHN1YkRhdGEub25zZXQkU3R1ZHlUcmlhbFtvXS0xKSAlaW4lIHN1YkRhdGEub2Zmc2V0JFN0dWR5VHJpYWwpIHsgICNpZiB0aGUgdHJpYWwgYmVmb3JlIGN1cnJlbnQgb25zZXQgaXMgcHJlc2VudCBpbiBvZmZzZXQgbGlzdCwga2VlcAogICAgICAgIHRyaWFsc19rZWVwIDwtIGModHJpYWxzX2tlZXAsIHN1YkRhdGEub25zZXQkVG90YWxFbmNvZGluZ1RyaWFsW29dKQogICAgICB9CiAgICB9IAogIH0KfQpvbnNldC5kYXRhLmNvciA8LSBvbnNldC5kYXRhLmNvcltvbnNldC5kYXRhLmNvciRUb3RhbEVuY29kaW5nVHJpYWwgJWluJSB0cmlhbHNfa2VlcCxdCgp0cmlhbHNfa2VlcCA8LSBjKCkKZm9yIChzIGluIDE6bGVuZ3RoKHN1YmplY3RzKSkgewogIGZvciAociBpbiB1bmlxdWUob2Zmc2V0LmRhdGEuY29yJFJ1bltvZmZzZXQuZGF0YS5jb3IkU3ViSUQgPT0gc3ViamVjdHNbc11dKSkgewogICAgc3ViRGF0YS5vbnNldCA8LSBzdWJzZXQob25zZXQuZGF0YS5jb3IsIFN1YklEID09IHN1YmplY3RzW3NdICYgUnVuID09IHIpCiAgICBzdWJEYXRhLm9mZnNldCA8LSBzdWJzZXQob2Zmc2V0LmRhdGEuY29yLCBTdWJJRCA9PSBzdWJqZWN0c1tzXSAmIFJ1biA9PSByKQogICAgZm9yIChvIGluIDE6bnJvdyhzdWJEYXRhLm9mZnNldCkpIHsKICAgICAgaWYgKChzdWJEYXRhLm9mZnNldCRTdHVkeVRyaWFsW29dKzEpICVpbiUgc3ViRGF0YS5vbnNldCRTdHVkeVRyaWFsKSB7ICNpZiB0aGUgdHJpYWwgYWZ0ZXIgY3VycmVudCBvZmZzZXQgaXMgcHJlc2VudCBpbiBvbnNldCBsaXN0LCBrZWVwCiAgICAgICAgdHJpYWxzX2tlZXAgPC0gYyh0cmlhbHNfa2VlcCwgc3ViRGF0YS5vZmZzZXQkVG90YWxFbmNvZGluZ1RyaWFsW29dKQogICAgICB9CiAgICB9IAogIH0KfQpvZmZzZXQuZGF0YS5jb3IgPC0gb2Zmc2V0LmRhdGEuY29yW29mZnNldC5kYXRhLmNvciRUb3RhbEVuY29kaW5nVHJpYWwgJWluJSB0cmlhbHNfa2VlcCxdCgoKIyBub3cgcnVuIHBlciBST0kgYW5kIHN1YmplY3QKY29ycmVsYXRpb25zIDwtIGRhdGEuZnJhbWUoYXJyYXkoMCxjKChsZW5ndGgoc3ViamVjdHMpKmxlbmd0aChyb2lzKSksMykpKQpjb2xuYW1lcyhjb3JyZWxhdGlvbnMpIDwtIGMoJ1N1YklEJywnUk9JJywnWicpCnJvdyA9IDAKZm9yIChyIGluIDE6bGVuZ3RoKHJvaXMpKSB7CiBmb3IgKHMgaW4gMTpsZW5ndGgoc3ViamVjdHMpKXsKICAgcm93ID0gcm93ICsxCiAgIHN1Yi5vbnNldCAgPC0gb25zZXQuZGF0YS5jb3Jbb25zZXQuZGF0YS5jb3IkU3ViSUQgPT0gc3ViamVjdHNbc10sY29sbmFtZXMob25zZXQuZGF0YS5jb3IpID09IHJvaXNbcl1dCiAgIHN1Yi5vZmZzZXQgPC0gb2Zmc2V0LmRhdGEuY29yW29mZnNldC5kYXRhLmNvciRTdWJJRCA9PSBzdWJqZWN0c1tzXSxjb2xuYW1lcyhvZmZzZXQuZGF0YS5jb3IpID09IHJvaXNbcl1dCiAgIGNvcnJlbGF0aW9ucyRTdWJJRFtyb3ddIDwtIHN1YmplY3RzW3NdCiAgIGNvcnJlbGF0aW9ucyRST0lbcm93XSAgIDwtIHJvaXNbcl0KICAgY29ycmVsYXRpb25zJFpbcm93XSAgICAgPC0gZmlzaGVyeihjb3Ioc3ViLm9uc2V0LHN1Yi5vZmZzZXQpKQogfSAgCn0KICAKc3ViLnN1bW1hcnkgPC0gY29ycmVsYXRpb25zICU+JSBncm91cF9ieShTdWJJRCkgJT4lCiAgICAgICAgICAgICAgICAgICBzdW1tYXJpc2UobWVhblogPSBtZWFuKFopKQpSID0gZmlzaGVyejJyKG1lYW4oc3ViLnN1bW1hcnkkbWVhblopKQpTRW1lYW4gPSBzZShzdWIuc3VtbWFyeSRtZWFuWikKY2F0KCdNZWFuIGNvcnJlbGF0aW9uIChyKSBiZXR3ZWVuIG9uc2V0IE4gYW5kIG9mZnNldCBOLTEgKHNlcGFyYXRlZCBieSAxcyBJVEkpIHdpdGhpbiBzdWJqZWN0IGFuZCBST0kgPScsUiwnLCBTRSA9JyxTRW1lYW4sJ1xuJykKCgoKIyMjIyMjIyMjIEIpICMjIyMjIyMjIwojIyBvbnNldCBOIHRvIG9mZnNldCBOCgpzaGFyZWQudHJpYWxzIDwtIGludGVyc2VjdChvbnNldC5kYXRhJFRvdGFsRW5jb2RpbmdUcmlhbCxvZmZzZXQuZGF0YSRUb3RhbEVuY29kaW5nVHJpYWwpCm9uc2V0LmRhdGEuY29yICA8LSBvbnNldC5kYXRhW29uc2V0LmRhdGEkVG90YWxFbmNvZGluZ1RyaWFsICVpbiUgc2hhcmVkLnRyaWFscyxdCm9mZnNldC5kYXRhLmNvciA8LSBvZmZzZXQuZGF0YVtvZmZzZXQuZGF0YSRUb3RhbEVuY29kaW5nVHJpYWwgJWluJSBzaGFyZWQudHJpYWxzLF0KCiMgbm93IHJ1biBwZXIgUk9JIGFuZCBzdWJqZWN0CmNvcnJlbGF0aW9ucyA8LSBkYXRhLmZyYW1lKGFycmF5KDAsYygobGVuZ3RoKHN1YmplY3RzKSpsZW5ndGgocm9pcykpLDMpKSkKY29sbmFtZXMoY29ycmVsYXRpb25zKSA8LSBjKCdTdWJJRCcsJ1JPSScsJ1onKQpyb3cgPSAwCmZvciAociBpbiAxOmxlbmd0aChyb2lzKSkgewogZm9yIChzIGluIDE6bGVuZ3RoKHN1YmplY3RzKSl7CiAgIHJvdyA9IHJvdyArMQogICBzdWIub25zZXQgIDwtIG9uc2V0LmRhdGEuY29yW29uc2V0LmRhdGEuY29yJFN1YklEID09IHN1YmplY3RzW3NdLGNvbG5hbWVzKG9uc2V0LmRhdGEuY29yKSA9PSByb2lzW3JdXQogICBzdWIub2Zmc2V0IDwtIG9mZnNldC5kYXRhLmNvcltvZmZzZXQuZGF0YS5jb3IkU3ViSUQgPT0gc3ViamVjdHNbc10sY29sbmFtZXMob2Zmc2V0LmRhdGEuY29yKSA9PSByb2lzW3JdXQogICBjb3JyZWxhdGlvbnMkU3ViSURbcm93XSA8LSBzdWJqZWN0c1tzXQogICBjb3JyZWxhdGlvbnMkUk9JW3Jvd10gICA8LSByb2lzW3JdCiAgIGNvcnJlbGF0aW9ucyRaW3Jvd10gICAgIDwtIGZpc2hlcnooY29yKHN1Yi5vbnNldCxzdWIub2Zmc2V0KSkKIH0gIAp9CiAgCnN1Yi5zdW1tYXJ5IDwtIGNvcnJlbGF0aW9ucyAlPiUgZ3JvdXBfYnkoU3ViSUQpICU+JQogICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKG1lYW5aID0gbWVhbihaKSkKUiA9IGZpc2hlcnoycihtZWFuKHN1Yi5zdW1tYXJ5JG1lYW5aKSkKU0VtZWFuID0gc2Uoc3ViLnN1bW1hcnkkbWVhblopCmNhdCgnTWVhbiBjb3JyZWxhdGlvbiAocikgYmV0d2VlbiBvbnNldCBOIGFuZCBvZmZzZXQgTiAoc2VwYXJhdGVkIGJ5IDZzIHRyaWFsKSB3aXRoaW4gc3ViamVjdCBhbmQgUk9JID0nLFIsJywgU0UgPScsU0VtZWFuLCdcbicpCgpgYGAKCiMgV2hvbGUgYnJhaW4gYW5hbHlzZXMgIApOb3RlIC0gZWFjaCBjaHVuayB3aWxsIHRha2UgYSBmZXcgbWludXRlcyB0byBydW4uICAKCiMjIE1lbW9yeSBkZXRhaWwKV2hlcmUgYWNyb3NzIHRoZSBicmFpbiBkb2VzIGFjdGl2aXR5IGF0IGxpbmVhcmx5IHNjYWxlIHdpdGggbWVtb3J5IHF1YW50aXR5ICgwLDEsMiwzKSBpbiB0aGUgdXBjb21pbmcgdHJpYWwgKHVzaW5nIG9uc2V0KSBvciB0aGUgcHJlY2VkaW5nIHRyaWFsICh1c2luZyBvZmZzZXQpPyAgICAgCmBgYHtyfQoKY3VyRmlsZSA9ICdtZW1vcnlkZXRhaWxfZWZmZWN0c19vbnNldG9mZnNldC5jc3YnCgojIyMgc2V0IHVwIGRhdGEgZnJhbWUgZm9yIG1peGVkIGVmZmVjdCBtb2RlbCByZXN1bHRzOgptaXhlZC5kYXRhIDwtIGRhdGEuZnJhbWUoYXJyYXkoMCxjKGxlbmd0aChyb2lzKSoyLDgpKSkKY29sbmFtZXMobWl4ZWQuZGF0YSkgPC0gYygnUk9JJywnUGhhc2UnLCdGZWF0dXJlJywnQmV0YScsJ1NFJywnVCcsJ0RmJywnUHZhbHVlJykKcm93ID0gMAoKCiMjIyBsb29wIG92ZXIgb25zZXQgKH4gdXBjb21pbmcgZXZlbnQgbWVtb3J5KSBhbmQgb2Zmc2V0ICh+IHByZWNlZGluZyBldmVudCBtZW1vcnkpCmZvciAocGhhc2UgaW4gMToyKSB7CiAgCiAgIyBnYXRoZXIgZGF0YSBzbyBiZWhhdmlvcmFsIHZhcmlhYmxlcyBhcmUgcmVwbGljYXRlZCBvdmVyIGVhY2ggUk9JIChub3cgYXMgYSBzaW5nbGUgY29sdW1uIGZhY3RvcikKICBpZiAocGhhc2UgPT0gMSkgewogICAgbW9kZWwuZGF0YSA8LSBnYXRoZXIob25zZXQuZGF0YSwgUk9JLCBCZXRhLCByb2lzKQogICAgdGltZSA9ICdPbnNldCcKICB9IGVsc2UgaWYgKHBoYXNlID09IDIpIHsKICAgIG1vZGVsLmRhdGEgPC0gZ2F0aGVyKG9mZnNldC5kYXRhLCBST0ksIEJldGEsIHJvaXMpCiAgICB0aW1lID0gJ09mZnNldCcKICB9CiAgCiAgIyAqKioqKioqKiBydW4gbGluZWFyIG1peGVkIGVmZmVjdHMgbW9kZWwgd2l0aGluIGVhY2ggUk9JIGFuZCBzdG9yZSByZXN1bHRzICoqKioqKioqKioKICBmb3IgKHIgaW4gMTpsZW5ndGgocm9pcykpIHsKICAgIAogICAgdGhpc1JPSSA8LSBhcy5jaGFyYWN0ZXIocm9pc1tyXSkKICAgIAogICAgcm9pRGF0YSA8LSBzdWJzZXQobW9kZWwuZGF0YSwgUk9JID09IHRoaXNST0kpCiAgICByb2lEYXRhJE1lbW9yeURldGFpbCA8LSBzY2FsZShyb2lEYXRhJE1lbW9yeURldGFpbCwgc2NhbGUgPSBGQUxTRSkgIyBtZWFuLWNlbnRlciBtZW1vcnkgcHJlZGljdG9yCiAgICAKICAgICMgcnVuIGxtZXIgYW5kIGdldCBmaXhlZCBlZmZlY3Qgc3RhdHMKICAgIGZ1bGwubW9kZWwgPC0gc3VwcHJlc3NNZXNzYWdlcyhsbWVyKEJldGEgfiBNZW1vcnlEZXRhaWwgKyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKDEgKyBNZW1vcnlEZXRhaWx8fFN1YklEKSwgZGF0YSA9IHJvaURhdGEpKQogICAgc3RhdHMgPC0gc3VtbWFyeShmdWxsLm1vZGVsKSRjb2VmZmljaWVudHMKICAgIG5JViA8LSBucm93KHN0YXRzKSAtIDEgIyBudW1iZXIgb2YgZml4ZWQgZWZmZWN0cywgZGlzY291bnRpbmcgaW50ZXJjZXB0CiAgCiAgICAjIyMgYWRkIGZpeGVkIGVmZmVjdHMgYW5kIHAgdmFsdWVzIHRvIGRhdGEgZnJhbWUKICAgIG1peGVkLmRhdGEkUk9JWyhyb3crMSk6KHJvdytuSVYpXSAgICAgIDwtIHRoaXNST0kKICAgIG1peGVkLmRhdGEkUGhhc2VbKHJvdysxKToocm93K25JVildICAgIDwtIHRpbWUKICAgIG1peGVkLmRhdGEkRmVhdHVyZVsocm93KzEpOihyb3crbklWKV0gIDwtIHJvdy5uYW1lcyhzdGF0cylbMjoobklWKzEpXQogICAgbWl4ZWQuZGF0YVsocm93KzEpOihyb3crbklWKSxjKCJCZXRhIiwiU0UiLCJUIiwiRGYiLCJQdmFsdWUiKV0gPC0gc3RhdHNbMjoobklWKzEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJFc3RpbWF0ZSIsIlN0ZC4gRXJyb3IiLCJ0IHZhbHVlIiwiZGYiLCJQcig+fHR8KSIpXQogICAgcm93ID0gcm93ICsgbklWCiAgfQp9CgojIyMjIEZEUi1jb3JyZWN0IGFsbCBwLXZhbHVlcyBmb3IgbXVsdGlwbGUgY29tcGFyaXNvbnMKbWl4ZWQuZGF0YSRQZmRyIDwtIDEKcC52YWx1ZXMgPC0gbWl4ZWQuZGF0YSRQdmFsdWUKbWl4ZWQuZGF0YSRQZmRyIDwtIHAuYWRqdXN0KHAudmFsdWVzLCBtZXRob2QgPSAiZmRyIikKCiMjIHNhdmUgb3V0IHBhcmFtZXRlciBlc3RpbWF0ZXMgYW5kIHAgdmFsdWVzIGFzIGNzdiBmaWxlIGZvciBjcmVhdGluZyBhIC5uaWkgbWFwIG9mIHJlc3VsdHMKd3JpdGUuY3N2KG1peGVkLmRhdGEsIGZpbGUgPSBjdXJGaWxlLCByb3cubmFtZXMgPSBGQUxTRSkKCmNhdCgnUmVzdWx0cyBzYXZlZCBpbicsY3VyRmlsZSkKCmBgYAoKIyMjIENvbnRyb2wgYW5hbHlzaXMgMQpUaGlzIGFuYWx5c2lzIGlzIHRoZSBzYW1lIGFzIGFib3ZlLCBidXQgd2UgYXJlIGNoZWNraW5nIHRoYXQgdGhlIHJlc3VsdHMgbG9vayBzaW1pbGFyIGlmIHdlIGV4cGxpY2l0bHkgaW5jbHVkZSBtZW1vcnkgZGV0YWlsIG9uIHRyaWFsIE4tMSBvciBOKzEgYXMgYSBjb3ZhcmlhdGUgd2hlbiB0ZXN0aW5nIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBvbnNldC9vZmZzZXQgTiBhbmQgbWVtb3J5IGRldGFpbCBvbiB0cmlhbCBOLiAgCmBgYHtyfQoKY3VyRmlsZSA9ICdtZW1vcnlkZXRhaWxfZWZmZWN0c19jb3ZhcmlhdGUuY3N2JwoKIyMjIHNldCB1cCBkYXRhIGZyYW1lIGZvciBtaXhlZCBlZmZlY3QgbW9kZWwgcmVzdWx0czoKbWl4ZWQuZGF0YSA8LSBkYXRhLmZyYW1lKGFycmF5KDAsYyhsZW5ndGgocm9pcykqMiw4KSkpCmNvbG5hbWVzKG1peGVkLmRhdGEpIDwtIGMoJ1JPSScsJ1BoYXNlJywnRmVhdHVyZScsJ0JldGEnLCdTRScsJ1QnLCdEZicsJ1B2YWx1ZScpCnJvdyA9IDAKCgojIyMgbG9vcCBvdmVyIG9uc2V0IGFuZCBvZmZzZXQKZm9yIChwaGFzZSBpbiAxOjIpIHsKICAKICB0cmlhbHMubiA8LSBjKCkKICB0cmlhbHMubjEgPC0gYygpCiAgICAKICAjIGdhdGhlciBkYXRhIHNvIGJlaGF2aW9yYWwgdmFyaWFibGVzIGFyZSByZXBsaWNhdGVkIG92ZXIgZWFjaCBST0kgKG5vdyBhcyBhIHNpbmdsZSBjb2x1bW4gZmFjdG9yKQogIGlmIChwaGFzZSA9PSAxKSB7CiAgICBtb2RlbC5kYXRhIDwtIG9uc2V0LmRhdGEKICAgIHRpbWUgPSAnT25zZXQnCiAgICAjIG5vdyBsb29wIHRocm91Z2ggYW5kIG9ubHkgcmV0YWluIHRyaWFsIE4gaWYgdHJpYWwgTi0xIChpbW1lZGlhdGVseSBhZGphY2VudCB0byBvbnNldCBOKSBpcyBwcmVzZW50ICh3aXRoaW4gc3ViamVjdCBhbmQgcnVuKQogICAgZm9yIChuIGluIDI6bnJvdyhtb2RlbC5kYXRhKSkgeyAgI2Nhbid0IGluY2x1ZGUgZmlyc3QgdHJpYWwgaW4gTiB2ZWN0b3IKICAgICAgaWYgKG1vZGVsLmRhdGEkRW5jb2RpbmdFdmVudFtuLTFdID09IG1vZGVsLmRhdGEkRW5jb2RpbmdFdmVudFtuXS0xKSB7CiAgICAgICAgICAgdHJpYWxzLm4gIDwtIGModHJpYWxzLm4sIG4pCiAgICAgICAgICAgdHJpYWxzLm4xIDwtIGModHJpYWxzLm4xLCBuLTEpCiAgICAgIH0KICAgIH0KICB9IGVsc2UgaWYgKHBoYXNlID09IDIpIHsKICAgIG1vZGVsLmRhdGEgPC0gb2Zmc2V0LmRhdGEKICAgIHRpbWUgPSAnT2Zmc2V0JwogICAgIyBub3cgbG9vcCB0aHJvdWdoIGFuZCBvbmx5IHJldGFpbiB0cmlhbCBOIGlmIHRyaWFsIE4rMSAoaW1tZWRpYXRlbHkgYWRqYWNlbnQgdG8gb2Zmc2V0IE4pIGlzIHByZXNlbnQgKHdpdGhpbiBzdWJqZWN0IGFuZCBydW4pCiAgICBmb3IgKG4gaW4gMToobnJvdyhtb2RlbC5kYXRhKS0xKSkgeyAgI2Nhbid0IGluY2x1ZGUgZmluYWwgdHJpYWwgTiB2ZWN0b3IKICAgICAgaWYgKG1vZGVsLmRhdGEkRW5jb2RpbmdFdmVudFtuKzFdID09IG1vZGVsLmRhdGEkRW5jb2RpbmdFdmVudFtuXSsxKSB7CiAgICAgICAgICAgdHJpYWxzLm4gIDwtIGModHJpYWxzLm4sIG4pCiAgICAgICAgICAgdHJpYWxzLm4xIDwtIGModHJpYWxzLm4xLCBuKzEpCiAgICAgIH0KICAgIH0KICB9CiAgCiAgIyBmb3JtYXQgZGF0YSBmb3IgTWVtb3J5RGV0YWlsIGFuZCBNZW1vcnlEZXRhaWxOMSBwcmVkaWN0b3JzCiAgbmV3LmRhdGEgICAgPC0gbW9kZWwuZGF0YVt0cmlhbHMubixdCiAgbmV3LmRhdGEubjEgPC0gbW9kZWwuZGF0YVt0cmlhbHMubjEsXQogIG5ldy5kYXRhJE1lbW9yeURldGFpbE4xICA8LSBuZXcuZGF0YS5uMSRNZW1vcnlEZXRhaWwKCiAgbW9kZWwuZGF0YSA8LSBnYXRoZXIobmV3LmRhdGEsIFJPSSwgQmV0YSwgcm9pcykKCiAgCiAgIyAqKioqKioqKiBydW4gbGluZWFyIG1peGVkIGVmZmVjdHMgd2l0aGluIGVhY2ggUk9JIGFuZCBzdG9yZSByZXN1bHRzICoqKioqKioqKioKICBmb3IgKHIgaW4gMTpsZW5ndGgocm9pcykpIHsKICAgIAogICAgdGhpc1JPSSA8LSBhcy5jaGFyYWN0ZXIocm9pc1tyXSkKICAgIAogICAgcm9pRGF0YSA8LSBzdWJzZXQobW9kZWwuZGF0YSwgUk9JID09IHRoaXNST0kpCiAgICByb2lEYXRhJE1lbW9yeURldGFpbCAgIDwtIHNjYWxlKHJvaURhdGEkTWVtb3J5RGV0YWlsLCBzY2FsZSA9IEZBTFNFKSAjIG1lYW4tY2VudGVyIHByZWRpY3RvcgogICAgcm9pRGF0YSRNZW1vcnlEZXRhaWxOMSA8LSBzY2FsZShyb2lEYXRhJE1lbW9yeURldGFpbE4xLCBzY2FsZSA9IEZBTFNFKSAjIG1lYW4tY2VudGVyIHByZWRpY3RvcgogICAKICAgICMgcnVuIGxtZXIgYW5kIGdldCBmaXhlZCBlZmZlY3Qgc3RhdHMKICAgIGZ1bGwubW9kZWwgPC0gc3VwcHJlc3NNZXNzYWdlcyhsbWVyKEJldGEgfiBNZW1vcnlEZXRhaWwgKyBNZW1vcnlEZXRhaWxOMSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICgxICsgTWVtb3J5RGV0YWlsICsgTWVtb3J5RGV0YWlsTjF8fFN1YklEKSwgZGF0YSA9IHJvaURhdGEpKQogICAgc3RhdHMgPC0gc3VtbWFyeShmdWxsLm1vZGVsKSRjb2VmZmljaWVudHMKICAgIG5JViA8LSBucm93KHN0YXRzKSAtIDIgIyBudW1iZXIgb2YgZml4ZWQgZWZmZWN0IHByZWRpY3RvcnMgLSBidXQgZG9uJ3QgbmVlZCB0byBzdG9yZSBjb3ZhcmlhdGUgcmVzdWx0cyBvciBpbnRlcmNlcHQKICAKICAgICMjIyBhZGQgZml4ZWQgZWZmZWN0cyBhbmQgcCB2YWx1ZXMgdG8gZGF0YSBtYXRyaXgKICAgIG1peGVkLmRhdGEkUk9JWyhyb3crMSk6KHJvdytuSVYpXSAgICAgIDwtIHRoaXNST0kKICAgIG1peGVkLmRhdGEkUGhhc2VbKHJvdysxKToocm93K25JVildICAgIDwtIHRpbWUKICAgIG1peGVkLmRhdGEkRmVhdHVyZVsocm93KzEpOihyb3crbklWKV0gIDwtIHJvdy5uYW1lcyhzdGF0cylbMjoobklWKzEpXQogICAgbWl4ZWQuZGF0YVsocm93KzEpOihyb3crbklWKSxjKCJCZXRhIiwiU0UiLCJUIiwiRGYiLCJQdmFsdWUiKV0gPC0gc3RhdHNbMjoobklWKzEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJFc3RpbWF0ZSIsIlN0ZC4gRXJyb3IiLCJ0IHZhbHVlIiwiZGYiLCJQcig+fHR8KSIpXQogICAgcm93ID0gcm93ICsgbklWCiAgfQp9CgojIyMjIEZEUi1jb3JyZWN0IGFsbCBwLXZhbHVlcyBmb3IgbXVsdGlwbGUgY29tcGFyaXNvbnMKbWl4ZWQuZGF0YSRQZmRyIDwtIDEKcC52YWx1ZXMgPC0gbWl4ZWQuZGF0YSRQdmFsdWUKbWl4ZWQuZGF0YSRQZmRyIDwtIHAuYWRqdXN0KHAudmFsdWVzLCBtZXRob2QgPSAiZmRyIikKCiMjIHNhdmUgb3V0IHBhcmFtZXRlciBlc3RpbWF0ZXMgYW5kIHAgdmFsdWVzIGFzIGNzdiBmaWxlIGZvciBjcmVhdGluZyBhIC5uaWkgbWFwIG9mIHJlc3VsdHMKd3JpdGUuY3N2KG1peGVkLmRhdGEsIGZpbGUgPSBjdXJGaWxlLCByb3cubmFtZXMgPSBGQUxTRSkKCmNhdCgnUmVzdWx0cyBzYXZlZCBpbicsY3VyRmlsZSkKCmBgYAoKIyMjIENvbnRyb2wgYW5hbHlzaXMgMgpJbnN0ZWFkIG9mIG1vZGVsaW5nIHRpbWUgcG9pbnRzIGF0IHRoZSBiZWdpbm5pbmcgYW5kIGVuZCBvZiBlYWNoIGV2ZW50LCB0aGlzIGFuYWx5c2lzIG1vZGVscyBlYWNoIGV2ZW50IChmcm9tIG9uc2V0IHRvIG9mZnNldCkgYXMgYSA2cyBlcG9jaC4gIApgYGB7cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIHNpbmdsZSB0cmlhbCBhY3Rpdml0eSBkYXRhICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIGdldCBzdWJqZWN0cycgZGF0YQpzdWJqZWN0cyA8LSBsaXN0LmZpbGVzKHBhdGg9Jy4uL2RhdGEvc2luZ2xlLXRyaWFsLWJldGFzLzZzLWV2ZW50LycsZnVsbC5uYW1lcz1UUlVFLCByZWN1cnNpdmUgPSBUUlVFLCBwYXR0ZXJuID0gJ2Vwb2NoLXdob2xlYnJhaW4nKQphbGxEYXRhICA9IGRvLmNhbGwocmJpbmQsIGxhcHBseShzdWJqZWN0cywgZnVuY3Rpb24oeCkge3JlYWQuY3N2KHgsIGhlYWRlciA9IFRSVUUpfSkpCgphbGxEYXRhJFN1YklEPWFzLmZhY3RvcihhbGxEYXRhJFN1YklEKQphbGxEYXRhJFJPSSA8LSBhcy5mYWN0b3IoYWxsRGF0YSRST0kpCmFsbERhdGEgPC0gYWxsRGF0YVssLWMoNSldICMgcmVtb3ZlIHZveCBudW1iZXJzIC0gbm90IG5lZWRlZCBoZXJlCgpyb2lzID0gbGV2ZWxzKGFsbERhdGEkUk9JKQoKCmNhdCgnTnVtYmVyIG9mIHN1YmplY3RzID0nLCBsZW5ndGgodW5pcXVlKGFsbERhdGEkU3ViSUQpKSwnXG4nKQpjYXQoJ1JlbW92aW5nIGluZmx1ZW50aWFsIGV2ZW50IGJldGFzLCB8enwgPicsIG91dGxpZXJfdmFsdWUsJ1xuJykKCiMgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiogIwoKIyMjIGFkZCB6IHNjb3JlIG9mIGJldGFzIHdpdGhpbiByZWdpb24gYW5kIHN1YmplY3QgdG8gcmVtb3ZlIG91dGxpZXJzOgphbGxEYXRhIDwtIGFsbERhdGEgJT4lCiAgICAgICAgICAgICAgIGdyb3VwX2J5KFN1YklELCBST0kpICU+JQogICAgICAgICAgICAgICBtdXRhdGUoWiA9IHpzY29yZShNZWFuQmV0YSkpCgojIHJlbW92ZSBiZXRhcyB0aGF0IGFyZSA+IFhTRCAtLSBwcmludCB0aGlzIG51bWJlcgphbGxEYXRhLmNsZWFuIDwtIGFsbERhdGEKYWxsRGF0YS5jbGVhbiRNZWFuQmV0YVthYnMoYWxsRGF0YS5jbGVhbiRaKSA+IG91dGxpZXJfdmFsdWVdIDwtIE5BICAjbWFyayBiZXRhIGFzIE5BIGlmIG91dGxpZXIKYWxsRGF0YS5jbGVhbiA8LSBhbGxEYXRhLmNsZWFuWywtYyg2KV0gIyByZW1vdmUgWiB2YWx1ZXMgIAoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKI3NwcmVhZCBkYXRhIHNvIGVhY2ggb2YgdGhlIDIwNCBST0lzIGlzIGluIGl0cyBvd24gY29sdW1uICh0byBhbGlnbiB3aXRoIGJlaGF2aW9yYWwgZGF0YSkKbXlCZXRhcyA9IGRhdGEuZnJhbWUoc3ByZWFkKGFsbERhdGEuY2xlYW4sIFJPSSwgTWVhbkJldGEpKQpteUJldGFzJFRvdGFsRW5jb2RpbmdUcmlhbCA8LSAxOm5Ub3RhbAoKCiMgcmVtb3ZlIGFsbCB0cmlhbHMgd2hlcmUgdGhlcmUgaXMgYXQgbGVhc3Qgb25lIE5BIChhdCBsZWFzdCBvbmUgb2YgdGhlIFJPSXMgaXMgYW4gb3V0bGllciBvbiB0aGF0IHRyaWFsKSAtIAojIHRvIGtlZXAgdGhlIHRyaWFscyBhbmFseXplZCB0aGUgc2FtZSBhY3Jvc3MgcmVnaW9uczoKaWR4QmV0YXMgPC0gY29tcGxldGUuY2FzZXMobXlCZXRhcykKbXlCZXRhcy5jbGVhbiA8LSBteUJldGFzW2lkeEJldGFzLF0KY2F0KCdUb3RhbCBudW1iZXIgb2YgZXZlbnRzIHJlbW92ZWQgPScsc3VtKCFpZHhCZXRhcyksJ291dCBvZicsblRvdGFsLCdcblxuJykKCgojIyMjIyBtZXJnZSBjbGVhbmVkIGJldGFzIHdpdGggYmVoYXZpb3JhbCBkYXRhLCByZW1vdmluZyBjb3JyZXNwb25kaW5nIGJlaGF2aW9yYWwgdHJpYWxzOgpiZWhhdkRhdGEuY2xlYW4gPC0gYmVoYXZEYXRhW2lkeEJldGFzLF0KdGVzdERhdGEgPC0gbWVyZ2UuZGF0YS5mcmFtZShteUJldGFzLmNsZWFuLGJlaGF2RGF0YS5jbGVhbixieT0iVG90YWxFbmNvZGluZ1RyaWFsIikKCgojIHJlbW92ZSBkdXBsaWNhdGUgY29sdW1ucyBmb3Igc3ViamVjdCwgcnVuLCBhbmQgc3R1ZHkgdHJpYWwKdGVzdERhdGEgPC0gdGVzdERhdGFbLC1jKDIsMyw0KV0KY29sbmFtZXModGVzdERhdGEpW2NvbG5hbWVzKHRlc3REYXRhKSA9PSAnU3ViSUQueSddIDwtICdTdWJJRCcKY29sbmFtZXModGVzdERhdGEpW2NvbG5hbWVzKHRlc3REYXRhKSA9PSAnUnVuLnknXSA8LSAnUnVuJwpzdWJqZWN0cyA8LSB1bmlxdWUodGVzdERhdGEkU3ViSUQpCgplcG9jaC5kYXRhICA8LSB0ZXN0RGF0YSAgI3N0b3JlIGV2ZW50IGJldGFzIGFuZCBiZWhhdmlvcmFsIGRhdGEgZm9yIGN1cnJlbnQgdHJpYWwKCmNhdCgnQ2xlYW5lZCBhbmQgbWVyZ2VkIGV2ZW50IGJldGFzIGFuZCBiZWhhdmlvcmFsIHRyaWFsc1xuJykKCmBgYAoKYGBge3J9CgpjdXJGaWxlID0gJ21lbW9yeWRldGFpbF9lZmZlY3RzX2V2ZW50ZXBvY2guY3N2JwoKIyMjIHNldCB1cCBkYXRhIGZyYW1lIGZvciBtaXhlZCBlZmZlY3QgbW9kZWwgcmVzdWx0czoKbWl4ZWQuZGF0YSA8LSBkYXRhLmZyYW1lKGFycmF5KDAsYyhsZW5ndGgocm9pcyksOCkpKQpjb2xuYW1lcyhtaXhlZC5kYXRhKSA8LSBjKCdST0knLCdQaGFzZScsJ0ZlYXR1cmUnLCdCZXRhJywnU0UnLCdUJywnRGYnLCdQdmFsdWUnKQpyb3cgPSAwCgoKbW9kZWwuZGF0YSA8LSBnYXRoZXIoZXBvY2guZGF0YSwgUk9JLCBCZXRhLCByb2lzKQp0aW1lID0gJ0Vwb2NoJwoKIyAqKioqKioqKiBydW4gbGluZWFyIG1peGVkIGVmZmVjdHMgd2l0aGluIGVhY2ggUk9JIGFuZCBzdG9yZSByZXN1bHRzICoqKioqKioqKioKZm9yIChyIGluIDE6bGVuZ3RoKHJvaXMpKSB7CiAgCiAgdGhpc1JPSSA8LSBhcy5jaGFyYWN0ZXIocm9pc1tyXSkKICAKICByb2lEYXRhIDwtIHN1YnNldChtb2RlbC5kYXRhLCBST0kgPT0gdGhpc1JPSSkKICByb2lEYXRhJE1lbW9yeURldGFpbCA8LSBzY2FsZShyb2lEYXRhJE1lbW9yeURldGFpbCwgc2NhbGUgPSBGQUxTRSkgIyBtZWFuLWNlbnRlciBwcmVkaWN0b3IKICAKICAjIHJ1biBsbWVyIGFuZCBnZXQgZml4ZWQgZWZmZWN0IHN0YXRzCiAgZnVsbC5tb2RlbCA8LSBzdXBwcmVzc01lc3NhZ2VzKGxtZXIoQmV0YSB+IE1lbW9yeURldGFpbCArIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKDEgKyBNZW1vcnlEZXRhaWx8fFN1YklEKSwgZGF0YSA9IHJvaURhdGEpKQogIHN0YXRzIDwtIHN1bW1hcnkoZnVsbC5tb2RlbCkkY29lZmZpY2llbnRzCiAgbklWIDwtIG5yb3coc3RhdHMpIC0gMSAjIG51bWJlciBvZiBmaXhlZCBlZmZlY3RzLCBkaXNjb3VudGluZyBpbnRlcmNlcHQKICAgIAogICMjIyBhZGQgZml4ZWQgZWZmZWN0cyBhbmQgcCB2YWx1ZXMgdG8gZGF0YSBtYXRyaXgKICBtaXhlZC5kYXRhJFJPSVsocm93KzEpOihyb3crbklWKV0gICAgICA8LSB0aGlzUk9JCiAgbWl4ZWQuZGF0YSRQaGFzZVsocm93KzEpOihyb3crbklWKV0gICAgPC0gdGltZQogIG1peGVkLmRhdGEkRmVhdHVyZVsocm93KzEpOihyb3crbklWKV0gIDwtIHJvdy5uYW1lcyhzdGF0cylbMjoobklWKzEpXQogIG1peGVkLmRhdGFbKHJvdysxKToocm93K25JViksYygiQmV0YSIsIlNFIiwiVCIsIkRmIiwiUHZhbHVlIildIDwtIHN0YXRzWzI6KG5JVisxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoIkVzdGltYXRlIiwiU3RkLiBFcnJvciIsInQgdmFsdWUiLCJkZiIsIlByKD58dHwpIildCiAgcm93ID0gcm93ICsgbklWCn0KCiMjIyMgRkRSLWNvcnJlY3QgYWxsIHAtdmFsdWVzIGZvciBtdWx0aXBsZSBjb21wYXJpc29ucwptaXhlZC5kYXRhJFBmZHIgPC0gMQpwLnZhbHVlcyA8LSBtaXhlZC5kYXRhJFB2YWx1ZQptaXhlZC5kYXRhJFBmZHIgPC0gcC5hZGp1c3QocC52YWx1ZXMsIG1ldGhvZCA9ICJmZHIiKQoKIyMgc2F2ZSBvdXQgcGFyYW1ldGVyIGVzdGltYXRlcyBhbmQgcCB2YWx1ZXMgYXMgY3N2IGZpbGUgZm9yIGNyZWF0aW5nIGEgLm5paSBtYXAgb2YgcmVzdWx0cwp3cml0ZS5jc3YobWl4ZWQuZGF0YSwgZmlsZSA9IGN1ckZpbGUsIHJvdy5uYW1lcyA9IEZBTFNFKQoKY2F0KCdSZXN1bHRzIHNhdmVkIGluJyxjdXJGaWxlKQoKYGBgCgojIyMgQ29udHJvbCBhbmFseXNpcyAzCkhlcmUsIHdlIGRlbW9uc3RyYXRlIHRoZSBzaW1pbGFyaXR5IG9mIG91ciBvbnNldC0gYW5kIG9mZnNldC1iYXNlZCBhbmFseXNlcyB0byBhIG1vZGVsIHdoZXJlIHdlIGRpc3JlZ2FyZCBhbnkgZGlzdGluY3Rpb24gYmV0d2VlbiBjb3JyZWxhdGVkIG9mZnNldCBOIGFuZCBvbnNldCBOKzEgc2lnbmFscywgbW9kZWxpbmcgdGhlbSB0b2dldGhlciBhcyBhIHNpbmdsZSAxcyBJVEkuIElUSSBhY3Rpdml0eSBpcyBwcmVkaWN0ZWQgYnkgc3Vic2VxdWVudCBtZW1vcnkgZm9yIHRoZSB1cGNvbWluZyBvciBwcmVjZWRpbmcgZXZlbnQuICAKYGBge3J9CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyBzaW5nbGUgdHJpYWwgYWN0aXZpdHkgZGF0YSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBnZXQgc3ViamVjdHMnIGRhdGEKc3ViamVjdHMgPC0gbGlzdC5maWxlcyhwYXRoPScuLi9kYXRhL3NpbmdsZS10cmlhbC1iZXRhcy8xcy1JVEkvJyxmdWxsLm5hbWVzPVRSVUUsIHJlY3Vyc2l2ZSA9IFRSVUUsIHBhdHRlcm4gPSAnSVRJLXdob2xlYnJhaW4nKQphbGxEYXRhICA9IGRvLmNhbGwocmJpbmQsIGxhcHBseShzdWJqZWN0cywgZnVuY3Rpb24oeCkge3JlYWQuY3N2KHgsIGhlYWRlciA9IFRSVUUpfSkpCgphbGxEYXRhJFN1YklEPWFzLmZhY3RvcihhbGxEYXRhJFN1YklEKQphbGxEYXRhJFJPSSA8LSBhcy5mYWN0b3IoYWxsRGF0YSRST0kpCmFsbERhdGEgPC0gYWxsRGF0YVssLWMoNSldICMgcmVtb3ZlIHZveCBudW1iZXJzIC0gbm90IG5lZWRlZCBoZXJlCgpyb2lzID0gbGV2ZWxzKGFsbERhdGEkUk9JKQoKCmNhdCgnTnVtYmVyIG9mIHN1YmplY3RzID0nLCBsZW5ndGgodW5pcXVlKGFsbERhdGEkU3ViSUQpKSwnXG4nKQpjYXQoJ1JlbW92aW5nIGluZmx1ZW50aWFsIElUSSBiZXRhcywgfHp8ID4nLCBvdXRsaWVyX3ZhbHVlLCdcbicpCgoKCiMgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiogIwoKIyMjIGFkZCB6IHNjb3JlIG9mIGJldGFzIHdpdGhpbiByZWdpb24gYW5kIHN1YmplY3QgdG8gcmVtb3ZlIG91dGxpZXIgYm91bmRhcmllczoKYWxsRGF0YSA8LSBhbGxEYXRhICU+JQogICAgICAgICAgICAgICBncm91cF9ieShTdWJJRCwgUk9JKSAlPiUKICAgICAgICAgICAgICAgbXV0YXRlKFogPSB6c2NvcmUoTWVhbkJldGEpKQoKIyByZW1vdmUgYmV0YXMgdGhhdCBhcmUgPiBYU0QgLS0gcHJpbnQgdGhpcyBudW1iZXIKYWxsRGF0YS5jbGVhbiA8LSBhbGxEYXRhCmFsbERhdGEuY2xlYW4kTWVhbkJldGFbYWJzKGFsbERhdGEuY2xlYW4kWikgPiBvdXRsaWVyX3ZhbHVlXSA8LSBOQSAgI21hcmsgYmV0YSBhcyBOQSBpZiBvdXRsaWVyCmFsbERhdGEuY2xlYW4gPC0gYWxsRGF0YS5jbGVhblssLWMoNildICMgcmVtb3ZlIFogdmFsdWVzCgoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojc3ByZWFkIGRhdGEgc28gZWFjaCBvZiB0aGUgMjA0IFJPSXMgaXMgaW4gaXRzIG93biBjb2x1bW4KbXlCZXRhcyA9IGRhdGEuZnJhbWUoc3ByZWFkKGFsbERhdGEuY2xlYW4sIFJPSSwgTWVhbkJldGEpKQpteUJldGFzJFRvdGFsRXZlbnQgPC0gMTpucm93KG15QmV0YXMpCgoKZm9yd2FyZEJlaGF2IDwtIGJlaGF2RGF0YVtiZWhhdkRhdGEkRW5jb2RpbmdFdmVudCA+IDEsXSAgI2lmIHByZWRpY3RpbmcgZm9yd2FyZCBmcm9tIElUSSwgd2UgY2FuIHVzZSBhbGwgYnV0IGJlaGF2aW9yYWwgdHJpYWwgMSAoZmlyc3QgdHJpYWwgcGVyIHJ1bikKZm9yd2FyZEJlaGF2JFRvdGFsRXZlbnQgPC0gMTpucm93KGZvcndhcmRCZWhhdikKYmFja3dhcmRCZWhhdiA8LSBiZWhhdkRhdGFbYmVoYXZEYXRhJEVuY29kaW5nRXZlbnQgPCAyNCxdICNpZiBwcmVkaWN0aW5nIGJhY2t3YXJkIGZyb20gSVRJLCB3ZSBjYW4gdXNlIGFsbCBidXQgYmVoYXZpb3JhbCB0cmlhbCAyNCAoZmluYWwgdHJpYWwgcGVyIHJ1bikKYmFja3dhcmRCZWhhdiRUb3RhbEV2ZW50IDwtIDE6bnJvdyhiYWNrd2FyZEJlaGF2KQoKCiMjIyBDTEVBTiBEQVRBIGFuZCBtZXJnZSB3aXRoIGJlaGF2aW9yCiMgcmVtb3ZlIGFsbCB0cmlhbHMgd2hlcmUgdGhlcmUgaXMgYXQgbGVhc3Qgb25lIE5BIChhdCBsZWFzdCBvbmUgb2YgdGhlIFJPSXMgaXMgYW4gb3V0bGllciBvbiB0aGF0IHRyaWFsKToKaWR4QmV0YXMgPC0gY29tcGxldGUuY2FzZXMobXlCZXRhcykKbXlCZXRhcy5jbGVhbiA8LSBteUJldGFzW2lkeEJldGFzLF0KZm9yd2FyZEJlaGF2LmNsZWFuIDwtIGZvcndhcmRCZWhhdltpZHhCZXRhcyxdCmJhY2t3YXJkQmVoYXYuY2xlYW4gPC0gYmFja3dhcmRCZWhhdltpZHhCZXRhcyxdCgpjYXQoJ1RvdGFsIG51bWJlciBvZiBJVElzIHJlbW92ZWQgPScsbnJvdyhteUJldGFzKS1ucm93KG15QmV0YXMuY2xlYW4pLCdvdXQgb2YnLG5yb3cobXlCZXRhcyksJ1xuXG4nKQoKZm9yd2FyZC5kYXRhIDwtIG1lcmdlLmRhdGEuZnJhbWUobXlCZXRhcy5jbGVhbixmb3J3YXJkQmVoYXYuY2xlYW4sYnk9IlRvdGFsRXZlbnQiKQpiYWNrd2FyZC5kYXRhIDwtIG1lcmdlLmRhdGEuZnJhbWUobXlCZXRhcy5jbGVhbixiYWNrd2FyZEJlaGF2LmNsZWFuLGJ5PSJUb3RhbEV2ZW50IikKCgojIHJlbW92ZSBkdXBsaWNhdGUgY29sdW1ucyBmb3Igc3ViamVjdCwgc3R1ZHkgdHJpYWwsIGFuZCBydW4gLS0tLS0tLS0tLQpmb3J3YXJkLmRhdGEgPC0gZm9yd2FyZC5kYXRhWywtYygyLDMsNCldCmNvbG5hbWVzKGZvcndhcmQuZGF0YSlbY29sbmFtZXMoZm9yd2FyZC5kYXRhKSA9PSAnU3ViSUQueSddIDwtICdTdWJJRCcKY29sbmFtZXMoZm9yd2FyZC5kYXRhKVtjb2xuYW1lcyhmb3J3YXJkLmRhdGEpID09ICdSdW4ueSddIDwtICdSdW4nCgpiYWNrd2FyZC5kYXRhIDwtIGJhY2t3YXJkLmRhdGFbLC1jKDIsMyw0KV0KY29sbmFtZXMoYmFja3dhcmQuZGF0YSlbY29sbmFtZXMoYmFja3dhcmQuZGF0YSkgPT0gJ1N1YklELnknXSA8LSAnU3ViSUQnCmNvbG5hbWVzKGJhY2t3YXJkLmRhdGEpW2NvbG5hbWVzKGJhY2t3YXJkLmRhdGEpID09ICdSdW4ueSddIDwtICdSdW4nCgoKc3ViamVjdHMgPC0gdW5pcXVlKGZvcndhcmQuZGF0YSRTdWJJRCkKCmNhdCgnQ2xlYW5lZCBhbmQgbWVyZ2VkIElUSSBiZXRhcyBhbmQgYmVoYXZpb3JhbCB0cmlhbHNcbicpCgpgYGAKCmBgYHtyfQoKY3VyRmlsZSA9ICdtZW1vcnlkZXRhaWxfZWZmZWN0c19JVEkuY3N2JwoKIyMjIHNldCB1cCBkYXRhIGZyYW1lIGZvciBtaXhlZCBlZmZlY3QgbW9kZWwgcmVzdWx0czoKbWl4ZWQuZGF0YSA8LSBkYXRhLmZyYW1lKGFycmF5KDAsYyhsZW5ndGgocm9pcykqMiw4KSkpCmNvbG5hbWVzKG1peGVkLmRhdGEpIDwtIGMoJ1JPSScsJ1BoYXNlJywnRmVhdHVyZScsJ0JldGEnLCdTRScsJ1QnLCdEZicsJ1B2YWx1ZScpCnJvdyA9IDAKCgojIyMgbG9vcCBvdmVyIG9uc2V0IGFuZCBvZmZzZXQKZm9yIChwaGFzZSBpbiAxOjIpIHsKICAKICAjIGdhdGhlciBkYXRhIHNvIGJlaGF2aW9yYWwgdmFyaWFibGVzIGFyZSByZXBsaWNhdGVkIG92ZXIgZWFjaCBST0kgKG5vdyBhcyBhIHNpbmdsZSBjb2x1bW4gZmFjdG9yKQogIGlmIChwaGFzZSA9PSAxKSB7CiAgICBtb2RlbC5kYXRhIDwtIGdhdGhlcihmb3J3YXJkLmRhdGEsIFJPSSwgQmV0YSwgcm9pcykKICAgIHRpbWUgPSAnVXBjb21pbmcnCiAgfSBlbHNlIGlmIChwaGFzZSA9PSAyKSB7CiAgICBtb2RlbC5kYXRhIDwtIGdhdGhlcihiYWNrd2FyZC5kYXRhLCBST0ksIEJldGEsIHJvaXMpCiAgICB0aW1lID0gJ1ByZWNlZGluZycKICB9CiAgCiAgIyAqKioqKioqKiBydW4gbGluZWFyIG1peGVkIGVmZmVjdHMgd2l0aGluIGVhY2ggUk9JIGFuZCBzdG9yZSByZXN1bHRzICoqKioqKioqKioKICBmb3IgKHIgaW4gMTpsZW5ndGgocm9pcykpIHsKICAgIAogICAgdGhpc1JPSSA8LSBhcy5jaGFyYWN0ZXIocm9pc1tyXSkKICAgIAogICAgcm9pRGF0YSA8LSBzdWJzZXQobW9kZWwuZGF0YSwgUk9JID09IHRoaXNST0kpCiAgICByb2lEYXRhJE1lbW9yeURldGFpbCA8LSBzY2FsZShyb2lEYXRhJE1lbW9yeURldGFpbCwgc2NhbGUgPSBGQUxTRSkgIyBtZWFuLWNlbnRlciBwcmVkaWN0b3IKICAgIAogICAgIyBydW4gbG1lciBhbmQgZ2V0IGZpeGVkIGVmZmVjdCBzdGF0cwogICAgZnVsbC5tb2RlbCA8LSBzdXBwcmVzc01lc3NhZ2VzKGxtZXIoQmV0YSB+IE1lbW9yeURldGFpbCArIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoMSArIE1lbW9yeURldGFpbHx8U3ViSUQpLCBkYXRhID0gcm9pRGF0YSkpCiAgICBzdGF0cyA8LSBzdW1tYXJ5KGZ1bGwubW9kZWwpJGNvZWZmaWNpZW50cwogICAgbklWIDwtIG5yb3coc3RhdHMpIC0gMSAjIG51bWJlciBvZiBmaXhlZCBlZmZlY3RzLCBkaXNjb3VudGluZyBpbnRlcmNlcHQKICAgIAogICAgIyMjIGFkZCBmaXhlZCBlZmZlY3RzIGFuZCBwIHZhbHVlcyB0byBkYXRhIG1hdHJpeAogICAgbWl4ZWQuZGF0YSRST0lbKHJvdysxKToocm93K25JVildICAgICAgPC0gdGhpc1JPSQogICAgbWl4ZWQuZGF0YSRQaGFzZVsocm93KzEpOihyb3crbklWKV0gICAgPC0gdGltZQogICAgbWl4ZWQuZGF0YSRGZWF0dXJlWyhyb3crMSk6KHJvdytuSVYpXSAgPC0gcm93Lm5hbWVzKHN0YXRzKVsyOihuSVYrMSldCiAgICBtaXhlZC5kYXRhWyhyb3crMSk6KHJvdytuSVYpLGMoIkJldGEiLCJTRSIsIlQiLCJEZiIsIlB2YWx1ZSIpXSA8LSBzdGF0c1syOihuSVYrMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiRXN0aW1hdGUiLCJTdGQuIEVycm9yIiwidCB2YWx1ZSIsImRmIiwiUHIoPnx0fCkiKV0KICAgIHJvdyA9IHJvdyArIG5JVgogIH0KfQoKIyMjIyBGRFItY29ycmVjdCBwLXZhbHVlcyBmb3IgbXVsdGlwbGUgY29tcGFyaXNvbnMKbWl4ZWQuZGF0YSRQZmRyIDwtIDEKcC52YWx1ZXMgPC0gbWl4ZWQuZGF0YSRQdmFsdWUKbWl4ZWQuZGF0YSRQZmRyIDwtIHAuYWRqdXN0KHAudmFsdWVzLCBtZXRob2QgPSAiZmRyIikKCgojIyBzYXZlIG91dCBwYXJhbWV0ZXIgZXN0aW1hdGVzIGFuZCBwIHZhbHVlcyBhcyBjc3YgZmlsZSBmb3IgY3JlYXRpbmcgYSAubmlpIG1hcCBvZiByZXN1bHRzCndyaXRlLmNzdihtaXhlZC5kYXRhLCBmaWxlID0gY3VyRmlsZSwgcm93Lm5hbWVzID0gRkFMU0UpCgpjYXQoJ1Jlc3VsdHMgc2F2ZWQgaW4nLGN1ckZpbGUpCgpgYGAKCiMjIFVuaXF1ZSBmZWF0dXJlIGVuY29kaW5nCkFmdGVyIGFjY291bnRpbmcgZm9yIGNvdmFyaWFuY2Ugd2l0aCB0aGUgb3RoZXIgdHdvIGZlYXR1cmVzIGluIHRoZSBtb2RlbCwgd2hlcmUgaXMgb25zZXQvb2Zmc2V0IGFjdGl2aXR5IHVuaXF1ZWx5IHByZWRpY3RlZCBieSBzdWNjZXNzZnVsIGNvbG9yLCBzY2VuZSwgb3Igc291bmQgZW5jb2Rpbmc/ICAKYGBge3J9CgpjdXJGaWxlID0gJ2ZlYXR1cmVfZWZmZWN0c19vbnNldG9mZnNldC5jc3YnCgojIyMgc2V0IHVwIGRhdGEgZnJhbWUgZm9yIG1peGVkIGVmZmVjdCBtb2RlbCByZXN1bHRzOgptaXhlZC5kYXRhIDwtIGRhdGEuZnJhbWUoYXJyYXkoMCxjKGxlbmd0aChyb2lzKSo2LDgpKSkgICMgKjYgKDMgZmVhdHVyZXMgeCAyIHRpbWVzKQpjb2xuYW1lcyhtaXhlZC5kYXRhKSA8LSBjKCdST0knLCdQaGFzZScsJ0ZlYXR1cmUnLCdCZXRhJywnU0UnLCdUJywnRGYnLCdQdmFsdWUnKQpyb3cgPSAwCgoKIyMjIGxvb3Agb3ZlciBvbnNldCBhbmQgb2Zmc2V0CmZvciAocGhhc2UgaW4gMToyKSB7CiAgCiAgIyBnYXRoZXIgZGF0YSBzbyBiZWhhdmlvcmFsIHZhcmlhYmxlcyBhcmUgcmVwbGljYXRlZCBvdmVyIGVhY2ggUk9JIChub3cgYXMgYSBzaW5nbGUgY29sdW1uIGZhY3RvcikKICBpZiAocGhhc2UgPT0gMSkgewogICAgbW9kZWwuZGF0YSA8LSBnYXRoZXIob25zZXQuZGF0YSwgUk9JLCBCZXRhLCByb2lzKQogICAgdGltZSA9ICdPbnNldCcKICB9IGVsc2UgaWYgKHBoYXNlID09IDIpIHsKICAgIG1vZGVsLmRhdGEgPC0gZ2F0aGVyKG9mZnNldC5kYXRhLCBST0ksIEJldGEsIHJvaXMpCiAgICB0aW1lID0gJ09mZnNldCcKICB9CiAgCiAgIyAqKioqKioqKiBydW4gbGluZWFyIG1peGVkIGVmZmVjdHMgd2l0aGluIGVhY2ggUk9JIGFuZCBzdG9yZSByZXN1bHRzIGZvciBlYWNoIGZlYXR1cmUgZml4ZWQgZWZmZWN0ICoqKioqKioqKioKICBmb3IgKHIgaW4gMTpsZW5ndGgocm9pcykpIHsKICAgIAogICAgdGhpc1JPSSA8LSBhcy5jaGFyYWN0ZXIocm9pc1tyXSkKCiAgICByb2lEYXRhIDwtIHN1YnNldChtb2RlbC5kYXRhLCBST0kgPT0gdGhpc1JPSSkKICAgIHJvaURhdGEkU291bmQgIDwtIHNjYWxlKHJvaURhdGEkU291bmQsIHNjYWxlID0gRkFMU0UpICMgbWVhbi1jZW50ZXIKICAgIHJvaURhdGEkQ29sb3IgIDwtIHNjYWxlKHJvaURhdGEkQ29sb3IsIHNjYWxlID0gRkFMU0UpICMgbWVhbi1jZW50ZXIKICAgIHJvaURhdGEkU2NlbmUgIDwtIHNjYWxlKHJvaURhdGEkU2NlbmUsIHNjYWxlID0gRkFMU0UpICMgbWVhbi1jZW50ZXIKICAgIAogICAgIyBydW4gbG1lciwgc2ltcGxpZnkgcmFuZG9tIGVmZmVjdCBzdHJ1Y3R1cmUgYXMgPiAyIGZpeGVkIGVmZmVjdHMsIGFuZCBnZXQgZml4ZWQgZWZmZWN0IHN0YXRzCiAgICBmdWxsLm1vZGVsIDwtIHN1cHByZXNzTWVzc2FnZXMoc3VwcHJlc3NXYXJuaW5ncyhsbWVyKEJldGEgfiBTb3VuZCArIENvbG9yICsgU2NlbmUgKyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoMSArIFNvdW5kICsgQ29sb3IgKyBTY2VuZXx8U3ViSUQpLCBkYXRhID0gcm9pRGF0YSkpKQogICAgZnVsbC5tb2RlbCA8LSBzdXBwcmVzc01lc3NhZ2VzKHN1cHByZXNzV2FybmluZ3MoZ2V0X21vZGVsKHN0ZXAoZnVsbC5tb2RlbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y2UucmFuZG9tID0gVFJVRSwgcmVkdWNlLmZpeGVkID0gRkFMU0UpKSkpCiAgICBzdGF0cyA8LSBzdW1tYXJ5KGZ1bGwubW9kZWwpJGNvZWZmaWNpZW50cwogICAgbklWIDwtIG5yb3coc3RhdHMpIC0gMSAjIG51bWJlciBvZiBmaXhlZCBlZmZlY3RzLCBkaXNjb3VudGluZyBpbnRlcmNlcHQKICAgIAogICAgIyMjIGFkZCBmaXhlZCBlZmZlY3RzIGFuZCBwIHZhbHVlcyB0byBkYXRhIG1hdHJpeAogICAgbWl4ZWQuZGF0YSRST0lbKHJvdysxKToocm93K25JVildICAgICAgPC0gdGhpc1JPSQogICAgbWl4ZWQuZGF0YSRQaGFzZVsocm93KzEpOihyb3crbklWKV0gICAgPC0gdGltZQogICAgbWl4ZWQuZGF0YSRGZWF0dXJlWyhyb3crMSk6KHJvdytuSVYpXSAgPC0gcm93Lm5hbWVzKHN0YXRzKVsyOihuSVYrMSldCiAgICBtaXhlZC5kYXRhWyhyb3crMSk6KHJvdytuSVYpLGMoIkJldGEiLCJTRSIsIlQiLCJEZiIsIlB2YWx1ZSIpXSA8LSBzdGF0c1syOihuSVYrMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoIkVzdGltYXRlIiwiU3RkLiBFcnJvciIsInQgdmFsdWUiLCJkZiIsIlByKD58dHwpIildCiAgICByb3cgPSByb3cgKyBuSVYKICB9Cn0KCiMjIyMgRkRSLWNvcnJlY3QgcC12YWx1ZXMgZm9yIG11bHRpcGxlIGNvbXBhcmlzb25zCm1peGVkLmRhdGEkUGZkciA8LSAxCnAudmFsdWVzIDwtIG1peGVkLmRhdGEkUHZhbHVlCm1peGVkLmRhdGEkUGZkciA8LSBwLmFkanVzdChwLnZhbHVlcywgbWV0aG9kID0gImZkciIpCgojIyBzYXZlIG91dCBwYXJhbWV0ZXIgZXN0aW1hdGVzIGFuZCBwIHZhbHVlcyBhcyBjc3YgZmlsZSBmb3IgY3JlYXRpbmcgYSAubmlpIG1hcCBvZiByZXN1bHRzCndyaXRlLmNzdihtaXhlZC5kYXRhLCBmaWxlID0gY3VyRmlsZSwgcm93Lm5hbWVzID0gRkFMU0UpCgpjYXQoJ1Jlc3VsdHMgc2F2ZWQgaW4nLGN1ckZpbGUpCgpgYGAKCiMjIyBDb250cm9sIGFuYWx5c2lzIC0gZXZlbnQgZXBvY2gKTGlrZSBmb3Igb3ZlcmFsbCBtZW1vcnkgZGV0YWlsLCBoZXJlIHdlIGFyZSBwcmVkaWN0aW5nIGFjdGl2aXR5IHRocm91Z2ggdGhlIGVudGlyZSA2cyB0cmlhbCAob25zZXQgdG8gb2Zmc2V0KSB3aXRoIHN1YnNlcXVlbnQgbWVtb3J5IGZvciBlYWNoIGZlYXR1cmUuICAKYGBge3J9CgpjdXJGaWxlID0gJ2ZlYXR1cmVfZWZmZWN0c19ldmVudGVwb2NoLmNzdicKCiMjIyBzZXQgdXAgZGF0YSBmcmFtZSBmb3IgbWl4ZWQgZWZmZWN0IG1vZGVsIHJlc3VsdHM6Cm1peGVkLmRhdGEgPC0gZGF0YS5mcmFtZShhcnJheSgwLGMobGVuZ3RoKHJvaXMpKjMsOCkpKSAgIyAqMyBmZWF0dXJlcwpjb2xuYW1lcyhtaXhlZC5kYXRhKSA8LSBjKCdST0knLCdQaGFzZScsJ0ZlYXR1cmUnLCdCZXRhJywnU0UnLCdUJywnRGYnLCdQdmFsdWUnKQpyb3cgPSAwCgoKbW9kZWwuZGF0YSA8LSBnYXRoZXIoZXBvY2guZGF0YSwgUk9JLCBCZXRhLCByb2lzKQp0aW1lID0gJ0Vwb2NoJwoKIyAqKioqKioqKiBydW4gbGluZWFyIG1peGVkIGVmZmVjdHMgd2l0aGluIGVhY2ggUk9JIGFuZCBzdG9yZSByZXN1bHRzIGZvciBlYWNoIGZlYXR1cmUgZml4ZWQgZWZmZWN0ICoqKioqKioqKioKZm9yIChyIGluIDE6bGVuZ3RoKHJvaXMpKSB7CiAgCiAgdGhpc1JPSSA8LSBhcy5jaGFyYWN0ZXIocm9pc1tyXSkKICAKICByb2lEYXRhIDwtIHN1YnNldChtb2RlbC5kYXRhLCBST0kgPT0gdGhpc1JPSSkKICByb2lEYXRhJFNvdW5kICA8LSBzY2FsZShyb2lEYXRhJFNvdW5kLCBzY2FsZSA9IEZBTFNFKSAjIG1lYW4tY2VudGVyCiAgcm9pRGF0YSRDb2xvciAgPC0gc2NhbGUocm9pRGF0YSRDb2xvciwgc2NhbGUgPSBGQUxTRSkgIyBtZWFuLWNlbnRlcgogIHJvaURhdGEkU2NlbmUgIDwtIHNjYWxlKHJvaURhdGEkU2NlbmUsIHNjYWxlID0gRkFMU0UpICMgbWVhbi1jZW50ZXIKICAKICAjIHJ1biBsbWVyLCBzaW1wbGlmeSByYW5kb20gZWZmZWN0IHN0cnVjdHVyZSBhcyA+IDIgZml4ZWQgZWZmZWN0cywgYW5kIGdldCBmaXhlZCBlZmZlY3Qgc3RhdHMKICBmdWxsLm1vZGVsIDwtIHN1cHByZXNzTWVzc2FnZXMoc3VwcHJlc3NXYXJuaW5ncyhsbWVyKEJldGEgfiBTb3VuZCArIENvbG9yICsgU2NlbmUgKyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKDEgKyBTb3VuZCArIENvbG9yICsgU2NlbmV8fFN1YklEKSwgZGF0YSA9IHJvaURhdGEpKSkKICBmdWxsLm1vZGVsIDwtIHN1cHByZXNzTWVzc2FnZXMoc3VwcHJlc3NXYXJuaW5ncyhnZXRfbW9kZWwoc3RlcChmdWxsLm1vZGVsLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y2UucmFuZG9tID0gVFJVRSwgcmVkdWNlLmZpeGVkID0gRkFMU0UpKSkpCiAgc3RhdHMgPC0gc3VtbWFyeShmdWxsLm1vZGVsKSRjb2VmZmljaWVudHMKICBuSVYgPC0gbnJvdyhzdGF0cykgLSAxICMgbnVtYmVyIG9mIGZpeGVkIGVmZmVjdHMsIGRpc2NvdW50aW5nIGludGVyY2VwdAogIAogICMjIyBhZGQgZml4ZWQgZWZmZWN0cyBhbmQgcCB2YWx1ZXMgdG8gZGF0YSBtYXRyaXgKICBtaXhlZC5kYXRhJFJPSVsocm93KzEpOihyb3crbklWKV0gICAgICA8LSB0aGlzUk9JCiAgbWl4ZWQuZGF0YSRQaGFzZVsocm93KzEpOihyb3crbklWKV0gICAgPC0gdGltZQogIG1peGVkLmRhdGEkRmVhdHVyZVsocm93KzEpOihyb3crbklWKV0gIDwtIHJvdy5uYW1lcyhzdGF0cylbMjoobklWKzEpXQogIG1peGVkLmRhdGFbKHJvdysxKToocm93K25JViksYygiQmV0YSIsIlNFIiwiVCIsIkRmIiwiUHZhbHVlIildIDwtIHN0YXRzWzI6KG5JVisxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJFc3RpbWF0ZSIsIlN0ZC4gRXJyb3IiLCJ0IHZhbHVlIiwiZGYiLCJQcig+fHR8KSIpXQogIHJvdyA9IHJvdyArIG5JVgp9CgojIyMjIEZEUi1jb3JyZWN0IGFsbCBwLXZhbHVlcyBmb3IgbXVsdGlwbGUgY29tcGFyaXNvbnMKbWl4ZWQuZGF0YSRQZmRyIDwtIDEKcC52YWx1ZXMgPC0gbWl4ZWQuZGF0YSRQdmFsdWUKbWl4ZWQuZGF0YSRQZmRyIDwtIHAuYWRqdXN0KHAudmFsdWVzLCBtZXRob2QgPSAiZmRyIikKCiMjIHNhdmUgb3V0IHBhcmFtZXRlciBlc3RpbWF0ZXMgYW5kIHAgdmFsdWVzIGFzIGNzdiBmaWxlIGZvciBjcmVhdGluZyBhIC5uaWkgbWFwIG9mIHJlc3VsdHMKd3JpdGUuY3N2KG1peGVkLmRhdGEsIGZpbGUgPSBjdXJGaWxlLCByb3cubmFtZXMgPSBGQUxTRSkKCmNhdCgnUmVzdWx0cyBzYXZlZCBpbicsY3VyRmlsZSkKCmBgYAoKIyBST0kgdmlzdWFsaXphdGlvbgojIyBEZWZpbmUgcmVnaW9ucyBhbmQgZGF0YQpOb3RlLCBoZXJlIHdlIGFyZSBwcmVkaWN0aW5nIG1lbW9yeSBvbiB0cmlhbCBOIGZyb20gb25zZXQgTiAqYW5kKiBvZmZzZXQgTiAoaW4gdGhlIHNhbWUgbW9kZWwpIHRvIHRlc3QgYW5kIGNvbnRyYXN0IHVuaXF1ZSB2YXJpYW5jZSBvZiBmaXhlZCBlZmZlY3RzICAgIApgYGB7cn0KCiMjIyMjIyMjIyMjIyBkZWZpbmUgcm9pcyAjIyMjIyMjIyMjIyMjIwojIDEuIFNpZ25pZmljYW50IG9mZnNldCBtZW1vcnkgZGV0YWlsIHJlc3BvbnNlIChhY3Rpdml0eSByZWxhdGVzIHRvIG1lbW9yeSBmb3IgcHJlY2VkaW5nIGV2ZW50KQpISVBQICAgIDwtIGMoJ0xIX0hJUFAnKQoKIyAzLiBTaWduaWZpY2FudCBvbnNldCBtZW1vcnkgZGV0YWlsIHJlc3BvbnNlIChhY3Rpdml0eSByZWxhdGVzIHRvIG1lbW9yeSBmb3IgdXBjb21pbmcgZXZlbnQpCklGRyAgIDwtIGMoJ0xIX0NvbnRBX1BGQ2xfMicsJ0xIX0RlZmF1bHRCX1BGQ3ZfNCcsJ0xIX0NvbnRBX1BGQ2xfMScpCgojIDIuIFNpZ25pZmljYW50IGZlYXR1cmUgb25zZXQgZWZmZWN0cyAoYWN0aXZpdHkgcmVsYXRlcyB0byBtZW1vcnkgZm9yIHVwY29taW5nIGV2ZW50KQpWVEMgICA8LSBjKCdSSF9Eb3JzQXR0bkFfVGVtcE9jY18xJywnUkhfTGltYmljQV9UZW1wUG9sZV80JywnUkhfVmlzQ2VudF9FeFN0cl8yJykgICMgQ29sb3IKQVVEICAgPC0gYygnUkhfU29tTW90Ql9BdWRfMScsJ0xIX1NvbU1vdEJfQXVkXzEnLCdSSF9Tb21Nb3RCX0F1ZF8yJywnTEhfU29tTW90Ql9BdWRfMicpICAjIFNvdW5kCk1UTCAgIDwtIGMoJ1JIX0RlZmF1bHRDX1BIQ18xJywnTEhfRGVmYXVsdENfUEhDXzEnLCdSSF9EZWZhdWx0Q19Sc3BfMScsJ0xIX0RlZmF1bHRDX1JzcF8xJykgICMgU2NlbmUKCm15Um9pcyA8LSBjKEhJUFAsSUZHLFZUQyxBVUQsTVRMKQpuZXdSb2lzIDwtIGMoJ0hJUFAnLCdJRkcnLCdWVEMnLCdBVUQnLCdNVEwnKQpteWNvbG9ycyA8LSBjKCIjN2M2N2EyIiwiI2ZmOTAwMCIsIiNGNjRGNTkiLCIjMUQ5NzZDIiwiIzAwOUZGRiIpCgpjYXQoJ1JlZ2lvbnMgYW5hbHl6ZWQ6XG5cbicsCiAgICAnSElQUDonLEhJUFAsJ1xuJywKICAgICdJRkc6JyxJRkcsJ1xuJywKICAgICdWVEM6JyxWVEMsJ1xuJywKICAgICdBVUQ6JyxBVUQsJ1xuJywKICAgICdNVEw6JyxNVEwsJ1xuJykKCgojIyMgbWFrZSBzdXJlIGVhY2ggdHJpYWwgdG8tYmUtbW9kZWxlZCBoYXMgYW4gYXNzb2NpYXRlZCBvbnNldCBhbmQgb2Zmc2V0IGJldGEKbWF0Y2hpbmdfdHJpYWxzIDwtIGludGVyc2VjdChvbnNldC5kYXRhJFRvdGFsRW5jb2RpbmdUcmlhbCxvZmZzZXQuZGF0YSRUb3RhbEVuY29kaW5nVHJpYWwpCm9uc2V0LmRhdGEucm9pICA8LSBvbnNldC5kYXRhW29uc2V0LmRhdGEkVG90YWxFbmNvZGluZ1RyaWFsICVpbiUgbWF0Y2hpbmdfdHJpYWxzLF0Kb2Zmc2V0LmRhdGEucm9pIDwtIG9mZnNldC5kYXRhW29mZnNldC5kYXRhJFRvdGFsRW5jb2RpbmdUcmlhbCAlaW4lIG1hdGNoaW5nX3RyaWFscyxdCmJlaGF2LnJvaSA8LSBvbnNldC5kYXRhLnJvaVssMjA2OjI0NF0gI2dyYWIgYmVoYXZpb3JhbCBkYXRhIGZvciBhbGwgdHJpYWxzIHdpdGggdmFsaWQgb25zZXRzICYgb2Zmc2V0cwoKCiMjIyMjIyMjIyMgYXZlcmFnZSBiZXRhcyB3aXRoaW4gdGhlc2UgbmV3IFJPSXMgIyMjIyMjIyMjIyMKCiMjIyMjIG9uc2V0IGRhdGEgZm9yIHJvaXMKb25zZXQuZGF0YS5yb2kgPC0gZ2F0aGVyKG9uc2V0LmRhdGEucm9pLCBST0ksIEJldGEsIG15Um9pcykKbmxlZnQgPC0gbGVuZ3RoKHJvaXMpIC0gbGVuZ3RoKG15Um9pcykgKyAxCm9uc2V0LmRhdGEucm9pIDwtIG9uc2V0LmRhdGEucm9pWywtYygxOm5sZWZ0KV0gI3JlbW92ZSBvdGhlciByZWdpb25zCgojIyMgYWxsb2NhdGUgJ25ldycgUk9JCm9uc2V0LmRhdGEucm9pJE5ld1JPSVtvbnNldC5kYXRhLnJvaSRST0kgJWluJSBISVBQXSA8LSAnSElQUCcKb25zZXQuZGF0YS5yb2kkTmV3Uk9JW29uc2V0LmRhdGEucm9pJFJPSSAlaW4lIElGR10gPC0gJ0lGRycKb25zZXQuZGF0YS5yb2kkTmV3Uk9JW29uc2V0LmRhdGEucm9pJFJPSSAlaW4lIFZUQ10gPC0gJ1ZUQycKb25zZXQuZGF0YS5yb2kkTmV3Uk9JW29uc2V0LmRhdGEucm9pJFJPSSAlaW4lIEFVRF0gPC0gJ0FVRCcKb25zZXQuZGF0YS5yb2kkTmV3Uk9JW29uc2V0LmRhdGEucm9pJFJPSSAlaW4lIE1UTF0gPC0gJ01UTCcKCiMjIyBub3cgc3VtbWFyaXNlIGFjdGl2aXR5IHdpdGhpbiB0aGVzZSBuZXcgUk9JcyBwZXIgdHJpYWwKbW9kZWwuZGF0YSA8LSBvbnNldC5kYXRhLnJvaQpncm91cGVkLmRhdGEgPC0gbW9kZWwuZGF0YSAlPiUgZ3JvdXBfYnkoU3ViSUQsU3R1ZHlUcmlhbCxOZXdST0kpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJpc2UoTmV3QmV0YSA9IG1lYW4oQmV0YSkpCmdyb3VwZWQuZGF0YSA8LSBzcHJlYWQoZ3JvdXBlZC5kYXRhLCBOZXdST0ksIE5ld0JldGEpCgpteVJPSURhdGEgPC0gbWVyZ2UoZ3JvdXBlZC5kYXRhLGJlaGF2LnJvaSkKbW9kZWwuZGF0YSA8LSBnYXRoZXIobXlST0lEYXRhLCBST0ksIEJldGEsIEFVRDpWVEMpCm1vZGVsLmRhdGEub25zZXQgPC0gbW9kZWwuZGF0YQoKCiMjIyMjIG9mZnNldCBkYXRhIGZvciByb2lzCm9mZnNldC5kYXRhLnJvaSA8LSBnYXRoZXIob2Zmc2V0LmRhdGEucm9pLCBST0ksIEJldGEsIG15Um9pcykKbmxlZnQgPC0gbGVuZ3RoKHJvaXMpIC0gbGVuZ3RoKG15Um9pcykgKyAxCm9mZnNldC5kYXRhLnJvaSA8LSBvZmZzZXQuZGF0YS5yb2lbLC1jKDE6bmxlZnQpXQoKIyMjIGFsbG9jYXRlICduZXcnIFJPSQpvZmZzZXQuZGF0YS5yb2kkTmV3Uk9JW29mZnNldC5kYXRhLnJvaSRST0kgJWluJSBISVBQXSA8LSAnSElQUCcKb2Zmc2V0LmRhdGEucm9pJE5ld1JPSVtvZmZzZXQuZGF0YS5yb2kkUk9JICVpbiUgSUZHXSA8LSAnSUZHJwpvZmZzZXQuZGF0YS5yb2kkTmV3Uk9JW29mZnNldC5kYXRhLnJvaSRST0kgJWluJSBWVENdIDwtICdWVEMnCm9mZnNldC5kYXRhLnJvaSROZXdST0lbb2Zmc2V0LmRhdGEucm9pJFJPSSAlaW4lIEFVRF0gPC0gJ0FVRCcKb2Zmc2V0LmRhdGEucm9pJE5ld1JPSVtvZmZzZXQuZGF0YS5yb2kkUk9JICVpbiUgTVRMXSA8LSAnTVRMJwoKIyMjIG5vdyBzdW1tYXJpc2Ugd2l0aGluIHRoZXNlIG5ldyBST0lzCm1vZGVsLmRhdGEgPC0gb2Zmc2V0LmRhdGEucm9pCmdyb3VwZWQuZGF0YSA8LSBtb2RlbC5kYXRhICU+JSBncm91cF9ieShTdWJJRCxTdHVkeVRyaWFsLE5ld1JPSSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZShOZXdCZXRhID0gbWVhbihCZXRhKSkKZ3JvdXBlZC5kYXRhIDwtIHNwcmVhZChncm91cGVkLmRhdGEsIE5ld1JPSSwgTmV3QmV0YSkKCm15Uk9JRGF0YSA8LSBtZXJnZShncm91cGVkLmRhdGEsYmVoYXYucm9pKQptb2RlbC5kYXRhIDwtIGdhdGhlcihteVJPSURhdGEsIFJPSSwgQmV0YSwgQVVEOlZUQykKbW9kZWwuZGF0YS5vZmZzZXQgPC0gbW9kZWwuZGF0YQoKCiMjIyMjIyBjb21iaW5lZCBvbnNldCBhbmQgb2Zmc2V0IGRhdGEKbW9kZWwuZGF0YS5vbnNldCRQaGFzZSA8LSAnT25zZXQnCm1vZGVsLmRhdGEub2Zmc2V0JFBoYXNlIDwtICdPZmZzZXQnCgptb2RlbC5kYXRhIDwtIHJiaW5kKG1vZGVsLmRhdGEub25zZXQsbW9kZWwuZGF0YS5vZmZzZXQpCgpgYGAKCiMjIE9uc2V0L09mZnNldCBzdWJzZXF1ZW50IG1lbW9yeQpOb3cgd2UgYXJlIHByZWRpY3RpbmcgbWVtb3J5IGRldGFpbCBvciBmZWF0dXJlIG1lbW9yeSBvbiB0cmlhbCBOIHdpdGggb25zZXQgTiBhbmQgb2Zmc2V0IE4gcmVncmVzc29ycyB0byBnZXQgdW5pcXVlIHZhcmlhbmNlIGFuZCB0byBydW4gb2Zmc2V0IC0gb25zZXQgY29udHJhc3QgcGVyIFJPSSAgCmBgYHtyLCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gM30KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwptaXhlZC5kYXRhIDwtIGRhdGEuZnJhbWUoYXJyYXkoMCxjKGxlbmd0aChuZXdSb2lzKSoyLDcpKSkKY29sbmFtZXMobWl4ZWQuZGF0YSkgPC0gYygnUk9JJywnRmVhdHVyZScsJ0JldGEnLCdTRScsJ1QnLCdEZicsJ1B2YWx1ZScpCnJvdyA9IDAKCmZvciAociBpbiAxOmxlbmd0aChuZXdSb2lzKSkgewogIAogIHRoaXMucm9pIDwtIG5ld1JvaXNbcl0KICAKICByb2lEYXRhIDwtIHN1YnNldChtb2RlbC5kYXRhLCBST0kgPT0gdGhpcy5yb2kpCiAgcm9pRGF0YSA8LSBzcHJlYWQocm9pRGF0YSwgUGhhc2UsIEJldGEpICAjbWFrZSBvbnNldCBhbmQgb2Zmc2V0IGFjdGl2aXR5IGRpZmZlcmVudCBjb2x1bW5zCiAgcm9pRGF0YSRPbnNldCAgPC0gc2NhbGUocm9pRGF0YSRPbnNldCwgc2NhbGUgPSBUUlVFKSAjIHotc2NvcmUgZm9yIGRpcmVjdCBjb21wYXJpc29uIG9mIGJldGFzCiAgcm9pRGF0YSRPZmZzZXQgPC0gc2NhbGUocm9pRGF0YSRPZmZzZXQsIHNjYWxlID0gVFJVRSkgIyB6LXNjb3JlIGZvciBkaXJlY3QgY29tcGFyaXNvbiBvZiBiZXRhcwogIAogIGlmICh0aGlzLnJvaSA9PSAnSElQUCcgfCB0aGlzLnJvaSA9PSAnSUZHJykgeyAgIyBNZW1vcnkgRGV0YWlsCiAgICAgICByb2lEYXRhJE1lbW9yeURldGFpbCA8LSBzY2FsZShyb2lEYXRhJE1lbW9yeURldGFpbCwgc2NhbGUgPSBUUlVFKSAjIHotc2NvcmUgZm9yIGRpcmVjdCB2aXN1YWwgY29tcGFyaXNvbiBhY3Jvc3MgZmVhdHVyZXMKICAgICAgICMgcnVuIGxtZXIsIHNpbXBsaWZ5IHJhbmRvbSBlZmZlY3Qgc3RydWN0dXJlLCBhbmQgZ2V0IGZpeGVkIGVmZmVjdCBzdGF0cwogICAgICAgZnVsbC5tb2RlbCA8LSBzdXBwcmVzc01lc3NhZ2VzKGxtZXIoTWVtb3J5RGV0YWlsIH4gT25zZXQgKyBPZmZzZXQgKyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoMSArIE9uc2V0ICsgT2Zmc2V0fHxTdWJJRCksIGRhdGEgPSByb2lEYXRhKSkKICAgICAgIAogIH0gZWxzZSBpZiAodGhpcy5yb2kgPT0gJ1ZUQycpIHsgICMgQ29sb3IKICAgICAgIHJvaURhdGEkQ29sb3IgPC0gc2NhbGUocm9pRGF0YSRDb2xvciwgc2NhbGUgPSBUUlVFKSAjIHotc2NvcmUgZm9yIGRpcmVjdCB2aXN1YWwgY29tcGFyaXNvbiBhY3Jvc3MgZmVhdHVyZXMKICAgICAgICMgcnVuIGxtZXIsIHNpbXBsaWZ5IHJhbmRvbSBlZmZlY3Qgc3RydWN0dXJlLCBhbmQgZ2V0IGZpeGVkIGVmZmVjdCBzdGF0cwogICAgICAgZnVsbC5tb2RlbCA8LSBzdXBwcmVzc01lc3NhZ2VzKGxtZXIoQ29sb3IgfiBPbnNldCArIE9mZnNldCArIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKDEgKyBPbnNldCArIE9mZnNldHx8U3ViSUQpLCBkYXRhID0gcm9pRGF0YSkpCgogIH0gZWxzZSBpZiAodGhpcy5yb2kgPT0gJ0FVRCcpIHsgICMgU291bmQKICAgICAgIHJvaURhdGEkU291bmQgPC0gc2NhbGUocm9pRGF0YSRTb3VuZCwgc2NhbGUgPSBUUlVFKSAjIHotc2NvcmUgZm9yIGRpcmVjdCB2aXN1YWwgY29tcGFyaXNvbiBhY3Jvc3MgZmVhdHVyZXMKICAgICAgICMgcnVuIGxtZXIsIHNpbXBsaWZ5IHJhbmRvbSBlZmZlY3Qgc3RydWN0dXJlLCBhbmQgZ2V0IGZpeGVkIGVmZmVjdCBzdGF0cwogICAgICAgZnVsbC5tb2RlbCA8LSBzdXBwcmVzc01lc3NhZ2VzKGxtZXIoU291bmQgfiBPbnNldCArIE9mZnNldCArIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKDEgKyBPbnNldCArIE9mZnNldHx8U3ViSUQpLCBkYXRhID0gcm9pRGF0YSkpCgogIH0gZWxzZSBpZiAodGhpcy5yb2kgPT0gJ01UTCcpIHsgICMgU2NlbmUKICAgICAgIHJvaURhdGEkU2NlbmUgPC0gc2NhbGUocm9pRGF0YSRTY2VuZSwgc2NhbGUgPSBUUlVFKSAjIHotc2NvcmUgZm9yIGRpcmVjdCB2aXN1YWwgY29tcGFyaXNvbiBhY3Jvc3MgZmVhdHVyZXMKICAgICAgICMgcnVuIGxtZXIsIHNpbXBsaWZ5IHJhbmRvbSBlZmZlY3Qgc3RydWN0dXJlLCBhbmQgZ2V0IGZpeGVkIGVmZmVjdCBzdGF0cwogICAgICAgZnVsbC5tb2RlbCA8LSBzdXBwcmVzc01lc3NhZ2VzKGxtZXIoU2NlbmUgfiBPbnNldCArIE9mZnNldCArIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKDEgKyBPbnNldCArIE9mZnNldHx8U3ViSUQpLCBkYXRhID0gcm9pRGF0YSkpCiAgfQogIHN0YXRzIDwtIHN1bW1hcnkoZnVsbC5tb2RlbCkkY29lZmZpY2llbnRzCiAgbklWIDwtIG5yb3coc3RhdHMpIC0gMSAjIG51bWJlciBvZiBmaXhlZCBlZmZlY3RzLCBkaXNjb3VudGluZyBpbnRlcmNlcHQKICAgIAogICMgb25lLXNhbXBsZSB0LXRlc3QgKGZpcnN0IHJlZ3Jlc3NvciBpcyBpbnRlcmNlcHQpIGFuZCBlZmZlY3Qgc2l6ZQogIGNvbiA8LSBjb250ZXN0KGZ1bGwubW9kZWwsIGMoMCwtMSwxKSwgam9pbnQgPSBGQUxTRSwgZGRmID0gIlNhdHRlcnRod2FpdGUiKQogIHByaW50KGthYmxlKGNvbiwgCiAgICAgICAgICAgICAgZm9ybWF0PSJwYW5kb2MiLCBjYXB0aW9uPXBhc3RlKHRoaXMucm9pLCc6IG9mZnNldCAtIG9uc2V0JyxzZXA9IiIpKSkKCiAgIyMjIGFkZCBmaXhlZCBlZmZlY3RzIGFuZCBwIHZhbHVlcyB0byBkYXRhIG1hdHJpeAogIG1peGVkLmRhdGEkUk9JWyhyb3crMSk6KHJvdytuSVYpXSAgICAgIDwtIGFzLmNoYXJhY3Rlcih0aGlzLnJvaSkKICBtaXhlZC5kYXRhJEZlYXR1cmVbKHJvdysxKToocm93K25JVildICA8LSByb3cubmFtZXMoc3RhdHMpWzI6KG5JVisxKV0KICBtaXhlZC5kYXRhWyhyb3crMSk6KHJvdytuSVYpLGMoIkJldGEiLCJTRSIsIlQiLCJEZiIsIlB2YWx1ZSIpXSA8LSBzdGF0c1syOihuSVYrMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJFc3RpbWF0ZSIsIlN0ZC4gRXJyb3IiLCJ0IHZhbHVlIiwiZGYiLCJQcig+fHR8KSIpXQogIHJvdyA9IHJvdyArIG5JVgp9CgoKIyBvcmRlciBmYWN0b3JzIGZvciBwbG90dGluZwptaXhlZC5kYXRhJFJPSSAgICAgPC0gZmFjdG9yKG1peGVkLmRhdGEkUk9JLCBsZXZlbHM9bmV3Um9pcykKbWl4ZWQuZGF0YSRGZWF0dXJlIDwtIGZhY3RvcihtaXhlZC5kYXRhJEZlYXR1cmUsIGxldmVscz1jKCdPbnNldCcsJ09mZnNldCcpKQoKIyMjIHByaW50IGZ1bGwgb3V0cHV0OgpwcmludChrYWJsZShtaXhlZC5kYXRhLGZvcm1hdD0icGFuZG9jIixjYXB0aW9uID0gJ01lbW9yeSBvbnNldCBhbmQgb2Zmc2V0IGZpeGVkIGVmZmVjdHMgZm9yIFJPSXMnKSkKCgojIyMjIyMgcGxvdApnZ3Bsb3QobWl4ZWQuZGF0YSwgYWVzKHg9Uk9JLCB5PUJldGEsIGZpbGw9RmVhdHVyZSkpICsKICAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCxzaXplPTEpICsKICAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgYWxwaGEgPSAwLjc1LCBzaXplID0gMSwgd2lkdGggPSAwLjc1LAogICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgxKSkgKwogICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNCRkJGQkYiLCIjNDA0MDQwIikpICsKICAgICBnZ3RpdGxlKCJVbmlxdWUgZWZmZWN0cyBvZiBwcmUtIGFuZCBwb3N0LWV2ZW50IGFjdGl2aXR5XG5vbiBzdWJzZXF1ZW50IG1lbW9yeSIpICsKICAgICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gQmV0YSAtIFNFLCB5bWF4ID0gQmV0YSArIFNFKSwgd2lkdGggPSAwLjQsIHNpemUgPSAxLAogICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDEpKSArCiAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZT0yOCwgbWFyZ2luPW1hcmdpbigwLDAsMjAsMCkpLAogICAgICAgICAgYXhpcy5saW5lLnggPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmxhY2siLCBzaXplID0gMSksIGF4aXMubGluZS55ID0gZWxlbWVudF9saW5lKGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDEpLAogICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMiksIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjYpLAogICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgdGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHk9IkhlbHZldGljYSIpLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbigxLCAxLCAxLCAxLCAiY20iKSkKCiNnZ3NhdmUoJ1JvaXMucG5nJyxwbG90PWxhc3RfcGxvdCgpLHdpZHRoPTExLGhlaWdodD01LHVuaXRzPSJpbiIsZHBpPTUwMCkgCgpgYGAKCiMjIyBNb2R1bGF0aW9uIG9mIG9uc2V0L29mZnNldCByZWxhdGlvbnNoaXAgYnkgbWVtb3J5IGRldGFpbApIZXJlLCB3ZSBhcmUgdGVzdGluZyBpZiB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gb25zZXQgYWN0aXZpdHkgb2YgSUZHL1ZUQy9BVUQvUEhDLVJTQyBhbmQgSElQUCBvZmZzZXQgYWN0aXZpdHkgaXMgbW9kdWxhdGVkIGJ5IHN1YnNlcXVlbnQgbWVtb3J5IGRldGFpbCAoc3Ryb25nZXN0IHN5bmNocm9ueSBpZiBtb3JlIGZlYXR1cmVzIHJlY2FsbGVkPykKYGBge3IsIGZpZy53aWR0aCA9IDUsIGZpZy5oZWlnaHQgPSAyLjV9CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKbWl4ZWQuZGF0YSA8LSBkYXRhLmZyYW1lKGFycmF5KDAsYyg0KjMsNykpKQpjb2xuYW1lcyhtaXhlZC5kYXRhKSA8LSBjKCdST0knLCdQcmVkaWN0b3InLCdCZXRhJywnU0UnLCdUJywnRGYnLCdQdmFsdWUnKQpyb3cgPSAwCgpuZXcuZGYgPC0gbW9kZWwuZGF0YVttb2RlbC5kYXRhJFJPSSA9PSAnSElQUCcgJiBtb2RlbC5kYXRhJFBoYXNlID09ICdPZmZzZXQnLGMoIlN1YklEIiwiQmV0YSIsIk1lbW9yeURldGFpbCIpXQpjb2xuYW1lcyhuZXcuZGYpWzJdIDwtICdISVBQJwpuZXcuZGYkSUZHIDwtIG1vZGVsLmRhdGFbbW9kZWwuZGF0YSRST0kgPT0gJ0lGRycgJiBtb2RlbC5kYXRhJFBoYXNlID09ICdPbnNldCcsYygiQmV0YSIpXQpuZXcuZGYkVlRDIDwtIG1vZGVsLmRhdGFbbW9kZWwuZGF0YSRST0kgPT0gJ1ZUQycgJiBtb2RlbC5kYXRhJFBoYXNlID09ICdPbnNldCcsYygiQmV0YSIpXQpuZXcuZGYkQVVEIDwtIG1vZGVsLmRhdGFbbW9kZWwuZGF0YSRST0kgPT0gJ0FVRCcgJiBtb2RlbC5kYXRhJFBoYXNlID09ICdPbnNldCcsYygiQmV0YSIpXQpuZXcuZGYkTVRMIDwtIG1vZGVsLmRhdGFbbW9kZWwuZGF0YSRST0kgPT0gJ01UTCcgJiBtb2RlbC5kYXRhJFBoYXNlID09ICdPbnNldCcsYygiQmV0YSIpXQpuZXcuZGZbLDI6N10gPC0gc2NhbGUobmV3LmRmWywyOjddLCBzY2FsZSA9IFRSVUUpICMgeiBzY29yZSBhbGwgdmFyaWFibGVzCgoKZm9yIChyIGluIDE6KGxlbmd0aChuZXdSb2lzKS0xKSkgewogIAogIGlmIChyID09IDEpIHsgICMgSUZHCiAgICAgICBmdWxsLm1vZGVsIDwtIHN1cHByZXNzTWVzc2FnZXMobG1lcihISVBQIH4gSUZHKk1lbW9yeURldGFpbCArIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoMSArIElGRypNZW1vcnlEZXRhaWx8fFN1YklEKSwgZGF0YSA9IG5ldy5kZikpCiAgfSBlbHNlIGlmIChyID09IDIpIHsgICMgVlRDCiAgICAgICBmdWxsLm1vZGVsIDwtIHN1cHByZXNzTWVzc2FnZXMobG1lcihISVBQIH4gVlRDKk1lbW9yeURldGFpbCArIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoMSArIFZUQypNZW1vcnlEZXRhaWx8fFN1YklEKSwgZGF0YSA9IG5ldy5kZikpCiAgfSBlbHNlIGlmIChyID09IDMpIHsgICMgQVVECiAgICAgICBmdWxsLm1vZGVsIDwtIHN1cHByZXNzTWVzc2FnZXMobG1lcihISVBQIH4gQVVEKk1lbW9yeURldGFpbCArIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoMSArIEFVRCpNZW1vcnlEZXRhaWx8fFN1YklEKSwgZGF0YSA9IG5ldy5kZikpCiAgfSBlbHNlIGlmIChyID09IDQpIHsgICMgTVRMCiAgICAgICBmdWxsLm1vZGVsIDwtIHN1cHByZXNzTWVzc2FnZXMobG1lcihISVBQIH4gTVRMKk1lbW9yeURldGFpbCArIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoMSArIE1UTCpNZW1vcnlEZXRhaWx8fFN1YklEKSwgZGF0YSA9IG5ldy5kZikpCiAgfQogIHN0YXRzIDwtIHN1bW1hcnkoZnVsbC5tb2RlbCkkY29lZmZpY2llbnRzCiAgbklWIDwtIG5yb3coc3RhdHMpIC0gMSAjcmVtb3ZpbmcgaW50ZXJjZXB0CgogICMjIyBhZGQgZml4ZWQgZWZmZWN0cyBhbmQgcCB2YWx1ZXMgdG8gZGF0YSBtYXRyaXgKICBtaXhlZC5kYXRhJFJPSVsocm93KzEpOihyb3crbklWKV0gICAgICAgIDwtIGFzLmNoYXJhY3RlcihuZXdSb2lzW3IrMV0pCiAgbWl4ZWQuZGF0YSRQcmVkaWN0b3JbKHJvdysxKToocm93K25JVildICA8LSByb3cubmFtZXMoc3RhdHMpWzI6KG5JVisxKV0KICBtaXhlZC5kYXRhWyhyb3crMSk6KHJvdytuSVYpLGMoIkJldGEiLCJTRSIsIlQiLCJEZiIsIlB2YWx1ZSIpXSA8LSBzdGF0c1syOihuSVYrMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiRXN0aW1hdGUiLCJTdGQuIEVycm9yIiwidCB2YWx1ZSIsImRmIiwiUHIoPnx0fCkiKV0KICByb3cgPSByb3cgKyBuSVYKfQoKIyMjIHByaW50IGZ1bGwgb3V0cHV0OgpwcmludChrYWJsZShtaXhlZC5kYXRhLGZvcm1hdD0icGFuZG9jIixjYXB0aW9uID0gJ0hpcHAgT2Zmc2V0IH4gW1JPSV0gT25zZXQgKiBNZW1vcnkgRGV0YWlsJykpCgpgYGAKCiMjIEhpcHBvY2FtcGFsIGZlYXR1cmUgY29tYmluYXRpb25zCldoaWNoIHNwZWNpZmljIGZlYXR1cmUgY29tYmluYXRpb25zIGlzIGhpcHBvY2FtcHVzIHNlbnNpdGl2ZSB0bz8gSGVyZSwgd2UgYXJlIHByZWRpY3RpbmcgTF9ISVBQIG9uc2V0IG9yIG9mZnNldCBhY3Rpdml0eSB3aXRoIGJpbmFyeSByZWdyZXNzb3JzIGNhcHR1cmluZyBlYWNoIHBvc3NpYmxlIGNvbWJpbmF0aW9uIG9mIGZlYXR1cmVzIHJlY2FsbGVkLiAgCmBgYHtyLCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gNH0KCm15LnBsb3RzID0gbGlzdCgpCnBsb3QubWF4ID0gYygwLjYsIDEpCgpjID0gMApmb3IgKHRoaXMucm9pIGluIGMoJ0hJUFAnKSkgewojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKbWl4ZWQuZGF0YSA8LSBkYXRhLmZyYW1lKGFycmF5KDAsYyg3KjIsNykpKQpjb2xuYW1lcyhtaXhlZC5kYXRhKSA8LSBjKCdGZWF0dXJlJywnQmV0YScsJ1NFJywnVCcsJ0RmJywnUHZhbHVlJywnUGhhc2UnKQpyb3c9MCAKCgojIyMgbG9vcCBvdmVyIG9uc2V0IGFuZCBvZmZzZXQKZm9yIChwaGFzZSBpbiAxOjIpIHsKICAKICAjIGdhdGhlciBkYXRhIHNvIGJlaGF2aW9yYWwgdmFyaWFibGVzIGFyZSByZXBsaWNhdGVkIG92ZXIgZWFjaCBST0kgKG5vdyBhcyBhIHNpbmdsZSBjb2x1bW4gZmFjdG9yKQogIGlmIChwaGFzZSA9PSAxKSB7CiAgICByb2lEYXRhIDwtIHN1YnNldChtb2RlbC5kYXRhLm9uc2V0LCBST0kgPT0gdGhpcy5yb2kpCiAgICB0aW1lID0gJ09uc2V0JwogIH0gZWxzZSBpZiAocGhhc2UgPT0gMikgewogICAgcm9pRGF0YSA8LSBzdWJzZXQobW9kZWwuZGF0YS5vZmZzZXQsIFJPSSA9PSB0aGlzLnJvaSkKICAgIHRpbWUgPSAnT2Zmc2V0JwogIH0KICAKICAjIE5PVEUuIG5vdCBtZWFuLWNlbnRlcmluZyBoZXJlIGFzIHplcm8gaXMgbWVhbmluZ2Z1bCAtLSBleGNsdWRpbmcgdHJpYWxzIGluIG90aGVyIGNvbmRpdGlvbnMuIEltcGxpY2l0IGJhc2VsaW5lID0gbm9uZSByZWNhbGxlZAogIGZ1bGwubW9kZWwgPC0gc3VwcHJlc3NNZXNzYWdlcyhzdXBwcmVzc1dhcm5pbmdzKGxtZXIoQmV0YSB+IENvbG9yLk9ubHkgKyBTb3VuZC5Pbmx5ICsgU2NlbmUuT25seSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ29sb3IuU2NlbmUgKyBTb3VuZC5Db2xvciArIFNjZW5lLlNvdW5kICsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDb2xvci5TY2VuZS5Tb3VuZCArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICgxICsgQ29sb3IuT25seSArIFNvdW5kLk9ubHkgKyBTY2VuZS5Pbmx5ICsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDb2xvci5TY2VuZSArIFNvdW5kLkNvbG9yICsgU2NlbmUuU291bmQgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENvbG9yLlNjZW5lLlNvdW5kfHxTdWJJRCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHJvaURhdGEpKSkKICBmdWxsLm1vZGVsIDwtIHN1cHByZXNzTWVzc2FnZXMoc3VwcHJlc3NXYXJuaW5ncyhnZXRfbW9kZWwoc3RlcChmdWxsLm1vZGVsLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y2UucmFuZG9tID0gVFJVRSwgcmVkdWNlLmZpeGVkID0gRkFMU0UpKSkpCiAgc3RhdHMgPC0gc3VtbWFyeShmdWxsLm1vZGVsKSRjb2VmZmljaWVudHMKICBuSVYgPC0gbnJvdyhzdGF0cykgLSAxICMgbnVtYmVyIG9mIGZpeGVkIGVmZmVjdHMsIGRpc2NvdW50aW5nIGludGVyY2VwdAogIAogICMgdGVzdCBpZiBzY2VuZSBiaW5kaW5nIGlzID4gcmVjYWxsaW5nIHNjZW5lIGFsb25lIChmaXJzdCBwb3NpdGlvbiBpcyBpbnRlcmNlcHQpCiAgY29uIDwtIGNvbnRlc3QoZnVsbC5tb2RlbCwgYygwLDAsMCwtMSwoMS8zKSwwLCgxLzMpLCgxLzMpKSwgam9pbnQgPSBGQUxTRSwgZGRmID0gIlNhdHRlcnRod2FpdGUiKQogIHByaW50KGthYmxlKGNvbiwKICAgICAgICAgICAgICBmb3JtYXQ9InBhbmRvYyIsIGNhcHRpb249cGFzdGUodGhpcy5yb2ksJzogU2NlbmUgQmluZGluZyB2cy4gU2NlbmUgQWxvbmUgYXQgJyx0aW1lLHNlcD0iIikpKQoKICAjIHRlc3QgaWYgc2NlbmUgYmluZGluZyBpcyA+IHJlY2FsbGluZyBjb2xvciBhbmQgc291bmQgd2l0aG91dCBzY2VuZSAoZmlyc3QgcG9zaXRpb24gaXMgaW50ZXJjZXB0KQogIGNvbiA8LSBjb250ZXN0KGZ1bGwubW9kZWwsIGMoMCwwLDAsMCwoMS8zKSwtMSwoMS8zKSwoMS8zKSksIGpvaW50ID0gRkFMU0UsIGRkZiA9ICJTYXR0ZXJ0aHdhaXRlIikKICBwcmludChrYWJsZShjb24sCiAgICAgICAgICAgICAgZm9ybWF0PSJwYW5kb2MiLCBjYXB0aW9uPXBhc3RlKHRoaXMucm9pLCc6IFNjZW5lIEJpbmRpbmcgdnMuIENvbG9yJlNvdW5kIGF0ICcsdGltZSxzZXA9IiIpKSkKICAKICAjIyMgYWRkIGZpeGVkIGVmZmVjdHMgYW5kIHAgdmFsdWVzIHRvIGRhdGEgbWF0cml4ICAgIAogIG1peGVkLmRhdGEkUGhhc2VbKHJvdysxKToocm93K25JVildICAgIDwtIHRpbWUKICBtaXhlZC5kYXRhJEZlYXR1cmVbKHJvdysxKToocm93K25JVildICA8LSByb3cubmFtZXMoc3RhdHMpWzI6KG5JVisxKV0KICBtaXhlZC5kYXRhWyhyb3crMSk6KHJvdytuSVYpLGMoIkJldGEiLCJTRSIsIlQiLCJEZiIsIlB2YWx1ZSIpXSA8LSBzdGF0c1syOihuSVYrMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiRXN0aW1hdGUiLCJTdGQuIEVycm9yIiwidCB2YWx1ZSIsImRmIiwiUHIoPnx0fCkiKV0KICByb3cgPSByb3cgKyBuSVYKfQoKIyMjIyMgUExPVApvcmRlciA8LSBjKCJDb2xvci5Pbmx5IiwiU2NlbmUuT25seSIsIlNvdW5kLk9ubHkiLCJTb3VuZC5Db2xvciIsIkNvbG9yLlNjZW5lIiwiU2NlbmUuU291bmQiLCJDb2xvci5TY2VuZS5Tb3VuZCIpCiMgY29sb3JzIGZvciB2ZW5uIGRpYWdyYW0gc3BhY2UgZmVhdHVyZSBjb21iaW5hdGlvbnMKYmFyY29sb3JzIDwtIGMoIiNmNzZjNjciLCIjNjdhYWY3IiwiIzBiY2E4MSIsIiNmNmE4NGYiLCIjOWM2N2Y3IiwiIzY3ZjdmMCIsIiM3RjdGN0YiKQptaXhlZC5kYXRhJEZlYXR1cmUgPC0gZmFjdG9yKG1peGVkLmRhdGEkRmVhdHVyZSwgbGV2ZWxzPW9yZGVyKQptaXhlZC5kYXRhJFBoYXNlIDwtIGZhY3RvcihtaXhlZC5kYXRhJFBoYXNlLCBsZXZlbHM9YygnT25zZXQnLCdPZmZzZXQnKSkKCmMgPSBjKzEKbXkucGxvdHNbW2NdXSA8LSBnZ3Bsb3QobWl4ZWQuZGF0YSwgYWVzKHg9RmVhdHVyZSwgeT1CZXRhLCBmaWxsPUZlYXR1cmUpKSArCiAgZmFjZXRfZ3JpZCguIH4gUGhhc2UpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCxzaXplPTEpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgYWxwaGEgPSAwLjgwLCBzaXplID0gMSwgd2lkdGggPSAwLjc1LAogICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgxKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGJhcmNvbG9ycykgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKC0wLjMscGxvdC5tYXhbY10pKSArCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbiA9IEJldGEgLSBTRSwgeW1heCA9IEJldGEgKyBTRSksIHdpZHRoID0gMC40LCBzaXplID0gMSwKICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgxKSkgKwogIGdndGl0bGUocGFzdGUodGhpcy5yb2ksJ2VuY29kaW5nIGFjdGl2aXR5IHZzLiBub25lIHJlY2FsbGVkJyxzZXA9IiAiKSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemU9MjgsIG1hcmdpbj1tYXJnaW4oMCwwLDIwLDApKSwKICAgICAgICBheGlzLmxpbmUueCA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJibGFjayIsIHNpemUgPSAxKSwgYXhpcy5saW5lLnkgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmxhY2siLCBzaXplID0gMSksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBhbmdsZSA9IDQ1LCBoanVzdD0xLCB2anVzdD0xKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjIpLCBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDI2KSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSJub25lIiwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9MjgpLCB0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseT0iSGVsdmV0aWNhIiksCiAgICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMSwgMSwgMSwgMSwgImNtIikpCgojIyMgcHJpbnQgZnVsbCBvdXRwdXQ6CnByaW50KGthYmxlKG1peGVkLmRhdGEsZm9ybWF0PSJwYW5kb2MiLGNhcHRpb24gPSBwYXN0ZSgnUmVtZW1iZXJlZCBmZWF0dXJlcyAodnMuIG5vbmUgcmVjYWxsZWQpIGZvcicsdGhpcy5yb2ksc2VwID0gIiAiKSkpCn0KCmdnYXJyYW5nZShwbG90bGlzdCA9IG15LnBsb3RzLCBuY29sID0gYywgbnJvdyA9IDEpCgpgYGAKCiMjIyBBbnRlcmlvciB2cy4gcG9zdGVyaW9yICAKYGBge3IsIGZpZy53aWR0aCA9IDUsIGZpZy5oZWlnaHQgPSA0fQoKIyMjIyMgbG9hZCBpbiBoaXBwb2NhbXBhbCB2b3hlbCBkYXRhIGZvciBhbGwgc3ViamVjdHM6CnN1YmplY3RzIDwtIGxpc3QuZmlsZXMocGF0aD0nLi4vZGF0YS9zaW5nbGUtdHJpYWwtYmV0YXMvZXZlbnQtb2Zmc2V0LycsZnVsbC5uYW1lcz1UUlVFLCByZWN1cnNpdmUgPSBUUlVFLCBwYXR0ZXJuID0gJ29mZnNldC1MSF9ISVBQJykKYWxsRGF0YSAgPSBkby5jYWxsKHJiaW5kLCBsYXBwbHkoc3ViamVjdHMsIGZ1bmN0aW9uKHgpIHtyZWFkLmNzdih4LCBoZWFkZXIgPSBUUlVFKX0pKQoKYWxsRGF0YSRTdWJJRD1hcy5mYWN0b3IoYWxsRGF0YSRTdWJJRCkKYWxsRGF0YSRTZWdtZW50IDwtIGFzLmZhY3RvcihhbGxEYXRhJFNlZ21lbnQpCgoKIyMjIGFkZCB6IHNjb3JlIG9mIGJldGFzIHdpdGhpbiByZWdpb24gYW5kIHN1YmplY3QgdG8gcmVtb3ZlIG91dGxpZXJzOgphbGxEYXRhIDwtIGFsbERhdGEgJT4lCiAgICAgICAgICAgICAgIGdyb3VwX2J5KFN1YklELCBTZWdtZW50KSAlPiUKICAgICAgICAgICAgICAgbXV0YXRlKFogPSB6c2NvcmUoQmV0YSkpCgojIG1hcmsgYmV0YXMgdGhhdCBhcmUgPiBYU0QKYWxsRGF0YSRCZXRhW2FicyhhbGxEYXRhJFopID4gb3V0bGllcl92YWx1ZV0gPC0gTkEgICNtYXJrIGJldGEgYXMgTkEgaWYgb3V0bGllcgphbGxEYXRhIDwtIGFsbERhdGFbLC1jKDYpXSAjIHJlbW92ZSBaIHZhbHVlcyAgCgojIG1lcmdlIHdpdGggYmVoYXZpb3JhbCBkYXRhCm15QmV0YXMgPSBzcHJlYWQoYWxsRGF0YSwgU2VnbWVudCwgQmV0YSkKbXlCZXRhcyRUb3RhbEVuY29kaW5nVHJpYWwgPC0gMTpuVG90YWwKc2VnbWVudC5pbmZvIDwtIG1lcmdlKG15QmV0YXMsIGJlaGF2RGF0YSwgYnk9IlRvdGFsRW5jb2RpbmdUcmlhbCIpCnNlZ21lbnQuaW5mbyA8LSBzZWdtZW50LmluZm9bLC1jKDIsMyw0KV0KY29sbmFtZXMoc2VnbWVudC5pbmZvKVtjb2xuYW1lcyhzZWdtZW50LmluZm8pID09ICdTdWJJRC55J10gPC0gJ1N1YklEJwpjb2xuYW1lcyhzZWdtZW50LmluZm8pW2NvbG5hbWVzKHNlZ21lbnQuaW5mbykgPT0gJ1J1bi55J10gPC0gJ1J1bicKCiMgcmVtb3ZlIGFsbCB0cmlhbHMgd2hlcmUgdGhlcmUgaXMgYXQgbGVhc3Qgb25lIE5BIChhdCBsZWFzdCBvbmUgb2YgdGhlIFJPSXMgaXMgYW4gb3V0bGllciBvbiB0aGF0IHRyaWFsKToKaWR4QmV0YXMgICAgIDwtIGNvbXBsZXRlLmNhc2VzKG15QmV0YXMpCnNlZ21lbnQuaW5mbyA8LSBzZWdtZW50LmluZm9baWR4QmV0YXMsXQpzZWdtZW50LmluZm8gPC0gZ2F0aGVyKHNlZ21lbnQuaW5mbywgU2VnbWVudCwgQmV0YSwgQW50ZXJpb3I6UG9zdGVyaW9yKQoKICAKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwptaXhlZC5kYXRhIDwtIGRhdGEuZnJhbWUoYXJyYXkoMCxjKDcqMiw3KSkpCmNvbG5hbWVzKG1peGVkLmRhdGEpIDwtIGMoJ1NlZ21lbnQnLCdGZWF0dXJlJywnQmV0YScsJ1NFJywnVCcsJ0RmJywnUHZhbHVlJykKcm93PTAgCgpmb3IgKHYgaW4gYygnQW50ZXJpb3InLCdQb3N0ZXJpb3InKSkgewogIAogIHRoaXMuZGF0YSA8LSBzdWJzZXQoc2VnbWVudC5pbmZvLCBTZWdtZW50ID09IHYpCiAgCiAgIyBOT1RFLiBub3QgbWVhbi1jZW50ZXJpbmcgaGVyZSBhcyB6ZXJvIGlzIG1lYW5pbmdmdWwgLS0gZXhjbHVkaW5nIHRyaWFscyBpbiBvdGhlciBjb25kaXRpb25zLiBJbXBsaWNpdCBiYXNlbGluZSA9IG5vbmUgcmVjYWxsZWQKICBmdWxsLm1vZGVsIDwtIHN1cHByZXNzTWVzc2FnZXMoc3VwcHJlc3NXYXJuaW5ncyhsbWVyKEJldGEgfiBDb2xvci5Pbmx5ICsgU291bmQuT25seSArIFNjZW5lLk9ubHkgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENvbG9yLlNjZW5lICsgU291bmQuQ29sb3IgKyBTY2VuZS5Tb3VuZCArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ29sb3IuU2NlbmUuU291bmQgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoMSArIENvbG9yLk9ubHkgKyBTb3VuZC5Pbmx5ICsgU2NlbmUuT25seSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ29sb3IuU2NlbmUgKyBTb3VuZC5Db2xvciArIFNjZW5lLlNvdW5kICsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDb2xvci5TY2VuZS5Tb3VuZHx8U3ViSUQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRoaXMuZGF0YSkpKQogIGZ1bGwubW9kZWwgPC0gc3VwcHJlc3NNZXNzYWdlcyhzdXBwcmVzc1dhcm5pbmdzKGdldF9tb2RlbChzdGVwKGZ1bGwubW9kZWwsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjZS5yYW5kb20gPSBUUlVFLCByZWR1Y2UuZml4ZWQgPSBGQUxTRSkpKSkKICBzdGF0cyA8LSBzdW1tYXJ5KGZ1bGwubW9kZWwpJGNvZWZmaWNpZW50cwogIG5JViA8LSBucm93KHN0YXRzKSAtIDEgIyBudW1iZXIgb2YgZml4ZWQgZWZmZWN0cywgZGlzY291bnRpbmcgaW50ZXJjZXB0CiAgCiAgIyBiaW5kaW5nOiA+PTIgZmVhdHVyZXMgdnMuIDEKICBjb24gPC0gY29udGVzdChmdWxsLm1vZGVsLCBjKDAsLSgxLzMpLC0oMS8zKSwtKDEvMyksKDEvNCksKDEvNCksKDEvNCksKDEvNCkpLCBqb2ludCA9IEZBTFNFLCBkZGYgPSAiU2F0dGVydGh3YWl0ZSIpCiAgcHJpbnQoa2FibGUoY29uLAogICAgICAgICAgICAgIGZvcm1hdD0icGFuZG9jIiwgY2FwdGlvbj1wYXN0ZSh0aGlzLnJvaSwnOiBCaW5kaW5nICg+MSBmZWF0dXJlIHZzLiAxIGZlYXR1cmUpOiAnLHYsc2VwPSIiKSkpCgogICMgdGVzdCBpZiBzY2VuZSBiaW5kaW5nIGlzID4gcmVjYWxsaW5nIGNvbG9yIGFuZCBzb3VuZCB3aXRob3V0IHNjZW5lIChmaXJzdCBwb3NpdGlvbiBpcyBpbnRlcmNlcHQpCiAgY29uIDwtIGNvbnRlc3QoZnVsbC5tb2RlbCwgYygwLDAsMCwwLCgxLzMpLC0xLCgxLzMpLCgxLzMpKSwgam9pbnQgPSBGQUxTRSwgZGRmID0gIlNhdHRlcnRod2FpdGUiKQogIHByaW50KGthYmxlKGNvbiwKICAgICAgICAgICAgICBmb3JtYXQ9InBhbmRvYyIsIGNhcHRpb249cGFzdGUodGhpcy5yb2ksJzogU2NlbmUgQmluZGluZyB2cy4gQ29sb3ImU291bmQ6ICcsdixzZXA9IiIpKSkKICAKICAjIyMgYWRkIGZpeGVkIGVmZmVjdHMgYW5kIHAgdmFsdWVzIHRvIGRhdGEgbWF0cml4ICAgIAogIG1peGVkLmRhdGEkU2VnbWVudFsocm93KzEpOihyb3crbklWKV0gIDwtIHYKICBtaXhlZC5kYXRhJEZlYXR1cmVbKHJvdysxKToocm93K25JVildICA8LSByb3cubmFtZXMoc3RhdHMpWzI6KG5JVisxKV0KICBtaXhlZC5kYXRhWyhyb3crMSk6KHJvdytuSVYpLGMoIkJldGEiLCJTRSIsIlQiLCJEZiIsIlB2YWx1ZSIpXSA8LSBzdGF0c1syOihuSVYrMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJFc3RpbWF0ZSIsIlN0ZC4gRXJyb3IiLCJ0IHZhbHVlIiwiZGYiLCJQcig+fHR8KSIpXQogIHJvdyA9IHJvdyArIG5JVgp9CgojIyMgcHJpbnQgZnVsbCBvdXRwdXQ6CnByaW50KGthYmxlKG1peGVkLmRhdGEsZm9ybWF0PSJwYW5kb2MiLGNhcHRpb24gPSBwYXN0ZSgnUmVtZW1iZXJlZCBmZWF0dXJlIGNvbWJpbmF0aW9ucyAodnMuIG5vbmUgcmVjYWxsZWQpIGZvciBBbnRlcmlvciB2cy4gUG9zdGVyaW9yJyx0aGlzLnJvaSxzZXAgPSAiICIpKSkKCgojIyMjIyBQTE9UCm9yZGVyIDwtIGMoIkNvbG9yLk9ubHkiLCJTY2VuZS5Pbmx5IiwiU291bmQuT25seSIsIlNvdW5kLkNvbG9yIiwiQ29sb3IuU2NlbmUiLCJTY2VuZS5Tb3VuZCIsIkNvbG9yLlNjZW5lLlNvdW5kIikKIyBjb2xvcnMgZm9yIHZlbm4gZGlhZ3JhbSBzcGFjZSBmZWF0dXJlIGNvbWJpbmF0aW9ucwpiYXJjb2xvcnMgPC0gYygiI2Y3NmM2NyIsIiM2N2FhZjciLCIjMGJjYTgxIiwiI2Y2YTg0ZiIsIiM5YzY3ZjciLCIjNjdmN2YwIiwiIzdGN0Y3RiIpCm1peGVkLmRhdGEkRmVhdHVyZSA8LSBmYWN0b3IobWl4ZWQuZGF0YSRGZWF0dXJlLCBsZXZlbHM9b3JkZXIpCm1peGVkLmRhdGEkU2VnbWVudCA8LSBhcy5mYWN0b3IobWl4ZWQuZGF0YSRTZWdtZW50KQoKIyMjIyMgUExPVApnZ3Bsb3QobWl4ZWQuZGF0YSwgYWVzKHg9RmVhdHVyZSwgeT1CZXRhLCBmaWxsPUZlYXR1cmUpKSArCiAgZmFjZXRfZ3JpZCguIH4gU2VnbWVudCkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0wLHNpemU9MSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGJhcmNvbG9ycykgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKC0wLjM1LDAuNjUpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGFscGhhID0gMC44MCwgc2l6ZSA9IDEsIHdpZHRoID0gMC43NSwKICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMSkpICsKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gQmV0YSAtIFNFLCB5bWF4ID0gQmV0YSArIFNFKSwgd2lkdGggPSAwLjQsIHNpemUgPSAxLAogICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDEpKSArCiAgZ2d0aXRsZSgiQW50ZXJpb3IvcG9zdGVyaW9yIGhpcHBvY2FtcGFsIG9mZnNldCBlbmNvZGluZyBhY3Rpdml0eSIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplPTI4LCBtYXJnaW49bWFyZ2luKDAsMCwyMCwwKSksCiAgICAgICAgYXhpcy5saW5lLnggPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmxhY2siLCBzaXplID0gMSksIGF4aXMubGluZS55ID0gZWxlbWVudF9saW5lKGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDEpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgYW5nbGUgPSA0NSwgaGp1c3Q9MSwgdmp1c3Q9MSksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIyKSwgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyNiksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTI4KSwgdGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHk9IkhlbHZldGljYSIpLAogICAgICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDEsIDEsIDEsIDEsICJjbSIpKQoKYGBgCg==