library(dplyr)
Attaching package: ‘dplyr’
The following objects are masked from ‘package:stats’:
filter, lag
The following objects are masked from ‘package:base’:
intersect, setdiff, setequal, union
library(ggplot2)
library(readr)
library(tidyverse)
── Attaching core tidyverse packages ──────────────────────────────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ forcats 1.0.0 ✔ stringr 1.5.1
✔ lubridate 1.9.4 ✔ tibble 3.2.1
✔ purrr 1.0.2 ✔ tidyr 1.3.1── Conflicts ────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(ggplot2)
library(conflicted)
library(pwr) # For power analysis
Warning: package ‘pwr’ was built under R version 4.4.3
library(boot) # For bootstrapping
#Reading the data set
data <- read.csv("dataset.csv")
conflicted::conflicts_prefer(dplyr::filter)
[conflicted] Will prefer dplyr::filter over any other package.
# Filtering dataset where explicit is "True" and taking a sample of 9,000 rows
sample_data <- data |> filter(explicit == "True") |> sample_n(9000)
data <- sample_data
data
Select a continuous (or ordered integer) column of data that seems
most “valuable” given the context of your data, and call this your
response variable.
# Summary statistics for popularity
summary(data$popularity)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0 20.0 38.0 36.5 56.0 98.0
# Check unique values to confirm it's numeric/ordered
unique(data$popularity)
[1] 50 49 78 15 56 28 34 17 74 71 70 76 68 60 1 29 51 21 52 46 0 24 48 27 45 63 25 69 66 37 55 53 67 65 40 33 22 61 54 43 16
[42] 20 23 30 79 8 58 59 47 26 39 12 41 62 18 36 72 64 38 57 44 32 35 19 6 31 42 77 82 83 2 94 75 9 4 81 87 10 13 80 96 3
[83] 84 73 91 7 14 89 85 5 86 11 97 93 90 88 98 92
The minimum popularity score is 0, and the maximum is 98, indicating
a wide range. The median (38.0) and mean (36.5) suggest a slightly
right-skewed distribution. The first quartile (Q1 = 20.0) and third
quartile (Q3 = 56.0) indicate that 50% of the tracks have popularity
scores between these values.
The unique(data$popularity) function confirms that popularity is an
ordered numeric variable. The presence of values across the full range
(0–98) suggests that popularity is not categorical but a continuous (or
ordinal integer) variable.
Visual - 1
# Histogram of Popularity
ggplot(data, aes(x = popularity)) +
geom_histogram(binwidth = 5, fill = "blue", alpha = 0.7) +
theme_minimal() +
labs(title = "Distribution of Track Popularity", x = "Popularity", y = "Count")

From the graph, 1. Many tracks have 0 popularity,
indicating low or no engagement, possibly due to niche artists or newly
added songs.
2. The distribution is right-skewed, with most songs
having moderate popularity (10-50) and only a few reaching high
popularity.
3. A bimodal trend is observed, with peaks at 0
and 25-30 popularity, suggesting two distinct groups of
songs.
4. Popularity declines gradually beyond 50, showing
that mainstream success is rare and highly
competitive.
Checking how popularity correlates with other numerical
features.
# Select numeric columns only
numeric_data <- data %>% select_if(is.numeric)
# Compute correlation matrix
cor_matrix <- cor(numeric_data, use = "complete.obs")
# Show correlation of popularity with other features
cor_matrix["popularity", ]
X popularity duration_ms danceability energy key loudness
0.069695956 1.000000000 -0.024994263 0.043700311 -0.138849719 -0.033808285 -0.038545986
mode speechiness acousticness instrumentalness liveness valence tempo
-0.020201275 -0.117183177 0.030176371 -0.084582897 -0.121186883 0.001573949 0.024953068
time_signature
0.042263188
Popularity has no strong correlation with any
numerical feature, though speechiness (-0.117) and liveness
(-0.121) show slight negative effects, while
danceability (0.044) and energy (0.044) have weak positive
impacts.
Visual - 2
ggplot(data, aes(x = danceability, y = popularity)) +
geom_point(alpha = 0.5, color = "red") +
theme_minimal() +
labs(title = "Danceability vs Popularity", x = "Danceability", y = "Popularity")

