This R Markdown Notebook demonstrates how to use the paperfetcher Python package to perform handsearching in R through the reticulate interface to Python.

To execute a code chunk in RStudio, click the Run button within the chunk or place your cursor inside it and press Cmd+Shift+Enter.

Setup

We first need to run a couple of lines of code to install reticulate, Python, and paperfetcher.

# Install reticulate if not already installed
if(!require("reticulate")){
  install.packages("reticulate")
  library(reticulate)
}
Loading required package: reticulate
# Install Python if not already installed, and create a new virtualenv for paperfetcher
# (Uncomment the lines below to run code)
#install_python("3.7:latest")
#virtualenv_create("paperfetcher", version="3.7:latest")
#use_virtualenv("paperfetcher")

# Install paperfetcher
# (Uncomment the lines below to run code)
#py_install("paperfetcher", envname="paperfetcher")

# Import the paperfetcher package
paperfetcher <- import("paperfetcher")

Handsearching

Let’s perform a simple task: to search for all journal articles published in the journal Science between January 01, 2023 and January 31, 2023.

A quick Google search reveals that the ISSN for the online edition of Science is 1095-9203.

Now let’s use this information to create a search object:

search <- paperfetcher$handsearch$CrossrefSearch(ISSN="1095-9203", from_date="2023-01-01", until_date="2023-02-01")
search()
2023-02-12 21:44:36.343 INFO    paperfetcher.handsearch: Fetching 144 works.


Fetching 8 batches of 20 articles:   0%|          | 0/8 [00:00<?, ?it/s]

Fetching 8 batches of 20 articles:  12%|█▎        | 1/8 [00:06<00:48,  7.00s/it]

Fetching 8 batches of 20 articles:  25%|██▌       | 2/8 [00:32<01:48, 18.08s/it]

Fetching 8 batches of 20 articles:  38%|███▊      | 3/8 [00:42<01:10, 14.13s/it]

Fetching 8 batches of 20 articles:  50%|█████     | 4/8 [00:45<00:39,  9.99s/it]

Fetching 8 batches of 20 articles:  62%|██████▎   | 5/8 [00:48<00:22,  7.34s/it]

Fetching 8 batches of 20 articles:  75%|███████▌  | 6/8 [00:58<00:16,  8.12s/it]

Fetching 8 batches of 20 articles:  88%|████████▊ | 7/8 [01:11<00:09,  9.85s/it]

Fetching 8 batches of 20 articles: 100%|██████████| 8/8 [01:21<00:00,  9.90s/it]
Fetching 8 batches of 20 articles: 100%|██████████| 8/8 [01:21<00:00, 10.20s/it]

How many articles did our search return?

py_len(search)
[1] 144

This was rather slow… Can we speed this up?

Yes, we can!

Why was the search so slow?

Paperfetcher retrieved all the metadata available on Crossref for each paper. Each paper can have a lot of metadata (abstract, citations, keywords, funding information, etc.) deposited on Crossref, and retrieving all this informating can take a lot of time (and also, memory!).

How do we make it faster?

By retrieving only the metadata we need!

For example, let’s say we need DOI, URL, article title, author list, and publication date. As per the Crossref API, these fields are: DOI, URL, title, author, and issued. We can ask paperfetcher to only select these fields using the ‘select_fields’ parameter:

search <- paperfetcher$handsearch$CrossrefSearch(ISSN="1095-9203", from_date="2023-01-01", until_date="2023-02-01")

search(select=TRUE, select_fields=list('DOI', 'URL', 'title', 'author', 'issued', 'abstract'))
2023-02-12 21:46:19.861 INFO    paperfetcher.handsearch: Fetching 144 works.


Fetching 8 batches of 20 articles:   0%|          | 0/8 [00:00<?, ?it/s]

Fetching 8 batches of 20 articles:  12%|█▎        | 1/8 [00:07<00:52,  7.48s/it]

Fetching 8 batches of 20 articles:  25%|██▌       | 2/8 [00:12<00:35,  5.96s/it]

