suppressMessages(suppressWarnings(install.packages("testthat", ask=FALSE)))
trying URL 'https://cran.rstudio.com/bin/macosx/big-sur-arm64/contrib/4.5/testthat_3.2.3.tgz'
Content type 'application/x-gzip' length 3091157 bytes (2.9 MB)
==================================================
downloaded 2.9 MB

The downloaded binary packages are in
    /var/folders/1d/schf58950zl9y22b_w4skq5r0000gn/T//RtmppN6Nz6/downloaded_packages
library(testthat)

Introduction

Welcome to your first assignment in R! This notebook will guide you through the basics of R programming, data manipulation using the dplyr package, and data visualization using the ggplot2 package. By the end of this assignment, you will be prepared to tackle more complex data engineering tasks in the next assignments.

Table of Contents

  1. Getting Started with R
  2. Basic Arithmetic and Variables
  3. Vectors and Data Types
  4. Data Frames and Basic Operations
  5. Data Manipulation with dplyr
  6. Data Visualization with ggplot2
  7. Practical Exercise: Analyzing Patient Data

1. Getting Started with R

R is a powerful programming language used primarily for statistical computing and graphics. It is widely used in various fields such as data science, bioinformatics, and social sciences. RStudio is an integrated development environment (IDE) for R that makes it easier to write and execute R code, manage projects, and visualize data.

In this section, we will get you started with R by printing a simple message and installing and loading some necessary packages. Printing a message will help you understand how to execute R code, and installing and loading packages will extend R’s functionality. Getting comfortable with these basics will lay the foundation for more advanced tasks you’ll encounter later.

Instructions

  1. Print a Simple Message: Let’s start by printing a simple message in R. This will help you understand how to execute R code.
### Your code here - Start ###
print("Hello, world!")
[1] "Hello, world!"
### Your code here - End ###
  1. Install and Load Necessary Packages: We will install and load some packages that we’ll use throughout this assignment. Packages in R are collections of functions and data sets developed by the community to extend the functionality of R.
# Install and load necessary packages
install.packages(c("dplyr", "ggplot2", "tidyr", "readr", "lubridate"))
trying URL 'https://cran.rstudio.com/bin/macosx/big-sur-arm64/contrib/4.5/dplyr_1.1.4.tgz'
trying URL 'https://cran.rstudio.com/bin/macosx/big-sur-arm64/contrib/4.5/ggplot2_4.0.0.tgz'
trying URL 'https://cran.rstudio.com/bin/macosx/big-sur-arm64/contrib/4.5/tidyr_1.3.1.tgz'
trying URL 'https://cran.rstudio.com/bin/macosx/big-sur-arm64/contrib/4.5/readr_2.1.5.tgz'
trying URL 'https://cran.rstudio.com/bin/macosx/big-sur-arm64/contrib/4.5/lubridate_1.9.4.tgz'

The downloaded binary packages are in
    /var/folders/1d/schf58950zl9y22b_w4skq5r0000gn/T//RtmppN6Nz6/downloaded_packages
library(dplyr)
library(ggplot2)
library(tidyr)
library(readr)
library(lubridate)

Before proceeding, it’s essential to practice printing messages and installing packages. Follow these steps:

2. Basic Arithmetic and Variables

R can perform basic arithmetic operations and handle variables easily. Variables in R are used to store data that can be used later in the program. Understanding how to perform basic arithmetic and work with variables is fundamental to programming in R.

In this section, you will learn how to perform arithmetic operations and assign values to variables. Arithmetic operations include addition, subtraction, multiplication, and division. Variables allow you to store results and reuse them without retyping the same values. Mastering these basics is crucial for writing more complex R scripts in the future.

Instructions

  1. Perform Basic Calculations: Add, subtract, multiply, and divide numbers. These operations are the foundation of more complex calculations.
# Basic arithmetic
5 + 3
[1] 8
10 - 2
[1] 8
4 * 3
[1] 12
8 / 2
[1] 4
  1. Assign Values to Variables: Store results in variables and use them. Variables make it possible to save intermediate results and reuse them without retyping the same values.
# Assigning values to variables
x <- 10
y <- 5
result <- x + y
result
[1] 15

Problems for Students:

It’s time to practice basic arithmetic and variable assignments. Solve the following problems:

  1. Calculate the result of 7 * 8 and assign it to a variable named product.
### Problem 2.1 - Start ###
product <- 7 * 8
### Problem 2.2 - End ###

# Test Feedback
tryCatch({
  testthat::test_that("Problem 2.1", {
    testthat::expect_equal(product, 56)
  })
}, error = function(e) {
  message("Ensure that you correctly assign the product of 7 * 8 to the variable 'product'.")
})
Test passed 😀
  1. Assign the value 15 to a variable named a and 20 to a variable named b. Then, calculate the sum of a and b.
### Problem 2.2 - Start ###
a <- 15
b <- 20
sum_ab <- a + b
### Problem 2.2 - End ###

# Hidden test code
tryCatch({
  testthat::test_that("Problem 2.2", {
    testthat::expect_equal(a, 15)
    testthat::expect_equal(b, 20)
    testthat::expect_equal(sum_ab, 35)
  })
}, error = function(e) {
  message("Ensure that you correctly assign values to 'a' and 'b', and compute their sum in 'sum_ab'.")
})
Test passed 🎉

3. Vectors and Data Types

Vectors are one of the basic data structures in R. They can store a sequence of elements of the same type, such as numbers, characters, or logical values. Understanding vectors and data types is crucial for effective data manipulation and analysis.

In this section, you will learn how to create vectors and perform operations on them. You will also explore different data types in R. Data types determine what kind of data can be stored and how it can be used. Being comfortable with these concepts will enable you to handle and process data efficiently.

Instructions

  1. Create and Manipulate Vectors: Learn how to create vectors and perform operations on them. Vectors are used to store data in a linear format.

  2. Understand Data Types: Explore different data types in R. Data types determine what kind of data can be stored and how it can be used.

# Creating vectors
numbers <- c(1, 2, 3, 4, 5)
characters <- c("a", "b", "c")

# Basic vector operations
sum(numbers)
[1] 15
mean(numbers)
[1] 3
length(characters)
[1] 3
# Data types
str(numbers)
 num [1:5] 1 2 3 4 5
str(characters)
 chr [1:3] "a" "b" "c"

Problems for Students:

Now, let’s apply what you’ve learned about vectors and data types:

  1. Create a vector named temperatures containing the values 23, 25, 20, 19, 22.
### Problem 3.1 - Start ###
temperatures <- c(23, 25, 20, 19, 22)
### Problem 3.2 - Start ###

# Hidden test code
tryCatch({
  testthat::test_that("Problem 3.1", {
    testthat::expect_true(is.numeric(temperatures))
    testthat::expect_equal(length(temperatures), 5)
  })
}, error = function(e) {
  message("Ensure that 'temperatures' is a numeric vector with the correct length.")
})
Test passed 🎉
  1. Calculate the average (mean) temperature and store it in a variable named mean_temperature.
### Problem 3.2 - Start ###
mean_temperature <- mean(temperatures)
### Problem 3.2 - end ###

# Hidden test code
tryCatch({
  testthat::test_that("Problem 3.2", {
    testthat::expect_equal(mean_temperature, 21.8)
  })
}, error = function(e) {
  message("Ensure that you correctly calculate and store the mean of 'temperatures' in 'mean_temperature'.")
})
Test passed 🥇
  1. Create a character vector named cities with the values “New York”, “Los Angeles”, “Chicago”.
### Problem 3.3 - Start ###
cities <- c("New York", "Los Angeles", "Chicago")
### Problem 3.3 - End ###

# Hidden test code
tryCatch({
  testthat::test_that("Problem 3.3", {
    testthat::expect_true(is.character(cities))
    testthat::expect_equal(length(cities), 3)
  })
}, error = function(e) {
  message("Ensure that 'cities' is a character vector with the correct length.")
})
Test passed 🥳

4. Data Frames and Basic Operations

Data frames are table-like structures that store data in rows and columns, similar to a spreadsheet. Each column can contain data of a different type, making data frames very flexible and useful for data analysis.

In this section, you will learn how to create data frames by combining vectors. You will also learn how to inspect and manipulate data frames, including viewing the structure of the data, summarizing it, and adding new columns. These skills are essential for handling real-world data in your analyses.

Instructions

  1. Create a Data Frame: Combine vectors into a data frame. Data frames are the most common data structure used in R for data analysis.

  2. Inspect and Manipulate Data Frames: Use functions to inspect and manipulate data frames. This includes viewing the structure of the data, summarizing it, and adding new columns.

# Creating a data frame
names <- c("John", "Jane", "Jim")
ages <- c(28, 34, 40)
data <- data.frame(Name = names, Age = ages)
data

# Inspect the data frame
head(data)
summary(data)
     Name                Age    
 Length:3           Min.   :28  
 Class :character   1st Qu.:31  
 Mode  :character   Median :34  
                    Mean   :34  
                    3rd Qu.:37  
                    Max.   :40  
str(data)
'data.frame':   3 obs. of  2 variables:
 $ Name: chr  "John" "Jane" "Jim"
 $ Age : num  28 34 40
# Manipulate data frames
data$Age_in_5_years <- data$Age + 5
data

Problems for Students:

Let’s practice creating and manipulating data frames:

  1. Create a data frame named students with the columns StudentName and Score, using the vectors c("Alice", "Bob", "Charlie") and c(85, 90, 88).
### Problem 4.1 - Start ###
StudentName <- c("Alice", "Bob", "Charlie")
Score <- c(85, 90, 88)
students <- data.frame(StudentName, Score)
students
### Problem 4.1 - End ###

# Hidden test code
tryCatch({
  testthat::test_that("Problem 4.1", {
    testthat::expect_true(is.data.frame(students))
    testthat::expect_equal(names(students), c("StudentName", "Score"))
    testthat::expect_equal(nrow(students), 3)
  })
}, error = function(e) {
  message("Ensure that 'students' is a data frame with the correct structure.")
})
Test passed 🎉
  1. Add a new column to the students data frame named Pass, which is TRUE if Score is greater than or equal to 50 and FALSE otherwise.
### Problem 4.2 - Start ###
students$Pass <- students$Score >= 50
students
### Problem 4.2 - End ###

# Hidden test code
tryCatch({
  testthat::test_that("Problem 4.2", {
    testthat::expect_true("Pass" %in% names(students))
    testthat::expect_equal(students$Pass, c(TRUE, TRUE, TRUE))
  })
}, error = function(e) {
  message("Ensure that you correctly add the 'Pass' column based on the 'Score'.")
})
Test passed 🌈

5. Data Manipulation with dplyr

The dplyr package provides a set of functions for data manipulation, making it easier to perform common data manipulation tasks such as filtering rows, selecting columns, and summarizing data. These functions are designed to be easy to use and efficient.

In this section, you will learn how to load the dplyr package and use its functions to manipulate data frames. These skills are vital for cleaning and transforming data before analysis.

Instructions

  1. Load the dplyr Package: Ensure the package is loaded. If not, install it using install.packages("dplyr").

  2. Perform Basic Data Manipulation: Use filter, select, mutate, and summarize functions to manipulate data frames.

# Ensure the package is loaded
library(dplyr)

# Filtering data
filtered_data <- filter(data, Age > 30)
filtered_data

# Selecting columns
selected_data <- select(data, Name, Age)
selected_data

# Mutating data
mutated_data <- mutate(data, Age_in_10_years = Age + 10)
mutated_data

# Summarizing data
summary_data <- summarize(data, Average_Age = mean(Age))
summary_data
NA

Problems for Students:

Practice using dplyr to manipulate data:

  1. Use the students data frame. Filter the rows where Score is greater than 85.
