Section 1: Core Fundamentals & Data Structures

Q1.1 Multi-Type Data Structure Creation

Create a nested data structure named company_data containing:

  • A vector of employee IDs
    • 101, 102, 103, 104
  • A list with two elements:
    • departments = c("HR", "Tech", "Sales")
    • active_projects = 5
  • A matrix representing quarterly sales (in thousands) for 3 products over 4 quarters
    • Values (filled by row):
      45, 67, 89, 34, 56, 78, 23, 45, 67, 89, 12, 34
  • A data frame with columns:
    • Employee
    • Salary
    • Years_Service
    • For 3 employees
company_data <- list(
  employee_ids = c(101, 102, 103, 104),
  company_info = list(
    departments = c("HR", "Tech", "Sales"),
    active_projects = 5
  ),
  quarterly_sales = matrix(
    c(45, 67, 89, 34, 56, 78, 23, 45, 67, 89, 12, 34),
    nrow = 3,
    ncol = 4,
    byrow = TRUE,
    dimnames = list(
      c("Product_A", "Product_B", "Product_C"),
      c("Q1", "Q2", "Q3", "Q4")
    )
  ),
  employees = data.frame(
    Employee = c("Alice", "Bob", "Charlie"),
    Salary = c(75000, 82000, 68000),
    Years_Service = c(5, 3, 7)
  )
)
company_data
$employee_ids
[1] 101 102 103 104

$company_info
$company_info$departments
[1] "HR"    "Tech"  "Sales"

$company_info$active_projects
[1] 5


$quarterly_sales
          Q1 Q2 Q3 Q4
Product_A 45 67 89 34
Product_B 56 78 23 45
Product_C 67 89 12 34

$employees
NA

Q1.2 Advanced Vector Operations

Given vectors:

  • x = c(12, NA, 25, 18, NA, 32, 45, NA)
  • y = c(5, 8, 12, 6, 9, NA, 15, 7)

Write code that:

  1. Removes NA values from both vectors

  2. Creates a new vector z

    • Each element is calculated as:
      \[ z_i = (x_i \times 2) + (y_i / 2) \]
  3. Calculates the weighted mean of z

    • Weights: w = c(0.1, 0.2, 0.15, 0.25, 0.3)
  4. Returns the positions

    • Where z > 40
# Given vectors
x <- c(12, NA, 25, 18, NA, 32, 45, NA)
y <- c(5, 8, 12, 6, 9, NA, 15, 7)

# 1. Remove NA values (keep only complete paired observations)
complete_cases <- complete.cases(x, y) #TRUE FALSE  TRUE  TRUE FALSE FALSE  TRUE FALSE
x_clean <- x[complete_cases]
y_clean <- y[complete_cases]

# 2. Create vector z: z_i = (x_i * 2) + (y_i / 2)
z <- (x_clean * 2) + (y_clean / 2)

# Results after cleaning (for reference)
# x_clean: 12, 25, 18, 45
# y_clean: 5,  12, 6,  15
# z:       26.5, 56, 39, 97.5   (length = 4)

# 3. Calculate weighted mean of z
# Note: There are only 4 valid observations after NA removal,
# but 5 weights are provided. To resolve this while keeping the spirit
# of the question, use the first 4 weights (a common practical approach
# when the number of observations varies).
w <- c(0.1, 0.2, 0.15, 0.25, 0.3)  # full weights
weighted_mean_z <- weighted.mean(z, w = w[1:length(z)])  # use first 4: 0.1, 0.2, 0.15, 0.25

# Alternative (if equal weights are acceptable):
# weighted_mean_z <- mean(z)

# 4. Find positions in z where z > 40 (positions in the cleaned vector z)
positions <- which(z > 40)

# Output results
z
[1] 26.5 56.0 39.0 97.5
# [1] 26.5 56.0 39.0 97.5

weighted_mean_z
[1] 62.96429
# [1] 59.225   (calculated as (26.5*0.1) + (56*0.2) + (39*0.15) + (97.5*0.25))

positions
[1] 2 4
# [1] 2 4

Section 2: Advanced Data Manipulation

Q2.1 Complex dplyr Pipeline

Using the starwars dataset from dplyr:

  1. Filter characters with mass between 50 and 100 kg

  2. Create a new column bmi_category based on:

    • If mass / height^2 × 10000 > 25“Overweight”
    • If between 18.5 and 25“Normal”
    • Else → “Underweight”
  3. Group the data by

    • species
    • bmi_category
  4. Calculate for each group

    • Count of characters
    • Average mass
    • Average height
  5. Arrange the result

    • First by species
    • Then by descending count
  6. Keep only species

    • With at least 2 members in the dataset
library(dplyr)

