Aim of this tutorial
You will use concepts learned in the lectures to:
- use computational linguistics to query text data
- represent text corpora as
- examine the sentiment of text data
- use psycholinguistics to look at additional text variables
Task 1: Preparing the corpus
In this tutorial, you will explore a unique dataset of YouTube transcripts extracted from left-leaning and right-leaning news channels. In the provided dataset, you have the transcripts of 2000 YouTube videos each from FoxNews (a right-leaning US news channel) and from The Young Turks (a left-leaning US news outlet).
Load the original dataframe called media_data
from data/media_data.RData
.
#your code
Make sure you have the following packages installed+loaded into your workspace:
- quanteda
- stringr
- sentimentr
- syuzhet
#your code
Take a look at the data and identify the column that contains the text data:
#your code
Now, before you create a corpus from the text column, makle sure that all strings are in the same format. You will see that some are all UPPERCASE. Equally, you can observe that some contain punctuation, while others do not.
Fix this by creating a new column called text_clean
that contains the lowercased strings and has the punctuation removed. (hint: take a look at the super useful stringr
package and this related SO question)
#your code
Now use the text_clean
column to create a corpus object called media_corpus
from the quanteda
package. Remove the orginal text column to avoid excessive data structures and the rename the column text_clean
to text
(this is important for quanteda to knwo where the text is located):
#your code
Take a look at the summary of the new object media_corpus
:
#your code
Use the summary
function (hint: you might want to change the n=
argument) on the corpus to create the object corpus_statistics
and calculate:
- the total number of tokens in the corpus
- the type/token ratio for each document
- the average type/token ration for both channels separately (hint:
tapply
)
#your code
Task 2: Building a TFIDF representation
Next, build a TFIDF representation from the corpus using the count
TF representation and the inverse
DF representation. Use the stemmed tokens but leave the stopwords in when you create a DFM.
Step 1: create the DFM
#your code
Take a look at the first 10 rows and first ten columns of your DFM.
#your code
Step 2: Weigh the DFM to a TFIDF representation
#your code
Again, take a look at the first 10 rows and first ten columns of your TFIDF-DFM.
#your code
Retrieve the top 10 features (tokens) according to their TFIDF value for both channel_name
values. (Hint: look at the groups
argument in the topfeatures
function).
#your code
Rebuild the TFIDF without stopwords and look at the top features again:
#your code
Task 3: Building ngram representations
Now take the above steps a bit further and produce a TF-IDF weighted bi-gram DFM.
Keep in mind that this involves several steps. Once you have created your bi-gram DFM (without the TFIDF weighting), remove those that do not occur in at least 5% of all documents. Stem the tokens.
Step 1: create the bigram DFM and apply the sparsity correction.
#your code
What was the original overall sparsity, and how did it change?
#your code
Step 2: apply TFIDF weighting
#your code
What are the top 5 features (per group) before TFIDF weighting, and after TFIDF weighting?
#your code
Task 4: Assessing the sentiment of the corpus
Now let’s look at the sentiment of the texts from these news outlets. We’ll start with the sentence-based approach from the sentimentr
package. Since only the data from FoxNews was punctuated and hence contained sentences, we’ll focus on these ones only.
Create a new object called foxnews_only
that contains only the transcripts of FoxNews:
#your code
Now use the sentiment
function to retrieve the sentiment of each sentence from this sub-corpus and store the results in a variable called foxnews_sentiments
(this will take a while):
#your code
The object foxnews_sentiments
now contains a sentiment value for each sentence in this sub-corpus.
Take a look at the distribution of these sentiments using a histogram:
#your code
What is the mean/median/min/max sentiment?
#your code
Task 5: Using the sentiment trajectory approach
Now let’s use the more advanced dynamic approach that can handle valence shifters as well as unpunctuated data.
Step 1: load the local source to access the ncs
(= naive context sentiment) function by running the command below:
source('./r_deps/naive_context_sentiment/ncs.R')
You now have access to the sentiment trajectory algorithm developed and introduced in this paper.
The main wrapper function is called ncs_full
and asks you to specify the following arguments:
- txt_input_col: the column where your text is located
- txt_id_col: an identifier column
- low_pass_filter_size: the degree of smoothing in the Fourier transformation
- transform_values: whether or not to scale the values to -1.00 : +1.0
- normalize_values: whether or not to normalise the values (i.e. mean = 0, sd = 1)
- min_tokens: the minimum number of tokens that a text must contain to be processed
- cluster_lower: the lower size of the context window around the sentiment word
- cluster_upper: the upper size of the context window
Extract the sentiment trajectories of the first 100 FoxNews and the first 100 The Young Turks transcripts and leave all values in their default state (i.e. only specify the txt_input_col
and txt_id_col
argument). Call the resulting data sentiment_trajectories_foxnews
and sentiment_trajectories_tyt
. Note that ncs_full
assumes that your input data is a data.frame (so use the media_data dataframe). Run the analysis on the cleaned text column. This operation will also take a few minutes.
#your code
Now take a look at some shapes of the sentiment trajectories. Compare 2 shapes from FoxNews with 2 shapes from The Young Turks by plotting them:
#your code
Task 6: Psycholinguistic variables
Finally, let’s explore the psycholinguistics dimension (and additional, deeper linguistic constructs as retrieved through the Linguistic Inquiry and Word Count Software a.k.a. LIWC.
Load the LIWC output for this corpus (LIWC extraction already done) as a csv file from ./data/media_data_liwc.csv
and call the resulting object liwc_data
.
#your code
Look at the first ten rows of this object:
#your code
Now calculate the average of the following variables per group:
- swear words
- focus on the future
- focus on the past
- personal pronouns (ppron)
- amount of analytical language
- references to ‘power’
Note: you will have to create the ‘group’ variable yourself from the file
variable.
#your code
Explore the data further to understand the LIWC.
LS0tCnRpdGxlOiAnVGV4dCBtaW5pbmcgaW4gUicKYXV0aG9yOiBCIEtsZWluYmVyZwpkYXRlOiA1IEZlYiAyMDE5CnN1YnRpdGxlOiBEZXB0IG9mIFNlY3VyaXR5IGFuZCBDcmltZSBTY2llbmNlLCBVQ0wKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKLS0tCgpUdXRvcmlhbCAzLCBBZHZhbmNlZCBDcmltZSBBbmFseXNpcywgQlNjIFNlY3VyaXR5IGFuZCBDcmltZSBTY2llbmNlLCBVQ0wKCi0tLQoKIyMgQWltIG9mIHRoaXMgdHV0b3JpYWwKCllvdSB3aWxsIHVzZSBjb25jZXB0cyBsZWFybmVkIGluIHRoZSBsZWN0dXJlcyB0bzoKCi0gdXNlIGNvbXB1dGF0aW9uYWwgbGluZ3Vpc3RpY3MgdG8gcXVlcnkgdGV4dCBkYXRhCi0gcmVwcmVzZW50IHRleHQgY29ycG9yYSBhcyAKLSBleGFtaW5lIHRoZSBzZW50aW1lbnQgb2YgdGV4dCBkYXRhCi0gdXNlIHBzeWNob2xpbmd1aXN0aWNzIHRvIGxvb2sgYXQgYWRkaXRpb25hbCB0ZXh0IHZhcmlhYmxlcwoKIyMjIFRhc2sgMTogUHJlcGFyaW5nIHRoZSBjb3JwdXMKCkluIHRoaXMgdHV0b3JpYWwsIHlvdSB3aWxsIGV4cGxvcmUgYSB1bmlxdWUgZGF0YXNldCBvZiBZb3VUdWJlIHRyYW5zY3JpcHRzIGV4dHJhY3RlZCBmcm9tIGxlZnQtbGVhbmluZyBhbmQgcmlnaHQtbGVhbmluZyBuZXdzIGNoYW5uZWxzLiBJbiB0aGUgcHJvdmlkZWQgZGF0YXNldCwgeW91IGhhdmUgdGhlIHRyYW5zY3JpcHRzIG9mIDIwMDAgWW91VHViZSB2aWRlb3MgZWFjaCBmcm9tIEZveE5ld3MgKGEgcmlnaHQtbGVhbmluZyBVUyBuZXdzIGNoYW5uZWwpIGFuZCBmcm9tIFRoZSBZb3VuZyBUdXJrcyAoYSBsZWZ0LWxlYW5pbmcgVVMgbmV3cyBvdXRsZXQpLgoKTG9hZCB0aGUgb3JpZ2luYWwgZGF0YWZyYW1lIGNhbGxlZCBgbWVkaWFfZGF0YWAgZnJvbSBgZGF0YS9tZWRpYV9kYXRhLlJEYXRhYC4KCmBgYHtyfQojeW91ciBjb2RlCgpgYGAKCk1ha2Ugc3VyZSB5b3UgaGF2ZSB0aGUgZm9sbG93aW5nIHBhY2thZ2VzIGluc3RhbGxlZCtsb2FkZWQgaW50byB5b3VyIHdvcmtzcGFjZToKCi0gcXVhbnRlZGEKLSBzdHJpbmdyCi0gc2VudGltZW50cgotIHN5dXpoZXQKCmBgYHtyfQojeW91ciBjb2RlCgpgYGAKClRha2UgYSBsb29rIGF0IHRoZSBkYXRhIGFuZCBpZGVudGlmeSB0aGUgY29sdW1uIHRoYXQgY29udGFpbnMgdGhlIHRleHQgZGF0YToKCmBgYHtyfQojeW91ciBjb2RlCgpgYGAKCk5vdywgYmVmb3JlIHlvdSBjcmVhdGUgYSBjb3JwdXMgZnJvbSB0aGUgdGV4dCBjb2x1bW4sIG1ha2xlIHN1cmUgdGhhdCBhbGwgc3RyaW5ncyBhcmUgaW4gdGhlIHNhbWUgZm9ybWF0LiBZb3Ugd2lsbCBzZWUgdGhhdCBzb21lIGFyZSBhbGwgVVBQRVJDQVNFLiBFcXVhbGx5LCB5b3UgY2FuIG9ic2VydmUgdGhhdCBzb21lIGNvbnRhaW4gcHVuY3R1YXRpb24sIHdoaWxlIG90aGVycyBkbyBub3QuCgpGaXggdGhpcyBieSBjcmVhdGluZyBhIG5ldyBjb2x1bW4gY2FsbGVkIGB0ZXh0X2NsZWFuYCB0aGF0IGNvbnRhaW5zIHRoZSBsb3dlcmNhc2VkIHN0cmluZ3MgYW5kIGhhcyB0aGUgcHVuY3R1YXRpb24gcmVtb3ZlZC4gKGhpbnQ6IHRha2UgYSBsb29rIGF0IHRoZSBzdXBlciB1c2VmdWwgYHN0cmluZ3JgIHBhY2thZ2UgYW5kIFt0aGlzIHJlbGF0ZWQgU08gcXVlc3Rpb25dKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vYS8xMDI5NDgxOCkpCgoKYGBge3J9CiN5b3VyIGNvZGUKCmBgYAoKTm93IHVzZSB0aGUgYHRleHRfY2xlYW5gIGNvbHVtbiB0byBjcmVhdGUgYSBjb3JwdXMgb2JqZWN0IGNhbGxlZCBgbWVkaWFfY29ycHVzYCBmcm9tIHRoZSBgcXVhbnRlZGFgIHBhY2thZ2UuIFJlbW92ZSB0aGUgb3JnaW5hbCB0ZXh0IGNvbHVtbiB0byBhdm9pZCBleGNlc3NpdmUgZGF0YSBzdHJ1Y3R1cmVzIGFuZCB0aGUgcmVuYW1lIHRoZSBjb2x1bW4gYHRleHRfY2xlYW5gIHRvIGB0ZXh0YCAodGhpcyBpcyBpbXBvcnRhbnQgZm9yIHF1YW50ZWRhIHRvIGtud28gd2hlcmUgdGhlIHRleHQgaXMgbG9jYXRlZCk6CgpgYGB7cn0KI3lvdXIgY29kZQoKYGBgCgpUYWtlIGEgbG9vayBhdCB0aGUgc3VtbWFyeSBvZiB0aGUgbmV3IG9iamVjdCBgbWVkaWFfY29ycHVzYDoKCmBgYHtyfQojeW91ciBjb2RlCgpgYGAKClVzZSB0aGUgYHN1bW1hcnlgIGZ1bmN0aW9uIChoaW50OiB5b3UgbWlnaHQgd2FudCB0byBjaGFuZ2UgdGhlIGBuPWAgYXJndW1lbnQpIG9uIHRoZSBjb3JwdXMgdG8gY3JlYXRlIHRoZSBvYmplY3QgYGNvcnB1c19zdGF0aXN0aWNzYCBhbmQgY2FsY3VsYXRlOgoKLSB0aGUgdG90YWwgbnVtYmVyIG9mIHRva2VucyBpbiB0aGUgY29ycHVzCi0gdGhlIHR5cGUvdG9rZW4gcmF0aW8gZm9yIGVhY2ggZG9jdW1lbnQKLSB0aGUgYXZlcmFnZSB0eXBlL3Rva2VuIHJhdGlvbiBmb3IgYm90aCBjaGFubmVscyBzZXBhcmF0ZWx5IChoaW50OiBgdGFwcGx5YCkKCmBgYHtyfQojeW91ciBjb2RlCgoKCmBgYAoKCiMjIyBUYXNrIDI6IEJ1aWxkaW5nIGEgVEZJREYgcmVwcmVzZW50YXRpb24KCk5leHQsIGJ1aWxkIGEgVEZJREYgcmVwcmVzZW50YXRpb24gZnJvbSB0aGUgY29ycHVzIHVzaW5nIHRoZSBgY291bnRgIFRGIHJlcHJlc2VudGF0aW9uIGFuZCB0aGUgYGludmVyc2VgIERGIHJlcHJlc2VudGF0aW9uLiBVc2UgdGhlIHN0ZW1tZWQgdG9rZW5zIGJ1dCBsZWF2ZSB0aGUgc3RvcHdvcmRzIGluIHdoZW4geW91IGNyZWF0ZSBhIERGTS4KClN0ZXAgMTogY3JlYXRlIHRoZSBERk0KCmBgYHtyfQojeW91ciBjb2RlCgpgYGAKClRha2UgYSBsb29rIGF0IHRoZSBmaXJzdCAxMCByb3dzIGFuZCBmaXJzdCB0ZW4gY29sdW1ucyBvZiB5b3VyIERGTS4KCmBgYHtyfQojeW91ciBjb2RlCgpgYGAKClN0ZXAgMjogV2VpZ2ggdGhlIERGTSB0byBhIFRGSURGIHJlcHJlc2VudGF0aW9uCgpgYGB7cn0KI3lvdXIgY29kZQoKYGBgCgpBZ2FpbiwgdGFrZSBhIGxvb2sgYXQgdGhlIGZpcnN0IDEwIHJvd3MgYW5kIGZpcnN0IHRlbiBjb2x1bW5zIG9mIHlvdXIgVEZJREYtREZNLgoKYGBge3J9CiN5b3VyIGNvZGUKCmBgYAoKUmV0cmlldmUgdGhlIHRvcCAxMCBmZWF0dXJlcyAodG9rZW5zKSBhY2NvcmRpbmcgdG8gdGhlaXIgVEZJREYgdmFsdWUgZm9yIGJvdGggYGNoYW5uZWxfbmFtZWAgdmFsdWVzLiAoSGludDogbG9vayBhdCB0aGUgYGdyb3Vwc2AgYXJndW1lbnQgaW4gdGhlIGB0b3BmZWF0dXJlc2AgZnVuY3Rpb24pLgoKYGBge3J9CiN5b3VyIGNvZGUKCmBgYAoKClJlYnVpbGQgdGhlIFRGSURGIHdpdGhvdXQgc3RvcHdvcmRzIGFuZCBsb29rIGF0IHRoZSB0b3AgZmVhdHVyZXMgYWdhaW46CgpgYGB7cn0KI3lvdXIgY29kZQoKYGBgCgojIyMgVGFzayAzOiBCdWlsZGluZyBuZ3JhbSByZXByZXNlbnRhdGlvbnMKCk5vdyB0YWtlIHRoZSBhYm92ZSBzdGVwcyBhIGJpdCBmdXJ0aGVyIGFuZCBwcm9kdWNlIGEgVEYtSURGIHdlaWdodGVkIGJpLWdyYW0gREZNLgoKS2VlcCBpbiBtaW5kIHRoYXQgdGhpcyBpbnZvbHZlcyBzZXZlcmFsIHN0ZXBzLiBPbmNlIHlvdSBoYXZlIGNyZWF0ZWQgeW91ciBiaS1ncmFtIERGTSAod2l0aG91dCB0aGUgVEZJREYgd2VpZ2h0aW5nKSwgcmVtb3ZlIHRob3NlIHRoYXQgZG8gbm90IG9jY3VyIGluIGF0IGxlYXN0IDUlIG9mIGFsbCBkb2N1bWVudHMuIFN0ZW0gdGhlIHRva2Vucy4KClN0ZXAgMTogY3JlYXRlIHRoZSBiaWdyYW0gREZNIGFuZCBhcHBseSB0aGUgc3BhcnNpdHkgY29ycmVjdGlvbi4KCmBgYHtyfQojeW91ciBjb2RlCgoKYGBgCgoKV2hhdCB3YXMgdGhlIG9yaWdpbmFsIG92ZXJhbGwgc3BhcnNpdHksIGFuZCBob3cgZGlkIGl0IGNoYW5nZT8KCmBgYHtyfQojeW91ciBjb2RlCgpgYGAKClN0ZXAgMjogYXBwbHkgVEZJREYgd2VpZ2h0aW5nCgpgYGB7cn0KI3lvdXIgY29kZQoKYGBgCgpXaGF0IGFyZSB0aGUgdG9wIDUgZmVhdHVyZXMgKHBlciBncm91cCkgYmVmb3JlIFRGSURGIHdlaWdodGluZywgYW5kIGFmdGVyIFRGSURGIHdlaWdodGluZz8KCmBgYHtyfQojeW91ciBjb2RlCgpgYGAKCgojIyMgVGFzayA0OiBBc3Nlc3NpbmcgdGhlIHNlbnRpbWVudCBvZiB0aGUgY29ycHVzCgpOb3cgbGV0J3MgbG9vayBhdCB0aGUgc2VudGltZW50IG9mIHRoZSB0ZXh0cyBmcm9tIHRoZXNlIG5ld3Mgb3V0bGV0cy4gV2UnbGwgc3RhcnQgd2l0aCB0aGUgc2VudGVuY2UtYmFzZWQgYXBwcm9hY2ggZnJvbSB0aGUgYHNlbnRpbWVudHJgIHBhY2thZ2UuIFNpbmNlIG9ubHkgdGhlIGRhdGEgZnJvbSBGb3hOZXdzIHdhcyBwdW5jdHVhdGVkIGFuZCBoZW5jZSBjb250YWluZWQgc2VudGVuY2VzLCB3ZSdsbCBmb2N1cyBvbiB0aGVzZSBvbmVzIG9ubHkuCgpDcmVhdGUgYSBuZXcgb2JqZWN0IGNhbGxlZCBgZm94bmV3c19vbmx5YCB0aGF0IGNvbnRhaW5zIG9ubHkgdGhlIHRyYW5zY3JpcHRzIG9mIEZveE5ld3M6CgpgYGB7cn0KI3lvdXIgY29kZQoKYGBgCgpOb3cgdXNlIHRoZSBgc2VudGltZW50YCBmdW5jdGlvbiB0byByZXRyaWV2ZSB0aGUgc2VudGltZW50IG9mIGVhY2ggc2VudGVuY2UgZnJvbSB0aGlzIHN1Yi1jb3JwdXMgYW5kIHN0b3JlIHRoZSByZXN1bHRzIGluIGEgdmFyaWFibGUgY2FsbGVkIGBmb3huZXdzX3NlbnRpbWVudHNgICh0aGlzIHdpbGwgdGFrZSBhIHdoaWxlKToKCmBgYHtyfQojeW91ciBjb2RlCgoKYGBgCgpUaGUgb2JqZWN0IGBmb3huZXdzX3NlbnRpbWVudHNgIG5vdyBjb250YWlucyBhIHNlbnRpbWVudCB2YWx1ZSBmb3IgZWFjaCBzZW50ZW5jZSBpbiB0aGlzIHN1Yi1jb3JwdXMuCgpUYWtlIGEgbG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZXNlIHNlbnRpbWVudHMgdXNpbmcgYSBoaXN0b2dyYW06CgpgYGB7cn0KI3lvdXIgY29kZQoKCmBgYAoKV2hhdCBpcyB0aGUgbWVhbi9tZWRpYW4vbWluL21heCBzZW50aW1lbnQ/CgpgYGB7cn0KI3lvdXIgY29kZQoKCmBgYAoKCiMjIyBUYXNrIDU6IFVzaW5nIHRoZSBzZW50aW1lbnQgdHJhamVjdG9yeSBhcHByb2FjaAoKTm93IGxldCdzIHVzZSB0aGUgbW9yZSBhZHZhbmNlZCBkeW5hbWljIGFwcHJvYWNoIHRoYXQgY2FuIGhhbmRsZSB2YWxlbmNlIHNoaWZ0ZXJzIGFzIHdlbGwgYXMgdW5wdW5jdHVhdGVkIGRhdGEuCgpTdGVwIDE6IGxvYWQgdGhlIGxvY2FsIHNvdXJjZSB0byBhY2Nlc3MgdGhlIGBuY3NgICg9IG5haXZlIGNvbnRleHQgc2VudGltZW50KSBmdW5jdGlvbiBieSBydW5uaW5nIHRoZSBjb21tYW5kIGJlbG93OgoKYGBge3J9CnNvdXJjZSgnLi9yX2RlcHMvbmFpdmVfY29udGV4dF9zZW50aW1lbnQvbmNzLlInKQpgYGAKCllvdSBub3cgaGF2ZSBhY2Nlc3MgdG8gdGhlIHNlbnRpbWVudCB0cmFqZWN0b3J5IGFsZ29yaXRobSBkZXZlbG9wZWQgYW5kIGludHJvZHVjZWQgaW4gW3RoaXNdKGh0dHA6Ly9hY2x3ZWIub3JnL2FudGhvbG9neS9EMTgtMTM5NCkgcGFwZXIuCgpUaGUgbWFpbiB3cmFwcGVyIGZ1bmN0aW9uIGlzIGNhbGxlZCBgbmNzX2Z1bGxgIGFuZCBhc2tzIHlvdSB0byBzcGVjaWZ5IHRoZSBmb2xsb3dpbmcgYXJndW1lbnRzOgoKLSB0eHRfaW5wdXRfY29sOiB0aGUgY29sdW1uIHdoZXJlIHlvdXIgdGV4dCBpcyBsb2NhdGVkCi0gdHh0X2lkX2NvbDogYW4gaWRlbnRpZmllciBjb2x1bW4KLSBsb3dfcGFzc19maWx0ZXJfc2l6ZTogdGhlIGRlZ3JlZSBvZiBzbW9vdGhpbmcgaW4gdGhlIEZvdXJpZXIgdHJhbnNmb3JtYXRpb24KLSB0cmFuc2Zvcm1fdmFsdWVzOiB3aGV0aGVyIG9yIG5vdCB0byBzY2FsZSB0aGUgdmFsdWVzIHRvIC0xLjAwIDogKzEuMAotIG5vcm1hbGl6ZV92YWx1ZXM6IHdoZXRoZXIgb3Igbm90IHRvIG5vcm1hbGlzZSB0aGUgdmFsdWVzIChpLmUuIG1lYW4gPSAwLCBzZCA9IDEpCi0gbWluX3Rva2VuczogdGhlIG1pbmltdW0gbnVtYmVyIG9mIHRva2VucyB0aGF0IGEgdGV4dCBtdXN0IGNvbnRhaW4gdG8gYmUgcHJvY2Vzc2VkCi0gY2x1c3Rlcl9sb3dlcjogdGhlIGxvd2VyIHNpemUgb2YgdGhlIGNvbnRleHQgd2luZG93IGFyb3VuZCB0aGUgc2VudGltZW50IHdvcmQKLSBjbHVzdGVyX3VwcGVyOiB0aGUgdXBwZXIgc2l6ZSBvZiB0aGUgY29udGV4dCB3aW5kb3cKCkV4dHJhY3QgdGhlIHNlbnRpbWVudCB0cmFqZWN0b3JpZXMgb2YgdGhlIGZpcnN0IDEwMCBGb3hOZXdzIGFuZCB0aGUgZmlyc3QgMTAwIFRoZSBZb3VuZyBUdXJrcyB0cmFuc2NyaXB0cyBhbmQgbGVhdmUgYWxsIHZhbHVlcyBpbiB0aGVpciBkZWZhdWx0IHN0YXRlIChpLmUuIG9ubHkgc3BlY2lmeSB0aGUgYHR4dF9pbnB1dF9jb2xgIGFuZCBgdHh0X2lkX2NvbGAgYXJndW1lbnQpLiBDYWxsIHRoZSByZXN1bHRpbmcgZGF0YSBgc2VudGltZW50X3RyYWplY3Rvcmllc19mb3huZXdzYCBhbmQgYHNlbnRpbWVudF90cmFqZWN0b3JpZXNfdHl0YC4gTm90ZSB0aGF0IGBuY3NfZnVsbGAgYXNzdW1lcyB0aGF0IHlvdXIgaW5wdXQgZGF0YSBpcyBhIGRhdGEuZnJhbWUgKHNvIHVzZSB0aGUgbWVkaWFfZGF0YSBkYXRhZnJhbWUpLiBSdW4gdGhlIGFuYWx5c2lzIG9uIHRoZSBjbGVhbmVkIHRleHQgY29sdW1uLiBUaGlzIG9wZXJhdGlvbiB3aWxsIGFsc28gdGFrZSBhIGZldyBtaW51dGVzLgoKYGBge3J9CiN5b3VyIGNvZGUKCmBgYAoKCk5vdyB0YWtlIGEgbG9vayBhdCBzb21lIHNoYXBlcyBvZiB0aGUgc2VudGltZW50IHRyYWplY3Rvcmllcy4gQ29tcGFyZSAyIHNoYXBlcyBmcm9tIEZveE5ld3Mgd2l0aCAyIHNoYXBlcyBmcm9tIFRoZSBZb3VuZyBUdXJrcyBieSBwbG90dGluZyB0aGVtOgoKYGBge3J9CiN5b3VyIGNvZGUKCgpgYGAKCgojIyMgVGFzayA2OiBQc3ljaG9saW5ndWlzdGljIHZhcmlhYmxlcwoKRmluYWxseSwgbGV0J3MgZXhwbG9yZSB0aGUgcHN5Y2hvbGluZ3Vpc3RpY3MgZGltZW5zaW9uIChhbmQgYWRkaXRpb25hbCwgZGVlcGVyIGxpbmd1aXN0aWMgY29uc3RydWN0cyBhcyByZXRyaWV2ZWQgdGhyb3VnaCB0aGUgTGluZ3Vpc3RpYyBJbnF1aXJ5IGFuZCBXb3JkIENvdW50IFNvZnR3YXJlIGEuay5hLiBMSVdDLiAKCkxvYWQgdGhlIExJV0Mgb3V0cHV0IGZvciB0aGlzIGNvcnB1cyAoTElXQyBleHRyYWN0aW9uIGFscmVhZHkgZG9uZSkgYXMgYSBjc3YgZmlsZSBmcm9tIGAuL2RhdGEvbWVkaWFfZGF0YV9saXdjLmNzdmAgYW5kIGNhbGwgdGhlIHJlc3VsdGluZyBvYmplY3QgYGxpd2NfZGF0YWAuCgpgYGB7cn0KI3lvdXIgY29kZQoKYGBgCgpMb29rIGF0IHRoZSBmaXJzdCB0ZW4gcm93cyBvZiB0aGlzIG9iamVjdDoKCmBgYHtyfQojeW91ciBjb2RlCgpgYGAKCk5vdyBjYWxjdWxhdGUgdGhlIGF2ZXJhZ2Ugb2YgdGhlIGZvbGxvd2luZyB2YXJpYWJsZXMgcGVyIGdyb3VwOgoKLSBzd2VhciB3b3JkcwotIGZvY3VzIG9uIHRoZSBmdXR1cmUKLSBmb2N1cyBvbiB0aGUgcGFzdAotIHBlcnNvbmFsIHByb25vdW5zIChwcHJvbikKLSBhbW91bnQgb2YgYW5hbHl0aWNhbCBsYW5ndWFnZQotIHJlZmVyZW5jZXMgdG8gJ3Bvd2VyJwoKTm90ZTogeW91IHdpbGwgaGF2ZSB0byBjcmVhdGUgdGhlICdncm91cCcgdmFyaWFibGUgeW91cnNlbGYgZnJvbSB0aGUgYGZpbGVgIHZhcmlhYmxlLgoKYGBge3J9CiN5b3VyIGNvZGUKCmBgYAoKCkV4cGxvcmUgdGhlIGRhdGEgZnVydGhlciB0byB1bmRlcnN0YW5kIHRoZSBMSVdDLgoKLS0tCgojIyBFTkQ=