This notebook contains solutions to the exercises from the “Working with Data” lecture.

1. Data Visualization

Exercise 1

ggplot(diamonds, aes(x = carat, y = price, color = cut)) +
  geom_point(alpha = 0.5) +
  labs(title = "Diamond Price vs. Carat",
       subtitle = "Colored by cut quality",
       x = "Carat",
       y = "Price (USD)",
       color = "Cut Quality") +
  theme_minimal()

Exercise 2

ggplot(mpg, aes(x = class, y = hwy, fill = class)) +
  geom_boxplot() +
  labs(title = "Highway MPG Distribution by Vehicle Class",
       x = "Vehicle Class",
       y = "Highway MPG",
       fill = "Vehicle Class") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Exercise 3

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

economics_long <- economics %>%
  dplyr::select(date, psavert, uempmed) %>%
  pivot_longer(cols = c(psavert, uempmed), names_to = "variable", values_to = "value")

ggplot(economics_long, aes(x = date, y = value, color = variable)) +
  geom_line() +
  scale_y_continuous(
    name = "Personal Savings Rate (%)",
    sec.axis = sec_axis(~./2, name = "Unemployment Rate (Weeks)")
  ) +
  scale_color_manual(values = c("psavert" = "blue", "uempmed" = "red"),
                     labels = c("psavert" = "Personal Savings Rate", "uempmed" = "Unemployment Rate")) +
  labs(title = "Personal Savings Rate and Unemployment Rate Over Time",
       x = "Date",
       color = "Measure") +
  theme_minimal()

2. Facets

Exercise 1

ggplot(diamonds, aes(x = price, fill = color)) +
  geom_histogram(binwidth = 1000, position = "dodge") +
  # geom_histogram(binwidth=1000) +
  facet_wrap(~ cut, scales = "free_y") +
  labs(title = "Distribution of Diamond Prices by Cut and Color",
       x = "Price (USD)",
       y = "Count",
       fill = "Diamond Color") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Exercise 2

ggplot(mpg, aes(x = cty, y = hwy, color = drv)) +
  geom_point() +
  geom_smooth(method = "lm", se = FALSE) +
  facet_wrap(~ cyl) +
  labs(title = "City MPG vs. Highway MPG by Number of Cylinders",
       subtitle = "Colored by Drive Type",
       x = "City MPG",
       y = "Highway MPG",
       color = "Drive Type") +
  theme_minimal()
`geom_smooth()` using formula = 'y ~ x'

Exercise 3

top_9_cities <- txhousing %>%
  group_by(city) %>%
  summarize(median_price = median(median, na.rm = TRUE)) %>%
  top_n(9, median_price) %>%
  pull(city)

txhousing_top9 <- txhousing %>%
  filter(city %in% top_9_cities)

ggplot(txhousing_top9, aes(x = date, y = median)) +
  geom_line() +
  facet_wrap(~ city, nrow = 3, scales = "free_y") +
  labs(title = "Median Housing Price Over Time for Top 9 Texas Cities",
       x = "Date",
       y = "Median Price (USD)") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Time Series

Exercise 1

library(ggplot2)
library(zoo)

# Create a data frame with date and psavert
economics_ts <- data.frame(
  date = economics$date,
  psavert = economics$psavert
)

# Calculate the 12-month moving average
economics_ts$ma_12 <- rollmean(economics_ts$psavert, k = 12, fill = NA, align = "right")

# Create the plot
ggplot(economics_ts, aes(x = date)) +
  geom_line(aes(y = psavert), color = "blue") +
  geom_line(aes(y = ma_12), color = "red", size = 1) +
  labs(title = "Personal Savings Rate (1967-2015)",
       subtitle = "With 12-month Moving Average",
       x = "Date",
       y = "Personal Savings Rate (%)") +
  theme_minimal()
Warning: Removed 11 rows containing missing values or values outside the scale range (`geom_line()`).

Exercise 2

# Load required libraries
library(ggplot2)
library(zoo)

# Create a time series object
nhtemp_ts <- ts(nhtemp, start = c(1912), end = c(1971), frequency = 1)

# Calculate a 5-year moving average for the trend
nhtemp_ma <- rollmean(nhtemp_ts, k = 5, fill = NA)

# Calculate the difference from the moving average
nhtemp_diff <- nhtemp_ts - nhtemp_ma

