Visualisasi Deskriptif

Pemrograman Sains Data I

Numerical Data

Boxplot

R Code (Boxplot)

# ==============================
# 1. Load Libraries
# ==============================
library(ggplot2)
library(dplyr)

# ==============================
# 2. Load and Prepare Data
# ==============================
data_bisnis <- read.csv("data_bisnis.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)) +
  # Basic boxplot
  geom_boxplot(fill = "skyblue", outlier.shape = NA) +
  
  # Add jittered points, highlight outliers in red
  geom_jitter(aes(color = Quantity < lower_whisker | Quantity > upper_whisker),
              width = 0.1, size = 1, alpha = 0.5) +
  scale_color_manual(values = c("FALSE" = "black", "TRUE" = "red"), guide = "none") +
  
  # Highlight max point if not an outlier
  geom_point(data = data_bisnis %>% filter(Quantity == stats$Max[[1]] & Quantity <= upper_whisker),
             aes(x = factor(1), y = Quantity),
             color = "red", size = 8) +
  
  # Annotations
  ggplot2::annotate("text", x = 1.2, y = stats$Mean[[1]], 
           label = paste("Mean:", round(stats$Mean[[1]], 2)), 
           hjust = 0, fontface = "bold", color = "blue") +
  ggplot2::annotate("text", x = 1.2, y = stats$Q1[[1]], 
           label = paste("Q1:", round(stats$Q1[[1]], 2)), 
           hjust = 0, color = "darkgreen") +
  ggplot2::annotate("text", x = 1.2, y = stats$Median[[1]], 
           label = paste("Median:", round(stats$Median[[1]], 2)), 
           hjust = 0, color = "purple") +
  ggplot2::annotate("text", x = 1.2, y = stats$Q3[[1]], 
           label = paste("Q3:", round(stats$Q3[[1]], 2)), 
           hjust = 0, color = "darkgreen") +
  ggplot2::annotate("text", x = 1.2, y = stats$Min[[1]], 
           label = paste("Min:", round(stats$Min[[1]], 2)), 
           hjust = 0, color = "orange") +
  ggplot2::annotate("text", x = 1.2, y = stats$Max[[1]], 
           label = paste("Max:", round(stats$Max[[1]], 2)), 
           hjust = 0, color = "orange") +
  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) +

  # Plot formatting
  labs(
    title = "Boxplot of Quantity with Jitter and Annotations",
    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 = 15),
    axis.text = element_text(size = 10)
  )

Python Code (Boxplot)

# ==============================
# 1. Load Libraries
# ==============================
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np

# ==============================
# 2. Load and Prepare Data
# ==============================
data_bisnis = pd.read_csv("data_bisnis.csv")

# Convert Quantity to numeric and filter missing
data_bisnis['Quantity'] = pd.to_numeric(data_bisnis['Quantity'], errors='coerce')
data_bisnis = data_bisnis.dropna(subset=['Quantity'])

# Compute IQR-based outlier bounds
Q1 = data_bisnis['Quantity'].quantile(0.25)
Q3 = data_bisnis['Quantity'].quantile(0.75)
IQR_value = Q3 - Q1
lower_whisker = Q1 - 1.5 * IQR_value
upper_whisker = Q3 + 1.5 * IQR_value

# ==============================
# 3. Summarize Statistics
# ==============================
stats = {
    'Mean': data_bisnis['Quantity'].mean(),
    'Q1': Q1,
    'Median': data_bisnis['Quantity'].median(),
    'Q3': Q3,
    'Min': data_bisnis['Quantity'].min(),
    'Max': data_bisnis['Quantity'].max(),
    'Outliers': ((data_bisnis['Quantity'] < lower_whisker) | (data_bisnis['Quantity'] > upper_whisker)).sum()
}

# ==============================
# 4. Basic Boxplot with Jitter and Annotations
# ==============================
plt.figure(figsize=(10, 8))
sns.boxplot(x=[""] * len(data_bisnis), y=data_bisnis['Quantity'], color='skyblue', showfliers=False)

# Add jitter points
is_outlier = (data_bisnis['Quantity'] < lower_whisker) | (data_bisnis['Quantity'] > upper_whisker)
sns.stripplot(x=[""] * len(data_bisnis), y=data_bisnis['Quantity'], 
              hue=is_outlier, palette={False: "black", True: "red"}, 
              dodge=False, jitter=0.1, size=5, alpha=0.5)
plt.legend([],[], frameon=False)

# Highlight max point if not an outlier
if stats['Max'] <= upper_whisker:
    plt.scatter(0, stats['Max'], color='red', s=200, zorder=3)

# Annotations
plt.text(0.1, stats['Mean'], f"Mean: {stats['Mean']:.2f}", color="blue", weight='bold')
plt.text(0.1, stats['Q1'], f"Q1: {stats['Q1']:.2f}", color="darkgreen")
plt.text(0.1, stats['Median'], f"Median: {stats['Median']:.2f}", color="purple")
plt.text(0.1, stats['Q3'], f"Q3: {stats['Q3']:.2f}", color="darkgreen")
plt.text(0.1, stats['Min'], f"Min: {stats['Min']:.2f}", color="orange")
plt.text(0.1, stats['Max'], f"Max: {stats['Max']:.2f}", color="orange")
plt.text(0, stats['Max'] * 1.05, f"Outliers: {stats['Outliers']}", color="red", style='italic', ha='center')

# Plot formatting
plt.title("Boxplot of Quantity with Jitter and Annotations", fontsize=20, weight='bold')
plt.ylabel("Quantity", fontsize=16)
plt.xticks([])
## ([], [])
plt.tick_params(axis='y', labelsize=14)
plt.tight_layout()
plt.show()

Violin-plot

R Code (Violin-plot)

# ==============================
# 1. Load Libraries
# ==============================
library(ggplot2)
library(dplyr)

# ==============================
# 2. Load and Prepare Data
# ==============================
data_bisnis <- read.csv("data_bisnis.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")
  )

# ==============================
# 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(is_outlier == "Outlier")
  )

# ==============================
# 4. Create Violin Plot with Colored Jitter and Annotations
# ==============================
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) +

  # Annotations via geom_text
  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() +
  theme(
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank(),
    plot.title = element_text(size = 20, face = "bold"),
    axis.title = element_text(size = 15),
    axis.text = element_text(size = 15),
    legend.position = "right",
    legend.title = element_text(size = 10),
    legend.text = element_text(size = 9)
  )

Python Code (Violin-plot)

# ==============================
# 1. Load Libraries
# ==============================
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

# ==============================
# 2. Load and Prepare Data
# ==============================
# Load CSV
data_bisnis = pd.read_csv("data_bisnis.csv")

# Convert Quantity to numeric and drop NA
data_bisnis['Quantity'] = pd.to_numeric(data_bisnis['Quantity'], errors='coerce')
data_bisnis = data_bisnis.dropna(subset=['Quantity'])

# Calculate quartiles and IQR
Q1 = data_bisnis['Quantity'].quantile(0.25)
Q3 = data_bisnis['Quantity'].quantile(0.75)
IQR_value = Q3 - Q1
lower_whisker = Q1 - 1.5 * IQR_value
upper_whisker = Q3 + 1.5 * IQR_value

# Label outliers
data_bisnis['is_outlier'] = data_bisnis['Quantity'].apply(
    lambda x: 'Outlier' if x < lower_whisker or x > upper_whisker else 'Normal'
)

# ==============================
# 3. Summarize Statistics
# ==============================
stats = {
    'Mean': data_bisnis['Quantity'].mean(),
    'Q1': Q1,
    'Median': data_bisnis['Quantity'].median(),
    'Q3': Q3,
    'Min': data_bisnis['Quantity'].min(),
    'Max': data_bisnis['Quantity'].max(),
    'Outliers': (data_bisnis['is_outlier'] == 'Outlier').sum()
}

# ==============================
# 4. Create Violin Plot with Colored Jitter and Annotations
# ==============================
plt.figure(figsize=(10, 8))

# Violin plot
sns.violinplot(
    x=[""] * len(data_bisnis),
    y=data_bisnis['Quantity'],
    inner=None,
    color="skyblue"
)

# Boxplot overlay
sns.boxplot(
    x=[""] * len(data_bisnis),
    y=data_bisnis['Quantity'],
    width=0.1,
    showcaps=True,
    boxprops={'facecolor': 'white', 'edgecolor': 'black'},
    whiskerprops={'color': 'black'},
    medianprops={'color': 'black'},
    flierprops={'marker': None}
)

# Jittered points with outlier color
sns.stripplot(
    x=[""] * len(data_bisnis),
    y=data_bisnis['Quantity'],
    hue=data_bisnis['is_outlier'],
    palette={"Normal": "black", "Outlier": "red"},
    dodge=False,
    jitter=0.1,
    alpha=0.6,
    size=4
)

# Highlight maximum point (if not outlier)
if stats['Max'] <= upper_whisker:
    plt.scatter(
        0,
        stats['Max'],
        color='red',
        s=200,
        zorder=3
    )

# ==============================
# 5. Annotations
# ==============================
plt.text(0.1, stats['Mean'],   f"Mean: {stats['Mean']:.2f}",   color="blue",     weight='bold')
plt.text(0.1, stats['Q1'],     f"Q1: {stats['Q1']:.2f}",       color="darkgreen")
plt.text(0.1, stats['Median'], f"Median: {stats['Median']:.2f}", color="purple")
plt.text(0.1, stats['Q3'],     f"Q3: {stats['Q3']:.2f}",       color="darkgreen")
plt.text(0.1, stats['Min'],    f"Min: {stats['Min']:.2f}",     color="orange")
plt.text(0.1, stats['Max'],    f"Max: {stats['Max']:.2f}",     color="orange")
plt.text(0, stats['Max'] * 1.05, f"Outliers: {stats['Outliers']}", color="red", style='italic', ha='center')

# ==============================
# 6. Plot Formatting
# ==============================
plt.title("Violin Plot of Quantity with Outlier Highlighted", fontsize=20, weight='bold')
plt.ylabel("Quantity", fontsize=16)
plt.xticks([])
## ([], [])
plt.tick_params(axis='y', labelsize=14)
plt.legend(title="Point Type", title_fontsize=10, fontsize=9, loc="upper right")
plt.tight_layout()
plt.show()

Combo

Grouped Bar Chart

R Code (Grouped Bar-chart)

# ==============================
# 1. Load Libraries
# ==============================
library(ggplot2)
library(dplyr)

# ==============================
# 2. Load Data
# ==============================
data_bisnis <- read.csv("data_bisnis.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 = 25, hjust = 1),
    plot.title = element_text(face = "bold", hjust = 0.5)
  )

Python Code (Grouped Bar-chart)

# ==============================
# 1. Load Libraries
# ==============================
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

# ==============================
# 2. Load Data
# ==============================
data_bisnis = pd.read_csv("data_bisnis.csv")

# ==============================
# 3. Data Summarization
# ==============================
sales_summary = (
    data_bisnis
    .groupby(['Product_Category', 'Region'], as_index=False)
    .agg(Total_Sales=('Total_Price', 'sum'))
)

# ==============================
# 4. Plot Grouped Bar Chart
# ==============================
plt.figure(figsize=(10, 6))
sns.set_theme(style="whitegrid")

barplot = sns.barplot(
    data=sales_summary,
    x="Product_Category",
    y="Total_Sales",
    hue="Region",
    dodge=True,
    palette="Set2"
)

# Tambahkan label dan title
barplot.set_title("Total Sales by Product Category and Region", fontsize=14, fontweight='bold')
barplot.set_xlabel("Product Category", fontsize=12)
barplot.set_ylabel("Total Sales (USD)", fontsize=12)

# Rotasi label sumbu x
plt.xticks(rotation=25, ha='right')
## ([0, 1, 2, 3, 4], [Text(0, 0, 'Books'), Text(1, 0, 'Clothing'), Text(2, 0, 'Electronics'), Text(3, 0, 'Groceries'), Text(4, 0, 'Home')])
# Perbaiki legend
plt.legend(title="Region", title_fontsize=10, fontsize=9)

plt.tight_layout()
plt.show()

Ridgeline Plot

