library(knitr)
library(tidyverse)
library(scholar)
library(openalexR)
library(rvest)
library(jsonlite)
library(httr)
library(rvest)
library(reshape2)
library(xml2)
library(openxlsx)
library(polite)
library(igraph)
library(sna)
library(genderizeR)
library(RSelenium)
library(netstat)
library(pingr)

#load the functions you need from the packages
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)
    }
  })
}

fsave <- function(x, file = NULL, location = "./data/processed/") {
  ifelse(!dir.exists("data"), dir.create("data"), FALSE)
  ifelse(!dir.exists("data/processed"), dir.create("data/processed"), FALSE)
  if (is.null(file))
    file = deparse(substitute(x))
  datename <- substr(gsub("[:-]", "", Sys.time()), 1, 8)
  totalname <- paste(location, datename, file, ".rda", sep = "")
  save(x, file = totalname)  #need to fix if file is reloaded as input name, not as x. 
}

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

fshowdf <- function(x, ...) {
  knitr::kable(x, digits = 2, "html", ...) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover")) %>%
    kableExtra::scroll_box(width = "100%", height = "300px")
}

Last compiled on September, 2025


1 Week 4



1.1 Workshop

Today we are going to look at network visualisation. It is important you look at the data



1.1.1 Zachary’s karate club

require(igraph)

# Visualise Zachary's network
g <- make_graph("Zachary")
plot(g)

# you see differences between ego's in centralities, it's a complete network, undirected and unweighted ties.
# Perhaps something like friendship or something.

gmat <- as_adjacency_matrix(g, type = "both", sparse = FALSE)
#gmat

# Descriptives
# number of nodes: vcount(g)

# number of edges: ecount(g)

# the number of degrees: heavliy skewed
igraph::degree(g)
##  [1] 16  9 10  6  3  4  4  4  5  2  3  1  2  5  2  2  2  2  2  3  2  2  2  5  3  3  2  4  3  4  4  6
## [33] 12 17
# transitivity
# be aware that directed graphs are considered as undirected. but g is undirected.
igraph::transitivity(g, type = c("localundirected"), isolates = c("NaN", "zero"))
##  [1] 0.1500000 0.3333333 0.2444444 0.6666667 0.6666667 0.5000000 0.5000000 1.0000000 0.5000000
## [10] 0.0000000 0.6666667       NaN 1.0000000 0.6000000 1.0000000 1.0000000 1.0000000 1.0000000
## [19] 1.0000000 0.3333333 1.0000000 1.0000000 1.0000000 0.4000000 0.3333333 0.3333333 1.0000000
## [28] 0.1666667 0.3333333 0.6666667 0.5000000 0.2000000 0.1969697 0.1102941
# betweenness: 1 and 34 very high
igraph::betweenness(g, directed = FALSE)
##  [1] 231.0714286  28.4785714  75.8507937   6.2880952   0.3333333  15.8333333  15.8333333   0.0000000
##  [9]  29.5293651   0.4476190   0.3333333   0.0000000   0.0000000  24.2158730   0.0000000   0.0000000
## [17]   0.0000000   0.0000000   0.0000000  17.1468254   0.0000000   0.0000000   0.0000000   9.3000000
## [25]   1.1666667   2.0277778   0.0000000  11.7920635   0.9476190   1.5428571   7.6095238  73.0095238
## [33]  76.6904762 160.5515873
# dyad census: igraph::dyad.census(g)
# igraph::triad.census(g)
# I will use sna because it shows the names of the triads as well.

igraph::transitivity(g, type = "global")
## [1] 0.2556818
#sna::gtrans(gmat)

triad_g <- data.frame(sna::triad.census(gmat))

# for transitivity on network level: divide number of transitive triads by all possible transitive triads

# X300 is the variable of transative triads, and the X201 is the one in which one person is linked with two persons, who are not linked: so it's a possible transitive triad. 
# therefore, X201 + X300 are all possible transititve triads.
transitivity_g <- (3 * triad_g$X300)/(triad_g$X201 + 3 * triad_g$X300)
transitivity_g
## [1] 0.2556818

1.2

1.2.1 Ashwin Presentation

You can look at network properties (descriptives, graph), Ashwin looks at how we can use networks to explain certain phenomena.

RQ: How are social and intellectual relations structured and given shape within research universities?

Not only about social relationships, but also intellectual thinking. Do shared interests lead to collaborations, or do collaborations lead to similar interests.

- structure:    collaboration leads to similar interests
- culture:      scientists with similar interests find each other
- homophily:    similar backgrounds lead to similar interest and collaboration
- propinquity:  physical proximity leads to similar research interest and collaboration

If you have a huge network of ties, you can see if there are people who do not work together with people you are observing, but that does not mean they don’t collaborate at all.

1.3

1.3.1 Continuing with Zachary’s karate club

# make sizes proportional to betweenness score
V(g)$size = igraph::betweenness(g, normalized = TRUE, directed = FALSE)* 60 + 10  #after some trial and error
plot(g, mode = "undirected")

# the + 10 is added because some nodes have a 0 on betweenness and dissapear in the graph without +10
# the * 60 makes the differences bigger

# now: we want no overlap 
set.seed(2345)
l <- layout_with_mds(g)  #https://igraph.org/r/doc/layout_with_mds.html
plot(g, layout = l)

# now you see three clusters: around 33/34, around 1 and around 3, and the cluser around 3 you didn't see before. And that the two biggest clusters are not linked directly.

# you can then say that specific ones should be further apart:
# l  #let us take a look at the coordinates
l[1, 1] <- 4
l[34, 1] <- -3.5
plot(g, layout = l)

# 1 and 34 are most connected, perhaps they are the owners/masters from the karate club
# and maybe they are having an argument and are therefore not connected

Looking at Jochem’s paper about twitter and the people in parliament. You see pollarisation: people mostly interact with people in their own party (clusters)



1.4 Homework



1.5 Friday September 19th



1.5.1 Figuring out assigning gender:

# from the tutorial
df <- fload(file = "./data/20230627df_names.rda")

firstname <- df |>
  distinct(firstname) |> #only unique
  select(firstname) |> #only this variable
  filter(firstname != "") |> #remove empty
  pull(firstname) #save as chr

test <- genderizeAPI(firstname[1])
#test$response

length(firstname)
## [1] 595
df_genderizer <- data.frame(matrix(ncol = 4, nrow = length(firstname)))
colnames(df_genderizer) <- c("count", "name", "gender", "probability")

#for (i in 1:length(firstname)) {
#    Sys.sleep(0.1)
#    df_genderizer[i, ] <- genderizeAPI(firstname[i])$response
#}
#colnames(df_genderizer) <- c("count", "name", "gender", "probability")
#df_genderizer$name <- firstname  #so we know which names are not found. 

#df_genderizer

# now we have a probability score, but you can also dummify it
#df_genderizer <- df_genderizer[df_genderizer$probability > 0.9, ]

#df_genderizer <- subset(df_genderizer, select = c("name", "gender"))

#fshowdf(df_genderizer)
#View(df_genderizer)

#df |>
#    left_join(df_genderizer, by = c(firstname = "name")) -> df_gender

#fshowdf(df_gender)

#fsave(df_gender)



1.5.2 Figured out assigning gender

# Rvest table object inspecteren en die geven aan het 
name = "Erik"

# Build URL robustly
base_url <- "https://nvb.meertens.knaw.nl/naam/is"
name_url <- paste0(base_url, "/", name)

table <- read_html(name_url) |>
  html_element("table") |>
  html_table()


# Replace "--" with "0" everywhere
table[table == "--"] <- "0"