Fetching 8 batches of 20 articles:  38%|███▊      | 3/8 [00:20<00:35,  7.08s/it]

Fetching 8 batches of 20 articles:  50%|█████     | 4/8 [00:22<00:19,  4.81s/it]

Fetching 8 batches of 20 articles:  62%|██████▎   | 5/8 [00:31<00:19,  6.54s/it]

Fetching 8 batches of 20 articles:  75%|███████▌  | 6/8 [00:36<00:11,  5.89s/it]

Fetching 8 batches of 20 articles:  88%|████████▊ | 7/8 [00:49<00:08,  8.41s/it]

Fetching 8 batches of 20 articles: 100%|██████████| 8/8 [01:09<00:00, 11.87s/it]
Fetching 8 batches of 20 articles: 100%|██████████| 8/8 [01:09<00:00,  8.65s/it]

Filtering articles by keywords

We can also pass a list of keywords to paperfetcher to refine our search:

search <- paperfetcher$handsearch$CrossrefSearch(ISSN="1095-9203", from_date="2022-01-01", until_date="2023-01-01", keyword_list=list("COVID"))

search(select=TRUE, select_fields=list('DOI', 'URL', 'title', 'author', 'issued', 'abstract'))
2023-02-12 21:49:08.712 INFO    paperfetcher.handsearch: Fetching 33 works.


Fetching 2 batches of 20 articles:   0%|          | 0/2 [00:00<?, ?it/s]

Fetching 2 batches of 20 articles:  50%|█████     | 1/2 [00:36<00:36, 36.15s/it]

Fetching 2 batches of 20 articles: 100%|██████████| 2/2 [00:58<00:00, 27.90s/it]
Fetching 2 batches of 20 articles: 100%|██████████| 2/2 [00:58<00:00, 29.14s/it]

Extracting data from the search results

paperfetcher provides many different ways to access the search result data, using special data structures called Datasets.

For example, we can make a Dataset of DOIs from the search results:

doi_ds <- search$get_DOIDataset()

We can display this as a DataFrame:

doi_ds$to_df()

We can also make a Dataset containing all the fields we retrieved, and display this as a DataFrame:

parsers <- import("paperfetcher.parsers", convert=FALSE)

ds <- search$get_CitationsDataset(field_list=list('DOI', 'URL', 'title', 'author', 'issued'),
                                  field_parsers_list=list(NULL, NULL, parsers$crossref_title_parser,
                                                     parsers$crossref_authors_parser, 
                                                     parsers$crossref_date_parser))
ds$to_df()

Exporting data

We can save the search results to

  • a text file using the save_txt method:
ds$save_txt("out/handsearching_citations.txt")
  • a CSV file using the save_csv method:
ds$save_csv("out/handsearching_citations.csv")
  • an Excel file using the save_excel method:
ds$save_excel("out/handsearching_citations.xslx")

Exporting data to RIS format

Citation data stored in the RIS (Research Information Systems) file format can easily be imported into systematic review screening tools (such as Covidence) and citation management software (such as Zotero). Paperfetcher can export search results to RIS files. Let’s take a look:

Exporting to RIS format without abstracts

Paperfetcher uses Crossref’s content negotiation service to get RIS data for each DOI. Unfortunately, this does not contain abstracts. However, there is a workaround, which we’ll get to in a bit.

First, let’s see how to export data to RIS format without abstracts:

ds <- search$get_RISDataset()


Converting results to RIS format.:   0%|          | 0/33 [00:00<?, ?it/s]

Converting results to RIS format.:   3%|▎         | 1/33 [00:00<00:08,  3.64it/s]

Converting results to RIS format.:   6%|▌         | 2/33 [00:00<00:07,  3.93it/s]

Converting results to RIS format.:   9%|▉         | 3/33 [00:00<00:06,  4.29it/s]

Converting results to RIS format.:  12%|█▏        | 4/33 [00:00<00:06,  4.57it/s]

Converting results to RIS format.:  15%|█▌        | 5/33 [00:01<00:06,  4.31it/s]

