library(data.table)
library(dplyr)
library(lubridate)
library(ggplot2)
library(tidyr)
library(patchwork)
df_wifi <- fread("C:/Users/Noval/Documents/Kuliah/SEMESTER 5/Data Mining dan Visualisasi/wifi.csv")
df_energy1 <- fread("C:/Users/Noval/Documents/Kuliah/SEMESTER 5/Data Mining dan Visualisasi/library1.csv")
df_energy2 <- fread("C:/Users/Noval/Documents/Kuliah/SEMESTER 5/Data Mining dan Visualisasi/library2.csv")
df_energy3 <- fread("C:/Users/Noval/Documents/Kuliah/SEMESTER 5/Data Mining dan Visualisasi/library3.csv")
subset_cols <- c("time", "Building", "Floor", "Associated Client Count")
cat("Jumlah baris awal di df_wifi:", nrow(df_wifi), "\n")
df_wifi_cleaned <- df_wifi %>%
distinct(across(all_of(subset_cols)), .keep_all = TRUE)
cat("Jumlah baris di df_wifi setelah duplikat sempurna dihapus:", nrow(df_wifi_cleaned), "\n")
df_wifi_library <- df_wifi_cleaned %>%
filter(tolower(trimws(Building)) == "library")
df_wifi_library$time <- ymd_hms(df_wifi_library$time, quiet = TRUE)
df_energy1$ts <- ymd_hms(df_energy1$ts, quiet = TRUE)
df_energy2$ts <- ymd_hms(df_energy2$ts, quiet = TRUE)
df_energy3$ts <- ymd_hms(df_energy3$ts, quiet = TRUE)
df_energy1 <- df_energy1 %>% distinct(ts, .keep_all = TRUE)
df_energy2 <- df_energy2 %>% distinct(ts, .keep_all = TRUE)
df_energy3 <- df_energy3 %>% distinct(ts, .keep_all = TRUE)
fill_missing_energy <- function(df) {
mean_first_144 <- mean(head(df$rate, 144), na.rm=TRUE)
df$rate[is.na(df$rate)] <- mean_first_144
return(df)
}
df_energy1 <- fill_missing_energy(df_energy1)
df_energy2 <- fill_missing_energy(df_energy2)
df_energy3 <- fill_missing_energy(df_energy3)
all_rates <- data.frame(
rate1 = df_energy1$rate,
rate2 = df_energy2$rate,
rate3 = df_energy3$rate
)
total_rate <- rowSums(all_rates, na.rm=TRUE)
df_energy_total <- data.frame(ts = df_energy1$ts, total_rate = total_rate)
df_wifi_library <- df_wifi_library %>%
arrange(time) %>%
mutate(time_10min = floor_date(time, unit = "10 minutes"))
df_wifi_resampled <- df_wifi_library %>%
group_by(time_10min) %>%
summarise(occupancy = mean(`Associated Client Count`, na.rm=TRUE)) %>%
ungroup()
# Gabungan data WiFi dan energi berdasarkan waktu 10 menit
df_final <- inner_join(df_wifi_resampled, df_energy_total, by = c("time_10min" = "ts"))
# Renamea kolom waktu
colnames(df_final)[1] <- "time"
library(scales)
p1 <- ggplot(df_final, aes(x = time)) +
geom_line(aes(y = occupancy, color = "Okupansi (WiFi)")) +
geom_line(aes(y = total_rate, color = "Konsumsi Energi")) +
scale_y_continuous(
name = "Rata-rata Jumlah Pengguna WiFi",
sec.axis = sec_axis(~ ., name = "Total Konsumsi Energi (rate)")
) +
scale_color_manual(values = c("Okupansi (WiFi)" = "blue", "Konsumsi Energi" = "red")) +
labs(title = "Tren Okupansi Perpustakaan vs. Konsumsi Energi", x = "Tanggal", color = "") +
theme_minimal()
ggsave("time_series_plot.png", p1, width=15, height=7)
print(p1)
p2 <- ggplot(df_final, aes(x = occupancy, y = total_rate)) +
geom_point(alpha = 0.3) +
geom_smooth(method = "lm", color = "red") +
labs(title = "Hubungan Korelasi antara Okupansi dan Konsumsi Energi",
x = "Okupansi (Rata-rata Pengguna WiFi)",
y = "Total Konsumsi Energi (rate)") +
theme_minimal()
ggsave("scatter_plot.png", p2, width=10, height=6)
print(p2)
df_final$hour <- hour(df_final$time)
daily_profile <- df_final %>%
group_by(hour) %>%
summarise(
occupancy = mean(occupancy, na.rm=TRUE),
total_rate = mean(total_rate, na.rm=TRUE)
)
p3 <- ggplot(daily_profile, aes(x = hour)) +
geom_line(aes(y = occupancy, color = "Okupansi (WiFi)"), size=1) +
geom_point(aes(y = occupancy, color = "Okupansi (WiFi)"), size=2) +
geom_line(aes(y = total_rate, color = "Konsumsi Energi"), size=1, linetype="dashed") +
geom_point(aes(y = total_rate, color = "Konsumsi Energi"), shape=4, size=2) +
scale_x_continuous(breaks = 0:23) +
scale_color_manual(values = c("Okupansi (WiFi)" = "blue", "Konsumsi Energi" = "red")) +
labs(title = "Pola Harian Rata-rata: Okupansi vs. Konsumsi Energi",
x = "Jam dalam Sehari (0-23)",
y = NULL,
color = "") +
theme_minimal()
ggsave("daily_profile_plot.png", p3, width=12, height=6)
print(p3)
peak_hour <- daily_profile$hour[which.max(daily_profile$occupancy)]
cat(sprintf("Jam tersibuk di perpustakaan (puncak okupansi) rata-rata terjadi pada jam: %02d:00\n", peak_hour))
correlation <- cor(df_final$occupancy, df_final$total_rate, use = "complete.obs")
cat(sprintf("Koefisien korelasi antara okupansi dan konsumsi energi adalah: %.4f\n", correlation))
df_final$day_of_week <- (wday(df_final$time) + 5) %% 7
df_final$day_type <- ifelse(df_final$day_of_week < 5, "Weekday", "Weekend")
df_weekday <- filter(df_final, day_type == "Weekday")
df_weekend <- filter(df_final, day_type == "Weekend")
("Data berhasil dipisahkan.\n")
cat("Jumlah data hari kerja (Weekday):", nrow(df_weekday), "baris\n")
cat("Jumlah data akhir pekan (Weekend):", nrow(df_weekend), "baris\n")
# Scatter plot hari kerja
p_weekday <- ggplot(df_weekday, aes(x = occupancy, y = total_rate)) +
geom_point(alpha = 0.3) +
geom_smooth(method = "lm", color = "green") +
labs(title = "Hubungan Okupansi vs. Energi (Hari Kerja)",
x = "Okupansi (Pengguna WiFi)",
y = "Total Konsumsi Energi") +
theme_minimal()
# Scatter plot akhir pekan
p_weekend <- ggplot(df_weekend, aes(x = occupancy, y = total_rate)) +
geom_point(alpha = 0.3) +
geom_smooth(method = "lm", color = "orange") +
labs(title = "Hubungan Okupansi vs. Energi (Akhir Pekan)",
x = "Okupansi (Pengguna WiFi)",
y = NULL) +
theme_minimal()
combined_plot <- p_weekday | p_weekend + plot_annotation(title = "Perbandingan Korelasi: Hari Kerja vs. Akhir Pekan")
print(combined_plot)
ggsave("scatter_comparison.png", combined_plot, width=16, height=6)
# Profil harian rata-rata per day_type
weekday_profile <- df_weekday %>%
group_by(hour = hour(time)) %>%
summarise(
occupancy = mean(occupancy, na.rm=TRUE),
total_rate = mean(total_rate, na.rm=TRUE)
)
weekend_profile <- df_weekend %>%
group_by(hour = hour(time)) %>%
summarise(
occupancy = mean(occupancy, na.rm=TRUE),
total_rate = mean(total_rate, na.rm=TRUE)
)
profile_combined <- bind_rows(
weekday_profile %>% mutate(day_type = "Hari Kerja"),
weekend_profile %>% mutate(day_type = "Akhir Pekan")
)
p_daily_compare <- ggplot(profile_combined, aes(x = hour)) +
geom_line(aes(y = occupancy, color = day_type, linetype = day_type), size=1) +
geom_point(aes(y = occupancy, color = day_type, shape = day_type), size=2) +
geom_line(aes(y = total_rate, color = day_type, linetype = day_type), size=1, alpha=0.7) +
geom_point(aes(y = total_rate, color = day_type, shape = day_type), size=2, alpha=0.7) +
scale_x_continuous(breaks = seq(0, 23, 2)) +
scale_color_manual(values = c("Hari Kerja" = "blue", "Akhir Pekan" = "cyan4")) +
scale_linetype_manual(values = c("Hari Kerja" = "solid", "Akhir Pekan" = "dashed")) +
scale_shape_manual(values = c("Hari Kerja" = 16, "Akhir Pekan" = 17)) +
labs(title = "Perbandingan Pola Harian Rata-rata: Hari Kerja vs. Akhir Pekan",
x = "Jam dalam Sehari (0-23)",
y = NULL,
color = "Tipe Hari",
linetype = "Tipe Hari",
shape = "Tipe Hari") +
theme_minimal() +
theme(legend.position = "top")
print(p_daily_compare)
ggsave("daily_profile_comparison.png", p_daily_compare, width=14, height=7)
LS0tDQp0aXRsZTogIlJlZG8gYXNzaWdubWVudCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KDQpOYW1hICAgOiBOYXVmYWwgTWFoZHkgTmFzaHJ1bGxhaA0KTlJQICAgIDogNTAwMzIzMTIwOQ0KS2VsYXMgIDogREFUQSBNSU5JTkcgREFOIFZJU1VBTElTQVNJIEsNCi0tLQ0KDQpgYGB7cn0NCmxpYnJhcnkoZGF0YS50YWJsZSkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KHBhdGNod29yaykNCmBgYA0KYGBge3J9DQpkZl93aWZpIDwtIGZyZWFkKCJDOi9Vc2Vycy9Ob3ZhbC9Eb2N1bWVudHMvS3VsaWFoL1NFTUVTVEVSIDUvRGF0YSBNaW5pbmcgZGFuIFZpc3VhbGlzYXNpL3dpZmkuY3N2IikNCmRmX2VuZXJneTEgPC0gZnJlYWQoIkM6L1VzZXJzL05vdmFsL0RvY3VtZW50cy9LdWxpYWgvU0VNRVNURVIgNS9EYXRhIE1pbmluZyBkYW4gVmlzdWFsaXNhc2kvbGlicmFyeTEuY3N2IikNCmRmX2VuZXJneTIgPC0gZnJlYWQoIkM6L1VzZXJzL05vdmFsL0RvY3VtZW50cy9LdWxpYWgvU0VNRVNURVIgNS9EYXRhIE1pbmluZyBkYW4gVmlzdWFsaXNhc2kvbGlicmFyeTIuY3N2IikNCmRmX2VuZXJneTMgPC0gZnJlYWQoIkM6L1VzZXJzL05vdmFsL0RvY3VtZW50cy9LdWxpYWgvU0VNRVNURVIgNS9EYXRhIE1pbmluZyBkYW4gVmlzdWFsaXNhc2kvbGlicmFyeTMuY3N2IikNCmBgYA0KYGBge3J9DQpzdWJzZXRfY29scyA8LSBjKCJ0aW1lIiwgIkJ1aWxkaW5nIiwgIkZsb29yIiwgIkFzc29jaWF0ZWQgQ2xpZW50IENvdW50IikNCmNhdCgiSnVtbGFoIGJhcmlzIGF3YWwgZGkgZGZfd2lmaToiLCBucm93KGRmX3dpZmkpLCAiXG4iKQ0KYGBgDQpgYGB7cn0NCmRmX3dpZmlfY2xlYW5lZCA8LSBkZl93aWZpICU+JQ0KICBkaXN0aW5jdChhY3Jvc3MoYWxsX29mKHN1YnNldF9jb2xzKSksIC5rZWVwX2FsbCA9IFRSVUUpDQoNCmNhdCgiSnVtbGFoIGJhcmlzIGRpIGRmX3dpZmkgc2V0ZWxhaCBkdXBsaWthdCBzZW1wdXJuYSBkaWhhcHVzOiIsIG5yb3coZGZfd2lmaV9jbGVhbmVkKSwgIlxuIikNCmBgYA0KYGBge3J9DQpkZl93aWZpX2xpYnJhcnkgPC0gZGZfd2lmaV9jbGVhbmVkICU+JQ0KICBmaWx0ZXIodG9sb3dlcih0cmltd3MoQnVpbGRpbmcpKSA9PSAibGlicmFyeSIpDQoNCg0KZGZfd2lmaV9saWJyYXJ5JHRpbWUgPC0geW1kX2htcyhkZl93aWZpX2xpYnJhcnkkdGltZSwgcXVpZXQgPSBUUlVFKQ0KZGZfZW5lcmd5MSR0cyA8LSB5bWRfaG1zKGRmX2VuZXJneTEkdHMsIHF1aWV0ID0gVFJVRSkNCmRmX2VuZXJneTIkdHMgPC0geW1kX2htcyhkZl9lbmVyZ3kyJHRzLCBxdWlldCA9IFRSVUUpDQpkZl9lbmVyZ3kzJHRzIDwtIHltZF9obXMoZGZfZW5lcmd5MyR0cywgcXVpZXQgPSBUUlVFKQ0KDQoNCmRmX2VuZXJneTEgPC0gZGZfZW5lcmd5MSAlPiUgZGlzdGluY3QodHMsIC5rZWVwX2FsbCA9IFRSVUUpDQpkZl9lbmVyZ3kyIDwtIGRmX2VuZXJneTIgJT4lIGRpc3RpbmN0KHRzLCAua2VlcF9hbGwgPSBUUlVFKQ0KZGZfZW5lcmd5MyA8LSBkZl9lbmVyZ3kzICU+JSBkaXN0aW5jdCh0cywgLmtlZXBfYWxsID0gVFJVRSkNCmBgYA0KYGBge3J9DQpmaWxsX21pc3NpbmdfZW5lcmd5IDwtIGZ1bmN0aW9uKGRmKSB7DQogIG1lYW5fZmlyc3RfMTQ0IDwtIG1lYW4oaGVhZChkZiRyYXRlLCAxNDQpLCBuYS5ybT1UUlVFKQ0KICBkZiRyYXRlW2lzLm5hKGRmJHJhdGUpXSA8LSBtZWFuX2ZpcnN0XzE0NA0KICByZXR1cm4oZGYpDQp9DQoNCmRmX2VuZXJneTEgPC0gZmlsbF9taXNzaW5nX2VuZXJneShkZl9lbmVyZ3kxKQ0KZGZfZW5lcmd5MiA8LSBmaWxsX21pc3NpbmdfZW5lcmd5KGRmX2VuZXJneTIpDQpkZl9lbmVyZ3kzIDwtIGZpbGxfbWlzc2luZ19lbmVyZ3koZGZfZW5lcmd5MykNCg0KYWxsX3JhdGVzIDwtIGRhdGEuZnJhbWUoDQogIHJhdGUxID0gZGZfZW5lcmd5MSRyYXRlLA0KICByYXRlMiA9IGRmX2VuZXJneTIkcmF0ZSwNCiAgcmF0ZTMgPSBkZl9lbmVyZ3kzJHJhdGUNCikNCg0KdG90YWxfcmF0ZSA8LSByb3dTdW1zKGFsbF9yYXRlcywgbmEucm09VFJVRSkNCmRmX2VuZXJneV90b3RhbCA8LSBkYXRhLmZyYW1lKHRzID0gZGZfZW5lcmd5MSR0cywgdG90YWxfcmF0ZSA9IHRvdGFsX3JhdGUpDQpgYGANCmBgYHtyfQ0KZGZfd2lmaV9saWJyYXJ5IDwtIGRmX3dpZmlfbGlicmFyeSAlPiUNCiAgYXJyYW5nZSh0aW1lKSAlPiUNCiAgbXV0YXRlKHRpbWVfMTBtaW4gPSBmbG9vcl9kYXRlKHRpbWUsIHVuaXQgPSAiMTAgbWludXRlcyIpKQ0KDQpkZl93aWZpX3Jlc2FtcGxlZCA8LSBkZl93aWZpX2xpYnJhcnkgJT4lDQogIGdyb3VwX2J5KHRpbWVfMTBtaW4pICU+JQ0KICBzdW1tYXJpc2Uob2NjdXBhbmN5ID0gbWVhbihgQXNzb2NpYXRlZCBDbGllbnQgQ291bnRgLCBuYS5ybT1UUlVFKSkgJT4lDQogIHVuZ3JvdXAoKQ0KDQojIEdhYnVuZ2FuIGRhdGEgV2lGaSBkYW4gZW5lcmdpIGJlcmRhc2Fya2FuIHdha3R1IDEwIG1lbml0DQpkZl9maW5hbCA8LSBpbm5lcl9qb2luKGRmX3dpZmlfcmVzYW1wbGVkLCBkZl9lbmVyZ3lfdG90YWwsIGJ5ID0gYygidGltZV8xMG1pbiIgPSAidHMiKSkNCg0KIyBSZW5hbWVhIGtvbG9tIHdha3R1DQpjb2xuYW1lcyhkZl9maW5hbClbMV0gPC0gInRpbWUiDQpgYGANCmBgYHtyfQ0KbGlicmFyeShzY2FsZXMpDQoNCnAxIDwtIGdncGxvdChkZl9maW5hbCwgYWVzKHggPSB0aW1lKSkgKw0KICBnZW9tX2xpbmUoYWVzKHkgPSBvY2N1cGFuY3ksIGNvbG9yID0gIk9rdXBhbnNpIChXaUZpKSIpKSArDQogIGdlb21fbGluZShhZXMoeSA9IHRvdGFsX3JhdGUsIGNvbG9yID0gIktvbnN1bXNpIEVuZXJnaSIpKSArDQogIHNjYWxlX3lfY29udGludW91cygNCiAgICBuYW1lID0gIlJhdGEtcmF0YSBKdW1sYWggUGVuZ2d1bmEgV2lGaSIsDQogICAgc2VjLmF4aXMgPSBzZWNfYXhpcyh+IC4sIG5hbWUgPSAiVG90YWwgS29uc3Vtc2kgRW5lcmdpIChyYXRlKSIpDQogICkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiT2t1cGFuc2kgKFdpRmkpIiA9ICJibHVlIiwgIktvbnN1bXNpIEVuZXJnaSIgPSAicmVkIikpICsNCiAgbGFicyh0aXRsZSA9ICJUcmVuIE9rdXBhbnNpIFBlcnB1c3Rha2FhbiB2cy4gS29uc3Vtc2kgRW5lcmdpIiwgeCA9ICJUYW5nZ2FsIiwgY29sb3IgPSAiIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KZ2dzYXZlKCJ0aW1lX3Nlcmllc19wbG90LnBuZyIsIHAxLCB3aWR0aD0xNSwgaGVpZ2h0PTcpDQpwcmludChwMSkNCmBgYA0KYGBge3J9DQpwMiA8LSBnZ3Bsb3QoZGZfZmluYWwsIGFlcyh4ID0gb2NjdXBhbmN5LCB5ID0gdG90YWxfcmF0ZSkpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMykgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBjb2xvciA9ICJyZWQiKSArDQogIGxhYnModGl0bGUgPSAiSHVidW5nYW4gS29yZWxhc2kgYW50YXJhIE9rdXBhbnNpIGRhbiBLb25zdW1zaSBFbmVyZ2kiLA0KICAgICAgIHggPSAiT2t1cGFuc2kgKFJhdGEtcmF0YSBQZW5nZ3VuYSBXaUZpKSIsDQogICAgICAgeSA9ICJUb3RhbCBLb25zdW1zaSBFbmVyZ2kgKHJhdGUpIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KZ2dzYXZlKCJzY2F0dGVyX3Bsb3QucG5nIiwgcDIsIHdpZHRoPTEwLCBoZWlnaHQ9NikNCnByaW50KHAyKQ0KYGBgDQpgYGB7cn0NCmRmX2ZpbmFsJGhvdXIgPC0gaG91cihkZl9maW5hbCR0aW1lKQ0KDQpkYWlseV9wcm9maWxlIDwtIGRmX2ZpbmFsICU+JQ0KICBncm91cF9ieShob3VyKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIG9jY3VwYW5jeSA9IG1lYW4ob2NjdXBhbmN5LCBuYS5ybT1UUlVFKSwNCiAgICB0b3RhbF9yYXRlID0gbWVhbih0b3RhbF9yYXRlLCBuYS5ybT1UUlVFKQ0KICApDQoNCnAzIDwtIGdncGxvdChkYWlseV9wcm9maWxlLCBhZXMoeCA9IGhvdXIpKSArDQogIGdlb21fbGluZShhZXMoeSA9IG9jY3VwYW5jeSwgY29sb3IgPSAiT2t1cGFuc2kgKFdpRmkpIiksIHNpemU9MSkgKw0KICBnZW9tX3BvaW50KGFlcyh5ID0gb2NjdXBhbmN5LCBjb2xvciA9ICJPa3VwYW5zaSAoV2lGaSkiKSwgc2l6ZT0yKSArDQogIGdlb21fbGluZShhZXMoeSA9IHRvdGFsX3JhdGUsIGNvbG9yID0gIktvbnN1bXNpIEVuZXJnaSIpLCBzaXplPTEsIGxpbmV0eXBlPSJkYXNoZWQiKSArDQogIGdlb21fcG9pbnQoYWVzKHkgPSB0b3RhbF9yYXRlLCBjb2xvciA9ICJLb25zdW1zaSBFbmVyZ2kiKSwgc2hhcGU9NCwgc2l6ZT0yKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAwOjIzKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJPa3VwYW5zaSAoV2lGaSkiID0gImJsdWUiLCAiS29uc3Vtc2kgRW5lcmdpIiA9ICJyZWQiKSkgKw0KICBsYWJzKHRpdGxlID0gIlBvbGEgSGFyaWFuIFJhdGEtcmF0YTogT2t1cGFuc2kgdnMuIEtvbnN1bXNpIEVuZXJnaSIsDQogICAgICAgeCA9ICJKYW0gZGFsYW0gU2VoYXJpICgwLTIzKSIsDQogICAgICAgeSA9IE5VTEwsDQogICAgICAgY29sb3IgPSAiIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KZ2dzYXZlKCJkYWlseV9wcm9maWxlX3Bsb3QucG5nIiwgcDMsIHdpZHRoPTEyLCBoZWlnaHQ9NikNCnByaW50KHAzKQ0KYGBgDQoNCmBgYHtyfQ0KcGVha19ob3VyIDwtIGRhaWx5X3Byb2ZpbGUkaG91clt3aGljaC5tYXgoZGFpbHlfcHJvZmlsZSRvY2N1cGFuY3kpXQ0KY2F0KHNwcmludGYoIkphbSB0ZXJzaWJ1ayBkaSBwZXJwdXN0YWthYW4gKHB1bmNhayBva3VwYW5zaSkgcmF0YS1yYXRhIHRlcmphZGkgcGFkYSBqYW06ICUwMmQ6MDBcbiIsIHBlYWtfaG91cikpDQpgYGANCmBgYHtyfQ0KY29ycmVsYXRpb24gPC0gY29yKGRmX2ZpbmFsJG9jY3VwYW5jeSwgZGZfZmluYWwkdG90YWxfcmF0ZSwgdXNlID0gImNvbXBsZXRlLm9icyIpDQpjYXQoc3ByaW50ZigiS29lZmlzaWVuIGtvcmVsYXNpIGFudGFyYSBva3VwYW5zaSBkYW4ga29uc3Vtc2kgZW5lcmdpIGFkYWxhaDogJS40ZlxuIiwgY29ycmVsYXRpb24pKQ0KYGBgDQpgYGB7cn0NCmRmX2ZpbmFsJGRheV9vZl93ZWVrIDwtICh3ZGF5KGRmX2ZpbmFsJHRpbWUpICsgNSkgJSUgNw0KZGZfZmluYWwkZGF5X3R5cGUgPC0gaWZlbHNlKGRmX2ZpbmFsJGRheV9vZl93ZWVrIDwgNSwgIldlZWtkYXkiLCAiV2Vla2VuZCIpDQoNCmRmX3dlZWtkYXkgPC0gZmlsdGVyKGRmX2ZpbmFsLCBkYXlfdHlwZSA9PSAiV2Vla2RheSIpDQpkZl93ZWVrZW5kIDwtIGZpbHRlcihkZl9maW5hbCwgZGF5X3R5cGUgPT0gIldlZWtlbmQiKQ0KYGBgDQpgYGB7cn0NCigiRGF0YSBiZXJoYXNpbCBkaXBpc2Foa2FuLlxuIikNCmNhdCgiSnVtbGFoIGRhdGEgaGFyaSBrZXJqYSAoV2Vla2RheSk6IiwgbnJvdyhkZl93ZWVrZGF5KSwgImJhcmlzXG4iKQ0KY2F0KCJKdW1sYWggZGF0YSBha2hpciBwZWthbiAoV2Vla2VuZCk6IiwgbnJvdyhkZl93ZWVrZW5kKSwgImJhcmlzXG4iKQ0KYGBgDQpgYGB7cn0NCiMgU2NhdHRlciBwbG90IGhhcmkga2VyamENCnBfd2Vla2RheSA8LSBnZ3Bsb3QoZGZfd2Vla2RheSwgYWVzKHggPSBvY2N1cGFuY3ksIHkgPSB0b3RhbF9yYXRlKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMC4zKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbG9yID0gImdyZWVuIikgKw0KICBsYWJzKHRpdGxlID0gIkh1YnVuZ2FuIE9rdXBhbnNpIHZzLiBFbmVyZ2kgKEhhcmkgS2VyamEpIiwNCiAgICAgICB4ID0gIk9rdXBhbnNpIChQZW5nZ3VuYSBXaUZpKSIsDQogICAgICAgeSA9ICJUb3RhbCBLb25zdW1zaSBFbmVyZ2kiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQojIFNjYXR0ZXIgcGxvdCBha2hpciBwZWthbg0KcF93ZWVrZW5kIDwtIGdncGxvdChkZl93ZWVrZW5kLCBhZXMoeCA9IG9jY3VwYW5jeSwgeSA9IHRvdGFsX3JhdGUpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjMpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3IgPSAib3JhbmdlIikgKw0KICBsYWJzKHRpdGxlID0gIkh1YnVuZ2FuIE9rdXBhbnNpIHZzLiBFbmVyZ2kgKEFraGlyIFBla2FuKSIsDQogICAgICAgeCA9ICJPa3VwYW5zaSAoUGVuZ2d1bmEgV2lGaSkiLA0KICAgICAgIHkgPSBOVUxMKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpjb21iaW5lZF9wbG90IDwtIHBfd2Vla2RheSB8IHBfd2Vla2VuZCArIHBsb3RfYW5ub3RhdGlvbih0aXRsZSA9ICJQZXJiYW5kaW5nYW4gS29yZWxhc2k6IEhhcmkgS2VyamEgdnMuIEFraGlyIFBla2FuIikNCnByaW50KGNvbWJpbmVkX3Bsb3QpDQpnZ3NhdmUoInNjYXR0ZXJfY29tcGFyaXNvbi5wbmciLCBjb21iaW5lZF9wbG90LCB3aWR0aD0xNiwgaGVpZ2h0PTYpDQoNCiMgUHJvZmlsIGhhcmlhbiByYXRhLXJhdGEgcGVyIGRheV90eXBlDQp3ZWVrZGF5X3Byb2ZpbGUgPC0gZGZfd2Vla2RheSAlPiUNCiAgZ3JvdXBfYnkoaG91ciA9IGhvdXIodGltZSkpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgb2NjdXBhbmN5ID0gbWVhbihvY2N1cGFuY3ksIG5hLnJtPVRSVUUpLA0KICAgIHRvdGFsX3JhdGUgPSBtZWFuKHRvdGFsX3JhdGUsIG5hLnJtPVRSVUUpDQogICkNCg0Kd2Vla2VuZF9wcm9maWxlIDwtIGRmX3dlZWtlbmQgJT4lDQogIGdyb3VwX2J5KGhvdXIgPSBob3VyKHRpbWUpKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIG9jY3VwYW5jeSA9IG1lYW4ob2NjdXBhbmN5LCBuYS5ybT1UUlVFKSwNCiAgICB0b3RhbF9yYXRlID0gbWVhbih0b3RhbF9yYXRlLCBuYS5ybT1UUlVFKQ0KICApDQoNCnByb2ZpbGVfY29tYmluZWQgPC0gYmluZF9yb3dzKA0KICB3ZWVrZGF5X3Byb2ZpbGUgJT4lIG11dGF0ZShkYXlfdHlwZSA9ICJIYXJpIEtlcmphIiksDQogIHdlZWtlbmRfcHJvZmlsZSAlPiUgbXV0YXRlKGRheV90eXBlID0gIkFraGlyIFBla2FuIikNCikNCg0KcF9kYWlseV9jb21wYXJlIDwtIGdncGxvdChwcm9maWxlX2NvbWJpbmVkLCBhZXMoeCA9IGhvdXIpKSArDQogIGdlb21fbGluZShhZXMoeSA9IG9jY3VwYW5jeSwgY29sb3IgPSBkYXlfdHlwZSwgbGluZXR5cGUgPSBkYXlfdHlwZSksIHNpemU9MSkgKw0KICBnZW9tX3BvaW50KGFlcyh5ID0gb2NjdXBhbmN5LCBjb2xvciA9IGRheV90eXBlLCBzaGFwZSA9IGRheV90eXBlKSwgc2l6ZT0yKSArDQogIGdlb21fbGluZShhZXMoeSA9IHRvdGFsX3JhdGUsIGNvbG9yID0gZGF5X3R5cGUsIGxpbmV0eXBlID0gZGF5X3R5cGUpLCBzaXplPTEsIGFscGhhPTAuNykgKw0KICBnZW9tX3BvaW50KGFlcyh5ID0gdG90YWxfcmF0ZSwgY29sb3IgPSBkYXlfdHlwZSwgc2hhcGUgPSBkYXlfdHlwZSksIHNpemU9MiwgYWxwaGE9MC43KSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMjMsIDIpKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJIYXJpIEtlcmphIiA9ICJibHVlIiwgIkFraGlyIFBla2FuIiA9ICJjeWFuNCIpKSArDQogIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXMgPSBjKCJIYXJpIEtlcmphIiA9ICJzb2xpZCIsICJBa2hpciBQZWthbiIgPSAiZGFzaGVkIikpICsNCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoIkhhcmkgS2VyamEiID0gMTYsICJBa2hpciBQZWthbiIgPSAxNykpICsNCiAgbGFicyh0aXRsZSA9ICJQZXJiYW5kaW5nYW4gUG9sYSBIYXJpYW4gUmF0YS1yYXRhOiBIYXJpIEtlcmphIHZzLiBBa2hpciBQZWthbiIsDQogICAgICAgeCA9ICJKYW0gZGFsYW0gU2VoYXJpICgwLTIzKSIsDQogICAgICAgeSA9IE5VTEwsDQogICAgICAgY29sb3IgPSAiVGlwZSBIYXJpIiwNCiAgICAgICBsaW5ldHlwZSA9ICJUaXBlIEhhcmkiLA0KICAgICAgIHNoYXBlID0gIlRpcGUgSGFyaSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpDQoNCnByaW50KHBfZGFpbHlfY29tcGFyZSkNCmdnc2F2ZSgiZGFpbHlfcHJvZmlsZV9jb21wYXJpc29uLnBuZyIsIHBfZGFpbHlfY29tcGFyZSwgd2lkdGg9MTQsIGhlaWdodD03KQ0KYGBgDQo=