Язык программирования R можно использовать не только для сложных вычислений, но и для облегчения рутинных операций при ознакомлении с научной литературой. Основной трудностью в этом плане, на мой взгляд, является уж очень большое количество публикаций практически по любой тематике, которых становится всё больше и больше. В итоге, актуальной задачей является облегчение труда исследователя. Хотя бы при подборе статей для прочтения.

1. Поиск литературы

Для поиска литературы я обычно использую следующие источники:

  1. Web of Science.
  2. Science Direct.
  3. Google Академия.

для удобства поиска в Google Академии я использую расширение Google Scholar:



Я предпочитаю работать со статьями в формате pdf, поэтому использую сервис Kopernio, который ставиться как расширение в браузер и упрощает поиск статей именно в формате pdf в перечисленных выше источниках:



Найденные статьи в формате pdf сохраняем на диске для последующей работы.

2. Поиск статей, содержащих ключевую фразу

Пространство — иллюзия, дисковое пространство — тем более.

Сохраняя файлы на диске, я стараюсь их классифицировать сразу. Но любая научная статья может попадать сразу в несколько категорий. Можно, конечно, делать несколько копий файлов — по одному в каждую категорию, но рано или поздно количество сохранённых файлов превысит разумные для запоминания пределы. Поэтому я пользуюсь следующим алгоритмом:

  1. Создать список всех статей.
  2. Найти те из них, что содержат нужную фразу.
  3. Открыть найденные файлы для просмотра.

2.1. Создание списка статей

Все файлы с расширением .pdf можно найти поиском, но чтобы этого не делать вручную, создадим командный файл pdf2list.cmd:

chcp 1251
forfiles /P "D:\Бібліотека" /M "*.pdf" /S /C "cmd /c if @isdir==FALSE echo @path" > pdf_list.txt
chcp 866

После запуска командного файла мы получаем файл pdf_list.txt, содержащий списой всех файлов с расширением .pdf в папке D:\Бібліотека.

Обратите внимание, что каждая строка заключена в кавычки:



2.2. Поиск статей, содержащих ключевую фразу

Для поиска статей, содержащих определённую ключевую фразу, можно воспользоваться следующим скриптом R:

# Задаём строку для поиска
findText = 'natural language processing'

# Список полных путей к pdf, для обновления списка запускать pdf2list.cmd
inputFileList = './pdf_list.txt'

# Подключаем библиотеку для чтения текста из pdf
library(pdftools)

# Подключаем библиотеку для concatenate
library(ngram)

# Загружаем список файлов с полными путями
files = scan(inputFileList, what = array(" "), sep="\n") 

# Убираем " из текста
files = gsub("\"", "", files)

# Загружаем текст из файлов
texts <- lapply(files, pdf_text)

# Один документ - одна строка
text_df <- data_frame(doc = 1:length(texts), text = unlist(lapply(texts, concatenate)))

# Подключаем библиотеку для %like%
library(data.table)

# Узнаём номера документов, содержащих findText
pdfIndexes = text_df$doc[text_df$text %like% findText]

# Создаём список файлов, содержащих findText
fileList = files[pdfIndexes]

# Сохраняем 
write(fileList, 'fileList.txt') 

В результате получаем файл fileList.txt, содержащий пути к статьям, содержащим ключевую фразу.

Можно или искать каждый файл вручную, или открыть их все сразу.

2.3. Открыть все найденные файлы для просмотра

Открыть все файлы из fileList.txt можно с помощью следюущего командного файла:

chcp 1251
FOR /F "tokens=*" %%i in (fileList.txt) do "C:\Program Files (x86)\Foxit Software\Foxit Reader\FoxitReader.exe" "%%i"
chcp 866

P.S. Если Вы пользуетесь другой программой для просмотра pdf, замените путь к программе.

3. Поиск статей с наибольшим упоминанием ключевых слов

Проблема поиска статей по ключевой фразе в том, что не учитывается сколько раз ключевые слова упоминаются в статье. Если найдено большое количество статей по ключевой фразе, то хотелось бы ранжировать их по частоте упоминания ключевых слов.

Для этого можно воспользоваться следующим скриптом R, записав ключевые слова в файл key_words.txt (каждое слово с новой строки):

# Список полных путей к pdf
inputFileList = './fileList.txt' 

# Ключевые слова
inputKeyWords = './key_words.txt'

# Подключаем библиотеку для чтения текста из pdf
library(pdftools)

# Подключаем библиотеку для работы с токенами
library(tidyverse)
library(tidytext)

# Подключаем библиотеку для concatenate
library(ngram)

