rdflib - package review

Reviewer: @annakrystalli

Review Submitted: 2018-01-30




This report contains documents the review of rOpenSci submitted package:

rdflib: ropensci/onboarding issue #169).


Package info

Description:

The Resource Description Framework, or ‘RDF’ is a widely used data representation model that forms the cornerstone of the Semantic Web. ‘RDF’ represents data as a graph rather than the familiar data table or rectangle of relational databases. The ‘rdflib’ package provides a friendly and concise user interface for performing common tasks on ‘RDF’ data, such as reading, writing and converting between the various serializations of ‘RDF’ data, including ‘rdfxml’, ‘turtle’, ‘nquads’, ‘ntriples’, ‘trig’, and ‘json-ld’; creating new ‘RDF’ graphs, and performing graph queries using ‘SPARQL’. This package wraps the low level ‘redland’ R package which provides direct bindings to the ‘redland’ C library. Additionally, the package supports the newer and more developer friendly ‘JSON-LD’ format through the ‘jsonld’ package. The package interface takes inspiration from the Python ‘rdflib’ library.

Author: Carl Boettiger cboettig@gmail.com [aut, cre, cph] (0000-0002-1642-628X)

repo url: https://github.com/cboettig/rdflib

website url: https://cboettig.github.io/rdflib/

Review info

See reviewer guidelines for further information on the rOpenSci review process.

key review checks:

  • Does the code comply with general principles in the Mozilla reviewing guide?
  • Does the package comply with the ROpenSci packaging guide?
  • Are there improvements that could be made to the code style?
  • Is there code duplication in the package that should be reduced?
  • Are there user interface improvements that could be made?
  • Are there performance improvements that could be made?
  • Is the documentation (installation instructions/vignettes/examples/demos) clear and sufficient?

Please be respectful and kind to the authors in your reviews. The rOpenSci code of conduct is mandatory for everyone involved in our review process.


session info

sessionInfo()
R version 3.4.3 (2017-11-30)
Platform: x86_64-apple-darwin15.6.0 (64-bit)
Running under: macOS High Sierra 10.13.2

Matrix products: default
BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/3.4/Resources/lib/libRlapack.dylib

locale:
[1] en_GB.UTF-8/en_GB.UTF-8/en_GB.UTF-8/C/en_GB.UTF-8/en_GB.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] jsonlite_1.5   SPARQL_1.16    RCurl_1.95-4.8 bitops_1.0-6   XML_3.98-1.9   rdflib_0.0.3  
[7] magrittr_1.5  

loaded via a namespace (and not attached):
 [1] Rcpp_0.12.15       xmlparsedata_1.0.1 compiler_3.4.3     pillar_1.1.0       base64enc_0.1-3   
 [6] remotes_1.1.0      tools_3.4.3        digest_0.6.14      praise_1.0.0       memoise_1.1.0     
[11] evaluate_0.10.1    tibble_1.4.2       pkgconfig_2.0.1    rlang_0.1.6        rex_1.1.2         
[16] whoami_1.1.2       rstudioapi_0.7     commonmark_1.4     curl_3.1           yaml_2.1.16       
[21] cyclocomp_1.1.0    roxygen2_6.0.1     stringr_1.2.0      pkgreviewr_0.1.0   withr_2.1.1.9000  
[26] jqr_1.0.0          httr_1.3.1         knitr_1.18         xml2_1.2.0         desc_1.1.1        
[31] devtools_1.13.4    hms_0.4.0          redland_1.0.17-9   jsonld_1.2         rprojroot_1.3-2   
[36] R6_2.2.2           rcmdcheck_1.2.1    rmarkdown_1.8      callr_1.0.0        readr_1.1.1       
[41] lintr_1.0.2        covr_3.0.1         backports_1.1.2    clisymbols_1.2.0   htmltools_0.3.6   
[46] rsconnect_0.8.5    assertthat_0.2.0   goodpractice_1.0.0 V8_1.5             stringi_1.1.6     
[51] lazyeval_0.2.1     crayon_1.3.4      

Test installation

test install of rdflib from GitHub with:

devtools::install_github("cboettig/rdflib", force = T, dependencies = T)
Downloading GitHub repo cboettig/rdflib@master
from URL https://api.github.com/repos/cboettig/rdflib/zipball/master
Installing rdflib
'/Library/Frameworks/R.framework/Resources/bin/R' --no-site-file --no-environ --no-save  \
  --no-restore --quiet CMD INSTALL  \
  '/private/var/folders/8p/87cqdx2s34vfvcgh04l6z72w0000gn/T/Rtmptt5hxc/devtools671d75f53311/cboettig-rdflib-2c150e2'  \
  --library='/Users/Anna/Library/R/3.4/library' --install-tests 

* installing *source* package ‘rdflib’ ...
** R
** inst
** tests
** preparing package for lazy loading
** help
*** installing help indices
** building package indices
** installing vignettes
** testing if installed package can be loaded
* DONE (rdflib)
remove.packages("rdflib")
Removing package from ‘/Users/Anna/Library/R/3.4/library’
(as ‘lib’ is unspecified)

comments:


test local rdflib install:

pkg_dir <- "/Users/Anna/Documents/workflows/rOpenSci/reviews/rdflib-review/../rdflib"
devtools::install(pkg_dir, dependencies = T, build_vignettes = T)
Installing rdflib
'/Library/Frameworks/R.framework/Resources/bin/R' --no-site-file --no-environ --no-save  \
  --no-restore --quiet CMD build '/Users/Anna/Documents/workflows/rOpenSci/reviews/rdflib'  \
  --no-resave-data --no-manual 
* checking for file ‘/Users/Anna/Documents/workflows/rOpenSci/reviews/rdflib/DESCRIPTION’ ... OK
* preparing ‘rdflib’:
* checking DESCRIPTION meta-information ... OK
* installing the package to build vignettes
* creating vignettes ... ERROR
Quitting from lines 21-38 (rdflib.Rmd) 
Error: processing vignette 'rdflib.Rmd' failed with diagnostics:
there is no package called 'jqr'
Execution halted
Error: Command failed (1)
pkg_dir <- "/Users/Anna/Documents/workflows/rOpenSci/reviews/rdflib-review/../rdflib"
devtools::install(pkg_dir, dependencies = T)
Installing rdflib
'/Library/Frameworks/R.framework/Resources/bin/R' --no-site-file --no-environ --no-save  \
  --no-restore --quiet CMD INSTALL '/Users/Anna/Documents/workflows/rOpenSci/reviews/rdflib'  \
  --library='/Users/Anna/Library/R/3.4/library' --install-tests 

* installing *source* package ‘rdflib’ ...
** R
** inst
** tests
** preparing package for lazy loading
** help
*** installing help indices
** building package indices
** installing vignettes
** testing if installed package can be loaded
* DONE (rdflib)
devtools::install(pkg_dir, dependencies = T, build_vignettes = T)
Installing rdflib
'/Library/Frameworks/R.framework/Resources/bin/R' --no-site-file --no-environ --no-save  \
  --no-restore --quiet CMD build '/Users/Anna/Documents/workflows/rOpenSci/reviews/rdflib'  \
  --no-resave-data --no-manual 
* checking for file ‘/Users/Anna/Documents/workflows/rOpenSci/reviews/rdflib/DESCRIPTION’ ... OK
* preparing ‘rdflib’:
* checking DESCRIPTION meta-information ... OK
* installing the package to build vignettes
* creating vignettes ... OK
* checking for LF line-endings in source and make files and shell scripts
* checking for empty or unneeded directories
* building ‘rdflib_0.0.3.tar.gz’

'/Library/Frameworks/R.framework/Resources/bin/R' --no-site-file --no-environ --no-save  \
  --no-restore --quiet CMD INSTALL  \
  '/private/var/folders/8p/87cqdx2s34vfvcgh04l6z72w0000gn/T/Rtmptt5hxc/rdflib_0.0.3.tar.gz'  \
  --library='/Users/Anna/Library/R/3.4/library' --install-tests 

* installing *source* package ‘rdflib’ ...
** R
** inst
** tests
** preparing package for lazy loading
** help
*** installing help indices
** building package indices
** installing vignettes
** testing if installed package can be loaded
* DONE (rdflib)

comments:

when I first ran devtools::install(pkg_dir, dependencies = T, build_vignettes = T), the building of the vignettes threw an error because suggests package ‘jqr’ had not been installed yet?

* checking for file ‘/Users/Anna/Documents/workflows/rOpenSci/reviews/rdflib/DESCRIPTION’ ... OK
* preparing ‘rdflib’:
* checking DESCRIPTION meta-information ... OK
* installing the package to build vignettes
* creating vignettes ... ERROR
Quitting from lines 21-38 (rdflib.Rmd) 
Error: processing vignette 'rdflib.Rmd' failed with diagnostics:
there is no package called 'jqr'
Execution halted

Check package integrity

run checks on rdflib source:

devtools::check(pkg_dir)
Updating rdflib documentation
Loading rdflib
Setting env vars -------------------------------------------------------------
CFLAGS  : -Wall -pedantic
CXXFLAGS: -Wall -pedantic
Building rdflib --------------------------------------------------------------
'/Library/Frameworks/R.framework/Resources/bin/R' --no-site-file  \
  --no-environ --no-save --no-restore --quiet CMD build  \
  '/Users/Anna/Documents/workflows/rOpenSci/reviews/rdflib' --no-resave-data  \
  --no-manual 
* checking for file ‘/Users/Anna/Documents/workflows/rOpenSci/reviews/rdflib/DESCRIPTION’ ... OK
* preparing ‘rdflib’:
* checking DESCRIPTION meta-information ... OK
* installing the package to build vignettes
* creating vignettes ... OK
* checking for LF line-endings in source and make files and shell scripts
* checking for empty or unneeded directories
* building ‘rdflib_0.0.3.tar.gz’

Setting env vars -------------------------------------------------------------
_R_CHECK_CRAN_INCOMING_ : FALSE
_R_CHECK_FORCE_SUGGESTS_: FALSE
Checking rdflib --------------------------------------------------------------
'/Library/Frameworks/R.framework/Resources/bin/R' --no-site-file  \
  --no-environ --no-save --no-restore --quiet CMD check  \
  '/var/folders/8p/87cqdx2s34vfvcgh04l6z72w0000gn/T//Rtmp5P7YC0/rdflib_0.0.3.tar.gz'  \
  --as-cran --timings --no-manual 
