1 Load các thư viện cần thiết

library(DT)           # Để hiển thị bảng dữ liệu
library(triangle)     # Để tạo phân phối tam giác
library(lubridate)    # Để xử lý ngày tháng
library(dplyr)        # Để sử dụng hàm case_when
library(knitr)        # Để tạo bảng
library(ggplot2)      # Để vẽ biểu đồ
library(patchwork)    # Để nối 2 biểu đồ
library(ggpubr)       # Cho các biểu đồ thống kê nâng cao
library(tidyverse)    # Tập hợp các package quan trọng cho data science
library(skimr)        # Xem tổng quan dữ liệu với thống kê mô tả
library(gt)           # Tạo bảng biểu đẹp và chuyên nghiệp
library(RColorBrewer) # Cung cấp các palette màu để vẽ biểu đồ đẹp
library(openxlsx)     # Xuất dữ liệu ra file exel

2 Biểu đồ các phân phối

2.1 Phân phối đều

# Tạo dữ liệu từ phân phối đều trong khoảng [5, 10]
ppDeu <- runif(1000, min = 5, max = 10)

# Vẽ biểu đồ histogram với trục Y là phần trăm
hist(ppDeu,
     breaks = 20,
     col = "lightgreen",
     freq = FALSE,  # biểu đồ theo mật độ
     main = "Phân phối Đều (Uniform)",
     xlab = "Giá trị",
     ylab = "Tần suất (%)")

# Chuyển trục Y từ mật độ sang phần trăm bằng cách nhân với 100
# (vì mật độ có tổng là 1, nên nhân 100 để thành phần trăm)
lines(density(ppDeu), col = "darkgreen", lwd = 2)

2.2 Phân phối chuẩn

# Tạo dữ liệu từ phân phối chuẩn (mean = 0, sd = 1)
ppChuan <- rnorm(1000, mean = 0, sd = 1)

# Vẽ biểu đồ histogram với trục Y là phần trăm
hist(ppChuan,
     breaks = 20,
     col = "skyblue",
     freq = FALSE,  # biểu đồ theo mật độ
     main = "Phân phối Chuẩn (Normal)",
     xlab = "Giá trị",
     ylab = "Tần suất (%)")

# Vẽ đường mật độ và nhân với 100 để chuyển sang phần trăm trực quan
lines(density(ppChuan), col = "blue", lwd = 2)

2.3 Phân phối Tam giác

# Tạo dữ liệu từ phân phối Tam giác (giá trị min = 0, mode = 5, max = 10)
ppTamGiac <- rtriangle(1000, a = 0, b = 10, c = 5)

# Vẽ biểu đồ histogram với trục Y là phần trăm
hist(ppTamGiac,
     breaks = 20,
     col = "lightblue",
     freq = FALSE,  # để biểu đồ chuẩn hóa
     main = "Phân phối Tam giác",
     xlab = "Giá trị",
     ylab = "Tần suất (%)")

# Chuyển tần suất sang phần trăm
lines(density(ppTamGiac), col = "blue", lwd = 2)

2.4 Phân phối rời rạc

ppRoiRac <- sample(c(1, 2, 3), size = 1000, replace = TRUE, prob = c(0.2, 0.5, 0.3))

table_pct <- prop.table(table(ppRoiRac)) * 100

barplot(table_pct,
        col = "salmon",
        main = "Phân phối Rời rạc",
        ylab = "Tần suất (%)",
        xlab = "Giá trị",
        ylim = c(0, max(table_pct) + 5))

3 Mô phỏng dữ liệu đơn hàng

# Khởi tạo seed để kết quả có thể tái lập
set.seed(123)

# 1. Tạo dữ liệu ngày trong năm 2025
start_date <- as.Date("2025-01-01")
end_date <- as.Date("2025-12-31")
dates <- seq.Date(start_date, end_date, by = "day")

# 2. Tạo mã đơn hàng theo tháng
generate_order_ids <- function(year, month) {
  days_in_month <- days_in_month(as.Date(paste(year, month, "01", sep = "-")))
  orders_per_day <- round(rtriangle(n = days_in_month, a = 10, b = 20, c = 15))
  
  order_ids <- character(0)
  for (day in 1:days_in_month) {
    day_orders <- sprintf("%s%02d%02d%03d", 
                          substr(year, 3, 4), 
                          month, 
                          day, 
                          1:orders_per_day[day])
    order_ids <- c(order_ids, day_orders)
  }
  return(order_ids)
}

# Tạo tất cả mã đơn hàng cho năm 2025
all_order_ids <- unlist(lapply(1:12, function(month) generate_order_ids(2025, month)))

# 3. Tạo loại sản phẩm theo phân phối rời rạc 
product_types <- sample(c("Decal", "Biểu mẫu", "Catalog"), 
                        size = length(all_order_ids), 
                        replace = TRUE, 
                        prob = c(0.5, 0.3, 0.2))

# Hàm làm tròn số lượng theo quy tắc thực tế
round_to_realistic <- function(x, product_type) {
  if (product_type == "Biểu mẫu") {
    round_to <- c(0)
  } else if (product_type == "Catalog") {
    round_to <- c(100, 500, 1000)
  } else { # Decal
    round_to <- c(100, 500, 1000)
  }
  
  differences <- sapply(round_to, function(r) abs(x - r))
  closest <- round_to[which.min(differences)]
  
  if (min(differences) > 0.2 * x) {
    return(round(x / 100) * 100)
  } else {
    return(closest)
  }
}

# 4. Tạo số lượng khách đặt cho từng loại sản phẩm
generate_product_data <- function(product_type) {
  if (product_type == "Biểu mẫu") {
    pages <- round(runif(1, 80, 100))  # Số trang mỗi cuốn biểu mẫu
    base_quantity <- rnorm(1, mean = 400, sd = 40)  # Số cuốn
    quantity <- round_to_realistic(base_quantity, product_type)
    return(list(quantity = quantity, pages_sheets = pages))
  } else if (product_type == "Catalog") {
    pages <- round(runif(1, 20, 40))  # Số trang mỗi cuốn catalog
    base_quantity <- rnorm(1, mean = 2000, sd = 200)  # Số cuốn
    quantity <- round_to_realistic(base_quantity, product_type)
    return(list(quantity = quantity, pages_sheets = pages))
  } else { # Decal
    sheets <- round(runif(1, 2000, 4000))  # Số tờ decal
    quantity <- round_to_realistic(sheets, product_type)
    return(list(quantity = quantity, pages_sheets = 0))  # Decal không có số trang
  }
}

# Tạo dữ liệu sản phẩm
product_data <- lapply(product_types, generate_product_data)
quantities <- sapply(product_data, function(x) x$quantity)
pages_sheets <- sapply(product_data, function(x) x$pages_sheets)

# 5. Tạo tỷ lệ lỗi 
error_rates <- numeric(length(product_types))

error_rates[product_types == "Biểu mẫu"] <-
  runif(sum(product_types == "Biểu mẫu"), 0.005, 0.01)

error_rates[product_types == "Catalog"] <-
  runif(sum(product_types == "Catalog"), 0.01, 0.02)

error_rates[product_types == "Decal"] <-
  runif(sum(product_types == "Decal"), 0.02, 0.03)

# 6. Tính số lượng lỗi
error_quantities <- round(quantities * error_rates)

# 7. Tạo đơn vị
units <- ifelse(product_types == "Decal", "Tờ", "Cuốn")

# 8. Tạo loại giấy
paper_types <- ifelse(product_types == "Decal", "CPWG-P0031",
                     ifelse(product_types == "Biểu mẫu", "Carbonless",
                            ifelse(runif(length(product_types)) > 0.5, "F100", "C100")))

# 9. Tạo ĐƠN GIÁ BÁN (giá bán cho khách hàng)
selling_prices <- case_when(
  paper_types == "CPWG-P0031" ~ 553,    # Giá bán mỗi tờ decal
  paper_types == "Carbonless" ~ 47,     # Giá bán mỗi trang biểu mẫu
  paper_types == "F100" ~ 30,           # Giá bán mỗi trang catalog
  paper_types == "C100" ~ 31            # Giá bán mỗi trang catalog
)

# 10. Tạo GIÁ VỐN (chi phí sản xuất)
cost_prices <- case_when(
  paper_types == "CPWG-P0031" ~ 388,    # Giá vốn mỗi tờ decal
  paper_types == "Carbonless" ~ 37,      # Giá vốn mỗi trang biểu mẫu
  paper_types == "F100" ~ 20,            # Giá vốn mỗi trang catalog
  paper_types == "C100" ~ 21             # Giá vốn mỗi trang catalog
)

# 11. Tính doanh thu theo từng loại sản phẩm (dùng giá bán)
calculate_revenue <- function(product_type, quantity, pages, selling_price) {
  if (product_type == "Decal") {
    # Decal: Số tờ × giá bán/tờ
    quantity * selling_price
  } else {
    # Biểu mẫu và Catalog: Số cuốn × số trang × giá bán/trang 
    quantity * pages * selling_price 
  }
}

revenue <- mapply(calculate_revenue, 
                 product_types, quantities, pages_sheets, selling_prices)

# 12. Tính chi phí biến động (dùng giá vốn)
calculate_variable_cost <- function(product_type, error_quantity, pages, cost_price) {
  if (product_type == "Decal") {
    # Decal: Số tờ lỗi × giá vốn/tờ
    error_quantity * cost_price
  } else {
    # Biểu mẫu và Catalog: Số cuốn lỗi × số trang × giá vốn/trang
    error_quantity * pages * cost_price
  }
}

variable_costs <- mapply(calculate_variable_cost, 
                        product_types, error_quantities, pages_sheets, cost_prices)

# Tạo data frame tổng hợp
daily_orders <- round(rtriangle(n = length(dates), a = 10, b = 20, c = 15))
data <- data.frame(
  Ngay = rep(dates, times = daily_orders)[1:length(revenue)],
  MaDonHang = all_order_ids[1:length(revenue)],
  LoaiSanPham = product_types[1:length(revenue)],
  SoLuongDatHang = quantities[1:length(revenue)],
  DonVi = units[1:length(revenue)],
  SoTrang = ifelse(product_types[1:length(revenue)] %in% c("Biểu mẫu", "Catalog"), 
                  pages_sheets[1:length(revenue)], 0),
  TyLeLoi = error_rates[1:length(revenue)],
  SoLuongLoi = error_quantities[1:length(revenue)],
  LoaiGiay = paper_types[1:length(revenue)],
  DonGiaBan = selling_prices[1:length(revenue)],
  GiaVon = cost_prices[1:length(revenue)],
  DoanhThu = revenue[1:length(revenue)],
  ChiPhiBienDong = variable_costs[1:length(revenue)]
)

# Sắp xếp lại theo ngày và làm sạch dữ liệu
data <- data[order(data$Ngay), ]
rownames(data) <- NULL

# Tạo bảng tổng hợp doanh thu và chi phí theo ngày
profit_data <- data %>% 
  group_by(Ngay) %>% 
  summarise(
    TongDoanhThu = sum(DoanhThu, na.rm = TRUE),
    TongChiPhiBienDong = sum(ChiPhiBienDong, na.rm = TRUE),
    ChiPhiCoDinh = 15000000,  # 15 triệu đồng/ngày
    .groups = "drop"
  ) %>% 
  mutate(
    LoiNhuan = TongDoanhThu - ChiPhiCoDinh - TongChiPhiBienDong
  )
# Tạo workbook mới
wb <- createWorkbook()

# Thêm sheet đầu tiên: dữ liệu gốc
addWorksheet(wb, "Data")
writeData(wb, sheet = "Data", data)

# Thêm sheet thứ hai: dữ liệu lợi nhuận
addWorksheet(wb, "Profit_Data")
writeData(wb, sheet = "Profit_Data", profit_data)

# Ghi file Excel ra đĩa
saveWorkbook(wb, "VuongNhuThuy2221000329_dulieu.xlsx", overwrite = TRUE)
datatable(data)
datatable(profit_data)

4 Thống kê mô tả các biến

4.1 Tổng quan

skim(data) %>% summary()
Data summary
Name data
Number of rows 5483
Number of columns 13
_______________________
Column type frequency:
character 4
Date 1
numeric 8
________________________
Group variables None

4.2 Thống kê mô tả biến Loại sản phẩm

data %>%
  count(LoaiSanPham) %>%
  mutate(Percent = round(n / sum(n) * 100, 2)) %>%
  kable(caption = "Bảng tần số và tần suất theo Loại sản phẩm")
Bảng tần số và tần suất theo Loại sản phẩm
LoaiSanPham n Percent
Biểu mẫu 1631 29.75
Catalog 1087 19.82
Decal 2765 50.43
# 1. Biểu đồ cột (KHÔNG hiển thị legend)
plot_bar <- ggplot(data, aes(x = LoaiSanPham, fill = LoaiSanPham)) +
  geom_bar() +
  geom_text(stat = 'count', aes(label = after_stat(count)),  position = position_stack(vjust = 0.5)) +
  labs(title = "SỐ LƯỢNG ĐƠN HÀNG",
       x = NULL,
       y = NULL) +
  theme_minimal() +
  scale_fill_brewer(palette = "Set4") +  # Dùng palette chung
  theme(legend.position = "none")  # Ẩn legend

# 2. Biểu đồ tròn (HIỂN THỊ legend)
plot_pie <- ggplot(data, aes(x = "", fill = LoaiSanPham)) +
  geom_bar(width = 0.5) +
  coord_polar("y") +
  geom_text(stat = "count",
            aes(label = scales::percent(after_stat(count)/sum(after_stat(count)))),
            position = position_stack(vjust = 0.5)) +
  labs(title = "TỶ LỆ PHÂN BỐ",
       x = NULL,
       y = NULL) +
  theme_void() +
  scale_fill_brewer(palette = "Set4")  # Dùng CÙNG palette với biểu đồ cột

# Kết hợp - chỉ hiển thị 1 legend
combined_plot <- plot_bar + plot_pie 

print(combined_plot)

4.3 Thống kê mô tả biến Loại giấy

data %>%
  count(LoaiGiay) %>%
  mutate(Percent = round(n / sum(n) * 100, 2)) %>%
  kable(caption = "Bảng tần số và tần suất theo Loại giấy")
Bảng tần số và tần suất theo Loại giấy
LoaiGiay n Percent
C100 522 9.52
CPWG-P0031 2765 50.43
Carbonless 1631 29.75
F100 565 10.30
# 1. Biểu đồ cột (KHÔNG hiển thị legend)
plot_bar <- ggplot(data, aes(x = LoaiGiay, fill = LoaiGiay)) +
  geom_bar() +
  geom_text(stat = 'count', aes(label = after_stat(count)),  position = position_stack(vjust = 0.5)) +
  labs(title = "SỐ LƯỢNG ĐƠN HÀNG",
       x = NULL,
       y = NULL) +
  theme_minimal() +
  scale_fill_brewer(palette = "Set4") +  # Dùng palette chung
  theme(legend.position = "none",  # Ẩn legend
  axis.text.x = element_text(size = 8, angle = 15, hjust = 1))
 
# 2. Biểu đồ tròn (HIỂN THỊ legend)
plot_pie <- ggplot(data, aes(x = "", fill = LoaiGiay)) +
  geom_bar(width = 0.5) +
  coord_polar("y") +
  geom_text(stat = "count",
            aes(label = scales::percent(after_stat(count)/sum(after_stat(count)))),
            position = position_stack(vjust = 0.5), size = 2.5) +
  labs(title = "TỶ LỆ PHÂN BỐ",
       x = NULL,
       y = NULL) +
  theme_void() +
  scale_fill_brewer(palette = "Set4")  # Dùng CÙNG palette với biểu đồ cột

# Kết hợp - chỉ hiển thị 1 legend
combined_plot <- plot_bar + plot_pie 

