Advanced Praticum:Functions,Loops and Data Science Simulation

Praticum Week 5

library(htmltools)

HTML('
<div class="profile-card">

  <div>
    <img src="JANA.JPEG">
  </div>

  <div class="profile-text">
    <h3>Januaria Teresinha</h3>

    <div class="profile-description">
      as a Student Data Science in Institut Teknologi Sains Bandung
    </div>

    <div class="profile-info-row">
      <p class="profile-info-item">
        <b>Mentored by:</b> <span class="mentor-name">Mr. Bakti Siregar M.Sc.,CDS</span>
      </p>
    </div>
    </div>
    
</div>
')

Januaria Teresinha

as a Student Data Science in Institut Teknologi Sains Bandung

Mentored by: Mr. Bakti Siregar M.Sc.,CDS

1 Introduction

This report is a comprehensive compilation of the Data Science Programming practicum. The primary focus of this study is the integration of fundamental programming building blocks—such as modular functions, nested loops, and multi-tier conditional logic—into complex Data Science workflows.

In a professional data environment, a Data Scientist must often build automated systems to process and simulate large-scale data efficiently. This practicum covers seven core implementations:

1.Mathematical Engine: Computing multi-formula growth models.

2.Sales Simulation: Managing transaction data with nested accumulation logic.

3.Performance Classification: Statistical categorization of workforce data.

4.Corporate Synthesis: Generating hierarchical datasets for multiple companies.

5.Monte Carlo Simulation: Estimating the mathematical constant \(\pi\).

6.Data Transformation: Normalization and Z-Score standardization.

7.Executive Dashboard: Advanced visualization and KPI reporting.

8.Automated Reporting: Lastly, we will develop a function that automatically generates individual reports for each company entity in separate file formats.

2 Program 1: Multi-Formula Computational Engine

  • Logic Explanation

Validation: The function compute_formula validates user input against a library of allowed models.

Nested Iteration: An outer loop cycles through formula types, while an inner loop calculates values for each \(x\) in the range \([1, 20]\).

Visualization: Uses ggplot2 with a Logarithmic Scale to ensure exponential growth doesn’t overshadow linear trends.

library(ggplot2)
library(tidyr)
library(dplyr)

compute_formula <- function(x_range, formula_list) {
  # Daftar formula yang valid
  valid_formulas <- c("linear", "quadratic", "cubic", "exponential")
  
  # Validasi input
  invalid <- setdiff(formula_list, valid_formulas)
  if (length(invalid) > 0) {
    stop(paste("Invalid Input:", paste(invalid, collapse = ", ")))
  }
  
  # Menghitung nilai y berdasarkan formula
  results <- data.frame(x = x_range)
  for (f in formula_list) {
    results[[f]] <- switch(f,
                           linear = x_range,
                           quadratic = x_range^2,
                           cubic = x_range^3,
                           exponential = 2^x_range)
  }
  return(results)
}

# Menghasilkan data
data_results <- compute_formula(1:20, c("linear", "quadratic", "cubic", "exponential"))
data_long <- pivot_longer(data_results, cols = -x, names_to = "Formula", values_to = "y")

# Membuat plot
ggplot(data_long, aes(x = x, y = y, color = Formula)) +
  geom_line(linewidth = 1) +
  geom_point(size = 2) +
  scale_y_log10() + # Skala logaritmik untuk sumbu y
  labs(title = "Comparison of Growth Models",
       subtitle = "Linear, Quadratic, Cubic, and Exponential Growth",
       x = "X Value",
       y = "Output (Y) on Log Scale") +
  theme_minimal() +
  scale_color_brewer(palette = "Set1") + # Palette warna yang lebih menarik
  theme(
    plot.title = element_text(size = 16, face = "bold"),
    plot.subtitle = element_text(size = 12),
    axis.title = element_text(size = 12),
    legend.title = element_text(size = 12),
    legend.text = element_text(size = 10)
  )

3 Program 2: Sales Simulation & Nested Accumulation

  • Logic Explanation

Business Rules: Discounts are applied based on sales volume (\(>8k=15\%\), \(>5k=10\%\), \(>2k=5\%\))

Encapsulation: Uses a nested function apply_logic to separate data generation from business calculations

Cumulative Tracking: A nested loop identifies each salesperson and calculates their running revenue total.

library(dplyr)
library(ggplot2)

simulate_sales <- function(n_salesperson, days) {
  set.seed(123)  # Untuk hasil yang konsisten
  total_records <- n_salesperson * days
  
  # Membuat dataset penjualan
  sales_data <- tibble(
    sales_id = 1:total_records,
    salesperson = rep(paste("Sales", 1:n_salesperson), each = days),
    day = rep(1:days, times = n_salesperson),
    sales_amount = round(runif(total_records, 1000, 10000), 2)
  )
  
  # Menghitung diskon dan penjualan bersih
  sales_data <- sales_data %>%
    mutate(
      discount_rate = case_when(
        sales_amount > 8000 ~ 0.15,
        sales_amount > 5000 ~ 0.10,
        sales_amount > 2000 ~ 0.05,
        TRUE ~ 0
      ),
      net_sales = sales_amount * (1 - discount_rate)
    )
  
  # Menghitung penjualan kumulatif per salesperson
  sales_data <- sales_data %>%
    group_by(salesperson) %>%
    mutate(cumulative_sales = cumsum(net_sales)) %>%
    ungroup()
  
  return(sales_data)
}

# Menghasilkan data penjualan
sales_final <- simulate_sales(5, 15)

# Membuat visualisasi
ggplot(sales_final, aes(x = day, y = cumulative_sales, color = salesperson)) +
  geom_line(linewidth = 1) +
  geom_point(size = 3, alpha = 0.7) +  # Menambahkan titik untuk kejelasan
  labs(
    title = "Cumulative Sales Performance Over Time",
    subtitle = "Comparison of Salespersons' Performance",
    x = "Day",
    y = "Cumulative Sales (Net)",
    color = "Salesperson"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(size = 16, face = "bold"),
    plot.subtitle = element_text(size = 12),
    axis.title = element_text(size = 12),
    legend.title = element_text(size = 12),
    legend.text = element_text(size = 10)
  ) +
  scale_color_brewer(palette = "Set1")  # Menggunakan palette warna yang menarik

4 Program 3: Performance Classification

  • Logic Explanation

Classification: Uses a vectorized function to map sales into 5 distinct categories.

Proportional Analysis: Calculates the relative frequency (%) to assess workforce distribution.

library(ggplot2)
library(dplyr)

# Fungsi untuk mengkategorikan performa penjualan
categorize_performance <- function(sales_vec) {
  cut(sales_vec,
      breaks = c(0, 3000, 5000, 7000, 9000, Inf),
      labels = c("Poor", "Average", "Good", "Very Good", "Excellent"),
      right = FALSE)
}

# Menghasilkan data penjualan
set.seed(456)
data_sales <- runif(100, 1000, 10000)

# Membuat dataframe distribusi kategori
dist_df <- data.frame(Category = categorize_performance(data_sales)) %>%
  count(Category) %>%
  mutate(Percentage = (n / sum(n)) * 100)

# Membuat visualisasi
ggplot(dist_df, aes(x = Category, y = n, fill = Category)) +
  geom_bar(stat = "identity", color = "black", linewidth = 0.3) +  # Menambahkan garis tepi
  geom_text(aes(label = paste0(round(Percentage, 1), "%")), vjust = -0.5, size = 4) +  # Menambahkan label persentase
  labs(
    title = "Distribution of Sales Performance Categories",
    subtitle = "Based on 100 Simulated Sales Data Points",
    x = "Performance Category",
    y = "Count",
    fill = "Category"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(size = 16, face = "bold"),
    plot.subtitle = element_text(size = 12),
    axis.title = element_text(size = 12),
    legend.title = element_text(size = 12),
    legend.text = element_text(size = 10),
    panel.grid.major.x = element_blank(),  # Menghilangkan grid horizontal
    panel.grid.minor.y = element_blank()  # Menghilangkan grid minor vertikal
  ) +
  scale_fill_brewer(palette = "Pastel1") +  # Menggunakan palette warna yang menarik
  scale_y_continuous(expand = expansion(mult = c(0, 0.1)))  # Menyesuaikan sumbu y

5 Program 4: Multi-Company Data Synthesis

  • Logic Explanation

Hierarchy: Generates data for multiple companies using nested loops.

KPI Logic: Identifies “Top Performers” with KPI scores above 90.

library(ggplot2)
library(dplyr)

# Fungsi untuk menghasilkan data perusahaan
generate_company_data <- function(n_comp, n_emp) {
  # Membuat data menggunakan `expand.grid` untuk menghindari nested loops
  all_data <- expand.grid(
    company_id = paste("Company", LETTERS[1:n_comp]),
    employee_id = 1:n_emp
  ) %>%
    mutate(
      salary = round(runif(n_comp * n_emp, 5000, 15000), 0),
      perf_score = round(runif(n_comp * n_emp, 1, 10), 1),
      kpi_score = round(runif(n_comp * n_emp, 50, 100), 0),
      status = ifelse(kpi_score > 90, "Top Performer", "Standard")
    )
  
  return(all_data)
}

# Menghasilkan data untuk 4 perusahaan dengan 50 karyawan masing-masing
set.seed(123)  # Untuk hasil yang konsisten
my_dataset <- generate_company_data(4, 50)

# Membuat visualisasi
ggplot(my_dataset, aes(x = perf_score, y = salary, color = company_id)) +
  geom_point(alpha = 0.7, size = 3) +  # Menambahkan titik dengan transparansi
  geom_smooth(method = "lm", se = FALSE, linewidth = 1, linetype = "dashed") +  # Menambahkan garis regresi
  labs(
    title = "Corporate Performance vs. Salary Analysis",
    subtitle = "Comparison of 4 Simulated Companies",
    x = "Performance Score (1-10)",
    y = "Annual Salary ($)",
    color = "Company ID"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(size = 16, face = "bold"),
    plot.subtitle = element_text(size = 12),
    axis.title = element_text(size = 12),
    legend.title = element_text(size = 12),
    legend.text = element_text(size = 10),
    panel.grid.major = element_line(color = "gray90"),
    panel.grid.minor = element_blank()
  ) +
  scale_color_brewer(palette = "Set1") +  # Menggunakan palette warna yang menarik
  facet_wrap(~ status, scales = "free")  # Membagi plot berdasarkan status performa

company_raw <- generate_company_data(4, 50)

6 Program 5: Monte Carlo Pi Estimation

  • Logic Explanation

Stochastic Method: Uses random points \((x,y)\) to calculate the area ratio of a circle inside a square.

Calculation: \(\pi \approx 4 \times (\text{Points Inside} / \text{Total Points})\).

library(ggplot2)
library(dplyr)

# 1. Fungsi yang Disempurnakan
simulate_pi_estimation <- function(n_points) {
  set.seed(789)  # Untuk hasil yang konsisten
  
  # Generate random points
  points <- data.frame(
    x = runif(n_points, -1, 1),
    y = runif(n_points, -1, 1)
  ) %>%
    mutate(
      distance = x^2 + y^2,
      inside = distance <= 1,
      point_type = ifelse(inside, "Inside Circle", "Outside Circle")
    )
  
  # Calculate pi estimate
  pi_estimate <- 4 * sum(points$inside) / n_points
  
  # Create circle boundary data
  circle_data <- data.frame(
    x = cos(seq(0, 2*pi, length.out = 100)),
    y = sin(seq(0, 2*pi, length.out = 100))
  )
  
  return(list(
    pi_estimate = pi_estimate,
    points = points,
    circle = circle_data,
    n_points = n_points
  ))
}

# 2. Eksekusi Simulasi
simulation_results <- simulate_pi_estimation(5000)

# 3. Visualisasi yang Ditingkatkan
ggplot() +
  # Background square
  geom_rect(aes(xmin = -1, xmax = 1, ymin = -1, ymax = 1),
            fill = "white", color = "black", alpha = 0.1) +
  
  # Points layer
  geom_point(
    data = simulation_results$points,
    aes(x, y, color = point_type),
    alpha = 0.6, size = 1.2, shape = 16
  ) +
  
  # Circle boundary
  geom_path(
    data = simulation_results$circle,
    aes(x, y),
    color = "black", linewidth = 1.2, linetype = "solid"
  ) +
  
  # Coordinate system and styling
  coord_fixed() +
  scale_color_manual(
    values = c("Inside Circle" = "#3498db", "Outside Circle" = "#e74c3c"),
    name = "Point Location"
  ) +
  
  # Labels and annotations
  labs(
    title = "Monte Carlo Simulation for π Estimation",
    subtitle = sprintf(
      "Using %d points | Estimated π = %.5f (Error: %.5f)",
      simulation_results$n_points,
      simulation_results$pi_estimate,
      abs(pi - simulation_results$pi_estimate)
    ),
    x = "X Coordinate",
    y = "Y Coordinate",
    caption = "Theoretical π value = 3.14159"
  ) +
  
  # Theme customization
  theme_minimal() +
  theme(
    plot.title = element_text(size = 16, face = "bold", hjust = 0.5),
    plot.subtitle = element_text(size = 12, hjust = 0.5),
    legend.position = "bottom",
    legend.title = element_text(size = 10),
    legend.text = element_text(size = 9),
    panel.grid = element_line(color = "gray90"),
    panel.border = element_rect(color = "black", fill = NA, linewidth = 0.5)
  ) +
  
  # Additional annotation showing the actual estimate
  annotate(
    "text", x = 0, y = -1.1,
    label = sprintf("Estimate: %.5f", simulation_results$pi_estimate),
    size = 5, fontface = "bold", color = "#2c3e50"
  )

7 Program 6: Data Normalization & Feature Engineering

  • Logic Explanation

Standardization: Implements Min-Max Normalization and Z-Score transformations for salary and KPI variables.

Efficiency Metric: Creates a new feature: \(\text{Performance} / \text{Salary}\).

# Load necessary libraries
library(dplyr)      # For data manipulation
library(ggplot2)    # For data visualization
library(scales)     # For better axis formatting
library(gridExtra)  # For arranging multiple plots
library(grid)       # For textGrob and gpar

## 1. DATA TRANSFORMATION FUNCTIONS ========================================

# Min-Max Normalization (0-1 range)
normalize_columns <- function(data, cols) {
  data %>%
    mutate(across(all_of(cols), 
                  ~ (. - min(., na.rm = TRUE)) / 
                    (max(., na.rm = TRUE) - min(., na.rm = TRUE)),
                  .names = "{col}_norm"))
}

# Z-Score Standardization (mean=0, sd=1)
standardize_columns <- function(data, cols) {
  data %>%
    mutate(across(all_of(cols),
                  ~ (. - mean(., na.rm = TRUE)) / sd(., na.rm = TRUE),
                  .names = "{col}_z"))
}

## 2. FEATURE ENGINEERING ==================================================

create_features <- function(data) {
  data %>%
    mutate(
      # Performance categories
      performance_level = cut(KPI_score,
                             breaks = c(0, 50, 75, 90, 100),
                             labels = c("Low", "Medium", "High", "Elite"),
                             include.lowest = TRUE),
      
      # Salary brackets using quartiles
      salary_bracket = case_when(
        salary < quantile(salary, 0.25, na.rm = TRUE) ~ "Low",
        salary < quantile(salary, 0.75, na.rm = TRUE) ~ "Medium",
        TRUE ~ "High"
      ),
      
      # Efficiency metric (KPI score per $1M salary)
      efficiency = ifelse(salary > 0, KPI_score / (salary / 1e6), NA_real_),
      
      # Outlier detection (Z-score > 2.5)
      is_outlier = ifelse(abs(scale(KPI_score)[,1]) > 2.5, "Outlier", "Normal")
    ) %>%
    mutate(across(c(performance_level, salary_bracket, is_outlier), as.factor))
}

## 3. DATA PREPARATION =====================================================

set.seed(123) 

employee_data <- data.frame(
  employee_id = 1:200,
  company = sample(c('Alpha', 'Beta', 'Gamma', 'Delta', 'Epsilon'), 
                  200, replace = TRUE, 
                  prob = c(0.3, 0.25, 0.2, 0.15, 0.1)),
  salary = pmax(pmin(rnorm(200, 70000, 15000), 150000), 30000),
  performance_raw = rnorm(200, 70, 10),
  department = sample(c('Sales', 'Engineering', 'HR', 'Finance', 'Marketing'), 
                       200, replace = TRUE,
                       prob = c(0.3, 0.25, 0.15, 0.15, 0.15))
) %>%
  mutate(
    performance = case_when(
      department == "Sales" ~ performance_raw * 1.1,
      department == "Engineering" ~ performance_raw * 0.9,
      TRUE ~ performance_raw
    ),
    KPI_score = pmin(pmax(performance + rnorm(200, 0, 5), 0), 100)
  ) %>%
  select(-performance_raw)

## 4. DATA PROCESSING PIPELINE =============================================

# Perbaikan: Kami tetap menyimpan kolom norm/z untuk visualisasi 
# dan tidak menghapus 'efficiency' karena akan diplot nanti.
processed_data <- employee_data %>%
  normalize_columns(c("salary", "KPI_score")) %>%
  standardize_columns(c("salary", "KPI_score")) %>%
  create_features()

## 5. ENHANCED VISUALIZATIONS ==============================================

my_theme <- theme_minimal() +
  theme(
    plot.title = element_text(size = 12, face = "bold", hjust = 0.5),
    axis.title = element_text(size = 10),
    legend.position = "bottom",
    panel.grid.minor = element_blank()
  )

# 1. Salary Distribution Comparison
salary_plot <- ggplot(processed_data) +
  geom_density(aes(x = salary, color = "Original"), linewidth = 1) +
  geom_density(aes(x = salary_norm * 100000, color = "Normalized (Scaled)"), linewidth = 1) +
  scale_color_manual(name = "Scale", 
                     values = c("Original" = "#E69F00", "Normalized (Scaled)" = "#56B4E9")) +
  labs(title = "Salary Distribution", x = "Value", y = "Density") +
  my_theme

# 2. Performance by Salary Bracket
performance_plot <- ggplot(processed_data, 
                          aes(x = salary_bracket, y = KPI_score, fill = salary_bracket)) +
  geom_boxplot(alpha = 0.7) +
  scale_fill_brewer(palette = "Set2") +
  labs(title = "KPI by Salary Bracket", x = "Salary Bracket", y = "KPI Score") +
  my_theme +
  theme(legend.position = "none")

# 3. Efficiency Analysis
efficiency_stats <- processed_data %>%
  filter(!is.na(efficiency)) %>%
  group_by(performance_level) %>%
  summarise(
    median_eff = median(efficiency, na.rm = TRUE),
    mean_eff = mean(efficiency, na.rm = TRUE),
    q1 = quantile(efficiency, 0.25, na.rm = TRUE),
    q3 = quantile(efficiency, 0.75, na.rm = TRUE)
  )

efficiency_plot <- processed_data %>%
  filter(!is.na(efficiency)) %>%
  ggplot(aes(x = performance_level, y = efficiency, fill = performance_level)) +
  geom_violin(alpha = 0.6, width = 0.8) +
  geom_boxplot(width = 0.1, fill = "white", outlier.shape = NA) +
  geom_point(data = efficiency_stats, aes(y = median_eff), color = "red", shape = 18, size = 2) +
  scale_fill_manual(values = c("#FF9AA2", "#FFB7B2", "#FFDAC1", "#E2F0CB")) +
  labs(title = "Efficiency Analysis",
       subtitle = "KPI Score per $1M Salary",
       x = "Performance Level", y = "Efficiency") +
  my_theme +
  theme(legend.position = "none")

## 6. FINAL DASHBOARD ======================================================

# Mengatur layout: Salary dan Performance di atas, Efficiency di bawah (lebar)
grid.arrange(
  salary_plot,
  performance_plot,
  efficiency_plot,
  layout_matrix = rbind(c(1, 2), c(3, 3)),
  top = textGrob("Employee Performance Analysis Dashboard", 
                 gp = gpar(fontsize = 16, fontface = "bold"))
)

  • Conclusion

practicum successfully demonstrated the versatility of R in managing end-to-end data tasks. By utilizing stochastic simulations, multi-tiered logic, and advanced visualization, we have built a solid foundation for professional data science modeling and automated reporting systems.

8 7 Program 7 : Integrated Corporate KPI Dashboard

Focus: Workforce Analytics, Stochastic Simulation, and Performance Correlation

Tooling: R Programming (tidyverse, ggplot2)

This is the finalized, professional English version of your Mini Project: Company KPI Dashboard. I have structured it to match the high-standard academic format of your previous sections, ensuring all technical logic is explained clearly with a clean, “report-ready” layout.

Practicum Project: Integrated Corporate KPI Dashboard

Focus: Workforce Analytics, Stochastic Simulation, and Performance Correlation

Tooling: R Programming (tidyverse, ggplot2)

1. Project Overview

The Company KPI Dashboard serves as a high-level simulation designed to evaluate organizational health across multiple corporate entities. By generating synthetic employee data, this project aims to:

  • Correlate Incentives: Determine if higher salaries align with superior performance.

  • Identify Excellence: Segment the workforce to isolate “Elite” performers.

  • Benchmarking: Compare departmental efficiency (Sales, IT, HR, etc.) to identify operational strengths and weaknesses.

2. Data Requirements & Simulation Logic

To ensure a realistic simulation, each observation in the dataset contains the following attributes:

  • Identifier: Unique Employee ID and Company ID.

  • Financials: Annual Salary (Scaled between 30 and 150 million).

  • Metrics: Performance and KPI scores generated using a normal distribution (\(N(\mu, \sigma^2)\)) to mimic real-world bell-curve distributions.

  • Categorization: Departmental assignments and Tier classification.

3. Implementation Logic

3.1 Data Synthesis & Stochastic Modeling

We use set.seed(123) for reproducibility. The salary and performance scores are generated using rnorm, then “clamped” using pmin and pmax to stay within logical boundaries (e.g., scores cannot exceed 100).

3.2 KPI Tier Classification

The workforce is segmented into four distinct tiers based on their KPI results using the cut() function:

  • Elite: 90 – 100

  • High: 75 – 89

  • Medium: 50 – 74

  • Low: Below 50

# --- 1. Environment Setup ---
library(dplyr)
library(ggplot2)
library(scales)  # Untuk formatting angka

# --- 2. Synthetic Data Generation ---
set.seed(123)

companies <- c('Company_A', 'Company_B', 'Company_C', 'Company_D', 'Company_E')
departments <- c('Sales', 'IT', 'HR', 'Finance', 'Marketing')

# Generating 200 employee records
df <- data.frame(
  employee_id = 1:200,
  company_id = sample(companies, 200, replace = TRUE),
  salary = pmax(pmin(rnorm(200, 70, 15), 150), 30) * 1e6,  # Salary in millions
  performance_score = pmax(pmin(rnorm(200, 70, 15), 100), 0),
  department = sample(departments, 200, replace = TRUE)
)

# Derived Logic: KPI score is 70% of performance + 30% random noise
df$KPI_score <- pmax(pmin(df$performance_score * 0.7 + rnorm(200, 20, 5), 100), 0)

# --- 3. Categorization Logic ---
df$KPI_tier <- cut(df$KPI_score, 
                   breaks = c(0, 50, 75, 90, 100),
                   labels = c('Low', 'Medium', 'High', 'Elite'),
                   include.lowest = TRUE)

# --- 4. Dashboard Visualization ---
ggplot(df, aes(x = KPI_score, y = salary / 1e6, color = department)) +
  geom_point(size = 3, alpha = 0.7) +
  geom_smooth(method = 'lm', se = FALSE, color = 'black', linetype = "dashed", linewidth = 1) +
  labs(
    title = 'Relationship Between Salary and KPI',
    subtitle = 'Visualization of Performance Correlation across 5 Companies',
    x = 'KPI Score (0-100)',
    y = 'Annual Salary (Millions)',
    color = 'Department'
  ) +
  scale_y_continuous(labels = dollar_format(prefix = "$", suffix = "M")) +  # Format salary
  scale_color_brewer(palette = "Set2") +
  theme_minimal() +
  theme(
    plot.title = element_text(size = 16, face = 'bold', hjust = 0.5),
    plot.subtitle = element_text(size = 12, hjust = 0.5, color = "gray50"),
    legend.position = "bottom",
    legend.title = element_text(face = "bold"),
    axis.title = element_text(size = 12, face = "bold"),
    axis.text = element_text(size = 10)
  )

5. Results & Analysis

5.1 Performance Correlation

The Scatter Plot reveals the relationship between financial compensation and output. The black trendline (Linear Model) illustrates the “Pay-for-Performance” slope. If the line slants upward, it suggests that higher salaries are effectively incentivizing better KPI scores.

5.2 Departmental Distribution

By observing the color-coded points, we can identify which departments (e.g., IT vs Sales) cluster in the “Elite” tier. This visualization allows HR managers to quickly spot which departments are over-performing relative to their average salary.

9 8. Program 8: Automated Corporate Reporting Engine

8.1 Logic & Automation Workflow

The objective of this module is to eliminate manual reporting by using a Functional Loop to filter data and export individual summaries for each company in the dataset.

  • Iterative Filtering: The program identifies all unique company_id values. It then loops through this list, creating a subset of data for one company at a time.

  • Metric Calculation: For every company, the engine calculates internal KPIs such as “Average Salary,” “Top Performer Count,” and “Departmental Strength.”

  • Multi-Format Export: To simulate a real-world workflow, the program is designed to save these summaries as CSV files (for data teams) and print Formatted Summaries (for management).

8.2 R Implementation (Automation Engine)

# --- 1. Automated Reporting Function ---
generate_company_reports <- function(master_df) {
  
  # Load library di dalam fungsi untuk memastikan dependensi tersedia
  if (!require("dplyr")) install.packages("dplyr", dependencies = TRUE)
  library(dplyr)
  
  # Validasi: Cek apakah kolom yang dibutuhkan ada
  required_cols <- c("company_id", "KPI_score", "KPI_tier")
  if (!all(required_cols %in% colnames(master_df))) {
    stop("Error: Kolom yang dibutuhkan tidak ditemukan dalam dataframe!")
  }
  
  # Step 1: Membuat folder output agar file rapi
  output_dir <- "Company_Reports"
  if (!dir.exists(output_dir)) {
    dir.create(output_dir, recursive = TRUE)
  }
  
  # Step 2: Ambil daftar unik perusahaan
  unique_companies <- unique(master_df$company_id)
  
  cat("==========================================
")
  cat("  STARTING AUTOMATED REPORT GENERATION    
")
  cat("==========================================
")
  
  # Step 3: Loop melalui setiap perusahaan
  for (comp in unique_companies) {
    
    # Filter data spesifik perusahaan
    comp_data <- master_df %>% filter(company_id == comp)
    
    # Perhitungan Metrik Lokal
    total_emp      <- nrow(comp_data)
    avg_kpi        <- mean(comp_data$KPI_score, na.rm = TRUE)
    top_performers <- sum(comp_data$KPI_tier == "Elite", na.rm = TRUE)
    
    # Pembuatan Nama File (Cleaning: spasi jadi underscore, lowercase)
    clean_name <- gsub(" ", "_", tolower(comp))
    file_path  <- file.path(output_dir, paste0(clean_name, "_Summary.csv"))
    
    # Ekspor Data ke CSV (Real Export)
    write.csv(comp_data, file_path, row.names = FALSE)
    
    # Cetak Summary ke Konsol untuk Verifikasi
    cat(paste0("[PROCESSING]: ", comp, "
"))
    cat(paste0("  - Workforce Size  : ", total_emp, "
"))
    cat(paste0("  - Mean KPI Score  : ", round(avg_kpi, 2), "
"))
    cat(paste0("  - Elite Talent    : ", top_performers, "
"))
    cat(paste0("  - File Saved to   : ", file_path, "
"))
    cat("------------------------------------------
")
  }
  
  cat("SUCCESS: All reports generated in '", output_dir, "' folder.
")
}

# --- 2. Eksekusi ---
# Menjalankan fungsi menggunakan dataframe 'df' yang sudah kita buat sebelumnya
generate_company_reports(df)
## ==========================================
##   STARTING AUTOMATED REPORT GENERATION    
## ==========================================
## [PROCESSING]: Company_C
##   - Workforce Size  : 37
##   - Mean KPI Score  : 66.92
##   - Elite Talent    : 1
##   - File Saved to   : Company_Reports/company_c_Summary.csv
## ------------------------------------------
## [PROCESSING]: Company_B
##   - Workforce Size  : 40
##   - Mean KPI Score  : 68.64
##   - Elite Talent    : 0
##   - File Saved to   : Company_Reports/company_b_Summary.csv
## ------------------------------------------
## [PROCESSING]: Company_E
##   - Workforce Size  : 43
##   - Mean KPI Score  : 67.64
##   - Elite Talent    : 1
##   - File Saved to   : Company_Reports/company_e_Summary.csv
## ------------------------------------------
## [PROCESSING]: Company_D
##   - Workforce Size  : 34
##   - Mean KPI Score  : 67.03
##   - Elite Talent    : 0
##   - File Saved to   : Company_Reports/company_d_Summary.csv
## ------------------------------------------
## [PROCESSING]: Company_A
##   - Workforce Size  : 46
##   - Mean KPI Score  : 69.45
##   - Elite Talent    : 1
##   - File Saved to   : Company_Reports/company_a_Summary.csv
## ------------------------------------------
## SUCCESS: All reports generated in ' Company_Reports ' folder.

9. Summary of Findings & Conclusion

Through these eight programming implementations, we have transitioned from basic syntax to a full-scale Data Science pipeline.

Key Takeaways:

1.Efficiency: Using loops and functions reduces code redundancy and allows for scaling from 1 company to 1,000 with zero extra manual effort.

2.Stochastic Power: Monte Carlo simulations prove that randomness can be a tool for mathematical precision.

3.Visualization: Data is only as good as it is communicated; using ggplot2 ensures that complex standardizations (like Z-Scores) are intuitive for non-technical stakeholders.

Final Academic Reflection

This practicum confirms that the core of Data Science Programming is not just writing code, but architecting logic that can adapt to changing data environments. By mastering these automated workflows, we are prepared to handle high-velocity data challenges in a professional setting.

LS0tDQp0aXRsZTogIkFkdmFuY2VkIFByYXRpY3VtOkZ1bmN0aW9ucyxMb29wcyBhbmQgRGF0YSBTY2llbmNlIFNpbXVsYXRpb24iICAgICAgICMgTWFpbiB0aXRsZSBvZiB0aGUgZG9jdW1lbnQNCnN1YnRpdGxlOiAiUHJhdGljdW0gV2VlayA1IiAgIyBTdWJ0aXRsZSBvciB0b3BpYyBmb3Igd2VlayA0DQphdXRob3I6IA0KLSAiSmFudWFyaWEgVGVyZXNpbmhhIiAgICAgICAjIFJlcGxhY2Ugd2l0aCB5b3VyIGZ1bGwgbmFtZQ0KZGF0ZTogICJgciBmb3JtYXQoU3lzLkRhdGUoKSwgJyVCICVkLCAlWScpYCIgIyBBdXRvIGRpc3BsYXlzIHRoZSBjdXJyZW50IGRhdGUNCm91dHB1dDogICAgICAgICAgICAgICAgICAgICAgICAgIyBPdXRwdXQgc2VjdGlvbiBkZWZpbmVzIHRoZSBmb3JtYXQgYW5kIGxheW91dCANCiAgcm1kZm9ybWF0czo6cmVhZHRoZWRvd246ICAgICAgIyBodHRwczovL2dpdGh1Yi5jb20vanViYS9ybWRmb3JtYXRzDQogICAgc2VsZl9jb250YWluZWQ6IHRydWUgICAgICAgICMgRW1iZWRzIGFsbCByZXNvdXJjZXMgKENTUywgSlMsIGltYWdlcykgDQogICAgdGh1bWJuYWlsczogdHJ1ZSAgICAgICAgICAgICMgRGlzcGxheXMgaW1hZ2UgdGh1bWJuYWlscyBpbiB0aGUgZG9jDQogICAgbGlnaHRib3g6IHRydWUgICAgICAgICAgICAgICMgRW5hYmxlcyBjbGljayB0byBlbmxhcmdlIGltYWdlcw0KICAgIGdhbGxlcnk6IHRydWUgICAgICAgICAgICAgICAjIEdyb3VwcyBpbWFnZXMgaW50byBhbiBpbnRlcmFjdGl2ZSBnYWxsZXJ5DQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlICAgICAgICMgQXV0b21hdGljYWxseSBudW1iZXJzIGFsbCBzZWN0aW9ucw0KICAgIGxpYl9kaXI6IGxpYnMgICAgICAgICAgICAgICAjIERpcmVjdG9yeSB3aGVyZSBKYXZhU2NyaXB0L0NTUyBsaWJyYXJpZXMNCiAgICBkZl9wcmludDogInBhZ2VkIiAgICAgICAgICAgIyBEaXNwbGF5cyBkYXRhIGZyYW1lcyBhcyBpbnRlcmFjdGl2ZSBwYWdlZCANCiAgICBjb2RlX2ZvbGRpbmc6ICJzaG93IiAgICAgICAgIyBBbGxvd3MgZm9sZGluZy91bmZvbGRpbmcgUiBjb2RlIGJsb2NrcyANCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMgICAgICAgICAgIyBBZGRzIGEgYnV0dG9uIHRvIGRvd25sb2FkIGFsbCBSIGNvZGUNCiAgICBjc3M6OlN0eWxlLmNzczoNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KPGJvZHk+DQo8c3R5bGU+DQp9DQoucHJvZmlsZS1jYXJkIHsNCiAgZGlzcGxheTogZmxleDsNCiAgYWxpZ24taXRlbXM6IGNlbnRlcjsNCiAgZ2FwOiAyNXB4Ow0KICBwYWRkaW5nOiAyNXB4Ow0KICBib3JkZXItcmFkaXVzOiAyMHB4Ow0KICBiYWNrZ3JvdW5kOiBsaW5lYXItZ3JhZGllbnQoMTM1ZGVnLCAjZmZlNmVmLCAjZmFkN2U4LCAjZmZlZWY3KTsNCiAgYm94LXNoYWRvdzogMCA2cHggMjBweCByZ2JhKDI1NSwgMTgyLCAxOTMsIDAuMyk7DQogIG1heC13aWR0aDogOTAwcHg7DQogIG1hcmdpbjogMjBweCBhdXRvOw0KfQ0KDQoucHJvZmlsZS1jYXJkIGltZyB7DQogIHdpZHRoOiAxODBweDsNCiAgYm9yZGVyLXJhZGl1czogNTAlOw0KICBib3JkZXI6IDRweCBzb2xpZCB3aGl0ZTsNCiAgYm94LXNoYWRvdzogMCAwIDEycHggcmdiYSgwLDAsMCwwLjI1KTsNCn0NCg0KLnByb2ZpbGUtdGV4dCBoMyB7DQogIGZvbnQtc2l6ZTogMzBweDsNCiAgZm9udC13ZWlnaHQ6IDkwMDsNCiAgbWFyZ2luLWJvdHRvbTogNnB4Ow0KICBjb2xvcjogIzhCMUU0MTsNCiAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KLnByb2ZpbGUtZGVzY3JpcHRpb24gew0KICBmb250LXNpemU6IDE0cHg7DQogIGZvbnQtd2VpZ2h0OiA3MDA7DQogIGZvbnQtc3R5bGU6IGl0YWxpYzsNCiAgY29sb3I6ICM0YTRhNGE7DQogIG1hcmdpbi1ib3R0b206IDE4cHg7DQogIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCi5wcm9maWxlLWluZm8tcm93IHsNCiAgZGlzcGxheTogZmxleDsNCiAgDQogIGdhcDogMjVweDsNCiAgZmxleC13cmFwOiB3cmFwOw0KfQ0KDQoucHJvZmlsZS1pbmZvLWl0ZW0gYiB7DQogIGZvbnQtd2VpZ2h0OiA3MDA7DQp9DQoNCi5wcm9maWxlLWluZm8taXRlbSAubWVudG9yLW5hbWUgew0KICBmb250LXNpemU6IDE1cHg7DQogIGZvbnQtd2VpZ2h0OiA3MDA7DQogIGZvbnQtc3R5bGU6IGl0YWxpYzsNCn0gIA0KPC9zdHlsZT4NCmBgYHtyfQ0KbGlicmFyeShodG1sdG9vbHMpDQoNCkhUTUwoJw0KPGRpdiBjbGFzcz0icHJvZmlsZS1jYXJkIj4NCg0KICA8ZGl2Pg0KICAgIDxpbWcgc3JjPSJKQU5BLkpQRUciPg0KICA8L2Rpdj4NCg0KICA8ZGl2IGNsYXNzPSJwcm9maWxlLXRleHQiPg0KICAgIDxoMz5KYW51YXJpYSBUZXJlc2luaGE8L2gzPg0KDQogICAgPGRpdiBjbGFzcz0icHJvZmlsZS1kZXNjcmlwdGlvbiI+DQogICAgICBhcyBhIFN0dWRlbnQgRGF0YSBTY2llbmNlIGluIEluc3RpdHV0IFRla25vbG9naSBTYWlucyBCYW5kdW5nDQogICAgPC9kaXY+DQoNCiAgICA8ZGl2IGNsYXNzPSJwcm9maWxlLWluZm8tcm93Ij4NCiAgICAgIDxwIGNsYXNzPSJwcm9maWxlLWluZm8taXRlbSI+DQogICAgICAgIDxiPk1lbnRvcmVkIGJ5OjwvYj4gPHNwYW4gY2xhc3M9Im1lbnRvci1uYW1lIj5Nci4gQmFrdGkgU2lyZWdhciBNLlNjLixDRFM8L3NwYW4+DQogICAgICA8L3A+DQogICAgPC9kaXY+DQogICAgPC9kaXY+DQogICAgDQo8L2Rpdj4NCicpDQpgYGANCg0KIyBJbnRyb2R1Y3Rpb24NClRoaXMgcmVwb3J0IGlzIGEgY29tcHJlaGVuc2l2ZSBjb21waWxhdGlvbiBvZiB0aGUgRGF0YSBTY2llbmNlIFByb2dyYW1taW5nIHByYWN0aWN1bS4gVGhlIHByaW1hcnkgZm9jdXMgb2YgdGhpcyBzdHVkeSBpcyB0aGUgaW50ZWdyYXRpb24gb2YgZnVuZGFtZW50YWwgcHJvZ3JhbW1pbmcgYnVpbGRpbmcgYmxvY2tz4oCUc3VjaCBhcyBtb2R1bGFyIGZ1bmN0aW9ucywgbmVzdGVkIGxvb3BzLCBhbmQgbXVsdGktdGllciBjb25kaXRpb25hbCBsb2dpY+KAlGludG8gY29tcGxleCBEYXRhIFNjaWVuY2Ugd29ya2Zsb3dzLg0KDQpJbiBhIHByb2Zlc3Npb25hbCBkYXRhIGVudmlyb25tZW50LCBhIERhdGEgU2NpZW50aXN0IG11c3Qgb2Z0ZW4gYnVpbGQgYXV0b21hdGVkIHN5c3RlbXMgdG8gcHJvY2VzcyBhbmQgc2ltdWxhdGUgbGFyZ2Utc2NhbGUgZGF0YSBlZmZpY2llbnRseS4gVGhpcyBwcmFjdGljdW0gY292ZXJzIHNldmVuIGNvcmUgaW1wbGVtZW50YXRpb25zOg0KDQoqKjEuTWF0aGVtYXRpY2FsIEVuZ2luZToqKiBDb21wdXRpbmcgbXVsdGktZm9ybXVsYSBncm93dGggbW9kZWxzLg0KDQoqKjIuU2FsZXMgU2ltdWxhdGlvbjoqKiBNYW5hZ2luZyB0cmFuc2FjdGlvbiBkYXRhIHdpdGggbmVzdGVkIGFjY3VtdWxhdGlvbiBsb2dpYy4NCg0KKiozLlBlcmZvcm1hbmNlIENsYXNzaWZpY2F0aW9uOioqIFN0YXRpc3RpY2FsIGNhdGVnb3JpemF0aW9uIG9mIHdvcmtmb3JjZSBkYXRhLg0KDQoqKjQuQ29ycG9yYXRlIFN5bnRoZXNpczoqKiBHZW5lcmF0aW5nIGhpZXJhcmNoaWNhbCBkYXRhc2V0cyBmb3IgbXVsdGlwbGUgY29tcGFuaWVzLg0KDQoqKjUuTW9udGUgQ2FybG8gU2ltdWxhdGlvbjoqKiBFc3RpbWF0aW5nIHRoZSBtYXRoZW1hdGljYWwgY29uc3RhbnQgJFxwaSQuDQoNCioqNi5EYXRhIFRyYW5zZm9ybWF0aW9uOioqIE5vcm1hbGl6YXRpb24gYW5kIFotU2NvcmUgc3RhbmRhcmRpemF0aW9uLg0KDQoqKjcuRXhlY3V0aXZlIERhc2hib2FyZDoqKiBBZHZhbmNlZCB2aXN1YWxpemF0aW9uIGFuZCBLUEkgcmVwb3J0aW5nLg0KDQoqKjguQXV0b21hdGVkIFJlcG9ydGluZyoqOiBMYXN0bHksIHdlIHdpbGwgZGV2ZWxvcCBhIGZ1bmN0aW9uIHRoYXQgYXV0b21hdGljYWxseSBnZW5lcmF0ZXMgaW5kaXZpZHVhbCByZXBvcnRzIGZvciBlYWNoIGNvbXBhbnkgZW50aXR5IGluIHNlcGFyYXRlIGZpbGUgZm9ybWF0cy4NCg0KDQojIFByb2dyYW0gMTogTXVsdGktRm9ybXVsYSBDb21wdXRhdGlvbmFsIEVuZ2luZQ0KDQotICoqTG9naWMgRXhwbGFuYXRpb24qKg0KDQoqKlZhbGlkYXRpb246KiogVGhlIGZ1bmN0aW9uIGNvbXB1dGVfZm9ybXVsYSB2YWxpZGF0ZXMgdXNlciBpbnB1dCBhZ2FpbnN0IGEgbGlicmFyeSBvZiBhbGxvd2VkIG1vZGVscy4NCg0KKipOZXN0ZWQgSXRlcmF0aW9uOioqIEFuIG91dGVyIGxvb3AgY3ljbGVzIHRocm91Z2ggZm9ybXVsYSB0eXBlcywgd2hpbGUgYW4gaW5uZXIgbG9vcCBjYWxjdWxhdGVzIHZhbHVlcyBmb3IgZWFjaCAkeCQgaW4gdGhlIHJhbmdlICRbMSwgMjBdJC4NCg0KKipWaXN1YWxpemF0aW9uOioqIFVzZXMgZ2dwbG90MiB3aXRoIGEgTG9nYXJpdGhtaWMgU2NhbGUgdG8gZW5zdXJlIGV4cG9uZW50aWFsIGdyb3d0aCBkb2Vzbid0IG92ZXJzaGFkb3cgbGluZWFyIHRyZW5kcy4NCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgb3V0LmV4dHJhPSdzdHlsZT0iZGlzcGxheTpibG9jazsgbWFyZ2luLWxlZnQ6YXV0bzsgbWFyZ2luLXJpZ2h0OmF1dG87Iid9DQoNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGRwbHlyKQ0KDQpjb21wdXRlX2Zvcm11bGEgPC0gZnVuY3Rpb24oeF9yYW5nZSwgZm9ybXVsYV9saXN0KSB7DQogICMgRGFmdGFyIGZvcm11bGEgeWFuZyB2YWxpZA0KICB2YWxpZF9mb3JtdWxhcyA8LSBjKCJsaW5lYXIiLCAicXVhZHJhdGljIiwgImN1YmljIiwgImV4cG9uZW50aWFsIikNCiAgDQogICMgVmFsaWRhc2kgaW5wdXQNCiAgaW52YWxpZCA8LSBzZXRkaWZmKGZvcm11bGFfbGlzdCwgdmFsaWRfZm9ybXVsYXMpDQogIGlmIChsZW5ndGgoaW52YWxpZCkgPiAwKSB7DQogICAgc3RvcChwYXN0ZSgiSW52YWxpZCBJbnB1dDoiLCBwYXN0ZShpbnZhbGlkLCBjb2xsYXBzZSA9ICIsICIpKSkNCiAgfQ0KICANCiAgIyBNZW5naGl0dW5nIG5pbGFpIHkgYmVyZGFzYXJrYW4gZm9ybXVsYQ0KICByZXN1bHRzIDwtIGRhdGEuZnJhbWUoeCA9IHhfcmFuZ2UpDQogIGZvciAoZiBpbiBmb3JtdWxhX2xpc3QpIHsNCiAgICByZXN1bHRzW1tmXV0gPC0gc3dpdGNoKGYsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5lYXIgPSB4X3JhbmdlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVhZHJhdGljID0geF9yYW5nZV4yLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgY3ViaWMgPSB4X3JhbmdlXjMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBleHBvbmVudGlhbCA9IDJeeF9yYW5nZSkNCiAgfQ0KICByZXR1cm4ocmVzdWx0cykNCn0NCg0KIyBNZW5naGFzaWxrYW4gZGF0YQ0KZGF0YV9yZXN1bHRzIDwtIGNvbXB1dGVfZm9ybXVsYSgxOjIwLCBjKCJsaW5lYXIiLCAicXVhZHJhdGljIiwgImN1YmljIiwgImV4cG9uZW50aWFsIikpDQpkYXRhX2xvbmcgPC0gcGl2b3RfbG9uZ2VyKGRhdGFfcmVzdWx0cywgY29scyA9IC14LCBuYW1lc190byA9ICJGb3JtdWxhIiwgdmFsdWVzX3RvID0gInkiKQ0KDQojIE1lbWJ1YXQgcGxvdA0KZ2dwbG90KGRhdGFfbG9uZywgYWVzKHggPSB4LCB5ID0geSwgY29sb3IgPSBGb3JtdWxhKSkgKw0KICBnZW9tX2xpbmUobGluZXdpZHRoID0gMSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAyKSArDQogIHNjYWxlX3lfbG9nMTAoKSArICMgU2thbGEgbG9nYXJpdG1payB1bnR1ayBzdW1idSB5DQogIGxhYnModGl0bGUgPSAiQ29tcGFyaXNvbiBvZiBHcm93dGggTW9kZWxzIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJMaW5lYXIsIFF1YWRyYXRpYywgQ3ViaWMsIGFuZCBFeHBvbmVudGlhbCBHcm93dGgiLA0KICAgICAgIHggPSAiWCBWYWx1ZSIsDQogICAgICAgeSA9ICJPdXRwdXQgKFkpIG9uIExvZyBTY2FsZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIpICsgIyBQYWxldHRlIHdhcm5hIHlhbmcgbGViaWggbWVuYXJpaw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiksDQogICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwNCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwNCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApDQogICkNCmBgYCANCiAgDQojIFByb2dyYW0gMjogU2FsZXMgU2ltdWxhdGlvbiAmIE5lc3RlZCBBY2N1bXVsYXRpb24NCg0KLSAqKkxvZ2ljIEV4cGxhbmF0aW9uKioNCg0KKipCdXNpbmVzcyBSdWxlczoqKiBEaXNjb3VudHMgYXJlIGFwcGxpZWQgYmFzZWQgb24gc2FsZXMgdm9sdW1lICgkPjhrPTE1XCUkLCAkPjVrPTEwXCUkLCAkPjJrPTVcJSQpDQoNCioqRW5jYXBzdWxhdGlvbjoqKiBVc2VzIGEgbmVzdGVkIGZ1bmN0aW9uIGFwcGx5X2xvZ2ljIHRvIHNlcGFyYXRlIGRhdGEgZ2VuZXJhdGlvbiBmcm9tIGJ1c2luZXNzIGNhbGN1bGF0aW9ucw0KDQoqKkN1bXVsYXRpdmUgVHJhY2tpbmc6KiogQSBuZXN0ZWQgbG9vcCBpZGVudGlmaWVzIGVhY2ggc2FsZXNwZXJzb24gYW5kIGNhbGN1bGF0ZXMgdGhlaXIgcnVubmluZyByZXZlbnVlIHRvdGFsLg0KDQoNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIG91dC5leHRyYT0nc3R5bGU9ImRpc3BsYXk6YmxvY2s7IG1hcmdpbi1sZWZ0OmF1dG87IG1hcmdpbi1yaWdodDphdXRvOyInfQ0KDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KDQpzaW11bGF0ZV9zYWxlcyA8LSBmdW5jdGlvbihuX3NhbGVzcGVyc29uLCBkYXlzKSB7DQogIHNldC5zZWVkKDEyMykgICMgVW50dWsgaGFzaWwgeWFuZyBrb25zaXN0ZW4NCiAgdG90YWxfcmVjb3JkcyA8LSBuX3NhbGVzcGVyc29uICogZGF5cw0KICANCiAgIyBNZW1idWF0IGRhdGFzZXQgcGVuanVhbGFuDQogIHNhbGVzX2RhdGEgPC0gdGliYmxlKA0KICAgIHNhbGVzX2lkID0gMTp0b3RhbF9yZWNvcmRzLA0KICAgIHNhbGVzcGVyc29uID0gcmVwKHBhc3RlKCJTYWxlcyIsIDE6bl9zYWxlc3BlcnNvbiksIGVhY2ggPSBkYXlzKSwNCiAgICBkYXkgPSByZXAoMTpkYXlzLCB0aW1lcyA9IG5fc2FsZXNwZXJzb24pLA0KICAgIHNhbGVzX2Ftb3VudCA9IHJvdW5kKHJ1bmlmKHRvdGFsX3JlY29yZHMsIDEwMDAsIDEwMDAwKSwgMikNCiAgKQ0KICANCiAgIyBNZW5naGl0dW5nIGRpc2tvbiBkYW4gcGVuanVhbGFuIGJlcnNpaA0KICBzYWxlc19kYXRhIDwtIHNhbGVzX2RhdGEgJT4lDQogICAgbXV0YXRlKA0KICAgICAgZGlzY291bnRfcmF0ZSA9IGNhc2Vfd2hlbigNCiAgICAgICAgc2FsZXNfYW1vdW50ID4gODAwMCB+IDAuMTUsDQogICAgICAgIHNhbGVzX2Ftb3VudCA+IDUwMDAgfiAwLjEwLA0KICAgICAgICBzYWxlc19hbW91bnQgPiAyMDAwIH4gMC4wNSwNCiAgICAgICAgVFJVRSB+IDANCiAgICAgICksDQogICAgICBuZXRfc2FsZXMgPSBzYWxlc19hbW91bnQgKiAoMSAtIGRpc2NvdW50X3JhdGUpDQogICAgKQ0KICANCiAgIyBNZW5naGl0dW5nIHBlbmp1YWxhbiBrdW11bGF0aWYgcGVyIHNhbGVzcGVyc29uDQogIHNhbGVzX2RhdGEgPC0gc2FsZXNfZGF0YSAlPiUNCiAgICBncm91cF9ieShzYWxlc3BlcnNvbikgJT4lDQogICAgbXV0YXRlKGN1bXVsYXRpdmVfc2FsZXMgPSBjdW1zdW0obmV0X3NhbGVzKSkgJT4lDQogICAgdW5ncm91cCgpDQogIA0KICByZXR1cm4oc2FsZXNfZGF0YSkNCn0NCg0KIyBNZW5naGFzaWxrYW4gZGF0YSBwZW5qdWFsYW4NCnNhbGVzX2ZpbmFsIDwtIHNpbXVsYXRlX3NhbGVzKDUsIDE1KQ0KDQojIE1lbWJ1YXQgdmlzdWFsaXNhc2kNCmdncGxvdChzYWxlc19maW5hbCwgYWVzKHggPSBkYXksIHkgPSBjdW11bGF0aXZlX3NhbGVzLCBjb2xvciA9IHNhbGVzcGVyc29uKSkgKw0KICBnZW9tX2xpbmUobGluZXdpZHRoID0gMSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAzLCBhbHBoYSA9IDAuNykgKyAgIyBNZW5hbWJhaGthbiB0aXRpayB1bnR1ayBrZWplbGFzYW4NCiAgbGFicygNCiAgICB0aXRsZSA9ICJDdW11bGF0aXZlIFNhbGVzIFBlcmZvcm1hbmNlIE92ZXIgVGltZSIsDQogICAgc3VidGl0bGUgPSAiQ29tcGFyaXNvbiBvZiBTYWxlc3BlcnNvbnMnIFBlcmZvcm1hbmNlIiwNCiAgICB4ID0gIkRheSIsDQogICAgeSA9ICJDdW11bGF0aXZlIFNhbGVzIChOZXQpIiwNCiAgICBjb2xvciA9ICJTYWxlc3BlcnNvbiINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwNCiAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksDQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLA0KICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLA0KICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkNCiAgKSArDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDEiKSAgIyBNZW5nZ3VuYWthbiBwYWxldHRlIHdhcm5hIHlhbmcgbWVuYXJpaw0KYGBgDQogIA0KIyBQcm9ncmFtIDM6IFBlcmZvcm1hbmNlIENsYXNzaWZpY2F0aW9uIA0KDQotICoqTG9naWMgRXhwbGFuYXRpb24qKg0KDQoqKkNsYXNzaWZpY2F0aW9uOioqIFVzZXMgYSB2ZWN0b3JpemVkIGZ1bmN0aW9uIHRvIG1hcCBzYWxlcyBpbnRvIDUgZGlzdGluY3QgY2F0ZWdvcmllcy4NCg0KKipQcm9wb3J0aW9uYWwgQW5hbHlzaXM6KiogQ2FsY3VsYXRlcyB0aGUgcmVsYXRpdmUgZnJlcXVlbmN5ICglKSB0byBhc3Nlc3Mgd29ya2ZvcmNlIGRpc3RyaWJ1dGlvbi4NCg0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBvdXQuZXh0cmE9J3N0eWxlPSJkaXNwbGF5OmJsb2NrOyBtYXJnaW4tbGVmdDphdXRvOyBtYXJnaW4tcmlnaHQ6YXV0bzsiJ30NCg0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCg0KIyBGdW5nc2kgdW50dWsgbWVuZ2thdGVnb3Jpa2FuIHBlcmZvcm1hIHBlbmp1YWxhbg0KY2F0ZWdvcml6ZV9wZXJmb3JtYW5jZSA8LSBmdW5jdGlvbihzYWxlc192ZWMpIHsNCiAgY3V0KHNhbGVzX3ZlYywNCiAgICAgIGJyZWFrcyA9IGMoMCwgMzAwMCwgNTAwMCwgNzAwMCwgOTAwMCwgSW5mKSwNCiAgICAgIGxhYmVscyA9IGMoIlBvb3IiLCAiQXZlcmFnZSIsICJHb29kIiwgIlZlcnkgR29vZCIsICJFeGNlbGxlbnQiKSwNCiAgICAgIHJpZ2h0ID0gRkFMU0UpDQp9DQoNCiMgTWVuZ2hhc2lsa2FuIGRhdGEgcGVuanVhbGFuDQpzZXQuc2VlZCg0NTYpDQpkYXRhX3NhbGVzIDwtIHJ1bmlmKDEwMCwgMTAwMCwgMTAwMDApDQoNCiMgTWVtYnVhdCBkYXRhZnJhbWUgZGlzdHJpYnVzaSBrYXRlZ29yaQ0KZGlzdF9kZiA8LSBkYXRhLmZyYW1lKENhdGVnb3J5ID0gY2F0ZWdvcml6ZV9wZXJmb3JtYW5jZShkYXRhX3NhbGVzKSkgJT4lDQogIGNvdW50KENhdGVnb3J5KSAlPiUNCiAgbXV0YXRlKFBlcmNlbnRhZ2UgPSAobiAvIHN1bShuKSkgKiAxMDApDQoNCiMgTWVtYnVhdCB2aXN1YWxpc2FzaQ0KZ2dwbG90KGRpc3RfZGYsIGFlcyh4ID0gQ2F0ZWdvcnksIHkgPSBuLCBmaWxsID0gQ2F0ZWdvcnkpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBjb2xvciA9ICJibGFjayIsIGxpbmV3aWR0aCA9IDAuMykgKyAgIyBNZW5hbWJhaGthbiBnYXJpcyB0ZXBpDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZTAocm91bmQoUGVyY2VudGFnZSwgMSksICIlIikpLCB2anVzdCA9IC0wLjUsIHNpemUgPSA0KSArICAjIE1lbmFtYmFoa2FuIGxhYmVsIHBlcnNlbnRhc2UNCiAgbGFicygNCiAgICB0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgU2FsZXMgUGVyZm9ybWFuY2UgQ2F0ZWdvcmllcyIsDQogICAgc3VidGl0bGUgPSAiQmFzZWQgb24gMTAwIFNpbXVsYXRlZCBTYWxlcyBEYXRhIFBvaW50cyIsDQogICAgeCA9ICJQZXJmb3JtYW5jZSBDYXRlZ29yeSIsDQogICAgeSA9ICJDb3VudCIsDQogICAgZmlsbCA9ICJDYXRlZ29yeSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwNCiAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksDQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLA0KICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLA0KICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksDQogICAgcGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpLCAgIyBNZW5naGlsYW5na2FuIGdyaWQgaG9yaXpvbnRhbA0KICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSAgIyBNZW5naGlsYW5na2FuIGdyaWQgbWlub3IgdmVydGlrYWwNCiAgKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiUGFzdGVsMSIpICsgICMgTWVuZ2d1bmFrYW4gcGFsZXR0ZSB3YXJuYSB5YW5nIG1lbmFyaWsNCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLCAwLjEpKSkgICMgTWVueWVzdWFpa2FuIHN1bWJ1IHkNCmBgYCAgDQogIA0KIyBQcm9ncmFtIDQ6IE11bHRpLUNvbXBhbnkgRGF0YSBTeW50aGVzaXMgDQoNCi0gKipMb2dpYyBFeHBsYW5hdGlvbioqDQoNCioqSGllcmFyY2h5OioqIEdlbmVyYXRlcyBkYXRhIGZvciBtdWx0aXBsZSBjb21wYW5pZXMgdXNpbmcgbmVzdGVkIGxvb3BzLg0KDQoqKktQSSBMb2dpYzoqKiBJZGVudGlmaWVzICJUb3AgUGVyZm9ybWVycyIgd2l0aCBLUEkgc2NvcmVzIGFib3ZlIDkwLg0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBvdXQuZXh0cmE9J3N0eWxlPSJkaXNwbGF5OmJsb2NrOyBtYXJnaW4tbGVmdDphdXRvOyBtYXJnaW4tcmlnaHQ6YXV0bzsiJ30NCg0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCg0KIyBGdW5nc2kgdW50dWsgbWVuZ2hhc2lsa2FuIGRhdGEgcGVydXNhaGFhbg0KZ2VuZXJhdGVfY29tcGFueV9kYXRhIDwtIGZ1bmN0aW9uKG5fY29tcCwgbl9lbXApIHsNCiAgIyBNZW1idWF0IGRhdGEgbWVuZ2d1bmFrYW4gYGV4cGFuZC5ncmlkYCB1bnR1ayBtZW5naGluZGFyaSBuZXN0ZWQgbG9vcHMNCiAgYWxsX2RhdGEgPC0gZXhwYW5kLmdyaWQoDQogICAgY29tcGFueV9pZCA9IHBhc3RlKCJDb21wYW55IiwgTEVUVEVSU1sxOm5fY29tcF0pLA0KICAgIGVtcGxveWVlX2lkID0gMTpuX2VtcA0KICApICU+JQ0KICAgIG11dGF0ZSgNCiAgICAgIHNhbGFyeSA9IHJvdW5kKHJ1bmlmKG5fY29tcCAqIG5fZW1wLCA1MDAwLCAxNTAwMCksIDApLA0KICAgICAgcGVyZl9zY29yZSA9IHJvdW5kKHJ1bmlmKG5fY29tcCAqIG5fZW1wLCAxLCAxMCksIDEpLA0KICAgICAga3BpX3Njb3JlID0gcm91bmQocnVuaWYobl9jb21wICogbl9lbXAsIDUwLCAxMDApLCAwKSwNCiAgICAgIHN0YXR1cyA9IGlmZWxzZShrcGlfc2NvcmUgPiA5MCwgIlRvcCBQZXJmb3JtZXIiLCAiU3RhbmRhcmQiKQ0KICAgICkNCiAgDQogIHJldHVybihhbGxfZGF0YSkNCn0NCg0KIyBNZW5naGFzaWxrYW4gZGF0YSB1bnR1ayA0IHBlcnVzYWhhYW4gZGVuZ2FuIDUwIGthcnlhd2FuIG1hc2luZy1tYXNpbmcNCnNldC5zZWVkKDEyMykgICMgVW50dWsgaGFzaWwgeWFuZyBrb25zaXN0ZW4NCm15X2RhdGFzZXQgPC0gZ2VuZXJhdGVfY29tcGFueV9kYXRhKDQsIDUwKQ0KDQojIE1lbWJ1YXQgdmlzdWFsaXNhc2kNCmdncGxvdChteV9kYXRhc2V0LCBhZXMoeCA9IHBlcmZfc2NvcmUsIHkgPSBzYWxhcnksIGNvbG9yID0gY29tcGFueV9pZCkpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNywgc2l6ZSA9IDMpICsgICMgTWVuYW1iYWhrYW4gdGl0aWsgZGVuZ2FuIHRyYW5zcGFyYW5zaQ0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFLCBsaW5ld2lkdGggPSAxLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArICAjIE1lbmFtYmFoa2FuIGdhcmlzIHJlZ3Jlc2kNCiAgbGFicygNCiAgICB0aXRsZSA9ICJDb3Jwb3JhdGUgUGVyZm9ybWFuY2UgdnMuIFNhbGFyeSBBbmFseXNpcyIsDQogICAgc3VidGl0bGUgPSAiQ29tcGFyaXNvbiBvZiA0IFNpbXVsYXRlZCBDb21wYW5pZXMiLA0KICAgIHggPSAiUGVyZm9ybWFuY2UgU2NvcmUgKDEtMTApIiwNCiAgICB5ID0gIkFubnVhbCBTYWxhcnkgKCQpIiwNCiAgICBjb2xvciA9ICJDb21wYW55IElEIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpLA0KICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwNCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksDQogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksDQogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwNCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKGNvbG9yID0gImdyYXk5MCIpLA0KICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkNCiAgKSArDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDEiKSArICAjIE1lbmdndW5ha2FuIHBhbGV0dGUgd2FybmEgeWFuZyBtZW5hcmlrDQogIGZhY2V0X3dyYXAofiBzdGF0dXMsIHNjYWxlcyA9ICJmcmVlIikgICMgTWVtYmFnaSBwbG90IGJlcmRhc2Fya2FuIHN0YXR1cyBwZXJmb3JtYQ0KYGBgDQpjb21wYW55X3JhdyA8LSBnZW5lcmF0ZV9jb21wYW55X2RhdGEoNCwgNTApDQoNCiMgUHJvZ3JhbSA1OiBNb250ZSBDYXJsbyBQaSBFc3RpbWF0aW9uDQoNCi0gKipMb2dpYyBFeHBsYW5hdGlvbioqDQoNCioqU3RvY2hhc3RpYyBNZXRob2Q6KiogVXNlcyByYW5kb20gcG9pbnRzICQoeCx5KSQgdG8gY2FsY3VsYXRlIHRoZSBhcmVhIHJhdGlvIG9mIGEgY2lyY2xlIGluc2lkZSBhIHNxdWFyZS4NCg0KKipDYWxjdWxhdGlvbjoqKiAkXHBpIFxhcHByb3ggNCBcdGltZXMgKFx0ZXh0e1BvaW50cyBJbnNpZGV9IC8gXHRleHR7VG90YWwgUG9pbnRzfSkkLg0KDQpgYGAge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgb3V0LmV4dHJhPSdzdHlsZT0iZGlzcGxheTpibG9jazsgbWFyZ2luLWxlZnQ6YXV0bzsgbWFyZ2luLXJpZ2h0OmF1dG87Iid9DQoNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQoNCiMgMS4gRnVuZ3NpIHlhbmcgRGlzZW1wdXJuYWthbg0Kc2ltdWxhdGVfcGlfZXN0aW1hdGlvbiA8LSBmdW5jdGlvbihuX3BvaW50cykgew0KICBzZXQuc2VlZCg3ODkpICAjIFVudHVrIGhhc2lsIHlhbmcga29uc2lzdGVuDQogIA0KICAjIEdlbmVyYXRlIHJhbmRvbSBwb2ludHMNCiAgcG9pbnRzIDwtIGRhdGEuZnJhbWUoDQogICAgeCA9IHJ1bmlmKG5fcG9pbnRzLCAtMSwgMSksDQogICAgeSA9IHJ1bmlmKG5fcG9pbnRzLCAtMSwgMSkNCiAgKSAlPiUNCiAgICBtdXRhdGUoDQogICAgICBkaXN0YW5jZSA9IHheMiArIHleMiwNCiAgICAgIGluc2lkZSA9IGRpc3RhbmNlIDw9IDEsDQogICAgICBwb2ludF90eXBlID0gaWZlbHNlKGluc2lkZSwgIkluc2lkZSBDaXJjbGUiLCAiT3V0c2lkZSBDaXJjbGUiKQ0KICAgICkNCiAgDQogICMgQ2FsY3VsYXRlIHBpIGVzdGltYXRlDQogIHBpX2VzdGltYXRlIDwtIDQgKiBzdW0ocG9pbnRzJGluc2lkZSkgLyBuX3BvaW50cw0KICANCiAgIyBDcmVhdGUgY2lyY2xlIGJvdW5kYXJ5IGRhdGENCiAgY2lyY2xlX2RhdGEgPC0gZGF0YS5mcmFtZSgNCiAgICB4ID0gY29zKHNlcSgwLCAyKnBpLCBsZW5ndGgub3V0ID0gMTAwKSksDQogICAgeSA9IHNpbihzZXEoMCwgMipwaSwgbGVuZ3RoLm91dCA9IDEwMCkpDQogICkNCiAgDQogIHJldHVybihsaXN0KA0KICAgIHBpX2VzdGltYXRlID0gcGlfZXN0aW1hdGUsDQogICAgcG9pbnRzID0gcG9pbnRzLA0KICAgIGNpcmNsZSA9IGNpcmNsZV9kYXRhLA0KICAgIG5fcG9pbnRzID0gbl9wb2ludHMNCiAgKSkNCn0NCg0KIyAyLiBFa3Nla3VzaSBTaW11bGFzaQ0Kc2ltdWxhdGlvbl9yZXN1bHRzIDwtIHNpbXVsYXRlX3BpX2VzdGltYXRpb24oNTAwMCkNCg0KIyAzLiBWaXN1YWxpc2FzaSB5YW5nIERpdGluZ2thdGthbg0KZ2dwbG90KCkgKw0KICAjIEJhY2tncm91bmQgc3F1YXJlDQogIGdlb21fcmVjdChhZXMoeG1pbiA9IC0xLCB4bWF4ID0gMSwgeW1pbiA9IC0xLCB5bWF4ID0gMSksDQogICAgICAgICAgICBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuMSkgKw0KICANCiAgIyBQb2ludHMgbGF5ZXINCiAgZ2VvbV9wb2ludCgNCiAgICBkYXRhID0gc2ltdWxhdGlvbl9yZXN1bHRzJHBvaW50cywNCiAgICBhZXMoeCwgeSwgY29sb3IgPSBwb2ludF90eXBlKSwNCiAgICBhbHBoYSA9IDAuNiwgc2l6ZSA9IDEuMiwgc2hhcGUgPSAxNg0KICApICsNCiAgDQogICMgQ2lyY2xlIGJvdW5kYXJ5DQogIGdlb21fcGF0aCgNCiAgICBkYXRhID0gc2ltdWxhdGlvbl9yZXN1bHRzJGNpcmNsZSwNCiAgICBhZXMoeCwgeSksDQogICAgY29sb3IgPSAiYmxhY2siLCBsaW5ld2lkdGggPSAxLjIsIGxpbmV0eXBlID0gInNvbGlkIg0KICApICsNCiAgDQogICMgQ29vcmRpbmF0ZSBzeXN0ZW0gYW5kIHN0eWxpbmcNCiAgY29vcmRfZml4ZWQoKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCgNCiAgICB2YWx1ZXMgPSBjKCJJbnNpZGUgQ2lyY2xlIiA9ICIjMzQ5OGRiIiwgIk91dHNpZGUgQ2lyY2xlIiA9ICIjZTc0YzNjIiksDQogICAgbmFtZSA9ICJQb2ludCBMb2NhdGlvbiINCiAgKSArDQogIA0KICAjIExhYmVscyBhbmQgYW5ub3RhdGlvbnMNCiAgbGFicygNCiAgICB0aXRsZSA9ICJNb250ZSBDYXJsbyBTaW11bGF0aW9uIGZvciDPgCBFc3RpbWF0aW9uIiwNCiAgICBzdWJ0aXRsZSA9IHNwcmludGYoDQogICAgICAiVXNpbmcgJWQgcG9pbnRzIHwgRXN0aW1hdGVkIM+AID0gJS41ZiAoRXJyb3I6ICUuNWYpIiwNCiAgICAgIHNpbXVsYXRpb25fcmVzdWx0cyRuX3BvaW50cywNCiAgICAgIHNpbXVsYXRpb25fcmVzdWx0cyRwaV9lc3RpbWF0ZSwNCiAgICAgIGFicyhwaSAtIHNpbXVsYXRpb25fcmVzdWx0cyRwaV9lc3RpbWF0ZSkNCiAgICApLA0KICAgIHggPSAiWCBDb29yZGluYXRlIiwNCiAgICB5ID0gIlkgQ29vcmRpbmF0ZSIsDQogICAgY2FwdGlvbiA9ICJUaGVvcmV0aWNhbCDPgCB2YWx1ZSA9IDMuMTQxNTkiDQogICkgKw0KICANCiAgIyBUaGVtZSBjdXN0b21pemF0aW9uDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiLCBoanVzdCA9IDAuNSksDQogICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGhqdXN0ID0gMC41KSwNCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwNCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gOSksDQogICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmF5OTAiKSwNCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3IgPSAiYmxhY2siLCBmaWxsID0gTkEsIGxpbmV3aWR0aCA9IDAuNSkNCiAgKSArDQogIA0KICAjIEFkZGl0aW9uYWwgYW5ub3RhdGlvbiBzaG93aW5nIHRoZSBhY3R1YWwgZXN0aW1hdGUNCiAgYW5ub3RhdGUoDQogICAgInRleHQiLCB4ID0gMCwgeSA9IC0xLjEsDQogICAgbGFiZWwgPSBzcHJpbnRmKCJFc3RpbWF0ZTogJS41ZiIsIHNpbXVsYXRpb25fcmVzdWx0cyRwaV9lc3RpbWF0ZSksDQogICAgc2l6ZSA9IDUsIGZvbnRmYWNlID0gImJvbGQiLCBjb2xvciA9ICIjMmMzZTUwIg0KICApDQpgYGANCg0KIyAgUHJvZ3JhbSA2OiBEYXRhIE5vcm1hbGl6YXRpb24gJiBGZWF0dXJlIEVuZ2luZWVyaW5nDQoNCi0gKipMb2dpYyBFeHBsYW5hdGlvbioqDQoNCioqU3RhbmRhcmRpemF0aW9uOioqIEltcGxlbWVudHMgTWluLU1heCBOb3JtYWxpemF0aW9uIGFuZCBaLVNjb3JlIHRyYW5zZm9ybWF0aW9ucyBmb3Igc2FsYXJ5IGFuZCBLUEkgdmFyaWFibGVzLg0KDQoqKkVmZmljaWVuY3kgTWV0cmljOioqIENyZWF0ZXMgYSBuZXcgZmVhdHVyZTogJFx0ZXh0e1BlcmZvcm1hbmNlfSAvIFx0ZXh0e1NhbGFyeX0kLg0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBvdXQuZXh0cmE9J3N0eWxlPSJkaXNwbGF5OmJsb2NrOyBtYXJnaW4tbGVmdDphdXRvOyBtYXJnaW4tcmlnaHQ6YXV0bzsiJ30NCiMgTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzDQpsaWJyYXJ5KGRwbHlyKSAgICAgICMgRm9yIGRhdGEgbWFuaXB1bGF0aW9uDQpsaWJyYXJ5KGdncGxvdDIpICAgICMgRm9yIGRhdGEgdmlzdWFsaXphdGlvbg0KbGlicmFyeShzY2FsZXMpICAgICAjIEZvciBiZXR0ZXIgYXhpcyBmb3JtYXR0aW5nDQpsaWJyYXJ5KGdyaWRFeHRyYSkgICMgRm9yIGFycmFuZ2luZyBtdWx0aXBsZSBwbG90cw0KbGlicmFyeShncmlkKSAgICAgICAjIEZvciB0ZXh0R3JvYiBhbmQgZ3Bhcg0KDQojIyAxLiBEQVRBIFRSQU5TRk9STUFUSU9OIEZVTkNUSU9OUyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCiMgTWluLU1heCBOb3JtYWxpemF0aW9uICgwLTEgcmFuZ2UpDQpub3JtYWxpemVfY29sdW1ucyA8LSBmdW5jdGlvbihkYXRhLCBjb2xzKSB7DQogIGRhdGEgJT4lDQogICAgbXV0YXRlKGFjcm9zcyhhbGxfb2YoY29scyksIA0KICAgICAgICAgICAgICAgICAgfiAoLiAtIG1pbiguLCBuYS5ybSA9IFRSVUUpKSAvIA0KICAgICAgICAgICAgICAgICAgICAobWF4KC4sIG5hLnJtID0gVFJVRSkgLSBtaW4oLiwgbmEucm0gPSBUUlVFKSksDQogICAgICAgICAgICAgICAgICAubmFtZXMgPSAie2NvbH1fbm9ybSIpKQ0KfQ0KDQojIFotU2NvcmUgU3RhbmRhcmRpemF0aW9uIChtZWFuPTAsIHNkPTEpDQpzdGFuZGFyZGl6ZV9jb2x1bW5zIDwtIGZ1bmN0aW9uKGRhdGEsIGNvbHMpIHsNCiAgZGF0YSAlPiUNCiAgICBtdXRhdGUoYWNyb3NzKGFsbF9vZihjb2xzKSwNCiAgICAgICAgICAgICAgICAgIH4gKC4gLSBtZWFuKC4sIG5hLnJtID0gVFJVRSkpIC8gc2QoLiwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgICAgICAgIC5uYW1lcyA9ICJ7Y29sfV96IikpDQp9DQoNCiMjIDIuIEZFQVRVUkUgRU5HSU5FRVJJTkcgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KY3JlYXRlX2ZlYXR1cmVzIDwtIGZ1bmN0aW9uKGRhdGEpIHsNCiAgZGF0YSAlPiUNCiAgICBtdXRhdGUoDQogICAgICAjIFBlcmZvcm1hbmNlIGNhdGVnb3JpZXMNCiAgICAgIHBlcmZvcm1hbmNlX2xldmVsID0gY3V0KEtQSV9zY29yZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygwLCA1MCwgNzUsIDkwLCAxMDApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJMb3ciLCAiTWVkaXVtIiwgIkhpZ2giLCAiRWxpdGUiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKSwNCiAgICAgIA0KICAgICAgIyBTYWxhcnkgYnJhY2tldHMgdXNpbmcgcXVhcnRpbGVzDQogICAgICBzYWxhcnlfYnJhY2tldCA9IGNhc2Vfd2hlbigNCiAgICAgICAgc2FsYXJ5IDwgcXVhbnRpbGUoc2FsYXJ5LCAwLjI1LCBuYS5ybSA9IFRSVUUpIH4gIkxvdyIsDQogICAgICAgIHNhbGFyeSA8IHF1YW50aWxlKHNhbGFyeSwgMC43NSwgbmEucm0gPSBUUlVFKSB+ICJNZWRpdW0iLA0KICAgICAgICBUUlVFIH4gIkhpZ2giDQogICAgICApLA0KICAgICAgDQogICAgICAjIEVmZmljaWVuY3kgbWV0cmljIChLUEkgc2NvcmUgcGVyICQxTSBzYWxhcnkpDQogICAgICBlZmZpY2llbmN5ID0gaWZlbHNlKHNhbGFyeSA+IDAsIEtQSV9zY29yZSAvIChzYWxhcnkgLyAxZTYpLCBOQV9yZWFsXyksDQogICAgICANCiAgICAgICMgT3V0bGllciBkZXRlY3Rpb24gKFotc2NvcmUgPiAyLjUpDQogICAgICBpc19vdXRsaWVyID0gaWZlbHNlKGFicyhzY2FsZShLUElfc2NvcmUpWywxXSkgPiAyLjUsICJPdXRsaWVyIiwgIk5vcm1hbCIpDQogICAgKSAlPiUNCiAgICBtdXRhdGUoYWNyb3NzKGMocGVyZm9ybWFuY2VfbGV2ZWwsIHNhbGFyeV9icmFja2V0LCBpc19vdXRsaWVyKSwgYXMuZmFjdG9yKSkNCn0NCg0KIyMgMy4gREFUQSBQUkVQQVJBVElPTiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQpzZXQuc2VlZCgxMjMpIA0KDQplbXBsb3llZV9kYXRhIDwtIGRhdGEuZnJhbWUoDQogIGVtcGxveWVlX2lkID0gMToyMDAsDQogIGNvbXBhbnkgPSBzYW1wbGUoYygnQWxwaGEnLCAnQmV0YScsICdHYW1tYScsICdEZWx0YScsICdFcHNpbG9uJyksIA0KICAgICAgICAgICAgICAgICAgMjAwLCByZXBsYWNlID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICBwcm9iID0gYygwLjMsIDAuMjUsIDAuMiwgMC4xNSwgMC4xKSksDQogIHNhbGFyeSA9IHBtYXgocG1pbihybm9ybSgyMDAsIDcwMDAwLCAxNTAwMCksIDE1MDAwMCksIDMwMDAwKSwNCiAgcGVyZm9ybWFuY2VfcmF3ID0gcm5vcm0oMjAwLCA3MCwgMTApLA0KICBkZXBhcnRtZW50ID0gc2FtcGxlKGMoJ1NhbGVzJywgJ0VuZ2luZWVyaW5nJywgJ0hSJywgJ0ZpbmFuY2UnLCAnTWFya2V0aW5nJyksIA0KICAgICAgICAgICAgICAgICAgICAgICAyMDAsIHJlcGxhY2UgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICBwcm9iID0gYygwLjMsIDAuMjUsIDAuMTUsIDAuMTUsIDAuMTUpKQ0KKSAlPiUNCiAgbXV0YXRlKA0KICAgIHBlcmZvcm1hbmNlID0gY2FzZV93aGVuKA0KICAgICAgZGVwYXJ0bWVudCA9PSAiU2FsZXMiIH4gcGVyZm9ybWFuY2VfcmF3ICogMS4xLA0KICAgICAgZGVwYXJ0bWVudCA9PSAiRW5naW5lZXJpbmciIH4gcGVyZm9ybWFuY2VfcmF3ICogMC45LA0KICAgICAgVFJVRSB+IHBlcmZvcm1hbmNlX3Jhdw0KICAgICksDQogICAgS1BJX3Njb3JlID0gcG1pbihwbWF4KHBlcmZvcm1hbmNlICsgcm5vcm0oMjAwLCAwLCA1KSwgMCksIDEwMCkNCiAgKSAlPiUNCiAgc2VsZWN0KC1wZXJmb3JtYW5jZV9yYXcpDQoNCiMjIDQuIERBVEEgUFJPQ0VTU0lORyBQSVBFTElORSA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KIyBQZXJiYWlrYW46IEthbWkgdGV0YXAgbWVueWltcGFuIGtvbG9tIG5vcm0veiB1bnR1ayB2aXN1YWxpc2FzaSANCiMgZGFuIHRpZGFrIG1lbmdoYXB1cyAnZWZmaWNpZW5jeScga2FyZW5hIGFrYW4gZGlwbG90IG5hbnRpLg0KcHJvY2Vzc2VkX2RhdGEgPC0gZW1wbG95ZWVfZGF0YSAlPiUNCiAgbm9ybWFsaXplX2NvbHVtbnMoYygic2FsYXJ5IiwgIktQSV9zY29yZSIpKSAlPiUNCiAgc3RhbmRhcmRpemVfY29sdW1ucyhjKCJzYWxhcnkiLCAiS1BJX3Njb3JlIikpICU+JQ0KICBjcmVhdGVfZmVhdHVyZXMoKQ0KDQojIyA1LiBFTkhBTkNFRCBWSVNVQUxJWkFUSU9OUyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCm15X3RoZW1lIDwtIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiLCBoanVzdCA9IDAuNSksDQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLA0KICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkNCiAgKQ0KDQojIDEuIFNhbGFyeSBEaXN0cmlidXRpb24gQ29tcGFyaXNvbg0Kc2FsYXJ5X3Bsb3QgPC0gZ2dwbG90KHByb2Nlc3NlZF9kYXRhKSArDQogIGdlb21fZGVuc2l0eShhZXMoeCA9IHNhbGFyeSwgY29sb3IgPSAiT3JpZ2luYWwiKSwgbGluZXdpZHRoID0gMSkgKw0KICBnZW9tX2RlbnNpdHkoYWVzKHggPSBzYWxhcnlfbm9ybSAqIDEwMDAwMCwgY29sb3IgPSAiTm9ybWFsaXplZCAoU2NhbGVkKSIpLCBsaW5ld2lkdGggPSAxKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gIlNjYWxlIiwgDQogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCJPcmlnaW5hbCIgPSAiI0U2OUYwMCIsICJOb3JtYWxpemVkIChTY2FsZWQpIiA9ICIjNTZCNEU5IikpICsNCiAgbGFicyh0aXRsZSA9ICJTYWxhcnkgRGlzdHJpYnV0aW9uIiwgeCA9ICJWYWx1ZSIsIHkgPSAiRGVuc2l0eSIpICsNCiAgbXlfdGhlbWUNCg0KIyAyLiBQZXJmb3JtYW5jZSBieSBTYWxhcnkgQnJhY2tldA0KcGVyZm9ybWFuY2VfcGxvdCA8LSBnZ3Bsb3QocHJvY2Vzc2VkX2RhdGEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IHNhbGFyeV9icmFja2V0LCB5ID0gS1BJX3Njb3JlLCBmaWxsID0gc2FsYXJ5X2JyYWNrZXQpKSArDQogIGdlb21fYm94cGxvdChhbHBoYSA9IDAuNykgKw0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDIiKSArDQogIGxhYnModGl0bGUgPSAiS1BJIGJ5IFNhbGFyeSBCcmFja2V0IiwgeCA9ICJTYWxhcnkgQnJhY2tldCIsIHkgPSAiS1BJIFNjb3JlIikgKw0KICBteV90aGVtZSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KIyAzLiBFZmZpY2llbmN5IEFuYWx5c2lzDQplZmZpY2llbmN5X3N0YXRzIDwtIHByb2Nlc3NlZF9kYXRhICU+JQ0KICBmaWx0ZXIoIWlzLm5hKGVmZmljaWVuY3kpKSAlPiUNCiAgZ3JvdXBfYnkocGVyZm9ybWFuY2VfbGV2ZWwpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgbWVkaWFuX2VmZiA9IG1lZGlhbihlZmZpY2llbmN5LCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW5fZWZmID0gbWVhbihlZmZpY2llbmN5LCBuYS5ybSA9IFRSVUUpLA0KICAgIHExID0gcXVhbnRpbGUoZWZmaWNpZW5jeSwgMC4yNSwgbmEucm0gPSBUUlVFKSwNCiAgICBxMyA9IHF1YW50aWxlKGVmZmljaWVuY3ksIDAuNzUsIG5hLnJtID0gVFJVRSkNCiAgKQ0KDQplZmZpY2llbmN5X3Bsb3QgPC0gcHJvY2Vzc2VkX2RhdGEgJT4lDQogIGZpbHRlcighaXMubmEoZWZmaWNpZW5jeSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBwZXJmb3JtYW5jZV9sZXZlbCwgeSA9IGVmZmljaWVuY3ksIGZpbGwgPSBwZXJmb3JtYW5jZV9sZXZlbCkpICsNCiAgZ2VvbV92aW9saW4oYWxwaGEgPSAwLjYsIHdpZHRoID0gMC44KSArDQogIGdlb21fYm94cGxvdCh3aWR0aCA9IDAuMSwgZmlsbCA9ICJ3aGl0ZSIsIG91dGxpZXIuc2hhcGUgPSBOQSkgKw0KICBnZW9tX3BvaW50KGRhdGEgPSBlZmZpY2llbmN5X3N0YXRzLCBhZXMoeSA9IG1lZGlhbl9lZmYpLCBjb2xvciA9ICJyZWQiLCBzaGFwZSA9IDE4LCBzaXplID0gMikgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjRkY5QUEyIiwgIiNGRkI3QjIiLCAiI0ZGREFDMSIsICIjRTJGMENCIikpICsNCiAgbGFicyh0aXRsZSA9ICJFZmZpY2llbmN5IEFuYWx5c2lzIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJLUEkgU2NvcmUgcGVyICQxTSBTYWxhcnkiLA0KICAgICAgIHggPSAiUGVyZm9ybWFuY2UgTGV2ZWwiLCB5ID0gIkVmZmljaWVuY3kiKSArDQogIG15X3RoZW1lICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQojIyA2LiBGSU5BTCBEQVNIQk9BUkQgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCiMgTWVuZ2F0dXIgbGF5b3V0OiBTYWxhcnkgZGFuIFBlcmZvcm1hbmNlIGRpIGF0YXMsIEVmZmljaWVuY3kgZGkgYmF3YWggKGxlYmFyKQ0KZ3JpZC5hcnJhbmdlKA0KICBzYWxhcnlfcGxvdCwNCiAgcGVyZm9ybWFuY2VfcGxvdCwNCiAgZWZmaWNpZW5jeV9wbG90LA0KICBsYXlvdXRfbWF0cml4ID0gcmJpbmQoYygxLCAyKSwgYygzLCAzKSksDQogIHRvcCA9IHRleHRHcm9iKCJFbXBsb3llZSBQZXJmb3JtYW5jZSBBbmFseXNpcyBEYXNoYm9hcmQiLCANCiAgICAgICAgICAgICAgICAgZ3AgPSBncGFyKGZvbnRzaXplID0gMTYsIGZvbnRmYWNlID0gImJvbGQiKSkNCikNCmBgYA0KDQotICoqQ29uY2x1c2lvbioqDQoNCiBwcmFjdGljdW0gc3VjY2Vzc2Z1bGx5IGRlbW9uc3RyYXRlZCB0aGUgdmVyc2F0aWxpdHkgb2YgUiBpbiBtYW5hZ2luZyBlbmQtdG8tZW5kIGRhdGEgdGFza3MuIEJ5IHV0aWxpemluZyBzdG9jaGFzdGljIHNpbXVsYXRpb25zLCBtdWx0aS10aWVyZWQgbG9naWMsIGFuZCBhZHZhbmNlZCB2aXN1YWxpemF0aW9uLCB3ZSBoYXZlIGJ1aWx0IGEgc29saWQgZm91bmRhdGlvbiBmb3IgcHJvZmVzc2lvbmFsIGRhdGEgc2NpZW5jZSBtb2RlbGluZyBhbmQgYXV0b21hdGVkIHJlcG9ydGluZyBzeXN0ZW1zLg0KIA0KIA0KIyA3IFByb2dyYW0gNyA6IEludGVncmF0ZWQgQ29ycG9yYXRlIEtQSSBEYXNoYm9hcmQNCg0KKipGb2N1czoqKiBXb3JrZm9yY2UgQW5hbHl0aWNzLCBTdG9jaGFzdGljIFNpbXVsYXRpb24sIGFuZCBQZXJmb3JtYW5jZSBDb3JyZWxhdGlvbg0KDQoqKlRvb2xpbmc6KiogUiBQcm9ncmFtbWluZyAodGlkeXZlcnNlLCBnZ3Bsb3QyKQ0KDQoNClRoaXMgaXMgdGhlIGZpbmFsaXplZCwgcHJvZmVzc2lvbmFsIEVuZ2xpc2ggdmVyc2lvbiBvZiB5b3VyIE1pbmkgUHJvamVjdDogQ29tcGFueSBLUEkgRGFzaGJvYXJkLiBJIGhhdmUgc3RydWN0dXJlZCBpdCB0byBtYXRjaCB0aGUgaGlnaC1zdGFuZGFyZCBhY2FkZW1pYyBmb3JtYXQgb2YgeW91ciBwcmV2aW91cyBzZWN0aW9ucywgZW5zdXJpbmcgYWxsIHRlY2huaWNhbCBsb2dpYyBpcyBleHBsYWluZWQgY2xlYXJseSB3aXRoIGEgY2xlYW4sICJyZXBvcnQtcmVhZHkiIGxheW91dC4NCg0KKipQcmFjdGljdW0gUHJvamVjdDoqKiBJbnRlZ3JhdGVkIENvcnBvcmF0ZSBLUEkgRGFzaGJvYXJkDQoNCioqRm9jdXM6KiogV29ya2ZvcmNlIEFuYWx5dGljcywgU3RvY2hhc3RpYyBTaW11bGF0aW9uLCBhbmQgUGVyZm9ybWFuY2UgQ29ycmVsYXRpb24NCg0KKipUb29saW5nOioqIFIgUHJvZ3JhbW1pbmcgKHRpZHl2ZXJzZSwgZ2dwbG90MikNCg0KKioxLiBQcm9qZWN0IE92ZXJ2aWV3KioNCg0KVGhlIENvbXBhbnkgS1BJIERhc2hib2FyZCBzZXJ2ZXMgYXMgYSBoaWdoLWxldmVsIHNpbXVsYXRpb24gZGVzaWduZWQgdG8gZXZhbHVhdGUgb3JnYW5pemF0aW9uYWwgaGVhbHRoIGFjcm9zcyBtdWx0aXBsZSBjb3Jwb3JhdGUgZW50aXRpZXMuIEJ5IGdlbmVyYXRpbmcgc3ludGhldGljIGVtcGxveWVlIGRhdGEsIHRoaXMgcHJvamVjdCBhaW1zIHRvOg0KDQotICoqQ29ycmVsYXRlIEluY2VudGl2ZXM6KiogRGV0ZXJtaW5lIGlmIGhpZ2hlciBzYWxhcmllcyBhbGlnbiB3aXRoIHN1cGVyaW9yIHBlcmZvcm1hbmNlLg0KDQotICoqSWRlbnRpZnkgRXhjZWxsZW5jZToqKiBTZWdtZW50IHRoZSB3b3JrZm9yY2UgdG8gaXNvbGF0ZSAiRWxpdGUiIHBlcmZvcm1lcnMuDQoNCi0gKipCZW5jaG1hcmtpbmc6KiogQ29tcGFyZSBkZXBhcnRtZW50YWwgZWZmaWNpZW5jeSAoU2FsZXMsIElULCBIUiwgZXRjLikgdG8gaWRlbnRpZnkgb3BlcmF0aW9uYWwgc3RyZW5ndGhzIGFuZCB3ZWFrbmVzc2VzLg0KDQoNCioqMi4gRGF0YSBSZXF1aXJlbWVudHMgJiBTaW11bGF0aW9uIExvZ2ljKiogDQoNClRvIGVuc3VyZSBhIHJlYWxpc3RpYyBzaW11bGF0aW9uLCBlYWNoIG9ic2VydmF0aW9uIGluIHRoZSBkYXRhc2V0IGNvbnRhaW5zIHRoZSBmb2xsb3dpbmcgYXR0cmlidXRlczoNCg0KLSAqKklkZW50aWZpZXI6KiogVW5pcXVlIEVtcGxveWVlIElEIGFuZCBDb21wYW55IElELg0KDQotICoqRmluYW5jaWFsczoqKiBBbm51YWwgU2FsYXJ5IChTY2FsZWQgYmV0d2VlbiAzMCBhbmQgMTUwIG1pbGxpb24pLg0KDQotICoqTWV0cmljczoqKiBQZXJmb3JtYW5jZSBhbmQgS1BJIHNjb3JlcyBnZW5lcmF0ZWQgdXNpbmcgYSBub3JtYWwgZGlzdHJpYnV0aW9uICgkTihcbXUsIFxzaWdtYV4yKSQpIHRvIG1pbWljIHJlYWwtd29ybGQgYmVsbC1jdXJ2ZSBkaXN0cmlidXRpb25zLg0KDQotICoqQ2F0ZWdvcml6YXRpb246KiogRGVwYXJ0bWVudGFsIGFzc2lnbm1lbnRzIGFuZCBUaWVyIGNsYXNzaWZpY2F0aW9uLg0KDQoNCioqMy4gSW1wbGVtZW50YXRpb24gTG9naWMqKg0KDQoqKjMuMSBEYXRhIFN5bnRoZXNpcyAmIFN0b2NoYXN0aWMgTW9kZWxpbmcqKg0KDQpXZSB1c2Ugc2V0LnNlZWQoMTIzKSBmb3IgcmVwcm9kdWNpYmlsaXR5LiBUaGUgc2FsYXJ5IGFuZCBwZXJmb3JtYW5jZSBzY29yZXMgYXJlIGdlbmVyYXRlZCB1c2luZyBybm9ybSwgdGhlbiAiY2xhbXBlZCIgdXNpbmcgcG1pbiBhbmQgcG1heCB0byBzdGF5IHdpdGhpbiBsb2dpY2FsIGJvdW5kYXJpZXMgKGUuZy4sIHNjb3JlcyBjYW5ub3QgZXhjZWVkIDEwMCkuDQoNCioqMy4yIEtQSSBUaWVyIENsYXNzaWZpY2F0aW9uKioNCg0KVGhlIHdvcmtmb3JjZSBpcyBzZWdtZW50ZWQgaW50byBmb3VyIGRpc3RpbmN0IHRpZXJzIGJhc2VkIG9uIHRoZWlyIEtQSSByZXN1bHRzIHVzaW5nIHRoZSBjdXQoKSBmdW5jdGlvbjoNCg0KLSAqKkVsaXRlOioqIDkwIOKAkyAxMDANCg0KLSAqKkhpZ2g6KiogNzUg4oCTIDg5DQoNCi0gKipNZWRpdW06KiogNTAg4oCTIDc0DQoNCi0gKipMb3c6KiogQmVsb3cgNTANCg0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBvdXQuZXh0cmE9J3N0eWxlPSJkaXNwbGF5OmJsb2NrOyBtYXJnaW4tbGVmdDphdXRvOyBtYXJnaW4tcmlnaHQ6YXV0bzsiJ30NCg0KIyAtLS0gMS4gRW52aXJvbm1lbnQgU2V0dXAgLS0tDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShzY2FsZXMpICAjIFVudHVrIGZvcm1hdHRpbmcgYW5na2ENCg0KIyAtLS0gMi4gU3ludGhldGljIERhdGEgR2VuZXJhdGlvbiAtLS0NCnNldC5zZWVkKDEyMykNCg0KY29tcGFuaWVzIDwtIGMoJ0NvbXBhbnlfQScsICdDb21wYW55X0InLCAnQ29tcGFueV9DJywgJ0NvbXBhbnlfRCcsICdDb21wYW55X0UnKQ0KZGVwYXJ0bWVudHMgPC0gYygnU2FsZXMnLCAnSVQnLCAnSFInLCAnRmluYW5jZScsICdNYXJrZXRpbmcnKQ0KDQojIEdlbmVyYXRpbmcgMjAwIGVtcGxveWVlIHJlY29yZHMNCmRmIDwtIGRhdGEuZnJhbWUoDQogIGVtcGxveWVlX2lkID0gMToyMDAsDQogIGNvbXBhbnlfaWQgPSBzYW1wbGUoY29tcGFuaWVzLCAyMDAsIHJlcGxhY2UgPSBUUlVFKSwNCiAgc2FsYXJ5ID0gcG1heChwbWluKHJub3JtKDIwMCwgNzAsIDE1KSwgMTUwKSwgMzApICogMWU2LCAgIyBTYWxhcnkgaW4gbWlsbGlvbnMNCiAgcGVyZm9ybWFuY2Vfc2NvcmUgPSBwbWF4KHBtaW4ocm5vcm0oMjAwLCA3MCwgMTUpLCAxMDApLCAwKSwNCiAgZGVwYXJ0bWVudCA9IHNhbXBsZShkZXBhcnRtZW50cywgMjAwLCByZXBsYWNlID0gVFJVRSkNCikNCg0KIyBEZXJpdmVkIExvZ2ljOiBLUEkgc2NvcmUgaXMgNzAlIG9mIHBlcmZvcm1hbmNlICsgMzAlIHJhbmRvbSBub2lzZQ0KZGYkS1BJX3Njb3JlIDwtIHBtYXgocG1pbihkZiRwZXJmb3JtYW5jZV9zY29yZSAqIDAuNyArIHJub3JtKDIwMCwgMjAsIDUpLCAxMDApLCAwKQ0KDQojIC0tLSAzLiBDYXRlZ29yaXphdGlvbiBMb2dpYyAtLS0NCmRmJEtQSV90aWVyIDwtIGN1dChkZiRLUElfc2NvcmUsIA0KICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoMCwgNTAsIDc1LCA5MCwgMTAwKSwNCiAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCdMb3cnLCAnTWVkaXVtJywgJ0hpZ2gnLCAnRWxpdGUnKSwNCiAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmxvd2VzdCA9IFRSVUUpDQoNCiMgLS0tIDQuIERhc2hib2FyZCBWaXN1YWxpemF0aW9uIC0tLQ0KZ2dwbG90KGRmLCBhZXMoeCA9IEtQSV9zY29yZSwgeSA9IHNhbGFyeSAvIDFlNiwgY29sb3IgPSBkZXBhcnRtZW50KSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAzLCBhbHBoYSA9IDAuNykgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nLCBzZSA9IEZBTFNFLCBjb2xvciA9ICdibGFjaycsIGxpbmV0eXBlID0gImRhc2hlZCIsIGxpbmV3aWR0aCA9IDEpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICdSZWxhdGlvbnNoaXAgQmV0d2VlbiBTYWxhcnkgYW5kIEtQSScsDQogICAgc3VidGl0bGUgPSAnVmlzdWFsaXphdGlvbiBvZiBQZXJmb3JtYW5jZSBDb3JyZWxhdGlvbiBhY3Jvc3MgNSBDb21wYW5pZXMnLA0KICAgIHggPSAnS1BJIFNjb3JlICgwLTEwMCknLA0KICAgIHkgPSAnQW5udWFsIFNhbGFyeSAoTWlsbGlvbnMpJywNCiAgICBjb2xvciA9ICdEZXBhcnRtZW50Jw0KICApICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGRvbGxhcl9mb3JtYXQocHJlZml4ID0gIiQiLCBzdWZmaXggPSAiTSIpKSArICAjIEZvcm1hdCBzYWxhcnkNCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAnYm9sZCcsIGhqdXN0ID0gMC41KSwNCiAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgaGp1c3QgPSAwLjUsIGNvbG9yID0gImdyYXk1MCIpLA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLA0KICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKSwNCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkNCiAgKQ0KYGBgDQoNCioqNS4gUmVzdWx0cyAmIEFuYWx5c2lzICoqDQoNCioqNS4xIFBlcmZvcm1hbmNlIENvcnJlbGF0aW9uKioNCg0KVGhlICoqU2NhdHRlciBQbG90KiogcmV2ZWFscyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gZmluYW5jaWFsIGNvbXBlbnNhdGlvbiBhbmQgb3V0cHV0LiBUaGUgYmxhY2sgdHJlbmRsaW5lIChMaW5lYXIgTW9kZWwpIGlsbHVzdHJhdGVzIHRoZSAiUGF5LWZvci1QZXJmb3JtYW5jZSIgc2xvcGUuIElmIHRoZSBsaW5lIHNsYW50cyB1cHdhcmQsIGl0IHN1Z2dlc3RzIHRoYXQgaGlnaGVyIHNhbGFyaWVzIGFyZSBlZmZlY3RpdmVseSBpbmNlbnRpdml6aW5nIGJldHRlciBLUEkgc2NvcmVzLg0KDQoqKjUuMiBEZXBhcnRtZW50YWwgRGlzdHJpYnV0aW9uKioNCg0KQnkgb2JzZXJ2aW5nIHRoZSBjb2xvci1jb2RlZCBwb2ludHMsIHdlIGNhbiBpZGVudGlmeSB3aGljaCBkZXBhcnRtZW50cyAoZS5nLiwgKipJVCoqIHZzICoqU2FsZXMqKikgY2x1c3RlciBpbiB0aGUgIkVsaXRlIiB0aWVyLiBUaGlzIHZpc3VhbGl6YXRpb24gYWxsb3dzIEhSIG1hbmFnZXJzIHRvIHF1aWNrbHkgc3BvdCB3aGljaCBkZXBhcnRtZW50cyBhcmUgb3Zlci1wZXJmb3JtaW5nIHJlbGF0aXZlIHRvIHRoZWlyIGF2ZXJhZ2Ugc2FsYXJ5Lg0KDQojIDguIFByb2dyYW0gODogQXV0b21hdGVkIENvcnBvcmF0ZSBSZXBvcnRpbmcgRW5naW5lDQoNCioqOC4xIExvZ2ljICYgQXV0b21hdGlvbiBXb3JrZmxvdyoqDQoNClRoZSBvYmplY3RpdmUgb2YgdGhpcyBtb2R1bGUgaXMgdG8gZWxpbWluYXRlIG1hbnVhbCByZXBvcnRpbmcgYnkgdXNpbmcgYSBGdW5jdGlvbmFsIExvb3AgdG8gZmlsdGVyIGRhdGEgYW5kIGV4cG9ydCBpbmRpdmlkdWFsIHN1bW1hcmllcyBmb3IgZWFjaCBjb21wYW55IGluIHRoZSBkYXRhc2V0Lg0KDQotICoqSXRlcmF0aXZlIEZpbHRlcmluZzoqKiBUaGUgcHJvZ3JhbSBpZGVudGlmaWVzIGFsbCB1bmlxdWUgY29tcGFueV9pZCB2YWx1ZXMuIEl0IHRoZW4gbG9vcHMgdGhyb3VnaCB0aGlzIGxpc3QsIGNyZWF0aW5nIGEgc3Vic2V0IG9mIGRhdGEgZm9yIG9uZSBjb21wYW55IGF0IGEgdGltZS4NCg0KLSAqKk1ldHJpYyBDYWxjdWxhdGlvbjoqKiBGb3IgZXZlcnkgY29tcGFueSwgdGhlIGVuZ2luZSBjYWxjdWxhdGVzIGludGVybmFsIEtQSXMgc3VjaCBhcyAiQXZlcmFnZSBTYWxhcnksIiAiVG9wIFBlcmZvcm1lciBDb3VudCwiIGFuZCAiRGVwYXJ0bWVudGFsIFN0cmVuZ3RoLiINCg0KLSAqKk11bHRpLUZvcm1hdCBFeHBvcnQ6KiogVG8gc2ltdWxhdGUgYSByZWFsLXdvcmxkIHdvcmtmbG93LCB0aGUgcHJvZ3JhbSBpcyBkZXNpZ25lZCB0byBzYXZlIHRoZXNlIHN1bW1hcmllcyBhcyAqKkNTViBmaWxlcyoqIChmb3IgZGF0YSB0ZWFtcykgYW5kIHByaW50ICoqRm9ybWF0dGVkIFN1bW1hcmllcyoqIChmb3IgbWFuYWdlbWVudCkuDQoNCioqOC4yIFIgSW1wbGVtZW50YXRpb24gKEF1dG9tYXRpb24gRW5naW5lKSoqDQoNCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgb3V0LmV4dHJhPSdzdHlsZT0iZGlzcGxheTpibG9jazsgbWFyZ2luLWxlZnQ6YXV0bzsgbWFyZ2luLXJpZ2h0OmF1dG87Iid9DQoNCiMgLS0tIDEuIEF1dG9tYXRlZCBSZXBvcnRpbmcgRnVuY3Rpb24gLS0tDQpnZW5lcmF0ZV9jb21wYW55X3JlcG9ydHMgPC0gZnVuY3Rpb24obWFzdGVyX2RmKSB7DQogIA0KICAjIExvYWQgbGlicmFyeSBkaSBkYWxhbSBmdW5nc2kgdW50dWsgbWVtYXN0aWthbiBkZXBlbmRlbnNpIHRlcnNlZGlhDQogIGlmICghcmVxdWlyZSgiZHBseXIiKSkgaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KICBsaWJyYXJ5KGRwbHlyKQ0KICANCiAgIyBWYWxpZGFzaTogQ2VrIGFwYWthaCBrb2xvbSB5YW5nIGRpYnV0dWhrYW4gYWRhDQogIHJlcXVpcmVkX2NvbHMgPC0gYygiY29tcGFueV9pZCIsICJLUElfc2NvcmUiLCAiS1BJX3RpZXIiKQ0KICBpZiAoIWFsbChyZXF1aXJlZF9jb2xzICVpbiUgY29sbmFtZXMobWFzdGVyX2RmKSkpIHsNCiAgICBzdG9wKCJFcnJvcjogS29sb20geWFuZyBkaWJ1dHVoa2FuIHRpZGFrIGRpdGVtdWthbiBkYWxhbSBkYXRhZnJhbWUhIikNCiAgfQ0KICANCiAgIyBTdGVwIDE6IE1lbWJ1YXQgZm9sZGVyIG91dHB1dCBhZ2FyIGZpbGUgcmFwaQ0KICBvdXRwdXRfZGlyIDwtICJDb21wYW55X1JlcG9ydHMiDQogIGlmICghZGlyLmV4aXN0cyhvdXRwdXRfZGlyKSkgew0KICAgIGRpci5jcmVhdGUob3V0cHV0X2RpciwgcmVjdXJzaXZlID0gVFJVRSkNCiAgfQ0KICANCiAgIyBTdGVwIDI6IEFtYmlsIGRhZnRhciB1bmlrIHBlcnVzYWhhYW4NCiAgdW5pcXVlX2NvbXBhbmllcyA8LSB1bmlxdWUobWFzdGVyX2RmJGNvbXBhbnlfaWQpDQogIA0KICBjYXQoIj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIikNCiAgY2F0KCIgIFNUQVJUSU5HIEFVVE9NQVRFRCBSRVBPUlQgR0VORVJBVElPTiAgICANCiIpDQogIGNhdCgiPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoiKQ0KICANCiAgIyBTdGVwIDM6IExvb3AgbWVsYWx1aSBzZXRpYXAgcGVydXNhaGFhbg0KICBmb3IgKGNvbXAgaW4gdW5pcXVlX2NvbXBhbmllcykgew0KICAgIA0KICAgICMgRmlsdGVyIGRhdGEgc3Blc2lmaWsgcGVydXNhaGFhbg0KICAgIGNvbXBfZGF0YSA8LSBtYXN0ZXJfZGYgJT4lIGZpbHRlcihjb21wYW55X2lkID09IGNvbXApDQogICAgDQogICAgIyBQZXJoaXR1bmdhbiBNZXRyaWsgTG9rYWwNCiAgICB0b3RhbF9lbXAgICAgICA8LSBucm93KGNvbXBfZGF0YSkNCiAgICBhdmdfa3BpICAgICAgICA8LSBtZWFuKGNvbXBfZGF0YSRLUElfc2NvcmUsIG5hLnJtID0gVFJVRSkNCiAgICB0b3BfcGVyZm9ybWVycyA8LSBzdW0oY29tcF9kYXRhJEtQSV90aWVyID09ICJFbGl0ZSIsIG5hLnJtID0gVFJVRSkNCiAgICANCiAgICAjIFBlbWJ1YXRhbiBOYW1hIEZpbGUgKENsZWFuaW5nOiBzcGFzaSBqYWRpIHVuZGVyc2NvcmUsIGxvd2VyY2FzZSkNCiAgICBjbGVhbl9uYW1lIDwtIGdzdWIoIiAiLCAiXyIsIHRvbG93ZXIoY29tcCkpDQogICAgZmlsZV9wYXRoICA8LSBmaWxlLnBhdGgob3V0cHV0X2RpciwgcGFzdGUwKGNsZWFuX25hbWUsICJfU3VtbWFyeS5jc3YiKSkNCiAgICANCiAgICAjIEVrc3BvciBEYXRhIGtlIENTViAoUmVhbCBFeHBvcnQpDQogICAgd3JpdGUuY3N2KGNvbXBfZGF0YSwgZmlsZV9wYXRoLCByb3cubmFtZXMgPSBGQUxTRSkNCiAgICANCiAgICAjIENldGFrIFN1bW1hcnkga2UgS29uc29sIHVudHVrIFZlcmlmaWthc2kNCiAgICBjYXQocGFzdGUwKCJbUFJPQ0VTU0lOR106ICIsIGNvbXAsICINCiIpKQ0KICAgIGNhdChwYXN0ZTAoIiAgLSBXb3JrZm9yY2UgU2l6ZSAgOiAiLCB0b3RhbF9lbXAsICINCiIpKQ0KICAgIGNhdChwYXN0ZTAoIiAgLSBNZWFuIEtQSSBTY29yZSAgOiAiLCByb3VuZChhdmdfa3BpLCAyKSwgIg0KIikpDQogICAgY2F0KHBhc3RlMCgiICAtIEVsaXRlIFRhbGVudCAgICA6ICIsIHRvcF9wZXJmb3JtZXJzLCAiDQoiKSkNCiAgICBjYXQocGFzdGUwKCIgIC0gRmlsZSBTYXZlZCB0byAgIDogIiwgZmlsZV9wYXRoLCAiDQoiKSkNCiAgICBjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIikNCiAgfQ0KICANCiAgY2F0KCJTVUNDRVNTOiBBbGwgcmVwb3J0cyBnZW5lcmF0ZWQgaW4gJyIsIG91dHB1dF9kaXIsICInIGZvbGRlci4NCiIpDQp9DQoNCiMgLS0tIDIuIEVrc2VrdXNpIC0tLQ0KIyBNZW5qYWxhbmthbiBmdW5nc2kgbWVuZ2d1bmFrYW4gZGF0YWZyYW1lICdkZicgeWFuZyBzdWRhaCBraXRhIGJ1YXQgc2ViZWx1bW55YQ0KZ2VuZXJhdGVfY29tcGFueV9yZXBvcnRzKGRmKQ0KYGBgDQoNCg0KKio5LiBTdW1tYXJ5IG9mIEZpbmRpbmdzICYgQ29uY2x1c2lvbioqDQoNClRocm91Z2ggdGhlc2UgZWlnaHQgcHJvZ3JhbW1pbmcgaW1wbGVtZW50YXRpb25zLCB3ZSBoYXZlIHRyYW5zaXRpb25lZCBmcm9tIGJhc2ljIHN5bnRheCB0byBhICoqZnVsbC1zY2FsZSBEYXRhIFNjaWVuY2UgcGlwZWxpbmUuKioNCg0KKipLZXkgVGFrZWF3YXlzOioqDQoNCioqMS5FZmZpY2llbmN5OioqIFVzaW5nIGxvb3BzIGFuZCBmdW5jdGlvbnMgcmVkdWNlcyBjb2RlIHJlZHVuZGFuY3kgYW5kIGFsbG93cyBmb3Igc2NhbGluZyBmcm9tIDEgY29tcGFueSB0byAxLDAwMCB3aXRoIHplcm8gZXh0cmEgbWFudWFsIGVmZm9ydC4NCg0KKioyLlN0b2NoYXN0aWMgUG93ZXI6KiogTW9udGUgQ2FybG8gc2ltdWxhdGlvbnMgcHJvdmUgdGhhdCByYW5kb21uZXNzIGNhbiBiZSBhIHRvb2wgZm9yIG1hdGhlbWF0aWNhbCBwcmVjaXNpb24uDQoNCioqMy5WaXN1YWxpemF0aW9uOioqIERhdGEgaXMgb25seSBhcyBnb29kIGFzIGl0IGlzIGNvbW11bmljYXRlZDsgdXNpbmcgZ2dwbG90MiBlbnN1cmVzIHRoYXQgY29tcGxleCBzdGFuZGFyZGl6YXRpb25zIChsaWtlIFotU2NvcmVzKSBhcmUgaW50dWl0aXZlIGZvciBub24tdGVjaG5pY2FsIHN0YWtlaG9sZGVycy4NCg0KKipGaW5hbCBBY2FkZW1pYyBSZWZsZWN0aW9uKioNCg0KVGhpcyBwcmFjdGljdW0gY29uZmlybXMgdGhhdCB0aGUgY29yZSBvZiBEYXRhIFNjaWVuY2UgUHJvZ3JhbW1pbmcgaXMgbm90IGp1c3Qgd3JpdGluZyBjb2RlLCBidXQgKiphcmNoaXRlY3RpbmcgbG9naWMqKiB0aGF0IGNhbiBhZGFwdCB0byBjaGFuZ2luZyBkYXRhIGVudmlyb25tZW50cy4gQnkgbWFzdGVyaW5nIHRoZXNlIGF1dG9tYXRlZCB3b3JrZmxvd3MsIHdlIGFyZSBwcmVwYXJlZCB0byBoYW5kbGUgaGlnaC12ZWxvY2l0eSBkYXRhIGNoYWxsZW5nZXMgaW4gYSBwcm9mZXNzaW9uYWwgc2V0dGluZy4NCg0KDQo=