Load Library

library(writexl) 
library(dplyr) 
library(lubridate) 
library(readr)
library(tidyverse) 
library(ggplot2)

Data Load wifi

wifi <- read.csv("C:\\Users\\Diana Eka Justitia\\Downloads\\wifi.csv")
head(wifi)

lib1 <- read.csv("C:\\Users\\Diana Eka Justitia\\Downloads\\library1.csv")
head(lib1)

lib2 <- read.csv("C:\\Users\\Diana Eka Justitia\\Downloads\\library2.csv")
head(lib2)

lib3 <- read.csv("C:\\Users\\Diana Eka Justitia\\Downloads\\library3.csv")
head(lib3)
NA

Cek data Building

table(wifi$Building)

                      Alex Square                     Bailrigg House                  Barker House Farm                     Bowland Annexe  
                             14328                              21230                               7164                              28654 
                      Bowland Ash                       Bowland hall                       Bowland Main                      Bowland North  
                             28652                              28652                              28656                              28655 
 Bowland Twr (Old Bowland Annexe)                  Central Workshops                              CETAD                  Chaplaincy Centre  
                            107447                              14326                              14328                              14328 
                   Charles Carter                  Conference Centre                             County                       County Field  
                             28656                               7164                              35816                              28656 
             County South/Cartmel                      Energy Centre                        Engineering                   Faraday and cTAP  
                             35819                               7164                              35980                              28656 
                    Field Station                       FU Hse 71-74                            Furness                      Furness outer  
                              7164                              42877                              35817                              28656 
                     FY Hse 65-70                              Fylde                         George Fox                   Graduate College  
                             42978                              35819                              28654                              28653 
                       Great Hall                          grize-res                          Grizedale                          Hazelrigg  
                             14328                              28652                              14326                               7164 
                        Hse 75 77                    Human Resources                            Infolab     Institute for Advanced Studies  
                             35820                              14328                              28656                               7164 
                     ISS Building                         John Creed                                LEC                            Library  
                             21492                              28652                              28656                              28652 
                             LICA              Lonsdale College (SW)                  Management School                                MDC  
                             21491                              14328                              28655                               7164 
                           Pendle                            Physics               Postgrad Stats (PSC)                          Preschool  
                             28652                              28656                              14326                               7163 
                        Reception                     Ruskin Library             Science and technology             Slaidburn House (LUSU)  
                             14328                              21491                              21492                              35820 
                    Sports Centre                     SW hse-158-179                       SW hse 12-16                       SW hse 17-19  
                             14326                               7163                              21489                              21489 
                        SW hse 20                       SW hse 21-23                       SW hse 24-26                       SW hse 27-28  
                             21489                              24926                              32408                              28655 
                        SW hse 29                       SW hse 30-31                       SW hse 32-33                          SW hse 34  
                             28655                              28656                              28652                              14326 
                        SW hse 35                          SW hse 36                       SW hse 37-38                          SW hse 39  
                             21489                              28652                              28652                              28656 
                     SW hse 40-42                       SW hse 43-45                       SW hse 46-49                       SW hse 50-52  
                             28656                              28656                              28656                              28656 
                     SW hse 53-54                       SW hse 55-56                   University House                            Whewell  
                             28656                              21492                              28656                              14328 

Hapus spasi dan ubah ke lowercase

wifi <- wifi[tolower(trimws(wifi$Building)) == "library", ] 
# jumlah baris 
cat("Jumlah baris untuk Library:", nrow(wifi), "\n")
Jumlah baris untuk Library: 28652 

Wifi

cat("Wifi missing values:\n")
Wifi missing values:
colSums(is.na(wifi))
                      time                 Event.Time    Associated.Client.Count Authenticated.Client.Count                        Uni                   Building 
                         0                          0                          0                          0                          0                          0 
                     Floor 
                         0 

Library1

cat("Library1 missing values:\n")
Library1 missing values:
colSums(is.na(lib1))
        ts       name    reading      units cumulative       rate 
         0          0       3041          0       3041       3047 

Library2

cat("Library2 missing values:\n")
Library2 missing values:
colSums(is.na(lib2))
        ts       name    reading      units cumulative       rate 
         0          0       3041          0       3041       3047 

Library3

cat("Library3 missing values:\n")
Library3 missing values:
colSums(is.na(lib3))
        ts       name    reading      units cumulative       rate 
         0          0       3041          0       3041       3047 

Isi kolom missing values

dfs <- list(lib1 = lib1, lib2 = lib2, lib3 = lib3)

for (name in names(dfs)) {
  df <- dfs[[name]]
  
  # kolom numerik yang mau diisi
  numeric_cols <- c("reading", "cumulative", "rate")
  
  # hitung rata-rata dari 144 baris pertama
  means <- colMeans(df[1:144, numeric_cols], na.rm = TRUE)
  
  # ganti NA dengan mean
  for (col in numeric_cols) {
    na_idx <- is.na(df[[col]])
    df[na_idx, col] <- means[col]
  }
  
  dfs[[name]] <- df
}

Kembalikan ke variabel asli

lib1 <- dfs$lib1
lib2 <- dfs$lib2
lib3 <- dfs$lib3

Cek kembali missing value

# Wifi
cat("Wifi missing values:\n")
Wifi missing values:
colSums(is.na(wifi))
                      time                 Event.Time    Associated.Client.Count Authenticated.Client.Count                        Uni                   Building 
                         0                          0                          0                          0                          0                          0 
                     Floor 
                         0 
# Library1
cat("Library1 missing values:\n")
Library1 missing values:
colSums(is.na(lib1))
        ts       name    reading      units cumulative       rate 
         0          0          0          0          0          0 
# Library2
cat("Library2 missing values:\n")
Library2 missing values:
colSums(is.na(lib2))
        ts       name    reading      units cumulative       rate 
         0          0          0          0          0          0 
# Library3
cat("Library3 missing values:\n")
Library3 missing values:
colSums(is.na(lib3))
        ts       name    reading      units cumulative       rate 
         0          0          0          0          0          0 

Cek Duplikat

cat("\nJumlah duplikat WiFi:", sum(duplicated(wifi)), "\n")

