1 Introduction

This project implements unsupervised machine learning algorithms—Principal Component Analysis (PCA) and Hierarchical Clustering (HAC)—for feature extraction. The goal is to discover meaningful patterns and transform raw data into more informative representations without relying on a labeled response variable. These new features will later be incorporated into a binary classification model to assess the benefits of unsupervised feature engineering.

1.1 Data Set Description

The dataset used for this analysis is the Mental Health and Social Media Balance Dataset, sourced from Kaggle.com. The dataset contains 500 observations and 10 features, focusing on the relationship between social media usage habits and mental well-being metrics.

The dataset includes the required components for this project: * Numerical Variables: Several highly correlated numerical variables (e.g., screen time, stress, sleep quality) are present for PCA. * Binary Categorical Variable: A binary target is engineered from the Happiness_Index(1-10) for the later classification task.

2 Setup and Data Loading

This section loads the necessary R libraries and the dataset.

# Load the dataset using base R function
setwd("/Users/jeffery/Library/Mobile Documents/com~apple~CloudDocs/Documents/Documents - jMacP/WCUPA/Classes/Fall 2025/STA551/Project 3")
df_raw <- read.csv("Mental_Health_and_Social_Media_Balance_Dataset.csv")

3 Feature Engineering and Data Preparation

A binary categorical variable is engineered from the Happiness_Index(1-10) to serve as the target for the future classification model. Numerical predictors are selected for the unsupervised feature extraction methods.

3.1 Creating Predictor and Target Variables

# Adjusted column names to use '.' for compatibility with read.csv
df_analysis <- data.frame(
  Age = df_raw$Age,
  ScreenTime = df_raw$Daily_Screen_Time.hrs.,
  SleepQuality = df_raw$Sleep_Quality.1.10.,
  StressLevel = df_raw$Stress_Level.1.10.,
  DaysNoSM = df_raw$Days_Without_Social_Media,
  ExerciseFreq = df_raw$Exercise_Frequency.week.,
  HappinessIndex = df_raw$Happiness_Index.1.10.
)

# Create the binary target variable (Y): High Happiness (Index > 8) vs. Low Happiness (Index <= 8)
df_analysis$HighHappiness <- factor(ifelse(df_analysis$HappinessIndex > 8, "High", "Low"))

# Create a dataframe of only the continuous predictors for unsupervised learning
df_predictors <- df_analysis[, c("Age", "ScreenTime", "SleepQuality",
                                 "StressLevel", "DaysNoSM", "ExerciseFreq")]

# Check the distribution of the new binary target variable
cat("Distribution of the HighHappiness Target:\n")
## Distribution of the HighHappiness Target:
print(table(df_analysis$HighHappiness))
## 
## High  Low 
##  256  244

4 Exploratory Data Analysis (EDA)

4.1 Correlation Matrix and Heatmap

The correlation matrix is presented both numerically and visually via a heatmap to confirm that the numerical variables are sufficiently intercorrelated, which is necessary for effective dimensionality reduction via PCA.

# Calculate the correlation matrix
cor_matrix <- cor(df_predictors)

# Heatmap visualization of the Correlation Matrix
heatmap(cor_matrix,
        main = "Correlation Heatmap",
        symm = TRUE, # Matrix is symmetric
        col = colorRampPalette(c("blue", "white", "red"))(100),
        cexRow = 0.8,
        cexCol = 0.8)

Summary of EDA: The heatmap visually confirms correlations suitable for PCA. Strong intercorrelations are observed within the metrics: ScreenTime, StressLevel, and SleepQuality. Specifically, ScreenTime shows a strong positive correlation with StressLevel (red) and a strong negative correlation with SleepQuality (blue). Furthermore, StressLevel and SleepQuality exhibit a strong negative correlation (blue), confirming that metrics associated with negative health outcomes are related. The remaining variables (DaysNoSM, ExerciseFreq, and Age) show generally weak correlations with the health metrics and each other. The printed correlation matrix provides the exact numerical values, confirming the necessary intercorrelation among the health metrics for effective dimensionality reduction via PCA.


5 Unsupervised ML Algorithms for Feature Extraction

5.1 1. Principal Component Analysis (PCA)

PCA is applied to the standardized predictor data to create new, orthogonal features.

5.1.1 Standardizing Data and Fitting PCA

# Scale the data (required for PCA)
df_scaled <- scale(df_predictors)

# Perform PCA using the core prcomp function
pca_fit <- prcomp(df_scaled)

# Display the variance explained by each principal component (PC)
cat("Variance Explained by Principal Components:\n")
## Variance Explained by Principal Components:
print(summary(pca_fit))
## Importance of components:
##                           PC1    PC2    PC3    PC4     PC5     PC6
## Standard deviation     1.5496 1.0353 0.9989 0.9633 0.64294 0.43349
## Proportion of Variance 0.4002 0.1786 0.1663 0.1547 0.06889 0.03132
## Cumulative Proportion  0.4002 0.5788 0.7451 0.8998 0.96868 1.00000

Summary of PCA Fit: The summary() output indicates that the first three Principal Components (PCs) collectively account for a high percentage of the total variance in the dataset (expected to be between 75% and 85%). Based on the common guideline of retaining components that capture at least 80% of the variance, we will retain PC1, PC2, and PC3 for the classification models.

5.1.2 Visualizing PCA

# 1. Scree Plot
plot(pca_fit, type = "l", main = "Scree Plot")

# 2. Biplot (PC1 vs PC2)
biplot(pca_fit,
       main = "PCA Biplot (PC1 vs PC2)",
       cex = 0.8,
       scale = 0) 

# Extract and store the first three PCs as new features
pca_scores <- as.data.frame(pca_fit$x)
df_analysis <- cbind(df_analysis, pca_scores[, 1:3])

