1 Zadanie 1 - dane Prestige

Zestaw danych “Prestige” (z pakietu “car”) zawiera dane nt. prestiżu n=102 Kanadyjskich zawodów z 1971 roku, a także średni dochód w danym zawodzie. Do zbadania zależności między prestiżem a dochodem wykorzystaj metody regresji nieparametrycznej.

Najpierw załaduj dane i zwizualizuj relację pomiędzy dochodem (X) a prestiżem (Y).

data("Prestige")
attach(Prestige)
## Następujący obiekt został zakryty z package:datasets:
## 
##     women
ggplot(Prestige)+
  geom_point(aes(x=prestige, y=income))

Związek wygląda na nieliniowy. Istnieje silna (pozytywna) liniowa zależność pomiędzy dochodem a prestiżem dla zawodów, które zarabiają mniej niż $10K. W przypadku zawodów, które zarabiają w zakresie od 10 do 25 tysięcy dolarów, związek ten ma znacznie inne nachylenie.

Metody regresji nieparametrycznej.

1.1 Local polynomial regression (locpoly) - metoda lokalnego wygładzania

Główne parametry, takie jak degree i bandwidth, mają kluczowy wpływ na sposób, w jaki wygładzana jest zależność.

Parametr degree określa stopień wielomianu użytego do lokalnej aproksymacji. Degree=0 oznacza użycie lokalnych średnich (co odpowiada regresji najbliższych sąsiadów).

Degree=1 oznacza wykorzystanie lokalnej regresji liniowej

Degree=2 oznacza wykorzystanie lokalnej regresji kwadratowej.

Parametr bandwidth decyduje o rozpiętości jądra, czyli definiuje rozmiar obszaru w jakim obserwacje wpływają na funkcję gęstości.Im większa wartość bandwidth, tym bardziej wygładzona jest funkcja. Zarówno zbyt niska wartość bandwidth może być nieodpowiednia (overfitting- model zbyt szczegółowy), jak i również zbyt wysoka wartość tego parametru (underfitting- zbyt duże wygładzenie, zbyt duże uogólnienie).

1.1.1 Porównanie różnych bandwidth

fit1 <- locpoly(prestige, income,
        degree=0, bandwidth=1) %>% as.tibble

ggplot(Prestige) +
  geom_point(aes(x=prestige,y=income)) +
  geom_line(data=fit1, aes(x=x,y=y), col='darkblue', size=0.8)+
  ggtitle("Kernel smoothing - szerokość pasma równa 1")

Zwiększenie szerokości pasma do 5.

fit2 <- locpoly(prestige, income,
        degree=0, bandwidth=5) %>% as.tibble

ggplot(Prestige) +
  geom_point(aes(x=prestige,y=income)) +
  geom_line(data=fit2, aes(x=x,y=y), col='pink',size=0.9)+
  ggtitle("Kernel smoothing - szerokość pasma 5")

Porównanie:

Niebieska linia: szerokość pasma 1 Różowa linia: szerokość pasma 5

ggplot(Prestige) +
  geom_point(aes(x=prestige,y=income)) +
  geom_line(data=fit1, aes(x=x,y=y), col='darkblue')+
  geom_line(data=fit2, aes(x=x,y=y), col='pink')+
  ggtitle("Kernel smoothing - porówananie dwóch szerokości pasm")

Aby dopasować szerokośc pasma

dopasowana <- dpill(prestige, income)
fit3 <- locpoly(prestige, income,
        degree=0, bandwidth=dopasowana) %>% as.tibble

paste("Dopasowana szerokość pasma wynosi:", round(dopasowana, 4))
## [1] "Dopasowana szerokość pasma wynosi: 1.6936"
ggplot(Prestige) +
  geom_point(aes(x=prestige,y=income))+
  geom_line(data=fit3, aes(x=x,y=y), col='darkgreen',size=0.7)

1.1.2 Porównanie różnych degree (stopień wielomianu)

fit1 <- locpoly(prestige, income,
        degree=0, bandwidth=dopasowana) %>% as.tibble
fit2 <- locpoly(prestige, income,
        degree=1, bandwidth=dopasowana) %>% as.tibble
fit3 <- locpoly(prestige, income,
        degree=2, bandwidth=dopasowana) %>% as.tibble

ggplot(Prestige) +
  geom_point(aes(x=prestige,y=income)) +
  geom_line(data=fit1, aes(x=x,y=y), col='darkblue',size=0.9)+
  geom_line(data=fit2, aes(x=x,y=y), col='pink',size=0.7)+
  geom_line(data=fit3, aes(x=x,y=y), col='darkgreen',size=0.5)+
  ggtitle("Kernel smoothing - porówananie trzech stopni wielomianu")

Wybór degree wpływa na funkcję gęstości na wykresie. W przypadku degree=2 występuje overfitting. Lepszym wyborem byłoby degree równe 0 lub 1.

1.2 Loess - lokalnie kwadratowy

# Dopasowanie modelu LOESS
smr <- loess(income ~ prestige, span = 0.75, degree = 2, family = "gaussian")

# Tworzenie ramki danych z wynikami modelu
loess_fit <- data.frame(
  prestige = Prestige$prestige, # Zmienna niezależna
  fitted_values = fitted(smr)   # Wartości dopasowane
)

# Wykres
ggplot(Prestige) +
  # Punkty danych
  geom_point(aes(x = prestige, y = income)) +
  # Dopasowana linia LOESS
  geom_line(data = loess_fit, aes(x = prestige, y = fitted_values), col = 'skyblue', size = 0.8) +
  theme_minimal() +
  labs(
    x = "Prestige",
    y = "Income",
    title = "Dopasowanie modelu LOESS",
    subtitle = "span = 0.75, degree = 2"
  )

ggplot(Prestige) +
  geom_point(aes(x=prestige,y=income))+
  geom_smooth(aes(x=prestige,y=income),method='loess',span=0.22)
## `geom_smooth()` using formula = 'y ~ x'

1.3 Sploty interpolujące - smooth.spline

1.3.1 Zmienianie parametru ‘spar’

fit1 <-smooth.spline(prestige, income, spar=0.2) 
smr1 <- data.frame(x=fit1$x,y=fit1$y)

fit2 <-smooth.spline(prestige, income, spar=0.8) 
smr2 <- data.frame(x=fit2$x,y=fit2$y)

ggplot(Prestige) +
  geom_point(aes(x=prestige,y=income)) +
  geom_line(data=smr1, aes(x=x,y=y), col='darkblue',size=0.8)+
  geom_line(data=smr2, aes(x=x,y=y), col='pink',size=0.7)+
  ggtitle("Sploty interpolujące - porównanie różnych wartości parametru 'spar'")

Spar=0.2 powoduje mocne dopasowanie do danych. Spar=0.8 prezentuje zbyt duże uogólnienie. Wniosek: Zmniejszanie ‘spar’ powoduje większe dopasowanie, natomiast zwiększanie - większe uogólnienie.

Aby automatycznie wybrać szerokośc pasma można zastosować cross validation

1.3.2 Zastosowanie cross validation (CV=TRUE)

smr2 <- smooth.spline(
  prestige,
  income,
  cv=TRUE)
smr2 <- data.frame(x=smr2$x,y=smr2$y)
ggplot(Prestige) +
  geom_point(aes(x=prestige,y=income)) +
  ggtitle("Sploty interpolujące, lambda wybrana przez CV") +
  geom_line(data=smr2, aes(x=x, y=y), col='darkblue', size=0.8)

