library(broom)        # tidy model data
library(performance)  # regression diagnostic
library(estimatr)     # robust standard-errors

library(tidyverse)
ggplot2::theme_set(theme_minimal())
##  tidy lm model results with broom

tidy_lm <- function(fit) {
  tidy(fit) %>% mutate_if(is.numeric, round, 2)
}

glance_lm <- function(fit) {
  glance(fit) %>% 
    select(r.squared, adj.r.squared, statistic, p.value, df, nobs) %>% 
    mutate_if(is.numeric, round, 2)
}

Wunsch I · Perfekte Daten

Eine Fee 🧚 die Wünsche 🧞 erfüllt?

Den Wunsch 🪄 nach perfekten Daten 📈 erfüllt sie nicht 😥.

Die Fee 🧚 überlegt, nutzt R 👩‍💻 und schafft fast perfekte Daten 📈.

param_const <- 500
param_alter <- 25

n <- 1000
sd_einkommen <-  100

dt_raw <- 
  tibble( 
    id = 1:n,
    alter = seq(20, 60, length.out = n) %>% round(2),
    einkommen_linear = round(param_const + param_alter*alter)
    ) %>% 
  mutate(einkommen = round(einkommen_linear + rnorm(n, sd = sd_einkommen)))

\[ Einkommen_{ID} = 500 + 25 \cdot Alter_{ID} \: + ( \: Fehler_{ID} \: )\]

ggplot(dt_raw, aes(x = alter, y = einkommen)) +
  geom_point(alpha = 0.2) +
  geom_smooth(method = lm)

Wunsch II · Diagnose für SoWis

Die Studierenden 🙇🙇🙇🙇 hatten einen eigenen Wunsch 🪄 an die Fee 🧚.

Kannst Du uns Regressions-Diagnose 📈 mit einer sozialwissenschaftlichen Geschichte 📚 erklären?

Daten Fee

Wir stellen uns Postbeamte 📯 vor die regelmäßig Lohnerhöhungen 💰 strikt nach dem Senioritätsprinzip 🥸 erhalten.

Wir haben also ein Modell 📈 bei dem das Einkommen 💰 vom Alter 🧑👴 abhängt.

Das wahre Einkommen 💰 (einkommen_linear) ist uns bekannt und unsere beobachteten Daten 🕵️ (einkommen) weichen davon normal-verteilt 🧮 ab

So hat die Fee 🧚 uns die Daten 📊 gegeben.

ggplot(dt_raw, aes(x = alter, y = einkommen)) +
  geom_point(alpha = 0.2) +
  geom_jitter(aes(y = einkommen_linear),
              width = 0.1, alpha = 0.5,
              colour = "darkgreen", shape = ".")


DT::datatable(dt_raw)

Modell (fast) perfekt

Wir bekommen schöne Ergebnisse bei der Schätzung des Modells. 😄

fit <- lm(einkommen ~ alter, data = dt_raw)
tidy_lm(fit)
glance_lm(fit)
check_model(fit)

Annahmen (1-7)

  1. korrekte Spezifikation (Linearität)
  2. Erwartungswert der Residuen (lokal) = 0
  3. Normalverteilte Residuen
  4. Varianzhomogenität (gleichmäßige Streuung Residuen)
  5. Berücksichtigung einflussreicher Fälle (keine Ausreisser)
  6. keine Multikollinearität (geringe Korrelation unabhängiger Variablen)
  7. keine Autokorrelation der Residuen (insbes. Zeitreihen)

Korrekte Spezifikation

Dritt-Variablen

Briefträger ✉️ arbeiten oft nur halbtags 🕐 und verdienen 💰 dann auch nur die Hälfte.

dt <-
  dt_raw %>% 
  mutate(einkommen = if_else(id %% 3 == 0, einkommen/2 + rnorm(n, sd = 50), einkommen),
         halbtags = if_else(id %% 3 == 0, "ja", "nein") %>% fct_relevel("ja", after = Inf))

ggplot(dt, aes(x = alter, y = einkommen, colour = halbtags)) +
  geom_point(alpha = 0.2)

Ohne Dritt-Variable 😟

fit <- lm(einkommen ~ alter, data = dt)
tidy_lm(fit)
glance_lm(fit)
check_model(fit)

Mit Dritt-Variable 🙂

fit <- lm(einkommen ~ alter + halbtags, data = dt)
tidy_lm(fit)
glance_lm(fit)
check_model(fit)

Noch besser mit Dritt-Variable und Interaktions-Effekt 😃

fit <- lm(einkommen ~ alter*halbtags, data = dt)
tidy_lm(fit)
glance_lm(fit)
check_model(fit)

Funktionale Form

Bei der Bundespost 📯 wurde eine Bezahlung 💰 nach Humankapital eingeführt.

Es wurde erkannt, dass bei jungen Beamten 🧑 die Produktivität stärker wächst als bei Älteren 🧓. Daher erhalten sie höhere Lohnzuwächse 💰.

dt <-
  dt_raw %>% 
  mutate(einkommen_polynom = -300 + 80*alter - 0.725*alter^2,
         einkommen = einkommen_polynom + rnorm(n, sd = sd_einkommen))

