This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Cmd+Shift+Enter.


Load the Obesity Dataset

Project Overview: Predicting Weight Based on Lifestyle Factors

For our project, we chose the Obesity Levels dataset from the UCI Machine Learning Repository. This dataset combines real and synthetic data, capturing details about people’s eating habits, physical activity, and biological traits (like age, gender, and height). Our main goal is to predict weight—a continuous variable—using these different lifestyle and health factors.

Data Preparation & Initial Insights

Before diving into modeling, we cleaned and prepared the data by:

Converting text-based (categorical) variables into numerical factors for analysis. Ensuring no missing values were present that could skew results. Splitting the data into an 80% training set (to build models) and a 20% testing set (to evaluate their performance fairly).

Key observations from early exploration:

Variables include age, height, dietary patterns (e.g., frequent high-calorie food consumption), and activity levels (exercise frequency, screen time). Weight is our target, analyzed as a continuous value—ideal for regression techniques. The dataset was already quite clean, requiring minimal adjustments before modeling.

# Load necessary libraries
library(tidyverse)
library(rpart)
library(rpart.plot)
library(caret)
library(knitr)  # for neat tables

# Read the dataset
obesity_data <- read.csv("~/Desktop/ObesityDataSet_raw_and_data_sinthetic.csv")

# View first few rows
head(obesity_data)

# Calculate BMI
obesity_data$BMI <- obesity_data$Weight / (obesity_data$Height^2)

# Remove NObeyesdad (target leakage)
obesity_data <- obesity_data %>% select(-NObeyesdad)

Data Preprocessing

# Check variable types
str(obesity_data)
'data.frame':   2111 obs. of  17 variables:
 $ Gender                        : chr  "Female" "Female" "Male" "Male" ...
 $ Age                           : num  21 21 23 27 22 29 23 22 24 22 ...
 $ Height                        : num  1.62 1.52 1.8 1.8 1.78 1.62 1.5 1.64 1.78 1.72 ...
 $ Weight                        : num  64 56 77 87 89.8 53 55 53 64 68 ...
 $ family_history_with_overweight: chr  "yes" "yes" "yes" "no" ...
 $ FAVC                          : chr  "no" "no" "no" "no" ...
 $ FCVC                          : num  2 3 2 3 2 2 3 2 3 2 ...
 $ NCP                           : num  3 3 3 3 1 3 3 3 3 3 ...
 $ CAEC                          : chr  "Sometimes" "Sometimes" "Sometimes" "Sometimes" ...
 $ SMOKE                         : chr  "no" "yes" "no" "no" ...
 $ CH2O                          : num  2 3 2 2 2 2 2 2 2 2 ...
 $ SCC                           : chr  "no" "yes" "no" "no" ...
 $ FAF                           : num  0 3 2 2 0 0 1 3 1 1 ...
 $ TUE                           : num  1 0 1 0 0 0 0 0 1 1 ...
 $ CALC                          : chr  "no" "Sometimes" "Frequently" "Frequently" ...
 $ MTRANS                        : chr  "Public_Transportation" "Public_Transportation" "Public_Transportation" "Walking" ...
 $ BMI                           : num  24.4 24.2 23.8 26.9 28.3 ...
# Convert character variables to factors
obesity_data <- obesity_data %>% 
  mutate(across(where(is.character), as.factor))

# Remove any missing values
obesity_data <- na.omit(obesity_data)

# Split the data into training and testing sets (80/20 split)
set.seed(123)
train_index <- createDataPartition(obesity_data$BMI, p = 0.8, list = FALSE)
train_data <- obesity_data[train_index, ]
test_data <- obesity_data[-train_index, ]

First, we checked the structure of our dataset (obesity_data) to see what types of variables we were working with—like numbers, categories, or text.

Since some columns were stored as text (characters), we converted them into factors—a format R understands better for statistical modeling. This helps the models recognize categories (like “Male” or “Female”) properly.

Next, we made sure our data was clean and complete by removing any rows with missing values. This avoids errors or biased results later on.

Finally, we split the data into two parts:

Training set (80%): Used to build and train our models. Testing set (20%): Reserved to check how well the models perform on unseen data. We set a random seed (set.seed(123)) to ensure this split is reproducible—meaning anyone running the code gets the same training/testing groups for fair comparisons.

Build a Shallow Decision Tree

# Train a shallow decision tree to predict BMI
tree_model <- rpart(BMI ~ ., 
                    data = train_data, 
                    method = "anova", 
                    control = rpart.control(maxdepth = 3, cp = 0.01))

