Kelompok 3
Anggota:
- Fikri Rizal Dhiya Ul Haq (5003221105)
- Ghifari Muammar Abimanyu (5003221127)
- Ardhi Nugroho (5003221174)
- Muhammad Faiz Maulana (5003221188)
Link RPubs: https://rpubs.com/KripikPedas/LibraryOccupancyEnergyConsumption

Library

library(tidyverse)
library(lubridate)

Pre-Processing Data

wifi_df <- read_csv('wifi.csv')
head(wifi_df)
library_df <- wifi_df %>%
  mutate(across(where(is.character), str_trim)) %>%
  filter(Building == "Library") %>%
  distinct()

head(library_df)

Mengubah data menjadi per 10 menit

library_df <- library_df %>%
  mutate(
    time = as.POSIXct(time, format="%Y-%m-%d %H:%M:%S"),
    time_floored = floor_date(time, "10 minutes")
  )

library_df

Resampling data dengan menggabungkan baris dengan rata-rata

library_df_clean <- library_df %>%
  group_by(time_floored) %>%
  summarise(AssociatedClientCount_mean = mean(`Associated Client Count`, na.rm = TRUE))

head(library_df_clean)

Mengextract data library 1 2 3

elec1 <- read_csv('library1.csv')
elec2 <- read_csv('library2.csv')
elec3 <- read_csv('library3.csv')

elec1
elec2
elec3

Data Integration

Menggabungkan semua dataset menjadi 1

merged_library <- library_df_clean %>%
  left_join(elec1, by = c("time_floored" = "ts")) %>%
  left_join(elec2, by = c("time_floored" = "ts"), suffix = c("", "_elec2")) %>%
  left_join(elec3, by = c("time_floored" = "ts"), suffix = c("", "_elec3"))

merged_library

Menghitung total pemakaian listrik

merged_library <- merged_library %>%
  mutate(total_rate = rowSums(select(., rate, rate_elec2, rate_elec3), na.rm = TRUE))

merged_library

Memilih variabel yang akan digunakan

# Memilih kolom yang relevan
merged_library <- merged_library %>%
  select(time_floored, AssociatedClientCount_mean, total_rate)

merged_library

Menghitung rata-rata untuk melengkapi row pertama total rate

mean_rate_first_day <- merged_library %>%
  filter(row_number() > 1 & row_number() <= 144) %>%
  summarise(mean_rate = mean(total_rate, na.rm = TRUE)) %>%
  pull(mean_rate)

merged_library$total_rate[1] <- mean_rate_first_day

merged_library

Visualisasi

Plot Time Series

ggplot(merged_library, aes(x = time_floored)) +
  geom_line(aes(y = AssociatedClientCount_mean, colour = "Occupancy")) +
  geom_line(aes(y = total_rate, colour = "Energy Consumption")) +
  labs(
    title = "Time Series of Occupancy & Energy Consumption",
    x = "Time",
    y = "Value",
    colour = "Legend"
  ) +
  theme_minimal()

Scatter Plot

ggplot(merged_library, aes(x = AssociatedClientCount_mean, y = total_rate)) +
  geom_point(alpha = 0.6) +
  labs(
    title = "Scatter Plot of Occupancy vs. Energy Consumption",
    x = "Occupancy",
    y = "Energy Consumption"
  ) +
  theme_minimal()

Daily Profile Cycle

# Buat kolom hari dan format waktu
merged_library <- merged_library %>%
  mutate(
    day_of_week = wday(time_floored, label = TRUE, week_start = 1),
    time_of_day = format(time_floored, "%H:%M")
  )

# Buat urutan waktu yang benar untuk digunakan sebagai level faktor
time_order <- format(
  seq.POSIXt(as.POSIXct("00:00", format = "%H:%M"), 
             as.POSIXct("23:50", format = "%H:%M"), 
             by = "10 min"), 
  "%H:%M"
)

# Hitung rata-rata per 10 menit untuk setiap hari
avg_per_10min_per_day <- merged_library %>%
  group_by(day_of_week, time_of_day) %>%
  summarise(
    avg_occupancy = mean(AssociatedClientCount_mean, na.rm = TRUE),
    avg_energy = mean(total_rate, na.rm = TRUE),
    .groups = 'drop' # .groups = 'drop' setara dengan ungroup()
  ) %>%
  # Ubah time_of_day menjadi faktor untuk memastikan urutan yang benar di plot
  mutate(time_of_day = factor(time_of_day, levels = time_order))

# Tentukan label waktu yang akan ditampilkan (setiap jam)
time_breaks <- time_order[seq(1, length(time_order), by = 6)]

occ_day <- ggplot(avg_per_10min_per_day, aes(x = time_of_day, y = avg_occupancy, color = day_of_week, group = day_of_week)) +
  geom_line(linewidth = 1) +
  scale_x_discrete(breaks = time_breaks) + 
  labs(
    title = 'Daily Occupancy (Monday-Sunday)',
    x = 'Time',
    y = 'Occupancy',
    color = 'Day'
  ) +
  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "bottom"
  )
occ_day


rate_day <- ggplot(avg_per_10min_per_day, aes(x = time_of_day, y = avg_energy, color = day_of_week, group = day_of_week)) +
  geom_line(linewidth = 1) +
  scale_x_discrete(breaks = time_breaks) +
  labs(
    title = 'Daily Energy Consumption (Monday-Sunday)',
    x = 'Time',
    y = 'Energy Consumption',
    color = 'Day'
  ) +
  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "bottom"
  )
rate_day

Average Daily Profile

