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

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

ggplot(Prestige) +
  geom_point(aes(x=prestige,y=income))+
  geom_line(aes(x=prestige,y=fitted(smr)), col='skyblue',size=0.8)

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)

LS0tDQp0aXRsZTogIlJhcG9ydCAzIg0KYXV0aG9yOiAiS2Fyb2xpbmEgV2FqYyINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0aGVtZTogY2VydWxlYW4NCiAgICBoaWdobGlnaHQ6IHRleHRtYXRlDQogICAgZm9udHNpemU6IDhwdA0KICAgIHRvYzogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IG5vDQogIHBkZl9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0Kc3VidGl0bGU6IFJlZ3Jlc2phIG5pZXBhcmFtZXRyeWN6bmENCmVkaXRvcl9vcHRpb25zOg0KICBtYXJrZG93bjoNCiAgICB3cmFwOiA3Mg0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQpsaWJyYXJ5KGNhcikNCmxpYnJhcnkoS2VyblNtb290aCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KYGBgDQoNCiMgWmFkYW5pZSAxIC0gZGFuZSBQcmVzdGlnZQ0KDQpaZXN0YXcgZGFueWNoIOKAnFByZXN0aWdl4oCdICh6IHBha2lldHUg4oCcY2Fy4oCdKSB6YXdpZXJhIGRhbmUgbnQuIHByZXN0acW8dSBuPTEwMiBLYW5hZHlqc2tpY2ggemF3b2TDs3cgeiAxOTcxIHJva3UsIGEgdGFrxbxlIMWbcmVkbmkgZG9jaMOzZCB3IGRhbnltIHphd29kemllLiBEbyB6YmFkYW5pYSB6YWxlxbxub8WbY2kgbWnEmWR6eSBwcmVzdGnFvGVtIGEgZG9jaG9kZW0gd3lrb3J6eXN0YWogbWV0b2R5IHJlZ3Jlc2ppIG5pZXBhcmFtZXRyeWN6bmVqLg0KDQpOYWpwaWVydyB6YcWCYWR1aiBkYW5lIGkgendpenVhbGl6dWogcmVsYWNqxJkgcG9tacSZZHp5IGRvY2hvZGVtIChYKSBhIHByZXN0acW8ZW0gKFkpLg0KDQpgYGB7cn0NCmRhdGEoIlByZXN0aWdlIikNCmF0dGFjaChQcmVzdGlnZSkNCg0KZ2dwbG90KFByZXN0aWdlKSsNCiAgZ2VvbV9wb2ludChhZXMoeD1wcmVzdGlnZSwgeT1pbmNvbWUpKQ0KYGBgDQoNClp3acSFemVrIHd5Z2zEhWRhIG5hIG5pZWxpbmlvd3kuIElzdG5pZWplIHNpbG5hIChwb3p5dHl3bmEpIGxpbmlvd2EgemFsZcW8bm/Fm8SHIHBvbWnEmWR6eSBkb2Nob2RlbSBhIHByZXN0acW8ZW0gZGxhIHphd29kw7N3LCBrdMOzcmUgemFyYWJpYWrEhSBtbmllaiBuacW8ICQxMEsuIFcgcHJ6eXBhZGt1IHphd29kw7N3LCBrdMOzcmUgemFyYWJpYWrEhSB3IHpha3Jlc2llIG9kIDEwIGRvIDI1IHR5c2nEmWN5IGRvbGFyw7N3LCB6d2nEhXplayB0ZW4gbWEgem5hY3puaWUgaW5uZSBuYWNoeWxlbmllLg0KDQpNZXRvZHkgcmVncmVzamkgbmllcGFyYW1ldHJ5Y3puZWouDQoNCiMjIExvY2FsIHBvbHlub21pYWwgcmVncmVzc2lvbiAobG9jcG9seSkgLSBtZXRvZGEgbG9rYWxuZWdvIHd5Z8WCYWR6YW5pYQ0KDQpHxYLDs3duZSBwYXJhbWV0cnksIHRha2llIGphayBkZWdyZWUgaSBiYW5kd2lkdGgsIG1hasSFIGtsdWN6b3d5IHdwxYJ5dyBuYSBzcG9zw7NiLCB3IGpha2kgd3lnxYJhZHphbmEgamVzdCB6YWxlxbxub8WbxIcuDQoNClBhcmFtZXRyIGRlZ3JlZSBva3JlxZtsYSBzdG9waWXFhCB3aWVsb21pYW51IHXFvHl0ZWdvIGRvIGxva2FsbmVqIGFwcm9rc3ltYWNqaS4gRGVncmVlPTAgb3puYWN6YSB1xbx5Y2llIGxva2FsbnljaCDFm3JlZG5pY2ggKGNvIG9kcG93aWFkYSByZWdyZXNqaSBuYWpibGnFvHN6eWNoIHPEhXNpYWTDs3cpLiANCg0KRGVncmVlPTEgb3puYWN6YSB3eWtvcnp5c3RhbmllIGxva2FsbmVqIHJlZ3Jlc2ppIGxpbmlvd2VqIA0KDQpEZWdyZWU9MiBvem5hY3phIHd5a29yenlzdGFuaWUgbG9rYWxuZWogcmVncmVzamkga3dhZHJhdG93ZWouDQoNClBhcmFtZXRyIGJhbmR3aWR0aCBkZWN5ZHVqZSBvIHJvenBpxJl0b8WbY2kgasSFZHJhLCBjenlsaSBkZWZpbml1amUgcm96bWlhciBvYnN6YXJ1IHcgamFraW0gb2JzZXJ3YWNqZSB3cMWCeXdhasSFIG5hIGZ1bmtjasSZIGfEmXN0b8WbY2kuSW0gd2nEmWtzemEgd2FydG/Fm8SHIGJhbmR3aWR0aCwgdHltIGJhcmR6aWVqIHd5Z8WCYWR6b25hIGplc3QgZnVua2NqYS4gWmFyw7N3bm8gemJ5dCBuaXNrYSB3YXJ0b8WbxIcgYmFuZHdpZHRoIG1vxbxlIGJ5xIcgbmllb2Rwb3dpZWRuaWEgKG92ZXJmaXR0aW5nLSBtb2RlbCB6Ynl0IHN6Y3plZ8OzxYJvd3kpLCBqYWsgaSByw7N3bmllxbwgemJ5dCB3eXNva2Egd2FydG/Fm8SHIHRlZ28gcGFyYW1ldHJ1ICh1bmRlcmZpdHRpbmctIHpieXQgZHXFvGUgd3lnxYJhZHplbmllLCB6Ynl0IGR1xbxlIHVvZ8OzbG5pZW5pZSkuDQoNCiMjIyBQb3LDs3duYW5pZSByw7PFvG55Y2ggYmFuZHdpZHRoDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0V9DQpmaXQxIDwtIGxvY3BvbHkocHJlc3RpZ2UsIGluY29tZSwNCiAgICAgICAgZGVncmVlPTAsIGJhbmR3aWR0aD0xKSAlPiUgYXMudGliYmxlDQoNCmdncGxvdChQcmVzdGlnZSkgKw0KICBnZW9tX3BvaW50KGFlcyh4PXByZXN0aWdlLHk9aW5jb21lKSkgKw0KICBnZW9tX2xpbmUoZGF0YT1maXQxLCBhZXMoeD14LHk9eSksIGNvbD0nZGFya2JsdWUnLCBzaXplPTAuOCkrDQogIGdndGl0bGUoIktlcm5lbCBzbW9vdGhpbmcgLSBzemVyb2tvxZvEhyBwYXNtYSByw7N3bmEgMSIpDQpgYGANCg0KWndpxJlrc3plbmllIHN6ZXJva2/Fm2NpIHBhc21hIGRvIDUuDQogIA0KYGBge3J9DQpmaXQyIDwtIGxvY3BvbHkocHJlc3RpZ2UsIGluY29tZSwNCiAgICAgICAgZGVncmVlPTAsIGJhbmR3aWR0aD01KSAlPiUgYXMudGliYmxlDQoNCmdncGxvdChQcmVzdGlnZSkgKw0KICBnZW9tX3BvaW50KGFlcyh4PXByZXN0aWdlLHk9aW5jb21lKSkgKw0KICBnZW9tX2xpbmUoZGF0YT1maXQyLCBhZXMoeD14LHk9eSksIGNvbD0ncGluaycsc2l6ZT0wLjkpKw0KICBnZ3RpdGxlKCJLZXJuZWwgc21vb3RoaW5nIC0gc3plcm9rb8WbxIcgcGFzbWEgNSIpDQpgYGANCg0KUG9yw7N3bmFuaWU6DQoNCk5pZWJpZXNrYSBsaW5pYTogc3plcm9rb8WbxIcgcGFzbWEgMQ0KUsOzxbxvd2EgbGluaWE6IHN6ZXJva2/Fm8SHIHBhc21hIDUNCg0KYGBge3J9DQpnZ3Bsb3QoUHJlc3RpZ2UpICsNCiAgZ2VvbV9wb2ludChhZXMoeD1wcmVzdGlnZSx5PWluY29tZSkpICsNCiAgZ2VvbV9saW5lKGRhdGE9Zml0MSwgYWVzKHg9eCx5PXkpLCBjb2w9J2RhcmtibHVlJykrDQogIGdlb21fbGluZShkYXRhPWZpdDIsIGFlcyh4PXgseT15KSwgY29sPSdwaW5rJykrDQogIGdndGl0bGUoIktlcm5lbCBzbW9vdGhpbmcgLSBwb3LDs3dhbmFuaWUgZHfDs2NoIHN6ZXJva2/Fm2NpIHBhc20iKQ0KYGBgDQoNCg0KQWJ5IGRvcGFzb3dhxIcgc3plcm9rb8WbYyBwYXNtYQ0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0KDQpkb3Bhc293YW5hIDwtIGRwaWxsKHByZXN0aWdlLCBpbmNvbWUpDQpmaXQzIDwtIGxvY3BvbHkocHJlc3RpZ2UsIGluY29tZSwNCiAgICAgICAgZGVncmVlPTAsIGJhbmR3aWR0aD1kb3Bhc293YW5hKSAlPiUgYXMudGliYmxlDQoNCnBhc3RlKCJEb3Bhc293YW5hIHN6ZXJva2/Fm8SHIHBhc21hIHd5bm9zaToiLCByb3VuZChkb3Bhc293YW5hLCA0KSkNCg0KZ2dwbG90KFByZXN0aWdlKSArDQogIGdlb21fcG9pbnQoYWVzKHg9cHJlc3RpZ2UseT1pbmNvbWUpKSsNCiAgZ2VvbV9saW5lKGRhdGE9Zml0MywgYWVzKHg9eCx5PXkpLCBjb2w9J2RhcmtncmVlbicsc2l6ZT0wLjcpDQpgYGANCg0KIyMjIFBvcsOzd25hbmllIHLDs8W8bnljaCBkZWdyZWUgKHN0b3BpZcWEIHdpZWxvbWlhbnUpDQoNCmBgYHtyfQ0KZml0MSA8LSBsb2Nwb2x5KHByZXN0aWdlLCBpbmNvbWUsDQogICAgICAgIGRlZ3JlZT0wLCBiYW5kd2lkdGg9ZG9wYXNvd2FuYSkgJT4lIGFzLnRpYmJsZQ0KZml0MiA8LSBsb2Nwb2x5KHByZXN0aWdlLCBpbmNvbWUsDQogICAgICAgIGRlZ3JlZT0xLCBiYW5kd2lkdGg9ZG9wYXNvd2FuYSkgJT4lIGFzLnRpYmJsZQ0KZml0MyA8LSBsb2Nwb2x5KHByZXN0aWdlLCBpbmNvbWUsDQogICAgICAgIGRlZ3JlZT0yLCBiYW5kd2lkdGg9ZG9wYXNvd2FuYSkgJT4lIGFzLnRpYmJsZQ0KDQpnZ3Bsb3QoUHJlc3RpZ2UpICsNCiAgZ2VvbV9wb2ludChhZXMoeD1wcmVzdGlnZSx5PWluY29tZSkpICsNCiAgZ2VvbV9saW5lKGRhdGE9Zml0MSwgYWVzKHg9eCx5PXkpLCBjb2w9J2RhcmtibHVlJyxzaXplPTAuOSkrDQogIGdlb21fbGluZShkYXRhPWZpdDIsIGFlcyh4PXgseT15KSwgY29sPSdwaW5rJyxzaXplPTAuNykrDQogIGdlb21fbGluZShkYXRhPWZpdDMsIGFlcyh4PXgseT15KSwgY29sPSdkYXJrZ3JlZW4nLHNpemU9MC41KSsNCiAgZ2d0aXRsZSgiS2VybmVsIHNtb290aGluZyAtIHBvcsOzd2FuYW5pZSB0cnplY2ggc3RvcG5pIHdpZWxvbWlhbnUiKQ0KYGBgDQpXeWLDs3IgZGVncmVlIHdwxYJ5d2EgbmEgZnVua2NqxJkgZ8SZc3RvxZtjaSBuYSB3eWtyZXNpZS4gDQpXIHByenlwYWRrdSBkZWdyZWU9MiB3eXN0xJlwdWplIG92ZXJmaXR0aW5nLiANCkxlcHN6eW0gd3lib3JlbSBiecWCb2J5IGRlZ3JlZSByw7N3bmUgMCBsdWIgMS4NCg0KIyMgTG9lc3MgLSBsb2thbG5pZSBrd2FkcmF0b3d5DQoNCmBgYHtyfQ0Kc21yIDwtIGxvZXNzIChpbmNvbWV+cHJlc3RpZ2Usc3Bhbj0wLjc1LCBkZWdyZWU9MiwNCiAgICAgICAgICAgICAgIGZhbWlseT0iZ2F1c3NpYW4iKQ0KDQpnZ3Bsb3QoUHJlc3RpZ2UpICsNCiAgZ2VvbV9wb2ludChhZXMoeD1wcmVzdGlnZSx5PWluY29tZSkpKw0KICBnZW9tX2xpbmUoYWVzKHg9cHJlc3RpZ2UseT1maXR0ZWQoc21yKSksIGNvbD0nc2t5Ymx1ZScsc2l6ZT0wLjgpDQpgYGANCmBgYHtyfQ0KZ2dwbG90KFByZXN0aWdlKSArDQogIGdlb21fcG9pbnQoYWVzKHg9cHJlc3RpZ2UseT1pbmNvbWUpKSsNCiAgZ2VvbV9zbW9vdGgoYWVzKHg9cHJlc3RpZ2UseT1pbmNvbWUpLG1ldGhvZD0nbG9lc3MnLHNwYW49MC4yMikNCmBgYA0KDQojIyBTcGxvdHkgaW50ZXJwb2x1asSFY2UgLSBzbW9vdGguc3BsaW5lDQoNCiMjIyBabWllbmlhbmllIHBhcmFtZXRydSAnc3BhcicNCg0KYGBge3J9DQoNCmZpdDEgPC1zbW9vdGguc3BsaW5lKHByZXN0aWdlLCBpbmNvbWUsIHNwYXI9MC4yKSANCnNtcjEgPC0gZGF0YS5mcmFtZSh4PWZpdDEkeCx5PWZpdDEkeSkNCg0KZml0MiA8LXNtb290aC5zcGxpbmUocHJlc3RpZ2UsIGluY29tZSwgc3Bhcj0wLjgpIA0Kc21yMiA8LSBkYXRhLmZyYW1lKHg9Zml0MiR4LHk9Zml0MiR5KQ0KDQpnZ3Bsb3QoUHJlc3RpZ2UpICsNCiAgZ2VvbV9wb2ludChhZXMoeD1wcmVzdGlnZSx5PWluY29tZSkpICsNCiAgZ2VvbV9saW5lKGRhdGE9c21yMSwgYWVzKHg9eCx5PXkpLCBjb2w9J2RhcmtibHVlJyxzaXplPTAuOCkrDQogIGdlb21fbGluZShkYXRhPXNtcjIsIGFlcyh4PXgseT15KSwgY29sPSdwaW5rJyxzaXplPTAuNykrDQogIGdndGl0bGUoIlNwbG90eSBpbnRlcnBvbHVqxIVjZSAtIHBvcsOzd25hbmllIHLDs8W8bnljaCB3YXJ0b8WbY2kgcGFyYW1ldHJ1ICdzcGFyJyIpDQpgYGANCg0KU3Bhcj0wLjIgcG93b2R1amUgbW9jbmUgZG9wYXNvd2FuaWUgZG8gZGFueWNoLiANClNwYXI9MC44IHByZXplbnR1amUgemJ5dCBkdcW8ZSB1b2fDs2xuaWVuaWUuDQpXbmlvc2VrOiANClptbmllanN6YW5pZSAnc3BhcicgcG93b2R1amUgd2nEmWtzemUgZG9wYXNvd2FuaWUsIG5hdG9taWFzdCB6d2nEmWtzemFuaWUgLSB3acSZa3N6ZSB1b2fDs2xuaWVuaWUuDQoNCkFieSBhdXRvbWF0eWN6bmllIHd5YnJhxIcgc3plcm9rb8WbYyBwYXNtYSBtb8W8bmEgemFzdG9zb3dhxIcgY3Jvc3MgdmFsaWRhdGlvbg0KDQojIyMgWmFzdG9zb3dhbmllIGNyb3NzIHZhbGlkYXRpb24gKENWPVRSVUUpDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0V9DQpzbXIyIDwtIHNtb290aC5zcGxpbmUoDQogIHByZXN0aWdlLA0KICBpbmNvbWUsDQogIGN2PVRSVUUpDQpzbXIyIDwtIGRhdGEuZnJhbWUoeD1zbXIyJHgseT1zbXIyJHkpDQpnZ3Bsb3QoUHJlc3RpZ2UpICsNCiAgZ2VvbV9wb2ludChhZXMoeD1wcmVzdGlnZSx5PWluY29tZSkpICsNCiAgZ2d0aXRsZSgiU3Bsb3R5IGludGVycG9sdWrEhWNlLCBsYW1iZGEgd3licmFuYSBwcnpleiBDViIpICsNCiAgZ2VvbV9saW5lKGRhdGE9c21yMiwgYWVzKHg9eCwgeT15KSwgY29sPSdkYXJrYmx1ZScsIHNpemU9MC44KQ0KYGBgDQoNCiMjIFBvcsOzd25hbmllIGZ1bmtjamkgbG9jcG9seSBpIHNtb290aC5zcGxpbmUNCg0KTmllYmllc2thIGxpbmlhIC0gbG9jcG9seQ0KUsOzxbxvd2EgbGluaWEgLSBzbW9vdGguc3BsaW5lDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0V9DQpmaXQgPC0gbG9jcG9seShwcmVzdGlnZSwgaW5jb21lLA0KICAgICAgICBkZWdyZWU9MCwgYmFuZHdpZHRoPWRvcGFzb3dhbmEpICU+JSBhcy50aWJibGUNCg0Kc21yIDwtIHNtb290aC5zcGxpbmUoDQogIHByZXN0aWdlLA0KICBpbmNvbWUsDQogIGN2PVRSVUUpDQpzbXIgPC0gZGF0YS5mcmFtZSh4PXNtciR4LHk9c21yJHkpDQoNCmdncGxvdChQcmVzdGlnZSkgKw0KICBnZW9tX3BvaW50KGFlcyh4PXByZXN0aWdlLHk9aW5jb21lKSkgKw0KICBnZW9tX2xpbmUoZGF0YT1maXQsIGFlcyh4PXgseT15KSwgY29sPSdkYXJrYmx1ZScsIHNpemU9MC43KSsNCiAgZ2VvbV9saW5lKGRhdGE9c21yLCBhZXMoeD14LHk9eSksIGNvbD0ncGluaycsIHNpemU9MC43KSsNCiAgZ2d0aXRsZSgiUG9yw7N3bmFuaWUgZnVua2NqaSBsb2Nwb2x5IGkgc21vb3RoLnNwbGluZSIpDQpgYGANCg0KIyMgTmF0dXJhbG5lIHNwbG90eQ0KDQpgYGB7cn0NCmxpYnJhcnkoc3BsaW5lcykNCg0KZml0IDwtbG0oaW5jb21lfm5zKHByZXN0aWdlLGRmPTYpLFByZXN0aWdlKQ0KZml0MiA8LWxtKGluY29tZX5ucyhwcmVzdGlnZSxkZj0xMiksUHJlc3RpZ2UpDQoNCmdncGxvdChQcmVzdGlnZSkrDQogIGdlb21fcG9pbnQoYWVzKHg9cHJlc3RpZ2UseT1pbmNvbWUpKSsNCiAgZ2d0aXRsZSgiTmF0dXJhbG5lIHNwbG90eSwgNiBkZihuaWViaWVza2kpIGkgMTIgZGYoY3plcndvbnkpIikrDQogIGdlb21fbGluZShhZXMoeD1wcmVzdGlnZSx5PWZpdHRlZChmaXQpKSxjb2w9J2RhcmtibHVlJywgc2l6ZT0wLjcpKw0KICBnZW9tX2xpbmUoYWVzKHg9cHJlc3RpZ2UseT1maXR0ZWQoZml0MikpLGNvbD0ncGluaycsIHNpemU9MC43KQ0KYGBgDQoNCiMjIFNwbG90eSBvcmF6IGdlb21fc21vb3RoKCkNCg0KYGBge3J9DQpnZ3Bsb3QoUHJlc3RpZ2UpICsNCiAgICBnZW9tX3BvaW50KGFlcyh4PXByZXN0aWdlLHk9aW5jb21lKSkgKw0KICAgIGdlb21fc21vb3RoKGFlcyh4PXByZXN0aWdlLHk9Zml0dGVkKGZpdCkpLCBtZXRob2Q9J2dhbScsDQogICAgICBmb3JtdWxhID0geSB+IHMoeCxrPTEyKSwgc2U9VFJVRSkNCmBgYA0KDQpgYGB7cn0NCmZpdCA8LSBsbShpbmNvbWUgfiBucyhwcmVzdGlnZSwgZGYgPSA2KSwgUHJlc3RpZ2UpDQpwcmVkIDwtIGRhdGEuZnJhbWUocHJlc3RpZ2UgPSBQcmVzdGlnZSRwcmVzdGlnZSwNCiAgICAgICAgICAgICAgICAgICBwcmVkaWN0KGZpdCwgaW50ZXJ2YWwgPSAiY29uZmlkZW5jZSIpKQ0KZ2dwbG90KFByZXN0aWdlLCBhZXMoeCA9IHByZXN0aWdlLCB5ID0gaW5jb21lKSkgKw0KICAgIGdlb21fcG9pbnQoKSArDQogICAgZ2VvbV9saW5lKGRhdGEgPSBwcmVkLCBhZXMoeSA9IGZpdCksIGNvbG9yID0gImRhcmtibHVlIiwgc2l6ZT0wLjcpICsNCiAgICBnZW9tX3JpYmJvbihkYXRhID0gcHJlZCwgYWVzKHltaW4gPSBsd3IsIHltYXggPSB1cHIpLCBhbHBoYSA9IDAuMikNCmBgYA0KDQojIFphZGFuaWUgMiAtIGRhbmUgbWN5Y2xlDQoNClpiacOzciBkYW55Y2gg4oCcbWN5Y2xl4oCdICh6IHBha2lldHUgTUFTUykgemF3aWVyYSBuPTEzMyBwYXJ5IHB1bmt0w7N3IGN6YXNvd3ljaCAodyBtcykgaSBvYnNlcndvd2FueWNoIHByenlzcGllc3plxYQgZ8WCb3d5ICh3IGcpLCBrdMOzcmUgem9zdGHFgnkgemFyZWplc3Ryb3dhbmUgdyBzeW11bG93YW55bSB3eXBhZGt1IG1vdG9jeWtsb3d5bS4NCg0KRG8gemJhZGFuaWEgemFsZcW8bm/Fm2NpIG1pxJlkenkgY3phc2VtIGEgcHJ6eXNwaWVzemVuaWVtIHd5a29yenlzdGFqIG1ldG9keSByZWdyZXNqaSBuaWVwYXJhbWV0cnljem5lai4NCg0KTmFqcGllcncgd2N6eXRhaiBkYW5lIGkgendpenVhbGl6dWogemFsZcW8bm/Fm8SHIG1pxJlkenkgY3phc2VtIChYKSBhIHByenlzcGllc3plbmllbSAoWSkuDQoNCmBgYHtyfQ0KbGlicmFyeShNQVNTKQ0KZGF0YSgibWN5Y2xlIikNCmF0dGFjaChtY3ljbGUpDQoNCmdncGxvdChtY3ljbGUpKw0KICBnZW9tX3BvaW50KGFlcyh4PXRpbWVzLHk9YWNjZWwpKQ0KYGBgDQpaYWxlxbxub8WbxIcgd3lnbMSFZGEgbmEgbmllbGluaW93xIUuDQoNClByenlzcGllc3plbmllIGplc3Qgc3RhYmlsbmUgb2QgMC0xNSBtcywgc3BhZGEgb2Qgb2suIDE1LTIwIG1zLCByb8WbbmllIG9kIDIwLTMwIG1zLCBzcGFkYSBvZCAzMC00MCBtcywgYSBuYXN0xJlwbmllIHphY3p5bmEgc2nEmSBzdGFiaWxpem93YcSHLg0KDQojIyBMb2NhbCBwb2x5bm9taWFsIHJlZ3Jlc3Npb24gKGxvY3BvbHkpIC0gbWV0b2RhIGxva2FsbmVnbyB3eWfFgmFkemFuaWENCg0KIyMjIFBvcsOzd25hbmllIHLDs8W8bnljaCBiYW5kd2lkdGgNCg0KYGBge3J9DQpmaXQxIDwtIGxvY3BvbHkodGltZXMsIGFjY2VsLA0KICAgICAgICBkZWdyZWU9MCwgYmFuZHdpZHRoPTEpICU+JSBhcy50aWJibGUNCg0KZ2dwbG90KG1jeWNsZSkgKw0KICBnZW9tX3BvaW50KGFlcyh4PXRpbWVzLHk9YWNjZWwpKSArDQogIGdlb21fbGluZShkYXRhPWZpdDEsIGFlcyh4PXgseT15KSwgY29sPSdncmVlbicsIHNpemU9MC43KSsNCiAgZ2d0aXRsZSgiS2VybmVsIHNtb290aGluZyAtIHN6ZXJva2/Fm8SHIHBhc21hIDEiKQ0KYGBgDQoNClp3acSZa3N6YW0gc3plcm9rb8WbxIcgcGFzbWEgZG8gNQ0KICANCmBgYHtyfQ0KZml0MiA8LSBsb2Nwb2x5KHRpbWVzLCBhY2NlbCwNCiAgICAgICAgZGVncmVlPTAsIGJhbmR3aWR0aD01KSAlPiUgYXMudGliYmxlDQoNCmdncGxvdChtY3ljbGUpICsNCiAgZ2VvbV9wb2ludChhZXMoeD10aW1lcyx5PWFjY2VsKSkgKw0KICBnZW9tX2xpbmUoZGF0YT1maXQyLCBhZXMoeD14LHk9eSksIGNvbD0nZGFya3JlZCcsIHNpemU9MC43KSsNCiAgZ2d0aXRsZSgiS2VybmVsIHNtb290aGluZyAtIHN6ZXJva2/Fm8SHIHBhc21hIDUiKQ0KYGBgDQoNClBvcsOzd25hbmllOg0KDQpaaWVsb25hIGxpbmlhOiBzemVyb2tvxZvEhyBwYXNtYSAxDQpDemVyd29uYSBsaW5pYTogc3plcm9rb8WbxIcgcGFzbWEgNQ0KDQpgYGB7cn0NCmdncGxvdChtY3ljbGUpICsNCiAgZ2VvbV9wb2ludChhZXMoeD10aW1lcyx5PWFjY2VsKSkgKw0KICBnZW9tX2xpbmUoZGF0YT1maXQxLCBhZXMoeD14LHk9eSksIGNvbD0nZ3JlZW4nLCBzaXplPTAuNykrDQogIGdlb21fbGluZShkYXRhPWZpdDIsIGFlcyh4PXgseT15KSwgY29sPSdkYXJrcmVkJywgc2l6ZT0wLjcpKw0KICBnZ3RpdGxlKCJLZXJuZWwgc21vb3RoaW5nIC0gcG9yw7N3YW5hbmllIHN6ZXJva2/Fm2NpIHBhc20iKQ0KYGBgDQoNCg0KQWJ5IGRvcGFzb3dhxIcgc3plcm9rb8WbYyBwYXNtYSAtIGNyb3NzIHZhbGlkYXRpb24NCg0KYGBge3J9DQoNCmRvcGFzb3dhbmEgPC0gZHBpbGwodGltZXMsIGFjY2VsKQ0KZml0MyA8LSBsb2Nwb2x5KHRpbWVzLCBhY2NlbCwNCiAgICAgICAgZGVncmVlPTAsIGJhbmR3aWR0aD1kb3Bhc293YW5hKSAlPiUgYXMudGliYmxlDQoNCnBhc3RlKCJEb3Bhc293YW5hIHN6ZXJva2/Fm8SHIHBhc21hIHd5bm9zaToiLCByb3VuZChkb3Bhc293YW5hLCA0KSkNCg0KZ2dwbG90KG1jeWNsZSkgKw0KICBnZW9tX3BvaW50KGFlcyh4PXRpbWVzLHk9YWNjZWwpKSsNCiAgZ2VvbV9saW5lKGRhdGE9Zml0MywgYWVzKHg9eCx5PXkpLCBjb2w9J2JsdWUnLHNpemU9MSkNCmBgYA0KDQojIyMgUG9yw7N3bmFuaWUgcsOzxbxueWNoIGRlZ3JlZSAoc3RvcGllxYQgd2llbG9taWFudSkNCg0KYGBge3J9DQpmaXQxIDwtIGxvY3BvbHkodGltZXMsIGFjY2VsLA0KICAgICAgICBkZWdyZWU9MCwgYmFuZHdpZHRoPWRvcGFzb3dhbmEpICU+JSBhcy50aWJibGUNCmZpdDIgPC0gbG9jcG9seSh0aW1lcywgYWNjZWwsLA0KICAgICAgICBkZWdyZWU9MSwgYmFuZHdpZHRoPWRvcGFzb3dhbmEpICU+JSBhcy50aWJibGUNCmZpdDMgPC0gbG9jcG9seSh0aW1lcywgYWNjZWwsDQogICAgICAgIGRlZ3JlZT0yLCBiYW5kd2lkdGg9ZG9wYXNvd2FuYSkgJT4lIGFzLnRpYmJsZQ0KDQpnZ3Bsb3QobWN5Y2xlKSArDQogIGdlb21fcG9pbnQoYWVzKHg9dGltZXMseT1hY2NlbCkpICsNCiAgZ2VvbV9saW5lKGRhdGE9Zml0MSwgYWVzKHg9eCx5PXkpLCBjb2w9J2dyZWVuJyxzaXplPTAuOCkrDQogIGdlb21fbGluZShkYXRhPWZpdDIsIGFlcyh4PXgseT15KSwgY29sPSdkYXJrcmVkJyxzaXplPTAuOCkrDQogIGdlb21fbGluZShkYXRhPWZpdDMsIGFlcyh4PXgseT15KSwgY29sPSdibHVlJyxzaXplPTAuOCkrDQogIGdndGl0bGUoIktlcm5lbCBzbW9vdGhpbmcgLSBwb3LDs3dhbmFuaWUgdHJ6ZWNoIHN0b3BuaSB3aWVsb21pYW51IikNCmBgYA0KDQojIyBMb2VzcyAtIGxva2FsbmllIGt3YWRyYXRvd3kNCg0KYGBge3J9DQpzbXIgPC0gbG9lc3MgKGFjY2VsfnRpbWVzLHNwYW49MC43NSwgZGVncmVlPTIsDQogICAgICAgICAgICAgICBmYW1pbHk9ImdhdXNzaWFuIikNCg0KZ2dwbG90KG1jeWNsZSkgKw0KICBnZW9tX3BvaW50KGFlcyh4PXRpbWVzLHk9YWNjZWwpKSsNCiAgZ2VvbV9saW5lKGFlcyh4PXRpbWVzLHk9Zml0dGVkKHNtcikpLCBjb2w9J3NreWJsdWUnLCBzaXplPTAuNykNCmBgYA0KYGBge3J9DQpnZ3Bsb3QobWN5Y2xlKSArDQogIGdlb21fcG9pbnQoYWVzKHg9dGltZXMseT1hY2NlbCkpKw0KICBnZW9tX3Ntb290aChhZXMoeD10aW1lcyx5PWFjY2VsKSxtZXRob2Q9J2xvZXNzJyxzcGFuPTAuMjIpDQpgYGANCg0KIyMgU3Bsb3R5IGludGVycG9sdWrEhWNlIC0gc21vb3RoLnNwbGluZQ0KDQojIyMgWm1pZW5pYW5pZSBwYXJhbWV0cnUg4oCYc3BhcuKAmQ0KDQpgYGB7cn0NCmZpdDEgPC1zbW9vdGguc3BsaW5lKHRpbWVzLCBhY2NlbCwgc3Bhcj0wLjIpIA0Kc21yMSA8LSBkYXRhLmZyYW1lKHg9Zml0MSR4LHk9Zml0MSR5KQ0KDQpmaXQyIDwtc21vb3RoLnNwbGluZSh0aW1lcywgYWNjZWwsLCBzcGFyPTAuOCkgDQpzbXIyIDwtIGRhdGEuZnJhbWUoeD1maXQyJHgseT1maXQyJHkpDQoNCmdncGxvdChtY3ljbGUpICsNCiAgZ2VvbV9wb2ludChhZXMoeD10aW1lcyx5PWFjY2VsKSkgKw0KICBnZW9tX2xpbmUoZGF0YT1zbXIxLCBhZXMoeD14LHk9eSksIGNvbD0nZ3JlZW4nLHNpemU9MC44KSsNCiAgZ2VvbV9saW5lKGRhdGE9c21yMiwgYWVzKHg9eCx5PXkpLCBjb2w9J2RhcmtyZWQnLHNpemU9MC44KSsNCiAgZ2d0aXRsZSgiU3Bsb3R5IGludGVycG9sdWrEhWNlIC0gcG9yw7N3bmFuaWUgcsOzxbxueWNoIHdhcnRvxZtjaSBwYXJhbWV0cnUgJ3NwYXInIikNCmBgYA0KDQpTcGFyPTAuMiBwb3dvZHVqZSB6Ynl0IG1vY25lIGRvcGFzb3dhbmllIGRvIGRhbnljaC4gDQpTcGFyPTAuOCBwcmV6ZW50dWplIHpieXQgZHXFvGUgdW9nw7NsbmllbmllLiANClduaW9zZWs6IA0KWm1uaWVqc3phbmllIOKAmHNwYXLigJkgcG93b2R1amUgd2nEmWtzemUgZG9wYXNvd2FuaWUsIGEgendpxJlrc3phbmllIC0gd2nEmWtzemUgdW9nw7NsbmllbmllLg0KDQpBYnkgYXV0b21hdHljem5pZSB3eWJyYcSHIHN6ZXJva2/Fm2MgcGFzbWEgbW/FvG5hIHphc3Rvc293YcSHIGNyb3NzIHZhbGlkYXRpb24NCg0KIyMjIFphc3Rvc293YW5pZSBjcm9zcyB2YWxpZGF0aW9uIChDVj1UUlVFKQ0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0Kc21yMiA8LSBzbW9vdGguc3BsaW5lKA0KICB0aW1lcywNCiAgYWNjZWwsDQogIGN2PVRSVUUpDQpzbXIyIDwtIGRhdGEuZnJhbWUoeD1zbXIyJHgseT1zbXIyJHkpDQpnZ3Bsb3QobWN5Y2xlKSArDQogIGdlb21fcG9pbnQoYWVzKHg9dGltZXMseT1hY2NlbCkpICsNCiAgZ2d0aXRsZSgiU3Bsb3R5IGludGVycG9sdWrEhWNlLCBsYW1iZGEgd3licmFuYSBwcnpleiBDViIpICsNCiAgZ2VvbV9saW5lKGRhdGE9c21yMiwgYWVzKHg9eCwgeT15KSwgY29sPSdncmVlbicsIHNpemU9MC43KQ0KYGBgDQoNCiMjIFBvcsOzd25hbmllIGZ1bmtjamkgbG9jcG9seSBpIHNtb290aC5zcGxpbmUNCg0KYGBge3Igd2FybmluZz1GQUxTRX0NCmZpdCA8LSBsb2Nwb2x5KHRpbWVzLCBhY2NlbCwNCiAgICAgICAgZGVncmVlPTAsIGJhbmR3aWR0aD1kb3Bhc293YW5hKSAlPiUgYXMudGliYmxlDQoNCnNtciA8LSBzbW9vdGguc3BsaW5lKA0KICB0aW1lcywNCiAgYWNjZWwsDQogIGN2PVRSVUUpDQpzbXIgPC0gZGF0YS5mcmFtZSh4PXNtciR4LHk9c21yJHkpDQoNCmdncGxvdChtY3ljbGUpICsNCiAgZ2VvbV9wb2ludChhZXMoeD10aW1lcyx5PWFjY2VsKSkgKw0KICBnZW9tX2xpbmUoZGF0YT1maXQsIGFlcyh4PXgseT15KSwgY29sPSdncmVlbicsIHNpemU9MC43KSsNCiAgZ2VvbV9saW5lKGRhdGE9c21yLCBhZXMoeD14LHk9eSksIGNvbD0nZGFya3JlZCcsIHNpemU9MC43KSsNCiAgZ2d0aXRsZSgiUG9yw7N3bmFuaWUgZnVua2NqaSBsb2Nwb2x5IGkgc21vb3RoLnNwbGluZSIpDQpgYGANCg0KIyMgTmF0dXJhbG5lIHNwbG90eQ0KDQpgYGB7cn0NCmxpYnJhcnkoc3BsaW5lcykNCg0KZml0IDwtbG0oYWNjZWx+bnModGltZXMsZGY9NiksbWN5Y2xlKQ0KZml0MiA8LWxtKGFjY2Vsfm5zKHRpbWVzLGRmPTEyKSxtY3ljbGUpDQoNCmdncGxvdChtY3ljbGUpKw0KICBnZW9tX3BvaW50KGFlcyh4PXRpbWVzLHk9YWNjZWwpKSsNCiAgZ2d0aXRsZSgiTmF0dXJhbG5lIHNwbG90eSwgNiBkZihuaWViaWVza2kpIGkgMTIgZGYoY3plcndvbnkpIikrDQogIGdlb21fbGluZShhZXMoeD10aW1lcyx5PWZpdHRlZChmaXQpKSxjb2w9J2dyZWVuJywgc2l6ZT0wLjcpKw0KICBnZW9tX2xpbmUoYWVzKHg9dGltZXMseT1maXR0ZWQoZml0MikpLGNvbD0nZGFya3JlZCcsIHNpemU9MC43KQ0KYGBgDQoNCiMjIFNwbG90eSBvcmF6IGdlb21fc21vb3RoKCkNCg0KYGBge3J9DQpnZ3Bsb3QobWN5Y2xlKSArDQogICAgZ2VvbV9wb2ludChhZXMoeD10aW1lcyx5PWFjY2VsKSkgKw0KICAgIGdlb21fc21vb3RoKGFlcyh4PXRpbWVzLHk9Zml0dGVkKGZpdCkpLCBtZXRob2Q9J2dhbScsDQogICAgICBmb3JtdWxhID0geSB+IHMoeCxrPTEyKSwgc2U9VFJVRSkNCmBgYA0KDQpgYGB7cn0NCmZpdCA8LSBsbShhY2NlbCB+IG5zKHRpbWVzLCBkZiA9IDYpLCBtY3ljbGUpDQpwcmVkIDwtIGRhdGEuZnJhbWUodGltZXMgPSBtY3ljbGUkdGltZXMsDQogICAgICAgICAgICAgICAgICAgcHJlZGljdChmaXQsIGludGVydmFsID0gImNvbmZpZGVuY2UiKSkNCmdncGxvdChtY3ljbGUsIGFlcyh4ID0gdGltZXMsIHkgPSBhY2NlbCkpICsNCiAgICBnZW9tX3BvaW50KCkgKw0KICAgIGdlb21fbGluZShkYXRhID0gcHJlZCwgYWVzKHkgPSBmaXQpLCBjb2xvciA9ICJncmVlbiIsIHNpemU9MC43KSArDQogICAgZ2VvbV9yaWJib24oZGF0YSA9IHByZWQsIGFlcyh5bWluID0gbHdyLCB5bWF4ID0gdXByKSwgYWxwaGEgPSAwLjIpDQpgYGANCg0K