Jumlah duplikat WiFi: 0 
cat("Jumlah duplikat Library1:", sum(duplicated(lib1)), "\n")
Jumlah duplikat Library1: 0 
cat("Jumlah duplikat Library2:", sum(duplicated(lib2)), "\n")
Jumlah duplikat Library2: 0 
cat("Jumlah duplikat Library3:", sum(duplicated(lib3)), "\n")
Jumlah duplikat Library3: 0 
sapply(wifi, class)
                      time                 Event.Time    Associated.Client.Count Authenticated.Client.Count                        Uni                   Building 
               "character"                "character"                  "integer"                  "integer"                "character"                "character" 
                     Floor 
               "character" 
sapply(lib1, class)
         ts        name     reading       units  cumulative        rate 
"character" "character"   "numeric" "character"   "numeric"   "numeric" 
sapply(lib2, class)
         ts        name     reading       units  cumulative        rate 
"character" "character"   "numeric" "character"   "numeric"   "numeric" 
sapply(lib3, class)
         ts        name     reading       units  cumulative        rate 
"character" "character"   "numeric" "character"   "numeric"   "numeric" 
wifi <- wifi %>%
  mutate(
    time = ymd_hms(time))          # otomatis parse jadi datetime

lib1 <- lib1 %>%
  mutate(ts = ymd_hms(ts))

lib2 <- lib2 %>%
  mutate(ts = ymd_hms(ts))

lib3 <- lib3 %>%
  mutate(ts = ymd_hms(ts))



sapply(wifi, class)
$time
[1] "POSIXct" "POSIXt" 

$Event.Time
[1] "character"

$Associated.Client.Count
[1] "integer"

$Authenticated.Client.Count
[1] "integer"

$Uni
[1] "character"

$Building
[1] "character"

$Floor
[1] "character"
sapply(lib1, class)
$ts
[1] "POSIXct" "POSIXt" 

$name
[1] "character"

$reading
[1] "numeric"

$units
[1] "character"

$cumulative
[1] "numeric"

$rate
[1] "numeric"
sapply(lib2, class)
$ts
[1] "POSIXct" "POSIXt" 

$name
[1] "character"

$reading
[1] "numeric"

$units
[1] "character"

$cumulative
[1] "numeric"

$rate
[1] "numeric"
sapply(lib3, class)
$ts
[1] "POSIXct" "POSIXt" 

$name
[1] "character"

$reading
[1] "numeric"

$units
[1] "character"

$cumulative
[1] "numeric"

$rate
[1] "numeric"

Ambil kolom ts dan rate masing-masing energi

lib1_clean <- lib1 %>%
  select(ts, rate) %>%
  rename(energy1 = rate)

lib2_clean <- lib2 %>%
  select(ts, rate) %>%
  rename(energy2 = rate)

lib3_clean <- lib3 %>%
  select(ts, rate) %>%
  rename(energy3 = rate)

energy_df <- lib1_clean %>%
  inner_join(lib2_clean, by = "ts") %>%
  inner_join(lib3_clean, by = "ts")

Hitung total konsumsi energi

energy_df <- energy_df %>%
  mutate(Total_Energy = energy1 + energy2 + energy3) %>%
  arrange(ts)
head(energy_df,10)

Pilih hanya kolom yang dibutuhkan di wifi

wifi <- wifi %>%
  select(time, `Associated.Client.Count`) %>%
  rename(Associated_Client_Count = `Associated.Client.Count`)

head(wifi,10)

Resampling per 10 menit (ambil rata-rata jumlah koneksi)

wifi_resampled <- wifi %>%
  mutate(time = floor_date(time, "10 minutes")) %>%
  group_by(time) %>%
  summarise(Associated_Client_Count = mean(Associated_Client_Count, na.rm = TRUE)) %>%
  ungroup()

head(wifi_resampled,10)

Gabungkan berdasarkan timestamp

final_df <- wifi_resampled %>%
  inner_join(energy_df, by = c("time" = "ts"))

head(final_df,10)

Simpan sebagai CSV

write_csv(final_df, "final_wifi_energy.csv")
getwd()
[1] "C:/Users/Diana Eka Justitia/Downloads"
##jika excel
#write_xlsx(final_df, "final_wifi_energy.xlsx")

Visualisasi

Time Series Plot

final_df <- read.csv("C:\\Users\\Diana Eka Justitia\\Downloads\\final_wifi_energy.csv")
head(final_df) 

Ubah format variabel time

final_df$time <- as.POSIXct(final_df$time, format = "%Y-%m-%dT%H:%M:%SZ", tz = "UTC")
str(final_df$time)
 POSIXct[1:3683], format: "2020-02-01 00:00:00" "2020-02-01 00:10:00" "2020-02-01 00:20:00" "2020-02-01 00:30:00" "2020-02-01 00:40:00" "2020-02-01 00:50:00" "2020-02-01 01:00:00" ...
head(final_df$time)
[1] "2020-02-01 00:00:00 UTC" "2020-02-01 00:10:00 UTC" "2020-02-01 00:20:00 UTC" "2020-02-01 00:30:00 UTC" "2020-02-01 00:40:00 UTC" "2020-02-01 00:50:00 UTC"

Plot Time Series

ggplot(final_df) +
  geom_line(aes(x = time, y = Associated_Client_Count, color = "Associated_Client_Count")) + 
  geom_line(aes(x = time, y = Total_Energy, color = "Total Energy")) +
  scale_x_datetime(date_breaks = "1 day", date_labels = "%b-%d") +
  xlab("") +
  ylab("Value") +
  ggtitle("Time Series: Associated_Client_Count vs Total Energy - Februari")  +
  scale_color_manual(
    name = "Variable",
    values = c("Associated_Client_Count" = "blue", "Total Energy" = "red")
  )+theme(axis.text.x = element_text(angle = 90, hjust = 1))

Scatter Plot Occupancy vs Total Energy

ggplot(final_df, aes(x =Associated_Client_Count , y = Total_Energy)) +
  geom_point(color = "blue", size = 2, alpha = 0.6) +
  xlab("Occupancy") +
  ylab("Total Energy") +
  ggtitle("Scatter Plot: Occupancy vs Total Energy") +
  theme_minimal()

Daily Profile Occupancy

tambah variabel time_of_day

final_df <- final_df %>%
  mutate(time_of_day = as.numeric(format(time, "%H")) + as.numeric(format(time, "%M"))/60,
 date = as.factor(as.Date(time)))

Hitung rata-rata per 10 menit

daily_avg <- final_df %>%
  group_by(time_of_day) %>%
  summarise(
    avg_occupancy = mean(Associated_Client_Count, na.rm = TRUE),
    avg_energy = mean(Total_Energy, na.rm = TRUE)
  )