Converting results to RIS format.:  18%|█▊        | 6/33 [00:01<00:05,  4.62it/s]

Converting results to RIS format.:  21%|██        | 7/33 [00:01<00:05,  4.66it/s]

Converting results to RIS format.:  24%|██▍       | 8/33 [00:01<00:05,  4.53it/s]

Converting results to RIS format.:  27%|██▋       | 9/33 [00:02<00:05,  4.11it/s]

Converting results to RIS format.:  30%|███       | 10/33 [00:02<00:05,  4.29it/s]

Converting results to RIS format.:  33%|███▎      | 11/33 [00:02<00:05,  4.23it/s]

Converting results to RIS format.:  36%|███▋      | 12/33 [00:02<00:04,  4.51it/s]

Converting results to RIS format.:  39%|███▉      | 13/33 [00:02<00:04,  4.69it/s]

Converting results to RIS format.:  42%|████▏     | 14/33 [00:03<00:04,  4.67it/s]

Converting results to RIS format.:  45%|████▌     | 15/33 [00:03<00:03,  4.70it/s]

Converting results to RIS format.:  48%|████▊     | 16/33 [00:03<00:03,  4.40it/s]

Converting results to RIS format.:  52%|█████▏    | 17/33 [00:03<00:03,  4.59it/s]

Converting results to RIS format.:  55%|█████▍    | 18/33 [00:04<00:03,  4.66it/s]

Converting results to RIS format.:  58%|█████▊    | 19/33 [00:04<00:03,  4.45it/s]

Converting results to RIS format.:  61%|██████    | 20/33 [00:04<00:02,  4.56it/s]

Converting results to RIS format.:  64%|██████▎   | 21/33 [00:04<00:02,  4.81it/s]

Converting results to RIS format.:  67%|██████▋   | 22/33 [00:04<00:02,  4.93it/s]

Converting results to RIS format.:  70%|██████▉   | 23/33 [00:05<00:02,  4.65it/s]

Converting results to RIS format.:  73%|███████▎  | 24/33 [00:05<00:01,  4.73it/s]

Converting results to RIS format.:  76%|███████▌  | 25/33 [00:05<00:01,  4.71it/s]

Converting results to RIS format.:  79%|███████▉  | 26/33 [00:05<00:01,  4.67it/s]

Converting results to RIS format.:  82%|████████▏ | 27/33 [00:05<00:01,  4.81it/s]

Converting results to RIS format.:  85%|████████▍ | 28/33 [00:06<00:01,  4.82it/s]

Converting results to RIS format.:  88%|████████▊ | 29/33 [00:06<00:00,  5.00it/s]

Converting results to RIS format.:  91%|█████████ | 30/33 [00:06<00:00,  4.86it/s]

Converting results to RIS format.:  94%|█████████▍| 31/33 [00:06<00:00,  4.95it/s]

Converting results to RIS format.:  97%|█████████▋| 32/33 [00:06<00:00,  5.03it/s]

Converting results to RIS format.: 100%|██████████| 33/33 [00:07<00:00,  4.84it/s]
Converting results to RIS format.: 100%|██████████| 33/33 [00:07<00:00,  4.62it/s]
ds$save_ris("out/handsearching.ris")

Exporting to RIS format with abstracts

Recall that we have already retrieved abstracts during our search. We can insert these abstracts as an extra field into the RIS dataset. Here’s how:

ds <- search$get_RISDataset(extra_field_list=list("abstract"),
                           extra_field_parser_list=list(NULL),
                           extra_field_rispy_tags=list("notes_abstract"))


Converting results to RIS format.:   0%|          | 0/33 [00:00<?, ?it/s]

Converting results to RIS format.:   3%|▎         | 1/33 [00:00<00:07,  4.45it/s]

Converting results to RIS format.:   6%|▌         | 2/33 [00:00<00:06,  4.63it/s]

Converting results to RIS format.:   9%|▉         | 3/33 [00:00<00:06,  4.97it/s]