print(combined_plot)

4.4 Thống kê mô tả biến Loại sản phẩm và Số lượng đặt hàng

stats_table <- data %>%
  group_by(LoaiSanPham) %>%
  summarise(
    Mean = mean(SoLuongDatHang, na.rm = TRUE),
    SD = sd(SoLuongDatHang, na.rm = TRUE),
    Median = median(SoLuongDatHang, na.rm = TRUE),
    Min = min(SoLuongDatHang, na.rm = TRUE),
    Max = max(SoLuongDatHang, na.rm = TRUE)
  ) %>%
  mutate(across(where(is.numeric), ~ round(., 2)))  # Làm tròn 2 chữ số

# Hiển thị bảng
print(stats_table)
## # A tibble: 3 × 6
##   LoaiSanPham  Mean    SD Median   Min   Max
##   <chr>       <dbl> <dbl>  <dbl> <dbl> <dbl>
## 1 Biểu mẫu     398.  45.3    400   300   500
## 2 Catalog     1994. 205.    2000  1300  2600
## 3 Decal       2989. 565.    3000  2000  4000
# 1. Boxplot - Phân bố số lượng đặt hàng theo loại sản phẩm
p1 <- ggplot(data, aes(x = LoaiSanPham, y = SoLuongDatHang, fill = LoaiSanPham)) +
  geom_boxplot(alpha = 0.7, outlier.shape = 19) +
  stat_summary(fun = mean, geom = "point", shape = 18, size = 3, color = "red") +
  labs(title = "PHÂN BỐ SỐ LƯỢNG ĐẶT HÀNG THEO LOẠI SP",
       x = NULL,
       y = "Số lượng đặt hàng") +
  theme_minimal() +
  scale_fill_brewer(palette = "Pastel1") +
  theme(legend.position = "none")

# 2. Violin plot + Jitter - Kết hợp phân bố và điểm dữ liệu
p2 <- ggplot(data, aes(x = LoaiSanPham, y = SoLuongDatHang, fill = LoaiSanPham)) +
  geom_violin(alpha = 0.5, trim = FALSE) +
  geom_jitter(width = 0.2, alpha = 0.3, size = 1.5) +
  labs(title = "MẬT ĐỘ PHÂN BỐ KÈM DATA POINTS",
       x = NULL,
       y = NULL) +
  theme_minimal() +
  scale_fill_brewer(palette = "Set2") +
  theme(legend.position = "none")

# 3. Barplot (Mean + SD)
p3 <- ggplot(data, aes(x = LoaiSanPham, y = SoLuongDatHang, fill = LoaiSanPham)) +
  stat_summary(fun = mean, geom = "bar", position = "dodge") +
  stat_summary(fun.data = mean_sdl, 
               geom = "errorbar", 
               width = 0.2,
               color = "gray30") +
  labs(title = "GIÁ TRỊ TRUNG BÌNH ± ĐỘ LỆCH CHUẨN",
       x = NULL,
       y = "Số lượng đặt hàng (Mean ± SD)") +
  theme_minimal() +
  scale_fill_brewer(palette = "Accent") +
  theme(legend.position = "none")

# 4. Phân bố tần suất (Histogram phân nhóm)
p4 <- ggplot(data, aes(x = SoLuongDatHang, fill = LoaiSanPham)) +
  geom_histogram(binwidth = 100, 
                 alpha = 0.7, 
                 position = "identity",
                 color = "white") +
  facet_wrap(~LoaiSanPham, ncol = 1) +
  labs(title = "PHÂN BỐ TẦN SỐ CỦA SỐ LƯỢNG ĐẶT HÀNG",
       x = "Số lượng đặt hàng",
       y = "Tần số") +
  theme_minimal() +
  scale_fill_brewer(palette = "Dark2")

# Kết hợp 4 biểu đồ
combined_plot <- (p1 + p2) / (p3 + p4) + 
  plot_layout(heights = c(1, 1.5)) +
  plot_annotation(title = "THỐNG KÊ MÔ TẢ: LOẠI SP vs SỐ LƯỢNG ĐẶT HÀNG",
                  theme = theme(plot.title = element_text(size = 16, hjust = 0.5, face = "bold")))

# Xuất file ảnh chất lượng cao (tùy chọn)
ggsave("LoaiSanPham_SoLuongDatHang.png", combined_plot, width = 12, height = 10, dpi = 300)
knitr::include_graphics("LoaiSanPham_SoLuongDatHang.png", error = FALSE)

data %>%
  filter(LoaiSanPham %in% c("Decal", "Biểu mẫu", "Catalog")) %>%
  count(LoaiSanPham, SoLuongDatHang) %>%
  group_by(LoaiSanPham) %>%
  gt() %>%
  tab_header(
    title = "BẢNG THỐNG KÊ TẦN SỐ CỦA SỐ LƯỢNG ĐẶT HÀNG",
    subtitle = "Phân theo Loại sản phẩm"
  ) %>%
  fmt_number(columns = n, decimals = 0) %>%
  cols_label(
    LoaiSanPham = "Loại sản phẩm",
    SoLuongDatHang = "Số lượng đặt hàng",
    n = "Tần số"
  )
BẢNG THỐNG KÊ TẦN SỐ CỦA SỐ LƯỢNG ĐẶT HÀNG
Phân theo Loại sản phẩm
Số lượng đặt hàng Tần số
Biểu mẫu
300 183
400 1,296
500 152
Catalog
1300 2
1400 5
1500 9
1600 38
1700 74
1800 138
1900 177
2000 204
2100 182
2200 156
2300 65
2400 30
2500 5
2600 2
Decal
2000 53
2100 134
2200 140
2300 139
2400 151
2500 142
2600 133
2700 133
2800 161
2900 150
3000 150
3100 161
3200 132
3300 136
3400 140
3500 126
3600 133
3700 136
3800 135
3900 118
4000 62
# Lọc Decal và thống kê tần số
plot_freq <- data %>%
  filter(LoaiSanPham == "Decal") %>%
  ggplot(aes(x = SoLuongDatHang)) +
  geom_bar(fill = "steelblue") +
  geom_text(stat = 'count', aes(label = after_stat(count)), 
            vjust = -0.5, size = 3) +
  labs(title = "TẦN SỐ SỐ LƯỢNG ĐẶT HÀNG DECAL",
       x = "Số lượng đặt hàng",
       y = "Tần số") +
  theme_minimal()

print(plot_freq)

# Lọc Biểu mẫu và thống kê tần số
plot_freq <- data %>%
  filter(LoaiSanPham == "Biểu mẫu") %>%
  ggplot(aes(x = SoLuongDatHang)) +
  geom_bar(fill = "steelblue") +
  geom_text(stat = 'count', aes(label = after_stat(count)), 
            vjust = -0.5, size = 3) +
  labs(title = "TẦN SỐ SỐ LƯỢNG ĐẶT HÀNG BIỂU MẪU",
       x = "Số lượng đặt hàng",
       y = "Tần số") +
  theme_minimal()

print(plot_freq)

# Lọc Catalog và thống kê tần số
plot_freq <- data %>%
  filter(LoaiSanPham == "Catalog") %>%
  ggplot(aes(x = SoLuongDatHang)) +
  geom_bar(fill = "steelblue") +
  geom_text(stat = 'count', aes(label = after_stat(count)), 
            vjust = -0.5, size = 3) +
  labs(title = "TẦN SỐ SỐ LƯỢNG ĐẶT HÀNG CATALOG",
       x = "Số lượng đặt hàng",
       y = "Tần số") +
  theme_minimal()

print(plot_freq)

4.5 Thống kê mô tả biến Loại sản phẩm và Số trang

stats_table <- data %>%
  group_by(LoaiSanPham) %>%
  summarise(
    Mean = mean(SoTrang, na.rm = TRUE),
    SD = sd(SoTrang, na.rm = TRUE),
    Median = median(SoTrang, na.rm = TRUE),
    Min = min(SoTrang, na.rm = TRUE),
    Max = max(SoTrang, na.rm = TRUE)
  ) %>%
  mutate(across(where(is.numeric), ~ round(., 2)))  # Làm tròn 2 chữ số

# Hiển thị bảng
print(stats_table)
## # A tibble: 3 × 6
##   LoaiSanPham  Mean    SD Median   Min   Max
##   <chr>       <dbl> <dbl>  <dbl> <dbl> <dbl>
## 1 Biểu mẫu     90.1  5.82     90    80   100
## 2 Catalog      30.2  5.75     30    20    40
## 3 Decal         0    0         0     0     0
# 1. Boxplot - Giữ nguyên
p1 <- ggplot(data, aes(x = LoaiSanPham, y = SoTrang, fill = LoaiSanPham)) +
  geom_boxplot(alpha = 0.7, outlier.shape = 19) +
  stat_summary(fun = mean, geom = "point", shape = 18, size = 3, color = "red") +
  labs(title = "PHÂN BỐ SỐ TRANG THEO LOẠI SP",
       x = NULL,
       y = "Số trang") +
  theme_minimal() +
  scale_fill_brewer(palette = "Pastel1") +
  theme(legend.position = "none")

# 2. Violin plot - Giữ nguyên
p2 <- ggplot(data, aes(x = LoaiSanPham, y = SoTrang, fill = LoaiSanPham)) +
  geom_violin(alpha = 0.5, trim = FALSE) +
  geom_jitter(width = 0.2, alpha = 0.3, size = 1.5) +
  labs(title = "MẬT ĐỘ PHÂN BỐ KÈM DATA POINTS",
       x = NULL,
       y = NULL) +
  theme_minimal() +
  scale_fill_brewer(palette = "Set2") +
  theme(legend.position = "none")

# 3. Barplot - Giữ nguyên
p3 <- ggplot(data, aes(x = LoaiSanPham, y = SoTrang, fill = LoaiSanPham)) +
  stat_summary(fun = mean, geom = "bar", position = "dodge") +
  stat_summary(fun.data = mean_sdl, 
               geom = "errorbar", 
               width = 0.2,
               color = "gray30") +
  labs(title = "GIÁ TRỊ TRUNG BÌNH ± ĐỘ LỆCH CHUẨN",
       x = NULL,
       y = "Số trang (Mean ± SD)") +
  theme_minimal() +
  scale_fill_brewer(palette = "Accent") +
  theme(legend.position = "none")

# 4. Sửa lại histogram phân nhóm
p4 <- ggplot(data, aes(x = SoTrang, fill = LoaiSanPham)) +
  geom_bar(stat = "count", alpha = 0.7, position = "dodge", color = "white") +
  facet_wrap(~LoaiSanPham, ncol = 1, scales = "free_y") +
  labs(title = "PHÂN BỐ TẦN SỐ CỦA SỐ TRANG",
       x = "Số trang",
       y = "Tần số") +
  theme_minimal() +
  scale_fill_brewer(palette = "Dark2") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  scale_x_continuous(breaks = scales::pretty_breaks(n = 10)) # Hiển thị nhiều giá trị hơn trên trục x

# Kết hợp 4 biểu đồ
combined_plot <- (p1 + p2) / (p3 + p4) + 
  plot_layout(heights = c(1, 1.5)) +
  plot_annotation(title = "THỐNG KÊ MÔ TẢ: LOẠI SP vs SỐ TRANG",
                  theme = theme(plot.title = element_text(size = 16, hjust = 0.5, face = "bold")))

# Xuất file
ggsave("LoaiSanPham_SoTrang.png", combined_plot, width = 12, height = 12, dpi = 300)
knitr::include_graphics("LoaiSanPham_SoTrang.png", error = FALSE)

data %>%
  filter(LoaiSanPham %in% c("Decal", "Biểu mẫu", "Catalog")) %>%
  count(LoaiSanPham, SoTrang) %>%
  group_by(LoaiSanPham) %>%
  gt() %>%
  tab_header(
    title = "BẢNG THỐNG KÊ TẦN SỐ CỦA SỐ TRANG",
    subtitle = "Phân theo Loại sản phẩm"
  ) %>%
  fmt_number(columns = n, decimals = 0) %>%
  cols_label(
    LoaiSanPham = "Loại sản phẩm",
    SoTrang = "Số trang",
    n = "Tần số"
  )
BẢNG THỐNG KÊ TẦN SỐ CỦA SỐ TRANG
Phân theo Loại sản phẩm
Số trang Tần số
Biểu mẫu
80 38
81 78
82 84
83 79
84 76
85 83
86 88
87 67
88 87
89 87
90 98
91 79
92 66
93 71
94 71
95 90
96 88
97 87
98 84
99 82
100 48
Catalog
20 26
21 51
22 45
23 56
24 52
25 59
26 57
27 41
28 52
29 66
30 72
31 44
32 44
33 53
34 56
35 62
36 57
37 54
38 58
39 55
40 27
Decal
0 2,765
# Lọc Biểu mẫu và thống kê tần số
plot_freq <- data %>%
  filter(LoaiSanPham == "Biểu mẫu") %>%
  ggplot(aes(x = SoTrang)) +
  geom_bar(fill = "steelblue") +
  geom_text(stat = 'count', aes(label = after_stat(count)), 
            vjust = -0.5, size = 3) +
  labs(title = "TẦN SỐ SỐ TRANG BIỂU MẪU",
       x = "Số trang",
       y = "Tần số") +
  theme_minimal()

print(plot_freq)

# Lọc Catalog và thống kê tần số
plot_freq <- data %>%
  filter(LoaiSanPham == "Catalog") %>%
  ggplot(aes(x = SoTrang)) +
  geom_bar(fill = "steelblue") +
  geom_text(stat = 'count', aes(label = after_stat(count)), 
            vjust = -0.5, size = 3) +
  labs(title = "TẦN SỐ SỐ TRANG CATALOG",
       x = "Số trang",
       y = "Tần số") +
  theme_minimal()

print(plot_freq)

4.6 Thống kê mô tả các biến định lượng còn lại

summary(data$TyLeLoi)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
## 0.005001 0.009193 0.020083 0.017779 0.024927 0.029997
summary(data$SoLuongLoi)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    2.00    4.00   45.00   44.39   73.00  120.00
summary(data$DoanhThu)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  954800 1493100 1692000 1698486 1880200 3042000
summary(data$ChiPhiBienDong)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    5920   12000   21340   21207   28712   46560
summary(profit_data$TongDoanhThu)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
## 17239300 23082300 25549300 25655086 28218600 34574100
summary(profit_data$TongChiPhiBienDong)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  160220  278811  318440  320324  360877  493791
summary(profit_data$LoiNhuan)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
##  2034995  7821102 10267809 10334762 12886244 19080309

5 Phân phối và diễn biến của Lợi nhuận theo thời gian

# 1. Phân phối lợi nhuận
ggplot(profit_data, aes(x = LoiNhuan)) +
  geom_histogram(bins = 30, fill = "steelblue", alpha = 0.7) +
  labs(title = "Phân phối Lợi nhuận hàng ngày",
       x = "Lợi nhuận (VND)", 
       y = "Tần số") +
  scale_x_continuous(labels = scales::comma) +  # Hiển thị số đầy đủ
  scale_y_continuous(labels = scales::comma) +  # Áp dụng cho cả trục y nếu cần
  theme_minimal()

# 2. Xu hướng thời gian
ggplot(profit_data, aes(x = Ngay, y = LoiNhuan)) +
  geom_line(color = "tomato") +
  geom_smooth(method = "loess", se = FALSE) +
  labs(title = "Diễn biến Lợi nhuận theo thời gian",
       x = "Ngày", 
       y = "Lợi nhuận (VND)") +
  scale_y_continuous(labels = scales::comma) +  # Hiển thị số đầy đủ
  theme_minimal()

6 Mô phỏng các vòng lặp của lợi nhuận