1.4 Porównanie funkcji locpoly i smooth.spline

Niebieska linia - locpoly Różowa linia - smooth.spline

fit <- locpoly(prestige, income,
        degree=0, bandwidth=dopasowana) %>% as.tibble

smr <- smooth.spline(
  prestige,
  income,
  cv=TRUE)
smr <- data.frame(x=smr$x,y=smr$y)

ggplot(Prestige) +
  geom_point(aes(x=prestige,y=income)) +
  geom_line(data=fit, aes(x=x,y=y), col='darkblue', size=0.7)+
  geom_line(data=smr, aes(x=x,y=y), col='pink', size=0.7)+
  ggtitle("Porównanie funkcji locpoly i smooth.spline")

1.5 Naturalne sploty

library(splines)

fit <-lm(income~ns(prestige,df=6),Prestige)
fit2 <-lm(income~ns(prestige,df=12),Prestige)

ggplot(Prestige)+
  geom_point(aes(x=prestige,y=income))+
  ggtitle("Naturalne sploty, 6 df(niebieski) i 12 df(czerwony)")+
  geom_line(aes(x=prestige,y=fitted(fit)),col='darkblue', size=0.7)+
  geom_line(aes(x=prestige,y=fitted(fit2)),col='pink', size=0.7)

1.6 Sploty oraz geom_smooth()

ggplot(Prestige) +
    geom_point(aes(x=prestige,y=income)) +
    geom_smooth(aes(x=prestige,y=fitted(fit)), method='gam',
      formula = y ~ s(x,k=12), se=TRUE)

fit <- lm(income ~ ns(prestige, df = 6), Prestige)
pred <- data.frame(prestige = Prestige$prestige,
                   predict(fit, interval = "confidence"))
ggplot(Prestige, aes(x = prestige, y = income)) +
    geom_point() +
    geom_line(data = pred, aes(y = fit), color = "darkblue", size=0.7) +
    geom_ribbon(data = pred, aes(ymin = lwr, ymax = upr), alpha = 0.2)

2 Zadanie 2 - dane mcycle

Zbiór danych “mcycle” (z pakietu MASS) zawiera n=133 pary punktów czasowych (w ms) i obserwowanych przyspieszeń głowy (w g), które zostały zarejestrowane w symulowanym wypadku motocyklowym.

Do zbadania zależności między czasem a przyspieszeniem wykorzystaj metody regresji nieparametrycznej.

Najpierw wczytaj dane i zwizualizuj zależność między czasem (X) a przyspieszeniem (Y).

library(MASS)
## 
## Dołączanie pakietu: 'MASS'
## Następujący obiekt został zakryty z 'package:dplyr':
## 
##     select
data("mcycle")
attach(mcycle)

ggplot(mcycle)+
  geom_point(aes(x=times,y=accel))

Zależność wygląda na nieliniową.

Przyspieszenie jest stabilne od 0-15 ms, spada od ok. 15-20 ms, rośnie od 20-30 ms, spada od 30-40 ms, a następnie zaczyna się stabilizować.

2.1 Local polynomial regression (locpoly) - metoda lokalnego wygładzania

2.1.1 Porównanie różnych bandwidth

fit1 <- locpoly(times, accel,
        degree=0, bandwidth=1) %>% as.tibble

ggplot(mcycle) +
  geom_point(aes(x=times,y=accel)) +
  geom_line(data=fit1, aes(x=x,y=y), col='green', size=0.7)+
  ggtitle("Kernel smoothing - szerokość pasma 1")

Zwiększam szerokość pasma do 5

fit2 <- locpoly(times, accel,
        degree=0, bandwidth=5) %>% as.tibble

ggplot(mcycle) +
  geom_point(aes(x=times,y=accel)) +
  geom_line(data=fit2, aes(x=x,y=y), col='darkred', size=0.7)+
  ggtitle("Kernel smoothing - szerokość pasma 5")

Porównanie:

Zielona linia: szerokość pasma 1 Czerwona linia: szerokość pasma 5

ggplot(mcycle) +
  geom_point(aes(x=times,y=accel)) +
  geom_line(data=fit1, aes(x=x,y=y), col='green', size=0.7)+
  geom_line(data=fit2, aes(x=x,y=y), col='darkred', size=0.7)+
  ggtitle("Kernel smoothing - porówananie szerokości pasm")

Aby dopasować szerokośc pasma - cross validation

dopasowana <- dpill(times, accel)
fit3 <- locpoly(times, accel,
        degree=0, bandwidth=dopasowana) %>% as.tibble

paste("Dopasowana szerokość pasma wynosi:", round(dopasowana, 4))
## [1] "Dopasowana szerokość pasma wynosi: 1.4453"
ggplot(mcycle) +
  geom_point(aes(x=times,y=accel))+
  geom_line(data=fit3, aes(x=x,y=y), col='blue',size=1)

2.1.2 Porównanie różnych degree (stopień wielomianu)

fit1 <- locpoly(times, accel,
        degree=0, bandwidth=dopasowana) %>% as.tibble
fit2 <- locpoly(times, accel,,
        degree=1, bandwidth=dopasowana) %>% as.tibble
fit3 <- locpoly(times, accel,
        degree=2, bandwidth=dopasowana) %>% as.tibble

ggplot(mcycle) +
  geom_point(aes(x=times,y=accel)) +
  geom_line(data=fit1, aes(x=x,y=y), col='green',size=0.8)+
  geom_line(data=fit2, aes(x=x,y=y), col='darkred',size=0.8)+
  geom_line(data=fit3, aes(x=x,y=y), col='blue',size=0.8)+
  ggtitle("Kernel smoothing - porówananie trzech stopni wielomianu")

2.2 Loess - lokalnie kwadratowy

smr <- loess (accel~times,span=0.75, degree=2,
               family="gaussian")

ggplot(mcycle) +
  geom_point(aes(x=times,y=accel))+
  geom_line(aes(x=times,y=fitted(smr)), col='skyblue', size=0.7)

ggplot(mcycle) +
  geom_point(aes(x=times,y=accel))+
  geom_smooth(aes(x=times,y=accel),method='loess',span=0.22)
## `geom_smooth()` using formula = 'y ~ x'

2.3 Sploty interpolujące - smooth.spline

2.3.1 Zmienianie parametru ‘spar’

fit1 <-smooth.spline(times, accel, spar=0.2) 
smr1 <- data.frame(x=fit1$x,y=fit1$y)

fit2 <-smooth.spline(times, accel,, spar=0.8) 
smr2 <- data.frame(x=fit2$x,y=fit2$y)

ggplot(mcycle) +
  geom_point(aes(x=times,y=accel)) +
  geom_line(data=smr1, aes(x=x,y=y), col='green',size=0.8)+
  geom_line(data=smr2, aes(x=x,y=y), col='darkred',size=0.8)+
  ggtitle("Sploty interpolujące - porównanie różnych wartości parametru 'spar'")

Spar=0.2 powoduje zbyt mocne dopasowanie do danych. Spar=0.8 prezentuje zbyt duże uogólnienie. Wniosek: Zmniejszanie ‘spar’ powoduje większe dopasowanie, a zwiększanie - większe uogólnienie.

Aby automatycznie wybrać szerokośc pasma można zastosować cross validation

2.3.2 Zastosowanie cross validation (CV=TRUE)

smr2 <- smooth.spline(
  times,
  accel,
  cv=TRUE)