* using log directory ‘/private/var/folders/8p/87cqdx2s34vfvcgh04l6z72w0000gn/T/Rtmp5P7YC0/rdflib.Rcheck’
* using R version 3.4.3 (2017-11-30)
* using platform: x86_64-apple-darwin15.6.0 (64-bit)
* using session charset: UTF-8
* using options ‘--no-manual --as-cran’
* checking for file ‘rdflib/DESCRIPTION’ ... OK
* this is package ‘rdflib’ version ‘0.0.3’
* package encoding: UTF-8
* checking package namespace information ... OK
* checking package dependencies ... OK
* checking if this is a source package ... OK
* checking if there is a namespace ... OK
* checking for executable files ... OK
* checking for hidden files and directories ... OK
* checking for portable file names ... OK
* checking for sufficient/correct file permissions ... OK
* checking whether package ‘rdflib’ can be installed ... OK
* checking installed package size ... OK
* checking package directory ... OK
* checking ‘build’ directory ... OK
* checking DESCRIPTION meta-information ... OK
* checking top-level files ... OK
* checking for left-over files ... OK
* checking index information ... OK
* checking package subdirectories ... OK
* checking R files for non-ASCII characters ... OK
* checking R files for syntax errors ... OK
* checking whether the package can be loaded ... OK
* checking whether the package can be loaded with stated dependencies ... OK
* checking whether the package can be unloaded cleanly ... OK
* checking whether the namespace can be loaded with stated dependencies ... OK
* checking whether the namespace can be unloaded cleanly ... OK
* checking loading without being on the library search path ... OK
* checking dependencies in R code ... OK
* checking S3 generic/method consistency ... OK
* checking replacement functions ... OK
* checking foreign function calls ... OK
* checking R code for possible problems ... OK
* checking Rd files ... OK
* checking Rd metadata ... OK
* checking Rd line widths ... OK
* checking Rd cross-references ... OK
* checking for missing documentation entries ... OK
* checking for code/documentation mismatches ... OK
* checking Rd \usage sections ... OK
* checking Rd contents ... OK
* checking for unstated dependencies in examples ... OK
* checking installed files from ‘inst/doc’ ... OK
* checking files in ‘vignettes’ ... OK
* checking examples ... OK
* checking for unstated dependencies in ‘tests’ ... OK
* checking tests ...
  Running ‘testthat.R’
 OK
* checking for unstated dependencies in vignettes ... OK
* checking package vignettes in ‘inst/doc’ ... OK
* checking re-building of vignette outputs ... OK
* DONE
Status: OK

R CMD check results
0 errors | 0 warnings | 0 notes

comments:


run tests on rdflib source:

devtools::test(pkg_dir)
Loading rdflib
Loading required package: testthat

Attaching package: ‘testthat’

The following objects are masked from ‘package:magrittr’:

    equals, is_less_than, not

The following object is masked from ‘package:devtools’:

    setup

Testing rdflib
✔ | OK F W S | Context

⠏ |  0       | test-rdf.R
⠋ |  1       | test-rdf.R
⠙ |  2       | test-rdf.R
⠹ |  3       | test-rdf.R
⠸ |  4       | test-rdf.R
⠼ |  5       | test-rdf.R
⠴ |  6       | test-rdf.R
⠦ |  7       | test-rdf.R
⠧ |  8       | test-rdf.R
⠇ |  9       | test-rdf.R
⠏ | 10       | test-rdf.R
trying URL 'https://tinyurl.com/ycf95c9h'
Content type 'text/plain; charset=utf-8' length 390 bytes
==================================================
downloaded 390 bytes

⠋ | 11       | test-rdf.R
✔ | 11       | test-rdf.R [1.1 s]

══ Results ════════════════════════════════════════════════════════════════════
Duration: 1.2 s

OK:       11
Failed:   0
Warnings: 0
Skipped:  0

YEE-HAW - priceless code.

comments:


check rdflib for goodpractice:

goodpractice::gp(pkg_dir)
Preparing: covr
Preparing: cyclocomp
* installing *source* package ‘rdflib’ ...
** R
** inst
** preparing package for lazy loading
** help
*** installing help indices
** building package indices
** installing vignettes
** testing if installed package can be loaded
* DONE (rdflib)
Preparing: description
Preparing: lintr
Preparing: namespace
Preparing: rcmdcheck

♥ Ole! Dandy package! Keep up the first-rate work!

comments:


Check package metadata files

spell check

devtools::spell_check(pkg_dir)
  WORD              FOUND IN
Boettiger         rdflib-package.Rd:43
browseVignettes   rdflib-package.Rd:32
json              description:8
JSON              rdflib-package.Rd:20,23, description:13
jsonld            rdf_parse.Rd:15, rdf_serialize.Rd:17, description:13
ld                description:8
LD                rdflib-package.Rd:20,23, description:13
nquads            rdf_parse.Rd:14, rdf_serialize.Rd:16, description:8
ntriples          rdf_parse.Rd:14, rdf_serialize.Rd:16, description:8
occured           rdf_serialize.Rd:27
rdf               rdf_add.Rd:5,11,26,29,32, rdf_parse.Rd:11,13,20, rdf_query.Rd:10, rdf_serialize.Rd:11,15, rdf.Rd:5,10,13
RDF               rdf_parse.Rd:5,24, rdf_serialize.Rd:5,30, rdflib-package.Rd:9,11,18,19,20,23, description:1,3,6,7,9
rdfxml            rdf_parse.Rd:14, rdf_serialize.Rd:16, description:8
redland           rdf_parse.Rd:20, rdf_query.Rd:14, description:10,11
SPARQL            rdf_query.Rd:5,12,20, rdflib-package.Rd:19,23, description:9
uri               rdf_add.Rd:19,21
URI               rdf_add.Rd:23

comments:


Check documentation

online documentation: https://cboettig.github.io/rdflib/

  • Is the documentation (installation instructions/vignettes/examples/demos) clear and sufficient?

test rdflib function help files:

help(package = "rdflib")

comments:


test rdflib vignettes:

vignette(package = "rdflib")

comments:

Online documentation:

My main suggestion is to try to define some terms and improve the concept map for the tools by adding some detail and broader context to the documentation. The following suggestions could also be address with links to further details if you think they are too superfluous for explicit documentation with the package.

  • a brief intro to the semantic could be useful (eg something like):

The semantic web aims to link data in a machine readable way through the web, making data more alignable and interoperable, much easier to search, enriching and compute on.

  • what a graph format for data is (eg triples etc).

  • the structure of an rdf S3 object (ie you introduced some aspects of the data format here: (user does not have to manage world, model and storage objects by default just to perform standard operations and conversions) which we are told we can ignore (which is great) but actually creates more questions… what is this mysterious “world” object that forms an opaque slot of an rdf S3 object?) Would be nice to explain the structure of the S3 rdf briefly. Is there usefull metadata that can be extracted from the structure? (see comment later)

  • rdf file formats. I think its would especially aid in appreciating the rdf_serialise function to expand briefly (and potentially signpost to a resource like this) on the various serialization formats, perhaps even why one would use one over another, and particularly, why serialization involves writing a file out. I feel these are important concepts to help appreciate use cases of the function. Indeed the file out aspect of the function could do with being flagged more prominently in function man page where just by looking at the (somewhat jargony if you don’t know what serialization is) description and running the example, you’ve ended up writing a file without realising.

Similarly, parsing can then be seen/described as reading in an rdf from their specific formats.

Spelling a few things out in plain english could really help folks follow what’s going better and understand what file types are inputs or outputs of different functions.

how do I find info on URIs?

In general what is missing for me is some signposting/guidance on how I can find information on the semantics dictating what information I can extract from an rdf object. eg. with a df or list you could use str to get an idea of how you could start indexing these objects. If confronted with a local rdf file, how would one go about figuring out even what they can query? I appreciate this is really one of the difficulties of working with rdf and semantic data in general but I feel some brief guidance or demo on how one would approach this would go a long way.

examples in general

For clarity to the reader who may not have looked at function documentation yet, I recommend using the full argument names when supplying arguments to functions (if not always atleast the first time an argument is introduced) in vignettes.

SPARQL queries to JSON data section

At the end of the intro to the section, you write:

Here is a query that for all papers where I am an author, returns a table of given name, family name and year of publication:

Am I right in thinking though that you are co-author on all papers in the rdf but the query is in fact filtering the names of your co-authors? (through FILTER ( ?coi_family != "Boettiger" ))

Turning RDF-XML into more friendly JSON

It would be nice if possible to see sample of print outs of the covenrsion of the different files or at least of the effect of compaction.

rdf_add man page

Would be nice to see a demo of using one or more of the additonal arguments.

Motivating example

I think an additonal, more detailed motivating example might illustrate more direct use case in a researchers workflow. In particular it would be good to highlight the great potential of triplestore APIs (and celebrate the efforts of many cool eg governmental linked data initiatives). So an example that incorporates a query to a triplestore and then enrichment of a researchers data could be a cool example. This could be a longer term project opr even just an rOpenSci blogpost but see comment re: rdf_query function below.


Test functionality:

  • Are there user interface improvements that could be made?
  • Are there performance improvements that could be made?
library("rdflib")
library(SPARQL)
library(jsonlite)
exports <-ls("package:rdflib")
exports
[1] "rdf"           "rdf_add"       "rdf_parse"     "rdf_query"    
[5] "rdf_serialize"

rdf_serialize

doc <- system.file("extdata", "dc.rdf", package="redland")

doc %>%
  rdf_parse() %>%
  rdf_serialize(doc = "test.nquads", format = "nquads")

doc %>%
  rdf_parse() %>%
  rdf_serialize(doc = "test.rdfxml", format = "rdfxml")


doc %>%
  rdf_parse() %>%
  rdf_serialize(doc = "test.ntriples", format = "ntriples")
readr::read_file("test.nquads")
[1] "<http://www.dajobe.org/> <http://purl.org/dc/elements/1.1/description> \"The generic home page of Dave Beckett.\" .\n<http://www.dajobe.org/> <http://purl.org/dc/elements/1.1/title> \"Dave Beckett's Home Page\" .\n<http://www.dajobe.org/> <http://purl.org/dc/elements/1.1/creator> \"Dave Beckett\" .\n"
readr::read_file("test.ntriples")
[1] "<http://www.dajobe.org/> <http://purl.org/dc/elements/1.1/description> \"The generic home page of Dave Beckett.\" .\n<http://www.dajobe.org/> <http://purl.org/dc/elements/1.1/title> \"Dave Beckett's Home Page\" .\n<http://www.dajobe.org/> <http://purl.org/dc/elements/1.1/creator> \"Dave Beckett\" .\n"
readr::read_file("test.rdfxml")
[1] "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n  <rdf:Description rdf:about=\"http://www.dajobe.org/\">\n    <ns0:description xmlns:ns0=\"http://purl.org/dc/elements/1.1/\">The generic home page of Dave Beckett.</ns0:description>\n  </rdf:Description>\n  <rdf:Description rdf:about=\"http://www.dajobe.org/\">\n    <ns0:title xmlns:ns0=\"http://purl.org/dc/elements/1.1/\">Dave Beckett's Home Page</ns0:title>\n  </rdf:Description>\n  <rdf:Description rdf:about=\"http://www.dajobe.org/\">\n    <ns0:creator xmlns:ns0=\"http://purl.org/dc/elements/1.1/\">Dave Beckett</ns0:creator>\n  </rdf:Description>\n</rdf:RDF>\n"
serialisation errors: turtle & trig
library(magrittr)
library(rdflib)
doc <- system.file("extdata", "dc.rdf", package="redland")
doc %>%
  rdf_parse() %>%
  rdf_serialize(doc = "test.turtle", format = "turtle")
librdf error - serializer 'turtle' not found
rdf_serializer.c:597: (librdf_serializer_serialize_model_to_file) assertion failed: object pointer of type librdf_serializer is NULL.
library(magrittr)
library(rdflib)
doc <- system.file("extdata", "dc.rdf", package="redland")
doc %>%
  rdf_parse() %>%
  rdf_serialize(doc = "test.trig", format = "trig")