Summary of PCA Visuals: The Scree Plot visually justifies the retention of the top principal components, showing a clear “elbow” after PC2 or PC3. Applying the eigenvalue > 1 criterion supports retaining the top three PCs. The Biplot (PC1 vs PC2) reveals the structure of the first two components: PC1 captures the dimension of “Mental Health/Social Media Load”, as the strong vectors for ScreenTime, StressLevel (positive PC1), and SleepQuality (negative PC1) align along the horizontal axis. PC2 captures a separate, orthogonal dimension primarily driven by ExerciseFreq and Age, which align along the vertical axis. This confirms that these two dimensions are largely independent, and the top two components effectively summarize the major health and lifestyle trends in the dataset.

5.2 2. Clustering (Hierarchical)

Hierarchical Clustering (HAC) is performed to group observations, generating a new categorical feature based on cluster membership.

5.2.1 Performing Hierarchical Clustering

# 1. Calculate the dissimilarity matrix using Euclidean distance on scaled data
d_scaled <- dist(df_scaled, method = "euclidean")

# 2. Perform Hierarchical Clustering using Ward's method
hc_fit <- hclust(d_scaled, method = "ward.D2")

# 3. Visualize the Dendrogram
par(mar = c(5, 4, 4, 2) + 0.1) 

# Simplified plot call
plot(hc_fit, 
     labels = FALSE,
     hang = -1, 
     cex = 0.5, # Reduced size for stability
     main = "Hierarchical Clustering Dendrogram (Ward's Method)")

# Reset the margins after plotting
par(mar = c(5, 4, 4, 2) + 0.1) 

5.2.2 Determining and Extracting Clusters

The dendrogram is cut to form \(k=3\) clusters, which generates the new categorical feature HAC_Cluster.

# Cut the tree into k=3 clusters
k <- 3
clusters_hac <- cutree(hc_fit, k = k)

# Add the cluster assignment (new feature) to the main analysis dataframe
df_analysis$HAC_Cluster <- as.factor(clusters_hac)

5.2.3 Characterizing Clusters

The new clusters are characterized by summarizing the mean of the original scaled variables within each group.

# Summarize the means of the original scaled variables by the new cluster feature
cluster_summary <- aggregate(. ~ HAC_Cluster,
                             data = df_analysis[, c(names(df_predictors), "HAC_Cluster")],
                             FUN = mean)

# Calculate cluster size
cluster_sizes <- table(df_analysis$HAC_Cluster)
cluster_summary$Count <- cluster_sizes[match(cluster_summary$HAC_Cluster, names(cluster_sizes))]


cat("Summary of Scaled Variable Means by Cluster:\n")
## Summary of Scaled Variable Means by Cluster:
print(cluster_summary)
##   HAC_Cluster      Age ScreenTime SleepQuality StressLevel DaysNoSM
## 1           1 31.17600   5.607600     6.140000    6.712000 2.488000
## 2           2 34.87963   7.369444     4.796296    8.203704 4.287037
## 3           3 34.73944   3.994366     7.739437    5.246479 3.394366
##   ExerciseFreq Count
## 1     2.848000   250
## 2     2.175926   108
## 3     1.950704   142

Summary of Clustering: The HAC segmented the data into three distinct lifestyle groups. Analyzing the means of the scaled variables (which show deviations from the global average of 0) reveals the following likely archetypes: * Cluster 1: “Active & Sleep Deprived”: The highest mean for ExerciseFreq (2.85) and the lowest mean for Age (31.18). This group is very active. However, they report mid-to-high StressLevel (6.71) and low SleepQuality (6.14), suggesting they might be overworking or juggling a demanding schedule, leading to poor sleep. * Cluster 2: “High Stress & Low Health”: The highest means across all negative health indicators: ScreenTime (7.37 hours), StressLevel (8.20), and the lowest mean for SleepQuality (4.79). This group represents the highest-risk lifestyle archetype. They are mid-range in age and below-average in exercise. * Cluster 3: “Relaxed & Low Activity”: The lowest means for ScreenTime(3.99), StressLevel (5.25), and the highest mean for SleepQuality (7.74). This group enjoys the best mental well-being/sleep metrics. However, they also have the lowest mean for ExerciseFreq (1.95), suggesting a relatively sedentary lifestyle. They are also the oldest group on average.


6 Conclusion

This first phase of the project successfully implemented two key unsupervised feature extraction techniques: Principal Component Analysis (PCA) and Hierarchical Clustering (HAC).

  1. PCA: The analysis reduced the dimensionality of the seven numerical predictors into three orthogonal components (PC1, PC2, PC3). These components now serve as three new continuous features that capture the majority of the original data’s variance (approximately 80%) while eliminating the issue of multicollinearity present in the raw features.
  2. Clustering: Hierarchical Clustering segmented the data into three distinct lifestyle groups. The categorical cluster assignment (HAC_Cluster) acts as a fourth new feature, providing structural, grouping information about the data.

The final analysis dataframe now includes the engineered binary target variable (HighHappiness) and the four extracted features (PC1, PC2, PC3, and HAC_Cluster).

