Setup R

I use these packages so frequently I will just load them all up front. Others that are occasionally useful will be loaded as needed. require statements generally precede the use of less common functions from these packages so you have an idea where they are from.

require(tm)
Loading required package: tm
Loading required package: NLP
require(stringr)    
Loading required package: stringr
require(tidyverse)
Loading required package: tidyverse
Loading tidyverse: ggplot2
Loading tidyverse: tibble
Loading tidyverse: tidyr
Loading tidyverse: readr
Loading tidyverse: purrr
Loading tidyverse: dplyr
Conflicts with tidy packages ----------------------------------------------------------------------
annotate(): ggplot2, NLP
filter():   dplyr, stats
lag():      dplyr, stats
source("text_utils.R")   # good turing, zipf plot

Notice that in the following code the option “eval=FALSE” is often set in the header to a chunk of R code, typically for View commands. This option tells R-Studio to skip that chunk when the “Run all chunks” or “Run all chunks above” commands are executed. You can always run these directly.

Getting the source data: The Federalist Papers

Start by obtaining the original source from the collection of open-source text offered by Project Gutenberg.

require(gutenbergr)
Loading required package: gutenbergr
content <- gutenberg_works()
names(content)
[1] "gutenberg_id"        "title"               "author"              "gutenberg_author_id"
[5] "language"            "gutenberg_bookshelf" "rights"              "has_text"           

There are too many titles to search manually, so use R.

length(content$title)
[1] 40737

Where are the Federalist papers in this collection? As in other situations, regular expressions work are very useful if you’re dealing with text data.

require(stringr)
b <- str_detect(content$title, ".*Federalist.*")
i <- which(b)
i
[1] 18
content$title[i]
[1] "The Federalist Papers"
content$author[i]        # What... no author?
[1] NA
content$gutenberg_id[i]
[1] 18

Now that we have the ID, we can get the text itself. (I routinely use capital letters for the names of data frames.)

TheFederalist <- gutenberg_download(18)
Determining mirror for Project Gutenberg from http://www.gutenberg.org/robot/harvest
Using mirror http://aleph.gutenberg.org

The result is a table of 25,563 rows and two columns.

dim(TheFederalist)
[1] 25563     2

It is important to take a look at the “raw” data in order to appreciate the following steps.

View(TheFederalist)

Only the second column is of interest to us. It has the actual text lines, many of which are blank. (Why two columns? gutenberg_download allows you to extract several documents at once; the first column would be used to separate these. Because all of these lines in this example come from the same document, the first column is constant. It would be more common to find these data in separate files, one for each paper.)

Pull out the text column, but leave the one column in a data frame to simplify the subsequent processing. (select comes from the tidyverse collection.)

TheFederalist <- select(TheFederalist, text)
dim(TheFederalist)
[1] 25563     1

[Just in case, I saved these data in case there’s a problem with internet access during class.

save(TheFederalist, file="federalist.sav")

You can recover the file using

load("federalist.sav")

Hopefully, I won’t need to use these commands in class.]

Preparing the text

The first tasks are to remove these blank lines (assuming we’re not interested in, say, counting those) and then group the text into the separate papers. dplyr is convenient for for this task, filter (selecting) rows from this one-column data frame to obtain a new data frame with fewer rows. The data contained about 7,000 blank lines.

FedPapers <- TheFederalist %>% filter(text != "")
dim(FedPapers)
[1] 18790     1

Now divide the text column into the separate papers, collecting the lines for each paper into one.

Here’s a trick I learned from the “Tidy Text” book to handle this task. The idea is to use the counter feature of dplyr to add a paper number. Then I can join the lines with these numbers. Once again, a regular expression is useful. (Each paper is spread over several lines that we’d like to join together as a single document.)

pattern <- "^FEDERALIST[. ]*No\\."   # ^ denotes start of line
FedPapers <- FedPapers %>% mutate(paper = cumsum(str_detect(text,pattern)))
head(FedPapers)
tail(FedPapers)
View(FedPapers)

After skimming the file, one discovers this…

as.character(FedPapers[14847,'text'])
[1] "*There are two slightly different versions of No. 70 included here."

I will remove the second, somewhat manually here from the source data and then assign paper numbers again. (Alternately, you could have used the assigned paper number, but that leaves a straggling line and messes up the numbers of the following papers.)

FedPapers <- TheFederalist %>% 
    filter(text != "")     %>%
    filter(row_number() < 14847 | 15155 < row_number()) %>%
    mutate(paper = cumsum(str_detect(text,pattern)))
head(FedPapers)

BTW, the leading numbers are footnotes.

tail(FedPapers)

Now pick out the author and build a data frame with each paper as a document. The author of the first paper (Alexander Hamilton) is listed on the 4th line. Let’s see if that pattern holds for other papers. String matching with a basic regular expression makes this easy.

Unfortunately, the pattern varies when topics are continued or dates get added.

Temp <-FedPapers                  %>% 
    mutate(line=row_number())     %>%   
    filter(str_detect(text,"HAMILTON|MADISON|JAY|FEDERALIST"))
Temp
FedPapers[321:325,]
FedPapers[1611:1617,]