smr2 <- data.frame(x=smr2$x,y=smr2$y)
ggplot(mcycle) +
  geom_point(aes(x=times,y=accel)) +
  ggtitle("Sploty interpolujące, lambda wybrana przez CV") +
  geom_line(data=smr2, aes(x=x, y=y), col='green', size=0.7)

2.4 Porównanie funkcji locpoly i smooth.spline

fit <- locpoly(times, accel,
        degree=0, bandwidth=dopasowana) %>% as.tibble

smr <- smooth.spline(
  times,
  accel,
  cv=TRUE)
smr <- data.frame(x=smr$x,y=smr$y)

ggplot(mcycle) +
  geom_point(aes(x=times,y=accel)) +
  geom_line(data=fit, aes(x=x,y=y), col='green', size=0.7)+
  geom_line(data=smr, aes(x=x,y=y), col='darkred', size=0.7)+
  ggtitle("Porównanie funkcji locpoly i smooth.spline")

2.5 Naturalne sploty

library(splines)

fit <-lm(accel~ns(times,df=6),mcycle)
fit2 <-lm(accel~ns(times,df=12),mcycle)

ggplot(mcycle)+
  geom_point(aes(x=times,y=accel))+
  ggtitle("Naturalne sploty, 6 df(niebieski) i 12 df(czerwony)")+
  geom_line(aes(x=times,y=fitted(fit)),col='green', size=0.7)+
  geom_line(aes(x=times,y=fitted(fit2)),col='darkred', size=0.7)

2.6 Sploty oraz geom_smooth()

ggplot(mcycle) +
    geom_point(aes(x=times,y=accel)) +
    geom_smooth(aes(x=times,y=fitted(fit)), method='gam',
      formula = y ~ s(x,k=12), se=TRUE)

fit <- lm(accel ~ ns(times, df = 6), mcycle)
pred <- data.frame(times = mcycle$times,
                   predict(fit, interval = "confidence"))
ggplot(mcycle, aes(x = times, y = accel)) +
    geom_point() +
    geom_line(data = pred, aes(y = fit), color = "green", size=0.7) +
    geom_ribbon(data = pred, aes(ymin = lwr, ymax = upr), alpha = 0.2)

