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

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:

  1. Residuals vs Fitted: Shows a random scatter around zero, but some clustering indicates potential non-linearity or heteroscedasticity.
  2. Q-Q Plot: Residuals deviate from the normal line, suggesting non-normality in errors.
  3. Scale-Location: Residuals appear evenly spread, but the slight upward trend suggests possible heteroscedasticity.
  4. 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==