# Hitung rata-rata okupansi dan energi untuk setiap slot 10 menit
avg_per_10min <- merged_library %>%
  group_by(time_of_day) %>%
  summarise(
    `Rata-rata Okupansi` = mean(AssociatedClientCount_mean, na.rm = TRUE),
    `Rata-rata Konsumsi Energi` = mean(total_rate, na.rm = TRUE),
    .groups = 'drop'
  ) %>%
  mutate(time_of_day = factor(time_of_day, levels = time_order))

# Ubah data dari format lebar ke panjang untuk plotting dengan ggplot
avg_per_10min_long <- avg_per_10min %>%
  pivot_longer(
    cols = c(`Rata-rata Okupansi`, `Rata-rata Konsumsi Energi`),
    names_to = "TipeMetrik",
    values_to = "NilaiRataRata"
  )

# Tentukan jeda label untuk sumbu-x
time_breaks <- time_order[seq(1, length(time_order), by = 6)]

# Buat plot
ggplot(avg_per_10min_long, aes(x = time_of_day, y = NilaiRataRata, color = TipeMetrik, group = TipeMetrik)) +
  geom_line(linewidth = 1) +
  scale_x_discrete(breaks = time_breaks) +
  scale_color_manual(values = c("Rata-rata Okupansi" = "#1f77b4", "Rata-rata Konsumsi Energi" = "darkorange")) + 
  labs(
    title = 'Average Occupancy & Energy Consumption',
    x = 'Time',
    y = 'Average Value',
    color = 'Legend'
  ) +
  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.position = "bottom"
  )

Analysis

Peak Hour

merged_library <- merged_library %>%
  mutate(hour = hour(time_floored))

hourly_avg_all_days <- merged_library %>%
  group_by(hour) %>%
  summarise(Avg_Occupancy = mean(AssociatedClientCount_mean, na.rm = TRUE))

peak_hour_data <- hourly_avg_all_days %>% filter(Avg_Occupancy == max(Avg_Occupancy))
peak_hour <- peak_hour_data$hour
peak_value <- peak_hour_data$Avg_Occupancy

cat(sprintf("Jam puncak okupansi adalah pada jam %d:00 dengan rata-rata %.0f orang.\n", peak_hour, peak_value))
Jam puncak okupansi adalah pada jam 15:00 dengan rata-rata 370 orang.
# Plot Jam Puncak
ggplot(hourly_avg_all_days, aes(x = hour, y = Avg_Occupancy)) +
  geom_bar(stat = "identity", aes(fill = ifelse(hour == peak_hour, "Peak", "Normal"))) +
  geom_text(aes(label = round(Avg_Occupancy, 0)), vjust = -0.5) +
  scale_fill_manual(values = c("Peak" = "#ff7f0e", "Normal" = "#1f77b4"), guide = "none") +
  labs(
    title = "Average Occupancy",
    x = "Hour",
    y = "Average Occupancy"
  ) +
  scale_x_continuous(breaks = seq(0, 23, 1)) +
  theme_minimal()

Occupancy Influence on Energy Consumption

correlation_test <- cor.test(merged_library$AssociatedClientCount_mean, merged_library$total_rate)
corr_value <- correlation_test$estimate
p_value <- correlation_test$p.value

cat(sprintf("Korelasi Pearson antara okupansi dan konsumsi energi: %.3f (p-value: %.3e)\n", corr_value, p_value))
Korelasi Pearson antara okupansi dan konsumsi energi: 0.878 (p-value: 0.000e+00)
if (corr_value > 0.5) {
  influence <- "korelasi positif yang kuat"
} else if (corr_value > 0.3) {
  influence <- "korelasi positif yang sedang"
} else if (corr_value > 0) {
  influence <- "korelasi positif yang lemah"
} else if (corr_value < -0.5) {
  influence <- "korelasi negatif yang kuat"
} else if (corr_value < -0.3) {
  influence <- "korelasi negatif yang sedang"
} else {
  influence <- "korelasi negatif yang lemah"
}
cat(sprintf("Ini menunjukkan adanya %s.\n", influence))
Ini menunjukkan adanya korelasi positif yang kuat.

Cases where energy consumption does not align with occupancy

# Analisis Kasus di mana Konsumsi Energi Tidak Sejalan dengan Okupansi
# Hitung z-scores
merged_library <- merged_library %>%
  mutate(
    occ_z = as.vector(scale(AssociatedClientCount_mean)),
    energy_z = as.vector(scale(total_rate))
  )

# Cari kasus di mana perbedaannya besar (misalnya, > 2 standar deviasi)
misaligned_cases <- merged_library %>%
  filter(abs(occ_z - energy_z) > 2)

if (nrow(misaligned_cases) == 0) {
  cat("Tidak ada kasus yang tidak selaras antara okupansi dan konsumsi energi.\n")
} else {
  print(misaligned_cases %>% select(time_floored, AssociatedClientCount_mean, total_rate, occ_z, energy_z))
}
Tidak ada kasus yang tidak selaras antara okupansi dan konsumsi energi.

Weekday Vs. Weekend

# Buat kolom tipe hari (Hari Kerja / Akhir Pekan)
merged_library <- merged_library %>%
  mutate(
    TipeHari = if_else(wday(time_floored, week_start = 1) < 6, "Weekday", "Weekend"),
    time_of_day = format(time_floored, "%H:%M")
  )

# Buat urutan waktu yang benar untuk plotting
time_order <- format(
  seq.POSIXt(as.POSIXct("00:00", format = "%H:%M"), 
             as.POSIXct("23:50", format = "%H:%M"), 
             by = "10 min"), 
  "%H:%M"
)

