Homework: Predicting Price of Used Car Sales

Consider the data on used cars (ToyotaCorolla.csv) with 1436 records and details on 38 attributes, including Price, Age, KM, HP, and other specifications. The goal is to predict the price of a used Toyota Corolla based on its specifications.

# import ToyotaCorolla.csv and
cars <- read.csv("ToyotaCorolla.csv")
str(cars)
## 'data.frame':    1436 obs. of  39 variables:
##  $ Id               : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ Model            : chr  "TOYOTA Corolla 2.0 D4D HATCHB TERRA 2/3-Doors" "TOYOTA Corolla 2.0 D4D HATCHB TERRA 2/3-Doors" "TOYOTA Corolla 2.0 D4D HATCHB TERRA 2/3-Doors" "TOYOTA Corolla 2.0 D4D HATCHB TERRA 2/3-Doors" ...
##  $ Price            : int  13500 13750 13950 14950 13750 12950 16900 18600 21500 12950 ...
##  $ Age_08_04        : int  23 23 24 26 30 32 27 30 27 23 ...
##  $ Mfg_Month        : int  10 10 9 7 3 1 6 3 6 10 ...
##  $ Mfg_Year         : int  2002 2002 2002 2002 2002 2002 2002 2002 2002 2002 ...
##  $ KM               : int  46986 72937 41711 48000 38500 61000 94612 75889 19700 71138 ...
##  $ Fuel_Type        : chr  "Diesel" "Diesel" "Diesel" "Diesel" ...
##  $ HP               : int  90 90 90 90 90 90 90 90 192 69 ...
##  $ Met_Color        : int  1 1 1 0 0 0 1 1 0 0 ...
##  $ Color            : chr  "Blue" "Silver" "Blue" "Black" ...
##  $ Automatic        : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ CC               : int  2000 2000 2000 2000 2000 2000 2000 2000 1800 1900 ...
##  $ Doors            : int  3 3 3 3 3 3 3 3 3 3 ...
##  $ Cylinders        : int  4 4 4 4 4 4 4 4 4 4 ...
##  $ Gears            : int  5 5 5 5 5 5 5 5 5 5 ...
##  $ Quarterly_Tax    : int  210 210 210 210 210 210 210 210 100 185 ...
##  $ Weight           : int  1165 1165 1165 1165 1170 1170 1245 1245 1185 1105 ...
##  $ Mfr_Guarantee    : int  0 0 1 1 1 0 0 1 0 0 ...
##  $ BOVAG_Guarantee  : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ Guarantee_Period : int  3 3 3 3 3 3 3 3 3 3 ...
##  $ ABS              : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ Airbag_1         : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ Airbag_2         : int  1 1 1 1 1 1 1 1 0 1 ...
##  $ Airco            : int  0 1 0 0 1 1 1 1 1 1 ...
##  $ Automatic_airco  : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ Boardcomputer    : int  1 1 1 1 1 1 1 1 0 1 ...
##  $ CD_Player        : int  0 1 0 0 0 0 0 1 0 0 ...
##  $ Central_Lock     : int  1 1 0 0 1 1 1 1 1 0 ...
##  $ Powered_Windows  : int  1 0 0 0 1 1 1 1 1 0 ...
##  $ Power_Steering   : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ Radio            : int  0 0 0 0 0 0 0 0 1 0 ...
##  $ Mistlamps        : int  0 0 0 0 1 1 0 0 0 0 ...
##  $ Sport_Model      : int  0 0 0 0 0 0 1 0 0 0 ...
##  $ Backseat_Divider : int  1 1 1 1 1 1 1 1 0 1 ...
##  $ Metallic_Rim     : int  0 0 0 0 0 0 0 0 1 0 ...
##  $ Radio_cassette   : int  0 0 0 0 0 0 0 0 1 0 ...
##  $ Parking_Assistant: int  0 0 0 0 0 0 0 0 0 0 ...
##  $ Tow_Bar          : int  0 0 0 0 0 0 0 0 0 0 ...

Exercise 1

  • Preprocess data appropriately for neural network modelling.
    • Select following variables: Price, Age_08_04, KM, Fuel_Type, HP, Automatic, Doors, Quarterly_Tax, Mfr_Guarantee, Guarantee_Period, Airco, Automatic_airco, CD_Player, Powered_Windows, Sport_Model, and Tow_Bar.
    • Remember to first scale the numerical predictor and outcome variables to a 0–1 scale.
    • Convert categorical predictors to dummies.
  • Use 70% of the data for training and 30% for test.
# Type your code here
set.seed(54321)
# Load necessary libraries
library(tidyverse)
library(caret) # for splitting the data
## Loading required package: lattice
## 
## Attaching package: 'lattice'
## The following objects are masked from 'package:openintro':
## 
##     ethanol, lsegments
## 
## Attaching package: 'caret'
## The following object is masked from 'package:openintro':
## 
##     dotPlot
## The following object is masked from 'package:purrr':
## 
##     lift
library(Metrics) # for RMSE calculation
## 
## Attaching package: 'Metrics'
## The following objects are masked from 'package:caret':
## 
##     precision, recall
library(dplyr)

