We will use the RSiena objects of clubs to describe the kudos networks, constant covariates and behavioral variables.


Getting started

Clean the working environment and load in the club data.

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

# load the RSiena objects
load("clubdata_rsiena_freq.RData")
load("clubdata_rsiena_vol.RData")

# and the list containing club data-setes
load("clubdata.RData")


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.
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"])
}

necessary packages

We install and load the packages we need later on:

  • RSiena: RSiena models, some descriptives on network level
  • igraph: Descriptives (dyad/triad census, degrees)
  • tidyverse
  • tidyr: for tidy data
  • moments: for calculating statistics (e.g., kurtosis, standard error)
  • dplyr: for data manipulation
  • ggplot2: for data visualization
  • forcats: for handling categorical variabl
  • knitr: for generating tables
  • kableExtra: for manipulating tables
  • network: for network analysis
  • sna: for network analysis
packages = c("RSiena", "igraph", "tidyverse", "tidyr", "moments", "dplyr", "ggplot2", "forcats", "knitr", "kableExtra", "network", "sna")

fpackage.check(packages)

additional functions

Now define some additional functions we use later on to describe our data (see www.jochemtolsma.nl).

  • fdensity: calculate density (exclude NA and structural zeros)
  • fdensityintra: calculate density within group (exclude NA and structural zeros)
  • fdensityinter: calculate density between groups (exclude NA and structural zeros)
  • fhomomat: based on ego/alter characteristics, construct dyad characteristic whether or not ego/alter are same
  • fndyads: calculate all valid dyads (no NA or structural zeros)
  • fscolnet: calculate Coleman’s segregation index on the network-level
  • fMoran.i: calculate Moran’s I spatial autocorrelation statistic (see here)
# density: observed relations divided by possible relations
fdensity <- function(x) {
    # x is your nomination network make sure diagonal cells are NA
    diag(x) <- NA
    # take care of RSiena structural zeros, set as missing.
    x[x == 10] <- NA
    sum(x == 1, na.rm = T)/(sum(x == 1 | x == 0, na.rm = T))
}

# calculate intragroup density
fdensityintra <- function(x, A) {
    # A is matrix indicating whether nodes in dyad have same node attributes
    diag(x) <- NA
    x[x == 10] <- NA
    diag(A) <- NA
    sum(x == 1 & A == 1, na.rm = T)/(sum((x == 1 | x == 0) & A == 1, na.rm = T))
}

# calculate intragroup density
fdensityinter <- function(x, A) {
    # A is matrix indicating whether nodes in dyad have same node attributes
    diag(x) <- NA
    x[x == 10] <- NA
    diag(A) <- NA
    sum(x == 1 & A != 1, na.rm = T)/(sum((x == 1 | x == 0) & A != 1, na.rm = T))
}

# construct dyad characteristic whether nodes are similar/homogenous
fhomomat <- function(x) {
    # x is a vector of node-covariate
    xmat <- matrix(x, nrow = length(x), ncol = length(x))
    xmatt <- t(xmat)
    xhomo <- xmat == xmatt
    return(xhomo)
}

# a function to calculate all valid dyads.
fndyads <- function(x) {
    diag(x) <- NA
    x[x == 10] <- NA
    (sum((x == 1 | x == 0), na.rm = T))
}

# a function to calculate all valid intragroupdyads.
fndyads2 <- function(x, A) {
    diag(x) <- NA
    x[x == 10] <- NA
    diag(A) <- NA
    (sum((x == 1 | x == 0) & A == 1, na.rm = T))
}


fscolnet <- function(network, ccovar) {
    # Calculate coleman on network level:
    # https://reader.elsevier.com/reader/sd/pii/S0378873314000239?token=A42F99FF6E2B750436DD2CB0DB7B1F41BDEC16052A45683C02644DAF88215A3379636B2AA197B65941D6373E9E2EE413
    
    fhomomat <- function(x) {
        xmat <- matrix(x, nrow = length(x), ncol = length(x))
        xmatt <- t(xmat)
        xhomo <- xmat == xmatt
        return(xhomo)
    }
    
    fsumintra <- function(x, A) {
        # A is matrix indicating whether nodes constituting dyad have same characteristics
        diag(x) <- NA
        x[x == 10] <- NA
        diag(A) <- NA
        sum(x == 1 & A == 1, na.rm = T)
    }
    
    # expecation w*=sum_g sum_i (ni((ng-1)/(N-1)))
    network[network == 10] <- NA
    ni <- rowSums(network, na.rm = T)
    ng <- NA
    for (i in 1:length(ccovar)) {
        ng[i] <- table(ccovar)[rownames(table(ccovar)) == ccovar[i]]
    }
    N <- length(ccovar)
    wexp <- sum(ni * ((ng - 1)/(N - 1)), na.rm = T)
    
    # wgg1 how many intragroup ties
    w <- fsumintra(network, fhomomat(ccovar))
    
    Scol_net <- ifelse(w >= wexp, (w - wexp)/(sum(ni, na.rm = T) - wexp), (w - wexp)/wexp)
    return(Scol_net)
}

fMoran.I <- function(x, weight, scaled = FALSE, na.rm = FALSE, alternative = "two.sided", rowstandardize = TRUE) {
    if (rowstandardize) {
        if (dim(weight)[1] != dim(weight)[2]) 
            stop("'weight' must be a square matrix")
        n <- length(x)
        if (dim(weight)[1] != n) 
            stop("'weight' must have as many rows as observations in 'x'")
        ei <- -1/(n - 1)
        nas <- is.na(x)
        if (any(nas)) {
            if (na.rm) {
                x <- x[!nas]
                n <- length(x)
                weight <- weight[!nas, !nas]
            } else {
                warning("'x' has missing values: maybe you wanted to set na.rm = TRUE?")
                return(list(observed = NA, expected = ei, sd = NA, p.value = NA))
            }
        }
        ROWSUM <- rowSums(weight)
        ROWSUM[ROWSUM == 0] <- 1
        weight <- weight/ROWSUM
        s <- sum(weight)
        m <- mean(x)
        y <- x - m
        cv <- sum(weight * y %o% y)
        v <- sum(y^2)
        obs <- (n/s) * (cv/v)
        if (scaled) {
            i.max <- (n/s) * (sd(rowSums(weight) * y)/sqrt(v/(n - 1)))
            obs <- obs/i.max
        }
        S1 <- 0.5 * sum((weight + t(weight))^2)
        S2 <- sum((apply(weight, 1, sum) + apply(weight, 2, sum))^2)
        s.sq <- s^2
        k <- (sum(y^4)/n)/(v/n)^2
        sdi <- sqrt((n * ((n^2 - 3 * n + 3) * S1 - n * S2 + 3 * s.sq) - k * (n * (n - 1) * S1 - 2 * n * 
            S2 + 6 * s.sq))/((n - 1) * (n - 2) * (n - 3) * s.sq) - 1/((n - 1)^2))
        alternative <- match.arg(alternative, c("two.sided", "less", "greater"))
        pv <- pnorm(obs, mean = ei, sd = sdi)
        if (alternative == "two.sided") 
            pv <- if (obs <= ei) 
                2 * pv else 2 * (1 - pv)
        if (alternative == "greater") 
            pv <- 1 - pv
        list(observed = obs, expected = ei, sd = sdi, p.value = pv)
    } else {
        if (dim(weight)[1] != dim(weight)[2]) 
            stop("'weight' must be a square matrix")
        n <- length(x)
        if (dim(weight)[1] != n) 
            stop("'weight' must have as many rows as observations in 'x'")
        ei <- -1/(n - 1)
        nas <- is.na(x)
        if (any(nas)) {
            if (na.rm) {
                x <- x[!nas]
                n <- length(x)
                weight <- weight[!nas, !nas]
            } else {
                warning("'x' has missing values: maybe you wanted to set na.rm = TRUE?")
                return(list(observed = NA, expected = ei, sd = NA, p.value = NA))
            }
        }
        # ROWSUM <- rowSums(weight) ROWSUM[ROWSUM == 0] <- 1 weight <- weight/ROWSUM
        s <- sum(weight)
        m <- mean(x)
        y <- x - m
        cv <- sum(weight * y %o% y)
        v <- sum(y^2)
        obs <- (n/s) * (cv/v)
        if (scaled) {
            i.max <- (n/s) * (sd(rowSums(weight) * y)/sqrt(v/(n - 1)))
            obs <- obs/i.max
        }
        S1 <- 0.5 * sum((weight + t(weight))^2)
        S2 <- sum((apply(weight, 1, sum) + apply(weight, 2, sum))^2)
        s.sq <- s^2
        k <- (sum(y^4)/n)/(v/n)^2
        sdi <- sqrt((n * ((n^2 - 3 * n + 3) * S1 - n * S2 + 3 * s.sq) - k * (n * (n - 1) * S1 - 2 * n * 
            S2 + 6 * s.sq))/((n - 1) * (n - 2) * (n - 3) * s.sq) - 1/((n - 1)^2))
        alternative <- match.arg(alternative, c("two.sided", "less", "greater"))
        pv <- pnorm(obs, mean = ei, sd = sdi)
        if (alternative == "two.sided") 
            pv <- if (obs <= ei) 
                2 * pv else 2 * (1 - pv)
        if (alternative == "greater") 
            pv <- 1 - pv
        list(observed = obs, expected = ei, sd = sdi, p.value = pv)
    }
    
    
}

We cover the following:

  • club characteristics
  • network structure in Kudos networks
  • gender composition / segregation
  • behavior: activity level (frequency and duration) and correlation
  • spatial network autocorrelation: behavioral similarity in networks


Club characteristics

Some club characteristics. We show the size of the network (the number of actors) for each club and the number of active members currently (18-1-2021) on Strava, by adding to www.strava.com/clubs/… the original club id.

# attrieve from the clubdata the number of actors in each network
netsize <- c( 
  length(clubdata_rsiena_freq[[1]]$nodeSets$Actors), 
  length(clubdata_rsiena_freq[[2]]$nodeSets$Actors), 
  length(clubdata_rsiena_freq[[3]]$nodeSets$Actors), 
  length(clubdata_rsiena_freq[[4]]$nodeSets$Actors), 
  length(clubdata_rsiena_freq[[5]]$nodeSets$Actors)) 

clubsize <- c(66, 127, 373, 15, 169) # find the number of members currently on Strava

df <- data.frame( netsize = netsize, clubsize = clubsize )

print(df)
#>   netsize clubsize
#> 1      27       66
#> 2      58      127
#> 3     159      373
#> 4       9       15
#> 5      76      169



Kudos network

Now let’s describe the Kudos network.

1. Node-level

Starting, again, with indegrees and outdegrees: who receives and gives Kudos? We take from the RSiena object the Kudos network for each club, subset the first wave, and turn it into an igraph object.

Club 1

df <- clubdata_rsiena_freq[[1]] # grab club 
knet <- df$depvars$kudonet # take Kudo network
knet1 <- knet[,,1] # take wave 1 only for now

# make an 'igraph object'
G1 <- igraph::graph_from_adjacency_matrix(knet1, mode = "directed", weighted = NULL, diag = TRUE, add.colnames = NA, add.rownames = NA)

# find in- and outdegree for each node
hist(igraph::degree(G1, mode="out"), xlab="outdegree", main="histogram of Kudo outdegree")

hist(igraph::degree(G1, mode="in"), xlab="indegree", main="histogram of Kudo indegree")

Club 2

df <- clubdata_rsiena_freq[[2]] # grab club t
knet <- df$depvars$kudonet # take Kudo network
knet1 <- knet[,,1] # take wave 1 only for now

# make an 'igraph object'
G1 <- igraph::graph_from_adjacency_matrix(knet1, mode = "directed", weighted = NULL, diag = TRUE, add.colnames = NA, add.rownames = NA)

# find in- and outdegree for each node
hist(igraph::degree(G1, mode="out"), xlab="outdegree", main="histogram of Kudo outdegree")

hist(igraph::degree(G1, mode="in"), xlab="indegree", main="histogram of Kudo indegree")

Club 3

df <- clubdata_rsiena_freq[[3]] # grab club 
knet <- df$depvars$kudonet # take Kudo network
knet1 <- knet[,,1] # take wave 1 only for now

# make an 'igraph object'
G1 <- igraph::graph_from_adjacency_matrix(knet1, mode = "directed", weighted = NULL, diag = TRUE, add.colnames = NA, add.rownames = NA)

# find in- and outdegree for each node
hist(igraph::degree(G1, mode="out"), xlab="outdegree", main="histogram of Kudo outdegree")

hist(igraph::degree(G1, mode="in"), xlab="indegree", main="histogram of Kudo indegree")

Club 4

df <- clubdata_rsiena_freq[[4]] # grab club 
knet <- df$depvars$kudonet # take Kudo network
knet1 <- knet[,,1] # take wave 1 only for now

# make an 'igraph object'
G1 <- igraph::graph_from_adjacency_matrix(knet1, mode = "directed", weighted = NULL, diag = TRUE, add.colnames = NA, add.rownames = NA)

# find in- and outdegree for each node
hist(igraph::degree(G1, mode="out"), xlab="outdegree", main="histogram of Kudo outdegree")

hist(igraph::degree(G1, mode="in"), xlab="indegree", main="histogram of Kudo indegree")

Club 5

df <- clubdata_rsiena_freq[[5]] # grab club 
knet <- df$depvars$kudonet # take Kudo network
knet1 <- knet[,,1] # take wave 1 only for now

# make an 'igraph object'
G1 <- igraph::graph_from_adjacency_matrix(knet1, mode = "directed", weighted = NULL, diag = TRUE, add.colnames = NA, add.rownames = NA)

# find in- and outdegree for each node
hist(igraph::degree(G1, mode="out"), xlab="outdegree", main="histogram of Kudo outdegree")

hist(igraph::degree(G1, mode="in"), xlab="indegree", main="histogram of Kudo indegree")

We can observe a Pareto-like-pattern: some give/receive most of the Kudos given, while most give/receive few.


2. Dyad-level

At the dyad-level: let’s see to what extent Kudos tend to be reciprocated between actors.

Club 1

# make igraph object for the club, at wave 1
df <- clubdata_rsiena_freq[[1]] # grab club 
knet <- df$depvars$kudonet # take Kudo network
knet1 <- knet[,,1] # take wave 1 only for now

# make an 'igraph object'
G1 <- igraph::graph_from_adjacency_matrix(knet1, mode = "directed", weighted = NULL, diag = TRUE, add.colnames = NA, add.rownames = NA)

# classify dyads
dyadcount <- igraph::dyad.census(G1)

# add the total number of dyads to the graph
dyadcount$total <- (vcount(G1)*(vcount(G1)-1))/2
dyadcount
#> $mut
#> [1] 30
#> 
#> $asym
#> [1] 5
#> 
#> $null
#> [1] 316
#> 
#> $total
#> [1] 351
# compare values with a random graph of the same size with the same density
dens <- igraph::graph.density(G1)
size <- igraph::vcount(G1)
trial <- 1000
recip <- rep(NA, trial)

for ( i in 1:trial ){
  random_graph <- igraph::erdos.renyi.game(n = size, p.or.m = dens, directed = TRUE)
  recip[i] <- igraph::dyad.census(random_graph)$mut
}

{hist(recip, main="number of reciprocated Kudos in random graph", xlab="", )
abline(v=dyadcount$mut, col="red", lwd=3)}

Club 2

# make igraph object for the club, at wave 1
df <- clubdata_rsiena_freq[[2]] # grab club 
knet <- df$depvars$kudonet # take Kudo network
knet1 <- knet[,,1] # take wave 1 only for now

# make an 'igraph object'
G1 <- igraph::graph_from_adjacency_matrix(knet1, mode = "directed", weighted = NULL, diag = TRUE, add.colnames = NA, add.rownames = NA)

# classify dyads
dyadcount <- igraph::dyad.census(G1)

# add the total number of dyads to the graph
dyadcount$total <- (vcount(G1)*(vcount(G1)-1))/2
dyadcount
#> $mut
#> [1] 231
#> 
#> $asym
#> [1] 99
#> 
#> $null
#> [1] 1323
#> 
#> $total
#> [1] 1653
# compare values with a random graph of the same size with the same density
dens <- igraph::graph.density(G1)
size <- igraph::vcount(G1)
trial <- 1000
recip <- rep(NA, trial)

for ( i in 1:trial ){
  random_graph <- igraph::erdos.renyi.game(n = size, p.or.m = dens, directed = TRUE)
  recip[i] <- igraph::dyad.census(random_graph)$mut
}

{hist(recip, main="number of reciprocated Kudos in random graph", xlab="", )
abline(v=dyadcount$mut, col="red", lwd=3)}

Club 3

# make igraph object for the club, at wave 1
df <- clubdata_rsiena_freq[[3]] # grab club 
knet <- df$depvars$kudonet # take Kudo network
knet1 <- knet[,,1] # take wave 1 only for now

# make an 'igraph object'
G1 <- igraph::graph_from_adjacency_matrix(knet1, mode = "directed", weighted = NULL, diag = TRUE, add.colnames = NA, add.rownames = NA)

# classify dyads
dyadcount <- igraph::dyad.census(G1)

# add the total number of dyads to the graph
dyadcount$total <- (vcount(G1)*(vcount(G1)-1))/2
dyadcount
#> $mut
#> [1] 91
#> 
#> $asym
#> [1] 104
#> 
#> $null
#> [1] 12366
#> 
#> $total
#> [1] 12561
# compare values with a random graph of the same size with the same density
dens <- igraph::graph.density(G1)
size <- igraph::vcount(G1)
trial <- 1000
recip <- rep(NA, trial)

for ( i in 1:trial ){
  random_graph <- igraph::erdos.renyi.game(n = size, p.or.m = dens, directed = TRUE)
  recip[i] <- igraph::dyad.census(random_graph)$mut
}

{hist(recip, main="number of reciprocated Kudos in random graph", xlab="", )
abline(v=dyadcount$mut, col="red", lwd=3)}

Club 4

# make igraph object for the club, at wave 1
df <- clubdata_rsiena_freq[[4]] # grab club 
knet <- df$depvars$kudonet # take Kudo network
knet1 <- knet[,,1] # take wave 1 only for now

# make an 'igraph object'
G1 <- igraph::graph_from_adjacency_matrix(knet1, mode = "directed", weighted = NULL, diag = TRUE, add.colnames = NA, add.rownames = NA)

# classify dyads
dyadcount <- igraph::dyad.census(G1)

# add the total number of dyads to the graph
dyadcount$total <- (vcount(G1)*(vcount(G1)-1))/2
dyadcount
#> $mut
#> [1] 7
#> 
#> $asym
#> [1] 2
#> 
#> $null
#> [1] 27
#> 
#> $total
#> [1] 36
# compare values with a random graph of the same size with the same density
dens <- igraph::graph.density(G1)
size <- igraph::vcount(G1)
trial <- 1000
recip <- rep(NA, trial)

for ( i in 1:trial ){
  random_graph <- igraph::erdos.renyi.game(n = size, p.or.m = dens, directed = TRUE)
  recip[i] <- igraph::dyad.census(random_graph)$mut
}

{hist(recip, main="number of reciprocated Kudos in random graph", xlab="", )
abline(v=dyadcount$mut, col="red", lwd=3)}

Club 5

# make igraph object for the club, at wave 1
df <- clubdata_rsiena_freq[[5]] # grab club 
knet <- df$depvars$kudonet # take Kudo network
knet1 <- knet[,,1] # take wave 1 only for now

# make an 'igraph object'
G1 <- igraph::graph_from_adjacency_matrix(knet1, mode = "directed", weighted = NULL, diag = TRUE, add.colnames = NA, add.rownames = NA)

# classify dyads
dyadcount <- igraph::dyad.census(G1)

# add the total number of dyads to the graph
dyadcount$total <- (vcount(G1)*(vcount(G1)-1))/2
dyadcount
#> $mut
#> [1] 195
#> 
#> $asym
#> [1] 119
#> 
#> $null
#> [1] 2536
#> 
#> $total
#> [1] 2850
# compare values with a random graph of the same size with the same density
dens <- igraph::graph.density(G1)
size <- igraph::vcount(G1)
trial <- 1000
recip <- rep(NA, trial)

for ( i in 1:trial ){
  random_graph <- igraph::erdos.renyi.game(n = size, p.or.m = dens, directed = TRUE)
  recip[i] <- igraph::dyad.census(random_graph)$mut
}

{hist(recip, main="number of reciprocated Kudos in random graph", xlab="", )
abline(v=dyadcount$mut, col="red", lwd=3)}

Conclusion: Kudos tend to be reciprocated!



Gender composition

Let’s investigate the gender composition of the club. We must retrieve gender from the object (note that we use the clubdata object, not the RSiena object). Then we make a categorical gender variable and plot it.

Club 1

df <- clubdata[[1]] # grab club 

# retrieve node-attribute gender from object
male <- df$male
female <- df$female
other <- df$other

# as factor
gender <- NA
gender <- ifelse(male == 1, "Male", gender)
gender <- ifelse(female == 1, "Female", gender)
gender <- ifelse(other == 1, "Other", gender)
gender <- ifelse(is.na(gender), "Missing", gender) # missing category

# make dataframe
df <- data.frame(
  gender = as.factor(c("Male", "Female", "Other", "Missing")),
  n = c(length(gender[gender == "Male"]), length(gender[gender == "Female"]), length(gender[gender == "Other"]), length(gender[gender == "Missing"])),
  freq = c(round((length(gender[gender=="Male"])/length(gender) *100), digits=1), round((length(gender[gender=="Female"])/length(gender) *100), digits=1), round((length(gender[gender=="Other"])/length(gender)*100), digits=1), round((length(gender[gender=="Missing"])/length(gender)*100), digits=1))
)

# plot
df %>%
  mutate(gender = fct_reorder(gender, -n)) %>%
           ggplot(aes(gender, n, fill=gender)) + 
           geom_bar(stat="identity", width=0.8) +
           geom_text(aes(label=paste0(freq,"%")), vjust=1.5, colour="white")

Club 2

df <- clubdata[[2]] # grab club 

# retrieve node-attribute gender from object
male <- df$male
female <- df$female
other <- df$other

# as factor
gender <- NA
gender <- ifelse(male == 1, "Male", gender)
gender <- ifelse(female == 1, "Female", gender)
gender <- ifelse(other == 1, "Other", gender)
gender <- ifelse(is.na(gender), "Missing", gender) # missing category

# make dataframe
df <- data.frame(
  gender = as.factor(c("Male", "Female", "Other", "Missing")),
  n = c(length(gender[gender == "Male"]), length(gender[gender == "Female"]), length(gender[gender == "Other"]), length(gender[gender == "Missing"])),
  freq = c(round((length(gender[gender=="Male"])/length(gender) *100), digits=1), round((length(gender[gender=="Female"])/length(gender) *100), digits=1), round((length(gender[gender=="Other"])/length(gender)*100), digits=1), round((length(gender[gender=="Missing"])/length(gender)*100), digits=1))
)

# plot
df %>%
  mutate(gender = fct_reorder(gender, -n)) %>%
           ggplot(aes(gender, n, fill=gender)) + 
           geom_bar(stat="identity", width=0.8) +
           geom_text(aes(label=paste0(freq,"%")), vjust=1.5, colour="white")

Club 3

df <- clubdata[[3]] # grab club 

# retrieve node-attribute gender from object
male <- df$male
female <- df$female
other <- df$other

# as factor
gender <- NA
gender <- ifelse(male == 1, "Male", gender)
gender <- ifelse(female == 1, "Female", gender)
gender <- ifelse(other == 1, "Other", gender)
gender <- ifelse(is.na(gender), "Missing", gender) # missing category

# make dataframe
df <- data.frame(
  gender = as.factor(c("Male", "Female", "Other", "Missing")),
  n = c(length(gender[gender == "Male"]), length(gender[gender == "Female"]), length(gender[gender == "Other"]), length(gender[gender == "Missing"])),
  freq = c(round((length(gender[gender=="Male"])/length(gender) *100), digits=1), round((length(gender[gender=="Female"])/length(gender) *100), digits=1), round((length(gender[gender=="Other"])/length(gender)*100), digits=1), round((length(gender[gender=="Missing"])/length(gender)*100), digits=1))
)

# plot
df %>%
  mutate(gender = fct_reorder(gender, -n)) %>%
           ggplot(aes(gender, n, fill=gender)) + 
           geom_bar(stat="identity", width=0.8) +
           geom_text(aes(label=paste0(freq,"%")), vjust=1.5, colour="white")

Club 4

df <- clubdata[[4]] # grab club 

# retrieve node-attribute gender from object
male <- df$male
female <- df$female
other <- df$other

# as factor
gender <- NA
gender <- ifelse(male == 1, "Male", gender)
gender <- ifelse(female == 1, "Female", gender)
gender <- ifelse(other == 1, "Other", gender)
gender <- ifelse(is.na(gender), "Missing", gender) # missing category

# make dataframe
df <- data.frame(
  gender = as.factor(c("Male", "Female", "Other", "Missing")),
  n = c(length(gender[gender == "Male"]), length(gender[gender == "Female"]), length(gender[gender == "Other"]), length(gender[gender == "Missing"])),
  freq = c(round((length(gender[gender=="Male"])/length(gender) *100), digits=1), round((length(gender[gender=="Female"])/length(gender) *100), digits=1), round((length(gender[gender=="Other"])/length(gender)*100), digits=1), round((length(gender[gender=="Missing"])/length(gender)*100), digits=1))
)

# plot
df %>%
  mutate(gender = fct_reorder(gender, -n)) %>%
           ggplot(aes(gender, n, fill=gender)) + 
           geom_bar(stat="identity", width=0.8) +
           geom_text(aes(label=paste0(freq,"%")), vjust=1.5, colour="white")

Club 5

df <- clubdata[[5]] # grab club 

# retrieve node-attribute gender from object
male <- df$male
female <- df$female
other <- df$other

# as factor
gender <- NA
gender <- ifelse(male == 1, "Male", gender)
gender <- ifelse(female == 1, "Female", gender)
gender <- ifelse(other == 1, "Other", gender)
gender <- ifelse(is.na(gender), "Missing", gender) # missing category

# make dataframe
df <- data.frame(
  gender = as.factor(c("Male", "Female", "Other", "Missing")),
  n = c(length(gender[gender == "Male"]), length(gender[gender == "Female"]), length(gender[gender == "Other"]), length(gender[gender == "Missing"])),
  freq = c(round((length(gender[gender=="Male"])/length(gender) *100), digits=1), round((length(gender[gender=="Female"])/length(gender) *100), digits=1), round((length(gender[gender=="Other"])/length(gender)*100), digits=1), round((length(gender[gender=="Missing"])/length(gender)*100), digits=1))
)

# plot
df %>%
  mutate(gender = fct_reorder(gender, -n)) %>%
           ggplot(aes(gender, n, fill=gender)) + 
           geom_bar(stat="identity", width=0.8) +
           geom_text(aes(label=paste0(freq,"%")), vjust=1.5, colour="white")

We can see that in all clubs men are the majority.



Gender segregation

Let’s now investigate segregation along gender in the kudos network.

Let’s start with describing the total density and intra- (same gender) and intergroup (different gender) densities. We also calculate the Coleman Homophily index for gender, which reflects gender segregation while taking into account the relative group size of gender categories.

Club 1

df <- clubdata_rsiena_freq[[1]] # grab club 
df2 <- clubdata[[1]]

knet <- df$depvars$kudonet # take Kudo network
knet1 <- knet[,,1] # take wave 1 only for now

# for some reason constructing the dyad-similarity matrix for gender with the rsiena object did not work, so we use the clubdata.RData.
male <- df2$male
female <- df2$female
other <- df2$other