librdf error - serializer 'trig' not found
rdf_serializer.c:597: (librdf_serializer_serialize_model_to_file) assertion failed: object pointer of type librdf_serializer is NULL.

rdf_serialize

doc %>%
  rdf_parse(type = "jsold")
<http://www.dajobe.org/> <http://purl.org/dc/elements/1.1/description> "The generic home page of Dave Beckett." .
<http://www.dajobe.org/> <http://purl.org/dc/elements/1.1/title> "Dave Beckett's Home Page" .
<http://www.dajobe.org/> <http://purl.org/dc/elements/1.1/creator> "Dave Beckett" .
doc %>%
  rdf_parse(type = "jsonld") %>% class
[1] "rdf"

https://cboettig.github.io/rdflib/articles/rdflib.html

ex <- system.file("extdata/vita.json", package="rdflib")
vita <- rdf_parse(ex, "jsonld")
sparql <-
 'PREFIX schema: <http://schema.org/>
  SELECT ?coi_given ?coi_family ?year
  WHERE { 
    ?paper a schema:ScholarlyArticle . 
    ?paper schema:author ?authors .
    ?paper schema:dateCreated ?year . 
    ?authors schema:familyName ?coi_family .
    OPTIONAL { ?authors schema:givenName ?coi_given . }
    FILTER ( ?coi_family != "Boettiger" )
}
'
vita %>% rdf_query(sparql)
sparql <-
 'PREFIX schema: <http://schema.org/>
  SELECT ?coi_given ?coi_family ?year
  WHERE { 
    ?paper a schema:ScholarlyArticle . 
    ?paper schema:author ?authors .
    ?paper schema:dateCreated ?year . 
    ?authors schema:familyName ?coi_family .
    OPTIONAL { ?authors schema:givenName ?coi_given . }
    FILTER ( ?year == "2009-10-19"^^xs:date)
}
'
vita %>% rdf_query(sparql)
librdf error  - syntax error, unexpected EQ
rdf_query_results.c:100: (librdf_query_results_finished) assertion failed: object pointer of type librdf_query_results is NULL.
rdf_query_results.c:100: (librdf_query_results_finished) assertion failed: object pointer of type librdf_query_results is NULL.

I’m finding it difficult to query by date. I realise you’ve opted to do date filtering later but, it could be another quite informative additional example as temporal filtering is probably a widespread usecase.

sparql <-
 'PREFIX schema: <http://schema.org/>

  SELECT ?coi_given ?coi_family ?year

  WHERE { 
    ?paper a schema:ScholarlyArticle . 
    ?paper schema:author ?authors .
    ?authors schema:familyName ?coi_family .
    OPTIONAL { ?authors schema:givenName ?coi_given . }

    FILTER ( ?coi_family != "Boettiger" )
}
'

vita %>% rdf_query(sparql)

Nice warning message

vita %>% str
List of 2
 $ world:Formal class 'World' [package "redland"] with 1 slot
  .. ..@ librdf_world:Formal class '_p_librdf_world_s' [package "redland"] with 1 slot
  .. .. .. ..@ ref:Formal class 'externalptr' [package ""] with 0 slots
 list()
 $ model:Formal class 'Model' [package "redland"] with 1 slot
  .. ..@ librdf_model:Formal class '_p_librdf_model_s' [package "redland"] with 1 slot
  .. .. .. ..@ ref:Formal class 'externalptr' [package ""] with 0 slots
 list()
 - attr(*, "class")= chr "rdf"
vita %>% class
[1] "rdf"
vita$world@librdf_world
An object of class "_p_librdf_world_s"
Slot "ref":
<pointer: 0x101a93760>
vita$model@librdf_model
An object of class "_p_librdf_model_s"
Slot "ref":
<pointer: 0x10dc174f0>
doc <- system.file("extdata", "dc.rdf", package="redland")

sparql <-
'PREFIX dc: <http://purl.org/dc/elements/1.1/>
 SELECT ?author ?c
 WHERE { ?author dc:creator ?c . }'

rdf <- rdf_parse(doc)
rdf_query(rdf, sparql)
jq
  readr::read_file(ex) %>%
  jqr::jq(
     '."@reverse".author[]  | 
       { year: .dateCreated, 
         author: .author[] | [.givenName, .familyName]  | join(" ")
       }') %>%
  jqr::combine() %>%
  jsonlite::fromJSON()

Select the first 15 triplets.

library(SPARQL)
endpoint <- "http://linked.bodc.ac.uk/sparql/"
query <- 'select * where {?s ?p ?o . } limit 15'
out <- SPARQL(endpoint, query)
query <- 'select * where {?subject ?predicate ?object . } limit 15'
res <- SPARQL(endpoint, query)
res %>% class
[1] "list"
xml
$results

$namespaces
NULL
"http://www.w3.org/ns/dcat#Dataset"
[1] "http://www.w3.org/ns/dcat#Dataset"
query <- 'select ?dataset where {?dataset a <http://www.w3.org/ns/dcat#Dataset> . } limit 15'
query <- 'SELECT DISTINCT ?s
WHERE {
  ?s a <http://www.w3.org/ns/dcat#Dataset>
} limit 15'
SPARQL(endpoint, query)
$results

$namespaces
NULL
res$results %>% toJSON() %>% class() 
[1] "json"
SPARQL::commonns
 [1] "xsd"                                          
 [2] "<http://www.w3.org/2001/XMLSchema#>"          
 [3] "rdf"                                          
 [4] "<http://www.w3.org/1999/02/22-rdf-syntax-ns#>"
 [5] "rdfs"                                         
 [6] "<http://www.w3.org/2000/01/rdf-schema#>"      
 [7] "owl"                                          
 [8] "<http://www.w3.org/2002/07/owl#>"             
 [9] "skos"                                         
[10] "<http://www.w3.org/2004/02/skos/core#>"       
[11] "dc"                                           
[12] "<http://purl.org/dc/elements/1.1/>"           
[13] "foaf"                                         
[14] "<http://xmlns.com/foaf/0.1/>"                 
[15] "wgs84"                                        
[16] "<http://www.w3.org/2003/01/geo/wgs84_pos#>"   
[17] "qb"                                           
[18] "<http://purl.org/linked-data/cube#>"          
ex <- system.file("extdata/vita.json", package="rdflib")
vita <- rdf_parse(ex, "json")
sparql <-
 'PREFIX schema: <http://schema.org/>
  SELECT ?coi_given ?coi_family ?year
  WHERE { 
    ?paper a schema:ScholarlyArticle . 
    ?paper schema:author ?authors .
    ?paper schema:dateCreated ?year . 
    ?authors schema:familyName ?coi_family .
    OPTIONAL { ?authors schema:givenName ?coi_given . }
    FILTER ( ?coi_family != "Boettiger" )
}
'
vita %>% rdf_query(sparql)

comments:

I know this is more the redland package and the whole point is I’d kinda like to know what world is. Despite this reference in the vignette: > user does not have to manage world, model and storage objects by default just to perform standard operations and conversions)

I’m not really sure what they are. Would be nice to explain what the structure of the S3 rdf is. Is there usefull metadata that can be extracted from the structure?

  • in rdf_serialize documentation: I think a brief mention of what serialisation actually is would really help understand this function. I also feel it should be flagged more prominently that the function writes a file out.

Inspect code:

pkgreviewr::pkgreview_print_source("rdflib")
## rdf
function () 
{
    world <- new("World")
    storage <- new("Storage", world, "hashes", name = "", options = "hash-type='memory'")
    model <- new("Model", world = world, storage, options = "")
    structure(list(world = world, model = model), class = "rdf")
}
<environment: namespace:rdflib>
--- 
 
## rdf_add
function (x, subject, predicate, object, subjectType = as.character(NA), 
    objectType = as.character(NA), datatype_uri = as.character(NA)) 
{
    stmt <- new("Statement", world = x$world, subject, predicate, 
        object, subjectType, objectType, datatype_uri)
    addStatement(x$model, stmt)
    invisible(x)
}
<environment: namespace:rdflib>
--- 
 
## rdf_parse
function (doc, format = c("rdfxml", "nquads", "ntriples", "trig", 
    "turtle", "jsonld"), ...) 
{
    format <- match.arg(format)
    doc <- text_or_url_to_doc(doc)
    if (format == "jsonld") {
        tmp <- tempfile()
        tmp <- add_base_uri(doc, tmp)
        rdf <- jsonld::jsonld_to_rdf(tmp)
        writeLines(rdf, tmp)
        format <- "nquads"
        doc <- tmp
    }
    x <- rdf()
    mimetype <- unname(rdf_mimetypes[format])
    parser <- new("Parser", x$world, name = format, mimeType = mimetype)
    redland::parseFileIntoModel(parser, x$world, doc, x$model)
    x
}
<environment: namespace:rdflib>
--- 
 
## rdf_query
function (x, query, ...) 
{
    queryObj <- new("Query", x$world, query, ...)
    queryResult <- redland::executeQuery(queryObj, x$model)
    out <- list()
    result <- redland::getNextResult(queryResult)
    out <- c(out, result)
    while (!is.null(result)) {
        result <- redland::getNextResult(queryResult)
        out <- c(out, result)
    }
    redland::freeQueryResults(queryResult)
    redland::freeQuery(queryObj)
    rectangularize_query_results(out)
}
<environment: namespace:rdflib>
--- 
 
## rdf_serialize
function (x, doc, format = c("rdfxml", "nquads", "ntriples", 
    "trig", "turtle", "jsonld"), namespace = NULL, prefix = NULL, 
    ...) 
{
    format <- match.arg(format)
    jsonld_output <- format == "jsonld"
    if (jsonld_output) {
        format <- "nquads"
    }
    mimetype <- rdf_mimetypes[format]
    serializer <- new("Serializer", x$world, name = format, mimeType = mimetype)
    if (!is.null(namespace)) {
        redland::setNameSpace(serializer, x$world, namespace = namespace, 
            prefix = prefix)
    }
    status <- redland::serializeToFile(serializer, x$world, x$model, 
        doc)
    if (jsonld_output) {
        txt <- paste(readLines(doc), collapse = "\n")
        if (length(txt) > 0) {
            json <- jsonld::jsonld_from_rdf(txt)
            writeLines(json, doc)
        }
    }
    invisible(status)
}
<environment: namespace:rdflib>
--- 
 
$rdf
NULL

$rdf_add
NULL

$rdf_parse
NULL

$rdf_query
NULL

$rdf_serialize
NULL
redland::serializeToFile
nonstandardGenericFunction for "serializeToFile" defined from package "redland"

function (.Object, world, model, filePath, ...) 
{
    standardGeneric("serializeToFile")
}
<environment: 0x10677db20>
Methods may be defined for arguments: .Object, world, model, filePath
Use  showMethods("serializeToFile")  for currently available ones.
rdflib:::rdf_mimetypes
                 nquads                ntriples                  rdfxml                    trig 
        "text/x-nquads" "application/n-triples"   "application/rdf+xml"    "application/x-trig" 
                 turtle 
   "application/turtle" 
rdflib:::rectangularize_query_results
function (out) 
{
    vars <- unique(names(out))
    X <- lapply(vars, function(v) gsub("\"(([^\\^])+)\"\\^*.*", 
        "\\1", as.character(out[names(out) == v])))
    names(X) <- vars
    as.data.frame(X, stringsAsFactors = FALSE)
}
<environment: namespace:rdflib>

