1 Explorative Datenanalyse - Teil 2

1.1 Kommunikation

Zuerst müssen einige Bibliotheken installiert und geladen werden:

install.packages(c("ggrepel", "viridis"))
library(ggrepel)
library(viridis)
library(tidyverse)

Der nächste Schritt nach dem Erlenen der Grundlagen von Datenvisualisierung und Datentransformation ist die Vorstellung und Kommunikation von Ergebnissen.

Um die eigenen Daten und Schlussfolgerungen einfach zugänglich und verständlich darzustellen, wollen wir an dieser Stelle den Fokus auf die Kommunikation legen.

1.1.1 Label/Beschriftungen

Beschriftungen der Achsen sowie Titel können helfen, direkt den Kontext einer Grafik bezüglich eines Themas oder einer Fragestellung zu erkennen.

Anstelle von “Ein Streudiagramm von X und Y” ist es sinnvoll, die Hauptresultate oder Zusammenhänge zu beschreiben:

ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(color = class)) +
  geom_smooth(se = FALSE) +
  labs(title = "Kraftstoffeffizienz sinkt tendenziell mit steigender Motorgröße")

Der Parameter title in labs() setzt hierbei den Text des Titels.

Zwei weitere Beschriftigungen sind möglich:

  • subtitle: Zusätzliche Details (in kleinerer Schriftfont) unter dem Titel

  • caption: Text unten rechts unter der Grafik; wird häufig für die Beschreibung der Datengrundlage genutzt

ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(color = class)) +
  geom_smooth(se = FALSE) +
  labs(title = "Kraftstoffeffizienz sinkt tendenziell mit steigender Motorgröße",
         subtitle = "Zweisitzer (Sportwagen) sind auf Grund ihres geringen Gewichts eine Ausnahme",
         caption = "Daten von fueleconomy.gov"
  )

Desweiteren lassen sich in labs() die Beschriftungen von Aesthetics verändern wie z. B die Achsen x und y:

ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(color = class)) +
  geom_smooth(se = FALSE) +
  labs(title = "Kraftstoffeffizienz sinkt tendenziell mit steigender Motorgröße",
         subtitle = "Zweisitzer (Sportwagen) sind auf Grund ihres geringen Gewichts eine Ausnahme",
         caption = "Daten von fueleconomy.gov",
         x = "Hubraum (in Liter)",
         y = "Kraftstoffeffizienz (Autobahnmeilen pro Gallone)",
         colour = "Antriebstyp"
  )

Desweiteren lassen sich auch Formeln o. ä. durch quote() verwenden. Siehe hierfür ?plotmath.

1.1.2 Fontart und Fontgröße

Um unter Windows weitere Fontarten verwenden zu können, müssen diese Fonts zunächst in R geladen werden.

Hierzu wird die Bibliothek extrafont verwendet, damit neben den Standardfonts auch weitere Fontarten importiert werden können:

#Standardfonts:
windowsFonts()

Die Installation von extrafont läuft wie üblich ab:

install.packages("extrafont")

Nun müssen Sie Rtools installieren gefolgt von den R Bibliotheken remotes und RTTf2pt1:

install.packages("remotes")
remotes::install_version("Rttf2pt1", version = "1.3.8")

Dann können Sie die Windowsfonts importieren. Das kann ein paar Minuten dauern, muss aber nur einmal durchgeführt werden:

extrafont::font_import()

In den nächsten Sessions können die Fonts mit den folgenden Befehl geladen:

library(extrafont)
loadfonts(device = "win")

Nun sollten Sie weitere Fonts sehen:

windowsFonts()

Hinweis: Arial ist unter der Fontfamilie Arial erhältlich und unterscheidet sich nur minimal von der Standardfont von R (meistens sans).

Die Fontart und Fontgröße einer Grafik lässt sich in ggplot unter theme(text = element_text(family = "Fontart", size = Größe)) anpassen:

ggplot(mpg) +
    geom_point(aes(displ, hwy)) +
  labs(title = "Kraftstoffeffizienz sinkt tendenziell mit steigender Motorgröße",
         x = "Hubraum",
         y= "Kraftstoffeffizienz") +
    theme(text = element_text(size = 11, family = "Arial"))

In der Grafikanzeige wird durch text = element_text() das interne Objekt für den Text mit den gewünschten Eigenschaften (Fontfamilie und Fontgröße) für den gesamten Text der Grafik angepasst.

In ?theme() können Sie nachschlagen, wie Sie die einzelnen Achsen, Titel, usw. separat anpassen können. In jedem Fall muss als Parameter ein element_text() Objekt gesetzt werden.

Im Folgenden eine kleine Übersicht:

  • text: gesamter Text der Grafik

  • axis.title: Titel der Achsen (Achsenbeschriftungen)

  • axis.text: Einheiten der Achsen

  • legend.text: gesamter Text der Legende

  • plot.title: Titel der Grafik

  • plot.subtitle: Untertitel der Grafik (unterhalb der Grafik)

  • plot.caption: Unterschrift der Grafik (unterhalb des Titels)

Alle Textelemente lassen sich auch separat anpassen (z. B. durch axis.title.x und axis.title.y).

Manchmal ist es beispielsweise sinnvoll, Titel und Achsenbeschriftungen größer darzustellen und die Einheiten der Achsen etwas kleiner:

ggplot(mpg) +
    geom_point(aes(displ, hwy)) +
  labs(title = "Kraftstoffeffizienz sinkt tendenziell mit steigender Motorgröße",
         x = "Hubraum",
         y = "Kraftstoffeffizienz") +
    theme(axis.title = element_text(size = 12, family = "Arial"),
                plot.title = element_text(size = 15, family = "Arial"),
                axis.text = element_text(size = 8, family = "Arial"))

Oder in manchen Grafiken sind die Einheiten standardmäßig zu klein:

ggplot(mpg) +
    geom_point(aes(displ, hwy)) +
  labs(title = "Kraftstoffeffizienz sinkt tendenziell mit steigender Motorgröße",
         x = "Hubraum",
         y = "Kraftstoffeffizienz") +
    theme(axis.text = element_text(size = 14, family = "Arial"))

Es lassen sich in element_text() auch weitere Parameter wie z. B. die Farbe oder die Ausrichtung verändern, sodass jedes Element (Achse, Titel, Legenden, usw.) bei Bedarf separat angepasst werden kann:

ggplot(mpg) +
    geom_point(aes(displ, hwy)) +
  labs(title = "Kraftstoffeffizienz sinkt tendenziell mit steigender Motorgröße",
         x = "Hubraum",
         y = "Kraftstoffeffizienz") +
    theme(axis.text = element_text(size = 10, family = "Serif", color = "red", angle = 90),
                axis.title = element_text(size = 14, family = "Arial Black", color = "blue"),
                plot.title = element_text(size = 20, family = "Times New Roman", face = "bold"))

1.1.3 Annotationen

Ebenfalls können Textbeschriftungen direkt in die Grafik eingezeichnet werden. Hierzu wird geom_text() verwendet werden, welches ähnlich zu geom_point() funktioniert, allerdings mit der Aesthetic label den Text einfügt.

Ein einfaches Beispiel, wo Beschriftungen eingefügt werden, ist das Filtern eines tibbles, der dann anschließend über die Grafik gezeichnet wird:

effizientestes_in_gruppe <- mpg %>%
    group_by(class) %>%
    filter(row_number(desc(hwy)) == 1)

ggplot(mpg, aes(displ, hwy)) +
    geom_point(aes(color = class)) +
    geom_text(aes(label = model), data = effizientestes_in_gruppe)

Da die Label und die Punkte überlappen, wirkt die Grafik sehr unübersichtlich. Zusätzlich können sich Label gegenseitig überdecken (siehe oben links).

Mit ein paar Anpassungen (geom_label() und nudge_y) kann die Grafik ein wenig verbessert werden:

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

Es wäre allerdings wünschenswert, dass es zu keinen Überlappungen kommt. Hierfür gibt es die Bibliothek ggrepel, welche die Label automatisch gut sichtbar platziert:

ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(colour = class)) +
  ggrepel::geom_label_repel(aes(label = model), data = effizientestes_in_gruppe)

Um die Punkte zusätzlich hervorzuheben, können auch dessen Aesthetics ein wenig angepasst werden:

ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(colour = class)) +
    geom_point(size = 2, shape = 5, data = effizientestes_in_gruppe) +
  ggrepel::geom_label_repel(aes(label = model), data = effizientestes_in_gruppe)

Nun lassen sich aus der Grafik die wichtigsten Punkte direkt einsehen. Betrachten wir die Grafik zuletzt noch mit allen Beschriftungen:

ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(colour = class)) +
    geom_point(size = 2, shape = 5, data = effizientestes_in_gruppe) +
  ggrepel::geom_label_repel(aes(label = model), data = effizientestes_in_gruppe) +
    labs(x = "Hubraum (in Liter)",
         y = "Kraftstoffeffizienz (Autobahnmeilen pro Gallone)",
         colour = "Antriebstyp",
             title = "Kraftstoffeffizienz sinkt tendenziell mit steigender Motorgröße",
         subtitle = "Die effizientesten Autos sind für jeden Antriebstyp markiert",
         caption = "Daten von fueleconomy.gov"
  ) +
    theme(legend.text = element_text(size = 8))

Der Befehl für die Erstellung wirkt zunächst etwas lang, jedoch lässt sich diese Grafik Schritt für Schritt mit den Label und zusätzlichen Informationen ergänzen, sodass die Grafik mit ihrer Aussagekraft ebenso Stück für Stück wächst, wobei jeder einzelne Schritt nachvollziehbar ist.

Ein weiteres Beispiel, um die Mediane der Punkte mit einem Label zu versehen:

# Median für jeden Autotypen
klassen_durchschnitt <- mpg %>%
  group_by(class) %>%
  summarise(
    displ = median(displ),
    hwy = median(hwy)
  )
# Label für die Mediane einfügen mit geom_label_repel
ggplot(mpg, aes(displ, hwy, colour = class)) +
  ggrepel::geom_label_repel(aes(label = class),
    data = klassen_durchschnitt,
    size = 4,
    label.size = 0
  ) +
  geom_point()

Zuerst wird klassen_durchschnitt erstellt, welcher durch group_by() und summarise() die Mediane aller Gruppen für displ und hwy umfasst. Dann werden die Label an der Stelle der Mediane eingefügt.

Der Befehl theme(legend.position = "none") entfernt hierbei die Legende.

Hilfreich bei dem Einfügen von Text direkt in die Grafik ist die summarise() Funktion, welche z. B. maximale oder minimale Werte berechnen kann. Das Problem ist hierbei, dass die Daten für die Grafiken immer Dataframes sein müssen, sodass immer ein Dataframe erstellt werden muss:

# Das Label hat dieselben x und y-Werte wie die folgende Grafik (bzw. sitzt durch max oben rechts)
label <- mpg %>%
  summarise(
    displ = max(displ),
    hwy = max(hwy),
    label = "Steigende Motorgröße geht mit\n verringerter Kraftstoffeffizienz einher."
  )
# Label durch geom_text einfügen
ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  geom_text(aes(label = label), data = label, vjust = "top", hjust = "right")

Um den Text direkt an den Rand zu schieben, können als Positionen Inf oder auch -Inf verwendet werden:

label <- tibble(
    displ = Inf,
    hwy = Inf,
    label = "Steigende Motorgröße geht mit\n verringerter Kraftstoffeffizienz einher."
  )

ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  geom_text(aes(label = label), data = label, vjust = "top", hjust = "right")

Hinweis: hjust und vjust geben an, wie der Text an seiner Position verschoben wird. Testen Sie einfach verschiedene Parameter und schauen Sie, was passiert:

Kombinationen von hjust und vjust

Weitere interessante Annotationsmöglichkeiten sind die folgenden:

  • geom_hline() und geom_vline() für horizontale und vertikale Linien.

  • geom_rect() für Rechtecke (um Punkte) mit Hilfe der Randkoordinaten xmin, xmax, ymin und ymax.

  • geom_segment() mit dem Argument arrow, um Pfeile auf Punkte zu zeichnen (Hervorhebung).

