1 Datenauswertung

In den vorherigen Vorlesungen und Übungen bestand das Ziel darin, Datenvisualisierung, Datentransformation und Kommunikation zu erlernen. In den kommenden Vorlesungen steht nun die Auswertung der Daten im Fokus.

1.1 Herangehensweise/Hilfestellungen

  1. Zunächst sollten Sie sich einen Überblick über Ihre Daten verschaffen. Stellen Sie sich dabei stets die folgenden Fragen:
    • Wie viele Variablen und Beobachtungen wurden gemessen?
    • Welche Datentypen liegen bei jeder Variablen vor? Qualitative (Kategorien) oder quantitative Daten?
    • Was ist die zugrundeliegende Fragestellung? Welches Ergebnis soll “abgeleitet” werden?
  2. Betrachten Sie die einzelnen Variablen. Was beschreiben/bedeuten Sie im Zusammenhang zur Datenerhebung?
    1. Wie sind quantitative Variablen verteilt (z. B. Histogramme, Boxplots)?
      • Liegen Ausreißer vor? Warum?
      • Liegen fehlende Werte vor? Warum?
      • Warum sind die Variablen so verteilt?
    2. Wie viele Beobachtungen liegen pro Kategorie bei qualitativen Variablen vor?
      • Liegen die Kategorien (größtenteils) im selben Ausmaß vor?
      • Liegen Kategorien sehr viel häufiger/seltener vor als andere? Warum?
        • Kann einen direkten Einfluss auf statistische Auswertungen haben
    3. Betrachten Sie hierfür auch statistische Maßzahlen wie z. B. Mittelwerte oder Standardabweichungen, um Ihre Daten zu beschreiben.
  3. Liegen Zusammenhänge vor?
    • Quantitative Variablen nach Kategorien aufteilen (z. B. durch Gitter/Farbhervorhebung in Grafiken)
      • Unterscheiden sich die unterschiedlichen Kategorien bezüglich der quantitativen Variablen? Warum?
    • Zusammenhänge von zwei (oder mehr) quantitativen Variablen
      • Gibt es einen steigenden/fallenden Trend oder keinen Zusammenhang? Warum?
        • Streudiagramm (ggf. pro Kategorie)
        • Korrelationskoeffizient
    • Zusammenhänge von qualitativen Variablen
      • Liegen manche Kategorien gemeinsam häufiger/seltener vor als andere? Warum?
        • Häufigkeitstabelle/Häufigkeitsgrafik
        • Gruppierte Balkendiagramme
  4. Suchen Sie unter den vorherigen Punkten untypische Daten/Aspekte und interpretieren Sie diese.
  5. Finden Sie für Ihre Fragestellungen die passende Auswertungstechnik:
    • ANOVA bei dem Einfluss von qualitativen Variablen auf quantitative Variablen
    • Regression bei dem Einfluss von quantitativen Variablen auf eine andere quantitative Variable

In manchen Fällen liegen auch interessante Beobachtungen vor, die zunächst nichts mit der ursprünglichen Fragestellung zutun haben. Es kann sich lohnen, nicht nur zielstrebig das gewünschte Ergebnis auszuwerten, sondern auch beiläufige Variablen/Aspekte zu betrachten. Es gibt natürlich noch weitere statistische Auswertungsmöglichkeiten. Allerdings lassen sich die meisten Experimente anhand von Varianzanalyse (ANOVA) oder Regression auswerten und sind daher in dem Rahmen des Kurses am sinnvollsten.

1.2 Kontinuierliche Daten

Bei der Datenanalyse-und auswertung wird zwischen qualitativen und quantitativen Daten unterschieden. Diese Unterscheidung ist essentiell bei der Entscheidung wie mit den Daten umgegangen werden kann. In dem Rahmen des Kurses haben Sie bereits einige Grafikdarstellungen kennengelernt und nun sollen diese verwendet werden, um Daten und die zugrundeliegenden Resultate auszuwerten.

1.2.1 Verteilung und Interpretation

Betrachten wir einen Datensatz über Kirschbäume und dessen Maße:

library(tidyverse)
trees <- as_tibble(trees)
trees

Der Datensatz beschreibt die Höhe, den Durchmesser und das Volumen von 31 gefällten Kirschbäumen.

Wir erhalten also bereits durch das Einsehen der ersten Spalten, worum es sich um unseren Datensatz handelt und wie viele Beobachtungen und Variablen es gibt. Ebenfalls sehen wir, dass alle Variablen quantitativ sind, da sie kontinuierliche Messwerte beschreiben.

Durch den Befehl ?trees sehen wir auch, dass der Durchmesser in Zoll, die Höhe in Fuß und das Volumen in Kubikfuß angegeben sind. Bereiten wir den Datensatz also zunächst so auf, dass ein Nichtamerikaner die (metrischen) Einheiten lesen kann:

trees <- trees %>%
    mutate(Girth = Girth * 2.54,
                 Height = Height * 30.48 / 100,
                 Volume = Volume * 35.315)
trees

Nun liegt der Durchmeser in Zentimeter, die Höhe in Meter und das Volumen in Kubikmeter vor.

Schauen wir uns nun die üblichen statistischen Maßzahlen

summary(trees)
     Girth           Height          Volume      
 Min.   :21.08   Min.   :19.20   Min.   : 360.2  
 1st Qu.:28.07   1st Qu.:21.95   1st Qu.: 685.1  
 Median :32.77   Median :23.16   Median : 854.6  
 Mean   :33.65   Mean   :23.16   Mean   :1065.5  
 3rd Qu.:38.73   3rd Qu.:24.38   3rd Qu.:1317.2  
 Max.   :52.32   Max.   :26.52   Max.   :2719.3  

und die Verteilungen der Variablen an

library(patchwork)
histo <- ggplot(trees) +
    geom_histogram(aes(x = Girth), binwidth = 3, fill = "steelblue", color = "black") +
    labs(x = NULL)
boxpl <- ggplot(trees) +
    geom_boxplot(aes(x = Girth), fill = "steelblue", color = "black") + 
    labs(x = "Durchmesser (cm)") +
    scale_y_continuous(breaks = NULL)
histo + boxpl + plot_layout(1, 2)

histo <- ggplot(trees) +
    geom_histogram(aes(x = Height), binwidth = 1, fill = "steelblue", color = "black") +
    labs(x = NULL)
boxpl <- ggplot(trees) +
    geom_boxplot(aes(x = Height), fill = "steelblue", color = "black") + 
    labs(x = "Höhe (m)") +
    scale_y_continuous(breaks = NULL)
histo + boxpl + plot_layout(1, 2) # funktioniert nur mit der patchwork Bibliothek

Hinweis: Bei der Klassenbreite (oder Anzahl der Klassen) sollten ein paar verschiedene Parameter ausprobiert werden (bins oder binwidth), um zu sehen, welcher Wert am sinnvollsten ist.

  • Je nach Fragestellung kann die generelle Information über die Höhe und den Durchmesser von Kirschbäumen bereits interessant sein, beispielsweise wenn man herausfinden will, was man erwarten kann wie hoch ein Kirschbaum (einer bestimmten Gattung) wächst.
  • Ohne jegliche weitere Information über die Kirschbäume zu haben, sehen wir bereits, dass diese Bäume sehr groß zu sein scheinen (Mittelwert 23,16m) und wohl nicht in dem Garten eines Kleingärtners stehen würden.
  • Die Höhe scheint normalverteilt zu sein (Histogramm ähnelt Glockenkurve)
  • Die Verteilung des Durchmessers ist nur schwer zu deuten
  • Beide Variablen weisen scheinbar keine Ausreißer auf

Es gibt zusätzlich oder ergänzend zum Histogramm noch die Möglichkeit die Verteilung durch Linien zu verdeutlichen. Für die absolute Häufigkeit lässt sich mit geom_freqpoly eine grobe Linie einzeichnen:

ggplot(trees, aes(x = Girth)) +
    geom_histogram(binwidth = 5, fill = "steelblue") +
    geom_freqpoly(binwidth = 5) +
    scale_y_continuous(breaks = seq(0, 10, 2)) # sonst stehen ungerade Werte auf der y-Achse

  • bins oder binwidth muss bei beiden Funktionen gleich sein
  • Wir sehen eine etwas verdeutlichte Auf-und Abbewegung
  • Je nach Klassenbreite ist dieser Trend weniger gut einsichtig

Um eine glattere Kurve zu betrachten, werden häufig Dichtelinien in ein Dichtehistogramm eingezeichnet. Bei dem Dichtehistogramm stehen hierbei die relativen Häufigkeiten geteilt durch Klassenbreite auf der y-Achse. Dichte lässt sich durch ..density.. als y-Wert in die Aesthetics einsetzen. Anstelle von geom_freqpoly verwenden wir für die Linie nun geom_density

ggplot(trees, aes(x = Girth, y = ..density..)) +
    geom_histogram(bins = 8, fill = "steelblue", color = "black") +
    geom_density(alpha = 0.25, fill = "grey", color = "grey")

  • wohlmöglich sind die Durchmesser doch normalverteilt (oder Gamma oder Chi-Quadrat …)?

1.2.2 Daten auf Normalverteilung testen

Unsere Intuition mag uns in manchen Fällen helfen abzuschätzen, welcher Verteilung unsere Daten folgen. Um sicher zu gehen, gibt es hierfür statistische Tests. Bei der Normalverteilung wird beispielsweise der Shapiro-Wilk-Test verwendet.

Nun stellt sich generell die Frage, warum man überhaupt Verteilungen betrachten sollte. Natürliche Beobachtungen und Prozesse folgen häufig Normalverteilungen, d. h. es gibt einen “Mittelpunkt”, um den sich die Beobachtungen verteilen und extreme Werte weit ober-und unterhalb sind eher unwahrscheinlich und werden selten beobachtet. Normalverteilungen sind für viele statistische Tests eine Voraussetzung. Wenn diese verletzt wird, dann verliert der Test an Aussagekraft.

Zudem kann es interessant sein, andere Verteilungen aufzufinden wie z. B. die bimodale Verteilung und zu erkunden, warum es mehrere “Peaks” gibt:

In R gibt es die Funktion shapiro.test(), um den Shapiro-Wilk-Test für die Normalverteilung durchzuführen:

  • Nullhypothese (H0): Die Daten sind normalverteilt

  • Alternativhypothese (H1): Die Daten sind nicht normalverteilt

  • Der Shapiro-Wilk-Test, wie auch jeder statistische Test, liefert eine Statistik sowie einen p-Wert.

Üblicherweise wird ein Signifikanzniveau ⍺ (= Schwellenwert) gesetzt, mit dem wir entscheiden, welche Hypothese angenommen werden soll. Häufig wird ein Niveau von 0.05 verwendet. Das p steht hierbei für “probability”, d. h. dieser Wert gibt uns an, wie wahrscheinlich es ist, dass ein extremerer Wert als die Teststatistik betrachtet wird.

In der Praxis bedeutet das, dass wir aus 20 durchgeführten Tests erwarten, dass ein Test ein falsches Ergebnis liefert, wenn wir ein Signifikanzniveau von 0.05 auswählen. Strengere Schwellenwerte führen also dazu, dass wir mehr Sicherheit haben, dass das Ergebnis tatsächlich wahr ist. Jedoch verlieren wir bei sehr strengen Schwellenwerte (z. B. 0.00001) wohlmöglich wahre Ergebnisse. Daher sind p-Werte ein sehr umstrittenes Thema.

Achten Sie immer darauf:

  • Gilt p >= ⍺ => wir lehnen H0 nicht ab

  • Gilt p < ⍺ => wir lehnen H0 ab und nehmen H1 an

Führen wir den Shapiro-Wilk-Test nun durch und wählen ⍺ = 0.05:

# Spalte aus Dataframe entnehmen:
trees$Girth
 [1] 21.082 21.844 22.352 26.670 27.178 27.432 27.940 27.940 28.194 28.448 28.702 28.956 28.956 29.718 30.480 32.766 32.766 33.782 34.798 35.052 35.560 36.068 36.830 40.640
[25] 41.402 43.942 44.450 45.466 45.720 45.720 52.324
# Shapiro-Test mit dieser Spalte:
shapiro.test(trees$Girth)

    Shapiro-Wilk normality test

data:  trees$Girth
W = 0.94117, p-value = 0.08893

Der Test nimmt einen Vektor (Variable) entgegen, d. h. wir müssen aus trees die Variable Girth mit dem $-Operator entnehmen.

  • Es gilt (p-Wert =) 0.089 > 0.05 (= ⍺) => H0 wird nicht abgelehnt und wir nehmen an, dass die Daten normalverteilt sind.

  • Dasselbe könnten wir nun für die anderen Variablen durchführen

Um das Testen mit tidyverse etwas zu vereinfachen, wollen wir das Paket rstatix verwenden:

install.packages("rstatix")

Mit rstatix können wir Tests in R durchführen und gleichzeitig weiter mit Dataframes arbeiten. Für den Shapiro-Wilk-Test gibt es hier die Funktion shapiro_test(), dem wir einfach die Variablen übergeben können:

library(rstatix)

Attache Paket: ‘rstatix’

Das folgende Objekt ist maskiert ‘package:stats’:

    filter
trees %>%
    shapiro_test(Girth, Height, Volume)

Der Vorteil hierbei ist, dass wir weiter mit Dataframes arbeiten, d. h. wir können die Daten auch direkt filtern (z. B. nach ⍺):

nicht_sig <- trees %>% 
    shapiro_test(Girth, Height, Volume) %>%
    filter(p >= 0.05)
nicht_sig
  • Wir sehen direkt, dass Durchmesser und Höhe normalverteilt sind und Volumen scheinbar nicht.

  • Insbesondere lässt sich der Prozess durch beispielsweise Gruppierung erweitern (dazu später mehr).

1.2.3 Zusammenhänge

Um Zusammenhänge von kontinuierlichen Variablen zu betrachten, werden Streudiagramme oder ähnliche Grafiken verwendet.
Falls verschiedene Kategorien/Gruppen vorhanden sind, kann dies ebenfalls durch Farbhervorhebung oder durch Gitter ergänzt werden.

Bei unserem Datensatz über Kirschbäume ist intuitiv klar, wie sich Durchmesser und Höhe verhalten. Oder?

ggplot(trees) +
    geom_point(aes(Girth, Height)) +
    labs(x = "Durchmesser (cm)",
             y = "Höhe (m)", 
             title = "Kirschbäume mit höherem Durchmesser sind tendenziell größer",
             caption = "Daten aus dem Buch 'The Minitab Student Handbook'")

  • Die Tendenz ist eindeutig: steigt der Durchmesser, steigt ebenfalls auch die Höhe.
  • Diese Aussage mag nicht für jeden einzelnen Punkt zutreffen. Wichtig ist hierbei, dass eine Tendenz zu sehen ist.
  • Dieser Zusammenhang könnte linear sein: legen wir eine passende Gerade durch die Punkte, werden die meisten Punkte mit wenig Abweichung gut getroffen.

Zuvor hatten wir durch Streudiagramme mit geom_smooth eine geglättete Linie gelegt:

ggplot(trees, aes(Girth, Height)) +
    geom_point() +
    geom_smooth() +
    labs(x = "Durchmesser (cm)",
             y = "Höhe (m)", 
             title = "Kirschbäume mit höherem Durchmesser sind tendenziell größer",
             caption = "Daten aus dem Buch 'The Minitab Student Handbook'")

  • Im Hintergrund von geom_smooth wird ein Modell erstellt, welches die Datenpunkte durch diese geglättete Linie annähert.
  • Je nach Methode (Modellart) können das glatte komplexe Polynome sein oder (einfache) Geraden.
  • Hierzu muss der method Parameter in geom_smooth angepasst werden (?geom_smooth).

Ein Gerade lässt sich beispielsweise durch "lm" einzeichnen:

ggplot(trees, aes(Girth, Height)) +
    geom_point() +
    geom_smooth(method = "lm", color = "red") +
    labs(x = "Durchmesser (cm)",
             y = "Höhe (m)", 
             title = "Kirschbäume mit höherem Durchmesser sind tendenziell größer",
             caption = "Daten aus dem Buch 'The Minitab Student Handbook'")

  • Die Gerade scheint nicht (viel) schlechter zu sein als die Kurve von oben, aber scheint den Zusammenhang vielleicht nicht perfekt darzustellen
  • Das ist ein typisches Phänomen. Später sehen wir, wie die Güte von diesem Modell zu bewerten ist.