gender <- NA
gender <- ifelse(male == 1, "Male", gender)
gender <- ifelse(female == 1, "Female", gender)
gender <- ifelse(other == 1, "Other", gender)
gender <- ifelse(is.na(gender), "Missing", gender) # missing category

# construct dyad similarity matrix
gender_m <- fhomomat(gender)


# make object to store results
desmat <- matrix(NA, nrow=4, ncol=1)

# use functions
desmat[1, 1] <- fdensity(knet1)
desmat[2, 1] <- fdensityintra(knet1, gender_m)
desmat[3, 1] <- fdensityinter(knet1, gender_m)
desmat[4, 1] <- fscolnet(knet1, gender)

colnames(desmat) <- c("Kudos network")
rownames(desmat) <- c("total density", "same gender density", "different gender density", "Coleman's homophily index")



knitr::kable(desmat, digits=3, "html", caption="Gender segregation in friendship and kudo network") %>% 
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover"))
Gender segregation in friendship and kudo network
Kudos network
total density 0.118
same gender density 0.151
different gender density 0.086
Coleman’s homophily index 0.232

Club 2

df <- clubdata_rsiena_freq[[2]] # grab club 
df2 <- clubdata[[2]]

knet <- df$depvars$kudonet # take Kudo network
knet1 <- knet[,,1] # take wave 1 only for now

# for some reason constructing the dyad-similarity matrix for gender with the rsiena object did not work, so we use the clubdata.RData.
male <- df2$male
female <- df2$female
other <- df2$other

gender <- NA
gender <- ifelse(male == 1, "Male", gender)
gender <- ifelse(female == 1, "Female", gender)
gender <- ifelse(other == 1, "Other", gender)
gender <- ifelse(is.na(gender), "Missing", gender) # missing category

# construct dyad similarity matrix
gender_m <- fhomomat(gender)


# make object to store results
desmat <- matrix(NA, nrow=4, ncol=1)

# use functions
desmat[1, 1] <- fdensity(knet1)
desmat[2, 1] <- fdensityintra(knet1, gender_m)
desmat[3, 1] <- fdensityinter(knet1, gender_m)
desmat[4, 1] <- fscolnet(knet1, gender)

colnames(desmat) <- c("Kudos network")
rownames(desmat) <- c("total density", "same gender density", "different gender density", "Coleman's homophily index")



knitr::kable(desmat, digits=3, "html", caption="Gender segregation in friendship and kudo network") %>% 
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover"))
Gender segregation in friendship and kudo network
Kudos network
total density 0.189
same gender density 0.250
different gender density 0.103
Coleman’s homophily index 0.343

Club 3

df <- clubdata_rsiena_freq[[3]] # grab club 
df2 <- clubdata[[3]]

knet <- df$depvars$kudonet # take Kudo network
knet1 <- knet[,,1] # take wave 1 only for now

# for some reason constructing the dyad-similarity matrix for gender with the rsiena object did not work, so we use the clubdata.RData.
male <- df2$male
female <- df2$female
other <- df2$other

gender <- NA
gender <- ifelse(male == 1, "Male", gender)
gender <- ifelse(female == 1, "Female", gender)
gender <- ifelse(other == 1, "Other", gender)
gender <- ifelse(is.na(gender), "Missing", gender) # missing category

# construct dyad similarity matrix
gender_m <- fhomomat(gender)


# make object to store results
desmat <- matrix(NA, nrow=4, ncol=1)

# use functions
desmat[1, 1] <- fdensity(knet1)
desmat[2, 1] <- fdensityintra(knet1, gender_m)
desmat[3, 1] <- fdensityinter(knet1, gender_m)
desmat[4, 1] <- fscolnet(knet1, gender)

colnames(desmat) <- c("Kudos network")
rownames(desmat) <- c("total density", "same gender density", "different gender density", "Coleman's homophily index")



knitr::kable(desmat, digits=3, "html", caption="Gender segregation in friendship and kudo network") %>% 
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover"))
Gender segregation in friendship and kudo network
Kudos network
total density 0.017
same gender density 0.017
different gender density 0.018
Coleman’s homophily index 0.040

Club 4

df <- clubdata_rsiena_freq[[4]] # grab club 
df2 <- clubdata[[4]]

knet <- df$depvars$kudonet # take Kudo network
knet1 <- knet[,,1] # take wave 1 only for now

# for some reason constructing the dyad-similarity matrix for gender with the rsiena object did not work, so we use the clubdata.RData.
male <- df2$male
female <- df2$female
other <- df2$other

gender <- NA
gender <- ifelse(male == 1, "Male", gender)
gender <- ifelse(female == 1, "Female", gender)
gender <- ifelse(other == 1, "Other", gender)
gender <- ifelse(is.na(gender), "Missing", gender) # missing category

# construct dyad similarity matrix
gender_m <- fhomomat(gender)


# make object to store results
desmat <- matrix(NA, nrow=4, ncol=1)

# use functions
desmat[1, 1] <- fdensity(knet1)
desmat[2, 1] <- fdensityintra(knet1, gender_m)
desmat[3, 1] <- fdensityinter(knet1, gender_m)
desmat[4, 1] <- fscolnet(knet1, gender)

colnames(desmat) <- c("Kudos network")
rownames(desmat) <- c("total density", "same gender density", "different gender density", "Coleman's homophily index")



knitr::kable(desmat, digits=3, "html", caption="Gender segregation in friendship and kudo network") %>% 
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover"))
Gender segregation in friendship and kudo network
Kudos network
total density 0.381
same gender density 0.286
different gender density 0.429
Coleman’s homophily index -0.347

Club 5

df <- clubdata_rsiena_freq[[5]] # grab club 
df2 <- clubdata[[5]]

knet <- df$depvars$kudonet # take Kudo network
knet1 <- knet[,,1] # take wave 1 only for now

# for some reason constructing the dyad-similarity matrix for gender with the rsiena object did not work, so we use the clubdata.RData.
male <- df2$male
female <- df2$female
other <- df2$other

gender <- NA
gender <- ifelse(male == 1, "Male", gender)
gender <- ifelse(female == 1, "Female", gender)
gender <- ifelse(other == 1, "Other", gender)
gender <- ifelse(is.na(gender), "Missing", gender) # missing category

# construct dyad similarity matrix
gender_m <- fhomomat(gender)


# make object to store results
desmat <- matrix(NA, nrow=4, ncol=1)

# use functions
desmat[1, 1] <- fdensity(knet1)
desmat[2, 1] <- fdensityintra(knet1, gender_m)
desmat[3, 1] <- fdensityinter(knet1, gender_m)
desmat[4, 1] <- fscolnet(knet1, gender)

colnames(desmat) <- c("Kudos network")
rownames(desmat) <- c("total density", "same gender density", "different gender density", "Coleman's homophily index")



knitr::kable(desmat, digits=3, "html", caption="Gender segregation in friendship and kudo network") %>% 
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover"))
Gender segregation in friendship and kudo network
Kudos network
total density 0.122
same gender density 0.117
different gender density 0.129
Coleman’s homophily index 0.025

Density in the kudos network is not much higher within-gender than between-genders, though this ratio differs per club (and in one clubs between-gender kudos are more common).

Coleman’s Homophily indices vary across clubs. In some clubs there is slight gender segregation (or same-gender preference) in kudos ties (values higher than 0, but rather close to 0), though in others members tend to give kudos to others outside their own gender group (values lower than 0).



Behavior

We plotted the development of the mean of running attributes (Figure 3 of the manuscript). The script to replicate this plot can be found here.


Correlation between frequency and volume

Let’s plot the relation between frequency in times per week and volume in hours per week. We also calculate Kendall’s tau-b, i.e. a non-parametric measure of correlation on ranks (cf. Khamis (2008)).

Club 1

df <- clubdata[[1]] # grab club 
df <- data.frame(x = as.matrix(df$time_run), y = as.matrix(df$freq_run))

c <- cor.test(df$x, df$y, method="kendall")

plot(df, type="b", 
     main = "Relation between weekly running variables: frequency and (half) hours",
     sub = "Note: running frequency capped with a maximum of 7, hours with a maximum of 7",
     xlab = "Hours per week", ylab = "sessions per week",
     col="blue", lwd=c(1,rep(6,10000))) + 
  text(x = 1.7, y = 5, round(c$estimate, digits=2)) +
  text(x = .7, y = 5, "Kendall's tau-b =")

#> integer(0)

Club 2

df <- clubdata[[2]] # grab club 
df <- data.frame(x = as.matrix(df$time_run), y = as.matrix(df$freq_run))
c <- cor.test(df$x,df$y,method="kendall")

plot(df, type="b", 
     main = "Relation between weekly running variables: frequency and hours",
     sub = "Note: running frequency capped with a maximum of 7, hours with a maximum of 7",
     xlab = "hours per week", ylab = "sessions per week",
     col="blue", lwd=c(1,rep(6,10000))) + 
  text(x = 1.7, y = 5, round(c$estimate, digits=2)) +
  text(x = .7, y = 5, "Kendall's tau-b =")

#> integer(0)

Club 3

df <- clubdata[[3]] # grab club 
df <- data.frame(x = as.matrix(df$time_run), y = as.matrix(df$freq_run))
c <- cor.test(df$x,df$y,method="kendall")

plot(df, type="b", 
     main = "Relation between weekly running variables: frequency and hours",
     sub = "Note: running frequency capped with a maximum of 7, hours with a maximum of 7",
     xlab = "hours per week", ylab = "sessions per week",
     col="blue", lwd=c(1,rep(6,10000))) + 
  text(x = 1.7, y = 5, round(c$estimate, digits=2)) +
  text(x = .7, y = 5, "Kendall's tau-b =")

#> integer(0)

Club 4

df <- clubdata[[4]] # grab club 
df <- data.frame(x = as.matrix(df$time_run), y = as.matrix(df$freq_run))
c <- cor.test(df$x,df$y,method="kendall")

plot(df, type="b", 
     main = "Relation between weekly running variables: frequency and hours",
     sub = "Note: running frequency capped with a maximum of 7, hours with a maximum of 7",
     xlab = "hours per week", ylab = "sessions per week",
     col="blue", lwd=c(1,rep(6,10000))) + 
  text(x = 1.5, y = 5, round(c$estimate, digits=2)) +
  text(x = .7, y = 5, "Kendall's tau-b =")

#> integer(0)

Club 5

df <- clubdata[[5]] # grab club 
df <- data.frame(x = as.matrix(df$time_run), y = as.matrix(df$freq_run))
c <- cor.test(df$x,df$y,method="kendall")

plot(df, type="b", 
     main = "Relation between weekly running variables: frequency and hours",
     sub = "Note: running frequency capped with a maximum of 7, hours with a maximum of 7",
     xlab = "hours per week", ylab = "sessions per week",
     col="blue", lwd=c(1,rep(6,10000))) + 
  text(x = 2, y = 7, round(c$estimate, digits=2)) +
  text(x = .7, y = 7, "Kendall's tau-b =")

#> integer(0)

Although frequency and duration are strongly related, there is some invariance.



Network autocorrelation

We have now covered the sport activity levels of our club-athletes, and the extent to which kudos-associations are segregated along gender. Last, we will explore if kudos-ties are also segregated along activity levels. Or in other words: do people with similar activity levels tend to socialize to a greater extent - by exchanging kudos - even when taking into account the opportunity structures for ‘interacting’ with (dis)similar others?

We use Moran’s I spatial autocorrelation measure for this, which is the correlation between the behavioral score of actor i and the (total/mean) behavioral score of alters j to whom i is connected directly. We also calculated Moran’s I by including the behavioral scores of the actors h to whom i is indirectly tied, and used the negative exponential function as described by Chen (2013) as a distance-decay function for assigning weights.

Club 1

df <- clubdata[[1]] # grab club 

# get behavioral data
# at time t=1
t=1
freq1 <- df$freq_run[,,t] # running frequencies wave 1
vol1 <- df$time_run[,,t] # running volume wave 1

# exclude NAs 
na <- which(is.na(freq1))
freq1 <- freq1[-na]
vol1 <- vol1[-na]

# get kudos net
knet1 <- df$kudo[,,t]
# exclude NAs
knet1 <- knet1[-na,-na]

# as network object
knet1 <- network::as.network(knet1)

# we include geodistances: shortest path lengths from i to j
geodistances <- sna::geodist(knet1, count.paths=T)
geodistances <- geodistances$gdist 

# set the distance 'to yourself' to 'Inf'
diag(geodistances) <- Inf

# first calculate Moran's i for alters at distance 1.
weights1 <- geodistances == 1

# and use the negative exponential distance-decay function
weights2 <- exp(-geodistances)

# calculate Moran's I
# for distance-1 and with distance decay, in the kudos network, for frequency and volume  respectively
# we do not row standardize!
Mfreq <- fMoran.I(freq1, scaled = FALSE, weight = weights1, na.rm = TRUE, rowstandardize = FALSE)
Mfreq_distd <- fMoran.I(freq1, scaled = FALSE, weight = weights2, na.rm = TRUE, rowstandardize = FALSE)

Mvol <- fMoran.I(vol1, scaled = FALSE, weight = weights1, na.rm = TRUE, rowstandardize = FALSE)
Mvol_distd <- fMoran.I(vol1, scaled = FALSE, weight = weights2, na.rm = TRUE, rowstandardize = FALSE)

# make object to store results
# 1. frequency
f_mat <- matrix(NA, nrow=2, ncol=4)

f_mat[1,1] <- Mfreq$observed
f_mat[1,2] <- Mfreq$expected
f_mat[1,3] <- Mfreq$sd
f_mat[1,4] <- Mfreq$p.value
f_mat[2,1] <- Mfreq_distd$observed
f_mat[2,2] <- Mfreq_distd$expected
f_mat[2,3] <- Mfreq_distd$sd
f_mat[2,4] <- Mfreq_distd$p.value

# 2. volume
v_mat <- matrix(NA, nrow=2, ncol=4)
v_mat[1,1] <- Mvol$observed
v_mat[1,2] <- Mvol$expected
v_mat[1,3] <- Mvol$sd
v_mat[1,4] <- Mvol$p.value
v_mat[2,1] <- Mvol_distd$observed
v_mat[2,2] <- Mvol_distd$expected
v_mat[2,3] <- Mvol_distd$sd
v_mat[2,4] <- Mvol_distd$p.value

colnames(f_mat) <- colnames(v_mat) <- c("observed", "expected", "sd", "p-value")
rownames(f_mat) <- rownames(v_mat) <- c("direct kudos ties", "direct and indirect kudos ties (distance-decay)")

knitr::kable(f_mat, digits=2, "html", caption="Moran's I statistic for spatial autocorrelation based on geodistances and weekly running frequency") %>% 
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover"))
Moran’s I statistic for spatial autocorrelation based on geodistances and weekly running frequency
observed expected sd p-value
direct kudos ties 0.01 -0.04 0.13 0.66
direct and indirect kudos ties (distance-decay) 0.00 -0.04 0.08 0.57
knitr::kable(v_mat, digits=2, "html", caption="Moran's I statistic for spatial autocorrelation based on geodistances and monthly running volume") %>% 
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover"))
Moran’s I statistic for spatial autocorrelation based on geodistances and monthly running volume
observed expected sd p-value
direct kudos ties -0.01 -0.04 0.13 0.80
direct and indirect kudos ties (distance-decay) -0.01 -0.04 0.08 0.71

Club 2

df <- clubdata[[2]] # grab club 

# get behavioral data
# at time t=1
t=1
freq1 <- df$freq_run[,,t] # running frequencies wave 1
vol1 <- df$time_run[,,t] # running volume wave 1

# exclude NAs 
na <- which(is.na(freq1))
freq1 <- freq1[-na]
vol1 <- vol1[-na]

# get kudos net
knet1 <- df$kudo[,,t]
# exclude NAs
knet1 <- knet1[-na,-na]

# as network object
knet1 <- network::as.network(knet1)

# we include geodistances: shortest path lengths from i to j
geodistances <- sna::geodist(knet1, count.paths=T)
geodistances <- geodistances$gdist 

# set the distance 'to yourself' to 'Inf'
diag(geodistances) <- Inf

# first calculate Moran's i for alters at distance 1.
weights1 <- geodistances == 1

# and use the negative exponential distance-decay function
weights2 <- exp(-geodistances)

# calculate Moran's I
# for distance-1 and with distance decay, in the kudos network, for frequency and volume  respectively
# we do not row standardize!
Mfreq <- fMoran.I(freq1, scaled = FALSE, weight = weights1, na.rm = TRUE, rowstandardize = FALSE)
Mfreq_distd <- fMoran.I(freq1, scaled = FALSE, weight = weights2, na.rm = TRUE, rowstandardize = FALSE)

Mvol <- fMoran.I(vol1, scaled = FALSE, weight = weights1, na.rm = TRUE, rowstandardize = FALSE)
Mvol_distd <- fMoran.I(vol1, scaled = FALSE, weight = weights2, na.rm = TRUE, rowstandardize = FALSE)

# make object to store results
# 1. frequency
f_mat <- matrix(NA, nrow=2, ncol=4)

f_mat[1,1] <- Mfreq$observed
f_mat[1,2] <- Mfreq$expected
f_mat[1,3] <- Mfreq$sd
f_mat[1,4] <- Mfreq$p.value
f_mat[2,1] <- Mfreq_distd$observed
f_mat[2,2] <- Mfreq_distd$expected
f_mat[2,3] <- Mfreq_distd$sd
f_mat[2,4] <- Mfreq_distd$p.value

# 2. volume
v_mat <- matrix(NA, nrow=2, ncol=4)
v_mat[1,1] <- Mvol$observed
v_mat[1,2] <- Mvol$expected
v_mat[1,3] <- Mvol$sd
v_mat[1,4] <- Mvol$p.value
v_mat[2,1] <- Mvol_distd$observed
v_mat[2,2] <- Mvol_distd$expected
v_mat[2,3] <- Mvol_distd$sd
v_mat[2,4] <- Mvol_distd$p.value

colnames(f_mat) <- colnames(v_mat) <- c("observed", "expected", "sd", "p-value")
rownames(f_mat) <- rownames(v_mat) <- c("direct kudos ties", "direct and indirect kudos ties (distance-decay)")

knitr::kable(f_mat, digits=2, "html", caption="Moran's I statistic for spatial autocorrelation based on geodistances and weekly running frequency") %>% 
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover"))
Moran’s I statistic for spatial autocorrelation based on geodistances and weekly running frequency
observed expected sd p-value
direct kudos ties 0.17 -0.02 0.04 0
direct and indirect kudos ties (distance-decay) 0.05 -0.02 0.01 0
knitr::kable(v_mat, digits=2, "html", caption="Moran's I statistic for spatial autocorrelation based on geodistances and monthly running volume") %>% 
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover"))
Moran’s I statistic for spatial autocorrelation based on geodistances and monthly running volume
observed expected sd p-value
direct kudos ties 0.21 -0.02 0.04 0
direct and indirect kudos ties (distance-decay) 0.06 -0.02 0.01 0

Club 3

df <- clubdata[[3]] # grab club 

# get behavioral data
# at time t=1
t=1
freq1 <- df$freq_run[,,t] # running frequencies wave 1
vol1 <- df$time_run[,,t] # running volume wave 1

# exclude NAs 
na <- which(is.na(freq1))
freq1 <- freq1[-na]
vol1 <- vol1[-na]

# get kudos net
knet1 <- df$kudo[,,t]
# exclude NAs
knet1 <- knet1[-na,-na]

# as network object
knet1 <- network::as.network(knet1)

# we include geodistances: shortest path lengths from i to j
geodistances <- sna::geodist(knet1, count.paths=T)
geodistances <- geodistances$gdist 

# set the distance 'to yourself' to 'Inf'
diag(geodistances) <- Inf

# first calculate Moran's i for alters at distance 1.
weights1 <- geodistances == 1

# and use the negative exponential distance-decay function
weights2 <- exp(-geodistances)

# calculate Moran's I
# for distance-1 and with distance decay, in the kudos network, for frequency and volume  respectively
# we do not row standardize!
Mfreq <- fMoran.I(freq1, scaled = FALSE, weight = weights1, na.rm = TRUE, rowstandardize = FALSE)
Mfreq_distd <- fMoran.I(freq1, scaled = FALSE, weight = weights2, na.rm = TRUE, rowstandardize = FALSE)

Mvol <- fMoran.I(vol1, scaled = FALSE, weight = weights1, na.rm = TRUE, rowstandardize = FALSE)
Mvol_distd <- fMoran.I(vol1, scaled = FALSE, weight = weights2, na.rm = TRUE, rowstandardize = FALSE)

# make object to store results
# 1. frequency
f_mat <- matrix(NA, nrow=2, ncol=4)

f_mat[1,1] <- Mfreq$observed
f_mat[1,2] <- Mfreq$expected
f_mat[1,3] <- Mfreq$sd
f_mat[1,4] <- Mfreq$p.value
f_mat[2,1] <- Mfreq_distd$observed
f_mat[2,2] <- Mfreq_distd$expected
f_mat[2,3] <- Mfreq_distd$sd
f_mat[2,4] <- Mfreq_distd$p.value

# 2. volume
v_mat <- matrix(NA, nrow=2, ncol=4)
v_mat[1,1] <- Mvol$observed
v_mat[1,2] <- Mvol$expected
v_mat[1,3] <- Mvol$sd
v_mat[1,4] <- Mvol$p.value
v_mat[2,1] <- Mvol_distd$observed
v_mat[2,2] <- Mvol_distd$expected
v_mat[2,3] <- Mvol_distd$sd
v_mat[2,4] <- Mvol_distd$p.value

colnames(f_mat) <- colnames(v_mat) <- c("observed", "expected", "sd", "p-value")
rownames(f_mat) <- rownames(v_mat) <- c("direct kudos ties", "direct and indirect kudos ties (distance-decay)")

knitr::kable(f_mat, digits=2, "html", caption="Moran's I statistic for spatial autocorrelation based on geodistances and weekly running frequency") %>% 
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover"))
Moran’s I statistic for spatial autocorrelation based on geodistances and weekly running frequency
observed expected sd p-value
direct kudos ties 0.03 -0.01 0.07 0.63
direct and indirect kudos ties (distance-decay) 0.02 -0.01 0.03 0.28
knitr::kable(v_mat, digits=2, "html", caption="Moran's I statistic for spatial autocorrelation based on geodistances and monthly running volume") %>% 
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover"))
Moran’s I statistic for spatial autocorrelation based on geodistances and monthly running volume
observed expected sd p-value
direct kudos ties 0.16 -0.01 0.07 0.02
direct and indirect kudos ties (distance-decay) 0.07 -0.01 0.03 0.01

Club 4

df <- clubdata[[4]] # grab club 

# get behavioral data
# at time t=1
t=1
freq1 <- df$freq_run[,,t] # running frequencies wave 1
vol1 <- df$time_run[,,t] # running volume wave 1

# exclude NAs 
na <- which(is.na(freq1))
freq1 <- freq1[-na]
vol1 <- vol1[-na]

# get kudos net
knet1 <- df$kudo[,,t]
# exclude NAs
knet1 <- knet1[-na,-na]

# as network object
knet1 <- network::as.network(knet1)

# we include geodistances: shortest path lengths from i to j
geodistances <- sna::geodist(knet1, count.paths=T)
geodistances <- geodistances$gdist 

# set the distance 'to yourself' to 'Inf'
diag(geodistances) <- Inf

# first calculate Moran's i for alters at distance 1.
weights1 <- geodistances == 1

# and use the negative exponential distance-decay function
weights2 <- exp(-geodistances)

# calculate Moran's I
# for distance-1 and with distance decay, in the kudos network, for frequency and volume  respectively
# we do not row standardize!
Mfreq <- fMoran.I(freq1, scaled = FALSE, weight = weights1, na.rm = TRUE, rowstandardize = FALSE)
Mfreq_distd <- fMoran.I(freq1, scaled = FALSE, weight = weights2, na.rm = TRUE, rowstandardize = FALSE)

Mvol <- fMoran.I(vol1, scaled = FALSE, weight = weights1, na.rm = TRUE, rowstandardize = FALSE)
Mvol_distd <- fMoran.I(vol1, scaled = FALSE, weight = weights2, na.rm = TRUE, rowstandardize = FALSE)

# make object to store results
# 1. frequency
f_mat <- matrix(NA, nrow=2, ncol=4)

f_mat[1,1] <- Mfreq$observed
f_mat[1,2] <- Mfreq$expected
f_mat[1,3] <- Mfreq$sd
f_mat[1,4] <- Mfreq$p.value
f_mat[2,1] <- Mfreq_distd$observed
f_mat[2,2] <- Mfreq_distd$expected
f_mat[2,3] <- Mfreq_distd$sd
f_mat[2,4] <- Mfreq_distd$p.value

# 2. volume
v_mat <- matrix(NA, nrow=2, ncol=4)
v_mat[1,1] <- Mvol$observed
v_mat[1,2] <- Mvol$expected
v_mat[1,3] <- Mvol$sd
v_mat[1,4] <- Mvol$p.value
v_mat[2,1] <- Mvol_distd$observed
v_mat[2,2] <- Mvol_distd$expected
v_mat[2,3] <- Mvol_distd$sd
v_mat[2,4] <- Mvol_distd$p.value

colnames(f_mat) <- colnames(v_mat) <- c("observed", "expected", "sd", "p-value")
rownames(f_mat) <- rownames(v_mat) <- c("direct kudos ties", "direct and indirect kudos ties (distance-decay)")

knitr::kable(f_mat, digits=2, "html", caption="Moran's I statistic for spatial autocorrelation based on geodistances and weekly running frequency") %>% 
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover"))
Moran’s I statistic for spatial autocorrelation based on geodistances and weekly running frequency
observed expected sd p-value
direct kudos ties -0.31 -0.17 0.26 0.57
direct and indirect kudos ties (distance-decay) -0.34 -0.17 0.25 0.49
knitr::kable(v_mat, digits=2, "html", caption="Moran's I statistic for spatial autocorrelation based on geodistances and monthly running volume") %>% 
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover"))
Moran’s I statistic for spatial autocorrelation based on geodistances and monthly running volume
observed expected sd p-value
direct kudos ties -0.28 -0.17 0.28 0.69
direct and indirect kudos ties (distance-decay) -0.30 -0.17 0.28 0.63

Club 5

df <- clubdata[[5]] # grab club 

# get behavioral data
# at time t=1
t=1
freq1 <- df$freq_run[,,t] # running frequencies wave 1
vol1 <- df$time_run[,,t] # running volume wave 1

# exclude NAs 
na <- which(is.na(freq1))
freq1 <- freq1[-na]
vol1 <- vol1[-na]

# get kudos net
knet1 <- df$kudo[,,t]
# exclude NAs
knet1 <- knet1[-na,-na]

# as network object
knet1 <- network::as.network(knet1)

# we include geodistances: shortest path lengths from i to j
geodistances <- sna::geodist(knet1, count.paths=T)
geodistances <- geodistances$gdist 

# set the distance 'to yourself' to 'Inf'
diag(geodistances) <- Inf

# first calculate Moran's i for alters at distance 1.
weights1 <- geodistances == 1

# and use the negative exponential distance-decay function
weights2 <- exp(-geodistances)

# calculate Moran's I
# for distance-1 and with distance decay, in the kudos network, for frequency and volume  respectively
# we do not row standardize!
Mfreq <- fMoran.I(freq1, scaled = FALSE, weight = weights1, na.rm = TRUE, rowstandardize = FALSE)
Mfreq_distd <- fMoran.I(freq1, scaled = FALSE, weight = weights2, na.rm = TRUE, rowstandardize = FALSE)

Mvol <- fMoran.I(vol1, scaled = FALSE, weight = weights1, na.rm = TRUE, rowstandardize = FALSE)
Mvol_distd <- fMoran.I(vol1, scaled = FALSE, weight = weights2, na.rm = TRUE, rowstandardize = FALSE)

