Tugas Individu
Tugas Data Science Programming 13
Data set
1 Categorical Data
1.1 Bar Chart
# Load required libraries
library(dplyr) # For data manipulation
library(ggplot2) # For creating the bar chart
library(viridis) # For color palette
library(scales) # For formatting currency labels
# Step 1: Prepare the data
data_bisnis <- read.csv("8 Descriptive Visualizations – Data Science Programming (1).csv")
sales_summary <- data_bisnis %>%
group_by(Product_Category) %>%
summarise(Total_Sales = sum(Total_Price, na.rm = TRUE)) %>%
arrange(desc(Total_Sales))
# Step 2: Generate a color palette
custom_colors <- viridis::turbo(n = nrow(sales_summary))
# Step 3: Create bar chart with value labels
ggplot(sales_summary, aes(x = reorder(Product_Category, -Total_Sales),
y = Total_Sales,
fill = Product_Category)) +
geom_col(show.legend = FALSE) +
geom_text(aes(label = scales::label_comma(prefix = "Rp ")(Total_Sales)),
vjust = -0.3, size = 3) +
scale_fill_manual(values = custom_colors) +
scale_y_continuous(labels = scales::label_comma(prefix = "Rp "),
expand = expansion(mult = c(0, 0.1))) +
labs(
title = "Total Sales by Product Category (2020–2024)",
subtitle = "Based on Transaction Value",
x = "Product Category",
y = "Total Sales",
caption = "@siregarbakti") +
theme_minimal(base_size = 10)
1.2 Pie Chart
# Load necessary libraries
library(dplyr) # For data manipulation
library(ggplot2) # For data visualization
library(viridis) # For color palettes
library(scales) # For formatting percentages
# Step 1: Summarize total sales by product category
data_bisnis <- read.csv("8 Descriptive Visualizations – Data Science Programming (1).csv")
sales_summary <- data_bisnis %>%
group_by(Product_Category) %>%
summarise(Total_Sales = sum(Total_Price, na.rm = TRUE)) %>%
arrange(desc(Total_Sales)) %>%
mutate(
Percentage = Total_Sales / sum(Total_Sales),# Calculate share
Label = paste0(Product_Category, "\n", # Create label with line break
scales::percent(Percentage, accuracy = 1)))
# Step 2: Create custom color palette
custom_colors <- viridis::turbo(n = nrow(sales_summary))
# Step 3: Plot donut chart
ggplot(sales_summary, aes(x =2, y = Percentage, fill = Product_Category)) +
geom_col(width = 1, color = "white", show.legend = FALSE) + # donut slices
coord_polar(theta = "y") + # Convert to circular layout
geom_text(aes(label = Label), # Add labels inside slices
position = position_stack(vjust = 0.5),
size = 3, color = "white", fontface = "bold") +
scale_fill_manual(values = custom_colors) +
xlim(0.5, 2.5) + # Expand size of donut
labs(
title = "Sales Distribution by Product Category (2020–2024)",
subtitle = "Based on Total Transaction Value",
caption = "@siregarbakti"
) +
theme_void(base_size = 10) + # Clean theme
theme(
plot.title = element_text(face = "bold", hjust = 0.5), # Centered title
plot.subtitle = element_text(margin = margin(t = 8, b = 20), hjust = 0.5),
plot.caption = element_text(margin = margin(t = 15), hjust = 1.5,
color = "gray20", face = "italic")
)
1.3 Word Could
# ==============================
# 1. Install & Load Required Packages
# ==============================
packages <- c("dplyr", "tm", "wordcloud", "RColorBrewer")
new_packages <- packages[!(packages %in% installed.packages()[, "Package"])]
if(length(new_packages)) install.packages(new_packages)
library(dplyr)
library(tm)
library(wordcloud)
library(RColorBrewer)
# ==============================
# 2. Read and Combine Text Columns
# ==============================
data_bisnis <- read.csv("8 Descriptive Visualizations – Data Science Programming (1).csv")
# Combine text columns into one
text_data <- paste(data_bisnis$Product_Category,
data_bisnis$Region,
data_bisnis$Sales_Channel,
sep = " ")
# ==============================
# 3. Clean and Prepare Text
# ==============================
corpus <- VCorpus(VectorSource(text_data))
corpus_clean <- corpus %>%
tm_map(content_transformer(tolower)) %>% # convert to lowercase
tm_map(removePunctuation) %>% # remove punctuation
tm_map(removeNumbers) %>% # remove numbers
tm_map(removeWords, stopwords("english")) %>% # remove English stopwords
tm_map(stripWhitespace) # remove extra whitespace
# Remove empty documents (if any)
non_empty_idx <- sapply(corpus_clean, function(doc) {
nchar(content(doc)) > 0
})
corpus_clean <- corpus_clean[non_empty_idx]
# ==============================
# 4. Create Term-Document Matrix & Word Frequencies
# ==============================
tdm <- TermDocumentMatrix(corpus_clean)
m <- as.matrix(tdm)
word_freqs <- sort(rowSums(m), decreasing = TRUE)
df_words <- data.frame(word = names(word_freqs), freq = word_freqs)
# ==============================
# 5. Generate Word Cloud (Full Screen)
# ==============================
set.seed(123)
wordcloud(words = df_words$word,
freq = df_words$freq,
scale = c(4, 4), # adjust for large size
min.freq = 1,
max.words = 300,
random.order = FALSE,
rot.per = 0.3,
colors = brewer.pal(8, "Dark2"))
1.4 Treemap
# ==============================
# 1. Install & Load Required Packages
# ==============================
packages <- c("treemapify", "dplyr", "ggplot2")
new_packages <- packages[!(packages %in% installed.packages()[, "Package"])]
if(length(new_packages)) install.packages(new_packages)
# Load libraries
library(treemapify)
library(ggplot2)
library(dplyr)
# ==============================
# 2. Prepare Aggregated Treemap Data
# ==============================
data_bisnis <- read.csv("8 Descriptive Visualizations – Data Science Programming (1).csv")
tree_data <- data_bisnis %>%
group_by(Product_Category, Region) %>%
summarise(
Total_Sales = sum(Total_Price, na.rm = TRUE),
.groups = "drop"
) %>%
mutate(
label_combined = paste0(Region, "\n", round(Total_Sales, 0))
)
# ==============================
# 3. Create Static Tree Map with Combined Labels
# ==============================
ggplot(tree_data, aes(
area = Total_Sales,
fill = Product_Category,
subgroup = Product_Category
)) +
geom_treemap() +
geom_treemap_subgroup_border(color = "white") +
geom_treemap_text(
aes(label = label_combined),
colour = "white",
place = "centre",
grow = FALSE,
reflow = TRUE,
size = 20 / .pt, # Adjust overall font size
min.size = 3
) +
labs(
title = "Tree Map of Total Sales by Product Category and Region"
) +
theme_minimal()
2 Numerical Data
2.1 Histogram
# ==============================
# 1. Load Required Libraries
# ==============================
library(ggplot2)
library(dplyr)
# ==============================
# 2. Prepare Data
# ==============================
data_bisnis <- read.csv("8 Descriptive Visualizations – Data Science Programming (1).csv")
data_bisnis <- data_bisnis %>%
mutate(Quantity = as.numeric(Quantity))
# ==============================
# 3. Create Histogram of Quantity with Custom Font Sizes
# ==============================
ggplot(data_bisnis, aes(x = Quantity)) +
geom_histogram(binwidth = 1,
fill = "skyblue",
color = "gray",
alpha = 0.7) +
labs(
title = "Histogram of Quantity Distribution",
x = "Quantity",
y = "Frequency"
) +
theme_minimal() +
theme(
plot.title = element_text(size = 15, face = "bold"), # Title size and bold
axis.title.x = element_text(size = 10), # X label size
axis.title.y = element_text(size = 10), # Y label size
axis.text.x = element_text(size = 9), # X axis numbers size
axis.text.y = element_text(size = 9) # Y axis numbers size
)
2.2 Density Plot
# ==============================
# 1. Load Required Libraries
# ==============================
library(ggplot2)
library(dplyr)
# ==============================
# 2. Prepare Data
# ==============================
data_bisnis <- read.csv("8 Descriptive Visualizations – Data Science Programming (1).csv")
# Ensure Quantity is numeric and remove NAs
data_bisnis <- data_bisnis %>%
mutate(Quantity = as.numeric(Quantity)) %>%
filter(!is.na(Quantity))
# Calculate mean of Quantity
mean_quantity <- mean(data_bisnis$Quantity, na.rm = TRUE)
# Estimate density to get y-position for label
density_data <- density(data_bisnis$Quantity)
max_y <- max(density_data$y)
# ==============================
# 3. Create Density Plot with Mean Line and Label
# ==============================
ggplot(data_bisnis, aes(x = Quantity)) +
geom_density(fill = "skyblue", alpha = 0.6) +
geom_vline(xintercept = mean_quantity, color = "red",
linetype = "dashed", linewidth = 1) +
geom_text(
data = data.frame(x = mean_quantity, y = max_y * 0.8),
aes(x = x, y = y),
label = paste("Mean =", round(mean_quantity, 2)),
color = "black",
angle = 90,
vjust = -0.5,
size = 3, # <= Ubah ukuran label jadi lebih kecil & enak dilihat
fontface = "bold",
inherit.aes = FALSE
) +
labs(
title = "Density Plot of Quantity with Mean",
x = "Quantity",
y = "Density"
) +
theme_minimal() +
theme(
plot.title = element_text(size = 16, face = "bold"), # <= dari 25 ke 16
axis.title = element_text(size = 12), # <= dari 20 ke 12
axis.text = element_text(size = 10) # <= dari 15 ke 10
)
2.3 Box Plot
# ==============================
# 1. Load Libraries
# ==============================
library(ggplot2)
library(dplyr)
# ==============================
# 2. Load and Prepare Data
# ==============================
data_bisnis <- read.csv("8 Descriptive Visualizations – Data Science Programming (1).csv", stringsAsFactors = FALSE)
# Convert Quantity to numeric and filter missing
data_bisnis <- data_bisnis %>%
mutate(Quantity = as.numeric(Quantity)) %>%
filter(!is.na(Quantity))
# Compute IQR-based outlier bounds
Q1 <- quantile(data_bisnis$Quantity, 0.25)
Q3 <- quantile(data_bisnis$Quantity, 0.75)
IQR_value <- IQR(data_bisnis$Quantity)
lower_whisker <- Q1 - 1.5 * IQR_value
upper_whisker <- Q3 + 1.5 * IQR_value
# ==============================
# 3. Summarize Statistics
# ==============================
stats <- data_bisnis %>%
summarise(
Mean = mean(Quantity),
Q1 = Q1,
Median = median(Quantity),
Q3 = Q3,
Min = min(Quantity),
Max = max(Quantity),
Outliers = sum(Quantity < lower_whisker | Quantity > upper_whisker)
)
# ==============================
# 4. Basic Boxplot with Jitter and Annotations
# ==============================
ggplot(data_bisnis, aes(x = factor(1), y = Quantity)) +
geom_boxplot(fill = "skyblue", outlier.shape = NA) +
geom_jitter(aes(color = Quantity < lower_whisker | Quantity > upper_whisker),
width = 0.1, size = 1.5, alpha = 0.5) +
scale_color_manual(values = c("FALSE" = "black", "TRUE" = "red"), guide = "none") +
geom_point(data = data_bisnis %>% filter(Quantity == stats$Max[[1]] & Quantity <= upper_whisker),
aes(x = factor(1), y = Quantity),
color = "red", size = 4) +
ggplot2::annotate("text", x = 1.2, y = stats$Mean[[1]],
label = paste("Mean:", round(stats$Mean[[1]], 2)),
hjust = 0, fontface = "bold", color = "blue", size = 4) +
ggplot2::annotate("text", x = 1.2, y = stats$Q1[[1]],
label = paste("Q1:", round(stats$Q1[[1]], 2)),
hjust = 0, color = "darkgreen", size = 3.5) +
ggplot2::annotate("text", x = 1.2, y = stats$Median[[1]],
label = paste("Median:", round(stats$Median[[1]], 2)),
hjust = 0, color = "purple", size = 3.5) +
ggplot2::annotate("text", x = 1.2, y = stats$Q3[[1]],
label = paste("Q3:", round(stats$Q3[[1]], 2)),
hjust = 0, color = "darkgreen", size = 3.5) +
ggplot2::annotate("text", x = 1.2, y = stats$Min[[1]],
label = paste("Min:", round(stats$Min[[1]], 2)),
hjust = 0, color = "orange", size = 3.5) +
ggplot2::annotate("text", x = 1.2, y = stats$Max[[1]],
label = paste("Max:", round(stats$Max[[1]], 2)),
hjust = 0, color = "orange", size = 3.5) +
ggplot2::annotate("text", x = 1, y = stats$Max[[1]] + 0.05 * stats$Max[[1]],
label = paste("Outliers:", stats$Outliers[[1]]),
color = "red", fontface = "italic", hjust = 0.5, size = 4) +
labs(
title = "Boxplot of Quantity with Jitter",
x = NULL,
y = "Quantity"
) +
theme_minimal() +
theme(
axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
plot.title = element_text(size = 20, face = "bold"),
axis.title = element_text(size = 16),
axis.text = element_text(size = 12)
)
2.4 Violin Plot
# ==============================
# 1. Load Libraries
library(ggplot2)
library(dplyr)
# 2. Load and Prepare Data
data_bisnis <- read.csv("8 Descriptive Visualizations – Data Science Programming (1).csv", stringsAsFactors = FALSE)
# Clean and convert Quantity to numeric
data_bisnis <- data_bisnis %>%
mutate(Quantity = as.numeric(Quantity)) %>%
filter(!is.na(Quantity))
# Calculate quartiles and IQR for outlier detection
Q1 <- quantile(data_bisnis$Quantity, 0.25)
Q3 <- quantile(data_bisnis$Quantity, 0.75)
IQR_value <- IQR(data_bisnis$Quantity)
upper_whisker <- Q3 + 1.5 * IQR_value
lower_whisker <- Q1 - 1.5 * IQR_value
# Mark outliers
data_bisnis <- data_bisnis %>%
mutate(is_outlier = ifelse(Quantity < lower_whisker | Quantity > upper_whisker, "Outlier", "Normal"))
# Summarize statistics
stats <- data_bisnis %>%
summarise(
Mean = mean(Quantity),
Q1 = Q1,
Median = median(Quantity),
Q3 = Q3,
Min = min(Quantity),
Max = max(Quantity),
Outliers = sum(is_outlier == "Outlier")
)
# 4. Create Violin Plot
ggplot(data_bisnis, aes(x = factor(1), y = Quantity)) +
geom_violin(fill = "skyblue", trim = FALSE) +
geom_boxplot(width = 0.1, outlier.shape = NA, color = "black") +
geom_jitter(aes(color = is_outlier), width = 0.1, alpha = 0.6, size = 2) +
geom_point(data = data_bisnis %>%
filter(Quantity == stats$Max[[1]] & Quantity <= upper_whisker),
aes(x = factor(1), y = Quantity),
color = "red", size = 8) +
geom_text(data = stats, aes(x = 1.2, y = Mean, label = paste("Mean:", round(Mean, 2))),
hjust = 0, color = "blue", fontface = "bold") +
geom_text(data = stats, aes(x = 1.2, y = Q1, label = paste("Q1:", round(Q1, 2))),
hjust = 0, color = "darkgreen") +
geom_text(data = stats, aes(x = 1.2, y = Median, label = paste("Median:", round(Median, 2))),
hjust = 0, color = "purple") +
geom_text(data = stats, aes(x = 1.2, y = Q3, label = paste("Q3:", round(Q3, 2))),
hjust = 0, color = "darkgreen") +
geom_text(data = stats, aes(x = 1.2, y = Min, label = paste("Min:", round(Min, 2))),
hjust = 0, color = "orange") +
geom_text(data = stats, aes(x = 1.2, y = Max, label = paste("Max:", round(Max, 2))),
hjust = 0, color = "orange") +
geom_text(data = stats, aes(x = 1, y = Max + 0.05 * Max, label = paste("Outliers:", Outliers)),
color = "red", fontface = "italic", hjust = 0.5) +
scale_color_manual(values = c("Normal" = "black", "Outlier" = "red")) +
labs(
title = "Violin Plot of Quantity with Outlier Highlighted",
x = NULL,
y = "Quantity",
color = "Point Type"
) +
theme_minimal(base_size = 15) +
theme(
axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
plot.title = element_text(size = 20, face = "bold"),
axis.title = element_text(size = 10),
axis.text = element_text(size = 10),
legend.position = "right",
legend.title = element_text(size = 10),
legend.text = element_text(size = 10) # <- Bagian yang sebelumnya error
)
3 Combo
3.1 Grouped Bar Chart
# ==============================
# 1. Load Libraries
# ==============================
library(ggplot2)
library(dplyr)
# ==============================
# 2. Load Data
# ==============================
data_bisnis <- read.csv("8 Descriptive Visualizations – Data Science Programming (1).csv", stringsAsFactors = FALSE)
# ==============================
# 3. Data Summarization
# ==============================
sales_summary <- data_bisnis %>%
group_by(Product_Category, Region) %>%
summarise(Total_Sales = sum(Total_Price, na.rm = TRUE), .groups = "drop")
# ==============================
# 4. Plot Grouped Bar Chart
# ==============================
ggplot(sales_summary, aes(x = Product_Category, y = Total_Sales, fill = Region)) +
geom_bar(stat = "identity", position = position_dodge()) +
labs(
title = "Total Sales by Product Category and Region",
x = "Product Category",
y = "Total Sales (USD)",
fill = "Region"
) +
theme_minimal(base_size = 10) +
theme(
axis.text.x = element_text(angle = 15, hjust = 1),
plot.title = element_text(face = "bold", hjust = 0.5)
)
3.2 Ridgeline Plot
# ==============================
# 1. Load Libraries
# ==============================
library(ggridges)
library(ggplot2)
library(dplyr)
library(scales)
# ==============================
# 2. Filter Valid Data
# ==============================
# Filter out rows where Price_per_Unit is NA, Inf, or NaN
data_bisnis <- read.csv("8 Descriptive Visualizations – Data Science Programming (1).csv", stringsAsFactors = FALSE)
data_bisnis_filtered <- data_bisnis %>%
filter(is.finite(Price_per_Unit))
# ==============================
# 3. Create Ridgeline Plot
# ==============================
ggplot(data_bisnis_filtered, aes(x = Price_per_Unit, y = Region, fill = Region)) +
geom_density_ridges(alpha = 0.7, scale = 1.2) +
scale_x_continuous(labels = dollar_format(prefix = "Rp", big.mark = ".", decimal.mark = ",")) +
labs(
title = "Distribution of Price per Unit by Region",
x = "Price per Unit",
y = "Region"
) +
theme_minimal() +
theme_minimal(base_size = 15) +
theme(legend.position = "none")
3.3 Boxplot by Category
# ==============================
# 1. Load Required Libraries
# ==============================
library(ggplot2)
library(dplyr)
# ==============================
# 2. Prepare Data
# ==============================
# Convert Quantity to numeric and remove NA
data_bisnis <- read.csv("8 Descriptive Visualizations – Data Science Programming (1).csv", stringsAsFactors = FALSE)
data_bisnis <- data_bisnis %>%
mutate(Quantity = as.numeric(Quantity)) %>%
filter(!is.na(Quantity))
# ==============================
# 3. Create Boxplot
# ==============================
ggplot(data_bisnis, aes(x = Product_Category, y = Quantity, fill = Product_Category)) +
geom_boxplot(outlier.colour = "red", outlier.shape = 16, outlier.size = 1) + # Boxplot with red outliers
labs(
title = "Boxplot of Quantity by Product Category",
x = "Product Category",
y = "Quantity"
) +
theme_minimal() +
theme_minimal(base_size = 10) +
theme(
plot.title = element_text(size = 12, face = "bold"),
axis.title = element_text(size = 11),
axis.text = element_text(size = 9),
legend.position = "none"
)
3.4 Lolipop Chart
Lolipop Chart(Gabungan)
# ==============================
# 1. Load Required Libraries
# ==============================
library(ggplot2)
library(dplyr)
# ==============================
# 2. Prepare Data
# ==============================
data_bisnis <- read.csv("8 Descriptive Visualizations – Data Science Programming (1).csv", stringsAsFactors = FALSE)
# Summarize total sales by Product_Category and Region
sales_grouped <- data_bisnis %>%
group_by(Product_Category, Region) %>%
summarise(Total_Sales = sum(Total_Price, na.rm = TRUE), .groups = "drop")
# ==============================
# 3. Grouped Lollipop Chart
# ==============================
ggplot(sales_grouped, aes(x = Total_Sales, y = reorder(Product_Category, Total_Sales), color = Region)) +
geom_segment(aes(x = 0, xend = Total_Sales, y = Product_Category, yend = Product_Category), size = 5) +
geom_point(size = 5) +
labs(
title = "Grouped Lollipop Chart",
x = "Total Sales",
y = "Product Category"
) +
theme_minimal() +
theme_minimal(base_size = 20) +
theme(
axis.text = element_text(size = 6),
axis.title = element_text(size = 7),
plot.title = element_text(size = 7, face = "bold")
)
Lolipop Chart(Yang dipisah)
# ==============================
# 4. Faceted Lollipop Chart
# ==============================
ggplot(sales_grouped, aes(x = Total_Sales, y = reorder(Product_Category, Total_Sales))) +
geom_segment(aes(x = 0, xend = Total_Sales, y = Product_Category, yend = Product_Category), color = "skyblue", size = 5) +
geom_point(color = "blue", size = 5) +
facet_wrap(~ Region, scales = "free_x") +
labs(
title = "Faceted Lollipop Chart",
x = "Total Sales",
y = "Product Category"
) +
theme_minimal() +
theme_minimal(base_size = 20) +
theme(
axis.text = element_text(size = 10),
axis.title = element_text(size = 10),
plot.title = element_text(size = 10, face = "bold")
)
3.5 Heatmap
library(ggplot2)
library(dplyr)
library(readr)
# Baca data
data <- read_csv("8 Descriptive Visualizations – Data Science Programming (1).csv")
# Hitung agregat total sales
agg_data <- data %>%
group_by(Region, Product_Category) %>%
summarise(Total_Sales = sum(Total_Price, na.rm = TRUE)) %>%
ungroup()
# Buat heatmap
ggplot(agg_data, aes(x = Product_Category, y = Region, fill = Total_Sales)) +
geom_tile(color = "white", linewidth = 0.7) +
geom_text(aes(label = round(Total_Sales, 0)), color = "black", size = 4) +
scale_fill_gradient(low = "lightyellow", high = "red", name = "Total Sales") +
labs(
title = "Heatmap Penjualan dengan Penanda Outlier",
x = "Kategori Produk",
y = "Wilayah"
) +
theme_minimal(base_size = 14) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
panel.grid = element_blank()
)
4 Relationship
4.1 Scatter Plot
# Aktifkan package visualisasi
library(tidyverse)
# Baca file data
data <- read.csv("8 Descriptive Visualizations – Data Science Programming (1).csv")
# Buat scatter plot dasar
ggplot(data, aes(x = Quantity, y = Total_Price)) +
geom_point(color = "orange", size = 3, alpha = 0.6) +
labs(
title = "Hubungan Antara Jumlah Produk dan Total Harga",
x = "Jumlah Produk Dibeli (Quantity)",
y = "Total Harga Pembelian (Total_Price)"
) +
theme_minimal()
4.2 Bubble Chart
# Aktifkan package visualisasi
library(tidyverse)
# Baca file data
data <- read.csv("8 Descriptive Visualizations – Data Science Programming (1).csv")
# Buat bubble chart
ggplot(data, aes(x = Quantity, y = Total_Price, size = Discount)) +
geom_point(color = "deepskyblue", alpha = 0.5) +
scale_size_continuous(range = c(1, 10)) + # Atur ukuran gelembung
labs(
title = "Bubble Chart: Quantity vs Total Price (Ukuran = Diskon)",
x = "Jumlah Produk Dibeli (Quantity)",
y = "Total Harga Pembelian (Total_Price)",
size = "Diskon"
) +
theme_minimal()
4.3 Correlation Matrix
library(readr)
library(dplyr)
library(ggcorrplot)
data <- read_csv("8 Descriptive Visualizations – Data Science Programming (1).csv")
num_data <- select_if(data, is.numeric)
cor_matrix <- cor(num_data, method = "pearson")
ggcorrplot(
cor_matrix,
method = "square",
type = "full",
lab = TRUE,
lab_size = 2,
colors = c("blue", "white", "red"),
title = "Heatmap Korelasi Pearson",
tl.cex = 10,
show.legend = TRUE,
legend.title = "Pearson Correlation"
) +
theme_minimal() +
theme(
plot.title = element_text(size = 15, face = "bold", hjust = 0.5),
axis.text.x = element_text(angle = 20, hjust = 1),
panel.grid = element_blank()
)
5 Time Series
5.1 Line Chart
library(dplyr)
library(ggplot2)
library(lubridate)
# Load data
data_bisnis <- read.csv("8 Descriptive Visualizations – Data Science Programming (1).csv", stringsAsFactors = FALSE)
# Pastikan Transaction_Date dalam format Date
data_bisnis <- data_bisnis %>%
mutate(Transaction_Date = as.Date(Transaction_Date))
# Hitung Total_Sales per transaksi
data_bisnis <- data_bisnis %>%
mutate(Total_Sales = Unit_Price * Quantity)
# Buat ringkasan total sales per bulan
monthly_sales <- data_bisnis %>%
mutate(year_month = floor_date(Transaction_Date, "month")) %>% # tanggal pertama tiap bulan
group_by(year_month) %>%
summarise(Total_Sales = sum(Total_Sales, na.rm = TRUE), .groups = "drop")
# Buat plot line chart
ggplot(monthly_sales, aes(x = year_month, y = Total_Sales)) +
geom_line(color = "darkorange", size = 1.2) +
geom_point(color = "red", size = 2) +
scale_x_date(
date_labels = "%b %Y",
date_breaks = "2 months"
) +
labs(title = "Total Sales Line Chart Over Time",
x = "Month",
y = "Total Sales") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
5.2 Area Chart
library(dplyr)
library(ggplot2)
library(lubridate)
# Load data
data_bisnis <- read.csv("8 Descriptive Visualizations – Data Science Programming (1).csv", stringsAsFactors = FALSE)
# Pastikan tanggal dalam format Date dan hitung Total_Sales
data_bisnis <- data_bisnis %>%
mutate(Transaction_Date = as.Date(Transaction_Date),
Total_Sales = Unit_Price * Quantity)
# Ringkas total sales per bulan
monthly_sales <- data_bisnis %>%
mutate(year_month = floor_date(Transaction_Date, "month")) %>%
group_by(year_month) %>%
summarise(Total_Sales = sum(Total_Sales, na.rm = TRUE), .groups = "drop")
# Buat area chart
ggplot(monthly_sales, aes(x = year_month, y = Total_Sales)) +
geom_area(fill = "darkorange", alpha = 0.6) + # area warna oranye transparan
geom_line(color = "red", size = 1) + # garis merah di atas area
geom_point(color = "red", size = 2) + # titik merah di tiap bulan
scale_x_date(
date_labels = "%b %Y",
date_breaks = "2 months"
) +
labs(
title = "Total Sales Area Chart Over Time",
x = "Month",
y = "Total Sales"
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
LS0tDQp0aXRsZTogIlR1Z2FzIEluZGl2aWR1ICINCnN1YnRpdGxlOiAiVHVnYXMgRGF0YSBTY2llbmNlIFByb2dyYW1taW5nIDEzIg0KYXV0aG9yOiAiT2xpdmlhIE1laWxpbmRhIERhdnRpbiBQZXNpcmVyb24iDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy5EYXRlKCksICclQiAlZCwgJVknKWAiDQpvdXRwdXQ6DQogIHJtZGZvcm1hdHM6OnJlYWR0aGVkb3duOiAgICMgaHR0cHM6Ly9naXRodWIuY29tL2p1YmEvcm1kZm9ybWF0cw0KICAgIHNlbGZfY29udGFpbmVkOiB0cnVlDQogICAgdGh1bWJuYWlsczogdHJ1ZQ0KICAgIGxpZ2h0Ym94OiB0cnVlDQogICAgZ2FsbGVyeTogdHJ1ZQ0KICAgIGxpYl9kaXI6IGxpYnMNCiAgICBkZl9wcmludDogInBhZ2VkIg0KICAgIGNvZGVfZm9sZGluZzogInNob3ciDQogICAgY29kZV9kb3dubG9hZDogeWVzIA0KICAgIGNzczogInN0eWxlLmNzcyINCi0tLQ0KDQo8aW1nIHNyYz0ibGl2aWthdGFueWEuanBnIiB3aWR0aD0iMzAwIiBzdHlsZT0iZGlzcGxheTogYmxvY2s7IG1hcmdpbjogYXV0bzsiIGFsdD0iRm90byBEaXJpIj4NCg0KLS0tDQoNCiMgRGF0YSBzZXQgDQoNCmBgYHtyIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCmxpYnJhcnkocmVhZHIpDQpkYXRhX2Jpc25pcyA8LSByZWFkLmNzdigiOCBEZXNjcmlwdGl2ZSBWaXN1YWxpemF0aW9ucyDigJMgRGF0YSBTY2llbmNlIFByb2dyYW1taW5nICgxKS5jc3YiKQ0KDQpkYXRhX2Jpc25pcw0KYGBgDQotLS0NCg0KIyAxIENhdGVnb3JpY2FsIERhdGENCg0KIyMgMS4xIEJhciBDaGFydA0KDQpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBMb2FkIHJlcXVpcmVkIGxpYnJhcmllcw0KbGlicmFyeShkcGx5cikgICAgICAgICMgRm9yIGRhdGEgbWFuaXB1bGF0aW9uDQpsaWJyYXJ5KGdncGxvdDIpICAgICAgIyBGb3IgY3JlYXRpbmcgdGhlIGJhciBjaGFydA0KbGlicmFyeSh2aXJpZGlzKSAgICAgICMgRm9yIGNvbG9yIHBhbGV0dGUNCmxpYnJhcnkoc2NhbGVzKSAgICAgICAjIEZvciBmb3JtYXR0aW5nIGN1cnJlbmN5IGxhYmVscw0KDQojIFN0ZXAgMTogUHJlcGFyZSB0aGUgZGF0YQ0KZGF0YV9iaXNuaXMgPC0gcmVhZC5jc3YoIjggRGVzY3JpcHRpdmUgVmlzdWFsaXphdGlvbnMg4oCTIERhdGEgU2NpZW5jZSBQcm9ncmFtbWluZyAoMSkuY3N2IikNCnNhbGVzX3N1bW1hcnkgPC0gZGF0YV9iaXNuaXMgJT4lDQogIGdyb3VwX2J5KFByb2R1Y3RfQ2F0ZWdvcnkpICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICBzdW1tYXJpc2UoVG90YWxfU2FsZXMgPSBzdW0oVG90YWxfUHJpY2UsIG5hLnJtID0gVFJVRSkpICU+JSANCiAgYXJyYW5nZShkZXNjKFRvdGFsX1NhbGVzKSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQoNCiMgU3RlcCAyOiBHZW5lcmF0ZSBhIGNvbG9yIHBhbGV0dGUNCmN1c3RvbV9jb2xvcnMgPC0gdmlyaWRpczo6dHVyYm8obiA9IG5yb3coc2FsZXNfc3VtbWFyeSkpICAgICAgDQoNCiMgU3RlcCAzOiBDcmVhdGUgYmFyIGNoYXJ0IHdpdGggdmFsdWUgbGFiZWxzDQpnZ3Bsb3Qoc2FsZXNfc3VtbWFyeSwgYWVzKHggPSByZW9yZGVyKFByb2R1Y3RfQ2F0ZWdvcnksIC1Ub3RhbF9TYWxlcyksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gVG90YWxfU2FsZXMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gUHJvZHVjdF9DYXRlZ29yeSkpICsNCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAgICAgICAgICAgICAgICAgICAgICAgICANCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNjYWxlczo6bGFiZWxfY29tbWEocHJlZml4ID0gIlJwICIpKFRvdGFsX1NhbGVzKSksDQogICAgICAgICAgICB2anVzdCA9IC0wLjMsIHNpemUgPSAzKSArICAgICAgICAgICAgICAgICAgICAgICANCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY3VzdG9tX2NvbG9ycykgKyAgICAgICAgICAgICANCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6bGFiZWxfY29tbWEocHJlZml4ID0gIlJwICIpLA0KICAgICAgICAgICAgICAgICAgICAgZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsIDAuMSkpKSArIA0KICBsYWJzKA0KICAgIHRpdGxlID0gIlRvdGFsIFNhbGVzIGJ5IFByb2R1Y3QgQ2F0ZWdvcnkgKDIwMjDigJMyMDI0KSIsDQogICAgc3VidGl0bGUgPSAiQmFzZWQgb24gVHJhbnNhY3Rpb24gVmFsdWUiLA0KICAgIHggPSAiUHJvZHVjdCBDYXRlZ29yeSIsDQogICAgeSA9ICJUb3RhbCBTYWxlcyIsDQogICAgY2FwdGlvbiA9ICJAc2lyZWdhcmJha3RpIikgKw0KICAgIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTApICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCmBgYA0KDQoNCg0KIyMgMS4yIFBpZSBDaGFydA0KDQpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCmxpYnJhcnkoZHBseXIpICAgICAgIyBGb3IgZGF0YSBtYW5pcHVsYXRpb24NCmxpYnJhcnkoZ2dwbG90MikgICAgIyBGb3IgZGF0YSB2aXN1YWxpemF0aW9uDQpsaWJyYXJ5KHZpcmlkaXMpICAgICMgRm9yIGNvbG9yIHBhbGV0dGVzDQpsaWJyYXJ5KHNjYWxlcykgICAgICMgRm9yIGZvcm1hdHRpbmcgcGVyY2VudGFnZXMNCg0KIyBTdGVwIDE6IFN1bW1hcml6ZSB0b3RhbCBzYWxlcyBieSBwcm9kdWN0IGNhdGVnb3J5DQpkYXRhX2Jpc25pcyA8LSByZWFkLmNzdigiOCBEZXNjcmlwdGl2ZSBWaXN1YWxpemF0aW9ucyDigJMgRGF0YSBTY2llbmNlIFByb2dyYW1taW5nICgxKS5jc3YiKQ0Kc2FsZXNfc3VtbWFyeSA8LSBkYXRhX2Jpc25pcyAlPiUNCiAgZ3JvdXBfYnkoUHJvZHVjdF9DYXRlZ29yeSkgJT4lDQogIHN1bW1hcmlzZShUb3RhbF9TYWxlcyA9IHN1bShUb3RhbF9QcmljZSwgbmEucm0gPSBUUlVFKSkgJT4lDQogIGFycmFuZ2UoZGVzYyhUb3RhbF9TYWxlcykpICU+JQ0KICBtdXRhdGUoDQogICAgUGVyY2VudGFnZSA9IFRvdGFsX1NhbGVzIC8gc3VtKFRvdGFsX1NhbGVzKSwjIENhbGN1bGF0ZSBzaGFyZQ0KICAgIExhYmVsID0gcGFzdGUwKFByb2R1Y3RfQ2F0ZWdvcnksICJcbiIsICAgICAgIyBDcmVhdGUgbGFiZWwgd2l0aCBsaW5lIGJyZWFrDQogICAgICAgICAgICAgICAgICAgc2NhbGVzOjpwZXJjZW50KFBlcmNlbnRhZ2UsIGFjY3VyYWN5ID0gMSkpKQ0KDQojIFN0ZXAgMjogQ3JlYXRlIGN1c3RvbSBjb2xvciBwYWxldHRlDQpjdXN0b21fY29sb3JzIDwtIHZpcmlkaXM6OnR1cmJvKG4gPSBucm93KHNhbGVzX3N1bW1hcnkpKQ0KDQojIFN0ZXAgMzogUGxvdCBkb251dCBjaGFydA0KZ2dwbG90KHNhbGVzX3N1bW1hcnksIGFlcyh4ID0yLCB5ID0gUGVyY2VudGFnZSwgZmlsbCA9IFByb2R1Y3RfQ2F0ZWdvcnkpKSArDQogIGdlb21fY29sKHdpZHRoID0gMSwgY29sb3IgPSAid2hpdGUiLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArICMgZG9udXQgc2xpY2VzDQogIGNvb3JkX3BvbGFyKHRoZXRhID0gInkiKSArICAgICAgICAgICAgICAgICAgICAjIENvbnZlcnQgdG8gY2lyY3VsYXIgbGF5b3V0DQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBMYWJlbCksICAgICAgICAgICAgICAgICAjIEFkZCBsYWJlbHMgaW5zaWRlIHNsaWNlcw0KICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSksDQogICAgICAgICAgICBzaXplID0gMywgY29sb3IgPSAid2hpdGUiLCBmb250ZmFjZSA9ICJib2xkIikgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fY29sb3JzKSArDQogIHhsaW0oMC41LCAyLjUpICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgRXhwYW5kIHNpemUgb2YgZG9udXQNCiAgbGFicygNCiAgICB0aXRsZSA9ICJTYWxlcyBEaXN0cmlidXRpb24gYnkgUHJvZHVjdCBDYXRlZ29yeSAoMjAyMOKAkzIwMjQpIiwNCiAgICBzdWJ0aXRsZSA9ICJCYXNlZCBvbiBUb3RhbCBUcmFuc2FjdGlvbiBWYWx1ZSIsDQogICAgY2FwdGlvbiA9ICJAc2lyZWdhcmJha3RpIg0KICApICsNCiAgdGhlbWVfdm9pZChiYXNlX3NpemUgPSAxMCkgKyAgICAgICAgICAgICAgICAgIyBDbGVhbiB0aGVtZQ0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41KSwgIyBDZW50ZXJlZCB0aXRsZQ0KICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQobWFyZ2luID0gbWFyZ2luKHQgPSA4LCBiID0gMjApLCBoanVzdCA9IDAuNSksDQogICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IG1hcmdpbih0ID0gMTUpLCBoanVzdCA9IDEuNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiZ3JheTIwIiwgZmFjZSA9ICJpdGFsaWMiKQ0KICApDQpgYGANCg0KDQojIyAxLjMgV29yZCBDb3VsZA0KDQpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMS4gSW5zdGFsbCAmIExvYWQgUmVxdWlyZWQgUGFja2FnZXMNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpwYWNrYWdlcyA8LSBjKCJkcGx5ciIsICJ0bSIsICJ3b3JkY2xvdWQiLCAiUkNvbG9yQnJld2VyIikNCm5ld19wYWNrYWdlcyA8LSBwYWNrYWdlc1shKHBhY2thZ2VzICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKClbLCAiUGFja2FnZSJdKV0NCmlmKGxlbmd0aChuZXdfcGFja2FnZXMpKSBpbnN0YWxsLnBhY2thZ2VzKG5ld19wYWNrYWdlcykNCg0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodG0pDQpsaWJyYXJ5KHdvcmRjbG91ZCkNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAyLiBSZWFkIGFuZCBDb21iaW5lIFRleHQgQ29sdW1ucw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmRhdGFfYmlzbmlzIDwtIHJlYWQuY3N2KCI4IERlc2NyaXB0aXZlIFZpc3VhbGl6YXRpb25zIOKAkyBEYXRhIFNjaWVuY2UgUHJvZ3JhbW1pbmcgKDEpLmNzdiIpDQoNCiMgQ29tYmluZSB0ZXh0IGNvbHVtbnMgaW50byBvbmUNCnRleHRfZGF0YSA8LSBwYXN0ZShkYXRhX2Jpc25pcyRQcm9kdWN0X0NhdGVnb3J5LA0KICAgICAgICAgICAgICAgICAgIGRhdGFfYmlzbmlzJFJlZ2lvbiwNCiAgICAgICAgICAgICAgICAgICBkYXRhX2Jpc25pcyRTYWxlc19DaGFubmVsLA0KICAgICAgICAgICAgICAgICAgIHNlcCA9ICIgIikNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMy4gQ2xlYW4gYW5kIFByZXBhcmUgVGV4dA0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmNvcnB1cyA8LSBWQ29ycHVzKFZlY3RvclNvdXJjZSh0ZXh0X2RhdGEpKQ0KDQpjb3JwdXNfY2xlYW4gPC0gY29ycHVzICU+JQ0KICB0bV9tYXAoY29udGVudF90cmFuc2Zvcm1lcih0b2xvd2VyKSkgJT4lICAjIGNvbnZlcnQgdG8gbG93ZXJjYXNlDQogIHRtX21hcChyZW1vdmVQdW5jdHVhdGlvbikgJT4lICAgICAgICAgICAgICMgcmVtb3ZlIHB1bmN0dWF0aW9uDQogIHRtX21hcChyZW1vdmVOdW1iZXJzKSAlPiUgICAgICAgICAgICAgICAgICMgcmVtb3ZlIG51bWJlcnMNCiAgdG1fbWFwKHJlbW92ZVdvcmRzLCBzdG9wd29yZHMoImVuZ2xpc2giKSkgJT4lICAjIHJlbW92ZSBFbmdsaXNoIHN0b3B3b3Jkcw0KICB0bV9tYXAoc3RyaXBXaGl0ZXNwYWNlKSAgICAgICAgICAgICAgICAgICAjIHJlbW92ZSBleHRyYSB3aGl0ZXNwYWNlDQoNCiMgUmVtb3ZlIGVtcHR5IGRvY3VtZW50cyAoaWYgYW55KQ0Kbm9uX2VtcHR5X2lkeCA8LSBzYXBwbHkoY29ycHVzX2NsZWFuLCBmdW5jdGlvbihkb2MpIHsNCiAgbmNoYXIoY29udGVudChkb2MpKSA+IDANCn0pDQpjb3JwdXNfY2xlYW4gPC0gY29ycHVzX2NsZWFuW25vbl9lbXB0eV9pZHhdDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDQuIENyZWF0ZSBUZXJtLURvY3VtZW50IE1hdHJpeCAmIFdvcmQgRnJlcXVlbmNpZXMNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQp0ZG0gPC0gVGVybURvY3VtZW50TWF0cml4KGNvcnB1c19jbGVhbikNCm0gPC0gYXMubWF0cml4KHRkbSkNCndvcmRfZnJlcXMgPC0gc29ydChyb3dTdW1zKG0pLCBkZWNyZWFzaW5nID0gVFJVRSkNCmRmX3dvcmRzIDwtIGRhdGEuZnJhbWUod29yZCA9IG5hbWVzKHdvcmRfZnJlcXMpLCBmcmVxID0gd29yZF9mcmVxcykNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgNS4gR2VuZXJhdGUgV29yZCBDbG91ZCAoRnVsbCBTY3JlZW4pDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0Kc2V0LnNlZWQoMTIzKQ0Kd29yZGNsb3VkKHdvcmRzID0gZGZfd29yZHMkd29yZCwNCiAgICAgICAgICBmcmVxID0gZGZfd29yZHMkZnJlcSwNCiAgICAgICAgICBzY2FsZSA9IGMoNCwgNCksICAgICAgICMgYWRqdXN0IGZvciBsYXJnZSBzaXplDQogICAgICAgICAgbWluLmZyZXEgPSAxLA0KICAgICAgICAgIG1heC53b3JkcyA9IDMwMCwNCiAgICAgICAgICByYW5kb20ub3JkZXIgPSBGQUxTRSwNCiAgICAgICAgICByb3QucGVyID0gMC4zLA0KICAgICAgICAgIGNvbG9ycyA9IGJyZXdlci5wYWwoOCwgIkRhcmsyIikpDQpgYGANCg0KDQojIyAxLjQgVHJlZW1hcA0KDQoNCmBgYHtyLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAxLiBJbnN0YWxsICYgTG9hZCBSZXF1aXJlZCBQYWNrYWdlcw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCnBhY2thZ2VzIDwtIGMoInRyZWVtYXBpZnkiLCAiZHBseXIiLCAiZ2dwbG90MiIpDQpuZXdfcGFja2FnZXMgPC0gcGFja2FnZXNbIShwYWNrYWdlcyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpWywgIlBhY2thZ2UiXSldDQppZihsZW5ndGgobmV3X3BhY2thZ2VzKSkgaW5zdGFsbC5wYWNrYWdlcyhuZXdfcGFja2FnZXMpDQoNCiMgTG9hZCBsaWJyYXJpZXMNCmxpYnJhcnkodHJlZW1hcGlmeSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDIuIFByZXBhcmUgQWdncmVnYXRlZCBUcmVlbWFwIERhdGENCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpkYXRhX2Jpc25pcyA8LSByZWFkLmNzdigiOCBEZXNjcmlwdGl2ZSBWaXN1YWxpemF0aW9ucyDigJMgRGF0YSBTY2llbmNlIFByb2dyYW1taW5nICgxKS5jc3YiKQ0KdHJlZV9kYXRhIDwtIGRhdGFfYmlzbmlzICU+JQ0KICBncm91cF9ieShQcm9kdWN0X0NhdGVnb3J5LCBSZWdpb24pICU+JQ0KICBzdW1tYXJpc2UoDQogICAgVG90YWxfU2FsZXMgPSBzdW0oVG90YWxfUHJpY2UsIG5hLnJtID0gVFJVRSksDQogICAgLmdyb3VwcyA9ICJkcm9wIg0KICApICU+JQ0KICBtdXRhdGUoDQogICAgbGFiZWxfY29tYmluZWQgPSBwYXN0ZTAoUmVnaW9uLCAiXG4iLCByb3VuZChUb3RhbF9TYWxlcywgMCkpDQogICkNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMy4gQ3JlYXRlIFN0YXRpYyBUcmVlIE1hcCB3aXRoIENvbWJpbmVkIExhYmVscw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KZ2dwbG90KHRyZWVfZGF0YSwgYWVzKA0KICBhcmVhID0gVG90YWxfU2FsZXMsDQogIGZpbGwgPSBQcm9kdWN0X0NhdGVnb3J5LA0KICBzdWJncm91cCA9IFByb2R1Y3RfQ2F0ZWdvcnkNCikpICsNCiAgZ2VvbV90cmVlbWFwKCkgKw0KICBnZW9tX3RyZWVtYXBfc3ViZ3JvdXBfYm9yZGVyKGNvbG9yID0gIndoaXRlIikgKw0KDQogIGdlb21fdHJlZW1hcF90ZXh0KA0KICAgIGFlcyhsYWJlbCA9IGxhYmVsX2NvbWJpbmVkKSwNCiAgICBjb2xvdXIgPSAid2hpdGUiLA0KICAgIHBsYWNlID0gImNlbnRyZSIsDQogICAgZ3JvdyA9IEZBTFNFLA0KICAgIHJlZmxvdyA9IFRSVUUsDQogICAgc2l6ZSA9IDIwIC8gLnB0LCAgICAgICAjIEFkanVzdCBvdmVyYWxsIGZvbnQgc2l6ZQ0KICAgIG1pbi5zaXplID0gMw0KICApICsNCg0KICBsYWJzKA0KICAgIHRpdGxlID0gIlRyZWUgTWFwIG9mIFRvdGFsIFNhbGVzIGJ5IFByb2R1Y3QgQ2F0ZWdvcnkgYW5kIFJlZ2lvbiINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCg0KLS0tDQoNCiMgMiBOdW1lcmljYWwgRGF0YQ0KDQojIyAyLjEgSGlzdG9ncmFtDQoNCmBgYHtyLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAxLiBMb2FkIFJlcXVpcmVkIExpYnJhcmllcw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDIuIFByZXBhcmUgRGF0YQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmRhdGFfYmlzbmlzIDwtIHJlYWQuY3N2KCI4IERlc2NyaXB0aXZlIFZpc3VhbGl6YXRpb25zIOKAkyBEYXRhIFNjaWVuY2UgUHJvZ3JhbW1pbmcgKDEpLmNzdiIpDQpkYXRhX2Jpc25pcyA8LSBkYXRhX2Jpc25pcyAlPiUNCiAgbXV0YXRlKFF1YW50aXR5ID0gYXMubnVtZXJpYyhRdWFudGl0eSkpDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDMuIENyZWF0ZSBIaXN0b2dyYW0gb2YgUXVhbnRpdHkgd2l0aCBDdXN0b20gRm9udCBTaXplcw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmdncGxvdChkYXRhX2Jpc25pcywgYWVzKHggPSBRdWFudGl0eSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxLA0KICAgICAgICAgICAgICAgICBmaWxsID0gInNreWJsdWUiLA0KICAgICAgICAgICAgICAgICBjb2xvciA9ICJncmF5IiwNCiAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjcpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJIaXN0b2dyYW0gb2YgUXVhbnRpdHkgRGlzdHJpYnV0aW9uIiwNCiAgICB4ID0gIlF1YW50aXR5IiwNCiAgICB5ID0gIkZyZXF1ZW5jeSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1LCBmYWNlID0gImJvbGQiKSwgICMgVGl0bGUgc2l6ZSBhbmQgYm9sZA0KICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLCAgICAgICAgICAgICAgICMgWCBsYWJlbCBzaXplDQogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksICAgICAgICAgICAgICAgIyBZIGxhYmVsIHNpemUNCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOSksICAgICAgICAgICAgICAgICMgWCBheGlzIG51bWJlcnMgc2l6ZQ0KICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSAgICAgICAgICAgICAgICAgIyBZIGF4aXMgbnVtYmVycyBzaXplDQogICkNCmBgYA0KDQojIyAyLjIgRGVuc2l0eSBQbG90DQoNCmBgYHtyLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAxLiBMb2FkIFJlcXVpcmVkIExpYnJhcmllcw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDIuIFByZXBhcmUgRGF0YQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmRhdGFfYmlzbmlzIDwtIHJlYWQuY3N2KCI4IERlc2NyaXB0aXZlIFZpc3VhbGl6YXRpb25zIOKAkyBEYXRhIFNjaWVuY2UgUHJvZ3JhbW1pbmcgKDEpLmNzdiIpDQoNCiMgRW5zdXJlIFF1YW50aXR5IGlzIG51bWVyaWMgYW5kIHJlbW92ZSBOQXMNCmRhdGFfYmlzbmlzIDwtIGRhdGFfYmlzbmlzICU+JQ0KICBtdXRhdGUoUXVhbnRpdHkgPSBhcy5udW1lcmljKFF1YW50aXR5KSkgJT4lDQogIGZpbHRlcighaXMubmEoUXVhbnRpdHkpKQ0KDQojIENhbGN1bGF0ZSBtZWFuIG9mIFF1YW50aXR5DQptZWFuX3F1YW50aXR5IDwtIG1lYW4oZGF0YV9iaXNuaXMkUXVhbnRpdHksIG5hLnJtID0gVFJVRSkNCg0KIyBFc3RpbWF0ZSBkZW5zaXR5IHRvIGdldCB5LXBvc2l0aW9uIGZvciBsYWJlbA0KZGVuc2l0eV9kYXRhIDwtIGRlbnNpdHkoZGF0YV9iaXNuaXMkUXVhbnRpdHkpDQptYXhfeSA8LSBtYXgoZGVuc2l0eV9kYXRhJHkpDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDMuIENyZWF0ZSBEZW5zaXR5IFBsb3Qgd2l0aCBNZWFuIExpbmUgYW5kIExhYmVsDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KZ2dwbG90KGRhdGFfYmlzbmlzLCBhZXMoeCA9IFF1YW50aXR5KSkgKw0KICBnZW9tX2RlbnNpdHkoZmlsbCA9ICJza3libHVlIiwgYWxwaGEgPSAwLjYpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbWVhbl9xdWFudGl0eSwgY29sb3IgPSAicmVkIiwgDQogICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwgbGluZXdpZHRoID0gMSkgKw0KICBnZW9tX3RleHQoDQogICAgZGF0YSA9IGRhdGEuZnJhbWUoeCA9IG1lYW5fcXVhbnRpdHksIHkgPSBtYXhfeSAqIDAuOCksDQogICAgYWVzKHggPSB4LCB5ID0geSksDQogICAgbGFiZWwgPSBwYXN0ZSgiTWVhbiA9Iiwgcm91bmQobWVhbl9xdWFudGl0eSwgMikpLA0KICAgIGNvbG9yID0gImJsYWNrIiwNCiAgICBhbmdsZSA9IDkwLA0KICAgIHZqdXN0ID0gLTAuNSwNCiAgICBzaXplID0gMywgICMgPD0gVWJhaCB1a3VyYW4gbGFiZWwgamFkaSBsZWJpaCBrZWNpbCAmIGVuYWsgZGlsaWhhdA0KICAgIGZvbnRmYWNlID0gImJvbGQiLA0KICAgIGluaGVyaXQuYWVzID0gRkFMU0UNCiAgKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiRGVuc2l0eSBQbG90IG9mIFF1YW50aXR5IHdpdGggTWVhbiIsDQogICAgeCA9ICJRdWFudGl0eSIsDQogICAgeSA9ICJEZW5zaXR5Ig0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpLCAgICMgPD0gZGFyaSAyNSBrZSAxNg0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgICAgICAgICAgICAgICAgICAjIDw9IGRhcmkgMjAga2UgMTINCiAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSAgICAgICAgICAgICAgICAgICAgIyA8PSBkYXJpIDE1IGtlIDEwDQogICkNCg0KYGBgDQoNCiMjIDIuMyBCb3ggUGxvdA0KDQpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMS4gTG9hZCBMaWJyYXJpZXMNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAyLiBMb2FkIGFuZCBQcmVwYXJlIERhdGENCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpkYXRhX2Jpc25pcyA8LSByZWFkLmNzdigiOCBEZXNjcmlwdGl2ZSBWaXN1YWxpemF0aW9ucyDigJMgRGF0YSBTY2llbmNlIFByb2dyYW1taW5nICgxKS5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQoNCiMgQ29udmVydCBRdWFudGl0eSB0byBudW1lcmljIGFuZCBmaWx0ZXIgbWlzc2luZw0KZGF0YV9iaXNuaXMgPC0gZGF0YV9iaXNuaXMgJT4lDQogIG11dGF0ZShRdWFudGl0eSA9IGFzLm51bWVyaWMoUXVhbnRpdHkpKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShRdWFudGl0eSkpDQoNCiMgQ29tcHV0ZSBJUVItYmFzZWQgb3V0bGllciBib3VuZHMNClExIDwtIHF1YW50aWxlKGRhdGFfYmlzbmlzJFF1YW50aXR5LCAwLjI1KQ0KUTMgPC0gcXVhbnRpbGUoZGF0YV9iaXNuaXMkUXVhbnRpdHksIDAuNzUpDQpJUVJfdmFsdWUgPC0gSVFSKGRhdGFfYmlzbmlzJFF1YW50aXR5KQ0KbG93ZXJfd2hpc2tlciA8LSBRMSAtIDEuNSAqIElRUl92YWx1ZQ0KdXBwZXJfd2hpc2tlciA8LSBRMyArIDEuNSAqIElRUl92YWx1ZQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAzLiBTdW1tYXJpemUgU3RhdGlzdGljcw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCnN0YXRzIDwtIGRhdGFfYmlzbmlzICU+JQ0KICBzdW1tYXJpc2UoDQogICAgTWVhbiA9IG1lYW4oUXVhbnRpdHkpLA0KICAgIFExID0gUTEsDQogICAgTWVkaWFuID0gbWVkaWFuKFF1YW50aXR5KSwNCiAgICBRMyA9IFEzLA0KICAgIE1pbiA9IG1pbihRdWFudGl0eSksDQogICAgTWF4ID0gbWF4KFF1YW50aXR5KSwNCiAgICBPdXRsaWVycyA9IHN1bShRdWFudGl0eSA8IGxvd2VyX3doaXNrZXIgfCBRdWFudGl0eSA+IHVwcGVyX3doaXNrZXIpDQogICkNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgNC4gQmFzaWMgQm94cGxvdCB3aXRoIEppdHRlciBhbmQgQW5ub3RhdGlvbnMNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpnZ3Bsb3QoZGF0YV9iaXNuaXMsIGFlcyh4ID0gZmFjdG9yKDEpLCB5ID0gUXVhbnRpdHkpKSArDQogIGdlb21fYm94cGxvdChmaWxsID0gInNreWJsdWUiLCBvdXRsaWVyLnNoYXBlID0gTkEpICsNCiAgDQogIGdlb21faml0dGVyKGFlcyhjb2xvciA9IFF1YW50aXR5IDwgbG93ZXJfd2hpc2tlciB8IFF1YW50aXR5ID4gdXBwZXJfd2hpc2tlciksDQogICAgICAgICAgICAgIHdpZHRoID0gMC4xLCBzaXplID0gMS41LCBhbHBoYSA9IDAuNSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiRkFMU0UiID0gImJsYWNrIiwgIlRSVUUiID0gInJlZCIpLCBndWlkZSA9ICJub25lIikgKw0KICANCiAgZ2VvbV9wb2ludChkYXRhID0gZGF0YV9iaXNuaXMgJT4lIGZpbHRlcihRdWFudGl0eSA9PSBzdGF0cyRNYXhbWzFdXSAmIFF1YW50aXR5IDw9IHVwcGVyX3doaXNrZXIpLA0KICAgICAgICAgICAgIGFlcyh4ID0gZmFjdG9yKDEpLCB5ID0gUXVhbnRpdHkpLA0KICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIHNpemUgPSA0KSArDQogIA0KICBnZ3Bsb3QyOjphbm5vdGF0ZSgidGV4dCIsIHggPSAxLjIsIHkgPSBzdGF0cyRNZWFuW1sxXV0sIA0KICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJNZWFuOiIsIHJvdW5kKHN0YXRzJE1lYW5bWzFdXSwgMikpLCANCiAgICAgICAgICAgaGp1c3QgPSAwLCBmb250ZmFjZSA9ICJib2xkIiwgY29sb3IgPSAiYmx1ZSIsIHNpemUgPSA0KSArDQogIGdncGxvdDI6OmFubm90YXRlKCJ0ZXh0IiwgeCA9IDEuMiwgeSA9IHN0YXRzJFExW1sxXV0sIA0KICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJRMToiLCByb3VuZChzdGF0cyRRMVtbMV1dLCAyKSksIA0KICAgICAgICAgICBoanVzdCA9IDAsIGNvbG9yID0gImRhcmtncmVlbiIsIHNpemUgPSAzLjUpICsNCiAgZ2dwbG90Mjo6YW5ub3RhdGUoInRleHQiLCB4ID0gMS4yLCB5ID0gc3RhdHMkTWVkaWFuW1sxXV0sIA0KICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJNZWRpYW46Iiwgcm91bmQoc3RhdHMkTWVkaWFuW1sxXV0sIDIpKSwgDQogICAgICAgICAgIGhqdXN0ID0gMCwgY29sb3IgPSAicHVycGxlIiwgc2l6ZSA9IDMuNSkgKw0KICBnZ3Bsb3QyOjphbm5vdGF0ZSgidGV4dCIsIHggPSAxLjIsIHkgPSBzdGF0cyRRM1tbMV1dLCANCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiUTM6Iiwgcm91bmQoc3RhdHMkUTNbWzFdXSwgMikpLCANCiAgICAgICAgICAgaGp1c3QgPSAwLCBjb2xvciA9ICJkYXJrZ3JlZW4iLCBzaXplID0gMy41KSArDQogIGdncGxvdDI6OmFubm90YXRlKCJ0ZXh0IiwgeCA9IDEuMiwgeSA9IHN0YXRzJE1pbltbMV1dLCANCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiTWluOiIsIHJvdW5kKHN0YXRzJE1pbltbMV1dLCAyKSksIA0KICAgICAgICAgICBoanVzdCA9IDAsIGNvbG9yID0gIm9yYW5nZSIsIHNpemUgPSAzLjUpICsNCiAgZ2dwbG90Mjo6YW5ub3RhdGUoInRleHQiLCB4ID0gMS4yLCB5ID0gc3RhdHMkTWF4W1sxXV0sIA0KICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJNYXg6Iiwgcm91bmQoc3RhdHMkTWF4W1sxXV0sIDIpKSwgDQogICAgICAgICAgIGhqdXN0ID0gMCwgY29sb3IgPSAib3JhbmdlIiwgc2l6ZSA9IDMuNSkgKw0KICBnZ3Bsb3QyOjphbm5vdGF0ZSgidGV4dCIsIHggPSAxLCB5ID0gc3RhdHMkTWF4W1sxXV0gKyAwLjA1ICogc3RhdHMkTWF4W1sxXV0sIA0KICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJPdXRsaWVyczoiLCBzdGF0cyRPdXRsaWVyc1tbMV1dKSwgDQogICAgICAgICAgIGNvbG9yID0gInJlZCIsIGZvbnRmYWNlID0gIml0YWxpYyIsIGhqdXN0ID0gMC41LCBzaXplID0gNCkgKw0KDQogIGxhYnMoDQogICAgdGl0bGUgPSAiQm94cGxvdCBvZiBRdWFudGl0eSB3aXRoIEppdHRlciIsDQogICAgeCA9IE5VTEwsDQogICAgeSA9ICJRdWFudGl0eSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCwgZmFjZSA9ICJib2xkIiksDQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpLA0KICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpDQogICkNCg0KYGBgDQoNCiMjIDIuNCBWaW9saW4gUGxvdA0KDQpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMS4gTG9hZCBMaWJyYXJpZXMNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQoNCiMgMi4gTG9hZCBhbmQgUHJlcGFyZSBEYXRhDQpkYXRhX2Jpc25pcyA8LSByZWFkLmNzdigiOCBEZXNjcmlwdGl2ZSBWaXN1YWxpemF0aW9ucyDigJMgRGF0YSBTY2llbmNlIFByb2dyYW1taW5nICgxKS5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQoNCiMgQ2xlYW4gYW5kIGNvbnZlcnQgUXVhbnRpdHkgdG8gbnVtZXJpYw0KZGF0YV9iaXNuaXMgPC0gZGF0YV9iaXNuaXMgJT4lDQogIG11dGF0ZShRdWFudGl0eSA9IGFzLm51bWVyaWMoUXVhbnRpdHkpKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShRdWFudGl0eSkpDQoNCiMgQ2FsY3VsYXRlIHF1YXJ0aWxlcyBhbmQgSVFSIGZvciBvdXRsaWVyIGRldGVjdGlvbg0KUTEgPC0gcXVhbnRpbGUoZGF0YV9iaXNuaXMkUXVhbnRpdHksIDAuMjUpDQpRMyA8LSBxdWFudGlsZShkYXRhX2Jpc25pcyRRdWFudGl0eSwgMC43NSkNCklRUl92YWx1ZSA8LSBJUVIoZGF0YV9iaXNuaXMkUXVhbnRpdHkpDQp1cHBlcl93aGlza2VyIDwtIFEzICsgMS41ICogSVFSX3ZhbHVlDQpsb3dlcl93aGlza2VyIDwtIFExIC0gMS41ICogSVFSX3ZhbHVlDQoNCiMgTWFyayBvdXRsaWVycw0KZGF0YV9iaXNuaXMgPC0gZGF0YV9iaXNuaXMgJT4lDQogIG11dGF0ZShpc19vdXRsaWVyID0gaWZlbHNlKFF1YW50aXR5IDwgbG93ZXJfd2hpc2tlciB8IFF1YW50aXR5ID4gdXBwZXJfd2hpc2tlciwgIk91dGxpZXIiLCAiTm9ybWFsIikpDQoNCiMgU3VtbWFyaXplIHN0YXRpc3RpY3MNCnN0YXRzIDwtIGRhdGFfYmlzbmlzICU+JQ0KICBzdW1tYXJpc2UoDQogICAgTWVhbiA9IG1lYW4oUXVhbnRpdHkpLA0KICAgIFExID0gUTEsDQogICAgTWVkaWFuID0gbWVkaWFuKFF1YW50aXR5KSwNCiAgICBRMyA9IFEzLA0KICAgIE1pbiA9IG1pbihRdWFudGl0eSksDQogICAgTWF4ID0gbWF4KFF1YW50aXR5KSwNCiAgICBPdXRsaWVycyA9IHN1bShpc19vdXRsaWVyID09ICJPdXRsaWVyIikNCiAgKQ0KDQojIDQuIENyZWF0ZSBWaW9saW4gUGxvdA0KZ2dwbG90KGRhdGFfYmlzbmlzLCBhZXMoeCA9IGZhY3RvcigxKSwgeSA9IFF1YW50aXR5KSkgKw0KICBnZW9tX3Zpb2xpbihmaWxsID0gInNreWJsdWUiLCB0cmltID0gRkFMU0UpICsNCiAgZ2VvbV9ib3hwbG90KHdpZHRoID0gMC4xLCBvdXRsaWVyLnNoYXBlID0gTkEsIGNvbG9yID0gImJsYWNrIikgKw0KICBnZW9tX2ppdHRlcihhZXMoY29sb3IgPSBpc19vdXRsaWVyKSwgd2lkdGggPSAwLjEsIGFscGhhID0gMC42LCBzaXplID0gMikgKw0KICBnZW9tX3BvaW50KGRhdGEgPSBkYXRhX2Jpc25pcyAlPiUNCiAgICAgICAgICAgICAgIGZpbHRlcihRdWFudGl0eSA9PSBzdGF0cyRNYXhbWzFdXSAmIFF1YW50aXR5IDw9IHVwcGVyX3doaXNrZXIpLA0KICAgICAgICAgICAgIGFlcyh4ID0gZmFjdG9yKDEpLCB5ID0gUXVhbnRpdHkpLA0KICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIHNpemUgPSA4KSArDQoNCiAgZ2VvbV90ZXh0KGRhdGEgPSBzdGF0cywgYWVzKHggPSAxLjIsIHkgPSBNZWFuLCBsYWJlbCA9IHBhc3RlKCJNZWFuOiIsIHJvdW5kKE1lYW4sIDIpKSksDQogICAgICAgICAgICBoanVzdCA9IDAsIGNvbG9yID0gImJsdWUiLCBmb250ZmFjZSA9ICJib2xkIikgKw0KICBnZW9tX3RleHQoZGF0YSA9IHN0YXRzLCBhZXMoeCA9IDEuMiwgeSA9IFExLCBsYWJlbCA9IHBhc3RlKCJRMToiLCByb3VuZChRMSwgMikpKSwNCiAgICAgICAgICAgIGhqdXN0ID0gMCwgY29sb3IgPSAiZGFya2dyZWVuIikgKw0KICBnZW9tX3RleHQoZGF0YSA9IHN0YXRzLCBhZXMoeCA9IDEuMiwgeSA9IE1lZGlhbiwgbGFiZWwgPSBwYXN0ZSgiTWVkaWFuOiIsIHJvdW5kKE1lZGlhbiwgMikpKSwNCiAgICAgICAgICAgIGhqdXN0ID0gMCwgY29sb3IgPSAicHVycGxlIikgKw0KICBnZW9tX3RleHQoZGF0YSA9IHN0YXRzLCBhZXMoeCA9IDEuMiwgeSA9IFEzLCBsYWJlbCA9IHBhc3RlKCJRMzoiLCByb3VuZChRMywgMikpKSwNCiAgICAgICAgICAgIGhqdXN0ID0gMCwgY29sb3IgPSAiZGFya2dyZWVuIikgKw0KICBnZW9tX3RleHQoZGF0YSA9IHN0YXRzLCBhZXMoeCA9IDEuMiwgeSA9IE1pbiwgbGFiZWwgPSBwYXN0ZSgiTWluOiIsIHJvdW5kKE1pbiwgMikpKSwNCiAgICAgICAgICAgIGhqdXN0ID0gMCwgY29sb3IgPSAib3JhbmdlIikgKw0KICBnZW9tX3RleHQoZGF0YSA9IHN0YXRzLCBhZXMoeCA9IDEuMiwgeSA9IE1heCwgbGFiZWwgPSBwYXN0ZSgiTWF4OiIsIHJvdW5kKE1heCwgMikpKSwNCiAgICAgICAgICAgIGhqdXN0ID0gMCwgY29sb3IgPSAib3JhbmdlIikgKw0KICBnZW9tX3RleHQoZGF0YSA9IHN0YXRzLCBhZXMoeCA9IDEsIHkgPSBNYXggKyAwLjA1ICogTWF4LCBsYWJlbCA9IHBhc3RlKCJPdXRsaWVyczoiLCBPdXRsaWVycykpLA0KICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgZm9udGZhY2UgPSAiaXRhbGljIiwgaGp1c3QgPSAwLjUpICsNCg0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiTm9ybWFsIiA9ICJibGFjayIsICJPdXRsaWVyIiA9ICJyZWQiKSkgKw0KDQogIGxhYnMoDQogICAgdGl0bGUgPSAiVmlvbGluIFBsb3Qgb2YgUXVhbnRpdHkgd2l0aCBPdXRsaWVyIEhpZ2hsaWdodGVkIiwNCiAgICB4ID0gTlVMTCwNCiAgICB5ID0gIlF1YW50aXR5IiwNCiAgICBjb2xvciA9ICJQb2ludCBUeXBlIg0KICApICsNCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxNSkgKw0KICB0aGVtZSgNCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjAsIGZhY2UgPSAiYm9sZCIpLA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwNCiAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwNCiAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLA0KICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLA0KICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkgICMgPC0gQmFnaWFuIHlhbmcgc2ViZWx1bW55YSBlcnJvcg0KICApDQoNCmBgYA0KDQotLS0NCg0KIyAzIENvbWJvDQoNCiMjIDMuMSBHcm91cGVkIEJhciBDaGFydA0KDQpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMS4gTG9hZCBMaWJyYXJpZXMNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAyLiBMb2FkIERhdGENCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpkYXRhX2Jpc25pcyA8LSByZWFkLmNzdigiOCBEZXNjcmlwdGl2ZSBWaXN1YWxpemF0aW9ucyDigJMgRGF0YSBTY2llbmNlIFByb2dyYW1taW5nICgxKS5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDMuIERhdGEgU3VtbWFyaXphdGlvbg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCnNhbGVzX3N1bW1hcnkgPC0gZGF0YV9iaXNuaXMgJT4lDQogIGdyb3VwX2J5KFByb2R1Y3RfQ2F0ZWdvcnksIFJlZ2lvbikgJT4lDQogIHN1bW1hcmlzZShUb3RhbF9TYWxlcyA9IHN1bShUb3RhbF9QcmljZSwgbmEucm0gPSBUUlVFKSwgLmdyb3VwcyA9ICJkcm9wIikNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgNC4gUGxvdCBHcm91cGVkIEJhciBDaGFydA0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmdncGxvdChzYWxlc19zdW1tYXJ5LCBhZXMoeCA9IFByb2R1Y3RfQ2F0ZWdvcnksIHkgPSBUb3RhbF9TYWxlcywgZmlsbCA9IFJlZ2lvbikpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoKSkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlRvdGFsIFNhbGVzIGJ5IFByb2R1Y3QgQ2F0ZWdvcnkgYW5kIFJlZ2lvbiIsDQogICAgeCA9ICJQcm9kdWN0IENhdGVnb3J5IiwNCiAgICB5ID0gIlRvdGFsIFNhbGVzIChVU0QpIiwNCiAgICBmaWxsID0gIlJlZ2lvbiINCiAgKSArDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTApICsNCiAgdGhlbWUoDQogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAxNSwgaGp1c3QgPSAxKSwNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41KQ0KICApDQpgYGANCg0KIyMgMy4yIFJpZGdlbGluZSBQbG90DQoNCmBgYHtyLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAxLiBMb2FkIExpYnJhcmllcw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmxpYnJhcnkoZ2dyaWRnZXMpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShzY2FsZXMpDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDIuIEZpbHRlciBWYWxpZCBEYXRhDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyBGaWx0ZXIgb3V0IHJvd3Mgd2hlcmUgUHJpY2VfcGVyX1VuaXQgaXMgTkEsIEluZiwgb3IgTmFODQpkYXRhX2Jpc25pcyA8LSByZWFkLmNzdigiOCBEZXNjcmlwdGl2ZSBWaXN1YWxpemF0aW9ucyDigJMgRGF0YSBTY2llbmNlIFByb2dyYW1taW5nICgxKS5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQoNCmRhdGFfYmlzbmlzX2ZpbHRlcmVkIDwtIGRhdGFfYmlzbmlzICU+JQ0KICBmaWx0ZXIoaXMuZmluaXRlKFByaWNlX3Blcl9Vbml0KSkNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMy4gQ3JlYXRlIFJpZGdlbGluZSBQbG90DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KZ2dwbG90KGRhdGFfYmlzbmlzX2ZpbHRlcmVkLCBhZXMoeCA9IFByaWNlX3Blcl9Vbml0LCB5ID0gUmVnaW9uLCBmaWxsID0gUmVnaW9uKSkgKw0KICBnZW9tX2RlbnNpdHlfcmlkZ2VzKGFscGhhID0gMC43LCBzY2FsZSA9IDEuMikgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0gZG9sbGFyX2Zvcm1hdChwcmVmaXggPSAiUnAiLCBiaWcubWFyayA9ICIuIiwgZGVjaW1hbC5tYXJrID0gIiwiKSkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBQcmljZSBwZXIgVW5pdCBieSBSZWdpb24iLA0KICAgIHggPSAiUHJpY2UgcGVyIFVuaXQiLA0KICAgIHkgPSAiUmVnaW9uIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxNSkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KIyMgMy4zIEJveHBsb3QgYnkgQ2F0ZWdvcnkNCg0KYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDEuIExvYWQgUmVxdWlyZWQgTGlicmFyaWVzDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMi4gUHJlcGFyZSBEYXRhDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyBDb252ZXJ0IFF1YW50aXR5IHRvIG51bWVyaWMgYW5kIHJlbW92ZSBOQQ0KZGF0YV9iaXNuaXMgPC0gcmVhZC5jc3YoIjggRGVzY3JpcHRpdmUgVmlzdWFsaXphdGlvbnMg4oCTIERhdGEgU2NpZW5jZSBQcm9ncmFtbWluZyAoMSkuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KZGF0YV9iaXNuaXMgPC0gZGF0YV9iaXNuaXMgJT4lDQogIG11dGF0ZShRdWFudGl0eSA9IGFzLm51bWVyaWMoUXVhbnRpdHkpKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShRdWFudGl0eSkpDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDMuIENyZWF0ZSBCb3hwbG90DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KZ2dwbG90KGRhdGFfYmlzbmlzLCBhZXMoeCA9IFByb2R1Y3RfQ2F0ZWdvcnksIHkgPSBRdWFudGl0eSwgZmlsbCA9IFByb2R1Y3RfQ2F0ZWdvcnkpKSArDQogIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG91ciA9ICJyZWQiLCBvdXRsaWVyLnNoYXBlID0gMTYsIG91dGxpZXIuc2l6ZSA9IDEpICsgICMgQm94cGxvdCB3aXRoIHJlZCBvdXRsaWVycw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkJveHBsb3Qgb2YgUXVhbnRpdHkgYnkgUHJvZHVjdCBDYXRlZ29yeSIsDQogICAgeCA9ICJQcm9kdWN0IENhdGVnb3J5IiwNCiAgICB5ID0gIlF1YW50aXR5Ig0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMCkgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksDQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEpLA0KICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gOSksDQogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiDQogICkNCmBgYA0KDQojIyAzLjQgTG9saXBvcCBDaGFydA0KDQojIyMgTG9saXBvcCBDaGFydChHYWJ1bmdhbikNCg0KYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDEuIExvYWQgUmVxdWlyZWQgTGlicmFyaWVzDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMi4gUHJlcGFyZSBEYXRhDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KZGF0YV9iaXNuaXMgPC0gcmVhZC5jc3YoIjggRGVzY3JpcHRpdmUgVmlzdWFsaXphdGlvbnMg4oCTIERhdGEgU2NpZW5jZSBQcm9ncmFtbWluZyAoMSkuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KDQojIFN1bW1hcml6ZSB0b3RhbCBzYWxlcyBieSBQcm9kdWN0X0NhdGVnb3J5IGFuZCBSZWdpb24NCnNhbGVzX2dyb3VwZWQgPC0gZGF0YV9iaXNuaXMgJT4lDQogIGdyb3VwX2J5KFByb2R1Y3RfQ2F0ZWdvcnksIFJlZ2lvbikgJT4lDQogIHN1bW1hcmlzZShUb3RhbF9TYWxlcyA9IHN1bShUb3RhbF9QcmljZSwgbmEucm0gPSBUUlVFKSwgLmdyb3VwcyA9ICJkcm9wIikNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMy4gR3JvdXBlZCBMb2xsaXBvcCBDaGFydA0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmdncGxvdChzYWxlc19ncm91cGVkLCBhZXMoeCA9IFRvdGFsX1NhbGVzLCB5ID0gcmVvcmRlcihQcm9kdWN0X0NhdGVnb3J5LCBUb3RhbF9TYWxlcyksIGNvbG9yID0gUmVnaW9uKSkgKw0KICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB4ZW5kID0gVG90YWxfU2FsZXMsIHkgPSBQcm9kdWN0X0NhdGVnb3J5LCB5ZW5kID0gUHJvZHVjdF9DYXRlZ29yeSksIHNpemUgPSA1KSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDUpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJHcm91cGVkIExvbGxpcG9wIENoYXJ0IiwNCiAgICB4ID0gIlRvdGFsIFNhbGVzIiwNCiAgICB5ID0gIlByb2R1Y3QgQ2F0ZWdvcnkiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDIwKSArDQogIHRoZW1lKA0KICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNiksDQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gNyksDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gNywgZmFjZSA9ICJib2xkIikNCiAgKQ0KYGBgDQoNCiMjIyBMb2xpcG9wIENoYXJ0KFlhbmcgZGlwaXNhaCkNCg0KYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDQuIEZhY2V0ZWQgTG9sbGlwb3AgQ2hhcnQNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpnZ3Bsb3Qoc2FsZXNfZ3JvdXBlZCwgYWVzKHggPSBUb3RhbF9TYWxlcywgeSA9IHJlb3JkZXIoUHJvZHVjdF9DYXRlZ29yeSwgVG90YWxfU2FsZXMpKSkgKw0KICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB4ZW5kID0gVG90YWxfU2FsZXMsIHkgPSBQcm9kdWN0X0NhdGVnb3J5LCB5ZW5kID0gUHJvZHVjdF9DYXRlZ29yeSksIGNvbG9yID0gInNreWJsdWUiLCBzaXplID0gNSkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gImJsdWUiLCBzaXplID0gNSkgKw0KICBmYWNldF93cmFwKH4gUmVnaW9uLCBzY2FsZXMgPSAiZnJlZV94IikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkZhY2V0ZWQgTG9sbGlwb3AgQ2hhcnQiLA0KICAgIHggPSAiVG90YWwgU2FsZXMiLA0KICAgIHkgPSAiUHJvZHVjdCBDYXRlZ29yeSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMjApICsNCiAgdGhlbWUoDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksDQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBmYWNlID0gImJvbGQiKQ0KICApDQpgYGANCg0KIyMgMy41IEhlYXRtYXANCg0KYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHJlYWRyKQ0KDQojIEJhY2EgZGF0YQ0KZGF0YSA8LSByZWFkX2NzdigiOCBEZXNjcmlwdGl2ZSBWaXN1YWxpemF0aW9ucyDigJMgRGF0YSBTY2llbmNlIFByb2dyYW1taW5nICgxKS5jc3YiKQ0KDQojIEhpdHVuZyBhZ3JlZ2F0IHRvdGFsIHNhbGVzDQphZ2dfZGF0YSA8LSBkYXRhICU+JQ0KICBncm91cF9ieShSZWdpb24sIFByb2R1Y3RfQ2F0ZWdvcnkpICU+JQ0KICBzdW1tYXJpc2UoVG90YWxfU2FsZXMgPSBzdW0oVG90YWxfUHJpY2UsIG5hLnJtID0gVFJVRSkpICU+JQ0KICB1bmdyb3VwKCkNCg0KDQojIEJ1YXQgaGVhdG1hcA0KZ2dwbG90KGFnZ19kYXRhLCBhZXMoeCA9IFByb2R1Y3RfQ2F0ZWdvcnksIHkgPSBSZWdpb24sIGZpbGwgPSBUb3RhbF9TYWxlcykpICsNCiAgZ2VvbV90aWxlKGNvbG9yID0gIndoaXRlIiwgbGluZXdpZHRoID0gMC43KSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSByb3VuZChUb3RhbF9TYWxlcywgMCkpLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSA0KSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gImxpZ2h0eWVsbG93IiwgaGlnaCA9ICJyZWQiLCBuYW1lID0gIlRvdGFsIFNhbGVzIikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkhlYXRtYXAgUGVuanVhbGFuIGRlbmdhbiBQZW5hbmRhIE91dGxpZXIiLA0KICAgIHggPSAiS2F0ZWdvcmkgUHJvZHVrIiwNCiAgICB5ID0gIldpbGF5YWgiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDE0KSArDQogIHRoZW1lKA0KICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksDQogICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKQ0KICApDQpgYGANCg0KLS0tDQoNCiMgNCBSZWxhdGlvbnNoaXANCg0KIyMgNC4xIFNjYXR0ZXIgUGxvdA0KDQpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBBa3RpZmthbiBwYWNrYWdlIHZpc3VhbGlzYXNpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCg0KIyBCYWNhIGZpbGUgZGF0YQ0KZGF0YSA8LSByZWFkLmNzdigiOCBEZXNjcmlwdGl2ZSBWaXN1YWxpemF0aW9ucyDigJMgRGF0YSBTY2llbmNlIFByb2dyYW1taW5nICgxKS5jc3YiKQ0KDQojIEJ1YXQgc2NhdHRlciBwbG90IGRhc2FyDQpnZ3Bsb3QoZGF0YSwgYWVzKHggPSBRdWFudGl0eSwgeSA9IFRvdGFsX1ByaWNlKSkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gIm9yYW5nZSIsIHNpemUgPSAzLCBhbHBoYSA9IDAuNikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkh1YnVuZ2FuIEFudGFyYSBKdW1sYWggUHJvZHVrIGRhbiBUb3RhbCBIYXJnYSIsDQogICAgeCA9ICJKdW1sYWggUHJvZHVrIERpYmVsaSAoUXVhbnRpdHkpIiwNCiAgICB5ID0gIlRvdGFsIEhhcmdhIFBlbWJlbGlhbiAoVG90YWxfUHJpY2UpIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmBgYA0KDQojIyA0LjIgQnViYmxlIENoYXJ0DQoNCmBgYHtyLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIEFrdGlma2FuIHBhY2thZ2UgdmlzdWFsaXNhc2kNCmxpYnJhcnkodGlkeXZlcnNlKQ0KDQojIEJhY2EgZmlsZSBkYXRhDQpkYXRhIDwtIHJlYWQuY3N2KCI4IERlc2NyaXB0aXZlIFZpc3VhbGl6YXRpb25zIOKAkyBEYXRhIFNjaWVuY2UgUHJvZ3JhbW1pbmcgKDEpLmNzdiIpDQoNCiMgQnVhdCBidWJibGUgY2hhcnQNCmdncGxvdChkYXRhLCBhZXMoeCA9IFF1YW50aXR5LCB5ID0gVG90YWxfUHJpY2UsIHNpemUgPSBEaXNjb3VudCkpICsNCiAgZ2VvbV9wb2ludChjb2xvciA9ICJkZWVwc2t5Ymx1ZSIsIGFscGhhID0gMC41KSArDQogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMSwgMTApKSArICAjIEF0dXIgdWt1cmFuIGdlbGVtYnVuZw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkJ1YmJsZSBDaGFydDogUXVhbnRpdHkgdnMgVG90YWwgUHJpY2UgKFVrdXJhbiA9IERpc2tvbikiLA0KICAgIHggPSAiSnVtbGFoIFByb2R1ayBEaWJlbGkgKFF1YW50aXR5KSIsDQogICAgeSA9ICJUb3RhbCBIYXJnYSBQZW1iZWxpYW4gKFRvdGFsX1ByaWNlKSIsDQogICAgc2l6ZSA9ICJEaXNrb24iDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNCiMjIDQuMyBDb3JyZWxhdGlvbiBNYXRyaXgNCg0KYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ2NvcnJwbG90KQ0KDQpkYXRhIDwtIHJlYWRfY3N2KCI4IERlc2NyaXB0aXZlIFZpc3VhbGl6YXRpb25zIOKAkyBEYXRhIFNjaWVuY2UgUHJvZ3JhbW1pbmcgKDEpLmNzdiIpDQpudW1fZGF0YSA8LSBzZWxlY3RfaWYoZGF0YSwgaXMubnVtZXJpYykNCmNvcl9tYXRyaXggPC0gY29yKG51bV9kYXRhLCBtZXRob2QgPSAicGVhcnNvbiIpDQoNCmdnY29ycnBsb3QoDQogIGNvcl9tYXRyaXgsDQogIG1ldGhvZCA9ICJzcXVhcmUiLA0KICB0eXBlID0gImZ1bGwiLA0KICBsYWIgPSBUUlVFLA0KICBsYWJfc2l6ZSA9IDIsDQogIGNvbG9ycyA9IGMoImJsdWUiLCAid2hpdGUiLCAicmVkIiksDQogIHRpdGxlID0gIkhlYXRtYXAgS29yZWxhc2kgUGVhcnNvbiIsDQogIHRsLmNleCA9IDEwLA0KICBzaG93LmxlZ2VuZCA9IFRSVUUsDQogIGxlZ2VuZC50aXRsZSA9ICJQZWFyc29uIENvcnJlbGF0aW9uIg0KKSArIA0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSwgZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAwLjUpLA0KICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMjAsIGhqdXN0ID0gMSksDQogICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKQ0KICApDQoNCmBgYA0KDQoNCi0tLQ0KDQojIDUgVGltZSBTZXJpZXMNCg0KIyMgNS4xIExpbmUgQ2hhcnQNCg0KYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCg0KIyBMb2FkIGRhdGENCmRhdGFfYmlzbmlzIDwtIHJlYWQuY3N2KCI4IERlc2NyaXB0aXZlIFZpc3VhbGl6YXRpb25zIOKAkyBEYXRhIFNjaWVuY2UgUHJvZ3JhbW1pbmcgKDEpLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCg0KIyBQYXN0aWthbiBUcmFuc2FjdGlvbl9EYXRlIGRhbGFtIGZvcm1hdCBEYXRlDQpkYXRhX2Jpc25pcyA8LSBkYXRhX2Jpc25pcyAlPiUNCiAgbXV0YXRlKFRyYW5zYWN0aW9uX0RhdGUgPSBhcy5EYXRlKFRyYW5zYWN0aW9uX0RhdGUpKQ0KDQojIEhpdHVuZyBUb3RhbF9TYWxlcyBwZXIgdHJhbnNha3NpDQpkYXRhX2Jpc25pcyA8LSBkYXRhX2Jpc25pcyAlPiUNCiAgbXV0YXRlKFRvdGFsX1NhbGVzID0gVW5pdF9QcmljZSAqIFF1YW50aXR5KQ0KDQojIEJ1YXQgcmluZ2thc2FuIHRvdGFsIHNhbGVzIHBlciBidWxhbg0KbW9udGhseV9zYWxlcyA8LSBkYXRhX2Jpc25pcyAlPiUNCiAgbXV0YXRlKHllYXJfbW9udGggPSBmbG9vcl9kYXRlKFRyYW5zYWN0aW9uX0RhdGUsICJtb250aCIpKSAlPiUgICAjIHRhbmdnYWwgcGVydGFtYSB0aWFwIGJ1bGFuDQogIGdyb3VwX2J5KHllYXJfbW9udGgpICU+JQ0KICBzdW1tYXJpc2UoVG90YWxfU2FsZXMgPSBzdW0oVG90YWxfU2FsZXMsIG5hLnJtID0gVFJVRSksIC5ncm91cHMgPSAiZHJvcCIpDQoNCiMgQnVhdCBwbG90IGxpbmUgY2hhcnQNCmdncGxvdChtb250aGx5X3NhbGVzLCBhZXMoeCA9IHllYXJfbW9udGgsIHkgPSBUb3RhbF9TYWxlcykpICsNCiAgZ2VvbV9saW5lKGNvbG9yID0gImRhcmtvcmFuZ2UiLCBzaXplID0gMS4yKSArICANCiAgZ2VvbV9wb2ludChjb2xvciA9ICJyZWQiLCBzaXplID0gMikgKyAgICAgICAgICANCiAgc2NhbGVfeF9kYXRlKA0KICAgIGRhdGVfbGFiZWxzID0gIiViICVZIiwgDQogICAgZGF0ZV9icmVha3MgPSAiMiBtb250aHMiDQogICkgKw0KICBsYWJzKHRpdGxlID0gIlRvdGFsIFNhbGVzIExpbmUgQ2hhcnQgT3ZlciBUaW1lIiwNCiAgICAgICB4ID0gIk1vbnRoIiwNCiAgICAgICB5ID0gIlRvdGFsIFNhbGVzIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KDQpgYGANCg0KIyMgNS4yIEFyZWEgQ2hhcnQNCg0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQoNCiMgTG9hZCBkYXRhDQpkYXRhX2Jpc25pcyA8LSByZWFkLmNzdigiOCBEZXNjcmlwdGl2ZSBWaXN1YWxpemF0aW9ucyDigJMgRGF0YSBTY2llbmNlIFByb2dyYW1taW5nICgxKS5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQoNCiMgUGFzdGlrYW4gdGFuZ2dhbCBkYWxhbSBmb3JtYXQgRGF0ZSBkYW4gaGl0dW5nIFRvdGFsX1NhbGVzDQpkYXRhX2Jpc25pcyA8LSBkYXRhX2Jpc25pcyAlPiUNCiAgbXV0YXRlKFRyYW5zYWN0aW9uX0RhdGUgPSBhcy5EYXRlKFRyYW5zYWN0aW9uX0RhdGUpLA0KICAgICAgICAgVG90YWxfU2FsZXMgPSBVbml0X1ByaWNlICogUXVhbnRpdHkpDQoNCiMgUmluZ2thcyB0b3RhbCBzYWxlcyBwZXIgYnVsYW4NCm1vbnRobHlfc2FsZXMgPC0gZGF0YV9iaXNuaXMgJT4lDQogIG11dGF0ZSh5ZWFyX21vbnRoID0gZmxvb3JfZGF0ZShUcmFuc2FjdGlvbl9EYXRlLCAibW9udGgiKSkgJT4lDQogIGdyb3VwX2J5KHllYXJfbW9udGgpICU+JQ0KICBzdW1tYXJpc2UoVG90YWxfU2FsZXMgPSBzdW0oVG90YWxfU2FsZXMsIG5hLnJtID0gVFJVRSksIC5ncm91cHMgPSAiZHJvcCIpDQoNCiMgQnVhdCBhcmVhIGNoYXJ0DQpnZ3Bsb3QobW9udGhseV9zYWxlcywgYWVzKHggPSB5ZWFyX21vbnRoLCB5ID0gVG90YWxfU2FsZXMpKSArDQogIGdlb21fYXJlYShmaWxsID0gImRhcmtvcmFuZ2UiLCBhbHBoYSA9IDAuNikgKyAgICAjIGFyZWEgd2FybmEgb3JhbnllIHRyYW5zcGFyYW4NCiAgZ2VvbV9saW5lKGNvbG9yID0gInJlZCIsIHNpemUgPSAxKSArICAgICAgICAgICAgICMgZ2FyaXMgbWVyYWggZGkgYXRhcyBhcmVhDQogIGdlb21fcG9pbnQoY29sb3IgPSAicmVkIiwgc2l6ZSA9IDIpICsgICAgICAgICAgICAjIHRpdGlrIG1lcmFoIGRpIHRpYXAgYnVsYW4NCiAgc2NhbGVfeF9kYXRlKA0KICAgIGRhdGVfbGFiZWxzID0gIiViICVZIiwNCiAgICBkYXRlX2JyZWFrcyA9ICIyIG1vbnRocyINCiAgKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiVG90YWwgU2FsZXMgQXJlYSBDaGFydCBPdmVyIFRpbWUiLA0KICAgIHggPSAiTW9udGgiLA0KICAgIHkgPSAiVG90YWwgU2FsZXMiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KDQpgYGANCg0K