The scatter plot shows a weak positive correlation
between danceability and popularity, meaning tracks with higher
danceability tend to be more popular but not strongly.
The points are widely dispersed, suggesting popularity is
influenced by multiple factors beyond danceability.
Select a categorical column of data (explanatory variable) that you
expect might influence the response variable.
We need a categorical variable that might influence popularity. A
good choice is “key” (musical key of the track), since different musical
keys might affect listener preferences.
# Check unique values in the key column
unique(data$key)
[1] 8 5 0 7 11 4 9 3 1 10 6 2
# Count occurrences of each key to ensure we have enough data
table(data$key)
0 1 2 3 4 5 6 7 8 9 10 11
770 1283 810 288 669 550 776 973 660 763 597 861
# If too many categories, consolidate into Major and Minor keys
data$key_grouped <- ifelse(data$mode == 1, "Major", "Minor")
# Check the new grouping
table(data$key_grouped)
Major Minor
5210 3790
The musical key is a good categorical variable to
analyze its impact on popularity. Since there are 12 distinct
keys, we can simplify by grouping them into Major and
Minor using the mode
column. The dataset has
5,210 Major key tracks and 3,790 Minor key
tracks, ensuring enough data for comparison. Analyzing
popularity across these groups can reveal if listeners prefer
certain keys more.
Devise a null hypothesis for an ANOVA test given this situation.
Test this hypothesis using ANOVA, and summarize your results (e.g., use
box plots). Be clear about how the R output relates to your
conclusions.
For an ANOVA test, we compare means of popularity across different
keys.
# Run ANOVA test
anova_result <- aov(popularity ~ key_grouped, data = data)
# Print ANOVA summary
summary(anova_result)
Df Sum Sq Mean Sq F value Pr(>F)
key_grouped 1 2167 2168 3.674 0.0553 .
Residuals 8998 5309050 590
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
- Null Hypothesis (H₀): The mean popularity of tracks
does not significantly differ between Major and Minor
keys.
- Alternative Hypothesis (H₁): There
is a significant difference in popularity between Major
and Minor keys.
The ANOVA test output shows an F-value of
3.674 with a p-value of 0.0553. Since
p > 0.05, we fail to reject H₀, meaning
there is no strong evidence that key type (Major vs. Minor)
influences popularity significantly. However, since the p-value
is close to 0.05, there might be a weak trend worth further
exploration.
Interpretation of ANOVA Results:
The ANOVA test evaluates whether there is a significant difference in
popularity between Major and Minor
keys. The p-value of 0.0864 is above the
conventional 5% significance level but below
10%, indicating weak evidence of an effect.
Conclusion:
Since the p-value is greater than 0.05, there is
no strong statistical evidence that key type (Major or
Minor) influences a track’s popularity. However, at a 10%
significance level, the results suggest a slight trend, though
not conclusive. Key type alone does not appear to be a major factor in
determining a song’s popularity. Other elements, such as genre,
lyrics, tempo, or artist recognition, likely have a stronger
influence on listener preferences.
# Box plot of Popularity vs Key Type
ggplot(data, aes(x = key_grouped, y = popularity, fill = key_grouped)) +
geom_boxplot() +
theme_minimal() +
labs(title = "Popularity by Key Type", x = "Key Type", y = "Popularity") +
scale_fill_manual(values = c("Major" = "blue", "Minor" = "red"))

The box plot visualizes the distribution of
popularity scores for songs in Major (blue) and
Minor (red) keys. The median popularity is nearly the same for
both groups, with a similar interquartile range and spread, indicating
that key type (Major or Minor) does not strongly influence
popularity.
Find a single continuous (or ordered integer, non-binary) column of
data that might influence the response variable. Make sure the
relationship between this variable and the response is roughly
linear.
# Check structure of danceability
str(data$danceability)
num [1:9000] 0.907 0.382 0.756 0.223 0.737 0.787 0.259 0.309 0.889 0.49 ...
# Summary statistics
summary(data$danceability)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0647 0.5230 0.6580 0.6369 0.7720 0.9710
# Check correlation with popularity
cor(data$popularity, data$danceability, use="complete.obs")
[1] 0.04370031
Interpretation of Analysis on Danceability and
Popularity
- Danceability is a continuous variable ranging from
0.0647 to 0.9710, with a median of
0.658.
- The correlation between danceability and popularity
is 0.0437, indicating a very weak positive
relationship.
- Since the correlation is close to zero,
danceability does not strongly influence popularity,
meaning higher danceability does not necessarily lead to more popular
songs.
Step 2: Check for Linearity Before fitting a linear regression model,
we visualize the relationship.
# Scatter plot with trend line
ggplot(data, aes(x = danceability, y = popularity)) +
geom_point(alpha = 0.4) +
geom_smooth(method = "lm", col = "blue") +
theme_minimal() +
labs(title = "Danceability vs. Popularity",
x = "Danceability",
y = "Popularity")

Interpretation of the Scatter Plot: Danceability
vs. Popularity
- Each dot represents a song, plotting its
danceability (x-axis) against
popularity (y-axis).
- The blue line represents the linear
trend fitted using a regression model.
- The line is nearly flat, indicating a very
weak positive correlation, which aligns with the calculated
correlation of 0.0437.
- The spread of points shows no clear linear pattern,
suggesting danceability is not a strong predictor of
popularity.
# Fit the model
lm_model <- lm(popularity ~ danceability, data = data)
# Display model summary
summary(lm_model)
Call:
lm(formula = popularity ~ danceability, data = data)
Residuals:
Min 1Q Median 3Q Max
-38.46 -16.05 0.78 19.26 61.97
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 32.5739 0.9796 33.253 < 2e-16 ***
danceability 6.1601 1.4846 4.149 3.37e-05 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 24.27 on 8998 degrees of freedom
Multiple R-squared: 0.00191, Adjusted R-squared: 0.001799
F-statistic: 17.22 on 1 and 8998 DF, p-value: 3.366e-05
The linear model shows that danceability has a small but
statistically significant effect on popularity (\(p < 0.001\)). However, the R²
value (0.00191) is extremely low, meaning danceability explains
only 0.19% of the variation in popularity. The
coefficient (6.16) suggests that increasing danceability by 1 unit
raises popularity by 6.16 points on average. However,
the high residual standard error (24.27) indicates
large unexplained variability. This suggests that danceability alone is
not a strong predictor, and other factors should be considered for
better modeling.
Residual Analysis
# Plot residuals to check model assumptions
par(mfrow = c(2,2))
plot(lm_model)