# make object to store results
# 1. frequency
f_mat <- matrix(NA, nrow=2, ncol=4)

f_mat[1,1] <- Mfreq$observed
f_mat[1,2] <- Mfreq$expected
f_mat[1,3] <- Mfreq$sd
f_mat[1,4] <- Mfreq$p.value
f_mat[2,1] <- Mfreq_distd$observed
f_mat[2,2] <- Mfreq_distd$expected
f_mat[2,3] <- Mfreq_distd$sd
f_mat[2,4] <- Mfreq_distd$p.value

# 2. volume
v_mat <- matrix(NA, nrow=2, ncol=4)
v_mat[1,1] <- Mvol$observed
v_mat[1,2] <- Mvol$expected
v_mat[1,3] <- Mvol$sd
v_mat[1,4] <- Mvol$p.value
v_mat[2,1] <- Mvol_distd$observed
v_mat[2,2] <- Mvol_distd$expected
v_mat[2,3] <- Mvol_distd$sd
v_mat[2,4] <- Mvol_distd$p.value

colnames(f_mat) <- colnames(v_mat) <- c("observed", "expected", "sd", "p-value")
rownames(f_mat) <- rownames(v_mat) <- c("direct kudos ties", "direct and indirect kudos ties (distance-decay)")

knitr::kable(f_mat, digits=2, "html", caption="Moran's I statistic for spatial autocorrelation based on geodistances and weekly running frequency") %>% 
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover"))
Moran’s I statistic for spatial autocorrelation based on geodistances and weekly running frequency
observed expected sd p-value
direct kudos ties 0.21 -0.02 0.05 0
direct and indirect kudos ties (distance-decay) 0.09 -0.02 0.02 0
knitr::kable(v_mat, digits=2, "html", caption="Moran's I statistic for spatial autocorrelation based on geodistances and monthly running volume") %>% 
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover"))
Moran’s I statistic for spatial autocorrelation based on geodistances and monthly running volume
observed expected sd p-value
direct kudos ties 0.25 -0.02 0.05 0
direct and indirect kudos ties (distance-decay) 0.10 -0.02 0.02 0

Here, the Moran’s I statistic tests whether club members that are closer to one another (i.e., having a shorter geodesic/path length), are more a similar with respect to their behavior, under the null hypothesis that behavior is ‘randomly distributed’ among the club members.

We observe that, indeed, kudos-ties that are closer to one another are more alike, with respect to running. Autocorrelation was stronger without the distance-decay function, which suggests that especially close alters (with path length one) are similar.



References

