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(readr)
library(tidyverse)
── Attaching core tidyverse packages ──────────────────────────────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.4     ✔ tidyr     1.3.1
✔ purrr     1.0.2     ── 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)
#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
# Listing out all the column_names of the dataset
names(data)
 [1] "X"                "track_id"         "artists"          "album_name"       "track_name"       "popularity"      
 [7] "duration_ms"      "explicit"         "danceability"     "energy"           "key"              "loudness"        
[13] "mode"             "speechiness"      "acousticness"     "instrumentalness" "liveness"         "valence"         
[19] "tempo"            "time_signature"   "track_genre"     

1. A list of at least 3 columns (or values) in your data which are unclear until you read the documentation.

  1. At first, by seeing the dataset, the column name X seems to be unknown. Later, after seeing values in it, I came to recognise it as the index. Also, again I wondered as ‘track_id’ can act as a unique identifier, but, after going through documentation, I found that track_id is unique id generated for each music track and X acts as an identifier of it in the dataset.It will be unclear whether this ID is unique for each track, reused across different versions, or linked to an external database.

  2. “mode” – The meaning of “mode” is unclear without refering to documentation. In music-related datasets, it refers to the musical mode (major(1) or minor(0)). This classification is based on Western music theory, where a major scale tends to sound positive, while a minor scale often conveys a more somber or emotional feel. 0 → Minor mode (associated with darker, sadder, or more melancholic tones) 1 → Major mode (associated with brighter, happier, or more uplifting tones)

  3. “valence” – This term isn’t self-explanatory. In Spotify dataset, “valence” represents the musical positivity of a track. Valence values are decimals which ranges from 0 to 1 varying from sad to happy state. In simpler terms, valence quantifies the emotional tone of a song, with higher values indicating a more upbeat, joyful feel, and lower values indicating a more somber, introspective vibe.

  4. “Time_signature” - It’s value is likely an integer that reflects one of these time signatures: 4: Represents 4/4 time (most common in pop and rock music-meaning there are 4 beats per measure, and each beat is a quarter note) 3: Represents 3/4 time (waltz or certain classical music-typically found in waltzes, meaning there are 3 beats per measure, each a quarter note) Other values (like 6 or 7) might appear but are less common.Often used in ballads or faster-paced tunes, meaning there are 6 beats per measure, and each beat is an eighth note.

Why do you think they chose to encode the data the way they did? What could have happened if you didn’t read the documentation?

The encoding choices in the music dataset, such as using binary values for “mode” (0 for minor, 1 for major), a 0-1 range for ”valence”** (emotional positivity), and integers for “time_signature”, are likely made to efficiently represent important musical attributes in a compact and meaningful way. These encodings allow for quick categorization and analysis, with “mode” distinguishing major and minor scales, “valence” capturing the emotional tone of the track, and “time_signature” reflecting the rhythmic structure of the song.

Without reading the documentation, however,one could easily misinterpret these features—such as assuming “mode” is an arbitrary number or misunderstanding “valence” as a technical feature. This could lead to confusion and incorrect analysis, emphasizing the importance of understanding the context behind the data to ensure accurate insights.

2. At least one element or your data that is unclear even after reading the documentation.

Even after reading the documentation of spotify dataset, one element of the data that might still remain unclear is the “track_id”. While it’s clear that this is an identifier for each track, the exact nature of this ID is ambiguous. For example, it is uncertain whether it is a globally unique identifier, if it’s linked to an external database, or if the same track might share the same ID in different contexts (such as different versions of a song or remixes). To clarify this, we need to explore the dataset further by checking for duplicates, cross-referencing with external sources like youtube music or music databases, or even analyzing the distribution and pattern of these IDs to understand their uniqueness and relevance.

3. Build at least two visualizations which use a column of data that is affected by the issue you brought up in bullet #2, above. In these visualizations, find a way to highlight the issue from different perspectives, explain what is unclear, and why it might be concerning.

# Creates a frequency table of track_ids
track_id_count <- table(data$track_id)

# Get the top 10 most frequent track_ids
top_10_tracks <- head(sort(track_id_count, decreasing = TRUE), 10)

# Creates a bar plot to visualize the top 10 most frequent track_ids
top_10_plot <- ggplot(as.data.frame(top_10_tracks), aes(x = Var1, y = Freq)) +
  geom_bar(stat = "identity", fill = "skyblue") +
  theme_minimal() +
  labs(title = "Top 10 Most Frequent Track IDs", x = "Track ID", y = "Frequency") +
  theme(axis.text.x = element_text(angle = 90, hjust = 1))


# Display the plot
print(top_10_plot)

The top 10 track_id frequency plot can reveal repeated tracks, indicating possible duplicates or multiple versions of the same song. If certain track_id appear too often, it suggests data redundancy, which can skew analysis. This could also indicate a data quality issue, as track_id should ideally be unique. Addressing these repetitions through data cleaning would ensure more accurate insights.

# Create a table of frequencies for mode (0 and 1)
mode_count <- table(data$mode)

# Create a pie chart to visualize the distribution of major (1) vs minor (0) tracks
mode_plot <- ggplot(as.data.frame(mode_count), aes(x = "", y = Freq, fill = factor(Var1))) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar(theta = "y") +
  scale_fill_manual(values = c("green", "orange")) +
  labs(title = "Distribution of Mode (Major vs Minor)", fill = "Mode") +
  theme_minimal()

# Display the plot
print(mode_plot)

The pie chart visually shows the distribution of mode values (0 for minor, 1 for major), highlighting how many tracks are in each category. If one category (minor or major) overwhelmingly dominates, it clarifies any potential imbalance in the dataset. The chart provides a clear overview of the mode feature, helping to ensure it’s interpreted correctly. It also addresses any uncertainty about the proportion of songs in major vs minor modes.

# Select relevant columns for the heatmap (danceability, energy, loudness, tempo, etc.)
df_selected <- data[, c("danceability", "energy", "loudness", "tempo", "popularity")]

# Calculate the correlation matrix for the selected columns
correlation_matrix <- cor(df_selected, use = "complete.obs")

