How has hyperdrive technology in Star Wars changed over time? Are there differences by trilogies? We analyze these data from the They Star Wars API (SWAPI).

Setup

Load the tidyverse which is a collection of R packages that share common philosophies and are designed to work together. Load rwars which accesses SWAPI. Load additional packages for visualizing the data. Create labels for the trilogies. Pull data from SWAPI.

library(tidyverse)
library(rwars)
library(forcats)
library(ggrepel)
library(ggthemes)

trilogies <- c(
  "Prequels: Episode I-III", 
  "Originals: Episode IV-VI", 
  "Sequels: Episode VII"
  )

films <- get_all_films()$results

Package: dplyr

dplyr is a grammar of data manipulation, providing a consistent set of verbs that help you solve the most common data manipulation challenges: mutating, selecting, filtering, summarizing, and arranging your data. These all combine naturally which allows you to perform any operation “by group”. You can learn more about them in vignette(“dplyr”).

starwars %>% 
  filter(species == "Droid")
starwars %>% 
  select(name, ends_with("color"))
starwars %>% 
  mutate(name, bmi = mass / ((height / 100)  ^ 2)) %>%
  select(name:mass, bmi)
starwars %>% 
  arrange(desc(mass))
starwars %>%
  group_by(species) %>%
  summarise(
    n = n(),
    mass = mean(mass, na.rm = TRUE)
  ) %>%
  filter(n > 1)

Package: tibble

A tibble, or tbl_df, is a modern reimagining of the data.frame, keeping what time has proven to be effective, and throwing out what is not. Tibbles are data.frames that are lazy and surly: they do less (i.e. they don’t change variable names or types, and don’t do partial matching) and complain more (e.g. when a variable does not exist). This forces you to confront problems earlier, typically leading to cleaner, more expressive code. Tibbles also have an enhanced print method() which makes them easier to use with large datasets containing complex objects.

Notice how data.frame converts characters into factors, whereas tibble does not. No more stringsAsFactors = FALSE.

data.frame(
  id = 1:3,
  trilogies = trilogies
  )
tibble(
  id = 1:3,
  trilogies = trilogies
  )

Package: purrr

purrr enhances R’s functional programming (FP) toolkit by providing a complete and consistent set of tools for working with functions and vectors. If you’ve never heard of FP before, the best place to start is the family of map() functions which allow you to replace many for loops with code that is both more succinct and easier to read. The best place to learn about the map() functions is the iteration chapter in R for data science.

We will compare lapply to purrr::map and purrr::map_chr.

cat("lapply\n\n")
lapply
lapply(films, function(x)x$title)
[[1]]
[1] "A New Hope"

[[2]]
[1] "Attack of the Clones"

[[3]]
[1] "The Phantom Menace"

[[4]]
[1] "Revenge of the Sith"

[[5]]
[1] "Return of the Jedi"

[[6]]
[1] "The Empire Strikes Back"

[[7]]
[1] "The Force Awakens"
A New Hope
Attack of the Clones
The Phantom Menace
Revenge of the Sith
Return of the Jedi
The Empire Strikes Back
The Force Awakens
cat("purrr::map\n\n")
purrr::map
map(films, "title")
[[1]]
[1] "A New Hope"

[[2]]
[1] "Attack of the Clones"

[[3]]
[1] "The Phantom Menace"

[[4]]
[1] "Revenge of the Sith"

[[5]]
[1] "Return of the Jedi"

[[6]]
[1] "The Empire Strikes Back"

[[7]]
[1] "The Force Awakens"
A New Hope
Attack of the Clones
The Phantom Menace
Revenge of the Sith
Return of the Jedi
The Empire Strikes Back
The Force Awakens
cat("purrr::map_chr\n\n")
purrr::map_chr
map_chr(films, "title")
[1] "A New Hope"              "Attack of the Clones"   
[3] "The Phantom Menace"      "Revenge of the Sith"    
[5] "Return of the Jedi"      "The Empire Strikes Back"
[7] "The Force Awakens"      
A New Hope