LS0tDQp0aXRsZTogIlJhcG9ydCAzIg0Kc3VidGl0bGU6ICJSZWdyZXNqYSBuaWVwYXJhbWV0cnljem5hIg0KYXV0aG9yOiAiSnVsaWEgQm9ndXN6Ig0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRoZW1lOiBjZXJ1bGVhbg0KICAgIGhpZ2hsaWdodDogdGV4dG1hdGUNCiAgICBmb250c2l6ZTogOHB0DQogICAgdG9jOiB0cnVlDQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogZmFsc2UNCmVkaXRvcl9vcHRpb25zOiANCiAgbWFya2Rvd246IA0KICAgIHdyYXA6IDcyDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCmxpYnJhcnkoY2FyKQ0KbGlicmFyeShLZXJuU21vb3RoKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpgYGANCg0KIyBaYWRhbmllIDEgLSBkYW5lIFByZXN0aWdlDQoNClplc3RhdyBkYW55Y2gg4oCcUHJlc3RpZ2XigJ0gKHogcGFraWV0dSDigJxjYXLigJ0pIHphd2llcmEgZGFuZSBudC4gcHJlc3Rpxbx1IG49MTAyIEthbmFkeWpza2ljaCB6YXdvZMOzdyB6IDE5NzEgcm9rdSwgYSB0YWvFvGUgxZtyZWRuaSBkb2Now7NkIHcgZGFueW0gemF3b2R6aWUuIERvIHpiYWRhbmlhIHphbGXFvG5vxZtjaSBtacSZZHp5IHByZXN0acW8ZW0gYSBkb2Nob2RlbSB3eWtvcnp5c3RhaiBtZXRvZHkgcmVncmVzamkgbmllcGFyYW1ldHJ5Y3puZWouDQoNCk5hanBpZXJ3IHphxYJhZHVqIGRhbmUgaSB6d2l6dWFsaXp1aiByZWxhY2rEmSBwb21pxJlkenkgZG9jaG9kZW0gKFgpIGEgcHJlc3RpxbxlbSAoWSkuDQoNCmBgYHtyfQ0KZGF0YSgiUHJlc3RpZ2UiKQ0KYXR0YWNoKFByZXN0aWdlKQ0KDQpnZ3Bsb3QoUHJlc3RpZ2UpKw0KICBnZW9tX3BvaW50KGFlcyh4PXByZXN0aWdlLCB5PWluY29tZSkpDQpgYGANCg0KWndpxIV6ZWsgd3lnbMSFZGEgbmEgbmllbGluaW93eS4gSXN0bmllamUgc2lsbmEgKHBvenl0eXduYSkgbGluaW93YSB6YWxlxbxub8WbxIcgcG9tacSZZHp5IGRvY2hvZGVtIGEgcHJlc3RpxbxlbSBkbGEgemF3b2TDs3csIGt0w7NyZSB6YXJhYmlhasSFIG1uaWVqIG5pxbwgJDEwSy4gVyBwcnp5cGFka3UgemF3b2TDs3csIGt0w7NyZSB6YXJhYmlhasSFIHcgemFrcmVzaWUgb2QgMTAgZG8gMjUgdHlzacSZY3kgZG9sYXLDs3csIHp3acSFemVrIHRlbiBtYSB6bmFjem5pZSBpbm5lIG5hY2h5bGVuaWUuDQoNCk1ldG9keSByZWdyZXNqaSBuaWVwYXJhbWV0cnljem5lai4NCg0KIyMgTG9jYWwgcG9seW5vbWlhbCByZWdyZXNzaW9uIChsb2Nwb2x5KSAtIG1ldG9kYSBsb2thbG5lZ28gd3lnxYJhZHphbmlhDQoNCkfFgsOzd25lIHBhcmFtZXRyeSwgdGFraWUgamFrIGRlZ3JlZSBpIGJhbmR3aWR0aCwgbWFqxIUga2x1Y3pvd3kgd3DFgnl3IG5hIHNwb3PDs2IsIHcgamFraSB3eWfFgmFkemFuYSBqZXN0IHphbGXFvG5vxZvEhy4NCg0KUGFyYW1ldHIgZGVncmVlIG9rcmXFm2xhIHN0b3BpZcWEIHdpZWxvbWlhbnUgdcW8eXRlZ28gZG8gbG9rYWxuZWogYXByb2tzeW1hY2ppLiBEZWdyZWU9MCBvem5hY3phIHXFvHljaWUgbG9rYWxueWNoIMWbcmVkbmljaCAoY28gb2Rwb3dpYWRhIHJlZ3Jlc2ppIG5hamJsacW8c3p5Y2ggc8SFc2lhZMOzdykuIA0KDQpEZWdyZWU9MSBvem5hY3phIHd5a29yenlzdGFuaWUgbG9rYWxuZWogcmVncmVzamkgbGluaW93ZWogDQoNCkRlZ3JlZT0yIG96bmFjemEgd3lrb3J6eXN0YW5pZSBsb2thbG5laiByZWdyZXNqaSBrd2FkcmF0b3dlai4NCg0KUGFyYW1ldHIgYmFuZHdpZHRoIGRlY3lkdWplIG8gcm96cGnEmXRvxZtjaSBqxIVkcmEsIGN6eWxpIGRlZmluaXVqZSByb3ptaWFyIG9ic3phcnUgdyBqYWtpbSBvYnNlcndhY2plIHdwxYJ5d2FqxIUgbmEgZnVua2NqxJkgZ8SZc3RvxZtjaS5JbSB3acSZa3N6YSB3YXJ0b8WbxIcgYmFuZHdpZHRoLCB0eW0gYmFyZHppZWogd3lnxYJhZHpvbmEgamVzdCBmdW5rY2phLiBaYXLDs3dubyB6Ynl0IG5pc2thIHdhcnRvxZvEhyBiYW5kd2lkdGggbW/FvGUgYnnEhyBuaWVvZHBvd2llZG5pYSAob3ZlcmZpdHRpbmctIG1vZGVsIHpieXQgc3pjemVnw7PFgm93eSksIGphayBpIHLDs3duaWXFvCB6Ynl0IHd5c29rYSB3YXJ0b8WbxIcgdGVnbyBwYXJhbWV0cnUgKHVuZGVyZml0dGluZy0gemJ5dCBkdcW8ZSB3eWfFgmFkemVuaWUsIHpieXQgZHXFvGUgdW9nw7NsbmllbmllKS4NCg0KIyMjIFBvcsOzd25hbmllIHLDs8W8bnljaCBiYW5kd2lkdGgNCg0KYGBge3Igd2FybmluZz1GQUxTRX0NCmZpdDEgPC0gbG9jcG9seShwcmVzdGlnZSwgaW5jb21lLA0KICAgICAgICBkZWdyZWU9MCwgYmFuZHdpZHRoPTEpICU+JSBhcy50aWJibGUNCg0KZ2dwbG90KFByZXN0aWdlKSArDQogIGdlb21fcG9pbnQoYWVzKHg9cHJlc3RpZ2UseT1pbmNvbWUpKSArDQogIGdlb21fbGluZShkYXRhPWZpdDEsIGFlcyh4PXgseT15KSwgY29sPSdkYXJrYmx1ZScsIHNpemU9MC44KSsNCiAgZ2d0aXRsZSgiS2VybmVsIHNtb290aGluZyAtIHN6ZXJva2/Fm8SHIHBhc21hIHLDs3duYSAxIikNCmBgYA0KDQpad2nEmWtzemVuaWUgc3plcm9rb8WbY2kgcGFzbWEgZG8gNS4NCiAgDQpgYGB7cn0NCmZpdDIgPC0gbG9jcG9seShwcmVzdGlnZSwgaW5jb21lLA0KICAgICAgICBkZWdyZWU9MCwgYmFuZHdpZHRoPTUpICU+JSBhcy50aWJibGUNCg0KZ2dwbG90KFByZXN0aWdlKSArDQogIGdlb21fcG9pbnQoYWVzKHg9cHJlc3RpZ2UseT1pbmNvbWUpKSArDQogIGdlb21fbGluZShkYXRhPWZpdDIsIGFlcyh4PXgseT15KSwgY29sPSdwaW5rJyxzaXplPTAuOSkrDQogIGdndGl0bGUoIktlcm5lbCBzbW9vdGhpbmcgLSBzemVyb2tvxZvEhyBwYXNtYSA1IikNCmBgYA0KDQpQb3LDs3duYW5pZToNCg0KTmllYmllc2thIGxpbmlhOiBzemVyb2tvxZvEhyBwYXNtYSAxDQpSw7PFvG93YSBsaW5pYTogc3plcm9rb8WbxIcgcGFzbWEgNQ0KDQpgYGB7cn0NCmdncGxvdChQcmVzdGlnZSkgKw0KICBnZW9tX3BvaW50KGFlcyh4PXByZXN0aWdlLHk9aW5jb21lKSkgKw0KICBnZW9tX2xpbmUoZGF0YT1maXQxLCBhZXMoeD14LHk9eSksIGNvbD0nZGFya2JsdWUnKSsNCiAgZ2VvbV9saW5lKGRhdGE9Zml0MiwgYWVzKHg9eCx5PXkpLCBjb2w9J3BpbmsnKSsNCiAgZ2d0aXRsZSgiS2VybmVsIHNtb290aGluZyAtIHBvcsOzd2FuYW5pZSBkd8OzY2ggc3plcm9rb8WbY2kgcGFzbSIpDQpgYGANCg0KDQpBYnkgZG9wYXNvd2HEhyBzemVyb2tvxZtjIHBhc21hDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0V9DQoNCmRvcGFzb3dhbmEgPC0gZHBpbGwocHJlc3RpZ2UsIGluY29tZSkNCmZpdDMgPC0gbG9jcG9seShwcmVzdGlnZSwgaW5jb21lLA0KICAgICAgICBkZWdyZWU9MCwgYmFuZHdpZHRoPWRvcGFzb3dhbmEpICU+JSBhcy50aWJibGUNCg0KcGFzdGUoIkRvcGFzb3dhbmEgc3plcm9rb8WbxIcgcGFzbWEgd3lub3NpOiIsIHJvdW5kKGRvcGFzb3dhbmEsIDQpKQ0KDQpnZ3Bsb3QoUHJlc3RpZ2UpICsNCiAgZ2VvbV9wb2ludChhZXMoeD1wcmVzdGlnZSx5PWluY29tZSkpKw0KICBnZW9tX2xpbmUoZGF0YT1maXQzLCBhZXMoeD14LHk9eSksIGNvbD0nZGFya2dyZWVuJyxzaXplPTAuNykNCmBgYA0KDQojIyMgUG9yw7N3bmFuaWUgcsOzxbxueWNoIGRlZ3JlZSAoc3RvcGllxYQgd2llbG9taWFudSkNCg0KYGBge3J9DQpmaXQxIDwtIGxvY3BvbHkocHJlc3RpZ2UsIGluY29tZSwNCiAgICAgICAgZGVncmVlPTAsIGJhbmR3aWR0aD1kb3Bhc293YW5hKSAlPiUgYXMudGliYmxlDQpmaXQyIDwtIGxvY3BvbHkocHJlc3RpZ2UsIGluY29tZSwNCiAgICAgICAgZGVncmVlPTEsIGJhbmR3aWR0aD1kb3Bhc293YW5hKSAlPiUgYXMudGliYmxlDQpmaXQzIDwtIGxvY3BvbHkocHJlc3RpZ2UsIGluY29tZSwNCiAgICAgICAgZGVncmVlPTIsIGJhbmR3aWR0aD1kb3Bhc293YW5hKSAlPiUgYXMudGliYmxlDQoNCmdncGxvdChQcmVzdGlnZSkgKw0KICBnZW9tX3BvaW50KGFlcyh4PXByZXN0aWdlLHk9aW5jb21lKSkgKw0KICBnZW9tX2xpbmUoZGF0YT1maXQxLCBhZXMoeD14LHk9eSksIGNvbD0nZGFya2JsdWUnLHNpemU9MC45KSsNCiAgZ2VvbV9saW5lKGRhdGE9Zml0MiwgYWVzKHg9eCx5PXkpLCBjb2w9J3BpbmsnLHNpemU9MC43KSsNCiAgZ2VvbV9saW5lKGRhdGE9Zml0MywgYWVzKHg9eCx5PXkpLCBjb2w9J2RhcmtncmVlbicsc2l6ZT0wLjUpKw0KICBnZ3RpdGxlKCJLZXJuZWwgc21vb3RoaW5nIC0gcG9yw7N3YW5hbmllIHRyemVjaCBzdG9wbmkgd2llbG9taWFudSIpDQpgYGANCld5YsOzciBkZWdyZWUgd3DFgnl3YSBuYSBmdW5rY2rEmSBnxJlzdG/Fm2NpIG5hIHd5a3Jlc2llLiANClcgcHJ6eXBhZGt1IGRlZ3JlZT0yIHd5c3TEmXB1amUgb3ZlcmZpdHRpbmcuIA0KTGVwc3p5bSB3eWJvcmVtIGJ5xYJvYnkgZGVncmVlIHLDs3duZSAwIGx1YiAxLg0KDQojIyBMb2VzcyAtIGxva2FsbmllIGt3YWRyYXRvd3kNCg0KYGBge3J9DQojIERvcGFzb3dhbmllIG1vZGVsdSBMT0VTUw0Kc21yIDwtIGxvZXNzKGluY29tZSB+IHByZXN0aWdlLCBzcGFuID0gMC43NSwgZGVncmVlID0gMiwgZmFtaWx5ID0gImdhdXNzaWFuIikNCg0KIyBUd29yemVuaWUgcmFta2kgZGFueWNoIHogd3luaWthbWkgbW9kZWx1DQpsb2Vzc19maXQgPC0gZGF0YS5mcmFtZSgNCiAgcHJlc3RpZ2UgPSBQcmVzdGlnZSRwcmVzdGlnZSwgIyBabWllbm5hIG5pZXphbGXFvG5hDQogIGZpdHRlZF92YWx1ZXMgPSBmaXR0ZWQoc21yKSAgICMgV2FydG/Fm2NpIGRvcGFzb3dhbmUNCikNCg0KIyBXeWtyZXMNCmdncGxvdChQcmVzdGlnZSkgKw0KICAjIFB1bmt0eSBkYW55Y2gNCiAgZ2VvbV9wb2ludChhZXMoeCA9IHByZXN0aWdlLCB5ID0gaW5jb21lKSkgKw0KICAjIERvcGFzb3dhbmEgbGluaWEgTE9FU1MNCiAgZ2VvbV9saW5lKGRhdGEgPSBsb2Vzc19maXQsIGFlcyh4ID0gcHJlc3RpZ2UsIHkgPSBmaXR0ZWRfdmFsdWVzKSwgY29sID0gJ3NreWJsdWUnLCBzaXplID0gMC44KSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIGxhYnMoDQogICAgeCA9ICJQcmVzdGlnZSIsDQogICAgeSA9ICJJbmNvbWUiLA0KICAgIHRpdGxlID0gIkRvcGFzb3dhbmllIG1vZGVsdSBMT0VTUyIsDQogICAgc3VidGl0bGUgPSAic3BhbiA9IDAuNzUsIGRlZ3JlZSA9IDIiDQogICkNCg0KYGBgDQpgYGB7cn0NCmdncGxvdChQcmVzdGlnZSkgKw0KICBnZW9tX3BvaW50KGFlcyh4PXByZXN0aWdlLHk9aW5jb21lKSkrDQogIGdlb21fc21vb3RoKGFlcyh4PXByZXN0aWdlLHk9aW5jb21lKSxtZXRob2Q9J2xvZXNzJyxzcGFuPTAuMjIpDQpgYGANCg0KIyMgU3Bsb3R5IGludGVycG9sdWrEhWNlIC0gc21vb3RoLnNwbGluZQ0KDQojIyMgWm1pZW5pYW5pZSBwYXJhbWV0cnUgJ3NwYXInDQoNCmBgYHtyfQ0KDQpmaXQxIDwtc21vb3RoLnNwbGluZShwcmVzdGlnZSwgaW5jb21lLCBzcGFyPTAuMikgDQpzbXIxIDwtIGRhdGEuZnJhbWUoeD1maXQxJHgseT1maXQxJHkpDQoNCmZpdDIgPC1zbW9vdGguc3BsaW5lKHByZXN0aWdlLCBpbmNvbWUsIHNwYXI9MC44KSANCnNtcjIgPC0gZGF0YS5mcmFtZSh4PWZpdDIkeCx5PWZpdDIkeSkNCg0KZ2dwbG90KFByZXN0aWdlKSArDQogIGdlb21fcG9pbnQoYWVzKHg9cHJlc3RpZ2UseT1pbmNvbWUpKSArDQogIGdlb21fbGluZShkYXRhPXNtcjEsIGFlcyh4PXgseT15KSwgY29sPSdkYXJrYmx1ZScsc2l6ZT0wLjgpKw0KICBnZW9tX2xpbmUoZGF0YT1zbXIyLCBhZXMoeD14LHk9eSksIGNvbD0ncGluaycsc2l6ZT0wLjcpKw0KICBnZ3RpdGxlKCJTcGxvdHkgaW50ZXJwb2x1asSFY2UgLSBwb3LDs3duYW5pZSByw7PFvG55Y2ggd2FydG/Fm2NpIHBhcmFtZXRydSAnc3BhciciKQ0KYGBgDQoNClNwYXI9MC4yIHBvd29kdWplIG1vY25lIGRvcGFzb3dhbmllIGRvIGRhbnljaC4gDQpTcGFyPTAuOCBwcmV6ZW50dWplIHpieXQgZHXFvGUgdW9nw7NsbmllbmllLg0KV25pb3NlazogDQpabW5pZWpzemFuaWUgJ3NwYXInIHBvd29kdWplIHdpxJlrc3plIGRvcGFzb3dhbmllLCBuYXRvbWlhc3QgendpxJlrc3phbmllIC0gd2nEmWtzemUgdW9nw7NsbmllbmllLg0KDQpBYnkgYXV0b21hdHljem5pZSB3eWJyYcSHIHN6ZXJva2/Fm2MgcGFzbWEgbW/FvG5hIHphc3Rvc293YcSHIGNyb3NzIHZhbGlkYXRpb24NCg0KIyMjIFphc3Rvc293YW5pZSBjcm9zcyB2YWxpZGF0aW9uIChDVj1UUlVFKQ0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0Kc21yMiA8LSBzbW9vdGguc3BsaW5lKA0KICBwcmVzdGlnZSwNCiAgaW5jb21lLA0KICBjdj1UUlVFKQ0Kc21yMiA8LSBkYXRhLmZyYW1lKHg9c21yMiR4LHk9c21yMiR5KQ0KZ2dwbG90KFByZXN0aWdlKSArDQogIGdlb21fcG9pbnQoYWVzKHg9cHJlc3RpZ2UseT1pbmNvbWUpKSArDQogIGdndGl0bGUoIlNwbG90eSBpbnRlcnBvbHVqxIVjZSwgbGFtYmRhIHd5YnJhbmEgcHJ6ZXogQ1YiKSArDQogIGdlb21fbGluZShkYXRhPXNtcjIsIGFlcyh4PXgsIHk9eSksIGNvbD0nZGFya2JsdWUnLCBzaXplPTAuOCkNCmBgYA0KDQojIyBQb3LDs3duYW5pZSBmdW5rY2ppIGxvY3BvbHkgaSBzbW9vdGguc3BsaW5lDQoNCk5pZWJpZXNrYSBsaW5pYSAtIGxvY3BvbHkNClLDs8W8b3dhIGxpbmlhIC0gc21vb3RoLnNwbGluZQ0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0KZml0IDwtIGxvY3BvbHkocHJlc3RpZ2UsIGluY29tZSwNCiAgICAgICAgZGVncmVlPTAsIGJhbmR3aWR0aD1kb3Bhc293YW5hKSAlPiUgYXMudGliYmxlDQoNCnNtciA8LSBzbW9vdGguc3BsaW5lKA0KICBwcmVzdGlnZSwNCiAgaW5jb21lLA0KICBjdj1UUlVFKQ0Kc21yIDwtIGRhdGEuZnJhbWUoeD1zbXIkeCx5PXNtciR5KQ0KDQpnZ3Bsb3QoUHJlc3RpZ2UpICsNCiAgZ2VvbV9wb2ludChhZXMoeD1wcmVzdGlnZSx5PWluY29tZSkpICsNCiAgZ2VvbV9saW5lKGRhdGE9Zml0LCBhZXMoeD14LHk9eSksIGNvbD0nZGFya2JsdWUnLCBzaXplPTAuNykrDQogIGdlb21fbGluZShkYXRhPXNtciwgYWVzKHg9eCx5PXkpLCBjb2w9J3BpbmsnLCBzaXplPTAuNykrDQogIGdndGl0bGUoIlBvcsOzd25hbmllIGZ1bmtjamkgbG9jcG9seSBpIHNtb290aC5zcGxpbmUiKQ0KYGBgDQoNCiMjIE5hdHVyYWxuZSBzcGxvdHkNCg0KYGBge3J9DQpsaWJyYXJ5KHNwbGluZXMpDQoNCmZpdCA8LWxtKGluY29tZX5ucyhwcmVzdGlnZSxkZj02KSxQcmVzdGlnZSkNCmZpdDIgPC1sbShpbmNvbWV+bnMocHJlc3RpZ2UsZGY9MTIpLFByZXN0aWdlKQ0KDQpnZ3Bsb3QoUHJlc3RpZ2UpKw0KICBnZW9tX3BvaW50KGFlcyh4PXByZXN0aWdlLHk9aW5jb21lKSkrDQogIGdndGl0bGUoIk5hdHVyYWxuZSBzcGxvdHksIDYgZGYobmllYmllc2tpKSBpIDEyIGRmKGN6ZXJ3b255KSIpKw0KICBnZW9tX2xpbmUoYWVzKHg9cHJlc3RpZ2UseT1maXR0ZWQoZml0KSksY29sPSdkYXJrYmx1ZScsIHNpemU9MC43KSsNCiAgZ2VvbV9saW5lKGFlcyh4PXByZXN0aWdlLHk9Zml0dGVkKGZpdDIpKSxjb2w9J3BpbmsnLCBzaXplPTAuNykNCmBgYA0KDQojIyBTcGxvdHkgb3JheiBnZW9tX3Ntb290aCgpDQoNCmBgYHtyfQ0KZ2dwbG90KFByZXN0aWdlKSArDQogICAgZ2VvbV9wb2ludChhZXMoeD1wcmVzdGlnZSx5PWluY29tZSkpICsNCiAgICBnZW9tX3Ntb290aChhZXMoeD1wcmVzdGlnZSx5PWZpdHRlZChmaXQpKSwgbWV0aG9kPSdnYW0nLA0KICAgICAgZm9ybXVsYSA9IHkgfiBzKHgsaz0xMiksIHNlPVRSVUUpDQpgYGANCg0KYGBge3J9DQpmaXQgPC0gbG0oaW5jb21lIH4gbnMocHJlc3RpZ2UsIGRmID0gNiksIFByZXN0aWdlKQ0KcHJlZCA8LSBkYXRhLmZyYW1lKHByZXN0aWdlID0gUHJlc3RpZ2UkcHJlc3RpZ2UsDQogICAgICAgICAgICAgICAgICAgcHJlZGljdChmaXQsIGludGVydmFsID0gImNvbmZpZGVuY2UiKSkNCmdncGxvdChQcmVzdGlnZSwgYWVzKHggPSBwcmVzdGlnZSwgeSA9IGluY29tZSkpICsNCiAgICBnZW9tX3BvaW50KCkgKw0KICAgIGdlb21fbGluZShkYXRhID0gcHJlZCwgYWVzKHkgPSBmaXQpLCBjb2xvciA9ICJkYXJrYmx1ZSIsIHNpemU9MC43KSArDQogICAgZ2VvbV9yaWJib24oZGF0YSA9IHByZWQsIGFlcyh5bWluID0gbHdyLCB5bWF4ID0gdXByKSwgYWxwaGEgPSAwLjIpDQpgYGANCg0KIyBaYWRhbmllIDIgLSBkYW5lIG1jeWNsZQ0KDQpaYmnDs3IgZGFueWNoIOKAnG1jeWNsZeKAnSAoeiBwYWtpZXR1IE1BU1MpIHphd2llcmEgbj0xMzMgcGFyeSBwdW5rdMOzdyBjemFzb3d5Y2ggKHcgbXMpIGkgb2JzZXJ3b3dhbnljaCBwcnp5c3BpZXN6ZcWEIGfFgm93eSAodyBnKSwga3TDs3JlIHpvc3RhxYJ5IHphcmVqZXN0cm93YW5lIHcgc3ltdWxvd2FueW0gd3lwYWRrdSBtb3RvY3lrbG93eW0uDQoNCkRvIHpiYWRhbmlhIHphbGXFvG5vxZtjaSBtacSZZHp5IGN6YXNlbSBhIHByenlzcGllc3plbmllbSB3eWtvcnp5c3RhaiBtZXRvZHkgcmVncmVzamkgbmllcGFyYW1ldHJ5Y3puZWouDQoNCk5hanBpZXJ3IHdjenl0YWogZGFuZSBpIHp3aXp1YWxpenVqIHphbGXFvG5vxZvEhyBtacSZZHp5IGN6YXNlbSAoWCkgYSBwcnp5c3BpZXN6ZW5pZW0gKFkpLg0KDQpgYGB7cn0NCmxpYnJhcnkoTUFTUykNCmRhdGEoIm1jeWNsZSIpDQphdHRhY2gobWN5Y2xlKQ0KDQpnZ3Bsb3QobWN5Y2xlKSsNCiAgZ2VvbV9wb2ludChhZXMoeD10aW1lcyx5PWFjY2VsKSkNCmBgYA0KWmFsZcW8bm/Fm8SHIHd5Z2zEhWRhIG5hIG5pZWxpbmlvd8SFLg0KDQpQcnp5c3BpZXN6ZW5pZSBqZXN0IHN0YWJpbG5lIG9kIDAtMTUgbXMsIHNwYWRhIG9kIG9rLiAxNS0yMCBtcywgcm/Fm25pZSBvZCAyMC0zMCBtcywgc3BhZGEgb2QgMzAtNDAgbXMsIGEgbmFzdMSZcG5pZSB6YWN6eW5hIHNpxJkgc3RhYmlsaXpvd2HEhy4NCg0KIyMgTG9jYWwgcG9seW5vbWlhbCByZWdyZXNzaW9uIChsb2Nwb2x5KSAtIG1ldG9kYSBsb2thbG5lZ28gd3lnxYJhZHphbmlhDQoNCiMjIyBQb3LDs3duYW5pZSByw7PFvG55Y2ggYmFuZHdpZHRoDQoNCmBgYHtyfQ0KZml0MSA8LSBsb2Nwb2x5KHRpbWVzLCBhY2NlbCwNCiAgICAgICAgZGVncmVlPTAsIGJhbmR3aWR0aD0xKSAlPiUgYXMudGliYmxlDQoNCmdncGxvdChtY3ljbGUpICsNCiAgZ2VvbV9wb2ludChhZXMoeD10aW1lcyx5PWFjY2VsKSkgKw0KICBnZW9tX2xpbmUoZGF0YT1maXQxLCBhZXMoeD14LHk9eSksIGNvbD0nZ3JlZW4nLCBzaXplPTAuNykrDQogIGdndGl0bGUoIktlcm5lbCBzbW9vdGhpbmcgLSBzemVyb2tvxZvEhyBwYXNtYSAxIikNCmBgYA0KDQpad2nEmWtzemFtIHN6ZXJva2/Fm8SHIHBhc21hIGRvIDUNCiAgDQpgYGB7cn0NCmZpdDIgPC0gbG9jcG9seSh0aW1lcywgYWNjZWwsDQogICAgICAgIGRlZ3JlZT0wLCBiYW5kd2lkdGg9NSkgJT4lIGFzLnRpYmJsZQ0KDQpnZ3Bsb3QobWN5Y2xlKSArDQogIGdlb21fcG9pbnQoYWVzKHg9dGltZXMseT1hY2NlbCkpICsNCiAgZ2VvbV9saW5lKGRhdGE9Zml0MiwgYWVzKHg9eCx5PXkpLCBjb2w9J2RhcmtyZWQnLCBzaXplPTAuNykrDQogIGdndGl0bGUoIktlcm5lbCBzbW9vdGhpbmcgLSBzemVyb2tvxZvEhyBwYXNtYSA1IikNCmBgYA0KDQpQb3LDs3duYW5pZToNCg0KWmllbG9uYSBsaW5pYTogc3plcm9rb8WbxIcgcGFzbWEgMQ0KQ3plcndvbmEgbGluaWE6IHN6ZXJva2/Fm8SHIHBhc21hIDUNCg0KYGBge3J9DQpnZ3Bsb3QobWN5Y2xlKSArDQogIGdlb21fcG9pbnQoYWVzKHg9dGltZXMseT1hY2NlbCkpICsNCiAgZ2VvbV9saW5lKGRhdGE9Zml0MSwgYWVzKHg9eCx5PXkpLCBjb2w9J2dyZWVuJywgc2l6ZT0wLjcpKw0KICBnZW9tX2xpbmUoZGF0YT1maXQyLCBhZXMoeD14LHk9eSksIGNvbD0nZGFya3JlZCcsIHNpemU9MC43KSsNCiAgZ2d0aXRsZSgiS2VybmVsIHNtb290aGluZyAtIHBvcsOzd2FuYW5pZSBzemVyb2tvxZtjaSBwYXNtIikNCmBgYA0KDQoNCkFieSBkb3Bhc293YcSHIHN6ZXJva2/Fm2MgcGFzbWEgLSBjcm9zcyB2YWxpZGF0aW9uDQoNCmBgYHtyfQ0KDQpkb3Bhc293YW5hIDwtIGRwaWxsKHRpbWVzLCBhY2NlbCkNCmZpdDMgPC0gbG9jcG9seSh0aW1lcywgYWNjZWwsDQogICAgICAgIGRlZ3JlZT0wLCBiYW5kd2lkdGg9ZG9wYXNvd2FuYSkgJT4lIGFzLnRpYmJsZQ0KDQpwYXN0ZSgiRG9wYXNvd2FuYSBzemVyb2tvxZvEhyBwYXNtYSB3eW5vc2k6Iiwgcm91bmQoZG9wYXNvd2FuYSwgNCkpDQoNCmdncGxvdChtY3ljbGUpICsNCiAgZ2VvbV9wb2ludChhZXMoeD10aW1lcyx5PWFjY2VsKSkrDQogIGdlb21fbGluZShkYXRhPWZpdDMsIGFlcyh4PXgseT15KSwgY29sPSdibHVlJyxzaXplPTEpDQpgYGANCg0KIyMjIFBvcsOzd25hbmllIHLDs8W8bnljaCBkZWdyZWUgKHN0b3BpZcWEIHdpZWxvbWlhbnUpDQoNCmBgYHtyfQ0KZml0MSA8LSBsb2Nwb2x5KHRpbWVzLCBhY2NlbCwNCiAgICAgICAgZGVncmVlPTAsIGJhbmR3aWR0aD1kb3Bhc293YW5hKSAlPiUgYXMudGliYmxlDQpmaXQyIDwtIGxvY3BvbHkodGltZXMsIGFjY2VsLCwNCiAgICAgICAgZGVncmVlPTEsIGJhbmR3aWR0aD1kb3Bhc293YW5hKSAlPiUgYXMudGliYmxlDQpmaXQzIDwtIGxvY3BvbHkodGltZXMsIGFjY2VsLA0KICAgICAgICBkZWdyZWU9MiwgYmFuZHdpZHRoPWRvcGFzb3dhbmEpICU+JSBhcy50aWJibGUNCg0KZ2dwbG90KG1jeWNsZSkgKw0KICBnZW9tX3BvaW50KGFlcyh4PXRpbWVzLHk9YWNjZWwpKSArDQogIGdlb21fbGluZShkYXRhPWZpdDEsIGFlcyh4PXgseT15KSwgY29sPSdncmVlbicsc2l6ZT0wLjgpKw0KICBnZW9tX2xpbmUoZGF0YT1maXQyLCBhZXMoeD14LHk9eSksIGNvbD0nZGFya3JlZCcsc2l6ZT0wLjgpKw0KICBnZW9tX2xpbmUoZGF0YT1maXQzLCBhZXMoeD14LHk9eSksIGNvbD0nYmx1ZScsc2l6ZT0wLjgpKw0KICBnZ3RpdGxlKCJLZXJuZWwgc21vb3RoaW5nIC0gcG9yw7N3YW5hbmllIHRyemVjaCBzdG9wbmkgd2llbG9taWFudSIpDQpgYGANCg0KIyMgTG9lc3MgLSBsb2thbG5pZSBrd2FkcmF0b3d5DQoNCmBgYHtyfQ0Kc21yIDwtIGxvZXNzIChhY2NlbH50aW1lcyxzcGFuPTAuNzUsIGRlZ3JlZT0yLA0KICAgICAgICAgICAgICAgZmFtaWx5PSJnYXVzc2lhbiIpDQoNCmdncGxvdChtY3ljbGUpICsNCiAgZ2VvbV9wb2ludChhZXMoeD10aW1lcyx5PWFjY2VsKSkrDQogIGdlb21fbGluZShhZXMoeD10aW1lcyx5PWZpdHRlZChzbXIpKSwgY29sPSdza3libHVlJywgc2l6ZT0wLjcpDQpgYGANCmBgYHtyfQ0KZ2dwbG90KG1jeWNsZSkgKw0KICBnZW9tX3BvaW50KGFlcyh4PXRpbWVzLHk9YWNjZWwpKSsNCiAgZ2VvbV9zbW9vdGgoYWVzKHg9dGltZXMseT1hY2NlbCksbWV0aG9kPSdsb2Vzcycsc3Bhbj0wLjIyKQ0KYGBgDQoNCiMjIFNwbG90eSBpbnRlcnBvbHVqxIVjZSAtIHNtb290aC5zcGxpbmUNCg0KIyMjIFptaWVuaWFuaWUgcGFyYW1ldHJ1IOKAmHNwYXLigJkNCg0KYGBge3J9DQpmaXQxIDwtc21vb3RoLnNwbGluZSh0aW1lcywgYWNjZWwsIHNwYXI9MC4yKSANCnNtcjEgPC0gZGF0YS5mcmFtZSh4PWZpdDEkeCx5PWZpdDEkeSkNCg0KZml0MiA8LXNtb290aC5zcGxpbmUodGltZXMsIGFjY2VsLCwgc3Bhcj0wLjgpIA0Kc21yMiA8LSBkYXRhLmZyYW1lKHg9Zml0MiR4LHk9Zml0MiR5KQ0KDQpnZ3Bsb3QobWN5Y2xlKSArDQogIGdlb21fcG9pbnQoYWVzKHg9dGltZXMseT1hY2NlbCkpICsNCiAgZ2VvbV9saW5lKGRhdGE9c21yMSwgYWVzKHg9eCx5PXkpLCBjb2w9J2dyZWVuJyxzaXplPTAuOCkrDQogIGdlb21fbGluZShkYXRhPXNtcjIsIGFlcyh4PXgseT15KSwgY29sPSdkYXJrcmVkJyxzaXplPTAuOCkrDQogIGdndGl0bGUoIlNwbG90eSBpbnRlcnBvbHVqxIVjZSAtIHBvcsOzd25hbmllIHLDs8W8bnljaCB3YXJ0b8WbY2kgcGFyYW1ldHJ1ICdzcGFyJyIpDQpgYGANCg0KU3Bhcj0wLjIgcG93b2R1amUgemJ5dCBtb2NuZSBkb3Bhc293YW5pZSBkbyBkYW55Y2guIA0KU3Bhcj0wLjggcHJlemVudHVqZSB6Ynl0IGR1xbxlIHVvZ8OzbG5pZW5pZS4gDQpXbmlvc2VrOiANClptbmllanN6YW5pZSDigJhzcGFy4oCZIHBvd29kdWplIHdpxJlrc3plIGRvcGFzb3dhbmllLCBhIHp3acSZa3N6YW5pZSAtIHdpxJlrc3plIHVvZ8OzbG5pZW5pZS4NCg0KQWJ5IGF1dG9tYXR5Y3puaWUgd3licmHEhyBzemVyb2tvxZtjIHBhc21hIG1vxbxuYSB6YXN0b3Nvd2HEhyBjcm9zcyB2YWxpZGF0aW9uDQoNCiMjIyBaYXN0b3Nvd2FuaWUgY3Jvc3MgdmFsaWRhdGlvbiAoQ1Y9VFJVRSkNCg0KYGBge3Igd2FybmluZz1GQUxTRX0NCnNtcjIgPC0gc21vb3RoLnNwbGluZSgNCiAgdGltZXMsDQogIGFjY2VsLA0KICBjdj1UUlVFKQ0Kc21yMiA8LSBkYXRhLmZyYW1lKHg9c21yMiR4LHk9c21yMiR5KQ0KZ2dwbG90KG1jeWNsZSkgKw0KICBnZW9tX3BvaW50KGFlcyh4PXRpbWVzLHk9YWNjZWwpKSArDQogIGdndGl0bGUoIlNwbG90eSBpbnRlcnBvbHVqxIVjZSwgbGFtYmRhIHd5YnJhbmEgcHJ6ZXogQ1YiKSArDQogIGdlb21fbGluZShkYXRhPXNtcjIsIGFlcyh4PXgsIHk9eSksIGNvbD0nZ3JlZW4nLCBzaXplPTAuNykNCmBgYA0KDQojIyBQb3LDs3duYW5pZSBmdW5rY2ppIGxvY3BvbHkgaSBzbW9vdGguc3BsaW5lDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0V9DQpmaXQgPC0gbG9jcG9seSh0aW1lcywgYWNjZWwsDQogICAgICAgIGRlZ3JlZT0wLCBiYW5kd2lkdGg9ZG9wYXNvd2FuYSkgJT4lIGFzLnRpYmJsZQ0KDQpzbXIgPC0gc21vb3RoLnNwbGluZSgNCiAgdGltZXMsDQogIGFjY2VsLA0KICBjdj1UUlVFKQ0Kc21yIDwtIGRhdGEuZnJhbWUoeD1zbXIkeCx5PXNtciR5KQ0KDQpnZ3Bsb3QobWN5Y2xlKSArDQogIGdlb21fcG9pbnQoYWVzKHg9dGltZXMseT1hY2NlbCkpICsNCiAgZ2VvbV9saW5lKGRhdGE9Zml0LCBhZXMoeD14LHk9eSksIGNvbD0nZ3JlZW4nLCBzaXplPTAuNykrDQogIGdlb21fbGluZShkYXRhPXNtciwgYWVzKHg9eCx5PXkpLCBjb2w9J2RhcmtyZWQnLCBzaXplPTAuNykrDQogIGdndGl0bGUoIlBvcsOzd25hbmllIGZ1bmtjamkgbG9jcG9seSBpIHNtb290aC5zcGxpbmUiKQ0KYGBgDQoNCiMjIE5hdHVyYWxuZSBzcGxvdHkNCg0KYGBge3J9DQpsaWJyYXJ5KHNwbGluZXMpDQoNCmZpdCA8LWxtKGFjY2Vsfm5zKHRpbWVzLGRmPTYpLG1jeWNsZSkNCmZpdDIgPC1sbShhY2NlbH5ucyh0aW1lcyxkZj0xMiksbWN5Y2xlKQ0KDQpnZ3Bsb3QobWN5Y2xlKSsNCiAgZ2VvbV9wb2ludChhZXMoeD10aW1lcyx5PWFjY2VsKSkrDQogIGdndGl0bGUoIk5hdHVyYWxuZSBzcGxvdHksIDYgZGYobmllYmllc2tpKSBpIDEyIGRmKGN6ZXJ3b255KSIpKw0KICBnZW9tX2xpbmUoYWVzKHg9dGltZXMseT1maXR0ZWQoZml0KSksY29sPSdncmVlbicsIHNpemU9MC43KSsNCiAgZ2VvbV9saW5lKGFlcyh4PXRpbWVzLHk9Zml0dGVkKGZpdDIpKSxjb2w9J2RhcmtyZWQnLCBzaXplPTAuNykNCmBgYA0KDQojIyBTcGxvdHkgb3JheiBnZW9tX3Ntb290aCgpDQoNCmBgYHtyfQ0KZ2dwbG90KG1jeWNsZSkgKw0KICAgIGdlb21fcG9pbnQoYWVzKHg9dGltZXMseT1hY2NlbCkpICsNCiAgICBnZW9tX3Ntb290aChhZXMoeD10aW1lcyx5PWZpdHRlZChmaXQpKSwgbWV0aG9kPSdnYW0nLA0KICAgICAgZm9ybXVsYSA9IHkgfiBzKHgsaz0xMiksIHNlPVRSVUUpDQpgYGANCg0KYGBge3J9DQpmaXQgPC0gbG0oYWNjZWwgfiBucyh0aW1lcywgZGYgPSA2KSwgbWN5Y2xlKQ0KcHJlZCA8LSBkYXRhLmZyYW1lKHRpbWVzID0gbWN5Y2xlJHRpbWVzLA0KICAgICAgICAgICAgICAgICAgIHByZWRpY3QoZml0LCBpbnRlcnZhbCA9ICJjb25maWRlbmNlIikpDQpnZ3Bsb3QobWN5Y2xlLCBhZXMoeCA9IHRpbWVzLCB5ID0gYWNjZWwpKSArDQogICAgZ2VvbV9wb2ludCgpICsNCiAgICBnZW9tX2xpbmUoZGF0YSA9IHByZWQsIGFlcyh5ID0gZml0KSwgY29sb3IgPSAiZ3JlZW4iLCBzaXplPTAuNykgKw0KICAgIGdlb21fcmliYm9uKGRhdGEgPSBwcmVkLCBhZXMoeW1pbiA9IGx3ciwgeW1heCA9IHVwciksIGFscGhhID0gMC4yKQ0KYGBgDQoNCg==