### Problem 5.1 - Start ###
filtered_students <- filter(students, Score > 85)
filtered_students
### Problem 5.1 - End ###

# Hidden test code
tryCatch({
  testthat::test_that("Problem 5.1", {
    testthat::expect_equal(nrow(filtered_students), 2)
  })
}, error = function(e) {
  message("Ensure that you correctly filter 'students' based on 'Score'.")
})
Test passed 🎊
  1. Select only the StudentName column from the students data frame.
### Problem 5.2 - Start ###
selected_students <- select(students, StudentName)
selected_students
### Problem 5.2 - End ###

# Hidden test code
tryCatch({
  testthat::test_that("Problem 5.2", {
    testthat::expect_equal(names(selected_students), "StudentName")
  })
}, error = function(e) {
  message("Ensure that you correctly select the 'StudentName' column.")
})
Test passed 🌈
  1. Create a new column in the students data frame named Score_in_10_years that adds 10 to the current Score.
### Problem 5.3 - Start ###
mutated_students <- mutate(students, Score_in_10_years = Score + 10)
mutated_students
### Problem 5.3 - End ###

# Hidden test code
tryCatch({
  testthat::test_that("Problem 5.3", {
    testthat::expect_true("Score_in_10_years" %in% names(mutated_students))
    testthat::expect_equal(mutated_students$Score_in_10_years, c(95, 100, 98))
  })
}, error = function(e) {
  message("Ensure that you correctly add the 'Score_in_10_years' column.")
})
Test passed 😸
  1. Calculate the average score of the students.
### Problem 5.4 - Start ###
average_score <- summarize(students, Average_Score = mean(Score))
average_score
### Problem 5.4 - Start ###

# Hidden test code
tryCatch({
  testthat::test_that("Problem 5.4", {
    testthat::expect_equal(average_score$Average_Score, 87.67, tolerance = 0.01)
  })
}, error = function(e) {
  message("Ensure that you correctly calculate the average score of 'students'.")
})
Test passed 🥳

6. Data Visualization with ggplot2

The ggplot2 package is a powerful tool for creating visualizations. It uses a coherent system of “grammar” to create a variety of plots, making it easier to understand and communicate data insights.

In this section, you will learn how to load the ggplot2 package and create basic plots. Visualizations are crucial for presenting your findings in a clear and compelling way.

Instructions

  1. Load the ggplot2 Package: Ensure the package is loaded. If not, install it using install.packages("ggplot2").

  2. Create a Simple Plot: Learn the basics of creating plots with ggplot2, such as scatter plots and bar charts.

# Ensure the package is loaded
library(ggplot2)

# Creating a scatter plot
ggplot(data, aes(x = Name, y = Age)) +
  geom_point()


# Creating a bar chart
ggplot(data, aes(x = Name, y = Age)) +
  geom_bar(stat = "identity")

Problems for Students:

Now it’s your turn to create visualizations:

  1. Create a scatter plot of Score vs. StudentName using the students data frame.
### Problem 6.1 - Start ###
ggplot(students, aes(x = StudentName, y = Score)) +
  geom_point()

### Problem 6.1 - End ###

# Hidden test code
tryCatch({
  testthat::test_that("Problem 6.1", {
    testthat::expect_s3_class(ggplot(students, aes(x = StudentName, y = Score)) + geom_point(), "gg")
  })
}, error = function(e) {
  message("Ensure that you correctly create a scatter plot of 'Score' vs. 'StudentName'.")
})
Test passed 🎉
  1. Create a bar chart showing the Score for each StudentName.
### Problem 6.2 - Start ###
ggplot(students, aes(x = StudentName, y = Score)) +
  geom_bar(stat = "identity")

### Problem 6.2 - End ###

# Hidden test code
tryCatch({
  testthat::test_that("Problem 6.2", {
    testthat::expect_s3_class(ggplot(students, aes(x = StudentName, y = Score)) + geom_bar(stat = "identity"), "gg")
  })
}, error = function(e) {
  message("Ensure that you correctly create a bar chart showing the 'Score' for each 'StudentName'.")
})
Test passed 🥇

7. Practical Exercise: Analyzing Patient Data

Now that you’ve learned the basics, let’s apply these skills to a practical exercise. We’ll analyze a simple patient dataset to gain insights into patient characteristics. The dataset is already loaded into memory and can be used for the following questions.

In this section, you will load a dataset, inspect and clean the data, perform basic data analysis, and create visualizations. This exercise will consolidate your learning and give you hands-on experience with real-world data.

Instructions

  1. Load the Dataset: Load a CSV file containing patient data.
  2. Inspect the Data: Understand the structure and contents of the dataset.
  3. Clean the Data: Handle missing values and ensure data types are correct.
  4. Analyze the Data: Perform basic data analysis to gain insights.
  5. Visualize the Data: Create visualizations to present the analysis results.
# Load the dataset
patients <- read.csv("patient_data_large.csv")

# Inspect the data
head(patients)
summary(patients)
   PatientID           Age           Gender          HeartDisease       BloodPressure    Cholesterol   
 Min.   :   1.0   Min.   :18.00   Length:1000        Length:1000        Min.   : 90.0   Min.   :103.9  
 1st Qu.: 250.8   1st Qu.:39.00   Class :character   Class :character   1st Qu.:109.5   1st Qu.:194.2  
 Median : 500.5   Median :51.00   Mode  :character   Mode  :character   Median :120.1   Median :219.7  
 Mean   : 500.5   Mean   :50.33                                         Mean   :119.9   Mean   :219.6  
 3rd Qu.: 750.2   3rd Qu.:61.00                                         3rd Qu.:129.7   3rd Qu.:244.0  
 Max.   :1000.0   Max.   :90.00                                         Max.   :169.7   Max.   :362.4  
str(patients)
'data.frame':   1000 obs. of  6 variables:
 $ PatientID    : int  1 2 3 4 5 6 7 8 9 10 ...
 $ Age          : int  65 53 78 28 62 68 57 47 19 66 ...
 $ Gender       : chr  "Male" "Female" "Female" "Male" ...
 $ HeartDisease : chr  "No" "No" "No" "No" ...
 $ BloodPressure: num  108.7 96.3 123.8 125.9 109.8 ...
 $ Cholesterol  : num  238 195 199 232 220 ...
# Clean the data
patients <- na.omit(patients)
patients$Age <- as.numeric(patients$Age)
patients$Gender <- as.factor(patients$Gender)
patients$HeartDisease <- as.factor(patients$HeartDisease)
summary(patients)
   PatientID           Age           Gender    HeartDisease BloodPressure    Cholesterol   
 Min.   :   1.0   Min.   :18.00   Female:510   No :590      Min.   : 90.0   Min.   :103.9  
 1st Qu.: 250.8   1st Qu.:39.00   Male  :490   Yes:410      1st Qu.:109.5   1st Qu.:194.2  
 Median : 500.5   Median :51.00                             Median :120.1   Median :219.7  
 Mean   : 500.5   Mean   :50.33                             Mean   :119.9   Mean   :219.6  
 3rd Qu.: 750.2   3rd Qu.:61.00                             3rd Qu.:129.7   3rd Qu.:244.0  
 Max.   :1000.0   Max.   :90.00                             Max.   :169.7   Max.   :362.4  
# Analyze the data
# Age distribution
summary(patients$Age)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  18.00   39.00   51.00   50.33   61.00   90.00 
hist(patients$Age, main = "Age Distribution", xlab = "Age", col = "lightblue", border = "white")


# Blood pressure distribution
summary(patients$BloodPressure)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   90.0   109.5   120.1   119.9   129.7   169.7 
hist(patients$BloodPressure, main = "Blood Pressure Distribution", xlab = "Blood Pressure", col = "lightgreen", border = "white")


# Cholesterol distribution
summary(patients$Cholesterol)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  103.9   194.2   219.7   219.6   244.0   362.4 
hist(patients$Cholesterol, main = "Cholesterol Distribution", xlab = "Cholesterol", col = "lightcoral", border = "white")


# Exploring relationships
# Age and heart disease
boxplot(Age ~ HeartDisease, data = patients, main = "Age and Heart Disease", xlab = "Heart Disease", ylab = "Age", col = c("lightblue", "lightpink"))


# Blood pressure and heart disease
boxplot(BloodPressure ~ HeartDisease, data = patients, main = "Blood Pressure and Heart Disease", xlab = "Heart Disease", ylab = "Blood Pressure", col = c("lightgreen", "lightpink"))


# Cholesterol and heart disease
boxplot(Cholesterol ~ HeartDisease, data = patients, main = "Cholesterol and Heart Disease", xlab = "Heart Disease", ylab = "Cholesterol", col = c("lightcoral", "lightpink"))

Problems for Students:

Now it’s your turn to analyze the patient data. Follow the steps below to complete the exercises.

  1. Filter and Summarize Data: Filter the patients dataset to include only male patients, and then calculate the average age of these patients. Store the result in a variable named avg_age_male.
### Problem 7.1 - Start ###
avg_age_male <- patients %>%
  filter(Gender == "Male") %>%
  summarize(avg_age = mean(Age)) %>%
  pull(avg_age)
avg_age_male
[1] 50.1551
### Problem 7.1 - End ###

# Hidden test code
tryCatch({
  testthat::test_that("Problem 7.1", {
    testthat::expect_equal(avg_age_male, mean(patients$Age[patients$Gender == "Male"]), tolerance = 0.01)
  })
}, error = function(e) {
  message("Ensure that you correctly filter and calculate the average age for male patients.")
})
Test passed 🌈
  1. Select and Mutate Data: Create a new data frame named high_cholesterol that includes only the PatientID and Cholesterol columns for patients with cholesterol levels greater than 250. Add a new column to this data frame named CholesterolCategory that labels these patients as “High”.
### Problem 7.2 - Start ###
high_cholesterol <- patients %>%
  filter(Cholesterol > 250) %>%
  select(PatientID, Cholesterol) %>%
  mutate(CholesterolCategory = "High")
high_cholesterol
### Problem 7.2 - End ###

# Hidden test code
tryCatch({
  testthat::test_that("Problem 7.2", {
    testthat::expect_true(all(high_cholesterol$Cholesterol > 250))
    testthat::expect_true("CholesterolCategory" %in% names(high_cholesterol))
    testthat::expect_equal(unique(high_cholesterol$CholesterolCategory), "High")
  })
}, error = function(e) {
  message("Ensure that you correctly filter, select, and mutate the data for high cholesterol patients.")
})
Test passed 🥇
  1. Visualize Data: Create a boxplot to compare the ages of patients with and without heart disease. Use the ggplot2 package for this visualization.
### Problem 7.3 - Start ###
ggplot(patients, aes(x = HeartDisease, y = Age, fill = HeartDisease)) +
  geom_boxplot() +
  labs(title = "Age Comparison of Patients with and without Heart Disease", x = "Heart Disease", y = "Age")

### Problem 7.3 - End ###

# Hidden test code
tryCatch({
  testthat::test_that("Problem 7.3", {
    testthat::expect_s3_class(ggplot(patients, aes(x = HeartDisease, y = Age, fill = HeartDisease)) +
                                geom_boxplot() +
                                labs(title = "Age Comparison of Patients with and without Heart Disease", x = "Heart Disease", y = "Age"), "gg")
  })
}, error = function(e) {
  message("Ensure that you correctly create a boxplot comparing ages of patients with and without heart disease.")
})
Test passed 😀
  1. Summarize and Visualize Data: Summarize the average cholesterol levels for patients grouped by heart disease status and create a bar plot to visualize these averages.
### Problem 7.4 - Start ###
avg_cholesterol <- patients %>%
  group_by(HeartDisease) %>%
  summarize(avg_chol = mean(Cholesterol))

