Data Preparation

Load required packages and read in data, removing unwanted columns exported automatically from Survey Monkey and rename variables whose names didn’t get properly imported.

packages <- c("likert", "poLCA", "stringr", "ggplot2", "tidyverse", "dplyr")
x = sapply(packages, function(x) if(!require(x, character.only = T)) install.packages(x))
Loading required package: poLCA
package 㤼㸱poLCA㤼㸲 was built under R version 3.3.3Loading required package: scatterplot3d
package 㤼㸱scatterplot3d㤼㸲 was built under R version 3.3.3Loading required package: MASS
Loading required package: stringr
package 㤼㸱stringr㤼㸲 was built under R version 3.3.3Loading required package: tidyverse
package 㤼㸱tidyverse㤼㸲 was built under R version 3.3.3Loading tidyverse: tibble
Loading tidyverse: tidyr
Loading tidyverse: readr
Loading tidyverse: purrr
Loading tidyverse: dplyr
package 㤼㸱purrr㤼㸲 was built under R version 3.3.3Conflicts with tidy packages ----------------------------------------------------
filter(): dplyr, stats
lag():    dplyr, stats
recode(): dplyr, likert
select(): dplyr, MASS

Data Wrangling

Calculate how many years of experience the person had when they started the job and put it next to the other year columns.

dat <- dat %>% 
  mutate(`Years experience when started position` = `Years in librarianship` - `Years in current position`) %>% 
  select(1:3, `Years experience when started position`, everything())
Error in select(., 1:3, `Years experience when started position`, everything()) : 
  unused arguments (1:3, `Years experience when started position`, everything())

Convert likert scale variables to ordered factors for easier analysis.

levs <- c("Don't know or N/A", "Not at all important", "Slightly important", "Important", "Very important", "Absolutely essential")
dat[21:68] <- lapply(dat[21:68], parse_factor, levels = levs)

Survey Monkey does some weird things with columns that allow multiple selections from a list, so clean those up by replacing all non-NA values with a 1.

fix_rows <- c(5:10, 14:20) #select the offending rows
dat[fix_rows] <- replace(dat[fix_rows], !is.na(dat[fix_rows]), 1) #make the replacement
dat[fix_rows] <- replace(dat[fix_rows], is.na(dat[fix_rows]), 0)
dat[fix_rows] <- sapply(dat[fix_rows], as.numeric) #the replacement gets created as a character var, so switch it to numeric

Some people didn’t complete any of the likert scale items at all, so their responses aren’t really very helpful. Partial responses are okay but remove anyone who didn’t answer any likert scale questions.

ind <- apply(dat[21:68], 1, function(x) all(is.na(x)))
dat <- dat[ !ind, ]

Demographic analysis

Looking at some characteristics of the people who responded.

Job titles

dat$`Job title` <- tolower(dat$`Job title`)
job_titles <- data.frame(table(dat$`Job title`))
word_list <- c("librarian", "data services", "data management", "research data", "informationist", "manager", "director")
for (i in 1:length(word_list)) {
  sentence <- paste(length(grep(word_list[i], dat$`Job title`)), "job titles contain the word", word_list[i])
  print(sentence)
}
[1] "47 job titles contain the word librarian"
[1] "13 job titles contain the word data services"
[1] "5 job titles contain the word data management"
[1] "11 job titles contain the word research data"
[1] "6 job titles contain the word informationist"
[1] "5 job titles contain the word manager"
[1] "5 job titles contain the word director"

Years of experience

Some visualizations of how long people have been in their current position and stuff. Filter out anyone who put they had been in their current position longer than their whole career.

fixed_dat <- dat %>% filter(`Years in librarianship` >= `Years in current position`) 
require(gridExtra)
Loading required package: gridExtra
package 㤼㸱gridExtra㤼㸲 was built under R version 3.3.3
Attaching package: 㤼㸱gridExtra㤼㸲

The following object is masked from 㤼㸱package:dplyr㤼㸲:

    combine
plot1 <- ggplot(fixed_dat, aes(x = `Years in current position`)) + geom_histogram() + ggtitle("Years in Current Position") + xlab("Years") + ylab("Number of Respondents") + theme_bw() + theme(plot.title = element_text(hjust = 0.5))
plot2 <- ggplot(fixed_dat, aes(x = `Years in librarianship`)) + geom_histogram() + ggtitle("Years in Librarianship Total") + xlab("Years") + ylab("Number of Respondents") + theme_bw() + theme(plot.title = element_text(hjust = 0.5))
grid.arrange(plot1, plot2, ncol=2)

For how many people is this their first library job, i.e. years in librarianship is equal to years in current position?

n_first <- dat %>% filter(`Years in librarianship` == `Years in current position`) %>% nrow
For 20 participants, their current job is their first in the field of librarianship.

Educational Experience

How many people have various degrees? Sum up the counts and plot them as a percentage.

62 participants havemore than one degree 
The mean number of degrees held is 2.216867

Areas supported

What disciplines do people work in?

How many people are supporting more than one discipline?

dat <- dat %>% 
  mutate(disciplines_served = `Biomedical and/or health sciences` + `Life sciences` + `Physical sciences`+ `Mathematics and/or statistics` + `Engineering and/or computer science` + `Social sciences`)
n_disciplines <- dat %>% filter(disciplines_served > 1) %>% nrow
mean_disciplines <- mean(dat$disciplines_served)
56 participants serve more than one discipline 
The mean number of disciplines served is 2.831325

Time spent on data vs non data activities

First remove any NA entries then make charts.

Analysis of Skill/Knowledge Importance Ratings

Do some data cleaning first for the likert package.