# Now you can safely convert to numeric
val_male   <- as.numeric(table[2, 3])
val_female <- as.numeric(table[6, 3])

# Compare and assign gender
gender <- if (val_male > val_female) {
  "male"
} else if (val_female >= val_male) {
  "female"
} else {
  NA
}

gender
## [1] "male"



1.5.3 Making a function genderizer

my_genderizer <- function(name) {
  name <- gsub("^/+|/+$", "", name)
  
  # Build URL
  base_url <- "https://nvb.meertens.knaw.nl/naam/is"
  name_url <- paste0(base_url, "/", name)
  
  # Try reading table
  table <- tryCatch(
    read_html(name_url) |>
      html_element("table") |>
      html_table(),
    error = function(e) NULL
  )
  
  if (is.null(table)) return(NA)  # return NA if no table found
  
  # Replace "--" with "0"
  table[table == "--"] <- "0"
  
  # Convert to numeric
  val_male   <- suppressWarnings(as.numeric(table[2, 3]))
  val_female <- suppressWarnings(as.numeric(table[6, 3]))
  
  # Decide gender
  if (is.na(val_male) & is.na(val_female)) {
    gender <- NA
  } else if (val_male > val_female) {
    gender <- "male"
  } else if (val_female > val_male) {
    gender <- "female"
  } else {
    gender <- NA
  }
  
  return(gender)
}

soc_pol <- readxl::read_excel("/Users/mylenehusson/Desktop/SocialNetworks/labjournal_mylene/data/20240419Scholarid_soc_pol.xlsx")
soc_pol_small <- soc_pol[1:20, ]

soc_pol_small <- soc_pol_small |>
  mutate(firstname = word(Naam, 1)) 

for (i in seq_len(nrow(soc_pol_small))) {
  name <- soc_pol_small$firstname[i]
  gender <- my_genderizer(name)

  soc_pol_small$gender[i] <- gender
}
## Warning: Unknown or uninitialised column: `gender`.
#View(soc_pol_small)



1.5.4 The nomination Network

# load in the data
scholars <- fload("./data/processed/scholars_20240924.rda")

# select scholars
# (sociology, RU)
demographics <- do.call(rbind.data.frame, scholars$demographics)
demographics <- demographics %>%
    mutate(Universiteit1.22 = replace(Universiteit1.22, is.na(Universiteit1.22), ""), Universiteit2.22 = replace(Universiteit2.22,
        is.na(Universiteit2.22), ""), Universiteit1.24 = replace(Universiteit1.24, is.na(Universiteit1.24),
        ""), Universiteit2.24 = replace(Universiteit2.24, is.na(Universiteit2.24), ""), discipline.22 = replace(discipline.22,
        is.na(discipline.22), ""), discipline.24 = replace(discipline.24, is.na(discipline.24), ""))

sample <- which((demographics$Universiteit1.22 == "RU" | demographics$Universiteit2.22 == "RU" | demographics$Universiteit1.24 ==
    "RU" | demographics$Universiteit2.24 == "RU") & (demographics$discipline.22 == "sociology" | demographics$discipline.24 ==
    "sociology"))

demographics_soc <- demographics[sample, ]
scholars_sel <- lapply(scholars, "[", sample)

# construct the empty adjacency matrix
ids <- demographics_soc$au_id
wave2 <- wave1 <- matrix(0, nrow = length(ids), ncol = length(ids), dimnames = list(ids, ids))

# filter works
works <- scholars_sel$work

works_id <- unlist(lapply(works, function(l) l$id))
works_author <- unlist(lapply(works, function(l) l$author), recursive = FALSE)
works_year <- unlist(lapply(works, function(l) l$publication_year), recursive = FALSE)

df_works <- tibble(works_id, works_author, works_year)

dups <- which(duplicated(works_id))

df_works <- df_works[-dups, ]
df_works_w2 <- df_works[df_works$works_year > 2019, ]

# undirected
# undirected
for (i in 1:nrow(df_works_w2)) {
    egos <- df_works_w2$works_author[i][[1]]$au_id

    if (sum(ids %in% egos) > 0) {
        wave2[which(ids %in% egos), which(ids %in% egos)] <- 1
    }
}


Now one function to rule them all:

fcolnet <- function(data = scholars, university = "RU", discipline = "sociology", waves = list(c(2015,
    2018), c(2019, 2023)), type = c("first")) {

    # step 1
    demographics <- do.call(rbind.data.frame, data$demographics)
    demographics <- demographics %>%
        mutate(Universiteit1.22 = replace(Universiteit1.22, is.na(Universiteit1.22), ""), Universiteit2.22 = replace(Universiteit2.22,
            is.na(Universiteit2.22), ""), Universiteit1.24 = replace(Universiteit1.24, is.na(Universiteit1.24),
            ""), Universiteit2.24 = replace(Universiteit2.24, is.na(Universiteit2.24), ""), discipline.22 = replace(discipline.22,
            is.na(discipline.22), ""), discipline.24 = replace(discipline.24, is.na(discipline.24), ""))

    sample <- which((demographics$Universiteit1.22 %in% university | demographics$Universiteit2.22 %in%
        university | demographics$Universiteit1.24 %in% university | demographics$Universiteit2.24 %in%
        university) & (demographics$discipline.22 %in% discipline | demographics$discipline.24 %in% discipline))

    demographics_soc <- demographics[sample, ]
    scholars_sel <- lapply(scholars, "[", sample)

    # step 2
    ids <- demographics_soc$au_id
    nwaves <- length(waves)
    nets <- array(0, dim = c(nwaves, length(ids), length(ids)), dimnames = list(wave = 1:nwaves, ids,
        ids))
    dimnames(nets)

    # step 3
    df_works <- tibble(works_id = unlist(lapply(scholars_sel$work, function(l) l$id)), works_author = unlist(lapply(scholars_sel$work,
        function(l) l$author), recursive = FALSE), works_year = unlist(lapply(scholars_sel$work, function(l) l$publication_year),
        recursive = FALSE))

    df_works <- df_works[!duplicated(df_works), ]

    # step 4
    if (type == "first") {
        for (j in 1:nwaves) {
            df_works_w <- df_works[df_works$works_year >= waves[[j]][1] & df_works$works_year <= waves[[j]][2],
                ]
            for (i in 1:nrow(df_works_w)) {
                ego <- df_works_w$works_author[i][[1]]$au_id[1]
                alters <- df_works_w$works_author[i][[1]]$au_id[-1]
                if (sum(ids %in% ego) > 0 & sum(ids %in% alters) > 0) {
                  nets[j, which(ids %in% ego), which(ids %in% alters)] <- 1
                }
            }
        }
    }

    if (type == "last") {
        for (j in 1:nwaves) {
            df_works_w <- df_works[df_works$works_year >= waves[[j]][1] & df_works$works_year <= waves[[j]][2],
                ]
            for (i in 1:nrow(df_works_w)) {
                ego <- rev(df_works_w$works_author[i][[1]]$au_id)[1]
                alters <- rev(df_works_w$works_author[i][[1]]$au_id)[-1]
                if (sum(ids %in% ego) > 0 & sum(ids %in% alters) > 0) {
                  nets[j, which(ids %in% ego), which(ids %in% alters)] <- 1
                }
            }
        }
    }

    if (type == "all") {
        for (j in 1:nwaves) {
            df_works_w <- df_works[df_works$works_year >= waves[[j]][1] & df_works$works_year <= waves[[j]][2],
                ]
            for (i in 1:nrow(df_works_w)) {
                egos <- df_works_w$works_author[i][[1]]$au_id
                if (sum(ids %in% egos) > 0) {
                  nets[j, which(ids %in% egos), which(ids %in% egos)] <- 1
                }
            }
        }
    }
    output <- list()
    output$data <- scholars_sel
    output$nets <- nets
    return(output)
}



