Data Exploration

Exercises ~ Week 3

Logo


1 Exercise 1

The following table shows sample information for three students. Each observation represents a single student and includes details such as their unique student ID, name, age, total credits completed, major field of study, and year level.

This dataset demonstrates a mixture of variable types:

  • Nominal: StudentID, Name, Major
  • Numeric: Age (continuous), CreditsCompleted (discrete)
  • Ordinal: YearLevel (Freshman → Senior)
StudentID Name Age CreditsCompleted Major YearLevel
S001 Alice 20 45 Data Sains Sophomore
S002 Budi 21 60 Mathematics Junior
S003 Citra 19 30 Statistics Freshman
# 1. Create vectors for each variable
StudentID <- c("S001", "S002", "S003")       # Nominal / ID
Name <- c("Alice", "Budi", "Citra")          # Nominal / Name
Age <- c(20, 21, 19)                         # Numeric / Continuous
CreditsCompleted <- c(45, 60, 30)            # Numeric / Discrete

# Nominal
Major <- c("Data Sains", "Mathematics", "Statistics")  

# Ordinal
YearLevel <- factor(c("Sophomore", "Junior", "Freshman"),
                    levels = c("Freshman","Sophomore","Junior","Senior"),
                    ordered = TRUE)          

# 2. Combine all vectors into a data frame
students <- data.frame(
  StudentID, Name, Age, CreditsCompleted, Major, YearLevel,
  stringsAsFactors = FALSE
)

# 3. Display the data frame
print(students)
##   StudentID  Name Age CreditsCompleted       Major YearLevel
## 1      S001 Alice  20               45  Data Sains Sophomore
## 2      S002  Budi  21               60 Mathematics    Junior
## 3      S003 Citra  19               30  Statistics  Freshman

2 Exercise 2

Identify Data Types: Determine the type of data for each of the following variables:

# Install knitr package if not already installed
# install.packages("knitr")
library(knitr)

# Create a data frame for Data Types
variables_info <- data.frame(
  No = 1:5,
  Variable = c(
    "Number of vehicles passing through the toll road each day",
    "Student height in cm",
    "Employee gender (Male / Female)",
    "Customer satisfaction level: Low, Medium, High",
    "Respondent's favorite color: Red, Blue, Green"
  ),
  DataType = c(
    "Numeric",
    "Numeric",
    "Categorical",
    "Categorical",
    "Categorical"
  ),
  Subtype = c(
    "Continuous",
    "Continuous",
    "Nominal",
    "Ordinal",
    "Nominal"
  ),
  stringsAsFactors = FALSE
)

# Display the data frame as a neat table
kable(variables_info, 
      caption = "Table of Variables and Data Types")
Table of Variables and Data Types
No Variable DataType Subtype
1 Number of vehicles passing through the toll road each day Numeric Continuous
2 Student height in cm Numeric Continuous
3 Employee gender (Male / Female) Categorical Nominal
4 Customer satisfaction level: Low, Medium, High Categorical Ordinal
5 Respondent’s favorite color: Red, Blue, Green Categorical Nominal

3 Exercise 3

Classify Data Sources: Determine whether the following data comes from internal or external sources, and whether it is structured or unstructured:

# Install DT package if not already installed
# install.packages("DT")
library(DT)

# Create a data frame for data sources 
data_sources <- data.frame(
  No = 1:4,
  DataSource = c(
    "Daily sales transaction data of the company",
    "Weather reports from BMKG",
    "Product reviews on social media",
    "Warehouse inventory reports"
  ),
  Internal_External = c(
    "Internal",
    "External",
    "External",
    "Internal"
  ),
  Structured_Unstructured = c(
    "Structured",
    "Structured",
    "Unstructured",
    "Structured"
  ),
  stringsAsFactors = FALSE
)

# Display the data frame as a neat table
datatable(data_sources, 
          caption = "Table of Data Sources",
          rownames = FALSE) # hides the index column

4 Exercise 4

Dataset Structure: Consider the following transaction table:

Date Qty Price Product CustomerTier
2025-10-01 2 1000 Laptop High
2025-10-01 5 20 Mouse Medium
2025-10-02 1 1000 Laptop Low
2025-10-02 3 30 Keyboard Medium
2025-10-03 4 50 Mouse Medium
2025-10-03 2 1000 Laptop High
2025-10-04 6 25 Keyboard Low
2025-10-04 1 1000 Laptop High
2025-10-05 3 40 Mouse Low
2025-10-05 5 10 Keyboard Medium

Your Assignment Instructions: Creating a Transactions Table above in R

  1. Create a data frame in R called transactions containing the data above.

  2. Identify which variables are numeric and which are categorical

  3. Calculate total revenue for each transaction by multiplying Qty × Price and add it as a new column Total.

  4. Compute summary statistics:

    • Total quantity sold for each product
    • Total revenue per product
    • Average price per product
  5. Visualize the data:

    • Create a barplot showing total quantity sold per product.
    • Create a pie chart showing the proportion of total revenue per customer tier.
  6. Optional Challenge:

    • Find which date had the highest total revenue.
    • Create a stacked bar chart showing quantity sold per product by customer tier.

Hints: Use data.frame(), aggregate(), barplot(), pie(), and basic arithmetic operations in R.

library(DT)
# 4.1 Transaction
transactions <- data.frame(
  No = 1 : 10,
  
  Date = c("2025-10-01", "2025-10-01", "2025-10-02", "2025-10-02",
            "2025-10-03","2025-10-03", "2025-10-04", "2025-10-04", 
            "2025-10-05", "2025-10-05"),
  
  Qty = c(2,5,1,3,4,2,6,1,3,5),
  
  Price = c(1000,20,1000,30,50,1000,25,1000,40,10),
  
  Product = c("Laptop", "Mouse", "Laptop", "Keyboard", "Mouse",
               "Laptop", "Keyboard", "Laptop", "Mouse", "Keyboard"),
  
  CostumerTier = c("High", "Medium", "Low", "Medium", "Medium",
                    "High", "Low", "High", "Low", "Medium")
  )
library(knitr)
# 4.2 Create a data frame for Data Types
variables_info <- data.frame(
  No = 1:4,
  Variable = c(
    "Qty",
    "Price",
    "Product",
    "Customertier"
  ),
  DataType = c(
    "Numeric",
    "Numeric",
    "Categorical",
    "Categorical"
  ),
  stringAsFactors = FALSE
)

library(knitr)
# 4.3 Display the data table as a neat table
kable(variables_info,
      caption = "Table of Variables and Data Types")
Table of Variables and Data Types
No Variable DataType stringAsFactors
1 Qty Numeric FALSE
2 Price Numeric FALSE
3 Product Categorical FALSE
4 Customertier Categorical FALSE
#4.3 Transactions Total
transactions$Total <- transactions$Qty * transactions$Price
datatable(transactions, 
          caption = "Table of Transaction",
          rownames = FALSE)