1.2.3.1 Korrelationsmatrix & Korrelationsgrafik

Zusammenhänge sind die Tendenz, dass das Steigen oder Fallen einer Variable ebenfalls mit dem Steigen oder Fallen einer anderen Variable einhergeht. Um die Stärke eines Zusammenhangs zu bestimmen, wird ein Korrelationskoffizient verwendet:

  • Liegt zwischen -1 (perfekt negativ) und +1 (perfekt positiv)

  • Ein Wert von 0 deutet darauf, dass die Variablen keinen Zusammenhang aufweisen

  • Es gibt verschiedene Korrelationskoeffiziente (Pearson, Spearman, Kendall)

    • Pearson: falls die Variablen (relativ) linear zusammenhängen und normalverteilt sind

    • Ansonsten: Kendall (ordinale Daten) oder Spearman verwenden

Wir hatten oben bereits getestet, dass Durchmesser und Höhe normalverteilt sind. Ebenfalls könnte ein linearer Zusammenhang möglich sein. Daher verwenden wir Pearsons Korrelationskoeffizient. Hierzu wird standardmäßig die cor() Funktion verwendet:

cor(trees$Girth, trees$Height)
[1] 0.5192801
  • Der Korrelationskoeffizient weist eine Wert von 0,52 auf und deutet somit einen moderaten Zusammenhang an. Das entspricht dem, was vor zuvor betrachtet haben.

  • Die Punkte liegen nicht exakt auf einer Geraden, sondern weichen etwas ab. Es liegt also ein tatsächlicher Zusammenhang vor.

  • Die Höhe von einem Kirschbaum hängt scheinbar nicht ausschließlich von dem Durchmesser ab, denn sonst wäre der Zusammenhang perfekt.

Hinweis: den Koeffizienten können wir mit method = X ändern:

cor(trees$Girth, trees$Height, method = "pearson") # Default/Standard
[1] 0.5192801
cor(trees$Girth, trees$Height, method = "spearman")
[1] 0.4408387
cor(trees$Girth, trees$Height, method = "kendall")
[1] 0.3168641
  • Die anderen Korrelationskoeffizienten weisen sogar noch etwas geringere Werte auf.

  • Zu jedem Korrelationskoeffizienten gibt es auch einen zugehörigen Test, ob der Koeffizient sich signifikant von 0 unterscheidet.

  • Anders formuliert: der Test gibt an, ob es einen Zusammenhang gibt. Die Testhypothesen lauten für alle Tests:

    • H0: Der Koeffizient ist gleich 0

    • H1: Der Koeffizient ist ungleich 0

Genauso wie cor() lässt sich ergänzend für diesen Test cor.test() verwenden:

cor.test(trees$Height, trees$Girth)

    Pearson's product-moment correlation

data:  trees$Height and trees$Girth
t = 3.2722, df = 29, p-value = 0.002758
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.2021327 0.7378538
sample estimates:
      cor 
0.5192801 
  • Hier sehen Sie ebenfalls den Korrelationskoeffizient ganz unten
  • Der p-Wert des Tests beträgt 0,0028 und ist somit kleiner als 0,05 (= typisches ⍺)
  • Es folgt, dass der Zusammenhang signifikant bzw. vorhanden ist (unterschiedlich von 0)

In einem Bericht könnte man beispielsweise ein Streudiagramm mit der geglätteten Kurve darstellen und zusätzlich das Ergebnis des Korrelationskoeffizienten berichten, sofern dieser Zusammenhang interessant ist.

Es gibt für Korrelationskoeffizienten einen “alles-in-einem” Trick, um schnell Zusammenhänge darzustellen und zu erkennen, insbesondere wenn viele Variablen vorliegen. Hierzu wird eine Matrix mit den p-Werten/Korrelationskoeffizienten für jedes Paar der Variablen erstellt und zusätzlich als Grafik veranschaulicht.

Ebenfalls mit dem cor() Befehl lässt sich eine Matrix für alle numerischen Variablen berechnen. Hierbei muss der Datensatz auf die numerischen Variablen reduziert werden. Nehmen wir als Beispiel den mpg Datensatz. Es gibt hierbei mehrere Möglichkeiten:

# Variablen per Hand auswählen:
numerisch <- mpg %>%
    select(displ, year, cyl, cty, hwy)
numerisch
# Variablen nach Bedingung filtern:
numerisch <- mpg %>%
    select_if(is.numeric)
numerisch
  • Bei manchen Variablen muss man sich überlegen, ob es Sinn ergibt, diese als Kategorie oder Zahlenwert zu betrachten (z. B. das Jahr)

Nun kann durch cor() jeder paarweiser Korrelationskoeffizient berechnet werden:

cor(numerisch)
           displ         year        cyl         cty          hwy
displ  1.0000000  0.147842816  0.9302271 -0.79852397 -0.766020021
year   0.1478428  1.000000000  0.1222453 -0.03723229  0.002157643
cyl    0.9302271  0.122245347  1.0000000 -0.80577141 -0.761912354
cty   -0.7985240 -0.037232291 -0.8057714  1.00000000  0.955915914
hwy   -0.7660200  0.002157643 -0.7619124  0.95591591  1.000000000
  • Die Matrix-Darstellung ist sehr hilfreich, um direkt konkrete Werte zu vergleichen
  • Beispielsweise scheint der Kraftstoffverbrauch in der Stadt (cty) sehr stark mit dem Kraftstoffverbrauch auf der Autobahn (hwy) zu korrelieren, was auch sinnvoll ist
  • Anschaulich ist diese Darstellung jedoch nicht

Bei unserem Kirschbaum-Datensatz war das Filtern hingegen nicht notwendig, da bereits alle Variablen numerisch sind:

cor(trees)
           Girth    Height    Volume
Girth  1.0000000 0.5192801 0.9671194
Height 0.5192801 1.0000000 0.5982497
Volume 0.9671194 0.5982497 1.0000000
  • Wir sehen für jedes Paar an Variablen einen Koeffizienten (als hätten wir für alle cor() separat aufgerufen)
  • Die Diagonale ist immer 1, da eine Variable stets mit sich selbst perfekt korreliert ist

Alternativ können aus dem rstatix Paket die Funktionen cor_test(), cor_mat() und cor_get_pval() verwendet werden, um die Koeffizienten bzw. die p-Werte der zugehörigen Tests darzustellen. Hierbei wird Filterung und Berechnung zusammengefasst in eine Funktion:

# Ausführlicher Korrelationstest:
trees %>%
    cor_test()
# Korrelationsmatrix
trees %>%
    cor_mat()
# p-Werte-Matrix:
trees %>%
    cor_mat() %>%
    cor_get_pval()
# konkrete Benennung:
trees %>%
    cor_mat(Girth, Height, Volume)

Um die Matrix etwas detailierte anzusehen, können wir entweder cor_test() verwenden oder cor_mat() und dann cor_gather:

trees %>%
    cor_mat() %>%
    cor_gather()

Da Grafiken eine schnellere Orientierungshilfe sind, wollen wir diese Matrix direkt darstellen. Hierzu gibt es in rstatix die cor_plot() Funktion:

mpg %>%
    cor_mat(year, displ, cyl, cty, hwy) %>%
    cor_plot()

Es lassen sich direkt einige Dinge ablesen:

  • Eine negative Korrelation wird in diesem Beispiel rot markiert, eine positive Korrelation durch blau

  • Je dunkler der Farbton desto stärker ist die Korrelation

  • Weiß stellt hierbei Korrelationskoeffizienten bei 0 dar

  • In diesem Beispiel werden zusätzlich die Koeffizienten unterschiedlich groß dargestellt (je nach Wert)

  • Sofern ein p-Wert oberhalb von 0.05 liegt, wird dieser ebenfalls durch ein Kreuz markiert (= kein Zusammenhang)

Einige dieser Optionen lassen sich anpassen (?cor_plot()):

korr <- mpg %>%
    cor_mat(year, displ, cyl, cty, hwy)
# Signifikanzlevel:
korr %>%
    cor_plot(significant.level = 0.1, insignificant = "blank")

# Form
korr %>%
    cor_plot(method = "square")

# Dreieck vs Quadrat
korr %>%
    cor_plot(type = "lower")

# Farbe und Label
korr %>%
    cor_plot(label = TRUE, palette = ggpubr::get_palette(c("tomato3", "white", "seagreen"), 200))

# Umsortieren:
korr %>%
    cor_reorder() %>%
    cor_plot()

# Kombination aus allem
korr %>%
    cor_reorder() %>%
    cor_plot(method = "ellipse", significant.level = 0.1, insignificant = "blank", type = "upper",
                     label = TRUE, palette = ggpubr::get_palette(c("darkred", "cornsilk", "darkcyan"), 200)
                     )

Für weitere Optionen bei der Arbeit mit Korrelationsgrafiken bietet sich ebenfalls das ggcorrplot Paket an. Hier lassen sich noch zusätzlich Einstellungen ändern mit dessen ggcorrplot() Funktion. Siehe hier für mehr Information zu ggcorrplot.

1.2.3.2 Regressionsmodelle

Bei der Korrelationsanalyse betrachten wir Zusammenhänge. Zusammenhänge müssen jedoch nicht kausal sein, d. h. es mag sein, dass eine Variable tendenziell mit einer anderen Variable steigt, jedoch nicht durch den Einfluss der anderen Variable.

Ein solcher Sachverhalt kann komplex sein, aber häufig steckt dahinter eine dritte (oder mehrere) Variable, die beide Variablen zugleich beinflusst. An dieser Stelle wollen wir das übliche Beispiel betrachten, um diesen Unterschied zu erläutern:

Mit einer erhöhten Anzahl an Störchen in einem beobachteten Gebiet steigt auch die Geburtenrate (tatsächliches Phänomen):

  • Die Korrelation liegt hierbei vor (signifikant)

  • Kausal ist dieser Zusammenhang jedoch nicht

  • Eine dritte Variable (die Industrialisierung) scheint beide Variablen zu beeinflussen: auf dem Land siedeln sicher eher Familien an und in Stadtgebieten gibt es wenige Störche.

  • Demnach ist der tatsächliche kausale Zusammenhang bei Industrialisierung und Geburtenrate/Anzahl Störche

Kleiner Einschub: betrachten wir einen Beispieldatensatz zu Störchen und der Geburtenrate. Wir sehen, dass die “Storchenrate” und die Geburtenrate stark positiv korreliert ist (und Anzahl von Störchen und Anzahl von Babies):

library(TeachingDemos)
stork <- as_tibble(stork)
stork
stork %>%
    cor_mat() %>%
    cor_plot()

Mit Hilfe der partiellen Korrelation können wir mögliche Störfaktoren aus der Korrelation “entfernen”:

library(ppcor)
Lade nötiges Paket: MASS

Attache Paket: ‘MASS’

Das folgende Objekt ist maskiert ‘package:rstatix’:

    select

Das folgende Objekt ist maskiert ‘package:patchwork’:

    area

Das folgende Objekt ist maskiert ‘package:dplyr’:

    select
stork %>%
    pcor() %>% # partielle Korrelation
    magrittr::extract2(1) %>% # ersten Eintrag entnehmen
    round(digits = 3) %>% # Dezimalstellen runden auf 3
    cor_plot()

  • Anzahl der Störche und Anzahl der Babies sind nun moderat negativ korreliert.

  • Wenn wir die Storchrate und die Geburtenrate betrachten, so liegt hier nur noch eine sehr schwache Korrelation vor.

Kehren wir zurück zur Regression.

Wichtig bei der Regressionsanalyse ist, dass wir einen kausalen Zusammenhang unterstellen. Es sollte, anders als bei dem Storchenbeispiel, ein echter kausaler Zusammenhang vorliegen.
Bei dem Datensatz der Kirschbäume kann es gut sein, dass der Durchmesser des Baums eine wichtige Rolle spielt, sodass der Baum höher wachsen kann.

In R werden lineare Regressionsmodelle typischerweise mit der lm() (= “Lineares Modell”) Funktion durchgeführt. Im einfachsten Fall, so wie wir es bereits mehrfach gesehen haben, wird hiermit eine “passende” Gerade durch unsere Punkte gezogen. Erstellen wir ein solches Modell für den Durchmesser und die Höhe der Kirschbäume:

lin_modell <- lm(Height ~ Girth, data = trees)
summary(lin_modell)

Call:
lm(formula = Height ~ Girth, data = trees)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.8349 -0.8439  0.0964  0.7537  3.0314 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 18.90714    1.33603  14.152 1.49e-14 ***
Girth        0.12652    0.03867   3.272  0.00276 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.688 on 29 degrees of freedom
Multiple R-squared:  0.2697,    Adjusted R-squared:  0.2445 
F-statistic: 10.71 on 1 and 29 DF,  p-value: 0.002758
  • Hier sehen wir erneut die Verwendung von dem Tilde “~” Symbol
  • Links von der Tilde steht die abhängige Variable, rechts von der Tilde steht die unabhängige Variable
  • Der Effekt der unabhängigen Variablen wird auf die abhängige Variable betrachtet, z. B. welchen Einfluss der Durchmesser auf die Baumhöhe hat
  • Der p-Wert ganz unten gibt an, ob das Modell signifikant ist, d. h. ob der Durchmesser einen Einfluss auf die Baumhöhe hat:
    • H0: Die unabhängigen Variablen haben keinen Einfluss

    • H1: Es gibt mindestens eine unabhängige Variable mit einem Einfluss

  • Da der p-Wert relativ klein ist, gehen wir davon aus, dass der Durchmesser einen signifikanten Einfluss auf die Baumhöhe hat
  • Multiple R-squared (R-Quadrat) gibt die Güte des Modells an:
    • Werte zwischen 0 (schlecht) und 1 (perfekt)
    • “Wie viel Varianz kann das Modell beschreiben” = “Wie viel weichen die Werte von der Modellkurve/Gerade ab?”
    • Da R-Quadrat = 0.27 ist, sehen/schlussfolgern wir erneut, dass der Durchmesser Einfluss auf die Baumhöhe hat, aber wohlmöglich nicht der einzige Faktor ist, der einen Einfluss hat (denn sonst wäre R-Quadrat sehr nahe bei 1)
  • Unter Coefficients sehen wir die Modellgerade: Height = 18,9 + 0.127 * Girth

Wollen wir die Modellgerade in eine Grafik einzeichnen, gibt es mehrere Möglichkeiten. Wir können predict() auf das Modell anwenden, um die Vorhersagen, d. h. y-Werte, des Modells zu erhalten und diese als Linien einzeichnen. Alternativ können wir einfach das Modell in geom_smooth() erstellen lassen:

# Die Vorhersagen des Modells verwenden:
ggplot(trees, aes(Girth, Height)) +
    geom_point() +
    geom_line(aes(y = predict(lin_modell)), color = "red")

# geom_smooth verwenden:
ggplot(trees, aes(Girth, Height)) +
    geom_point() +
    geom_smooth(method = "lm", formula = y ~ x, color = "red", se = FALSE)

Betrachten wir abschließend noch den Durchmesser, die Höhe und das Volumen.

ggplot(trees, aes(x = Girth, y = Volume)) +
    geom_point() +
    geom_smooth()

ggplot(trees, aes(x = Height, y = Volume)) +
    geom_point() +
    geom_smooth()

  • Wenn wir Durchmesser und Volumen betrachten, könnte der Zusammenhang wohlmöglich (leicht) quadratisch (Kurve) sein.

  • Bei Höhe und Volumen ist das nicht ganz eindeutig. Hier könnte es sich aber vermutlich um einen linearen Zusammenhang handeln.

Es können auch alle drei kontinuierlichen Variablen gleichzeitig betrachtet werden. Hierzu werden Gitter erstellt, wobei eine Variable in Intervalle mit einer cut_*() Funktion aufgeteilt wird. Um beispielsweise den Zusammenhang von Volumen, Durchmesser und Höhe zu betrachten, wird hier ein normales Streudiagramm mit Durchmesser und Volumen erstellt, wobei Gitter basierend auf den Intervallen der Höhe eingeteilt werden:

ggplot(trees, aes(x = Girth, y = Volume)) +
    geom_point() +
    geom_smooth(method = "lm", se = FALSE) +
    facet_wrap(~ cut_width(Height, 3))