starwars_analysis <- starwars %>%
  filter(mass >= 50 & mass <= 100, !is.na(height), !is.na(mass)) %>%
  mutate(
    bmi = mass / (height/100)^2,
    bmi_category = case_when(
      bmi > 25 ~ "Overweight",
      bmi >= 18.5 & bmi <= 25 ~ "Normal",
      TRUE ~ "Underweight"
    )
  ) %>%
  group_by(species, bmi_category) %>%
  summarise(
    count = n(),
    avg_mass = mean(mass, na.rm = TRUE),
    avg_height = mean(height, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(species, desc(count)) %>%
  group_by(species) %>%
  filter(sum(count) >= 2) %>%
  ungroup()
starwars_analysis

Section 2: Statistical Analysis & Probability

Q2.1 Custom Statistical Functions

Task: Create a function distribution_analyzer that performs comprehensive statistical analysis.

Requirements:

  1. Inputs:
    • A numeric vector
    • An optional weights vector
  2. Outputs: Returns a list containing:
    • Mean
    • Median
    • Mode (implement your own mode function)
    • Standard deviation
    • Variance
    • Skewness and kurtosis (use the moments package or calculate manually)
    • 95% confidence interval for the mean
    • Shapiro-Wilk test for normality
  3. Additional Features:
    • If weights are provided, calculate weighted statistics.
    • Include error handling for invalid inputs (e.g., non-numeric vectors, mismatched weights, empty vectors).

Notes:

  • Ensure that all calculations are robust and handle missing values (NA) appropriately.
  • The function should return a well-structured list for easy interpretation.
distribution_analyzer <- function(x, weights = NULL, na.rm = TRUE) {
  
  # Error handling
  if(!is.numeric(x)) stop("Input must be numeric")
  if(na.rm) {
    x <- x[!is.na(x)]
    if(!is.null(weights)) weights <- weights[!is.na(x)]
  }
  
  # Custom mode function
  compute_mode <- function(v) {
    uniqv <- unique(v)
    uniqv[which.max(tabulate(match(v, uniqv)))]
  }
  
  # Basic statistics
  results <- list()
  results$mean <- if(is.null(weights)) mean(x) else weighted.mean(x, weights)
  results$median <- median(x)
  results$mode <- compute_mode(x)
  results$sd <- if(is.null(weights)) sd(x) else sqrt(Hmisc::wtd.var(x, weights))
  results$var <- results$sd^2
  
  # Higher moments (manual calculation)
  n <- length(x)
  centered <- x - results$mean
  results$skewness <- (sum(centered^3)/n) / (results$sd^3)
  results$kurtosis <- (sum(centered^4)/n) / (results$sd^4)
  
  # Confidence interval
  se <- results$sd / sqrt(n)
  t_critical <- qt(0.975, df = n-1)
  results$ci_lower <- results$mean - t_critical * se
  results$ci_upper <- results$mean + t_critical * se
  
  # Normality test
  results$shapiro_test <- shapiro.test(x)
  
  return(results)
}



# Sample data
x <- c(10, 20, 30, 20, 10)
weights <- c(1, 2, 3, 2, 1)

# Run the function with weights
result_weighted <- distribution_analyzer(x, weights = weights)

# View results
result_weighted
$mean
[1] 21.11111

$median
[1] 20

$mode
[1] 10

$sd
[1] 7.81736

$var
[1] 61.11111

$skewness
[1] -0.8556743

$kurtosis
[1] 1.966983

$ci_lower
[1] 11.40458

$ci_upper
[1] 30.81765

$shapiro_test

    Shapiro-Wilk normality test

data:  x
W = 0.88104, p-value = 0.314

Q2.2 Probability Simulation

Task

Simulate a casino game:

  • You roll two dice.
  • Win $10 if the sum is 7 or 11.
  • Lose $5 otherwise.

Steps

  1. Run 10,000 simulations of the game.
  2. Calculate:
    • Expected value
    • Probability of winning
    • 95% confidence interval for expected value
  3. Create a function that allows:
    • Variable bet amounts
    • Variable payout ratios
simulate_dice_game <- function(n_sims = 10000, bet = 5, win_amount = 10) {
  set.seed(123)
  
  results <- replicate(n_sims, {
    s <- sum(sample(1:6, 2, replace = TRUE))
    if (s %in% c(7, 11)) win_amount else -bet
  })
  
  # Estimates
  EV <- mean(results)
  p_win <- mean(results > 0)
  
  # 95% CI (Normal / CLT)
  se <- sd(results) / sqrt(n_sims)
  ci <- EV + c(-1, 1) * qnorm(0.975) * se   
  
  list(
    expected_value = EV,
    win_probability = p_win,
    confidence_interval = ci
  )
}

# Run simulation
game_results <- simulate_dice_game(
  n_sims = 10000,
  bet = 5,
  win_amount = 10
)

game_results
$expected_value
[1] -1.7645

$win_probability
[1] 0.2157

$confidence_interval
[1] -1.885428 -1.643572

Section 3: Advanced Visualization

Q3.1 Multi-Panel Diagnostic Plot

Create a comprehensive diagnostic plot for a linear model with the following panels:

model <- lm(mpg ~ wt + hp + qsec, data = mtcars)

  • Top-left: Residuals vs Fitted values
  • Top-right: Q-Q plot of residuals
  • Bottom-left: Scale-Location plot
  • Bottom-right: Residuals vs Leverage with Cook’s distance

Requirements:

  • Add appropriate titles for each panel.
  • Include reference lines where necessary (e.g., horizontal line at 0 for Residuals vs Fitted).
  • Use color coding to highlight points with high influence (e.g., large Cook’s distance).
library(ggplot2)
library(gridExtra)

# Fit model
model <- lm(mpg ~ wt + hp + qsec, data = mtcars)
# Prepare data
diagnostic_data <- data.frame(
  fitted = fitted(model),
  residuals = residuals(model),
  sqrt_abs_resid = sqrt(abs(residuals(model))),
  leverage = hatvalues(model),
  cooks_d = cooks.distance(model)
)

# 1. Residuals vs Fitted
p1 <- ggplot(diagnostic_data, aes(x = fitted, y = residuals)) +
  geom_point(alpha = 0.7) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "red") +
  geom_smooth(method = "loess", se = FALSE, color = "blue") +
  labs(title = "Residuals vs Fitted", x = "Fitted Values", y = "Residuals")

# 2. Q-Q Plot
p2 <- ggplot(diagnostic_data, aes(sample = residuals)) +
  stat_qq() + stat_qq_line(color = "red") +
  labs(title = "Normal Q-Q", x = "Theoretical Quantiles", y = "Sample Quantiles")

# 3. Scale-Location
p3 <- ggplot(diagnostic_data, aes(x = fitted, y = sqrt_abs_resid)) +
  geom_point(alpha = 0.7) +
  geom_smooth(method = "loess", se = FALSE, color = "blue") +
  labs(title = "Scale-Location", x = "Fitted Values", y = "√|Standardized Residuals|")

# 4. Residuals vs Leverage
p4 <- ggplot(diagnostic_data, aes(x = leverage, y = residuals)) +
  geom_point(aes(size = cooks_d, color = cooks_d > 0.5), alpha = 0.7) +
  scale_color_manual(values = c("black", "red")) +
  geom_hline(yintercept = 0, linetype = "dashed") +
  labs(title = "Residuals vs Leverage", x = "Leverage", y = "Residuals") +
  guides(color = "none")

# Arrange plots
grid.arrange(p1, p2, p3, p4, ncol = 2)

Section 4: Machine Learning Implementation

Time: 50 minutes

Q4.1 Custom k-NN with Cross-Validation

Tasks:

  1. Implement a k-NN function from scratch (without using caret or similar packages) that can handle both:

    • Classification
    • Regression
  2. Implement k-fold cross-validation to select the optimal value of k.

  3. Compute performance metrics:

    • Accuracy for classification
    • RMSE for regression
  4. Create a visualization of k versus performance to identify the best k.

library(class)
library(dplyr)

custom_knn <- function(train_data, test_data, train_labels, k, type = "classification") {
  predictions <- knn(
    train = as.matrix(train_data),
    test = as.matrix(test_data),
    cl = train_labels,
    k = k
  )
  
  if(type == "regression") {
    # For regression, convert factor predictions back to numeric
    predictions <- as.numeric(as.character(predictions))
  }
  
  return(predictions)
}

kfold_cv_knn <- function(data, target, k_values = 1:20, n_folds = 5, type = "classification") {
  
  # Create folds
  n <- nrow(data)
  folds <- sample(rep(1:n_folds, length.out = n))
  
  results <- data.frame()
  
  for(k in k_values) {
    fold_metrics <- numeric(n_folds)
    
    for(fold in 1:n_folds) {
      # Split data
      train_idx <- folds != fold
      test_idx <- folds == fold
      
      # Make prediction
      preds <- custom_knn(
        data[train_idx, ],
        data[test_idx, ],
        target[train_idx],
        k = k,
        type = type
      )
      
      # Calculate performance
      if(type == "classification") {
        # Accuracy
        fold_metrics[fold] <- sum(preds == target[test_idx]) / length(preds)
      } else {
        # RMSE for regression
        fold_metrics[fold] <- sqrt(mean((preds - target[test_idx])^2))
      }
    }
    
    results <- rbind(results, data.frame(
      k = k,
      mean_performance = mean(fold_metrics),
      sd_performance = sd(fold_metrics)
    ))
  }
  
  # Find best k
  if(type == "classification") {
    best_k <- results$k[which.max(results$mean_performance)]
  } else {
    best_k <- results$k[which.min(results$mean_performance)]
  }
  
  return(list(
    cv_results = results,
    best_k = best_k
  ))
}

# Example usage
data(iris)
iris_features <- iris[, 1:4]
iris_species <- iris$Species

cv_results <- kfold_cv_knn(iris_features, iris_species, k_values = 1:15, type = "classification")

# Plot results
library(ggplot2)
ggplot(cv_results$cv_results, aes(x = k, y = mean_performance)) +
  geom_line() +
  geom_ribbon(aes(ymin = mean_performance - sd_performance,
                  ymax = mean_performance + sd_performance),
              alpha = 0.2) +
  geom_vline(xintercept = cv_results$best_k, linetype = "dashed", color = "red") +
  labs(title = "k-NN Cross-Validation Results", x = "k", y = "Accuracy") +
  theme_minimal()

library(class)
library(dplyr)
library(ggplot2)
library(MASS)

data(Boston)

boston_features <- Boston[, -14]
boston_target <- Boston$medv

boston_features_scaled <- as.data.frame(scale(boston_features))

cv_results_reg <- kfold_cv_knn(
  data = boston_features_scaled,
  target = boston_target,
  k_values = 1:20,
  n_folds = 5,
  type = "regression"
)

ggplot(cv_results_reg$cv_results, aes(x = k, y = mean_performance)) +
  geom_line(color = "blue") +
  geom_ribbon(aes(ymin = mean_performance - sd_performance,
                  ymax = mean_performance + sd_performance),
              alpha = 0.2, fill = "blue") +
  geom_vline(xintercept = cv_results_reg$best_k, linetype = "dashed", color = "red") +
  labs(title = "k-NN CV Results (Boston Housing Regression)",
       x = "k",
       y = "Mean RMSE",
       subtitle = paste("Best k =", cv_results_reg$best_k)) +
  theme_minimal()

library(class)
library(dplyr)
library(ggplot2)

data(mtcars)

mtcars_features <- mtcars[, c("mpg", "disp", "hp", "wt", "qsec")]
mtcars_target <- as.factor(mtcars$cyl)

mtcars_features_scaled <- as.data.frame(scale(mtcars_features))

cv_results_class <- kfold_cv_knn(
  data = mtcars_features_scaled,
  target = mtcars_target,
  k_values = 1:10,
  n_folds = 5,
  type = "classification"
)

ggplot(cv_results_class$cv_results, aes(x = k, y = mean_performance)) +
  geom_line(color = "darkgreen") +
  geom_ribbon(aes(ymin = mean_performance - sd_performance,
                  ymax = mean_performance + sd_performance),
              alpha = 0.2, fill = "green") +
  geom_vline(xintercept = cv_results_class$best_k, linetype = "dashed", color = "red") +
  labs(title = "k-NN CV Results (mtcars Cylinder Classification)",
       x = "k",
       y = "Mean Accuracy",
       subtitle = paste("Best k =", cv_results_class$best_k)) +
  theme_minimal()

Section 5: Complete Project

Time: 60 minutes

Q5: End-to-End Data Analysis Project

Dataset: diamonds from ggplot2

1. Data Preparation

  • Handle outliers in price using the IQR method.
  • Create new features:
    • price_per_carat = price ÷ carat
    • volume = x × y × z
    • depth_category (categorize depth)
  • Convert appropriate columns to factors.

2. Exploratory Analysis

  • Visualize correlation matrix.
  • Plot distribution of price by cut, color, clarity using faceting.
  • Create a 3D scatterplot of carat, price, and depth (use plotly if available).

3. Statistical Modeling

  • Build a linear model predicting log(price).
  • Build a random forest model (ranger or randomForest package).
  • Compare models using cross-validated RMSE.

4. Production Output

  • Create an R Markdown report summarizing all findings.
  • Save models as .rds files.
  • Implement a prediction function that takes new data as input.
library(tidyverse)
library(caret)
library(ranger)

# 1. Data Preparation
diamonds_clean <- diamonds %>%
  mutate(
    price_per_carat = price / carat,
    volume = x * y * z,
    depth_category = cut(depth, breaks = 5, labels = c("Very Shallow", "Shallow", "Medium", "Deep", "Very Deep"))
  ) %>%
  filter(price <= quantile(price, 0.99) & price >= quantile(price, 0.01))

# 2. EDA - Correlation heatmap
library(corrplot)
numeric_cols <- diamonds_clean %>% 
  select(where(is.numeric)) %>%
  select(-x, -y, -z)  # Remove dimension columns
cor_matrix <- cor(numeric_cols)
corrplot(cor_matrix, method = "color", type = "upper")

# 3. Modeling setup
set.seed(123)
diamonds_clean$log_price <- log(diamonds_clean$price)

# Train-test split
train_idx <- createDataPartition(diamonds_clean$log_price, p = 0.7, list = FALSE)
train_data <- diamonds_clean[train_idx, ]
test_data <- diamonds_clean[-train_idx, ]

# Linear model
lm_model <- lm(log_price ~ carat + cut + color + clarity + depth + table, 
               data = train_data)

# Random Forest
rf_model <- ranger(
  log_price ~ carat + cut + color + clarity + depth + table,
  data = train_data,
  num.trees = 500,
  mtry = 3,
  importance = "impurity"
)

# Cross-validation comparison
train_control <- trainControl(method = "cv", number = 5)

# Linear model CV
lm_cv <- train(
  log_price ~ carat + cut + color + clarity + depth + table,
  data = train_data,
  method = "lm",
  trControl = train_control
)

# Random Forest CV
rf_cv <- train(
  log_price ~ carat + cut + color + clarity + depth + table,
  data = train_data,
  method = "ranger",
  trControl = train_control,
  tuneGrid = expand.grid(
    mtry = c(2, 3, 4),
    splitrule = "variance",
    min.node.size = 5
  )
)
Growing trees.. Progress: 65%. Estimated remaining time: 16 seconds.
# Compare models
results <- resamples(list(
  Linear = lm_cv,
  RandomForest = rf_cv
))
summary(results)

Call:
summary.resamples(object = results)

Models: Linear, RandomForest 
Number of resamples: 5 

MAE 
                  Min.   1st Qu.    Median      Mean   3rd Qu.      Max. NA's
Linear       0.2550032 0.2556220 0.2559445 0.2565396 0.2577014 0.2584267    0
RandomForest 0.1407062 0.1423713 0.1483277 0.1460058 0.1485131 0.1501109    0

RMSE 
                  Min.   1st Qu.    Median      Mean   3rd Qu.      Max. NA's
Linear       0.3192101 0.3220438 0.3251787 0.3262524 0.3262542 0.3385752    0
RandomForest 0.1847776 0.1887349 0.1947069 0.1929534 0.1974694 0.1990780    0

Rsquared 
                  Min.   1st Qu.    Median      Mean   3rd Qu.      Max. NA's
Linear       0.8829245 0.8917176 0.8919805 0.8909221 0.8921635 0.8958243    0
RandomForest 0.9725903 0.9730766 0.9733414 0.9738127 0.9742229 0.9758326    0
# 4. Production outputs
# Save models
saveRDS(lm_model, "diamonds_lm_model.rds")
saveRDS(rf_model, "diamonds_rf_model.rds")

# Prediction function
predict_diamond_price <- function(new_data, model_type = "rf") {
  if(model_type == "rf") {
    model <- readRDS("diamonds_rf_model.rds")
    pred_log <- predict(model, new_data)$predictions
  } else {
    model <- readRDS("diamonds_lm_model.rds")
    pred_log <- predict(model, new_data)
  }
  
  # Convert back from log scale
  price_pred <- exp(pred_log)
  
  return(data.frame(
    predicted_price = price_pred,
    predicted_log_price = pred_log
  ))
}
LS0tDQp0aXRsZTogIjJKYW5fUW5BKGx2bC0yKSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQojIFNlY3Rpb24gMTogQ29yZSBGdW5kYW1lbnRhbHMgJiBEYXRhIFN0cnVjdHVyZXMNCg0KDQojIyBRMS4xIE11bHRpLVR5cGUgRGF0YSBTdHJ1Y3R1cmUgQ3JlYXRpb24NCg0KQ3JlYXRlIGEgbmVzdGVkIGRhdGEgc3RydWN0dXJlIG5hbWVkICoqYGNvbXBhbnlfZGF0YWAqKiBjb250YWluaW5nOg0KDQotICoqQSB2ZWN0b3Igb2YgZW1wbG95ZWUgSURzKioNCiAgLSBgMTAxLCAxMDIsIDEwMywgMTA0YA0KDQotICoqQSBsaXN0Kiogd2l0aCB0d28gZWxlbWVudHM6DQogIC0gYGRlcGFydG1lbnRzID0gYygiSFIiLCAiVGVjaCIsICJTYWxlcyIpYA0KICAtIGBhY3RpdmVfcHJvamVjdHMgPSA1YA0KDQotICoqQSBtYXRyaXgqKiByZXByZXNlbnRpbmcgcXVhcnRlcmx5IHNhbGVzIChpbiB0aG91c2FuZHMpIGZvciAqKjMgcHJvZHVjdHMgb3ZlciA0IHF1YXJ0ZXJzKiogIA0KICAtIFZhbHVlcyAoZmlsbGVkIGJ5IHJvdyk6ICANCiAgICBgNDUsIDY3LCA4OSwgMzQsIDU2LCA3OCwgMjMsIDQ1LCA2NywgODksIDEyLCAzNGANCg0KLSAqKkEgZGF0YSBmcmFtZSoqIHdpdGggY29sdW1uczoNCiAgLSBgRW1wbG95ZWVgDQogIC0gYFNhbGFyeWANCiAgLSBgWWVhcnNfU2VydmljZWAgIA0KICAtIEZvciAqKjMgZW1wbG95ZWVzKioNCmBgYHtyfQ0KY29tcGFueV9kYXRhIDwtIGxpc3QoDQogIGVtcGxveWVlX2lkcyA9IGMoMTAxLCAxMDIsIDEwMywgMTA0KSwNCiAgY29tcGFueV9pbmZvID0gbGlzdCgNCiAgICBkZXBhcnRtZW50cyA9IGMoIkhSIiwgIlRlY2giLCAiU2FsZXMiKSwNCiAgICBhY3RpdmVfcHJvamVjdHMgPSA1DQogICksDQogIHF1YXJ0ZXJseV9zYWxlcyA9IG1hdHJpeCgNCiAgICBjKDQ1LCA2NywgODksIDM0LCA1NiwgNzgsIDIzLCA0NSwgNjcsIDg5LCAxMiwgMzQpLA0KICAgIG5yb3cgPSAzLA0KICAgIG5jb2wgPSA0LA0KICAgIGJ5cm93ID0gVFJVRSwNCiAgICBkaW1uYW1lcyA9IGxpc3QoDQogICAgICBjKCJQcm9kdWN0X0EiLCAiUHJvZHVjdF9CIiwgIlByb2R1Y3RfQyIpLA0KICAgICAgYygiUTEiLCAiUTIiLCAiUTMiLCAiUTQiKQ0KICAgICkNCiAgKSwNCiAgZW1wbG95ZWVzID0gZGF0YS5mcmFtZSgNCiAgICBFbXBsb3llZSA9IGMoIkFsaWNlIiwgIkJvYiIsICJDaGFybGllIiksDQogICAgU2FsYXJ5ID0gYyg3NTAwMCwgODIwMDAsIDY4MDAwKSwNCiAgICBZZWFyc19TZXJ2aWNlID0gYyg1LCAzLCA3KQ0KICApDQopDQpjb21wYW55X2RhdGENCmBgYA0KIyMgUTEuMiBBZHZhbmNlZCBWZWN0b3IgT3BlcmF0aW9ucw0KDQpHaXZlbiB2ZWN0b3JzOg0KDQotIGB4ID0gYygxMiwgTkEsIDI1LCAxOCwgTkEsIDMyLCA0NSwgTkEpYA0KLSBgeSA9IGMoNSwgOCwgMTIsIDYsIDksIE5BLCAxNSwgNylgDQoNCldyaXRlIGNvZGUgdGhhdDoNCg0KMS4gKipSZW1vdmVzIGBOQWAgdmFsdWVzKiogZnJvbSBib3RoIHZlY3RvcnMNCg0KMi4gKipDcmVhdGVzIGEgbmV3IHZlY3RvciBgemAqKg0KICAgLSBFYWNoIGVsZW1lbnQgaXMgY2FsY3VsYXRlZCBhczogIA0KICAgICBcWw0KICAgICB6X2kgPSAoeF9pIFx0aW1lcyAyKSArICh5X2kgLyAyKQ0KICAgICBcXQ0KDQozLiAqKkNhbGN1bGF0ZXMgdGhlIHdlaWdodGVkIG1lYW4gb2YgYHpgKioNCiAgIC0gV2VpZ2h0czogYHcgPSBjKDAuMSwgMC4yLCAwLjE1LCAwLjI1LCAwLjMpYA0KDQo0LiAqKlJldHVybnMgdGhlIHBvc2l0aW9ucyoqDQogICAtIFdoZXJlIGB6ID4gNDBgDQpgYGB7cn0NCiMgR2l2ZW4gdmVjdG9ycw0KeCA8LSBjKDEyLCBOQSwgMjUsIDE4LCBOQSwgMzIsIDQ1LCBOQSkNCnkgPC0gYyg1LCA4LCAxMiwgNiwgOSwgTkEsIDE1LCA3KQ0KDQojIDEuIFJlbW92ZSBOQSB2YWx1ZXMgKGtlZXAgb25seSBjb21wbGV0ZSBwYWlyZWQgb2JzZXJ2YXRpb25zKQ0KY29tcGxldGVfY2FzZXMgPC0gY29tcGxldGUuY2FzZXMoeCwgeSkgI1RSVUUgRkFMU0UgIFRSVUUgIFRSVUUgRkFMU0UgRkFMU0UgIFRSVUUgRkFMU0UNCnhfY2xlYW4gPC0geFtjb21wbGV0ZV9jYXNlc10NCnlfY2xlYW4gPC0geVtjb21wbGV0ZV9jYXNlc10NCg0KIyAyLiBDcmVhdGUgdmVjdG9yIHo6IHpfaSA9ICh4X2kgKiAyKSArICh5X2kgLyAyKQ0KeiA8LSAoeF9jbGVhbiAqIDIpICsgKHlfY2xlYW4gLyAyKQ0KDQojIFJlc3VsdHMgYWZ0ZXIgY2xlYW5pbmcgKGZvciByZWZlcmVuY2UpDQojIHhfY2xlYW46IDEyLCAyNSwgMTgsIDQ1DQojIHlfY2xlYW46IDUsICAxMiwgNiwgIDE1DQojIHo6ICAgICAgIDI2LjUsIDU2LCAzOSwgOTcuNSAgIChsZW5ndGggPSA0KQ0KDQojIDMuIENhbGN1bGF0ZSB3ZWlnaHRlZCBtZWFuIG9mIHoNCiMgTm90ZTogVGhlcmUgYXJlIG9ubHkgNCB2YWxpZCBvYnNlcnZhdGlvbnMgYWZ0ZXIgTkEgcmVtb3ZhbCwNCiMgYnV0IDUgd2VpZ2h0cyBhcmUgcHJvdmlkZWQuIFRvIHJlc29sdmUgdGhpcyB3aGlsZSBrZWVwaW5nIHRoZSBzcGlyaXQNCiMgb2YgdGhlIHF1ZXN0aW9uLCB1c2UgdGhlIGZpcnN0IDQgd2VpZ2h0cyAoYSBjb21tb24gcHJhY3RpY2FsIGFwcHJvYWNoDQojIHdoZW4gdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgdmFyaWVzKS4NCncgPC0gYygwLjEsIDAuMiwgMC4xNSwgMC4yNSwgMC4zKSAgIyBmdWxsIHdlaWdodHMNCndlaWdodGVkX21lYW5feiA8LSB3ZWlnaHRlZC5tZWFuKHosIHcgPSB3WzE6bGVuZ3RoKHopXSkgICMgdXNlIGZpcnN0IDQ6IDAuMSwgMC4yLCAwLjE1LCAwLjI1DQoNCiMgQWx0ZXJuYXRpdmUgKGlmIGVxdWFsIHdlaWdodHMgYXJlIGFjY2VwdGFibGUpOg0KIyB3ZWlnaHRlZF9tZWFuX3ogPC0gbWVhbih6KQ0KDQojIDQuIEZpbmQgcG9zaXRpb25zIGluIHogd2hlcmUgeiA+IDQwIChwb3NpdGlvbnMgaW4gdGhlIGNsZWFuZWQgdmVjdG9yIHopDQpwb3NpdGlvbnMgPC0gd2hpY2goeiA+IDQwKQ0KDQojIE91dHB1dCByZXN1bHRzDQp6DQojIFsxXSAyNi41IDU2LjAgMzkuMCA5Ny41DQoNCndlaWdodGVkX21lYW5feg0KIyBbMV0gNTkuMjI1ICAgKGNhbGN1bGF0ZWQgYXMgKDI2LjUqMC4xKSArICg1NiowLjIpICsgKDM5KjAuMTUpICsgKDk3LjUqMC4yNSkpDQoNCnBvc2l0aW9ucw0KIyBbMV0gMiA0DQpgYGANCiMjIFNlY3Rpb24gMjogQWR2YW5jZWQgRGF0YSBNYW5pcHVsYXRpb24gIA0KDQojIyMgUTIuMSBDb21wbGV4IGBkcGx5cmAgUGlwZWxpbmUNCg0KVXNpbmcgdGhlICoqYHN0YXJ3YXJzYCoqIGRhdGFzZXQgZnJvbSAqKmBkcGx5cmAqKjoNCg0KMS4gKipGaWx0ZXIqKiBjaGFyYWN0ZXJzIHdpdGggbWFzcyBiZXR3ZWVuICoqNTAgYW5kIDEwMCBrZyoqDQoNCjIuICoqQ3JlYXRlIGEgbmV3IGNvbHVtbiBgYm1pX2NhdGVnb3J5YCoqIGJhc2VkIG9uOg0KICAgLSBJZiBgbWFzcyAvIGhlaWdodF4yIMOXIDEwMDAwID4gMjVgIOKGkiAqKiJPdmVyd2VpZ2h0IioqDQogICAtIElmIGJldHdlZW4gKioxOC41IGFuZCAyNSoqIOKGkiAqKiJOb3JtYWwiKioNCiAgIC0gRWxzZSDihpIgKioiVW5kZXJ3ZWlnaHQiKioNCg0KMy4gKipHcm91cCB0aGUgZGF0YSBieSoqDQogICAtIGBzcGVjaWVzYA0KICAgLSBgYm1pX2NhdGVnb3J5YA0KDQo0LiAqKkNhbGN1bGF0ZSBmb3IgZWFjaCBncm91cCoqDQogICAtIENvdW50IG9mIGNoYXJhY3RlcnMNCiAgIC0gQXZlcmFnZSBtYXNzDQogICAtIEF2ZXJhZ2UgaGVpZ2h0DQoNCjUuICoqQXJyYW5nZSB0aGUgcmVzdWx0KioNCiAgIC0gRmlyc3QgYnkgYHNwZWNpZXNgDQogICAtIFRoZW4gYnkgKipkZXNjZW5kaW5nIGNvdW50KioNCg0KNi4gKipLZWVwIG9ubHkgc3BlY2llcyoqDQogICAtIFdpdGggKiphdCBsZWFzdCAyIG1lbWJlcnMqKiBpbiB0aGUgZGF0YXNldA0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KDQpzdGFyd2Fyc19hbmFseXNpcyA8LSBzdGFyd2FycyAlPiUNCiAgZmlsdGVyKG1hc3MgPj0gNTAgJiBtYXNzIDw9IDEwMCwgIWlzLm5hKGhlaWdodCksICFpcy5uYShtYXNzKSkgJT4lDQogIG11dGF0ZSgNCiAgICBibWkgPSBtYXNzIC8gKGhlaWdodC8xMDApXjIsDQogICAgYm1pX2NhdGVnb3J5ID0gY2FzZV93aGVuKA0KICAgICAgYm1pID4gMjUgfiAiT3ZlcndlaWdodCIsDQogICAgICBibWkgPj0gMTguNSAmIGJtaSA8PSAyNSB+ICJOb3JtYWwiLA0KICAgICAgVFJVRSB+ICJVbmRlcndlaWdodCINCiAgICApDQogICkgJT4lDQogIGdyb3VwX2J5KHNwZWNpZXMsIGJtaV9jYXRlZ29yeSkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBjb3VudCA9IG4oKSwNCiAgICBhdmdfbWFzcyA9IG1lYW4obWFzcywgbmEucm0gPSBUUlVFKSwNCiAgICBhdmdfaGVpZ2h0ID0gbWVhbihoZWlnaHQsIG5hLnJtID0gVFJVRSksDQogICAgLmdyb3VwcyA9ICJkcm9wIg0KICApICU+JQ0KICBhcnJhbmdlKHNwZWNpZXMsIGRlc2MoY291bnQpKSAlPiUNCiAgZ3JvdXBfYnkoc3BlY2llcykgJT4lDQogIGZpbHRlcihzdW0oY291bnQpID49IDIpICU+JQ0KICB1bmdyb3VwKCkNCnN0YXJ3YXJzX2FuYWx5c2lzDQpgYGANCiMgU2VjdGlvbiAyOiBTdGF0aXN0aWNhbCBBbmFseXNpcyAmIFByb2JhYmlsaXR5DQoNCiMjIFEyLjEgQ3VzdG9tIFN0YXRpc3RpY2FsIEZ1bmN0aW9ucw0KDQoqKlRhc2s6KiogQ3JlYXRlIGEgZnVuY3Rpb24gYGRpc3RyaWJ1dGlvbl9hbmFseXplcmAgdGhhdCBwZXJmb3JtcyBjb21wcmVoZW5zaXZlIHN0YXRpc3RpY2FsIGFuYWx5c2lzLg0KDQojIyMgUmVxdWlyZW1lbnRzOg0KDQoxLiAqKklucHV0czoqKg0KICAgLSBBIG51bWVyaWMgdmVjdG9yDQogICAtIEFuIG9wdGlvbmFsIHdlaWdodHMgdmVjdG9yDQoNCjIuICoqT3V0cHV0czoqKiBSZXR1cm5zIGEgbGlzdCBjb250YWluaW5nOg0KICAgLSAqKk1lYW4qKg0KICAgLSAqKk1lZGlhbioqDQogICAtICoqTW9kZSoqIChpbXBsZW1lbnQgeW91ciBvd24gbW9kZSBmdW5jdGlvbikNCiAgIC0gKipTdGFuZGFyZCBkZXZpYXRpb24qKg0KICAgLSAqKlZhcmlhbmNlKioNCiAgIC0gKipTa2V3bmVzcyBhbmQga3VydG9zaXMqKiAodXNlIHRoZSBgbW9tZW50c2AgcGFja2FnZSBvciBjYWxjdWxhdGUgbWFudWFsbHkpDQogICAtICoqOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yIHRoZSBtZWFuKioNCiAgIC0gKipTaGFwaXJvLVdpbGsgdGVzdCoqIGZvciBub3JtYWxpdHkNCg0KMy4gKipBZGRpdGlvbmFsIEZlYXR1cmVzOioqDQogICAtIElmIHdlaWdodHMgYXJlIHByb3ZpZGVkLCBjYWxjdWxhdGUgKip3ZWlnaHRlZCBzdGF0aXN0aWNzKiouDQogICAtIEluY2x1ZGUgKiplcnJvciBoYW5kbGluZyoqIGZvciBpbnZhbGlkIGlucHV0cyAoZS5nLiwgbm9uLW51bWVyaWMgdmVjdG9ycywgbWlzbWF0Y2hlZCB3ZWlnaHRzLCBlbXB0eSB2ZWN0b3JzKS4NCg0KIyMjIE5vdGVzOg0KLSBFbnN1cmUgdGhhdCBhbGwgY2FsY3VsYXRpb25zIGFyZSByb2J1c3QgYW5kIGhhbmRsZSBtaXNzaW5nIHZhbHVlcyAoYE5BYCkgYXBwcm9wcmlhdGVseS4NCi0gVGhlIGZ1bmN0aW9uIHNob3VsZCByZXR1cm4gYSAqKndlbGwtc3RydWN0dXJlZCBsaXN0KiogZm9yIGVhc3kgaW50ZXJwcmV0YXRpb24uDQpgYGB7cn0NCmRpc3RyaWJ1dGlvbl9hbmFseXplciA8LSBmdW5jdGlvbih4LCB3ZWlnaHRzID0gTlVMTCwgbmEucm0gPSBUUlVFKSB7DQogIA0KICAjIEVycm9yIGhhbmRsaW5nDQogIGlmKCFpcy5udW1lcmljKHgpKSBzdG9wKCJJbnB1dCBtdXN0IGJlIG51bWVyaWMiKQ0KICBpZihuYS5ybSkgew0KICAgIHggPC0geFshaXMubmEoeCldDQogICAgaWYoIWlzLm51bGwod2VpZ2h0cykpIHdlaWdodHMgPC0gd2VpZ2h0c1shaXMubmEoeCldDQogIH0NCiAgDQogICMgQ3VzdG9tIG1vZGUgZnVuY3Rpb24NCiAgY29tcHV0ZV9tb2RlIDwtIGZ1bmN0aW9uKHYpIHsNCiAgICB1bmlxdiA8LSB1bmlxdWUodikNCiAgICB1bmlxdlt3aGljaC5tYXgodGFidWxhdGUobWF0Y2godiwgdW5pcXYpKSldDQogIH0NCiAgDQogICMgQmFzaWMgc3RhdGlzdGljcw0KICByZXN1bHRzIDwtIGxpc3QoKQ0KICByZXN1bHRzJG1lYW4gPC0gaWYoaXMubnVsbCh3ZWlnaHRzKSkgbWVhbih4KSBlbHNlIHdlaWdodGVkLm1lYW4oeCwgd2VpZ2h0cykNCiAgcmVzdWx0cyRtZWRpYW4gPC0gbWVkaWFuKHgpDQogIHJlc3VsdHMkbW9kZSA8LSBjb21wdXRlX21vZGUoeCkNCiAgcmVzdWx0cyRzZCA8LSBpZihpcy5udWxsKHdlaWdodHMpKSBzZCh4KSBlbHNlIHNxcnQoSG1pc2M6Ond0ZC52YXIoeCwgd2VpZ2h0cykpDQogIHJlc3VsdHMkdmFyIDwtIHJlc3VsdHMkc2ReMg0KICANCiAgIyBIaWdoZXIgbW9tZW50cyAobWFudWFsIGNhbGN1bGF0aW9uKQ0KICBuIDwtIGxlbmd0aCh4KQ0KICBjZW50ZXJlZCA8LSB4IC0gcmVzdWx0cyRtZWFuDQogIHJlc3VsdHMkc2tld25lc3MgPC0gKHN1bShjZW50ZXJlZF4zKS9uKSAvIChyZXN1bHRzJHNkXjMpDQogIHJlc3VsdHMka3VydG9zaXMgPC0gKHN1bShjZW50ZXJlZF40KS9uKSAvIChyZXN1bHRzJHNkXjQpDQogIA0KICAjIENvbmZpZGVuY2UgaW50ZXJ2YWwNCiAgc2UgPC0gcmVzdWx0cyRzZCAvIHNxcnQobikNCiAgdF9jcml0aWNhbCA8LSBxdCgwLjk3NSwgZGYgPSBuLTEpDQogIHJlc3VsdHMkY2lfbG93ZXIgPC0gcmVzdWx0cyRtZWFuIC0gdF9jcml0aWNhbCAqIHNlDQogIHJlc3VsdHMkY2lfdXBwZXIgPC0gcmVzdWx0cyRtZWFuICsgdF9jcml0aWNhbCAqIHNlDQogIA0KICAjIE5vcm1hbGl0eSB0ZXN0DQogIHJlc3VsdHMkc2hhcGlyb190ZXN0IDwtIHNoYXBpcm8udGVzdCh4KQ0KICANCiAgcmV0dXJuKHJlc3VsdHMpDQp9DQoNCg0KDQojIFNhbXBsZSBkYXRhDQp4IDwtIGMoMTAsIDIwLCAzMCwgMjAsIDEwKQ0Kd2VpZ2h0cyA8LSBjKDEsIDIsIDMsIDIsIDEpDQoNCiMgUnVuIHRoZSBmdW5jdGlvbiB3aXRoIHdlaWdodHMNCnJlc3VsdF93ZWlnaHRlZCA8LSBkaXN0cmlidXRpb25fYW5hbHl6ZXIoeCwgd2VpZ2h0cyA9IHdlaWdodHMpDQoNCiMgVmlldyByZXN1bHRzDQpyZXN1bHRfd2VpZ2h0ZWQNCg0KDQpgYGANCiMgUTIuMiBQcm9iYWJpbGl0eSBTaW11bGF0aW9uDQoNCiMjIFRhc2sNClNpbXVsYXRlIGEgY2FzaW5vIGdhbWU6DQoNCi0gWW91IHJvbGwgKip0d28gZGljZSoqLg0KLSAqKldpbiAkMTAqKiBpZiB0aGUgc3VtIGlzICoqNyBvciAxMSoqLg0KLSAqKkxvc2UgJDUqKiBvdGhlcndpc2UuDQoNCi0tLQ0KDQojIyBTdGVwcw0KDQoxLiAqKlJ1biAxMCwwMDAgc2ltdWxhdGlvbnMqKiBvZiB0aGUgZ2FtZS4NCjIuICoqQ2FsY3VsYXRlKio6DQogICAtIEV4cGVjdGVkIHZhbHVlDQogICAtIFByb2JhYmlsaXR5IG9mIHdpbm5pbmcNCiAgIC0gOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yIGV4cGVjdGVkIHZhbHVlDQozLiAqKkNyZWF0ZSBhIGZ1bmN0aW9uKiogdGhhdCBhbGxvd3M6DQogICAtIFZhcmlhYmxlIGJldCBhbW91bnRzDQogICAtIFZhcmlhYmxlIHBheW91dCByYXRpb3MNCg0KYGBge3J9DQpzaW11bGF0ZV9kaWNlX2dhbWUgPC0gZnVuY3Rpb24obl9zaW1zID0gMTAwMDAsIGJldCA9IDUsIHdpbl9hbW91bnQgPSAxMCkgew0KICBzZXQuc2VlZCgxMjMpDQogIA0KICByZXN1bHRzIDwtIHJlcGxpY2F0ZShuX3NpbXMsIHsNCiAgICBzIDwtIHN1bShzYW1wbGUoMTo2LCAyLCByZXBsYWNlID0gVFJVRSkpDQogICAgaWYgKHMgJWluJSBjKDcsIDExKSkgd2luX2Ftb3VudCBlbHNlIC1iZXQNCiAgfSkNCiAgDQogICMgRXN0aW1hdGVzDQogIEVWIDwtIG1lYW4ocmVzdWx0cykNCiAgcF93aW4gPC0gbWVhbihyZXN1bHRzID4gMCkNCiAgDQogICMgOTUlIENJIChOb3JtYWwgLyBDTFQpDQogIHNlIDwtIHNkKHJlc3VsdHMpIC8gc3FydChuX3NpbXMpDQogIGNpIDwtIEVWICsgYygtMSwgMSkgKiBxbm9ybSgwLjk3NSkgKiBzZSAgIA0KICANCiAgbGlzdCgNCiAgICBleHBlY3RlZF92YWx1ZSA9IEVWLA0KICAgIHdpbl9wcm9iYWJpbGl0eSA9IHBfd2luLA0KICAgIGNvbmZpZGVuY2VfaW50ZXJ2YWwgPSBjaQ0KICApDQp9DQoNCiMgUnVuIHNpbXVsYXRpb24NCmdhbWVfcmVzdWx0cyA8LSBzaW11bGF0ZV9kaWNlX2dhbWUoDQogIG5fc2ltcyA9IDEwMDAwLA0KICBiZXQgPSA1LA0KICB3aW5fYW1vdW50ID0gMTANCikNCg0KZ2FtZV9yZXN1bHRzDQpgYGANCiMgU2VjdGlvbiAzOiBBZHZhbmNlZCBWaXN1YWxpemF0aW9uDQoNCiMjIFEzLjEgTXVsdGktUGFuZWwgRGlhZ25vc3RpYyBQbG90DQoNCkNyZWF0ZSBhIGNvbXByZWhlbnNpdmUgZGlhZ25vc3RpYyBwbG90IGZvciBhIGxpbmVhciBtb2RlbCB3aXRoIHRoZSBmb2xsb3dpbmcgcGFuZWxzOg0KDQptb2RlbCA8LSBsbShtcGcgfiB3dCArIGhwICsgcXNlYywgZGF0YSA9IG10Y2FycykNCg0KLSAqKlRvcC1sZWZ0OioqIFJlc2lkdWFscyB2cyBGaXR0ZWQgdmFsdWVzICANCi0gKipUb3AtcmlnaHQ6KiogUS1RIHBsb3Qgb2YgcmVzaWR1YWxzICANCi0gKipCb3R0b20tbGVmdDoqKiBTY2FsZS1Mb2NhdGlvbiBwbG90ICANCi0gKipCb3R0b20tcmlnaHQ6KiogUmVzaWR1YWxzIHZzIExldmVyYWdlIHdpdGggQ29vaydzIGRpc3RhbmNlICANCg0KKipSZXF1aXJlbWVudHM6KioNCg0KLSBBZGQgYXBwcm9wcmlhdGUgdGl0bGVzIGZvciBlYWNoIHBhbmVsLiAgDQotIEluY2x1ZGUgcmVmZXJlbmNlIGxpbmVzIHdoZXJlIG5lY2Vzc2FyeSAoZS5nLiwgaG9yaXpvbnRhbCBsaW5lIGF0IDAgZm9yIFJlc2lkdWFscyB2cyBGaXR0ZWQpLiAgDQotIFVzZSBjb2xvciBjb2RpbmcgdG8gaGlnaGxpZ2h0IHBvaW50cyB3aXRoIGhpZ2ggaW5mbHVlbmNlIChlLmcuLCBsYXJnZSBDb29rJ3MgZGlzdGFuY2UpLiAgDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZ3JpZEV4dHJhKQ0KDQojIEZpdCBtb2RlbA0KbW9kZWwgPC0gbG0obXBnIH4gd3QgKyBocCArIHFzZWMsIGRhdGEgPSBtdGNhcnMpDQojIFByZXBhcmUgZGF0YQ0KZGlhZ25vc3RpY19kYXRhIDwtIGRhdGEuZnJhbWUoDQogIGZpdHRlZCA9IGZpdHRlZChtb2RlbCksDQogIHJlc2lkdWFscyA9IHJlc2lkdWFscyhtb2RlbCksDQogIHNxcnRfYWJzX3Jlc2lkID0gc3FydChhYnMocmVzaWR1YWxzKG1vZGVsKSkpLA0KICBsZXZlcmFnZSA9IGhhdHZhbHVlcyhtb2RlbCksDQogIGNvb2tzX2QgPSBjb29rcy5kaXN0YW5jZShtb2RlbCkNCikNCg0KIyAxLiBSZXNpZHVhbHMgdnMgRml0dGVkDQpwMSA8LSBnZ3Bsb3QoZGlhZ25vc3RpY19kYXRhLCBhZXMoeCA9IGZpdHRlZCwgeSA9IHJlc2lkdWFscykpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNykgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIsIHNlID0gRkFMU0UsIGNvbG9yID0gImJsdWUiKSArDQogIGxhYnModGl0bGUgPSAiUmVzaWR1YWxzIHZzIEZpdHRlZCIsIHggPSAiRml0dGVkIFZhbHVlcyIsIHkgPSAiUmVzaWR1YWxzIikNCg0KIyAyLiBRLVEgUGxvdA0KcDIgPC0gZ2dwbG90KGRpYWdub3N0aWNfZGF0YSwgYWVzKHNhbXBsZSA9IHJlc2lkdWFscykpICsNCiAgc3RhdF9xcSgpICsgc3RhdF9xcV9saW5lKGNvbG9yID0gInJlZCIpICsNCiAgbGFicyh0aXRsZSA9ICJOb3JtYWwgUS1RIiwgeCA9ICJUaGVvcmV0aWNhbCBRdWFudGlsZXMiLCB5ID0gIlNhbXBsZSBRdWFudGlsZXMiKQ0KDQojIDMuIFNjYWxlLUxvY2F0aW9uDQpwMyA8LSBnZ3Bsb3QoZGlhZ25vc3RpY19kYXRhLCBhZXMoeCA9IGZpdHRlZCwgeSA9IHNxcnRfYWJzX3Jlc2lkKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMC43KSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIsIHNlID0gRkFMU0UsIGNvbG9yID0gImJsdWUiKSArDQogIGxhYnModGl0bGUgPSAiU2NhbGUtTG9jYXRpb24iLCB4ID0gIkZpdHRlZCBWYWx1ZXMiLCB5ID0gIuKImnxTdGFuZGFyZGl6ZWQgUmVzaWR1YWxzfCIpDQoNCiMgNC4gUmVzaWR1YWxzIHZzIExldmVyYWdlDQpwNCA8LSBnZ3Bsb3QoZGlhZ25vc3RpY19kYXRhLCBhZXMoeCA9IGxldmVyYWdlLCB5ID0gcmVzaWR1YWxzKSkgKw0KICBnZW9tX3BvaW50KGFlcyhzaXplID0gY29va3NfZCwgY29sb3IgPSBjb29rc19kID4gMC41KSwgYWxwaGEgPSAwLjcpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiwgInJlZCIpKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgbGFicyh0aXRsZSA9ICJSZXNpZHVhbHMgdnMgTGV2ZXJhZ2UiLCB4ID0gIkxldmVyYWdlIiwgeSA9ICJSZXNpZHVhbHMiKSArDQogIGd1aWRlcyhjb2xvciA9ICJub25lIikNCg0KIyBBcnJhbmdlIHBsb3RzDQpncmlkLmFycmFuZ2UocDEsIHAyLCBwMywgcDQsIG5jb2wgPSAyKQ0KYGBgDQojIFNlY3Rpb24gNDogTWFjaGluZSBMZWFybmluZyBJbXBsZW1lbnRhdGlvbg0KKipUaW1lOioqIDUwIG1pbnV0ZXMNCg0KIyMgUTQuMSBDdXN0b20gay1OTiB3aXRoIENyb3NzLVZhbGlkYXRpb24NCg0KKipUYXNrczoqKg0KDQoxLiBJbXBsZW1lbnQgYSBrLU5OIGZ1bmN0aW9uIGZyb20gc2NyYXRjaCAod2l0aG91dCB1c2luZyBgY2FyZXRgIG9yIHNpbWlsYXIgcGFja2FnZXMpIHRoYXQgY2FuIGhhbmRsZSBib3RoOg0KICAgLSAqKkNsYXNzaWZpY2F0aW9uKioNCiAgIC0gKipSZWdyZXNzaW9uKioNCg0KMi4gSW1wbGVtZW50ICoqay1mb2xkIGNyb3NzLXZhbGlkYXRpb24qKiB0byBzZWxlY3QgdGhlIG9wdGltYWwgdmFsdWUgb2YgYGtgLg0KDQozLiBDb21wdXRlICoqcGVyZm9ybWFuY2UgbWV0cmljcyoqOg0KICAgLSAqKkFjY3VyYWN5KiogZm9yIGNsYXNzaWZpY2F0aW9uDQogICAtICoqUk1TRSoqIGZvciByZWdyZXNzaW9uDQoNCjQuIENyZWF0ZSBhICoqdmlzdWFsaXphdGlvbioqIG9mIGBrYCB2ZXJzdXMgcGVyZm9ybWFuY2UgdG8gaWRlbnRpZnkgdGhlIGJlc3QgYGtgLg0KYGBge3J9DQpsaWJyYXJ5KGNsYXNzKQ0KbGlicmFyeShkcGx5cikNCg0KY3VzdG9tX2tubiA8LSBmdW5jdGlvbih0cmFpbl9kYXRhLCB0ZXN0X2RhdGEsIHRyYWluX2xhYmVscywgaywgdHlwZSA9ICJjbGFzc2lmaWNhdGlvbiIpIHsNCiAgcHJlZGljdGlvbnMgPC0ga25uKA0KICAgIHRyYWluID0gYXMubWF0cml4KHRyYWluX2RhdGEpLA0KICAgIHRlc3QgPSBhcy5tYXRyaXgodGVzdF9kYXRhKSwNCiAgICBjbCA9IHRyYWluX2xhYmVscywNCiAgICBrID0gaw0KICApDQogIA0KICBpZih0eXBlID09ICJyZWdyZXNzaW9uIikgew0KICAgICMgRm9yIHJlZ3Jlc3Npb24sIGNvbnZlcnQgZmFjdG9yIHByZWRpY3Rpb25zIGJhY2sgdG8gbnVtZXJpYw0KICAgIHByZWRpY3Rpb25zIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHByZWRpY3Rpb25zKSkNCiAgfQ0KICANCiAgcmV0dXJuKHByZWRpY3Rpb25zKQ0KfQ0KDQprZm9sZF9jdl9rbm4gPC0gZnVuY3Rpb24oZGF0YSwgdGFyZ2V0LCBrX3ZhbHVlcyA9IDE6MjAsIG5fZm9sZHMgPSA1LCB0eXBlID0gImNsYXNzaWZpY2F0aW9uIikgew0KICANCiAgIyBDcmVhdGUgZm9sZHMNCiAgbiA8LSBucm93KGRhdGEpDQogIGZvbGRzIDwtIHNhbXBsZShyZXAoMTpuX2ZvbGRzLCBsZW5ndGgub3V0ID0gbikpDQogIA0KICByZXN1bHRzIDwtIGRhdGEuZnJhbWUoKQ0KICANCiAgZm9yKGsgaW4ga192YWx1ZXMpIHsNCiAgICBmb2xkX21ldHJpY3MgPC0gbnVtZXJpYyhuX2ZvbGRzKQ0KICAgIA0KICAgIGZvcihmb2xkIGluIDE6bl9mb2xkcykgew0KICAgICAgIyBTcGxpdCBkYXRhDQogICAgICB0cmFpbl9pZHggPC0gZm9sZHMgIT0gZm9sZA0KICAgICAgdGVzdF9pZHggPC0gZm9sZHMgPT0gZm9sZA0KICAgICAgDQogICAgICAjIE1ha2UgcHJlZGljdGlvbg0KICAgICAgcHJlZHMgPC0gY3VzdG9tX2tubigNCiAgICAgICAgZGF0YVt0cmFpbl9pZHgsIF0sDQogICAgICAgIGRhdGFbdGVzdF9pZHgsIF0sDQogICAgICAgIHRhcmdldFt0cmFpbl9pZHhdLA0KICAgICAgICBrID0gaywNCiAgICAgICAgdHlwZSA9IHR5cGUNCiAgICAgICkNCiAgICAgIA0KICAgICAgIyBDYWxjdWxhdGUgcGVyZm9ybWFuY2UNCiAgICAgIGlmKHR5cGUgPT0gImNsYXNzaWZpY2F0aW9uIikgew0KICAgICAgICAjIEFjY3VyYWN5DQogICAgICAgIGZvbGRfbWV0cmljc1tmb2xkXSA8LSBzdW0ocHJlZHMgPT0gdGFyZ2V0W3Rlc3RfaWR4XSkgLyBsZW5ndGgocHJlZHMpDQogICAgICB9IGVsc2Ugew0KICAgICAgICAjIFJNU0UgZm9yIHJlZ3Jlc3Npb24NCiAgICAgICAgZm9sZF9tZXRyaWNzW2ZvbGRdIDwtIHNxcnQobWVhbigocHJlZHMgLSB0YXJnZXRbdGVzdF9pZHhdKV4yKSkNCiAgICAgIH0NCiAgICB9DQogICAgDQogICAgcmVzdWx0cyA8LSByYmluZChyZXN1bHRzLCBkYXRhLmZyYW1lKA0KICAgICAgayA9IGssDQogICAgICBtZWFuX3BlcmZvcm1hbmNlID0gbWVhbihmb2xkX21ldHJpY3MpLA0KICAgICAgc2RfcGVyZm9ybWFuY2UgPSBzZChmb2xkX21ldHJpY3MpDQogICAgKSkNCiAgfQ0KICANCiAgIyBGaW5kIGJlc3Qgaw0KICBpZih0eXBlID09ICJjbGFzc2lmaWNhdGlvbiIpIHsNCiAgICBiZXN0X2sgPC0gcmVzdWx0cyRrW3doaWNoLm1heChyZXN1bHRzJG1lYW5fcGVyZm9ybWFuY2UpXQ0KICB9IGVsc2Ugew0KICAgIGJlc3RfayA8LSByZXN1bHRzJGtbd2hpY2gubWluKHJlc3VsdHMkbWVhbl9wZXJmb3JtYW5jZSldDQogIH0NCiAgDQogIHJldHVybihsaXN0KA0KICAgIGN2X3Jlc3VsdHMgPSByZXN1bHRzLA0KICAgIGJlc3RfayA9IGJlc3Rfaw0KICApKQ0KfQ0KDQojIEV4YW1wbGUgdXNhZ2UNCmRhdGEoaXJpcykNCmlyaXNfZmVhdHVyZXMgPC0gaXJpc1ssIDE6NF0NCmlyaXNfc3BlY2llcyA8LSBpcmlzJFNwZWNpZXMNCg0KY3ZfcmVzdWx0cyA8LSBrZm9sZF9jdl9rbm4oaXJpc19mZWF0dXJlcywgaXJpc19zcGVjaWVzLCBrX3ZhbHVlcyA9IDE6MTUsIHR5cGUgPSAiY2xhc3NpZmljYXRpb24iKQ0KDQojIFBsb3QgcmVzdWx0cw0KbGlicmFyeShnZ3Bsb3QyKQ0KZ2dwbG90KGN2X3Jlc3VsdHMkY3ZfcmVzdWx0cywgYWVzKHggPSBrLCB5ID0gbWVhbl9wZXJmb3JtYW5jZSkpICsNCiAgZ2VvbV9saW5lKCkgKw0KICBnZW9tX3JpYmJvbihhZXMoeW1pbiA9IG1lYW5fcGVyZm9ybWFuY2UgLSBzZF9wZXJmb3JtYW5jZSwNCiAgICAgICAgICAgICAgICAgIHltYXggPSBtZWFuX3BlcmZvcm1hbmNlICsgc2RfcGVyZm9ybWFuY2UpLA0KICAgICAgICAgICAgICBhbHBoYSA9IDAuMikgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjdl9yZXN1bHRzJGJlc3RfaywgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIikgKw0KICBsYWJzKHRpdGxlID0gImstTk4gQ3Jvc3MtVmFsaWRhdGlvbiBSZXN1bHRzIiwgeCA9ICJrIiwgeSA9ICJBY2N1cmFjeSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCmBgYHtyfQ0KbGlicmFyeShjbGFzcykNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KE1BU1MpDQoNCmRhdGEoQm9zdG9uKQ0KDQpib3N0b25fZmVhdHVyZXMgPC0gQm9zdG9uWywgLTE0XQ0KYm9zdG9uX3RhcmdldCA8LSBCb3N0b24kbWVkdg0KDQpib3N0b25fZmVhdHVyZXNfc2NhbGVkIDwtIGFzLmRhdGEuZnJhbWUoc2NhbGUoYm9zdG9uX2ZlYXR1cmVzKSkNCg0KY3ZfcmVzdWx0c19yZWcgPC0ga2ZvbGRfY3Zfa25uKA0KICBkYXRhID0gYm9zdG9uX2ZlYXR1cmVzX3NjYWxlZCwNCiAgdGFyZ2V0ID0gYm9zdG9uX3RhcmdldCwNCiAga192YWx1ZXMgPSAxOjIwLA0KICBuX2ZvbGRzID0gNSwNCiAgdHlwZSA9ICJyZWdyZXNzaW9uIg0KKQ0KDQpnZ3Bsb3QoY3ZfcmVzdWx0c19yZWckY3ZfcmVzdWx0cywgYWVzKHggPSBrLCB5ID0gbWVhbl9wZXJmb3JtYW5jZSkpICsNCiAgZ2VvbV9saW5lKGNvbG9yID0gImJsdWUiKSArDQogIGdlb21fcmliYm9uKGFlcyh5bWluID0gbWVhbl9wZXJmb3JtYW5jZSAtIHNkX3BlcmZvcm1hbmNlLA0KICAgICAgICAgICAgICAgICAgeW1heCA9IG1lYW5fcGVyZm9ybWFuY2UgKyBzZF9wZXJmb3JtYW5jZSksDQogICAgICAgICAgICAgIGFscGhhID0gMC4yLCBmaWxsID0gImJsdWUiKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGN2X3Jlc3VsdHNfcmVnJGJlc3RfaywgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIikgKw0KICBsYWJzKHRpdGxlID0gImstTk4gQ1YgUmVzdWx0cyAoQm9zdG9uIEhvdXNpbmcgUmVncmVzc2lvbikiLA0KICAgICAgIHggPSAiayIsDQogICAgICAgeSA9ICJNZWFuIFJNU0UiLA0KICAgICAgIHN1YnRpdGxlID0gcGFzdGUoIkJlc3QgayA9IiwgY3ZfcmVzdWx0c19yZWckYmVzdF9rKSkgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KYGBge3J9DQpsaWJyYXJ5KGNsYXNzKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dwbG90MikNCg0KZGF0YShtdGNhcnMpDQoNCm10Y2Fyc19mZWF0dXJlcyA8LSBtdGNhcnNbLCBjKCJtcGciLCAiZGlzcCIsICJocCIsICJ3dCIsICJxc2VjIildDQptdGNhcnNfdGFyZ2V0IDwtIGFzLmZhY3RvcihtdGNhcnMkY3lsKQ0KDQptdGNhcnNfZmVhdHVyZXNfc2NhbGVkIDwtIGFzLmRhdGEuZnJhbWUoc2NhbGUobXRjYXJzX2ZlYXR1cmVzKSkNCg0KY3ZfcmVzdWx0c19jbGFzcyA8LSBrZm9sZF9jdl9rbm4oDQogIGRhdGEgPSBtdGNhcnNfZmVhdHVyZXNfc2NhbGVkLA0KICB0YXJnZXQgPSBtdGNhcnNfdGFyZ2V0LA0KICBrX3ZhbHVlcyA9IDE6MTAsDQogIG5fZm9sZHMgPSA1LA0KICB0eXBlID0gImNsYXNzaWZpY2F0aW9uIg0KKQ0KDQpnZ3Bsb3QoY3ZfcmVzdWx0c19jbGFzcyRjdl9yZXN1bHRzLCBhZXMoeCA9IGssIHkgPSBtZWFuX3BlcmZvcm1hbmNlKSkgKw0KICBnZW9tX2xpbmUoY29sb3IgPSAiZGFya2dyZWVuIikgKw0KICBnZW9tX3JpYmJvbihhZXMoeW1pbiA9IG1lYW5fcGVyZm9ybWFuY2UgLSBzZF9wZXJmb3JtYW5jZSwNCiAgICAgICAgICAgICAgICAgIHltYXggPSBtZWFuX3BlcmZvcm1hbmNlICsgc2RfcGVyZm9ybWFuY2UpLA0KICAgICAgICAgICAgICBhbHBoYSA9IDAuMiwgZmlsbCA9ICJncmVlbiIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gY3ZfcmVzdWx0c19jbGFzcyRiZXN0X2ssIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsNCiAgbGFicyh0aXRsZSA9ICJrLU5OIENWIFJlc3VsdHMgKG10Y2FycyBDeWxpbmRlciBDbGFzc2lmaWNhdGlvbikiLA0KICAgICAgIHggPSAiayIsDQogICAgICAgeSA9ICJNZWFuIEFjY3VyYWN5IiwNCiAgICAgICBzdWJ0aXRsZSA9IHBhc3RlKCJCZXN0IGsgPSIsIGN2X3Jlc3VsdHNfY2xhc3MkYmVzdF9rKSkgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KIyBTZWN0aW9uIDU6IENvbXBsZXRlIFByb2plY3QNCioqVGltZToqKiA2MCBtaW51dGVzDQoNCiMjIFE1OiBFbmQtdG8tRW5kIERhdGEgQW5hbHlzaXMgUHJvamVjdA0KDQoqKkRhdGFzZXQ6KiogYGRpYW1vbmRzYCBmcm9tIGBnZ3Bsb3QyYA0KDQojIyMgMS4gRGF0YSBQcmVwYXJhdGlvbg0KLSBIYW5kbGUgb3V0bGllcnMgaW4gYHByaWNlYCB1c2luZyB0aGUgKipJUVIgbWV0aG9kKiouICANCi0gQ3JlYXRlIG5ldyBmZWF0dXJlczoNCiAgLSBgcHJpY2VfcGVyX2NhcmF0YCA9IHByaWNlIMO3IGNhcmF0ICANCiAgLSBgdm9sdW1lYCA9IHggw5cgeSDDlyB6ICANCiAgLSBgZGVwdGhfY2F0ZWdvcnlgIChjYXRlZ29yaXplIGRlcHRoKSAgDQotIENvbnZlcnQgYXBwcm9wcmlhdGUgY29sdW1ucyB0byAqKmZhY3RvcnMqKi4NCg0KIyMjIDIuIEV4cGxvcmF0b3J5IEFuYWx5c2lzDQotIFZpc3VhbGl6ZSAqKmNvcnJlbGF0aW9uIG1hdHJpeCoqLiAgDQotIFBsb3QgKipkaXN0cmlidXRpb24gb2YgcHJpY2UqKiBieSBgY3V0YCwgYGNvbG9yYCwgYGNsYXJpdHlgIHVzaW5nICoqZmFjZXRpbmcqKi4gIA0KLSBDcmVhdGUgYSAqKjNEIHNjYXR0ZXJwbG90Kiogb2YgYGNhcmF0YCwgYHByaWNlYCwgYW5kIGBkZXB0aGAgKHVzZSBgcGxvdGx5YCBpZiBhdmFpbGFibGUpLg0KDQojIyMgMy4gU3RhdGlzdGljYWwgTW9kZWxpbmcNCi0gQnVpbGQgYSAqKmxpbmVhciBtb2RlbCoqIHByZWRpY3RpbmcgYGxvZyhwcmljZSlgLiAgDQotIEJ1aWxkIGEgKipyYW5kb20gZm9yZXN0IG1vZGVsKiogKGByYW5nZXJgIG9yIGByYW5kb21Gb3Jlc3RgIHBhY2thZ2UpLiAgDQotIENvbXBhcmUgbW9kZWxzIHVzaW5nICoqY3Jvc3MtdmFsaWRhdGVkIFJNU0UqKi4NCg0KIyMjIDQuIFByb2R1Y3Rpb24gT3V0cHV0DQotIENyZWF0ZSBhbiAqKlIgTWFya2Rvd24gcmVwb3J0Kiogc3VtbWFyaXppbmcgYWxsIGZpbmRpbmdzLiAgDQotIFNhdmUgbW9kZWxzIGFzICoqLnJkcyBmaWxlcyoqLiAgDQotIEltcGxlbWVudCBhICoqcHJlZGljdGlvbiBmdW5jdGlvbioqIHRoYXQgdGFrZXMgbmV3IGRhdGEgYXMgaW5wdXQuDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGNhcmV0KQ0KbGlicmFyeShyYW5nZXIpDQoNCiMgMS4gRGF0YSBQcmVwYXJhdGlvbg0KZGlhbW9uZHNfY2xlYW4gPC0gZGlhbW9uZHMgJT4lDQogIG11dGF0ZSgNCiAgICBwcmljZV9wZXJfY2FyYXQgPSBwcmljZSAvIGNhcmF0LA0KICAgIHZvbHVtZSA9IHggKiB5ICogeiwNCiAgICBkZXB0aF9jYXRlZ29yeSA9IGN1dChkZXB0aCwgYnJlYWtzID0gNSwgbGFiZWxzID0gYygiVmVyeSBTaGFsbG93IiwgIlNoYWxsb3ciLCAiTWVkaXVtIiwgIkRlZXAiLCAiVmVyeSBEZWVwIikpDQogICkgJT4lDQogIGZpbHRlcihwcmljZSA8PSBxdWFudGlsZShwcmljZSwgMC45OSkgJiBwcmljZSA+PSBxdWFudGlsZShwcmljZSwgMC4wMSkpDQoNCiMgMi4gRURBIC0gQ29ycmVsYXRpb24gaGVhdG1hcA0KbGlicmFyeShjb3JycGxvdCkNCm51bWVyaWNfY29scyA8LSBkaWFtb25kc19jbGVhbiAlPiUgDQogIHNlbGVjdCh3aGVyZShpcy5udW1lcmljKSkgJT4lDQogIHNlbGVjdCgteCwgLXksIC16KSAgIyBSZW1vdmUgZGltZW5zaW9uIGNvbHVtbnMNCmNvcl9tYXRyaXggPC0gY29yKG51bWVyaWNfY29scykNCmNvcnJwbG90KGNvcl9tYXRyaXgsIG1ldGhvZCA9ICJjb2xvciIsIHR5cGUgPSAidXBwZXIiKQ0KDQojIDMuIE1vZGVsaW5nIHNldHVwDQpzZXQuc2VlZCgxMjMpDQpkaWFtb25kc19jbGVhbiRsb2dfcHJpY2UgPC0gbG9nKGRpYW1vbmRzX2NsZWFuJHByaWNlKQ0KDQojIFRyYWluLXRlc3Qgc3BsaXQNCnRyYWluX2lkeCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKGRpYW1vbmRzX2NsZWFuJGxvZ19wcmljZSwgcCA9IDAuNywgbGlzdCA9IEZBTFNFKQ0KdHJhaW5fZGF0YSA8LSBkaWFtb25kc19jbGVhblt0cmFpbl9pZHgsIF0NCnRlc3RfZGF0YSA8LSBkaWFtb25kc19jbGVhblstdHJhaW5faWR4LCBdDQoNCiMgTGluZWFyIG1vZGVsDQpsbV9tb2RlbCA8LSBsbShsb2dfcHJpY2UgfiBjYXJhdCArIGN1dCArIGNvbG9yICsgY2xhcml0eSArIGRlcHRoICsgdGFibGUsIA0KICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluX2RhdGEpDQoNCiMgUmFuZG9tIEZvcmVzdA0KcmZfbW9kZWwgPC0gcmFuZ2VyKA0KICBsb2dfcHJpY2UgfiBjYXJhdCArIGN1dCArIGNvbG9yICsgY2xhcml0eSArIGRlcHRoICsgdGFibGUsDQogIGRhdGEgPSB0cmFpbl9kYXRhLA0KICBudW0udHJlZXMgPSA1MDAsDQogIG10cnkgPSAzLA0KICBpbXBvcnRhbmNlID0gImltcHVyaXR5Ig0KKQ0KDQojIENyb3NzLXZhbGlkYXRpb24gY29tcGFyaXNvbg0KdHJhaW5fY29udHJvbCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgbnVtYmVyID0gNSkNCg0KIyBMaW5lYXIgbW9kZWwgQ1YNCmxtX2N2IDwtIHRyYWluKA0KICBsb2dfcHJpY2UgfiBjYXJhdCArIGN1dCArIGNvbG9yICsgY2xhcml0eSArIGRlcHRoICsgdGFibGUsDQogIGRhdGEgPSB0cmFpbl9kYXRhLA0KICBtZXRob2QgPSAibG0iLA0KICB0ckNvbnRyb2wgPSB0cmFpbl9jb250cm9sDQopDQoNCiMgUmFuZG9tIEZvcmVzdCBDVg0KcmZfY3YgPC0gdHJhaW4oDQogIGxvZ19wcmljZSB+IGNhcmF0ICsgY3V0ICsgY29sb3IgKyBjbGFyaXR5ICsgZGVwdGggKyB0YWJsZSwNCiAgZGF0YSA9IHRyYWluX2RhdGEsDQogIG1ldGhvZCA9ICJyYW5nZXIiLA0KICB0ckNvbnRyb2wgPSB0cmFpbl9jb250cm9sLA0KICB0dW5lR3JpZCA9IGV4cGFuZC5ncmlkKA0KICAgIG10cnkgPSBjKDIsIDMsIDQpLA0KICAgIHNwbGl0cnVsZSA9ICJ2YXJpYW5jZSIsDQogICAgbWluLm5vZGUuc2l6ZSA9IDUNCiAgKQ0KKQ0KDQojIENvbXBhcmUgbW9kZWxzDQpyZXN1bHRzIDwtIHJlc2FtcGxlcyhsaXN0KA0KICBMaW5lYXIgPSBsbV9jdiwNCiAgUmFuZG9tRm9yZXN0ID0gcmZfY3YNCikpDQpzdW1tYXJ5KHJlc3VsdHMpDQoNCiMgNC4gUHJvZHVjdGlvbiBvdXRwdXRzDQojIFNhdmUgbW9kZWxzDQpzYXZlUkRTKGxtX21vZGVsLCAiZGlhbW9uZHNfbG1fbW9kZWwucmRzIikNCnNhdmVSRFMocmZfbW9kZWwsICJkaWFtb25kc19yZl9tb2RlbC5yZHMiKQ0KDQojIFByZWRpY3Rpb24gZnVuY3Rpb24NCnByZWRpY3RfZGlhbW9uZF9wcmljZSA8LSBmdW5jdGlvbihuZXdfZGF0YSwgbW9kZWxfdHlwZSA9ICJyZiIpIHsNCiAgaWYobW9kZWxfdHlwZSA9PSAicmYiKSB7DQogICAgbW9kZWwgPC0gcmVhZFJEUygiZGlhbW9uZHNfcmZfbW9kZWwucmRzIikNCiAgICBwcmVkX2xvZyA8LSBwcmVkaWN0KG1vZGVsLCBuZXdfZGF0YSkkcHJlZGljdGlvbnMNCiAgfSBlbHNlIHsNCiAgICBtb2RlbCA8LSByZWFkUkRTKCJkaWFtb25kc19sbV9tb2RlbC5yZHMiKQ0KICAgIHByZWRfbG9nIDwtIHByZWRpY3QobW9kZWwsIG5ld19kYXRhKQ0KICB9DQogIA0KICAjIENvbnZlcnQgYmFjayBmcm9tIGxvZyBzY2FsZQ0KICBwcmljZV9wcmVkIDwtIGV4cChwcmVkX2xvZykNCiAgDQogIHJldHVybihkYXRhLmZyYW1lKA0KICAgIHByZWRpY3RlZF9wcmljZSA9IHByaWNlX3ByZWQsDQogICAgcHJlZGljdGVkX2xvZ19wcmljZSA9IHByZWRfbG9nDQogICkpDQp9DQpgYGANCg0KDQoNCg==