# Tạo hàm lấy mẫu ngẫu nhiên và tính lợi nhuận trung bình
simulate_profit <- function(n, profit_data) {
  means <- numeric(n)
  for (i in 1:n) {
    sampled_days <- sample(profit_data$LoiNhuan, size = n, replace = TRUE)
    means[i] <- mean(sampled_days)
  }
  return(means)
}

# Danh sách số vòng lặp
iterations <- c(10, 100, 1000, 10000)

# Thực hiện mô phỏng
simulations <- lapply(iterations, function(n) simulate_profit(n, profit_data))

# Chuẩn bị dữ liệu để vẽ biểu đồ
sim_df <- data.frame(
  Iteration = rep(iterations, times = sapply(simulations, length)),
  EstimatedProfit = unlist(simulations)
)

# Gán tên dễ hiểu
sim_df$Label <- paste0("n = ", sim_df$Iteration)

# Vẽ biểu đồ giống hình ảnh π
ggplot(sim_df, aes(x = Label, y = EstimatedProfit)) +
  geom_jitter(width = 0.1, height = 0, alpha = 0.4, color = "steelblue") +
  stat_summary(fun = mean, geom = "point", size = 5, color = "darkred") +
  labs(title = "Ước lượng lợi nhuận trung bình",
       x = "Số lần mô phỏng (n)",
       y = "Lợi nhuận trung bình/ngày (VND)") +
  scale_y_continuous(labels = scales::comma) +
  theme_minimal(base_size = 14)

# Hàm mô phỏng lợi nhuận trung bình
simulate_and_plot <- function(n, profit_data) {
  means <- numeric(n)
  for (i in 1:n) {
    sampled_days <- sample(profit_data$LoiNhuan, size = n, replace = TRUE)
    means[i] <- mean(sampled_days)
  }
  mean_est <- mean(means)
  
  # Tạo biểu đồ
  p <- ggplot(data.frame(x = means), aes(x = x)) +
    geom_histogram(bins = 30, fill = "#377eb8", alpha = 0.7, color = "black") +
    geom_vline(xintercept = mean_est, color = "red", linetype = "dashed") +
    labs(
      title = paste0(n, " Lần Mô Phỏng"),
      subtitle = paste0("Lợi nhuận TB ≈ ", round(mean_est, 0), " VND"),
      x = NULL,
      y = NULL
    ) +
    scale_x_continuous(labels = scales::comma) +
    theme_minimal(base_size = 13)
  return(p)
}

# Danh sách số vòng lặp
iterations <- c(10, 100, 1000, 10000)

# Tạo danh sách biểu đồ
plots <- lapply(iterations, function(n) simulate_and_plot(n, profit_data))

# Dùng patchwork để sắp xếp thành 2 hàng 3 cột
final_plot <- (plots[[1]] + plots[[2]]) / (plots[[3]] + plots[[4]])

# Hiển thị
ggsave("Mô phỏng Lợi nhuận.png", final_plot, width = 12, height = 6)
knitr::include_graphics("Mô phỏng Lợi nhuận.png", error = FALSE)

7 Các kịch bản của mô hình

# 1. HÀM MÔ PHỎNG LỢI NHUẬN
simulate_profit_mean <- function(profit_vector, n) {
  replicate(n, mean(sample(profit_vector, n, replace = TRUE)))
}

# 2. TẠO KỊCH BẢN MỚI (thay đổi dữ liệu)

create_modified_profit_data <- function(data, factor_error = 1, factor_cost = 1, factor_order = 1) {
  df <- data
  
  # Tăng/giảm tỷ lệ lỗi
  df$TyLeLoi <- pmin(df$TyLeLoi * factor_error, 1)  # Không vượt 100%
  df$SoLuongLoi <- round(df$SoLuongDatHang * df$TyLeLoi)
  
  # Tăng/giảm giá vốn
  df$GiaVon <- df$GiaVon * factor_cost
  
  # Tính lại chi phí biến động
  df$ChiPhiBienDong <- mapply(function(type, error_q, pages, cost) {
    if (type == "Decal") error_q * cost else error_q * pages * cost
  }, df$LoaiSanPham, df$SoLuongLoi, df$SoTrang, df$GiaVon)
  
  # Tăng/giảm số đơn hàng/ngày
  daily_orders <- df %>%
    group_by(Ngay) %>%
    summarise(
      TongDoanhThu = sum(DoanhThu, na.rm = TRUE),
      TongChiPhiBienDong = sum(ChiPhiBienDong, na.rm = TRUE),
      ChiPhiCoDinh = 15000000,
      .groups = "drop"
    ) %>%
    mutate(
      LoiNhuan = (TongDoanhThu * factor_order) - ChiPhiCoDinh - (TongChiPhiBienDong * factor_order)
    )
  
  return(daily_orders$LoiNhuan)
}

# 3. TẠO KỊCH BẢN VÀ MÔ PHỎNG
scenarios <- list(
  "Bình thường" = c(1, 1, 1),
  "Tăng lỗi" = c(1.5, 1, 1),
  "Giảm lỗi" = c(0.5, 1, 1),
  "Tăng giá vốn" = c(1, 1.2, 1),
  "Giảm giá vốn" = c(1, 0.8, 1),
  "Tăng đơn hàng" = c(1, 1, 1.3),
  "Giảm đơn hàng" = c(1, 1, 0.7)
)

iterations <- c(10, 100, 1000, 10000)

# Lưu kết quả mô phỏng
result_list <- list()

for (scenario_name in names(scenarios)) {
  factors <- scenarios[[scenario_name]]
  profit_vec <- create_modified_profit_data(data, 
                                            factor_error = factors[1], 
                                            factor_cost = factors[2], 
                                            factor_order = factors[3])
  
  for (n in iterations) {
    profits <- simulate_profit_mean(profit_vec, n)
    result_list[[paste0(scenario_name, "_n", n)]] <- data.frame(
      Scenario = scenario_name,
      Iteration = n,
      EstimatedProfit = profits
    )
  }
}

# Gộp dữ liệu
all_simulations <- bind_rows(result_list)

# 4. VẼ BIỂU ĐỒ
aa <- ggplot(all_simulations, aes(x = factor(Iteration), y = EstimatedProfit, fill = Scenario)) +
  geom_boxplot(alpha = 0.6, outlier.shape = NA) +
  facet_wrap(~Scenario, scales = "free_y") +
  labs(
    title = "Ảnh hưởng của các yếu tố đến lợi nhuận trung bình/ngày",
    x = "Số vòng lặp mô phỏng (n)",
    y = "Lợi nhuận trung bình/ngày (VND)"
  ) +
  theme_minimal(base_size = 13) +       
  theme(legend.position = "none")

print(aa)

ggsave("Ảnh hưởng LN.png", aa, width = 12, height = 6)
# Tính lợi nhuận trung bình/ngày theo từng kịch bản và từng số vòng lặp
summary_table <- all_simulations %>%
  group_by(Scenario, Iteration) %>%
  summarise(MeanProfit = mean(EstimatedProfit), .groups = "drop") %>%
  arrange(Iteration)

# Hiển thị
datatable(summary_table)

8 Đánh giá hiệu quả mô phỏng

evaluate_simulation <- function(sim_values, confidence = 0.95) {
  n <- length(sim_values)
  mean_val <- mean(sim_values)
  sd_val <- sd(sim_values)
  sem <- sd_val / sqrt(n)
  z <- qnorm((1 + confidence)/2)  # z = 1.96 với CI 95%
  ci_lower <- mean_val - z * sem
  ci_upper <- mean_val + z * sem
  cv <- sd_val / mean_val
  
  var_5 <- quantile(sim_values, probs = 0.05)
  es_5 <- mean(sim_values[sim_values <= var_5])
  
  return(data.frame(
    N = n,
    Mean = mean_val,
    SD = sd_val,
    SEM = sem,
    CI_Lower = ci_lower,
    CI_Upper = ci_upper,
    CV = cv,
    VaR_5 = var_5,
    ES_5 = es_5
  ))
}
# Áp dụng đánh giá cho từng kịch bản + n
evaluation_results <- all_simulations %>%
  group_by(Scenario, Iteration) %>%
  summarise(
    Eval = list(evaluate_simulation(EstimatedProfit)),
    .groups = "drop"
  ) %>%
  unnest(Eval) 

