Introduction

This notebook covers various aspects of working with data, including data visualization, facets, time series analysis, linear regression, and classification. We’ll use R and its popular libraries to demonstrate these concepts, provide detailed explanations, and offer exercises for practice.

1. Data Visualization

Explanation

Data visualization is the graphical representation of information and data. It uses statistical graphics, plots, and other visual elements to communicate complex data relationships and patterns effectively. The human brain processes visual information much faster than text, making data visualization an essential tool for understanding and presenting data.

In R, the ‘ggplot2’ package is widely used for creating data visualizations. It’s based on the Grammar of Graphics, a layered approach to describing and constructing visualizations.

Key components of a ggplot: 1. Data: The dataset you’re visualizing 2. Aesthetics (aes): Mapping of variables to visual properties (e.g., x-axis, y-axis, color, size) 3. Geometries (geom): The type of plot (e.g., points, lines, bars) 4. Scales: How the data is mapped to the visual properties 5. Facets: Division of the plot into subplots based on categorical variables 6. Theme: Overall visual style of the plot

Example

Let’s create a more complex visualization using the mtcars dataset:

ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl), size = hp)) +
  geom_point(alpha = 0.7) +
  geom_smooth(method = "lm", se = FALSE) +
  scale_color_viridis_d() +
  labs(title = "Car Weight vs. MPG",
       subtitle = "Colored by number of cylinders, size represents horsepower",
       x = "Weight (1000 lbs)",
       y = "Miles Per Gallon",
       color = "Cylinders",
       size = "Horsepower") +
  theme_minimal()
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
`geom_smooth()` using formula = 'y ~ x'
Warning: The following aesthetics were dropped during statistical transformation: size.
ℹ This can happen when ggplot fails to infer the correct grouping structure in the data.
ℹ Did you forget to specify a `group` aesthetic or to convert a numerical variable into a factor?

This plot shows: - The relationship between weight (x-axis) and miles per gallon (y-axis) - The number of cylinders (color) - The horsepower (size of points) - A linear regression line for each cylinder group

Exercises

  1. Using the built-in diamonds dataset, create a scatter plot of price vs. carat. Color the points by the cut quality and add a title and proper axis labels.

  2. With the mpg dataset, create a box plot showing the distribution of highway miles per gallon (hwy) for different car classes (class). Add color to the boxes and include a title and axis labels.

  3. Using the economics dataset, create a line plot showing both personal savings rate (psavert) and unemployment rate (uempmed) over time. Use two different y-axes (hint: look up sec_axis()). Include a legend, title, and appropriate labels.

2. Facets

Explanation

Faceting is a technique used to split a plot into multiple subplots based on one or more categorical variables. This allows for easy comparison of patterns across different subgroups in the data. In ggplot2, there are two main faceting functions:

  1. facet_wrap(): Arranges a series of plots into a 2D grid, “wrapping” around with a specified number of rows or columns.
  2. facet_grid(): Creates a grid of plots with rows and columns based on different categorical variables.

Faceting is particularly useful when you want to: - Compare trends or patterns across different categories - Visualize how relationships change across subgroups - Display multiple related plots in a compact format

Example

Let’s create a faceted plot using the mpg dataset:

ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point(aes(color = class), alpha = 0.7) +
  geom_smooth(method = "lm", se = FALSE, color = "black") +
  facet_wrap(~ manufacturer, scales = "free") +
  labs(title = "Highway MPG vs. Engine Displacement",
       subtitle = "Faceted by manufacturer, colored by vehicle class",
       x = "Engine Displacement (L)",
       y = "Highway MPG",
       color = "Vehicle Class") +
  theme_minimal() +
  theme(legend.position = "bottom")
`geom_smooth()` using formula = 'y ~ x'

This plot shows: - The relationship between engine displacement and highway MPG - Facets for each car manufacturer - Colors representing different vehicle classes - A linear regression line for each manufacturer

Exercises

  1. Using the diamonds dataset, create a faceted histogram of diamond prices. Facet by cut quality and fill the histograms by diamond color. Add appropriate labels and a title.

  2. With the mpg dataset, create a faceted scatter plot of city MPG vs. highway MPG. Facet by the number of cylinders and color the points by the type of drive (drv). Include a regression line for each facet.

  3. Using the txhousing dataset, create a faceted line plot showing the median housing price over time for different cities. Use facet_wrap() to create a 3x3 grid of plots for the top 9 cities by median housing price. Add appropriate labels and a title.

3. Time Series

Explanation

Time series analysis involves studying data points collected over time, typically at regular intervals. It’s used to identify trends, seasonal patterns, and other time-dependent structures in the data. Key components of time series analysis include:

  1. Trend: Long-term increase or decrease in the data
  2. Seasonality: Repeating patterns at fixed intervals
  3. Cyclical patterns: Fluctuations not tied to a fixed period
  4. Random variations: Unexplained variability in the data

In R, time series data is often stored in specialized objects like ts (base R) or xts (from the xts package). The lubridate package is useful for handling dates and times, while forecast provides functions for time series forecasting.

Common techniques in time series analysis include: - Moving averages - Exponential smoothing - ARIMA (Autoregressive Integrated Moving Average) models - Decomposition of time series into trend, seasonal, and residual components

Example

Let’s create a more complex time series example using the AirPassengers dataset:

data("AirPassengers")
ap_df <- data.frame(
  date = seq(as.Date("1949-01-01"), as.Date("1960-12-01"), by = "month"),
  passengers = as.numeric(AirPassengers)
)

# Decompose the time series
ap_decomp <- decompose(AirPassengers)

# Plot the original series and its components
par(mfrow = c(4, 1), mar = c(2, 2, 2, 2))
plot(ap_decomp$x, main = "Original Time Series", ylab = "Passengers")
plot(ap_decomp$trend, main = "Trend", ylab = "Trend")
plot(ap_decomp$seasonal, main = "Seasonal", ylab = "Seasonal")
plot(ap_decomp$random, main = "Random", ylab = "Random")

This example shows: - The original time series of airline passengers - The decomposition of the series into trend, seasonal, and random components