Mit Geduld und Kreativität lassen sich so Grafiken deutlich interessanter und informativer gestalten!

1.1.4 Skalen

ggplot2 setzt die Achsenskalierung automatisch, um die gesamten Daten darzustellen. So skaliert ggplot die Daten direkt auf Grundlage des Datentyps und der Spannweite.

In manchen Fällen kann es sinnvoll sein, die Skalen selbständig zu definieren:

  • Die Beschriftungen und Abstände der Skalierungen ändern

  • Eine andere Skalierungsart auswählen

Die folgenden Skalierungen führt ggplot2 in dem Beispiel selbständig durch:

# Die Grafiken sind gleich
ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(colour = class))

ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(colour = class)) +
  scale_x_continuous() +
  scale_y_continuous() +
  scale_colour_discrete()

Die Funktionen zum Skalieren fangen immer mit scale_ an, dann dem Namen der Aesthetic und schließlich _ und dem Skalierungstypen. Ein Beispiel wäre also scale_colour_discrete(), welches die Aesthetic colour diskret skaliert.

1.1.4.1 Achsen und Legenden

Um die Skalen der Achsen zu verändern werden die zwei Argumente breaks und labels verwendet:

  • breaks gibt die Position der Einträge auf den Achsen an

  • labels gibt die Namen (Text) der Einträge auf den Achsen an

Im Folgenden wird nun also die y-Achse so angepasst, dass von 15 bis 40 die Werte in 4er bzw. 5er Schritten angegeben werden:

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

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

labels lässt sich genauso benutzen wie breaks (als Vektor von Namen). Zusätzlich können die Beschriftungen auch entfernt werden, indem labels = NULL gesetzt wird:

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

In manchen Fällen wird es notwendig, die Achsen anzupassen, insbesondere wenn bestimmte Punkte hervorgehoben werden sollen.

In dem folgenden Beispiel werden Präsidentschaftszeiten aufgezeigt. Hierbei wäre es natürlich interessant, direkt zu sehen, in welchem exakten Zeitraum eine Regierungsperiode war:

presidential %>%
  mutate(id = 33 + row_number()) %>%
  ggplot(aes(start, id)) +
    geom_point() +
    geom_segment(aes(xend = end, yend = id))

Das lässt sich ebenfalls über die breaks und labels der Achsen einstellen. Da es sich hierbei um Daten handelt und keine Zahlen, verhält sich die zugehörige Funktion scale_x_date etwas anders. Für weitere Information hierzu, lesen Sie hier nach.

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")

1.1.4.2 Legenden Layout

Eine weitere Möglichkeit, um eine Grafik anschaulicher zu machen, sind Legenden. Diese werden von ggplot direkt hinzugefügt, aber können unter Umständen nicht aussagekräftig genug sein oder dessen Aussehen nicht direkt Ihren Vorstellungen entsprechen.

Die Legende wird über theme() verändert. Um beispielsweise die Position zu verändern, wird das Argument legend.position herangezogen (wie bereits vorher einmal angedeutet):

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

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

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

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

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

Ebenfalls kann die Legende mit legend.position = "none" entfernt werden.

Um die Darstellung der Legende zu ändern, werden guides(), guide_legend() und guide_colourbar() verwendet. Im folgenden Beispiel wird die Legende nach unten verschoben durch legend.position und durch nrow die Einträge auf eine Reihe verteilt. Zusätzlich wird eine Aesthetic überschrieben, sodass die Punkte (in der Legende) größer werden:

# Ohne Anpassung hat die Legende mehrere Reihen:
ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(colour = class)) +
  geom_smooth(se = FALSE) +
  theme(legend.position = "bottom")

# Mit Anpassung erhalten wir eine Reihe, wobei die Aesthetic der Größe der Punkte erhöht wird:
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)))

1.1.4.3 Eine Skalierung ersetzen

In manchen Fällen soll die Skala komplett ersetzt werden. In den meisten dieser Fällen handelt es sich um die kontinuierliche Skala oder die Farbskala. Häufig ist es beispielsweise hilfreich, die Skalen zu transformieren, um Beziehungen besser einzusehen:

# Ohne log-Transformation
ggplot(diamonds, aes(carat, price)) +
  geom_bin2d()

# Mit manueller log-Transformation
ggplot(diamonds, aes(log10(carat), log10(price))) +
  geom_bin2d()

Als Resultat werden die Achsen jedoch mit log10(x) beschriftet. Anstelle der Transformation der Aesthetic, können die Skalen auch direkt mit scale_x_log10() und scale_y_log10() transformiert werden (mit demselben Resultat):

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

In dem Fall der Transformation ist es sicherlich sinnvoll, das an einer Stelle zu erwähnen (Achsenbeschriftung oder mindestens in der Bildbeschreibung).

Die andere Skala, die häufig verändert wird, ist die Farbskala. Standardmäßig wird mit der kategorischen Skala das Farbschema automatisch so ausgesucht, dass die Farben maximal unterschiedlich sind. Besonders für Leute mit einer Farbschwäche bietet sich die Skala mit ColorBrewer an. Die Farbpaletten von ColorBrewer sind leichter auseinanderzuhalten:

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

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

Um die verschiedenen Farbpaletten von ColorBrewer in R nachzuschlagen, schauen Sie unter der entsprechenden Skalierungsfunktion nach (?scale_colour_brewer(), ?scale_fill_brewer()).

Ebenfalls kann es nützlich sein, ergänzend die Formen der Punkte zu ändern, um sie besser zu unterscheiden:

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

Die ColorBrewer Skalen sind hier visuell einsehbar und über das RColorBrewer Paket in R verwendbar.

Im folgenden sind ein paar Farbpaletten von ColorBrewer dargestellt:

# Farbpaletten für fünf Kategorien
RColorBrewer::display.brewer.all(n = 5)

Um die Farben manuell zu setzen, wird scale_colour_manual() verwendet. Im folgenden Beispiel werden die Präsidenten gemäß ihrer Partei eingefärbt (blau für Demokrat, rot für Republikaner):

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"))

Für kontinuierliche Farben wird scale_colour_gradient() oder scale_fill_gradient() oder scale_colour_gradient2() (positive und negative Zahlen) verwendet.

Ebenfalls kann scale_colour_viridis() aus dem viridis Paket verwendet werden, um Farbgradienten gut auseinanderzuhalten:

# Zufällige Werte
df <- tibble(
  x = rnorm(10000),
  y = rnorm(10000)
)
# Standarddarstellung
ggplot(df, aes(x, y)) +
  geom_hex() +
  coord_fixed()

# Farben gemäß viridis
ggplot(df, aes(x, y)) +
  geom_hex() +
  viridis::scale_fill_viridis() +
  coord_fixed()

Für die fill und colour Aesthetics gibt es jeweils separat Funktionen, um die Skalen zu ändern (scale_fill_x() oder scale_colour_x()).

1.1.5 Zoomen

Es gibt mehrere Möglichkeiten, den Anzeigebereich von Grafiken zu ändern:

  1. Die Daten anpassen, die angezeigt werden sollen

  2. Die Achsen Limitierungen der Skalen einzeln setzen

  3. xlim und ylim in coord_cartesian() setzen

Üblicherweise sollte coord_cartesian() genügen:

# Die Achsen beschränken:
ggplot(mpg, mapping = aes(displ, hwy)) +
  geom_point(aes(color = class)) +
  geom_smooth() +
  coord_cartesian(xlim = c(5, 7), ylim = c(10, 30))

# Punkte auf den Bereich filtern
# Hierduch wird geom_smooth verändert:
mpg %>%
  filter(displ >= 5, displ <= 7, hwy >= 10, hwy <= 30) %>%
  ggplot(aes(displ, hwy)) +
  geom_point(aes(color = class)) +
  geom_smooth()

Wir sehen, dass sich nach dem Filtern die Linie verändert, da sie nur auf den Bereich erstellt wird.

Der Skalenbereich lässt sich auch für jede Skala einzeln setzen. Das ist besonders nützlich, um Skalen zwischen mehreren Grafiken gleich zu gestalten. In dem folgenden Beispiel siehen wir das Problem, dass die Skalenbereiche der Grafiken nicht gleich sind, sodass sich die Grafiken nicht gut vergleichen lassen:

# Vergleiche SUVs und kompakte Autos
suv <- mpg %>% filter(class == "suv")
compact <- mpg %>% filter(class == "compact")
# SUVs:
ggplot(suv, aes(displ, hwy, colour = drv)) +
  geom_point()

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

Hierzu könnten wir mit limits die Achsen speichern und jeder Grafik separat beifügen:

# Skalen separat erstellen (limits gibt den kleinsten und größten Wert an, welcher durch range() wiedergegeben wird)
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))
# SUVs:
ggplot(suv, aes(displ, hwy, colour = drv)) +
  geom_point() +
  x_scale +
  y_scale +
  col_scale

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

Nun sind die Skalenbereiche gleich. In vielen Fällen bietet sich stattdessen jedoch eher ein Gitter mit facets an.

1.1.6 Themes

Letztendlich können auch verschiedene Themes ausgewählt werden. Das Standard-Theme ist theme_bw(). Weitere Themes sind in dem Paket ggthemes enthalten:

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

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

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

Zur Veränderung weiterer Elemente der Grafik lesen Sie am besten in dem ggplot2 Buch nach.

Unter anderem lassen sich auch eigene Themes erstellen und vieles mehr.

1.1.7 Grafiken speichern

Um Grafiken zu speichern lässt sich beispielsweise auf den Export-Knopf in der Grafikanzeige rechts klicken.

Ebenfalls gibt es die ggsave Funktion, um den letzten Plot oder ein geom-Objekt zu speichern:

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

ggsave("my-plot.pdf")
# oder:
plot <- ggplot(mpg, aes(displ, hwy)) + geom_point()
ggsave("my-plot2.png", plot)

2 Aufgaben

2.1 Übungsaufgaben

  1. Erstellen Sie eine beliebige Grafik des diamonds Datensatzes und fügen Sie der Grafik einen Überschrift (title), einen Untertitel (subtitle), eine “Unterschrift” (caption) und Beschriftungen der x-und y-Achse (x und y) sowie ein Farblabel bei.

  2. Verwenden Sie geom_text(), um Text in jede Ecke dieser Grafik zu zeichnen.

  3. Schauen Sie in die Beispiele der ?annotate() Funktion und fügen Sie mit Hilfe dieser Text in eine Grafik ein ohne vorher einen zusätzlichen tibble zu erstellen.

  4. Erstellen Sie ein Streudiagramm von displ und hwy des mpg Datensatzes.

    1. Filtern Sie zusätzlich für jeden Autotyp class nach dem effizientesten Auto.

    2. Markieren Sie diese Autos farblich anders als den Rest.

    3. Fügen Sie mit geom_segment und arrow() Pfeile in die Grafik ein, die auf die Punkte zeigen.

    4. Fügen Sie zusätzlich Label an das Ende der Pfeile ein, die das Automodell beschreiben.

    5. Fügen Sie schlussendlich der Grafik aussagekräftige Beschriftungen, usw. bei.

  5. Warum funktioniert der folgende Befehl nicht?

df <- tibble(
  x = rnorm(10000),
  y = rnorm(10000)
)
ggplot(df, aes(x, y)) +
  geom_hex() +
  scale_color_gradient(low = "white", high = "red") +
  coord_fixed()

6. Überschreiben Sie die Aesthetics in dem folgenden Befehl, um die Legende anschaulicher zu machen.

ggplot(diamonds, aes(carat, price)) +
  geom_point(aes(colour = cut), alpha = 1/20)

  1. Zeichnen Sie ein Streudiagramm von Sepal.Width und Sepal.Length des iris Datensatzes.
    1. Färben Sie die Punkte gemäß der Spezies ein.

    2. Fügen Sie Linien für jede Spezies hinzu und ändern Sie die linetype.

    3. Markieren Sie die kleinste und größte Pflanze jeder Spezies mit Pfeilen und Label.

    4. Beschriften Sie die Legende, Achsen und Titel sinnvoll.