# Hitung rata-rata per 10 menit untuk setiap tipe hari
avg_per_tipe_hari <- merged_library %>%
  group_by(TipeHari, time_of_day) %>%
  summarise(
    `Okupansi Rata-rata` = mean(AssociatedClientCount_mean, na.rm = TRUE),
    `Konsumsi Energi Rata-rata` = mean(total_rate, na.rm = TRUE),
    .groups = 'drop'
  ) %>%
  mutate(time_of_day = factor(time_of_day, levels = time_order))

# Tentukan jeda label untuk sumbu-x yang akan digunakan di kedua plot
time_breaks <- time_order[seq(1, length(time_order), by = 6)]

# Occupancy
ggplot(avg_per_tipe_hari, aes(x = time_of_day, y = `Okupansi Rata-rata`, color = TipeHari, group = TipeHari)) +
  geom_line(linewidth = 1) +
  scale_x_discrete(breaks = time_breaks) +
  scale_color_brewer(palette = "Set1") +
  labs(
    title = "Average Occupancy Weekday vs. Weekend",
    x = "Time",
    y = "Average Occupancy",
    color = "Day Type"
  ) +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))


#Energy Consumption
ggplot(avg_per_tipe_hari, aes(x = time_of_day, y = `Konsumsi Energi Rata-rata`, color = TipeHari, group = TipeHari)) +
  geom_line(linewidth = 1) +
  scale_x_discrete(breaks = time_breaks) +
  scale_color_brewer(palette = "Dark2") +
  labs(
    title = "Average Energy Consumption Weekday Vs. Weekend",
    x = "Time",
    y = "Average Energy Consumption",
    color = "Day Type"
  ) +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

