Let’s keep this TidyTuesday rolling by loading packages and reading the data:
library(tidyverse)
library(lubridate)
library(lme4)
library(arm)
library(ggeffects)
wheels <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2022/2022-08-09/wheels.csv')
head(wheels)
## # A tibble: 6 x 22
## X1 name height diameter opened closed country location
## <dbl> <chr> <dbl> <dbl> <date> <date> <chr> <chr>
## 1 1 360 ~ 200 NA 2012-07-03 2013-01-01 USA Pensaco~
## 2 2 Amur~ 303 200. 2004-01-01 NA Japan Kagoshi~
## 3 3 Asia~ 200 200 2012-12-15 NA Tailand Asiatiq~
## 4 4 Auro~ 295 272 NA NA Japan Nagashi~
## 5 5 Bagh~ 180 NA 2011-01-01 NA Iraq Al-Zawr~
## 6 6 Beij~ 693. 643. NA NA China Chaoyan~
## # ... with 14 more variables: number_of_cabins <dbl>,
## # passengers_per_cabin <dbl>, seating_capacity <dbl>, hourly_capacity <dbl>,
## # ride_duration_minutes <dbl>, climate_controlled <chr>,
## # construction_cost <chr>, status <chr>, design_manufacturer <chr>,
## # type <chr>, vip_area <chr>, ticket_cost_to_ride <chr>,
## # official_website <chr>, turns <dbl>
Things are relatively clean this week! We are going to focus on replicating the second image provided on GitHub. To do so, let’s steal some secrets from Stack Overflow
circleFun <- function(center = c(0,0),diameter = 1, npoints = 100){
r = diameter / 2
tt <- seq(0,2*pi,length.out = npoints)
xx <- center[1] + r * cos(tt)
yy <- center[2] + r * sin(tt)
return(data.frame(x = xx, y = yy))
}
dat <- circleFun(c(1,-1),2.3,npoints = 100)
#geom_path will do open circles, geom_polygon will do filled circles
ggplot(dat,aes(x,y)) +
geom_path() +
coord_fixed()

We got a circle! Now let’s think about how to replicate the image for one wheel. We need one for which we have all of (1) name, (2) height, (3) diameter, and (4) number_of_cabins.
wheels %>%
dplyr::select(name, height, diameter, number_of_cabins) %>%
na.omit(.) -> full_wheels
We may as well calculate the centers right away:
full_wheels %>%
mutate(center = height - diameter/2) -> full_wheels
Let’s start with the first one.
arbitrary_wheel <- full_wheels[1,]
Let’s start by plotting the basic circle and cleaning things up:
with(arbitrary_wheel,
circleFun(center = c(0,center),
diameter = diameter,
npoints = number_of_cabins)) %>%
ggplot(aes(x,y)) +
geom_path() +
coord_fixed() +
theme_bw() +
xlab("") +
ylab("") +
ggtitle(arbitrary_wheel$name) +
scale_y_continuous(expand = c(0, 0),
limits = c(0, arbitrary_wheel$diameter/2 +
arbitrary_wheel$center + 10))-> basic
basic

Sweet sweet victory! Now let’s add the legs. A feature which is a bit annoying about this approach is that we have to re-introduce our underlying dataset.
basic +
geom_segment(data=arbitrary_wheel,aes(x = 0 - center/3,
y = 0,
xend = 0,
yend = center)) +
geom_segment(data=arbitrary_wheel,aes(x = 0 + center/3,
y = 0,
xend = 0,
yend = center)) -> basic2
basic2

This one is a bit strange due to its height, but the basic idea is there.
Now let’s add on some carts. We need however many carts are indicated, broken into three groups, and arrayed across the wheel. Luckily, we have those points already in our underlying circle dataframe!
basic2 +
geom_point(pch=24,
position = position_nudge(y=-5),
size = 2,
aes(fill = as.factor(rep(1:3,length = length(x))))) +
theme(legend.position = "none")

