1 Zakład Bioinformatyki, Instytut Informatyki, Uniwersytet w Białymstoku

Correspondence: Jarosław Kotowicz <>

1 Książki i inne rzeczy

  1. R for Data Science
  2. ggplot2: Elegant Graphics for Data Analysis
  3. Colors in R

2 Praca ggplot2

2.1 Czyścimy środowisko

rm(list=ls())

3 R for Data Science (rozdział 28)

library(tidyverse)

Rysunki w oparciu o kody z książki R for Data Science.

3.1 Wykonywanie map (prostych)

library(maps)

3.1.1 Rozciągnięcie mapy i zapobieganie rozciągnięciu

nz <- map_data("nz")
ggplot(nz, aes(long, lat, group = group)) +
  geom_polygon(fill = "white", colour = "black")

ggplot(nz, aes(long, lat, group = group)) +
  geom_polygon(fill = "white", colour = "black") +
  coord_quickmap()

3.1.2 Mapa świata

world <- map_data("world")
ggplot(world, aes(long, lat, group = group)) +
  geom_polygon(fill = "white", colour = "black") +
  coord_quickmap()

3.2 Trochę wykresów ze se statystyki

bar <- ggplot(data = diamonds) + 
  geom_bar(
    mapping = aes(x = cut, fill = cut), 
    show.legend = FALSE,
    width = 1
  ) + 
  theme(aspect.ratio = 1) +
  labs(x = NULL, y = NULL)

3.2.1 Słupkowe

bar

bar + coord_flip()

3.2.2 Kołowe

bar + coord_polar()

3.3 Estetyka wykresów (co dorobić do wykresu, aby był czytelny)

3.3.1 Tytuł

ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(color = class)) +
  geom_smooth(se = FALSE) +
  labs(title = "Fuel efficiency generally decreases with engine size")

3.3.2 Podtytuł i podpis

ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(color = class)) +
  geom_smooth(se = FALSE) +
  labs(
    title = "Fuel efficiency generally decreases with engine size",
    subtitle = "Two seaters (sports cars) are an exception because of their light weight",
    caption = "Data from fueleconomy.gov"
  )

3.3.3 Osie

ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(colour = class)) +
  geom_smooth(se = FALSE) +
  labs(
    title = "Fuel efficiency generally decreases with engine size",
    subtitle = "Two seaters (sports cars) are an exception because of their light weight",
    caption = "Data from fueleconomy.gov",
    x = "Engine displacement (L)",
    y = "Highway fuel economy (mpg)",
    colour = "Car type"
  )

3.3.4 Jak używać symboli i wzorów np. w nazwach osi

set.seed(2020)
df <- tibble(
  x = runif(10),
  y = runif(10)
)
ggplot(df, aes(x, y)) +
  geom_point() +
  labs(
    x = quote(sum(x[i] ^ 2, i == 1, n)),
    y = quote(alpha + beta + frac(delta, theta))
  )

3.3.5 Umieszczanie textu na rysunku

best_in_class <- mpg %>%
  group_by(class) %>%
  filter(row_number(desc(hwy)) == 1)
ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(colour = class)) +
  geom_text(aes(label = model), data = best_in_class)

3.3.5.1 Lepszy sposób umieszczania textu na rysunku

ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(colour = class)) +
  geom_label(aes(label = model), data = best_in_class, nudge_y = 2, alpha = 0.5)

3.3.5.2 Pakiet ggrepel i jego warstwa geom_label_repel - jeszcze lepszy sposób umieszczania textu na rysunku

ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(colour = class), position = "jitter") +
  geom_point(size = 3, shape = 1, data = best_in_class) +
  ggrepel::geom_label_repel(aes(label = model), data = best_in_class)

3.3.5.3 Text na rysunku zamiast legendy do niego plus funkcja theme

class_avg <- mpg %>%
  group_by(class) %>%
  summarise(
    displ = median(displ),
    hwy = median(hwy)
  )
ggplot(mpg, aes(displ, hwy, colour = class)) +
  ggrepel::geom_label_repel(aes(label = class),
                            data = class_avg,
                            size = 6,
                            label.size = 0,
                            segment.color = NA
  ) +
  geom_point() +
  theme(legend.position = "none")

3.3.5.4 Umieszczanie dłuższych napisów

label <- mpg %>%
  summarise(
    displ = max(displ),
    hwy = max(hwy),
    label = "Increasing engine size is \nrelated to decreasing fuel economy."
  )
ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  geom_text(aes(label = label), data = label, vjust = "top", hjust = "right")

label <- tibble(
  displ = Inf,
  hwy = Inf,
  label = "Increasing engine size is \nrelated to decreasing fuel economy."
)
ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  geom_text(aes(label = label), data = label, vjust = "top", hjust = "right")

3.3.6 Ustawianie parametrów osi