LS0tCnRpdGxlOiAiU3RhdGlzdGlrIHVuZCBEYXRhIFNjaWVuY2UgLSBFeHBsb3JhdGl2ZSBEYXRlbmFuYWx5c2UgMiIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIG51bWJlcl9zZWN0aW9uczogeWVzCi0tLQoKIyBFeHBsb3JhdGl2ZSBEYXRlbmFuYWx5c2UgLSBUZWlsIDIKCiMjIEtvbW11bmlrYXRpb24KClp1ZXJzdCBtw7xzc2VuIGVpbmlnZSBCaWJsaW90aGVrZW4gaW5zdGFsbGllcnQgdW5kIGdlbGFkZW4gd2VyZGVuOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KaW5zdGFsbC5wYWNrYWdlcyhjKCJnZ3JlcGVsIiwgInZpcmlkaXMiKSkKYGBgCgpgYGB7cn0KbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCkRlciBuw6RjaHN0ZSBTY2hyaXR0IG5hY2ggZGVtIEVybGVuZW4gZGVyIEdydW5kbGFnZW4gdm9uCkRhdGVudmlzdWFsaXNpZXJ1bmcgdW5kIERhdGVudHJhbnNmb3JtYXRpb24gaXN0IGRpZSBWb3JzdGVsbHVuZyB1bmQKS29tbXVuaWthdGlvbiB2b24gRXJnZWJuaXNzZW4uCgpVbSBkaWUgZWlnZW5lbiBEYXRlbiB1bmQgU2NobHVzc2ZvbGdlcnVuZ2VuIGVpbmZhY2ggenVnw6RuZ2xpY2ggdW5kCnZlcnN0w6RuZGxpY2ggZGFyenVzdGVsbGVuLCB3b2xsZW4gd2lyIGFuIGRpZXNlciBTdGVsbGUgZGVuIEZva3VzIGF1ZiBkaWUKS29tbXVuaWthdGlvbiBsZWdlbi4KCiMjIyBMYWJlbC9CZXNjaHJpZnR1bmdlbgoKQmVzY2hyaWZ0dW5nZW4gZGVyIEFjaHNlbiBzb3dpZSBUaXRlbCBrw7ZubmVuIGhlbGZlbiwgZGlyZWt0IGRlbiBLb250ZXh0CmVpbmVyIEdyYWZpayBiZXrDvGdsaWNoIGVpbmVzIFRoZW1hcyBvZGVyIGVpbmVyIEZyYWdlc3RlbGx1bmcgenUKZXJrZW5uZW4uCgpBbnN0ZWxsZSB2b24gIkVpbiBTdHJldWRpYWdyYW1tIHZvbiBYIHVuZCBZIiBpc3QgZXMgc2lubnZvbGwsIGRpZQpIYXVwdHJlc3VsdGF0ZSBvZGVyIFp1c2FtbWVuaMOkbmdlIHp1IGJlc2NocmVpYmVuOgoKYGBge3J9CmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gY2xhc3MpKSArCiAgZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSkgKwogIGxhYnModGl0bGUgPSAiS3JhZnRzdG9mZmVmZml6aWVueiBzaW5rdCB0ZW5kZW56aWVsbCBtaXQgc3RlaWdlbmRlciBNb3Rvcmdyw7bDn2UiKQpgYGAKCkRlciBQYXJhbWV0ZXIgYHRpdGxlYCBpbiBgbGFicygpYCBzZXR6dCBoaWVyYmVpIGRlbiBUZXh0IGRlcyBUaXRlbHMuCgpad2VpIHdlaXRlcmUgQmVzY2hyaWZ0aWd1bmdlbiBzaW5kIG3DtmdsaWNoOgoKLSAgIGBzdWJ0aXRsZWA6IFp1c8OkdHpsaWNoZSBEZXRhaWxzIChpbiBrbGVpbmVyZXIgU2NocmlmdGZvbnQpIHVudGVyIGRlbQogICAgVGl0ZWwKCi0gICBgY2FwdGlvbmA6IFRleHQgdW50ZW4gcmVjaHRzIHVudGVyIGRlciBHcmFmaWs7IHdpcmQgaMOkdWZpZyBmw7xyIGRpZQogICAgQmVzY2hyZWlidW5nIGRlciBEYXRlbmdydW5kbGFnZSBnZW51dHp0CgpgYGB7cn0KZ2dwbG90KG1wZywgYWVzKGRpc3BsLCBod3kpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBjbGFzcykpICsKICBnZW9tX3Ntb290aChzZSA9IEZBTFNFKSArCiAgbGFicyh0aXRsZSA9ICJLcmFmdHN0b2ZmZWZmaXppZW56IHNpbmt0IHRlbmRlbnppZWxsIG1pdCBzdGVpZ2VuZGVyIE1vdG9yZ3LDtsOfZSIsCiAgCQkgc3VidGl0bGUgPSAiWndlaXNpdHplciAoU3BvcnR3YWdlbikgc2luZCBhdWYgR3J1bmQgaWhyZXMgZ2VyaW5nZW4gR2V3aWNodHMgZWluZSBBdXNuYWhtZSIsCiAgCQkgY2FwdGlvbiA9ICJEYXRlbiB2b24gZnVlbGVjb25vbXkuZ292IgogICkKYGBgCgpEZXN3ZWl0ZXJlbiBsYXNzZW4gc2ljaCBpbiBgbGFicygpYCBkaWUgQmVzY2hyaWZ0dW5nZW4gdm9uIEFlc3RoZXRpY3MKdmVyw6RuZGVybiB3aWUgei4gQiBkaWUgQWNoc2VuIGB4YCB1bmQgYHlgOgoKYGBge3J9CmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gY2xhc3MpKSArCiAgZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSkgKwogIGxhYnModGl0bGUgPSAiS3JhZnRzdG9mZmVmZml6aWVueiBzaW5rdCB0ZW5kZW56aWVsbCBtaXQgc3RlaWdlbmRlciBNb3Rvcmdyw7bDn2UiLAogIAkJIHN1YnRpdGxlID0gIlp3ZWlzaXR6ZXIgKFNwb3J0d2FnZW4pIHNpbmQgYXVmIEdydW5kIGlocmVzIGdlcmluZ2VuIEdld2ljaHRzIGVpbmUgQXVzbmFobWUiLAogIAkJIGNhcHRpb24gPSAiRGF0ZW4gdm9uIGZ1ZWxlY29ub215LmdvdiIsCiAgCQkgeCA9ICJIdWJyYXVtIChpbiBMaXRlcikiLAogIAkJIHkgPSAiS3JhZnRzdG9mZmVmZml6aWVueiAoQXV0b2JhaG5tZWlsZW4gcHJvIEdhbGxvbmUpIiwKICAJCSBjb2xvdXIgPSAiQW50cmllYnN0eXAiCiAgKQpgYGAKCkRlc3dlaXRlcmVuIGxhc3NlbiBzaWNoIGF1Y2ggRm9ybWVsbiBvLiDDpC4gZHVyY2ggYHF1b3RlKClgIHZlcndlbmRlbi4KU2llaGUgaGllcmbDvHIgYD9wbG90bWF0aGAuCgojIyMgRm9udGFydCB1bmQgRm9udGdyw7bDn2UKClVtIHVudGVyIFdpbmRvd3Mgd2VpdGVyZSBGb250YXJ0ZW4gdmVyd2VuZGVuIHp1IGvDtm5uZW4sIG3DvHNzZW4gZGllc2UKRm9udHMgenVuw6RjaHN0IGluIFIgZ2VsYWRlbiB3ZXJkZW4uCgpIaWVyenUgd2lyZCBkaWUgQmlibGlvdGhlayBgZXh0cmFmb250YCB2ZXJ3ZW5kZXQsIGRhbWl0IG5lYmVuIGRlbgpTdGFuZGFyZGZvbnRzIGF1Y2ggd2VpdGVyZSBGb250YXJ0ZW4gaW1wb3J0aWVydCB3ZXJkZW4ga8O2bm5lbjoKCmBgYHtyLCBldmFsPUZBTFNFfQojU3RhbmRhcmRmb250czoKd2luZG93c0ZvbnRzKCkKYGBgCgpEaWUgSW5zdGFsbGF0aW9uIHZvbiBgZXh0cmFmb250YCBsw6R1ZnQgd2llIMO8YmxpY2ggYWI6CgpgYGB7ciwgZXZhbD1GQUxTRX0KaW5zdGFsbC5wYWNrYWdlcygiZXh0cmFmb250IikKYGBgCgpOdW4gbcO8c3NlbiBTaWUKW1J0b29sc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvYmluL3dpbmRvd3MvUnRvb2xzL3J0b29sczQyL3J0b29scy5odG1sKQppbnN0YWxsaWVyZW4gZ2Vmb2xndCB2b24gZGVuIFIgQmlibGlvdGhla2VuIGByZW1vdGVzYCB1bmQgYFJUVGYycHQxYDoKCmBgYHtyLCBldmFsPUZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJyZW1vdGVzIikKcmVtb3Rlczo6aW5zdGFsbF92ZXJzaW9uKCJSdHRmMnB0MSIsIHZlcnNpb24gPSAiMS4zLjgiKQpgYGAKCkRhbm4ga8O2bm5lbiBTaWUgZGllIFdpbmRvd3Nmb250cyBpbXBvcnRpZXJlbi4gRGFzIGthbm4gZWluIHBhYXIgTWludXRlbgpkYXVlcm4sIG11c3MgYWJlciBudXIgZWlubWFsIGR1cmNoZ2Vmw7xocnQgd2VyZGVuOgoKYGBge3IsIGV2YWw9RkFMU0V9CmV4dHJhZm9udDo6Zm9udF9pbXBvcnQoKQpgYGAKCkluIGRlbiBuw6RjaHN0ZW4gU2Vzc2lvbnMga8O2bm5lbiBkaWUgRm9udHMgbWl0IGRlbiBmb2xnZW5kZW4gQmVmZWhsCmdlbGFkZW46CgpgYGB7ciwgZXZhbD1GQUxTRX0KbGlicmFyeShleHRyYWZvbnQpCmxvYWRmb250cyhkZXZpY2UgPSAid2luIikKYGBgCgpOdW4gc29sbHRlbiBTaWUgd2VpdGVyZSBGb250cyBzZWhlbjoKCmBgYHtyLCBldmFsPUZBTFNFfQp3aW5kb3dzRm9udHMoKQpgYGAKCkhpbndlaXM6IEFyaWFsIGlzdCB1bnRlciBkZXIgRm9udGZhbWlsaWUgYEFyaWFsYCBlcmjDpGx0bGljaCB1bmQKdW50ZXJzY2hlaWRldCBzaWNoIG51ciBtaW5pbWFsIHZvbiBkZXIgU3RhbmRhcmRmb250IHZvbiBSIChtZWlzdGVucwpgc2Fuc2ApLgoKRGllIEZvbnRhcnQgdW5kIEZvbnRncsO2w59lIGVpbmVyIEdyYWZpayBsw6Rzc3Qgc2ljaCBpbiBgZ2dwbG90YCB1bnRlcgpgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiRm9udGFydCIsIHNpemUgPSBHcsO2w59lKSlgIGFucGFzc2VuOgoKYGBge3J9CmdncGxvdChtcGcpICsKCWdlb21fcG9pbnQoYWVzKGRpc3BsLCBod3kpKSArCiAgbGFicyh0aXRsZSA9ICJLcmFmdHN0b2ZmZWZmaXppZW56IHNpbmt0IHRlbmRlbnppZWxsIG1pdCBzdGVpZ2VuZGVyIE1vdG9yZ3LDtsOfZSIsCiAgCQkgeCA9ICJIdWJyYXVtIiwKICAJCSB5PSAiS3JhZnRzdG9mZmVmZml6aWVueiIpICsKCXRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDExLCBmYW1pbHkgPSAiQXJpYWwiKSkKYGBgCgpJbiBkZXIgR3JhZmlrYW56ZWlnZSB3aXJkIGR1cmNoIGB0ZXh0ID0gZWxlbWVudF90ZXh0KClgIGRhcyBpbnRlcm5lCk9iamVrdCBmw7xyIGRlbiBUZXh0IG1pdCBkZW4gZ2V3w7xuc2NodGVuIEVpZ2Vuc2NoYWZ0ZW4gKEZvbnRmYW1pbGllIHVuZApGb250Z3LDtsOfZSkgZsO8ciBkZW4gZ2VzYW10ZW4gVGV4dCBkZXIgR3JhZmlrIGFuZ2VwYXNzdC4KCkluIGA/dGhlbWUoKWAga8O2bm5lbiBTaWUgbmFjaHNjaGxhZ2VuLCB3aWUgU2llIGRpZSBlaW56ZWxuZW4gQWNoc2VuLApUaXRlbCwgdXN3LiBzZXBhcmF0IGFucGFzc2VuIGvDtm5uZW4uIEluIGplZGVtIEZhbGwgbXVzcyBhbHMgUGFyYW1ldGVyCmVpbiBgZWxlbWVudF90ZXh0KClgIE9iamVrdCBnZXNldHp0IHdlcmRlbi4KCkltIEZvbGdlbmRlbiBlaW5lIGtsZWluZSDDnGJlcnNpY2h0OgoKLSAgIGB0ZXh0YDogZ2VzYW10ZXIgVGV4dCBkZXIgR3JhZmlrCgotICAgYGF4aXMudGl0bGVgOiBUaXRlbCBkZXIgQWNoc2VuIChBY2hzZW5iZXNjaHJpZnR1bmdlbikKCi0gICBgYXhpcy50ZXh0YDogRWluaGVpdGVuIGRlciBBY2hzZW4KCi0gICBgbGVnZW5kLnRleHRgOiBnZXNhbXRlciBUZXh0IGRlciBMZWdlbmRlCgotICAgYHBsb3QudGl0bGVgOiBUaXRlbCBkZXIgR3JhZmlrCgotICAgYHBsb3Quc3VidGl0bGVgOiBVbnRlcnRpdGVsIGRlciBHcmFmaWsgKHVudGVyaGFsYiBkZXIgR3JhZmlrKQoKLSAgIGBwbG90LmNhcHRpb25gOiBVbnRlcnNjaHJpZnQgZGVyIEdyYWZpayAodW50ZXJoYWxiIGRlcyBUaXRlbHMpCgpBbGxlIFRleHRlbGVtZW50ZSBsYXNzZW4gc2ljaCBhdWNoIHNlcGFyYXQgYW5wYXNzZW4gKHouIEIuIGR1cmNoCmBheGlzLnRpdGxlLnhgIHVuZCBgYXhpcy50aXRsZS55YCkuCgpNYW5jaG1hbCBpc3QgZXMgYmVpc3BpZWxzd2Vpc2Ugc2lubnZvbGwsIFRpdGVsIHVuZCBBY2hzZW5iZXNjaHJpZnR1bmdlbgpncsO2w59lciBkYXJ6dXN0ZWxsZW4gdW5kIGRpZSBFaW5oZWl0ZW4gZGVyIEFjaHNlbiBldHdhcyBrbGVpbmVyOgoKYGBge3J9CmdncGxvdChtcGcpICsKCWdlb21fcG9pbnQoYWVzKGRpc3BsLCBod3kpKSArCiAgbGFicyh0aXRsZSA9ICJLcmFmdHN0b2ZmZWZmaXppZW56IHNpbmt0IHRlbmRlbnppZWxsIG1pdCBzdGVpZ2VuZGVyIE1vdG9yZ3LDtsOfZSIsCiAgCQkgeCA9ICJIdWJyYXVtIiwKICAJCSB5ID0gIktyYWZ0c3RvZmZlZmZpemllbnoiKSArCgl0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFtaWx5ID0gIkFyaWFsIiksCgkJCQlwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSwgZmFtaWx5ID0gIkFyaWFsIiksCgkJCQlheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgsIGZhbWlseSA9ICJBcmlhbCIpKQpgYGAKCk9kZXIgaW4gbWFuY2hlbiBHcmFmaWtlbiBzaW5kIGRpZSBFaW5oZWl0ZW4gc3RhbmRhcmRtw6TDn2lnIHp1IGtsZWluOgoKYGBge3J9CmdncGxvdChtcGcpICsKCWdlb21fcG9pbnQoYWVzKGRpc3BsLCBod3kpKSArCiAgbGFicyh0aXRsZSA9ICJLcmFmdHN0b2ZmZWZmaXppZW56IHNpbmt0IHRlbmRlbnppZWxsIG1pdCBzdGVpZ2VuZGVyIE1vdG9yZ3LDtsOfZSIsCiAgCQkgeCA9ICJIdWJyYXVtIiwKICAJCSB5ID0gIktyYWZ0c3RvZmZlZmZpemllbnoiKSArCgl0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYW1pbHkgPSAiQXJpYWwiKSkKYGBgCgpFcyBsYXNzZW4gc2ljaCBpbiBgZWxlbWVudF90ZXh0KClgIGF1Y2ggd2VpdGVyZSBQYXJhbWV0ZXIgd2llIHouIEIuIGRpZQpGYXJiZSBvZGVyIGRpZSBBdXNyaWNodHVuZyB2ZXLDpG5kZXJuLCBzb2Rhc3MgamVkZXMgRWxlbWVudCAoQWNoc2UsClRpdGVsLCBMZWdlbmRlbiwgdXN3LikgYmVpIEJlZGFyZiBzZXBhcmF0IGFuZ2VwYXNzdCB3ZXJkZW4ga2FubjoKCmBgYHtyfQpnZ3Bsb3QobXBnKSArCglnZW9tX3BvaW50KGFlcyhkaXNwbCwgaHd5KSkgKwogIGxhYnModGl0bGUgPSAiS3JhZnRzdG9mZmVmZml6aWVueiBzaW5rdCB0ZW5kZW56aWVsbCBtaXQgc3RlaWdlbmRlciBNb3Rvcmdyw7bDn2UiLAogIAkJIHggPSAiSHVicmF1bSIsCiAgCQkgeSA9ICJLcmFmdHN0b2ZmZWZmaXppZW56IikgKwoJdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgZmFtaWx5ID0gIlNlcmlmIiwgY29sb3IgPSAicmVkIiwgYW5nbGUgPSA5MCksCgkJCQlheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgZmFtaWx5ID0gIkFyaWFsIEJsYWNrIiwgY29sb3IgPSAiYmx1ZSIpLAoJCQkJcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjAsIGZhbWlseSA9ICJUaW1lcyBOZXcgUm9tYW4iLCBmYWNlID0gImJvbGQiKSkKYGBgCgojIyMgQW5ub3RhdGlvbmVuCgpFYmVuZmFsbHMga8O2bm5lbiBUZXh0YmVzY2hyaWZ0dW5nZW4gZGlyZWt0IGluIGRpZSBHcmFmaWsgZWluZ2V6ZWljaG5ldAp3ZXJkZW4uIEhpZXJ6dSB3aXJkIGBnZW9tX3RleHQoKWAgdmVyd2VuZGV0IHdlcmRlbiwgd2VsY2hlcyDDpGhubGljaCB6dQpgZ2VvbV9wb2ludCgpYCBmdW5rdGlvbmllcnQsIGFsbGVyZGluZ3MgbWl0IGRlciBBZXN0aGV0aWMgYGxhYmVsYCBkZW4KVGV4dCBlaW5mw7xndC4KCkVpbiBlaW5mYWNoZXMgQmVpc3BpZWwsIHdvIEJlc2NocmlmdHVuZ2VuIGVpbmdlZsO8Z3Qgd2VyZGVuLCBpc3QgZGFzCkZpbHRlcm4gZWluZXMgYHRpYmJsZXNgLCBkZXIgZGFubiBhbnNjaGxpZcOfZW5kIMO8YmVyIGRpZSBHcmFmaWsKZ2V6ZWljaG5ldCB3aXJkOgoKYGBge3J9CmVmZml6aWVudGVzdGVzX2luX2dydXBwZSA8LSBtcGcgJT4lCglncm91cF9ieShjbGFzcykgJT4lCglmaWx0ZXIocm93X251bWJlcihkZXNjKGh3eSkpID09IDEpICMgb2RlciBzbGljZV9heChod3kpCgpnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsIGh3eSkpICsKCWdlb21fcG9pbnQoYWVzKGNvbG9yID0gY2xhc3MpKSArCglnZW9tX3RleHQoYWVzKGxhYmVsID0gbW9kZWwpLCBkYXRhID0gZWZmaXppZW50ZXN0ZXNfaW5fZ3J1cHBlKQpgYGAKCkRhIGRpZSBMYWJlbCB1bmQgZGllIFB1bmt0ZSDDvGJlcmxhcHBlbiwgd2lya3QgZGllIEdyYWZpayBzZWhyCnVuw7xiZXJzaWNodGxpY2guIFp1c8OkdHpsaWNoIGvDtm5uZW4gc2ljaCBMYWJlbCBnZWdlbnNlaXRpZyDDvGJlcmRlY2tlbgooc2llaGUgb2JlbiBsaW5rcykuCgpNaXQgZWluIHBhYXIgQW5wYXNzdW5nZW4gKGBnZW9tX2xhYmVsKClgIHVuZCBgbnVkZ2VfeSlgIGthbm4gZGllIEdyYWZpawplaW4gd2VuaWcgdmVyYmVzc2VydCB3ZXJkZW46CgpgYGB7cn0KZ2dwbG90KG1wZywgYWVzKGRpc3BsLCBod3kpKSArCglnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNsYXNzKSkgKwoJZ2VvbV9sYWJlbChhZXMobGFiZWwgPSBtb2RlbCksIGRhdGEgPSBlZmZpemllbnRlc3Rlc19pbl9ncnVwcGUsIG51ZGdlX3kgPSAyLCBhbHBoYSA9IDAuNSkKYGBgCgpFcyB3w6RyZSBhbGxlcmRpbmdzIHfDvG5zY2hlbnN3ZXJ0LCBkYXNzIGVzIHp1IGtlaW5lbiDDnGJlcmxhcHB1bmdlbiBrb21tdC4KSGllcmbDvHIgZ2lidCBlcyBkaWUgQmlibGlvdGhlayBgZ2dyZXBlbGAsIHdlbGNoZSBkaWUgTGFiZWwgYXV0b21hdGlzY2gKZ3V0IHNpY2h0YmFyIHBsYXR6aWVydDoKCmBgYHtyfQpnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsIGh3eSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBjbGFzcykpICsKICBnZ3JlcGVsOjpnZW9tX2xhYmVsX3JlcGVsKGFlcyhsYWJlbCA9IG1vZGVsKSwgZGF0YSA9IGVmZml6aWVudGVzdGVzX2luX2dydXBwZSkKYGBgCgpVbSBkaWUgUHVua3RlIHp1c8OkdHpsaWNoIGhlcnZvcnp1aGViZW4sIGvDtm5uZW4gYXVjaCBkZXNzZW4gQWVzdGhldGljcwplaW4gd2VuaWcgYW5nZXBhc3N0IHdlcmRlbjoKCmBgYHtyfQpnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsIGh3eSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBjbGFzcykpICsKCWdlb21fcG9pbnQoc2l6ZSA9IDIsIHNoYXBlID0gNSwgZGF0YSA9IGVmZml6aWVudGVzdGVzX2luX2dydXBwZSkgKwogIGdncmVwZWw6Omdlb21fbGFiZWxfcmVwZWwoYWVzKGxhYmVsID0gbW9kZWwpLCBkYXRhID0gZWZmaXppZW50ZXN0ZXNfaW5fZ3J1cHBlKQpgYGAKCk51biBsYXNzZW4gc2ljaCBhdXMgZGVyIEdyYWZpayBkaWUgd2ljaHRpZ3N0ZW4gUHVua3RlIGRpcmVrdCBlaW5zZWhlbi4KQmV0cmFjaHRlbiB3aXIgZGllIEdyYWZpayB6dWxldHp0IG5vY2ggbWl0IGFsbGVuIEJlc2NocmlmdHVuZ2VuOgoKYGBge3J9CmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGNsYXNzKSkgKwoJZ2VvbV9wb2ludChzaXplID0gMiwgc2hhcGUgPSA1LCBkYXRhID0gZWZmaXppZW50ZXN0ZXNfaW5fZ3J1cHBlKSArCiAgZ2dyZXBlbDo6Z2VvbV9sYWJlbF9yZXBlbChhZXMobGFiZWwgPSBtb2RlbCksIGRhdGEgPSBlZmZpemllbnRlc3Rlc19pbl9ncnVwcGUpICsKCWxhYnMoeCA9ICJIdWJyYXVtIChpbiBMaXRlcikiLAogIAkJIHkgPSAiS3JhZnRzdG9mZmVmZml6aWVueiAoQXV0b2JhaG5tZWlsZW4gcHJvIEdhbGxvbmUpIiwKICAJCSBjb2xvdXIgPSAiQW50cmllYnN0eXAiLAoJCQkgdGl0bGUgPSAiS3JhZnRzdG9mZmVmZml6aWVueiBzaW5rdCB0ZW5kZW56aWVsbCBtaXQgc3RlaWdlbmRlciBNb3Rvcmdyw7bDn2UiLAogIAkJIHN1YnRpdGxlID0gIkRpZSBlZmZpemllbnRlc3RlbiBBdXRvcyBzaW5kIGbDvHIgamVkZW4gQW50cmllYnN0eXAgbWFya2llcnQiLAogIAkJIGNhcHRpb24gPSAiRGF0ZW4gdm9uIGZ1ZWxlY29ub215LmdvdiIKICApICsKCXRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSkKYGBgCgpEZXIgQmVmZWhsIGbDvHIgZGllIEVyc3RlbGx1bmcgd2lya3QgenVuw6RjaHN0IGV0d2FzIGxhbmcsIGplZG9jaCBsw6Rzc3QKc2ljaCBkaWVzZSBHcmFmaWsgU2Nocml0dCBmw7xyIFNjaHJpdHQgbWl0IGRlbiBMYWJlbCB1bmQgenVzw6R0emxpY2hlbgpJbmZvcm1hdGlvbmVuIGVyZ8Okbnplbiwgc29kYXNzIGRpZSBHcmFmaWsgbWl0IGlocmVyIEF1c3NhZ2VrcmFmdCBlYmVuc28KU3TDvGNrIGbDvHIgU3TDvGNrIHfDpGNoc3QsIHdvYmVpIGplZGVyIGVpbnplbG5lIFNjaHJpdHQgbmFjaHZvbGx6aWVoYmFyCmlzdC4KCkVpbiB3ZWl0ZXJlcyBCZWlzcGllbCwgdW0gZGllIE1lZGlhbmUgZGVyIFB1bmt0ZSBtaXQgZWluZW0gTGFiZWwgenUKdmVyc2VoZW46CgpgYGB7cn0KIyBNZWRpYW4gZsO8ciBqZWRlbiBBdXRvdHlwZW4Ka2xhc3Nlbl9kdXJjaHNjaG5pdHQgPC0gbXBnICU+JQogIGdyb3VwX2J5KGNsYXNzKSAlPiUKICBzdW1tYXJpc2UoCiAgICBkaXNwbCA9IG1lZGlhbihkaXNwbCksCiAgICBod3kgPSBtZWRpYW4oaHd5KQogICkKIyBMYWJlbCBmw7xyIGRpZSBNZWRpYW5lIGVpbmbDvGdlbiBtaXQgZ2VvbV9sYWJlbF9yZXBlbApnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsIGh3eSwgY29sb3VyID0gY2xhc3MpKSArCiAgZ2dyZXBlbDo6Z2VvbV9sYWJlbF9yZXBlbChhZXMobGFiZWwgPSBjbGFzcyksCiAgICBkYXRhID0ga2xhc3Nlbl9kdXJjaHNjaG5pdHQsCiAgICBzaXplID0gNCwKICAgIGxhYmVsLnNpemUgPSAwCiAgKSArCiAgZ2VvbV9wb2ludCgpCmBgYAoKWnVlcnN0IHdpcmQgYGtsYXNzZW5fZHVyY2hzY2huaXR0YCBlcnN0ZWxsdCwgd2VsY2hlciBkdXJjaCBgZ3JvdXBfYnkoKWAKdW5kIGBzdW1tYXJpc2UoKWAgZGllIE1lZGlhbmUgYWxsZXIgR3J1cHBlbiBmw7xyIGBkaXNwbGAgdW5kIGBod3lgCnVtZmFzc3QuIERhbm4gd2VyZGVuIGRpZSBMYWJlbCBhbiBkZXIgU3RlbGxlIGRlciBNZWRpYW5lIGVpbmdlZsO8Z3QuCgpEZXIgQmVmZWhsIGB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpYCBlbnRmZXJudCBoaWVyYmVpIGRpZQpMZWdlbmRlLgoKSGlsZnJlaWNoIGJlaSBkZW0gRWluZsO8Z2VuIHZvbiBUZXh0IGRpcmVrdCBpbiBkaWUgR3JhZmlrIGlzdCBkaWUKYHN1bW1hcmlzZSgpYCBGdW5rdGlvbiwgd2VsY2hlIHouIEIuIG1heGltYWxlIG9kZXIgbWluaW1hbGUgV2VydGUKYmVyZWNobmVuIGthbm4uIERhcyBQcm9ibGVtIGlzdCBoaWVyYmVpLCBkYXNzIGRpZSBEYXRlbiBmw7xyIGRpZSBHcmFmaWtlbgppbW1lciBEYXRhZnJhbWVzIHNlaW4gbcO8c3Nlbiwgc29kYXNzIGltbWVyIGVpbiBEYXRhZnJhbWUgZXJzdGVsbHQgd2VyZGVuCm11c3M6CgpgYGB7cn0KIyBEYXMgTGFiZWwgaGF0IGRpZXNlbGJlbiB4IHVuZCB5LVdlcnRlIHdpZSBkaWUgZm9sZ2VuZGUgR3JhZmlrIChiencuIHNpdHp0IGR1cmNoIG1heCBvYmVuIHJlY2h0cykKbGFiZWwgPC0gbXBnICU+JQogIHN1bW1hcmlzZSgKICAgIGRpc3BsID0gbWF4KGRpc3BsKSwKICAgIGh3eSA9IG1heChod3kpLAogICAgbGFiZWwgPSAiU3RlaWdlbmRlIE1vdG9yZ3LDtsOfZSBnZWh0IG1pdFxuIHZlcnJpbmdlcnRlciBLcmFmdHN0b2ZmZWZmaXppZW56IGVpbmhlci4iCiAgKQojIExhYmVsIGR1cmNoIGdlb21fdGV4dCBlaW5mw7xnZW4KZ2dwbG90KG1wZywgYWVzKGRpc3BsLCBod3kpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gbGFiZWwpLCBkYXRhID0gbGFiZWwsIHZqdXN0ID0gInRvcCIsIGhqdXN0ID0gInJpZ2h0IikKYGBgCgpVbSBkZW4gVGV4dCBkaXJla3QgYW4gZGVuIFJhbmQgenUgc2NoaWViZW4sIGvDtm5uZW4gYWxzIFBvc2l0aW9uZW4gYEluZmAKb2RlciBhdWNoIGAtSW5mYCB2ZXJ3ZW5kZXQgd2VyZGVuOgoKYGBge3J9CmxhYmVsIDwtIHRpYmJsZSgKICAgIGRpc3BsID0gSW5mLAogICAgaHd5ID0gSW5mLAogICAgbGFiZWwgPSAiU3RlaWdlbmRlIE1vdG9yZ3LDtsOfZSBnZWh0IG1pdFxuIHZlcnJpbmdlcnRlciBLcmFmdHN0b2ZmZWZmaXppZW56IGVpbmhlci4iCiAgKQoKZ2dwbG90KG1wZywgYWVzKGRpc3BsLCBod3kpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gbGFiZWwpLCBkYXRhID0gbGFiZWwsIHZqdXN0ID0gInRvcCIsIGhqdXN0ID0gInJpZ2h0IikKYGBgCgpIaW53ZWlzOiBgaGp1c3RgIHVuZCBgdmp1c3RgIGdlYmVuIGFuLCB3aWUgZGVyIFRleHQgYW4gc2VpbmVyIFBvc2l0aW9uCnZlcnNjaG9iZW4gd2lyZC4gVGVzdGVuIFNpZSBlaW5mYWNoIHZlcnNjaGllZGVuZSBQYXJhbWV0ZXIgdW5kIHNjaGF1ZW4KU2llLCB3YXMgcGFzc2llcnQ6CgohWypLb21iaW5hdGlvbmVuIHZvbiBoanVzdCB1bmQKdmp1c3QqXShodHRwczovL2QzM3d1YnJma2kwbDY4LmNsb3VkZnJvbnQubmV0LzlmOWYxODA2YjVjNTgzODc0YTcyNWJkZmY1MjZlOTkwYmQ0ZmNiYTYvYjk1NWEvY29tbXVuaWNhdGUtcGxvdHNfZmlsZXMvZmlndXJlLWh0bWwvanVzdC0xLnBuZykKCldlaXRlcmUgaW50ZXJlc3NhbnRlIEFubm90YXRpb25zbcO2Z2xpY2hrZWl0ZW4gc2luZCBkaWUgZm9sZ2VuZGVuOgoKLSAgIFtgZ2VvbV9obGluZSgpYF0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dlb21fYWJsaW5lLmh0bWwpCiAgICB1bmQKICAgIFtgZ2VvbV92bGluZSgpYF0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dlb21fYWJsaW5lLmh0bWwpCiAgICBmw7xyIGhvcml6b250YWxlIHVuZCB2ZXJ0aWthbGUgTGluaWVuLgoKLSAgIFtgZ2VvbV9yZWN0KClgXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZ2VvbV90aWxlLmh0bWwpCiAgICBmw7xyIFJlY2h0ZWNrZSAodW0gUHVua3RlKSBtaXQgSGlsZmUgZGVyIFJhbmRrb29yZGluYXRlbiBgeG1pbmAsCiAgICBgeG1heGAsIGB5bWluYCB1bmQgYHltYXhgLgoKLSAgIFtgZ2VvbV9zZWdtZW50KClgXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZ2VvbV9zZWdtZW50Lmh0bWwpCiAgICBtaXQgZGVtIEFyZ3VtZW50IGBhcnJvd2AsIHVtIFBmZWlsZSBhdWYgUHVua3RlIHp1IHplaWNobmVuCiAgICAoSGVydm9yaGVidW5nKS4KCk1pdCBHZWR1bGQgdW5kIEtyZWF0aXZpdMOkdCBsYXNzZW4gc2ljaCBzbyBHcmFmaWtlbiBkZXV0bGljaAppbnRlcmVzc2FudGVyIHVuZCBpbmZvcm1hdGl2ZXIgZ2VzdGFsdGVuIQoKIyMjIFNrYWxlbgoKYGdncGxvdDJgIHNldHp0IGRpZSBBY2hzZW5za2FsaWVydW5nIGF1dG9tYXRpc2NoLCB1bSBkaWUgZ2VzYW10ZW4gRGF0ZW4KZGFyenVzdGVsbGVuLiBTbyBza2FsaWVydCBgZ2dwbG90YCBkaWUgRGF0ZW4gZGlyZWt0IGF1ZiBHcnVuZGxhZ2UgZGVzCkRhdGVudHlwcyB1bmQgZGVyIFNwYW5ud2VpdGUuCgpJbiBtYW5jaGVuIEbDpGxsZW4ga2FubiBlcyBzaW5udm9sbCBzZWluLCBkaWUgU2thbGVuIHNlbGJzdMOkbmRpZyB6dQpkZWZpbmllcmVuOgoKLSAgIERpZSBCZXNjaHJpZnR1bmdlbiB1bmQgQWJzdMOkbmRlIGRlciBTa2FsaWVydW5nZW4gw6RuZGVybgoKLSAgIEVpbmUgYW5kZXJlIFNrYWxpZXJ1bmdzYXJ0IGF1c3fDpGhsZW4KCkRpZSBmb2xnZW5kZW4gU2thbGllcnVuZ2VuIGbDvGhydCBgZ2dwbG90MmAgaW4gZGVtIEJlaXNwaWVsIHNlbGJzdMOkbmRpZwpkdXJjaDoKCmBgYHtyfQojIERpZSBHcmFmaWtlbiBzaW5kIGdsZWljaApnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsIGh3eSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBjbGFzcykpCmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGNsYXNzKSkgKwogIHNjYWxlX3hfY29udGludW91cygpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoKSArCiAgc2NhbGVfY29sb3VyX2Rpc2NyZXRlKCkKYGBgCgpEaWUgRnVua3Rpb25lbiB6dW0gU2thbGllcmVuIGZhbmdlbiBpbW1lciBtaXQgYHNjYWxlX2AgYW4sIGRhbm4gZGVtCk5hbWVuIGRlciBBZXN0aGV0aWMgdW5kIHNjaGxpZcOfbGljaCBgX2AgdW5kIGRlbSBTa2FsaWVydW5nc3R5cGVuLiBFaW4KQmVpc3BpZWwgd8OkcmUgYWxzbyBgc2NhbGVfY29sb3VyX2Rpc2NyZXRlKClgLCB3ZWxjaGVzIGRpZSBBZXN0aGV0aWMKYGNvbG91cmAgZGlza3JldCBza2FsaWVydC4KCiMjIyMgQWNoc2VuIHVuZCBMZWdlbmRlbgoKVW0gZGllIFNrYWxlbiBkZXIgQWNoc2VuIHp1IHZlcsOkbmRlcm4gd2VyZGVuIGRpZSB6d2VpIEFyZ3VtZW50ZSBgYnJlYWtzYAp1bmQgYGxhYmVsc2AgdmVyd2VuZGV0OgoKLSAgIGBicmVha3NgIGdpYnQgZGllIFBvc2l0aW9uIGRlciBFaW50csOkZ2UgYXVmIGRlbiBBY2hzZW4gYW4KCi0gICBgbGFiZWxzYCBnaWJ0IGRpZSBOYW1lbiAoVGV4dCkgZGVyIEVpbnRyw6RnZSBhdWYgZGVuIEFjaHNlbiBhbgoKSW0gRm9sZ2VuZGVuIHdpcmQgbnVuIGFsc28gZGllIHktQWNoc2Ugc28gYW5nZXBhc3N0LCBkYXNzIHZvbiAxNSBiaXMgNDAKZGllIFdlcnRlIGluIDRlciBiencuIDVlciBTY2hyaXR0ZW4gYW5nZWdlYmVuIHdlcmRlbjoKCmBgYHtyfQpnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsIGh3eSkpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMTUsIDQwLCBieSA9IDQpKQpnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsIGh3eSkpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMTUsIDQwLCBieSA9IDUpKQpgYGAKCmBsYWJlbHNgIGzDpHNzdCBzaWNoIGdlbmF1c28gYmVudXR6ZW4gd2llIGBicmVha3NgIChhbHMgVmVrdG9yIHZvbgpOYW1lbikuIFp1c8OkdHpsaWNoIGvDtm5uZW4gZGllIEJlc2NocmlmdHVuZ2VuIGF1Y2ggZW50ZmVybnQgd2VyZGVuLCBpbmRlbQpgbGFiZWxzID0gTlVMTGAgZ2VzZXR6dCB3aXJkOgoKYGBge3J9CmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IE5VTEwpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gTlVMTCkKYGBgCgpJbiBtYW5jaGVuIEbDpGxsZW4gd2lyZCBlcyBub3R3ZW5kaWcsIGRpZSBBY2hzZW4gYW56dXBhc3NlbiwgaW5zYmVzb25kZXJlCndlbm4gYmVzdGltbXRlIFB1bmt0ZSBoZXJ2b3JnZWhvYmVuIHdlcmRlbiBzb2xsZW4uCgpJbiBkZW0gZm9sZ2VuZGVuIEJlaXNwaWVsIHdlcmRlbiBQcsOkc2lkZW50c2NoYWZ0c3plaXRlbiBhdWZnZXplaWd0LgpIaWVyYmVpIHfDpHJlIGVzIG5hdMO8cmxpY2ggaW50ZXJlc3NhbnQsIGRpcmVrdCB6dSBzZWhlbiwgaW4gd2VsY2hlbQpleGFrdGVuIFplaXRyYXVtIGVpbmUgUmVnaWVydW5nc3BlcmlvZGUgd2FyOgoKYGBge3J9CnByZXNpZGVudGlhbCAlPiUKICBtdXRhdGUoaWQgPSAzMyArIHJvd19udW1iZXIoKSkgJT4lCiAgZ2dwbG90KGFlcyhzdGFydCwgaWQpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgZ2VvbV9zZWdtZW50KGFlcyh4ZW5kID0gZW5kLCB5ZW5kID0gaWQpKQpgYGAKCkRhcyBsw6Rzc3Qgc2ljaCBlYmVuZmFsbHMgw7xiZXIgZGllIGBicmVha3NgIHVuZCBgbGFiZWxzYCBkZXIgQWNoc2VuCmVpbnN0ZWxsZW4uIERhIGVzIHNpY2ggaGllcmJlaSB1bSBEYXRlbiBoYW5kZWx0IHVuZCBrZWluZSBaYWhsZW4sCnZlcmjDpGx0IHNpY2ggZGllIHp1Z2Vow7ZyaWdlIEZ1bmt0aW9uIGBzY2FsZV94X2RhdGVgIGV0d2FzIGFuZGVycy4gRsO8cgp3ZWl0ZXJlIEluZm9ybWF0aW9uIGhpZXJ6dSwgbGVzZW4gU2llCltoaWVyXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2Uvc2NhbGVfZGF0ZS5odG1sKSBuYWNoLgoKYGBge3J9CnByZXNpZGVudGlhbCAlPiUKICBtdXRhdGUoaWQgPSAzMyArIHJvd19udW1iZXIoKSkgJT4lCiAgZ2dwbG90KGFlcyhzdGFydCwgaWQpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgZ2VvbV9zZWdtZW50KGFlcyh4ZW5kID0gZW5kLCB5ZW5kID0gaWQpKSArCiAgICBzY2FsZV94X2RhdGUoTlVMTCwgYnJlYWtzID0gcHJlc2lkZW50aWFsJHN0YXJ0LCBkYXRlX2xhYmVscyA9ICInJXkiKQpgYGAKCiMjIyMgTGVnZW5kZW4gTGF5b3V0CgpFaW5lIHdlaXRlcmUgTcO2Z2xpY2hrZWl0LCB1bSBlaW5lIEdyYWZpayBhbnNjaGF1bGljaGVyIHp1IG1hY2hlbiwgc2luZApMZWdlbmRlbi4gRGllc2Ugd2VyZGVuIHZvbiBgZ2dwbG90YCBkaXJla3QgaGluenVnZWbDvGd0LCBhYmVyIGvDtm5uZW4KdW50ZXIgVW1zdMOkbmRlbiBuaWNodCBhdXNzYWdla3LDpGZ0aWcgZ2VudWcgc2VpbiBvZGVyIGRlc3NlbiBBdXNzZWhlbgpuaWNodCBkaXJla3QgSWhyZW4gVm9yc3RlbGx1bmdlbiBlbnRzcHJlY2hlbi4KCkRpZSBMZWdlbmRlIHdpcmQgw7xiZXIgYHRoZW1lKClgIHZlcsOkbmRlcnQuIFVtIGJlaXNwaWVsc3dlaXNlIGRpZQpQb3NpdGlvbiB6dSB2ZXLDpG5kZXJuLCB3aXJkIGRhcyBBcmd1bWVudCBgbGVnZW5kLnBvc2l0aW9uYCBoZXJhbmdlem9nZW4KKHdpZSBiZXJlaXRzIHZvcmhlciBlaW5tYWwgYW5nZWRldXRldCk6CgpgYGB7cn0KYmFzZSA8LSBnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsIGh3eSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBjbGFzcykpCgpiYXNlICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImxlZnQiKQpiYXNlICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmJhc2UgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKYmFzZSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICMgdGhlIGRlZmF1bHQKYGBgCgpFYmVuZmFsbHMga2FubiBkaWUgTGVnZW5kZSBtaXQgYGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lImAgZW50ZmVybnQKd2VyZGVuLgoKVW0gZGllIERhcnN0ZWxsdW5nIGRlciBMZWdlbmRlIHp1IMOkbmRlcm4sIHdlcmRlbiBgZ3VpZGVzKClgLApgZ3VpZGVfbGVnZW5kKClgIHVuZCBgZ3VpZGVfY29sb3VyYmFyKClgIHZlcndlbmRldC4gSW0gZm9sZ2VuZGVuCkJlaXNwaWVsIHdpcmQgZGllIExlZ2VuZGUgbmFjaCB1bnRlbiB2ZXJzY2hvYmVuIGR1cmNoIGBsZWdlbmQucG9zaXRpb25gCnVuZCBkdXJjaCBgbnJvd2AgZGllIEVpbnRyw6RnZSBhdWYgZWluZSBSZWloZSB2ZXJ0ZWlsdC4gWnVzw6R0emxpY2ggd2lyZAplaW5lIEFlc3RoZXRpYyDDvGJlcnNjaHJpZWJlbiwgc29kYXNzIGRpZSBQdW5rdGUgKGluIGRlciBMZWdlbmRlKSBncsO2w59lcgp3ZXJkZW46CgpgYGB7cn0KIyBPaG5lIEFucGFzc3VuZyBoYXQgZGllIExlZ2VuZGUgbWVocmVyZSBSZWloZW46CmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGNsYXNzKSkgKwogIGdlb21fc21vb3RoKHNlID0gRkFMU0UpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKIyBNaXQgQW5wYXNzdW5nIGVyaGFsdGVuIHdpciBlaW5lIFJlaWhlLCB3b2JlaSBkaWUgQWVzdGhldGljIGRlciBHcsO2w59lIGRlciBQdW5rdGUgZXJow7ZodCB3aXJkOgpnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsIGh3eSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBjbGFzcykpICsKICBnZW9tX3Ntb290aChzZSA9IEZBTFNFKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsKICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG5yb3cgPSAxLCBvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSA0KSkpCmBgYAoKIyMjIyBFaW5lIFNrYWxpZXJ1bmcgZXJzZXR6ZW4KCkluIG1hbmNoZW4gRsOkbGxlbiBzb2xsIGRpZSBTa2FsYSBrb21wbGV0dCBlcnNldHp0IHdlcmRlbi4gSW4gZGVuIG1laXN0ZW4KZGllc2VyIEbDpGxsZW4gaGFuZGVsdCBlcyBzaWNoIHVtIGRpZSBrb250aW51aWVybGljaGUgU2thbGEgb2RlciBkaWUKRmFyYnNrYWxhLiBIw6R1ZmlnIGlzdCBlcyBiZWlzcGllbHN3ZWlzZSBoaWxmcmVpY2gsIGRpZSBTa2FsZW4genUKdHJhbnNmb3JtaWVyZW4sIHVtIEJlemllaHVuZ2VuIGJlc3NlciBlaW56dXNlaGVuOgoKYGBge3J9CiMgT2huZSBsb2ctVHJhbnNmb3JtYXRpb24KZ2dwbG90KGRpYW1vbmRzLCBhZXMoY2FyYXQsIHByaWNlKSkgKwogIGdlb21fYmluMmQoKQojIE1pdCBtYW51ZWxsZXIgbG9nLVRyYW5zZm9ybWF0aW9uCmdncGxvdChkaWFtb25kcywgYWVzKGxvZzEwKGNhcmF0KSwgbG9nMTAocHJpY2UpKSkgKwogIGdlb21fYmluMmQoKQpgYGAKCkFscyBSZXN1bHRhdCB3ZXJkZW4gZGllIEFjaHNlbiBqZWRvY2ggbWl0IGBsb2cxMCh4KWAgYmVzY2hyaWZ0ZXQuCkFuc3RlbGxlIGRlciBUcmFuc2Zvcm1hdGlvbiBkZXIgQWVzdGhldGljLCBrw7ZubmVuIGRpZSBTa2FsZW4gYXVjaCBkaXJla3QKbWl0IGBzY2FsZV94X2xvZzEwKClgIHVuZCBgc2NhbGVfeV9sb2cxMCgpYCB0cmFuc2Zvcm1pZXJ0IHdlcmRlbiAobWl0CmRlbXNlbGJlbiBSZXN1bHRhdCk6CgpgYGB7cn0KZ2dwbG90KGRpYW1vbmRzLCBhZXMoY2FyYXQsIHByaWNlKSkgKwogIGdlb21fYmluMmQoKSArIAogIHNjYWxlX3hfbG9nMTAoKSArIAogIHNjYWxlX3lfbG9nMTAoKQpgYGAKCkluIGRlbSBGYWxsIGRlciBUcmFuc2Zvcm1hdGlvbiBpc3QgZXMgc2ljaGVybGljaCBzaW5udm9sbCwgZGFzIGFuIGVpbmVyClN0ZWxsZSB6dSBlcnfDpGhuZW4gKEFjaHNlbmJlc2NocmlmdHVuZyBvZGVyIG1pbmRlc3RlbnMgaW4gZGVyCkJpbGRiZXNjaHJlaWJ1bmcpLgoKRGllIGFuZGVyZSBTa2FsYSwgZGllIGjDpHVmaWcgdmVyw6RuZGVydCB3aXJkLCBpc3QgZGllIEZhcmJza2FsYS4KU3RhbmRhcmRtw6TDn2lnIHdpcmQgbWl0IGRlciBrYXRlZ29yaXNjaGVuIFNrYWxhIGRhcyBGYXJic2NoZW1hCmF1dG9tYXRpc2NoIHNvIGF1c2dlc3VjaHQsIGRhc3MgZGllIEZhcmJlbiBtYXhpbWFsIHVudGVyc2NoaWVkbGljaCBzaW5kLgpCZXNvbmRlcnMgZsO8ciBMZXV0ZSBtaXQgZWluZXIgRmFyYnNjaHfDpGNoZSBiaWV0ZXQgc2ljaCBkaWUgU2thbGEgbWl0CmBDb2xvckJyZXdlcmAgYW4uIERpZSBGYXJicGFsZXR0ZW4gdm9uIGBDb2xvckJyZXdlcmAgc2luZCBsZWljaHRlcgphdXNlaW5hbmRlcnp1aGFsdGVuOgoKYGBge3J9CiMgU3RhbmRhcmRmYXJiZW4KZ2dwbG90KG1wZywgYWVzKGRpc3BsLCBod3kpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBkcnYpKQojIENvbG9yQnJld2VyCmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gZHJ2KSkgKwogIHNjYWxlX2NvbG91cl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikKYGBgCgpVbSBkaWUgdmVyc2NoaWVkZW5lbiBGYXJicGFsZXR0ZW4gdm9uIGBDb2xvckJyZXdlcmAgaW4gUiBuYWNoenVzY2hsYWdlbiwKc2NoYXVlbiBTaWUgdW50ZXIgZGVyIGVudHNwcmVjaGVuZGVuIFNrYWxpZXJ1bmdzZnVua3Rpb24gbmFjaAooYD9zY2FsZV9jb2xvdXJfYnJld2VyKClgLCBgP3NjYWxlX2ZpbGxfYnJld2VyKClgKS4KCkViZW5mYWxscyBrYW5uIGVzIG7DvHR6bGljaCBzZWluLCBlcmfDpG56ZW5kIGRpZSBGb3JtZW4gZGVyIFB1bmt0ZSB6dQrDpG5kZXJuLCB1bSBzaWUgYmVzc2VyIHp1IHVudGVyc2NoZWlkZW46CgpgYGB7cn0KZ2dwbG90KG1wZywgYWVzKGRpc3BsLCBod3kpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBkcnYsIHNoYXBlID0gZHJ2KSkgKwogIHNjYWxlX2NvbG91cl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikKYGBgCgpEaWUgYENvbG9yQnJld2VyYCBTa2FsZW4gc2luZApbaGllcl0oaHR0cHM6Ly9jb2xvcmJyZXdlcjIub3JnLyN0eXBlPXNlcXVlbnRpYWwmc2NoZW1lPUJ1R24mbj0zKQp2aXN1ZWxsIGVpbnNlaGJhciB1bmQgw7xiZXIgZGFzIGBSQ29sb3JCcmV3ZXJgIFBha2V0IGluIFIgdmVyd2VuZGJhci4KCkltIGZvbGdlbmRlbiBzaW5kIGVpbiBwYWFyIEZhcmJwYWxldHRlbiB2b24gYENvbG9yQnJld2VyYCBkYXJnZXN0ZWxsdDoKCmBgYHtyfQojIEZhcmJwYWxldHRlbiBmw7xyIGbDvG5mIEthdGVnb3JpZW4KUkNvbG9yQnJld2VyOjpkaXNwbGF5LmJyZXdlci5hbGwobiA9IDUpCmBgYAoKVW0gZGllIEZhcmJlbiBtYW51ZWxsIHp1IHNldHplbiwgd2lyZCBgc2NhbGVfY29sb3VyX21hbnVhbCgpYCB2ZXJ3ZW5kZXQuCkltIGZvbGdlbmRlbiBCZWlzcGllbCB3ZXJkZW4gZGllIFByw6RzaWRlbnRlbiBnZW3DpMOfIGlocmVyIFBhcnRlaQplaW5nZWbDpHJidCAoYmxhdSBmw7xyIERlbW9rcmF0LCByb3QgZsO8ciBSZXB1Ymxpa2FuZXIpOgoKYGBge3J9CnByZXNpZGVudGlhbCAlPiUKICBtdXRhdGUoaWQgPSAzMyArIHJvd19udW1iZXIoKSkgJT4lCiAgZ2dwbG90KGFlcyhzdGFydCwgaWQsIGNvbG91ciA9IHBhcnR5KSkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIGdlb21fc2VnbWVudChhZXMoeGVuZCA9IGVuZCwgeWVuZCA9IGlkKSkgKwogICAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKFJlcHVibGljYW4gPSAicmVkIiwgRGVtb2NyYXRpYyA9ICJibHVlIikpCmBgYAoKRsO8ciBrb250aW51aWVybGljaGUgRmFyYmVuIHdpcmQgYHNjYWxlX2NvbG91cl9ncmFkaWVudCgpYCBvZGVyCmBzY2FsZV9maWxsX2dyYWRpZW50KClgIG9kZXIgYHNjYWxlX2NvbG91cl9ncmFkaWVudDIoKWAgKHBvc2l0aXZlIHVuZApuZWdhdGl2ZSBaYWhsZW4pIHZlcndlbmRldC4KCkViZW5mYWxscyBrYW5uIGBzY2FsZV9jb2xvdXJfdmlyaWRpcygpYCBhdXMgZGVtIGB2aXJpZGlzYCBQYWtldAp2ZXJ3ZW5kZXQgd2VyZGVuLCB1bSBGYXJiZ3JhZGllbnRlbiBndXQgYXVzZWluYW5kZXJ6dWhhbHRlbjoKCmBgYHtyfQojIFp1ZsOkbGxpZ2UgV2VydGUKZGYgPC0gdGliYmxlKAogIHggPSBybm9ybSgxMDAwMCksCiAgeSA9IHJub3JtKDEwMDAwKQopCiMgU3RhbmRhcmRkYXJzdGVsbHVuZwpnZ3Bsb3QoZGYsIGFlcyh4LCB5KSkgKwogIGdlb21faGV4KCkgKwogIGNvb3JkX2ZpeGVkKCkKIyBGYXJiZW4gZ2Vtw6TDnyB2aXJpZGlzCmdncGxvdChkZiwgYWVzKHgsIHkpKSArCiAgZ2VvbV9oZXgoKSArCiAgdmlyaWRpczo6c2NhbGVfZmlsbF92aXJpZGlzKCkgKwogIGNvb3JkX2ZpeGVkKCkKYGBgCgpGw7xyIGRpZSBgZmlsbGAgdW5kIGBjb2xvdXJgIEFlc3RoZXRpY3MgZ2lidCBlcyBqZXdlaWxzIHNlcGFyYXQKRnVua3Rpb25lbiwgdW0gZGllIFNrYWxlbiB6dSDDpG5kZXJuIChgc2NhbGVfZmlsbF94KClgIG9kZXIKYHNjYWxlX2NvbG91cl94KClgKS4KCiMjIyBab29tZW4KCkVzIGdpYnQgbWVocmVyZSBNw7ZnbGljaGtlaXRlbiwgZGVuIEFuemVpZ2ViZXJlaWNoIHZvbiBHcmFmaWtlbiB6dQrDpG5kZXJuOgoKMS4gIERpZSBEYXRlbiBhbnBhc3NlbiwgZGllIGFuZ2V6ZWlndCB3ZXJkZW4gc29sbGVuCgoyLiAgRGllIEFjaHNlbiBMaW1pdGllcnVuZ2VuIGRlciBTa2FsZW4gZWluemVsbiBzZXR6ZW4KCjMuICBgeGxpbWAgdW5kIGB5bGltYCBpbiBgY29vcmRfY2FydGVzaWFuKClgIHNldHplbgoKw5xibGljaGVyd2Vpc2Ugc29sbHRlIGBjb29yZF9jYXJ0ZXNpYW4oKWAgZ2Vuw7xnZW46CgpgYGB7cn0KIyBEaWUgQWNoc2VuIGJlc2NocsOkbmtlbjoKZ2dwbG90KG1wZywgbWFwcGluZyA9IGFlcyhkaXNwbCwgaHd5KSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gY2xhc3MpKSArCiAgZ2VvbV9zbW9vdGgoKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDUsIDcpLCB5bGltID0gYygxMCwgMzApKQojIFB1bmt0ZSBhdWYgZGVuIEJlcmVpY2ggZmlsdGVybgojIEhpZXJkdWNoIHdpcmQgZ2VvbV9zbW9vdGggdmVyw6RuZGVydDoKbXBnICU+JQogIGZpbHRlcihkaXNwbCA+PSA1LCBkaXNwbCA8PSA3LCBod3kgPj0gMTAsIGh3eSA8PSAzMCkgJT4lCiAgZ2dwbG90KGFlcyhkaXNwbCwgaHd5KSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gY2xhc3MpKSArCiAgZ2VvbV9zbW9vdGgoKQpgYGAKCldpciBzZWhlbiwgZGFzcyBzaWNoIG5hY2ggZGVtIEZpbHRlcm4gZGllIExpbmllIHZlcsOkbmRlcnQsIGRhIHNpZSBudXIKYXVmIGRlbiBCZXJlaWNoIGVyc3RlbGx0IHdpcmQuCgpEZXIgU2thbGVuYmVyZWljaCBsw6Rzc3Qgc2ljaCBhdWNoIGbDvHIgamVkZSBTa2FsYSBlaW56ZWxuIHNldHplbi4gRGFzIGlzdApiZXNvbmRlcnMgbsO8dHpsaWNoLCB1bSBTa2FsZW4gendpc2NoZW4gbWVocmVyZW4gR3JhZmlrZW4gZ2xlaWNoIHp1Cmdlc3RhbHRlbi4gSW4gZGVtIGZvbGdlbmRlbiBCZWlzcGllbCBzaWVoZW4gd2lyIGRhcyBQcm9ibGVtLCBkYXNzIGRpZQpTa2FsZW5iZXJlaWNoZSBkZXIgR3JhZmlrZW4gbmljaHQgZ2xlaWNoIHNpbmQsIHNvZGFzcyBzaWNoIGRpZSBHcmFmaWtlbgpuaWNodCBndXQgdmVyZ2xlaWNoZW4gbGFzc2VuOgoKYGBge3J9CiMgVmVyZ2xlaWNoZSBTVVZzIHVuZCBrb21wYWt0ZSBBdXRvcwpzdXYgPC0gbXBnICU+JSBmaWx0ZXIoY2xhc3MgPT0gInN1diIpCmNvbXBhY3QgPC0gbXBnICU+JSBmaWx0ZXIoY2xhc3MgPT0gImNvbXBhY3QiKQojIFNVVnM6CmdncGxvdChzdXYsIGFlcyhkaXNwbCwgaHd5LCBjb2xvdXIgPSBkcnYpKSArCiAgZ2VvbV9wb2ludCgpCiMga29tcGFrdGUgQXV0b3MKZ2dwbG90KGNvbXBhY3QsIGFlcyhkaXNwbCwgaHd5LCBjb2xvdXIgPSBkcnYpKSArCiAgZ2VvbV9wb2ludCgpCmBgYAoKSGllcnp1IGvDtm5udGVuIHdpciBtaXQgYGxpbWl0c2AgZGllIEFjaHNlbiBzcGVpY2hlcm4gdW5kIGplZGVyIEdyYWZpawpzZXBhcmF0IGJlaWbDvGdlbjoKCmBgYHtyfQojIFNrYWxlbiBzZXBhcmF0IGVyc3RlbGxlbiAobGltaXRzIGdpYnQgZGVuIGtsZWluc3RlbiB1bmQgZ3LDtsOfdGVuIFdlcnQgYW4sIHdlbGNoZXIgZHVyY2ggcmFuZ2UoKSB3aWVkZXJnZWdlYmVuIHdpcmQpCnhfc2NhbGUgPC0gc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IHJhbmdlKG1wZyRkaXNwbCkpCnlfc2NhbGUgPC0gc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IHJhbmdlKG1wZyRod3kpKQpjb2xfc2NhbGUgPC0gc2NhbGVfY29sb3VyX2Rpc2NyZXRlKGxpbWl0cyA9IHVuaXF1ZShtcGckZHJ2KSkKIyBTVVZzOgpnZ3Bsb3Qoc3V2LCBhZXMoZGlzcGwsIGh3eSwgY29sb3VyID0gZHJ2KSkgKwogIGdlb21fcG9pbnQoKSArCiAgeF9zY2FsZSArCiAgeV9zY2FsZSArCiAgY29sX3NjYWxlCiMga29tcGFrdGUgQXV0b3MKZ2dwbG90KGNvbXBhY3QsIGFlcyhkaXNwbCwgaHd5LCBjb2xvdXIgPSBkcnYpKSArCiAgZ2VvbV9wb2ludCgpICsKICB4X3NjYWxlICsKICB5X3NjYWxlICsKICBjb2xfc2NhbGUKYGBgCgpOdW4gc2luZCBkaWUgU2thbGVuYmVyZWljaGUgZ2xlaWNoLiBJbiB2aWVsZW4gRsOkbGxlbiBiaWV0ZXQgc2ljaApzdGF0dGRlc3NlbiBqZWRvY2ggZWhlciBlaW4gR2l0dGVyIG1pdCBgZmFjZXRzYCBhbi4KCiMjIyBUaGVtZXMKCkxldHp0ZW5kbGljaCBrw7ZubmVuIGF1Y2ggdmVyc2NoaWVkZW5lIFRoZW1lcyBhdXNnZXfDpGhsdCB3ZXJkZW4uIERhcwpTdGFuZGFyZC1UaGVtZSBpc3QgYHRoZW1lX2J3KClgLiBXZWl0ZXJlIFRoZW1lcyBzaW5kIGluIGRlbSBQYWtldApgZ2d0aGVtZXNgIGVudGhhbHRlbjoKCmBgYHtyfQpnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsIGh3eSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNsYXNzKSkgKwogIGdlb21fc21vb3RoKHNlID0gRkFMU0UpICsKICB0aGVtZV9idygpCmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gY2xhc3MpKSArCiAgZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSkgKwogIHRoZW1lX2RhcmsoKQpnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsIGh3eSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNsYXNzKSkgKwogIGdlb21fc21vb3RoKHNlID0gRkFMU0UpICsKICB0aGVtZV9ncmF5KCkKYGBgCgpadXIgVmVyw6RuZGVydW5nIHdlaXRlcmVyIEVsZW1lbnRlIGRlciBHcmFmaWsgbGVzZW4gU2llIGFtIGJlc3RlbiBpbiBkZW0KW2dncGxvdDIgQnVjaF0oaHR0cHM6Ly9nZ3Bsb3QyLWJvb2sub3JnLykgbmFjaC4KClVudGVyIGFuZGVyZW0gbGFzc2VuIHNpY2ggYXVjaCBlaWdlbmUgVGhlbWVzIGVyc3RlbGxlbiB1bmQgdmllbGVzIG1laHIuCgojIyMgR3JhZmlrZW4gc3BlaWNoZXJuCgpVbSBHcmFmaWtlbiB6dSBzcGVpY2hlcm4gbMOkc3N0IHNpY2ggYmVpc3BpZWxzd2Vpc2UgYXVmIGRlbiBFeHBvcnQtS25vcGYKaW4gZGVyIEdyYWZpa2FuemVpZ2UgcmVjaHRzIGtsaWNrZW4uCgpFYmVuZmFsbHMgZ2lidCBlcyBkaWUgYGdnc2F2ZWAgRnVua3Rpb24sIHVtIGRlbiBsZXR6dGVuIFBsb3Qgb2RlciBlaW4KZ2VvbS1PYmpla3QgenUgc3BlaWNoZXJuOgoKYGBge3J9CmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKyBnZW9tX3BvaW50KCkKYGBgCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpnZ3NhdmUoIm15LXBsb3QucGRmIikKIyBvZGVyOgpwbG90IDwtIGdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKyBnZW9tX3BvaW50KCkKZ2dzYXZlKCJteS1wbG90Mi5wbmciLCBwbG90KQpgYGAKCiMgQXVmZ2FiZW4KCiMjIMOcYnVuZ3NhdWZnYWJlbgoKMS4gIEVyc3RlbGxlbiBTaWUgZWluZSBiZWxpZWJpZ2UgR3JhZmlrIGRlcyBgZGlhbW9uZHNgIERhdGVuc2F0emVzIHVuZAogICAgZsO8Z2VuIFNpZSBkZXIgR3JhZmlrIGVpbmVuIMOcYmVyc2NocmlmdCAoYHRpdGxlKWAsIGVpbmVuIFVudGVydGl0ZWwKICAgIChgc3VidGl0bGVgKSwgZWluZSAiVW50ZXJzY2hyaWZ0IiAoYGNhcHRpb25gKSB1bmQgQmVzY2hyaWZ0dW5nZW4gZGVyCiAgICB4LXVuZCB5LUFjaHNlIChgeGAgdW5kIGB5YCkgc293aWUgZWluIEZhcmJsYWJlbCBiZWkuCgoyLiAgVmVyd2VuZGVuIFNpZSBgZ2VvbV90ZXh0KClgLCB1bSBUZXh0IGluIGplZGUgRWNrZSBkaWVzZXIgR3JhZmlrIHp1CiAgICB6ZWljaG5lbi4KCjMuICBTY2hhdWVuIFNpZSBpbiBkaWUgQmVpc3BpZWxlIGRlciBgP2Fubm90YXRlKClgIEZ1bmt0aW9uIHVuZCBmw7xnZW4KICAgIFNpZSBtaXQgSGlsZmUgZGllc2VyIFRleHQgaW4gZWluZSBHcmFmaWsgZWluIG9obmUgdm9yaGVyIGVpbmVuCiAgICB6dXPDpHR6bGljaGVuIGB0aWJibGVgIHp1IGVyc3RlbGxlbi4KCjQuICBFcnN0ZWxsZW4gU2llIGVpbiBTdHJldWRpYWdyYW1tIHZvbiBgZGlzcGxgIHVuZCBgaHd5YCBkZXMgYG1wZ2AKICAgIERhdGVuc2F0emVzLgoKICAgIDEuICBGaWx0ZXJuIFNpZSB6dXPDpHR6bGljaCBmw7xyIGplZGVuIEF1dG90eXAgYGNsYXNzYCBuYWNoIGRlbQogICAgICAgIGVmZml6aWVudGVzdGVuIEF1dG8uCgogICAgMi4gIE1hcmtpZXJlbiBTaWUgZGllc2UgQXV0b3MgZmFyYmxpY2ggYW5kZXJzIGFscyBkZW4gUmVzdC4KCiAgICAzLiAgRsO8Z2VuIFNpZSBtaXQgYGdlb21fc2VnbWVudGAgdW5kIGBhcnJvdygpYCBQZmVpbGUgaW4gZGllIEdyYWZpawogICAgICAgIGVpbiwgZGllIGF1ZiBkaWUgUHVua3RlIHplaWdlbi4KCiAgICA0LiAgRsO8Z2VuIFNpZSB6dXPDpHR6bGljaCBMYWJlbCBhbiBkYXMgRW5kZSBkZXIgUGZlaWxlIGVpbiwgZGllIGRhcwogICAgICAgIEF1dG9tb2RlbGwgYmVzY2hyZWliZW4uCgogICAgNS4gIEbDvGdlbiBTaWUgc2NobHVzc2VuZGxpY2ggZGVyIEdyYWZpayBhdXNzYWdla3LDpGZ0aWdlCiAgICAgICAgQmVzY2hyaWZ0dW5nZW4sIHVzdy4gYmVpLgoKNS4gIFdhcnVtIGZ1bmt0aW9uaWVydCBkZXIgZm9sZ2VuZGUgQmVmZWhsIG5pY2h0PwoKYGBge3J9CmRmIDwtIHRpYmJsZSgKICB4ID0gcm5vcm0oMTAwMDApLAogIHkgPSBybm9ybSgxMDAwMCkKKQpnZ3Bsb3QoZGYsIGFlcyh4LCB5KSkgKwogIGdlb21faGV4KCkgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsIGhpZ2ggPSAicmVkIikgKwogIGNvb3JkX2ZpeGVkKCkKYGBgCgo2XC4gw5xiZXJzY2hyZWliZW4gU2llIGRpZSBBZXN0aGV0aWNzIGluIGRlbSBmb2xnZW5kZW4gQmVmZWhsLCB1bSBkaWUKTGVnZW5kZSBhbnNjaGF1bGljaGVyIHp1IG1hY2hlbi4KCmBgYHtyfQpnZ3Bsb3QoZGlhbW9uZHMsIGFlcyhjYXJhdCwgcHJpY2UpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gY3V0KSwgYWxwaGEgPSAxLzIwKQpgYGAKCjcuICBaZWljaG5lbiBTaWUgZWluIFN0cmV1ZGlhZ3JhbW0gdm9uIGBTZXBhbC5XaWR0aGAgdW5kIGBTZXBhbC5MZW5ndGhgCiAgICBkZXMgYGlyaXNgIERhdGVuc2F0emVzLgogICAgMS4gIEbDpHJiZW4gU2llIGRpZSBQdW5rdGUgZ2Vtw6TDnyBkZXIgU3BlemllcyBlaW4uCgogICAgMi4gIEbDvGdlbiBTaWUgTGluaWVuIGbDvHIgamVkZSBTcGV6aWVzIGhpbnp1IHVuZCDDpG5kZXJuIFNpZSBkaWUKICAgICAgICBgbGluZXR5cGVgLgoKICAgIDMuICBNYXJraWVyZW4gU2llIGRpZSBrbGVpbnN0ZSB1bmQgZ3LDtsOfdGUgUGZsYW56ZSBqZWRlciBTcGV6aWVzIG1pdAogICAgICAgIFBmZWlsZW4gdW5kIExhYmVsLgoKICAgIDQuICBCZXNjaHJpZnRlbiBTaWUgZGllIExlZ2VuZGUsIEFjaHNlbiB1bmQgVGl0ZWwgc2lubnZvbGwuCg==