Cute. Now that we have the basic structure down, let’s go ahead and get things ready to make these in bulk. The strategy will be to generate a new dataset which includes the above plotting fields for all of the wheels in our set. Here is a quick approach, subsetted down to the few names in the
wheelzzz <- full_wheels$name
lapply(1:length(wheelzzz), function(i){
with(full_wheels[i,],
circleFun(center = c(0,center),
diameter = diameter,
npoints = number_of_cabins)
) -> out
out$name <- wheelzzz[i]
out$diameter <- rep(full_wheels[i,"diameter"],nrow(out))
out$center <- rep(full_wheels[i,"center"],nrow(out))
out
}) -> wheelzzz
wheelzzz <- do.call("rbind",wheelzzz)
Now let’s plot them all as a facet wrap, subsetting down to just those six wheels included in the graphic:
which_wheels <- c("Beijing Great Wheel",
"Golden Gate Flyer",
"High Roller",
"London Eye",
"Singapore Flyer",
"Wiener Riesenrad")
wheelzzz %>%
filter(name %in% which_wheels) -> less_wheelzzz
less_wheelzzz %>%
dplyr::select(name, diameter, center) %>%
group_by(name) %>%
summarise(name = unique(name),
diameter = as.numeric(unique(diameter)),
center = as.numeric(unique(center)),
.groups = "keep") -> legzzz
less_wheelzzz %>%
ggplot(aes(x,y)) +
facet_wrap(~name, ncol = 3) +
geom_path() +
coord_fixed() +
theme_bw() +
xlab("") +
ylab("") +
geom_point(pch=24,
position = position_nudge(y=-20),
size = 1.5,
aes(fill = as.factor(rep(1:3,length = length(x))))) +
theme(legend.position = "none") +
geom_segment(data=legzzz,aes(x = 0 - center/3,
y = 0,
xend = 0,
yend = center)) +
geom_segment(data=legzzz,aes(x = 0 + center/3,
y = 0,
xend = 0,
yend = center)) +
scale_y_continuous(expand = c(0, 0),
limits = c(-9, legzzz$diameter/2 +
legzzz$center + 50))

Hey, good enough for government work! Now that the replication is complete, is there anything more interesting we can look at with this data?
One opportunity to practice some Tidy skillz might be to look at the cost of building these wheely-boiis, fitting a model, and making a nice graphical illustration of the model. To start, we need to clean the cost vector, which is super easy with Tidy:
head(wheels$construction_cost)
## [1] "Unknown" "Unknown" "Unknown" "Unknown"
## [5] "$6 million USD" "$290 million USD"
Gross. Luckily there is a common structure for all of these observations, except for the Golden Gate Flyer, which reports a range of estimates for the construction cost. Regardless, everything is listed in millions of USD so we might just …
wheels$cost <- parse_number(wheels$construction_cost)
## Warning: 40 parsing failures.
## row col expected actual
## 1 -- a number Unknown
## 2 -- a number Unknown
## 3 -- a number Unknown
## 4 -- a number Unknown
## 8 -- a number Unknown
## ... ... ........ .......
## See problems(...) for more details.
The parsing errors are fine, they just mean “could not find a number” here. Now let’s run a model after a little more cleaning. Note that there are not many observations, and there is a good amount of missingness which guides covariate selection, and the resulting model will be almost by definition quite crummy.
wheels$country <- as.factor(wheels$country)
wheels %>%
dplyr::select(cost, height, country) %>%
na.omit(.) -> wheel_dat
m <- lmer(cost ~ poly(height,2) + (1 |country),
data = wheel_dat)
pr <- ggpredict(m, "height [all]")
plot(pr) +
xlab("Wheel Height") +
ylab("Wheel Cost")

The above is the predicted cost as a function of height. As we would expect, it’s increasing at an increasing rate. Now let’s think about plotting the BLUPs.
ranefs <- data.frame(rownames(ranef(m)$country),
ranef(m)$country,
se.ranef(m)$country)
colnames(ranefs) <- c("country", "intercept", "se")
ranefs$title <- "Random Intercept Estimates"
ranefs$country <- factor(as.character(ranefs$country),
levels = ranefs$country[order(ranefs$intercept)])
ggplot(ranefs, aes(x = country, y = intercept)) +
geom_point() +
geom_errorbar(aes(ymin = intercept - 2*se,
ymax = intercept + 2*se),width=0) +
coord_flip() +
theme_bw() +
facet_grid(~title) +
geom_hline(yintercept = 0,
lty = 2) +
ylab("Expenditure Deviation from Average") +
xlab("Country") +
theme(strip.text.x = element_text(size = 12))