1.5.5 Assigning Gender to demographics

df_scholars <- do.call(rbind, scholars$demographics)

#View(df_scholars)

df_scholars2 <- df_scholars |>
  mutate(firstname = word(Naam, 1))  # extract first names

df_scholars2$gender <- NA_character_  # initialize gender column

for (i in seq_len(nrow(df_scholars2))) {
  name <- df_scholars2$firstname[i]
  gender <- my_genderizer(name)
  
  df_scholars2$gender[i] <- gender
  Sys.sleep(0.1)  # pause between requests
}

#View(df_scholars2)



1.5.6 Who works together with whom?

LS0tCnRpdGxlOiAiSm91cm5hbCAxIgojYmlibGlvZ3JhcGh5OiByZWZlcmVuY2VzLmJpYgphdXRob3I6ICJNeWzDqG5lIEh1c3NvbiIKLS0tCgpgYGB7ciwgZWNobz1UUlVFLCByZXN1bHRzPSdoaWRlJywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShrbml0cikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoc2Nob2xhcikKbGlicmFyeShvcGVuYWxleFIpCmxpYnJhcnkocnZlc3QpCmxpYnJhcnkoanNvbmxpdGUpCmxpYnJhcnkoaHR0cikKbGlicmFyeShydmVzdCkKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeSh4bWwyKQpsaWJyYXJ5KG9wZW54bHN4KQpsaWJyYXJ5KHBvbGl0ZSkKbGlicmFyeShpZ3JhcGgpCmxpYnJhcnkoc25hKQpsaWJyYXJ5KGdlbmRlcml6ZVIpCmxpYnJhcnkoUlNlbGVuaXVtKQpsaWJyYXJ5KG5ldHN0YXQpCmxpYnJhcnkocGluZ3IpCgojbG9hZCB0aGUgZnVuY3Rpb25zIHlvdSBuZWVkIGZyb20gdGhlIHBhY2thZ2VzCmZwYWNrYWdlLmNoZWNrIDwtIGZ1bmN0aW9uKHBhY2thZ2VzKSB7CiAgbGFwcGx5KHBhY2thZ2VzLCBGVU4gPSBmdW5jdGlvbih4KSB7CiAgICBpZiAoIXJlcXVpcmUoeCwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKSkgewogICAgICBpbnN0YWxsLnBhY2thZ2VzKHgsIGRlcGVuZGVuY2llcyA9IFRSVUUpCiAgICAgIGxpYnJhcnkoeCwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQogICAgfQogIH0pCn0KCmZzYXZlIDwtIGZ1bmN0aW9uKHgsIGZpbGUgPSBOVUxMLCBsb2NhdGlvbiA9ICIuL2RhdGEvcHJvY2Vzc2VkLyIpIHsKICBpZmVsc2UoIWRpci5leGlzdHMoImRhdGEiKSwgZGlyLmNyZWF0ZSgiZGF0YSIpLCBGQUxTRSkKICBpZmVsc2UoIWRpci5leGlzdHMoImRhdGEvcHJvY2Vzc2VkIiksIGRpci5jcmVhdGUoImRhdGEvcHJvY2Vzc2VkIiksIEZBTFNFKQogIGlmIChpcy5udWxsKGZpbGUpKQogICAgZmlsZSA9IGRlcGFyc2Uoc3Vic3RpdHV0ZSh4KSkKICBkYXRlbmFtZSA8LSBzdWJzdHIoZ3N1YigiWzotXSIsICIiLCBTeXMudGltZSgpKSwgMSwgOCkKICB0b3RhbG5hbWUgPC0gcGFzdGUobG9jYXRpb24sIGRhdGVuYW1lLCBmaWxlLCAiLnJkYSIsIHNlcCA9ICIiKQogIHNhdmUoeCwgZmlsZSA9IHRvdGFsbmFtZSkgICNuZWVkIHRvIGZpeCBpZiBmaWxlIGlzIHJlbG9hZGVkIGFzIGlucHV0IG5hbWUsIG5vdCBhcyB4LiAKfQoKZmxvYWQgPC0gZnVuY3Rpb24oZmlsZW5hbWUpIHsKICBsb2FkKGZpbGVuYW1lKQogIGdldChscygpW2xzKCkgIT0gImZpbGVuYW1lIl0pCn0KCmZzaG93ZGYgPC0gZnVuY3Rpb24oeCwgLi4uKSB7CiAga25pdHI6OmthYmxlKHgsIGRpZ2l0cyA9IDIsICJodG1sIiwgLi4uKSAlPiUKICAgIGthYmxlRXh0cmE6OmthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpICU+JQogICAga2FibGVFeHRyYTo6c2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIiwgaGVpZ2h0ID0gIjMwMHB4IikKfQoKYGBgCgpgYGB7ciBrbGlwcHksIGVjaG89RkFMU0UsIGluY2x1ZGU9VFJVRX0Ka2xpcHB5OjprbGlwcHkocG9zaXRpb24gPSBjKCd0b3AnLCAncmlnaHQnKSkKI2tsaXBweTo6a2xpcHB5KGNvbG9yID0gJ2RhcmtyZWQnKQoja2xpcHB5OjprbGlwcHkodG9vbHRpcF9tZXNzYWdlID0gJ0NsaWNrIHRvIGNvcHknLCB0b29sdGlwX3N1Y2Nlc3MgPSAnRG9uZScpCmBgYAoKTGFzdCBjb21waWxlZCBvbiBgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVCLCAlWScpYAo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgV2VlayA0Cjxicj4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgV29ya3Nob3AKClRvZGF5IHdlIGFyZSBnb2luZyB0byBsb29rIGF0IG5ldHdvcmsgdmlzdWFsaXNhdGlvbi4gSXQgaXMgaW1wb3J0YW50IHlvdSBsb29rIGF0IHRoZSBkYXRhCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIyAgWmFjaGFyeeKAmXMga2FyYXRlIGNsdWIKCmBgYHtyfQpyZXF1aXJlKGlncmFwaCkKCiMgVmlzdWFsaXNlIFphY2hhcnkncyBuZXR3b3JrCmcgPC0gbWFrZV9ncmFwaCgiWmFjaGFyeSIpCnBsb3QoZykKCiMgeW91IHNlZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGVnbydzIGluIGNlbnRyYWxpdGllcywgaXQncyBhIGNvbXBsZXRlIG5ldHdvcmssIHVuZGlyZWN0ZWQgYW5kIHVud2VpZ2h0ZWQgdGllcy4KIyBQZXJoYXBzIHNvbWV0aGluZyBsaWtlIGZyaWVuZHNoaXAgb3Igc29tZXRoaW5nLgoKZ21hdCA8LSBhc19hZGphY2VuY3lfbWF0cml4KGcsIHR5cGUgPSAiYm90aCIsIHNwYXJzZSA9IEZBTFNFKQojZ21hdAoKIyBEZXNjcmlwdGl2ZXMKIyBudW1iZXIgb2Ygbm9kZXM6IHZjb3VudChnKQoKIyBudW1iZXIgb2YgZWRnZXM6IGVjb3VudChnKQoKIyB0aGUgbnVtYmVyIG9mIGRlZ3JlZXM6IGhlYXZsaXkgc2tld2VkCmlncmFwaDo6ZGVncmVlKGcpCgojIHRyYW5zaXRpdml0eQojIGJlIGF3YXJlIHRoYXQgZGlyZWN0ZWQgZ3JhcGhzIGFyZSBjb25zaWRlcmVkIGFzIHVuZGlyZWN0ZWQuIGJ1dCBnIGlzIHVuZGlyZWN0ZWQuCmlncmFwaDo6dHJhbnNpdGl2aXR5KGcsIHR5cGUgPSBjKCJsb2NhbHVuZGlyZWN0ZWQiKSwgaXNvbGF0ZXMgPSBjKCJOYU4iLCAiemVybyIpKQoKIyBiZXR3ZWVubmVzczogMSBhbmQgMzQgdmVyeSBoaWdoCmlncmFwaDo6YmV0d2Vlbm5lc3MoZywgZGlyZWN0ZWQgPSBGQUxTRSkKCiMgZHlhZCBjZW5zdXM6IGlncmFwaDo6ZHlhZC5jZW5zdXMoZykKIyBpZ3JhcGg6OnRyaWFkLmNlbnN1cyhnKQojIEkgd2lsbCB1c2Ugc25hIGJlY2F1c2UgaXQgc2hvd3MgdGhlIG5hbWVzIG9mIHRoZSB0cmlhZHMgYXMgd2VsbC4KCmlncmFwaDo6dHJhbnNpdGl2aXR5KGcsIHR5cGUgPSAiZ2xvYmFsIikKI3NuYTo6Z3RyYW5zKGdtYXQpCgp0cmlhZF9nIDwtIGRhdGEuZnJhbWUoc25hOjp0cmlhZC5jZW5zdXMoZ21hdCkpCgojIGZvciB0cmFuc2l0aXZpdHkgb24gbmV0d29yayBsZXZlbDogZGl2aWRlIG51bWJlciBvZiB0cmFuc2l0aXZlIHRyaWFkcyBieSBhbGwgcG9zc2libGUgdHJhbnNpdGl2ZSB0cmlhZHMKCiMgWDMwMCBpcyB0aGUgdmFyaWFibGUgb2YgdHJhbnNhdGl2ZSB0cmlhZHMsIGFuZCB0aGUgWDIwMSBpcyB0aGUgb25lIGluIHdoaWNoIG9uZSBwZXJzb24gaXMgbGlua2VkIHdpdGggdHdvIHBlcnNvbnMsIHdobyBhcmUgbm90IGxpbmtlZDogc28gaXQncyBhIHBvc3NpYmxlIHRyYW5zaXRpdmUgdHJpYWQuIAojIHRoZXJlZm9yZSwgWDIwMSArIFgzMDAgYXJlIGFsbCBwb3NzaWJsZSB0cmFuc2l0aXR2ZSB0cmlhZHMuCnRyYW5zaXRpdml0eV9nIDwtICgzICogdHJpYWRfZyRYMzAwKS8odHJpYWRfZyRYMjAxICsgMyAqIHRyaWFkX2ckWDMwMCkKdHJhbnNpdGl2aXR5X2cKYGBgCjxicj4KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMgQXNod2luIFByZXNlbnRhdGlvbgoKWW91IGNhbiBsb29rIGF0IG5ldHdvcmsgcHJvcGVydGllcyAoZGVzY3JpcHRpdmVzLCBncmFwaCksIEFzaHdpbiBsb29rcyBhdCBob3cgd2UgY2FuIHVzZSBuZXR3b3JrcyB0byBleHBsYWluIGNlcnRhaW4gcGhlbm9tZW5hLiAKClJROiBIb3cgYXJlIHNvY2lhbCBhbmQgaW50ZWxsZWN0dWFsIHJlbGF0aW9ucyBzdHJ1Y3R1cmVkIGFuZCBnaXZlbiBzaGFwZSB3aXRoaW4gcmVzZWFyY2ggdW5pdmVyc2l0aWVzPwoKTm90IG9ubHkgYWJvdXQgc29jaWFsIHJlbGF0aW9uc2hpcHMsIGJ1dCBhbHNvIGludGVsbGVjdHVhbCB0aGlua2luZy4KRG8gc2hhcmVkIGludGVyZXN0cyBsZWFkIHRvIGNvbGxhYm9yYXRpb25zLCBvciBkbyBjb2xsYWJvcmF0aW9ucyBsZWFkIHRvIHNpbWlsYXIgaW50ZXJlc3RzLgoKICAgIC0gc3RydWN0dXJlOiAgICBjb2xsYWJvcmF0aW9uIGxlYWRzIHRvIHNpbWlsYXIgaW50ZXJlc3RzCiAgICAtIGN1bHR1cmU6ICAgICAgc2NpZW50aXN0cyB3aXRoIHNpbWlsYXIgaW50ZXJlc3RzIGZpbmQgZWFjaCBvdGhlcgogICAgLSBob21vcGhpbHk6ICAgIHNpbWlsYXIgYmFja2dyb3VuZHMgbGVhZCB0byBzaW1pbGFyIGludGVyZXN0IGFuZCBjb2xsYWJvcmF0aW9uCiAgICAtIHByb3BpbnF1aXR5OiAgcGh5c2ljYWwgcHJveGltaXR5IGxlYWRzIHRvIHNpbWlsYXIgcmVzZWFyY2ggaW50ZXJlc3QgYW5kIGNvbGxhYm9yYXRpb24KCklmIHlvdSBoYXZlIGEgaHVnZSBuZXR3b3JrIG9mIHRpZXMsIHlvdSBjYW4gc2VlIGlmIHRoZXJlIGFyZSBwZW9wbGUgd2hvIGRvIG5vdCB3b3JrIHRvZ2V0aGVyIHdpdGggcGVvcGxlIHlvdSBhcmUgb2JzZXJ2aW5nLCBidXQgdGhhdCBkb2VzIG5vdCBtZWFuIHRoZXkgZG9uJ3QgY29sbGFib3JhdGUgYXQgYWxsLiAKCjxicj4KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMgQ29udGludWluZyB3aXRoIFphY2hhcnnigJlzIGthcmF0ZSBjbHViCgpgYGB7cn0KIyBtYWtlIHNpemVzIHByb3BvcnRpb25hbCB0byBiZXR3ZWVubmVzcyBzY29yZQpWKGcpJHNpemUgPSBpZ3JhcGg6OmJldHdlZW5uZXNzKGcsIG5vcm1hbGl6ZWQgPSBUUlVFLCBkaXJlY3RlZCA9IEZBTFNFKSogNjAgKyAxMCAgI2FmdGVyIHNvbWUgdHJpYWwgYW5kIGVycm9yCnBsb3QoZywgbW9kZSA9ICJ1bmRpcmVjdGVkIikKCiMgdGhlICsgMTAgaXMgYWRkZWQgYmVjYXVzZSBzb21lIG5vZGVzIGhhdmUgYSAwIG9uIGJldHdlZW5uZXNzIGFuZCBkaXNzYXBlYXIgaW4gdGhlIGdyYXBoIHdpdGhvdXQgKzEwCiMgdGhlICogNjAgbWFrZXMgdGhlIGRpZmZlcmVuY2VzIGJpZ2dlcgoKIyBub3c6IHdlIHdhbnQgbm8gb3ZlcmxhcCAKc2V0LnNlZWQoMjM0NSkKbCA8LSBsYXlvdXRfd2l0aF9tZHMoZykgICNodHRwczovL2lncmFwaC5vcmcvci9kb2MvbGF5b3V0X3dpdGhfbWRzLmh0bWwKcGxvdChnLCBsYXlvdXQgPSBsKQojIG5vdyB5b3Ugc2VlIHRocmVlIGNsdXN0ZXJzOiBhcm91bmQgMzMvMzQsIGFyb3VuZCAxIGFuZCBhcm91bmQgMywgYW5kIHRoZSBjbHVzZXIgYXJvdW5kIDMgeW91IGRpZG4ndCBzZWUgYmVmb3JlLiBBbmQgdGhhdCB0aGUgdHdvIGJpZ2dlc3QgY2x1c3RlcnMgYXJlIG5vdCBsaW5rZWQgZGlyZWN0bHkuCgojIHlvdSBjYW4gdGhlbiBzYXkgdGhhdCBzcGVjaWZpYyBvbmVzIHNob3VsZCBiZSBmdXJ0aGVyIGFwYXJ0OgojIGwgICNsZXQgdXMgdGFrZSBhIGxvb2sgYXQgdGhlIGNvb3JkaW5hdGVzCmxbMSwgMV0gPC0gNApsWzM0LCAxXSA8LSAtMy41CnBsb3QoZywgbGF5b3V0ID0gbCkKCiMgMSBhbmQgMzQgYXJlIG1vc3QgY29ubmVjdGVkLCBwZXJoYXBzIHRoZXkgYXJlIHRoZSBvd25lcnMvbWFzdGVycyBmcm9tIHRoZSBrYXJhdGUgY2x1YgojIGFuZCBtYXliZSB0aGV5IGFyZSBoYXZpbmcgYW4gYXJndW1lbnQgYW5kIGFyZSB0aGVyZWZvcmUgbm90IGNvbm5lY3RlZApgYGAKCkxvb2tpbmcgYXQgSm9jaGVtJ3MgcGFwZXIgYWJvdXQgdHdpdHRlciBhbmQgdGhlIHBlb3BsZSBpbiBwYXJsaWFtZW50LiAKWW91IHNlZSBwb2xsYXJpc2F0aW9uOiBwZW9wbGUgbW9zdGx5IGludGVyYWN0IHdpdGggcGVvcGxlIGluIHRoZWlyIG93biBwYXJ0eSAoY2x1c3RlcnMpCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIEhvbWV3b3JrCgoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBGcmlkYXkgU2VwdGVtYmVyIDE5dGgKCjxicj4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIEZpZ3VyaW5nIG91dCBhc3NpZ25pbmcgZ2VuZGVyOiAKCmBgYHtyfQojIGZyb20gdGhlIHR1dG9yaWFsCmRmIDwtIGZsb2FkKGZpbGUgPSAiLi9kYXRhLzIwMjMwNjI3ZGZfbmFtZXMucmRhIikKCmZpcnN0bmFtZSA8LSBkZiB8PgogIGRpc3RpbmN0KGZpcnN0bmFtZSkgfD4gI29ubHkgdW5pcXVlCiAgc2VsZWN0KGZpcnN0bmFtZSkgfD4gI29ubHkgdGhpcyB2YXJpYWJsZQogIGZpbHRlcihmaXJzdG5hbWUgIT0gIiIpIHw+ICNyZW1vdmUgZW1wdHkKICBwdWxsKGZpcnN0bmFtZSkgI3NhdmUgYXMgY2hyCgp0ZXN0IDwtIGdlbmRlcml6ZUFQSShmaXJzdG5hbWVbMV0pCiN0ZXN0JHJlc3BvbnNlCgpsZW5ndGgoZmlyc3RuYW1lKQpkZl9nZW5kZXJpemVyIDwtIGRhdGEuZnJhbWUobWF0cml4KG5jb2wgPSA0LCBucm93ID0gbGVuZ3RoKGZpcnN0bmFtZSkpKQpjb2xuYW1lcyhkZl9nZW5kZXJpemVyKSA8LSBjKCJjb3VudCIsICJuYW1lIiwgImdlbmRlciIsICJwcm9iYWJpbGl0eSIpCgojZm9yIChpIGluIDE6bGVuZ3RoKGZpcnN0bmFtZSkpIHsKIyAgICBTeXMuc2xlZXAoMC4xKQojICAgIGRmX2dlbmRlcml6ZXJbaSwgXSA8LSBnZW5kZXJpemVBUEkoZmlyc3RuYW1lW2ldKSRyZXNwb25zZQojfQojY29sbmFtZXMoZGZfZ2VuZGVyaXplcikgPC0gYygiY291bnQiLCAibmFtZSIsICJnZW5kZXIiLCAicHJvYmFiaWxpdHkiKQojZGZfZ2VuZGVyaXplciRuYW1lIDwtIGZpcnN0bmFtZSAgI3NvIHdlIGtub3cgd2hpY2ggbmFtZXMgYXJlIG5vdCBmb3VuZC4gCgojZGZfZ2VuZGVyaXplcgoKIyBub3cgd2UgaGF2ZSBhIHByb2JhYmlsaXR5IHNjb3JlLCBidXQgeW91IGNhbiBhbHNvIGR1bW1pZnkgaXQKI2RmX2dlbmRlcml6ZXIgPC0gZGZfZ2VuZGVyaXplcltkZl9nZW5kZXJpemVyJHByb2JhYmlsaXR5ID4gMC45LCBdCgojZGZfZ2VuZGVyaXplciA8LSBzdWJzZXQoZGZfZ2VuZGVyaXplciwgc2VsZWN0ID0gYygibmFtZSIsICJnZW5kZXIiKSkKCiNmc2hvd2RmKGRmX2dlbmRlcml6ZXIpCiNWaWV3KGRmX2dlbmRlcml6ZXIpCgojZGYgfD4KIyAgICBsZWZ0X2pvaW4oZGZfZ2VuZGVyaXplciwgYnkgPSBjKGZpcnN0bmFtZSA9ICJuYW1lIikpIC0+IGRmX2dlbmRlcgoKI2ZzaG93ZGYoZGZfZ2VuZGVyKQoKI2ZzYXZlKGRmX2dlbmRlcikKCmBgYAo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIyBGaWd1cmVkIG91dCBhc3NpZ25pbmcgZ2VuZGVyCmBgYHtyfQojIFJ2ZXN0IHRhYmxlIG9iamVjdCBpbnNwZWN0ZXJlbiBlbiBkaWUgZ2V2ZW4gYWFuIGhldCAKbmFtZSA9ICJFcmlrIgoKIyBCdWlsZCBVUkwgcm9idXN0bHkKYmFzZV91cmwgPC0gImh0dHBzOi8vbnZiLm1lZXJ0ZW5zLmtuYXcubmwvbmFhbS9pcyIKbmFtZV91cmwgPC0gcGFzdGUwKGJhc2VfdXJsLCAiLyIsIG5hbWUpCgp0YWJsZSA8LSByZWFkX2h0bWwobmFtZV91cmwpIHw+CiAgaHRtbF9lbGVtZW50KCJ0YWJsZSIpIHw+CiAgaHRtbF90YWJsZSgpCgoKIyBSZXBsYWNlICItLSIgd2l0aCAiMCIgZXZlcnl3aGVyZQp0YWJsZVt0YWJsZSA9PSAiLS0iXSA8LSAiMCIKCiMgTm93IHlvdSBjYW4gc2FmZWx5IGNvbnZlcnQgdG8gbnVtZXJpYwp2YWxfbWFsZSAgIDwtIGFzLm51bWVyaWModGFibGVbMiwgM10pCnZhbF9mZW1hbGUgPC0gYXMubnVtZXJpYyh0YWJsZVs2LCAzXSkKCiMgQ29tcGFyZSBhbmQgYXNzaWduIGdlbmRlcgpnZW5kZXIgPC0gaWYgKHZhbF9tYWxlID4gdmFsX2ZlbWFsZSkgewogICJtYWxlIgp9IGVsc2UgaWYgKHZhbF9mZW1hbGUgPj0gdmFsX21hbGUpIHsKICAiZmVtYWxlIgp9IGVsc2UgewogIE5BCn0KCmdlbmRlcgoKYGBgCjxicj4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIE1ha2luZyBhIGZ1bmN0aW9uIGdlbmRlcml6ZXIKCmBgYHtyfQoKbXlfZ2VuZGVyaXplciA8LSBmdW5jdGlvbihuYW1lKSB7CiAgbmFtZSA8LSBnc3ViKCJeLyt8LyskIiwgIiIsIG5hbWUpCiAgCiAgIyBCdWlsZCBVUkwKICBiYXNlX3VybCA8LSAiaHR0cHM6Ly9udmIubWVlcnRlbnMua25hdy5ubC9uYWFtL2lzIgogIG5hbWVfdXJsIDwtIHBhc3RlMChiYXNlX3VybCwgIi8iLCBuYW1lKQogIAogICMgVHJ5IHJlYWRpbmcgdGFibGUKICB0YWJsZSA8LSB0cnlDYXRjaCgKICAgIHJlYWRfaHRtbChuYW1lX3VybCkgfD4KICAgICAgaHRtbF9lbGVtZW50KCJ0YWJsZSIpIHw+CiAgICAgIGh0bWxfdGFibGUoKSwKICAgIGVycm9yID0gZnVuY3Rpb24oZSkgTlVMTAogICkKICAKICBpZiAoaXMubnVsbCh0YWJsZSkpIHJldHVybihOQSkgICMgcmV0dXJuIE5BIGlmIG5vIHRhYmxlIGZvdW5kCiAgCiAgIyBSZXBsYWNlICItLSIgd2l0aCAiMCIKICB0YWJsZVt0YWJsZSA9PSAiLS0iXSA8LSAiMCIKICAKICAjIENvbnZlcnQgdG8gbnVtZXJpYwogIHZhbF9tYWxlICAgPC0gc3VwcHJlc3NXYXJuaW5ncyhhcy5udW1lcmljKHRhYmxlWzIsIDNdKSkKICB2YWxfZmVtYWxlIDwtIHN1cHByZXNzV2FybmluZ3MoYXMubnVtZXJpYyh0YWJsZVs2LCAzXSkpCiAgCiAgIyBEZWNpZGUgZ2VuZGVyCiAgaWYgKGlzLm5hKHZhbF9tYWxlKSAmIGlzLm5hKHZhbF9mZW1hbGUpKSB7CiAgICBnZW5kZXIgPC0gTkEKICB9IGVsc2UgaWYgKHZhbF9tYWxlID4gdmFsX2ZlbWFsZSkgewogICAgZ2VuZGVyIDwtICJtYWxlIgogIH0gZWxzZSBpZiAodmFsX2ZlbWFsZSA+IHZhbF9tYWxlKSB7CiAgICBnZW5kZXIgPC0gImZlbWFsZSIKICB9IGVsc2UgewogICAgZ2VuZGVyIDwtIE5BCiAgfQogIAogIHJldHVybihnZW5kZXIpCn0KCnNvY19wb2wgPC0gcmVhZHhsOjpyZWFkX2V4Y2VsKCIvVXNlcnMvbXlsZW5laHVzc29uL0Rlc2t0b3AvU29jaWFsTmV0d29ya3MvbGFiam91cm5hbF9teWxlbmUvZGF0YS8yMDI0MDQxOVNjaG9sYXJpZF9zb2NfcG9sLnhsc3giKQpzb2NfcG9sX3NtYWxsIDwtIHNvY19wb2xbMToyMCwgXQoKc29jX3BvbF9zbWFsbCA8LSBzb2NfcG9sX3NtYWxsIHw+CiAgbXV0YXRlKGZpcnN0bmFtZSA9IHdvcmQoTmFhbSwgMSkpIAoKZm9yIChpIGluIHNlcV9sZW4obnJvdyhzb2NfcG9sX3NtYWxsKSkpIHsKICBuYW1lIDwtIHNvY19wb2xfc21hbGwkZmlyc3RuYW1lW2ldCiAgZ2VuZGVyIDwtIG15X2dlbmRlcml6ZXIobmFtZSkKCiAgc29jX3BvbF9zbWFsbCRnZW5kZXJbaV0gPC0gZ2VuZGVyCn0KCiNWaWV3KHNvY19wb2xfc21hbGwpCmBgYAoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMgVGhlIG5vbWluYXRpb24gTmV0d29yawoKYGBge3J9CiMgbG9hZCBpbiB0aGUgZGF0YQpzY2hvbGFycyA8LSBmbG9hZCgiLi9kYXRhL3Byb2Nlc3NlZC9zY2hvbGFyc18yMDI0MDkyNC5yZGEiKQoKIyBzZWxlY3Qgc2Nob2xhcnMKIyAoc29jaW9sb2d5LCBSVSkKZGVtb2dyYXBoaWNzIDwtIGRvLmNhbGwocmJpbmQuZGF0YS5mcmFtZSwgc2Nob2xhcnMkZGVtb2dyYXBoaWNzKQpkZW1vZ3JhcGhpY3MgPC0gZGVtb2dyYXBoaWNzICU+JQogICAgbXV0YXRlKFVuaXZlcnNpdGVpdDEuMjIgPSByZXBsYWNlKFVuaXZlcnNpdGVpdDEuMjIsIGlzLm5hKFVuaXZlcnNpdGVpdDEuMjIpLCAiIiksIFVuaXZlcnNpdGVpdDIuMjIgPSByZXBsYWNlKFVuaXZlcnNpdGVpdDIuMjIsCiAgICAgICAgaXMubmEoVW5pdmVyc2l0ZWl0Mi4yMiksICIiKSwgVW5pdmVyc2l0ZWl0MS4yNCA9IHJlcGxhY2UoVW5pdmVyc2l0ZWl0MS4yNCwgaXMubmEoVW5pdmVyc2l0ZWl0MS4yNCksCiAgICAgICAgIiIpLCBVbml2ZXJzaXRlaXQyLjI0ID0gcmVwbGFjZShVbml2ZXJzaXRlaXQyLjI0LCBpcy5uYShVbml2ZXJzaXRlaXQyLjI0KSwgIiIpLCBkaXNjaXBsaW5lLjIyID0gcmVwbGFjZShkaXNjaXBsaW5lLjIyLAogICAgICAgIGlzLm5hKGRpc2NpcGxpbmUuMjIpLCAiIiksIGRpc2NpcGxpbmUuMjQgPSByZXBsYWNlKGRpc2NpcGxpbmUuMjQsIGlzLm5hKGRpc2NpcGxpbmUuMjQpLCAiIikpCgpzYW1wbGUgPC0gd2hpY2goKGRlbW9ncmFwaGljcyRVbml2ZXJzaXRlaXQxLjIyID09ICJSVSIgfCBkZW1vZ3JhcGhpY3MkVW5pdmVyc2l0ZWl0Mi4yMiA9PSAiUlUiIHwgZGVtb2dyYXBoaWNzJFVuaXZlcnNpdGVpdDEuMjQgPT0KICAgICJSVSIgfCBkZW1vZ3JhcGhpY3MkVW5pdmVyc2l0ZWl0Mi4yNCA9PSAiUlUiKSAmIChkZW1vZ3JhcGhpY3MkZGlzY2lwbGluZS4yMiA9PSAic29jaW9sb2d5IiB8IGRlbW9ncmFwaGljcyRkaXNjaXBsaW5lLjI0ID09CiAgICAic29jaW9sb2d5IikpCgpkZW1vZ3JhcGhpY3Nfc29jIDwtIGRlbW9ncmFwaGljc1tzYW1wbGUsIF0Kc2Nob2xhcnNfc2VsIDwtIGxhcHBseShzY2hvbGFycywgIlsiLCBzYW1wbGUpCgojIGNvbnN0cnVjdCB0aGUgZW1wdHkgYWRqYWNlbmN5IG1hdHJpeAppZHMgPC0gZGVtb2dyYXBoaWNzX3NvYyRhdV9pZAp3YXZlMiA8LSB3YXZlMSA8LSBtYXRyaXgoMCwgbnJvdyA9IGxlbmd0aChpZHMpLCBuY29sID0gbGVuZ3RoKGlkcyksIGRpbW5hbWVzID0gbGlzdChpZHMsIGlkcykpCgojIGZpbHRlciB3b3Jrcwp3b3JrcyA8LSBzY2hvbGFyc19zZWwkd29yawoKd29ya3NfaWQgPC0gdW5saXN0KGxhcHBseSh3b3JrcywgZnVuY3Rpb24obCkgbCRpZCkpCndvcmtzX2F1dGhvciA8LSB1bmxpc3QobGFwcGx5KHdvcmtzLCBmdW5jdGlvbihsKSBsJGF1dGhvciksIHJlY3Vyc2l2ZSA9IEZBTFNFKQp3b3Jrc195ZWFyIDwtIHVubGlzdChsYXBwbHkod29ya3MsIGZ1bmN0aW9uKGwpIGwkcHVibGljYXRpb25feWVhciksIHJlY3Vyc2l2ZSA9IEZBTFNFKQoKZGZfd29ya3MgPC0gdGliYmxlKHdvcmtzX2lkLCB3b3Jrc19hdXRob3IsIHdvcmtzX3llYXIpCgpkdXBzIDwtIHdoaWNoKGR1cGxpY2F0ZWQod29ya3NfaWQpKQoKZGZfd29ya3MgPC0gZGZfd29ya3NbLWR1cHMsIF0KZGZfd29ya3NfdzIgPC0gZGZfd29ya3NbZGZfd29ya3Mkd29ya3NfeWVhciA+IDIwMTksIF0KCiMgdW5kaXJlY3RlZAojIHVuZGlyZWN0ZWQKZm9yIChpIGluIDE6bnJvdyhkZl93b3Jrc193MikpIHsKICAgIGVnb3MgPC0gZGZfd29ya3NfdzIkd29ya3NfYXV0aG9yW2ldW1sxXV0kYXVfaWQKCiAgICBpZiAoc3VtKGlkcyAlaW4lIGVnb3MpID4gMCkgewogICAgICAgIHdhdmUyW3doaWNoKGlkcyAlaW4lIGVnb3MpLCB3aGljaChpZHMgJWluJSBlZ29zKV0gPC0gMQogICAgfQp9CmBgYAo8YnI+CgpOb3cgb25lIGZ1bmN0aW9uIHRvIHJ1bGUgdGhlbSBhbGw6CgpgYGB7cn0KZmNvbG5ldCA8LSBmdW5jdGlvbihkYXRhID0gc2Nob2xhcnMsIHVuaXZlcnNpdHkgPSAiUlUiLCBkaXNjaXBsaW5lID0gInNvY2lvbG9neSIsIHdhdmVzID0gbGlzdChjKDIwMTUsCiAgICAyMDE4KSwgYygyMDE5LCAyMDIzKSksIHR5cGUgPSBjKCJmaXJzdCIpKSB7CgogICAgIyBzdGVwIDEKICAgIGRlbW9ncmFwaGljcyA8LSBkby5jYWxsKHJiaW5kLmRhdGEuZnJhbWUsIGRhdGEkZGVtb2dyYXBoaWNzKQogICAgZGVtb2dyYXBoaWNzIDwtIGRlbW9ncmFwaGljcyAlPiUKICAgICAgICBtdXRhdGUoVW5pdmVyc2l0ZWl0MS4yMiA9IHJlcGxhY2UoVW5pdmVyc2l0ZWl0MS4yMiwgaXMubmEoVW5pdmVyc2l0ZWl0MS4yMiksICIiKSwgVW5pdmVyc2l0ZWl0Mi4yMiA9IHJlcGxhY2UoVW5pdmVyc2l0ZWl0Mi4yMiwKICAgICAgICAgICAgaXMubmEoVW5pdmVyc2l0ZWl0Mi4yMiksICIiKSwgVW5pdmVyc2l0ZWl0MS4yNCA9IHJlcGxhY2UoVW5pdmVyc2l0ZWl0MS4yNCwgaXMubmEoVW5pdmVyc2l0ZWl0MS4yNCksCiAgICAgICAgICAgICIiKSwgVW5pdmVyc2l0ZWl0Mi4yNCA9IHJlcGxhY2UoVW5pdmVyc2l0ZWl0Mi4yNCwgaXMubmEoVW5pdmVyc2l0ZWl0Mi4yNCksICIiKSwgZGlzY2lwbGluZS4yMiA9IHJlcGxhY2UoZGlzY2lwbGluZS4yMiwKICAgICAgICAgICAgaXMubmEoZGlzY2lwbGluZS4yMiksICIiKSwgZGlzY2lwbGluZS4yNCA9IHJlcGxhY2UoZGlzY2lwbGluZS4yNCwgaXMubmEoZGlzY2lwbGluZS4yNCksICIiKSkKCiAgICBzYW1wbGUgPC0gd2hpY2goKGRlbW9ncmFwaGljcyRVbml2ZXJzaXRlaXQxLjIyICVpbiUgdW5pdmVyc2l0eSB8IGRlbW9ncmFwaGljcyRVbml2ZXJzaXRlaXQyLjIyICVpbiUKICAgICAgICB1bml2ZXJzaXR5IHwgZGVtb2dyYXBoaWNzJFVuaXZlcnNpdGVpdDEuMjQgJWluJSB1bml2ZXJzaXR5IHwgZGVtb2dyYXBoaWNzJFVuaXZlcnNpdGVpdDIuMjQgJWluJQogICAgICAgIHVuaXZlcnNpdHkpICYgKGRlbW9ncmFwaGljcyRkaXNjaXBsaW5lLjIyICVpbiUgZGlzY2lwbGluZSB8IGRlbW9ncmFwaGljcyRkaXNjaXBsaW5lLjI0ICVpbiUgZGlzY2lwbGluZSkpCgogICAgZGVtb2dyYXBoaWNzX3NvYyA8LSBkZW1vZ3JhcGhpY3Nbc2FtcGxlLCBdCiAgICBzY2hvbGFyc19zZWwgPC0gbGFwcGx5KHNjaG9sYXJzLCAiWyIsIHNhbXBsZSkKCiAgICAjIHN0ZXAgMgogICAgaWRzIDwtIGRlbW9ncmFwaGljc19zb2MkYXVfaWQKICAgIG53YXZlcyA8LSBsZW5ndGgod2F2ZXMpCiAgICBuZXRzIDwtIGFycmF5KDAsIGRpbSA9IGMobndhdmVzLCBsZW5ndGgoaWRzKSwgbGVuZ3RoKGlkcykpLCBkaW1uYW1lcyA9IGxpc3Qod2F2ZSA9IDE6bndhdmVzLCBpZHMsCiAgICAgICAgaWRzKSkKICAgIGRpbW5hbWVzKG5ldHMpCgogICAgIyBzdGVwIDMKICAgIGRmX3dvcmtzIDwtIHRpYmJsZSh3b3Jrc19pZCA9IHVubGlzdChsYXBwbHkoc2Nob2xhcnNfc2VsJHdvcmssIGZ1bmN0aW9uKGwpIGwkaWQpKSwgd29ya3NfYXV0aG9yID0gdW5saXN0KGxhcHBseShzY2hvbGFyc19zZWwkd29yaywKICAgICAgICBmdW5jdGlvbihsKSBsJGF1dGhvciksIHJlY3Vyc2l2ZSA9IEZBTFNFKSwgd29ya3NfeWVhciA9IHVubGlzdChsYXBwbHkoc2Nob2xhcnNfc2VsJHdvcmssIGZ1bmN0aW9uKGwpIGwkcHVibGljYXRpb25feWVhciksCiAgICAgICAgcmVjdXJzaXZlID0gRkFMU0UpKQoKICAgIGRmX3dvcmtzIDwtIGRmX3dvcmtzWyFkdXBsaWNhdGVkKGRmX3dvcmtzKSwgXQoKICAgICMgc3RlcCA0CiAgICBpZiAodHlwZSA9PSAiZmlyc3QiKSB7CiAgICAgICAgZm9yIChqIGluIDE6bndhdmVzKSB7CiAgICAgICAgICAgIGRmX3dvcmtzX3cgPC0gZGZfd29ya3NbZGZfd29ya3Mkd29ya3NfeWVhciA+PSB3YXZlc1tbal1dWzFdICYgZGZfd29ya3Mkd29ya3NfeWVhciA8PSB3YXZlc1tbal1dWzJdLAogICAgICAgICAgICAgICAgXQogICAgICAgICAgICBmb3IgKGkgaW4gMTpucm93KGRmX3dvcmtzX3cpKSB7CiAgICAgICAgICAgICAgICBlZ28gPC0gZGZfd29ya3NfdyR3b3Jrc19hdXRob3JbaV1bWzFdXSRhdV9pZFsxXQogICAgICAgICAgICAgICAgYWx0ZXJzIDwtIGRmX3dvcmtzX3ckd29ya3NfYXV0aG9yW2ldW1sxXV0kYXVfaWRbLTFdCiAgICAgICAgICAgICAgICBpZiAoc3VtKGlkcyAlaW4lIGVnbykgPiAwICYgc3VtKGlkcyAlaW4lIGFsdGVycykgPiAwKSB7CiAgICAgICAgICAgICAgICAgIG5ldHNbaiwgd2hpY2goaWRzICVpbiUgZWdvKSwgd2hpY2goaWRzICVpbiUgYWx0ZXJzKV0gPC0gMQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgfQoKICAgIGlmICh0eXBlID09ICJsYXN0IikgewogICAgICAgIGZvciAoaiBpbiAxOm53YXZlcykgewogICAgICAgICAgICBkZl93b3Jrc193IDwtIGRmX3dvcmtzW2RmX3dvcmtzJHdvcmtzX3llYXIgPj0gd2F2ZXNbW2pdXVsxXSAmIGRmX3dvcmtzJHdvcmtzX3llYXIgPD0gd2F2ZXNbW2pdXVsyXSwKICAgICAgICAgICAgICAgIF0KICAgICAgICAgICAgZm9yIChpIGluIDE6bnJvdyhkZl93b3Jrc193KSkgewogICAgICAgICAgICAgICAgZWdvIDwtIHJldihkZl93b3Jrc193JHdvcmtzX2F1dGhvcltpXVtbMV1dJGF1X2lkKVsxXQogICAgICAgICAgICAgICAgYWx0ZXJzIDwtIHJldihkZl93b3Jrc193JHdvcmtzX2F1dGhvcltpXVtbMV1dJGF1X2lkKVstMV0KICAgICAgICAgICAgICAgIGlmIChzdW0oaWRzICVpbiUgZWdvKSA+IDAgJiBzdW0oaWRzICVpbiUgYWx0ZXJzKSA+IDApIHsKICAgICAgICAgICAgICAgICAgbmV0c1tqLCB3aGljaChpZHMgJWluJSBlZ28pLCB3aGljaChpZHMgJWluJSBhbHRlcnMpXSA8LSAxCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9CgogICAgaWYgKHR5cGUgPT0gImFsbCIpIHsKICAgICAgICBmb3IgKGogaW4gMTpud2F2ZXMpIHsKICAgICAgICAgICAgZGZfd29ya3NfdyA8LSBkZl93b3Jrc1tkZl93b3JrcyR3b3Jrc195ZWFyID49IHdhdmVzW1tqXV1bMV0gJiBkZl93b3JrcyR3b3Jrc195ZWFyIDw9IHdhdmVzW1tqXV1bMl0sCiAgICAgICAgICAgICAgICBdCiAgICAgICAgICAgIGZvciAoaSBpbiAxOm5yb3coZGZfd29ya3NfdykpIHsKICAgICAgICAgICAgICAgIGVnb3MgPC0gZGZfd29ya3NfdyR3b3Jrc19hdXRob3JbaV1bWzFdXSRhdV9pZAogICAgICAgICAgICAgICAgaWYgKHN1bShpZHMgJWluJSBlZ29zKSA+IDApIHsKICAgICAgICAgICAgICAgICAgbmV0c1tqLCB3aGljaChpZHMgJWluJSBlZ29zKSwgd2hpY2goaWRzICVpbiUgZWdvcyldIDwtIDEKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0KICAgIG91dHB1dCA8LSBsaXN0KCkKICAgIG91dHB1dCRkYXRhIDwtIHNjaG9sYXJzX3NlbAogICAgb3V0cHV0JG5ldHMgPC0gbmV0cwogICAgcmV0dXJuKG91dHB1dCkKfQpgYGAKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMgQXNzaWduaW5nIEdlbmRlciB0byBkZW1vZ3JhcGhpY3MKYGBge3J9CgpkZl9zY2hvbGFycyA8LSBkby5jYWxsKHJiaW5kLCBzY2hvbGFycyRkZW1vZ3JhcGhpY3MpCgojVmlldyhkZl9zY2hvbGFycykKCmRmX3NjaG9sYXJzMiA8LSBkZl9zY2hvbGFycyB8PgogIG11dGF0ZShmaXJzdG5hbWUgPSB3b3JkKE5hYW0sIDEpKSAgIyBleHRyYWN0IGZpcnN0IG5hbWVzCgpkZl9zY2hvbGFyczIkZ2VuZGVyIDwtIE5BX2NoYXJhY3Rlcl8gICMgaW5pdGlhbGl6ZSBnZW5kZXIgY29sdW1uCgpmb3IgKGkgaW4gc2VxX2xlbihucm93KGRmX3NjaG9sYXJzMikpKSB7CiAgbmFtZSA8LSBkZl9zY2hvbGFyczIkZmlyc3RuYW1lW2ldCiAgZ2VuZGVyIDwtIG15X2dlbmRlcml6ZXIobmFtZSkKICAKICBkZl9zY2hvbGFyczIkZ2VuZGVyW2ldIDwtIGdlbmRlcgogIFN5cy5zbGVlcCgwLjEpICAjIHBhdXNlIGJldHdlZW4gcmVxdWVzdHMKfQoKI1ZpZXcoZGZfc2Nob2xhcnMyKQoKYGBgCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIyBXaG8gd29ya3MgdG9nZXRoZXIgd2l0aCB3aG9tPwpgYGB7cn0KCgpgYGAKCg==