Is there a way to return a non regularised query result ie return an rdf instead?

I’m thinking about a usecase when maybe it’s better to enrich data by merging rdfs?

Ie, researcher queries a triples store through an API (yeyyy open data!), combines their not fully matching but interoperable rdf data with rdf_add (ie try to show how triplestore is better than tabular non-linked data for merging) and then queries the merged rdf to extract an enrched analytical tabular dataset?



comments:

Review test suite:

test coverage

covr::package_coverage(pkg_dir)
rdflib Coverage: 100.00%
R/rdf.R: 100.00%

inspect tests

comments:

Add tests for being able to serialise to trig and turtles which at the moment is throwing an error.

Perhaps a test for parsing/serialising each format could be good.


LS0tCm91dHB1dDoKICAgIGh0bWxfbm90ZWJvb2s6CiAgICAgICAgdG9jOiB0cnVlCiAgICAgICAgdG9jX2Zsb2F0OiB0cnVlCmVkaXRvcl9vcHRpb25zOgogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgo8IS0tIFJFQURNRS5tZCBpcyBnZW5lcmF0ZWQgZnJvbSBSRUFETUUuUm1kLiBQbGVhc2UgZWRpdCB0aGF0IGZpbGUgLS0+CgpgYGB7ciBzZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgY29sbGFwc2UgPSBUUlVFLAogIGNvbW1lbnQgPSAiIz4iLAogIGZpZy5wYXRoID0gIm1hbi9maWd1cmVzL1JFQURNRS0iLAogIG91dC53aWR0aCA9ICIxMDAlIgopCgpsaWJyYXJ5KG1hZ3JpdHRyKQpgYGAKCiMgYHJkZmxpYmAgLSBwYWNrYWdlIHJldmlldwoKIyMgKipSZXZpZXdlcjoqKiBbXEBhbm5ha3J5c3RhbGxpXShodHRwczovL2dpdGh1Yi5jb20vYW5uYWtyeXN0YWxsaSkKCiMjIyBSZXZpZXcgU3VibWl0dGVkOiAyMDE4LTAxLTMwCgoqKmByIGNhdChzcHJpbnRmKCIqKkxhc3QgdXBkYXRlZDoqKiAlcyIsIFN5cy5EYXRlKCkpKWAqKgoKKioqCgo8YnI+CgpUaGlzIHJlcG9ydCBjb250YWlucyBkb2N1bWVudHMgdGhlIHJldmlldyBvZiAqKnJPcGVuU2NpKiogc3VibWl0dGVkIHBhY2thZ2U6CgojIyMgKipgcmRmbGliYDogcm9wZW5zY2kvb25ib2FyZGluZyoqICBpc3N1ZSBbXCMxNjldKGh0dHBzOi8vZ2l0aHViLmNvbS9yb3BlbnNjaS9vbmJvYXJkaW5nL2lzc3Vlcy8xNjkpKS4KCjxicj4KCiMjIFBhY2thZ2UgaW5mbwoKKipEZXNjcmlwdGlvbjoqKgoKVGhlIFJlc291cmNlIERlc2NyaXB0aW9uIEZyYW1ld29yaywgb3IgJ1JERicgaXMgYSB3aWRlbHkgdXNlZAogICAgICAgICAgICAgZGF0YSByZXByZXNlbnRhdGlvbiBtb2RlbCB0aGF0IGZvcm1zIHRoZSBjb3JuZXJzdG9uZSBvZiB0aGUgCiAgICAgICAgICAgICBTZW1hbnRpYyBXZWIuICdSREYnIHJlcHJlc2VudHMgZGF0YSBhcyBhIGdyYXBoIHJhdGhlciB0aGFuIAogICAgICAgICAgICAgdGhlIGZhbWlsaWFyIGRhdGEgdGFibGUgb3IgcmVjdGFuZ2xlIG9mIHJlbGF0aW9uYWwgZGF0YWJhc2VzLgogICAgICAgICAgICAgVGhlICdyZGZsaWInIHBhY2thZ2UgcHJvdmlkZXMgYSBmcmllbmRseSBhbmQgY29uY2lzZSB1c2VyIGludGVyZmFjZQogICAgICAgICAgICAgZm9yIHBlcmZvcm1pbmcgY29tbW9uIHRhc2tzIG9uICdSREYnIGRhdGEsIHN1Y2ggYXMgcmVhZGluZywgd3JpdGluZwogICAgICAgICAgICAgYW5kIGNvbnZlcnRpbmcgYmV0d2VlbiB0aGUgdmFyaW91cyBzZXJpYWxpemF0aW9ucyBvZiAnUkRGJyBkYXRhLAogICAgICAgICAgICAgaW5jbHVkaW5nICdyZGZ4bWwnLCAndHVydGxlJywgJ25xdWFkcycsICdudHJpcGxlcycsICd0cmlnJywgYW5kICdqc29uLWxkJzsKICAgICAgICAgICAgIGNyZWF0aW5nIG5ldyAnUkRGJyBncmFwaHMsIGFuZCBwZXJmb3JtaW5nIGdyYXBoIHF1ZXJpZXMgdXNpbmcgJ1NQQVJRTCcuCiAgICAgICAgICAgICBUaGlzIHBhY2thZ2Ugd3JhcHMgdGhlIGxvdyBsZXZlbCAncmVkbGFuZCcgUiBwYWNrYWdlIHdoaWNoCiAgICAgICAgICAgICBwcm92aWRlcyBkaXJlY3QgYmluZGluZ3MgdG8gdGhlICdyZWRsYW5kJyBDIGxpYnJhcnkuICBBZGRpdGlvbmFsbHksCiAgICAgICAgICAgICB0aGUgcGFja2FnZSBzdXBwb3J0cyB0aGUgbmV3ZXIgYW5kIG1vcmUgZGV2ZWxvcGVyIGZyaWVuZGx5CiAgICAgICAgICAgICAnSlNPTi1MRCcgZm9ybWF0IHRocm91Z2ggdGhlICdqc29ubGQnIHBhY2thZ2UuIFRoZSBwYWNrYWdlCiAgICAgICAgICAgICBpbnRlcmZhY2UgdGFrZXMgaW5zcGlyYXRpb24gZnJvbSB0aGUgUHl0aG9uICdyZGZsaWInIGxpYnJhcnkuCgoqKkF1dGhvcjoqKiBgciBwZXJzb24oIkNhcmwiLCAiQm9ldHRpZ2VyIiwgCiAgICAgICAgICAgICAgICAgIGVtYWlsID0gImNib2V0dGlnQGdtYWlsLmNvbSIsIAogICAgICAgICAgICAgICAgICByb2xlID0gYygiYXV0IiwgImNyZSIsICJjcGgiKSwKICAgICAgICAgICAgICAgICAgY29tbWVudD1jKE9SQ0lEID0gIjAwMDAtMDAwMi0xNjQyLTYyOFgiKSlgCgoqKnJlcG8gdXJsOioqIDxodHRwczovL2dpdGh1Yi5jb20vY2JvZXR0aWcvcmRmbGliPgoKKip3ZWJzaXRlIHVybDoqKiA8aHR0cHM6Ly9jYm9ldHRpZy5naXRodWIuaW8vcmRmbGliLz4KCiMjIFJldmlldyBpbmZvCgoKIyMjIyBTZWUgW3Jldmlld2VyIGd1aWRlbGluZXNdKGh0dHBzOi8vZ2l0aHViLmNvbS9yb3BlbnNjaS9vbmJvYXJkaW5nL2Jsb2IvbWFzdGVyL3Jldmlld2luZ19ndWlkZS5tZCkgZm9yIGZ1cnRoZXIgaW5mb3JtYXRpb24gb24gdGhlIHJPcGVuU2NpIHJldmlldyBwcm9jZXNzLgoKKiprZXkgcmV2aWV3IGNoZWNrczoqKgoKLSBEb2VzIHRoZSBjb2RlIGNvbXBseSB3aXRoICoqZ2VuZXJhbCBwcmluY2lwbGVzIGluIHRoZSBbTW96aWxsYSByZXZpZXdpbmcgZ3VpZGVdKGh0dHBzOi8vbW96aWxsYXNjaWVuY2UuZ2l0aHViLmlvL2NvZGVSZXZpZXcvcmV2aWV3Lmh0bWwpKio/Ci0gRG9lcyB0aGUgcGFja2FnZSAqKmNvbXBseSB3aXRoIHRoZSBbUk9wZW5TY2kgcGFja2FnaW5nIGd1aWRlXShodHRwczovL2dpdGh1Yi5jb20vcm9wZW5zY2kvb25ib2FyZGluZy9ibG9iL21hc3Rlci9wYWNrYWdpbmdfZ3VpZGUubWQpKio/Ci0gQXJlIHRoZXJlICoqaW1wcm92ZW1lbnRzKiogdGhhdCBjb3VsZCBiZSBtYWRlIHRvIHRoZSAqKmNvZGUgc3R5bGU/KioKLSBJcyB0aGVyZSAqKmNvZGUgZHVwbGljYXRpb24qKiBpbiB0aGUgcGFja2FnZSB0aGF0IHNob3VsZCBiZSByZWR1Y2VkPwotIEFyZSB0aGVyZSAqKnVzZXIgaW50ZXJmYWNlIGltcHJvdmVtZW50cyoqIHRoYXQgY291bGQgYmUgbWFkZT8KLSBBcmUgdGhlcmUgKipwZXJmb3JtYW5jZSBpbXByb3ZlbWVudHMqKiB0aGF0IGNvdWxkIGJlIG1hZGU/Ci0gSXMgdGhlICoqZG9jdW1lbnRhdGlvbioqIChpbnN0YWxsYXRpb24gaW5zdHJ1Y3Rpb25zL3ZpZ25ldHRlcy9leGFtcGxlcy9kZW1vcykgKipjbGVhciBhbmQgc3VmZmljaWVudCoqPwoKUGxlYXNlIGJlIHJlc3BlY3RmdWwgYW5kIGtpbmQgdG8gdGhlIGF1dGhvcnMgaW4geW91ciByZXZpZXdzLiBUaGUgck9wZW5TY2kgW2NvZGUgb2YgY29uZHVjdF0ocG9saWNpZXMubWQjY29kZS1vZi1jb25kdWN0KSBpcyBtYW5kYXRvcnkgZm9yIGV2ZXJ5b25lIGludm9sdmVkIGluIG91ciByZXZpZXcgcHJvY2Vzcy4KCioqKgoKIyMjIHNlc3Npb24gaW5mbwoKCmBgYHtyIHNlc3Npb25JbmZvfQpzZXNzaW9uSW5mbygpCmBgYAoKCmBgYHtyIHBrZ19kaXIsIGVjaG8gPSBGfQpwa2dfZGlyIDwtICIvVXNlcnMvQW5uYS9Eb2N1bWVudHMvd29ya2Zsb3dzL3JPcGVuU2NpL3Jldmlld3MvcmRmbGliLXJldmlldy8uLi9yZGZsaWIiCmBgYAoKIyMgVGVzdCBpbnN0YWxsYXRpb24KCiMjIyB0ZXN0IGluc3RhbGwgb2YgYHJkZmxpYmAgZnJvbSBHaXRIdWIgd2l0aDoKCmBgYHtyIHRlc3QtZ2l0aHVifQpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImNib2V0dGlnL3JkZmxpYiIsIGZvcmNlID0gVCwgZGVwZW5kZW5jaWVzID0gVCkKYGBgCmBgYHtyIGdpdGh1Yi1ybX0KcmVtb3ZlLnBhY2thZ2VzKCJyZGZsaWIiKQpgYGAKCiMjIyMgKipjb21tZW50czoqKgoKPCEtLSByZWNvcmQgY29tbWVudHMgb24gZ2l0aHViIGluc3RhbGwgaGVyZSAtLT4KCioqKgoKIyMjIHRlc3QgbG9jYWwgYHJkZmxpYmAgaW5zdGFsbDoKCmBgYHtyIHRlc3QtbG9jYWx9CnBrZ19kaXIgPC0gIi9Vc2Vycy9Bbm5hL0RvY3VtZW50cy93b3JrZmxvd3Mvck9wZW5TY2kvcmV2aWV3cy9yZGZsaWItcmV2aWV3Ly4uL3JkZmxpYiIKZGV2dG9vbHM6Omluc3RhbGwocGtnX2RpciwgZGVwZW5kZW5jaWVzID0gVCwgYnVpbGRfdmlnbmV0dGVzID0gVCkKYGBgCgpgYGB7cn0KcGtnX2RpciA8LSAiL1VzZXJzL0FubmEvRG9jdW1lbnRzL3dvcmtmbG93cy9yT3BlblNjaS9yZXZpZXdzL3JkZmxpYi1yZXZpZXcvLi4vcmRmbGliIgpkZXZ0b29sczo6aW5zdGFsbChwa2dfZGlyLCBkZXBlbmRlbmNpZXMgPSBUKQpkZXZ0b29sczo6aW5zdGFsbChwa2dfZGlyLCBkZXBlbmRlbmNpZXMgPSBULCBidWlsZF92aWduZXR0ZXMgPSBUKQpgYGAKCgoKCiMjIyMgKipjb21tZW50czoqKgoKPCEtLSByZWNvcmQgY29tbWVudHMgb24gbG9jYWwgaW5zdGFsbCBoZXJlIC0tPgoKd2hlbiBJIGZpcnN0IHJhbiBgZGV2dG9vbHM6Omluc3RhbGwocGtnX2RpciwgZGVwZW5kZW5jaWVzID0gVCwgYnVpbGRfdmlnbmV0dGVzID0gVClgLCB0aGUgYnVpbGRpbmcgb2YgdGhlIHZpZ25ldHRlcyB0aHJldyBhbiBlcnJvciBiZWNhdXNlIHN1Z2dlc3RzIHBhY2thZ2UgYOKAmGpxcuKAmWAgaGFkIG5vdCBiZWVuIGluc3RhbGxlZCB5ZXQ/CgoKYGBgCiogY2hlY2tpbmcgZm9yIGZpbGUg4oCYL1VzZXJzL0FubmEvRG9jdW1lbnRzL3dvcmtmbG93cy9yT3BlblNjaS9yZXZpZXdzL3JkZmxpYi9ERVNDUklQVElPTuKAmSAuLi4gT0sKKiBwcmVwYXJpbmcg4oCYcmRmbGli4oCZOgoqIGNoZWNraW5nIERFU0NSSVBUSU9OIG1ldGEtaW5mb3JtYXRpb24gLi4uIE9LCiogaW5zdGFsbGluZyB0aGUgcGFja2FnZSB0byBidWlsZCB2aWduZXR0ZXMKKiBjcmVhdGluZyB2aWduZXR0ZXMgLi4uIEVSUk9SClF1aXR0aW5nIGZyb20gbGluZXMgMjEtMzggKHJkZmxpYi5SbWQpIApFcnJvcjogcHJvY2Vzc2luZyB2aWduZXR0ZSAncmRmbGliLlJtZCcgZmFpbGVkIHdpdGggZGlhZ25vc3RpY3M6CnRoZXJlIGlzIG5vIHBhY2thZ2UgY2FsbGVkICdqcXInCkV4ZWN1dGlvbiBoYWx0ZWQKCmBgYAoKKioqCgojIyBDaGVjayBwYWNrYWdlIGludGVncml0eQoKIyMjIHJ1biBjaGVja3Mgb24gYHJkZmxpYmAgc291cmNlOgoKYGBge3IgY2hlY2stY2hlY2tzfQpkZXZ0b29sczo6Y2hlY2socGtnX2RpcikKYGBgCiMjIyMgKipjb21tZW50czoqKgoKPCEtLSByZWNvcmQgY29tbWVudHMgb24gZ29vZHByYWN0aWNlIGhlcmUgLS0+CgoqKioKCiMjIyBydW4gdGVzdHMgb24gYHJkZmxpYmAgc291cmNlOgoKYGBge3IgY2hlY2stdGVzdHN9CmRldnRvb2xzOjp0ZXN0KHBrZ19kaXIpCmBgYAojIyMjICoqY29tbWVudHM6KioKCjwhLS0gcmVjb3JkIGNvbW1lbnRzIG9uIGdvb2RwcmFjdGljZSBoZXJlIC0tPgoKKioqCgoKIyMjIGNoZWNrIGByZGZsaWJgIGZvciBnb29kcHJhY3RpY2U6CgpgYGB7ciB0ZXN0LWdvb2RwcmFjdGljZX0KZ29vZHByYWN0aWNlOjpncChwa2dfZGlyKQpgYGAKIyMjIyAqKmNvbW1lbnRzOioqCgo8IS0tIHJlY29yZCBjb21tZW50cyBvbiBnb29kcHJhY3RpY2UgaGVyZSAtLT4KCioqKgoKIyMgQ2hlY2sgcGFja2FnZSBtZXRhZGF0YSBmaWxlcwoKIyMjIGluc3BlY3QKCi0gIyMjIyBbUkVBRE1FXShodHRwczovL2dpdGh1Yi5jb20vY2JvZXR0aWcvcmRmbGliKQotICMjIyMgW0RFU0NSSVBUSU9OXShodHRwczovL2dpdGh1Yi5jb20vY2JvZXR0aWcvcmRmbGliL2Jsb2IvbWFzdGVyL0RFU0NSSVBUSU9OKQotICMjIyMgW05BTUVTUEFDRV0oaHR0cHM6Ly9naXRodWIuY29tL2Nib2V0dGlnL3JkZmxpYi9ibG9iL21hc3Rlci9OQU1FU1BBQ0UpCgojIyMgc3BlbGwgY2hlY2sKCmBgYHtyIHNwZWxsLWNoZWNrfQpkZXZ0b29sczo6c3BlbGxfY2hlY2socGtnX2RpcikKYGBgCgojIyMjICoqY29tbWVudHM6KioKCjwhLS0gcmVjb3JkIGNvbW1lbnRzIG9uIG1ldGFkYXRhIGZpbGVzIGhlcmUgLS0+CgoqKioKCiMjIENoZWNrIGRvY3VtZW50YXRpb24KCm9ubGluZSBkb2N1bWVudGF0aW9uOiAqKjxodHRwczovL2Nib2V0dGlnLmdpdGh1Yi5pby9yZGZsaWIvPioqCgoqIElzIHRoZSBkb2N1bWVudGF0aW9uIChpbnN0YWxsYXRpb24gaW5zdHJ1Y3Rpb25zL3ZpZ25ldHRlcy9leGFtcGxlcy9kZW1vcykgY2xlYXIgYW5kIHN1ZmZpY2llbnQ/CgojIyMgdGVzdCBgcmRmbGliYCBmdW5jdGlvbiBoZWxwIGZpbGVzOgoKYGBge3IgdGVzdC1oZWxwfQpoZWxwKHBhY2thZ2UgPSAicmRmbGliIikKYGBgCgojIyMjICoqY29tbWVudHM6KioKCjwhLS0gcmVjb3JkIGNvbW1lbnRzIG9uIGhlbHAgZmlsZXMgaGVyZSAtLT4KCioqKgoKIyMjIHRlc3QgYHJkZmxpYmAgdmlnbmV0dGVzOgoKYGBge3IgdGVzdC12aWduZXR0ZXN9CnZpZ25ldHRlKHBhY2thZ2UgPSAicmRmbGliIikKYGBgCgojIyMjICoqY29tbWVudHM6KioKCjwhLS0gcmVjb3JkIGNvbW1lbnRzIG9uIHZpZ25ldHRlcyBoZXJlIC0tPgoKIyMgT25saW5lIGRvY3VtZW50YXRpb246CgpNeSBtYWluIHN1Z2dlc3Rpb24gaXMgdG8gdHJ5IHRvIGRlZmluZSBzb21lIHRlcm1zIGFuZCBpbXByb3ZlIHRoZSBjb25jZXB0IG1hcCBmb3IgdGhlIHRvb2xzIGJ5IGFkZGluZyBzb21lIGRldGFpbCBhbmQgYnJvYWRlciBjb250ZXh0IHRvIHRoZSBkb2N1bWVudGF0aW9uLiBUaGUgZm9sbG93aW5nIHN1Z2dlc3Rpb25zIGNvdWxkIGFsc28gYmUgYWRkcmVzcyB3aXRoIGxpbmtzIHRvIGZ1cnRoZXIgZGV0YWlscyBpZiB5b3UgdGhpbmsgdGhleSBhcmUgdG9vIHN1cGVyZmx1b3VzIGZvciBleHBsaWNpdCBkb2N1bWVudGF0aW9uIHdpdGggdGhlIHBhY2thZ2UuCgoKLSBhIGJyaWVmIGludHJvIHRvIHRoZSBzZW1hbnRpYyBjb3VsZCBiZSB1c2VmdWwgKGVnIHNvbWV0aGluZyBsaWtlKToKCiAgPiBUaGUgc2VtYW50aWMgd2ViIGFpbXMgdG8gbGluayBkYXRhIGluIGEgbWFjaGluZSByZWFkYWJsZSB3YXkgdGhyb3VnaCB0aGUgd2ViLCBtYWtpbmcgZGF0YSBtb3JlIGFsaWduYWJsZSBhbmQgaW50ZXJvcGVyYWJsZSwgbXVjaCBlYXNpZXIgdG8gc2VhcmNoLCBlbnJpY2hpbmcgYW5kIGNvbXB1dGUgb24uIAogIAoKLSB3aGF0IGEgZ3JhcGggZm9ybWF0IGZvciBkYXRhIGlzIChlZyB0cmlwbGVzIGV0YykuCgotIHRoZSBzdHJ1Y3R1cmUgb2YgYW4gYHJkZmAgUzMgb2JqZWN0CihpZSB5b3UgaW50cm9kdWNlZCBzb21lIGFzcGVjdHMgb2YgdGhlIGRhdGEgZm9ybWF0IGhlcmU6IGAodXNlciBkb2VzIG5vdCBoYXZlIHRvIG1hbmFnZSB3b3JsZCwgbW9kZWwgYW5kIHN0b3JhZ2Ugb2JqZWN0cyBieSBkZWZhdWx0IGp1c3QgdG8gcGVyZm9ybSBzdGFuZGFyZCBvcGVyYXRpb25zIGFuZCBjb252ZXJzaW9ucylgICB3aGljaCB3ZSBhcmUgdG9sZCB3ZSBjYW4gaWdub3JlICh3aGljaCBpcyBncmVhdCkgYnV0IGFjdHVhbGx5IGNyZWF0ZXMgbW9yZSBxdWVzdGlvbnMuLi4gd2hhdCBpcyB0aGlzIG15c3RlcmlvdXMgIndvcmxkIiBvYmplY3QgdGhhdCBmb3JtcyBhbiBvcGFxdWUgc2xvdCBvZiBhbiByZGYgUzMgb2JqZWN0PykgV291bGQgYmUgbmljZSB0byBleHBsYWluIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIFMzIHJkZiBicmllZmx5LiBJcyB0aGVyZSB1c2VmdWxsIG1ldGFkYXRhIHRoYXQgY2FuIGJlIGV4dHJhY3RlZCBmcm9tIHRoZSBzdHJ1Y3R1cmU/IChzZWUgY29tbWVudCBsYXRlcikKCi0gYHJkZmAgZmlsZSBmb3JtYXRzLiAKSSB0aGluayBpdHMgd291bGQgZXNwZWNpYWxseSBhaWQgaW4gYXBwcmVjaWF0aW5nIHRoZSBgcmRmX3NlcmlhbGlzZWAgZnVuY3Rpb24gdG8gKipleHBhbmQgYnJpZWZseSoqIChhbmQgcG90ZW50aWFsbHkgc2lnbnBvc3QgdG8gYSByZXNvdXJjZSBsaWtlIFt0aGlzXShodHRwOi8vYmxvZy5zd2lycmwuY29tL2FydGljbGVzL3JkZi1zZXJpYWxpc2F0aW9uLWZvcm1hdHMvKSkgb24gdGhlICoqdmFyaW91cyBzZXJpYWxpemF0aW9uIGZvcm1hdHMqKiwgcGVyaGFwcyBldmVuIHdoeSBvbmUgd291bGQgdXNlIG9uZSBvdmVyIGFub3RoZXIsIGFuZCBwYXJ0aWN1bGFybHksIHdoeSBzZXJpYWxpemF0aW9uIGludm9sdmVzIHdyaXRpbmcgYSBmaWxlIG91dC4gSSBmZWVsIHRoZXNlIGFyZSBpbXBvcnRhbnQgY29uY2VwdHMgdG8gaGVscCBhcHByZWNpYXRlIHVzZSBjYXNlcyBvZiB0aGUgZnVuY3Rpb24uIEluZGVlZCB0aGUgKipmaWxlIG91dCoqIGFzcGVjdCBvZiB0aGUgZnVuY3Rpb24gY291bGQgZG8gd2l0aCBiZWluZyBmbGFnZ2VkIG1vcmUgcHJvbWluZW50bHkgaW4gZnVuY3Rpb24gbWFuIHBhZ2Ugd2hlcmUganVzdCBieSBsb29raW5nIGF0IHRoZSAoc29tZXdoYXQgamFyZ29ueSBpZiB5b3UgZG9uJ3Qga25vdyB3aGF0IHNlcmlhbGl6YXRpb24gaXMpIGRlc2NyaXB0aW9uIGFuZCBydW5uaW5nIHRoZSBleGFtcGxlLCB5b3UndmUgZW5kZWQgdXAgd3JpdGluZyBhIGZpbGUgd2l0aG91dCByZWFsaXNpbmcuCgpTaW1pbGFybHksIHBhcnNpbmcgY2FuIHRoZW4gYmUgc2Vlbi9kZXNjcmliZWQgYXMgcmVhZGluZyBpbiBhbiBgcmRmYCBmcm9tIHRoZWlyIHNwZWNpZmljIGZvcm1hdHMuCgpTcGVsbGluZyBhIGZldyB0aGluZ3Mgb3V0IGluIHBsYWluIGVuZ2xpc2ggY291bGQgcmVhbGx5IGhlbHAgZm9sa3MgZm9sbG93IHdoYXQncyBnb2luZyBiZXR0ZXIgYW5kIHVuZGVyc3RhbmQgd2hhdCBmaWxlIHR5cGVzIGFyZSBpbnB1dHMgb3Igb3V0cHV0cyBvZiBkaWZmZXJlbnQgZnVuY3Rpb25zLgoKIyMjIyBob3cgZG8gSSBmaW5kIGluZm8gb24gVVJJcz8gCgpJbiBnZW5lcmFsIHdoYXQgaXMgbWlzc2luZyBmb3IgbWUgaXMgc29tZSBzaWducG9zdGluZy9ndWlkYW5jZSBvbiBob3cgSSBjYW4gZmluZCBpbmZvcm1hdGlvbiBvbiB0aGUgc2VtYW50aWNzIGRpY3RhdGluZyB3aGF0IGluZm9ybWF0aW9uIEkgY2FuIGV4dHJhY3QgZnJvbSBhbiBgcmRmYCBvYmplY3QuIGVnLiB3aXRoIGEgYGRmYCBvciBgbGlzdGAgeW91IGNvdWxkIHVzZSBgc3RyYCB0byBnZXQgYW4gaWRlYSBvZiBob3cgeW91IGNvdWxkIHN0YXJ0IGluZGV4aW5nIHRoZXNlIG9iamVjdHMuIElmIGNvbmZyb250ZWQgd2l0aCBhIGxvY2FsIGByZGZgIGZpbGUsIGhvdyB3b3VsZCBvbmUgZ28gYWJvdXQgZmlndXJpbmcgb3V0IGV2ZW4gd2hhdCB0aGV5IGNhbiBxdWVyeT8gSSBhcHByZWNpYXRlIHRoaXMgaXMgcmVhbGx5IG9uZSBvZiB0aGUgZGlmZmljdWx0aWVzIG9mIHdvcmtpbmcgd2l0aCBgcmRmYCBhbmQgc2VtYW50aWMgZGF0YSBpbiBnZW5lcmFsIGJ1dCBJIGZlZWwgc29tZSBicmllZiBndWlkYW5jZSBvciBkZW1vIG9uIGhvdyBvbmUgd291bGQgYXBwcm9hY2ggdGhpcyB3b3VsZCBnbyBhIGxvbmcgd2F5LgoKIyMjIGV4YW1wbGVzIGluIGdlbmVyYWwKCkZvciBjbGFyaXR5IHRvIHRoZSByZWFkZXIgd2hvIG1heSBub3QgaGF2ZSBsb29rZWQgYXQgZnVuY3Rpb24gZG9jdW1lbnRhdGlvbiB5ZXQsIEkgcmVjb21tZW5kIHVzaW5nIHRoZSBmdWxsIGFyZ3VtZW50IG5hbWVzIHdoZW4gc3VwcGx5aW5nIGFyZ3VtZW50cyB0byBmdW5jdGlvbnMgKGlmIG5vdCBhbHdheXMgYXRsZWFzdCB0aGUgZmlyc3QgdGltZSBhbiBhcmd1bWVudCBpcyBpbnRyb2R1Y2VkKSBpbiB2aWduZXR0ZXMuCgojIyMgU1BBUlFMIHF1ZXJpZXMgdG8gSlNPTiBkYXRhIHNlY3Rpb24KCkF0IHRoZSBlbmQgb2YgdGhlIGludHJvIHRvIHRoZSBzZWN0aW9uLCB5b3Ugd3JpdGU6Cgo+IEhlcmUgaXMgYSBxdWVyeSB0aGF0IGZvciBhbGwgcGFwZXJzIHdoZXJlIEkgYW0gYW4gYXV0aG9yLCByZXR1cm5zIGEgdGFibGUgb2YgZ2l2ZW4gbmFtZSwgZmFtaWx5IG5hbWUgYW5kIHllYXIgb2YgcHVibGljYXRpb246CgpBbSBJIHJpZ2h0IGluIHRoaW5raW5nIHRob3VnaCB0aGF0IHlvdSBhcmUgY28tYXV0aG9yIG9uIGFsbCBwYXBlcnMgaW4gdGhlIHJkZiBidXQgdGhlIHF1ZXJ5IGlzIGluIGZhY3QgZmlsdGVyaW5nIHRoZSBuYW1lcyBvZiB5b3VyIGNvLWF1dGhvcnM/ICh0aHJvdWdoIGBGSUxURVIgKCA/Y29pX2ZhbWlseSAhPSAiQm9ldHRpZ2VyIiApYCkgCgojIyMgVHVybmluZyBSREYtWE1MIGludG8gbW9yZSBmcmllbmRseSBKU09OCgpJdCB3b3VsZCBiZSBuaWNlIGlmIHBvc3NpYmxlIHRvIHNlZSBzYW1wbGUgb2YgcHJpbnQgb3V0cyBvZiB0aGUgY292ZW5yc2lvbiBvZiB0aGUgZGlmZmVyZW50IGZpbGVzIG9yIGF0IGxlYXN0IG9mIHRoZSBlZmZlY3Qgb2YgY29tcGFjdGlvbi4gCgojIyMgYHJkZl9hZGRgIG1hbiBwYWdlCgpXb3VsZCBiZSBuaWNlIHRvIHNlZSBhIGRlbW8gb2YgdXNpbmcgb25lIG9yIG1vcmUgb2YgdGhlIGFkZGl0b25hbCBhcmd1bWVudHMuCgoKIyMjIE1vdGl2YXRpbmcgZXhhbXBsZQoKSSB0aGluayBhbiBhZGRpdG9uYWwsIG1vcmUgZGV0YWlsZWQgbW90aXZhdGluZyBleGFtcGxlIG1pZ2h0IGlsbHVzdHJhdGUgbW9yZSBkaXJlY3QgdXNlIGNhc2UgaW4gYSByZXNlYXJjaGVycyB3b3JrZmxvdy4gSW4gcGFydGljdWxhciBpdCB3b3VsZCBiZSBnb29kIHRvIGhpZ2hsaWdodCB0aGUgZ3JlYXQgcG90ZW50aWFsIG9mIHRyaXBsZXN0b3JlIEFQSXMgKGFuZCBjZWxlYnJhdGUgdGhlIGVmZm9ydHMgb2YgbWFueSBjb29sIGVnIGdvdmVybm1lbnRhbCBsaW5rZWQgZGF0YSBpbml0aWF0aXZlcykuIFNvIGFuIGV4YW1wbGUgdGhhdCBpbmNvcnBvcmF0ZXMgYSBxdWVyeSB0byBhIHRyaXBsZXN0b3JlIGFuZCB0aGVuIGVucmljaG1lbnQgb2YgYSByZXNlYXJjaGVycyBkYXRhIGNvdWxkIGJlIGEgY29vbCBleGFtcGxlLiBUaGlzIGNvdWxkIGJlIGEgbG9uZ2VyIHRlcm0gcHJvamVjdCBvcHIgZXZlbiBqdXN0IGFuIHJPcGVuU2NpIGJsb2dwb3N0IGJ1dCBzZWUgY29tbWVudCByZTogYHJkZl9xdWVyeWAgZnVuY3Rpb24gYmVsb3cuCgoKKioqCgojIyBUZXN0IGZ1bmN0aW9uYWxpdHk6CgotIEFyZSB0aGVyZSAqKnVzZXIgaW50ZXJmYWNlIGltcHJvdmVtZW50cyoqIHRoYXQgY291bGQgYmUgbWFkZT8KLSBBcmUgdGhlcmUgKipwZXJmb3JtYW5jZSBpbXByb3ZlbWVudHMqKiB0aGF0IGNvdWxkIGJlIG1hZGU/CgpgYGB7ciBmcmVlLXN0eWxlfQpsaWJyYXJ5KCJyZGZsaWIiKQpsaWJyYXJ5KFNQQVJRTCkKbGlicmFyeShqc29ubGl0ZSkKYGBgCgpgYGB7ciBwYXJzZS1mdW5jdGlvbnN9CmV4cG9ydHMgPC1scygicGFja2FnZTpyZGZsaWIiKQpleHBvcnRzCmBgYAoKPCEtLSBleHBlcmltZW50IHdpdGggcGFja2FnZSBmdW5jdGlvbnMgLS0+CgojIyMgYHJkZl9zZXJpYWxpemVgCgpgYGB7ciBleHAtY2h1bmt9CmRvYyA8LSBzeXN0ZW0uZmlsZSgiZXh0ZGF0YSIsICJkYy5yZGYiLCBwYWNrYWdlPSJyZWRsYW5kIikKCmRvYyAlPiUKICByZGZfcGFyc2UoKSAlPiUKICByZGZfc2VyaWFsaXplKGRvYyA9ICJ0ZXN0Lm5xdWFkcyIsIGZvcm1hdCA9ICJucXVhZHMiKQoKZG9jICU+JQogIHJkZl9wYXJzZSgpICU+JQogIHJkZl9zZXJpYWxpemUoZG9jID0gInRlc3QucmRmeG1sIiwgZm9ybWF0ID0gInJkZnhtbCIpCgoKZG9jICU+JQogIHJkZl9wYXJzZSgpICU+JQogIHJkZl9zZXJpYWxpemUoZG9jID0gInRlc3QubnRyaXBsZXMiLCBmb3JtYXQgPSAibnRyaXBsZXMiKQoKYGBgCgoKYGBge3J9CnJlYWRyOjpyZWFkX2ZpbGUoInRlc3QubnF1YWRzIikKYGBgCmBgYHtyfQpyZWFkcjo6cmVhZF9maWxlKCJ0ZXN0Lm50cmlwbGVzIikKYGBgCmBgYHtyfQpyZWFkcjo6cmVhZF9maWxlKCJ0ZXN0LnJkZnhtbCIpCmBgYAoKCiMjIyMjIHNlcmlhbGlzYXRpb24gZXJyb3JzOiAqKmB0dXJ0bGVgKiogJiAqKmB0cmlnYCoqCgpgYGB7cn0KbGlicmFyeShtYWdyaXR0cikKbGlicmFyeShyZGZsaWIpCmRvYyA8LSBzeXN0ZW0uZmlsZSgiZXh0ZGF0YSIsICJkYy5yZGYiLCBwYWNrYWdlPSJyZWRsYW5kIikKZG9jICU+JQogIHJkZl9wYXJzZSgpICU+JQogIHJkZl9zZXJpYWxpemUoZG9jID0gInRlc3QudHVydGxlIiwgZm9ybWF0ID0gInR1cnRsZSIpCmBgYAoKYGBge3J9CmRvYyA8LSBzeXN0ZW0uZmlsZSgiZXh0ZGF0YSIsICJkYy5yZGYiLCBwYWNrYWdlPSJyZWRsYW5kIikKZG9jICU+JQogIHJkZl9wYXJzZSgpICU+JQogIHJkZl9zZXJpYWxpemUoZG9jID0gInRlc3QudHJpZyIsIGZvcm1hdCA9ICJ0cmlnIikKYGBgCgojIyMgYHJkZl9zZXJpYWxpemVgCgpgYGB7cn0KZG9jICU+JQogIHJkZl9wYXJzZSh0eXBlID0gImpzb2xkIikKYGBgCgoKCmBgYHtyfQpkb2MgJT4lCiAgcmRmX3BhcnNlKHR5cGUgPSAianNvbmxkIikgJT4lIGNsYXNzCmBgYAoKYGBge3J9CgpgYGAKCgoKIyMjIyBodHRwczovL2Nib2V0dGlnLmdpdGh1Yi5pby9yZGZsaWIvYXJ0aWNsZXMvcmRmbGliLmh0bWwKCmBgYHtyfQpleCA8LSBzeXN0ZW0uZmlsZSgiZXh0ZGF0YS92aXRhLmpzb24iLCBwYWNrYWdlPSJyZGZsaWIiKQp2aXRhIDwtIHJkZl9wYXJzZShleCwgImpzb25sZCIpCgpzcGFycWwgPC0KICdQUkVGSVggc2NoZW1hOiA8aHR0cDovL3NjaGVtYS5vcmcvPgoKICBTRUxFQ1QgP2NvaV9naXZlbiA/Y29pX2ZhbWlseSA/eWVhcgoKICBXSEVSRSB7IAogICAgP3BhcGVyIGEgc2NoZW1hOlNjaG9sYXJseUFydGljbGUgLiAKICAgID9wYXBlciBzY2hlbWE6YXV0aG9yID9hdXRob3JzIC4KICAgID9wYXBlciBzY2hlbWE6ZGF0ZUNyZWF0ZWQgP3llYXIgLiAKICAgID9hdXRob3JzIHNjaGVtYTpmYW1pbHlOYW1lID9jb2lfZmFtaWx5IC4KICAgIE9QVElPTkFMIHsgP2F1dGhvcnMgc2NoZW1hOmdpdmVuTmFtZSA/Y29pX2dpdmVuIC4gfQoKICAgIEZJTFRFUiAoID9jb2lfZmFtaWx5ICE9ICJCb2V0dGlnZXIiICkKfQonCgp2aXRhICU+JSByZGZfcXVlcnkoc3BhcnFsKQpgYGAKCmBgYHtyfQpzcGFycWwgPC0KICdQUkVGSVggc2NoZW1hOiA8aHR0cDovL3NjaGVtYS5vcmcvPgoKICBTRUxFQ1QgP2NvaV9naXZlbiA/Y29pX2ZhbWlseSA/eWVhcgoKICBXSEVSRSB7IAogICAgP3BhcGVyIGEgc2NoZW1hOlNjaG9sYXJseUFydGljbGUgLiAKICAgID9wYXBlciBzY2hlbWE6YXV0aG9yID9hdXRob3JzIC4KICAgID9wYXBlciBzY2hlbWE6ZGF0ZUNyZWF0ZWQgP3llYXIgLiAKICAgID9hdXRob3JzIHNjaGVtYTpmYW1pbHlOYW1lID9jb2lfZmFtaWx5IC4KICAgIE9QVElPTkFMIHsgP2F1dGhvcnMgc2NoZW1hOmdpdmVuTmFtZSA/Y29pX2dpdmVuIC4gfQoKICAgIEZJTFRFUiAoID95ZWFyID09ICIyMDA5LTEwLTE5Il5eeHM6ZGF0ZSkKfQonCgp2aXRhICU+JSByZGZfcXVlcnkoc3BhcnFsKQpgYGAKCkknbSBmaW5kaW5nIGl0IGRpZmZpY3VsdCB0byBxdWVyeSBieSBkYXRlLiBJIHJlYWxpc2UgeW91J3ZlIG9wdGVkIHRvIGRvIGRhdGUgZmlsdGVyaW5nIGxhdGVyIGJ1dCwgaXQgY291bGQgYmUgYW5vdGhlciBxdWl0ZSBpbmZvcm1hdGl2ZSBhZGRpdGlvbmFsIGV4YW1wbGUgYXMgdGVtcG9yYWwgZmlsdGVyaW5nIGlzIHByb2JhYmx5IGEgd2lkZXNwcmVhZCB1c2VjYXNlLiAKCgoKYGBge3J9CnNwYXJxbCA8LQogJ1BSRUZJWCBzY2hlbWE6IDxodHRwOi8vc2NoZW1hLm9yZy8+CgogIFNFTEVDVCA/Y29pX2dpdmVuID9jb2lfZmFtaWx5ID95ZWFyCgogIFdIRVJFIHsgCiAgICA/cGFwZXIgYSBzY2hlbWE6U2Nob2xhcmx5QXJ0aWNsZSAuIAogICAgP3BhcGVyIHNjaGVtYTphdXRob3IgP2F1dGhvcnMgLgogICAgP2F1dGhvcnMgc2NoZW1hOmZhbWlseU5hbWUgP2NvaV9mYW1pbHkgLgogICAgT1BUSU9OQUwgeyA/YXV0aG9ycyBzY2hlbWE6Z2l2ZW5OYW1lID9jb2lfZ2l2ZW4gLiB9CgogICAgRklMVEVSICggP2NvaV9mYW1pbHkgIT0gIkJvZXR0aWdlciIgKQp9CicKCnZpdGEgJT4lIHJkZl9xdWVyeShzcGFycWwpCmBgYAoKTmljZSB3YXJuaW5nIG1lc3NhZ2UKCmBgYHtyfQp2aXRhICU+JSBzdHIKYGBgCgoKYGBge3J9Cgp2aXRhICU+JSBjbGFzcwpgYGAKCmBgYHtyfQp2aXRhJHdvcmxkQGxpYnJkZl93b3JsZAoKYGBgCgoKYGBge3J9CnZpdGEkbW9kZWxAbGlicmRmX21vZGVsCgpgYGAKCgpgYGB7cn0KZG9jIDwtIHN5c3RlbS5maWxlKCJleHRkYXRhIiwgImRjLnJkZiIsIHBhY2thZ2U9InJlZGxhbmQiKQoKc3BhcnFsIDwtCidQUkVGSVggZGM6IDxodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLz4KIFNFTEVDVCA/YXV0aG9yID9jCiBXSEVSRSB7ID9hdXRob3IgZGM6Y3JlYXRvciA/YyAuIH0nCgpyZGYgPC0gcmRmX3BhcnNlKGRvYykKcmRmX3F1ZXJ5KHJkZiwgc3BhcnFsKQpgYGAKCgojIyMjIyBganFgCgpgYGB7cn0KCiAgcmVhZHI6OnJlYWRfZmlsZShleCkgJT4lCiAganFyOjpqcSgKICAgICAnLiJAcmV2ZXJzZSIuYXV0aG9yW10gIHwgCiAgICAgICB7IHllYXI6IC5kYXRlQ3JlYXRlZCwgCiAgICAgICAgIGF1dGhvcjogLmF1dGhvcltdIHwgWy5naXZlbk5hbWUsIC5mYW1pbHlOYW1lXSAgfCBqb2luKCIgIikKICAgICAgIH0nKSAlPiUKICBqcXI6OmNvbWJpbmUoKSAlPiUKICBqc29ubGl0ZTo6ZnJvbUpTT04oKQpgYGAKCgpTZWxlY3QgdGhlIGZpcnN0IDE1IHRyaXBsZXRzLgoKYGBge3J9CmxpYnJhcnkoU1BBUlFMKQplbmRwb2ludCA8LSAiaHR0cDovL2xpbmtlZC5ib2RjLmFjLnVrL3NwYXJxbC8iCnF1ZXJ5IDwtICdzZWxlY3QgKiB3aGVyZSB7P3MgP3AgP28gLiB9IGxpbWl0IDE1JwoKb3V0IDwtIFNQQVJRTChlbmRwb2ludCwgcXVlcnkpCmBgYAoKCmBgYHtyfQoKcXVlcnkgPC0gJ3NlbGVjdCAqIHdoZXJlIHs/c3ViamVjdCA/cHJlZGljYXRlID9vYmplY3QgLiB9IGxpbWl0IDE1JwpyZXMgPC0gU1BBUlFMKGVuZHBvaW50LCBxdWVyeSkKCnJlcyAlPiUgY2xhc3MKYGBgCmBgYHtyfQp4bWwKYGBgCgoKYGBge3J9CiJodHRwOi8vd3d3LnczLm9yZy9ucy9kY2F0I0RhdGFzZXQiCgpxdWVyeSA8LSAnc2VsZWN0ID9kYXRhc2V0IHdoZXJlIHs/ZGF0YXNldCBhIDxodHRwOi8vd3d3LnczLm9yZy9ucy9kY2F0I0RhdGFzZXQ+IC4gfSBsaW1pdCAxNScKCnF1ZXJ5IDwtICdTRUxFQ1QgRElTVElOQ1QgP3MKV0hFUkUgewogID9zIGEgPGh0dHA6Ly93d3cudzMub3JnL25zL2RjYXQjRGF0YXNldD4KfSBsaW1pdCAxNScKU1BBUlFMKGVuZHBvaW50LCBxdWVyeSkKCmBgYAoKCmBgYHtyfQpyZXMkcmVzdWx0cyAlPiUgdG9KU09OKCkgJT4lIGNsYXNzKCkgJT4lIHJkZl9wYXJzZSgianNvbiIpCmBgYAoKYGBge3J9ClNQQVJRTDo6Y29tbW9ubnMKYGBgCgoKYGBge3J9CmV4IDwtIHN5c3RlbS5maWxlKCJleHRkYXRhL3ZpdGEuanNvbiIsIHBhY2thZ2U9InJkZmxpYiIpCnZpdGEgPC0gcmRmX3BhcnNlKGV4LCAianNvbiIpCgpzcGFycWwgPC0KICdQUkVGSVggc2NoZW1hOiA8aHR0cDovL3NjaGVtYS5vcmcvPgoKICBTRUxFQ1QgP2NvaV9naXZlbiA/Y29pX2ZhbWlseSA/eWVhcgoKICBXSEVSRSB7IAogICAgP3BhcGVyIGEgc2NoZW1hOlNjaG9sYXJseUFydGljbGUgLiAKICAgID9wYXBlciBzY2hlbWE6YXV0aG9yID9hdXRob3JzIC4KICAgID9wYXBlciBzY2hlbWE6ZGF0ZUNyZWF0ZWQgP3llYXIgLiAKICAgID9hdXRob3JzIHNjaGVtYTpmYW1pbHlOYW1lID9jb2lfZmFtaWx5IC4KICAgIE9QVElPTkFMIHsgP2F1dGhvcnMgc2NoZW1hOmdpdmVuTmFtZSA/Y29pX2dpdmVuIC4gfQoKICAgIEZJTFRFUiAoID9jb2lfZmFtaWx5ICE9ICJCb2V0dGlnZXIiICkKfQonCgp2aXRhICU+JSByZGZfcXVlcnkoc3BhcnFsKQpgYGAKCgoKCiMjIyMgKipjb21tZW50czoqKgoKPCEtLSByZWNvcmQgY29tbWVudHMgb24gcmRmbGliIGV4cGVyaW1lbnRhdGlvbiBoZXJlIC0tPgoKSSBrbm93IHRoaXMgaXMgbW9yZSB0aGUgYHJlZGxhbmRgIHBhY2thZ2UgYW5kIHRoZSB3aG9sZSBwb2ludCBpcyBJJ2Qga2luZGEgbGlrZSB0byBrbm93IHdoYXQgd29ybGQgaXMuIERlc3BpdGUgdGhpcyByZWZlcmVuY2UgaW4gdGhlIHZpZ25ldHRlOgo+IHVzZXIgZG9lcyBub3QgaGF2ZSB0byBtYW5hZ2Ugd29ybGQsIG1vZGVsIGFuZCBzdG9yYWdlIG9iamVjdHMgYnkgZGVmYXVsdCBqdXN0IHRvIHBlcmZvcm0gc3RhbmRhcmQgb3BlcmF0aW9ucyBhbmQgY29udmVyc2lvbnMpCgpJJ20gbm90IHJlYWxseSBzdXJlIHdoYXQgdGhleSBhcmUuIFdvdWxkIGJlIG5pY2UgdG8gZXhwbGFpbiB3aGF0IHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIFMzIHJkZiBpcy4gSXMgdGhlcmUgdXNlZnVsbCBtZXRhZGF0YSB0aGF0IGNhbiBiZSBleHRyYWN0ZWQgZnJvbSB0aGUgc3RydWN0dXJlPwoKLSBpbiBgcmRmX3NlcmlhbGl6ZWAgZG9jdW1lbnRhdGlvbjogSSB0aGluayBhIGJyaWVmIG1lbnRpb24gb2Ygd2hhdCBzZXJpYWxpc2F0aW9uIGFjdHVhbGx5IGlzIHdvdWxkIHJlYWxseSBoZWxwIHVuZGVyc3RhbmQgdGhpcyBmdW5jdGlvbi4gSSBhbHNvIGZlZWwgaXQgc2hvdWxkIGJlIGZsYWdnZWQgbW9yZSBwcm9taW5lbnRseSB0aGF0IHRoZSBmdW5jdGlvbiAqKndyaXRlcyBhIGZpbGUgb3V0KiouCgoKKioqCgojIyBJbnNwZWN0IGNvZGU6CgotIERvZXMgdGhlIHBhY2thZ2UgKipjb21wbHkgd2l0aCB0aGUgW1JPcGVuU2NpIHBhY2thZ2luZyBndWlkZV0oaHR0cHM6Ly9naXRodWIuY29tL3JvcGVuc2NpL29uYm9hcmRpbmcvYmxvYi9tYXN0ZXIvcGFja2FnaW5nX2d1aWRlLm1kKSoqPwogICAgKiBnb29kIFtmdW5jdGlvbiAmIHZhcmlhYmxlIG5hbWluZz9dKGh0dHBzOi8vZ2l0aHViLmNvbS9yb3BlbnNjaS9vbmJvYXJkaW5nL2Jsb2IvbWFzdGVyL3BhY2thZ2luZ19ndWlkZS5tZCNmdW52YXIpCiAgICAqIGdvb2QgW2RlcGVuZGVuY3kgbWFuYWdlbWVudF0oaHR0cHM6Ly9naXRodWIuY29tL3JvcGVuc2NpL29uYm9hcmRpbmcvYmxvYi9tYXN0ZXIvcGFja2FnaW5nX2d1aWRlLm1kI2RlcHMpPwotIEFyZSB0aGVyZSAqKmltcHJvdmVtZW50cyoqIHRoYXQgY291bGQgYmUgbWFkZSB0byB0aGUgKipjb2RlIHN0eWxlPyoqCi0gSXMgdGhlcmUgKipjb2RlIGR1cGxpY2F0aW9uKiogaW4gdGhlIHBhY2thZ2UgdGhhdCBzaG91bGQgYmUgcmVkdWNlZD8KCmBgYHtyIGluc3BlY3QtY29kZX0KcGtncmV2aWV3cjo6cGtncmV2aWV3X3ByaW50X3NvdXJjZSgicmRmbGliIikKYGBgCgpgYGB7cn0KcmVkbGFuZDo6c2VyaWFsaXplVG9GaWxlCmBgYAoKCmBgYHtyfQpyZGZsaWI6OjpyZGZfbWltZXR5cGVzCmBgYAoKYGBge3J9CnJkZmxpYjo6OnJlY3Rhbmd1bGFyaXplX3F1ZXJ5X3Jlc3VsdHMKYGBgCgoKSXMgdGhlcmUgYSB3YXkgdG8gcmV0dXJuIGEgbm9uIHJlZ3VsYXJpc2VkIHF1ZXJ5IHJlc3VsdCBpZSByZXR1cm4gYW4gYHJkZmAgaW5zdGVhZD8gCgpJJ20gdGhpbmtpbmcgYWJvdXQgYSB1c2VjYXNlIHdoZW4gbWF5YmUgaXQncyBiZXR0ZXIgdG8gZW5yaWNoIGRhdGEgYnkgbWVyZ2luZyBgcmRmYHM/IAoKSWUsIHJlc2VhcmNoZXIgcXVlcmllcyBhIHRyaXBsZXMgc3RvcmUgdGhyb3VnaCBhbiBBUEkgKHlleXl5IG9wZW4gZGF0YSEpLCBjb21iaW5lcyB0aGVpciBub3QgZnVsbHkgbWF0Y2hpbmcgYnV0IGludGVyb3BlcmFibGUgYHJkZmAgZGF0YSB3aXRoIGByZGZfYWRkYCAoaWUgdHJ5IHRvIHNob3cgaG93IHRyaXBsZXN0b3JlIGlzIGJldHRlciB0aGFuIHRhYnVsYXIgbm9uLWxpbmtlZCBkYXRhIGZvciBtZXJnaW5nKSBhbmQgdGhlbiBxdWVyaWVzIHRoZSBtZXJnZWQgcmRmIHRvIGV4dHJhY3QgYW4gZW5yY2hlZCBhbmFseXRpY2FsIHRhYnVsYXIgZGF0YXNldD8gCgoKPGJyPgo8YnI+CgojIyMjICoqY29tbWVudHM6KioKCjwhLS0gcmVjb3JkIGNvbW1lbnRzIG9uIHJkZmxpYiBzb3VyY2UgY29kZSBoZXJlIC0tPgoKCiMjIFJldmlldyB0ZXN0IHN1aXRlOgoKIyMjIHRlc3QgY292ZXJhZ2UKCmBgYHtyIHBrZ19jb3ZlcmFnZX0KY292cjo6cGFja2FnZV9jb3ZlcmFnZShwa2dfZGlyKQoKYGBgCgojIyMgaW5zcGVjdCBbdGVzdHNdKGh0dHBzOi8vZ2l0aHViLmNvbS9jYm9ldHRpZy9yZGZsaWIvYmxvYi9tYXN0ZXIvdGVzdHMvdGVzdHRoYXQpCgojIyMjICoqY29tbWVudHM6KioKCjwhLS0gcmVjb3JkIGNvbW1lbnRzIG9uIHRlc3Rpbmcgc3VpdGUgaGVyZSAtLT4KCgpBZGQgdGVzdHMgZm9yIGJlaW5nIGFibGUgdG8gc2VyaWFsaXNlIHRvIGB0cmlnYCBhbmQgYHR1cnRsZXNgIHdoaWNoIGF0IHRoZSBtb21lbnQgaXMgdGhyb3dpbmcgYW4gZXJyb3IuCgpQZXJoYXBzIGEgdGVzdCBmb3IgcGFyc2luZy9zZXJpYWxpc2luZyBlYWNoIGZvcm1hdCBjb3VsZCBiZSBnb29kLgoKKioqCg==