library(knitr)
# 4.4 Compute Summary Statistics
# Total Qty per Product
total_qty_per_product <- aggregate(Qty ~ Product, data = transactions, FUN = sum)
print("Total Kuantitas Terjual per Produk:")
## [1] "Total Kuantitas Terjual per Produk:"
print(total_qty_per_product)
##    Product Qty
## 1 Keyboard  14
## 2   Laptop   6
## 3    Mouse  12
# Total Revenue per Product
total_revenue_per_product <- aggregate(Total ~ Product, data = transactions, FUN = sum)
print("Total Pendapatan per Produk:")
## [1] "Total Pendapatan per Produk:"
print(total_revenue_per_product)
##    Product Total
## 1 Keyboard   290
## 2   Laptop  6000
## 3    Mouse   420
# Total Revenue per Customer Tier
total_revenue_per_tier <- aggregate(Total ~ CostumerTier, data = transactions, FUN = sum)
print("Total Pendapatan Per Pelanggan:")
## [1] "Total Pendapatan Per Pelanggan:"
print(total_revenue_per_tier)
##   CostumerTier Total
## 1         High  5000
## 2          Low  1270
## 3       Medium   440
# Average Price per Product
average_price_per_product <- aggregate(Price ~ Product, data = transactions, FUN = mean)
print("Harga Rata-rata per Produk")
## [1] "Harga Rata-rata per Produk"
print(average_price_per_product)
##    Product      Price
## 1 Keyboard   21.66667
## 2   Laptop 1000.00000
## 3    Mouse   36.66667
library(knitr)
# 4.5 Visualize Data
barplot(
  height = total_qty_per_product$Qty, 
  names.arg = total_qty_per_product$Product,
  col = c("pink", "blue", "green"),
  main = "Total Transactions",
  xlab = "Product",
  ylab = "Quantity",
  ylim = c(0, max(total_qty_per_product$Qty) + 2))

#Pie chart
revenue_per_tier <- aggregate(Total ~ CostumerTier, data = transactions, FUN = sum)
total_revenue <- sum(revenue_per_tier$Total)
percentages <- round(revenue_per_tier$Total / total_revenue * 100, 1)
pie_labels <- paste(revenue_per_tier$CostumerTier, "(", percentages, "%)", sep = "")

pie(
  x = revenue_per_tier$Total,
  labels = pie_labels,
  main = "Proportion of Total Revenue per CustomerTier",
  col = c("purple", "yellow", "red")
)

library(knitr)
# 4.6 Find the Highest date of total revenue
transactions$Revenue <- transactions$Price * transactions$Qty
total_per_date <- aggregate(Revenue ~ Date, data = transactions, FUN = sum)
highest <- total_per_date[which.max(total_per_date$Revenue), ]
print("The highest data of total Revenue")
## [1] "The highest data of total Revenue"
print(total_per_date)
##         Date Revenue
## 1 2025-10-01    2100
## 2 2025-10-02    1090
## 3 2025-10-03    2200
## 4 2025-10-04    1150
## 5 2025-10-05     170
print(highest)
##         Date Revenue
## 3 2025-10-03    2200
# Stacked Bar Chart
table_transactions <- with(transactions, tapply(Qty, list(Product, CostumerTier), sum, na.rm = TRUE))
table_transactions[is.na(table_transactions)] <- 0
barplot(
  t(table_transactions),
  beside = FALSE,
  col = c("brown", "gold", "orange"),
  main = "Quantity Sold per Product by CustomerTier",
  xlab = "Product",
  ylab = "Quantity Sold",
  legend.text = TRUE)

5 Exercise 5

Create Your Own Data Frame:

Objective: Create a data frame in R with 30 rows containing a mix of data types: continuous, discrete, nominal, and ordinal.

5.1 Instructions

  1. Open RStudio or the R console.

  2. Create a vector for each column in your data frame:

    • Date: 30 dates (can be sequential or random within a month/year)
    • Continuous: numeric values that can take decimal values (e.g., height, weight, temperature)
    • Discrete: numeric values that can only take whole numbers (e.g., number of items, number of vehicles)
    • Nominal: categorical values with no order (e.g., color, gender, city)
    • Ordinal: categorical values with a defined order (e.g., Low, Medium, High; Beginner, Intermediate, Expert)
  3. Combine all vectors into a data frame called my_data.

  4. Check your data frame using head() or View() to ensure it has 30 rows and the columns are correct.

  5. Optional tasks:

    • Summarize each column using summary()
    • Count the frequency of each category for Nominal and Ordinal columns using table()
library(DT)
library(knitr)
# 5.1 Create a vector for each column
set.seed(50)
tanggal <- as.Date("2024-01-01") + sample(0:365, 30, replace = TRUE)
hewan <- c("Kucing", "Anjing", "Kelinci", "Burung",
           "Kuda", "Gajah", "Singa", "Harimau", "Zebra", "Panda", "Rusa",
           "Unta", "Kudanil", "Badak", "Beruang", "Monyet", "Gorila", "Kanguru",
           "Koala", "Serigala", "Kucing", "Anjing", "Burung", "Gajah", "Singa", 
           "Harimau", "Zebra", "Panda", "Rusa", "Unta")
hewan <- factor(hewan)

jumlah_porsi_makanan <- c(5, 5, 3, 4, 3, 
                  3, 5, 5, 5, 6, 
                  4, 4, 6, 6, 5, 
                  5, 6, 4, 4, 5, 
                  5, 4, 5, 6, 7, 
                  6, 5, 6, 4, 4)

berat <- c(24.56, 48.76, 15.48, 9.64, 289.67, 785.54, 293.27, 257.38, 158.35, 
           93.32, 82.67, 632.75, 429.71, 836.54, 274.68, 18.56, 467.82, 152.74,
           52.85, 172.92, 38.56, 95.27, 6.32, 364.78, 284.92, 256.85, 193.65,
           123.45, 110.43, 576.98)

kelincahan <- c("Tinggi", "Tinggi", "Sedang", "Tinggi", "Sedang", "Rendah", "Tinggi",
                "Tinggi", "Sedang", "Sedang", "Sedang", "Rendah", "Rendah", "Rendah",
                "Sedang", "Tinggi", "Rendah", "Tinggi", "Sedang", "Tinggi", "Sedang", 
                "Tinggi", "Tinggi", "Sedang", "Tinggi", "Tinggi", "Sedang", "Sedang",
                "Sedang", "Rendah")
kelincahan <- factor(kelincahan,
                     levels = c("Rendah", "Sedang", "Tinggi"),
                     ordered = TRUE)

# 5.2 Combine all vectors into a data frame
Data_Hewan <- data.frame(tanggal, hewan, jumlah_porsi_makanan, berat, kelincahan)

# 5.3 Create a data frame as a neat table
datatable(Data_Hewan, caption = "Rekapitulasi Data Hewan: Jenis, Berat dan Kelincahan", options = list(pageLength = 10))
# 5.3 Create a table of variable types
variables_info2 <- data.frame(
  No = 1:4,
  Variable = c(
    "Hewan",
    "JumlahPorsiMakanan",
    "Berat",
    "Kelincahan"),
  DataType = c(
    "Nominal",
    "Diskrit",
    "Continuous",
    "Ordinal"),
  stringsAsFactors = FALSE
)