ggplot(mpg, aes(displ, hwy)) +
  geom_point()

ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  scale_y_continuous(breaks = seq(15, 40, by = 5))

ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  scale_x_continuous(labels = NULL) +
  scale_y_continuous(labels = NULL)

3.3.6.1 oraz geometria odcinków geom_segment

library(DT)
presidential %>% datatable
presidential %>%
  mutate(id = 33 + row_number()) %>%
  ggplot(aes(start, id)) +
  geom_point() +
  geom_segment(aes(xend = end, yend = id)) +
  scale_x_date(NULL, breaks = presidential$start, date_labels = "'%y")

base <- ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(colour = class))

3.3.7 Gdzie chcemy legendę?

base + theme(legend.position = "left")

base + theme(legend.position = "top")

base + theme(legend.position = "bottom")

base + theme(legend.position = "right") # the default

3.3.7.1 Funcja guides do pracy z legendą

ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(colour = class)) +
  geom_smooth(se = FALSE) +
  theme(legend.position = "bottom") +
  guides(colour = guide_legend(nrow = 1, override.aes = list(size = 4)))

ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(colour = class)) +
  geom_smooth(se = FALSE) +
  theme(legend.position = "bottom") +
  guides(colour = guide_legend(nrow = 1))

3.3.8 Geometria geom_bind2

ggplot(diamonds, aes(carat, price)) +
  geom_bin2d()

ggplot(diamonds, aes(log10(carat), log10(price))) +
  geom_bin2d()

3.3.8.1 wraz ze skalowaniem logarytmicznym osi

ggplot(diamonds, aes(carat, price)) +
  geom_bin2d() + 
  scale_x_log10() + 
  scale_y_log10()

3.3.9 Zmiana wybieranych kolorów do mapowania zmiennych czynnikowych

ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(color = drv))

library(RColorBrewer)
display.brewer.all()

Proszę zobaczyć stronę z lepszym obrazowaniem kolorów z pakietu RColorBrewer

ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(color = drv)) +
  scale_colour_brewer(palette = "Set2")

presidential %>%
  mutate(id = 33 + row_number()) %>%
  ggplot(aes(start, id, colour = party)) +
  geom_point() +
  geom_segment(aes(xend = end, yend = id)) +
  scale_colour_manual(values = c(Republican = "red", Democratic = "blue"))

set.seed(2020)
df <- tibble(
  x = rnorm(10000),
  y = rnorm(10000)
)

3.3.10 Układ współrzędnych (nierówne skale na osich i co z tym zrobić)

ggplot(df, aes(x, y)) +
  geom_hex()

ggplot(df, aes(x, y)) +
  geom_hex() +
  coord_fixed()

3.3.10.1 Trochę poprawy kolorów wypełnienia z pakietem viridis

ggplot(df, aes(x, y)) +
  geom_hex() +
  viridis::scale_fill_viridis() +
  coord_fixed()

3.3.11 Zoom na rysunek, czyli wyświetlamy fragment rysunku

ggplot(mpg, mapping = aes(displ, hwy)) +
  geom_point(aes(color = class)) +
  geom_smooth() +
  coord_cartesian(xlim = c(5, 7), ylim = c(10, 30))

3.3.11.0.1 A to robi troszeczkę coś innego
mpg %>%
  filter(displ >= 5, displ <= 7, hwy >= 10, hwy <= 30) %>%
  ggplot(aes(displ, hwy)) +
  geom_point(aes(color = class)) +
  geom_smooth()

3.3.12 Zoom na dane, czyli ograniczenie wyświetlanych danych (filtrowanie na różne sposoby)

suv <- mpg %>% filter(class == "suv")
compact <- mpg %>% filter(class == "compact")
ggplot(suv, aes(displ, hwy, colour = drv)) +
  geom_point()

ggplot(compact, aes(displ, hwy, colour = drv)) +
  geom_point()

x_scale <- scale_x_continuous(limits = range(mpg$displ))
y_scale <- scale_y_continuous(limits = range(mpg$hwy))
col_scale <- scale_colour_discrete(limits = unique(mpg$drv))
ggplot(suv, aes(displ, hwy, colour = drv)) +
  geom_point() +
  x_scale +
  y_scale +
  col_scale

ggplot(compact, aes(displ, hwy, colour = drv)) +
  geom_point() +
  x_scale +
  y_scale +
  col_scale

3.3.13 Różne predefiniowane sposoby wyświetlania rysunków, czyli theme_XX

ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(color = class)) +
  geom_smooth(se = FALSE) +
  theme_bw()

ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(colour = class)) +
  geom_smooth(se = FALSE) +
  labs(
    title = "Fuel efficiency generally decreases with engine size",
    subtitle = "Two seaters (sports cars) are an exception because of their light weight",
    caption = "Data from fueleconomy.gov",
    x = "Engine displacement (L)",
    y = "Highway fuel economy (mpg)",
    colour = "Car type"
  )

3.3.14 Zapisywanie rysunku do zmiennych globalnych