R Code (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("data_bisnis.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(legend.position = "none")

Python Code (Ridgeline Plot)

# ==============================
# 1. Load Libraries
# ==============================
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from joypy import joyplot
import numpy as np
import matplotlib.ticker as ticker

# ==============================
# 2. Load and Filter Valid Data
# ==============================
data_bisnis = pd.read_csv("data_bisnis.csv")

# Filter: hanya baris dengan Price_per_Unit yang finite
data_bisnis_filtered = data_bisnis[np.isfinite(data_bisnis["Price_per_Unit"])]

# ==============================
# 3. Create Ridgeline Plot
# ==============================
plt.figure(figsize=(10, 6))

# Joyplot (Ridgeline)
fig, axes = joyplot(
    data_bisnis_filtered,
    by="Region",
    column="Price_per_Unit",
    colormap=plt.cm.Set3,
    alpha=0.7,
    overlap=1.2,
    linewidth=1,
    fade=True
)

# Formatting x-axis as Indonesian-style currency (Rp)
axes[-1].xaxis.set_major_formatter(ticker.FuncFormatter(
    lambda x, pos: f'Rp{x:,.0f}'.replace(',', '.')
))

# Title and labels
plt.title("Distribution of Price per Unit by Region", fontsize=14, weight='bold')
plt.xlabel("Price per Unit", fontsize=10)
plt.ylabel("Region", fontsize=10)

plt.tight_layout()
plt.show()

Boxplot by Category

R Code (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("data_bisnis.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 = 2) +  # Boxplot with red outliers
  labs(
    title = "Boxplot of Quantity by Product Category",
    x = "Product Category",
    y = "Quantity"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(size = 14, face = "bold"),
    axis.title = element_text(size = 12),
    axis.text = element_text(size = 10),
    legend.position = "none"
  )

Python Code (Boxplot by Category)

# ==============================
# 1. Load Required Libraries
# ==============================
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

# ==============================
# 2. Prepare Data
# ==============================
# Load data and preprocess
data_bisnis = pd.read_csv("data_bisnis.csv")
data_bisnis['Quantity'] = pd.to_numeric(data_bisnis['Quantity'], errors='coerce')
data_bisnis = data_bisnis.dropna(subset=['Quantity'])

# Sort categories alphabetically for consistent x-axis ordering
sorted_categories = sorted(data_bisnis['Product_Category'].dropna().unique())
data_bisnis['Product_Category'] = pd.Categorical(data_bisnis['Product_Category'], categories=sorted_categories, ordered=True)

# ==============================
# 3. Create Boxplot
# ==============================
plt.figure(figsize=(10, 6))
sns.boxplot(
    x='Product_Category',
    y='Quantity',
    data=data_bisnis,
    palette='pastel',
    showfliers=True,
    flierprops=dict(marker='o', markerfacecolor='red', markersize=5, linestyle='none')
)

plt.title("Boxplot of Quantity by Product Category", fontsize=14, fontweight='bold')
plt.xlabel("Product Category", fontsize=12)
plt.ylabel("Quantity", fontsize=12)
plt.xticks(fontsize=10)
## ([0, 1, 2, 3, 4], [Text(0, 0, 'Books'), Text(1, 0, 'Clothing'), Text(2, 0, 'Electronics'), Text(3, 0, 'Groceries'), Text(4, 0, 'Home')])
plt.yticks(fontsize=10)
## (array([-2.,  0.,  2.,  4.,  6.,  8., 10.]), [Text(0, -2.0, '−2'), Text(0, 0.0, '0'), Text(0, 2.0, '2'), Text(0, 4.0, '4'), Text(0, 6.0, '6'), Text(0, 8.0, '8'), Text(0, 10.0, '10')])
sns.despine()  # equivalent to theme_minimal
plt.tight_layout()
plt.show()

Lollipop Chart

R Code (Lollipop Chart)

# ==============================
# 1. Load Required Libraries
# ==============================
library(ggplot2)
library(dplyr)

# ==============================
# 2. Prepare Data
# ==============================
data_bisnis <- read.csv("data_bisnis.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 = 1) +
  geom_point(size = 4) +
  labs(
    title = "Grouped Lollipop Chart",
    x = "Total Sales",
    y = "Product Category"
  ) +
  theme_minimal() +
  theme(
    axis.text = element_text(size = 12),
    axis.title = element_text(size = 14),
    plot.title = element_text(size = 16, face = "bold")
  )

# ==============================
# 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 = 1) +
  geom_point(color = "blue", size = 4) +
  facet_wrap(~ Region, scales = "free_x") +
  labs(
    title = "Faceted Lollipop Chart",
    x = "Total Sales",
    y = "Product Category"
  ) +
  theme_minimal() +
  theme(
    axis.text = element_text(size = 12),
    axis.title = element_text(size = 14),
    plot.title = element_text(size = 16, face = "bold")
  )

Python Code (Lollipop Chart)

# ==============================
# 1. Load Required Libraries
# ==============================
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# Optional for theme style
sns.set_theme(style="whitegrid")

# ==============================
# 2. Prepare Data
# ==============================
data_bisnis = pd.read_csv("data_bisnis.csv")

# Summarize total sales by Product_Category and Region
sales_grouped = (
    data_bisnis
    .groupby(["Product_Category", "Region"], as_index=False)
    .agg(Total_Sales=("Total_Price", "sum"))
)

# ==============================
# 3. Grouped Lollipop Chart
# ==============================
plt.figure(figsize=(10, 6))

# Sort to get consistent y-axis
sales_grouped_sorted = sales_grouped.sort_values("Total_Sales")
categories = sales_grouped_sorted["Product_Category"].unique()

# Plot
for region in sales_grouped["Region"].unique():
    region_data = sales_grouped[sales_grouped["Region"] == region]
    region_data = region_data.sort_values("Total_Sales")
    plt.hlines(y=region_data["Product_Category"], xmin=0, xmax=region_data["Total_Sales"], label=region, linewidth=2)
    plt.plot(region_data["Total_Sales"], region_data["Product_Category"], 'o', markersize=8, label=f"{region}")

plt.title("Grouped Lollipop Chart", fontsize=16, fontweight='bold')
plt.xlabel("Total Sales", fontsize=14)
plt.ylabel("Product Category", fontsize=14)
plt.xticks(fontsize=12)
## (array([-200.,    0.,  200.,  400.,  600.,  800., 1000., 1200., 1400.,
##        1600.]), [Text(-200.0, 0, '−200'), Text(0.0, 0, '0'), Text(200.0, 0, '200'), Text(400.0, 0, '400'), Text(600.0, 0, '600'), Text(800.0, 0, '800'), Text(1000.0, 0, '1000'), Text(1200.0, 0, '1200'), Text(1400.0, 0, '1400'), Text(1600.0, 0, '1600')])
plt.yticks(fontsize=12)
## ([0, 1, 2, 3, 4], [Text(0, 0, 'Groceries'), Text(0, 1, 'Books'), Text(0, 2, 'Electronics'), Text(0, 3, 'Home'), Text(0, 4, 'Clothing')])
plt.legend(title="Region")
plt.tight_layout()
plt.show()

# ==============================
# 4. Faceted Lollipop Chart (2x2 Layout)
# ==============================
# Create FacetGrid with 2 columns per row
g = sns.FacetGrid(sales_grouped, col="Region", col_wrap=2, sharex=False, height=5, aspect=1.2)

# Define plotting function
def lollipop(data, **kwargs):
    data = data.sort_values("Total_Sales")
    plt.hlines(y=data["Product_Category"], xmin=0, xmax=data["Total_Sales"], color="skyblue", linewidth=2)
    plt.plot(data["Total_Sales"], data["Product_Category"], 'o', color="blue", markersize=8)

# Map to each facet
g.map_dataframe(lollipop)

g.set_axis_labels("Total Sales", "Product Category")

g.set_titles(col_template="{col_name}")

g.fig.subplots_adjust(top=0.9)
g.fig.suptitle("Faceted Lollipop Chart", fontsize=16, fontweight='bold')
plt.show()

Heatmap

R Code (Heatmap)

# ==============================
# ==============================
# 1. Load Required Libraries
# ==============================
library(ggplot2)
library(dplyr)
library(readr)
library(forcats)  # Untuk fct_rev()

# ==============================
# 2. Load and Prepare Data
# ==============================
data_bisnis <- read_csv("data_bisnis.csv")

# Hitung total sales berdasarkan Region dan Product_Category
heatmap_data <- data_bisnis %>%
  group_by(Region, Product_Category) %>%
  summarise(Total_Sales = sum(Total_Price, na.rm = TRUE), .groups = "drop") %>%
  mutate(Product_Category = fct_rev(factor(Product_Category)))  # Urutkan alfabet terbalik

# ==============================
# 3. Create Heatmap with Labels
# ==============================
ggplot(heatmap_data, aes(x = Region, y = Product_Category, fill = Total_Sales)) +
  geom_tile(color = "white") +
  geom_text(aes(label = round(Total_Sales, 0)), color = "black", size = 4) +
  scale_fill_gradient(low = "#ffe5e5", high = "#990000", name = "Total Sales") +
  labs(
    title = "Heatmap of Total Sales by Region and Product Category",
    x = "Region",
    y = "Product Category"
  ) +
  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 25, hjust = 1),
    plot.title = element_text(size = 16, face = "bold"),
    axis.title = element_text(size = 14),
    axis.text = element_text(size = 12)
  )

Python Code (Heatmap)

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

# ==============================
# 1. Load and Prepare Data
# ==============================
df = pd.read_csv('data_bisnis.csv')

# Hitung total sales per kategori dan region
heatmap_data = df.groupby(['Product_Category', 'Region'])['Total_Price'].sum().reset_index()

# Pivot data menjadi format matriks
heatmap_pivot = heatmap_data.pivot(index='Product_Category', columns='Region', values='Total_Price')

# ==============================
# 2. Plot Heatmap
# ==============================
plt.figure(figsize=(10, 6))
sns.heatmap(heatmap_pivot, cmap='Reds', linewidths=0.5, annot=True, fmt=".0f")

plt.title('Heatmap of Total Sales by Region and Product Category', fontsize=16, fontweight='bold')
plt.xlabel('Region', fontsize=14)
plt.ylabel('Product Category', fontsize=14)
plt.xticks(rotation=25)
## (array([0.5, 1.5, 2.5, 3.5]), [Text(0.5, 0, 'East'), Text(1.5, 0, 'North'), Text(2.5, 0, 'South'), Text(3.5, 0, 'West')])
plt.yticks(rotation=0)
## (array([0.5, 1.5, 2.5, 3.5, 4.5]), [Text(0, 0.5, 'Books'), Text(0, 1.5, 'Clothing'), Text(0, 2.5, 'Electronics'), Text(0, 3.5, 'Groceries'), Text(0, 4.5, 'Home')])
plt.tight_layout()
plt.show()

Relationship

Scatter Plot

R Code (Scatter Plot)

# ==============================
# 1. Load Required Libraries
# ==============================
library(ggplot2)
library(dplyr)
library(readr)

# ==============================
# 2. Load and Prepare Data
# ==============================
data_bisnis <- read_csv("data_bisnis.csv")

# Pastikan Quantity dan Total_Price bertipe numerik dan tidak NA
data_bisnis <- data_bisnis %>%
  mutate(
    Quantity = as.numeric(Quantity),
    Total_Price = as.numeric(Total_Price)
  ) %>%
  filter(!is.na(Quantity), !is.na(Total_Price))

# ==============================
# 3. Create Scatter Plot
# ==============================
ggplot(data_bisnis, aes(x = Quantity, y = Total_Price, color = Product_Category)) +
  geom_point(alpha = 0.7, size = 3) +
  labs(
    title = "Scatter Plot of Total Price vs Quantity",
    x = "Quantity",
    y = "Total Price",
    color = "Product Category"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(size = 16, face = "bold"),
    axis.title = element_text(size = 14),
    axis.text = element_text(size = 12)
  )

Python Code (Scatter Plot)

# ==============================
# 1. Load Required Libraries
# ==============================
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

# ==============================
# 2. Load and Prepare Data
# ==============================
df = pd.read_csv("data_bisnis.csv")

# Pastikan kolom numerik benar
df['Quantity'] = pd.to_numeric(df['Quantity'], errors='coerce')
df['Total_Price'] = pd.to_numeric(df['Total_Price'], errors='coerce')
df = df.dropna(subset=['Quantity', 'Total_Price'])

# ==============================
# 3. Create Scatter Plot
# ==============================
plt.figure(figsize=(10, 6))
sns.scatterplot(
    data=df,
    x='Quantity',
    y='Total_Price',
    hue='Product_Category',
    palette='Set2',
    alpha=0.7,
    s=80
)

plt.title("Scatter Plot of Total Price vs Quantity", fontsize=16, weight='bold')
plt.xlabel("Quantity", fontsize=14)
plt.ylabel("Total Price", fontsize=14)
plt.xticks(fontsize=12)
## (array([-2.,  0.,  2.,  4.,  6.,  8., 10.]), [Text(-2.0, 0, '−2'), Text(0.0, 0, '0'), Text(2.0, 0, '2'), Text(4.0, 0, '4'), Text(6.0, 0, '6'), Text(8.0, 0, '8'), Text(10.0, 0, '10')])
plt.yticks(fontsize=12)
## (array([-25.,   0.,  25.,  50.,  75., 100., 125., 150., 175., 200.]), [Text(0, -25.0, '−25'), Text(0, 0.0, '0'), Text(0, 25.0, '25'), Text(0, 50.0, '50'), Text(0, 75.0, '75'), Text(0, 100.0, '100'), Text(0, 125.0, '125'), Text(0, 150.0, '150'), Text(0, 175.0, '175'), Text(0, 200.0, '200')])
plt.legend(title='Product Category', fontsize=10, title_fontsize=12)
plt.grid(True)
plt.tight_layout()
plt.show()

Bubble Chart

R Code (Bubble Chart)

# ==============================
# 1. Load Required Libraries
# ==============================
library(ggplot2)
library(dplyr)
library(readr)

# ==============================
# 2. Load and Prepare Data
# ==============================
data_bisnis <- read_csv("data_bisnis.csv")

# Pastikan kolom numerik
data_bisnis <- data_bisnis %>%
  mutate(
    Quantity = as.numeric(Quantity),
    Unit_Price = as.numeric(Unit_Price),
    Total_Price = as.numeric(Total_Price)
  ) %>%
  filter(!is.na(Quantity) & !is.na(Unit_Price) & !is.na(Total_Price) & !is.na(Product_Category))

# ==============================
# 3. Bubble Chart
# ==============================
ggplot(data_bisnis, aes(x = Quantity, y = Unit_Price, size = Total_Price, color = Product_Category)) +
  geom_point(alpha = 0.6) +
  scale_size_continuous(range = c(2, 20)) +  # Atur ukuran bubble
  labs(
    title = "Bubble Chart of Quantity vs Unit Price",
    subtitle = "Bubble Size = Total Price",
    x = "Quantity",
    y = "Unit Price",
    size = "Total Price",
    color = "Product Category"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(size = 16, face = "bold"),
    axis.title = element_text(size = 14),
    legend.title = element_text(size = 12)
  )

Python Code (Bubble Chart)

# ==============================
# 1. Load Required Libraries
# ==============================
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# ==============================
# 2. Load and Prepare Data
# ==============================
data = pd.read_csv("data_bisnis.csv")

# Pastikan kolom numerik
data['Quantity'] = pd.to_numeric(data['Quantity'], errors='coerce')
data['Unit_Price'] = pd.to_numeric(data['Unit_Price'], errors='coerce')
data['Total_Price'] = pd.to_numeric(data['Total_Price'], errors='coerce')

# Hapus baris yang mengandung NA
data = data.dropna(subset=['Quantity', 'Unit_Price', 'Total_Price', 'Product_Category'])

# ==============================
# 3. Bubble Chart
# ==============================
plt.figure(figsize=(12, 8))
bubble = sns.scatterplot(
    data=data,
    x='Quantity',
    y='Unit_Price',
    size='Total_Price',
    hue='Product_Category',
    sizes=(50, 1200),   # Ukuran gelembung serupa dengan R (2-20 dikonversi)
    alpha=0.6,
    palette='tab10',
    edgecolor='w',
    linewidth=0.5
)

# ==============================
# 4. Customize Plot
# ==============================
plt.title("Bubble Chart of Quantity vs Unit Price", fontsize=16, fontweight='bold')
plt.suptitle("Bubble Size = Total Price", fontsize=12)
plt.xlabel("Quantity", fontsize=14)
plt.ylabel("Unit Price", fontsize=14)
plt.legend(title="Product Category", title_fontsize=12, loc='best', bbox_to_anchor=(1.05, 1))
plt.tight_layout()
plt.grid(True)
plt.show()

Correlation Matrix

R Code (Correlation Matrix)

# ==============================
# 1. Load Required Libraries
# ==============================
library(readr)
library(dplyr)
library(ggplot2)
library(reshape2)
library(RColorBrewer)

# ==============================
# 2. Load and Select Data
# ==============================
data <- read_csv("data_bisnis.csv")

# Pilih variabel numerik dan hilangkan NA
selected_vars <- data %>%
  select(Quantity, Unit_Price, Discount, Feature_Interaction) %>%
  na.omit()

# ==============================
# 3. Compute Correlation Matrix
# ==============================
cor_mat <- round(cor(selected_vars, use = "complete.obs"), 2)

# Ubah ke format long untuk heatmap
melted_cor <- melt(cor_mat)

# ==============================
# 4. Plot Correlation Heatmap
# ==============================
ggplot(melted_cor, aes(x = Var1, y = factor(Var2, levels = rev(colnames(cor_mat))), fill = value)) +
  geom_tile(color = "white") +
  geom_text(aes(label = value), color = "black", size = 4) +
  scale_fill_gradientn(
    colors = brewer.pal(n = 9, name = "YlGnBu"),
    limits = c(-1, 1),
    name = "Correlation"
  ) +
  coord_fixed() +
  labs(
    title = "Correlation Matrix Heatmap",
    x = NULL,
    y = NULL
  ) +
  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    axis.text = element_text(size = 12),
    plot.title = element_text(size = 16, face = "bold")
  )

Python Code (Correlation Matrix)

# ==============================
# 1. Load Required Libraries
# ==============================
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

# ==============================
# 2. Load and Select Data
# ==============================
# Baca data dari file CSV
data = pd.read_csv("data_bisnis.csv")

# Pilih variabel numerik yang relevan
selected_vars = data[['Quantity', 'Unit_Price', 'Discount', 'Feature_Interaction']].dropna()

# ==============================
# 3. Compute Correlation Matrix
# ==============================
cor_matrix = selected_vars.corr().round(2)

# ==============================
# 4. Plot Correlation Heatmap
# ==============================
plt.figure(figsize=(8, 6))
heatmap = sns.heatmap(
    cor_matrix,          
    annot=True,                     # Tampilkan nilai korelasi
    fmt=".2f",                      # Format desimal
    cmap="YlGnBu",                  # Warna gradasi
    vmin=-1, vmax=1,                # Skala korelasi
    linewidths=0.5, linecolor='white'  # Garis antar sel
)

# ==============================
# 5. Customize Aesthetics
# ==============================
plt.title("Correlation Matrix Heatmap", fontsize=16, fontweight='bold')
plt.xticks(rotation=45, ha='right')
## (array([0.5, 1.5, 2.5, 3.5]), [Text(0.5, 0, 'Quantity'), Text(1.5, 0, 'Unit_Price'), Text(2.5, 0, 'Discount'), Text(3.5, 0, 'Feature_Interaction')])
plt.yticks(rotation=0)
## (array([0.5, 1.5, 2.5, 3.5]), [Text(0, 0.5, 'Quantity'), Text(0, 1.5, 'Unit_Price'), Text(0, 2.5, 'Discount'), Text(0, 3.5, 'Feature_Interaction')])
plt.tight_layout()

# Tampilkan plot
plt.show()

Time Series

Line Chart

R Code (Line Chart)

# ==============================
# 1. Load Required Libraries
# ==============================
library(ggplot2)
library(dplyr)
library(readr)
library(lubridate)
library(scales)

# ==============================
# 2. Load and Prepare Data
# ==============================
data <- read_csv("data_bisnis.csv")

# Format tanggal dan buat kolom YearMonth
data <- data %>%
  mutate(
    Transaction_Date = as.Date(Transaction_Date),
    YearMonth = floor_date(Transaction_Date, "month")
  )

# Agregasi total sales per bulan per region
monthly_region_sales <- data %>%
  group_by(YearMonth, Region) %>%
  summarise(Total_Sales = sum(Total_Price, na.rm = TRUE), .groups = "drop")

# ==============================
# 3. Plot Line Chart per Region (X: tahun, data per bulan)
# ==============================
ggplot(monthly_region_sales, aes(x = YearMonth, y = Total_Sales, color = Region)) +
  geom_line(size = 1.2, alpha = 0.9) +
  geom_point(size = 2, alpha = 0.8) +
  labs(
    title = "Tren Total Penjualan Bulanan per Region",
    x = "Tahun",
    y = "Total Penjualan",
    color = "Region"
  ) +
  scale_x_date(
    date_labels = "%Y",            # Tampilkan hanya tahun
    date_breaks = "1 year"         # Interval label 1 tahun
  ) +
  theme_minimal(base_size = 13) +
  theme(
    plot.title = element_text(size = 18, face = "bold", hjust = 0.5),
    plot.subtitle = element_text(size = 13, face = "italic", hjust = 0.5),
    axis.text.x = element_text(angle = 0, hjust = 0.5),
    axis.title = element_text(size = 14),
    legend.title = element_text(size = 13),
    legend.text = element_text(size = 11)
  )

Python Code (Line Chart)

# ==============================
# 1. Load Required Libraries
# ==============================
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# ==============================
# 2. Load and Prepare Data
# ==============================
# Baca data
data = pd.read_csv("data_bisnis.csv")

# Pastikan kolom tanggal dalam format datetime dan buat kolom YearMonth
data['Transaction_Date'] = pd.to_datetime(data['Transaction_Date'])
data['YearMonth'] = data['Transaction_Date'].dt.to_period('M').dt.to_timestamp()

# Agregasi total sales per bulan per region
monthly_region_sales = data.groupby(['YearMonth', 'Region'])['Total_Price'].sum().reset_index()

# ==============================
# 3. Plot Line Chart per Region
# ==============================
plt.figure(figsize=(12, 6))
sns.lineplot(data=monthly_region_sales, x='YearMonth', y='Total_Price', hue='Region', marker='o')

# ==============================
# 4. Customize Aesthetics
# ==============================
plt.title("Monthly Total Sales by Region", fontsize=16, fontweight='bold')
plt.xlabel("Year")
plt.ylabel("Total Sales")
plt.legend(title='Region')
plt.grid(True)

# Format x-axis: show label every January only
plt.xticks(
    ticks=[d for d in monthly_region_sales['YearMonth'].unique() if d.month == 1],
    labels=[d.strftime('%Y') for d in monthly_region_sales['YearMonth'].unique() if d.month == 1],
    rotation=0
)
## ([<matplotlib.axis.XTick object at 0x000001B442EE3A10>, <matplotlib.axis.XTick object at 0x000001B44310C350>, <matplotlib.axis.XTick object at 0x000001B443091D60>, <matplotlib.axis.XTick object at 0x000001B44F09A6F0>, <matplotlib.axis.XTick object at 0x000001B44F099A00>], [Text(18262.0, 0, '2020'), Text(18628.0, 0, '2021'), Text(18993.0, 0, '2022'), Text(19358.0, 0, '2023'), Text(19723.0, 0, '2024')])
plt.tight_layout()
plt.show()

Area Chart

R Code (Area Chart)

# ==============================
# 1. Load Required Libraries
# ==============================
library(ggplot2)
library(dplyr)
library(readr)
library(lubridate)
library(scales)

# ==============================
# 2. Load and Prepare Data
# ==============================
# Baca data
data <- read_csv("data_bisnis.csv")

# Format tanggal dan buat kolom YearMonth
data <- data %>%
  mutate(
    Transaction_Date = as.Date(Transaction_Date),
    YearMonth = floor_date(Transaction_Date, "month")
  )

# Agregasi total sales per bulan per region
monthly_region_sales <- data %>%
  group_by(YearMonth, Region) %>%
  summarise(Total_Sales = sum(Total_Price, na.rm = TRUE), .groups = "drop")

# ==============================
# 3. Plot Area Chart
# ==============================
ggplot(monthly_region_sales, aes(x = YearMonth, y = Total_Sales, fill = Region)) +
  geom_area(alpha = 0.8, size = 0.5, colour = "white") +
  scale_fill_brewer(palette = "Set2") +
  scale_x_date(date_breaks = "1 year", date_labels = "%Y") +
  labs(
    title = "Monthly Total Sales by Region (Area Chart)",
    x = "Year",
    y = "Total Sales",
    fill = "Region"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(size = 16, face = "bold"),
    axis.title = element_text(size = 14),
    axis.text.x = element_text(size = 11, angle = 0, vjust = 0.5),
    axis.text.y = element_text(size = 12),
    legend.title = element_text(size = 13),
    legend.text = element_text(size = 11)
  )

import pandas as pd
import matplotlib.pyplot as plt

# ==============================
# 1. Load and Prepare Data
# ==============================
data = pd.read_csv("data_bisnis.csv")
data['Transaction_Date'] = pd.to_datetime(data['Transaction_Date'])
data['YearMonth'] = data['Transaction_Date'].dt.to_period('M').dt.to_timestamp()

# Agregasi total sales per bulan per region
monthly_sales = data.groupby(['YearMonth', 'Region'])['Total_Price'].sum().reset_index()

# Pivot untuk area chart
pivot_data = monthly_sales.pivot(index='YearMonth', columns='Region', values='Total_Price').fillna(0)

# ==============================
# 2. Plot Area Chart
# ==============================
plt.figure(figsize=(12, 6))
pivot_data.plot(kind='area', stacked=True, figsize=(12, 6), alpha=0.7)

plt.title("Monthly Total Sales by Region (Area Chart)", fontsize=16, fontweight='bold')
plt.xlabel("Year")
plt.ylabel("Total Sales")
plt.xticks(rotation=45)
## (array([600, 612, 624, 636, 648]), [Text(600, 0, '2020'), Text(612, 0, '2021'), Text(624, 0, '2022'), Text(636, 0, '2023'), Text(648, 0, '2024')])
plt.tight_layout()
plt.grid(True)
plt.legend(title='Region')
plt.show()

LS0tDQp0aXRsZTogIlZpc3VhbGlzYXNpIERlc2tyaXB0aWYiDQpzdWJ0aXRsZTogIlBlbXJvZ3JhbWFuIFNhaW5zIERhdGEgSSINCmF1dGhvcjogIkpPQU5TIEhFTktZIFNFUlZBVElVUyBTSU1BTlVMTEFORyINCmRhdGU6ICJgciBmb3JtYXQoU3lzLkRhdGUoKSwgJyVCICVkLCAlWScpYCINCm91dHB1dDoNCiAgcm1kZm9ybWF0czo6cmVhZHRoZWRvd246DQogICAgc2VsZl9jb250YWluZWQ6IHRydWUNCiAgICB0aHVtYm5haWxzOiB0cnVlDQogICAgbGlnaHRib3g6IHRydWUNCiAgICBnYWxsZXJ5OiB0cnVlDQogICAgbGliX2RpcjogbGlicw0KICAgIGRmX3ByaW50OiAicGFnZWQiDQogICAgY29kZV9mb2xkaW5nOiAic2hvdyINCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBjc3M6ICJzdHlsZSAxLmNzcyINCi0tLQ0KDQojIE51bWVyaWNhbCBEYXRhDQoNCiMjIEJveHBsb3QNCg0KIyMjIFIgQ29kZSAoQm94cGxvdCkNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAxLiBMb2FkIExpYnJhcmllcw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDIuIExvYWQgYW5kIFByZXBhcmUgRGF0YQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmRhdGFfYmlzbmlzIDwtIHJlYWQuY3N2KCJkYXRhX2Jpc25pcy5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQoNCiMgQ29udmVydCBRdWFudGl0eSB0byBudW1lcmljIGFuZCBmaWx0ZXIgbWlzc2luZw0KZGF0YV9iaXNuaXMgPC0gZGF0YV9iaXNuaXMgJT4lDQogIG11dGF0ZShRdWFudGl0eSA9IGFzLm51bWVyaWMoUXVhbnRpdHkpKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShRdWFudGl0eSkpDQoNCiMgQ29tcHV0ZSBJUVItYmFzZWQgb3V0bGllciBib3VuZHMNClExIDwtIHF1YW50aWxlKGRhdGFfYmlzbmlzJFF1YW50aXR5LCAwLjI1KQ0KUTMgPC0gcXVhbnRpbGUoZGF0YV9iaXNuaXMkUXVhbnRpdHksIDAuNzUpDQpJUVJfdmFsdWUgPC0gSVFSKGRhdGFfYmlzbmlzJFF1YW50aXR5KQ0KbG93ZXJfd2hpc2tlciA8LSBRMSAtIDEuNSAqIElRUl92YWx1ZQ0KdXBwZXJfd2hpc2tlciA8LSBRMyArIDEuNSAqIElRUl92YWx1ZQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAzLiBTdW1tYXJpemUgU3RhdGlzdGljcw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCnN0YXRzIDwtIGRhdGFfYmlzbmlzICU+JQ0KICBzdW1tYXJpc2UoDQogICAgTWVhbiA9IG1lYW4oUXVhbnRpdHkpLA0KICAgIFExID0gUTEsDQogICAgTWVkaWFuID0gbWVkaWFuKFF1YW50aXR5KSwNCiAgICBRMyA9IFEzLA0KICAgIE1pbiA9IG1pbihRdWFudGl0eSksDQogICAgTWF4ID0gbWF4KFF1YW50aXR5KSwNCiAgICBPdXRsaWVycyA9IHN1bShRdWFudGl0eSA8IGxvd2VyX3doaXNrZXIgfCBRdWFudGl0eSA+IHVwcGVyX3doaXNrZXIpDQogICkNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgNC4gQmFzaWMgQm94cGxvdCB3aXRoIEppdHRlciBhbmQgQW5ub3RhdGlvbnMNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpnZ3Bsb3QoZGF0YV9iaXNuaXMsIGFlcyh4ID0gZmFjdG9yKDEpLCB5ID0gUXVhbnRpdHkpKSArDQogICMgQmFzaWMgYm94cGxvdA0KICBnZW9tX2JveHBsb3QoZmlsbCA9ICJza3libHVlIiwgb3V0bGllci5zaGFwZSA9IE5BKSArDQogIA0KICAjIEFkZCBqaXR0ZXJlZCBwb2ludHMsIGhpZ2hsaWdodCBvdXRsaWVycyBpbiByZWQNCiAgZ2VvbV9qaXR0ZXIoYWVzKGNvbG9yID0gUXVhbnRpdHkgPCBsb3dlcl93aGlza2VyIHwgUXVhbnRpdHkgPiB1cHBlcl93aGlza2VyKSwNCiAgICAgICAgICAgICAgd2lkdGggPSAwLjEsIHNpemUgPSAxLCBhbHBoYSA9IDAuNSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiRkFMU0UiID0gImJsYWNrIiwgIlRSVUUiID0gInJlZCIpLCBndWlkZSA9ICJub25lIikgKw0KICANCiAgIyBIaWdobGlnaHQgbWF4IHBvaW50IGlmIG5vdCBhbiBvdXRsaWVyDQogIGdlb21fcG9pbnQoZGF0YSA9IGRhdGFfYmlzbmlzICU+JSBmaWx0ZXIoUXVhbnRpdHkgPT0gc3RhdHMkTWF4W1sxXV0gJiBRdWFudGl0eSA8PSB1cHBlcl93aGlza2VyKSwNCiAgICAgICAgICAgICBhZXMoeCA9IGZhY3RvcigxKSwgeSA9IFF1YW50aXR5KSwNCiAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBzaXplID0gOCkgKw0KICANCiAgIyBBbm5vdGF0aW9ucw0KICBnZ3Bsb3QyOjphbm5vdGF0ZSgidGV4dCIsIHggPSAxLjIsIHkgPSBzdGF0cyRNZWFuW1sxXV0sIA0KICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJNZWFuOiIsIHJvdW5kKHN0YXRzJE1lYW5bWzFdXSwgMikpLCANCiAgICAgICAgICAgaGp1c3QgPSAwLCBmb250ZmFjZSA9ICJib2xkIiwgY29sb3IgPSAiYmx1ZSIpICsNCiAgZ2dwbG90Mjo6YW5ub3RhdGUoInRleHQiLCB4ID0gMS4yLCB5ID0gc3RhdHMkUTFbWzFdXSwgDQogICAgICAgICAgIGxhYmVsID0gcGFzdGUoIlExOiIsIHJvdW5kKHN0YXRzJFExW1sxXV0sIDIpKSwgDQogICAgICAgICAgIGhqdXN0ID0gMCwgY29sb3IgPSAiZGFya2dyZWVuIikgKw0KICBnZ3Bsb3QyOjphbm5vdGF0ZSgidGV4dCIsIHggPSAxLjIsIHkgPSBzdGF0cyRNZWRpYW5bWzFdXSwgDQogICAgICAgICAgIGxhYmVsID0gcGFzdGUoIk1lZGlhbjoiLCByb3VuZChzdGF0cyRNZWRpYW5bWzFdXSwgMikpLCANCiAgICAgICAgICAgaGp1c3QgPSAwLCBjb2xvciA9ICJwdXJwbGUiKSArDQogIGdncGxvdDI6OmFubm90YXRlKCJ0ZXh0IiwgeCA9IDEuMiwgeSA9IHN0YXRzJFEzW1sxXV0sIA0KICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJRMzoiLCByb3VuZChzdGF0cyRRM1tbMV1dLCAyKSksIA0KICAgICAgICAgICBoanVzdCA9IDAsIGNvbG9yID0gImRhcmtncmVlbiIpICsNCiAgZ2dwbG90Mjo6YW5ub3RhdGUoInRleHQiLCB4ID0gMS4yLCB5ID0gc3RhdHMkTWluW1sxXV0sIA0KICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJNaW46Iiwgcm91bmQoc3RhdHMkTWluW1sxXV0sIDIpKSwgDQogICAgICAgICAgIGhqdXN0ID0gMCwgY29sb3IgPSAib3JhbmdlIikgKw0KICBnZ3Bsb3QyOjphbm5vdGF0ZSgidGV4dCIsIHggPSAxLjIsIHkgPSBzdGF0cyRNYXhbWzFdXSwgDQogICAgICAgICAgIGxhYmVsID0gcGFzdGUoIk1heDoiLCByb3VuZChzdGF0cyRNYXhbWzFdXSwgMikpLCANCiAgICAgICAgICAgaGp1c3QgPSAwLCBjb2xvciA9ICJvcmFuZ2UiKSArDQogIGdncGxvdDI6OmFubm90YXRlKCJ0ZXh0IiwgeCA9IDEsIHkgPSBzdGF0cyRNYXhbWzFdXSArIDAuMDUgKiBzdGF0cyRNYXhbWzFdXSwgDQogICAgICAgICAgIGxhYmVsID0gcGFzdGUoIk91dGxpZXJzOiIsIHN0YXRzJE91dGxpZXJzW1sxXV0pLCANCiAgICAgICAgICAgY29sb3IgPSAicmVkIiwgZm9udGZhY2UgPSAiaXRhbGljIiwgaGp1c3QgPSAwLjUpICsNCg0KICAjIFBsb3QgZm9ybWF0dGluZw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkJveHBsb3Qgb2YgUXVhbnRpdHkgd2l0aCBKaXR0ZXIgYW5kIEFubm90YXRpb25zIiwNCiAgICB4ID0gTlVMTCwNCiAgICB5ID0gIlF1YW50aXR5Ig0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoDQogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwLCBmYWNlID0gImJvbGQiKSwNCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkNCiAgKQ0KYGBgDQoNCiMjIyBQeXRob24gQ29kZSAoQm94cGxvdCkNCg0KYGBge3B5dGhvbn0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDEuIExvYWQgTGlicmFyaWVzDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KaW1wb3J0IHBhbmRhcyBhcyBwZA0KaW1wb3J0IG1hdHBsb3RsaWIucHlwbG90IGFzIHBsdA0KaW1wb3J0IHNlYWJvcm4gYXMgc25zDQppbXBvcnQgbnVtcHkgYXMgbnANCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMi4gTG9hZCBhbmQgUHJlcGFyZSBEYXRhDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KZGF0YV9iaXNuaXMgPSBwZC5yZWFkX2NzdigiZGF0YV9iaXNuaXMuY3N2IikNCg0KIyBDb252ZXJ0IFF1YW50aXR5IHRvIG51bWVyaWMgYW5kIGZpbHRlciBtaXNzaW5nDQpkYXRhX2Jpc25pc1snUXVhbnRpdHknXSA9IHBkLnRvX251bWVyaWMoZGF0YV9iaXNuaXNbJ1F1YW50aXR5J10sIGVycm9ycz0nY29lcmNlJykNCmRhdGFfYmlzbmlzID0gZGF0YV9iaXNuaXMuZHJvcG5hKHN1YnNldD1bJ1F1YW50aXR5J10pDQoNCiMgQ29tcHV0ZSBJUVItYmFzZWQgb3V0bGllciBib3VuZHMNClExID0gZGF0YV9iaXNuaXNbJ1F1YW50aXR5J10ucXVhbnRpbGUoMC4yNSkNClEzID0gZGF0YV9iaXNuaXNbJ1F1YW50aXR5J10ucXVhbnRpbGUoMC43NSkNCklRUl92YWx1ZSA9IFEzIC0gUTENCmxvd2VyX3doaXNrZXIgPSBRMSAtIDEuNSAqIElRUl92YWx1ZQ0KdXBwZXJfd2hpc2tlciA9IFEzICsgMS41ICogSVFSX3ZhbHVlDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDMuIFN1bW1hcml6ZSBTdGF0aXN0aWNzDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0Kc3RhdHMgPSB7DQogICAgJ01lYW4nOiBkYXRhX2Jpc25pc1snUXVhbnRpdHknXS5tZWFuKCksDQogICAgJ1ExJzogUTEsDQogICAgJ01lZGlhbic6IGRhdGFfYmlzbmlzWydRdWFudGl0eSddLm1lZGlhbigpLA0KICAgICdRMyc6IFEzLA0KICAgICdNaW4nOiBkYXRhX2Jpc25pc1snUXVhbnRpdHknXS5taW4oKSwNCiAgICAnTWF4JzogZGF0YV9iaXNuaXNbJ1F1YW50aXR5J10ubWF4KCksDQogICAgJ091dGxpZXJzJzogKChkYXRhX2Jpc25pc1snUXVhbnRpdHknXSA8IGxvd2VyX3doaXNrZXIpIHwgKGRhdGFfYmlzbmlzWydRdWFudGl0eSddID4gdXBwZXJfd2hpc2tlcikpLnN1bSgpDQp9DQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDQuIEJhc2ljIEJveHBsb3Qgd2l0aCBKaXR0ZXIgYW5kIEFubm90YXRpb25zDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KcGx0LmZpZ3VyZShmaWdzaXplPSgxMCwgOCkpDQpzbnMuYm94cGxvdCh4PVsiIl0gKiBsZW4oZGF0YV9iaXNuaXMpLCB5PWRhdGFfYmlzbmlzWydRdWFudGl0eSddLCBjb2xvcj0nc2t5Ymx1ZScsIHNob3dmbGllcnM9RmFsc2UpDQoNCiMgQWRkIGppdHRlciBwb2ludHMNCmlzX291dGxpZXIgPSAoZGF0YV9iaXNuaXNbJ1F1YW50aXR5J10gPCBsb3dlcl93aGlza2VyKSB8IChkYXRhX2Jpc25pc1snUXVhbnRpdHknXSA+IHVwcGVyX3doaXNrZXIpDQpzbnMuc3RyaXBwbG90KHg9WyIiXSAqIGxlbihkYXRhX2Jpc25pcyksIHk9ZGF0YV9iaXNuaXNbJ1F1YW50aXR5J10sIA0KICAgICAgICAgICAgICBodWU9aXNfb3V0bGllciwgcGFsZXR0ZT17RmFsc2U6ICJibGFjayIsIFRydWU6ICJyZWQifSwgDQogICAgICAgICAgICAgIGRvZGdlPUZhbHNlLCBqaXR0ZXI9MC4xLCBzaXplPTUsIGFscGhhPTAuNSkNCnBsdC5sZWdlbmQoW10sW10sIGZyYW1lb249RmFsc2UpDQoNCiMgSGlnaGxpZ2h0IG1heCBwb2ludCBpZiBub3QgYW4gb3V0bGllcg0KaWYgc3RhdHNbJ01heCddIDw9IHVwcGVyX3doaXNrZXI6DQogICAgcGx0LnNjYXR0ZXIoMCwgc3RhdHNbJ01heCddLCBjb2xvcj0ncmVkJywgcz0yMDAsIHpvcmRlcj0zKQ0KDQojIEFubm90YXRpb25zDQpwbHQudGV4dCgwLjEsIHN0YXRzWydNZWFuJ10sIGYiTWVhbjoge3N0YXRzWydNZWFuJ106LjJmfSIsIGNvbG9yPSJibHVlIiwgd2VpZ2h0PSdib2xkJykNCnBsdC50ZXh0KDAuMSwgc3RhdHNbJ1ExJ10sIGYiUTE6IHtzdGF0c1snUTEnXTouMmZ9IiwgY29sb3I9ImRhcmtncmVlbiIpDQpwbHQudGV4dCgwLjEsIHN0YXRzWydNZWRpYW4nXSwgZiJNZWRpYW46IHtzdGF0c1snTWVkaWFuJ106LjJmfSIsIGNvbG9yPSJwdXJwbGUiKQ0KcGx0LnRleHQoMC4xLCBzdGF0c1snUTMnXSwgZiJRMzoge3N0YXRzWydRMyddOi4yZn0iLCBjb2xvcj0iZGFya2dyZWVuIikNCnBsdC50ZXh0KDAuMSwgc3RhdHNbJ01pbiddLCBmIk1pbjoge3N0YXRzWydNaW4nXTouMmZ9IiwgY29sb3I9Im9yYW5nZSIpDQpwbHQudGV4dCgwLjEsIHN0YXRzWydNYXgnXSwgZiJNYXg6IHtzdGF0c1snTWF4J106LjJmfSIsIGNvbG9yPSJvcmFuZ2UiKQ0KcGx0LnRleHQoMCwgc3RhdHNbJ01heCddICogMS4wNSwgZiJPdXRsaWVyczoge3N0YXRzWydPdXRsaWVycyddfSIsIGNvbG9yPSJyZWQiLCBzdHlsZT0naXRhbGljJywgaGE9J2NlbnRlcicpDQoNCiMgUGxvdCBmb3JtYXR0aW5nDQpwbHQudGl0bGUoIkJveHBsb3Qgb2YgUXVhbnRpdHkgd2l0aCBKaXR0ZXIgYW5kIEFubm90YXRpb25zIiwgZm9udHNpemU9MjAsIHdlaWdodD0nYm9sZCcpDQpwbHQueWxhYmVsKCJRdWFudGl0eSIsIGZvbnRzaXplPTE2KQ0KcGx0Lnh0aWNrcyhbXSkNCnBsdC50aWNrX3BhcmFtcyhheGlzPSd5JywgbGFiZWxzaXplPTE0KQ0KcGx0LnRpZ2h0X2xheW91dCgpDQpwbHQuc2hvdygpDQpgYGANCg0KIyMgVmlvbGluLXBsb3QNCg0KIyMjIFIgQ29kZSAoVmlvbGluLXBsb3QpDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMS4gTG9hZCBMaWJyYXJpZXMNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAyLiBMb2FkIGFuZCBQcmVwYXJlIERhdGENCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpkYXRhX2Jpc25pcyA8LSByZWFkLmNzdigiZGF0YV9iaXNuaXMuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KDQojIENsZWFuIGFuZCBjb252ZXJ0IFF1YW50aXR5IHRvIG51bWVyaWMNCmRhdGFfYmlzbmlzIDwtIGRhdGFfYmlzbmlzICU+JQ0KICBtdXRhdGUoUXVhbnRpdHkgPSBhcy5udW1lcmljKFF1YW50aXR5KSkgJT4lDQogIGZpbHRlcighaXMubmEoUXVhbnRpdHkpKQ0KDQojIENhbGN1bGF0ZSBxdWFydGlsZXMgYW5kIElRUiBmb3Igb3V0bGllciBkZXRlY3Rpb24NClExIDwtIHF1YW50aWxlKGRhdGFfYmlzbmlzJFF1YW50aXR5LCAwLjI1KQ0KUTMgPC0gcXVhbnRpbGUoZGF0YV9iaXNuaXMkUXVhbnRpdHksIDAuNzUpDQpJUVJfdmFsdWUgPC0gSVFSKGRhdGFfYmlzbmlzJFF1YW50aXR5KQ0KdXBwZXJfd2hpc2tlciA8LSBRMyArIDEuNSAqIElRUl92YWx1ZQ0KbG93ZXJfd2hpc2tlciA8LSBRMSAtIDEuNSAqIElRUl92YWx1ZQ0KDQojIE1hcmsgb3V0bGllcnMNCmRhdGFfYmlzbmlzIDwtIGRhdGFfYmlzbmlzICU+JQ0KICBtdXRhdGUoDQogICAgaXNfb3V0bGllciA9IGlmZWxzZShRdWFudGl0eSA8IGxvd2VyX3doaXNrZXIgfCBRdWFudGl0eSA+IHVwcGVyX3doaXNrZXIsICJPdXRsaWVyIiwgIk5vcm1hbCIpDQogICkNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMy4gU3VtbWFyaXplIFN0YXRpc3RpY3MNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpzdGF0cyA8LSBkYXRhX2Jpc25pcyAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIE1lYW4gPSBtZWFuKFF1YW50aXR5KSwNCiAgICBRMSA9IFExLA0KICAgIE1lZGlhbiA9IG1lZGlhbihRdWFudGl0eSksDQogICAgUTMgPSBRMywNCiAgICBNaW4gPSBtaW4oUXVhbnRpdHkpLA0KICAgIE1heCA9IG1heChRdWFudGl0eSksDQogICAgT3V0bGllcnMgPSBzdW0oaXNfb3V0bGllciA9PSAiT3V0bGllciIpDQogICkNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgNC4gQ3JlYXRlIFZpb2xpbiBQbG90IHdpdGggQ29sb3JlZCBKaXR0ZXIgYW5kIEFubm90YXRpb25zDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KZ2dwbG90KGRhdGFfYmlzbmlzLCBhZXMoeCA9IGZhY3RvcigxKSwgeSA9IFF1YW50aXR5KSkgKw0KICBnZW9tX3Zpb2xpbihmaWxsID0gInNreWJsdWUiLCB0cmltID0gRkFMU0UpICsNCiAgZ2VvbV9ib3hwbG90KHdpZHRoID0gMC4xLCBvdXRsaWVyLnNoYXBlID0gTkEsIGNvbG9yID0gImJsYWNrIikgKw0KICBnZW9tX2ppdHRlcihhZXMoY29sb3IgPSBpc19vdXRsaWVyKSwgd2lkdGggPSAwLjEsIGFscGhhID0gMC42LCBzaXplID0gMikgKw0KICBnZW9tX3BvaW50KGRhdGEgPSBkYXRhX2Jpc25pcyAlPiUNCiAgICAgICAgICAgICAgIGZpbHRlcihRdWFudGl0eSA9PSBzdGF0cyRNYXhbWzFdXSAmIFF1YW50aXR5IDw9IHVwcGVyX3doaXNrZXIpLA0KICAgICAgICAgICAgIGFlcyh4ID0gZmFjdG9yKDEpLCB5ID0gUXVhbnRpdHkpLA0KICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIHNpemUgPSA4KSArDQoNCiAgIyBBbm5vdGF0aW9ucyB2aWEgZ2VvbV90ZXh0DQogIGdlb21fdGV4dChkYXRhID0gc3RhdHMsIGFlcyh4ID0gMS4yLCB5ID0gTWVhbiwgbGFiZWwgPSBwYXN0ZSgiTWVhbjoiLCByb3VuZChNZWFuLCAyKSkpLA0KICAgICAgICAgICAgaGp1c3QgPSAwLCBjb2xvciA9ICJibHVlIiwgZm9udGZhY2UgPSAiYm9sZCIpICsNCiAgZ2VvbV90ZXh0KGRhdGEgPSBzdGF0cywgYWVzKHggPSAxLjIsIHkgPSBRMSwgbGFiZWwgPSBwYXN0ZSgiUTE6Iiwgcm91bmQoUTEsIDIpKSksDQogICAgICAgICAgICBoanVzdCA9IDAsIGNvbG9yID0gImRhcmtncmVlbiIpICsNCiAgZ2VvbV90ZXh0KGRhdGEgPSBzdGF0cywgYWVzKHggPSAxLjIsIHkgPSBNZWRpYW4sIGxhYmVsID0gcGFzdGUoIk1lZGlhbjoiLCByb3VuZChNZWRpYW4sIDIpKSksDQogICAgICAgICAgICBoanVzdCA9IDAsIGNvbG9yID0gInB1cnBsZSIpICsNCiAgZ2VvbV90ZXh0KGRhdGEgPSBzdGF0cywgYWVzKHggPSAxLjIsIHkgPSBRMywgbGFiZWwgPSBwYXN0ZSgiUTM6Iiwgcm91bmQoUTMsIDIpKSksDQogICAgICAgICAgICBoanVzdCA9IDAsIGNvbG9yID0gImRhcmtncmVlbiIpICsNCiAgZ2VvbV90ZXh0KGRhdGEgPSBzdGF0cywgYWVzKHggPSAxLjIsIHkgPSBNaW4sIGxhYmVsID0gcGFzdGUoIk1pbjoiLCByb3VuZChNaW4sIDIpKSksDQogICAgICAgICAgICBoanVzdCA9IDAsIGNvbG9yID0gIm9yYW5nZSIpICsNCiAgZ2VvbV90ZXh0KGRhdGEgPSBzdGF0cywgYWVzKHggPSAxLjIsIHkgPSBNYXgsIGxhYmVsID0gcGFzdGUoIk1heDoiLCByb3VuZChNYXgsIDIpKSksDQogICAgICAgICAgICBoanVzdCA9IDAsIGNvbG9yID0gIm9yYW5nZSIpICsNCiAgZ2VvbV90ZXh0KGRhdGEgPSBzdGF0cywgYWVzKHggPSAxLCB5ID0gTWF4ICsgMC4wNSAqIE1heCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiT3V0bGllcnM6IiwgT3V0bGllcnMpKSwNCiAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIGZvbnRmYWNlID0gIml0YWxpYyIsIGhqdXN0ID0gMC41KSArDQoNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIk5vcm1hbCIgPSAiYmxhY2siLCAiT3V0bGllciIgPSAicmVkIikpICsNCg0KICBsYWJzKA0KICAgIHRpdGxlID0gIlZpb2xpbiBQbG90IG9mIFF1YW50aXR5IHdpdGggT3V0bGllciBIaWdobGlnaHRlZCIsDQogICAgeCA9IE5VTEwsDQogICAgeSA9ICJRdWFudGl0eSIsDQogICAgY29sb3IgPSAiUG9pbnQgVHlwZSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCwgZmFjZSA9ICJib2xkIiksDQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLA0KICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsDQogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksDQogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDkpDQogICkNCmBgYA0KDQojIyMgUHl0aG9uIENvZGUgKFZpb2xpbi1wbG90KQ0KDQpgYGB7cHl0aG9ufQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMS4gTG9hZCBMaWJyYXJpZXMNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQppbXBvcnQgcGFuZGFzIGFzIHBkDQppbXBvcnQgc2VhYm9ybiBhcyBzbnMNCmltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMi4gTG9hZCBhbmQgUHJlcGFyZSBEYXRhDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyBMb2FkIENTVg0KZGF0YV9iaXNuaXMgPSBwZC5yZWFkX2NzdigiZGF0YV9iaXNuaXMuY3N2IikNCg0KIyBDb252ZXJ0IFF1YW50aXR5IHRvIG51bWVyaWMgYW5kIGRyb3AgTkENCmRhdGFfYmlzbmlzWydRdWFudGl0eSddID0gcGQudG9fbnVtZXJpYyhkYXRhX2Jpc25pc1snUXVhbnRpdHknXSwgZXJyb3JzPSdjb2VyY2UnKQ0KZGF0YV9iaXNuaXMgPSBkYXRhX2Jpc25pcy5kcm9wbmEoc3Vic2V0PVsnUXVhbnRpdHknXSkNCg0KIyBDYWxjdWxhdGUgcXVhcnRpbGVzIGFuZCBJUVINClExID0gZGF0YV9iaXNuaXNbJ1F1YW50aXR5J10ucXVhbnRpbGUoMC4yNSkNClEzID0gZGF0YV9iaXNuaXNbJ1F1YW50aXR5J10ucXVhbnRpbGUoMC43NSkNCklRUl92YWx1ZSA9IFEzIC0gUTENCmxvd2VyX3doaXNrZXIgPSBRMSAtIDEuNSAqIElRUl92YWx1ZQ0KdXBwZXJfd2hpc2tlciA9IFEzICsgMS41ICogSVFSX3ZhbHVlDQoNCiMgTGFiZWwgb3V0bGllcnMNCmRhdGFfYmlzbmlzWydpc19vdXRsaWVyJ10gPSBkYXRhX2Jpc25pc1snUXVhbnRpdHknXS5hcHBseSgNCiAgICBsYW1iZGEgeDogJ091dGxpZXInIGlmIHggPCBsb3dlcl93aGlza2VyIG9yIHggPiB1cHBlcl93aGlza2VyIGVsc2UgJ05vcm1hbCcNCikNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMy4gU3VtbWFyaXplIFN0YXRpc3RpY3MNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpzdGF0cyA9IHsNCiAgICAnTWVhbic6IGRhdGFfYmlzbmlzWydRdWFudGl0eSddLm1lYW4oKSwNCiAgICAnUTEnOiBRMSwNCiAgICAnTWVkaWFuJzogZGF0YV9iaXNuaXNbJ1F1YW50aXR5J10ubWVkaWFuKCksDQogICAgJ1EzJzogUTMsDQogICAgJ01pbic6IGRhdGFfYmlzbmlzWydRdWFudGl0eSddLm1pbigpLA0KICAgICdNYXgnOiBkYXRhX2Jpc25pc1snUXVhbnRpdHknXS5tYXgoKSwNCiAgICAnT3V0bGllcnMnOiAoZGF0YV9iaXNuaXNbJ2lzX291dGxpZXInXSA9PSAnT3V0bGllcicpLnN1bSgpDQp9DQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDQuIENyZWF0ZSBWaW9saW4gUGxvdCB3aXRoIENvbG9yZWQgSml0dGVyIGFuZCBBbm5vdGF0aW9ucw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCnBsdC5maWd1cmUoZmlnc2l6ZT0oMTAsIDgpKQ0KDQojIFZpb2xpbiBwbG90DQpzbnMudmlvbGlucGxvdCgNCiAgICB4PVsiIl0gKiBsZW4oZGF0YV9iaXNuaXMpLA0KICAgIHk9ZGF0YV9iaXNuaXNbJ1F1YW50aXR5J10sDQogICAgaW5uZXI9Tm9uZSwNCiAgICBjb2xvcj0ic2t5Ymx1ZSINCikNCg0KIyBCb3hwbG90IG92ZXJsYXkNCnNucy5ib3hwbG90KA0KICAgIHg9WyIiXSAqIGxlbihkYXRhX2Jpc25pcyksDQogICAgeT1kYXRhX2Jpc25pc1snUXVhbnRpdHknXSwNCiAgICB3aWR0aD0wLjEsDQogICAgc2hvd2NhcHM9VHJ1ZSwNCiAgICBib3hwcm9wcz17J2ZhY2Vjb2xvcic6ICd3aGl0ZScsICdlZGdlY29sb3InOiAnYmxhY2snfSwNCiAgICB3aGlza2VycHJvcHM9eydjb2xvcic6ICdibGFjayd9LA0KICAgIG1lZGlhbnByb3BzPXsnY29sb3InOiAnYmxhY2snfSwNCiAgICBmbGllcnByb3BzPXsnbWFya2VyJzogTm9uZX0NCikNCg0KIyBKaXR0ZXJlZCBwb2ludHMgd2l0aCBvdXRsaWVyIGNvbG9yDQpzbnMuc3RyaXBwbG90KA0KICAgIHg9WyIiXSAqIGxlbihkYXRhX2Jpc25pcyksDQogICAgeT1kYXRhX2Jpc25pc1snUXVhbnRpdHknXSwNCiAgICBodWU9ZGF0YV9iaXNuaXNbJ2lzX291dGxpZXInXSwNCiAgICBwYWxldHRlPXsiTm9ybWFsIjogImJsYWNrIiwgIk91dGxpZXIiOiAicmVkIn0sDQogICAgZG9kZ2U9RmFsc2UsDQogICAgaml0dGVyPTAuMSwNCiAgICBhbHBoYT0wLjYsDQogICAgc2l6ZT00DQopDQoNCiMgSGlnaGxpZ2h0IG1heGltdW0gcG9pbnQgKGlmIG5vdCBvdXRsaWVyKQ0KaWYgc3RhdHNbJ01heCddIDw9IHVwcGVyX3doaXNrZXI6DQogICAgcGx0LnNjYXR0ZXIoDQogICAgICAgIDAsDQogICAgICAgIHN0YXRzWydNYXgnXSwNCiAgICAgICAgY29sb3I9J3JlZCcsDQogICAgICAgIHM9MjAwLA0KICAgICAgICB6b3JkZXI9Mw0KICAgICkNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgNS4gQW5ub3RhdGlvbnMNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpwbHQudGV4dCgwLjEsIHN0YXRzWydNZWFuJ10sICAgZiJNZWFuOiB7c3RhdHNbJ01lYW4nXTouMmZ9IiwgICBjb2xvcj0iYmx1ZSIsICAgICB3ZWlnaHQ9J2JvbGQnKQ0KcGx0LnRleHQoMC4xLCBzdGF0c1snUTEnXSwgICAgIGYiUTE6IHtzdGF0c1snUTEnXTouMmZ9IiwgICAgICAgY29sb3I9ImRhcmtncmVlbiIpDQpwbHQudGV4dCgwLjEsIHN0YXRzWydNZWRpYW4nXSwgZiJNZWRpYW46IHtzdGF0c1snTWVkaWFuJ106LjJmfSIsIGNvbG9yPSJwdXJwbGUiKQ0KcGx0LnRleHQoMC4xLCBzdGF0c1snUTMnXSwgICAgIGYiUTM6IHtzdGF0c1snUTMnXTouMmZ9IiwgICAgICAgY29sb3I9ImRhcmtncmVlbiIpDQpwbHQudGV4dCgwLjEsIHN0YXRzWydNaW4nXSwgICAgZiJNaW46IHtzdGF0c1snTWluJ106LjJmfSIsICAgICBjb2xvcj0ib3JhbmdlIikNCnBsdC50ZXh0KDAuMSwgc3RhdHNbJ01heCddLCAgICBmIk1heDoge3N0YXRzWydNYXgnXTouMmZ9IiwgICAgIGNvbG9yPSJvcmFuZ2UiKQ0KcGx0LnRleHQoMCwgc3RhdHNbJ01heCddICogMS4wNSwgZiJPdXRsaWVyczoge3N0YXRzWydPdXRsaWVycyddfSIsIGNvbG9yPSJyZWQiLCBzdHlsZT0naXRhbGljJywgaGE9J2NlbnRlcicpDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDYuIFBsb3QgRm9ybWF0dGluZw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCnBsdC50aXRsZSgiVmlvbGluIFBsb3Qgb2YgUXVhbnRpdHkgd2l0aCBPdXRsaWVyIEhpZ2hsaWdodGVkIiwgZm9udHNpemU9MjAsIHdlaWdodD0nYm9sZCcpDQpwbHQueWxhYmVsKCJRdWFudGl0eSIsIGZvbnRzaXplPTE2KQ0KcGx0Lnh0aWNrcyhbXSkNCnBsdC50aWNrX3BhcmFtcyhheGlzPSd5JywgbGFiZWxzaXplPTE0KQ0KcGx0LmxlZ2VuZCh0aXRsZT0iUG9pbnQgVHlwZSIsIHRpdGxlX2ZvbnRzaXplPTEwLCBmb250c2l6ZT05LCBsb2M9InVwcGVyIHJpZ2h0IikNCnBsdC50aWdodF9sYXlvdXQoKQ0KcGx0LnNob3coKQ0KYGBgDQoNCiMgQ29tYm8NCg0KIyMgR3JvdXBlZCBCYXIgQ2hhcnQNCg0KIyMjIFIgQ29kZSAoR3JvdXBlZCBCYXItY2hhcnQpDQpgYGB7cn0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDEuIExvYWQgTGlicmFyaWVzDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMi4gTG9hZCBEYXRhDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KZGF0YV9iaXNuaXMgPC0gcmVhZC5jc3YoImRhdGFfYmlzbmlzLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMy4gRGF0YSBTdW1tYXJpemF0aW9uDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0Kc2FsZXNfc3VtbWFyeSA8LSBkYXRhX2Jpc25pcyAlPiUNCiAgZ3JvdXBfYnkoUHJvZHVjdF9DYXRlZ29yeSwgUmVnaW9uKSAlPiUNCiAgc3VtbWFyaXNlKFRvdGFsX1NhbGVzID0gc3VtKFRvdGFsX1ByaWNlLCBuYS5ybSA9IFRSVUUpLCAuZ3JvdXBzID0gImRyb3AiKQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyA0LiBQbG90IEdyb3VwZWQgQmFyIENoYXJ0DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KZ2dwbG90KHNhbGVzX3N1bW1hcnksIGFlcyh4ID0gUHJvZHVjdF9DYXRlZ29yeSwgeSA9IFRvdGFsX1NhbGVzLCBmaWxsID0gUmVnaW9uKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgpKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiVG90YWwgU2FsZXMgYnkgUHJvZHVjdCBDYXRlZ29yeSBhbmQgUmVnaW9uIiwNCiAgICB4ID0gIlByb2R1Y3QgQ2F0ZWdvcnkiLA0KICAgIHkgPSAiVG90YWwgU2FsZXMgKFVTRCkiLA0KICAgIGZpbGwgPSAiUmVnaW9uIg0KICApICsNCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMCkgKw0KICB0aGVtZSgNCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDI1LCBoanVzdCA9IDEpLA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAwLjUpDQogICkNCmBgYA0KDQojIyMgUHl0aG9uIENvZGUgKEdyb3VwZWQgQmFyLWNoYXJ0KQ0KDQpgYGB7cHl0aG9ufQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMS4gTG9hZCBMaWJyYXJpZXMNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQppbXBvcnQgcGFuZGFzIGFzIHBkDQppbXBvcnQgc2VhYm9ybiBhcyBzbnMNCmltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMi4gTG9hZCBEYXRhDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KZGF0YV9iaXNuaXMgPSBwZC5yZWFkX2NzdigiZGF0YV9iaXNuaXMuY3N2IikNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMy4gRGF0YSBTdW1tYXJpemF0aW9uDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0Kc2FsZXNfc3VtbWFyeSA9ICgNCiAgICBkYXRhX2Jpc25pcw0KICAgIC5ncm91cGJ5KFsnUHJvZHVjdF9DYXRlZ29yeScsICdSZWdpb24nXSwgYXNfaW5kZXg9RmFsc2UpDQogICAgLmFnZyhUb3RhbF9TYWxlcz0oJ1RvdGFsX1ByaWNlJywgJ3N1bScpKQ0KKQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyA0LiBQbG90IEdyb3VwZWQgQmFyIENoYXJ0DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KcGx0LmZpZ3VyZShmaWdzaXplPSgxMCwgNikpDQpzbnMuc2V0X3RoZW1lKHN0eWxlPSJ3aGl0ZWdyaWQiKQ0KDQpiYXJwbG90ID0gc25zLmJhcnBsb3QoDQogICAgZGF0YT1zYWxlc19zdW1tYXJ5LA0KICAgIHg9IlByb2R1Y3RfQ2F0ZWdvcnkiLA0KICAgIHk9IlRvdGFsX1NhbGVzIiwNCiAgICBodWU9IlJlZ2lvbiIsDQogICAgZG9kZ2U9VHJ1ZSwNCiAgICBwYWxldHRlPSJTZXQyIg0KKQ0KDQojIFRhbWJhaGthbiBsYWJlbCBkYW4gdGl0bGUNCmJhcnBsb3Quc2V0X3RpdGxlKCJUb3RhbCBTYWxlcyBieSBQcm9kdWN0IENhdGVnb3J5IGFuZCBSZWdpb24iLCBmb250c2l6ZT0xNCwgZm9udHdlaWdodD0nYm9sZCcpDQpiYXJwbG90LnNldF94bGFiZWwoIlByb2R1Y3QgQ2F0ZWdvcnkiLCBmb250c2l6ZT0xMikNCmJhcnBsb3Quc2V0X3lsYWJlbCgiVG90YWwgU2FsZXMgKFVTRCkiLCBmb250c2l6ZT0xMikNCg0KIyBSb3Rhc2kgbGFiZWwgc3VtYnUgeA0KcGx0Lnh0aWNrcyhyb3RhdGlvbj0yNSwgaGE9J3JpZ2h0JykNCg0KIyBQZXJiYWlraSBsZWdlbmQNCnBsdC5sZWdlbmQodGl0bGU9IlJlZ2lvbiIsIHRpdGxlX2ZvbnRzaXplPTEwLCBmb250c2l6ZT05KQ0KDQpwbHQudGlnaHRfbGF5b3V0KCkNCnBsdC5zaG93KCkNCmBgYA0KDQojIyBSaWRnZWxpbmUgUGxvdA0KDQojIyMgUiBDb2RlIChSaWRnZWxpbmUgUGxvdCkNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAxLiBMb2FkIExpYnJhcmllcw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmxpYnJhcnkoZ2dyaWRnZXMpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShzY2FsZXMpDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDIuIEZpbHRlciBWYWxpZCBEYXRhDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyBGaWx0ZXIgb3V0IHJvd3Mgd2hlcmUgUHJpY2VfcGVyX1VuaXQgaXMgTkEsIEluZiwgb3IgTmFODQpkYXRhX2Jpc25pcyA8LSByZWFkLmNzdigiZGF0YV9iaXNuaXMuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KDQpkYXRhX2Jpc25pc19maWx0ZXJlZCA8LSBkYXRhX2Jpc25pcyAlPiUNCiAgZmlsdGVyKGlzLmZpbml0ZShQcmljZV9wZXJfVW5pdCkpDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDMuIENyZWF0ZSBSaWRnZWxpbmUgUGxvdA0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmdncGxvdChkYXRhX2Jpc25pc19maWx0ZXJlZCwgYWVzKHggPSBQcmljZV9wZXJfVW5pdCwgeSA9IFJlZ2lvbiwgZmlsbCA9IFJlZ2lvbikpICsNCiAgZ2VvbV9kZW5zaXR5X3JpZGdlcyhhbHBoYSA9IDAuNywgc2NhbGUgPSAxLjIpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IGRvbGxhcl9mb3JtYXQocHJlZml4ID0gIlJwIiwgYmlnLm1hcmsgPSAiLiIsIGRlY2ltYWwubWFyayA9ICIsIikpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgUHJpY2UgcGVyIFVuaXQgYnkgUmVnaW9uIiwNCiAgICB4ID0gIlByaWNlIHBlciBVbml0IiwNCiAgICB5ID0gIlJlZ2lvbiINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQojIyMgUHl0aG9uIENvZGUgKFJpZGdlbGluZSBQbG90KQ0KDQpgYGB7cHl0aG9ufQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMS4gTG9hZCBMaWJyYXJpZXMNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQppbXBvcnQgcGFuZGFzIGFzIHBkDQppbXBvcnQgbWF0cGxvdGxpYi5weXBsb3QgYXMgcGx0DQppbXBvcnQgc2VhYm9ybiBhcyBzbnMNCmZyb20gam95cHkgaW1wb3J0IGpveXBsb3QNCmltcG9ydCBudW1weSBhcyBucA0KaW1wb3J0IG1hdHBsb3RsaWIudGlja2VyIGFzIHRpY2tlcg0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAyLiBMb2FkIGFuZCBGaWx0ZXIgVmFsaWQgRGF0YQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmRhdGFfYmlzbmlzID0gcGQucmVhZF9jc3YoImRhdGFfYmlzbmlzLmNzdiIpDQoNCiMgRmlsdGVyOiBoYW55YSBiYXJpcyBkZW5nYW4gUHJpY2VfcGVyX1VuaXQgeWFuZyBmaW5pdGUNCmRhdGFfYmlzbmlzX2ZpbHRlcmVkID0gZGF0YV9iaXNuaXNbbnAuaXNmaW5pdGUoZGF0YV9iaXNuaXNbIlByaWNlX3Blcl9Vbml0Il0pXQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAzLiBDcmVhdGUgUmlkZ2VsaW5lIFBsb3QNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpwbHQuZmlndXJlKGZpZ3NpemU9KDEwLCA2KSkNCg0KIyBKb3lwbG90IChSaWRnZWxpbmUpDQpmaWcsIGF4ZXMgPSBqb3lwbG90KA0KICAgIGRhdGFfYmlzbmlzX2ZpbHRlcmVkLA0KICAgIGJ5PSJSZWdpb24iLA0KICAgIGNvbHVtbj0iUHJpY2VfcGVyX1VuaXQiLA0KICAgIGNvbG9ybWFwPXBsdC5jbS5TZXQzLA0KICAgIGFscGhhPTAuNywNCiAgICBvdmVybGFwPTEuMiwNCiAgICBsaW5ld2lkdGg9MSwNCiAgICBmYWRlPVRydWUNCikNCg0KIyBGb3JtYXR0aW5nIHgtYXhpcyBhcyBJbmRvbmVzaWFuLXN0eWxlIGN1cnJlbmN5IChScCkNCmF4ZXNbLTFdLnhheGlzLnNldF9tYWpvcl9mb3JtYXR0ZXIodGlja2VyLkZ1bmNGb3JtYXR0ZXIoDQogICAgbGFtYmRhIHgsIHBvczogZidScHt4OiwuMGZ9Jy5yZXBsYWNlKCcsJywgJy4nKQ0KKSkNCg0KIyBUaXRsZSBhbmQgbGFiZWxzDQpwbHQudGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBQcmljZSBwZXIgVW5pdCBieSBSZWdpb24iLCBmb250c2l6ZT0xNCwgd2VpZ2h0PSdib2xkJykNCnBsdC54bGFiZWwoIlByaWNlIHBlciBVbml0IiwgZm9udHNpemU9MTApDQpwbHQueWxhYmVsKCJSZWdpb24iLCBmb250c2l6ZT0xMCkNCg0KcGx0LnRpZ2h0X2xheW91dCgpDQpwbHQuc2hvdygpDQpgYGANCg0KIyMgQm94cGxvdCBieSBDYXRlZ29yeQ0KDQojIyMgUiBDb2RlIChCb3hwbG90IGJ5IENhdGVnb3J5KQ0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDEuIExvYWQgUmVxdWlyZWQgTGlicmFyaWVzDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMi4gUHJlcGFyZSBEYXRhDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyBDb252ZXJ0IFF1YW50aXR5IHRvIG51bWVyaWMgYW5kIHJlbW92ZSBOQQ0KZGF0YV9iaXNuaXMgPC0gcmVhZC5jc3YoImRhdGFfYmlzbmlzLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCmRhdGFfYmlzbmlzIDwtIGRhdGFfYmlzbmlzICU+JQ0KICBtdXRhdGUoUXVhbnRpdHkgPSBhcy5udW1lcmljKFF1YW50aXR5KSkgJT4lDQogIGZpbHRlcighaXMubmEoUXVhbnRpdHkpKQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAzLiBDcmVhdGUgQm94cGxvdA0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmdncGxvdChkYXRhX2Jpc25pcywgYWVzKHggPSBQcm9kdWN0X0NhdGVnb3J5LCB5ID0gUXVhbnRpdHksIGZpbGwgPSBQcm9kdWN0X0NhdGVnb3J5KSkgKw0KICBnZW9tX2JveHBsb3Qob3V0bGllci5jb2xvdXIgPSAicmVkIiwgb3V0bGllci5zaGFwZSA9IDE2LCBvdXRsaWVyLnNpemUgPSAyKSArICAjIEJveHBsb3Qgd2l0aCByZWQgb3V0bGllcnMNCiAgbGFicygNCiAgICB0aXRsZSA9ICJCb3hwbG90IG9mIFF1YW50aXR5IGJ5IFByb2R1Y3QgQ2F0ZWdvcnkiLA0KICAgIHggPSAiUHJvZHVjdCBDYXRlZ29yeSIsDQogICAgeSA9ICJRdWFudGl0eSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiKSwNCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksDQogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiDQogICkNCmBgYA0KDQojIyMgUHl0aG9uIENvZGUgKEJveHBsb3QgYnkgQ2F0ZWdvcnkpDQoNCmBgYHtweXRob259DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAxLiBMb2FkIFJlcXVpcmVkIExpYnJhcmllcw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmltcG9ydCBwYW5kYXMgYXMgcGQNCmltcG9ydCBzZWFib3JuIGFzIHNucw0KaW1wb3J0IG1hdHBsb3RsaWIucHlwbG90IGFzIHBsdA0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAyLiBQcmVwYXJlIERhdGENCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIExvYWQgZGF0YSBhbmQgcHJlcHJvY2Vzcw0KZGF0YV9iaXNuaXMgPSBwZC5yZWFkX2NzdigiZGF0YV9iaXNuaXMuY3N2IikNCmRhdGFfYmlzbmlzWydRdWFudGl0eSddID0gcGQudG9fbnVtZXJpYyhkYXRhX2Jpc25pc1snUXVhbnRpdHknXSwgZXJyb3JzPSdjb2VyY2UnKQ0KZGF0YV9iaXNuaXMgPSBkYXRhX2Jpc25pcy5kcm9wbmEoc3Vic2V0PVsnUXVhbnRpdHknXSkNCg0KIyBTb3J0IGNhdGVnb3JpZXMgYWxwaGFiZXRpY2FsbHkgZm9yIGNvbnNpc3RlbnQgeC1heGlzIG9yZGVyaW5nDQpzb3J0ZWRfY2F0ZWdvcmllcyA9IHNvcnRlZChkYXRhX2Jpc25pc1snUHJvZHVjdF9DYXRlZ29yeSddLmRyb3BuYSgpLnVuaXF1ZSgpKQ0KZGF0YV9iaXNuaXNbJ1Byb2R1Y3RfQ2F0ZWdvcnknXSA9IHBkLkNhdGVnb3JpY2FsKGRhdGFfYmlzbmlzWydQcm9kdWN0X0NhdGVnb3J5J10sIGNhdGVnb3JpZXM9c29ydGVkX2NhdGVnb3JpZXMsIG9yZGVyZWQ9VHJ1ZSkNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMy4gQ3JlYXRlIEJveHBsb3QNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpwbHQuZmlndXJlKGZpZ3NpemU9KDEwLCA2KSkNCnNucy5ib3hwbG90KA0KICAgIHg9J1Byb2R1Y3RfQ2F0ZWdvcnknLA0KICAgIHk9J1F1YW50aXR5JywNCiAgICBkYXRhPWRhdGFfYmlzbmlzLA0KICAgIHBhbGV0dGU9J3Bhc3RlbCcsDQogICAgc2hvd2ZsaWVycz1UcnVlLA0KICAgIGZsaWVycHJvcHM9ZGljdChtYXJrZXI9J28nLCBtYXJrZXJmYWNlY29sb3I9J3JlZCcsIG1hcmtlcnNpemU9NSwgbGluZXN0eWxlPSdub25lJykNCikNCg0KcGx0LnRpdGxlKCJCb3hwbG90IG9mIFF1YW50aXR5IGJ5IFByb2R1Y3QgQ2F0ZWdvcnkiLCBmb250c2l6ZT0xNCwgZm9udHdlaWdodD0nYm9sZCcpDQpwbHQueGxhYmVsKCJQcm9kdWN0IENhdGVnb3J5IiwgZm9udHNpemU9MTIpDQpwbHQueWxhYmVsKCJRdWFudGl0eSIsIGZvbnRzaXplPTEyKQ0KcGx0Lnh0aWNrcyhmb250c2l6ZT0xMCkNCnBsdC55dGlja3MoZm9udHNpemU9MTApDQpzbnMuZGVzcGluZSgpICAjIGVxdWl2YWxlbnQgdG8gdGhlbWVfbWluaW1hbA0KcGx0LnRpZ2h0X2xheW91dCgpDQpwbHQuc2hvdygpDQpgYGANCg0KIyMgTG9sbGlwb3AgQ2hhcnQNCg0KIyMjIFIgQ29kZSAoTG9sbGlwb3AgQ2hhcnQpDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMS4gTG9hZCBSZXF1aXJlZCBMaWJyYXJpZXMNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAyLiBQcmVwYXJlIERhdGENCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpkYXRhX2Jpc25pcyA8LSByZWFkLmNzdigiZGF0YV9iaXNuaXMuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KDQojIFN1bW1hcml6ZSB0b3RhbCBzYWxlcyBieSBQcm9kdWN0X0NhdGVnb3J5IGFuZCBSZWdpb24NCnNhbGVzX2dyb3VwZWQgPC0gZGF0YV9iaXNuaXMgJT4lDQogIGdyb3VwX2J5KFByb2R1Y3RfQ2F0ZWdvcnksIFJlZ2lvbikgJT4lDQogIHN1bW1hcmlzZShUb3RhbF9TYWxlcyA9IHN1bShUb3RhbF9QcmljZSwgbmEucm0gPSBUUlVFKSwgLmdyb3VwcyA9ICJkcm9wIikNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMy4gR3JvdXBlZCBMb2xsaXBvcCBDaGFydA0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmdncGxvdChzYWxlc19ncm91cGVkLCBhZXMoeCA9IFRvdGFsX1NhbGVzLCB5ID0gcmVvcmRlcihQcm9kdWN0X0NhdGVnb3J5LCBUb3RhbF9TYWxlcyksIGNvbG9yID0gUmVnaW9uKSkgKw0KICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB4ZW5kID0gVG90YWxfU2FsZXMsIHkgPSBQcm9kdWN0X0NhdGVnb3J5LCB5ZW5kID0gUHJvZHVjdF9DYXRlZ29yeSksIHNpemUgPSAxKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDQpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJHcm91cGVkIExvbGxpcG9wIENoYXJ0IiwNCiAgICB4ID0gIlRvdGFsIFNhbGVzIiwNCiAgICB5ID0gIlByb2R1Y3QgQ2F0ZWdvcnkiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwNCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpDQogICkNCmBgYA0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDQuIEZhY2V0ZWQgTG9sbGlwb3AgQ2hhcnQNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpnZ3Bsb3Qoc2FsZXNfZ3JvdXBlZCwgYWVzKHggPSBUb3RhbF9TYWxlcywgeSA9IHJlb3JkZXIoUHJvZHVjdF9DYXRlZ29yeSwgVG90YWxfU2FsZXMpKSkgKw0KICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB4ZW5kID0gVG90YWxfU2FsZXMsIHkgPSBQcm9kdWN0X0NhdGVnb3J5LCB5ZW5kID0gUHJvZHVjdF9DYXRlZ29yeSksIGNvbG9yID0gInNreWJsdWUiLCBzaXplID0gMSkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gImJsdWUiLCBzaXplID0gNCkgKw0KICBmYWNldF93cmFwKH4gUmVnaW9uLCBzY2FsZXMgPSAiZnJlZV94IikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkZhY2V0ZWQgTG9sbGlwb3AgQ2hhcnQiLA0KICAgIHggPSAiVG90YWwgU2FsZXMiLA0KICAgIHkgPSAiUHJvZHVjdCBDYXRlZ29yeSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIikNCiAgKQ0KYGBgDQoNCiMjIyBQeXRob24gQ29kZSAoTG9sbGlwb3AgQ2hhcnQpDQoNCmBgYHtweXRob259DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAxLiBMb2FkIFJlcXVpcmVkIExpYnJhcmllcw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmltcG9ydCBwYW5kYXMgYXMgcGQNCmltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQNCmltcG9ydCBzZWFib3JuIGFzIHNucw0KDQojIE9wdGlvbmFsIGZvciB0aGVtZSBzdHlsZQ0Kc25zLnNldF90aGVtZShzdHlsZT0id2hpdGVncmlkIikNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMi4gUHJlcGFyZSBEYXRhDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KZGF0YV9iaXNuaXMgPSBwZC5yZWFkX2NzdigiZGF0YV9iaXNuaXMuY3N2IikNCg0KIyBTdW1tYXJpemUgdG90YWwgc2FsZXMgYnkgUHJvZHVjdF9DYXRlZ29yeSBhbmQgUmVnaW9uDQpzYWxlc19ncm91cGVkID0gKA0KICAgIGRhdGFfYmlzbmlzDQogICAgLmdyb3VwYnkoWyJQcm9kdWN0X0NhdGVnb3J5IiwgIlJlZ2lvbiJdLCBhc19pbmRleD1GYWxzZSkNCiAgICAuYWdnKFRvdGFsX1NhbGVzPSgiVG90YWxfUHJpY2UiLCAic3VtIikpDQopDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDMuIEdyb3VwZWQgTG9sbGlwb3AgQ2hhcnQNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpwbHQuZmlndXJlKGZpZ3NpemU9KDEwLCA2KSkNCg0KIyBTb3J0IHRvIGdldCBjb25zaXN0ZW50IHktYXhpcw0Kc2FsZXNfZ3JvdXBlZF9zb3J0ZWQgPSBzYWxlc19ncm91cGVkLnNvcnRfdmFsdWVzKCJUb3RhbF9TYWxlcyIpDQpjYXRlZ29yaWVzID0gc2FsZXNfZ3JvdXBlZF9zb3J0ZWRbIlByb2R1Y3RfQ2F0ZWdvcnkiXS51bmlxdWUoKQ0KDQojIFBsb3QNCmZvciByZWdpb24gaW4gc2FsZXNfZ3JvdXBlZFsiUmVnaW9uIl0udW5pcXVlKCk6DQogICAgcmVnaW9uX2RhdGEgPSBzYWxlc19ncm91cGVkW3NhbGVzX2dyb3VwZWRbIlJlZ2lvbiJdID09IHJlZ2lvbl0NCiAgICByZWdpb25fZGF0YSA9IHJlZ2lvbl9kYXRhLnNvcnRfdmFsdWVzKCJUb3RhbF9TYWxlcyIpDQogICAgcGx0LmhsaW5lcyh5PXJlZ2lvbl9kYXRhWyJQcm9kdWN0X0NhdGVnb3J5Il0sIHhtaW49MCwgeG1heD1yZWdpb25fZGF0YVsiVG90YWxfU2FsZXMiXSwgbGFiZWw9cmVnaW9uLCBsaW5ld2lkdGg9MikNCiAgICBwbHQucGxvdChyZWdpb25fZGF0YVsiVG90YWxfU2FsZXMiXSwgcmVnaW9uX2RhdGFbIlByb2R1Y3RfQ2F0ZWdvcnkiXSwgJ28nLCBtYXJrZXJzaXplPTgsIGxhYmVsPWYie3JlZ2lvbn0iKQ0KDQpwbHQudGl0bGUoIkdyb3VwZWQgTG9sbGlwb3AgQ2hhcnQiLCBmb250c2l6ZT0xNiwgZm9udHdlaWdodD0nYm9sZCcpDQpwbHQueGxhYmVsKCJUb3RhbCBTYWxlcyIsIGZvbnRzaXplPTE0KQ0KcGx0LnlsYWJlbCgiUHJvZHVjdCBDYXRlZ29yeSIsIGZvbnRzaXplPTE0KQ0KcGx0Lnh0aWNrcyhmb250c2l6ZT0xMikNCnBsdC55dGlja3MoZm9udHNpemU9MTIpDQpwbHQubGVnZW5kKHRpdGxlPSJSZWdpb24iKQ0KcGx0LnRpZ2h0X2xheW91dCgpDQpwbHQuc2hvdygpDQpgYGANCg0KYGBge3B5dGhvbn0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDQuIEZhY2V0ZWQgTG9sbGlwb3AgQ2hhcnQgKDJ4MiBMYXlvdXQpDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyBDcmVhdGUgRmFjZXRHcmlkIHdpdGggMiBjb2x1bW5zIHBlciByb3cNCmcgPSBzbnMuRmFjZXRHcmlkKHNhbGVzX2dyb3VwZWQsIGNvbD0iUmVnaW9uIiwgY29sX3dyYXA9Miwgc2hhcmV4PUZhbHNlLCBoZWlnaHQ9NSwgYXNwZWN0PTEuMikNCg0KIyBEZWZpbmUgcGxvdHRpbmcgZnVuY3Rpb24NCmRlZiBsb2xsaXBvcChkYXRhLCAqKmt3YXJncyk6DQogICAgZGF0YSA9IGRhdGEuc29ydF92YWx1ZXMoIlRvdGFsX1NhbGVzIikNCiAgICBwbHQuaGxpbmVzKHk9ZGF0YVsiUHJvZHVjdF9DYXRlZ29yeSJdLCB4bWluPTAsIHhtYXg9ZGF0YVsiVG90YWxfU2FsZXMiXSwgY29sb3I9InNreWJsdWUiLCBsaW5ld2lkdGg9MikNCiAgICBwbHQucGxvdChkYXRhWyJUb3RhbF9TYWxlcyJdLCBkYXRhWyJQcm9kdWN0X0NhdGVnb3J5Il0sICdvJywgY29sb3I9ImJsdWUiLCBtYXJrZXJzaXplPTgpDQoNCiMgTWFwIHRvIGVhY2ggZmFjZXQNCmcubWFwX2RhdGFmcmFtZShsb2xsaXBvcCkNCmcuc2V0X2F4aXNfbGFiZWxzKCJUb3RhbCBTYWxlcyIsICJQcm9kdWN0IENhdGVnb3J5IikNCmcuc2V0X3RpdGxlcyhjb2xfdGVtcGxhdGU9Intjb2xfbmFtZX0iKQ0KZy5maWcuc3VicGxvdHNfYWRqdXN0KHRvcD0wLjkpDQpnLmZpZy5zdXB0aXRsZSgiRmFjZXRlZCBMb2xsaXBvcCBDaGFydCIsIGZvbnRzaXplPTE2LCBmb250d2VpZ2h0PSdib2xkJykNCnBsdC5zaG93KCkNCmBgYA0KDQojIyBIZWF0bWFwDQoNCiMjIyBSIENvZGUgKEhlYXRtYXApDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDEuIExvYWQgUmVxdWlyZWQgTGlicmFyaWVzDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGZvcmNhdHMpICAjIFVudHVrIGZjdF9yZXYoKQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAyLiBMb2FkIGFuZCBQcmVwYXJlIERhdGENCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpkYXRhX2Jpc25pcyA8LSByZWFkX2NzdigiZGF0YV9iaXNuaXMuY3N2IikNCg0KIyBIaXR1bmcgdG90YWwgc2FsZXMgYmVyZGFzYXJrYW4gUmVnaW9uIGRhbiBQcm9kdWN0X0NhdGVnb3J5DQpoZWF0bWFwX2RhdGEgPC0gZGF0YV9iaXNuaXMgJT4lDQogIGdyb3VwX2J5KFJlZ2lvbiwgUHJvZHVjdF9DYXRlZ29yeSkgJT4lDQogIHN1bW1hcmlzZShUb3RhbF9TYWxlcyA9IHN1bShUb3RhbF9QcmljZSwgbmEucm0gPSBUUlVFKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogIG11dGF0ZShQcm9kdWN0X0NhdGVnb3J5ID0gZmN0X3JldihmYWN0b3IoUHJvZHVjdF9DYXRlZ29yeSkpKSAgIyBVcnV0a2FuIGFsZmFiZXQgdGVyYmFsaWsNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMy4gQ3JlYXRlIEhlYXRtYXAgd2l0aCBMYWJlbHMNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpnZ3Bsb3QoaGVhdG1hcF9kYXRhLCBhZXMoeCA9IFJlZ2lvbiwgeSA9IFByb2R1Y3RfQ2F0ZWdvcnksIGZpbGwgPSBUb3RhbF9TYWxlcykpICsNCiAgZ2VvbV90aWxlKGNvbG9yID0gIndoaXRlIikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcm91bmQoVG90YWxfU2FsZXMsIDApKSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gNCkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICIjZmZlNWU1IiwgaGlnaCA9ICIjOTkwMDAwIiwgbmFtZSA9ICJUb3RhbCBTYWxlcyIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJIZWF0bWFwIG9mIFRvdGFsIFNhbGVzIGJ5IFJlZ2lvbiBhbmQgUHJvZHVjdCBDYXRlZ29yeSIsDQogICAgeCA9ICJSZWdpb24iLA0KICAgIHkgPSAiUHJvZHVjdCBDYXRlZ29yeSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMjUsIGhqdXN0ID0gMSksDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpLA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwNCiAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKQ0KICApDQoNCmBgYA0KDQojIyMgUHl0aG9uIENvZGUgKEhlYXRtYXApDQoNCmBgYHtweXRob259DQppbXBvcnQgcGFuZGFzIGFzIHBkDQppbXBvcnQgc2VhYm9ybiBhcyBzbnMNCmltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMS4gTG9hZCBhbmQgUHJlcGFyZSBEYXRhDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KZGYgPSBwZC5yZWFkX2NzdignZGF0YV9iaXNuaXMuY3N2JykNCg0KIyBIaXR1bmcgdG90YWwgc2FsZXMgcGVyIGthdGVnb3JpIGRhbiByZWdpb24NCmhlYXRtYXBfZGF0YSA9IGRmLmdyb3VwYnkoWydQcm9kdWN0X0NhdGVnb3J5JywgJ1JlZ2lvbiddKVsnVG90YWxfUHJpY2UnXS5zdW0oKS5yZXNldF9pbmRleCgpDQoNCiMgUGl2b3QgZGF0YSBtZW5qYWRpIGZvcm1hdCBtYXRyaWtzDQpoZWF0bWFwX3Bpdm90ID0gaGVhdG1hcF9kYXRhLnBpdm90KGluZGV4PSdQcm9kdWN0X0NhdGVnb3J5JywgY29sdW1ucz0nUmVnaW9uJywgdmFsdWVzPSdUb3RhbF9QcmljZScpDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDIuIFBsb3QgSGVhdG1hcA0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCnBsdC5maWd1cmUoZmlnc2l6ZT0oMTAsIDYpKQ0Kc25zLmhlYXRtYXAoaGVhdG1hcF9waXZvdCwgY21hcD0nUmVkcycsIGxpbmV3aWR0aHM9MC41LCBhbm5vdD1UcnVlLCBmbXQ9Ii4wZiIpDQoNCnBsdC50aXRsZSgnSGVhdG1hcCBvZiBUb3RhbCBTYWxlcyBieSBSZWdpb24gYW5kIFByb2R1Y3QgQ2F0ZWdvcnknLCBmb250c2l6ZT0xNiwgZm9udHdlaWdodD0nYm9sZCcpDQpwbHQueGxhYmVsKCdSZWdpb24nLCBmb250c2l6ZT0xNCkNCnBsdC55bGFiZWwoJ1Byb2R1Y3QgQ2F0ZWdvcnknLCBmb250c2l6ZT0xNCkNCnBsdC54dGlja3Mocm90YXRpb249MjUpDQpwbHQueXRpY2tzKHJvdGF0aW9uPTApDQpwbHQudGlnaHRfbGF5b3V0KCkNCnBsdC5zaG93KCkNCmBgYA0KDQojIFJlbGF0aW9uc2hpcA0KDQojIyBTY2F0dGVyIFBsb3QNCg0KIyMjIFIgQ29kZSAoU2NhdHRlciBQbG90KQ0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDEuIExvYWQgUmVxdWlyZWQgTGlicmFyaWVzDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkocmVhZHIpDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDIuIExvYWQgYW5kIFByZXBhcmUgRGF0YQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmRhdGFfYmlzbmlzIDwtIHJlYWRfY3N2KCJkYXRhX2Jpc25pcy5jc3YiKQ0KDQojIFBhc3Rpa2FuIFF1YW50aXR5IGRhbiBUb3RhbF9QcmljZSBiZXJ0aXBlIG51bWVyaWsgZGFuIHRpZGFrIE5BDQpkYXRhX2Jpc25pcyA8LSBkYXRhX2Jpc25pcyAlPiUNCiAgbXV0YXRlKA0KICAgIFF1YW50aXR5ID0gYXMubnVtZXJpYyhRdWFudGl0eSksDQogICAgVG90YWxfUHJpY2UgPSBhcy5udW1lcmljKFRvdGFsX1ByaWNlKQ0KICApICU+JQ0KICBmaWx0ZXIoIWlzLm5hKFF1YW50aXR5KSwgIWlzLm5hKFRvdGFsX1ByaWNlKSkNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMy4gQ3JlYXRlIFNjYXR0ZXIgUGxvdA0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmdncGxvdChkYXRhX2Jpc25pcywgYWVzKHggPSBRdWFudGl0eSwgeSA9IFRvdGFsX1ByaWNlLCBjb2xvciA9IFByb2R1Y3RfQ2F0ZWdvcnkpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjcsIHNpemUgPSAzKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiU2NhdHRlciBQbG90IG9mIFRvdGFsIFByaWNlIHZzIFF1YW50aXR5IiwNCiAgICB4ID0gIlF1YW50aXR5IiwNCiAgICB5ID0gIlRvdGFsIFByaWNlIiwNCiAgICBjb2xvciA9ICJQcm9kdWN0IENhdGVnb3J5Ig0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpLA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwNCiAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKQ0KICApDQpgYGANCg0KIyMjIFB5dGhvbiBDb2RlIChTY2F0dGVyIFBsb3QpDQoNCmBgYHtweXRob259DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAxLiBMb2FkIFJlcXVpcmVkIExpYnJhcmllcw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmltcG9ydCBwYW5kYXMgYXMgcGQNCmltcG9ydCBzZWFib3JuIGFzIHNucw0KaW1wb3J0IG1hdHBsb3RsaWIucHlwbG90IGFzIHBsdA0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAyLiBMb2FkIGFuZCBQcmVwYXJlIERhdGENCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpkZiA9IHBkLnJlYWRfY3N2KCJkYXRhX2Jpc25pcy5jc3YiKQ0KDQojIFBhc3Rpa2FuIGtvbG9tIG51bWVyaWsgYmVuYXINCmRmWydRdWFudGl0eSddID0gcGQudG9fbnVtZXJpYyhkZlsnUXVhbnRpdHknXSwgZXJyb3JzPSdjb2VyY2UnKQ0KZGZbJ1RvdGFsX1ByaWNlJ10gPSBwZC50b19udW1lcmljKGRmWydUb3RhbF9QcmljZSddLCBlcnJvcnM9J2NvZXJjZScpDQpkZiA9IGRmLmRyb3BuYShzdWJzZXQ9WydRdWFudGl0eScsICdUb3RhbF9QcmljZSddKQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAzLiBDcmVhdGUgU2NhdHRlciBQbG90DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KcGx0LmZpZ3VyZShmaWdzaXplPSgxMCwgNikpDQpzbnMuc2NhdHRlcnBsb3QoDQogICAgZGF0YT1kZiwNCiAgICB4PSdRdWFudGl0eScsDQogICAgeT0nVG90YWxfUHJpY2UnLA0KICAgIGh1ZT0nUHJvZHVjdF9DYXRlZ29yeScsDQogICAgcGFsZXR0ZT0nU2V0MicsDQogICAgYWxwaGE9MC43LA0KICAgIHM9ODANCikNCg0KcGx0LnRpdGxlKCJTY2F0dGVyIFBsb3Qgb2YgVG90YWwgUHJpY2UgdnMgUXVhbnRpdHkiLCBmb250c2l6ZT0xNiwgd2VpZ2h0PSdib2xkJykNCnBsdC54bGFiZWwoIlF1YW50aXR5IiwgZm9udHNpemU9MTQpDQpwbHQueWxhYmVsKCJUb3RhbCBQcmljZSIsIGZvbnRzaXplPTE0KQ0KcGx0Lnh0aWNrcyhmb250c2l6ZT0xMikNCnBsdC55dGlja3MoZm9udHNpemU9MTIpDQpwbHQubGVnZW5kKHRpdGxlPSdQcm9kdWN0IENhdGVnb3J5JywgZm9udHNpemU9MTAsIHRpdGxlX2ZvbnRzaXplPTEyKQ0KcGx0LmdyaWQoVHJ1ZSkNCnBsdC50aWdodF9sYXlvdXQoKQ0KcGx0LnNob3coKQ0KYGBgDQoNCiMjIEJ1YmJsZSBDaGFydA0KDQojIyMgUiBDb2RlIChCdWJibGUgQ2hhcnQpDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMS4gTG9hZCBSZXF1aXJlZCBMaWJyYXJpZXMNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShyZWFkcikNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMi4gTG9hZCBhbmQgUHJlcGFyZSBEYXRhDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KZGF0YV9iaXNuaXMgPC0gcmVhZF9jc3YoImRhdGFfYmlzbmlzLmNzdiIpDQoNCiMgUGFzdGlrYW4ga29sb20gbnVtZXJpaw0KZGF0YV9iaXNuaXMgPC0gZGF0YV9iaXNuaXMgJT4lDQogIG11dGF0ZSgNCiAgICBRdWFudGl0eSA9IGFzLm51bWVyaWMoUXVhbnRpdHkpLA0KICAgIFVuaXRfUHJpY2UgPSBhcy5udW1lcmljKFVuaXRfUHJpY2UpLA0KICAgIFRvdGFsX1ByaWNlID0gYXMubnVtZXJpYyhUb3RhbF9QcmljZSkNCiAgKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShRdWFudGl0eSkgJiAhaXMubmEoVW5pdF9QcmljZSkgJiAhaXMubmEoVG90YWxfUHJpY2UpICYgIWlzLm5hKFByb2R1Y3RfQ2F0ZWdvcnkpKQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAzLiBCdWJibGUgQ2hhcnQNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpnZ3Bsb3QoZGF0YV9iaXNuaXMsIGFlcyh4ID0gUXVhbnRpdHksIHkgPSBVbml0X1ByaWNlLCBzaXplID0gVG90YWxfUHJpY2UsIGNvbG9yID0gUHJvZHVjdF9DYXRlZ29yeSkpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNikgKw0KICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDIsIDIwKSkgKyAgIyBBdHVyIHVrdXJhbiBidWJibGUNCiAgbGFicygNCiAgICB0aXRsZSA9ICJCdWJibGUgQ2hhcnQgb2YgUXVhbnRpdHkgdnMgVW5pdCBQcmljZSIsDQogICAgc3VidGl0bGUgPSAiQnViYmxlIFNpemUgPSBUb3RhbCBQcmljZSIsDQogICAgeCA9ICJRdWFudGl0eSIsDQogICAgeSA9ICJVbml0IFByaWNlIiwNCiAgICBzaXplID0gIlRvdGFsIFByaWNlIiwNCiAgICBjb2xvciA9ICJQcm9kdWN0IENhdGVnb3J5Ig0KICApICsNCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMikgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiksDQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLA0KICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpDQogICkNCg0KYGBgDQoNCiMjIyBQeXRob24gQ29kZSAoQnViYmxlIENoYXJ0KQ0KDQpgYGB7cHl0aG9ufQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMS4gTG9hZCBSZXF1aXJlZCBMaWJyYXJpZXMNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQppbXBvcnQgcGFuZGFzIGFzIHBkDQppbXBvcnQgbWF0cGxvdGxpYi5weXBsb3QgYXMgcGx0DQppbXBvcnQgc2VhYm9ybiBhcyBzbnMNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMi4gTG9hZCBhbmQgUHJlcGFyZSBEYXRhDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KZGF0YSA9IHBkLnJlYWRfY3N2KCJkYXRhX2Jpc25pcy5jc3YiKQ0KDQojIFBhc3Rpa2FuIGtvbG9tIG51bWVyaWsNCmRhdGFbJ1F1YW50aXR5J10gPSBwZC50b19udW1lcmljKGRhdGFbJ1F1YW50aXR5J10sIGVycm9ycz0nY29lcmNlJykNCmRhdGFbJ1VuaXRfUHJpY2UnXSA9IHBkLnRvX251bWVyaWMoZGF0YVsnVW5pdF9QcmljZSddLCBlcnJvcnM9J2NvZXJjZScpDQpkYXRhWydUb3RhbF9QcmljZSddID0gcGQudG9fbnVtZXJpYyhkYXRhWydUb3RhbF9QcmljZSddLCBlcnJvcnM9J2NvZXJjZScpDQoNCiMgSGFwdXMgYmFyaXMgeWFuZyBtZW5nYW5kdW5nIE5BDQpkYXRhID0gZGF0YS5kcm9wbmEoc3Vic2V0PVsnUXVhbnRpdHknLCAnVW5pdF9QcmljZScsICdUb3RhbF9QcmljZScsICdQcm9kdWN0X0NhdGVnb3J5J10pDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDMuIEJ1YmJsZSBDaGFydA0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCnBsdC5maWd1cmUoZmlnc2l6ZT0oMTIsIDgpKQ0KYnViYmxlID0gc25zLnNjYXR0ZXJwbG90KA0KICAgIGRhdGE9ZGF0YSwNCiAgICB4PSdRdWFudGl0eScsDQogICAgeT0nVW5pdF9QcmljZScsDQogICAgc2l6ZT0nVG90YWxfUHJpY2UnLA0KICAgIGh1ZT0nUHJvZHVjdF9DYXRlZ29yeScsDQogICAgc2l6ZXM9KDUwLCAxMjAwKSwgICAjIFVrdXJhbiBnZWxlbWJ1bmcgc2VydXBhIGRlbmdhbiBSICgyLTIwIGRpa29udmVyc2kpDQogICAgYWxwaGE9MC42LA0KICAgIHBhbGV0dGU9J3RhYjEwJywNCiAgICBlZGdlY29sb3I9J3cnLA0KICAgIGxpbmV3aWR0aD0wLjUNCikNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgNC4gQ3VzdG9taXplIFBsb3QNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpwbHQudGl0bGUoIkJ1YmJsZSBDaGFydCBvZiBRdWFudGl0eSB2cyBVbml0IFByaWNlIiwgZm9udHNpemU9MTYsIGZvbnR3ZWlnaHQ9J2JvbGQnKQ0KcGx0LnN1cHRpdGxlKCJCdWJibGUgU2l6ZSA9IFRvdGFsIFByaWNlIiwgZm9udHNpemU9MTIpDQpwbHQueGxhYmVsKCJRdWFudGl0eSIsIGZvbnRzaXplPTE0KQ0KcGx0LnlsYWJlbCgiVW5pdCBQcmljZSIsIGZvbnRzaXplPTE0KQ0KcGx0LmxlZ2VuZCh0aXRsZT0iUHJvZHVjdCBDYXRlZ29yeSIsIHRpdGxlX2ZvbnRzaXplPTEyLCBsb2M9J2Jlc3QnLCBiYm94X3RvX2FuY2hvcj0oMS4wNSwgMSkpDQpwbHQudGlnaHRfbGF5b3V0KCkNCnBsdC5ncmlkKFRydWUpDQpwbHQuc2hvdygpDQoNCmBgYA0KDQojIyBDb3JyZWxhdGlvbiBNYXRyaXgNCg0KIyMjIFIgQ29kZSAoQ29ycmVsYXRpb24gTWF0cml4KQ0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDEuIExvYWQgUmVxdWlyZWQgTGlicmFyaWVzDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHJlc2hhcGUyKQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDIuIExvYWQgYW5kIFNlbGVjdCBEYXRhDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KZGF0YSA8LSByZWFkX2NzdigiZGF0YV9iaXNuaXMuY3N2IikNCg0KIyBQaWxpaCB2YXJpYWJlbCBudW1lcmlrIGRhbiBoaWxhbmdrYW4gTkENCnNlbGVjdGVkX3ZhcnMgPC0gZGF0YSAlPiUNCiAgc2VsZWN0KFF1YW50aXR5LCBVbml0X1ByaWNlLCBEaXNjb3VudCwgRmVhdHVyZV9JbnRlcmFjdGlvbikgJT4lDQogIG5hLm9taXQoKQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAzLiBDb21wdXRlIENvcnJlbGF0aW9uIE1hdHJpeA0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmNvcl9tYXQgPC0gcm91bmQoY29yKHNlbGVjdGVkX3ZhcnMsIHVzZSA9ICJjb21wbGV0ZS5vYnMiKSwgMikNCg0KIyBVYmFoIGtlIGZvcm1hdCBsb25nIHVudHVrIGhlYXRtYXANCm1lbHRlZF9jb3IgPC0gbWVsdChjb3JfbWF0KQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyA0LiBQbG90IENvcnJlbGF0aW9uIEhlYXRtYXANCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpnZ3Bsb3QobWVsdGVkX2NvciwgYWVzKHggPSBWYXIxLCB5ID0gZmFjdG9yKFZhcjIsIGxldmVscyA9IHJldihjb2xuYW1lcyhjb3JfbWF0KSkpLCBmaWxsID0gdmFsdWUpKSArDQogIGdlb21fdGlsZShjb2xvciA9ICJ3aGl0ZSIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHZhbHVlKSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gNCkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50bigNCiAgICBjb2xvcnMgPSBicmV3ZXIucGFsKG4gPSA5LCBuYW1lID0gIllsR25CdSIpLA0KICAgIGxpbWl0cyA9IGMoLTEsIDEpLA0KICAgIG5hbWUgPSAiQ29ycmVsYXRpb24iDQogICkgKw0KICBjb29yZF9maXhlZCgpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJDb3JyZWxhdGlvbiBNYXRyaXggSGVhdG1hcCIsDQogICAgeCA9IE5VTEwsDQogICAgeSA9IE5VTEwNCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpDQogICkNCg0KYGBgDQoNCiMjIyBQeXRob24gQ29kZSAoQ29ycmVsYXRpb24gTWF0cml4KQ0KDQpgYGB7cHl0aG9ufQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMS4gTG9hZCBSZXF1aXJlZCBMaWJyYXJpZXMNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQppbXBvcnQgcGFuZGFzIGFzIHBkDQppbXBvcnQgc2VhYm9ybiBhcyBzbnMNCmltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMi4gTG9hZCBhbmQgU2VsZWN0IERhdGENCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIEJhY2EgZGF0YSBkYXJpIGZpbGUgQ1NWDQpkYXRhID0gcGQucmVhZF9jc3YoImRhdGFfYmlzbmlzLmNzdiIpDQoNCiMgUGlsaWggdmFyaWFiZWwgbnVtZXJpayB5YW5nIHJlbGV2YW4NCnNlbGVjdGVkX3ZhcnMgPSBkYXRhW1snUXVhbnRpdHknLCAnVW5pdF9QcmljZScsICdEaXNjb3VudCcsICdGZWF0dXJlX0ludGVyYWN0aW9uJ11dLmRyb3BuYSgpDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDMuIENvbXB1dGUgQ29ycmVsYXRpb24gTWF0cml4DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KY29yX21hdHJpeCA9IHNlbGVjdGVkX3ZhcnMuY29ycigpLnJvdW5kKDIpDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDQuIFBsb3QgQ29ycmVsYXRpb24gSGVhdG1hcA0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCnBsdC5maWd1cmUoZmlnc2l6ZT0oOCwgNikpDQpoZWF0bWFwID0gc25zLmhlYXRtYXAoDQogICAgY29yX21hdHJpeCwgICAgICAgICAgDQogICAgYW5ub3Q9VHJ1ZSwgICAgICAgICAgICAgICAgICAgICAjIFRhbXBpbGthbiBuaWxhaSBrb3JlbGFzaQ0KICAgIGZtdD0iLjJmIiwgICAgICAgICAgICAgICAgICAgICAgIyBGb3JtYXQgZGVzaW1hbA0KICAgIGNtYXA9IllsR25CdSIsICAgICAgICAgICAgICAgICAgIyBXYXJuYSBncmFkYXNpDQogICAgdm1pbj0tMSwgdm1heD0xLCAgICAgICAgICAgICAgICAjIFNrYWxhIGtvcmVsYXNpDQogICAgbGluZXdpZHRocz0wLjUsIGxpbmVjb2xvcj0nd2hpdGUnICAjIEdhcmlzIGFudGFyIHNlbA0KKQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyA1LiBDdXN0b21pemUgQWVzdGhldGljcw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCnBsdC50aXRsZSgiQ29ycmVsYXRpb24gTWF0cml4IEhlYXRtYXAiLCBmb250c2l6ZT0xNiwgZm9udHdlaWdodD0nYm9sZCcpDQpwbHQueHRpY2tzKHJvdGF0aW9uPTQ1LCBoYT0ncmlnaHQnKQ0KcGx0Lnl0aWNrcyhyb3RhdGlvbj0wKQ0KcGx0LnRpZ2h0X2xheW91dCgpDQoNCiMgVGFtcGlsa2FuIHBsb3QNCnBsdC5zaG93KCkNCg0KYGBgDQoNCiMgVGltZSBTZXJpZXMNCg0KIyMgTGluZSBDaGFydA0KDQojIyMgUiBDb2RlIChMaW5lIENoYXJ0KQ0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDEuIExvYWQgUmVxdWlyZWQgTGlicmFyaWVzDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoc2NhbGVzKQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAyLiBMb2FkIGFuZCBQcmVwYXJlIERhdGENCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpkYXRhIDwtIHJlYWRfY3N2KCJkYXRhX2Jpc25pcy5jc3YiKQ0KDQojIEZvcm1hdCB0YW5nZ2FsIGRhbiBidWF0IGtvbG9tIFllYXJNb250aA0KZGF0YSA8LSBkYXRhICU+JQ0KICBtdXRhdGUoDQogICAgVHJhbnNhY3Rpb25fRGF0ZSA9IGFzLkRhdGUoVHJhbnNhY3Rpb25fRGF0ZSksDQogICAgWWVhck1vbnRoID0gZmxvb3JfZGF0ZShUcmFuc2FjdGlvbl9EYXRlLCAibW9udGgiKQ0KICApDQoNCiMgQWdyZWdhc2kgdG90YWwgc2FsZXMgcGVyIGJ1bGFuIHBlciByZWdpb24NCm1vbnRobHlfcmVnaW9uX3NhbGVzIDwtIGRhdGEgJT4lDQogIGdyb3VwX2J5KFllYXJNb250aCwgUmVnaW9uKSAlPiUNCiAgc3VtbWFyaXNlKFRvdGFsX1NhbGVzID0gc3VtKFRvdGFsX1ByaWNlLCBuYS5ybSA9IFRSVUUpLCAuZ3JvdXBzID0gImRyb3AiKQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAzLiBQbG90IExpbmUgQ2hhcnQgcGVyIFJlZ2lvbiAoWDogdGFodW4sIGRhdGEgcGVyIGJ1bGFuKQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmdncGxvdChtb250aGx5X3JlZ2lvbl9zYWxlcywgYWVzKHggPSBZZWFyTW9udGgsIHkgPSBUb3RhbF9TYWxlcywgY29sb3IgPSBSZWdpb24pKSArDQogIGdlb21fbGluZShzaXplID0gMS4yLCBhbHBoYSA9IDAuOSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAyLCBhbHBoYSA9IDAuOCkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlRyZW4gVG90YWwgUGVuanVhbGFuIEJ1bGFuYW4gcGVyIFJlZ2lvbiIsDQogICAgeCA9ICJUYWh1biIsDQogICAgeSA9ICJUb3RhbCBQZW5qdWFsYW4iLA0KICAgIGNvbG9yID0gIlJlZ2lvbiINCiAgKSArDQogIHNjYWxlX3hfZGF0ZSgNCiAgICBkYXRlX2xhYmVscyA9ICIlWSIsICAgICAgICAgICAgIyBUYW1waWxrYW4gaGFueWEgdGFodW4NCiAgICBkYXRlX2JyZWFrcyA9ICIxIHllYXIiICAgICAgICAgIyBJbnRlcnZhbCBsYWJlbCAxIHRhaHVuDQogICkgKw0KICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDEzKSArDQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4LCBmYWNlID0gImJvbGQiLCBoanVzdCA9IDAuNSksDQogICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMsIGZhY2UgPSAiaXRhbGljIiwgaGp1c3QgPSAwLjUpLA0KICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgaGp1c3QgPSAwLjUpLA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwNCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzKSwNCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEpDQogICkNCg0KYGBgDQoNCiMjIyBQeXRob24gQ29kZSAoTGluZSBDaGFydCkNCg0KYGBge3B5dGhvbn0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDEuIExvYWQgUmVxdWlyZWQgTGlicmFyaWVzDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KaW1wb3J0IHBhbmRhcyBhcyBwZA0KaW1wb3J0IG1hdHBsb3RsaWIucHlwbG90IGFzIHBsdA0KaW1wb3J0IHNlYWJvcm4gYXMgc25zDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDIuIExvYWQgYW5kIFByZXBhcmUgRGF0YQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgQmFjYSBkYXRhDQpkYXRhID0gcGQucmVhZF9jc3YoImRhdGFfYmlzbmlzLmNzdiIpDQoNCiMgUGFzdGlrYW4ga29sb20gdGFuZ2dhbCBkYWxhbSBmb3JtYXQgZGF0ZXRpbWUgZGFuIGJ1YXQga29sb20gWWVhck1vbnRoDQpkYXRhWydUcmFuc2FjdGlvbl9EYXRlJ10gPSBwZC50b19kYXRldGltZShkYXRhWydUcmFuc2FjdGlvbl9EYXRlJ10pDQpkYXRhWydZZWFyTW9udGgnXSA9IGRhdGFbJ1RyYW5zYWN0aW9uX0RhdGUnXS5kdC50b19wZXJpb2QoJ00nKS5kdC50b190aW1lc3RhbXAoKQ0KDQojIEFncmVnYXNpIHRvdGFsIHNhbGVzIHBlciBidWxhbiBwZXIgcmVnaW9uDQptb250aGx5X3JlZ2lvbl9zYWxlcyA9IGRhdGEuZ3JvdXBieShbJ1llYXJNb250aCcsICdSZWdpb24nXSlbJ1RvdGFsX1ByaWNlJ10uc3VtKCkucmVzZXRfaW5kZXgoKQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAzLiBQbG90IExpbmUgQ2hhcnQgcGVyIFJlZ2lvbg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCnBsdC5maWd1cmUoZmlnc2l6ZT0oMTIsIDYpKQ0Kc25zLmxpbmVwbG90KGRhdGE9bW9udGhseV9yZWdpb25fc2FsZXMsIHg9J1llYXJNb250aCcsIHk9J1RvdGFsX1ByaWNlJywgaHVlPSdSZWdpb24nLCBtYXJrZXI9J28nKQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyA0LiBDdXN0b21pemUgQWVzdGhldGljcw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCnBsdC50aXRsZSgiTW9udGhseSBUb3RhbCBTYWxlcyBieSBSZWdpb24iLCBmb250c2l6ZT0xNiwgZm9udHdlaWdodD0nYm9sZCcpDQpwbHQueGxhYmVsKCJZZWFyIikNCnBsdC55bGFiZWwoIlRvdGFsIFNhbGVzIikNCnBsdC5sZWdlbmQodGl0bGU9J1JlZ2lvbicpDQpwbHQuZ3JpZChUcnVlKQ0KDQojIEZvcm1hdCB4LWF4aXM6IHNob3cgbGFiZWwgZXZlcnkgSmFudWFyeSBvbmx5DQpwbHQueHRpY2tzKA0KICAgIHRpY2tzPVtkIGZvciBkIGluIG1vbnRobHlfcmVnaW9uX3NhbGVzWydZZWFyTW9udGgnXS51bmlxdWUoKSBpZiBkLm1vbnRoID09IDFdLA0KICAgIGxhYmVscz1bZC5zdHJmdGltZSgnJVknKSBmb3IgZCBpbiBtb250aGx5X3JlZ2lvbl9zYWxlc1snWWVhck1vbnRoJ10udW5pcXVlKCkgaWYgZC5tb250aCA9PSAxXSwNCiAgICByb3RhdGlvbj0wDQopDQoNCnBsdC50aWdodF9sYXlvdXQoKQ0KcGx0LnNob3coKQ0KDQpgYGANCg0KIyMgQXJlYSBDaGFydA0KDQojIyMgUiBDb2RlIChBcmVhIENoYXJ0KQ0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDEuIExvYWQgUmVxdWlyZWQgTGlicmFyaWVzDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoc2NhbGVzKQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAyLiBMb2FkIGFuZCBQcmVwYXJlIERhdGENCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIEJhY2EgZGF0YQ0KZGF0YSA8LSByZWFkX2NzdigiZGF0YV9iaXNuaXMuY3N2IikNCg0KIyBGb3JtYXQgdGFuZ2dhbCBkYW4gYnVhdCBrb2xvbSBZZWFyTW9udGgNCmRhdGEgPC0gZGF0YSAlPiUNCiAgbXV0YXRlKA0KICAgIFRyYW5zYWN0aW9uX0RhdGUgPSBhcy5EYXRlKFRyYW5zYWN0aW9uX0RhdGUpLA0KICAgIFllYXJNb250aCA9IGZsb29yX2RhdGUoVHJhbnNhY3Rpb25fRGF0ZSwgIm1vbnRoIikNCiAgKQ0KDQojIEFncmVnYXNpIHRvdGFsIHNhbGVzIHBlciBidWxhbiBwZXIgcmVnaW9uDQptb250aGx5X3JlZ2lvbl9zYWxlcyA8LSBkYXRhICU+JQ0KICBncm91cF9ieShZZWFyTW9udGgsIFJlZ2lvbikgJT4lDQogIHN1bW1hcmlzZShUb3RhbF9TYWxlcyA9IHN1bShUb3RhbF9QcmljZSwgbmEucm0gPSBUUlVFKSwgLmdyb3VwcyA9ICJkcm9wIikNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMy4gUGxvdCBBcmVhIENoYXJ0DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KZ2dwbG90KG1vbnRobHlfcmVnaW9uX3NhbGVzLCBhZXMoeCA9IFllYXJNb250aCwgeSA9IFRvdGFsX1NhbGVzLCBmaWxsID0gUmVnaW9uKSkgKw0KICBnZW9tX2FyZWEoYWxwaGEgPSAwLjgsIHNpemUgPSAwLjUsIGNvbG91ciA9ICJ3aGl0ZSIpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikgKw0KICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3MgPSAiMSB5ZWFyIiwgZGF0ZV9sYWJlbHMgPSAiJVkiKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiTW9udGhseSBUb3RhbCBTYWxlcyBieSBSZWdpb24gKEFyZWEgQ2hhcnQpIiwNCiAgICB4ID0gIlllYXIiLA0KICAgIHkgPSAiVG90YWwgU2FsZXMiLA0KICAgIGZpbGwgPSAiUmVnaW9uIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpLA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwNCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEsIGFuZ2xlID0gMCwgdmp1c3QgPSAwLjUpLA0KICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksDQogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMyksDQogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDExKQ0KICApDQpgYGANCmBgYHtweXRob259DQppbXBvcnQgcGFuZGFzIGFzIHBkDQppbXBvcnQgbWF0cGxvdGxpYi5weXBsb3QgYXMgcGx0DQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDEuIExvYWQgYW5kIFByZXBhcmUgRGF0YQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmRhdGEgPSBwZC5yZWFkX2NzdigiZGF0YV9iaXNuaXMuY3N2IikNCmRhdGFbJ1RyYW5zYWN0aW9uX0RhdGUnXSA9IHBkLnRvX2RhdGV0aW1lKGRhdGFbJ1RyYW5zYWN0aW9uX0RhdGUnXSkNCmRhdGFbJ1llYXJNb250aCddID0gZGF0YVsnVHJhbnNhY3Rpb25fRGF0ZSddLmR0LnRvX3BlcmlvZCgnTScpLmR0LnRvX3RpbWVzdGFtcCgpDQoNCiMgQWdyZWdhc2kgdG90YWwgc2FsZXMgcGVyIGJ1bGFuIHBlciByZWdpb24NCm1vbnRobHlfc2FsZXMgPSBkYXRhLmdyb3VwYnkoWydZZWFyTW9udGgnLCAnUmVnaW9uJ10pWydUb3RhbF9QcmljZSddLnN1bSgpLnJlc2V0X2luZGV4KCkNCg0KIyBQaXZvdCB1bnR1ayBhcmVhIGNoYXJ0DQpwaXZvdF9kYXRhID0gbW9udGhseV9zYWxlcy5waXZvdChpbmRleD0nWWVhck1vbnRoJywgY29sdW1ucz0nUmVnaW9uJywgdmFsdWVzPSdUb3RhbF9QcmljZScpLmZpbGxuYSgwKQ0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAyLiBQbG90IEFyZWEgQ2hhcnQNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpwbHQuZmlndXJlKGZpZ3NpemU9KDEyLCA2KSkNCnBpdm90X2RhdGEucGxvdChraW5kPSdhcmVhJywgc3RhY2tlZD1UcnVlLCBmaWdzaXplPSgxMiwgNiksIGFscGhhPTAuNykNCg0KcGx0LnRpdGxlKCJNb250aGx5IFRvdGFsIFNhbGVzIGJ5IFJlZ2lvbiAoQXJlYSBDaGFydCkiLCBmb250c2l6ZT0xNiwgZm9udHdlaWdodD0nYm9sZCcpDQpwbHQueGxhYmVsKCJZZWFyIikNCnBsdC55bGFiZWwoIlRvdGFsIFNhbGVzIikNCnBsdC54dGlja3Mocm90YXRpb249NDUpDQpwbHQudGlnaHRfbGF5b3V0KCkNCnBsdC5ncmlkKFRydWUpDQpwbHQubGVnZW5kKHRpdGxlPSdSZWdpb24nKQ0KcGx0LnNob3coKQ0KDQpgYGANCg==