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. Dla zawodów, które zarabiają mniej niż $10K, istnieje silna (pozytywna) liniowa zależność pomiędzy dochodem a prestiżem. Jednak w przypadku zawodów, które zarabiają 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='blue')+
  ggtitle("Kernel smoothing - szerokość pasma równa 1")

Zwiększam szerokość 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='red')+
  ggtitle("Kernel smoothing - szerokość pasma równa 5")

Porównanie:

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

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

Aby dopasować szerokośc pasma - cross validation

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='green',size=1)

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='blue',size=0.8)+
  geom_line(data=fit2, aes(x=x,y=y), col='red',size=0.8)+
  geom_line(data=fit3, aes(x=x,y=y), col='green',size=0.8)+
  ggtitle("Kernel smoothing - porówananie trzech stopni wielomianu")

Można zauważyć, że 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=1)

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='blue',size=0.8)+
  geom_line(data=smr2, aes(x=x,y=y), col='red',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

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='blue')

1.4 Porównanie funkcji locpoly i smooth.spline

Niebieska linia - locpoly Czerwona 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='blue')+
  geom_line(data=smr, aes(x=x,y=y), col='red')+
  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='blue')+
  geom_line(aes(x=prestige,y=fitted(fit2)),col='red')

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 = "blue") +
    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='blue')+
  ggtitle("Kernel smoothing - szerokość pasma równa 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='red')+
  ggtitle("Kernel smoothing - szerokość pasma równa 5")

Porównanie:

Niebieska 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='blue')+
  geom_line(data=fit2, aes(x=x,y=y), col='red')+
  ggtitle("Kernel smoothing - porówananie dwóch 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='green',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='blue',size=0.8)+
  geom_line(data=fit2, aes(x=x,y=y), col='red',size=0.8)+
  geom_line(data=fit3, aes(x=x,y=y), col='green',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=1)

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='blue',size=0.8)+
  geom_line(data=smr2, aes(x=x,y=y), col='red',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='blue')

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='blue')+
  geom_line(data=smr, aes(x=x,y=y), col='red')+
  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='blue')+
  geom_line(aes(x=times,y=fitted(fit2)),col='red')

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 = "blue") +
    geom_ribbon(data = pred, aes(ymin = lwr, ymax = upr), alpha = 0.2)