rysunek <- ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(colour = class)) +
  geom_smooth(se = FALSE) +
  labs(
    title = "Fuel efficiency generally decreases with engine size",
    subtitle = "Two seaters (sports cars) are an exception because of their light weight",
    caption = "Data from fueleconomy.gov",
    x = "Engine displacement (L)",
    y = "Highway fuel economy (mpg)",
    colour = "Car type"
  )

4 Jakich blibliotek możemy jeszcze użyć

  1. Predefiniowane tematy
  1. Pomoc w modyfikowaniu parametr ów rysunku
  1. Dla map

itd.

5 Praca domowa z dnia 17 marca 2020r. (do wykładu)

Wypisz do 20 biblioteki ze strony projekty R, które rozszerzają działania pakietu ggplot2.

LS0tDQp0aXRsZTogIldwcm93YWR6ZW5pZSBkbyAgxZtyb2Rvd2lza2EgKlIqIC0gZ3JhZmlrYSB6IGdncGxvdDIiDQphdXRob3I6DQotIEphcm9zxYJhdyBLb3Rvd2ljejoNCiAgICBjb3JyZXNwb25kZW5jZTogbm8NCiAgICBlbWFpbDogai5rb3Rvd2ljekB1d2IuZWR1LnBsDQogICAgaW5zdGl0dXRlOiBJSVV3Qg0KZGF0ZTogIjE3IG1hcmNhIDIwMjAiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIGhpZ2hsaWdodDogaGFkZG9jaw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgcGFuZG9jX2FyZ3M6DQogICAgLSAtLWx1YS1maWx0ZXI9c2Nob2xhcmx5LW1ldGFkYXRhLmx1YQ0KICAgIC0gLS1sdWEtZmlsdGVyPWF1dGhvci1pbmZvLWJsb2Nrcy5sdWENCiAgICB0aGVtZTogY2VydWxlYW4NCiAgICB0b2M6IHllcw0KYmlibGlvZ3JhcGh5OiBJbmZFa28uYmliDQppbnN0aXR1dGU6DQotIElJVXdCOiBaYWvFgmFkIEJpb2luZm9ybWF0eWtpLCBJbnN0eXR1dCBJbmZvcm1hdHlraSwgVW5pd2Vyc3l0ZXQgdyBCaWHFgnltc3Rva3UNCmNzbDogYmlnLWRhdGEtYW5kLWluZm9ybWF0aW9uLWFuYWx5dGljcy5jc2wNCmFsd2F5c19hbGxvd19odG1sOiB5ZXMNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYA0KDQojIEtzacSFxbxraSBpIGlubmUgcnplY3p5DQoNCjEuIFtSIGZvciBEYXRhIFNjaWVuY2VdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovaW5kZXguaHRtbCkNCjIuIFtnZ3Bsb3QyOiBFbGVnYW50IEdyYXBoaWNzIGZvciBEYXRhIEFuYWx5c2lzXShodHRwczovL2dncGxvdDItYm9vay5vcmcvKQ0KMy4gW0NvbG9ycyBpbiBSXShodHRwOi8vd3d3LnN0YXQuY29sdW1iaWEuZWR1L350emhlbmcvZmlsZXMvUmNvbG9yLnBkZikNCg0KIyBQcmFjYSBnZ3Bsb3QyDQoNCiMjIEN6ecWbY2lteSDFm3JvZG93aXNrbw0KYGBge3IgY3p5c3pjemVuaWVfZGFueWNoLCBlY2hvPVRSVUV9DQpybShsaXN0PWxzKCkpDQpgYGANCg0KIyBSIGZvciBEYXRhIFNjaWVuY2UgKHJvemR6aWHFgiAyOCkNCmBgYHtyIHBha2lldF90aWR5dmVyc2V9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQpSeXN1bmtpIHcgb3BhcmNpdSBvIGtvZHkgeiBrc2nEhcW8a2kgKipSIGZvciBEYXRhIFNjaWVuY2UqKi4NCg0KIyMgV3lrb255d2FuaWUgbWFwIChwcm9zdHljaCkNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkobWFwcykNCmBgYA0KDQojIyMgUm96Y2nEhWduacSZY2llIG1hcHkgaSB6YXBvYmllZ2FuaWUgcm96Y2nEhWduacSZY2l1DQpgYGB7cn0NCm56IDwtIG1hcF9kYXRhKCJueiIpDQoNCmdncGxvdChueiwgYWVzKGxvbmcsIGxhdCwgZ3JvdXAgPSBncm91cCkpICsNCiAgZ2VvbV9wb2x5Z29uKGZpbGwgPSAid2hpdGUiLCBjb2xvdXIgPSAiYmxhY2siKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KG56LCBhZXMobG9uZywgbGF0LCBncm91cCA9IGdyb3VwKSkgKw0KICBnZW9tX3BvbHlnb24oZmlsbCA9ICJ3aGl0ZSIsIGNvbG91ciA9ICJibGFjayIpICsNCiAgY29vcmRfcXVpY2ttYXAoKQ0KYGBgDQoNCiMjIyBNYXBhIMWbd2lhdGENCmBgYHtyfQ0Kd29ybGQgPC0gbWFwX2RhdGEoIndvcmxkIikNCg0KZ2dwbG90KHdvcmxkLCBhZXMobG9uZywgbGF0LCBncm91cCA9IGdyb3VwKSkgKw0KICBnZW9tX3BvbHlnb24oZmlsbCA9ICJ3aGl0ZSIsIGNvbG91ciA9ICJibGFjayIpICsNCiAgY29vcmRfcXVpY2ttYXAoKQ0KYGBgDQojIyBUcm9jaMSZIHd5a3Jlc8OzdyB6ZSBzZSBzdGF0eXN0eWtpDQoNCmBgYHtyfQ0KYmFyIDwtIGdncGxvdChkYXRhID0gZGlhbW9uZHMpICsgDQogIGdlb21fYmFyKA0KICAgIG1hcHBpbmcgPSBhZXMoeCA9IGN1dCwgZmlsbCA9IGN1dCksIA0KICAgIHNob3cubGVnZW5kID0gRkFMU0UsDQogICAgd2lkdGggPSAxDQogICkgKyANCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKw0KICBsYWJzKHggPSBOVUxMLCB5ID0gTlVMTCkNCmBgYA0KDQojIyMgU8WCdXBrb3dlDQpgYGB7cn0NCmJhcg0KYGBgDQoNCmBgYHtyfQ0KYmFyICsgY29vcmRfZmxpcCgpDQpgYGANCg0KIyMjIEtvxYJvd2UNCmBgYHtyfQ0KYmFyICsgY29vcmRfcG9sYXIoKQ0KYGBgDQojIyBFc3RldHlrYSB3eWtyZXPDs3cgKGNvIGRvcm9iacSHIGRvIHd5a3Jlc3UsIGFieSBiecWCIGN6eXRlbG55KQ0KDQojIyMgVHl0dcWCDQpgYGB7cn0NCmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNsYXNzKSkgKw0KICBnZW9tX3Ntb290aChzZSA9IEZBTFNFKSArDQogIGxhYnModGl0bGUgPSAiRnVlbCBlZmZpY2llbmN5IGdlbmVyYWxseSBkZWNyZWFzZXMgd2l0aCBlbmdpbmUgc2l6ZSIpDQpgYGANCiMjIyBQb2R0eXR1xYIgaSBwb2RwaXMNCmBgYHtyfQ0KZ2dwbG90KG1wZywgYWVzKGRpc3BsLCBod3kpKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gY2xhc3MpKSArDQogIGdlb21fc21vb3RoKHNlID0gRkFMU0UpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJGdWVsIGVmZmljaWVuY3kgZ2VuZXJhbGx5IGRlY3JlYXNlcyB3aXRoIGVuZ2luZSBzaXplIiwNCiAgICBzdWJ0aXRsZSA9ICJUd28gc2VhdGVycyAoc3BvcnRzIGNhcnMpIGFyZSBhbiBleGNlcHRpb24gYmVjYXVzZSBvZiB0aGVpciBsaWdodCB3ZWlnaHQiLA0KICAgIGNhcHRpb24gPSAiRGF0YSBmcm9tIGZ1ZWxlY29ub215LmdvdiINCiAgKQ0KYGBgDQoNCiMjIyBPc2llDQoNCmBgYHtyfQ0KZ2dwbG90KG1wZywgYWVzKGRpc3BsLCBod3kpKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGNsYXNzKSkgKw0KICBnZW9tX3Ntb290aChzZSA9IEZBTFNFKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiRnVlbCBlZmZpY2llbmN5IGdlbmVyYWxseSBkZWNyZWFzZXMgd2l0aCBlbmdpbmUgc2l6ZSIsDQogICAgc3VidGl0bGUgPSAiVHdvIHNlYXRlcnMgKHNwb3J0cyBjYXJzKSBhcmUgYW4gZXhjZXB0aW9uIGJlY2F1c2Ugb2YgdGhlaXIgbGlnaHQgd2VpZ2h0IiwNCiAgICBjYXB0aW9uID0gIkRhdGEgZnJvbSBmdWVsZWNvbm9teS5nb3YiLA0KICAgIHggPSAiRW5naW5lIGRpc3BsYWNlbWVudCAoTCkiLA0KICAgIHkgPSAiSGlnaHdheSBmdWVsIGVjb25vbXkgKG1wZykiLA0KICAgIGNvbG91ciA9ICJDYXIgdHlwZSINCiAgKQ0KYGBgDQojIyMgSmFrIHXFvHl3YcSHIHN5bWJvbGkgaSB3em9yw7N3ICBucC4gdyBuYXp3YWNoIG9zaQ0KYGBge3J9DQpzZXQuc2VlZCgyMDIwKQ0KZGYgPC0gdGliYmxlKA0KICB4ID0gcnVuaWYoMTApLA0KICB5ID0gcnVuaWYoMTApDQopDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGYsIGFlcyh4LCB5KSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBsYWJzKA0KICAgIHggPSBxdW90ZShzdW0oeFtpXSBeIDIsIGkgPT0gMSwgbikpLA0KICAgIHkgPSBxdW90ZShhbHBoYSArIGJldGEgKyBmcmFjKGRlbHRhLCB0aGV0YSkpDQogICkNCmBgYA0KDQojIyMgVW1pZXN6Y3phbmllIHRleHR1IG5hIHJ5c3Vua3UNCg0KYGBge3J9DQpiZXN0X2luX2NsYXNzIDwtIG1wZyAlPiUNCiAgZ3JvdXBfYnkoY2xhc3MpICU+JQ0KICBmaWx0ZXIocm93X251bWJlcihkZXNjKGh3eSkpID09IDEpDQoNCmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBjbGFzcykpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IG1vZGVsKSwgZGF0YSA9IGJlc3RfaW5fY2xhc3MpDQpgYGANCg0KIyMjIyBMZXBzenkgc3Bvc8OzYiB1bWllc3pjemFuaWEgdGV4dHUgbmEgcnlzdW5rdQ0KDQpgYGB7cn0NCmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBjbGFzcykpICsNCiAgZ2VvbV9sYWJlbChhZXMobGFiZWwgPSBtb2RlbCksIGRhdGEgPSBiZXN0X2luX2NsYXNzLCBudWRnZV95ID0gMiwgYWxwaGEgPSAwLjUpDQpgYGANCg0KIyMjIyBQYWtpZXQgKipnZ3JlcGVsKiogaSBqZWdvIHdhcnN0d2EgKmdlb21fbGFiZWxfcmVwZWwqIC0gamVzemN6ZSBsZXBzenkgc3Bvc8OzYiB1bWllc3pjemFuaWEgdGV4dHUgbmEgcnlzdW5rdQ0KDQpgYGB7cn0NCmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBjbGFzcyksIHBvc2l0aW9uID0gImppdHRlciIpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMywgc2hhcGUgPSAxLCBkYXRhID0gYmVzdF9pbl9jbGFzcykgKw0KICBnZ3JlcGVsOjpnZW9tX2xhYmVsX3JlcGVsKGFlcyhsYWJlbCA9IG1vZGVsKSwgZGF0YSA9IGJlc3RfaW5fY2xhc3MpDQpgYGANCg0KIyMjIyBUZXh0IG5hIHJ5c3Vua3UgemFtaWFzdCBsZWdlbmR5IGRvIG5pZWdvIHBsdXMgZnVua2NqYSAqKnRoZW1lKioNCmBgYHtyfQ0KY2xhc3NfYXZnIDwtIG1wZyAlPiUNCiAgZ3JvdXBfYnkoY2xhc3MpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgZGlzcGwgPSBtZWRpYW4oZGlzcGwpLA0KICAgIGh3eSA9IG1lZGlhbihod3kpDQogICkNCg0KZ2dwbG90KG1wZywgYWVzKGRpc3BsLCBod3ksIGNvbG91ciA9IGNsYXNzKSkgKw0KICBnZ3JlcGVsOjpnZW9tX2xhYmVsX3JlcGVsKGFlcyhsYWJlbCA9IGNsYXNzKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gY2xhc3NfYXZnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSA2LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsLnNpemUgPSAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZ21lbnQuY29sb3IgPSBOQQ0KICApICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KYGBgDQojIyMjIFVtaWVzemN6YW5pZSBkxYJ1xbxzenljaCBuYXBpc8OzdyANCmBgYHtyfQ0KbGFiZWwgPC0gbXBnICU+JQ0KICBzdW1tYXJpc2UoDQogICAgZGlzcGwgPSBtYXgoZGlzcGwpLA0KICAgIGh3eSA9IG1heChod3kpLA0KICAgIGxhYmVsID0gIkluY3JlYXNpbmcgZW5naW5lIHNpemUgaXMgXG5yZWxhdGVkIHRvIGRlY3JlYXNpbmcgZnVlbCBlY29ub215LiINCiAgKQ0KDQpnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsIGh3eSkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGxhYmVsKSwgZGF0YSA9IGxhYmVsLCB2anVzdCA9ICJ0b3AiLCBoanVzdCA9ICJyaWdodCIpDQpgYGANCg0KYGBge3J9DQpsYWJlbCA8LSB0aWJibGUoDQogIGRpc3BsID0gSW5mLA0KICBod3kgPSBJbmYsDQogIGxhYmVsID0gIkluY3JlYXNpbmcgZW5naW5lIHNpemUgaXMgXG5yZWxhdGVkIHRvIGRlY3JlYXNpbmcgZnVlbCBlY29ub215LiINCikNCg0KZ2dwbG90KG1wZywgYWVzKGRpc3BsLCBod3kpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBsYWJlbCksIGRhdGEgPSBsYWJlbCwgdmp1c3QgPSAidG9wIiwgaGp1c3QgPSAicmlnaHQiKQ0KYGBgDQojIyMgVXN0YXdpYW5pZSBwYXJhbWV0csOzdyBvc2kNCg0KYGBge3J9DQpnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsIGh3eSkpICsNCiAgZ2VvbV9wb2ludCgpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsIGh3eSkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgxNSwgNDAsIGJ5ID0gNSkpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsIGh3eSkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IE5VTEwpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IE5VTEwpDQpgYGANCiMjIyMgIG9yYXogZ2VvbWV0cmlhIG9kY2lua8OzdyAqZ2VvbV9zZWdtZW50KiANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoRFQpDQpgYGANCg0KYGBge3J9DQpwcmVzaWRlbnRpYWwgJT4lIGRhdGF0YWJsZQ0KYGBgDQoNCmBgYHtyfQ0KcHJlc2lkZW50aWFsICU+JQ0KICBtdXRhdGUoaWQgPSAzMyArIHJvd19udW1iZXIoKSkgJT4lDQogIGdncGxvdChhZXMoc3RhcnQsIGlkKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3NlZ21lbnQoYWVzKHhlbmQgPSBlbmQsIHllbmQgPSBpZCkpICsNCiAgc2NhbGVfeF9kYXRlKE5VTEwsIGJyZWFrcyA9IHByZXNpZGVudGlhbCRzdGFydCwgZGF0ZV9sYWJlbHMgPSAiJyV5IikNCmBgYA0KDQpgYGB7cn0NCmJhc2UgPC0gZ2dwbG90KG1wZywgYWVzKGRpc3BsLCBod3kpKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGNsYXNzKSkNCmBgYA0KDQojIyMgR2R6aWUgY2hjZW15IGxlZ2VuZMSZPw0KDQpgYGB7cn0NCmJhc2UgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibGVmdCIpDQpgYGANCg0KYGBge3J9DQpiYXNlICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpDQpgYGANCg0KYGBge3J9DQpiYXNlICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQpgYGANCg0KYGBge3J9DQpiYXNlICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgIyB0aGUgZGVmYXVsdA0KYGBgDQoNCiMjIyMgRnVuY2phICpndWlkZXMqIGRvIHByYWN5IHogbGVnZW5kxIUNCmBgYHtyfQ0KZ2dwbG90KG1wZywgYWVzKGRpc3BsLCBod3kpKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGNsYXNzKSkgKw0KICBnZW9tX3Ntb290aChzZSA9IEZBTFNFKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArDQogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQobnJvdyA9IDEsIG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDQpKSkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBjbGFzcykpICsNCiAgZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKw0KICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG5yb3cgPSAxKSkNCmBgYA0KDQojIyMgR2VvbWV0cmlhICpnZW9tX2JpbmQyKg0KYGBge3J9DQpnZ3Bsb3QoZGlhbW9uZHMsIGFlcyhjYXJhdCwgcHJpY2UpKSArDQogIGdlb21fYmluMmQoKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGRpYW1vbmRzLCBhZXMobG9nMTAoY2FyYXQpLCBsb2cxMChwcmljZSkpKSArDQogIGdlb21fYmluMmQoKQ0KYGBgDQojIyMjIHdyYXogemUgc2thbG93YW5pZW0gbG9nYXJ5dG1pY3pueW0gb3NpDQpgYGB7cn0NCmdncGxvdChkaWFtb25kcywgYWVzKGNhcmF0LCBwcmljZSkpICsNCiAgZ2VvbV9iaW4yZCgpICsgDQogIHNjYWxlX3hfbG9nMTAoKSArIA0KICBzY2FsZV95X2xvZzEwKCkNCmBgYA0KIyMjIFptaWFuYSB3eWJpZXJhbnljaCBrb2xvcsOzdyBkbyBtYXBvd2FuaWEgem1pZW5ueWNoIGN6eW5uaWtvd3ljaA0KYGBge3J9DQpnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsIGh3eSkpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBkcnYpKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQpgYGANCg0KYGBge3J9DQpkaXNwbGF5LmJyZXdlci5hbGwoKQ0KYGBgDQoNCltQcm9zesSZIHpvYmFjennEhyBzdHJvbsSZIHogbGVwc3p5bSBvYnJhem93YW5pZW0ga29sb3LDs3cgeiBwYWtpZXR1IFJDb2xvckJyZXdlcl0oaHR0cHM6Ly9yZHJyLmlvL2NyYW4vUkNvbG9yQnJld2VyL21hbi9Db2xvckJyZXdlci5odG1sICJDb2xvckJyZXdlcjogQ29sb3JCcmV3ZXIgcGFsZXR0ZSIpDQpgYGB7cn0NCmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGRydikpICsNCiAgc2NhbGVfY29sb3VyX2JyZXdlcihwYWxldHRlID0gIlNldDIiKQ0KYGBgDQoNCmBgYHtyfQ0KcHJlc2lkZW50aWFsICU+JQ0KICBtdXRhdGUoaWQgPSAzMyArIHJvd19udW1iZXIoKSkgJT4lDQogIGdncGxvdChhZXMoc3RhcnQsIGlkLCBjb2xvdXIgPSBwYXJ0eSkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zZWdtZW50KGFlcyh4ZW5kID0gZW5kLCB5ZW5kID0gaWQpKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYyhSZXB1YmxpY2FuID0gInJlZCIsIERlbW9jcmF0aWMgPSAiYmx1ZSIpKQ0KYGBgDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMjAyMCkNCmRmIDwtIHRpYmJsZSgNCiAgeCA9IHJub3JtKDEwMDAwKSwNCiAgeSA9IHJub3JtKDEwMDAwKQ0KKQ0KYGBgDQoNCiMjIyBVa8WCYWQgd3Nww7PFgnJ6xJlkbnljaCAobmllcsOzd25lIHNrYWxlIG5hIG9zaWNoIGkgY28geiB0eW0genJvYmnEhykNCg0KYGBge3J9DQpnZ3Bsb3QoZGYsIGFlcyh4LCB5KSkgKw0KICBnZW9tX2hleCgpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGYsIGFlcyh4LCB5KSkgKw0KICBnZW9tX2hleCgpICsNCiAgY29vcmRfZml4ZWQoKQ0KYGBgDQoNCiMjIyMgVHJvY2jEmSBwb3ByYXd5IGtvbG9yw7N3IHd5cGXFgm5pZW5pYSB6IHBha2lldGVtICoqdmlyaWRpcyoqDQpgYGB7cn0NCmdncGxvdChkZiwgYWVzKHgsIHkpKSArDQogIGdlb21faGV4KCkgKw0KICB2aXJpZGlzOjpzY2FsZV9maWxsX3ZpcmlkaXMoKSArDQogIGNvb3JkX2ZpeGVkKCkNCmBgYA0KIyMjIFpvb20gbmEgcnlzdW5laywgY3p5bGkgd3nFm3dpZXRsYW15IGZyYWdtZW50IHJ5c3Vua3UNCmBgYHtyfQ0KZ2dwbG90KG1wZywgbWFwcGluZyA9IGFlcyhkaXNwbCwgaHd5KSkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNsYXNzKSkgKw0KICBnZW9tX3Ntb290aCgpICsNCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDUsIDcpLCB5bGltID0gYygxMCwgMzApKQ0KYGBgDQojIyMjIyBBIHRvIHJvYmkgdHJvc3plY3prxJkgY2/FmyBpbm5lZ28NCg0KYGBge3J9DQptcGcgJT4lDQogIGZpbHRlcihkaXNwbCA+PSA1LCBkaXNwbCA8PSA3LCBod3kgPj0gMTAsIGh3eSA8PSAzMCkgJT4lDQogIGdncGxvdChhZXMoZGlzcGwsIGh3eSkpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBjbGFzcykpICsNCiAgZ2VvbV9zbW9vdGgoKQ0KYGBgDQojIyMgWm9vbSBuYSBkYW5lLCBjenlsaSBvZ3JhbmljemVuaWUgd3nFm3dpZXRsYW55Y2ggZGFueWNoIChmaWx0cm93YW5pZSBuYSByw7PFvG5lIHNwb3NvYnkpDQpgYGB7cn0NCnN1diA8LSBtcGcgJT4lIGZpbHRlcihjbGFzcyA9PSAic3V2IikNCmNvbXBhY3QgPC0gbXBnICU+JSBmaWx0ZXIoY2xhc3MgPT0gImNvbXBhY3QiKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KHN1diwgYWVzKGRpc3BsLCBod3ksIGNvbG91ciA9IGRydikpICsNCiAgZ2VvbV9wb2ludCgpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoY29tcGFjdCwgYWVzKGRpc3BsLCBod3ksIGNvbG91ciA9IGRydikpICsNCiAgZ2VvbV9wb2ludCgpDQpgYGANCg0KYGBge3J9DQp4X3NjYWxlIDwtIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSByYW5nZShtcGckZGlzcGwpKQ0KeV9zY2FsZSA8LSBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gcmFuZ2UobXBnJGh3eSkpDQpjb2xfc2NhbGUgPC0gc2NhbGVfY29sb3VyX2Rpc2NyZXRlKGxpbWl0cyA9IHVuaXF1ZShtcGckZHJ2KSkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChzdXYsIGFlcyhkaXNwbCwgaHd5LCBjb2xvdXIgPSBkcnYpKSArDQogIGdlb21fcG9pbnQoKSArDQogIHhfc2NhbGUgKw0KICB5X3NjYWxlICsNCiAgY29sX3NjYWxlDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoY29tcGFjdCwgYWVzKGRpc3BsLCBod3ksIGNvbG91ciA9IGRydikpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgeF9zY2FsZSArDQogIHlfc2NhbGUgKw0KICBjb2xfc2NhbGUNCmBgYA0KIyMjIFLDs8W8bmUgcHJlZGVmaW5pb3dhbmUgc3Bvc29ieSB3ecWbd2lldGxhbmlhIHJ5c3Vua8OzdywgY3p5bGkgKip0aGVtZV9YWCoqDQoNCmBgYHtyfQ0KZ2dwbG90KG1wZywgYWVzKGRpc3BsLCBod3kpKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gY2xhc3MpKSArDQogIGdlb21fc21vb3RoKHNlID0gRkFMU0UpICsNCiAgdGhlbWVfYncoKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KG1wZywgYWVzKGRpc3BsLCBod3kpKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGNsYXNzKSkgKw0KICBnZW9tX3Ntb290aChzZSA9IEZBTFNFKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiRnVlbCBlZmZpY2llbmN5IGdlbmVyYWxseSBkZWNyZWFzZXMgd2l0aCBlbmdpbmUgc2l6ZSIsDQogICAgc3VidGl0bGUgPSAiVHdvIHNlYXRlcnMgKHNwb3J0cyBjYXJzKSBhcmUgYW4gZXhjZXB0aW9uIGJlY2F1c2Ugb2YgdGhlaXIgbGlnaHQgd2VpZ2h0IiwNCiAgICBjYXB0aW9uID0gIkRhdGEgZnJvbSBmdWVsZWNvbm9teS5nb3YiLA0KICAgIHggPSAiRW5naW5lIGRpc3BsYWNlbWVudCAoTCkiLA0KICAgIHkgPSAiSGlnaHdheSBmdWVsIGVjb25vbXkgKG1wZykiLA0KICAgIGNvbG91ciA9ICJDYXIgdHlwZSINCiAgKQ0KYGBgDQoNCiMjIyBaYXBpc3l3YW5pZSByeXN1bmt1IGRvIHptaWVubnljaCBnbG9iYWxueWNoDQpgYGB7cn0NCnJ5c3VuZWsgPC0gZ2dwbG90KG1wZywgYWVzKGRpc3BsLCBod3kpKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGNsYXNzKSkgKw0KICBnZW9tX3Ntb290aChzZSA9IEZBTFNFKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiRnVlbCBlZmZpY2llbmN5IGdlbmVyYWxseSBkZWNyZWFzZXMgd2l0aCBlbmdpbmUgc2l6ZSIsDQogICAgc3VidGl0bGUgPSAiVHdvIHNlYXRlcnMgKHNwb3J0cyBjYXJzKSBhcmUgYW4gZXhjZXB0aW9uIGJlY2F1c2Ugb2YgdGhlaXIgbGlnaHQgd2VpZ2h0IiwNCiAgICBjYXB0aW9uID0gIkRhdGEgZnJvbSBmdWVsZWNvbm9teS5nb3YiLA0KICAgIHggPSAiRW5naW5lIGRpc3BsYWNlbWVudCAoTCkiLA0KICAgIHkgPSAiSGlnaHdheSBmdWVsIGVjb25vbXkgKG1wZykiLA0KICAgIGNvbG91ciA9ICJDYXIgdHlwZSINCiAgKQ0KYGBgDQoNCg0KIyBKYWtpY2ggYmxpYmxpb3RlayBtb8W8ZW15IGplc3pjemUgdcW8ecSHDQoNCjEuIFByZWRlZmluaW93YW5lIHRlbWF0eQ0KICAtICoqZ2d0aGVtZXMqKg0KICAtICoqaHJicnRoZW1lcyoqDQoyLiBQb21vYyB3IG1vZHlmaWtvd2FuaXUgcGFyYW1ldHIgw7N3IHJ5c3Vua3UNCiAgLSAqKmdnVGhlbWVBc3Npc3QqKiBpICpnZ1RoZW1lQXNzaXN0R2FkZ2V0Kg0KMy4gRGxhIG1hcA0KICAtICoqZ2dtYXBzKioNCg0KaXRkLg0KDQoNCiMgUHJhY2EgZG9tb3dhIHogZG5pYSAxNyBtYXJjYSAyMDIwci4gKGRvIHd5a8WCYWR1KQ0KV3lwaXN6IGRvIDIwIGJpYmxpb3Rla2kgemUgW3N0cm9ueSBwcm9qZWt0eSBSXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZyksIGt0w7NyZSByb3pzemVyemFqxIUgZHppYcWCYW5pYSBwYWtpZXR1ICoqZ2dwbG90MioqLg0K