Chen, Yanguang. 2013. “New Approaches for Calculating Moran’s Index of Spatial Autocorrelation.” PLOS ONE 8 (7): –. https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0068336.
Khamis, Harry. 2008. “Measures of Association: How to Choose?” Journal of Diagnostic Medical Sonography 24 (3): 155–62. doi:10.1177/8756479308317006.
LS0tDQp0aXRsZTogIkRlc2NyaXB0aXZlIHN0YXRpc3RpY3MiDQpkYXRlOiAiTGFzdCBjb21waWxlZCBvbiBgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVCLCAlWScpYCINCmJpYmxpb2dyYXBoeTogcmVmZXJlbmNlcy5iaWINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjc3M6IHR3ZWFrcy5jc3MNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQ0KICAgIHRvY19kZXB0aDogMQ0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KLS0tDQoNCg0KYGBge3IsIGdsb2JhbHNldHRpbmdzLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShrbml0cikNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCm9wdHNfY2h1bmskc2V0KHRpZHkub3B0cz1saXN0KHdpZHRoLmN1dG9mZj0xMDApLHRpZHk9VFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsY29tbWVudCA9ICIjPiIsIGNhY2hlPVRSVUUsIGNsYXNzLnNvdXJjZT1jKCJ0ZXN0IiksIGNsYXNzLm91dHB1dD1jKCJ0ZXN0MiIpKQ0Kb3B0aW9ucyh3aWR0aCA9IDEwMCkNCnJnbDo6c2V0dXBLbml0cigpDQoNCg0KDQpjb2xvcml6ZSA8LSBmdW5jdGlvbih4LCBjb2xvcikge3NwcmludGYoIjxzcGFuIHN0eWxlPSdjb2xvcjogJXM7Jz4lczwvc3Bhbj4iLCBjb2xvciwgeCkgfQ0KDQpgYGANCg0KYGBge3Iga2xpcHB5LCBlY2hvPUZBTFNFLCBpbmNsdWRlPVRSVUV9DQprbGlwcHk6OmtsaXBweShwb3NpdGlvbiA9IGMoJ3RvcCcsICdyaWdodCcpKQ0KI2tsaXBweTo6a2xpcHB5KGNvbG9yID0gJ2RhcmtyZWQnKQ0KI2tsaXBweTo6a2xpcHB5KHRvb2x0aXBfbWVzc2FnZSA9ICdDbGljayB0byBjb3B5JywgdG9vbHRpcF9zdWNjZXNzID0gJ0RvbmUnKQ0KYGBgDQoNCg0KDQotLS0NCg0KV2Ugd2lsbCB1c2UgdGhlIFJTaWVuYSBvYmplY3RzIG9mIGNsdWJzIHRvIGRlc2NyaWJlIHRoZSBrdWRvcyBuZXR3b3JrcywgY29uc3RhbnQgY292YXJpYXRlcyBhbmQgYmVoYXZpb3JhbCB2YXJpYWJsZXMuIA0KDQo8YnI+DQoNCiMgR2V0dGluZyBzdGFydGVkDQoNCkNsZWFuIHRoZSB3b3JraW5nIGVudmlyb25tZW50IGFuZCBsb2FkIGluIHRoZSBjbHViIGRhdGEuDQoNCmBgYHtyLCBhdHRyLm91dHB1dD0nc3R5bGU9Im1heC1oZWlnaHQ6IDIwMHB4OyInfQ0KIyBjbGVhbiB0aGUgd29ya2luZyBlbnZpcm9ubWVudCANCnJtIChsaXN0ID0gbHMoICkpDQoNCiMgbG9hZCB0aGUgUlNpZW5hIG9iamVjdHMNCmxvYWQoImNsdWJkYXRhX3JzaWVuYV9mcmVxLlJEYXRhIikNCmxvYWQoImNsdWJkYXRhX3JzaWVuYV92b2wuUkRhdGEiKQ0KDQojIGFuZCB0aGUgbGlzdCBjb250YWluaW5nIGNsdWIgZGF0YS1zZXRlcw0KbG9hZCgiY2x1YmRhdGEuUkRhdGEiKQ0KDQpgYGANCg0KPGJyPg0KDQojIyBnZW5lcmFsIGN1c3RvbSBmdW5jdGlvbnMNCg0KLSBgZnBhY2thZ2UuY2hlY2tgOiBDaGVjayBpZiBwYWNrYWdlcyBhcmUgaW5zdGFsbGVkIChhbmQgaW5zdGFsbCBpZiBub3QpIGluIFIgKFtzb3VyY2VdKGh0dHBzOi8vdmJhbGlnYS5naXRodWIuaW8vdmVyaWZ5LXRoYXQtci1wYWNrYWdlcy1hcmUtaW5zdGFsbGVkLWFuZC1sb2FkZWQvKSkNCi0gYGZsb2FkLlJgOiBmdW5jdGlvbiB0byBsb2FkIFItb2JqZWN0cyB1bmRlciBuZXcgbmFtZXMuDQoNCmBgYHtyLCByZXN1bHRzPSdoaWRlJ30NCg0KZnBhY2thZ2UuY2hlY2sgPC0gZnVuY3Rpb24ocGFja2FnZXMpIHsNCiAgICBsYXBwbHkocGFja2FnZXMsIEZVTiA9IGZ1bmN0aW9uKHgpIHsNCiAgICAgICAgaWYgKCFyZXF1aXJlKHgsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpIHsNCiAgICAgICAgICAgIGluc3RhbGwucGFja2FnZXMoeCwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiAgICAgICAgICAgIGxpYnJhcnkoeCwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQ0KICAgICAgICB9DQogICAgfSkNCn0NCg0KZmxvYWQuUiAgPC0gZnVuY3Rpb24oZmlsZU5hbWUpew0KICBsb2FkKGZpbGVOYW1lKQ0KICBnZXQobHMoKVtscygpICE9ICJmaWxlTmFtZSJdKQ0KfQ0KDQpgYGANCg0KDQojIyBuZWNlc3NhcnkgcGFja2FnZXMNCg0KV2UgaW5zdGFsbCBhbmQgbG9hZCB0aGUgcGFja2FnZXMgd2UgbmVlZCBsYXRlciBvbjoNCg0KLSBgUlNpZW5hYDogUlNpZW5hIG1vZGVscywgc29tZSBkZXNjcmlwdGl2ZXMgb24gbmV0d29yayBsZXZlbA0KLSBgaWdyYXBoYDogRGVzY3JpcHRpdmVzIChkeWFkL3RyaWFkIGNlbnN1cywgZGVncmVlcykNCi0gYHRpZHl2ZXJzZWANCi0gYHRpZHlyYDogZm9yIHRpZHkgZGF0YQ0KLSBgbW9tZW50c2A6IGZvciBjYWxjdWxhdGluZyBzdGF0aXN0aWNzIChlLmcuLCBrdXJ0b3Npcywgc3RhbmRhcmQgZXJyb3IpDQotIGBkcGx5cmA6IGZvciBkYXRhIG1hbmlwdWxhdGlvbg0KLSBgZ2dwbG90MmA6IGZvciBkYXRhIHZpc3VhbGl6YXRpb24NCi0gYGZvcmNhdHNgOiBmb3IgaGFuZGxpbmcgY2F0ZWdvcmljYWwgdmFyaWFibA0KLSBga25pdHJgOiBmb3IgZ2VuZXJhdGluZyB0YWJsZXMNCi0gYGthYmxlRXh0cmFgOiBmb3IgbWFuaXB1bGF0aW5nIHRhYmxlcw0KLSBgbmV0d29ya2A6IGZvciBuZXR3b3JrIGFuYWx5c2lzDQotIGBzbmFgOiBmb3IgbmV0d29yayBhbmFseXNpcw0KDQoNCg0KYGBge3IgcGFja2FnZXMsIHJlc3VsdHM9J2hpZGUnfQ0KDQpwYWNrYWdlcyA9IGMoIlJTaWVuYSIsICJpZ3JhcGgiLCAidGlkeXZlcnNlIiwgInRpZHlyIiwgIm1vbWVudHMiLCAiZHBseXIiLCAiZ2dwbG90MiIsICJmb3JjYXRzIiwgImtuaXRyIiwgImthYmxlRXh0cmEiLCAibmV0d29yayIsICJzbmEiKQ0KDQpmcGFja2FnZS5jaGVjayhwYWNrYWdlcykNCmBgYA0KDQoNCiMjIGFkZGl0aW9uYWwgZnVuY3Rpb25zIA0KDQpOb3cgZGVmaW5lIHNvbWUgYWRkaXRpb25hbCBmdW5jdGlvbnMgd2UgdXNlIGxhdGVyIG9uIHRvIGRlc2NyaWJlIG91ciBkYXRhIChzZWUgW3d3dy5qb2NoZW10b2xzbWEubmxdKGh0dHBzOi8vd3d3LmpvY2hlbXRvbHNtYS5ubC9jb3Vyc2VzL2NvbXBsZXRlLW5ldHdvcmtzL3NvY2lvNi8jZGVzY3JpcHRpdmUtc3RhdGlzdGljcykpLiAgDQoNCi0gYGZkZW5zaXR5YDogY2FsY3VsYXRlIGRlbnNpdHkgKGV4Y2x1ZGUgTkEgYW5kIHN0cnVjdHVyYWwgemVyb3MpICANCi0gYGZkZW5zaXR5aW50cmFgOiBjYWxjdWxhdGUgZGVuc2l0eSB3aXRoaW4gZ3JvdXAgKGV4Y2x1ZGUgTkEgYW5kIHN0cnVjdHVyYWwgemVyb3MpICANCi0gYGZkZW5zaXR5aW50ZXJgOiBjYWxjdWxhdGUgZGVuc2l0eSBiZXR3ZWVuIGdyb3VwcyAoZXhjbHVkZSBOQSBhbmQgc3RydWN0dXJhbCB6ZXJvcykgIA0KLSBgZmhvbW9tYXRgOiBiYXNlZCBvbiBlZ28vYWx0ZXIgY2hhcmFjdGVyaXN0aWNzLCBjb25zdHJ1Y3QgZHlhZCBjaGFyYWN0ZXJpc3RpYyB3aGV0aGVyIG9yIG5vdCBlZ28vYWx0ZXIgYXJlIHNhbWUgDQotIGBmbmR5YWRzYDogY2FsY3VsYXRlIGFsbCB2YWxpZCBkeWFkcyAobm8gTkEgb3Igc3RydWN0dXJhbCB6ZXJvcykgIA0KLSBgZnNjb2xuZXRgOiBjYWxjdWxhdGUgQ29sZW1hbidzIHNlZ3JlZ2F0aW9uIGluZGV4IG9uIHRoZSBuZXR3b3JrLWxldmVsICANCi0gYGZNb3Jhbi5pYDogY2FsY3VsYXRlIE1vcmFuJ3MgSSBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiBzdGF0aXN0aWMgKHNlZSBbaGVyZV0oaHR0cHM6Ly93d3cuam9jaGVtdG9sc21hLm5sL2NvdXJzZXMvY29tcGxldGUtbmV0d29ya3Mvc29jaW83LyNtb3JhbnMtYXV0b2NvcnJlbGF0aW9uLWZvci1vdXRnb2luZy10aWVzLXJzaWVuYS1idWlsZC1pbi1kYXRhc2V0KSkNCg0KDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICdmb2xkLWhpZGUnfQ0KIyBkZW5zaXR5OiBvYnNlcnZlZCByZWxhdGlvbnMgZGl2aWRlZCBieSBwb3NzaWJsZSByZWxhdGlvbnMNCmZkZW5zaXR5IDwtIGZ1bmN0aW9uKHgpIHsNCiAgICAjIHggaXMgeW91ciBub21pbmF0aW9uIG5ldHdvcmsgbWFrZSBzdXJlIGRpYWdvbmFsIGNlbGxzIGFyZSBOQQ0KICAgIGRpYWcoeCkgPC0gTkENCiAgICAjIHRha2UgY2FyZSBvZiBSU2llbmEgc3RydWN0dXJhbCB6ZXJvcywgc2V0IGFzIG1pc3NpbmcuDQogICAgeFt4ID09IDEwXSA8LSBOQQ0KICAgIHN1bSh4ID09IDEsIG5hLnJtID0gVCkvKHN1bSh4ID09IDEgfCB4ID09IDAsIG5hLnJtID0gVCkpDQp9DQoNCiMgY2FsY3VsYXRlIGludHJhZ3JvdXAgZGVuc2l0eQ0KZmRlbnNpdHlpbnRyYSA8LSBmdW5jdGlvbih4LCBBKSB7DQogICAgIyBBIGlzIG1hdHJpeCBpbmRpY2F0aW5nIHdoZXRoZXIgbm9kZXMgaW4gZHlhZCBoYXZlIHNhbWUgbm9kZSBhdHRyaWJ1dGVzDQogICAgZGlhZyh4KSA8LSBOQQ0KICAgIHhbeCA9PSAxMF0gPC0gTkENCiAgICBkaWFnKEEpIDwtIE5BDQogICAgc3VtKHggPT0gMSAmIEEgPT0gMSwgbmEucm0gPSBUKS8oc3VtKCh4ID09IDEgfCB4ID09IDApICYgQSA9PSAxLCBuYS5ybSA9IFQpKQ0KfQ0KDQojIGNhbGN1bGF0ZSBpbnRyYWdyb3VwIGRlbnNpdHkNCmZkZW5zaXR5aW50ZXIgPC0gZnVuY3Rpb24oeCwgQSkgew0KICAgICMgQSBpcyBtYXRyaXggaW5kaWNhdGluZyB3aGV0aGVyIG5vZGVzIGluIGR5YWQgaGF2ZSBzYW1lIG5vZGUgYXR0cmlidXRlcw0KICAgIGRpYWcoeCkgPC0gTkENCiAgICB4W3ggPT0gMTBdIDwtIE5BDQogICAgZGlhZyhBKSA8LSBOQQ0KICAgIHN1bSh4ID09IDEgJiBBICE9IDEsIG5hLnJtID0gVCkvKHN1bSgoeCA9PSAxIHwgeCA9PSAwKSAmIEEgIT0gMSwgbmEucm0gPSBUKSkNCn0NCg0KIyBjb25zdHJ1Y3QgZHlhZCBjaGFyYWN0ZXJpc3RpYyB3aGV0aGVyIG5vZGVzIGFyZSBzaW1pbGFyL2hvbW9nZW5vdXMNCmZob21vbWF0IDwtIGZ1bmN0aW9uKHgpIHsNCiAgICAjIHggaXMgYSB2ZWN0b3Igb2Ygbm9kZS1jb3ZhcmlhdGUNCiAgICB4bWF0IDwtIG1hdHJpeCh4LCBucm93ID0gbGVuZ3RoKHgpLCBuY29sID0gbGVuZ3RoKHgpKQ0KICAgIHhtYXR0IDwtIHQoeG1hdCkNCiAgICB4aG9tbyA8LSB4bWF0ID09IHhtYXR0DQogICAgcmV0dXJuKHhob21vKQ0KfQ0KDQojIGEgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIGFsbCB2YWxpZCBkeWFkcy4NCmZuZHlhZHMgPC0gZnVuY3Rpb24oeCkgew0KICAgIGRpYWcoeCkgPC0gTkENCiAgICB4W3ggPT0gMTBdIDwtIE5BDQogICAgKHN1bSgoeCA9PSAxIHwgeCA9PSAwKSwgbmEucm0gPSBUKSkNCn0NCg0KIyBhIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSBhbGwgdmFsaWQgaW50cmFncm91cGR5YWRzLg0KZm5keWFkczIgPC0gZnVuY3Rpb24oeCwgQSkgew0KICAgIGRpYWcoeCkgPC0gTkENCiAgICB4W3ggPT0gMTBdIDwtIE5BDQogICAgZGlhZyhBKSA8LSBOQQ0KICAgIChzdW0oKHggPT0gMSB8IHggPT0gMCkgJiBBID09IDEsIG5hLnJtID0gVCkpDQp9DQoNCg0KZnNjb2xuZXQgPC0gZnVuY3Rpb24obmV0d29yaywgY2NvdmFyKSB7DQogICAgIyBDYWxjdWxhdGUgY29sZW1hbiBvbiBuZXR3b3JrIGxldmVsOg0KICAgICMgaHR0cHM6Ly9yZWFkZXIuZWxzZXZpZXIuY29tL3JlYWRlci9zZC9waWkvUzAzNzg4NzMzMTQwMDAyMzk/dG9rZW49QTQyRjk5RkY2RTJCNzUwNDM2REQyQ0IwREI3QjFGNDFCREVDMTYwNTJBNDU2ODNDMDI2NDREQUY4ODIxNUEzMzc5NjM2QjJBQTE5N0I2NTk0MUQ2MzczRTlFMkVFNDEzDQogICAgDQogICAgZmhvbW9tYXQgPC0gZnVuY3Rpb24oeCkgew0KICAgICAgICB4bWF0IDwtIG1hdHJpeCh4LCBucm93ID0gbGVuZ3RoKHgpLCBuY29sID0gbGVuZ3RoKHgpKQ0KICAgICAgICB4bWF0dCA8LSB0KHhtYXQpDQogICAgICAgIHhob21vIDwtIHhtYXQgPT0geG1hdHQNCiAgICAgICAgcmV0dXJuKHhob21vKQ0KICAgIH0NCiAgICANCiAgICBmc3VtaW50cmEgPC0gZnVuY3Rpb24oeCwgQSkgew0KICAgICAgICAjIEEgaXMgbWF0cml4IGluZGljYXRpbmcgd2hldGhlciBub2RlcyBjb25zdGl0dXRpbmcgZHlhZCBoYXZlIHNhbWUgY2hhcmFjdGVyaXN0aWNzDQogICAgICAgIGRpYWcoeCkgPC0gTkENCiAgICAgICAgeFt4ID09IDEwXSA8LSBOQQ0KICAgICAgICBkaWFnKEEpIDwtIE5BDQogICAgICAgIHN1bSh4ID09IDEgJiBBID09IDEsIG5hLnJtID0gVCkNCiAgICB9DQogICAgDQogICAgIyBleHBlY2F0aW9uIHcqPXN1bV9nIHN1bV9pIChuaSgobmctMSkvKE4tMSkpKQ0KICAgIG5ldHdvcmtbbmV0d29yayA9PSAxMF0gPC0gTkENCiAgICBuaSA8LSByb3dTdW1zKG5ldHdvcmssIG5hLnJtID0gVCkNCiAgICBuZyA8LSBOQQ0KICAgIGZvciAoaSBpbiAxOmxlbmd0aChjY292YXIpKSB7DQogICAgICAgIG5nW2ldIDwtIHRhYmxlKGNjb3Zhcilbcm93bmFtZXModGFibGUoY2NvdmFyKSkgPT0gY2NvdmFyW2ldXQ0KICAgIH0NCiAgICBOIDwtIGxlbmd0aChjY292YXIpDQogICAgd2V4cCA8LSBzdW0obmkgKiAoKG5nIC0gMSkvKE4gLSAxKSksIG5hLnJtID0gVCkNCiAgICANCiAgICAjIHdnZzEgaG93IG1hbnkgaW50cmFncm91cCB0aWVzDQogICAgdyA8LSBmc3VtaW50cmEobmV0d29yaywgZmhvbW9tYXQoY2NvdmFyKSkNCiAgICANCiAgICBTY29sX25ldCA8LSBpZmVsc2UodyA+PSB3ZXhwLCAodyAtIHdleHApLyhzdW0obmksIG5hLnJtID0gVCkgLSB3ZXhwKSwgKHcgLSB3ZXhwKS93ZXhwKQ0KICAgIHJldHVybihTY29sX25ldCkNCn0NCg0KZk1vcmFuLkkgPC0gZnVuY3Rpb24oeCwgd2VpZ2h0LCBzY2FsZWQgPSBGQUxTRSwgbmEucm0gPSBGQUxTRSwgYWx0ZXJuYXRpdmUgPSAidHdvLnNpZGVkIiwgcm93c3RhbmRhcmRpemUgPSBUUlVFKSB7DQogICAgaWYgKHJvd3N0YW5kYXJkaXplKSB7DQogICAgICAgIGlmIChkaW0od2VpZ2h0KVsxXSAhPSBkaW0od2VpZ2h0KVsyXSkgDQogICAgICAgICAgICBzdG9wKCInd2VpZ2h0JyBtdXN0IGJlIGEgc3F1YXJlIG1hdHJpeCIpDQogICAgICAgIG4gPC0gbGVuZ3RoKHgpDQogICAgICAgIGlmIChkaW0od2VpZ2h0KVsxXSAhPSBuKSANCiAgICAgICAgICAgIHN0b3AoIid3ZWlnaHQnIG11c3QgaGF2ZSBhcyBtYW55IHJvd3MgYXMgb2JzZXJ2YXRpb25zIGluICd4JyIpDQogICAgICAgIGVpIDwtIC0xLyhuIC0gMSkNCiAgICAgICAgbmFzIDwtIGlzLm5hKHgpDQogICAgICAgIGlmIChhbnkobmFzKSkgew0KICAgICAgICAgICAgaWYgKG5hLnJtKSB7DQogICAgICAgICAgICAgICAgeCA8LSB4WyFuYXNdDQogICAgICAgICAgICAgICAgbiA8LSBsZW5ndGgoeCkNCiAgICAgICAgICAgICAgICB3ZWlnaHQgPC0gd2VpZ2h0WyFuYXMsICFuYXNdDQogICAgICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICAgICAgIHdhcm5pbmcoIid4JyBoYXMgbWlzc2luZyB2YWx1ZXM6IG1heWJlIHlvdSB3YW50ZWQgdG8gc2V0IG5hLnJtID0gVFJVRT8iKQ0KICAgICAgICAgICAgICAgIHJldHVybihsaXN0KG9ic2VydmVkID0gTkEsIGV4cGVjdGVkID0gZWksIHNkID0gTkEsIHAudmFsdWUgPSBOQSkpDQogICAgICAgICAgICB9DQogICAgICAgIH0NCiAgICAgICAgUk9XU1VNIDwtIHJvd1N1bXMod2VpZ2h0KQ0KICAgICAgICBST1dTVU1bUk9XU1VNID09IDBdIDwtIDENCiAgICAgICAgd2VpZ2h0IDwtIHdlaWdodC9ST1dTVU0NCiAgICAgICAgcyA8LSBzdW0od2VpZ2h0KQ0KICAgICAgICBtIDwtIG1lYW4oeCkNCiAgICAgICAgeSA8LSB4IC0gbQ0KICAgICAgICBjdiA8LSBzdW0od2VpZ2h0ICogeSAlbyUgeSkNCiAgICAgICAgdiA8LSBzdW0oeV4yKQ0KICAgICAgICBvYnMgPC0gKG4vcykgKiAoY3YvdikNCiAgICAgICAgaWYgKHNjYWxlZCkgew0KICAgICAgICAgICAgaS5tYXggPC0gKG4vcykgKiAoc2Qocm93U3Vtcyh3ZWlnaHQpICogeSkvc3FydCh2LyhuIC0gMSkpKQ0KICAgICAgICAgICAgb2JzIDwtIG9icy9pLm1heA0KICAgICAgICB9DQogICAgICAgIFMxIDwtIDAuNSAqIHN1bSgod2VpZ2h0ICsgdCh3ZWlnaHQpKV4yKQ0KICAgICAgICBTMiA8LSBzdW0oKGFwcGx5KHdlaWdodCwgMSwgc3VtKSArIGFwcGx5KHdlaWdodCwgMiwgc3VtKSleMikNCiAgICAgICAgcy5zcSA8LSBzXjINCiAgICAgICAgayA8LSAoc3VtKHleNCkvbikvKHYvbileMg0KICAgICAgICBzZGkgPC0gc3FydCgobiAqICgobl4yIC0gMyAqIG4gKyAzKSAqIFMxIC0gbiAqIFMyICsgMyAqIHMuc3EpIC0gayAqIChuICogKG4gLSAxKSAqIFMxIC0gMiAqIG4gKiANCiAgICAgICAgICAgIFMyICsgNiAqIHMuc3EpKS8oKG4gLSAxKSAqIChuIC0gMikgKiAobiAtIDMpICogcy5zcSkgLSAxLygobiAtIDEpXjIpKQ0KICAgICAgICBhbHRlcm5hdGl2ZSA8LSBtYXRjaC5hcmcoYWx0ZXJuYXRpdmUsIGMoInR3by5zaWRlZCIsICJsZXNzIiwgImdyZWF0ZXIiKSkNCiAgICAgICAgcHYgPC0gcG5vcm0ob2JzLCBtZWFuID0gZWksIHNkID0gc2RpKQ0KICAgICAgICBpZiAoYWx0ZXJuYXRpdmUgPT0gInR3by5zaWRlZCIpIA0KICAgICAgICAgICAgcHYgPC0gaWYgKG9icyA8PSBlaSkgDQogICAgICAgICAgICAgICAgMiAqIHB2IGVsc2UgMiAqICgxIC0gcHYpDQogICAgICAgIGlmIChhbHRlcm5hdGl2ZSA9PSAiZ3JlYXRlciIpIA0KICAgICAgICAgICAgcHYgPC0gMSAtIHB2DQogICAgICAgIGxpc3Qob2JzZXJ2ZWQgPSBvYnMsIGV4cGVjdGVkID0gZWksIHNkID0gc2RpLCBwLnZhbHVlID0gcHYpDQogICAgfSBlbHNlIHsNCiAgICAgICAgaWYgKGRpbSh3ZWlnaHQpWzFdICE9IGRpbSh3ZWlnaHQpWzJdKSANCiAgICAgICAgICAgIHN0b3AoIid3ZWlnaHQnIG11c3QgYmUgYSBzcXVhcmUgbWF0cml4IikNCiAgICAgICAgbiA8LSBsZW5ndGgoeCkNCiAgICAgICAgaWYgKGRpbSh3ZWlnaHQpWzFdICE9IG4pIA0KICAgICAgICAgICAgc3RvcCgiJ3dlaWdodCcgbXVzdCBoYXZlIGFzIG1hbnkgcm93cyBhcyBvYnNlcnZhdGlvbnMgaW4gJ3gnIikNCiAgICAgICAgZWkgPC0gLTEvKG4gLSAxKQ0KICAgICAgICBuYXMgPC0gaXMubmEoeCkNCiAgICAgICAgaWYgKGFueShuYXMpKSB7DQogICAgICAgICAgICBpZiAobmEucm0pIHsNCiAgICAgICAgICAgICAgICB4IDwtIHhbIW5hc10NCiAgICAgICAgICAgICAgICBuIDwtIGxlbmd0aCh4KQ0KICAgICAgICAgICAgICAgIHdlaWdodCA8LSB3ZWlnaHRbIW5hcywgIW5hc10NCiAgICAgICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgICAgICAgd2FybmluZygiJ3gnIGhhcyBtaXNzaW5nIHZhbHVlczogbWF5YmUgeW91IHdhbnRlZCB0byBzZXQgbmEucm0gPSBUUlVFPyIpDQogICAgICAgICAgICAgICAgcmV0dXJuKGxpc3Qob2JzZXJ2ZWQgPSBOQSwgZXhwZWN0ZWQgPSBlaSwgc2QgPSBOQSwgcC52YWx1ZSA9IE5BKSkNCiAgICAgICAgICAgIH0NCiAgICAgICAgfQ0KICAgICAgICAjIFJPV1NVTSA8LSByb3dTdW1zKHdlaWdodCkgUk9XU1VNW1JPV1NVTSA9PSAwXSA8LSAxIHdlaWdodCA8LSB3ZWlnaHQvUk9XU1VNDQogICAgICAgIHMgPC0gc3VtKHdlaWdodCkNCiAgICAgICAgbSA8LSBtZWFuKHgpDQogICAgICAgIHkgPC0geCAtIG0NCiAgICAgICAgY3YgPC0gc3VtKHdlaWdodCAqIHkgJW8lIHkpDQogICAgICAgIHYgPC0gc3VtKHleMikNCiAgICAgICAgb2JzIDwtIChuL3MpICogKGN2L3YpDQogICAgICAgIGlmIChzY2FsZWQpIHsNCiAgICAgICAgICAgIGkubWF4IDwtIChuL3MpICogKHNkKHJvd1N1bXMod2VpZ2h0KSAqIHkpL3NxcnQodi8obiAtIDEpKSkNCiAgICAgICAgICAgIG9icyA8LSBvYnMvaS5tYXgNCiAgICAgICAgfQ0KICAgICAgICBTMSA8LSAwLjUgKiBzdW0oKHdlaWdodCArIHQod2VpZ2h0KSleMikNCiAgICAgICAgUzIgPC0gc3VtKChhcHBseSh3ZWlnaHQsIDEsIHN1bSkgKyBhcHBseSh3ZWlnaHQsIDIsIHN1bSkpXjIpDQogICAgICAgIHMuc3EgPC0gc14yDQogICAgICAgIGsgPC0gKHN1bSh5XjQpL24pLyh2L24pXjINCiAgICAgICAgc2RpIDwtIHNxcnQoKG4gKiAoKG5eMiAtIDMgKiBuICsgMykgKiBTMSAtIG4gKiBTMiArIDMgKiBzLnNxKSAtIGsgKiAobiAqIChuIC0gMSkgKiBTMSAtIDIgKiBuICogDQogICAgICAgICAgICBTMiArIDYgKiBzLnNxKSkvKChuIC0gMSkgKiAobiAtIDIpICogKG4gLSAzKSAqIHMuc3EpIC0gMS8oKG4gLSAxKV4yKSkNCiAgICAgICAgYWx0ZXJuYXRpdmUgPC0gbWF0Y2guYXJnKGFsdGVybmF0aXZlLCBjKCJ0d28uc2lkZWQiLCAibGVzcyIsICJncmVhdGVyIikpDQogICAgICAgIHB2IDwtIHBub3JtKG9icywgbWVhbiA9IGVpLCBzZCA9IHNkaSkNCiAgICAgICAgaWYgKGFsdGVybmF0aXZlID09ICJ0d28uc2lkZWQiKSANCiAgICAgICAgICAgIHB2IDwtIGlmIChvYnMgPD0gZWkpIA0KICAgICAgICAgICAgICAgIDIgKiBwdiBlbHNlIDIgKiAoMSAtIHB2KQ0KICAgICAgICBpZiAoYWx0ZXJuYXRpdmUgPT0gImdyZWF0ZXIiKSANCiAgICAgICAgICAgIHB2IDwtIDEgLSBwdg0KICAgICAgICBsaXN0KG9ic2VydmVkID0gb2JzLCBleHBlY3RlZCA9IGVpLCBzZCA9IHNkaSwgcC52YWx1ZSA9IHB2KQ0KICAgIH0NCiAgICANCiAgICANCn0NCmBgYA0KDQotLS0tDQoNCldlIGNvdmVyIHRoZSBmb2xsb3dpbmc6DQoNCiogY2x1YiBjaGFyYWN0ZXJpc3RpY3MNCiogbmV0d29yayBzdHJ1Y3R1cmUgaW4gS3Vkb3MgbmV0d29ya3MNCiogZ2VuZGVyIGNvbXBvc2l0aW9uIC8gc2VncmVnYXRpb24NCiogYmVoYXZpb3I6IGFjdGl2aXR5IGxldmVsIChmcmVxdWVuY3kgYW5kIGR1cmF0aW9uKSBhbmQgY29ycmVsYXRpb24NCiogc3BhdGlhbCBuZXR3b3JrIGF1dG9jb3JyZWxhdGlvbjogYmVoYXZpb3JhbCBzaW1pbGFyaXR5IGluIG5ldHdvcmtzDQoNCi0tLS0NCg0KPGJyPg0KDQojIFByaW50IHJlcG9ydA0KIyMgey50YWJzZXQgLnRhYnNldC1mYWRlfSANCk1ha2Ugc3VyZSB0byBjaGVjayB0aGUgb3V0cHV0IG9mIHRoZSAncHJpbnQwMVJlcG9ydCgpJyBmdW5jdGlvbiBmb3IgZ2VuZXJhbCBkYXRhIGRlc2NyaXB0b24gKGRlZ3JlZXMsIG5ldHdvcmsgc2l6ZSwgZXRjLikgYW5kIGEgZ2VuZXJhbCBvdmVydmlldyBvZiB0aGUgZGF0YXNldC4gT3V0cHV0IGlzIHByaW50ZWQgaW4gYSAudHh0IGZpbGUgaW4gdGhlIGRpcmVjdG9yeSBzcGVjaWZpZWQuDQoNCg0KIyMjIENsdWIgMQ0KYGBge3IgZXZhbD0gRn0NCmRmIDwtIGNsdWJkYXRhX3JzaWVuYV9mcmVxW1sxXV0gIyBncmFiIGNsdWIgDQpwcmludDAxUmVwb3J0KGRmLCBtb2RlbG5hbWU9ImZpbGVzL2NsdWIxIikNCmBgYA0KDQohW10oZmlsZXMvY2x1YjEudHh0KXsjaWQgLmNsYXNzIHdpZHRoPTEwMCUgaGVpZ2h0PTIwMHB4fQ0KDQojIyMgQ2x1YiAyDQpgYGB7ciBldmFsPSBGfQ0KZGYgPC0gY2x1YmRhdGFfcnNpZW5hX2ZyZXFbWzJdXSAjIGdyYWIgY2x1YiANCnByaW50MDFSZXBvcnQoZGYsIG1vZGVsbmFtZT0iZmlsZXMvY2x1YjIiKQ0KYGBgDQoNCiFbXShmaWxlcy9jbHViMi50eHQpeyNpZCAuY2xhc3Mgd2lkdGg9MTAwJSBoZWlnaHQ9MjAwcHh9DQoNCiMjIyBDbHViIDMNCmBgYHtyIGV2YWw9IEZ9DQpkZiA8LSBjbHViZGF0YV9yc2llbmFfZnJlcVtbM11dICMgZ3JhYiBjbHViIA0KcHJpbnQwMVJlcG9ydChkZiwgbW9kZWxuYW1lPSJmaWxlcy9jbHViMyIpDQpgYGANCg0KIVtdKGZpbGVzL2NsdWIzLnR4dCl7I2lkIC5jbGFzcyB3aWR0aD0xMDAlIGhlaWdodD0yMDBweH0NCg0KIyMjIENsdWIgNA0KYGBge3IgZXZhbD0gRn0NCmRmIDwtIGNsdWJkYXRhX3JzaWVuYV9mcmVxW1s0XV0gIyBncmFiIGNsdWIgDQpwcmludDAxUmVwb3J0KGRmLCBtb2RlbG5hbWU9ImZpbGVzL2NsdWI0IikNCmBgYA0KDQohW10oZmlsZXMvY2x1YjQudHh0KXsjaWQgLmNsYXNzIHdpZHRoPTEwMCUgaGVpZ2h0PTIwMHB4fQ0KDQojIyMgQ2x1YiA1DQpgYGB7ciBldmFsPSBGfQ0KZGYgPC0gY2x1YmRhdGFfcnNpZW5hX2ZyZXFbWzVdXSAjIGdyYWIgY2x1YiANCnByaW50MDFSZXBvcnQoZGYsIG1vZGVsbmFtZT0iZmlsZXMvY2x1YjUiKQ0KYGBgDQoNCiFbXShmaWxlcy9jbHViNS50eHQpeyNpZCAuY2xhc3Mgd2lkdGg9MTAwJSBoZWlnaHQ9MjAwcHh9DQoNCiMjIHstfQ0KDQotLS0tDQoNCiMgQ2x1YiBjaGFyYWN0ZXJpc3RpY3MNCg0KU29tZSBjbHViIGNoYXJhY3RlcmlzdGljcy4gV2Ugc2hvdyB0aGUgc2l6ZSBvZiB0aGUgbmV0d29yayAodGhlIG51bWJlciBvZiBhY3RvcnMpIGZvciBlYWNoIGNsdWIgYW5kIHRoZSBudW1iZXIgb2YgYWN0aXZlIG1lbWJlcnMgY3VycmVudGx5ICgxOC0xLTIwMjEpIG9uIFN0cmF2YSwgYnkgYWRkaW5nIHRvICp3d3cuc3RyYXZhLmNvbS9jbHVicy8uLi4qIHRoZSBvcmlnaW5hbCBjbHViIGlkLiANCg0KYGBge3J9DQojIGF0dHJpZXZlIGZyb20gdGhlIGNsdWJkYXRhIHRoZSBudW1iZXIgb2YgYWN0b3JzIGluIGVhY2ggbmV0d29yaw0KbmV0c2l6ZSA8LSBjKCANCiAgbGVuZ3RoKGNsdWJkYXRhX3JzaWVuYV9mcmVxW1sxXV0kbm9kZVNldHMkQWN0b3JzKSwgDQogIGxlbmd0aChjbHViZGF0YV9yc2llbmFfZnJlcVtbMl1dJG5vZGVTZXRzJEFjdG9ycyksIA0KICBsZW5ndGgoY2x1YmRhdGFfcnNpZW5hX2ZyZXFbWzNdXSRub2RlU2V0cyRBY3RvcnMpLCANCiAgbGVuZ3RoKGNsdWJkYXRhX3JzaWVuYV9mcmVxW1s0XV0kbm9kZVNldHMkQWN0b3JzKSwgDQogIGxlbmd0aChjbHViZGF0YV9yc2llbmFfZnJlcVtbNV1dJG5vZGVTZXRzJEFjdG9ycykpIA0KDQpjbHVic2l6ZSA8LSBjKDY2LCAxMjcsIDM3MywgMTUsIDE2OSkgIyBmaW5kIHRoZSBudW1iZXIgb2YgbWVtYmVycyBjdXJyZW50bHkgb24gU3RyYXZhDQoNCmRmIDwtIGRhdGEuZnJhbWUoIG5ldHNpemUgPSBuZXRzaXplLCBjbHVic2l6ZSA9IGNsdWJzaXplICkNCg0KcHJpbnQoZGYpDQpgYGANCg0KPGJyPg0KDQotLS0tDQoNCg0KDQojIEt1ZG9zIG5ldHdvcmsNCg0KTm93IGxldCdzIGRlc2NyaWJlIHRoZSBLdWRvcyBuZXR3b3JrLg0KDQojIyAxLiBOb2RlLWxldmVsIHsudGFic2V0IC50YWJzZXQtZmFkZX0NCg0KU3RhcnRpbmcsIGFnYWluLCB3aXRoIGluZGVncmVlcyBhbmQgb3V0ZGVncmVlczogd2hvIHJlY2VpdmVzIGFuZCBnaXZlcyBLdWRvcz8gV2UgdGFrZSBmcm9tIHRoZSBSU2llbmEgb2JqZWN0IHRoZSBLdWRvcyBuZXR3b3JrIGZvciBlYWNoIGNsdWIsIHN1YnNldCB0aGUgZmlyc3Qgd2F2ZSwgYW5kIHR1cm4gaXQgaW50byBhbiAqaWdyYXBoKiBvYmplY3QuIA0KDQojIyMgQ2x1YiAxDQpgYGB7cn0NCmRmIDwtIGNsdWJkYXRhX3JzaWVuYV9mcmVxW1sxXV0gIyBncmFiIGNsdWIgDQprbmV0IDwtIGRmJGRlcHZhcnMka3Vkb25ldCAjIHRha2UgS3VkbyBuZXR3b3JrDQprbmV0MSA8LSBrbmV0WywsMV0gIyB0YWtlIHdhdmUgMSBvbmx5IGZvciBub3cNCg0KIyBtYWtlIGFuICdpZ3JhcGggb2JqZWN0Jw0KRzEgPC0gaWdyYXBoOjpncmFwaF9mcm9tX2FkamFjZW5jeV9tYXRyaXgoa25ldDEsIG1vZGUgPSAiZGlyZWN0ZWQiLCB3ZWlnaHRlZCA9IE5VTEwsIGRpYWcgPSBUUlVFLCBhZGQuY29sbmFtZXMgPSBOQSwgYWRkLnJvd25hbWVzID0gTkEpDQoNCiMgZmluZCBpbi0gYW5kIG91dGRlZ3JlZSBmb3IgZWFjaCBub2RlDQpoaXN0KGlncmFwaDo6ZGVncmVlKEcxLCBtb2RlPSJvdXQiKSwgeGxhYj0ib3V0ZGVncmVlIiwgbWFpbj0iaGlzdG9ncmFtIG9mIEt1ZG8gb3V0ZGVncmVlIikNCmhpc3QoaWdyYXBoOjpkZWdyZWUoRzEsIG1vZGU9ImluIiksIHhsYWI9ImluZGVncmVlIiwgbWFpbj0iaGlzdG9ncmFtIG9mIEt1ZG8gaW5kZWdyZWUiKQ0KYGBgDQoNCiMjIyBDbHViIDINCmBgYHtyfQ0KZGYgPC0gY2x1YmRhdGFfcnNpZW5hX2ZyZXFbWzJdXSAjIGdyYWIgY2x1YiB0DQprbmV0IDwtIGRmJGRlcHZhcnMka3Vkb25ldCAjIHRha2UgS3VkbyBuZXR3b3JrDQprbmV0MSA8LSBrbmV0WywsMV0gIyB0YWtlIHdhdmUgMSBvbmx5IGZvciBub3cNCg0KIyBtYWtlIGFuICdpZ3JhcGggb2JqZWN0Jw0KRzEgPC0gaWdyYXBoOjpncmFwaF9mcm9tX2FkamFjZW5jeV9tYXRyaXgoa25ldDEsIG1vZGUgPSAiZGlyZWN0ZWQiLCB3ZWlnaHRlZCA9IE5VTEwsIGRpYWcgPSBUUlVFLCBhZGQuY29sbmFtZXMgPSBOQSwgYWRkLnJvd25hbWVzID0gTkEpDQoNCiMgZmluZCBpbi0gYW5kIG91dGRlZ3JlZSBmb3IgZWFjaCBub2RlDQpoaXN0KGlncmFwaDo6ZGVncmVlKEcxLCBtb2RlPSJvdXQiKSwgeGxhYj0ib3V0ZGVncmVlIiwgbWFpbj0iaGlzdG9ncmFtIG9mIEt1ZG8gb3V0ZGVncmVlIikNCg0KaGlzdChpZ3JhcGg6OmRlZ3JlZShHMSwgbW9kZT0iaW4iKSwgeGxhYj0iaW5kZWdyZWUiLCBtYWluPSJoaXN0b2dyYW0gb2YgS3VkbyBpbmRlZ3JlZSIpDQpgYGANCg0KIyMjIENsdWIgMw0KYGBge3J9DQpkZiA8LSBjbHViZGF0YV9yc2llbmFfZnJlcVtbM11dICMgZ3JhYiBjbHViIA0Ka25ldCA8LSBkZiRkZXB2YXJzJGt1ZG9uZXQgIyB0YWtlIEt1ZG8gbmV0d29yaw0Ka25ldDEgPC0ga25ldFssLDFdICMgdGFrZSB3YXZlIDEgb25seSBmb3Igbm93DQoNCiMgbWFrZSBhbiAnaWdyYXBoIG9iamVjdCcNCkcxIDwtIGlncmFwaDo6Z3JhcGhfZnJvbV9hZGphY2VuY3lfbWF0cml4KGtuZXQxLCBtb2RlID0gImRpcmVjdGVkIiwgd2VpZ2h0ZWQgPSBOVUxMLCBkaWFnID0gVFJVRSwgYWRkLmNvbG5hbWVzID0gTkEsIGFkZC5yb3duYW1lcyA9IE5BKQ0KDQojIGZpbmQgaW4tIGFuZCBvdXRkZWdyZWUgZm9yIGVhY2ggbm9kZQ0KaGlzdChpZ3JhcGg6OmRlZ3JlZShHMSwgbW9kZT0ib3V0IiksIHhsYWI9Im91dGRlZ3JlZSIsIG1haW49Imhpc3RvZ3JhbSBvZiBLdWRvIG91dGRlZ3JlZSIpDQoNCmhpc3QoaWdyYXBoOjpkZWdyZWUoRzEsIG1vZGU9ImluIiksIHhsYWI9ImluZGVncmVlIiwgbWFpbj0iaGlzdG9ncmFtIG9mIEt1ZG8gaW5kZWdyZWUiKQ0KYGBgDQoNCiMjIyBDbHViIDQNCmBgYHtyfQ0KZGYgPC0gY2x1YmRhdGFfcnNpZW5hX2ZyZXFbWzRdXSAjIGdyYWIgY2x1YiANCmtuZXQgPC0gZGYkZGVwdmFycyRrdWRvbmV0ICMgdGFrZSBLdWRvIG5ldHdvcmsNCmtuZXQxIDwtIGtuZXRbLCwxXSAjIHRha2Ugd2F2ZSAxIG9ubHkgZm9yIG5vdw0KDQojIG1ha2UgYW4gJ2lncmFwaCBvYmplY3QnDQpHMSA8LSBpZ3JhcGg6OmdyYXBoX2Zyb21fYWRqYWNlbmN5X21hdHJpeChrbmV0MSwgbW9kZSA9ICJkaXJlY3RlZCIsIHdlaWdodGVkID0gTlVMTCwgZGlhZyA9IFRSVUUsIGFkZC5jb2xuYW1lcyA9IE5BLCBhZGQucm93bmFtZXMgPSBOQSkNCg0KIyBmaW5kIGluLSBhbmQgb3V0ZGVncmVlIGZvciBlYWNoIG5vZGUNCmhpc3QoaWdyYXBoOjpkZWdyZWUoRzEsIG1vZGU9Im91dCIpLCB4bGFiPSJvdXRkZWdyZWUiLCBtYWluPSJoaXN0b2dyYW0gb2YgS3VkbyBvdXRkZWdyZWUiKQ0KDQpoaXN0KGlncmFwaDo6ZGVncmVlKEcxLCBtb2RlPSJpbiIpLCB4bGFiPSJpbmRlZ3JlZSIsIG1haW49Imhpc3RvZ3JhbSBvZiBLdWRvIGluZGVncmVlIikNCmBgYA0KDQojIyMgQ2x1YiA1DQpgYGB7cn0NCmRmIDwtIGNsdWJkYXRhX3JzaWVuYV9mcmVxW1s1XV0gIyBncmFiIGNsdWIgDQprbmV0IDwtIGRmJGRlcHZhcnMka3Vkb25ldCAjIHRha2UgS3VkbyBuZXR3b3JrDQprbmV0MSA8LSBrbmV0WywsMV0gIyB0YWtlIHdhdmUgMSBvbmx5IGZvciBub3cNCg0KIyBtYWtlIGFuICdpZ3JhcGggb2JqZWN0Jw0KRzEgPC0gaWdyYXBoOjpncmFwaF9mcm9tX2FkamFjZW5jeV9tYXRyaXgoa25ldDEsIG1vZGUgPSAiZGlyZWN0ZWQiLCB3ZWlnaHRlZCA9IE5VTEwsIGRpYWcgPSBUUlVFLCBhZGQuY29sbmFtZXMgPSBOQSwgYWRkLnJvd25hbWVzID0gTkEpDQoNCiMgZmluZCBpbi0gYW5kIG91dGRlZ3JlZSBmb3IgZWFjaCBub2RlDQpoaXN0KGlncmFwaDo6ZGVncmVlKEcxLCBtb2RlPSJvdXQiKSwgeGxhYj0ib3V0ZGVncmVlIiwgbWFpbj0iaGlzdG9ncmFtIG9mIEt1ZG8gb3V0ZGVncmVlIikNCg0KaGlzdChpZ3JhcGg6OmRlZ3JlZShHMSwgbW9kZT0iaW4iKSwgeGxhYj0iaW5kZWdyZWUiLCBtYWluPSJoaXN0b2dyYW0gb2YgS3VkbyBpbmRlZ3JlZSIpDQpgYGANCg0KIyMgey19DQoNCldlIGNhbiBvYnNlcnZlIGEgUGFyZXRvLWxpa2UtcGF0dGVybjogc29tZSBnaXZlL3JlY2VpdmUgbW9zdCBvZiB0aGUgS3Vkb3MgZ2l2ZW4sIHdoaWxlIG1vc3QgZ2l2ZS9yZWNlaXZlIGZldy4NCg0KPGJyPg0KDQojIyAyLiBEeWFkLWxldmVsIHsudGFic2V0IC50YWJzZXQtZmFkZX0NCg0KQXQgdGhlIGR5YWQtbGV2ZWw6IGxldCdzIHNlZSB0byB3aGF0IGV4dGVudCBLdWRvcyB0ZW5kIHRvIGJlIHJlY2lwcm9jYXRlZCBiZXR3ZWVuIGFjdG9ycy4NCg0KIyMjIENsdWIgMQ0KYGBge3IgfQ0KIyBtYWtlIGlncmFwaCBvYmplY3QgZm9yIHRoZSBjbHViLCBhdCB3YXZlIDENCmRmIDwtIGNsdWJkYXRhX3JzaWVuYV9mcmVxW1sxXV0gIyBncmFiIGNsdWIgDQprbmV0IDwtIGRmJGRlcHZhcnMka3Vkb25ldCAjIHRha2UgS3VkbyBuZXR3b3JrDQprbmV0MSA8LSBrbmV0WywsMV0gIyB0YWtlIHdhdmUgMSBvbmx5IGZvciBub3cNCg0KIyBtYWtlIGFuICdpZ3JhcGggb2JqZWN0Jw0KRzEgPC0gaWdyYXBoOjpncmFwaF9mcm9tX2FkamFjZW5jeV9tYXRyaXgoa25ldDEsIG1vZGUgPSAiZGlyZWN0ZWQiLCB3ZWlnaHRlZCA9IE5VTEwsIGRpYWcgPSBUUlVFLCBhZGQuY29sbmFtZXMgPSBOQSwgYWRkLnJvd25hbWVzID0gTkEpDQoNCiMgY2xhc3NpZnkgZHlhZHMNCmR5YWRjb3VudCA8LSBpZ3JhcGg6OmR5YWQuY2Vuc3VzKEcxKQ0KDQojIGFkZCB0aGUgdG90YWwgbnVtYmVyIG9mIGR5YWRzIHRvIHRoZSBncmFwaA0KZHlhZGNvdW50JHRvdGFsIDwtICh2Y291bnQoRzEpKih2Y291bnQoRzEpLTEpKS8yDQpkeWFkY291bnQNCg0KIyBjb21wYXJlIHZhbHVlcyB3aXRoIGEgcmFuZG9tIGdyYXBoIG9mIHRoZSBzYW1lIHNpemUgd2l0aCB0aGUgc2FtZSBkZW5zaXR5DQpkZW5zIDwtIGlncmFwaDo6Z3JhcGguZGVuc2l0eShHMSkNCnNpemUgPC0gaWdyYXBoOjp2Y291bnQoRzEpDQp0cmlhbCA8LSAxMDAwDQpyZWNpcCA8LSByZXAoTkEsIHRyaWFsKQ0KDQpmb3IgKCBpIGluIDE6dHJpYWwgKXsNCiAgcmFuZG9tX2dyYXBoIDwtIGlncmFwaDo6ZXJkb3MucmVueWkuZ2FtZShuID0gc2l6ZSwgcC5vci5tID0gZGVucywgZGlyZWN0ZWQgPSBUUlVFKQ0KICByZWNpcFtpXSA8LSBpZ3JhcGg6OmR5YWQuY2Vuc3VzKHJhbmRvbV9ncmFwaCkkbXV0DQp9DQoNCntoaXN0KHJlY2lwLCBtYWluPSJudW1iZXIgb2YgcmVjaXByb2NhdGVkIEt1ZG9zIGluIHJhbmRvbSBncmFwaCIsIHhsYWI9IiIsICkNCmFibGluZSh2PWR5YWRjb3VudCRtdXQsIGNvbD0icmVkIiwgbHdkPTMpfQ0KYGBgDQoNCiMjIyBDbHViIDINCmBgYHtyIH0NCiMgbWFrZSBpZ3JhcGggb2JqZWN0IGZvciB0aGUgY2x1YiwgYXQgd2F2ZSAxDQpkZiA8LSBjbHViZGF0YV9yc2llbmFfZnJlcVtbMl1dICMgZ3JhYiBjbHViIA0Ka25ldCA8LSBkZiRkZXB2YXJzJGt1ZG9uZXQgIyB0YWtlIEt1ZG8gbmV0d29yaw0Ka25ldDEgPC0ga25ldFssLDFdICMgdGFrZSB3YXZlIDEgb25seSBmb3Igbm93DQoNCiMgbWFrZSBhbiAnaWdyYXBoIG9iamVjdCcNCkcxIDwtIGlncmFwaDo6Z3JhcGhfZnJvbV9hZGphY2VuY3lfbWF0cml4KGtuZXQxLCBtb2RlID0gImRpcmVjdGVkIiwgd2VpZ2h0ZWQgPSBOVUxMLCBkaWFnID0gVFJVRSwgYWRkLmNvbG5hbWVzID0gTkEsIGFkZC5yb3duYW1lcyA9IE5BKQ0KDQojIGNsYXNzaWZ5IGR5YWRzDQpkeWFkY291bnQgPC0gaWdyYXBoOjpkeWFkLmNlbnN1cyhHMSkNCg0KIyBhZGQgdGhlIHRvdGFsIG51bWJlciBvZiBkeWFkcyB0byB0aGUgZ3JhcGgNCmR5YWRjb3VudCR0b3RhbCA8LSAodmNvdW50KEcxKSoodmNvdW50KEcxKS0xKSkvMg0KZHlhZGNvdW50DQoNCiMgY29tcGFyZSB2YWx1ZXMgd2l0aCBhIHJhbmRvbSBncmFwaCBvZiB0aGUgc2FtZSBzaXplIHdpdGggdGhlIHNhbWUgZGVuc2l0eQ0KZGVucyA8LSBpZ3JhcGg6OmdyYXBoLmRlbnNpdHkoRzEpDQpzaXplIDwtIGlncmFwaDo6dmNvdW50KEcxKQ0KdHJpYWwgPC0gMTAwMA0KcmVjaXAgPC0gcmVwKE5BLCB0cmlhbCkNCg0KZm9yICggaSBpbiAxOnRyaWFsICl7DQogIHJhbmRvbV9ncmFwaCA8LSBpZ3JhcGg6OmVyZG9zLnJlbnlpLmdhbWUobiA9IHNpemUsIHAub3IubSA9IGRlbnMsIGRpcmVjdGVkID0gVFJVRSkNCiAgcmVjaXBbaV0gPC0gaWdyYXBoOjpkeWFkLmNlbnN1cyhyYW5kb21fZ3JhcGgpJG11dA0KfQ0KDQp7aGlzdChyZWNpcCwgbWFpbj0ibnVtYmVyIG9mIHJlY2lwcm9jYXRlZCBLdWRvcyBpbiByYW5kb20gZ3JhcGgiLCB4bGFiPSIiLCApDQphYmxpbmUodj1keWFkY291bnQkbXV0LCBjb2w9InJlZCIsIGx3ZD0zKX0NCmBgYA0KDQojIyMgQ2x1YiAzDQpgYGB7ciB9DQojIG1ha2UgaWdyYXBoIG9iamVjdCBmb3IgdGhlIGNsdWIsIGF0IHdhdmUgMQ0KZGYgPC0gY2x1YmRhdGFfcnNpZW5hX2ZyZXFbWzNdXSAjIGdyYWIgY2x1YiANCmtuZXQgPC0gZGYkZGVwdmFycyRrdWRvbmV0ICMgdGFrZSBLdWRvIG5ldHdvcmsNCmtuZXQxIDwtIGtuZXRbLCwxXSAjIHRha2Ugd2F2ZSAxIG9ubHkgZm9yIG5vdw0KDQojIG1ha2UgYW4gJ2lncmFwaCBvYmplY3QnDQpHMSA8LSBpZ3JhcGg6OmdyYXBoX2Zyb21fYWRqYWNlbmN5X21hdHJpeChrbmV0MSwgbW9kZSA9ICJkaXJlY3RlZCIsIHdlaWdodGVkID0gTlVMTCwgZGlhZyA9IFRSVUUsIGFkZC5jb2xuYW1lcyA9IE5BLCBhZGQucm93bmFtZXMgPSBOQSkNCg0KIyBjbGFzc2lmeSBkeWFkcw0KZHlhZGNvdW50IDwtIGlncmFwaDo6ZHlhZC5jZW5zdXMoRzEpDQoNCiMgYWRkIHRoZSB0b3RhbCBudW1iZXIgb2YgZHlhZHMgdG8gdGhlIGdyYXBoDQpkeWFkY291bnQkdG90YWwgPC0gKHZjb3VudChHMSkqKHZjb3VudChHMSktMSkpLzINCmR5YWRjb3VudA0KDQojIGNvbXBhcmUgdmFsdWVzIHdpdGggYSByYW5kb20gZ3JhcGggb2YgdGhlIHNhbWUgc2l6ZSB3aXRoIHRoZSBzYW1lIGRlbnNpdHkNCmRlbnMgPC0gaWdyYXBoOjpncmFwaC5kZW5zaXR5KEcxKQ0Kc2l6ZSA8LSBpZ3JhcGg6OnZjb3VudChHMSkNCnRyaWFsIDwtIDEwMDANCnJlY2lwIDwtIHJlcChOQSwgdHJpYWwpDQoNCmZvciAoIGkgaW4gMTp0cmlhbCApew0KICByYW5kb21fZ3JhcGggPC0gaWdyYXBoOjplcmRvcy5yZW55aS5nYW1lKG4gPSBzaXplLCBwLm9yLm0gPSBkZW5zLCBkaXJlY3RlZCA9IFRSVUUpDQogIHJlY2lwW2ldIDwtIGlncmFwaDo6ZHlhZC5jZW5zdXMocmFuZG9tX2dyYXBoKSRtdXQNCn0NCg0Ke2hpc3QocmVjaXAsIG1haW49Im51bWJlciBvZiByZWNpcHJvY2F0ZWQgS3Vkb3MgaW4gcmFuZG9tIGdyYXBoIiwgeGxhYj0iIiwgKQ0KYWJsaW5lKHY9ZHlhZGNvdW50JG11dCwgY29sPSJyZWQiLCBsd2Q9Myl9DQpgYGANCg0KIyMjIENsdWIgNA0KYGBge3IgfQ0KIyBtYWtlIGlncmFwaCBvYmplY3QgZm9yIHRoZSBjbHViLCBhdCB3YXZlIDENCmRmIDwtIGNsdWJkYXRhX3JzaWVuYV9mcmVxW1s0XV0gIyBncmFiIGNsdWIgDQprbmV0IDwtIGRmJGRlcHZhcnMka3Vkb25ldCAjIHRha2UgS3VkbyBuZXR3b3JrDQprbmV0MSA8LSBrbmV0WywsMV0gIyB0YWtlIHdhdmUgMSBvbmx5IGZvciBub3cNCg0KIyBtYWtlIGFuICdpZ3JhcGggb2JqZWN0Jw0KRzEgPC0gaWdyYXBoOjpncmFwaF9mcm9tX2FkamFjZW5jeV9tYXRyaXgoa25ldDEsIG1vZGUgPSAiZGlyZWN0ZWQiLCB3ZWlnaHRlZCA9IE5VTEwsIGRpYWcgPSBUUlVFLCBhZGQuY29sbmFtZXMgPSBOQSwgYWRkLnJvd25hbWVzID0gTkEpDQoNCiMgY2xhc3NpZnkgZHlhZHMNCmR5YWRjb3VudCA8LSBpZ3JhcGg6OmR5YWQuY2Vuc3VzKEcxKQ0KDQojIGFkZCB0aGUgdG90YWwgbnVtYmVyIG9mIGR5YWRzIHRvIHRoZSBncmFwaA0KZHlhZGNvdW50JHRvdGFsIDwtICh2Y291bnQoRzEpKih2Y291bnQoRzEpLTEpKS8yDQpkeWFkY291bnQNCg0KIyBjb21wYXJlIHZhbHVlcyB3aXRoIGEgcmFuZG9tIGdyYXBoIG9mIHRoZSBzYW1lIHNpemUgd2l0aCB0aGUgc2FtZSBkZW5zaXR5DQpkZW5zIDwtIGlncmFwaDo6Z3JhcGguZGVuc2l0eShHMSkNCnNpemUgPC0gaWdyYXBoOjp2Y291bnQoRzEpDQp0cmlhbCA8LSAxMDAwDQpyZWNpcCA8LSByZXAoTkEsIHRyaWFsKQ0KDQpmb3IgKCBpIGluIDE6dHJpYWwgKXsNCiAgcmFuZG9tX2dyYXBoIDwtIGlncmFwaDo6ZXJkb3MucmVueWkuZ2FtZShuID0gc2l6ZSwgcC5vci5tID0gZGVucywgZGlyZWN0ZWQgPSBUUlVFKQ0KICByZWNpcFtpXSA8LSBpZ3JhcGg6OmR5YWQuY2Vuc3VzKHJhbmRvbV9ncmFwaCkkbXV0DQp9DQoNCntoaXN0KHJlY2lwLCBtYWluPSJudW1iZXIgb2YgcmVjaXByb2NhdGVkIEt1ZG9zIGluIHJhbmRvbSBncmFwaCIsIHhsYWI9IiIsICkNCmFibGluZSh2PWR5YWRjb3VudCRtdXQsIGNvbD0icmVkIiwgbHdkPTMpfQ0KYGBgDQoNCiMjIyBDbHViIDUNCmBgYHtyfQ0KIyBtYWtlIGlncmFwaCBvYmplY3QgZm9yIHRoZSBjbHViLCBhdCB3YXZlIDENCmRmIDwtIGNsdWJkYXRhX3JzaWVuYV9mcmVxW1s1XV0gIyBncmFiIGNsdWIgDQprbmV0IDwtIGRmJGRlcHZhcnMka3Vkb25ldCAjIHRha2UgS3VkbyBuZXR3b3JrDQprbmV0MSA8LSBrbmV0WywsMV0gIyB0YWtlIHdhdmUgMSBvbmx5IGZvciBub3cNCg0KIyBtYWtlIGFuICdpZ3JhcGggb2JqZWN0Jw0KRzEgPC0gaWdyYXBoOjpncmFwaF9mcm9tX2FkamFjZW5jeV9tYXRyaXgoa25ldDEsIG1vZGUgPSAiZGlyZWN0ZWQiLCB3ZWlnaHRlZCA9IE5VTEwsIGRpYWcgPSBUUlVFLCBhZGQuY29sbmFtZXMgPSBOQSwgYWRkLnJvd25hbWVzID0gTkEpDQoNCiMgY2xhc3NpZnkgZHlhZHMNCmR5YWRjb3VudCA8LSBpZ3JhcGg6OmR5YWQuY2Vuc3VzKEcxKQ0KDQojIGFkZCB0aGUgdG90YWwgbnVtYmVyIG9mIGR5YWRzIHRvIHRoZSBncmFwaA0KZHlhZGNvdW50JHRvdGFsIDwtICh2Y291bnQoRzEpKih2Y291bnQoRzEpLTEpKS8yDQpkeWFkY291bnQNCg0KIyBjb21wYXJlIHZhbHVlcyB3aXRoIGEgcmFuZG9tIGdyYXBoIG9mIHRoZSBzYW1lIHNpemUgd2l0aCB0aGUgc2FtZSBkZW5zaXR5DQpkZW5zIDwtIGlncmFwaDo6Z3JhcGguZGVuc2l0eShHMSkNCnNpemUgPC0gaWdyYXBoOjp2Y291bnQoRzEpDQp0cmlhbCA8LSAxMDAwDQpyZWNpcCA8LSByZXAoTkEsIHRyaWFsKQ0KDQpmb3IgKCBpIGluIDE6dHJpYWwgKXsNCiAgcmFuZG9tX2dyYXBoIDwtIGlncmFwaDo6ZXJkb3MucmVueWkuZ2FtZShuID0gc2l6ZSwgcC5vci5tID0gZGVucywgZGlyZWN0ZWQgPSBUUlVFKQ0KICByZWNpcFtpXSA8LSBpZ3JhcGg6OmR5YWQuY2Vuc3VzKHJhbmRvbV9ncmFwaCkkbXV0DQp9DQoNCntoaXN0KHJlY2lwLCBtYWluPSJudW1iZXIgb2YgcmVjaXByb2NhdGVkIEt1ZG9zIGluIHJhbmRvbSBncmFwaCIsIHhsYWI9IiIsICkNCmFibGluZSh2PWR5YWRjb3VudCRtdXQsIGNvbD0icmVkIiwgbHdkPTMpfQ0KYGBgDQoNCiMjIHstfQ0KDQpDb25jbHVzaW9uOiBLdWRvcyB0ZW5kIHRvIGJlIHJlY2lwcm9jYXRlZCENCg0KLS0tLQ0KDQoNCjxicj4NCg0KIyBHZW5kZXIgY29tcG9zaXRpb24gDQoNCiMjIHsudGFic2V0IC50YWJzZXQtZmFkZX0NCg0KTGV0J3MgaW52ZXN0aWdhdGUgdGhlIGdlbmRlciBjb21wb3NpdGlvbiBvZiB0aGUgY2x1Yi4NCldlIG11c3QgcmV0cmlldmUgZ2VuZGVyIGZyb20gdGhlIG9iamVjdCAobm90ZSB0aGF0IHdlIHVzZSB0aGUgY2x1YmRhdGEgb2JqZWN0LCBub3QgdGhlIFJTaWVuYSBvYmplY3QpLiBUaGVuIHdlIG1ha2UgYSBjYXRlZ29yaWNhbCBnZW5kZXIgdmFyaWFibGUgYW5kIHBsb3QgaXQuDQoNCiMjIyBDbHViIDENCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICdmb2xkLWhpZGUnfQ0KDQpkZiA8LSBjbHViZGF0YVtbMV1dICMgZ3JhYiBjbHViIA0KDQojIHJldHJpZXZlIG5vZGUtYXR0cmlidXRlIGdlbmRlciBmcm9tIG9iamVjdA0KbWFsZSA8LSBkZiRtYWxlDQpmZW1hbGUgPC0gZGYkZmVtYWxlDQpvdGhlciA8LSBkZiRvdGhlcg0KDQojIGFzIGZhY3Rvcg0KZ2VuZGVyIDwtIE5BDQpnZW5kZXIgPC0gaWZlbHNlKG1hbGUgPT0gMSwgIk1hbGUiLCBnZW5kZXIpDQpnZW5kZXIgPC0gaWZlbHNlKGZlbWFsZSA9PSAxLCAiRmVtYWxlIiwgZ2VuZGVyKQ0KZ2VuZGVyIDwtIGlmZWxzZShvdGhlciA9PSAxLCAiT3RoZXIiLCBnZW5kZXIpDQpnZW5kZXIgPC0gaWZlbHNlKGlzLm5hKGdlbmRlciksICJNaXNzaW5nIiwgZ2VuZGVyKSAjIG1pc3NpbmcgY2F0ZWdvcnkNCg0KIyBtYWtlIGRhdGFmcmFtZQ0KZGYgPC0gZGF0YS5mcmFtZSgNCiAgZ2VuZGVyID0gYXMuZmFjdG9yKGMoIk1hbGUiLCAiRmVtYWxlIiwgIk90aGVyIiwgIk1pc3NpbmciKSksDQogIG4gPSBjKGxlbmd0aChnZW5kZXJbZ2VuZGVyID09ICJNYWxlIl0pLCBsZW5ndGgoZ2VuZGVyW2dlbmRlciA9PSAiRmVtYWxlIl0pLCBsZW5ndGgoZ2VuZGVyW2dlbmRlciA9PSAiT3RoZXIiXSksIGxlbmd0aChnZW5kZXJbZ2VuZGVyID09ICJNaXNzaW5nIl0pKSwNCiAgZnJlcSA9IGMocm91bmQoKGxlbmd0aChnZW5kZXJbZ2VuZGVyPT0iTWFsZSJdKS9sZW5ndGgoZ2VuZGVyKSAqMTAwKSwgZGlnaXRzPTEpLCByb3VuZCgobGVuZ3RoKGdlbmRlcltnZW5kZXI9PSJGZW1hbGUiXSkvbGVuZ3RoKGdlbmRlcikgKjEwMCksIGRpZ2l0cz0xKSwgcm91bmQoKGxlbmd0aChnZW5kZXJbZ2VuZGVyPT0iT3RoZXIiXSkvbGVuZ3RoKGdlbmRlcikqMTAwKSwgZGlnaXRzPTEpLCByb3VuZCgobGVuZ3RoKGdlbmRlcltnZW5kZXI9PSJNaXNzaW5nIl0pL2xlbmd0aChnZW5kZXIpKjEwMCksIGRpZ2l0cz0xKSkNCikNCg0KIyBwbG90DQpkZiAlPiUNCiAgbXV0YXRlKGdlbmRlciA9IGZjdF9yZW9yZGVyKGdlbmRlciwgLW4pKSAlPiUNCiAgICAgICAgICAgZ2dwbG90KGFlcyhnZW5kZXIsIG4sIGZpbGw9Z2VuZGVyKSkgKyANCiAgICAgICAgICAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCB3aWR0aD0wLjgpICsNCiAgICAgICAgICAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1wYXN0ZTAoZnJlcSwiJSIpKSwgdmp1c3Q9MS41LCBjb2xvdXI9IndoaXRlIikNCg0KYGBgDQoNCiMjIyBDbHViIDINCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICdmb2xkLWhpZGUnfQ0KZGYgPC0gY2x1YmRhdGFbWzJdXSAjIGdyYWIgY2x1YiANCg0KIyByZXRyaWV2ZSBub2RlLWF0dHJpYnV0ZSBnZW5kZXIgZnJvbSBvYmplY3QNCm1hbGUgPC0gZGYkbWFsZQ0KZmVtYWxlIDwtIGRmJGZlbWFsZQ0Kb3RoZXIgPC0gZGYkb3RoZXINCg0KIyBhcyBmYWN0b3INCmdlbmRlciA8LSBOQQ0KZ2VuZGVyIDwtIGlmZWxzZShtYWxlID09IDEsICJNYWxlIiwgZ2VuZGVyKQ0KZ2VuZGVyIDwtIGlmZWxzZShmZW1hbGUgPT0gMSwgIkZlbWFsZSIsIGdlbmRlcikNCmdlbmRlciA8LSBpZmVsc2Uob3RoZXIgPT0gMSwgIk90aGVyIiwgZ2VuZGVyKQ0KZ2VuZGVyIDwtIGlmZWxzZShpcy5uYShnZW5kZXIpLCAiTWlzc2luZyIsIGdlbmRlcikgIyBtaXNzaW5nIGNhdGVnb3J5DQoNCiMgbWFrZSBkYXRhZnJhbWUNCmRmIDwtIGRhdGEuZnJhbWUoDQogIGdlbmRlciA9IGFzLmZhY3RvcihjKCJNYWxlIiwgIkZlbWFsZSIsICJPdGhlciIsICJNaXNzaW5nIikpLA0KICBuID0gYyhsZW5ndGgoZ2VuZGVyW2dlbmRlciA9PSAiTWFsZSJdKSwgbGVuZ3RoKGdlbmRlcltnZW5kZXIgPT0gIkZlbWFsZSJdKSwgbGVuZ3RoKGdlbmRlcltnZW5kZXIgPT0gIk90aGVyIl0pLCBsZW5ndGgoZ2VuZGVyW2dlbmRlciA9PSAiTWlzc2luZyJdKSksDQogIGZyZXEgPSBjKHJvdW5kKChsZW5ndGgoZ2VuZGVyW2dlbmRlcj09Ik1hbGUiXSkvbGVuZ3RoKGdlbmRlcikgKjEwMCksIGRpZ2l0cz0xKSwgcm91bmQoKGxlbmd0aChnZW5kZXJbZ2VuZGVyPT0iRmVtYWxlIl0pL2xlbmd0aChnZW5kZXIpICoxMDApLCBkaWdpdHM9MSksIHJvdW5kKChsZW5ndGgoZ2VuZGVyW2dlbmRlcj09Ik90aGVyIl0pL2xlbmd0aChnZW5kZXIpKjEwMCksIGRpZ2l0cz0xKSwgcm91bmQoKGxlbmd0aChnZW5kZXJbZ2VuZGVyPT0iTWlzc2luZyJdKS9sZW5ndGgoZ2VuZGVyKSoxMDApLCBkaWdpdHM9MSkpDQopDQoNCiMgcGxvdA0KZGYgJT4lDQogIG11dGF0ZShnZW5kZXIgPSBmY3RfcmVvcmRlcihnZW5kZXIsIC1uKSkgJT4lDQogICAgICAgICAgIGdncGxvdChhZXMoZ2VuZGVyLCBuLCBmaWxsPWdlbmRlcikpICsgDQogICAgICAgICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5Iiwgd2lkdGg9MC44KSArDQogICAgICAgICAgIGdlb21fdGV4dChhZXMobGFiZWw9cGFzdGUwKGZyZXEsIiUiKSksIHZqdXN0PTEuNSwgY29sb3VyPSJ3aGl0ZSIpDQpgYGANCg0KIyMjIENsdWIgMw0KYGBge3IgY2xhc3Muc291cmNlID0gJ2ZvbGQtaGlkZSd9DQpkZiA8LSBjbHViZGF0YVtbM11dICMgZ3JhYiBjbHViIA0KDQojIHJldHJpZXZlIG5vZGUtYXR0cmlidXRlIGdlbmRlciBmcm9tIG9iamVjdA0KbWFsZSA8LSBkZiRtYWxlDQpmZW1hbGUgPC0gZGYkZmVtYWxlDQpvdGhlciA8LSBkZiRvdGhlcg0KDQojIGFzIGZhY3Rvcg0KZ2VuZGVyIDwtIE5BDQpnZW5kZXIgPC0gaWZlbHNlKG1hbGUgPT0gMSwgIk1hbGUiLCBnZW5kZXIpDQpnZW5kZXIgPC0gaWZlbHNlKGZlbWFsZSA9PSAxLCAiRmVtYWxlIiwgZ2VuZGVyKQ0KZ2VuZGVyIDwtIGlmZWxzZShvdGhlciA9PSAxLCAiT3RoZXIiLCBnZW5kZXIpDQpnZW5kZXIgPC0gaWZlbHNlKGlzLm5hKGdlbmRlciksICJNaXNzaW5nIiwgZ2VuZGVyKSAjIG1pc3NpbmcgY2F0ZWdvcnkNCg0KIyBtYWtlIGRhdGFmcmFtZQ0KZGYgPC0gZGF0YS5mcmFtZSgNCiAgZ2VuZGVyID0gYXMuZmFjdG9yKGMoIk1hbGUiLCAiRmVtYWxlIiwgIk90aGVyIiwgIk1pc3NpbmciKSksDQogIG4gPSBjKGxlbmd0aChnZW5kZXJbZ2VuZGVyID09ICJNYWxlIl0pLCBsZW5ndGgoZ2VuZGVyW2dlbmRlciA9PSAiRmVtYWxlIl0pLCBsZW5ndGgoZ2VuZGVyW2dlbmRlciA9PSAiT3RoZXIiXSksIGxlbmd0aChnZW5kZXJbZ2VuZGVyID09ICJNaXNzaW5nIl0pKSwNCiAgZnJlcSA9IGMocm91bmQoKGxlbmd0aChnZW5kZXJbZ2VuZGVyPT0iTWFsZSJdKS9sZW5ndGgoZ2VuZGVyKSAqMTAwKSwgZGlnaXRzPTEpLCByb3VuZCgobGVuZ3RoKGdlbmRlcltnZW5kZXI9PSJGZW1hbGUiXSkvbGVuZ3RoKGdlbmRlcikgKjEwMCksIGRpZ2l0cz0xKSwgcm91bmQoKGxlbmd0aChnZW5kZXJbZ2VuZGVyPT0iT3RoZXIiXSkvbGVuZ3RoKGdlbmRlcikqMTAwKSwgZGlnaXRzPTEpLCByb3VuZCgobGVuZ3RoKGdlbmRlcltnZW5kZXI9PSJNaXNzaW5nIl0pL2xlbmd0aChnZW5kZXIpKjEwMCksIGRpZ2l0cz0xKSkNCikNCg0KIyBwbG90DQpkZiAlPiUNCiAgbXV0YXRlKGdlbmRlciA9IGZjdF9yZW9yZGVyKGdlbmRlciwgLW4pKSAlPiUNCiAgICAgICAgICAgZ2dwbG90KGFlcyhnZW5kZXIsIG4sIGZpbGw9Z2VuZGVyKSkgKyANCiAgICAgICAgICAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCB3aWR0aD0wLjgpICsNCiAgICAgICAgICAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1wYXN0ZTAoZnJlcSwiJSIpKSwgdmp1c3Q9MS41LCBjb2xvdXI9IndoaXRlIikNCmBgYA0KDQojIyMgQ2x1YiA0DQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJ30NCmRmIDwtIGNsdWJkYXRhW1s0XV0gIyBncmFiIGNsdWIgDQoNCiMgcmV0cmlldmUgbm9kZS1hdHRyaWJ1dGUgZ2VuZGVyIGZyb20gb2JqZWN0DQptYWxlIDwtIGRmJG1hbGUNCmZlbWFsZSA8LSBkZiRmZW1hbGUNCm90aGVyIDwtIGRmJG90aGVyDQoNCiMgYXMgZmFjdG9yDQpnZW5kZXIgPC0gTkENCmdlbmRlciA8LSBpZmVsc2UobWFsZSA9PSAxLCAiTWFsZSIsIGdlbmRlcikNCmdlbmRlciA8LSBpZmVsc2UoZmVtYWxlID09IDEsICJGZW1hbGUiLCBnZW5kZXIpDQpnZW5kZXIgPC0gaWZlbHNlKG90aGVyID09IDEsICJPdGhlciIsIGdlbmRlcikNCmdlbmRlciA8LSBpZmVsc2UoaXMubmEoZ2VuZGVyKSwgIk1pc3NpbmciLCBnZW5kZXIpICMgbWlzc2luZyBjYXRlZ29yeQ0KDQojIG1ha2UgZGF0YWZyYW1lDQpkZiA8LSBkYXRhLmZyYW1lKA0KICBnZW5kZXIgPSBhcy5mYWN0b3IoYygiTWFsZSIsICJGZW1hbGUiLCAiT3RoZXIiLCAiTWlzc2luZyIpKSwNCiAgbiA9IGMobGVuZ3RoKGdlbmRlcltnZW5kZXIgPT0gIk1hbGUiXSksIGxlbmd0aChnZW5kZXJbZ2VuZGVyID09ICJGZW1hbGUiXSksIGxlbmd0aChnZW5kZXJbZ2VuZGVyID09ICJPdGhlciJdKSwgbGVuZ3RoKGdlbmRlcltnZW5kZXIgPT0gIk1pc3NpbmciXSkpLA0KICBmcmVxID0gYyhyb3VuZCgobGVuZ3RoKGdlbmRlcltnZW5kZXI9PSJNYWxlIl0pL2xlbmd0aChnZW5kZXIpICoxMDApLCBkaWdpdHM9MSksIHJvdW5kKChsZW5ndGgoZ2VuZGVyW2dlbmRlcj09IkZlbWFsZSJdKS9sZW5ndGgoZ2VuZGVyKSAqMTAwKSwgZGlnaXRzPTEpLCByb3VuZCgobGVuZ3RoKGdlbmRlcltnZW5kZXI9PSJPdGhlciJdKS9sZW5ndGgoZ2VuZGVyKSoxMDApLCBkaWdpdHM9MSksIHJvdW5kKChsZW5ndGgoZ2VuZGVyW2dlbmRlcj09Ik1pc3NpbmciXSkvbGVuZ3RoKGdlbmRlcikqMTAwKSwgZGlnaXRzPTEpKQ0KKQ0KDQojIHBsb3QNCmRmICU+JQ0KICBtdXRhdGUoZ2VuZGVyID0gZmN0X3Jlb3JkZXIoZ2VuZGVyLCAtbikpICU+JQ0KICAgICAgICAgICBnZ3Bsb3QoYWVzKGdlbmRlciwgbiwgZmlsbD1nZW5kZXIpKSArIA0KICAgICAgICAgICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHdpZHRoPTAuOCkgKw0KICAgICAgICAgICBnZW9tX3RleHQoYWVzKGxhYmVsPXBhc3RlMChmcmVxLCIlIikpLCB2anVzdD0xLjUsIGNvbG91cj0id2hpdGUiKQ0KYGBgDQoNCiMjIyBDbHViIDUNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICdmb2xkLWhpZGUnfQ0KZGYgPC0gY2x1YmRhdGFbWzVdXSAjIGdyYWIgY2x1YiANCg0KIyByZXRyaWV2ZSBub2RlLWF0dHJpYnV0ZSBnZW5kZXIgZnJvbSBvYmplY3QNCm1hbGUgPC0gZGYkbWFsZQ0KZmVtYWxlIDwtIGRmJGZlbWFsZQ0Kb3RoZXIgPC0gZGYkb3RoZXINCg0KIyBhcyBmYWN0b3INCmdlbmRlciA8LSBOQQ0KZ2VuZGVyIDwtIGlmZWxzZShtYWxlID09IDEsICJNYWxlIiwgZ2VuZGVyKQ0KZ2VuZGVyIDwtIGlmZWxzZShmZW1hbGUgPT0gMSwgIkZlbWFsZSIsIGdlbmRlcikNCmdlbmRlciA8LSBpZmVsc2Uob3RoZXIgPT0gMSwgIk90aGVyIiwgZ2VuZGVyKQ0KZ2VuZGVyIDwtIGlmZWxzZShpcy5uYShnZW5kZXIpLCAiTWlzc2luZyIsIGdlbmRlcikgIyBtaXNzaW5nIGNhdGVnb3J5DQoNCiMgbWFrZSBkYXRhZnJhbWUNCmRmIDwtIGRhdGEuZnJhbWUoDQogIGdlbmRlciA9IGFzLmZhY3RvcihjKCJNYWxlIiwgIkZlbWFsZSIsICJPdGhlciIsICJNaXNzaW5nIikpLA0KICBuID0gYyhsZW5ndGgoZ2VuZGVyW2dlbmRlciA9PSAiTWFsZSJdKSwgbGVuZ3RoKGdlbmRlcltnZW5kZXIgPT0gIkZlbWFsZSJdKSwgbGVuZ3RoKGdlbmRlcltnZW5kZXIgPT0gIk90aGVyIl0pLCBsZW5ndGgoZ2VuZGVyW2dlbmRlciA9PSAiTWlzc2luZyJdKSksDQogIGZyZXEgPSBjKHJvdW5kKChsZW5ndGgoZ2VuZGVyW2dlbmRlcj09Ik1hbGUiXSkvbGVuZ3RoKGdlbmRlcikgKjEwMCksIGRpZ2l0cz0xKSwgcm91bmQoKGxlbmd0aChnZW5kZXJbZ2VuZGVyPT0iRmVtYWxlIl0pL2xlbmd0aChnZW5kZXIpICoxMDApLCBkaWdpdHM9MSksIHJvdW5kKChsZW5ndGgoZ2VuZGVyW2dlbmRlcj09Ik90aGVyIl0pL2xlbmd0aChnZW5kZXIpKjEwMCksIGRpZ2l0cz0xKSwgcm91bmQoKGxlbmd0aChnZW5kZXJbZ2VuZGVyPT0iTWlzc2luZyJdKS9sZW5ndGgoZ2VuZGVyKSoxMDApLCBkaWdpdHM9MSkpDQopDQoNCiMgcGxvdA0KZGYgJT4lDQogIG11dGF0ZShnZW5kZXIgPSBmY3RfcmVvcmRlcihnZW5kZXIsIC1uKSkgJT4lDQogICAgICAgICAgIGdncGxvdChhZXMoZ2VuZGVyLCBuLCBmaWxsPWdlbmRlcikpICsgDQogICAgICAgICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5Iiwgd2lkdGg9MC44KSArDQogICAgICAgICAgIGdlb21fdGV4dChhZXMobGFiZWw9cGFzdGUwKGZyZXEsIiUiKSksIHZqdXN0PTEuNSwgY29sb3VyPSJ3aGl0ZSIpDQpgYGANCg0KIyMgey19DQoNCldlIGNhbiBzZWUgdGhhdCBpbiBhbGwgY2x1YnMgbWVuIGFyZSB0aGUgbWFqb3JpdHkuIA0KDQotLS0tDQoNCjxicj4NCg0KIyBHZW5kZXIgc2VncmVnYXRpb24NCg0KTGV0J3Mgbm93IGludmVzdGlnYXRlIHNlZ3JlZ2F0aW9uIGFsb25nIGdlbmRlciBpbiB0aGUga3Vkb3MgbmV0d29yay4NCg0KDQojIyB7LnRhYnNldCAudGFic2V0LWZhZGV9DQoNCkxldCdzIHN0YXJ0IHdpdGggZGVzY3JpYmluZyB0aGUgdG90YWwgZGVuc2l0eSBhbmQgaW50cmEtIChzYW1lIGdlbmRlcikgYW5kIGludGVyZ3JvdXAgKGRpZmZlcmVudCBnZW5kZXIpIGRlbnNpdGllcy4gV2UgYWxzbyBjYWxjdWxhdGUgdGhlIENvbGVtYW4gSG9tb3BoaWx5IGluZGV4IGZvciBnZW5kZXIsIHdoaWNoIHJlZmxlY3RzIGdlbmRlciBzZWdyZWdhdGlvbiB3aGlsZSB0YWtpbmcgaW50byBhY2NvdW50IHRoZSByZWxhdGl2ZSBncm91cCBzaXplIG9mIGdlbmRlciBjYXRlZ29yaWVzLiANCg0KIyMjIENsdWIgMQ0KYGBge3IgfQ0KZGYgPC0gY2x1YmRhdGFfcnNpZW5hX2ZyZXFbWzFdXSAjIGdyYWIgY2x1YiANCmRmMiA8LSBjbHViZGF0YVtbMV1dDQoNCmtuZXQgPC0gZGYkZGVwdmFycyRrdWRvbmV0ICMgdGFrZSBLdWRvIG5ldHdvcmsNCmtuZXQxIDwtIGtuZXRbLCwxXSAjIHRha2Ugd2F2ZSAxIG9ubHkgZm9yIG5vdw0KDQojIGZvciBzb21lIHJlYXNvbiBjb25zdHJ1Y3RpbmcgdGhlIGR5YWQtc2ltaWxhcml0eSBtYXRyaXggZm9yIGdlbmRlciB3aXRoIHRoZSByc2llbmEgb2JqZWN0IGRpZCBub3Qgd29yaywgc28gd2UgdXNlIHRoZSBjbHViZGF0YS5SRGF0YS4NCm1hbGUgPC0gZGYyJG1hbGUNCmZlbWFsZSA8LSBkZjIkZmVtYWxlDQpvdGhlciA8LSBkZjIkb3RoZXINCg0KZ2VuZGVyIDwtIE5BDQpnZW5kZXIgPC0gaWZlbHNlKG1hbGUgPT0gMSwgIk1hbGUiLCBnZW5kZXIpDQpnZW5kZXIgPC0gaWZlbHNlKGZlbWFsZSA9PSAxLCAiRmVtYWxlIiwgZ2VuZGVyKQ0KZ2VuZGVyIDwtIGlmZWxzZShvdGhlciA9PSAxLCAiT3RoZXIiLCBnZW5kZXIpDQpnZW5kZXIgPC0gaWZlbHNlKGlzLm5hKGdlbmRlciksICJNaXNzaW5nIiwgZ2VuZGVyKSAjIG1pc3NpbmcgY2F0ZWdvcnkNCg0KIyBjb25zdHJ1Y3QgZHlhZCBzaW1pbGFyaXR5IG1hdHJpeA0KZ2VuZGVyX20gPC0gZmhvbW9tYXQoZ2VuZGVyKQ0KDQoNCiMgbWFrZSBvYmplY3QgdG8gc3RvcmUgcmVzdWx0cw0KZGVzbWF0IDwtIG1hdHJpeChOQSwgbnJvdz00LCBuY29sPTEpDQoNCiMgdXNlIGZ1bmN0aW9ucw0KZGVzbWF0WzEsIDFdIDwtIGZkZW5zaXR5KGtuZXQxKQ0KZGVzbWF0WzIsIDFdIDwtIGZkZW5zaXR5aW50cmEoa25ldDEsIGdlbmRlcl9tKQ0KZGVzbWF0WzMsIDFdIDwtIGZkZW5zaXR5aW50ZXIoa25ldDEsIGdlbmRlcl9tKQ0KZGVzbWF0WzQsIDFdIDwtIGZzY29sbmV0KGtuZXQxLCBnZW5kZXIpDQoNCmNvbG5hbWVzKGRlc21hdCkgPC0gYygiS3Vkb3MgbmV0d29yayIpDQpyb3duYW1lcyhkZXNtYXQpIDwtIGMoInRvdGFsIGRlbnNpdHkiLCAic2FtZSBnZW5kZXIgZGVuc2l0eSIsICJkaWZmZXJlbnQgZ2VuZGVyIGRlbnNpdHkiLCAiQ29sZW1hbidzIGhvbW9waGlseSBpbmRleCIpDQoNCg0KDQprbml0cjo6a2FibGUoZGVzbWF0LCBkaWdpdHM9MywgImh0bWwiLCBjYXB0aW9uPSJHZW5kZXIgc2VncmVnYXRpb24gaW4gZnJpZW5kc2hpcCBhbmQga3VkbyBuZXR3b3JrIikgJT4lIA0KICBrYWJsZUV4dHJhOjprYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQ0KYGBgDQoNCiMjIyBDbHViIDINCmBgYHtyfQ0KZGYgPC0gY2x1YmRhdGFfcnNpZW5hX2ZyZXFbWzJdXSAjIGdyYWIgY2x1YiANCmRmMiA8LSBjbHViZGF0YVtbMl1dDQoNCmtuZXQgPC0gZGYkZGVwdmFycyRrdWRvbmV0ICMgdGFrZSBLdWRvIG5ldHdvcmsNCmtuZXQxIDwtIGtuZXRbLCwxXSAjIHRha2Ugd2F2ZSAxIG9ubHkgZm9yIG5vdw0KDQojIGZvciBzb21lIHJlYXNvbiBjb25zdHJ1Y3RpbmcgdGhlIGR5YWQtc2ltaWxhcml0eSBtYXRyaXggZm9yIGdlbmRlciB3aXRoIHRoZSByc2llbmEgb2JqZWN0IGRpZCBub3Qgd29yaywgc28gd2UgdXNlIHRoZSBjbHViZGF0YS5SRGF0YS4NCm1hbGUgPC0gZGYyJG1hbGUNCmZlbWFsZSA8LSBkZjIkZmVtYWxlDQpvdGhlciA8LSBkZjIkb3RoZXINCg0KZ2VuZGVyIDwtIE5BDQpnZW5kZXIgPC0gaWZlbHNlKG1hbGUgPT0gMSwgIk1hbGUiLCBnZW5kZXIpDQpnZW5kZXIgPC0gaWZlbHNlKGZlbWFsZSA9PSAxLCAiRmVtYWxlIiwgZ2VuZGVyKQ0KZ2VuZGVyIDwtIGlmZWxzZShvdGhlciA9PSAxLCAiT3RoZXIiLCBnZW5kZXIpDQpnZW5kZXIgPC0gaWZlbHNlKGlzLm5hKGdlbmRlciksICJNaXNzaW5nIiwgZ2VuZGVyKSAjIG1pc3NpbmcgY2F0ZWdvcnkNCg0KIyBjb25zdHJ1Y3QgZHlhZCBzaW1pbGFyaXR5IG1hdHJpeA0KZ2VuZGVyX20gPC0gZmhvbW9tYXQoZ2VuZGVyKQ0KDQoNCiMgbWFrZSBvYmplY3QgdG8gc3RvcmUgcmVzdWx0cw0KZGVzbWF0IDwtIG1hdHJpeChOQSwgbnJvdz00LCBuY29sPTEpDQoNCiMgdXNlIGZ1bmN0aW9ucw0KZGVzbWF0WzEsIDFdIDwtIGZkZW5zaXR5KGtuZXQxKQ0KZGVzbWF0WzIsIDFdIDwtIGZkZW5zaXR5aW50cmEoa25ldDEsIGdlbmRlcl9tKQ0KZGVzbWF0WzMsIDFdIDwtIGZkZW5zaXR5aW50ZXIoa25ldDEsIGdlbmRlcl9tKQ0KZGVzbWF0WzQsIDFdIDwtIGZzY29sbmV0KGtuZXQxLCBnZW5kZXIpDQoNCmNvbG5hbWVzKGRlc21hdCkgPC0gYygiS3Vkb3MgbmV0d29yayIpDQpyb3duYW1lcyhkZXNtYXQpIDwtIGMoInRvdGFsIGRlbnNpdHkiLCAic2FtZSBnZW5kZXIgZGVuc2l0eSIsICJkaWZmZXJlbnQgZ2VuZGVyIGRlbnNpdHkiLCAiQ29sZW1hbidzIGhvbW9waGlseSBpbmRleCIpDQoNCg0KDQprbml0cjo6a2FibGUoZGVzbWF0LCBkaWdpdHM9MywgImh0bWwiLCBjYXB0aW9uPSJHZW5kZXIgc2VncmVnYXRpb24gaW4gZnJpZW5kc2hpcCBhbmQga3VkbyBuZXR3b3JrIikgJT4lIA0KICBrYWJsZUV4dHJhOjprYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQ0KYGBgDQoNCiMjIyBDbHViIDMNCmBgYHtyfQ0KZGYgPC0gY2x1YmRhdGFfcnNpZW5hX2ZyZXFbWzNdXSAjIGdyYWIgY2x1YiANCmRmMiA8LSBjbHViZGF0YVtbM11dDQoNCmtuZXQgPC0gZGYkZGVwdmFycyRrdWRvbmV0ICMgdGFrZSBLdWRvIG5ldHdvcmsNCmtuZXQxIDwtIGtuZXRbLCwxXSAjIHRha2Ugd2F2ZSAxIG9ubHkgZm9yIG5vdw0KDQojIGZvciBzb21lIHJlYXNvbiBjb25zdHJ1Y3RpbmcgdGhlIGR5YWQtc2ltaWxhcml0eSBtYXRyaXggZm9yIGdlbmRlciB3aXRoIHRoZSByc2llbmEgb2JqZWN0IGRpZCBub3Qgd29yaywgc28gd2UgdXNlIHRoZSBjbHViZGF0YS5SRGF0YS4NCm1hbGUgPC0gZGYyJG1hbGUNCmZlbWFsZSA8LSBkZjIkZmVtYWxlDQpvdGhlciA8LSBkZjIkb3RoZXINCg0KZ2VuZGVyIDwtIE5BDQpnZW5kZXIgPC0gaWZlbHNlKG1hbGUgPT0gMSwgIk1hbGUiLCBnZW5kZXIpDQpnZW5kZXIgPC0gaWZlbHNlKGZlbWFsZSA9PSAxLCAiRmVtYWxlIiwgZ2VuZGVyKQ0KZ2VuZGVyIDwtIGlmZWxzZShvdGhlciA9PSAxLCAiT3RoZXIiLCBnZW5kZXIpDQpnZW5kZXIgPC0gaWZlbHNlKGlzLm5hKGdlbmRlciksICJNaXNzaW5nIiwgZ2VuZGVyKSAjIG1pc3NpbmcgY2F0ZWdvcnkNCg0KIyBjb25zdHJ1Y3QgZHlhZCBzaW1pbGFyaXR5IG1hdHJpeA0KZ2VuZGVyX20gPC0gZmhvbW9tYXQoZ2VuZGVyKQ0KDQoNCiMgbWFrZSBvYmplY3QgdG8gc3RvcmUgcmVzdWx0cw0KZGVzbWF0IDwtIG1hdHJpeChOQSwgbnJvdz00LCBuY29sPTEpDQoNCiMgdXNlIGZ1bmN0aW9ucw0KZGVzbWF0WzEsIDFdIDwtIGZkZW5zaXR5KGtuZXQxKQ0KZGVzbWF0WzIsIDFdIDwtIGZkZW5zaXR5aW50cmEoa25ldDEsIGdlbmRlcl9tKQ0KZGVzbWF0WzMsIDFdIDwtIGZkZW5zaXR5aW50ZXIoa25ldDEsIGdlbmRlcl9tKQ0KZGVzbWF0WzQsIDFdIDwtIGZzY29sbmV0KGtuZXQxLCBnZW5kZXIpDQoNCmNvbG5hbWVzKGRlc21hdCkgPC0gYygiS3Vkb3MgbmV0d29yayIpDQpyb3duYW1lcyhkZXNtYXQpIDwtIGMoInRvdGFsIGRlbnNpdHkiLCAic2FtZSBnZW5kZXIgZGVuc2l0eSIsICJkaWZmZXJlbnQgZ2VuZGVyIGRlbnNpdHkiLCAiQ29sZW1hbidzIGhvbW9waGlseSBpbmRleCIpDQoNCg0KDQprbml0cjo6a2FibGUoZGVzbWF0LCBkaWdpdHM9MywgImh0bWwiLCBjYXB0aW9uPSJHZW5kZXIgc2VncmVnYXRpb24gaW4gZnJpZW5kc2hpcCBhbmQga3VkbyBuZXR3b3JrIikgJT4lIA0KICBrYWJsZUV4dHJhOjprYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQ0KYGBgDQoNCiMjIyBDbHViIDQNCmBgYHtyfQ0KZGYgPC0gY2x1YmRhdGFfcnNpZW5hX2ZyZXFbWzRdXSAjIGdyYWIgY2x1YiANCmRmMiA8LSBjbHViZGF0YVtbNF1dDQoNCmtuZXQgPC0gZGYkZGVwdmFycyRrdWRvbmV0ICMgdGFrZSBLdWRvIG5ldHdvcmsNCmtuZXQxIDwtIGtuZXRbLCwxXSAjIHRha2Ugd2F2ZSAxIG9ubHkgZm9yIG5vdw0KDQojIGZvciBzb21lIHJlYXNvbiBjb25zdHJ1Y3RpbmcgdGhlIGR5YWQtc2ltaWxhcml0eSBtYXRyaXggZm9yIGdlbmRlciB3aXRoIHRoZSByc2llbmEgb2JqZWN0IGRpZCBub3Qgd29yaywgc28gd2UgdXNlIHRoZSBjbHViZGF0YS5SRGF0YS4NCm1hbGUgPC0gZGYyJG1hbGUNCmZlbWFsZSA8LSBkZjIkZmVtYWxlDQpvdGhlciA8LSBkZjIkb3RoZXINCg0KZ2VuZGVyIDwtIE5BDQpnZW5kZXIgPC0gaWZlbHNlKG1hbGUgPT0gMSwgIk1hbGUiLCBnZW5kZXIpDQpnZW5kZXIgPC0gaWZlbHNlKGZlbWFsZSA9PSAxLCAiRmVtYWxlIiwgZ2VuZGVyKQ0KZ2VuZGVyIDwtIGlmZWxzZShvdGhlciA9PSAxLCAiT3RoZXIiLCBnZW5kZXIpDQpnZW5kZXIgPC0gaWZlbHNlKGlzLm5hKGdlbmRlciksICJNaXNzaW5nIiwgZ2VuZGVyKSAjIG1pc3NpbmcgY2F0ZWdvcnkNCg0KIyBjb25zdHJ1Y3QgZHlhZCBzaW1pbGFyaXR5IG1hdHJpeA0KZ2VuZGVyX20gPC0gZmhvbW9tYXQoZ2VuZGVyKQ0KDQoNCiMgbWFrZSBvYmplY3QgdG8gc3RvcmUgcmVzdWx0cw0KZGVzbWF0IDwtIG1hdHJpeChOQSwgbnJvdz00LCBuY29sPTEpDQoNCiMgdXNlIGZ1bmN0aW9ucw0KZGVzbWF0WzEsIDFdIDwtIGZkZW5zaXR5KGtuZXQxKQ0KZGVzbWF0WzIsIDFdIDwtIGZkZW5zaXR5aW50cmEoa25ldDEsIGdlbmRlcl9tKQ0KZGVzbWF0WzMsIDFdIDwtIGZkZW5zaXR5aW50ZXIoa25ldDEsIGdlbmRlcl9tKQ0KZGVzbWF0WzQsIDFdIDwtIGZzY29sbmV0KGtuZXQxLCBnZW5kZXIpDQoNCmNvbG5hbWVzKGRlc21hdCkgPC0gYygiS3Vkb3MgbmV0d29yayIpDQpyb3duYW1lcyhkZXNtYXQpIDwtIGMoInRvdGFsIGRlbnNpdHkiLCAic2FtZSBnZW5kZXIgZGVuc2l0eSIsICJkaWZmZXJlbnQgZ2VuZGVyIGRlbnNpdHkiLCAiQ29sZW1hbidzIGhvbW9waGlseSBpbmRleCIpDQoNCg0KDQprbml0cjo6a2FibGUoZGVzbWF0LCBkaWdpdHM9MywgImh0bWwiLCBjYXB0aW9uPSJHZW5kZXIgc2VncmVnYXRpb24gaW4gZnJpZW5kc2hpcCBhbmQga3VkbyBuZXR3b3JrIikgJT4lIA0KICBrYWJsZUV4dHJhOjprYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQ0KYGBgDQoNCiMjIyBDbHViIDUNCmBgYHtyfQ0KZGYgPC0gY2x1YmRhdGFfcnNpZW5hX2ZyZXFbWzVdXSAjIGdyYWIgY2x1YiANCmRmMiA8LSBjbHViZGF0YVtbNV1dDQoNCmtuZXQgPC0gZGYkZGVwdmFycyRrdWRvbmV0ICMgdGFrZSBLdWRvIG5ldHdvcmsNCmtuZXQxIDwtIGtuZXRbLCwxXSAjIHRha2Ugd2F2ZSAxIG9ubHkgZm9yIG5vdw0KDQojIGZvciBzb21lIHJlYXNvbiBjb25zdHJ1Y3RpbmcgdGhlIGR5YWQtc2ltaWxhcml0eSBtYXRyaXggZm9yIGdlbmRlciB3aXRoIHRoZSByc2llbmEgb2JqZWN0IGRpZCBub3Qgd29yaywgc28gd2UgdXNlIHRoZSBjbHViZGF0YS5SRGF0YS4NCm1hbGUgPC0gZGYyJG1hbGUNCmZlbWFsZSA8LSBkZjIkZmVtYWxlDQpvdGhlciA8LSBkZjIkb3RoZXINCg0KZ2VuZGVyIDwtIE5BDQpnZW5kZXIgPC0gaWZlbHNlKG1hbGUgPT0gMSwgIk1hbGUiLCBnZW5kZXIpDQpnZW5kZXIgPC0gaWZlbHNlKGZlbWFsZSA9PSAxLCAiRmVtYWxlIiwgZ2VuZGVyKQ0KZ2VuZGVyIDwtIGlmZWxzZShvdGhlciA9PSAxLCAiT3RoZXIiLCBnZW5kZXIpDQpnZW5kZXIgPC0gaWZlbHNlKGlzLm5hKGdlbmRlciksICJNaXNzaW5nIiwgZ2VuZGVyKSAjIG1pc3NpbmcgY2F0ZWdvcnkNCg0KIyBjb25zdHJ1Y3QgZHlhZCBzaW1pbGFyaXR5IG1hdHJpeA0KZ2VuZGVyX20gPC0gZmhvbW9tYXQoZ2VuZGVyKQ0KDQoNCiMgbWFrZSBvYmplY3QgdG8gc3RvcmUgcmVzdWx0cw0KZGVzbWF0IDwtIG1hdHJpeChOQSwgbnJvdz00LCBuY29sPTEpDQoNCiMgdXNlIGZ1bmN0aW9ucw0KZGVzbWF0WzEsIDFdIDwtIGZkZW5zaXR5KGtuZXQxKQ0KZGVzbWF0WzIsIDFdIDwtIGZkZW5zaXR5aW50cmEoa25ldDEsIGdlbmRlcl9tKQ0KZGVzbWF0WzMsIDFdIDwtIGZkZW5zaXR5aW50ZXIoa25ldDEsIGdlbmRlcl9tKQ0KZGVzbWF0WzQsIDFdIDwtIGZzY29sbmV0KGtuZXQxLCBnZW5kZXIpDQoNCmNvbG5hbWVzKGRlc21hdCkgPC0gYygiS3Vkb3MgbmV0d29yayIpDQpyb3duYW1lcyhkZXNtYXQpIDwtIGMoInRvdGFsIGRlbnNpdHkiLCAic2FtZSBnZW5kZXIgZGVuc2l0eSIsICJkaWZmZXJlbnQgZ2VuZGVyIGRlbnNpdHkiLCAiQ29sZW1hbidzIGhvbW9waGlseSBpbmRleCIpDQoNCg0KDQprbml0cjo6a2FibGUoZGVzbWF0LCBkaWdpdHM9MywgImh0bWwiLCBjYXB0aW9uPSJHZW5kZXIgc2VncmVnYXRpb24gaW4gZnJpZW5kc2hpcCBhbmQga3VkbyBuZXR3b3JrIikgJT4lIA0KICBrYWJsZUV4dHJhOjprYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQ0KYGBgDQoNCiMjIHstfQ0KDQpEZW5zaXR5IGluIHRoZSBrdWRvcyBuZXR3b3JrIGlzIG5vdCBtdWNoIGhpZ2hlciB3aXRoaW4tZ2VuZGVyIHRoYW4gYmV0d2Vlbi1nZW5kZXJzLCB0aG91Z2ggdGhpcyByYXRpbyBkaWZmZXJzIHBlciBjbHViIChhbmQgaW4gb25lIGNsdWJzIGJldHdlZW4tZ2VuZGVyIGt1ZG9zIGFyZSBtb3JlIGNvbW1vbikuIA0KDQpDb2xlbWFuJ3MgSG9tb3BoaWx5IGluZGljZXMgdmFyeSBhY3Jvc3MgY2x1YnMuIEluIHNvbWUgY2x1YnMgdGhlcmUgaXMgc2xpZ2h0IGdlbmRlciBzZWdyZWdhdGlvbiAob3Igc2FtZS1nZW5kZXIgcHJlZmVyZW5jZSkgaW4ga3Vkb3MgdGllcyAodmFsdWVzIGhpZ2hlciB0aGFuIDAsIGJ1dCByYXRoZXIgY2xvc2UgdG8gMCksIHRob3VnaCBpbiBvdGhlcnMgbWVtYmVycyB0ZW5kIHRvIGdpdmUga3Vkb3MgdG8gb3RoZXJzIG91dHNpZGUgdGhlaXIgb3duIGdlbmRlciBncm91cCAodmFsdWVzIGxvd2VyIHRoYW4gMCkuIA0KDQotLS0tDQoNCjxicj4NCg0KIyBCZWhhdmlvcg0KDQoNCldlIHBsb3R0ZWQgdGhlIGRldmVsb3BtZW50IG9mIHRoZSBtZWFuIG9mIHJ1bm5pbmcgYXR0cmlidXRlcyAoRmlndXJlIDMgb2YgdGhlIG1hbnVzY3JpcHQpLiBUaGUgc2NyaXB0IHRvIHJlcGxpY2F0ZSB0aGlzIHBsb3QgY2FuIGJlIGZvdW5kIFtoZXJlXShodHRwczovL3JvYmZyYW5rZW4uZ2l0aHViLmlvL1N0cmF2YS9kZXNmaWcuaHRtbCkuDQoNCg0KLS0tLQ0KDQojIENvcnJlbGF0aW9uIGJldHdlZW4gZnJlcXVlbmN5IGFuZCB2b2x1bWUNCg0KTGV0J3MgcGxvdCB0aGUgcmVsYXRpb24gYmV0d2VlbiBmcmVxdWVuY3kgaW4gdGltZXMgcGVyIHdlZWsgYW5kIHZvbHVtZSBpbiBob3VycyBwZXIgd2Vlay4gV2UgYWxzbyBjYWxjdWxhdGUgS2VuZGFsbCdzIHRhdS1iLCBpLmUuIGEgbm9uLXBhcmFtZXRyaWMgbWVhc3VyZSBvZiBjb3JyZWxhdGlvbiBvbiByYW5rcyAoY2YuIEBraGFtaXMpLg0KDQojIyB7LnRhYnNldCAudGFic2V0LWZhZGV9DQoNCiMjIyBDbHViIDENCg0KYGBge3J9DQpkZiA8LSBjbHViZGF0YVtbMV1dICMgZ3JhYiBjbHViIA0KZGYgPC0gZGF0YS5mcmFtZSh4ID0gYXMubWF0cml4KGRmJHRpbWVfcnVuKSwgeSA9IGFzLm1hdHJpeChkZiRmcmVxX3J1bikpDQoNCmMgPC0gY29yLnRlc3QoZGYkeCwgZGYkeSwgbWV0aG9kPSJrZW5kYWxsIikNCg0KcGxvdChkZiwgdHlwZT0iYiIsIA0KICAgICBtYWluID0gIlJlbGF0aW9uIGJldHdlZW4gd2Vla2x5IHJ1bm5pbmcgdmFyaWFibGVzOiBmcmVxdWVuY3kgYW5kIChoYWxmKSBob3VycyIsDQogICAgIHN1YiA9ICJOb3RlOiBydW5uaW5nIGZyZXF1ZW5jeSBjYXBwZWQgd2l0aCBhIG1heGltdW0gb2YgNywgaG91cnMgd2l0aCBhIG1heGltdW0gb2YgNyIsDQogICAgIHhsYWIgPSAiSG91cnMgcGVyIHdlZWsiLCB5bGFiID0gInNlc3Npb25zIHBlciB3ZWVrIiwNCiAgICAgY29sPSJibHVlIiwgbHdkPWMoMSxyZXAoNiwxMDAwMCkpKSArIA0KICB0ZXh0KHggPSAxLjcsIHkgPSA1LCByb3VuZChjJGVzdGltYXRlLCBkaWdpdHM9MikpICsNCiAgdGV4dCh4ID0gLjcsIHkgPSA1LCAiS2VuZGFsbCdzIHRhdS1iID0iKQ0KDQpgYGANCg0KIyMjIENsdWIgMg0KDQpgYGB7cn0NCmRmIDwtIGNsdWJkYXRhW1syXV0gIyBncmFiIGNsdWIgDQpkZiA8LSBkYXRhLmZyYW1lKHggPSBhcy5tYXRyaXgoZGYkdGltZV9ydW4pLCB5ID0gYXMubWF0cml4KGRmJGZyZXFfcnVuKSkNCmMgPC0gY29yLnRlc3QoZGYkeCxkZiR5LG1ldGhvZD0ia2VuZGFsbCIpDQoNCnBsb3QoZGYsIHR5cGU9ImIiLCANCiAgICAgbWFpbiA9ICJSZWxhdGlvbiBiZXR3ZWVuIHdlZWtseSBydW5uaW5nIHZhcmlhYmxlczogZnJlcXVlbmN5IGFuZCBob3VycyIsDQogICAgIHN1YiA9ICJOb3RlOiBydW5uaW5nIGZyZXF1ZW5jeSBjYXBwZWQgd2l0aCBhIG1heGltdW0gb2YgNywgaG91cnMgd2l0aCBhIG1heGltdW0gb2YgNyIsDQogICAgIHhsYWIgPSAiaG91cnMgcGVyIHdlZWsiLCB5bGFiID0gInNlc3Npb25zIHBlciB3ZWVrIiwNCiAgICAgY29sPSJibHVlIiwgbHdkPWMoMSxyZXAoNiwxMDAwMCkpKSArIA0KICB0ZXh0KHggPSAxLjcsIHkgPSA1LCByb3VuZChjJGVzdGltYXRlLCBkaWdpdHM9MikpICsNCiAgdGV4dCh4ID0gLjcsIHkgPSA1LCAiS2VuZGFsbCdzIHRhdS1iID0iKQ0KYGBgDQoNCiMjIyBDbHViIDMNCg0KYGBge3J9DQpkZiA8LSBjbHViZGF0YVtbM11dICMgZ3JhYiBjbHViIA0KZGYgPC0gZGF0YS5mcmFtZSh4ID0gYXMubWF0cml4KGRmJHRpbWVfcnVuKSwgeSA9IGFzLm1hdHJpeChkZiRmcmVxX3J1bikpDQpjIDwtIGNvci50ZXN0KGRmJHgsZGYkeSxtZXRob2Q9ImtlbmRhbGwiKQ0KDQpwbG90KGRmLCB0eXBlPSJiIiwgDQogICAgIG1haW4gPSAiUmVsYXRpb24gYmV0d2VlbiB3ZWVrbHkgcnVubmluZyB2YXJpYWJsZXM6IGZyZXF1ZW5jeSBhbmQgaG91cnMiLA0KICAgICBzdWIgPSAiTm90ZTogcnVubmluZyBmcmVxdWVuY3kgY2FwcGVkIHdpdGggYSBtYXhpbXVtIG9mIDcsIGhvdXJzIHdpdGggYSBtYXhpbXVtIG9mIDciLA0KICAgICB4bGFiID0gImhvdXJzIHBlciB3ZWVrIiwgeWxhYiA9ICJzZXNzaW9ucyBwZXIgd2VlayIsDQogICAgIGNvbD0iYmx1ZSIsIGx3ZD1jKDEscmVwKDYsMTAwMDApKSkgKyANCiAgdGV4dCh4ID0gMS43LCB5ID0gNSwgcm91bmQoYyRlc3RpbWF0ZSwgZGlnaXRzPTIpKSArDQogIHRleHQoeCA9IC43LCB5ID0gNSwgIktlbmRhbGwncyB0YXUtYiA9IikNCmBgYA0KDQojIyMgQ2x1YiA0DQoNCmBgYHtyfQ0KZGYgPC0gY2x1YmRhdGFbWzRdXSAjIGdyYWIgY2x1YiANCmRmIDwtIGRhdGEuZnJhbWUoeCA9IGFzLm1hdHJpeChkZiR0aW1lX3J1biksIHkgPSBhcy5tYXRyaXgoZGYkZnJlcV9ydW4pKQ0KYyA8LSBjb3IudGVzdChkZiR4LGRmJHksbWV0aG9kPSJrZW5kYWxsIikNCg0KcGxvdChkZiwgdHlwZT0iYiIsIA0KICAgICBtYWluID0gIlJlbGF0aW9uIGJldHdlZW4gd2Vla2x5IHJ1bm5pbmcgdmFyaWFibGVzOiBmcmVxdWVuY3kgYW5kIGhvdXJzIiwNCiAgICAgc3ViID0gIk5vdGU6IHJ1bm5pbmcgZnJlcXVlbmN5IGNhcHBlZCB3aXRoIGEgbWF4aW11bSBvZiA3LCBob3VycyB3aXRoIGEgbWF4aW11bSBvZiA3IiwNCiAgICAgeGxhYiA9ICJob3VycyBwZXIgd2VlayIsIHlsYWIgPSAic2Vzc2lvbnMgcGVyIHdlZWsiLA0KICAgICBjb2w9ImJsdWUiLCBsd2Q9YygxLHJlcCg2LDEwMDAwKSkpICsgDQogIHRleHQoeCA9IDEuNSwgeSA9IDUsIHJvdW5kKGMkZXN0aW1hdGUsIGRpZ2l0cz0yKSkgKw0KICB0ZXh0KHggPSAuNywgeSA9IDUsICJLZW5kYWxsJ3MgdGF1LWIgPSIpDQpgYGANCg0KIyMjIENsdWIgNQ0KDQpgYGB7cn0NCmRmIDwtIGNsdWJkYXRhW1s1XV0gIyBncmFiIGNsdWIgDQpkZiA8LSBkYXRhLmZyYW1lKHggPSBhcy5tYXRyaXgoZGYkdGltZV9ydW4pLCB5ID0gYXMubWF0cml4KGRmJGZyZXFfcnVuKSkNCmMgPC0gY29yLnRlc3QoZGYkeCxkZiR5LG1ldGhvZD0ia2VuZGFsbCIpDQoNCnBsb3QoZGYsIHR5cGU9ImIiLCANCiAgICAgbWFpbiA9ICJSZWxhdGlvbiBiZXR3ZWVuIHdlZWtseSBydW5uaW5nIHZhcmlhYmxlczogZnJlcXVlbmN5IGFuZCBob3VycyIsDQogICAgIHN1YiA9ICJOb3RlOiBydW5uaW5nIGZyZXF1ZW5jeSBjYXBwZWQgd2l0aCBhIG1heGltdW0gb2YgNywgaG91cnMgd2l0aCBhIG1heGltdW0gb2YgNyIsDQogICAgIHhsYWIgPSAiaG91cnMgcGVyIHdlZWsiLCB5bGFiID0gInNlc3Npb25zIHBlciB3ZWVrIiwNCiAgICAgY29sPSJibHVlIiwgbHdkPWMoMSxyZXAoNiwxMDAwMCkpKSArIA0KICB0ZXh0KHggPSAyLCB5ID0gNywgcm91bmQoYyRlc3RpbWF0ZSwgZGlnaXRzPTIpKSArDQogIHRleHQoeCA9IC43LCB5ID0gNywgIktlbmRhbGwncyB0YXUtYiA9IikNCmBgYA0KDQojIyB7LX0NCg0KQWx0aG91Z2ggZnJlcXVlbmN5IGFuZCBkdXJhdGlvbiBhcmUgc3Ryb25nbHkgcmVsYXRlZCwgdGhlcmUgaXMgc29tZSBpbnZhcmlhbmNlLiANCg0KLS0tLQ0KDQo8YnI+DQoNCg0KDQojIE5ldHdvcmsgYXV0b2NvcnJlbGF0aW9uDQoNCiMjIHsudGFic2V0IC50YWJzZXQtZmFkZX0NCldlIGhhdmUgbm93IGNvdmVyZWQgdGhlIHNwb3J0IGFjdGl2aXR5IGxldmVscyBvZiBvdXIgY2x1Yi1hdGhsZXRlcywgYW5kIHRoZSBleHRlbnQgdG8gd2hpY2gga3Vkb3MtYXNzb2NpYXRpb25zIGFyZSBzZWdyZWdhdGVkIGFsb25nIGdlbmRlci4gTGFzdCwgd2Ugd2lsbCBleHBsb3JlIGlmIGt1ZG9zLXRpZXMgYXJlIGFsc28gc2VncmVnYXRlZCBhbG9uZyBhY3Rpdml0eSBsZXZlbHMuIE9yIGluIG90aGVyIHdvcmRzOiBkbyBwZW9wbGUgd2l0aCBzaW1pbGFyIGFjdGl2aXR5IGxldmVscyB0ZW5kIHRvIHNvY2lhbGl6ZSB0byBhIGdyZWF0ZXIgZXh0ZW50IC0gYnkgZXhjaGFuZ2luZyBrdWRvcyAtIGV2ZW4gd2hlbiB0YWtpbmcgaW50byBhY2NvdW50IHRoZSBvcHBvcnR1bml0eSBzdHJ1Y3R1cmVzIGZvciAnaW50ZXJhY3RpbmcnIHdpdGggKGRpcylzaW1pbGFyIG90aGVycz8gDQoNCldlIHVzZSBNb3JhbidzIEkgc3BhdGlhbCBhdXRvY29ycmVsYXRpb24gbWVhc3VyZSBmb3IgdGhpcywgd2hpY2ggaXMgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIGJlaGF2aW9yYWwgc2NvcmUgb2YgYWN0b3IgKmkqIGFuZCB0aGUgKHRvdGFsL21lYW4pIGJlaGF2aW9yYWwgc2NvcmUgb2YgYWx0ZXJzICpqKiB0byB3aG9tICppKiBpcyBjb25uZWN0ZWQgKipkaXJlY3RseSoqLiBXZSBhbHNvIGNhbGN1bGF0ZWQgTW9yYW4ncyBJIGJ5IGluY2x1ZGluZyB0aGUgYmVoYXZpb3JhbCBzY29yZXMgb2YgdGhlIGFjdG9ycyAqaCogdG8gd2hvbSAqaSogaXMgaW5kaXJlY3RseSB0aWVkLCBhbmQgdXNlZCB0aGUgbmVnYXRpdmUgZXhwb25lbnRpYWwgZnVuY3Rpb24gYXMgZGVzY3JpYmVkIGJ5IEBjaGVuMjAxMyBhcyBhIGRpc3RhbmNlLWRlY2F5IGZ1bmN0aW9uIGZvciBhc3NpZ25pbmcgd2VpZ2h0cy4NCg0KIyMjIENsdWIgMQ0KYGBge3J9DQpkZiA8LSBjbHViZGF0YVtbMV1dICMgZ3JhYiBjbHViIA0KDQojIGdldCBiZWhhdmlvcmFsIGRhdGENCiMgYXQgdGltZSB0PTENCnQ9MQ0KZnJlcTEgPC0gZGYkZnJlcV9ydW5bLCx0XSAjIHJ1bm5pbmcgZnJlcXVlbmNpZXMgd2F2ZSAxDQp2b2wxIDwtIGRmJHRpbWVfcnVuWywsdF0gIyBydW5uaW5nIHZvbHVtZSB3YXZlIDENCg0KIyBleGNsdWRlIE5BcyANCm5hIDwtIHdoaWNoKGlzLm5hKGZyZXExKSkNCmZyZXExIDwtIGZyZXExWy1uYV0NCnZvbDEgPC0gdm9sMVstbmFdDQoNCiMgZ2V0IGt1ZG9zIG5ldA0Ka25ldDEgPC0gZGYka3Vkb1ssLHRdDQojIGV4Y2x1ZGUgTkFzDQprbmV0MSA8LSBrbmV0MVstbmEsLW5hXQ0KDQojIGFzIG5ldHdvcmsgb2JqZWN0DQprbmV0MSA8LSBuZXR3b3JrOjphcy5uZXR3b3JrKGtuZXQxKQ0KDQojIHdlIGluY2x1ZGUgZ2VvZGlzdGFuY2VzOiBzaG9ydGVzdCBwYXRoIGxlbmd0aHMgZnJvbSBpIHRvIGoNCmdlb2Rpc3RhbmNlcyA8LSBzbmE6Omdlb2Rpc3Qoa25ldDEsIGNvdW50LnBhdGhzPVQpDQpnZW9kaXN0YW5jZXMgPC0gZ2VvZGlzdGFuY2VzJGdkaXN0IA0KDQojIHNldCB0aGUgZGlzdGFuY2UgJ3RvIHlvdXJzZWxmJyB0byAnSW5mJw0KZGlhZyhnZW9kaXN0YW5jZXMpIDwtIEluZg0KDQojIGZpcnN0IGNhbGN1bGF0ZSBNb3JhbidzIGkgZm9yIGFsdGVycyBhdCBkaXN0YW5jZSAxLg0Kd2VpZ2h0czEgPC0gZ2VvZGlzdGFuY2VzID09IDENCg0KIyBhbmQgdXNlIHRoZSBuZWdhdGl2ZSBleHBvbmVudGlhbCBkaXN0YW5jZS1kZWNheSBmdW5jdGlvbg0Kd2VpZ2h0czIgPC0gZXhwKC1nZW9kaXN0YW5jZXMpDQoNCiMgY2FsY3VsYXRlIE1vcmFuJ3MgSQ0KIyBmb3IgZGlzdGFuY2UtMSBhbmQgd2l0aCBkaXN0YW5jZSBkZWNheSwgaW4gdGhlIGt1ZG9zIG5ldHdvcmssIGZvciBmcmVxdWVuY3kgYW5kIHZvbHVtZSAgcmVzcGVjdGl2ZWx5DQojIHdlIGRvIG5vdCByb3cgc3RhbmRhcmRpemUhDQpNZnJlcSA8LSBmTW9yYW4uSShmcmVxMSwgc2NhbGVkID0gRkFMU0UsIHdlaWdodCA9IHdlaWdodHMxLCBuYS5ybSA9IFRSVUUsIHJvd3N0YW5kYXJkaXplID0gRkFMU0UpDQpNZnJlcV9kaXN0ZCA8LSBmTW9yYW4uSShmcmVxMSwgc2NhbGVkID0gRkFMU0UsIHdlaWdodCA9IHdlaWdodHMyLCBuYS5ybSA9IFRSVUUsIHJvd3N0YW5kYXJkaXplID0gRkFMU0UpDQoNCk12b2wgPC0gZk1vcmFuLkkodm9sMSwgc2NhbGVkID0gRkFMU0UsIHdlaWdodCA9IHdlaWdodHMxLCBuYS5ybSA9IFRSVUUsIHJvd3N0YW5kYXJkaXplID0gRkFMU0UpDQpNdm9sX2Rpc3RkIDwtIGZNb3Jhbi5JKHZvbDEsIHNjYWxlZCA9IEZBTFNFLCB3ZWlnaHQgPSB3ZWlnaHRzMiwgbmEucm0gPSBUUlVFLCByb3dzdGFuZGFyZGl6ZSA9IEZBTFNFKQ0KDQojIG1ha2Ugb2JqZWN0IHRvIHN0b3JlIHJlc3VsdHMNCiMgMS4gZnJlcXVlbmN5DQpmX21hdCA8LSBtYXRyaXgoTkEsIG5yb3c9MiwgbmNvbD00KQ0KDQpmX21hdFsxLDFdIDwtIE1mcmVxJG9ic2VydmVkDQpmX21hdFsxLDJdIDwtIE1mcmVxJGV4cGVjdGVkDQpmX21hdFsxLDNdIDwtIE1mcmVxJHNkDQpmX21hdFsxLDRdIDwtIE1mcmVxJHAudmFsdWUNCmZfbWF0WzIsMV0gPC0gTWZyZXFfZGlzdGQkb2JzZXJ2ZWQNCmZfbWF0WzIsMl0gPC0gTWZyZXFfZGlzdGQkZXhwZWN0ZWQNCmZfbWF0WzIsM10gPC0gTWZyZXFfZGlzdGQkc2QNCmZfbWF0WzIsNF0gPC0gTWZyZXFfZGlzdGQkcC52YWx1ZQ0KDQojIDIuIHZvbHVtZQ0Kdl9tYXQgPC0gbWF0cml4KE5BLCBucm93PTIsIG5jb2w9NCkNCnZfbWF0WzEsMV0gPC0gTXZvbCRvYnNlcnZlZA0Kdl9tYXRbMSwyXSA8LSBNdm9sJGV4cGVjdGVkDQp2X21hdFsxLDNdIDwtIE12b2wkc2QNCnZfbWF0WzEsNF0gPC0gTXZvbCRwLnZhbHVlDQp2X21hdFsyLDFdIDwtIE12b2xfZGlzdGQkb2JzZXJ2ZWQNCnZfbWF0WzIsMl0gPC0gTXZvbF9kaXN0ZCRleHBlY3RlZA0Kdl9tYXRbMiwzXSA8LSBNdm9sX2Rpc3RkJHNkDQp2X21hdFsyLDRdIDwtIE12b2xfZGlzdGQkcC52YWx1ZQ0KDQpjb2xuYW1lcyhmX21hdCkgPC0gY29sbmFtZXModl9tYXQpIDwtIGMoIm9ic2VydmVkIiwgImV4cGVjdGVkIiwgInNkIiwgInAtdmFsdWUiKQ0Kcm93bmFtZXMoZl9tYXQpIDwtIHJvd25hbWVzKHZfbWF0KSA8LSBjKCJkaXJlY3Qga3Vkb3MgdGllcyIsICJkaXJlY3QgYW5kIGluZGlyZWN0IGt1ZG9zIHRpZXMgKGRpc3RhbmNlLWRlY2F5KSIpDQoNCmtuaXRyOjprYWJsZShmX21hdCwgZGlnaXRzPTIsICJodG1sIiwgY2FwdGlvbj0iTW9yYW4ncyBJIHN0YXRpc3RpYyBmb3Igc3BhdGlhbCBhdXRvY29ycmVsYXRpb24gYmFzZWQgb24gZ2VvZGlzdGFuY2VzIGFuZCB3ZWVrbHkgcnVubmluZyBmcmVxdWVuY3kiKSAlPiUgDQogIGthYmxlRXh0cmE6OmthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpDQoNCmtuaXRyOjprYWJsZSh2X21hdCwgZGlnaXRzPTIsICJodG1sIiwgY2FwdGlvbj0iTW9yYW4ncyBJIHN0YXRpc3RpYyBmb3Igc3BhdGlhbCBhdXRvY29ycmVsYXRpb24gYmFzZWQgb24gZ2VvZGlzdGFuY2VzIGFuZCBtb250aGx5IHJ1bm5pbmcgdm9sdW1lIikgJT4lIA0KICBrYWJsZUV4dHJhOjprYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQ0KYGBgDQoNCiMjIyBDbHViIDINCmBgYHtyfQ0KZGYgPC0gY2x1YmRhdGFbWzJdXSAjIGdyYWIgY2x1YiANCg0KIyBnZXQgYmVoYXZpb3JhbCBkYXRhDQojIGF0IHRpbWUgdD0xDQp0PTENCmZyZXExIDwtIGRmJGZyZXFfcnVuWywsdF0gIyBydW5uaW5nIGZyZXF1ZW5jaWVzIHdhdmUgMQ0Kdm9sMSA8LSBkZiR0aW1lX3J1blssLHRdICMgcnVubmluZyB2b2x1bWUgd2F2ZSAxDQoNCiMgZXhjbHVkZSBOQXMgDQpuYSA8LSB3aGljaChpcy5uYShmcmVxMSkpDQpmcmVxMSA8LSBmcmVxMVstbmFdDQp2b2wxIDwtIHZvbDFbLW5hXQ0KDQojIGdldCBrdWRvcyBuZXQNCmtuZXQxIDwtIGRmJGt1ZG9bLCx0XQ0KIyBleGNsdWRlIE5Bcw0Ka25ldDEgPC0ga25ldDFbLW5hLC1uYV0NCg0KIyBhcyBuZXR3b3JrIG9iamVjdA0Ka25ldDEgPC0gbmV0d29yazo6YXMubmV0d29yayhrbmV0MSkNCg0KIyB3ZSBpbmNsdWRlIGdlb2Rpc3RhbmNlczogc2hvcnRlc3QgcGF0aCBsZW5ndGhzIGZyb20gaSB0byBqDQpnZW9kaXN0YW5jZXMgPC0gc25hOjpnZW9kaXN0KGtuZXQxLCBjb3VudC5wYXRocz1UKQ0KZ2VvZGlzdGFuY2VzIDwtIGdlb2Rpc3RhbmNlcyRnZGlzdCANCg0KIyBzZXQgdGhlIGRpc3RhbmNlICd0byB5b3Vyc2VsZicgdG8gJ0luZicNCmRpYWcoZ2VvZGlzdGFuY2VzKSA8LSBJbmYNCg0KIyBmaXJzdCBjYWxjdWxhdGUgTW9yYW4ncyBpIGZvciBhbHRlcnMgYXQgZGlzdGFuY2UgMS4NCndlaWdodHMxIDwtIGdlb2Rpc3RhbmNlcyA9PSAxDQoNCiMgYW5kIHVzZSB0aGUgbmVnYXRpdmUgZXhwb25lbnRpYWwgZGlzdGFuY2UtZGVjYXkgZnVuY3Rpb24NCndlaWdodHMyIDwtIGV4cCgtZ2VvZGlzdGFuY2VzKQ0KDQojIGNhbGN1bGF0ZSBNb3JhbidzIEkNCiMgZm9yIGRpc3RhbmNlLTEgYW5kIHdpdGggZGlzdGFuY2UgZGVjYXksIGluIHRoZSBrdWRvcyBuZXR3b3JrLCBmb3IgZnJlcXVlbmN5IGFuZCB2b2x1bWUgIHJlc3BlY3RpdmVseQ0KIyB3ZSBkbyBub3Qgcm93IHN0YW5kYXJkaXplIQ0KTWZyZXEgPC0gZk1vcmFuLkkoZnJlcTEsIHNjYWxlZCA9IEZBTFNFLCB3ZWlnaHQgPSB3ZWlnaHRzMSwgbmEucm0gPSBUUlVFLCByb3dzdGFuZGFyZGl6ZSA9IEZBTFNFKQ0KTWZyZXFfZGlzdGQgPC0gZk1vcmFuLkkoZnJlcTEsIHNjYWxlZCA9IEZBTFNFLCB3ZWlnaHQgPSB3ZWlnaHRzMiwgbmEucm0gPSBUUlVFLCByb3dzdGFuZGFyZGl6ZSA9IEZBTFNFKQ0KDQpNdm9sIDwtIGZNb3Jhbi5JKHZvbDEsIHNjYWxlZCA9IEZBTFNFLCB3ZWlnaHQgPSB3ZWlnaHRzMSwgbmEucm0gPSBUUlVFLCByb3dzdGFuZGFyZGl6ZSA9IEZBTFNFKQ0KTXZvbF9kaXN0ZCA8LSBmTW9yYW4uSSh2b2wxLCBzY2FsZWQgPSBGQUxTRSwgd2VpZ2h0ID0gd2VpZ2h0czIsIG5hLnJtID0gVFJVRSwgcm93c3RhbmRhcmRpemUgPSBGQUxTRSkNCg0KIyBtYWtlIG9iamVjdCB0byBzdG9yZSByZXN1bHRzDQojIDEuIGZyZXF1ZW5jeQ0KZl9tYXQgPC0gbWF0cml4KE5BLCBucm93PTIsIG5jb2w9NCkNCg0KZl9tYXRbMSwxXSA8LSBNZnJlcSRvYnNlcnZlZA0KZl9tYXRbMSwyXSA8LSBNZnJlcSRleHBlY3RlZA0KZl9tYXRbMSwzXSA8LSBNZnJlcSRzZA0KZl9tYXRbMSw0XSA8LSBNZnJlcSRwLnZhbHVlDQpmX21hdFsyLDFdIDwtIE1mcmVxX2Rpc3RkJG9ic2VydmVkDQpmX21hdFsyLDJdIDwtIE1mcmVxX2Rpc3RkJGV4cGVjdGVkDQpmX21hdFsyLDNdIDwtIE1mcmVxX2Rpc3RkJHNkDQpmX21hdFsyLDRdIDwtIE1mcmVxX2Rpc3RkJHAudmFsdWUNCg0KIyAyLiB2b2x1bWUNCnZfbWF0IDwtIG1hdHJpeChOQSwgbnJvdz0yLCBuY29sPTQpDQp2X21hdFsxLDFdIDwtIE12b2wkb2JzZXJ2ZWQNCnZfbWF0WzEsMl0gPC0gTXZvbCRleHBlY3RlZA0Kdl9tYXRbMSwzXSA8LSBNdm9sJHNkDQp2X21hdFsxLDRdIDwtIE12b2wkcC52YWx1ZQ0Kdl9tYXRbMiwxXSA8LSBNdm9sX2Rpc3RkJG9ic2VydmVkDQp2X21hdFsyLDJdIDwtIE12b2xfZGlzdGQkZXhwZWN0ZWQNCnZfbWF0WzIsM10gPC0gTXZvbF9kaXN0ZCRzZA0Kdl9tYXRbMiw0XSA8LSBNdm9sX2Rpc3RkJHAudmFsdWUNCg0KY29sbmFtZXMoZl9tYXQpIDwtIGNvbG5hbWVzKHZfbWF0KSA8LSBjKCJvYnNlcnZlZCIsICJleHBlY3RlZCIsICJzZCIsICJwLXZhbHVlIikNCnJvd25hbWVzKGZfbWF0KSA8LSByb3duYW1lcyh2X21hdCkgPC0gYygiZGlyZWN0IGt1ZG9zIHRpZXMiLCAiZGlyZWN0IGFuZCBpbmRpcmVjdCBrdWRvcyB0aWVzIChkaXN0YW5jZS1kZWNheSkiKQ0KDQprbml0cjo6a2FibGUoZl9tYXQsIGRpZ2l0cz0yLCAiaHRtbCIsIGNhcHRpb249Ik1vcmFuJ3MgSSBzdGF0aXN0aWMgZm9yIHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uIGJhc2VkIG9uIGdlb2Rpc3RhbmNlcyBhbmQgd2Vla2x5IHJ1bm5pbmcgZnJlcXVlbmN5IikgJT4lIA0KICBrYWJsZUV4dHJhOjprYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQ0KDQprbml0cjo6a2FibGUodl9tYXQsIGRpZ2l0cz0yLCAiaHRtbCIsIGNhcHRpb249Ik1vcmFuJ3MgSSBzdGF0aXN0aWMgZm9yIHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uIGJhc2VkIG9uIGdlb2Rpc3RhbmNlcyBhbmQgbW9udGhseSBydW5uaW5nIHZvbHVtZSIpICU+JSANCiAga2FibGVFeHRyYTo6a2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSkNCmBgYA0KDQojIyMgQ2x1YiAzDQpgYGB7cn0NCmRmIDwtIGNsdWJkYXRhW1szXV0gIyBncmFiIGNsdWIgDQoNCiMgZ2V0IGJlaGF2aW9yYWwgZGF0YQ0KIyBhdCB0aW1lIHQ9MQ0KdD0xDQpmcmVxMSA8LSBkZiRmcmVxX3J1blssLHRdICMgcnVubmluZyBmcmVxdWVuY2llcyB3YXZlIDENCnZvbDEgPC0gZGYkdGltZV9ydW5bLCx0XSAjIHJ1bm5pbmcgdm9sdW1lIHdhdmUgMQ0KDQojIGV4Y2x1ZGUgTkFzIA0KbmEgPC0gd2hpY2goaXMubmEoZnJlcTEpKQ0KZnJlcTEgPC0gZnJlcTFbLW5hXQ0Kdm9sMSA8LSB2b2wxWy1uYV0NCg0KIyBnZXQga3Vkb3MgbmV0DQprbmV0MSA8LSBkZiRrdWRvWywsdF0NCiMgZXhjbHVkZSBOQXMNCmtuZXQxIDwtIGtuZXQxWy1uYSwtbmFdDQoNCiMgYXMgbmV0d29yayBvYmplY3QNCmtuZXQxIDwtIG5ldHdvcms6OmFzLm5ldHdvcmsoa25ldDEpDQoNCiMgd2UgaW5jbHVkZSBnZW9kaXN0YW5jZXM6IHNob3J0ZXN0IHBhdGggbGVuZ3RocyBmcm9tIGkgdG8gag0KZ2VvZGlzdGFuY2VzIDwtIHNuYTo6Z2VvZGlzdChrbmV0MSwgY291bnQucGF0aHM9VCkNCmdlb2Rpc3RhbmNlcyA8LSBnZW9kaXN0YW5jZXMkZ2Rpc3QgDQoNCiMgc2V0IHRoZSBkaXN0YW5jZSAndG8geW91cnNlbGYnIHRvICdJbmYnDQpkaWFnKGdlb2Rpc3RhbmNlcykgPC0gSW5mDQoNCiMgZmlyc3QgY2FsY3VsYXRlIE1vcmFuJ3MgaSBmb3IgYWx0ZXJzIGF0IGRpc3RhbmNlIDEuDQp3ZWlnaHRzMSA8LSBnZW9kaXN0YW5jZXMgPT0gMQ0KDQojIGFuZCB1c2UgdGhlIG5lZ2F0aXZlIGV4cG9uZW50aWFsIGRpc3RhbmNlLWRlY2F5IGZ1bmN0aW9uDQp3ZWlnaHRzMiA8LSBleHAoLWdlb2Rpc3RhbmNlcykNCg0KIyBjYWxjdWxhdGUgTW9yYW4ncyBJDQojIGZvciBkaXN0YW5jZS0xIGFuZCB3aXRoIGRpc3RhbmNlIGRlY2F5LCBpbiB0aGUga3Vkb3MgbmV0d29yaywgZm9yIGZyZXF1ZW5jeSBhbmQgdm9sdW1lICByZXNwZWN0aXZlbHkNCiMgd2UgZG8gbm90IHJvdyBzdGFuZGFyZGl6ZSENCk1mcmVxIDwtIGZNb3Jhbi5JKGZyZXExLCBzY2FsZWQgPSBGQUxTRSwgd2VpZ2h0ID0gd2VpZ2h0czEsIG5hLnJtID0gVFJVRSwgcm93c3RhbmRhcmRpemUgPSBGQUxTRSkNCk1mcmVxX2Rpc3RkIDwtIGZNb3Jhbi5JKGZyZXExLCBzY2FsZWQgPSBGQUxTRSwgd2VpZ2h0ID0gd2VpZ2h0czIsIG5hLnJtID0gVFJVRSwgcm93c3RhbmRhcmRpemUgPSBGQUxTRSkNCg0KTXZvbCA8LSBmTW9yYW4uSSh2b2wxLCBzY2FsZWQgPSBGQUxTRSwgd2VpZ2h0ID0gd2VpZ2h0czEsIG5hLnJtID0gVFJVRSwgcm93c3RhbmRhcmRpemUgPSBGQUxTRSkNCk12b2xfZGlzdGQgPC0gZk1vcmFuLkkodm9sMSwgc2NhbGVkID0gRkFMU0UsIHdlaWdodCA9IHdlaWdodHMyLCBuYS5ybSA9IFRSVUUsIHJvd3N0YW5kYXJkaXplID0gRkFMU0UpDQoNCiMgbWFrZSBvYmplY3QgdG8gc3RvcmUgcmVzdWx0cw0KIyAxLiBmcmVxdWVuY3kNCmZfbWF0IDwtIG1hdHJpeChOQSwgbnJvdz0yLCBuY29sPTQpDQoNCmZfbWF0WzEsMV0gPC0gTWZyZXEkb2JzZXJ2ZWQNCmZfbWF0WzEsMl0gPC0gTWZyZXEkZXhwZWN0ZWQNCmZfbWF0WzEsM10gPC0gTWZyZXEkc2QNCmZfbWF0WzEsNF0gPC0gTWZyZXEkcC52YWx1ZQ0KZl9tYXRbMiwxXSA8LSBNZnJlcV9kaXN0ZCRvYnNlcnZlZA0KZl9tYXRbMiwyXSA8LSBNZnJlcV9kaXN0ZCRleHBlY3RlZA0KZl9tYXRbMiwzXSA8LSBNZnJlcV9kaXN0ZCRzZA0KZl9tYXRbMiw0XSA8LSBNZnJlcV9kaXN0ZCRwLnZhbHVlDQoNCiMgMi4gdm9sdW1lDQp2X21hdCA8LSBtYXRyaXgoTkEsIG5yb3c9MiwgbmNvbD00KQ0Kdl9tYXRbMSwxXSA8LSBNdm9sJG9ic2VydmVkDQp2X21hdFsxLDJdIDwtIE12b2wkZXhwZWN0ZWQNCnZfbWF0WzEsM10gPC0gTXZvbCRzZA0Kdl9tYXRbMSw0XSA8LSBNdm9sJHAudmFsdWUNCnZfbWF0WzIsMV0gPC0gTXZvbF9kaXN0ZCRvYnNlcnZlZA0Kdl9tYXRbMiwyXSA8LSBNdm9sX2Rpc3RkJGV4cGVjdGVkDQp2X21hdFsyLDNdIDwtIE12b2xfZGlzdGQkc2QNCnZfbWF0WzIsNF0gPC0gTXZvbF9kaXN0ZCRwLnZhbHVlDQoNCmNvbG5hbWVzKGZfbWF0KSA8LSBjb2xuYW1lcyh2X21hdCkgPC0gYygib2JzZXJ2ZWQiLCAiZXhwZWN0ZWQiLCAic2QiLCAicC12YWx1ZSIpDQpyb3duYW1lcyhmX21hdCkgPC0gcm93bmFtZXModl9tYXQpIDwtIGMoImRpcmVjdCBrdWRvcyB0aWVzIiwgImRpcmVjdCBhbmQgaW5kaXJlY3Qga3Vkb3MgdGllcyAoZGlzdGFuY2UtZGVjYXkpIikNCg0Ka25pdHI6OmthYmxlKGZfbWF0LCBkaWdpdHM9MiwgImh0bWwiLCBjYXB0aW9uPSJNb3JhbidzIEkgc3RhdGlzdGljIGZvciBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiBiYXNlZCBvbiBnZW9kaXN0YW5jZXMgYW5kIHdlZWtseSBydW5uaW5nIGZyZXF1ZW5jeSIpICU+JSANCiAga2FibGVFeHRyYTo6a2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSkNCg0Ka25pdHI6OmthYmxlKHZfbWF0LCBkaWdpdHM9MiwgImh0bWwiLCBjYXB0aW9uPSJNb3JhbidzIEkgc3RhdGlzdGljIGZvciBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiBiYXNlZCBvbiBnZW9kaXN0YW5jZXMgYW5kIG1vbnRobHkgcnVubmluZyB2b2x1bWUiKSAlPiUgDQogIGthYmxlRXh0cmE6OmthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpDQpgYGANCg0KDQojIyMgQ2x1YiA0DQpgYGB7cn0NCmRmIDwtIGNsdWJkYXRhW1s0XV0gIyBncmFiIGNsdWIgDQoNCiMgZ2V0IGJlaGF2aW9yYWwgZGF0YQ0KIyBhdCB0aW1lIHQ9MQ0KdD0xDQpmcmVxMSA8LSBkZiRmcmVxX3J1blssLHRdICMgcnVubmluZyBmcmVxdWVuY2llcyB3YXZlIDENCnZvbDEgPC0gZGYkdGltZV9ydW5bLCx0XSAjIHJ1bm5pbmcgdm9sdW1lIHdhdmUgMQ0KDQojIGV4Y2x1ZGUgTkFzIA0KbmEgPC0gd2hpY2goaXMubmEoZnJlcTEpKQ0KZnJlcTEgPC0gZnJlcTFbLW5hXQ0Kdm9sMSA8LSB2b2wxWy1uYV0NCg0KIyBnZXQga3Vkb3MgbmV0DQprbmV0MSA8LSBkZiRrdWRvWywsdF0NCiMgZXhjbHVkZSBOQXMNCmtuZXQxIDwtIGtuZXQxWy1uYSwtbmFdDQoNCiMgYXMgbmV0d29yayBvYmplY3QNCmtuZXQxIDwtIG5ldHdvcms6OmFzLm5ldHdvcmsoa25ldDEpDQoNCiMgd2UgaW5jbHVkZSBnZW9kaXN0YW5jZXM6IHNob3J0ZXN0IHBhdGggbGVuZ3RocyBmcm9tIGkgdG8gag0KZ2VvZGlzdGFuY2VzIDwtIHNuYTo6Z2VvZGlzdChrbmV0MSwgY291bnQucGF0aHM9VCkNCmdlb2Rpc3RhbmNlcyA8LSBnZW9kaXN0YW5jZXMkZ2Rpc3QgDQoNCiMgc2V0IHRoZSBkaXN0YW5jZSAndG8geW91cnNlbGYnIHRvICdJbmYnDQpkaWFnKGdlb2Rpc3RhbmNlcykgPC0gSW5mDQoNCiMgZmlyc3QgY2FsY3VsYXRlIE1vcmFuJ3MgaSBmb3IgYWx0ZXJzIGF0IGRpc3RhbmNlIDEuDQp3ZWlnaHRzMSA8LSBnZW9kaXN0YW5jZXMgPT0gMQ0KDQojIGFuZCB1c2UgdGhlIG5lZ2F0aXZlIGV4cG9uZW50aWFsIGRpc3RhbmNlLWRlY2F5IGZ1bmN0aW9uDQp3ZWlnaHRzMiA8LSBleHAoLWdlb2Rpc3RhbmNlcykNCg0KIyBjYWxjdWxhdGUgTW9yYW4ncyBJDQojIGZvciBkaXN0YW5jZS0xIGFuZCB3aXRoIGRpc3RhbmNlIGRlY2F5LCBpbiB0aGUga3Vkb3MgbmV0d29yaywgZm9yIGZyZXF1ZW5jeSBhbmQgdm9sdW1lICByZXNwZWN0aXZlbHkNCiMgd2UgZG8gbm90IHJvdyBzdGFuZGFyZGl6ZSENCk1mcmVxIDwtIGZNb3Jhbi5JKGZyZXExLCBzY2FsZWQgPSBGQUxTRSwgd2VpZ2h0ID0gd2VpZ2h0czEsIG5hLnJtID0gVFJVRSwgcm93c3RhbmRhcmRpemUgPSBGQUxTRSkNCk1mcmVxX2Rpc3RkIDwtIGZNb3Jhbi5JKGZyZXExLCBzY2FsZWQgPSBGQUxTRSwgd2VpZ2h0ID0gd2VpZ2h0czIsIG5hLnJtID0gVFJVRSwgcm93c3RhbmRhcmRpemUgPSBGQUxTRSkNCg0KTXZvbCA8LSBmTW9yYW4uSSh2b2wxLCBzY2FsZWQgPSBGQUxTRSwgd2VpZ2h0ID0gd2VpZ2h0czEsIG5hLnJtID0gVFJVRSwgcm93c3RhbmRhcmRpemUgPSBGQUxTRSkNCk12b2xfZGlzdGQgPC0gZk1vcmFuLkkodm9sMSwgc2NhbGVkID0gRkFMU0UsIHdlaWdodCA9IHdlaWdodHMyLCBuYS5ybSA9IFRSVUUsIHJvd3N0YW5kYXJkaXplID0gRkFMU0UpDQoNCiMgbWFrZSBvYmplY3QgdG8gc3RvcmUgcmVzdWx0cw0KIyAxLiBmcmVxdWVuY3kNCmZfbWF0IDwtIG1hdHJpeChOQSwgbnJvdz0yLCBuY29sPTQpDQoNCmZfbWF0WzEsMV0gPC0gTWZyZXEkb2JzZXJ2ZWQNCmZfbWF0WzEsMl0gPC0gTWZyZXEkZXhwZWN0ZWQNCmZfbWF0WzEsM10gPC0gTWZyZXEkc2QNCmZfbWF0WzEsNF0gPC0gTWZyZXEkcC52YWx1ZQ0KZl9tYXRbMiwxXSA8LSBNZnJlcV9kaXN0ZCRvYnNlcnZlZA0KZl9tYXRbMiwyXSA8LSBNZnJlcV9kaXN0ZCRleHBlY3RlZA0KZl9tYXRbMiwzXSA8LSBNZnJlcV9kaXN0ZCRzZA0KZl9tYXRbMiw0XSA8LSBNZnJlcV9kaXN0ZCRwLnZhbHVlDQoNCiMgMi4gdm9sdW1lDQp2X21hdCA8LSBtYXRyaXgoTkEsIG5yb3c9MiwgbmNvbD00KQ0Kdl9tYXRbMSwxXSA8LSBNdm9sJG9ic2VydmVkDQp2X21hdFsxLDJdIDwtIE12b2wkZXhwZWN0ZWQNCnZfbWF0WzEsM10gPC0gTXZvbCRzZA0Kdl9tYXRbMSw0XSA8LSBNdm9sJHAudmFsdWUNCnZfbWF0WzIsMV0gPC0gTXZvbF9kaXN0ZCRvYnNlcnZlZA0Kdl9tYXRbMiwyXSA8LSBNdm9sX2Rpc3RkJGV4cGVjdGVkDQp2X21hdFsyLDNdIDwtIE12b2xfZGlzdGQkc2QNCnZfbWF0WzIsNF0gPC0gTXZvbF9kaXN0ZCRwLnZhbHVlDQoNCmNvbG5hbWVzKGZfbWF0KSA8LSBjb2xuYW1lcyh2X21hdCkgPC0gYygib2JzZXJ2ZWQiLCAiZXhwZWN0ZWQiLCAic2QiLCAicC12YWx1ZSIpDQpyb3duYW1lcyhmX21hdCkgPC0gcm93bmFtZXModl9tYXQpIDwtIGMoImRpcmVjdCBrdWRvcyB0aWVzIiwgImRpcmVjdCBhbmQgaW5kaXJlY3Qga3Vkb3MgdGllcyAoZGlzdGFuY2UtZGVjYXkpIikNCg0Ka25pdHI6OmthYmxlKGZfbWF0LCBkaWdpdHM9MiwgImh0bWwiLCBjYXB0aW9uPSJNb3JhbidzIEkgc3RhdGlzdGljIGZvciBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiBiYXNlZCBvbiBnZW9kaXN0YW5jZXMgYW5kIHdlZWtseSBydW5uaW5nIGZyZXF1ZW5jeSIpICU+JSANCiAga2FibGVFeHRyYTo6a2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSkNCg0Ka25pdHI6OmthYmxlKHZfbWF0LCBkaWdpdHM9MiwgImh0bWwiLCBjYXB0aW9uPSJNb3JhbidzIEkgc3RhdGlzdGljIGZvciBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiBiYXNlZCBvbiBnZW9kaXN0YW5jZXMgYW5kIG1vbnRobHkgcnVubmluZyB2b2x1bWUiKSAlPiUgDQogIGthYmxlRXh0cmE6OmthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpDQpgYGANCg0KDQojIyMgQ2x1YiA1DQpgYGB7cn0NCmRmIDwtIGNsdWJkYXRhW1s1XV0gIyBncmFiIGNsdWIgDQoNCiMgZ2V0IGJlaGF2aW9yYWwgZGF0YQ0KIyBhdCB0aW1lIHQ9MQ0KdD0xDQpmcmVxMSA8LSBkZiRmcmVxX3J1blssLHRdICMgcnVubmluZyBmcmVxdWVuY2llcyB3YXZlIDENCnZvbDEgPC0gZGYkdGltZV9ydW5bLCx0XSAjIHJ1bm5pbmcgdm9sdW1lIHdhdmUgMQ0KDQojIGV4Y2x1ZGUgTkFzIA0KbmEgPC0gd2hpY2goaXMubmEoZnJlcTEpKQ0KZnJlcTEgPC0gZnJlcTFbLW5hXQ0Kdm9sMSA8LSB2b2wxWy1uYV0NCg0KIyBnZXQga3Vkb3MgbmV0DQprbmV0MSA8LSBkZiRrdWRvWywsdF0NCiMgZXhjbHVkZSBOQXMNCmtuZXQxIDwtIGtuZXQxWy1uYSwtbmFdDQoNCiMgYXMgbmV0d29yayBvYmplY3QNCmtuZXQxIDwtIG5ldHdvcms6OmFzLm5ldHdvcmsoa25ldDEpDQoNCiMgd2UgaW5jbHVkZSBnZW9kaXN0YW5jZXM6IHNob3J0ZXN0IHBhdGggbGVuZ3RocyBmcm9tIGkgdG8gag0KZ2VvZGlzdGFuY2VzIDwtIHNuYTo6Z2VvZGlzdChrbmV0MSwgY291bnQucGF0aHM9VCkNCmdlb2Rpc3RhbmNlcyA8LSBnZW9kaXN0YW5jZXMkZ2Rpc3QgDQoNCiMgc2V0IHRoZSBkaXN0YW5jZSAndG8geW91cnNlbGYnIHRvICdJbmYnDQpkaWFnKGdlb2Rpc3RhbmNlcykgPC0gSW5mDQoNCiMgZmlyc3QgY2FsY3VsYXRlIE1vcmFuJ3MgaSBmb3IgYWx0ZXJzIGF0IGRpc3RhbmNlIDEuDQp3ZWlnaHRzMSA8LSBnZW9kaXN0YW5jZXMgPT0gMQ0KDQojIGFuZCB1c2UgdGhlIG5lZ2F0aXZlIGV4cG9uZW50aWFsIGRpc3RhbmNlLWRlY2F5IGZ1bmN0aW9uDQp3ZWlnaHRzMiA8LSBleHAoLWdlb2Rpc3RhbmNlcykNCg0KIyBjYWxjdWxhdGUgTW9yYW4ncyBJDQojIGZvciBkaXN0YW5jZS0xIGFuZCB3aXRoIGRpc3RhbmNlIGRlY2F5LCBpbiB0aGUga3Vkb3MgbmV0d29yaywgZm9yIGZyZXF1ZW5jeSBhbmQgdm9sdW1lICByZXNwZWN0aXZlbHkNCiMgd2UgZG8gbm90IHJvdyBzdGFuZGFyZGl6ZSENCk1mcmVxIDwtIGZNb3Jhbi5JKGZyZXExLCBzY2FsZWQgPSBGQUxTRSwgd2VpZ2h0ID0gd2VpZ2h0czEsIG5hLnJtID0gVFJVRSwgcm93c3RhbmRhcmRpemUgPSBGQUxTRSkNCk1mcmVxX2Rpc3RkIDwtIGZNb3Jhbi5JKGZyZXExLCBzY2FsZWQgPSBGQUxTRSwgd2VpZ2h0ID0gd2VpZ2h0czIsIG5hLnJtID0gVFJVRSwgcm93c3RhbmRhcmRpemUgPSBGQUxTRSkNCg0KTXZvbCA8LSBmTW9yYW4uSSh2b2wxLCBzY2FsZWQgPSBGQUxTRSwgd2VpZ2h0ID0gd2VpZ2h0czEsIG5hLnJtID0gVFJVRSwgcm93c3RhbmRhcmRpemUgPSBGQUxTRSkNCk12b2xfZGlzdGQgPC0gZk1vcmFuLkkodm9sMSwgc2NhbGVkID0gRkFMU0UsIHdlaWdodCA9IHdlaWdodHMyLCBuYS5ybSA9IFRSVUUsIHJvd3N0YW5kYXJkaXplID0gRkFMU0UpDQoNCiMgbWFrZSBvYmplY3QgdG8gc3RvcmUgcmVzdWx0cw0KIyAxLiBmcmVxdWVuY3kNCmZfbWF0IDwtIG1hdHJpeChOQSwgbnJvdz0yLCBuY29sPTQpDQoNCmZfbWF0WzEsMV0gPC0gTWZyZXEkb2JzZXJ2ZWQNCmZfbWF0WzEsMl0gPC0gTWZyZXEkZXhwZWN0ZWQNCmZfbWF0WzEsM10gPC0gTWZyZXEkc2QNCmZfbWF0WzEsNF0gPC0gTWZyZXEkcC52YWx1ZQ0KZl9tYXRbMiwxXSA8LSBNZnJlcV9kaXN0ZCRvYnNlcnZlZA0KZl9tYXRbMiwyXSA8LSBNZnJlcV9kaXN0ZCRleHBlY3RlZA0KZl9tYXRbMiwzXSA8LSBNZnJlcV9kaXN0ZCRzZA0KZl9tYXRbMiw0XSA8LSBNZnJlcV9kaXN0ZCRwLnZhbHVlDQoNCiMgMi4gdm9sdW1lDQp2X21hdCA8LSBtYXRyaXgoTkEsIG5yb3c9MiwgbmNvbD00KQ0Kdl9tYXRbMSwxXSA8LSBNdm9sJG9ic2VydmVkDQp2X21hdFsxLDJdIDwtIE12b2wkZXhwZWN0ZWQNCnZfbWF0WzEsM10gPC0gTXZvbCRzZA0Kdl9tYXRbMSw0XSA8LSBNdm9sJHAudmFsdWUNCnZfbWF0WzIsMV0gPC0gTXZvbF9kaXN0ZCRvYnNlcnZlZA0Kdl9tYXRbMiwyXSA8LSBNdm9sX2Rpc3RkJGV4cGVjdGVkDQp2X21hdFsyLDNdIDwtIE12b2xfZGlzdGQkc2QNCnZfbWF0WzIsNF0gPC0gTXZvbF9kaXN0ZCRwLnZhbHVlDQoNCmNvbG5hbWVzKGZfbWF0KSA8LSBjb2xuYW1lcyh2X21hdCkgPC0gYygib2JzZXJ2ZWQiLCAiZXhwZWN0ZWQiLCAic2QiLCAicC12YWx1ZSIpDQpyb3duYW1lcyhmX21hdCkgPC0gcm93bmFtZXModl9tYXQpIDwtIGMoImRpcmVjdCBrdWRvcyB0aWVzIiwgImRpcmVjdCBhbmQgaW5kaXJlY3Qga3Vkb3MgdGllcyAoZGlzdGFuY2UtZGVjYXkpIikNCg0Ka25pdHI6OmthYmxlKGZfbWF0LCBkaWdpdHM9MiwgImh0bWwiLCBjYXB0aW9uPSJNb3JhbidzIEkgc3RhdGlzdGljIGZvciBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiBiYXNlZCBvbiBnZW9kaXN0YW5jZXMgYW5kIHdlZWtseSBydW5uaW5nIGZyZXF1ZW5jeSIpICU+JSANCiAga2FibGVFeHRyYTo6a2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSkNCg0Ka25pdHI6OmthYmxlKHZfbWF0LCBkaWdpdHM9MiwgImh0bWwiLCBjYXB0aW9uPSJNb3JhbidzIEkgc3RhdGlzdGljIGZvciBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiBiYXNlZCBvbiBnZW9kaXN0YW5jZXMgYW5kIG1vbnRobHkgcnVubmluZyB2b2x1bWUiKSAlPiUgDQogIGthYmxlRXh0cmE6OmthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpDQpgYGANCg0KDQojIyB7LX0NCg0KSGVyZSwgdGhlIE1vcmFuJ3MgSSBzdGF0aXN0aWMgdGVzdHMgd2hldGhlciBjbHViIG1lbWJlcnMgdGhhdCBhcmUgKmNsb3NlciB0byBvbmUgYW5vdGhlciogKGkuZS4sIGhhdmluZyBhIHNob3J0ZXIgZ2VvZGVzaWMvcGF0aCBsZW5ndGgpLCBhcmUgbW9yZSBhIHNpbWlsYXIgd2l0aCByZXNwZWN0IHRvIHRoZWlyIGJlaGF2aW9yLCB1bmRlciB0aGUgbnVsbCBoeXBvdGhlc2lzIHRoYXQgYmVoYXZpb3IgaXMgJ3JhbmRvbWx5IGRpc3RyaWJ1dGVkJyBhbW9uZyB0aGUgY2x1YiBtZW1iZXJzLg0KDQpXZSBvYnNlcnZlIHRoYXQsIGluZGVlZCwga3Vkb3MtdGllcyB0aGF0IGFyZSBjbG9zZXIgdG8gb25lIGFub3RoZXIgYXJlIG1vcmUgYWxpa2UsIHdpdGggcmVzcGVjdCB0byBydW5uaW5nLiBBdXRvY29ycmVsYXRpb24gd2FzIHN0cm9uZ2VyIHdpdGhvdXQgdGhlIGRpc3RhbmNlLWRlY2F5IGZ1bmN0aW9uLCB3aGljaCBzdWdnZXN0cyB0aGF0IGVzcGVjaWFsbHkgY2xvc2UgYWx0ZXJzICh3aXRoIHBhdGggbGVuZ3RoIG9uZSkgYXJlIHNpbWlsYXIuIA0KDQo8YnI+DQoNCg0KDQotLS0tDQojIyBSZWZlcmVuY2Vz


Copyright © 2021 Rob Franken