Aim: to explore effect heterogeneity in social influence between
actors within clubs, according to running level and gender.
Our model already considers rather many parameters given the
information available in the data-set. We have limited power to detect
behavior evolution effects, as the number of data points is at the order
\(n\). If we consider network
evolution, we have data points of the order \(n^2\). Therefore, our data does now allow
for backward elimination (i.e., estimating the ‘full model’, including
all interactions simultaneously, and stepwisely removing effects that
are not significant according to some criterion).
Instead, we estimate our main model and assess the interactions by
testing their parameters \((H_0:\theta=0)\) without
estimating them, using score-type tests, a procedure proposed by Schweinberger (2012). Score-type tests indicate
whether and which of the tested effects will improve a model, when
included. We will consider the results of the (one-sided) one-parameter
test. Its test statistic is standard normally distributed.
If we find interaction effects that would improve the model, we will
estimate these effects simultaneously with the other model effects.
Thus, we use a two-step exploratory procedure.
Getting started
clean up
rm (list = ls( ))
#gc()
general custom functions
fpackage.check
: Check if packages are installed (and
install if not) in R (source)
fload.R
: function to load R-objects under new
names.
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)
}
})
}
fload.R <- function(fileName){
load(fileName)
get(ls()[ls() != "fileName"])
}
colorize <- function(x, color) {sprintf("<span style='color: %s;'>%s</span>", color, x) }
necessary packages
RSiena
parallel
: utilize more cores
packages = c("RSiena", "parallel")
fpackage.check(packages)
Down below, we focus on running frequency; but the scripts can be
modified to model running volume.
read in RSiena data objects
load("clubdata_rsiena_freq.Rdata")
#load("clubdata_rsiena_vol.Rdata")
Approach
We probe for moderational effects of:
- gender: to see whether receiving kudos or the activities of
alters have differential effects for males and females;
- running level: to see whether runners with different
running experience are more/less s usceptible to influence. We interact
the influence effects with the
linear
shape effect (which
represents ego’s current running) and a time-constant actor-covariate
representing the number of years that actors were active on Strava
(labeled novice
).
We discuss here why we
use the linear
shape effect, and why we use
avSim
rather than avAttLower
in the
interaction models.
We focus on 2 influence effects, and 3 interactions; thus a total of
6 models for each club.
Step 1: score-type test
#detectCores()
# make a list to store our final results in.
results.list <- vector("list", length(clubdata_rsiena_freq)) #"pre-allocate" empty list of length 5
for (i in 1:5) { # for club i
print(paste0("next up: club ", i))
#get rsiena object
mydata <- clubdata_rsiena_freq[[i]]
#and the list containing myeff objects
load(file=paste0("test/myeff/myeff_club",i,".RData"))
#load(file=paste0("test/myeff/duration/myeff_club",i,".RData"))
#take 6th element, which is the model with avSim
myeff <- myeff[[6]]
#include interaction effects, fixed to 0 and test=TRUE
#here, it is important that in the `getEffects` command, the `behNintn` argument is set to a high enough value...
#current behavior (linear shape)
beh <- "freq_run"
#beh <- "time_run"
myeff1 <- includeInteraction(myeff, linear, indeg, name=beh, interaction1=c("","kudonet"), fix=TRUE, test=TRUE)
myeff2 <- includeInteraction(myeff, linear, avSim, name=beh, interaction1=c("","kudonet"), fix=TRUE, test=TRUE)
#novice
myeff3 <- includeInteraction(myeff, effFrom, indeg, name = beh, interaction1 = c("novice", "kudonet"), fix=TRUE, test=TRUE)
myeff4 <- includeInteraction(myeff, effFrom, avSim, name = beh, interaction1 = c("novice", "kudonet"), fix=TRUE, test=TRUE)
#gender:
myeff5 <- includeInteraction(myeff, effFrom, indeg, name = beh, interaction1 = c("gender", "kudonet"), fix=TRUE, test=TRUE)
myeff6 <- includeInteraction(myeff, effFrom, avSim, name = beh, interaction1 = c("gender", "kudonet"), fix=TRUE, test=TRUE)
myeffL<-list(myeff1,myeff2, myeff3,myeff4,myeff5,myeff6)
# specify algorithm
myalgorithm <- sienaAlgorithmCreate(projname = "test", nsub=5, n3=5000 )
# myalgorithm <- sienaAlgorithmCreate(projname = "test", nsub=3, n3=50 ) #test
ansL <- vector("list", length(myeffL)) #"pre-allocate" empty list of length 6 to store models j in, for club i
#estimate
for (j in 1:length(ansL)) {
print(paste0("club ", i, ": estimating model ", j, "/6"))
ans <- siena07(myalgorithm, data=mydata, effects=myeffL[[j]], nbrNodes=10, initC=TRUE, useCluster = TRUE, batch=TRUE)
ansL[[j]] <- ans
}
results.list[[i]] <- ansL
}
#takes a long time, so save the results...
save(results.list, file="test/sienaFit/interactions_frequency.RData")
#save(results.list, file="test/sienaFit/interactions_volume.RData")
indeg x novice
appears to improve the model in club
5;
avSim x novice
appears to improve the model in clubs 2
and 4.
Step 2: estimate interaction effects
We found some indication that the effects of receiving kudos and the
running of friends worked differently for club members who are newer to
Strava (at least, in some clubs). These dynamics were no different for
the genders, nor for athletes that differed in their current running. In
step 2, we include these (missing) predictors of change in running
behavior in our model. Thus, we not only test
the effect, but we also estimate it simultaneously with the other
effects.
indeg x novice
load("clubdata_rsiena_freq.Rdata")
mydata <- clubdata_rsiena_freq[[5]]
#load effects object
load(file=paste0("test/myeff/myeff_club","5",".RData"))
myeff <- myeff[[6]]
# include interaction, and estimate the parameter
beh <- "freq_run"
myeff <- includeInteraction(myeff, effFrom, indeg, name = beh, interaction1 = c("novice", "kudonet"), fix=FALSE, test=FALSE)
# specify algorithm
myalgorithm <- sienaAlgorithmCreate(projname = "test", nsub=5, n3=5000 )
# estimate
ans <- siena07(myalgorithm, data=mydata, effects=myeff, nbrNodes=10, initC=TRUE, useCluster = TRUE, batch=TRUE)
# save output
save(ans, file="test/sienaFit/indeg_novice_club5.RData")
The interaction effect indeg x novice
is
negative and significant, suggesting that club members
who joined Strava relatively recently, are less susceptible to
the positive motivational effect of receiving kudos from clubmates on
Strava.
avSim x novice
rm(list=setdiff(ls(), "clubdata_rsiena_freq"))
# make a list to store our results in.
results.list <- vector("list", length(clubdata_rsiena_freq)) #"pre-allocate" empty list of length 5
#club we need to do the estimation for
clubs <- c("2","4")
for (i in unique(clubs)) { # for club i
#get rsiena object
mydata <- clubdata_rsiena_freq[[2]]
#and the list containing myeff objects
load(file=paste0("test/myeff/myeff_club",i,".RData"))
#take 6th element, which is the model with avSim
myeff <- myeff[[6]]
#include interaction, estimate parameter
beh <- "freq_run"
myeff <- includeInteraction(myeff, effFrom, avSim, name = beh, interaction1 = c("novice", "kudonet"), fix=FALSE, test=FALSE)
# specify algorithm
myalgorithm <- sienaAlgorithmCreate(projname = "test", nsub=5, n3=5000 )
#estimate saom
ans <- siena07(myalgorithm, data=mydata, effects=myeff, nbrNodes=10, initC=TRUE, useCluster = TRUE, batch=TRUE)
#store in results.list
results.list[[i]] <- ans
}
# save output
save(results.list, file="test/sienaFit/avsim_novice_club2_4.RData")
In both clubs, avSim x novice
is
positive and significant, suggesting that the tendency
to minimize the dissimilarity in running behaviors compared to friends
is greater for athletes that are relatively new to Strava…
All in all, we do not find consistent findings for these
moderations.
LS0tDQp0aXRsZTogIlBvc3QtaG9jIHByb2Jpbmcgb2YgbW9kZXJhdGlvbmFsIGVmZmVjdHMiDQpkYXRlOiAiTGFzdCBjb21waWxlZCBvbiBgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVCLCAlWScpYCINCmJpYmxpb2dyYXBoeTogcmVmZXJlbmNlcy5iaWINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjc3M6IHR3ZWFrcy5jc3MNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQ0KICAgIHRvY19kZXB0aDogMQ0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KLS0tDQogIA0KYGBge3IsIGdsb2JhbHNldHRpbmdzLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCg0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0Kb3B0c19jaHVuayRzZXQodGlkeS5vcHRzPWxpc3Qod2lkdGguY3V0b2ZmPTEwMCksdGlkeT1UUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSxjb21tZW50ID0gIiM+IiwgY2FjaGU9VFJVRSwgY2xhc3Muc291cmNlPWMoInRlc3QiKSwgY2xhc3Mub3V0cHV0PWMoInRlc3QyIikpDQpvcHRpb25zKHdpZHRoID0gMTAwKQ0KcmdsOjpzZXR1cEtuaXRyKCkNCg0KDQoNCmBgYA0KDQpgYGB7ciBrbGlwcHksIGVjaG89RkFMU0UsIGluY2x1ZGU9VFJVRX0NCmtsaXBweTo6a2xpcHB5KHBvc2l0aW9uID0gYygndG9wJywgJ3JpZ2h0JykpDQoja2xpcHB5OjprbGlwcHkoY29sb3IgPSAnZGFya3JlZCcpDQoja2xpcHB5OjprbGlwcHkodG9vbHRpcF9tZXNzYWdlID0gJ0NsaWNrIHRvIGNvcHknLCB0b29sdGlwX3N1Y2Nlc3MgPSAnRG9uZScpDQpgYGANCg0KDQoNCi0tLQ0KICANCkFpbTogdG8gZXhwbG9yZSBlZmZlY3QgaGV0ZXJvZ2VuZWl0eSBpbiBzb2NpYWwgaW5mbHVlbmNlIGJldHdlZW4gYWN0b3JzIHdpdGhpbiBjbHVicywgYWNjb3JkaW5nIHRvIHJ1bm5pbmcgbGV2ZWwgYW5kIGdlbmRlci4NCg0KDQpPdXIgbW9kZWwgYWxyZWFkeSBjb25zaWRlcnMgcmF0aGVyIG1hbnkgcGFyYW1ldGVycyBnaXZlbiB0aGUgaW5mb3JtYXRpb24gYXZhaWxhYmxlIGluIHRoZSBkYXRhLXNldC4gV2UgaGF2ZSBsaW1pdGVkIHBvd2VyIHRvIGRldGVjdCBiZWhhdmlvciBldm9sdXRpb24gZWZmZWN0cywgYXMgdGhlIG51bWJlciBvZiBkYXRhIHBvaW50cyBpcyBhdCB0aGUgb3JkZXIgJG4kLiBJZiB3ZSBjb25zaWRlciBuZXR3b3JrIGV2b2x1dGlvbiwgd2UgaGF2ZSBkYXRhIHBvaW50cyBvZiB0aGUgb3JkZXIgJG5eMiQuIFRoZXJlZm9yZSwgb3VyIGRhdGEgZG9lcyBub3cgYWxsb3cgZm9yIGJhY2t3YXJkIGVsaW1pbmF0aW9uIChpLmUuLCBlc3RpbWF0aW5nIHRoZSAnZnVsbCBtb2RlbCcsIGluY2x1ZGluZyBhbGwgaW50ZXJhY3Rpb25zIHNpbXVsdGFuZW91c2x5LCBhbmQgc3RlcHdpc2VseSByZW1vdmluZyBlZmZlY3RzIHRoYXQgYXJlIG5vdCBzaWduaWZpY2FudCBhY2NvcmRpbmcgdG8gc29tZSBjcml0ZXJpb24pLiANCg0KSW5zdGVhZCwgd2UgZXN0aW1hdGUgb3VyIG1haW4gbW9kZWwgYW5kIGFzc2VzcyB0aGUgaW50ZXJhY3Rpb25zIGJ5IHRlc3RpbmcgdGhlaXIgcGFyYW1ldGVycyAkKEhfMDpcdGhldGE9MCkkICp3aXRob3V0KiBlc3RpbWF0aW5nIHRoZW0sIHVzaW5nIHNjb3JlLXR5cGUgdGVzdHMsIGEgcHJvY2VkdXJlIHByb3Bvc2VkIGJ5IEBzY2h3ZWluYmVyZ2VyLiBTY29yZS10eXBlIHRlc3RzIGluZGljYXRlIHdoZXRoZXIgYW5kIHdoaWNoIG9mIHRoZSB0ZXN0ZWQgZWZmZWN0cyB3aWxsIGltcHJvdmUgYSBtb2RlbCwgd2hlbiBpbmNsdWRlZC4gV2Ugd2lsbCBjb25zaWRlciB0aGUgcmVzdWx0cyBvZiB0aGUgKG9uZS1zaWRlZCkgb25lLXBhcmFtZXRlciB0ZXN0LiBJdHMgdGVzdCBzdGF0aXN0aWMgaXMgc3RhbmRhcmQgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuIA0KDQpJZiB3ZSBmaW5kIGludGVyYWN0aW9uIGVmZmVjdHMgdGhhdCB3b3VsZCBpbXByb3ZlIHRoZSBtb2RlbCwgd2Ugd2lsbCBlc3RpbWF0ZSB0aGVzZSBlZmZlY3RzIHNpbXVsdGFuZW91c2x5IHdpdGggdGhlIG90aGVyIG1vZGVsIGVmZmVjdHMuIFRodXMsIHdlIHVzZSBhIHR3by1zdGVwIGV4cGxvcmF0b3J5IHByb2NlZHVyZS4NCg0KDQotLS0tDQoNCiMgR2V0dGluZyBzdGFydGVkDQoNCiMjIGNsZWFuIHVwDQoNCmBgYHtyLCBhdHRyLm91dHB1dD0nc3R5bGU9Im1heC1oZWlnaHQ6IDIwMHB4OyInfQ0Kcm0gKGxpc3QgPSBscyggKSkNCiNnYygpDQpgYGANCg0KPGJyPg0KDQojIyBnZW5lcmFsIGN1c3RvbSBmdW5jdGlvbnMNCg0KLSBgZnBhY2thZ2UuY2hlY2tgOiBDaGVjayBpZiBwYWNrYWdlcyBhcmUgaW5zdGFsbGVkIChhbmQgaW5zdGFsbCBpZiBub3QpIGluIFIgKFtzb3VyY2VdKGh0dHBzOi8vdmJhbGlnYS5naXRodWIuaW8vdmVyaWZ5LXRoYXQtci1wYWNrYWdlcy1hcmUtaW5zdGFsbGVkLWFuZC1sb2FkZWQvKSkNCi0gYGZsb2FkLlJgOiBmdW5jdGlvbiB0byBsb2FkIFItb2JqZWN0cyB1bmRlciBuZXcgbmFtZXMuDQoNCmBgYHtyLCBldmFsPUZ9DQoNCmZwYWNrYWdlLmNoZWNrIDwtIGZ1bmN0aW9uKHBhY2thZ2VzKSB7DQogICAgbGFwcGx5KHBhY2thZ2VzLCBGVU4gPSBmdW5jdGlvbih4KSB7DQogICAgICAgIGlmICghcmVxdWlyZSh4LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKSB7DQogICAgICAgICAgICBpbnN0YWxsLnBhY2thZ2VzKHgsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogICAgICAgICAgICBsaWJyYXJ5KHgsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkNCiAgICAgICAgfQ0KICAgIH0pDQp9DQoNCmZsb2FkLlIgIDwtIGZ1bmN0aW9uKGZpbGVOYW1lKXsNCiAgbG9hZChmaWxlTmFtZSkNCiAgZ2V0KGxzKClbbHMoKSAhPSAiZmlsZU5hbWUiXSkNCn0NCg0KY29sb3JpemUgPC0gZnVuY3Rpb24oeCwgY29sb3IpIHtzcHJpbnRmKCI8c3BhbiBzdHlsZT0nY29sb3I6ICVzOyc+JXM8L3NwYW4+IiwgY29sb3IsIHgpIH0NCg0KYGBgDQoNCg0KIyMgbmVjZXNzYXJ5IHBhY2thZ2VzDQoNCi0gYFJTaWVuYWANCi0gYHBhcmFsbGVsYDogdXRpbGl6ZSBtb3JlIGNvcmVzDQoNCmBgYHtyIHBhY2thZ2VzLCBldmFsPUZ9DQoNCnBhY2thZ2VzID0gYygiUlNpZW5hIiwgInBhcmFsbGVsIikNCg0KZnBhY2thZ2UuY2hlY2socGFja2FnZXMpDQpgYGANCg0KPGJyPg0KDQpEb3duIGJlbG93LCB3ZSBmb2N1cyBvbiBydW5uaW5nIGZyZXF1ZW5jeTsgYnV0IHRoZSBzY3JpcHRzIGNhbiBiZSBtb2RpZmllZCB0byBtb2RlbCBydW5uaW5nIHZvbHVtZS4NCg0KDQojIyByZWFkIGluIFJTaWVuYSBkYXRhIG9iamVjdHMNCmBgYHtyLCBldmFsPUZ9DQpsb2FkKCJjbHViZGF0YV9yc2llbmFfZnJlcS5SZGF0YSIpDQojbG9hZCgiY2x1YmRhdGFfcnNpZW5hX3ZvbC5SZGF0YSIpDQpgYGANCg0KPGJyPg0KDQotLS0tDQoNCiMgQXBwcm9hY2gNCg0KV2UgcHJvYmUgZm9yIG1vZGVyYXRpb25hbCBlZmZlY3RzIG9mOg0KDQotICpnZW5kZXIqOiB0byBzZWUgd2hldGhlciByZWNlaXZpbmcga3Vkb3Mgb3IgdGhlIGFjdGl2aXRpZXMgb2YgYWx0ZXJzIGhhdmUgZGlmZmVyZW50aWFsIGVmZmVjdHMgZm9yIG1hbGVzIGFuZCBmZW1hbGVzOw0KLSAqcnVubmluZyBsZXZlbCo6IHRvIHNlZSB3aGV0aGVyIHJ1bm5lcnMgd2l0aCBkaWZmZXJlbnQgcnVubmluZyBleHBlcmllbmNlIGFyZSBtb3JlL2xlc3MgcyAgdXNjZXB0aWJsZSB0byBpbmZsdWVuY2UuIFdlIGludGVyYWN0IHRoZSBpbmZsdWVuY2UgZWZmZWN0cyB3aXRoIHRoZSBgbGluZWFyYCBzaGFwZSBlZmZlY3QgKHdoaWNoIHJlcHJlc2VudHMgZWdvJ3MgY3VycmVudCBydW5uaW5nKSBhbmQgYSB0aW1lLWNvbnN0YW50IGFjdG9yLWNvdmFyaWF0ZSByZXByZXNlbnRpbmcgdGhlIG51bWJlciBvZiB5ZWFycyB0aGF0IGFjdG9ycyB3ZXJlIGFjdGl2ZSBvbiBTdHJhdmEgKGxhYmVsZWQgYG5vdmljZWApLiANCg0KV2UgZGlzY3VzcyBbaGVyZV0oaHR0cHM6Ly9yb2JmcmFua2VuLmdpdGh1Yi5pby9TdHJhdmEvaW50ZXJhY3Rpb24pIHdoeSB3ZSB1c2UgdGhlIGBsaW5lYXJgIHNoYXBlIGVmZmVjdCwgYW5kIHdoeSB3ZSB1c2UgYGF2U2ltYCByYXRoZXIgdGhhbiBgYXZBdHRMb3dlcmAgaW4gdGhlIGludGVyYWN0aW9uIG1vZGVscy4gDQogDQpXZSBmb2N1cyBvbiAyIGluZmx1ZW5jZSBlZmZlY3RzLCBhbmQgMyBpbnRlcmFjdGlvbnM7IHRodXMgYSB0b3RhbCBvZiA2IG1vZGVscyBmb3IgZWFjaCBjbHViLg0KDQoNCg0KIyBTdGVwIDE6IHNjb3JlLXR5cGUgdGVzdA0KDQpgYGB7ciwgZXZhbD1GfQ0KI2RldGVjdENvcmVzKCkNCg0KDQojIG1ha2UgYSBsaXN0IHRvIHN0b3JlIG91ciBmaW5hbCByZXN1bHRzIGluLg0KcmVzdWx0cy5saXN0IDwtIHZlY3RvcigibGlzdCIsIGxlbmd0aChjbHViZGF0YV9yc2llbmFfZnJlcSkpICMicHJlLWFsbG9jYXRlIiBlbXB0eSBsaXN0IG9mIGxlbmd0aCA1DQoNCmZvciAoaSBpbiAxOjUpIHsgIyBmb3IgY2x1YiBpDQogIA0KICBwcmludChwYXN0ZTAoIm5leHQgdXA6IGNsdWIgIiwgaSkpDQogIA0KICAjZ2V0IHJzaWVuYSBvYmplY3QNCiAgbXlkYXRhIDwtIGNsdWJkYXRhX3JzaWVuYV9mcmVxW1tpXV0NCiAgDQogICNhbmQgdGhlIGxpc3QgY29udGFpbmluZyBteWVmZiBvYmplY3RzDQogIGxvYWQoZmlsZT1wYXN0ZTAoInRlc3QvbXllZmYvbXllZmZfY2x1YiIsaSwiLlJEYXRhIikpDQogICNsb2FkKGZpbGU9cGFzdGUwKCJ0ZXN0L215ZWZmL2R1cmF0aW9uL215ZWZmX2NsdWIiLGksIi5SRGF0YSIpKQ0KDQogICN0YWtlIDZ0aCBlbGVtZW50LCB3aGljaCBpcyB0aGUgbW9kZWwgd2l0aCBhdlNpbQ0KICBteWVmZiA8LSBteWVmZltbNl1dDQogIA0KICAjaW5jbHVkZSBpbnRlcmFjdGlvbiBlZmZlY3RzLCBmaXhlZCB0byAwIGFuZCB0ZXN0PVRSVUUNCiAgI2hlcmUsIGl0IGlzIGltcG9ydGFudCB0aGF0IGluIHRoZSBgZ2V0RWZmZWN0c2AgY29tbWFuZCwgdGhlIGBiZWhOaW50bmAgYXJndW1lbnQgaXMgc2V0IHRvIGEgaGlnaCBlbm91Z2ggdmFsdWUuLi4NCiAgDQogICNjdXJyZW50IGJlaGF2aW9yIChsaW5lYXIgc2hhcGUpDQogIGJlaCA8LSAiZnJlcV9ydW4iDQogICNiZWggPC0gInRpbWVfcnVuIg0KICBteWVmZjEgPC0gaW5jbHVkZUludGVyYWN0aW9uKG15ZWZmLCBsaW5lYXIsIGluZGVnLCBuYW1lPWJlaCwgaW50ZXJhY3Rpb24xPWMoIiIsImt1ZG9uZXQiKSwgZml4PVRSVUUsIHRlc3Q9VFJVRSkNCiAgbXllZmYyIDwtIGluY2x1ZGVJbnRlcmFjdGlvbihteWVmZiwgbGluZWFyLCBhdlNpbSwgbmFtZT1iZWgsIGludGVyYWN0aW9uMT1jKCIiLCJrdWRvbmV0IiksIGZpeD1UUlVFLCB0ZXN0PVRSVUUpDQoNCiAgI25vdmljZQ0KICBteWVmZjMgPC0gaW5jbHVkZUludGVyYWN0aW9uKG15ZWZmLCBlZmZGcm9tLCBpbmRlZywgbmFtZSA9IGJlaCwgaW50ZXJhY3Rpb24xID0gYygibm92aWNlIiwgImt1ZG9uZXQiKSwgZml4PVRSVUUsIHRlc3Q9VFJVRSkNCiAgbXllZmY0IDwtIGluY2x1ZGVJbnRlcmFjdGlvbihteWVmZiwgZWZmRnJvbSwgYXZTaW0sIG5hbWUgPSBiZWgsIGludGVyYWN0aW9uMSA9IGMoIm5vdmljZSIsICJrdWRvbmV0IiksIGZpeD1UUlVFLCB0ZXN0PVRSVUUpDQogIA0KICAjZ2VuZGVyOg0KICBteWVmZjUgPC0gaW5jbHVkZUludGVyYWN0aW9uKG15ZWZmLCBlZmZGcm9tLCBpbmRlZywgbmFtZSA9IGJlaCwgaW50ZXJhY3Rpb24xID0gYygiZ2VuZGVyIiwgImt1ZG9uZXQiKSwgZml4PVRSVUUsIHRlc3Q9VFJVRSkNCiAgbXllZmY2IDwtIGluY2x1ZGVJbnRlcmFjdGlvbihteWVmZiwgZWZmRnJvbSwgYXZTaW0sIG5hbWUgPSBiZWgsIGludGVyYWN0aW9uMSA9IGMoImdlbmRlciIsICJrdWRvbmV0IiksIGZpeD1UUlVFLCB0ZXN0PVRSVUUpDQogIA0KICBteWVmZkw8LWxpc3QobXllZmYxLG15ZWZmMiwgbXllZmYzLG15ZWZmNCxteWVmZjUsbXllZmY2KQ0KICANCiAgIyBzcGVjaWZ5IGFsZ29yaXRobQ0KICBteWFsZ29yaXRobSA8LSBzaWVuYUFsZ29yaXRobUNyZWF0ZShwcm9qbmFtZSA9ICJ0ZXN0IiwgbnN1Yj01LCBuMz01MDAwICkNCiAjIG15YWxnb3JpdGhtIDwtIHNpZW5hQWxnb3JpdGhtQ3JlYXRlKHByb2puYW1lID0gInRlc3QiLCBuc3ViPTMsIG4zPTUwICkgI3Rlc3QNCiAgDQogIGFuc0wgPC0gdmVjdG9yKCJsaXN0IiwgbGVuZ3RoKG15ZWZmTCkpICMicHJlLWFsbG9jYXRlIiBlbXB0eSBsaXN0IG9mIGxlbmd0aCA2IHRvIHN0b3JlIG1vZGVscyBqIGluLCBmb3IgY2x1YiBpDQoNCiAgI2VzdGltYXRlDQogIGZvciAoaiBpbiAxOmxlbmd0aChhbnNMKSkgew0KICAgIA0KICAgIHByaW50KHBhc3RlMCgiY2x1YiAiLCBpLCAiOiBlc3RpbWF0aW5nIG1vZGVsICIsIGosICIvNiIpKQ0KICAgIA0KICAgIGFucyA8LSBzaWVuYTA3KG15YWxnb3JpdGhtLCBkYXRhPW15ZGF0YSwgZWZmZWN0cz1teWVmZkxbW2pdXSwgbmJyTm9kZXM9MTAsIGluaXRDPVRSVUUsIHVzZUNsdXN0ZXIgPSBUUlVFLCBiYXRjaD1UUlVFKQ0KICAgIGFuc0xbW2pdXSA8LSBhbnMNCiAgfQ0KICANCiAgcmVzdWx0cy5saXN0W1tpXV0gPC0gYW5zTA0KfQ0KDQojdGFrZXMgYSBsb25nIHRpbWUsIHNvIHNhdmUgdGhlIHJlc3VsdHMuLi4NCnNhdmUocmVzdWx0cy5saXN0LCBmaWxlPSJ0ZXN0L3NpZW5hRml0L2ludGVyYWN0aW9uc19mcmVxdWVuY3kuUkRhdGEiKQ0KDQojc2F2ZShyZXN1bHRzLmxpc3QsIGZpbGU9InRlc3Qvc2llbmFGaXQvaW50ZXJhY3Rpb25zX3ZvbHVtZS5SRGF0YSIpDQoNCmBgYA0KDQotLS0NCg0KPGJyPg0KDQotIGBpbmRlZyB4IG5vdmljZWAgYXBwZWFycyB0byBpbXByb3ZlIHRoZSBtb2RlbCBpbiBjbHViIDU7DQotIGBhdlNpbSB4IG5vdmljZWAgYXBwZWFycyB0byBpbXByb3ZlIHRoZSBtb2RlbCBpbiBjbHVicyAyIGFuZCA0Lg0KDQo8YnI+DQoNCi0tLQ0KDQojIFN0ZXAgMjogZXN0aW1hdGUgaW50ZXJhY3Rpb24gZWZmZWN0cw0KDQpXZSBmb3VuZCBzb21lIGluZGljYXRpb24gdGhhdCB0aGUgZWZmZWN0cyBvZiByZWNlaXZpbmcga3Vkb3MgYW5kIHRoZSBydW5uaW5nIG9mIGZyaWVuZHMgd29ya2VkIGRpZmZlcmVudGx5IGZvciBjbHViIG1lbWJlcnMgd2hvIGFyZSBuZXdlciB0byBTdHJhdmEgKGF0IGxlYXN0LCBpbiBzb21lIGNsdWJzKS4gVGhlc2UgZHluYW1pY3Mgd2VyZSBubyBkaWZmZXJlbnQgZm9yIHRoZSBnZW5kZXJzLCBub3IgZm9yIGF0aGxldGVzIHRoYXQgZGlmZmVyZWQgaW4gdGhlaXIgY3VycmVudCBydW5uaW5nLiBJbiBzdGVwIDIsIHdlIGluY2x1ZGUgdGhlc2UgKG1pc3NpbmcpIHByZWRpY3RvcnMgb2YgY2hhbmdlIGluIHJ1bm5pbmcgYmVoYXZpb3IgaW4gb3VyIG1vZGVsLiBgciBjb2xvcml6ZSgiVGh1cywgd2Ugbm90IG9ubHkgdGVzdCB0aGUgZWZmZWN0LCBidXQgd2UgYWxzbyBlc3RpbWF0ZSBpdCBzaW11bHRhbmVvdXNseSB3aXRoIHRoZSBvdGhlciBlZmZlY3RzLiIsICJyZWQiKWANCg0KDQoNCjxicj4NCg0KIyMgYGluZGVnIHggbm92aWNlYA0KYGBge3IsIGV2YWw9Rn0NCmxvYWQoImNsdWJkYXRhX3JzaWVuYV9mcmVxLlJkYXRhIikNCg0KbXlkYXRhIDwtIGNsdWJkYXRhX3JzaWVuYV9mcmVxW1s1XV0NCg0KI2xvYWQgZWZmZWN0cyBvYmplY3QNCmxvYWQoZmlsZT1wYXN0ZTAoInRlc3QvbXllZmYvbXllZmZfY2x1YiIsIjUiLCIuUkRhdGEiKSkNCm15ZWZmIDwtIG15ZWZmW1s2XV0gDQoNCiMgaW5jbHVkZSBpbnRlcmFjdGlvbiwgYW5kIGVzdGltYXRlIHRoZSBwYXJhbWV0ZXINCmJlaCA8LSAiZnJlcV9ydW4iDQpteWVmZiA8LSBpbmNsdWRlSW50ZXJhY3Rpb24obXllZmYsIGVmZkZyb20sIGluZGVnLCBuYW1lID0gYmVoLCBpbnRlcmFjdGlvbjEgPSBjKCJub3ZpY2UiLCAia3Vkb25ldCIpLCBmaXg9RkFMU0UsIHRlc3Q9RkFMU0UpDQoNCiMgc3BlY2lmeSBhbGdvcml0aG0NCm15YWxnb3JpdGhtIDwtIHNpZW5hQWxnb3JpdGhtQ3JlYXRlKHByb2puYW1lID0gInRlc3QiLCBuc3ViPTUsIG4zPTUwMDAgKQ0KDQojIGVzdGltYXRlDQphbnMgPC0gc2llbmEwNyhteWFsZ29yaXRobSwgZGF0YT1teWRhdGEsIGVmZmVjdHM9bXllZmYsIG5ick5vZGVzPTEwLCBpbml0Qz1UUlVFLCB1c2VDbHVzdGVyID0gVFJVRSwgYmF0Y2g9VFJVRSkNCg0KIyBzYXZlIG91dHB1dA0Kc2F2ZShhbnMsIGZpbGU9InRlc3Qvc2llbmFGaXQvaW5kZWdfbm92aWNlX2NsdWI1LlJEYXRhIikNCmBgYA0KDQo8YnI+DQoNClRoZSBpbnRlcmFjdGlvbiBlZmZlY3QgYGluZGVnIHggbm92aWNlYCBpcyAqKm5lZ2F0aXZlKiogYW5kIHNpZ25pZmljYW50LCBzdWdnZXN0aW5nIHRoYXQgY2x1YiBtZW1iZXJzIHdobyBqb2luZWQgU3RyYXZhIHJlbGF0aXZlbHkgcmVjZW50bHksIGFyZSAqbGVzcyogc3VzY2VwdGlibGUgdG8gdGhlIHBvc2l0aXZlIG1vdGl2YXRpb25hbCBlZmZlY3Qgb2YgcmVjZWl2aW5nIGt1ZG9zIGZyb20gY2x1Ym1hdGVzIG9uIFN0cmF2YS4NCg0KPGJyPg0KDQojIyBgYXZTaW0geCBub3ZpY2VgDQpgYGB7ciwgZXZhbD1GfQ0Kcm0obGlzdD1zZXRkaWZmKGxzKCksICJjbHViZGF0YV9yc2llbmFfZnJlcSIpKQ0KDQojIG1ha2UgYSBsaXN0IHRvIHN0b3JlIG91ciByZXN1bHRzIGluLg0KcmVzdWx0cy5saXN0IDwtIHZlY3RvcigibGlzdCIsIGxlbmd0aChjbHViZGF0YV9yc2llbmFfZnJlcSkpICMicHJlLWFsbG9jYXRlIiBlbXB0eSBsaXN0IG9mIGxlbmd0aCA1DQoNCiNjbHViIHdlIG5lZWQgdG8gZG8gdGhlIGVzdGltYXRpb24gZm9yDQpjbHVicyA8LSBjKCIyIiwiNCIpDQoNCmZvciAoaSBpbiB1bmlxdWUoY2x1YnMpKSB7ICMgZm9yIGNsdWIgaQ0KICAgI2dldCByc2llbmEgb2JqZWN0DQogICBteWRhdGEgPC0gY2x1YmRhdGFfcnNpZW5hX2ZyZXFbWzJdXQ0KICAgDQogICNhbmQgdGhlIGxpc3QgY29udGFpbmluZyBteWVmZiBvYmplY3RzDQogIGxvYWQoZmlsZT1wYXN0ZTAoInRlc3QvbXllZmYvbXllZmZfY2x1YiIsaSwiLlJEYXRhIikpDQoNCiAgI3Rha2UgNnRoIGVsZW1lbnQsIHdoaWNoIGlzIHRoZSBtb2RlbCB3aXRoIGF2U2ltDQogIG15ZWZmIDwtIG15ZWZmW1s2XV0NCiAgDQogICNpbmNsdWRlIGludGVyYWN0aW9uLCBlc3RpbWF0ZSBwYXJhbWV0ZXINCiAgYmVoIDwtICJmcmVxX3J1biINCiAgbXllZmYgPC0gaW5jbHVkZUludGVyYWN0aW9uKG15ZWZmLCBlZmZGcm9tLCBhdlNpbSwgbmFtZSA9IGJlaCwgaW50ZXJhY3Rpb24xID0gYygibm92aWNlIiwgImt1ZG9uZXQiKSwgZml4PUZBTFNFLCB0ZXN0PUZBTFNFKQ0KICANCiAgIyBzcGVjaWZ5IGFsZ29yaXRobQ0KICBteWFsZ29yaXRobSA8LSBzaWVuYUFsZ29yaXRobUNyZWF0ZShwcm9qbmFtZSA9ICJ0ZXN0IiwgbnN1Yj01LCBuMz01MDAwICkNCiAgDQogICNlc3RpbWF0ZSBzYW9tDQogIGFucyA8LSBzaWVuYTA3KG15YWxnb3JpdGhtLCBkYXRhPW15ZGF0YSwgZWZmZWN0cz1teWVmZiwgbmJyTm9kZXM9MTAsIGluaXRDPVRSVUUsIHVzZUNsdXN0ZXIgPSBUUlVFLCBiYXRjaD1UUlVFKQ0KICANCiAgI3N0b3JlIGluIHJlc3VsdHMubGlzdA0KICByZXN1bHRzLmxpc3RbW2ldXSA8LSBhbnMNCn0NCg0KIyBzYXZlIG91dHB1dA0Kc2F2ZShyZXN1bHRzLmxpc3QsIGZpbGU9InRlc3Qvc2llbmFGaXQvYXZzaW1fbm92aWNlX2NsdWIyXzQuUkRhdGEiKQ0KYGBgDQoNCjxicj4NCg0KSW4gYm90aCBjbHVicywgYGF2U2ltIHggbm92aWNlYCBpcyAqKnBvc2l0aXZlKiogYW5kIHNpZ25pZmljYW50LCBzdWdnZXN0aW5nIHRoYXQgdGhlIHRlbmRlbmN5IHRvIG1pbmltaXplIHRoZSBkaXNzaW1pbGFyaXR5IGluIHJ1bm5pbmcgYmVoYXZpb3JzIGNvbXBhcmVkIHRvIGZyaWVuZHMgaXMgZ3JlYXRlciBmb3IgYXRobGV0ZXMgdGhhdCBhcmUgcmVsYXRpdmVseSBuZXcgdG8gU3RyYXZhLi4uDQoNCg0KQWxsIGluIGFsbCwgd2UgZG8gbm90IGZpbmQgY29uc2lzdGVudCBmaW5kaW5ncyBmb3IgdGhlc2UgbW9kZXJhdGlvbnMuDQoNCjxicj4gDQoNCi0tLS0NCg0KIyMgUmVmZXJlbmNlcw0KDQo=
Copyright © 2021 Rob Franken