# Hiển thị bảng kết quả
datatable(evaluation_results)
LS0tDQp0aXRsZTogIlRITk4yIg0KYXV0aG9yOiAiVsawxqFuZyBOaMawIFRo4buneSINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICB0b2NfZGVwdGg6IDQNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCiAgICBkZl9wcmludDoga2FibGUNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogICAgdGhlbWU6IHNhbmRzdG9uZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSkNCmBgYA0KDQojIExvYWQgY8OhYyB0aMawIHZp4buHbiBj4bqnbiB0aGnhur90DQpgYGB7cn0NCmxpYnJhcnkoRFQpICAgICAgICAgICAjIMSQ4buDIGhp4buDbiB0aOG7iyBi4bqjbmcgZOG7ryBsaeG7h3UNCmxpYnJhcnkodHJpYW5nbGUpICAgICAjIMSQ4buDIHThuqFvIHBow6JuIHBo4buRaSB0YW0gZ2nDoWMNCmxpYnJhcnkobHVicmlkYXRlKSAgICAjIMSQ4buDIHjhu60gbMO9IG5nw6B5IHRow6FuZw0KbGlicmFyeShkcGx5cikgICAgICAgICMgxJDhu4Mgc+G7rSBk4bulbmcgaMOgbSBjYXNlX3doZW4NCmxpYnJhcnkoa25pdHIpICAgICAgICAjIMSQ4buDIHThuqFvIGLhuqNuZw0KbGlicmFyeShnZ3Bsb3QyKSAgICAgICMgxJDhu4MgduG6vSBiaeG7g3UgxJHhu5MNCmxpYnJhcnkocGF0Y2h3b3JrKSAgICAjIMSQ4buDIG7hu5FpIDIgYmnhu4N1IMSR4buTDQpsaWJyYXJ5KGdncHVicikgICAgICAgIyBDaG8gY8OhYyBiaeG7g3UgxJHhu5MgdGjhu5FuZyBrw6ogbsOibmcgY2FvDQpsaWJyYXJ5KHRpZHl2ZXJzZSkgICAgIyBU4bqtcCBo4bujcCBjw6FjIHBhY2thZ2UgcXVhbiB0cuG7jW5nIGNobyBkYXRhIHNjaWVuY2UNCmxpYnJhcnkoc2tpbXIpICAgICAgICAjIFhlbSB04buVbmcgcXVhbiBk4buvIGxp4buHdSB24bubaSB0aOG7kW5nIGvDqiBtw7QgdOG6ow0KbGlicmFyeShndCkgICAgICAgICAgICMgVOG6oW8gYuG6o25nIGJp4buDdSDEkeG6uXAgdsOgIGNodXnDqm4gbmdoaeG7h3ANCmxpYnJhcnkoUkNvbG9yQnJld2VyKSAjIEN1bmcgY+G6pXAgY8OhYyBwYWxldHRlIG3DoHUgxJHhu4MgduG6vSBiaeG7g3UgxJHhu5MgxJHhurlwDQpsaWJyYXJ5KG9wZW54bHN4KSAgICAgIyBYdeG6pXQgZOG7ryBsaeG7h3UgcmEgZmlsZSBleGVsDQpgYGANCg0KIyBCaeG7g3UgxJHhu5MgY8OhYyBwaMOibiBwaOG7kWkNCg0KIyMgUGjDom4gcGjhu5FpIMSR4buBdQ0KYGBge3J9DQojIFThuqFvIGThu68gbGnhu4d1IHThu6sgcGjDom4gcGjhu5FpIMSR4buBdSB0cm9uZyBraG/huqNuZyBbNSwgMTBdDQpwcERldSA8LSBydW5pZigxMDAwLCBtaW4gPSA1LCBtYXggPSAxMCkNCg0KIyBW4bq9IGJp4buDdSDEkeG7kyBoaXN0b2dyYW0gduG7m2kgdHLhu6VjIFkgbMOgIHBo4bqnbiB0csSDbQ0KaGlzdChwcERldSwNCiAgICAgYnJlYWtzID0gMjAsDQogICAgIGNvbCA9ICJsaWdodGdyZWVuIiwNCiAgICAgZnJlcSA9IEZBTFNFLCAgIyBiaeG7g3UgxJHhu5MgdGhlbyBt4bqtdCDEkeG7mQ0KICAgICBtYWluID0gIlBow6JuIHBo4buRaSDEkOG7gXUgKFVuaWZvcm0pIiwNCiAgICAgeGxhYiA9ICJHacOhIHRy4buLIiwNCiAgICAgeWxhYiA9ICJU4bqnbiBzdeG6pXQgKCUpIikNCg0KIyBDaHV54buDbiB0cuG7pWMgWSB04burIG3huq10IMSR4buZIHNhbmcgcGjhuqduIHRyxINtIGLhurFuZyBjw6FjaCBuaMOibiB24bubaSAxMDANCiMgKHbDrCBt4bqtdCDEkeG7mSBjw7MgdOG7lW5nIGzDoCAxLCBuw6puIG5ow6JuIDEwMCDEkeG7gyB0aMOgbmggcGjhuqduIHRyxINtKQ0KbGluZXMoZGVuc2l0eShwcERldSksIGNvbCA9ICJkYXJrZ3JlZW4iLCBsd2QgPSAyKQ0KYGBgDQoNCiMjIFBow6JuIHBo4buRaSBjaHXhuqluDQpgYGB7cn0NCiMgVOG6oW8gZOG7ryBsaeG7h3UgdOG7qyBwaMOibiBwaOG7kWkgY2h14bqpbiAobWVhbiA9IDAsIHNkID0gMSkNCnBwQ2h1YW4gPC0gcm5vcm0oMTAwMCwgbWVhbiA9IDAsIHNkID0gMSkNCg0KIyBW4bq9IGJp4buDdSDEkeG7kyBoaXN0b2dyYW0gduG7m2kgdHLhu6VjIFkgbMOgIHBo4bqnbiB0csSDbQ0KaGlzdChwcENodWFuLA0KICAgICBicmVha3MgPSAyMCwNCiAgICAgY29sID0gInNreWJsdWUiLA0KICAgICBmcmVxID0gRkFMU0UsICAjIGJp4buDdSDEkeG7kyB0aGVvIG3huq10IMSR4buZDQogICAgIG1haW4gPSAiUGjDom4gcGjhu5FpIENodeG6qW4gKE5vcm1hbCkiLA0KICAgICB4bGFiID0gIkdpw6EgdHLhu4siLA0KICAgICB5bGFiID0gIlThuqduIHN14bqldCAoJSkiKQ0KDQojIFbhur0gxJHGsOG7nW5nIG3huq10IMSR4buZIHbDoCBuaMOibiB24bubaSAxMDAgxJHhu4MgY2h1eeG7g24gc2FuZyBwaOG6p24gdHLEg20gdHLhu7FjIHF1YW4NCmxpbmVzKGRlbnNpdHkocHBDaHVhbiksIGNvbCA9ICJibHVlIiwgbHdkID0gMikNCmBgYA0KDQojIyBQaMOibiBwaOG7kWkgVGFtIGdpw6FjDQpgYGB7cn0NCiMgVOG6oW8gZOG7ryBsaeG7h3UgdOG7qyBwaMOibiBwaOG7kWkgVGFtIGdpw6FjIChnacOhIHRy4buLIG1pbiA9IDAsIG1vZGUgPSA1LCBtYXggPSAxMCkNCnBwVGFtR2lhYyA8LSBydHJpYW5nbGUoMTAwMCwgYSA9IDAsIGIgPSAxMCwgYyA9IDUpDQoNCiMgVuG6vSBiaeG7g3UgxJHhu5MgaGlzdG9ncmFtIHbhu5tpIHRy4bulYyBZIGzDoCBwaOG6p24gdHLEg20NCmhpc3QocHBUYW1HaWFjLA0KICAgICBicmVha3MgPSAyMCwNCiAgICAgY29sID0gImxpZ2h0Ymx1ZSIsDQogICAgIGZyZXEgPSBGQUxTRSwgICMgxJHhu4MgYmnhu4N1IMSR4buTIGNodeG6qW4gaMOzYQ0KICAgICBtYWluID0gIlBow6JuIHBo4buRaSBUYW0gZ2nDoWMiLA0KICAgICB4bGFiID0gIkdpw6EgdHLhu4siLA0KICAgICB5bGFiID0gIlThuqduIHN14bqldCAoJSkiKQ0KDQojIENodXnhu4NuIHThuqduIHN14bqldCBzYW5nIHBo4bqnbiB0csSDbQ0KbGluZXMoZGVuc2l0eShwcFRhbUdpYWMpLCBjb2wgPSAiYmx1ZSIsIGx3ZCA9IDIpDQpgYGANCg0KIyMgUGjDom4gcGjhu5FpIHLhu51pIHLhuqFjDQpgYGB7cn0NCnBwUm9pUmFjIDwtIHNhbXBsZShjKDEsIDIsIDMpLCBzaXplID0gMTAwMCwgcmVwbGFjZSA9IFRSVUUsIHByb2IgPSBjKDAuMiwgMC41LCAwLjMpKQ0KDQp0YWJsZV9wY3QgPC0gcHJvcC50YWJsZSh0YWJsZShwcFJvaVJhYykpICogMTAwDQoNCmJhcnBsb3QodGFibGVfcGN0LA0KICAgICAgICBjb2wgPSAic2FsbW9uIiwNCiAgICAgICAgbWFpbiA9ICJQaMOibiBwaOG7kWkgUuG7nWkgcuG6oWMiLA0KICAgICAgICB5bGFiID0gIlThuqduIHN14bqldCAoJSkiLA0KICAgICAgICB4bGFiID0gIkdpw6EgdHLhu4siLA0KICAgICAgICB5bGltID0gYygwLCBtYXgodGFibGVfcGN0KSArIDUpKQ0KYGBgDQoNCiMgTcO0IHBo4buPbmcgZOG7ryBsaeG7h3UgxJHGoW4gaMOgbmcNCmBgYHtyfQ0KIyBLaOG7n2kgdOG6oW8gc2VlZCDEkeG7gyBr4bq/dCBxdeG6oyBjw7MgdGjhu4MgdMOhaSBs4bqtcA0Kc2V0LnNlZWQoMTIzKQ0KDQojIDEuIFThuqFvIGThu68gbGnhu4d1IG5nw6B5IHRyb25nIG7Eg20gMjAyNQ0Kc3RhcnRfZGF0ZSA8LSBhcy5EYXRlKCIyMDI1LTAxLTAxIikNCmVuZF9kYXRlIDwtIGFzLkRhdGUoIjIwMjUtMTItMzEiKQ0KZGF0ZXMgPC0gc2VxLkRhdGUoc3RhcnRfZGF0ZSwgZW5kX2RhdGUsIGJ5ID0gImRheSIpDQoNCiMgMi4gVOG6oW8gbcOjIMSRxqFuIGjDoG5nIHRoZW8gdGjDoW5nDQpnZW5lcmF0ZV9vcmRlcl9pZHMgPC0gZnVuY3Rpb24oeWVhciwgbW9udGgpIHsNCiAgZGF5c19pbl9tb250aCA8LSBkYXlzX2luX21vbnRoKGFzLkRhdGUocGFzdGUoeWVhciwgbW9udGgsICIwMSIsIHNlcCA9ICItIikpKQ0KICBvcmRlcnNfcGVyX2RheSA8LSByb3VuZChydHJpYW5nbGUobiA9IGRheXNfaW5fbW9udGgsIGEgPSAxMCwgYiA9IDIwLCBjID0gMTUpKQ0KICANCiAgb3JkZXJfaWRzIDwtIGNoYXJhY3RlcigwKQ0KICBmb3IgKGRheSBpbiAxOmRheXNfaW5fbW9udGgpIHsNCiAgICBkYXlfb3JkZXJzIDwtIHNwcmludGYoIiVzJTAyZCUwMmQlMDNkIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnN0cih5ZWFyLCAzLCA0KSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIG1vbnRoLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgZGF5LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgMTpvcmRlcnNfcGVyX2RheVtkYXldKQ0KICAgIG9yZGVyX2lkcyA8LSBjKG9yZGVyX2lkcywgZGF5X29yZGVycykNCiAgfQ0KICByZXR1cm4ob3JkZXJfaWRzKQ0KfQ0KDQojIFThuqFvIHThuqV0IGPhuqMgbcOjIMSRxqFuIGjDoG5nIGNobyBuxINtIDIwMjUNCmFsbF9vcmRlcl9pZHMgPC0gdW5saXN0KGxhcHBseSgxOjEyLCBmdW5jdGlvbihtb250aCkgZ2VuZXJhdGVfb3JkZXJfaWRzKDIwMjUsIG1vbnRoKSkpDQoNCiMgMy4gVOG6oW8gbG/huqFpIHPhuqNuIHBo4bqpbSB0aGVvIHBow6JuIHBo4buRaSBy4budaSBy4bqhYyANCnByb2R1Y3RfdHlwZXMgPC0gc2FtcGxlKGMoIkRlY2FsIiwgIkJp4buDdSBt4bqrdSIsICJDYXRhbG9nIiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IGxlbmd0aChhbGxfb3JkZXJfaWRzKSwgDQogICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICAgICAgICBwcm9iID0gYygwLjUsIDAuMywgMC4yKSkNCg0KIyBIw6BtIGzDoG0gdHLDsm4gc+G7kSBsxrDhu6NuZyB0aGVvIHF1eSB04bqvYyB0aOG7sWMgdOG6vw0Kcm91bmRfdG9fcmVhbGlzdGljIDwtIGZ1bmN0aW9uKHgsIHByb2R1Y3RfdHlwZSkgew0KICBpZiAocHJvZHVjdF90eXBlID09ICJCaeG7g3UgbeG6q3UiKSB7DQogICAgcm91bmRfdG8gPC0gYygwKQ0KICB9IGVsc2UgaWYgKHByb2R1Y3RfdHlwZSA9PSAiQ2F0YWxvZyIpIHsNCiAgICByb3VuZF90byA8LSBjKDEwMCwgNTAwLCAxMDAwKQ0KICB9IGVsc2UgeyAjIERlY2FsDQogICAgcm91bmRfdG8gPC0gYygxMDAsIDUwMCwgMTAwMCkNCiAgfQ0KICANCiAgZGlmZmVyZW5jZXMgPC0gc2FwcGx5KHJvdW5kX3RvLCBmdW5jdGlvbihyKSBhYnMoeCAtIHIpKQ0KICBjbG9zZXN0IDwtIHJvdW5kX3RvW3doaWNoLm1pbihkaWZmZXJlbmNlcyldDQogIA0KICBpZiAobWluKGRpZmZlcmVuY2VzKSA+IDAuMiAqIHgpIHsNCiAgICByZXR1cm4ocm91bmQoeCAvIDEwMCkgKiAxMDApDQogIH0gZWxzZSB7DQogICAgcmV0dXJuKGNsb3Nlc3QpDQogIH0NCn0NCg0KIyA0LiBU4bqhbyBz4buRIGzGsOG7o25nIGtow6FjaCDEkeG6t3QgY2hvIHThu6tuZyBsb+G6oWkgc+G6o24gcGjhuqltDQpnZW5lcmF0ZV9wcm9kdWN0X2RhdGEgPC0gZnVuY3Rpb24ocHJvZHVjdF90eXBlKSB7DQogIGlmIChwcm9kdWN0X3R5cGUgPT0gIkJp4buDdSBt4bqrdSIpIHsNCiAgICBwYWdlcyA8LSByb3VuZChydW5pZigxLCA4MCwgMTAwKSkgICMgU+G7kSB0cmFuZyBt4buXaSBjdeG7kW4gYmnhu4N1IG3huqt1DQogICAgYmFzZV9xdWFudGl0eSA8LSBybm9ybSgxLCBtZWFuID0gNDAwLCBzZCA9IDQwKSAgIyBT4buRIGN14buRbg0KICAgIHF1YW50aXR5IDwtIHJvdW5kX3RvX3JlYWxpc3RpYyhiYXNlX3F1YW50aXR5LCBwcm9kdWN0X3R5cGUpDQogICAgcmV0dXJuKGxpc3QocXVhbnRpdHkgPSBxdWFudGl0eSwgcGFnZXNfc2hlZXRzID0gcGFnZXMpKQ0KICB9IGVsc2UgaWYgKHByb2R1Y3RfdHlwZSA9PSAiQ2F0YWxvZyIpIHsNCiAgICBwYWdlcyA8LSByb3VuZChydW5pZigxLCAyMCwgNDApKSAgIyBT4buRIHRyYW5nIG3hu5dpIGN14buRbiBjYXRhbG9nDQogICAgYmFzZV9xdWFudGl0eSA8LSBybm9ybSgxLCBtZWFuID0gMjAwMCwgc2QgPSAyMDApICAjIFPhu5EgY3Xhu5FuDQogICAgcXVhbnRpdHkgPC0gcm91bmRfdG9fcmVhbGlzdGljKGJhc2VfcXVhbnRpdHksIHByb2R1Y3RfdHlwZSkNCiAgICByZXR1cm4obGlzdChxdWFudGl0eSA9IHF1YW50aXR5LCBwYWdlc19zaGVldHMgPSBwYWdlcykpDQogIH0gZWxzZSB7ICMgRGVjYWwNCiAgICBzaGVldHMgPC0gcm91bmQocnVuaWYoMSwgMjAwMCwgNDAwMCkpICAjIFPhu5EgdOG7nSBkZWNhbA0KICAgIHF1YW50aXR5IDwtIHJvdW5kX3RvX3JlYWxpc3RpYyhzaGVldHMsIHByb2R1Y3RfdHlwZSkNCiAgICByZXR1cm4obGlzdChxdWFudGl0eSA9IHF1YW50aXR5LCBwYWdlc19zaGVldHMgPSAwKSkgICMgRGVjYWwga2jDtG5nIGPDsyBz4buRIHRyYW5nDQogIH0NCn0NCg0KIyBU4bqhbyBk4buvIGxp4buHdSBz4bqjbiBwaOG6qW0NCnByb2R1Y3RfZGF0YSA8LSBsYXBwbHkocHJvZHVjdF90eXBlcywgZ2VuZXJhdGVfcHJvZHVjdF9kYXRhKQ0KcXVhbnRpdGllcyA8LSBzYXBwbHkocHJvZHVjdF9kYXRhLCBmdW5jdGlvbih4KSB4JHF1YW50aXR5KQ0KcGFnZXNfc2hlZXRzIDwtIHNhcHBseShwcm9kdWN0X2RhdGEsIGZ1bmN0aW9uKHgpIHgkcGFnZXNfc2hlZXRzKQ0KDQojIDUuIFThuqFvIHThu7cgbOG7hyBs4buXaSANCmVycm9yX3JhdGVzIDwtIG51bWVyaWMobGVuZ3RoKHByb2R1Y3RfdHlwZXMpKQ0KDQplcnJvcl9yYXRlc1twcm9kdWN0X3R5cGVzID09ICJCaeG7g3UgbeG6q3UiXSA8LQ0KICBydW5pZihzdW0ocHJvZHVjdF90eXBlcyA9PSAiQmnhu4N1IG3huqt1IiksIDAuMDA1LCAwLjAxKQ0KDQplcnJvcl9yYXRlc1twcm9kdWN0X3R5cGVzID09ICJDYXRhbG9nIl0gPC0NCiAgcnVuaWYoc3VtKHByb2R1Y3RfdHlwZXMgPT0gIkNhdGFsb2ciKSwgMC4wMSwgMC4wMikNCg0KZXJyb3JfcmF0ZXNbcHJvZHVjdF90eXBlcyA9PSAiRGVjYWwiXSA8LQ0KICBydW5pZihzdW0ocHJvZHVjdF90eXBlcyA9PSAiRGVjYWwiKSwgMC4wMiwgMC4wMykNCg0KIyA2LiBUw61uaCBz4buRIGzGsOG7o25nIGzhu5dpDQplcnJvcl9xdWFudGl0aWVzIDwtIHJvdW5kKHF1YW50aXRpZXMgKiBlcnJvcl9yYXRlcykNCg0KIyA3LiBU4bqhbyDEkcahbiB24buLDQp1bml0cyA8LSBpZmVsc2UocHJvZHVjdF90eXBlcyA9PSAiRGVjYWwiLCAiVOG7nSIsICJDdeG7kW4iKQ0KDQojIDguIFThuqFvIGxv4bqhaSBnaeG6pXkNCnBhcGVyX3R5cGVzIDwtIGlmZWxzZShwcm9kdWN0X3R5cGVzID09ICJEZWNhbCIsICJDUFdHLVAwMDMxIiwNCiAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShwcm9kdWN0X3R5cGVzID09ICJCaeG7g3UgbeG6q3UiLCAiQ2FyYm9ubGVzcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHJ1bmlmKGxlbmd0aChwcm9kdWN0X3R5cGVzKSkgPiAwLjUsICJGMTAwIiwgIkMxMDAiKSkpDQoNCiMgOS4gVOG6oW8gxJDGoE4gR0nDgSBCw4FOIChnacOhIGLDoW4gY2hvIGtow6FjaCBow6BuZykNCnNlbGxpbmdfcHJpY2VzIDwtIGNhc2Vfd2hlbigNCiAgcGFwZXJfdHlwZXMgPT0gIkNQV0ctUDAwMzEiIH4gNTUzLCAgICAjIEdpw6EgYsOhbiBt4buXaSB04budIGRlY2FsDQogIHBhcGVyX3R5cGVzID09ICJDYXJib25sZXNzIiB+IDQ3LCAgICAgIyBHacOhIGLDoW4gbeG7l2kgdHJhbmcgYmnhu4N1IG3huqt1DQogIHBhcGVyX3R5cGVzID09ICJGMTAwIiB+IDMwLCAgICAgICAgICAgIyBHacOhIGLDoW4gbeG7l2kgdHJhbmcgY2F0YWxvZw0KICBwYXBlcl90eXBlcyA9PSAiQzEwMCIgfiAzMSAgICAgICAgICAgICMgR2nDoSBiw6FuIG3hu5dpIHRyYW5nIGNhdGFsb2cNCikNCg0KIyAxMC4gVOG6oW8gR0nDgSBW4buQTiAoY2hpIHBow60gc+G6o24geHXhuqV0KQ0KY29zdF9wcmljZXMgPC0gY2FzZV93aGVuKA0KICBwYXBlcl90eXBlcyA9PSAiQ1BXRy1QMDAzMSIgfiAzODgsICAgICMgR2nDoSB24buRbiBt4buXaSB04budIGRlY2FsDQogIHBhcGVyX3R5cGVzID09ICJDYXJib25sZXNzIiB+IDM3LCAgICAgICMgR2nDoSB24buRbiBt4buXaSB0cmFuZyBiaeG7g3UgbeG6q3UNCiAgcGFwZXJfdHlwZXMgPT0gIkYxMDAiIH4gMjAsICAgICAgICAgICAgIyBHacOhIHbhu5FuIG3hu5dpIHRyYW5nIGNhdGFsb2cNCiAgcGFwZXJfdHlwZXMgPT0gIkMxMDAiIH4gMjEgICAgICAgICAgICAgIyBHacOhIHbhu5FuIG3hu5dpIHRyYW5nIGNhdGFsb2cNCikNCg0KIyAxMS4gVMOtbmggZG9hbmggdGh1IHRoZW8gdOG7q25nIGxv4bqhaSBz4bqjbiBwaOG6qW0gKGTDuW5nIGdpw6EgYsOhbikNCmNhbGN1bGF0ZV9yZXZlbnVlIDwtIGZ1bmN0aW9uKHByb2R1Y3RfdHlwZSwgcXVhbnRpdHksIHBhZ2VzLCBzZWxsaW5nX3ByaWNlKSB7DQogIGlmIChwcm9kdWN0X3R5cGUgPT0gIkRlY2FsIikgew0KICAgICMgRGVjYWw6IFPhu5EgdOG7nSDDlyBnacOhIGLDoW4vdOG7nQ0KICAgIHF1YW50aXR5ICogc2VsbGluZ19wcmljZQ0KICB9IGVsc2Ugew0KICAgICMgQmnhu4N1IG3huqt1IHbDoCBDYXRhbG9nOiBT4buRIGN14buRbiDDlyBz4buRIHRyYW5nIMOXIGdpw6EgYsOhbi90cmFuZyANCiAgICBxdWFudGl0eSAqIHBhZ2VzICogc2VsbGluZ19wcmljZSANCiAgfQ0KfQ0KDQpyZXZlbnVlIDwtIG1hcHBseShjYWxjdWxhdGVfcmV2ZW51ZSwgDQogICAgICAgICAgICAgICAgIHByb2R1Y3RfdHlwZXMsIHF1YW50aXRpZXMsIHBhZ2VzX3NoZWV0cywgc2VsbGluZ19wcmljZXMpDQoNCiMgMTIuIFTDrW5oIGNoaSBwaMOtIGJp4bq/biDEkeG7mW5nIChkw7luZyBnacOhIHbhu5FuKQ0KY2FsY3VsYXRlX3ZhcmlhYmxlX2Nvc3QgPC0gZnVuY3Rpb24ocHJvZHVjdF90eXBlLCBlcnJvcl9xdWFudGl0eSwgcGFnZXMsIGNvc3RfcHJpY2UpIHsNCiAgaWYgKHByb2R1Y3RfdHlwZSA9PSAiRGVjYWwiKSB7DQogICAgIyBEZWNhbDogU+G7kSB04budIGzhu5dpIMOXIGdpw6EgduG7kW4vdOG7nQ0KICAgIGVycm9yX3F1YW50aXR5ICogY29zdF9wcmljZQ0KICB9IGVsc2Ugew0KICAgICMgQmnhu4N1IG3huqt1IHbDoCBDYXRhbG9nOiBT4buRIGN14buRbiBs4buXaSDDlyBz4buRIHRyYW5nIMOXIGdpw6EgduG7kW4vdHJhbmcNCiAgICBlcnJvcl9xdWFudGl0eSAqIHBhZ2VzICogY29zdF9wcmljZQ0KICB9DQp9DQoNCnZhcmlhYmxlX2Nvc3RzIDwtIG1hcHBseShjYWxjdWxhdGVfdmFyaWFibGVfY29zdCwgDQogICAgICAgICAgICAgICAgICAgICAgICBwcm9kdWN0X3R5cGVzLCBlcnJvcl9xdWFudGl0aWVzLCBwYWdlc19zaGVldHMsIGNvc3RfcHJpY2VzKQ0KDQojIFThuqFvIGRhdGEgZnJhbWUgdOG7lW5nIGjhu6NwDQpkYWlseV9vcmRlcnMgPC0gcm91bmQocnRyaWFuZ2xlKG4gPSBsZW5ndGgoZGF0ZXMpLCBhID0gMTAsIGIgPSAyMCwgYyA9IDE1KSkNCmRhdGEgPC0gZGF0YS5mcmFtZSgNCiAgTmdheSA9IHJlcChkYXRlcywgdGltZXMgPSBkYWlseV9vcmRlcnMpWzE6bGVuZ3RoKHJldmVudWUpXSwNCiAgTWFEb25IYW5nID0gYWxsX29yZGVyX2lkc1sxOmxlbmd0aChyZXZlbnVlKV0sDQogIExvYWlTYW5QaGFtID0gcHJvZHVjdF90eXBlc1sxOmxlbmd0aChyZXZlbnVlKV0sDQogIFNvTHVvbmdEYXRIYW5nID0gcXVhbnRpdGllc1sxOmxlbmd0aChyZXZlbnVlKV0sDQogIERvblZpID0gdW5pdHNbMTpsZW5ndGgocmV2ZW51ZSldLA0KICBTb1RyYW5nID0gaWZlbHNlKHByb2R1Y3RfdHlwZXNbMTpsZW5ndGgocmV2ZW51ZSldICVpbiUgYygiQmnhu4N1IG3huqt1IiwgIkNhdGFsb2ciKSwgDQogICAgICAgICAgICAgICAgICBwYWdlc19zaGVldHNbMTpsZW5ndGgocmV2ZW51ZSldLCAwKSwNCiAgVHlMZUxvaSA9IGVycm9yX3JhdGVzWzE6bGVuZ3RoKHJldmVudWUpXSwNCiAgU29MdW9uZ0xvaSA9IGVycm9yX3F1YW50aXRpZXNbMTpsZW5ndGgocmV2ZW51ZSldLA0KICBMb2FpR2lheSA9IHBhcGVyX3R5cGVzWzE6bGVuZ3RoKHJldmVudWUpXSwNCiAgRG9uR2lhQmFuID0gc2VsbGluZ19wcmljZXNbMTpsZW5ndGgocmV2ZW51ZSldLA0KICBHaWFWb24gPSBjb3N0X3ByaWNlc1sxOmxlbmd0aChyZXZlbnVlKV0sDQogIERvYW5oVGh1ID0gcmV2ZW51ZVsxOmxlbmd0aChyZXZlbnVlKV0sDQogIENoaVBoaUJpZW5Eb25nID0gdmFyaWFibGVfY29zdHNbMTpsZW5ndGgocmV2ZW51ZSldDQopDQoNCiMgU+G6r3AgeOG6v3AgbOG6oWkgdGhlbyBuZ8OgeSB2w6AgbMOgbSBz4bqhY2ggZOG7ryBsaeG7h3UNCmRhdGEgPC0gZGF0YVtvcmRlcihkYXRhJE5nYXkpLCBdDQpyb3duYW1lcyhkYXRhKSA8LSBOVUxMDQoNCiMgVOG6oW8gYuG6o25nIHThu5VuZyBo4bujcCBkb2FuaCB0aHUgdsOgIGNoaSBwaMOtIHRoZW8gbmfDoHkNCnByb2ZpdF9kYXRhIDwtIGRhdGEgJT4lIA0KICBncm91cF9ieShOZ2F5KSAlPiUgDQogIHN1bW1hcmlzZSgNCiAgICBUb25nRG9hbmhUaHUgPSBzdW0oRG9hbmhUaHUsIG5hLnJtID0gVFJVRSksDQogICAgVG9uZ0NoaVBoaUJpZW5Eb25nID0gc3VtKENoaVBoaUJpZW5Eb25nLCBuYS5ybSA9IFRSVUUpLA0KICAgIENoaVBoaUNvRGluaCA9IDE1MDAwMDAwLCAgIyAxNSB0cmnhu4d1IMSR4buTbmcvbmfDoHkNCiAgICAuZ3JvdXBzID0gImRyb3AiDQogICkgJT4lIA0KICBtdXRhdGUoDQogICAgTG9pTmh1YW4gPSBUb25nRG9hbmhUaHUgLSBDaGlQaGlDb0RpbmggLSBUb25nQ2hpUGhpQmllbkRvbmcNCiAgKQ0KYGBgDQoNCmBgYHtyfQ0KIyBU4bqhbyB3b3JrYm9vayBt4bubaQ0Kd2IgPC0gY3JlYXRlV29ya2Jvb2soKQ0KDQojIFRow6ptIHNoZWV0IMSR4bqndSB0acOqbjogZOG7ryBsaeG7h3UgZ+G7kWMNCmFkZFdvcmtzaGVldCh3YiwgIkRhdGEiKQ0Kd3JpdGVEYXRhKHdiLCBzaGVldCA9ICJEYXRhIiwgZGF0YSkNCg0KIyBUaMOqbSBzaGVldCB0aOG7qSBoYWk6IGThu68gbGnhu4d1IGzhu6NpIG5odeG6rW4NCmFkZFdvcmtzaGVldCh3YiwgIlByb2ZpdF9EYXRhIikNCndyaXRlRGF0YSh3Yiwgc2hlZXQgPSAiUHJvZml0X0RhdGEiLCBwcm9maXRfZGF0YSkNCg0KIyBHaGkgZmlsZSBFeGNlbCByYSDEkcSpYQ0Kc2F2ZVdvcmtib29rKHdiLCAiVnVvbmdOaHVUaHV5MjIyMTAwMDMyOV9kdWxpZXUueGxzeCIsIG92ZXJ3cml0ZSA9IFRSVUUpDQpgYGANCg0KYGBge3J9DQpkYXRhdGFibGUoZGF0YSkNCmRhdGF0YWJsZShwcm9maXRfZGF0YSkNCmBgYA0KDQojIFRo4buRbmcga8OqIG3DtCB04bqjIGPDoWMgYmnhur9uDQojIyBU4buVbmcgcXVhbg0KYGBge3J9DQpza2ltKGRhdGEpICU+JSBzdW1tYXJ5KCkNCmBgYA0KDQojIyBUaOG7kW5nIGvDqiBtw7QgdOG6oyBiaeG6v24gTG/huqFpIHPhuqNuIHBo4bqpbSANCmBgYHtyfQ0KZGF0YSAlPiUNCiAgY291bnQoTG9haVNhblBoYW0pICU+JQ0KICBtdXRhdGUoUGVyY2VudCA9IHJvdW5kKG4gLyBzdW0obikgKiAxMDAsIDIpKSAlPiUNCiAga2FibGUoY2FwdGlvbiA9ICJC4bqjbmcgdOG6p24gc+G7kSB2w6AgdOG6p24gc3XhuqV0IHRoZW8gTG/huqFpIHPhuqNuIHBo4bqpbSIpDQpgYGANCg0KYGBge3J9DQojIDEuIEJp4buDdSDEkeG7kyBj4buZdCAoS0jDlE5HIGhp4buDbiB0aOG7iyBsZWdlbmQpDQpwbG90X2JhciA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSBMb2FpU2FuUGhhbSwgZmlsbCA9IExvYWlTYW5QaGFtKSkgKw0KICBnZW9tX2JhcigpICsNCiAgZ2VvbV90ZXh0KHN0YXQgPSAnY291bnQnLCBhZXMobGFiZWwgPSBhZnRlcl9zdGF0KGNvdW50KSksICBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSkgKw0KICBsYWJzKHRpdGxlID0gIlPhu5AgTMav4buiTkcgxJDGoE4gSMOATkciLA0KICAgICAgIHggPSBOVUxMLA0KICAgICAgIHkgPSBOVUxMKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0NCIpICsgICMgRMO5bmcgcGFsZXR0ZSBjaHVuZw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICAjIOG6qG4gbGVnZW5kDQoNCiMgMi4gQmnhu4N1IMSR4buTIHRyw7JuIChISeG7gk4gVEjhu4ogbGVnZW5kKQ0KcGxvdF9waWUgPC0gZ2dwbG90KGRhdGEsIGFlcyh4ID0gIiIsIGZpbGwgPSBMb2FpU2FuUGhhbSkpICsNCiAgZ2VvbV9iYXIod2lkdGggPSAwLjUpICsNCiAgY29vcmRfcG9sYXIoInkiKSArDQogIGdlb21fdGV4dChzdGF0ID0gImNvdW50IiwNCiAgICAgICAgICAgIGFlcyhsYWJlbCA9IHNjYWxlczo6cGVyY2VudChhZnRlcl9zdGF0KGNvdW50KS9zdW0oYWZ0ZXJfc3RhdChjb3VudCkpKSksDQogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSkgKw0KICBsYWJzKHRpdGxlID0gIlThu7YgTOG7hiBQSMOCTiBC4buQIiwNCiAgICAgICB4ID0gTlVMTCwNCiAgICAgICB5ID0gTlVMTCkgKw0KICB0aGVtZV92b2lkKCkgKw0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDQiKSAgIyBEw7luZyBDw5lORyBwYWxldHRlIHbhu5tpIGJp4buDdSDEkeG7kyBj4buZdA0KDQojIEvhur90IGjhu6NwIC0gY2jhu4kgaGnhu4NuIHRo4buLIDEgbGVnZW5kDQpjb21iaW5lZF9wbG90IDwtIHBsb3RfYmFyICsgcGxvdF9waWUgDQoNCnByaW50KGNvbWJpbmVkX3Bsb3QpDQpgYGANCg0KIyMgVGjhu5FuZyBrw6ogbcO0IHThuqMgYmnhur9uIExv4bqhaSBnaeG6pXkNCmBgYHtyfQ0KZGF0YSAlPiUNCiAgY291bnQoTG9haUdpYXkpICU+JQ0KICBtdXRhdGUoUGVyY2VudCA9IHJvdW5kKG4gLyBzdW0obikgKiAxMDAsIDIpKSAlPiUNCiAga2FibGUoY2FwdGlvbiA9ICJC4bqjbmcgdOG6p24gc+G7kSB2w6AgdOG6p24gc3XhuqV0IHRoZW8gTG/huqFpIGdp4bqleSIpDQpgYGANCg0KYGBge3J9DQojIDEuIEJp4buDdSDEkeG7kyBj4buZdCAoS0jDlE5HIGhp4buDbiB0aOG7iyBsZWdlbmQpDQpwbG90X2JhciA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSBMb2FpR2lheSwgZmlsbCA9IExvYWlHaWF5KSkgKw0KICBnZW9tX2JhcigpICsNCiAgZ2VvbV90ZXh0KHN0YXQgPSAnY291bnQnLCBhZXMobGFiZWwgPSBhZnRlcl9zdGF0KGNvdW50KSksICBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSkgKw0KICBsYWJzKHRpdGxlID0gIlPhu5AgTMav4buiTkcgxJDGoE4gSMOATkciLA0KICAgICAgIHggPSBOVUxMLA0KICAgICAgIHkgPSBOVUxMKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0NCIpICsgICMgRMO5bmcgcGFsZXR0ZSBjaHVuZw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsICAjIOG6qG4gbGVnZW5kDQogIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBhbmdsZSA9IDE1LCBoanVzdCA9IDEpKQ0KIA0KIyAyLiBCaeG7g3UgxJHhu5MgdHLDsm4gKEhJ4buCTiBUSOG7iiBsZWdlbmQpDQpwbG90X3BpZSA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSAiIiwgZmlsbCA9IExvYWlHaWF5KSkgKw0KICBnZW9tX2Jhcih3aWR0aCA9IDAuNSkgKw0KICBjb29yZF9wb2xhcigieSIpICsNCiAgZ2VvbV90ZXh0KHN0YXQgPSAiY291bnQiLA0KICAgICAgICAgICAgYWVzKGxhYmVsID0gc2NhbGVzOjpwZXJjZW50KGFmdGVyX3N0YXQoY291bnQpL3N1bShhZnRlcl9zdGF0KGNvdW50KSkpKSwNCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCBzaXplID0gMi41KSArDQogIGxhYnModGl0bGUgPSAiVOG7tiBM4buGIFBIw4JOIELhu5AiLA0KICAgICAgIHggPSBOVUxMLA0KICAgICAgIHkgPSBOVUxMKSArDQogIHRoZW1lX3ZvaWQoKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0NCIpICAjIETDuW5nIEPDmU5HIHBhbGV0dGUgduG7m2kgYmnhu4N1IMSR4buTIGPhu5l0DQoNCiMgS+G6v3QgaOG7o3AgLSBjaOG7iSBoaeG7g24gdGjhu4sgMSBsZWdlbmQNCmNvbWJpbmVkX3Bsb3QgPC0gcGxvdF9iYXIgKyBwbG90X3BpZSANCg0KcHJpbnQoY29tYmluZWRfcGxvdCkNCmBgYA0KDQoNCiMjIFRo4buRbmcga8OqIG3DtCB04bqjIGJp4bq/biBMb+G6oWkgc+G6o24gcGjhuqltIHbDoCBT4buRIGzGsOG7o25nIMSR4bq3dCBow6BuZw0KYGBge3J9DQpzdGF0c190YWJsZSA8LSBkYXRhICU+JQ0KICBncm91cF9ieShMb2FpU2FuUGhhbSkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBNZWFuID0gbWVhbihTb0x1b25nRGF0SGFuZywgbmEucm0gPSBUUlVFKSwNCiAgICBTRCA9IHNkKFNvTHVvbmdEYXRIYW5nLCBuYS5ybSA9IFRSVUUpLA0KICAgIE1lZGlhbiA9IG1lZGlhbihTb0x1b25nRGF0SGFuZywgbmEucm0gPSBUUlVFKSwNCiAgICBNaW4gPSBtaW4oU29MdW9uZ0RhdEhhbmcsIG5hLnJtID0gVFJVRSksDQogICAgTWF4ID0gbWF4KFNvTHVvbmdEYXRIYW5nLCBuYS5ybSA9IFRSVUUpDQogICkgJT4lDQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIH4gcm91bmQoLiwgMikpKSAgIyBMw6BtIHRyw7JuIDIgY2jhu68gc+G7kQ0KDQojIEhp4buDbiB0aOG7iyBi4bqjbmcNCnByaW50KHN0YXRzX3RhYmxlKQ0KYGBgDQoNCmBgYHtyfQ0KIyAxLiBCb3hwbG90IC0gUGjDom4gYuG7kSBz4buRIGzGsOG7o25nIMSR4bq3dCBow6BuZyB0aGVvIGxv4bqhaSBz4bqjbiBwaOG6qW0NCnAxIDwtIGdncGxvdChkYXRhLCBhZXMoeCA9IExvYWlTYW5QaGFtLCB5ID0gU29MdW9uZ0RhdEhhbmcsIGZpbGwgPSBMb2FpU2FuUGhhbSkpICsNCiAgZ2VvbV9ib3hwbG90KGFscGhhID0gMC43LCBvdXRsaWVyLnNoYXBlID0gMTkpICsNCiAgc3RhdF9zdW1tYXJ5KGZ1biA9IG1lYW4sIGdlb20gPSAicG9pbnQiLCBzaGFwZSA9IDE4LCBzaXplID0gMywgY29sb3IgPSAicmVkIikgKw0KICBsYWJzKHRpdGxlID0gIlBIw4JOIELhu5AgU+G7kCBMxq/hu6JORyDEkOG6tlQgSMOATkcgVEhFTyBMT+G6oEkgU1AiLA0KICAgICAgIHggPSBOVUxMLA0KICAgICAgIHkgPSAiU+G7kSBsxrDhu6NuZyDEkeG6t3QgaMOgbmciKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiUGFzdGVsMSIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQojIDIuIFZpb2xpbiBwbG90ICsgSml0dGVyIC0gS+G6v3QgaOG7o3AgcGjDom4gYuG7kSB2w6AgxJFp4buDbSBk4buvIGxp4buHdQ0KcDIgPC0gZ2dwbG90KGRhdGEsIGFlcyh4ID0gTG9haVNhblBoYW0sIHkgPSBTb0x1b25nRGF0SGFuZywgZmlsbCA9IExvYWlTYW5QaGFtKSkgKw0KICBnZW9tX3Zpb2xpbihhbHBoYSA9IDAuNSwgdHJpbSA9IEZBTFNFKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gMC4yLCBhbHBoYSA9IDAuMywgc2l6ZSA9IDEuNSkgKw0KICBsYWJzKHRpdGxlID0gIk3huqxUIMSQ4buYIFBIw4JOIELhu5AgS8OITSBEQVRBIFBPSU5UUyIsDQogICAgICAgeCA9IE5VTEwsDQogICAgICAgeSA9IE5VTEwpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCiMgMy4gQmFycGxvdCAoTWVhbiArIFNEKQ0KcDMgPC0gZ2dwbG90KGRhdGEsIGFlcyh4ID0gTG9haVNhblBoYW0sIHkgPSBTb0x1b25nRGF0SGFuZywgZmlsbCA9IExvYWlTYW5QaGFtKSkgKw0KICBzdGF0X3N1bW1hcnkoZnVuID0gbWVhbiwgZ2VvbSA9ICJiYXIiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsNCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gbWVhbl9zZGwsIA0KICAgICAgICAgICAgICAgZ2VvbSA9ICJlcnJvcmJhciIsIA0KICAgICAgICAgICAgICAgd2lkdGggPSAwLjIsDQogICAgICAgICAgICAgICBjb2xvciA9ICJncmF5MzAiKSArDQogIGxhYnModGl0bGUgPSAiR0nDgSBUUuG7iiBUUlVORyBCw4xOSCDCsSDEkOG7mCBM4buGQ0ggQ0hV4bqoTiIsDQogICAgICAgeCA9IE5VTEwsDQogICAgICAgeSA9ICJT4buRIGzGsOG7o25nIMSR4bq3dCBow6BuZyAoTWVhbiDCsSBTRCkiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiQWNjZW50IikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCiMgNC4gUGjDom4gYuG7kSB04bqnbiBzdeG6pXQgKEhpc3RvZ3JhbSBwaMOibiBuaMOzbSkNCnA0IDwtIGdncGxvdChkYXRhLCBhZXMoeCA9IFNvTHVvbmdEYXRIYW5nLCBmaWxsID0gTG9haVNhblBoYW0pKSArDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMTAwLCANCiAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjcsIA0KICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9ICJpZGVudGl0eSIsDQogICAgICAgICAgICAgICAgIGNvbG9yID0gIndoaXRlIikgKw0KICBmYWNldF93cmFwKH5Mb2FpU2FuUGhhbSwgbmNvbCA9IDEpICsNCiAgbGFicyh0aXRsZSA9ICJQSMOCTiBC4buQIFThuqZOIFPhu5AgQ+G7pkEgU+G7kCBMxq/hu6JORyDEkOG6tlQgSMOATkciLA0KICAgICAgIHggPSAiU+G7kSBsxrDhu6NuZyDEkeG6t3QgaMOgbmciLA0KICAgICAgIHkgPSAiVOG6p24gc+G7kSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpDQoNCiMgS+G6v3QgaOG7o3AgNCBiaeG7g3UgxJHhu5MNCmNvbWJpbmVkX3Bsb3QgPC0gKHAxICsgcDIpIC8gKHAzICsgcDQpICsgDQogIHBsb3RfbGF5b3V0KGhlaWdodHMgPSBjKDEsIDEuNSkpICsNCiAgcGxvdF9hbm5vdGF0aW9uKHRpdGxlID0gIlRI4buQTkcgS8OKIE3DlCBU4bqiOiBMT+G6oEkgU1AgdnMgU+G7kCBMxq/hu6JORyDEkOG6tlQgSMOATkciLA0KICAgICAgICAgICAgICAgICAgdGhlbWUgPSB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpKSkNCg0KIyBYdeG6pXQgZmlsZSDhuqNuaCBjaOG6pXQgbMaw4bujbmcgY2FvICh0w7l5IGNo4buNbikNCmdnc2F2ZSgiTG9haVNhblBoYW1fU29MdW9uZ0RhdEhhbmcucG5nIiwgY29tYmluZWRfcGxvdCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTAsIGRwaSA9IDMwMCkNCmBgYA0KDQpgYGB7ciwgb3V0LndpZHRoPSIxMDAlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJMb2FpU2FuUGhhbV9Tb0x1b25nRGF0SGFuZy5wbmciLCBlcnJvciA9IEZBTFNFKQ0KYGBgDQoNCmBgYHtyfQ0KZGF0YSAlPiUNCiAgZmlsdGVyKExvYWlTYW5QaGFtICVpbiUgYygiRGVjYWwiLCAiQmnhu4N1IG3huqt1IiwgIkNhdGFsb2ciKSkgJT4lDQogIGNvdW50KExvYWlTYW5QaGFtLCBTb0x1b25nRGF0SGFuZykgJT4lDQogIGdyb3VwX2J5KExvYWlTYW5QaGFtKSAlPiUNCiAgZ3QoKSAlPiUNCiAgdGFiX2hlYWRlcigNCiAgICB0aXRsZSA9ICJC4bqiTkcgVEjhu5BORyBLw4ogVOG6pk4gU+G7kCBD4bumQSBT4buQIEzGr+G7ok5HIMSQ4bq2VCBIw4BORyIsDQogICAgc3VidGl0bGUgPSAiUGjDom4gdGhlbyBMb+G6oWkgc+G6o24gcGjhuqltIg0KICApICU+JQ0KICBmbXRfbnVtYmVyKGNvbHVtbnMgPSBuLCBkZWNpbWFscyA9IDApICU+JQ0KICBjb2xzX2xhYmVsKA0KICAgIExvYWlTYW5QaGFtID0gIkxv4bqhaSBz4bqjbiBwaOG6qW0iLA0KICAgIFNvTHVvbmdEYXRIYW5nID0gIlPhu5EgbMaw4bujbmcgxJHhurd0IGjDoG5nIiwNCiAgICBuID0gIlThuqduIHPhu5EiDQogICkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBM4buNYyBEZWNhbCB2w6AgdGjhu5FuZyBrw6ogdOG6p24gc+G7kQ0KcGxvdF9mcmVxIDwtIGRhdGEgJT4lDQogIGZpbHRlcihMb2FpU2FuUGhhbSA9PSAiRGVjYWwiKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gU29MdW9uZ0RhdEhhbmcpKSArDQogIGdlb21fYmFyKGZpbGwgPSAic3RlZWxibHVlIikgKw0KICBnZW9tX3RleHQoc3RhdCA9ICdjb3VudCcsIGFlcyhsYWJlbCA9IGFmdGVyX3N0YXQoY291bnQpKSwgDQogICAgICAgICAgICB2anVzdCA9IC0wLjUsIHNpemUgPSAzKSArDQogIGxhYnModGl0bGUgPSAiVOG6pk4gU+G7kCBT4buQIEzGr+G7ok5HIMSQ4bq2VCBIw4BORyBERUNBTCIsDQogICAgICAgeCA9ICJT4buRIGzGsOG7o25nIMSR4bq3dCBow6BuZyIsDQogICAgICAgeSA9ICJU4bqnbiBz4buRIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KcHJpbnQocGxvdF9mcmVxKQ0KYGBgDQoNCmBgYHtyfQ0KIyBM4buNYyBCaeG7g3UgbeG6q3UgdsOgIHRo4buRbmcga8OqIHThuqduIHPhu5ENCnBsb3RfZnJlcSA8LSBkYXRhICU+JQ0KICBmaWx0ZXIoTG9haVNhblBoYW0gPT0gIkJp4buDdSBt4bqrdSIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBTb0x1b25nRGF0SGFuZykpICsNCiAgZ2VvbV9iYXIoZmlsbCA9ICJzdGVlbGJsdWUiKSArDQogIGdlb21fdGV4dChzdGF0ID0gJ2NvdW50JywgYWVzKGxhYmVsID0gYWZ0ZXJfc3RhdChjb3VudCkpLCANCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDMpICsNCiAgbGFicyh0aXRsZSA9ICJU4bqmTiBT4buQIFPhu5AgTMav4buiTkcgxJDhurZUIEjDgE5HIEJJ4buCVSBN4bqqVSIsDQogICAgICAgeCA9ICJT4buRIGzGsOG7o25nIMSR4bq3dCBow6BuZyIsDQogICAgICAgeSA9ICJU4bqnbiBz4buRIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KcHJpbnQocGxvdF9mcmVxKQ0KYGBgDQoNCmBgYHtyfQ0KIyBM4buNYyBDYXRhbG9nIHbDoCB0aOG7kW5nIGvDqiB04bqnbiBz4buRDQpwbG90X2ZyZXEgPC0gZGF0YSAlPiUNCiAgZmlsdGVyKExvYWlTYW5QaGFtID09ICJDYXRhbG9nIikgJT4lDQogIGdncGxvdChhZXMoeCA9IFNvTHVvbmdEYXRIYW5nKSkgKw0KICBnZW9tX2JhcihmaWxsID0gInN0ZWVsYmx1ZSIpICsNCiAgZ2VvbV90ZXh0KHN0YXQgPSAnY291bnQnLCBhZXMobGFiZWwgPSBhZnRlcl9zdGF0KGNvdW50KSksIA0KICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBzaXplID0gMykgKw0KICBsYWJzKHRpdGxlID0gIlThuqZOIFPhu5AgU+G7kCBMxq/hu6JORyDEkOG6tlQgSMOATkcgQ0FUQUxPRyIsDQogICAgICAgeCA9ICJT4buRIGzGsOG7o25nIMSR4bq3dCBow6BuZyIsDQogICAgICAgeSA9ICJU4bqnbiBz4buRIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KcHJpbnQocGxvdF9mcmVxKQ0KYGBgDQoNCiMjIFRo4buRbmcga8OqIG3DtCB04bqjIGJp4bq/biBMb+G6oWkgc+G6o24gcGjhuqltIHbDoCBT4buRIHRyYW5nDQpgYGB7cn0NCnN0YXRzX3RhYmxlIDwtIGRhdGEgJT4lDQogIGdyb3VwX2J5KExvYWlTYW5QaGFtKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIE1lYW4gPSBtZWFuKFNvVHJhbmcsIG5hLnJtID0gVFJVRSksDQogICAgU0QgPSBzZChTb1RyYW5nLCBuYS5ybSA9IFRSVUUpLA0KICAgIE1lZGlhbiA9IG1lZGlhbihTb1RyYW5nLCBuYS5ybSA9IFRSVUUpLA0KICAgIE1pbiA9IG1pbihTb1RyYW5nLCBuYS5ybSA9IFRSVUUpLA0KICAgIE1heCA9IG1heChTb1RyYW5nLCBuYS5ybSA9IFRSVUUpDQogICkgJT4lDQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIH4gcm91bmQoLiwgMikpKSAgIyBMw6BtIHRyw7JuIDIgY2jhu68gc+G7kQ0KDQojIEhp4buDbiB0aOG7iyBi4bqjbmcNCnByaW50KHN0YXRzX3RhYmxlKQ0KYGBgDQoNCmBgYHtyfQ0KIyAxLiBCb3hwbG90IC0gR2nhu68gbmd1ecOqbg0KcDEgPC0gZ2dwbG90KGRhdGEsIGFlcyh4ID0gTG9haVNhblBoYW0sIHkgPSBTb1RyYW5nLCBmaWxsID0gTG9haVNhblBoYW0pKSArDQogIGdlb21fYm94cGxvdChhbHBoYSA9IDAuNywgb3V0bGllci5zaGFwZSA9IDE5KSArDQogIHN0YXRfc3VtbWFyeShmdW4gPSBtZWFuLCBnZW9tID0gInBvaW50Iiwgc2hhcGUgPSAxOCwgc2l6ZSA9IDMsIGNvbG9yID0gInJlZCIpICsNCiAgbGFicyh0aXRsZSA9ICJQSMOCTiBC4buQIFPhu5AgVFJBTkcgVEhFTyBMT+G6oEkgU1AiLA0KICAgICAgIHggPSBOVUxMLA0KICAgICAgIHkgPSAiU+G7kSB0cmFuZyIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJQYXN0ZWwxIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCiMgMi4gVmlvbGluIHBsb3QgLSBHaeG7ryBuZ3V5w6puDQpwMiA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSBMb2FpU2FuUGhhbSwgeSA9IFNvVHJhbmcsIGZpbGwgPSBMb2FpU2FuUGhhbSkpICsNCiAgZ2VvbV92aW9saW4oYWxwaGEgPSAwLjUsIHRyaW0gPSBGQUxTRSkgKw0KICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMiwgYWxwaGEgPSAwLjMsIHNpemUgPSAxLjUpICsNCiAgbGFicyh0aXRsZSA9ICJN4bqsVCDEkOG7mCBQSMOCTiBC4buQIEvDiE0gREFUQSBQT0lOVFMiLA0KICAgICAgIHggPSBOVUxMLA0KICAgICAgIHkgPSBOVUxMKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQojIDMuIEJhcnBsb3QgLSBHaeG7ryBuZ3V5w6puDQpwMyA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSBMb2FpU2FuUGhhbSwgeSA9IFNvVHJhbmcsIGZpbGwgPSBMb2FpU2FuUGhhbSkpICsNCiAgc3RhdF9zdW1tYXJ5KGZ1biA9IG1lYW4sIGdlb20gPSAiYmFyIiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArDQogIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IG1lYW5fc2RsLCANCiAgICAgICAgICAgICAgIGdlb20gPSAiZXJyb3JiYXIiLCANCiAgICAgICAgICAgICAgIHdpZHRoID0gMC4yLA0KICAgICAgICAgICAgICAgY29sb3IgPSAiZ3JheTMwIikgKw0KICBsYWJzKHRpdGxlID0gIkdJw4EgVFLhu4ogVFJVTkcgQsOMTkggwrEgxJDhu5ggTOG7hkNIIENIVeG6qE4iLA0KICAgICAgIHggPSBOVUxMLA0KICAgICAgIHkgPSAiU+G7kSB0cmFuZyAoTWVhbiDCsSBTRCkiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiQWNjZW50IikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCiMgNC4gU+G7rWEgbOG6oWkgaGlzdG9ncmFtIHBow6JuIG5ow7NtDQpwNCA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSBTb1RyYW5nLCBmaWxsID0gTG9haVNhblBoYW0pKSArDQogIGdlb21fYmFyKHN0YXQgPSAiY291bnQiLCBhbHBoYSA9IDAuNywgcG9zaXRpb24gPSAiZG9kZ2UiLCBjb2xvciA9ICJ3aGl0ZSIpICsNCiAgZmFjZXRfd3JhcCh+TG9haVNhblBoYW0sIG5jb2wgPSAxLCBzY2FsZXMgPSAiZnJlZV95IikgKw0KICBsYWJzKHRpdGxlID0gIlBIw4JOIELhu5AgVOG6pk4gU+G7kCBD4bumQSBT4buQIFRSQU5HIiwNCiAgICAgICB4ID0gIlPhu5EgdHJhbmciLA0KICAgICAgIHkgPSAiVOG6p24gc+G7kSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2NhbGVzOjpwcmV0dHlfYnJlYWtzKG4gPSAxMCkpICMgSGnhu4NuIHRo4buLIG5oaeG7gXUgZ2nDoSB0cuG7iyBoxqFuIHRyw6puIHRy4bulYyB4DQoNCiMgS+G6v3QgaOG7o3AgNCBiaeG7g3UgxJHhu5MNCmNvbWJpbmVkX3Bsb3QgPC0gKHAxICsgcDIpIC8gKHAzICsgcDQpICsgDQogIHBsb3RfbGF5b3V0KGhlaWdodHMgPSBjKDEsIDEuNSkpICsNCiAgcGxvdF9hbm5vdGF0aW9uKHRpdGxlID0gIlRI4buQTkcgS8OKIE3DlCBU4bqiOiBMT+G6oEkgU1AgdnMgU+G7kCBUUkFORyIsDQogICAgICAgICAgICAgICAgICB0aGVtZSA9IHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIikpKQ0KDQojIFh14bqldCBmaWxlDQpnZ3NhdmUoIkxvYWlTYW5QaGFtX1NvVHJhbmcucG5nIiwgY29tYmluZWRfcGxvdCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTIsIGRwaSA9IDMwMCkNCmBgYA0KDQpgYGB7ciwgb3V0LndpZHRoPSIxMDAlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJMb2FpU2FuUGhhbV9Tb1RyYW5nLnBuZyIsIGVycm9yID0gRkFMU0UpDQpgYGANCg0KYGBge3J9DQpkYXRhICU+JQ0KICBmaWx0ZXIoTG9haVNhblBoYW0gJWluJSBjKCJEZWNhbCIsICJCaeG7g3UgbeG6q3UiLCAiQ2F0YWxvZyIpKSAlPiUNCiAgY291bnQoTG9haVNhblBoYW0sIFNvVHJhbmcpICU+JQ0KICBncm91cF9ieShMb2FpU2FuUGhhbSkgJT4lDQogIGd0KCkgJT4lDQogIHRhYl9oZWFkZXIoDQogICAgdGl0bGUgPSAiQuG6ok5HIFRI4buQTkcgS8OKIFThuqZOIFPhu5AgQ+G7pkEgU+G7kCBUUkFORyIsDQogICAgc3VidGl0bGUgPSAiUGjDom4gdGhlbyBMb+G6oWkgc+G6o24gcGjhuqltIg0KICApICU+JQ0KICBmbXRfbnVtYmVyKGNvbHVtbnMgPSBuLCBkZWNpbWFscyA9IDApICU+JQ0KICBjb2xzX2xhYmVsKA0KICAgIExvYWlTYW5QaGFtID0gIkxv4bqhaSBz4bqjbiBwaOG6qW0iLA0KICAgIFNvVHJhbmcgPSAiU+G7kSB0cmFuZyIsDQogICAgbiA9ICJU4bqnbiBz4buRIg0KICApDQpgYGANCg0KYGBge3J9DQojIEzhu41jIEJp4buDdSBt4bqrdSB2w6AgdGjhu5FuZyBrw6ogdOG6p24gc+G7kQ0KcGxvdF9mcmVxIDwtIGRhdGEgJT4lDQogIGZpbHRlcihMb2FpU2FuUGhhbSA9PSAiQmnhu4N1IG3huqt1IikgJT4lDQogIGdncGxvdChhZXMoeCA9IFNvVHJhbmcpKSArDQogIGdlb21fYmFyKGZpbGwgPSAic3RlZWxibHVlIikgKw0KICBnZW9tX3RleHQoc3RhdCA9ICdjb3VudCcsIGFlcyhsYWJlbCA9IGFmdGVyX3N0YXQoY291bnQpKSwgDQogICAgICAgICAgICB2anVzdCA9IC0wLjUsIHNpemUgPSAzKSArDQogIGxhYnModGl0bGUgPSAiVOG6pk4gU+G7kCBT4buQIFRSQU5HIEJJ4buCVSBN4bqqVSIsDQogICAgICAgeCA9ICJT4buRIHRyYW5nIiwNCiAgICAgICB5ID0gIlThuqduIHPhu5EiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpwcmludChwbG90X2ZyZXEpDQpgYGANCg0KYGBge3J9DQojIEzhu41jIENhdGFsb2cgdsOgIHRo4buRbmcga8OqIHThuqduIHPhu5ENCnBsb3RfZnJlcSA8LSBkYXRhICU+JQ0KICBmaWx0ZXIoTG9haVNhblBoYW0gPT0gIkNhdGFsb2ciKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gU29UcmFuZykpICsNCiAgZ2VvbV9iYXIoZmlsbCA9ICJzdGVlbGJsdWUiKSArDQogIGdlb21fdGV4dChzdGF0ID0gJ2NvdW50JywgYWVzKGxhYmVsID0gYWZ0ZXJfc3RhdChjb3VudCkpLCANCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDMpICsNCiAgbGFicyh0aXRsZSA9ICJU4bqmTiBT4buQIFPhu5AgVFJBTkcgQ0FUQUxPRyIsDQogICAgICAgeCA9ICJT4buRIHRyYW5nIiwNCiAgICAgICB5ID0gIlThuqduIHPhu5EiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpwcmludChwbG90X2ZyZXEpDQpgYGANCg0KIyMgVGjhu5FuZyBrw6ogbcO0IHThuqMgY8OhYyBiaeG6v24gxJHhu4tuaCBsxrDhu6NuZyBjw7JuIGzhuqFpIA0KYGBge3J9DQpzdW1tYXJ5KGRhdGEkVHlMZUxvaSkNCnN1bW1hcnkoZGF0YSRTb0x1b25nTG9pKQ0Kc3VtbWFyeShkYXRhJERvYW5oVGh1KQ0Kc3VtbWFyeShkYXRhJENoaVBoaUJpZW5Eb25nKQ0KDQpzdW1tYXJ5KHByb2ZpdF9kYXRhJFRvbmdEb2FuaFRodSkNCnN1bW1hcnkocHJvZml0X2RhdGEkVG9uZ0NoaVBoaUJpZW5Eb25nKQ0Kc3VtbWFyeShwcm9maXRfZGF0YSRMb2lOaHVhbikNCmBgYA0KDQoNCg0KIyBQaMOibiBwaOG7kWkgdsOgIGRp4buFbiBiaeG6v24gY+G7p2EgTOG7o2kgbmh14bqtbiB0aGVvIHRo4budaSBnaWFuIA0KYGBge3J9DQojIDEuIFBow6JuIHBo4buRaSBs4bujaSBuaHXhuq1uDQpnZ3Bsb3QocHJvZml0X2RhdGEsIGFlcyh4ID0gTG9pTmh1YW4pKSArDQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAzMCwgZmlsbCA9ICJzdGVlbGJsdWUiLCBhbHBoYSA9IDAuNykgKw0KICBsYWJzKHRpdGxlID0gIlBow6JuIHBo4buRaSBM4bujaSBuaHXhuq1uIGjDoG5nIG5nw6B5IiwNCiAgICAgICB4ID0gIkzhu6NpIG5odeG6rW4gKFZORCkiLCANCiAgICAgICB5ID0gIlThuqduIHPhu5EiKSArDQogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmNvbW1hKSArICAjIEhp4buDbiB0aOG7iyBz4buRIMSR4bqneSDEkeG7pw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpjb21tYSkgKyAgIyDDgXAgZOG7pW5nIGNobyBj4bqjIHRy4bulYyB5IG7hur91IGPhuqduDQogIHRoZW1lX21pbmltYWwoKQ0KDQojIDIuIFh1IGjGsOG7m25nIHRo4budaSBnaWFuDQpnZ3Bsb3QocHJvZml0X2RhdGEsIGFlcyh4ID0gTmdheSwgeSA9IExvaU5odWFuKSkgKw0KICBnZW9tX2xpbmUoY29sb3IgPSAidG9tYXRvIikgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBzZSA9IEZBTFNFKSArDQogIGxhYnModGl0bGUgPSAiRGnhu4VuIGJp4bq/biBM4bujaSBuaHXhuq1uIHRoZW8gdGjhu51pIGdpYW4iLA0KICAgICAgIHggPSAiTmfDoHkiLCANCiAgICAgICB5ID0gIkzhu6NpIG5odeG6rW4gKFZORCkiKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmNvbW1hKSArICAjIEhp4buDbiB0aOG7iyBz4buRIMSR4bqneSDEkeG7pw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQojIE3DtCBwaOG7j25nIGPDoWMgdsOybmcgbOG6t3AgY+G7p2EgbOG7o2kgbmh14bqtbg0KYGBge3J9DQojIFThuqFvIGjDoG0gbOG6pXkgbeG6q3Ugbmfhuqt1IG5oacOqbiB2w6AgdMOtbmggbOG7o2kgbmh14bqtbiB0cnVuZyBiw6xuaA0Kc2ltdWxhdGVfcHJvZml0IDwtIGZ1bmN0aW9uKG4sIHByb2ZpdF9kYXRhKSB7DQogIG1lYW5zIDwtIG51bWVyaWMobikNCiAgZm9yIChpIGluIDE6bikgew0KICAgIHNhbXBsZWRfZGF5cyA8LSBzYW1wbGUocHJvZml0X2RhdGEkTG9pTmh1YW4sIHNpemUgPSBuLCByZXBsYWNlID0gVFJVRSkNCiAgICBtZWFuc1tpXSA8LSBtZWFuKHNhbXBsZWRfZGF5cykNCiAgfQ0KICByZXR1cm4obWVhbnMpDQp9DQoNCiMgRGFuaCBzw6FjaCBz4buRIHbDsm5nIGzhurdwDQppdGVyYXRpb25zIDwtIGMoMTAsIDEwMCwgMTAwMCwgMTAwMDApDQoNCiMgVGjhu7FjIGhp4buHbiBtw7QgcGjhu49uZw0Kc2ltdWxhdGlvbnMgPC0gbGFwcGx5KGl0ZXJhdGlvbnMsIGZ1bmN0aW9uKG4pIHNpbXVsYXRlX3Byb2ZpdChuLCBwcm9maXRfZGF0YSkpDQoNCiMgQ2h14bqpbiBi4buLIGThu68gbGnhu4d1IMSR4buDIHbhur0gYmnhu4N1IMSR4buTDQpzaW1fZGYgPC0gZGF0YS5mcmFtZSgNCiAgSXRlcmF0aW9uID0gcmVwKGl0ZXJhdGlvbnMsIHRpbWVzID0gc2FwcGx5KHNpbXVsYXRpb25zLCBsZW5ndGgpKSwNCiAgRXN0aW1hdGVkUHJvZml0ID0gdW5saXN0KHNpbXVsYXRpb25zKQ0KKQ0KDQojIEfDoW4gdMOqbiBk4buFIGhp4buDdQ0Kc2ltX2RmJExhYmVsIDwtIHBhc3RlMCgibiA9ICIsIHNpbV9kZiRJdGVyYXRpb24pDQoNCiMgVuG6vSBiaeG7g3UgxJHhu5MgZ2nhu5FuZyBow6xuaCDhuqNuaCDPgA0KZ2dwbG90KHNpbV9kZiwgYWVzKHggPSBMYWJlbCwgeSA9IEVzdGltYXRlZFByb2ZpdCkpICsNCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjEsIGhlaWdodCA9IDAsIGFscGhhID0gMC40LCBjb2xvciA9ICJzdGVlbGJsdWUiKSArDQogIHN0YXRfc3VtbWFyeShmdW4gPSBtZWFuLCBnZW9tID0gInBvaW50Iiwgc2l6ZSA9IDUsIGNvbG9yID0gImRhcmtyZWQiKSArDQogIGxhYnModGl0bGUgPSAixq/hu5tjIGzGsOG7o25nIGzhu6NpIG5odeG6rW4gdHJ1bmcgYsOsbmgiLA0KICAgICAgIHggPSAiU+G7kSBs4bqnbiBtw7QgcGjhu49uZyAobikiLA0KICAgICAgIHkgPSAiTOG7o2kgbmh14bqtbiB0cnVuZyBiw6xuaC9uZ8OgeSAoVk5EKSIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6Y29tbWEpICsNCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxNCkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBIw6BtIG3DtCBwaOG7j25nIGzhu6NpIG5odeG6rW4gdHJ1bmcgYsOsbmgNCnNpbXVsYXRlX2FuZF9wbG90IDwtIGZ1bmN0aW9uKG4sIHByb2ZpdF9kYXRhKSB7DQogIG1lYW5zIDwtIG51bWVyaWMobikNCiAgZm9yIChpIGluIDE6bikgew0KICAgIHNhbXBsZWRfZGF5cyA8LSBzYW1wbGUocHJvZml0X2RhdGEkTG9pTmh1YW4sIHNpemUgPSBuLCByZXBsYWNlID0gVFJVRSkNCiAgICBtZWFuc1tpXSA8LSBtZWFuKHNhbXBsZWRfZGF5cykNCiAgfQ0KICBtZWFuX2VzdCA8LSBtZWFuKG1lYW5zKQ0KICANCiAgIyBU4bqhbyBiaeG7g3UgxJHhu5MNCiAgcCA8LSBnZ3Bsb3QoZGF0YS5mcmFtZSh4ID0gbWVhbnMpLCBhZXMoeCA9IHgpKSArDQogICAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDMwLCBmaWxsID0gIiMzNzdlYjgiLCBhbHBoYSA9IDAuNywgY29sb3IgPSAiYmxhY2siKSArDQogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbWVhbl9lc3QsIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgICBsYWJzKA0KICAgICAgdGl0bGUgPSBwYXN0ZTAobiwgIiBM4bqnbiBNw7QgUGjhu49uZyIpLA0KICAgICAgc3VidGl0bGUgPSBwYXN0ZTAoIkzhu6NpIG5odeG6rW4gVEIg4omIICIsIHJvdW5kKG1lYW5fZXN0LCAwKSwgIiBWTkQiKSwNCiAgICAgIHggPSBOVUxMLA0KICAgICAgeSA9IE5VTEwNCiAgICApICsNCiAgICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpjb21tYSkgKw0KICAgIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTMpDQogIHJldHVybihwKQ0KfQ0KDQojIERhbmggc8OhY2ggc+G7kSB2w7JuZyBs4bq3cA0KaXRlcmF0aW9ucyA8LSBjKDEwLCAxMDAsIDEwMDAsIDEwMDAwKQ0KDQojIFThuqFvIGRhbmggc8OhY2ggYmnhu4N1IMSR4buTDQpwbG90cyA8LSBsYXBwbHkoaXRlcmF0aW9ucywgZnVuY3Rpb24obikgc2ltdWxhdGVfYW5kX3Bsb3QobiwgcHJvZml0X2RhdGEpKQ0KDQojIETDuW5nIHBhdGNod29yayDEkeG7gyBz4bqvcCB44bq/cCB0aMOgbmggMiBow6BuZyAzIGPhu5l0DQpmaW5hbF9wbG90IDwtIChwbG90c1tbMV1dICsgcGxvdHNbWzJdXSkgLyAocGxvdHNbWzNdXSArIHBsb3RzW1s0XV0pDQoNCiMgSGnhu4NuIHRo4buLDQpnZ3NhdmUoIk3DtCBwaOG7j25nIEzhu6NpIG5odeG6rW4ucG5nIiwgZmluYWxfcGxvdCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNikNCmBgYA0KDQpgYGB7ciwgb3V0LndpZHRoPSIxMDAlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJNw7QgcGjhu49uZyBM4bujaSBuaHXhuq1uLnBuZyIsIGVycm9yID0gRkFMU0UpDQpgYGANCg0KIyBDw6FjIGvhu4tjaCBi4bqjbiBj4bunYSBtw7QgaMOsbmggDQpgYGB7cn0NCiMgMS4gSMOATSBNw5QgUEjhu45ORyBM4buiSSBOSFXhuqxODQpzaW11bGF0ZV9wcm9maXRfbWVhbiA8LSBmdW5jdGlvbihwcm9maXRfdmVjdG9yLCBuKSB7DQogIHJlcGxpY2F0ZShuLCBtZWFuKHNhbXBsZShwcm9maXRfdmVjdG9yLCBuLCByZXBsYWNlID0gVFJVRSkpKQ0KfQ0KDQojIDIuIFThuqBPIEvhu4pDSCBC4bqiTiBN4buaSSAodGhheSDEkeG7lWkgZOG7ryBsaeG7h3UpDQoNCmNyZWF0ZV9tb2RpZmllZF9wcm9maXRfZGF0YSA8LSBmdW5jdGlvbihkYXRhLCBmYWN0b3JfZXJyb3IgPSAxLCBmYWN0b3JfY29zdCA9IDEsIGZhY3Rvcl9vcmRlciA9IDEpIHsNCiAgZGYgPC0gZGF0YQ0KICANCiAgIyBUxINuZy9naeG6o20gdOG7tyBs4buHIGzhu5dpDQogIGRmJFR5TGVMb2kgPC0gcG1pbihkZiRUeUxlTG9pICogZmFjdG9yX2Vycm9yLCAxKSAgIyBLaMO0bmcgdsaw4bujdCAxMDAlDQogIGRmJFNvTHVvbmdMb2kgPC0gcm91bmQoZGYkU29MdW9uZ0RhdEhhbmcgKiBkZiRUeUxlTG9pKQ0KICANCiAgIyBUxINuZy9naeG6o20gZ2nDoSB24buRbg0KICBkZiRHaWFWb24gPC0gZGYkR2lhVm9uICogZmFjdG9yX2Nvc3QNCiAgDQogICMgVMOtbmggbOG6oWkgY2hpIHBow60gYmnhur9uIMSR4buZbmcNCiAgZGYkQ2hpUGhpQmllbkRvbmcgPC0gbWFwcGx5KGZ1bmN0aW9uKHR5cGUsIGVycm9yX3EsIHBhZ2VzLCBjb3N0KSB7DQogICAgaWYgKHR5cGUgPT0gIkRlY2FsIikgZXJyb3JfcSAqIGNvc3QgZWxzZSBlcnJvcl9xICogcGFnZXMgKiBjb3N0DQogIH0sIGRmJExvYWlTYW5QaGFtLCBkZiRTb0x1b25nTG9pLCBkZiRTb1RyYW5nLCBkZiRHaWFWb24pDQogIA0KICAjIFTEg25nL2dp4bqjbSBz4buRIMSRxqFuIGjDoG5nL25nw6B5DQogIGRhaWx5X29yZGVycyA8LSBkZiAlPiUNCiAgICBncm91cF9ieShOZ2F5KSAlPiUNCiAgICBzdW1tYXJpc2UoDQogICAgICBUb25nRG9hbmhUaHUgPSBzdW0oRG9hbmhUaHUsIG5hLnJtID0gVFJVRSksDQogICAgICBUb25nQ2hpUGhpQmllbkRvbmcgPSBzdW0oQ2hpUGhpQmllbkRvbmcsIG5hLnJtID0gVFJVRSksDQogICAgICBDaGlQaGlDb0RpbmggPSAxNTAwMDAwMCwNCiAgICAgIC5ncm91cHMgPSAiZHJvcCINCiAgICApICU+JQ0KICAgIG11dGF0ZSgNCiAgICAgIExvaU5odWFuID0gKFRvbmdEb2FuaFRodSAqIGZhY3Rvcl9vcmRlcikgLSBDaGlQaGlDb0RpbmggLSAoVG9uZ0NoaVBoaUJpZW5Eb25nICogZmFjdG9yX29yZGVyKQ0KICAgICkNCiAgDQogIHJldHVybihkYWlseV9vcmRlcnMkTG9pTmh1YW4pDQp9DQoNCiMgMy4gVOG6oE8gS+G7ikNIIELhuqJOIFbDgCBNw5QgUEjhu45ORw0Kc2NlbmFyaW9zIDwtIGxpc3QoDQogICJCw6xuaCB0aMaw4budbmciID0gYygxLCAxLCAxKSwNCiAgIlTEg25nIGzhu5dpIiA9IGMoMS41LCAxLCAxKSwNCiAgIkdp4bqjbSBs4buXaSIgPSBjKDAuNSwgMSwgMSksDQogICJUxINuZyBnacOhIHbhu5FuIiA9IGMoMSwgMS4yLCAxKSwNCiAgIkdp4bqjbSBnacOhIHbhu5FuIiA9IGMoMSwgMC44LCAxKSwNCiAgIlTEg25nIMSRxqFuIGjDoG5nIiA9IGMoMSwgMSwgMS4zKSwNCiAgIkdp4bqjbSDEkcahbiBow6BuZyIgPSBjKDEsIDEsIDAuNykNCikNCg0KaXRlcmF0aW9ucyA8LSBjKDEwLCAxMDAsIDEwMDAsIDEwMDAwKQ0KDQojIEzGsHUga+G6v3QgcXXhuqMgbcO0IHBo4buPbmcNCnJlc3VsdF9saXN0IDwtIGxpc3QoKQ0KDQpmb3IgKHNjZW5hcmlvX25hbWUgaW4gbmFtZXMoc2NlbmFyaW9zKSkgew0KICBmYWN0b3JzIDwtIHNjZW5hcmlvc1tbc2NlbmFyaW9fbmFtZV1dDQogIHByb2ZpdF92ZWMgPC0gY3JlYXRlX21vZGlmaWVkX3Byb2ZpdF9kYXRhKGRhdGEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWN0b3JfZXJyb3IgPSBmYWN0b3JzWzFdLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjdG9yX2Nvc3QgPSBmYWN0b3JzWzJdLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjdG9yX29yZGVyID0gZmFjdG9yc1szXSkNCiAgDQogIGZvciAobiBpbiBpdGVyYXRpb25zKSB7DQogICAgcHJvZml0cyA8LSBzaW11bGF0ZV9wcm9maXRfbWVhbihwcm9maXRfdmVjLCBuKQ0KICAgIHJlc3VsdF9saXN0W1twYXN0ZTAoc2NlbmFyaW9fbmFtZSwgIl9uIiwgbildXSA8LSBkYXRhLmZyYW1lKA0KICAgICAgU2NlbmFyaW8gPSBzY2VuYXJpb19uYW1lLA0KICAgICAgSXRlcmF0aW9uID0gbiwNCiAgICAgIEVzdGltYXRlZFByb2ZpdCA9IHByb2ZpdHMNCiAgICApDQogIH0NCn0NCg0KIyBH4buZcCBk4buvIGxp4buHdQ0KYWxsX3NpbXVsYXRpb25zIDwtIGJpbmRfcm93cyhyZXN1bHRfbGlzdCkNCg0KIyA0LiBW4bq8IEJJ4buCVSDEkOG7kg0KYWEgPC0gZ2dwbG90KGFsbF9zaW11bGF0aW9ucywgYWVzKHggPSBmYWN0b3IoSXRlcmF0aW9uKSwgeSA9IEVzdGltYXRlZFByb2ZpdCwgZmlsbCA9IFNjZW5hcmlvKSkgKw0KICBnZW9tX2JveHBsb3QoYWxwaGEgPSAwLjYsIG91dGxpZXIuc2hhcGUgPSBOQSkgKw0KICBmYWNldF93cmFwKH5TY2VuYXJpbywgc2NhbGVzID0gImZyZWVfeSIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICLhuqJuaCBoxrDhu59uZyBj4bunYSBjw6FjIHnhur91IHThu5EgxJHhur9uIGzhu6NpIG5odeG6rW4gdHJ1bmcgYsOsbmgvbmfDoHkiLA0KICAgIHggPSAiU+G7kSB2w7JuZyBs4bq3cCBtw7QgcGjhu49uZyAobikiLA0KICAgIHkgPSAiTOG7o2kgbmh14bqtbiB0cnVuZyBiw6xuaC9uZ8OgeSAoVk5EKSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTMpICsgICAgICAgDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KcHJpbnQoYWEpDQoNCmdnc2F2ZSgi4bqibmggaMaw4bufbmcgTE4ucG5nIiwgYWEsIHdpZHRoID0gMTIsIGhlaWdodCA9IDYpDQpgYGANCg0KYGBge3J9DQojIFTDrW5oIGzhu6NpIG5odeG6rW4gdHJ1bmcgYsOsbmgvbmfDoHkgdGhlbyB04burbmcga+G7i2NoIGLhuqNuIHbDoCB04burbmcgc+G7kSB2w7JuZyBs4bq3cA0Kc3VtbWFyeV90YWJsZSA8LSBhbGxfc2ltdWxhdGlvbnMgJT4lDQogIGdyb3VwX2J5KFNjZW5hcmlvLCBJdGVyYXRpb24pICU+JQ0KICBzdW1tYXJpc2UoTWVhblByb2ZpdCA9IG1lYW4oRXN0aW1hdGVkUHJvZml0KSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogIGFycmFuZ2UoSXRlcmF0aW9uKQ0KDQojIEhp4buDbiB0aOG7iw0KZGF0YXRhYmxlKHN1bW1hcnlfdGFibGUpDQpgYGANCg0KIyDEkMOhbmggZ2nDoSBoaeG7h3UgcXXhuqMgbcO0IHBo4buPbmcNCmBgYHtyfQ0KZXZhbHVhdGVfc2ltdWxhdGlvbiA8LSBmdW5jdGlvbihzaW1fdmFsdWVzLCBjb25maWRlbmNlID0gMC45NSkgew0KICBuIDwtIGxlbmd0aChzaW1fdmFsdWVzKQ0KICBtZWFuX3ZhbCA8LSBtZWFuKHNpbV92YWx1ZXMpDQogIHNkX3ZhbCA8LSBzZChzaW1fdmFsdWVzKQ0KICBzZW0gPC0gc2RfdmFsIC8gc3FydChuKQ0KICB6IDwtIHFub3JtKCgxICsgY29uZmlkZW5jZSkvMikgICMgeiA9IDEuOTYgduG7m2kgQ0kgOTUlDQogIGNpX2xvd2VyIDwtIG1lYW5fdmFsIC0geiAqIHNlbQ0KICBjaV91cHBlciA8LSBtZWFuX3ZhbCArIHogKiBzZW0NCiAgY3YgPC0gc2RfdmFsIC8gbWVhbl92YWwNCiAgDQogIHZhcl81IDwtIHF1YW50aWxlKHNpbV92YWx1ZXMsIHByb2JzID0gMC4wNSkNCiAgZXNfNSA8LSBtZWFuKHNpbV92YWx1ZXNbc2ltX3ZhbHVlcyA8PSB2YXJfNV0pDQogIA0KICByZXR1cm4oZGF0YS5mcmFtZSgNCiAgICBOID0gbiwNCiAgICBNZWFuID0gbWVhbl92YWwsDQogICAgU0QgPSBzZF92YWwsDQogICAgU0VNID0gc2VtLA0KICAgIENJX0xvd2VyID0gY2lfbG93ZXIsDQogICAgQ0lfVXBwZXIgPSBjaV91cHBlciwNCiAgICBDViA9IGN2LA0KICAgIFZhUl81ID0gdmFyXzUsDQogICAgRVNfNSA9IGVzXzUNCiAgKSkNCn0NCmBgYA0KDQpgYGB7cn0NCiMgw4FwIGThu6VuZyDEkcOhbmggZ2nDoSBjaG8gdOG7q25nIGvhu4tjaCBi4bqjbiArIG4NCmV2YWx1YXRpb25fcmVzdWx0cyA8LSBhbGxfc2ltdWxhdGlvbnMgJT4lDQogIGdyb3VwX2J5KFNjZW5hcmlvLCBJdGVyYXRpb24pICU+JQ0KICBzdW1tYXJpc2UoDQogICAgRXZhbCA9IGxpc3QoZXZhbHVhdGVfc2ltdWxhdGlvbihFc3RpbWF0ZWRQcm9maXQpKSwNCiAgICAuZ3JvdXBzID0gImRyb3AiDQogICkgJT4lDQogIHVubmVzdChFdmFsKSANCg0KIyBIaeG7g24gdGjhu4sgYuG6o25nIGvhur90IHF14bqjDQpkYXRhdGFibGUoZXZhbHVhdGlvbl9yZXN1bHRzKQ0KYGBgDQoNCg0KDQoNCg0KDQoNCg==