LS0tDQp0aXRsZTogIlR1Z2FzIERhdGEgTWluaW5nIFczIFIiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCktlbG9tcG9rIDMgPGJyPg0KQW5nZ290YTogPGJyPg0KLSBGaWtyaSBSaXphbCBEaGl5YSBVbCBIYXEgKDUwMDMyMjExMDUpIDxicj4NCi0gR2hpZmFyaSBNdWFtbWFyIEFiaW1hbnl1ICg1MDAzMjIxMTI3KSA8YnI+DQotIEFyZGhpIE51Z3JvaG8gKDUwMDMyMjExNzQpIDxicj4NCi0gTXVoYW1tYWQgRmFpeiBNYXVsYW5hICg1MDAzMjIxMTg4KSA8YnI+DQpMaW5rIFJQdWJzOiBodHRwczovL3JwdWJzLmNvbS9LcmlwaWtQZWRhcy9MaWJyYXJ5T2NjdXBhbmN5RW5lcmd5Q29uc3VtcHRpb24NCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQojIyBMaWJyYXJ5DQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpgYGANCiMjIFByZS1Qcm9jZXNzaW5nIERhdGENCmBgYHtyfQ0Kd2lmaV9kZiA8LSByZWFkX2Nzdignd2lmaS5jc3YnKQ0KaGVhZCh3aWZpX2RmKQ0KYGBgDQpgYGB7cn0NCmxpYnJhcnlfZGYgPC0gd2lmaV9kZiAlPiUNCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5jaGFyYWN0ZXIpLCBzdHJfdHJpbSkpICU+JQ0KICBmaWx0ZXIoQnVpbGRpbmcgPT0gIkxpYnJhcnkiKSAlPiUNCiAgZGlzdGluY3QoKQ0KDQpoZWFkKGxpYnJhcnlfZGYpDQpgYGANCiMjIyBNZW5ndWJhaCBkYXRhIG1lbmphZGkgcGVyIDEwIG1lbml0DQpgYGB7cn0NCmxpYnJhcnlfZGYgPC0gbGlicmFyeV9kZiAlPiUNCiAgbXV0YXRlKA0KICAgIHRpbWUgPSBhcy5QT1NJWGN0KHRpbWUsIGZvcm1hdD0iJVktJW0tJWQgJUg6JU06JVMiKSwNCiAgICB0aW1lX2Zsb29yZWQgPSBmbG9vcl9kYXRlKHRpbWUsICIxMCBtaW51dGVzIikNCiAgKQ0KDQpsaWJyYXJ5X2RmDQpgYGANCiMjIyBSZXNhbXBsaW5nIGRhdGEgZGVuZ2FuIG1lbmdnYWJ1bmdrYW4gYmFyaXMgZGVuZ2FuIHJhdGEtcmF0YQ0KYGBge3J9DQpsaWJyYXJ5X2RmX2NsZWFuIDwtIGxpYnJhcnlfZGYgJT4lDQogIGdyb3VwX2J5KHRpbWVfZmxvb3JlZCkgJT4lDQogIHN1bW1hcmlzZShBc3NvY2lhdGVkQ2xpZW50Q291bnRfbWVhbiA9IG1lYW4oYEFzc29jaWF0ZWQgQ2xpZW50IENvdW50YCwgbmEucm0gPSBUUlVFKSkNCg0KaGVhZChsaWJyYXJ5X2RmX2NsZWFuKQ0KYGBgDQojIyMgTWVuZ2V4dHJhY3QgZGF0YSBsaWJyYXJ5IDEgMiAzDQpgYGB7cn0NCmVsZWMxIDwtIHJlYWRfY3N2KCdsaWJyYXJ5MS5jc3YnKQ0KZWxlYzIgPC0gcmVhZF9jc3YoJ2xpYnJhcnkyLmNzdicpDQplbGVjMyA8LSByZWFkX2NzdignbGlicmFyeTMuY3N2JykNCg0KZWxlYzENCmVsZWMyDQplbGVjMw0KYGBgDQojIyBEYXRhIEludGVncmF0aW9uDQojIE1lbmdnYWJ1bmdrYW4gc2VtdWEgZGF0YXNldCBtZW5qYWRpIDENCmBgYHtyfQ0KbWVyZ2VkX2xpYnJhcnkgPC0gbGlicmFyeV9kZl9jbGVhbiAlPiUNCiAgbGVmdF9qb2luKGVsZWMxLCBieSA9IGMoInRpbWVfZmxvb3JlZCIgPSAidHMiKSkgJT4lDQogIGxlZnRfam9pbihlbGVjMiwgYnkgPSBjKCJ0aW1lX2Zsb29yZWQiID0gInRzIiksIHN1ZmZpeCA9IGMoIiIsICJfZWxlYzIiKSkgJT4lDQogIGxlZnRfam9pbihlbGVjMywgYnkgPSBjKCJ0aW1lX2Zsb29yZWQiID0gInRzIiksIHN1ZmZpeCA9IGMoIiIsICJfZWxlYzMiKSkNCg0KbWVyZ2VkX2xpYnJhcnkNCmBgYA0KIyMjIE1lbmdoaXR1bmcgdG90YWwgcGVtYWthaWFuIGxpc3RyaWsNCmBgYHtyfQ0KbWVyZ2VkX2xpYnJhcnkgPC0gbWVyZ2VkX2xpYnJhcnkgJT4lDQogIG11dGF0ZSh0b3RhbF9yYXRlID0gcm93U3VtcyhzZWxlY3QoLiwgcmF0ZSwgcmF0ZV9lbGVjMiwgcmF0ZV9lbGVjMyksIG5hLnJtID0gVFJVRSkpDQoNCm1lcmdlZF9saWJyYXJ5DQpgYGANCiMjIyBNZW1pbGloIHZhcmlhYmVsIHlhbmcgYWthbiBkaWd1bmFrYW4NCmBgYHtyfQ0KIyBNZW1pbGloIGtvbG9tIHlhbmcgcmVsZXZhbg0KbWVyZ2VkX2xpYnJhcnkgPC0gbWVyZ2VkX2xpYnJhcnkgJT4lDQogIHNlbGVjdCh0aW1lX2Zsb29yZWQsIEFzc29jaWF0ZWRDbGllbnRDb3VudF9tZWFuLCB0b3RhbF9yYXRlKQ0KDQptZXJnZWRfbGlicmFyeQ0KYGBgDQojIyMgTWVuZ2hpdHVuZyByYXRhLXJhdGEgdW50dWsgbWVsZW5na2FwaSByb3cgcGVydGFtYSB0b3RhbCByYXRlDQpgYGB7cn0NCm1lYW5fcmF0ZV9maXJzdF9kYXkgPC0gbWVyZ2VkX2xpYnJhcnkgJT4lDQogIGZpbHRlcihyb3dfbnVtYmVyKCkgPiAxICYgcm93X251bWJlcigpIDw9IDE0NCkgJT4lDQogIHN1bW1hcmlzZShtZWFuX3JhdGUgPSBtZWFuKHRvdGFsX3JhdGUsIG5hLnJtID0gVFJVRSkpICU+JQ0KICBwdWxsKG1lYW5fcmF0ZSkNCg0KbWVyZ2VkX2xpYnJhcnkkdG90YWxfcmF0ZVsxXSA8LSBtZWFuX3JhdGVfZmlyc3RfZGF5DQoNCm1lcmdlZF9saWJyYXJ5DQpgYGANCiMjIFZpc3VhbGlzYXNpDQojIyMjIFBsb3QgVGltZSBTZXJpZXMNCmBgYHtyLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9N30NCmdncGxvdChtZXJnZWRfbGlicmFyeSwgYWVzKHggPSB0aW1lX2Zsb29yZWQpKSArDQogIGdlb21fbGluZShhZXMoeSA9IEFzc29jaWF0ZWRDbGllbnRDb3VudF9tZWFuLCBjb2xvdXIgPSAiT2NjdXBhbmN5IikpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gdG90YWxfcmF0ZSwgY29sb3VyID0gIkVuZXJneSBDb25zdW1wdGlvbiIpKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiVGltZSBTZXJpZXMgb2YgT2NjdXBhbmN5ICYgRW5lcmd5IENvbnN1bXB0aW9uIiwNCiAgICB4ID0gIlRpbWUiLA0KICAgIHkgPSAiVmFsdWUiLA0KICAgIGNvbG91ciA9ICJMZWdlbmQiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KIyMjIyBTY2F0dGVyIFBsb3QNCmBgYHtyLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9N30NCmdncGxvdChtZXJnZWRfbGlicmFyeSwgYWVzKHggPSBBc3NvY2lhdGVkQ2xpZW50Q291bnRfbWVhbiwgeSA9IHRvdGFsX3JhdGUpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjYpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJTY2F0dGVyIFBsb3Qgb2YgT2NjdXBhbmN5IHZzLiBFbmVyZ3kgQ29uc3VtcHRpb24iLA0KICAgIHggPSAiT2NjdXBhbmN5IiwNCiAgICB5ID0gIkVuZXJneSBDb25zdW1wdGlvbiINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQojIyMjIERhaWx5IFByb2ZpbGUgQ3ljbGUNCmBgYHtyfQ0KIyBCdWF0IGtvbG9tIGhhcmkgZGFuIGZvcm1hdCB3YWt0dQ0KbWVyZ2VkX2xpYnJhcnkgPC0gbWVyZ2VkX2xpYnJhcnkgJT4lDQogIG11dGF0ZSgNCiAgICBkYXlfb2Zfd2VlayA9IHdkYXkodGltZV9mbG9vcmVkLCBsYWJlbCA9IFRSVUUsIHdlZWtfc3RhcnQgPSAxKSwNCiAgICB0aW1lX29mX2RheSA9IGZvcm1hdCh0aW1lX2Zsb29yZWQsICIlSDolTSIpDQogICkNCg0KIyBCdWF0IHVydXRhbiB3YWt0dSB5YW5nIGJlbmFyIHVudHVrIGRpZ3VuYWthbiBzZWJhZ2FpIGxldmVsIGZha3Rvcg0KdGltZV9vcmRlciA8LSBmb3JtYXQoDQogIHNlcS5QT1NJWHQoYXMuUE9TSVhjdCgiMDA6MDAiLCBmb3JtYXQgPSAiJUg6JU0iKSwgDQogICAgICAgICAgICAgYXMuUE9TSVhjdCgiMjM6NTAiLCBmb3JtYXQgPSAiJUg6JU0iKSwgDQogICAgICAgICAgICAgYnkgPSAiMTAgbWluIiksIA0KICAiJUg6JU0iDQopDQoNCiMgSGl0dW5nIHJhdGEtcmF0YSBwZXIgMTAgbWVuaXQgdW50dWsgc2V0aWFwIGhhcmkNCmF2Z19wZXJfMTBtaW5fcGVyX2RheSA8LSBtZXJnZWRfbGlicmFyeSAlPiUNCiAgZ3JvdXBfYnkoZGF5X29mX3dlZWssIHRpbWVfb2ZfZGF5KSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIGF2Z19vY2N1cGFuY3kgPSBtZWFuKEFzc29jaWF0ZWRDbGllbnRDb3VudF9tZWFuLCBuYS5ybSA9IFRSVUUpLA0KICAgIGF2Z19lbmVyZ3kgPSBtZWFuKHRvdGFsX3JhdGUsIG5hLnJtID0gVFJVRSksDQogICAgLmdyb3VwcyA9ICdkcm9wJyAjIC5ncm91cHMgPSAnZHJvcCcgc2V0YXJhIGRlbmdhbiB1bmdyb3VwKCkNCiAgKSAlPiUNCiAgIyBVYmFoIHRpbWVfb2ZfZGF5IG1lbmphZGkgZmFrdG9yIHVudHVrIG1lbWFzdGlrYW4gdXJ1dGFuIHlhbmcgYmVuYXIgZGkgcGxvdA0KICBtdXRhdGUodGltZV9vZl9kYXkgPSBmYWN0b3IodGltZV9vZl9kYXksIGxldmVscyA9IHRpbWVfb3JkZXIpKQ0KDQojIFRlbnR1a2FuIGxhYmVsIHdha3R1IHlhbmcgYWthbiBkaXRhbXBpbGthbiAoc2V0aWFwIGphbSkNCnRpbWVfYnJlYWtzIDwtIHRpbWVfb3JkZXJbc2VxKDEsIGxlbmd0aCh0aW1lX29yZGVyKSwgYnkgPSA2KV0NCg0Kb2NjX2RheSA8LSBnZ3Bsb3QoYXZnX3Blcl8xMG1pbl9wZXJfZGF5LCBhZXMoeCA9IHRpbWVfb2ZfZGF5LCB5ID0gYXZnX29jY3VwYW5jeSwgY29sb3IgPSBkYXlfb2Zfd2VlaywgZ3JvdXAgPSBkYXlfb2Zfd2VlaykpICsNCiAgZ2VvbV9saW5lKGxpbmV3aWR0aCA9IDEpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShicmVha3MgPSB0aW1lX2JyZWFrcykgKyANCiAgbGFicygNCiAgICB0aXRsZSA9ICdEYWlseSBPY2N1cGFuY3kgKE1vbmRheS1TdW5kYXkpJywNCiAgICB4ID0gJ1RpbWUnLA0KICAgIHkgPSAnT2NjdXBhbmN5JywNCiAgICBjb2xvciA9ICdEYXknDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iDQogICkNCm9jY19kYXkNCg0KcmF0ZV9kYXkgPC0gZ2dwbG90KGF2Z19wZXJfMTBtaW5fcGVyX2RheSwgYWVzKHggPSB0aW1lX29mX2RheSwgeSA9IGF2Z19lbmVyZ3ksIGNvbG9yID0gZGF5X29mX3dlZWssIGdyb3VwID0gZGF5X29mX3dlZWspKSArDQogIGdlb21fbGluZShsaW5ld2lkdGggPSAxKSArDQogIHNjYWxlX3hfZGlzY3JldGUoYnJlYWtzID0gdGltZV9icmVha3MpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICdEYWlseSBFbmVyZ3kgQ29uc3VtcHRpb24gKE1vbmRheS1TdW5kYXkpJywNCiAgICB4ID0gJ1RpbWUnLA0KICAgIHkgPSAnRW5lcmd5IENvbnN1bXB0aW9uJywNCiAgICBjb2xvciA9ICdEYXknDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iDQogICkNCnJhdGVfZGF5DQpgYGANCiMjIyBBdmVyYWdlIERhaWx5IFByb2ZpbGUNCmBgYHtyfQ0KIyBIaXR1bmcgcmF0YS1yYXRhIG9rdXBhbnNpIGRhbiBlbmVyZ2kgdW50dWsgc2V0aWFwIHNsb3QgMTAgbWVuaXQNCmF2Z19wZXJfMTBtaW4gPC0gbWVyZ2VkX2xpYnJhcnkgJT4lDQogIGdyb3VwX2J5KHRpbWVfb2ZfZGF5KSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIGBSYXRhLXJhdGEgT2t1cGFuc2lgID0gbWVhbihBc3NvY2lhdGVkQ2xpZW50Q291bnRfbWVhbiwgbmEucm0gPSBUUlVFKSwNCiAgICBgUmF0YS1yYXRhIEtvbnN1bXNpIEVuZXJnaWAgPSBtZWFuKHRvdGFsX3JhdGUsIG5hLnJtID0gVFJVRSksDQogICAgLmdyb3VwcyA9ICdkcm9wJw0KICApICU+JQ0KICBtdXRhdGUodGltZV9vZl9kYXkgPSBmYWN0b3IodGltZV9vZl9kYXksIGxldmVscyA9IHRpbWVfb3JkZXIpKQ0KDQojIFViYWggZGF0YSBkYXJpIGZvcm1hdCBsZWJhciBrZSBwYW5qYW5nIHVudHVrIHBsb3R0aW5nIGRlbmdhbiBnZ3Bsb3QNCmF2Z19wZXJfMTBtaW5fbG9uZyA8LSBhdmdfcGVyXzEwbWluICU+JQ0KICBwaXZvdF9sb25nZXIoDQogICAgY29scyA9IGMoYFJhdGEtcmF0YSBPa3VwYW5zaWAsIGBSYXRhLXJhdGEgS29uc3Vtc2kgRW5lcmdpYCksDQogICAgbmFtZXNfdG8gPSAiVGlwZU1ldHJpayIsDQogICAgdmFsdWVzX3RvID0gIk5pbGFpUmF0YVJhdGEiDQogICkNCg0KIyBUZW50dWthbiBqZWRhIGxhYmVsIHVudHVrIHN1bWJ1LXgNCnRpbWVfYnJlYWtzIDwtIHRpbWVfb3JkZXJbc2VxKDEsIGxlbmd0aCh0aW1lX29yZGVyKSwgYnkgPSA2KV0NCg0KIyBCdWF0IHBsb3QNCmdncGxvdChhdmdfcGVyXzEwbWluX2xvbmcsIGFlcyh4ID0gdGltZV9vZl9kYXksIHkgPSBOaWxhaVJhdGFSYXRhLCBjb2xvciA9IFRpcGVNZXRyaWssIGdyb3VwID0gVGlwZU1ldHJpaykpICsNCiAgZ2VvbV9saW5lKGxpbmV3aWR0aCA9IDEpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShicmVha3MgPSB0aW1lX2JyZWFrcykgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiUmF0YS1yYXRhIE9rdXBhbnNpIiA9ICIjMWY3N2I0IiwgIlJhdGEtcmF0YSBLb25zdW1zaSBFbmVyZ2kiID0gImRhcmtvcmFuZ2UiKSkgKyANCiAgbGFicygNCiAgICB0aXRsZSA9ICdBdmVyYWdlIE9jY3VwYW5jeSAmIEVuZXJneSBDb25zdW1wdGlvbicsDQogICAgeCA9ICdUaW1lJywNCiAgICB5ID0gJ0F2ZXJhZ2UgVmFsdWUnLA0KICAgIGNvbG9yID0gJ0xlZ2VuZCcNCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksDQogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSINCiAgKQ0KYGBgDQojIyBBbmFseXNpcw0KIyMjIFBlYWsgSG91cg0KYGBge3J9DQptZXJnZWRfbGlicmFyeSA8LSBtZXJnZWRfbGlicmFyeSAlPiUNCiAgbXV0YXRlKGhvdXIgPSBob3VyKHRpbWVfZmxvb3JlZCkpDQoNCmhvdXJseV9hdmdfYWxsX2RheXMgPC0gbWVyZ2VkX2xpYnJhcnkgJT4lDQogIGdyb3VwX2J5KGhvdXIpICU+JQ0KICBzdW1tYXJpc2UoQXZnX09jY3VwYW5jeSA9IG1lYW4oQXNzb2NpYXRlZENsaWVudENvdW50X21lYW4sIG5hLnJtID0gVFJVRSkpDQoNCnBlYWtfaG91cl9kYXRhIDwtIGhvdXJseV9hdmdfYWxsX2RheXMgJT4lIGZpbHRlcihBdmdfT2NjdXBhbmN5ID09IG1heChBdmdfT2NjdXBhbmN5KSkNCnBlYWtfaG91ciA8LSBwZWFrX2hvdXJfZGF0YSRob3VyDQpwZWFrX3ZhbHVlIDwtIHBlYWtfaG91cl9kYXRhJEF2Z19PY2N1cGFuY3kNCg0KY2F0KHNwcmludGYoIkphbSBwdW5jYWsgb2t1cGFuc2kgYWRhbGFoIHBhZGEgamFtICVkOjAwIGRlbmdhbiByYXRhLXJhdGEgJS4wZiBvcmFuZy5cbiIsIHBlYWtfaG91ciwgcGVha192YWx1ZSkpDQoNCiMgUGxvdCBKYW0gUHVuY2FrDQpnZ3Bsb3QoaG91cmx5X2F2Z19hbGxfZGF5cywgYWVzKHggPSBob3VyLCB5ID0gQXZnX09jY3VwYW5jeSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGFlcyhmaWxsID0gaWZlbHNlKGhvdXIgPT0gcGVha19ob3VyLCAiUGVhayIsICJOb3JtYWwiKSkpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHJvdW5kKEF2Z19PY2N1cGFuY3ksIDApKSwgdmp1c3QgPSAtMC41KSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIlBlYWsiID0gIiNmZjdmMGUiLCAiTm9ybWFsIiA9ICIjMWY3N2I0IiksIGd1aWRlID0gIm5vbmUiKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiQXZlcmFnZSBPY2N1cGFuY3kiLA0KICAgIHggPSAiSG91ciIsDQogICAgeSA9ICJBdmVyYWdlIE9jY3VwYW5jeSINCiAgKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMjMsIDEpKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQojIyMgT2NjdXBhbmN5IEluZmx1ZW5jZSBvbiBFbmVyZ3kgQ29uc3VtcHRpb24NCmBgYHtyfQ0KY29ycmVsYXRpb25fdGVzdCA8LSBjb3IudGVzdChtZXJnZWRfbGlicmFyeSRBc3NvY2lhdGVkQ2xpZW50Q291bnRfbWVhbiwgbWVyZ2VkX2xpYnJhcnkkdG90YWxfcmF0ZSkNCmNvcnJfdmFsdWUgPC0gY29ycmVsYXRpb25fdGVzdCRlc3RpbWF0ZQ0KcF92YWx1ZSA8LSBjb3JyZWxhdGlvbl90ZXN0JHAudmFsdWUNCg0KY2F0KHNwcmludGYoIktvcmVsYXNpIFBlYXJzb24gYW50YXJhIG9rdXBhbnNpIGRhbiBrb25zdW1zaSBlbmVyZ2k6ICUuM2YgKHAtdmFsdWU6ICUuM2UpXG4iLCBjb3JyX3ZhbHVlLCBwX3ZhbHVlKSkNCg0KaWYgKGNvcnJfdmFsdWUgPiAwLjUpIHsNCiAgaW5mbHVlbmNlIDwtICJrb3JlbGFzaSBwb3NpdGlmIHlhbmcga3VhdCINCn0gZWxzZSBpZiAoY29ycl92YWx1ZSA+IDAuMykgew0KICBpbmZsdWVuY2UgPC0gImtvcmVsYXNpIHBvc2l0aWYgeWFuZyBzZWRhbmciDQp9IGVsc2UgaWYgKGNvcnJfdmFsdWUgPiAwKSB7DQogIGluZmx1ZW5jZSA8LSAia29yZWxhc2kgcG9zaXRpZiB5YW5nIGxlbWFoIg0KfSBlbHNlIGlmIChjb3JyX3ZhbHVlIDwgLTAuNSkgew0KICBpbmZsdWVuY2UgPC0gImtvcmVsYXNpIG5lZ2F0aWYgeWFuZyBrdWF0Ig0KfSBlbHNlIGlmIChjb3JyX3ZhbHVlIDwgLTAuMykgew0KICBpbmZsdWVuY2UgPC0gImtvcmVsYXNpIG5lZ2F0aWYgeWFuZyBzZWRhbmciDQp9IGVsc2Ugew0KICBpbmZsdWVuY2UgPC0gImtvcmVsYXNpIG5lZ2F0aWYgeWFuZyBsZW1haCINCn0NCmNhdChzcHJpbnRmKCJJbmkgbWVudW5qdWtrYW4gYWRhbnlhICVzLlxuIiwgaW5mbHVlbmNlKSkNCmBgYA0KIyMjIENhc2VzIHdoZXJlIGVuZXJneSBjb25zdW1wdGlvbiBkb2VzIG5vdCBhbGlnbiB3aXRoIG9jY3VwYW5jeQ0KYGBge3J9DQojIEFuYWxpc2lzIEthc3VzIGRpIG1hbmEgS29uc3Vtc2kgRW5lcmdpIFRpZGFrIFNlamFsYW4gZGVuZ2FuIE9rdXBhbnNpDQojIEhpdHVuZyB6LXNjb3Jlcw0KbWVyZ2VkX2xpYnJhcnkgPC0gbWVyZ2VkX2xpYnJhcnkgJT4lDQogIG11dGF0ZSgNCiAgICBvY2NfeiA9IGFzLnZlY3RvcihzY2FsZShBc3NvY2lhdGVkQ2xpZW50Q291bnRfbWVhbikpLA0KICAgIGVuZXJneV96ID0gYXMudmVjdG9yKHNjYWxlKHRvdGFsX3JhdGUpKQ0KICApDQoNCiMgQ2FyaSBrYXN1cyBkaSBtYW5hIHBlcmJlZGFhbm55YSBiZXNhciAobWlzYWxueWEsID4gMiBzdGFuZGFyIGRldmlhc2kpDQptaXNhbGlnbmVkX2Nhc2VzIDwtIG1lcmdlZF9saWJyYXJ5ICU+JQ0KICBmaWx0ZXIoYWJzKG9jY196IC0gZW5lcmd5X3opID4gMikNCg0KaWYgKG5yb3cobWlzYWxpZ25lZF9jYXNlcykgPT0gMCkgew0KICBjYXQoIlRpZGFrIGFkYSBrYXN1cyB5YW5nIHRpZGFrIHNlbGFyYXMgYW50YXJhIG9rdXBhbnNpIGRhbiBrb25zdW1zaSBlbmVyZ2kuXG4iKQ0KfSBlbHNlIHsNCiAgcHJpbnQobWlzYWxpZ25lZF9jYXNlcyAlPiUgc2VsZWN0KHRpbWVfZmxvb3JlZCwgQXNzb2NpYXRlZENsaWVudENvdW50X21lYW4sIHRvdGFsX3JhdGUsIG9jY196LCBlbmVyZ3lfeikpDQp9DQpgYGANCiMjIFdlZWtkYXkgVnMuIFdlZWtlbmQNCmBgYHtyfQ0KIyBCdWF0IGtvbG9tIHRpcGUgaGFyaSAoSGFyaSBLZXJqYSAvIEFraGlyIFBla2FuKQ0KbWVyZ2VkX2xpYnJhcnkgPC0gbWVyZ2VkX2xpYnJhcnkgJT4lDQogIG11dGF0ZSgNCiAgICBUaXBlSGFyaSA9IGlmX2Vsc2Uod2RheSh0aW1lX2Zsb29yZWQsIHdlZWtfc3RhcnQgPSAxKSA8IDYsICJXZWVrZGF5IiwgIldlZWtlbmQiKSwNCiAgICB0aW1lX29mX2RheSA9IGZvcm1hdCh0aW1lX2Zsb29yZWQsICIlSDolTSIpDQogICkNCg0KIyBCdWF0IHVydXRhbiB3YWt0dSB5YW5nIGJlbmFyIHVudHVrIHBsb3R0aW5nDQp0aW1lX29yZGVyIDwtIGZvcm1hdCgNCiAgc2VxLlBPU0lYdChhcy5QT1NJWGN0KCIwMDowMCIsIGZvcm1hdCA9ICIlSDolTSIpLCANCiAgICAgICAgICAgICBhcy5QT1NJWGN0KCIyMzo1MCIsIGZvcm1hdCA9ICIlSDolTSIpLCANCiAgICAgICAgICAgICBieSA9ICIxMCBtaW4iKSwgDQogICIlSDolTSINCikNCg0KIyBIaXR1bmcgcmF0YS1yYXRhIHBlciAxMCBtZW5pdCB1bnR1ayBzZXRpYXAgdGlwZSBoYXJpDQphdmdfcGVyX3RpcGVfaGFyaSA8LSBtZXJnZWRfbGlicmFyeSAlPiUNCiAgZ3JvdXBfYnkoVGlwZUhhcmksIHRpbWVfb2ZfZGF5KSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIGBPa3VwYW5zaSBSYXRhLXJhdGFgID0gbWVhbihBc3NvY2lhdGVkQ2xpZW50Q291bnRfbWVhbiwgbmEucm0gPSBUUlVFKSwNCiAgICBgS29uc3Vtc2kgRW5lcmdpIFJhdGEtcmF0YWAgPSBtZWFuKHRvdGFsX3JhdGUsIG5hLnJtID0gVFJVRSksDQogICAgLmdyb3VwcyA9ICdkcm9wJw0KICApICU+JQ0KICBtdXRhdGUodGltZV9vZl9kYXkgPSBmYWN0b3IodGltZV9vZl9kYXksIGxldmVscyA9IHRpbWVfb3JkZXIpKQ0KDQojIFRlbnR1a2FuIGplZGEgbGFiZWwgdW50dWsgc3VtYnUteCB5YW5nIGFrYW4gZGlndW5ha2FuIGRpIGtlZHVhIHBsb3QNCnRpbWVfYnJlYWtzIDwtIHRpbWVfb3JkZXJbc2VxKDEsIGxlbmd0aCh0aW1lX29yZGVyKSwgYnkgPSA2KV0NCg0KIyBPY2N1cGFuY3kNCmdncGxvdChhdmdfcGVyX3RpcGVfaGFyaSwgYWVzKHggPSB0aW1lX29mX2RheSwgeSA9IGBPa3VwYW5zaSBSYXRhLXJhdGFgLCBjb2xvciA9IFRpcGVIYXJpLCBncm91cCA9IFRpcGVIYXJpKSkgKw0KICBnZW9tX2xpbmUobGluZXdpZHRoID0gMSkgKw0KICBzY2FsZV94X2Rpc2NyZXRlKGJyZWFrcyA9IHRpbWVfYnJlYWtzKSArDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDEiKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiQXZlcmFnZSBPY2N1cGFuY3kgV2Vla2RheSB2cy4gV2Vla2VuZCIsDQogICAgeCA9ICJUaW1lIiwNCiAgICB5ID0gIkF2ZXJhZ2UgT2NjdXBhbmN5IiwNCiAgICBjb2xvciA9ICJEYXkgVHlwZSINCiAgKSArDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KDQojRW5lcmd5IENvbnN1bXB0aW9uDQpnZ3Bsb3QoYXZnX3Blcl90aXBlX2hhcmksIGFlcyh4ID0gdGltZV9vZl9kYXksIHkgPSBgS29uc3Vtc2kgRW5lcmdpIFJhdGEtcmF0YWAsIGNvbG9yID0gVGlwZUhhcmksIGdyb3VwID0gVGlwZUhhcmkpKSArDQogIGdlb21fbGluZShsaW5ld2lkdGggPSAxKSArDQogIHNjYWxlX3hfZGlzY3JldGUoYnJlYWtzID0gdGltZV9icmVha3MpICsNCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiQXZlcmFnZSBFbmVyZ3kgQ29uc3VtcHRpb24gV2Vla2RheSBWcy4gV2Vla2VuZCIsDQogICAgeCA9ICJUaW1lIiwNCiAgICB5ID0gIkF2ZXJhZ2UgRW5lcmd5IENvbnN1bXB0aW9uIiwNCiAgICBjb2xvciA9ICJEYXkgVHlwZSINCiAgKSArDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KYGBgDQoNCg==