The residual analysis plots assess the assumptions of linear
regression:
- Residuals vs Fitted: Shows a random
scatter around zero, but some clustering indicates potential
non-linearity or heteroscedasticity.
- Q-Q Plot: Residuals deviate from the normal
line, suggesting non-normality in
errors.
- Scale-Location: Residuals appear evenly
spread, but the slight upward trend suggests possible
heteroscedasticity.
- Residuals vs Leverage: No extreme leverage points,
but a few observations may influence the model.
Overall, the plots indicate violations of linear regression
assumptions, suggesting a more complex model might be needed. A
multiple regression model with more features (e.g., tempo, energy,
loudness, speechiness) is needed to better understand popularity.
LS0tDQp0aXRsZTogIkRhdGEgRGl2ZSAtIDgiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShjb25mbGljdGVkKQ0KbGlicmFyeShwd3IpICAgIyBGb3IgcG93ZXIgYW5hbHlzaXMNCmxpYnJhcnkoYm9vdCkgICMgRm9yIGJvb3RzdHJhcHBpbmcNCmBgYA0KDQpgYGB7cn0NCiNSZWFkaW5nIHRoZSBkYXRhIHNldA0KZGF0YSA8LSByZWFkLmNzdigiZGF0YXNldC5jc3YiKQ0KY29uZmxpY3RlZDo6Y29uZmxpY3RzX3ByZWZlcihkcGx5cjo6ZmlsdGVyKQ0KIyBGaWx0ZXJpbmcgZGF0YXNldCB3aGVyZSBleHBsaWNpdCBpcyAiVHJ1ZSIgYW5kIHRha2luZyBhIHNhbXBsZSBvZiA5LDAwMCByb3dzDQpzYW1wbGVfZGF0YSA8LSBkYXRhIHw+IGZpbHRlcihleHBsaWNpdCA9PSAiVHJ1ZSIpIHw+IHNhbXBsZV9uKDkwMDApDQpkYXRhIDwtIHNhbXBsZV9kYXRhDQpkYXRhDQpgYGANCiMgU2VsZWN0IGEgY29udGludW91cyAob3Igb3JkZXJlZCBpbnRlZ2VyKSBjb2x1bW4gb2YgZGF0YSB0aGF0IHNlZW1zIG1vc3QgInZhbHVhYmxlIiBnaXZlbiB0aGUgY29udGV4dCBvZiB5b3VyIGRhdGEsIGFuZCBjYWxsIHRoaXMgeW91ciByZXNwb25zZSB2YXJpYWJsZS4NCg0KYGBge3J9DQojIFN1bW1hcnkgc3RhdGlzdGljcyBmb3IgcG9wdWxhcml0eQ0Kc3VtbWFyeShkYXRhJHBvcHVsYXJpdHkpDQoNCiMgQ2hlY2sgdW5pcXVlIHZhbHVlcyB0byBjb25maXJtIGl0J3MgbnVtZXJpYy9vcmRlcmVkDQp1bmlxdWUoZGF0YSRwb3B1bGFyaXR5KQ0KDQpgYGANCg0KDQpUaGUgbWluaW11bSBwb3B1bGFyaXR5IHNjb3JlIGlzIDAsIGFuZCB0aGUgbWF4aW11bSBpcyA5OCwgaW5kaWNhdGluZyBhIHdpZGUgcmFuZ2UuDQpUaGUgbWVkaWFuICgzOC4wKSBhbmQgbWVhbiAoMzYuNSkgc3VnZ2VzdCBhIHNsaWdodGx5IHJpZ2h0LXNrZXdlZCBkaXN0cmlidXRpb24uDQpUaGUgZmlyc3QgcXVhcnRpbGUgKFExID0gMjAuMCkgYW5kIHRoaXJkIHF1YXJ0aWxlIChRMyA9IDU2LjApIGluZGljYXRlIHRoYXQgNTAlIG9mIHRoZSB0cmFja3MgaGF2ZSBwb3B1bGFyaXR5IHNjb3JlcyBiZXR3ZWVuIHRoZXNlIHZhbHVlcy4NCg0KVGhlIHVuaXF1ZShkYXRhJHBvcHVsYXJpdHkpIGZ1bmN0aW9uIGNvbmZpcm1zIHRoYXQgcG9wdWxhcml0eSBpcyBhbiBvcmRlcmVkIG51bWVyaWMgdmFyaWFibGUuDQpUaGUgcHJlc2VuY2Ugb2YgdmFsdWVzIGFjcm9zcyB0aGUgZnVsbCByYW5nZSAoMOKAkzk4KSBzdWdnZXN0cyB0aGF0IHBvcHVsYXJpdHkgaXMgbm90IGNhdGVnb3JpY2FsIGJ1dCBhIGNvbnRpbnVvdXMgKG9yIG9yZGluYWwgaW50ZWdlcikgdmFyaWFibGUuDQoNCiMgVmlzdWFsIC0gMQ0KYGBge3J9DQojIEhpc3RvZ3JhbSBvZiBQb3B1bGFyaXR5DQpnZ3Bsb3QoZGF0YSwgYWVzKHggPSBwb3B1bGFyaXR5KSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDUsIGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC43KSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFRyYWNrIFBvcHVsYXJpdHkiLCB4ID0gIlBvcHVsYXJpdHkiLCB5ID0gIkNvdW50IikNCmBgYA0KRnJvbSB0aGUgZ3JhcGgsIA0KMS4gTWFueSB0cmFja3MgaGF2ZSAqKjAgcG9wdWxhcml0eSoqLCBpbmRpY2F0aW5nIGxvdyBvciBubyBlbmdhZ2VtZW50LCBwb3NzaWJseSBkdWUgdG8gbmljaGUgYXJ0aXN0cyBvciBuZXdseSBhZGRlZCBzb25ncy4gIA0KMi4gVGhlIGRpc3RyaWJ1dGlvbiBpcyAqKnJpZ2h0LXNrZXdlZCoqLCB3aXRoIG1vc3Qgc29uZ3MgaGF2aW5nIG1vZGVyYXRlIHBvcHVsYXJpdHkgKDEwLTUwKSBhbmQgb25seSBhIGZldyByZWFjaGluZyBoaWdoIHBvcHVsYXJpdHkuICANCjMuIEEgKipiaW1vZGFsIHRyZW5kKiogaXMgb2JzZXJ2ZWQsIHdpdGggcGVha3MgYXQgKiowIGFuZCAyNS0zMCBwb3B1bGFyaXR5KiosIHN1Z2dlc3RpbmcgdHdvIGRpc3RpbmN0IGdyb3VwcyBvZiBzb25ncy4gIA0KNC4gUG9wdWxhcml0eSAqKmRlY2xpbmVzIGdyYWR1YWxseSoqIGJleW9uZCA1MCwgc2hvd2luZyB0aGF0ICoqbWFpbnN0cmVhbSBzdWNjZXNzIGlzIHJhcmUgYW5kIGhpZ2hseSBjb21wZXRpdGl2ZSoqLg0KDQoNCg0KIyBDaGVja2luZyBob3cgcG9wdWxhcml0eSBjb3JyZWxhdGVzIHdpdGggb3RoZXIgbnVtZXJpY2FsIGZlYXR1cmVzLg0KYGBge3J9DQojIFNlbGVjdCBudW1lcmljIGNvbHVtbnMgb25seQ0KbnVtZXJpY19kYXRhIDwtIGRhdGEgJT4lIHNlbGVjdF9pZihpcy5udW1lcmljKQ0KDQojIENvbXB1dGUgY29ycmVsYXRpb24gbWF0cml4DQpjb3JfbWF0cml4IDwtIGNvcihudW1lcmljX2RhdGEsIHVzZSA9ICJjb21wbGV0ZS5vYnMiKQ0KDQojIFNob3cgY29ycmVsYXRpb24gb2YgcG9wdWxhcml0eSB3aXRoIG90aGVyIGZlYXR1cmVzDQpjb3JfbWF0cml4WyJwb3B1bGFyaXR5IiwgXQ0KYGBgDQoNClBvcHVsYXJpdHkgaGFzICoqbm8gc3Ryb25nIGNvcnJlbGF0aW9uKiogd2l0aCBhbnkgbnVtZXJpY2FsIGZlYXR1cmUsIHRob3VnaCAqKnNwZWVjaGluZXNzICgtMC4xMTcpIGFuZCBsaXZlbmVzcyAoLTAuMTIxKSBzaG93IHNsaWdodCBuZWdhdGl2ZSBlZmZlY3RzKiosIHdoaWxlICoqZGFuY2VhYmlsaXR5ICgwLjA0NCkgYW5kIGVuZXJneSAoMC4wNDQpIGhhdmUgd2VhayBwb3NpdGl2ZSBpbXBhY3RzKiouIA0KDQoNCiMgVmlzdWFsIC0gMg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSwgYWVzKHggPSBkYW5jZWFiaWxpdHksIHkgPSBwb3B1bGFyaXR5KSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMC41LCBjb2xvciA9ICJyZWQiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIGxhYnModGl0bGUgPSAiRGFuY2VhYmlsaXR5IHZzIFBvcHVsYXJpdHkiLCB4ID0gIkRhbmNlYWJpbGl0eSIsIHkgPSAiUG9wdWxhcml0eSIpDQpgYGANClRoZSBzY2F0dGVyIHBsb3Qgc2hvd3MgYSAqKndlYWsgcG9zaXRpdmUgY29ycmVsYXRpb24qKiBiZXR3ZWVuIGRhbmNlYWJpbGl0eSBhbmQgcG9wdWxhcml0eSwgbWVhbmluZyB0cmFja3Mgd2l0aCBoaWdoZXIgZGFuY2VhYmlsaXR5ICoqdGVuZCB0byBiZSBtb3JlIHBvcHVsYXIqKiBidXQgbm90IHN0cm9uZ2x5LiBUaGUgcG9pbnRzIGFyZSB3aWRlbHkgZGlzcGVyc2VkLCBzdWdnZXN0aW5nICoqcG9wdWxhcml0eSBpcyBpbmZsdWVuY2VkIGJ5IG11bHRpcGxlIGZhY3RvcnMqKiBiZXlvbmQgZGFuY2VhYmlsaXR5LiANCg0KIyBTZWxlY3QgYSBjYXRlZ29yaWNhbCBjb2x1bW4gb2YgZGF0YSAoZXhwbGFuYXRvcnkgdmFyaWFibGUpIHRoYXQgeW91IGV4cGVjdCBtaWdodCBpbmZsdWVuY2UgdGhlIHJlc3BvbnNlIHZhcmlhYmxlLg0KDQpXZSBuZWVkIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgdGhhdCBtaWdodCBpbmZsdWVuY2UgcG9wdWxhcml0eS4gQSBnb29kIGNob2ljZSBpcyAia2V5IiAobXVzaWNhbCBrZXkgb2YgdGhlIHRyYWNrKSwgc2luY2UgZGlmZmVyZW50IG11c2ljYWwga2V5cyBtaWdodCBhZmZlY3QgbGlzdGVuZXIgcHJlZmVyZW5jZXMuDQpgYGB7cn0NCg0KIyBDaGVjayB1bmlxdWUgdmFsdWVzIGluIHRoZSBrZXkgY29sdW1uDQp1bmlxdWUoZGF0YSRrZXkpDQoNCiMgQ291bnQgb2NjdXJyZW5jZXMgb2YgZWFjaCBrZXkgdG8gZW5zdXJlIHdlIGhhdmUgZW5vdWdoIGRhdGENCnRhYmxlKGRhdGEka2V5KQ0KDQojIElmIHRvbyBtYW55IGNhdGVnb3JpZXMsIGNvbnNvbGlkYXRlIGludG8gTWFqb3IgYW5kIE1pbm9yIGtleXMNCmRhdGEka2V5X2dyb3VwZWQgPC0gaWZlbHNlKGRhdGEkbW9kZSA9PSAxLCAiTWFqb3IiLCAiTWlub3IiKQ0KDQojIENoZWNrIHRoZSBuZXcgZ3JvdXBpbmcNCnRhYmxlKGRhdGEka2V5X2dyb3VwZWQpDQoNCmBgYA0KDQpUaGUgKiptdXNpY2FsIGtleSoqIGlzIGEgZ29vZCBjYXRlZ29yaWNhbCB2YXJpYWJsZSB0byBhbmFseXplIGl0cyBpbXBhY3Qgb24gcG9wdWxhcml0eS4gU2luY2UgdGhlcmUgYXJlICoqMTIgZGlzdGluY3Qga2V5cyoqLCB3ZSBjYW4gc2ltcGxpZnkgYnkgZ3JvdXBpbmcgdGhlbSBpbnRvICoqTWFqb3IgYW5kIE1pbm9yKiogdXNpbmcgdGhlIGBtb2RlYCBjb2x1bW4uIFRoZSBkYXRhc2V0IGhhcyAqKjUsMjEwIE1ham9yIGtleSB0cmFja3MqKiBhbmQgKiozLDc5MCBNaW5vciBrZXkgdHJhY2tzKiosIGVuc3VyaW5nIGVub3VnaCBkYXRhIGZvciBjb21wYXJpc29uLiBBbmFseXppbmcgcG9wdWxhcml0eSBhY3Jvc3MgdGhlc2UgZ3JvdXBzIGNhbiByZXZlYWwgaWYgKipsaXN0ZW5lcnMgcHJlZmVyIGNlcnRhaW4ga2V5cyBtb3JlKiouDQoNCiMgRGV2aXNlIGEgbnVsbCBoeXBvdGhlc2lzIGZvciBhbiBBTk9WQSB0ZXN0IGdpdmVuIHRoaXMgc2l0dWF0aW9uLiBUZXN0IHRoaXMgaHlwb3RoZXNpcyB1c2luZyBBTk9WQSwgYW5kIHN1bW1hcml6ZSB5b3VyIHJlc3VsdHMgKGUuZy4sIHVzZSBib3ggcGxvdHMpLiBCZSBjbGVhciBhYm91dCBob3cgdGhlIFIgb3V0cHV0IHJlbGF0ZXMgdG8geW91ciBjb25jbHVzaW9ucy4NCg0KYGBge3J9DQoNCmBgYA0KDQpGb3IgYW4gQU5PVkEgdGVzdCwgd2UgY29tcGFyZSBtZWFucyBvZiBwb3B1bGFyaXR5IGFjcm9zcyBkaWZmZXJlbnQga2V5cy4NCg0KYGBge3J9DQojIFJ1biBBTk9WQSB0ZXN0DQphbm92YV9yZXN1bHQgPC0gYW92KHBvcHVsYXJpdHkgfiBrZXlfZ3JvdXBlZCwgZGF0YSA9IGRhdGEpDQoNCiMgUHJpbnQgQU5PVkEgc3VtbWFyeQ0Kc3VtbWFyeShhbm92YV9yZXN1bHQpDQpgYGANCi0gKipOdWxsIEh5cG90aGVzaXMgKEjigoApKio6IFRoZSBtZWFuIHBvcHVsYXJpdHkgb2YgdHJhY2tzIGRvZXMgKipub3QqKiBzaWduaWZpY2FudGx5IGRpZmZlciBiZXR3ZWVuIE1ham9yIGFuZCBNaW5vciBrZXlzLiAgDQotICoqQWx0ZXJuYXRpdmUgSHlwb3RoZXNpcyAoSOKCgSkqKjogVGhlcmUgKippcyoqIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBpbiBwb3B1bGFyaXR5IGJldHdlZW4gTWFqb3IgYW5kIE1pbm9yIGtleXMuICANCg0KVGhlICoqQU5PVkEgdGVzdCBvdXRwdXQqKiBzaG93cyBhbiBGLXZhbHVlIG9mICoqMy42NzQqKiB3aXRoIGEgcC12YWx1ZSBvZiAqKjAuMDU1MyoqLiBTaW5jZSAqKnAgPiAwLjA1KiosIHdlIGZhaWwgdG8gcmVqZWN0IEjigoAsIG1lYW5pbmcgKip0aGVyZSBpcyBubyBzdHJvbmcgZXZpZGVuY2UgdGhhdCBrZXkgdHlwZSAoTWFqb3IgdnMuIE1pbm9yKSBpbmZsdWVuY2VzIHBvcHVsYXJpdHkgc2lnbmlmaWNhbnRseSoqLiBIb3dldmVyLCBzaW5jZSB0aGUgcC12YWx1ZSBpcyBjbG9zZSB0byAwLjA1LCB0aGVyZSBtaWdodCBiZSBhIHdlYWsgdHJlbmQgd29ydGggZnVydGhlciBleHBsb3JhdGlvbi4NCg0KDQojIyMgSW50ZXJwcmV0YXRpb24gb2YgQU5PVkEgUmVzdWx0czogIA0KVGhlIEFOT1ZBIHRlc3QgZXZhbHVhdGVzIHdoZXRoZXIgdGhlcmUgaXMgYSBzaWduaWZpY2FudCBkaWZmZXJlbmNlIGluICoqcG9wdWxhcml0eSoqIGJldHdlZW4gKipNYWpvciBhbmQgTWlub3Iga2V5cyoqLiBUaGUgKipwLXZhbHVlIG9mIDAuMDg2NCoqIGlzIGFib3ZlIHRoZSBjb252ZW50aW9uYWwgKio1JSBzaWduaWZpY2FuY2UgbGV2ZWwqKiBidXQgYmVsb3cgKioxMCUqKiwgaW5kaWNhdGluZyB3ZWFrIGV2aWRlbmNlIG9mIGFuIGVmZmVjdC4gIA0KDQojIyMgQ29uY2x1c2lvbjogIA0KU2luY2UgdGhlICoqcC12YWx1ZSBpcyBncmVhdGVyIHRoYW4gMC4wNSoqLCB0aGVyZSBpcyAqKm5vIHN0cm9uZyBzdGF0aXN0aWNhbCBldmlkZW5jZSoqIHRoYXQga2V5IHR5cGUgKE1ham9yIG9yIE1pbm9yKSBpbmZsdWVuY2VzIGEgdHJhY2sncyBwb3B1bGFyaXR5LiBIb3dldmVyLCBhdCBhICoqMTAlIHNpZ25pZmljYW5jZSBsZXZlbCoqLCB0aGUgcmVzdWx0cyBzdWdnZXN0IGEgc2xpZ2h0IHRyZW5kLCB0aG91Z2ggbm90IGNvbmNsdXNpdmUuIEtleSB0eXBlIGFsb25lIGRvZXMgbm90IGFwcGVhciB0byBiZSBhIG1ham9yIGZhY3RvciBpbiBkZXRlcm1pbmluZyBhIHNvbmfigJlzIHBvcHVsYXJpdHkuIE90aGVyIGVsZW1lbnRzLCBzdWNoIGFzICoqZ2VucmUsIGx5cmljcywgdGVtcG8sIG9yIGFydGlzdCByZWNvZ25pdGlvbioqLCBsaWtlbHkgaGF2ZSBhIHN0cm9uZ2VyIGluZmx1ZW5jZSBvbiBsaXN0ZW5lciBwcmVmZXJlbmNlcy4NCg0KYGBge3J9DQojIEJveCBwbG90IG9mIFBvcHVsYXJpdHkgdnMgS2V5IFR5cGUNCmdncGxvdChkYXRhLCBhZXMoeCA9IGtleV9ncm91cGVkLCB5ID0gcG9wdWxhcml0eSwgZmlsbCA9IGtleV9ncm91cGVkKSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIGxhYnModGl0bGUgPSAiUG9wdWxhcml0eSBieSBLZXkgVHlwZSIsIHggPSAiS2V5IFR5cGUiLCB5ID0gIlBvcHVsYXJpdHkiKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIk1ham9yIiA9ICJibHVlIiwgIk1pbm9yIiA9ICJyZWQiKSkNCmBgYA0KVGhlICoqYm94IHBsb3QqKiB2aXN1YWxpemVzIHRoZSBkaXN0cmlidXRpb24gb2YgKipwb3B1bGFyaXR5IHNjb3JlcyoqIGZvciBzb25ncyBpbiAqKk1ham9yIChibHVlKSBhbmQgTWlub3IgKHJlZCkga2V5cyoqLiBUaGUgbWVkaWFuIHBvcHVsYXJpdHkgaXMgbmVhcmx5IHRoZSBzYW1lIGZvciBib3RoIGdyb3Vwcywgd2l0aCBhIHNpbWlsYXIgaW50ZXJxdWFydGlsZSByYW5nZSBhbmQgc3ByZWFkLCBpbmRpY2F0aW5nIHRoYXQgKiprZXkgdHlwZSAoTWFqb3Igb3IgTWlub3IpIGRvZXMgbm90IHN0cm9uZ2x5IGluZmx1ZW5jZSBwb3B1bGFyaXR5KiouDQoNCiMgRmluZCBhIHNpbmdsZSBjb250aW51b3VzIChvciBvcmRlcmVkIGludGVnZXIsIG5vbi1iaW5hcnkpIGNvbHVtbiBvZiBkYXRhIHRoYXQgbWlnaHQgaW5mbHVlbmNlIHRoZSByZXNwb25zZSB2YXJpYWJsZS4gTWFrZSBzdXJlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGlzIHZhcmlhYmxlIGFuZCB0aGUgcmVzcG9uc2UgaXMgcm91Z2hseSBsaW5lYXIuDQoNCmBgYHtyfQ0KIyBDaGVjayBzdHJ1Y3R1cmUgb2YgZGFuY2VhYmlsaXR5DQpzdHIoZGF0YSRkYW5jZWFiaWxpdHkpDQoNCiMgU3VtbWFyeSBzdGF0aXN0aWNzDQpzdW1tYXJ5KGRhdGEkZGFuY2VhYmlsaXR5KQ0KDQojIENoZWNrIGNvcnJlbGF0aW9uIHdpdGggcG9wdWxhcml0eQ0KY29yKGRhdGEkcG9wdWxhcml0eSwgZGF0YSRkYW5jZWFiaWxpdHksIHVzZT0iY29tcGxldGUub2JzIikNCmBgYA0KDQojIyMgKipJbnRlcnByZXRhdGlvbiBvZiBBbmFseXNpcyBvbiBEYW5jZWFiaWxpdHkgYW5kIFBvcHVsYXJpdHkqKg0KDQotICoqRGFuY2VhYmlsaXR5KiogaXMgYSBjb250aW51b3VzIHZhcmlhYmxlIHJhbmdpbmcgZnJvbSAqKjAuMDY0NyB0byAwLjk3MTAqKiwgd2l0aCBhIG1lZGlhbiBvZiAqKjAuNjU4KiouDQotIFRoZSAqKmNvcnJlbGF0aW9uIGJldHdlZW4gZGFuY2VhYmlsaXR5IGFuZCBwb3B1bGFyaXR5KiogaXMgKiowLjA0MzcqKiwgaW5kaWNhdGluZyBhICoqdmVyeSB3ZWFrIHBvc2l0aXZlIHJlbGF0aW9uc2hpcCoqLg0KLSBTaW5jZSB0aGUgY29ycmVsYXRpb24gaXMgY2xvc2UgdG8gKip6ZXJvKiosIGRhbmNlYWJpbGl0eSAqKmRvZXMgbm90IHN0cm9uZ2x5IGluZmx1ZW5jZSBwb3B1bGFyaXR5KiosIG1lYW5pbmcgaGlnaGVyIGRhbmNlYWJpbGl0eSBkb2VzIG5vdCBuZWNlc3NhcmlseSBsZWFkIHRvIG1vcmUgcG9wdWxhciBzb25ncy4NCg0KDQpTdGVwIDI6IENoZWNrIGZvciBMaW5lYXJpdHkNCkJlZm9yZSBmaXR0aW5nIGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwsIHdlIHZpc3VhbGl6ZSB0aGUgcmVsYXRpb25zaGlwLg0KYGBge3J9DQoNCiMgU2NhdHRlciBwbG90IHdpdGggdHJlbmQgbGluZQ0KZ2dwbG90KGRhdGEsIGFlcyh4ID0gZGFuY2VhYmlsaXR5LCB5ID0gcG9wdWxhcml0eSkpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNCkgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBjb2wgPSAiYmx1ZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgbGFicyh0aXRsZSA9ICJEYW5jZWFiaWxpdHkgdnMuIFBvcHVsYXJpdHkiLA0KICAgICAgIHggPSAiRGFuY2VhYmlsaXR5IiwNCiAgICAgICB5ID0gIlBvcHVsYXJpdHkiKQ0KDQpgYGANCg0KIyMjICoqSW50ZXJwcmV0YXRpb24gb2YgdGhlIFNjYXR0ZXIgUGxvdDogRGFuY2VhYmlsaXR5IHZzLiBQb3B1bGFyaXR5KioNCi0gRWFjaCAqKmRvdCoqIHJlcHJlc2VudHMgYSBzb25nLCBwbG90dGluZyBpdHMgKipkYW5jZWFiaWxpdHkqKiAoeC1heGlzKSBhZ2FpbnN0ICoqcG9wdWxhcml0eSoqICh5LWF4aXMpLiAgDQotIFRoZSAqKmJsdWUgbGluZSoqIHJlcHJlc2VudHMgdGhlICoqbGluZWFyIHRyZW5kKiogZml0dGVkIHVzaW5nIGEgcmVncmVzc2lvbiBtb2RlbC4gIA0KLSBUaGUgKipsaW5lIGlzIG5lYXJseSBmbGF0KiosIGluZGljYXRpbmcgYSAqKnZlcnkgd2VhayBwb3NpdGl2ZSBjb3JyZWxhdGlvbioqLCB3aGljaCBhbGlnbnMgd2l0aCB0aGUgY2FsY3VsYXRlZCBjb3JyZWxhdGlvbiBvZiAqKjAuMDQzNyoqLiAgDQotIFRoZSAqKnNwcmVhZCBvZiBwb2ludHMqKiBzaG93cyBubyBjbGVhciBsaW5lYXIgcGF0dGVybiwgc3VnZ2VzdGluZyAqKmRhbmNlYWJpbGl0eSBpcyBub3QgYSBzdHJvbmcgcHJlZGljdG9yIG9mIHBvcHVsYXJpdHkqKi4NCg0KDQpgYGB7cn0NCiMgRml0IHRoZSBtb2RlbA0KbG1fbW9kZWwgPC0gbG0ocG9wdWxhcml0eSB+IGRhbmNlYWJpbGl0eSwgZGF0YSA9IGRhdGEpDQoNCiMgRGlzcGxheSBtb2RlbCBzdW1tYXJ5DQpzdW1tYXJ5KGxtX21vZGVsKQ0KYGBgDQoNClRoZSBsaW5lYXIgbW9kZWwgc2hvd3MgdGhhdCBkYW5jZWFiaWxpdHkgaGFzIGEgc21hbGwgYnV0IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgZWZmZWN0IG9uIHBvcHVsYXJpdHkgKFwoIHAgPCAwLjAwMSBcKSkuIEhvd2V2ZXIsIHRoZSAqKlLCsiB2YWx1ZSAoMC4wMDE5MSkqKiBpcyBleHRyZW1lbHkgbG93LCBtZWFuaW5nIGRhbmNlYWJpbGl0eSBleHBsYWlucyAqKm9ubHkgMC4xOSUgb2YgdGhlIHZhcmlhdGlvbioqIGluIHBvcHVsYXJpdHkuIFRoZSBjb2VmZmljaWVudCAoNi4xNikgc3VnZ2VzdHMgdGhhdCBpbmNyZWFzaW5nIGRhbmNlYWJpbGl0eSBieSAxIHVuaXQgcmFpc2VzIHBvcHVsYXJpdHkgYnkgKio2LjE2IHBvaW50cyBvbiBhdmVyYWdlKiouIEhvd2V2ZXIsIHRoZSAqKmhpZ2ggcmVzaWR1YWwgc3RhbmRhcmQgZXJyb3IgKDI0LjI3KSoqIGluZGljYXRlcyBsYXJnZSB1bmV4cGxhaW5lZCB2YXJpYWJpbGl0eS4gVGhpcyBzdWdnZXN0cyB0aGF0IGRhbmNlYWJpbGl0eSBhbG9uZSBpcyBub3QgYSBzdHJvbmcgcHJlZGljdG9yLCBhbmQgb3RoZXIgZmFjdG9ycyBzaG91bGQgYmUgY29uc2lkZXJlZCBmb3IgYmV0dGVyIG1vZGVsaW5nLg0KDQojIFJlc2lkdWFsIEFuYWx5c2lzDQpgYGB7cn0NCiMgUGxvdCByZXNpZHVhbHMgdG8gY2hlY2sgbW9kZWwgYXNzdW1wdGlvbnMNCnBhcihtZnJvdyA9IGMoMiwyKSkNCnBsb3QobG1fbW9kZWwpDQpgYGANCg0KVGhlIHJlc2lkdWFsIGFuYWx5c2lzIHBsb3RzIGFzc2VzcyB0aGUgYXNzdW1wdGlvbnMgb2YgbGluZWFyIHJlZ3Jlc3Npb246ICANCg0KMS4gKipSZXNpZHVhbHMgdnMgRml0dGVkKio6IFNob3dzIGEgKipyYW5kb20gc2NhdHRlcioqIGFyb3VuZCB6ZXJvLCBidXQgc29tZSBjbHVzdGVyaW5nIGluZGljYXRlcyBwb3RlbnRpYWwgbm9uLWxpbmVhcml0eSBvciBoZXRlcm9zY2VkYXN0aWNpdHkuICANCjIuICoqUS1RIFBsb3QqKjogUmVzaWR1YWxzICoqZGV2aWF0ZSBmcm9tIHRoZSBub3JtYWwgbGluZSoqLCBzdWdnZXN0aW5nICoqbm9uLW5vcm1hbGl0eSoqIGluIGVycm9ycy4gIA0KMy4gKipTY2FsZS1Mb2NhdGlvbioqOiBSZXNpZHVhbHMgYXBwZWFyICoqZXZlbmx5IHNwcmVhZCoqLCBidXQgdGhlIHNsaWdodCB1cHdhcmQgdHJlbmQgc3VnZ2VzdHMgcG9zc2libGUgaGV0ZXJvc2NlZGFzdGljaXR5LiAgDQo0LiAqKlJlc2lkdWFscyB2cyBMZXZlcmFnZSoqOiBObyBleHRyZW1lIGxldmVyYWdlIHBvaW50cywgYnV0IGEgZmV3IG9ic2VydmF0aW9ucyBtYXkgKippbmZsdWVuY2UgdGhlIG1vZGVsKiouICANCg0KT3ZlcmFsbCwgdGhlIHBsb3RzIGluZGljYXRlICoqdmlvbGF0aW9ucyBvZiBsaW5lYXIgcmVncmVzc2lvbiBhc3N1bXB0aW9ucyoqLCBzdWdnZXN0aW5nIGEgbW9yZSBjb21wbGV4IG1vZGVsIG1pZ2h0IGJlIG5lZWRlZC4NCkEgbXVsdGlwbGUgcmVncmVzc2lvbiBtb2RlbCB3aXRoIG1vcmUgZmVhdHVyZXMgKGUuZy4sIHRlbXBvLCBlbmVyZ3ksIGxvdWRuZXNzLCBzcGVlY2hpbmVzcykgaXMgbmVlZGVkIHRvIGJldHRlciB1bmRlcnN0YW5kIHBvcHVsYXJpdHkuDQoNCg0KDQoNCg0KDQoNCg==