Abschließend noch ein Ausblick, was sich mit Regressionsmodellen noch machen lässt: Ebenfalls können wir zu Volumen und Höhe/Durchmesser Regressionsmodelle erstellen:

lin_modell2 <- lm(Volume ~ Girth, data = trees)
summary(lin_modell2)

Call:
lm(formula = Volume ~ Girth, data = trees)

Residuals:
    Min      1Q  Median      3Q     Max 
-284.83 -109.71    5.37  123.42  338.56 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) -1304.658    118.840  -10.98 7.62e-12 ***
Girth          70.433      3.439   20.48  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 150.2 on 29 degrees of freedom
Multiple R-squared:  0.9353,    Adjusted R-squared:  0.9331 
F-statistic: 419.4 on 1 and 29 DF,  p-value: < 2.2e-16
lin_modell3 <- lm(Volume ~ Height, data = trees)
summary(lin_modell3)

Call:
lm(formula = Volume ~ Height, data = trees)

Residuals:
   Min     1Q Median     3Q    Max 
-751.3 -349.4 -102.2  426.2 1054.2 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) -3076.77    1033.78  -2.976 0.005835 ** 
Height        178.82      44.48   4.021 0.000378 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 473.1 on 29 degrees of freedom
Multiple R-squared:  0.3579,    Adjusted R-squared:  0.3358 
F-statistic: 16.16 on 1 and 29 DF,  p-value: 0.0003784

Der Durchmesser scheint relativ gut zu sein, um das Volumen zu bestimmen (R-Quadrat = 0.935). Allerdings wissen wir auch, dass sich das Volumen im Allgemeinen (von zylinder-artigen Objekten) berechnen lässt durch die Formel:

pi * Radius^2 * Höhe

Hierzu muss für den Durchmesser I(X ** 2) geschrieben werden, damit der Durchmesser auch quadriert in die Formel eingeht. Statt der Multiplikation * (~ Addition und Multiplikation) wird in Modellen : geschrieben.

Betrachten wir zunächst ein Modell mit dem Durchmesser zum Quadrat:

lin_modell4 <- lm(Volume ~ I(Girth ** 2), data = trees)
summary(lin_modell4)

Call:
lm(formula = Volume ~ I(Girth^2), data = trees)

Residuals:
     Min       1Q   Median       3Q      Max 
-220.630  -90.894    9.025   75.367  247.420 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) -118.4868    50.0379  -2.368   0.0248 *  
I(Girth^2)     0.9917     0.0379  26.169   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 119 on 29 degrees of freedom
Multiple R-squared:  0.9594,    Adjusted R-squared:  0.958 
F-statistic: 684.8 on 1 and 29 DF,  p-value: < 2.2e-16
ggplot(trees, aes(Girth, Volume)) +
    geom_point() +
    geom_smooth(formula = y ~ I(x ** 2), method = "lm")

Tatsächlich scheint eine quadratische Kurve gut zu passen. Darauf basierend können wir unser Modell weiter verbessern. Hierzu multiplizieren wir die Höhe mit dem Durchmesser im Quadrat:

lin_modell5 <- lm(Volume ~ Height:I(Girth ** 2), data = trees)
summary(lin_modell5)

Call:
lm(formula = Volume ~ Height:I(Girth^2), data = trees)

Residuals:
     Min       1Q   Median       3Q      Max 
-163.136  -38.853   -5.848   61.629  148.237 

Coefficients:
                    Estimate Std. Error t value Pr(>|t|)    
(Intercept)       -10.512549  34.027959  -0.309     0.76    
Height:I(Girth^2)   0.038151   0.001068  35.711   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 88.04 on 29 degrees of freedom
Multiple R-squared:  0.9778,    Adjusted R-squared:  0.977 
F-statistic:  1275 on 1 and 29 DF,  p-value: < 2.2e-16

Mit einem R-Quadrat von 0.978 ist das Modell sogar noch etwas besser als zuvor und scheint nun beinahe perfekt das Volumen der Kirschbäume anhand von Durchmesser und Höhe zu bestimmen, auch wenn der Zusammenhang bereits bekannt war.

Was könnte der Grund sein, dass typischerweise (trotz eindeutiger funktionaler Beziehung) kein perfekter R-Quadrat von 1 erhalten werden kann?

2 Aufgaben

In dieser Übung wollen wir uns mit kontinuierlichen Daten beschäftigen. Hierzu betrachten wir den Datensatz airquality, welcher verschiedene Messungen bezüglich Luftqualität zwischen Mai und September im Jahre 1973 umfasst.
Für mehr Information siehe ?airquality.

library(tidyverse)
airquality <- as_tibble(airquality)
# Faktoren aus Monat und Tag machen:
airquality <- airquality %>%
  mutate_at(c("Month", "Day"), factor)
# Alternativ:
# airquality %>%
#   mutate(Month = factor(Month), Day = factor(Day))
airquality

2.1 Übungsaufgaben

  1. Schaffen Sie sich zunächst eine (statistische) Übersicht über die Daten. Wie viele Beobachtungen und Variablen wurden erfasst?
  2. Versichern Sie sich, dass die Daten einigermaßen kontinuierlich innerhalb des Zeitraums erfasst wurden.
  3. Veranschaulichen Sie die Verteilungen der kontinuierlichen Variablen von airquality. Welche Aspekte fallen ins Auge und könnten interessant sein?
  4. Finden Sie Zusammenhänge zwischen den verschiedenen Variablen.
    1. Visualisieren Sie für alle sinnvollen Variablen die paarweisen Beziehungen. Sind diese alle linear?
    2. Berechnen Sie für alle sinnvollen Variablen eine Korrelationsmatrix. Bietet sich Pearsons Korrelationskoeffizient an?
    3. Bestimmen Sie durch Visualisierung, wo Zusammenhänge vorliegen.
  5. Erstellen Sie für relevante (kausale) Zusammenhänge Regressionsmodelle.