# Загружаем список файлов с полными путями
files     = scan(inputFileList, what = array(" "), sep="\n")

# Загружаем список ключевых слов
key_words = scan(inputKeyWords, what = array(" "), sep="\n", encoding = "UTF-8")

# Загружаем текст из файлов
texts <- lapply(files, pdf_text)

# Загружавем список стоп-слов библиотеки tidytext
data(stop_words)

# Один документ - одна строка
text_df <- data_frame(doc = 1:length(texts), text = unlist(lapply(texts, concatenate)))

# Поиск всех слов
pdf_words <- text_df %>%
  unnest_tokens(word, text)

# Убираем стоп-слова
pdf_words <- pdf_words %>%
  anti_join(stop_words)

# Подсчитываем количество слов по документам
pdf_word_counts_by_doc <- pdf_words %>%
  count(doc, word, sort = TRUE) %>%
  ungroup()

# Сохранение результатов
write.csv2(pdf_word_counts_by_doc[pdf_word_counts_by_doc$word == key_words, ], 'pdf_word_counts_by_doc.csv', fileEncoding = "UTF-8")

По итогам работы скрипта мы получаем файл pdf_word_counts_by_doc.csv, содержащий номер документа (номер строки файла fileList.txt), ключевые слова и частоту их упоминания в каждом документе. Открыв файл либо в Excel, либо в LibreOffice Calc, получим примерно такую картину:



Для удобства можно воспользоваться инструментом Сводная таблица:



Таким образом легче найти релевантные статьи. Остаётся только прочитать. Но что делать, если вы не знаете язык, на котором написана статья?

4. Перевод иностранных статей с помощью переводчика Google

К примеру, Вы нашли файл pdf со статьёй на латышском/корейском/китайском языке. И после прочтения аннотации на английском языке хотите хоть в общих чертах понять о чём статья. Один из быстрых способов — использовать Переводчик Google.

Вот только при копировании текста из pdf файла приходиться вручную убирать переносы строк:

This is a sample
text that has copied
from PDF file.

При копировании большого куска текста это несколько утомляет. А если строки длинные, то эти переносы не особо то и видно:


Для исправления переносов можно использовать любой текстовый редактор с нумерацией строк, например Notepad++:


Скрипт R, который убирает переносы строк и копирует результат в буфер обмена, а потом открывает в браузере окно с переводчиком.

# Открываем файл с переносами (если без sep = '\n', то каждое слово отдельно)
txt = scan('./text_from_PDF.txt', what = character(), sep = '\n', encoding = "UTF-8")

# Соединяем все строки в одну
library(ngram)
newText = concatenate(txt)
  
# Сохраняем в файл одну строку без переносов
writeLines(newText, "./one-line_text.txt", useBytes = T)

# Открываем переводчик гугл, а текст передаём в буфер обмена
url = 'https://translate.google.com.ua/#en/uk/'
writeClipboard(newText)
browseURL(url)

Копируем текст из pdf файла в text_from_PDF.txt и запускаем скрипт. Для удобства я рекомендую использовать IDE RStudio.

Остаётся только вставить текст из буфера обмена компинацией клавиш Ctrl + V.

В случае, если текст из буфера вставляется в неправильной кодировке, то открываем файл one-line_text.txt.

Если процедура разовая, то вручную всё исправить проще.

Выводы

Если Вам кажется, что всё описанное здесь излишне, то, возможно, в Вашем случае так и есть. Но на больших объёмах данный скрипт позволяет сэкономить время и нервы.

