Getting started

clean up

rm(list=ls())


general custom functions

  • fpackage.check: Check if packages are installed (and install if not) in R (source)
  • fload.R: function to load R-objects under new names.
  • fidmaker: creating mock IDs
fpackage.check <- function(packages) {
    lapply(packages, FUN = function(x) {
        if (!require(x, character.only = TRUE)) {
            install.packages(x, dependencies = TRUE)
            library(x, character.only = TRUE)
        }
    })
}

fload.R  <- function(fileName){
  load(fileName)
  get(ls()[ls() != "fileName"])
}

fidmaker <- function(x) {
    max.val = x * 1e+05
    count <- nchar(as.character(max.val))  # find out how many 'numbers' each id will have after the letter
    size <- paste("%0", count, "d", sep = "")  # set the variable to be fed into 'sprintf' to ensure we have leading 0's
    lets <- toupper(sample(letters, x, replace = T))  # randomizing the letters 
    nums <- sprintf(size, sample(1:max.val)[1:x])  # randomizing the numbers, and ensuring they all have the same number of characters
    ids <- paste(lets, nums, sep = "")  # joining them together
    return(ids)
  }


necessary packages

  • RSiena: creating RSiena data objects
  • dplyr: package for data wrangling
packages = c("RSiena", "dplyr")
fpackage.check(packages)


clubdata.RData

In the following scripts a list containing the (anonymized) data of all clubs is made (clubdata.RData).

  • Our primary network variable is the Kudos-network. A tie i -> j exists if ego i award at least 1 Kudos to alter j.

  • For the behavioral data we include information on the frequency (i.e., in times per week) and volume (i.e., in hours per week) of running activities. We included activity (frequency and volume) in other sports (e.g., cycling and swimming) as a time-varying covariate.

# club string represents the club ID
club_str <- c("clubid1", "clubid2", "clubid3", "clubid4" ,"clubid5") 

# the following script reads the data of the clubs from the folder for each club, stores them in a list, and saves it in an object in the last function call of this script. 


for (i in (1: length(club_str))) {
  
  club_id <- club_str[i]
  
  # read the data from the club
  clubdata <- read.csv(paste("clubs/", club_id, "/", "egoData_extended.csv", sep = ""), row.names = NULL,
                       sep = ",")
  # saving club size
  size <- length(clubdata[, "gender"])
  # the number of months that we want to add as waves
  n_waves <- 12
  
  # let's load the friendship/following network
  friend_data <- as.matrix(read.csv(paste("clubs/", club_id, "/", "socialnetwork.csv", sep = ""), row.names = NULL,
                                    sep = ","))
  # remove the first column (represents index made in the csv)
  friend_data <- friend_data[, 2:ncol(friend_data)]
  
  # and anonymize the user id, with our id-maker function
  
  fakeid <- fidmaker(nrow(friend_data))  # generate random id
  colnames(friend_data) <- fakeid  # anonymizing users
  
  # let's load the kudos network
  path <- paste("clubs/", club_id, "/", "kudos", sep = "")  # create path
  {
    kudo_w1 <- as.matrix(read.csv(paste(path, "1-2019.csv", sep = ""), row.names = NULL, sep = ","))
    kudo_w1 <- kudo_w1[, 2:ncol(kudo_w1)]
    kudo_w2 <- as.matrix(read.csv(paste(path, "2-2019.csv", sep = ""), row.names = NULL, sep = ","))
    kudo_w2 <- kudo_w2[, 2:ncol(kudo_w2)]
    kudo_w3 <- as.matrix(read.csv(paste(path, "3-2019.csv", sep = ""), row.names = NULL, sep = ","))
    kudo_w3 <- kudo_w3[, 2:ncol(kudo_w3)]
    kudo_w4 <- as.matrix(read.csv(paste(path, "4-2019.csv", sep = ""), row.names = NULL, sep = ","))
    kudo_w4 <- kudo_w4[, 2:ncol(kudo_w4)]
    kudo_w5 <- as.matrix(read.csv(paste(path, "5-2019.csv", sep = ""), row.names = NULL, sep = ","))
    kudo_w5 <- kudo_w5[, 2:ncol(kudo_w5)]
    kudo_w6 <- as.matrix(read.csv(paste(path, "6-2019.csv", sep = ""), row.names = NULL, sep = ","))
    kudo_w6 <- kudo_w6[, 2:ncol(kudo_w6)]
    kudo_w7 <- as.matrix(read.csv(paste(path, "7-2019.csv", sep = ""), row.names = NULL, sep = ","))
    kudo_w7 <- kudo_w7[, 2:ncol(kudo_w7)]
    kudo_w8 <- as.matrix(read.csv(paste(path, "8-2019.csv", sep = ""), row.names = NULL, sep = ","))
    kudo_w8 <- kudo_w8[, 2:ncol(kudo_w8)]
    kudo_w9 <- as.matrix(read.csv(paste(path, "9-2019.csv", sep = ""), row.names = NULL, sep = ","))
    kudo_w9 <- kudo_w9[, 2:ncol(kudo_w9)]
    kudo_w10 <- as.matrix(read.csv(paste(path, "10-2019.csv", sep = ""), row.names = NULL, sep = ","))
    kudo_w10 <- kudo_w10[, 2:ncol(kudo_w10)]
    kudo_w11 <- as.matrix(read.csv(paste(path, "11-2019.csv", sep = ""), row.names = NULL, sep = ","))
    kudo_w11 <- kudo_w11[, 2:ncol(kudo_w11)]
    kudo_w12 <- as.matrix(read.csv(paste(path, "12-2019.csv", sep = ""), row.names = NULL, sep = ","))
    kudo_w12 <- kudo_w12[, 2:ncol(kudo_w12)]
  }
  kudos <- array(c(kudo_w1, kudo_w2, kudo_w3, kudo_w4, kudo_w5, kudo_w6, kudo_w7, kudo_w8, kudo_w9,
                   kudo_w10, kudo_w11, kudo_w12), dim = c(size, size, n_waves))  #Kudos matrix
  
  kudo_data <- ifelse(kudos > 0, 1, 0)  #if at least 1 Kudo is send, tie exists
  
  # running time (in hours per week)
  time_run <- array(c(clubdata[, "time_run_1.2019"], clubdata[, "time_run_2.2019"], clubdata[, "time_run_3.2019"],
                      clubdata[, "time_run_4.2019"], clubdata[, "time_run_5.2019"], clubdata[, "time_run_6.2019"],
                      clubdata[, "time_run_7.2019"], clubdata[, "time_run_8.2019"], clubdata[, "time_run_9.2019"],
                      clubdata[, "time_run_10.2019"], clubdata[, "time_run_11.2019"], clubdata[, "time_run_12.2019"]),
                    dim = c(size, 1, n_waves))  # minutes per month
  time_run_h <- time_run/60  # hours per month
  # time_run_30 <- time_run_h * 2 # half hours
  time <- ceiling(time_run_h/4)  # per week
  
  # provide some descriptives 
  # time %>% # absolute table() time %>% # proportionate table() %>%
  # prop.table() %>% round(3) cumprop <- time %>% # cumulative proportions table() %>%
  # prop.table() %>% round(3) %>% cumsum() print(cumprop) 1-cumprop # the percentage of all
  # values that is higher than the particular value
  
  # our idea was to cap running time of at 7+ hours per week.  this resulted in rather smooth
  # (right-skewed) distribution of values for most clubs except for club 5, where 7+ hours
  # category was highly populated over time.  We added 2 extra categories (capped of at 9+
  # hours); this resulted in a rather smooth distribution for this club.
  
  if (club_id == "clubid5") {
    time_run_temp <- ifelse(time > 9, 9, time)
  } else {
    time_run_temp <- ifelse(time > 7, 7, time)
  }
  
  # running frequency (times per week)
  freq_run <- array(c(clubdata[, "X.run_1.2019"], clubdata[, "X.run_2.2019"], clubdata[, "X.run_3.2019"],
                      clubdata[, "X.run_4.2019"], clubdata[, "X.run_5.2019"], clubdata[, "X.run_6.2019"], clubdata[,
                                                                                                                   "X.run_7.2019"], clubdata[, "X.run_8.2019"], clubdata[, "X.run_9.2019"], clubdata[, "X.run_10.2019"],
                      clubdata[, "X.run_11.2019"], clubdata[, "X.run_12.2019"]), dim = c(size, 1, n_waves))  # frequencies per month
  frequencies <- ceiling(freq_run/4)  # per week
  freq_run_temp <- ifelse(frequencies > 7, 7, frequencies)  # cap off at 7 times per week
  

  # we deal with composition change: actors joining the Strava club over time (we assume no leavers);
  # node set was determined at t12; from there, we 'scraped backward' in time;
  # down below, we count the number of waves, from t1 onward, that actors scored 0 on all DVs 
  # that is, where the sum of number of kudos in- and out-ties (network activity), frequency and volume (behavior activity), equals 0.
  # this number +1 is the wave in which actors are assumed to have joined 
  # our model will then assume that entries of joiners happen at the midpoint of the unobserved time period between this and the subsequent wave.
  
  sum_dv <- matrix(NA, nr=size, nc=n_waves)
  
  for (i in 1:size) {
    for (j in 1:n_waves) {
      sum_dv[i,j] <- sum( c( # sum of
        kudo_data[i,,j],     # number of out-ties of actor i at time j
        kudo_data[,i,j],     # number of in-ties of actor i at time j
        freq_run_temp[i,,j], # running frequency of i at j
        time_run_temp[i,,j]  # running volume of i at j
      ))
    }
  }

  t <- rep(1, size) # starting entry t=1
  for ( i in 1:size) {
    for( j in 1:n_waves) {
      
      # if sum = 0 for actor i in wave j, 
      # increment the entry period t with 1
      if(sum_dv[i,j] == 0) {
        t[i] = t[i] + 1
      }
      # if sum > 0, break the loop
      if(sum_dv[i,j] > 0) {
        break
      }
    }
  }
  # entry-waves cannot surpass 12:
  t[which(t>12)] <- 12
  
  # actors that join at t=12, cannot be included into the estimation;
  # they are structural zero;
  # we exclude the rows (i) and columns (j) corresponding to these actors
  # from the kudos matrices
  # we also remove these actors' elements from the running variable objects.
  
  # first, identify these actors (get their index)
  x <- which(t==12)
  
  # make new arrays to store entries of actors, excluding structural zeros
  kudo_data.nm <- array(NA, dim = c(size - length(x),
                                    size - length(x),
                                    n_waves))
  freq_run_temp.nm <- array(NA, dim = c(size - length(x), 1, n_waves))
  time_run_temp.nm <- array(NA, dim = c(size - length(x), 1, n_waves))
  
  # fill arrays  
  for (i in 1:n_waves) {
    kudo_data.nm[,,i] <- subset(kudo_data[,,i], select = -x)[-x, ]
    freq_run_temp.nm[,,i] <- freq_run_temp[,,i][-x]
    time_run_temp.nm[,,i] <- time_run_temp[,,i][-x]
  }
  
  # and adjust network size accordingly
  size.nm <- size - length(x)


  # create a list for the times of composition change
  # see 4.3.3 of RSIENA manual
  comp <- rep(list(NA), size.nm)
  for (i in 1:size.nm) {
    comp[[i]] <- c(t[-x][i],n_waves)
  }
  
  # RSiena GOF functions do not combine properly with the method of joiners and leavers
  # But if we use NA codes for the tie variables of absent actors, sienaGOF will work properly.

  # get time of joining for each actor:
  t_j <- t[which(t!=12)] 
  
  # for actors that are partially absent, set entries in the kudos-matrix to NA, 
  # for those waves they are absent, i.e., waves 1 : (t_j-1)
  for ( i in which(t_j>1)) {     # for actors that join later
    for (j in 1:n_waves) {       # for all waves
      if(t_j[i]>j) {             # if their time of joining comes after the current wave
        kudo_data.nm[i,,j] <- NA # set their out-degree entries to NA
        kudo_data.nm[,i,j] <- NA # but also their in-degree entries
        freq_run_temp.nm[i,1,j] <- NA # and their behaviors
        time_run_temp.nm[i,1,j] <- NA
      }g
    }
  }

  #  let's load other activity data (e.g., cycling, swimming) time
  time_ride <- array(c(clubdata[, "time_ride_1.2019"], clubdata[, "time_ride_2.2019"], clubdata[, "time_ride_3.2019"],
                       clubdata[, "time_ride_4.2019"], clubdata[, "time_ride_5.2019"], clubdata[, "time_ride_6.2019"],
                       clubdata[, "time_ride_7.2019"], clubdata[, "time_ride_8.2019"], clubdata[, "time_ride_9.2019"],
                       clubdata[, "time_ride_10.2019"], clubdata[, "time_ride_11.2019"], clubdata[, "time_ride_12.2019"]),
                     dim = c(size, 1, n_waves))
  time_other <- array(c(clubdata[, "time_other_1.2019"], clubdata[, "time_other_2.2019"], clubdata[,
                                                                                                   "time_other_3.2019"], clubdata[, "time_other_4.2019"], clubdata[, "time_other_5.2019"], clubdata[,
                                                                                                                                                                                                    "time_other_6.2019"], clubdata[, "time_other_7.2019"], clubdata[, "time_other_8.2019"], clubdata[,
                                                                                                                                                                                                                                                                                                     "time_other_12.2019"]), dim = c(size, 1, n_waves))

  time_other <- time_ride + time_other  # minutes per month
  time_other_h <- time_other/60  # hours per month
  # time_other_h <- time_other_h * 2 # half hours
  time <- ceiling(time_other_h/4)  #per week
  time_other_temp <- ifelse(time > 7, 7, time)  # cap off at 7 hours per week
  #table(time_other_temp)
  
  # frequency
  freq_ride <- array(c(clubdata[, "X.ride_1.2019"], clubdata[, "X.ride_2.2019"], clubdata[, "X.ride_3.2019"],
                       clubdata[, "X.ride_4.2019"], clubdata[, "X.ride_5.2019"], clubdata[, "X.ride_6.2019"], clubdata[,
                                                                                                                       "X.ride_7.2019"], clubdata[, "X.ride_8.2019"], clubdata[, "X.ride_9.2019"], clubdata[, "X.ride_10.2019"],
                       clubdata[, "X.ride_11.2019"], clubdata[, "X.ride_12.2019"]), dim = c(size, 1, n_waves))
  freq_other <- array(c(clubdata[, "X.other_1.2019"], clubdata[, "X.other_2.2019"], clubdata[, "X.other_3.2019"],
                        clubdata[, "X.other_4.2019"], clubdata[, "X.other_5.2019"], clubdata[, "X.other_6.2019"], clubdata[,
                                                                                                                           "X.other_7.2019"], clubdata[, "X.other_8.2019"], clubdata[, "X.other_9.2019"], clubdata[,
                                                                                                                                                                                                                   "X.other_10.2019"], clubdata[, "X.other_11.2019"], clubdata[, "X.other_12.2019"]), dim = c(size,
                                                                                                                                                                                                                                                                                                              1, n_waves))
  
  freq_other <- freq_ride + freq_other
  frequencies <- ceiling(freq_other/4)
  freq_other_temp <- ifelse(frequencies > 7, 7, frequencies)
  
  # and again, exclude the structural zeros:
  freq_other_temp.nm <- array(NA, dim = c(size.nm, 1, n_waves))
  time_other_temp.nm <- array(NA, dim = c(size.nm, 1, n_waves))
  for (i in 1:n_waves) {
    freq_other_temp.nm[,,i] <- freq_other_temp[,,i][-x]
    time_other_temp.nm[,,i] <- time_other_temp[,,i][-x]
  }
  
  # separating male/female/other
  
  male <- ifelse(clubdata[, "gender"][-x] == "M", 1,0)
  female <- ifelse(clubdata[, "gender"][-x] == "F", 1, 0)
  other <- ifelse(clubdata[, "gender"][-x] == "O", 1, 0)
  
  # specify months of winter in case we want to use it as a varying covariate starts with
  # december
  winter <- rep(c(1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0), size.nm)
  winter <- matrix(winter, nr = size.nm, nc = n_waves, byrow = TRUE)
  
  # in case we want to condition on the first activity recorded by users:
  st_yr <- clubdata[,'start_date_year']
  st_yr <- st_yr[-x] #exclude NAs
  
  start_year <- ifelse(st_yr <= 2011, 1, 
                       ifelse(st_yr == 2012, 2,
                              ifelse(st_yr == 2013, 3,
                                     ifelse(st_yr == 2014, 4,
                                            ifelse(st_yr == 2015, 5,
                                                   ifelse(st_yr == 2016, 6,
                                                          ifelse(st_yr == 2017, 7,
                                                                 ifelse(st_yr == 2018, 8,
                                                                        9))))))))
  #hist(start_year)
  
  # create a list containing all the read club data for the current club
  club <- list(friendship = friend_data, kudo = kudo_data.nm, freq_run = freq_run_temp.nm, time_run = time_run_temp.nm,
               freq_other = freq_other_temp.nm, time_other = time_other_temp.nm, winter = winter, male = male, female = female,
               other = other, netsize = size.nm, composition = comp, novice=start_year)
  
  
  # save the club object
  save(club, file = paste("clubs/", "club", club_id, ".RData", sep = ""))
  
}