Attack of the Clones

The Phantom Menace

Revenge of the Sith

Return of the Jedi

The Empire Strikes Back

The Force Awakens

Example: The rise of hyperdrive

What is the ratio of ships to vehicles in each movie? We will use the tidyverse to organize and visualize Star Wars data.

results <- tibble(
  title = map_chr(films, "title"),
  episode = map_dbl(films, "episode_id"),
  starships = map_dbl(films, ~length(.x$starships)),
  vehicles = map_dbl(films, ~length(.x$vehicles)),
  planets = map_dbl(films, ~length(.x$planets))
  ) %>%
  mutate(ships = vehicles + starships) %>%
  mutate(ratio = starships / ships * 100) %>% 
  mutate(Trilogy = trilogies[findInterval(episode, c(1,4,7))])
results

Notice that in the output format each column is a variable and each row is an observation. This is the tidy format that is useful for modeling and visualization.

ggplot(results, aes(reorder(title, episode), ratio)) + 
  geom_bar(aes(fill = Trilogy), stat = "identity", size = 1) +
  labs(
    title = "The Rise of Hyperdrive",
    subtitle = "Percentage of Ships with Hyperdrive Capability"
  ) +
  scale_y_continuous(labels = function(x){paste(x,"%")}) +
  theme_fivethirtyeight() +
  scale_colour_fivethirtyeight() +
  theme(
    axis.text.x = element_text(angle = 35, vjust = 0.9, hjust = 0.9)
  )

Insights

These data indicate an increased emphasis on hyperdrive from one trilogy to the next. However, it is important to note that the trilogies were made out of order. So there was actually a decrease in the percentage of hyperdrives from the second to the first trilogy.

Example: Predicting hyperdrive

We will visually examine vehicls with hyperdrive (starships) to the total number of vehicles (starships + vehicles) to determine if there are trends over time or by trilogy. There is a strong correlation between the number of ships with hyperdrive and the total number of ships. Notice that the number of ships increases within each trilogy. Expect more ships in Episode VIII: The Last Jedi.

results %>%
  ggplot(aes(ships, starships)) +
  geom_point(aes(color = Trilogy)) +
  theme_fivethirtyeight() +
  geom_smooth(method = "lm") +
  geom_text(aes(label = title), vjust = -1, size = 2.5) +
  labs(
    title = "Hyperdrive Correlations",
    subtitle = "The Number of Ships with Hyperdrive vs Total Ships"
  )

starship_model <- lm(starships ~ ships, data = results)
coef_ships <- coef(starship_model)['ships']
summary(starship_model)

Call:
lm(formula = starships ~ ships, data = results)

Residuals:
      1       2       3       4       5       6       7 
 1.2740 -1.3325 -1.7260 -0.5865  1.6675  0.9215 -0.2180 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)   
(Intercept)  1.31637    1.30877   1.006  0.36068   
ships        0.45081    0.07858   5.737  0.00225 **
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 1.442 on 5 degrees of freedom
Multiple R-squared:  0.8681,    Adjusted R-squared:  0.8418 
F-statistic: 32.92 on 1 and 5 DF,  p-value: 0.002254

The data show a positive trend for the percentage of ships with hyperdrive capability. Notice that 100% of the ships in The Force Awakens had hyperdrive. What will be the percentage for The Last Jedi?. Based on our visual inspection, we fit a simple linear model that predicts the number of ships with hyperdrive. The model indicates that for every additional ship introduced there are 0.45 more ships with hyperdrive capability added. In other words, the number of ships with hyperdrive is half of all ships plus one.

Predictions

There is a strong correlation between total number of ships and the number of ships with hyperdrive. The model predicts the number of ships with hyperdrive is roughly half of all ships plus one.