# Create a heatmap to visualize the correlation between danceability and other features
library(reshape2)
library(ggplot2)
library(RColorBrewer)

# Melt the correlation matrix for ggplot2
cor_melted <- melt(correlation_matrix)

# Create a heatmap
heatmap_plot <- ggplot(cor_melted, aes(Var1, Var2, fill = value)) +
  geom_tile() +
  scale_fill_gradient2(low = "blue", high = "red", mid = "white", midpoint = 0) +
  theme_minimal() +
  labs(title = "Correlation Heatmap for Danceability and Other Features",
       x = "Feature", y = "Feature") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

# Display the plot
print(heatmap_plot)

This heatmap helps clarify if the track_id represents meaningful variations in musical features or if there’s an overrepresentation of certain track_ids that might skew the dataset. By aggregating values for each track, the heatmap can reveal patterns that indicate whether track_id is simply an identifier or if it plays a larger role in distinguishing track characteristics. Since track_id likely represents different songs, we can use a heatmap to see if any track_id has distinct patterns across features such as popularity, energy, or danceability. By aggregating values of other columns by track_id, we can observe if certain tracks dominate specific ranges of values.

There seems certain risks and respective solutions for them -

  1. Risk: Overrepresentation of Certain Tracks Solution: Ensure each track_id represents a unique song.
  2. Risk: Data Redundancy Solution: Filter out redundant entries and aggregate data to represent each song only once.
  3. Risk: Inaccurate Aggregation of Multiple Versions Solution: Carefully aggregate features by track_id, ensuring different versions of the same song are handled appropriately.
  4. Risk: Overfitting in Models Solution: Use cross-validation techniques to ensure the model generalizes well and is not biased by repeated tracks.

4. For at least two categorical columns, check for examples of the following, and describe what you find:

Are there explicitly missing rows?

Are there implicitly missing rows?

Are there empty groups?

# Check for missing values (NA) in each column of the dataset
missing_values <- sapply(data, function(x) sum(is.na(x)))

# Display the count of missing values per column
print(missing_values)

a. Artists Column

  1. Name of the categorical column observed: artists

  2. Explicit missing rows: There are no explicitly missing rows in the artists column. All rows contain an artist name.

  3. Implicit missing rows: There don’t appear to be any implicitly missing rows. The dataset seems to include a wide variety of artists without any obvious omissions.

  4. Empty groups: There are no empty groups in the artists column. Each row has an associated artist.

  5. Additional observation: The artists column contains a mix of individual artists (e.g., “Gen Hoshino”, “Jason Mraz”) and collaborations (e.g., “Ingrid Michaelson; ZAYN”).

b. Track Genre Column

  1. Name of the categorical column observed: track_genre

  2. Explicit missing rows: There are no explicitly missing rows in the track_genre column. All rows have a genre assigned.

  3. Implicit missing rows: There don’t appear to be any implicitly missing rows in terms of genres. However, it’s worth noting that all tracks in the visible data are labeled as “acoustic”, which might suggest that other genres are implicitly missing from this dataset.

  4. Empty groups: There are no empty groups in the track_genre column. All rows have the genre “acoustic” assigned.

  5. Additional observation: The lack of variety in the track_genre column (all tracks labeled as “acoustic”) might indicate that this dataset is focused specifically on acoustic tracks or that there’s an issue with the genre classification in the data.

5.For at least one continuous column, what would you define as an outlier and why?

To analyze outliers in a continuous column, selecting ‘popularity’ column, which contains numerical values ranging from 0 to 82.

An outlier in the popularity column can be defined as a value that falls significantly outside the typical range of popularity scores for the tracks in this dataset.

Here’s how we can define outliers using the IQR method: Compute the Interquartile Range (IQR) = Q3 - Q1= 60 - 30 = 30 Define the lower bound as Q1 - 1.5 * IQR = 30 - 1.5 * 30 = -15 Define the upper bound as Q3 + 1.5 * IQR = 60 + 1.5 * 30 = 105

Any value below the lower bound or above the upper bound is considered an outlier. Since popularity scores are always non-negative and have a maximum of 100, we can adjust our definition: Any popularity score below 0 (which isn’t possible in this case) or above 105 would be considered an outlier.

In this dataset, we don’t see any values above 105, so there are no upper outliers. However, tracks with very low popularity scores (close to 0) could be considered as lower outliers.