# Create a data frame for plotting
nhtemp_df <- data.frame(
  year = time(nhtemp_ts),
  temperature = as.vector(nhtemp_ts),
  trend = as.vector(nhtemp_ma),
  fluctuation = as.vector(nhtemp_diff)
)

# Plot the original time series and its components
ggplot(nhtemp_df, aes(x = year)) +
  geom_line(aes(y = temperature), color = "blue") +
  geom_line(aes(y = trend), color = "red", size = 1) +
  labs(title = "New Haven Temperatures (1912-1971)",
       x = "Year",
       y = "Temperature (°F)") +
  theme_minimal()

# Plot the fluctuations
ggplot(nhtemp_df, aes(x = year, y = fluctuation)) +
  geom_line(color = "green") +
  geom_hline(yintercept = 0, linetype = "dashed", color = "black") +
  labs(title = "Temperature Fluctuations from 5-Year Moving Average",
       x = "Year",
       y = "Temperature Difference (°F)") +
  theme_minimal()

Exercise 3

library(forecast)

co2_ts <- ts(co2, start = c(1959, 1), frequency = 12)
co2_model <- auto.arima(co2_ts)
co2_forecast <- forecast(co2_model, h = 24)

autoplot(co2_forecast) +
  labs(title = "CO2 Concentration Forecast",
       x = "Year",
       y = "CO2 Concentration (ppm)") +
  theme_minimal()

Linear regression

Exercise 1

library(MASS)

model <- lm(medv ~ rm, data = Boston)
summary(model)

ggplot(Boston, aes(x = rm, y = medv)) +
  geom_point() +
  geom_smooth(method = "lm", se = FALSE, color = "red") +
  labs(title = "Median House Value vs. Average Number of Rooms",
       x = "Average Number of Rooms",
       y = "Median House Value ($1000s)") +
  theme_minimal()

Exercise 2

model <- lm(qsec ~ hp + wt + factor(am), data = mtcars)
summary(model)

par(mfrow = c(2, 2))
plot(model)

Exercise 3

model <- lm(price ~ carat + cut + color + clarity, data = diamonds)
summary(model)

Classification

Exercise 1

library(pROC)

# Prepare the data
mtcars$am <- as.factor(mtcars$am)
set.seed(123)
train_indices <- createDataPartition(mtcars$am, p = 0.7, list = FALSE)
train_data <- mtcars[train_indices, ]
test_data <- mtcars[-train_indices, ]

# Fit the logistic regression model
model <- glm(am ~ mpg + hp + wt, data = train_data, family = "binomial")

# Make predictions
predictions <- predict(model, newdata = test_data, type = "response")
predicted_classes <- ifelse(predictions > 0.5, 1, 0)

# Confusion matrix
conf_matrix <- table(Predicted = predicted_classes, Actual = test_data$am)
print(conf_matrix)
         Actual
Predicted 0 1
        0 5 0
        1 0 3
# ROC curve
roc_curve <- roc(test_data$am, predictions)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
plot(roc_curve, main = "ROC Curve for Transmission Type Prediction")

Exercise 2

library(rattle)
data(wine)

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

# Find the best k using cross-validation
k_values <- 1:20
accuracy <- sapply(k_values, function(k) {
  model <- train(Type ~ ., data = train_data, method = "knn", 
                 trControl = trainControl(method = "cv", number = 5),
                 tuneGrid = data.frame(k = k))
  return(model$results$Accuracy)
})

best_k <- k_values[which.max(accuracy)]

# Train the final model with the best k
final_model <- train(Type ~ ., data = train_data, method = "knn", 
                     trControl = trainControl(method = "cv", number = 5),
                     tuneGrid = data.frame(k = best_k))

# Evaluate on the test set
predictions <- predict(final_model, newdata = test_data)
confusionMatrix(predictions, test_data$Type)
Confusion Matrix and Statistics

          Reference
Prediction  1  2  3
         1 14  0  0
         2  0 12  5
         3  3  9  9

Overall Statistics
                                          
               Accuracy : 0.6731          
                 95% CI : (0.5289, 0.7967)
    No Information Rate : 0.4038          
    P-Value [Acc > NIR] : 7.949e-05       
                                          
                  Kappa : 0.5129          
                                          
 Mcnemar's Test P-Value : NA              

Statistics by Class:

                     Class: 1 Class: 2 Class: 3