We predict that Episode VIII will have more ships overall than Episode VII, and that it will have a very high percentage of ships with hyperdrive.

LS0tCnRpdGxlOiAiU3RhciBXYXJzIGFuZCB0aGUgdGlkeXZlcnNlIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAotLS0KCkhvdyBoYXMgaHlwZXJkcml2ZSB0ZWNobm9sb2d5IGluIFN0YXIgV2FycyBjaGFuZ2VkIG92ZXIgdGltZT8gQXJlIHRoZXJlIGRpZmZlcmVuY2VzIGJ5IHRyaWxvZ2llcz8gV2UgYW5hbHl6ZSB0aGVzZSBkYXRhIGZyb20gdGhlIFtUaGV5IFN0YXIgV2FycyBBUEkgKFNXQVBJKV0oaHR0cDovL3N3YXBpLmNvLykuCgojIyBTZXR1cAoKTG9hZCB0aGUgYHRpZHl2ZXJzZWAgd2hpY2ggaXMgYSBjb2xsZWN0aW9uIG9mIFIgcGFja2FnZXMgdGhhdCBzaGFyZSBjb21tb24gcGhpbG9zb3BoaWVzIGFuZCBhcmUgZGVzaWduZWQgdG8gd29yayB0b2dldGhlci4gTG9hZCBgcndhcnNgIHdoaWNoIGFjY2Vzc2VzIFNXQVBJLiBMb2FkIGFkZGl0aW9uYWwgcGFja2FnZXMgZm9yIHZpc3VhbGl6aW5nIHRoZSBkYXRhLiBDcmVhdGUgbGFiZWxzIGZvciB0aGUgdHJpbG9naWVzLiBQdWxsIGRhdGEgZnJvbSBTV0FQSS4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShyd2FycykKbGlicmFyeShmb3JjYXRzKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoZ2d0aGVtZXMpCgp0cmlsb2dpZXMgPC0gYygKICAiUHJlcXVlbHM6IEVwaXNvZGUgSS1JSUkiLCAKICAiT3JpZ2luYWxzOiBFcGlzb2RlIElWLVZJIiwgCiAgIlNlcXVlbHM6IEVwaXNvZGUgVklJIgogICkKCmZpbG1zIDwtIGdldF9hbGxfZmlsbXMoKSRyZXN1bHRzCmBgYAoKIyMgUGFja2FnZTogZHBseXIKCmBkcGx5cmAgaXMgYSBncmFtbWFyIG9mIGRhdGEgbWFuaXB1bGF0aW9uLCBwcm92aWRpbmcgYSBjb25zaXN0ZW50IHNldCBvZiB2ZXJicyB0aGF0IGhlbHAgeW91IHNvbHZlIHRoZSBtb3N0IGNvbW1vbiBkYXRhIG1hbmlwdWxhdGlvbiBjaGFsbGVuZ2VzOiBtdXRhdGluZywgc2VsZWN0aW5nLCBmaWx0ZXJpbmcsIHN1bW1hcml6aW5nLCBhbmQgYXJyYW5naW5nIHlvdXIgZGF0YS4gVGhlc2UgYWxsIGNvbWJpbmUgbmF0dXJhbGx5IHdoaWNoIGFsbG93cyB5b3UgdG8gcGVyZm9ybSBhbnkgb3BlcmF0aW9uICJieSBncm91cCIuIFlvdSBjYW4gbGVhcm4gbW9yZSBhYm91dCB0aGVtIGluIHZpZ25ldHRlKCJkcGx5ciIpLgoKYGBge3J9CnN0YXJ3YXJzICU+JSAKICBmaWx0ZXIoc3BlY2llcyA9PSAiRHJvaWQiKQoKc3RhcndhcnMgJT4lIAogIHNlbGVjdChuYW1lLCBlbmRzX3dpdGgoImNvbG9yIikpCgpzdGFyd2FycyAlPiUgCiAgbXV0YXRlKG5hbWUsIGJtaSA9IG1hc3MgLyAoKGhlaWdodCAvIDEwMCkgIF4gMikpICU+JQogIHNlbGVjdChuYW1lOm1hc3MsIGJtaSkKCnN0YXJ3YXJzICU+JSAKICBhcnJhbmdlKGRlc2MobWFzcykpCgpzdGFyd2FycyAlPiUKICBncm91cF9ieShzcGVjaWVzKSAlPiUKICBzdW1tYXJpc2UoCiAgICBuID0gbigpLAogICAgbWFzcyA9IG1lYW4obWFzcywgbmEucm0gPSBUUlVFKQogICkgJT4lCiAgZmlsdGVyKG4gPiAxKQpgYGAKCiMjIFBhY2thZ2U6IHRpYmJsZQoKQSAqdGliYmxlKiwgb3IgKnRibF9kZiosIGlzIGEgbW9kZXJuIHJlaW1hZ2luaW5nIG9mIHRoZSBkYXRhLmZyYW1lLCBrZWVwaW5nIHdoYXQgdGltZSBoYXMgcHJvdmVuIHRvIGJlIGVmZmVjdGl2ZSwgYW5kIHRocm93aW5nIG91dCB3aGF0IGlzIG5vdC4gVGliYmxlcyBhcmUgZGF0YS5mcmFtZXMgdGhhdCBhcmUgKmxhenkgYW5kIHN1cmx5KjogdGhleSBkbyBsZXNzIChpLmUuIHRoZXkgZG9u4oCZdCBjaGFuZ2UgdmFyaWFibGUgbmFtZXMgb3IgdHlwZXMsIGFuZCBkb27igJl0IGRvIHBhcnRpYWwgbWF0Y2hpbmcpIGFuZCBjb21wbGFpbiBtb3JlIChlLmcuIHdoZW4gYSB2YXJpYWJsZSBkb2VzIG5vdCBleGlzdCkuIFRoaXMgZm9yY2VzIHlvdSB0byBjb25mcm9udCBwcm9ibGVtcyBlYXJsaWVyLCB0eXBpY2FsbHkgbGVhZGluZyB0byBjbGVhbmVyLCBtb3JlIGV4cHJlc3NpdmUgY29kZS4gVGliYmxlcyBhbHNvIGhhdmUgYW4gZW5oYW5jZWQgcHJpbnQgbWV0aG9kKCkgd2hpY2ggbWFrZXMgdGhlbSBlYXNpZXIgdG8gdXNlIHdpdGggbGFyZ2UgZGF0YXNldHMgY29udGFpbmluZyBjb21wbGV4IG9iamVjdHMuICAKCk5vdGljZSBob3cgYGRhdGEuZnJhbWVgIGNvbnZlcnRzIGNoYXJhY3RlcnMgaW50byBmYWN0b3JzLCB3aGVyZWFzIGB0aWJibGVgIGRvZXMgbm90LiBObyBtb3JlIGBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0VgLgoKYGBge3J9CmRhdGEuZnJhbWUoCiAgaWQgPSAxOjMsCiAgdHJpbG9naWVzID0gdHJpbG9naWVzCiAgKQoKdGliYmxlKAogIGlkID0gMTozLAogIHRyaWxvZ2llcyA9IHRyaWxvZ2llcwogICkKYGBgCgojIyBQYWNrYWdlOiBwdXJyciAKIAoqcHVycnIqIGVuaGFuY2VzIFLigJlzIGZ1bmN0aW9uYWwgcHJvZ3JhbW1pbmcgKEZQKSB0b29sa2l0IGJ5IHByb3ZpZGluZyBhIGNvbXBsZXRlIGFuZCBjb25zaXN0ZW50IHNldCBvZiB0b29scyBmb3Igd29ya2luZyB3aXRoIGZ1bmN0aW9ucyBhbmQgdmVjdG9ycy4gSWYgeW914oCZdmUgbmV2ZXIgaGVhcmQgb2YgRlAgYmVmb3JlLCB0aGUgYmVzdCBwbGFjZSB0byBzdGFydCBpcyB0aGUgZmFtaWx5IG9mIG1hcCgpIGZ1bmN0aW9ucyB3aGljaCBhbGxvdyB5b3UgdG8gcmVwbGFjZSBtYW55IGZvciBsb29wcyB3aXRoIGNvZGUgdGhhdCBpcyBib3RoIG1vcmUgc3VjY2luY3QgYW5kIGVhc2llciB0byByZWFkLiBUaGUgYmVzdCBwbGFjZSB0byBsZWFybiBhYm91dCB0aGUgbWFwKCkgZnVuY3Rpb25zIGlzIHRoZSBpdGVyYXRpb24gY2hhcHRlciBpbiBSIGZvciBkYXRhIHNjaWVuY2UuCgpXZSB3aWxsIGNvbXBhcmUgYGxhcHBseWAgdG8gYHB1cnJyOjptYXBgIGFuZCBgcHVycnI6Om1hcF9jaHJgLgoKYGBge3J9CmNhdCgibGFwcGx5XG5cbiIpCmxhcHBseShmaWxtcywgZnVuY3Rpb24oeCl4JHRpdGxlKQpjYXQoInB1cnJyOjptYXBcblxuIikKbWFwKGZpbG1zLCAidGl0bGUiKQpjYXQoInB1cnJyOjptYXBfY2hyXG5cbiIpCm1hcF9jaHIoZmlsbXMsICJ0aXRsZSIpCmBgYAoKIyMgRXhhbXBsZTogVGhlIHJpc2Ugb2YgaHlwZXJkcml2ZQoKV2hhdCBpcyB0aGUgcmF0aW8gb2Ygc2hpcHMgdG8gdmVoaWNsZXMgaW4gZWFjaCBtb3ZpZT8gV2Ugd2lsbCB1c2UgdGhlIGB0aWR5dmVyc2VgIHRvIG9yZ2FuaXplIGFuZCB2aXN1YWxpemUgU3RhciBXYXJzIGRhdGEuCgpgYGB7cn0KcmVzdWx0cyA8LSB0aWJibGUoCiAgdGl0bGUgPSBtYXBfY2hyKGZpbG1zLCAidGl0bGUiKSwKICBlcGlzb2RlID0gbWFwX2RibChmaWxtcywgImVwaXNvZGVfaWQiKSwKICBzdGFyc2hpcHMgPSBtYXBfZGJsKGZpbG1zLCB+bGVuZ3RoKC54JHN0YXJzaGlwcykpLAogIHZlaGljbGVzID0gbWFwX2RibChmaWxtcywgfmxlbmd0aCgueCR2ZWhpY2xlcykpLAogIHBsYW5ldHMgPSBtYXBfZGJsKGZpbG1zLCB+bGVuZ3RoKC54JHBsYW5ldHMpKQogICkgJT4lCiAgbXV0YXRlKHNoaXBzID0gdmVoaWNsZXMgKyBzdGFyc2hpcHMpICU+JQogIG11dGF0ZShyYXRpbyA9IHN0YXJzaGlwcyAvIHNoaXBzICogMTAwKSAlPiUgCiAgbXV0YXRlKFRyaWxvZ3kgPSB0cmlsb2dpZXNbZmluZEludGVydmFsKGVwaXNvZGUsIGMoMSw0LDcpKV0pCnJlc3VsdHMKYGBgCgpOb3RpY2UgdGhhdCBpbiB0aGUgb3V0cHV0IGZvcm1hdCBlYWNoIGNvbHVtbiBpcyBhIHZhcmlhYmxlIGFuZCBlYWNoIHJvdyBpcyBhbiBvYnNlcnZhdGlvbi4gVGhpcyBpcyB0aGUgdGlkeSBmb3JtYXQgdGhhdCBpcyB1c2VmdWwgZm9yIG1vZGVsaW5nIGFuZCB2aXN1YWxpemF0aW9uLgoKYGBge3J9CmdncGxvdChyZXN1bHRzLCBhZXMocmVvcmRlcih0aXRsZSwgZXBpc29kZSksIHJhdGlvKSkgKyAKICBnZW9tX2JhcihhZXMoZmlsbCA9IFRyaWxvZ3kpLCBzdGF0ID0gImlkZW50aXR5Iiwgc2l6ZSA9IDEpICsKICBsYWJzKAogICAgdGl0bGUgPSAiVGhlIFJpc2Ugb2YgSHlwZXJkcml2ZSIsCiAgICBzdWJ0aXRsZSA9ICJQZXJjZW50YWdlIG9mIFNoaXBzIHdpdGggSHlwZXJkcml2ZSBDYXBhYmlsaXR5IgogICkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBmdW5jdGlvbih4KXtwYXN0ZSh4LCIlIil9KSArCiAgdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KCkgKwogIHNjYWxlX2NvbG91cl9maXZldGhpcnR5ZWlnaHQoKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDM1LCB2anVzdCA9IDAuOSwgaGp1c3QgPSAwLjkpCiAgKQpgYGAKCiMjIEluc2lnaHRzCgpUaGVzZSBkYXRhIGluZGljYXRlIGFuIGluY3JlYXNlZCBlbXBoYXNpcyBvbiBoeXBlcmRyaXZlIGZyb20gb25lIHRyaWxvZ3kgdG8gdGhlIG5leHQuIEhvd2V2ZXIsIGl0IGlzIGltcG9ydGFudCB0byBub3RlIHRoYXQgdGhlIHRyaWxvZ2llcyB3ZXJlIG1hZGUgb3V0IG9mIG9yZGVyLiBTbyB0aGVyZSB3YXMgYWN0dWFsbHkgYSBkZWNyZWFzZSBpbiB0aGUgcGVyY2VudGFnZSBvZiBoeXBlcmRyaXZlcyBmcm9tIHRoZSBzZWNvbmQgdG8gdGhlIGZpcnN0IHRyaWxvZ3kuCgojIyBFeGFtcGxlOiBQcmVkaWN0aW5nIGh5cGVyZHJpdmUKCldlIHdpbGwgdmlzdWFsbHkgZXhhbWluZSB2ZWhpY2xzIHdpdGggaHlwZXJkcml2ZSAoYHN0YXJzaGlwc2ApIHRvIHRoZSB0b3RhbCBudW1iZXIgb2YgdmVoaWNsZXMgKGBzdGFyc2hpcHMgKyB2ZWhpY2xlc2ApIHRvIGRldGVybWluZSBpZiB0aGVyZSBhcmUgdHJlbmRzIG92ZXIgdGltZSBvciBieSB0cmlsb2d5LiBUaGVyZSBpcyBhIHN0cm9uZyBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBudW1iZXIgb2Ygc2hpcHMgd2l0aCBoeXBlcmRyaXZlIGFuZCB0aGUgdG90YWwgbnVtYmVyIG9mIHNoaXBzLiBOb3RpY2UgdGhhdCB0aGUgbnVtYmVyIG9mIHNoaXBzIGluY3JlYXNlcyB3aXRoaW4gZWFjaCB0cmlsb2d5LiBFeHBlY3QgbW9yZSBzaGlwcyBpbiAqRXBpc29kZSBWSUlJOiBUaGUgTGFzdCBKZWRpKi4KCmBgYHtyfQpyZXN1bHRzICU+JQogIGdncGxvdChhZXMoc2hpcHMsIHN0YXJzaGlwcykpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IFRyaWxvZ3kpKSArCiAgdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gdGl0bGUpLCB2anVzdCA9IC0xLCBzaXplID0gMi41KSArCiAgbGFicygKICAgIHRpdGxlID0gIkh5cGVyZHJpdmUgQ29ycmVsYXRpb25zIiwKICAgIHN1YnRpdGxlID0gIlRoZSBOdW1iZXIgb2YgU2hpcHMgd2l0aCBIeXBlcmRyaXZlIHZzIFRvdGFsIFNoaXBzIgogICkKCgpzdGFyc2hpcF9tb2RlbCA8LSBsbShzdGFyc2hpcHMgfiBzaGlwcywgZGF0YSA9IHJlc3VsdHMpCmNvZWZfc2hpcHMgPC0gY29lZihzdGFyc2hpcF9tb2RlbClbJ3NoaXBzJ10Kc3VtbWFyeShzdGFyc2hpcF9tb2RlbCkKYGBgCgpUaGUgZGF0YSBzaG93IGEgcG9zaXRpdmUgdHJlbmQgZm9yIHRoZSBwZXJjZW50YWdlIG9mIHNoaXBzIHdpdGggaHlwZXJkcml2ZSBjYXBhYmlsaXR5LiBOb3RpY2UgdGhhdCAxMDAlIG9mIHRoZSBzaGlwcyBpbiAqVGhlIEZvcmNlIEF3YWtlbnMqIGhhZCBoeXBlcmRyaXZlLiBXaGF0IHdpbGwgYmUgdGhlIHBlcmNlbnRhZ2UgZm9yICpUaGUgTGFzdCBKZWRpPyouIEJhc2VkIG9uIG91ciB2aXN1YWwgaW5zcGVjdGlvbiwgd2UgZml0IGEgc2ltcGxlIGxpbmVhciBtb2RlbCB0aGF0IHByZWRpY3RzIHRoZSBudW1iZXIgb2Ygc2hpcHMgd2l0aCBoeXBlcmRyaXZlLiBUaGUgbW9kZWwgaW5kaWNhdGVzIHRoYXQgZm9yIGV2ZXJ5IGFkZGl0aW9uYWwgc2hpcCBpbnRyb2R1Y2VkIHRoZXJlIGFyZSBgciByb3VuZChjb2VmX3NoaXBzLCAyKWAgbW9yZSBzaGlwcyB3aXRoIGh5cGVyZHJpdmUgY2FwYWJpbGl0eSBhZGRlZC4gSW4gb3RoZXIgd29yZHMsIHRoZSBudW1iZXIgb2Ygc2hpcHMgd2l0aCBoeXBlcmRyaXZlIGlzIGhhbGYgb2YgYWxsIHNoaXBzIHBsdXMgb25lLgoKCiMjIFByZWRpY3Rpb25zCgpUaGVyZSBpcyBhIHN0cm9uZyBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRvdGFsIG51bWJlciBvZiBzaGlwcyBhbmQgdGhlIG51bWJlciBvZiBzaGlwcyB3aXRoIGh5cGVyZHJpdmUuIFRoZSBtb2RlbCBwcmVkaWN0cyB0aGUgbnVtYmVyIG9mIHNoaXBzIHdpdGggaHlwZXJkcml2ZSBpcyByb3VnaGx5IGhhbGYgb2YgYWxsIHNoaXBzIHBsdXMgb25lLgoKV2UgcHJlZGljdCB0aGF0ICpFcGlzb2RlIFZJSUkqIHdpbGwgaGF2ZSBtb3JlIHNoaXBzIG92ZXJhbGwgdGhhbiAqRXBpc29kZSBWSUkqLCBhbmQgdGhhdCBpdCB3aWxsIGhhdmUgYSB2ZXJ5IGhpZ2ggcGVyY2VudGFnZSBvZiBzaGlwcyB3aXRoIGh5cGVyZHJpdmUuCg==