Exercises

  1. Using the economics dataset, create a time series plot of the personal savings rate (psavert) from 1967 to 2015. Add a trend line using a simple moving average with a window of 12 months.

  2. With the nhtemp dataset (average yearly temperatures in New Haven), create a time series plot and decompose it into trend, seasonal, and random components. Plot each component separately.

  3. Using the co2 dataset (atmospheric CO2 concentrations), create a time series forecast for the next 24 months using an appropriate model (e.g., ARIMA). Plot the original data, the fitted values, and the forecast with prediction intervals.

4. Linear Regression

Explanation

Linear regression is a statistical method used to model the relationship between a dependent variable and one or more independent variables. In its simplest form (simple linear regression), it assumes a linear relationship between two variables:

y = β₀ + β₁x + ε

Where: - y is the dependent variable - x is the independent variable - β₀ is the y-intercept - β₁ is the slope - ε is the error term

Multiple linear regression extends this to multiple independent variables:

y = β₀ + β₁x₁ + β₂x₂ + … + βₚxₚ + ε

Key concepts in linear regression: 1. Coefficients: Estimated using the method of least squares 2. R-squared: Measure of how well the model fits the data 3. Residuals: Differences between observed and predicted values 4. Assumptions: Linearity, independence, homoscedasticity, normality of residuals

Example

Let’s perform a multiple linear regression using the mtcars dataset:

# Fit a multiple linear regression model
model <- lm(mpg ~ wt + hp + qsec, data = mtcars)

# Print the summary
summary(model)

Call:
lm(formula = mpg ~ wt + hp + qsec, data = mtcars)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.8591 -1.6418 -0.4636  1.1940  5.6092 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 27.61053    8.41993   3.279  0.00278 ** 
wt          -4.35880    0.75270  -5.791 3.22e-06 ***
hp          -0.01782    0.01498  -1.190  0.24418    
qsec         0.51083    0.43922   1.163  0.25463    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2.578 on 28 degrees of freedom
Multiple R-squared:  0.8348,    Adjusted R-squared:  0.8171 
F-statistic: 47.15 on 3 and 28 DF,  p-value: 4.506e-11
# Plot diagnostics
par(mfrow = c(2, 2))
plot(model)

This example shows: - A multiple linear regression model predicting MPG from weight, horsepower, and quarter-mile time - The summary output with coefficients, R-squared, and p-values - Diagnostic plots for assessing model assumptions

Exercises

  1. Using the boston dataset from the MASS package, create a simple linear regression model to predict median house value (medv) based on the average number of rooms (rm). Plot the data points and the regression line, and interpret the coefficients.

  2. With the mtcars dataset, build a multiple linear regression model to predict quarter-mile time (qsec) using horsepower (hp), weight (wt), and transmission type (am). Interpret the results and check the model assumptions using diagnostic plots.

  3. Using the diamonds dataset, create a linear regression model to predict the price of a diamond based on its carat, cut, color, and clarity. Use dummy variables for categorical predictors. Interpret the results and discuss which factors have the most significant impact on diamond prices.

5. Classification

Explanation

Classification is a supervised machine learning technique used to predict categorical outcomes. It involves training a model on labeled data to learn the relationship between features and class labels, then using that model to predict the class of new, unseen instances.

Common classification algorithms include: 1. Logistic Regression 2. k-Nearest Neighbors (k-NN) 3. Decision Trees 4. Random Forests 5. Support Vector Machines (SVM) 6. Neural Networks

Key concepts in classification: - Features: Input variables used to make predictions - Target variable: The categorical outcome we’re trying to predict - Training and testing sets: Data used to build and evaluate the model - Confusion matrix: Table showing correct and incorrect predictions - Accuracy, precision, recall, and F1-score: Metrics for evaluating model performance

Example

Let’s perform a more complex classification task using the iris dataset and a Random Forest classifier:

# Load necessary library
library(randomForest)

# Split the data
set.seed(123)
train_indices <- createDataPartition(iris$Species, p = 0.7, list = FALSE)
train_data <- iris[train_indices, ]
test_data <- iris[-train_indices, ]

# Train a Random Forest model
rf_model <- randomForest(Species ~ ., data = train_data, ntree = 100)

# Make predictions
predictions <- predict(rf_model, newdata = test_data)

# Evaluate the model
confusionMatrix(predictions, test_data$Species)
Confusion Matrix and Statistics

            Reference
Prediction   setosa versicolor virginica
  setosa         15          0         0
  versicolor      0         14         2
  virginica       0          1        13

Overall Statistics
                                         
               Accuracy : 0.9333         
                 95% CI : (0.8173, 0.986)
    No Information Rate : 0.3333         
    P-Value [Acc > NIR] : < 2.2e-16      
                                         
                  Kappa : 0.9            
                                         
 Mcnemar's Test P-Value : NA             

Statistics by Class:

                     Class: setosa Class: versicolor Class: virginica
Sensitivity                 1.0000            0.9333           0.8667
Specificity                 1.0000            0.9333           0.9667
Pos Pred Value              1.0000            0.8750           0.9286
Neg Pred Value              1.0000            0.9655           0.9355
Prevalence                  0.3333            0.3333           0.3333
Detection Rate              0.3333            0.3111           0.2889
Detection Prevalence        0.3333            0.3556           0.3111
Balanced Accuracy           1.0000            0.9333           0.9167
# Plot feature importance
varImpPlot(rf_model, main = "Feature Importance in Random Forest Model")

This example demonstrates: - Training a Random Forest classifier on the iris dataset - Making predictions on a test set - Evaluating the model using a confusion matrix - Visualizing feature importance

Exercises

  1. Using the mtcars dataset, create a logistic regression model to predict whether a car has an automatic or manual transmission (am) based on other features. Split the data into training and testing sets, fit the model, make predictions, and evaluate its performance using a confusion matrix and ROC curve.

  2. With the wine dataset from the rattle package, build a k-Nearest Neighbors classifier to predict the wine type. Experiment with different values of k and use cross-validation to select the best model. Evaluate the final model’s performance on a held-out test set.

  3. Using the Default dataset from the ISLR package, create a decision tree classifier to predict whether a customer will default on their credit card payment. Visualize the tree, interpret the results, and evaluate the model’s performance. Then, compare its performance to a random forest classifier on the same data.