It is useful to skim this temporary data frame to see the other differences, such as what happens when there are multiple authors (#20) or unknown authorship (#50)

View(Temp)

To keep track of the authorship of the 85 papers, remove the author names to a separate data frame for later use in labeling the Federalist Papers with a join operation.

Authors <- Temp %>% filter(str_detect(text,"HAMILTON|MADISON|JAY"))
dim(Authors)
[1] 85  3

It is useful for our later classification task to take a look at the names of the authors.

View(Authors)

According to the Gutenberg version, papers 49-57 and 62-63 have “disputed” authorship: either Hamilton or Madison. (Wikipedia has slightly different list of the papers of of disputed authorship, namely 49-58 and 62-63.)

Now remove the author names from the data. (You could remove the numbering line as well.)

FedPapers <- FedPapers %>% filter(!str_detect(text,"HAMILTON|MADISON|JAY"))
dim(FedPapers)
[1] 18396     2
head(FedPapers)

At this point, the tidytext approach to text analysis diverges from the tm approach. (I have alternating opinions, but will use the “tm” approach in these lectures.) For tm, pull the text for each document together rather than being spread over several lines. tapply is a very useful function for jobs like this.

FedPapers <- tapply(FedPapers$text,         # data on separate lines
                    FedPapers$paper,        # grouping variable
                    str_c, collapse=' ')    # function applied to group   (aka, "paste")
length(FedPapers)
[1] 85

Now label these papers by the author by making a data frame.

head(Authors$text)
[1] "HAMILTON" "JAY"      "JAY"      "JAY"      "JAY"      "HAMILTON"
FederalistPapers <- tibble(author=Authors$text, text=FedPapers)

Have a look at the result.

head(FederalistPapers)

Save this version too (though it is not too hard to recreate).

save(FederalistPapers, file="~/data/text/federalist/FederalistPapers.sav")

Now I can “rejoin” the analysis from this point by running this command.

load("~/data/text/federalist/FederalistPapers.sav")

Tokenization

The next task seems obvious, but has a lasting impact: What’s a word? Is a number a word? What about punctuation? Are “subject” and “subjects” different words? tm has a collection of tools for taking care of these tasks, but you need to decide which to use.

As in other examples, put the text into a corpus object. A corpus is usually created when using tm when a collection of separte documents is read into R. tm nicely handles many different types of documents, including PDF and Word files. In this example, the text is already in a variable, so use VectorSource to convert the data into a corpus.

FederalistCorpus <- Corpus(VectorSource(FederalistPapers$text))
FederalistCorpus
<<SimpleCorpus>>
Metadata:  corpus specific: 1, document level (indexed): 0
Content:  documents: 85

A corpus object in tm is a decorated list, a list that has been adorned with special attributes when created. That means we can peek into the corpus as if it were a list by referring to the number elements of the list (which are the documents).

To see the text itself, use the inspect method

inspect(FederalistCorpus[[1]])
<<PlainTextDocument>>
Metadata:  7
Content:  chars: 9543

FEDERALIST. No. 1 General Introduction For the Independent Journal. To the People of the State of New York: AFTER an unequivocal experience of the inefficacy of the subsisting federal government, you are called upon to deliberate on a new Constitution for the United States of America. The subject speaks its own importance; comprehending in its consequences nothing less than the existence of the UNION, the safety and welfare of the parts of which it is composed, the fate of an empire in many respects the most interesting in the world. It has been frequently remarked that it seems to have been reserved to the people of this country, by their conduct and example, to decide the important question, whether societies of men are really capable or not of establishing good government from reflection and choice, or whether they are forever destined to depend for their political constitutions on accident and force. If there be any truth in the remark, the crisis at which we are arrived may with propriety be regarded as the era in which that decision is to be made; and a wrong election of the part we shall act may, in this view, deserve to be considered as the general misfortune of mankind. This idea will add the inducements of philanthropy to those of patriotism, to heighten the solicitude which all considerate and good men must feel for the event. Happy will it be if our choice should be directed by a judicious estimate of our true interests, unperplexed and unbiased by considerations not connected with the public good. But this is a thing more ardently to be wished than seriously to be expected. The plan offered to our deliberations affects too many particular interests, innovates upon too many local institutions, not to involve in its discussion a variety of objects foreign to its merits, and of views, passions and prejudices little favorable to the discovery of truth. Among the most formidable of the obstacles which the new Constitution will have to encounter may readily be distinguished the obvious interest of a certain class of men in every State to resist all changes which may hazard a diminution of the power, emolument, and consequence of the offices they hold under the State establishments; and the perverted ambition of another class of men, who will either hope to aggrandize themselves by the confusions of their country, or will flatter themselves with fairer prospects of elevation from the subdivision of the empire into several partial confederacies than from its union under one government. It is not, however, my design to dwell upon observations of this nature. I am well aware that it would be disingenuous to resolve indiscriminately the opposition of any set of men (merely because their situations might subject them to suspicion) into interested or ambitious views. Candor will oblige us to admit that even such men may be actuated by upright intentions; and it cannot be doubted that much of the opposition which has made its appearance, or may hereafter make its appearance, will spring from sources, blameless at least, if not respectable--the honest errors of minds led astray by preconceived jealousies and fears. So numerous indeed and so powerful are the causes which serve to give a false bias to the judgment, that we, upon many occasions, see wise and good men on the wrong as well as on the right side of questions of the first magnitude to society. This circumstance, if duly attended to, would furnish a lesson of moderation to those who are ever so much persuaded of their being in the right in any controversy. And a further reason for caution, in this respect, might be drawn from the reflection that we are not always sure that those who advocate the truth are influenced by purer principles than their antagonists. Ambition, avarice, personal animosity, party opposition, and many other motives not more laudable than these, are apt to operate as well upon those who support as those who oppose the right side of a question. Were there not even these inducements to moderation, nothing could be more ill-judged than that intolerant spirit which has, at all times, characterized political parties. For in politics, as in religion, it is equally absurd to aim at making proselytes by fire and sword. Heresies in either can rarely be cured by persecution. And yet, however just these sentiments will be allowed to be, we have already sufficient indications that it will happen in this as in all former cases of great national discussion. A torrent of angry and malignant passions will be let loose. To judge from the conduct of the opposite parties, we shall be led to conclude that they will mutually hope to evince the justness of their opinions, and to increase the number of their converts by the loudness of their declamations and the bitterness of their invectives. An enlightened zeal for the energy and efficiency of government will be stigmatized as the offspring of a temper fond of despotic power and hostile to the principles of liberty. An over-scrupulous jealousy of danger to the rights of the people, which is more commonly the fault of the head than of the heart, will be represented as mere pretense and artifice, the stale bait for popularity at the expense of the public good. It will be forgotten, on the one hand, that jealousy is the usual concomitant of love, and that the noble enthusiasm of liberty is apt to be infected with a spirit of narrow and illiberal distrust. On the other hand, it will be equally forgotten that the vigor of government is essential to the security of liberty; that, in the contemplation of a sound and well-informed judgment, their interest can never be separated; and that a dangerous ambition more often lurks behind the specious mask of zeal for the rights of the people than under the forbidden appearance of zeal for the firmness and efficiency of government. History will teach us that the former has been found a much more certain road to the introduction of despotism than the latter, and that of those men who have overturned the liberties of republics, the greatest number have begun their career by paying an obsequious court to the people; commencing demagogues, and ending tyrants. In the course of the preceding observations, I have had an eye, my fellow-citizens, to putting you upon your guard against all attempts, from whatever quarter, to influence your decision in a matter of the utmost moment to your welfare, by any impressions other than those which may result from the evidence of truth. You will, no doubt, at the same time, have collected from the general scope of them, that they proceed from a source not unfriendly to the new Constitution. Yes, my countrymen, I own to you that, after having given it an attentive consideration, I am clearly of opinion it is your interest to adopt it. I am convinced that this is the safest course for your liberty, your dignity, and your happiness. I affect not reserves which I do not feel. I will not amuse you with an appearance of deliberation when I have decided. I frankly acknowledge to you my convictions, and I will freely lay before you the reasons on which they are founded. The consciousness of good intentions disdains ambiguity. I shall not, however, multiply professions on this head. My motives must remain in the depository of my own breast. My arguments will be open to all, and may be judged of by all. They shall at least be offered in a spirit which will not disgrace the cause of truth. I propose, in a series of papers, to discuss the following interesting particulars: THE UTILITY OF THE UNION TO YOUR POLITICAL PROSPERITY THE INSUFFICIENCY OF THE PRESENT CONFEDERATION TO PRESERVE THAT UNION  THE NECESSITY OF A GOVERNMENT AT LEAST EQUALLY ENERGETIC WITH THE ONE PROPOSED, TO THE ATTAINMENT OF THIS OBJECT  THE CONFORMITY OF THE PROPOSED CONSTITUTION TO THE TRUE PRINCIPLES OF REPUBLICAN GOVERNMENT ITS ANALOGY TO YOUR OWN STATE CONSTITUTION and lastly, THE ADDITIONAL SECURITY WHICH ITS ADOPTION WILL AFFORD TO THE PRESERVATION OF THAT SPECIES OF GOVERNMENT, TO LIBERTY, AND TO PROPERTY. In the progress of this discussion I shall endeavor to give a satisfactory answer to all the objections which shall have made their appearance, that may seem to have any claim to your attention. It may perhaps be thought superfluous to offer arguments to prove the utility of the UNION, a point, no doubt, deeply engraved on the hearts of the great body of the people in every State, and one, which it may be imagined, has no adversaries. But the fact is, that we already hear it whispered in the private circles of those who oppose the new Constitution, that the thirteen States are of too great extent for any general system, and that we must of necessity resort to separate confederacies of distinct portions of the whole.1 This doctrine will, in all probability, be gradually propagated, till it has votaries enough to countenance an open avowal of it. For nothing can be more evident, to those who are able to take an enlarged view of the subject, than the alternative of an adoption of the new Constitution or a dismemberment of the Union. It will therefore be of use to begin by examining the advantages of that Union, the certain evils, and the probable dangers, to which every State will be exposed from its dissolution. This shall accordingly constitute the subject of my next address. PUBLIUS. 1 The same idea, tracing the arguments to their consequences, is held out in several of the late publications against the new Constitution.

Now tokenize the text. This is the same “script” used in other examples. Be careful with the order of the operations; you cannot replace “FEDERALIST” after moving to lower case, for example. Notice that the stopwords remain since the use of these may indicate the style of an author.

replace <- content_transformer(function(text, from, to) str_replace_all(text, from, to))
toSpace <- content_transformer(function(text, pattern) str_replace_all(text, pattern, " "))
toLower <- content_transformer(function(text) tolower(text))
FederalistCorpus <- tm_map(FederalistCorpus, removeWords, c('FEDERALIST', 'No', 'No.'))
FederalistCorpus <- tm_map(FederalistCorpus, toLower)
FederalistCorpus <- tm_map(FederalistCorpus, toSpace, '-|/|,|\\.') 
FederalistCorpus <- tm_map(FederalistCorpus, removePunctuation)
FederalistCorpus <- tm_map(FederalistCorpus, removeNumbers)
FederalistCorpus <- tm_map(FederalistCorpus, stripWhitespace)
inspect(FederalistCorpus[[1]])
<<PlainTextDocument>>
Metadata:  7
Content:  chars: 9355

 general introduction for the independent journal to the people of the state of new york after an unequivocal experience of the inefficacy of the subsisting federal government you are called upon to deliberate on a new constitution for the united states of america the subject speaks its own importance comprehending in its consequences nothing less than the existence of the union the safety and welfare of the parts of which it is composed the fate of an empire in many respects the most interesting in the world it has been frequently remarked that it seems to have been reserved to the people of this country by their conduct and example to decide the important question whether societies of men are really capable or not of establishing good government from reflection and choice or whether they are forever destined to depend for their political constitutions on accident and force if there be any truth in the remark the crisis at which we are arrived may with propriety be regarded as the era in which that decision is to be made and a wrong election of the part we shall act may in this view deserve to be considered as the general misfortune of mankind this idea will add the inducements of philanthropy to those of patriotism to heighten the solicitude which all considerate and good men must feel for the event happy will it be if our choice should be directed by a judicious estimate of our true interests unperplexed and unbiased by considerations not connected with the public good but this is a thing more ardently to be wished than seriously to be expected the plan offered to our deliberations affects too many particular interests innovates upon too many local institutions not to involve in its discussion a variety of objects foreign to its merits and of views passions and prejudices little favorable to the discovery of truth among the most formidable of the obstacles which the new constitution will have to encounter may readily be distinguished the obvious interest of a certain class of men in every state to resist all changes which may hazard a diminution of the power emolument and consequence of the offices they hold under the state establishments and the perverted ambition of another class of men who will either hope to aggrandize themselves by the confusions of their country or will flatter themselves with fairer prospects of elevation from the subdivision of the empire into several partial confederacies than from its union under one government it is not however my design to dwell upon observations of this nature i am well aware that it would be disingenuous to resolve indiscriminately the opposition of any set of men merely because their situations might subject them to suspicion into interested or ambitious views candor will oblige us to admit that even such men may be actuated by upright intentions and it cannot be doubted that much of the opposition which has made its appearance or may hereafter make its appearance will spring from sources blameless at least if not respectable the honest errors of minds led astray by preconceived jealousies and fears so numerous indeed and so powerful are the causes which serve to give a false bias to the judgment that we upon many occasions see wise and good men on the wrong as well as on the right side of questions of the first magnitude to society this circumstance if duly attended to would furnish a lesson of moderation to those who are ever so much persuaded of their being in the right in any controversy and a further reason for caution in this respect might be drawn from the reflection that we are not always sure that those who advocate the truth are influenced by purer principles than their antagonists ambition avarice personal animosity party opposition and many other motives not more laudable than these are apt to operate as well upon those who support as those who oppose the right side of a question were there not even these inducements to moderation nothing could be more ill judged than that intolerant spirit which has at all times characterized political parties for in politics as in religion it is equally absurd to aim at making proselytes by fire and sword heresies in either can rarely be cured by persecution and yet however just these sentiments will be allowed to be we have already sufficient indications that it will happen in this as in all former cases of great national discussion a torrent of angry and malignant passions will be let loose to judge from the conduct of the opposite parties we shall be led to conclude that they will mutually hope to evince the justness of their opinions and to increase the number of their converts by the loudness of their declamations and the bitterness of their invectives an enlightened zeal for the energy and efficiency of government will be stigmatized as the offspring of a temper fond of despotic power and hostile to the principles of liberty an over scrupulous jealousy of danger to the rights of the people which is more commonly the fault of the head than of the heart will be represented as mere pretense and artifice the stale bait for popularity at the expense of the public good it will be forgotten on the one hand that jealousy is the usual concomitant of love and that the noble enthusiasm of liberty is apt to be infected with a spirit of narrow and illiberal distrust on the other hand it will be equally forgotten that the vigor of government is essential to the security of liberty that in the contemplation of a sound and well informed judgment their interest can never be separated and that a dangerous ambition more often lurks behind the specious mask of zeal for the rights of the people than under the forbidden appearance of zeal for the firmness and efficiency of government history will teach us that the former has been found a much more certain road to the introduction of despotism than the latter and that of those men who have overturned the liberties of republics the greatest number have begun their career by paying an obsequious court to the people commencing demagogues and ending tyrants in the course of the preceding observations i have had an eye my fellow citizens to putting you upon your guard against all attempts from whatever quarter to influence your decision in a matter of the utmost moment to your welfare by any impressions other than those which may result from the evidence of truth you will no doubt at the same time have collected from the general scope of them that they proceed from a source not unfriendly to the new constitution yes my countrymen i own to you that after having given it an attentive consideration i am clearly of opinion it is your interest to adopt it i am convinced that this is the safest course for your liberty your dignity and your happiness i affect not reserves which i do not feel i will not amuse you with an appearance of deliberation when i have decided i frankly acknowledge to you my convictions and i will freely lay before you the reasons on which they are founded the consciousness of good intentions disdains ambiguity i shall not however multiply professions on this head my motives must remain in the depository of my own breast my arguments will be open to all and may be judged of by all they shall at least be offered in a spirit which will not disgrace the cause of truth i propose in a series of papers to discuss the following interesting particulars the utility of the union to your political prosperity the insufficiency of the present confederation to preserve that union the necessity of a government at least equally energetic with the one proposed to the attainment of this object the conformity of the proposed constitution to the true principles of republican government its analogy to your own state constitution and lastly the additional security which its adoption will afford to the preservation of that species of government to liberty and to property in the progress of this discussion i shall endeavor to give a satisfactory answer to all the objections which shall have made their appearance that may seem to have any claim to your attention it may perhaps be thought superfluous to offer arguments to prove the utility of the union a point no doubt deeply engraved on the hearts of the great body of the people in every state and one which it may be imagined has no adversaries but the fact is that we already hear it whispered in the private circles of those who oppose the new constitution that the thirteen states are of too great extent for any general system and that we must of necessity resort to separate confederacies of distinct portions of the whole this doctrine will in all probability be gradually propagated till it has votaries enough to countenance an open avowal of it for nothing can be more evident to those who are able to take an enlarged view of the subject than the alternative of an adoption of the new constitution or a dismemberment of the union it will therefore be of use to begin by examining the advantages of that union the certain evils and the probable dangers to which every state will be exposed from its dissolution this shall accordingly constitute the subject of my next address publius the same idea tracing the arguments to their consequences is held out in several of the late publications against the new constitution 

Document term matrix

We can start to do statistics now. The document term matrix contains the counts of every word time in each document. Each row represents a Federalist Paper, and each column is a word type. Because most of the matrix entries are zeros, it is held in “sparse” format. Only 8% of the elements in the document term matrix are not zero. (Notice that you cannot recover the source corpus from the document term matrix. This matrix represents each document as a “bag of words”.)

dtm <- DocumentTermMatrix(FederalistCorpus)
dtm
<<DocumentTermMatrix (documents: 85, terms: 8590)>>
Non-/sparse entries: 58211/671939
Sparsity           : 92%
Maximal term length: 19
Weighting          : term frequency (tf)

It is now simple to use matrix functions from R to find the number of words in each document and the number of times each type appears (albeit at the cost of converting the sparse matrix into a dense matrix).

ni <- rowSums(as.matrix(dtm))  # tokens in each document
mj <- colSums(as.matrix(dtm))  # columns are named by the word types; frequency of each

Lots more words in these papers than in wine review. (qplot is the ggplot version of plot.)

qplot(1:85, ni, xlab="Federalist Paper", ylab="Word Count")

The frequency counts in mj are named and in alphabetical order. We can use these names to produce a nice bar graph with ggplot.

Freq <- tibble(type = names(mj), count = mj)     # ggplot and dplyr want data frames
Freq %>%
    top_n(25, count)                         %>%
    mutate(type=reorder(type,count))         %>%     # rather than alphabetical
    ggplot(aes(type,count)) + geom_col() + coord_flip()

Let’s see what happens without the stop words.

Freq %>%
    filter (!type %in% stopwords('english')) %>%     # syntax of %in% resembles %>%
    top_n(25, count)                         %>%
    mutate(type=reorder(type,count))         %>%     # rather than alphabetical
    ggplot(aes(type,count)) + geom_col() + coord_flip()

This is a good chance to check out whether this text matches a Zipf distribution.

Recall that a Zipf distribution is characterized by a power law: the frequency of the second most common word is inversely proportional to its rank, \(p_k \propto 1/k\). Said differently, the frequency of the second most common word is half that of the most common, the frequency of the third is one-third the most common, etc. A little algebra shows that for this to occur, then \(\log p_k \approx b_0 - \log k\). That is, a plot of the log of the frequencies should be linear in the log of the rank \(k\), with slope -1. In this example, the slope is larger than -1, but the plot is quite linear.

zipf_plot(mj)

Call:
lm(formula = ly ~ lx, data = df[1:min(n.fit, nrow(df)), ])

Coefficients:
(Intercept)           lx  
     9.1633      -0.8661  

The least squares slope for all of the data would besteeper, being dominated by the many less common words.

Comparing vocabularies

We can compare vocabulary used by Hamilton to that used by Madison by picking out the papers known to have been written by one or the other. These counts form the basis of naive Bayes model. It is of some concern that we have so many fewer papers written by Madison.

dtm.madison <- dtm[FederalistPapers$author=='MADISON',]
dim(dtm.madison)
[1]   15 8590
dtm.hamilton <- dtm[FederalistPapers$author=='HAMILTON',]
dim(dtm.hamilton)
[1]   51 8590
mj.madison  <- colSums(as.matrix(dtm.madison))       # type frequencies for each
mj.hamilton <- colSums(as.matrix(dtm.hamilton))
Counts <- bind_rows(                                 # dplyr style stacks these
            data_frame(author="Madison",  word=names(mj.madison),  count=mj.madison),
            data_frame(author="Hamilton", word=names(mj.hamilton), count=mj.hamilton))
Counts

Counts of words by author are confounded with the frequency of authorship: Hamilton wrote more of the Federalist Papers. (We have too few for John Jay. Plus he is not considered in the running for writing the papers of unknown authorship.) The Tidy Text book has many examples of this style of plotting produced by ggplot.

Counts %>%
    filter(300 < count) %>%
    ggplot(aes(word,count)) + geom_col() + coord_flip() +
    facet_wrap(~author)

Proportions make more sense.

Counts %>%
    group_by(author) %>%
    mutate(proportion = count / sum(count)) %>%
    filter(0.005 < proportion)        %>%
    ggplot(aes(word,proportion)) + geom_col() + coord_flip() +
    facet_wrap(~author)

A scatterplot offers yet a different way to view these data. Rather than look at bar charts, plot the proportions for Hamilton versus those for Madison. A scatterplot requires two variables… one for the Hamilton proportions and another for the Madison proportions. Our data so far has just one column to facilitate using ggplot. The function spread in dplyr splits a column into two.

Proportions <- Counts %>%
    group_by(author)                        %>%
    mutate(proportion = count / sum(count)) %>%
    select(-count)                          %>%   # messed up without this
    spread(key=author, value=proportion)
Proportions

A quick check that these are indeed probability distributions.

colSums(Proportions[,2:3], na.rm=TRUE)
Hamilton  Madison 
       1        1 
Proportions %>% 
    filter(0.001 < Hamilton & 0.001 < Madison) %>%
    ggplot(aes(x=Hamilton, y=Madison))  +
    geom_abline(color = "gray40", lty = 2) +
    geom_point(color='lightgray') +
    geom_text(aes(label = word), check_overlap=TRUE, vjust=1.5) + 
    scale_y_log10() + scale_x_log10()

Building the naive Bayes classifier

We can use these distributions of word types to classify the documents of disputed authorship.

Naive Bayes allows us to convert these distributions over the word types into a classifier. The idea is intuitive and works like this. Ignoring issues of sampling variation (as if we had computed the word frequencies from a very large corpus), for every word type \(W_j\) we “know” the probability \(P_{author}(W_j)\) for \(author \in \{Hamilton, Madison\}\). Here comes two big assumptions: we’re going to ignore the order of the words and then pretend that they occur independently conditionally on knowing the author.

There’s a rationale that supports this approach, and this rationale uses Bayes Theorem (hence the name). Given a document \(D = \{w_1, w_2, \ldots, w_i, \ldots, w_n\}\) – a sequence of word tokens – we want to assign the document to a class, namely identify the author as either Hamilton or Madison. The optimal solution is to assign based on the maximal probability, \(P(author|D)\). But how can we find that conditional probability? Bayes Rule: \(P(author|D) = P(D|author)P(author)/P(D)\). The normalizing factor \(P(D)\) is constant (does not depend on the author), so we need to find the author that maximizes \(P(D|author)P(author)\). The prior probability is something we can defer to historians (or just set to 1/2), but the other probability is harder.

What should we use for \(P(D|author) = P(\{w_1, w_2, \ldots, w_i, \ldots, w_n\}|author)\)? That’s easy if we’re willing to assume the word choices are independent given the author: \[ P(\{w_1, w_2, \ldots, w_n\}|author) = \prod_{i=1}^n P(w_i|author)\]

This expression explains why this is called “naive” Bayes! Do you really think the choice of the next word is independent given you know the author. That said, this assumption makes it easy to compute because we have both the proportions and the counts for the various documents.

The only catch is what to do if, say, Paper #49 has a word that, say, Hamilton never used in the papers he is known to have written. Should this make the probability of Hamilton being the author zero? There are an elaborate collection of ways to handle such out-of-vocabulary words. Good-Turing smoothing replaces the zero (and shifts other small probabilities as well). (Yes, this is the same Alan Turing as in the recent movie.) The function good_turing_probababilities (from the \(\tt text_files.R\) collection) does the needed adjustment.

prob.madison  <- good_turing_probabilities(mj.madison )
prob.hamilton <- good_turing_probabilities(mj.hamilton)

The log-probability is now easy to compute. (Be careful… we want larger values, but log probabilities are negative) Let’s start with papers of known authorship. Paper #1 is by Hamilton, and the naive Bayes agrees.

C <- as.matrix(dtm)
paper <- 1
sum(log(prob.hamilton)*C[paper,])
[1] -8243.313
sum(log(prob.madison) *C[paper,])
[1] -8426.641

Madison wrote Paper #10, and again naive Bayes agrees.

paper <- 10
sum(log(prob.hamilton)*C[paper,])
[1] -15701.96
sum(log(prob.madison) *C[paper,])
[1] -15277.86

For paper 49 (of debated authorship) naive Bayes gives the authorship nod to Madison, albeit by a much closer margin than the others of known authorship (which were used to build the probabilities used by naive Bayes).

paper <- 49
sum(log(prob.hamilton)*C[paper,])
[1] -8284.31
sum(log(prob.madison) *C[paper,])
[1] -8264.077

We can make a nice plot that summarizes these results for all of the papers. Matrix multiplication avoids looping over the papers.

dim(dtm)
[1]   85 8590
length(prob.hamilton)
[1] 8590
lp.hamilton <- C %*% log(prob.hamilton)
lp.madison  <- C %*% log(prob.madison)
diff <- lp.hamilton - lp.madison
diff[c(1,10,49)]
[1]  183.32795 -424.09451  -20.23272

Naive Bayes assigns most – but not all – of the disputed papers to Madison. The Wiki would differ! What would LSA do?

tibble(paper=1:85, author=FederalistPapers$author, diff=as.vector(diff)) %>%
    ggplot(aes(paper,diff,color=author)) +
    geom_point() + labs(y="Log Likelihood Ratio")

NA
LS0tCnRpdGxlOiAiVGV4dCBhcyBEYXRhOiBOYWl2ZSBCYXllcyIKb3V0cHV0OiBodG1sX25vdGVib29rCmF1dGhvcjogUm9iZXJ0IFN0aW5lCmRhdGU6IEp1bHkgMjAxNwotLS0KCiMgU2V0dXAgUgoKSSB1c2UgdGhlc2UgcGFja2FnZXMgc28gZnJlcXVlbnRseSBJIHdpbGwganVzdCBsb2FkIHRoZW0gYWxsIHVwIGZyb250LiAgT3RoZXJzIHRoYXQgYXJlIG9jY2FzaW9uYWxseSB1c2VmdWwgd2lsbCBiZSBsb2FkZWQgYXMgbmVlZGVkLiAgYHJlcXVpcmVgIHN0YXRlbWVudHMgZ2VuZXJhbGx5IHByZWNlZGUgdGhlIHVzZSBvZiBsZXNzIGNvbW1vbiBmdW5jdGlvbnMgZnJvbSB0aGVzZSBwYWNrYWdlcyBzbyB5b3UgaGF2ZSBhbiBpZGVhIHdoZXJlIHRoZXkgYXJlIGZyb20uCgpgYGB7cn0KcmVxdWlyZSh0bSkKcmVxdWlyZShzdHJpbmdyKSAgICAKcmVxdWlyZSh0aWR5dmVyc2UpCgpzb3VyY2UoInRleHRfdXRpbHMuUiIpICAgIyBnb29kIHR1cmluZywgemlwZiBwbG90CmBgYAoKTm90aWNlIHRoYXQgaW4gdGhlIGZvbGxvd2luZyBjb2RlIHRoZSBvcHRpb24gImV2YWw9RkFMU0UiIGlzIG9mdGVuIHNldCBpbiB0aGUgaGVhZGVyIHRvIGEgY2h1bmsgb2YgUiBjb2RlLCB0eXBpY2FsbHkgZm9yIGBWaWV3YCBjb21tYW5kcy4gIFRoaXMgb3B0aW9uIHRlbGxzIFItU3R1ZGlvIHRvIHNraXAgdGhhdCBjaHVuayB3aGVuIHRoZSAiUnVuIGFsbCBjaHVua3MiIG9yICJSdW4gYWxsIGNodW5rcyBhYm92ZSIgY29tbWFuZHMgYXJlIGV4ZWN1dGVkLiAgWW91IGNhbiBhbHdheXMgcnVuIHRoZXNlIGRpcmVjdGx5LgoKCiMgR2V0dGluZyB0aGUgc291cmNlIGRhdGE6IFRoZSBGZWRlcmFsaXN0IFBhcGVycwoKU3RhcnQgYnkgb2J0YWluaW5nIHRoZSBvcmlnaW5hbCBzb3VyY2UgZnJvbSB0aGUgY29sbGVjdGlvbiBvZiBvcGVuLXNvdXJjZSB0ZXh0IG9mZmVyZWQgYnkgUHJvamVjdCBHdXRlbmJlcmcuIAoKYGBge3J9CnJlcXVpcmUoZ3V0ZW5iZXJncikKCmNvbnRlbnQgPC0gZ3V0ZW5iZXJnX3dvcmtzKCkKbmFtZXMoY29udGVudCkKYGBgCgpUaGVyZSBhcmUgdG9vIG1hbnkgdGl0bGVzIHRvIHNlYXJjaCBtYW51YWxseSwgc28gdXNlIFIuCgpgYGB7cn0KbGVuZ3RoKGNvbnRlbnQkdGl0bGUpCmBgYAoKV2hlcmUgYXJlIHRoZSBGZWRlcmFsaXN0IHBhcGVycyBpbiB0aGlzIGNvbGxlY3Rpb24/ICBBcyBpbiBvdGhlciBzaXR1YXRpb25zLCAqcmVndWxhciBleHByZXNzaW9ucyogd29yayBhcmUgdmVyeSB1c2VmdWwgaWYgeW91J3JlIGRlYWxpbmcgd2l0aCB0ZXh0IGRhdGEuCgpgYGB7cn0KcmVxdWlyZShzdHJpbmdyKQpiIDwtIHN0cl9kZXRlY3QoY29udGVudCR0aXRsZSwgIi4qRmVkZXJhbGlzdC4qIikKaSA8LSB3aGljaChiKQppCmBgYAoKYGBge3J9CmNvbnRlbnQkdGl0bGVbaV0KY29udGVudCRhdXRob3JbaV0gICAgICAgICMgV2hhdC4uLiBubyBhdXRob3I/CmNvbnRlbnQkZ3V0ZW5iZXJnX2lkW2ldCmBgYAoKTm93IHRoYXQgd2UgaGF2ZSB0aGUgSUQsIHdlIGNhbiBnZXQgdGhlIHRleHQgaXRzZWxmLiAgKEkgcm91dGluZWx5IHVzZSBjYXBpdGFsIGxldHRlcnMgZm9yIHRoZSBuYW1lcyBvZiBkYXRhIGZyYW1lcy4pCgpgYGB7cn0KVGhlRmVkZXJhbGlzdCA8LSBndXRlbmJlcmdfZG93bmxvYWQoMTgpCmBgYAoKVGhlIHJlc3VsdCBpcyBhIHRhYmxlIG9mIDI1LDU2MyByb3dzIGFuZCB0d28gY29sdW1ucy4KCmBgYHtyfQpkaW0oVGhlRmVkZXJhbGlzdCkKYGBgCgpJdCBpcyBpbXBvcnRhbnQgdG8gdGFrZSBhIGxvb2sgYXQgdGhlICJyYXciIGRhdGEgaW4gb3JkZXIgdG8gYXBwcmVjaWF0ZSB0aGUgZm9sbG93aW5nIHN0ZXBzLgoKYGBge3J9ClZpZXcoVGhlRmVkZXJhbGlzdCkKYGBgCgpPbmx5IHRoZSBzZWNvbmQgY29sdW1uIGlzIG9mIGludGVyZXN0IHRvIHVzLiAgSXQgaGFzIHRoZSBhY3R1YWwgdGV4dCBsaW5lcywgbWFueSBvZiB3aGljaCBhcmUgYmxhbmsuIChXaHkgdHdvIGNvbHVtbnM/IGBndXRlbmJlcmdfZG93bmxvYWRgIGFsbG93cyB5b3UgdG8gZXh0cmFjdCBzZXZlcmFsIGRvY3VtZW50cyBhdCBvbmNlOyB0aGUgZmlyc3QgY29sdW1uIHdvdWxkIGJlIHVzZWQgdG8gc2VwYXJhdGUgdGhlc2UuIEJlY2F1c2UgYWxsIG9mIHRoZXNlIGxpbmVzIGluIHRoaXMgZXhhbXBsZSBjb21lIGZyb20gdGhlIHNhbWUgZG9jdW1lbnQsIHRoZSBmaXJzdCBjb2x1bW4gaXMgY29uc3RhbnQuICBJdCB3b3VsZCBiZSBtb3JlIGNvbW1vbiB0byBmaW5kIHRoZXNlIGRhdGEgaW4gc2VwYXJhdGUgZmlsZXMsIG9uZSBmb3IgZWFjaCBwYXBlci4pCgpQdWxsIG91dCB0aGUgdGV4dCBjb2x1bW4sIGJ1dCBsZWF2ZSB0aGUgb25lIGNvbHVtbiBpbiBhIGRhdGEgZnJhbWUgdG8gc2ltcGxpZnkgdGhlIHN1YnNlcXVlbnQgcHJvY2Vzc2luZy4gIChgc2VsZWN0YCBjb21lcyBmcm9tIHRoZSBgdGlkeXZlcnNlYCBjb2xsZWN0aW9uLikKCmBgYHtyfQpUaGVGZWRlcmFsaXN0IDwtIHNlbGVjdChUaGVGZWRlcmFsaXN0LCB0ZXh0KQpkaW0oVGhlRmVkZXJhbGlzdCkKYGBgCgpbSnVzdCBpbiBjYXNlLCBJIHNhdmVkIHRoZXNlIGRhdGEgaW4gY2FzZSB0aGVyZSdzIGEgcHJvYmxlbSB3aXRoIGludGVybmV0IGFjY2VzcyBkdXJpbmcgY2xhc3MuCmBgYHtyIGV2YWw9RkFMU0V9CnNhdmUoVGhlRmVkZXJhbGlzdCwgZmlsZT0iZmVkZXJhbGlzdC5zYXYiKQpgYGAKWW91IGNhbiByZWNvdmVyIHRoZSBmaWxlIHVzaW5nCmBgYHtyIGV2YWw9RkFMU0V9CmxvYWQoImZlZGVyYWxpc3Quc2F2IikKYGBgCkhvcGVmdWxseSwgSSB3b24ndCBuZWVkIHRvIHVzZSB0aGVzZSBjb21tYW5kcyBpbiBjbGFzcy5dCgoKIyBQcmVwYXJpbmcgdGhlIHRleHQKClRoZSBmaXJzdCB0YXNrcyBhcmUgdG8gcmVtb3ZlIHRoZXNlIGJsYW5rIGxpbmVzIChhc3N1bWluZyB3ZSdyZSBub3QgaW50ZXJlc3RlZCBpbiwgc2F5LCBjb3VudGluZyB0aG9zZSkgYW5kIHRoZW4gZ3JvdXAgdGhlIHRleHQgaW50byB0aGUgc2VwYXJhdGUgcGFwZXJzLiAgYGRwbHlyYCBpcyBjb252ZW5pZW50IGZvciBmb3IgdGhpcyB0YXNrLCBmaWx0ZXIgKHNlbGVjdGluZykgcm93cyBmcm9tIHRoaXMgb25lLWNvbHVtbiAqZGF0YSBmcmFtZSogdG8gb2J0YWluIGEgbmV3IGRhdGEgZnJhbWUgd2l0aCBmZXdlciByb3dzLiAgVGhlIGRhdGEgY29udGFpbmVkIGFib3V0IDcsMDAwIGJsYW5rIGxpbmVzLgoKYGBge3J9CkZlZFBhcGVycyA8LSBUaGVGZWRlcmFsaXN0ICU+JSBmaWx0ZXIodGV4dCAhPSAiIikKZGltKEZlZFBhcGVycykKYGBgCgpOb3cgZGl2aWRlIHRoZSBgdGV4dGAgY29sdW1uIGludG8gdGhlIHNlcGFyYXRlIHBhcGVycywgY29sbGVjdGluZyB0aGUgbGluZXMgZm9yIGVhY2ggcGFwZXIgaW50byBvbmUuCgpIZXJlJ3MgYSB0cmljayBJIGxlYXJuZWQgZnJvbSB0aGUgIlRpZHkgVGV4dCIgYm9vayB0byBoYW5kbGUgdGhpcyB0YXNrLiAgVGhlIGlkZWEgaXMgdG8gdXNlIHRoZSBjb3VudGVyIGZlYXR1cmUgb2YgYGRwbHlyYCB0byBhZGQgYSBwYXBlciBudW1iZXIuICBUaGVuIEkgY2FuIGpvaW4gdGhlIGxpbmVzIHdpdGggdGhlc2UgbnVtYmVycy4gIE9uY2UgYWdhaW4sIGEgcmVndWxhciBleHByZXNzaW9uIGlzIHVzZWZ1bC4gKEVhY2ggcGFwZXIgaXMgc3ByZWFkIG92ZXIgc2V2ZXJhbCBsaW5lcyB0aGF0IHdlJ2QgbGlrZSB0byBqb2luIHRvZ2V0aGVyIGFzIGEgc2luZ2xlIGRvY3VtZW50LikKCmBgYHtyfQpwYXR0ZXJuIDwtICJeRkVERVJBTElTVFsuIF0qTm9cXC4iICAgIyBeIGRlbm90ZXMgc3RhcnQgb2YgbGluZQpGZWRQYXBlcnMgPC0gRmVkUGFwZXJzICU+JSBtdXRhdGUocGFwZXIgPSBjdW1zdW0oc3RyX2RldGVjdCh0ZXh0LHBhdHRlcm4pKSkKCmhlYWQoRmVkUGFwZXJzKQpgYGAKCmBgYHtyfQp0YWlsKEZlZFBhcGVycykKYGBgCgpgYGB7ciBldmFsPUZBTFNFfQpWaWV3KEZlZFBhcGVycykKYGBgCgpBZnRlciBza2ltbWluZyB0aGUgZmlsZSwgb25lIGRpc2NvdmVycyB0aGlzLi4uCgpgYGB7cn0KYXMuY2hhcmFjdGVyKEZlZFBhcGVyc1sxNDg0NywndGV4dCddKQpgYGAKCkkgd2lsbCByZW1vdmUgdGhlIHNlY29uZCwgc29tZXdoYXQgbWFudWFsbHkgaGVyZSBmcm9tIHRoZSBzb3VyY2UgZGF0YSBhbmQgdGhlbiBhc3NpZ24gcGFwZXIgbnVtYmVycyBhZ2Fpbi4gIChBbHRlcm5hdGVseSwgeW91IGNvdWxkIGhhdmUgdXNlZCB0aGUgYXNzaWduZWQgcGFwZXIgbnVtYmVyLCBidXQgdGhhdCBsZWF2ZXMgYSBzdHJhZ2dsaW5nIGxpbmUgYW5kIG1lc3NlcyB1cCB0aGUgbnVtYmVycyBvZiB0aGUgZm9sbG93aW5nIHBhcGVycy4pCgpgYGB7cn0KRmVkUGFwZXJzIDwtIFRoZUZlZGVyYWxpc3QgJT4lIAogICAgZmlsdGVyKHRleHQgIT0gIiIpICAgICAlPiUKICAgIGZpbHRlcihyb3dfbnVtYmVyKCkgPCAxNDg0NyB8IDE1MTU1IDwgcm93X251bWJlcigpKSAlPiUKICAgIG11dGF0ZShwYXBlciA9IGN1bXN1bShzdHJfZGV0ZWN0KHRleHQscGF0dGVybikpKQoKaGVhZChGZWRQYXBlcnMpCmBgYAoKQlRXLCB0aGUgbGVhZGluZyBudW1iZXJzIGFyZSAqZm9vdG5vdGVzKi4KCmBgYHtyfQp0YWlsKEZlZFBhcGVycykKYGBgCgpOb3cgcGljayBvdXQgdGhlIGF1dGhvciBhbmQgYnVpbGQgYSBkYXRhIGZyYW1lIHdpdGggZWFjaCBwYXBlciBhcyBhIGRvY3VtZW50LiAgVGhlIGF1dGhvciBvZiB0aGUgZmlyc3QgcGFwZXIgKEFsZXhhbmRlciBIYW1pbHRvbikgaXMgbGlzdGVkIG9uIHRoZSA0dGggbGluZS4gIExldCdzIHNlZSBpZiB0aGF0IHBhdHRlcm4gaG9sZHMgZm9yIG90aGVyIHBhcGVycy4gIFN0cmluZyBtYXRjaGluZyB3aXRoIGEgYmFzaWMgcmVndWxhciBleHByZXNzaW9uIG1ha2VzIHRoaXMgZWFzeS4gCgpVbmZvcnR1bmF0ZWx5LCB0aGUgcGF0dGVybiB2YXJpZXMgd2hlbiB0b3BpY3MgYXJlIGNvbnRpbnVlZCBvciBkYXRlcyBnZXQgYWRkZWQuCgpgYGB7cn0KVGVtcCA8LUZlZFBhcGVycyAgICAgICAgICAgICAgICAgICU+JSAKICAgIG11dGF0ZShsaW5lPXJvd19udW1iZXIoKSkgICAgICU+JSAgIAogICAgZmlsdGVyKHN0cl9kZXRlY3QodGV4dCwiSEFNSUxUT058TUFESVNPTnxKQVl8RkVERVJBTElTVCIpKQpUZW1wCmBgYAoKYGBge3J9CkZlZFBhcGVyc1szMjE6MzI1LF0KYGBgCgpgYGB7cn0KRmVkUGFwZXJzWzE2MTE6MTYxNyxdCmBgYAoKSXQgaXMgdXNlZnVsIHRvIHNraW0gdGhpcyB0ZW1wb3JhcnkgZGF0YSBmcmFtZSB0byBzZWUgdGhlIG90aGVyIGRpZmZlcmVuY2VzLCBzdWNoIGFzIHdoYXQgaGFwcGVucyB3aGVuIHRoZXJlIGFyZSBtdWx0aXBsZSBhdXRob3JzICgjMjApIG9yIHVua25vd24gYXV0aG9yc2hpcCAoIzUwKQoKYGBge3IgZXZhbD1GQUxTRX0KVmlldyhUZW1wKQpgYGAKClRvIGtlZXAgdHJhY2sgb2YgdGhlIGF1dGhvcnNoaXAgb2YgdGhlIDg1IHBhcGVycywgIHJlbW92ZSB0aGUgYXV0aG9yIG5hbWVzIHRvIGEgc2VwYXJhdGUgZGF0YSBmcmFtZSBmb3IgbGF0ZXIgdXNlIGluIGxhYmVsaW5nIHRoZSBGZWRlcmFsaXN0IFBhcGVycyB3aXRoIGEgKmpvaW4qIG9wZXJhdGlvbi4gIAoKYGBge3J9CkF1dGhvcnMgPC0gVGVtcCAlPiUgZmlsdGVyKHN0cl9kZXRlY3QodGV4dCwiSEFNSUxUT058TUFESVNPTnxKQVkiKSkKZGltKEF1dGhvcnMpCmBgYAoKSXQgaXMgdXNlZnVsIGZvciBvdXIgbGF0ZXIgY2xhc3NpZmljYXRpb24gdGFzayB0byB0YWtlIGEgbG9vayBhdCB0aGUgbmFtZXMgb2YgdGhlIGF1dGhvcnMuICAKCmBgYHtyIGV2YWw9RkFMU0V9ClZpZXcoQXV0aG9ycykKYGBgCgpBY2NvcmRpbmcgdG8gdGhlIEd1dGVuYmVyZyB2ZXJzaW9uLCBwYXBlcnMgNDktNTcgYW5kIDYyLTYzIGhhdmUgImRpc3B1dGVkIiBhdXRob3JzaGlwOiBlaXRoZXIgSGFtaWx0b24gb3IgTWFkaXNvbi4gIChXaWtpcGVkaWEgaGFzIHNsaWdodGx5IGRpZmZlcmVudCBsaXN0IG9mIHRoZSBwYXBlcnMgb2Ygb2YgZGlzcHV0ZWQgYXV0aG9yc2hpcCwgbmFtZWx5IDQ5LTU4IGFuZCA2Mi02My4pCgpOb3cgcmVtb3ZlIHRoZSBhdXRob3IgbmFtZXMgZnJvbSB0aGUgZGF0YS4gIChZb3UgY291bGQgcmVtb3ZlIHRoZSBudW1iZXJpbmcgbGluZSBhcyB3ZWxsLikKYGBge3J9CkZlZFBhcGVycyA8LSBGZWRQYXBlcnMgJT4lIGZpbHRlcighc3RyX2RldGVjdCh0ZXh0LCJIQU1JTFRPTnxNQURJU09OfEpBWSIpKQpkaW0oRmVkUGFwZXJzKQpgYGAKYGBge3J9CmhlYWQoRmVkUGFwZXJzKQpgYGAKCkF0IHRoaXMgcG9pbnQsIHRoZSBgdGlkeXRleHRgIGFwcHJvYWNoIHRvIHRleHQgYW5hbHlzaXMgZGl2ZXJnZXMgZnJvbSB0aGUgYHRtYCBhcHByb2FjaC4gIChJIGhhdmUgYWx0ZXJuYXRpbmcgb3BpbmlvbnMsIGJ1dCB3aWxsIHVzZSB0aGUgInRtIiBhcHByb2FjaCBpbiB0aGVzZSBsZWN0dXJlcy4pIEZvciBgdG1gLCBwdWxsIHRoZSB0ZXh0IGZvciBlYWNoIGRvY3VtZW50IHRvZ2V0aGVyIHJhdGhlciB0aGFuIGJlaW5nIHNwcmVhZCBvdmVyIHNldmVyYWwgbGluZXMuICBgdGFwcGx5YCBpcyBhIHZlcnkgdXNlZnVsIGZ1bmN0aW9uIGZvciBqb2JzIGxpa2UgdGhpcy4KCmBgYHtyfQpGZWRQYXBlcnMgPC0gdGFwcGx5KEZlZFBhcGVycyR0ZXh0LCAgICAgICAgICMgZGF0YSBvbiBzZXBhcmF0ZSBsaW5lcwogICAgICAgICAgICAgICAgICAgIEZlZFBhcGVycyRwYXBlciwgICAgICAgICMgZ3JvdXBpbmcgdmFyaWFibGUKICAgICAgICAgICAgICAgICAgICBzdHJfYywgY29sbGFwc2U9JyAnKSAgICAjIGZ1bmN0aW9uIGFwcGxpZWQgdG8gZ3JvdXAgICAoYWthLCAicGFzdGUiKQpgYGAKCmBgYHtyfQpsZW5ndGgoRmVkUGFwZXJzKQpgYGAKCk5vdyBsYWJlbCB0aGVzZSBwYXBlcnMgYnkgdGhlIGF1dGhvciBieSBtYWtpbmcgYSBkYXRhIGZyYW1lLiAgCgpgYGB7cn0KaGVhZChBdXRob3JzJHRleHQpCmBgYAoKYGBge3J9CkZlZGVyYWxpc3RQYXBlcnMgPC0gdGliYmxlKGF1dGhvcj1BdXRob3JzJHRleHQsIHRleHQ9RmVkUGFwZXJzKQpgYGAKCkhhdmUgYSBsb29rIGF0IHRoZSByZXN1bHQuCgpgYGB7cn0KaGVhZChGZWRlcmFsaXN0UGFwZXJzKQpgYGAKClNhdmUgdGhpcyB2ZXJzaW9uIHRvbyAodGhvdWdoIGl0IGlzIG5vdCB0b28gaGFyZCB0byByZWNyZWF0ZSkuCgpgYGB7ciBldmFsPUZBTFNFfQpzYXZlKEZlZGVyYWxpc3RQYXBlcnMsIGZpbGU9In4vZGF0YS90ZXh0L2ZlZGVyYWxpc3QvRmVkZXJhbGlzdFBhcGVycy5zYXYiKQpgYGAKCk5vdyBJIGNhbiAicmVqb2luIiB0aGUgYW5hbHlzaXMgZnJvbSB0aGlzIHBvaW50IGJ5IHJ1bm5pbmcgdGhpcyBjb21tYW5kLgoKYGBge3IgZXZhbD1GQUxTRX0KbG9hZCgifi9kYXRhL3RleHQvZmVkZXJhbGlzdC9GZWRlcmFsaXN0UGFwZXJzLnNhdiIpCmBgYAoKCiMgVG9rZW5pemF0aW9uCgpUaGUgbmV4dCB0YXNrIHNlZW1zIG9idmlvdXMsIGJ1dCBoYXMgYSBsYXN0aW5nIGltcGFjdDogIFdoYXQncyBhIHdvcmQ/ICBJcyBhIG51bWJlciBhIHdvcmQ/ICBXaGF0IGFib3V0IHB1bmN0dWF0aW9uPyAgQXJlICJzdWJqZWN0IiBhbmQgInN1YmplY3RzIiBkaWZmZXJlbnQgd29yZHM/ICBgdG1gIGhhcyBhIGNvbGxlY3Rpb24gb2YgdG9vbHMgZm9yIHRha2luZyBjYXJlIG9mIHRoZXNlIHRhc2tzLCBidXQgeW91IG5lZWQgdG8gZGVjaWRlIHdoaWNoIHRvIHVzZS4gCgpBcyBpbiBvdGhlciBleGFtcGxlcywgcHV0IHRoZSB0ZXh0IGludG8gYSBgY29ycHVzYCBvYmplY3QuICBBIGNvcnB1cyBpcyB1c3VhbGx5IGNyZWF0ZWQgd2hlbiB1c2luZyBgdG1gIHdoZW4gYSBjb2xsZWN0aW9uIG9mIHNlcGFydGUgZG9jdW1lbnRzIGlzIHJlYWQgaW50byBSLiAgYHRtYCBuaWNlbHkgaGFuZGxlcyBtYW55IGRpZmZlcmVudCB0eXBlcyBvZiBkb2N1bWVudHMsIGluY2x1ZGluZyBQREYgYW5kIFdvcmQgZmlsZXMuICBJbiB0aGlzIGV4YW1wbGUsIHRoZSB0ZXh0IGlzIGFscmVhZHkgaW4gYSB2YXJpYWJsZSwgc28gdXNlIGBWZWN0b3JTb3VyY2VgIHRvIGNvbnZlcnQgdGhlIGRhdGEgaW50byBhIGNvcnB1cy4KCmBgYHtyfQpGZWRlcmFsaXN0Q29ycHVzIDwtIENvcnB1cyhWZWN0b3JTb3VyY2UoRmVkZXJhbGlzdFBhcGVycyR0ZXh0KSkKRmVkZXJhbGlzdENvcnB1cwpgYGAKCkEgY29ycHVzIG9iamVjdCBpbiBgdG1gIGlzIGEgZGVjb3JhdGVkIGxpc3QsIGEgbGlzdCB0aGF0IGhhcyBiZWVuIGFkb3JuZWQgd2l0aCBzcGVjaWFsIGF0dHJpYnV0ZXMgd2hlbiBjcmVhdGVkLiAgVGhhdCBtZWFucyB3ZSBjYW4gcGVlayBpbnRvIHRoZSBjb3JwdXMgYXMgaWYgaXQgd2VyZSBhIGxpc3QgYnkgcmVmZXJyaW5nIHRvIHRoZSBudW1iZXIgZWxlbWVudHMgb2YgdGhlIGxpc3QgKHdoaWNoIGFyZSB0aGUgZG9jdW1lbnRzKS4KClRvIHNlZSB0aGUgdGV4dCBpdHNlbGYsIHVzZSB0aGUgYGluc3BlY3RgIG1ldGhvZAoKYGBge3J9Cmluc3BlY3QoRmVkZXJhbGlzdENvcnB1c1tbMV1dKQpgYGAKCk5vdyB0b2tlbml6ZSB0aGUgdGV4dC4gIFRoaXMgaXMgdGhlIHNhbWUgInNjcmlwdCIgdXNlZCBpbiBvdGhlciBleGFtcGxlcy4gIEJlIGNhcmVmdWwgd2l0aCB0aGUgb3JkZXIgb2YgdGhlIG9wZXJhdGlvbnM7IHlvdSBjYW5ub3QgcmVwbGFjZSAiRkVERVJBTElTVCIgYWZ0ZXIgbW92aW5nIHRvIGxvd2VyIGNhc2UsIGZvciBleGFtcGxlLiAgTm90aWNlIHRoYXQgdGhlIHN0b3B3b3JkcyByZW1haW4gc2luY2UgdGhlIHVzZSBvZiB0aGVzZSBtYXkgaW5kaWNhdGUgdGhlIHN0eWxlIG9mIGFuIGF1dGhvci4KCmBgYHtyfQpyZXBsYWNlIDwtIGNvbnRlbnRfdHJhbnNmb3JtZXIoZnVuY3Rpb24odGV4dCwgZnJvbSwgdG8pIHN0cl9yZXBsYWNlX2FsbCh0ZXh0LCBmcm9tLCB0bykpCnRvU3BhY2UgPC0gY29udGVudF90cmFuc2Zvcm1lcihmdW5jdGlvbih0ZXh0LCBwYXR0ZXJuKSBzdHJfcmVwbGFjZV9hbGwodGV4dCwgcGF0dGVybiwgIiAiKSkKdG9Mb3dlciA8LSBjb250ZW50X3RyYW5zZm9ybWVyKGZ1bmN0aW9uKHRleHQpIHRvbG93ZXIodGV4dCkpCgpGZWRlcmFsaXN0Q29ycHVzIDwtIHRtX21hcChGZWRlcmFsaXN0Q29ycHVzLCByZW1vdmVXb3JkcywgYygnRkVERVJBTElTVCcsICdObycsICdOby4nKSkKRmVkZXJhbGlzdENvcnB1cyA8LSB0bV9tYXAoRmVkZXJhbGlzdENvcnB1cywgdG9Mb3dlcikKRmVkZXJhbGlzdENvcnB1cyA8LSB0bV9tYXAoRmVkZXJhbGlzdENvcnB1cywgdG9TcGFjZSwgJy18L3wsfFxcLicpIApGZWRlcmFsaXN0Q29ycHVzIDwtIHRtX21hcChGZWRlcmFsaXN0Q29ycHVzLCByZW1vdmVQdW5jdHVhdGlvbikKRmVkZXJhbGlzdENvcnB1cyA8LSB0bV9tYXAoRmVkZXJhbGlzdENvcnB1cywgcmVtb3ZlTnVtYmVycykKRmVkZXJhbGlzdENvcnB1cyA8LSB0bV9tYXAoRmVkZXJhbGlzdENvcnB1cywgc3RyaXBXaGl0ZXNwYWNlKQpgYGAKCmBgYHtyfQppbnNwZWN0KEZlZGVyYWxpc3RDb3JwdXNbWzFdXSkKYGBgCgoKIyBEb2N1bWVudCB0ZXJtIG1hdHJpeAoKV2UgY2FuIHN0YXJ0IHRvIGRvIHN0YXRpc3RpY3Mgbm93LiAgVGhlIGRvY3VtZW50IHRlcm0gbWF0cml4IGNvbnRhaW5zIHRoZSBjb3VudHMgb2YgZXZlcnkgd29yZCB0aW1lIGluIGVhY2ggZG9jdW1lbnQuIEVhY2ggcm93IHJlcHJlc2VudHMgYSBGZWRlcmFsaXN0IFBhcGVyLCBhbmQgZWFjaCBjb2x1bW4gaXMgYSB3b3JkIHR5cGUuIEJlY2F1c2UgbW9zdCBvZiB0aGUgbWF0cml4IGVudHJpZXMgYXJlIHplcm9zLCBpdCBpcyBoZWxkIGluICJzcGFyc2UiIGZvcm1hdC4gT25seSA4JSBvZiB0aGUgZWxlbWVudHMgaW4gdGhlIGRvY3VtZW50IHRlcm0gbWF0cml4IGFyZSBub3QgemVyby4gIChOb3RpY2UgdGhhdCB5b3UgKmNhbm5vdCogcmVjb3ZlciB0aGUgc291cmNlIGNvcnB1cyBmcm9tIHRoZSBkb2N1bWVudCB0ZXJtIG1hdHJpeC4gVGhpcyBtYXRyaXggcmVwcmVzZW50cyBlYWNoIGRvY3VtZW50IGFzIGEgImJhZyBvZiB3b3JkcyIuKQoKYGBge3J9CmR0bSA8LSBEb2N1bWVudFRlcm1NYXRyaXgoRmVkZXJhbGlzdENvcnB1cykKZHRtCmBgYAoKSXQgaXMgbm93IHNpbXBsZSB0byB1c2UgbWF0cml4IGZ1bmN0aW9ucyBmcm9tIFIgdG8gZmluZCB0aGUgbnVtYmVyIG9mIHdvcmRzIGluIGVhY2ggZG9jdW1lbnQgYW5kIHRoZSBudW1iZXIgb2YgdGltZXMgZWFjaCB0eXBlIGFwcGVhcnMgKGFsYmVpdCBhdCB0aGUgY29zdCBvZiBjb252ZXJ0aW5nIHRoZSBzcGFyc2UgbWF0cml4IGludG8gYSBkZW5zZSBtYXRyaXgpLiAgCgpgYGB7cn0KbmkgPC0gcm93U3Vtcyhhcy5tYXRyaXgoZHRtKSkgICMgdG9rZW5zIGluIGVhY2ggZG9jdW1lbnQKbWogPC0gY29sU3Vtcyhhcy5tYXRyaXgoZHRtKSkgICMgY29sdW1ucyBhcmUgbmFtZWQgYnkgdGhlIHdvcmQgdHlwZXM7IGZyZXF1ZW5jeSBvZiBlYWNoCmBgYAoKTG90cyBtb3JlIHdvcmRzIGluIHRoZXNlIHBhcGVycyB0aGFuIGluIHdpbmUgcmV2aWV3LiAgKGBxcGxvdGAgaXMgdGhlIGBnZ3Bsb3RgIHZlcnNpb24gb2YgYHBsb3RgLikKCmBgYHtyfQpxcGxvdCgxOjg1LCBuaSwgeGxhYj0iRmVkZXJhbGlzdCBQYXBlciIsIHlsYWI9IldvcmQgQ291bnQiKQpgYGAKClRoZSBmcmVxdWVuY3kgY291bnRzIGluIGBtamAgYXJlIG5hbWVkIGFuZCBpbiBhbHBoYWJldGljYWwgb3JkZXIuICBXZSBjYW4gdXNlIHRoZXNlIG5hbWVzIHRvIHByb2R1Y2UgYSBuaWNlIGJhciBncmFwaCB3aXRoIGBnZ3Bsb3RgLiAgCgpgYGB7cn0KRnJlcSA8LSB0aWJibGUodHlwZSA9IG5hbWVzKG1qKSwgY291bnQgPSBtaikgICAgICMgZ2dwbG90IGFuZCBkcGx5ciB3YW50IGRhdGEgZnJhbWVzCgpGcmVxICU+JQogICAgdG9wX24oMjUsIGNvdW50KSAgICAgICAgICAgICAgICAgICAgICAgICAlPiUKICAgIG11dGF0ZSh0eXBlPXJlb3JkZXIodHlwZSxjb3VudCkpICAgICAgICAgJT4lICAgICAjIHJhdGhlciB0aGFuIGFscGhhYmV0aWNhbAogICAgZ2dwbG90KGFlcyh0eXBlLGNvdW50KSkgKyBnZW9tX2NvbCgpICsgY29vcmRfZmxpcCgpCmBgYAoKTGV0J3Mgc2VlIHdoYXQgaGFwcGVucyB3aXRob3V0IHRoZSBzdG9wIHdvcmRzLgoKYGBge3J9CkZyZXEgJT4lCiAgICBmaWx0ZXIgKCF0eXBlICVpbiUgc3RvcHdvcmRzKCdlbmdsaXNoJykpICU+JSAgICAgIyBzeW50YXggb2YgJWluJSByZXNlbWJsZXMgJT4lCiAgICB0b3BfbigyNSwgY291bnQpICAgICAgICAgICAgICAgICAgICAgICAgICU+JQogICAgbXV0YXRlKHR5cGU9cmVvcmRlcih0eXBlLGNvdW50KSkgICAgICAgICAlPiUgICAgICMgcmF0aGVyIHRoYW4gYWxwaGFiZXRpY2FsCiAgICBnZ3Bsb3QoYWVzKHR5cGUsY291bnQpKSArIGdlb21fY29sKCkgKyBjb29yZF9mbGlwKCkKYGBgCgpUaGlzIGlzIGEgZ29vZCBjaGFuY2UgdG8gY2hlY2sgb3V0IHdoZXRoZXIgdGhpcyB0ZXh0IG1hdGNoZXMgYSBaaXBmIGRpc3RyaWJ1dGlvbi4gIAoKUmVjYWxsIHRoYXQgYSBaaXBmIGRpc3RyaWJ1dGlvbiBpcyBjaGFyYWN0ZXJpemVkIGJ5IGEgcG93ZXIgbGF3OiAgdGhlIGZyZXF1ZW5jeSBvZiB0aGUgc2Vjb25kIG1vc3QgY29tbW9uIHdvcmQgaXMgaW52ZXJzZWx5IHByb3BvcnRpb25hbCB0byBpdHMgcmFuaywgJHBfayBccHJvcHRvIDEvayQuICBTYWlkIGRpZmZlcmVudGx5LCB0aGUgZnJlcXVlbmN5IG9mIHRoZSBzZWNvbmQgbW9zdCBjb21tb24gd29yZCBpcyBoYWxmIHRoYXQgb2YgdGhlIG1vc3QgY29tbW9uLCB0aGUgZnJlcXVlbmN5IG9mIHRoZSB0aGlyZCBpcyBvbmUtdGhpcmQgdGhlIG1vc3QgY29tbW9uLCBldGMuICBBIGxpdHRsZSBhbGdlYnJhIHNob3dzIHRoYXQgZm9yIHRoaXMgdG8gb2NjdXIsIHRoZW4gJFxsb2cgcF9rIFxhcHByb3ggYl8wIC0gXGxvZyBrJC4gIFRoYXQgaXMsIGEgcGxvdCBvZiB0aGUgbG9nIG9mIHRoZSBmcmVxdWVuY2llcyBzaG91bGQgYmUgbGluZWFyIGluIHRoZSBsb2cgb2YgdGhlIHJhbmsgJGskLCB3aXRoIHNsb3BlIC0xLiAgSW4gIHRoaXMgZXhhbXBsZSwgdGhlIHNsb3BlIGlzIGxhcmdlciB0aGFuIC0xLCBidXQgdGhlIHBsb3QgaXMgcXVpdGUgbGluZWFyLgoKYGBge3J9CnppcGZfcGxvdChtaikKYGBgCgpUaGUgbGVhc3Qgc3F1YXJlcyBzbG9wZSBmb3IgYWxsIG9mIHRoZSBkYXRhIHdvdWxkIGJlc3RlZXBlciwgYmVpbmcgZG9taW5hdGVkIGJ5IHRoZSBtYW55IGxlc3MgY29tbW9uIHdvcmRzLgoKIyBDb21wYXJpbmcgdm9jYWJ1bGFyaWVzCgpXZSBjYW4gY29tcGFyZSB2b2NhYnVsYXJ5IHVzZWQgYnkgSGFtaWx0b24gdG8gdGhhdCB1c2VkIGJ5IE1hZGlzb24gYnkgcGlja2luZyBvdXQgdGhlIHBhcGVycyBrbm93biB0byBoYXZlIGJlZW4gd3JpdHRlbiBieSBvbmUgb3IgdGhlIG90aGVyLiAgVGhlc2UgY291bnRzIGZvcm0gdGhlIGJhc2lzIG9mIG5haXZlIEJheWVzIG1vZGVsLiAgSXQgaXMgb2Ygc29tZSBjb25jZXJuIHRoYXQgd2UgaGF2ZSBzbyBtYW55IGZld2VyIHBhcGVycyB3cml0dGVuIGJ5IE1hZGlzb24uCgpgYGB7cn0KZHRtLm1hZGlzb24gPC0gZHRtW0ZlZGVyYWxpc3RQYXBlcnMkYXV0aG9yPT0nTUFESVNPTicsXQpkaW0oZHRtLm1hZGlzb24pCgpkdG0uaGFtaWx0b24gPC0gZHRtW0ZlZGVyYWxpc3RQYXBlcnMkYXV0aG9yPT0nSEFNSUxUT04nLF0KZGltKGR0bS5oYW1pbHRvbikKYGBgCgpgYGB7cn0KbWoubWFkaXNvbiAgPC0gY29sU3Vtcyhhcy5tYXRyaXgoZHRtLm1hZGlzb24pKSAgICAgICAjIHR5cGUgZnJlcXVlbmNpZXMgZm9yIGVhY2gKbWouaGFtaWx0b24gPC0gY29sU3Vtcyhhcy5tYXRyaXgoZHRtLmhhbWlsdG9uKSkKCkNvdW50cyA8LSBiaW5kX3Jvd3MoICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBkcGx5ciBzdHlsZSBzdGFja3MgdGhlc2UKICAgICAgICAgICAgZGF0YV9mcmFtZShhdXRob3I9Ik1hZGlzb24iLCAgd29yZD1uYW1lcyhtai5tYWRpc29uKSwgIGNvdW50PW1qLm1hZGlzb24pLAogICAgICAgICAgICBkYXRhX2ZyYW1lKGF1dGhvcj0iSGFtaWx0b24iLCB3b3JkPW5hbWVzKG1qLmhhbWlsdG9uKSwgY291bnQ9bWouaGFtaWx0b24pKQoKQ291bnRzCmBgYAoKQ291bnRzIG9mIHdvcmRzIGJ5IGF1dGhvciBhcmUgY29uZm91bmRlZCB3aXRoIHRoZSBmcmVxdWVuY3kgb2YgYXV0aG9yc2hpcDogSGFtaWx0b24gd3JvdGUgbW9yZSBvZiB0aGUgRmVkZXJhbGlzdCBQYXBlcnMuICAoV2UgaGF2ZSB0b28gZmV3IGZvciBKb2huIEpheS4gIFBsdXMgaGUgaXMgbm90IGNvbnNpZGVyZWQgaW4gdGhlIHJ1bm5pbmcgZm9yIHdyaXRpbmcgdGhlIHBhcGVycyBvZiB1bmtub3duIGF1dGhvcnNoaXAuKSAgVGhlIFRpZHkgVGV4dCBib29rIGhhcyBtYW55IGV4YW1wbGVzIG9mIHRoaXMgc3R5bGUgb2YgcGxvdHRpbmcgcHJvZHVjZWQgYnkgYGdncGxvdGAuIAoKYGBge3J9CkNvdW50cyAlPiUKICAgIGZpbHRlcigzMDAgPCBjb3VudCkgJT4lCiAgICBnZ3Bsb3QoYWVzKHdvcmQsY291bnQpKSArIGdlb21fY29sKCkgKyBjb29yZF9mbGlwKCkgKwogICAgZmFjZXRfd3JhcCh+YXV0aG9yKQpgYGAgICAgICAgICAgICAgICAgIAoKUHJvcG9ydGlvbnMgbWFrZSBtb3JlIHNlbnNlLgoKYGBge3J9CkNvdW50cyAlPiUKICAgIGdyb3VwX2J5KGF1dGhvcikgJT4lCiAgICBtdXRhdGUocHJvcG9ydGlvbiA9IGNvdW50IC8gc3VtKGNvdW50KSkgJT4lCiAgICBmaWx0ZXIoMC4wMDUgPCBwcm9wb3J0aW9uKSAgICAgICAgJT4lCiAgICBnZ3Bsb3QoYWVzKHdvcmQscHJvcG9ydGlvbikpICsgZ2VvbV9jb2woKSArIGNvb3JkX2ZsaXAoKSArCiAgICBmYWNldF93cmFwKH5hdXRob3IpCmBgYAoKQSBzY2F0dGVycGxvdCBvZmZlcnMgeWV0IGEgZGlmZmVyZW50IHdheSB0byB2aWV3IHRoZXNlIGRhdGEuICBSYXRoZXIgdGhhbiBsb29rIGF0IGJhciBjaGFydHMsIHBsb3QgdGhlIHByb3BvcnRpb25zIGZvciBIYW1pbHRvbiB2ZXJzdXMgdGhvc2UgZm9yIE1hZGlzb24uICBBIHNjYXR0ZXJwbG90IHJlcXVpcmVzICp0d28qIHZhcmlhYmxlcy4uLiBvbmUgZm9yIHRoZSBIYW1pbHRvbiBwcm9wb3J0aW9ucyBhbmQgYW5vdGhlciBmb3IgdGhlIE1hZGlzb24gcHJvcG9ydGlvbnMuICBPdXIgZGF0YSBzbyBmYXIgaGFzIGp1c3QgKm9uZSogY29sdW1uIHRvIGZhY2lsaXRhdGUgdXNpbmcgYGdncGxvdGAuIFRoZSBmdW5jdGlvbiBgc3ByZWFkYCBpbiBgZHBseXJgIHNwbGl0cyBhIGNvbHVtbiBpbnRvIHR3by4gIAoKYGBge3J9ClByb3BvcnRpb25zIDwtIENvdW50cyAlPiUKICAgIGdyb3VwX2J5KGF1dGhvcikgICAgICAgICAgICAgICAgICAgICAgICAlPiUKICAgIG11dGF0ZShwcm9wb3J0aW9uID0gY291bnQgLyBzdW0oY291bnQpKSAlPiUKICAgIHNlbGVjdCgtY291bnQpICAgICAgICAgICAgICAgICAgICAgICAgICAlPiUgICAjIG1lc3NlZCB1cCB3aXRob3V0IHRoaXMKICAgIHNwcmVhZChrZXk9YXV0aG9yLCB2YWx1ZT1wcm9wb3J0aW9uKQoKUHJvcG9ydGlvbnMKYGBgCgpBIHF1aWNrIGNoZWNrIHRoYXQgdGhlc2UgYXJlIGluZGVlZCBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb25zLgoKYGBge3J9CmNvbFN1bXMoUHJvcG9ydGlvbnNbLDI6M10sIG5hLnJtPVRSVUUpCmBgYAoKCmBgYHtyfQpQcm9wb3J0aW9ucyAlPiUgCiAgICBmaWx0ZXIoMC4wMDEgPCBIYW1pbHRvbiAmIDAuMDAxIDwgTWFkaXNvbikgJT4lCiAgICBnZ3Bsb3QoYWVzKHg9SGFtaWx0b24sIHk9TWFkaXNvbikpICArCiAgICBnZW9tX2FibGluZShjb2xvciA9ICJncmF5NDAiLCBsdHkgPSAyKSArCiAgICBnZW9tX3BvaW50KGNvbG9yPSdsaWdodGdyYXknKSArCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gd29yZCksIGNoZWNrX292ZXJsYXA9VFJVRSwgdmp1c3Q9MS41KSArIAogICAgc2NhbGVfeV9sb2cxMCgpICsgc2NhbGVfeF9sb2cxMCgpCmBgYAoKCiMgQnVpbGRpbmcgdGhlIG5haXZlIEJheWVzIGNsYXNzaWZpZXIKCldlIGNhbiB1c2UgdGhlc2UgZGlzdHJpYnV0aW9ucyBvZiB3b3JkIHR5cGVzIHRvIGNsYXNzaWZ5IHRoZSBkb2N1bWVudHMgb2YgZGlzcHV0ZWQgYXV0aG9yc2hpcC4KCk5haXZlIEJheWVzIGFsbG93cyB1cyB0byBjb252ZXJ0IHRoZXNlIGRpc3RyaWJ1dGlvbnMgb3ZlciB0aGUgd29yZCB0eXBlcyBpbnRvIGEgY2xhc3NpZmllci4gIFRoZSBpZGVhIGlzIGludHVpdGl2ZSBhbmQgd29ya3MgbGlrZSB0aGlzLiAgSWdub3JpbmcgaXNzdWVzIG9mIHNhbXBsaW5nIHZhcmlhdGlvbiAoYXMgaWYgd2UgaGFkIGNvbXB1dGVkIHRoZSB3b3JkIGZyZXF1ZW5jaWVzIGZyb20gYSAqdmVyeSogbGFyZ2UgY29ycHVzKSwgZm9yIGV2ZXJ5IHdvcmQgdHlwZSAkV19qJCB3ZSAia25vdyIgdGhlIHByb2JhYmlsaXR5ICRQX3thdXRob3J9KFdfaikkIGZvciAkYXV0aG9yIFxpbiBce0hhbWlsdG9uLCBNYWRpc29uXH0kLiAgSGVyZSBjb21lcyB0d28gYmlnIGFzc3VtcHRpb25zOiB3ZSdyZSBnb2luZyB0byBpZ25vcmUgdGhlIG9yZGVyIG9mIHRoZSB3b3JkcyBhbmQgdGhlbiBwcmV0ZW5kIHRoYXQgdGhleSBvY2N1ciBpbmRlcGVuZGVudGx5ICpjb25kaXRpb25hbGx5KiBvbiBrbm93aW5nIHRoZSBhdXRob3IuCgpUaGVyZSdzIGEgcmF0aW9uYWxlIHRoYXQgc3VwcG9ydHMgdGhpcyBhcHByb2FjaCwgYW5kIHRoaXMgcmF0aW9uYWxlIHVzZXMgQmF5ZXMgVGhlb3JlbSAoaGVuY2UgdGhlIG5hbWUpLiAgR2l2ZW4gYSBkb2N1bWVudCAkRCA9IFx7d18xLCB3XzIsIFxsZG90cywgd19pLCBcbGRvdHMsIHdfblx9JCAtLSBhIHNlcXVlbmNlIG9mIHdvcmQgdG9rZW5zIC0tIHdlIHdhbnQgdG8gYXNzaWduIHRoZSBkb2N1bWVudCB0byBhICpjbGFzcyosIG5hbWVseSBpZGVudGlmeSB0aGUgYXV0aG9yIGFzIGVpdGhlciBIYW1pbHRvbiBvciBNYWRpc29uLiAgVGhlIG9wdGltYWwgc29sdXRpb24gaXMgdG8gYXNzaWduIGJhc2VkIG9uIHRoZSBtYXhpbWFsIHByb2JhYmlsaXR5LCAkUChhdXRob3J8RCkkLiAgQnV0IGhvdyBjYW4gd2UgZmluZCB0aGF0IGNvbmRpdGlvbmFsIHByb2JhYmlsaXR5PyAgQmF5ZXMgUnVsZTogICRQKGF1dGhvcnxEKSA9IFAoRHxhdXRob3IpUChhdXRob3IpL1AoRCkkLiAgVGhlIG5vcm1hbGl6aW5nIGZhY3RvciAkUChEKSQgaXMgY29uc3RhbnQgKGRvZXMgbm90IGRlcGVuZCBvbiB0aGUgYXV0aG9yKSwgc28gd2UgbmVlZCB0byBmaW5kIHRoZSBhdXRob3IgdGhhdCBtYXhpbWl6ZXMgJFAoRHxhdXRob3IpUChhdXRob3IpJC4gIFRoZSBwcmlvciBwcm9iYWJpbGl0eSBpcyBzb21ldGhpbmcgd2UgY2FuIGRlZmVyIHRvIGhpc3RvcmlhbnMgKG9yIGp1c3Qgc2V0IHRvIDEvMiksIGJ1dCB0aGUgb3RoZXIgcHJvYmFiaWxpdHkgaXMgaGFyZGVyLgoKV2hhdCBzaG91bGQgd2UgdXNlIGZvciAkUChEfGF1dGhvcikgPSBQKFx7d18xLCB3XzIsIFxsZG90cywgd19pLCBcbGRvdHMsIHdfblx9fGF1dGhvcikkPyAgVGhhdCdzIGVhc3kgKmlmKiB3ZSdyZSB3aWxsaW5nIHRvIGFzc3VtZSB0aGUgd29yZCBjaG9pY2VzIGFyZSBpbmRlcGVuZGVudCAqZ2l2ZW4qIHRoZSBhdXRob3I6CiQkIFAoXHt3XzEsIHdfMiwgXGxkb3RzLCB3X25cfXxhdXRob3IpID0gXHByb2Rfe2k9MX1ebiBQKHdfaXxhdXRob3IpJCQKClRoaXMgZXhwcmVzc2lvbiBleHBsYWlucyB3aHkgdGhpcyBpcyBjYWxsZWQgIm5haXZlIiBCYXllcyEgRG8geW91IHJlYWxseSB0aGluayB0aGUgY2hvaWNlIG9mIHRoZSBuZXh0IHdvcmQgaXMgaW5kZXBlbmRlbnQgZ2l2ZW4geW91IGtub3cgdGhlIGF1dGhvci4gIFRoYXQgc2FpZCwgdGhpcyBhc3N1bXB0aW9uIG1ha2VzIGl0IGVhc3kgdG8gY29tcHV0ZSBiZWNhdXNlIHdlIGhhdmUgYm90aCB0aGUgcHJvcG9ydGlvbnMgYW5kIHRoZSBjb3VudHMgZm9yIHRoZSB2YXJpb3VzIGRvY3VtZW50cy4gIAoKVGhlIG9ubHkgY2F0Y2ggaXMgd2hhdCB0byBkbyBpZiwgc2F5LCBQYXBlciAjNDkgaGFzIGEgd29yZCB0aGF0LCBzYXksIEhhbWlsdG9uIG5ldmVyIHVzZWQgaW4gdGhlIHBhcGVycyBoZSBpcyBrbm93biB0byBoYXZlIHdyaXR0ZW4uICBTaG91bGQgdGhpcyBtYWtlIHRoZSBwcm9iYWJpbGl0eSBvZiBIYW1pbHRvbiBiZWluZyB0aGUgYXV0aG9yIHplcm8/ICBUaGVyZSBhcmUgYW4gZWxhYm9yYXRlIGNvbGxlY3Rpb24gb2Ygd2F5cyB0byBoYW5kbGUgc3VjaCAqb3V0LW9mLXZvY2FidWxhcnkqIHdvcmRzLiAgR29vZC1UdXJpbmcgc21vb3RoaW5nIHJlcGxhY2VzIHRoZSB6ZXJvIChhbmQgc2hpZnRzIG90aGVyIHNtYWxsIHByb2JhYmlsaXRpZXMgYXMgd2VsbCkuIChZZXMsIHRoaXMgaXMgdGhlIHNhbWUgQWxhbiBUdXJpbmcgYXMgaW4gdGhlIHJlY2VudCBtb3ZpZS4pIFRoZSBmdW5jdGlvbiBgZ29vZF90dXJpbmdfcHJvYmFiYWJpbGl0aWVzYCAoZnJvbSB0aGUgJFx0dCB0ZXh0X2ZpbGVzLlIkIGNvbGxlY3Rpb24pIGRvZXMgdGhlIG5lZWRlZCBhZGp1c3RtZW50LgoKYGBge3J9CnByb2IubWFkaXNvbiAgPC0gZ29vZF90dXJpbmdfcHJvYmFiaWxpdGllcyhtai5tYWRpc29uICkKcHJvYi5oYW1pbHRvbiA8LSBnb29kX3R1cmluZ19wcm9iYWJpbGl0aWVzKG1qLmhhbWlsdG9uKQpgYGAKClRoZSBsb2ctcHJvYmFiaWxpdHkgaXMgbm93IGVhc3kgdG8gY29tcHV0ZS4gIChCZSBjYXJlZnVsLi4uIHdlIHdhbnQgbGFyZ2VyIHZhbHVlcywgYnV0IGxvZyBwcm9iYWJpbGl0aWVzIGFyZSBuZWdhdGl2ZSkgIExldCdzIHN0YXJ0IHdpdGggcGFwZXJzIG9mIGtub3duIGF1dGhvcnNoaXAuICBQYXBlciAjMSBpcyBieSBIYW1pbHRvbiwgYW5kIHRoZSBuYWl2ZSBCYXllcyBhZ3JlZXMuCgpgYGB7cn0KQyA8LSBhcy5tYXRyaXgoZHRtKQoKcGFwZXIgPC0gMQpzdW0obG9nKHByb2IuaGFtaWx0b24pKkNbcGFwZXIsXSkKc3VtKGxvZyhwcm9iLm1hZGlzb24pICpDW3BhcGVyLF0pCmBgYAoKTWFkaXNvbiB3cm90ZSBQYXBlciAjMTAsIGFuZCBhZ2FpbiBuYWl2ZSBCYXllcyBhZ3JlZXMuCgpgYGB7cn0KcGFwZXIgPC0gMTAKc3VtKGxvZyhwcm9iLmhhbWlsdG9uKSpDW3BhcGVyLF0pCnN1bShsb2cocHJvYi5tYWRpc29uKSAqQ1twYXBlcixdKQpgYGAKCkZvciBwYXBlciA0OSAob2YgZGViYXRlZCBhdXRob3JzaGlwKSBuYWl2ZSBCYXllcyBnaXZlcyB0aGUgYXV0aG9yc2hpcCBub2QgdG8gTWFkaXNvbiwgYWxiZWl0IGJ5IGEgbXVjaCBjbG9zZXIgbWFyZ2luIHRoYW4gdGhlIG90aGVycyBvZiBrbm93biBhdXRob3JzaGlwICh3aGljaCB3ZXJlIHVzZWQgdG8gYnVpbGQgdGhlIHByb2JhYmlsaXRpZXMgdXNlZCBieSBuYWl2ZSBCYXllcykuCgpgYGB7cn0KcGFwZXIgPC0gNDkKc3VtKGxvZyhwcm9iLmhhbWlsdG9uKSpDW3BhcGVyLF0pCnN1bShsb2cocHJvYi5tYWRpc29uKSAqQ1twYXBlcixdKQpgYGAKCldlIGNhbiBtYWtlIGEgbmljZSBwbG90IHRoYXQgc3VtbWFyaXplcyB0aGVzZSByZXN1bHRzIGZvciBhbGwgb2YgdGhlIHBhcGVycy4gIE1hdHJpeCBtdWx0aXBsaWNhdGlvbiBhdm9pZHMgbG9vcGluZyBvdmVyIHRoZSBwYXBlcnMuCgpgYGB7cn0KZGltKGR0bSkKbGVuZ3RoKHByb2IuaGFtaWx0b24pCmBgYAoKYGBge3J9CgpscC5oYW1pbHRvbiA8LSBDICUqJSBsb2cocHJvYi5oYW1pbHRvbikKbHAubWFkaXNvbiAgPC0gQyAlKiUgbG9nKHByb2IubWFkaXNvbikKCmRpZmYgPC0gbHAuaGFtaWx0b24gLSBscC5tYWRpc29uCmRpZmZbYygxLDEwLDQ5KV0KYGBgCgpOYWl2ZSBCYXllcyBhc3NpZ25zIG1vc3QgLS0gYnV0IG5vdCBhbGwgLS0gb2YgdGhlIGRpc3B1dGVkIHBhcGVycyB0byBNYWRpc29uLiAgVGhlIFdpa2kgd291bGQgZGlmZmVyISAgV2hhdCB3b3VsZCBMU0EgZG8/CgpgYGB7cn0KdGliYmxlKHBhcGVyPTE6ODUsIGF1dGhvcj1GZWRlcmFsaXN0UGFwZXJzJGF1dGhvciwgZGlmZj1hcy52ZWN0b3IoZGlmZikpICU+JQogICAgZ2dwbG90KGFlcyhwYXBlcixkaWZmLGNvbG9yPWF1dGhvcikpICsKICAgIGdlb21fcG9pbnQoKSArIGxhYnMoeT0iTG9nIExpa2VsaWhvb2QgUmF0aW8iKQogICAgCmBgYAoK