# Load the dataset (Assuming the dataset is loaded in the variable 'cars')
# cars <- read.csv("ToyotaCorolla.csv")

# Step 1: Select relevant variables
cars_selected <- cars[, c("Price", "Age_08_04", "KM", "Fuel_Type", "HP", "Automatic", "Doors", 
                          "Quarterly_Tax", "Mfr_Guarantee", "Guarantee_Period", "Airco", 
                          "Automatic_airco", "CD_Player", "Powered_Windows", "Sport_Model", "Tow_Bar")]


# Step 2: Scale numerical predictors and outcome variables (Price, Age_08_04, KM, HP, etc.)
# Scaling function to rescale to 0-1 range
scaled_cars <- cars_selected %>%
  mutate(
    Price = (Price - min(Price)) / (max(Price) - min(Price)), # Scaling Price
    Age_08_04 = (Age_08_04 - min(Age_08_04)) / (max(Age_08_04) - min(Age_08_04)), # Scaling Age
    KM = (KM - min(KM)) / (max(KM) - min(KM)), # Scaling KM
    HP = (HP - min(HP)) / (max(HP) - min(HP)), # Scaling HP
    Quarterly_Tax = (Quarterly_Tax - min(Quarterly_Tax)) / (max(Quarterly_Tax) - min(Quarterly_Tax)), # Scaling Quarterly_Tax
    Guarantee_Period = (Guarantee_Period - min(Guarantee_Period)) / (max(Guarantee_Period) - min(Guarantee_Period)) # Scaling Guarantee_Period
  )

# Step 3: Convert 'Fuel_Type' to dummy variables (one-hot encoding)
fuel_dummies <- model.matrix(~ Fuel_Type - 1, data = scaled_cars)

# Step 4: Bind the dummy variables back to the data frame
scaled_cars <- bind_cols(scaled_cars, fuel_dummies)

# Remove the original 'Fuel_Type' column using base R
scaled_cars <- scaled_cars[, !grepl("Fuel_Type", colnames(scaled_cars))]

# Check the updated structure of the data frame
str(scaled_cars)
## 'data.frame':    1436 obs. of  15 variables:
##  $ Price           : num  0.325 0.334 0.341 0.377 0.334 ...
##  $ Age_08_04       : num  0.278 0.278 0.291 0.316 0.367 ...
##  $ KM              : num  0.193 0.3 0.172 0.198 0.158 ...
##  $ HP              : num  0.171 0.171 0.171 0.171 0.171 ...
##  $ Automatic       : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ Doors           : int  3 3 3 3 3 3 3 3 3 3 ...
##  $ Quarterly_Tax   : num  0.723 0.723 0.723 0.723 0.723 ...
##  $ Mfr_Guarantee   : int  0 0 1 1 1 0 0 1 0 0 ...
##  $ Guarantee_Period: num  0 0 0 0 0 0 0 0 0 0 ...
##  $ Airco           : int  0 1 0 0 1 1 1 1 1 1 ...
##  $ Automatic_airco : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ CD_Player       : int  0 1 0 0 0 0 0 1 0 0 ...
##  $ Powered_Windows : int  1 0 0 0 1 1 1 1 1 0 ...
##  $ Sport_Model     : int  0 0 0 0 0 0 1 0 0 0 ...
##  $ Tow_Bar         : int  0 0 0 0 0 0 0 0 0 0 ...
# Step 5: Split the data into training (70%) and test (30%) sets
set.seed(54321) # Set a seed for reproducibility
trainIndex <- createDataPartition(scaled_cars$Price, p = 0.7, list = FALSE)

# Create training and testing datasets
train_data <- scaled_cars[trainIndex, ]
test_data <- scaled_cars[-trainIndex, ]

# View the structure of the train and test data
str(train_data)
## 'data.frame':    1007 obs. of  15 variables:
##  $ Price           : num  0.325 0.341 0.377 0.334 0.506 ...
##  $ Age_08_04       : num  0.278 0.291 0.316 0.367 0.367 ...
##  $ KM              : num  0.193 0.172 0.198 0.158 0.312 ...
##  $ HP              : num  0.171 0.171 0.171 0.171 0.171 ...
##  $ Automatic       : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ Doors           : int  3 3 3 3 3 3 3 3 3 3 ...
##  $ Quarterly_Tax   : num  0.723 0.723 0.723 0.723 0.723 ...
##  $ Mfr_Guarantee   : int  0 1 1 1 1 0 1 1 1 1 ...
##  $ Guarantee_Period: num  0 0 0 0 0 ...
##  $ Airco           : int  0 0 0 1 1 1 1 1 1 1 ...
##  $ Automatic_airco : int  0 0 0 0 0 0 1 1 1 1 ...
##  $ CD_Player       : int  0 0 0 0 1 0 1 0 0 1 ...
##  $ Powered_Windows : int  1 0 0 1 1 1 1 1 1 1 ...
##  $ Sport_Model     : int  0 0 0 0 0 0 0 1 1 1 ...
##  $ Tow_Bar         : int  0 0 0 0 0 0 0 0 0 0 ...
str(test_data)
## 'data.frame':    429 obs. of  15 variables:
##  $ Price           : num  0.334 0.306 0.446 0.306 0.448 ...
##  $ Age_08_04       : num  0.278 0.392 0.329 0.278 0.367 ...
##  $ KM              : num  0.3 0.251 0.389 0.293 0.265 ...
##  $ HP              : num  0.171 0.171 0.171 0 0.333 ...
##  $ Automatic       : int  0 0 0 0 0 0 1 0 0 0 ...
##  $ Doors           : int  3 3 3 3 3 3 3 3 3 3 ...
##  $ Quarterly_Tax   : num  0.723 0.723 0.723 0.629 0.25 ...
##  $ Mfr_Guarantee   : int  0 0 0 0 1 1 0 1 1 1 ...
##  $ Guarantee_Period: num  0 0 0 0 0 0 0 0 0 0 ...
##  $ Airco           : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ Automatic_airco : int  0 0 0 0 0 0 1 1 1 1 ...
##  $ CD_Player       : int  1 0 0 0 1 1 0 1 1 0 ...
##  $ Powered_Windows : int  0 1 1 0 1 1 1 1 1 1 ...
##  $ Sport_Model     : int  0 0 1 0 1 1 1 1 1 1 ...
##  $ Tow_Bar         : int  0 0 0 0 0 1 0 0 0 0 ...