LS0tCnRpdGxlOiAiV29ya2luZyB3aXRoIERhdGE6IEEgQ29tcHJlaGVuc2l2ZSBHdWlkZSB3aXRoIEV4ZXJjaXNlcyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShjYXJldCkKbGlicmFyeSh0aWR5cikKbGlicmFyeShmb3JlY2FzdCkKbGlicmFyeShyYW5kb21Gb3Jlc3QpIApgYGAKCiMjIEludHJvZHVjdGlvbgoKVGhpcyBub3RlYm9vayBjb3ZlcnMgdmFyaW91cyBhc3BlY3RzIG9mIHdvcmtpbmcgd2l0aCBkYXRhLCBpbmNsdWRpbmcgZGF0YSB2aXN1YWxpemF0aW9uLCBmYWNldHMsIHRpbWUgc2VyaWVzIGFuYWx5c2lzLCBsaW5lYXIgcmVncmVzc2lvbiwgYW5kIGNsYXNzaWZpY2F0aW9uLiBXZSdsbCB1c2UgUiBhbmQgaXRzIHBvcHVsYXIgbGlicmFyaWVzIHRvIGRlbW9uc3RyYXRlIHRoZXNlIGNvbmNlcHRzLCBwcm92aWRlIGRldGFpbGVkIGV4cGxhbmF0aW9ucywgYW5kIG9mZmVyIGV4ZXJjaXNlcyBmb3IgcHJhY3RpY2UuCgojIyAxLiBEYXRhIFZpc3VhbGl6YXRpb24KCiMjIyBFeHBsYW5hdGlvbgoKRGF0YSB2aXN1YWxpemF0aW9uIGlzIHRoZSBncmFwaGljYWwgcmVwcmVzZW50YXRpb24gb2YgaW5mb3JtYXRpb24gYW5kIGRhdGEuIEl0IHVzZXMgc3RhdGlzdGljYWwgZ3JhcGhpY3MsIHBsb3RzLCBhbmQgb3RoZXIgdmlzdWFsIGVsZW1lbnRzIHRvIGNvbW11bmljYXRlIGNvbXBsZXggZGF0YSByZWxhdGlvbnNoaXBzIGFuZCBwYXR0ZXJucyBlZmZlY3RpdmVseS4gVGhlIGh1bWFuIGJyYWluIHByb2Nlc3NlcyB2aXN1YWwgaW5mb3JtYXRpb24gbXVjaCBmYXN0ZXIgdGhhbiB0ZXh0LCBtYWtpbmcgZGF0YSB2aXN1YWxpemF0aW9uIGFuIGVzc2VudGlhbCB0b29sIGZvciB1bmRlcnN0YW5kaW5nIGFuZCBwcmVzZW50aW5nIGRhdGEuCgpJbiBSLCB0aGUgJ2dncGxvdDInIHBhY2thZ2UgaXMgd2lkZWx5IHVzZWQgZm9yIGNyZWF0aW5nIGRhdGEgdmlzdWFsaXphdGlvbnMuIEl0J3MgYmFzZWQgb24gdGhlIEdyYW1tYXIgb2YgR3JhcGhpY3MsIGEgbGF5ZXJlZCBhcHByb2FjaCB0byBkZXNjcmliaW5nIGFuZCBjb25zdHJ1Y3RpbmcgdmlzdWFsaXphdGlvbnMuCgpLZXkgY29tcG9uZW50cyBvZiBhIGdncGxvdDoKMS4gRGF0YTogVGhlIGRhdGFzZXQgeW91J3JlIHZpc3VhbGl6aW5nCjIuIEFlc3RoZXRpY3MgKGFlcyk6IE1hcHBpbmcgb2YgdmFyaWFibGVzIHRvIHZpc3VhbCBwcm9wZXJ0aWVzIChlLmcuLCB4LWF4aXMsIHktYXhpcywgY29sb3IsIHNpemUpCjMuIEdlb21ldHJpZXMgKGdlb20pOiBUaGUgdHlwZSBvZiBwbG90IChlLmcuLCBwb2ludHMsIGxpbmVzLCBiYXJzKQo0LiBTY2FsZXM6IEhvdyB0aGUgZGF0YSBpcyBtYXBwZWQgdG8gdGhlIHZpc3VhbCBwcm9wZXJ0aWVzCjUuIEZhY2V0czogRGl2aXNpb24gb2YgdGhlIHBsb3QgaW50byBzdWJwbG90cyBiYXNlZCBvbiBjYXRlZ29yaWNhbCB2YXJpYWJsZXMKNi4gVGhlbWU6IE92ZXJhbGwgdmlzdWFsIHN0eWxlIG9mIHRoZSBwbG90CgojIyMgRXhhbXBsZQoKTGV0J3MgY3JlYXRlIGEgbW9yZSBjb21wbGV4IHZpc3VhbGl6YXRpb24gdXNpbmcgdGhlIG10Y2FycyBkYXRhc2V0OgoKYGBge3J9CmdncGxvdChtdGNhcnMsIGFlcyh4ID0gd3QsIHkgPSBtcGcsIGNvbG9yID0gZmFjdG9yKGN5bCksIHNpemUgPSBocCkpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC43KSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfZCgpICsKICBsYWJzKHRpdGxlID0gIkNhciBXZWlnaHQgdnMuIE1QRyIsCiAgICAgICBzdWJ0aXRsZSA9ICJDb2xvcmVkIGJ5IG51bWJlciBvZiBjeWxpbmRlcnMsIHNpemUgcmVwcmVzZW50cyBob3JzZXBvd2VyIiwKICAgICAgIHggPSAiV2VpZ2h0ICgxMDAwIGxicykiLAogICAgICAgeSA9ICJNaWxlcyBQZXIgR2FsbG9uIiwKICAgICAgIGNvbG9yID0gIkN5bGluZGVycyIsCiAgICAgICBzaXplID0gIkhvcnNlcG93ZXIiKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKVGhpcyBwbG90IHNob3dzOgotIFRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB3ZWlnaHQgKHgtYXhpcykgYW5kIG1pbGVzIHBlciBnYWxsb24gKHktYXhpcykKLSBUaGUgbnVtYmVyIG9mIGN5bGluZGVycyAoY29sb3IpCi0gVGhlIGhvcnNlcG93ZXIgKHNpemUgb2YgcG9pbnRzKQotIEEgbGluZWFyIHJlZ3Jlc3Npb24gbGluZSBmb3IgZWFjaCBjeWxpbmRlciBncm91cAoKIyMjIEV4ZXJjaXNlcwoKMS4gVXNpbmcgdGhlIGJ1aWx0LWluIGBkaWFtb25kc2AgZGF0YXNldCwgY3JlYXRlIGEgc2NhdHRlciBwbG90IG9mIHByaWNlIHZzLiBjYXJhdC4gQ29sb3IgdGhlIHBvaW50cyBieSB0aGUgY3V0IHF1YWxpdHkgYW5kIGFkZCBhIHRpdGxlIGFuZCBwcm9wZXIgYXhpcyBsYWJlbHMuCgoyLiBXaXRoIHRoZSBgbXBnYCBkYXRhc2V0LCBjcmVhdGUgYSBib3ggcGxvdCBzaG93aW5nIHRoZSBkaXN0cmlidXRpb24gb2YgaGlnaHdheSBtaWxlcyBwZXIgZ2FsbG9uIChod3kpIGZvciBkaWZmZXJlbnQgY2FyIGNsYXNzZXMgKGNsYXNzKS4gQWRkIGNvbG9yIHRvIHRoZSBib3hlcyBhbmQgaW5jbHVkZSBhIHRpdGxlIGFuZCBheGlzIGxhYmVscy4KCjMuIFVzaW5nIHRoZSBgZWNvbm9taWNzYCBkYXRhc2V0LCBjcmVhdGUgYSBsaW5lIHBsb3Qgc2hvd2luZyBib3RoIHBlcnNvbmFsIHNhdmluZ3MgcmF0ZSAocHNhdmVydCkgYW5kIHVuZW1wbG95bWVudCByYXRlICh1ZW1wbWVkKSBvdmVyIHRpbWUuIFVzZSB0d28gZGlmZmVyZW50IHktYXhlcyAoaGludDogbG9vayB1cCBgc2VjX2F4aXMoKWApLiBJbmNsdWRlIGEgbGVnZW5kLCB0aXRsZSwgYW5kIGFwcHJvcHJpYXRlIGxhYmVscy4KCiMjIDIuIEZhY2V0cwoKIyMjIEV4cGxhbmF0aW9uCgpGYWNldGluZyBpcyBhIHRlY2huaXF1ZSB1c2VkIHRvIHNwbGl0IGEgcGxvdCBpbnRvIG11bHRpcGxlIHN1YnBsb3RzIGJhc2VkIG9uIG9uZSBvciBtb3JlIGNhdGVnb3JpY2FsIHZhcmlhYmxlcy4gVGhpcyBhbGxvd3MgZm9yIGVhc3kgY29tcGFyaXNvbiBvZiBwYXR0ZXJucyBhY3Jvc3MgZGlmZmVyZW50IHN1Ymdyb3VwcyBpbiB0aGUgZGF0YS4gSW4gZ2dwbG90MiwgdGhlcmUgYXJlIHR3byBtYWluIGZhY2V0aW5nIGZ1bmN0aW9uczoKCjEuIGBmYWNldF93cmFwKClgOiBBcnJhbmdlcyBhIHNlcmllcyBvZiBwbG90cyBpbnRvIGEgMkQgZ3JpZCwgIndyYXBwaW5nIiBhcm91bmQgd2l0aCBhIHNwZWNpZmllZCBudW1iZXIgb2Ygcm93cyBvciBjb2x1bW5zLgoyLiBgZmFjZXRfZ3JpZCgpYDogQ3JlYXRlcyBhIGdyaWQgb2YgcGxvdHMgd2l0aCByb3dzIGFuZCBjb2x1bW5zIGJhc2VkIG9uIGRpZmZlcmVudCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuCgpGYWNldGluZyBpcyBwYXJ0aWN1bGFybHkgdXNlZnVsIHdoZW4geW91IHdhbnQgdG86Ci0gQ29tcGFyZSB0cmVuZHMgb3IgcGF0dGVybnMgYWNyb3NzIGRpZmZlcmVudCBjYXRlZ29yaWVzCi0gVmlzdWFsaXplIGhvdyByZWxhdGlvbnNoaXBzIGNoYW5nZSBhY3Jvc3Mgc3ViZ3JvdXBzCi0gRGlzcGxheSBtdWx0aXBsZSByZWxhdGVkIHBsb3RzIGluIGEgY29tcGFjdCBmb3JtYXQKCiMjIyBFeGFtcGxlCgpMZXQncyBjcmVhdGUgYSBmYWNldGVkIHBsb3QgdXNpbmcgdGhlIGBtcGdgIGRhdGFzZXQ6CgpgYGB7cn0KZ2dwbG90KG1wZywgYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNsYXNzKSwgYWxwaGEgPSAwLjcpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFLCBjb2xvciA9ICJibGFjayIpICsKICBmYWNldF93cmFwKH4gbWFudWZhY3R1cmVyLCBzY2FsZXMgPSAiZnJlZSIpICsKICBsYWJzKHRpdGxlID0gIkhpZ2h3YXkgTVBHIHZzLiBFbmdpbmUgRGlzcGxhY2VtZW50IiwKICAgICAgIHN1YnRpdGxlID0gIkZhY2V0ZWQgYnkgbWFudWZhY3R1cmVyLCBjb2xvcmVkIGJ5IHZlaGljbGUgY2xhc3MiLAogICAgICAgeCA9ICJFbmdpbmUgRGlzcGxhY2VtZW50IChMKSIsCiAgICAgICB5ID0gIkhpZ2h3YXkgTVBHIiwKICAgICAgIGNvbG9yID0gIlZlaGljbGUgQ2xhc3MiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKYGBgCgpUaGlzIHBsb3Qgc2hvd3M6Ci0gVGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGVuZ2luZSBkaXNwbGFjZW1lbnQgYW5kIGhpZ2h3YXkgTVBHCi0gRmFjZXRzIGZvciBlYWNoIGNhciBtYW51ZmFjdHVyZXIKLSBDb2xvcnMgcmVwcmVzZW50aW5nIGRpZmZlcmVudCB2ZWhpY2xlIGNsYXNzZXMKLSBBIGxpbmVhciByZWdyZXNzaW9uIGxpbmUgZm9yIGVhY2ggbWFudWZhY3R1cmVyCgojIyMgRXhlcmNpc2VzCgoxLiBVc2luZyB0aGUgYGRpYW1vbmRzYCBkYXRhc2V0LCBjcmVhdGUgYSBmYWNldGVkIGhpc3RvZ3JhbSBvZiBkaWFtb25kIHByaWNlcy4gRmFjZXQgYnkgY3V0IHF1YWxpdHkgYW5kIGZpbGwgdGhlIGhpc3RvZ3JhbXMgYnkgZGlhbW9uZCBjb2xvci4gQWRkIGFwcHJvcHJpYXRlIGxhYmVscyBhbmQgYSB0aXRsZS4KCjIuIFdpdGggdGhlIGBtcGdgIGRhdGFzZXQsIGNyZWF0ZSBhIGZhY2V0ZWQgc2NhdHRlciBwbG90IG9mIGNpdHkgTVBHIHZzLiBoaWdod2F5IE1QRy4gRmFjZXQgYnkgdGhlIG51bWJlciBvZiBjeWxpbmRlcnMgYW5kIGNvbG9yIHRoZSBwb2ludHMgYnkgdGhlIHR5cGUgb2YgZHJpdmUgKGRydikuIEluY2x1ZGUgYSByZWdyZXNzaW9uIGxpbmUgZm9yIGVhY2ggZmFjZXQuCgozLiBVc2luZyB0aGUgYHR4aG91c2luZ2AgZGF0YXNldCwgY3JlYXRlIGEgZmFjZXRlZCBsaW5lIHBsb3Qgc2hvd2luZyB0aGUgbWVkaWFuIGhvdXNpbmcgcHJpY2Ugb3ZlciB0aW1lIGZvciBkaWZmZXJlbnQgY2l0aWVzLiBVc2UgYGZhY2V0X3dyYXAoKWAgdG8gY3JlYXRlIGEgM3gzIGdyaWQgb2YgcGxvdHMgZm9yIHRoZSB0b3AgOSBjaXRpZXMgYnkgbWVkaWFuIGhvdXNpbmcgcHJpY2UuIEFkZCBhcHByb3ByaWF0ZSBsYWJlbHMgYW5kIGEgdGl0bGUuCgojIyAzLiBUaW1lIFNlcmllcwoKIyMjIEV4cGxhbmF0aW9uCgpUaW1lIHNlcmllcyBhbmFseXNpcyBpbnZvbHZlcyBzdHVkeWluZyBkYXRhIHBvaW50cyBjb2xsZWN0ZWQgb3ZlciB0aW1lLCB0eXBpY2FsbHkgYXQgcmVndWxhciBpbnRlcnZhbHMuIEl0J3MgdXNlZCB0byBpZGVudGlmeSB0cmVuZHMsIHNlYXNvbmFsIHBhdHRlcm5zLCBhbmQgb3RoZXIgdGltZS1kZXBlbmRlbnQgc3RydWN0dXJlcyBpbiB0aGUgZGF0YS4gS2V5IGNvbXBvbmVudHMgb2YgdGltZSBzZXJpZXMgYW5hbHlzaXMgaW5jbHVkZToKCjEuIFRyZW5kOiBMb25nLXRlcm0gaW5jcmVhc2Ugb3IgZGVjcmVhc2UgaW4gdGhlIGRhdGEKMi4gU2Vhc29uYWxpdHk6IFJlcGVhdGluZyBwYXR0ZXJucyBhdCBmaXhlZCBpbnRlcnZhbHMKMy4gQ3ljbGljYWwgcGF0dGVybnM6IEZsdWN0dWF0aW9ucyBub3QgdGllZCB0byBhIGZpeGVkIHBlcmlvZAo0LiBSYW5kb20gdmFyaWF0aW9uczogVW5leHBsYWluZWQgdmFyaWFiaWxpdHkgaW4gdGhlIGRhdGEKCkluIFIsIHRpbWUgc2VyaWVzIGRhdGEgaXMgb2Z0ZW4gc3RvcmVkIGluIHNwZWNpYWxpemVkIG9iamVjdHMgbGlrZSBgdHNgIChiYXNlIFIpIG9yIGB4dHNgIChmcm9tIHRoZSB4dHMgcGFja2FnZSkuIFRoZSBgbHVicmlkYXRlYCBwYWNrYWdlIGlzIHVzZWZ1bCBmb3IgaGFuZGxpbmcgZGF0ZXMgYW5kIHRpbWVzLCB3aGlsZSBgZm9yZWNhc3RgIHByb3ZpZGVzIGZ1bmN0aW9ucyBmb3IgdGltZSBzZXJpZXMgZm9yZWNhc3RpbmcuCgpDb21tb24gdGVjaG5pcXVlcyBpbiB0aW1lIHNlcmllcyBhbmFseXNpcyBpbmNsdWRlOgotIE1vdmluZyBhdmVyYWdlcwotIEV4cG9uZW50aWFsIHNtb290aGluZwotIEFSSU1BIChBdXRvcmVncmVzc2l2ZSBJbnRlZ3JhdGVkIE1vdmluZyBBdmVyYWdlKSBtb2RlbHMKLSBEZWNvbXBvc2l0aW9uIG9mIHRpbWUgc2VyaWVzIGludG8gdHJlbmQsIHNlYXNvbmFsLCBhbmQgcmVzaWR1YWwgY29tcG9uZW50cwoKIyMjIEV4YW1wbGUKCkxldCdzIGNyZWF0ZSBhIG1vcmUgY29tcGxleCB0aW1lIHNlcmllcyBleGFtcGxlIHVzaW5nIHRoZSBgQWlyUGFzc2VuZ2Vyc2AgZGF0YXNldDoKCmBgYHtyfQpkYXRhKCJBaXJQYXNzZW5nZXJzIikKYXBfZGYgPC0gZGF0YS5mcmFtZSgKICBkYXRlID0gc2VxKGFzLkRhdGUoIjE5NDktMDEtMDEiKSwgYXMuRGF0ZSgiMTk2MC0xMi0wMSIpLCBieSA9ICJtb250aCIpLAogIHBhc3NlbmdlcnMgPSBhcy5udW1lcmljKEFpclBhc3NlbmdlcnMpCikKCiMgRGVjb21wb3NlIHRoZSB0aW1lIHNlcmllcwphcF9kZWNvbXAgPC0gZGVjb21wb3NlKEFpclBhc3NlbmdlcnMpCgojIFBsb3QgdGhlIG9yaWdpbmFsIHNlcmllcyBhbmQgaXRzIGNvbXBvbmVudHMKcGFyKG1mcm93ID0gYyg0LCAxKSwgbWFyID0gYygyLCAyLCAyLCAyKSkKcGxvdChhcF9kZWNvbXAkeCwgbWFpbiA9ICJPcmlnaW5hbCBUaW1lIFNlcmllcyIsIHlsYWIgPSAiUGFzc2VuZ2VycyIpCnBsb3QoYXBfZGVjb21wJHRyZW5kLCBtYWluID0gIlRyZW5kIiwgeWxhYiA9ICJUcmVuZCIpCnBsb3QoYXBfZGVjb21wJHNlYXNvbmFsLCBtYWluID0gIlNlYXNvbmFsIiwgeWxhYiA9ICJTZWFzb25hbCIpCnBsb3QoYXBfZGVjb21wJHJhbmRvbSwgbWFpbiA9ICJSYW5kb20iLCB5bGFiID0gIlJhbmRvbSIpCmBgYAoKVGhpcyBleGFtcGxlIHNob3dzOgotIFRoZSBvcmlnaW5hbCB0aW1lIHNlcmllcyBvZiBhaXJsaW5lIHBhc3NlbmdlcnMKLSBUaGUgZGVjb21wb3NpdGlvbiBvZiB0aGUgc2VyaWVzIGludG8gdHJlbmQsIHNlYXNvbmFsLCBhbmQgcmFuZG9tIGNvbXBvbmVudHMKCiMjIyBFeGVyY2lzZXMKCjEuIFVzaW5nIHRoZSBgZWNvbm9taWNzYCBkYXRhc2V0LCBjcmVhdGUgYSB0aW1lIHNlcmllcyBwbG90IG9mIHRoZSBwZXJzb25hbCBzYXZpbmdzIHJhdGUgKHBzYXZlcnQpIGZyb20gMTk2NyB0byAyMDE1LiBBZGQgYSB0cmVuZCBsaW5lIHVzaW5nIGEgc2ltcGxlIG1vdmluZyBhdmVyYWdlIHdpdGggYSB3aW5kb3cgb2YgMTIgbW9udGhzLgoKMi4gV2l0aCB0aGUgYG5odGVtcGAgZGF0YXNldCAoYXZlcmFnZSB5ZWFybHkgdGVtcGVyYXR1cmVzIGluIE5ldyBIYXZlbiksIGNyZWF0ZSBhIHRpbWUgc2VyaWVzIHBsb3QgYW5kIGRlY29tcG9zZSBpdCBpbnRvIHRyZW5kLCBzZWFzb25hbCwgYW5kIHJhbmRvbSBjb21wb25lbnRzLiBQbG90IGVhY2ggY29tcG9uZW50IHNlcGFyYXRlbHkuCgozLiBVc2luZyB0aGUgYGNvMmAgZGF0YXNldCAoYXRtb3NwaGVyaWMgQ08yIGNvbmNlbnRyYXRpb25zKSwgY3JlYXRlIGEgdGltZSBzZXJpZXMgZm9yZWNhc3QgZm9yIHRoZSBuZXh0IDI0IG1vbnRocyB1c2luZyBhbiBhcHByb3ByaWF0ZSBtb2RlbCAoZS5nLiwgQVJJTUEpLiBQbG90IHRoZSBvcmlnaW5hbCBkYXRhLCB0aGUgZml0dGVkIHZhbHVlcywgYW5kIHRoZSBmb3JlY2FzdCB3aXRoIHByZWRpY3Rpb24gaW50ZXJ2YWxzLgoKIyMgNC4gTGluZWFyIFJlZ3Jlc3Npb24KCiMjIyBFeHBsYW5hdGlvbgoKTGluZWFyIHJlZ3Jlc3Npb24gaXMgYSBzdGF0aXN0aWNhbCBtZXRob2QgdXNlZCB0byBtb2RlbCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYSBkZXBlbmRlbnQgdmFyaWFibGUgYW5kIG9uZSBvciBtb3JlIGluZGVwZW5kZW50IHZhcmlhYmxlcy4gSW4gaXRzIHNpbXBsZXN0IGZvcm0gKHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiksIGl0IGFzc3VtZXMgYSBsaW5lYXIgcmVsYXRpb25zaGlwIGJldHdlZW4gdHdvIHZhcmlhYmxlczoKCnkgPSDOsuKCgCArIM6y4oKBeCArIM61CgpXaGVyZToKLSB5IGlzIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUKLSB4IGlzIHRoZSBpbmRlcGVuZGVudCB2YXJpYWJsZQotIM6y4oKAIGlzIHRoZSB5LWludGVyY2VwdAotIM6y4oKBIGlzIHRoZSBzbG9wZQotIM61IGlzIHRoZSBlcnJvciB0ZXJtCgpNdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBleHRlbmRzIHRoaXMgdG8gbXVsdGlwbGUgaW5kZXBlbmRlbnQgdmFyaWFibGVzOgoKeSA9IM6y4oKAICsgzrLigoF44oKBICsgzrLigoJ44oKCICsgLi4uICsgzrLigpp44oKaICsgzrUKCktleSBjb25jZXB0cyBpbiBsaW5lYXIgcmVncmVzc2lvbjoKMS4gQ29lZmZpY2llbnRzOiBFc3RpbWF0ZWQgdXNpbmcgdGhlIG1ldGhvZCBvZiBsZWFzdCBzcXVhcmVzCjIuIFItc3F1YXJlZDogTWVhc3VyZSBvZiBob3cgd2VsbCB0aGUgbW9kZWwgZml0cyB0aGUgZGF0YQozLiBSZXNpZHVhbHM6IERpZmZlcmVuY2VzIGJldHdlZW4gb2JzZXJ2ZWQgYW5kIHByZWRpY3RlZCB2YWx1ZXMKNC4gQXNzdW1wdGlvbnM6IExpbmVhcml0eSwgaW5kZXBlbmRlbmNlLCBob21vc2NlZGFzdGljaXR5LCBub3JtYWxpdHkgb2YgcmVzaWR1YWxzCgojIyMgRXhhbXBsZQoKTGV0J3MgcGVyZm9ybSBhIG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uIHVzaW5nIHRoZSBtdGNhcnMgZGF0YXNldDoKCmBgYHtyfQojIEZpdCBhIG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsCm1vZGVsIDwtIGxtKG1wZyB+IHd0ICsgaHAgKyBxc2VjLCBkYXRhID0gbXRjYXJzKQoKIyBQcmludCB0aGUgc3VtbWFyeQpzdW1tYXJ5KG1vZGVsKQoKIyBQbG90IGRpYWdub3N0aWNzCnBhcihtZnJvdyA9IGMoMiwgMikpCnBsb3QobW9kZWwpCmBgYAoKVGhpcyBleGFtcGxlIHNob3dzOgotIEEgbXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgcHJlZGljdGluZyBNUEcgZnJvbSB3ZWlnaHQsIGhvcnNlcG93ZXIsIGFuZCBxdWFydGVyLW1pbGUgdGltZQotIFRoZSBzdW1tYXJ5IG91dHB1dCB3aXRoIGNvZWZmaWNpZW50cywgUi1zcXVhcmVkLCBhbmQgcC12YWx1ZXMKLSBEaWFnbm9zdGljIHBsb3RzIGZvciBhc3Nlc3NpbmcgbW9kZWwgYXNzdW1wdGlvbnMKCiMjIyBFeGVyY2lzZXMKCjEuIFVzaW5nIHRoZSBgYm9zdG9uYCBkYXRhc2V0IGZyb20gdGhlIE1BU1MgcGFja2FnZSwgY3JlYXRlIGEgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHRvIHByZWRpY3QgbWVkaWFuIGhvdXNlIHZhbHVlIChtZWR2KSBiYXNlZCBvbiB0aGUgYXZlcmFnZSBudW1iZXIgb2Ygcm9vbXMgKHJtKS4gUGxvdCB0aGUgZGF0YSBwb2ludHMgYW5kIHRoZSByZWdyZXNzaW9uIGxpbmUsIGFuZCBpbnRlcnByZXQgdGhlIGNvZWZmaWNpZW50cy4KCjIuIFdpdGggdGhlIGBtdGNhcnNgIGRhdGFzZXQsIGJ1aWxkIGEgbXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgdG8gcHJlZGljdCBxdWFydGVyLW1pbGUgdGltZSAocXNlYykgdXNpbmcgaG9yc2Vwb3dlciAoaHApLCB3ZWlnaHQgKHd0KSwgYW5kIHRyYW5zbWlzc2lvbiB0eXBlIChhbSkuIEludGVycHJldCB0aGUgcmVzdWx0cyBhbmQgY2hlY2sgdGhlIG1vZGVsIGFzc3VtcHRpb25zIHVzaW5nIGRpYWdub3N0aWMgcGxvdHMuCgozLiBVc2luZyB0aGUgYGRpYW1vbmRzYCBkYXRhc2V0LCBjcmVhdGUgYSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB0byBwcmVkaWN0IHRoZSBwcmljZSBvZiBhIGRpYW1vbmQgYmFzZWQgb24gaXRzIGNhcmF0LCBjdXQsIGNvbG9yLCBhbmQgY2xhcml0eS4gVXNlIGR1bW15IHZhcmlhYmxlcyBmb3IgY2F0ZWdvcmljYWwgcHJlZGljdG9ycy4gSW50ZXJwcmV0IHRoZSByZXN1bHRzIGFuZCBkaXNjdXNzIHdoaWNoIGZhY3RvcnMgaGF2ZSB0aGUgbW9zdCBzaWduaWZpY2FudCBpbXBhY3Qgb24gZGlhbW9uZCBwcmljZXMuCgojIyA1LiBDbGFzc2lmaWNhdGlvbgoKIyMjIEV4cGxhbmF0aW9uCgpDbGFzc2lmaWNhdGlvbiBpcyBhIHN1cGVydmlzZWQgbWFjaGluZSBsZWFybmluZyB0ZWNobmlxdWUgdXNlZCB0byBwcmVkaWN0IGNhdGVnb3JpY2FsIG91dGNvbWVzLiBJdCBpbnZvbHZlcyB0cmFpbmluZyBhIG1vZGVsIG9uIGxhYmVsZWQgZGF0YSB0byBsZWFybiB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gZmVhdHVyZXMgYW5kIGNsYXNzIGxhYmVscywgdGhlbiB1c2luZyB0aGF0IG1vZGVsIHRvIHByZWRpY3QgdGhlIGNsYXNzIG9mIG5ldywgdW5zZWVuIGluc3RhbmNlcy4KCkNvbW1vbiBjbGFzc2lmaWNhdGlvbiBhbGdvcml0aG1zIGluY2x1ZGU6CjEuIExvZ2lzdGljIFJlZ3Jlc3Npb24KMi4gay1OZWFyZXN0IE5laWdoYm9ycyAoay1OTikKMy4gRGVjaXNpb24gVHJlZXMKNC4gUmFuZG9tIEZvcmVzdHMKNS4gU3VwcG9ydCBWZWN0b3IgTWFjaGluZXMgKFNWTSkKNi4gTmV1cmFsIE5ldHdvcmtzCgpLZXkgY29uY2VwdHMgaW4gY2xhc3NpZmljYXRpb246Ci0gRmVhdHVyZXM6IElucHV0IHZhcmlhYmxlcyB1c2VkIHRvIG1ha2UgcHJlZGljdGlvbnMKLSBUYXJnZXQgdmFyaWFibGU6IFRoZSBjYXRlZ29yaWNhbCBvdXRjb21lIHdlJ3JlIHRyeWluZyB0byBwcmVkaWN0Ci0gVHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0czogRGF0YSB1c2VkIHRvIGJ1aWxkIGFuZCBldmFsdWF0ZSB0aGUgbW9kZWwKLSBDb25mdXNpb24gbWF0cml4OiBUYWJsZSBzaG93aW5nIGNvcnJlY3QgYW5kIGluY29ycmVjdCBwcmVkaWN0aW9ucwotIEFjY3VyYWN5LCBwcmVjaXNpb24sIHJlY2FsbCwgYW5kIEYxLXNjb3JlOiBNZXRyaWNzIGZvciBldmFsdWF0aW5nIG1vZGVsIHBlcmZvcm1hbmNlCgojIyMgRXhhbXBsZQoKTGV0J3MgcGVyZm9ybSBhIG1vcmUgY29tcGxleCBjbGFzc2lmaWNhdGlvbiB0YXNrIHVzaW5nIHRoZSBpcmlzIGRhdGFzZXQgYW5kIGEgUmFuZG9tIEZvcmVzdCBjbGFzc2lmaWVyOgoKYGBge3J9CiMgTG9hZCBuZWNlc3NhcnkgbGlicmFyeQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkKCiMgU3BsaXQgdGhlIGRhdGEKc2V0LnNlZWQoMTIzKQp0cmFpbl9pbmRpY2VzIDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oaXJpcyRTcGVjaWVzLCBwID0gMC43LCBsaXN0ID0gRkFMU0UpCnRyYWluX2RhdGEgPC0gaXJpc1t0cmFpbl9pbmRpY2VzLCBdCnRlc3RfZGF0YSA8LSBpcmlzWy10cmFpbl9pbmRpY2VzLCBdCgojIFRyYWluIGEgUmFuZG9tIEZvcmVzdCBtb2RlbApyZl9tb2RlbCA8LSByYW5kb21Gb3Jlc3QoU3BlY2llcyB+IC4sIGRhdGEgPSB0cmFpbl9kYXRhLCBudHJlZSA9IDEwMCkKCiMgTWFrZSBwcmVkaWN0aW9ucwpwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KHJmX21vZGVsLCBuZXdkYXRhID0gdGVzdF9kYXRhKQoKIyBFdmFsdWF0ZSB0aGUgbW9kZWwKY29uZnVzaW9uTWF0cml4KHByZWRpY3Rpb25zLCB0ZXN0X2RhdGEkU3BlY2llcykKCiMgUGxvdCBmZWF0dXJlIGltcG9ydGFuY2UKdmFySW1wUGxvdChyZl9tb2RlbCwgbWFpbiA9ICJGZWF0dXJlIEltcG9ydGFuY2UgaW4gUmFuZG9tIEZvcmVzdCBNb2RlbCIpCmBgYAoKVGhpcyBleGFtcGxlIGRlbW9uc3RyYXRlczoKLSBUcmFpbmluZyBhIFJhbmRvbSBGb3Jlc3QgY2xhc3NpZmllciBvbiB0aGUgaXJpcyBkYXRhc2V0Ci0gTWFraW5nIHByZWRpY3Rpb25zIG9uIGEgdGVzdCBzZXQKLSBFdmFsdWF0aW5nIHRoZSBtb2RlbCB1c2luZyBhIGNvbmZ1c2lvbiBtYXRyaXgKLSBWaXN1YWxpemluZyBmZWF0dXJlIGltcG9ydGFuY2UKCiMjIyBFeGVyY2lzZXMKCjEuIFVzaW5nIHRoZSBgbXRjYXJzYCBkYXRhc2V0LCBjcmVhdGUgYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHRvIHByZWRpY3Qgd2hldGhlciBhIGNhciBoYXMgYW4gYXV0b21hdGljIG9yIG1hbnVhbCB0cmFuc21pc3Npb24gKGFtKSBiYXNlZCBvbiBvdGhlciBmZWF0dXJlcy4gU3BsaXQgdGhlIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzLCBmaXQgdGhlIG1vZGVsLCBtYWtlIHByZWRpY3Rpb25zLCBhbmQgZXZhbHVhdGUgaXRzIHBlcmZvcm1hbmNlIHVzaW5nIGEgY29uZnVzaW9uIG1hdHJpeCBhbmQgUk9DIGN1cnZlLgoKMi4gV2l0aCB0aGUgYHdpbmVgIGRhdGFzZXQgZnJvbSB0aGUgcmF0dGxlIHBhY2thZ2UsIGJ1aWxkIGEgay1OZWFyZXN0IE5laWdoYm9ycyBjbGFzc2lmaWVyIHRvIHByZWRpY3QgdGhlIHdpbmUgdHlwZS4gRXhwZXJpbWVudCB3aXRoIGRpZmZlcmVudCB2YWx1ZXMgb2YgayBhbmQgdXNlIGNyb3NzLXZhbGlkYXRpb24gdG8gc2VsZWN0IHRoZSBiZXN0IG1vZGVsLiBFdmFsdWF0ZSB0aGUgZmluYWwgbW9kZWwncyBwZXJmb3JtYW5jZSBvbiBhIGhlbGQtb3V0IHRlc3Qgc2V0LgoKMy4gVXNpbmcgdGhlIGBEZWZhdWx0YCBkYXRhc2V0IGZyb20gdGhlIElTTFIgcGFja2FnZSwgY3JlYXRlIGEgZGVjaXNpb24gdHJlZSBjbGFzc2lmaWVyIHRvIHByZWRpY3Qgd2hldGhlciBhIGN1c3RvbWVyIHdpbGwgZGVmYXVsdCBvbiB0aGVpciBjcmVkaXQgY2FyZCBwYXltZW50LiBWaXN1YWxpemUgdGhlIHRyZWUsIGludGVycHJldCB0aGUgcmVzdWx0cywgYW5kIGV2YWx1YXRlIHRoZSBtb2RlbCdzIHBlcmZvcm1hbmNlLiBUaGVuLCBjb21wYXJlIGl0cyBwZXJmb3JtYW5jZSB0byBhIHJhbmRvbSBmb3Jlc3QgY2xhc3NpZmllciBvbiB0aGUgc2FtZSBkYXRhLgoK