LS0tDQp0aXRsZTogIlJhcG9ydCAzIg0Kc3VidGl0bGU6ICJSZWdyZXNqYSBuaWVwYXJhbWV0cnljem5hIg0KYXV0aG9yOiAiSm9hbm5hIEtvxZtjacWEc2thIg0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRoZW1lOiBjZXJ1bGVhbg0KICAgIGhpZ2hsaWdodDogdGV4dG1hdGUNCiAgICBmb250c2l6ZTogOHB0DQogICAgdG9jOiB0cnVlDQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogZmFsc2UNCmVkaXRvcl9vcHRpb25zOiANCiAgbWFya2Rvd246IA0KICAgIHdyYXA6IDcyDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCmxpYnJhcnkoY2FyKQ0KbGlicmFyeShLZXJuU21vb3RoKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpgYGANCg0KIyBaYWRhbmllIDEgLSBkYW5lIFByZXN0aWdlDQoNClplc3RhdyBkYW55Y2gg4oCcUHJlc3RpZ2XigJ0gKHogcGFraWV0dSDigJxjYXLigJ0pIHphd2llcmEgZGFuZSBudC4gcHJlc3Rpxbx1IG49MTAyIEthbmFkeWpza2ljaCB6YXdvZMOzdyB6IDE5NzEgcm9rdSwgYSB0YWvFvGUgxZtyZWRuaSBkb2Now7NkIHcgZGFueW0gemF3b2R6aWUuIERvIHpiYWRhbmlhIHphbGXFvG5vxZtjaSBtacSZZHp5IHByZXN0acW8ZW0gYSBkb2Nob2RlbSB3eWtvcnp5c3RhaiBtZXRvZHkgcmVncmVzamkgbmllcGFyYW1ldHJ5Y3puZWouDQoNCk5hanBpZXJ3IHphxYJhZHVqIGRhbmUgaSB6d2l6dWFsaXp1aiByZWxhY2rEmSBwb21pxJlkenkgZG9jaG9kZW0gKFgpIGEgcHJlc3RpxbxlbSAoWSkuDQoNCmBgYHtyfQ0KZGF0YSgiUHJlc3RpZ2UiKQ0KYXR0YWNoKFByZXN0aWdlKQ0KDQpnZ3Bsb3QoUHJlc3RpZ2UpKw0KICBnZW9tX3BvaW50KGFlcyh4PXByZXN0aWdlLCB5PWluY29tZSkpDQpgYGANCg0KWndpxIV6ZWsgd3lnbMSFZGEgbmEgbmllbGluaW93eS4gRGxhIHphd29kw7N3LCBrdMOzcmUgemFyYWJpYWrEhSBtbmllaiBuacW8ICQxMEssIGlzdG5pZWplIHNpbG5hIChwb3p5dHl3bmEpIGxpbmlvd2EgemFsZcW8bm/Fm8SHIHBvbWnEmWR6eSBkb2Nob2RlbSBhIHByZXN0acW8ZW0uIEplZG5hayB3IHByenlwYWRrdSB6YXdvZMOzdywga3TDs3JlIHphcmFiaWFqxIUgb2QgMTAgZG8gMjUgdHlzacSZY3kgZG9sYXLDs3csIHp3acSFemVrIHRlbiBtYSB6bmFjem5pZSBpbm5lIG5hY2h5bGVuaWUuDQoNCk1ldG9keSByZWdyZXNqaSBuaWVwYXJhbWV0cnljem5lai4NCg0KIyMgTG9jYWwgcG9seW5vbWlhbCByZWdyZXNzaW9uIChsb2Nwb2x5KSAtIG1ldG9kYSBsb2thbG5lZ28gd3lnxYJhZHphbmlhDQoNCkfFgsOzd25lIHBhcmFtZXRyeSwgdGFraWUgamFrIGRlZ3JlZSBpIGJhbmR3aWR0aCwgbWFqxIUga2x1Y3pvd3kgd3DFgnl3IG5hIHNwb3PDs2IsIHcgamFraSB3eWfFgmFkemFuYSBqZXN0IHphbGXFvG5vxZvEhy4NCg0KUGFyYW1ldHIgZGVncmVlIG9rcmXFm2xhIHN0b3BpZcWEIHdpZWxvbWlhbnUgdcW8eXRlZ28gZG8gbG9rYWxuZWogYXByb2tzeW1hY2ppLiBEZWdyZWU9MCBvem5hY3phIHXFvHljaWUgbG9rYWxueWNoIMWbcmVkbmljaCAoY28gb2Rwb3dpYWRhIHJlZ3Jlc2ppIG5hamJsacW8c3p5Y2ggc8SFc2lhZMOzdykuIA0KDQpEZWdyZWU9MSBvem5hY3phIHd5a29yenlzdGFuaWUgbG9rYWxuZWogcmVncmVzamkgbGluaW93ZWogDQoNCkRlZ3JlZT0yIG96bmFjemEgd3lrb3J6eXN0YW5pZSBsb2thbG5laiByZWdyZXNqaSBrd2FkcmF0b3dlai4NCg0KUGFyYW1ldHIgYmFuZHdpZHRoIGRlY3lkdWplIG8gcm96cGnEmXRvxZtjaSBqxIVkcmEsIGN6eWxpIGRlZmluaXVqZSByb3ptaWFyIG9ic3phcnUgdyBqYWtpbSBvYnNlcndhY2plIHdwxYJ5d2FqxIUgbmEgZnVua2NqxJkgZ8SZc3RvxZtjaS5JbSB3acSZa3N6YSB3YXJ0b8WbxIcgYmFuZHdpZHRoLCB0eW0gYmFyZHppZWogd3lnxYJhZHpvbmEgamVzdCBmdW5rY2phLiBaYXLDs3dubyB6Ynl0IG5pc2thIHdhcnRvxZvEhyBiYW5kd2lkdGggbW/FvGUgYnnEhyBuaWVvZHBvd2llZG5pYSAob3ZlcmZpdHRpbmctIG1vZGVsIHpieXQgc3pjemVnw7PFgm93eSksIGphayBpIHLDs3duaWXFvCB6Ynl0IHd5c29rYSB3YXJ0b8WbxIcgdGVnbyBwYXJhbWV0cnUgKHVuZGVyZml0dGluZy0gemJ5dCBkdcW8ZSB3eWfFgmFkemVuaWUsIHpieXQgZHXFvGUgdW9nw7NsbmllbmllKS4NCg0KIyMjIFBvcsOzd25hbmllIHLDs8W8bnljaCBiYW5kd2lkdGgNCg0KYGBge3Igd2FybmluZz1GQUxTRX0NCmZpdDEgPC0gbG9jcG9seShwcmVzdGlnZSwgaW5jb21lLA0KICAgICAgICBkZWdyZWU9MCwgYmFuZHdpZHRoPTEpICU+JSBhcy50aWJibGUNCg0KZ2dwbG90KFByZXN0aWdlKSArDQogIGdlb21fcG9pbnQoYWVzKHg9cHJlc3RpZ2UseT1pbmNvbWUpKSArDQogIGdlb21fbGluZShkYXRhPWZpdDEsIGFlcyh4PXgseT15KSwgY29sPSdibHVlJykrDQogIGdndGl0bGUoIktlcm5lbCBzbW9vdGhpbmcgLSBzemVyb2tvxZvEhyBwYXNtYSByw7N3bmEgMSIpDQpgYGANCg0KWndpxJlrc3phbSBzemVyb2tvxZvEhyBwYXNtYSBkbyA1DQogIA0KYGBge3J9DQpmaXQyIDwtIGxvY3BvbHkocHJlc3RpZ2UsIGluY29tZSwNCiAgICAgICAgZGVncmVlPTAsIGJhbmR3aWR0aD01KSAlPiUgYXMudGliYmxlDQoNCmdncGxvdChQcmVzdGlnZSkgKw0KICBnZW9tX3BvaW50KGFlcyh4PXByZXN0aWdlLHk9aW5jb21lKSkgKw0KICBnZW9tX2xpbmUoZGF0YT1maXQyLCBhZXMoeD14LHk9eSksIGNvbD0ncmVkJykrDQogIGdndGl0bGUoIktlcm5lbCBzbW9vdGhpbmcgLSBzemVyb2tvxZvEhyBwYXNtYSByw7N3bmEgNSIpDQpgYGANCg0KUG9yw7N3bmFuaWU6DQoNCk5pZWJpZXNrYSBsaW5pYTogc3plcm9rb8WbxIcgcGFzbWEgMQ0KQ3plcndvbmEgbGluaWE6IHN6ZXJva2/Fm8SHIHBhc21hIDUNCg0KYGBge3J9DQpnZ3Bsb3QoUHJlc3RpZ2UpICsNCiAgZ2VvbV9wb2ludChhZXMoeD1wcmVzdGlnZSx5PWluY29tZSkpICsNCiAgZ2VvbV9saW5lKGRhdGE9Zml0MSwgYWVzKHg9eCx5PXkpLCBjb2w9J2JsdWUnKSsNCiAgZ2VvbV9saW5lKGRhdGE9Zml0MiwgYWVzKHg9eCx5PXkpLCBjb2w9J3JlZCcpKw0KICBnZ3RpdGxlKCJLZXJuZWwgc21vb3RoaW5nIC0gcG9yw7N3YW5hbmllIGR3w7NjaCBzemVyb2tvxZtjaSBwYXNtIikNCmBgYA0KDQoNCkFieSBkb3Bhc293YcSHIHN6ZXJva2/Fm2MgcGFzbWEgLSBjcm9zcyB2YWxpZGF0aW9uDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0V9DQoNCmRvcGFzb3dhbmEgPC0gZHBpbGwocHJlc3RpZ2UsIGluY29tZSkNCmZpdDMgPC0gbG9jcG9seShwcmVzdGlnZSwgaW5jb21lLA0KICAgICAgICBkZWdyZWU9MCwgYmFuZHdpZHRoPWRvcGFzb3dhbmEpICU+JSBhcy50aWJibGUNCg0KcGFzdGUoIkRvcGFzb3dhbmEgc3plcm9rb8WbxIcgcGFzbWEgd3lub3NpOiIsIHJvdW5kKGRvcGFzb3dhbmEsIDQpKQ0KDQpnZ3Bsb3QoUHJlc3RpZ2UpICsNCiAgZ2VvbV9wb2ludChhZXMoeD1wcmVzdGlnZSx5PWluY29tZSkpKw0KICBnZW9tX2xpbmUoZGF0YT1maXQzLCBhZXMoeD14LHk9eSksIGNvbD0nZ3JlZW4nLHNpemU9MSkNCmBgYA0KDQojIyMgUG9yw7N3bmFuaWUgcsOzxbxueWNoIGRlZ3JlZSAoc3RvcGllxYQgd2llbG9taWFudSkNCg0KYGBge3J9DQpmaXQxIDwtIGxvY3BvbHkocHJlc3RpZ2UsIGluY29tZSwNCiAgICAgICAgZGVncmVlPTAsIGJhbmR3aWR0aD1kb3Bhc293YW5hKSAlPiUgYXMudGliYmxlDQpmaXQyIDwtIGxvY3BvbHkocHJlc3RpZ2UsIGluY29tZSwNCiAgICAgICAgZGVncmVlPTEsIGJhbmR3aWR0aD1kb3Bhc293YW5hKSAlPiUgYXMudGliYmxlDQpmaXQzIDwtIGxvY3BvbHkocHJlc3RpZ2UsIGluY29tZSwNCiAgICAgICAgZGVncmVlPTIsIGJhbmR3aWR0aD1kb3Bhc293YW5hKSAlPiUgYXMudGliYmxlDQoNCmdncGxvdChQcmVzdGlnZSkgKw0KICBnZW9tX3BvaW50KGFlcyh4PXByZXN0aWdlLHk9aW5jb21lKSkgKw0KICBnZW9tX2xpbmUoZGF0YT1maXQxLCBhZXMoeD14LHk9eSksIGNvbD0nYmx1ZScsc2l6ZT0wLjgpKw0KICBnZW9tX2xpbmUoZGF0YT1maXQyLCBhZXMoeD14LHk9eSksIGNvbD0ncmVkJyxzaXplPTAuOCkrDQogIGdlb21fbGluZShkYXRhPWZpdDMsIGFlcyh4PXgseT15KSwgY29sPSdncmVlbicsc2l6ZT0wLjgpKw0KICBnZ3RpdGxlKCJLZXJuZWwgc21vb3RoaW5nIC0gcG9yw7N3YW5hbmllIHRyemVjaCBzdG9wbmkgd2llbG9taWFudSIpDQpgYGANCg0KTW/FvG5hIHphdXdhxbx5xIcsIMW8ZSB3eWLDs3IgZGVncmVlIHdwxYJ5d2EgbmEgZnVua2NqxJkgZ8SZc3RvxZtjaSBuYSB3eWtyZXNpZS4gVyBwcnp5cGFka3UgZGVncmVlPTIgd3lzdMSZcHVqZSBvdmVyZml0dGluZy4gTGVwc3p5bSB3eWJvcmVtIGJ5xYJvYnkgZGVncmVlIHLDs3duZSAwIGx1YiAxLg0KDQojIyBMb2VzcyAtIGxva2FsbmllIGt3YWRyYXRvd3kNCg0KYGBge3J9DQpzbXIgPC0gbG9lc3MgKGluY29tZX5wcmVzdGlnZSxzcGFuPTAuNzUsIGRlZ3JlZT0yLA0KICAgICAgICAgICAgICAgZmFtaWx5PSJnYXVzc2lhbiIpDQoNCmdncGxvdChQcmVzdGlnZSkgKw0KICBnZW9tX3BvaW50KGFlcyh4PXByZXN0aWdlLHk9aW5jb21lKSkrDQogIGdlb21fbGluZShhZXMoeD1wcmVzdGlnZSx5PWZpdHRlZChzbXIpKSwgY29sPSdza3libHVlJyxzaXplPTEpDQpgYGANCmBgYHtyfQ0KZ2dwbG90KFByZXN0aWdlKSArDQogIGdlb21fcG9pbnQoYWVzKHg9cHJlc3RpZ2UseT1pbmNvbWUpKSsNCiAgZ2VvbV9zbW9vdGgoYWVzKHg9cHJlc3RpZ2UseT1pbmNvbWUpLG1ldGhvZD0nbG9lc3MnLHNwYW49MC4yMikNCmBgYA0KDQojIyBTcGxvdHkgaW50ZXJwb2x1asSFY2UgLSBzbW9vdGguc3BsaW5lDQoNCiMjIyBabWllbmlhbmllIHBhcmFtZXRydSAnc3BhcicNCg0KYGBge3J9DQoNCmZpdDEgPC1zbW9vdGguc3BsaW5lKHByZXN0aWdlLCBpbmNvbWUsIHNwYXI9MC4yKSANCnNtcjEgPC0gZGF0YS5mcmFtZSh4PWZpdDEkeCx5PWZpdDEkeSkNCg0KZml0MiA8LXNtb290aC5zcGxpbmUocHJlc3RpZ2UsIGluY29tZSwgc3Bhcj0wLjgpIA0Kc21yMiA8LSBkYXRhLmZyYW1lKHg9Zml0MiR4LHk9Zml0MiR5KQ0KDQpnZ3Bsb3QoUHJlc3RpZ2UpICsNCiAgZ2VvbV9wb2ludChhZXMoeD1wcmVzdGlnZSx5PWluY29tZSkpICsNCiAgZ2VvbV9saW5lKGRhdGE9c21yMSwgYWVzKHg9eCx5PXkpLCBjb2w9J2JsdWUnLHNpemU9MC44KSsNCiAgZ2VvbV9saW5lKGRhdGE9c21yMiwgYWVzKHg9eCx5PXkpLCBjb2w9J3JlZCcsc2l6ZT0wLjgpKw0KICBnZ3RpdGxlKCJTcGxvdHkgaW50ZXJwb2x1asSFY2UgLSBwb3LDs3duYW5pZSByw7PFvG55Y2ggd2FydG/Fm2NpIHBhcmFtZXRydSAnc3BhciciKQ0KYGBgDQoNClNwYXI9MC4yIHBvd29kdWplIHpieXQgbW9jbmUgZG9wYXNvd2FuaWUgZG8gZGFueWNoLiBTcGFyPTAuOCBwcmV6ZW50dWplIHpieXQgZHXFvGUgdW9nw7NsbmllbmllLg0KV25pb3NlazogWm1uaWVqc3phbmllICdzcGFyJyBwb3dvZHVqZSB3acSZa3N6ZSBkb3Bhc293YW5pZSwgYSB6d2nEmWtzemFuaWUgLSB3acSZa3N6ZSB1b2fDs2xuaWVuaWUuDQoNCkFieSBhdXRvbWF0eWN6bmllIHd5YnJhxIcgc3plcm9rb8WbYyBwYXNtYSBtb8W8bmEgemFzdG9zb3dhxIcgY3Jvc3MgdmFsaWRhdGlvbg0KDQojIyMgWmFzdG9zb3dhbmllIGNyb3NzIHZhbGlkYXRpb24gKENWPVRSVUUpDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0V9DQpzbXIyIDwtIHNtb290aC5zcGxpbmUoDQogIHByZXN0aWdlLA0KICBpbmNvbWUsDQogIGN2PVRSVUUpDQpzbXIyIDwtIGRhdGEuZnJhbWUoeD1zbXIyJHgseT1zbXIyJHkpDQpnZ3Bsb3QoUHJlc3RpZ2UpICsNCiAgZ2VvbV9wb2ludChhZXMoeD1wcmVzdGlnZSx5PWluY29tZSkpICsNCiAgZ2d0aXRsZSgiU3Bsb3R5IGludGVycG9sdWrEhWNlLCBsYW1iZGEgd3licmFuYSBwcnpleiBDViIpICsNCiAgZ2VvbV9saW5lKGRhdGE9c21yMiwgYWVzKHg9eCwgeT15KSwgY29sPSdibHVlJykNCmBgYA0KDQojIyBQb3LDs3duYW5pZSBmdW5rY2ppIGxvY3BvbHkgaSBzbW9vdGguc3BsaW5lDQoNCk5pZWJpZXNrYSBsaW5pYSAtIGxvY3BvbHkNCkN6ZXJ3b25hIGxpbmlhIC0gc21vb3RoLnNwbGluZQ0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0KZml0IDwtIGxvY3BvbHkocHJlc3RpZ2UsIGluY29tZSwNCiAgICAgICAgZGVncmVlPTAsIGJhbmR3aWR0aD1kb3Bhc293YW5hKSAlPiUgYXMudGliYmxlDQoNCnNtciA8LSBzbW9vdGguc3BsaW5lKA0KICBwcmVzdGlnZSwNCiAgaW5jb21lLA0KICBjdj1UUlVFKQ0Kc21yIDwtIGRhdGEuZnJhbWUoeD1zbXIkeCx5PXNtciR5KQ0KDQpnZ3Bsb3QoUHJlc3RpZ2UpICsNCiAgZ2VvbV9wb2ludChhZXMoeD1wcmVzdGlnZSx5PWluY29tZSkpICsNCiAgZ2VvbV9saW5lKGRhdGE9Zml0LCBhZXMoeD14LHk9eSksIGNvbD0nYmx1ZScpKw0KICBnZW9tX2xpbmUoZGF0YT1zbXIsIGFlcyh4PXgseT15KSwgY29sPSdyZWQnKSsNCiAgZ2d0aXRsZSgiUG9yw7N3bmFuaWUgZnVua2NqaSBsb2Nwb2x5IGkgc21vb3RoLnNwbGluZSIpDQpgYGANCg0KIyMgTmF0dXJhbG5lIHNwbG90eQ0KDQpgYGB7cn0NCmxpYnJhcnkoc3BsaW5lcykNCg0KZml0IDwtbG0oaW5jb21lfm5zKHByZXN0aWdlLGRmPTYpLFByZXN0aWdlKQ0KZml0MiA8LWxtKGluY29tZX5ucyhwcmVzdGlnZSxkZj0xMiksUHJlc3RpZ2UpDQoNCmdncGxvdChQcmVzdGlnZSkrDQogIGdlb21fcG9pbnQoYWVzKHg9cHJlc3RpZ2UseT1pbmNvbWUpKSsNCiAgZ2d0aXRsZSgiTmF0dXJhbG5lIHNwbG90eSwgNiBkZihuaWViaWVza2kpIGkgMTIgZGYoY3plcndvbnkpIikrDQogIGdlb21fbGluZShhZXMoeD1wcmVzdGlnZSx5PWZpdHRlZChmaXQpKSxjb2w9J2JsdWUnKSsNCiAgZ2VvbV9saW5lKGFlcyh4PXByZXN0aWdlLHk9Zml0dGVkKGZpdDIpKSxjb2w9J3JlZCcpDQpgYGANCg0KIyMgU3Bsb3R5IG9yYXogZ2VvbV9zbW9vdGgoKQ0KDQpgYGB7cn0NCmdncGxvdChQcmVzdGlnZSkgKw0KICAgIGdlb21fcG9pbnQoYWVzKHg9cHJlc3RpZ2UseT1pbmNvbWUpKSArDQogICAgZ2VvbV9zbW9vdGgoYWVzKHg9cHJlc3RpZ2UseT1maXR0ZWQoZml0KSksIG1ldGhvZD0nZ2FtJywNCiAgICAgIGZvcm11bGEgPSB5IH4gcyh4LGs9MTIpLCBzZT1UUlVFKQ0KYGBgDQoNCmBgYHtyfQ0KZml0IDwtIGxtKGluY29tZSB+IG5zKHByZXN0aWdlLCBkZiA9IDYpLCBQcmVzdGlnZSkNCnByZWQgPC0gZGF0YS5mcmFtZShwcmVzdGlnZSA9IFByZXN0aWdlJHByZXN0aWdlLA0KICAgICAgICAgICAgICAgICAgIHByZWRpY3QoZml0LCBpbnRlcnZhbCA9ICJjb25maWRlbmNlIikpDQpnZ3Bsb3QoUHJlc3RpZ2UsIGFlcyh4ID0gcHJlc3RpZ2UsIHkgPSBpbmNvbWUpKSArDQogICAgZ2VvbV9wb2ludCgpICsNCiAgICBnZW9tX2xpbmUoZGF0YSA9IHByZWQsIGFlcyh5ID0gZml0KSwgY29sb3IgPSAiYmx1ZSIpICsNCiAgICBnZW9tX3JpYmJvbihkYXRhID0gcHJlZCwgYWVzKHltaW4gPSBsd3IsIHltYXggPSB1cHIpLCBhbHBoYSA9IDAuMikNCmBgYA0KDQojIFphZGFuaWUgMiAtIGRhbmUgbWN5Y2xlDQoNClpiacOzciBkYW55Y2gg4oCcbWN5Y2xl4oCdICh6IHBha2lldHUgTUFTUykgemF3aWVyYSBuPTEzMyBwYXJ5IHB1bmt0w7N3IGN6YXNvd3ljaCAodyBtcykgaSBvYnNlcndvd2FueWNoIHByenlzcGllc3plxYQgZ8WCb3d5ICh3IGcpLCBrdMOzcmUgem9zdGHFgnkgemFyZWplc3Ryb3dhbmUgdyBzeW11bG93YW55bSB3eXBhZGt1IG1vdG9jeWtsb3d5bS4NCg0KRG8gemJhZGFuaWEgemFsZcW8bm/Fm2NpIG1pxJlkenkgY3phc2VtIGEgcHJ6eXNwaWVzemVuaWVtIHd5a29yenlzdGFqIG1ldG9keSByZWdyZXNqaSBuaWVwYXJhbWV0cnljem5lai4NCg0KTmFqcGllcncgd2N6eXRhaiBkYW5lIGkgendpenVhbGl6dWogemFsZcW8bm/Fm8SHIG1pxJlkenkgY3phc2VtIChYKSBhIHByenlzcGllc3plbmllbSAoWSkuDQoNCmBgYHtyfQ0KbGlicmFyeShNQVNTKQ0KZGF0YSgibWN5Y2xlIikNCmF0dGFjaChtY3ljbGUpDQoNCmdncGxvdChtY3ljbGUpKw0KICBnZW9tX3BvaW50KGFlcyh4PXRpbWVzLHk9YWNjZWwpKQ0KYGBgDQpaYWxlxbxub8WbxIcgd3lnbMSFZGEgbmEgbmllbGluaW93xIUuDQoNClByenlzcGllc3plbmllIGplc3Qgc3RhYmlsbmUgb2QgMC0xNSBtcywgc3BhZGEgb2Qgb2suIDE1LTIwIG1zLCByb8WbbmllIG9kIDIwLTMwIG1zLCBzcGFkYSBvZCAzMC00MCBtcywgYSBuYXN0xJlwbmllIHphY3p5bmEgc2nEmSBzdGFiaWxpem93YcSHLg0KDQojIyBMb2NhbCBwb2x5bm9taWFsIHJlZ3Jlc3Npb24gKGxvY3BvbHkpIC0gbWV0b2RhIGxva2FsbmVnbyB3eWfFgmFkemFuaWENCg0KIyMjIFBvcsOzd25hbmllIHLDs8W8bnljaCBiYW5kd2lkdGgNCg0KYGBge3J9DQpmaXQxIDwtIGxvY3BvbHkodGltZXMsIGFjY2VsLA0KICAgICAgICBkZWdyZWU9MCwgYmFuZHdpZHRoPTEpICU+JSBhcy50aWJibGUNCg0KZ2dwbG90KG1jeWNsZSkgKw0KICBnZW9tX3BvaW50KGFlcyh4PXRpbWVzLHk9YWNjZWwpKSArDQogIGdlb21fbGluZShkYXRhPWZpdDEsIGFlcyh4PXgseT15KSwgY29sPSdibHVlJykrDQogIGdndGl0bGUoIktlcm5lbCBzbW9vdGhpbmcgLSBzemVyb2tvxZvEhyBwYXNtYSByw7N3bmEgMSIpDQpgYGANCg0KWndpxJlrc3phbSBzemVyb2tvxZvEhyBwYXNtYSBkbyA1DQogIA0KYGBge3J9DQpmaXQyIDwtIGxvY3BvbHkodGltZXMsIGFjY2VsLA0KICAgICAgICBkZWdyZWU9MCwgYmFuZHdpZHRoPTUpICU+JSBhcy50aWJibGUNCg0KZ2dwbG90KG1jeWNsZSkgKw0KICBnZW9tX3BvaW50KGFlcyh4PXRpbWVzLHk9YWNjZWwpKSArDQogIGdlb21fbGluZShkYXRhPWZpdDIsIGFlcyh4PXgseT15KSwgY29sPSdyZWQnKSsNCiAgZ2d0aXRsZSgiS2VybmVsIHNtb290aGluZyAtIHN6ZXJva2/Fm8SHIHBhc21hIHLDs3duYSA1IikNCmBgYA0KDQpQb3LDs3duYW5pZToNCg0KTmllYmllc2thIGxpbmlhOiBzemVyb2tvxZvEhyBwYXNtYSAxDQpDemVyd29uYSBsaW5pYTogc3plcm9rb8WbxIcgcGFzbWEgNQ0KDQpgYGB7cn0NCmdncGxvdChtY3ljbGUpICsNCiAgZ2VvbV9wb2ludChhZXMoeD10aW1lcyx5PWFjY2VsKSkgKw0KICBnZW9tX2xpbmUoZGF0YT1maXQxLCBhZXMoeD14LHk9eSksIGNvbD0nYmx1ZScpKw0KICBnZW9tX2xpbmUoZGF0YT1maXQyLCBhZXMoeD14LHk9eSksIGNvbD0ncmVkJykrDQogIGdndGl0bGUoIktlcm5lbCBzbW9vdGhpbmcgLSBwb3LDs3dhbmFuaWUgZHfDs2NoIHN6ZXJva2/Fm2NpIHBhc20iKQ0KYGBgDQoNCg0KQWJ5IGRvcGFzb3dhxIcgc3plcm9rb8WbYyBwYXNtYSAtIGNyb3NzIHZhbGlkYXRpb24NCg0KYGBge3J9DQoNCmRvcGFzb3dhbmEgPC0gZHBpbGwodGltZXMsIGFjY2VsKQ0KZml0MyA8LSBsb2Nwb2x5KHRpbWVzLCBhY2NlbCwNCiAgICAgICAgZGVncmVlPTAsIGJhbmR3aWR0aD1kb3Bhc293YW5hKSAlPiUgYXMudGliYmxlDQoNCnBhc3RlKCJEb3Bhc293YW5hIHN6ZXJva2/Fm8SHIHBhc21hIHd5bm9zaToiLCByb3VuZChkb3Bhc293YW5hLCA0KSkNCg0KZ2dwbG90KG1jeWNsZSkgKw0KICBnZW9tX3BvaW50KGFlcyh4PXRpbWVzLHk9YWNjZWwpKSsNCiAgZ2VvbV9saW5lKGRhdGE9Zml0MywgYWVzKHg9eCx5PXkpLCBjb2w9J2dyZWVuJyxzaXplPTEpDQpgYGANCg0KIyMjIFBvcsOzd25hbmllIHLDs8W8bnljaCBkZWdyZWUgKHN0b3BpZcWEIHdpZWxvbWlhbnUpDQoNCmBgYHtyfQ0KZml0MSA8LSBsb2Nwb2x5KHRpbWVzLCBhY2NlbCwNCiAgICAgICAgZGVncmVlPTAsIGJhbmR3aWR0aD1kb3Bhc293YW5hKSAlPiUgYXMudGliYmxlDQpmaXQyIDwtIGxvY3BvbHkodGltZXMsIGFjY2VsLCwNCiAgICAgICAgZGVncmVlPTEsIGJhbmR3aWR0aD1kb3Bhc293YW5hKSAlPiUgYXMudGliYmxlDQpmaXQzIDwtIGxvY3BvbHkodGltZXMsIGFjY2VsLA0KICAgICAgICBkZWdyZWU9MiwgYmFuZHdpZHRoPWRvcGFzb3dhbmEpICU+JSBhcy50aWJibGUNCg0KZ2dwbG90KG1jeWNsZSkgKw0KICBnZW9tX3BvaW50KGFlcyh4PXRpbWVzLHk9YWNjZWwpKSArDQogIGdlb21fbGluZShkYXRhPWZpdDEsIGFlcyh4PXgseT15KSwgY29sPSdibHVlJyxzaXplPTAuOCkrDQogIGdlb21fbGluZShkYXRhPWZpdDIsIGFlcyh4PXgseT15KSwgY29sPSdyZWQnLHNpemU9MC44KSsNCiAgZ2VvbV9saW5lKGRhdGE9Zml0MywgYWVzKHg9eCx5PXkpLCBjb2w9J2dyZWVuJyxzaXplPTAuOCkrDQogIGdndGl0bGUoIktlcm5lbCBzbW9vdGhpbmcgLSBwb3LDs3dhbmFuaWUgdHJ6ZWNoIHN0b3BuaSB3aWVsb21pYW51IikNCmBgYA0KDQojIyBMb2VzcyAtIGxva2FsbmllIGt3YWRyYXRvd3kNCg0KYGBge3J9DQpzbXIgPC0gbG9lc3MgKGFjY2VsfnRpbWVzLHNwYW49MC43NSwgZGVncmVlPTIsDQogICAgICAgICAgICAgICBmYW1pbHk9ImdhdXNzaWFuIikNCg0KZ2dwbG90KG1jeWNsZSkgKw0KICBnZW9tX3BvaW50KGFlcyh4PXRpbWVzLHk9YWNjZWwpKSsNCiAgZ2VvbV9saW5lKGFlcyh4PXRpbWVzLHk9Zml0dGVkKHNtcikpLCBjb2w9J3NreWJsdWUnLHNpemU9MSkNCmBgYA0KYGBge3J9DQpnZ3Bsb3QobWN5Y2xlKSArDQogIGdlb21fcG9pbnQoYWVzKHg9dGltZXMseT1hY2NlbCkpKw0KICBnZW9tX3Ntb290aChhZXMoeD10aW1lcyx5PWFjY2VsKSxtZXRob2Q9J2xvZXNzJyxzcGFuPTAuMjIpDQpgYGANCg0KIyMgU3Bsb3R5IGludGVycG9sdWrEhWNlIC0gc21vb3RoLnNwbGluZQ0KDQojIyMgWm1pZW5pYW5pZSBwYXJhbWV0cnUg4oCYc3BhcuKAmQ0KDQpgYGB7cn0NCmZpdDEgPC1zbW9vdGguc3BsaW5lKHRpbWVzLCBhY2NlbCwgc3Bhcj0wLjIpIA0Kc21yMSA8LSBkYXRhLmZyYW1lKHg9Zml0MSR4LHk9Zml0MSR5KQ0KDQpmaXQyIDwtc21vb3RoLnNwbGluZSh0aW1lcywgYWNjZWwsLCBzcGFyPTAuOCkgDQpzbXIyIDwtIGRhdGEuZnJhbWUoeD1maXQyJHgseT1maXQyJHkpDQoNCmdncGxvdChtY3ljbGUpICsNCiAgZ2VvbV9wb2ludChhZXMoeD10aW1lcyx5PWFjY2VsKSkgKw0KICBnZW9tX2xpbmUoZGF0YT1zbXIxLCBhZXMoeD14LHk9eSksIGNvbD0nYmx1ZScsc2l6ZT0wLjgpKw0KICBnZW9tX2xpbmUoZGF0YT1zbXIyLCBhZXMoeD14LHk9eSksIGNvbD0ncmVkJyxzaXplPTAuOCkrDQogIGdndGl0bGUoIlNwbG90eSBpbnRlcnBvbHVqxIVjZSAtIHBvcsOzd25hbmllIHLDs8W8bnljaCB3YXJ0b8WbY2kgcGFyYW1ldHJ1ICdzcGFyJyIpDQpgYGANCg0KU3Bhcj0wLjIgcG93b2R1amUgemJ5dCBtb2NuZSBkb3Bhc293YW5pZSBkbyBkYW55Y2guIFNwYXI9MC44IHByZXplbnR1amUgemJ5dCBkdcW8ZSB1b2fDs2xuaWVuaWUuIFduaW9zZWs6IFptbmllanN6YW5pZSDigJhzcGFy4oCZIHBvd29kdWplIHdpxJlrc3plIGRvcGFzb3dhbmllLCBhIHp3acSZa3N6YW5pZSAtIHdpxJlrc3plIHVvZ8OzbG5pZW5pZS4NCg0KQWJ5IGF1dG9tYXR5Y3puaWUgd3licmHEhyBzemVyb2tvxZtjIHBhc21hIG1vxbxuYSB6YXN0b3Nvd2HEhyBjcm9zcyB2YWxpZGF0aW9uDQoNCiMjIyBaYXN0b3Nvd2FuaWUgY3Jvc3MgdmFsaWRhdGlvbiAoQ1Y9VFJVRSkNCg0KYGBge3Igd2FybmluZz1GQUxTRX0NCnNtcjIgPC0gc21vb3RoLnNwbGluZSgNCiAgdGltZXMsDQogIGFjY2VsLA0KICBjdj1UUlVFKQ0Kc21yMiA8LSBkYXRhLmZyYW1lKHg9c21yMiR4LHk9c21yMiR5KQ0KZ2dwbG90KG1jeWNsZSkgKw0KICBnZW9tX3BvaW50KGFlcyh4PXRpbWVzLHk9YWNjZWwpKSArDQogIGdndGl0bGUoIlNwbG90eSBpbnRlcnBvbHVqxIVjZSwgbGFtYmRhIHd5YnJhbmEgcHJ6ZXogQ1YiKSArDQogIGdlb21fbGluZShkYXRhPXNtcjIsIGFlcyh4PXgsIHk9eSksIGNvbD0nYmx1ZScpDQpgYGANCg0KIyMgUG9yw7N3bmFuaWUgZnVua2NqaSBsb2Nwb2x5IGkgc21vb3RoLnNwbGluZQ0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0KZml0IDwtIGxvY3BvbHkodGltZXMsIGFjY2VsLA0KICAgICAgICBkZWdyZWU9MCwgYmFuZHdpZHRoPWRvcGFzb3dhbmEpICU+JSBhcy50aWJibGUNCg0Kc21yIDwtIHNtb290aC5zcGxpbmUoDQogIHRpbWVzLA0KICBhY2NlbCwNCiAgY3Y9VFJVRSkNCnNtciA8LSBkYXRhLmZyYW1lKHg9c21yJHgseT1zbXIkeSkNCg0KZ2dwbG90KG1jeWNsZSkgKw0KICBnZW9tX3BvaW50KGFlcyh4PXRpbWVzLHk9YWNjZWwpKSArDQogIGdlb21fbGluZShkYXRhPWZpdCwgYWVzKHg9eCx5PXkpLCBjb2w9J2JsdWUnKSsNCiAgZ2VvbV9saW5lKGRhdGE9c21yLCBhZXMoeD14LHk9eSksIGNvbD0ncmVkJykrDQogIGdndGl0bGUoIlBvcsOzd25hbmllIGZ1bmtjamkgbG9jcG9seSBpIHNtb290aC5zcGxpbmUiKQ0KYGBgDQoNCiMjIE5hdHVyYWxuZSBzcGxvdHkNCg0KYGBge3J9DQpsaWJyYXJ5KHNwbGluZXMpDQoNCmZpdCA8LWxtKGFjY2Vsfm5zKHRpbWVzLGRmPTYpLG1jeWNsZSkNCmZpdDIgPC1sbShhY2NlbH5ucyh0aW1lcyxkZj0xMiksbWN5Y2xlKQ0KDQpnZ3Bsb3QobWN5Y2xlKSsNCiAgZ2VvbV9wb2ludChhZXMoeD10aW1lcyx5PWFjY2VsKSkrDQogIGdndGl0bGUoIk5hdHVyYWxuZSBzcGxvdHksIDYgZGYobmllYmllc2tpKSBpIDEyIGRmKGN6ZXJ3b255KSIpKw0KICBnZW9tX2xpbmUoYWVzKHg9dGltZXMseT1maXR0ZWQoZml0KSksY29sPSdibHVlJykrDQogIGdlb21fbGluZShhZXMoeD10aW1lcyx5PWZpdHRlZChmaXQyKSksY29sPSdyZWQnKQ0KYGBgDQoNCiMjIFNwbG90eSBvcmF6IGdlb21fc21vb3RoKCkNCg0KYGBge3J9DQpnZ3Bsb3QobWN5Y2xlKSArDQogICAgZ2VvbV9wb2ludChhZXMoeD10aW1lcyx5PWFjY2VsKSkgKw0KICAgIGdlb21fc21vb3RoKGFlcyh4PXRpbWVzLHk9Zml0dGVkKGZpdCkpLCBtZXRob2Q9J2dhbScsDQogICAgICBmb3JtdWxhID0geSB+IHMoeCxrPTEyKSwgc2U9VFJVRSkNCmBgYA0KDQpgYGB7cn0NCmZpdCA8LSBsbShhY2NlbCB+IG5zKHRpbWVzLCBkZiA9IDYpLCBtY3ljbGUpDQpwcmVkIDwtIGRhdGEuZnJhbWUodGltZXMgPSBtY3ljbGUkdGltZXMsDQogICAgICAgICAgICAgICAgICAgcHJlZGljdChmaXQsIGludGVydmFsID0gImNvbmZpZGVuY2UiKSkNCmdncGxvdChtY3ljbGUsIGFlcyh4ID0gdGltZXMsIHkgPSBhY2NlbCkpICsNCiAgICBnZW9tX3BvaW50KCkgKw0KICAgIGdlb21fbGluZShkYXRhID0gcHJlZCwgYWVzKHkgPSBmaXQpLCBjb2xvciA9ICJibHVlIikgKw0KICAgIGdlb21fcmliYm9uKGRhdGEgPSBwcmVkLCBhZXMoeW1pbiA9IGx3ciwgeW1heCA9IHVwciksIGFscGhhID0gMC4yKQ0KYGBgDQoNCg==