LS0tDQp0aXRsZTogyPHv7uv85/Pl7CBSIOTr/yDu4evl4/fl7ej/IODt4Ovo5+Ag7eDz9+3u6SDr6PLl8ODy8/D7DQphdXRob3I6IFZhc3lsIFRzeWtobXlzdHJvDQpkYXRlOiAxOS4wNy4yMDE4DQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiBUUlVFDQotLS0NCg0K3+f76iDv8O7j8ODs7Ojw7uLg7ej/IFtSXShodHRwOi8vd3d3LnItcHJvamVjdC5vcmcpIOzu5u3uIOjx7+7r/Ofu4uDy/CDt5SDy7uv86u4g5Ov/IPHr7ubt+/Ug4vv36PHr5e3o6Swg7e4g6CDk6/8g7uHr5eP35e3o/yDw8/Lo7e379SDu7+Xw4Pbo6SDv8Ogg7uft4Oru7Ovl7ejoIPEg7eDz9+3u6SDr6PLl8ODy8/Du6S4gzvHt7uLt7ukg8vDz5O3u8fL8/iDiIP3y7uwg7+vg7eUsIO3gIOzu6SDi5+Pr/+QsIP/i6//l8vH/IPPmIO735e38IOHu6/z47uUg6u7r6Pfl8fLi7iDv8+Hr6Org9ujpIO/w4Ory6Pfl8eroIO/uIOv+4e7pIPLl7ODy6OrlLCDq7vLu8Pv1IPHy4O3u4ujy8f8g4vG4IOHu6/z45SDoIOHu6/z45S4gwiDo8u7j5Swg4Ory8+Dr/O3u6SDn4OTg9+XpIP/i6//l8vH/IO7h6+Xj9+Xt6OUg8vDz5OAg6PHx6+Xk7uLg8uXr/y4g1e7y/yDh+yDv8Ogg7+7k4e7w5SDx8uDy5ekg5Ov/IO/w7vfy5e3o/y4gIA0KDQojIyAxLiDP7ujx6iDr6PLl8ODy8/D7DQoNCsTr/yDv7ujx6uAg6+jy5fDg8vPw+yD/IO7h+/ft7iDo8e/u6/zn8/4g8evl5PP++ejlIOjx8u737ejq6DogIA0KDQoxLiBbV2ViIG9mIFNjaWVuY2VdKGh0dHA6Ly93ZWJvZmtub3dsZWRnZS5jb20pLg0KMi4gW1NjaWVuY2UgRGlyZWN0XShodHRwczovL3d3dy5zY2llbmNlZGlyZWN0LmNvbSkuDQozLiBbR29vZ2xlIMDq4OTl7Oj/XShodHRwczovL3NjaG9sYXIuZ29vZ2xlLmNvbS8/b2k9Z3NiJmhsPXVrKS4NCg0K5Ov/IPPk7uHx8uLgIO/u6PHq4CDiIFtHb29nbGUgwOrg5OXs6OhdKGh0dHBzOi8vc2Nob2xhci5nb29nbGUuY29tLz9vaT1nc2ImaGw9dWspIP8g6PHv7uv85/P+IPDg8fjo8OXt6OUgKipHb29nbGUgU2Nob2xhcioqOg0KDQo8L2JyPjxkaXYgYWxpZ24gPSAiY2VudGVyIj4hW10ocGljXzAzLnBuZyk8L2Rpdj48L2JyPg0KDQrfIO/w5eTv7vfo8uD+IPDg4e7y4PL8IPHuIPHy4PL8/+zoIOIg9O7w7ODy5SAqKnBkZioqLCDv7v3y7uzzIOjx7+7r/Ofz/iDx5fDi6PEgW0tvcGVybmlvXShodHRwczovL2tvcGVybmlvLmNvbSksIOru8u7w++kg8fLg4ujy/PH/IOrg6iDw4PH46PDl7ejlIOIg4fDg8+fl8CDoIPPv8O754OXyIO/u6PHqIPHy4PLl6SDo7OXt7e4g4iD07vDs4PLlICoqcGRmKiog4iDv5fDl9+jx6+Xt7fv1IOL7+OUg6PHy7vft6Org9ToNCg0KPC9icj48ZGl2IGFsaWduID0gImNlbnRlciI+IVtdKHBpY18wNC5wbmcpPC9kaXY+PC9icj4NCg0KzeDp5OXt7fvlIPHy4PL86CDiIPTu8Ozg8uUgKipwZGYqKiDx7vXw4O3/5ewg7eAg5Ojx6uUg5Ov/IO/u8evl5PP++eXpIPDg4e7y+y4gIA0KDQojIyAyLiDP7ujx6iDx8uDy5eksIPHu5OXw5uD56PUg6uv+9+Xi8/4g9PDg5/MNCg0KPs/w7vHy8ODt8fLi7iAmbWRhc2g7IOjr6/7n6P8sIOTo8eru4u7lIO/w7vHy8ODt8fLi7iAmbWRhc2g7IPLl7CDh7uvl5S4gDQoNCtHu9fDg7f//IPTg6ev7IO3gIOTo8erlLCD/IPHy4PDg/vH8IOj1IOrr4PHx6PTo9ujw7uLg8vwg8fDg5/MuIM3uIOv+4eD/IO3g8/ft4P8g8fLg8vz/IOzu5uXyIO/u7+Dk4PL8IPHw4OfzIOIg7eXx6u7r/OruIOrg8uXj7vDo6S4gzO7m7e4sIOru7eX37e4sIOTl6+Dy/CDt5fHq7uv86u4g6u7v6Okg9ODp6+7iICZtZGFzaDsg7+4g7uTt7uzzIOIg6uDm5PP+IOrg8uXj7vDo/iwg7e4g8ODt7iDo6+gg7+7n5O3uIOru6+j35fHy4u4g8e718ODtuO3t+/Ug9ODp6+7iIO/w5eL78ejyIPDg5/Ps7fvlIOTr/yDn4O/u7Ojt4O3o/yDv8OXk5ev7LiDP7v3y7uzzIP8g7+7r/Ofz/vH8IPHr5eTz/vno7CDg6+Pu8Ojy7O7sOg0KDQoxLiDR7ufk4PL8IPHv6PHu6iDi8eX1IPHy4PLl6S4NCjIuIM3g6fLoIPLlIOjnIO3o9Swg9/LuIPHu5OXw5uDyIO3z5u3z/iD08ODn8y4NCjMuIM7y6vD78vwg7eDp5OXt7fvlIPTg6ev7IOTr/yDv8O7x7O7y8OAuDQoNCiMjIyAyLjEuINHu5+Tg7ejlIPHv6PHq4CDx8uDy5ekNCg0KwvHlIPTg6ev7IPEg8ODx+Ojw5e3o5ewgKioucGRmKiog7O7m7e4g7eDp8ugg7+7o8eru7Cwg7e4g9/Lu4fsg/fLu4+4g7eUg5OXr4PL8IOLw8/ft8/4sIPHu5+Tg5OjsIOru7ODt5O376SD04OnrICoqcGRmMmxpc3QuY21kKio6DQoNCmBgYA0KY2hjcCAxMjUxDQpmb3JmaWxlcyAvUCAiRDpcwbPh67Pu8uXq4CIgL00gIioucGRmIiAvUyAvQyAiY21kIC9jIGlmIEBpc2Rpcj09RkFMU0UgZWNobyBAcGF0aCIgPiBwZGZfbGlzdC50eHQNCmNoY3AgODY2DQpgYGANCg0Kz+7x6+Ug5+Dv8/Hq4CDq7uzg7eTt7uPuIPTg6evgIOz7IO/u6/P34OXsIPTg6esgKipwZGZfbGlzdC50eHQqKiwg8e7k5fDm4Pno6SDx7+jx7ukg4vHl9SD04Onr7uIg8SDw4PH46PDl7ejl7CAqKi5wZGYqKiDiIO/g7+rlICoqRDpcXMGz4euz7vLl6uAqKi4gIA0KDQrO4fDg8ujy5SDi7ejs4O3o5Swg9/LuIOrg5uTg/yDx8vDu6uAg5+Dq6/735e3gIOIg6uDi+/fq6DoNCg0KPC9icj48ZGl2IGFsaWduID0gImNlbnRlciI+IVtdKHBpY18wNS5wbmcpPC9kaXY+PC9icj4NCg0KIyMjIDIuMi4gz+7o8eog8fLg8uXpLCDx7uTl8Obg+ej1IOrr/vfl4vP+IPTw4OfzDQoNCsTr/yDv7ujx6uAg8fLg8uXpLCDx7uTl8Obg+ej1IO7v8OXk5eu47e3z/iDq6/735eLz/iD08ODn8ywg7O7m7e4g4u7x7+7r/Ofu4uDy/PH/IPHr5eTz/vno7CDx6vDo7/Lu7CBSOg0KDQpgYGB7ciwgZXZhbD1GQUxTRSwgaW5jbHVkZT1UUlVFfQ0KIyDH4OTguOwg8fLw7urzIOTr/yDv7ujx6uANCmZpbmRUZXh0ID0gJ25hdHVyYWwgbGFuZ3VhZ2UgcHJvY2Vzc2luZycNCg0KIyDR7+jx7uog7+7r7fv1IO/z8uXpIOogcGRmLCDk6/8g7uHt7uLr5e3o/yDx7+jx6uAg5+Dv8/Hq4PL8IHBkZjJsaXN0LmNtZA0KaW5wdXRGaWxlTGlzdCA9ICcuL3BkZl9saXN0LnR4dCcNCg0KIyDP7uTq6/734OXsIOHo4evo7vLl6vMg5Ov/IPfy5e3o/yDy5erx8uAg6OcgcGRmDQpsaWJyYXJ5KHBkZnRvb2xzKQ0KDQojIM/u5Orr/vfg5ewg4ejh6+ju8uXq8yDk6/8gY29uY2F0ZW5hdGUNCmxpYnJhcnkobmdyYW0pDQoNCiMgx+Dj8PPm4OXsIPHv6PHu6iD04Onr7uIg8SDv7uvt++zoIO/z8v/s6A0KZmlsZXMgPSBzY2FuKGlucHV0RmlsZUxpc3QsIHdoYXQgPSBhcnJheSgiICIpLCBzZXA9IlxuIikgDQoNCiMg0+Ho8ODl7CAiIOjnIPLl6vHy4A0KZmlsZXMgPSBnc3ViKCJcIiIsICIiLCBmaWxlcykNCg0KIyDH4OPw8+bg5ewg8uXq8fIg6Ocg9ODp6+7iDQp0ZXh0cyA8LSBsYXBwbHkoZmlsZXMsIHBkZl90ZXh0KQ0KDQojIM7k6O0g5O7q8+zl7fIgLSDu5O3gIPHy8O7q4A0KdGV4dF9kZiA8LSBkYXRhX2ZyYW1lKGRvYyA9IDE6bGVuZ3RoKHRleHRzKSwgdGV4dCA9IHVubGlzdChsYXBwbHkodGV4dHMsIGNvbmNhdGVuYXRlKSkpDQoNCiMgz+7k6uv+9+Dl7CDh6OHr6O7y5erzIOTr/yAlbGlrZSUNCmxpYnJhcnkoZGF0YS50YWJsZSkNCg0KIyDT5+3guOwg7e7s5fDgIOTu6vPs5e3y7uIsIPHu5OXw5uD56PUgZmluZFRleHQNCnBkZkluZGV4ZXMgPSB0ZXh0X2RmJGRvY1t0ZXh0X2RmJHRleHQgJWxpa2UlIGZpbmRUZXh0XQ0KDQojINHu5+TguOwg8e/o8e7qIPTg6evu4iwg8e7k5fDm4Pno9SBmaW5kVGV4dA0KZmlsZUxpc3QgPSBmaWxlc1twZGZJbmRleGVzXQ0KDQojINHu9fDg7f/l7CANCndyaXRlKGZpbGVMaXN0LCAnZmlsZUxpc3QudHh0JykgDQpgYGANCg0KwiDw5efz6/zy4PLlIO/u6/P34OXsIPTg6esgKipmaWxlTGlzdC50eHQqKiwg8e7k5fDm4Pno6SDv8/LoIOog8fLg8vz/7Cwg8e7k5fDm4Pno7CDq6/735eLz/iD08ODn8y4gIA0KDQrM7ubt7iDo6+gg6PHq4PL8IOrg5uT76SD04OnrIOLw8/ft8/4sIOjr6CDu8urw+/L8IOj1IOLx5SDx8ODn8y4NCg0KIyMjIDIuMy4gzvLq8Pvy/CDi8eUg7eDp5OXt7fvlIPTg6ev7IOTr/yDv8O7x7O7y8OANCg0KzvLq8Pvy/CDi8eUg9ODp6/sg6OcgKipmaWxlTGlzdC50eHQqKiDs7ubt7iDxIO/u7O75/P4g8evl5P7z+eXj7iDq7uzg7eTt7uPuIPTg6evgOg0KDQpgYGANCmNoY3AgMTI1MQ0KRk9SIC9GICJ0b2tlbnM9KiIgJSVpIGluIChmaWxlTGlzdC50eHQpIGRvICJDOlxQcm9ncmFtIEZpbGVzICh4ODYpXEZveGl0IFNvZnR3YXJlXEZveGl0IFJlYWRlclxGb3hpdFJlYWRlci5leGUiICIlJWkiDQpjaGNwIDg2Ng0KYGBgDQoNCioqUC5TLioqIMXx6+ggwvsg7+7r/Ofz5fLl8fwg5PDz4+7pIO/w7uPw4Ozs7ukg5Ov/IO/w7vHs7vLw4CAqKnBkZioqLCDn4Ozl7ejy5SDv8/L8IOog7/Du4/Dg7OzlLg0KDQojIyAzLiDP7ujx6iDx8uDy5ekg8SDt4Ojh7uv8+OjsIPPv7uzo7eDt6OXsIOrr/vfl4vv1IPHr7uINCg0Kz/Du4evl7OAg7+7o8ergIPHy4PLl6SDv7iDq6/735eLu6SD08ODn5SDiIPLu7Cwg9/LuIO3lIPP36PL74uDl8vH/IPHq7uv86u4g8ODnIOrr/vfl4vvlIPHr7uLgIPPv7uzo7eD+8vH/IOIg8fLg8vzlLiDF8evoIO3g6eTl7e4g4e7r/Pju5SDq7uvo9+Xx8uLuIPHy4PLl6SDv7iDq6/735eLu6SD08ODn5Swg8u4g9e7y5evu8fwg4fsg8ODt5ujw7uLg8vwg6PUg7+4g9+Dx8u7y5SDz7+7s6O3g7ej/IOrr/vfl4vv1IPHr7uIuDQoNCsTr/yD98u7j7iDs7ubt7iDi7vHv7uv85+7i4PL88f8g8evl5PP++ejsIPHq8Ojv8u7sIFIsIOfg7+jx4OIg6uv+9+Xi++Ug8evu4uAg4iD04OnrICoqa2V5X3dvcmRzLnR4dCoqICjq4Obk7uUg8evu4u4g8SDt7uLu6SDx8vDu6ugpOg0KDQpgYGB7ciwgZXZhbD1GQUxTRSwgaW5jbHVkZT1UUlVFfQ0KIyDR7+jx7uog7+7r7fv1IO/z8uXpIOogcGRmDQppbnB1dEZpbGVMaXN0ID0gJy4vZmlsZUxpc3QudHh0JyANCg0KIyDK6/735eL75SDx6+7i4A0KaW5wdXRLZXlXb3JkcyA9ICcuL2tleV93b3Jkcy50eHQnDQoNCiMgz+7k6uv+9+Dl7CDh6OHr6O7y5erzIOTr/yD38uXt6P8g8uXq8fLgIOjnIHBkZg0KbGlicmFyeShwZGZ0b29scykNCg0KIyDP7uTq6/734OXsIOHo4evo7vLl6vMg5Ov/IPDg4e7y+yDxIPLu6uXt4OzoDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkodGlkeXRleHQpDQoNCiMgz+7k6uv+9+Dl7CDh6OHr6O7y5erzIOTr/yBjb25jYXRlbmF0ZQ0KbGlicmFyeShuZ3JhbSkNCg0KIyDH4OPw8+bg5ewg8e/o8e7qIPTg6evu4iDxIO/u6+377Ogg7/Py/+zoDQpmaWxlcyAgICAgPSBzY2FuKGlucHV0RmlsZUxpc3QsIHdoYXQgPSBhcnJheSgiICIpLCBzZXA9IlxuIikNCg0KIyDH4OPw8+bg5ewg8e/o8e7qIOrr/vfl4vv1IPHr7uINCmtleV93b3JkcyA9IHNjYW4oaW5wdXRLZXlXb3Jkcywgd2hhdCA9IGFycmF5KCIgIiksIHNlcD0iXG4iLCBlbmNvZGluZyA9ICJVVEYtOCIpDQoNCiMgx+Dj8PPm4OXsIPLl6vHyIOjnIPTg6evu4g0KdGV4dHMgPC0gbGFwcGx5KGZpbGVzLCBwZGZfdGV4dCkNCg0KIyDH4OPw8+bg4uXsIPHv6PHu6iDx8u7vLfHr7uIg4ejh6+ju8uXq6CB0aWR5dGV4dA0KZGF0YShzdG9wX3dvcmRzKQ0KDQojIM7k6O0g5O7q8+zl7fIgLSDu5O3gIPHy8O7q4A0KdGV4dF9kZiA8LSBkYXRhX2ZyYW1lKGRvYyA9IDE6bGVuZ3RoKHRleHRzKSwgdGV4dCA9IHVubGlzdChsYXBwbHkodGV4dHMsIGNvbmNhdGVuYXRlKSkpDQoNCiMgz+7o8eog4vHl9SDx6+7iDQpwZGZfd29yZHMgPC0gdGV4dF9kZiAlPiUNCiAgdW5uZXN0X3Rva2Vucyh3b3JkLCB0ZXh0KQ0KDQojINPh6PDg5ewg8fLu7y3x6+7i4A0KcGRmX3dvcmRzIDwtIHBkZl93b3JkcyAlPiUNCiAgYW50aV9qb2luKHN0b3Bfd29yZHMpDQoNCiMgz+7k8ffo8vvi4OXsIOru6+j35fHy4u4g8evu4iDv7iDk7urz7OXt8uDsDQpwZGZfd29yZF9jb3VudHNfYnlfZG9jIDwtIHBkZl93b3JkcyAlPiUNCiAgY291bnQoZG9jLCB3b3JkLCBzb3J0ID0gVFJVRSkgJT4lDQogIHVuZ3JvdXAoKQ0KDQojINHu9fDg7eXt6OUg8OXn8+v88uDy7uINCndyaXRlLmNzdjIocGRmX3dvcmRfY291bnRzX2J5X2RvY1twZGZfd29yZF9jb3VudHNfYnlfZG9jJHdvcmQgPT0ga2V5X3dvcmRzLCBdLCAncGRmX3dvcmRfY291bnRzX2J5X2RvYy5jc3YnLCBmaWxlRW5jb2RpbmcgPSAiVVRGLTgiKQ0KDQpgYGANCg0Kz+4g6PLu4+DsIPDg4e7y+yDx6vDo7/LgIOz7IO/u6/P34OXsIPTg6esgKipwZGZfd29yZF9jb3VudHNfYnlfZG9jLmNzdioqLCDx7uTl8Obg+ejpIO3u7OXwIOTu6vPs5e3y4CAo7e7s5fAg8fLw7uroIPTg6evgICoqZmlsZUxpc3QudHh0KiopLCDq6/735eL75SDx6+7i4CDoIPfg8fLu8vMg6PUg8+/u7Ojt4O3o/yDiIOrg5uTu7CDk7urz7OXt8uUuIM7y6vD74iD04OnrIOvo4e4g4iAqKkV4Y2VsKiosIOvo4e4g4iAqKkxpYnJlT2ZmaWNlIENhbGMqKiwg7+7r8/fo7CDv8Ojs5fDt7iDy4Orz/iDq4PDy6O3zOg0KDQo8L2JyPjxkaXYgYWxpZ24gPSAiY2VudGVyIj4hW10ocGljXzA2LnBuZyk8L2Rpdj48L2JyPg0KDQrE6/8g8+Tu4fHy4uAg7O7m7e4g4u7x7+7r/Ofu4uDy/PH/IOjt8fLw8+zl7fLu7CAq0eLu5O3g/yDy4OHr6PbgKjoNCg0KPC9icj48ZGl2IGFsaWduID0gImNlbnRlciI+IVtdKHBpY18wNy5wbmcpPC9kaXY+PC9icj4NCg0K0uDq6Owg7uHw4Ofu7CDr5eP35SDt4Ony6CDw5evl4uDt8u375SDx8uDy/OguIM7x8uC48vH/IPLu6/zq7iDv8O736PLg8vwuIM3uIPfy7iDk5evg8vwsIOXx6+gg4vsg7eUg5+3g5fLlIP/n++osIO3gIOru8u7w7uwg7eDv6PHg7eAg8fLg8vz/Pw0KDQojIyA0LiDP5fDl4u7kIOjt7vHy8ODt7fv1IPHy4PLl6SDxIO/u7O75/P4g7+Xw5eLu5Pfo6uAgR29vZ2xlDQoNCsog7/Do7OXw8ywgwvsg7eD46+gg9ODp6yAqKnBkZioqIPHuIPHy4PL8uOkg7eAg6+Dy+/jx6u7sL+ru8OXp8eru7C/q6PLg6fHq7uwg/+f76uUuIMgg7+7x6+Ug7/Du9/Ll7ej/IODt7e7y4Pbo6CDt4CDg7ePr6Onx6u7sIP/n++rlIPXu8ujy5SD17vL8IOIg7uH56PUg9+Xw8uD1IO/u7f/y/CDuIPe47CDx8uDy/P8uIM7k6O0g6Ocg4fvx8vD79SDx7+7x7uHu4iAmbWRhc2g7IOjx7+7r/Ofu4uDy/CBbz+Xw5eLu5Pfo6iBHb29nbGVdKGh0dHBzOi8vdHJhbnNsYXRlLmdvb2dsZS5jb20udWEpLiAgDQogIA0KDQrC7vIg8u7r/OruIO/w6CDq7u/o8O7i4O3o6CDy5erx8uAg6OcgKipwZGYqKiD04Onr4CDv8Oj17uTo8vzx/yDi8PP37fP+IPPh6PDg8vwg7+Xw5e3u8fsg8fLw7uo6ICANCiAgDQoNCj5UaGlzIGlzIGEgc2FtcGxlICANCj50ZXh0IHRoYXQgaGFzIGNvcGllZCAgDQo+ZnJvbSBQREYgZmlsZS4gDQogIA0KICANCs/w6CDq7u/o8O7i4O3o6CDh7uv8+O7j7iDq8/Hq4CDy5erx8uAg/fLuIO3l8eru6/zq7iDz8u7s6//l8i4gwCDl8evoIPHy8O7q6CDk6+jt7fvlLCDy7iD98ugg7+Xw5e3u8fsg7eUg7vHu4e4g8u4g6CDi6OTt7jogIA0KPC9icj4gIA0KDQo8ZGl2IGFsaWduID0gImNlbnRlciI+IVtdKHBpY18wMS5wbmcpPC9kaXY+IA0KICANCjwvYnI+DQrE6/8g6PHv8ODi6+Xt6P8g7+Xw5e3u8e7iIOzu5u3uIOjx7+7r/Ofu4uDy/CDr/uHu6SDy5erx8u7i++kg8OXk4Ory7vAg8SDt8+zl8OD26OXpIPHy8O7qLCDt4O/w6Ozl8CBbTm90ZXBhZCsrXShodHRwczovL25vdGVwYWQtcGx1cy1wbHVzLm9yZy8pOiAgDQo8L2JyPiAgDQoNCjxkaXYgYWxpZ24gPSAiY2VudGVyIj4hW10ocGljXzAyLnBuZyk8L2Rpdj4NCg0KICANCjwvYnI+DQrR6vDo7/IgUiwg6u7y7vD76SDz4ejw4OXyIO/l8OXt7vH7IPHy8O7qIOgg6u7v6PDz5fIg8OXn8+v88uDyIOIg4fP05fAg7uHs5e3gLCDgIO/u8u7sIO7y6vD74uDl8iDiIOHw4PPn5fDlIO7q7e4g8SDv5fDl4u7k9+jq7uwuIA0KICANCg0KYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1UUlVFfQ0KDQoNCiMgzvLq8Pvi4OXsIPTg6esg8SDv5fDl7e7x4OzoICjl8evoIOHl5yBzZXAgPSAnXG4nLCDy7iDq4Obk7uUg8evu4u4g7vLk5ev87e4pDQp0eHQgPSBzY2FuKCcuL3RleHRfZnJvbV9QREYudHh0Jywgd2hhdCA9IGNoYXJhY3RlcigpLCBzZXAgPSAnXG4nLCBlbmNvZGluZyA9ICJVVEYtOCIpDQoNCiMg0e7l5Ojt/+XsIOLx5SDx8vDu6ugg4iDu5O3zDQpsaWJyYXJ5KG5ncmFtKQ0KbmV3VGV4dCA9IGNvbmNhdGVuYXRlKHR4dCkNCiAgDQojINHu9fDg7f/l7CDiIPTg6esg7uTt8yDx8vDu6vMg4eXnIO/l8OXt7vHu4g0Kd3JpdGVMaW5lcyhuZXdUZXh0LCAiLi9vbmUtbGluZV90ZXh0LnR4dCIsIHVzZUJ5dGVzID0gVCkNCg0KIyDO8urw++Lg5ewg7+Xw5eLu5Pfo6iDj8+PrLCDgIPLl6vHyIO/l8OXk4LjsIOIg4fP05fAg7uHs5e3gDQp1cmwgPSAnaHR0cHM6Ly90cmFuc2xhdGUuZ29vZ2xlLmNvbS51YS8jZW4vdWsvJw0Kd3JpdGVDbGlwYm9hcmQobmV3VGV4dCkNCmJyb3dzZVVSTCh1cmwpDQpgYGANCg0Kyu7v6PDz5ewg8uXq8fIg6OcgKipwZGYqKiD04Onr4CDiICoqdGV4dF9mcm9tX1BERi50eHQqKiDoIOfg7/Px6uDl7CDx6vDo7/IuIMTr/yDz5O7h8fLi4CD/IPDl6u7s5e3k8/4g6PHv7uv85+7i4PL8IElERSBbUlN0dWRpb10oaHR0cHM6Ly93d3cucnN0dWRpby5jb20pLiAgDQoNCs7x8uC48vH/IPLu6/zq7iDi8fLg4ujy/CDy5erx8iDo5yDh8/Tl8OAg7uHs5e3gIOru7O/o7eD26OXpIOrr4OLo+CAqQ3RybCArIFYqLiAgDQoNCsIg8evz9+DlLCDl8evoIPLl6vHyIOjnIOHz9OXw4CDi8fLg4uv/5fLx/yDiIO3l7/Dg4ujr/O3u6SDq7uTo8O7i6uUsIPLuIO7y6vD74uDl7CD04OnrICoqb25lLWxpbmVfdGV4dC50eHQqKi4gIA0KDQrF8evoIO/w7vbl5PPw4CDw4Ofu4uD/LCDy7iDi8PP37fP+IOLxuCDo8e/w4OLo8vwg7/Du+eUuIA0KDQojIyDC++Lu5PsNCg0KxfHr6CDC4Owg6uDm5fLx/ywg9/LuIOLxuCDu7+jx4O3t7uUg5+Tl8fwg6Ofr6Pjt5Swg8u4sIOLu5+zu5u3uLCDiIMLg+OXsIPHr8/fg5SDy4Oog6CDl8fL8Lg0Kze4g7eAg4e7r/Pjo9SDu4fq47OD1IOTg7e376SDx6vDo7/Ig7+7n4u7r/+XyIPH96u7t7uzo8vwg4vDl7P8g6CDt5fDi+y4NCg==