Sensitivity            0.8235   0.5714   0.6429
Specificity            1.0000   0.8387   0.6842
Pos Pred Value         1.0000   0.7059   0.4286
Neg Pred Value         0.9211   0.7429   0.8387
Prevalence             0.3269   0.4038   0.2692
Detection Rate         0.2692   0.2308   0.1731
Detection Prevalence   0.2692   0.3269   0.4038
Balanced Accuracy      0.9118   0.7051   0.6635

Exercise 3

library(ISLR)
library(rpart)
library(rpart.plot)
library(randomForest)

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

# Decision Tree
tree_model <- rpart(default ~ ., data = train_data, method = "class")
rpart.plot(tree_model, extra = 101)


tree_predictions <- predict(tree_model, newdata = test_data, type = "class")
tree_cm <- confusionMatrix(tree_predictions, test_data$default)
print(tree_cm)
Confusion Matrix and Statistics

          Reference
Prediction   No  Yes
       No  2885   64
       Yes   15   35
                                          
               Accuracy : 0.9737          
                 95% CI : (0.9673, 0.9791)
    No Information Rate : 0.967           
    P-Value [Acc > NIR] : 0.0204          
                                          
                  Kappa : 0.4578          
                                          
 Mcnemar's Test P-Value : 6.648e-08       
                                          
            Sensitivity : 0.9948          
            Specificity : 0.3535          
         Pos Pred Value : 0.9783          
         Neg Pred Value : 0.7000          
             Prevalence : 0.9670          
         Detection Rate : 0.9620          
   Detection Prevalence : 0.9833          
      Balanced Accuracy : 0.6742          
                                          
       'Positive' Class : No              
                                          
# Random Forest
rf_model <- randomForest(default ~ ., data = train_data)
rf_predictions <- predict(rf_model, newdata = test_data)
rf_cm <- confusionMatrix(rf_predictions, test_data$default)
print(rf_cm)
Confusion Matrix and Statistics

          Reference
Prediction   No  Yes
       No  2884   70
       Yes   16   29
                                         
               Accuracy : 0.9713         
                 95% CI : (0.9647, 0.977)
    No Information Rate : 0.967          
    P-Value [Acc > NIR] : 0.09875        
                                         
                  Kappa : 0.3902         
                                         
 Mcnemar's Test P-Value : 1.096e-08      
                                         
            Sensitivity : 0.9945         
            Specificity : 0.2929         
         Pos Pred Value : 0.9763         
         Neg Pred Value : 0.6444         
             Prevalence : 0.9670         
         Detection Rate : 0.9617         
   Detection Prevalence : 0.9850         
      Balanced Accuracy : 0.6437         
                                         
       'Positive' Class : No             
                                         