Converting results to RIS format.:  12%|█▏        | 4/33 [00:00<00:06,  4.45it/s]

Converting results to RIS format.:  15%|█▌        | 5/33 [00:01<00:06,  4.55it/s]

Converting results to RIS format.:  18%|█▊        | 6/33 [00:01<00:06,  4.48it/s]

Converting results to RIS format.:  21%|██        | 7/33 [00:01<00:05,  4.71it/s]

Converting results to RIS format.:  24%|██▍       | 8/33 [00:01<00:05,  4.91it/s]

Converting results to RIS format.:  27%|██▋       | 9/33 [00:01<00:04,  4.93it/s]

Converting results to RIS format.:  30%|███       | 10/33 [00:02<00:04,  4.94it/s]

Converting results to RIS format.:  33%|███▎      | 11/33 [00:02<00:04,  4.63it/s]

Converting results to RIS format.:  36%|███▋      | 12/33 [00:02<00:04,  4.83it/s]

Converting results to RIS format.:  39%|███▉      | 13/33 [00:02<00:04,  4.91it/s]

Converting results to RIS format.:  42%|████▏     | 14/33 [00:02<00:03,  4.92it/s]

Converting results to RIS format.:  45%|████▌     | 15/33 [00:03<00:03,  4.97it/s]

Converting results to RIS format.:  48%|████▊     | 16/33 [00:03<00:03,  5.11it/s]

Converting results to RIS format.:  52%|█████▏    | 17/33 [00:03<00:03,  4.95it/s]

Converting results to RIS format.:  55%|█████▍    | 18/33 [00:03<00:03,  4.67it/s]

Converting results to RIS format.:  58%|█████▊    | 19/33 [00:04<00:03,  4.33it/s]

Converting results to RIS format.:  61%|██████    | 20/33 [00:04<00:03,  4.29it/s]

Converting results to RIS format.:  64%|██████▎   | 21/33 [00:04<00:02,  4.54it/s]

Converting results to RIS format.:  67%|██████▋   | 22/33 [00:04<00:02,  4.55it/s]

Converting results to RIS format.:  70%|██████▉   | 23/33 [00:04<00:02,  4.26it/s]

Converting results to RIS format.:  73%|███████▎  | 24/33 [00:05<00:02,  4.23it/s]

Converting results to RIS format.:  76%|███████▌  | 25/33 [00:05<00:01,  4.17it/s]

Converting results to RIS format.:  79%|███████▉  | 26/33 [00:05<00:01,  4.15it/s]

Converting results to RIS format.:  82%|████████▏ | 27/33 [00:05<00:01,  4.14it/s]

Converting results to RIS format.:  85%|████████▍ | 28/33 [00:06<00:01,  4.31it/s]

Converting results to RIS format.:  88%|████████▊ | 29/33 [00:06<00:00,  4.63it/s]

Converting results to RIS format.:  91%|█████████ | 30/33 [00:06<00:00,  4.37it/s]

Converting results to RIS format.:  94%|█████████▍| 31/33 [00:06<00:00,  4.47it/s]

Converting results to RIS format.:  97%|█████████▋| 32/33 [00:07<00:00,  4.43it/s]