knitr::kable(variables_info2, caption = "Table of Variable and Data Type")
Table of Variable and Data Type
No Variable DataType
1 Hewan Nominal
2 JumlahPorsiMakanan Diskrit
3 Berat Continuous
4 Kelincahan Ordinal
# 5.4 Summarize each column
summary(Data_Hewan)
##     tanggal               hewan    jumlah_porsi_makanan     berat       
##  Min.   :2024-01-07   Anjing : 2   Min.   :3.000        Min.   :  6.32  
##  1st Qu.:2024-04-04   Burung : 2   1st Qu.:4.000        1st Qu.: 60.30  
##  Median :2024-09-06   Gajah  : 2   Median :5.000        Median :165.63  
##  Mean   :2024-07-21   Harimau: 2   Mean   :4.833        Mean   :238.28  
##  3rd Qu.:2024-10-28   Kucing : 2   3rd Qu.:5.750        3rd Qu.:292.37  
##  Max.   :2024-12-28   Panda  : 2   Max.   :7.000        Max.   :836.54  
##                       (Other):18                                        
##   kelincahan
##  Rendah: 6  
##  Sedang:12  
##  Tinggi:12  
##             
##             
##             
## 
summary(transactions)
##        No            Date                Qty           Price        
##  Min.   : 1.00   Length:10          Min.   :1.00   Min.   :  10.00  
##  1st Qu.: 3.25   Class :character   1st Qu.:2.00   1st Qu.:  26.25  
##  Median : 5.50   Mode  :character   Median :3.00   Median :  45.00  
##  Mean   : 5.50                      Mean   :3.20   Mean   : 417.50  
##  3rd Qu.: 7.75                      3rd Qu.:4.75   3rd Qu.:1000.00  
##  Max.   :10.00                      Max.   :6.00   Max.   :1000.00  
##    Product          CostumerTier           Total         Revenue    
##  Length:10          Length:10          Min.   :  50   Min.   :  50  
##  Class :character   Class :character   1st Qu.: 105   1st Qu.: 105  
##  Mode  :character   Mode  :character   Median : 175   Median : 175  
##                                        Mean   : 671   Mean   : 671  
##                                        3rd Qu.:1000   3rd Qu.:1000  
##                                        Max.   :2000   Max.   :2000
summary_table <- summary(Data_Hewan)
knitr::kable(as.data.frame(summary_table), caption = "Summary of Data Hewan")
Summary of Data Hewan
Var1 Var2 Freq
tanggal Min. :2024-01-07
tanggal 1st Qu.:2024-04-04
tanggal Median :2024-09-06
tanggal Mean :2024-07-21
tanggal 3rd Qu.:2024-10-28
tanggal Max. :2024-12-28
tanggal NA
hewan Anjing : 2
hewan Burung : 2
hewan Gajah : 2
hewan Harimau: 2
hewan Kucing : 2
hewan Panda : 2
hewan (Other):18
jumlah_porsi_makanan Min. :3.000
jumlah_porsi_makanan 1st Qu.:4.000
jumlah_porsi_makanan Median :5.000
jumlah_porsi_makanan Mean :4.833
jumlah_porsi_makanan 3rd Qu.:5.750
jumlah_porsi_makanan Max. :7.000
jumlah_porsi_makanan NA
berat Min. : 6.32
berat 1st Qu.: 60.30
berat Median :165.63
berat Mean :238.28
berat 3rd Qu.:292.37
berat Max. :836.54
berat NA
kelincahan Rendah: 6
kelincahan Sedang:12
kelincahan Tinggi:12
kelincahan NA
kelincahan NA
kelincahan NA
kelincahan NA
# 5.4 Count the frequency of each category for **Nominal** and **Ordinal** columns
list(
  Nominal = table(Data_Hewan$hewan),
  Ordinal = table(Data_Hewan$kelincahan)
)
## $Nominal
## 
##   Anjing    Badak  Beruang   Burung    Gajah   Gorila  Harimau  Kanguru 
##        2        1        1        2        2        1        2        1 
##  Kelinci    Koala   Kucing     Kuda  Kudanil   Monyet    Panda     Rusa 
##        1        1        2        1        1        1        2        2 
## Serigala    Singa     Unta    Zebra 
##        1        2        2        2 
## 
## $Ordinal
## 
## Rendah Sedang Tinggi 
##      6     12     12
LS0tDQp0aXRsZTogIkRhdGEgRXhwbG9yYXRpb24iICAgICAgICMgTWFpbiB0aXRsZSBvZiB0aGUgZG9jdW1lbnQNCnN1YnRpdGxlOiAiRXhlcmNpc2VzIH4gV2VlayAzIiAgIyBTdWJ0aXRsZSBvciB0b3BpYyBmb3Igd2VlayAyDQphdXRob3I6IA0KLSAiS2F5bGEgQXByaWxpYSIgICANCi0gIk9jdGF2aWEgTWFpYSBSZWdvIg0KLSAiSmFudWFyaWEgVGVyZXNpbmhhIg0KLSAiQW5kcmUiDQotICJBZGFtIFJpY2hpZSBXaWpheWEiDQotICJNb3JyaXMgQWxleGFuZGVyIFBhbmdhcmlidWFuIiMgUmVwbGFjZSB3aXRoIHlvdXIgZnVsbCBuYW1lDQpkYXRlOiAgImByIGZvcm1hdChTeXMuRGF0ZSgpLCAnJUIgJWQsICVZJylgIiAjIEF1dG8gZGlzcGxheXMgdGhlIGN1cnJlbnQgZGF0ZQ0Kb3V0cHV0OiAgICAgICAgICAgICAgICAgICAgICAgICAjIE91dHB1dCBzZWN0aW9uIGRlZmluZXMgdGhlIGZvcm1hdCBhbmQgbGF5b3V0IA0KICBybWRmb3JtYXRzOjpyZWFkdGhlZG93bjogICAgICAjIGh0dHBzOi8vZ2l0aHViLmNvbS9qdWJhL3JtZGZvcm1hdHMNCiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZSAgICAgICAgIyBFbWJlZHMgYWxsIHJlc291cmNlcyAoQ1NTLCBKUywgaW1hZ2VzKSANCiAgICB0aHVtYm5haWxzOiB0cnVlICAgICAgICAgICAgIyBEaXNwbGF5cyBpbWFnZSB0aHVtYm5haWxzIGluIHRoZSBkb2MNCiAgICBsaWdodGJveDogdHJ1ZSAgICAgICAgICAgICAgIyBFbmFibGVzIGNsaWNrIHRvIGVubGFyZ2UgaW1hZ2VzDQogICAgZ2FsbGVyeTogdHJ1ZSAgICAgICAgICAgICAgICMgR3JvdXBzIGltYWdlcyBpbnRvIGFuIGludGVyYWN0aXZlIGdhbGxlcnkNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUgICAgICAgIyBBdXRvbWF0aWNhbGx5IG51bWJlcnMgYWxsIHNlY3Rpb25zDQogICAgbGliX2RpcjogbGlicyAgICAgICAgICAgICAgICMgRGlyZWN0b3J5IHdoZXJlIEphdmFTY3JpcHQvQ1NTIGxpYnJhcmllcw0KICAgIGRmX3ByaW50OiAicGFnZWQiICAgICAgICAgICAjIERpc3BsYXlzIGRhdGEgZnJhbWVzIGFzIGludGVyYWN0aXZlIHBhZ2VkIA0KICAgIGNvZGVfZm9sZGluZzogInNob3ciICAgICAgICAjIEFsbG93cyBmb2xkaW5nL3VuZm9sZGluZyBSIGNvZGUgYmxvY2tzIA0KICAgIGNvZGVfZG93bmxvYWQ6IHllcyAgICAgICAgICAjIEFkZHMgYSBidXR0b24gdG8gZG93bmxvYWQgYWxsIFIgY29kZQ0KLS0tDQoNCg0KPGltZyBpZD0iRm90byIgc3JjPSJodHRwczovL2dpdGh1Yi5jb20vZHNjaWVuY2VsYWJzL2ltYWdlcy9ibG9iL21hc3Rlci9Mb2dvX0RzY2llbmNlbGFic192MS5wbmc/cmF3PXRydWUiIGFsdD0iTG9nbyIgc3R5bGU9IndpZHRoOjIwMHB4OyBkaXNwbGF5OiBibG9jazsgbWFyZ2luOiBhdXRvOyI+DQoNCi0tLQ0KDQojIyBFeGVyY2lzZSAxDQoNClRoZSBmb2xsb3dpbmcgdGFibGUgc2hvd3Mgc2FtcGxlIGluZm9ybWF0aW9uIGZvciB0aHJlZSBzdHVkZW50cy4gRWFjaCBvYnNlcnZhdGlvbiByZXByZXNlbnRzIGEgc2luZ2xlIHN0dWRlbnQgYW5kIGluY2x1ZGVzIGRldGFpbHMgc3VjaCBhcyB0aGVpciB1bmlxdWUgc3R1ZGVudCBJRCwgbmFtZSwgYWdlLCB0b3RhbCBjcmVkaXRzIGNvbXBsZXRlZCwgbWFqb3IgZmllbGQgb2Ygc3R1ZHksIGFuZCB5ZWFyIGxldmVsLiAgDQoNClRoaXMgZGF0YXNldCBkZW1vbnN0cmF0ZXMgYSBtaXh0dXJlIG9mIHZhcmlhYmxlIHR5cGVzOiAgDQoNCi0gKipOb21pbmFsOioqIFN0dWRlbnRJRCwgTmFtZSwgTWFqb3IgIA0KLSAqKk51bWVyaWM6KiogQWdlIChjb250aW51b3VzKSwgQ3JlZGl0c0NvbXBsZXRlZCAoZGlzY3JldGUpICANCi0gKipPcmRpbmFsOioqIFllYXJMZXZlbCAoRnJlc2htYW4g4oaSIFNlbmlvcikgIA0KDQp8IFN0dWRlbnRJRCB8IE5hbWUgICB8IEFnZSB8IENyZWRpdHNDb21wbGV0ZWQgfCBNYWpvciAgICAgICAgICAgIHwgWWVhckxldmVsIHwNCnwtLS0tLS0tLS0tLXwtLS0tLS0tLXwtLS0tLXwtLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLXwNCnwgUzAwMSAgICAgIHwgQWxpY2UgIHwgMjAgIHwgNDUgICAgICAgICAgICAgIHwgRGF0YSBTYWlucyAgICAgIHwgU29waG9tb3JlIHwNCnwgUzAwMiAgICAgIHwgQnVkaSAgIHwgMjEgIHwgNjAgICAgICAgICAgICAgIHwgTWF0aGVtYXRpY3MgICAgIHwgSnVuaW9yICAgIHwNCnwgUzAwMyAgICAgIHwgQ2l0cmEgIHwgMTkgIHwgMzAgICAgICAgICAgICAgIHwgU3RhdGlzdGljcyAgICAgIHwgRnJlc2htYW4gIHwNCg0KYGBge3J9DQojIDEuIENyZWF0ZSB2ZWN0b3JzIGZvciBlYWNoIHZhcmlhYmxlDQpTdHVkZW50SUQgPC0gYygiUzAwMSIsICJTMDAyIiwgIlMwMDMiKSAgICAgICAjIE5vbWluYWwgLyBJRA0KTmFtZSA8LSBjKCJBbGljZSIsICJCdWRpIiwgIkNpdHJhIikgICAgICAgICAgIyBOb21pbmFsIC8gTmFtZQ0KQWdlIDwtIGMoMjAsIDIxLCAxOSkgICAgICAgICAgICAgICAgICAgICAgICAgIyBOdW1lcmljIC8gQ29udGludW91cw0KQ3JlZGl0c0NvbXBsZXRlZCA8LSBjKDQ1LCA2MCwgMzApICAgICAgICAgICAgIyBOdW1lcmljIC8gRGlzY3JldGUNCg0KIyBOb21pbmFsDQpNYWpvciA8LSBjKCJEYXRhIFNhaW5zIiwgIk1hdGhlbWF0aWNzIiwgIlN0YXRpc3RpY3MiKSAgDQoNCiMgT3JkaW5hbA0KWWVhckxldmVsIDwtIGZhY3RvcihjKCJTb3Bob21vcmUiLCAiSnVuaW9yIiwgIkZyZXNobWFuIiksDQogICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkZyZXNobWFuIiwiU29waG9tb3JlIiwiSnVuaW9yIiwiU2VuaW9yIiksDQogICAgICAgICAgICAgICAgICAgIG9yZGVyZWQgPSBUUlVFKSAgICAgICAgICANCg0KIyAyLiBDb21iaW5lIGFsbCB2ZWN0b3JzIGludG8gYSBkYXRhIGZyYW1lDQpzdHVkZW50cyA8LSBkYXRhLmZyYW1lKA0KICBTdHVkZW50SUQsIE5hbWUsIEFnZSwgQ3JlZGl0c0NvbXBsZXRlZCwgTWFqb3IsIFllYXJMZXZlbCwNCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFDQopDQoNCiMgMy4gRGlzcGxheSB0aGUgZGF0YSBmcmFtZQ0KcHJpbnQoc3R1ZGVudHMpDQpgYGANCiMjIEV4ZXJjaXNlIDINCg0KKipJZGVudGlmeSBEYXRhIFR5cGVzOioqIERldGVybWluZSB0aGUgdHlwZSBvZiBkYXRhIGZvciBlYWNoIG9mIHRoZSBmb2xsb3dpbmcgdmFyaWFibGVzOg0KDQpgYGB7cn0NCiMgSW5zdGFsbCBrbml0ciBwYWNrYWdlIGlmIG5vdCBhbHJlYWR5IGluc3RhbGxlZA0KIyBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpDQpsaWJyYXJ5KGtuaXRyKQ0KDQojIENyZWF0ZSBhIGRhdGEgZnJhbWUgZm9yIERhdGEgVHlwZXMNCnZhcmlhYmxlc19pbmZvIDwtIGRhdGEuZnJhbWUoDQogIE5vID0gMTo1LA0KICBWYXJpYWJsZSA9IGMoDQogICAgIk51bWJlciBvZiB2ZWhpY2xlcyBwYXNzaW5nIHRocm91Z2ggdGhlIHRvbGwgcm9hZCBlYWNoIGRheSIsDQogICAgIlN0dWRlbnQgaGVpZ2h0IGluIGNtIiwNCiAgICAiRW1wbG95ZWUgZ2VuZGVyIChNYWxlIC8gRmVtYWxlKSIsDQogICAgIkN1c3RvbWVyIHNhdGlzZmFjdGlvbiBsZXZlbDogTG93LCBNZWRpdW0sIEhpZ2giLA0KICAgICJSZXNwb25kZW50J3MgZmF2b3JpdGUgY29sb3I6IFJlZCwgQmx1ZSwgR3JlZW4iDQogICksDQogIERhdGFUeXBlID0gYygNCiAgICAiTnVtZXJpYyIsDQogICAgIk51bWVyaWMiLA0KICAgICJDYXRlZ29yaWNhbCIsDQogICAgIkNhdGVnb3JpY2FsIiwNCiAgICAiQ2F0ZWdvcmljYWwiDQogICksDQogIFN1YnR5cGUgPSBjKA0KICAgICJDb250aW51b3VzIiwNCiAgICAiQ29udGludW91cyIsDQogICAgIk5vbWluYWwiLA0KICAgICJPcmRpbmFsIiwNCiAgICAiTm9taW5hbCINCiAgKSwNCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFDQopDQoNCiMgRGlzcGxheSB0aGUgZGF0YSBmcmFtZSBhcyBhIG5lYXQgdGFibGUNCmthYmxlKHZhcmlhYmxlc19pbmZvLCANCiAgICAgIGNhcHRpb24gPSAiVGFibGUgb2YgVmFyaWFibGVzIGFuZCBEYXRhIFR5cGVzIikNCmBgYA0KLS0tDQoNCiMjIEV4ZXJjaXNlIDMNCg0KKipDbGFzc2lmeSBEYXRhIFNvdXJjZXM6KiogRGV0ZXJtaW5lIHdoZXRoZXIgdGhlIGZvbGxvd2luZyBkYXRhIGNvbWVzIGZyb20gKippbnRlcm5hbCoqIG9yICoqZXh0ZXJuYWwgc291cmNlcyoqLCBhbmQgd2hldGhlciBpdCBpcyAqKnN0cnVjdHVyZWQqKiBvciAqKnVuc3RydWN0dXJlZCoqOg0KDQpgYGB7cn0NCiMgSW5zdGFsbCBEVCBwYWNrYWdlIGlmIG5vdCBhbHJlYWR5IGluc3RhbGxlZA0KIyBpbnN0YWxsLnBhY2thZ2VzKCJEVCIpDQpsaWJyYXJ5KERUKQ0KDQojIENyZWF0ZSBhIGRhdGEgZnJhbWUgZm9yIGRhdGEgc291cmNlcyANCmRhdGFfc291cmNlcyA8LSBkYXRhLmZyYW1lKA0KICBObyA9IDE6NCwNCiAgRGF0YVNvdXJjZSA9IGMoDQogICAgIkRhaWx5IHNhbGVzIHRyYW5zYWN0aW9uIGRhdGEgb2YgdGhlIGNvbXBhbnkiLA0KICAgICJXZWF0aGVyIHJlcG9ydHMgZnJvbSBCTUtHIiwNCiAgICAiUHJvZHVjdCByZXZpZXdzIG9uIHNvY2lhbCBtZWRpYSIsDQogICAgIldhcmVob3VzZSBpbnZlbnRvcnkgcmVwb3J0cyINCiAgKSwNCiAgSW50ZXJuYWxfRXh0ZXJuYWwgPSBjKA0KICAgICJJbnRlcm5hbCIsDQogICAgIkV4dGVybmFsIiwNCiAgICAiRXh0ZXJuYWwiLA0KICAgICJJbnRlcm5hbCINCiAgKSwNCiAgU3RydWN0dXJlZF9VbnN0cnVjdHVyZWQgPSBjKA0KICAgICJTdHJ1Y3R1cmVkIiwNCiAgICAiU3RydWN0dXJlZCIsDQogICAgIlVuc3RydWN0dXJlZCIsDQogICAgIlN0cnVjdHVyZWQiDQogICksDQogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQ0KKQ0KDQojIERpc3BsYXkgdGhlIGRhdGEgZnJhbWUgYXMgYSBuZWF0IHRhYmxlDQpkYXRhdGFibGUoZGF0YV9zb3VyY2VzLCANCiAgICAgICAgICBjYXB0aW9uID0gIlRhYmxlIG9mIERhdGEgU291cmNlcyIsDQogICAgICAgICAgcm93bmFtZXMgPSBGQUxTRSkgIyBoaWRlcyB0aGUgaW5kZXggY29sdW1uDQpgYGANCg0KLS0tDQoNCiMjIEV4ZXJjaXNlIDQNCg0KKipEYXRhc2V0IFN0cnVjdHVyZToqKiBDb25zaWRlciB0aGUgZm9sbG93aW5nIHRyYW5zYWN0aW9uIHRhYmxlOg0KDQp8IERhdGUgICAgICAgfCBRdHkgfCBQcmljZSB8IFByb2R1Y3QgIHwgQ3VzdG9tZXJUaWVyIHwNCnwtLS0tLS0tLS0tLS18LS0tLS18LS0tLS0tLXwtLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tfA0KfCAyMDI1LTEwLTAxIHwgMiAgIHwgMTAwMCAgfCBMYXB0b3AgICB8IEhpZ2ggICAgICAgICB8DQp8IDIwMjUtMTAtMDEgfCA1ICAgfCAyMCAgICB8IE1vdXNlICAgIHwgTWVkaXVtICAgICAgIHwNCnwgMjAyNS0xMC0wMiB8IDEgICB8IDEwMDAgIHwgTGFwdG9wICAgfCBMb3cgICAgICAgICAgfA0KfCAyMDI1LTEwLTAyIHwgMyAgIHwgMzAgICAgfCBLZXlib2FyZCB8IE1lZGl1bSAgICAgICB8DQp8IDIwMjUtMTAtMDMgfCA0ICAgfCA1MCAgICB8IE1vdXNlICAgIHwgTWVkaXVtICAgICAgIHwNCnwgMjAyNS0xMC0wMyB8IDIgICB8IDEwMDAgIHwgTGFwdG9wICAgfCBIaWdoICAgICAgICAgfA0KfCAyMDI1LTEwLTA0IHwgNiAgIHwgMjUgICAgfCBLZXlib2FyZCB8IExvdyAgICAgICAgICB8DQp8IDIwMjUtMTAtMDQgfCAxICAgfCAxMDAwICB8IExhcHRvcCAgIHwgSGlnaCAgICAgICAgIHwNCnwgMjAyNS0xMC0wNSB8IDMgICB8IDQwICAgIHwgTW91c2UgICAgfCBMb3cgICAgICAgICAgfA0KfCAyMDI1LTEwLTA1IHwgNSAgIHwgMTAgICAgfCBLZXlib2FyZCB8IE1lZGl1bSAgICAgICB8DQoNCg0KKipZb3VyIEFzc2lnbm1lbnQgSW5zdHJ1Y3Rpb25zOioqIENyZWF0aW5nIGEgVHJhbnNhY3Rpb25zIFRhYmxlIGFib3ZlIGluIFINCg0KMS4gKipDcmVhdGUgYSBkYXRhIGZyYW1lKiogaW4gUiBjYWxsZWQgYHRyYW5zYWN0aW9uc2AgY29udGFpbmluZyB0aGUgZGF0YSBhYm92ZS4NCg0KMi4gSWRlbnRpZnkgd2hpY2ggdmFyaWFibGVzIGFyZSBudW1lcmljIGFuZCB3aGljaCBhcmUgY2F0ZWdvcmljYWwNCg0KMy4gKipDYWxjdWxhdGUgdG90YWwgcmV2ZW51ZSoqIGZvciBlYWNoIHRyYW5zYWN0aW9uIGJ5IG11bHRpcGx5aW5nIGBRdHkgw5cgUHJpY2VgIGFuZCBhZGQgaXQgYXMgYSBuZXcgY29sdW1uIGBUb3RhbGAuDQoNCjQuICoqQ29tcHV0ZSBzdW1tYXJ5IHN0YXRpc3RpY3MqKjoNCiAgIC0gVG90YWwgcXVhbnRpdHkgc29sZCBmb3IgZWFjaCBwcm9kdWN0DQogICAtIFRvdGFsIHJldmVudWUgcGVyIHByb2R1Y3QNCiAgIC0gQXZlcmFnZSBwcmljZSBwZXIgcHJvZHVjdA0KDQo1LiAqKlZpc3VhbGl6ZSB0aGUgZGF0YSoqOg0KICAgLSBDcmVhdGUgYSAqKmJhcnBsb3QqKiBzaG93aW5nIHRvdGFsIHF1YW50aXR5IHNvbGQgcGVyIHByb2R1Y3QuDQogICAtIENyZWF0ZSBhICoqcGllIGNoYXJ0Kiogc2hvd2luZyB0aGUgcHJvcG9ydGlvbiBvZiB0b3RhbCByZXZlbnVlIHBlciBjdXN0b21lciB0aWVyLg0KDQo2LiAqKk9wdGlvbmFsIENoYWxsZW5nZSoqOg0KICAgLSBGaW5kIHdoaWNoICoqZGF0ZSoqIGhhZCB0aGUgaGlnaGVzdCB0b3RhbCByZXZlbnVlLg0KICAgLSBDcmVhdGUgYSAqKnN0YWNrZWQgYmFyIGNoYXJ0Kiogc2hvd2luZyBxdWFudGl0eSBzb2xkIHBlciBwcm9kdWN0IGJ5IGN1c3RvbWVyIHRpZXIuDQoNCioqSGludHM6KiogVXNlIGBkYXRhLmZyYW1lKClgLCBgYWdncmVnYXRlKClgLCBgYmFycGxvdCgpYCwgYHBpZSgpYCwgYW5kIGJhc2ljIGFyaXRobWV0aWMgb3BlcmF0aW9ucyBpbiBSLg0KDQpgYGB7cn0NCmxpYnJhcnkoRFQpDQojIDQuMSBUcmFuc2FjdGlvbg0KdHJhbnNhY3Rpb25zIDwtIGRhdGEuZnJhbWUoDQogIE5vID0gMSA6IDEwLA0KICANCiAgRGF0ZSA9IGMoIjIwMjUtMTAtMDEiLCAiMjAyNS0xMC0wMSIsICIyMDI1LTEwLTAyIiwgIjIwMjUtMTAtMDIiLA0KICAgICAgICAgICAgIjIwMjUtMTAtMDMiLCIyMDI1LTEwLTAzIiwgIjIwMjUtMTAtMDQiLCAiMjAyNS0xMC0wNCIsIA0KICAgICAgICAgICAgIjIwMjUtMTAtMDUiLCAiMjAyNS0xMC0wNSIpLA0KICANCiAgUXR5ID0gYygyLDUsMSwzLDQsMiw2LDEsMyw1KSwNCiAgDQogIFByaWNlID0gYygxMDAwLDIwLDEwMDAsMzAsNTAsMTAwMCwyNSwxMDAwLDQwLDEwKSwNCiAgDQogIFByb2R1Y3QgPSBjKCJMYXB0b3AiLCAiTW91c2UiLCAiTGFwdG9wIiwgIktleWJvYXJkIiwgIk1vdXNlIiwNCiAgICAgICAgICAgICAgICJMYXB0b3AiLCAiS2V5Ym9hcmQiLCAiTGFwdG9wIiwgIk1vdXNlIiwgIktleWJvYXJkIiksDQogIA0KICBDb3N0dW1lclRpZXIgPSBjKCJIaWdoIiwgIk1lZGl1bSIsICJMb3ciLCAiTWVkaXVtIiwgIk1lZGl1bSIsDQogICAgICAgICAgICAgICAgICAgICJIaWdoIiwgIkxvdyIsICJIaWdoIiwgIkxvdyIsICJNZWRpdW0iKQ0KICApDQpsaWJyYXJ5KGtuaXRyKQ0KIyA0LjIgQ3JlYXRlIGEgZGF0YSBmcmFtZSBmb3IgRGF0YSBUeXBlcw0KdmFyaWFibGVzX2luZm8gPC0gZGF0YS5mcmFtZSgNCiAgTm8gPSAxOjQsDQogIFZhcmlhYmxlID0gYygNCiAgICAiUXR5IiwNCiAgICAiUHJpY2UiLA0KICAgICJQcm9kdWN0IiwNCiAgICAiQ3VzdG9tZXJ0aWVyIg0KICApLA0KICBEYXRhVHlwZSA9IGMoDQogICAgIk51bWVyaWMiLA0KICAgICJOdW1lcmljIiwNCiAgICAiQ2F0ZWdvcmljYWwiLA0KICAgICJDYXRlZ29yaWNhbCINCiAgKSwNCiAgc3RyaW5nQXNGYWN0b3JzID0gRkFMU0UNCikNCg0KbGlicmFyeShrbml0cikNCiMgNC4zIERpc3BsYXkgdGhlIGRhdGEgdGFibGUgYXMgYSBuZWF0IHRhYmxlDQprYWJsZSh2YXJpYWJsZXNfaW5mbywNCiAgICAgIGNhcHRpb24gPSAiVGFibGUgb2YgVmFyaWFibGVzIGFuZCBEYXRhIFR5cGVzIikNCg0KIzQuMyBUcmFuc2FjdGlvbnMgVG90YWwNCnRyYW5zYWN0aW9ucyRUb3RhbCA8LSB0cmFuc2FjdGlvbnMkUXR5ICogdHJhbnNhY3Rpb25zJFByaWNlDQpkYXRhdGFibGUodHJhbnNhY3Rpb25zLCANCiAgICAgICAgICBjYXB0aW9uID0gIlRhYmxlIG9mIFRyYW5zYWN0aW9uIiwNCiAgICAgICAgICByb3duYW1lcyA9IEZBTFNFKQ0KDQpsaWJyYXJ5KGtuaXRyKQ0KIyA0LjQgQ29tcHV0ZSBTdW1tYXJ5IFN0YXRpc3RpY3MNCiMgVG90YWwgUXR5IHBlciBQcm9kdWN0DQp0b3RhbF9xdHlfcGVyX3Byb2R1Y3QgPC0gYWdncmVnYXRlKFF0eSB+IFByb2R1Y3QsIGRhdGEgPSB0cmFuc2FjdGlvbnMsIEZVTiA9IHN1bSkNCnByaW50KCJUb3RhbCBLdWFudGl0YXMgVGVyanVhbCBwZXIgUHJvZHVrOiIpDQpwcmludCh0b3RhbF9xdHlfcGVyX3Byb2R1Y3QpDQoNCiMgVG90YWwgUmV2ZW51ZSBwZXIgUHJvZHVjdA0KdG90YWxfcmV2ZW51ZV9wZXJfcHJvZHVjdCA8LSBhZ2dyZWdhdGUoVG90YWwgfiBQcm9kdWN0LCBkYXRhID0gdHJhbnNhY3Rpb25zLCBGVU4gPSBzdW0pDQpwcmludCgiVG90YWwgUGVuZGFwYXRhbiBwZXIgUHJvZHVrOiIpDQpwcmludCh0b3RhbF9yZXZlbnVlX3Blcl9wcm9kdWN0KQ0KDQojIFRvdGFsIFJldmVudWUgcGVyIEN1c3RvbWVyIFRpZXINCnRvdGFsX3JldmVudWVfcGVyX3RpZXIgPC0gYWdncmVnYXRlKFRvdGFsIH4gQ29zdHVtZXJUaWVyLCBkYXRhID0gdHJhbnNhY3Rpb25zLCBGVU4gPSBzdW0pDQpwcmludCgiVG90YWwgUGVuZGFwYXRhbiBQZXIgUGVsYW5nZ2FuOiIpDQpwcmludCh0b3RhbF9yZXZlbnVlX3Blcl90aWVyKQ0KDQojIEF2ZXJhZ2UgUHJpY2UgcGVyIFByb2R1Y3QNCmF2ZXJhZ2VfcHJpY2VfcGVyX3Byb2R1Y3QgPC0gYWdncmVnYXRlKFByaWNlIH4gUHJvZHVjdCwgZGF0YSA9IHRyYW5zYWN0aW9ucywgRlVOID0gbWVhbikNCnByaW50KCJIYXJnYSBSYXRhLXJhdGEgcGVyIFByb2R1ayIpDQpwcmludChhdmVyYWdlX3ByaWNlX3Blcl9wcm9kdWN0KQ0KDQpsaWJyYXJ5KGtuaXRyKQ0KIyA0LjUgVmlzdWFsaXplIERhdGENCmJhcnBsb3QoDQogIGhlaWdodCA9IHRvdGFsX3F0eV9wZXJfcHJvZHVjdCRRdHksIA0KICBuYW1lcy5hcmcgPSB0b3RhbF9xdHlfcGVyX3Byb2R1Y3QkUHJvZHVjdCwNCiAgY29sID0gYygicGluayIsICJibHVlIiwgImdyZWVuIiksDQogIG1haW4gPSAiVG90YWwgVHJhbnNhY3Rpb25zIiwNCiAgeGxhYiA9ICJQcm9kdWN0IiwNCiAgeWxhYiA9ICJRdWFudGl0eSIsDQogIHlsaW0gPSBjKDAsIG1heCh0b3RhbF9xdHlfcGVyX3Byb2R1Y3QkUXR5KSArIDIpKQ0KDQojUGllIGNoYXJ0DQpyZXZlbnVlX3Blcl90aWVyIDwtIGFnZ3JlZ2F0ZShUb3RhbCB+IENvc3R1bWVyVGllciwgZGF0YSA9IHRyYW5zYWN0aW9ucywgRlVOID0gc3VtKQ0KdG90YWxfcmV2ZW51ZSA8LSBzdW0ocmV2ZW51ZV9wZXJfdGllciRUb3RhbCkNCnBlcmNlbnRhZ2VzIDwtIHJvdW5kKHJldmVudWVfcGVyX3RpZXIkVG90YWwgLyB0b3RhbF9yZXZlbnVlICogMTAwLCAxKQ0KcGllX2xhYmVscyA8LSBwYXN0ZShyZXZlbnVlX3Blcl90aWVyJENvc3R1bWVyVGllciwgIigiLCBwZXJjZW50YWdlcywgIiUpIiwgc2VwID0gIiIpDQoNCnBpZSgNCiAgeCA9IHJldmVudWVfcGVyX3RpZXIkVG90YWwsDQogIGxhYmVscyA9IHBpZV9sYWJlbHMsDQogIG1haW4gPSAiUHJvcG9ydGlvbiBvZiBUb3RhbCBSZXZlbnVlIHBlciBDdXN0b21lclRpZXIiLA0KICBjb2wgPSBjKCJwdXJwbGUiLCAieWVsbG93IiwgInJlZCIpDQopDQoNCmxpYnJhcnkoa25pdHIpDQojIDQuNiBGaW5kIHRoZSBIaWdoZXN0IGRhdGUgb2YgdG90YWwgcmV2ZW51ZQ0KdHJhbnNhY3Rpb25zJFJldmVudWUgPC0gdHJhbnNhY3Rpb25zJFByaWNlICogdHJhbnNhY3Rpb25zJFF0eQ0KdG90YWxfcGVyX2RhdGUgPC0gYWdncmVnYXRlKFJldmVudWUgfiBEYXRlLCBkYXRhID0gdHJhbnNhY3Rpb25zLCBGVU4gPSBzdW0pDQpoaWdoZXN0IDwtIHRvdGFsX3Blcl9kYXRlW3doaWNoLm1heCh0b3RhbF9wZXJfZGF0ZSRSZXZlbnVlKSwgXQ0KcHJpbnQoIlRoZSBoaWdoZXN0IGRhdGEgb2YgdG90YWwgUmV2ZW51ZSIpDQpwcmludCh0b3RhbF9wZXJfZGF0ZSkNCnByaW50KGhpZ2hlc3QpDQoNCiMgU3RhY2tlZCBCYXIgQ2hhcnQNCnRhYmxlX3RyYW5zYWN0aW9ucyA8LSB3aXRoKHRyYW5zYWN0aW9ucywgdGFwcGx5KFF0eSwgbGlzdChQcm9kdWN0LCBDb3N0dW1lclRpZXIpLCBzdW0sIG5hLnJtID0gVFJVRSkpDQp0YWJsZV90cmFuc2FjdGlvbnNbaXMubmEodGFibGVfdHJhbnNhY3Rpb25zKV0gPC0gMA0KYmFycGxvdCgNCiAgdCh0YWJsZV90cmFuc2FjdGlvbnMpLA0KICBiZXNpZGUgPSBGQUxTRSwNCiAgY29sID0gYygiYnJvd24iLCAiZ29sZCIsICJvcmFuZ2UiKSwNCiAgbWFpbiA9ICJRdWFudGl0eSBTb2xkIHBlciBQcm9kdWN0IGJ5IEN1c3RvbWVyVGllciIsDQogIHhsYWIgPSAiUHJvZHVjdCIsDQogIHlsYWIgPSAiUXVhbnRpdHkgU29sZCIsDQogIGxlZ2VuZC50ZXh0ID0gVFJVRSkNCiAgDQoNCmBgYA0KDQojIyBFeGVyY2lzZSA1DQoNCioqQ3JlYXRlIFlvdXIgT3duIERhdGEgRnJhbWU6KioNCg0KKipPYmplY3RpdmU6KiogQ3JlYXRlIGEgZGF0YSBmcmFtZSBpbiBSIHdpdGggKiozMCByb3dzKiogY29udGFpbmluZyBhIG1peCBvZiBkYXRhIHR5cGVzOiBjb250aW51b3VzLCBkaXNjcmV0ZSwgbm9taW5hbCwgYW5kIG9yZGluYWwuICANCg0KIyMjIEluc3RydWN0aW9ucw0KDQoxLiAqKk9wZW4gUlN0dWRpbyoqIG9yIHRoZSBSIGNvbnNvbGUuICANCg0KMi4gKipDcmVhdGUgYSB2ZWN0b3IgZm9yIGVhY2ggY29sdW1uKiogaW4geW91ciBkYXRhIGZyYW1lOiAgDQoNCiAgIC0gKipEYXRlKio6IDMwIGRhdGVzIChjYW4gYmUgc2VxdWVudGlhbCBvciByYW5kb20gd2l0aGluIGEgbW9udGgveWVhcikgIA0KICAgLSAqKkNvbnRpbnVvdXMqKjogbnVtZXJpYyB2YWx1ZXMgdGhhdCBjYW4gdGFrZSBkZWNpbWFsIHZhbHVlcyAoZS5nLiwgaGVpZ2h0LCB3ZWlnaHQsIHRlbXBlcmF0dXJlKSAgDQogICAtICoqRGlzY3JldGUqKjogbnVtZXJpYyB2YWx1ZXMgdGhhdCBjYW4gb25seSB0YWtlIHdob2xlIG51bWJlcnMgKGUuZy4sIG51bWJlciBvZiBpdGVtcywgbnVtYmVyIG9mIHZlaGljbGVzKSAgDQogICAtICoqTm9taW5hbCoqOiBjYXRlZ29yaWNhbCB2YWx1ZXMgd2l0aCAqKm5vIG9yZGVyKiogKGUuZy4sIGNvbG9yLCBnZW5kZXIsIGNpdHkpICANCiAgIC0gKipPcmRpbmFsKio6IGNhdGVnb3JpY2FsIHZhbHVlcyB3aXRoIGEgKipkZWZpbmVkIG9yZGVyKiogKGUuZy4sIExvdywgTWVkaXVtLCBIaWdoOyBCZWdpbm5lciwgSW50ZXJtZWRpYXRlLCBFeHBlcnQpICANCg0KMy4gKipDb21iaW5lIGFsbCB2ZWN0b3JzIGludG8gYSBkYXRhIGZyYW1lKiogY2FsbGVkIGBteV9kYXRhYC4gIA0KDQo0LiAqKkNoZWNrIHlvdXIgZGF0YSBmcmFtZSoqIHVzaW5nIGBoZWFkKClgIG9yIGBWaWV3KClgIHRvIGVuc3VyZSBpdCBoYXMgKiozMCByb3dzKiogYW5kIHRoZSBjb2x1bW5zIGFyZSBjb3JyZWN0LiAgDQoNCjUuICoqT3B0aW9uYWwgdGFza3MqKjogIA0KICAgLSBTdW1tYXJpemUgZWFjaCBjb2x1bW4gdXNpbmcgYHN1bW1hcnkoKWAgIA0KICAgLSBDb3VudCB0aGUgZnJlcXVlbmN5IG9mIGVhY2ggY2F0ZWdvcnkgZm9yICoqTm9taW5hbCoqIGFuZCAqKk9yZGluYWwqKiBjb2x1bW5zIHVzaW5nIGB0YWJsZSgpYCAgDQoNCmBgYHtyfQ0KbGlicmFyeShEVCkNCmxpYnJhcnkoa25pdHIpDQojIDUuMSBDcmVhdGUgYSB2ZWN0b3IgZm9yIGVhY2ggY29sdW1uDQpzZXQuc2VlZCg1MCkNCnRhbmdnYWwgPC0gYXMuRGF0ZSgiMjAyNC0wMS0wMSIpICsgc2FtcGxlKDA6MzY1LCAzMCwgcmVwbGFjZSA9IFRSVUUpDQpoZXdhbiA8LSBjKCJLdWNpbmciLCAiQW5qaW5nIiwgIktlbGluY2kiLCAiQnVydW5nIiwNCiAgICAgICAgICAgIkt1ZGEiLCAiR2FqYWgiLCAiU2luZ2EiLCAiSGFyaW1hdSIsICJaZWJyYSIsICJQYW5kYSIsICJSdXNhIiwNCiAgICAgICAgICAgIlVudGEiLCAiS3VkYW5pbCIsICJCYWRhayIsICJCZXJ1YW5nIiwgIk1vbnlldCIsICJHb3JpbGEiLCAiS2FuZ3VydSIsDQogICAgICAgICAgICJLb2FsYSIsICJTZXJpZ2FsYSIsICJLdWNpbmciLCAiQW5qaW5nIiwgIkJ1cnVuZyIsICJHYWphaCIsICJTaW5nYSIsIA0KICAgICAgICAgICAiSGFyaW1hdSIsICJaZWJyYSIsICJQYW5kYSIsICJSdXNhIiwgIlVudGEiKQ0KaGV3YW4gPC0gZmFjdG9yKGhld2FuKQ0KDQpqdW1sYWhfcG9yc2lfbWFrYW5hbiA8LSBjKDUsIDUsIDMsIDQsIDMsIA0KICAgICAgICAgICAgICAgICAgMywgNSwgNSwgNSwgNiwgDQogICAgICAgICAgICAgICAgICA0LCA0LCA2LCA2LCA1LCANCiAgICAgICAgICAgICAgICAgIDUsIDYsIDQsIDQsIDUsIA0KICAgICAgICAgICAgICAgICAgNSwgNCwgNSwgNiwgNywgDQogICAgICAgICAgICAgICAgICA2LCA1LCA2LCA0LCA0KQ0KDQpiZXJhdCA8LSBjKDI0LjU2LCA0OC43NiwgMTUuNDgsIDkuNjQsIDI4OS42NywgNzg1LjU0LCAyOTMuMjcsIDI1Ny4zOCwgMTU4LjM1LCANCiAgICAgICAgICAgOTMuMzIsIDgyLjY3LCA2MzIuNzUsIDQyOS43MSwgODM2LjU0LCAyNzQuNjgsIDE4LjU2LCA0NjcuODIsIDE1Mi43NCwNCiAgICAgICAgICAgNTIuODUsIDE3Mi45MiwgMzguNTYsIDk1LjI3LCA2LjMyLCAzNjQuNzgsIDI4NC45MiwgMjU2Ljg1LCAxOTMuNjUsDQogICAgICAgICAgIDEyMy40NSwgMTEwLjQzLCA1NzYuOTgpDQoNCmtlbGluY2FoYW4gPC0gYygiVGluZ2dpIiwgIlRpbmdnaSIsICJTZWRhbmciLCAiVGluZ2dpIiwgIlNlZGFuZyIsICJSZW5kYWgiLCAiVGluZ2dpIiwNCiAgICAgICAgICAgICAgICAiVGluZ2dpIiwgIlNlZGFuZyIsICJTZWRhbmciLCAiU2VkYW5nIiwgIlJlbmRhaCIsICJSZW5kYWgiLCAiUmVuZGFoIiwNCiAgICAgICAgICAgICAgICAiU2VkYW5nIiwgIlRpbmdnaSIsICJSZW5kYWgiLCAiVGluZ2dpIiwgIlNlZGFuZyIsICJUaW5nZ2kiLCAiU2VkYW5nIiwgDQogICAgICAgICAgICAgICAgIlRpbmdnaSIsICJUaW5nZ2kiLCAiU2VkYW5nIiwgIlRpbmdnaSIsICJUaW5nZ2kiLCAiU2VkYW5nIiwgIlNlZGFuZyIsDQogICAgICAgICAgICAgICAgIlNlZGFuZyIsICJSZW5kYWgiKQ0Ka2VsaW5jYWhhbiA8LSBmYWN0b3Ioa2VsaW5jYWhhbiwNCiAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIlJlbmRhaCIsICJTZWRhbmciLCAiVGluZ2dpIiksDQogICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVFJVRSkNCg0KIyA1LjIgQ29tYmluZSBhbGwgdmVjdG9ycyBpbnRvIGEgZGF0YSBmcmFtZQ0KRGF0YV9IZXdhbiA8LSBkYXRhLmZyYW1lKHRhbmdnYWwsIGhld2FuLCBqdW1sYWhfcG9yc2lfbWFrYW5hbiwgYmVyYXQsIGtlbGluY2FoYW4pDQoNCiMgNS4zIENyZWF0ZSBhIGRhdGEgZnJhbWUgYXMgYSBuZWF0IHRhYmxlDQpkYXRhdGFibGUoRGF0YV9IZXdhbiwgY2FwdGlvbiA9ICJSZWthcGl0dWxhc2kgRGF0YSBIZXdhbjogSmVuaXMsIEJlcmF0IGRhbiBLZWxpbmNhaGFuIiwgb3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDEwKSkNCg0KIyA1LjMgQ3JlYXRlIGEgdGFibGUgb2YgdmFyaWFibGUgdHlwZXMNCnZhcmlhYmxlc19pbmZvMiA8LSBkYXRhLmZyYW1lKA0KICBObyA9IDE6NCwNCiAgVmFyaWFibGUgPSBjKA0KICAgICJIZXdhbiIsDQogICAgIkp1bWxhaFBvcnNpTWFrYW5hbiIsDQogICAgIkJlcmF0IiwNCiAgICAiS2VsaW5jYWhhbiIpLA0KICBEYXRhVHlwZSA9IGMoDQogICAgIk5vbWluYWwiLA0KICAgICJEaXNrcml0IiwNCiAgICAiQ29udGludW91cyIsDQogICAgIk9yZGluYWwiKSwNCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFDQopDQoNCmtuaXRyOjprYWJsZSh2YXJpYWJsZXNfaW5mbzIsIGNhcHRpb24gPSAiVGFibGUgb2YgVmFyaWFibGUgYW5kIERhdGEgVHlwZSIpDQoNCiMgNS40IFN1bW1hcml6ZSBlYWNoIGNvbHVtbg0Kc3VtbWFyeShEYXRhX0hld2FuKQ0Kc3VtbWFyeSh0cmFuc2FjdGlvbnMpDQoNCnN1bW1hcnlfdGFibGUgPC0gc3VtbWFyeShEYXRhX0hld2FuKQ0Ka25pdHI6OmthYmxlKGFzLmRhdGEuZnJhbWUoc3VtbWFyeV90YWJsZSksIGNhcHRpb24gPSAiU3VtbWFyeSBvZiBEYXRhIEhld2FuIikNCg0KDQojIDUuNCBDb3VudCB0aGUgZnJlcXVlbmN5IG9mIGVhY2ggY2F0ZWdvcnkgZm9yICoqTm9taW5hbCoqIGFuZCAqKk9yZGluYWwqKiBjb2x1bW5zDQpsaXN0KA0KICBOb21pbmFsID0gdGFibGUoRGF0YV9IZXdhbiRoZXdhbiksDQogIE9yZGluYWwgPSB0YWJsZShEYXRhX0hld2FuJGtlbGluY2FoYW4pDQopDQoNCmBgYA0KDQoNCg0KDQoNCg0KDQo=