# Plot the decision tree
rpart.plot(tree_model,
           type = 4,
           extra = 101,
           fallen.leaves = TRUE,
           box.palette = "Blues",
           shadow.col = "gray")

We started by training a basic Decision Tree model to predict Body Mass Index (BMI) using all other available variables in the dataset (excluding NObeyesdad, which was removed to prevent target leakage).

To keep the model intentionally simple and interpretable, we restricted its complexity in two ways:

We limited the maximum depth to 3 levels to avoid overfitting and unnecessary complexity. We set a relatively high complexity parameter (cp = 0.01) to encourage pruning of less informative splits. Since BMI is a continuous variable, we used the ‘anova’ method in the rpart() function to indicate regression rather than classification.

For visualization, we created a clean and informative tree plot that shows:

The hierarchical decision points (splits) based on the most influential predictors The predicted BMI values at each terminal node (leaf) A color gradient (using a blue palette) to visually indicate different prediction ranges Subtle shadows to improve readability and distinguish branches

This restrained approach gives us a model that is:

Easy to interpret and explain to non-technical stakeholders Quick to train and computationally efficient Provides a solid baseline for comparison with more complex models such as XGBoost The tree visualization acts as both a diagnostic tool (to assess if splits make logical sense) and a communication tool (to help explain how input factors influence BMI outcomes).

Evaluate the Model

# Predict on training and test data
train_pred <- predict(tree_model, newdata = train_data)
test_pred <- predict(tree_model, newdata = test_data)

# Calculate RMSE (Root Mean Squared Error)
train_rmse <- sqrt(mean((train_pred - train_data$BMI)^2))
test_rmse <- sqrt(mean((test_pred - test_data$BMI)^2))

# Print RMSE neatly
cat("Training RMSE:", round(train_rmse, 2), "\n")
Training RMSE: 2.51 
cat("Testing RMSE:", round(test_rmse, 2), "\n")
Testing RMSE: 2.65 

We used our decision tree to predict BMI in two scenarios:

For individuals in the training set (data the model had already seen) For individuals in the testing set (new, unseen data)

To evaluate how accurate the model’s predictions were, we calculated the Root Mean Squared Error (RMSE). RMSE gives us one number that summarizes the typical prediction error — smaller values indicate more accurate predictions.

The steps were as follows:

First, we generated predictions for both the training and testing sets.

Then, we calculated RMSE by:

  1. Taking the difference between the predicted and actual BMI values
  2. Squaring those differences
  3. Averaging them
  4. Taking the square root of that average

Finally, we printed the Training RMSE and Testing RMSE:

Training RMSE shows how well the model fits the data it was trained on. Testing RMSE shows how well the model performs on new data, which is a better indicator of real-world performance. This evaluation helps us understand whether the model is overfitting, underfitting, or performing as expected, and gives us a sense of how accurate its BMI predictions are in practice.

Analyze Important Features

# Check variable importance
importance <- data.frame(
  Variable = names(tree_model$variable.importance),
  Importance = as.numeric(tree_model$variable.importance)
)

# Display neatly
kable(importance, caption = "Variable Importance in Decision Tree Model")
Variable Importance in Decision Tree Model
Variable Importance
Weight 92294.2396
Height 18200.5106
Age 16343.7744
FCVC 14226.0842
Gender 8125.2846
CH2O 7416.5660
CAEC 3835.4491
family_history_with_overweight 3096.4359
FAF 2365.9833
NCP 1446.6894
FAVC 316.7405

Understanding What Really Affects Weight Predictions

After building our BMI prediction model, we wanted to identify which variables had the most influence on the predictions. To do this, we extracted the variable importance scores from the decision tree model.

These scores reflect how frequently and effectively each variable was used to split the data in the tree. Variables that played a larger role in creating accurate predictions received higher scores.

We then organized this information into a clear table with two columns:

Variable: The name of each predictor (e.g., Height, Age, FCVC) Importance: A numeric score indicating how influential the variable was in the model

Using the kable() function, we displayed the table in a professional format and added a clear caption for context. This made it easy to compare which features had the most impact.

This analysis helps answer key questions such as:

  1. Which lifestyle or demographic factors most influence BMI?
  2. Are there specific variables we should focus on in future data collection?
  3. Do the most important predictors align with our expectations and domain knowledge?

Conclusion:

Methods - Describe building a shallow Decision Tree with depth = 3, reason for balance between interpretability and prediction. Results - Report Training RMSE, Testing RMSE, include the Decision Tree plot (Figure 1), and Variable Importance (Table 1). Discussion - Discuss Decision Trees being simple to interpret but sometimes underfitting complex patterns.

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gCgpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ21kK1NoaWZ0K0VudGVyKi4gCgotLS0tLS0tLQoKIyBMb2FkIHRoZSBPYmVzaXR5IERhdGFzZXQKCgojIFByb2plY3QgT3ZlcnZpZXc6IFByZWRpY3RpbmcgV2VpZ2h0IEJhc2VkIG9uIExpZmVzdHlsZSBGYWN0b3JzCgpGb3Igb3VyIHByb2plY3QsIHdlIGNob3NlIHRoZSBPYmVzaXR5IExldmVscyBkYXRhc2V0IGZyb20gdGhlIFVDSSBNYWNoaW5lIExlYXJuaW5nIFJlcG9zaXRvcnkuIFRoaXMgZGF0YXNldCBjb21iaW5lcyByZWFsIGFuZCBzeW50aGV0aWMgZGF0YSwgY2FwdHVyaW5nIGRldGFpbHMgYWJvdXQgcGVvcGxl4oCZcyBlYXRpbmcgaGFiaXRzLCBwaHlzaWNhbCBhY3Rpdml0eSwgYW5kIGJpb2xvZ2ljYWwgdHJhaXRzIChsaWtlIGFnZSwgZ2VuZGVyLCBhbmQgaGVpZ2h0KS4gT3VyIG1haW4gZ29hbCBpcyB0byBwcmVkaWN0IHdlaWdodOKAlGEgY29udGludW91cyB2YXJpYWJsZeKAlHVzaW5nIHRoZXNlIGRpZmZlcmVudCBsaWZlc3R5bGUgYW5kIGhlYWx0aCBmYWN0b3JzLgoKIyBEYXRhIFByZXBhcmF0aW9uICYgSW5pdGlhbCBJbnNpZ2h0cwpCZWZvcmUgZGl2aW5nIGludG8gbW9kZWxpbmcsIHdlIGNsZWFuZWQgYW5kIHByZXBhcmVkIHRoZSBkYXRhIGJ5OgoKQ29udmVydGluZyB0ZXh0LWJhc2VkIChjYXRlZ29yaWNhbCkgdmFyaWFibGVzIGludG8gbnVtZXJpY2FsIGZhY3RvcnMgZm9yIGFuYWx5c2lzLgpFbnN1cmluZyBubyBtaXNzaW5nIHZhbHVlcyB3ZXJlIHByZXNlbnQgdGhhdCBjb3VsZCBza2V3IHJlc3VsdHMuClNwbGl0dGluZyB0aGUgZGF0YSBpbnRvIGFuIDgwJSB0cmFpbmluZyBzZXQgKHRvIGJ1aWxkIG1vZGVscykgYW5kIGEgMjAlIHRlc3Rpbmcgc2V0ICh0byBldmFsdWF0ZSB0aGVpciBwZXJmb3JtYW5jZSBmYWlybHkpLgoKIyBLZXkgb2JzZXJ2YXRpb25zIGZyb20gZWFybHkgZXhwbG9yYXRpb246CgpWYXJpYWJsZXMgaW5jbHVkZSBhZ2UsIGhlaWdodCwgZGlldGFyeSBwYXR0ZXJucyAoZS5nLiwgZnJlcXVlbnQgaGlnaC1jYWxvcmllIGZvb2QgY29uc3VtcHRpb24pLCBhbmQgYWN0aXZpdHkgbGV2ZWxzIChleGVyY2lzZSBmcmVxdWVuY3ksIHNjcmVlbiB0aW1lKS4KV2VpZ2h0IGlzIG91ciB0YXJnZXQsIGFuYWx5emVkIGFzIGEgY29udGludW91cyB2YWx1ZeKAlGlkZWFsIGZvciByZWdyZXNzaW9uIHRlY2huaXF1ZXMuClRoZSBkYXRhc2V0IHdhcyBhbHJlYWR5IHF1aXRlIGNsZWFuLCByZXF1aXJpbmcgbWluaW1hbCBhZGp1c3RtZW50cyBiZWZvcmUgbW9kZWxpbmcuCgpgYGB7cn0KIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocnBhcnQpCmxpYnJhcnkocnBhcnQucGxvdCkKbGlicmFyeShjYXJldCkKbGlicmFyeShrbml0cikgICMgZm9yIG5lYXQgdGFibGVzCgojIFJlYWQgdGhlIGRhdGFzZXQKb2Jlc2l0eV9kYXRhIDwtIHJlYWQuY3N2KCJ+L0Rlc2t0b3AvT2Jlc2l0eURhdGFTZXRfcmF3X2FuZF9kYXRhX3NpbnRoZXRpYy5jc3YiKQoKIyBWaWV3IGZpcnN0IGZldyByb3dzCmhlYWQob2Jlc2l0eV9kYXRhKQoKIyBDYWxjdWxhdGUgQk1JCm9iZXNpdHlfZGF0YSRCTUkgPC0gb2Jlc2l0eV9kYXRhJFdlaWdodCAvIChvYmVzaXR5X2RhdGEkSGVpZ2h0XjIpCgojIFJlbW92ZSBOT2JleWVzZGFkICh0YXJnZXQgbGVha2FnZSkKb2Jlc2l0eV9kYXRhIDwtIG9iZXNpdHlfZGF0YSAlPiUgc2VsZWN0KC1OT2JleWVzZGFkKQpgYGAKCgojIERhdGEgUHJlcHJvY2Vzc2luZwoKYGBge3J9CiMgQ2hlY2sgdmFyaWFibGUgdHlwZXMKc3RyKG9iZXNpdHlfZGF0YSkKCiMgQ29udmVydCBjaGFyYWN0ZXIgdmFyaWFibGVzIHRvIGZhY3RvcnMKb2Jlc2l0eV9kYXRhIDwtIG9iZXNpdHlfZGF0YSAlPiUgCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5jaGFyYWN0ZXIpLCBhcy5mYWN0b3IpKQoKIyBSZW1vdmUgYW55IG1pc3NpbmcgdmFsdWVzCm9iZXNpdHlfZGF0YSA8LSBuYS5vbWl0KG9iZXNpdHlfZGF0YSkKCiMgU3BsaXQgdGhlIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzICg4MC8yMCBzcGxpdCkKc2V0LnNlZWQoMTIzKQp0cmFpbl9pbmRleCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKG9iZXNpdHlfZGF0YSRCTUksIHAgPSAwLjgsIGxpc3QgPSBGQUxTRSkKdHJhaW5fZGF0YSA8LSBvYmVzaXR5X2RhdGFbdHJhaW5faW5kZXgsIF0KdGVzdF9kYXRhIDwtIG9iZXNpdHlfZGF0YVstdHJhaW5faW5kZXgsIF0KYGBgCgpGaXJzdCwgd2UgY2hlY2tlZCB0aGUgc3RydWN0dXJlIG9mIG91ciBkYXRhc2V0IChvYmVzaXR5X2RhdGEpIHRvIHNlZSB3aGF0IHR5cGVzIG9mIHZhcmlhYmxlcyB3ZSB3ZXJlIHdvcmtpbmcgd2l0aOKAlGxpa2UgbnVtYmVycywgY2F0ZWdvcmllcywgb3IgdGV4dC4KClNpbmNlIHNvbWUgY29sdW1ucyB3ZXJlIHN0b3JlZCBhcyB0ZXh0IChjaGFyYWN0ZXJzKSwgd2UgY29udmVydGVkIHRoZW0gaW50byBmYWN0b3Jz4oCUYSBmb3JtYXQgUiB1bmRlcnN0YW5kcyBiZXR0ZXIgZm9yIHN0YXRpc3RpY2FsIG1vZGVsaW5nLiBUaGlzIGhlbHBzIHRoZSBtb2RlbHMgcmVjb2duaXplIGNhdGVnb3JpZXMgKGxpa2UgIk1hbGUiIG9yICJGZW1hbGUiKSBwcm9wZXJseS4KCk5leHQsIHdlIG1hZGUgc3VyZSBvdXIgZGF0YSB3YXMgY2xlYW4gYW5kIGNvbXBsZXRlIGJ5IHJlbW92aW5nIGFueSByb3dzIHdpdGggbWlzc2luZyB2YWx1ZXMuIFRoaXMgYXZvaWRzIGVycm9ycyBvciBiaWFzZWQgcmVzdWx0cyBsYXRlciBvbi4KCkZpbmFsbHksIHdlIHNwbGl0IHRoZSBkYXRhIGludG8gdHdvIHBhcnRzOgoKVHJhaW5pbmcgc2V0ICg4MCUpOiBVc2VkIHRvIGJ1aWxkIGFuZCB0cmFpbiBvdXIgbW9kZWxzLgpUZXN0aW5nIHNldCAoMjAlKTogUmVzZXJ2ZWQgdG8gY2hlY2sgaG93IHdlbGwgdGhlIG1vZGVscyBwZXJmb3JtIG9uIHVuc2VlbiBkYXRhLgpXZSBzZXQgYSByYW5kb20gc2VlZCAoc2V0LnNlZWQoMTIzKSkgdG8gZW5zdXJlIHRoaXMgc3BsaXQgaXMgcmVwcm9kdWNpYmxl4oCUbWVhbmluZyBhbnlvbmUgcnVubmluZyB0aGUgY29kZSBnZXRzIHRoZSBzYW1lIHRyYWluaW5nL3Rlc3RpbmcgZ3JvdXBzIGZvciBmYWlyIGNvbXBhcmlzb25zLgoKCiMgQnVpbGQgYSBTaGFsbG93IERlY2lzaW9uIFRyZWUKCmBgYHtyfQojIFRyYWluIGEgc2hhbGxvdyBkZWNpc2lvbiB0cmVlIHRvIHByZWRpY3QgQk1JCnRyZWVfbW9kZWwgPC0gcnBhcnQoQk1JIH4gLiwgCiAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluX2RhdGEsIAogICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJhbm92YSIsIAogICAgICAgICAgICAgICAgICAgIGNvbnRyb2wgPSBycGFydC5jb250cm9sKG1heGRlcHRoID0gMywgY3AgPSAwLjAxKSkKCiMgUGxvdCB0aGUgZGVjaXNpb24gdHJlZQpycGFydC5wbG90KHRyZWVfbW9kZWwsCiAgICAgICAgICAgdHlwZSA9IDQsCiAgICAgICAgICAgZXh0cmEgPSAxMDEsCiAgICAgICAgICAgZmFsbGVuLmxlYXZlcyA9IFRSVUUsCiAgICAgICAgICAgYm94LnBhbGV0dGUgPSAiQmx1ZXMiLAogICAgICAgICAgIHNoYWRvdy5jb2wgPSAiZ3JheSIpCmBgYApXZSBzdGFydGVkIGJ5IHRyYWluaW5nIGEgYmFzaWMgRGVjaXNpb24gVHJlZSBtb2RlbCB0byBwcmVkaWN0IEJvZHkgTWFzcyBJbmRleCAoQk1JKSB1c2luZyBhbGwgb3RoZXIgYXZhaWxhYmxlIHZhcmlhYmxlcyBpbiB0aGUgZGF0YXNldCAoZXhjbHVkaW5nIE5PYmV5ZXNkYWQsIHdoaWNoIHdhcyByZW1vdmVkIHRvIHByZXZlbnQgdGFyZ2V0IGxlYWthZ2UpLgoKVG8ga2VlcCB0aGUgbW9kZWwgaW50ZW50aW9uYWxseSBzaW1wbGUgYW5kIGludGVycHJldGFibGUsIHdlIHJlc3RyaWN0ZWQgaXRzIGNvbXBsZXhpdHkgaW4gdHdvIHdheXM6CgpXZSBsaW1pdGVkIHRoZSBtYXhpbXVtIGRlcHRoIHRvIDMgbGV2ZWxzIHRvIGF2b2lkIG92ZXJmaXR0aW5nIGFuZCB1bm5lY2Vzc2FyeSBjb21wbGV4aXR5LgpXZSBzZXQgYSByZWxhdGl2ZWx5IGhpZ2ggY29tcGxleGl0eSBwYXJhbWV0ZXIgKGNwID0gMC4wMSkgdG8gZW5jb3VyYWdlIHBydW5pbmcgb2YgbGVzcyBpbmZvcm1hdGl2ZSBzcGxpdHMuClNpbmNlIEJNSSBpcyBhIGNvbnRpbnVvdXMgdmFyaWFibGUsIHdlIHVzZWQgdGhlICdhbm92YScgbWV0aG9kIGluIHRoZSBycGFydCgpIGZ1bmN0aW9uIHRvIGluZGljYXRlIHJlZ3Jlc3Npb24gcmF0aGVyIHRoYW4gY2xhc3NpZmljYXRpb24uCgpGb3IgdmlzdWFsaXphdGlvbiwgd2UgY3JlYXRlZCBhIGNsZWFuIGFuZCBpbmZvcm1hdGl2ZSB0cmVlIHBsb3QgdGhhdCBzaG93czoKClRoZSBoaWVyYXJjaGljYWwgZGVjaXNpb24gcG9pbnRzIChzcGxpdHMpIGJhc2VkIG9uIHRoZSBtb3N0IGluZmx1ZW50aWFsIHByZWRpY3RvcnMKVGhlIHByZWRpY3RlZCBCTUkgdmFsdWVzIGF0IGVhY2ggdGVybWluYWwgbm9kZSAobGVhZikKQSBjb2xvciBncmFkaWVudCAodXNpbmcgYSBibHVlIHBhbGV0dGUpIHRvIHZpc3VhbGx5IGluZGljYXRlIGRpZmZlcmVudCBwcmVkaWN0aW9uIHJhbmdlcwpTdWJ0bGUgc2hhZG93cyB0byBpbXByb3ZlIHJlYWRhYmlsaXR5IGFuZCBkaXN0aW5ndWlzaCBicmFuY2hlcwoKVGhpcyByZXN0cmFpbmVkIGFwcHJvYWNoIGdpdmVzIHVzIGEgbW9kZWwgdGhhdCBpczoKCkVhc3kgdG8gaW50ZXJwcmV0IGFuZCBleHBsYWluIHRvIG5vbi10ZWNobmljYWwgc3Rha2Vob2xkZXJzClF1aWNrIHRvIHRyYWluIGFuZCBjb21wdXRhdGlvbmFsbHkgZWZmaWNpZW50ClByb3ZpZGVzIGEgc29saWQgYmFzZWxpbmUgZm9yIGNvbXBhcmlzb24gd2l0aCBtb3JlIGNvbXBsZXggbW9kZWxzIHN1Y2ggYXMgWEdCb29zdApUaGUgdHJlZSB2aXN1YWxpemF0aW9uIGFjdHMgYXMgYm90aCBhIGRpYWdub3N0aWMgdG9vbCAodG8gYXNzZXNzIGlmIHNwbGl0cyBtYWtlIGxvZ2ljYWwgc2Vuc2UpIGFuZCBhIGNvbW11bmljYXRpb24gdG9vbCAodG8gaGVscCBleHBsYWluIGhvdyBpbnB1dCBmYWN0b3JzIGluZmx1ZW5jZSBCTUkgb3V0Y29tZXMpLgoKCiMgRXZhbHVhdGUgdGhlIE1vZGVsCgpgYGB7cn0KIyBQcmVkaWN0IG9uIHRyYWluaW5nIGFuZCB0ZXN0IGRhdGEKdHJhaW5fcHJlZCA8LSBwcmVkaWN0KHRyZWVfbW9kZWwsIG5ld2RhdGEgPSB0cmFpbl9kYXRhKQp0ZXN0X3ByZWQgPC0gcHJlZGljdCh0cmVlX21vZGVsLCBuZXdkYXRhID0gdGVzdF9kYXRhKQoKIyBDYWxjdWxhdGUgUk1TRSAoUm9vdCBNZWFuIFNxdWFyZWQgRXJyb3IpCnRyYWluX3Jtc2UgPC0gc3FydChtZWFuKCh0cmFpbl9wcmVkIC0gdHJhaW5fZGF0YSRCTUkpXjIpKQp0ZXN0X3Jtc2UgPC0gc3FydChtZWFuKCh0ZXN0X3ByZWQgLSB0ZXN0X2RhdGEkQk1JKV4yKSkKCiMgUHJpbnQgUk1TRSBuZWF0bHkKY2F0KCJUcmFpbmluZyBSTVNFOiIsIHJvdW5kKHRyYWluX3Jtc2UsIDIpLCAiXG4iKQpjYXQoIlRlc3RpbmcgUk1TRToiLCByb3VuZCh0ZXN0X3Jtc2UsIDIpLCAiXG4iKQpgYGAKCldlIHVzZWQgb3VyIGRlY2lzaW9uIHRyZWUgdG8gcHJlZGljdCBCTUkgaW4gdHdvIHNjZW5hcmlvczoKCkZvciBpbmRpdmlkdWFscyBpbiB0aGUgdHJhaW5pbmcgc2V0IChkYXRhIHRoZSBtb2RlbCBoYWQgYWxyZWFkeSBzZWVuKQpGb3IgaW5kaXZpZHVhbHMgaW4gdGhlIHRlc3Rpbmcgc2V0IChuZXcsIHVuc2VlbiBkYXRhKQoKVG8gZXZhbHVhdGUgaG93IGFjY3VyYXRlIHRoZSBtb2RlbOKAmXMgcHJlZGljdGlvbnMgd2VyZSwgd2UgY2FsY3VsYXRlZCB0aGUgUm9vdCBNZWFuIFNxdWFyZWQgRXJyb3IgKFJNU0UpLiBSTVNFIGdpdmVzIHVzIG9uZSBudW1iZXIgdGhhdCBzdW1tYXJpemVzIHRoZSB0eXBpY2FsIHByZWRpY3Rpb24gZXJyb3Ig4oCUIHNtYWxsZXIgdmFsdWVzIGluZGljYXRlIG1vcmUgYWNjdXJhdGUgcHJlZGljdGlvbnMuCgpUaGUgc3RlcHMgd2VyZSBhcyBmb2xsb3dzOgoKRmlyc3QsIHdlIGdlbmVyYXRlZCBwcmVkaWN0aW9ucyBmb3IgYm90aCB0aGUgdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cy4KClRoZW4sIHdlIGNhbGN1bGF0ZWQgUk1TRSBieToKCjEuIFRha2luZyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBwcmVkaWN0ZWQgYW5kIGFjdHVhbCBCTUkgdmFsdWVzCjIuIFNxdWFyaW5nIHRob3NlIGRpZmZlcmVuY2VzCjMuIEF2ZXJhZ2luZyB0aGVtCjQuIFRha2luZyB0aGUgc3F1YXJlIHJvb3Qgb2YgdGhhdCBhdmVyYWdlCgpGaW5hbGx5LCB3ZSBwcmludGVkIHRoZSBUcmFpbmluZyBSTVNFIGFuZCBUZXN0aW5nIFJNU0U6CgpUcmFpbmluZyBSTVNFIHNob3dzIGhvdyB3ZWxsIHRoZSBtb2RlbCBmaXRzIHRoZSBkYXRhIGl0IHdhcyB0cmFpbmVkIG9uLgpUZXN0aW5nIFJNU0Ugc2hvd3MgaG93IHdlbGwgdGhlIG1vZGVsIHBlcmZvcm1zIG9uIG5ldyBkYXRhLCB3aGljaCBpcyBhIGJldHRlciBpbmRpY2F0b3Igb2YgcmVhbC13b3JsZCBwZXJmb3JtYW5jZS4KVGhpcyBldmFsdWF0aW9uIGhlbHBzIHVzIHVuZGVyc3RhbmQgd2hldGhlciB0aGUgbW9kZWwgaXMgb3ZlcmZpdHRpbmcsIHVuZGVyZml0dGluZywgb3IgcGVyZm9ybWluZyBhcyBleHBlY3RlZCwgYW5kIGdpdmVzIHVzIGEgc2Vuc2Ugb2YgaG93IGFjY3VyYXRlIGl0cyBCTUkgcHJlZGljdGlvbnMgYXJlIGluIHByYWN0aWNlLgoKCiMgQW5hbHl6ZSBJbXBvcnRhbnQgRmVhdHVyZXMKCmBgYHtyfQojIENoZWNrIHZhcmlhYmxlIGltcG9ydGFuY2UKaW1wb3J0YW5jZSA8LSBkYXRhLmZyYW1lKAogIFZhcmlhYmxlID0gbmFtZXModHJlZV9tb2RlbCR2YXJpYWJsZS5pbXBvcnRhbmNlKSwKICBJbXBvcnRhbmNlID0gYXMubnVtZXJpYyh0cmVlX21vZGVsJHZhcmlhYmxlLmltcG9ydGFuY2UpCikKCiMgRGlzcGxheSBuZWF0bHkKa2FibGUoaW1wb3J0YW5jZSwgY2FwdGlvbiA9ICJWYXJpYWJsZSBJbXBvcnRhbmNlIGluIERlY2lzaW9uIFRyZWUgTW9kZWwiKQpgYGAKCiMgVW5kZXJzdGFuZGluZyBXaGF0IFJlYWxseSBBZmZlY3RzIFdlaWdodCBQcmVkaWN0aW9ucwoKQWZ0ZXIgYnVpbGRpbmcgb3VyIEJNSSBwcmVkaWN0aW9uIG1vZGVsLCB3ZSB3YW50ZWQgdG8gaWRlbnRpZnkgd2hpY2ggdmFyaWFibGVzIGhhZCB0aGUgbW9zdCBpbmZsdWVuY2Ugb24gdGhlIHByZWRpY3Rpb25zLiBUbyBkbyB0aGlzLCB3ZSBleHRyYWN0ZWQgdGhlIHZhcmlhYmxlIGltcG9ydGFuY2Ugc2NvcmVzIGZyb20gdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwuCgpUaGVzZSBzY29yZXMgcmVmbGVjdCBob3cgZnJlcXVlbnRseSBhbmQgZWZmZWN0aXZlbHkgZWFjaCB2YXJpYWJsZSB3YXMgdXNlZCB0byBzcGxpdCB0aGUgZGF0YSBpbiB0aGUgdHJlZS4gVmFyaWFibGVzIHRoYXQgcGxheWVkIGEgbGFyZ2VyIHJvbGUgaW4gY3JlYXRpbmcgYWNjdXJhdGUgcHJlZGljdGlvbnMgcmVjZWl2ZWQgaGlnaGVyIHNjb3Jlcy4KCldlIHRoZW4gb3JnYW5pemVkIHRoaXMgaW5mb3JtYXRpb24gaW50byBhIGNsZWFyIHRhYmxlIHdpdGggdHdvIGNvbHVtbnM6CgpWYXJpYWJsZTogVGhlIG5hbWUgb2YgZWFjaCBwcmVkaWN0b3IgKGUuZy4sIEhlaWdodCwgQWdlLCBGQ1ZDKQpJbXBvcnRhbmNlOiBBIG51bWVyaWMgc2NvcmUgaW5kaWNhdGluZyBob3cgaW5mbHVlbnRpYWwgdGhlIHZhcmlhYmxlIHdhcyBpbiB0aGUgbW9kZWwKClVzaW5nIHRoZSBrYWJsZSgpIGZ1bmN0aW9uLCB3ZSBkaXNwbGF5ZWQgdGhlIHRhYmxlIGluIGEgcHJvZmVzc2lvbmFsIGZvcm1hdCBhbmQgYWRkZWQgYSBjbGVhciBjYXB0aW9uIGZvciBjb250ZXh0LiBUaGlzIG1hZGUgaXQgZWFzeSB0byBjb21wYXJlIHdoaWNoIGZlYXR1cmVzIGhhZCB0aGUgbW9zdCBpbXBhY3QuCgpUaGlzIGFuYWx5c2lzIGhlbHBzIGFuc3dlciBrZXkgcXVlc3Rpb25zIHN1Y2ggYXM6CgoxLiBXaGljaCBsaWZlc3R5bGUgb3IgZGVtb2dyYXBoaWMgZmFjdG9ycyBtb3N0IGluZmx1ZW5jZSBCTUk/CjIuIEFyZSB0aGVyZSBzcGVjaWZpYyB2YXJpYWJsZXMgd2Ugc2hvdWxkIGZvY3VzIG9uIGluIGZ1dHVyZSBkYXRhIGNvbGxlY3Rpb24/CjMuIERvIHRoZSBtb3N0IGltcG9ydGFudCBwcmVkaWN0b3JzIGFsaWduIHdpdGggb3VyIGV4cGVjdGF0aW9ucyBhbmQgZG9tYWluIGtub3dsZWRnZT8KCgojIENvbmNsdXNpb246CgpNZXRob2RzCS0gRGVzY3JpYmUgYnVpbGRpbmcgYSBzaGFsbG93IERlY2lzaW9uIFRyZWUgd2l0aCBkZXB0aCA9IDMsIHJlYXNvbiBmb3IgYmFsYW5jZSBiZXR3ZWVuIGludGVycHJldGFiaWxpdHkgYW5kIHByZWRpY3Rpb24uClJlc3VsdHMJLSBSZXBvcnQgVHJhaW5pbmcgUk1TRSwgVGVzdGluZyBSTVNFLCBpbmNsdWRlIHRoZSBEZWNpc2lvbiBUcmVlIHBsb3QgKEZpZ3VyZSAxKSwgYW5kIFZhcmlhYmxlIEltcG9ydGFuY2UgKFRhYmxlIDEpLgpEaXNjdXNzaW9uCS0gRGlzY3VzcyBEZWNpc2lvbiBUcmVlcyBiZWluZyBzaW1wbGUgdG8gaW50ZXJwcmV0IGJ1dCBzb21ldGltZXMgdW5kZXJmaXR0aW5nIGNvbXBsZXggcGF0dGVybnMuCgoK