Converting results to RIS format.: 100%|██████████| 33/33 [00:07<00:00,  3.99it/s]
Converting results to RIS format.: 100%|██████████| 33/33 [00:07<00:00,  4.50it/s]
ds$save_ris("out/handsearching_abstracts.ris")
LS0tCnRpdGxlOiAiSGFuZHNlYXJjaGluZyB3aXRoIHBhcGVyZmV0Y2hlciIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2sgZGVtb25zdHJhdGVzIGhvdyB0byB1c2UgdGhlIHBhcGVyZmV0Y2hlciBQeXRob24gcGFja2FnZSB0byBwZXJmb3JtIGhhbmRzZWFyY2hpbmcgaW4gUiB0aHJvdWdoIHRoZSBbcmV0aWN1bGF0ZV0oaHR0cHM6Ly9odHRwczovL3JzdHVkaW8uZ2l0aHViLmlvL3JldGljdWxhdGUvKSBpbnRlcmZhY2UgdG8gUHl0aG9uLgoKVG8gZXhlY3V0ZSBhIGNvZGUgY2h1bmsgaW4gUlN0dWRpbywgY2xpY2sgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIHBsYWNlIHlvdXIgY3Vyc29yIGluc2lkZSBpdCBhbmQgcHJlc3MgKkNtZCtTaGlmdCtFbnRlciouIAoKIyBTZXR1cAoKV2UgZmlyc3QgbmVlZCB0byBydW4gYSBjb3VwbGUgb2YgbGluZXMgb2YgY29kZSB0byBpbnN0YWxsIHJldGljdWxhdGUsIFB5dGhvbiwgYW5kIHBhcGVyZmV0Y2hlci4KCmBgYHtyfQojIEluc3RhbGwgcmV0aWN1bGF0ZSBpZiBub3QgYWxyZWFkeSBpbnN0YWxsZWQKaWYoIXJlcXVpcmUoInJldGljdWxhdGUiKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygicmV0aWN1bGF0ZSIpCiAgbGlicmFyeShyZXRpY3VsYXRlKQp9CgojIEluc3RhbGwgUHl0aG9uIGlmIG5vdCBhbHJlYWR5IGluc3RhbGxlZCwgYW5kIGNyZWF0ZSBhIG5ldyB2aXJ0dWFsZW52IGZvciBwYXBlcmZldGNoZXIKIyAoVW5jb21tZW50IHRoZSBsaW5lcyBiZWxvdyB0byBydW4gY29kZSkKI2luc3RhbGxfcHl0aG9uKCIzLjc6bGF0ZXN0IikKI3ZpcnR1YWxlbnZfY3JlYXRlKCJwYXBlcmZldGNoZXIiLCB2ZXJzaW9uPSIzLjc6bGF0ZXN0IikKI3VzZV92aXJ0dWFsZW52KCJwYXBlcmZldGNoZXIiKQoKIyBJbnN0YWxsIHBhcGVyZmV0Y2hlcgojIChVbmNvbW1lbnQgdGhlIGxpbmVzIGJlbG93IHRvIHJ1biBjb2RlKQojcHlfaW5zdGFsbCgicGFwZXJmZXRjaGVyIiwgZW52bmFtZT0icGFwZXJmZXRjaGVyIikKCiMgSW1wb3J0IHRoZSBwYXBlcmZldGNoZXIgcGFja2FnZQpwYXBlcmZldGNoZXIgPC0gaW1wb3J0KCJwYXBlcmZldGNoZXIiKQpgYGAKCiMgSGFuZHNlYXJjaGluZwoKTGV0J3MgcGVyZm9ybSBhIHNpbXBsZSB0YXNrOiB0byBzZWFyY2ggZm9yIGFsbCBqb3VybmFsIGFydGljbGVzIHB1Ymxpc2hlZCBpbiB0aGUgam91cm5hbCBTY2llbmNlIGJldHdlZW4gSmFudWFyeSAwMSwgMjAyMyBhbmQgSmFudWFyeSAzMSwgMjAyMy4KCkEgcXVpY2sgR29vZ2xlIHNlYXJjaCByZXZlYWxzIHRoYXQgdGhlIElTU04gZm9yIHRoZSBvbmxpbmUgZWRpdGlvbiBvZiBTY2llbmNlIGlzIDEwOTUtOTIwMy4KCk5vdyBsZXQncyB1c2UgdGhpcyBpbmZvcm1hdGlvbiB0byBjcmVhdGUgYSBzZWFyY2ggb2JqZWN0OgoKYGBge3J9CnNlYXJjaCA8LSBwYXBlcmZldGNoZXIkaGFuZHNlYXJjaCRDcm9zc3JlZlNlYXJjaChJU1NOPSIxMDk1LTkyMDMiLCBmcm9tX2RhdGU9IjIwMjMtMDEtMDEiLCB1bnRpbF9kYXRlPSIyMDIzLTAyLTAxIikKc2VhcmNoKCkKYGBgCgpIb3cgbWFueSBhcnRpY2xlcyBkaWQgb3VyIHNlYXJjaCByZXR1cm4/CgpgYGB7cn0KcHlfbGVuKHNlYXJjaCkKYGBgCgpUaGlzIHdhcyByYXRoZXIgc2xvdy4uLiBDYW4gd2Ugc3BlZWQgdGhpcyB1cD8KCipZZXMsIHdlIGNhbiEqCgoqKldoeSB3YXMgdGhlIHNlYXJjaCBzbyBzbG93PyoqCgpQYXBlcmZldGNoZXIgcmV0cmlldmVkIGFsbCB0aGUgbWV0YWRhdGEgYXZhaWxhYmxlIG9uIENyb3NzcmVmIGZvciBlYWNoIHBhcGVyLiBFYWNoIHBhcGVyIGNhbiBoYXZlIGEgbG90IG9mIG1ldGFkYXRhIChhYnN0cmFjdCwgY2l0YXRpb25zLCBrZXl3b3JkcywgZnVuZGluZyBpbmZvcm1hdGlvbiwgZXRjLikgZGVwb3NpdGVkIG9uIENyb3NzcmVmLCBhbmQgcmV0cmlldmluZyBhbGwgdGhpcyBpbmZvcm1hdGluZyBjYW4gdGFrZSBhIGxvdCBvZiB0aW1lIChhbmQgYWxzbywgbWVtb3J5ISkuCgoqKkhvdyBkbyB3ZSBtYWtlIGl0IGZhc3Rlcj8qKgoKQnkgcmV0cmlldmluZyBvbmx5IHRoZSBtZXRhZGF0YSB3ZSBuZWVkIQoKRm9yIGV4YW1wbGUsIGxldCdzIHNheSB3ZSBuZWVkIERPSSwgVVJMLCBhcnRpY2xlIHRpdGxlLCBhdXRob3IgbGlzdCwgYW5kIHB1YmxpY2F0aW9uIGRhdGUuIEFzIHBlciB0aGUgQ3Jvc3NyZWYgQVBJLCB0aGVzZSBmaWVsZHMgYXJlOgpgRE9JYCwgYFVSTGAsIGB0aXRsZWAsIGBhdXRob3JgLCBhbmQgYGlzc3VlZGAuIFdlIGNhbiBhc2sgcGFwZXJmZXRjaGVyIHRvIG9ubHkgc2VsZWN0IHRoZXNlIGZpZWxkcyB1c2luZyB0aGUgJ3NlbGVjdF9maWVsZHMnIHBhcmFtZXRlcjoKCmBgYHtyfQpzZWFyY2ggPC0gcGFwZXJmZXRjaGVyJGhhbmRzZWFyY2gkQ3Jvc3NyZWZTZWFyY2goSVNTTj0iMTA5NS05MjAzIiwgZnJvbV9kYXRlPSIyMDIzLTAxLTAxIiwgdW50aWxfZGF0ZT0iMjAyMy0wMi0wMSIpCgpzZWFyY2goc2VsZWN0PVRSVUUsIHNlbGVjdF9maWVsZHM9bGlzdCgnRE9JJywgJ1VSTCcsICd0aXRsZScsICdhdXRob3InLCAnaXNzdWVkJywgJ2Fic3RyYWN0JykpCmBgYAoKKipGaWx0ZXJpbmcgYXJ0aWNsZXMgYnkga2V5d29yZHMqKgoKV2UgY2FuIGFsc28gcGFzcyBhIGxpc3Qgb2Yga2V5d29yZHMgdG8gcGFwZXJmZXRjaGVyIHRvIHJlZmluZSBvdXIgc2VhcmNoOgoKYGBge3J9CnNlYXJjaCA8LSBwYXBlcmZldGNoZXIkaGFuZHNlYXJjaCRDcm9zc3JlZlNlYXJjaChJU1NOPSIxMDk1LTkyMDMiLCBmcm9tX2RhdGU9IjIwMjItMDEtMDEiLCB1bnRpbF9kYXRlPSIyMDIzLTAxLTAxIiwga2V5d29yZF9saXN0PWxpc3QoIkNPVklEIikpCgpzZWFyY2goc2VsZWN0PVRSVUUsIHNlbGVjdF9maWVsZHM9bGlzdCgnRE9JJywgJ1VSTCcsICd0aXRsZScsICdhdXRob3InLCAnaXNzdWVkJywgJ2Fic3RyYWN0JykpCmBgYAoKCiMjIEV4dHJhY3RpbmcgZGF0YSBmcm9tIHRoZSBzZWFyY2ggcmVzdWx0cwoKcGFwZXJmZXRjaGVyIHByb3ZpZGVzIG1hbnkgZGlmZmVyZW50IHdheXMgdG8gYWNjZXNzIHRoZSBzZWFyY2ggcmVzdWx0IGRhdGEsIHVzaW5nIHNwZWNpYWwgZGF0YSBzdHJ1Y3R1cmVzIGNhbGxlZCBEYXRhc2V0cy4KCkZvciBleGFtcGxlLCB3ZSBjYW4gbWFrZSBhIERhdGFzZXQgb2YgRE9JcyBmcm9tIHRoZSBzZWFyY2ggcmVzdWx0czoKCmBgYHtyfQpkb2lfZHMgPC0gc2VhcmNoJGdldF9ET0lEYXRhc2V0KCkKYGBgCgpXZSBjYW4gZGlzcGxheSB0aGlzIGFzIGEgRGF0YUZyYW1lOgoKYGBge3J9CmRvaV9kcyR0b19kZigpCmBgYAoKV2UgY2FuIGFsc28gbWFrZSBhIERhdGFzZXQgY29udGFpbmluZyBhbGwgdGhlIGZpZWxkcyB3ZSByZXRyaWV2ZWQsIGFuZCBkaXNwbGF5IHRoaXMgYXMgYSBEYXRhRnJhbWU6CgpgYGB7cn0KcGFyc2VycyA8LSBpbXBvcnQoInBhcGVyZmV0Y2hlci5wYXJzZXJzIiwgY29udmVydD1GQUxTRSkKCmRzIDwtIHNlYXJjaCRnZXRfQ2l0YXRpb25zRGF0YXNldChmaWVsZF9saXN0PWxpc3QoJ0RPSScsICdVUkwnLCAndGl0bGUnLCAnYXV0aG9yJywgJ2lzc3VlZCcpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmllbGRfcGFyc2Vyc19saXN0PWxpc3QoTlVMTCwgTlVMTCwgcGFyc2VycyRjcm9zc3JlZl90aXRsZV9wYXJzZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyc2VycyRjcm9zc3JlZl9hdXRob3JzX3BhcnNlciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyc2VycyRjcm9zc3JlZl9kYXRlX3BhcnNlcikpCmRzJHRvX2RmKCkKYGBgCgoKIyMgRXhwb3J0aW5nIGRhdGEKCldlIGNhbiBzYXZlIHRoZSBzZWFyY2ggcmVzdWx0cyB0byAKCi0gYSB0ZXh0IGZpbGUgdXNpbmcgdGhlIHNhdmVfdHh0IG1ldGhvZDoKCmBgYHtyfQpkcyRzYXZlX3R4dCgib3V0L2hhbmRzZWFyY2hpbmdfY2l0YXRpb25zLnR4dCIpCmBgYAoKLSBhIENTViBmaWxlIHVzaW5nIHRoZSBzYXZlX2NzdiBtZXRob2Q6CgpgYGB7cn0KZHMkc2F2ZV9jc3YoIm91dC9oYW5kc2VhcmNoaW5nX2NpdGF0aW9ucy5jc3YiKQpgYGAKCi0gYW4gRXhjZWwgZmlsZSB1c2luZyB0aGUgc2F2ZV9leGNlbCBtZXRob2Q6CgpgYGB7cn0KZHMkc2F2ZV9leGNlbCgib3V0L2hhbmRzZWFyY2hpbmdfY2l0YXRpb25zIikKYGBgCgojIyBFeHBvcnRpbmcgZGF0YSB0byBSSVMgZm9ybWF0CgpDaXRhdGlvbiBkYXRhIHN0b3JlZCBpbiB0aGUgUklTIChSZXNlYXJjaCBJbmZvcm1hdGlvbiBTeXN0ZW1zKSBmaWxlIGZvcm1hdCBjYW4gZWFzaWx5IGJlIGltcG9ydGVkIGludG8gc3lzdGVtYXRpYyByZXZpZXcgc2NyZWVuaW5nIHRvb2xzIChzdWNoIGFzIENvdmlkZW5jZSkgYW5kIGNpdGF0aW9uIG1hbmFnZW1lbnQgc29mdHdhcmUgKHN1Y2ggYXMgWm90ZXJvKS4gUGFwZXJmZXRjaGVyIGNhbiBleHBvcnQgc2VhcmNoIHJlc3VsdHMgdG8gUklTIGZpbGVzLiBMZXQncyB0YWtlIGEgbG9vazoKCioqRXhwb3J0aW5nIHRvIFJJUyBmb3JtYXQgd2l0aG91dCBhYnN0cmFjdHMqKgoKUGFwZXJmZXRjaGVyIHVzZXMgW0Nyb3NzcmVmJ3MgY29udGVudCBuZWdvdGlhdGlvbiBzZXJ2aWNlXShodHRwczovL3d3dy5jcm9zc3JlZi5vcmcvZG9jdW1lbnRhdGlvbi9yZXRyaWV2ZS1tZXRhZGF0YS9jb250ZW50LW5lZ290aWF0aW9uLykgdG8gZ2V0IFJJUyBkYXRhIGZvciBlYWNoIERPSS4gVW5mb3J0dW5hdGVseSwgdGhpcyBkb2VzIG5vdCBjb250YWluIGFic3RyYWN0cy4gSG93ZXZlciwgdGhlcmUgaXMgYSB3b3JrYXJvdW5kLCB3aGljaCB3ZSdsbCBnZXQgdG8gaW4gYSBiaXQuCgpGaXJzdCwgbGV0J3Mgc2VlIGhvdyB0byBleHBvcnQgZGF0YSB0byBSSVMgZm9ybWF0IHdpdGhvdXQgYWJzdHJhY3RzOgoKYGBge3J9CmRzIDwtIHNlYXJjaCRnZXRfUklTRGF0YXNldCgpCmRzJHNhdmVfcmlzKCJvdXQvaGFuZHNlYXJjaGluZy5yaXMiKQpgYGAKCioqRXhwb3J0aW5nIHRvIFJJUyBmb3JtYXQgd2l0aCBhYnN0cmFjdHMqKgoKUmVjYWxsIHRoYXQgd2UgaGF2ZSBhbHJlYWR5IHJldHJpZXZlZCBhYnN0cmFjdHMgZHVyaW5nIG91ciBzZWFyY2guIFdlIGNhbiBpbnNlcnQgdGhlc2UgYWJzdHJhY3RzIGFzIGFuIGV4dHJhIGZpZWxkIGludG8gdGhlIFJJUyBkYXRhc2V0LiBIZXJlJ3MgaG93OgoKYGBge3J9CmRzIDwtIHNlYXJjaCRnZXRfUklTRGF0YXNldChleHRyYV9maWVsZF9saXN0PWxpc3QoImFic3RyYWN0IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4dHJhX2ZpZWxkX3BhcnNlcl9saXN0PWxpc3QoTlVMTCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4dHJhX2ZpZWxkX3Jpc3B5X3RhZ3M9bGlzdCgibm90ZXNfYWJzdHJhY3QiKSkKCmRzJHNhdmVfcmlzKCJvdXQvaGFuZHNlYXJjaGluZ19hYnN0cmFjdHMucmlzIikKYGBgCgoKCg==