LS0tDQp0aXRsZTogIkRhdGEgRGl2ZSA1Ig0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCmBgYHtyfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoY29uZmxpY3RlZCkNCmBgYA0KDQpgYGB7cn0NCiNSZWFkaW5nIHRoZSBkYXRhIHNldA0KZGF0YSA8LSByZWFkLmNzdigiZGF0YXNldC5jc3YiKQ0KY29uZmxpY3RlZDo6Y29uZmxpY3RzX3ByZWZlcihkcGx5cjo6ZmlsdGVyKQ0KIyBGaWx0ZXJpbmcgZGF0YXNldCB3aGVyZSBleHBsaWNpdCBpcyAiVHJ1ZSIgYW5kIHRha2luZyBhIHNhbXBsZSBvZiA5LDAwMCByb3dzDQpzYW1wbGVfZGF0YSA8LSBkYXRhIHw+IGZpbHRlcihleHBsaWNpdCA9PSAiVHJ1ZSIpIHw+IHNhbXBsZV9uKDkwMDApDQpkYXRhIDwtIHNhbXBsZV9kYXRhDQpkYXRhDQpgYGANCg0KYGBge3J9IA0KIyBMaXN0aW5nIG91dCBhbGwgdGhlIGNvbHVtbl9uYW1lcyBvZiB0aGUgZGF0YXNldA0KbmFtZXMoZGF0YSkNCmBgYA0KIyMjIDEuIEEgbGlzdCBvZiBhdCBsZWFzdCAzIGNvbHVtbnMgKG9yIHZhbHVlcykgaW4geW91ciBkYXRhIHdoaWNoIGFyZSB1bmNsZWFyIHVudGlsIHlvdSByZWFkIHRoZSBkb2N1bWVudGF0aW9uLg0KDQoxLiBBdCBmaXJzdCwgYnkgc2VlaW5nIHRoZSBkYXRhc2V0LCB0aGUgY29sdW1uIG5hbWUgWCBzZWVtcyB0byBiZSB1bmtub3duLg0KTGF0ZXIsIGFmdGVyIHNlZWluZyB2YWx1ZXMgaW4gaXQsIEkgY2FtZSB0byByZWNvZ25pc2UgaXQgYXMgdGhlIGluZGV4Lg0KQWxzbywgYWdhaW4gSSB3b25kZXJlZCBhcyAndHJhY2tfaWQnIGNhbiBhY3QgYXMgYSB1bmlxdWUgaWRlbnRpZmllciwgYnV0LCBhZnRlciBnb2luZyB0aHJvdWdoIGRvY3VtZW50YXRpb24sIEkgZm91bmQgdGhhdCB0cmFja19pZCBpcyB1bmlxdWUgaWQgZ2VuZXJhdGVkIGZvciBlYWNoIG11c2ljIHRyYWNrIGFuZCBYIGFjdHMgYXMgYW4gaWRlbnRpZmllciBvZiBpdCBpbiB0aGUgZGF0YXNldC5JdCB3aWxsIGJlIHVuY2xlYXIgd2hldGhlciB0aGlzIElEIGlzIHVuaXF1ZSBmb3IgZWFjaCB0cmFjaywgcmV1c2VkIGFjcm9zcyBkaWZmZXJlbnQgdmVyc2lvbnMsIG9yIGxpbmtlZCB0byBhbiBleHRlcm5hbCBkYXRhYmFzZS4NCg0KMi4gIm1vZGUiIOKAkyBUaGUgbWVhbmluZyBvZiAibW9kZSIgaXMgdW5jbGVhciB3aXRob3V0IHJlZmVyaW5nIHRvIGRvY3VtZW50YXRpb24uIEluIG11c2ljLXJlbGF0ZWQgZGF0YXNldHMsIGl0IHJlZmVycyB0byB0aGUgbXVzaWNhbCBtb2RlIChtYWpvcigxKSBvciBtaW5vcigwKSkuIFRoaXMgY2xhc3NpZmljYXRpb24gaXMgYmFzZWQgb24gV2VzdGVybiBtdXNpYyB0aGVvcnksIHdoZXJlIGEgbWFqb3Igc2NhbGUgdGVuZHMgdG8gc291bmQgcG9zaXRpdmUsIHdoaWxlIGEgbWlub3Igc2NhbGUgb2Z0ZW4gY29udmV5cyBhIG1vcmUgc29tYmVyIG9yIGVtb3Rpb25hbCBmZWVsLg0KMCDihpIgTWlub3IgbW9kZSAoYXNzb2NpYXRlZCB3aXRoIGRhcmtlciwgc2FkZGVyLCBvciBtb3JlIG1lbGFuY2hvbGljIHRvbmVzKQ0KMSDihpIgTWFqb3IgbW9kZSAoYXNzb2NpYXRlZCB3aXRoIGJyaWdodGVyLCBoYXBwaWVyLCBvciBtb3JlIHVwbGlmdGluZyB0b25lcykNCg0KMy4gInZhbGVuY2UiIOKAkyBUaGlzIHRlcm0gaXNuJ3Qgc2VsZi1leHBsYW5hdG9yeS4gSW4gU3BvdGlmeSBkYXRhc2V0LCAidmFsZW5jZSIgcmVwcmVzZW50cyB0aGUgbXVzaWNhbCBwb3NpdGl2aXR5IG9mIGEgdHJhY2suDQpWYWxlbmNlIHZhbHVlcyBhcmUgZGVjaW1hbHMgd2hpY2ggcmFuZ2VzIGZyb20gMCB0byAxIHZhcnlpbmcgZnJvbSBzYWQgdG8gaGFwcHkgc3RhdGUuIEluIHNpbXBsZXIgdGVybXMsIHZhbGVuY2UgcXVhbnRpZmllcyB0aGUgZW1vdGlvbmFsIHRvbmUgb2YgYSBzb25nLCB3aXRoIGhpZ2hlciB2YWx1ZXMgaW5kaWNhdGluZyBhIG1vcmUgdXBiZWF0LCBqb3lmdWwgZmVlbCwgYW5kIGxvd2VyIHZhbHVlcyBpbmRpY2F0aW5nIGEgbW9yZSBzb21iZXIsIGludHJvc3BlY3RpdmUgdmliZS4NCg0KNC4gIlRpbWVfc2lnbmF0dXJlIiAtIEl0J3MgdmFsdWUgaXMgbGlrZWx5IGFuIGludGVnZXIgdGhhdCByZWZsZWN0cyBvbmUgb2YgdGhlc2UgdGltZSBzaWduYXR1cmVzOiANCiAgICAgIDQ6IFJlcHJlc2VudHMgNC80IHRpbWUgKG1vc3QgY29tbW9uIGluIHBvcCBhbmQgcm9jayBtdXNpYy1tZWFuaW5nIHRoZXJlIGFyZSA0IGJlYXRzIHBlciBtZWFzdXJlLCBhbmQgZWFjaCBiZWF0IGlzIGEgcXVhcnRlciBub3RlKQ0KICAgICAgMzogUmVwcmVzZW50cyAzLzQgdGltZSAod2FsdHogb3IgY2VydGFpbiBjbGFzc2ljYWwgbXVzaWMtdHlwaWNhbGx5IGZvdW5kIGluIHdhbHR6ZXMsIG1lYW5pbmcgdGhlcmUgYXJlIDMgYmVhdHMgcGVyIG1lYXN1cmUsIGVhY2ggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGEgcXVhcnRlciBub3RlKQ0KICAgICAgT3RoZXIgdmFsdWVzIChsaWtlIDYgb3IgNykgbWlnaHQgYXBwZWFyIGJ1dCBhcmUgbGVzcyBjb21tb24uT2Z0ZW4gdXNlZCBpbiBiYWxsYWRzIG9yIGZhc3Rlci1wYWNlZCB0dW5lcywgbWVhbmluZyB0aGVyZSBhcmUgNiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmVhdHMgcGVyIG1lYXN1cmUsIGFuZCBlYWNoIGJlYXQgaXMgYW4gZWlnaHRoIG5vdGUuDQogDQogDQojIyMgV2h5IGRvIHlvdSB0aGluayB0aGV5IGNob3NlIHRvIGVuY29kZSB0aGUgZGF0YSB0aGUgd2F5IHRoZXkgZGlkPyBXaGF0IGNvdWxkIGhhdmUgaGFwcGVuZWQgaWYgeW91IGRpZG4ndCByZWFkIHRoZSBkb2N1bWVudGF0aW9uPw0KDQpUaGUgZW5jb2RpbmcgY2hvaWNlcyBpbiB0aGUgbXVzaWMgZGF0YXNldCwgc3VjaCBhcyB1c2luZyBiaW5hcnkgdmFsdWVzIGZvciAqKiJtb2RlIioqICgwIGZvciBtaW5vciwgMSBmb3IgbWFqb3IpLCBhICoqMC0xIHJhbmdlIGZvciAqKiJ2YWxlbmNlIioqIChlbW90aW9uYWwgcG9zaXRpdml0eSksIGFuZCBpbnRlZ2VycyBmb3IgKioidGltZV9zaWduYXR1cmUiKiosIGFyZSBsaWtlbHkgbWFkZSB0byBlZmZpY2llbnRseSByZXByZXNlbnQgaW1wb3J0YW50IG11c2ljYWwgYXR0cmlidXRlcyBpbiBhIGNvbXBhY3QgYW5kIG1lYW5pbmdmdWwgd2F5LiBUaGVzZSBlbmNvZGluZ3MgYWxsb3cgZm9yIHF1aWNrIGNhdGVnb3JpemF0aW9uIGFuZCBhbmFseXNpcywgd2l0aCAqKiJtb2RlIioqIGRpc3Rpbmd1aXNoaW5nIG1ham9yIGFuZCBtaW5vciBzY2FsZXMsICoqInZhbGVuY2UiKiogY2FwdHVyaW5nIHRoZSBlbW90aW9uYWwgdG9uZSBvZiB0aGUgdHJhY2ssIGFuZCAqKiJ0aW1lX3NpZ25hdHVyZSIqKiByZWZsZWN0aW5nIHRoZSByaHl0aG1pYyBzdHJ1Y3R1cmUgb2YgdGhlIHNvbmcuIA0KDQpXaXRob3V0IHJlYWRpbmcgdGhlIGRvY3VtZW50YXRpb24sIGhvd2V2ZXIsb25lIGNvdWxkIGVhc2lseSBtaXNpbnRlcnByZXQgdGhlc2UgZmVhdHVyZXPigJRzdWNoIGFzIGFzc3VtaW5nICoqIm1vZGUiKiogaXMgYW4gYXJiaXRyYXJ5IG51bWJlciBvciBtaXN1bmRlcnN0YW5kaW5nICoqInZhbGVuY2UiKiogYXMgYSB0ZWNobmljYWwgZmVhdHVyZS4gVGhpcyBjb3VsZCBsZWFkIHRvIGNvbmZ1c2lvbiBhbmQgaW5jb3JyZWN0IGFuYWx5c2lzLCBlbXBoYXNpemluZyB0aGUgaW1wb3J0YW5jZSBvZiB1bmRlcnN0YW5kaW5nIHRoZSBjb250ZXh0IGJlaGluZCB0aGUgZGF0YSB0byBlbnN1cmUgYWNjdXJhdGUgaW5zaWdodHMuDQoNCiMjIyAyLiBBdCBsZWFzdCBvbmUgZWxlbWVudCBvciB5b3VyIGRhdGEgdGhhdCBpcyB1bmNsZWFyIGV2ZW4gYWZ0ZXIgcmVhZGluZyB0aGUgZG9jdW1lbnRhdGlvbi4NCg0KRXZlbiBhZnRlciByZWFkaW5nIHRoZSBkb2N1bWVudGF0aW9uIG9mIHNwb3RpZnkgZGF0YXNldCwgb25lIGVsZW1lbnQgb2YgdGhlIGRhdGEgdGhhdCBtaWdodCBzdGlsbCByZW1haW4gdW5jbGVhciBpcyB0aGUgKioidHJhY2tfaWQiKiouIFdoaWxlIGl04oCZcyBjbGVhciB0aGF0IHRoaXMgaXMgYW4gaWRlbnRpZmllciBmb3IgZWFjaCB0cmFjaywgdGhlIGV4YWN0IG5hdHVyZSBvZiB0aGlzIElEIGlzIGFtYmlndW91cy4gRm9yIGV4YW1wbGUsIGl0IGlzIHVuY2VydGFpbiB3aGV0aGVyIGl0IGlzIGEgZ2xvYmFsbHkgdW5pcXVlIGlkZW50aWZpZXIsIGlmIGl04oCZcyBsaW5rZWQgdG8gYW4gZXh0ZXJuYWwgZGF0YWJhc2UsIG9yIGlmIHRoZSBzYW1lIHRyYWNrIG1pZ2h0IHNoYXJlIHRoZSBzYW1lIElEIGluIGRpZmZlcmVudCBjb250ZXh0cyAoc3VjaCBhcyBkaWZmZXJlbnQgdmVyc2lvbnMgb2YgYSBzb25nIG9yIHJlbWl4ZXMpLiBUbyBjbGFyaWZ5IHRoaXMsIHdlIG5lZWQgdG8gZXhwbG9yZSB0aGUgZGF0YXNldCBmdXJ0aGVyIGJ5IGNoZWNraW5nIGZvciBkdXBsaWNhdGVzLCBjcm9zcy1yZWZlcmVuY2luZyB3aXRoIGV4dGVybmFsIHNvdXJjZXMgbGlrZSB5b3V0dWJlIG11c2ljIG9yIG11c2ljIGRhdGFiYXNlcywgb3IgZXZlbiBhbmFseXppbmcgdGhlIGRpc3RyaWJ1dGlvbiBhbmQgcGF0dGVybiBvZiB0aGVzZSBJRHMgdG8gdW5kZXJzdGFuZCB0aGVpciB1bmlxdWVuZXNzIGFuZCByZWxldmFuY2UuDQoNCg0KIyMjIDMuIEJ1aWxkIGF0IGxlYXN0IHR3byB2aXN1YWxpemF0aW9ucyB3aGljaCB1c2UgYSBjb2x1bW4gb2YgZGF0YSB0aGF0IGlzIGFmZmVjdGVkIGJ5IHRoZSBpc3N1ZSB5b3UgYnJvdWdodCB1cCBpbiBidWxsZXQgIzIsIGFib3ZlLiBJbiB0aGVzZSB2aXN1YWxpemF0aW9ucywgZmluZCBhIHdheSB0byBoaWdobGlnaHQgdGhlIGlzc3VlIGZyb20gZGlmZmVyZW50IHBlcnNwZWN0aXZlcywgZXhwbGFpbiB3aGF0IGlzIHVuY2xlYXIsIGFuZCB3aHkgaXQgbWlnaHQgYmUgY29uY2VybmluZy4NCg0KYGBge3J9DQojIENyZWF0ZXMgYSBmcmVxdWVuY3kgdGFibGUgb2YgdHJhY2tfaWRzDQp0cmFja19pZF9jb3VudCA8LSB0YWJsZShkYXRhJHRyYWNrX2lkKQ0KDQojIEdldCB0aGUgdG9wIDEwIG1vc3QgZnJlcXVlbnQgdHJhY2tfaWRzDQp0b3BfMTBfdHJhY2tzIDwtIGhlYWQoc29ydCh0cmFja19pZF9jb3VudCwgZGVjcmVhc2luZyA9IFRSVUUpLCAxMCkNCg0KIyBDcmVhdGVzIGEgYmFyIHBsb3QgdG8gdmlzdWFsaXplIHRoZSB0b3AgMTAgbW9zdCBmcmVxdWVudCB0cmFja19pZHMNCnRvcF8xMF9wbG90IDwtIGdncGxvdChhcy5kYXRhLmZyYW1lKHRvcF8xMF90cmFja3MpLCBhZXMoeCA9IFZhcjEsIHkgPSBGcmVxKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJza3libHVlIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKHRpdGxlID0gIlRvcCAxMCBNb3N0IEZyZXF1ZW50IFRyYWNrIElEcyIsIHggPSAiVHJhY2sgSUQiLCB5ID0gIkZyZXF1ZW5jeSIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkNCg0KDQojIERpc3BsYXkgdGhlIHBsb3QNCnByaW50KHRvcF8xMF9wbG90KQ0KYGBgDQpUaGUgdG9wIDEwICoqdHJhY2tfaWQqKiBmcmVxdWVuY3kgcGxvdCBjYW4gcmV2ZWFsIHJlcGVhdGVkIHRyYWNrcywgaW5kaWNhdGluZyBwb3NzaWJsZSBkdXBsaWNhdGVzIG9yIG11bHRpcGxlIHZlcnNpb25zIG9mIHRoZSBzYW1lIHNvbmcuIElmIGNlcnRhaW4gKip0cmFja19pZCoqIGFwcGVhciB0b28gb2Z0ZW4sIGl0IHN1Z2dlc3RzIGRhdGEgcmVkdW5kYW5jeSwgd2hpY2ggY2FuIHNrZXcgYW5hbHlzaXMuIFRoaXMgY291bGQgYWxzbyBpbmRpY2F0ZSBhIGRhdGEgcXVhbGl0eSBpc3N1ZSwgYXMgKip0cmFja19pZCoqIHNob3VsZCBpZGVhbGx5IGJlIHVuaXF1ZS4gQWRkcmVzc2luZyB0aGVzZSByZXBldGl0aW9ucyB0aHJvdWdoIGRhdGEgY2xlYW5pbmcgd291bGQgZW5zdXJlIG1vcmUgYWNjdXJhdGUgaW5zaWdodHMuDQoNCmBgYHtyfQ0KIyBDcmVhdGUgYSB0YWJsZSBvZiBmcmVxdWVuY2llcyBmb3IgbW9kZSAoMCBhbmQgMSkNCm1vZGVfY291bnQgPC0gdGFibGUoZGF0YSRtb2RlKQ0KDQojIENyZWF0ZSBhIHBpZSBjaGFydCB0byB2aXN1YWxpemUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBtYWpvciAoMSkgdnMgbWlub3IgKDApIHRyYWNrcw0KbW9kZV9wbG90IDwtIGdncGxvdChhcy5kYXRhLmZyYW1lKG1vZGVfY291bnQpLCBhZXMoeCA9ICIiLCB5ID0gRnJlcSwgZmlsbCA9IGZhY3RvcihWYXIxKSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMSkgKw0KICBjb29yZF9wb2xhcih0aGV0YSA9ICJ5IikgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJncmVlbiIsICJvcmFuZ2UiKSkgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBNb2RlIChNYWpvciB2cyBNaW5vcikiLCBmaWxsID0gIk1vZGUiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQojIERpc3BsYXkgdGhlIHBsb3QNCnByaW50KG1vZGVfcGxvdCkNCmBgYA0KVGhlIHBpZSBjaGFydCB2aXN1YWxseSBzaG93cyB0aGUgZGlzdHJpYnV0aW9uIG9mICoqbW9kZSoqIHZhbHVlcyAoMCBmb3IgbWlub3IsIDEgZm9yIG1ham9yKSwgaGlnaGxpZ2h0aW5nIGhvdyBtYW55IHRyYWNrcyBhcmUgaW4gZWFjaCBjYXRlZ29yeS4gSWYgb25lIGNhdGVnb3J5IChtaW5vciBvciBtYWpvcikgb3ZlcndoZWxtaW5nbHkgZG9taW5hdGVzLCBpdCBjbGFyaWZpZXMgYW55IHBvdGVudGlhbCBpbWJhbGFuY2UgaW4gdGhlIGRhdGFzZXQuIFRoZSBjaGFydCBwcm92aWRlcyBhIGNsZWFyIG92ZXJ2aWV3IG9mIHRoZSBtb2RlIGZlYXR1cmUsIGhlbHBpbmcgdG8gZW5zdXJlIGl0J3MgaW50ZXJwcmV0ZWQgY29ycmVjdGx5LiBJdCBhbHNvIGFkZHJlc3NlcyBhbnkgdW5jZXJ0YWludHkgYWJvdXQgdGhlIHByb3BvcnRpb24gb2Ygc29uZ3MgaW4gbWFqb3IgdnMgbWlub3IgbW9kZXMuDQoNCmBgYHtyfQ0KIyBTZWxlY3QgcmVsZXZhbnQgY29sdW1ucyBmb3IgdGhlIGhlYXRtYXAgKGRhbmNlYWJpbGl0eSwgZW5lcmd5LCBsb3VkbmVzcywgdGVtcG8sIGV0Yy4pDQpkZl9zZWxlY3RlZCA8LSBkYXRhWywgYygiZGFuY2VhYmlsaXR5IiwgImVuZXJneSIsICJsb3VkbmVzcyIsICJ0ZW1wbyIsICJwb3B1bGFyaXR5IildDQoNCiMgQ2FsY3VsYXRlIHRoZSBjb3JyZWxhdGlvbiBtYXRyaXggZm9yIHRoZSBzZWxlY3RlZCBjb2x1bW5zDQpjb3JyZWxhdGlvbl9tYXRyaXggPC0gY29yKGRmX3NlbGVjdGVkLCB1c2UgPSAiY29tcGxldGUub2JzIikNCg0KIyBDcmVhdGUgYSBoZWF0bWFwIHRvIHZpc3VhbGl6ZSB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiBkYW5jZWFiaWxpdHkgYW5kIG90aGVyIGZlYXR1cmVzDQpsaWJyYXJ5KHJlc2hhcGUyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQoNCiMgTWVsdCB0aGUgY29ycmVsYXRpb24gbWF0cml4IGZvciBnZ3Bsb3QyDQpjb3JfbWVsdGVkIDwtIG1lbHQoY29ycmVsYXRpb25fbWF0cml4KQ0KDQojIENyZWF0ZSBhIGhlYXRtYXANCmhlYXRtYXBfcGxvdCA8LSBnZ3Bsb3QoY29yX21lbHRlZCwgYWVzKFZhcjEsIFZhcjIsIGZpbGwgPSB2YWx1ZSkpICsNCiAgZ2VvbV90aWxlKCkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAiYmx1ZSIsIGhpZ2ggPSAicmVkIiwgbWlkID0gIndoaXRlIiwgbWlkcG9pbnQgPSAwKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIGxhYnModGl0bGUgPSAiQ29ycmVsYXRpb24gSGVhdG1hcCBmb3IgRGFuY2VhYmlsaXR5IGFuZCBPdGhlciBGZWF0dXJlcyIsDQogICAgICAgeCA9ICJGZWF0dXJlIiwgeSA9ICJGZWF0dXJlIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KDQojIERpc3BsYXkgdGhlIHBsb3QNCnByaW50KGhlYXRtYXBfcGxvdCkNCg0KYGBgDQpUaGlzIGhlYXRtYXAgaGVscHMgY2xhcmlmeSBpZiB0aGUgdHJhY2tfaWQgcmVwcmVzZW50cyBtZWFuaW5nZnVsIHZhcmlhdGlvbnMgaW4gbXVzaWNhbCBmZWF0dXJlcyBvciBpZiB0aGVyZeKAmXMgYW4gb3ZlcnJlcHJlc2VudGF0aW9uIG9mIGNlcnRhaW4gdHJhY2tfaWRzIHRoYXQgbWlnaHQgc2tldyB0aGUgZGF0YXNldC4gDQpCeSBhZ2dyZWdhdGluZyB2YWx1ZXMgZm9yIGVhY2ggdHJhY2ssIHRoZSBoZWF0bWFwIGNhbiByZXZlYWwgcGF0dGVybnMgdGhhdCBpbmRpY2F0ZSB3aGV0aGVyIHRyYWNrX2lkIGlzIHNpbXBseSBhbiBpZGVudGlmaWVyIG9yIGlmIGl0IHBsYXlzIGEgbGFyZ2VyIHJvbGUgaW4gZGlzdGluZ3Vpc2hpbmcgdHJhY2sgY2hhcmFjdGVyaXN0aWNzLiANClNpbmNlIHRyYWNrX2lkIGxpa2VseSByZXByZXNlbnRzIGRpZmZlcmVudCBzb25ncywgd2UgY2FuIHVzZSBhIGhlYXRtYXAgdG8gc2VlIGlmIGFueSB0cmFja19pZCBoYXMgZGlzdGluY3QgcGF0dGVybnMgYWNyb3NzIGZlYXR1cmVzIHN1Y2ggYXMgcG9wdWxhcml0eSwgZW5lcmd5LCBvciBkYW5jZWFiaWxpdHkuIA0KQnkgYWdncmVnYXRpbmcgdmFsdWVzIG9mIG90aGVyIGNvbHVtbnMgYnkgdHJhY2tfaWQsIHdlIGNhbiBvYnNlcnZlIGlmIGNlcnRhaW4gdHJhY2tzIGRvbWluYXRlIHNwZWNpZmljIHJhbmdlcyBvZiB2YWx1ZXMuDQoNClRoZXJlIHNlZW1zIGNlcnRhaW4gcmlza3MgYW5kIHJlc3BlY3RpdmUgc29sdXRpb25zIGZvciB0aGVtIC0NCg0KMS4gUmlzazogT3ZlcnJlcHJlc2VudGF0aW9uIG9mIENlcnRhaW4gVHJhY2tzDQogICBTb2x1dGlvbjogRW5zdXJlIGVhY2ggdHJhY2tfaWQgcmVwcmVzZW50cyBhIHVuaXF1ZSBzb25nLg0KMi4gUmlzazogRGF0YSBSZWR1bmRhbmN5DQogICBTb2x1dGlvbjogRmlsdGVyIG91dCByZWR1bmRhbnQgZW50cmllcyBhbmQgYWdncmVnYXRlIGRhdGEgdG8gcmVwcmVzZW50IGVhY2ggc29uZyBvbmx5IG9uY2UuDQozLiBSaXNrOiBJbmFjY3VyYXRlIEFnZ3JlZ2F0aW9uIG9mIE11bHRpcGxlIFZlcnNpb25zDQogICBTb2x1dGlvbjogQ2FyZWZ1bGx5IGFnZ3JlZ2F0ZSBmZWF0dXJlcyBieSB0cmFja19pZCwgZW5zdXJpbmcgZGlmZmVyZW50IHZlcnNpb25zIG9mIHRoZSBzYW1lIHNvbmcgYXJlIGhhbmRsZWQgYXBwcm9wcmlhdGVseS4NCjQuIFJpc2s6IE92ZXJmaXR0aW5nIGluIE1vZGVscw0KICAgU29sdXRpb246IFVzZSBjcm9zcy12YWxpZGF0aW9uIHRlY2huaXF1ZXMgdG8gZW5zdXJlIHRoZSBtb2RlbCBnZW5lcmFsaXplcyB3ZWxsIGFuZCBpcyBub3QgYmlhc2VkIGJ5IHJlcGVhdGVkIHRyYWNrcy4NCiAgIA0KICAgDQojIyMgNC4gRm9yIGF0IGxlYXN0IHR3byBjYXRlZ29yaWNhbCBjb2x1bW5zLCBjaGVjayBmb3IgZXhhbXBsZXMgb2YgdGhlIGZvbGxvd2luZywgYW5kIGRlc2NyaWJlIHdoYXQgeW91IGZpbmQ6DQojIyMgQXJlIHRoZXJlIGV4cGxpY2l0bHkgbWlzc2luZyByb3dzPw0KIyMjIEFyZSB0aGVyZSBpbXBsaWNpdGx5IG1pc3Npbmcgcm93cz8NCiMjIyBBcmUgdGhlcmUgZW1wdHkgZ3JvdXBzPw0KDQotIEV4cGxpY2l0bHkgbWlzc2luZyByb3dzOiBSb3dzIHdoZXJlIHRoZSBkYXRhIGlzIGNsZWFybHkgbWFya2VkIGFzIG1pc3NpbmcgKGUuZy4sIGVtcHR5IGNlbGxzLCAiTi9BIiwgb3IgbnVsbCB2YWx1ZXMpLg0KLSBJbXBsaWNpdGx5IG1pc3Npbmcgcm93czogUm93cyB0aGF0IGFyZSBub3QgcHJlc2VudCBpbiB0aGUgZGF0YXNldCBidXQgc2hvdWxkIGxvZ2ljYWxseSBleGlzdCBiYXNlZCBvbiB0aGUgY29udGV4dCBvZiBvdGhlciBkYXRhLg0KLSBFbXB0eSBncm91cHM6IENhdGVnb3JpZXMgd2l0aGluIGEgY29sdW1uIHRoYXQgaGF2ZSBubyBjb3JyZXNwb25kaW5nIGRhdGEgZW50cmllcy4NCg0KYGBge3J9DQojIENoZWNrIGZvciBtaXNzaW5nIHZhbHVlcyAoTkEpIGluIGVhY2ggY29sdW1uIG9mIHRoZSBkYXRhc2V0DQptaXNzaW5nX3ZhbHVlcyA8LSBzYXBwbHkoZGF0YSwgZnVuY3Rpb24oeCkgc3VtKGlzLm5hKHgpKSkNCg0KIyBEaXNwbGF5IHRoZSBjb3VudCBvZiBtaXNzaW5nIHZhbHVlcyBwZXIgY29sdW1uDQpwcmludChtaXNzaW5nX3ZhbHVlcykNCmBgYA0KDQojIyMgYS4gQXJ0aXN0cyBDb2x1bW4NCg0KMS4gTmFtZSBvZiB0aGUgY2F0ZWdvcmljYWwgY29sdW1uIG9ic2VydmVkOiBhcnRpc3RzDQoNCjIuIEV4cGxpY2l0IG1pc3Npbmcgcm93czogVGhlcmUgYXJlIG5vIGV4cGxpY2l0bHkgbWlzc2luZyByb3dzIGluIHRoZSBhcnRpc3RzIGNvbHVtbi4gQWxsIHJvd3MgY29udGFpbiBhbiBhcnRpc3QgbmFtZS4NCg0KMy4gSW1wbGljaXQgbWlzc2luZyByb3dzOiBUaGVyZSBkb24ndCBhcHBlYXIgdG8gYmUgYW55IGltcGxpY2l0bHkgbWlzc2luZyByb3dzLiBUaGUgZGF0YXNldCBzZWVtcyB0byBpbmNsdWRlIGEgd2lkZSB2YXJpZXR5IG9mIGFydGlzdHMgICAgd2l0aG91dCBhbnkgb2J2aW91cyBvbWlzc2lvbnMuDQoNCjQuIEVtcHR5IGdyb3VwczogVGhlcmUgYXJlIG5vIGVtcHR5IGdyb3VwcyBpbiB0aGUgYXJ0aXN0cyBjb2x1bW4uIEVhY2ggcm93IGhhcyBhbiBhc3NvY2lhdGVkIGFydGlzdC4NCg0KNS4gQWRkaXRpb25hbCBvYnNlcnZhdGlvbjogVGhlIGFydGlzdHMgY29sdW1uIGNvbnRhaW5zIGEgbWl4IG9mIGluZGl2aWR1YWwgYXJ0aXN0cyAoZS5nLiwgIkdlbiBIb3NoaW5vIiwgIkphc29uIE1yYXoiKSBhbmQgY29sbGFib3JhdGlvbnMgKGUuZy4sICJJbmdyaWQgTWljaGFlbHNvbjsgWkFZTiIpLg0KDQoNCg0KIyMjIGIuIFRyYWNrIEdlbnJlIENvbHVtbg0KDQoxLiBOYW1lIG9mIHRoZSBjYXRlZ29yaWNhbCBjb2x1bW4gb2JzZXJ2ZWQ6IHRyYWNrX2dlbnJlDQoNCjIuIEV4cGxpY2l0IG1pc3Npbmcgcm93czogVGhlcmUgYXJlIG5vIGV4cGxpY2l0bHkgbWlzc2luZyByb3dzIGluIHRoZSB0cmFja19nZW5yZSBjb2x1bW4uIEFsbCByb3dzIGhhdmUgYSBnZW5yZSBhc3NpZ25lZC4NCg0KMy4gSW1wbGljaXQgbWlzc2luZyByb3dzOiBUaGVyZSBkb24ndCBhcHBlYXIgdG8gYmUgYW55IGltcGxpY2l0bHkgbWlzc2luZyByb3dzIGluIHRlcm1zIG9mIGdlbnJlcy4gSG93ZXZlciwgaXQncyB3b3J0aCBub3RpbmcgdGhhdCBhbGwgdHJhY2tzIGluIHRoZSB2aXNpYmxlIGRhdGEgYXJlIGxhYmVsZWQgYXMgImFjb3VzdGljIiwgd2hpY2ggbWlnaHQgc3VnZ2VzdCB0aGF0IG90aGVyIGdlbnJlcyBhcmUgaW1wbGljaXRseSBtaXNzaW5nIGZyb20gdGhpcyBkYXRhc2V0Lg0KDQo0LiBFbXB0eSBncm91cHM6IFRoZXJlIGFyZSBubyBlbXB0eSBncm91cHMgaW4gdGhlIHRyYWNrX2dlbnJlIGNvbHVtbi4gQWxsIHJvd3MgaGF2ZSB0aGUgZ2VucmUgImFjb3VzdGljIiBhc3NpZ25lZC4NCg0KNS4gQWRkaXRpb25hbCBvYnNlcnZhdGlvbjogVGhlIGxhY2sgb2YgdmFyaWV0eSBpbiB0aGUgdHJhY2tfZ2VucmUgY29sdW1uIChhbGwgdHJhY2tzIGxhYmVsZWQgYXMgImFjb3VzdGljIikgbWlnaHQgaW5kaWNhdGUgdGhhdCB0aGlzIGRhdGFzZXQgaXMgZm9jdXNlZCBzcGVjaWZpY2FsbHkgb24gYWNvdXN0aWMgdHJhY2tzIG9yIHRoYXQgdGhlcmUncyBhbiBpc3N1ZSB3aXRoIHRoZSBnZW5yZSBjbGFzc2lmaWNhdGlvbiBpbiB0aGUgZGF0YS4NCg0KDQojIyMgNS5Gb3IgYXQgbGVhc3Qgb25lIGNvbnRpbnVvdXMgY29sdW1uLCB3aGF0IHdvdWxkIHlvdSBkZWZpbmUgYXMgYW4gb3V0bGllciBhbmQgd2h5Pw0KDQpUbyBhbmFseXplIG91dGxpZXJzIGluIGEgY29udGludW91cyBjb2x1bW4sIHNlbGVjdGluZyAncG9wdWxhcml0eScgY29sdW1uLCB3aGljaCBjb250YWlucyBudW1lcmljYWwgdmFsdWVzIHJhbmdpbmcgZnJvbSAwIHRvIDgyLiANCg0KQW4gb3V0bGllciBpbiB0aGUgcG9wdWxhcml0eSBjb2x1bW4gY2FuIGJlIGRlZmluZWQgYXMgYSB2YWx1ZSB0aGF0IGZhbGxzIHNpZ25pZmljYW50bHkgb3V0c2lkZSB0aGUgdHlwaWNhbCByYW5nZSBvZiBwb3B1bGFyaXR5IHNjb3JlcyBmb3IgdGhlIHRyYWNrcyBpbiB0aGlzIGRhdGFzZXQuDQoNCkhlcmUncyBob3cgd2UgY2FuIGRlZmluZSBvdXRsaWVycyB1c2luZyB0aGUgSVFSIG1ldGhvZDoNCkNvbXB1dGUgdGhlIEludGVycXVhcnRpbGUgUmFuZ2UgKElRUikgPSBRMyAtIFExPSA2MCAtIDMwID0gMzANCkRlZmluZSB0aGUgbG93ZXIgYm91bmQgYXMgUTEgLSAxLjUgKiBJUVIgPSAzMCAtIDEuNSAqIDMwID0gLTE1DQpEZWZpbmUgdGhlIHVwcGVyIGJvdW5kIGFzIFEzICsgMS41ICogSVFSID0gNjAgKyAxLjUgKiAzMCA9IDEwNQ0KDQpBbnkgdmFsdWUgYmVsb3cgdGhlIGxvd2VyIGJvdW5kIG9yIGFib3ZlIHRoZSB1cHBlciBib3VuZCBpcyBjb25zaWRlcmVkIGFuIG91dGxpZXIuDQpTaW5jZSBwb3B1bGFyaXR5IHNjb3JlcyBhcmUgYWx3YXlzIG5vbi1uZWdhdGl2ZSBhbmQgaGF2ZSBhIG1heGltdW0gb2YgMTAwLCB3ZSBjYW4gYWRqdXN0IG91ciBkZWZpbml0aW9uOg0KQW55IHBvcHVsYXJpdHkgc2NvcmUgYmVsb3cgMCAod2hpY2ggaXNuJ3QgcG9zc2libGUgaW4gdGhpcyBjYXNlKSBvciBhYm92ZSAxMDUgd291bGQgYmUgY29uc2lkZXJlZCBhbiBvdXRsaWVyLg0KDQpJbiB0aGlzIGRhdGFzZXQsIHdlIGRvbid0IHNlZSBhbnkgdmFsdWVzIGFib3ZlIDEwNSwgc28gdGhlcmUgYXJlIG5vIHVwcGVyIG91dGxpZXJzLiANCkhvd2V2ZXIsIHRyYWNrcyB3aXRoIHZlcnkgbG93IHBvcHVsYXJpdHkgc2NvcmVzIChjbG9zZSB0byAwKSBjb3VsZCBiZSBjb25zaWRlcmVkIGFzIGxvd2VyIG91dGxpZXJzLg0KDQo=