This lab journal shows the data preparation for analyses ‘starting to
publish’ and ‘stopping to publish’
Custom functions
package.check
: Check if packages are installed (and
install if not) in R (source).
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)
}
})
}
Packages
tidyverse
: for tidy data manipulations
dplyr
: for data manipulations
zoo
: to calculate running averages and minimums for the
‘stopping to publish’ analyses
packages = c("tidyverse", "dplyr", "zoo")
fpackage.check(packages)
Creating a person-period file
We start by cleaning up the publications data, and then summarize the
publications per PhD per year (i.e. a person-period data format).
pubs_metadf$type <- as.factor(pubs_metadf$type)
# selecting only articles, books and book chapters
pubs_metadf <- pubs_metadf[(pubs_metadf$type == "Artikel") | (pubs_metadf$type == "Boek" | (pubs_metadf$type ==
"Boekdeel")), ]
# derive publication year from variable Data.issued
pubs_metadf <- pubs_metadf[!is.na(pubs_metadf$date.issued), ]
pubs_metadf %>%
mutate(pub_year = as.character(date.issued)) %>%
mutate(pub_year = substr(pub_year, 1, 4)) %>%
mutate(pub_year = as.numeric(pub_year)) -> pubs_metadf
# last complete year of publications data is 2022, so remove publications from the following years
pubs_metadf <- pubs_metadf[pubs_metadf$pub_year < 2023, ]
# now, we create a person-period file: one row per PhD per year
pubs_metadf %>%
group_by(id, pub_year) %>%
dplyr::summarize(npubs = n(), id = first(id)) -> df_ppf
That looks something like this:
knitr::kable(df_ppf, format = "markdown")
f |
2009 |
2 |
f |
2011 |
1 |
f |
2012 |
2 |
f |
2013 |
4 |
f |
2014 |
1 |
f |
2019 |
2 |
g |
2016 |
1 |
g |
2017 |
1 |
g |
2018 |
3 |
g |
2020 |
3 |
g |
2021 |
4 |
g |
2022 |
10 |
h |
1998 |
1 |
h |
1999 |
1 |
h |
2000 |
1 |
h |
2001 |
1 |
h |
2005 |
3 |
h |
2006 |
2 |
h |
2009 |
3 |
h |
2010 |
2 |
h |
2012 |
2 |
h |
2015 |
1 |
Starting to publish
We create a variable for whether a person has started publishing
within 3 years after their PhD, by looking at the year of first
publication and the PhD year.
# We add PhD year to the person-period file to select only publications from after the PhD year
year <- subset(phd_df, select = c(id, phd_year))
starting <- df_ppf %>%
left_join(year, by = "id")
starting <- starting[starting$phd_year < starting$pub_year, ] # only pubs after PhD
# We add the year in which a person first published after obtaining their PhD
starting %>%
group_by(id) %>%
dplyr::summarize(pub_min = min(pub_year)) %>%
ungroup() -> minpub
starting %>%
left_join(minpub, by = "id") -> starting
starting <- na.omit(starting) # remove missings
starting <- subset(starting, select = -c(phd_year)) # remove phd year variable again, because it will be added to the data when merging with phd_df
# Now we combine combine the PhD data with the publications data
starting %>%
inner_join(phd_df, by = "id") -> starting
# Creating a variable for whether a person has published within three years after the PhD in other
# words, the first publication should occur less than four years after the year in which their
# dissertation was published
starting$start_pub <- ifelse(starting$pub_min < (starting$phd_year + 4), 1, 0)
Next, we add the “start_pub” variable to the PhD data.
# select only the starting variable and ID to match
starting %>%
select(id, start_pub) -> starting
# keep a single row per individual
starting <- starting[!duplicated(starting$id), ]
# merge onto the PhD dataset
phd_df <- left_join(phd_df, starting, by = "id")
phd_df$start_pub <- ifelse(is.na(phd_df$start_pub), 0, phd_df$start_pub) # all those who do not have a profile automatically score a 0
# because we look at in the 3 years following the PhD for start_pub, we exclude PhDs from cohorts
# after 2019 for cohorts 2020 and later, we have fewer than 3 years of publication data
phd_df <- phd_df[phd_df$phd_year < 2020, ]
# adding a cohort variable : phd_year centered on the minimum
phd_df$phd_cohort <- as.numeric(phd_df$phd_year) - 1990
And save the data for use in the analyses for “starting to
publish”
f |
1 |
LU |
2012 |
22 |
Physical and Mathematical Sciences |
Physical and Mathematical Sciences |
moroccan |
minority |
women |
g |
1 |
UU |
2019 |
29 |
Biological and Health Sciences |
Biological and Health Sciences |
other |
other |
men |
h |
1 |
WUR |
2000 |
10 |
Social and Behavioral Sciences |
Social and Behavioral Sciences |
other |
other |
women |
Stopping to publish
For the analyses under ‘stopping to publish’, we continue with the
sample of PhDs who have ‘started publishing’ according to analysis
#1.
df_stopping <- phd_df[phd_df$start_pub == 1, ]
df_stopping %>%
select(-c(start_pub)) -> df_stopping
We again need the person-period file in order to analyse publications
for PhDs over multiple years, but this time, we want to include rows
with 0 publications. This means we have to start with an empty
person-period file.
pub_year <- c(1988:2022) # this is the time-window in which we scraped data.
npubs_zero <- rep(0, length(pub_year)) # default to 0 publications
id <- unique(df_ppf$id) #identify the unique PHD ids
nid <- length(id)
# based on this info make the empty dataset
pub_year <- rep(pub_year, nid)
npubs_zero <- rep(npubs_zero, nid)
id <- rep(id, each = length(c(1988:2022)))
empty_ppf <- data.frame(id, pub_year, npubs_zero)
Adding info to the empty person-period file
# adding time-invariant variables to the person-period file
df_ppf %>%
inner_join(df_stopping, by = "id") -> df_ppf
# filling up the empty person-period file with the actual publications data
empty_ppf %>%
full_join(df_ppf, by = c("id", "pub_year")) %>%
arrange(id, pub_year) %>%
select(id, pub_year, npubs, gender, ethnicity, ethnicity2, field, field2, uni, phd_year, phd_cohort) ->
df_ppf
# all time-constant vars are empty in rows with 0 pubs. Let's fix this.
df_ppf %>%
group_by(id) %>%
fill(gender, .direction = "downup") %>%
fill(ethnicity, .direction = "downup") %>%
fill(ethnicity2, .direction = "downup") %>%
fill(field, .direction = "downup") %>%
fill(field2, .direction = "downup") %>%
fill(uni, .direction = "downup") %>%
fill(phd_cohort, .direction = "downup") %>%
fill(phd_year, .direction = "downup") %>%
ungroup() -> df_ppf
# replacing NA values in npubs with 0
df_ppf %>%
mutate(npubs = replace_na(npubs, 0)) -> df_ppf
# next, we include a variable with the average number of publications in the previous years rolling
# average here computes the average number of publications in the year t, t-1 and t-2 then we take
# the value of the rolling average for the preceding year in npubs_prev (when it is present) for
# the first year (1988), we take the value of the year itself
df_ppf %>%
group_by(id) %>%
mutate(npubs_rollavg = rollapply(npubs, 3, mean, align = "right", fill = 0), npubs_prev = lag(npubs_rollavg,
n = 1, order_by = pub_year), npubs_prev = ifelse(is.na(npubs_prev), lead(npubs_prev, n = 1, order_by = pub_year),
npubs_prev)) %>%
ungroup() -> df_ppf
# we log-transform the number of publications to account for outliers
df_ppf$npubs_prev_s <- log10(df_ppf$npubs_prev + 1) # +1 to avoid negative infinity
Creating the variable ‘stopping to publish’.
# creating a time variable (= how many years since obtaining doctorate); removing years before
# doctorate
df_ppf %>%
mutate(time = as.numeric(pub_year - phd_year)) -> df_ppf
# We look at cohorts 1990-2018 because we selected publishing scholars from the analyses for
# 'starting to publish', PhDs from cohort 2019 cannot become inactive by design
df_ppf3 <- df_ppf[df_ppf$phd_year < 2019, ]
# we compute rolling maximum across 3 time periods align = left ensures that it looks forward in
# time: no publication at t, but 1+ pub at t+1 or t+2 ensures non-zero pubs at t.
df_ppf3 %>%
group_by(id) %>%
mutate(npubs3 = rollapply(npubs, 3, sum, align = "left", fill = "extend")) %>%
ungroup() -> df_ppf3
# if a person did not have any publications in the three year period (i.e. the max is 0), we assume
# career exit
df_ppf3 %>%
mutate(inactive = ifelse((npubs3 > 0), 0, 1)) -> df_ppf3
# we only look at publications after the PhD
df_ppf3 <- df_ppf3[(df_ppf3$pub_year > df_ppf3$phd_year), ]
# currently, a person can become inactive, and then active again. We only look at the first
# transition to inactivity.
df_ppf3 %>%
group_by(id) %>%
dplyr::arrange(time, .by_group = TRUE) %>%
mutate(inactive_cs = cumsum(inactive)) %>%
filter(inactive_cs < 2) -> df_ppf3
Alternative time window: 5 years
# Alternative publication window: 5 year
df_ppf5 <- df_ppf[df_ppf$phd_year < 2017, ]
df_ppf5 <- df_ppf5 %>%
mutate(npubs = replace_na(npubs, 0))
# Rolling maximum across 5 years
df_ppf5 %>%
group_by(id) %>%
mutate(npubs5 = rollapply(npubs, 5, sum, align = "left", fill = "extend")) %>%
ungroup() -> df_ppf5
df_ppf5 %>%
mutate(inactive = ifelse((npubs5 > 0), 0, 1)) -> df_ppf5
df_ppf5 <- df_ppf5[(df_ppf5$pub_year > df_ppf5$phd_year), ]
# Max. 1 time inactive
df_ppf5 %>%
group_by(id) %>%
arrange(time, .by_group = TRUE) %>%
mutate(inactive_cs = cumsum(inactive)) %>%
filter(inactive_cs < 2) -> df_ppf5
df_ppf5 <- na.omit(df_ppf5)
LS0tDQp0aXRsZTogIkRhdGEgcHJlcGFyYXRpb24gJ3N0YXJ0aW5nIHRvIHB1Ymxpc2gnIg0KZGF0ZTogIkxhc3QgY29tcGlsZWQgb24gYHIgZm9ybWF0KFN5cy50aW1lKCksICclQiwgJVknKWAiDQpvdXRwdXQ6IA0KICBodG1sX2RvY3VtZW50Og0KICAgIGNzczogdHdlYWtzLmNzcw0KICAgIHRvYzogIHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQogICAgY29kZV9kb3dubG9hZDogeWVzDQoNCi0tLQ0KDQoNCg0KDQpgYGB7ciwgZ2xvYmFsc2V0dGluZ3MsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9ImhpZGUifQ0KDQpsaWJyYXJ5KGtuaXRyKQ0KI2xpYnJhcnkocmdsKQ0Kb3B0c19jaHVuayRzZXQodGlkeS5vcHRzPWxpc3Qod2lkdGguY3V0b2ZmPTEwMCksdGlkeT1UUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSxjb21tZW50ID0gIiM+IiwgY2FjaGU9VFJVRSwgY2xhc3Muc291cmNlPWMoInRlc3QiKSwgY2xhc3Mub3V0cHV0PWMoInRlc3QyIiksIGNhY2hlLmxhenkgPSBGQUxTRSkNCm9wdGlvbnMod2lkdGggPSAxMDApDQpyZ2w6OnNldHVwS25pdHIoKQ0KDQpjb2xvcml6ZSA8LSBmdW5jdGlvbih4LCBjb2xvcikge3NwcmludGYoIjxzcGFuIHN0eWxlPSdjb2xvcjogJXM7Jz4lczwvc3Bhbj4iLCBjb2xvciwgeCkgfQ0KDQpgYGANCg0KYGBge3Iga2xpcHB5LCBlY2hvPUZBTFNFLCBpbmNsdWRlPVRSVUUsIGV2YWw9VFJVRX0NCmtsaXBweTo6a2xpcHB5KHBvc2l0aW9uID0gYygndG9wJywgJ3JpZ2h0JykpDQoja2xpcHB5OjprbGlwcHkoY29sb3IgPSAnZGFya3JlZCcpDQoja2xpcHB5OjprbGlwcHkodG9vbHRpcF9tZXNzYWdlID0gJ0NsaWNrIHRvIGNvcHknLCB0b29sdGlwX3N1Y2Nlc3MgPSAnRG9uZScpDQpgYGANCg0KLS0tLQ0KDQpUaGlzIGxhYiBqb3VybmFsIHNob3dzIHRoZSBkYXRhIHByZXBhcmF0aW9uIGZvciBhbmFseXNlcyAnc3RhcnRpbmcgdG8gcHVibGlzaCcgYW5kICdzdG9wcGluZyB0byBwdWJsaXNoJw0KICANCg0KLS0tLQ0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCg0Kcm0obGlzdCA9IGxzKCkpDQoNCmBgYA0KDQoNCg0KIyBDdXN0b20gZnVuY3Rpb25zDQoNCi0gYHBhY2thZ2UuY2hlY2tgOiBDaGVjayBpZiBwYWNrYWdlcyBhcmUgaW5zdGFsbGVkIChhbmQgaW5zdGFsbCBpZiBub3QpIGluIFIgKFtzb3VyY2VdKGh0dHBzOi8vdmJhbGlnYS5naXRodWIuaW8vdmVyaWZ5LXRoYXQtci1wYWNrYWdlcy1hcmUtaW5zdGFsbGVkLWFuZC1sb2FkZWQvKSkuICANCg0KDQpgYGB7ciwgcmVzdWx0cz0naGlkZSd9DQoNCmZwYWNrYWdlLmNoZWNrIDwtIGZ1bmN0aW9uKHBhY2thZ2VzKSB7DQogIGxhcHBseShwYWNrYWdlcywgRlVOID0gZnVuY3Rpb24oeCkgew0KICAgIGlmICghcmVxdWlyZSh4LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKSB7DQogICAgICBpbnN0YWxsLnBhY2thZ2VzKHgsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogICAgICBsaWJyYXJ5KHgsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkNCiAgICB9DQogIH0pDQp9DQoNCmBgYA0KDQoNCi0tLSAgDQoNCiMgUGFja2FnZXMNCg0KLSBgdGlkeXZlcnNlYDogZm9yIHRpZHkgZGF0YSBtYW5pcHVsYXRpb25zDQotIGBkcGx5cmA6IGZvciBkYXRhIG1hbmlwdWxhdGlvbnMNCi0gYHpvb2A6IHRvIGNhbGN1bGF0ZSBydW5uaW5nIGF2ZXJhZ2VzIGFuZCBtaW5pbXVtcyBmb3IgdGhlICdzdG9wcGluZyB0byBwdWJsaXNoJyBhbmFseXNlcw0KDQoNCmBgYHtyLCByZXN1bHRzPSdoaWRlJ30NCg0KcGFja2FnZXMgPSBjKCJ0aWR5dmVyc2UiLCAiZHBseXIiLCAiem9vIikNCg0KZnBhY2thZ2UuY2hlY2socGFja2FnZXMpDQoNCmBgYA0KDQoNCi0tLSANCg0KIyBJbnB1dA0KDQoNCg0KV2UgdXNlIG9uZSBwcm9jZXNzZWQgZGF0YXNldDoNCg0KKiBbcGhkZmllbGRdKCJodHRwczovL2dpdGh1Yi5jb20vYW1tdWxkZXJzL2FtYXR0ZXJvZnRpbWUvZGF0YS9wcm9jZXNzZWQvcGhkZmllbGQucmRhIik6IHByb2Nlc3NlZCBleGFtcGxlIGRhdGFzZXQgb2YgUGhEcyB3aXRoIGdlbmRlciwgZXRobmljaXR5IGFuZCBmaWVsZCB2YXJpYWJsZXMNCiAgICAtIG5hbWUgb2YgZGF0YXNldDogYHBoZF9maWVsZGAgLyBgcGhkX2RmYCANCiAgICANCkZ1cnRoZXIsIHdlIHVzZSBvbmUgcmF3IGRhdGFzZXQ6DQoqIFtwdWJzX21ldGFkZl0oImh0dHBzOi8vZ2l0aHViLmNvbS9hbW11bGRlcnMvYW1hdHRlcm9mdGltZS9kYXRhL3B1YnNfbWV0YWRmLnJkYSIpOiBwdWJsaWNhdGlvbnMgZGF0YXNldCAgIA0KICAgIC0gbmFtZSBvZiBkYXRhc2V0OiBgcHVic19tZXRhZGZgIA0KDQoNCmBgYHtyIGRhdGF9DQoNCmxvYWQoZmlsZSA9ICIuL2RhdGEvcHJvY2Vzc2VkL3BoZGZpZWxkLnJkYSIpDQpwaGRfZGYgPC0gcGhkZmllbGQNCg0KbG9hZChmaWxlID0gIi4vZGF0YS9wdWJzX21ldGFkZi5yZGEiKQ0KDQpgYGANCg0KDQoNCg0KIyBDcmVhdGluZyBhIHBlcnNvbi1wZXJpb2QgZmlsZQ0KDQpXZSBzdGFydCBieSBjbGVhbmluZyB1cCB0aGUgcHVibGljYXRpb25zIGRhdGEsIGFuZCB0aGVuIHN1bW1hcml6ZSB0aGUgcHVibGljYXRpb25zIHBlciBQaEQgcGVyIHllYXIgKGkuZS4gYSBwZXJzb24tcGVyaW9kIGRhdGEgZm9ybWF0KS4gDQoNCg0KYGBge3J9DQoNCnB1YnNfbWV0YWRmJHR5cGUgPC0gYXMuZmFjdG9yKHB1YnNfbWV0YWRmJHR5cGUpDQoNCiMgc2VsZWN0aW5nIG9ubHkgYXJ0aWNsZXMsIGJvb2tzIGFuZCBib29rIGNoYXB0ZXJzDQpwdWJzX21ldGFkZiA8LSBwdWJzX21ldGFkZlsocHVic19tZXRhZGYkdHlwZT09IkFydGlrZWwiKSB8IChwdWJzX21ldGFkZiR0eXBlPT0iQm9layIgfCAocHVic19tZXRhZGYkdHlwZT09IkJvZWtkZWVsIikpLCBdICANCg0KIyBkZXJpdmUgcHVibGljYXRpb24geWVhciBmcm9tIHZhcmlhYmxlIERhdGEuaXNzdWVkDQpwdWJzX21ldGFkZiA8LSBwdWJzX21ldGFkZlshaXMubmEocHVic19tZXRhZGYkZGF0ZS5pc3N1ZWQpLF0NCg0KDQpwdWJzX21ldGFkZiAlPiUgDQogIG11dGF0ZShwdWJfeWVhciA9IGFzLmNoYXJhY3RlcihkYXRlLmlzc3VlZCkpICU+JQ0KICBtdXRhdGUocHViX3llYXIgPSBzdWJzdHIocHViX3llYXIsIDEsNCkpICU+JQ0KICBtdXRhdGUocHViX3llYXIgPSBhcy5udW1lcmljKHB1Yl95ZWFyKSkgLT4gcHVic19tZXRhZGYNCg0KIyBsYXN0IGNvbXBsZXRlIHllYXIgb2YgcHVibGljYXRpb25zIGRhdGEgaXMgMjAyMiwgc28gcmVtb3ZlIHB1YmxpY2F0aW9ucyBmcm9tIHRoZSBmb2xsb3dpbmcgeWVhcnMNCnB1YnNfbWV0YWRmIDwtIHB1YnNfbWV0YWRmW3B1YnNfbWV0YWRmJHB1Yl95ZWFyPDIwMjMsXQ0KDQoNCiMgbm93LCB3ZSBjcmVhdGUgYSBwZXJzb24tcGVyaW9kIGZpbGU6IG9uZSByb3cgcGVyIFBoRCBwZXIgeWVhcg0KcHVic19tZXRhZGYgJT4lIA0KICBncm91cF9ieShpZCwgcHViX3llYXIpICU+JQ0KICBkcGx5cjo6c3VtbWFyaXplKG5wdWJzID0gbigpLA0KICAgICAgICAgICAgaWQgPSBmaXJzdChpZCkpIC0+IGRmX3BwZg0KDQoNCmBgYA0KDQpUaGF0IGxvb2tzIHNvbWV0aGluZyBsaWtlIHRoaXM6IA0KDQpgYGB7cn0NCg0Ka25pdHI6OmthYmxlKGRmX3BwZiwgZm9ybWF0PSJtYXJrZG93biIpDQoNCmBgYA0KDQoNCg0KDQojIFN0YXJ0aW5nIHRvIHB1Ymxpc2gNCg0KV2UgY3JlYXRlIGEgdmFyaWFibGUgZm9yIHdoZXRoZXIgYSBwZXJzb24gaGFzIHN0YXJ0ZWQgcHVibGlzaGluZyB3aXRoaW4gMyB5ZWFycyBhZnRlciB0aGVpciBQaEQsIGJ5IGxvb2tpbmcgYXQgdGhlIHllYXIgb2YgZmlyc3QgcHVibGljYXRpb24gYW5kIHRoZSBQaEQgeWVhci4gIA0KDQpgYGB7cn0NCg0KIyBXZSBhZGQgUGhEIHllYXIgdG8gdGhlIHBlcnNvbi1wZXJpb2QgZmlsZSB0byBzZWxlY3Qgb25seSBwdWJsaWNhdGlvbnMgZnJvbSBhZnRlciB0aGUgUGhEIHllYXINCnllYXIgPC0gc3Vic2V0KHBoZF9kZiwgc2VsZWN0PWMoaWQsIHBoZF95ZWFyKSkNCnN0YXJ0aW5nIDwtIGRmX3BwZiAlPiUgbGVmdF9qb2luKHllYXIsIGJ5ID0gImlkIikgDQoNCnN0YXJ0aW5nIDwtIHN0YXJ0aW5nW3N0YXJ0aW5nJHBoZF95ZWFyPHN0YXJ0aW5nJHB1Yl95ZWFyLF0gIyBvbmx5IHB1YnMgYWZ0ZXIgUGhEDQoNCg0KIyBXZSBhZGQgdGhlIHllYXIgaW4gd2hpY2ggYSBwZXJzb24gZmlyc3QgcHVibGlzaGVkIGFmdGVyIG9idGFpbmluZyB0aGVpciBQaEQNCnN0YXJ0aW5nICU+JQ0KICBncm91cF9ieShpZCkgJT4lDQogIGRwbHlyOjpzdW1tYXJpemUocHViX21pbiA9IG1pbihwdWJfeWVhcikpICU+JQ0KICB1bmdyb3VwKCkgLT4gbWlucHViDQoNCnN0YXJ0aW5nICU+JQ0KICBsZWZ0X2pvaW4obWlucHViLCBieSA9ICJpZCIpIC0+IHN0YXJ0aW5nDQoNCnN0YXJ0aW5nIDwtIG5hLm9taXQoc3RhcnRpbmcpICMgcmVtb3ZlIG1pc3NpbmdzIA0KDQpzdGFydGluZyA8LSBzdWJzZXQoc3RhcnRpbmcsIHNlbGVjdD0tYyhwaGRfeWVhcikpICMgcmVtb3ZlIHBoZCB5ZWFyIHZhcmlhYmxlIGFnYWluLCBiZWNhdXNlIGl0IHdpbGwgYmUgYWRkZWQgdG8gdGhlIGRhdGEgd2hlbiBtZXJnaW5nIHdpdGggcGhkX2RmDQoNCg0KIyBOb3cgd2UgY29tYmluZSBjb21iaW5lIHRoZSBQaEQgZGF0YSB3aXRoIHRoZSBwdWJsaWNhdGlvbnMgZGF0YQ0Kc3RhcnRpbmcgJT4lIGlubmVyX2pvaW4ocGhkX2RmLCBieT0iaWQiKSAtPiBzdGFydGluZw0KDQoNCiMgQ3JlYXRpbmcgYSB2YXJpYWJsZSBmb3Igd2hldGhlciBhIHBlcnNvbiBoYXMgcHVibGlzaGVkIHdpdGhpbiB0aHJlZSB5ZWFycyBhZnRlciB0aGUgUGhEDQojIGluIG90aGVyIHdvcmRzLCB0aGUgZmlyc3QgcHVibGljYXRpb24gc2hvdWxkIG9jY3VyIGxlc3MgdGhhbiBmb3VyIHllYXJzIGFmdGVyIHRoZSB5ZWFyIGluIHdoaWNoIHRoZWlyIGRpc3NlcnRhdGlvbiB3YXMgcHVibGlzaGVkDQpzdGFydGluZyRzdGFydF9wdWIgPC0gaWZlbHNlKHN0YXJ0aW5nJHB1Yl9taW4gPCAoc3RhcnRpbmckcGhkX3llYXIgKyA0KSwgMSwgMCkNCg0KYGBgDQoNCg0KTmV4dCwgd2UgYWRkIHRoZSAic3RhcnRfcHViIiB2YXJpYWJsZSB0byB0aGUgUGhEIGRhdGEuIA0KDQpgYGAge3J9DQoNCiMgc2VsZWN0IG9ubHkgdGhlIHN0YXJ0aW5nIHZhcmlhYmxlIGFuZCBJRCB0byBtYXRjaA0Kc3RhcnRpbmcgJT4lDQogIHNlbGVjdChpZCwgc3RhcnRfcHViKSAtPiBzdGFydGluZw0KDQojIGtlZXAgYSBzaW5nbGUgcm93IHBlciBpbmRpdmlkdWFsDQpzdGFydGluZyA8LSBzdGFydGluZ1shZHVwbGljYXRlZChzdGFydGluZyRpZCksXQ0KDQoNCiMgbWVyZ2Ugb250byB0aGUgUGhEIGRhdGFzZXQNCnBoZF9kZiA8LSBsZWZ0X2pvaW4ocGhkX2RmLCBzdGFydGluZywgYnkgPSAiaWQiKSAgICAgDQoNCnBoZF9kZiRzdGFydF9wdWIgPC0gaWZlbHNlKGlzLm5hKHBoZF9kZiRzdGFydF9wdWIpLCAwLCBwaGRfZGYkc3RhcnRfcHViKSAjIGFsbCB0aG9zZSB3aG8gZG8gbm90IGhhdmUgYSBwcm9maWxlIGF1dG9tYXRpY2FsbHkgc2NvcmUgYSAwDQoNCg0KIyBiZWNhdXNlIHdlIGxvb2sgYXQgaW4gdGhlIDMgeWVhcnMgZm9sbG93aW5nIHRoZSBQaEQgZm9yIHN0YXJ0X3B1Yiwgd2UgZXhjbHVkZSBQaERzIGZyb20gY29ob3J0cyBhZnRlciAyMDE5DQojIGZvciBjb2hvcnRzIDIwMjAgYW5kIGxhdGVyLCB3ZSBoYXZlIGZld2VyIHRoYW4gMyB5ZWFycyBvZiBwdWJsaWNhdGlvbiBkYXRhDQpwaGRfZGYgPC0gcGhkX2RmW3BoZF9kZiRwaGRfeWVhcjwyMDIwLF0NCg0KDQojIGFkZGluZyBhIGNvaG9ydCB2YXJpYWJsZSA6IHBoZF95ZWFyIGNlbnRlcmVkIG9uIHRoZSBtaW5pbXVtDQpwaGRfZGYkcGhkX2NvaG9ydCA8LSBhcy5udW1lcmljKHBoZF9kZiRwaGRfeWVhcikgLSAxOTkwDQoNCg0KYGBgDQoNCg0KDQpBbmQgc2F2ZSB0aGUgZGF0YSBmb3IgdXNlIGluIHRoZSBhbmFseXNlcyBmb3IgInN0YXJ0aW5nIHRvIHB1Ymxpc2giDQpgYGB7ciwgZWNobz1GQUxTRX0NCg0KZGZfc3RhcnRpbmcgPC0gc3Vic2V0KHBoZF9kZiwgc2VsZWN0ID0gYyhpZCwgc3RhcnRfcHViLCB1bmksIHBoZF95ZWFyLCBwaGRfY29ob3J0LCBmaWVsZCwgZmllbGQyLCBldGhuaWNpdHksIGV0aG5pY2l0eTIsIGdlbmRlcikpDQoNCmtuaXRyOjprYWJsZShkZl9zdGFydGluZywgZm9ybWF0PSJtYXJrZG93biIpDQoNCg0KYGBgDQoNCg0KDQojIFN0b3BwaW5nIHRvIHB1Ymxpc2ggDQoNCkZvciB0aGUgYW5hbHlzZXMgdW5kZXIgJ3N0b3BwaW5nIHRvIHB1Ymxpc2gnLCB3ZSBjb250aW51ZSB3aXRoIHRoZSBzYW1wbGUgb2YgUGhEcyB3aG8gaGF2ZSAnc3RhcnRlZCBwdWJsaXNoaW5nJyBhY2NvcmRpbmcgdG8gYW5hbHlzaXMgIzEuIA0KDQoNCmBgYHtyfQ0KDQpkZl9zdG9wcGluZyA8LSBwaGRfZGZbcGhkX2RmJHN0YXJ0X3B1Yj09MSwgXQ0KDQpkZl9zdG9wcGluZyAlPiUNCiAgc2VsZWN0KC1jKHN0YXJ0X3B1YikpIC0+IGRmX3N0b3BwaW5nDQoNCmBgYA0KDQoNCldlIGFnYWluIG5lZWQgdGhlIHBlcnNvbi1wZXJpb2QgZmlsZSBpbiBvcmRlciB0byBhbmFseXNlIHB1YmxpY2F0aW9ucyBmb3IgUGhEcyBvdmVyIG11bHRpcGxlIHllYXJzLCBidXQgdGhpcyB0aW1lLCB3ZSB3YW50IHRvIGluY2x1ZGUgcm93cyB3aXRoIDAgcHVibGljYXRpb25zLiBUaGlzIG1lYW5zIHdlIGhhdmUgdG8gc3RhcnQgd2l0aCBhbiBlbXB0eSBwZXJzb24tcGVyaW9kIGZpbGUuIA0KDQpgYGB7cn0NCg0KcHViX3llYXIgPC0gYygxOTg4OjIwMjIpICMgdGhpcyBpcyB0aGUgdGltZS13aW5kb3cgaW4gd2hpY2ggd2Ugc2NyYXBlZCBkYXRhLiANCg0KbnB1YnNfemVybyA8LSByZXAoMCwgbGVuZ3RoKHB1Yl95ZWFyKSkgIyBkZWZhdWx0IHRvIDAgcHVibGljYXRpb25zDQoNCmlkIDwtIHVuaXF1ZShkZl9wcGYkaWQpICNpZGVudGlmeSB0aGUgdW5pcXVlIFBIRCBpZHMNCg0KbmlkIDwtIGxlbmd0aChpZCkNCg0KI2Jhc2VkIG9uIHRoaXMgaW5mbyBtYWtlIHRoZSBlbXB0eSBkYXRhc2V0DQpwdWJfeWVhciA8LSByZXAocHViX3llYXIsIG5pZCkNCm5wdWJzX3plcm8gPC0gcmVwKG5wdWJzX3plcm8sIG5pZCkNCmlkIDwtIHJlcChpZCwgZWFjaD1sZW5ndGgoYygxOTg4OjIwMjIpKSkgIA0KZW1wdHlfcHBmIDwtIGRhdGEuZnJhbWUoaWQsIHB1Yl95ZWFyLCBucHVic196ZXJvKQ0KDQpgYGAgDQoNCg0KQWRkaW5nIGluZm8gdG8gdGhlIGVtcHR5IHBlcnNvbi1wZXJpb2QgZmlsZSANCg0KYGBge3J9DQoNCiMgYWRkaW5nIHRpbWUtaW52YXJpYW50IHZhcmlhYmxlcyB0byB0aGUgcGVyc29uLXBlcmlvZCBmaWxlDQpkZl9wcGYgJT4lIGlubmVyX2pvaW4oZGZfc3RvcHBpbmcsIGJ5PSJpZCIpIC0+IGRmX3BwZg0KDQoNCiMgZmlsbGluZyB1cCB0aGUgZW1wdHkgcGVyc29uLXBlcmlvZCBmaWxlIHdpdGggdGhlIGFjdHVhbCBwdWJsaWNhdGlvbnMgZGF0YQ0KZW1wdHlfcHBmICU+JSANCiAgZnVsbF9qb2luKGRmX3BwZiwgYnk9YygiaWQiLCAicHViX3llYXIiKSkgJT4lDQogIGFycmFuZ2UoaWQsIHB1Yl95ZWFyKSAlPiUNCiAgc2VsZWN0KGlkLCBwdWJfeWVhciwgbnB1YnMsIGdlbmRlciwgZXRobmljaXR5LCBldGhuaWNpdHkyLCBmaWVsZCwgZmllbGQyLCB1bmksIHBoZF95ZWFyLCBwaGRfY29ob3J0KSAtPiBkZl9wcGYNCg0KDQojIGFsbCB0aW1lLWNvbnN0YW50IHZhcnMgYXJlIGVtcHR5IGluIHJvd3Mgd2l0aCAwIHB1YnMuIExldCdzIGZpeCB0aGlzLiANCmRmX3BwZiAlPiUNCiAgZ3JvdXBfYnkoaWQpICU+JQ0KICBmaWxsKGdlbmRlciwgLmRpcmVjdGlvbiA9ICJkb3dudXAiKSAlPiUNCiAgZmlsbChldGhuaWNpdHksIC5kaXJlY3Rpb24gPSAiZG93bnVwIikgJT4lDQogIGZpbGwoZXRobmljaXR5MiwgLmRpcmVjdGlvbiA9ICJkb3dudXAiKSAlPiUNCiAgZmlsbChmaWVsZCwgLmRpcmVjdGlvbiA9ICJkb3dudXAiKSAlPiUNCiAgZmlsbChmaWVsZDIsIC5kaXJlY3Rpb24gPSAiZG93bnVwIikgJT4lDQogIGZpbGwodW5pLCAuZGlyZWN0aW9uID0gImRvd251cCIpICU+JQ0KICBmaWxsKHBoZF9jb2hvcnQsIC5kaXJlY3Rpb24gPSAiZG93bnVwIikgJT4lDQogIGZpbGwocGhkX3llYXIsIC5kaXJlY3Rpb24gPSAiZG93bnVwIikgJT4lDQogIHVuZ3JvdXAgKCkgLT4gZGZfcHBmDQoNCiMgcmVwbGFjaW5nIE5BIHZhbHVlcyBpbiBucHVicyB3aXRoIDANCmRmX3BwZiAlPiUgbXV0YXRlKG5wdWJzID0gcmVwbGFjZV9uYShucHVicywgMCkpIC0+IGRmX3BwZg0KDQoNCiMgbmV4dCwgd2UgaW5jbHVkZSBhIHZhcmlhYmxlIHdpdGggdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIHB1YmxpY2F0aW9ucyBpbiB0aGUgcHJldmlvdXMgeWVhcnMNCiMgcm9sbGluZyBhdmVyYWdlIGhlcmUgY29tcHV0ZXMgdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIHB1YmxpY2F0aW9ucyBpbiB0aGUgeWVhciB0LCB0LTEgYW5kIHQtMg0KIyB0aGVuIHdlIHRha2UgdGhlIHZhbHVlIG9mIHRoZSByb2xsaW5nIGF2ZXJhZ2UgZm9yIHRoZSBwcmVjZWRpbmcgeWVhciBpbiBucHVic19wcmV2ICh3aGVuIGl0IGlzIHByZXNlbnQpDQojIGZvciB0aGUgZmlyc3QgeWVhciAoMTk4OCksIHdlIHRha2UgdGhlIHZhbHVlIG9mIHRoZSB5ZWFyIGl0c2VsZg0KZGZfcHBmICU+JQ0KICBncm91cF9ieShpZCkgJT4lDQogIG11dGF0ZShucHVic19yb2xsYXZnID0gcm9sbGFwcGx5KG5wdWJzLCAzLCBtZWFuLCBhbGlnbiA9ICJyaWdodCIsIGZpbGwgPSAwKSwgDQogICAgICAgICBucHVic19wcmV2ID0gbGFnKG5wdWJzX3JvbGxhdmcsIG49MSwgb3JkZXJfYnkgPSBwdWJfeWVhciksDQogICAgICAgICBucHVic19wcmV2ID0gaWZlbHNlKGlzLm5hKG5wdWJzX3ByZXYpLCBsZWFkKG5wdWJzX3ByZXYsIG49MSwgb3JkZXJfYnkgPSBwdWJfeWVhciksIG5wdWJzX3ByZXYpKSAlPiUNCiAgdW5ncm91cCgpIC0+IGRmX3BwZg0KDQoNCiMgd2UgbG9nLXRyYW5zZm9ybSB0aGUgbnVtYmVyIG9mIHB1YmxpY2F0aW9ucyB0byBhY2NvdW50IGZvciBvdXRsaWVycyANCmRmX3BwZiRucHVic19wcmV2X3MgPC0gbG9nMTAoZGZfcHBmJG5wdWJzX3ByZXYgKyAxKSAjICsxIHRvIGF2b2lkIG5lZ2F0aXZlIGluZmluaXR5DQoNCg0KYGBgDQoNCg0KDQpDcmVhdGluZyB0aGUgdmFyaWFibGUgJ3N0b3BwaW5nIHRvIHB1Ymxpc2gnLiANCg0KYGBge3J9DQoNCiMgY3JlYXRpbmcgYSB0aW1lIHZhcmlhYmxlICg9IGhvdyBtYW55IHllYXJzIHNpbmNlIG9idGFpbmluZyBkb2N0b3JhdGUpOyByZW1vdmluZyB5ZWFycyBiZWZvcmUgZG9jdG9yYXRlIA0KZGZfcHBmICU+JQ0KICBtdXRhdGUodGltZSA9IGFzLm51bWVyaWMocHViX3llYXIgLSBwaGRfeWVhcikpIC0+IGRmX3BwZg0KDQojIFdlIGxvb2sgYXQgY29ob3J0cyAxOTkwLTIwMTgNCiMgYmVjYXVzZSB3ZSBzZWxlY3RlZCBwdWJsaXNoaW5nIHNjaG9sYXJzIGZyb20gdGhlIGFuYWx5c2VzIGZvciAic3RhcnRpbmcgdG8gcHVibGlzaCIsIFBoRHMgZnJvbSBjb2hvcnQgMjAxOSBjYW5ub3QgYmVjb21lIGluYWN0aXZlIGJ5IGRlc2lnbg0KZGZfcHBmMyA8LSBkZl9wcGZbZGZfcHBmJHBoZF95ZWFyPDIwMTksIF0NCg0KDQojIHdlIGNvbXB1dGUgcm9sbGluZyBtYXhpbXVtIGFjcm9zcyAzIHRpbWUgcGVyaW9kcw0KIyBhbGlnbiA9IGxlZnQgZW5zdXJlcyB0aGF0IGl0IGxvb2tzIGZvcndhcmQgaW4gdGltZTogbm8gcHVibGljYXRpb24gYXQgdCwgYnV0IDErIHB1YiBhdCB0KzEgb3IgdCsyIGVuc3VyZXMgbm9uLXplcm8gcHVicyBhdCB0LiANCmRmX3BwZjMgJT4lDQogIGdyb3VwX2J5KGlkKSAlPiUNCiAgbXV0YXRlKG5wdWJzMyA9IHJvbGxhcHBseShucHVicywgMywgc3VtLCBhbGlnbiA9ICJsZWZ0IiwgZmlsbCA9ICJleHRlbmQiKSkgJT4lDQogIHVuZ3JvdXAoKSAtPiBkZl9wcGYzDQoNCiMgaWYgYSBwZXJzb24gZGlkIG5vdCBoYXZlIGFueSBwdWJsaWNhdGlvbnMgaW4gdGhlIHRocmVlIHllYXIgcGVyaW9kIChpLmUuIHRoZSBtYXggaXMgMCksIHdlIGFzc3VtZSBjYXJlZXIgZXhpdA0KZGZfcHBmMyAlPiUNCiAgbXV0YXRlKGluYWN0aXZlID0gaWZlbHNlKChucHViczM+MCksIDAsIDEpKSAtPiBkZl9wcGYzDQoNCg0KIyB3ZSBvbmx5IGxvb2sgYXQgcHVibGljYXRpb25zIGFmdGVyIHRoZSBQaEQNCmRmX3BwZjMgPC0gZGZfcHBmM1soZGZfcHBmMyRwdWJfeWVhciA+IGRmX3BwZjMkcGhkX3llYXIpLCBdDQoNCg0KDQojIGN1cnJlbnRseSwgYSBwZXJzb24gY2FuIGJlY29tZSBpbmFjdGl2ZSwgYW5kIHRoZW4gYWN0aXZlIGFnYWluLiBXZSBvbmx5IGxvb2sgYXQgdGhlIGZpcnN0IHRyYW5zaXRpb24gdG8gaW5hY3Rpdml0eS4gDQpkZl9wcGYzICU+JSANCglncm91cF9ieShpZCkgJT4lIA0KICBkcGx5cjo6YXJyYW5nZSh0aW1lLCAuYnlfZ3JvdXA9VFJVRSkgJT4lDQoJbXV0YXRlKGluYWN0aXZlX2NzID0gY3Vtc3VtKGluYWN0aXZlKSkgJT4lDQogIGZpbHRlcihpbmFjdGl2ZV9jcyA8IDIpIC0+IGRmX3BwZjMNCg0KDQpgYGANCg0KDQoNCkFsdGVybmF0aXZlIHRpbWUgd2luZG93OiA1IHllYXJzDQoNCmBgYHtyfQ0KDQojIEFsdGVybmF0aXZlIHB1YmxpY2F0aW9uIHdpbmRvdzogNSB5ZWFyDQpkZl9wcGY1IDwtIGRmX3BwZltkZl9wcGYkcGhkX3llYXI8MjAxNywgXQ0KZGZfcHBmNSA8LSBkZl9wcGY1ICU+JSBtdXRhdGUobnB1YnMgPSByZXBsYWNlX25hKG5wdWJzLCAwKSkNCg0KIyBSb2xsaW5nIG1heGltdW0gYWNyb3NzIDUgeWVhcnMNCmRmX3BwZjUgJT4lDQogIGdyb3VwX2J5KGlkKSAlPiUNCiAgbXV0YXRlKG5wdWJzNSA9IHJvbGxhcHBseShucHVicywgNSwgc3VtLCBhbGlnbiA9ICJsZWZ0IiwgZmlsbCA9ICJleHRlbmQiKSkgJT4lDQogIHVuZ3JvdXAoKSAtPiBkZl9wcGY1DQoNCmRmX3BwZjUgJT4lDQogIG11dGF0ZShpbmFjdGl2ZSA9IGlmZWxzZSgobnB1YnM1PjApLCAwLCAxKSkgLT4gZGZfcHBmNQ0KDQoNCmRmX3BwZjUgPC0gZGZfcHBmNVsoZGZfcHBmNSRwdWJfeWVhciA+IGRmX3BwZjUkcGhkX3llYXIpLCBdDQoNCg0KIyBNYXguIDEgdGltZSBpbmFjdGl2ZQ0KZGZfcHBmNSAlPiUgDQoJZ3JvdXBfYnkoaWQpICU+JSANCiAgYXJyYW5nZSh0aW1lLCAuYnlfZ3JvdXA9VFJVRSkgJT4lDQoJbXV0YXRlKGluYWN0aXZlX2NzID0gY3Vtc3VtKGluYWN0aXZlKSkgJT4lDQogIGZpbHRlcihpbmFjdGl2ZV9jcyA8IDIpIC0+IGRmX3BwZjUNCg0KDQpkZl9wcGY1IDwtIG5hLm9taXQoZGZfcHBmNSkNCg0KYGBgDQoNCg0KDQoNCg==
Copyright © 2023