Exercise 2

Fit a neural network model to the data. Use a single hidden layer with 2 nodes.
- Use predictors Age_08_04, KM, Fuel_Type, HP, Automatic, Doors, Quarterly_Tax, Mfr_Guarantee, Guarantee_Period, Airco, Automatic_airco, CD_Player, Powered_Windows, Sport_Model, and Tow_Bar.
- Record the RMSE for the training data and the test data.

# Type your code here
# Load necessary libraries
library(neuralnet)
## 
## Attaching package: 'neuralnet'
## The following object is masked from 'package:dplyr':
## 
##     compute
library(caret)

# Step 1: Split the data into training (70%) and test (30%) sets
set.seed(54321)
trainIndex <- createDataPartition(scaled_cars$Price, p = 0.7, list = FALSE)
train_data <- scaled_cars[trainIndex, ]
test_data <- scaled_cars[-trainIndex, ]

# Step 2: Define the formula for the neural network model
formula <- Price ~ Age_08_04 + KM + HP + Automatic + Doors + 
  Quarterly_Tax + Mfr_Guarantee + Guarantee_Period + Airco + 
  Automatic_airco + CD_Player + Powered_Windows + Sport_Model + Tow_Bar

# Step 3: Fit the neural network model with a single hidden layer (2 nodes)
nn_model <- neuralnet(formula, data = train_data, hidden = 2, linear.output = TRUE)

plot(nn_model)

# Step 4: Make predictions on the training and test data
train_predictions <- predict(nn_model, train_data)
test_predictions <- predict(nn_model, test_data)

# Step 5: Calculate RMSE for both the training and test data
rmse_train <- sqrt(mean((train_predictions - train_data$Price)^2))
rmse_test <- sqrt(mean((test_predictions - test_data$Price)^2))

# Print RMSE values
print(paste("RMSE on training data:", round(rmse_train, 2)))
## [1] "RMSE on training data: 0.04"
print(paste("RMSE on test data:", round(rmse_test, 2)))
## [1] "RMSE on test data: 0.04"

Exercise 3

Repeat the process, changing the number of hidden layers and nodes to {single layer with 5 nodes}, {two layers, 5 nodes in each layer}
i. What happens to the RMS error for the training data as the number of layers and nodes increases?
ii. What happens to the RMS error for the validation data?
iii. Comment on the appropriate number of layers and nodes for this application.

# Load necessary libraries
library(neuralnet)
library(caret)
library(gridExtra)
## 
## Attaching package: 'gridExtra'
## The following object is masked from 'package:dplyr':
## 
##     combine
# Step 1: Split the data into training (70%) and test (30%) sets
set.seed(54321)
trainIndex <- createDataPartition(scaled_cars$Price, p = 0.7, list = FALSE)
train_data <- scaled_cars[trainIndex, ]
test_data <- scaled_cars[-trainIndex, ]

# Step 2: Define the formula for the neural network model
formula <- Price ~ Age_08_04 + KM + HP + Automatic + Doors + 
  Quarterly_Tax + Mfr_Guarantee + Guarantee_Period + Airco + 
  Automatic_airco + CD_Player + Powered_Windows + Sport_Model + Tow_Bar

# -----------------------------------------------
# Model 1: Single hidden layer with 5 nodes
# -----------------------------------------------
nn_model_1 <- neuralnet(formula, data = train_data, hidden = 5, linear.output = TRUE)

# Plot the neural network model
#plot(nn_model_1)
# Plot the neural network model for Model 1
plot(nn_model_1, main = "Model 1: Single Hidden Layer with 5 Nodes")


# Step 3: Make predictions on the training and test data
train_predictions_1 <- predict(nn_model_1, train_data)
test_predictions_1 <- predict(nn_model_1, test_data)