test <- for (i in 21:68) {
  levels(dat_df[i])[levels(dat_df[i] == "Don't know or N/A"] <- NA
Error: unexpected ']' in:
"test <- for (i in 21:68) {
  levels(dat_df[i])[levels(dat_df[i] == "Don't know or N/A"]"

Break the different skills/knowledge areas out into their category groupings to make this a little easier. First get the demographic info we want in all the data subsets, then pull out the category subsets. There’s too many in the data management category and the charts look crappy so break it into two.

demo_rows <- 1:10
data_mgmt1 <- 21:25
data_mgmt2 <- 26:30
tech <- 31:35
eval_assess <- 36:38
teaching <- 39:43
library <- 44:49
outreach <- 50:53
involvement <- 54:56
personal_attrs <- 57:63
education <- 64:68
subset_cols <- list(data_mgmt1, data_mgmt2, tech, eval_assess, teaching, library, outreach, involvement, personal_attrs, education)
##come back later and pretty up these names for your plots
names(subset_cols) <- c("data_mgmt1", "data_mgmt2", "tech", "eval_assess", "teaching", "library", "outreach", "involvement", "personal_attrs", "education")

Likert scale charts

Note: the likert package can’t handle tibbles, so convert to a data frame when you pass it to the likert function.

for (i in 1:length(subset_cols)) {
  col_nums <- unlist(subset_cols[i])
  lik_dat <- likert(dat_df[col_nums])
  p <- plot(lik_dat) + ggtitle(names(subset_cols)[i]) + scale_fill_grey()
  print(p)
}
Scale for 'fill' is already present. Adding another scale for
'fill', which will replace the existing scale.

Charting How many skills people think are important for various categories

for (i in 1:length(subset_cols)) {
  g_name <- names(subset_cols)[i]
  g_rows <- subset_cols[[i]]
  t_all$group[g_rows, ] <- g_name
}
Error in t_all$group[g_rows, ] <- g_name : 
  incorrect number of subscripts on matrix

Breaking down the analysis by demographics

Do people who spend more than half their time doing data related work feel differently about what’s important?

dat_df$majority_data_time <- ifelse(dat_df$`Percent of time spent on data-related work` >= 50,
c("yes"), c("no")) 
dat_df$majority_data_time <- as.factor(dat_df$majority_data_time)

dat_df <- dat_df[!is.na(dat_df$majority_data_time), ]

Make the same Likert scales but broken out by group.

for (i in 1:length(subset_cols)) {
  col_nums <- unlist(subset_cols[i])
  lik_dat <- likert(dat_df[, col_nums], grouping = dat_df$majority_data_time)
  p <- plot(lik_dat) + ggtitle(names(subset_cols)[i])
  print(p)
}

PhD vs none

Some job ads specifically ask for a PhD. Let’s see if there’s anything different about the people who have one or don’t.

MLIS vs none

dat_df$hasmlis <- ifelse(dat_df$`ALA-accredited master’s degree` == 1,
c("yes"), c("no")) 
dat_df$hasmlis <- as.factor(dat_df$hasmlis)
dat_df <- dat_df[!is.na(dat_df$hasmlis), ]

for (i in 1:length(subset_cols)) {
  col_nums <- unlist(subset_cols[i])
  lik_dat <- likert(dat_df[, col_nums], grouping = dat_df$hasmlis)
  p <- plot(lik_dat) + ggtitle(names(subset_cols)[i]) + theme(text = element_text(size = 6))
  print(p)
}

Breakdown by disciplines supported

Latent Class Analysis

Prepare the data for the analysis

Convert the likert data to a numeric scale, with 1 being low, 5 being high, and 6 NA. Some levels didn’t have any don’t know/NA so add a 6 level just in case. Also, I think we need to collapse this into just 3 levels instead of 5 to make the model work.

dat_lca <- dat
dat_lca[21:68] <- lapply(dat_lca[21:68], recode_factor, 
"Don't know or N/A" = "6", "Not at all important" = "1", "Slightly important" = "2", "Important" = "3", "Very important" = "4", "Absolutely essential" = "5")

##We used dat for this rather than dat_df so add in the majority data tiome variable
dat_lca$majority_data_time <- ifelse(dat_lca$`Percent of time spent on data-related work` >= 50,
c("yes"), c("no")) 
dat_lca$majority_data_time <- as.factor(dat_lca$majority_data_time)

Add 1 to all 0/1 columns so they are 1/2 as per instructions. Also make the majority of time on data a numeric column.

recode_factor(dat_lca$majority_data_time, "yes" = "2", "no" = "1")
 [1] 2    <NA> 2    1    2    1    1    2    2    <NA> 2    2   
[13] 2    1    2    1    1    2    1    2    2    2    2    1   

Remove unwanted columns

drops <- c(11:13, 69:70)
dat_lca <- dat_lca[-drops]

Replace any remaining NAs with 6

dat_lca[is.na(dat_lca)] <- 6
invalid factor level, NA generated

K-Means and One Hot Encoding

Make a new converting all likert scale responses to one-hot encoded variables, then add back in the non-likert variables. Also discard that one where the person said they’ve been in their current position longer than in librarianship total.

library(ade4)
one_hot <- acm.disjonctif(dat_df[1:82, 21:68])
one_hot <- cbind(dat_df[1:82, c(1:3, 5:10, 12:20)], one_hot)

Calculate the similarities using gower distance

This suggests 2 clusters is best.

pam_fit <- pam(gower_dist, diss = TRUE, k = 2)
pam_results <- one_hot %>%
  dplyr::select(-`Job title`) %>%
  mutate(cluster = pam_fit$clustering) %>%
  group_by(cluster) %>%
  do(the_summary = summary(.))
Error: could not find function "%>%"

Trying a different implementation of this

library(fpc)
pc = pamk(gower_dist, krange=1:5, criterion="asw")
pc[2:3]
$nc
[1] 2

$crit
[1]  0.000000000  0.022472510 -0.005254221 -0.013158479 -0.013707184
hc.m = hclust(gower_dist, method="median")
hc.s = hclust(gower_dist, method="single")
hc.c = hclust(gower_dist, method="complete")
plot(hc.m)

plot(hc.s)

plot(hc.c)

table(cutree(hc.m, k=2), cutree(hc.s, k=2))
   
     1  2
  1 80  1
  2  1  0

Looking at the complete clustering.

dat_df$group[group1l, ] <- 1
Error in dat_df$group[group1l, ] <- 1 : 
  incorrect number of subscripts on matrix

Look at groupings

group1 <- group1 %>% 
  mutate(disciplines_served = `Biomedical and/or health sciences` + `Life sciences` + `Physical sciences`+ `Mathematics and/or statistics` + `Engineering and/or computer science` + `Social sciences`)
n_disciplines1 <- group1 %>% filter(disciplines_served > 1) %>% nrow
mean_disciplines1 <- mean(group1$disciplines_served)

group2 <- group2 %>% 
  mutate(disciplines_served = `Biomedical and/or health sciences` + `Life sciences` + `Physical sciences`+ `Mathematics and/or statistics` + `Engineering and/or computer science` + `Social sciences`)
n_disciplines2 <- group2 %>% filter(disciplines_served > 1) %>% nrow
mean_disciplines2 <- mean(group2$disciplines_served)
13 participants in Group 1 serve more than one discipline 
The mean number of disciplines served is 1.964286 .  43 participants in Group 2 serve more than one discipline 
The mean number of disciplines served is 3.314815

group1 <- group1 %>% 
  mutate(degrees_held = `ALA-accredited master’s degree` + `Science master’s degree` + `Other non-ALA, non-science master’s degree` + `Undergraduate science degree` + `PhD (any discipline)` + `Specialized librarianship certification (such as data or medical library certification)` + `Other non-degree, non-certificate training in data, science, or specialized librarianship`)
n_degrees1 <- group1 %>% filter(degrees_held > 1) %>% nrow
mean_degrees1 <- mean(group1$degrees_held)

group2 <- group2 %>% 
  mutate(degrees_held = `ALA-accredited master’s degree` + `Science master’s degree` + `Other non-ALA, non-science master’s degree` + `Undergraduate science degree` + `PhD (any discipline)` + `Specialized librarianship certification (such as data or medical library certification)` + `Other non-degree, non-certificate training in data, science, or specialized librarianship`)
n_degrees2 <- group2 %>% filter(degrees_held > 1) %>% nrow
mean_degrees2 <- mean(group2$degrees_held)
15 participants in Group 1 hvae more than one degree 
The mean number of degrees held is 1.75 .  43 participants in Group 2 have more than one degree 
The mean number of degrees held is 2.481481

n_first1 <- group1 %>% filter(`Years in librarianship` == `Years in current position`) %>% nrow
n_first2 <- group2 %>% filter(`Years in librarianship` == `Years in current position`) %>% nrow
For 7 Specialists, their current job is their first in the field of librarianship.  For 13 generalists their current job is their first in the field.

group1$majority_data_time <- ifelse(group1$`Percent of time spent on data-related work` >= 50,
c("yes"), c("no")) 
㤼㸱>=㤼㸲 not meaningful for factors
group1$majority_data_time <- as.factor(group1$majority_data_time)
g1 <- gather(g1, value, n, -title) %>%
  group_by(title, value, n) %>%
  tally
Using n as weighting variable
Error in summarise_impl(.data, dots) : 
  invalid 'type' (character) of argument
LS0tDQp0aXRsZTogIkRhdGEgTGlicmFyaWFuIENvbXBldGVuY2llcyINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KICBodG1sX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KI0RhdGEgUHJlcGFyYXRpb24NCkxvYWQgcmVxdWlyZWQgcGFja2FnZXMgYW5kIHJlYWQgaW4gZGF0YSwgcmVtb3ZpbmcgdW53YW50ZWQgY29sdW1ucyBleHBvcnRlZCBhdXRvbWF0aWNhbGx5IGZyb20gU3VydmV5IE1vbmtleSBhbmQgcmVuYW1lIHZhcmlhYmxlcyB3aG9zZSBuYW1lcyBkaWRuJ3QgZ2V0IHByb3Blcmx5IGltcG9ydGVkLiAgDQpgYGB7ciBtZXNzYWdlID0gRiwgd2FybmluZz1GfQ0KcGFja2FnZXMgPC0gYygibGlrZXJ0IiwgInBvTENBIiwgInN0cmluZ3IiLCAiZ2dwbG90MiIsICJ0aWR5dmVyc2UiLCAiZHBseXIiKQ0KeCA9IHNhcHBseShwYWNrYWdlcywgZnVuY3Rpb24oeCkgaWYoIXJlcXVpcmUoeCwgY2hhcmFjdGVyLm9ubHkgPSBUKSkgaW5zdGFsbC5wYWNrYWdlcyh4KSkNCg0KDQpkYXQgPC0gcmVhZF9jc3YoIlNoZWV0XzEuY3N2Iiwgc2tpcCA9IDEpICU+JSANCiAgc2VsZWN0KC1jKDE6OSkpICU+JSANCiAgcmVuYW1lKGBKb2IgdGl0bGVgID0gYE9wZW4tRW5kZWQgUmVzcG9uc2VgLCBgWWVhcnMgaW4gY3VycmVudCBwb3NpdGlvbmAgPSBgT3Blbi1FbmRlZCBSZXNwb25zZV8xYCwgYFllYXJzIGluIGxpYnJhcmlhbnNoaXBgID0gYE9wZW4tRW5kZWQgUmVzcG9uc2VfMmAsIGBPdGhlciBpbXBvcnRhbnQgc2tpbGxzYCA9IGBPcGVuLUVuZGVkIFJlc3BvbnNlXzNgLCBgQWRkaXRpb25hbCB0aG91Z2h0cyBvciBjb21tZW50c2AgPSBgT3Blbi1FbmRlZCBSZXNwb25zZV80YCkgDQoNCmBgYA0KDQojI0RhdGEgV3JhbmdsaW5nDQpDYWxjdWxhdGUgaG93IG1hbnkgeWVhcnMgb2YgZXhwZXJpZW5jZSB0aGUgcGVyc29uIGhhZCB3aGVuIHRoZXkgc3RhcnRlZCB0aGUgam9iIGFuZCBwdXQgaXQgbmV4dCB0byB0aGUgb3RoZXIgeWVhciBjb2x1bW5zLg0KYGBge3J9DQpkYXQgPC0gZGF0ICU+JSANCiAgbXV0YXRlKGBZZWFycyBleHBlcmllbmNlIHdoZW4gc3RhcnRlZCBwb3NpdGlvbmAgPSBgWWVhcnMgaW4gbGlicmFyaWFuc2hpcGAgLSBgWWVhcnMgaW4gY3VycmVudCBwb3NpdGlvbmApICU+JSANCiAgc2VsZWN0KDE6MywgYFllYXJzIGV4cGVyaWVuY2Ugd2hlbiBzdGFydGVkIHBvc2l0aW9uYCwgZXZlcnl0aGluZygpKQ0KYGBgDQoNCkNvbnZlcnQgbGlrZXJ0IHNjYWxlIHZhcmlhYmxlcyB0byBvcmRlcmVkIGZhY3RvcnMgZm9yIGVhc2llciBhbmFseXNpcy4NCmBgYHtyfQ0KbGV2cyA8LSBjKCJEb24ndCBrbm93IG9yIE4vQSIsICJOb3QgYXQgYWxsIGltcG9ydGFudCIsICJTbGlnaHRseSBpbXBvcnRhbnQiLCAiSW1wb3J0YW50IiwgIlZlcnkgaW1wb3J0YW50IiwgIkFic29sdXRlbHkgZXNzZW50aWFsIikNCg0KZGF0WzIxOjY4XSA8LSBsYXBwbHkoZGF0WzIxOjY4XSwgcGFyc2VfZmFjdG9yLCBsZXZlbHMgPSBsZXZzKQ0KYGBgDQoNClN1cnZleSBNb25rZXkgZG9lcyBzb21lIHdlaXJkIHRoaW5ncyB3aXRoIGNvbHVtbnMgdGhhdCBhbGxvdyBtdWx0aXBsZSBzZWxlY3Rpb25zIGZyb20gYSBsaXN0LCBzbyBjbGVhbiB0aG9zZSB1cCBieSByZXBsYWNpbmcgYWxsIG5vbi1OQSB2YWx1ZXMgd2l0aCBhIDEuDQpgYGB7cn0NCmZpeF9yb3dzIDwtIGMoNToxMCwgMTQ6MjApICNzZWxlY3QgdGhlIG9mZmVuZGluZyByb3dzDQpkYXRbZml4X3Jvd3NdIDwtIHJlcGxhY2UoZGF0W2ZpeF9yb3dzXSwgIWlzLm5hKGRhdFtmaXhfcm93c10pLCAxKSAjbWFrZSB0aGUgcmVwbGFjZW1lbnQNCmRhdFtmaXhfcm93c10gPC0gcmVwbGFjZShkYXRbZml4X3Jvd3NdLCBpcy5uYShkYXRbZml4X3Jvd3NdKSwgMCkNCmRhdFtmaXhfcm93c10gPC0gc2FwcGx5KGRhdFtmaXhfcm93c10sIGFzLm51bWVyaWMpICN0aGUgcmVwbGFjZW1lbnQgZ2V0cyBjcmVhdGVkIGFzIGEgY2hhcmFjdGVyIHZhciwgc28gc3dpdGNoIGl0IHRvIG51bWVyaWMNCg0KYGBgDQpTb21lIHBlb3BsZSBkaWRuJ3QgY29tcGxldGUgYW55IG9mIHRoZSBsaWtlcnQgc2NhbGUgaXRlbXMgYXQgYWxsLCBzbyB0aGVpciByZXNwb25zZXMgYXJlbid0IHJlYWxseSB2ZXJ5IGhlbHBmdWwuICBQYXJ0aWFsIHJlc3BvbnNlcyBhcmUgb2theSBidXQgcmVtb3ZlIGFueW9uZSB3aG8gZGlkbid0IGFuc3dlciBhbnkgbGlrZXJ0IHNjYWxlIHF1ZXN0aW9ucy4NCmBgYHtyfQ0KaW5kIDwtIGFwcGx5KGRhdFsyMTo2OF0sIDEsIGZ1bmN0aW9uKHgpIGFsbChpcy5uYSh4KSkpDQpkYXQgPC0gZGF0WyAhaW5kLCBdDQpgYGANCg0KDQojRGVtb2dyYXBoaWMgYW5hbHlzaXMNCkxvb2tpbmcgYXQgc29tZSBjaGFyYWN0ZXJpc3RpY3Mgb2YgdGhlIHBlb3BsZSB3aG8gcmVzcG9uZGVkLg0KDQojI0pvYiB0aXRsZXMNCmBgYHtyfQ0KZGF0JGBKb2IgdGl0bGVgIDwtIHRvbG93ZXIoZGF0JGBKb2IgdGl0bGVgKQ0Kam9iX3RpdGxlcyA8LSBkYXRhLmZyYW1lKHRhYmxlKGRhdCRgSm9iIHRpdGxlYCkpDQoNCndvcmRfbGlzdCA8LSBjKCJsaWJyYXJpYW4iLCAiZGF0YSBzZXJ2aWNlcyIsICJkYXRhIG1hbmFnZW1lbnQiLCAicmVzZWFyY2ggZGF0YSIsICJpbmZvcm1hdGlvbmlzdCIsICJtYW5hZ2VyIiwgImRpcmVjdG9yIikNCg0KZm9yIChpIGluIDE6bGVuZ3RoKHdvcmRfbGlzdCkpIHsNCiAgc2VudGVuY2UgPC0gcGFzdGUobGVuZ3RoKGdyZXAod29yZF9saXN0W2ldLCBkYXQkYEpvYiB0aXRsZWApKSwgImpvYiB0aXRsZXMgY29udGFpbiB0aGUgd29yZCIsIHdvcmRfbGlzdFtpXSkNCiAgcHJpbnQoc2VudGVuY2UpDQp9DQoNCmBgYA0KDQoNCiMjWWVhcnMgb2YgZXhwZXJpZW5jZQ0KU29tZSB2aXN1YWxpemF0aW9ucyBvZiBob3cgbG9uZyBwZW9wbGUgaGF2ZSBiZWVuIGluIHRoZWlyIGN1cnJlbnQgcG9zaXRpb24gYW5kIHN0dWZmLiAgRmlsdGVyIG91dCBhbnlvbmUgd2hvIHB1dCB0aGV5IGhhZCBiZWVuIGluIHRoZWlyIGN1cnJlbnQgcG9zaXRpb24gbG9uZ2VyIHRoYW4gdGhlaXIgd2hvbGUgY2FyZWVyLg0KDQpgYGB7cn0NCmZpeGVkX2RhdCA8LSBkYXQgJT4lIGZpbHRlcihgWWVhcnMgaW4gbGlicmFyaWFuc2hpcGAgPj0gYFllYXJzIGluIGN1cnJlbnQgcG9zaXRpb25gKSANCg0KcmVxdWlyZShncmlkRXh0cmEpDQoNCnBsb3QxIDwtIGdncGxvdChmaXhlZF9kYXQsIGFlcyh4ID0gYFllYXJzIGluIGN1cnJlbnQgcG9zaXRpb25gKSkgKyBnZW9tX2hpc3RvZ3JhbSgpICsgZ2d0aXRsZSgiWWVhcnMgaW4gQ3VycmVudCBQb3NpdGlvbiIpICsgeGxhYigiWWVhcnMiKSArIHlsYWIoIk51bWJlciBvZiBSZXNwb25kZW50cyIpICsgdGhlbWVfYncoKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KDQpwbG90MiA8LSBnZ3Bsb3QoZml4ZWRfZGF0LCBhZXMoeCA9IGBZZWFycyBpbiBsaWJyYXJpYW5zaGlwYCkpICsgZ2VvbV9oaXN0b2dyYW0oKSArIGdndGl0bGUoIlllYXJzIGluIExpYnJhcmlhbnNoaXAgVG90YWwiKSArIHhsYWIoIlllYXJzIikgKyB5bGFiKCJOdW1iZXIgb2YgUmVzcG9uZGVudHMiKSArIHRoZW1lX2J3KCkgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCg0KZ3JpZC5hcnJhbmdlKHBsb3QxLCBwbG90MiwgbmNvbD0yKQ0KDQpgYGANCg0KDQpGb3IgaG93IG1hbnkgcGVvcGxlIGlzIHRoaXMgdGhlaXIgZmlyc3QgbGlicmFyeSBqb2IsIGkuZS4geWVhcnMgaW4gbGlicmFyaWFuc2hpcCBpcyBlcXVhbCB0byB5ZWFycyBpbiBjdXJyZW50IHBvc2l0aW9uPw0KYGBge3J9DQpuX2ZpcnN0IDwtIGRhdCAlPiUgZmlsdGVyKGBZZWFycyBpbiBsaWJyYXJpYW5zaGlwYCA9PSBgWWVhcnMgaW4gY3VycmVudCBwb3NpdGlvbmApICU+JSBucm93DQpgYGANCmBgYHtyIGVjaG8gPSBGfQ0KY2F0KCJGb3IiLCBuX2ZpcnN0LCAicGFydGljaXBhbnRzLCB0aGVpciBjdXJyZW50IGpvYiBpcyB0aGVpciBmaXJzdCBpbiB0aGUgZmllbGQgb2YgbGlicmFyaWFuc2hpcC4iKQ0KYGBgDQoNCiMjRWR1Y2F0aW9uYWwgRXhwZXJpZW5jZQ0KSG93IG1hbnkgcGVvcGxlIGhhdmUgdmFyaW91cyBkZWdyZWVzPyAgU3VtIHVwIHRoZSBjb3VudHMgYW5kIHBsb3QgdGhlbSBhcyBhIHBlcmNlbnRhZ2UuDQpgYGB7cn0NCmRlZ3JlZXMgPC0gZGF0YS5mcmFtZShkZWdyZWUgPSBuYW1lcyhkYXRbMTQ6MjBdKSwgY291bnQgPSBjb2xTdW1zKGRhdFsxNDoyMF0sIG5hLnJtID0gVFJVRSkpDQpkZWdyZWVzJHBlcmNlbnQgPC0gZGVncmVlcyRjb3VudC9ucm93KGRhdCkgKjEwMA0KZGVncmVlcyRsYWJlbCA8LSBwYXN0ZSgibiA9IiwgZGVncmVlcyRjb3VudCkNCg0KZ2dwbG90KGRlZ3JlZXMsIGFlcyh4ID0gcmVvcmRlcihkZWdyZWUsIHBlcmNlbnQpLCB5ID0gcGVyY2VudCkpICsgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgY29vcmRfZmxpcCgpICsgeWxhYigiUGVyY2VudCBvZiBSZXNwb25kZW50c1xud2l0aCBEZWdyZWUiKSArIHRoZW1lX2J3KCkgKyBnZ3RpdGxlKCJFZHVjYXRpb25hbCBFeHBlcmllbmNlIikgKyBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZ1bmN0aW9uKHgpIGxhcHBseShzdHJ3cmFwKHgsIHdpZHRoID0gMzUsIHNpbXBsaWZ5ID0gRkFMU0UpLCBwYXN0ZSwgY29sbGFwc2U9IlxuIikpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsgeGxhYigiRGVncmVlIG9yIGNlcnRpZmljYXRlIikgKyBnZW9tX3RleHQoYWVzKGxhYmVsID0gbGFiZWwsIHggPSBkZWdyZWUsIHkgPSBjb3VudCksIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjgpLCBoanVzdCA9IC44LCBjb2xvciA9ICJ3aGl0ZSIpDQogIA0KDQpkYXQgPC0gZGF0ICU+JSANCiAgbXV0YXRlKGRlZ3JlZXNfaGVsZCA9IGBBTEEtYWNjcmVkaXRlZCBtYXN0ZXLigJlzIGRlZ3JlZWAgKyBgU2NpZW5jZSBtYXN0ZXLigJlzIGRlZ3JlZWAgKyBgT3RoZXIgbm9uLUFMQSwgbm9uLXNjaWVuY2UgbWFzdGVy4oCZcyBkZWdyZWVgICsgYFVuZGVyZ3JhZHVhdGUgc2NpZW5jZSBkZWdyZWVgICsgYFBoRCAoYW55IGRpc2NpcGxpbmUpYCArIGBTcGVjaWFsaXplZCBsaWJyYXJpYW5zaGlwIGNlcnRpZmljYXRpb24gKHN1Y2ggYXMgZGF0YSBvciBtZWRpY2FsIGxpYnJhcnkgY2VydGlmaWNhdGlvbilgICsgYE90aGVyIG5vbi1kZWdyZWUsIG5vbi1jZXJ0aWZpY2F0ZSB0cmFpbmluZyBpbiBkYXRhLCBzY2llbmNlLCBvciBzcGVjaWFsaXplZCBsaWJyYXJpYW5zaGlwYCkNCm5fZGVncmVlcyA8LSBkYXQgJT4lIGZpbHRlcihkZWdyZWVzX2hlbGQgPiAxKSAlPiUgbnJvdw0KbWVhbl9kZWdyZWVzIDwtIG1lYW4oZGF0JGRlZ3JlZXNfaGVsZCkNCmBgYA0KYGBge3IgZWNobyA9IEZ9DQpjYXQobl9kZWdyZWVzLCAicGFydGljaXBhbnRzIGhhdmVtb3JlIHRoYW4gb25lIGRlZ3JlZSIsICJcblRoZSBtZWFuIG51bWJlciBvZiBkZWdyZWVzIGhlbGQgaXMiLCBtZWFuX2RlZ3JlZXMpDQpgYGANCg0KDQojI0FyZWFzIHN1cHBvcnRlZA0KV2hhdCBkaXNjaXBsaW5lcyBkbyBwZW9wbGUgd29yayBpbj8NCmBgYHtyfQ0KZGlzY2lwbGluZSA8LSBkYXRhLmZyYW1lKGRpc2NpcGxpbmUgPSBuYW1lcyhkYXRbNToxMF0pLCBjb3VudCA9IGNvbFN1bXMoZGF0WzU6MTBdLCBuYS5ybSA9IFRSVUUpKQ0KZGlzY2lwbGluZSRwZXJjZW50IDwtIGRpc2NpcGxpbmUkY291bnQvbnJvdyhkYXQpICoxMDANCmRpc2NpcGxpbmUkbGFiZWwgPC0gcGFzdGUoIm4gPSIsIGRpc2NpcGxpbmUkY291bnQpDQoNCg0KZ2dwbG90KGRpc2NpcGxpbmUsIGFlcyh4ID0gcmVvcmRlcihkaXNjaXBsaW5lLCBwZXJjZW50KSwgeSA9IHBlcmNlbnQpKSArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIGNvb3JkX2ZsaXAoKSArIHlsYWIoIlBlcmNlbnQgb2YgUmVzcG9uZGVudHNcblN1cHBvcnRpbmcgRGlzY2lwbGluZSIpICsgdGhlbWVfYncoKSArIGdndGl0bGUoIkRpc2NpcGxpbmFyeSBTdXBwb3J0IikgKyBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZ1bmN0aW9uKHgpIGxhcHBseShzdHJ3cmFwKHgsIHdpZHRoID0gMzUsIHNpbXBsaWZ5ID0gRkFMU0UpLCBwYXN0ZSwgY29sbGFwc2U9IlxuIikpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsgeGxhYigiRGlzY2lwbGluZSIpICsgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGxhYmVsLCB4ID0gZGlzY2lwbGluZSwgeSA9IGNvdW50KSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuOCksIGhqdXN0ID0gLjgsIGNvbG9yID0gIndoaXRlIikNCg0KYGBgDQoNCkhvdyBtYW55IHBlb3BsZSBhcmUgc3VwcG9ydGluZyBtb3JlIHRoYW4gb25lIGRpc2NpcGxpbmU/DQpgYGB7cn0NCmRhdCA8LSBkYXQgJT4lIA0KICBtdXRhdGUoZGlzY2lwbGluZXNfc2VydmVkID0gYEJpb21lZGljYWwgYW5kL29yIGhlYWx0aCBzY2llbmNlc2AgKyBgTGlmZSBzY2llbmNlc2AgKyBgUGh5c2ljYWwgc2NpZW5jZXNgKyBgTWF0aGVtYXRpY3MgYW5kL29yIHN0YXRpc3RpY3NgICsgYEVuZ2luZWVyaW5nIGFuZC9vciBjb21wdXRlciBzY2llbmNlYCArIGBTb2NpYWwgc2NpZW5jZXNgKQ0Kbl9kaXNjaXBsaW5lcyA8LSBkYXQgJT4lIGZpbHRlcihkaXNjaXBsaW5lc19zZXJ2ZWQgPiAxKSAlPiUgbnJvdw0KbWVhbl9kaXNjaXBsaW5lcyA8LSBtZWFuKGRhdCRkaXNjaXBsaW5lc19zZXJ2ZWQpDQpgYGANCmBgYHtyIGVjaG8gPSBGfQ0KY2F0KG5fZGlzY2lwbGluZXMsICJwYXJ0aWNpcGFudHMgc2VydmUgbW9yZSB0aGFuIG9uZSBkaXNjaXBsaW5lIiwgIlxuVGhlIG1lYW4gbnVtYmVyIG9mIGRpc2NpcGxpbmVzIHNlcnZlZCBpcyIsIG1lYW5fZGlzY2lwbGluZXMpDQpgYGANCg0KDQoNCiMjVGltZSBzcGVudCBvbiBkYXRhIHZzIG5vbiBkYXRhIGFjdGl2aXRpZXMNCkZpcnN0IHJlbW92ZSBhbnkgTkEgZW50cmllcyB0aGVuIG1ha2UgY2hhcnRzLg0KDQpgYGB7cn0NCmRhdCAlPiUgbXV0YXRlKHN1bV9vZl90aW1lID0gYFBlcmNlbnQgb2YgdGltZSBzcGVudCBvbiBvdGhlciB3b3JrYCArIGBQZXJjZW50IG9mIHRpbWUgc3BlbnQgb24gZGF0YS1yZWxhdGVkIHdvcmtgKSAlPiUgDQogIGZpbHRlcighaXMubmEoc3VtX29mX3RpbWUpKSAlPiUgZ2dwbG90KGFlcyh4ID0gYFBlcmNlbnQgb2YgdGltZSBzcGVudCBvbiBkYXRhLXJlbGF0ZWQgd29ya2ApKSArIGdlb21faGlzdG9ncmFtKCkgKyB5bGFiKCJOdW1iZXIgb2YgcmVzcG9uZGVudHMiKSArIHRoZW1lX2J3KCkgKyBnZ3RpdGxlKCJUaW1lIFNwZW50IG9uIERhdGEtUmVsYXRlZCBXb3JrIikgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKyB4bGFiKCJQZXJjZW50IG9mIHRpbWUiKQ0KDQoNCg0KDQpgYGANCg0KI0FuYWx5c2lzIG9mIFNraWxsL0tub3dsZWRnZSBJbXBvcnRhbmNlIFJhdGluZ3MNCkRvIHNvbWUgZGF0YSBjbGVhbmluZyBmaXJzdCBmb3IgdGhlIGxpa2VydCBwYWNrYWdlLg0KYGBge3J9DQpkYXRfZGYgPC0gYXMuZGF0YS5mcmFtZShkYXQpICMjbGlrZXJ0IHBhY2thZ2UgY2FuJ3QgaGFuZGxlIHRpYmJsZXMsIHNvIGNvbnZlcnQgdG8gYSBkZg0KDQojI25lZWQgdG8gaGFuZGxlIHRoZSBOQS9kb24ndCBrbm93IC0gY29udmVydCB0aGVzZSBmcm9tIGEgbGV2ZWwgdG8gYW4gTkENCmRhdF9kZlsyMTo2OF0gPC0gbGFwcGx5KGRhdF9kZlsyMTo2OF0sIHJlY29kZV9mYWN0b3IsICJEb24ndCBrbm93IG9yIE4vQSIgPSBOQV9jaGFyYWN0ZXJfKQ0KDQpgYGANCg0KDQoNCkJyZWFrIHRoZSBkaWZmZXJlbnQgc2tpbGxzL2tub3dsZWRnZSBhcmVhcyBvdXQgaW50byB0aGVpciBjYXRlZ29yeSBncm91cGluZ3MgdG8gbWFrZSB0aGlzIGEgbGl0dGxlIGVhc2llci4gIEZpcnN0IGdldCB0aGUgZGVtb2dyYXBoaWMgaW5mbyB3ZSB3YW50IGluIGFsbCB0aGUgZGF0YSBzdWJzZXRzLCB0aGVuIHB1bGwgb3V0IHRoZSBjYXRlZ29yeSBzdWJzZXRzLiAgVGhlcmUncyB0b28gbWFueSBpbiB0aGUgZGF0YSBtYW5hZ2VtZW50IGNhdGVnb3J5IGFuZCB0aGUgY2hhcnRzIGxvb2sgY3JhcHB5IHNvIGJyZWFrIGl0IGludG8gdHdvLg0KDQpgYGB7cn0NCmRlbW9fcm93cyA8LSAxOjEwDQpkYXRhX21nbXQxIDwtIDIxOjI1DQpkYXRhX21nbXQyIDwtIDI2OjMwDQpkYXRhX21nbXR0b3RhbCA8LSAyMTozMA0KdGVjaCA8LSAzMTozNQ0KZXZhbF9hc3Nlc3MgPC0gMzY6MzgNCnRlYWNoaW5nIDwtIDM5OjQzDQpsaWJyYXJ5IDwtIDQ0OjQ5DQpvdXRyZWFjaCA8LSA1MDo1Mw0KaW52b2x2ZW1lbnQgPC0gNTQ6NTYNCnBlcnNvbmFsX2F0dHJzIDwtIDU3OjYzDQplZHVjYXRpb24gPC0gNjQ6NjgNCg0Kc3Vic2V0X2NvbHMgPC0gbGlzdChkYXRhX21nbXQxLCBkYXRhX21nbXQyLCBkYXRhX21nbXR0b3RhbCwgdGVjaCwgZXZhbF9hc3Nlc3MsIHRlYWNoaW5nLCBsaWJyYXJ5LCBvdXRyZWFjaCwgaW52b2x2ZW1lbnQsIHBlcnNvbmFsX2F0dHJzLCBlZHVjYXRpb24pDQoNCiNQcmV0dHkgbmFtZXMgZm9yIHBsb3RzDQpuYW1lcyhzdWJzZXRfY29scykgPC0gYygiRGF0YSBNYW5hZ2VtZW50IFNraWxscywgUGFydCAxIiwgIkRhdGEgTWFuYWdlbWVudCBTa2lsbHMsIFBhcnQgMiIsICJEYXRhIE1hbmFnZW1lbnQgU2tpbGxzIiwgIlRlY2hub2xvZ3kgU2tpbGxzIiwgIkV2YWx1YXRpb24gYW5kIEFzc2Vzc21lbnQgU2tpbGxzIiwgIlRlYWNoaW5nIFNraWxscyIsICJMaWJyYXJ5IFNraWxscyIsICJOZXR3b3JraW5nIGFuZCBPdXRyZWFjaCBTa2lsbHMiLCAiUHJvZmVzc2lvbmFsIEludm9sdmVtZW50IiwgIlBlcnNvbmFsIEF0dHJpYnV0ZXMiLCAiRWR1Y2F0aW9uIikNCg0KDQoNCmBgYA0KDQoNCiMjTGlrZXJ0IHNjYWxlIGNoYXJ0cw0KTm90ZTogdGhlIGxpa2VydCBwYWNrYWdlIGNhbid0IGhhbmRsZSB0aWJibGVzLCBzbyBjb252ZXJ0IHRvIGEgZGF0YSBmcmFtZSB3aGVuIHlvdSBwYXNzIGl0IHRvIHRoZSBsaWtlcnQgZnVuY3Rpb24uDQpgYGB7cn0NCnBhcihtZnJvdyA9IGMoMiwgNCkpDQoNCmZvciAoaSBpbiAxOmxlbmd0aChzdWJzZXRfY29scykpIHsNCiAgY29sX251bXMgPC0gdW5saXN0KHN1YnNldF9jb2xzW2ldKQ0KICBsaWtfZGF0IDwtIGxpa2VydChkYXRfZGZbY29sX251bXNdKQ0KICBwIDwtIHBsb3QobGlrX2RhdCkgKyBnZ3RpdGxlKG5hbWVzKHN1YnNldF9jb2xzKVtpXSkgKyBzY2FsZV9maWxsX2dyZXkoKQ0KICBwcmludChwKQ0KfQ0KDQpgYGANCg0KDQoNCiNDaGFydGluZyBIb3cgbWFueSBza2lsbHMgcGVvcGxlIHRoaW5rIGFyZSBpbXBvcnRhbnQgZm9yIHZhcmlvdXMgY2F0ZWdvcmllcw0KYGBge3J9DQp0X2FsbCA8LSBhcy5kYXRhLmZyYW1lKHQoZGF0X2RmKSkNCnRfYWxsJGdyb3VwIDwtIE5BDQoNCmZvciAoaSBpbiAxOmxlbmd0aChzdWJzZXRfY29scykpIHsNCiAgZ19uYW1lIDwtIG5hbWVzKHN1YnNldF9jb2xzKVtpXQ0KICB0X2FsbCRncm91cFtzdWJzZXRfY29sc1tbaV1dXSA8LSBnX25hbWUNCn0NCnRfYWxsIDwtIHRfYWxsWzIxOjY4LF0NCmBgYA0KDQoNCg0KDQoNCg0KIyNCcmVha2luZyBkb3duIHRoZSBhbmFseXNpcyBieSBkZW1vZ3JhcGhpY3MNCg0KRG8gcGVvcGxlIHdobyBzcGVuZCBtb3JlIHRoYW4gaGFsZiB0aGVpciB0aW1lIGRvaW5nIGRhdGEgcmVsYXRlZCB3b3JrIGZlZWwgZGlmZmVyZW50bHkgYWJvdXQgd2hhdCdzIGltcG9ydGFudD8NCg0KYGBge3J9DQpkYXRfZGYkbWFqb3JpdHlfZGF0YV90aW1lIDwtIGlmZWxzZShkYXRfZGYkYFBlcmNlbnQgb2YgdGltZSBzcGVudCBvbiBkYXRhLXJlbGF0ZWQgd29ya2AgPj0gNTAsDQpjKCJ5ZXMiKSwgYygibm8iKSkgDQpkYXRfZGYkbWFqb3JpdHlfZGF0YV90aW1lIDwtIGFzLmZhY3RvcihkYXRfZGYkbWFqb3JpdHlfZGF0YV90aW1lKQ0KDQpkYXRfZGYgPC0gZGF0X2RmWyFpcy5uYShkYXRfZGYkbWFqb3JpdHlfZGF0YV90aW1lKSwgXQ0KYGBgDQoNCk1ha2UgdGhlIHNhbWUgTGlrZXJ0IHNjYWxlcyBidXQgYnJva2VuIG91dCBieSBncm91cC4NCg0KDQpgYGB7cn0NCmZvciAoaSBpbiAxOmxlbmd0aChzdWJzZXRfY29scykpIHsNCiAgY29sX251bXMgPC0gdW5saXN0KHN1YnNldF9jb2xzW2ldKQ0KICBsaWtfZGF0IDwtIGxpa2VydChkYXRfZGZbLCBjb2xfbnVtc10sIGdyb3VwaW5nID0gZGF0X2RmJG1ham9yaXR5X2RhdGFfdGltZSkNCiAgcCA8LSBwbG90KGxpa19kYXQpICsgZ2d0aXRsZShuYW1lcyhzdWJzZXRfY29scylbaV0pDQogIHByaW50KHApDQp9DQpgYGANCg0KDQoNCiMjUGhEIHZzIG5vbmUNClNvbWUgam9iIGFkcyBzcGVjaWZpY2FsbHkgYXNrIGZvciBhIFBoRC4gTGV0J3Mgc2VlIGlmIHRoZXJlJ3MgYW55dGhpbmcgZGlmZmVyZW50IGFib3V0IHRoZSBwZW9wbGUgd2hvIGhhdmUgb25lIG9yIGRvbid0Lg0KDQpgYGB7cn0NCmRhdF9kZiRoYXNwaGQgPC0gaWZlbHNlKGRhdF9kZiRgUGhEIChhbnkgZGlzY2lwbGluZSlgID09IDEsDQpjKCJ5ZXMiKSwgYygibm8iKSkgDQpkYXRfZGYkaGFzcGhkIDwtIGFzLmZhY3RvcihkYXRfZGYkaGFzcGhkKQ0KDQpkYXRfZGYgPC0gZGF0X2RmWyFpcy5uYShkYXRfZGYkaGFzcGhkKSwgXQ0KDQpmb3IgKGkgaW4gMTpsZW5ndGgoc3Vic2V0X2NvbHMpKSB7DQogIGNvbF9udW1zIDwtIHVubGlzdChzdWJzZXRfY29sc1tpXSkNCiAgbGlrX2RhdCA8LSBsaWtlcnQoZGF0X2RmWywgY29sX251bXNdLCBncm91cGluZyA9IGRhdF9kZiRoYXNwaGQpDQogIHAgPC0gcGxvdChsaWtfZGF0KSArIGdndGl0bGUobmFtZXMoc3Vic2V0X2NvbHMpW2ldKQ0KICBwcmludChwKQ0KfQ0KYGBgDQoNCiMjTUxJUyB2cyBub25lDQpgYGB7cn0NCmRhdF9kZiRoYXNtbGlzIDwtIGlmZWxzZShkYXRfZGYkYEFMQS1hY2NyZWRpdGVkIG1hc3RlcuKAmXMgZGVncmVlYCA9PSAxLA0KYygieWVzIiksIGMoIm5vIikpIA0KZGF0X2RmJGhhc21saXMgPC0gYXMuZmFjdG9yKGRhdF9kZiRoYXNtbGlzKQ0KZGF0X2RmIDwtIGRhdF9kZlshaXMubmEoZGF0X2RmJGhhc21saXMpLCBdDQoNCmZvciAoaSBpbiAxOmxlbmd0aChzdWJzZXRfY29scykpIHsNCiAgY29sX251bXMgPC0gdW5saXN0KHN1YnNldF9jb2xzW2ldKQ0KICBsaWtfZGF0IDwtIGxpa2VydChkYXRfZGZbLCBjb2xfbnVtc10sIGdyb3VwaW5nID0gZGF0X2RmJGhhc21saXMpDQogIHAgPC0gcGxvdChsaWtfZGF0KSArIGdndGl0bGUobmFtZXMoc3Vic2V0X2NvbHMpW2ldKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpKQ0KICBwcmludChwKQ0KfQ0KYGBgDQoNCg0KIyNCcmVha2Rvd24gYnkgZGlzY2lwbGluZXMgc3VwcG9ydGVkDQoNCiNMYXRlbnQgQ2xhc3MgQW5hbHlzaXMNCiMjUHJlcGFyZSB0aGUgZGF0YSBmb3IgdGhlIGFuYWx5c2lzDQoNCkNvbnZlcnQgdGhlIGxpa2VydCBkYXRhIHRvIGEgbnVtZXJpYyBzY2FsZSwgd2l0aCAxIGJlaW5nIGxvdywgNSBiZWluZyBoaWdoLCBhbmQgNiBOQS4gIFNvbWUgbGV2ZWxzIGRpZG4ndCBoYXZlIGFueSBkb24ndCBrbm93L05BIHNvIGFkZCBhIDYgbGV2ZWwganVzdCBpbiBjYXNlLiAgQWxzbywgSSB0aGluayB3ZSBuZWVkIHRvIGNvbGxhcHNlIHRoaXMgaW50byBqdXN0IDMgbGV2ZWxzIGluc3RlYWQgb2YgNSB0byBtYWtlIHRoZSBtb2RlbCB3b3JrLg0KYGBge3J9DQpkYXRfbGNhIDwtIGRhdA0KZGF0X2xjYVsyMTo2OF0gPC0gbGFwcGx5KGRhdF9sY2FbMjE6NjhdLCByZWNvZGVfZmFjdG9yLCANCiJEb24ndCBrbm93IG9yIE4vQSIgPSAiNiIsICJOb3QgYXQgYWxsIGltcG9ydGFudCIgPSAiMSIsICJTbGlnaHRseSBpbXBvcnRhbnQiID0gIjIiLCAiSW1wb3J0YW50IiA9ICIzIiwgIlZlcnkgaW1wb3J0YW50IiA9ICI0IiwgIkFic29sdXRlbHkgZXNzZW50aWFsIiA9ICI1IikNCg0KIyNXZSB1c2VkIGRhdCBmb3IgdGhpcyByYXRoZXIgdGhhbiBkYXRfZGYgc28gYWRkIGluIHRoZSBtYWpvcml0eSBkYXRhIHRpb21lIHZhcmlhYmxlDQpkYXRfbGNhJG1ham9yaXR5X2RhdGFfdGltZSA8LSBpZmVsc2UoZGF0X2xjYSRgUGVyY2VudCBvZiB0aW1lIHNwZW50IG9uIGRhdGEtcmVsYXRlZCB3b3JrYCA+PSA1MCwNCmMoInllcyIpLCBjKCJubyIpKSANCmRhdF9sY2EkbWFqb3JpdHlfZGF0YV90aW1lIDwtIGFzLmZhY3RvcihkYXRfbGNhJG1ham9yaXR5X2RhdGFfdGltZSkNCg0KYGBgDQoNCkFkZCAxIHRvIGFsbCAwLzEgY29sdW1ucyBzbyB0aGV5IGFyZSAxLzIgYXMgcGVyIGluc3RydWN0aW9ucy4gIEFsc28gbWFrZSB0aGUgbWFqb3JpdHkgb2YgdGltZSBvbiBkYXRhIGEgbnVtZXJpYyBjb2x1bW4uDQpgYGB7cn0NCmRhdF9sY2FbYyg1OjEwLCAxNDoyMCldIDwtIGRhdF9sY2FbYyg1OjEwLCAxNDoyMCldICsgMQ0KZGF0X2xjYSRtYWpvcml0eV9kYXRhX3RpbWUgPC0gcmVjb2RlX2ZhY3RvcihkYXRfbGNhJG1ham9yaXR5X2RhdGFfdGltZSwgInllcyIgPSAiMiIsICJubyIgPSAiMSIpDQoNCmRhdF9sY2FbYygyMTo2OCwgNzEpXSA8LSBsYXBwbHkoZGF0X2xjYVtjKDIxOjY4LCA3MSldLCBmYWN0b3IsIGxldmVscyA9IGMoMSwgMiwgMywgNCwgNSwgNikpDQpgYGANCg0KUmVtb3ZlIHVud2FudGVkIGNvbHVtbnMNCmBgYHtyfQ0KZHJvcHMgPC0gYygxMToxMywgNjk6NzApDQpkYXRfbGNhIDwtIGRhdF9sY2FbLWRyb3BzXQ0KYGBgDQoNClJlcGxhY2UgYW55IHJlbWFpbmluZyBOQXMgd2l0aCA2DQpgYGB7cn0NCmRhdF9sY2FbaXMubmEoZGF0X2xjYSldIDwtIDYNCmBgYA0KDQojSy1NZWFucyBhbmQgT25lIEhvdCBFbmNvZGluZw0KDQpNYWtlIGEgbmV3IGNvbnZlcnRpbmcgYWxsIGxpa2VydCBzY2FsZSByZXNwb25zZXMgdG8gb25lLWhvdCBlbmNvZGVkIHZhcmlhYmxlcywgdGhlbiBhZGQgYmFjayBpbiB0aGUgbm9uLWxpa2VydCB2YXJpYWJsZXMuICBBbHNvIGRpc2NhcmQgdGhhdCBvbmUgd2hlcmUgdGhlIHBlcnNvbiBzYWlkIHRoZXkndmUgYmVlbiBpbiB0aGVpciBjdXJyZW50IHBvc2l0aW9uIGxvbmdlciB0aGFuIGluIGxpYnJhcmlhbnNoaXAgdG90YWwuDQpgYGB7cn0NCmxpYnJhcnkoYWRlNCkNCm9uZV9ob3QgPC0gYWNtLmRpc2pvbmN0aWYoZGF0X2RmWzE6ODIsIDIxOjY4XSkNCm9uZV9ob3QgPC0gY2JpbmQoZGF0X2RmWzE6ODIsIGMoMTozLCA1OjEwLCAxMjoyMCldLCBvbmVfaG90KQ0KYGBgDQoNCkNhbGN1bGF0ZSB0aGUgc2ltaWxhcml0aWVzIHVzaW5nIGdvd2VyIGRpc3RhbmNlDQpgYGB7cn0NCmdvd2VyX2Rpc3QgPC0gZGFpc3kob25lX2hvdFssIC0xXSwNCiAgICAgICAgICAgICAgICAgICAgbWV0cmljID0gImdvd2VyIikNCg0KDQoNCnNpbF93aWR0aCA8LSBjKE5BKQ0KDQpmb3IoaSBpbiAyOjEwKXsNCiAgDQogIHBhbV9maXQgPC0gcGFtKGdvd2VyX2Rpc3QsDQogICAgICAgICAgICAgICAgIGRpc3MgPSBUUlVFLA0KICAgICAgICAgICAgICAgICBrID0gaSkNCiAgDQogIHNpbF93aWR0aFtpXSA8LSBwYW1fZml0JHNpbGluZm8kYXZnLndpZHRoDQogIA0KfQ0KDQojIFBsb3Qgc2lob3VldHRlIHdpZHRoIChoaWdoZXIgaXMgYmV0dGVyKQ0KDQpwbG90KDE6MTAsIHNpbF93aWR0aCwNCiAgICAgeGxhYiA9ICJOdW1iZXIgb2YgY2x1c3RlcnMiLA0KICAgICB5bGFiID0gIlNpbGhvdWV0dGUgV2lkdGgiKQ0KbGluZXMoMToxMCwgc2lsX3dpZHRoKQ0KDQpgYGANCg0KVGhpcyBzdWdnZXN0cyAyIGNsdXN0ZXJzIGlzIGJlc3QuDQoNCg0KDQpgYGB7cn0NCnBhbV9maXQgPC0gcGFtKGdvd2VyX2Rpc3QsIGRpc3MgPSBUUlVFLCBrID0gMikNCg0KcGFtX3Jlc3VsdHMgPC0gb25lX2hvdCAlPiUNCiAgZHBseXI6OnNlbGVjdCgtYEpvYiB0aXRsZWApICU+JQ0KICBtdXRhdGUoY2x1c3RlciA9IHBhbV9maXQkY2x1c3RlcmluZykgJT4lDQogIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQ0KICBkbyh0aGVfc3VtbWFyeSA9IHN1bW1hcnkoLikpDQoNCnBhbV9yZXN1bHRzJHRoZV9zdW1tYXJ5DQpgYGANCg0KDQpUcnlpbmcgYSBkaWZmZXJlbnQgaW1wbGVtZW50YXRpb24gb2YgdGhpcw0KYGBge3J9DQpsaWJyYXJ5KGZwYykNCnBjID0gcGFtayhnb3dlcl9kaXN0LCBrcmFuZ2U9MTo1LCBjcml0ZXJpb249ImFzdyIpDQpwY1syOjNdDQoNCmhjLm0gPSBoY2x1c3QoZ293ZXJfZGlzdCwgbWV0aG9kPSJtZWRpYW4iKQ0KaGMucyA9IGhjbHVzdChnb3dlcl9kaXN0LCBtZXRob2Q9InNpbmdsZSIpDQpoYy5jID0gaGNsdXN0KGdvd2VyX2Rpc3QsIG1ldGhvZD0iY29tcGxldGUiKQ0KcGxvdChoYy5tKQ0KcGxvdChoYy5zKQ0KcGxvdChoYy5jKQ0KDQoNCnRhYmxlKGN1dHJlZShoYy5tLCBrPTIpLCBjdXRyZWUoaGMucywgaz0yKSkNCmBgYA0KDQpMb29raW5nIGF0IHRoZSBjb21wbGV0ZSBjbHVzdGVyaW5nLg0KYGBge3J9DQpncm91cDFsIDwtIGhjLmMkb3JkZXJbMToyOF0NCmdyb3VwMmwgPC0gaGMuYyRvcmRlclsyOTo4Ml0NCg0KZGF0X2RmWyw1OjIwXSA8LSBsYXBwbHkoZGF0X2RmWywgNToyMF0sIGFzLmZhY3RvcikNCg0KZ3JvdXAxIDwtIGRhdF9kZltncm91cDFsLCBdDQpncm91cDIgPC0gZGF0X2RmW2dyb3VwMmwsIF0NCg0KZ3JvdXAxJEdyb3VwIDwtICJTcGVjaWFsaXN0cyINCmdyb3VwMiRHcm91cCA8LSAiR2VuZXJhbGlzdHMiDQoNCmdyb3VwZWQgPC0gcmJpbmQoZ3JvdXAxLCBncm91cDIpDQpgYGANCg0KTG9vayBhdCBncm91cGluZ3MNCmBgYHtyfQ0KZm9yIChpIGluIDE6bGVuZ3RoKHN1YnNldF9jb2xzKSkgew0KICBjb2xfbnVtcyA8LSB1bmxpc3Qoc3Vic2V0X2NvbHNbaV0pDQogIGxpa19kYXQgPC0gbGlrZXJ0KGdyb3VwZWRbLCBjb2xfbnVtc10sIGdyb3VwaW5nID0gZ3JvdXBlZCRncm91cCkNCiAgcCA8LSBwbG90KGxpa19kYXQpICsgZ2d0aXRsZShuYW1lcyhzdWJzZXRfY29scylbaV0pDQogIHByaW50KHApDQp9DQpgYGANCg0KDQoNCmBgYHtyfQ0KZ3JvdXAxWzU6MTBdIDwtIGxhcHBseShncm91cDFbNToxMF0sIGFzLm51bWVyaWMpDQpncm91cDFbNToxMF0gPC0gZ3JvdXAxWzU6MTBdIC0gMQ0KZGlzY2lwbGluZTEgPC0gZGF0YS5mcmFtZShkaXNjaXBsaW5lID0gbmFtZXMoZ3JvdXAxWzU6MTBdKSwgY291bnQgPSBjb2xTdW1zKGdyb3VwMVs1OjEwXSwgbmEucm0gPSBUUlVFKSkNCmRpc2NpcGxpbmUxJHBlcmNlbnQgPC0gZGlzY2lwbGluZTEkY291bnQvbnJvdyhncm91cDEpICoxMDANCmRpc2NpcGxpbmUxJGxhYmVsIDwtIHBhc3RlKCJuID0iLCBkaXNjaXBsaW5lMSRjb3VudCkNCg0KDQpncm91cDJbNToxMF0gPC0gbGFwcGx5KGdyb3VwMls1OjEwXSwgYXMubnVtZXJpYykNCmdyb3VwMls1OjEwXSA8LSBncm91cDJbNToxMF0gLSAxDQpkaXNjaXBsaW5lMiA8LSBkYXRhLmZyYW1lKGRpc2NpcGxpbmUgPSBuYW1lcyhncm91cDJbNToxMF0pLCBjb3VudCA9IGNvbFN1bXMoZ3JvdXAyWzU6MTBdLCBuYS5ybSA9IFRSVUUpKQ0KZGlzY2lwbGluZTIkcGVyY2VudCA8LSBkaXNjaXBsaW5lMiRjb3VudC9ucm93KGdyb3VwMikgKjEwMA0KZGlzY2lwbGluZTIkbGFiZWwgPC0gcGFzdGUoIm4gPSIsIGRpc2NpcGxpbmUyJGNvdW50KQ0KDQpkaXNjaXBsaW5lMiRHcm91cCA8LSAiR2VuZXJhbGlzdHMiDQpkaXNjaXBsaW5lMSRHcm91cCA8LSAiU3BlY2lhbGlzdHMiDQoNCmRpc2NpcGxpbmUgPC0gcmJpbmQoZGlzY2lwbGluZTEsIGRpc2NpcGxpbmUyKQ0KDQpnZ3Bsb3QoZGlzY2lwbGluZSwgYWVzKHggPSByZW9yZGVyKGRpc2NpcGxpbmUsIHBlcmNlbnQpLCB5ID0gcGVyY2VudCwgZmlsbCA9IEdyb3VwKSkgKyBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArIGNvb3JkX2ZsaXAoKSArIHlsYWIoIlBlcmNlbnQgb2YgUmVzcG9uZGVudHNcblN1cHBvcnRpbmcgRGlzY2lwbGluZSIpICsgdGhlbWVfYncoKSArIGdndGl0bGUoIkRpc2NpcGxpbmFyeSBTdXBwb3J0IGJ5IEdyb3VwIikgKyBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZ1bmN0aW9uKHgpIGxhcHBseShzdHJ3cmFwKHgsIHdpZHRoID0gMzUsIHNpbXBsaWZ5ID0gRkFMU0UpLCBwYXN0ZSwgY29sbGFwc2U9IlxuIikpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsgeGxhYigiRGlzY2lwbGluZSIpICsgc2NhbGVfZmlsbF9ncmV5KCkNCg0KYGBgDQoNCmBgYHtyfQ0KZ3JvdXAxIDwtIGdyb3VwMSAlPiUgDQogIG11dGF0ZShkaXNjaXBsaW5lc19zZXJ2ZWQgPSBgQmlvbWVkaWNhbCBhbmQvb3IgaGVhbHRoIHNjaWVuY2VzYCArIGBMaWZlIHNjaWVuY2VzYCArIGBQaHlzaWNhbCBzY2llbmNlc2ArIGBNYXRoZW1hdGljcyBhbmQvb3Igc3RhdGlzdGljc2AgKyBgRW5naW5lZXJpbmcgYW5kL29yIGNvbXB1dGVyIHNjaWVuY2VgICsgYFNvY2lhbCBzY2llbmNlc2ApDQpuX2Rpc2NpcGxpbmVzMSA8LSBncm91cDEgJT4lIGZpbHRlcihkaXNjaXBsaW5lc19zZXJ2ZWQgPiAxKSAlPiUgbnJvdw0KbWVhbl9kaXNjaXBsaW5lczEgPC0gbWVhbihncm91cDEkZGlzY2lwbGluZXNfc2VydmVkKQ0KDQpncm91cDIgPC0gZ3JvdXAyICU+JSANCiAgbXV0YXRlKGRpc2NpcGxpbmVzX3NlcnZlZCA9IGBCaW9tZWRpY2FsIGFuZC9vciBoZWFsdGggc2NpZW5jZXNgICsgYExpZmUgc2NpZW5jZXNgICsgYFBoeXNpY2FsIHNjaWVuY2VzYCsgYE1hdGhlbWF0aWNzIGFuZC9vciBzdGF0aXN0aWNzYCArIGBFbmdpbmVlcmluZyBhbmQvb3IgY29tcHV0ZXIgc2NpZW5jZWAgKyBgU29jaWFsIHNjaWVuY2VzYCkNCm5fZGlzY2lwbGluZXMyIDwtIGdyb3VwMiAlPiUgZmlsdGVyKGRpc2NpcGxpbmVzX3NlcnZlZCA+IDEpICU+JSBucm93DQptZWFuX2Rpc2NpcGxpbmVzMiA8LSBtZWFuKGdyb3VwMiRkaXNjaXBsaW5lc19zZXJ2ZWQpDQoNCmBgYA0KYGBge3IgZWNobyA9IEZ9DQpjYXQobl9kaXNjaXBsaW5lczEsICJwYXJ0aWNpcGFudHMgaW4gR3JvdXAgMSBzZXJ2ZSBtb3JlIHRoYW4gb25lIGRpc2NpcGxpbmUiLCAiXG5UaGUgbWVhbiBudW1iZXIgb2YgZGlzY2lwbGluZXMgc2VydmVkIGlzIiwgbWVhbl9kaXNjaXBsaW5lczEsICIuICIsIG5fZGlzY2lwbGluZXMyLCAicGFydGljaXBhbnRzIGluIEdyb3VwIDIgc2VydmUgbW9yZSB0aGFuIG9uZSBkaXNjaXBsaW5lIiwgIlxuVGhlIG1lYW4gbnVtYmVyIG9mIGRpc2NpcGxpbmVzIHNlcnZlZCBpcyIsIG1lYW5fZGlzY2lwbGluZXMyKQ0KDQoNCmBgYA0KDQpgYGB7cn0NCmdyb3VwMVsxNDoyMF0gPC0gbGFwcGx5KGdyb3VwMVsxNDoyMF0sIGFzLm51bWVyaWMpDQpncm91cDFbMTQ6MjBdIDwtIGdyb3VwMVsxNDoyMF0gLSAxDQpkZWdyZWUxIDwtIGRhdGEuZnJhbWUoZGVncmVlID0gbmFtZXMoZ3JvdXAxWzE0OjIwXSksIGNvdW50ID0gY29sU3Vtcyhncm91cDFbMTQ6MjBdLCBuYS5ybSA9IFRSVUUpKQ0KZGVncmVlMSRwZXJjZW50IDwtIGRlZ3JlZTEkY291bnQvbnJvdyhncm91cDEpICoxMDANCmRlZ3JlZTEkbGFiZWwgPC0gcGFzdGUoIm4gPSIsIGRlZ3JlZTEkY291bnQpDQoNCmdyb3VwMlsxNDoyMF0gPC0gbGFwcGx5KGdyb3VwMlsxNDoyMF0sIGFzLm51bWVyaWMpDQpncm91cDJbMTQ6MjBdIDwtIGdyb3VwMlsxNDoyMF0gLSAxDQpkZWdyZWUyIDwtIGRhdGEuZnJhbWUoZGVncmVlID0gbmFtZXMoZ3JvdXAyWzE0OjIwXSksIGNvdW50ID0gY29sU3Vtcyhncm91cDJbMTQ6MjBdLCBuYS5ybSA9IFRSVUUpKQ0KZGVncmVlMiRwZXJjZW50IDwtIGRlZ3JlZTIkY291bnQvbnJvdyhncm91cDIpICoxMDANCmRlZ3JlZTIkbGFiZWwgPC0gcGFzdGUoIm4gPSIsIGRlZ3JlZTIkY291bnQpDQoNCg0KZGVncmVlMiRHcm91cCA8LSAiR2VuZXJhbGlzdHMiDQpkZWdyZWUxJEdyb3VwIDwtICJTcGVjaWFsaXN0cyINCg0KZGVncmVlMyA8LSByYmluZChkZWdyZWUxLCBkZWdyZWUyKQ0KDQpnZ3Bsb3QoZGVncmVlMywgYWVzKHggPSByZW9yZGVyKGRlZ3JlZSwgcGVyY2VudCksIHkgPSBwZXJjZW50LCBmaWxsID0gR3JvdXApKSArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsgY29vcmRfZmxpcCgpICsgeWxhYigiUGVyY2VudCBvZiBSZXNwb25kZW50c1xud2l0aCBEZWdyZWUiKSArIHRoZW1lX2J3KCkgKyBnZ3RpdGxlKCJFZHVjYXRpb25hbCBFeHBlcmllbmNlIGJ5IEdyb3VwIikgKyBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZ1bmN0aW9uKHgpIGxhcHBseShzdHJ3cmFwKHgsIHdpZHRoID0gMzUsIHNpbXBsaWZ5ID0gRkFMU0UpLCBwYXN0ZSwgY29sbGFwc2U9IlxuIikpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsgeGxhYigiRGVncmVlIG9yIGNlcnRpZmljYXRlIikgKyBzY2FsZV9maWxsX2dyZXkoKQ0KDQoNCg0KYGBgDQoNCmBgYHtyfQ0KZ3JvdXAxIDwtIGdyb3VwMSAlPiUgDQogIG11dGF0ZShkZWdyZWVzX2hlbGQgPSBgQUxBLWFjY3JlZGl0ZWQgbWFzdGVy4oCZcyBkZWdyZWVgICsgYFNjaWVuY2UgbWFzdGVy4oCZcyBkZWdyZWVgICsgYE90aGVyIG5vbi1BTEEsIG5vbi1zY2llbmNlIG1hc3RlcuKAmXMgZGVncmVlYCArIGBVbmRlcmdyYWR1YXRlIHNjaWVuY2UgZGVncmVlYCArIGBQaEQgKGFueSBkaXNjaXBsaW5lKWAgKyBgU3BlY2lhbGl6ZWQgbGlicmFyaWFuc2hpcCBjZXJ0aWZpY2F0aW9uIChzdWNoIGFzIGRhdGEgb3IgbWVkaWNhbCBsaWJyYXJ5IGNlcnRpZmljYXRpb24pYCArIGBPdGhlciBub24tZGVncmVlLCBub24tY2VydGlmaWNhdGUgdHJhaW5pbmcgaW4gZGF0YSwgc2NpZW5jZSwgb3Igc3BlY2lhbGl6ZWQgbGlicmFyaWFuc2hpcGApDQpuX2RlZ3JlZXMxIDwtIGdyb3VwMSAlPiUgZmlsdGVyKGRlZ3JlZXNfaGVsZCA+IDEpICU+JSBucm93DQptZWFuX2RlZ3JlZXMxIDwtIG1lYW4oZ3JvdXAxJGRlZ3JlZXNfaGVsZCkNCg0KZ3JvdXAyIDwtIGdyb3VwMiAlPiUgDQogIG11dGF0ZShkZWdyZWVzX2hlbGQgPSBgQUxBLWFjY3JlZGl0ZWQgbWFzdGVy4oCZcyBkZWdyZWVgICsgYFNjaWVuY2UgbWFzdGVy4oCZcyBkZWdyZWVgICsgYE90aGVyIG5vbi1BTEEsIG5vbi1zY2llbmNlIG1hc3RlcuKAmXMgZGVncmVlYCArIGBVbmRlcmdyYWR1YXRlIHNjaWVuY2UgZGVncmVlYCArIGBQaEQgKGFueSBkaXNjaXBsaW5lKWAgKyBgU3BlY2lhbGl6ZWQgbGlicmFyaWFuc2hpcCBjZXJ0aWZpY2F0aW9uIChzdWNoIGFzIGRhdGEgb3IgbWVkaWNhbCBsaWJyYXJ5IGNlcnRpZmljYXRpb24pYCArIGBPdGhlciBub24tZGVncmVlLCBub24tY2VydGlmaWNhdGUgdHJhaW5pbmcgaW4gZGF0YSwgc2NpZW5jZSwgb3Igc3BlY2lhbGl6ZWQgbGlicmFyaWFuc2hpcGApDQpuX2RlZ3JlZXMyIDwtIGdyb3VwMiAlPiUgZmlsdGVyKGRlZ3JlZXNfaGVsZCA+IDEpICU+JSBucm93DQptZWFuX2RlZ3JlZXMyIDwtIG1lYW4oZ3JvdXAyJGRlZ3JlZXNfaGVsZCkNCg0KYGBgDQpgYGB7ciBlY2hvID0gRn0NCmNhdChuX2RlZ3JlZXMxLCAicGFydGljaXBhbnRzIGluIEdyb3VwIDEgaGF2ZSBtb3JlIHRoYW4gb25lIGRlZ3JlZSIsICJcblRoZSBtZWFuIG51bWJlciBvZiBkZWdyZWVzIGhlbGQgaXMiLCBtZWFuX2RlZ3JlZXMxLCAiLiAiLCBuX2Rpc2NpcGxpbmVzMiwgInBhcnRpY2lwYW50cyBpbiBHcm91cCAyIGhhdmUgbW9yZSB0aGFuIG9uZSBkZWdyZWUiLCAiXG5UaGUgbWVhbiBudW1iZXIgb2YgZGVncmVlcyBoZWxkIGlzIiwgbWVhbl9kZWdyZWVzMikNCmBgYA0KDQpgYGB7cn0NCg0KcGxvdDEgPC0gZ2dwbG90KGdyb3VwZWQsIGFlcyh5ID0gYFllYXJzIGluIGN1cnJlbnQgcG9zaXRpb25gLCB4ID0gR3JvdXApKSArIGdlb21fYm94cGxvdCgpICsgZ2d0aXRsZSgiWWVhcnMgaW4gQ3VycmVudCBQb3NpdGlvbiBieSBHcm91cCIpICsgeWxhYigiWWVhcnMiKSArIHRoZW1lX2J3KCkgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKyB4bGFiKCIiKQ0KDQpwbG90MiA8LSBnZ3Bsb3QoZ3JvdXBlZCwgYWVzKHkgPSBgWWVhcnMgaW4gbGlicmFyaWFuc2hpcGAsIHggPSBHcm91cCkpICsgZ2VvbV9ib3hwbG90KCkgKyBnZ3RpdGxlKCJZZWFycyBpbiBMaWJyYXJpYW5zaGlwIFRvdGFsIGJ5IEdyb3VwIikgKyB5bGFiKCJZZWFycyIpICsgdGhlbWVfYncoKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArIHhsYWIoIiIpDQoNCmdyaWQuYXJyYW5nZShwbG90MSwgcGxvdDIsIG5jb2w9MikNCmBgYA0KDQoNCmBgYHtyfQ0Kbl9maXJzdDEgPC0gZ3JvdXAxICU+JSBmaWx0ZXIoYFllYXJzIGluIGxpYnJhcmlhbnNoaXBgID09IGBZZWFycyBpbiBjdXJyZW50IHBvc2l0aW9uYCkgJT4lIG5yb3cNCm5fZmlyc3QyIDwtIGdyb3VwMiAlPiUgZmlsdGVyKGBZZWFycyBpbiBsaWJyYXJpYW5zaGlwYCA9PSBgWWVhcnMgaW4gY3VycmVudCBwb3NpdGlvbmApICU+JSBucm93DQoNCmBgYA0KYGBge3IgZWNobyA9IEZ9DQpjYXQoIkZvciIsIG5fZmlyc3QxLCAiU3BlY2lhbGlzdHMsIHRoZWlyIGN1cnJlbnQgam9iIGlzIHRoZWlyIGZpcnN0IGluIHRoZSBmaWVsZCBvZiBsaWJyYXJpYW5zaGlwLiAgRm9yIiwgbl9maXJzdDIsICJnZW5lcmFsaXN0cyB0aGVpciBjdXJyZW50IGpvYiBpcyB0aGVpciBmaXJzdCBpbiB0aGUgZmllbGQuIikNCmBgYA0KDQpgYGB7cn0NCmdyb3VwMiRkYXRhX3RpbWUgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZ3JvdXAyJGBQZXJjZW50IG9mIHRpbWUgc3BlbnQgb24gZGF0YS1yZWxhdGVkIHdvcmtgKSkNCmdyb3VwMSRkYXRhX3RpbWUgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZ3JvdXAxJGBQZXJjZW50IG9mIHRpbWUgc3BlbnQgb24gZGF0YS1yZWxhdGVkIHdvcmtgKSkNCg0KZ3JvdXAxICU+JSBnZ3Bsb3QoYWVzKHggPSBkYXRhX3RpbWUpKSArIGdlb21faGlzdG9ncmFtKCkgKyB5bGFiKCJOdW1iZXIgb2YgcmVzcG9uZGVudHMiKSArIHRoZW1lX2J3KCkgKyBnZ3RpdGxlKCJUaW1lIFNwZW50IG9uIERhdGEtUmVsYXRlZCBXb3JrIGZvciBTcGVjaWFsaXN0cyIpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsgeGxhYigiUGVyY2VudCBvZiB0aW1lIikNCg0KZ3JvdXAyICU+JSBnZ3Bsb3QoYWVzKHggPSBkYXRhX3RpbWUpKSArIGdlb21faGlzdG9ncmFtKCkgKyB5bGFiKCJOdW1iZXIgb2YgcmVzcG9uZGVudHMiKSArIHRoZW1lX2J3KCkgKyBnZ3RpdGxlKCJUaW1lIFNwZW50IG9uIERhdGEtUmVsYXRlZCBXb3JrIGZvciBHZW5lcmFsaXN0cyIpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsgeGxhYigiUGVyY2VudCBvZiB0aW1lIikNCg0KDQpgYGANCg0KYGBge3J9DQpncm91cDEkbWFqb3JpdHlfZGF0YV90aW1lIDwtIGlmZWxzZShncm91cDEkZGF0YV90aW1lID49IDUwLA0KYygieWVzIiksIGMoIm5vIikpIA0KZ3JvdXAxJG1ham9yaXR5X2RhdGFfdGltZSA8LSBhcy5mYWN0b3IoZ3JvdXAxJG1ham9yaXR5X2RhdGFfdGltZSkNCg0KZ3JvdXAyJG1ham9yaXR5X2RhdGFfdGltZSA8LSBpZmVsc2UoZ3JvdXAyJGRhdGFfdGltZSA+PSA1MCwNCmMoInllcyIpLCBjKCJubyIpKSANCmdyb3VwMiRtYWpvcml0eV9kYXRhX3RpbWUgPC0gYXMuZmFjdG9yKGdyb3VwMiRtYWpvcml0eV9kYXRhX3RpbWUpDQoNCg0KYGBgDQoNCmBgYHtyfQ0KdF9ncm91cDEgPC0gZ3JvdXAxWyxjKDEsIDIxOjY4KV0NCnRfZ3JvdXAyIDwtIGdyb3VwMlssYygxLCAyMTo2OCldDQoNCm1lbHRlZF9nMSA8LSB0YmxfZGYobWVsdCh0X2dyb3VwMSwgaWQgPSBjKCJKb2IgdGl0bGUiKSkpDQpuYW1lcyhtZWx0ZWRfZzEpWzFdIDwtICJ0aXRsZSINCg0KZzEgPC0gZ2F0aGVyKG1lbHRlZF9nMSwgdmFyaWFibGUsIHZhbHVlLCAtdGl0bGUpICU+JQ0KICBncm91cF9ieSh0aXRsZSwgdmFyaWFibGUsIHZhbHVlKSAlPiUNCiAgdGFsbHkgJT4lIA0KICBzdWJzZXQodmFyaWFibGUgPT0gInZhbHVlIikNCg0KDQoNCg0KYGBgDQoNCg==