ggplot(dt, aes(x = alter, y = einkommen)) +
  geom_point(alpha = 0.2) +
    geom_jitter(aes(y = einkommen_polynom),
                width = 0.1, alpha = 0.5,
                colour = "darkblue", shape = ".")

Ein lineares Modell ist falsch spezifiziert. 😟 Die Regressions-Diagnose zeigt dies. ☝️

fit <- lm(einkommen ~ alter, data = dt)
tidy_lm(fit)
glance_lm(fit)
check_model(fit)

Korrekt ist ein Modell mit einem Polynom (hier einem quadratischem Term). 😃

fit <- lm(einkommen ~ alter + I(alter^2), data = dt)
tidy_lm(fit)
glance_lm(fit)
check_model(fit)

( Den R Hinweis auf Multi-Kollinearität ignorieren wir einfach. Natürlich gibt es einen Zusammenhang zwischen alter und dem quadrierten alter^2. )

Varianzhomogenität

Bei der Post 📯 haben hohe Beamte und Minster ein höheres Einkommen 💰 als einfache Beamte. Aber nicht alle werden hohe Beamte 🧑‍💼 oder Post-Minister 🏤.

dt <- dt_raw %>% mutate(einkommen = einkommen_linear + id/500 * rnorm(n, sd = sd_einkommen))

ggplot(dt, aes(x = alter, y = einkommen)) +
  geom_point(alpha = 0.2) +
    geom_jitter(aes(y = einkommen_linear),
                width = 0.1, alpha = 0.5,
                colour = "darkgreen", shape = ".")

Im Standard-Modell erhalten wir verzerrte Standard-Fehler. 😟

fit <- lm(einkommen ~ alter, data = dt)
tidy_lm(fit)
glance_lm(fit)
check_model(fit)

Besser ist ein Modell mit robusten 💪 Standard-Fehlern. 😄

fit <- lm_robust(einkommen ~ alter, data = dt)
tidy_lm(fit)
glance(fit) %>% mutate_if(is.numeric, round, 2) 

Multikollinearität

Ältere 👴👵 Postbeamte 📯 sind kleiner als Jüngere 👧👦.

dt <- dt_raw %>% mutate(groesse_cm = round(seq(180, 160, length.out = n) + rnorm(n, sd = 2.5), 3))

ggplot(dt, aes(x = alter, y = groesse_cm)) +
  geom_point(alpha = 0.2) +
  geom_smooth(method = lm)

Es gibt dann auch einen Zusammenhang 📉 zwischen groesse_cm 👵 und einkommen 💰, natürlich keinen kausalen ⚙️.

ggplot(dt, aes(x = groesse_cm, y = einkommen)) +
  geom_point(alpha = 0.2) +
  geom_smooth(method = lm)

fit <- lm(einkommen ~ groesse_cm, data = dt)
tidy_lm(fit)
glance_lm(fit)

Nehmen wir alter 👵 und groesse_cm 📐 in einem Modell auf 📉 haben wir Multikollinearität 🔗 😟

fit <- lm(einkommen ~ alter + groesse_cm, data = dt)
tidy_lm(fit)
glance_lm(fit)
check_model(fit)

Multikollinearität im Modell 📉 erkennen 🕵️ wir auch an der sehr starken Korrellation der unabhängigen Variablen.

dt %>% 
  select(alter, einkommen) %>% 
  cor() %>% 
  as_tibble() %>% 
  mutate_all(round, 2)

Ausreisser

Bei der Datenübertragung 📡 gab es einen Fehler ⚠️ und die letzte Ziffer 🔢 wurde bei einigen Gehaltsangaben 💰 entfernt.

id_random <- runif(80, min = 1, max = n) %>% round()

dt <-
  dt_raw %>% 
  mutate(einkommen = if_else(id %in% id_random, round(einkommen/10), einkommen))

ggplot(dt, aes(x = alter, y = einkommen)) +
  geom_point(alpha = 0.2)

Wir erhalten eine verzerrte Schätzung. 😟

fit <- lm(einkommen ~ alter, data = dt)
tidy_lm(fit)
glance_lm(fit)
check_model(fit)

Die falschen Beobachtungen müssen wir entfernen (oder korrigieren). 😃

dt_tmp <- dt %>% filter(einkommen > 500)

fit <- lm(einkommen ~ alter, data = dt_tmp)
tidy_lm(fit)
glance_lm(fit)
check_model(fit)

Autokorrelation

Die Postbeamten 📯 wurden 2010 und 2020 📅 nach ihrem Einkommen 💰 befragt. ☎️

dt_tmp <-
  dt_raw %>% 
  mutate(einkommen = round(einkommen - param_alter*10 + rnorm(n, sd = 10)),
         alter = alter - 10,
         befragung = 2010) %>% 
  filter(alter >= 20)

dt <-
  dt_raw %>% 
  mutate(befragung = 2020) %>% 
  rbind(dt_tmp) %>% 
  arrange(id, befragung) %>% 
  relocate(befragung, .after = id)

DT::datatable(dt %>% filter(id > 500))  # Daten ab ID 500 um Panel-Struktur zu verdeutlichen 

Postbeamte 📯 die 2010 📅 älter als 50 waren sind inzwischen in Pension 👵 und haben an der 2020 Befragung ☎️ nicht mehr teilgenommen.

dt_tmp <- dt %>% mutate(befragung = factor(befragung))