####################################################

# Now that we have saved the clubdata for all clubs, let's combine them in one list

# first clean the working directory, except our club string; we still need that one.
# and our function fload.R
rm(list = setdiff(ls(), c("club_str", "fload.R")))


# load in the separate club-objects
club1 <- fload.R(paste("clubs", "/", "club", club_str[1], ".RData", sep=""))
club2 <- fload.R(paste("clubs", "/", "club", club_str[2], ".RData", sep=""))
club3 <- fload.R(paste("clubs", "/", "club", club_str[3], ".RData", sep=""))
club4 <- fload.R(paste("clubs", "/", "club", club_str[4], ".RData", sep=""))
club5 <- fload.R(paste("clubs", "/", "club", club_str[5], ".RData", sep=""))

# and make a list containing all the clubdata
clubdata <- list(club1, club2, club3, club4, club5)

# save the output
save(clubdata, file = "clubs/clubdata.RData")

clubdata_rsiena.RData

The following script creates a list containing RSiena objects for all clubs. We create a list containing RSiena objects with running frequency as the behavior variable (clubdata_rsiena_freq.RData) and running volume (clubdata_rsiena_vol.RData).

Note: in some groups, dependent variables (either the kudos-network or running behaviors) only have upward or downward changes in particular periods. We lift RSiena’s automatic restriction to follow this pattern in the simulations, by using ‘allowOnly = FALSE’. This must be done for the subsequent meta-analysis.

# clean the working environment 
rm (list = ls( ))

# load the clubdata
load("clubs/clubdata.RData")
str(clubdata) # inspect structure
# clubdata is a list of 5 lists, 
# with each of these lists containing data of the corresponding club.

####################################################


clubdata_rsiena_freq <- list()
clubdata_rsiena_vol <- list()


for (i in 1:5) { 
  club <- clubdata[[i]]
  # specify the roles of variables
  names(club)
  
  # A: network variables
  kudonet <- sienaDependent(club$kudo, allowOnly = FALSE)  #at least one Kudo
  
  # B: behavioral variables
  time_run <- sienaDependent(club$time_run, type= "behavior", allowOnly = FALSE)
  freq_run <- sienaDependent(club$freq_run, type= "behavior", allowOnly = FALSE)
  
  time_other <- varCovar(club$time_other[,,])
  freq_other <- varCovar(club$freq_other[,,])

  # C: covariates
  winter <- varCovar(club$winter)
  gender <- NA #we dichotomize gender as binary (men vs. women and other)
  gender <- ifelse(club$male == 1, 1, gender)
  gender <- ifelse(club$female == 1, 2, gender)
  gender <- ifelse(club$other == 1, 2, gender)
  gender <- coCovar(gender)
  novice <- coCovar(club$novice)
  
  
  # D: composition change (joiners)
  joiners <- sienaCompositionChange(club$composition)
  
  # now combine the dependent and independent variables in a data object
  mydata <- sienaDataCreate(kudonet, freq_run, time_other, freq_other, gender, winter, joiners, novice)
  mydata2 <- sienaDataCreate(kudonet, time_run, time_other, freq_other, gender, winter, joiners, novice)

  # this finishes the data specification
  clubdata_rsiena_freq[[i]] <- mydata
  clubdata_rsiena_vol[[i]] <- mydata2
} 