Plot Daily Occupancy Profile

ggplot(final_df, aes(x = time_of_day, y = Associated_Client_Count)) +
  geom_line(aes(group = date), color = "gray", alpha = 0.3) +  # tiap hari
  geom_line(data = daily_avg, aes(x = time_of_day, y = avg_occupancy), color = "red", size = 1.5) +
  xlab("Hour of Day") +
  ylab("Occupancy") +
  ggtitle("Daily Occupancy Profiles with Average (26 Days)") +
  scale_x_continuous(breaks = seq(0, 24, by = 1)) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, hjust = 1))

Analisis

Peak

Peak Occupancy

peak_occupancy_time <- daily_avg$time_of_day[which.max(daily_avg$avg_occupancy)]

Peak Total Energy

peak_energy_time <- daily_avg$time_of_day[which.max(daily_avg$avg_energy)]

Plot Peak Time

ggplot(daily_avg, aes(x = time_of_day)) +
  geom_line(aes(y = avg_occupancy, color = "Occupancy"), linewidth = 1.2) +
  geom_line(aes(y = avg_energy, color = "Total Energy"), linewidth = 1.2) +
  # Garis vertikal peak otomatis
  geom_vline(xintercept = peak_occupancy_time, color = "blue", linetype = "dashed", linewidth = 1) +
  geom_vline(xintercept = peak_energy_time, color = "red", linetype = "dashed", linewidth = 1) +
  xlab("Jam") +
  ylab("Rata-rata") +
  ggtitle("Profil Harian (10 Menit) - Occupancy & Total Energy dengan Peak Otomatis") +
  scale_x_continuous(
    breaks = seq(0, 24, by = 10/60),
    labels = function(x) sprintf("%02d:%02d", floor(x), round((x - floor(x))*60))
  ) +
  scale_color_manual(values = c("Occupancy" = "blue", "Total Energy" = "red")) +
  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 90, hjust = 1),
    legend.title = element_blank()
  )

Regres Polinomial

# orde 2
model_poly2 <- lm(Total_Energy ~ poly(Associated_Client_Count, 2, raw = TRUE), data = final_df)

# ringkasan model
summary(model_poly2)

Call:
lm(formula = Total_Energy ~ poly(Associated_Client_Count, 2, 
    raw = TRUE), data = final_df)

Residuals:
    Min      1Q  Median      3Q     Max 
-45.619  -9.043  -0.504   8.922  50.936 

Coefficients:
                                                Estimate Std. Error t value Pr(>|t|)    
(Intercept)                                    8.784e+01  4.173e-01  210.51   <2e-16 ***
poly(Associated_Client_Count, 2, raw = TRUE)1  6.288e-01  5.821e-03  108.02   <2e-16 ***
poly(Associated_Client_Count, 2, raw = TRUE)2 -8.855e-04  1.367e-05  -64.78   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 14.29 on 3680 degrees of freedom
Multiple R-squared:  0.8931,    Adjusted R-squared:  0.8931 
F-statistic: 1.538e+04 on 2 and 3680 DF,  p-value: < 2.2e-16

Anomalies

#menghitung residual 
final_df <- final_df %>%
  mutate(predicted_energy = predict(model_poly2, newdata = ),
         residual = Total_Energy - predicted_energy)

# menghitung batas toleransi (+-2σ)
res_sd <- sd(final_df$residual, na.rm = TRUE)
upper_bound <- 2 * res_sd
lower_bound <- -2 * res_sd

# Menandai anomali berdasarkan 
final_df <- final_df %>%
  mutate(anomaly = case_when(
    residual > upper_bound ~ "High Energy",
    residual < lower_bound ~ "Low Energy",
    TRUE ~ "Normal"
  ))

Plot Anomali

ggplot(final_df, aes(x = Associated_Client_Count, y = Total_Energy, color = anomaly)) +
  geom_point(alpha = 0.6) +
  geom_line(aes(y = predicted_energy), color = "green", linewidth = 1) +
  scale_color_manual(values = c("Normal" = "gray", "High Energy" = "red", "Low Energy" = "blue")) +
  xlab("Occupancy") +
  ylab("Total Energy") +
  ggtitle("Deteksi Anomali Energy vs Occupancy (±2σ Residual)") +
  theme_minimal()

Weekend VS Weekday

Memberi label untuk hari

final_df$day_name <- weekdays(final_df$time)
head(final_df)

Memberi label weekend dan weekday

final_df$is_weekend <- ifelse(final_df$day_name %in% c("Saturday", "Sunday"), "Weekend", "Weekday")
head(final_df)

Plot

daily_avg <- final_df %>%
  group_by(time_of_day, is_weekend) %>%
  summarise(avg_occupancy = mean(Associated_Client_Count, na.rm = TRUE),
            .groups = "drop")


ggplot(daily_avg, aes(x = time_of_day, y = avg_occupancy, color = is_weekend)) +
  geom_line(linewidth = 1.2) +
  scale_x_continuous(
    breaks = seq(0, 24, by = 2),
    labels = function(x) sprintf("%02d:%02d", floor(x), round((x - floor(x))*60))
  ) +
  labs(title = "Profil Harian Occupancy: Weekday vs Weekend",
       x = "Jam", y = "Rata-rata Occupancy", color = "Kategori") +
  theme_minimal()

NA
NA

Plot Time Series Weekend dan Weekday

final_df_hourly <- final_df %>%
  mutate(hour = lubridate::floor_date(time, "hour")) %>%
  group_by(hour, is_weekend, day_name) %>%
  summarise(avg_occupancy = mean (Associated_Client_Count, na.rm = TRUE), .group = "drop")
`summarise()` has grouped output by 'hour', 'is_weekend'. You can override using the `.groups` argument.
ggplot(final_df_hourly, aes(x = hour, y = avg_occupancy,
                             color = is_weekend,
                             group = is_weekend)) +
  geom_line(linewidth = 0.9) +
  labs(title = "Time Series Occupancy per Jam (Weekday vs Weekend)",
       x = "Waktu", y = "Rata-rata Occupancy", color = "Kategori") +
  theme_minimal()

NA
NA

Scatter Plot Weekend dan Weekday