So it looks like, taking height into account, the USA spends about 48 million USD more than average on their wheels whereas China spends about 76 million USD less. As the saying goes Chabuduo! Close enough …
p.s. Farewell to the ICPSR 2022 Summer Program!
LS0tDQp0aXRsZTogIlRpZHlUdWVzZGF5IDIwMjIsIFdlZWsgMzIiDQphdXRob3I6ICJDaHJpc3RvcGhlciBTY2h3YXJ6Ig0KZGF0ZTogIjgvMDkvMjAyMiINCnBhZ2VzOg0KICBleHRyYTogdHJ1ZQ0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCg0KTGV0J3Mga2VlcCB0aGlzIFRpZHlUdWVzZGF5IHJvbGxpbmcgYnkgbG9hZGluZyBwYWNrYWdlcyBhbmQgcmVhZGluZyB0aGUgZGF0YToNCg0KYGBge3IsIHdhcm5pbmc9RixtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkobG1lNCkNCmxpYnJhcnkoYXJtKQ0KbGlicmFyeShnZ2VmZmVjdHMpDQoNCndoZWVscyA8LSByZWFkcjo6cmVhZF9jc3YoJ2h0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9yZm9yZGF0YXNjaWVuY2UvdGlkeXR1ZXNkYXkvbWFzdGVyL2RhdGEvMjAyMi8yMDIyLTA4LTA5L3doZWVscy5jc3YnKQ0KDQpoZWFkKHdoZWVscykNCmBgYA0KDQpUaGluZ3MgYXJlIHJlbGF0aXZlbHkgY2xlYW4gdGhpcyB3ZWVrISAgV2UgYXJlIGdvaW5nIHRvIGZvY3VzIG9uIHJlcGxpY2F0aW5nIHRoZSBzZWNvbmQgaW1hZ2UgcHJvdmlkZWQgb24gR2l0SHViLiAgVG8gZG8gc28sIGxldCdzIHN0ZWFsIHNvbWUgc2VjcmV0cyBmcm9tIFtTdGFjayBPdmVyZmxvd10oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNjg2Mjc0Mi9kcmF3LWEtY2lyY2xlLXdpdGgtZ2dwbG90MikNCg0KDQpgYGB7cn0NCg0KY2lyY2xlRnVuIDwtIGZ1bmN0aW9uKGNlbnRlciA9IGMoMCwwKSxkaWFtZXRlciA9IDEsIG5wb2ludHMgPSAxMDApew0KICAgIHIgPSBkaWFtZXRlciAvIDINCiAgICB0dCA8LSBzZXEoMCwyKnBpLGxlbmd0aC5vdXQgPSBucG9pbnRzKQ0KICAgIHh4IDwtIGNlbnRlclsxXSArIHIgKiBjb3ModHQpDQogICAgeXkgPC0gY2VudGVyWzJdICsgciAqIHNpbih0dCkNCiAgICByZXR1cm4oZGF0YS5mcmFtZSh4ID0geHgsIHkgPSB5eSkpDQp9DQoNCmRhdCA8LSBjaXJjbGVGdW4oYygxLC0xKSwyLjMsbnBvaW50cyA9IDEwMCkNCiNnZW9tX3BhdGggd2lsbCBkbyBvcGVuIGNpcmNsZXMsIGdlb21fcG9seWdvbiB3aWxsIGRvIGZpbGxlZCBjaXJjbGVzDQpnZ3Bsb3QoZGF0LGFlcyh4LHkpKSArIA0KICBnZW9tX3BhdGgoKSArIA0KICBjb29yZF9maXhlZCgpDQpgYGANCg0KV2UgZ290IGEgY2lyY2xlISAgTm93IGxldCdzIHRoaW5rIGFib3V0IGhvdyB0byByZXBsaWNhdGUgdGhlIGltYWdlIGZvciBvbmUgd2hlZWwuICBXZSBuZWVkIG9uZSBmb3Igd2hpY2ggd2UgaGF2ZSBhbGwgb2YgKDEpIG5hbWUsICgyKSBoZWlnaHQsICgzKSBkaWFtZXRlciwgYW5kICg0KSBudW1iZXJfb2ZfY2FiaW5zLg0KDQpgYGB7cn0NCg0Kd2hlZWxzICU+JSANCiAgZHBseXI6OnNlbGVjdChuYW1lLCBoZWlnaHQsIGRpYW1ldGVyLCBudW1iZXJfb2ZfY2FiaW5zKSAlPiUgDQogIG5hLm9taXQoLikgLT4gZnVsbF93aGVlbHMgDQoNCmBgYA0KDQoNCldlIG1heSBhcyB3ZWxsIGNhbGN1bGF0ZSB0aGUgY2VudGVycyByaWdodCBhd2F5Og0KDQpgYGB7cn0NCmZ1bGxfd2hlZWxzICU+JSANCiAgbXV0YXRlKGNlbnRlciA9IGhlaWdodCAtIGRpYW1ldGVyLzIpIC0+IGZ1bGxfd2hlZWxzDQpgYGANCg0KTGV0J3Mgc3RhcnQgd2l0aCB0aGUgZmlyc3Qgb25lLg0KDQpgYGB7cn0NCmFyYml0cmFyeV93aGVlbCA8LSBmdWxsX3doZWVsc1sxLF0NCmBgYA0KDQpMZXQncyBzdGFydCBieSBwbG90dGluZyB0aGUgYmFzaWMgY2lyY2xlIGFuZCBjbGVhbmluZyB0aGluZ3MgdXA6DQoNCmBgYHtyfQ0KICB3aXRoKGFyYml0cmFyeV93aGVlbCwNCiAgICAgICBjaXJjbGVGdW4oY2VudGVyID0gYygwLGNlbnRlciksIA0KICAgICAgICAgICAgICAgICBkaWFtZXRlciA9IGRpYW1ldGVyLA0KICAgICAgICAgICAgICAgICBucG9pbnRzID0gbnVtYmVyX29mX2NhYmlucykpICU+JSANCiAgZ2dwbG90KGFlcyh4LHkpKSArDQogICAgZ2VvbV9wYXRoKCkgKyANCiAgICBjb29yZF9maXhlZCgpICsNCiAgICB0aGVtZV9idygpICsNCiAgICB4bGFiKCIiKSArDQogICAgeWxhYigiIikgKw0KICAgIGdndGl0bGUoYXJiaXRyYXJ5X3doZWVsJG5hbWUpICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSwgDQogICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMCwgYXJiaXRyYXJ5X3doZWVsJGRpYW1ldGVyLzIgKyANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcmJpdHJhcnlfd2hlZWwkY2VudGVyICsgMTApKS0+IGJhc2ljDQoNCmJhc2ljDQpgYGANCg0KU3dlZXQgc3dlZXQgdmljdG9yeSEgIE5vdyBsZXQncyBhZGQgdGhlIGxlZ3MuICBBIGZlYXR1cmUgd2hpY2ggaXMgYSBiaXQgYW5ub3lpbmcgYWJvdXQgdGhpcyBhcHByb2FjaCBpcyB0aGF0IHdlIGhhdmUgdG8gcmUtaW50cm9kdWNlIG91ciB1bmRlcmx5aW5nIGRhdGFzZXQuDQoNCmBgYHtyfQ0KYmFzaWMgKw0KICBnZW9tX3NlZ21lbnQoZGF0YT1hcmJpdHJhcnlfd2hlZWwsYWVzKHggPSAwIC0gY2VudGVyLzMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeGVuZCA9IDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVuZCA9IGNlbnRlcikpICsNCiAgZ2VvbV9zZWdtZW50KGRhdGE9YXJiaXRyYXJ5X3doZWVsLGFlcyh4ID0gMCArIGNlbnRlci8zLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhlbmQgPSAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllbmQgPSBjZW50ZXIpKSAtPiBiYXNpYzINCg0KYmFzaWMyDQpgYGANCg0KVGhpcyBvbmUgaXMgYSBiaXQgc3RyYW5nZSBkdWUgdG8gaXRzIGhlaWdodCwgYnV0IHRoZSBiYXNpYyBpZGVhIGlzIHRoZXJlLg0KDQpOb3cgbGV0J3MgYWRkIG9uIHNvbWUgY2FydHMuICBXZSBuZWVkIGhvd2V2ZXIgbWFueSBjYXJ0cyBhcmUgaW5kaWNhdGVkLCBicm9rZW4gaW50byB0aHJlZSBncm91cHMsIGFuZCBhcnJheWVkIGFjcm9zcyB0aGUgd2hlZWwuICBMdWNraWx5LCB3ZSBoYXZlIHRob3NlIHBvaW50cyBhbHJlYWR5IGluIG91ciB1bmRlcmx5aW5nIGNpcmNsZSBkYXRhZnJhbWUhDQoNCmBgYHtyfQ0KYmFzaWMyICsNCiAgZ2VvbV9wb2ludChwY2g9MjQsIA0KICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fbnVkZ2UoeT0tNSksDQogICAgICAgICAgICAgc2l6ZSA9IDIsDQogICAgICAgICAgICAgYWVzKGZpbGwgPSBhcy5mYWN0b3IocmVwKDE6MyxsZW5ndGggPSBsZW5ndGgoeCkpKSkpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQpgYGANCg0KQ3V0ZS4gIE5vdyB0aGF0IHdlIGhhdmUgdGhlIGJhc2ljIHN0cnVjdHVyZSBkb3duLCBsZXQncyBnbyBhaGVhZCBhbmQgZ2V0IHRoaW5ncyByZWFkeSB0byBtYWtlIHRoZXNlIGluIGJ1bGsuICBUaGUgc3RyYXRlZ3kgd2lsbCBiZSB0byBnZW5lcmF0ZSBhIG5ldyBkYXRhc2V0IHdoaWNoIGluY2x1ZGVzIHRoZSBhYm92ZSBwbG90dGluZyBmaWVsZHMgZm9yIGFsbCBvZiB0aGUgd2hlZWxzIGluIG91ciBzZXQuICBIZXJlIGlzIGEgcXVpY2sgYXBwcm9hY2gsIHN1YnNldHRlZCBkb3duIHRvIHRoZSBmZXcgbmFtZXMgaW4gdGhlIA0KDQpgYGB7cn0NCndoZWVsenp6IDwtIGZ1bGxfd2hlZWxzJG5hbWUNCg0KbGFwcGx5KDE6bGVuZ3RoKHdoZWVsenp6KSwgZnVuY3Rpb24oaSl7DQogIHdpdGgoZnVsbF93aGVlbHNbaSxdLA0KICAgICAgIGNpcmNsZUZ1bihjZW50ZXIgPSBjKDAsY2VudGVyKSwgDQogICAgICAgICAgICAgICAgIGRpYW1ldGVyID0gZGlhbWV0ZXIsDQogICAgICAgICAgICAgICAgIG5wb2ludHMgPSBudW1iZXJfb2ZfY2FiaW5zKQ0KICAgICAgICkgLT4gb3V0DQogIG91dCRuYW1lIDwtIHdoZWVsenp6W2ldDQogIG91dCRkaWFtZXRlciA8LSByZXAoZnVsbF93aGVlbHNbaSwiZGlhbWV0ZXIiXSxucm93KG91dCkpDQogIG91dCRjZW50ZXIgPC0gcmVwKGZ1bGxfd2hlZWxzW2ksImNlbnRlciJdLG5yb3cob3V0KSkNCiAgb3V0DQp9KSAtPiB3aGVlbHp6eg0KDQp3aGVlbHp6eiA8LSBkby5jYWxsKCJyYmluZCIsd2hlZWx6enopDQpgYGANCg0KTm93IGxldCdzIHBsb3QgdGhlbSBhbGwgYXMgYSBmYWNldCB3cmFwLCBzdWJzZXR0aW5nIGRvd24gdG8ganVzdCB0aG9zZSBzaXggd2hlZWxzIGluY2x1ZGVkIGluIHRoZSBncmFwaGljOg0KDQpgYGB7cn0NCg0Kd2hpY2hfd2hlZWxzIDwtIGMoIkJlaWppbmcgR3JlYXQgV2hlZWwiLA0KICAgICAgICAgICAgICAgICAgIkdvbGRlbiBHYXRlIEZseWVyIiwNCiAgICAgICAgICAgICAgICAgICJIaWdoIFJvbGxlciIsDQogICAgICAgICAgICAgICAgICAiTG9uZG9uIEV5ZSIsDQogICAgICAgICAgICAgICAgICAiU2luZ2Fwb3JlIEZseWVyIiwNCiAgICAgICAgICAgICAgICAgICJXaWVuZXIgUmllc2VucmFkIikNCg0Kd2hlZWx6enogJT4lIA0KICBmaWx0ZXIobmFtZSAlaW4lIHdoaWNoX3doZWVscykgLT4gbGVzc193aGVlbHp6eg0KDQpsZXNzX3doZWVsenp6ICU+JSANCiAgZHBseXI6OnNlbGVjdChuYW1lLCBkaWFtZXRlciwgY2VudGVyKSAlPiUgDQogIGdyb3VwX2J5KG5hbWUpICU+JSANCiAgc3VtbWFyaXNlKG5hbWUgPSB1bmlxdWUobmFtZSksDQogICAgICAgICAgICBkaWFtZXRlciA9IGFzLm51bWVyaWModW5pcXVlKGRpYW1ldGVyKSksDQogICAgICAgICAgICBjZW50ZXIgPSBhcy5udW1lcmljKHVuaXF1ZShjZW50ZXIpKSwNCiAgICAgICAgICAgIC5ncm91cHMgPSAia2VlcCIpIC0+IGxlZ3p6eg0KDQpsZXNzX3doZWVsenp6ICU+JSANCiAgZ2dwbG90KGFlcyh4LHkpKSArDQogICAgZmFjZXRfd3JhcCh+bmFtZSwgbmNvbCA9IDMpICsNCiAgICBnZW9tX3BhdGgoKSArIA0KICAgIGNvb3JkX2ZpeGVkKCkgKw0KICAgIHRoZW1lX2J3KCkgKw0KICAgIHhsYWIoIiIpICsNCiAgICB5bGFiKCIiKSArDQogICAgZ2VvbV9wb2ludChwY2g9MjQsIA0KICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh5PS0yMCksDQogICAgICAgICAgICAgICBzaXplID0gMS41LA0KICAgICAgICAgICAgICAgYWVzKGZpbGwgPSBhcy5mYWN0b3IocmVwKDE6MyxsZW5ndGggPSBsZW5ndGgoeCkpKSkpICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgICBnZW9tX3NlZ21lbnQoZGF0YT1sZWd6enosYWVzKHggPSAwIC0gY2VudGVyLzMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhlbmQgPSAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVuZCA9IGNlbnRlcikpICsNCiAgICBnZW9tX3NlZ21lbnQoZGF0YT1sZWd6enosYWVzKHggPSAwICsgY2VudGVyLzMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhlbmQgPSAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVuZCA9IGNlbnRlcikpICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSwgDQogICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoLTksIGxlZ3p6eiRkaWFtZXRlci8yICsgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ3p6eiRjZW50ZXIgKyA1MCkpDQpgYGANCg0KSGV5LCBnb29kIGVub3VnaCBmb3IgZ292ZXJubWVudCB3b3JrISAgTm93IHRoYXQgdGhlIHJlcGxpY2F0aW9uIGlzIGNvbXBsZXRlLCBpcyB0aGVyZSBhbnl0aGluZyBtb3JlIGludGVyZXN0aW5nIHdlIGNhbiBsb29rIGF0IHdpdGggdGhpcyBkYXRhPw0KDQpPbmUgb3Bwb3J0dW5pdHkgdG8gcHJhY3RpY2Ugc29tZSBUaWR5IHNraWxseiBtaWdodCBiZSB0byBsb29rIGF0IHRoZSBjb3N0IG9mIGJ1aWxkaW5nIHRoZXNlIHdoZWVseS1ib2lpcywgZml0dGluZyBhIG1vZGVsLCBhbmQgbWFraW5nIGEgbmljZSBncmFwaGljYWwgaWxsdXN0cmF0aW9uIG9mIHRoZSBtb2RlbC4gIFRvIHN0YXJ0LCB3ZSBuZWVkIHRvIGNsZWFuIHRoZSBjb3N0IHZlY3Rvciwgd2hpY2ggaXMgc3VwZXIgZWFzeSB3aXRoIFRpZHk6DQoNCmBgYHtyfQ0KaGVhZCh3aGVlbHMkY29uc3RydWN0aW9uX2Nvc3QpDQpgYGANCg0KR3Jvc3MuICBMdWNraWx5IHRoZXJlIGlzIGEgY29tbW9uIHN0cnVjdHVyZSBmb3IgYWxsIG9mIHRoZXNlIG9ic2VydmF0aW9ucywgZXhjZXB0IGZvciB0aGUgR29sZGVuIEdhdGUgRmx5ZXIsIHdoaWNoIHJlcG9ydHMgYSByYW5nZSBvZiBlc3RpbWF0ZXMgZm9yIHRoZSBjb25zdHJ1Y3Rpb24gY29zdC4gIFJlZ2FyZGxlc3MsIGV2ZXJ5dGhpbmcgaXMgbGlzdGVkIGluIG1pbGxpb25zIG9mIFVTRCBzbyB3ZSBtaWdodCBqdXN0IC4uLg0KDQpgYGB7cn0NCndoZWVscyRjb3N0IDwtIHBhcnNlX251bWJlcih3aGVlbHMkY29uc3RydWN0aW9uX2Nvc3QpDQpgYGANCg0KVGhlIHBhcnNpbmcgZXJyb3JzIGFyZSBmaW5lLCB0aGV5IGp1c3QgbWVhbiAiY291bGQgbm90IGZpbmQgYSBudW1iZXIiIGhlcmUuICBOb3cgbGV0J3MgcnVuIGEgbW9kZWwgYWZ0ZXIgYSBsaXR0bGUgbW9yZSBjbGVhbmluZy4gIE5vdGUgdGhhdCB0aGVyZSBhcmUgbm90IG1hbnkgb2JzZXJ2YXRpb25zLCBhbmQgdGhlcmUgaXMgYSBnb29kIGFtb3VudCBvZiBtaXNzaW5nbmVzcyB3aGljaCBndWlkZXMgY292YXJpYXRlIHNlbGVjdGlvbiwgYW5kIHRoZSByZXN1bHRpbmcgbW9kZWwgd2lsbCBiZSBhbG1vc3QgYnkgZGVmaW5pdGlvbiBxdWl0ZSBjcnVtbXkuDQoNCmBgYHtyfQ0Kd2hlZWxzJGNvdW50cnkgPC0gYXMuZmFjdG9yKHdoZWVscyRjb3VudHJ5KQ0KDQp3aGVlbHMgJT4lIA0KICBkcGx5cjo6c2VsZWN0KGNvc3QsIGhlaWdodCwgY291bnRyeSkgJT4lIA0KICBuYS5vbWl0KC4pIC0+IHdoZWVsX2RhdA0KDQptIDwtIGxtZXIoY29zdCB+IHBvbHkoaGVpZ2h0LDIpICsgKDEgfGNvdW50cnkpLCANCiAgICAgICAgICAgIGRhdGEgPSB3aGVlbF9kYXQpDQoNCnByIDwtIGdncHJlZGljdChtLCAiaGVpZ2h0IFthbGxdIikNCnBsb3QocHIpICsNCiAgeGxhYigiV2hlZWwgSGVpZ2h0IikgKw0KICB5bGFiKCJXaGVlbCBDb3N0IikNCmBgYA0KDQpUaGUgYWJvdmUgaXMgdGhlIHByZWRpY3RlZCBjb3N0IGFzIGEgZnVuY3Rpb24gb2YgaGVpZ2h0LiAgQXMgd2Ugd291bGQgZXhwZWN0LCBpdCdzIGluY3JlYXNpbmcgYXQgYW4gaW5jcmVhc2luZyByYXRlLiAgTm93IGxldCdzIHRoaW5rIGFib3V0IHBsb3R0aW5nIHRoZSBCTFVQcy4NCg0KYGBge3J9DQpyYW5lZnMgPC0gZGF0YS5mcmFtZShyb3duYW1lcyhyYW5lZihtKSRjb3VudHJ5KSwNCiAgICAgICAgICAgICAgICAgICAgIHJhbmVmKG0pJGNvdW50cnksDQogICAgICAgICAgICAgICAgICAgICBzZS5yYW5lZihtKSRjb3VudHJ5KQ0KY29sbmFtZXMocmFuZWZzKSA8LSBjKCJjb3VudHJ5IiwgImludGVyY2VwdCIsICJzZSIpDQpyYW5lZnMkdGl0bGUgPC0gIlJhbmRvbSBJbnRlcmNlcHQgRXN0aW1hdGVzIg0KDQpyYW5lZnMkY291bnRyeSA8LSBmYWN0b3IoYXMuY2hhcmFjdGVyKHJhbmVmcyRjb3VudHJ5KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSByYW5lZnMkY291bnRyeVtvcmRlcihyYW5lZnMkaW50ZXJjZXB0KV0pDQoNCmdncGxvdChyYW5lZnMsIGFlcyh4ID0gY291bnRyeSwgeSA9IGludGVyY2VwdCkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbiA9IGludGVyY2VwdCAtIDIqc2UsDQogICAgICAgICAgICAgICAgICAgIHltYXggPSBpbnRlcmNlcHQgKyAyKnNlKSx3aWR0aD0wKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIHRoZW1lX2J3KCkgKw0KICBmYWNldF9ncmlkKH50aXRsZSkgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLA0KICAgICAgICAgICAgIGx0eSA9IDIpICsNCiAgeWxhYigiRXhwZW5kaXR1cmUgRGV2aWF0aW9uIGZyb20gQXZlcmFnZSIpICsNCiAgeGxhYigiQ291bnRyeSIpICsNCiAgdGhlbWUoc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMikpDQoNCmBgYA0KDQpTbyBpdCBsb29rcyBsaWtlLCB0YWtpbmcgaGVpZ2h0IGludG8gYWNjb3VudCwgdGhlIFVTQSBzcGVuZHMgYWJvdXQgNDggbWlsbGlvbiBVU0QgbW9yZSB0aGFuIGF2ZXJhZ2Ugb24gdGhlaXIgd2hlZWxzIHdoZXJlYXMgQ2hpbmEgc3BlbmRzIGFib3V0IDc2IG1pbGxpb24gVVNEIGxlc3MuICBBcyB0aGUgc2F5aW5nIGdvZXMgW0NoYWJ1ZHVvISBDbG9zZSBlbm91Z2gg4oCmXShodHRwczovL2Flb24uY28vZXNzYXlzL3doYXQtY2hpbmVzZS1jb3JuZXItY3V0dGluZy1yZXZlYWxzLWFib3V0LW1vZGVybml0eSkNCg0KcC5zLiBGYXJld2VsbCB0byB0aGUgW0lDUFNSIDIwMjIgU3VtbWVyIFByb2dyYW1dKGh0dHBzOi8vd2ViLmN2ZW50LmNvbS9ldmVudC81N2VmN2MxMC1mYmIzLTRjN2YtYjZiZS1mOWFkYmMxYWVmYjMvc3VtbWFyeSkh