# Step 4: Calculate RMSE for both the training and test data for Model 1
rmse_train_1 <- sqrt(mean((train_predictions_1 - train_data$Price)^2))
rmse_test_1 <- sqrt(mean((test_predictions_1 - test_data$Price)^2))

# Print RMSE values for Model 1
cat("Model 1 - Single hidden layer with 5 nodes:\n")
## Model 1 - Single hidden layer with 5 nodes:
cat("RMSE on training data:", round(rmse_train_1, 2), "\n")
## RMSE on training data: 0.04
cat("RMSE on test data:", round(rmse_test_1, 2), "\n\n")
## RMSE on test data: 0.04
# -----------------------------------------------
# Model 2: Two hidden layers, 5 nodes in each layer
# -----------------------------------------------
nn_model_2 <- neuralnet(formula, data = train_data, hidden = c(5, 5), linear.output = TRUE)

# Plot the neural network model for Model 2
plot(nn_model_2, main = "Model 2: Two Hidden Layers with 5 Nodes Each")

# Step 3: Make predictions on the training and test data
train_predictions_2 <- predict(nn_model_2, train_data)
test_predictions_2 <- predict(nn_model_2, test_data)

# Step 4: Calculate RMSE for both the training and test data for Model 2
rmse_train_2 <- sqrt(mean((train_predictions_2 - train_data$Price)^2))
rmse_test_2 <- sqrt(mean((test_predictions_2 - test_data$Price)^2))