ggplot(final_df, aes(x = Associated_Client_Count, 
                     y = Total_Energy, 
                     color = is_weekend)) +
  geom_point(alpha = 0.6) +
  geom_smooth(method = "loess", se = FALSE, color = "green", linewidth = 1) +
  xlab("Occupancy") +
  ylab("Total Energy") +
  ggtitle("Occupancy vs Total Energy: Weekend vs Weekday") +
  theme_minimal()

NA
NA
LS0tDQp0aXRsZTogIkRhdGEgTWluaW5nIEFkdmVudHVyZVIiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyBMb2FkIExpYnJhcnkNCg0KYGBge3J9DQpsaWJyYXJ5KHdyaXRleGwpIA0KbGlicmFyeShkcGx5cikgDQpsaWJyYXJ5KGx1YnJpZGF0ZSkgDQpsaWJyYXJ5KHJlYWRyKQ0KbGlicmFyeSh0aWR5dmVyc2UpIA0KbGlicmFyeShnZ3Bsb3QyKQ0KYGBgDQojIyMgRGF0YSBMb2FkIHdpZmkNCg0KYGBge3J9DQp3aWZpIDwtIHJlYWQuY3N2KCJDOlxcVXNlcnNcXERpYW5hIEVrYSBKdXN0aXRpYVxcRG93bmxvYWRzXFx3aWZpLmNzdiIpDQpoZWFkKHdpZmkpDQoNCmxpYjEgPC0gcmVhZC5jc3YoIkM6XFxVc2Vyc1xcRGlhbmEgRWthIEp1c3RpdGlhXFxEb3dubG9hZHNcXGxpYnJhcnkxLmNzdiIpDQpoZWFkKGxpYjEpDQoNCmxpYjIgPC0gcmVhZC5jc3YoIkM6XFxVc2Vyc1xcRGlhbmEgRWthIEp1c3RpdGlhXFxEb3dubG9hZHNcXGxpYnJhcnkyLmNzdiIpDQpoZWFkKGxpYjIpDQoNCmxpYjMgPC0gcmVhZC5jc3YoIkM6XFxVc2Vyc1xcRGlhbmEgRWthIEp1c3RpdGlhXFxEb3dubG9hZHNcXGxpYnJhcnkzLmNzdiIpDQpoZWFkKGxpYjMpDQoNCmBgYA0KDQojIyBDZWsgZGF0YSBCdWlsZGluZw0KYGBge3J9DQp0YWJsZSh3aWZpJEJ1aWxkaW5nKQ0KYGBgDQojIyBIYXB1cyBzcGFzaSBkYW4gdWJhaCBrZSBsb3dlcmNhc2UgDQoNCmBgYHtyfQ0Kd2lmaSA8LSB3aWZpW3RvbG93ZXIodHJpbXdzKHdpZmkkQnVpbGRpbmcpKSA9PSAibGlicmFyeSIsIF0gDQojIGp1bWxhaCBiYXJpcyANCmNhdCgiSnVtbGFoIGJhcmlzIHVudHVrIExpYnJhcnk6IiwgbnJvdyh3aWZpKSwgIlxuIikNCmBgYA0KIyMjIFdpZmkNCg0KYGBge3J9DQpjYXQoIldpZmkgbWlzc2luZyB2YWx1ZXM6XG4iKQ0KY29sU3Vtcyhpcy5uYSh3aWZpKSkNCmBgYA0KIyMjIExpYnJhcnkxDQoNCmBgYHtyfQ0KY2F0KCJMaWJyYXJ5MSBtaXNzaW5nIHZhbHVlczpcbiIpDQpjb2xTdW1zKGlzLm5hKGxpYjEpKQ0KYGBgDQoNCiMjIyBMaWJyYXJ5Mg0KDQpgYGB7cn0NCmNhdCgiTGlicmFyeTIgbWlzc2luZyB2YWx1ZXM6XG4iKQ0KY29sU3Vtcyhpcy5uYShsaWIyKSkNCmBgYA0KDQojIyMgTGlicmFyeTMNCg0KYGBge3J9DQpjYXQoIkxpYnJhcnkzIG1pc3NpbmcgdmFsdWVzOlxuIikNCmNvbFN1bXMoaXMubmEobGliMykpDQpgYGANCg0KIyMjIElzaSBrb2xvbSBtaXNzaW5nIHZhbHVlcw0KYGBge3J9DQpkZnMgPC0gbGlzdChsaWIxID0gbGliMSwgbGliMiA9IGxpYjIsIGxpYjMgPSBsaWIzKQ0KDQpmb3IgKG5hbWUgaW4gbmFtZXMoZGZzKSkgew0KICBkZiA8LSBkZnNbW25hbWVdXQ0KICANCiAgIyBrb2xvbSBudW1lcmlrIHlhbmcgbWF1IGRpaXNpDQogIG51bWVyaWNfY29scyA8LSBjKCJyZWFkaW5nIiwgImN1bXVsYXRpdmUiLCAicmF0ZSIpDQogIA0KICAjIGhpdHVuZyByYXRhLXJhdGEgZGFyaSAxNDQgYmFyaXMgcGVydGFtYQ0KICBtZWFucyA8LSBjb2xNZWFucyhkZlsxOjE0NCwgbnVtZXJpY19jb2xzXSwgbmEucm0gPSBUUlVFKQ0KICANCiAgIyBnYW50aSBOQSBkZW5nYW4gbWVhbg0KICBmb3IgKGNvbCBpbiBudW1lcmljX2NvbHMpIHsNCiAgICBuYV9pZHggPC0gaXMubmEoZGZbW2NvbF1dKQ0KICAgIGRmW25hX2lkeCwgY29sXSA8LSBtZWFuc1tjb2xdDQogIH0NCiAgDQogIGRmc1tbbmFtZV1dIDwtIGRmDQp9DQpgYGANCiMjIyBLZW1iYWxpa2FuIGtlIHZhcmlhYmVsIGFzbGkNCg0KYGBge3J9DQpsaWIxIDwtIGRmcyRsaWIxDQpsaWIyIDwtIGRmcyRsaWIyDQpsaWIzIDwtIGRmcyRsaWIzDQpgYGANCg0KIyMjIENlayBrZW1iYWxpIG1pc3NpbmcgdmFsdWUgDQoNCmBgYHtyfQ0KIyBXaWZpDQpjYXQoIldpZmkgbWlzc2luZyB2YWx1ZXM6XG4iKQ0KY29sU3Vtcyhpcy5uYSh3aWZpKSkNCg0KIyBMaWJyYXJ5MQ0KY2F0KCJMaWJyYXJ5MSBtaXNzaW5nIHZhbHVlczpcbiIpDQpjb2xTdW1zKGlzLm5hKGxpYjEpKQ0KDQojIExpYnJhcnkyDQpjYXQoIkxpYnJhcnkyIG1pc3NpbmcgdmFsdWVzOlxuIikNCmNvbFN1bXMoaXMubmEobGliMikpDQoNCiMgTGlicmFyeTMNCmNhdCgiTGlicmFyeTMgbWlzc2luZyB2YWx1ZXM6XG4iKQ0KY29sU3Vtcyhpcy5uYShsaWIzKSkNCmBgYA0KDQojIyMgQ2VrIER1cGxpa2F0DQoNCmBgYHtyfQ0KY2F0KCJcbkp1bWxhaCBkdXBsaWthdCBXaUZpOiIsIHN1bShkdXBsaWNhdGVkKHdpZmkpKSwgIlxuIikNCmNhdCgiSnVtbGFoIGR1cGxpa2F0IExpYnJhcnkxOiIsIHN1bShkdXBsaWNhdGVkKGxpYjEpKSwgIlxuIikNCmNhdCgiSnVtbGFoIGR1cGxpa2F0IExpYnJhcnkyOiIsIHN1bShkdXBsaWNhdGVkKGxpYjIpKSwgIlxuIikNCmNhdCgiSnVtbGFoIGR1cGxpa2F0IExpYnJhcnkzOiIsIHN1bShkdXBsaWNhdGVkKGxpYjMpKSwgIlxuIikNCg0Kc2FwcGx5KHdpZmksIGNsYXNzKQ0Kc2FwcGx5KGxpYjEsIGNsYXNzKQ0Kc2FwcGx5KGxpYjIsIGNsYXNzKQ0Kc2FwcGx5KGxpYjMsIGNsYXNzKQ0KDQp3aWZpIDwtIHdpZmkgJT4lDQogIG11dGF0ZSgNCiAgICB0aW1lID0geW1kX2htcyh0aW1lKSkgICAgICAgICAgIyBvdG9tYXRpcyBwYXJzZSBqYWRpIGRhdGV0aW1lDQoNCmxpYjEgPC0gbGliMSAlPiUNCiAgbXV0YXRlKHRzID0geW1kX2htcyh0cykpDQoNCmxpYjIgPC0gbGliMiAlPiUNCiAgbXV0YXRlKHRzID0geW1kX2htcyh0cykpDQoNCmxpYjMgPC0gbGliMyAlPiUNCiAgbXV0YXRlKHRzID0geW1kX2htcyh0cykpDQoNCg0KDQpzYXBwbHkod2lmaSwgY2xhc3MpDQpzYXBwbHkobGliMSwgY2xhc3MpDQpzYXBwbHkobGliMiwgY2xhc3MpDQpzYXBwbHkobGliMywgY2xhc3MpDQpgYGANCg0KDQojIyMgQW1iaWwga29sb20gdHMgZGFuIHJhdGUgbWFzaW5nLW1hc2luZyBlbmVyZ2kNCg0KYGBge3J9DQpsaWIxX2NsZWFuIDwtIGxpYjEgJT4lDQogIHNlbGVjdCh0cywgcmF0ZSkgJT4lDQogIHJlbmFtZShlbmVyZ3kxID0gcmF0ZSkNCg0KbGliMl9jbGVhbiA8LSBsaWIyICU+JQ0KICBzZWxlY3QodHMsIHJhdGUpICU+JQ0KICByZW5hbWUoZW5lcmd5MiA9IHJhdGUpDQoNCmxpYjNfY2xlYW4gPC0gbGliMyAlPiUNCiAgc2VsZWN0KHRzLCByYXRlKSAlPiUNCiAgcmVuYW1lKGVuZXJneTMgPSByYXRlKQ0KYGBgDQoNCiMjIw0KDQpgYGB7cn0NCmVuZXJneV9kZiA8LSBsaWIxX2NsZWFuICU+JQ0KICBpbm5lcl9qb2luKGxpYjJfY2xlYW4sIGJ5ID0gInRzIikgJT4lDQogIGlubmVyX2pvaW4obGliM19jbGVhbiwgYnkgPSAidHMiKQ0KYGBgDQojIyMgSGl0dW5nIHRvdGFsIGtvbnN1bXNpIGVuZXJnaQ0KDQpgYGB7cn0NCmVuZXJneV9kZiA8LSBlbmVyZ3lfZGYgJT4lDQogIG11dGF0ZShUb3RhbF9FbmVyZ3kgPSBlbmVyZ3kxICsgZW5lcmd5MiArIGVuZXJneTMpICU+JQ0KICBhcnJhbmdlKHRzKQ0KaGVhZChlbmVyZ3lfZGYsMTApDQpgYGANCiMjIyBQaWxpaCBoYW55YSBrb2xvbSB5YW5nIGRpYnV0dWhrYW4gZGkgd2lmaQ0KDQpgYGB7cn0NCndpZmkgPC0gd2lmaSAlPiUNCiAgc2VsZWN0KHRpbWUsIGBBc3NvY2lhdGVkLkNsaWVudC5Db3VudGApICU+JQ0KICByZW5hbWUoQXNzb2NpYXRlZF9DbGllbnRfQ291bnQgPSBgQXNzb2NpYXRlZC5DbGllbnQuQ291bnRgKQ0KDQpoZWFkKHdpZmksMTApDQpgYGANCg0KIyMjIFJlc2FtcGxpbmcgcGVyIDEwIG1lbml0IChhbWJpbCByYXRhLXJhdGEganVtbGFoIGtvbmVrc2kpDQoNCmBgYHtyfQ0Kd2lmaV9yZXNhbXBsZWQgPC0gd2lmaSAlPiUNCiAgbXV0YXRlKHRpbWUgPSBmbG9vcl9kYXRlKHRpbWUsICIxMCBtaW51dGVzIikpICU+JQ0KICBncm91cF9ieSh0aW1lKSAlPiUNCiAgc3VtbWFyaXNlKEFzc29jaWF0ZWRfQ2xpZW50X0NvdW50ID0gbWVhbihBc3NvY2lhdGVkX0NsaWVudF9Db3VudCwgbmEucm0gPSBUUlVFKSkgJT4lDQogIHVuZ3JvdXAoKQ0KDQpoZWFkKHdpZmlfcmVzYW1wbGVkLDEwKQ0KYGBgDQoNCiMjIyBHYWJ1bmdrYW4gYmVyZGFzYXJrYW4gdGltZXN0YW1wDQoNCmBgYHtyfQ0KZmluYWxfZGYgPC0gd2lmaV9yZXNhbXBsZWQgJT4lDQogIGlubmVyX2pvaW4oZW5lcmd5X2RmLCBieSA9IGMoInRpbWUiID0gInRzIikpDQoNCmhlYWQoZmluYWxfZGYsMTApDQpgYGANCg0KIyMjIFNpbXBhbiBzZWJhZ2FpIENTVg0KYGBge3J9DQp3cml0ZV9jc3YoZmluYWxfZGYsICJmaW5hbF93aWZpX2VuZXJneS5jc3YiKQ0KZ2V0d2QoKQ0KDQojI2ppa2EgZXhjZWwNCiN3cml0ZV94bHN4KGZpbmFsX2RmLCAiZmluYWxfd2lmaV9lbmVyZ3kueGxzeCIpDQpgYGANCiMjICoqVmlzdWFsaXNhc2kqKg0KIyMjICpUaW1lIFNlcmllcyBQbG90Kg0KYGBge3J9DQpmaW5hbF9kZiA8LSByZWFkLmNzdigiQzpcXFVzZXJzXFxEaWFuYSBFa2EgSnVzdGl0aWFcXERvd25sb2Fkc1xcZmluYWxfd2lmaV9lbmVyZ3kuY3N2IikNCmhlYWQoZmluYWxfZGYpIA0KYGBgDQoNCiMjIyBVYmFoIGZvcm1hdCB2YXJpYWJlbCB0aW1lDQpgYGB7cn0NCmZpbmFsX2RmJHRpbWUgPC0gYXMuUE9TSVhjdChmaW5hbF9kZiR0aW1lLCBmb3JtYXQgPSAiJVktJW0tJWRUJUg6JU06JVNaIiwgdHogPSAiVVRDIikNCnN0cihmaW5hbF9kZiR0aW1lKQ0KaGVhZChmaW5hbF9kZiR0aW1lKQ0KYGBgDQojIyMgUGxvdCBUaW1lIFNlcmllcw0KYGBge3J9DQpnZ3Bsb3QoZmluYWxfZGYpICsNCiAgZ2VvbV9saW5lKGFlcyh4ID0gdGltZSwgeSA9IEFzc29jaWF0ZWRfQ2xpZW50X0NvdW50LCBjb2xvciA9ICJBc3NvY2lhdGVkX0NsaWVudF9Db3VudCIpKSArIA0KICBnZW9tX2xpbmUoYWVzKHggPSB0aW1lLCB5ID0gVG90YWxfRW5lcmd5LCBjb2xvciA9ICJUb3RhbCBFbmVyZ3kiKSkgKw0KICBzY2FsZV94X2RhdGV0aW1lKGRhdGVfYnJlYWtzID0gIjEgZGF5IiwgZGF0ZV9sYWJlbHMgPSAiJWItJWQiKSArDQogIHhsYWIoIiIpICsNCiAgeWxhYigiVmFsdWUiKSArDQogIGdndGl0bGUoIlRpbWUgU2VyaWVzOiBBc3NvY2lhdGVkX0NsaWVudF9Db3VudCB2cyBUb3RhbCBFbmVyZ3kgLSBGZWJydWFyaSIpICArDQogIHNjYWxlX2NvbG9yX21hbnVhbCgNCiAgICBuYW1lID0gIlZhcmlhYmxlIiwNCiAgICB2YWx1ZXMgPSBjKCJBc3NvY2lhdGVkX0NsaWVudF9Db3VudCIgPSAiYmx1ZSIsICJUb3RhbCBFbmVyZ3kiID0gInJlZCIpDQogICkrdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkNCmBgYA0KIyMjIFNjYXR0ZXIgUGxvdCBPY2N1cGFuY3kgdnMgVG90YWwgRW5lcmd5DQpgYGB7cn0NCmdncGxvdChmaW5hbF9kZiwgYWVzKHggPUFzc29jaWF0ZWRfQ2xpZW50X0NvdW50ICwgeSA9IFRvdGFsX0VuZXJneSkpICsNCiAgZ2VvbV9wb2ludChjb2xvciA9ICJibHVlIiwgc2l6ZSA9IDIsIGFscGhhID0gMC42KSArDQogIHhsYWIoIk9jY3VwYW5jeSIpICsNCiAgeWxhYigiVG90YWwgRW5lcmd5IikgKw0KICBnZ3RpdGxlKCJTY2F0dGVyIFBsb3Q6IE9jY3VwYW5jeSB2cyBUb3RhbCBFbmVyZ3kiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQojIyMgRGFpbHkgUHJvZmlsZSBPY2N1cGFuY3kNCiMjIyMgKnRhbWJhaCB2YXJpYWJlbCB0aW1lX29mX2RheSoNCmBgYHtyfQ0KZmluYWxfZGYgPC0gZmluYWxfZGYgJT4lDQogIG11dGF0ZSh0aW1lX29mX2RheSA9IGFzLm51bWVyaWMoZm9ybWF0KHRpbWUsICIlSCIpKSArIGFzLm51bWVyaWMoZm9ybWF0KHRpbWUsICIlTSIpKS82MCwNCiBkYXRlID0gYXMuZmFjdG9yKGFzLkRhdGUodGltZSkpKQ0KYGBgDQojIyMjICpIaXR1bmcgcmF0YS1yYXRhIHBlciAxMCBtZW5pdCoNCmBgYHtyfQ0KZGFpbHlfYXZnIDwtIGZpbmFsX2RmICU+JQ0KICBncm91cF9ieSh0aW1lX29mX2RheSkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBhdmdfb2NjdXBhbmN5ID0gbWVhbihBc3NvY2lhdGVkX0NsaWVudF9Db3VudCwgbmEucm0gPSBUUlVFKSwNCiAgICBhdmdfZW5lcmd5ID0gbWVhbihUb3RhbF9FbmVyZ3ksIG5hLnJtID0gVFJVRSkNCiAgKQ0KYGBgDQoNCiMjIyBQbG90IERhaWx5IE9jY3VwYW5jeSBQcm9maWxlDQpgYGB7cn0NCmdncGxvdChmaW5hbF9kZiwgYWVzKHggPSB0aW1lX29mX2RheSwgeSA9IEFzc29jaWF0ZWRfQ2xpZW50X0NvdW50KSkgKw0KICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gZGF0ZSksIGNvbG9yID0gImdyYXkiLCBhbHBoYSA9IDAuMykgKyAgIyB0aWFwIGhhcmkNCiAgZ2VvbV9saW5lKGRhdGEgPSBkYWlseV9hdmcsIGFlcyh4ID0gdGltZV9vZl9kYXksIHkgPSBhdmdfb2NjdXBhbmN5KSwgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDEuNSkgKw0KICB4bGFiKCJIb3VyIG9mIERheSIpICsNCiAgeWxhYigiT2NjdXBhbmN5IikgKw0KICBnZ3RpdGxlKCJEYWlseSBPY2N1cGFuY3kgUHJvZmlsZXMgd2l0aCBBdmVyYWdlICgyNiBEYXlzKSIpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAyNCwgYnkgPSAxKSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQ0KYGBgDQojIyAqKkFuYWxpc2lzKioNCiMjIyAqUGVhayoNCiMjIyMgUGVhayBPY2N1cGFuY3kNCmBgYHtyfQ0KcGVha19vY2N1cGFuY3lfdGltZSA8LSBkYWlseV9hdmckdGltZV9vZl9kYXlbd2hpY2gubWF4KGRhaWx5X2F2ZyRhdmdfb2NjdXBhbmN5KV0NCmBgYA0KIyMjIyBQZWFrIFRvdGFsIEVuZXJneQ0KDQpgYGB7cn0NCnBlYWtfZW5lcmd5X3RpbWUgPC0gZGFpbHlfYXZnJHRpbWVfb2ZfZGF5W3doaWNoLm1heChkYWlseV9hdmckYXZnX2VuZXJneSldDQpgYGANCiMjIyBQbG90IFBlYWsgVGltZQ0KYGBge3J9DQpnZ3Bsb3QoZGFpbHlfYXZnLCBhZXMoeCA9IHRpbWVfb2ZfZGF5KSkgKw0KICBnZW9tX2xpbmUoYWVzKHkgPSBhdmdfb2NjdXBhbmN5LCBjb2xvciA9ICJPY2N1cGFuY3kiKSwgbGluZXdpZHRoID0gMS4yKSArDQogIGdlb21fbGluZShhZXMoeSA9IGF2Z19lbmVyZ3ksIGNvbG9yID0gIlRvdGFsIEVuZXJneSIpLCBsaW5ld2lkdGggPSAxLjIpICsNCiAgIyBHYXJpcyB2ZXJ0aWthbCBwZWFrIG90b21hdGlzDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHBlYWtfb2NjdXBhbmN5X3RpbWUsIGNvbG9yID0gImJsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBsaW5ld2lkdGggPSAxKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHBlYWtfZW5lcmd5X3RpbWUsIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIGxpbmV3aWR0aCA9IDEpICsNCiAgeGxhYigiSmFtIikgKw0KICB5bGFiKCJSYXRhLXJhdGEiKSArDQogIGdndGl0bGUoIlByb2ZpbCBIYXJpYW4gKDEwIE1lbml0KSAtIE9jY3VwYW5jeSAmIFRvdGFsIEVuZXJneSBkZW5nYW4gUGVhayBPdG9tYXRpcyIpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKA0KICAgIGJyZWFrcyA9IHNlcSgwLCAyNCwgYnkgPSAxMC82MCksDQogICAgbGFiZWxzID0gZnVuY3Rpb24oeCkgc3ByaW50ZigiJTAyZDolMDJkIiwgZmxvb3IoeCksIHJvdW5kKCh4IC0gZmxvb3IoeCkpKjYwKSkNCiAgKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJPY2N1cGFuY3kiID0gImJsdWUiLCAiVG90YWwgRW5lcmd5IiA9ICJyZWQiKSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpLA0KICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKQ0KICApDQpgYGANCiMjIyBSZWdyZXMgUG9saW5vbWlhbCANCmBgYHtyfQ0KIyBvcmRlIDINCm1vZGVsX3BvbHkyIDwtIGxtKFRvdGFsX0VuZXJneSB+IHBvbHkoQXNzb2NpYXRlZF9DbGllbnRfQ291bnQsIDIsIHJhdyA9IFRSVUUpLCBkYXRhID0gZmluYWxfZGYpDQoNCiMgcmluZ2thc2FuIG1vZGVsDQpzdW1tYXJ5KG1vZGVsX3BvbHkyKQ0KYGBgDQojIyMgQW5vbWFsaWVzDQoNCmBgYHtyfQ0KI21lbmdoaXR1bmcgcmVzaWR1YWwgDQpmaW5hbF9kZiA8LSBmaW5hbF9kZiAlPiUNCiAgbXV0YXRlKHByZWRpY3RlZF9lbmVyZ3kgPSBwcmVkaWN0KG1vZGVsX3BvbHkyLCBuZXdkYXRhID0gKSwNCiAgICAgICAgIHJlc2lkdWFsID0gVG90YWxfRW5lcmd5IC0gcHJlZGljdGVkX2VuZXJneSkNCg0KIyBtZW5naGl0dW5nIGJhdGFzIHRvbGVyYW5zaSAoKy0yz4MpDQpyZXNfc2QgPC0gc2QoZmluYWxfZGYkcmVzaWR1YWwsIG5hLnJtID0gVFJVRSkNCnVwcGVyX2JvdW5kIDwtIDIgKiByZXNfc2QNCmxvd2VyX2JvdW5kIDwtIC0yICogcmVzX3NkDQoNCiMgTWVuYW5kYWkgYW5vbWFsaSBiZXJkYXNhcmthbiANCmZpbmFsX2RmIDwtIGZpbmFsX2RmICU+JQ0KICBtdXRhdGUoYW5vbWFseSA9IGNhc2Vfd2hlbigNCiAgICByZXNpZHVhbCA+IHVwcGVyX2JvdW5kIH4gIkhpZ2ggRW5lcmd5IiwNCiAgICByZXNpZHVhbCA8IGxvd2VyX2JvdW5kIH4gIkxvdyBFbmVyZ3kiLA0KICAgIFRSVUUgfiAiTm9ybWFsIg0KICApKQ0KYGBgDQojIyMgUGxvdCBBbm9tYWxpDQoNCmBgYHtyfQ0KZ2dwbG90KGZpbmFsX2RmLCBhZXMoeCA9IEFzc29jaWF0ZWRfQ2xpZW50X0NvdW50LCB5ID0gVG90YWxfRW5lcmd5LCBjb2xvciA9IGFub21hbHkpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjYpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZGljdGVkX2VuZXJneSksIGNvbG9yID0gImdyZWVuIiwgbGluZXdpZHRoID0gMSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiTm9ybWFsIiA9ICJncmF5IiwgIkhpZ2ggRW5lcmd5IiA9ICJyZWQiLCAiTG93IEVuZXJneSIgPSAiYmx1ZSIpKSArDQogIHhsYWIoIk9jY3VwYW5jeSIpICsNCiAgeWxhYigiVG90YWwgRW5lcmd5IikgKw0KICBnZ3RpdGxlKCJEZXRla3NpIEFub21hbGkgRW5lcmd5IHZzIE9jY3VwYW5jeSAowrEyz4MgUmVzaWR1YWwpIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KIyMgV2Vla2VuZCBWUyBXZWVrZGF5DQojIyMgTWVtYmVyaSBsYWJlbCB1bnR1ayBoYXJpDQoNCmBgYHtyfQ0KZmluYWxfZGYkZGF5X25hbWUgPC0gd2Vla2RheXMoZmluYWxfZGYkdGltZSkNCmhlYWQoZmluYWxfZGYpDQpgYGANCiMjIyBNZW1iZXJpIGxhYmVsIHdlZWtlbmQgZGFuIHdlZWtkYXkNCg0KYGBge3J9DQpmaW5hbF9kZiRpc193ZWVrZW5kIDwtIGlmZWxzZShmaW5hbF9kZiRkYXlfbmFtZSAlaW4lIGMoIlNhdHVyZGF5IiwgIlN1bmRheSIpLCAiV2Vla2VuZCIsICJXZWVrZGF5IikNCmhlYWQoZmluYWxfZGYpDQpgYGANCiMjIyBQbG90DQpgYGB7cn0NCmRhaWx5X2F2ZyA8LSBmaW5hbF9kZiAlPiUNCiAgZ3JvdXBfYnkodGltZV9vZl9kYXksIGlzX3dlZWtlbmQpICU+JQ0KICBzdW1tYXJpc2UoYXZnX29jY3VwYW5jeSA9IG1lYW4oQXNzb2NpYXRlZF9DbGllbnRfQ291bnQsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICAuZ3JvdXBzID0gImRyb3AiKQ0KDQoNCmdncGxvdChkYWlseV9hdmcsIGFlcyh4ID0gdGltZV9vZl9kYXksIHkgPSBhdmdfb2NjdXBhbmN5LCBjb2xvciA9IGlzX3dlZWtlbmQpKSArDQogIGdlb21fbGluZShsaW5ld2lkdGggPSAxLjIpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKA0KICAgIGJyZWFrcyA9IHNlcSgwLCAyNCwgYnkgPSAyKSwNCiAgICBsYWJlbHMgPSBmdW5jdGlvbih4KSBzcHJpbnRmKCIlMDJkOiUwMmQiLCBmbG9vcih4KSwgcm91bmQoKHggLSBmbG9vcih4KSkqNjApKQ0KICApICsNCiAgbGFicyh0aXRsZSA9ICJQcm9maWwgSGFyaWFuIE9jY3VwYW5jeTogV2Vla2RheSB2cyBXZWVrZW5kIiwNCiAgICAgICB4ID0gIkphbSIsIHkgPSAiUmF0YS1yYXRhIE9jY3VwYW5jeSIsIGNvbG9yID0gIkthdGVnb3JpIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KDQpgYGANCiMjIyBQbG90IFRpbWUgU2VyaWVzIFdlZWtlbmQgZGFuIFdlZWtkYXkNCmBgYHtyfQ0KZmluYWxfZGZfaG91cmx5IDwtIGZpbmFsX2RmICU+JQ0KICBtdXRhdGUoaG91ciA9IGx1YnJpZGF0ZTo6Zmxvb3JfZGF0ZSh0aW1lLCAiaG91ciIpKSAlPiUNCiAgZ3JvdXBfYnkoaG91ciwgaXNfd2Vla2VuZCwgZGF5X25hbWUpICU+JQ0KICBzdW1tYXJpc2UoYXZnX29jY3VwYW5jeSA9IG1lYW4gKEFzc29jaWF0ZWRfQ2xpZW50X0NvdW50LCBuYS5ybSA9IFRSVUUpLCAuZ3JvdXAgPSAiZHJvcCIpDQoNCmdncGxvdChmaW5hbF9kZl9ob3VybHksIGFlcyh4ID0gaG91ciwgeSA9IGF2Z19vY2N1cGFuY3ksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gaXNfd2Vla2VuZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBpc193ZWVrZW5kKSkgKw0KICBnZW9tX2xpbmUobGluZXdpZHRoID0gMC45KSArDQogIGxhYnModGl0bGUgPSAiVGltZSBTZXJpZXMgT2NjdXBhbmN5IHBlciBKYW0gKFdlZWtkYXkgdnMgV2Vla2VuZCkiLA0KICAgICAgIHggPSAiV2FrdHUiLCB5ID0gIlJhdGEtcmF0YSBPY2N1cGFuY3kiLCBjb2xvciA9ICJLYXRlZ29yaSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCg0KYGBgDQojIyMgU2NhdHRlciBQbG90IFdlZWtlbmQgZGFuIFdlZWtkYXkNCmBgYHtyfQ0KZ2dwbG90KGZpbmFsX2RmLCBhZXMoeCA9IEFzc29jaWF0ZWRfQ2xpZW50X0NvdW50LCANCiAgICAgICAgICAgICAgICAgICAgIHkgPSBUb3RhbF9FbmVyZ3ksIA0KICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBpc193ZWVrZW5kKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMC42KSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIsIHNlID0gRkFMU0UsIGNvbG9yID0gImdyZWVuIiwgbGluZXdpZHRoID0gMSkgKw0KICB4bGFiKCJPY2N1cGFuY3kiKSArDQogIHlsYWIoIlRvdGFsIEVuZXJneSIpICsNCiAgZ2d0aXRsZSgiT2NjdXBhbmN5IHZzIFRvdGFsIEVuZXJneTogV2Vla2VuZCB2cyBXZWVrZGF5IikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KDQpgYGA=