ggplot(avg_cholesterol, aes(x = HeartDisease, y = avg_chol, fill = HeartDisease)) +
  geom_bar(stat = "identity") +
  labs(title = "Average Cholesterol Levels by Heart Disease Status", x = "Heart Disease", y = "Average Cholesterol")

### Problem 7.4 - End ###

### Feadback - Test Case - Problem 7.4 - Start ###
tryCatch({
  testthat::test_that("Problem 7.4", {
    testthat::expect_s3_class(ggplot(avg_cholesterol, aes(x = HeartDisease, y = avg_chol, fill = HeartDisease)) +
                                geom_bar(stat = "identity") +
                                labs(title = "Average Cholesterol Levels by Heart Disease Status", x = "Heart Disease", y = "Average Cholesterol"), "gg")
  })
}, error = function(e) {
  message("Ensure that you correctly summarize and visualize the average cholesterol levels by heart disease status.")
})
Test passed 😸
### Feadback - Test Case - Problem 7.4 - End ###

Conclusion

In this assignment, you have learned the basics of R programming, data manipulation with dplyr, and data visualization with ggplot2. You also applied these skills to analyze a simple patient dataset. These foundational skills will prepare you for more complex data engineering tasks in the next assignments. Well done!

LS0tCnRpdGxlOiAnQXNzaWdubWVudCAxOiBJbnRyb2R1Y3Rpb24gdG8gUicKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKLS0tCgpgYGB7cn0Kc3VwcHJlc3NNZXNzYWdlcyhzdXBwcmVzc1dhcm5pbmdzKGluc3RhbGwucGFja2FnZXMoInRlc3R0aGF0IiwgYXNrPUZBTFNFKSkpCmxpYnJhcnkodGVzdHRoYXQpCmBgYAoKIyMgSW50cm9kdWN0aW9uCgpXZWxjb21lIHRvIHlvdXIgZmlyc3QgYXNzaWdubWVudCBpbiBSISBUaGlzIG5vdGVib29rIHdpbGwgZ3VpZGUgeW91IHRocm91Z2ggdGhlIGJhc2ljcyBvZiBSIHByb2dyYW1taW5nLCBkYXRhIG1hbmlwdWxhdGlvbiB1c2luZyB0aGUgYGRwbHlyYCBwYWNrYWdlLCBhbmQgZGF0YSB2aXN1YWxpemF0aW9uIHVzaW5nIHRoZSBgZ2dwbG90MmAgcGFja2FnZS4gQnkgdGhlIGVuZCBvZiB0aGlzIGFzc2lnbm1lbnQsIHlvdSB3aWxsIGJlIHByZXBhcmVkIHRvIHRhY2tsZSBtb3JlIGNvbXBsZXggZGF0YSBlbmdpbmVlcmluZyB0YXNrcyBpbiB0aGUgbmV4dCBhc3NpZ25tZW50cy4KCiMjIyBUYWJsZSBvZiBDb250ZW50cwoKMS4gIEdldHRpbmcgU3RhcnRlZCB3aXRoIFIKMi4gIEJhc2ljIEFyaXRobWV0aWMgYW5kIFZhcmlhYmxlcwozLiAgVmVjdG9ycyBhbmQgRGF0YSBUeXBlcwo0LiAgRGF0YSBGcmFtZXMgYW5kIEJhc2ljIE9wZXJhdGlvbnMKNS4gIERhdGEgTWFuaXB1bGF0aW9uIHdpdGggYGRwbHlyYAo2LiAgRGF0YSBWaXN1YWxpemF0aW9uIHdpdGggYGdncGxvdDJgCjcuICBQcmFjdGljYWwgRXhlcmNpc2U6IEFuYWx5emluZyBQYXRpZW50IERhdGEKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIDEuIEdldHRpbmcgU3RhcnRlZCB3aXRoIFIKClIgaXMgYSBwb3dlcmZ1bCBwcm9ncmFtbWluZyBsYW5ndWFnZSB1c2VkIHByaW1hcmlseSBmb3Igc3RhdGlzdGljYWwgY29tcHV0aW5nIGFuZCBncmFwaGljcy4gSXQgaXMgd2lkZWx5IHVzZWQgaW4gdmFyaW91cyBmaWVsZHMgc3VjaCBhcyBkYXRhIHNjaWVuY2UsIGJpb2luZm9ybWF0aWNzLCBhbmQgc29jaWFsIHNjaWVuY2VzLiBSU3R1ZGlvIGlzIGFuIGludGVncmF0ZWQgZGV2ZWxvcG1lbnQgZW52aXJvbm1lbnQgKElERSkgZm9yIFIgdGhhdCBtYWtlcyBpdCBlYXNpZXIgdG8gd3JpdGUgYW5kIGV4ZWN1dGUgUiBjb2RlLCBtYW5hZ2UgcHJvamVjdHMsIGFuZCB2aXN1YWxpemUgZGF0YS4KCkluIHRoaXMgc2VjdGlvbiwgd2Ugd2lsbCBnZXQgeW91IHN0YXJ0ZWQgd2l0aCBSIGJ5IHByaW50aW5nIGEgc2ltcGxlIG1lc3NhZ2UgYW5kIGluc3RhbGxpbmcgYW5kIGxvYWRpbmcgc29tZSBuZWNlc3NhcnkgcGFja2FnZXMuIFByaW50aW5nIGEgbWVzc2FnZSB3aWxsIGhlbHAgeW91IHVuZGVyc3RhbmQgaG93IHRvIGV4ZWN1dGUgUiBjb2RlLCBhbmQgaW5zdGFsbGluZyBhbmQgbG9hZGluZyBwYWNrYWdlcyB3aWxsIGV4dGVuZCBS4oCZcyBmdW5jdGlvbmFsaXR5LiBHZXR0aW5nIGNvbWZvcnRhYmxlIHdpdGggdGhlc2UgYmFzaWNzIHdpbGwgbGF5IHRoZSBmb3VuZGF0aW9uIGZvciBtb3JlIGFkdmFuY2VkIHRhc2tzIHlvdSdsbCBlbmNvdW50ZXIgbGF0ZXIuCgojIyMjIEluc3RydWN0aW9ucwoKMS4gICoqUHJpbnQgYSBTaW1wbGUgTWVzc2FnZSoqOiBMZXQncyBzdGFydCBieSBwcmludGluZyBhIHNpbXBsZSBtZXNzYWdlIGluIFIuIFRoaXMgd2lsbCBoZWxwIHlvdSB1bmRlcnN0YW5kIGhvdyB0byBleGVjdXRlIFIgY29kZS4KCmBgYHtyfQojIyMgWW91ciBjb2RlIGhlcmUgLSBTdGFydCAjIyMKcHJpbnQoIkhlbGxvLCB3b3JsZCEiKQojIyMgWW91ciBjb2RlIGhlcmUgLSBFbmQgIyMjCmBgYAoKMi4gICoqSW5zdGFsbCBhbmQgTG9hZCBOZWNlc3NhcnkgUGFja2FnZXMqKjogV2Ugd2lsbCBpbnN0YWxsIGFuZCBsb2FkIHNvbWUgcGFja2FnZXMgdGhhdCB3ZSdsbCB1c2UgdGhyb3VnaG91dCB0aGlzIGFzc2lnbm1lbnQuIFBhY2thZ2VzIGluIFIgYXJlIGNvbGxlY3Rpb25zIG9mIGZ1bmN0aW9ucyBhbmQgZGF0YSBzZXRzIGRldmVsb3BlZCBieSB0aGUgY29tbXVuaXR5IHRvIGV4dGVuZCB0aGUgZnVuY3Rpb25hbGl0eSBvZiBSLgoKYGBge3J9CiMgSW5zdGFsbCBhbmQgbG9hZCBuZWNlc3NhcnkgcGFja2FnZXMKaW5zdGFsbC5wYWNrYWdlcyhjKCJkcGx5ciIsICJnZ3Bsb3QyIiwgInRpZHlyIiwgInJlYWRyIiwgImx1YnJpZGF0ZSIpKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkocmVhZHIpCmxpYnJhcnkobHVicmlkYXRlKQpgYGAKCiMjIyMgQmVmb3JlIHByb2NlZWRpbmcsIGl04oCZcyBlc3NlbnRpYWwgdG8gcHJhY3RpY2UgcHJpbnRpbmcgbWVzc2FnZXMgYW5kIGluc3RhbGxpbmcgcGFja2FnZXMuIEZvbGxvdyB0aGVzZSBzdGVwczoKCiMjIyMgMS4gUHJpbnQgdGhlIG1lc3NhZ2UgIldlbGNvbWUgdG8gUiBQcm9ncmFtbWluZyEiLgoKYGBge3J9CiMjIyBQcm9ibGVtIDEuMSAtIFN0YXJ0ICMjIwpwcmludCgiV2VsY29tZSB0byBSIFByb2dyYW1taW5nISIpCiMjIyBQcm9ibGVtIDEuMSAtIEVuZCAjIyMKYGBgCgoyLiAgSW5zdGFsbCBhbmQgbG9hZCB0aGUgc3RyaW5nciBwYWNrYWdlLCB3aGljaCBpcyB1c2VmdWwgZm9yIHN0cmluZyBtYW5pcHVsYXRpb24uCgpgYGB7cn0KIyMjIFByb2JsZW0gMS4yIC0gU3RhcnQgIyMjCmluc3RhbGwucGFja2FnZXMoInN0cmluZ3IiKQpsaWJyYXJ5KHN0cmluZ3IpCiMjIyBQcm9ibGVtIDEuMiAtIEVuZCAjIyMKYGBgCgojIyMgKioyLiBCYXNpYyBBcml0aG1ldGljIGFuZCBWYXJpYWJsZXMqKgoKUiBjYW4gcGVyZm9ybSBiYXNpYyBhcml0aG1ldGljIG9wZXJhdGlvbnMgYW5kIGhhbmRsZSB2YXJpYWJsZXMgZWFzaWx5LiBWYXJpYWJsZXMgaW4gUiBhcmUgdXNlZCB0byBzdG9yZSBkYXRhIHRoYXQgY2FuIGJlIHVzZWQgbGF0ZXIgaW4gdGhlIHByb2dyYW0uIFVuZGVyc3RhbmRpbmcgaG93IHRvIHBlcmZvcm0gYmFzaWMgYXJpdGhtZXRpYyBhbmQgd29yayB3aXRoIHZhcmlhYmxlcyBpcyBmdW5kYW1lbnRhbCB0byBwcm9ncmFtbWluZyBpbiBSLgoKSW4gdGhpcyBzZWN0aW9uLCB5b3Ugd2lsbCBsZWFybiBob3cgdG8gcGVyZm9ybSBhcml0aG1ldGljIG9wZXJhdGlvbnMgYW5kIGFzc2lnbiB2YWx1ZXMgdG8gdmFyaWFibGVzLiBBcml0aG1ldGljIG9wZXJhdGlvbnMgaW5jbHVkZSBhZGRpdGlvbiwgc3VidHJhY3Rpb24sIG11bHRpcGxpY2F0aW9uLCBhbmQgZGl2aXNpb24uIFZhcmlhYmxlcyBhbGxvdyB5b3UgdG8gc3RvcmUgcmVzdWx0cyBhbmQgcmV1c2UgdGhlbSB3aXRob3V0IHJldHlwaW5nIHRoZSBzYW1lIHZhbHVlcy4gTWFzdGVyaW5nIHRoZXNlIGJhc2ljcyBpcyBjcnVjaWFsIGZvciB3cml0aW5nIG1vcmUgY29tcGxleCBSIHNjcmlwdHMgaW4gdGhlIGZ1dHVyZS4KCiMjIyMgKipJbnN0cnVjdGlvbnMqKgoKMS4gICoqUGVyZm9ybSBCYXNpYyBDYWxjdWxhdGlvbnMqKjogQWRkLCBzdWJ0cmFjdCwgbXVsdGlwbHksIGFuZCBkaXZpZGUgbnVtYmVycy4gVGhlc2Ugb3BlcmF0aW9ucyBhcmUgdGhlIGZvdW5kYXRpb24gb2YgbW9yZSBjb21wbGV4IGNhbGN1bGF0aW9ucy4KCmBgYHtyfQojIEJhc2ljIGFyaXRobWV0aWMKNSArIDMKMTAgLSAyCjQgKiAzCjggLyAyCmBgYAoKMi4gICoqQXNzaWduIFZhbHVlcyB0byBWYXJpYWJsZXMqKjogU3RvcmUgcmVzdWx0cyBpbiB2YXJpYWJsZXMgYW5kIHVzZSB0aGVtLiBWYXJpYWJsZXMgbWFrZSBpdCBwb3NzaWJsZSB0byBzYXZlIGludGVybWVkaWF0ZSByZXN1bHRzIGFuZCByZXVzZSB0aGVtIHdpdGhvdXQgcmV0eXBpbmcgdGhlIHNhbWUgdmFsdWVzLgoKYGBge3J9CiMgQXNzaWduaW5nIHZhbHVlcyB0byB2YXJpYWJsZXMKeCA8LSAxMAp5IDwtIDUKcmVzdWx0IDwtIHggKyB5CnJlc3VsdApgYGAKCioqUHJvYmxlbXMgZm9yIFN0dWRlbnRzKio6CgpJdOKAmXMgdGltZSB0byBwcmFjdGljZSBiYXNpYyBhcml0aG1ldGljIGFuZCB2YXJpYWJsZSBhc3NpZ25tZW50cy4gU29sdmUgdGhlIGZvbGxvd2luZyBwcm9ibGVtczoKCjEuICBDYWxjdWxhdGUgdGhlIHJlc3VsdCBvZiAqKmA3ICogOGAqKiBhbmQgYXNzaWduIGl0IHRvIGEgdmFyaWFibGUgbmFtZWQgKipgcHJvZHVjdGAqKi4KCmBgYHtyfQojIyMgUHJvYmxlbSAyLjEgLSBTdGFydCAjIyMKcHJvZHVjdCA8LSA3ICogOAojIyMgUHJvYmxlbSAyLjIgLSBFbmQgIyMjCgojIFRlc3QgRmVlZGJhY2sKdHJ5Q2F0Y2goewogIHRlc3R0aGF0Ojp0ZXN0X3RoYXQoIlByb2JsZW0gMi4xIiwgewogICAgdGVzdHRoYXQ6OmV4cGVjdF9lcXVhbChwcm9kdWN0LCA1NikKICB9KQp9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsKICBtZXNzYWdlKCJFbnN1cmUgdGhhdCB5b3UgY29ycmVjdGx5IGFzc2lnbiB0aGUgcHJvZHVjdCBvZiA3ICogOCB0byB0aGUgdmFyaWFibGUgJ3Byb2R1Y3QnLiIpCn0pCmBgYAoKMi4gIEFzc2lnbiB0aGUgdmFsdWUgKipgMTVgKiogdG8gYSB2YXJpYWJsZSBuYW1lZCAqKmBhYCoqIGFuZCAqKmAyMGAqKiB0byBhIHZhcmlhYmxlIG5hbWVkICoqYGJgKiouIFRoZW4sIGNhbGN1bGF0ZSB0aGUgc3VtIG9mICoqYGFgKiogYW5kICoqYGJgKiouCgpgYGB7cn0KIyMjIFByb2JsZW0gMi4yIC0gU3RhcnQgIyMjCmEgPC0gMTUKYiA8LSAyMApzdW1fYWIgPC0gYSArIGIKIyMjIFByb2JsZW0gMi4yIC0gRW5kICMjIwoKIyBIaWRkZW4gdGVzdCBjb2RlCnRyeUNhdGNoKHsKICB0ZXN0dGhhdDo6dGVzdF90aGF0KCJQcm9ibGVtIDIuMiIsIHsKICAgIHRlc3R0aGF0OjpleHBlY3RfZXF1YWwoYSwgMTUpCiAgICB0ZXN0dGhhdDo6ZXhwZWN0X2VxdWFsKGIsIDIwKQogICAgdGVzdHRoYXQ6OmV4cGVjdF9lcXVhbChzdW1fYWIsIDM1KQogIH0pCn0sIGVycm9yID0gZnVuY3Rpb24oZSkgewogIG1lc3NhZ2UoIkVuc3VyZSB0aGF0IHlvdSBjb3JyZWN0bHkgYXNzaWduIHZhbHVlcyB0byAnYScgYW5kICdiJywgYW5kIGNvbXB1dGUgdGhlaXIgc3VtIGluICdzdW1fYWInLiIpCn0pCgpgYGAKCiMjIyAqKjMuIFZlY3RvcnMgYW5kIERhdGEgVHlwZXMqKgoKVmVjdG9ycyBhcmUgb25lIG9mIHRoZSBiYXNpYyBkYXRhIHN0cnVjdHVyZXMgaW4gUi4gVGhleSBjYW4gc3RvcmUgYSBzZXF1ZW5jZSBvZiBlbGVtZW50cyBvZiB0aGUgc2FtZSB0eXBlLCBzdWNoIGFzIG51bWJlcnMsIGNoYXJhY3RlcnMsIG9yIGxvZ2ljYWwgdmFsdWVzLiBVbmRlcnN0YW5kaW5nIHZlY3RvcnMgYW5kIGRhdGEgdHlwZXMgaXMgY3J1Y2lhbCBmb3IgZWZmZWN0aXZlIGRhdGEgbWFuaXB1bGF0aW9uIGFuZCBhbmFseXNpcy4KCkluIHRoaXMgc2VjdGlvbiwgeW91IHdpbGwgbGVhcm4gaG93IHRvIGNyZWF0ZSB2ZWN0b3JzIGFuZCBwZXJmb3JtIG9wZXJhdGlvbnMgb24gdGhlbS4gWW91IHdpbGwgYWxzbyBleHBsb3JlIGRpZmZlcmVudCBkYXRhIHR5cGVzIGluIFIuIERhdGEgdHlwZXMgZGV0ZXJtaW5lIHdoYXQga2luZCBvZiBkYXRhIGNhbiBiZSBzdG9yZWQgYW5kIGhvdyBpdCBjYW4gYmUgdXNlZC4gQmVpbmcgY29tZm9ydGFibGUgd2l0aCB0aGVzZSBjb25jZXB0cyB3aWxsIGVuYWJsZSB5b3UgdG8gaGFuZGxlIGFuZCBwcm9jZXNzIGRhdGEgZWZmaWNpZW50bHkuCgojIyMjICoqSW5zdHJ1Y3Rpb25zKioKCjEuICAqKkNyZWF0ZSBhbmQgTWFuaXB1bGF0ZSBWZWN0b3JzKio6IExlYXJuIGhvdyB0byBjcmVhdGUgdmVjdG9ycyBhbmQgcGVyZm9ybSBvcGVyYXRpb25zIG9uIHRoZW0uIFZlY3RvcnMgYXJlIHVzZWQgdG8gc3RvcmUgZGF0YSBpbiBhIGxpbmVhciBmb3JtYXQuCgoyLiAgKipVbmRlcnN0YW5kIERhdGEgVHlwZXMqKjogRXhwbG9yZSBkaWZmZXJlbnQgZGF0YSB0eXBlcyBpbiBSLiBEYXRhIHR5cGVzIGRldGVybWluZSB3aGF0IGtpbmQgb2YgZGF0YSBjYW4gYmUgc3RvcmVkIGFuZCBob3cgaXQgY2FuIGJlIHVzZWQuCgpgYGB7cn0KIyBDcmVhdGluZyB2ZWN0b3JzCm51bWJlcnMgPC0gYygxLCAyLCAzLCA0LCA1KQpjaGFyYWN0ZXJzIDwtIGMoImEiLCAiYiIsICJjIikKCiMgQmFzaWMgdmVjdG9yIG9wZXJhdGlvbnMKc3VtKG51bWJlcnMpCm1lYW4obnVtYmVycykKbGVuZ3RoKGNoYXJhY3RlcnMpCgojIERhdGEgdHlwZXMKc3RyKG51bWJlcnMpCnN0cihjaGFyYWN0ZXJzKQpgYGAKCioqUHJvYmxlbXMgZm9yIFN0dWRlbnRzKio6CgpOb3csIGxldOKAmXMgYXBwbHkgd2hhdCB5b3XigJl2ZSBsZWFybmVkIGFib3V0IHZlY3RvcnMgYW5kIGRhdGEgdHlwZXM6CgoxLiAgQ3JlYXRlIGEgdmVjdG9yIG5hbWVkICoqYHRlbXBlcmF0dXJlc2AqKiBjb250YWluaW5nIHRoZSB2YWx1ZXMgKipgMjMsIDI1LCAyMCwgMTksIDIyYCoqLgoKYGBge3J9CiMjIyBQcm9ibGVtIDMuMSAtIFN0YXJ0ICMjIwp0ZW1wZXJhdHVyZXMgPC0gYygyMywgMjUsIDIwLCAxOSwgMjIpCiMjIyBQcm9ibGVtIDMuMiAtIFN0YXJ0ICMjIwoKIyBIaWRkZW4gdGVzdCBjb2RlCnRyeUNhdGNoKHsKICB0ZXN0dGhhdDo6dGVzdF90aGF0KCJQcm9ibGVtIDMuMSIsIHsKICAgIHRlc3R0aGF0OjpleHBlY3RfdHJ1ZShpcy5udW1lcmljKHRlbXBlcmF0dXJlcykpCiAgICB0ZXN0dGhhdDo6ZXhwZWN0X2VxdWFsKGxlbmd0aCh0ZW1wZXJhdHVyZXMpLCA1KQogIH0pCn0sIGVycm9yID0gZnVuY3Rpb24oZSkgewogIG1lc3NhZ2UoIkVuc3VyZSB0aGF0ICd0ZW1wZXJhdHVyZXMnIGlzIGEgbnVtZXJpYyB2ZWN0b3Igd2l0aCB0aGUgY29ycmVjdCBsZW5ndGguIikKfSkKYGBgCgoyLiAgQ2FsY3VsYXRlIHRoZSBhdmVyYWdlIChtZWFuKSB0ZW1wZXJhdHVyZSBhbmQgc3RvcmUgaXQgaW4gYSB2YXJpYWJsZSBuYW1lZCAqKmBtZWFuX3RlbXBlcmF0dXJlYCoqLgoKYGBge3J9CiMjIyBQcm9ibGVtIDMuMiAtIFN0YXJ0ICMjIwptZWFuX3RlbXBlcmF0dXJlIDwtIG1lYW4odGVtcGVyYXR1cmVzKQojIyMgUHJvYmxlbSAzLjIgLSBlbmQgIyMjCgojIEhpZGRlbiB0ZXN0IGNvZGUKdHJ5Q2F0Y2goewogIHRlc3R0aGF0Ojp0ZXN0X3RoYXQoIlByb2JsZW0gMy4yIiwgewogICAgdGVzdHRoYXQ6OmV4cGVjdF9lcXVhbChtZWFuX3RlbXBlcmF0dXJlLCAyMS44KQogIH0pCn0sIGVycm9yID0gZnVuY3Rpb24oZSkgewogIG1lc3NhZ2UoIkVuc3VyZSB0aGF0IHlvdSBjb3JyZWN0bHkgY2FsY3VsYXRlIGFuZCBzdG9yZSB0aGUgbWVhbiBvZiAndGVtcGVyYXR1cmVzJyBpbiAnbWVhbl90ZW1wZXJhdHVyZScuIikKfSkKYGBgCgozLiAgQ3JlYXRlIGEgY2hhcmFjdGVyIHZlY3RvciBuYW1lZCAqKmBjaXRpZXNgKiogd2l0aCB0aGUgdmFsdWVzICJOZXcgWW9yayIsICJMb3MgQW5nZWxlcyIsICJDaGljYWdvIi4KCmBgYHtyfQojIyMgUHJvYmxlbSAzLjMgLSBTdGFydCAjIyMKY2l0aWVzIDwtIGMoIk5ldyBZb3JrIiwgIkxvcyBBbmdlbGVzIiwgIkNoaWNhZ28iKQojIyMgUHJvYmxlbSAzLjMgLSBFbmQgIyMjCgojIEhpZGRlbiB0ZXN0IGNvZGUKdHJ5Q2F0Y2goewogIHRlc3R0aGF0Ojp0ZXN0X3RoYXQoIlByb2JsZW0gMy4zIiwgewogICAgdGVzdHRoYXQ6OmV4cGVjdF90cnVlKGlzLmNoYXJhY3RlcihjaXRpZXMpKQogICAgdGVzdHRoYXQ6OmV4cGVjdF9lcXVhbChsZW5ndGgoY2l0aWVzKSwgMykKICB9KQp9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsKICBtZXNzYWdlKCJFbnN1cmUgdGhhdCAnY2l0aWVzJyBpcyBhIGNoYXJhY3RlciB2ZWN0b3Igd2l0aCB0aGUgY29ycmVjdCBsZW5ndGguIikKfSkKCmBgYAoKIyMjICoqNC4gRGF0YSBGcmFtZXMgYW5kIEJhc2ljIE9wZXJhdGlvbnMqKgoKRGF0YSBmcmFtZXMgYXJlIHRhYmxlLWxpa2Ugc3RydWN0dXJlcyB0aGF0IHN0b3JlIGRhdGEgaW4gcm93cyBhbmQgY29sdW1ucywgc2ltaWxhciB0byBhIHNwcmVhZHNoZWV0LiBFYWNoIGNvbHVtbiBjYW4gY29udGFpbiBkYXRhIG9mIGEgZGlmZmVyZW50IHR5cGUsIG1ha2luZyBkYXRhIGZyYW1lcyB2ZXJ5IGZsZXhpYmxlIGFuZCB1c2VmdWwgZm9yIGRhdGEgYW5hbHlzaXMuCgpJbiB0aGlzIHNlY3Rpb24sIHlvdSB3aWxsIGxlYXJuIGhvdyB0byBjcmVhdGUgZGF0YSBmcmFtZXMgYnkgY29tYmluaW5nIHZlY3RvcnMuIFlvdSB3aWxsIGFsc28gbGVhcm4gaG93IHRvIGluc3BlY3QgYW5kIG1hbmlwdWxhdGUgZGF0YSBmcmFtZXMsIGluY2x1ZGluZyB2aWV3aW5nIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGEsIHN1bW1hcml6aW5nIGl0LCBhbmQgYWRkaW5nIG5ldyBjb2x1bW5zLiBUaGVzZSBza2lsbHMgYXJlIGVzc2VudGlhbCBmb3IgaGFuZGxpbmcgcmVhbC13b3JsZCBkYXRhIGluIHlvdXIgYW5hbHlzZXMuCgojIyMjICoqSW5zdHJ1Y3Rpb25zKioKCjEuICAqKkNyZWF0ZSBhIERhdGEgRnJhbWUqKjogQ29tYmluZSB2ZWN0b3JzIGludG8gYSBkYXRhIGZyYW1lLiBEYXRhIGZyYW1lcyBhcmUgdGhlIG1vc3QgY29tbW9uIGRhdGEgc3RydWN0dXJlIHVzZWQgaW4gUiBmb3IgZGF0YSBhbmFseXNpcy4KCjIuICAqKkluc3BlY3QgYW5kIE1hbmlwdWxhdGUgRGF0YSBGcmFtZXMqKjogVXNlIGZ1bmN0aW9ucyB0byBpbnNwZWN0IGFuZCBtYW5pcHVsYXRlIGRhdGEgZnJhbWVzLiBUaGlzIGluY2x1ZGVzIHZpZXdpbmcgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YSwgc3VtbWFyaXppbmcgaXQsIGFuZCBhZGRpbmcgbmV3IGNvbHVtbnMuCgpgYGB7cn0KIyBDcmVhdGluZyBhIGRhdGEgZnJhbWUKbmFtZXMgPC0gYygiSm9obiIsICJKYW5lIiwgIkppbSIpCmFnZXMgPC0gYygyOCwgMzQsIDQwKQpkYXRhIDwtIGRhdGEuZnJhbWUoTmFtZSA9IG5hbWVzLCBBZ2UgPSBhZ2VzKQpkYXRhCgojIEluc3BlY3QgdGhlIGRhdGEgZnJhbWUKaGVhZChkYXRhKQpzdW1tYXJ5KGRhdGEpCnN0cihkYXRhKQoKIyBNYW5pcHVsYXRlIGRhdGEgZnJhbWVzCmRhdGEkQWdlX2luXzVfeWVhcnMgPC0gZGF0YSRBZ2UgKyA1CmRhdGEKYGBgCgoqKlByb2JsZW1zIGZvciBTdHVkZW50cyoqOgoKTGV04oCZcyBwcmFjdGljZSBjcmVhdGluZyBhbmQgbWFuaXB1bGF0aW5nIGRhdGEgZnJhbWVzOgoKMS4gIENyZWF0ZSBhIGRhdGEgZnJhbWUgbmFtZWQgKipgc3R1ZGVudHNgKiogd2l0aCB0aGUgY29sdW1ucyAqKmBTdHVkZW50TmFtZWAqKiBhbmQgKipgU2NvcmVgKiosIHVzaW5nIHRoZSB2ZWN0b3JzICoqYGMoIkFsaWNlIiwgIkJvYiIsICJDaGFybGllIilgKiogYW5kICoqYGMoODUsIDkwLCA4OClgKiouCgpgYGB7cn0KIyMjIFByb2JsZW0gNC4xIC0gU3RhcnQgIyMjClN0dWRlbnROYW1lIDwtIGMoIkFsaWNlIiwgIkJvYiIsICJDaGFybGllIikKU2NvcmUgPC0gYyg4NSwgOTAsIDg4KQpzdHVkZW50cyA8LSBkYXRhLmZyYW1lKFN0dWRlbnROYW1lLCBTY29yZSkKc3R1ZGVudHMKIyMjIFByb2JsZW0gNC4xIC0gRW5kICMjIwoKIyBIaWRkZW4gdGVzdCBjb2RlCnRyeUNhdGNoKHsKICB0ZXN0dGhhdDo6dGVzdF90aGF0KCJQcm9ibGVtIDQuMSIsIHsKICAgIHRlc3R0aGF0OjpleHBlY3RfdHJ1ZShpcy5kYXRhLmZyYW1lKHN0dWRlbnRzKSkKICAgIHRlc3R0aGF0OjpleHBlY3RfZXF1YWwobmFtZXMoc3R1ZGVudHMpLCBjKCJTdHVkZW50TmFtZSIsICJTY29yZSIpKQogICAgdGVzdHRoYXQ6OmV4cGVjdF9lcXVhbChucm93KHN0dWRlbnRzKSwgMykKICB9KQp9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsKICBtZXNzYWdlKCJFbnN1cmUgdGhhdCAnc3R1ZGVudHMnIGlzIGEgZGF0YSBmcmFtZSB3aXRoIHRoZSBjb3JyZWN0IHN0cnVjdHVyZS4iKQp9KQoKYGBgCgoyLiAgQWRkIGEgbmV3IGNvbHVtbiB0byB0aGUgKipgc3R1ZGVudHNgKiogZGF0YSBmcmFtZSBuYW1lZCAqKmBQYXNzYCoqLCB3aGljaCBpcyAqKmBUUlVFYCoqIGlmICoqYFNjb3JlYCoqIGlzIGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byA1MCBhbmQgKipgRkFMU0VgKiogb3RoZXJ3aXNlLgoKYGBge3J9CiMjIyBQcm9ibGVtIDQuMiAtIFN0YXJ0ICMjIwpzdHVkZW50cyRQYXNzIDwtIHN0dWRlbnRzJFNjb3JlID49IDUwCnN0dWRlbnRzCiMjIyBQcm9ibGVtIDQuMiAtIEVuZCAjIyMKCiMgSGlkZGVuIHRlc3QgY29kZQp0cnlDYXRjaCh7CiAgdGVzdHRoYXQ6OnRlc3RfdGhhdCgiUHJvYmxlbSA0LjIiLCB7CiAgICB0ZXN0dGhhdDo6ZXhwZWN0X3RydWUoIlBhc3MiICVpbiUgbmFtZXMoc3R1ZGVudHMpKQogICAgdGVzdHRoYXQ6OmV4cGVjdF9lcXVhbChzdHVkZW50cyRQYXNzLCBjKFRSVUUsIFRSVUUsIFRSVUUpKQogIH0pCn0sIGVycm9yID0gZnVuY3Rpb24oZSkgewogIG1lc3NhZ2UoIkVuc3VyZSB0aGF0IHlvdSBjb3JyZWN0bHkgYWRkIHRoZSAnUGFzcycgY29sdW1uIGJhc2VkIG9uIHRoZSAnU2NvcmUnLiIpCn0pCgpgYGAKCiMjIyAqKjUuIERhdGEgTWFuaXB1bGF0aW9uIHdpdGggYGRwbHlyYCoqCgpUaGUgKipgZHBseXJgKiogcGFja2FnZSBwcm92aWRlcyBhIHNldCBvZiBmdW5jdGlvbnMgZm9yIGRhdGEgbWFuaXB1bGF0aW9uLCBtYWtpbmcgaXQgZWFzaWVyIHRvIHBlcmZvcm0gY29tbW9uIGRhdGEgbWFuaXB1bGF0aW9uIHRhc2tzIHN1Y2ggYXMgZmlsdGVyaW5nIHJvd3MsIHNlbGVjdGluZyBjb2x1bW5zLCBhbmQgc3VtbWFyaXppbmcgZGF0YS4gVGhlc2UgZnVuY3Rpb25zIGFyZSBkZXNpZ25lZCB0byBiZSBlYXN5IHRvIHVzZSBhbmQgZWZmaWNpZW50LgoKSW4gdGhpcyBzZWN0aW9uLCB5b3Ugd2lsbCBsZWFybiBob3cgdG8gbG9hZCB0aGUgKipgZHBseXJgKiogcGFja2FnZSBhbmQgdXNlIGl0cyBmdW5jdGlvbnMgdG8gbWFuaXB1bGF0ZSBkYXRhIGZyYW1lcy4gVGhlc2Ugc2tpbGxzIGFyZSB2aXRhbCBmb3IgY2xlYW5pbmcgYW5kIHRyYW5zZm9ybWluZyBkYXRhIGJlZm9yZSBhbmFseXNpcy4KCiMjIyMgKipJbnN0cnVjdGlvbnMqKgoKMS4gICoqTG9hZCB0aGUgYGRwbHlyYCBQYWNrYWdlKio6IEVuc3VyZSB0aGUgcGFja2FnZSBpcyBsb2FkZWQuIElmIG5vdCwgaW5zdGFsbCBpdCB1c2luZyAqKmBpbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpYCoqLgoKMi4gICoqUGVyZm9ybSBCYXNpYyBEYXRhIE1hbmlwdWxhdGlvbioqOiBVc2UgKipgZmlsdGVyYCoqLCAqKmBzZWxlY3RgKiosICoqYG11dGF0ZWAqKiwgYW5kICoqYHN1bW1hcml6ZWAqKiBmdW5jdGlvbnMgdG8gbWFuaXB1bGF0ZSBkYXRhIGZyYW1lcy4KCmBgYHtyfQojIEVuc3VyZSB0aGUgcGFja2FnZSBpcyBsb2FkZWQKbGlicmFyeShkcGx5cikKCiMgRmlsdGVyaW5nIGRhdGEKZmlsdGVyZWRfZGF0YSA8LSBmaWx0ZXIoZGF0YSwgQWdlID4gMzApCmZpbHRlcmVkX2RhdGEKCiMgU2VsZWN0aW5nIGNvbHVtbnMKc2VsZWN0ZWRfZGF0YSA8LSBzZWxlY3QoZGF0YSwgTmFtZSwgQWdlKQpzZWxlY3RlZF9kYXRhCgojIE11dGF0aW5nIGRhdGEKbXV0YXRlZF9kYXRhIDwtIG11dGF0ZShkYXRhLCBBZ2VfaW5fMTBfeWVhcnMgPSBBZ2UgKyAxMCkKbXV0YXRlZF9kYXRhCgojIFN1bW1hcml6aW5nIGRhdGEKc3VtbWFyeV9kYXRhIDwtIHN1bW1hcml6ZShkYXRhLCBBdmVyYWdlX0FnZSA9IG1lYW4oQWdlKSkKc3VtbWFyeV9kYXRhCgpgYGAKCioqUHJvYmxlbXMgZm9yIFN0dWRlbnRzKio6CgpQcmFjdGljZSB1c2luZyAqKmBkcGx5cmAqKiB0byBtYW5pcHVsYXRlIGRhdGE6CgoxLiAgVXNlIHRoZSAqKmBzdHVkZW50c2AqKiBkYXRhIGZyYW1lLiBGaWx0ZXIgdGhlIHJvd3Mgd2hlcmUgKipgU2NvcmVgKiogaXMgZ3JlYXRlciB0aGFuICoqYDg1YCoqLgoKYGBge3J9CiMjIyBQcm9ibGVtIDUuMSAtIFN0YXJ0ICMjIwpmaWx0ZXJlZF9zdHVkZW50cyA8LSBmaWx0ZXIoc3R1ZGVudHMsIFNjb3JlID4gODUpCmZpbHRlcmVkX3N0dWRlbnRzCiMjIyBQcm9ibGVtIDUuMSAtIEVuZCAjIyMKCiMgSGlkZGVuIHRlc3QgY29kZQp0cnlDYXRjaCh7CiAgdGVzdHRoYXQ6OnRlc3RfdGhhdCgiUHJvYmxlbSA1LjEiLCB7CiAgICB0ZXN0dGhhdDo6ZXhwZWN0X2VxdWFsKG5yb3coZmlsdGVyZWRfc3R1ZGVudHMpLCAyKQogIH0pCn0sIGVycm9yID0gZnVuY3Rpb24oZSkgewogIG1lc3NhZ2UoIkVuc3VyZSB0aGF0IHlvdSBjb3JyZWN0bHkgZmlsdGVyICdzdHVkZW50cycgYmFzZWQgb24gJ1Njb3JlJy4iKQp9KQoKYGBgCgoyLiAgU2VsZWN0IG9ubHkgdGhlICoqYFN0dWRlbnROYW1lYCoqIGNvbHVtbiBmcm9tIHRoZSAqKmBzdHVkZW50c2AqKiBkYXRhIGZyYW1lLgoKYGBge3J9CiMjIyBQcm9ibGVtIDUuMiAtIFN0YXJ0ICMjIwpzZWxlY3RlZF9zdHVkZW50cyA8LSBzZWxlY3Qoc3R1ZGVudHMsIFN0dWRlbnROYW1lKQpzZWxlY3RlZF9zdHVkZW50cwojIyMgUHJvYmxlbSA1LjIgLSBFbmQgIyMjCgojIEhpZGRlbiB0ZXN0IGNvZGUKdHJ5Q2F0Y2goewogIHRlc3R0aGF0Ojp0ZXN0X3RoYXQoIlByb2JsZW0gNS4yIiwgewogICAgdGVzdHRoYXQ6OmV4cGVjdF9lcXVhbChuYW1lcyhzZWxlY3RlZF9zdHVkZW50cyksICJTdHVkZW50TmFtZSIpCiAgfSkKfSwgZXJyb3IgPSBmdW5jdGlvbihlKSB7CiAgbWVzc2FnZSgiRW5zdXJlIHRoYXQgeW91IGNvcnJlY3RseSBzZWxlY3QgdGhlICdTdHVkZW50TmFtZScgY29sdW1uLiIpCn0pCgpgYGAKCjMuICBDcmVhdGUgYSBuZXcgY29sdW1uIGluIHRoZSAqKmBzdHVkZW50c2AqKiBkYXRhIGZyYW1lIG5hbWVkICoqYFNjb3JlX2luXzEwX3llYXJzYCoqIHRoYXQgYWRkcyAqKmAxMGAqKiB0byB0aGUgY3VycmVudCAqKmBTY29yZWAqKi4KCmBgYHtyfQojIyMgUHJvYmxlbSA1LjMgLSBTdGFydCAjIyMKbXV0YXRlZF9zdHVkZW50cyA8LSBtdXRhdGUoc3R1ZGVudHMsIFNjb3JlX2luXzEwX3llYXJzID0gU2NvcmUgKyAxMCkKbXV0YXRlZF9zdHVkZW50cwojIyMgUHJvYmxlbSA1LjMgLSBFbmQgIyMjCgojIEhpZGRlbiB0ZXN0IGNvZGUKdHJ5Q2F0Y2goewogIHRlc3R0aGF0Ojp0ZXN0X3RoYXQoIlByb2JsZW0gNS4zIiwgewogICAgdGVzdHRoYXQ6OmV4cGVjdF90cnVlKCJTY29yZV9pbl8xMF95ZWFycyIgJWluJSBuYW1lcyhtdXRhdGVkX3N0dWRlbnRzKSkKICAgIHRlc3R0aGF0OjpleHBlY3RfZXF1YWwobXV0YXRlZF9zdHVkZW50cyRTY29yZV9pbl8xMF95ZWFycywgYyg5NSwgMTAwLCA5OCkpCiAgfSkKfSwgZXJyb3IgPSBmdW5jdGlvbihlKSB7CiAgbWVzc2FnZSgiRW5zdXJlIHRoYXQgeW91IGNvcnJlY3RseSBhZGQgdGhlICdTY29yZV9pbl8xMF95ZWFycycgY29sdW1uLiIpCn0pCgpgYGAKCjQuICBDYWxjdWxhdGUgdGhlIGF2ZXJhZ2Ugc2NvcmUgb2YgdGhlIHN0dWRlbnRzLgoKYGBge3J9CiMjIyBQcm9ibGVtIDUuNCAtIFN0YXJ0ICMjIwphdmVyYWdlX3Njb3JlIDwtIHN1bW1hcml6ZShzdHVkZW50cywgQXZlcmFnZV9TY29yZSA9IG1lYW4oU2NvcmUpKQphdmVyYWdlX3Njb3JlCiMjIyBQcm9ibGVtIDUuNCAtIFN0YXJ0ICMjIwoKIyBIaWRkZW4gdGVzdCBjb2RlCnRyeUNhdGNoKHsKICB0ZXN0dGhhdDo6dGVzdF90aGF0KCJQcm9ibGVtIDUuNCIsIHsKICAgIHRlc3R0aGF0OjpleHBlY3RfZXF1YWwoYXZlcmFnZV9zY29yZSRBdmVyYWdlX1Njb3JlLCA4Ny42NywgdG9sZXJhbmNlID0gMC4wMSkKICB9KQp9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsKICBtZXNzYWdlKCJFbnN1cmUgdGhhdCB5b3UgY29ycmVjdGx5IGNhbGN1bGF0ZSB0aGUgYXZlcmFnZSBzY29yZSBvZiAnc3R1ZGVudHMnLiIpCn0pCgpgYGAKCiMjIyAqKjYuIERhdGEgVmlzdWFsaXphdGlvbiB3aXRoIGBnZ3Bsb3QyYCoqCgpUaGUgKipgZ2dwbG90MmAqKiBwYWNrYWdlIGlzIGEgcG93ZXJmdWwgdG9vbCBmb3IgY3JlYXRpbmcgdmlzdWFsaXphdGlvbnMuIEl0IHVzZXMgYSBjb2hlcmVudCBzeXN0ZW0gb2YgImdyYW1tYXIiIHRvIGNyZWF0ZSBhIHZhcmlldHkgb2YgcGxvdHMsIG1ha2luZyBpdCBlYXNpZXIgdG8gdW5kZXJzdGFuZCBhbmQgY29tbXVuaWNhdGUgZGF0YSBpbnNpZ2h0cy4KCkluIHRoaXMgc2VjdGlvbiwgeW91IHdpbGwgbGVhcm4gaG93IHRvIGxvYWQgdGhlICoqYGdncGxvdDJgKiogcGFja2FnZSBhbmQgY3JlYXRlIGJhc2ljIHBsb3RzLiBWaXN1YWxpemF0aW9ucyBhcmUgY3J1Y2lhbCBmb3IgcHJlc2VudGluZyB5b3VyIGZpbmRpbmdzIGluIGEgY2xlYXIgYW5kIGNvbXBlbGxpbmcgd2F5LgoKIyMjIyAqKkluc3RydWN0aW9ucyoqCgoxLiAgKipMb2FkIHRoZSBgZ2dwbG90MmAgUGFja2FnZSoqOiBFbnN1cmUgdGhlIHBhY2thZ2UgaXMgbG9hZGVkLiBJZiBub3QsIGluc3RhbGwgaXQgdXNpbmcgKipgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpYCoqLgoKMi4gICoqQ3JlYXRlIGEgU2ltcGxlIFBsb3QqKjogTGVhcm4gdGhlIGJhc2ljcyBvZiBjcmVhdGluZyBwbG90cyB3aXRoICoqYGdncGxvdDJgKiosIHN1Y2ggYXMgc2NhdHRlciBwbG90cyBhbmQgYmFyIGNoYXJ0cy4KCmBgYHtyfQojIEVuc3VyZSB0aGUgcGFja2FnZSBpcyBsb2FkZWQKbGlicmFyeShnZ3Bsb3QyKQoKIyBDcmVhdGluZyBhIHNjYXR0ZXIgcGxvdApnZ3Bsb3QoZGF0YSwgYWVzKHggPSBOYW1lLCB5ID0gQWdlKSkgKwogIGdlb21fcG9pbnQoKQoKIyBDcmVhdGluZyBhIGJhciBjaGFydApnZ3Bsb3QoZGF0YSwgYWVzKHggPSBOYW1lLCB5ID0gQWdlKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKQoKYGBgCgoqKlByb2JsZW1zIGZvciBTdHVkZW50cyoqOgoKTm93IGl04oCZcyB5b3VyIHR1cm4gdG8gY3JlYXRlIHZpc3VhbGl6YXRpb25zOgoKMS4gIENyZWF0ZSBhIHNjYXR0ZXIgcGxvdCBvZiAqKmBTY29yZWAqKiB2cy4gKipgU3R1ZGVudE5hbWVgKiogdXNpbmcgdGhlICoqYHN0dWRlbnRzYCoqIGRhdGEgZnJhbWUuCgpgYGB7cn0KIyMjIFByb2JsZW0gNi4xIC0gU3RhcnQgIyMjCmdncGxvdChzdHVkZW50cywgYWVzKHggPSBTdHVkZW50TmFtZSwgeSA9IFNjb3JlKSkgKwogIGdlb21fcG9pbnQoKQojIyMgUHJvYmxlbSA2LjEgLSBFbmQgIyMjCgojIEhpZGRlbiB0ZXN0IGNvZGUKdHJ5Q2F0Y2goewogIHRlc3R0aGF0Ojp0ZXN0X3RoYXQoIlByb2JsZW0gNi4xIiwgewogICAgdGVzdHRoYXQ6OmV4cGVjdF9zM19jbGFzcyhnZ3Bsb3Qoc3R1ZGVudHMsIGFlcyh4ID0gU3R1ZGVudE5hbWUsIHkgPSBTY29yZSkpICsgZ2VvbV9wb2ludCgpLCAiZ2ciKQogIH0pCn0sIGVycm9yID0gZnVuY3Rpb24oZSkgewogIG1lc3NhZ2UoIkVuc3VyZSB0aGF0IHlvdSBjb3JyZWN0bHkgY3JlYXRlIGEgc2NhdHRlciBwbG90IG9mICdTY29yZScgdnMuICdTdHVkZW50TmFtZScuIikKfSkKCmBgYAoKMi4gIENyZWF0ZSBhIGJhciBjaGFydCBzaG93aW5nIHRoZSAqKmBTY29yZWAqKiBmb3IgZWFjaCAqKmBTdHVkZW50TmFtZWAqKi4KCmBgYHtyfQojIyMgUHJvYmxlbSA2LjIgLSBTdGFydCAjIyMKZ2dwbG90KHN0dWRlbnRzLCBhZXMoeCA9IFN0dWRlbnROYW1lLCB5ID0gU2NvcmUpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpCiMjIyBQcm9ibGVtIDYuMiAtIEVuZCAjIyMKCiMgSGlkZGVuIHRlc3QgY29kZQp0cnlDYXRjaCh7CiAgdGVzdHRoYXQ6OnRlc3RfdGhhdCgiUHJvYmxlbSA2LjIiLCB7CiAgICB0ZXN0dGhhdDo6ZXhwZWN0X3MzX2NsYXNzKGdncGxvdChzdHVkZW50cywgYWVzKHggPSBTdHVkZW50TmFtZSwgeSA9IFNjb3JlKSkgKyBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiksICJnZyIpCiAgfSkKfSwgZXJyb3IgPSBmdW5jdGlvbihlKSB7CiAgbWVzc2FnZSgiRW5zdXJlIHRoYXQgeW91IGNvcnJlY3RseSBjcmVhdGUgYSBiYXIgY2hhcnQgc2hvd2luZyB0aGUgJ1Njb3JlJyBmb3IgZWFjaCAnU3R1ZGVudE5hbWUnLiIpCn0pCgpgYGAKCiMjIyA3LiBQcmFjdGljYWwgRXhlcmNpc2U6IEFuYWx5emluZyBQYXRpZW50IERhdGEKCk5vdyB0aGF0IHlvdSd2ZSBsZWFybmVkIHRoZSBiYXNpY3MsIGxldCdzIGFwcGx5IHRoZXNlIHNraWxscyB0byBhIHByYWN0aWNhbCBleGVyY2lzZS4gV2UnbGwgYW5hbHl6ZSBhIHNpbXBsZSBwYXRpZW50IGRhdGFzZXQgdG8gZ2FpbiBpbnNpZ2h0cyBpbnRvIHBhdGllbnQgY2hhcmFjdGVyaXN0aWNzLiBUaGUgZGF0YXNldCBpcyBhbHJlYWR5IGxvYWRlZCBpbnRvIG1lbW9yeSBhbmQgY2FuIGJlIHVzZWQgZm9yIHRoZSBmb2xsb3dpbmcgcXVlc3Rpb25zLgoKSW4gdGhpcyBzZWN0aW9uLCB5b3Ugd2lsbCBsb2FkIGEgZGF0YXNldCwgaW5zcGVjdCBhbmQgY2xlYW4gdGhlIGRhdGEsIHBlcmZvcm0gYmFzaWMgZGF0YSBhbmFseXNpcywgYW5kIGNyZWF0ZSB2aXN1YWxpemF0aW9ucy4gVGhpcyBleGVyY2lzZSB3aWxsIGNvbnNvbGlkYXRlIHlvdXIgbGVhcm5pbmcgYW5kIGdpdmUgeW91IGhhbmRzLW9uIGV4cGVyaWVuY2Ugd2l0aCByZWFsLXdvcmxkIGRhdGEuCgojIyMjIEluc3RydWN0aW9ucwoKMS4gICoqTG9hZCB0aGUgRGF0YXNldCoqOiBMb2FkIGEgQ1NWIGZpbGUgY29udGFpbmluZyBwYXRpZW50IGRhdGEuCjIuICAqKkluc3BlY3QgdGhlIERhdGEqKjogVW5kZXJzdGFuZCB0aGUgc3RydWN0dXJlIGFuZCBjb250ZW50cyBvZiB0aGUgZGF0YXNldC4KMy4gICoqQ2xlYW4gdGhlIERhdGEqKjogSGFuZGxlIG1pc3NpbmcgdmFsdWVzIGFuZCBlbnN1cmUgZGF0YSB0eXBlcyBhcmUgY29ycmVjdC4KNC4gICoqQW5hbHl6ZSB0aGUgRGF0YSoqOiBQZXJmb3JtIGJhc2ljIGRhdGEgYW5hbHlzaXMgdG8gZ2FpbiBpbnNpZ2h0cy4KNS4gICoqVmlzdWFsaXplIHRoZSBEYXRhKio6IENyZWF0ZSB2aXN1YWxpemF0aW9ucyB0byBwcmVzZW50IHRoZSBhbmFseXNpcyByZXN1bHRzLgoKYGBge3J9CiMgTG9hZCB0aGUgZGF0YXNldApwYXRpZW50cyA8LSByZWFkLmNzdigicGF0aWVudF9kYXRhX2xhcmdlLmNzdiIpCgojIEluc3BlY3QgdGhlIGRhdGEKaGVhZChwYXRpZW50cykKc3VtbWFyeShwYXRpZW50cykKc3RyKHBhdGllbnRzKQoKIyBDbGVhbiB0aGUgZGF0YQpwYXRpZW50cyA8LSBuYS5vbWl0KHBhdGllbnRzKQpwYXRpZW50cyRBZ2UgPC0gYXMubnVtZXJpYyhwYXRpZW50cyRBZ2UpCnBhdGllbnRzJEdlbmRlciA8LSBhcy5mYWN0b3IocGF0aWVudHMkR2VuZGVyKQpwYXRpZW50cyRIZWFydERpc2Vhc2UgPC0gYXMuZmFjdG9yKHBhdGllbnRzJEhlYXJ0RGlzZWFzZSkKc3VtbWFyeShwYXRpZW50cykKCiMgQW5hbHl6ZSB0aGUgZGF0YQojIEFnZSBkaXN0cmlidXRpb24Kc3VtbWFyeShwYXRpZW50cyRBZ2UpCmhpc3QocGF0aWVudHMkQWdlLCBtYWluID0gIkFnZSBEaXN0cmlidXRpb24iLCB4bGFiID0gIkFnZSIsIGNvbCA9ICJsaWdodGJsdWUiLCBib3JkZXIgPSAid2hpdGUiKQoKIyBCbG9vZCBwcmVzc3VyZSBkaXN0cmlidXRpb24Kc3VtbWFyeShwYXRpZW50cyRCbG9vZFByZXNzdXJlKQpoaXN0KHBhdGllbnRzJEJsb29kUHJlc3N1cmUsIG1haW4gPSAiQmxvb2QgUHJlc3N1cmUgRGlzdHJpYnV0aW9uIiwgeGxhYiA9ICJCbG9vZCBQcmVzc3VyZSIsIGNvbCA9ICJsaWdodGdyZWVuIiwgYm9yZGVyID0gIndoaXRlIikKCiMgQ2hvbGVzdGVyb2wgZGlzdHJpYnV0aW9uCnN1bW1hcnkocGF0aWVudHMkQ2hvbGVzdGVyb2wpCmhpc3QocGF0aWVudHMkQ2hvbGVzdGVyb2wsIG1haW4gPSAiQ2hvbGVzdGVyb2wgRGlzdHJpYnV0aW9uIiwgeGxhYiA9ICJDaG9sZXN0ZXJvbCIsIGNvbCA9ICJsaWdodGNvcmFsIiwgYm9yZGVyID0gIndoaXRlIikKCiMgRXhwbG9yaW5nIHJlbGF0aW9uc2hpcHMKIyBBZ2UgYW5kIGhlYXJ0IGRpc2Vhc2UKYm94cGxvdChBZ2UgfiBIZWFydERpc2Vhc2UsIGRhdGEgPSBwYXRpZW50cywgbWFpbiA9ICJBZ2UgYW5kIEhlYXJ0IERpc2Vhc2UiLCB4bGFiID0gIkhlYXJ0IERpc2Vhc2UiLCB5bGFiID0gIkFnZSIsIGNvbCA9IGMoImxpZ2h0Ymx1ZSIsICJsaWdodHBpbmsiKSkKCiMgQmxvb2QgcHJlc3N1cmUgYW5kIGhlYXJ0IGRpc2Vhc2UKYm94cGxvdChCbG9vZFByZXNzdXJlIH4gSGVhcnREaXNlYXNlLCBkYXRhID0gcGF0aWVudHMsIG1haW4gPSAiQmxvb2QgUHJlc3N1cmUgYW5kIEhlYXJ0IERpc2Vhc2UiLCB4bGFiID0gIkhlYXJ0IERpc2Vhc2UiLCB5bGFiID0gIkJsb29kIFByZXNzdXJlIiwgY29sID0gYygibGlnaHRncmVlbiIsICJsaWdodHBpbmsiKSkKCiMgQ2hvbGVzdGVyb2wgYW5kIGhlYXJ0IGRpc2Vhc2UKYm94cGxvdChDaG9sZXN0ZXJvbCB+IEhlYXJ0RGlzZWFzZSwgZGF0YSA9IHBhdGllbnRzLCBtYWluID0gIkNob2xlc3Rlcm9sIGFuZCBIZWFydCBEaXNlYXNlIiwgeGxhYiA9ICJIZWFydCBEaXNlYXNlIiwgeWxhYiA9ICJDaG9sZXN0ZXJvbCIsIGNvbCA9IGMoImxpZ2h0Y29yYWwiLCAibGlnaHRwaW5rIikpCmBgYAoKKipQcm9ibGVtcyBmb3IgU3R1ZGVudHMqKjoKCk5vdyBpdCdzIHlvdXIgdHVybiB0byBhbmFseXplIHRoZSBwYXRpZW50IGRhdGEuIEZvbGxvdyB0aGUgc3RlcHMgYmVsb3cgdG8gY29tcGxldGUgdGhlIGV4ZXJjaXNlcy4KCjEuICAqKkZpbHRlciBhbmQgU3VtbWFyaXplIERhdGEqKjogRmlsdGVyIHRoZSAqKmBwYXRpZW50c2AqKiBkYXRhc2V0IHRvIGluY2x1ZGUgb25seSBtYWxlIHBhdGllbnRzLCBhbmQgdGhlbiBjYWxjdWxhdGUgdGhlIGF2ZXJhZ2UgYWdlIG9mIHRoZXNlIHBhdGllbnRzLiBTdG9yZSB0aGUgcmVzdWx0IGluIGEgdmFyaWFibGUgbmFtZWQgKipgYXZnX2FnZV9tYWxlYCoqLgoKYGBge3J9CiMjIyBQcm9ibGVtIDcuMSAtIFN0YXJ0ICMjIwphdmdfYWdlX21hbGUgPC0gcGF0aWVudHMgJT4lCiAgZmlsdGVyKEdlbmRlciA9PSAiTWFsZSIpICU+JQogIHN1bW1hcml6ZShhdmdfYWdlID0gbWVhbihBZ2UpKSAlPiUKICBwdWxsKGF2Z19hZ2UpCmF2Z19hZ2VfbWFsZQojIyMgUHJvYmxlbSA3LjEgLSBFbmQgIyMjCgojIEhpZGRlbiB0ZXN0IGNvZGUKdHJ5Q2F0Y2goewogIHRlc3R0aGF0Ojp0ZXN0X3RoYXQoIlByb2JsZW0gNy4xIiwgewogICAgdGVzdHRoYXQ6OmV4cGVjdF9lcXVhbChhdmdfYWdlX21hbGUsIG1lYW4ocGF0aWVudHMkQWdlW3BhdGllbnRzJEdlbmRlciA9PSAiTWFsZSJdKSwgdG9sZXJhbmNlID0gMC4wMSkKICB9KQp9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsKICBtZXNzYWdlKCJFbnN1cmUgdGhhdCB5b3UgY29ycmVjdGx5IGZpbHRlciBhbmQgY2FsY3VsYXRlIHRoZSBhdmVyYWdlIGFnZSBmb3IgbWFsZSBwYXRpZW50cy4iKQp9KQoKYGBgCgoyLiAgKipTZWxlY3QgYW5kIE11dGF0ZSBEYXRhKio6IENyZWF0ZSBhIG5ldyBkYXRhIGZyYW1lIG5hbWVkICoqYGhpZ2hfY2hvbGVzdGVyb2xgKiogdGhhdCBpbmNsdWRlcyBvbmx5IHRoZSAqKmBQYXRpZW50SURgKiogYW5kICoqYENob2xlc3Rlcm9sYCoqIGNvbHVtbnMgZm9yIHBhdGllbnRzIHdpdGggY2hvbGVzdGVyb2wgbGV2ZWxzIGdyZWF0ZXIgdGhhbiAyNTAuIEFkZCBhIG5ldyBjb2x1bW4gdG8gdGhpcyBkYXRhIGZyYW1lIG5hbWVkICoqYENob2xlc3Rlcm9sQ2F0ZWdvcnlgKiogdGhhdCBsYWJlbHMgdGhlc2UgcGF0aWVudHMgYXMgIkhpZ2giLgoKYGBge3J9CiMjIyBQcm9ibGVtIDcuMiAtIFN0YXJ0ICMjIwpoaWdoX2Nob2xlc3Rlcm9sIDwtIHBhdGllbnRzICU+JQogIGZpbHRlcihDaG9sZXN0ZXJvbCA+IDI1MCkgJT4lCiAgc2VsZWN0KFBhdGllbnRJRCwgQ2hvbGVzdGVyb2wpICU+JQogIG11dGF0ZShDaG9sZXN0ZXJvbENhdGVnb3J5ID0gIkhpZ2giKQpoaWdoX2Nob2xlc3Rlcm9sCiMjIyBQcm9ibGVtIDcuMiAtIEVuZCAjIyMKCiMgSGlkZGVuIHRlc3QgY29kZQp0cnlDYXRjaCh7CiAgdGVzdHRoYXQ6OnRlc3RfdGhhdCgiUHJvYmxlbSA3LjIiLCB7CiAgICB0ZXN0dGhhdDo6ZXhwZWN0X3RydWUoYWxsKGhpZ2hfY2hvbGVzdGVyb2wkQ2hvbGVzdGVyb2wgPiAyNTApKQogICAgdGVzdHRoYXQ6OmV4cGVjdF90cnVlKCJDaG9sZXN0ZXJvbENhdGVnb3J5IiAlaW4lIG5hbWVzKGhpZ2hfY2hvbGVzdGVyb2wpKQogICAgdGVzdHRoYXQ6OmV4cGVjdF9lcXVhbCh1bmlxdWUoaGlnaF9jaG9sZXN0ZXJvbCRDaG9sZXN0ZXJvbENhdGVnb3J5KSwgIkhpZ2giKQogIH0pCn0sIGVycm9yID0gZnVuY3Rpb24oZSkgewogIG1lc3NhZ2UoIkVuc3VyZSB0aGF0IHlvdSBjb3JyZWN0bHkgZmlsdGVyLCBzZWxlY3QsIGFuZCBtdXRhdGUgdGhlIGRhdGEgZm9yIGhpZ2ggY2hvbGVzdGVyb2wgcGF0aWVudHMuIikKfSkKCmBgYAoKMy4gICoqVmlzdWFsaXplIERhdGEqKjogQ3JlYXRlIGEgYm94cGxvdCB0byBjb21wYXJlIHRoZSBhZ2VzIG9mIHBhdGllbnRzIHdpdGggYW5kIHdpdGhvdXQgaGVhcnQgZGlzZWFzZS4gVXNlIHRoZSAqKmBnZ3Bsb3QyYCoqIHBhY2thZ2UgZm9yIHRoaXMgdmlzdWFsaXphdGlvbi4KCmBgYHtyfQojIyMgUHJvYmxlbSA3LjMgLSBTdGFydCAjIyMKZ2dwbG90KHBhdGllbnRzLCBhZXMoeCA9IEhlYXJ0RGlzZWFzZSwgeSA9IEFnZSwgZmlsbCA9IEhlYXJ0RGlzZWFzZSkpICsKICBnZW9tX2JveHBsb3QoKSArCiAgbGFicyh0aXRsZSA9ICJBZ2UgQ29tcGFyaXNvbiBvZiBQYXRpZW50cyB3aXRoIGFuZCB3aXRob3V0IEhlYXJ0IERpc2Vhc2UiLCB4ID0gIkhlYXJ0IERpc2Vhc2UiLCB5ID0gIkFnZSIpCiMjIyBQcm9ibGVtIDcuMyAtIEVuZCAjIyMKCiMgSGlkZGVuIHRlc3QgY29kZQp0cnlDYXRjaCh7CiAgdGVzdHRoYXQ6OnRlc3RfdGhhdCgiUHJvYmxlbSA3LjMiLCB7CiAgICB0ZXN0dGhhdDo6ZXhwZWN0X3MzX2NsYXNzKGdncGxvdChwYXRpZW50cywgYWVzKHggPSBIZWFydERpc2Vhc2UsIHkgPSBBZ2UsIGZpbGwgPSBIZWFydERpc2Vhc2UpKSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VvbV9ib3hwbG90KCkgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYnModGl0bGUgPSAiQWdlIENvbXBhcmlzb24gb2YgUGF0aWVudHMgd2l0aCBhbmQgd2l0aG91dCBIZWFydCBEaXNlYXNlIiwgeCA9ICJIZWFydCBEaXNlYXNlIiwgeSA9ICJBZ2UiKSwgImdnIikKICB9KQp9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsKICBtZXNzYWdlKCJFbnN1cmUgdGhhdCB5b3UgY29ycmVjdGx5IGNyZWF0ZSBhIGJveHBsb3QgY29tcGFyaW5nIGFnZXMgb2YgcGF0aWVudHMgd2l0aCBhbmQgd2l0aG91dCBoZWFydCBkaXNlYXNlLiIpCn0pCgpgYGAKCjQuICAqKlN1bW1hcml6ZSBhbmQgVmlzdWFsaXplIERhdGEqKjogU3VtbWFyaXplIHRoZSBhdmVyYWdlIGNob2xlc3Rlcm9sIGxldmVscyBmb3IgcGF0aWVudHMgZ3JvdXBlZCBieSBoZWFydCBkaXNlYXNlIHN0YXR1cyBhbmQgY3JlYXRlIGEgYmFyIHBsb3QgdG8gdmlzdWFsaXplIHRoZXNlIGF2ZXJhZ2VzLgoKYGBge3J9CiMjIyBQcm9ibGVtIDcuNCAtIFN0YXJ0ICMjIwphdmdfY2hvbGVzdGVyb2wgPC0gcGF0aWVudHMgJT4lCiAgZ3JvdXBfYnkoSGVhcnREaXNlYXNlKSAlPiUKICBzdW1tYXJpemUoYXZnX2Nob2wgPSBtZWFuKENob2xlc3Rlcm9sKSkKCmdncGxvdChhdmdfY2hvbGVzdGVyb2wsIGFlcyh4ID0gSGVhcnREaXNlYXNlLCB5ID0gYXZnX2Nob2wsIGZpbGwgPSBIZWFydERpc2Vhc2UpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBsYWJzKHRpdGxlID0gIkF2ZXJhZ2UgQ2hvbGVzdGVyb2wgTGV2ZWxzIGJ5IEhlYXJ0IERpc2Vhc2UgU3RhdHVzIiwgeCA9ICJIZWFydCBEaXNlYXNlIiwgeSA9ICJBdmVyYWdlIENob2xlc3Rlcm9sIikKIyMjIFByb2JsZW0gNy40IC0gRW5kICMjIwoKIyMjIEZlYWRiYWNrIC0gVGVzdCBDYXNlIC0gUHJvYmxlbSA3LjQgLSBTdGFydCAjIyMKdHJ5Q2F0Y2goewogIHRlc3R0aGF0Ojp0ZXN0X3RoYXQoIlByb2JsZW0gNy40IiwgewogICAgdGVzdHRoYXQ6OmV4cGVjdF9zM19jbGFzcyhnZ3Bsb3QoYXZnX2Nob2xlc3Rlcm9sLCBhZXMoeCA9IEhlYXJ0RGlzZWFzZSwgeSA9IGF2Z19jaG9sLCBmaWxsID0gSGVhcnREaXNlYXNlKSkgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFicyh0aXRsZSA9ICJBdmVyYWdlIENob2xlc3Rlcm9sIExldmVscyBieSBIZWFydCBEaXNlYXNlIFN0YXR1cyIsIHggPSAiSGVhcnQgRGlzZWFzZSIsIHkgPSAiQXZlcmFnZSBDaG9sZXN0ZXJvbCIpLCAiZ2ciKQogIH0pCn0sIGVycm9yID0gZnVuY3Rpb24oZSkgewogIG1lc3NhZ2UoIkVuc3VyZSB0aGF0IHlvdSBjb3JyZWN0bHkgc3VtbWFyaXplIGFuZCB2aXN1YWxpemUgdGhlIGF2ZXJhZ2UgY2hvbGVzdGVyb2wgbGV2ZWxzIGJ5IGhlYXJ0IGRpc2Vhc2Ugc3RhdHVzLiIpCn0pCiMjIyBGZWFkYmFjayAtIFRlc3QgQ2FzZSAtIFByb2JsZW0gNy40IC0gRW5kICMjIwoKYGBgCgojIyMgKipDb25jbHVzaW9uKioKCkluIHRoaXMgYXNzaWdubWVudCwgeW91IGhhdmUgbGVhcm5lZCB0aGUgYmFzaWNzIG9mIFIgcHJvZ3JhbW1pbmcsIGRhdGEgbWFuaXB1bGF0aW9uIHdpdGggKipgZHBseXJgKiosIGFuZCBkYXRhIHZpc3VhbGl6YXRpb24gd2l0aCAqKmBnZ3Bsb3QyYCoqLiBZb3UgYWxzbyBhcHBsaWVkIHRoZXNlIHNraWxscyB0byBhbmFseXplIGEgc2ltcGxlIHBhdGllbnQgZGF0YXNldC4gVGhlc2UgZm91bmRhdGlvbmFsIHNraWxscyB3aWxsIHByZXBhcmUgeW91IGZvciBtb3JlIGNvbXBsZXggZGF0YSBlbmdpbmVlcmluZyB0YXNrcyBpbiB0aGUgbmV4dCBhc3NpZ25tZW50cy4gV2VsbCBkb25lIQo=