# Print RMSE values for Model 2
cat("Model 2 - Two hidden layers with 5 nodes each:\n")
## Model 2 - Two hidden layers with 5 nodes each:
cat("RMSE on training data:", round(rmse_train_2, 2), "\n")
## RMSE on training data: 0.03
cat("RMSE on test data:", round(rmse_test_2, 2), "\n\n")
## RMSE on test data: 0.04
LS0tCnRpdGxlOiAiRUNPTiAzMjAwOiBIb21ld29yayA1IgphdXRob3I6ICJTYXR5YSBOYXJheWFuYSBQYW5kYSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6IG9wZW5pbnRybzo6bGFiX3JlcG9ydAotLS0KCmBgYHtyIGxvYWQtcGFja2FnZXMsIG1lc3NhZ2U9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChldmFsID0gVFJVRSkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkob3BlbmludHJvKQpsaWJyYXJ5KGRwbHlyKQpgYGAKCiMgSG9tZXdvcms6IFByZWRpY3RpbmcgUHJpY2Ugb2YgVXNlZCBDYXIgU2FsZXMKCkNvbnNpZGVyIHRoZSBkYXRhIG9uIHVzZWQgY2FycyAoYFRveW90YUNvcm9sbGEuY3N2YCkgd2l0aCAxNDM2IHJlY29yZHMgYW5kIGRldGFpbHMgb24gMzggYXR0cmlidXRlcywgaW5jbHVkaW5nIFByaWNlLCBBZ2UsIEtNLCBIUCwgYW5kIG90aGVyIHNwZWNpZmljYXRpb25zLiBUaGUgZ29hbCBpcyB0byBwcmVkaWN0IHRoZSBwcmljZSBvZiBhIHVzZWQgVG95b3RhIENvcm9sbGEgYmFzZWQgb24gaXRzIHNwZWNpZmljYXRpb25zLgoKYGBge3J9CiMgaW1wb3J0IFRveW90YUNvcm9sbGEuY3N2IGFuZApjYXJzIDwtIHJlYWQuY3N2KCJUb3lvdGFDb3JvbGxhLmNzdiIpCnN0cihjYXJzKQpgYGAKCiMjIEV4ZXJjaXNlIDEKCi0gICBQcmVwcm9jZXNzIGRhdGEgYXBwcm9wcmlhdGVseSBmb3IgbmV1cmFsIG5ldHdvcmsgbW9kZWxsaW5nLgogICAgLSAgIFNlbGVjdCBmb2xsb3dpbmcgdmFyaWFibGVzOiBQcmljZSwgQWdlXzA4XzA0LCBLTSwgRnVlbF9UeXBlLCBIUCwgQXV0b21hdGljLCBEb29ycywgUXVhcnRlcmx5X1RheCwgTWZyX0d1YXJhbnRlZSwgR3VhcmFudGVlX1BlcmlvZCwgQWlyY28sIEF1dG9tYXRpY19haXJjbywgQ0RfUGxheWVyLCBQb3dlcmVkX1dpbmRvd3MsIFNwb3J0X01vZGVsLCBhbmQgVG93X0Jhci5cCiAgICAtICAgUmVtZW1iZXIgdG8gZmlyc3Qgc2NhbGUgdGhlIG51bWVyaWNhbCBwcmVkaWN0b3IgYW5kIG91dGNvbWUgdmFyaWFibGVzIHRvIGEgMOKAkzEgc2NhbGUuXAogICAgLSAgIENvbnZlcnQgY2F0ZWdvcmljYWwgcHJlZGljdG9ycyB0byBkdW1taWVzLlwKLSAgIFVzZSA3MCUgb2YgdGhlIGRhdGEgZm9yIHRyYWluaW5nIGFuZCAzMCUgZm9yIHRlc3QuCgpgYGB7cn0KIyBUeXBlIHlvdXIgY29kZSBoZXJlCnNldC5zZWVkKDU0MzIxKQojIExvYWQgbmVjZXNzYXJ5IGxpYnJhcmllcwpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShjYXJldCkgIyBmb3Igc3BsaXR0aW5nIHRoZSBkYXRhCmxpYnJhcnkoTWV0cmljcykgIyBmb3IgUk1TRSBjYWxjdWxhdGlvbgpsaWJyYXJ5KGRwbHlyKQoKIyBMb2FkIHRoZSBkYXRhc2V0IChBc3N1bWluZyB0aGUgZGF0YXNldCBpcyBsb2FkZWQgaW4gdGhlIHZhcmlhYmxlICdjYXJzJykKIyBjYXJzIDwtIHJlYWQuY3N2KCJUb3lvdGFDb3JvbGxhLmNzdiIpCgojIFN0ZXAgMTogU2VsZWN0IHJlbGV2YW50IHZhcmlhYmxlcwpjYXJzX3NlbGVjdGVkIDwtIGNhcnNbLCBjKCJQcmljZSIsICJBZ2VfMDhfMDQiLCAiS00iLCAiRnVlbF9UeXBlIiwgIkhQIiwgIkF1dG9tYXRpYyIsICJEb29ycyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICJRdWFydGVybHlfVGF4IiwgIk1mcl9HdWFyYW50ZWUiLCAiR3VhcmFudGVlX1BlcmlvZCIsICJBaXJjbyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICJBdXRvbWF0aWNfYWlyY28iLCAiQ0RfUGxheWVyIiwgIlBvd2VyZWRfV2luZG93cyIsICJTcG9ydF9Nb2RlbCIsICJUb3dfQmFyIildCgoKIyBTdGVwIDI6IFNjYWxlIG51bWVyaWNhbCBwcmVkaWN0b3JzIGFuZCBvdXRjb21lIHZhcmlhYmxlcyAoUHJpY2UsIEFnZV8wOF8wNCwgS00sIEhQLCBldGMuKQojIFNjYWxpbmcgZnVuY3Rpb24gdG8gcmVzY2FsZSB0byAwLTEgcmFuZ2UKc2NhbGVkX2NhcnMgPC0gY2Fyc19zZWxlY3RlZCAlPiUKICBtdXRhdGUoCiAgICBQcmljZSA9IChQcmljZSAtIG1pbihQcmljZSkpIC8gKG1heChQcmljZSkgLSBtaW4oUHJpY2UpKSwgIyBTY2FsaW5nIFByaWNlCiAgICBBZ2VfMDhfMDQgPSAoQWdlXzA4XzA0IC0gbWluKEFnZV8wOF8wNCkpIC8gKG1heChBZ2VfMDhfMDQpIC0gbWluKEFnZV8wOF8wNCkpLCAjIFNjYWxpbmcgQWdlCiAgICBLTSA9IChLTSAtIG1pbihLTSkpIC8gKG1heChLTSkgLSBtaW4oS00pKSwgIyBTY2FsaW5nIEtNCiAgICBIUCA9IChIUCAtIG1pbihIUCkpIC8gKG1heChIUCkgLSBtaW4oSFApKSwgIyBTY2FsaW5nIEhQCiAgICBRdWFydGVybHlfVGF4ID0gKFF1YXJ0ZXJseV9UYXggLSBtaW4oUXVhcnRlcmx5X1RheCkpIC8gKG1heChRdWFydGVybHlfVGF4KSAtIG1pbihRdWFydGVybHlfVGF4KSksICMgU2NhbGluZyBRdWFydGVybHlfVGF4CiAgICBHdWFyYW50ZWVfUGVyaW9kID0gKEd1YXJhbnRlZV9QZXJpb2QgLSBtaW4oR3VhcmFudGVlX1BlcmlvZCkpIC8gKG1heChHdWFyYW50ZWVfUGVyaW9kKSAtIG1pbihHdWFyYW50ZWVfUGVyaW9kKSkgIyBTY2FsaW5nIEd1YXJhbnRlZV9QZXJpb2QKICApCgojIFN0ZXAgMzogQ29udmVydCAnRnVlbF9UeXBlJyB0byBkdW1teSB2YXJpYWJsZXMgKG9uZS1ob3QgZW5jb2RpbmcpCmZ1ZWxfZHVtbWllcyA8LSBtb2RlbC5tYXRyaXgofiBGdWVsX1R5cGUgLSAxLCBkYXRhID0gc2NhbGVkX2NhcnMpCgojIFN0ZXAgNDogQmluZCB0aGUgZHVtbXkgdmFyaWFibGVzIGJhY2sgdG8gdGhlIGRhdGEgZnJhbWUKc2NhbGVkX2NhcnMgPC0gYmluZF9jb2xzKHNjYWxlZF9jYXJzLCBmdWVsX2R1bW1pZXMpCgojIFJlbW92ZSB0aGUgb3JpZ2luYWwgJ0Z1ZWxfVHlwZScgY29sdW1uIHVzaW5nIGJhc2UgUgpzY2FsZWRfY2FycyA8LSBzY2FsZWRfY2Fyc1ssICFncmVwbCgiRnVlbF9UeXBlIiwgY29sbmFtZXMoc2NhbGVkX2NhcnMpKV0KCiMgQ2hlY2sgdGhlIHVwZGF0ZWQgc3RydWN0dXJlIG9mIHRoZSBkYXRhIGZyYW1lCnN0cihzY2FsZWRfY2FycykKCgojIFN0ZXAgNTogU3BsaXQgdGhlIGRhdGEgaW50byB0cmFpbmluZyAoNzAlKSBhbmQgdGVzdCAoMzAlKSBzZXRzCnNldC5zZWVkKDU0MzIxKSAjIFNldCBhIHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQp0cmFpbkluZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oc2NhbGVkX2NhcnMkUHJpY2UsIHAgPSAwLjcsIGxpc3QgPSBGQUxTRSkKCiMgQ3JlYXRlIHRyYWluaW5nIGFuZCB0ZXN0aW5nIGRhdGFzZXRzCnRyYWluX2RhdGEgPC0gc2NhbGVkX2NhcnNbdHJhaW5JbmRleCwgXQp0ZXN0X2RhdGEgPC0gc2NhbGVkX2NhcnNbLXRyYWluSW5kZXgsIF0KCiMgVmlldyB0aGUgc3RydWN0dXJlIG9mIHRoZSB0cmFpbiBhbmQgdGVzdCBkYXRhCnN0cih0cmFpbl9kYXRhKQpzdHIodGVzdF9kYXRhKQoKCmBgYAoKIyMgRXhlcmNpc2UgMgoKRml0IGEgbmV1cmFsIG5ldHdvcmsgbW9kZWwgdG8gdGhlIGRhdGEuIFVzZSBhIHNpbmdsZSBoaWRkZW4gbGF5ZXIgd2l0aCAyIG5vZGVzLlwKLSBVc2UgcHJlZGljdG9ycyBBZ2VfMDhfMDQsIEtNLCBGdWVsX1R5cGUsIEhQLCBBdXRvbWF0aWMsIERvb3JzLCBRdWFydGVybHlfVGF4LCBNZnJfR3VhcmFudGVlLCBHdWFyYW50ZWVfUGVyaW9kLCBBaXJjbywgQXV0b21hdGljX2FpcmNvLCBDRF9QbGF5ZXIsIFBvd2VyZWRfV2luZG93cywgU3BvcnRfTW9kZWwsIGFuZCBUb3dfQmFyLlwKLSBSZWNvcmQgdGhlIFJNU0UgZm9yIHRoZSB0cmFpbmluZyBkYXRhIGFuZCB0aGUgdGVzdCBkYXRhLgoKYGBge3J9CiMgVHlwZSB5b3VyIGNvZGUgaGVyZQojIExvYWQgbmVjZXNzYXJ5IGxpYnJhcmllcwpsaWJyYXJ5KG5ldXJhbG5ldCkKbGlicmFyeShjYXJldCkKCiMgU3RlcCAxOiBTcGxpdCB0aGUgZGF0YSBpbnRvIHRyYWluaW5nICg3MCUpIGFuZCB0ZXN0ICgzMCUpIHNldHMKc2V0LnNlZWQoNTQzMjEpCnRyYWluSW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbihzY2FsZWRfY2FycyRQcmljZSwgcCA9IDAuNywgbGlzdCA9IEZBTFNFKQp0cmFpbl9kYXRhIDwtIHNjYWxlZF9jYXJzW3RyYWluSW5kZXgsIF0KdGVzdF9kYXRhIDwtIHNjYWxlZF9jYXJzWy10cmFpbkluZGV4LCBdCgojIFN0ZXAgMjogRGVmaW5lIHRoZSBmb3JtdWxhIGZvciB0aGUgbmV1cmFsIG5ldHdvcmsgbW9kZWwKZm9ybXVsYSA8LSBQcmljZSB+IEFnZV8wOF8wNCArIEtNICsgSFAgKyBBdXRvbWF0aWMgKyBEb29ycyArIAogIFF1YXJ0ZXJseV9UYXggKyBNZnJfR3VhcmFudGVlICsgR3VhcmFudGVlX1BlcmlvZCArIEFpcmNvICsgCiAgQXV0b21hdGljX2FpcmNvICsgQ0RfUGxheWVyICsgUG93ZXJlZF9XaW5kb3dzICsgU3BvcnRfTW9kZWwgKyBUb3dfQmFyCgojIFN0ZXAgMzogRml0IHRoZSBuZXVyYWwgbmV0d29yayBtb2RlbCB3aXRoIGEgc2luZ2xlIGhpZGRlbiBsYXllciAoMiBub2RlcykKbm5fbW9kZWwgPC0gbmV1cmFsbmV0KGZvcm11bGEsIGRhdGEgPSB0cmFpbl9kYXRhLCBoaWRkZW4gPSAyLCBsaW5lYXIub3V0cHV0ID0gVFJVRSkKCnBsb3Qobm5fbW9kZWwpCgojIFN0ZXAgNDogTWFrZSBwcmVkaWN0aW9ucyBvbiB0aGUgdHJhaW5pbmcgYW5kIHRlc3QgZGF0YQp0cmFpbl9wcmVkaWN0aW9ucyA8LSBwcmVkaWN0KG5uX21vZGVsLCB0cmFpbl9kYXRhKQp0ZXN0X3ByZWRpY3Rpb25zIDwtIHByZWRpY3Qobm5fbW9kZWwsIHRlc3RfZGF0YSkKCiMgU3RlcCA1OiBDYWxjdWxhdGUgUk1TRSBmb3IgYm90aCB0aGUgdHJhaW5pbmcgYW5kIHRlc3QgZGF0YQpybXNlX3RyYWluIDwtIHNxcnQobWVhbigodHJhaW5fcHJlZGljdGlvbnMgLSB0cmFpbl9kYXRhJFByaWNlKV4yKSkKcm1zZV90ZXN0IDwtIHNxcnQobWVhbigodGVzdF9wcmVkaWN0aW9ucyAtIHRlc3RfZGF0YSRQcmljZSleMikpCgojIFByaW50IFJNU0UgdmFsdWVzCnByaW50KHBhc3RlKCJSTVNFIG9uIHRyYWluaW5nIGRhdGE6Iiwgcm91bmQocm1zZV90cmFpbiwgMikpKQpwcmludChwYXN0ZSgiUk1TRSBvbiB0ZXN0IGRhdGE6Iiwgcm91bmQocm1zZV90ZXN0LCAyKSkpCgoKCgoKCmBgYAoKIyMgRXhlcmNpc2UgMwoKUmVwZWF0IHRoZSBwcm9jZXNzLCBjaGFuZ2luZyB0aGUgbnVtYmVyIG9mIGhpZGRlbiBsYXllcnMgYW5kIG5vZGVzIHRvIHtzaW5nbGUgbGF5ZXIgd2l0aCA1IG5vZGVzfSwge3R3byBsYXllcnMsIDUgbm9kZXMgaW4gZWFjaCBsYXllcn1cCmkuIFdoYXQgaGFwcGVucyB0byB0aGUgUk1TIGVycm9yIGZvciB0aGUgdHJhaW5pbmcgZGF0YSBhcyB0aGUgbnVtYmVyIG9mIGxheWVycyBhbmQgbm9kZXMgaW5jcmVhc2VzP1wKaWkuIFdoYXQgaGFwcGVucyB0byB0aGUgUk1TIGVycm9yIGZvciB0aGUgdmFsaWRhdGlvbiBkYXRhP1wKaWlpLiBDb21tZW50IG9uIHRoZSBhcHByb3ByaWF0ZSBudW1iZXIgb2YgbGF5ZXJzIGFuZCBub2RlcyBmb3IgdGhpcyBhcHBsaWNhdGlvbi4KCmBgYHtyIH0KIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMKbGlicmFyeShuZXVyYWxuZXQpCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkoZ3JpZEV4dHJhKQoKIyBTdGVwIDE6IFNwbGl0IHRoZSBkYXRhIGludG8gdHJhaW5pbmcgKDcwJSkgYW5kIHRlc3QgKDMwJSkgc2V0cwpzZXQuc2VlZCg1NDMyMSkKdHJhaW5JbmRleCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHNjYWxlZF9jYXJzJFByaWNlLCBwID0gMC43LCBsaXN0ID0gRkFMU0UpCnRyYWluX2RhdGEgPC0gc2NhbGVkX2NhcnNbdHJhaW5JbmRleCwgXQp0ZXN0X2RhdGEgPC0gc2NhbGVkX2NhcnNbLXRyYWluSW5kZXgsIF0KCiMgU3RlcCAyOiBEZWZpbmUgdGhlIGZvcm11bGEgZm9yIHRoZSBuZXVyYWwgbmV0d29yayBtb2RlbApmb3JtdWxhIDwtIFByaWNlIH4gQWdlXzA4XzA0ICsgS00gKyBIUCArIEF1dG9tYXRpYyArIERvb3JzICsgCiAgUXVhcnRlcmx5X1RheCArIE1mcl9HdWFyYW50ZWUgKyBHdWFyYW50ZWVfUGVyaW9kICsgQWlyY28gKyAKICBBdXRvbWF0aWNfYWlyY28gKyBDRF9QbGF5ZXIgKyBQb3dlcmVkX1dpbmRvd3MgKyBTcG9ydF9Nb2RlbCArIFRvd19CYXIKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBNb2RlbCAxOiBTaW5nbGUgaGlkZGVuIGxheWVyIHdpdGggNSBub2RlcwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCm5uX21vZGVsXzEgPC0gbmV1cmFsbmV0KGZvcm11bGEsIGRhdGEgPSB0cmFpbl9kYXRhLCBoaWRkZW4gPSA1LCBsaW5lYXIub3V0cHV0ID0gVFJVRSkKCiMgUGxvdCB0aGUgbmV1cmFsIG5ldHdvcmsgbW9kZWwKI3Bsb3Qobm5fbW9kZWxfMSkKIyBQbG90IHRoZSBuZXVyYWwgbmV0d29yayBtb2RlbCBmb3IgTW9kZWwgMQpwbG90KG5uX21vZGVsXzEsIG1haW4gPSAiTW9kZWwgMTogU2luZ2xlIEhpZGRlbiBMYXllciB3aXRoIDUgTm9kZXMiKQoKCiMgU3RlcCAzOiBNYWtlIHByZWRpY3Rpb25zIG9uIHRoZSB0cmFpbmluZyBhbmQgdGVzdCBkYXRhCnRyYWluX3ByZWRpY3Rpb25zXzEgPC0gcHJlZGljdChubl9tb2RlbF8xLCB0cmFpbl9kYXRhKQp0ZXN0X3ByZWRpY3Rpb25zXzEgPC0gcHJlZGljdChubl9tb2RlbF8xLCB0ZXN0X2RhdGEpCgojIFN0ZXAgNDogQ2FsY3VsYXRlIFJNU0UgZm9yIGJvdGggdGhlIHRyYWluaW5nIGFuZCB0ZXN0IGRhdGEgZm9yIE1vZGVsIDEKcm1zZV90cmFpbl8xIDwtIHNxcnQobWVhbigodHJhaW5fcHJlZGljdGlvbnNfMSAtIHRyYWluX2RhdGEkUHJpY2UpXjIpKQpybXNlX3Rlc3RfMSA8LSBzcXJ0KG1lYW4oKHRlc3RfcHJlZGljdGlvbnNfMSAtIHRlc3RfZGF0YSRQcmljZSleMikpCgojIFByaW50IFJNU0UgdmFsdWVzIGZvciBNb2RlbCAxCmNhdCgiTW9kZWwgMSAtIFNpbmdsZSBoaWRkZW4gbGF5ZXIgd2l0aCA1IG5vZGVzOlxuIikKY2F0KCJSTVNFIG9uIHRyYWluaW5nIGRhdGE6Iiwgcm91bmQocm1zZV90cmFpbl8xLCAyKSwgIlxuIikKY2F0KCJSTVNFIG9uIHRlc3QgZGF0YToiLCByb3VuZChybXNlX3Rlc3RfMSwgMiksICJcblxuIikKCgpgYGAKCmBgYHtyIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTYsIGVjaG89VFJVRX0KCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBNb2RlbCAyOiBUd28gaGlkZGVuIGxheWVycywgNSBub2RlcyBpbiBlYWNoIGxheWVyCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0Kbm5fbW9kZWxfMiA8LSBuZXVyYWxuZXQoZm9ybXVsYSwgZGF0YSA9IHRyYWluX2RhdGEsIGhpZGRlbiA9IGMoNSwgNSksIGxpbmVhci5vdXRwdXQgPSBUUlVFKQoKIyBQbG90IHRoZSBuZXVyYWwgbmV0d29yayBtb2RlbCBmb3IgTW9kZWwgMgpwbG90KG5uX21vZGVsXzIsIG1haW4gPSAiTW9kZWwgMjogVHdvIEhpZGRlbiBMYXllcnMgd2l0aCA1IE5vZGVzIEVhY2giKQoKIyBTdGVwIDM6IE1ha2UgcHJlZGljdGlvbnMgb24gdGhlIHRyYWluaW5nIGFuZCB0ZXN0IGRhdGEKdHJhaW5fcHJlZGljdGlvbnNfMiA8LSBwcmVkaWN0KG5uX21vZGVsXzIsIHRyYWluX2RhdGEpCnRlc3RfcHJlZGljdGlvbnNfMiA8LSBwcmVkaWN0KG5uX21vZGVsXzIsIHRlc3RfZGF0YSkKCiMgU3RlcCA0OiBDYWxjdWxhdGUgUk1TRSBmb3IgYm90aCB0aGUgdHJhaW5pbmcgYW5kIHRlc3QgZGF0YSBmb3IgTW9kZWwgMgpybXNlX3RyYWluXzIgPC0gc3FydChtZWFuKCh0cmFpbl9wcmVkaWN0aW9uc18yIC0gdHJhaW5fZGF0YSRQcmljZSleMikpCnJtc2VfdGVzdF8yIDwtIHNxcnQobWVhbigodGVzdF9wcmVkaWN0aW9uc18yIC0gdGVzdF9kYXRhJFByaWNlKV4yKSkKCiMgUHJpbnQgUk1TRSB2YWx1ZXMgZm9yIE1vZGVsIDIKY2F0KCJNb2RlbCAyIC0gVHdvIGhpZGRlbiBsYXllcnMgd2l0aCA1IG5vZGVzIGVhY2g6XG4iKQpjYXQoIlJNU0Ugb24gdHJhaW5pbmcgZGF0YToiLCByb3VuZChybXNlX3RyYWluXzIsIDIpLCAiXG4iKQpjYXQoIlJNU0Ugb24gdGVzdCBkYXRhOiIsIHJvdW5kKHJtc2VfdGVzdF8yLCAyKSwgIlxuXG4iKQoKCmBgYAo=