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