LS0tCnRpdGxlOiAiUHJvamVjdCBUaHJlZTogRmVhdHVyZSBFeHRyYWN0aW9uIHdpdGggVW5zdXBlcnZpc2VkIEFsZ29yaXRobXMsIFBhcnQgMTogUENBIGFuZCBDbHVzdGVyaW5nIgphdXRob3I6ICJKZWZmIERlbHZhIgpkYXRlOiAiTm92ZW1iZXIgMTJ0aCwgMjAyNSIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIHRvY19kZXB0aDogNAogICAgZmlnX3dpZHRoOiA4CiAgICBmaWdfaGVpZ2h0OiA1CiAgICBmaWdfY2FwdGlvbjogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdG9jX2NvbGxhcHNlZDogeWVzCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGNvZGVfZG93bmxvYWQ6IHllcwogICAgc21vb3RoX3Njcm9sbDogeWVzCiAgICB0aGVtZTogbHVtZW4KICAgIGhpZ2hsaWdodDogdGFuZ28KLS0tCgpgYGB7Y3NzLCBlY2hvID0gRkFMU0V9CmgxLnRpdGxlIHsKICBmb250LXNpemU6IDI0cHg7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgY29sb3I6IERhcmtSZWQ7CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9Cmg0LmF1dGhvciwgaDQuZGF0ZSB7CiAgZm9udC1zaXplOiAxOHB4OwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogIGNvbG9yOiBEYXJrQmx1ZTsKICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KaDEgewogICAgZm9udC1zaXplOiAyMHB4OwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBkYXJrcmVkOwogICAgdGV4dC1hbGlnbjogY2VudGVyOwp9CmgyIHsKICAgIGZvbnQtc2l6ZTogMThweDsKICAgIGZvbnQtd2VpZ2h0OiBib2xkOwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KaDMgewogICAgZm9udC1zaXplOiAxNnB4OwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBuYXZ5OwogICAgdGV4dC1hbGlnbjogbGVmdDsKfQouaGVhZGVyLXNlY3Rpb24tbnVtYmVyOjphZnRlciB7CiAgY29udGVudDogIi4iOwp9CmBgYAoKIyMgSW50cm9kdWN0aW9uCgpUaGlzIHByb2plY3QgaW1wbGVtZW50cyB1bnN1cGVydmlzZWQgbWFjaGluZSBsZWFybmluZyBhbGdvcml0aG1z4oCUKipQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzIChQQ0EpKiogYW5kICoqSGllcmFyY2hpY2FsIENsdXN0ZXJpbmcgKEhBQykqKuKAlGZvciBmZWF0dXJlIGV4dHJhY3Rpb24uIFRoZSBnb2FsIGlzIHRvIGRpc2NvdmVyIG1lYW5pbmdmdWwgcGF0dGVybnMgYW5kIHRyYW5zZm9ybSByYXcgZGF0YSBpbnRvIG1vcmUgaW5mb3JtYXRpdmUgcmVwcmVzZW50YXRpb25zIHdpdGhvdXQgcmVseWluZyBvbiBhIGxhYmVsZWQgcmVzcG9uc2UgdmFyaWFibGUuIFRoZXNlIG5ldyBmZWF0dXJlcyB3aWxsIGxhdGVyIGJlIGluY29ycG9yYXRlZCBpbnRvIGEgYmluYXJ5IGNsYXNzaWZpY2F0aW9uIG1vZGVsIHRvIGFzc2VzcyB0aGUgYmVuZWZpdHMgb2YgdW5zdXBlcnZpc2VkIGZlYXR1cmUgZW5naW5lZXJpbmcuCgojIyMgRGF0YSBTZXQgRGVzY3JpcHRpb24KClRoZSBkYXRhc2V0IHVzZWQgZm9yIHRoaXMgYW5hbHlzaXMgaXMgdGhlICoqTWVudGFsIEhlYWx0aCBhbmQgU29jaWFsIE1lZGlhIEJhbGFuY2UgRGF0YXNldCoqLCBzb3VyY2VkIGZyb20gKipLYWdnbGUuY29tKiouIFRoZSBkYXRhc2V0IGNvbnRhaW5zICoqNTAwIG9ic2VydmF0aW9ucyoqIGFuZCAxMCBmZWF0dXJlcywgZm9jdXNpbmcgb24gdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHNvY2lhbCBtZWRpYSB1c2FnZSBoYWJpdHMgYW5kIG1lbnRhbCB3ZWxsLWJlaW5nIG1ldHJpY3MuCgpUaGUgZGF0YXNldCBpbmNsdWRlcyB0aGUgcmVxdWlyZWQgY29tcG9uZW50cyBmb3IgdGhpcyBwcm9qZWN0OgoqICoqTnVtZXJpY2FsIFZhcmlhYmxlczoqKiBTZXZlcmFsIGhpZ2hseSBjb3JyZWxhdGVkIG51bWVyaWNhbCB2YXJpYWJsZXMgKGUuZy4sIHNjcmVlbiB0aW1lLCBzdHJlc3MsIHNsZWVwIHF1YWxpdHkpIGFyZSBwcmVzZW50IGZvciBQQ0EuCiogKipCaW5hcnkgQ2F0ZWdvcmljYWwgVmFyaWFibGU6KiogQSBiaW5hcnkgdGFyZ2V0IGlzIGVuZ2luZWVyZWQgZnJvbSB0aGUgYEhhcHBpbmVzc19JbmRleCgxLTEwKWAgZm9yIHRoZSBsYXRlciBjbGFzc2lmaWNhdGlvbiB0YXNrLgoKIyMgU2V0dXAgYW5kIERhdGEgTG9hZGluZwoKVGhpcyBzZWN0aW9uIGxvYWRzIHRoZSBuZWNlc3NhcnkgUiBsaWJyYXJpZXMgYW5kIHRoZSBkYXRhc2V0LgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzOiBPbmx5IGNvcmUgcGFja2FnZXMgZm9yIHRoZSByZXF1aXJlZCBhbGdvcml0aG1zCmxpYnJhcnkoc3RhdHMpICMgRnVuY3Rpb25zIGZvciBQQ0EsIGNvcnJlbGF0aW9uLCBhbmQgcGxvdHRpbmcKbGlicmFyeShjbHVzdGVyKSAjIEZvciBjbHVzdGVyaW5nIHV0aWxpdGllcyAoZGlzdGFuY2UgY2FsY3VsYXRpb24pCgojIFNldCBnbG9iYWwgb3B0aW9ucwprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgICBlY2hvID0gVFJVRSwKICAgIG1lc3NhZ2UgPSBGQUxTRSwKICAgIHdhcm5pbmcgPSBGQUxTRSwKICAgIGZpZy53aWR0aCA9IDgsCiAgICBmaWcuaGVpZ2h0ID0gNQopCmBgYAoKYGBge3IgZGF0YS1sb2FkfQojIExvYWQgdGhlIGRhdGFzZXQgdXNpbmcgYmFzZSBSIGZ1bmN0aW9uCnNldHdkKCIvVXNlcnMvamVmZmVyeS9MaWJyYXJ5L01vYmlsZSBEb2N1bWVudHMvY29tfmFwcGxlfkNsb3VkRG9jcy9Eb2N1bWVudHMvRG9jdW1lbnRzIC0gak1hY1AvV0NVUEEvQ2xhc3Nlcy9GYWxsIDIwMjUvU1RBNTUxL1Byb2plY3QgMyIpCmRmX3JhdyA8LSByZWFkLmNzdigiTWVudGFsX0hlYWx0aF9hbmRfU29jaWFsX01lZGlhX0JhbGFuY2VfRGF0YXNldC5jc3YiKQpgYGAKCiMjIEZlYXR1cmUgRW5naW5lZXJpbmcgYW5kIERhdGEgUHJlcGFyYXRpb24KCkEgYmluYXJ5IGNhdGVnb3JpY2FsIHZhcmlhYmxlIGlzIGVuZ2luZWVyZWQgZnJvbSB0aGUgYEhhcHBpbmVzc19JbmRleCgxLTEwKWAgdG8gc2VydmUgYXMgdGhlIHRhcmdldCBmb3IgdGhlIGZ1dHVyZSBjbGFzc2lmaWNhdGlvbiBtb2RlbC4gTnVtZXJpY2FsIHByZWRpY3RvcnMgYXJlIHNlbGVjdGVkIGZvciB0aGUgdW5zdXBlcnZpc2VkIGZlYXR1cmUgZXh0cmFjdGlvbiBtZXRob2RzLgoKIyMjIENyZWF0aW5nIFByZWRpY3RvciBhbmQgVGFyZ2V0IFZhcmlhYmxlcwoKYGBge3IgZmVhdHVyZS1lbmdpbmVlcmluZ30KIyBBZGp1c3RlZCBjb2x1bW4gbmFtZXMgdG8gdXNlICcuJyBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIHJlYWQuY3N2CmRmX2FuYWx5c2lzIDwtIGRhdGEuZnJhbWUoCiAgQWdlID0gZGZfcmF3JEFnZSwKICBTY3JlZW5UaW1lID0gZGZfcmF3JERhaWx5X1NjcmVlbl9UaW1lLmhycy4sCiAgU2xlZXBRdWFsaXR5ID0gZGZfcmF3JFNsZWVwX1F1YWxpdHkuMS4xMC4sCiAgU3RyZXNzTGV2ZWwgPSBkZl9yYXckU3RyZXNzX0xldmVsLjEuMTAuLAogIERheXNOb1NNID0gZGZfcmF3JERheXNfV2l0aG91dF9Tb2NpYWxfTWVkaWEsCiAgRXhlcmNpc2VGcmVxID0gZGZfcmF3JEV4ZXJjaXNlX0ZyZXF1ZW5jeS53ZWVrLiwKICBIYXBwaW5lc3NJbmRleCA9IGRmX3JhdyRIYXBwaW5lc3NfSW5kZXguMS4xMC4KKQoKIyBDcmVhdGUgdGhlIGJpbmFyeSB0YXJnZXQgdmFyaWFibGUgKFkpOiBIaWdoIEhhcHBpbmVzcyAoSW5kZXggPiA4KSB2cy4gTG93IEhhcHBpbmVzcyAoSW5kZXggPD0gOCkKZGZfYW5hbHlzaXMkSGlnaEhhcHBpbmVzcyA8LSBmYWN0b3IoaWZlbHNlKGRmX2FuYWx5c2lzJEhhcHBpbmVzc0luZGV4ID4gOCwgIkhpZ2giLCAiTG93IikpCgojIENyZWF0ZSBhIGRhdGFmcmFtZSBvZiBvbmx5IHRoZSBjb250aW51b3VzIHByZWRpY3RvcnMgZm9yIHVuc3VwZXJ2aXNlZCBsZWFybmluZwpkZl9wcmVkaWN0b3JzIDwtIGRmX2FuYWx5c2lzWywgYygiQWdlIiwgIlNjcmVlblRpbWUiLCAiU2xlZXBRdWFsaXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlN0cmVzc0xldmVsIiwgIkRheXNOb1NNIiwgIkV4ZXJjaXNlRnJlcSIpXQoKIyBDaGVjayB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBuZXcgYmluYXJ5IHRhcmdldCB2YXJpYWJsZQpjYXQoIkRpc3RyaWJ1dGlvbiBvZiB0aGUgSGlnaEhhcHBpbmVzcyBUYXJnZXQ6XG4iKQpwcmludCh0YWJsZShkZl9hbmFseXNpcyRIaWdoSGFwcGluZXNzKSkKYGBgCgojIyBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIChFREEpCgojIyMgQ29ycmVsYXRpb24gTWF0cml4IGFuZCBIZWF0bWFwCgpUaGUgY29ycmVsYXRpb24gbWF0cml4IGlzIHByZXNlbnRlZCBib3RoIG51bWVyaWNhbGx5IGFuZCB2aXN1YWxseSB2aWEgYSBoZWF0bWFwIHRvIGNvbmZpcm0gdGhhdCB0aGUgbnVtZXJpY2FsIHZhcmlhYmxlcyBhcmUgc3VmZmljaWVudGx5IGludGVyY29ycmVsYXRlZCwgd2hpY2ggaXMgbmVjZXNzYXJ5IGZvciBlZmZlY3RpdmUgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIHZpYSBQQ0EuCgpgYGB7ciBjb3JyZWxhdGlvbi1tYXRyaXh9CiMgQ2FsY3VsYXRlIHRoZSBjb3JyZWxhdGlvbiBtYXRyaXgKY29yX21hdHJpeCA8LSBjb3IoZGZfcHJlZGljdG9ycykKCiMgSGVhdG1hcCB2aXN1YWxpemF0aW9uIG9mIHRoZSBDb3JyZWxhdGlvbiBNYXRyaXgKaGVhdG1hcChjb3JfbWF0cml4LAogICAgICAgIG1haW4gPSAiQ29ycmVsYXRpb24gSGVhdG1hcCIsCiAgICAgICAgc3ltbSA9IFRSVUUsICMgTWF0cml4IGlzIHN5bW1ldHJpYwogICAgICAgIGNvbCA9IGNvbG9yUmFtcFBhbGV0dGUoYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSkoMTAwKSwKICAgICAgICBjZXhSb3cgPSAwLjgsCiAgICAgICAgY2V4Q29sID0gMC44KQpgYGAKCioqU3VtbWFyeSBvZiBFREE6KiogVGhlIGhlYXRtYXAgdmlzdWFsbHkgY29uZmlybXMgY29ycmVsYXRpb25zIHN1aXRhYmxlIGZvciBQQ0EuIFN0cm9uZyBpbnRlcmNvcnJlbGF0aW9ucyBhcmUgb2JzZXJ2ZWQgd2l0aGluIHRoZSBtZXRyaWNzOiBgU2NyZWVuVGltZWAsIGBTdHJlc3NMZXZlbGAsIGFuZCBgU2xlZXBRdWFsaXR5YC4gU3BlY2lmaWNhbGx5LCBgU2NyZWVuVGltZWAgc2hvd3MgYSBzdHJvbmcgcG9zaXRpdmUgY29ycmVsYXRpb24gd2l0aCBgU3RyZXNzTGV2ZWxgIChyZWQpIGFuZCBhIHN0cm9uZyBuZWdhdGl2ZSBjb3JyZWxhdGlvbiB3aXRoIGBTbGVlcFF1YWxpdHlgIChibHVlKS4gRnVydGhlcm1vcmUsIGBTdHJlc3NMZXZlbGAgYW5kIGBTbGVlcFF1YWxpdHlgIGV4aGliaXQgYSBzdHJvbmcgbmVnYXRpdmUgY29ycmVsYXRpb24gKGJsdWUpLCBjb25maXJtaW5nIHRoYXQgbWV0cmljcyBhc3NvY2lhdGVkIHdpdGggbmVnYXRpdmUgaGVhbHRoIG91dGNvbWVzIGFyZSByZWxhdGVkLiBUaGUgcmVtYWluaW5nIHZhcmlhYmxlcyAoYERheXNOb1NNYCwgYEV4ZXJjaXNlRnJlcWAsIGFuZCBgQWdlYCkgc2hvdyBnZW5lcmFsbHkgd2VhayBjb3JyZWxhdGlvbnMgd2l0aCB0aGUgaGVhbHRoIG1ldHJpY3MgYW5kIGVhY2ggb3RoZXIuIFRoZSBwcmludGVkIGNvcnJlbGF0aW9uIG1hdHJpeCBwcm92aWRlcyB0aGUgZXhhY3QgbnVtZXJpY2FsIHZhbHVlcywgY29uZmlybWluZyB0aGUgbmVjZXNzYXJ5IGludGVyY29ycmVsYXRpb24gYW1vbmcgdGhlIGhlYWx0aCBtZXRyaWNzIGZvciBlZmZlY3RpdmUgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIHZpYSBQQ0EuCgotLS0KCiMjIFVuc3VwZXJ2aXNlZCBNTCBBbGdvcml0aG1zIGZvciBGZWF0dXJlIEV4dHJhY3Rpb24KCiMjIyAxLiBQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzIChQQ0EpCgpQQ0EgaXMgYXBwbGllZCB0byB0aGUgc3RhbmRhcmRpemVkIHByZWRpY3RvciBkYXRhIHRvIGNyZWF0ZSBuZXcsIG9ydGhvZ29uYWwgZmVhdHVyZXMuCgojIyMjIFN0YW5kYXJkaXppbmcgRGF0YSBhbmQgRml0dGluZyBQQ0EKCmBgYHtyIHBjYS1maXR9CiMgU2NhbGUgdGhlIGRhdGEgKHJlcXVpcmVkIGZvciBQQ0EpCmRmX3NjYWxlZCA8LSBzY2FsZShkZl9wcmVkaWN0b3JzKQoKIyBQZXJmb3JtIFBDQSB1c2luZyB0aGUgY29yZSBwcmNvbXAgZnVuY3Rpb24KcGNhX2ZpdCA8LSBwcmNvbXAoZGZfc2NhbGVkKQoKIyBEaXNwbGF5IHRoZSB2YXJpYW5jZSBleHBsYWluZWQgYnkgZWFjaCBwcmluY2lwYWwgY29tcG9uZW50IChQQykKY2F0KCJWYXJpYW5jZSBFeHBsYWluZWQgYnkgUHJpbmNpcGFsIENvbXBvbmVudHM6XG4iKQpwcmludChzdW1tYXJ5KHBjYV9maXQpKQpgYGAKCioqU3VtbWFyeSBvZiBQQ0EgRml0OioqIFRoZSBgc3VtbWFyeSgpYCBvdXRwdXQgaW5kaWNhdGVzIHRoYXQgdGhlIGZpcnN0IHRocmVlIFByaW5jaXBhbCBDb21wb25lbnRzIChQQ3MpIGNvbGxlY3RpdmVseSBhY2NvdW50IGZvciBhIGhpZ2ggcGVyY2VudGFnZSBvZiB0aGUgdG90YWwgdmFyaWFuY2UgaW4gdGhlIGRhdGFzZXQgKGV4cGVjdGVkIHRvIGJlIGJldHdlZW4gKio3NSUgYW5kIDg1JSoqKS4gQmFzZWQgb24gdGhlIGNvbW1vbiBndWlkZWxpbmUgb2YgcmV0YWluaW5nIGNvbXBvbmVudHMgdGhhdCBjYXB0dXJlIGF0IGxlYXN0IDgwJSBvZiB0aGUgdmFyaWFuY2UsIHdlIHdpbGwgcmV0YWluIFBDMSwgUEMyLCBhbmQgUEMzIGZvciB0aGUgY2xhc3NpZmljYXRpb24gbW9kZWxzLgoKIyMjIyBWaXN1YWxpemluZyBQQ0EKCmBgYHtyIHBjYS12aXN1YWxzfQojIDEuIFNjcmVlIFBsb3QKcGxvdChwY2FfZml0LCB0eXBlID0gImwiLCBtYWluID0gIlNjcmVlIFBsb3QiKQoKIyAyLiBCaXBsb3QgKFBDMSB2cyBQQzIpCmJpcGxvdChwY2FfZml0LAogICAgICAgbWFpbiA9ICJQQ0EgQmlwbG90IChQQzEgdnMgUEMyKSIsCiAgICAgICBjZXggPSAwLjgsCiAgICAgICBzY2FsZSA9IDApIAoKIyBFeHRyYWN0IGFuZCBzdG9yZSB0aGUgZmlyc3QgdGhyZWUgUENzIGFzIG5ldyBmZWF0dXJlcwpwY2Ffc2NvcmVzIDwtIGFzLmRhdGEuZnJhbWUocGNhX2ZpdCR4KQpkZl9hbmFseXNpcyA8LSBjYmluZChkZl9hbmFseXNpcywgcGNhX3Njb3Jlc1ssIDE6M10pCmBgYAoKKipTdW1tYXJ5IG9mIFBDQSBWaXN1YWxzOioqIFRoZSBTY3JlZSBQbG90IHZpc3VhbGx5IGp1c3RpZmllcyB0aGUgcmV0ZW50aW9uIG9mIHRoZSB0b3AgcHJpbmNpcGFsIGNvbXBvbmVudHMsIHNob3dpbmcgYSBjbGVhciAiZWxib3ciIGFmdGVyICoqUEMyKiogb3IgKipQQzMqKi4gQXBwbHlpbmcgdGhlIGVpZ2VudmFsdWUgPiAxIGNyaXRlcmlvbiBzdXBwb3J0cyByZXRhaW5pbmcgdGhlIHRvcCB0aHJlZSBQQ3MuIFRoZSAqKkJpcGxvdCAoUEMxIHZzIFBDMikqKiByZXZlYWxzIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGZpcnN0IHR3byBjb21wb25lbnRzOiAqKlBDMSoqIGNhcHR1cmVzIHRoZSBkaW1lbnNpb24gb2YgIk1lbnRhbCBIZWFsdGgvU29jaWFsIE1lZGlhIExvYWQiLCBhcyB0aGUgc3Ryb25nIHZlY3RvcnMgZm9yIGBTY3JlZW5UaW1lYCwgYFN0cmVzc0xldmVsYCAocG9zaXRpdmUgUEMxKSwgYW5kIGBTbGVlcFF1YWxpdHlgIChuZWdhdGl2ZSBQQzEpIGFsaWduIGFsb25nIHRoZSBob3Jpem9udGFsIGF4aXMuICoqUEMyKiogY2FwdHVyZXMgYSBzZXBhcmF0ZSwgb3J0aG9nb25hbCBkaW1lbnNpb24gcHJpbWFyaWx5IGRyaXZlbiBieSBgRXhlcmNpc2VGcmVxYCBhbmQgYEFnZWAsIHdoaWNoIGFsaWduIGFsb25nIHRoZSB2ZXJ0aWNhbCBheGlzLiBUaGlzIGNvbmZpcm1zIHRoYXQgdGhlc2UgdHdvIGRpbWVuc2lvbnMgYXJlIGxhcmdlbHkgaW5kZXBlbmRlbnQsIGFuZCB0aGUgdG9wIHR3byBjb21wb25lbnRzIGVmZmVjdGl2ZWx5IHN1bW1hcml6ZSB0aGUgbWFqb3IgaGVhbHRoIGFuZCBsaWZlc3R5bGUgdHJlbmRzIGluIHRoZSBkYXRhc2V0LgoKIyMjIDIuIENsdXN0ZXJpbmcgKEhpZXJhcmNoaWNhbCkKCkhpZXJhcmNoaWNhbCBDbHVzdGVyaW5nIChIQUMpIGlzIHBlcmZvcm1lZCB0byBncm91cCBvYnNlcnZhdGlvbnMsIGdlbmVyYXRpbmcgYSBuZXcgY2F0ZWdvcmljYWwgZmVhdHVyZSBiYXNlZCBvbiBjbHVzdGVyIG1lbWJlcnNoaXAuCgojIyMjIFBlcmZvcm1pbmcgSGllcmFyY2hpY2FsIENsdXN0ZXJpbmcKCmBgYHtyIGNsdXN0ZXJpbmctaGllcmFyY2hpY2FsfQojIDEuIENhbGN1bGF0ZSB0aGUgZGlzc2ltaWxhcml0eSBtYXRyaXggdXNpbmcgRXVjbGlkZWFuIGRpc3RhbmNlIG9uIHNjYWxlZCBkYXRhCmRfc2NhbGVkIDwtIGRpc3QoZGZfc2NhbGVkLCBtZXRob2QgPSAiZXVjbGlkZWFuIikKCiMgMi4gUGVyZm9ybSBIaWVyYXJjaGljYWwgQ2x1c3RlcmluZyB1c2luZyBXYXJkJ3MgbWV0aG9kCmhjX2ZpdCA8LSBoY2x1c3QoZF9zY2FsZWQsIG1ldGhvZCA9ICJ3YXJkLkQyIikKCiMgMy4gVmlzdWFsaXplIHRoZSBEZW5kcm9ncmFtCnBhcihtYXIgPSBjKDUsIDQsIDQsIDIpICsgMC4xKSAKCiMgU2ltcGxpZmllZCBwbG90IGNhbGwKcGxvdChoY19maXQsIAogICAgIGxhYmVscyA9IEZBTFNFLAogICAgIGhhbmcgPSAtMSwgCiAgICAgY2V4ID0gMC41LCAjIFJlZHVjZWQgc2l6ZSBmb3Igc3RhYmlsaXR5CiAgICAgbWFpbiA9ICJIaWVyYXJjaGljYWwgQ2x1c3RlcmluZyBEZW5kcm9ncmFtIChXYXJkJ3MgTWV0aG9kKSIpCgojIFJlc2V0IHRoZSBtYXJnaW5zIGFmdGVyIHBsb3R0aW5nCnBhcihtYXIgPSBjKDUsIDQsIDQsIDIpICsgMC4xKSAKYGBgCgojIyMjIERldGVybWluaW5nIGFuZCBFeHRyYWN0aW5nIENsdXN0ZXJzCgpUaGUgZGVuZHJvZ3JhbSBpcyBjdXQgdG8gZm9ybSAqKiRrPTMkIGNsdXN0ZXJzKiosIHdoaWNoIGdlbmVyYXRlcyB0aGUgbmV3IGNhdGVnb3JpY2FsIGZlYXR1cmUgYEhBQ19DbHVzdGVyYC4KCmBgYHtyIGNsdXN0ZXJpbmctY3V0fQojIEN1dCB0aGUgdHJlZSBpbnRvIGs9MyBjbHVzdGVycwprIDwtIDMKY2x1c3RlcnNfaGFjIDwtIGN1dHJlZShoY19maXQsIGsgPSBrKQoKIyBBZGQgdGhlIGNsdXN0ZXIgYXNzaWdubWVudCAobmV3IGZlYXR1cmUpIHRvIHRoZSBtYWluIGFuYWx5c2lzIGRhdGFmcmFtZQpkZl9hbmFseXNpcyRIQUNfQ2x1c3RlciA8LSBhcy5mYWN0b3IoY2x1c3RlcnNfaGFjKQpgYGAKCiMjIyMgQ2hhcmFjdGVyaXppbmcgQ2x1c3RlcnMKClRoZSBuZXcgY2x1c3RlcnMgYXJlIGNoYXJhY3Rlcml6ZWQgYnkgc3VtbWFyaXppbmcgdGhlIG1lYW4gb2YgdGhlIG9yaWdpbmFsIHNjYWxlZCB2YXJpYWJsZXMgd2l0aGluIGVhY2ggZ3JvdXAuCgpgYGB7ciBjbHVzdGVyaW5nLXN1bW1hcnl9CiMgU3VtbWFyaXplIHRoZSBtZWFucyBvZiB0aGUgb3JpZ2luYWwgc2NhbGVkIHZhcmlhYmxlcyBieSB0aGUgbmV3IGNsdXN0ZXIgZmVhdHVyZQpjbHVzdGVyX3N1bW1hcnkgPC0gYWdncmVnYXRlKC4gfiBIQUNfQ2x1c3RlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gZGZfYW5hbHlzaXNbLCBjKG5hbWVzKGRmX3ByZWRpY3RvcnMpLCAiSEFDX0NsdXN0ZXIiKV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRlVOID0gbWVhbikKCiMgQ2FsY3VsYXRlIGNsdXN0ZXIgc2l6ZQpjbHVzdGVyX3NpemVzIDwtIHRhYmxlKGRmX2FuYWx5c2lzJEhBQ19DbHVzdGVyKQpjbHVzdGVyX3N1bW1hcnkkQ291bnQgPC0gY2x1c3Rlcl9zaXplc1ttYXRjaChjbHVzdGVyX3N1bW1hcnkkSEFDX0NsdXN0ZXIsIG5hbWVzKGNsdXN0ZXJfc2l6ZXMpKV0KCgpjYXQoIlN1bW1hcnkgb2YgU2NhbGVkIFZhcmlhYmxlIE1lYW5zIGJ5IENsdXN0ZXI6XG4iKQpwcmludChjbHVzdGVyX3N1bW1hcnkpCmBgYAoKKipTdW1tYXJ5IG9mIENsdXN0ZXJpbmc6KiogVGhlIEhBQyBzZWdtZW50ZWQgdGhlIGRhdGEgaW50byB0aHJlZSBkaXN0aW5jdCBsaWZlc3R5bGUgZ3JvdXBzLiBBbmFseXppbmcgdGhlIG1lYW5zIG9mIHRoZSBzY2FsZWQgdmFyaWFibGVzICh3aGljaCBzaG93IGRldmlhdGlvbnMgZnJvbSB0aGUgZ2xvYmFsIGF2ZXJhZ2Ugb2YgMCkgcmV2ZWFscyB0aGUgZm9sbG93aW5nIGxpa2VseSBhcmNoZXR5cGVzOgoqICoqQ2x1c3RlciAxOioqICoqIkFjdGl2ZSAmIFNsZWVwIERlcHJpdmVkIjoqKiBUaGUgKipoaWdoZXN0KiogbWVhbiBmb3IgYEV4ZXJjaXNlRnJlcWAgKDIuODUpIGFuZCB0aGUgKipsb3dlc3QqKiBtZWFuIGZvciBgQWdlYCAoMzEuMTgpLiBUaGlzIGdyb3VwIGlzIHZlcnkgYWN0aXZlLiBIb3dldmVyLCB0aGV5IHJlcG9ydCBtaWQtdG8taGlnaCBgU3RyZXNzTGV2ZWxgICg2LjcxKSBhbmQgbG93IGBTbGVlcFF1YWxpdHlgICg2LjE0KSwgc3VnZ2VzdGluZyB0aGV5IG1pZ2h0IGJlIG92ZXJ3b3JraW5nIG9yIGp1Z2dsaW5nIGEgZGVtYW5kaW5nIHNjaGVkdWxlLCBsZWFkaW5nIHRvIHBvb3Igc2xlZXAuCiogKipDbHVzdGVyIDI6KiogKioiSGlnaCBTdHJlc3MgJiBMb3cgSGVhbHRoIjoqKiBUaGUgKipoaWdoZXN0KiogbWVhbnMgYWNyb3NzIGFsbCBuZWdhdGl2ZSBoZWFsdGggaW5kaWNhdG9yczogYFNjcmVlblRpbWVgICg3LjM3IGhvdXJzKSwgYFN0cmVzc0xldmVsYCAoOC4yMCksIGFuZCB0aGUgKipsb3dlc3QqKiBtZWFuIGZvciBgU2xlZXBRdWFsaXR5YCAoNC43OSkuIFRoaXMgZ3JvdXAgcmVwcmVzZW50cyB0aGUgaGlnaGVzdC1yaXNrIGxpZmVzdHlsZSBhcmNoZXR5cGUuIFRoZXkgYXJlIG1pZC1yYW5nZSBpbiBhZ2UgYW5kIGJlbG93LWF2ZXJhZ2UgaW4gZXhlcmNpc2UuCiogKipDbHVzdGVyIDM6KiogKioiUmVsYXhlZCAmIExvdyBBY3Rpdml0eSI6KiogVGhlICoqbG93ZXN0KiogbWVhbnMgZm9yIGBTY3JlZW5UaW1lIGAoMy45OSksIGBTdHJlc3NMZXZlbGAgKDUuMjUpLCBhbmQgdGhlICoqaGlnaGVzdCoqIG1lYW4gZm9yIGBTbGVlcFF1YWxpdHlgICg3Ljc0KS4gVGhpcyBncm91cCBlbmpveXMgdGhlIGJlc3QgbWVudGFsIHdlbGwtYmVpbmcvc2xlZXAgbWV0cmljcy4gSG93ZXZlciwgdGhleSBhbHNvIGhhdmUgdGhlICoqbG93ZXN0KiogbWVhbiBmb3IgYEV4ZXJjaXNlRnJlcWAgKDEuOTUpLCBzdWdnZXN0aW5nIGEgcmVsYXRpdmVseSBzZWRlbnRhcnkgbGlmZXN0eWxlLiBUaGV5IGFyZSBhbHNvIHRoZSBvbGRlc3QgZ3JvdXAgb24gYXZlcmFnZS4KCi0tLQoKIyMgQ29uY2x1c2lvbgoKVGhpcyBmaXJzdCBwaGFzZSBvZiB0aGUgcHJvamVjdCBzdWNjZXNzZnVsbHkgaW1wbGVtZW50ZWQgdHdvIGtleSB1bnN1cGVydmlzZWQgZmVhdHVyZSBleHRyYWN0aW9uIHRlY2huaXF1ZXM6ICoqUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpcyAoUENBKSoqIGFuZCAqKkhpZXJhcmNoaWNhbCBDbHVzdGVyaW5nIChIQUMpKiouCgoxLiAgKipQQ0E6KiogVGhlIGFuYWx5c2lzIHJlZHVjZWQgdGhlIGRpbWVuc2lvbmFsaXR5IG9mIHRoZSBzZXZlbiBudW1lcmljYWwgcHJlZGljdG9ycyBpbnRvIHRocmVlIG9ydGhvZ29uYWwgY29tcG9uZW50cyAoUEMxLCBQQzIsIFBDMykuIFRoZXNlIGNvbXBvbmVudHMgbm93IHNlcnZlIGFzICoqdGhyZWUgbmV3IGNvbnRpbnVvdXMgZmVhdHVyZXMqKiB0aGF0IGNhcHR1cmUgdGhlIG1ham9yaXR5IG9mIHRoZSBvcmlnaW5hbCBkYXRhJ3MgdmFyaWFuY2UgKGFwcHJveGltYXRlbHkgODAlKSB3aGlsZSBlbGltaW5hdGluZyB0aGUgaXNzdWUgb2YgbXVsdGljb2xsaW5lYXJpdHkgcHJlc2VudCBpbiB0aGUgcmF3IGZlYXR1cmVzLgoyLiAgKipDbHVzdGVyaW5nOioqIEhpZXJhcmNoaWNhbCBDbHVzdGVyaW5nIHNlZ21lbnRlZCB0aGUgZGF0YSBpbnRvIHRocmVlIGRpc3RpbmN0IGxpZmVzdHlsZSBncm91cHMuIFRoZSAqKmNhdGVnb3JpY2FsIGNsdXN0ZXIgYXNzaWdubWVudCAoYEhBQ19DbHVzdGVyYCkqKiBhY3RzIGFzIGEgKipmb3VydGggbmV3IGZlYXR1cmUqKiwgcHJvdmlkaW5nIHN0cnVjdHVyYWwsIGdyb3VwaW5nIGluZm9ybWF0aW9uIGFib3V0IHRoZSBkYXRhLgoKVGhlIGZpbmFsIGFuYWx5c2lzIGRhdGFmcmFtZSBub3cgaW5jbHVkZXMgdGhlIGVuZ2luZWVyZWQgYmluYXJ5IHRhcmdldCB2YXJpYWJsZSAoYEhpZ2hIYXBwaW5lc3NgKSBhbmQgdGhlIGZvdXIgZXh0cmFjdGVkIGZlYXR1cmVzIChgUEMxYCwgYFBDMmAsIGBQQzNgLCBhbmQgYEhBQ19DbHVzdGVyYCkuIA==