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.



1 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)



2 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



3 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]])
}



4 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]])
}



5 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]])
}



6 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)



7 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



8 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==