ggplot(dt_tmp, aes(x = alter, y = einkommen, colour = befragung)) +
  geom_point(alpha = 0.2)

Im Modell (n = 1750) sind Beobachtungen (und Residuen) nicht mehr unabhängig. 😟 Wir haben ja viele zweimal befragt. ☎️

fit <- lm(einkommen ~ alter, data = dt_tmp)
tidy_lm(fit)
glance_lm(fit)
check_model(fit)

Aufgabe

Diskutieren Sie 🙇🙇🙇 die Regressions-Annahmen 📈 und zeigen Sie ob und wie Verletzungen 🏥 der Annahmen erkannt 🕵️ werden können.


LS0tCnRpdGxlOiAiUmVncmVzc2lvbnMtRGlhZ25vc2Ugwrcg8J+UjSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwotLS0KCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShicm9vbSkgICAgICAgICMgdGlkeSBtb2RlbCBkYXRhCmxpYnJhcnkocGVyZm9ybWFuY2UpICAjIHJlZ3Jlc3Npb24gZGlhZ25vc3RpYwpsaWJyYXJ5KGVzdGltYXRyKSAgICAgIyByb2J1c3Qgc3RhbmRhcmQtZXJyb3JzCgpsaWJyYXJ5KHRpZHl2ZXJzZSkKZ2dwbG90Mjo6dGhlbWVfc2V0KHRoZW1lX21pbmltYWwoKSkKYGBgCgoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CiMjICB0aWR5IGxtIG1vZGVsIHJlc3VsdHMgd2l0aCBicm9vbQoKdGlkeV9sbSA8LSBmdW5jdGlvbihmaXQpIHsKICB0aWR5KGZpdCkgJT4lIG11dGF0ZV9pZihpcy5udW1lcmljLCByb3VuZCwgMikKfQoKZ2xhbmNlX2xtIDwtIGZ1bmN0aW9uKGZpdCkgewogIGdsYW5jZShmaXQpICU+JSAKICAgIHNlbGVjdChyLnNxdWFyZWQsIGFkai5yLnNxdWFyZWQsIHN0YXRpc3RpYywgcC52YWx1ZSwgZGYsIG5vYnMpICU+JSAKICAgIG11dGF0ZV9pZihpcy5udW1lcmljLCByb3VuZCwgMikKfQpgYGAKCgojIFd1bnNjaCBJIMK3IFBlcmZla3RlIERhdGVuCgpFaW5lIEZlZSDwn6eaIGRpZSBXw7xuc2NoZSDwn6eeIGVyZsO8bGx0PwoKIVtdKGJpbGRlci9mYWlyLmpwZykKCkRlbiBXdW5zY2gg8J+qhCBuYWNoIHBlcmZla3RlbiBEYXRlbiDwn5OIIGVyZsO8bGx0IHNpZSBuaWNodCDwn5ilLgogCkRpZSBGZWUg8J+nmiDDvGJlcmxlZ3QsIG51dHp0IFtfX1JfX10oaHR0cHM6Ly9lZHVjYXRpb24ucnN0dWRpby5jb20vbGVhcm4vYmVnaW5uZXIvKSDwn5Gp4oCN8J+SuyB1bmQgc2NoYWZmdCBmYXN0IHBlcmZla3RlIERhdGVuIPCfk4guCgpgYGB7cn0KcGFyYW1fY29uc3QgPC0gNTAwCnBhcmFtX2FsdGVyIDwtIDI1CgpuIDwtIDEwMDAKc2RfZWlua29tbWVuIDwtICAxMDAKCmR0X3JhdyA8LSAKICB0aWJibGUoIAogICAgaWQgPSAxOm4sCiAgICBhbHRlciA9IHNlcSgyMCwgNjAsIGxlbmd0aC5vdXQgPSBuKSAlPiUgcm91bmQoMiksCiAgICBlaW5rb21tZW5fbGluZWFyID0gcm91bmQocGFyYW1fY29uc3QgKyBwYXJhbV9hbHRlciphbHRlcikKICAgICkgJT4lIAogIG11dGF0ZShlaW5rb21tZW4gPSByb3VuZChlaW5rb21tZW5fbGluZWFyICsgcm5vcm0obiwgc2QgPSBzZF9laW5rb21tZW4pKSkKYGBgCgoKJCQgRWlua29tbWVuX3tJRH0gPSA1MDAgKyAyNSBcY2RvdCBBbHRlcl97SUR9IFw6ICsgKCBcOiBGZWhsZXJfe0lEfSBcOiApJCQKCmBgYHtyfQpnZ3Bsb3QoZHRfcmF3LCBhZXMoeCA9IGFsdGVyLCB5ID0gZWlua29tbWVuKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjIpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSBsbSkKYGBgCgoKIyBXdW5zY2ggSUkgwrcgRGlhZ25vc2UgZsO8ciBTb1dpcwoKRGllIFN0dWRpZXJlbmRlbiDwn5mH8J+Zh/CfmYfwn5mHIGhhdHRlbiBlaW5lbiBlaWdlbmVuIFd1bnNjaCDwn6qEIGFuIGRpZSBGZWUg8J+nmi4KCkthbm5zdCBEdSB1bnMgUmVncmVzc2lvbnMtRGlhZ25vc2Ug8J+TiCBtaXQgZWluZXIgc296aWFsd2lzc2Vuc2NoYWZ0bGljaGVuIEdlc2NoaWNodGUg8J+TmiBlcmtsw6RyZW4/CgohW10oYmlsZGVyL2ZhaXIuanBnKQoKIyMgRGF0ZW4gRmVlCgpXaXIgc3RlbGxlbiB1bnMgUG9zdGJlYW10ZSDwn5OvIHZvciBkaWUgcmVnZWxtw6TDn2lnIExvaG5lcmjDtmh1bmdlbiDwn5KwIHN0cmlrdCBuYWNoIGRlbSBTZW5pb3JpdMOkdHNwcmluemlwIPCfpbggZXJoYWx0ZW4uIAoKV2lyIGhhYmVuIGFsc28gZWluIE1vZGVsbCDwn5OIIGJlaSBkZW0gZGFzIF9fRWlua29tbWVuX18g8J+SsCB2b20gX19BbHRlcl9fIPCfp5Hwn5G0IGFiaMOkbmd0LgoKIVtdKGJpbGRlci9wb3N0YmVhbXRlLmpwZykKCkRhcyB3YWhyZSBFaW5rb21tZW4g8J+SsCAoYGVpbmtvbW1lbl9saW5lYXJgKSBpc3QgdW5zIGJla2FubnQgdW5kIHVuc2VyZSBiZW9iYWNodGV0ZW4gRGF0ZW4g8J+Vte+4jyAoYGVpbmtvbW1lbmApIHdlaWNoZW4gZGF2b24gbm9ybWFsLXZlcnRlaWx0IPCfp64gYWIKClNvIGhhdCBkaWUgRmVlIPCfp5ogdW5zIGRpZSBEYXRlbiDwn5OKIGdlZ2ViZW4uCgpgYGB7cn0KZ2dwbG90KGR0X3JhdywgYWVzKHggPSBhbHRlciwgeSA9IGVpbmtvbW1lbikpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgZ2VvbV9qaXR0ZXIoYWVzKHkgPSBlaW5rb21tZW5fbGluZWFyKSwKICAgICAgICAgICAgICB3aWR0aCA9IDAuMSwgYWxwaGEgPSAwLjUsCiAgICAgICAgICAgICAgY29sb3VyID0gImRhcmtncmVlbiIsIHNoYXBlID0gIi4iKQoKRFQ6OmRhdGF0YWJsZShkdF9yYXcpCmBgYAoKIyMgTW9kZWxsIChmYXN0KSBwZXJmZWt0CgpXaXIgYmVrb21tZW4gc2Now7ZuZSBFcmdlYm5pc3NlIGJlaSBkZXIgU2Now6R0enVuZyBkZXMgTW9kZWxscy4g8J+YhAoKYGBge3J9CmZpdCA8LSBsbShlaW5rb21tZW4gfiBhbHRlciwgZGF0YSA9IGR0X3JhdykKdGlkeV9sbShmaXQpCmdsYW5jZV9sbShmaXQpCmNoZWNrX21vZGVsKGZpdCkKYGBgCgoKIyMgQW5uYWhtZW4gKDEtNykKCjEuIGtvcnJla3RlIFNwZXppZmlrYXRpb24gKExpbmVhcml0w6R0KQoyLiBFcndhcnR1bmdzd2VydCBkZXIgUmVzaWR1ZW4gKGxva2FsKSA9IDAKMy4gTm9ybWFsdmVydGVpbHRlIFJlc2lkdWVuCjQuIFZhcmlhbnpob21vZ2VuaXTDpHQgKGdsZWljaG3DpMOfaWdlIFN0cmV1dW5nIFJlc2lkdWVuKQo1LiBCZXLDvGNrc2ljaHRpZ3VuZyBlaW5mbHVzc3JlaWNoZXIgRsOkbGxlIChrZWluZSBBdXNyZWlzc2VyKQo2LiBfa2VpbmVfIE11bHRpa29sbGluZWFyaXTDpHQgKGdlcmluZ2UgS29ycmVsYXRpb24gdW5hYmjDpG5naWdlciBWYXJpYWJsZW4pCjcuIF9rZWluZV8gQXV0b2tvcnJlbGF0aW9uIGRlciBSZXNpZHVlbiAoaW5zYmVzLiBaZWl0cmVpaGVuKQoKCiMjIEtvcnJla3RlIFNwZXppZmlrYXRpb24KCiMjIyBEcml0dC1WYXJpYWJsZW4KCiFbXShiaWxkZXIvYnJpZWZ0cmFlZ2VyLWZyYXVlbi5qcGcpCgpCcmllZnRyw6RnZXIg4pyJ77iPIGFyYmVpdGVuIG9mdCBudXIgaGFsYnRhZ3Mg8J+VkCB1bmQgdmVyZGllbmVuIPCfkrAgZGFubiBhdWNoIG51ciBkaWUgSMOkbGZ0ZS4KCmBgYHtyfQpkdCA8LQogIGR0X3JhdyAlPiUgCiAgbXV0YXRlKGVpbmtvbW1lbiA9IGlmX2Vsc2UoaWQgJSUgMyA9PSAwLCBlaW5rb21tZW4vMiArIHJub3JtKG4sIHNkID0gNTApLCBlaW5rb21tZW4pLAogICAgICAgICBoYWxidGFncyA9IGlmX2Vsc2UoaWQgJSUgMyA9PSAwLCAiamEiLCAibmVpbiIpICU+JSBmY3RfcmVsZXZlbCgiamEiLCBhZnRlciA9IEluZikpCgpnZ3Bsb3QoZHQsIGFlcyh4ID0gYWx0ZXIsIHkgPSBlaW5rb21tZW4sIGNvbG91ciA9IGhhbGJ0YWdzKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjIpCmBgYAoKT2huZSBEcml0dC1WYXJpYWJsZSDwn5ifCgpgYGB7cn0KZml0IDwtIGxtKGVpbmtvbW1lbiB+IGFsdGVyLCBkYXRhID0gZHQpCnRpZHlfbG0oZml0KQpnbGFuY2VfbG0oZml0KQpjaGVja19tb2RlbChmaXQpCmBgYAoKTWl0IERyaXR0LVZhcmlhYmxlIPCfmYIKCmBgYHtyfQpmaXQgPC0gbG0oZWlua29tbWVuIH4gYWx0ZXIgKyBoYWxidGFncywgZGF0YSA9IGR0KQp0aWR5X2xtKGZpdCkKZ2xhbmNlX2xtKGZpdCkKY2hlY2tfbW9kZWwoZml0KQpgYGAKCk5vY2ggYmVzc2VyIG1pdCBEcml0dC1WYXJpYWJsZSB1bmQgSW50ZXJha3Rpb25zLUVmZmVrdCDwn5iDCgpgYGB7cn0KZml0IDwtIGxtKGVpbmtvbW1lbiB+IGFsdGVyKmhhbGJ0YWdzLCBkYXRhID0gZHQpCnRpZHlfbG0oZml0KQpnbGFuY2VfbG0oZml0KQpjaGVja19tb2RlbChmaXQpCmBgYAoKIyMjIEZ1bmt0aW9uYWxlIEZvcm0KCiFbXShiaWxkZXIvYnVuZGVzcG9zdC5qcGcpCgpCZWkgZGVyIEJ1bmRlc3Bvc3Qg8J+TryB3dXJkZSBlaW5lIEJlemFobHVuZyDwn5KwIG5hY2ggSHVtYW5rYXBpdGFsIGVpbmdlZsO8aHJ0LiAKCkVzIHd1cmRlIGVya2FubnQsIGRhc3MgYmVpIGp1bmdlbiBCZWFtdGVuIPCfp5EgZGllIFByb2R1a3Rpdml0w6R0IHN0w6Rya2VyIHfDpGNoc3QgYWxzIGJlaSDDhGx0ZXJlbiDwn6eTLiBEYWhlciBlcmhhbHRlbiBzaWUgaMO2aGVyZSBMb2huenV3w6RjaHNlIPCfkrAuCgpgYGB7cn0KZHQgPC0KICBkdF9yYXcgJT4lIAogIG11dGF0ZShlaW5rb21tZW5fcG9seW5vbSA9IC0zMDAgKyA4MCphbHRlciAtIDAuNzI1KmFsdGVyXjIsCiAgICAgICAgIGVpbmtvbW1lbiA9IGVpbmtvbW1lbl9wb2x5bm9tICsgcm5vcm0obiwgc2QgPSBzZF9laW5rb21tZW4pKQoKZ2dwbG90KGR0LCBhZXMoeCA9IGFsdGVyLCB5ID0gZWlua29tbWVuKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjIpICsKICAgIGdlb21faml0dGVyKGFlcyh5ID0gZWlua29tbWVuX3BvbHlub20pLAogICAgICAgICAgICAgICAgd2lkdGggPSAwLjEsIGFscGhhID0gMC41LAogICAgICAgICAgICAgICAgY29sb3VyID0gImRhcmtibHVlIiwgc2hhcGUgPSAiLiIpCmBgYApFaW4gbGluZWFyZXMgTW9kZWxsIGlzdCBmYWxzY2ggc3BlemlmaXppZXJ0LiDwn5ifIERpZSBSZWdyZXNzaW9ucy1EaWFnbm9zZSB6ZWlndCBkaWVzLiDimJ3vuI8KCmBgYHtyfQpmaXQgPC0gbG0oZWlua29tbWVuIH4gYWx0ZXIsIGRhdGEgPSBkdCkKdGlkeV9sbShmaXQpCmdsYW5jZV9sbShmaXQpCmNoZWNrX21vZGVsKGZpdCkKYGBgCgpLb3JyZWt0IGlzdCBlaW4gTW9kZWxsIG1pdCBlaW5lbSBQb2x5bm9tIChoaWVyIGVpbmVtIHF1YWRyYXRpc2NoZW0gVGVybSkuIPCfmIMKCmBgYHtyfQpmaXQgPC0gbG0oZWlua29tbWVuIH4gYWx0ZXIgKyBJKGFsdGVyXjIpLCBkYXRhID0gZHQpCnRpZHlfbG0oZml0KQpnbGFuY2VfbG0oZml0KQpjaGVja19tb2RlbChmaXQpCmBgYAoKKCBEZW4gUiBIaW53ZWlzIGF1ZiBNdWx0aS1Lb2xsaW5lYXJpdMOkdCBpZ25vcmllcmVuIHdpciBlaW5mYWNoLiBOYXTDvHJsaWNoIGdpYnQgZXMgZWluZW4gWnVzYW1tZW5oYW5nIHp3aXNjaGVuIGBhbHRlcmAgdW5kIGRlbSBxdWFkcmllcnRlbiBgYWx0ZXJeMmAuICkKCgojIyBWYXJpYW56aG9tb2dlbml0w6R0CgohW10oYmlsZGVyL21pbmlzdGVyLmpwZykKCkJlaSBkZXIgUG9zdCDwn5OvIGhhYmVuIGhvaGUgQmVhbXRlIHVuZCBNaW5zdGVyIGVpbiBow7ZoZXJlcyBFaW5rb21tZW4g8J+SsCBhbHMgZWluZmFjaGUgQmVhbXRlLiBBYmVyIG5pY2h0IGFsbGUgd2VyZGVuIGhvaGUgQmVhbXRlIPCfp5HigI3wn5K8IG9kZXIgW1Bvc3QtTWluaXN0ZXJdKGh0dHBzOi8vZGUud2lraXBlZGlhLm9yZy93aWtpL0NocmlzdGlhbl9TY2h3YXJ6LVNjaGlsbGluZykg8J+PpC4KCmBgYHtyfQpkdCA8LSBkdF9yYXcgJT4lIG11dGF0ZShlaW5rb21tZW4gPSBlaW5rb21tZW5fbGluZWFyICsgaWQvNTAwICogcm5vcm0obiwgc2QgPSBzZF9laW5rb21tZW4pKQoKZ2dwbG90KGR0LCBhZXMoeCA9IGFsdGVyLCB5ID0gZWlua29tbWVuKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjIpICsKICAgIGdlb21faml0dGVyKGFlcyh5ID0gZWlua29tbWVuX2xpbmVhciksCiAgICAgICAgICAgICAgICB3aWR0aCA9IDAuMSwgYWxwaGEgPSAwLjUsCiAgICAgICAgICAgICAgICBjb2xvdXIgPSAiZGFya2dyZWVuIiwgc2hhcGUgPSAiLiIpCmBgYAoKSW0gU3RhbmRhcmQtTW9kZWxsIGVyaGFsdGVuIHdpciB2ZXJ6ZXJydGUgU3RhbmRhcmQtRmVobGVyLiDwn5ifCgpgYGB7cn0KZml0IDwtIGxtKGVpbmtvbW1lbiB+IGFsdGVyLCBkYXRhID0gZHQpCnRpZHlfbG0oZml0KQpnbGFuY2VfbG0oZml0KQpjaGVja19tb2RlbChmaXQpCmBgYAoKQmVzc2VyIGlzdCBlaW4gTW9kZWxsIG1pdCBfcm9idXN0ZW5fIPCfkqogU3RhbmRhcmQtRmVobGVybi4g8J+YhAoKYGBge3J9CmZpdCA8LSBsbV9yb2J1c3QoZWlua29tbWVuIH4gYWx0ZXIsIGRhdGEgPSBkdCkKdGlkeV9sbShmaXQpCmdsYW5jZShmaXQpICU+JSBtdXRhdGVfaWYoaXMubnVtZXJpYywgcm91bmQsIDIpIApgYGAKCiMjIE11bHRpa29sbGluZWFyaXTDpHQKCiFbXShiaWxkZXIvYnJpZWZ0cmFlZ2VyLWdydXBwZS5qcGcpCgrDhGx0ZXJlIPCfkbTwn5G1IFBvc3RiZWFtdGUg8J+TryBzaW5kIGtsZWluZXIgYWxzIErDvG5nZXJlIPCfkafwn5GmLgoKYGBge3J9CmR0IDwtIGR0X3JhdyAlPiUgbXV0YXRlKGdyb2Vzc2VfY20gPSByb3VuZChzZXEoMTgwLCAxNjAsIGxlbmd0aC5vdXQgPSBuKSArIHJub3JtKG4sIHNkID0gMi41KSwgMykpCgpnZ3Bsb3QoZHQsIGFlcyh4ID0gYWx0ZXIsIHkgPSBncm9lc3NlX2NtKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjIpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSBsbSkKYGBgCkVzIGdpYnQgZGFubiBhdWNoIGVpbmVuIFp1c2FtbWVuaGFuZyDwn5OJIHp3aXNjaGVuIGBncm9lc3NlX2NtYCDwn5G1IHVuZCBgZWlua29tbWVuYCDwn5KwLCBuYXTDvHJsaWNoIGtlaW5lbiBrYXVzYWxlbiDimpnvuI8uCgpgYGB7cn0KZ2dwbG90KGR0LCBhZXMoeCA9IGdyb2Vzc2VfY20sIHkgPSBlaW5rb21tZW4pKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtKQpgYGAKCmBgYHtyfQpmaXQgPC0gbG0oZWlua29tbWVuIH4gZ3JvZXNzZV9jbSwgZGF0YSA9IGR0KQp0aWR5X2xtKGZpdCkKZ2xhbmNlX2xtKGZpdCkKYGBgCgpOZWhtZW4gd2lyIGBhbHRlcmAg8J+RtSB1bmQgYGdyb2Vzc2VfY21gIPCfk5AgaW4gZWluZW0gTW9kZWxsIGF1ZiDwn5OJIGhhYmVuIHdpciBNdWx0aWtvbGxpbmVhcml0w6R0IPCflJcg8J+YnwoKYGBge3J9CmZpdCA8LSBsbShlaW5rb21tZW4gfiBhbHRlciArIGdyb2Vzc2VfY20sIGRhdGEgPSBkdCkKdGlkeV9sbShmaXQpCmdsYW5jZV9sbShmaXQpCmNoZWNrX21vZGVsKGZpdCkKYGBgCgoKTXVsdGlrb2xsaW5lYXJpdMOkdCBpbSBNb2RlbGwg8J+TiSAgZXJrZW5uZW4g8J+Vte+4jyB3aXIgYXVjaCAgYW4gZGVyIHNlaHIgc3RhcmtlbiBLb3JyZWxsYXRpb24gZGVyIHVuYWJow6RuZ2lnZW4gVmFyaWFibGVuLgoKYGBge3J9CmR0ICU+JSAKICBzZWxlY3QoYWx0ZXIsIGVpbmtvbW1lbikgJT4lIAogIGNvcigpICU+JSAKICBhc190aWJibGUoKSAlPiUgCiAgbXV0YXRlX2FsbChyb3VuZCwgMikKYGBgCgoKIyMgQXVzcmVpc3NlcgoKIVtdKGJpbGRlci9mZXJubWVsZGVhbXQuanBnKQoKQmVpIGRlciBEYXRlbsO8YmVydHJhZ3VuZyDwn5OhIGdhYiBlcyBlaW5lbiBGZWhsZXIg4pqg77iPIHVuZCBkaWUgbGV0enRlIFppZmZlciDwn5SiIHd1cmRlIGJlaSBlaW5pZ2VuIEdlaGFsdHNhbmdhYmVuIPCfkrAgZW50ZmVybnQuIAoKYGBge3J9CmlkX3JhbmRvbSA8LSBydW5pZig4MCwgbWluID0gMSwgbWF4ID0gbikgJT4lIHJvdW5kKCkKCmR0IDwtCiAgZHRfcmF3ICU+JSAKICBtdXRhdGUoZWlua29tbWVuID0gaWZfZWxzZShpZCAlaW4lIGlkX3JhbmRvbSwgcm91bmQoZWlua29tbWVuLzEwKSwgZWlua29tbWVuKSkKCmdncGxvdChkdCwgYWVzKHggPSBhbHRlciwgeSA9IGVpbmtvbW1lbikpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4yKQpgYGAKCldpciBlcmhhbHRlbiBlaW5lIHZlcnplcnJ0ZSBTY2jDpHR6dW5nLiDwn5ifCgpgYGB7cn0KZml0IDwtIGxtKGVpbmtvbW1lbiB+IGFsdGVyLCBkYXRhID0gZHQpCnRpZHlfbG0oZml0KQpnbGFuY2VfbG0oZml0KQpjaGVja19tb2RlbChmaXQpCmBgYAoKRGllIGZhbHNjaGVuIEJlb2JhY2h0dW5nZW4gbcO8c3NlbiB3aXIgZW50ZmVybmVuIChvZGVyIGtvcnJpZ2llcmVuKS4g8J+YgwoKYGBge3J9CmR0X3RtcCA8LSBkdCAlPiUgZmlsdGVyKGVpbmtvbW1lbiA+IDUwMCkKCmZpdCA8LSBsbShlaW5rb21tZW4gfiBhbHRlciwgZGF0YSA9IGR0X3RtcCkKdGlkeV9sbShmaXQpCmdsYW5jZV9sbShmaXQpCmNoZWNrX21vZGVsKGZpdCkKYGBgCgoKCiMjIEF1dG9rb3JyZWxhdGlvbgoKIVtdKGJpbGRlci90ZWxlZm9uemVudHJhbGUuanBnKQoKRGllIFBvc3RiZWFtdGVuIPCfk68gd3VyZGVuIDIwMTAgdW5kIDIwMjAg8J+ThSBuYWNoIGlocmVtIEVpbmtvbW1lbiDwn5KwIGJlZnJhZ3QuIOKYju+4jwoKYGBge3J9CmR0X3RtcCA8LQogIGR0X3JhdyAlPiUgCiAgbXV0YXRlKGVpbmtvbW1lbiA9IHJvdW5kKGVpbmtvbW1lbiAtIHBhcmFtX2FsdGVyKjEwICsgcm5vcm0obiwgc2QgPSAxMCkpLAogICAgICAgICBhbHRlciA9IGFsdGVyIC0gMTAsCiAgICAgICAgIGJlZnJhZ3VuZyA9IDIwMTApICU+JSAKICBmaWx0ZXIoYWx0ZXIgPj0gMjApCgpkdCA8LQogIGR0X3JhdyAlPiUgCiAgbXV0YXRlKGJlZnJhZ3VuZyA9IDIwMjApICU+JSAKICByYmluZChkdF90bXApICU+JSAKICBhcnJhbmdlKGlkLCBiZWZyYWd1bmcpICU+JSAKICByZWxvY2F0ZShiZWZyYWd1bmcsIC5hZnRlciA9IGlkKQoKRFQ6OmRhdGF0YWJsZShkdCAlPiUgZmlsdGVyKGlkID4gNTAwKSkgICMgRGF0ZW4gYWIgSUQgNTAwIHVtIFBhbmVsLVN0cnVrdHVyIHp1IHZlcmRldXRsaWNoZW4gCmBgYAoKUG9zdGJlYW10ZSDwn5OvIGRpZSAyMDEwICDwn5OFIMOkbHRlciBhbHMgNTAgd2FyZW4gc2luZCBpbnp3aXNjaGVuIGluIFBlbnNpb24g8J+RtSB1bmQgaGFiZW4gYW4gZGVyIDIwMjAgQmVmcmFndW5nIOKYju+4jyBuaWNodCBtZWhyIHRlaWxnZW5vbW1lbi4KCmBgYHtyfQpkdF90bXAgPC0gZHQgJT4lIG11dGF0ZShiZWZyYWd1bmcgPSBmYWN0b3IoYmVmcmFndW5nKSkKCmdncGxvdChkdF90bXAsIGFlcyh4ID0gYWx0ZXIsIHkgPSBlaW5rb21tZW4sIGNvbG91ciA9IGJlZnJhZ3VuZykpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4yKQpgYGAKCkltIE1vZGVsbCAobiA9IDE3NTApIHNpbmQgQmVvYmFjaHR1bmdlbiAodW5kIFJlc2lkdWVuKSBuaWNodCBtZWhyIHVuYWJow6RuZ2lnLiDwn5ifIFdpciBoYWJlbiBqYSB2aWVsZSB6d2VpbWFsIGJlZnJhZ3QuIOKYju+4jwoKYGBge3J9CmZpdCA8LSBsbShlaW5rb21tZW4gfiBhbHRlciwgZGF0YSA9IGR0X3RtcCkKdGlkeV9sbShmaXQpCmdsYW5jZV9sbShmaXQpCmNoZWNrX21vZGVsKGZpdCkKYGBgCgojIEF1ZmdhYmUKCiFbXShiaWxkZXIvcmFkaW9xdWl6LmpwZykKCkRpc2t1dGllcmVuIFNpZSDwn5mH8J+Zh/CfmYcgZGllIFJlZ3Jlc3Npb25zLUFubmFobWVuICDwn5OIICB1bmQgemVpZ2VuIFNpZSBvYiB1bmQgd2llIFZlcmxldHp1bmdlbiDwn4+lIGRlciBBbm5haG1lbiBlcmthbm50IPCflbXvuI8gIHdlcmRlbiBrw7ZubmVuLgoKLS0tCgojIFF1ZWxsZW4KCisgPGh0dHBzOi8vY29tbW9ucy53aWtpbWVkaWEub3JnL3dpa2kvRmlsZTpTb3BoaWVBbmRlcnNvblRha2V0aGVmYWlyZmFjZW9mV29tYW4uanBnPgorIDxodHRwczovL2NvbW1vbnMud2lraW1lZGlhLm9yZy93aWtpL0ZpbGU6QnVuZGVzYXJjaGl2X0JpbGRfMTgzLTYzMTQyLTAwMDEsX0NvdHRidXMsX0JyaWVmdHIlQzMlQTRnZXJpbm5lbi5qcGc+CisgPGh0dHBzOi8vY29tbW9ucy53aWtpbWVkaWEub3JnL3dpa2kvRmlsZTpCdW5kZXNhcmNoaXZfQmlsZF8xODMtMTk5MC0wOTI0LTAyMCxfR2VyYSxfUHJvdGVzdF92b25fUG9zdC1HZXdlcmtzY2hhZnRlcm4uanBnPgorIDxodHRwczovL2NvbW1vbnMud2lraW1lZGlhLm9yZy93aWtpL0ZpbGU6QnVuZGVzYXJjaGl2X0JpbGRfMTgzLVUwMjA1LTAwMTAsX0JlcmxpbixfVGFnX2Rlc19Qb3N0LV91bmRfRmVybm1lbGRld2VzZW5zLF9Wb3JiZXJlaXR1bmcuanBnPgorIDxodHRwczovL2NvbW1vbnMud2lraW1lZGlhLm9yZy93aWtpL0ZpbGU6QnVuZGVzYXJjaGl2X0JpbGRfMTgzLTU5MTExLTAwMDIsX0JlcmxpbixfUG9zdGFtdF9OXzU4LF9CcmllZnRyJUMzJUE0Z2VyLmpwZz4KKyA8aHR0cHM6Ly9jb21tb25zLndpa2ltZWRpYS5vcmcvd2lraS9GaWxlOlRlbGVmb256ZW50cmFsZV9iZWlfZGVyXzIuSW5mYW50ZXJpZWRpdmlzaW9uXyhCaWxkSURfMTU3MDUxMjUpLmpwZz4KKyA8aHR0cHM6Ly9jb21tb25zLndpa2ltZWRpYS5vcmcvd2lraS9GaWxlOlJhZGlvcXVpel8lMjJBbGxlaW5fZ2VnZW5fYWxsZSUyMl9hdXNfZGVtX1JhdHNzYWFsXyhLaWVsXzQ1LjU2NykuanBnPgoKLS0tCgohW10oYmlsZGVyL2ZhaXIuanBnKQ==