#clean environment
rm(list = setdiff(ls(), c("clubdata_rsiena_freq", "clubdata_rsiena_vol")))

# save the output
save(clubdata_rsiena_freq, file = "clubs/clubdata_rsiena_freq.RData")
save(clubdata_rsiena_vol, file = "clubs/clubdata_rsiena_vol.RData")

LS0tDQp0aXRsZTogIkRhdGEgcHJlcGFyYXRpb24iDQpiaWJsaW9ncmFwaHk6IHJlZmVyZW5jZXMuYmliDQpkYXRlOiAiTGFzdCBjb21waWxlZCBvbiBgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVCLCAlWScpYCINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY3NzOiB0d2Vha3MuY3NzDQogICAgdG9jOiAgdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UNCiAgICB0b2NfZGVwdGg6IDENCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCi0tLQ0KDQpgYGB7ciwgZ2xvYmFsc2V0dGluZ3MsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KbGlicmFyeShrbml0cikNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCm9wdHNfY2h1bmskc2V0KHRpZHkub3B0cz1saXN0KHdpZHRoLmN1dG9mZj0xMDApLHRpZHk9VFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsY29tbWVudCA9ICIjPiIsIGNhY2hlPVRSVUUsIGNsYXNzLnNvdXJjZT1jKCJ0ZXN0IiksIGNsYXNzLm91dHB1dD1jKCJ0ZXN0MiIpKQ0Kb3B0aW9ucyh3aWR0aCA9IDEwMCkNCnJnbDo6c2V0dXBLbml0cigpDQoNCg0KY29sb3JpemUgPC0gZnVuY3Rpb24oeCwgY29sb3IpIHtzcHJpbnRmKCI8c3BhbiBzdHlsZT0nY29sb3I6ICVzOyc+JXM8L3NwYW4+IiwgY29sb3IsIHgpIH0NCmBgYA0KDQoNCmBgYHtyIGtsaXBweSwgZWNobz1GQUxTRSwgaW5jbHVkZT1UUlVFfQ0Ka2xpcHB5OjprbGlwcHkocG9zaXRpb24gPSBjKCd0b3AnLCAncmlnaHQnKSkNCiNrbGlwcHk6OmtsaXBweShjb2xvciA9ICdkYXJrcmVkJykNCiNrbGlwcHk6OmtsaXBweSh0b29sdGlwX21lc3NhZ2UgPSAnQ2xpY2sgdG8gY29weScsIHRvb2x0aXBfc3VjY2VzcyA9ICdEb25lJykNCmBgYA0KDQoNCi0tLSAgDQoNCiMgR2V0dGluZyBzdGFydGVkDQoNCg0KIyMgY2xlYW4gdXANCg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnfQ0Kcm0obGlzdD1scygpKQ0KYGBgDQoNCjxicj4NCg0KIyMgZ2VuZXJhbCBjdXN0b20gZnVuY3Rpb25zDQoNCi0gYGZwYWNrYWdlLmNoZWNrYDogQ2hlY2sgaWYgcGFja2FnZXMgYXJlIGluc3RhbGxlZCAoYW5kIGluc3RhbGwgaWYgbm90KSBpbiBSIChbc291cmNlXShodHRwczovL3ZiYWxpZ2EuZ2l0aHViLmlvL3ZlcmlmeS10aGF0LXItcGFja2FnZXMtYXJlLWluc3RhbGxlZC1hbmQtbG9hZGVkLykpDQotIGBmbG9hZC5SYDogZnVuY3Rpb24gdG8gbG9hZCBSLW9iamVjdHMgdW5kZXIgbmV3IG5hbWVzLg0KLSBgZmlkbWFrZXJgOiBjcmVhdGluZyBtb2NrIElEcw0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCg0KZnBhY2thZ2UuY2hlY2sgPC0gZnVuY3Rpb24ocGFja2FnZXMpIHsNCiAgICBsYXBwbHkocGFja2FnZXMsIEZVTiA9IGZ1bmN0aW9uKHgpIHsNCiAgICAgICAgaWYgKCFyZXF1aXJlKHgsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpIHsNCiAgICAgICAgICAgIGluc3RhbGwucGFja2FnZXMoeCwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiAgICAgICAgICAgIGxpYnJhcnkoeCwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQ0KICAgICAgICB9DQogICAgfSkNCn0NCg0KZmxvYWQuUiAgPC0gZnVuY3Rpb24oZmlsZU5hbWUpew0KICBsb2FkKGZpbGVOYW1lKQ0KICBnZXQobHMoKVtscygpICE9ICJmaWxlTmFtZSJdKQ0KfQ0KDQpmaWRtYWtlciA8LSBmdW5jdGlvbih4KSB7DQogICAgbWF4LnZhbCA9IHggKiAxZSswNQ0KICAgIGNvdW50IDwtIG5jaGFyKGFzLmNoYXJhY3RlcihtYXgudmFsKSkgICMgZmluZCBvdXQgaG93IG1hbnkgJ251bWJlcnMnIGVhY2ggaWQgd2lsbCBoYXZlIGFmdGVyIHRoZSBsZXR0ZXINCiAgICBzaXplIDwtIHBhc3RlKCIlMCIsIGNvdW50LCAiZCIsIHNlcCA9ICIiKSAgIyBzZXQgdGhlIHZhcmlhYmxlIHRvIGJlIGZlZCBpbnRvICdzcHJpbnRmJyB0byBlbnN1cmUgd2UgaGF2ZSBsZWFkaW5nIDAncw0KICAgIGxldHMgPC0gdG91cHBlcihzYW1wbGUobGV0dGVycywgeCwgcmVwbGFjZSA9IFQpKSAgIyByYW5kb21pemluZyB0aGUgbGV0dGVycyANCiAgICBudW1zIDwtIHNwcmludGYoc2l6ZSwgc2FtcGxlKDE6bWF4LnZhbClbMTp4XSkgICMgcmFuZG9taXppbmcgdGhlIG51bWJlcnMsIGFuZCBlbnN1cmluZyB0aGV5IGFsbCBoYXZlIHRoZSBzYW1lIG51bWJlciBvZiBjaGFyYWN0ZXJzDQogICAgaWRzIDwtIHBhc3RlKGxldHMsIG51bXMsIHNlcCA9ICIiKSAgIyBqb2luaW5nIHRoZW0gdG9nZXRoZXINCiAgICByZXR1cm4oaWRzKQ0KICB9DQoNCmBgYA0KDQo8YnI+DQoNCiMjIG5lY2Vzc2FyeSBwYWNrYWdlcw0KDQotIGBSU2llbmFgOiBjcmVhdGluZyBSU2llbmEgZGF0YSBvYmplY3RzDQotIGBkcGx5cmA6IHBhY2thZ2UgZm9yIGRhdGEgd3JhbmdsaW5nDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KcGFja2FnZXMgPSBjKCJSU2llbmEiLCAiZHBseXIiKQ0KZnBhY2thZ2UuY2hlY2socGFja2FnZXMpDQpgYGANCg0KPGJyPg0KLS0tLQ0KDQojIGNsdWJkYXRhLlJEYXRhDQoNCkluIHRoZSBmb2xsb3dpbmcgc2NyaXB0cyBhIGxpc3QgY29udGFpbmluZyB0aGUgKGFub255bWl6ZWQpIGRhdGEgb2YgYWxsIGNsdWJzIGlzIG1hZGUgKGNsdWJkYXRhLlJEYXRhKS4NCg0KKiBPdXIgcHJpbWFyeSBuZXR3b3JrIHZhcmlhYmxlIGlzIHRoZSBLdWRvcy1uZXR3b3JrLiBBIHRpZSBpIC0+IGogZXhpc3RzIGlmIGVnbyBpIGF3YXJkIGF0IGxlYXN0IDEgS3Vkb3MgdG8gYWx0ZXIgai4NCg0KKiBGb3IgdGhlIGJlaGF2aW9yYWwgZGF0YSB3ZSBpbmNsdWRlIGluZm9ybWF0aW9uIG9uIHRoZSAqZnJlcXVlbmN5KiAoaS5lLiwgaW4gdGltZXMgcGVyIHdlZWspIGFuZCAqdm9sdW1lKiAoaS5lLiwgaW4gaG91cnMgcGVyIHdlZWspIG9mIHJ1bm5pbmcgYWN0aXZpdGllcy4gV2UgaW5jbHVkZWQgYWN0aXZpdHkgKGZyZXF1ZW5jeSBhbmQgdm9sdW1lKSBpbiBvdGhlciBzcG9ydHMgKGUuZy4sIGN5Y2xpbmcgYW5kIHN3aW1taW5nKSBhcyBhIHRpbWUtdmFyeWluZyBjb3ZhcmlhdGUuDQoNCg0KYGBge3IgY2x1YnMsIGV2YWw9RkFMU0V9DQoNCiMgY2x1YiBzdHJpbmcgcmVwcmVzZW50cyB0aGUgY2x1YiBJRA0KY2x1Yl9zdHIgPC0gYygiY2x1YmlkMSIsICJjbHViaWQyIiwgImNsdWJpZDMiLCAiY2x1YmlkNCIgLCJjbHViaWQ1IikgDQoNCiMgdGhlIGZvbGxvd2luZyBzY3JpcHQgcmVhZHMgdGhlIGRhdGEgb2YgdGhlIGNsdWJzIGZyb20gdGhlIGZvbGRlciBmb3IgZWFjaCBjbHViLCBzdG9yZXMgdGhlbSBpbiBhIGxpc3QsIGFuZCBzYXZlcyBpdCBpbiBhbiBvYmplY3QgaW4gdGhlIGxhc3QgZnVuY3Rpb24gY2FsbCBvZiB0aGlzIHNjcmlwdC4gDQoNCg0KZm9yIChpIGluICgxOiBsZW5ndGgoY2x1Yl9zdHIpKSkgew0KICANCiAgY2x1Yl9pZCA8LSBjbHViX3N0cltpXQ0KICANCiAgIyByZWFkIHRoZSBkYXRhIGZyb20gdGhlIGNsdWINCiAgY2x1YmRhdGEgPC0gcmVhZC5jc3YocGFzdGUoImNsdWJzLyIsIGNsdWJfaWQsICIvIiwgImVnb0RhdGFfZXh0ZW5kZWQuY3N2Iiwgc2VwID0gIiIpLCByb3cubmFtZXMgPSBOVUxMLA0KICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiLCIpDQogICMgc2F2aW5nIGNsdWIgc2l6ZQ0KICBzaXplIDwtIGxlbmd0aChjbHViZGF0YVssICJnZW5kZXIiXSkNCiAgIyB0aGUgbnVtYmVyIG9mIG1vbnRocyB0aGF0IHdlIHdhbnQgdG8gYWRkIGFzIHdhdmVzDQogIG5fd2F2ZXMgPC0gMTINCiAgDQogICMgbGV0J3MgbG9hZCB0aGUgZnJpZW5kc2hpcC9mb2xsb3dpbmcgbmV0d29yaw0KICBmcmllbmRfZGF0YSA8LSBhcy5tYXRyaXgocmVhZC5jc3YocGFzdGUoImNsdWJzLyIsIGNsdWJfaWQsICIvIiwgInNvY2lhbG5ldHdvcmsuY3N2Iiwgc2VwID0gIiIpLCByb3cubmFtZXMgPSBOVUxMLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIiwiKSkNCiAgIyByZW1vdmUgdGhlIGZpcnN0IGNvbHVtbiAocmVwcmVzZW50cyBpbmRleCBtYWRlIGluIHRoZSBjc3YpDQogIGZyaWVuZF9kYXRhIDwtIGZyaWVuZF9kYXRhWywgMjpuY29sKGZyaWVuZF9kYXRhKV0NCiAgDQogICMgYW5kIGFub255bWl6ZSB0aGUgdXNlciBpZCwgd2l0aCBvdXIgaWQtbWFrZXIgZnVuY3Rpb24NCiAgDQogIGZha2VpZCA8LSBmaWRtYWtlcihucm93KGZyaWVuZF9kYXRhKSkgICMgZ2VuZXJhdGUgcmFuZG9tIGlkDQogIGNvbG5hbWVzKGZyaWVuZF9kYXRhKSA8LSBmYWtlaWQgICMgYW5vbnltaXppbmcgdXNlcnMNCiAgDQogICMgbGV0J3MgbG9hZCB0aGUga3Vkb3MgbmV0d29yaw0KICBwYXRoIDwtIHBhc3RlKCJjbHVicy8iLCBjbHViX2lkLCAiLyIsICJrdWRvcyIsIHNlcCA9ICIiKSAgIyBjcmVhdGUgcGF0aA0KICB7DQogICAga3Vkb193MSA8LSBhcy5tYXRyaXgocmVhZC5jc3YocGFzdGUocGF0aCwgIjEtMjAxOS5jc3YiLCBzZXAgPSAiIiksIHJvdy5uYW1lcyA9IE5VTEwsIHNlcCA9ICIsIikpDQogICAga3Vkb193MSA8LSBrdWRvX3cxWywgMjpuY29sKGt1ZG9fdzEpXQ0KICAgIGt1ZG9fdzIgPC0gYXMubWF0cml4KHJlYWQuY3N2KHBhc3RlKHBhdGgsICIyLTIwMTkuY3N2Iiwgc2VwID0gIiIpLCByb3cubmFtZXMgPSBOVUxMLCBzZXAgPSAiLCIpKQ0KICAgIGt1ZG9fdzIgPC0ga3Vkb193MlssIDI6bmNvbChrdWRvX3cyKV0NCiAgICBrdWRvX3czIDwtIGFzLm1hdHJpeChyZWFkLmNzdihwYXN0ZShwYXRoLCAiMy0yMDE5LmNzdiIsIHNlcCA9ICIiKSwgcm93Lm5hbWVzID0gTlVMTCwgc2VwID0gIiwiKSkNCiAgICBrdWRvX3czIDwtIGt1ZG9fdzNbLCAyOm5jb2woa3Vkb193MyldDQogICAga3Vkb193NCA8LSBhcy5tYXRyaXgocmVhZC5jc3YocGFzdGUocGF0aCwgIjQtMjAxOS5jc3YiLCBzZXAgPSAiIiksIHJvdy5uYW1lcyA9IE5VTEwsIHNlcCA9ICIsIikpDQogICAga3Vkb193NCA8LSBrdWRvX3c0WywgMjpuY29sKGt1ZG9fdzQpXQ0KICAgIGt1ZG9fdzUgPC0gYXMubWF0cml4KHJlYWQuY3N2KHBhc3RlKHBhdGgsICI1LTIwMTkuY3N2Iiwgc2VwID0gIiIpLCByb3cubmFtZXMgPSBOVUxMLCBzZXAgPSAiLCIpKQ0KICAgIGt1ZG9fdzUgPC0ga3Vkb193NVssIDI6bmNvbChrdWRvX3c1KV0NCiAgICBrdWRvX3c2IDwtIGFzLm1hdHJpeChyZWFkLmNzdihwYXN0ZShwYXRoLCAiNi0yMDE5LmNzdiIsIHNlcCA9ICIiKSwgcm93Lm5hbWVzID0gTlVMTCwgc2VwID0gIiwiKSkNCiAgICBrdWRvX3c2IDwtIGt1ZG9fdzZbLCAyOm5jb2woa3Vkb193NildDQogICAga3Vkb193NyA8LSBhcy5tYXRyaXgocmVhZC5jc3YocGFzdGUocGF0aCwgIjctMjAxOS5jc3YiLCBzZXAgPSAiIiksIHJvdy5uYW1lcyA9IE5VTEwsIHNlcCA9ICIsIikpDQogICAga3Vkb193NyA8LSBrdWRvX3c3WywgMjpuY29sKGt1ZG9fdzcpXQ0KICAgIGt1ZG9fdzggPC0gYXMubWF0cml4KHJlYWQuY3N2KHBhc3RlKHBhdGgsICI4LTIwMTkuY3N2Iiwgc2VwID0gIiIpLCByb3cubmFtZXMgPSBOVUxMLCBzZXAgPSAiLCIpKQ0KICAgIGt1ZG9fdzggPC0ga3Vkb193OFssIDI6bmNvbChrdWRvX3c4KV0NCiAgICBrdWRvX3c5IDwtIGFzLm1hdHJpeChyZWFkLmNzdihwYXN0ZShwYXRoLCAiOS0yMDE5LmNzdiIsIHNlcCA9ICIiKSwgcm93Lm5hbWVzID0gTlVMTCwgc2VwID0gIiwiKSkNCiAgICBrdWRvX3c5IDwtIGt1ZG9fdzlbLCAyOm5jb2woa3Vkb193OSldDQogICAga3Vkb193MTAgPC0gYXMubWF0cml4KHJlYWQuY3N2KHBhc3RlKHBhdGgsICIxMC0yMDE5LmNzdiIsIHNlcCA9ICIiKSwgcm93Lm5hbWVzID0gTlVMTCwgc2VwID0gIiwiKSkNCiAgICBrdWRvX3cxMCA8LSBrdWRvX3cxMFssIDI6bmNvbChrdWRvX3cxMCldDQogICAga3Vkb193MTEgPC0gYXMubWF0cml4KHJlYWQuY3N2KHBhc3RlKHBhdGgsICIxMS0yMDE5LmNzdiIsIHNlcCA9ICIiKSwgcm93Lm5hbWVzID0gTlVMTCwgc2VwID0gIiwiKSkNCiAgICBrdWRvX3cxMSA8LSBrdWRvX3cxMVssIDI6bmNvbChrdWRvX3cxMSldDQogICAga3Vkb193MTIgPC0gYXMubWF0cml4KHJlYWQuY3N2KHBhc3RlKHBhdGgsICIxMi0yMDE5LmNzdiIsIHNlcCA9ICIiKSwgcm93Lm5hbWVzID0gTlVMTCwgc2VwID0gIiwiKSkNCiAgICBrdWRvX3cxMiA8LSBrdWRvX3cxMlssIDI6bmNvbChrdWRvX3cxMildDQogIH0NCiAga3Vkb3MgPC0gYXJyYXkoYyhrdWRvX3cxLCBrdWRvX3cyLCBrdWRvX3czLCBrdWRvX3c0LCBrdWRvX3c1LCBrdWRvX3c2LCBrdWRvX3c3LCBrdWRvX3c4LCBrdWRvX3c5LA0KICAgICAgICAgICAgICAgICAgIGt1ZG9fdzEwLCBrdWRvX3cxMSwga3Vkb193MTIpLCBkaW0gPSBjKHNpemUsIHNpemUsIG5fd2F2ZXMpKSAgI0t1ZG9zIG1hdHJpeA0KICANCiAga3Vkb19kYXRhIDwtIGlmZWxzZShrdWRvcyA+IDAsIDEsIDApICAjaWYgYXQgbGVhc3QgMSBLdWRvIGlzIHNlbmQsIHRpZSBleGlzdHMNCiAgDQogICMgcnVubmluZyB0aW1lIChpbiBob3VycyBwZXIgd2VlaykNCiAgdGltZV9ydW4gPC0gYXJyYXkoYyhjbHViZGF0YVssICJ0aW1lX3J1bl8xLjIwMTkiXSwgY2x1YmRhdGFbLCAidGltZV9ydW5fMi4yMDE5Il0sIGNsdWJkYXRhWywgInRpbWVfcnVuXzMuMjAxOSJdLA0KICAgICAgICAgICAgICAgICAgICAgIGNsdWJkYXRhWywgInRpbWVfcnVuXzQuMjAxOSJdLCBjbHViZGF0YVssICJ0aW1lX3J1bl81LjIwMTkiXSwgY2x1YmRhdGFbLCAidGltZV9ydW5fNi4yMDE5Il0sDQogICAgICAgICAgICAgICAgICAgICAgY2x1YmRhdGFbLCAidGltZV9ydW5fNy4yMDE5Il0sIGNsdWJkYXRhWywgInRpbWVfcnVuXzguMjAxOSJdLCBjbHViZGF0YVssICJ0aW1lX3J1bl85LjIwMTkiXSwNCiAgICAgICAgICAgICAgICAgICAgICBjbHViZGF0YVssICJ0aW1lX3J1bl8xMC4yMDE5Il0sIGNsdWJkYXRhWywgInRpbWVfcnVuXzExLjIwMTkiXSwgY2x1YmRhdGFbLCAidGltZV9ydW5fMTIuMjAxOSJdKSwNCiAgICAgICAgICAgICAgICAgICAgZGltID0gYyhzaXplLCAxLCBuX3dhdmVzKSkgICMgbWludXRlcyBwZXIgbW9udGgNCiAgdGltZV9ydW5faCA8LSB0aW1lX3J1bi82MCAgIyBob3VycyBwZXIgbW9udGgNCiAgIyB0aW1lX3J1bl8zMCA8LSB0aW1lX3J1bl9oICogMiAjIGhhbGYgaG91cnMNCiAgdGltZSA8LSBjZWlsaW5nKHRpbWVfcnVuX2gvNCkgICMgcGVyIHdlZWsNCiAgDQogICMgcHJvdmlkZSBzb21lIGRlc2NyaXB0aXZlcyANCiAgIyB0aW1lICU+JSAjIGFic29sdXRlIHRhYmxlKCkgdGltZSAlPiUgIyBwcm9wb3J0aW9uYXRlIHRhYmxlKCkgJT4lDQogICMgcHJvcC50YWJsZSgpICU+JSByb3VuZCgzKSBjdW1wcm9wIDwtIHRpbWUgJT4lICMgY3VtdWxhdGl2ZSBwcm9wb3J0aW9ucyB0YWJsZSgpICU+JQ0KICAjIHByb3AudGFibGUoKSAlPiUgcm91bmQoMykgJT4lIGN1bXN1bSgpIHByaW50KGN1bXByb3ApIDEtY3VtcHJvcCAjIHRoZSBwZXJjZW50YWdlIG9mIGFsbA0KICAjIHZhbHVlcyB0aGF0IGlzIGhpZ2hlciB0aGFuIHRoZSBwYXJ0aWN1bGFyIHZhbHVlDQogIA0KICAjIG91ciBpZGVhIHdhcyB0byBjYXAgcnVubmluZyB0aW1lIG9mIGF0IDcrIGhvdXJzIHBlciB3ZWVrLiAgdGhpcyByZXN1bHRlZCBpbiByYXRoZXIgc21vb3RoDQogICMgKHJpZ2h0LXNrZXdlZCkgZGlzdHJpYnV0aW9uIG9mIHZhbHVlcyBmb3IgbW9zdCBjbHVicyBleGNlcHQgZm9yIGNsdWIgNSwgd2hlcmUgNysgaG91cnMNCiAgIyBjYXRlZ29yeSB3YXMgaGlnaGx5IHBvcHVsYXRlZCBvdmVyIHRpbWUuICBXZSBhZGRlZCAyIGV4dHJhIGNhdGVnb3JpZXMgKGNhcHBlZCBvZiBhdCA5Kw0KICAjIGhvdXJzKTsgdGhpcyByZXN1bHRlZCBpbiBhIHJhdGhlciBzbW9vdGggZGlzdHJpYnV0aW9uIGZvciB0aGlzIGNsdWIuDQogIA0KICBpZiAoY2x1Yl9pZCA9PSAiY2x1YmlkNSIpIHsNCiAgICB0aW1lX3J1bl90ZW1wIDwtIGlmZWxzZSh0aW1lID4gOSwgOSwgdGltZSkNCiAgfSBlbHNlIHsNCiAgICB0aW1lX3J1bl90ZW1wIDwtIGlmZWxzZSh0aW1lID4gNywgNywgdGltZSkNCiAgfQ0KICANCiAgIyBydW5uaW5nIGZyZXF1ZW5jeSAodGltZXMgcGVyIHdlZWspDQogIGZyZXFfcnVuIDwtIGFycmF5KGMoY2x1YmRhdGFbLCAiWC5ydW5fMS4yMDE5Il0sIGNsdWJkYXRhWywgIlgucnVuXzIuMjAxOSJdLCBjbHViZGF0YVssICJYLnJ1bl8zLjIwMTkiXSwNCiAgICAgICAgICAgICAgICAgICAgICBjbHViZGF0YVssICJYLnJ1bl80LjIwMTkiXSwgY2x1YmRhdGFbLCAiWC5ydW5fNS4yMDE5Il0sIGNsdWJkYXRhWywgIlgucnVuXzYuMjAxOSJdLCBjbHViZGF0YVssDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlgucnVuXzcuMjAxOSJdLCBjbHViZGF0YVssICJYLnJ1bl84LjIwMTkiXSwgY2x1YmRhdGFbLCAiWC5ydW5fOS4yMDE5Il0sIGNsdWJkYXRhWywgIlgucnVuXzEwLjIwMTkiXSwNCiAgICAgICAgICAgICAgICAgICAgICBjbHViZGF0YVssICJYLnJ1bl8xMS4yMDE5Il0sIGNsdWJkYXRhWywgIlgucnVuXzEyLjIwMTkiXSksIGRpbSA9IGMoc2l6ZSwgMSwgbl93YXZlcykpICAjIGZyZXF1ZW5jaWVzIHBlciBtb250aA0KICBmcmVxdWVuY2llcyA8LSBjZWlsaW5nKGZyZXFfcnVuLzQpICAjIHBlciB3ZWVrDQogIGZyZXFfcnVuX3RlbXAgPC0gaWZlbHNlKGZyZXF1ZW5jaWVzID4gNywgNywgZnJlcXVlbmNpZXMpICAjIGNhcCBvZmYgYXQgNyB0aW1lcyBwZXIgd2Vlaw0KICANCg0KICAjIHdlIGRlYWwgd2l0aCBjb21wb3NpdGlvbiBjaGFuZ2U6IGFjdG9ycyBqb2luaW5nIHRoZSBTdHJhdmEgY2x1YiBvdmVyIHRpbWUgKHdlIGFzc3VtZSBubyBsZWF2ZXJzKTsNCiAgIyBub2RlIHNldCB3YXMgZGV0ZXJtaW5lZCBhdCB0MTI7IGZyb20gdGhlcmUsIHdlICdzY3JhcGVkIGJhY2t3YXJkJyBpbiB0aW1lOw0KICAjIGRvd24gYmVsb3csIHdlIGNvdW50IHRoZSBudW1iZXIgb2Ygd2F2ZXMsIGZyb20gdDEgb253YXJkLCB0aGF0IGFjdG9ycyBzY29yZWQgMCBvbiBhbGwgRFZzIA0KICAjIHRoYXQgaXMsIHdoZXJlIHRoZSBzdW0gb2YgbnVtYmVyIG9mIGt1ZG9zIGluLSBhbmQgb3V0LXRpZXMgKG5ldHdvcmsgYWN0aXZpdHkpLCBmcmVxdWVuY3kgYW5kIHZvbHVtZSAoYmVoYXZpb3IgYWN0aXZpdHkpLCBlcXVhbHMgMC4NCiAgIyB0aGlzIG51bWJlciArMSBpcyB0aGUgd2F2ZSBpbiB3aGljaCBhY3RvcnMgYXJlIGFzc3VtZWQgdG8gaGF2ZSBqb2luZWQgDQogICMgb3VyIG1vZGVsIHdpbGwgdGhlbiBhc3N1bWUgdGhhdCBlbnRyaWVzIG9mIGpvaW5lcnMgaGFwcGVuIGF0IHRoZSBtaWRwb2ludCBvZiB0aGUgdW5vYnNlcnZlZCB0aW1lIHBlcmlvZCBiZXR3ZWVuIHRoaXMgYW5kIHRoZSBzdWJzZXF1ZW50IHdhdmUuDQogIA0KICBzdW1fZHYgPC0gbWF0cml4KE5BLCBucj1zaXplLCBuYz1uX3dhdmVzKQ0KICANCiAgZm9yIChpIGluIDE6c2l6ZSkgew0KICAgIGZvciAoaiBpbiAxOm5fd2F2ZXMpIHsNCiAgICAgIHN1bV9kdltpLGpdIDwtIHN1bSggYyggIyBzdW0gb2YNCiAgICAgICAga3Vkb19kYXRhW2ksLGpdLCAgICAgIyBudW1iZXIgb2Ygb3V0LXRpZXMgb2YgYWN0b3IgaSBhdCB0aW1lIGoNCiAgICAgICAga3Vkb19kYXRhWyxpLGpdLCAgICAgIyBudW1iZXIgb2YgaW4tdGllcyBvZiBhY3RvciBpIGF0IHRpbWUgag0KICAgICAgICBmcmVxX3J1bl90ZW1wW2ksLGpdLCAjIHJ1bm5pbmcgZnJlcXVlbmN5IG9mIGkgYXQgag0KICAgICAgICB0aW1lX3J1bl90ZW1wW2ksLGpdICAjIHJ1bm5pbmcgdm9sdW1lIG9mIGkgYXQgag0KICAgICAgKSkNCiAgICB9DQogIH0NCg0KICB0IDwtIHJlcCgxLCBzaXplKSAjIHN0YXJ0aW5nIGVudHJ5IHQ9MQ0KICBmb3IgKCBpIGluIDE6c2l6ZSkgew0KICAgIGZvciggaiBpbiAxOm5fd2F2ZXMpIHsNCiAgICAgIA0KICAgICAgIyBpZiBzdW0gPSAwIGZvciBhY3RvciBpIGluIHdhdmUgaiwgDQogICAgICAjIGluY3JlbWVudCB0aGUgZW50cnkgcGVyaW9kIHQgd2l0aCAxDQogICAgICBpZihzdW1fZHZbaSxqXSA9PSAwKSB7DQogICAgICAgIHRbaV0gPSB0W2ldICsgMQ0KICAgICAgfQ0KICAgICAgIyBpZiBzdW0gPiAwLCBicmVhayB0aGUgbG9vcA0KICAgICAgaWYoc3VtX2R2W2ksal0gPiAwKSB7DQogICAgICAgIGJyZWFrDQogICAgICB9DQogICAgfQ0KICB9DQogICMgZW50cnktd2F2ZXMgY2Fubm90IHN1cnBhc3MgMTI6DQogIHRbd2hpY2godD4xMildIDwtIDEyDQogIA0KICAjIGFjdG9ycyB0aGF0IGpvaW4gYXQgdD0xMiwgY2Fubm90IGJlIGluY2x1ZGVkIGludG8gdGhlIGVzdGltYXRpb247DQogICMgdGhleSBhcmUgc3RydWN0dXJhbCB6ZXJvOw0KICAjIHdlIGV4Y2x1ZGUgdGhlIHJvd3MgKGkpIGFuZCBjb2x1bW5zIChqKSBjb3JyZXNwb25kaW5nIHRvIHRoZXNlIGFjdG9ycw0KICAjIGZyb20gdGhlIGt1ZG9zIG1hdHJpY2VzDQogICMgd2UgYWxzbyByZW1vdmUgdGhlc2UgYWN0b3JzJyBlbGVtZW50cyBmcm9tIHRoZSBydW5uaW5nIHZhcmlhYmxlIG9iamVjdHMuDQogIA0KICAjIGZpcnN0LCBpZGVudGlmeSB0aGVzZSBhY3RvcnMgKGdldCB0aGVpciBpbmRleCkNCiAgeCA8LSB3aGljaCh0PT0xMikNCiAgDQogICMgbWFrZSBuZXcgYXJyYXlzIHRvIHN0b3JlIGVudHJpZXMgb2YgYWN0b3JzLCBleGNsdWRpbmcgc3RydWN0dXJhbCB6ZXJvcw0KICBrdWRvX2RhdGEubm0gPC0gYXJyYXkoTkEsIGRpbSA9IGMoc2l6ZSAtIGxlbmd0aCh4KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgLSBsZW5ndGgoeCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX3dhdmVzKSkNCiAgZnJlcV9ydW5fdGVtcC5ubSA8LSBhcnJheShOQSwgZGltID0gYyhzaXplIC0gbGVuZ3RoKHgpLCAxLCBuX3dhdmVzKSkNCiAgdGltZV9ydW5fdGVtcC5ubSA8LSBhcnJheShOQSwgZGltID0gYyhzaXplIC0gbGVuZ3RoKHgpLCAxLCBuX3dhdmVzKSkNCiAgDQogICMgZmlsbCBhcnJheXMgIA0KICBmb3IgKGkgaW4gMTpuX3dhdmVzKSB7DQogICAga3Vkb19kYXRhLm5tWywsaV0gPC0gc3Vic2V0KGt1ZG9fZGF0YVssLGldLCBzZWxlY3QgPSAteClbLXgsIF0NCiAgICBmcmVxX3J1bl90ZW1wLm5tWywsaV0gPC0gZnJlcV9ydW5fdGVtcFssLGldWy14XQ0KICAgIHRpbWVfcnVuX3RlbXAubm1bLCxpXSA8LSB0aW1lX3J1bl90ZW1wWywsaV1bLXhdDQogIH0NCiAgDQogICMgYW5kIGFkanVzdCBuZXR3b3JrIHNpemUgYWNjb3JkaW5nbHkNCiAgc2l6ZS5ubSA8LSBzaXplIC0gbGVuZ3RoKHgpDQoNCg0KICAjIGNyZWF0ZSBhIGxpc3QgZm9yIHRoZSB0aW1lcyBvZiBjb21wb3NpdGlvbiBjaGFuZ2UNCiAgIyBzZWUgNC4zLjMgb2YgUlNJRU5BIG1hbnVhbA0KICBjb21wIDwtIHJlcChsaXN0KE5BKSwgc2l6ZS5ubSkNCiAgZm9yIChpIGluIDE6c2l6ZS5ubSkgew0KICAgIGNvbXBbW2ldXSA8LSBjKHRbLXhdW2ldLG5fd2F2ZXMpDQogIH0NCiAgDQogICMgUlNpZW5hIEdPRiBmdW5jdGlvbnMgZG8gbm90IGNvbWJpbmUgcHJvcGVybHkgd2l0aCB0aGUgbWV0aG9kIG9mIGpvaW5lcnMgYW5kIGxlYXZlcnMNCiAgIyBCdXQgaWYgd2UgdXNlIE5BIGNvZGVzIGZvciB0aGUgdGllIHZhcmlhYmxlcyBvZiBhYnNlbnQgYWN0b3JzLCBzaWVuYUdPRiB3aWxsIHdvcmsgcHJvcGVybHkuDQoNCiAgIyBnZXQgdGltZSBvZiBqb2luaW5nIGZvciBlYWNoIGFjdG9yOg0KICB0X2ogPC0gdFt3aGljaCh0IT0xMildIA0KICANCiAgIyBmb3IgYWN0b3JzIHRoYXQgYXJlIHBhcnRpYWxseSBhYnNlbnQsIHNldCBlbnRyaWVzIGluIHRoZSBrdWRvcy1tYXRyaXggdG8gTkEsIA0KICAjIGZvciB0aG9zZSB3YXZlcyB0aGV5IGFyZSBhYnNlbnQsIGkuZS4sIHdhdmVzIDEgOiAodF9qLTEpDQogIGZvciAoIGkgaW4gd2hpY2godF9qPjEpKSB7ICAgICAjIGZvciBhY3RvcnMgdGhhdCBqb2luIGxhdGVyDQogICAgZm9yIChqIGluIDE6bl93YXZlcykgeyAgICAgICAjIGZvciBhbGwgd2F2ZXMNCiAgICAgIGlmKHRfaltpXT5qKSB7ICAgICAgICAgICAgICMgaWYgdGhlaXIgdGltZSBvZiBqb2luaW5nIGNvbWVzIGFmdGVyIHRoZSBjdXJyZW50IHdhdmUNCiAgICAgICAga3Vkb19kYXRhLm5tW2ksLGpdIDwtIE5BICMgc2V0IHRoZWlyIG91dC1kZWdyZWUgZW50cmllcyB0byBOQQ0KICAgICAgICBrdWRvX2RhdGEubm1bLGksal0gPC0gTkEgIyBidXQgYWxzbyB0aGVpciBpbi1kZWdyZWUgZW50cmllcw0KICAgICAgICBmcmVxX3J1bl90ZW1wLm5tW2ksMSxqXSA8LSBOQSAjIGFuZCB0aGVpciBiZWhhdmlvcnMNCiAgICAgICAgdGltZV9ydW5fdGVtcC5ubVtpLDEsal0gPC0gTkENCiAgICAgIH1nDQogICAgfQ0KICB9DQoNCiAgIyAgbGV0J3MgbG9hZCBvdGhlciBhY3Rpdml0eSBkYXRhIChlLmcuLCBjeWNsaW5nLCBzd2ltbWluZykgdGltZQ0KICB0aW1lX3JpZGUgPC0gYXJyYXkoYyhjbHViZGF0YVssICJ0aW1lX3JpZGVfMS4yMDE5Il0sIGNsdWJkYXRhWywgInRpbWVfcmlkZV8yLjIwMTkiXSwgY2x1YmRhdGFbLCAidGltZV9yaWRlXzMuMjAxOSJdLA0KICAgICAgICAgICAgICAgICAgICAgICBjbHViZGF0YVssICJ0aW1lX3JpZGVfNC4yMDE5Il0sIGNsdWJkYXRhWywgInRpbWVfcmlkZV81LjIwMTkiXSwgY2x1YmRhdGFbLCAidGltZV9yaWRlXzYuMjAxOSJdLA0KICAgICAgICAgICAgICAgICAgICAgICBjbHViZGF0YVssICJ0aW1lX3JpZGVfNy4yMDE5Il0sIGNsdWJkYXRhWywgInRpbWVfcmlkZV84LjIwMTkiXSwgY2x1YmRhdGFbLCAidGltZV9yaWRlXzkuMjAxOSJdLA0KICAgICAgICAgICAgICAgICAgICAgICBjbHViZGF0YVssICJ0aW1lX3JpZGVfMTAuMjAxOSJdLCBjbHViZGF0YVssICJ0aW1lX3JpZGVfMTEuMjAxOSJdLCBjbHViZGF0YVssICJ0aW1lX3JpZGVfMTIuMjAxOSJdKSwNCiAgICAgICAgICAgICAgICAgICAgIGRpbSA9IGMoc2l6ZSwgMSwgbl93YXZlcykpDQogIHRpbWVfb3RoZXIgPC0gYXJyYXkoYyhjbHViZGF0YVssICJ0aW1lX290aGVyXzEuMjAxOSJdLCBjbHViZGF0YVssICJ0aW1lX290aGVyXzIuMjAxOSJdLCBjbHViZGF0YVssDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidGltZV9vdGhlcl8zLjIwMTkiXSwgY2x1YmRhdGFbLCAidGltZV9vdGhlcl80LjIwMTkiXSwgY2x1YmRhdGFbLCAidGltZV9vdGhlcl81LjIwMTkiXSwgY2x1YmRhdGFbLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0aW1lX290aGVyXzYuMjAxOSJdLCBjbHViZGF0YVssICJ0aW1lX290aGVyXzcuMjAxOSJdLCBjbHViZGF0YVssICJ0aW1lX290aGVyXzguMjAxOSJdLCBjbHViZGF0YVssDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0aW1lX290aGVyXzEyLjIwMTkiXSksIGRpbSA9IGMoc2l6ZSwgMSwgbl93YXZlcykpDQoNCiAgdGltZV9vdGhlciA8LSB0aW1lX3JpZGUgKyB0aW1lX290aGVyICAjIG1pbnV0ZXMgcGVyIG1vbnRoDQogIHRpbWVfb3RoZXJfaCA8LSB0aW1lX290aGVyLzYwICAjIGhvdXJzIHBlciBtb250aA0KICAjIHRpbWVfb3RoZXJfaCA8LSB0aW1lX290aGVyX2ggKiAyICMgaGFsZiBob3Vycw0KICB0aW1lIDwtIGNlaWxpbmcodGltZV9vdGhlcl9oLzQpICAjcGVyIHdlZWsNCiAgdGltZV9vdGhlcl90ZW1wIDwtIGlmZWxzZSh0aW1lID4gNywgNywgdGltZSkgICMgY2FwIG9mZiBhdCA3IGhvdXJzIHBlciB3ZWVrDQogICN0YWJsZSh0aW1lX290aGVyX3RlbXApDQogIA0KICAjIGZyZXF1ZW5jeQ0KICBmcmVxX3JpZGUgPC0gYXJyYXkoYyhjbHViZGF0YVssICJYLnJpZGVfMS4yMDE5Il0sIGNsdWJkYXRhWywgIlgucmlkZV8yLjIwMTkiXSwgY2x1YmRhdGFbLCAiWC5yaWRlXzMuMjAxOSJdLA0KICAgICAgICAgICAgICAgICAgICAgICBjbHViZGF0YVssICJYLnJpZGVfNC4yMDE5Il0sIGNsdWJkYXRhWywgIlgucmlkZV81LjIwMTkiXSwgY2x1YmRhdGFbLCAiWC5yaWRlXzYuMjAxOSJdLCBjbHViZGF0YVssDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJYLnJpZGVfNy4yMDE5Il0sIGNsdWJkYXRhWywgIlgucmlkZV84LjIwMTkiXSwgY2x1YmRhdGFbLCAiWC5yaWRlXzkuMjAxOSJdLCBjbHViZGF0YVssICJYLnJpZGVfMTAuMjAxOSJdLA0KICAgICAgICAgICAgICAgICAgICAgICBjbHViZGF0YVssICJYLnJpZGVfMTEuMjAxOSJdLCBjbHViZGF0YVssICJYLnJpZGVfMTIuMjAxOSJdKSwgZGltID0gYyhzaXplLCAxLCBuX3dhdmVzKSkNCiAgZnJlcV9vdGhlciA8LSBhcnJheShjKGNsdWJkYXRhWywgIlgub3RoZXJfMS4yMDE5Il0sIGNsdWJkYXRhWywgIlgub3RoZXJfMi4yMDE5Il0sIGNsdWJkYXRhWywgIlgub3RoZXJfMy4yMDE5Il0sDQogICAgICAgICAgICAgICAgICAgICAgICBjbHViZGF0YVssICJYLm90aGVyXzQuMjAxOSJdLCBjbHViZGF0YVssICJYLm90aGVyXzUuMjAxOSJdLCBjbHViZGF0YVssICJYLm90aGVyXzYuMjAxOSJdLCBjbHViZGF0YVssDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiWC5vdGhlcl83LjIwMTkiXSwgY2x1YmRhdGFbLCAiWC5vdGhlcl84LjIwMTkiXSwgY2x1YmRhdGFbLCAiWC5vdGhlcl85LjIwMTkiXSwgY2x1YmRhdGFbLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJYLm90aGVyXzEwLjIwMTkiXSwgY2x1YmRhdGFbLCAiWC5vdGhlcl8xMS4yMDE5Il0sIGNsdWJkYXRhWywgIlgub3RoZXJfMTIuMjAxOSJdKSwgZGltID0gYyhzaXplLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAxLCBuX3dhdmVzKSkNCiAgDQogIGZyZXFfb3RoZXIgPC0gZnJlcV9yaWRlICsgZnJlcV9vdGhlcg0KICBmcmVxdWVuY2llcyA8LSBjZWlsaW5nKGZyZXFfb3RoZXIvNCkNCiAgZnJlcV9vdGhlcl90ZW1wIDwtIGlmZWxzZShmcmVxdWVuY2llcyA+IDcsIDcsIGZyZXF1ZW5jaWVzKQ0KICANCiAgIyBhbmQgYWdhaW4sIGV4Y2x1ZGUgdGhlIHN0cnVjdHVyYWwgemVyb3M6DQogIGZyZXFfb3RoZXJfdGVtcC5ubSA8LSBhcnJheShOQSwgZGltID0gYyhzaXplLm5tLCAxLCBuX3dhdmVzKSkNCiAgdGltZV9vdGhlcl90ZW1wLm5tIDwtIGFycmF5KE5BLCBkaW0gPSBjKHNpemUubm0sIDEsIG5fd2F2ZXMpKQ0KICBmb3IgKGkgaW4gMTpuX3dhdmVzKSB7DQogICAgZnJlcV9vdGhlcl90ZW1wLm5tWywsaV0gPC0gZnJlcV9vdGhlcl90ZW1wWywsaV1bLXhdDQogICAgdGltZV9vdGhlcl90ZW1wLm5tWywsaV0gPC0gdGltZV9vdGhlcl90ZW1wWywsaV1bLXhdDQogIH0NCiAgDQogICMgc2VwYXJhdGluZyBtYWxlL2ZlbWFsZS9vdGhlcg0KICANCiAgbWFsZSA8LSBpZmVsc2UoY2x1YmRhdGFbLCAiZ2VuZGVyIl1bLXhdID09ICJNIiwgMSwwKQ0KICBmZW1hbGUgPC0gaWZlbHNlKGNsdWJkYXRhWywgImdlbmRlciJdWy14XSA9PSAiRiIsIDEsIDApDQogIG90aGVyIDwtIGlmZWxzZShjbHViZGF0YVssICJnZW5kZXIiXVsteF0gPT0gIk8iLCAxLCAwKQ0KICANCiAgIyBzcGVjaWZ5IG1vbnRocyBvZiB3aW50ZXIgaW4gY2FzZSB3ZSB3YW50IHRvIHVzZSBpdCBhcyBhIHZhcnlpbmcgY292YXJpYXRlIHN0YXJ0cyB3aXRoDQogICMgZGVjZW1iZXINCiAgd2ludGVyIDwtIHJlcChjKDEsIDEsIDEsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDApLCBzaXplLm5tKQ0KICB3aW50ZXIgPC0gbWF0cml4KHdpbnRlciwgbnIgPSBzaXplLm5tLCBuYyA9IG5fd2F2ZXMsIGJ5cm93ID0gVFJVRSkNCiAgDQogICMgaW4gY2FzZSB3ZSB3YW50IHRvIGNvbmRpdGlvbiBvbiB0aGUgZmlyc3QgYWN0aXZpdHkgcmVjb3JkZWQgYnkgdXNlcnM6DQogIHN0X3lyIDwtIGNsdWJkYXRhWywnc3RhcnRfZGF0ZV95ZWFyJ10NCiAgc3RfeXIgPC0gc3RfeXJbLXhdICNleGNsdWRlIE5Bcw0KICANCiAgc3RhcnRfeWVhciA8LSBpZmVsc2Uoc3RfeXIgPD0gMjAxMSwgMSwgDQogICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShzdF95ciA9PSAyMDEyLCAyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHN0X3lyID09IDIwMTMsIDMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHN0X3lyID09IDIwMTQsIDQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShzdF95ciA9PSAyMDE1LCA1LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHN0X3lyID09IDIwMTYsIDYsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHN0X3lyID09IDIwMTcsIDcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShzdF95ciA9PSAyMDE4LCA4LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgOSkpKSkpKSkpDQogICNoaXN0KHN0YXJ0X3llYXIpDQogIA0KICAjIGNyZWF0ZSBhIGxpc3QgY29udGFpbmluZyBhbGwgdGhlIHJlYWQgY2x1YiBkYXRhIGZvciB0aGUgY3VycmVudCBjbHViDQogIGNsdWIgPC0gbGlzdChmcmllbmRzaGlwID0gZnJpZW5kX2RhdGEsIGt1ZG8gPSBrdWRvX2RhdGEubm0sIGZyZXFfcnVuID0gZnJlcV9ydW5fdGVtcC5ubSwgdGltZV9ydW4gPSB0aW1lX3J1bl90ZW1wLm5tLA0KICAgICAgICAgICAgICAgZnJlcV9vdGhlciA9IGZyZXFfb3RoZXJfdGVtcC5ubSwgdGltZV9vdGhlciA9IHRpbWVfb3RoZXJfdGVtcC5ubSwgd2ludGVyID0gd2ludGVyLCBtYWxlID0gbWFsZSwgZmVtYWxlID0gZmVtYWxlLA0KICAgICAgICAgICAgICAgb3RoZXIgPSBvdGhlciwgbmV0c2l6ZSA9IHNpemUubm0sIGNvbXBvc2l0aW9uID0gY29tcCwgbm92aWNlPXN0YXJ0X3llYXIpDQogIA0KICANCiAgIyBzYXZlIHRoZSBjbHViIG9iamVjdA0KICBzYXZlKGNsdWIsIGZpbGUgPSBwYXN0ZSgiY2x1YnMvIiwgImNsdWIiLCBjbHViX2lkLCAiLlJEYXRhIiwgc2VwID0gIiIpKQ0KICANCn0NCg0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQoNCiMgTm93IHRoYXQgd2UgaGF2ZSBzYXZlZCB0aGUgY2x1YmRhdGEgZm9yIGFsbCBjbHVicywgbGV0J3MgY29tYmluZSB0aGVtIGluIG9uZSBsaXN0DQoNCiMgZmlyc3QgY2xlYW4gdGhlIHdvcmtpbmcgZGlyZWN0b3J5LCBleGNlcHQgb3VyIGNsdWIgc3RyaW5nOyB3ZSBzdGlsbCBuZWVkIHRoYXQgb25lLg0KIyBhbmQgb3VyIGZ1bmN0aW9uIGZsb2FkLlINCnJtKGxpc3QgPSBzZXRkaWZmKGxzKCksIGMoImNsdWJfc3RyIiwgImZsb2FkLlIiKSkpDQoNCg0KIyBsb2FkIGluIHRoZSBzZXBhcmF0ZSBjbHViLW9iamVjdHMNCmNsdWIxIDwtIGZsb2FkLlIocGFzdGUoImNsdWJzIiwgIi8iLCAiY2x1YiIsIGNsdWJfc3RyWzFdLCAiLlJEYXRhIiwgc2VwPSIiKSkNCmNsdWIyIDwtIGZsb2FkLlIocGFzdGUoImNsdWJzIiwgIi8iLCAiY2x1YiIsIGNsdWJfc3RyWzJdLCAiLlJEYXRhIiwgc2VwPSIiKSkNCmNsdWIzIDwtIGZsb2FkLlIocGFzdGUoImNsdWJzIiwgIi8iLCAiY2x1YiIsIGNsdWJfc3RyWzNdLCAiLlJEYXRhIiwgc2VwPSIiKSkNCmNsdWI0IDwtIGZsb2FkLlIocGFzdGUoImNsdWJzIiwgIi8iLCAiY2x1YiIsIGNsdWJfc3RyWzRdLCAiLlJEYXRhIiwgc2VwPSIiKSkNCmNsdWI1IDwtIGZsb2FkLlIocGFzdGUoImNsdWJzIiwgIi8iLCAiY2x1YiIsIGNsdWJfc3RyWzVdLCAiLlJEYXRhIiwgc2VwPSIiKSkNCg0KIyBhbmQgbWFrZSBhIGxpc3QgY29udGFpbmluZyBhbGwgdGhlIGNsdWJkYXRhDQpjbHViZGF0YSA8LSBsaXN0KGNsdWIxLCBjbHViMiwgY2x1YjMsIGNsdWI0LCBjbHViNSkNCg0KIyBzYXZlIHRoZSBvdXRwdXQNCnNhdmUoY2x1YmRhdGEsIGZpbGUgPSAiY2x1YnMvY2x1YmRhdGEuUkRhdGEiKQ0KDQoNCmBgYCANCg0KLS0tICANCg0KIyBjbHViZGF0YV9yc2llbmEuUkRhdGENCg0KVGhlIGZvbGxvd2luZyBzY3JpcHQgY3JlYXRlcyBhIGxpc3QgY29udGFpbmluZyBSU2llbmEgb2JqZWN0cyBmb3IgYWxsIGNsdWJzLiANCldlIGNyZWF0ZSBhIGxpc3QgY29udGFpbmluZyBSU2llbmEgb2JqZWN0cyB3aXRoIHJ1bm5pbmcgZnJlcXVlbmN5IGFzIHRoZSBiZWhhdmlvciB2YXJpYWJsZSAoY2x1YmRhdGFfcnNpZW5hX2ZyZXEuUkRhdGEpIGFuZCBydW5uaW5nIHZvbHVtZSAoY2x1YmRhdGFfcnNpZW5hX3ZvbC5SRGF0YSkuIA0KDQpOb3RlOiBpbiBzb21lIGdyb3VwcywgZGVwZW5kZW50IHZhcmlhYmxlcyAoZWl0aGVyIHRoZSBrdWRvcy1uZXR3b3JrIG9yIHJ1bm5pbmcgYmVoYXZpb3JzKSBvbmx5IGhhdmUgdXB3YXJkIG9yIGRvd253YXJkIGNoYW5nZXMgaW4gcGFydGljdWxhciBwZXJpb2RzLiBXZSBsaWZ0IFJTaWVuYSdzIGF1dG9tYXRpYyByZXN0cmljdGlvbiB0byAgZm9sbG93IHRoaXMgcGF0dGVybiBpbiB0aGUgc2ltdWxhdGlvbnMsIGJ5IHVzaW5nICcqYWxsb3dPbmx5KiA9IEZBTFNFJy4gVGhpcyBtdXN0IGJlIGRvbmUgZm9yIHRoZSBzdWJzZXF1ZW50IG1ldGEtYW5hbHlzaXMuDQoNCmBgYHtyIGV2YWw9RkFMU0V9DQojIGNsZWFuIHRoZSB3b3JraW5nIGVudmlyb25tZW50IA0Kcm0gKGxpc3QgPSBscyggKSkNCg0KIyBsb2FkIHRoZSBjbHViZGF0YQ0KbG9hZCgiY2x1YnMvY2x1YmRhdGEuUkRhdGEiKQ0Kc3RyKGNsdWJkYXRhKSAjIGluc3BlY3Qgc3RydWN0dXJlDQojIGNsdWJkYXRhIGlzIGEgbGlzdCBvZiA1IGxpc3RzLCANCiMgd2l0aCBlYWNoIG9mIHRoZXNlIGxpc3RzIGNvbnRhaW5pbmcgZGF0YSBvZiB0aGUgY29ycmVzcG9uZGluZyBjbHViLg0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQoNCg0KY2x1YmRhdGFfcnNpZW5hX2ZyZXEgPC0gbGlzdCgpDQpjbHViZGF0YV9yc2llbmFfdm9sIDwtIGxpc3QoKQ0KDQoNCmZvciAoaSBpbiAxOjUpIHsgDQogIGNsdWIgPC0gY2x1YmRhdGFbW2ldXQ0KICAjIHNwZWNpZnkgdGhlIHJvbGVzIG9mIHZhcmlhYmxlcw0KICBuYW1lcyhjbHViKQ0KICANCiAgIyBBOiBuZXR3b3JrIHZhcmlhYmxlcw0KICBrdWRvbmV0IDwtIHNpZW5hRGVwZW5kZW50KGNsdWIka3VkbywgYWxsb3dPbmx5ID0gRkFMU0UpICAjYXQgbGVhc3Qgb25lIEt1ZG8NCiAgDQogICMgQjogYmVoYXZpb3JhbCB2YXJpYWJsZXMNCiAgdGltZV9ydW4gPC0gc2llbmFEZXBlbmRlbnQoY2x1YiR0aW1lX3J1biwgdHlwZT0gImJlaGF2aW9yIiwgYWxsb3dPbmx5ID0gRkFMU0UpDQogIGZyZXFfcnVuIDwtIHNpZW5hRGVwZW5kZW50KGNsdWIkZnJlcV9ydW4sIHR5cGU9ICJiZWhhdmlvciIsIGFsbG93T25seSA9IEZBTFNFKQ0KICANCiAgdGltZV9vdGhlciA8LSB2YXJDb3ZhcihjbHViJHRpbWVfb3RoZXJbLCxdKQ0KICBmcmVxX290aGVyIDwtIHZhckNvdmFyKGNsdWIkZnJlcV9vdGhlclssLF0pDQoNCiAgIyBDOiBjb3ZhcmlhdGVzDQogIHdpbnRlciA8LSB2YXJDb3ZhcihjbHViJHdpbnRlcikNCiAgZ2VuZGVyIDwtIE5BICN3ZSBkaWNob3RvbWl6ZSBnZW5kZXIgYXMgYmluYXJ5IChtZW4gdnMuIHdvbWVuIGFuZCBvdGhlcikNCiAgZ2VuZGVyIDwtIGlmZWxzZShjbHViJG1hbGUgPT0gMSwgMSwgZ2VuZGVyKQ0KICBnZW5kZXIgPC0gaWZlbHNlKGNsdWIkZmVtYWxlID09IDEsIDIsIGdlbmRlcikNCiAgZ2VuZGVyIDwtIGlmZWxzZShjbHViJG90aGVyID09IDEsIDIsIGdlbmRlcikNCiAgZ2VuZGVyIDwtIGNvQ292YXIoZ2VuZGVyKQ0KICBub3ZpY2UgPC0gY29Db3ZhcihjbHViJG5vdmljZSkNCiAgDQogIA0KICAjIEQ6IGNvbXBvc2l0aW9uIGNoYW5nZSAoam9pbmVycykNCiAgam9pbmVycyA8LSBzaWVuYUNvbXBvc2l0aW9uQ2hhbmdlKGNsdWIkY29tcG9zaXRpb24pDQogIA0KICAjIG5vdyBjb21iaW5lIHRoZSBkZXBlbmRlbnQgYW5kIGluZGVwZW5kZW50IHZhcmlhYmxlcyBpbiBhIGRhdGEgb2JqZWN0DQogIG15ZGF0YSA8LSBzaWVuYURhdGFDcmVhdGUoa3Vkb25ldCwgZnJlcV9ydW4sIHRpbWVfb3RoZXIsIGZyZXFfb3RoZXIsIGdlbmRlciwgd2ludGVyLCBqb2luZXJzLCBub3ZpY2UpDQogIG15ZGF0YTIgPC0gc2llbmFEYXRhQ3JlYXRlKGt1ZG9uZXQsIHRpbWVfcnVuLCB0aW1lX290aGVyLCBmcmVxX290aGVyLCBnZW5kZXIsIHdpbnRlciwgam9pbmVycywgbm92aWNlKQ0KDQogICMgdGhpcyBmaW5pc2hlcyB0aGUgZGF0YSBzcGVjaWZpY2F0aW9uDQogIGNsdWJkYXRhX3JzaWVuYV9mcmVxW1tpXV0gPC0gbXlkYXRhDQogIGNsdWJkYXRhX3JzaWVuYV92b2xbW2ldXSA8LSBteWRhdGEyDQp9IA0KDQoNCiNjbGVhbiBlbnZpcm9ubWVudA0Kcm0obGlzdCA9IHNldGRpZmYobHMoKSwgYygiY2x1YmRhdGFfcnNpZW5hX2ZyZXEiLCAiY2x1YmRhdGFfcnNpZW5hX3ZvbCIpKSkNCg0KIyBzYXZlIHRoZSBvdXRwdXQNCnNhdmUoY2x1YmRhdGFfcnNpZW5hX2ZyZXEsIGZpbGUgPSAiY2x1YnMvY2x1YmRhdGFfcnNpZW5hX2ZyZXEuUkRhdGEiKQ0Kc2F2ZShjbHViZGF0YV9yc2llbmFfdm9sLCBmaWxlID0gImNsdWJzL2NsdWJkYXRhX3JzaWVuYV92b2wuUkRhdGEiKQ0KYGBgDQoNCg0KDQotLS0NCg0K


Copyright © 2021 Rob Franken