# Compare performance
cat("Decision Tree Accuracy:", tree_cm$overall["Accuracy"], "\n")
Decision Tree Accuracy: 0.9736579 
cat("Random Forest Accuracy:", rf_cm$overall["Accuracy"], "\n")
Random Forest Accuracy: 0.9713238 
LS0tCnRpdGxlOiAiV29ya2luZyB3aXRoIERhdGE6IEV4ZXJjaXNlIFNvbHV0aW9ucyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShjYXJldCkKbGlicmFyeSh0aWR5cikKbGlicmFyeShmb3JlY2FzdCkKbGlicmFyeShNQVNTKQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkKbGlicmFyeShyYXR0bGUpCmxpYnJhcnkoSVNMUikKbGlicmFyeShwUk9DKQpsaWJyYXJ5KHJwYXJ0KQpsaWJyYXJ5KHJwYXJ0LnBsb3QpCmBgYAoKVGhpcyBub3RlYm9vayBjb250YWlucyBzb2x1dGlvbnMgdG8gdGhlIGV4ZXJjaXNlcyBmcm9tIHRoZSAiV29ya2luZyB3aXRoIERhdGEiIGxlY3R1cmUuCgojIyAxLiBEYXRhIFZpc3VhbGl6YXRpb24KCiMjIyBFeGVyY2lzZSAxCgpgYGB7cn0KZ2dwbG90KGRpYW1vbmRzLCBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UsIGNvbG9yID0gY3V0KSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpICsKICBsYWJzKHRpdGxlID0gIkRpYW1vbmQgUHJpY2UgdnMuIENhcmF0IiwKICAgICAgIHN1YnRpdGxlID0gIkNvbG9yZWQgYnkgY3V0IHF1YWxpdHkiLAogICAgICAgeCA9ICJDYXJhdCIsCiAgICAgICB5ID0gIlByaWNlIChVU0QpIiwKICAgICAgIGNvbG9yID0gIkN1dCBRdWFsaXR5IikgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCiMjIyBFeGVyY2lzZSAyCgpgYGB7cn0KZ2dwbG90KG1wZywgYWVzKHggPSBjbGFzcywgeSA9IGh3eSwgZmlsbCA9IGNsYXNzKSkgKwogIGdlb21fYm94cGxvdCgpICsKICBsYWJzKHRpdGxlID0gIkhpZ2h3YXkgTVBHIERpc3RyaWJ1dGlvbiBieSBWZWhpY2xlIENsYXNzIiwKICAgICAgIHggPSAiVmVoaWNsZSBDbGFzcyIsCiAgICAgICB5ID0gIkhpZ2h3YXkgTVBHIiwKICAgICAgIGZpbGwgPSAiVmVoaWNsZSBDbGFzcyIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpCmBgYAoKIyMjIEV4ZXJjaXNlIDMKCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCgplY29ub21pY3NfbG9uZyA8LSBlY29ub21pY3MgJT4lCiAgZHBseXI6OnNlbGVjdChkYXRlLCBwc2F2ZXJ0LCB1ZW1wbWVkKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IGMocHNhdmVydCwgdWVtcG1lZCksIG5hbWVzX3RvID0gInZhcmlhYmxlIiwgdmFsdWVzX3RvID0gInZhbHVlIikKCmdncGxvdChlY29ub21pY3NfbG9uZywgYWVzKHggPSBkYXRlLCB5ID0gdmFsdWUsIGNvbG9yID0gdmFyaWFibGUpKSArCiAgZ2VvbV9saW5lKCkgKwogIHNjYWxlX3lfY29udGludW91cygKICAgIG5hbWUgPSAiUGVyc29uYWwgU2F2aW5ncyBSYXRlICglKSIsCiAgICBzZWMuYXhpcyA9IHNlY19heGlzKH4uLzIsIG5hbWUgPSAiVW5lbXBsb3ltZW50IFJhdGUgKFdlZWtzKSIpCiAgKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoInBzYXZlcnQiID0gImJsdWUiLCAidWVtcG1lZCIgPSAicmVkIiksCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoInBzYXZlcnQiID0gIlBlcnNvbmFsIFNhdmluZ3MgUmF0ZSIsICJ1ZW1wbWVkIiA9ICJVbmVtcGxveW1lbnQgUmF0ZSIpKSArCiAgbGFicyh0aXRsZSA9ICJQZXJzb25hbCBTYXZpbmdzIFJhdGUgYW5kIFVuZW1wbG95bWVudCBSYXRlIE92ZXIgVGltZSIsCiAgICAgICB4ID0gIkRhdGUiLAogICAgICAgY29sb3IgPSAiTWVhc3VyZSIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgojIyAyLiBGYWNldHMKCiMjIyBFeGVyY2lzZSAxCgpgYGB7cn0KZ2dwbG90KGRpYW1vbmRzLCBhZXMoeCA9IHByaWNlLCBmaWxsID0gY29sb3IpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxMDAwLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICAjIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTEwMDApICsKICBmYWNldF93cmFwKH4gY3V0LCBzY2FsZXMgPSAiZnJlZV95IikgKwogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIERpYW1vbmQgUHJpY2VzIGJ5IEN1dCBhbmQgQ29sb3IiLAogICAgICAgeCA9ICJQcmljZSAoVVNEKSIsCiAgICAgICB5ID0gIkNvdW50IiwKICAgICAgIGZpbGwgPSAiRGlhbW9uZCBDb2xvciIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpCmBgYAoKIyMjIEV4ZXJjaXNlIDIKCmBgYHtyfQpnZ3Bsb3QobXBnLCBhZXMoeCA9IGN0eSwgeSA9IGh3eSwgY29sb3IgPSBkcnYpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFKSArCiAgZmFjZXRfd3JhcCh+IGN5bCkgKwogIGxhYnModGl0bGUgPSAiQ2l0eSBNUEcgdnMuIEhpZ2h3YXkgTVBHIGJ5IE51bWJlciBvZiBDeWxpbmRlcnMiLAogICAgICAgc3VidGl0bGUgPSAiQ29sb3JlZCBieSBEcml2ZSBUeXBlIiwKICAgICAgIHggPSAiQ2l0eSBNUEciLAogICAgICAgeSA9ICJIaWdod2F5IE1QRyIsCiAgICAgICBjb2xvciA9ICJEcml2ZSBUeXBlIikgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCiMjIyBFeGVyY2lzZSAzCgpgYGB7cn0KdG9wXzlfY2l0aWVzIDwtIHR4aG91c2luZyAlPiUKICBncm91cF9ieShjaXR5KSAlPiUKICBzdW1tYXJpemUobWVkaWFuX3ByaWNlID0gbWVkaWFuKG1lZGlhbiwgbmEucm0gPSBUUlVFKSkgJT4lCiAgdG9wX24oOSwgbWVkaWFuX3ByaWNlKSAlPiUKICBwdWxsKGNpdHkpCgp0eGhvdXNpbmdfdG9wOSA8LSB0eGhvdXNpbmcgJT4lCiAgZmlsdGVyKGNpdHkgJWluJSB0b3BfOV9jaXRpZXMpCgpnZ3Bsb3QodHhob3VzaW5nX3RvcDksIGFlcyh4ID0gZGF0ZSwgeSA9IG1lZGlhbikpICsKICBnZW9tX2xpbmUoKSArCiAgZmFjZXRfd3JhcCh+IGNpdHksIG5yb3cgPSAzLCBzY2FsZXMgPSAiZnJlZV95IikgKwogIGxhYnModGl0bGUgPSAiTWVkaWFuIEhvdXNpbmcgUHJpY2UgT3ZlciBUaW1lIGZvciBUb3AgOSBUZXhhcyBDaXRpZXMiLAogICAgICAgeCA9ICJEYXRlIiwKICAgICAgIHkgPSAiTWVkaWFuIFByaWNlIChVU0QpIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkKYGBgCgojIyBUaW1lIFNlcmllcwoKIyMjIEV4ZXJjaXNlIDEKCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoem9vKQoKIyBDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggZGF0ZSBhbmQgcHNhdmVydAplY29ub21pY3NfdHMgPC0gZGF0YS5mcmFtZSgKICBkYXRlID0gZWNvbm9taWNzJGRhdGUsCiAgcHNhdmVydCA9IGVjb25vbWljcyRwc2F2ZXJ0CikKCiMgQ2FsY3VsYXRlIHRoZSAxMi1tb250aCBtb3ZpbmcgYXZlcmFnZQplY29ub21pY3NfdHMkbWFfMTIgPC0gcm9sbG1lYW4oZWNvbm9taWNzX3RzJHBzYXZlcnQsIGsgPSAxMiwgZmlsbCA9IE5BLCBhbGlnbiA9ICJyaWdodCIpCgojIENyZWF0ZSB0aGUgcGxvdApnZ3Bsb3QoZWNvbm9taWNzX3RzLCBhZXMoeCA9IGRhdGUpKSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gcHNhdmVydCksIGNvbG9yID0gImJsdWUiKSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gbWFfMTIpLCBjb2xvciA9ICJyZWQiLCBzaXplID0gMSkgKwogIGxhYnModGl0bGUgPSAiUGVyc29uYWwgU2F2aW5ncyBSYXRlICgxOTY3LTIwMTUpIiwKICAgICAgIHN1YnRpdGxlID0gIldpdGggMTItbW9udGggTW92aW5nIEF2ZXJhZ2UiLAogICAgICAgeCA9ICJEYXRlIiwKICAgICAgIHkgPSAiUGVyc29uYWwgU2F2aW5ncyBSYXRlICglKSIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgojIyMgRXhlcmNpc2UgMgoKYGBge3J9CiMgTG9hZCByZXF1aXJlZCBsaWJyYXJpZXMKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHpvbykKCiMgQ3JlYXRlIGEgdGltZSBzZXJpZXMgb2JqZWN0Cm5odGVtcF90cyA8LSB0cyhuaHRlbXAsIHN0YXJ0ID0gYygxOTEyKSwgZW5kID0gYygxOTcxKSwgZnJlcXVlbmN5ID0gMSkKCiMgQ2FsY3VsYXRlIGEgNS15ZWFyIG1vdmluZyBhdmVyYWdlIGZvciB0aGUgdHJlbmQKbmh0ZW1wX21hIDwtIHJvbGxtZWFuKG5odGVtcF90cywgayA9IDUsIGZpbGwgPSBOQSkKCiMgQ2FsY3VsYXRlIHRoZSBkaWZmZXJlbmNlIGZyb20gdGhlIG1vdmluZyBhdmVyYWdlCm5odGVtcF9kaWZmIDwtIG5odGVtcF90cyAtIG5odGVtcF9tYQoKIyBDcmVhdGUgYSBkYXRhIGZyYW1lIGZvciBwbG90dGluZwpuaHRlbXBfZGYgPC0gZGF0YS5mcmFtZSgKICB5ZWFyID0gdGltZShuaHRlbXBfdHMpLAogIHRlbXBlcmF0dXJlID0gYXMudmVjdG9yKG5odGVtcF90cyksCiAgdHJlbmQgPSBhcy52ZWN0b3Iobmh0ZW1wX21hKSwKICBmbHVjdHVhdGlvbiA9IGFzLnZlY3RvcihuaHRlbXBfZGlmZikKKQoKIyBQbG90IHRoZSBvcmlnaW5hbCB0aW1lIHNlcmllcyBhbmQgaXRzIGNvbXBvbmVudHMKZ2dwbG90KG5odGVtcF9kZiwgYWVzKHggPSB5ZWFyKSkgKwogIGdlb21fbGluZShhZXMoeSA9IHRlbXBlcmF0dXJlKSwgY29sb3IgPSAiYmx1ZSIpICsKICBnZW9tX2xpbmUoYWVzKHkgPSB0cmVuZCksIGNvbG9yID0gInJlZCIsIHNpemUgPSAxKSArCiAgbGFicyh0aXRsZSA9ICJOZXcgSGF2ZW4gVGVtcGVyYXR1cmVzICgxOTEyLTE5NzEpIiwKICAgICAgIHggPSAiWWVhciIsCiAgICAgICB5ID0gIlRlbXBlcmF0dXJlICjCsEYpIikgKwogIHRoZW1lX21pbmltYWwoKQoKIyBQbG90IHRoZSBmbHVjdHVhdGlvbnMKZ2dwbG90KG5odGVtcF9kZiwgYWVzKHggPSB5ZWFyLCB5ID0gZmx1Y3R1YXRpb24pKSArCiAgZ2VvbV9saW5lKGNvbG9yID0gImdyZWVuIikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImJsYWNrIikgKwogIGxhYnModGl0bGUgPSAiVGVtcGVyYXR1cmUgRmx1Y3R1YXRpb25zIGZyb20gNS1ZZWFyIE1vdmluZyBBdmVyYWdlIiwKICAgICAgIHggPSAiWWVhciIsCiAgICAgICB5ID0gIlRlbXBlcmF0dXJlIERpZmZlcmVuY2UgKMKwRikiKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKIyMjIEV4ZXJjaXNlIDMKCmBgYHtyfQpsaWJyYXJ5KGZvcmVjYXN0KQoKY28yX3RzIDwtIHRzKGNvMiwgc3RhcnQgPSBjKDE5NTksIDEpLCBmcmVxdWVuY3kgPSAxMikKY28yX21vZGVsIDwtIGF1dG8uYXJpbWEoY28yX3RzKQpjbzJfZm9yZWNhc3QgPC0gZm9yZWNhc3QoY28yX21vZGVsLCBoID0gMjQpCgphdXRvcGxvdChjbzJfZm9yZWNhc3QpICsKICBsYWJzKHRpdGxlID0gIkNPMiBDb25jZW50cmF0aW9uIEZvcmVjYXN0IiwKICAgICAgIHggPSAiWWVhciIsCiAgICAgICB5ID0gIkNPMiBDb25jZW50cmF0aW9uIChwcG0pIikgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCiMjIExpbmVhciByZWdyZXNzaW9uCgojIyMgRXhlcmNpc2UgMQoKYGBge3J9CmxpYnJhcnkoTUFTUykKCm1vZGVsIDwtIGxtKG1lZHYgfiBybSwgZGF0YSA9IEJvc3RvbikKc3VtbWFyeShtb2RlbCkKCmdncGxvdChCb3N0b24sIGFlcyh4ID0gcm0sIHkgPSBtZWR2KSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgY29sb3IgPSAicmVkIikgKwogIGxhYnModGl0bGUgPSAiTWVkaWFuIEhvdXNlIFZhbHVlIHZzLiBBdmVyYWdlIE51bWJlciBvZiBSb29tcyIsCiAgICAgICB4ID0gIkF2ZXJhZ2UgTnVtYmVyIG9mIFJvb21zIiwKICAgICAgIHkgPSAiTWVkaWFuIEhvdXNlIFZhbHVlICgkMTAwMHMpIikgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCiMjIyBFeGVyY2lzZSAyCgpgYGB7cn0KbW9kZWwgPC0gbG0ocXNlYyB+IGhwICsgd3QgKyBmYWN0b3IoYW0pLCBkYXRhID0gbXRjYXJzKQpzdW1tYXJ5KG1vZGVsKQoKcGFyKG1mcm93ID0gYygyLCAyKSkKcGxvdChtb2RlbCkKYGBgCgojIyMgRXhlcmNpc2UgMwoKYGBge3J9Cm1vZGVsIDwtIGxtKHByaWNlIH4gY2FyYXQgKyBjdXQgKyBjb2xvciArIGNsYXJpdHksIGRhdGEgPSBkaWFtb25kcykKc3VtbWFyeShtb2RlbCkKYGBgCgojIyBDbGFzc2lmaWNhdGlvbgoKIyMjIEV4ZXJjaXNlIDEKCmBgYHtyfQpsaWJyYXJ5KHBST0MpCgojIFByZXBhcmUgdGhlIGRhdGEKbXRjYXJzJGFtIDwtIGFzLmZhY3RvcihtdGNhcnMkYW0pCnNldC5zZWVkKDEyMykKdHJhaW5faW5kaWNlcyA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKG10Y2FycyRhbSwgcCA9IDAuNywgbGlzdCA9IEZBTFNFKQp0cmFpbl9kYXRhIDwtIG10Y2Fyc1t0cmFpbl9pbmRpY2VzLCBdCnRlc3RfZGF0YSA8LSBtdGNhcnNbLXRyYWluX2luZGljZXMsIF0KCiMgRml0IHRoZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsCm1vZGVsIDwtIGdsbShhbSB+IG1wZyArIGhwICsgd3QsIGRhdGEgPSB0cmFpbl9kYXRhLCBmYW1pbHkgPSAiYmlub21pYWwiKQoKIyBNYWtlIHByZWRpY3Rpb25zCnByZWRpY3Rpb25zIDwtIHByZWRpY3QobW9kZWwsIG5ld2RhdGEgPSB0ZXN0X2RhdGEsIHR5cGUgPSAicmVzcG9uc2UiKQpwcmVkaWN0ZWRfY2xhc3NlcyA8LSBpZmVsc2UocHJlZGljdGlvbnMgPiAwLjUsIDEsIDApCgojIENvbmZ1c2lvbiBtYXRyaXgKY29uZl9tYXRyaXggPC0gdGFibGUoUHJlZGljdGVkID0gcHJlZGljdGVkX2NsYXNzZXMsIEFjdHVhbCA9IHRlc3RfZGF0YSRhbSkKcHJpbnQoY29uZl9tYXRyaXgpCgojIFJPQyBjdXJ2ZQpyb2NfY3VydmUgPC0gcm9jKHRlc3RfZGF0YSRhbSwgcHJlZGljdGlvbnMpCnBsb3Qocm9jX2N1cnZlLCBtYWluID0gIlJPQyBDdXJ2ZSBmb3IgVHJhbnNtaXNzaW9uIFR5cGUgUHJlZGljdGlvbiIpCmBgYAoKIyMjIEV4ZXJjaXNlIDIKCmBgYHtyfQpsaWJyYXJ5KHJhdHRsZSkKZGF0YSh3aW5lKQoKIyBQcmVwYXJlIHRoZSBkYXRhCnNldC5zZWVkKDEyMykKdHJhaW5faW5kaWNlcyA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHdpbmUkVHlwZSwgcCA9IDAuNywgbGlzdCA9IEZBTFNFKQp0cmFpbl9kYXRhIDwtIHdpbmVbdHJhaW5faW5kaWNlcywgXQp0ZXN0X2RhdGEgPC0gd2luZVstdHJhaW5faW5kaWNlcywgXQoKIyBGaW5kIHRoZSBiZXN0IGsgdXNpbmcgY3Jvc3MtdmFsaWRhdGlvbgprX3ZhbHVlcyA8LSAxOjIwCmFjY3VyYWN5IDwtIHNhcHBseShrX3ZhbHVlcywgZnVuY3Rpb24oaykgewogIG1vZGVsIDwtIHRyYWluKFR5cGUgfiAuLCBkYXRhID0gdHJhaW5fZGF0YSwgbWV0aG9kID0gImtubiIsIAogICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSA1KSwKICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGRhdGEuZnJhbWUoayA9IGspKQogIHJldHVybihtb2RlbCRyZXN1bHRzJEFjY3VyYWN5KQp9KQoKYmVzdF9rIDwtIGtfdmFsdWVzW3doaWNoLm1heChhY2N1cmFjeSldCgojIFRyYWluIHRoZSBmaW5hbCBtb2RlbCB3aXRoIHRoZSBiZXN0IGsKZmluYWxfbW9kZWwgPC0gdHJhaW4oVHlwZSB+IC4sIGRhdGEgPSB0cmFpbl9kYXRhLCBtZXRob2QgPSAia25uIiwgCiAgICAgICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSA1KSwKICAgICAgICAgICAgICAgICAgICAgdHVuZUdyaWQgPSBkYXRhLmZyYW1lKGsgPSBiZXN0X2spKQoKIyBFdmFsdWF0ZSBvbiB0aGUgdGVzdCBzZXQKcHJlZGljdGlvbnMgPC0gcHJlZGljdChmaW5hbF9tb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YSkKY29uZnVzaW9uTWF0cml4KHByZWRpY3Rpb25zLCB0ZXN0X2RhdGEkVHlwZSkKYGBgCgojIyMgRXhlcmNpc2UgMwoKYGBge3J9CmxpYnJhcnkoSVNMUikKbGlicmFyeShycGFydCkKbGlicmFyeShycGFydC5wbG90KQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkKCiMgUHJlcGFyZSB0aGUgZGF0YQpzZXQuc2VlZCgxMjMpCnRyYWluX2luZGljZXMgPC0gY3JlYXRlRGF0YVBhcnRpdGlvbihEZWZhdWx0JGRlZmF1bHQsIHAgPSAwLjcsIGxpc3QgPSBGQUxTRSkKdHJhaW5fZGF0YSA8LSBEZWZhdWx0W3RyYWluX2luZGljZXMsIF0KdGVzdF9kYXRhIDwtIERlZmF1bHRbLXRyYWluX2luZGljZXMsIF0KCiMgRGVjaXNpb24gVHJlZQp0cmVlX21vZGVsIDwtIHJwYXJ0KGRlZmF1bHQgfiAuLCBkYXRhID0gdHJhaW5fZGF0YSwgbWV0aG9kID0gImNsYXNzIikKcnBhcnQucGxvdCh0cmVlX21vZGVsLCBleHRyYSA9IDEwMSkKCnRyZWVfcHJlZGljdGlvbnMgPC0gcHJlZGljdCh0cmVlX21vZGVsLCBuZXdkYXRhID0gdGVzdF9kYXRhLCB0eXBlID0gImNsYXNzIikKdHJlZV9jbSA8LSBjb25mdXNpb25NYXRyaXgodHJlZV9wcmVkaWN0aW9ucywgdGVzdF9kYXRhJGRlZmF1bHQpCnByaW50KHRyZWVfY20pCgojIFJhbmRvbSBGb3Jlc3QKcmZfbW9kZWwgPC0gcmFuZG9tRm9yZXN0KGRlZmF1bHQgfiAuLCBkYXRhID0gdHJhaW5fZGF0YSkKcmZfcHJlZGljdGlvbnMgPC0gcHJlZGljdChyZl9tb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YSkKcmZfY20gPC0gY29uZnVzaW9uTWF0cml4KHJmX3ByZWRpY3Rpb25zLCB0ZXN0X2RhdGEkZGVmYXVsdCkKcHJpbnQocmZfY20pCgojIENvbXBhcmUgcGVyZm9ybWFuY2UKY2F0KCJEZWNpc2lvbiBUcmVlIEFjY3VyYWN5OiIsIHRyZWVfY20kb3ZlcmFsbFsiQWNjdXJhY3kiXSwgIlxuIikKY2F0KCJSYW5kb20gRm9yZXN0IEFjY3VyYWN5OiIsIHJmX2NtJG92ZXJhbGxbIkFjY3VyYWN5Il0sICJcbiIpCmBgYA==