Last compiled on May, 2025
This page describes how we went about for the data manipulation ahead
NSUM Bayesian estimation method, the different scenarios, and the
estimation itself. Note that this was done on a computing cluster, and
it will very likely take a while of you do this locally. Note the
parellel computing as well to speed up the process. We start out with
the data that was gathered from
the questionnaire.
Initatiating R
environment
Start out with a custom function to load a set of required
packages.
# packages and read data
rm(list = ls())
# (c) Jochem Tolsma
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 = c("haven", "NSUM", "coda", "matrixStats", "parallel", "MASS", "doParallel")
fpackage.check(packages)
Loading the data
We then load the data as we got them from IO research. Based on the
questionnaire as shown here. Note that I check whether there are any
missings on the demographic variables first (column 89 to 100).
# repo <- '/yourscratch/' # yourpath
df <- read_dta("data/DQUESTUU_eindbestand.dta")
# df <- data.frame(df)
nrow(as.matrix(na.omit(df[, c(89:100)]))) # no NAs on the demographic variables, just a check
Data manipulation
1
I then move to some data manipulation. I extract the columns for the
X categories on University, Applied University, Tertiary Vocational,
Son/Daughter, Randstad, and Corona infections. I also declare the number
of those categories in the entire population in that specific point in
time that the survey was conducted. I extract those columns from the
data as well and fill up the “not knowing someone of that category” with
zeros, such that it works in the estimation process.
#--------------------------------------------------------------------------------
# some data prep
# v1 in data
ref1 <- c(84957, 75214, 145600, 168066, 2500, 29808, 1558549)
# ORDER: uni, hbo, mbo, dochter/zoon, tweeling, randstad, corona
df1_1 <- df[, c(8:14)]
df1_2 <- df[, c(1:7)]
# conditional filling in of zeros when respondents answer 'no' I don't know that person loop of
# collength
for (i in 1:length(df1_2)) {
df1_1[[i]] <- ifelse(df1_2[[i]] == 2, 0, df1_1[[i]])
}
Data manipulation
2
We do the same as above, but for the categories electrical vehicle,
scooter, and vegans. We again declare the population numbers as
well.
#--------------------------------------------------------------------------------
# v2 in data
ref2 <- c(273259, 460618, 261000)
# ORDER: elecauto, scooter, vegan,
df2_1 <- df[, c(18:20)]
df2_2 <- df[, c(15:17)]
# filling of zeros loop of collength
for (i in 1:length(df2_2)) {
df2_1[[i]] <- ifelse(df2_2[[i]] == 2, 0, df2_1[[i]])
}
Data manipulation
3
And again, we do the same as above, but for the name categories (and
see the Name search and Name selection pages so as to find out how we
selected them).
#--------------------------------------------------------------------------------
# v3 in data
ref3 <- c(15276, 16350, 27394, 21200, 25681, 334502, 29955, 266522, 39481, 2692, 136296, 110231, 112807,
98208, 1386, 2186, 11640, 22704, 13276, 40543, 17024, 23167, 307032, 36411, 49182, 186746, 35973,
134956, 118610, 86500, 102296, 4213, 5003, 4517)
# ORDER: Sophie, Julia,Sanne,Lisa,Laura,Maria,Linda,Johanna,Monique,Ester,
# Anna,Elisabeth,Cornelia,Wilhelmina,Amira,Samira,Sara,Daan,Sem,
# Thomas,Max,Kevin,Johannes,Dennis,Jeroen,Jan,Marcel,Cornelis,Hendrik,Petrus,
# Willem,Ali,Mohammed,Noor
df3_1 <- df[, c(55:88)]
df3_2 <- df[, c(21:54)]
# filling in of zeros loop of collength
for (i in 1:length(df3_2)) {
df3_1[[i]] <- ifelse(df3_2[[i]] == 2, 0, df3_1[[i]])
}
Data manipulation
4
Before we move into the estimation, I need to do some final data
manipulation. I first columbind the different X’s together, both the
actual answers from respondents, as well as the population numbers. It
is important that these are in the same order. I also create an object
with the total Dutch population. Note that I drop “Randstad”, as there
is a discrepancy between the survey quesiton and what we know from the
entire population (Randstad versus “four biggest cities”, these are two
different things.)
#--------------------------------------------------------------------------------
# now cbind those matrices together
cont <- cbind(df1_1[, c(1, 2, 3, 4, 5, 7)], df2_1, df3_1)
cont <- as.matrix(na.omit(cont)) # nice, only 60 or so missings
pop <- c(ref1[c(1, 2, 3, 4, 5, 7)], ref2, ref3) #without randstad (only 4 grote steden, not 'randstad'), These are the known population sizes
totpop <- 17407585 # Total dutch population in 2020
#--------------------------------------------------------------------------------
# this is important: these are the data we can directly match to, same row order.
df_nomissings <- cbind(df1_2[, c(1, 2, 3, 4, 5, 7)], df1_1[, c(1, 2, 3, 4, 5, 7)], df2_2, df2_1, df3_2,
df3_1, df[, c(89:100)]) # loose randstad to get at same N
df_nomissings <- na.omit(df_nomissings)
Initiate
estimation
So we set a number of hyperparemeters first, 40K iterations, with a
burnin of 1K, where we retain 4K chains. We do not save the entire NSUM
output object, as we have 172 of those and they are 1GB each. So we
extract the necessary information from each.
# This is where the magic happens So we don't save every NSUM object, it's about 1gb in size per
# heldout category
iters <- 40000
burns <- 1000
retain <- 4000
# paralellize the estimation
closeAllConnections()
numCores <- detectCores()
if (numCores > 42) {
registerDoParallel(cores = 43)
} else {
print("Too few cores!")
}
holdouts <- 1:43
Estimation at tauK
0.25
So this is the parellelized for loop where we estimate the NSUM
output at tauK 0.25. We take starting parameter from the naive NSUM
equation and move forward with that. Note that each of the 43 X
categories is held out once. This implies that we have 43 network sizes
for all respondents using tauK 0.25. We extract the average network size
of the 4K chains for each respondent in each estimation scenario. We
save that in a txt file. We repeat this process for tauK 0.5, 0.75, and
0.99. Only the file names and “tauK.start” value is different in those
scenarios, the rest of the code remains similar. Note that there are
thus 172 output files (4 x 43X’s x 4 tauK scenarios).
#--------------------------------------------------------------------------------
## TauK 0.25
#--------------------------------------------------------------------------------
# some empty lsits to fill up alter on
# note how we don't save any starting values, nor de nsum objects
kds <- list()
kdssd <- list()
data <- list()
# set.seed(3004)
# holdouts <- sort(sample(1:43, 10, replace=FALSE))
foreach (i = holdouts) %dopar% {
cd <- cont[, -c(i)] # Then take out the known subpopulation of interest
# caclulate starting values
z <- NSUM::killworth.start(cbind(cd, cont[, c(i)]), # paste the "takenout" at the max of matrix
pop[-c(i)],
totpop)
# run the Bayesian estimation
degree <- NSUM::nsum.mcmc(cbind(cd, cont[, c(i)]), # gets pasted at the last column
pop[-c(i)],
totpop, model = "combined", # combined control for transmission and recall errors
indices.k = 43, # notice that "holdout" gets pasted as last column
iterations = iters, burnin = burns, size = retain, # 40k iterations, retain 4k chains
d.start = z$d.start, mu.start = z$mu.start, # starting values from simpe estimator
sigma.start = z$sigma.start, NK.start = z$NK.start, tauK.start = 0.25)
# calculate rowmean and it's SD of the retained 4k chains
kds[[i]] <- rowMeans(degree$d.values,na.rm = TRUE) # calculate rowmean of netsize iterations
kdssd[[i]] <- matrixStats::rowSds(degree$d.values) # calculate sd of 4k estimates per row
data[[i]] <- cbind(kds[[i]],kdssd[[i]])
data <- as.data.frame(data[[i]])
# Save the data, new .txt for each iteration, if something goes wrong, we can always start at prior one and combine
write.csv(data, paste0("data/degree_estimates_tauk025_holdout", i, ".txt"))
}
LS0tCnRpdGxlOiAiTlNVTSBlc3RpbWF0aW9uIgojYmlibGlvZ3JhcGh5OiByZWZlcmVuY2VzLmJpYgphdXRob3I6ICJCYXMgSG9mc3RyYSIKLS0tCgpgYGB7ciwgZ2xvYmFsc2V0dGluZ3MsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQpsaWJyYXJ5KGtuaXRyKQoKa25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpvcHRzX2NodW5rJHNldCh0aWR5Lm9wdHM9bGlzdCh3aWR0aC5jdXRvZmY9MTAwKSx0aWR5PVRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLGNvbW1lbnQgPSAiIz4iLCBjYWNoZT1UUlVFLCBjbGFzcy5zb3VyY2U9YygidGVzdCIpLCBjbGFzcy5vdXRwdXQ9YygidGVzdDIiKSkKb3B0aW9ucyh3aWR0aCA9IDEwMCkKcmdsOjpzZXR1cEtuaXRyKCkKCgoKY29sb3JpemUgPC0gZnVuY3Rpb24oeCwgY29sb3IpIHtzcHJpbnRmKCI8c3BhbiBzdHlsZT0nY29sb3I6ICVzOyc+JXM8L3NwYW4+IiwgY29sb3IsIHgpIH0KCmBgYAoKYGBge3Iga2xpcHB5LCBlY2hvPUZBTFNFLCBpbmNsdWRlPVRSVUV9CmtsaXBweTo6a2xpcHB5KHBvc2l0aW9uID0gYygndG9wJywgJ3JpZ2h0JykpCiNrbGlwcHk6OmtsaXBweShjb2xvciA9ICdkYXJrcmVkJykKI2tsaXBweTo6a2xpcHB5KHRvb2x0aXBfbWVzc2FnZSA9ICdDbGljayB0byBjb3B5JywgdG9vbHRpcF9zdWNjZXNzID0gJ0RvbmUnKQpgYGAKCkxhc3QgY29tcGlsZWQgb24gYHIgZm9ybWF0KFN5cy50aW1lKCksICclQiwgJVknKWAKCjxicj4KCi0tLS0KClRoaXMgcGFnZSBkZXNjcmliZXMgaG93IHdlIHdlbnQgYWJvdXQgZm9yIHRoZSBkYXRhIG1hbmlwdWxhdGlvbiBhaGVhZCBOU1VNIEJheWVzaWFuIGVzdGltYXRpb24gbWV0aG9kLCB0aGUgZGlmZmVyZW50IHNjZW5hcmlvcywgYW5kIHRoZSBlc3RpbWF0aW9uIGl0c2VsZi4gTm90ZSB0aGF0IHRoaXMgd2FzIGRvbmUgb24gYSBjb21wdXRpbmcgY2x1c3RlciwgYW5kIGl0IHdpbGwgdmVyeSBsaWtlbHkgdGFrZSBhIHdoaWxlIG9mIHlvdSBkbyB0aGlzIGxvY2FsbHkuIE5vdGUgdGhlIHBhcmVsbGVsIGNvbXB1dGluZyBhcyB3ZWxsIHRvIHNwZWVkIHVwIHRoZSBwcm9jZXNzLiBXZSBzdGFydCBvdXQgd2l0aCB0aGUgZGF0YSB0aGF0IHdhcyBnYXRoZXJlZCBbZnJvbSB0aGUgcXVlc3Rpb25uYWlyZV0oaHR0cHM6Ly9iaG9mc3RyYS5naXRodWIuaW8vbmV0c2l6ZV9kdXRjaC9xdWVzdGlvbm5haXJlLmh0bWwpLgoKPGJyPgoKLS0tLQoKIyBJbml0YXRpYXRpbmcgUiBlbnZpcm9ubWVudAoKU3RhcnQgb3V0IHdpdGggYSBjdXN0b20gZnVuY3Rpb24gdG8gbG9hZCBhIHNldCBvZiByZXF1aXJlZCBwYWNrYWdlcy4KICAKYGBge3IgaW5pdCwgZXZhbD1GQUxTRX0KIyBwYWNrYWdlcyBhbmQgcmVhZCBkYXRhCgpybShsaXN0ID0gbHMoKSkKCiMgKGMpIEpvY2hlbSBUb2xzbWEKZnBhY2thZ2UuY2hlY2sgPC0gZnVuY3Rpb24ocGFja2FnZXMpIHsKICBsYXBwbHkocGFja2FnZXMsIEZVTiA9IGZ1bmN0aW9uKHgpIHsKICAgIGlmICghcmVxdWlyZSh4LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKSB7CiAgICAgIGluc3RhbGwucGFja2FnZXMoeCwgZGVwZW5kZW5jaWVzID0gVFJVRSkKICAgICAgbGlicmFyeSh4LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpCiAgICB9CiAgfSkKfQpwYWNrYWdlcyA9IGMoImhhdmVuIiwgIk5TVU0iLCAiY29kYSIsICJtYXRyaXhTdGF0cyIsICJwYXJhbGxlbCIsICJNQVNTIiwgImRvUGFyYWxsZWwiKQpmcGFja2FnZS5jaGVjayhwYWNrYWdlcykKYGBgCgoKPGJyPgoKLS0tLQoKIyBMb2FkaW5nIHRoZSBkYXRhCgpXZSB0aGVuIGxvYWQgdGhlIGRhdGEgYXMgd2UgZ290IHRoZW0gZnJvbSBJTyByZXNlYXJjaC4gQmFzZWQgb24gdGhlIHF1ZXN0aW9ubmFpcmUgYXMgc2hvd24gaGVyZS4gTm90ZSB0aGF0IEkgY2hlY2sgd2hldGhlciB0aGVyZSBhcmUgYW55IG1pc3NpbmdzIG9uIHRoZSBkZW1vZ3JhcGhpYyB2YXJpYWJsZXMgZmlyc3QgKGNvbHVtbiA4OSB0byAxMDApLgoKYGBge3IgZGF0YWxhb2QsIGV2YWw9RkFMU0V9CiNyZXBvIDwtICIveW91cnNjcmF0Y2gvIiAjIHlvdXJwYXRoCmRmIDwtIHJlYWRfZHRhKCJkYXRhL0RRVUVTVFVVX2VpbmRiZXN0YW5kLmR0YSIpCiNkZiA8LSBkYXRhLmZyYW1lKGRmKQoKbnJvdyhhcy5tYXRyaXgobmEub21pdChkZlssYyg4OToxMDApXSkpKSAjIG5vIE5BcyBvbiB0aGUgZGVtb2dyYXBoaWMgdmFyaWFibGVzLCBqdXN0IGEgY2hlY2sKYGBgCgo8YnI+CgotLS0tCgojIERhdGEgbWFuaXB1bGF0aW9uIDEKCkkgdGhlbiBtb3ZlIHRvIHNvbWUgZGF0YSBtYW5pcHVsYXRpb24uIEkgZXh0cmFjdCB0aGUgY29sdW1ucyBmb3IgdGhlIFggY2F0ZWdvcmllcyBvbiBVbml2ZXJzaXR5LCBBcHBsaWVkIFVuaXZlcnNpdHksIFRlcnRpYXJ5IFZvY2F0aW9uYWwsIFNvbi9EYXVnaHRlciwgUmFuZHN0YWQsIGFuZCBDb3JvbmEgaW5mZWN0aW9ucy4gSSBhbHNvIGRlY2xhcmUgdGhlIG51bWJlciBvZiB0aG9zZSBjYXRlZ29yaWVzIGluIHRoZSBlbnRpcmUgcG9wdWxhdGlvbiBpbiB0aGF0IHNwZWNpZmljIHBvaW50IGluIHRpbWUgdGhhdCB0aGUgc3VydmV5IHdhcyBjb25kdWN0ZWQuIEkgZXh0cmFjdCB0aG9zZSBjb2x1bW5zIGZyb20gdGhlIGRhdGEgYXMgd2VsbCBhbmQgZmlsbCB1cCB0aGUgIm5vdCBrbm93aW5nIHNvbWVvbmUgb2YgdGhhdCBjYXRlZ29yeSIgd2l0aCB6ZXJvcywgc3VjaCB0aGF0IGl0IHdvcmtzIGluIHRoZSBlc3RpbWF0aW9uIHByb2Nlc3MuCgoKYGBge3IsIGV2YWw9RkFMU0V9CiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIHNvbWUgZGF0YSBwcmVwCgojdjEgaW4gZGF0YQpyZWYxIDwtIGMoODQ5NTcsIDc1MjE0LCAxNDU2MDAsIDE2ODA2NiwgMjUwMCwgMjk4MDgsIDE1NTg1NDkpCiMgT1JERVI6ICB1bmksIGhibywgbWJvLCBkb2NodGVyL3pvb24sIHR3ZWVsaW5nLCByYW5kc3RhZCwgY29yb25hCmRmMV8xIDwtIGRmWywgYyg4OjE0KV0KZGYxXzIgPC0gZGZbLCBjKDE6NyldCgojIGNvbmRpdGlvbmFsIGZpbGxpbmcgaW4gb2YgemVyb3Mgd2hlbiByZXNwb25kZW50cyBhbnN3ZXIgIm5vIiBJIGRvbid0IGtub3cgdGhhdCBwZXJzb24KZm9yIChpIGluIDE6bGVuZ3RoKGRmMV8yKSl7ICMgbG9vcCBvZiBjb2xsZW5ndGgKICAgZGYxXzFbW2ldXSA8LSBpZmVsc2UoZGYxXzJbW2ldXSA9PSAyLCAwLCBkZjFfMVtbaV1dKQp9CmBgYAoKCjxicj4KCi0tLS0KCiMgRGF0YSBtYW5pcHVsYXRpb24gMgoKV2UgZG8gdGhlIHNhbWUgYXMgYWJvdmUsIGJ1dCBmb3IgdGhlIGNhdGVnb3JpZXMgZWxlY3RyaWNhbCB2ZWhpY2xlLCBzY29vdGVyLCBhbmQgdmVnYW5zLiBXZSBhZ2FpbiBkZWNsYXJlIHRoZSBwb3B1bGF0aW9uIG51bWJlcnMgYXMgd2VsbC4KCmBgYHtyLCBldmFsPUZBTFNFfQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KI3YyIGluIGRhdGEKcmVmMiA8LSBjKDI3MzI1OSwgNDYwNjE4LCAyNjEwMDApCiMgT1JERVI6ICAgZWxlY2F1dG8sIHNjb290ZXIsIHZlZ2FuLApkZjJfMSA8LSBkZlssIGMoMTg6MjApXQpkZjJfMiA8LSBkZlssIGMoMTU6MTcpXQoKIyBmaWxsaW5nIG9mIHplcm9zCmZvciAoaSBpbiAxOmxlbmd0aChkZjJfMikpeyAjIGxvb3Agb2YgY29sbGVuZ3RoCiAgZGYyXzFbW2ldXSA8LSBpZmVsc2UoZGYyXzJbW2ldXSA9PSAyLCAwLCBkZjJfMVtbaV1dKQp9CgoKCmBgYAoKPGJyPgoKLS0tLQoKIyBEYXRhIG1hbmlwdWxhdGlvbiAzCgpBbmQgYWdhaW4sIHdlIGRvIHRoZSBzYW1lIGFzIGFib3ZlLCBidXQgZm9yIHRoZSBuYW1lIGNhdGVnb3JpZXMgKGFuZCBzZWUgdGhlIE5hbWUgc2VhcmNoIGFuZCBOYW1lIHNlbGVjdGlvbiBwYWdlcyBzbyBhcyB0byBmaW5kIG91dCBob3cgd2Ugc2VsZWN0ZWQgdGhlbSkuCgoKYGBge3IsIGV2YWw9RkFMU0V9CiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojdjMgaW4gZGF0YQpyZWYzIDwtIGMoMTUyNzYsMTYzNTAsMjczOTQsMjEyMDAsMjU2ODEsMzM0NTAyLDI5OTU1LDI2NjUyMiwzOTQ4MSwgMjY5MiwKICAgICAgICAgIDEzNjI5NiwxMTAyMzEsMTEyODA3LDk4MjA4LDEzODYsMjE4NiwxMTY0MCwyMjcwNCwxMzI3NiwKICAgICAgICAgIDQwNTQzLDE3MDI0LDIzMTY3LDMwNzAzMiwzNjQxMSw0OTE4MiwxODY3NDYsMzU5NzMsMTM0OTU2LDExODYxMCw4NjUwMCwKICAgICAgICAgIDEwMjI5Niw0MjEzLDUwMDMsNDUxNykKIyBPUkRFUjogICBTb3BoaWUsIEp1bGlhLFNhbm5lLExpc2EsTGF1cmEsTWFyaWEsTGluZGEsSm9oYW5uYSxNb25pcXVlLEVzdGVyLAojICAgICAgICAgICAgIEFubmEsRWxpc2FiZXRoLENvcm5lbGlhLFdpbGhlbG1pbmEsQW1pcmEsU2FtaXJhLFNhcmEsRGFhbixTZW0sCiMgICAgICAgICAgICAgVGhvbWFzLE1heCxLZXZpbixKb2hhbm5lcyxEZW5uaXMsSmVyb2VuLEphbixNYXJjZWwsQ29ybmVsaXMsSGVuZHJpayxQZXRydXMsCiMgICAgICAgICAgICAgV2lsbGVtLEFsaSxNb2hhbW1lZCxOb29yCgpkZjNfMSA8LSBkZlssIGMoNTU6ODgpXQpkZjNfMiA8LSBkZlssIGMoMjE6NTQpXQoKIyBmaWxsaW5nIGluIG9mIHplcm9zCmZvciAoaSBpbiAxOmxlbmd0aChkZjNfMikpeyAjIGxvb3Agb2YgY29sbGVuZ3RoCiAgZGYzXzFbW2ldXSA8LSBpZmVsc2UoZGYzXzJbW2ldXSA9PSAyLCAwLCBkZjNfMVtbaV1dKQp9CmBgYAoKCjxicj4KCi0tLS0KCiMgRGF0YSBtYW5pcHVsYXRpb24gNAoKQmVmb3JlIHdlIG1vdmUgaW50byB0aGUgZXN0aW1hdGlvbiwgSSBuZWVkIHRvIGRvIHNvbWUgZmluYWwgZGF0YSBtYW5pcHVsYXRpb24uIEkgZmlyc3QgY29sdW1iaW5kIHRoZSBkaWZmZXJlbnQgWCdzIHRvZ2V0aGVyLCBib3RoIHRoZSBhY3R1YWwgYW5zd2VycyBmcm9tIHJlc3BvbmRlbnRzLCBhcyB3ZWxsIGFzIHRoZSBwb3B1bGF0aW9uIG51bWJlcnMuIEl0IGlzIGltcG9ydGFudCB0aGF0IHRoZXNlIGFyZSBpbiB0aGUgc2FtZSBvcmRlci4gSSBhbHNvIGNyZWF0ZSBhbiBvYmplY3Qgd2l0aCB0aGUgdG90YWwgRHV0Y2ggcG9wdWxhdGlvbi4gTm90ZSB0aGF0IEkgZHJvcCAiUmFuZHN0YWQiLCBhcyB0aGVyZSBpcyBhIGRpc2NyZXBhbmN5IGJldHdlZW4gdGhlIHN1cnZleSBxdWVzaXRvbiBhbmQgd2hhdCB3ZSBrbm93IGZyb20gdGhlIGVudGlyZSBwb3B1bGF0aW9uIChSYW5kc3RhZCB2ZXJzdXMgImZvdXIgYmlnZ2VzdCBjaXRpZXMiLCB0aGVzZSBhcmUgdHdvIGRpZmZlcmVudCB0aGluZ3MuKQoKYGBge3IsIGV2YWw9RkFMU0V9CiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIG5vdyBjYmluZCB0aG9zZSBtYXRyaWNlcyB0b2dldGhlcgpjb250IDwtIGNiaW5kKGRmMV8xWywgYygxLDIsMyw0LDUsNyldLCBkZjJfMSwgZGYzXzEpCmNvbnQgPC0gYXMubWF0cml4KG5hLm9taXQoY29udCkpICMgbmljZSwgb25seSA2MCBvciBzbyBtaXNzaW5ncwpwb3AgPC0gYyhyZWYxW2MoMSwyLDMsNCw1LDcpXSwgcmVmMiwgcmVmMykgI3dpdGhvdXQgcmFuZHN0YWQgKG9ubHkgNCBncm90ZSBzdGVkZW4sIG5vdCAicmFuZHN0YWQiKSwgVGhlc2UgYXJlIHRoZSBrbm93biBwb3B1bGF0aW9uIHNpemVzIAp0b3Rwb3AgPC0gMTc0MDc1ODUgIyBUb3RhbCBkdXRjaCBwb3B1bGF0aW9uIGluIDIwMjAKCgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyB0aGlzIGlzIGltcG9ydGFudDogdGhlc2UgYXJlIHRoZSBkYXRhIHdlIGNhbiBkaXJlY3RseSBtYXRjaCB0bywgc2FtZSByb3cgb3JkZXIuCmRmX25vbWlzc2luZ3MgPC0gY2JpbmQoZGYxXzJbLCBjKDEsMiwzLDQsNSw3KV0sIGRmMV8xWywgYygxLDIsMyw0LDUsNyldLCBkZjJfMiwgZGYyXzEsIGRmM18yLCBkZjNfMSwgZGZbLCBjKDg5OjEwMCldKSAjIGxvb3NlIHJhbmRzdGFkIHRvIGdldCBhdCBzYW1lIE4KZGZfbm9taXNzaW5ncyA8LSBuYS5vbWl0KGRmX25vbWlzc2luZ3MpCgpgYGAKCjxicj4KCi0tLS0KCiMgSW5pdGlhdGUgZXN0aW1hdGlvbgoKU28gd2Ugc2V0IGEgbnVtYmVyIG9mIGh5cGVycGFyZW1ldGVycyBmaXJzdCwgNDBLIGl0ZXJhdGlvbnMsIHdpdGggYSBidXJuaW4gb2YgMUssIHdoZXJlIHdlIHJldGFpbiA0SyBjaGFpbnMuIFdlIGRvIG5vdCBzYXZlIHRoZSBlbnRpcmUgTlNVTSBvdXRwdXQgb2JqZWN0LCBhcyB3ZSBoYXZlIDE3MiBvZiB0aG9zZSBhbmQgdGhleSBhcmUgMUdCIGVhY2guIFNvIHdlIGV4dHJhY3QgdGhlIG5lY2Vzc2FyeSBpbmZvcm1hdGlvbiBmcm9tIGVhY2guCgoKYGBge3IsIGV2YWw9RkFMU0V9CiNUaGlzIGlzIHdoZXJlIHRoZSBtYWdpYyBoYXBwZW5zCiMgU28gd2UgZG9uJ3Qgc2F2ZSBldmVyeSBOU1VNIG9iamVjdCwgaXQncyBhYm91dCAxZ2IgaW4gc2l6ZSBwZXIgaGVsZG91dCBjYXRlZ29yeQoKaXRlcnMgPC0gNDAwMDAKYnVybnMgPC0gMTAwMApyZXRhaW4gPC0gNDAwMAoKCgojIHBhcmFsZWxsaXplIHRoZSBlc3RpbWF0aW9uCmNsb3NlQWxsQ29ubmVjdGlvbnMoKQpudW1Db3JlcyA8LSBkZXRlY3RDb3JlcygpCmlmIChudW1Db3JlcyA+IDQyKSB7CiAgcmVnaXN0ZXJEb1BhcmFsbGVsKGNvcmVzID0gNDMpCiAgfSBlbHNlIHsgICAgCiAgICBwcmludCgiVG9vIGZldyBjb3JlcyEiKSAKfSAKaG9sZG91dHMgPC0gMTo0MwoKYGBgCgo8YnI+CgotLS0tCgojIEVzdGltYXRpb24gYXQgdGF1SyAwLjI1CgpTbyB0aGlzIGlzIHRoZSBwYXJlbGxlbGl6ZWQgZm9yIGxvb3Agd2hlcmUgd2UgZXN0aW1hdGUgdGhlIE5TVU0gb3V0cHV0IGF0IHRhdUsgMC4yNS4gV2UgdGFrZSBzdGFydGluZyBwYXJhbWV0ZXIgZnJvbSB0aGUgbmFpdmUgTlNVTSBlcXVhdGlvbiBhbmQgbW92ZSBmb3J3YXJkIHdpdGggdGhhdC4gTm90ZSB0aGF0IGVhY2ggb2YgdGhlIDQzIFggY2F0ZWdvcmllcyBpcyBoZWxkIG91dCBvbmNlLiBUaGlzIGltcGxpZXMgdGhhdCB3ZSBoYXZlIDQzIG5ldHdvcmsgc2l6ZXMgZm9yIGFsbCByZXNwb25kZW50cyB1c2luZyB0YXVLIDAuMjUuIFdlIGV4dHJhY3QgdGhlIGF2ZXJhZ2UgbmV0d29yayBzaXplIG9mIHRoZSA0SyBjaGFpbnMgZm9yIGVhY2ggcmVzcG9uZGVudCBpbiBlYWNoIGVzdGltYXRpb24gc2NlbmFyaW8uIFdlIHNhdmUgdGhhdCBpbiBhIHR4dCBmaWxlLiBXZSByZXBlYXQgdGhpcyBwcm9jZXNzIGZvciB0YXVLIDAuNSwgMC43NSwgYW5kIDAuOTkuIE9ubHkgdGhlIGZpbGUgbmFtZXMgYW5kICJ0YXVLLnN0YXJ0IiB2YWx1ZSBpcyBkaWZmZXJlbnQgaW4gdGhvc2Ugc2NlbmFyaW9zLCB0aGUgcmVzdCBvZiB0aGUgY29kZSByZW1haW5zIHNpbWlsYXIuIE5vdGUgdGhhdCB0aGVyZSBhcmUgdGh1cyAxNzIgb3V0cHV0IGZpbGVzICg0IHggNDNYJ3MgeCA0IHRhdUsgc2NlbmFyaW9zKS4KCmBgYHtyLCBldmFsPUZBTFNFfQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMgVGF1SyAwLjI1CiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIHNvbWUgZW1wdHkgbHNpdHMgdG8gZmlsbCB1cCBhbHRlciBvbgojIG5vdGUgaG93IHdlIGRvbid0IHNhdmUgYW55IHN0YXJ0aW5nIHZhbHVlcywgbm9yIGRlIG5zdW0gb2JqZWN0cwprZHMgPC0gbGlzdCgpCmtkc3NkIDwtIGxpc3QoKQpkYXRhIDwtIGxpc3QoKQoKIyBzZXQuc2VlZCgzMDA0KQojIGhvbGRvdXRzIDwtIHNvcnQoc2FtcGxlKDE6NDMsIDEwLCByZXBsYWNlPUZBTFNFKSkKCmZvcmVhY2ggKGkgPSBob2xkb3V0cykgJWRvcGFyJSB7CiAgCiAgY2QgPC0gY29udFssIC1jKGkpXSAjIFRoZW4gdGFrZSBvdXQgdGhlIGtub3duIHN1YnBvcHVsYXRpb24gb2YgaW50ZXJlc3QKICAKICAjIGNhY2x1bGF0ZSBzdGFydGluZyB2YWx1ZXMKICB6IDwtIE5TVU06OmtpbGx3b3J0aC5zdGFydChjYmluZChjZCwgY29udFssIGMoaSldKSwgIyBwYXN0ZSB0aGUgInRha2Vub3V0IiBhdCB0aGUgbWF4IG9mIG1hdHJpeAogICAgICAgICAgICAgICAgICAgICAgIHBvcFstYyhpKV0sIAogICAgICAgICAgICAgICAgICAgICAgIHRvdHBvcCkKICAKICAjIHJ1biB0aGUgQmF5ZXNpYW4gZXN0aW1hdGlvbgogIGRlZ3JlZSA8LSBOU1VNOjpuc3VtLm1jbWMoY2JpbmQoY2QsIGNvbnRbLCBjKGkpXSksICMgZ2V0cyBwYXN0ZWQgYXQgdGhlIGxhc3QgY29sdW1uCiAgICAgICAgICAgICAgICAgICAgICBwb3BbLWMoaSldLCAKICAgICAgICAgICAgICAgICAgICAgIHRvdHBvcCwgbW9kZWwgPSAiY29tYmluZWQiLCAjIGNvbWJpbmVkIGNvbnRyb2wgZm9yIHRyYW5zbWlzc2lvbiBhbmQgcmVjYWxsIGVycm9ycwogICAgICAgICAgICAgICAgICAgICAgaW5kaWNlcy5rID0gNDMsICMgbm90aWNlIHRoYXQgImhvbGRvdXQiIGdldHMgcGFzdGVkIGFzIGxhc3QgY29sdW1uCiAgICAgICAgICAgICAgICAgICAgICBpdGVyYXRpb25zID0gaXRlcnMsIGJ1cm5pbiA9IGJ1cm5zLCBzaXplID0gcmV0YWluLCAjIDQwayBpdGVyYXRpb25zLCByZXRhaW4gNGsgY2hhaW5zCiAgICAgICAgICAgICAgICAgICAgICBkLnN0YXJ0ID0geiRkLnN0YXJ0LCBtdS5zdGFydCA9IHokbXUuc3RhcnQsICMgc3RhcnRpbmcgdmFsdWVzIGZyb20gc2ltcGUgZXN0aW1hdG9yCiAgICAgICAgICAgICAgICAgICAgICBzaWdtYS5zdGFydCA9IHokc2lnbWEuc3RhcnQsIE5LLnN0YXJ0ID0geiROSy5zdGFydCwgdGF1Sy5zdGFydCA9IDAuMjUpCiAgCiAgIyBjYWxjdWxhdGUgcm93bWVhbiBhbmQgaXQncyBTRCBvZiB0aGUgcmV0YWluZWQgNGsgY2hhaW5zCiAga2RzW1tpXV0gPC0gcm93TWVhbnMoZGVncmVlJGQudmFsdWVzLG5hLnJtID0gVFJVRSkgIyBjYWxjdWxhdGUgcm93bWVhbiBvZiBuZXRzaXplIGl0ZXJhdGlvbnMKICBrZHNzZFtbaV1dIDwtIG1hdHJpeFN0YXRzOjpyb3dTZHMoZGVncmVlJGQudmFsdWVzKSAjIGNhbGN1bGF0ZSBzZCBvZiA0ayBlc3RpbWF0ZXMgcGVyIHJvdwogIGRhdGFbW2ldXSA8LSBjYmluZChrZHNbW2ldXSxrZHNzZFtbaV1dKQogIGRhdGEgPC0gYXMuZGF0YS5mcmFtZShkYXRhW1tpXV0pCiAgCiAgIyBTYXZlIHRoZSBkYXRhLCBuZXcgLnR4dCBmb3IgZWFjaCBpdGVyYXRpb24sIGlmIHNvbWV0aGluZyBnb2VzIHdyb25nLCB3ZSBjYW4gYWx3YXlzIHN0YXJ0IGF0IHByaW9yIG9uZSBhbmQgY29tYmluZQogIHdyaXRlLmNzdihkYXRhLCBwYXN0ZTAoImRhdGEvZGVncmVlX2VzdGltYXRlc190YXVrMDI1X2hvbGRvdXQiLCBpLCAiLnR4dCIpKQogIAp9CmBgYA==