LS0tCnRpdGxlOiAiU3RhdGlzdGlrIHVuZCBEYXRhIFNjaWVuY2UgLSBLb3JyZWxhdGlvbiAmIFJlZ3Jlc3Npb24iCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwotLS0KCiMgRGF0ZW5hdXN3ZXJ0dW5nCgpJbiBkZW4gdm9yaGVyaWdlbiBWb3JsZXN1bmdlbiB1bmQgw5xidW5nZW4gYmVzdGFuZCBkYXMgWmllbCBkYXJpbiwKRGF0ZW52aXN1YWxpc2llcnVuZywgRGF0ZW50cmFuc2Zvcm1hdGlvbiB1bmQgS29tbXVuaWthdGlvbiB6dSBlcmxlcm5lbi4KSW4gZGVuIGtvbW1lbmRlbiBWb3JsZXN1bmdlbiBzdGVodCBudW4gZGllIEF1c3dlcnR1bmcgZGVyIERhdGVuIGltCkZva3VzLgoKIyMgSGVyYW5nZWhlbnN3ZWlzZS9IaWxmZXN0ZWxsdW5nZW4KCjEuICBadW7DpGNoc3Qgc29sbHRlbiBTaWUgc2ljaCBlaW5lbiDDnGJlcmJsaWNrIMO8YmVyIElocmUgRGF0ZW4KICAgIHZlcnNjaGFmZmVuLiBTdGVsbGVuIFNpZSBzaWNoIGRhYmVpIHN0ZXRzIGRpZSBmb2xnZW5kZW4gRnJhZ2VuOgogICAgLSAgIFdpZSB2aWVsZSBWYXJpYWJsZW4gdW5kIEJlb2JhY2h0dW5nZW4gd3VyZGVuIGdlbWVzc2VuPwogICAgLSAgIFdlbGNoZSBEYXRlbnR5cGVuIGxpZWdlbiBiZWkgamVkZXIgVmFyaWFibGVuIHZvcj8gUXVhbGl0YXRpdmUKICAgICAgICAoS2F0ZWdvcmllbikgb2RlciBxdWFudGl0YXRpdmUgRGF0ZW4/CiAgICAtICAgV2FzIGlzdCBkaWUgenVncnVuZGVsaWVnZW5kZSBGcmFnZXN0ZWxsdW5nPyBXZWxjaGVzIEVyZ2VibmlzCiAgICAgICAgc29sbCAiYWJnZWxlaXRldCIgd2VyZGVuPwoyLiAgQmV0cmFjaHRlbiBTaWUgZGllIGVpbnplbG5lbiBWYXJpYWJsZW4uIFdhcyBiZXNjaHJlaWJlbi9iZWRldXRlbiBTaWUKICAgIGltIFp1c2FtbWVuaGFuZyB6dXIgRGF0ZW5lcmhlYnVuZz8KICAgIDEuICBXaWUgc2luZCBxdWFudGl0YXRpdmUgVmFyaWFibGVuIHZlcnRlaWx0ICh6LiBCLiBIaXN0b2dyYW1tZSwKICAgICAgICBCb3hwbG90cyk/CiAgICAgICAgLSAgIExpZWdlbiBBdXNyZWnDn2VyIHZvcj8gV2FydW0/CiAgICAgICAgLSAgIExpZWdlbiBmZWhsZW5kZSBXZXJ0ZSB2b3I/IFdhcnVtPwogICAgICAgIC0gICBXYXJ1bSBzaW5kIGRpZSBWYXJpYWJsZW4gc28gdmVydGVpbHQ/CiAgICAyLiAgV2llIHZpZWxlIEJlb2JhY2h0dW5nZW4gbGllZ2VuIHBybyBLYXRlZ29yaWUgYmVpIHF1YWxpdGF0aXZlbgogICAgICAgIFZhcmlhYmxlbiB2b3I/CiAgICAgICAgLSAgIExpZWdlbiBkaWUgS2F0ZWdvcmllbiAoZ3LDtsOfdGVudGVpbHMpIGltIHNlbGJlbiBBdXNtYcOfIHZvcj8KICAgICAgICAtICAgTGllZ2VuIEthdGVnb3JpZW4gc2VociB2aWVsIGjDpHVmaWdlci9zZWx0ZW5lciB2b3IgYWxzCiAgICAgICAgICAgIGFuZGVyZT8gV2FydW0/CiAgICAgICAgICAgIC0gICBLYW5uIGVpbmVuIGRpcmVrdGVuIEVpbmZsdXNzIGF1ZiBzdGF0aXN0aXNjaGUKICAgICAgICAgICAgICAgIEF1c3dlcnR1bmdlbiBoYWJlbgogICAgMy4gIEJldHJhY2h0ZW4gU2llIGhpZXJmw7xyIGF1Y2ggc3RhdGlzdGlzY2hlIE1hw596YWhsZW4gd2llIHouIEIuCiAgICAgICAgTWl0dGVsd2VydGUgb2RlciBTdGFuZGFyZGFid2VpY2h1bmdlbiwgdW0gSWhyZSBEYXRlbiB6dQogICAgICAgIGJlc2NocmVpYmVuLgozLiAgTGllZ2VuIFp1c2FtbWVuaMOkbmdlIHZvcj8KICAgIC0gICBRdWFudGl0YXRpdmUgVmFyaWFibGVuIG5hY2ggS2F0ZWdvcmllbiBhdWZ0ZWlsZW4gKHouIEIuIGR1cmNoCiAgICAgICAgR2l0dGVyL0ZhcmJoZXJ2b3JoZWJ1bmcgaW4gR3JhZmlrZW4pCiAgICAgICAgLSAgIFVudGVyc2NoZWlkZW4gc2ljaCBkaWUgdW50ZXJzY2hpZWRsaWNoZW4gS2F0ZWdvcmllbgogICAgICAgICAgICBiZXrDvGdsaWNoIGRlciBxdWFudGl0YXRpdmVuIFZhcmlhYmxlbj8gV2FydW0/CiAgICAtICAgWnVzYW1tZW5ow6RuZ2Ugdm9uIHp3ZWkgKG9kZXIgbWVocikgcXVhbnRpdGF0aXZlbiBWYXJpYWJsZW4KICAgICAgICAtICAgR2lidCBlcyBlaW5lbiBzdGVpZ2VuZGVuL2ZhbGxlbmRlbiBUcmVuZCBvZGVyIGtlaW5lbgogICAgICAgICAgICBadXNhbW1lbmhhbmc/IFdhcnVtPwogICAgICAgICAgICAtICAgU3RyZXVkaWFncmFtbSAoZ2dmLiBwcm8gS2F0ZWdvcmllKQogICAgICAgICAgICAtICAgS29ycmVsYXRpb25za29lZmZpemllbnQKICAgIC0gICBadXNhbW1lbmjDpG5nZSB2b24gcXVhbGl0YXRpdmVuIFZhcmlhYmxlbgogICAgICAgIC0gICBMaWVnZW4gbWFuY2hlIEthdGVnb3JpZW4gZ2VtZWluc2FtIGjDpHVmaWdlci9zZWx0ZW5lciB2b3IgYWxzCiAgICAgICAgICAgIGFuZGVyZT8gV2FydW0/CiAgICAgICAgICAgIC0gICBIw6R1Zmlna2VpdHN0YWJlbGxlL0jDpHVmaWdrZWl0c2dyYWZpawogICAgICAgICAgICAtICAgR3J1cHBpZXJ0ZSBCYWxrZW5kaWFncmFtbWUKNC4gIFN1Y2hlbiBTaWUgdW50ZXIgZGVuIHZvcmhlcmlnZW4gUHVua3RlbiB1bnR5cGlzY2hlIERhdGVuL0FzcGVrdGUgdW5kCiAgICBpbnRlcnByZXRpZXJlbiBTaWUgZGllc2UuCjUuICBGaW5kZW4gU2llIGbDvHIgSWhyZSBGcmFnZXN0ZWxsdW5nZW4gZGllIHBhc3NlbmRlIEF1c3dlcnR1bmdzdGVjaG5pazoKICAgIC0gICBBTk9WQSBiZWkgZGVtIEVpbmZsdXNzIHZvbiBxdWFsaXRhdGl2ZW4gVmFyaWFibGVuIGF1ZgogICAgICAgIHF1YW50aXRhdGl2ZSBWYXJpYWJsZW4KICAgIC0gICBSZWdyZXNzaW9uIGJlaSBkZW0gRWluZmx1c3Mgdm9uIHF1YW50aXRhdGl2ZW4gVmFyaWFibGVuIGF1ZiBlaW5lCiAgICAgICAgYW5kZXJlIHF1YW50aXRhdGl2ZSBWYXJpYWJsZQoKSW4gbWFuY2hlbiBGw6RsbGVuIGxpZWdlbiBhdWNoIGludGVyZXNzYW50ZSBCZW9iYWNodHVuZ2VuIHZvciwgZGllCnp1bsOkY2hzdCBuaWNodHMgbWl0IGRlciB1cnNwcsO8bmdsaWNoZW4gRnJhZ2VzdGVsbHVuZyB6dXR1biBoYWJlbi4gRXMKa2FubiBzaWNoIGxvaG5lbiwgbmljaHQgbnVyIHppZWxzdHJlYmlnIGRhcyBnZXfDvG5zY2h0ZSBFcmdlYm5pcwphdXN6dXdlcnRlbiwgc29uZGVybiBhdWNoIGJlaWzDpHVmaWdlIFZhcmlhYmxlbi9Bc3Bla3RlIHp1IGJldHJhY2h0ZW4uIEVzCmdpYnQgbmF0w7xybGljaCBub2NoIHdlaXRlcmUgc3RhdGlzdGlzY2hlIEF1c3dlcnR1bmdzbcO2Z2xpY2hrZWl0ZW4uCkFsbGVyZGluZ3MgbGFzc2VuIHNpY2ggZGllIG1laXN0ZW4gRXhwZXJpbWVudGUgYW5oYW5kIHZvbiBWYXJpYW56YW5hbHlzZQooQU5PVkEpIG9kZXIgUmVncmVzc2lvbiBhdXN3ZXJ0ZW4gdW5kIHNpbmQgZGFoZXIgaW4gZGVtIFJhaG1lbiBkZXMKS3Vyc2VzIGFtIHNpbm52b2xsc3Rlbi4KCiMjIEtvbnRpbnVpZXJsaWNoZSBEYXRlbgoKQmVpIGRlciBEYXRlbmFuYWx5c2UtdW5kIGF1c3dlcnR1bmcgd2lyZCB6d2lzY2hlbiBxdWFsaXRhdGl2ZW4gdW5kCnF1YW50aXRhdGl2ZW4gRGF0ZW4gdW50ZXJzY2hpZWRlbi4gRGllc2UgVW50ZXJzY2hlaWR1bmcgaXN0IGVzc2VudGllbGwKYmVpIGRlciBFbnRzY2hlaWR1bmcgd2llIG1pdCBkZW4gRGF0ZW4gdW1nZWdhbmdlbiB3ZXJkZW4ga2Fubi4gSW4gZGVtClJhaG1lbiBkZXMgS3Vyc2VzIGhhYmVuIFNpZSBiZXJlaXRzIGVpbmlnZSBHcmFmaWtkYXJzdGVsbHVuZ2VuCmtlbm5lbmdlbGVybnQgdW5kIG51biBzb2xsZW4gZGllc2UgdmVyd2VuZGV0IHdlcmRlbiwgdW0gRGF0ZW4gdW5kIGRpZQp6dWdydW5kZWxpZWdlbmRlbiBSZXN1bHRhdGUgYXVzenV3ZXJ0ZW4uCgojIyMgVmVydGVpbHVuZyB1bmQgSW50ZXJwcmV0YXRpb24KCkJldHJhY2h0ZW4gd2lyIGVpbmVuIERhdGVuc2F0eiDDvGJlciBLaXJzY2hiw6R1bWUgdW5kIGRlc3NlbiBNYcOfZToKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKdHJlZXMgPC0gYXNfdGliYmxlKHRyZWVzKQp0cmVlcwpgYGAKCkRlciBEYXRlbnNhdHogYmVzY2hyZWlidCBkaWUgSMO2aGUsIGRlbiBEdXJjaG1lc3NlciB1bmQgZGFzIFZvbHVtZW4gdm9uCjMxIGdlZsOkbGx0ZW4gS2lyc2NoYsOkdW1lbi4KCldpciBlcmhhbHRlbiBhbHNvIGJlcmVpdHMgZHVyY2ggZGFzIEVpbnNlaGVuIGRlciBlcnN0ZW4gU3BhbHRlbiwgd29ydW0KZXMgc2ljaCB1bSB1bnNlcmVuIERhdGVuc2F0eiBoYW5kZWx0IHVuZCB3aWUgdmllbGUgQmVvYmFjaHR1bmdlbiB1bmQKVmFyaWFibGVuIGVzIGdpYnQuIEViZW5mYWxscyBzZWhlbiB3aXIsIGRhc3MgYWxsZSBWYXJpYWJsZW4gcXVhbnRpdGF0aXYKc2luZCwgZGEgc2llIGtvbnRpbnVpZXJsaWNoZSBNZXNzd2VydGUgYmVzY2hyZWliZW4uCgpEdXJjaCBkZW4gQmVmZWhsIGA/dHJlZXNgIHNlaGVuIHdpciBhdWNoLCBkYXNzIGRlciBEdXJjaG1lc3NlciBpbiBab2xsLApkaWUgSMO2aGUgaW4gRnXDnyB1bmQgZGFzIFZvbHVtZW4gaW4gS3ViaWtmdcOfIGFuZ2VnZWJlbiBzaW5kLiBCZXJlaXRlbiB3aXIKZGVuIERhdGVuc2F0eiBhbHNvIHp1bsOkY2hzdCBzbyBhdWYsIGRhc3MgZWluIE5pY2h0YW1lcmlrYW5lciBkaWUKKG1ldHJpc2NoZW4pIEVpbmhlaXRlbiBsZXNlbiBrYW5uOgoKYGBge3J9CnRyZWVzIDwtIHRyZWVzICU+JQoJbXV0YXRlKEdpcnRoID0gR2lydGggKiAyLjU0LAoJCQkJIEhlaWdodCA9IEhlaWdodCAqIDMwLjQ4IC8gMTAwLAoJCQkJIFZvbHVtZSA9IFZvbHVtZSAqIDM1LjMxNSkKdHJlZXMKYGBgCgpOdW4gbGllZ3QgZGVyIER1cmNobWVzZXIgaW4gWmVudGltZXRlciwgZGllIEjDtmhlIGluIE1ldGVyIHVuZCBkYXMKVm9sdW1lbiBpbiBLdWJpa21ldGVyIHZvci4KClNjaGF1ZW4gd2lyIHVucyBudW4gZGllIMO8YmxpY2hlbiBzdGF0aXN0aXNjaGVuIE1hw596YWhsZW4KCmBgYHtyfQpzdW1tYXJ5KHRyZWVzKQpgYGAKCnVuZCBkaWUgVmVydGVpbHVuZ2VuIGRlciBWYXJpYWJsZW4gYW4KCmBgYHtyfQpsaWJyYXJ5KHBhdGNod29yaykKaGlzdG8gPC0gZ2dwbG90KHRyZWVzKSArCglnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IEdpcnRoKSwgYmlud2lkdGggPSAzLCBmaWxsID0gInN0ZWVsYmx1ZSIsIGNvbG9yID0gImJsYWNrIikgKwoJbGFicyh4ID0gTlVMTCkKYm94cGwgPC0gZ2dwbG90KHRyZWVzKSArCglnZW9tX2JveHBsb3QoYWVzKHggPSBHaXJ0aCksIGZpbGwgPSAic3RlZWxibHVlIiwgY29sb3IgPSAiYmxhY2siKSArIAoJbGFicyh4ID0gIkR1cmNobWVzc2VyIChjbSkiKSArCglzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gTlVMTCkKaGlzdG8gKyBib3hwbCArIHBsb3RfbGF5b3V0KDEsIDIpICMgZnVua3Rpb25pZXJ0IG51ciBtaXQgZGVyIHBhdGNod29yayBCaWJsaW90aGVrCmBgYAoKYGBge3J9Cmhpc3RvIDwtIGdncGxvdCh0cmVlcykgKwoJZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBIZWlnaHQpLCBiaW53aWR0aCA9IDEsIGZpbGwgPSAic3RlZWxibHVlIiwgY29sb3IgPSAiYmxhY2siKSArCglsYWJzKHggPSBOVUxMKQpib3hwbCA8LSBnZ3Bsb3QodHJlZXMpICsKCWdlb21fYm94cGxvdChhZXMoeCA9IEhlaWdodCksIGZpbGwgPSAic3RlZWxibHVlIiwgY29sb3IgPSAiYmxhY2siKSArIAoJbGFicyh4ID0gIkjDtmhlIChtKSIpICsKCXNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBOVUxMKQpoaXN0byArIGJveHBsICsgcGxvdF9sYXlvdXQoMSwgMikgIyBmdW5rdGlvbmllcnQgbnVyIG1pdCBkZXIgcGF0Y2h3b3JrIEJpYmxpb3RoZWsKYGBgCgpIaW53ZWlzOiBCZWkgZGVyIEtsYXNzZW5icmVpdGUgKG9kZXIgQW56YWhsIGRlciBLbGFzc2VuKSBzb2xsdGVuIGVpbgpwYWFyIHZlcnNjaGllZGVuZSBQYXJhbWV0ZXIgYXVzcHJvYmllcnQgd2VyZGVuIChgYmluc2Agb2RlciBgYmlud2lkdGhgKSwKdW0genUgc2VoZW4sIHdlbGNoZXIgV2VydCBhbSBzaW5udm9sbHN0ZW4gaXN0LgoKLSAgIEplIG5hY2ggRnJhZ2VzdGVsbHVuZyBrYW5uIGRpZSBnZW5lcmVsbGUgSW5mb3JtYXRpb24gw7xiZXIgZGllIEjDtmhlCiAgICB1bmQgZGVuIER1cmNobWVzc2VyIHZvbiBLaXJzY2hiw6R1bWVuIGJlcmVpdHMgaW50ZXJlc3NhbnQgc2VpbiwKICAgIGJlaXNwaWVsc3dlaXNlIHdlbm4gbWFuIGhlcmF1c2ZpbmRlbiB3aWxsLCB3YXMgbWFuIGVyd2FydGVuIGthbm4gd2llCiAgICBob2NoIGVpbiBLaXJzY2hiYXVtIChlaW5lciBiZXN0aW1tdGVuIEdhdHR1bmcpIHfDpGNoc3QuCi0gICBPaG5lIGplZ2xpY2hlIHdlaXRlcmUgSW5mb3JtYXRpb24gw7xiZXIgZGllIEtpcnNjaGLDpHVtZSB6dSBoYWJlbiwKICAgIHNlaGVuIHdpciBiZXJlaXRzLCBkYXNzIGRpZXNlIELDpHVtZSBzZWhyIGdyb8OfIHp1IHNlaW4gc2NoZWluZW4KICAgIChNaXR0ZWx3ZXJ0IDIzLDE2bSkgdW5kIHdvaGwgbmljaHQgaW4gZGVtIEdhcnRlbiBlaW5lcyBLbGVpbmfDpHJ0bmVycwogICAgc3RlaGVuIHfDvHJkZW4uCi0gICBEaWUgSMO2aGUgc2NoZWludCBub3JtYWx2ZXJ0ZWlsdCB6dSBzZWluIChIaXN0b2dyYW1tIMOkaG5lbHQKICAgIEdsb2NrZW5rdXJ2ZSkKLSAgIERpZSBWZXJ0ZWlsdW5nIGRlcyBEdXJjaG1lc3NlcnMgaXN0IG51ciBzY2h3ZXIgenUgZGV1dGVuCi0gICBCZWlkZSBWYXJpYWJsZW4gd2Vpc2VuIHNjaGVpbmJhciBrZWluZSBBdXNyZWnDn2VyIGF1ZgoKRXMgZ2lidCB6dXPDpHR6bGljaCBvZGVyIGVyZ8OkbnplbmQgenVtIEhpc3RvZ3JhbW0gbm9jaCBkaWUgTcO2Z2xpY2hrZWl0CmRpZSBWZXJ0ZWlsdW5nIGR1cmNoIExpbmllbiB6dSB2ZXJkZXV0bGljaGVuLiBGw7xyIGRpZSBhYnNvbHV0ZQpIw6R1Zmlna2VpdCBsw6Rzc3Qgc2ljaCBtaXQgYGdlb21fZnJlcXBvbHlgIGVpbmUgZ3JvYmUgTGluaWUgZWluemVpY2huZW46CgpgYGB7cn0KZ2dwbG90KHRyZWVzLCBhZXMoeCA9IEdpcnRoKSkgKwoJZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSA1LCBmaWxsID0gInN0ZWVsYmx1ZSIpICsKCWdlb21fZnJlcXBvbHkoYmlud2lkdGggPSA1KSArCglzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwLCAyKSkgIyBzb25zdCBzdGVoZW4gdW5nZXJhZGUgV2VydGUgYXVmIGRlciB5LUFjaHNlCmBgYAoKLSAgIGBiaW5zYCBvZGVyIGBiaW53aWR0aGAgbXVzcyBiZWkgYmVpZGVuIEZ1bmt0aW9uZW4gZ2xlaWNoIHNlaW4KLSAgIFdpciBzZWhlbiBlaW5lIGV0d2FzIHZlcmRldXRsaWNodGUgQXVmLXVuZCBBYmJld2VndW5nCi0gICBKZSBuYWNoIEtsYXNzZW5icmVpdGUgaXN0IGRpZXNlciBUcmVuZCB3ZW5pZ2VyIGd1dCBlaW5zaWNodGlnCgpVbSBlaW5lIGdsYXR0ZXJlIEt1cnZlIHp1IGJldHJhY2h0ZW4sIHdlcmRlbiBow6R1ZmlnIERpY2h0ZWxpbmllbiBpbiBlaW4KRGljaHRlaGlzdG9ncmFtbSBlaW5nZXplaWNobmV0LiBCZWkgZGVtIERpY2h0ZWhpc3RvZ3JhbW0gc3RlaGVuIGhpZXJiZWkKZGllIHJlbGF0aXZlbiBIw6R1Zmlna2VpdGVuIGdldGVpbHQgZHVyY2ggS2xhc3NlbmJyZWl0ZSBhdWYgZGVyIHktQWNoc2UuCkRpY2h0ZSBsw6Rzc3Qgc2ljaCBkdXJjaCBgLi5kZW5zaXR5Li5gIGFscyB5LVdlcnQgaW4gZGllIEFlc3RoZXRpY3MKZWluc2V0emVuLiBBbnN0ZWxsZSB2b24gYGdlb21fZnJlcXBvbHlgIHZlcndlbmRlbiB3aXIgZsO8ciBkaWUgTGluaWUgbnVuCmBnZW9tX2RlbnNpdHlgCgpgYGB7cn0KZ2dwbG90KHRyZWVzLCBhZXMoeCA9IEdpcnRoLCB5ID0gLi5kZW5zaXR5Li4pKSArCglnZW9tX2hpc3RvZ3JhbShiaW5zID0gOCwgZmlsbCA9ICJzdGVlbGJsdWUiLCBjb2xvciA9ICJibGFjayIpICsKCWdlb21fZGVuc2l0eShhbHBoYSA9IDAuMjUsIGZpbGwgPSAiZ3JleSIsIGNvbG9yID0gImdyZXkiKQpgYGAKCi0gICB3b2hsbcO2Z2xpY2ggc2luZCBkaWUgRHVyY2htZXNzZXIgZG9jaCBub3JtYWx2ZXJ0ZWlsdCAob2RlciBHYW1tYQogICAgb2RlciBDaGktUXVhZHJhdCAuLi4pPwoKIyMjIERhdGVuIGF1ZiBOb3JtYWx2ZXJ0ZWlsdW5nIHRlc3RlbgoKVW5zZXJlIEludHVpdGlvbiBtYWcgdW5zIGluIG1hbmNoZW4gRsOkbGxlbiBoZWxmZW4gYWJ6dXNjaMOkdHplbiwgd2VsY2hlcgpWZXJ0ZWlsdW5nIHVuc2VyZSBEYXRlbiBmb2xnZW4uIFVtIHNpY2hlciB6dSBnZWhlbiwgZ2lidCBlcyBoaWVyZsO8cgpzdGF0aXN0aXNjaGUgVGVzdHMuIEJlaSBkZXIgTm9ybWFsdmVydGVpbHVuZyB3aXJkIGJlaXNwaWVsc3dlaXNlIGRlcgpTaGFwaXJvLVdpbGstVGVzdCB2ZXJ3ZW5kZXQuCgpOdW4gc3RlbGx0IHNpY2ggZ2VuZXJlbGwgZGllIEZyYWdlLCB3YXJ1bSBtYW4gw7xiZXJoYXVwdCBWZXJ0ZWlsdW5nZW4KYmV0cmFjaHRlbiBzb2xsdGUuIE5hdMO8cmxpY2hlIEJlb2JhY2h0dW5nZW4gdW5kIFByb3plc3NlIGZvbGdlbiBow6R1ZmlnCk5vcm1hbHZlcnRlaWx1bmdlbiwgZC4gaC4gZXMgZ2lidCBlaW5lbiAiTWl0dGVscHVua3QiLCB1bSBkZW4gc2ljaCBkaWUKQmVvYmFjaHR1bmdlbiB2ZXJ0ZWlsZW4gdW5kIGV4dHJlbWUgV2VydGUgd2VpdCBvYmVyLXVuZCB1bnRlcmhhbGIgc2luZAplaGVyIHVud2FocnNjaGVpbmxpY2ggdW5kIHdlcmRlbiBzZWx0ZW4gYmVvYmFjaHRldC4gTm9ybWFsdmVydGVpbHVuZ2VuCnNpbmQgZsO8ciB2aWVsZSBzdGF0aXN0aXNjaGUgVGVzdHMgZWluZSBWb3JhdXNzZXR6dW5nLiBXZW5uIGRpZXNlCnZlcmxldHp0IHdpcmQsIGRhbm4gdmVybGllcnQgZGVyIFRlc3QgYW4gQXVzc2FnZWtyYWZ0LgoKWnVkZW0ga2FubiBlcyBpbnRlcmVzc2FudCBzZWluLCBhbmRlcmUgVmVydGVpbHVuZ2VuIGF1Znp1ZmluZGVuIHdpZSB6LgpCLiBkaWUgYmltb2RhbGUgVmVydGVpbHVuZyB1bmQgenUgZXJrdW5kZW4sIHdhcnVtIGVzIG1laHJlcmUgIlBlYWtzIgpnaWJ0OgoKOjo6IHtzdHlsZT0idGV4dC1hbGlnbjogY2VudGVyOyJ9CiFbXShodHRwczovL3VwbG9hZC53aWtpbWVkaWEub3JnL3dpa2lwZWRpYS9jb21tb25zL2UvZTIvQmltb2RhbC5wbmcpCjo6OgoKSW4gUiBnaWJ0IGVzIGRpZSBGdW5rdGlvbiBgc2hhcGlyby50ZXN0KClgLCB1bSBkZW4gU2hhcGlyby1XaWxrLVRlc3QgZsO8cgpkaWUgTm9ybWFsdmVydGVpbHVuZyBkdXJjaHp1ZsO8aHJlbjoKCi0gICBOdWxsaHlwb3RoZXNlIChIMCk6IERpZSBEYXRlbiBzaW5kIG5vcm1hbHZlcnRlaWx0CgotICAgQWx0ZXJuYXRpdmh5cG90aGVzZSAoSDEpOiBEaWUgRGF0ZW4gc2luZCBuaWNodCBub3JtYWx2ZXJ0ZWlsdAoKLSAgIERlciBTaGFwaXJvLVdpbGstVGVzdCwgd2llIGF1Y2ggamVkZXIgc3RhdGlzdGlzY2hlIFRlc3QsIGxpZWZlcnQKICAgIGVpbmUgU3RhdGlzdGlrIHNvd2llIGVpbmVuIHAtV2VydC4KCsOcYmxpY2hlcndlaXNlIHdpcmQgZWluIFNpZ25pZmlrYW56bml2ZWF1IOKNuiAoPSBTY2h3ZWxsZW53ZXJ0KSBnZXNldHp0LAptaXQgZGVtIHdpciBlbnRzY2hlaWRlbiwgd2VsY2hlIEh5cG90aGVzZSBhbmdlbm9tbWVuIHdlcmRlbiBzb2xsLiBIw6R1ZmlnCndpcmQgZWluIE5pdmVhdSB2b24gMC4wNSB2ZXJ3ZW5kZXQuIERhcyBwIHN0ZWh0IGhpZXJiZWkgZsO8cgoicHJvYmFiaWxpdHkiLCBkLiBoLiBkaWVzZXIgV2VydCBnaWJ0IHVucyBhbiwgd2llIHdhaHJzY2hlaW5saWNoIGVzIGlzdCwKZGFzcyBlaW4gZXh0cmVtZXJlciBXZXJ0IGFscyBkaWUgVGVzdHN0YXRpc3RpayBiZXRyYWNodGV0IHdpcmQuCgpJbiBkZXIgUHJheGlzIGJlZGV1dGV0IGRhcywgZGFzcyB3aXIgYXVzIDIwIGR1cmNoZ2Vmw7xocnRlbiBUZXN0cwplcndhcnRlbiwgZGFzcyBlaW4gVGVzdCBlaW4gZmFsc2NoZXMgRXJnZWJuaXMgbGllZmVydCwgd2VubiB3aXIgZWluClNpZ25pZmlrYW56bml2ZWF1IHZvbiAwLjA1IGF1c3fDpGhsZW4uIFN0cmVuZ2VyZSBTY2h3ZWxsZW53ZXJ0ZSBmw7xocmVuCmFsc28gZGF6dSwgZGFzcyB3aXIgbWVociBTaWNoZXJoZWl0IGhhYmVuLCBkYXNzIGRhcyBFcmdlYm5pcyB0YXRzw6RjaGxpY2gKd2FociBpc3QuIEplZG9jaCB2ZXJsaWVyZW4gd2lyIGJlaSBzZWhyIHN0cmVuZ2VuIFNjaHdlbGxlbndlcnRlICh6LiBCLgowLjAwMDAxKSB3b2hsbcO2Z2xpY2ggd2FocmUgRXJnZWJuaXNzZS4gRGFoZXIgc2luZCBwLVdlcnRlIGVpbiBzZWhyCnVtc3RyaXR0ZW5lcyBUaGVtYS4KCkFjaHRlbiBTaWUgaW1tZXIgZGFyYXVmOgoKLSAgIEdpbHQgcCBcPj0g4o26ID1cPiB3aXIgbGVobmVuIEgwIG5pY2h0IGFiCgotICAgR2lsdCBwIFw8IOKNuiA9XD4gd2lyIGxlaG5lbiBIMCBhYiB1bmQgbmVobWVuIEgxIGFuCgpGw7xocmVuIHdpciBkZW4gU2hhcGlyby1XaWxrLVRlc3QgbnVuIGR1cmNoIHVuZCB3w6RobGVuIOKNuiA9IDAuMDU6CgpgYGB7cn0KIyBTcGFsdGUgYXVzIERhdGFmcmFtZSBlbnRuZWhtZW46CnRyZWVzJEdpcnRoCiMgU2hhcGlyby1UZXN0IG1pdCBkaWVzZXIgU3BhbHRlOgpzaGFwaXJvLnRlc3QodHJlZXMkR2lydGgpCmBgYAoKRGVyIFRlc3QgbmltbXQgZWluZW4gVmVrdG9yIChWYXJpYWJsZSkgZW50Z2VnZW4sIGQuIGguIHdpciBtw7xzc2VuIGF1cwpgdHJlZXNgIGRpZSBWYXJpYWJsZSBgR2lydGhgIG1pdCBkZW0gYCRgLU9wZXJhdG9yIGVudG5laG1lbi4KCi0gICBFcyBnaWx0IChwLVdlcnQgPSkgMC4wODkgXD4gMC4wNSAoPSDijbopID1cPiBIMCB3aXJkIG5pY2h0IGFiZ2VsZWhudAogICAgdW5kIHdpciBuZWhtZW4gYW4sIGRhc3MgZGllIERhdGVuIG5vcm1hbHZlcnRlaWx0IHNpbmQuCgotICAgRGFzc2VsYmUga8O2bm50ZW4gd2lyIG51biBmw7xyIGRpZSBhbmRlcmVuIFZhcmlhYmxlbiBkdXJjaGbDvGhyZW4KClVtIGRhcyBUZXN0ZW4gbWl0IGB0aWR5dmVyc2VgIGV0d2FzIHp1IHZlcmVpbmZhY2hlbiwgd29sbGVuIHdpciBkYXMKUGFrZXQgYHJzdGF0aXhgIHZlcndlbmRlbjoKCmBgYHtyLCBldmFsID0gRkFMU0V9Cmluc3RhbGwucGFja2FnZXMoInJzdGF0aXgiKQpgYGAKCk1pdCBgcnN0YXRpeGAga8O2bm5lbiB3aXIgVGVzdHMgaW4gUiBkdXJjaGbDvGhyZW4gdW5kIGdsZWljaHplaXRpZyB3ZWl0ZXIKbWl0IERhdGFmcmFtZXMgYXJiZWl0ZW4uIEbDvHIgZGVuIFNoYXBpcm8tV2lsay1UZXN0IGdpYnQgZXMgaGllciBkaWUKRnVua3Rpb24gYHNoYXBpcm9fdGVzdCgpYCwgZGVtIHdpciBlaW5mYWNoIGRpZSBWYXJpYWJsZW4gw7xiZXJnZWJlbgprw7ZubmVuOgoKYGBge3J9CmxpYnJhcnkocnN0YXRpeCkKdHJlZXMgJT4lCglzaGFwaXJvX3Rlc3QoR2lydGgsIEhlaWdodCwgVm9sdW1lKQpgYGAKCkRlciBWb3J0ZWlsIGhpZXJiZWkgaXN0LCBkYXNzIHdpciB3ZWl0ZXIgbWl0IERhdGFmcmFtZXMgYXJiZWl0ZW4sIGQuIGguCndpciBrw7ZubmVuIGRpZSBEYXRlbiBhdWNoIGRpcmVrdCBmaWx0ZXJuICh6LiBCLiBuYWNoIOKNuik6CgpgYGB7cn0KbmljaHRfc2lnIDwtIHRyZWVzICU+JSAKCXNoYXBpcm9fdGVzdChHaXJ0aCwgSGVpZ2h0LCBWb2x1bWUpICU+JQoJZmlsdGVyKHAgPj0gMC4wNSkKbmljaHRfc2lnCmBgYAoKLSAgIFdpciBzZWhlbiBkaXJla3QsIGRhc3MgRHVyY2htZXNzZXIgdW5kIEjDtmhlIG5vcm1hbHZlcnRlaWx0IHNpbmQgdW5kCiAgICBWb2x1bWVuIHNjaGVpbmJhciBuaWNodC4KCi0gICBJbnNiZXNvbmRlcmUgbMOkc3N0IHNpY2ggZGVyIFByb3plc3MgZHVyY2ggYmVpc3BpZWxzd2Vpc2UgR3J1cHBpZXJ1bmcKICAgIGVyd2VpdGVybiAoZGF6dSBzcMOkdGVyIG1laHIpLgoKIyMjIFp1c2FtbWVuaMOkbmdlCgpVbSBadXNhbW1lbmjDpG5nZSB2b24ga29udGludWllcmxpY2hlbiBWYXJpYWJsZW4genUgYmV0cmFjaHRlbiwgd2VyZGVuClN0cmV1ZGlhZ3JhbW1lIG9kZXIgw6RobmxpY2hlIEdyYWZpa2VuIHZlcndlbmRldC5cCkZhbGxzIHZlcnNjaGllZGVuZSBLYXRlZ29yaWVuL0dydXBwZW4gdm9yaGFuZGVuIHNpbmQsIGthbm4gZGllcwplYmVuZmFsbHMgZHVyY2ggRmFyYmhlcnZvcmhlYnVuZyBvZGVyIGR1cmNoIEdpdHRlciBlcmfDpG56dCB3ZXJkZW4uCgpCZWkgdW5zZXJlbSBEYXRlbnNhdHogw7xiZXIgS2lyc2NoYsOkdW1lIGlzdCBpbnR1aXRpdiBrbGFyLCB3aWUgc2ljaApEdXJjaG1lc3NlciB1bmQgSMO2aGUgdmVyaGFsdGVuLiBPZGVyPwoKYGBge3J9CmdncGxvdCh0cmVlcykgKwoJZ2VvbV9wb2ludChhZXMoR2lydGgsIEhlaWdodCkpICsKCWxhYnMoeCA9ICJEdXJjaG1lc3NlciAoY20pIiwKCQkJIHkgPSAiSMO2aGUgKG0pIiwgCgkJCSB0aXRsZSA9ICJLaXJzY2hiw6R1bWUgbWl0IGjDtmhlcmVtIER1cmNobWVzc2VyIHNpbmQgdGVuZGVuemllbGwgZ3LDtsOfZXIiLAoJCQkgY2FwdGlvbiA9ICJEYXRlbiBhdXMgZGVtIEJ1Y2ggJ1RoZSBNaW5pdGFiIFN0dWRlbnQgSGFuZGJvb2snIikKYGBgCgotICAgRGllIFRlbmRlbnogaXN0IGVpbmRldXRpZzogc3RlaWd0IGRlciBEdXJjaG1lc3Nlciwgc3RlaWd0IGViZW5mYWxscwogICAgYXVjaCBkaWUgSMO2aGUuCi0gICBEaWVzZSBBdXNzYWdlIG1hZyBuaWNodCBmw7xyIGplZGVuIGVpbnplbG5lbiBQdW5rdCB6dXRyZWZmZW4uIFdpY2h0aWcKICAgIGlzdCBoaWVyYmVpLCBkYXNzIGVpbmUgVGVuZGVueiB6dSBzZWhlbiBpc3QuCi0gICBEaWVzZXIgWnVzYW1tZW5oYW5nIGvDtm5udGUgbGluZWFyIHNlaW46IGxlZ2VuIHdpciBlaW5lIHBhc3NlbmRlCiAgICBHZXJhZGUgZHVyY2ggZGllIFB1bmt0ZSwgd2VyZGVuIGRpZSBtZWlzdGVuIFB1bmt0ZSBtaXQgd2VuaWcKICAgIEFid2VpY2h1bmcgZ3V0IGdldHJvZmZlbi4KClp1dm9yIGhhdHRlbiB3aXIgZHVyY2ggU3RyZXVkaWFncmFtbWUgbWl0IGBnZW9tX3Ntb290aGAgZWluZSBnZWdsw6R0dGV0ZQpMaW5pZSBnZWxlZ3Q6CgpgYGB7cn0KZ2dwbG90KHRyZWVzLCBhZXMoR2lydGgsIEhlaWdodCkpICsKCWdlb21fcG9pbnQoKSArCglnZW9tX3Ntb290aCgpICsKCWxhYnMoeCA9ICJEdXJjaG1lc3NlciAoY20pIiwKCQkJIHkgPSAiSMO2aGUgKG0pIiwgCgkJCSB0aXRsZSA9ICJLaXJzY2hiw6R1bWUgbWl0IGjDtmhlcmVtIER1cmNobWVzc2VyIHNpbmQgdGVuZGVuemllbGwgZ3LDtsOfZXIiLAoJCQkgY2FwdGlvbiA9ICJEYXRlbiBhdXMgZGVtIEJ1Y2ggJ1RoZSBNaW5pdGFiIFN0dWRlbnQgSGFuZGJvb2snIikKYGBgCgotICAgSW0gSGludGVyZ3J1bmQgdm9uIGBnZW9tX3Ntb290aGAgd2lyZCBlaW4gTW9kZWxsIGVyc3RlbGx0LCB3ZWxjaGVzCiAgICBkaWUgRGF0ZW5wdW5rdGUgZHVyY2ggZGllc2UgZ2VnbMOkdHRldGUgTGluaWUgYW5uw6RoZXJ0LgotICAgSmUgbmFjaCBNZXRob2RlIChNb2RlbGxhcnQpIGvDtm5uZW4gZGFzIGdsYXR0ZSBrb21wbGV4ZSBQb2x5bm9tZSBzZWluCiAgICBvZGVyIChlaW5mYWNoZSkgR2VyYWRlbi4KLSAgIEhpZXJ6dSBtdXNzIGRlciBgbWV0aG9kYCBQYXJhbWV0ZXIgaW4gYGdlb21fc21vb3RoYCBhbmdlcGFzc3Qgd2VyZGVuCiAgICAoYD9nZW9tX3Ntb290aGApLgoKRWluIEdlcmFkZSBsw6Rzc3Qgc2ljaCBiZWlzcGllbHN3ZWlzZSBkdXJjaCBgImxtImAgZWluemVpY2huZW46CgpgYGB7cn0KZ2dwbG90KHRyZWVzLCBhZXMoR2lydGgsIEhlaWdodCkpICsKCWdlb21fcG9pbnQoKSArCglnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBjb2xvciA9ICJyZWQiKSArCglsYWJzKHggPSAiRHVyY2htZXNzZXIgKGNtKSIsCgkJCSB5ID0gIkjDtmhlIChtKSIsIAoJCQkgdGl0bGUgPSAiS2lyc2NoYsOkdW1lIG1pdCBow7ZoZXJlbSBEdXJjaG1lc3NlciBzaW5kIHRlbmRlbnppZWxsIGdyw7bDn2VyIiwKCQkJIGNhcHRpb24gPSAiRGF0ZW4gYXVzIGRlbSBCdWNoICdUaGUgTWluaXRhYiBTdHVkZW50IEhhbmRib29rJyIpCmBgYAoKLSAgIERpZSBHZXJhZGUgc2NoZWludCBuaWNodCAodmllbCkgc2NobGVjaHRlciB6dSBzZWluIGFscyBkaWUgS3VydmUgdm9uCiAgICBvYmVuLCBhYmVyIHNjaGVpbnQgZGVuIFp1c2FtbWVuaGFuZyB2aWVsbGVpY2h0IG5pY2h0IHBlcmZla3QKICAgIGRhcnp1c3RlbGxlbgotICAgRGFzIGlzdCBlaW4gdHlwaXNjaGVzIFBow6Rub21lbi4gU3DDpHRlciBzZWhlbiB3aXIsIHdpZSBkaWUgR8O8dGUgdm9uCiAgICBkaWVzZW0gTW9kZWxsIHp1IGJld2VydGVuIGlzdC4KCiMjIyMgS29ycmVsYXRpb25zbWF0cml4ICYgS29ycmVsYXRpb25zZ3JhZmlrCgpadXNhbW1lbmjDpG5nZSBzaW5kIGRpZSBUZW5kZW56LCBkYXNzIGRhcyBTdGVpZ2VuIG9kZXIgRmFsbGVuIGVpbmVyClZhcmlhYmxlIGViZW5mYWxscyBtaXQgZGVtIFN0ZWlnZW4gb2RlciBGYWxsZW4gZWluZXIgYW5kZXJlbiBWYXJpYWJsZQplaW5oZXJnZWh0LiBVbSBkaWUgU3TDpHJrZSBlaW5lcyBadXNhbW1lbmhhbmdzIHp1IGJlc3RpbW1lbiwgd2lyZCBlaW4KS29ycmVsYXRpb25za29mZml6aWVudCB2ZXJ3ZW5kZXQ6CgotICAgTGllZ3Qgendpc2NoZW4gLTEgKHBlcmZla3QgbmVnYXRpdikgdW5kICsxIChwZXJmZWt0IHBvc2l0aXYpCgotICAgRWluIFdlcnQgdm9uIDAgZGV1dGV0IGRhcmF1ZiwgZGFzcyBkaWUgVmFyaWFibGVuIGtlaW5lbiBadXNhbW1lbmhhbmcKICAgIGF1ZndlaXNlbgoKLSAgIEVzIGdpYnQgdmVyc2NoaWVkZW5lIEtvcnJlbGF0aW9uc2tvZWZmaXppZW50ZSAoUGVhcnNvbiwgU3BlYXJtYW4sCiAgICBLZW5kYWxsKQoKICAgIC0gICBQZWFyc29uOiBmYWxscyBkaWUgVmFyaWFibGVuIChyZWxhdGl2KSBsaW5lYXIgenVzYW1tZW5ow6RuZ2VuIHVuZAogICAgICAgIG5vcm1hbHZlcnRlaWx0IHNpbmQKCiAgICAtICAgQW5zb25zdGVuOiBLZW5kYWxsIChvcmRpbmFsZSBEYXRlbikgb2RlciBTcGVhcm1hbiB2ZXJ3ZW5kZW4KCldpciBoYXR0ZW4gb2JlbiBiZXJlaXRzIGdldGVzdGV0LCBkYXNzIER1cmNobWVzc2VyIHVuZCBIw7ZoZQpub3JtYWx2ZXJ0ZWlsdCBzaW5kLiBFYmVuZmFsbHMga8O2bm50ZSBlaW4gbGluZWFyZXIgWnVzYW1tZW5oYW5nIG3DtmdsaWNoCnNlaW4uIERhaGVyIHZlcndlbmRlbiB3aXIgUGVhcnNvbnMgS29ycmVsYXRpb25za29lZmZpemllbnQuIEhpZXJ6dSB3aXJkCnN0YW5kYXJkbcOkw59pZyBkaWUgYGNvcigpYCBGdW5rdGlvbiB2ZXJ3ZW5kZXQ6CgpgYGB7cn0KY29yKHRyZWVzJEdpcnRoLCB0cmVlcyRIZWlnaHQpCmBgYAoKLSAgIERlciBLb3JyZWxhdGlvbnNrb2VmZml6aWVudCB3ZWlzdCBlaW5lIFdlcnQgdm9uIDAsNTIgYXVmIHVuZCBkZXV0ZXQKICAgIHNvbWl0IGVpbmVuIG1vZGVyYXRlbiBadXNhbW1lbmhhbmcgYW4uIERhcyBlbnRzcHJpY2h0IGRlbSwgd2FzIHZvcgogICAgenV2b3IgYmV0cmFjaHRldCBoYWJlbi4KCi0gICBEaWUgUHVua3RlIGxpZWdlbiBuaWNodCBleGFrdCBhdWYgZWluZXIgR2VyYWRlbiwgc29uZGVybiB3ZWljaGVuCiAgICBldHdhcyBhYi4gRXMgbGllZ3QgYWxzbyBlaW4gdGF0c8OkY2hsaWNoZXIgWnVzYW1tZW5oYW5nIHZvci4KCi0gICBEaWUgSMO2aGUgdm9uIGVpbmVtIEtpcnNjaGJhdW0gaMOkbmd0IHNjaGVpbmJhciBuaWNodCBhdXNzY2hsaWXDn2xpY2gKICAgIHZvbiBkZW0gRHVyY2htZXNzZXIgYWIsIGRlbm4gc29uc3Qgd8OkcmUgZGVyIFp1c2FtbWVuaGFuZyBwZXJmZWt0LgoKSGlud2VpczogZGVuIEtvZWZmaXppZW50ZW4ga8O2bm5lbiB3aXIgbWl0IGBtZXRob2QgPSBYYCDDpG5kZXJuOgoKYGBge3J9CmNvcih0cmVlcyRHaXJ0aCwgdHJlZXMkSGVpZ2h0LCBtZXRob2QgPSAicGVhcnNvbiIpICMgRGVmYXVsdC9TdGFuZGFyZApjb3IodHJlZXMkR2lydGgsIHRyZWVzJEhlaWdodCwgbWV0aG9kID0gInNwZWFybWFuIikKY29yKHRyZWVzJEdpcnRoLCB0cmVlcyRIZWlnaHQsIG1ldGhvZCA9ICJrZW5kYWxsIikKYGBgCgotICAgRGllIGFuZGVyZW4gS29ycmVsYXRpb25za29lZmZpemllbnRlbiB3ZWlzZW4gc29nYXIgbm9jaCBldHdhcwogICAgZ2VyaW5nZXJlIFdlcnRlIGF1Zi4KCi0gICBadSBqZWRlbSBLb3JyZWxhdGlvbnNrb2VmZml6aWVudGVuIGdpYnQgZXMgYXVjaCBlaW5lbiB6dWdlaMO2cmlnZW4KICAgIFRlc3QsIG9iIGRlciBLb2VmZml6aWVudCBzaWNoIHNpZ25pZmlrYW50IHZvbiAwIHVudGVyc2NoZWlkZXQuCgotICAgQW5kZXJzIGZvcm11bGllcnQ6IGRlciBUZXN0IGdpYnQgYW4sIG9iIGVzIGVpbmVuIFp1c2FtbWVuaGFuZyBnaWJ0LgogICAgRGllIFRlc3RoeXBvdGhlc2VuIGxhdXRlbiBmw7xyIGFsbGUgVGVzdHM6CgogICAgLSAgIEgwOiBEZXIgS29lZmZpemllbnQgaXN0IGdsZWljaCAwCgogICAgLSAgIEgxOiBEZXIgS29lZmZpemllbnQgaXN0IHVuZ2xlaWNoIDAKCkdlbmF1c28gd2llIGBjb3IoKWAgbMOkc3N0IHNpY2ggZXJnw6RuemVuZCBmw7xyIGRpZXNlbiBUZXN0IGBjb3IudGVzdCgpYAp2ZXJ3ZW5kZW46CgpgYGB7cn0KY29yLnRlc3QodHJlZXMkSGVpZ2h0LCB0cmVlcyRHaXJ0aCkKYGBgCgotICAgSGllciBzZWhlbiBTaWUgZWJlbmZhbGxzIGRlbiBLb3JyZWxhdGlvbnNrb2VmZml6aWVudCBnYW56IHVudGVuCi0gICBEZXIgcC1XZXJ0IGRlcyBUZXN0cyBiZXRyw6RndCAwLDAwMjggdW5kIGlzdCBzb21pdCBrbGVpbmVyIGFscyAwLDA1CiAgICAoPSB0eXBpc2NoZXMg4o26KQotICAgRXMgZm9sZ3QsIGRhc3MgZGVyIFp1c2FtbWVuaGFuZyBzaWduaWZpa2FudCBiencuIHZvcmhhbmRlbiBpc3QKICAgICh1bnRlcnNjaGllZGxpY2ggdm9uIDApCgpJbiBlaW5lbSBCZXJpY2h0IGvDtm5udGUgbWFuIGJlaXNwaWVsc3dlaXNlIGVpbiBTdHJldWRpYWdyYW1tIG1pdCBkZXIKZ2VnbMOkdHRldGVuIEt1cnZlIGRhcnN0ZWxsZW4gdW5kIHp1c8OkdHpsaWNoIGRhcyBFcmdlYm5pcyBkZXMKS29ycmVsYXRpb25za29lZmZpemllbnRlbiBiZXJpY2h0ZW4sIHNvZmVybiBkaWVzZXIgWnVzYW1tZW5oYW5nCmludGVyZXNzYW50IGlzdC4KCkVzIGdpYnQgZsO8ciBLb3JyZWxhdGlvbnNrb2VmZml6aWVudGVuIGVpbmVuICJhbGxlcy1pbi1laW5lbSIgVHJpY2ssIHVtCnNjaG5lbGwgWnVzYW1tZW5ow6RuZ2UgZGFyenVzdGVsbGVuIHVuZCB6dSBlcmtlbm5lbiwgaW5zYmVzb25kZXJlIHdlbm4KdmllbGUgVmFyaWFibGVuIHZvcmxpZWdlbi4gSGllcnp1IHdpcmQgZWluZSBNYXRyaXggbWl0IGRlbgpwLVdlcnRlbi9Lb3JyZWxhdGlvbnNrb2VmZml6aWVudGVuIGbDvHIgamVkZXMgUGFhciBkZXIgVmFyaWFibGVuIGVyc3RlbGx0CnVuZCB6dXPDpHR6bGljaCBhbHMgR3JhZmlrIHZlcmFuc2NoYXVsaWNodC4KCkViZW5mYWxscyBtaXQgZGVtIGBjb3IoKWAgQmVmZWhsIGzDpHNzdCBzaWNoIGVpbmUgTWF0cml4IGbDvHIgYWxsZQpudW1lcmlzY2hlbiBWYXJpYWJsZW4gYmVyZWNobmVuLiBIaWVyYmVpIG11c3MgZGVyIERhdGVuc2F0eiBhdWYgZGllCm51bWVyaXNjaGVuIFZhcmlhYmxlbiByZWR1emllcnQgd2VyZGVuLiBOZWhtZW4gd2lyIGFscyBCZWlzcGllbCBkZW4KYG1wZ2AgRGF0ZW5zYXR6LiBFcyBnaWJ0IGhpZXJiZWkgbWVocmVyZSBNw7ZnbGljaGtlaXRlbjoKCmBgYHtyfQojIFZhcmlhYmxlbiBwZXIgSGFuZCBhdXN3w6RobGVuOgpudW1lcmlzY2ggPC0gbXBnICU+JQoJc2VsZWN0KGRpc3BsLCB5ZWFyLCBjeWwsIGN0eSwgaHd5KQpudW1lcmlzY2gKIyBWYXJpYWJsZW4gbmFjaCBCZWRpbmd1bmcgZmlsdGVybjoKbnVtZXJpc2NoIDwtIG1wZyAlPiUKCXNlbGVjdF9pZihpcy5udW1lcmljKQpudW1lcmlzY2gKYGBgCgotICAgQmVpIG1hbmNoZW4gVmFyaWFibGVuIG11c3MgbWFuIHNpY2ggw7xiZXJsZWdlbiwgb2IgZXMgU2lubiBlcmdpYnQsCiAgICBkaWVzZSBhbHMgS2F0ZWdvcmllIG9kZXIgWmFobGVud2VydCB6dSBiZXRyYWNodGVuICh6LiBCLiBkYXMgSmFocikKCk51biBrYW5uIGR1cmNoIGBjb3IoKWAgamVkZXIgcGFhcndlaXNlciBLb3JyZWxhdGlvbnNrb2VmZml6aWVudApiZXJlY2huZXQgd2VyZGVuOgoKYGBge3J9CmNvcihudW1lcmlzY2gpCmBgYAoKLSAgIERpZSBNYXRyaXgtRGFyc3RlbGx1bmcgaXN0IHNlaHIgaGlsZnJlaWNoLCB1bSBkaXJla3Qga29ua3JldGUgV2VydGUKICAgIHp1IHZlcmdsZWljaGVuCi0gICBCZWlzcGllbHN3ZWlzZSBzY2hlaW50IGRlciBLcmFmdHN0b2ZmdmVyYnJhdWNoIGluIGRlciBTdGFkdCAoYGN0eWApCiAgICBzZWhyIHN0YXJrIG1pdCBkZW0gS3JhZnRzdG9mZnZlcmJyYXVjaCBhdWYgZGVyIEF1dG9iYWhuIChgaHd5YCkgenUKICAgIGtvcnJlbGllcmVuLCB3YXMgYXVjaCBzaW5udm9sbCBpc3QKLSAgIEFuc2NoYXVsaWNoIGlzdCBkaWVzZSBEYXJzdGVsbHVuZyBqZWRvY2ggbmljaHQKCkJlaSB1bnNlcmVtIEtpcnNjaGJhdW0tRGF0ZW5zYXR6IHdhciBkYXMgRmlsdGVybiBoaW5nZWdlbiBuaWNodApub3R3ZW5kaWcsIGRhIGJlcmVpdHMgYWxsZSBWYXJpYWJsZW4gbnVtZXJpc2NoIHNpbmQ6CgpgYGB7cn0KY29yKHRyZWVzKQpgYGAKCi0gICBXaXIgc2VoZW4gZsO8ciBqZWRlcyBQYWFyIGFuIFZhcmlhYmxlbiBlaW5lbiBLb2VmZml6aWVudGVuIChhbHMKICAgIGjDpHR0ZW4gd2lyIGbDvHIgYWxsZSBgY29yKClgIHNlcGFyYXQgYXVmZ2VydWZlbikKLSAgIERpZSBEaWFnb25hbGUgaXN0IGltbWVyIDEsIGRhIGVpbmUgVmFyaWFibGUgc3RldHMgbWl0IHNpY2ggc2VsYnN0CiAgICBwZXJmZWt0IGtvcnJlbGllcnQgaXN0CgpBbHRlcm5hdGl2IGvDtm5uZW4gYXVzIGRlbSBgcnN0YXRpeGAgUGFrZXQgZGllIEZ1bmt0aW9uZW4gYGNvcl90ZXN0KClgLApgY29yX21hdCgpYCB1bmQgYGNvcl9nZXRfcHZhbCgpYCB2ZXJ3ZW5kZXQgd2VyZGVuLCB1bSBkaWUgS29lZmZpemllbnRlbgpiencuIGRpZSBwLVdlcnRlIGRlciB6dWdlaMO2cmlnZW4gVGVzdHMgZGFyenVzdGVsbGVuLiBIaWVyYmVpIHdpcmQKRmlsdGVydW5nIHVuZCBCZXJlY2hudW5nIHp1c2FtbWVuZ2VmYXNzdCBpbiBlaW5lIEZ1bmt0aW9uOgoKYGBge3J9CiMgQXVzZsO8aHJsaWNoZXIgS29ycmVsYXRpb25zdGVzdDoKdHJlZXMgJT4lCgljb3JfdGVzdCgpCiMgS29ycmVsYXRpb25zbWF0cml4CnRyZWVzICU+JQoJY29yX21hdCgpCiMgcC1XZXJ0ZS1NYXRyaXg6CnRyZWVzICU+JQoJY29yX21hdCgpICU+JQoJY29yX2dldF9wdmFsKCkKIyBrb25rcmV0ZSBCZW5lbm51bmc6CnRyZWVzICU+JQoJY29yX21hdChHaXJ0aCwgSGVpZ2h0LCBWb2x1bWUpCmBgYAoKVW0gZGllIE1hdHJpeCBldHdhcyBkZXRhaWxpZXJ0ZSBhbnp1c2VoZW4sIGvDtm5uZW4gd2lyIGVudHdlZGVyCmBjb3JfdGVzdCgpYCB2ZXJ3ZW5kZW4gb2RlciBgY29yX21hdCgpYCB1bmQgZGFubiBgY29yX2dhdGhlcmA6CgpgYGB7cn0KdHJlZXMgJT4lCgljb3JfbWF0KCkgJT4lCgljb3JfZ2F0aGVyKCkKYGBgCgpEYSBHcmFmaWtlbiBlaW5lIHNjaG5lbGxlcmUgT3JpZW50aWVydW5nc2hpbGZlIHNpbmQsIHdvbGxlbiB3aXIgZGllc2UKTWF0cml4IGRpcmVrdCBkYXJzdGVsbGVuLiBIaWVyenUgZ2lidCBlcyBpbiBgcnN0YXRpeGAgZGllIGBjb3JfcGxvdCgpYApGdW5rdGlvbjoKCmBgYHtyfQptcGcgJT4lCgljb3JfbWF0KHllYXIsIGRpc3BsLCBjeWwsIGN0eSwgaHd5KSAlPiUKCWNvcl9wbG90KCkKYGBgCgpFcyBsYXNzZW4gc2ljaCBkaXJla3QgZWluaWdlIERpbmdlIGFibGVzZW46CgotICAgRWluZSBuZWdhdGl2ZSBLb3JyZWxhdGlvbiB3aXJkIGluIGRpZXNlbSBCZWlzcGllbCByb3QgbWFya2llcnQsIGVpbmUKICAgIHBvc2l0aXZlIEtvcnJlbGF0aW9uIGR1cmNoIGJsYXUKCi0gICBKZSBkdW5rbGVyIGRlciBGYXJidG9uIGRlc3RvIHN0w6Rya2VyIGlzdCBkaWUgS29ycmVsYXRpb24KCi0gICBXZWnDnyBzdGVsbHQgaGllcmJlaSBLb3JyZWxhdGlvbnNrb2VmZml6aWVudGVuIGJlaSAwIGRhcgoKLSAgIEluIGRpZXNlbSBCZWlzcGllbCB3ZXJkZW4genVzw6R0emxpY2ggZGllIEtvZWZmaXppZW50ZW4KICAgIHVudGVyc2NoaWVkbGljaCBncm/DnyBkYXJnZXN0ZWxsdCAoamUgbmFjaCBXZXJ0KQoKLSAgIFNvZmVybiBlaW4gcC1XZXJ0IG9iZXJoYWxiIHZvbiAwLjA1IGxpZWd0LCB3aXJkIGRpZXNlciBlYmVuZmFsbHMKICAgIGR1cmNoIGVpbiBLcmV1eiBtYXJraWVydCAoPSBrZWluIFp1c2FtbWVuaGFuZykKCkVpbmlnZSBkaWVzZXIgT3B0aW9uZW4gbGFzc2VuIHNpY2ggYW5wYXNzZW4gKGA/Y29yX3Bsb3QoKWApOgoKYGBge3J9CmtvcnIgPC0gbXBnICU+JQoJY29yX21hdCh5ZWFyLCBkaXNwbCwgY3lsLCBjdHksIGh3eSkKIyBTaWduaWZpa2FuemxldmVsOgprb3JyICU+JQoJY29yX3Bsb3Qoc2lnbmlmaWNhbnQubGV2ZWwgPSAwLjEsIGluc2lnbmlmaWNhbnQgPSAiYmxhbmsiKQojIEZvcm0Ka29yciAlPiUKCWNvcl9wbG90KG1ldGhvZCA9ICJzcXVhcmUiKQojIERyZWllY2sgdnMgUXVhZHJhdAprb3JyICU+JQoJY29yX3Bsb3QodHlwZSA9ICJsb3dlciIpCiMgRmFyYmUgdW5kIExhYmVsCmtvcnIgJT4lCgljb3JfcGxvdChsYWJlbCA9IFRSVUUsIHBhbGV0dGUgPSBnZ3B1YnI6OmdldF9wYWxldHRlKGMoInRvbWF0bzMiLCAid2hpdGUiLCAic2VhZ3JlZW4iKSwgMjAwKSkKIyBVbXNvcnRpZXJlbjoKa29yciAlPiUKCWNvcl9yZW9yZGVyKCkgJT4lCgljb3JfcGxvdCgpCiMgS29tYmluYXRpb24gYXVzIGFsbGVtCmtvcnIgJT4lCgljb3JfcmVvcmRlcigpICU+JQoJY29yX3Bsb3QobWV0aG9kID0gImVsbGlwc2UiLCBzaWduaWZpY2FudC5sZXZlbCA9IDAuMSwgaW5zaWduaWZpY2FudCA9ICJibGFuayIsIHR5cGUgPSAidXBwZXIiLAoJCQkJCSBsYWJlbCA9IFRSVUUsIHBhbGV0dGUgPSBnZ3B1YnI6OmdldF9wYWxldHRlKGMoImRhcmtyZWQiLCAiY29ybnNpbGsiLCAiZGFya2N5YW4iKSwgMjAwKQoJCQkJCSApCgpgYGAKCkbDvHIgd2VpdGVyZSBPcHRpb25lbiBiZWkgZGVyIEFyYmVpdCBtaXQgS29ycmVsYXRpb25zZ3JhZmlrZW4gYmlldGV0IHNpY2gKZWJlbmZhbGxzIGRhcyBgZ2djb3JycGxvdGAgUGFrZXQgYW4uIEhpZXIgbGFzc2VuIHNpY2ggbm9jaCB6dXPDpHR6bGljaApFaW5zdGVsbHVuZ2VuIMOkbmRlcm4gbWl0IGRlc3NlbiBgZ2djb3JycGxvdCgpYCBGdW5rdGlvbi4gU2llaGUKW2hpZXJdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9nZ2NvcnJwbG90L3JlYWRtZS9SRUFETUUuaHRtbCkKZsO8ciBtZWhyIEluZm9ybWF0aW9uIHp1IGBnZ2NvcnJwbG90YC4KCiMjIyMgUmVncmVzc2lvbnNtb2RlbGxlCgpCZWkgZGVyIEtvcnJlbGF0aW9uc2FuYWx5c2UgYmV0cmFjaHRlbiB3aXIgWnVzYW1tZW5ow6RuZ2UuIFp1c2FtbWVuaMOkbmdlCm3DvHNzZW4gamVkb2NoIG5pY2h0IGthdXNhbCBzZWluLCBkLiBoLiBlcyBtYWcgc2VpbiwgZGFzcyBlaW5lIFZhcmlhYmxlCnRlbmRlbnppZWxsIG1pdCBlaW5lciBhbmRlcmVuIFZhcmlhYmxlIHN0ZWlndCwgamVkb2NoIG5pY2h0IGR1cmNoIGRlbgpFaW5mbHVzcyBkZXIgYW5kZXJlbiBWYXJpYWJsZS4KCkVpbiBzb2xjaGVyIFNhY2h2ZXJoYWx0IGthbm4ga29tcGxleCBzZWluLCBhYmVyIGjDpHVmaWcgc3RlY2t0IGRhaGludGVyCmVpbmUgZHJpdHRlIChvZGVyIG1laHJlcmUpIFZhcmlhYmxlLCBkaWUgYmVpZGUgVmFyaWFibGVuIHp1Z2xlaWNoCmJlaW5mbHVzc3QuIEFuIGRpZXNlciBTdGVsbGUgd29sbGVuIHdpciBkYXMgw7xibGljaGUgQmVpc3BpZWwgYmV0cmFjaHRlbiwKdW0gZGllc2VuIFVudGVyc2NoaWVkIHp1IGVybMOkdXRlcm46CgpNaXQgZWluZXIgZXJow7ZodGVuIEFuemFobCBhbiBTdMO2cmNoZW4gaW4gZWluZW0gYmVvYmFjaHRldGVuIEdlYmlldApzdGVpZ3QgYXVjaCBkaWUgR2VidXJ0ZW5yYXRlICh0YXRzw6RjaGxpY2hlcyBQaMOkbm9tZW4pOgoKLSAgIERpZSBLb3JyZWxhdGlvbiBsaWVndCBoaWVyYmVpIHZvciAoc2lnbmlmaWthbnQpCgotICAgS2F1c2FsIGlzdCBkaWVzZXIgWnVzYW1tZW5oYW5nIGplZG9jaCBuaWNodAoKLSAgIEVpbmUgZHJpdHRlIFZhcmlhYmxlIChkaWUgSW5kdXN0cmlhbGlzaWVydW5nKSBzY2hlaW50IGJlaWRlCiAgICBWYXJpYWJsZW4genUgYmVlaW5mbHVzc2VuOiBhdWYgZGVtIExhbmQgc2llZGVsbiBzaWNoZXIgZWhlciBGYW1pbGllbgogICAgYW4gdW5kIGluIFN0YWR0Z2ViaWV0ZW4gZ2lidCBlcyB3ZW5pZ2UgU3TDtnJjaGUuCgotICAgRGVtbmFjaCBpc3QgZGVyIHRhdHPDpGNobGljaGUga2F1c2FsZSBadXNhbW1lbmhhbmcgYmVpCiAgICBJbmR1c3RyaWFsaXNpZXJ1bmcgdW5kIEdlYnVydGVucmF0ZS9BbnphaGwgU3TDtnJjaGUKCktsZWluZXIgRWluc2NodWI6IGJldHJhY2h0ZW4gd2lyIGVpbmVuIEJlaXNwaWVsZGF0ZW5zYXR6IHp1IFN0w7ZyY2hlbiB1bmQKZGVyIEdlYnVydGVucmF0ZS4gV2lyIHNlaGVuLCBkYXNzIGRpZSAiU3RvcmNoZW5yYXRlIiB1bmQgZGllCkdlYnVydGVucmF0ZSBzdGFyayBwb3NpdGl2IGtvcnJlbGllcnQgaXN0ICh1bmQgQW56YWhsIHZvbiBTdMO2cmNoZW4gdW5kCkFuemFobCB2b24gQmFiaWVzKToKCmBgYHtyfQpsaWJyYXJ5KFRlYWNoaW5nRGVtb3MpCnN0b3JrIDwtIGFzX3RpYmJsZShzdG9yaykKc3RvcmsKc3RvcmsgJT4lCgljb3JfbWF0KCkgJT4lCgljb3JfcGxvdCgpCmBgYAoKTWl0IEhpbGZlIGRlciBwYXJ0aWVsbGVuIEtvcnJlbGF0aW9uIGvDtm5uZW4gd2lyIG3DtmdsaWNoZSBTdMO2cmZha3RvcmVuCmF1cyBkZXIgS29ycmVsYXRpb24gImVudGZlcm5lbiI6CgpgYGB7cn0KbGlicmFyeShwcGNvcikKc3RvcmsgJT4lCglwY29yKCkgJT4lICMgcGFydGllbGxlIEtvcnJlbGF0aW9uCgltYWdyaXR0cjo6ZXh0cmFjdDIoMSkgJT4lICMgZXJzdGVuIEVpbnRyYWcgZW50bmVobWVuCglyb3VuZChkaWdpdHMgPSAzKSAlPiUgIyBEZXppbWFsc3RlbGxlbiBydW5kZW4gYXVmIDMKCWNvcl9wbG90KCkKYGBgCgotICAgQW56YWhsIGRlciBTdMO2cmNoZSB1bmQgQW56YWhsIGRlciBCYWJpZXMgc2luZCBudW4gbW9kZXJhdCBuZWdhdGl2CiAgICBrb3JyZWxpZXJ0LgoKLSAgIFdlbm4gd2lyIGRpZSBTdG9yY2hyYXRlIHVuZCBkaWUgR2VidXJ0ZW5yYXRlIGJldHJhY2h0ZW4sIHNvIGxpZWd0CiAgICBoaWVyIG51ciBub2NoIGVpbmUgc2VociBzY2h3YWNoZSBLb3JyZWxhdGlvbiB2b3IuCgpLZWhyZW4gd2lyIHp1csO8Y2sgenVyIFJlZ3Jlc3Npb24uCgpXaWNodGlnIGJlaSBkZXIgUmVncmVzc2lvbnNhbmFseXNlIGlzdCwgZGFzcyB3aXIgZWluZW4ga2F1c2FsZW4KWnVzYW1tZW5oYW5nIHVudGVyc3RlbGxlbi4gRXMgc29sbHRlLCBhbmRlcnMgYWxzIGJlaSBkZW0KU3RvcmNoZW5iZWlzcGllbCwgZWluIGVjaHRlciBrYXVzYWxlciBadXNhbW1lbmhhbmcgdm9ybGllZ2VuLlwKQmVpIGRlbSBEYXRlbnNhdHogZGVyIEtpcnNjaGLDpHVtZSBrYW5uIGVzIGd1dCBzZWluLCBkYXNzIGRlciBEdXJjaG1lc3NlcgpkZXMgQmF1bXMgZWluZSB3aWNodGlnZSBSb2xsZSBzcGllbHQsIHNvZGFzcyBkZXIgQmF1bSBow7ZoZXIgd2FjaHNlbgprYW5uLgoKSW4gUiB3ZXJkZW4gbGluZWFyZSBSZWdyZXNzaW9uc21vZGVsbGUgdHlwaXNjaGVyd2Vpc2UgbWl0IGRlciBgbG0oKWAgKD0KIkxpbmVhcmVzIE1vZGVsbCIpIEZ1bmt0aW9uIGR1cmNoZ2Vmw7xocnQuIEltIGVpbmZhY2hzdGVuIEZhbGwsIHNvIHdpZQp3aXIgZXMgYmVyZWl0cyBtZWhyZmFjaCBnZXNlaGVuIGhhYmVuLCB3aXJkIGhpZXJtaXQgZWluZSAicGFzc2VuZGUiCkdlcmFkZSBkdXJjaCB1bnNlcmUgUHVua3RlIGdlem9nZW4uIEVyc3RlbGxlbiB3aXIgZWluIHNvbGNoZXMgTW9kZWxsIGbDvHIKZGVuIER1cmNobWVzc2VyIHVuZCBkaWUgSMO2aGUgZGVyIEtpcnNjaGLDpHVtZToKCmBgYHtyfQpsaW5fbW9kZWxsIDwtIGxtKEhlaWdodCB+IEdpcnRoLCBkYXRhID0gdHJlZXMpCnN1bW1hcnkobGluX21vZGVsbCkKYGBgCgotICAgSGllciBzZWhlbiB3aXIgZXJuZXV0IGRpZSBWZXJ3ZW5kdW5nIHZvbiBkZW0gVGlsZGUgIlx+IiBTeW1ib2wKLSAgIExpbmtzIHZvbiBkZXIgVGlsZGUgc3RlaHQgZGllIGFiaMOkbmdpZ2UgVmFyaWFibGUsIHJlY2h0cyB2b24gZGVyCiAgICBUaWxkZSBzdGVodCBkaWUgdW5hYmjDpG5naWdlIFZhcmlhYmxlCi0gICBEZXIgRWZmZWt0IGRlciB1bmFiaMOkbmdpZ2VuIFZhcmlhYmxlbiB3aXJkIGF1ZiBkaWUgYWJow6RuZ2lnZQogICAgVmFyaWFibGUgYmV0cmFjaHRldCwgei4gQi4gd2VsY2hlbiBFaW5mbHVzcyBkZXIgRHVyY2htZXNzZXIgYXVmIGRpZQogICAgQmF1bWjDtmhlIGhhdAotICAgRGVyIHAtV2VydCBnYW56IHVudGVuIGdpYnQgYW4sIG9iIGRhcyBNb2RlbGwgc2lnbmlmaWthbnQgaXN0LCBkLiBoLgogICAgb2IgZGVyIER1cmNobWVzc2VyIGVpbmVuIEVpbmZsdXNzIGF1ZiBkaWUgQmF1bWjDtmhlIGhhdDoKICAgIC0gICBIMDogRGllIHVuYWJow6RuZ2lnZW4gVmFyaWFibGVuIGhhYmVuIGtlaW5lbiBFaW5mbHVzcwoKICAgIC0gICBIMTogRXMgZ2lidCBtaW5kZXN0ZW5zIGVpbmUgdW5hYmjDpG5naWdlIFZhcmlhYmxlIG1pdCBlaW5lbQogICAgICAgIEVpbmZsdXNzCi0gICBEYSBkZXIgcC1XZXJ0IHJlbGF0aXYga2xlaW4gaXN0LCBnZWhlbiB3aXIgZGF2b24gYXVzLCBkYXNzIGRlcgogICAgRHVyY2htZXNzZXIgZWluZW4gc2lnbmlmaWthbnRlbiBFaW5mbHVzcyBhdWYgZGllIEJhdW1ow7ZoZSBoYXQKLSAgIE11bHRpcGxlIFItc3F1YXJlZCAoUi1RdWFkcmF0KSBnaWJ0IGRpZSBHw7x0ZSBkZXMgTW9kZWxscyBhbjoKICAgIC0gICBXZXJ0ZSB6d2lzY2hlbiAwIChzY2hsZWNodCkgdW5kIDEgKHBlcmZla3QpCiAgICAtICAgIldpZSB2aWVsIFZhcmlhbnoga2FubiBkYXMgTW9kZWxsIGJlc2NocmVpYmVuIiA9ICJXaWUgdmllbAogICAgICAgIHdlaWNoZW4gZGllIFdlcnRlIHZvbiBkZXIgTW9kZWxsa3VydmUvR2VyYWRlIGFiPyIKICAgIC0gICBEYSBSLVF1YWRyYXQgPSAwLjI3IGlzdCwgc2VoZW4vc2NobHVzc2ZvbGdlcm4gd2lyIGVybmV1dCwgZGFzcwogICAgICAgIGRlciBEdXJjaG1lc3NlciBFaW5mbHVzcyBhdWYgZGllIEJhdW1ow7ZoZSBoYXQsIGFiZXIgd29obG3DtmdsaWNoCiAgICAgICAgbmljaHQgZGVyIGVpbnppZ2UgRmFrdG9yIGlzdCwgZGVyIGVpbmVuIEVpbmZsdXNzIGhhdCAoZGVubiBzb25zdAogICAgICAgIHfDpHJlIFItUXVhZHJhdCBzZWhyIG5haGUgYmVpIDEpCi0gICBVbnRlciBgQ29lZmZpY2llbnRzYCBzZWhlbiB3aXIgZGllIE1vZGVsbGdlcmFkZToKICAgIGBIZWlnaHQgPSAxOCw5ICsgMC4xMjcgKiBHaXJ0aGAKCldvbGxlbiB3aXIgZGllIE1vZGVsbGdlcmFkZSBpbiBlaW5lIEdyYWZpayBlaW56ZWljaG5lbiwgZ2lidCBlcyBtZWhyZXJlCk3DtmdsaWNoa2VpdGVuLiBXaXIga8O2bm5lbiBgcHJlZGljdCgpYCBhdWYgZGFzIE1vZGVsbCBhbndlbmRlbiwgdW0gZGllClZvcmhlcnNhZ2VuLCBkLiBoLiB5LVdlcnRlLCBkZXMgTW9kZWxscyB6dSBlcmhhbHRlbiB1bmQgZGllc2UgYWxzIExpbmllbgplaW56ZWljaG5lbi4gQWx0ZXJuYXRpdiBrw7ZubmVuIHdpciBlaW5mYWNoIGRhcyBNb2RlbGwgaW4gYGdlb21fc21vb3RoKClgCmVyc3RlbGxlbiBsYXNzZW46CgpgYGB7cn0KIyBEaWUgVm9yaGVyc2FnZW4gZGVzIE1vZGVsbHMgdmVyd2VuZGVuOgpnZ3Bsb3QodHJlZXMsIGFlcyhHaXJ0aCwgSGVpZ2h0KSkgKwoJZ2VvbV9wb2ludCgpICsKCWdlb21fbGluZShhZXMoeSA9IHByZWRpY3QobGluX21vZGVsbCkpLCBjb2xvciA9ICJyZWQiKQojIGdlb21fc21vb3RoIHZlcndlbmRlbjoKZ2dwbG90KHRyZWVzLCBhZXMoR2lydGgsIEhlaWdodCkpICsKCWdlb21fcG9pbnQoKSArCglnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBmb3JtdWxhID0geSB+IHgsIGNvbG9yID0gInJlZCIsIHNlID0gRkFMU0UpCmBgYAoKQmV0cmFjaHRlbiB3aXIgYWJzY2hsaWXDn2VuZCBub2NoIGRlbiBEdXJjaG1lc3NlciwgZGllIEjDtmhlIHVuZCBkYXMKVm9sdW1lbi4KCmBgYHtyfQpnZ3Bsb3QodHJlZXMsIGFlcyh4ID0gR2lydGgsIHkgPSBWb2x1bWUpKSArCglnZW9tX3BvaW50KCkgKwoJZ2VvbV9zbW9vdGgoKQpnZ3Bsb3QodHJlZXMsIGFlcyh4ID0gSGVpZ2h0LCB5ID0gVm9sdW1lKSkgKwoJZ2VvbV9wb2ludCgpICsKCWdlb21fc21vb3RoKCkKYGBgCgotICAgV2VubiB3aXIgRHVyY2htZXNzZXIgdW5kIFZvbHVtZW4gYmV0cmFjaHRlbiwga8O2bm50ZSBkZXIgWnVzYW1tZW5oYW5nCiAgICB3b2hsbcO2Z2xpY2ggKGxlaWNodCkgcXVhZHJhdGlzY2ggKEt1cnZlKSBzZWluLgoKLSAgIEJlaSBIw7ZoZSB1bmQgVm9sdW1lbiBpc3QgZGFzIG5pY2h0IGdhbnogZWluZGV1dGlnLiBIaWVyIGvDtm5udGUgZXMKICAgIHNpY2ggYWJlciB2ZXJtdXRsaWNoIHVtIGVpbmVuIGxpbmVhcmVuIFp1c2FtbWVuaGFuZyBoYW5kZWxuLgoKRXMga8O2bm5lbiBhdWNoIGFsbGUgZHJlaSBrb250aW51aWVybGljaGVuIFZhcmlhYmxlbiBnbGVpY2h6ZWl0aWcKYmV0cmFjaHRldCB3ZXJkZW4uIEhpZXJ6dSB3ZXJkZW4gR2l0dGVyIGVyc3RlbGx0LCB3b2JlaSBlaW5lIFZhcmlhYmxlIGluCkludGVydmFsbGUgbWl0IGVpbmVyIGBjdXRfKigpYCBGdW5rdGlvbiBhdWZnZXRlaWx0IHdpcmQuIFVtCmJlaXNwaWVsc3dlaXNlIGRlbiBadXNhbW1lbmhhbmcgdm9uIFZvbHVtZW4sIER1cmNobWVzc2VyIHVuZCBIw7ZoZSB6dQpiZXRyYWNodGVuLCB3aXJkIGhpZXIgZWluIG5vcm1hbGVzIFN0cmV1ZGlhZ3JhbW0gbWl0IER1cmNobWVzc2VyIHVuZApWb2x1bWVuIGVyc3RlbGx0LCB3b2JlaSBHaXR0ZXIgYmFzaWVyZW5kIGF1ZiBkZW4gSW50ZXJ2YWxsZW4gZGVyIEjDtmhlCmVpbmdldGVpbHQgd2VyZGVuOgoKYGBge3J9CmdncGxvdCh0cmVlcywgYWVzKHggPSBHaXJ0aCwgeSA9IFZvbHVtZSkpICsKCWdlb21fcG9pbnQoKSArCglnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFKSArCglmYWNldF93cmFwKH4gY3V0X3dpZHRoKEhlaWdodCwgMykpCmBgYAoKQWJzY2hsaWXDn2VuZCBub2NoIGVpbiBBdXNibGljaywgd2FzIHNpY2ggbWl0IFJlZ3Jlc3Npb25zbW9kZWxsZW4gbm9jaAptYWNoZW4gbMOkc3N0OiBFYmVuZmFsbHMga8O2bm5lbiB3aXIgenUgVm9sdW1lbiB1bmQgSMO2aGUvRHVyY2htZXNzZXIKUmVncmVzc2lvbnNtb2RlbGxlIGVyc3RlbGxlbjoKCmBgYHtyfQpsaW5fbW9kZWxsMiA8LSBsbShWb2x1bWUgfiBHaXJ0aCwgZGF0YSA9IHRyZWVzKQpzdW1tYXJ5KGxpbl9tb2RlbGwyKQpsaW5fbW9kZWxsMyA8LSBsbShWb2x1bWUgfiBIZWlnaHQsIGRhdGEgPSB0cmVlcykKc3VtbWFyeShsaW5fbW9kZWxsMykKYGBgCgpEZXIgRHVyY2htZXNzZXIgc2NoZWludCByZWxhdGl2IGd1dCB6dSBzZWluLCB1bSBkYXMgVm9sdW1lbiB6dSBiZXN0aW1tZW4KKFItUXVhZHJhdCA9IDAuOTM1KS4gQWxsZXJkaW5ncyB3aXNzZW4gd2lyIGF1Y2gsIGRhc3Mgc2ljaCBkYXMgVm9sdW1lbgppbSBBbGxnZW1laW5lbiAodm9uIHp5bGluZGVyLWFydGlnZW4gT2JqZWt0ZW4pIGJlcmVjaG5lbiBsw6Rzc3QgZHVyY2ggZGllCkZvcm1lbDoKCmBwaSAqIFJhZGl1c14yICogSMO2aGVgCgpIaWVyenUgbXVzcyBmw7xyIGRlbiBEdXJjaG1lc3NlciBgSShYICoqIDIpYCBnZXNjaHJpZWJlbiB3ZXJkZW4sIGRhbWl0CmRlciBEdXJjaG1lc3NlciBhdWNoIHF1YWRyaWVydCBpbiBkaWUgRm9ybWVsIGVpbmdlaHQuIFN0YXR0IGRlcgpNdWx0aXBsaWthdGlvbiBgKmAgKFx+IEFkZGl0aW9uIHVuZCBNdWx0aXBsaWthdGlvbikgd2lyZCBpbiBNb2RlbGxlbiBgOmAKZ2VzY2hyaWViZW4uCgpCZXRyYWNodGVuIHdpciB6dW7DpGNoc3QgZWluIE1vZGVsbCBtaXQgZGVtIER1cmNobWVzc2VyIHp1bSBRdWFkcmF0OgoKYGBge3J9Cmxpbl9tb2RlbGw0IDwtIGxtKFZvbHVtZSB+IEkoR2lydGggKiogMiksIGRhdGEgPSB0cmVlcykKc3VtbWFyeShsaW5fbW9kZWxsNCkKZ2dwbG90KHRyZWVzLCBhZXMoR2lydGgsIFZvbHVtZSkpICsKCWdlb21fcG9pbnQoKSArCglnZW9tX3Ntb290aChmb3JtdWxhID0geSB+IEkoeCAqKiAyKSwgbWV0aG9kID0gImxtIikKYGBgCgpUYXRzw6RjaGxpY2ggc2NoZWludCBlaW5lIHF1YWRyYXRpc2NoZSBLdXJ2ZSBndXQgenUgcGFzc2VuLiBEYXJhdWYKYmFzaWVyZW5kIGvDtm5uZW4gd2lyIHVuc2VyIE1vZGVsbCB3ZWl0ZXIgdmVyYmVzc2Vybi4gSGllcnp1Cm11bHRpcGxpemllcmVuIHdpciBkaWUgSMO2aGUgbWl0IGRlbSBEdXJjaG1lc3NlciBpbSBRdWFkcmF0OgoKYGBge3J9Cmxpbl9tb2RlbGw1IDwtIGxtKFZvbHVtZSB+IEhlaWdodDpJKEdpcnRoICoqIDIpLCBkYXRhID0gdHJlZXMpCnN1bW1hcnkobGluX21vZGVsbDUpCmBgYAoKTWl0IGVpbmVtIFItUXVhZHJhdCB2b24gMC45NzggaXN0IGRhcyBNb2RlbGwgc29nYXIgbm9jaCBldHdhcyBiZXNzZXIgYWxzCnp1dm9yIHVuZCBzY2hlaW50IG51biBiZWluYWhlIHBlcmZla3QgZGFzIFZvbHVtZW4gZGVyIEtpcnNjaGLDpHVtZSBhbmhhbmQKdm9uIER1cmNobWVzc2VyIHVuZCBIw7ZoZSB6dSBiZXN0aW1tZW4sIGF1Y2ggd2VubiBkZXIgWnVzYW1tZW5oYW5nCmJlcmVpdHMgYmVrYW5udCB3YXIuCgpXYXMga8O2bm50ZSBkZXIgR3J1bmQgc2VpbiwgZGFzcyB0eXBpc2NoZXJ3ZWlzZSAodHJvdHogZWluZGV1dGlnZXIKZnVua3Rpb25hbGVyIEJlemllaHVuZykga2VpbiBwZXJmZWt0ZXIgUi1RdWFkcmF0IHZvbiAxIGVyaGFsdGVuIHdlcmRlbgprYW5uPwoKIyBBdWZnYWJlbgoKSW4gZGllc2VyIMOcYnVuZyB3b2xsZW4gd2lyIHVucyBtaXQga29udGludWllcmxpY2hlbiBEYXRlbiBiZXNjaMOkZnRpZ2VuLgpIaWVyenUgYmV0cmFjaHRlbiB3aXIgZGVuIERhdGVuc2F0eiBgYWlycXVhbGl0eWAsIHdlbGNoZXIgdmVyc2NoaWVkZW5lCk1lc3N1bmdlbiBiZXrDvGdsaWNoIEx1ZnRxdWFsaXTDpHQgendpc2NoZW4gTWFpIHVuZCBTZXB0ZW1iZXIgaW0gSmFocmUKMTk3MyB1bWZhc3N0LlwKRsO8ciBtZWhyIEluZm9ybWF0aW9uIHNpZWhlIGA/YWlycXVhbGl0eWAuCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmFpcnF1YWxpdHkgPC0gYXNfdGliYmxlKGFpcnF1YWxpdHkpCiMgRmFrdG9yZW4gYXVzIE1vbmF0IHVuZCBUYWcgbWFjaGVuOgphaXJxdWFsaXR5IDwtIGFpcnF1YWxpdHkgJT4lCiAgbXV0YXRlX2F0KGMoIk1vbnRoIiwgIkRheSIpLCBmYWN0b3IpCiMgQWx0ZXJuYXRpdjoKIyBhaXJxdWFsaXR5ICU+JQojICAgbXV0YXRlKE1vbnRoID0gZmFjdG9yKE1vbnRoKSwgRGF5ID0gZmFjdG9yKERheSkpCmFpcnF1YWxpdHkKYGBgCgojIyDDnGJ1bmdzYXVmZ2FiZW4KCjEuICBTY2hhZmZlbiBTaWUgc2ljaCB6dW7DpGNoc3QgZWluZSAoc3RhdGlzdGlzY2hlKSDDnGJlcnNpY2h0IMO8YmVyIGRpZQogICAgRGF0ZW4uIFdpZSB2aWVsZSBCZW9iYWNodHVuZ2VuIHVuZCBWYXJpYWJsZW4gd3VyZGVuIGVyZmFzc3Q/CjIuICBWZXJzaWNoZXJuIFNpZSBzaWNoLCBkYXNzIGRpZSBEYXRlbiBlaW5pZ2VybWHDn2VuIGtvbnRpbnVpZXJsaWNoCiAgICBpbm5lcmhhbGIgZGVzIFplaXRyYXVtcyBlcmZhc3N0IHd1cmRlbi4KMy4gIFZlcmFuc2NoYXVsaWNoZW4gU2llIGRpZSBWZXJ0ZWlsdW5nZW4gZGVyIGtvbnRpbnVpZXJsaWNoZW4gVmFyaWFibGVuCiAgICB2b24gYGFpcnF1YWxpdHlgLiBXZWxjaGUgQXNwZWt0ZSBmYWxsZW4gaW5zIEF1Z2UgdW5kIGvDtm5udGVuCiAgICBpbnRlcmVzc2FudCBzZWluPwo0LiAgRmluZGVuIFNpZSBadXNhbW1lbmjDpG5nZSB6d2lzY2hlbiBkZW4gdmVyc2NoaWVkZW5lbiBWYXJpYWJsZW4uCiAgICAxLiAgVmlzdWFsaXNpZXJlbiBTaWUgZsO8ciBhbGxlIHNpbm52b2xsZW4gVmFyaWFibGVuIGRpZSBwYWFyd2Vpc2VuCiAgICAgICAgQmV6aWVodW5nZW4uIFNpbmQgZGllc2UgYWxsZSBsaW5lYXI/CiAgICAyLiAgQmVyZWNobmVuIFNpZSBmw7xyIGFsbGUgc2lubnZvbGxlbiBWYXJpYWJsZW4gZWluZQogICAgICAgIEtvcnJlbGF0aW9uc21hdHJpeC4gQmlldGV0IHNpY2ggUGVhcnNvbnMgS29ycmVsYXRpb25za29lZmZpemllbnQKICAgICAgICBhbj8KICAgIDMuICBCZXN0aW1tZW4gU2llIGR1cmNoIFZpc3VhbGlzaWVydW5nLCB3byBadXNhbW1lbmjDpG5nZSB2b3JsaWVnZW4uCjUuICBFcnN0ZWxsZW4gU2llIGbDvHIgcmVsZXZhbnRlIChrYXVzYWxlKSBadXNhbW1lbmjDpG5nZQogICAgUmVncmVzc2lvbnNtb2RlbGxlLgo=